diff --git a/.agents/skills/working-with-qemu/SKILL.md b/.agents/skills/working-with-qemu/SKILL.md new file mode 100644 index 0000000000..659784115f --- /dev/null +++ b/.agents/skills/working-with-qemu/SKILL.md @@ -0,0 +1,70 @@ +--- +name: working-with-qemu +description: Use when building, launching, debugging, or capturing screenshots of PebbleOS under QEMU. +--- + +# Working with QEMU + +PebbleOS can run under a custom QEMU version shipped with the PebbleOS SDK. + +## Configure & build + +```sh +./pbl configure --board $BOARD +./pbl build +``` + +where `$BOARD` is any of the `qemu_*` boards: + +- `qemu_emery` +- `qemu_flint` +- `qemu_gabbro` + +QEMU boards target a specific platform, e.g. `qemu_emery` targets the Emery platform, which is the platform used by Pebble Time 2. + +## Launch + +```sh +./pbl qemu +``` + +The launched QEMU exposes: + +- Interactive QEMU monitor on the launching terminal (`-monitor stdio`) +- Programmatic socket monitor (`-monitor unix:build/qemu-mon.sock`) +- Serial console over TCP on `localhost:12345` (console) and `localhost:12344` (pebble-tool) + +UART1 output is also captured to `build/uart1.log`. + +## Console + +```sh +./pbl console +``` + +Requires QEMU to be running. +Uses the TCP serial port to connect to the QEMU console, and provides a prompt for sending commands and receiving responses. + +## Screenshot + +```sh +./pbl screenshot # defaults to build/screenshot.png +./pbl screenshot --screenshot-output /tmp/foo.png +``` + +Requires QEMU to be running. +Uses the programmatic socket monitor to capture a screenshot of the QEMU display and save it to disk. +Useful to validate or iterate on UI changes. + +## Interaction + +Keyboard input is captured by QEMU, so you can interact with the PebbleOS UI. +Keys can also be send programmatically over the socket monitor using the `sendkey` command. +They key mapping is: + +| QEMU key | PebbleOS key | +| -------- | ------------ | +| `left` | `back` | +| `right` | `select` | +| `up` | `up` | +| `down` | `down` | diff --git a/.clang-format b/.clang-format index 7f61ec4889..643199a8a9 100644 --- a/.clang-format +++ b/.clang-format @@ -5,4 +5,5 @@ UseTab: Never PointerAlignment: Right ColumnLimit: 100 SortIncludes: Never +AllowShortFunctionsOnASingleLine: Empty --- diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 120000 index 0000000000..be77ac83a1 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000000..2b7a412b8f --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..88736e5655 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,6 @@ +# PebbleOS code owners. +# +# Everything is currently owned by the maintainers below. As the project +# grows, ownership may be split by area. + +* @gmarull @jplexer diff --git a/.github/workflows/build-bootloader.yml b/.github/workflows/build-bootloader.yml deleted file mode 100644 index 2c09f3b700..0000000000 --- a/.github/workflows/build-bootloader.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Build Bootloader - -on: - push: - branches: [main] - pull_request: - branches: [main] - paths: - - '.github/workflows/build-bootloader.yml' - - 'platform/**' - - 'third_party/**' - -jobs: - build: - runs-on: ubuntu-24.04 - - container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 - - strategy: - matrix: - board: ["asterix"] - - steps: - - name: Mark Github workspace as safe - run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: true - - - name: Install Python dependencies - run: | - pip install -U pip - pip install -r requirements.txt - - - name: Obtain platform name - id: get-platform - run: | - BOARD=${{ matrix.board }} - PLATFORM=${BOARD%%_*} - echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - - - name: Configure bootloader - working-directory: platform/${{ steps.get-platform.outputs.platform }}/boot - run: ./waf configure --board ${{ matrix.board }} - - - name: Build bootloader - working-directory: platform/${{ steps.get-platform.outputs.platform }}/boot - run: ./waf build - - - name: Store bootloader images - uses: actions/upload-artifact@v4 - with: - name: bootloader-${{ matrix.board }} - path: | - platform/${{ steps.get-platform.outputs.platform }}/boot/build/tintin_boot.* diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index e8867c7ed0..cd6395afa9 100644 --- a/.github/workflows/build-firmware.yml +++ b/.github/workflows/build-firmware.yml @@ -5,35 +5,58 @@ on: branches: [main] pull_request: branches: [main] - paths: - - '.github/workflows/build-firmware.yml' - - 'resources/**' - - 'sdk/**' - - 'src/**' - - 'stored_apps/**' - - 'tools/**' - - 'third_party/**' - - 'waftools/**' - - 'waf' - - 'wscript' jobs: - build: + changes-firmware: + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + outputs: + should-build: ${{ github.event_name == 'push' || steps.filter.outputs.src == 'true' }} + steps: + - uses: dorny/paths-filter@v4 + if: github.event_name == 'pull_request' + id: filter + with: + filters: | + src: + - '.github/workflows/build-firmware.yml' + - 'boards/**' + - 'platform/**' + - 'resources/**' + - 'sdk/**' + - 'src/**' + - 'stored_apps/**' + - 'tools/**' + - 'third_party/**' + - 'waftools/**' + - 'waf' + - 'wscript' + + build-firmware: + needs: changes-firmware + if: needs.changes-firmware.outputs.should-build == 'true' runs-on: ubuntu-24.04 container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 strategy: matrix: - board: ["snowy_bb2", "spalding_bb2", "silk_bb2", "asterix", "obelix", "obelix_bb"] + board: + - asterix + - obelix@dvt + - obelix@pvt + - getafix@evt + - getafix@dvt + - getafix@dvt2 steps: - name: Mark Github workspace as safe run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true @@ -48,16 +71,14 @@ jobs: shell: bash run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT} - - name: Node modules cache - uses: actions/cache@v4 - with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-node-${{ hashFiles('third_party/jerryscript/jerryscript/js_tooling/package.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Set artifact board name + id: artifact_board + run: echo "NAME=$(printf '%s' "$BOARD" | tr @ _)" >> "$GITHUB_OUTPUT" + env: + BOARD: ${{ matrix.board }} - name: Configure - run: ./waf configure --board ${{ matrix.board }} + run: ./waf configure --board '${{ matrix.board }}' - name: Build run: ./waf build @@ -66,9 +87,9 @@ jobs: run: ./waf bundle - name: Store - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: - name: firmware-${{ matrix.board }} + name: firmware-${{ steps.artifact_board.outputs.NAME }} path: | build/**/*.elf build/**/*.pbz @@ -77,17 +98,24 @@ jobs: - name: Get Build ID id: build_id run: | - echo "BUILD_ID=$(arm-none-eabi-readelf -n build/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" + echo "BUILD_ID=$(readelf -n build/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" - name: Upload log hash dictionary - uses: Noelware/s3-action@2.3.1 - if: ${{ github.event_name == 'push' }} - with: - access-key-id: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} - secret-key: ${{ secrets.LOG_HASH_BUCKET_SECRET }} - endpoint: ${{ vars.LOG_HASH_BUCKET_ENDPOINT }} - bucket: ${{ vars.LOG_HASH_BUCKET_NAME }} - files: | - build/src/fw/tintin_fw_loghash_dict.json - path-format: ${{ steps.build_id.outputs.BUILD_ID }}-${{ github.sha }}-normal.json - + if: ${{ github.event_name == 'push' && github.repository == 'coredevices/PebbleOS' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.LOG_HASH_BUCKET_SECRET }} + AWS_DEFAULT_REGION: us-east-1 + run: | + pip install awscli + aws s3 cp build/src/fw/tintin_fw_loghash_dict.json \ + "s3://${{ vars.LOG_HASH_BUCKET_NAME }}/${{ steps.build_id.outputs.BUILD_ID }}-${{ github.sha }}-normal.json" \ + --endpoint-url "${{ vars.LOG_HASH_BUCKET_ENDPOINT }}" + + build-firmware-status: + needs: [changes-firmware, build-firmware] + if: always() + runs-on: ubuntu-24.04 + steps: + - if: needs.build-firmware.result == 'failure' || needs.build-firmware.result == 'cancelled' + run: exit 1 diff --git a/.github/workflows/build-prf.yml b/.github/workflows/build-prf.yml index f2dd5d26a7..1fab0411b9 100644 --- a/.github/workflows/build-prf.yml +++ b/.github/workflows/build-prf.yml @@ -5,34 +5,59 @@ on: branches: [main] pull_request: branches: [main] - paths: - - '.github/workflows/build-prf.yml' - - 'resources/**' - - 'src/**' - - 'tools/**' - - 'third_party/**' - - 'waftools/**' - - 'waf' - - 'wscript' jobs: - build: + changes-prf: + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + outputs: + should-build: ${{ github.event_name == 'push' || steps.filter.outputs.src == 'true' }} + steps: + - uses: dorny/paths-filter@v4 + if: github.event_name == 'pull_request' + id: filter + with: + filters: | + src: + - '.github/workflows/build-prf.yml' + - 'boards/**' + - 'platform/**' + - 'resources/**' + - 'src/**' + - 'tools/**' + - 'third_party/**' + - 'waftools/**' + - 'waf' + - 'wscript' + + build-prf: + needs: changes-prf + if: needs.changes-prf.outputs.should-build == 'true' runs-on: ubuntu-24.04 container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 strategy: matrix: - board: ["asterix", "obelix", "obelix_bb"] - mode: ["normal", "mfg"] + board: + - asterix + - obelix@dvt + - obelix@pvt + - getafix@evt + - getafix@dvt + - getafix@dvt2 + mode: + - normal + - mfg steps: - name: Mark Github workspace as safe run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true @@ -47,50 +72,56 @@ jobs: shell: bash run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT} - - name: Node modules cache - uses: actions/cache@v4 - with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-node-${{ hashFiles('third_party/jerryscript/jerryscript/js_tooling/package.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Set artifact board name + id: artifact_board + run: echo "NAME=$(printf '%s' "$BOARD" | tr @ _)" >> "$GITHUB_OUTPUT" + env: + BOARD: ${{ matrix.board }} - name: Configure run: | if [ "${{ matrix.mode }}" == "mfg" ]; then - OPTS="--mfg --nohash" + OPTS="-DCONFIG_MFG=y -DCONFIG_LOG_HASHED=n" fi - ./waf configure --board ${{ matrix.board }} $OPTS + ./waf configure --board '${{ matrix.board }}' --variant=prf $OPTS - name: Build - run: ./waf build_prf + run: ./waf build - name: Bundle - run: ./waf bundle_prf + run: ./waf bundle - name: Store - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: - name: prf-${{ matrix.board }}-${{ matrix.mode }} + name: prf-${{ steps.artifact_board.outputs.NAME }}-${{ matrix.mode }} path: | build/**/*.elf build/**/*.pbz - build/prf/src/fw/tintin_fw_loghash_dict.json + build/src/fw/tintin_fw_loghash_dict.json - name: Get Build ID id: build_id run: | - echo "BUILD_ID=$(arm-none-eabi-readelf -n build/prf/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" + echo "BUILD_ID=$(readelf -n build/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" - name: Upload log hash dictionary - uses: Noelware/s3-action@2.3.1 - if: ${{ github.event_name == 'push' }} - with: - access-key-id: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} - secret-key: ${{ secrets.LOG_HASH_BUCKET_SECRET }} - endpoint: ${{ vars.LOG_HASH_BUCKET_ENDPOINT }} - bucket: ${{ vars.LOG_HASH_BUCKET_NAME }} - files: | - build/prf/src/fw/tintin_fw_loghash_dict.json - path-format: ${{ steps.build_id.outputs.BUILD_ID }}-${{ github.sha }}-prf.json + if: ${{ github.event_name == 'push' && github.repository == 'coredevices/PebbleOS' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.LOG_HASH_BUCKET_SECRET }} + AWS_DEFAULT_REGION: us-east-1 + run: | + pip install awscli + aws s3 cp build/src/fw/tintin_fw_loghash_dict.json \ + "s3://${{ vars.LOG_HASH_BUCKET_NAME }}/${{ steps.build_id.outputs.BUILD_ID }}-${{ github.sha }}-prf.json" \ + --endpoint-url "${{ vars.LOG_HASH_BUCKET_ENDPOINT }}" + + build-prf-status: + needs: [changes-prf, build-prf] + if: always() + runs-on: ubuntu-24.04 + steps: + - if: needs.build-prf.result == 'failure' || needs.build-prf.result == 'cancelled' + run: exit 1 diff --git a/.github/workflows/build-qemu-sdkshell.yml b/.github/workflows/build-qemu-sdkshell.yml new file mode 100644 index 0000000000..b7991a63cf --- /dev/null +++ b/.github/workflows/build-qemu-sdkshell.yml @@ -0,0 +1,87 @@ +name: Build Firmware (QEMU + SDK Shell) + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + changes-qemu-sdkshell: + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + outputs: + should-build: ${{ github.event_name == 'push' || steps.filter.outputs.src == 'true' }} + steps: + - uses: dorny/paths-filter@v4 + if: github.event_name == 'pull_request' + id: filter + with: + filters: | + src: + - '.github/workflows/build-qemu-sdkshell.yml' + - 'platform/**' + - 'resources/**' + - 'sdk/**' + - 'src/**' + - 'stored_apps/**' + - 'tools/**' + - 'third_party/**' + - 'waftools/**' + - 'waf' + - 'wscript' + + build-qemu-sdkshell: + needs: changes-qemu-sdkshell + if: needs.changes-qemu-sdkshell.outputs.should-build == 'true' + runs-on: ubuntu-24.04 + + container: + image: ghcr.io/coredevices/pebbleos-docker:v5 + + strategy: + matrix: + board: ["qemu_emery", "qemu_flint", "qemu_gabbro"] + + steps: + - name: Mark Github workspace as safe + run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" + + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + submodules: true + + - name: Install Python dependencies + run: | + pip install -U pip + pip install -r requirements.txt + + - name: Get npm cache directory + id: npm-cache-dir + shell: bash + run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT} + + - name: Configure + run: ./waf configure --board ${{ matrix.board }} -DCONFIG_SHELL_SDK=y + + - name: Build + run: ./waf build qemu_image_micro qemu_image_spi + + - name: Store + uses: actions/upload-artifact@v6 + with: + name: firmware-${{ matrix.board }}-qemu + path: | + build/qemu_micro_flash.bin + build/qemu_spi_flash.bin + + build-qemu-sdkshell-status: + needs: [changes-qemu-sdkshell, build-qemu-sdkshell] + if: always() + runs-on: ubuntu-24.04 + steps: + - if: needs.build-qemu-sdkshell.result == 'failure' || needs.build-qemu-sdkshell.result == 'cancelled' + run: exit 1 diff --git a/.github/workflows/build-qemu.yml b/.github/workflows/build-qemu.yml index a8c975d263..34e9d9a4d6 100644 --- a/.github/workflows/build-qemu.yml +++ b/.github/workflows/build-qemu.yml @@ -5,35 +5,51 @@ on: branches: [main] pull_request: branches: [main] - paths: - - '.github/workflows/build-qemu.yml' - - 'resources/**' - - 'sdk/**' - - 'src/**' - - 'stored_apps/**' - - 'tools/**' - - 'third_party/**' - - 'waftools/**' - - 'waf' - - 'wscript' jobs: - build: + changes-qemu: + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + outputs: + should-build: ${{ github.event_name == 'push' || steps.filter.outputs.src == 'true' }} + steps: + - uses: dorny/paths-filter@v4 + if: github.event_name == 'pull_request' + id: filter + with: + filters: | + src: + - '.github/workflows/build-qemu.yml' + - 'platform/**' + - 'resources/**' + - 'sdk/**' + - 'src/**' + - 'stored_apps/**' + - 'tools/**' + - 'third_party/**' + - 'waftools/**' + - 'waf' + - 'wscript' + + build-qemu: + needs: changes-qemu + if: needs.changes-qemu.outputs.should-build == 'true' runs-on: ubuntu-24.04 container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 strategy: matrix: - board: ["snowy_bb2", "spalding_bb2", "silk_bb2"] + board: ["qemu_flint", "qemu_emery", "qemu_gabbro"] steps: - name: Mark Github workspace as safe run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true @@ -48,24 +64,24 @@ jobs: shell: bash run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT} - - name: Node modules cache - uses: actions/cache@v4 - with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-node-${{ hashFiles('third_party/jerryscript/jerryscript/js_tooling/package.json') }} - restore-keys: | - ${{ runner.os }}-node- - - name: Configure - run: ./waf configure --board ${{ matrix.board }} --qemu + run: ./waf configure --board ${{ matrix.board }} - name: Build run: ./waf build qemu_image_micro qemu_image_spi - name: Store - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: firmware-${{ matrix.board }}-qemu path: | build/qemu_micro_flash.bin build/qemu_spi_flash.bin + + build-qemu-status: + needs: [changes-qemu, build-qemu] + if: always() + runs-on: ubuntu-24.04 + steps: + - if: needs.build-qemu.result == 'failure' || needs.build-qemu.result == 'cancelled' + run: exit 1 diff --git a/.github/workflows/build-translation-source.yml b/.github/workflows/build-translation-source.yml index ed9869e600..0c5602e1f4 100644 --- a/.github/workflows/build-translation-source.yml +++ b/.github/workflows/build-translation-source.yml @@ -16,14 +16,14 @@ jobs: BOARD_NAME: "asterix" container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 steps: - name: Mark Github workspace as safe run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml index ac8e9fe232..d71ad71236 100644 --- a/.github/workflows/compliance.yml +++ b/.github/workflows/compliance.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 @@ -16,5 +16,104 @@ jobs: - name: Install GitLint run: pip install gitlint - - name: Run GitLint + - name: Run GitLint run: gitlint --commits "${{ github.event.pull_request.base.sha }}..HEAD" + + ruff: + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Install ruff + run: pip install ruff + + - name: Run ruff format check + run: | + base="${{ github.event.pull_request.base.sha }}" + + # collect submodule paths to skip + submodules=$(git config --file .gitmodules --get-regexp '\.path$' \ + | awk '{print $2}') + + files=() + for file in $(git diff --name-only --diff-filter=d "$base"...HEAD -- '*.py'); do + skip=false + for sm in $submodules; do + case "$file" in "$sm"/*) skip=true; break ;; esac + done + if $skip; then continue; fi + files+=("$file") + done + + if [ ${#files[@]} -eq 0 ]; then + echo "No Python files changed." + exit 0 + fi + + ruff format --check "${files[@]}" + + license: + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Check SPDX license headers + run: | + base="${{ github.event.pull_request.base.sha }}" + failed_files=() + + # collect submodule paths to skip + submodules=$(git config --file .gitmodules --get-regexp '\.path$' \ + | awk '{print $2}') + + for file in $(git diff --name-only --diff-filter=d "$base"...HEAD -- \ + '*.c' '*.h' '*.S' '*.py' '*.sh'); do + # skip files inside git submodules + skip=false + for sm in $submodules; do + case "$file" in "$sm"/*) skip=true; break ;; esac + done + if $skip; then continue; fi + + if ! head -10 "$file" | grep -q "SPDX-License-Identifier: Apache-2.0"; then + # Resolve the file's pre-PR identity: either same path or + # the source of a rename. + origin="$file" + if ! git cat-file -e "$base":"$origin" 2>/dev/null; then + origin=$(git diff --find-renames --diff-filter=R --name-status \ + "$base"...HEAD | awk -v f="$file" '$3 == f {print $2}') + fi + + # If the file (or its rename source) existed before this PR and + # already lacked Apache-2.0, skip it as pre-existing. + if [ -n "$origin" ] && git cat-file -e "$base":"$origin" 2>/dev/null; then + if ! git show "$base":"$origin" | head -10 \ + | grep -q "SPDX-License-Identifier: Apache-2.0"; then + echo "::notice file=$file::Skipping: pre-existing non-Apache-2.0 license" + continue + fi + fi + echo "::error file=$file::Missing 'SPDX-License-Identifier: Apache-2.0' header" + failed_files+=("$file") + fi + done + + if [ ${#failed_files[@]} -ne 0 ]; then + echo "" + echo "${#failed_files[@]} file(s) missing Apache-2.0 SPDX license header:" + for f in "${failed_files[@]}"; do + echo " - $f" + done + echo "" + echo "Expected within the first 10 lines:" + echo " /* SPDX-License-Identifier: Apache-2.0 */" + exit 1 + fi diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 6ebf483452..61f057d6b3 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Pull translations from Crowdin uses: crowdin/github-action@v2 diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml new file mode 100644 index 0000000000..f18705bceb --- /dev/null +++ b/.github/workflows/nix.yml @@ -0,0 +1,76 @@ +name: Nix + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + changes-nix: + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + outputs: + should-build: ${{ steps.filter.outputs.src == 'true' }} + steps: + - name: Checkout + uses: actions/checkout@v5 + + - uses: dorny/paths-filter@v4 + id: filter + with: + filters: | + src: + - '.github/workflows/nix.yml' + - 'flake.nix' + - 'flake.lock' + + nix-shell: + needs: changes-nix + if: needs.changes-nix.outputs.should-build == 'true' + runs-on: ${{ matrix.os }} + timeout-minutes: 90 + + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15] + + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + submodules: true + + - name: Install Nix + uses: cachix/install-nix-action@v31 + + - name: Check flake + run: nix flake check + + # Running the SDK binaries verifies autoPatchelfHook resolved all of + # their shared-library dependencies. + - name: Smoke test SDK tools + run: | + nix develop --command bash -c ' + set -ex + arm-none-eabi-gcc --version + qemu-pebble --version + sftool --version + ' + + - name: Configure + run: nix develop --command ./waf configure --board asterix + + - name: Build + run: nix develop --command ./waf build + + nix-status: + needs: [changes-nix, nix-shell] + if: always() + runs-on: ubuntu-24.04 + steps: + - if: needs.nix-shell.result == 'failure' || needs.nix-shell.result == 'cancelled' + run: exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab38cbce6f..05a3a1ba33 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,92 +9,28 @@ env: MEMFAULT_CLI_VERSION: "1.6.0" jobs: - build-bootloader: - runs-on: ubuntu-24.04 - - container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 - - strategy: - matrix: - board: ["asterix"] - - steps: - - name: Mark Github workspace as safe - run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: true - - - name: Fetch tags - run: | - git fetch --tags --force - - - name: Install Python dependencies - run: | - pip install -U pip - pip install -r requirements.txt - - - name: Obtain platform name - id: get-platform - run: | - BOARD=${{ matrix.board }} - PLATFORM=${BOARD%%_*} - echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - - - name: Configure bootloader - working-directory: platform/${{ steps.get-platform.outputs.platform }}/boot - run: ./waf configure --board ${{ matrix.board }} - - - name: Build bootloader - working-directory: platform/${{ steps.get-platform.outputs.platform }}/boot - run: ./waf build - - - name: Copy bootloader artifacts - run: | - mkdir -p artifacts - cp platform/${{ steps.get-platform.outputs.platform }}/boot/build/tintin_boot.hex \ - artifacts/bootloader_${{ matrix.board }}_${{github.ref_name}}.hex - - - name: Configure bootloader (nowatchdog) - working-directory: platform/${{ steps.get-platform.outputs.platform }}/boot - run: ./waf configure --board ${{ matrix.board }} --nowatchdog - - - name: Build bootloader (nowatchdog) - working-directory: platform/${{ steps.get-platform.outputs.platform }}/boot - run: ./waf build - - - name: Copy bootloader artifacts (nowatchdog) - run: | - mkdir -p artifacts - cp platform/${{ steps.get-platform.outputs.platform }}/boot/build/tintin_boot.hex \ - artifacts/bootloader_nowatchdog_${{ matrix.board }}_${{github.ref_name}}.hex - - - name: Store - uses: actions/upload-artifact@v4 - with: - path: artifacts - name: artifacts-bootloader-${{ matrix.board }} - build-prf: runs-on: ubuntu-24.04 container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 strategy: matrix: - board: ["asterix", "obelix"] + board: + - asterix + - obelix@dvt + - obelix@pvt + - getafix@evt + - getafix@dvt + - getafix@dvt2 steps: - name: Mark Github workspace as safe run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true @@ -108,69 +44,83 @@ jobs: pip install -U pip pip install -r requirements.txt + - name: Set artifact board name + id: artifact_board + run: echo "NAME=$(printf '%s' "$BOARD" | tr @ _)" >> "$GITHUB_OUTPUT" + env: + BOARD: ${{ matrix.board }} + - name: Configure - run: ./waf configure --board ${{ matrix.board }} --release + run: ./waf configure --board '${{ matrix.board }}' --variant=prf -DCONFIG_RELEASE=y - name: Build PRF - run: ./waf build_prf + run: ./waf build - name: Bundle PRF - run: ./waf bundle_prf + run: ./waf bundle - name: Copy PRF artifacts run: | mkdir -p artifacts - cp build/prf/src/fw/tintin_fw.hex artifacts/prf_${{ matrix.board }}_${{github.ref_name}}.hex - cp build/prf/src/fw/tintin_fw.bin artifacts/prf_${{ matrix.board }}_${{github.ref_name}}.bin - cp build/prf/src/fw/tintin_fw.elf artifacts/prf_${{ matrix.board }}_${{github.ref_name}}.elf - cp build/prf/*.pbz artifacts - cp build/prf/src/fw/tintin_fw_loghash_dict.json artifacts/prf_${{ matrix.board }}_${{github.ref_name}}_loghash_dict.json + cp build/src/fw/tintin_fw.hex artifacts/prf_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}.hex + cp build/src/fw/tintin_fw.bin artifacts/prf_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}.bin + cp build/src/fw/tintin_fw.elf artifacts/prf_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}.elf + cp build/*.pbz artifacts + cp build/src/fw/tintin_fw_loghash_dict.json artifacts/prf_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}_loghash_dict.json - name: Get PRF Build ID id: prf_build_id run: | - echo "BUILD_ID=$(arm-none-eabi-readelf -n build/prf/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" + echo "BUILD_ID=$(readelf -n build/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" - name: Upload PRF log hash dictionary - uses: Noelware/s3-action@2.3.1 - with: - access-key-id: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} - secret-key: ${{ secrets.LOG_HASH_BUCKET_SECRET }} - endpoint: ${{ vars.LOG_HASH_BUCKET_ENDPOINT }} - bucket: ${{ vars.LOG_HASH_BUCKET_NAME }} - files: | - build/prf/src/fw/tintin_fw_loghash_dict.json - path-format: ${{ steps.prf_build_id.outputs.BUILD_ID }}-${{ github.sha }}-prf.json + env: + AWS_ACCESS_KEY_ID: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.LOG_HASH_BUCKET_SECRET }} + AWS_DEFAULT_REGION: us-east-1 + run: | + pip install awscli + aws s3 cp build/src/fw/tintin_fw_loghash_dict.json \ + "s3://${{ vars.LOG_HASH_BUCKET_NAME }}/${{ steps.prf_build_id.outputs.BUILD_ID }}-${{ github.sha }}-prf.json" \ + --endpoint-url "${{ vars.LOG_HASH_BUCKET_ENDPOINT }}" - name: Configure PRF MFG - run: ./waf configure --board ${{ matrix.board }} --mfg --nohash --release + run: ./waf configure --board '${{ matrix.board }}' --variant=prf -DCONFIG_MFG=y -DCONFIG_LOG_HASHED=n -DCONFIG_RELEASE=y - name: Build MFG PRF - run: ./waf build_prf + run: ./waf build - name: Copy MFG PRF artifacts run: | mkdir -p artifacts - cp build/prf/src/fw/tintin_fw.hex artifacts/prf_mfg_${{ matrix.board }}_${{github.ref_name}}.hex - cp build/prf/src/fw/tintin_fw.bin artifacts/prf_mfg_${{ matrix.board }}_${{github.ref_name}}.bin - cp build/prf/src/fw/tintin_fw.elf artifacts/prf_mfg_${{ matrix.board }}_${{github.ref_name}}.elf + cp build/src/fw/tintin_fw.hex artifacts/prf_mfg_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}.hex + cp build/src/fw/tintin_fw.bin artifacts/prf_mfg_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}.bin + cp build/src/fw/tintin_fw.elf artifacts/prf_mfg_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}.elf - name: Store - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: - name: artifacts-prf-${{ matrix.board }} + name: artifacts-prf-${{ steps.artifact_board.outputs.NAME }} path: artifacts build-firmware: runs-on: ubuntu-24.04 container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 strategy: matrix: - board: ["asterix", "obelix"] - slot: [0, 1] + board: + - asterix + - obelix@dvt + - obelix@pvt + - getafix@evt + - getafix@dvt + - getafix@dvt2 + slot: + - 0 + - 1 exclude: - board: "asterix" @@ -181,7 +131,7 @@ jobs: run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true @@ -200,16 +150,25 @@ jobs: shell: bash run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT} - - name: Node modules cache - uses: actions/cache@v4 - with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-node-${{ hashFiles('third_party/jerryscript/jerryscript/js_tooling/package.json') }} - restore-keys: | - ${{ runner.os }}-node- + - name: Set artifact board name + id: artifact_board + run: echo "NAME=$(printf '%s' "$BOARD" | tr @ _)" >> "$GITHUB_OUTPUT" + env: + BOARD: ${{ matrix.board }} + + - name: Set slot suffix + id: slot_suffix + run: | + NEEDS_SUFFIX="obelix_dvt obelix_pvt getafix_evt getafix_dvt getafix_dvt2" + + if echo $NEEDS_SUFFIX | grep -wq "${{ steps.artifact_board.outputs.NAME }}"; then + echo "SLOT_SUFFIX=_slot${{ matrix.slot }}" >> "$GITHUB_OUTPUT" + else + echo "SLOT_SUFFIX=" >> "$GITHUB_OUTPUT" + fi - name: Configure - run: ./waf configure --board ${{ matrix.board }} --slot ${{ matrix.slot }} --release + run: ./waf configure --board '${{ matrix.board }}' --slot ${{ matrix.slot }} -DCONFIG_RELEASE=y - name: Build firmware run: ./waf build @@ -220,35 +179,36 @@ jobs: - name: Copy firmware artifacts run: | mkdir -p artifacts - cp build/src/fw/tintin_fw.hex artifacts/firmware_${{ matrix.board }}_${{github.ref_name}}_slot${{ matrix.slot }}.hex - cp build/src/fw/tintin_fw.bin artifacts/firmware_${{ matrix.board }}_${{github.ref_name}}_slot${{ matrix.slot }}.bin - cp build/src/fw/tintin_fw.elf artifacts/firmware_${{ matrix.board }}_${{github.ref_name}}_slot${{ matrix.slot }}.elf + cp build/src/fw/tintin_fw.hex artifacts/firmware_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}${{ steps.slot_suffix.outputs.SLOT_SUFFIX }}.hex + cp build/src/fw/tintin_fw.bin artifacts/firmware_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}${{ steps.slot_suffix.outputs.SLOT_SUFFIX }}.bin + cp build/src/fw/tintin_fw.elf artifacts/firmware_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}${{ steps.slot_suffix.outputs.SLOT_SUFFIX }}.elf cp build/*.pbz artifacts - cp build/src/fw/tintin_fw_loghash_dict.json artifacts/firmware_${{ matrix.board }}_${{github.ref_name}}_slot${{ matrix.slot }}_loghash_dict.json + cp build/src/fw/tintin_fw_loghash_dict.json artifacts/firmware_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}${{ steps.slot_suffix.outputs.SLOT_SUFFIX }}_loghash_dict.json - name: Get Build ID id: build_id run: | - echo "BUILD_ID=$(arm-none-eabi-readelf -n build/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" + echo "BUILD_ID=$(readelf -n build/src/fw/tintin_fw.elf | sed -n -e 's/^.*Build ID: //p')" >> "$GITHUB_OUTPUT" - name: Upload log hash dictionary - uses: Noelware/s3-action@2.3.1 - with: - access-key-id: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} - secret-key: ${{ secrets.LOG_HASH_BUCKET_SECRET }} - endpoint: ${{ vars.LOG_HASH_BUCKET_ENDPOINT }} - bucket: ${{ vars.LOG_HASH_BUCKET_NAME }} - files: | - build/src/fw/tintin_fw_loghash_dict.json - path-format: ${{ steps.build_id.outputs.BUILD_ID }}-${{ github.sha }}-normal.json + if: ${{ github.repository == 'coredevices/PebbleOS' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.LOG_HASH_BUCKET_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.LOG_HASH_BUCKET_SECRET }} + AWS_DEFAULT_REGION: us-east-1 + run: | + pip install awscli + aws s3 cp build/src/fw/tintin_fw_loghash_dict.json \ + "s3://${{ vars.LOG_HASH_BUCKET_NAME }}/${{ steps.build_id.outputs.BUILD_ID }}-${{ github.sha }}-normal.json" \ + --endpoint-url "${{ vars.LOG_HASH_BUCKET_ENDPOINT }}" - name: Store - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: - name: artifacts-${{ matrix.board }}-slot${{ matrix.slot }} + name: artifacts-${{ steps.artifact_board.outputs.NAME }}${{ steps.slot_suffix.outputs.SLOT_SUFFIX }} path: artifacts - - name: Upload to Memfault + - name: Upload symbols to Memfault run: | pip install memfault-cli==${MEMFAULT_CLI_VERSION} @@ -257,37 +217,41 @@ jobs: --org ${{ secrets.MEMFAULT_ORG }} \ --project ${{ secrets.MEMFAULT_PROJECT }} \ upload-mcu-symbols \ - artifacts/firmware_${{ matrix.board }}_${{github.ref_name}}_slot${{ matrix.slot }}.elf - - memfault \ - --org-token ${{ secrets.MEMFAULT_ORG_TOKEN }} \ - --org ${{ secrets.MEMFAULT_ORG }} \ - --project ${{ secrets.MEMFAULT_PROJECT }} \ - upload-ota-payload \ - --hardware-version ${{ matrix.board }}-slot${{ matrix.slot }} \ - --software-type pebbleos \ - --software-version ${{ github.ref_name }} \ - artifacts/normal_${{ matrix.board }}_${{ github.ref_name }}_slot${{ matrix.slot }}.pbz + artifacts/firmware_${{ steps.artifact_board.outputs.NAME }}_${{github.ref_name}}${{ steps.slot_suffix.outputs.SLOT_SUFFIX }}.elf release: runs-on: ubuntu-24.04 needs: - - build-bootloader - build-prf - build-firmware permissions: contents: write steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 - name: Display artifacts run: ls -R + - name: Merge slot-specific pbz files + run: | + NEEDS_MERGE="obelix_dvt obelix_pvt getafix_evt getafix_dvt getafix_dvt2" + + mkdir artifacts-merged + for board in $NEEDS_MERGE; do + python3 tools/merge_pbz.py \ + --slot0-pbz "artifacts-${board}_slot0/normal_${board}_${{ github.ref_name }}_slot0.pbz" \ + --slot1-pbz "artifacts-${board}_slot1/normal_${board}_${{ github.ref_name }}_slot1.pbz" \ + --output "artifacts-merged/normal_${board}_${{ github.ref_name }}.pbz" + done + - name: Create release - uses: softprops/action-gh-release@v2.2.2 + uses: softprops/action-gh-release@v3.0.0 with: files: artifacts-*/* @@ -295,6 +259,25 @@ jobs: run: | pip install memfault-cli==${MEMFAULT_CLI_VERSION} + for file in artifacts-*/normal*.pbz; do + # Skip slot-specific pbzs + if echo $file | grep -q "_slot"; then + continue + fi + + BOARD=$(echo $file | sed -n -e 's/^.*normal_\([a-zA-Z0-9_]*\)_v.*\.pbz$/\1/p') + + memfault \ + --org-token ${{ secrets.MEMFAULT_ORG_TOKEN }} \ + --org ${{ secrets.MEMFAULT_ORG }} \ + --project ${{ secrets.MEMFAULT_PROJECT }} \ + upload-ota-payload \ + --hardware-version $BOARD \ + --software-type pebbleos \ + --software-version ${{ github.ref_name }} \ + $file + done + memfault \ --org-token ${{ secrets.MEMFAULT_ORG_TOKEN }} \ --org ${{ secrets.MEMFAULT_ORG }} \ diff --git a/.github/workflows/stale-pr.yml b/.github/workflows/stale-pr.yml index de41d78c29..3a4ffba976 100644 --- a/.github/workflows/stale-pr.yml +++ b/.github/workflows/stale-pr.yml @@ -11,7 +11,7 @@ jobs: stale: runs-on: ubuntu-24.04 steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 with: stale-pr-message: 'This pull request has been marked as stale because it has been open (more than) 30 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 7 days. Note, that you can always re-open a closed pull request at any time.' days-before-pr-stale: 30 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 070b72196f..aab11f034f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,32 +5,49 @@ on: branches: [main] pull_request: branches: [main] - paths: - - '.github/workflows/test.yml' - - 'resources/**' - - 'sdk/**' - - 'src/**' - - 'stored_apps/**' - - 'tools/**' - - 'third_party/**' - - 'waftools/**' - - 'waf' - - 'wscript' env: - TEST_BOARD: 'snowy_bb2' + TEST_BOARD: 'qemu_gabbro' jobs: - build: + changes-test: + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + outputs: + should-test: ${{ github.event_name == 'push' || steps.filter.outputs.src == 'true' }} + steps: + - uses: dorny/paths-filter@v4 + if: github.event_name == 'pull_request' + id: filter + with: + filters: | + src: + - '.github/workflows/test.yml' + - 'platform/**' + - 'resources/**' + - 'sdk/**' + - 'src/**' + - 'stored_apps/**' + - 'tests/**' + - 'tools/**' + - 'third_party/**' + - 'waftools/**' + - 'waf' + - 'wscript' + + build-test: + needs: changes-test + if: needs.changes-test.outputs.should-test == 'true' runs-on: ubuntu-24.04 container: - image: ghcr.io/pebble-dev/pebbleos-docker:v1 + image: ghcr.io/coredevices/pebbleos-docker:v5 steps: - name: Mark Github workspace as safe run: git config --system --add safe.directory "${GITHUB_WORKSPACE}" - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 submodules: true @@ -44,18 +61,28 @@ jobs: run: ./waf configure --board ${{env.TEST_BOARD}} - name: Run tests - run: ./waf test - continue-on-error: true + # -k keeps going after a failing test so the run reports every failure, + # not just the first. The job still fails if anything failed. + run: ./waf test -k - name: Publish Test Report - uses: mikepenz/action-junit-report@v5 - if: success() || failure() + uses: mikepenz/action-junit-report@v6 + if: (!cancelled()) with: report_paths: build/test/junit.xml annotate_only: true - name: Store failed test images - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 + if: (!cancelled()) with: name: failed_diff_images path: build/test/tests/failed/*-diff.png + + test-status: + needs: [changes-test, build-test] + if: always() + runs-on: ubuntu-24.04 + steps: + - if: needs.build-test.result == 'failure' || needs.build-test.result == 'cancelled' + run: exit 1 diff --git a/.gitignore b/.gitignore index 5d0d70f3b9..569195ae17 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ a.out tools/font/ttf roundrect.h log.txt -openocd.cfg .DS_Store tools/bitmaps/*.h tools/bitmaps/*.pbi @@ -63,3 +62,7 @@ waflib.zip docs/_build artifacts/ + +.claude/settings.local.json +.envrc +.direnv \ No newline at end of file diff --git a/.gitlint b/.gitlint index bf75b765a2..2cafa89312 100644 --- a/.gitlint +++ b/.gitlint @@ -1,4 +1,5 @@ [general] +extra-path=tools/gitlint contrib=contrib-body-requires-signed-off-by,contrib-disallow-cleanup-commits ignore-merge-commits=false ignore-fixup-commits=false diff --git a/.gitmodules b/.gitmodules index cb9641c02f..41ee2d4a0d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,9 @@ [submodule "src/fw/vendor/CMSIS"] path = third_party/cmsis_core/CMSIS url = https://github.com/ARM-software/CMSIS_5.git -[submodule "src/fw/vendor/stm32-sdk"] - path = third_party/hal_stm32/stm32-sdk - url = https://github.com/pebble-dev/stm32-sdk.git [submodule "src/bluetooth-fw/nimble/vendor/mynewt-nimble"] path = third_party/nimble/mynewt-nimble url = https://github.com/coredevices/mynewt-nimble.git -[submodule "src/bluetooth-fw/nimble/vendor/ti-service-packs"] - path = third_party/ti_bt_sp/ti-service-packs - url = https://github.com/coredevices/ti-bt-service-packs.git [submodule "third_party/hal_nordic/nrfx"] path = third_party/hal_nordic/nrfx url = https://github.com/NordicSemiconductor/nrfx @@ -24,7 +18,7 @@ url = https://github.com/MersenneTwister-Lab/TinyMT [submodule "third_party/hal_sifli/SiFli-SDK"] path = third_party/hal_sifli/SiFli-SDK - url = https://github.com/OpenSiFli/SiFli-SDK.git + url = https://github.com/coredevices/SiFli-SDK.git [submodule "third_party/memfault/memfault-firmware-sdk"] path = third_party/memfault/memfault-firmware-sdk url = https://github.com/memfault/memfault-firmware-sdk.git @@ -40,3 +34,9 @@ [submodule "third_party/hal_lsm6dso/lsm6dso-pid"] path = third_party/hal_lsm6dso/lsm6dso-pid url = https://github.com/STMicroelectronics/lsm6dso-pid.git +[submodule "third_party/resources/iconography"] + path = third_party/resources/iconography + url = https://github.com/pebble-dev/iconography +[submodule "third_party/moddable/moddable"] + path = third_party/moddable/moddable + url = https://github.com/coredevices/moddable.git diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9878d8227f..d7dbe1fc5a 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,16 +1,5 @@ -# Copyright 2025 Core Devices LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2025 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..7066e0266f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,86 @@ +# PebbleOS + +PebbleOS is the operating system running on Pebble smartwatches. + +## Organization + +- `docs`: project documentation +- `python_libs`: tools used in multiple areas, e.g. log dehashing, console, etc. +- `resources`: firmware resources (icons, fonts, etc.) +- `sdk`: application SDK generation files +- `src`: firmware source +- `tests`: tests +- `third_party`: third-party code in git submodules, also includes glue code +- `tools`: a variety of tools or scripts used in multiple areas, from build + system, tests, etc. +- `waftools`: scripts used by the build system + +## Code style + +- clang-format for C code +- ruff for Python code +- Keep code comments short and concise. Extended descriptions can be kept in + the Git commit message. +- Do not put references to issues in the code, only add those to the Git commit message. + +## Logging + +- `PBL_LOG_WRN` / `PBL_LOG_ERR` are for warnings and errors — use them as + the names suggest. +- Default to `PBL_LOG_DBG` for routine lifecycle / state-transition logs. + Reserve `PBL_LOG_INFO` for events that genuinely warrant attention in a + default-level log capture; if a code path can fire repeatedly under + normal use (e.g. play/pause spam, frequent state changes), it must not + log at INFO. + +## Firmware development + +- Configure: `./pbl configure --board BOARD_NAME` + + - Board names can be obtained from `./pbl --help` + - `-DCONFIG_RELEASE=y` enables release mode + - `-DCONFIG_MFG=y` enables manufacturing mode + - `--variant=normal|prf` selects build variant (default: normal) + +- Build firmware: `./pbl build` +- Run tests: `./pbl test` + +## Adding a new SDK function + +When exposing a new function to third-party apps (i.e. anything declared +in an `applib/` header that user apps can call), three things must change +together — the firmware build alone won't surface it to apps: + +1. **Implement the applib wrapper and syscall** — add the function to the + appropriate `src/fw/applib/.../.c/.h`, declare the syscall in + `src/fw/syscall/syscall.h`, and define it with `DEFINE_SYSCALL` in + `src/fw/syscall/syscall_.c`. +2. **Register the symbol** in + `tools/generate_native_sdk/exported_symbols.json` under the matching + group, with an `addedRevision` matching the new SDK revision. +3. **Bump the SDK revision** in + `src/fw/process_management/pebble_process_info.h`: increment + `PROCESS_INFO_CURRENT_SDK_VERSION_MINOR` and add a `// sdk.major:0xN + .minor:0xM -- (rev )` comment line above the + `#define`. The revision number in the comment must match + `addedRevision` from step 2. + +Forgetting steps 2 or 3 means the function compiles into the firmware +but is invisible to the app SDK build, so third-party apps can't link +against it. + +## Git rules + +Main rules: + +- Commit using `-s` git option, so commits have `Signed-Off-By` +- Always indicate commit is co-authored by the current AI model +- Commit in small chunks, trying to preserve bisectability +- Commit format is `area: short description`, with longer description in the + body if necessary +- Run `gitlint` on every commit to verify rules are followed + +Others: + +- If fixing Linear or GitHub issues, include in the commit body a line with + `Fixes XXX`, where XXX is the issue number. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 584d4359b4..aec5209087 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,7 @@ # Contribution Guidelines +Thank you for your interest in contributing to PebbleOS! We welcome contributions of all kinds, including bug fixes, new features, documentation improvements, and more. To ensure a smooth contribution process, please follow the guidelines outlined below. + ## Developer Certification of Origin (DCO) To make a good faith effort to ensure licensing criteria are met, this @@ -54,8 +56,7 @@ Signed-off-by: Your Name For your commits, replace: -- `Your Name` with your legal name (pseudonyms, hacker handles, and the - names of groups are not allowed) +- `Your Name` with a known identity (sorry, no anonymous contributions.) - `your.email@example.com` with the same email address you are using to author the commit (CI will fail if there is no match) @@ -67,3 +68,25 @@ Additional requirements: - If you are altering an existing commit created by someone else, you must add your Signed-off-by: line without removing the existing one. + +## Commit Requirements +- All commits must be atomic and self-contained. Each commit should represent a single logical change to the codebase. Avoid bundling multiple unrelated changes into a single commit. +- Each commit message must be in the format of "area: description", where "area" is a short identifier for the part of the codebase being changed (e.g., "services" or "applib") and "description" is a brief summary of the change. +- Each commit message must include the appropriate DCO sign-off as described above. +- If your commit addresses a specific issue, please include a reference to the issue number in the commit message (e.g., "Fixes #123"). +- If your commit includes changes to documentation, please ensure that the documentation is updated accordingly and that the commit message reflects this. + +## Generative AI usage +The use of large language models (LLMs) is acceptable **if and only if** a thorough manual review is performed. **By submitting a pull request, you are guaranteeing the following:** +- You have *personally* reviewed all generated code to make sure that it will not cause undue technical issues, including testing for edge cases, ensuring data integrity, and verifying that performance standards are maintained. +- You have ensured that PebbleOS's UX standards are not meaningfully degraded by this change. +- You have ensured that any changes to the user interface are in keeping with the design of the rest of the app, or have provided good reason to change them. +- You fully understand the content of the pull request and are prepared to explain it. +- You have verified that the change does not reduce overall code quality or maintainability. +- You own the rights to all content provided, or the license or attribution from the licensed content is properly included. + +The following rules additionally apply to contributions made in whole or part using generative AI: +- The use of generative AI is clearly disclosed with one or more of the following: + - Co-authored tag on relevant commits (eg. `Co-Authored-By: Claude Opus 4.7 ` in the commit description, as generated by Claude Code) +- All submitted text is in English. We do not have the capacity to review text or translations for other languages at this time and we do not trust LLMs to handle this correctly. +- No AI-generated images, icons, SVG files, or other critically artistic works are included in the content of the pull request. \ No newline at end of file diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000000..72ef0a6136 --- /dev/null +++ b/Kconfig @@ -0,0 +1,143 @@ +mainmenu "PebbleOS Configuration" + +# Per-SoC symbol defaults; loaded first so they take precedence. +osource "src/fw/soc/*/Kconfig.defconfig" + +config RELEASE + bool "Release build" + help + Whether this is a release build. Pass + `-DCONFIG_RELEASE=y` to `./waf configure` to enable. Gates + features like Memfault crash reporting that should only run on + shipping firmware. + +config RECOVERY_FW + bool "Recovery firmware (PRF)" + help + Build the recovery firmware variant. Set via prj_prf.conf when + configuring with --variant=prf; do not set manually. + +config MFG + bool "Manufacturing firmware" + help + Enable manufacturing-only functionality in the PRF build. PRF + and MFG firmwares are mostly the same, so MFG builds also + define the recovery-firmware symbols. + +config PBLBOOT + bool "PBLBOOT bootloader" + help + Build for the PBLBOOT bootloader: prepend the PBLBOOT image + header to the firmware binary and assume a two-slot flash + layout with direct XIP. Disable for boards whose SoC bootloader + consumes a raw image and uses a single firmware slot. + +menu "Compiler options" + +config LTO + bool "Link-time optimization" + +choice + prompt "Optimization level" + default SIZE_OPTIMIZATIONS + +config SIZE_OPTIMIZATIONS + bool "Optimize for size (-Os)" + +config DEBUG_OPTIMIZATIONS + bool "Optimize for debugging (-Og)" + +config NO_OPTIMIZATIONS + bool "No optimizations (-O0)" + help + Previously known as "fat firmware". Requires 1M of onboard + flash. + +endchoice + +config COMPILER_SAVE_TEMPS + bool "Save compiler temporaries" + help + Save *.i and *.s files during compilation. + +config DEBUG_INFO + bool "Debug information" + default y + help + Compile with -g3 debug information. + +endmenu + +config IS_BIGBOARD + bool "Bigboard revision" + help + Engineering/development PCB revision of a board (`_bb` suffix + in the board name). Gates bigboard-only behaviour that differs + from the shipping board. + +# Source the active board's Kconfig and its per-board defaults; kconfig.py +# exports $(BOARD) and $(BOARD_REVISION) from the --board argument. +rsource "boards/$(BOARD)/Kconfig" +osource "boards/$(BOARD)/Kconfig.$(BOARD_REVISION)" +rsource "boards/$(BOARD)/Kconfig.defconfig" +osource "boards/$(BOARD)/Kconfig.defconfig.$(BOARD_REVISION)" + +config BOARD_FAMILY_ASTERIX + bool + help + Hidden symbol selected by boards in the Asterix family. + +config BOARD_FAMILY_OBELIX + bool + help + Hidden symbol selected by boards in the Obelix family. + +config BOARD_FAMILY_GETAFIX + bool + help + Hidden symbol selected by boards in the Getafix family. + +config BOARD_FAMILY_NAME + string + help + Lowercase token identifying the board family (or the single + board, for QEMU boards). Used for resource path lookups + and string outputs. Set per board in + boards//Kconfig.defconfig. + +config QEMU_MACHINE + string + depends on QEMU + help + QEMU machine identifier passed to qemu-system-arm via the + `-machine` flag. Set per board in + boards//Kconfig.defconfig. + +choice + prompt "SDK platform" +config PLATFORM_EMERY + bool "Emery" +config PLATFORM_FLINT + bool "Flint" +config PLATFORM_GABBRO + bool "Gabbro" +endchoice + +choice + prompt "Screen color depth" + +config SCREEN_COLOR_DEPTH_BITS_1 + bool "1-bit (monochrome)" + +config SCREEN_COLOR_DEPTH_BITS_8 + bool "8-bit (palette)" + +endchoice + +config SCREEN_COLOR_DEPTH_BITS + int + default 1 if SCREEN_COLOR_DEPTH_BITS_1 + default 8 if SCREEN_COLOR_DEPTH_BITS_8 + +rsource "src/Kconfig" +rsource "third_party/Kconfig" diff --git a/README.md b/README.md index 4b11a0955f..f1662f6e19 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@

- + - +

## Resources @@ -19,15 +19,15 @@ Here's a quick summary of resources to help you find your way around: ### Getting Started - 📖 [Documentation](https://pebbleos-core.readthedocs.io/en/latest) -- 🚀 [Getting Started Guide](https://pebbleos-core.readthedocs.io/en/latest/getting_started.html) +- 🚀 [Prerequisites Guide](https://pebbleos-core.readthedocs.io/en/latest/development/getting_started.html) ### Code and Development -- ⌚ [Source Code Repository](https://github.com/coredevices/pebbleos) -- 🐛 [Issue Tracker](https://github.com/coredevices/pebbleos/issues) +- ⌚ [Source Code Repository](https://github.com/coredevices/PebbleOS) +- 🐛 [Issue Tracker](https://github.com/coredevices/PebbleOS/issues) - 🤝 [Contribution Guide](CONTRIBUTING.md) ### Community and Support - 💬 [Discord](https://discordapp.com/invite/aRUAYFN) -- 👥 [Discussions](https://github.com/coredevices/pebbleos/discussions) +- 👥 [Discussions](https://github.com/coredevices/PebbleOS/discussions) diff --git a/SDK_VERSION b/SDK_VERSION new file mode 100644 index 0000000000..c946ee6160 --- /dev/null +++ b/SDK_VERSION @@ -0,0 +1 @@ +0.1.6 diff --git a/applib-targets/emscripten/emscripten_app.c b/applib-targets/emscripten/emscripten_app.c deleted file mode 100644 index 0215a7fbd4..0000000000 --- a/applib-targets/emscripten/emscripten_app.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "emscripten_app.h" -#include "emscripten_graphics.h" -#include "emscripten_resources.h" -#include "emscripten_tick_timer_service.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" - -#include - -__attribute__((weak)) int app_main(void) { - Window *window = window_create(); - app_window_stack_push(window, false); - app_event_loop(); - return 0; -} - -static void prv_event_loop(void) { - Window *window = app_window_stack_get_top_window(); - if (window && window->is_render_scheduled) { - GContext *ctx = rocky_api_graphics_get_gcontext(); - layer_render_tree(&window->layer, ctx); - window->is_render_scheduled = false; - EM_ASM( - // Implemented by html-binding.js: - if (Module.frameBufferMarkDirty) { - Module.frameBufferMarkDirty() - } - ); - } -} - -void emx_app_init(void) { - emx_graphics_init(); - emx_resources_init(); - emx_tick_timer_service_init(); -} - -void emx_app_deinit(void) { - emx_resources_deinit(); -} - -void emx_app_event_loop(void) { - emscripten_set_main_loop(prv_event_loop, - 0, /* using window.requestAnimationFrame() */ - 1 /* Simulate infinite loop */); -} - -int main(int argc, char **argv) { - emx_app_init(); - app_main(); - emx_app_deinit(); - - return 0; -} diff --git a/applib-targets/emscripten/emscripten_app.h b/applib-targets/emscripten/emscripten_app.h deleted file mode 100644 index 65ba245f88..0000000000 --- a/applib-targets/emscripten/emscripten_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void emx_app_init(void); - -void emx_app_event_loop(void); diff --git a/applib-targets/emscripten/emscripten_clock.c b/applib-targets/emscripten/emscripten_clock.c deleted file mode 100644 index 6dec898226..0000000000 --- a/applib-targets/emscripten/emscripten_clock.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/clock.h" - -static bool s_clock_is_24h_style; - -bool clock_is_24h_style(void) { - return s_clock_is_24h_style; -} - -void clock_set_24h_style(bool is_24h_style) { - s_clock_is_24h_style = is_24h_style; -} diff --git a/applib-targets/emscripten/emscripten_graphics.c b/applib-targets/emscripten/emscripten_graphics.c deleted file mode 100644 index 6df3d9d8fa..0000000000 --- a/applib-targets/emscripten/emscripten_graphics.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "applib/fonts/fonts.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/gtypes.h" -#include "applib/graphics/framebuffer.h" -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/gdraw_command_sequence.h" -#include "applib/graphics/gdraw_command_frame.h" -#include "applib/graphics/gbitmap_sequence.h" -#include "applib/graphics/text.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" -#include "applib/rockyjs/api/rocky_api_util.h" -#include "applib/rockyjs/api/rocky_api_errors.h" - -#include "jerry-api.h" -#include "process_state/app_state/app_state.h" - -#include - -static GContext s_gcontext = {}; -// FIXME: PBL-43469 Support for changing platforms will require a dynamic framebuffer. -static FrameBuffer s_framebuffer = {}; -static TextRenderState s_text_render_state = {}; -static UnobstructedAreaState s_unobstructed_area_state = {}; - -// FIXME: Right now, rocky only supports 1 window anyways -static Window *s_top_window; - -GContext *emx_graphics_get_gcontext(void) { - return &s_gcontext; -} - -void *emx_graphics_get_pixels(void) { - return s_gcontext.dest_bitmap.addr; -} - -TextRenderState *app_state_get_text_render_state(void) { - return &s_text_render_state; -} - -Layer** app_state_get_layer_tree_stack(void) { - static Layer *layer_tree_stack[LAYER_TREE_STACK_SIZE]; - return layer_tree_stack; -} - -Layer** kernel_applib_get_layer_tree_stack(void) { - PBL_ASSERT(0, "Not expected to be called when compiling to applib-emscripten..."); - return NULL; -} - -// FIXME: Emscripten is cannot deal with two files with the same name -// (even if the path is different) The framebuffer.c files end up not -// getting linked in. A longer term fix would be to rename the object -// file in WAF -volatile const int FrameBuffer_MaxX = DISP_COLS; -volatile const int FrameBuffer_MaxY = DISP_ROWS; -void framebuffer_mark_dirty_rect(FrameBuffer* f, GRect rect) { -} - -size_t framebuffer_get_size_bytes(FrameBuffer *f) { - return FRAMEBUFFER_SIZE_BYTES; -} - -Window *app_window_stack_get_top_window(void) { - return s_top_window; -} - -void app_window_stack_push(Window *window, bool animated) { - PBL_ASSERT(!s_top_window, "Already have a window"); - s_top_window = window; -} - -GContext *graphics_context_get_current_context(void) { - return &s_gcontext; -} - -// TODO: PBL-43467 Support a user-specified unobstructed area -UnobstructedAreaState *app_state_get_unobstructed_area_state(void) { - return &s_unobstructed_area_state; -} - -void unobstructed_area_service_get_area(UnobstructedAreaState *state, GRect *area_out) { - *area_out = state->area; -} - -// FIXME: PBL-43496 This should take width, height, and format to dynamically -// allocate our framebuffer GBitmap and support changing platforms. -void emx_graphics_init(void) { - framebuffer_init(&s_framebuffer, &(GSize) {DISP_COLS, DISP_ROWS}); - memset(s_framebuffer.buffer, 0xff, FRAMEBUFFER_SIZE_BYTES); - framebuffer_dirty_all(&s_framebuffer); - graphics_context_init(&s_gcontext, &s_framebuffer, GContextInitializationMode_App); - - s_unobstructed_area_state = (UnobstructedAreaState) { - .area = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 144, .h = 168 }, - }, - }; -} diff --git a/applib-targets/emscripten/emscripten_graphics.h b/applib-targets/emscripten/emscripten_graphics.h deleted file mode 100644 index 83e0ec0bf8..0000000000 --- a/applib-targets/emscripten/emscripten_graphics.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" -#include "process_state/app_state/app_state.h" - -void emx_graphics_init(void); -GContext *emx_graphics_get_gcontext(void); -void *emx_graphics_get_pixes(void); -TextRenderState *app_state_get_text_render_state(void); -void emx_graphics_call_canvas_update_proc(void); diff --git a/applib-targets/emscripten/emscripten_jerry_api.c b/applib-targets/emscripten/emscripten_jerry_api.c deleted file mode 100644 index 2bc5eef965..0000000000 --- a/applib-targets/emscripten/emscripten_jerry_api.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jerry-api.h" -#include - -#include - -#define TYPE_ERROR \ - jerry_create_error(JERRY_ERROR_TYPE, NULL); - -#define TYPE_ERROR_ARG \ - jerry_create_error(JERRY_ERROR_TYPE, (const jerry_char_t *)"wrong type of argument"); - -#define TYPE_ERROR_FLAG \ - jerry_create_error(JERRY_ERROR_TYPE, (const jerry_char_t *)"argument cannot have an error flag"); - -//////////////////////////////////////////////////////////////////////////////// -// Parser and Executor Function -//////////////////////////////////////////////////////////////////////////////// - -// Note that `is_strict` is currently unsupported by emscripten -jerry_value_t jerry_eval(const jerry_char_t *source_p, size_t source_size, bool is_strict) { - return (jerry_value_t)EM_ASM_INT({ - // jerry_eval() uses an indirect eval() call, - // so the global execution context is used. - // Also see ECMA 5.1 -- 10.4.2 Entering Eval Code. - var indirectEval = eval; - try { - return __jerryRefs.ref(indirectEval(Module.Pointer_stringify($0, $1))); - } catch (e) { - var error_ref = __jerryRefs.ref(e); - __jerryRefs.setError(error_ref, true); - return error_ref; - } - }, source_p, source_size); -} - -jerry_value_t jerry_acquire_value(jerry_value_t value) { - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.acquire($0); - }, value); -} - -void jerry_release_value(jerry_value_t value) { - EM_ASM_INT({ - __jerryRefs.release($0); - }, value); -} -//////////////////////////////////////////////////////////////////////////////// -// Get the global context -//////////////////////////////////////////////////////////////////////////////// -jerry_value_t jerry_get_global_object(void) { - return ((jerry_value_t)EM_ASM_INT_V({ \ - return __jerryRefs.ref(Function('return this;')()); \ - })); -} - -jerry_value_t jerry_get_global_builtin(const jerry_char_t *builtin_name) { - return ((jerry_value_t)EM_ASM_INT({ \ - return __jerryRefs.ref(Function('return this;')()[Module.Pointer_stringify($0)]); \ - }, builtin_name)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Jerry Value Type Checking -//////////////////////////////////////////////////////////////////////////////// - -#define JERRY_VALUE_HAS_TYPE(ref, typename) \ - ((bool)EM_ASM_INT({ \ - return typeof __jerryRefs.get($0) === (typename); \ - }, (ref))) - -#define JERRY_VALUE_IS_INSTANCE(ref, type) \ - ((bool)EM_ASM_INT({ \ - return __jerryRefs.get($0) instanceof (type); \ - }, (ref))) - -bool jerry_value_is_array(const jerry_value_t value) { - return JERRY_VALUE_IS_INSTANCE(value, Array); -} - -bool jerry_value_is_boolean(const jerry_value_t value) { - return JERRY_VALUE_HAS_TYPE(value, 'boolean'); -} - -bool jerry_value_is_constructor(const jerry_value_t value) { - return jerry_value_is_function(value); -} - -bool jerry_value_is_function(const jerry_value_t value) { - return JERRY_VALUE_HAS_TYPE(value, 'function'); -} - -bool jerry_value_is_number(const jerry_value_t value) { - return JERRY_VALUE_HAS_TYPE(value, 'number'); -} - -bool jerry_value_is_null(const jerry_value_t value) { - return ((bool)EM_ASM_INT({ - return __jerryRefs.get($0) === null; - }, value)); -} - -bool jerry_value_is_object(const jerry_value_t value) { - return !jerry_value_is_null(value) && - (JERRY_VALUE_HAS_TYPE(value, 'object') || jerry_value_is_function(value)); -} - -bool jerry_value_is_string(const jerry_value_t value) { - return JERRY_VALUE_HAS_TYPE(value, 'string'); -} - -bool jerry_value_is_undefined(const jerry_value_t value) { - return JERRY_VALUE_HAS_TYPE(value, 'undefined'); -} - -//////////////////////////////////////////////////////////////////////////////// -// Jerry Value Getter Functions -//////////////////////////////////////////////////////////////////////////////// - -bool jerry_get_boolean_value(const jerry_value_t value) { - if (!jerry_value_is_boolean(value)) { - return false; - } - return (bool)EM_ASM_INT({ - return (__jerryRefs.get($0) === true); - }, value); -} - -double jerry_get_number_value(const jerry_value_t value) { - if (!jerry_value_is_number(value)) { - return 0.0; - } - return EM_ASM_DOUBLE({ - return __jerryRefs.get($0); - }, value); -} -//////////////////////////////////////////////////////////////////////////////// -// Functions for UTF-8 encoded string values -//////////////////////////////////////////////////////////////////////////////// -jerry_size_t jerry_get_utf8_string_size(const jerry_value_t value) { - if (!jerry_value_is_string(value)) { - return 0; - } - return (jerry_size_t)EM_ASM_INT({ - return Module.lengthBytesUTF8(__jerryRefs.get($0)); - }, value); -} - -jerry_size_t jerry_string_to_utf8_char_buffer(const jerry_value_t value, - jerry_char_t *buffer_p, - jerry_size_t buffer_size) { - const jerry_size_t str_size = jerry_get_utf8_string_size(value); - if (str_size == 0 || buffer_size < str_size || buffer_p == NULL) { - return 0; - } - - EM_ASM_INT({ - var str = __jerryRefs.get($0); - // Add one onto the buffer size, since Module.stringToUTF8 adds a null - // character at the end. This will lead to truncation if we just use - // buffer_size. Since the actual jerry-api does not do this, we are - // always careful to allocate space for a null character at the end. - // Allow stringToUTF8 to write that extra null beyond the passed in - // buffer_length. - Module.stringToUTF8(str, $1, $2 + 1); - }, value, buffer_p, buffer_size); - return strlen((const char *)buffer_p); -} - -//////////////////////////////////////////////////////////////////////////////// -// Functions for array object values -//////////////////////////////////////////////////////////////////////////////// -uint32_t jerry_get_array_length(const jerry_value_t value) { - if (!jerry_value_is_array(value)) { - return 0; - } - return (uint32_t)EM_ASM_INT({ - return __jerryRefs.get($0).length; - }, value); -} - -//////////////////////////////////////////////////////////////////////////////// -// Jerry Value Creation API -//////////////////////////////////////////////////////////////////////////////// -#define JERRY_CREATE_VALUE(value) \ - ((jerry_value_t)EM_ASM_INT_V({ \ - return __jerryRefs.ref((value)); \ - })) - -jerry_value_t jerry_create_array(uint32_t size) { - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(new Array($0)); - }, size); -} - -jerry_value_t jerry_create_boolean(bool value) { - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(Boolean($0)); - }, value); -} - -jerry_value_t jerry_create_error(jerry_error_t error_type, const jerry_char_t *message_p) { - return jerry_create_error_sz(error_type, message_p, strlen((const char *)message_p)); -} - -#define JERRY_ERROR(type, msg, sz) (jerry_value_t)(EM_ASM_INT({ \ - return __jerryRefs.ref(new (type)(Module.Pointer_stringify($0, $1))) \ - }, (msg), (sz))) - -jerry_value_t jerry_create_error_sz(jerry_error_t error_type, - const jerry_char_t *message_p, - jerry_size_t message_size) { - jerry_value_t error_ref = 0; - switch (error_type) { - case JERRY_ERROR_COMMON: - error_ref = JERRY_ERROR(Error, message_p, message_size); - break; - case JERRY_ERROR_EVAL: - error_ref = JERRY_ERROR(EvalError, message_p, message_size); - break; - case JERRY_ERROR_RANGE: - error_ref = JERRY_ERROR(RangeError, message_p, message_size); - break; - case JERRY_ERROR_REFERENCE: - error_ref = JERRY_ERROR(ReferenceError, message_p, message_size); - break; - case JERRY_ERROR_SYNTAX: - error_ref = JERRY_ERROR(SyntaxError, message_p, message_size); - break; - case JERRY_ERROR_TYPE: - error_ref = JERRY_ERROR(TypeError, message_p, message_size); - break; - case JERRY_ERROR_URI: - error_ref = JERRY_ERROR(URIError, message_p, message_size); - break; - default: - EM_ASM_INT({ - abort('Cannot create error type: ' + $0); - }, error_type); - break; - } - jerry_value_set_error_flag(&error_ref); - return error_ref; -} - -jerry_value_t jerry_create_external_function(jerry_external_handler_t handler_p) { - return (jerry_value_t)EM_ASM_INT({ - return __jerry_create_external_function($0); - }, handler_p); -} - -jerry_value_t jerry_create_number(double value) { - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref($0); - }, value); -} - -jerry_value_t jerry_create_number_infinity(bool negative) { - if (negative) { - return JERRY_CREATE_VALUE(-Infinity); - } else { - return JERRY_CREATE_VALUE(Infinity); - } -} - -jerry_value_t jerry_create_number_nan(void) { - return JERRY_CREATE_VALUE(NaN); -} - -jerry_value_t jerry_create_null(void) { - return JERRY_CREATE_VALUE(null); -} - -jerry_value_t jerry_create_object(void) { - return JERRY_CREATE_VALUE(new Object()); -} - -jerry_value_t jerry_create_string(const jerry_char_t *str_p) { - if (!str_p) { - return jerry_create_undefined(); - } - return jerry_create_string_utf8_sz(str_p, strlen((const char *)str_p)); -} - -jerry_value_t jerry_create_string_sz(const jerry_char_t *str_p, jerry_size_t str_size) { - return jerry_create_string_utf8_sz(str_p, str_size); -} - -jerry_value_t jerry_create_string_utf8(const jerry_char_t *str_p) { - if (!str_p) { - return jerry_create_undefined(); - } - return jerry_create_string_utf8_sz(str_p, strlen((const char *)str_p)); -} - -jerry_value_t jerry_create_string_utf8_sz(const jerry_char_t *str_p, jerry_size_t str_size) { - if (!str_p) { - return jerry_create_undefined(); - } - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(Module.Pointer_stringify($0, $1)); - }, str_p, str_size); -} - -jerry_value_t jerry_create_undefined(void) { - return JERRY_CREATE_VALUE(undefined); -} - -//////////////////////////////////////////////////////////////////////////////// -// General API Functions of JS Objects -//////////////////////////////////////////////////////////////////////////////// - -bool jerry_has_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) { - if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) { - return false; - } - return (bool)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - var name = __jerryRefs.get($1); - return (name in obj); - }, obj_val, prop_name_val); -} - -bool jerry_has_own_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) { - if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) { - return false; - } - return (bool)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - var name = __jerryRefs.get($1); - return obj.hasOwnProperty(name); - }, obj_val, prop_name_val); -} - -bool jerry_delete_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) { - if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) { - return false; - } - return (bool)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - var name = __jerryRefs.get($1); - try { - return delete obj[name]; - } catch (e) { - // In strict mode, delete throws SyntaxError if the property is an - // own non-configurable property. - return false; - } - return true; - }, obj_val, prop_name_val); -} - -jerry_value_t jerry_get_property(const jerry_value_t obj_val, const jerry_value_t prop_name_val) { - if (!jerry_value_is_object(obj_val) || !jerry_value_is_string(prop_name_val)) { - return TYPE_ERROR_ARG; - } - return (jerry_value_t)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - var name = __jerryRefs.get($1); - return __jerryRefs.ref(obj[name]); - }, obj_val, prop_name_val); -} - -jerry_value_t jerry_get_property_by_index(const jerry_value_t obj_val, uint32_t index) { - if (!jerry_value_is_object(obj_val)) { - return TYPE_ERROR; - } - return (jerry_value_t)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - return __jerryRefs.ref(obj[$1]); - }, obj_val, index); -} - -jerry_value_t jerry_set_property(const jerry_value_t obj_val, - const jerry_value_t prop_name_val, - const jerry_value_t value_to_set) { - if (jerry_value_has_error_flag(value_to_set) || - !jerry_value_is_object(obj_val) || - !jerry_value_is_string(prop_name_val)) { - return TYPE_ERROR_ARG; - } - return (jerry_value_t)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - var name = __jerryRefs.get($1); - var to_set = __jerryRefs.get($2); - obj[name] = to_set; - return __jerryRefs.ref(true); - }, obj_val, prop_name_val, value_to_set); -} - -jerry_value_t jerry_set_property_by_index(const jerry_value_t obj_val, - uint32_t index, - const jerry_value_t value_to_set) { - if (jerry_value_has_error_flag(value_to_set) || - !jerry_value_is_object(obj_val)) { - return TYPE_ERROR_ARG; - } - return (jerry_value_t)EM_ASM_INT({ - var obj = __jerryRefs.get($0); - var to_set = __jerryRefs.get($2); - obj[$1] = to_set; - return __jerryRefs.ref(true); - }, obj_val, index, value_to_set); -} - -void jerry_init_property_descriptor_fields(jerry_property_descriptor_t *prop_desc_p) { - *prop_desc_p = (jerry_property_descriptor_t) { - .value = jerry_create_undefined(), - .getter = jerry_create_undefined(), - .setter = jerry_create_undefined(), - }; -} - -jerry_value_t jerry_define_own_property(const jerry_value_t obj_val, - const jerry_value_t prop_name_val, - const jerry_property_descriptor_t *pdp) { - if (!jerry_value_is_object(obj_val) && !jerry_value_is_string(obj_val)) { - return TYPE_ERROR_ARG; - } - if ((pdp->is_writable_defined || pdp->is_value_defined) - && (pdp->is_get_defined || pdp->is_set_defined)) { - return TYPE_ERROR_ARG; - } - if (pdp->is_get_defined && !jerry_value_is_function(pdp->getter)) { - return TYPE_ERROR_ARG; - } - if (pdp->is_set_defined && !jerry_value_is_function(pdp->setter)) { - return TYPE_ERROR_ARG; - } - - return (jerry_value_t)(EM_ASM_INT({ - var obj = __jerryRefs.get($12 /* obj_val */); - var name = __jerryRefs.get($13 /* prop_name_val */); - var desc = {}; - if ($0 /* is_value_defined */) { - desc.value = __jerryRefs.get($9); - } - if ($1 /* is_get_defined */) { - desc.get = __jerryRefs.get($10); - } - if ($2 /* is_set_defined */) { - desc.set = __jerryRefs.get($11); - } - if ($3 /* is_writable_defined */) { - desc.writable = Boolean($4 /* is_writable */); - } - if ($5 /* is_enumerable_defined */) { - desc.enumerable = Boolean($6 /* is_enumerable */); - } - if ($7 /* is_configurable */) { - desc.configurable = Boolean($8 /* is_configurable */); - } - - Object.defineProperty(obj, name, desc); - return __jerryRefs.ref(Boolean(true)); - }, pdp->is_value_defined, /* $0 */ - pdp->is_get_defined, /* $1 */ - pdp->is_set_defined, /* $2 */ - pdp->is_writable_defined, /* $3 */ - pdp->is_writable, /* $4 */ - pdp->is_enumerable_defined, /* $5 */ - pdp->is_enumerable, /* $6 */ - pdp->is_configurable_defined, /* $7 */ - pdp->is_configurable, /* $8 */ - pdp->value, /* $9 */ - pdp->getter, /* $10 */ - pdp->setter, /* $11 */ - obj_val, /* $12 */ - prop_name_val /* $13 */ - )); -} - -jerry_value_t emscripten_call_jerry_function(jerry_external_handler_t func_obj_p, - const jerry_value_t func_obj_val, - const jerry_value_t this_val, - const jerry_value_t args_p[], - jerry_size_t args_count) { - return (func_obj_p)(func_obj_val, this_val, args_p, args_count); -} - -jerry_value_t jerry_call_function(const jerry_value_t func_obj_val, - const jerry_value_t this_val, - const jerry_value_t args_p[], - jerry_size_t args_count) { - if (!jerry_value_is_function(func_obj_val)) { - return TYPE_ERROR_ARG; - } - - return (jerry_value_t)EM_ASM_INT({ - var func_obj = __jerryRefs.get($0); - var this_val = __jerryRefs.get($1); - var args = []; - for (var i = 0; i < $3; ++i) { - args.push(__jerryRefs.get(getValue($2 + i*4, 'i32'))); - } - try { - var rv = func_obj.apply(this_val, args); - } catch (e) { - var error_ref = __jerryRefs.ref(e); - __jerryRefs.setError(error_ref, true); - return error_ref; - } - return __jerryRefs.ref(rv); - }, func_obj_val, this_val, args_p, args_count); -} - -jerry_value_t jerry_construct_object(const jerry_value_t func_obj_val, - const jerry_value_t args_p[], - jerry_size_t args_count) { - if (!jerry_value_is_constructor(func_obj_val)) { - return TYPE_ERROR_ARG; - } - return (jerry_value_t)EM_ASM_INT({ - var func_obj = __jerryRefs.get($0); - var args = []; - for (var i = 0; i < $2; ++i) { - args.push(__jerryRefs.get(getValue($1 + i*4, 'i32'))); - } - // Call the constructor with new object as `this` - var bindArgs = [null].concat(args); - var boundConstructor = func_obj.bind.apply(func_obj, bindArgs); - var rv = new boundConstructor(); - return __jerryRefs.ref(rv); - }, func_obj_val, args_p, args_count); -} - -jerry_size_t jerry_string_to_char_buffer(const jerry_value_t value, - jerry_char_t *buffer_p, - jerry_size_t buffer_size) { - return jerry_string_to_utf8_char_buffer(value, buffer_p, buffer_size); -} - -jerry_size_t jerry_object_to_string_to_utf8_char_buffer(const jerry_value_t object, - jerry_char_t *buffer_p, - jerry_size_t buffer_size) { - jerry_value_t str_ref = (jerry_value_t)EM_ASM_INT({ - var str = __jerryRefs.ref(String(__jerryRefs.get($0))); - return str; - }, object); - jerry_size_t len = jerry_string_to_utf8_char_buffer(str_ref, buffer_p, buffer_size); - jerry_release_value(str_ref); - - return len; -} - -// FIXME: PBL-43551 Propery CESU-8 => UTF-8 conversion. -jerry_size_t jerry_object_to_string_to_char_buffer(const jerry_value_t object, - jerry_char_t *buffer_p, - jerry_size_t buffer_size) { - return jerry_object_to_string_to_utf8_char_buffer(object, - buffer_p, - buffer_size); -} - -jerry_value_t jerry_get_object_keys(const jerry_value_t value) { - if (!jerry_value_is_object(value)) { - return TYPE_ERROR_ARG; - } - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(Object.keys(__jerryRefs.get($0))); - }, value); -} - -jerry_value_t jerry_get_prototype(const jerry_value_t value) { - if (!jerry_value_is_object(value)) { - return TYPE_ERROR_ARG; - } - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(__jerryRefs.get($0).prototype); - }, value); -} - -jerry_value_t jerry_set_prototype(const jerry_value_t obj_val, const jerry_value_t proto_obj_val) { - return 0; // FIXME: Not sure what to do here -} - -bool jerry_get_object_native_handle(const jerry_value_t obj_val, uintptr_t *out_handle_p) { - return EM_ASM_INT({ - var ptr = __jerryRefs.getNativeHandle($0); - if (ptr === undefined) { - return false; - } - Module.setValue($1, ptr, '*'); - return true; - }, obj_val, out_handle_p); -} - -void jerry_set_object_native_handle(const jerry_value_t obj_val, uintptr_t handle_p, - jerry_object_free_callback_t freecb_p) { - EM_ASM_INT({ - __jerryRefs.setNativeHandle($0, $1, $2); - }, obj_val, handle_p, freecb_p); -} - -void emscripten_call_jerry_object_free_callback(jerry_object_free_callback_t freecb_p, - uintptr_t handle_p) { - if (freecb_p) { - freecb_p(handle_p); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Error flag manipulation functions -//////////////////////////////////////////////////////////////////////////////// -// -// The error flag is stored alongside the value in __jerryRefs. -// This allows for us to keep a valid value, like jerryscript does, and be able -// to add / remove a flag specifying whether there was an error or not. - -bool jerry_value_has_error_flag(const jerry_value_t value) { - return (bool)(EM_ASM_INT({ - return __jerryRefs.getError($0); - }, value)); -} - -void jerry_value_clear_error_flag(jerry_value_t *value_p) { - EM_ASM_INT({ - return __jerryRefs.setError($0, false); - }, *value_p); -} - -void jerry_value_set_error_flag(jerry_value_t *value_p) { - EM_ASM_INT({ - return __jerryRefs.setError($0, true); - }, *value_p); -} -//////////////////////////////////////////////////////////////////////////////// -// Converters of `jerry_value_t` -//////////////////////////////////////////////////////////////////////////////// - -bool jerry_value_to_boolean(const jerry_value_t value) { - if (jerry_value_has_error_flag(value)) { - return false; - } - return (bool)EM_ASM_INT({ - return Boolean(__jerryRefs.get($0)); - }, value); -} - - - -jerry_value_t jerry_value_to_number(const jerry_value_t value) { - if (jerry_value_has_error_flag(value)) { - return TYPE_ERROR_FLAG; - } - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(Number(__jerryRefs.get($0))); - }, value); -} - -jerry_value_t jerry_value_to_object(const jerry_value_t value) { - if (jerry_value_has_error_flag(value)) { - return TYPE_ERROR_FLAG; - } - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(new Object(__jerryRefs.get($0))); - }, value); -} - -jerry_value_t jerry_value_to_primitive(const jerry_value_t value) { - if (jerry_value_has_error_flag(value)) { - return TYPE_ERROR_FLAG; - } - return (jerry_value_t)EM_ASM_INT({ - var val = __jerryRefs.get($0); - var rv; - if ((typeof val === 'object' && val != null) - || (typeof val === 'function')) { - rv = val.valueOf(); // unbox - } else { - rv = val; // already a primitive - } - return __jerryRefs.ref(rv); - }, value); -} - -jerry_value_t jerry_value_to_string(const jerry_value_t value) { - if (jerry_value_has_error_flag(value)) { - return TYPE_ERROR_FLAG; - } - return (jerry_value_t)EM_ASM_INT({ - return __jerryRefs.ref(String(__jerryRefs.get($0))); - }, value); -} - -int jerry_obj_refcount(jerry_value_t o) { - return EM_ASM_INT({ - try { - return __jerryRefs.getRefCount($0); - } catch (e) { - return 0; - } - }, o); -} - -//////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////// - -void jerry_init(jerry_init_flag_t flags) { - EM_ASM(__jerryRefs.reset()); -} - -void jerry_cleanup(void) { -} diff --git a/applib-targets/emscripten/emscripten_jerry_port.c b/applib-targets/emscripten/emscripten_jerry_port.c deleted file mode 100644 index 3564955d24..0000000000 --- a/applib-targets/emscripten/emscripten_jerry_port.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -void rocky_runtime_context_init(void) { -} - -void rocky_runtime_context_deinit(void) { -} diff --git a/applib-targets/emscripten/emscripten_resources.c b/applib-targets/emscripten/emscripten_resources.c deleted file mode 100644 index 8ab1bd44a0..0000000000 --- a/applib-targets/emscripten/emscripten_resources.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "emscripten_resources.h" - -#include "resource/resource_storage_impl.h" - -#include -#include -#include - -typedef struct { - uint32_t offset; - uint32_t length; -} Resource; - -#define MANIFEST_SIZE (sizeof(ResourceManifest)) -#define TABLE_ENTRY_SIZE (sizeof(ResTableEntry)) -#define MAX_RESOURCES_FOR_SYSTEM_STORE 512 -#define SYSTEM_STORE_METADATA_BYTES \ - (MANIFEST_SIZE + MAX_RESOURCES_FOR_SYSTEM_STORE * TABLE_ENTRY_SIZE) - -//////////////////////////////////////////////// -// Custom Resources -// -// Custom resources in Rocky.js are implemented with a set of callbacks -// on the javascript side which implement resource APIs -// -// We store those callbacks in an array and return a resource ID which -// can then be used as if it was a valid resource -// -// Under the hood, we just lookup & call the initially provided callbacks - -typedef struct { - uint32_t resource_id; - ResourceReadCb read; - ResourceGetSizeCb get_size; -} EmxCustomResource; - -typedef struct { - EmxCustomResource *custom_resources; - uint32_t last_id; - int array_size; - int next_index; -} CustomResList; - -static CustomResList s_custom_res_list = {0}; - -static int prv_custom_resource_get_index(uint32_t resource_id) { - int index; - for (index = 0; index < s_custom_res_list.next_index; ++index) { - if (s_custom_res_list.custom_resources[index].resource_id == resource_id) { - return index; - } - } - - return -1; -} - -static EmxCustomResource *prv_custom_resource_get(uint32_t resource_id) { - int index = prv_custom_resource_get_index(resource_id); - if (index < 0) { - return NULL; - } else { - return &s_custom_res_list.custom_resources[index]; - } -} - - -static void prv_custom_resource_add(EmxCustomResource *res) { - if (s_custom_res_list.array_size <= s_custom_res_list.next_index) { - // grow the list - int new_size = (s_custom_res_list.array_size + 1) * 2; - s_custom_res_list.custom_resources = realloc(s_custom_res_list.custom_resources, new_size); - s_custom_res_list.array_size = new_size; - } - - s_custom_res_list.custom_resources[s_custom_res_list.next_index] = *res; - ++s_custom_res_list.next_index; -} - -static void prv_custom_resource_remove(uint32_t resource_id) { - int index = prv_custom_resource_get_index(resource_id); - if (index < 0) { - return; - } - - --s_custom_res_list.next_index; - if (s_custom_res_list.next_index == index) { - // we had the last resource, we're done - return; - } - - memmove(&s_custom_res_list.custom_resources[index], - &s_custom_res_list.custom_resources[index + 1], - (s_custom_res_list.next_index - index) * sizeof(EmxCustomResource)); -} - -////////////////////////////////////// -// System Resources -// -// In this case, we shove the pbpack in an emscripten "file" (baked into the resulting JS -// and reimplement system resource APIs using standard C file I/O - -static FILE *s_resource_file = NULL; - -static uint32_t prv_read(uint32_t offset, void *data, size_t num_bytes) { - if (!s_resource_file || fseek(s_resource_file, offset, SEEK_SET)) { - printf("%s: Couldn't seek to %d\n", __FILE__, offset); - return 0; - } - return fread(data, 1, num_bytes, s_resource_file); -} - -static ResourceManifest prv_get_manifest(void) { - ResourceManifest manifest = {}; - prv_read(0, &manifest, sizeof(ResourceManifest)); - return manifest; -} - -static bool prv_get_table_entry(ResTableEntry *entry, uint32_t index) { - uint32_t addr = sizeof(ResourceManifest) + index * sizeof(ResTableEntry); - return prv_read(addr, entry, sizeof(ResTableEntry)); -} - -static bool prv_get_resource(uint32_t resource_id, Resource *res) { - *res = (Resource){ - .length = 0, - .offset = 0, - }; - - ResourceManifest manifest = prv_get_manifest(); - - if (resource_id > manifest.num_resources) { - printf("%s: resource id %d > %d is out of range\n", __FILE__, - resource_id, - manifest.num_resources); - return false; - } - - ResTableEntry entry; - if (!prv_get_table_entry(&entry, resource_id - 1)) { - printf("%s: Failed to read table entry for %d\n", __FILE__, resource_id); - return false; - } - - if ((entry.resource_id != resource_id) || - (entry.length == 0)) { - // empty resource - printf("%s: Invalid resourcel for %d\n", __FILE__, resource_id); - return false; - } - - res->offset = SYSTEM_STORE_METADATA_BYTES + entry.offset; - res->length = entry.length; - - return true; -} - -/////////////////////////////// -// API - -size_t emx_resources_read(ResAppNum app_num, - uint32_t resource_id, - uint32_t offset, - uint8_t *buf, - size_t num_bytes) { - if (offset > INT_MAX || num_bytes > INT_MAX) { - return 0; - } - - EmxCustomResource *custom_res = NULL; - if (app_num != SYSTEM_APP && (custom_res = prv_custom_resource_get(resource_id))) { - return custom_res->read(offset, buf, num_bytes); - } - - Resource resource = {}; - if (!prv_get_resource(resource_id, &resource)) { - return 0; - } - - if (offset + num_bytes > resource.length) { - if (offset >= resource.length) { - // Can't recover from trying to read from beyond the resource. Read nothing. - printf("%s: Reading past the end of the resource!\n", __FILE__); - return 0; - } - num_bytes = resource.length - offset; - } - - return prv_read(offset + resource.offset, buf, num_bytes); -} - -size_t emx_resources_get_size(ResAppNum app_num, uint32_t resource_id) { - EmxCustomResource *custom_res = NULL; - if (app_num != SYSTEM_APP && (custom_res = prv_custom_resource_get(resource_id))) { - return custom_res->get_size(); - } - - Resource resource = {}; - if (!prv_get_resource(resource_id, &resource)) { - return 0; - } - - return resource.length; -} - -bool emx_resources_init(void) { - s_resource_file = fopen("system_resources.pbpack", "r"); - - if (!s_resource_file) { - printf("Error: Failed to open resources file\n"); - return false; - } - - return true; -} - -void emx_resources_deinit(void) { - fclose(s_resource_file); -} - -uint32_t emx_resources_register_custom(ResourceReadCb read_cb, ResourceGetSizeCb get_size_cb) { - EmxCustomResource custom_res = { - .resource_id = ++s_custom_res_list.last_id, - .read = read_cb, - .get_size = get_size_cb, - }; - prv_custom_resource_add(&custom_res); - - return custom_res.resource_id; -} - -void emx_resources_remove_custom(uint32_t resource_id) { - prv_custom_resource_remove(resource_id); -} diff --git a/applib-targets/emscripten/emscripten_resources.h b/applib-targets/emscripten/emscripten_resources.h deleted file mode 100644 index 40239ccbc1..0000000000 --- a/applib-targets/emscripten/emscripten_resources.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "resource/resource.h" - -// transformed to int to avoid surpises between C->JS -typedef int (*ResourceReadCb)(int offset, uint8_t *buf, int num_bytes); -typedef int (*ResourceGetSizeCb)(void); - -bool emx_resources_init(void); -void emx_resources_deinit(void); -size_t emx_resources_get_size(ResAppNum app_num, uint32_t resource_id); -size_t emx_resources_read(ResAppNum app_num, - uint32_t resource_id, - uint32_t offset, - uint8_t *buf, - size_t num_bytes); -uint32_t emx_resources_register_custom(ResourceReadCb read_cb, ResourceGetSizeCb get_size_cb); -void emx_resources_remove_custom(uint32_t resource_id); diff --git a/applib-targets/emscripten/emscripten_tick_timer_service.c b/applib-targets/emscripten/emscripten_tick_timer_service.c deleted file mode 100644 index 88cf9c975b..0000000000 --- a/applib-targets/emscripten/emscripten_tick_timer_service.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app_logging.h" -#include "applib/tick_timer_service_private.h" - -#include -#include -#include - -time_t time(time_t *time); - -static TickTimerServiceState s_state; - -static void prv_schedule_next_update(void); - -static void prv_do_update(void *data) { - if (!s_state.handler) { - return; - } - - // The data pointer value is used to pass a boolean value directly: - const bool is_update_due_to_time_change = (uintptr_t)data; - - struct tm currtime; - time_t t = time(NULL); - localtime_r(&t, &currtime); - - TimeUnits units_changed; - if (is_update_due_to_time_change) { - units_changed = (SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | YEAR_UNIT); - } else { - prv_schedule_next_update(); - units_changed = 0; - if (!s_state.first_tick) { - if (s_state.last_time.tm_sec != currtime.tm_sec) { - units_changed |= SECOND_UNIT; - } - if (s_state.last_time.tm_min != currtime.tm_min) { - units_changed |= MINUTE_UNIT; - } - if (s_state.last_time.tm_hour != currtime.tm_hour) { - units_changed |= HOUR_UNIT; - } - if (s_state.last_time.tm_mday != currtime.tm_mday) { - units_changed |= DAY_UNIT; - } - if (s_state.last_time.tm_mon != currtime.tm_mon) { - units_changed |= MONTH_UNIT; - } - if (s_state.last_time.tm_year != currtime.tm_year) { - units_changed |= YEAR_UNIT; - } - } - } - - s_state.last_time = currtime; - s_state.first_tick = false; - - if ((s_state.tick_units & units_changed) || (units_changed == 0)) { - s_state.handler(&currtime, units_changed); - } -} - -static void prv_schedule_next_update(void) { - // Schedule this to fire again at the top of the next second - const int ms_into_s = EM_ASM_INT_V({ - return Date.now().getMilliseconds; - }); - const double wait_ms = 1000 - ms_into_s; - const bool is_update_due_to_time_change = false; - emscripten_async_call(prv_do_update, (void *)(uintptr_t)is_update_due_to_time_change, wait_ms); -} - -void tick_timer_service_handle_time_change(void) { - const bool is_update_due_to_time_change = true; - prv_do_update((void *)(uintptr_t)is_update_due_to_time_change); -} - -void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) { - const bool first = (s_state.handler == NULL); - s_state = (TickTimerServiceState) { - .handler = handler, - .tick_units = tick_units, - .first_tick = true, - }; - - if (first && handler != NULL) { - prv_schedule_next_update(); - } -} - -void emx_tick_timer_service_init(void) { - s_state = (TickTimerServiceState){}; -} diff --git a/applib-targets/emscripten/emscripten_tick_timer_service.h b/applib-targets/emscripten/emscripten_tick_timer_service.h deleted file mode 100644 index 87f5f5129b..0000000000 --- a/applib-targets/emscripten/emscripten_tick_timer_service.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void emx_tick_timer_service_init(void); diff --git a/applib-targets/emscripten/exported_functions.json b/applib-targets/emscripten/exported_functions.json deleted file mode 100644 index 3e3c3b7d01..0000000000 --- a/applib-targets/emscripten/exported_functions.json +++ /dev/null @@ -1,62 +0,0 @@ -["_main", - "_emx_graphics_get_pixels", - "_graphics_context_set_stroke_width", - "_graphics_context_set_antialiased", - "_emx_resources_read", - "_emx_resources_get_size", - "_app_state_get_graphics_context", - "_gdraw_command_image_create_with_resource", - "_gdraw_command_image_get_command_list", - "_gdraw_command_list_get_command", - "_gdraw_command_list_get_num_commands", - "_gdraw_command_sequence_create_with_resource", - "_gdraw_command_sequence_get_frame_by_elapsed", - "_gdraw_command_sequence_get_total_duration", - "_gdraw_command_get_type", - "_gdraw_command_get_stroke_width", - "_gdraw_command_set_stroke_width", - "_gdraw_command_set_path_open", - "_gdraw_command_get_path_open", - "_gdraw_command_set_hidden", - "_gdraw_command_get_hidden", - "_gdraw_command_get_num_points", - "_gdraw_command_set_radius", - "_gdraw_command_get_radius", - "_gdraw_command_sequence_get_play_count", - "_gdraw_command_sequence_set_play_count", - "_gdraw_command_sequence_get_total_duration", - "_gdraw_command_sequence_get_num_frames", - "_gdraw_command_sequence_get_frame_by_elapsed", - "_gdraw_command_sequence_get_frame_by_index", - "_gdraw_command_frame_set_duration", - "_gdraw_command_frame_get_duration", - "_gdraw_command_frame_get_command_list", - "_fonts_get_system_font", - "_fonts_load_custom_font", - "_fonts_unload_custom_font", - "_gbitmap_create_with_data", - "_gbitmap_create_from_png_data", - "_gbitmap_destroy", - "_gbitmap_get_format", - "_graphics_context_set_compositing_mode", - "_emx_resources_register_custom", - "_emx_resources_remove_custom", - "_gpath_draw_filled", - "_gpath_draw_outline", - "_gpath_draw_outline_open", - "_graphics_draw_text", - "_gbitmap_sequence_create_with_resource", - "_gbitmap_sequence_destroy", - "_gbitmap_sequence_get_current_frame_delay_ms", - "_gbitmap_sequence_get_total_num_frames", - "_gbitmap_sequence_get_current_frame_idx", - "_gbitmap_sequence_get_play_count", - "_gbitmap_sequence_set_play_count", - - "_rocky_api_watchface_init", - "_jerry_call_function", - "_emscripten_call_jerry_function", - "_emscripten_call_jerry_object_free_callback", - "_tick_timer_service_handle_time_change", - "_clock_set_24h_style" -] diff --git a/applib-targets/emscripten/html-binding.js b/applib-targets/emscripten/html-binding.js deleted file mode 100644 index 272e98f58b..0000000000 --- a/applib-targets/emscripten/html-binding.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - - Copyright © 2015-2016 Pebble Technology Corp., - All Rights Reserved. http://pebble.github.io/rockyjs/LICENSE - - This describes functionality to bind the Rocky Simulator to an HTML canvas - element. This file is included into the Emscripten output using --post-js, - as such it will end up in body of the RockySimulator(options) constructor. - - */ - -if (typeof(Module) === 'undefined') { - var Module = {}; -} - -Module.bindCanvas = function(canvas) { - // in a future version, these values should adapt automatically - // also, we want the ability to create framebuffers of larger sizes - var canvasW = canvas.width; - var canvasH = canvas.height; - var framebufferW = 144; - var framebufferH = 168; - - // scale gives us the ability to do a nearest-neighbor scaling - var scale = options.scale || - Math.min(canvasW / framebufferW, canvasH / framebufferH); - - // pixel access to read (framebuffer) and write to (canvas) - var canvasCtx = canvas.getContext('2d'); - var canvasPixelData = canvasCtx.createImageData(canvasW, canvasH); - var canvasPixels = canvasPixelData.data; - var framebufferPixelPTR = Module.ccall( - 'emx_graphics_get_pixels', 'number', [] - ); - - var isRenderRequested = false; - var copyFrameBufferToCanvas = function(timestamp) { - console.log('copying pixels...'); - isRenderRequested = false; - var framebufferPixels = new Uint8Array(Module.HEAPU8.buffer, - framebufferPixelPTR, - framebufferW * framebufferH); - // renders current state of the framebuffer to the bound canvas - // respecting the passed scale - for (var y = 0; y < canvasH; y++) { - var pebbleY = (y / scale) >> 0; - if (pebbleY >= framebufferH) { - break; - } - for (var x = 0; x < canvasW; x++) { - var pebbleX = (x / scale) >> 0; - if (pebbleX >= framebufferW) { - break; - } - var pebbleOffset = pebbleY * framebufferW + pebbleX; - var in_values = framebufferPixels[pebbleOffset]; - var r = ((in_values >> 4) & 0x3) * 85; - var g = ((in_values >> 2) & 0x3) * 85; - var b = ((in_values >> 0) & 0x3) * 85; - var canvasOffset = (y * canvasW + x) * 4; - canvasPixels[canvasOffset + 0] = r; - canvasPixels[canvasOffset + 1] = g; - canvasPixels[canvasOffset + 2] = b; - canvasPixels[canvasOffset + 3] = 255; - } - } - canvasCtx.putImageData(canvasPixelData, 0, 0); - }; - - Module.frameBufferMarkDirty = function() { - if (isRenderRequested) { - return; - } - console.log('request render'); - isRenderRequested = true; - window.requestAnimationFrame(copyFrameBufferToCanvas); - } -}; - -// Apply `options` from the RockySimulator(options) constructor: -if (typeof(options) !== 'undefined' && options.canvas) { - Module.bindCanvas(options.canvas); -} diff --git a/applib-targets/emscripten/html/css/LICENSE b/applib-targets/emscripten/html/css/LICENSE deleted file mode 100644 index c8cdb14cf3..0000000000 --- a/applib-targets/emscripten/html/css/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2011-2016 Twitter, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/applib-targets/emscripten/html/css/bootstrap.css b/applib-targets/emscripten/html/css/bootstrap.css deleted file mode 100644 index 8a4be566a7..0000000000 --- a/applib-targets/emscripten/html/css/bootstrap.css +++ /dev/null @@ -1,6756 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -[hidden], -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} -/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ -@media print { - *, - *:before, - *:after { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 11px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group .form-control:focus { - z-index: 3; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 2; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 3; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - padding-right: 15px; - padding-left: 15px; - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - background-color: rgba(0, 0, 0, 0); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -10px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -10px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -10px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-header:before, -.modal-header:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-header:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} diff --git a/applib-targets/emscripten/html/css/style.css b/applib-targets/emscripten/html/css/style.css deleted file mode 100644 index d63447b79b..0000000000 --- a/applib-targets/emscripten/html/css/style.css +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@import url(https://fonts.googleapis.com/css?family=Bowlby+One+SC|Roboto:400,700,400italic); - -body { - font: 16px/1.5 'Roboto', sans-serif; -} - -body { - padding-bottom: 2em; -} - -body > .container:first-child > .row:first-child > div > p:first-child { - display: none; -} - -h1 { - font-size: 3em; -} - -h1 + blockquote { - border: 0 none; - max-width: 600px; - text-align: center; - margin: 0 auto 3em; - padding: 0; -} - -h1, h2 { - font-family: "Bowlby One SC"; - overflow: hidden; - text-align: center; - margin: 2em 0 1em; - text-transform: uppercase; -} - -h1:before, h1:after, h2:before, h2:after { - background-color: #ddd; - content: ""; - display: inline-block; - height: 1px; - position: relative; - vertical-align: middle; - width: 50%; -} - -h1:before, h2:before { - right: 0.5em; - margin-left: -50%; -} - -h1:after, h2:after { - left: 0.5em; - margin-right: -50%; -} - -h3 { - margin: 1em 0 0.5em; -} - -form { - width: 100%; -} - -label { - width: 100%; -} - -#githubForkLink { - background: url(../img/forkBanner.png) no-repeat top right; - position: absolute; - display: block; - width: 149px; - height: 149px; - top: 0; - right: 0; - - /* Hide the text. */ - text-indent: 100%; - white-space: nowrap; - overflow: hidden; -} - -table.table-compatibility>tbody>tr.standard { - background-color: #d9edf7; -} - -table.table-compatibility>tbody>tr.implemented { - background-color: #dff0db; -} - -table.table-compatibility>tbody>tr.partial { - background-color: #ffffff; -} - -table.table-compatibility>tbody>tr.planned { - background-color: #fcf8e3; -} - -table.table-compatibility>tbody>tr.not-planned { - background-color: #f2dede -} diff --git a/applib-targets/emscripten/html/index.html b/applib-targets/emscripten/html/index.html deleted file mode 100644 index a5587d7a8c..0000000000 --- a/applib-targets/emscripten/html/index.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - Simple Rocky Simulator - - - - - - - - - - - -
- - -
- -
- -
-

Controls

-

- - - - - - -

-
-
- - - - diff --git a/applib-targets/emscripten/html/js/controls.js b/applib-targets/emscripten/html/js/controls.js deleted file mode 100644 index f628a06b2d..0000000000 --- a/applib-targets/emscripten/html/js/controls.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -window.addEventListener('load', function() { - var form = document.getElementById("controls-form"); - form.addEventListener('submit', function(e) { - e.preventDefault(); - }); - - var timestampInput = document.getElementById("timestamp-input"); - timestampInput.addEventListener('change', function(e) { - rockySimulator.setTime(Number(e.target.value) * 1000); - }); - - var timezoneSlider = document.getElementById("timezone-offset-input"); - var timezoneSpan = document.getElementById("timezone-offset-span"); - var timezoneSliderOnChange = function() { - var offset = timezoneSlider.value; - rockySimulator.setTimezoneOffset(offset); - var sign; - if (offset > 0) { - sign = '+'; - } else if (offset < 0) { - sign = '-'; - } else { - sign = ''; - } - var offsetMinutes = (offset % 60); - var absOffsetHours = Math.abs((offset - offsetMinutes) / 60); - var gmtText = 'GMT ' + sign + absOffsetHours; - if (offsetMinutes) { - var absOffsetMinutes = Math.abs(offsetMinutes); - gmtText += ':' + (absOffsetMinutes < 10 ? '0' : '') + absOffsetMinutes; - } - timezoneSpan.innerText = gmtText; - }; - timezoneSlider.addEventListener('change', timezoneSliderOnChange); - timezoneSlider.addEventListener('input', timezoneSliderOnChange); - - // After loading the page, set the slider to the local TZ: - var localTimezoneOffset = new Date().getTimezoneOffset(); - timezoneSlider.value = localTimezoneOffset; - timezoneSliderOnChange(); - - var time24hStyle = document.getElementById("24h-style-input"); - time24hStyle.addEventListener('change', function(e) { - rockySimulator.set24hStyle(e.target.checked); - }); -}); diff --git a/applib-targets/emscripten/html/js/tictoc.js b/applib-targets/emscripten/html/js/tictoc.js deleted file mode 100644 index 12f87ee93e..0000000000 --- a/applib-targets/emscripten/html/js/tictoc.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var WatchfaceHelper = function(date) { - function clockwiseRad(fraction) { - // TODO: figure out if this is actually correct orientation for Canvas APIs - return (1.5 - fraction) * 2 * Math.PI; - } - - date = date || new Date(); - var secondFraction = date.getSeconds() / 60; - var minuteFraction = (date.getMinutes()) / 60; - var hourFraction = (date.getHours() % 12 + minuteFraction) / 12; - this.secondAngle = clockwiseRad(secondFraction); - this.minuteAngle = clockwiseRad(minuteFraction); - this.hourAngle = clockwiseRad(hourFraction); -}; - -// book keeping so that we can easily animate the two hands for the watchface -// .scale/.angle are updated by tween/event handler (see below) -var renderState = { - minute: {style: 'white', scale: 0.80, angle: 0}, - hour: {style: 'red', scale: 0.51, angle: 0} -}; - -// helper function for the draw function (see below) -// extracted as a standalone function to satisfy common believe in efficient JS code -// TODO: verify that this has actually any effect on byte code level -var drawHand = function(handState, ctx, cx, cy, maxRadius) { - ctx.lineWidth = 8; - ctx.strokeStyle = handState.style; - ctx.beginPath(); - ctx.moveTo(cx, cy); - ctx.lineTo(cx + Math.sin(handState.angle) * handState.scale * maxRadius, - cy + Math.cos(handState.angle) * handState.scale * maxRadius); - ctx.stroke(); -}; - -// the 'draw' event is being emitted after each call to rocky.requestDraw() but -// at most once for each screen update, even if .requestDraw() is called frequently -// the 'draw' event might also fire at other meaningful times (e.g. upon launch) -rocky.on('draw', function(drawEvent) { - var ctx = drawEvent.context; - var w = ctx.canvas.unobstructedWidth; - var h = ctx.canvas.unobstructedHeight; - - // clear canvas on each render - ctx.fillStyle = 'black'; - ctx.fillRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight); - - // center point - var cx = w / 2; - var cy = h / 2; - var maxRadius = Math.min(w, h - 2 * 10) / 2; - drawHand(renderState.minute, ctx, cx, cy, maxRadius); - drawHand(renderState.hour, ctx, cx, cy, maxRadius); - - // Draw a 12 o clock indicator - drawHand({style: 'white', scale: 0, angle: 0}, ctx, cx, 8, 0); - // overdraw center so that no white part of the minute hand is visible - drawHand({style: 'red', scale: 0, angle: 0}, ctx, cx, cy, 0); -}); - -// listener is called on each full minute and once immediately after registration -rocky.on('minutechange', function(e) { - // WatchfaceHelper will later be extracted as npm module - var wfh = new WatchfaceHelper(e.date); - renderState.minute.angle = wfh.minuteAngle; - renderState.hour.angle = wfh.hourAngle; - rocky.requestDraw(); -}); - -rocky.on('secondchange', function(e) { - console.log(e.date.toLocaleTimeString() + ' ' + e.date.toLocaleDateString() + - ' ' + e.date.toLocaleString()); -}); - -console.log('TicToc launched'); diff --git a/applib-targets/emscripten/integration_tests/.gitignore b/applib-targets/emscripten/integration_tests/.gitignore deleted file mode 100644 index 3c3629e647..0000000000 --- a/applib-targets/emscripten/integration_tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/applib-targets/emscripten/integration_tests/wscript b/applib-targets/emscripten/integration_tests/wscript deleted file mode 100644 index 66dc94d6c3..0000000000 --- a/applib-targets/emscripten/integration_tests/wscript +++ /dev/null @@ -1,6 +0,0 @@ -def configure(conf): - pass - - -def build(bld): - pass diff --git a/applib-targets/emscripten/jerry_api.js b/applib-targets/emscripten/jerry_api.js deleted file mode 100644 index 60481708d1..0000000000 --- a/applib-targets/emscripten/jerry_api.js +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Polyfill Number.isNaN -if (Number.isNaN === undefined) { - Number.isNaN = Number.isNaN || function(value) { - return value !== value; - } -} - -var __jerryRefs = { - - // Jerryscript values (jerry_value_t) are integers which contain information - // about some javascript value that has been internally created. - // Create _objMap which will allow us to store and retrieve javascript values - // from a jerry_value_t, and perform refcounts on values that we still need an - // internal reference to, and avoid them from being garbage collected. - - _objMap : {}, - _nextObjectRef : 1, - _findValue : function(value) { - if (Number.isNaN(value)) { - // Special case to find NaN - for (var jerry_val in this._objMap) { - if (Number.isNaN(this._objMap[jerry_val].value)) { - return jerry_val; - } - } - } else { - for (var jerry_val in this._objMap) { - if (this._objMap[jerry_val].value === value) { - return jerry_val; - } - } - } - return 0; - }, - _getEntry : function(jerry_value) { - var entry = this._objMap[jerry_value]; - if (!entry) { - throw new Error('Entry at ' + jerry_value + ' does not exist'); - } - return entry; - }, - - reset: function() { - this._objMap = {}; - this._nextObjectRef = 1; - }, - - // Given a jerry value, return the stored javascript value. - get : function(jerry_value) { - return this._getEntry(jerry_value).value; - }, - - // Given a javascript value, return a jerry value that refers to it. - // If the value already exists in the map, increment its refcount and return - // the jerry value. - // Otherwise, create a new entry and return the jerry value. - ref : function(value) { - var jerry_value = this._findValue(value); - if (jerry_value) { - this._getEntry(jerry_value).refCount++; - return jerry_value; - } - - jerry_value = this._nextObjectRef++; - this._objMap[jerry_value] = { - refCount : 1, - value : value, - error : false, - }; - // console.log('created entry ' + jerry_value + ' for ' + value + ' at ' + stackTrace()); - return jerry_value; - }, - - getRefCount : function(jerry_value) { - return this._getEntry(jerry_value).refCount; - }, - - // Increase the reference count of the given jerry value - acquire : function(jerry_value) { - this._getEntry(jerry_value).refCount++; - return jerry_value; - }, - - // Decrease the reference count of the given jerry value and delete it if - // there are no more internal references. - release : function(ref) { - var entry = this._getEntry(ref); - entry.refCount--; - - if (entry.refCount <= 0) { - if (entry.freeCallbackPtr) { - Module.ccall( - 'emscripten_call_jerry_object_free_callback', - null, - ['number', 'number'], - [entry.freeCallbackPtr, entry.nativeHandlePtr]); - } - // console.log('deleting ' + ref + ' at ' + stackTrace()); - delete this._objMap[ref]; - } - }, - - setError : function(ref, state) { - this._getEntry(ref).error = state; - }, - - getError : function(ref) { - return this._getEntry(ref).error; - }, - - setNativeHandle : function(jerryValue, nativeHandlePtr, freeCallbackPtr) { - var entry = this._getEntry(jerryValue); - entry.nativeHandlePtr = nativeHandlePtr; - entry.freeCallbackPtr = freeCallbackPtr; - }, - - getNativeHandle : function(jerryValue) { - return this._getEntry(jerryValue).nativeHandlePtr; - } -}; - -function __jerry_create_external_function(function_ptr) { - var f = function() { - var nativeHandlerArgs = [ - function_ptr, /* the function pointer for us to call */ - __jerryRefs.ref(f), /* ref to the actual js function */ - __jerryRefs.ref(this) /* our this object */ - ]; - - var numArgs = arguments.length; - var jsRefs = []; - for (var i = 0; i < numArgs; i++) { - jsRefs.push(__jerryRefs.ref(arguments[i])); - } - - // Arg 4 is a uint32 array of jerry_value_t arguments - var jsArgs = Module._malloc(numArgs * 4); - for (var i = 0; i < numArgs; i++) { - Module.setValue(jsArgs + i*4, jsRefs[i], 'i32'); - } - nativeHandlerArgs.push(jsArgs); - nativeHandlerArgs.push(numArgs); - - // this is just the classy Emscripten calling. function_ptr is a C-pointer here - // and we know the signature of the C function as it needs to follow - var result_ref = Module.ccall('emscripten_call_jerry_function', - 'number', - ['number', 'number', 'number', 'number', 'number'], - nativeHandlerArgs); - - // Free and release all js args - Module._free(jsArgs); - while (jsRefs.length > 0) { - __jerryRefs.release(jsRefs.pop()); - } - - // decrease refcount of native handler arguments - __jerryRefs.release(nativeHandlerArgs[1]); // jsFunctionRef - __jerryRefs.release(nativeHandlerArgs[2]); // our this object - - // delete native handler arguments - nativeHandlerArgs.length = 0; - - var result_val = __jerryRefs.get(result_ref); - var has_error = __jerryRefs.getError(result_ref); - __jerryRefs.release(result_ref); - - if (has_error) { - throw result_val; - } - - return result_val; - }; - - return __jerryRefs.ref(f); -} diff --git a/applib-targets/emscripten/shims.c b/applib-targets/emscripten/shims.c deleted file mode 100644 index d2157f3e8a..0000000000 --- a/applib-targets/emscripten/shims.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "util/heap.h" -#include "util/size.h" -#include "applib/app_logging.h" -#include "applib/fonts/fonts.h" -#include "applib/fonts/fonts_private.h" -#include "applib/graphics/text_resources.h" -#include "applib/rockyjs/api/rocky_api.h" -#include "resource/resource_ids.auto.h" -#include "font_resource_keys.auto.h" -#include "font_resource_table.auto.h" - -#include "emscripten_app.h" -#include "emscripten_graphics.h" -#include "emscripten_resources.h" - -#include - -#define NUM_SYSTEM_FONTS (ARRAY_LENGTH(s_font_resource_keys)) - -void *task_malloc(size_t bytes) { - return malloc(bytes); -} - -void *task_zalloc(size_t bytes) { - void *ptr = malloc(bytes); - if (ptr) { - memset(ptr, 0, bytes); - } - return ptr; -} - -void *task_zalloc_check(size_t bytes) { - void *ptr = task_zalloc(bytes); - if (!ptr) { - wtf(); - } - return ptr; -} - -void *task_realloc(void *ptr, size_t bytes) { - return realloc(ptr, bytes); -} - -void task_free(void *ptr) { - free(ptr); -} - -void app_log(uint8_t log_level, const char* src_filename, - int src_line_number, const char* fmt, ...) { - printf("%s:%d", src_filename, src_line_number); - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - printf("\n"); -} - -GContext* app_state_get_graphics_context() { - return emx_graphics_get_gcontext(); -} - -bool app_state_get_text_perimeter_debugging_enabled(void) { - return false; -} - -Heap *app_state_get_heap(void) { - return NULL; -} - -GBitmap* app_state_legacy2_get_2bit_framebuffer(void) { - return NULL; -} - -bool heap_is_allocated(Heap* const heap, void* ptr) { - return false; -} - -void passert_failed(const char* filename, int line_number, const char* message, ...) { - APP_LOG(LOG_LEVEL_ERROR, "ASSERTION FAILED: %s:%d", filename, line_number); - EM_ASM_INT_V({ abort(); }); - while (1) ; -} - -void passert_failed_no_message(const char* filename, int line_number) { - passert_failed(filename, line_number, NULL); - while (1) ; -} - -void passert_failed_hashed_no_message(void) { - EM_ASM_INT_V({ abort(); }); - while (1); -} - -void passert_failed_hashed(uint32_t packed_loghash, ...) { - EM_ASM_INT_V({ abort(); }); - while (1); -} - -void pbl_log(uint8_t log_level, const char* src_filename, - int src_line_number, const char* fmt, ...) { - printf("%s:%d ", src_filename, src_line_number); - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - printf("\n"); -} - -bool process_manager_compiled_with_legacy2_sdk(void) { - return false; -} - -ResAppNum sys_get_current_resource_num(void) { - return 1; // 0 is system -} - -size_t sys_resource_load_range(ResAppNum app_num, uint32_t id, uint32_t start_bytes, - uint8_t *buffer, size_t num_bytes) { - return emx_resources_read(app_num, id, start_bytes, buffer, num_bytes); -} - -size_t sys_resource_size(ResAppNum app_num, uint32_t handle) { - return emx_resources_get_size(app_num, handle); -} - -GFont sys_font_get_system_font(const char *font_key) { - static FontInfo s_system_fonts_info_table[NUM_SYSTEM_FONTS + 1] = {}; - - for (int i = 0; i < (int) NUM_SYSTEM_FONTS; ++i) { - if (0 == strcmp(font_key, s_font_resource_keys[i].key_name)) { - FontInfo *fontinfo = &s_system_fonts_info_table[i]; - uint32_t resource = s_font_resource_keys[i].resource_id; - // if the font has not been initialized yet - if (!fontinfo->loaded) { - if (!text_resources_init_font(SYSTEM_APP, - resource, 0, &s_system_fonts_info_table[i])) { - // Can't initialize the font for some reason - return NULL; - } - } - return &s_system_fonts_info_table[i]; - } - } - - // Didn't find the given font, invalid key. - return (GFont)NULL; -} - -void sys_font_reload_font(FontInfo *fontinfo) { - text_resources_init_font(fontinfo->base.app_num, fontinfo->base.resource_id, - fontinfo->extension.resource_id, fontinfo); -} - -uint32_t sys_resource_get_and_cache(ResAppNum app_num, uint32_t resource_id) { - return resource_id; -} - -bool sys_resource_is_valid(ResAppNum app_num, uint32_t resource_id) { - return true; -} - -ResourceCallbackHandle resource_watch(ResAppNum app_num, - uint32_t resource_id, - ResourceChangedCallback callback, - void *data) { - return NULL; -} - -void applib_resource_munmap_or_free(void *bytes) { - free(bytes); -} - -void *applib_resource_mmap_or_load(ResAppNum app_num, uint32_t resource_id, - size_t offset, size_t num_bytes, bool used_aligned) { - if (num_bytes == 0) { - return NULL; - } - - uint8_t *result = malloc(num_bytes + (used_aligned ? 7 :0)); - if (!result - || sys_resource_load_range(app_num, resource_id, offset, result, num_bytes) != num_bytes) { - free(result); - return NULL; - } - return result; -} - -void wtf(void) { - printf(">>>> WTF\n"); - EM_ASM_INT_V({ abort(); }); - while (1) ; -} - -PebbleTask pebble_task_get_current(void) { - return PebbleTask_App; -} - -void app_event_loop(void) { - // FIXME: PBL-43469 will need to remove this init from here when multiple - // platform support is implemented. - rocky_api_watchface_init(); - emx_app_event_loop(); -} diff --git a/applib-targets/emscripten/tests/test_custom_resources.c b/applib-targets/emscripten/tests/test_custom_resources.c deleted file mode 100644 index 35d475160e..0000000000 --- a/applib-targets/emscripten/tests/test_custom_resources.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../../emscripten_resources.h" - -#include -#include - -#define ASSERT(expr) \ - do { \ - if (!(expr)) { \ - printf("%s:%d " #expr " false\n", __FILE__, __LINE__); \ - exit(-1); \ - } \ - } while (0) - - -#define CUSTOM_RES_GEN(x) \ - static uint32_t s_read_##x##_called = 0; \ - static uint32_t s_size_##x##_called = 0; \ - int custom_res_read_##x(int offset, uint8_t *buf, int num_bytes) { \ - uint32_t *buf_ptr = (uint32_t *)buf; \ - *buf_ptr = x; \ - s_read_##x##_called++; \ - return 4; \ - } \ - int custom_res_size_##x(void) { \ - s_size_##x##_called++; \ - return 4; \ - } - -CUSTOM_RES_GEN(1); -CUSTOM_RES_GEN(2); -CUSTOM_RES_GEN(3); -CUSTOM_RES_GEN(4); - -int main(int argc, char **argv) { - // 1 res - uint32_t id_1 = emx_resources_register_custom(custom_res_read_1, custom_res_size_1); - ASSERT(emx_resources_get_size(1, id_1) == 4); - uint32_t buf = 0; - ASSERT(emx_resources_read(1, id_1, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 1); - ASSERT(s_read_1_called == 1); - ASSERT(s_size_1_called == 1); - - // 2nd res - uint32_t id_2 = emx_resources_register_custom(custom_res_read_2, custom_res_size_2); - ASSERT(emx_resources_get_size(1, id_2) == 4); - buf = 0; - ASSERT(emx_resources_read(1, id_2, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 2); - ASSERT(s_read_2_called == 1); - ASSERT(s_size_2_called == 1); - - // 3rd res - uint32_t id_3 = emx_resources_register_custom(custom_res_read_3, custom_res_size_3); - ASSERT(emx_resources_get_size(1, id_3) == 4); - buf = 0; - ASSERT(emx_resources_read(1, id_3, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 3); - ASSERT(s_read_3_called == 1); - ASSERT(s_size_3_called == 1); - - // remove 2 - emx_resources_remove_custom(id_2); - ASSERT(emx_resources_get_size(1, id_2) == 0); - buf = 0; - ASSERT(emx_resources_read(1, id_2, 0, (uint8_t *)&buf, 1) == 0); - ASSERT(buf == 0); - // verify 1 & 3 are OK - buf = 0; - ASSERT(emx_resources_read(1, id_3, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 3); - ASSERT(s_read_3_called == 2); - buf = 0; - ASSERT(emx_resources_read(1, id_1, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 1); - ASSERT(s_read_1_called == 2); - - // add 4 - uint32_t id_4 = emx_resources_register_custom(custom_res_read_4, custom_res_size_4); - ASSERT(emx_resources_get_size(1, id_4) == 4); - buf = 0; - ASSERT(emx_resources_read(1, id_4, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 4); - ASSERT(s_read_4_called == 1); - ASSERT(s_size_4_called == 1); - - // remove 1 & 3 - emx_resources_remove_custom(id_1); - ASSERT(emx_resources_get_size(1, id_1) == 0); - emx_resources_remove_custom(id_3); - ASSERT(emx_resources_get_size(1, id_3) == 0); - // verify 4 is ok - buf = 0; - ASSERT(emx_resources_read(1, id_4, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 4); - ASSERT(s_read_4_called == 2); - - // remove 4 - emx_resources_remove_custom(id_4); - ASSERT(emx_resources_get_size(1, id_4) == 0); - ASSERT(s_size_4_called == 1); - - // add 4 again - id_4 = emx_resources_register_custom(custom_res_read_4, custom_res_size_4); - ASSERT(emx_resources_get_size(1, id_4) == 4); - buf = 0; - ASSERT(emx_resources_read(1, id_4, 0, (uint8_t *)&buf, 1) == 4); - ASSERT(buf == 4); - ASSERT(s_read_4_called == 3); - ASSERT(s_size_4_called == 2); - - // remove 4 again - emx_resources_remove_custom(id_4); - ASSERT(emx_resources_get_size(1, id_4) == 0); - ASSERT(s_size_4_called == 2); -} diff --git a/applib-targets/emscripten/tests/wscript b/applib-targets/emscripten/tests/wscript deleted file mode 100644 index fdd495a46e..0000000000 --- a/applib-targets/emscripten/tests/wscript +++ /dev/null @@ -1,42 +0,0 @@ -from waflib import Logs - - -def configure(conf): - conf.load('gcc waf_unit_test') - pass - - -def show_results(bld): - lst = getattr(bld, 'utest_results', []) - if lst: - Logs.pprint('CYAN', 'execution summary') - - total = len(lst) - tfail = len([x for x in lst if x[1]]) - - Logs.pprint('CYAN', ' tests that pass %d/%d' % (total-tfail, total)) - for (f, code, out, err) in lst: - if not code: - Logs.pprint('CYAN', ' %s' % f) - - if (tfail): - Logs.pprint('RED', ' tests that fail %d/%d' % (tfail, total)) - for (f, code, out, err) in lst: - if code: - Logs.pprint('CYAN', ' %s' % f) - Logs.pprint('WHITE', ' %s' % out) - - -def build(bld): - includes = ['.', '../', '../../../src/fw/'] - sources = bld.path.ant_glob('*.c') - sources.append(bld.path.parent.find_node('emscripten_resources.c')) - bld.program(features='test', - source=sources, - target='test', - cflags='-g', - includes=includes) - - bld.add_post_fun(show_results) - -# vim:filetype=python diff --git a/applib-targets/emscripten/timeshift-js/.npmignore b/applib-targets/emscripten/timeshift-js/.npmignore deleted file mode 100644 index f8cf2c305e..0000000000 --- a/applib-targets/emscripten/timeshift-js/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -npm-debug.log - diff --git a/applib-targets/emscripten/timeshift-js/LICENSE b/applib-targets/emscripten/timeshift-js/LICENSE deleted file mode 100644 index 085caa93bd..0000000000 --- a/applib-targets/emscripten/timeshift-js/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mobile Wellness Solutions MWS Ltd, Sampo Niskanen - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/applib-targets/emscripten/timeshift-js/README.md b/applib-targets/emscripten/timeshift-js/README.md deleted file mode 100644 index 647b738436..0000000000 --- a/applib-targets/emscripten/timeshift-js/README.md +++ /dev/null @@ -1,90 +0,0 @@ -TimeShift.js -============ - -TimeShift.js allows mocking / overriding JavaScript's Date object so that you can set the current time and timezone. It is meant for creating repeatable tests that utilize the current time or date. - -Usage ------ - -```javascript -new Date().toString(); // Original Date object -"Fri Aug 09 2013 23:37:42 GMT+0300 (EEST)" - -Date = TimeShift.Date; // Overwrite Date object -new Date().toString(); -"Fri Aug 09 2013 23:37:43 GMT+0300" - -TimeShift.setTimezoneOffset(-60); // Set timezone to GMT+0100 (note the sign) -new Date().toString(); -"Fri Aug 09 2013 21:37:44 GMT+0100" - -TimeShift.setTime(1328230923000); // Set the time to 2012-02-03 01:02:03 GMT -new Date().toString(); -"Fri Feb 03 2012 02:02:03 GMT+0100" - -TimeShift.setTimezoneOffset(0); // Set timezone to GMT -new Date().toString(); -"Fri Feb 03 2012 01:02:03 GMT" - -TimeShift.getTime(); // Get overridden values -1328230923000 -TimeShift.getTimezoneOffset(); -0 - -TimeShift.setTime(undefined); // Reset to current time -new Date().toString(); -"Fri Aug 09 2013 20:37:45 GMT" - -new Date().desc(); // Helper method -"utc=Fri, 09 Aug 2013 20:37:46 GMT local=Fri, 09 Aug 2013 20:37:46 GMT offset=0" - -new TimeShift.OriginalDate().toString(); // Use original Date object -"Fri Aug 09 2013 23:37:47 GMT+0300 (EEST)" -``` - -Time zones ----------- - -TimeShift.js always utilizes its internal time zone offset when converting between local time and UTC. The offset factor is fixed, and it does not take into account DST changes. Effectively it emulates a time zone with no DST. - -```javascript -new Date(1370034000000).toString(); // Original Date object uses variable offset -"Sat Jun 01 2013 00:00:00 GMT+0300 (EEST)" -new Date(1356991200000).toString(); -"Tue Jan 01 2013 00:00:00 GMT+0200 (EET)" - -Date = TimeShift.Date; // TimeShift.js uses fixed offset -new Date(1370034000000).toString(); -"Sat Jun 01 2013 00:00:00 GMT+0300" -new Date(1356991200000).toString(); -"Tue Jan 01 2013 01:00:00 GMT+0300" -``` - -The default time zone offset is the current local time zone offset. Note that this can change depending on local DST. Setting the time zone offset affects also previously created Date instances. - -The time zone offset has the same sign as [Date.getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset). For example, -120 is GMT+0200 and +120 is GMT-0200. - -Caveats -------- - -The mock implementation of Date is not perfect. - -* Many string-generation methods are incomplete and return something indicative, but not fully correct. In particular `toDateString`, `toLocaleDateString`, `toLocaleString`, `toLocaleTimeString`, `toTimeString` produce somewhat incorrect results. - -* The `toString` method does not contain any time zone name. - -* The `parse` method delegates directly to the original method and may not handle time zones correctly. - -* DST changes cannot be emulated. The time zone offset it always fixed. - -* If a library or other code holds an original Date object or a reference to the Date prototype, things may break (e.g. error messages like "this is not a Date object"). In this case you should overwrite the Date object before loading the library. - - -If you'd like to fix some of these issues, please fork the repository, implement the desired functionality, add unit tests to `tests.js` and send a pull request. - -License -------- - -TimeShift.js is Copyright 2013 Mobile Wellness Solutions MWS Ltd and Sampo Niskanen. - -It is released under the MIT license. diff --git a/applib-targets/emscripten/timeshift-js/package.json b/applib-targets/emscripten/timeshift-js/package.json deleted file mode 100644 index ab14f4a18b..0000000000 --- a/applib-targets/emscripten/timeshift-js/package.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "_args": [ - [ - { - "raw": "timeshift-js@^1.0.0", - "scope": null, - "escapedName": "timeshift-js", - "name": "timeshift-js", - "rawSpec": "^1.0.0", - "spec": ">=1.0.0 <2.0.0", - "type": "range" - }, - "/Users/martijn/Documents/Pebble/pebblesdk-generator/basalt/applib-targets/emscripten" - ] - ], - "_from": "timeshift-js@>=1.0.0 <2.0.0", - "_id": "timeshift-js@1.0.0", - "_inCache": true, - "_installable": true, - "_location": "/timeshift-js", - "_nodeVersion": "5.3.0", - "_npmUser": { - "name": "plaa", - "email": "sampo.niskanen@iki.fi" - }, - "_npmVersion": "3.3.12", - "_phantomChildren": {}, - "_requested": { - "raw": "timeshift-js@^1.0.0", - "scope": null, - "escapedName": "timeshift-js", - "name": "timeshift-js", - "rawSpec": "^1.0.0", - "spec": ">=1.0.0 <2.0.0", - "type": "range" - }, - "_requiredBy": [ - "/" - ], - "_resolved": "https://registry.npmjs.org/timeshift-js/-/timeshift-js-1.0.0.tgz", - "_shasum": "61c2eebc12e9dabc81e5f99bbae2dc8d14593f70", - "_shrinkwrap": null, - "_spec": "timeshift-js@^1.0.0", - "_where": "/Users/martijn/Documents/Pebble/pebblesdk-generator/basalt/applib-targets/emscripten", - "author": { - "name": "Sampo Niskanen", - "email": "sampo.niskanen@iki.fi" - }, - "bugs": { - "url": "https://github.com/plaa/TimeShift-js/issues" - }, - "dependencies": {}, - "description": "Time and timezone mocking / overriding", - "devDependencies": {}, - "directories": {}, - "dist": { - "shasum": "61c2eebc12e9dabc81e5f99bbae2dc8d14593f70", - "tarball": "https://registry.npmjs.org/timeshift-js/-/timeshift-js-1.0.0.tgz" - }, - "gitHead": "2b0ae910e28ebbe40482c13ba48dc5c54adfe2b7", - "homepage": "https://github.com/plaa/TimeShift-js", - "keywords": [ - "time", - "timezone", - "mocking" - ], - "license": "MIT", - "main": "timeshift.js", - "maintainers": [ - { - "name": "plaa", - "email": "sampo.niskanen@iki.fi" - } - ], - "name": "timeshift-js", - "optionalDependencies": {}, - "readme": "ERROR: No README data found!", - "repository": { - "type": "git", - "url": "git+https://github.com/plaa/TimeShift-js.git" - }, - "scripts": {}, - "version": "1.0.0" -} diff --git a/applib-targets/emscripten/timeshift-js/qunit/qunit-1.12.0.css b/applib-targets/emscripten/timeshift-js/qunit/qunit-1.12.0.css deleted file mode 100644 index 7ba3f9a30b..0000000000 --- a/applib-targets/emscripten/timeshift-js/qunit/qunit-1.12.0.css +++ /dev/null @@ -1,244 +0,0 @@ -/** - * QUnit v1.12.0 - A JavaScript Unit Testing Framework - * - * http://qunitjs.com - * - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - -webkit-border-top-right-radius: 5px; - -webkit-border-top-left-radius: 5px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-testrunner-toolbar label { - display: inline-block; - padding: 0 .5em 0 .1em; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; - overflow: hidden; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - -#qunit-modulefilter-container { - float: right; -} - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests li .runtime { - float: right; - font-size: smaller; -} - -.qunit-assert-list { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; -} - -.qunit-collapsed { - display: none; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - padding: 5px; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #3c510c; - background-color: #fff; - border-left: 10px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 10px solid #EE5757; - white-space: pre; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 5px 5px; - -moz-border-radius: 0 0 5px 5px; - -webkit-border-bottom-right-radius: 5px; - -webkit-border-bottom-left-radius: 5px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} -#qunit-testresult .module-name { - font-weight: bold; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; - width: 1000px; - height: 1000px; -} diff --git a/applib-targets/emscripten/timeshift-js/qunit/qunit-1.12.0.js b/applib-targets/emscripten/timeshift-js/qunit/qunit-1.12.0.js deleted file mode 100644 index 84c73907de..0000000000 --- a/applib-targets/emscripten/timeshift-js/qunit/qunit-1.12.0.js +++ /dev/null @@ -1,2212 +0,0 @@ -/** - * QUnit v1.12.0 - A JavaScript Unit Testing Framework - * - * http://qunitjs.com - * - * Copyright 2013 jQuery Foundation and other contributors - * Released under the MIT license. - * https://jquery.org/license/ - */ - -(function( window ) { - -var QUnit, - assert, - config, - onErrorFnPrev, - testId = 0, - fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - // Keep a local reference to Date (GH-283) - Date = window.Date, - setTimeout = window.setTimeout, - defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch( e ) { - return false; - } - }()) - }, - /** - * Provides a normalized error string, correcting an issue - * with IE 7 (and prior) where Error.prototype.toString is - * not properly implemented - * - * Based on http://es5.github.com/#x15.11.4.4 - * - * @param {String|Error} error - * @return {String} error message - */ - errorString = function( error ) { - var name, message, - errorString = error.toString(); - if ( errorString.substring( 0, 7 ) === "[object" ) { - name = error.name ? error.name.toString() : "Error"; - message = error.message ? error.message.toString() : ""; - if ( name && message ) { - return name + ": " + message; - } else if ( name ) { - return name; - } else if ( message ) { - return message; - } else { - return "Error"; - } - } else { - return errorString; - } - }, - /** - * Makes a clone of an object using only Array or Object as base, - * and copies over the own enumerable properties. - * - * @param {Object} obj - * @return {Object} New object with only the own properties (recursively). - */ - objectValues = function( obj ) { - // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392. - /*jshint newcap: false */ - var key, val, - vals = QUnit.is( "array", obj ) ? [] : {}; - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - val = obj[key]; - vals[key] = val === Object(val) ? objectValues(val) : val; - } - } - return vals; - }; - -function Test( settings ) { - extend( this, settings ); - this.assertions = []; - this.testNumber = ++Test.count; -} - -Test.count = 0; - -Test.prototype = { - init: function() { - var a, b, li, - tests = id( "qunit-tests" ); - - if ( tests ) { - b = document.createElement( "strong" ); - b.innerHTML = this.nameHtml; - - // `a` initialized at top of scope - a = document.createElement( "a" ); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ testNumber: this.testNumber }); - - li = document.createElement( "li" ); - li.appendChild( b ); - li.appendChild( a ); - li.className = "running"; - li.id = this.id = "qunit-test-output" + testId++; - - tests.appendChild( li ); - } - }, - setup: function() { - if ( - // Emit moduleStart when we're switching from one module to another - this.module !== config.previousModule || - // They could be equal (both undefined) but if the previousModule property doesn't - // yet exist it means this is the first test in a suite that isn't wrapped in a - // module, in which case we'll just emit a moduleStart event for 'undefined'. - // Without this, reporters can get testStart before moduleStart which is a problem. - !hasOwn.call( config, "previousModule" ) - ) { - if ( hasOwn.call( config, "previousModule" ) ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - runLoggingCallbacks( "moduleStart", QUnit, { - name: this.module - }); - } - - config.current = this; - - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment ); - - this.started = +new Date(); - runLoggingCallbacks( "testStart", QUnit, { - name: this.testName, - module: this.module - }); - - /*jshint camelcase:false */ - - - /** - * Expose the current test environment. - * - * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead. - */ - QUnit.current_testEnvironment = this.testEnvironment; - - /*jshint camelcase:true */ - - if ( !config.pollution ) { - saveGlobal(); - } - if ( config.notrycatch ) { - this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert ); - return; - } - try { - this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert ); - } catch( e ) { - QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); - } - }, - run: function() { - config.current = this; - - var running = id( "qunit-testresult" ); - - if ( running ) { - running.innerHTML = "Running:
" + this.nameHtml; - } - - if ( this.async ) { - QUnit.stop(); - } - - this.callbackStarted = +new Date(); - - if ( config.notrycatch ) { - this.callback.call( this.testEnvironment, QUnit.assert ); - this.callbackRuntime = +new Date() - this.callbackStarted; - return; - } - - try { - this.callback.call( this.testEnvironment, QUnit.assert ); - this.callbackRuntime = +new Date() - this.callbackStarted; - } catch( e ) { - this.callbackRuntime = +new Date() - this.callbackStarted; - - QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - QUnit.start(); - } - } - }, - teardown: function() { - config.current = this; - if ( config.notrycatch ) { - if ( typeof this.callbackRuntime === "undefined" ) { - this.callbackRuntime = +new Date() - this.callbackStarted; - } - this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert ); - return; - } else { - try { - this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert ); - } catch( e ) { - QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); - } - } - checkPollution(); - }, - finish: function() { - config.current = this; - if ( config.requireExpects && this.expected === null ) { - QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); - } else if ( this.expected !== null && this.expected !== this.assertions.length ) { - QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); - } else if ( this.expected === null && !this.assertions.length ) { - QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); - } - - var i, assertion, a, b, time, li, ol, - test = this, - good = 0, - bad = 0, - tests = id( "qunit-tests" ); - - this.runtime = +new Date() - this.started; - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - ol = document.createElement( "ol" ); - ol.className = "qunit-assert-list"; - - for ( i = 0; i < this.assertions.length; i++ ) { - assertion = this.assertions[i]; - - li = document.createElement( "li" ); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if ( bad ) { - sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); - } else { - sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); - } - } - - if ( bad === 0 ) { - addClass( ol, "qunit-collapsed" ); - } - - // `b` initialized at top of scope - b = document.createElement( "strong" ); - b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - addEvent(b, "click", function() { - var next = b.parentNode.lastChild, - collapsed = hasClass( next, "qunit-collapsed" ); - ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" ); - }); - - addEvent(b, "dblclick", function( e ) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ testNumber: test.testNumber }); - } - }); - - // `time` initialized at top of scope - time = document.createElement( "span" ); - time.className = "runtime"; - time.innerHTML = this.runtime + " ms"; - - // `li` initialized at top of scope - li = id( this.id ); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - a = li.firstChild; - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( time ); - li.appendChild( ol ); - - } else { - for ( i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - runLoggingCallbacks( "testDone", QUnit, { - name: this.testName, - module: this.module, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length, - duration: this.runtime - }); - - QUnit.reset(); - - config.current = undefined; - }, - - queue: function() { - var bad, - test = this; - - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - - // `bad` initialized at top of scope - // defer when previous test run passed, if storage is available - bad = QUnit.config.reorder && defined.sessionStorage && - +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); - - if ( bad ) { - run(); - } else { - synchronize( run, true ); - } - } -}; - -// Root QUnit object. -// `QUnit` initialized at top of scope -QUnit = { - - // call on start of module test to prepend name to all tests - module: function( name, testEnvironment ) { - config.currentModule = name; - config.currentModuleTestEnvironment = testEnvironment; - config.modules[name] = true; - }, - - asyncTest: function( testName, expected, callback ) { - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - QUnit.test( testName, expected, callback, true ); - }, - - test: function( testName, expected, callback, async ) { - var test, - nameHtml = "" + escapeText( testName ) + ""; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - if ( config.currentModule ) { - nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml; - } - - test = new Test({ - nameHtml: nameHtml, - testName: testName, - expected: expected, - async: async, - callback: callback, - module: config.currentModule, - moduleTestEnvironment: config.currentModuleTestEnvironment, - stack: sourceFromStacktrace( 2 ) - }); - - if ( !validTest( test ) ) { - return; - } - - test.queue(); - }, - - // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through. - expect: function( asserts ) { - if (arguments.length === 1) { - config.current.expected = asserts; - } else { - return config.current.expected; - } - }, - - start: function( count ) { - // QUnit hasn't been initialized yet. - // Note: RequireJS (et al) may delay onLoad - if ( config.semaphore === undefined ) { - QUnit.begin(function() { - // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first - setTimeout(function() { - QUnit.start( count ); - }); - }); - return; - } - - config.semaphore -= count || 1; - // don't start until equal number of stop-calls - if ( config.semaphore > 0 ) { - return; - } - // ignore if start is called more often then stop - if ( config.semaphore < 0 ) { - config.semaphore = 0; - QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) ); - return; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - setTimeout(function() { - if ( config.semaphore > 0 ) { - return; - } - if ( config.timeout ) { - clearTimeout( config.timeout ); - } - - config.blocking = false; - process( true ); - }, 13); - } else { - config.blocking = false; - process( true ); - } - }, - - stop: function( count ) { - config.semaphore += count || 1; - config.blocking = true; - - if ( config.testTimeout && defined.setTimeout ) { - clearTimeout( config.timeout ); - config.timeout = setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - config.semaphore = 1; - QUnit.start(); - }, config.testTimeout ); - } - } -}; - -// `assert` initialized at top of scope -// Assert helpers -// All of these must either call QUnit.push() or manually do: -// - runLoggingCallbacks( "log", .. ); -// - config.current.assertions.push({ .. }); -// We attach it to the QUnit object *after* we expose the public API, -// otherwise `assert` will become a global variable in browsers (#341). -assert = { - /** - * Asserts rough true-ish result. - * @name ok - * @function - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function( result, msg ) { - if ( !config.current ) { - throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - result = !!result; - msg = msg || (result ? "okay" : "failed" ); - - var source, - details = { - module: config.current.module, - name: config.current.testName, - result: result, - message: msg - }; - - msg = "" + escapeText( msg ) + ""; - - if ( !result ) { - source = sourceFromStacktrace( 2 ); - if ( source ) { - details.source = source; - msg += "
Source:
" + escapeText( source ) + "
"; - } - } - runLoggingCallbacks( "log", QUnit, details ); - config.current.assertions.push({ - result: result, - message: msg - }); - }, - - /** - * Assert that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * @name equal - * @function - * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); - */ - equal: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - QUnit.push( expected == actual, actual, expected, message ); - }, - - /** - * @name notEqual - * @function - */ - notEqual: function( actual, expected, message ) { - /*jshint eqeqeq:false */ - QUnit.push( expected != actual, actual, expected, message ); - }, - - /** - * @name propEqual - * @function - */ - propEqual: function( actual, expected, message ) { - actual = objectValues(actual); - expected = objectValues(expected); - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name notPropEqual - * @function - */ - notPropEqual: function( actual, expected, message ) { - actual = objectValues(actual); - expected = objectValues(expected); - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name deepEqual - * @function - */ - deepEqual: function( actual, expected, message ) { - QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name notDeepEqual - * @function - */ - notDeepEqual: function( actual, expected, message ) { - QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); - }, - - /** - * @name strictEqual - * @function - */ - strictEqual: function( actual, expected, message ) { - QUnit.push( expected === actual, actual, expected, message ); - }, - - /** - * @name notStrictEqual - * @function - */ - notStrictEqual: function( actual, expected, message ) { - QUnit.push( expected !== actual, actual, expected, message ); - }, - - "throws": function( block, expected, message ) { - var actual, - expectedOutput = expected, - ok = false; - - // 'expected' is optional - if ( typeof expected === "string" ) { - message = expected; - expected = null; - } - - config.current.ignoreGlobalErrors = true; - try { - block.call( config.current.testEnvironment ); - } catch (e) { - actual = e; - } - config.current.ignoreGlobalErrors = false; - - if ( actual ) { - // we don't want to validate thrown error - if ( !expected ) { - ok = true; - expectedOutput = null; - // expected is a regexp - } else if ( QUnit.objectType( expected ) === "regexp" ) { - ok = expected.test( errorString( actual ) ); - // expected is a constructor - } else if ( actual instanceof expected ) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if ( expected.call( {}, actual ) === true ) { - expectedOutput = null; - ok = true; - } - - QUnit.push( ok, actual, expectedOutput, message ); - } else { - QUnit.pushFailure( message, null, "No exception was thrown." ); - } - } -}; - -/** - * @deprecated since 1.8.0 - * Kept assertion helpers in root for backwards compatibility. - */ -extend( QUnit, assert ); - -/** - * @deprecated since 1.9.0 - * Kept root "raises()" for backwards compatibility. - * (Note that we don't introduce assert.raises). - */ -QUnit.raises = assert[ "throws" ]; - -/** - * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 - * Kept to avoid TypeErrors for undefined methods. - */ -QUnit.equals = function() { - QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); -}; -QUnit.same = function() { - QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); -}; - -// We want access to the constructor's prototype -(function() { - function F() {} - F.prototype = QUnit; - QUnit = new F(); - // Make F QUnit's constructor so that we can add to the prototype later - QUnit.constructor = F; -}()); - -/** - * Config object: Maintain internal state - * Later exposed as QUnit.config - * `config` initialized at top of scope - */ -config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // when enabled, show only failing tests - // gets persisted through sessionStorage and can be changed in UI via checkbox - hidepassed: false, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - // by default, modify document.title when suite is done - altertitle: true, - - // when enabled, all tests must call expect() - requireExpects: false, - - // add checkboxes that are persisted in the query-string - // when enabled, the id is set to `true` as a `QUnit.config` property - urlConfig: [ - { - id: "noglobals", - label: "Check for Globals", - tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." - }, - { - id: "notrycatch", - label: "No try-catch", - tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." - } - ], - - // Set of all modules. - modules: {}, - - // logging callback queues - begin: [], - done: [], - log: [], - testStart: [], - testDone: [], - moduleStart: [], - moduleDone: [] -}; - -// Export global variables, unless an 'exports' object exists, -// in that case we assume we're in CommonJS (dealt with on the bottom of the script) -if ( typeof exports === "undefined" ) { - extend( window, QUnit.constructor.prototype ); - - // Expose QUnit object - window.QUnit = QUnit; -} - -// Initialize more QUnit.config and QUnit.urlParams -(function() { - var i, - location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - } - } - - QUnit.urlParams = urlParams; - - // String search anywhere in moduleName+testName - config.filter = urlParams.filter; - - // Exact match of the module name - config.module = urlParams.module; - - config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = location.protocol === "file:"; -}()); - -// Extend QUnit object, -// these after set here because they should not be exposed as global functions -extend( QUnit, { - assert: assert, - - config: config, - - // Initialize the configuration options - init: function() { - extend( config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date(), - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 1 - }); - - var tests, banner, result, - qunit = id( "qunit" ); - - if ( qunit ) { - qunit.innerHTML = - "

" + escapeText( document.title ) + "

" + - "

" + - "
" + - "

" + - "
    "; - } - - tests = id( "qunit-tests" ); - banner = id( "qunit-banner" ); - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = "Running...
     "; - } - }, - - // Resets the test setup. Useful for tests that modify the DOM. - /* - DEPRECATED: Use multiple tests instead of resetting inside a test. - Use testStart or testDone for custom cleanup. - This method will throw an error in 2.0, and will be removed in 2.1 - */ - reset: function() { - var fixture = id( "qunit-fixture" ); - if ( fixture ) { - fixture.innerHTML = config.fixture; - } - }, - - // Trigger an event on an element. - // @example triggerEvent( document.body, "click" ); - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent( "MouseEvents" ); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - - elem.dispatchEvent( event ); - } else if ( elem.fireEvent ) { - elem.fireEvent( "on" + type ); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) === type; - }, - - objectType: function( obj ) { - if ( typeof obj === "undefined" ) { - return "undefined"; - // consider: typeof null === object - } - if ( obj === null ) { - return "null"; - } - - var match = toString.call( obj ).match(/^\[object\s(.*)\]$/), - type = match && match[1] || ""; - - switch ( type ) { - case "Number": - if ( isNaN(obj) ) { - return "nan"; - } - return "number"; - case "String": - case "Boolean": - case "Array": - case "Date": - case "RegExp": - case "Function": - return type.toLowerCase(); - } - if ( typeof obj === "object" ) { - return "object"; - } - return undefined; - }, - - push: function( result, actual, expected, message ) { - if ( !config.current ) { - throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); - } - - var output, source, - details = { - module: config.current.module, - name: config.current.testName, - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeText( message ) || ( result ? "okay" : "failed" ); - message = "" + message + ""; - output = message; - - if ( !result ) { - expected = escapeText( QUnit.jsDump.parse(expected) ); - actual = escapeText( QUnit.jsDump.parse(actual) ); - output += ""; - - if ( actual !== expected ) { - output += ""; - output += ""; - } - - source = sourceFromStacktrace(); - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
    Expected:
    " + expected + "
    Result:
    " + actual + "
    Diff:
    " + QUnit.diff( expected, actual ) + "
    Source:
    " + escapeText( source ) + "
    "; - } - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - pushFailure: function( message, source, actual ) { - if ( !config.current ) { - throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); - } - - var output, - details = { - module: config.current.module, - name: config.current.testName, - result: false, - message: message - }; - - message = escapeText( message ) || "error"; - message = "" + message + ""; - output = message; - - output += ""; - - if ( actual ) { - output += ""; - } - - if ( source ) { - details.source = source; - output += ""; - } - - output += "
    Result:
    " + escapeText( actual ) + "
    Source:
    " + escapeText( source ) + "
    "; - - runLoggingCallbacks( "log", QUnit, details ); - - config.current.assertions.push({ - result: false, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var key, - querystring = "?"; - - for ( key in params ) { - if ( hasOwn.call( params, key ) ) { - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - } - return window.location.protocol + "//" + window.location.host + - window.location.pathname + querystring.slice( 0, -1 ); - }, - - extend: extend, - id: id, - addEvent: addEvent, - addClass: addClass, - hasClass: hasClass, - removeClass: removeClass - // load, equiv, jsDump, diff: Attached later -}); - -/** - * @deprecated: Created for backwards compatibility with test runner that set the hook function - * into QUnit.{hook}, instead of invoking it and passing the hook function. - * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. - * Doing this allows us to tell if the following methods have been overwritten on the actual - * QUnit object. - */ -extend( QUnit.constructor.prototype, { - - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: registerLoggingCallback( "begin" ), - - // done: { failed, passed, total, runtime } - done: registerLoggingCallback( "done" ), - - // log: { result, actual, expected, message } - log: registerLoggingCallback( "log" ), - - // testStart: { name } - testStart: registerLoggingCallback( "testStart" ), - - // testDone: { name, failed, passed, total, duration } - testDone: registerLoggingCallback( "testDone" ), - - // moduleStart: { name } - moduleStart: registerLoggingCallback( "moduleStart" ), - - // moduleDone: { name, failed, passed, total } - moduleDone: registerLoggingCallback( "moduleDone" ) -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -QUnit.load = function() { - runLoggingCallbacks( "begin", QUnit, {} ); - - // Initialize the config, saving the execution queue - var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, - urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter, - numModules = 0, - moduleNames = [], - moduleFilterHtml = "", - urlConfigHtml = "", - oldconfig = extend( {}, config ); - - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - len = config.urlConfig.length; - - for ( i = 0; i < len; i++ ) { - val = config.urlConfig[i]; - if ( typeof val === "string" ) { - val = { - id: val, - label: val, - tooltip: "[no tooltip available]" - }; - } - config[ val.id ] = QUnit.urlParams[ val.id ]; - urlConfigHtml += ""; - } - for ( i in config.modules ) { - if ( config.modules.hasOwnProperty( i ) ) { - moduleNames.push(i); - } - } - numModules = moduleNames.length; - moduleNames.sort( function( a, b ) { - return a.localeCompare( b ); - }); - moduleFilterHtml += ""; - - // `userAgent` initialized at top of scope - userAgent = id( "qunit-userAgent" ); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - - // `banner` initialized at top of scope - banner = id( "qunit-header" ); - if ( banner ) { - banner.innerHTML = "" + banner.innerHTML + " "; - } - - // `toolbar` initialized at top of scope - toolbar = id( "qunit-testrunner-toolbar" ); - if ( toolbar ) { - // `filter` initialized at top of scope - filter = document.createElement( "input" ); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - - addEvent( filter, "click", function() { - var tmp, - ol = document.getElementById( "qunit-tests" ); - - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace( / hidepass /, " " ); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); - } else { - sessionStorage.removeItem( "qunit-filter-passed-tests" ); - } - } - }); - - if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { - filter.checked = true; - // `ol` initialized at top of scope - ol = document.getElementById( "qunit-tests" ); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - // `label` initialized at top of scope - label = document.createElement( "label" ); - label.setAttribute( "for", "qunit-filter-pass" ); - label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." ); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - - urlConfigCheckboxesContainer = document.createElement("span"); - urlConfigCheckboxesContainer.innerHTML = urlConfigHtml; - urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input"); - // For oldIE support: - // * Add handlers to the individual elements instead of the container - // * Use "click" instead of "change" - // * Fallback from event.target to event.srcElement - addEvents( urlConfigCheckboxes, "click", function( event ) { - var params = {}, - target = event.target || event.srcElement; - params[ target.name ] = target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - toolbar.appendChild( urlConfigCheckboxesContainer ); - - if (numModules > 1) { - moduleFilter = document.createElement( "span" ); - moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); - moduleFilter.innerHTML = moduleFilterHtml; - addEvent( moduleFilter.lastChild, "change", function() { - var selectBox = moduleFilter.getElementsByTagName("select")[0], - selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); - - window.location = QUnit.url({ - module: ( selectedModule === "" ) ? undefined : selectedModule, - // Remove any existing filters - filter: undefined, - testNumber: undefined - }); - }); - toolbar.appendChild(moduleFilter); - } - } - - // `main` initialized at top of scope - main = id( "qunit-fixture" ); - if ( main ) { - config.fixture = main.innerHTML; - } - - if ( config.autostart ) { - QUnit.start(); - } -}; - -addEvent( window, "load", QUnit.load ); - -// `onErrorFnPrev` initialized at top of scope -// Preserve other handlers -onErrorFnPrev = window.onerror; - -// Cover uncaught exceptions -// Returning true will suppress the default browser handler, -// returning false will let it run. -window.onerror = function ( error, filePath, linerNr ) { - var ret = false; - if ( onErrorFnPrev ) { - ret = onErrorFnPrev( error, filePath, linerNr ); - } - - // Treat return value as window.onerror itself does, - // Only do our handling if not suppressed. - if ( ret !== true ) { - if ( QUnit.config.current ) { - if ( QUnit.config.current.ignoreGlobalErrors ) { - return true; - } - QUnit.pushFailure( error, filePath + ":" + linerNr ); - } else { - QUnit.test( "global failure", extend( function() { - QUnit.pushFailure( error, filePath + ":" + linerNr ); - }, { validTest: validTest } ) ); - } - return false; - } - - return ret; -}; - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - runLoggingCallbacks( "moduleDone", QUnit, { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - }); - } - delete config.previousModule; - - var i, key, - banner = id( "qunit-banner" ), - tests = id( "qunit-tests" ), - runtime = +new Date() - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - "Tests completed in ", - runtime, - " milliseconds.
    ", - "", - passed, - " assertions of ", - config.stats.all, - " passed, ", - config.stats.bad, - " failed." - ].join( "" ); - - if ( banner ) { - banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - if ( config.altertitle && typeof document !== "undefined" && document.title ) { - // show ✖ for good, ✔ for bad suite result in title - // use escape sequences in case file gets loaded with non-utf-8-charset - document.title = [ - ( config.stats.bad ? "\u2716" : "\u2714" ), - document.title.replace( /^[\u2714\u2716] /i, "" ) - ].join( " " ); - } - - // clear own sessionStorage items if all tests passed - if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { - // `key` & `i` initialized at top of scope - for ( i = 0; i < sessionStorage.length; i++ ) { - key = sessionStorage.key( i++ ); - if ( key.indexOf( "qunit-test-" ) === 0 ) { - sessionStorage.removeItem( key ); - } - } - } - - // scroll back to top to show results - if ( window.scrollTo ) { - window.scrollTo(0, 0); - } - - runLoggingCallbacks( "done", QUnit, { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - }); -} - -/** @return Boolean: true if this test should be ran */ -function validTest( test ) { - var include, - filter = config.filter && config.filter.toLowerCase(), - module = config.module && config.module.toLowerCase(), - fullName = (test.module + ": " + test.testName).toLowerCase(); - - // Internally-generated tests are always valid - if ( test.callback && test.callback.validTest === validTest ) { - delete test.callback.validTest; - return true; - } - - if ( config.testNumber ) { - return test.testNumber === config.testNumber; - } - - if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { - return false; - } - - if ( !filter ) { - return true; - } - - include = filter.charAt( 0 ) !== "!"; - if ( !include ) { - filter = filter.slice( 1 ); - } - - // If the filter matches, we need to honour include - if ( fullName.indexOf( filter ) !== -1 ) { - return include; - } - - // Otherwise, do the opposite - return !include; -} - -// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) -// Later Safari and IE10 are supposed to support error.stack as well -// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack -function extractStacktrace( e, offset ) { - offset = offset === undefined ? 3 : offset; - - var stack, include, i; - - if ( e.stacktrace ) { - // Opera - return e.stacktrace.split( "\n" )[ offset + 3 ]; - } else if ( e.stack ) { - // Firefox, Chrome - stack = e.stack.split( "\n" ); - if (/^error$/i.test( stack[0] ) ) { - stack.shift(); - } - if ( fileName ) { - include = []; - for ( i = offset; i < stack.length; i++ ) { - if ( stack[ i ].indexOf( fileName ) !== -1 ) { - break; - } - include.push( stack[ i ] ); - } - if ( include.length ) { - return include.join( "\n" ); - } - } - return stack[ offset ]; - } else if ( e.sourceURL ) { - // Safari, PhantomJS - // hopefully one day Safari provides actual stacktraces - // exclude useless self-reference for generated Error objects - if ( /qunit.js$/.test( e.sourceURL ) ) { - return; - } - // for actual exceptions, this is useful - return e.sourceURL + ":" + e.line; - } -} -function sourceFromStacktrace( offset ) { - try { - throw new Error(); - } catch ( e ) { - return extractStacktrace( e, offset ); - } -} - -/** - * Escape text for attribute or text content. - */ -function escapeText( s ) { - if ( !s ) { - return ""; - } - s = s + ""; - // Both single quotes and double quotes (for attributes) - return s.replace( /['"<>&]/g, function( s ) { - switch( s ) { - case "'": - return "'"; - case "\"": - return """; - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - } - }); -} - -function synchronize( callback, last ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process( last ); - } -} - -function process( last ) { - function next() { - process( last ); - } - var start = new Date().getTime(); - config.depth = config.depth ? config.depth + 1 : 1; - - while ( config.queue.length && !config.blocking ) { - if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { - config.queue.shift()(); - } else { - setTimeout( next, 13 ); - break; - } - } - config.depth--; - if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - if ( hasOwn.call( window, key ) ) { - // in Opera sometimes DOM element ids show up here, ignore them - if ( /^qunit-test-output/.test( key ) ) { - continue; - } - config.pollution.push( key ); - } - } - } -} - -function checkPollution() { - var newGlobals, - deletedGlobals, - old = config.pollution; - - saveGlobal(); - - newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var i, j, - result = a.slice(); - - for ( i = 0; i < result.length; i++ ) { - for ( j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice( i, 1 ); - i--; - break; - } - } - } - return result; -} - -function extend( a, b ) { - for ( var prop in b ) { - if ( hasOwn.call( b, prop ) ) { - // Avoid "Member not found" error in IE8 caused by messing with window.constructor - if ( !( prop === "constructor" && a === window ) ) { - if ( b[ prop ] === undefined ) { - delete a[ prop ]; - } else { - a[ prop ] = b[ prop ]; - } - } - } - } - - return a; -} - -/** - * @param {HTMLElement} elem - * @param {string} type - * @param {Function} fn - */ -function addEvent( elem, type, fn ) { - // Standards-based browsers - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - // IE - } else { - elem.attachEvent( "on" + type, fn ); - } -} - -/** - * @param {Array|NodeList} elems - * @param {string} type - * @param {Function} fn - */ -function addEvents( elems, type, fn ) { - var i = elems.length; - while ( i-- ) { - addEvent( elems[i], type, fn ); - } -} - -function hasClass( elem, name ) { - return (" " + elem.className + " ").indexOf(" " + name + " ") > -1; -} - -function addClass( elem, name ) { - if ( !hasClass( elem, name ) ) { - elem.className += (elem.className ? " " : "") + name; - } -} - -function removeClass( elem, name ) { - var set = " " + elem.className + " "; - // Class name may appear multiple times - while ( set.indexOf(" " + name + " ") > -1 ) { - set = set.replace(" " + name + " " , " "); - } - // If possible, trim it for prettiness, but not necessarily - elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); -} - -function id( name ) { - return !!( typeof document !== "undefined" && document && document.getElementById ) && - document.getElementById( name ); -} - -function registerLoggingCallback( key ) { - return function( callback ) { - config[key].push( callback ); - }; -} - -// Supports deprecated method of completely overwriting logging callbacks -function runLoggingCallbacks( key, scope, args ) { - var i, callbacks; - if ( QUnit.hasOwnProperty( key ) ) { - QUnit[ key ].call(scope, args ); - } else { - callbacks = config[ key ]; - for ( i = 0; i < callbacks.length; i++ ) { - callbacks[ i ].call( scope, args ); - } - } -} - -// Test for equality any JavaScript type. -// Author: Philippe Rathé -QUnit.equiv = (function() { - - // Call the o related callback with the given arguments. - function bindCallbacks( o, callbacks, args ) { - var prop = QUnit.objectType( o ); - if ( prop ) { - if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { - return callbacks[ prop ].apply( callbacks, args ); - } else { - return callbacks[ prop ]; // or undefined - } - } - } - - // the real equiv function - var innerEquiv, - // stack to decide between skip/abort functions - callers = [], - // stack to avoiding loops from circular referencing - parents = [], - parentsB = [], - - getProto = Object.getPrototypeOf || function ( obj ) { - /*jshint camelcase:false */ - return obj.__proto__; - }, - callbacks = (function () { - - // for string, boolean, number and null - function useStrictEquality( b, a ) { - /*jshint eqeqeq:false */ - if ( b instanceof a.constructor || a instanceof b.constructor ) { - // to catch short annotation VS 'new' annotation of a - // declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function( b ) { - return isNaN( b ); - }, - - "date": function( b, a ) { - return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function( b, a ) { - return QUnit.objectType( b ) === "regexp" && - // the regex itself - a.source === b.source && - // and its modifiers - a.global === b.global && - // (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline && - a.sticky === b.sticky; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function() { - var caller = callers[callers.length - 1]; - return caller !== Object && typeof caller !== "undefined"; - }, - - "array": function( b, a ) { - var i, j, len, loop, aCircular, bCircular; - - // b could be an object literal here - if ( QUnit.objectType( b ) !== "array" ) { - return false; - } - - len = a.length; - if ( len !== b.length ) { - // safe and faster - return false; - } - - // track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - for ( i = 0; i < len; i++ ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[j] === a[i]; - bCircular = parentsB[j] === b[i]; - if ( aCircular || bCircular ) { - if ( a[i] === b[i] || aCircular && bCircular ) { - loop = true; - } else { - parents.pop(); - parentsB.pop(); - return false; - } - } - } - if ( !loop && !innerEquiv(a[i], b[i]) ) { - parents.pop(); - parentsB.pop(); - return false; - } - } - parents.pop(); - parentsB.pop(); - return true; - }, - - "object": function( b, a ) { - /*jshint forin:false */ - var i, j, loop, aCircular, bCircular, - // Default to true - eq = true, - aProperties = [], - bProperties = []; - - // comparing constructors is more strict than using - // instanceof - if ( a.constructor !== b.constructor ) { - // Allow objects with no prototype to be equivalent to - // objects with Object as their constructor. - if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || - ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { - return false; - } - } - - // stack constructor before traversing properties - callers.push( a.constructor ); - - // track reference to avoid circular references - parents.push( a ); - parentsB.push( b ); - - // be strict: don't ensure hasOwnProperty and go deep - for ( i in a ) { - loop = false; - for ( j = 0; j < parents.length; j++ ) { - aCircular = parents[j] === a[i]; - bCircular = parentsB[j] === b[i]; - if ( aCircular || bCircular ) { - if ( a[i] === b[i] || aCircular && bCircular ) { - loop = true; - } else { - eq = false; - break; - } - } - } - aProperties.push(i); - if ( !loop && !innerEquiv(a[i], b[i]) ) { - eq = false; - break; - } - } - - parents.pop(); - parentsB.pop(); - callers.pop(); // unstack, we are done - - for ( i in b ) { - bProperties.push( i ); // collect b's properties - } - - // Ensures identical properties name - return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); - } - }; - }()); - - innerEquiv = function() { // can take multiple arguments - var args = [].slice.apply( arguments ); - if ( args.length < 2 ) { - return true; // end transition - } - - return (function( a, b ) { - if ( a === b ) { - return true; // catch the most you can - } else if ( a === null || b === null || typeof a === "undefined" || - typeof b === "undefined" || - QUnit.objectType(a) !== QUnit.objectType(b) ) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [ b, a ]); - } - - // apply transition with (1..n) arguments - }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) ); - }; - - return innerEquiv; -}()); - -/** - * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | - * http://flesler.blogspot.com Licensed under BSD - * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 - * - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; - } - function literal( o ) { - return o + ""; - } - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) { - arr = arr.join( "," + s + inner ); - } - if ( !arr ) { - return pre + post; - } - return [ pre, inner + arr, base + post ].join(s); - } - function array( arr, stack ) { - var i = arr.length, ret = new Array(i); - this.up(); - while ( i-- ) { - ret[i] = this.parse( arr[i] , undefined , stack); - } - this.down(); - return join( "[", ret, "]" ); - } - - var reName = /^function (\w+)/, - jsDump = { - // type is used mostly internally, you can fix a (custom)type in advance - parse: function( obj, type, stack ) { - stack = stack || [ ]; - var inStack, res, - parser = this.parsers[ type || this.typeOf(obj) ]; - - type = typeof parser; - inStack = inArray( obj, stack ); - - if ( inStack !== -1 ) { - return "recursion(" + (inStack - stack.length) + ")"; - } - if ( type === "function" ) { - stack.push( obj ); - res = parser.call( this, obj, stack ); - stack.pop(); - return res; - } - return ( type === "string" ) ? parser : this.parsers.error; - }, - typeOf: function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if ( typeof obj === "undefined" ) { - type = "undefined"; - } else if ( QUnit.is( "regexp", obj) ) { - type = "regexp"; - } else if ( QUnit.is( "date", obj) ) { - type = "date"; - } else if ( QUnit.is( "function", obj) ) { - type = "function"; - } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { - type = "window"; - } else if ( obj.nodeType === 9 ) { - type = "document"; - } else if ( obj.nodeType ) { - type = "node"; - } else if ( - // native arrays - toString.call( obj ) === "[object Array]" || - // NodeList objects - ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) - ) { - type = "array"; - } else if ( obj.constructor === Error.prototype.constructor ) { - type = "error"; - } else { - type = typeof obj; - } - return type; - }, - separator: function() { - return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; - }, - // extra can be a number, shortcut for increasing-calling-decreasing - indent: function( extra ) { - if ( !this.multiline ) { - return ""; - } - var chr = this.indentChar; - if ( this.HTML ) { - chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); - } - return new Array( this.depth + ( extra || 0 ) ).join(chr); - }, - up: function( a ) { - this.depth += a || 1; - }, - down: function( a ) { - this.depth -= a || 1; - }, - setParser: function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote: quote, - literal: literal, - join: join, - // - depth: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers: { - window: "[Window]", - document: "[Document]", - error: function(error) { - return "Error(\"" + error.message + "\")"; - }, - unknown: "[Unknown]", - "null": "null", - "undefined": "undefined", - "function": function( fn ) { - var ret = "function", - // functions never have name in IE - name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; - - if ( name ) { - ret += " " + name; - } - ret += "( "; - - ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); - return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); - }, - array: array, - nodelist: array, - "arguments": array, - object: function( map, stack ) { - /*jshint forin:false */ - var ret = [ ], keys, key, val, i; - QUnit.jsDump.up(); - keys = []; - for ( key in map ) { - keys.push( key ); - } - keys.sort(); - for ( i = 0; i < keys.length; i++ ) { - key = keys[ i ]; - val = map[ key ]; - ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); - } - QUnit.jsDump.down(); - return join( "{", ret, "}" ); - }, - node: function( node ) { - var len, i, val, - open = QUnit.jsDump.HTML ? "<" : "<", - close = QUnit.jsDump.HTML ? ">" : ">", - tag = node.nodeName.toLowerCase(), - ret = open + tag, - attrs = node.attributes; - - if ( attrs ) { - for ( i = 0, len = attrs.length; i < len; i++ ) { - val = attrs[i].nodeValue; - // IE6 includes all attributes in .attributes, even ones not explicitly set. - // Those have values like undefined, null, 0, false, "" or "inherit". - if ( val && val !== "inherit" ) { - ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" ); - } - } - } - ret += close; - - // Show content of TextNode or CDATASection - if ( node.nodeType === 3 || node.nodeType === 4 ) { - ret += node.nodeValue; - } - - return ret + open + "/" + tag + close; - }, - // function calls it internally, it's the arguments part of the function - functionArgs: function( fn ) { - var args, - l = fn.length; - - if ( !l ) { - return ""; - } - - args = new Array(l); - while ( l-- ) { - // 97 is 'a' - args[l] = String.fromCharCode(97+l); - } - return " " + args.join( ", " ) + " "; - }, - // object calls it internally, the key part of an item in a map - key: quote, - // function calls it internally, it's the content of the function - functionCode: "[code]", - // node calls it internally, it's an html attribute value - attribute: quote, - string: quote, - date: quote, - regexp: literal, - number: literal, - "boolean": literal - }, - // if true, entities are escaped ( <, >, \t, space and \n ) - HTML: false, - // indentation unit - indentChar: " ", - // if true, items in a collection, are separated by a \n, else just a space. - multiline: true - }; - - return jsDump; -}()); - -// from jquery.js -function inArray( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; -} - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" - */ -QUnit.diff = (function() { - /*jshint eqeqeq:false, eqnull:true */ - function diff( o, n ) { - var i, - ns = {}, - os = {}; - - for ( i = 0; i < n.length; i++ ) { - if ( !hasOwn.call( ns, n[i] ) ) { - ns[ n[i] ] = { - rows: [], - o: null - }; - } - ns[ n[i] ].rows.push( i ); - } - - for ( i = 0; i < o.length; i++ ) { - if ( !hasOwn.call( os, o[i] ) ) { - os[ o[i] ] = { - rows: [], - n: null - }; - } - os[ o[i] ].rows.push( i ); - } - - for ( i in ns ) { - if ( hasOwn.call( ns, i ) ) { - if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) { - n[ ns[i].rows[0] ] = { - text: n[ ns[i].rows[0] ], - row: os[i].rows[0] - }; - o[ os[i].rows[0] ] = { - text: o[ os[i].rows[0] ], - row: ns[i].rows[0] - }; - } - } - } - - for ( i = 0; i < n.length - 1; i++ ) { - if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && - n[ i + 1 ] == o[ n[i].row + 1 ] ) { - - n[ i + 1 ] = { - text: n[ i + 1 ], - row: n[i].row + 1 - }; - o[ n[i].row + 1 ] = { - text: o[ n[i].row + 1 ], - row: i + 1 - }; - } - } - - for ( i = n.length - 1; i > 0; i-- ) { - if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && - n[ i - 1 ] == o[ n[i].row - 1 ]) { - - n[ i - 1 ] = { - text: n[ i - 1 ], - row: n[i].row - 1 - }; - o[ n[i].row - 1 ] = { - text: o[ n[i].row - 1 ], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function( o, n ) { - o = o.replace( /\s+$/, "" ); - n = n.replace( /\s+$/, "" ); - - var i, pre, - str = "", - out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), - oSpace = o.match(/\s+/g), - nSpace = n.match(/\s+/g); - - if ( oSpace == null ) { - oSpace = [ " " ]; - } - else { - oSpace.push( " " ); - } - - if ( nSpace == null ) { - nSpace = [ " " ]; - } - else { - nSpace.push( " " ); - } - - if ( out.n.length === 0 ) { - for ( i = 0; i < out.o.length; i++ ) { - str += "" + out.o[i] + oSpace[i] + ""; - } - } - else { - if ( out.n[0].text == null ) { - for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { - str += "" + out.o[n] + oSpace[n] + ""; - } - } - - for ( i = 0; i < out.n.length; i++ ) { - if (out.n[i].text == null) { - str += "" + out.n[i] + nSpace[i] + ""; - } - else { - // `pre` initialized at top of scope - pre = ""; - - for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { - pre += "" + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -}()); - -// for CommonJS environments, export everything -if ( typeof exports !== "undefined" ) { - extend( exports, QUnit.constructor.prototype ); -} - -// get at whatever the global object is, like window in browsers -}( (function() {return this;}.call()) )); diff --git a/applib-targets/emscripten/timeshift-js/tests.html b/applib-targets/emscripten/timeshift-js/tests.html deleted file mode 100644 index 0cb517039a..0000000000 --- a/applib-targets/emscripten/timeshift-js/tests.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - -TimeShift.js unit tests - - - -
    -
    - - - - - diff --git a/applib-targets/emscripten/timeshift-js/tests.js b/applib-targets/emscripten/timeshift-js/tests.js deleted file mode 100644 index cf00f65f50..0000000000 --- a/applib-targets/emscripten/timeshift-js/tests.js +++ /dev/null @@ -1,520 +0,0 @@ -var JAN = 0; -var FEB = 1; -var MAR = 2; -var APR = 3; -var MAY = 4; -var JUN = 5; -var JUL = 6; -var AUG = 7; -var SEP = 8; -var OCT = 9; -var NOV = 10; -var DEC = 11; - -var SUN = 0; -var MON = 1; -var TUE = 2; -var WED = 3; -var THU = 4; -var FRI = 5; -var SAT = 6; - -function matches(d, year, month, day, hour, min, sec, msec, wkday) { - equal(d.getFullYear(), year, "year"); - equal(d.getMonth(), month, "month"); - equal(d.getDate(), day, "day of month"); - equal(d.getHours(), hour, "hour"); - equal(d.getMinutes(), min, "minutes"); - equal(d.getSeconds(), sec, "seconds"); - equal(d.getMilliseconds(), msec, "milliseconds"); - equal(d.getDay(), wkday, "weekday"); - equal(d.getYear(), year-1900, "year-1900"); -} - -function matchesUTC(d, year, month, day, hour, min, sec, msec, wkday) { - equal(d.getUTCFullYear(), year, "UTC year"); - equal(d.getUTCMonth(), month, "UTC month"); - equal(d.getUTCDate(), day, "UTC day of month"); - equal(d.getUTCHours(), hour, "UTC hour"); - equal(d.getUTCMinutes(), min, "UTC minutes"); - equal(d.getUTCSeconds(), sec, "UTC seconds"); - equal(d.getUTCMilliseconds(), msec, "UTC milliseconds"); - equal(d.getUTCDay(), wkday, "UTC weekday"); -} - - -////////////////// Constructor tests /////////////////// - -test("no-arg constructor; no time set", function() { - TimeShift.setTimezoneOffset(-120); - var now = new Date(); - var d = new TimeShift.Date(); - ok(now.getTime() - d.getTime() < 500); -}); - -test("no-arg constructor; time set", function() { - TimeShift.setTimezoneOffset(-120); - TimeShift.setTime(1375991584123); // Thu 2013-08-08 19:53:04.123 UTC - var d = new TimeShift.Date(); - matches(d, 2013, AUG, 8, 21, 53, 4, 123, THU); - matchesUTC(d, 2013, AUG, 8, 19, 53, 4, 123, THU); - TimeShift.setTime(undefined); -}); - -test("no-arg constructor; reset to normal time", function() { - TimeShift.setTimezoneOffset(-120); - TimeShift.setTime(1375991584123); // Thu 2013-08-08 19:53:04.123 UTC - TimeShift.setTime(undefined); - var now = new Date(); - var d = new TimeShift.Date(); - ok(now.getTime() - d.getTime() < 500); -}); - - -test("timestamp constructor", function() { - TimeShift.setTimezoneOffset(-120); - var d = new TimeShift.Date(1375991584123); // Thu 2013-08-08 19:53:04.123 UTC - matches(d, 2013, AUG, 8, 21, 53, 4, 123, THU); - matchesUTC(d, 2013, AUG, 8, 19, 53, 4, 123, THU); -}); - -test("constructor year-month", function() { - TimeShift.setTimezoneOffset(180); - var d = new TimeShift.Date(2012, MAR); // Thu 2012-03-01 00:00:00 -0300 - matches(d, 2012, MAR, 1, 00, 00, 00, 000, THU); - matchesUTC(d, 2012, MAR, 1, 03, 00, 00, 000, THU); -}); - -test("constructor year-month-day", function() { - TimeShift.setTimezoneOffset(-180); - var d = new TimeShift.Date(2012, MAR, 5); // Mon 2012-03-05 00:00:00 +0300 - matches(d, 2012, MAR, 5, 00, 00, 00, 000, MON); - matchesUTC(d, 2012, MAR, 4, 21, 00, 00, 000, SUN); -}); - -test("constructor year-month-day-hour", function() { - TimeShift.setTimezoneOffset(-180); - var d = new TimeShift.Date(2012, MAR, 5, 23); // Mon 2012-03-05 23:00:00 +0300 - matches(d, 2012, MAR, 5, 23, 00, 00, 000, MON); - matchesUTC(d, 2012, MAR, 5, 20, 00, 00, 000, MON); -}); - -test("constructor year-month-day-hour-min", function() { - TimeShift.setTimezoneOffset(-180); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45); // Mon 2012-03-05 23:45:00 +0300 - matches(d, 2012, MAR, 5, 23, 45, 00, 000, MON); - matchesUTC(d, 2012, MAR, 5, 20, 45, 00, 000, MON); -}); - -test("constructor year-month-day-hour-min-sec", function() { - TimeShift.setTimezoneOffset(-180); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0300 - matches(d, 2012, MAR, 5, 23, 45, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 20, 45, 12, 000, MON); -}); - -test("constructor year-month-day-hour-min-sec-msec", function() { - TimeShift.setTimezoneOffset(-180); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12, 23); // Mon 2012-03-05 23:45:12.023 +0300 - matches(d, 2012, MAR, 5, 23, 45, 12, 23, MON); - matchesUTC(d, 2012, MAR, 5, 20, 45, 12, 23, MON); -}); - -test("constructor year-month with timezone shift over month border", function() { - TimeShift.setTimezoneOffset(-120); - var d = new TimeShift.Date(2012, MAR); // Thu 2012-03-01 00:00:00 +0200 - matches(d, 2012, MAR, 1, 00, 00, 00, 000, THU); - matchesUTC(d, 2012, FEB, 29, 22, 00, 00, 000, WED); -}); - -test("timezone shift over year boundary", function() { - TimeShift.setTimezoneOffset(-630); - var d = new TimeShift.Date(1356989624234); // Mon 2012-12-31 21:33:44.234 UTC - matches(d, 2013, JAN, 1, 8, 03, 44, 234, TUE); - matchesUTC(d, 2012, DEC, 31, 21, 33, 44, 234, MON); -}); - - -/////////////////// Setter tests ///////////////////// - -test("set test precondition", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - matches(d, 2012, MAR, 5, 23, 45, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 12, 000, MON); -}); - -//// Local time - -test("setFullYear(year)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setFullYear(2011); - matches(d, 2011, MAR, 5, 23, 45, 12, 000, SAT); - matchesUTC(d, 2011, MAR, 5, 18, 45, 12, 000, SAT); -}); - -test("setFullYear(year, month)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setFullYear(2011, APR); - matches(d, 2011, APR, 5, 23, 45, 12, 000, TUE); - matchesUTC(d, 2011, APR, 5, 18, 45, 12, 000, TUE); -}); - -test("setFullYear(year, month, day)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setFullYear(2011, APR, 7); - matches(d, 2011, APR, 7, 23, 45, 12, 000, THU); - matchesUTC(d, 2011, APR, 7, 18, 45, 12, 000, THU); -}); - -test("setYear(year)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setYear(111); - matches(d, 2011, MAR, 5, 23, 45, 12, 000, SAT); - matchesUTC(d, 2011, MAR, 5, 18, 45, 12, 000, SAT); -}); - -test("setMonth(month)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setMonth(APR); - matches(d, 2012, APR, 5, 23, 45, 12, 000, THU); - matchesUTC(d, 2012, APR, 5, 18, 45, 12, 000, THU); -}); - -test("setMonth(month, day)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setMonth(APR, 7); - matches(d, 2012, APR, 7, 23, 45, 12, 000, SAT); - matchesUTC(d, 2012, APR, 7, 18, 45, 12, 000, SAT); -}); - -test("setDate(day)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setDate(7); - matches(d, 2012, MAR, 7, 23, 45, 12, 000, WED); - matchesUTC(d, 2012, MAR, 7, 18, 45, 12, 000, WED); -}); - -test("setHours(hour)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setHours(20); - matches(d, 2012, MAR, 5, 20, 45, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 15, 45, 12, 000, MON); -}); - -test("setHours(hour, min)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setHours(20, 51); - matches(d, 2012, MAR, 5, 20, 51, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 15, 51, 12, 000, MON); -}); - -test("setHours(hour, min, sec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setHours(20, 51, 22); - matches(d, 2012, MAR, 5, 20, 51, 22, 000, MON); - matchesUTC(d, 2012, MAR, 5, 15, 51, 22, 000, MON); -}); - -test("setHours(hour, min, sec, msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setHours(20, 51, 22, 123); - matches(d, 2012, MAR, 5, 20, 51, 22, 123, MON); - matchesUTC(d, 2012, MAR, 5, 15, 51, 22, 123, MON); -}); - -test("setMinutes(min)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setMinutes(52); - matches(d, 2012, MAR, 5, 23, 52, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 52, 12, 000, MON); -}); - -test("setMinutes(min, sec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setMinutes(52, 22); - matches(d, 2012, MAR, 5, 23, 52, 22, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 52, 22, 000, MON); -}); - -test("setMinutes(min, sec, msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setMinutes(52, 22, 123); - matches(d, 2012, MAR, 5, 23, 52, 22, 123, MON); - matchesUTC(d, 2012, MAR, 5, 18, 52, 22, 123, MON); -}); - -test("setSeconds(sec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setSeconds(22); - matches(d, 2012, MAR, 5, 23, 45, 22, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 22, 000, MON); -}); - -test("setSeconds(sec, msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setSeconds(22, 123); - matches(d, 2012, MAR, 5, 23, 45, 22, 123, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 22, 123, MON); -}); - -test("setMilliseconds(msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setMilliseconds(123); - matches(d, 2012, MAR, 5, 23, 45, 12, 123, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 12, 123, MON); -}); - - -//// UTC time - -test("setUTCFullYear(year)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCFullYear(2011); - matches(d, 2011, MAR, 5, 23, 45, 12, 000, SAT); - matchesUTC(d, 2011, MAR, 5, 18, 45, 12, 000, SAT); -}); - -test("setUTCFullYear(year, month)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCFullYear(2011, APR); - matches(d, 2011, APR, 5, 23, 45, 12, 000, TUE); - matchesUTC(d, 2011, APR, 5, 18, 45, 12, 000, TUE); -}); - -test("setUTCFullYear(year, month, day)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCFullYear(2011, APR, 7); - matches(d, 2011, APR, 7, 23, 45, 12, 000, THU); - matchesUTC(d, 2011, APR, 7, 18, 45, 12, 000, THU); -}); - -test("setUTCMonth(month)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCMonth(APR); - matches(d, 2012, APR, 5, 23, 45, 12, 000, THU); - matchesUTC(d, 2012, APR, 5, 18, 45, 12, 000, THU); -}); - -test("setUTCMonth(month, day)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCMonth(APR, 7); - matches(d, 2012, APR, 7, 23, 45, 12, 000, SAT); - matchesUTC(d, 2012, APR, 7, 18, 45, 12, 000, SAT); -}); - -test("setUTCDate(day)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCDate(7); - matches(d, 2012, MAR, 7, 23, 45, 12, 000, WED); - matchesUTC(d, 2012, MAR, 7, 18, 45, 12, 000, WED); -}); - -test("setUTCHours(hour)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCHours(10); - matches(d, 2012, MAR, 5, 15, 45, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 10, 45, 12, 000, MON); -}); - -test("setUTCHours(hour, min)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCHours(10, 51); - matches(d, 2012, MAR, 5, 15, 51, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 10, 51, 12, 000, MON); -}); - -test("setUTCHours(hour, min, sec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCHours(10, 51, 22); - matches(d, 2012, MAR, 5, 15, 51, 22, 000, MON); - matchesUTC(d, 2012, MAR, 5, 10, 51, 22, 000, MON); -}); - -test("setUTCHours(hour, min, sec, msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCHours(10, 51, 22, 123); - matches(d, 2012, MAR, 5, 15, 51, 22, 123, MON); - matchesUTC(d, 2012, MAR, 5, 10, 51, 22, 123, MON); -}); - -test("setUTCMinutes(min)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCMinutes(52); - matches(d, 2012, MAR, 5, 23, 52, 12, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 52, 12, 000, MON); -}); - -test("setUTCMinutes(min, sec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCMinutes(52, 22); - matches(d, 2012, MAR, 5, 23, 52, 22, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 52, 22, 000, MON); -}); - -test("setUTCMinutes(min, sec, msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCMinutes(52, 22, 123); - matches(d, 2012, MAR, 5, 23, 52, 22, 123, MON); - matchesUTC(d, 2012, MAR, 5, 18, 52, 22, 123, MON); -}); - -test("setUTCSeconds(sec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCSeconds(22); - matches(d, 2012, MAR, 5, 23, 45, 22, 000, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 22, 000, MON); -}); - -test("setUTCSeconds(sec, msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCSeconds(22, 123); - matches(d, 2012, MAR, 5, 23, 45, 22, 123, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 22, 123, MON); -}); - -test("setUTCMilliseconds(msec)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setUTCMilliseconds(123); - matches(d, 2012, MAR, 5, 23, 45, 12, 123, MON); - matchesUTC(d, 2012, MAR, 5, 18, 45, 12, 123, MON); -}); - -//// Other setters - -test("setTime(time)", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12); // Mon 2012-03-05 23:45:12 +0500 - d.setTime(1375991584123); // Thu 2013-08-08 19:53:04.123 UTC - matches(d, 2013, AUG, 9, 00, 53, 04, 123, FRI); - matchesUTC(d, 2013, AUG, 8, 19, 53, 04, 123, THU); -}); - - -//////////////////// Other functionality //////////////////// - -test("getTime(), valueOf()", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12, 123); // Mon 2012-03-05 23:45:12.123 +0500 - equal(d.getTime(), 1330973112123); - equal(d.valueOf(), 1330973112123); -}); - -test("getTimezoneOffset()", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12, 123); // Mon 2012-03-05 23:45:12.123 +0500 - equal(d.getTimezoneOffset(), -300); - TimeShift.setTimezoneOffset(-650); - equal(d.getTimezoneOffset(), -650); - TimeShift.setTimezoneOffset(550); - equal(d.getTimezoneOffset(), 550); -}); - - -///////////////////// "Class" functions //////////////////// - -test("Date.now(); no time set", function() { - TimeShift.setTimezoneOffset(-120); - var now = Date.now(); - var t = TimeShift.Date.now(); - ok(now - t < 500); -}); - -test("Date.now(); time set", function() { - TimeShift.setTimezoneOffset(-120); - TimeShift.setTime(1375991584123); // Thu 2013-08-08 19:53:04.123 UTC - var t = TimeShift.Date.now(); - equal(t, 1375991584123); - TimeShift.setTime(undefined); -}); - -test("Date.now(); reset to normal time", function() { - TimeShift.setTimezoneOffset(-120); - TimeShift.setTime(1375991584123); // Thu 2013-08-08 19:53:04.123 UTC - TimeShift.setTime(undefined); - var now = Date.now(); - var t = TimeShift.Date.now(); - ok(now - t < 500); -}); - -test("Date.UTC", function() { - TimeShift.setTimezoneOffset(-120); - var t = TimeShift.Date.UTC(2012, MAR, 5, 23, 45, 12, 123); // Mon 2012-03-05 23:45:12 UTC - equal(t, 1330991112123); -}); - -// Date.parse is probably not correct - - -///////////////////// String functions (approximate) ///////////////////// - -test("toString()", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 6, 7, 8, 123); // Mon 2012-03-05 06:07:08.123 +0500 - equal(d.toString(), "Mon Mar 05 2012 06:07:08 GMT+0500"); -}); - -test("toString() 2", function() { - TimeShift.setTimezoneOffset(300); - var d = new TimeShift.Date(2012, DEC, 29, 6, 7, 8, 123); // Sat 2012-12-29 06:07:08.123 -0500 - equal(d.toString(), "Sat Dec 29 2012 06:07:08 GMT-0500"); -}); - -test("toString() 3", function() { - TimeShift.setTimezoneOffset(0); - var d = new TimeShift.Date(2012, JAN, 1, 6, 7, 8, 123); // Sun 2012-01-01 06:07:08.123 GMT - equal(d.toString(), "Sun Jan 01 2012 06:07:08 GMT"); -}); - -test("toUTCString()", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12, 123); // Mon 2012-03-05 23:45:12.123 +0500 - // IE has slightly own format - ok(d.toUTCString().match(/^Mon, 0?5 Mar 2012 18:45:12 (GMT|UTC)$/)); -}); - -test("toISOString()", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12, 123); // Mon 2012-03-05 23:45:12.123 +0500 - equal(d.toISOString(), "2012-03-05T18:45:12.123Z"); -}); - -test("toJSON()", function() { - TimeShift.setTimezoneOffset(-300); - var d = new TimeShift.Date(2012, MAR, 5, 23, 45, 12, 123); // Mon 2012-03-05 23:45:12.123 +0500 - equal(d.toJSON(), "2012-03-05T18:45:12.123Z"); -}); - diff --git a/applib-targets/emscripten/timeshift-js/timeshift.js b/applib-targets/emscripten/timeshift-js/timeshift.js deleted file mode 100644 index 24dc12c34c..0000000000 --- a/applib-targets/emscripten/timeshift-js/timeshift.js +++ /dev/null @@ -1,244 +0,0 @@ -/*! - * TimeShift.js version 20130811 - * - * Copyright 2013 Mobile Wellness Solutions MWS Ltd, Sampo Niskanen - * Released under the MIT license - */ - -// Add to Rocky.Module: - -Module.TimeShift = (function(originalDate) { - - var OriginalDate = originalDate; - - var TimeShift = {}; - - var currentTime = undefined; - var timezoneOffset = new OriginalDate().getTimezoneOffset(); - - function currentDate() { - if (currentTime) { - return new OriginalDate(currentTime); - } else { - return new OriginalDate(); - } - } - - - function realLocalToUtc(realLocal) { - return new OriginalDate(realLocal.getTime() - realLocal.getTimezoneOffset()*60*1000 + timezoneOffset*60*1000); - } - function utcToLocal(utc) { - return new OriginalDate(utc.getTime() - timezoneOffset*60*1000); - } - function localToUtc(local) { - return new OriginalDate(local.getTime() + timezoneOffset*60*1000); - } - function twoDigit(n) { - if (n < 10) { - return "0" + n; - } else { - return "" + n; - } - } - function timezoneName() { - var zone = "GMT"; - var offset = Math.abs(timezoneOffset); - if (timezoneOffset < 0) { - zone = zone + "+"; - } else if (timezoneOffset > 0) { - zone = zone + "-"; - } else { - return zone; - } - return zone + twoDigit(Math.floor(offset/60)) + twoDigit(offset%60); - } - - - /** - * Return the current time zone offset in minutes. A value of -60 corresponds to GMT+1, - * +60 to GTM-1. Default value is from new Date().getTimezoneOffset(). - */ - TimeShift.getTimezoneOffset = function() { - return timezoneOffset; - } - - /** - * Set the time zone offset in minutes. -60 corresponds to GMT+1, +60 to GTM-1. - * Changing this will affect the results also for previously created Date instances. - */ - TimeShift.setTimezoneOffset = function(offset) { - timezoneOffset = offset; - } - - /** - * Return the currently overridden time value as milliseconds after Jan 1 1970 in UTC time. - * The default value is undefined, which indicates using the real current time. - */ - TimeShift.getTime = function() { - return currentTime; - } - - /** - * Set the current time in milliseconds after Jan 1 1970 in UTC time. Setting this - * to undefined will reset to the real current time. - */ - TimeShift.setTime = function(time) { - currentTime = time; - } - - /** - * Access to the original Date constructor. - */ - TimeShift.OriginalDate = OriginalDate; - - - /** - * Mock implementation of Date. - */ - TimeShift.Date = function() { - - // Detect whether we're being called with 'new' - // From http://stackoverflow.com/questions/367768/how-to-detect-if-a-function-is-called-as-constructor - var isConstructor = false; - if (this instanceof TimeShift.Date && !this.__previouslyConstructedByTimeShift) { - isConstructor = true; - this.__previouslyConstructedByTimeShift = true; - } - if (!isConstructor) { - return (new TimeShift.Date()).toString(); - } - - switch (arguments.length) { - case 0: - this.utc = currentDate(); - break; - case 1: - this.utc = new OriginalDate(arguments[0]); - break; - case 2: - this.utc = realLocalToUtc(new OriginalDate(arguments[0], arguments[1])); - break; - case 3: - this.utc = realLocalToUtc(new OriginalDate(arguments[0], arguments[1], arguments[2])); - break; - case 4: - this.utc = realLocalToUtc(new OriginalDate(arguments[0], arguments[1], arguments[2], arguments[3])); - break; - case 5: - this.utc = realLocalToUtc(new OriginalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4])); - break; - case 6: - this.utc = realLocalToUtc(new OriginalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5])); - break; - default: - this.utc = realLocalToUtc(new OriginalDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6])); - break; - } - } - - TimeShift.Date.prototype.getDate = function() { return utcToLocal(this.utc).getUTCDate(); } - TimeShift.Date.prototype.getDay = function() { return utcToLocal(this.utc).getUTCDay(); } - TimeShift.Date.prototype.getFullYear = function() { return utcToLocal(this.utc).getUTCFullYear(); } - TimeShift.Date.prototype.getHours = function() { return utcToLocal(this.utc).getUTCHours(); } - TimeShift.Date.prototype.getMilliseconds = function() { return utcToLocal(this.utc).getUTCMilliseconds(); } - TimeShift.Date.prototype.getMinutes = function() { return utcToLocal(this.utc).getUTCMinutes(); } - TimeShift.Date.prototype.getMonth = function() { return utcToLocal(this.utc).getUTCMonth(); } - TimeShift.Date.prototype.getSeconds = function() { return utcToLocal(this.utc).getUTCSeconds(); } - - TimeShift.Date.prototype.getUTCDate = function() { return this.utc.getUTCDate(); } - TimeShift.Date.prototype.getUTCDay = function() { return this.utc.getUTCDay(); } - TimeShift.Date.prototype.getUTCFullYear = function() { return this.utc.getUTCFullYear(); } - TimeShift.Date.prototype.getUTCHours = function() { return this.utc.getUTCHours(); } - TimeShift.Date.prototype.getUTCMilliseconds = function() { return this.utc.getUTCMilliseconds(); } - TimeShift.Date.prototype.getUTCMinutes = function() { return this.utc.getUTCMinutes(); } - TimeShift.Date.prototype.getUTCMonth = function() { return this.utc.getUTCMonth(); } - TimeShift.Date.prototype.getUTCSeconds = function() { return this.utc.getUTCSeconds(); } - - TimeShift.Date.prototype.setDate = function() { var d = utcToLocal(this.utc); d.setUTCDate.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - TimeShift.Date.prototype.setFullYear = function() { var d = utcToLocal(this.utc); d.setUTCFullYear.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - TimeShift.Date.prototype.setHours = function() { var d = utcToLocal(this.utc); d.setUTCHours.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - TimeShift.Date.prototype.setMilliseconds = function() { var d = utcToLocal(this.utc); d.setUTCMilliseconds.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - TimeShift.Date.prototype.setMinutes = function() { var d = utcToLocal(this.utc); d.setUTCMinutes.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - TimeShift.Date.prototype.setMonth = function() { var d = utcToLocal(this.utc); d.setUTCMonth.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - TimeShift.Date.prototype.setSeconds = function() { var d = utcToLocal(this.utc); d.setUTCSeconds.apply(d, Array.prototype.slice.call(arguments, 0)); this.utc = localToUtc(d); } - - TimeShift.Date.prototype.setUTCDate = function() { this.utc.setUTCDate.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - TimeShift.Date.prototype.setUTCFullYear = function() { this.utc.setUTCFullYear.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - TimeShift.Date.prototype.setUTCHours = function() { this.utc.setUTCHours.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - TimeShift.Date.prototype.setUTCMilliseconds = function() { this.utc.setUTCMilliseconds.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - TimeShift.Date.prototype.setUTCMinutes = function() { this.utc.setUTCMinutes.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - TimeShift.Date.prototype.setUTCMonth = function() { this.utc.setUTCMonth.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - TimeShift.Date.prototype.setUTCSeconds = function() { this.utc.setUTCSeconds.apply(this.utc, Array.prototype.slice.call(arguments, 0)); } - - - TimeShift.Date.prototype.getYear = function() { return this.getFullYear() - 1900; } - TimeShift.Date.prototype.setYear = function(v) { this.setFullYear(v + 1900); } - - TimeShift.Date.prototype.getTime = function() { return this.utc.getTime(); } - TimeShift.Date.prototype.setTime = function(v) { this.utc.setTime(v); } - - TimeShift.Date.prototype.getTimezoneOffset = function() { return timezoneOffset; } - - TimeShift.Date.prototype.toDateString = function() { return utcToLocal(this.utc).toDateString(); } // Wrong - // Added to Date.prototype by rocky_api_datetime.c: - // TimeShift.Date.prototype.toLocaleDateString = function() { return utcToLocal(this.utc).toLocaleDateString(); } // Wrong - - TimeShift.Date.prototype.toISOString = function() { return this.utc.toISOString(); } - TimeShift.Date.prototype.toGMTString = function() { return this.utc.toGMTString(); } - TimeShift.Date.prototype.toUTCString = function() { return this.utc.toUTCString(); } - - TimeShift.Date.prototype.toString = function() { - var wkdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - var d = utcToLocal(this.utc); - // Mon Mar 05 2012 06:07:08 GMT+0500 - return wkdays[d.getUTCDay()] + " " + months[d.getUTCMonth()] + " " + twoDigit(d.getUTCDate()) + " " + d.getUTCFullYear() + - " " + twoDigit(d.getUTCHours()) + ":" + twoDigit(d.getUTCMinutes()) + ":" + twoDigit(d.getUTCSeconds()) + " " + timezoneName(); - } - - // Added to Date.prototype by rocky_api_datetime.c: - // TimeShift.Date.prototype.toLocaleString = function() { return this.toString(); } // Wrong - // TimeShift.Date.prototype.toLocaleTimeString = function() { return this.toString(); } // Wrong - TimeShift.Date.prototype.toTimeString = function() { return this.toString(); } // Wrong - - TimeShift.Date.prototype.toJSON = function() { return this.utc.toJSON(); } - TimeShift.Date.prototype.valueOf = function() { return this.utc.getTime(); } - - - TimeShift.Date.now = function() { return currentDate().getTime(); } - TimeShift.Date.parse = OriginalDate.parse; // Wrong - TimeShift.Date.UTC = OriginalDate.UTC; - - - /** - * Helper method that describes a Date object contents. - */ - TimeShift.Date.prototype.desc = function() { - return "utc=" + this.utc.toUTCString() + " local=" + utcToLocal(this.utc).toUTCString() + " offset=" + timezoneOffset; - } - - return TimeShift; -})(Date); - -// Replace original Date: -Date = Module.TimeShift.Date; - -var callTickTimerServiceHandleTimeChange = function() { - Module.ccall('tick_timer_service_handle_time_change'); -} - -Module.setTime = function(time) { - Module.TimeShift.setTime(time); - callTickTimerServiceHandleTimeChange(); -} - -Module.setTimezoneOffset = function(offset) { - Module.TimeShift.setTimezoneOffset(offset); - callTickTimerServiceHandleTimeChange(); -} - -Module.set24hStyle = function(is_24h_style) { - Module.ccall('clock_set_24h_style', null, ['number'], [is_24h_style]); - callTickTimerServiceHandleTimeChange(); -} diff --git a/applib-targets/emscripten/transform_js.py b/applib-targets/emscripten/transform_js.py deleted file mode 100755 index b1eeca9dff..0000000000 --- a/applib-targets/emscripten/transform_js.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import re -import sys - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('js_file') - parser.add_argument('--unittest', action='store_true') - args = parser.parse_args() - - # load file to be processed - with open(args.js_file, "r") as f: - source = f.read() - - # remove all known functions for memory access - # note: this implementation uses a weak heuristic: only the closing } of a - # given function has no indentation - for func in ["SAFE_HEAP_LOAD", "SAFE_HEAP_LOAD_D", "SAFE_HEAP_STORE", "SAFE_HEAP_STORE_D"]: - source = re.sub("function %s\([^\)]*\)\s*{(.*\n)+?}" % func, "", source) - - # applies the same patch as seen at - # https://github.com/kripken/emscripten/commit/bc11547fbf446993ee0f6f30a0deb3f80f205c35 - # which is part of the fix for https://github.com/kripken/emscripten/issues/3945 - # TODO: fix after PBL-32521 is done - orig_source = source - source = source.replace("funcstr += arg + '=' + convertCode.returnValue + ';';", - "funcstr += arg + '=(' + convertCode.returnValue + ');';") - # assert source != orig_source, "Emscripten output does not match expected output of 1.35.0" - - # we're not using emscripten's --pre-js and --post-js as it interferes - # with --embed-file - with open(args.js_file, "w") as f: - f.write(PROLOGUE) - if args.unittest: - f.write(UNITTEST_PROLOGUE) - f.write(source) - f.write(EPILOGUE) - if args.unittest: - f.write("new RockySimulator();\n") - -PROLOGUE = """ -RockySimulator = function(options) { - options = options || {}; - - var Module = { - print: function(text) { - console.log(text); - }, - printErr: function(text) { - console.error(text); - }, - }; - -""" - -EPILOGUE = """ -// support non-aligned memory access -function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { - if (dest <= 0) abort('segmentation fault storing ' + bytes + ' bytes to address ' + dest); - if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP); - assert(DYNAMICTOP <= TOTAL_MEMORY); - if (dest % bytes !== 0) { - for (var i = 0; i < bytes; i++) { - HEAPU8[dest + i >> 0] = (value >> (8 * i)) & 0xff; - } - } else { - setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); - } -} - -function SAFE_HEAP_STORE_D(dest, value, bytes) { - SAFE_HEAP_STORE(dest, value, bytes, true); -} - -function SAFE_HEAP_LOAD(dest, bytes, unsigned, isFloat) { - // overrule - if (dest <= 0) abort('segmentation fault loading ' + bytes + ' bytes from address ' + dest); - if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP); - assert(DYNAMICTOP <= TOTAL_MEMORY); - var type = getSafeHeapType(bytes, isFloat); - var ret; - if (dest % bytes !== 0) { - for (var i = 0; i < bytes; i++) { - ret |= HEAPU8[dest + i >> 0] << (8 * i); - } - } else { - ret = getValue(dest, type, 1); - } - if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); - return ret; -} -function SAFE_HEAP_LOAD_D(dest, bytes, unsigned) { - return SAFE_HEAP_LOAD(dest, bytes, unsigned, true); -} - - return Module; -}; - -if (typeof(module) !== "undefined") { - module.exports = RockySimulator; -} - -""" - -UNITTEST_PROLOGUE = """ -var defaultPreRun = Module.preRun; -Module.preRun = function() { - if (defaultPreRun) { - defaultPreRun(); - } - // Mount the host filesystem to make the fixture files accessible: - FS.mkdir('/node_fs'); - FS.mount(NODEFS, { root: '/' }, '/node_fs'); -} -""" - -if __name__ == "__main__": - main() diff --git a/applib-targets/emscripten/wscript b/applib-targets/emscripten/wscript deleted file mode 100644 index fedf52a196..0000000000 --- a/applib-targets/emscripten/wscript +++ /dev/null @@ -1,137 +0,0 @@ -from waflib import Logs - - -def em_resource(task): - packager = task.env.EMSCRIPTEN_ROOT + '/tools/file_packager.py' - task.exec_command(['python', - packager, - task.outputs[0].abspath(), - '--embed', - task.inputs[0].abspath(), - '--js-output=' + task.outputs[0].abspath()]) - - -def configure(conf): - conf.find_program('node', var='NODE', errmsg='node is not installed') - - # Make sure we're about to modify the 'local' conf: - prev_env = conf.env - conf.set_env(conf.all_envs['local']) - - conf.load('emscripten') - - # The standard lib emscripten bundles uses a different format - # for its stack guard!! - conf.env.CFLAGS.append('-D_TIME_H') - - # For unit tests: DUMA depends on pthread, - # which I didn't get to work with emscripten. - conf.env.DEFINES.append('DUMA_DISABLED') - - # Flags that emcc doesn't support, just remove them: - unwanted_cflags = ['-gdwarf-4'] - - if 'RELEASE' not in conf.env.DEFINES: - conf.env.EMCC_DEBUG = 2 - conf.env['CFLAGS'].extend(['-g4']) - unwanted_cflags.extend(['-g3', '-g']) - - conf.env['CFLAGS'] = filter( - lambda flag: flag not in unwanted_cflags, - conf.env['CFLAGS'] - ) - - conf.env.EMX_OTHER_SETTINGS = [ - 'SAFE_HEAP=1', - # absurdly large value so we don't worry: - 'RESERVED_FUNCTION_POINTERS=1000', - 'ERROR_ON_UNDEFINED_SYMBOLS=1' - ] - - conf.add_platform_defines(conf.env) - - conf.recurse('integration_tests') - - conf.setenv('emscripten', conf.env) - - conf.set_env(prev_env) - - -def apply_config_for_applib_and_test_rocky_emx_builds(bld): - bld.env.DEFINES.append("APPLIB_EMSCRIPTEN=1") - - # __builtin_return_address() doesn't seem to be supported by Emscripten, - # it fail at runtime due to a missing `llvm_return_address` function. - bld.env.CFLAGS.extend(['-D__builtin_return_address(level)=(0)']) - - jerry_api_js = bld.path.make_node('jerry_api.js') - timeshift_js = bld.path.find_node('timeshift-js/timeshift.js') - html_binding_js = bld.path.make_node('html-binding.js') - bld.env.EMX_PRE_JS_FILES = [jerry_api_js, timeshift_js] - bld.env.EMX_POST_JS_FILES = [html_binding_js] - # use external transformation script instead of --pre-js and --post-js so - # we can replace functions and wrap entire file without interfering with - # --embed-file - transform_js_node_and_args = [bld.path.make_node('transform_js.py')] - if bld.variant == 'test_rocky_emx': - transform_js_node_and_args.append(' --unittest') - bld.env.EMX_TRANSFORM_JS_NODE_AND_ARGS = transform_js_node_and_args - - -def build(bld): - if bld.variant == 'test': - bld.recurse('tests') - return - - # Extend waf's 'cprogram' feature with Emscripten-specific things: - bld.load('emscripten') - - apply_config_for_applib_and_test_rocky_emx_builds(bld) - - # Fine to use 'stlib' here vs emscripten_program, because we're only - # invoking emcc to generate an archive file, so only 'standard' compiler - # flags need to be passed. - bld.objects(source=['emscripten_jerry_api.c'], - target='emscripten_jerry_api', - use=['jerry_port_includes']) - - if bld.variant == 'test_rocky_emx': - return - - # Copy stuff from html folder: - html_node = bld.path.find_dir('html') - html_bld_node = bld.path.get_bld().make_node('html') - for file in html_node.ant_glob('**/*'): - bld(rule="cp ${SRC} ${TGT}", - source=file, - target=html_bld_node.make_node(file.path_from(html_node))) - - pbpack = bld.path.parent.parent.get_bld().make_node('system_resources.pbpack') - exported_functions = bld.path.make_node('exported_functions.json') - sources = bld.path.ant_glob('*.c', excl='emscripten_jerry_api.c') - rockyjs_node = html_bld_node.make_node('rocky.js') - bld.program(source=sources, - target=rockyjs_node, - emx_pre_js_files=[], - emx_post_js_files=[], - emx_exported_functions=exported_functions, - emx_other_settings=[], - emx_embed_files=[pbpack], - use=['emscripten_jerry_api', - 'applib', - 'applib_includes', - 'nanopb', - 'fw_includes', - 'libutil', - 'upng']) - - bld.recurse('integration_tests') - - def print_index_html_path(bld): - index_html_path = html_bld_node.find_node('index.html').abspath() - Logs.pprint('PINK', - 'Built Rocky Simulator: file://{}'.format(index_html_path)) - bld.add_post_fun(print_index_html_path) - - -# vim:filetype=python diff --git a/applib-targets/overrides/FreeRTOS.h b/applib-targets/overrides/FreeRTOS.h deleted file mode 100644 index 6740fc5009..0000000000 --- a/applib-targets/overrides/FreeRTOS.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef long BaseType_t; diff --git a/applib-targets/overrides/applib/applib_malloc.auto.h b/applib-targets/overrides/applib/applib_malloc.auto.h deleted file mode 100644 index 56398de5c8..0000000000 --- a/applib-targets/overrides/applib/applib_malloc.auto.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -static void* applib_zalloc(size_t size) { - void* result = malloc(size); - if (result) { - memset(result, 0, size); - } - return result; -} - -#define applib_type_zalloc(Type) applib_zalloc(sizeof(Type)) -#define applib_type_malloc(Type) malloc(sizeof(Type)) -#define applib_type_size(Type) sizeof(Type) -#define applib_malloc(size) malloc(size) -#define applib_free(ptr) free(ptr) diff --git a/applib-targets/overrides/os/mutex.h b/applib-targets/overrides/os/mutex.h deleted file mode 100644 index 4d57e392f2..0000000000 --- a/applib-targets/overrides/os/mutex.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -struct pebble_mutex_t; -typedef struct pebble_mutex_t PebbleMutex; -struct pebble_recursive_mutex_t; -typedef struct pebble_recursive_mutex_t PebbleRecursiveMutex; diff --git a/applib-targets/overrides/portmacro.h b/applib-targets/overrides/portmacro.h deleted file mode 100644 index 6740fc5009..0000000000 --- a/applib-targets/overrides/portmacro.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef long BaseType_t; diff --git a/applib-targets/overrides/queue.h b/applib-targets/overrides/queue.h deleted file mode 100644 index 1bb2a6faa3..0000000000 --- a/applib-targets/overrides/queue.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef void * QueueHandle_t; diff --git a/applib-targets/overrides/semphr.h b/applib-targets/overrides/semphr.h deleted file mode 100644 index d22f7c8754..0000000000 --- a/applib-targets/overrides/semphr.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once diff --git a/applib-targets/overrides/task.h b/applib-targets/overrides/task.h deleted file mode 100644 index 52aedc94c7..0000000000 --- a/applib-targets/overrides/task.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef void * TaskHandle_t; - -typedef struct { -} TaskParameters_t; diff --git a/applib-targets/sdl/examples/main.c b/applib-targets/sdl/examples/main.c deleted file mode 100644 index 1b4791c535..0000000000 --- a/applib-targets/sdl/examples/main.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "sdl_graphics.h" -#include "sdl_app.h" - -#include "applib/app.h" -#include "applib/graphics/gtypes.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/graphics_line.h" - - -int main(void) { - GContext *context = sdl_graphics_get_gcontext(); - graphics_context_set_stroke_color(context, GColorBrightGreen); - graphics_context_set_stroke_width(context, 2); - graphics_draw_line(context, (GPoint){0, 0}, (GPoint){100, 100}); - graphics_draw_line(context, (GPoint){0, 10}, (GPoint){100, 10}); - graphics_draw_line(context, (GPoint){0, 20}, (GPoint){100, 20}); - graphics_draw_line(context, (GPoint){0, 30}, (GPoint){100, 30}); - graphics_draw_circle(context, (GPoint){50, 50}, 20); - app_event_loop(); - - return 0; -} diff --git a/applib-targets/sdl/examples/wscript b/applib-targets/sdl/examples/wscript deleted file mode 100644 index c2627c0e1c..0000000000 --- a/applib-targets/sdl/examples/wscript +++ /dev/null @@ -1,12 +0,0 @@ -from waflib import Task -from waflib.TaskGen import feature, before_method - - -def build(bld): - sources = bld.path.ant_glob('*.c') - bld.program(source=sources, - target='sdl-example', - defines=['main=app_main'], - use=['applib_sdl', 'fw_includes']) - -# vim:filetype=python diff --git a/applib-targets/sdl/sdl_app.c b/applib-targets/sdl/sdl_app.c deleted file mode 100644 index c7c7d79730..0000000000 --- a/applib-targets/sdl/sdl_app.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sdl_app.h" -#include "sdl_graphics.h" - -#include - -extern int app_main(void); - -int main(int argc, char **argv) { - if (!sdl_app_init()) { - return -1; - } - - app_main(); - sdl_app_deinit(); - - return 0; -} - -#include - -bool sdl_app_init(void) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - printf("Error: Failed to init SDL\n"); - return false; - } - - if (!sdl_graphics_init()) { - printf("Error: Failed to init graphics\n"); - return false; - } - - return true; -} - -void sdl_app_deinit(void) { - SDL_Quit(); -} - -void sdl_app_event_loop(void) { - SDL_Event event; - int keypress = 0; - - while (!keypress) { - sdl_graphics_render(); - while (SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_QUIT: - keypress = 1; - break; - case SDL_KEYDOWN: - keypress = 1; - break; - } - } - } -} diff --git a/applib-targets/sdl/sdl_app.h b/applib-targets/sdl/sdl_app.h deleted file mode 100644 index 88406c1d67..0000000000 --- a/applib-targets/sdl/sdl_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool sdl_app_init(void); -void sdl_app_deinit(void); -void sdl_app_event_loop(void); diff --git a/applib-targets/sdl/sdl_graphics.c b/applib-targets/sdl/sdl_graphics.c deleted file mode 100644 index 3054af7e00..0000000000 --- a/applib-targets/sdl/sdl_graphics.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sdl_graphics.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/8_bit/framebuffer.h" -#include "util/circular_cache.h" - -#include -#include -#include - -static GContext s_gcontext = {}; -static FrameBuffer fb = {}; -static SDL_Surface *screen = NULL; - -bool sdl_graphics_init(void) { - if (!(screen = SDL_SetVideoMode(DISP_COLS, DISP_ROWS, 8 /* bits/pixel */, SDL_HWSURFACE))) { - return false; - } - - framebuffer_init(&fb, &(GSize) {DISP_COLS, DISP_ROWS}); - s_gcontext = (GContext) { - .dest_bitmap = (GBitmap) { - .addr = screen->pixels, - .row_size_bytes = DISP_COLS, - .info = (BitmapInfo) {.format = GBITMAP_NATIVE_FORMAT }, - .bounds = (GRect) { { 0, 0 }, { DISP_COLS, DISP_ROWS } }, - .data_row_infos = NULL, - }, - .parent_framebuffer = &fb, - .parent_framebuffer_vertical_offset = 0, - .lock = false - }; - - graphics_context_set_default_drawing_state(&s_gcontext, GContextInitializationMode_App); - - return true; -} - -GContext *sdl_graphics_get_gcontext(void) { - return &s_gcontext; -} - -void sdl_graphics_render(void) { - SDL_Flip(screen); -} diff --git a/applib-targets/sdl/sdl_graphics.h b/applib-targets/sdl/sdl_graphics.h deleted file mode 100644 index 3e8e938de4..0000000000 --- a/applib-targets/sdl/sdl_graphics.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "applib/graphics/gtypes.h" - -bool sdl_graphics_init(void); -void sdl_graphics_render(void); -GContext *sdl_graphics_get_gcontext(void); diff --git a/applib-targets/sdl/shims.c b/applib-targets/sdl/shims.c deleted file mode 100644 index f92c2895ff..0000000000 --- a/applib-targets/sdl/shims.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "util/heap.h" -#include "util/circular_cache.h" - -#include "sdl_app.h" -#include "sdl_graphics.h" - -void *task_malloc(size_t bytes) { - return malloc(bytes); -} - -void task_free(void *ptr) { - free(ptr); -} - -void app_log(uint8_t log_level, const char* src_filename, - int src_line_number, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); -} - -GContext* app_state_get_graphics_context() { - return sdl_graphics_get_gcontext(); -} - -Heap *app_state_get_heap(void) { - return NULL; -} - -GBitmap* app_state_legacy2_get_2bit_framebuffer(void) { - return NULL; -} - -void circular_cache_init(CircularCache* c, uint8_t* buffer, size_t item_size, - int total_items, Comparator compare_cb) { -} - -bool heap_is_allocated(Heap* const heap, void* ptr) { - return false; -} - -void passert_failed(const char* filename, int line_number, const char* message, ...) { -} - -void passert_failed_no_message(const char* filename, int line_number) { -} - -void pbl_log(uint8_t log_level, const char* src_filename, - int src_line_number, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); -} - -bool process_manager_compiled_with_legacy2_sdk(void) { - return false; -} - -ResAppNum sys_get_current_resource_num(void) { - return 0; -} - -const uint8_t * sys_resource_builtin_bytes(ResAppNum app_num, uint32_t resource_id, - uint32_t *num_bytes_out) { - return 0; -} - -size_t sys_resource_load_range(ResAppNum app_num, uint32_t id, uint32_t start_bytes, - uint8_t *buffer, size_t num_bytes) { - return 0; -} - -size_t sys_resource_size(ResAppNum app_num, uint32_t handle) { - return 0; -} - -void app_event_loop(void) { - sdl_app_event_loop(); -} - -void wtf(void) { - printf(">>> WTF\n"); -} diff --git a/applib-targets/sdl/wscript b/applib-targets/sdl/wscript deleted file mode 100644 index 51d0f7bbaa..0000000000 --- a/applib-targets/sdl/wscript +++ /dev/null @@ -1,27 +0,0 @@ -import waflib - - -def configure(conf): - conf.check_cfg(msg='Checking for sdl-config', - path='sdl-config', - package='', - args='--cflags --libs', - uselib_store='SDL') - - conf.find_program('objcopy gobjcopy', var='OBJCOPY') - - # We are overriding the gcc toolchain include/time.h with our own - # just to check/force our version of - conf.env.CFLAGS.append('-D_TIME_H_') - - -def build(bld): - sources = bld.path.ant_glob('*.c') - bld.stlib(source=sources, - target='applib_sdl', - includes='.', - export_includes='.', - use=['applib', 'fw_includes', 'libutil', 'upng', 'SDL']) - bld.recurse('examples') - -# vim:filetype=python diff --git a/applib-targets/wscript b/applib-targets/wscript deleted file mode 100644 index d067e20963..0000000000 --- a/applib-targets/wscript +++ /dev/null @@ -1,33 +0,0 @@ -def options(opt): - opt.add_option('--target', action='store', - choices=['sdl', 'emscripten'], - help='What backend we are compiling applib against (#rockyJS)') - - -def configure(conf): - if conf.options.target is None: - return - else: - conf.env.APPLIB_TARGET = conf.options.target - conf.recurse(conf.options.target) - - -def build(bld): - if bld.variant == 'test': - bld.recurse('emscripten') - return - - if bld.env.APPLIB_TARGET is None: - bld(export_includes=[], name='target_includes') - return - - bld.set_env(bld.all_envs['local']) - - # time_t is defined in sys/types in newlib, and time.h on recent Linux - # so just force the defined type for testing time - bld.env.CFLAGS.append('-Dtime_t=__SYSCALL_SLONG_TYPE') - - bld(export_includes=['overrides'], name='target_includes') - bld.recurse(bld.env.APPLIB_TARGET) - -# vim:filetype=python diff --git a/bin/boot/boot_asterix@1753450013.bin b/bin/boot/boot_asterix@1753450013.bin deleted file mode 100755 index 7603504e3e..0000000000 Binary files a/bin/boot/boot_asterix@1753450013.bin and /dev/null differ diff --git a/bin/boot/boot_asterix@1753450013.elf b/bin/boot/boot_asterix@1753450013.elf deleted file mode 100755 index ab529e80b9..0000000000 Binary files a/bin/boot/boot_asterix@1753450013.elf and /dev/null differ diff --git a/bin/boot/boot_asterix@1753450013.hex b/bin/boot/boot_asterix@1753450013.hex deleted file mode 100644 index 570b0a699f..0000000000 --- a/bin/boot/boot_asterix@1753450013.hex +++ /dev/null @@ -1,1021 +0,0 @@ -:1000000000000420E53A0000553B0000E51F000019 -:10001000593B00005B3B00005D3B0000000000001E -:100020000000000000000000000000005F3B000036 -:10003000613B000000000000633B0000653B0000E6 -:10004000673B0000673B0000673B0000673B000028 -:10005000673B0000673B0000673B0000673B000018 -:10006000673B0000673B0000673B0000673B000008 -:10007000673B0000673B0000673B0000673B0000F8 -:10008000673B0000673B0000673B0000673B0000E8 -:10009000673B0000673B0000673B0000673B0000D8 -:1000A000673B0000673B0000673B0000673B0000C8 -:1000B000673B0000673B00000000000000000000FC -:1000C000673B0000673B0000673B0000673B0000A8 -:1000D000673B0000673B0000673B0000673B000098 -:1000E000673B0000673B0000673B0000000000002A -:1000F00000000000673B000000000000673B0000BC -:1001000000000000000000000000000000000000EF -:1001100000000000000000000000000000000000DF -:1001200000000000000000000000000000000000CF -:1001300000000000000000000000000000000000BF -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000000000000000000000000000000002F -:1001D000000000000000000000000000000000001F -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000537475636B20627574746F6E20726567CA -:10021000697374657220697320696E76616C6964B4 -:100220002C20636C656172696E672E004275747470 -:100230006F6E2069642000697320737475636B218D -:1002400000427574746F6E207761732070757368E7 -:100250006564206F6E20626F6F742E204275747417 -:100260006F6E20636F756E7465723A200042616331 -:100270006B0055700053656C65637400446F776E56 -:10028000006D783235752E63004A45444543204958 -:10029000443A2000616464726573732000206973BE -:1002A000206F7574736964652073797374656D204C -:1002B000666C6173680073797374656D5F666C61F9 -:1002C00073685F65726173653A2061646472657317 -:1002D00073206E6F7420776F726420616C69676E33 -:1002E00065640073797374656D5F666C6173685FD4 -:1002F00077726974653A2061646472657373206E05 -:100300006F7420776F726420616C69676E6564003A -:10031000496E76616C6964206669726D7761726599 -:10032000206465736372697074696F6E210043683D -:1003300065636B73756D6D696E67206669726D7745 -:10034000617265207570646174650043616C6375EA -:100350006C6174656420636865636B73756D3A20C6 -:1003600000496E76616C6964206669726D776172AE -:10037000652043524320696E2053504920666C61CA -:100380007368210065726173655F6F6C645F666995 -:10039000726D776172650077726974655F6E6577FB -:1003A0005F6669726D7761726500576527726520B7 -:1003B0006465616400436865636B73756D6D696E38 -:1003C0006720002062797465730D0A0043686563D5 -:1003D0006B73756D202D2077616E7465642000202D -:1003E000676F7420004F757220696E7465726E615C -:1003F0006C20666C61736820636F6E74656E7473D5 -:1004000020617265206261642028636865636B7394 -:10041000756D206661696C65642921205468697373 -:10042000206973207265616C6C79206261642100BF -:100430004F75722070726576696F75732066697288 -:100440006D7761726520757064617465206661699D -:100450006C65642C2061626F7274696E67207570C0 -:10046000646174652E004E6577206669726D7761F0 -:10047000726520697320617661696C61626C6521C7 -:10048000004C6F6164696E67207265636F76657298 -:1004900079206669726D77617265004661696C6585 -:1004A0006420746F206C6F6164207265636F766581 -:1004B0007279206669726D776172652C207374722F -:1004C000696B65206F6E652E2054727920616761BB -:1004D000696E2E004661696C656420746F206C6FD4 -:1004E0006164207265636F76657279206669726DEA -:1004F000776172652C20737472696B652074776FF5 -:100500002E2054727920616761696E2E0046616900 -:100510006C656420746F206C6F6164207265636F1A -:1005200076657279206669726D776172652C2073C9 -:100530007472696B652074687265652E205341443E -:100540002057415443480048415244204641554CAD -:1005500054002020205F202020202020205F202009 -:100560002020202020202020205F2020202020006C -:1005700020202F5F5C2020205F5F7C207C5F205F3D -:100580005F5F205F205F285F295F205F5F00202F73 -:10059000205F205C20285F2D3C20205F2F202D5FD6 -:1005A0002920275F7C205C205C202F002F5F2F20DC -:1005B0005C5F5C2F5F5F2F5C5F5F5C5F5F5F7C5F9A -:1005C0007C207C5F2F5F5C5F5C00626F6F742062D9 -:1005D0006974004C617374206669726D776172652D -:1005E00020626F6F742077617320737461626C6531 -:1005F0003B20636C65617220737472696B65730074 -:10060000504D494320696E6974206661696C656468 -:1006100000537475636B20627574746F6E00427260 -:100620006F6B656E20666C61736800486F6C642048 -:10063000646F776E205550202B204241434B202B76 -:100640002053454C45435420666F72203520736516 -:1006500063732E20746F20666F7263652D626F6FF7 -:100660007420505246004669726D77617265206948 -:100670007320657261736564005761746368646FA9 -:1006800067206361757365642061207265736574AA -:1006900000536F667477617265206661696C75726C -:1006A000652063617573656420612072657365748C -:1006B000004661696C656420746F207374617274A4 -:1006C000206669726D776172652C20737472696B34 -:1006D000652074687265652E004661696C656420EA -:1006E000746F207374617274206669726D776172C1 -:1006F000652C20737472696B652074776F2E0046C9 -:1007000061696C656420746F207374617274206613 -:1007100069726D776172652C20737472696B6520E4 -:100720006F6E652E00466F7263652D626F6F746920 -:100730006E67207265636F76657279206D6F646590 -:100740002E2E2E00426F6F74696E67206669726D7F -:1007500077617265204020002E2E2E0D0A0D0A00B2 -:10076000426F6F7420626974733A2000536F66742D -:1007700077617265206661696C7572653B20726590 -:1007800073657474696E6721004669726D77617272 -:1007900065206C656E6774683A2000436865636B1A -:1007A00073756D3A20004153534552543A20003A34 -:1007B00000415353455254004153534552544E0047 -:1007C0002A2A2A20575446200053544D33320053CE -:1007D000544D3332207065726970686572616C20A7 -:1007E0006C696272617279207472697070656420DC -:1007F000616E206173736572740043524F414B20E8 -:100800004F4F4D0052657461696E65642072656773 -:10081000697374657220435243206661696C656434 -:100820003A20657870656374656420435243200004 -:100830002C20676F742043524320002E2020436CED -:10084000656172696E6720626F6F7462697473218B -:100850000069746F612062756666657220746F6FDF -:1008600020736D616C6C002E2E2F2E2E2F2E2E2FAE -:100870002E2E2F74686972645F70617274792F68AC -:10088000616C5F6E6F726469632F6E7266782F6839 -:10089000616C2F6E72665F6770696F2E68002E2E16 -:1008A0002F2E2E2F2E2E2F2E2E2F74686972645FFE -:1008B00070617274792F68616C5F6E6F72646963C6 -:1008C0002F6E7266782F647269766572732F7372F9 -:1008D000632F6E7266785F717370692E63002E2EBF -:1008E0002F2E2E2F2E2E2F2E2E2F74686972645FBE -:1008F00070617274792F68616C5F6E6F7264696386 -:100900002F6E7266782F647269766572732F7372B8 -:10091000632F6E7266785F7370696D2E63002E2E82 -:100920002F2E2E2F2E2E2F2E2E2F74686972645F7D -:1009300070617274792F68616C5F6E6F7264696345 -:100940002F6E7266782F647269766572732F737278 -:10095000632F6E7266785F7477692E630000000003 -:100960006D020000000000501C00000003000000A9 -:1009700072020000000000501D0000000300000093 -:1009800075020000000000501E000000030000007F -:100990007C020000000000501F0000000300000067 -:1009A00001424F4F544C4F4144455200000000005B -:1009B000002A2A00000000000000000000000000E3 -:1009C0000338FDD870470000000000000000000060 -:1009D000000400000000000000000000000000040F -:1009E0000000000000000000000000000000000007 -:1009F0000000000000000000008000042000000053 -:100A00000000000000000000010E100000000000C7 -:100A1000000000000000803F0000000000C00060F7 -:100A200000300000FCF1073E000000C00060003014 -:100A30000000FCE0077F000000C000600030000004 -:100A40000C008663000000C00060003000000C0055 -:100A50008601F001F8C07C603E30E0030C008601A6 -:100A6000FC07FEC3FFE1FF30F80F4E108E030E0EA1 -:100A700007C783E3C1311C1C46100C0F060C03C6CC -:100A800001E380310C184310183E039881CF0066B3 -:100A9000003306BE030038780398F9CF00660033B0 -:100AA000E63F431018600398FFC000660033FE0362 -:100AB000860F0CC003980FC0006600333E000E0086 -:100AC0000EC0039801CC0066003306300C00C6C08F -:100AD000070C038601C380310C180C00C6C00F0E32 -:100AE0008F8783C3C1311C1C0C008661FF07FE0386 -:100AF000FF81FF70F80FFCE0877FF301F8007C00B6 -:100B00003E60E003FCF1071E03000000000000004F -:100B10000000803F00000300000000000000000013 -:100B2000010E10000300000000000000008000041F -:100B30002000030000000000000000000000000092 -:100B400000000000000000000000000400000000A1 -:100B5000000000000000000000040000000000E0B1 -:100B6000FF0F000000000000F8FF7F000000000001 -:100B700000FEFFFF030000000080FFFFFF1F0000DA -:100B80000000E01F00F87F00000000F80F00C0FF29 -:100B900000000000FE030000FE010000807F000056 -:100BA00000F0030000C01F000000C0070000C007E5 -:100BB000000000800F0000E003000000001F0000A4 -:100BC000E001000000003E0000F000000000007C9A -:100BD0000000F00000000000780000780000000035 -:100BE00000F00000780000000000F000003C300041 -:100BF000000000E001003C7000000000E001001E69 -:100C00007000700000C003001EF000780000C003F8 -:100C1000001EE0007C00008007000FE0013E0000A5 -:100C20008007000FC0011F00008007000FC0830F66 -:100C300000008007000FC0C307000080078007E0A6 -:100C4000C303000080078007F0C103000080078015 -:100C500007F880030000800780077C800700008081 -:100C600007C0033E000700008007C0031E000F00FE -:100C7000008007E0030E000E00008007F001000076 -:100C80000E00008007F80000000C000080077800CC -:100C9000C07100000080077C00E079001800800728 -:100CA0003C00F0FF003C0080073C00F8FF003C00E7 -:100CB00080073E00F8FF011E0080071E0038CF01AC -:100CC0001E0080071E0000C7010F00C0031F0000A8 -:100CD00000000F00C0030F000000800700E0010FBC -:100CE000000000800700E0010F000000C00300F0DA -:100CF000000F000000C00300F0000F000000C00360 -:100D00000078000F600000C0030078000FF80000BA -:100D1000C0033C3C000FFE0100C0033F3C009FFFAE -:100D20000300C0C33F1E00FFFF0700C0E30F1E000B -:100D3000FE870F00C0FF031E00F8031F00C0FF0066 -:100D40001E0060003E00807F001E000000FC0000CE -:100D50003E001E000000F80F0018001E000000F00A -:100D60007F0000001E000000E0FF0700001E0000E2 -:100D70000080FF3F00001E00000000F8FF03003C61 -:100D800000000000C0FF0F003C0000000000FC3F1E -:100D900000780000000000E0FF00F8000000000004 -:100DA00000FE03F0010000000000F01FE00300005F -:100DB000000000C07FC007000000000000FFFF0F20 -:100DC000000000000000FCFF1F000000000000F019 -:100DD000FF1F000000000000C0FF0F7C00FE00FFAE -:100DE00001C701C701C701C701C701C701C701C7C3 -:100DF00001C701C701C701C701FF01FE007C003820 -:100E0000003C003E003E0038003800380038003812 -:100E100000380038003800380038003800FE00FE86 -:100E200000FE007C00FE00FF01C701C701C001C039 -:100E300001E000F00078003C001E000E000F0007EB -:100E400000FF01FF01FF017C00FE00FF01C701C799 -:100E500001C001C001F8007800F800C001C001C065 -:100E600001C701C701FF01FE007C00E000E000F0C7 -:100E700000F000F800F800F800FC00EC00EE00E6DE -:100E800000FF01FF01FF01E000E000E000E000FFE3 -:100E900000FF00FF000700070007007F00FF00FFC2 -:100EA00001C701C001C001C701C701C701FF01FEA1 -:100EB000007C007C00FE00FF01C701C7010700079E -:100EC000007700FF00FF01C701C701C701C701C7C5 -:100ED00001FF01FE007C00FF01FF01FF01E000E0D7 -:100EE0000070007000700038003800380038001CB6 -:100EF000001C001C001C001C001C007C00FE00FFED -:100F000001C701C701C701C701FE007C00FE00C781 -:100F100001C701C701C701C701FF01FE007C007CBA -:100F200000FE00FF01C701C701C701C701C701FFDC -:100F300001FE01DC01C001C001C701C701FF01FEC4 -:100F4000007C0000000000000000007C00FE00FFAC -:100F500001C701C701F001FC01CE01C701C701E7CC -:100F600001FF01DF01CE010700070007000700E7CE -:100F700000F701FF01CF01C701C701C701C701C7C2 -:100F800001C701CF01FF01F701E7000000000000E9 -:100F90000000007C00FE00FF01C701C70107000739 -:100FA0000007000700C701C701FF01FE007C00C069 -:100FB00001C001C001C001CE01DF01FF01E701C78F -:100FC00001C701C701C701C701C701E701FF01DF71 -:100FD00001CE0100000000000000007C00FE00FFC8 -:100FE00001C701C701C701FF01FF010700C701C712 -:100FF00001FF01FE007C00E000F000F8003800FE78 -:1010000000FE00FE00380038003800380038003894 -:101010000038003800380038003800FFFFFFFFFFBD -:10102000FFFFFFFFFFFFFF010000000000000000C6 -:1010300000008001000000000000000000008001AE -:10104000000000000000000000008001000000001F -:10105000000000000000800100000000000000000F -:10106000000080010000000000000000000080FF80 -:10107000FFFFFFFFFFFFFFFFFFFFFF00000000007B -:101080000338FDD8704700000E0000000F0000007C -:101090000000980107000000004000400000000030 -:1010A0000701010E01000B06000B010104020104FF -:1010B0000501040701040C00040D00040E000415D2 -:1010C00000020200030501050A01030C08030D04D8 -:1010D000030810030900030A2A03010103000103A6 -:1010E00010BB0311010312A40313020314540315CC -:1010F000010316540317010123440123900123FA2D -:101100000123CE034604035001030401080500082F -:101110000C08080801080001080600080D08080965 -:101120000108020106000006010006020006030095 -:1011300006040000000000006410B71DC8206E3BCC -:10114000AC30D9269041DC76F4516B6B5861B24DCE -:101150003C7105502083B8ED44930FF0E8A3D6D638 -:101160008CB361CBB0C2649BD4D2D38678E20AA0A0 -:101170001CF2BDBD000000000000000000000000E7 -:101180000338FDD870470000000000000000000098 -:101190000338FDD870470000FF000000FF0000008A -:1011A000FF000000FF000000FF000000FF00000043 -:0811B0000338FDD87047000070 -:0811B80094000000010000009A -:1011C0000348044B834202D0034B03B118477047D6 -:1011D0002C0100202C010020000000000548064BD7 -:1011E0001B1AD90F01EBA301491002D0034B03B125 -:1011F000184770472C0100202C010020000000003F -:1012000010B5064C237843B9FFF7DAFF044B13B14E -:101210000448AFF300800123237010BD2C0100208F -:1012200000000000083E000008B5054B1BB1054951 -:101230000548AFF30080BDE80840FFF7CFBF00BF0F -:101240000000000030010020083E0000A3F5803AB5 -:10125000704700BF174B002B08BF134B9D46FFF78D -:10126000F5FF00218B460F461348144A121A02F06C -:1012700081FD0E4B002B00D098470D4B002B00D06A -:1012800098470020002104000D000D48002802D0DE -:101290000C48AFF3008002F075FD2000290000F03B -:1012A000EFFE02F055FD00BF000008000000000046 -:1012B00000000000000004202C010020F0020120AA -:1012C0000000000000000000F7B5012001F072F9F5 -:1012D0000024019001AD2746E0B200F049F806462F -:1012E000A0B928700134042C05F10105F4D1019C4A -:1012F0003CB1174800F0F6F8204600F012F900F073 -:1013000087F82146012001F047F90DE02B78042BE6 -:101310000DD9104800F0FEF8019800F002F9002104 -:10132000012001F039F90027384603B0F0BD013340 -:10133000DBB2052B2B70D5D1074800F0D3F820463F -:1013400000F0EFF8054800F0E5F83746CAE700BFBF -:1013500041020000000200002C02000037020000E1 -:1013600008B500F049FB80F00100C0B208BD0000E4 -:10137000064B03EB001043688268D3F81035012058 -:1013800090401840B0FA80F04009704760090000B2 -:10139000002110B50C46C8B2FFF7EAFF88400131C2 -:1013A00004430429E4B2F6D1204610BD10B5074B22 -:1013B0000422D3E90141187B01F5E0718000013A74 -:1013C00044F8210003F11003F3D110BD60090000BF -:1013D000074B0422C3F80025C3F81C05D3F81C21D1 -:1013E000012AFBD10022C3F81C21C3F80025704755 -:1013F00000200040054B4FF08052C3F82425012205 -:101400009A601B22C3F80C25704700BF00200040E3 -:101410002DE9F843304E346804F12E0323F007031E -:1014200000AFADEB030D50238DF8003021238DF874 -:10143000013000238DF8023003238DF8033004F1CE -:1014400021035BBAADF80430244B04F127086D4644 -:101450000DF1060203F11C0153F8040B42F8040BD2 -:101460008B42F9D11B78137004F1230922461C49E1 -:1014700005F1230002F0AAFC4A462946002001F0AB -:10148000CBF804F59273FE22B3FBF2F34344073327 -:1014900023F00703ADEB030D45F80900424629464A -:1014A000684601F08DF801465520FFF791FF6C4624 -:1014B00069448C4207D15520FFF78AFF002333602F -:1014C000BD46BDE8F88314F8010B552808BF00207D -:1014D000FFF77EFFEDE700BF48010020A0090000F4 -:1014E0004C01002038B50A4D441E14F8012F12B1EA -:1014F0002B68FF2B00D938BD0A2A02D1FFF788FFDD -:10150000F3E70D2A1FBF591C5B1929601A71ECE71C -:101510004801002008B5FFF7E5FFBDE80840FFF7E8 -:1015200077BF1FB50C2201A901F0A4F801A8FFF7AD -:10153000D9FF05B05DF804FB7FB50DF1070300226C -:101540008DF80700029302A9012304480592CDE912 -:10155000033201F08FFE07B05DF804FB00010020AC -:10156000F8B50733122706460698079C0731DD08B1 -:1015700007FB02F303EBD1031044234407FB0044B1 -:1015800004EBD104314618462A4602F01FFC0346FC -:101590001233A3422E44F5D1F8BD0000044B0822BB -:1015A000C3F80825034B4FF4E07043F001031847DC -:1015B00000030050C009000010B5084C4FF4E07063 -:1015C00044F00104A047064B0822C3F80C254FF451 -:1015D00080702346BDE81040184700BFC0090000D6 -:1015E000000300502DE9F0410646FFF7D7FF8020A9 -:1015F000FFF7A2FF0025012420BA90FAA0F0C0B2A4 -:10160000FFF79AFF06EB0508002718F8010B00BA50 -:1016100090FAA0F00137C0B2FFF78EFF122FF4D17D -:1016200000200134FFF788FFA92C05F11205E3D152 -:101630000020FFF781FFBDE8F041FFF7BDBF0000CC -:1016400010B5ADF6D83D02AC4FF43D620021204606 -:1016500002F090FB1D2300934522702310210548C2 -:101660000194FFF77DFF2046FFF7BCFF0DF6D83D44 -:1016700010BD00BFC60900002DE9F04FADF6E43DF6 -:1016800004AF03904FF43D620021384602F072FB34 -:101690004723264800930197452318222321FFF76B -:1016A0005FFF1C2005464FF024084FF0010C039A01 -:1016B0000F23834013401E4AC34008FB03230DF64B -:1016C0003806002403F1010913F814B00022082A97 -:1016D00006D00CFA02F111EA0B0F05D10132F6E740 -:1016E00019F81410C9070FD55119C1F3C40E01F030 -:1016F000070116F80EA00CFA01F1013241EA0A01C5 -:10170000092A06F80E10E2D10134122C06F1120655 -:10171000DAD10B35742DA0F10400C8D13846FFF79B -:1017200061FF0DF6E43DBDE8F08F00BF5C0B0000EB -:10173000DB0D000008B5FFF731FF0020FFF7FCFECE -:101740000020FFF7F9FE0020FFF7F6FEBDE8084095 -:10175000FFF732BF5E23F0B55843214BB0FBF1F4E5 -:101760009A68A242ADF6DC3D37D002AD4FF43D623F -:10177000002128469C6002F0FDFA1D2319480093C1 -:101780004522702310210195FFF7EAFE08230093FC -:1017900018211548019560236A22FFF7E1FE0DF23A -:1017A0008E7700214FF0010C781800220DE002F135 -:1017B0001903C3F3C40603F0070310F806E00CFA9C -:1017C00003F343EA0E0383550132A242EFD11231F3 -:1017D0006C29E9D12846FFF705FF0DF6DC3DF0BD89 -:1017E00000010020C60900001B10000038B51B4D89 -:1017F0001B481C4B2B621C4A48F20C0303802F230E -:10180000C2F8603501210723C2F8001540F21144E7 -:10181000C2F80C350023C2F80435C2F80845C2F8F6 -:101820001435C2F81035C2F82005C2F82415284630 -:10183000C2F82835C2F82C3591601A4605F10C0122 -:1018400001F080FC094B0322C3F80C274FF0A043A2 -:10185000C3F810271022C3F8082538BD0001002066 -:101860004C02002040420F0000C001400003005025 -:1018700008B5044801F0A2FC034B0022C3F8002580 -:1018800008BD00BF0001002000C0014070B50C463B -:10189000ADF5806D00254618B4F5806FA6EB040108 -:1018A0000BD82246684600F0C9F822466946284609 -:1018B00000F0B2FE0DF5806D70BD4FF48062684699 -:1018C00000F0BCF828464FF48062694600F0A4FEA0 -:1018D000A4F580640546DFE707B540F20113013245 -:1018E000ADF8023000238DF800008DF80120684625 -:1018F0000A461946ADF8043001F064F9044B9842E9 -:1019000003D00448162100F0E9FD03B05DF804FBA4 -:101910000000AD0B8102000007B5013240F2011357 -:101920008DF800008DF8012068460022ADF80230E5 -:10193000ADF8042001F046F9044B984203D0044866 -:101940001F2100F0CBFD03B05DF804FB0000AD0BE0 -:101950008102000010B513238CB00024CDE90143AF -:1019600011221423CDE9032315211623CDE90513F4 -:101970001E4BDFF880C01720CDE907034FF4FA7340 -:10198000CDE909C322460723214601A80B9301F0A4 -:10199000E9F8224621466620FFF7BEFF224621468F -:1019A0009920FFF7B9FF124B4FF40C6043F001038D -:1019B000984722462146B720FFF7AEFF694601222D -:1019C0000520FFF789FF01220DEB02013520FFF70B -:1019D00083FF9DF80130022243F002036946012093 -:1019E0008DF80130FFF798FF0CB010BD0402010024 -:1019F000801000000500000313B5032201A99F20F9 -:101A0000FFF76AFF9DF805409DF80430094824025D -:101A100044EA03449DF806301C43FFF763FD20466B -:101A2000FFF77FFDFFF7F4FC0348231A58425841A3 -:101A300002B010BD890200001960C8002DE9F34111 -:101A4000454205F00305954288BFD5B2541B04F00A -:101A5000030806460F4624F003049DB10A460421FC -:101A60000DEB010001F016F9184B984203D05D21EF -:101A7000174800F033FD30462A4601A902F0A6F9C6 -:101A80002F442E445CB13A462146304601F002F91B -:101A90000E4B984201D06621EAE727442644B8F16C -:101AA000000F0FD004213A460DEB010001F0F2F8CF -:101AB000064B984201D06E21DAE7424601A9304632 -:101AC00002F084F902B0BDE8F08100BF0000AD0B68 -:101AD000810200002DE9F0411F4900231A46ACB0F5 -:101AE00001F1100001F0B6FE1C4B984206462ED1C3 -:101AF0001B4801F003FF1B49932207A802F066F977 -:101B000007AC00274FF4D6480025032021786278DF -:101B1000A3788DF80410CDE9030501A8CDE90505EA -:101B20008DF805200E48ADF808802A4602A98DF8E8 -:101B3000063001F02FFFB04209D10137312F04F1F7 -:101B40000304E1D1064801F0F9FE284601E04FF018 -:101B5000FF302CB0BDE8F081881000000000AD0B14 -:101B600098100000A0100000B0F5801F10B50446CA -:101B70000BD90F48FFF7B6FC2046FFF7D2FC0D4803 -:101B8000FFF7C8FC4FF0FF3010BD00231A46B4EB3E -:101B9000023F03F101034FEA023105D301F5805101 -:101BA0008C4201D21046EFE7FF2BEFD11846EBE74E -:101BB000940200009D0200002DE9F04705461646FC -:101BC0009846894641B310F0030705D01C48FFF73B -:101BD000A1FC0020BDE8F087FFF7C6FF0446681EA1 -:101BE0004844FFF7C1FF611C0546F2D0421CF0D00B -:101BF000A0EB040909F101091EB1424649463846E5 -:101C0000B047104F0223C7F80435C4F1010AAC42B3 -:101C100004DD0023C7F804350120DBE72303C7F800 -:101C20000835D7F80034DB07FBD500F067F826B19C -:101C3000424649460AEB0400B0470134E7E700BFDB -:101C4000B602000000E001402DE9F74F10F0030458 -:101C500006460F46984606D01F48FFF75BFC00205B -:101C600003B0BDE8F08FDFF874B0012302F0030A7F -:101C700022F00309CBF80435D5094C4518D3BAF145 -:101C8000000F0FD04FF0FF330193DBF80034DB0778 -:101C9000FBD5524607EB090101A802F097F8019B1A -:101CA00046F809300D4B0022C3F804250120D7E780 -:101CB000DBF80034D907FBD53B59A35100F01EF8DF -:101CC000B8F1000F05D0620603D10C9A2946E0094D -:101CD000C0470434D1E700BFE302000000E0014048 -:101CE000054B01224FF48021C3F80C25C3F80825C9 -:101CF000C3F804151A607047000001400B4AD2F87F -:101D0000003430B58BB10A4800230121D2F80845D0 -:101D100001FA03F5254218BF03F5C07403F101036E -:101D200018BF42F82400082BF0D130BD000001405C -:101D30003546526E4FF080434FF0FF32D3F8000427 -:101D4000C3F80024C0F3400070474900FFF702BD0C -:101D50002DE9F047012186B005460020FFF7FAFC87 -:101D6000042000F0F8FA03A8294600F055FB03A868 -:101D700000F076FB30B93C48FFF7CCFB012006B001 -:101D8000BDE8F08739480C35FFF7C4FB04992846B5 -:101D9000FFF77CFD04463648FFF7A4FB2046FFF71B -:101DA000C0FBFFF735FB059B9C4201D03148E3E7C0 -:101DB000049CDFF8E0803048FFF7ACFB21462F4A57 -:101DC00000234046FFF7F8FE2D48049FFFF7A2FBD3 -:101DD0000024A1464FEA570AA74217D93E1BB6F581 -:101DE000803F28BF4FF48036264832462919FFF736 -:101DF00025FE2449CDF800900023324608EB04006C -:101E0000FFF722FF38BB2048FFF784FB1F48FFF78E -:101E100069FB0498FFF785FB1D48FFF763FB049AF5 -:101E20001C49002000F0F8FB04461B48FFF75AFB52 -:101E30000598FFF776FB1948FFF754FB2046FFF79C -:101E400070FBFFF7E5FA059B9C420BD01448FFF7A7 -:101E500061FB022093E7344439460AEB5400FFF754 -:101E600079FCB9E700208AE7100300002E03000088 -:101E70004B03000061030000840300004B1D0000C1 -:101E8000970300004E020020AA030000B5030000E3 -:101E9000C303000000800000CC030000DF0300004B -:101EA000E503000008B5022000F06DFAD8B3042065 -:101EB00000F069FA70B11C48FFF72CFB042000F019 -:101EC00056FA022000F053FABDE808404FF40040F3 -:101ED00000F04DBA1548FFF71DFB082000F047FA47 -:101EE000102000F044FA202000F041FA402000F0D9 -:101EF0003EFA4FF48010FFF72BFF022807D108208D -:101F000000F029FA102000F026FA00F00FFB042060 -:101F100000F02DFA022000F02AFABDE808404FF444 -:101F2000004000F018BA08BD30040000660400004C -:101F300010B52448FFF7EEFA0020FFF709FF01383B -:101F4000C0B201280ED9202000F011FA402000F084 -:101F50000EFA802000F0FFF90124042000F007FAB7 -:101F6000204610BD082000F0F6F9102000F0F3F92B -:101F7000202000F008FA58B91348FFF7CBFA2020C8 -:101F800000F0E9F94FF4007000F0E5F900F0CEFA46 -:101F9000402000F0F8F920B90C48FFF7BBFA4020C8 -:101FA000EEE70B48FFF7B6FA082000F0E0F9102042 -:101FB00000F0DDF9202000F0DAF9402000F0D7F938 -:101FC0000024CAE7810400009B040000D404000040 -:101FD0000D05000008B50248FFF79CFA00F0ABFAC7 -:101FE000470500001EF0040F0CBFEFF30880EFF36D -:101FF0000980FFF7EFBF704708B50120FFF7B8F978 -:1020000018B9002000F0010008BD0020FFF7B0F96A -:102010000028F6D00220FFF7ABF90028F1D003200A -:10202000FFF7A6F980F00100C0B2EBE71FB5044648 -:102030001148FFF76FFA0C2201A9204600F01AFBA5 -:1020400001A8FFF767FA2046FFF716FBFFF7A0F994 -:102050000A4C20F004002070FFF79AF9237800F072 -:10206000FB00834201D000F061FAFFF747FE0A202F -:1020700000F0F2FAF0E700BF3D0500004E0201203B -:1020800030B585B0FFF73AFEFFF7B4F99748FFF790 -:1020900041FA9748FFF73EFA9648FFF73BFA964811 -:1020A000FFF738FA9548FFF735FA9048FFF732FA0C -:1020B00000F073F99248FFF72DFA00F097F900232A -:1020C000CDE90133039300F08DF90C2201A900F052 -:1020D000D1FA01A8FFF71EFA8448FFF71BFA8348DC -:1020E000FFF718FA4FF4804000F04DF990B18548A1 -:1020F000FFF710FA4FF4804000F039F9082000F0A3 -:1021000036F9102000F033F9202000F030F940209B -:1021100000F02DF9FFF7DEFC28B17B48FFF7FAF954 -:102120007A48FFF783FFFFF715FCFFF73FF9FFF74A -:102130005DFBFFF785FAFFF7C7F820B17448FFF79A -:10214000E9F97448EDE7FFF70BF920B17248FFF7A2 -:10215000E1F97248E5E700F035F9802000F013F965 -:1021600020B1802000F003F96D48DAE74FF4003029 -:1021700000F009F938B14FF4003000F0F8F8694880 -:10218000FFF7C8F94AE0FFF737FF78B16648FFF775 -:10219000C1F941F28834FFF72FFF70B1FFF7AEFDB0 -:1021A000012000F059FA013CF5D1E8E75F4B1A68CD -:1021B0005B68013312D0013210D0FFF7BBFD88B944 -:1021C0004FF4007000F0DFF878B9082000F0CFF885 -:1021D000102000F0CCF8FFF765FE23E05448FFF72D -:1021E00099F9CCE75348FFF795F94FF4007000F0E8 -:1021F000CAF810B15048FFF78DF94FF4007000F0A5 -:10220000B6F8102000F0BFF878B34C48FFF782F919 -:10221000082000F0ACF8102000F0A9F8FFF788FEC5 -:102220000028A1D04FF4005000F0ADF805464FF45F -:10223000805000F0A8F844004FF4006000F0A3F8CC -:1022400044EA850464B2204340B207281DD14FF40C -:10225000006000F08CF84FF4805000F088F84FF4E4 -:10226000005000F084F836485BE7082000F08BF857 -:1022700030B13448FFF74EF9102000F06CF8AAE7AF -:102280003148FFF747F90820F7E7032823D020F06B -:10229000040001284FF4006018D000F05CF8FFF74C -:1022A000E7FAFFF71DFD214B28485C681D68FFF722 -:1022B00019F92046FFF735F92548FFF713F9FFF71D -:1022C0001DFD63B64FF0FF3EAD46204700F04FF8CE -:1022D0004FF48050E1E74FF4006000F048F84FF40D -:1022E000805000F044F84FF40050D6E75F0700003C -:1022F00052050000700500008E050000AC050000CE -:10230000CA050000D305000000060000054550FE88 -:1023100011060000014550FE1E060000024550FE59 -:10232000034550FE250700002B060000008000003A -:10233000660600007906000091060000B106000064 -:10234000044550FED9060000FF06000044070000C7 -:1023500058070000704710B50446002000F02AF925 -:1023600040EA0401BDE81040002000F015B910B5A6 -:102370000446002000F01EF920EA0401BDE81040E8 -:10238000002000F009B910B50446002000F012F951 -:102390002040003818BF012010BD4FF0804208B522 -:1023A000D2F8043943F48023C2F8043900F0C6F8A7 -:1023B0000120FFF7E8FF20B9BDE80840012100F047 -:1023C000EBB808BD08B50648FFF78CF8002000F010 -:1023D000F1F8FFF7A6F8BDE80840FFF719B800BF0D -:1023E00060070000022000F0E5B8000008B5FFF724 -:1023F000F9FF0449884204D0BDE80840022000F0FB -:10240000CBB808BD1D86836808B50248FFF782F87F -:1024100000F08CF86C0700001FB50C22044601A8E0 -:10242000FFF70CFB0C48FFF75DF80298FFF779F80F -:10243000FEF7EEFF0948FFF755F80398FFF771F82C -:10244000FEF7E6FF01AB03CB20601868A0602046D2 -:10245000616004B010BD00BF890700009B07000049 -:102460000068A0F10C0358425841704780B50646F9 -:10247000174610480D461C46FFF734F83846FFF75C -:1024800031F80D48FFF72EF83046FFF72BF80B48D0 -:10249000FFF728F82846FFF744F82CB10848FFF763 -:1024A00021F82046FFF71EF80648FFF733F8FFF73C -:1024B000ABFF00BFA60700006D050000AF070000DE -:1024C0006E0500005F0700001FB506AC1A4654F801 -:1024D000043B00940394FFF7C9FF000007B50023F5 -:1024E00000937246014BFFF7EFFF00BFB8070000F3 -:1024F00007B5034B00937246024BFFF7E5FF00BFA1 -:10250000CF070000C9070000BFF34F8F0549064BF6 -:10251000CA6802F4E0621343CB60BFF34F8F00BF81 -:10252000FDE700BF00ED00E00400FA0508B5FFF785 -:1025300001F9FFF7E9FF08B5FFF7E6FF38B5124CE0 -:102540007C222146002000F067F8E36F83420546B5 -:1025500018D00E48FEF7C6FF2846FEF7E2FF0C48EB -:10256000FEF7C0FFE06FFEF7DCFF0A48FEF7BAFF98 -:10257000FEF74EFF20464FF48072BDE83840002140 -:1025800001F0F8BB38BD00BF0000002004080000C7 -:10259000300800003B08000010B5054C7C2244F8D0 -:1025A00020102146002000F037F8E06710BD00BF82 -:1025B00000000020014B53F820007047000000206D -:1025C0002DE9F0410546002401202746034602F18B -:1025D000FF38944205EB070C03D18CF80030BDE8BE -:1025E000F08111F804E0461CBEF1000F05D1EB5557 -:1025F0000746012301343046EBE70133DBB2FF2B02 -:1026000005F800E0F6D1A04504D037468CF800303C -:10261000861CEEE73046E0E730B5C9B10D4DC0434A -:102620000A44914201D1C04330BD11F8013B83EA15 -:10263000000404F00F0455F8244084EA101080EAE6 -:10264000131303F00F0355F8233083EA1010E8E763 -:102650000846E9E73411000038B5054D044645F059 -:10266000010504B938BD4FF47A40A847013CF8E7AA -:10267000801100000A2A30B403DC30BC1048FEF799 -:1026800049BF0C46302304F8023B78234B701C22D0 -:102690000F2505FA02F30340D340092B0AD8303343 -:1026A000DBB2043A04F8013B131DF2D1002330BC25 -:1026B0008B7270470F2B01D85733F1E72023F0E7D7 -:1026C0005108000070B546090D4600F01F0419D0EE -:1026D000012E04D14FF6FF73E340DB0704D4134807 -:1026E0004FF46C71FFF704FF7EB1012E1CD00F4830 -:1026F00040F2B931FFF7FCFE04F5E074002353F813 -:102700002430FFDE4FF0FF33E6E74FF0A04202EB4C -:102710008402074BD2F800170B4043EA052343F02D -:102720000203C2F8003770BD024AF0E767080000F4 -:10273000F0F8FCFF000300500E4B0238D3F83C26A3 -:10274000D3F8383610B5072813D8DFE800F0110F9A -:102750000D0B0A080604100EC871100C8871100ABF -:1027600048710A711A0ECA701A0C8A701A0A4A70D5 -:102770000B7010BD00900240023807281FD8DFE818 -:1027800000F02927251123211F04CB791B068A7904 -:1027900043EA02434A7943EA02230A7913430E4A81 -:1027A000C2F83C36CB781B068A7843EA02434A7863 -:1027B00043EA02230A781343074AC2F838367047BF -:1027C0000023E4E70023E5E70023E6E70023EBE747 -:1027D0000023ECE70023EDE7009002400268154B70 -:1027E000FF2A08BF4FF0FF32C3F824254268FF2AB2 -:1027F00008BF4FF0FF32C3F828258268FF2A08BFC0 -:102800004FF0FF32C3F83025C268FF2A08BF4FF0EF -:10281000FF32C3F834250269FF2A08BF4FF0FF32A8 -:10282000C3F838254269FF2A08BF4FF0FF32C3F8CA -:102830003C25704700900240F8B5114D6C6BB4B167 -:102840004FF47A735C430A23B4FBF3F40D4E0E4F3E -:1028500046F00106D7F8003163B995F839304BB925 -:102860004FF42070B047013CF4D10848F8BD4CF259 -:102870005034EBE795F83930002BF6D10448F5E7F2 -:102880005002012090110000009002400700AD0BA3 -:102890000000AD0B0A4B10B500220124C3F800451F -:1028A000C3F800211C6038B1FFF7C6FF054B984202 -:1028B00004D1054B83F83840024810BD0348FCE7BB -:1028C000009002400000AD0B500201200700AD0B4C -:1028D0002DE9F8433D4C994694F830300646884639 -:1028E00017461BB93A487F21FFF702FE2EB9384838 -:1028F0008021FFF7FDFD374841E006F06043B3F16A -:10290000005FF8D1B307F6D194F83030012B05D031 -:102910004B4558D194F83830002B54D02E4AB9F199 -:10292000020F4FF000030BBFC2F81075C2F8047518 -:10293000C2F81465C2F808650CBFC2F81885C2F861 -:102940000C8584F8393023680CBF08250425C3B9E9 -:1029500094F838306BB1204B05F1804505F52435EE -:102960000022C3F8002101232B60BDE8F843FFF7E4 -:1029700063BF0120FFF78EFF184B9842EBD1BDE8F3 -:10298000F883A3682BB1E660C4F81480E7611448AB -:10299000F5E7114AA660C4F81080A76184F830906A -:1029A000C2F800310123C2F8043394F8382002F051 -:1029B000FF0012B9FFF76EFFE9E705F1804505F565 -:1029C00024352B60E3E70748D9E700BF5002012018 -:1029D0009E0800000A00AD0B009002400700AD0BFE -:1029E0000000AD0B0B00AD0B30B5194D0FCD8DB008 -:1029F00006AC0FC495E8030084E803001549D1F83C -:102A00002435DBB20093D1F82835DBB20193D1F83D -:102A10003035DBB20293D1F83435DBB20393D1F811 -:102A20003835DBB20493D1F83C3506A8DBB2059308 -:102A3000FFF7D4FE0023C1F8003101230B60D1F869 -:102A40000031002BFBD06846FFF7C8FE0DB030BD4B -:102A5000981100000090024090F8293070B50446AB -:102A600013B190F82A206ABB6068FF286DD0A26875 -:102A7000FF2A6AD0E268FF2A67D02269FF2A64D061 -:102A8000D3B90321FFF71EFEA0680321FFF71AFE4A -:102A9000E0680321FFF716FE20690321FFF712FE0D -:102AA0006069FF2802D00321FFF70CFEA069FF2810 -:102AB00002D00321FFF706FE94F82A3013B9201D37 -:102AC000FFF78CFE224E636A224D736394F829301F -:102AD00086F83A300123C5F80833FFF785FF2368ED -:102AE000C5F84035A37F617F227F9B0143EAC10384 -:102AF0001343E27F43EAC213C5F84435D5F80026F4 -:102B000094F8203022F07F4222F0FF02134394F821 -:102B1000212043EA026394F8222043EA426394F8B6 -:102B2000232043EA0273C5F8003633685BB194F89A -:102B30002830094A5B01DBB282F829334FF4007375 -:102B4000C2F884315360054870BD0548FCE700BFFA -:102B5000500201200090024000E100E00000AD0BB7 -:102B60000400AD0BF8B50F461646054620B91048CF -:102B70004FF4DB71FFF7BCFC0E4C94F83030A3B976 -:102B8000C4E900764DB90023C4E9023301220A48A2 -:102B900084F8302084F83830F8BD2846FFF75CFF11 -:102BA000054B9842EFD00548F6E70548F4E700BF2B -:102BB0009E080000500201200000AD0B0400AD0B88 -:102BC0000C00AD0B2DE9F047DFF8C4A09AF83030C7 -:102BD00005468946164623B928484FF40171FFF788 -:102BE00087FC9AF83000012846D19AF83830DFF88F -:102BF000948063B3224F0123C7F80833FFF7F4FE34 -:102C00006C78B9F1000F29D1AA784FF000098AF841 -:102C100039902302C7F8009143EA02332A7813431C -:102C2000EA7843EA42332A7943EA82336A7943EA0B -:102C3000C233C7F83436FFF7FFFD404513D12B7977 -:102C40004BB90E4840F23621FFF752FC03E0FFF784 -:102C500021FE4045CED10B48BDE8F08749462046CD -:102C6000FFF78AFDD0E7C7F800911EB168783146BA -:102C7000FFF762FD0448EFE70448EDE79E08000017 -:102C8000009002400700AD0B0000AD0B0B00AD0B38 -:102C9000500201200323FFF71BBE30B50546900804 -:102CA00040380124844014EA010009D0A85838B102 -:102CB0000021A950AA5813B11A6822431A600120B2 -:102CC00030BD2DE9F341002304460D4601939046A3 -:102CD00001AB4FF4A672FFF7E0FF4FF48272294672 -:102CE0002046FFF7DAFF4FF48C720746294620464C -:102CF000FFF7D3FF064630B1B8F1000F1DD017B172 -:102D0000D4F83C353E4601AB4FF488722946204644 -:102D1000FFF7C3FF30B1B8F1000F03D016B90FB100 -:102D2000D4F83C3501AB4FF4907229462046FFF7AA -:102D3000B4FF019802B0BDE8F0814646E3E7000029 -:102D400003685A0910B504461CD0012A06D14FF673 -:102D5000FF7203F01F03DA40D30704D40D484FF489 -:102D60006C71FFF7C5FB236803F01F025B0922604B -:102D70000BD0012B0CD0074840F2B931FFF7B8FB5C -:102D8000002010BD4FF0FF32E3E74FF0A040F8E71E -:102D90000148F6E7670800000003005007B50190FE -:102DA00001A8FFF7CDFF019A01239340C0F8083531 -:102DB00003B05DF804FB07B5019001A8FFF7C0FF61 -:102DC000019A01239340C0F80C3503B05DF804FB71 -:102DD000F7B51746421C1C460646089B0D4618D000 -:102DE000012902D1BBB1FFF7D9FF01A80196FFF776 -:102DF000A7FF019B00EB83002402D0F80027074BBC -:102E000044EA87042C4313401C4344EA4504C0F8B9 -:102E1000004703B0F0BDFFF7CEFFE6E7F0F8FCFF98 -:102E2000F8B51A4F012304460D46436164261F433B -:102E3000002202212046FFF744FF18B94020B8477E -:102E4000013EF5D14FF080530022D3F830116A775C -:102E5000082918D1D3F83431ABB94FF0FF32D4F888 -:102E60000805D4F80C15C4F80035C4F80825C4F8D2 -:102E70000C250922C4F80025C4F80035C4F808055B -:102E8000C4F80C15F8BDC4F80025FBE7B01100002C -:102E9000104B98421BD011D80F4B984217D008D82E -:102EA0000E4A0F4B984218BF90420CBF01200020E1 -:102EB00070470C4BC31A5842584170470A4B98420E -:102EC00005D002D8094A0A4BECE70A4BF2E7012089 -:102ED000704700BF80841E0020A1070090D003002F -:102EE00048E8010040420F000024F40000127A007C -:102EF00000093D000048E8012DE9F3474B69794A94 -:102F000090F80490934207460C4600F0D08000F2FF -:102F1000B880754A934200F0CD8000F2AB80734ACE -:102F2000934200F0CA80724A934200F0C98071480F -:102F30004FF4BC71FFF7DCFA4FF08045DFF8D08129 -:102F4000E67E242202FB0983997F66F300019977CC -:102F50003B79217C02FB03839A7F61F341029A77DC -:102F60005EBB6269644B9A420CBF4FF0030A4FF09C -:102F7000000A237E012B94BF0023012300932068C5 -:102F8000534632460121FFF723FF009660685346FF -:102F900032460121FFF71CFF0096A27EA06853462F -:102FA0003146FFF715FF237C83F001030093E068AF -:102FB000534632460121FFF70BFF242303FB09830D -:102FC000E2681A62217C9A7F267F61F341029A7738 -:102FD00094F812C0217E94F819E03B6896B9D4E9C0 -:102FE00001902268C3F80825C3F80C95C3F81005B2 -:102FF0004FF0FF30C3F86C05C3F81405C3F86865DB -:10300000C3F86465BEF1000218BF01220229C3F8AB -:10301000C0C5C3F8245559D003295AD0012901D17C -:1030200042F00202C3F854250222C3F860253A791F -:1030300024214A4358F80220CAB1627C43F3073383 -:103040005201002BD2B247DB03F1604101F561412F -:1030500081F8002303F01F015B099B0003F160432B -:1030600003F5614301228A40C3F880211A6002B04F -:10307000BDE8F087214A93427FF459AF4FF0805565 -:103080005CE71F4A93421ED00AD81E4A93423FF47F -:1030900053AF1D4A93427FF44AAF4FF000454DE7CE -:1030A000154A93427FF443AF4FF0A05546E74FF0E7 -:1030B000005543E74FF0006540E74FF000753DE7EE -:1030C0004FF080653AE74FF0206537E742F00402A1 -:1030D000A8E742F00602A5E70C4903F00F03CA5423 -:1030E000C5E700BF80841E0020A1070048E801005A -:1030F00090D00300DE0800000048E80140420F00C5 -:103100000024F40000093D0000127A0014ED00E0F4 -:103110008C020120431C07B50ED0019001A8FFF7D7 -:103120000FFE019B00EB8300054BD0F800271340F6 -:1031300043F00203C0F8003703B05DF804FB00BFA2 -:10314000F0F8FCFF2DE9F8430646914698460D46F7 -:1031500021B9154840F24B21FFF7CAF93779134CD2 -:10316000242303FB07431B7FD3B91DB16869FFF715 -:103170008FFEB8B124217943631844F80190C3F855 -:1031800004801DB129463046FFF7B6FE242303FB19 -:10319000074400236377064801232377BDE8F883BB -:1031A0000448FBE70448F9E7DE0800008C02012030 -:1031B0000000AD0B0C00AD0B0400AD0B2DE9F84386 -:1031C000274C0679242303FB064305461B7F23B9BE -:1031D00024484FF42B71FFF78BF92B6843F3073327 -:1031E000002B0DDB5A09012103F01F03994002F166 -:1031F00020031D4A42F82310BFF34F8FBFF36F8F98 -:1032000024217143635833B12868184BC0F8083340 -:103210002144FFF705FE24272B6807FB0647D3F858 -:103220000805D3F80C85D3F81095BB7FDB070AD4CB -:10323000FFF770FF4846FFF76DFF4046FFF76AFF54 -:10324000386AFFF767FF2B68094A934204BF0122DF -:103250005A60242303FB064400232377BDE8F88348 -:103260008C020120DE08000000E100E052010800AD -:1032700000F002402DE9F04F6D4F90F804802423B8 -:1032800003FB087385B01B7F82460D46164623B9A3 -:10329000684840F29131FFF72BF92B6833B96B681E -:1032A00023B1644840F29231FFF722F9AB68002B5A -:1032B00055D1EB68002B4DD1EB88002B52D116F085 -:1032C00008090AD0242303FB08731B6A013304D0C6 -:1032D000584840F29731FFF70BF9242303FB08F31A -:1032E000FA18517F002940F0A180FB5823B116F055 -:1032F000140F04BF0123537795E80F00242404FB27 -:10330000087404F10C0C8CE80F00206A431C04D0F4 -:10331000A37F99072CD5FFF741FDD5F800B0BBF18D -:10332000000F04D00BF06043B3F1005F09D1D5F872 -:1033300008E0BEF1000F1ED00EF06043B3F1005F55 -:1033400019D0242303FB087300223B485A7705B0A9 -:10335000BDE8F08F374840F29331FFF7C9F8EB89A9 -:10336000002BA9D033484FF46571FFF7C1F8A6E7E9 -:10337000FFF721FDD1E7DAF8004016F0010318BF8E -:103380000123C4F8503516F0020318BF0123C4F816 -:10339000403595E80F00C4F844B58DE80F00C4F837 -:1033A0004815C4F834E5C4F838350023C4F818319A -:1033B000D4F818310723C4F80035242303FB08F39D -:1033C000EC46FB58B9F1000F21D10122226103BB69 -:1033D000D4F81831002BFBD0624640212046FFF77D -:1033E00070FC242303FB0873186A421C04D09B7FE3 -:1033F0009B0718D5FFF7DFFCB9F1000F05D1242199 -:1034000001FB08712046FFF70BFD0C489FE7002BDE -:10341000E7D016F0040F4FF0400314BFC4F8083390 -:10342000C4F80433F1E7FFF7B9FCE5E704488EE799 -:103430008C020120DE0800000A00AD0B0000AD0B7D -:103440000B00AD0BCA7B036810B52AB9D1E9004265 -:10345000C3F80845C3F80C258A68C3F82425027907 -:103460003C20424311488258CAB10A7B43F30733D8 -:103470005201002BD2B213DB03F1604101F561412F -:1034800081F8002303F01F015B099B0003F16043F7 -:1034900003F5614301228A40C3F880211A6010BD00 -:1034A000034903F00F03CA54F9E700BFB00201203B -:1034B00014ED00E0D0F8043130B52BB10023C0F892 -:1034C0000431D0F80431012391F82F2022B30022D7 -:1034D000C0F82421D0F82441C0F81C21D0F81C41A8 -:1034E000C0F80821D0F80821002B79D0002281F8FB -:1034F0002D2091F82F20002A37D14D6B8C6AA542E0 -:103500001FBF0523C0F80025C0F80035134683F01F -:10351000010381F82F3028E0D0F8244114B1C0F81D -:1035200024210EE0D0F81C216AB3C0F81C41D0F869 -:103530001C214A6BD0F8245101324A6345B1C0F8CE -:103540002441D0F824210122426181F82F20CBE7C9 -:103550008C6AA24204D24C6AA25CC0F81C25C3E764 -:1035600091F82C201AB10123C361002030BD0A6AF2 -:1035700012F0400F4FF0010203D0C26181F82D20FC -:10358000F3E74261B0E7D0F80841002CACD0C0F8B6 -:103590000821D0F80841D0F82441002CBFD14A6B53 -:1035A0008C6AA242A0D24C6AD0F81855A5544A6B36 -:1035B0008C6A0132651EAA424A6308D10A6A54061F -:1035C00002D40222C0F80022012202628CE7A24249 -:1035D000FAD10A6A520687D5022381F82D30C4E752 -:1035E0000120C3E72DE9F0415FEA50180E46174667 -:1035F0001C4600F01F051DD0B8F1010F04D14FF695 -:10360000FF73EB40DB0704D416484FF46C71FEF7F0 -:103610006FFFB8F1000F10D0B8F1010F1FD01148A3 -:1036200040F2B931FEF764FF05F5E075002353F869 -:103630002530FFDE4FF0FF33E3E74FF0A04303EB0D -:103640008503094AD3F80017240244EA87040A4094 -:1036500044EA46041443C3F80047BDE8F081034B35 -:10366000EDE700BF67080000F0F8FCFF0003005022 -:1036700013B53A4B01930023C0F80431D0F804216C -:10368000C0F82431D0F82421C0F81C31D0F81C2116 -:10369000C0F80831D0F80821C0F800324B6381F837 -:1036A0002F300123036291F82D209A4218BF8360C6 -:1036B0008B6A044673B14B6A1B78C0F81C350B68E3 -:1036C00003B340F286238B608B682548C4F804332B -:1036D00002B010BD91F82C3013B10123C361EEE7A5 -:1036E0000B6A13F0400F4FF0010316BFC361436133 -:1036F00081F82D30E3E72046FFF7DCFE28B1019B7F -:10370000013B0193019B002BF5DC91F82F305BB955 -:103710001348019B002BDBDC0023C4F80035052394 -:103720001048C4F80035D3E7D4F8C434C4F8C4341E -:103730008BB10C4A0C4813F0010F08BF104613F070 -:10374000020F02F5803218BF104613F0040F074B2A -:1037500018BF1846DDE70348DBE700BFA08601007D -:103760000000AD0B0100AD0B0000AE0B0200AE0B74 -:1037700013B5334B01930023C0F80431D0F8042172 -:10378000C0F82431D0F82421C0F81C31D0F81C2115 -:10379000C0F80831D0F808214B6381F82F308B6ACC -:1037A000012B044617D10B6A5B0614D40223C4F81C -:1037B00000320123236291F82D20022A18BF2360D2 -:1037C0000B688BB140F286238B608B681D48C4F870 -:1037D000043302B010BD0123E9E72046FFF76AFE7B -:1037E00028B1019B013B0193019B002BF5DC91F873 -:1037F0002F305BB91348019B002BEADC0023C4F88F -:10380000003505231048C4F80035E2E7D4F8C43485 -:10381000C4F8C4348BB10C4A0C4813F0010F08BF34 -:10382000104613F0020F02F5803218BF104613F055 -:10383000040F074B18BF1846DDE70348DBE700BF5E -:10384000A08601000000AD0B0100AD0B0000AE0B27 -:103850000200AE0B2DE9F047074691469A460C460A -:1038600019B92248B521FEF743FE97F80480204D90 -:103870003C2000FB08F0291891F82E609EBB8E605A -:1038800045F80090C1F804A081F82D6081F83160FE -:1038900081F83060F4B1637B81F83830A37B81F824 -:1038A00039309BB9D4E900239A4203D10F48E8216B -:1038B000FEF71EFE2068062303220021FFF792FE7A -:1038C0006068062303220021FFF78CFE214638465C -:1038D000FFF7B8FD3C2303FB08550648012385F894 -:1038E0002E30BDE8F0870448FBE700BF1E0900004A -:1038F000B00201200000AD0B0C00AD0B70B50D4CFB -:1039000006793C2303FB0643054693F82E30012B32 -:1039100004D0094840F23111FEF7EAFD2B68052278 -:10392000C3F800253C2303FB0644022384F82E3011 -:1039300070BD00BFB00201201E09000070B5124C1E -:1039400006793C2303FB0643054693F82E3023B942 -:103950000E484FF49F71FEF7CBFD2B680C4AC3F85D -:103960000823D3F8002222F00302C3F80022002229 -:10397000C3F800253C2303FB0644012384F82E30C2 -:1039800084F8302070BD00BFB00201201E09000085 -:10399000864204002DE9F8434D4F06793C2303FB92 -:1039A0000673054693F82E30022B0C46904604D041 -:1039B000484840F2AE21FEF79BFDE36833B96368E7 -:1039C00023B1444840F2AF21FEF792FD236933B999 -:1039D000A36823B13F484FF42C71FEF789FD4FF0E7 -:1039E0003C0909FB06F957F80930A3B92378022BE3 -:1039F00008D02378032B0ED136484FF42D71FEF7F3 -:103A000077FD08E0334840F2B321FEF771FD57F827 -:103A10000930002BEDD03C2303FB0673D5F8009052 -:103A200093F82D30012B04D12378012B06D12A489D -:103A300011E0022B02D12378012BF8D13C25274B32 -:103A4000C9F8083305FB067595F830302BB1AB6823 -:103A50002348C9F80433BDE8F883C8F3800383F032 -:103A60000103DBB2A64685F83030BEE80F0005F151 -:103A70000C0CACE80F00DEF80030CCF800306368C6 -:103A8000AB62E368C5F820806B626378C9F888355B -:103A90002378012B17D09BB9C8F340183C2303FBB4 -:103AA00006732946484683F82C80FFF7E1FD3C2346 -:103AB0005E43BA19BB59002BCDD182F83030CAE72A -:103AC0004FF00108EAE729464846FFF751FEEEE7C6 -:103AD000B00201201E0900000500AD0B8642040063 -:103AE0000B00AD0B0F49104A104B00F010F81049B5 -:103AF000104A114B00F00BF81049114A114B00F01D -:103B000006F81149114A124B00F001F806E0521A6A -:103B100003DD043A98588850FBDC704700F03AF80F -:103B2000FDF798FB000100202C010020543E00000E -:103B30002C0100202C010020803E00002C010020E0 -:103B40002C010020803E00002C0100202C010020D0 -:103B5000803E0000FEE7FEE7FEE7FEE7FEE7FEE749 -:103B6000FEE7FEE7FEE7FEE74FF08053D3F8302193 -:103B7000082A03BFD3F83401B0FA80F040090020CE -:103B800070470000024AD2F80034002BFBD0704787 -:103B900000E0014008B54FF08053D3F83021082AE7 -:103BA0004ED14FF080420021C2F80C11C2F8101122 -:103BB000C2F8381502F54042D3F80414C2F82015B3 -:103BC000D3F80814C2F82415D3F80C14C2F8281539 -:103BD000D3F81014C2F82C15D3F81414C2F8301509 -:103BE000D3F81814C2F83415D3F81C14C2F84015D1 -:103BF000D3F82014C2F84415D3F82414C2F8481599 -:103C0000D3F82814C2F84C15D3F82C14C2F8501568 -:103C1000D3F83014C2F85415D3F83414C2F8601530 -:103C2000D3F83814C2F86415D3F83C14C2F86815F8 -:103C3000D3F84014C2F86C15D3F84434C2F8703588 -:103C4000FFF792FF18B13B4B3B4AC3F88C26FFF7B6 -:103C50008BFF18B1394BFB22C3F81825FFF784FFFF -:103C600058B14FF080424FF08051D2F8E43ED1F885 -:103C7000581261F30303C2F8E43EFFF775FF20B169 -:103C80002F4B4FF40072C3F840264FF08053D3F807 -:103C90003031082B09D14FF08043D3F80024D207EC -:103CA00044BF6FF00102C3F80024264AD2F88830DE -:103CB00043F47003C2F88830BFF34F8FBFF36F8FA8 -:103CC0004FF08053D3F83021082A0BD1D3F8343188 -:103CD000042B81BF4FF01023D3F808224FF080430C -:103CE000C3F858254FF01021D1F80C32DB071FD54F -:103CF00015480123C0F80435FFF744FFD1F80C3212 -:103D000023F00103C1F80C32FFF73CFF0023C0F899 -:103D10000435FFF737FFBFF34F8F0A490B4BCA68D3 -:103D200002F4E0621343CB60BFF34F8F00BFFDE7A7 -:103D300008BD00BF005000404881030000F0004073 -:103D40000090024000ED00E000E001400400FA05B0 -:103D500008B5064B044613B10021AFF30080044BB5 -:103D60001B6803B198472046FEF7F4FA00000000F4 -:103D7000EC02012002440346934200D1704703F84D -:103D8000011BF9E770B50D4B0D4D5B1B9C10002618 -:103D9000A64209D100F028F80A4D0B4B5B1B9C1082 -:103DA0000026A64205D170BD55F8043B9847013660 -:103DB000EEE755F8043B98470136F2E7F43D000082 -:103DC000F43D0000F43D0000F83D00000A4491423B -:103DD00000F1FF3300D1704710B511F8014B03F823 -:103DE000014F9142F9D110BDF8B500BFF8BC08BC35 -:103DF0009E46704729120000F8B500BFF8BC08BC09 -:0C3E00009E467047011200000000000008 -:103E0C00543E0000000100200B000000803E00002A -:103E1C002C01002000000000803E00002C0100203E -:103E2C0000000000803E00002C010020000000007B -:103E3C002C01002071400000F00201200000000065 -:083E4C00F0020120000000005B -:103E540000F0024000000000FFFFFFFF060000002A -:103E640008000000FFFFFFFFFFFFFFFF0007FF0048 -:0C3E740000093D000000000000000000FC -:020000042000DA -:1000000000000000000000000000000000000000F0 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000000000000000000000000000B0 -:1000500000000000000000000000000000000000A0 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:100080000000000000000000000000000000000070 -:100090000000000000000000000000000000000060 -:1000A0000000000000000000000000000000000050 -:1000B0000000000000000000000000000000000040 -:1000C0000000000000000000000000000000000030 -:1000D0000000000000000000000000000000000020 -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:0400000300003AE5DA -:00000001FF diff --git a/bin/boot/boot_bb2@1447134832.bin b/bin/boot/boot_bb2@1447134832.bin deleted file mode 100755 index c591c2ed3a..0000000000 Binary files a/bin/boot/boot_bb2@1447134832.bin and /dev/null differ diff --git a/bin/boot/boot_bb2@1447134832.hex b/bin/boot/boot_bb2@1447134832.hex deleted file mode 100644 index 14072ac25d..0000000000 --- a/bin/boot/boot_bb2@1447134832.hex +++ /dev/null @@ -1,1062 +0,0 @@ -:020000040800F2 -:10000000482001208D2C0008ED2F0008ED2F00085E -:10001000ED2F0008ED2F0008ED2F00080000000074 -:10002000000000000000000000000000ED2F0008AC -:10003000ED2F000800000000ED2F0008ED2F000854 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0C2FD3A -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:08016000AFFD05B05DF804FBE2 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:080188009BFD05B05DF804FBCE -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F06CFCDDF820C026 -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F00DFC089B9CE0BA4268 -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00C93F00084B45F7DBEDE70D9907910798BB -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01D03F0008D1 -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C00BFF34F8F044A054BD16801F4E0610B43E0 -:10191C00D360BFF34F8FFEE700ED00E00400FA0543 -:10192C0010B5044608484021FFF784FC0028F9D084 -:10193C0005482146FFF77AFC03484021FFF77AFC63 -:10194C000028F9D010BD00BF00480040014608B582 -:10195C000448FFF757FC03480221FFF755FC002809 -:10196C00F9D008BD0038004000B9074803680133BE -:10197C0008D04368013305D0C069044BC31A5842E0 -:10198C005841704700207047004000086F57654E63 -:10199C00074B1A69C2F306427F2A07D11869C0F3B4 -:1019AC000C00B0F1FF0358425841704700207047BB -:1019BC000028004000F13F4000F57E00800A082816 -:1019CC000FD8084A52F820305BB1013B42F8203066 -:1019DC003BB9012101FA00F0034B1A6B22EA00001B -:1019EC0018637047200000200038024000F13F408F -:1019FC0000F57E00800A08280DD8074A52F82030DE -:101A0C00591C42F8201033B9012101FA00F0034BA4 -:101A1C001A6B1043186370472000002000380240F6 -:101A2C0010B50C4C2046FFF7E1FF072001F01AFB24 -:101A3C004FF4805100222046FFF776FB042001F082 -:101A4C0011FB2046FFF7B6FF4FF480400021FFF753 -:101A5C0067F810BD0004024010B50B4C01214FF487 -:101A6C008040FFF75DF82046FFF7C0FF20464FF49B -:101A7C0080510122FFF758FB072001F0F3FA2046B2 -:101A8C00FFF798FF10BD00BF00040240F0B55E25C3 -:101A9C006843204BB0FBF1F51A68ADF6DC3D95427E -:101AAC0034D002AC00214FF43D6220461D60FEF79D -:101ABC00CEFA1B238DE81800174810214022692309 -:101ACC0001F0B3FA08238DE818001448602318219C -:101ADC006A2201F0AAFA0DF2FA760DF28E73002248 -:101AEC00AA420DD002F11900C0B2C108012700F0C2 -:101AFC00070007FA00F05F5C013238435854EFE7F7 -:101B0C001233B342EBD1204601F0F9FA0DF6DC3D6D -:101B1C00F0BD00BF00000020FC370008913B00081E -:101B2C002DE9F043ADF6DC3D02AC00214FF43D62F3 -:101B3C0080462046FEF78BFA2C238DE818002022D5 -:101B4C00284818215C2301F070FA1C2215460F2737 -:101B5C00974007EA0807D740234B242101FB07379E -:101B6C000DF50366002117F801C00023082B07D0E0 -:101B7C000120984010EA0C0F0CBF0020012003E05C -:101B8C007818407800F0010078B15819C0B24FEACB -:101B9C00D00E4FF0010900F0070009FA00F016F81A -:101BAC000E9040EA090006F80E000133092BDDD136 -:101BBC000231242906F11206D5D10B35742DA2F170 -:101BCC000402C4D10E238DE81800102190226D233D -:101BDC00064801F02AFA204601F091FA0DF6DC3D98 -:101BEC00BDE8F083EC350008E83200082835000821 -:101BFC0010B5ADF6D83D02AC204600214FF43D6245 -:101C0C00FEF725FA1B238DE81800102140226923CA -:101C1C00044801F00AFA204601F071FA0DF6D83D9D -:101C2C0010BD00BFFC3700082DE9F74F434E444F61 -:101C3C0001214FF48040FEF773FF3046FFF7D6FECC -:101C4C003846404CFFF7D2FE30460D210522FFF7F7 -:101C5C0070FA052230460F21FFF76BFA3046214609 -:101C6C000125FFF70EFA2023384621462360257103 -:101C7C00A5714FF0000A01934FF4805BFFF701FA56 -:101C8C00304621464FF002084FF48049257184F804 -:101C9C0006A0C4F800B0FFF7F4F93046214684F8EA -:101CAC000480C4F80080FFF7ECF921462571A5717A -:101CBC00C4F80090244C3046FFF7E3F92046FFF7B8 -:101CCC0049F920462149FFF76FFA20462946FFF7CC -:101CDC008AFA019B384619465246FFF725FA2A46DE -:101CEC0030464946FFF720FA40462946FEF718FFD2 -:101CFC00A4F5505480232380F92323854FF4F0421C -:101D0C004FF6FF13E3623046A28329462364424612 -:101D1C00A4F820B0FFF70DFA29462046A582FEF75D -:101D2C002FFD3846FFF746FE3046FFF743FE484688 -:101D3C005146FEF7F5FE03B0BDE8F08F00040240FB -:101D4C000008024004000020003800400C00002075 -:101D5C0010B5074C2046FFF749FE20461021FFF72F -:101D6C00DFF92046FFF726FE012001F07BF910BDBC -:101D7C000000024038B50A4B0C2404FB00346068A8 -:101D8C00FFF734FE21896068FFF7C4F905466068E7 -:101D9C00FFF710FED5F1010038BF002038BD00BFA1 -:101DAC00A032000810B5064C2046FFF71FFE204657 -:101DBC001021FFF7B7F92046FFF7FCFD10BD00BF5F -:101DCC0000000240F0B587B0FFF7E2FD002841D1DA -:101DDC000320FFF769F807460420FFF765F8474B27 -:101DEC000A241A68C2F30545C2F30626C2F302138D -:101DFC0002F00F0204FB0322330906F00F0604FB6A -:101E0C0003662B0905F00F0504FB03533C2505FB6A -:101E1C00036305FB0323191A44BF01F5A83101F52F -:101E2C00C07107EB91210320FFF72EF803A8FFF7F1 -:101E3C00A8F903A8FEF704FF01A8FFF7A8F901A968 -:101E4C000020FEF741FF02A8FFF7A7F9002002A926 -:101E5C00FEF7A4FF0A2442F2107501F072F910B9D2 -:101E6C00013DFAD107E0264D01F06BF908B1013DB7 -:101E7C00FAD1013CEFD10020FEF7A2FD00214FF278 -:101E8C000400FFF72DF84FF43C50FFF755F81D4BAD -:101E9C0001201A6842F004021A60FEF791FD1A4BF9 -:101EAC001B781BB1194801F06BF908E01848FEF7D4 -:101EBC0011F9174902461748FEF7EDF970B11648AB -:101ECC0001F05EF90020FEF7FFFF08200121FEF76C -:101EDC00C7FF4FF400400121FFF702F80F4801F053 -:101EEC004FF9084B1A6842F002021A600C4B1A693F -:101EFC0042F004021A6130BF07B0F0BD0028004068 -:101F0C00A086010000700040017AFF1F763900089E -:101F1C008B3900082078FF1F91390008A63900087A -:101F2C0000ED00E0B0F1006F10B504460BD20D4887 -:101F3C0001F01DF9204601F029F90B4801F020F9B8 -:101F4C004FF0FF3010BD094A002352F8041FA14284 -:101F5C0002D851688C4202D30133072BF5D11846B5 -:101F6C0010BD00BFBA3A0008B73900087C3200082F -:101F7C0008B5024801F004F9FFF7C2FCD03900089B -:101F8C0010B504460A480221FFF73EF90028F9D0A3 -:101F9C0007482146FFF736F905480121FFF734F9C8 -:101FAC000028F9D00248FFF72AF9C0B210BD00BFD3 -:101FBC00003001402DE9F04F87B00121814600AF80 -:101FCC000020FFF763FD042001F067F807F10C0017 -:101FDC0049460C2201F0FFF8FD683C690C2DD7F83E -:101FEC0014B013D0994801F0C2F8284601F0CEF88D -:101FFC00974801F0BCF8204601F0C8F8954801F06C -:10200C00B6F8584601F0C2F8934833E0934801F013 -:10201C00B7F84FF480500121FEF776FD09F10C0959 -:10202C00EA46FEF7EFFBA0B06E462546C846802D6B -:10203C000CD941463046802201F0CDF830462021A3 -:10204C00FEF7ECFB803D08F18008F0E725B1304647 -:10205C0041462A4601F0BFF82946304600F0CAFF37 -:10206C00002180464FF48050FEF74EFDD845D546F2 -:10207C0004D07B4801F084F80120E3E088B0684686 -:10208C004946202201F0A7F86846FFF76DFC7860FE -:10209C007448D54601F074F87B682BB9724801F08E -:1020AC006FF804F5404600E02646002E3BD06F4802 -:1020BC00FFF738FF05466E483044FFF733FF002D1D -:1020CC00824630DB00282EDB431B01334FEA430BE7 -:1020DC0000205946FFF7DAFCFEF7C0FB654B0026E3 -:1020EC0003EB45033B6055451BDCF320FEF7CCFBB3 -:1020FC003B68002133F81600FEF7E6FB082806F1D2 -:10210C00010608D05C4801F032F8284601F03EF890 -:10211C0001F025F805E030465946FFF7B7FC0135CC -:10212C00E1E7FEF7A9FB554801F02AF87B680BB1F3 -:10213C004E4B00E0524B7B6063083B600025A54290 -:10214C003CD2661BB6F5803F28BF4FF480362EB1CB -:10215C004C4805EB0901324601F03DF8D7F804A0D4 -:10216C00FEF77CFBF320FEF78FFBAA444FF0000B2D -:10217C00B34519D043490BEB0A0011F80B10FEF7CD -:10218C00DBFB08280DD0404800F0F1FF504600F072 -:10219C00FDFF00F0E4FFFEF76FFB3C4800F0F0FFA2 -:1021AC000CE00BF1010BE3E7FEF766FB3B68214605 -:1021BC0003EB5500FFF76AFC5D44C0E7344800F0C0 -:1021CC00D6FF204600F0E2FF324800F0D9FF002095 -:1021DC00FFF7CAFB20B1304800F0D2FF234D03E0DB -:1021EC002E4800F0CDFF264D4FF480500121A6085B -:1021FC00FEF78AFCFEF706FB31462846FEF70EFB7F -:10220C0004F0030105EB860000F0F4FE0021044607 -:10221C004FF48050FEF778FC214800F0A8FF4046B0 -:10222C0000F0B4FF1F4800F0A2FF204600F0AEFF04 -:10223C0000F095FF444504D01B4800F0A1FF02209C -:10224C0000E000201C37BD46BDE8F08FDB390008EC -:10225C00E4390008EE390008F5390008143A000892 -:10226C00313A0008543A0008673A00080040000868 -:10227C00FF3F0008D83200087F3A0008973A000860 -:10228C000000010844000020AA3A0008C33A0008E4 -:10229C00CE3A0008DC3A0008E33A0008083B000894 -:1022AC002D3B0008403B0008463B000870B54FF43E -:1022BC0080500121FEF740FCFFF774FD0520164C01 -:1022CC00FFF75EFE002C04F1FF341FDCFFF740FD2E -:1022DC00FFF768FD9F20FFF753FEA920FFF750FE84 -:1022EC000546A920FFF74CFE0646A920FFF748FE3D -:1022FC003602044646EA0545FFF72AFD4FF48050A6 -:10230C000021FEF719FC45EA040070BDA920FFF777 -:10231C0037FEC307D6D4D9E70090D00308B50446DE -:10232C00114800F024FF204600F030FF00F017FFAA -:10233C002046FFF7F5FB00F004FF0C4B20F00400E7 -:10234C0018701D4600F0FDFE2B7800F0FB00834258 -:10235C0001D10B2401E000F0C0FE013CF2D04FF49F -:10236C007A7000F07FFEF8E7F13B00084400012092 -:10237C0080B5464B4FF4C06290B01A6001214FF00B -:10238C008050FEF7CDFB0120FEF714FB4048FFF711 -:10239C002DFB4FF480200121FEF7C2FB3C480A21A3 -:1023AC000722FEF7C6FE072239480B21FEF7C1FEB5 -:1023BC0010AF4FF4806347F8383D00244FF001080C -:1023CC000225334839468DF80E408DF80F808DF874 -:1023DC000C508DF80D50FEF754FE4FF400632C4852 -:1023EC00394602938DF80C5004AEFEF74AFE4FF4BA -:1023FC006133049327480C233146ADF81A30ADF8FD -:10240C001440ADF81640ADF81840ADF81C40FEF77E -:10241C00CFFD41461F48FEF7FDFE1D48FFF7CAFAE7 -:10242C001D4800F0A4FE1D4B586F00F0AFFE00F0ED -:10243C0096FE7920FEF7C8FB404502D1184800F003 -:10244C009FFE012000F014FE10B90121FEF71CFDC7 -:10245C004120FEF7B9FB58BB124800F091FE012059 -:10246C00FEF7E0FA47F230544120FEF7ADFBF8B925 -:10247C004FF47A7000F0F6FD013CF5D10A4800F0FB -:10248C007FFEFFF7D1FB094837E100BF003C02405B -:10249C000008024000480040FD3B000800380240A4 -:1024AC000D3C00081D3C0008353C0008114550FE51 -:1024BC004FF48070FEF70AFB0120FEF71FFBFEF7BE -:1024CC00E1FB0020FEF77CFA01214FF48040FEF77F -:1024DC0033FBBB48FFF78AFA3046FEF712FE0024A6 -:1024EC000125B74831468DF815408DF816408DF80A -:1024FC00174004958DF81450FEF7C3FD2246B048E2 -:10250C002946FEF711FEAE48FFF754FAAD4C04F124 -:10251C00300954F8040CFFF769FA3046FEF7F1FD68 -:10252C00236854F8040C4FF000083146022504933C -:10253C008DF814808DF815508DF817800C34FEF73B -:10254C00A0FD54F8100CFFF735FA4C45E1D14FF4CF -:10255C0080404146FEF7F0FA4FF4807000F088FDA1 -:10256C00002800F0CC804FF4807000F08BFDFFF75A -:10257C0029FC954800F004FE0220FEF795FC9349D7 -:10258C00884202D00220FEF77FFC914800F0EFFD5C -:10259C000220FEF789FC00F0F9FD00F0E0FD4FF49D -:1025AC00804000F065FD90B18A4800F0E9FD4FF4E1 -:1025BC00804000F067FD082000F064FD102000F062 -:1025CC0061FD202000F05EFD402000F05BFDFFF778 -:1025DC002BFBFFF70DFB01214FF48050FEF7ACFAFB -:1025EC007748FFF703FA01200146FEF78DFA052123 -:1025FC000A467348FEF79DFD714806210522FEF739 -:10260C0098FD05226E480721FEF793FD0024022554 -:10261C00C0236B4839468DF80C508DF80D50029341 -:10262C008DF80E408DF80F40FEF72BFD20236448EB -:10263C0039468DF80F5002930125FEF722FD102329 -:10264C0039465F4802938DF80C508DF80F50FEF709 -:10265C0018FD6148FEF77EFC4FF48273ADF8123022 -:10266C004FF40073ADF81A305B4807233146ADF8D0 -:10267C002030ADF81040ADF81440ADF81640ADF870 -:10268C001840ADF81C40ADF81E40FEF78DFD2946F4 -:10269C005148FEF7A8FD4A48FFF78CF9FFF782FB7B -:1026AC00AB20FFF76DFCFFF753FB642000F0DAFC66 -:1026BC0021464FF48050FEF73FFAFFF7F7FD28460E -:1026CC00FEF7F2FB25460490E8B2FFF753FB98BBEC -:1026DC0070550135042DF7D1049D3DB13F4800F0F4 -:1026EC0046FD284600F052FD00F039FD0120294638 -:1026FC00FEF7CAFB002C3DD03948FFF70FFE2846E9 -:10270C00FEF764F9012814D12846FEF769F94FF455 -:10271C000060FEF703FC18B1324800F031FDB9E05F -:10272C00314C00F00EFD002800F00481013CF8D182 -:10273C00B0E000F0BDFC1CE7735D042B0ADD2B48F8 -:10274C0000F01EFD049800F021FD01200021FEF791 -:10275C009BFB0FE00133DBB2042B7355B9DD24482E -:10276C0000F005FD284600F011FD224800F008FDA0 -:10277C000124AEE7FFF79AFD1F4B0344012B01D94F -:10278C001E48BAE71E4800F0F2FC0020FEF78CFB56 -:10279C0000F0FCFC00F0E3FC802000F069FC18B1B8 -:1027AC00802000F06FFC73E04FF4003000F060FC10 -:1027BC00044638B34FF4003000F064FC04E100BF71 -:1027CC0000000240A83200086D3C0008708641569B -:1027DC00A93C0008BE3C0008003001402C3D00081C -:1027EC00014550FE523C0008A0860100EB3C00085D -:1027FC00173D0008223D0008EA44DFFF024550FE69 -:10280C00583D00080120FFF7B5FAD8B12046FFF774 -:10281C00B1FAB8B1B74800F0B3FC41F288340120EA -:10282C00FFF7A8FA002800F08B800020FFF7A2FA2F -:10283C00002800F085804FF47A7000F013FC013C06 -:10284C00EDD1C1E00020FFF78FF808B1AA4B00E0F2 -:10285C00AA4B0CCB013301D0013271D1A84800F046 -:10286C008FFCB1E0A74800F08BFC082000F00AFCBC -:10287C00102000F007FC202000F004FC402000F0A9 -:10288C0001FC0024042000F0FDFB002C40F008812A -:10289C009D4832E7FFF77CF8002847D0002001A9BB -:1028AC009A4DFEF759FA344600203946FEF7B8FA2D -:1028BC000FCD0FC40FCD0FC495E80F0084E80F00A7 -:1028CC009DF8092010A901EB82039DF80BE053F849 -:1028DC00343C40F26D1101FB0E3303EB9E030EF101 -:1028EC00010E1EF0030F02D1022A88BF01339DF89E -:1028FC000A409DF80410013C2344182404FB0314E3 -:10290C009DF805103C2202FB04149DF806100320D0 -:10291C0002FB0414FEF7C8FA01190320FEF7B4FAFF -:10292C0004200021FEF7B0FA00F0C2FB784800F05A -:10293C0027FC1EE6774800F023FCFFF743FAF5E787 -:10294C007D20FEF741F970B94FF4007000F090FB58 -:10295C0048B9714800F014FC082000F093FB1020DB -:10296C0000F090FB85E07D20FEF72EF910B16B484E -:10297C0000F006FC4FF4007000F07AFB10B16848D0 -:10298C0000F0FEFB4FF4007000F07CFB102000F018 -:10299C006FFB48B1634800F0F3FB082000F072FBBA -:1029AC00102000F06FFB12E0082000F061FB20B15A -:1029BC005D4800F0E5FB102003E05C4800F0E0FB14 -:1029CC00082000F06AFB54E0594800F0D9FB594844 -:1029DC0000F0D6FB4FF40010FFF7ECFA10B1022810 -:1029EC0028D809E0202000F04DFB402000F04AFBE5 -:1029FC00802000F052FB1DE0082000F04EFB102060 -:102A0C0000F04BFB202000F033FB20B94A4800F0CB -:102A1C00B7FB202009E0402000F02AFB00287FF4BF -:102A2C0021AF464800F0ACFB402000F036FB4FF4E1 -:102A3C0000701AE0012425E7414800F0A1FB0820B2 -:102A4C0000F020FB102000F01DFB202000F01AFBF2 -:102A5C00402000F017FB2046FFF7ACFA022840F0AC -:102A6C00F480082000F019FB102000F016FB00F099 -:102A7C0034FB022000F0FCFA90B1042000F0F8FACC -:102A8C0004460028D8D02F4800F07AFB042000F030 -:102A9C00F9FA022000F0F6FA4FF4004000F0F2FAD6 -:102AAC004FF4005000F0E4FA04464FF4805000F06C -:102ABC00DFFA400040EA84044FF4006000F0D8FADA -:102ACC00E4B22043431C03F0FF03013B4FF40060CE -:102ADC00062B5CD8DFE803F03C383C043C383C0067 -:102AEC0000F0D0FA4FF4805000F0CCFA4FF40050C4 -:102AFC002EE000BF643D0008004000080000010803 -:102B0C00963D0008C03E0008034550FE50320008B8 -:102B1C005D3C00085F3F0008A93D0008BA3D000875 -:102B2C00D23D0008F23D0008083E00081E3E000899 -:102B3C00743F0008343E00084E3E0008873E0008F3 -:102B4C00303F0008FA3E000800F09CFA4FF4805029 -:102B5C0000F0A3FA45F25550FDF70AFF0420FDF7EB -:102B6C000DFF40F6FF70FDF70FFF0020FDF700FF93 -:102B7C004FF480500121FDF765FEFDF713FFFDF7C3 -:102B8C0009FF0020FEF7F0FE68B9354B0CE000F0B1 -:102B9C0079FA4FF4805000F075FA4FF4005000F0C1 -:102BAC0071FA3048A9E5304B5D6830481E6800F07A -:102BBC00DEFA284600F0EAFA00F0D1FA00F0CFFA7B -:102BCC0000239A0002F16042013302F561424FF09A -:102BDC00FF31082BC2F88010C2F88011F1D1244BC0 -:102BEC0000241C635C639C631C645C64FDF700FF45 -:102BFC0020480121FDF7ACFF21461E48FDF7A8FF38 -:102C0C00F1200121FDF7B0FF2146F120FDF7ACFFCB -:102C1C0001200146FDF7B4FF21460120FDF7B0FF6E -:102C2C0015480121FDF7B8FF21461348FDF7B4FF05 -:102C3C0012480121FDF7BCFF10482146FDF7B8FFF3 -:102C4C0063B64FF0FF3EB5462847042000F01AFA51 -:102C5C00022000F017FA4FF4004000F01EFA1FE7B4 -:102C6C0000000108044550FE004000084B3F0008DE -:102C7C000038024000106022FFC9FE3633590700AD -:102C8C000748084A08B50849121AFDF7D7F907484A -:102C9C00074A0021121AFDF7DAF9FFF769FB00BFAA -:102CAC000000002020000020E03F00082000002051 -:102CBC00480001202DE9F04FDDE909AB0E469C469A -:102CCC0003783B490746C95C01300D07F8D42B2B20 -:102CDC0005D02D2B04D107464FF0FF3301E00746FA -:102CEC000123397830290ED17978782905D132F041 -:102CFC00100103D10237102207E00AB1082A04D1CF -:102D0C000137082201E002B90A2200200021B8464E -:102D1C0017F8015BA5F13004E4B2092C0DD9A5F12B -:102D2C004104192C02D8A5F1370405E0A5F1610482 -:102D3C00192C30D8A5F15704E4B294422BDA4FEA9F -:102D4C00E27900FB09F502FB0155A0FB0201E4B29C -:102D5C00294400194FF0000541EB0501BCF1000FAF -:102D6C00D5D0012B06D182457BEB0104CFDA50463E -:102D7C005946CCE75C1CCAD1DDE90B89444261EBB6 -:102D8C004105444575EB0909C1DADDE90B01404207 -:102D9C0061EB4101BBE70EB1C6F80080DD1700FB0B -:102DAC0005F203FB0122A0FB03011144BDE8F08FE7 -:102DBC004C1200082DE9F04F91B01F9E0C463106C5 -:102DCC0091468046DDE91A231E9D04D5CDE90223E8 -:102DDC004FF0000C1BE0002A73F100071046194657 -:102DEC0002DA504263EB4301002A73F1000ACDE989 -:102DFC00020107DBB70708D416F0040C07D04FF01C -:102E0C00200C04E04FF02D0C01E04FF02B0C16F0D1 -:102E1C004007089714BF704F704FDDE902AB07975E -:102E2C0000271C980137C117CDE90401DDE9042303 -:102E3C000AA93944099150465946CDF804C0FEF709 -:102E4C007FFA07980999835C504601F8013C594672 -:102E5C00DDE90423FEF774FA82468B465AEA0B022C -:102E6C00DDF804C001D0162FDBD116F0080239466C -:102E7C001DD01C9B082B02D0102B0ED01CE0DDE9C2 -:102E8C0002AB1DB95AEA0B0B13D004E05AEA0B0B38 -:102E9C0012D0BD4210DC7B1C0FE0DDE902AB5AEA1C -:102EAC000B0B09D008982B4648B1582208E02B464A -:102EBC0006E02A46012303E02B46002200E078229C -:102ECC001CF1000B1D98C7EB030A18BF4FF0010B48 -:102EDC00BB42A8BF1F46C01B2AEAEA7ACBEB000014 -:102EEC000AB1022700E01746C71B16F0010027EABB -:102EFC00E77701D07F4205E0F60603D5013504BF24 -:102F0C00BA4407463D46002D0BDD2068013D461CAA -:102F1C004E453CBF202608F8006020680130206038 -:102F2C00F1E727EAE7703F1ABBF1000F08D02068E1 -:102F3C00451C4D4538BF08F800C0206801302060A2 -:102F4C008AB12068451C4D453CBF302508F800501F -:102F5C002568681C02354D45206038BF08F80020F4 -:102F6C00226801322260BAF1000F0CD022680AF1FB -:102F7C00FF3A501C48453CBF302008F8020022683C -:102F8C0001322260EFE7DDE902AB5AEA0B0B10D1FC -:102F9C007BB919460DE0236801395A1C4A4505D204 -:102FAC0001F1280568462A5C08F8032023680133E0 -:102FBC0023600029EFD15FB1236801375A1C4A45C1 -:102FCC003CBF202208F80320236801332360F2E77A -:102FDC0011B0BDE8F08F00BFA73F0008B83F000854 -:102FEC001EF0040F0CBFEFF30880EFF30980FEF71F -:102FFC00BFBF7047032970B505460C4606D98E082D -:10300C003146FDF70BFCB6003544A41B14B9FDF793 -:10301C0013FC70BD00231846EA5C0133A34242EA5C -:10302C000020F9D1FDF7F4FB70BD2DE9F041073319 -:10303C00DE08079B4FF0120808FB02320731069F8F -:10304C00054602EBD1080024BC4209D0122000FB3B -:10305C00048029463246FCF7F1FF01343544F3E78E -:10306C00BDE8F0810623584301387FF4FDAF70476B -:10307C0010B504460020FDF717FF20420CBF0020BE -:10308C00012010BD10B504460020FDF70DFF20EA0D -:10309C0004010020FDF7F8FE10BD10B50446002019 -:1030AC00FDF702FF40EA04010020FDF7EDFE10BD24 -:1030BC001FB501A8FEF765F80723029301A80323A7 -:1030CC000393FDF7BDFD6846FEF761F869460020E5 -:1030DC00FDF7FAFD05B05DF804FB08B5FEF7BCFC86 -:1030EC000020FEF733FC0020FEF730FC0020FEF73A -:1030FC002DFCFEF795FCFDF73BFBFEF701FC70B5D4 -:10310C000646FEF7A9FC8020FEF720FCA82420BA76 -:10311C0090FAA0F0C0B2FEF719FC1125705DFEF715 -:10312C0015FC15F1FF35F9D20020FEF70FFC013C20 -:10313C0006F11206EBD12046FEF708FCFEF770FCF8 -:10314C0070BD38B500242546E0B2FEF713FEA04052 -:10315C0001340543042CEDB2F6D1284638BD08B530 -:10316C000D20FEF7DDFB0A20FEF7DAFB08BD10B5DB -:10317C00441E14F8010F10B1FEF7D2FBF9E710BD95 -:10318C0008B5FFF7F4FFFFF7EAFF08BD1FB53023C2 -:10319C008DF8043078238DF80530072399000F2221 -:1031AC008A400240CA40092A01D8303202E00F2A74 -:1031BC0002D85732D2B200E0202201A9CC1A13F166 -:1031CC00FF336272EAD2002308468DF80E30FFF707 -:1031DC00CEFF04B010BD70B504460D464FF48050C0 -:1031EC0001211646FDF7A8FCFEF7DCFD0520FEF7D5 -:1031FC00C7FEA920FEF7C4FEC307FAD4FEF7A8FD4C -:10320C00FEF7D0FD0320FEF7BBFEC5F30740FEF72B -:10321C00B7FEC5F30720FEF7B3FEE8B2FEF7B0FE2B -:10322C002644B44205D0A920FEF7AAFE04F8010BEF -:10323C00F7E7FEF78DFD4FF480500021FDF77CFC85 -:10324C0070BD0000000000001F0000003B000000EB -:10325C005A0000007800000097000000B500000044 -:10326C00D4000000F3000000110100003001000048 -:10327C004E0100000000000800400008008000081B -:10328C0000C000080000010800000208000004084B -:10329C0000000608933F00080008024008000000E8 -:1032AC00983F000800000240040000009B3F00080B -:1032BC000008024040000000A23F0008000002404D -:1032CC0002000000000002400100000000000800A5 -:1032DC001000180020002800300038007C00FE0090 -:1032EC00FF01C701C701C701C701C701C701C7015A -:1032FC00C701C701C701C701C701FF01FE007C0060 -:10330C0038003C003E003E003800380038003800E1 -:10331C003800380038003800380038003800FE001B -:10332C00FE00FE007C00FE00FF01C701C701C001CA -:10333C00C001E000F00078003C001E000E000F0001 -:10334C000700FF01FF01FF017C00FE00FF01C70128 -:10335C00C701C001C001F8007800F800C001C0012D -:10336C00C001C701C701FF01FE007C00E000E000C6 -:10337C00F000F000F800F800F800FC00EC00EE00A3 -:10338C00E600FF01FF01FF01E000E000E000E000CB -:10339C00FF00FF00FF000700070007007F00FF0091 -:1033AC00FF01C701C001C001C701C701C701FF016F -:1033BC00FE007C007C00FE00FF01C701C701070076 -:1033CC0007007700FF00FF01C701C701C701C70154 -:1033DC00C701FF01FE007C00FF01FF01FF01E000BF -:1033EC00E0007000700070003800380038003800C1 -:1033FC001C001C001C001C001C001C007C00FE009F -:10340C00FF01C701C701C701C701FE007C00FE0018 -:10341C00C701C701C701C701C701FF01FE007C003E -:10342C007C00FE00FF01C701C701C701C701C7012E -:10343C00FF01FE01DC01C001C001C701C701FF0192 -:10344C00FE007C0000000000000000007C00FE007C -:10345C00FF01C701C701F001FC01CE01C701C70183 -:10346C00E701FF01DF01CE0107000700070007009D -:10347C00E700F701FF01CF01C701C701C701C70171 -:10348C00C701C701CF01FF01F701E70000000000F1 -:10349C00000000007C00FE00FF01C701C70107000F -:1034AC00070007000700C701C701FF01FE007C00F1 -:1034BC00C001C001C001C001CE01DF01FF01E70165 -:1034CC00C701C701C701C701C701C701E701FF0158 -:1034DC00DF01CE0100000000000000007C00FE00B7 -:1034EC00FF01C701C701C701FF01FF010700C701A9 -:1034FC00C701FF01FE007C00E000F000F80038007E -:10350C00FE00FE00FE00380038003800380038009D -:10351C0038003800380038003800380000000683C6 -:10352C000100000000000000000000000683010004 -:10353C000000000C000000000000068301000000E9 -:10354C00000C00000000D878369B79C0E3D90C8CB5 -:10355C0067DB3C1BF8FD7EBFFDE0F7FB1FC6EFFBF6 -:10356C007E1F98CD66B3CD60369B19C66C1866036A -:10357C0098FD66B3FD60309B19C66F18660398FD05 -:10358C0066B3FD60309B19C36F186603980D66B364 -:10359C000D60369B19C360186603F8FD7EBFFDEC09 -:1035AC00F79B19C36F187E03D878369B79CCE399B7 -:1035BC00998167183C03180000000000000080018E -:1035CC0000000000180000000000000000000000D7 -:1035DC0000001800000000000000000000000000C7 -:1035EC00000C000600000000600030000018000312 -:1035FC0000000000C0001800003080010000000036 -:10360C0080010C000060C0000000000000030600F8 -:10361C0000C06000000000000006030000803100C4 -:10362C0000000000008C010000001B0000000000E6 -:10363C0000D8000000000E00000000000070000028 -:10364C0000000E00000000000070000000001B00D5 -:10365C000000000000D800000080310000000000D5 -:10366C00008C010000C06000000000000006030098 -:10367C000060C00000000000000306000030800164 -:10368C000000000080010C00001800030000000086 -:10369C00C0001800000C00060000000060003000A4 -:1036AC00000000000000000000000000000000000E -:1036BC0000000000000000000000000000000000FE -:1036CC0000000000000000000000000000000000EE -:1036DC0000000000000000000000000000000000DE -:1036EC0000000000000000000000000000000000CE -:1036FC0000000000000000000000000000000000BE -:10370C000000000080FFFF0100000000000000002E -:10371C00FEFFFF7F00000000000000E07F0000FEC5 -:10372C0007000000000000FE010000807F00000088 -:10373C000000801F00000000F80100000000F001F4 -:10374C0000000000800F000000007C000000000062 -:10375C00003E000000000F000000000000F0000020 -:10376C00008003000000000000C0010000E0000029 -:10377C0000000000000007000070000000000000C6 -:10378C0000000E00001800000000000000001800EF -:10379C00000C0000000000000000300000060000DB -:1037AC0000000000000060000003000000000000AA -:1037BC000000C0008001000000000000000080013B -:1037CC00C0000000000000000000000360000000CA -:1037DC0000000000000000063000000000000000A7 -:1037EC000000000C100000000000000000000008A9 -:1037FC000000000000600000030018000000000042 -:10380C000000006000000300180000000000000031 -:10381C0000600000030018000000000000000060C1 -:10382C000000030018000000000000000060000011 -:10383C0003001800000000E003001F60F800C3073D -:10384C0018C0070000F80FC07F60FE03F31F18F0CC -:10385C001F00000C1860C06003061B3018183000E5 -:10386C000006303080E1010C0F60180C6000000382 -:10387C00601800E3001807C01806C0008001400C57 -:10388C00006200100380180380008001C00C0066E9 -:10389C0000300380190380018001C0FCFF670030F9 -:1038AC00038019FFFF018001C0FCFF63003003801F -:1038BC0019FFFF008001C00C006000300380190369 -:1038CC0000008001C00C0060003003801903000070 -:1038DC008001400C006200100380180380008003FC -:1038EC00601800C3001806C01806C00080073030EE -:1038FC008081010C0C60180C6000800D1860C000F9 -:10390C00030618303818300080F90FC07F00FE0312 -:10391C00F01F70F01F0080E103001F00F800C007CB -:10392C0060C00700800100000000000000000000E3 -:10393C0000008001000000000000000000000000FA -:10394C008001000000000000000000000000800169 -:10395C0000000000000000000000000080010000DA -:10396C00000000000000000000004E6F20485720AF -:10397C0056657273696F6E20696E204F5450004209 -:10398C0042322E30007573622077616B6575702042 -:10399C00737570706F7274656400456E74657269CE -:1039AC006E67207374616E64627900206973206F96 -:1039BC007574736964652073797374656D20666CB6 -:1039CC006173680048415244204641554C540044B0 -:1039DC006573636C656E20000A4669726D6C656E6A -:1039EC0020000A5873756D20000A496E76616C6967 -:1039FC0064206669726D77617265206465736372A9 -:103A0C00697074696F6E2100436865636B73756DC3 -:103A1C006D696E67206669726D776172652075706D -:103A2C006461746500496E76616C696420666972C4 -:103A3C006D776172652043524320696E2053504963 -:103A4C0020666C617368210065726173655F6F6CD1 -:103A5C00645F6669726D77617265004F6C642057A4 -:103A6C006F726C64206669726D7761726520626139 -:103A7C007365006661696C656420746F20657261A2 -:103A8C00736520736563746F722000777269746557 -:103A9C005F6E65775F6669726D77617265006661EE -:103AAC00696C656420746F20777269746520616439 -:103ABC0064726573732000576527726520646561B5 -:103ACC006400436865636B73756D6D696E67200088 -:103ADC00206279746573004E657720576F726C6441 -:103AEC00206669726D776172652073797374656D88 -:103AFC005F666C6173685F62617365004F6C642014 -:103B0C00576F726C64206669726D77617265207391 -:103B1C00797374656D5F666C6173685F6261736500 -:103B2C0000436865636B73756D202D2077616E742F -:103B3C006564200020676F7420004F757220696ED9 -:103B4C007465726E616C20666C61736820636F6E55 -:103B5C0074656E74732061726520626164202863E1 -:103B6C006865636B73756D206661696C656429218A -:103B7C002054686973206973207265616C6C7920BC -:103B8C006261642100FFFFFFFFFFFFFFFFFFFFFFEC -:103B9C00FF01000000000000000000008001000098 -:103BAC000000000000000000800100000000000088 -:103BBC000000000080010000000000000000000078 -:103BCC0080010000000000000000000080010000E7 -:103BDC00000000000000000080FFFFFFFFFFFFFF60 -:103BEC00FFFFFFFFFF5341442057415443483A2005 -:103BFC000052657365742052656769737465722031 -:103C0C000042726F776E206F7574207265736574E5 -:103C1C00005374617274696E67204C5345206F7346 -:103C2C0063696C6C61746F72004C5345206F7363E5 -:103C3C00696C6C61746F7220646964206E6F74209F -:103C4C007374617274005553422077616B657570A3 -:103C5C00006C656176696E67207374616E6462795D -:103C6C0000205F5F5F5F5F5F202020205F5F0D0A99 -:103C7C002F5F20205F5F2F205F5F2F202F0D0A20EA -:103C8C002F202F2020202F5F20205F5F2F0D0A2F49 -:103C9C005F2F20202020202F5F2F0D0A00426F6FF6 -:103CAC00746C6F616465722076657273696F6E3ABD -:103CBC0020004C617374206669726D7761726520A7 -:103CCC00626F6F742077617320737461626C653BF3 -:103CDC0020636C65617220737472696B6573005339 -:103CEC007475636B20627574746F6E20726567698E -:103CFC007374657220697320696E76616C69642CCB -:103D0C0020636C656172696E672E00427574746F06 -:103D1C006E2069642000697320737475636B2100D5 -:103D2C00427574746F6E207761732070757368655B -:103D3C0064206F6E20626F6F742E20427574746FE6 -:103D4C006E20636F756E7465723A2000426F6F74EB -:103D5C0020626974733A2000486F6C6420646F773A -:103D6C006E205550202B204241434B20666F722011 -:103D7C003520736563732E20746F20666F726365D4 -:103D8C002D626F6F7420505246004669726D7761D8 -:103D9C0072652069732065726173656400426F6F90 -:103DAC0074696E67206E6F726D616C6C790057610F -:103DBC00746368646F672063617573656420612048 -:103DCC00726573657400536F6674776172652066F3 -:103DDC0061696C757265206361757365642061201F -:103DEC00726573657400426F6F74206661696C65EF -:103DFC00642C20737472696B65203300426F6F748E -:103E0C00206661696C65642C20737472696B652023 -:103E1C003200426F6F74206661696C65642C20738C -:103E2C007472696B652031004C6F6164696E672038 -:103E3C007265636F76657279206669726D776172EF -:103E4C0065004661696C656420746F206C6F6164F9 -:103E5C00207265636F76657279206669726D776121 -:103E6C0072652C20737472696B65206F6E652E20E1 -:103E7C0054727920616761696E2E004661696C65C8 -:103E8C006420746F206C6F6164207265636F76655B -:103E9C007279206669726D776172652C2073747209 -:103EAC00696B652074776F2E20547279206167617D -:103EBC00696E2E004661696C656420746F206C6FAE -:103ECC006164207265636F76657279206669726DC4 -:103EDC00776172652C20737472696B6520746872DB -:103EEC0065652E20534144205741544348004F757B -:103EFC00722070726576696F7573206669726D7762 -:103F0C0061726520757064617465206661696C65A9 -:103F1C00642C2061626F7274696E672075706461C5 -:103F2C0074652E004E6577206669726D77617265D7 -:103F3C0020697320617661696C61626C6521004255 -:103F4C006F6F74696E67206669726D776172652038 -:103F5C0040200072657475726E696E6720746F20F4 -:103F6C007374616E64627900466F7263652D626F63 -:103F7C006F74696E67207265636F76657279206DF8 -:103F8C006F64652E2E2E004261636B0055700053DA -:103F9C00656C65637400446F776E00303132333476 -:103FAC003536373839414243444546003031323397 -:103FBC0034353637383961626364656600286E754E -:103FCC006C6C2900000000000102030401020304D0 -:043FDC0006070809C3 -:103FE000FFFFFFFF00A000000202000000C004016C -:103FF00000000000000000021000000007000000A8 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/boot_ev2_4@1447134832.bin b/bin/boot/boot_ev2_4@1447134832.bin deleted file mode 100755 index c6f9844f43..0000000000 Binary files a/bin/boot/boot_ev2_4@1447134832.bin and /dev/null differ diff --git a/bin/boot/boot_ev2_4@1447134832.hex b/bin/boot/boot_ev2_4@1447134832.hex deleted file mode 100644 index 5b291e4887..0000000000 --- a/bin/boot/boot_ev2_4@1447134832.hex +++ /dev/null @@ -1,1053 +0,0 @@ -:020000040800F2 -:1000000048200120392C0008992F0008992F00085A -:10001000992F0008992F0008992F00080000000070 -:10002000000000000000000000000000992F000800 -:10003000992F000800000000992F0008992F000850 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F098FD64 -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:0801600085FD05B05DF804FB0C -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:0801880071FD05B05DF804FBF8 -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F042FCDDF820C050 -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F0E3FB089B9CE0BA4293 -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00453F00084B45F7DBEDE70D99079107983F -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D014C3F000855 -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C00BFF34F8F044A054BD16801F4E0610B43E0 -:10191C00D360BFF34F8FFEE700ED00E00400FA0543 -:10192C0010B5044608484021FFF784FC0028F9D084 -:10193C0005482146FFF77AFC03484021FFF77AFC63 -:10194C000028F9D010BD00BF00480040014608B582 -:10195C000448FFF757FC03480221FFF755FC002809 -:10196C00F9D008BD0038004000B9074803680133BE -:10197C0008D04368013305D0C069044BC31A5842E0 -:10198C005841704700207047004000086F57654E63 -:10199C00074B1A69C2F306427F2A07D11869C0F3B4 -:1019AC000C00B0F1FF0358425841704700207047BB -:1019BC000028004000F13F4000F57E00800A082816 -:1019CC000FD8084A52F820305BB1013B42F8203066 -:1019DC003BB9012101FA00F0034B1A6B22EA00001B -:1019EC0018637047200000200038024000F13F408F -:1019FC0000F57E00800A08280DD8074A52F82030DE -:101A0C00591C42F8201033B9012101FA00F0034BA4 -:101A1C001A6B1043186370472000002000380240F6 -:101A2C0010B50C4C2046FFF7E1FF072001F0F0FA4F -:101A3C004FF4805100222046FFF776FB042001F082 -:101A4C00E7FA2046FFF7B6FF4FF480400021FFF77E -:101A5C0067F810BD0004024010B50B4C01214FF487 -:101A6C008040FFF75DF82046FFF7C0FF20464FF49B -:101A7C0080510122FFF758FB072001F0C9FA2046DC -:101A8C00FFF798FF10BD00BF00040240F0B55E25C3 -:101A9C006843204BB0FBF1F51A68ADF6DC3D95427E -:101AAC0034D002AC00214FF43D6220461D60FEF79D -:101ABC00CEFA1B238DE81800174810214022692309 -:101ACC0001F089FA08238DE81800144860231821C6 -:101ADC006A2201F080FA0DF2FA760DF28E73002272 -:101AEC00AA420DD002F11900C0B2C108012700F0C2 -:101AFC00070007FA00F05F5C013238435854EFE7F7 -:101B0C001233B342EBD1204601F0CFFA0DF6DC3D97 -:101B1C00F0BD00BF00000020943200080E34000815 -:101B2C002DE9F043ADF6DC3D02AC00214FF43D62F3 -:101B3C0080462046FEF78BFA2C238DE818002022D5 -:101B4C00284818215C2301F046FA1C2215460F2761 -:101B5C00974007EA0807D740234B242101FB07379E -:101B6C000DF50366002117F801C00023082B07D0E0 -:101B7C000120984010EA0C0F0CBF0020012003E05C -:101B8C007818407800F0010078B15819C0B24FEACB -:101B9C00D00E4FF0010900F0070009FA00F016F81A -:101BAC000E9040EA090006F80E000133092BDDD136 -:101BBC000231242906F11206D5D10B35742DA2F170 -:101BCC000402C4D10E238DE81800102190226D233D -:101BDC00064801F000FA204601F067FA0DF6DC3DEC -:101BEC00BDE8F0835D3900086E34000899380008B0 -:101BFC0010B5ADF6D83D02AC204600214FF43D6245 -:101C0C00FEF725FA1B238DE81800102140226923CA -:101C1C00044801F0E0F9204601F047FA0DF6D83DF2 -:101C2C0010BD00BF943200082DE9F74F434E444FCE -:101C3C0001214FF48040FEF773FF3046FFF7D6FECC -:101C4C003846404CFFF7D2FE30460D210522FFF7F7 -:101C5C0070FA052230460F21FFF76BFA3046214609 -:101C6C000125FFF70EFA2023384621462360257103 -:101C7C00A5714FF0000A01934FF4805BFFF701FA56 -:101C8C00304621464FF002084FF48049257184F804 -:101C9C0006A0C4F800B0FFF7F4F93046214684F8EA -:101CAC000480C4F80080FFF7ECF921462571A5717A -:101CBC00C4F80090244C3046FFF7E3F92046FFF7B8 -:101CCC0049F920462149FFF76FFA20462946FFF7CC -:101CDC008AFA019B384619465246FFF725FA2A46DE -:101CEC0030464946FFF720FA40462946FEF718FFD2 -:101CFC00A4F5505480232380F92323854FF4F0421C -:101D0C004FF6FF13E3623046A28329462364424612 -:101D1C00A4F820B0FFF70DFA29462046A582FEF75D -:101D2C002FFD3846FFF746FE3046FFF743FE484688 -:101D3C005146FEF7F5FE03B0BDE8F08F00040240FB -:101D4C000008024004000020003800400C00002075 -:101D5C0010B5074C2046FFF749FE20461021FFF72F -:101D6C00DFF92046FFF726FE012001F051F910BDE6 -:101D7C000000024038B50A4B0C2404FB00346068A8 -:101D8C00FFF734FE21896068FFF7C4F905466068E7 -:101D9C00FFF710FED5F1010038BF002038BD00BFA1 -:101DAC004C32000810B5064C2046FFF71FFE2046AB -:101DBC001021FFF7B7F92046FFF7FCFD10BD00BF5F -:101DCC0000000240F0B587B0FFF7E2FD002841D1DA -:101DDC000320FFF769F807460420FFF765F8374B37 -:101DEC000A241A68C2F30545C2F30626C2F302138D -:101DFC0002F00F0204FB0322330906F00F0604FB6A -:101E0C0003662B0905F00F0504FB03533C2505FB6A -:101E1C00036305FB0323191A44BF01F5A83101F52F -:101E2C00C07107EB91210320FFF72EF803A8FFF7F1 -:101E3C00A8F903A8FEF704FF01A8FFF7A8F901A968 -:101E4C000020FEF741FF02A8FFF7A7F9002002A926 -:101E5C00FEF7A4FF0A2442F2107501F048F910B9FC -:101E6C00013DFAD107E0164D01F041F908B1013DF1 -:101E7C00FAD1013CEFD10020FEF7A2FD114C00215C -:101E8C004FF20400FFF72CF84FF43C50FFF754F8D6 -:101E9C002368012043F004032360FEF791FD0A48F8 -:101EAC0001F044F9236843F002032360074B1A69DD -:101EBC0042F004021A6130BF07B0F0BD00280040A8 -:101ECC00A086010000700040AE36000800ED00E076 -:101EDC00B0F1006F10B504460BD20D4801F01DF99E -:101EEC00204601F029F90B4801F020F94FF0FF30A2 -:101EFC0010BD094A002352F8041FA14202D85168B0 -:101F0C008C4202D30133072BF5D1184610BD00BF0C -:101F1C00C2370008BF3600082832000808B502484E -:101F2C0001F004F9FFF7ECFCD836000810B50446B4 -:101F3C000A480221FFF768F90028F9D00748214622 -:101F4C00FFF760F905480121FFF75EF90028F9D089 -:101F5C000248FFF754F9C0B210BD00BF0030014079 -:101F6C002DE9F04F87B00121814600AF0020FFF72B -:101F7C008DFD042001F067F807F10C0049460C2296 -:101F8C0001F0FFF8FD683C690C2DD7F814B013D0A4 -:101F9C00994801F0C2F8284601F0CEF8974801F0B4 -:101FAC00BCF8204601F0C8F8954801F0B6F8584640 -:101FBC0001F0C2F8934833E0934801F0B7F84FF4BE -:101FCC0080500121FEF7A0FD09F10C09EA46FEF74D -:101FDC0019FCA0B06E462546C846802D0CD941464A -:101FEC003046802201F0CDF830462021FEF716FC59 -:101FFC00803D08F18008F0E725B1304641462A467D -:10200C0001F0BFF82946304600F0CAFF0021804697 -:10201C004FF48050FEF778FDD845D54604D07B4868 -:10202C0001F084F80120E3E088B06846494620229C -:10203C0001F0A7F86846FFF797FC78607448D5461E -:10204C0001F074F87B682BB9724801F06FF804F555 -:10205C00404600E02646002E3BD06F48FFF738FF85 -:10206C0005466E483044FFF733FF002D824630DBC7 -:10207C0000282EDB431B01334FEA430B002059464B -:10208C00FFF704FDFEF7EAFB654B002603EB450367 -:10209C003B6055451BDCF320FEF7F6FB3B6800214B -:1020AC0033F81600FEF710FC082806F1010608D0DC -:1020BC005C4801F032F8284601F03EF801F025F8B2 -:1020CC0005E030465946FFF7E1FC0135E1E7FEF744 -:1020DC00D3FB554801F02AF87B680BB14E4B00E05E -:1020EC00524B7B6063083B600025A5423CD2661BCB -:1020FC00B6F5803F28BF4FF480362EB14C4805EB27 -:10210C000901324601F03DF8D7F804A0FEF7A6FB12 -:10211C00F320FEF7B9FBAA444FF0000BB34519D0DE -:10212C0043490BEB0A0011F80B10FEF705FC0828CD -:10213C000DD0404800F0F1FF504600F0FDFF00F0DC -:10214C00E4FFFEF799FB3C4800F0F0FF0CE00BF1CC -:10215C00010BE3E7FEF790FB3B68214603EB5500D0 -:10216C00FFF794FC5D44C0E7344800F0D6FF2046EE -:10217C0000F0E2FF324800F0D9FF0020FFF7F4FB3B -:10218C0020B1304800F0D2FF234D03E02E4800F080 -:10219C00CDFF264D4FF480500121A608FEF7B4FC6C -:1021AC00FEF730FB31462846FEF738FB04F00301FE -:1021BC0005EB860000F0F4FE002104464FF480503D -:1021CC00FEF7A2FC214800F0A8FF404600F0B4FF47 -:1021DC001F4800F0A2FF204600F0AEFF00F095FF74 -:1021EC00444504D01B4800F0A1FF022000E0002071 -:1021FC001C37BD46BDE8F08FE3360008EC3600080E -:10220C00F6360008FD3600081C3700083937000880 -:10221C005C3700086F37000800400008FF3F0008DB -:10222C0084320008873700089F3700080000010837 -:10223C0044000020B2370008CB370008D63700081E -:10224C00E4370008EB370008103800083538000870 -:10225C00483800084E38000870B54FF48050012102 -:10226C00FEF76AFCFFF79EFD0520164CFFF75EFE9D -:10227C00002C04F1FF341FDCFFF76AFDFFF792FD21 -:10228C009F20FFF753FEA920FFF750FE0546A9201B -:10229C00FFF74CFE0646A920FFF748FE360204461F -:1022AC0046EA0545FFF754FD4FF480500021FEF738 -:1022BC0043FC45EA040070BDA920FFF737FEC307B5 -:1022CC00D6D4D9E70090D00308B50446114800F0E5 -:1022DC0024FF204600F030FF00F017FF2046FFF7E8 -:1022EC001FFC00F004FF0C4B20F0040018701D467E -:1022FC0000F0FDFE2B7800F0FB00834201D10B2493 -:10230C0001E000F0C0FE013CF2D04FF47A7000F016 -:10231C007FFEF8E76D3B00084400012080B5464B7A -:10232C004FF4C06290B01A6001214FF08050FEF75C -:10233C00F7FB0120FEF73EFB4048FFF757FB4FF43D -:10234C0080200121FEF7ECFB3C480A210722FEF716 -:10235C00F0FE072239480B21FEF7EBFE10AF4FF4CD -:10236C00806347F8383D00244FF0010802253348BC -:10237C0039468DF80E408DF80F808DF80C508DF885 -:10238C000D50FEF77EFE4FF400632C483946029345 -:10239C008DF80C5004AEFEF774FE4FF461330493C9 -:1023AC0027480C233146ADF81A30ADF81440ADF87F -:1023BC001640ADF81840ADF81C40FEF7F9FD41464B -:1023CC001F48FEF727FF1D48FFF7F4FA1D4800F0E1 -:1023DC00A4FE1D4B586F00F0AFFE00F096FE792066 -:1023EC00FEF7F2FB404502D1184800F09FFE012099 -:1023FC0000F014FE10B90121FEF746FD4120FEF756 -:10240C00E3FB58BB124800F091FE0120FEF70AFBDB -:10241C0047F230544120FEF7D7FBF8B94FF47A70ED -:10242C0000F0F6FD013CF5D10A4800F07FFEFFF705 -:10243C00FBFB094837E100BF003C024000080240AA -:10244C0000480040793B000800380240893B0008F6 -:10245C00993B0008B13B0008114550FE4FF48070C9 -:10246C00FEF734FB0120FEF749FBFEF70BFC0020C6 -:10247C00FEF7A6FA01214FF48040FEF75DFBBB4846 -:10248C00FFF7B4FA3046FEF73CFE00240125B748AE -:10249C0031468DF815408DF816408DF8174004958F -:1024AC008DF81450FEF7EDFD2246B0482946FEF794 -:1024BC003BFEAE48FFF77EFAAD4C04F1300954F800 -:1024CC00040CFFF793FA3046FEF71BFE236854F812 -:1024DC00040C4FF000083146022504938DF814804B -:1024EC008DF815508DF817800C34FEF7CAFD54F892 -:1024FC00100CFFF75FFA4C45E1D14FF48040414698 -:10250C00FEF71AFB4FF4807000F088FD002800F0F5 -:10251C00CC804FF4807000F08BFDFFF753FC954896 -:10252C0000F004FE0220FEF7BFFC9349884202D063 -:10253C000220FEF7A9FC914800F0EFFD0220FEF707 -:10254C00B3FC00F0F9FD00F0E0FD4FF4804000F02A -:10255C0065FD90B18A4800F0E9FD4FF4804000F031 -:10256C0067FD082000F064FD102000F061FD2020C4 -:10257C0000F05EFD402000F05BFDFFF755FBFFF720 -:10258C0037FB01214FF48050FEF7D6FA7748FFF75E -:10259C002DFA01200146FEF7B7FA05210A467348C9 -:1025AC00FEF7C7FD714806210522FEF7C2FD052284 -:1025BC006E480721FEF7BDFD00240225C0236B48A1 -:1025CC0039468DF80C508DF80D5002938DF80E4055 -:1025DC008DF80F40FEF755FD2023644839468DF8E1 -:1025EC000F5002930125FEF74CFD102339465F482E -:1025FC0002938DF80C508DF80F50FEF742FD614898 -:10260C00FEF7A8FC4FF48273ADF812304FF4007350 -:10261C00ADF81A305B4807233146ADF82030ADF8E1 -:10262C001040ADF81440ADF81640ADF81840ADF8B8 -:10263C001C40ADF81E40FEF7B7FD29465148FEF789 -:10264C00D2FD4A48FFF7B6F9FFF7ACFBAB20FFF71A -:10265C006DFCFFF77DFB642000F0DAFC21464FF4A3 -:10266C008050FEF769FAFFF7F7FD2846FEF71CFCD1 -:10267C0025460490E8B2FFF77DFB98BB70550135F9 -:10268C00042DF7D1049D3DB13F4800F046FD28468E -:10269C0000F052FD00F039FD01202946FEF7F4FB55 -:1026AC00002C3DD03948FFF70FFE2846FEF78EF977 -:1026BC00012814D12846FEF793F94FF40060FEF779 -:1026CC002DFC18B1324800F031FDB9E0314C00F06E -:1026DC000EFD002800F00481013CF8D1B0E000F0C0 -:1026EC00BDFC1CE7735D042B0ADD2B4800F01EFDBE -:1026FC00049800F021FD01200021FEF7C5FB0FE03E -:10270C000133DBB2042B7355B9DD244800F005FD11 -:10271C00284600F011FD224800F008FD0124AEE728 -:10272C00FFF79AFD1F4B0344012B01D91E48BAE752 -:10273C001E4800F0F2FC0020FEF7B6FB00F0FCFC9B -:10274C0000F0E3FC802000F069FC18B1802000F060 -:10275C006FFC73E04FF4003000F060FC044638B3BB -:10276C004FF4003000F064FC04E100BF00000240B4 -:10277C0054320008E93B000870864156253C00089D -:10278C003A3C000800300140A83C0008014550FECE -:10279C00CE3B0008A0860100673C0008933C000873 -:1027AC009E3C0008EA44DFFF024550FED43C000882 -:1027BC000120FFF7DFFAD8B12046FFF7DBFAB8B1FA -:1027CC00B74800F0B3FC41F288340120FFF7D2FA8D -:1027DC00002800F08B800020FFF7CCFA002800F0D6 -:1027EC0085804FF47A7000F013FC013CEDD1C1E010 -:1027FC000020FFF7B9F808B1AA4B00E0AA4B0CCBAC -:10280C00013301D0013271D1A84800F08FFCB1E046 -:10281C00A74800F08BFC082000F00AFC102000F008 -:10282C0007FC202000F004FC402000F001FC0024F8 -:10283C00042000F0FDFB002C40F008819D4832E79D -:10284C00FFF7A6F8002847D0002001A99A4DFEF703 -:10285C0083FA344600203946FEF7E2FA0FCD0FC456 -:10286C000FCD0FC495E80F0084E80F009DF80920E8 -:10287C0010A901EB82039DF80BE053F8343C40F2B5 -:10288C006D1101FB0E3303EB9E030EF1010E1EF0D6 -:10289C00030F02D1022A88BF01339DF80A409DF82C -:1028AC000410013C2344182404FB03149DF8051068 -:1028BC003C2202FB04149DF80610032002FB0414B6 -:1028CC00FEF7F2FA01190320FEF7DEFA04200021CC -:1028DC00FEF7DAFA00F0C2FB784800F027FC1EE69F -:1028EC00774800F023FCFFF76DFAF5E77D20FEF743 -:1028FC006BF970B94FF4007000F090FB48B9714857 -:10290C0000F014FC082000F093FB102000F090FB6A -:10291C0085E07D20FEF758F910B16B4800F006FCFD -:10292C004FF4007000F07AFB10B1684800F0FEFB29 -:10293C004FF4007000F07CFB102000F06FFB48B1EE -:10294C00634800F0F3FB082000F072FB102000F04D -:10295C006FFB12E0082000F061FB20B15D4800F035 -:10296C00E5FB102003E05C4800F0E0FB082000F0E1 -:10297C006AFB54E0594800F0D9FB594800F0D6FBEB -:10298C004FF40010FFF7ECFA10B1022828D809E038 -:10299C00202000F04DFB402000F04AFB802000F08E -:1029AC0052FB1DE0082000F04EFB102000F04BFB0A -:1029BC00202000F033FB20B94A4800F0B7FB202060 -:1029CC0009E0402000F02AFB00287FF421AF4648A4 -:1029DC0000F0ACFB402000F036FB4FF400701AE026 -:1029EC00012425E7414800F0A1FB082000F020FB62 -:1029FC00102000F01DFB202000F01AFB402000F0FE -:102A0C0017FB2046FFF7ACFA022840F0F4800820B0 -:102A1C0000F019FB102000F016FB00F034FB022034 -:102A2C0000F0FCFA90B1042000F0F8FA04460028FB -:102A3C00D8D02F4800F07AFB042000F0F9FA0220DD -:102A4C0000F0F6FA4FF4004000F0F2FA4FF40050A8 -:102A5C0000F0E4FA04464FF4805000F0DFFA400036 -:102A6C0040EA84044FF4006000F0D8FAE4B220434A -:102A7C00431C03F0FF03013B4FF40060062B5CD8B2 -:102A8C00DFE803F03C383C043C383C0000F0D0FA62 -:102A9C004FF4805000F0CCFA4FF400502EE000BF01 -:102AAC00E03C00080040000800000108123D00084E -:102ABC003C3E0008034550FEFC310008D93B0008A1 -:102ACC00DB3E0008253D0008363D00084E3D000861 -:102ADC006E3D0008843D00089A3D0008F03E000859 -:102AEC00B03D0008CA3D0008033E0008AC3E00089B -:102AFC00763E000800F09CFA4FF4805000F0A3FAE8 -:102B0C0045F25550FDF734FF0420FDF737FF40F632 -:102B1C00FF70FDF739FF0020FDF72AFF4FF48050BE -:102B2C000121FDF78FFEFDF73DFFFDF733FF002080 -:102B3C00FEF71AFF68B9354B0CE000F079FA4FF448 -:102B4C00805000F075FA4FF4005000F071FA3048E4 -:102B5C00A9E5304B5D6830481E6800F0DEFA284667 -:102B6C0000F0EAFA00F0D1FA00F0CFFA00239A0054 -:102B7C0002F16042013302F561424FF0FF31082B44 -:102B8C00C2F88010C2F88011F1D1244B00241C63D0 -:102B9C005C639C631C645C64FDF72AFF2048012184 -:102BAC00FDF7D6FF21461E48FDF7D2FFF12001218B -:102BBC00FDF7DAFF2146F120FDF7D6FF0120014693 -:102BCC00FDF7DEFF21460120FDF7DAFF1548012154 -:102BDC00FDF7E2FF21461348FDF7DEFF1248012105 -:102BEC00FDF7E6FF10482146FDF7E2FF63B64FF014 -:102BFC00FF3EB5462847042000F01AFA022000F0E8 -:102C0C0017FA4FF4004000F01EFA1FE7000001080D -:102C1C00044550FE00400008C73E00080038024042 -:102C2C0000106022FFC9FE36335907000748084AD6 -:102C3C0008B50849121AFDF701FA0748074A00219E -:102C4C00121AFDF704FAFFF769FB00BF0000002021 -:102C5C00200000205C3F00082000002048000120DC -:102C6C002DE9F04FDDE909AB0E469C4603783B4954 -:102C7C000746C95C01300D07F8D42B2B05D02D2B42 -:102C8C0004D107464FF0FF3301E0074601233978A2 -:102C9C0030290ED17978782905D132F0100103D181 -:102CAC000237102207E00AB1082A04D101370822A2 -:102CBC0001E002B90A2200200021B84617F8015B96 -:102CCC00A5F13004E4B2092C0DD9A5F14104192C5D -:102CDC0002D8A5F1370405E0A5F16104192C30D810 -:102CEC00A5F15704E4B294422BDA4FEAE27900FBE7 -:102CFC0009F502FB0155A0FB0201E4B229440019BD -:102D0C004FF0000541EB0501BCF1000FD5D0012BB4 -:102D1C0006D182457BEB0104CFDA50465946CCE70D -:102D2C005C1CCAD1DDE90B89444261EB4105444589 -:102D3C0075EB0909C1DADDE90B01404261EB410198 -:102D4C00BBE70EB1C6F80080DD1700FB05F203FBF4 -:102D5C000122A0FB03011144BDE8F08F4C120008C6 -:102D6C002DE9F04F91B01F9E0C46310691468046DE -:102D7C00DDE91A231E9D04D5CDE902234FF0000C8A -:102D8C001BE0002A73F100071046194602DA504284 -:102D9C0063EB4301002A73F1000ACDE9020107DB62 -:102DAC00B70708D416F0040C07D04FF0200C04E041 -:102DBC004FF02D0C01E04FF02B0C16F0400708974C -:102DCC0014BF704F704FDDE902AB079700271C98BA -:102DDC000137C117CDE90401DDE904230AA93944FF -:102DEC00099150465946CDF804C0FEF7A9FA079848 -:102DFC000999835C504601F8013C5946DDE90423EE -:102E0C00FEF79EFA82468B465AEA0B02DDF804C0A6 -:102E1C0001D0162FDBD116F0080239461DD01C9BB1 -:102E2C00082B02D0102B0ED01CE0DDE902AB1DB933 -:102E3C005AEA0B0B13D004E05AEA0B0B12D0BD422A -:102E4C0010DC7B1C0FE0DDE902AB5AEA0B0B09D05E -:102E5C0008982B4648B1582208E02B4606E02A4633 -:102E6C00012303E02B46002200E078221CF1000B2A -:102E7C001D98C7EB030A18BF4FF0010BBB42A8BF4C -:102E8C001F46C01B2AEAEA7ACBEB00000AB10227E4 -:102E9C0000E01746C71B16F0010027EAE77701D0C0 -:102EAC007F4205E0F60603D5013504BFBA44074658 -:102EBC003D46002D0BDD2068013D461C4E453CBFB8 -:102ECC00202608F80060206801302060F1E727EA2E -:102EDC00E7703F1ABBF1000F08D02068451C4D4528 -:102EEC0038BF08F800C02068013020608AB1206823 -:102EFC00451C4D453CBF302508F800502568681C22 -:102F0C0002354D45206038BF08F800202268013298 -:102F1C002260BAF1000F0CD022680AF1FF3A501C63 -:102F2C0048453CBF302008F802002268013222607C -:102F3C00EFE7DDE902AB5AEA0B0B10D17BB919466E -:102F4C000DE0236801395A1C4A4505D201F12805C8 -:102F5C0068462A5C08F803202368013323600029A3 -:102F6C00EFD15FB1236801375A1C4A453CBF202280 -:102F7C0008F80320236801332360F2E711B0BDE8A1 -:102F8C00F08F00BF233F0008343F00081EF0040FF1 -:102F9C000CBFEFF30880EFF30980FEF7BFBF70475B -:102FAC00032970B505460C4606D98E083146FDF747 -:102FBC0035FCB6003544A41B14B9FDF73DFC70BDBF -:102FCC0000231846EA5C0133A34242EA0020F9D1FF -:102FDC00FDF71EFC70BD2DE9F0410733DE08079BA1 -:102FEC004FF0120808FB02320731069F054602EB30 -:102FFC00D1080024BC4209D0122000FB04802946D1 -:10300C003246FDF71BF801343544F3E7BDE8F08197 -:10301C000623584301387FF4FDAF704710B50446C2 -:10302C000020FDF741FF20420CBF0020012010BD05 -:10303C0010B504460020FDF737FF20EA04010020FC -:10304C00FDF722FF10BD10B504460020FDF72CFF44 -:10305C0040EA04010020FDF717FF10BD1FB501A8C1 -:10306C00FEF78FF80723029301A803230393FDF7C0 -:10307C00E7FD6846FEF78BF869460020FDF724FE55 -:10308C0005B05DF804FB08B5FEF7E6FC0020FEF782 -:10309C005DFC0020FEF75AFC0020FEF757FCFEF703 -:1030AC00BFFCFDF765FBFEF72BFC70B50646FEF783 -:1030BC00D3FC8020FEF74AFCA82420BA90FAA0F09A -:1030CC00C0B2FEF743FC1125705DFEF73FFC15F115 -:1030DC00FF35F9D20020FEF739FC013C06F112064F -:1030EC00EBD12046FEF732FCFEF79AFC70BD38B5EA -:1030FC0000242546E0B2FEF73DFEA0400134054316 -:10310C00042CEDB2F6D1284638BD08B50D20FEF7DB -:10311C0007FC0A20FEF704FC08BD10B5441E14F889 -:10312C00010F10B1FEF7FCFBF9E710BD08B5FFF776 -:10313C00F4FFFFF7EAFF08BD1FB530238DF804300C -:10314C0078238DF80530072399000F228A4002401E -:10315C00CA40092A01D8303202E00F2A02D857326D -:10316C00D2B200E0202201A9CC1A13F1FF33627213 -:10317C00EAD2002308468DF80E30FFF7CEFF04B0DC -:10318C0010BD70B504460D464FF480500121164613 -:10319C00FDF7D2FCFEF706FE0520FEF7C7FEA920C0 -:1031AC00FEF7C4FEC307FAD4FEF7D2FDFEF7FAFD14 -:1031BC000320FEF7BBFEC5F30740FEF7B7FEC5F3D1 -:1031CC000720FEF7B3FEE8B2FEF7B0FE2644B44289 -:1031DC0005D0A920FEF7AAFE04F8010BF7E7FEF7CD -:1031EC00B7FD4FF480500021FDF7A6FC70BD000028 -:1031FC00000000001F0000003B0000005A0000000F -:10320C007800000097000000B5000000D40000001A -:10321C00F300000011010000300100004E0100001D -:10322C0000000008004000080080000800C00008F2 -:10323C000000010800000208000004080000060855 -:10324C000F3F00080008024008000000143F00086F -:10325C000000024004000000173F00080008024074 -:10326C00400000001E3F0008000002400200000069 -:10327C0000000240010000000000080010001800CF -:10328C002000280030003800000000000060000022 -:10329C0003001800000000000000006000000300A4 -:1032AC00180000000000000000600000030018007F -:1032BC000000000000000060000003001800000087 -:1032CC00000000000060000003001800000000E097 -:1032DC0003001F60F800C30718C0070000F80FC0F8 -:1032EC007F60FE03F31F18F01F00000C1860C06015 -:1032FC0003061B30181830000006303080E1010C3A -:10330C000F60180C60000003601800E3001807C081 -:10331C001806C0008001400C0062001003801803E6 -:10332C0080008001C00C006600300380190380010E -:10333C008001C0FCFF670030038019FFFF01800192 -:10334C00C0FCFF630030038019FFFF008001C00C3C -:10335C00006000300380190300008001C00C006085 -:10336C0000300380190300008001400C0062001043 -:10337C000380180380008003601800C3001806C087 -:10338C001806C000800730308081010C0C60180CCE -:10339C006000800D1860C00003061830381830002B -:1033AC0080F90FC07F00FE03F01F70F01F0080E15A -:1033BC0003001F00F800C00760C007008001000078 -:1033CC000000000000000000000080010000000070 -:1033DC000000000000000000800100000000000060 -:1033EC000000000000008001000000000000000050 -:1033FC000000000080010000000000000000000040 -:10340C000000FFFFFFFFFFFFFFFFFFFFFFFF0100BB -:10341C00000000000000000000800100000000001F -:10342C00000000000080010000000000000000000F -:10343C00008001000000000000000000008001007E -:10344C0000000000000000000080010000000000EF -:10345C00000000000080FFFFFFFFFFFFFFFFFFFFEA -:10346C00FFFF7C00FE00FF01C701C701C701C701B8 -:10347C00C701C701C701C701C701C701C701C70100 -:10348C00FF01FE007C0038003C003E003E0038008E -:10349C003800380038003800380038003800380060 -:1034AC0038003800FE00FE00FE007C00FE00FF012C -:1034BC00C701C701C001C001E000F00078003C006A -:1034CC001E000E000F000700FF01FF01FF017C0032 -:1034DC00FE00FF01C701C701C001C001F800780060 -:1034EC00F800C001C001C001C701C701FF01FE0007 -:1034FC007C00E000E000F000F000F800F800F800BC -:10350C00FC00EC00EE00E600FF01FF01FF01E00013 -:10351C00E000E000E000FF00FF00FF0007000700F4 -:10352C0007007F00FF00FF01C701C001C001C701F8 -:10353C00C701C701FF01FE007C007C00FE00FF01FB -:10354C00C701C701070007007700FF00FF01C70193 -:10355C00C701C701C701C701FF01FE007C00FF01C5 -:10356C00FF01FF01E000E000700070007000380007 -:10357C003800380038001C001C001C001C001C000B -:10358C001C007C00FE00FF01C701C701C701C70179 -:10359C00FE007C00FE00C701C701C701C701C701BF -:1035AC00FF01FE007C007C00FE00FF01C701C7018B -:1035BC00C701C701C701FF01FE01DC01C001C00149 -:1035CC00C701C701FF01FE007C00000000000000E5 -:1035DC0000007C00FE00FF01C701C701F001FC01E7 -:1035EC00CE01C701C701E701FF01DF01CE010700D2 -:1035FC00070007000700E700F701FF01CF01C70133 -:10360C00C701C701C701C701C701CF01FF01F701FE -:10361C00E70000000000000000007C00FE00FF013D -:10362C00C701C7010700070007000700C701C70152 -:10363C00FF01FE007C00C001C001C001C001CE0131 -:10364C00DF01FF01E701C701C701C701C701C701BE -:10365C00C701E701FF01DF01CE01000000000000FF -:10366C0000007C00FE00FF01C701C701C701FF017C -:10367C00FF010700C701C701FF01FE007C00E0004D -:10368C00F000F8003800FE00FE00FE0038003800A4 -:10369C00380038003800380038003800380038005E -:1036AC003800456E746572696E67207374616E6460 -:1036BC00627900206973206F757473696465207377 -:1036CC00797374656D20666C61736800484152446F -:1036DC00204641554C5400446573636C656E200064 -:1036EC000A4669726D6C656E20000A5873756D2000 -:1036FC00000A496E76616C6964206669726D776147 -:10370C007265206465736372697074696F6E2100F1 -:10371C00436865636B73756D6D696E67206669725E -:10372C006D776172652075706461746500496E76A1 -:10373C00616C6964206669726D77617265204352B1 -:10374C004320696E2053504920666C6173682100D8 -:10375C0065726173655F6F6C645F6669726D7761CA -:10376C007265004F6C6420576F726C6420666972CE -:10377C006D776172652062617365006661696C6565 -:10378C006420746F20657261736520736563746F58 -:10379C0072200077726974655F6E65775F66697217 -:1037AC006D77617265006661696C656420746F2069 -:1037BC007772697465206164647265737320005755 -:1037CC0065277265206465616400436865636B738B -:1037DC00756D6D696E672000206279746573004E9B -:1037EC00657720576F726C64206669726D776172B1 -:1037FC00652073797374656D5F666C6173685F6265 -:10380C00617365004F6C6420576F726C642066693D -:10381C00726D776172652073797374656D5F666C18 -:10382C006173685F6261736500436865636B737590 -:10383C006D202D2077616E746564200020676F7495 -:10384C0020004F757220696E7465726E616C206613 -:10385C006C61736820636F6E74656E747320617233 -:10386C0065206261642028636865636B73756D20E5 -:10387C006661696C656429212054686973206973D9 -:10388C00207265616C6C7920626164210000000615 -:10389C00830100000000000000000000000683010E -:1038AC00000000000C000000000000068301000076 -:1038BC0000000C00000000D878369B79C0E3D90CCE -:1038CC008C67DB3C1BF8FD7EBFFDE0F7FB1FC6EFF2 -:1038DC00FB7E1F98CD66B3CD60369B19C66C1866FF -:1038EC000398FD66B3FD60309B19C66F186603988C -:1038FC00FD66B3FD60309B19C36F186603980D66A7 -:10390C00B30D60369B19C360186603F8FD7EBFFDCE -:10391C00ECF79B19C36F187E03D878369B79CCE3F0 -:10392C0099998167183C0318000000000000008082 -:10393C000100000000180000000000000000000062 -:10394C000000001800000000000000000000000053 -:10395C0000000C00060000000060003000001800A1 -:10396C000300000000C000180000308001000000BF -:10397C000080010C000060C0000000000000030685 -:10398C000000C06000000000000006030000803151 -:10399C000000000000008C010000001B0000000073 -:1039AC000000D8000000000E0000000000007000B5 -:1039BC000000000E00000000000070000000001B62 -:1039CC00000000000000D800000080310000000062 -:1039DC0000008C010000C060000000000000060325 -:1039EC00000060C0000000000000030600003080F2 -:1039FC00010000000080010C000018000300000012 -:103A0C0000C0001800000C00060000000060003030 -:103A1C00000000000000000000000000000000009A -:103A2C00000000000000000000000000000000008A -:103A3C00000000000000000000000000000000007A -:103A4C00000000000000000000000000000000006A -:103A5C00000000000000000000000000000000005A -:103A6C00000000000000000000000000000000004A -:103A7C00000000000080FFFF0100000000000000BB -:103A8C0000FEFFFF7F00000000000000E07F000050 -:103A9C00FE07000000000000FE010000807F000017 -:103AAC00000000801F00000000F80100000000F082 -:103ABC000100000000800F000000007C00000000EE -:103ACC0000003E000000000F000000000000F000AD -:103ADC0000008003000000000000C0010000E000B6 -:103AEC000000000000000007000070000000000053 -:103AFC000000000E0000180000000000000000187C -:103B0C0000000C0000000000000000300000060067 -:103B1C000000000000000060000003000000000036 -:103B2C00000000C0008001000000000000000080C8 -:103B3C0001C0000000000000000000000360000055 -:103B4C000000000000000000063000000000000033 -:103B5C00000000000C10000000000000000000003D -:103B6C00085341442057415443483A20005265734E -:103B7C006574205265676973746572200042726FB8 -:103B8C00776E206F75742072657365740053746161 -:103B9C007274696E67204C5345206F7363696C6C4B -:103BAC0061746F72004C5345206F7363696C6C6168 -:103BBC00746F7220646964206E6F74207374617208 -:103BCC0074005553422077616B657570006C6561AC -:103BDC0076696E67207374616E64627900205F5F32 -:103BEC005F5F5F5F202020205F5F0D0A2F5F20202A -:103BFC005F5F2F205F5F2F202F0D0A202F202F209B -:103C0C0020202F5F20205F5F2F0D0A2F5F2F202099 -:103C1C002020202F5F2F0D0A00426F6F746C6F6194 -:103C2C006465722076657273696F6E3A20004C6120 -:103C3C007374206669726D7761726520626F6F7440 -:103C4C002077617320737461626C653B20636C65D3 -:103C5C00617220737472696B657300537475636B56 -:103C6C0020627574746F6E20726567697374657207 -:103C7C0020697320696E76616C69642C20636C65B5 -:103C8C006172696E672E00427574746F6E20696480 -:103C9C002000697320737475636B21004275747412 -:103CAC006F6E2077617320707573686564206F6E1A -:103CBC0020626F6F742E20427574746F6E20636F68 -:103CCC00756E7465723A2000426F6F74206269746D -:103CDC00733A2000486F6C6420646F776E205550E7 -:103CEC00202B204241434B20666F72203520736598 -:103CFC0063732E20746F20666F7263652D626F6F15 -:103D0C007420505246004669726D77617265206965 -:103D1C00732065726173656400426F6F74696E67BE -:103D2C00206E6F726D616C6C79005761746368649E -:103D3C006F672063617573656420612072657365BC -:103D4C007400536F667477617265206661696C7577 -:103D5C00726520636175736564206120726573659B -:103D6C007400426F6F74206661696C65642C2073FB -:103D7C007472696B65203300426F6F7420666169E1 -:103D8C006C65642C20737472696B65203200426F11 -:103D9C006F74206661696C65642C20737472696B36 -:103DAC00652031004C6F6164696E67207265636FCA -:103DBC0076657279206669726D776172650046610D -:103DCC00696C656420746F206C6F6164207265632C -:103DDC006F76657279206669726D776172652C20D9 -:103DEC00737472696B65206F6E652E205472792026 -:103DFC00616761696E2E004661696C656420746F41 -:103E0C00206C6F6164207265636F766572792066D1 -:103E1C0069726D776172652C20737472696B6520A1 -:103E2C0074776F2E2054727920616761696E2E0051 -:103E3C004661696C656420746F206C6F61642072DC -:103E4C0065636F76657279206669726D77617265EC -:103E5C002C20737472696B652074687265652E20F2 -:103E6C00534144205741544348004F75722070729F -:103E7C006576696F7573206669726D7761726520FE -:103E8C00757064617465206661696C65642C206171 -:103E9C00626F7274696E67207570646174652E0050 -:103EAC004E6577206669726D776172652069732043 -:103EBC00617661696C61626C652100426F6F746937 -:103ECC006E67206669726D776172652040200072A2 -:103EDC00657475726E696E6720746F207374616E91 -:103EEC0064627900466F7263652D626F6F74696EE0 -:103EFC0067207265636F76657279206D6F64652ECD -:103F0C002E2E004261636B0055700053656C656327 -:103F1C007400446F776E00303132333435363738B5 -:103F2C00394142434445460030313233343536371B -:103F3C00383961626364656600286E756C6C2900A3 -:103F4C000000000001020304010203040607080933 -:103F5C00FFFFFFFF00A000000202000000C00401F0 -:103F6C00000000000000000210000000070000002C -:040000050800134C90 -:00000001FF diff --git a/bin/boot/boot_robert_bb2@1478015115.bin b/bin/boot/boot_robert_bb2@1478015115.bin deleted file mode 100755 index ad5cb98e67..0000000000 Binary files a/bin/boot/boot_robert_bb2@1478015115.bin and /dev/null differ diff --git a/bin/boot/boot_robert_bb2@1478015115.hex b/bin/boot/boot_robert_bb2@1478015115.hex deleted file mode 100644 index b548adf66e..0000000000 --- a/bin/boot/boot_robert_bb2@1478015115.hex +++ /dev/null @@ -1,1798 +0,0 @@ -:020000040800F2 -:1000000000AA0120E1250008DD2500081D1E0008CA -:10001000DD250008DD250008DD25000800000000C2 -:10002000000000000000000000000000DD250008C6 -:10003000DD25000800000000DD250008DD250008A2 -:10004000DD250008DD250008DD250008DD25000888 -:10005000DD250008DD250008DD250008DD25000878 -:10006000DD250008DD250008DD250008DD25000868 -:10007000DD250008DD250008DD250008DD25000858 -:10008000DD250008DD250008DD250008DD25000848 -:10009000DD250008DD250008DD250008DD25000838 -:1000A000DD250008DD250008DD250008DD25000828 -:1000B000DD250008DD250008DD250008DD25000818 -:1000C000DD250008DD250008DD250008DD25000808 -:1000D000DD250008DD250008DD250008DD250008F8 -:1000E000DD250008DD250008DD250008DD250008E8 -:1000F000DD250008DD250008DD250008DD250008D8 -:10010000DD250008DD250008DD250008DD250008C7 -:10011000DD250008DD250008DD250008DD250008B7 -:10012000DD250008DD250008DD250008DD250008A7 -:10013000DD250008DD250008DD250008DD25000897 -:10014000DD250008DD250008DD250008DD25000887 -:10015000DD250008DD250008DD250008DD25000877 -:10016000DD250008DD250008DD250008DD25000867 -:10017000DD250008DD250008DD250008DD25000857 -:10018000DD250008DD250008DD250008DD25000847 -:10019000DD250008DD250008DD250008DD25000837 -:1001A000DD250008DD250008DD250008DD25000827 -:1001B000DD250008DD250008DD25000861070008B1 -:1001C0006D070008DD25000800000000DD2500089F -:1001D000DD250008DD250008DD250008DD250008F7 -:1001E000DD250008DD250008DD250008DD250008E7 -:0801F000DD250008DD250008F3 -:0801F8009C0000000100000062 -:1002000053B94AB9002908BF00281CBF4FF0FF317D -:100210004FF0FF3000F03CB882B0EC462DE90050C2 -:1002200000F01EF8DDF804E002B00CBC704700BF1F -:100230002DE9F041904606460F461D46069C00F00B -:1002400029F808FB01FC8646A8FB002300FB05C536 -:10025000B21A2B4467EB0303C4E90023BDE8F08125 -:100260002DE9F8431D46174680468946089E00F052 -:1002700053F900FB05F3A0FB074507FB0137B8EB7B -:1002800004043D4469EB0505C6E90045BDE8F88373 -:10029000704700BF00292DE9F047C0F2A280002678 -:1002A000002BC0F298808C4690469E461546044628 -:1002B0000F46CBBB8A4256D9B2FA82F33BB1C3F1A7 -:1002C00020029F409D409C4020FA02F21743280CD8 -:1002D000220C1FFA85FEB7FBF0F100FB11770EFB35 -:1002E00001F342EA0747BB4207D97F1980F0018139 -:1002F000BB4240F2FE8002392F44FF1AA4B2B7FB82 -:10030000F0F300FB13770EFB03FE44EA0747BE45FC -:1003100006D97F1980F0EB80BE4540F2E880023BB1 -:1003200043EA0143002203E08B420FD90022134627 -:10033000341C4FF0000518BF0124604265EB4501F5 -:100340005840514000196941BDE8F087B3FA83F283 -:10035000002A40F08380804540F2CD808B42C0F07F -:10036000CA801346E4E712B90123B3FBF2F5B5FAEC -:1003700085F2002A3BD1781B4FEA154E1FFA85FC07 -:100380000122210CB0FBFEF80EFB18000CFB08F359 -:1003900041EA0047BB4208D97F1980F0B080BB42D8 -:1003A00040F2AD80A8F102082F44FF1AA4B2B7FBB7 -:1003B000FEF30EFB13770CFB03FC44EA0747BC4536 -:1003C00006D97F1980F09980BC4540F29680023BA7 -:1003D00043EA0843ACE752426FEA060663EB430385 -:1003E00061E740424FF0FF3661EB410158E795402D -:1003F000C2F1200107FA02F34FEA154ECF4024FA6A -:1004000001F194401FFA85FC1943B7FBFEF24FEA55 -:1004100011480EFB12770CFB02F348EA0747BB4278 -:1004200005D97F1971D2BB426FD9023A2F44FF1A06 -:1004300089B2B7FBFEF80EFB18770CFB08F041EA17 -:100440000743984206D95B1961D298425FD9A8F157 -:1004500002082B44181A48EA024292E7C2F1200728 -:1004600003FA02FE08FA02F5914028FA07F32CFA83 -:1004700007FCF84043EA0E0E08434FEA1E48070CFB -:100480001FFA8EFABCFBF8F908FB19CC0AFB09F13C -:1004900047EA0C4C614507D91CEB0E0C32D2614582 -:1004A00030D9A9F10209F444C1EB0C0C80B2BCFBB9 -:1004B000F8F308FB13CC0AFB03FA40EA0C418A4527 -:1004C00006D911EB0E0125D28A4523D9023B71448E -:1004D00043EA0943CAEB0101A3FB0589494503D35C -:1004E00003D19440444500D2013B002220E7013B68 -:1004F00016E7013901E701231AE7013B68E708F134 -:10050000FF3852E709F1FF39CEE7013A8FE708F1EA -:10051000FF389FE7013BDBE72DE9F043002B40D19B -:100520008A42044615464AD9B2FA82F30F464BB1C5 -:10053000C3F12006994000FA03F402FA03F5F040F3 -:1005400040EA0107290C260C1FFA85FEB7FBF1F0E3 -:1005500001FB10770EFB00F246EA07439A4207D9E7 -:100560005B1980F0EA809A4240F2E78002382B441F -:100570009A1AA4B2B2FBF1F301FB13220EFB03FEA5 -:1005800044EA0242964506D9521980F0DA8096452F -:1005900040F2D780023B43EA004000263146BDE8E6 -:1005A000F0838B4244D8B3FA83F6002E45D18242C1 -:1005B00040F2BF808B42C0F0BC803046EEE712B9FB -:1005C0000125B5FBF2F5B5FA85F2002A7BD14A1B6D -:1005D0002F0C1FFA85FE0126230CB2FBF7F007FB58 -:1005E00010220EFB00FC43EA02418C4507D9491951 -:1005F00080F0A1808C4540F29E8002382944CCEBEB -:100600000101A4B2B1FBF7F307FB13110EFB03FECC -:1006100044EA0144A64506D9641980F09080A645B5 -:1006200040F28D80023B43EA00403146BDE8F08352 -:10063000002630463146BDE8F083C6F12005B340C0 -:1006400002FA06F701FA06F4EA40E94020FA05F555 -:100650001A4325434FEA124C4FEA154E93B2B1FBB1 -:10066000FCF80CFB181103FB08F44EEA01418C4224 -:1006700006D9891869D28C4267D9A8F102081144B9 -:10068000091BADB2B1FBFCF40CFB141103FB04FE1F -:1006900045EA01439E4505D99B1854D29E4552D93F -:1006A000023C134444EA0844CEEB0303A4FB07894D -:1006B0004B4503D351D1B04040454ED20026601E79 -:1006C0003146BDE8F083C2F12006954001FA02F3FD -:1006D00000FA02F42F0CF140F0401FFA85FEB1FB46 -:1006E000F7F6184307FB16110EFB06F24FEA104C03 -:1006F0004CEA01439A4205D95B1929D29A4227D97B -:10070000023E2B449B1A80B2B3FBF7FC07FB1C3361 -:100710000EFB0CF140EA0343994206D95B1919D24A -:10072000994217D9ACF1020C2B445A1A4CEA0646EE -:1007300052E7012032E7013861E7013818E7013B51 -:1007400071E7013B27E7013CACE708F1FF3897E789 -:10075000013ED7E70CF1FF3CE7E7204600261DE706 -:10076000014800F0D6BF00BFE42B0008014801F0AB -:1007700038B800BFE42B0008014800F0F9BD00BF05 -:10078000E42B000837B50024012002F007FA01909D -:100790002546E0B200F046F801AA08B910551DE060 -:1007A000135D042B0BDD174800F03CF9019800F0B5 -:1007B00040F90120002102F0E1F9002020E001339E -:1007C000DBB2042B135509D90F48012500F012F9AB -:1007D000204600F02EF90D4800F024F90134042CD5 -:1007E000D7D1019C3CB10A4800F004F9204600F042 -:1007F00020F900F08FF80120214602F0BFF92846C9 -:1008000003B030BDC1650008ED650008F86500085B -:100810000266000808B500F077FB80F00100C0B266 -:1008200008BD000008B50C22044B02FB003000F0AC -:100830008DFC80F00100C0B208BD00BF842C000810 -:1008400010B5002401F094F8054820440C34017AD6 -:1008500000F062FC302CF7D1BDE8104001F089B8FF -:10086000842C0008044BDA691106FBD59862DA691A -:100870001206FCD5704700BF0048004070B5214DFE -:100880008AB02148082101F075F828464FF48021EC -:100890001E4C01F06FF82B68642623F0010394E8E6 -:1008A00007002B600C348DE8070000216846022207 -:1008B0000B4600F013FC03AB94E8070083E8070045 -:1008C00000211846022200240B4600F007FC06A86F -:1008D00002F07AF8089821460D4A70430023FFF78A -:1008E0008FFCC0F3420320F00F000004000C1843FB -:1008F0000D23B0FBF6F0E8606C60AC602B600AB0D2 -:1009000070BD00BF00480040000C0240B42C00083D -:1009100040420F002DE9F043334D83B02C6800AF07 -:1009200004F12E0304F1270823F00703ADEB030DB8 -:1009300050238DF8003021236E460DF106028DF80C -:10094000013000238DF8023003238DF8033004F1C9 -:1009500021035BBAADF80430244B03F11C0153F8BA -:10096000040B8B4242F8040BF9D11B7804F12309E4 -:1009700006F123001E491370224602F01FF931468A -:100980004A46002001F070FD07F1080304F5927457 -:10099000314643F8040D42461868FE23B4FBF3F3D6 -:1009A000434446F80900073323F00703ADEB030D7A -:1009B00068466E4601F030FD044655206C44FFF752 -:1009C00051FFA64207D016F8010B552808BF00209A -:1009D000FFF748FFF5E75520FFF744FF00230C37EA -:1009E0002B60BD46BDE8F08338000020CC2C000809 -:1009F0003C00002010B5441E14F8012F7AB10849BC -:100A00000B68FF2B0BD80A2A02D1FFF783FFF3E70D -:100A10000D2A1FBF581C5B1808601A71ECE710BD47 -:100A20003800002008B5FFF7E5FFBDE80840FFF7F4 -:100A300071BF1FB50C2201A901F044FD01A8FFF709 -:100A4000D9FF05B05DF804FB07B502A9012201F842 -:100A5000010D042000F096F903B05DF804FB0021BD -:100A600002200A4600F08EB910B540F2F51400F0ED -:100A7000B9F850B1013C04D10548FFF7D3FF204637 -:100A800010BD642001F010FDF1E7012010BD00BF92 -:100A90002E66000810B5074C43F2CD71064848F6A3 -:100AA000B803224601F056FD01462046BDE810403D -:100AB00000F0E4B83C010020E92C000810B500F07B -:100AC00099F8FFF7E7FF08B9154816E0002400F091 -:100AD00039F9FFF7C4FF0120FFF7B6FFFFF7C4FFA6 -:100AE00078B1002103200A4600F04CF90D48FFF7C9 -:100AF00081FF2046FFF79DFF0B48BDE81040FFF740 -:100B000091BFFFF7C7FF0028DED001340B2CE2D1E4 -:100B10000648FFF787FFBDE81040FFF7A0BF00BF02 -:100B2000516600086C660008876600089166000838 -:100B300008B5FFF799FF0120FFF786FFFFF794FF45 -:100B4000002103200A46BDE8084000F01BB9000060 -:100B50002F2307B54A1C58430B4B00EB52001A7861 -:100B6000B0FBF1F0C0B282420AD002A91870042290 -:100B700041F8040D012000F005F90220FFF764FFA1 -:100B800003B05DF804FB00BF0000002007B502A918 -:100B9000042241F8040D012000F0F4F80320FFF7CF -:100BA00053FF03B05DF804FBFFF759BF0C4B014442 -:100BB00030B51C68884207D022689568AD07FCD51F -:100BC00010F8015B1573F5E71B681A68936813F456 -:100BD000C05FFBD10A20BDE8304001F065BC00BF1A -:100BE000342C0008024B1868403000F0AFBA00BF48 -:100BF000342C000838B5204B012200211C6804F178 -:100C00002005284600F03CFA0021284600F052FA60 -:100C100004F1380000F094FA04F1400000F090FA7A -:100C2000012104F12C000A4600F02AFA002104F107 -:100C3000080001220B4600F051FA0021012204F1C4 -:100C400014000B4600F04AFA6068012101F06EFFC3 -:100C50006068002101F06AFF94E8030000F08AFE5A -:100C600023684CF207321A604FF4B8525A601A687F -:100C700042F040021A6038BD342C00082B4B2DE99D -:100C8000F7431C68814601208846E36A04F1200787 -:100C900004F12C060093236B019301F009FC384604 -:100CA000012100F007FA0021304600F003FA01208C -:100CB00001F0FEFB684600F049FA054620B11C48E9 -:100CC0000025FFF7AFFE2CE030460121383400F05C -:100CD000F1F9012001F0ECFB204600F037FA06465E -:100CE00008B114481BE0684600F030FA054608B920 -:100CF000114814E0012001F0DBFB48464146FFF7B4 -:100D000055FF3846314600F0D5F90C480921FFF768 -:100D10004DFF204600F01AFA054610B90848FFF7C3 -:100D200081FE284603B0BDE8F08300BF342C0008E4 -:100D3000B0660008CC660008E6660008B6640008E5 -:100D40000267000808B5022001F0B2FB0A4B1B68DD -:100D500093F8483043B10948FFF764FE012000F0E2 -:100D6000D3FD022001F0A4FB0548FFF75BFE012044 -:100D7000BDE8084000F0C2BD342C0008236700081D -:100D80003F6700080F4BF7B51C6802AD0F46012105 -:100D9000203405F8010D1646204600F08BF964203A -:100DA00001F082FB28460121FFF700FF1EB1384603 -:100DB0003146FFF7FBFE2046002100F07BF903B02F -:100DC000F0BD00BF342C0008F0B50746ADF2044D6D -:100DD0000E460C460025B919B4F5806F6846A1EBA4 -:100DE00004010DD94FF48062A4F5806400F0CCF8C2 -:100DF000284669464FF4806201F036FB0546EAE773 -:100E0000224600F0C1F869462246284601F02CFB34 -:100E10000DF2044DF0BD08B5202001F09FFD002823 -:100E2000FAD108BD08B5022001F098FD0028FAD0DB -:100E30000220BDE8084001F09BBD00B58DB06846BA -:100E400001F015FD0023684600934FF480730A9368 -:100E500035230B9301F03CFDFFF7E4FFFFF7DBFFC9 -:100E60000DB05DF804FB000030B5002495B02448B7 -:100E7000022100F07FFD23480021032220440C348E -:100E80000B4600F02BF9482CF5D101A8002401F005 -:100E9000E5FC102301A802940193172303944FF457 -:100EA0004075059304940694079401F0EFFC01202B -:100EB00001F032FDFFF7C1FF08A801F0D8FC66235E -:100EC00008A808941393129501F002FDFFF7AAFFFA -:100ED00008A801F0CCFC992308A808941393129554 -:100EE00001F0F6FCFFF79EFF4CF2503001F0DCFA07 -:100EF000FFF7A3FF0248022100F046FD15B030BD08 -:100F0000001000A0C064000830B502218DB01C485C -:100F100000F030FD022001F013FD684601F0A7FC4F -:100F20004FF08063684600934FF0407304934FF492 -:100F300040730A93AF230B9301F0CAFCFFF772FFD3 -:100F400001F006FD054601F003FD044601F000FD39 -:100F5000240244EA004040EA0504FFF75CFF094B25 -:100F60009C4204D020460024FFF763FD00E00124EA -:100F70000348022100F008FD20460DB030BD00BF3F -:100F8000001000A020BB190070B515468CB00446B7 -:100F90000E461B48022100F0EDFC681E01F0D0FC5B -:100FA000684601F064FC4FF0806368462544009376 -:100FB0004FF0407304930A2305934FF44063099361 -:100FC0004FF440730A934FF4005308930B230B9391 -:100FD00001F07EFC304601F0ABFCAC4204D001F0E5 -:100FE000B7FC04F8010BF8E7022001F0C1FCFFF7A1 -:100FF00012FF0348022100F0C7FC0CB070BD00BF17 -:10100000001000A0034A136B23F4FF6323F00703CF -:10101000136370470038024000F13F4000F57E0046 -:10102000C0F387200A280AD8054A135C591C1154BA -:101030002BB9044901230A6B834013430B637047A8 -:10104000F48900200038024000F13F4000F57E00A6 -:10105000C0F387200A280DD8074A135C53B1013B1F -:10106000DBB2135433B9054901230A6B834022EAEA -:1010700003030B63704700BFF4890020003802406F -:10108000436813B500930123044600688DF80430CB -:1010900000238DF805208DF807308DF80610FFF736 -:1010A000BBFF2068694601F063FB2068FFF7CCFFB7 -:1010B00002B010BD38B5037A04460D460BB981F075 -:1010C00001052068FFF7A8FF2068A1882A4601F0E3 -:1010D0008DFB2068BDE83840FFF7B6BF13B5044666 -:1010E00040688DF80730009002208DF805208DF8BB -:1010F000040020688DF80610FFF78EFFA27A2068A2 -:10110000218901F077FB2068694601F031FB2068F6 -:10111000FFF79AFF02B010BD002313B504460193F8 -:101120004368006800938DF80710FFF775FF20688B -:10113000694601F01DFB2068FFF786FF02B010BD75 -:10114000036813B10021FFF7E7BF704738B50446C5 -:101150000068FFF761FFA188206801F041FB0546A8 -:101160002068FFF771FF281C18BF012038BD1FB58C -:10117000836804460093C36868460193FFF7E0FF65 -:10118000636902A80293A3690393FFF7D9FF04B030 -:1011900010BD10B504460121083002220023FFF7DC -:1011A0009DFF04F11400012102220023BDE810403C -:1011B000FFF794BF0368DB6910B5044673B9C36ACF -:1011C00063B10A2001F074F92046FFF7D0FF2046F2 -:1011D000E36A01219847142001F06AF92046FFF7DD -:1011E000D8FF2046BDE8104000F0E0B910B5044635 -:1011F00000F04FFA48B10A2001F05AF9204600F0F9 -:1012000048FA80F00100C0B210BD012010BD30B519 -:10121000044687B0C36A00219847A36800210125CE -:1012200000936846E3680A468DF808500193FFF77B -:1012300027FF68460021FFF73DFF6369002103A8EF -:101240000393A3690A468DF814500493FFF718FF1F -:1012500003A80021FFF72EFF07B030BD10B50446EC -:1012600000F00AFA2368DB6933B9E36A4BB1204620 -:10127000BDE81040FFF7CBBF2046BDE81040FFF7A8 -:1012800076BF10BD10B50446FFF7E8FF2046BDE865 -:101290001040FFF78FBF00002DE9F3419846036827 -:1012A00004460E46DB6917460BB900205BE000F0F0 -:1012B000F0F9B8B9256814220021284601F089FC0C -:1012C0009DF828306F8020466B71089B2E71AB60B3 -:1012D000099B85F806802B6100F0ECF92368002259 -:1012E0009A6111E02046FFF7CDFF2046FFF77EFF11 -:1012F0000028DFD1D9E79A69B2F57A7F23DA013283 -:1013000002209A6101F0D4F8204600F0DFF90122B2 -:10131000236883F82020154A0192019A511E0191F9 -:101320001AB193F82020002AF7D1002283F8202058 -:10133000019A42B11D7D05F0FD02012ADBD16B1E31 -:101340005D425D4103E00025204600F0A8F92368D6 -:101350000022204683F82020FFF748FF10B92046DE -:10136000FFF790FF284602B0BDE8F08180841E00A0 -:1013700010B5044618B90E48A92101F015F800214E -:101380002422006801F025FC204600F0CBF8236AF7 -:101390002BB1002104F120000A46FFF771FEE36A39 -:1013A00023B12046BDE81040FFF731BF10BD00BF9C -:1013B0009567000810B5044618B90848B62100F032 -:1013C000F3FF00680368DB690BB9FFF7F3FE2368DE -:1013D0001A68D3690133D36110BD00BF9567000857 -:1013E00008B518B90748BF2100F0DEFF00680268A1 -:1013F000D36933B1013BD3611BB9BDE80840FFF7A6 -:101400002DBF08BD956700081FB50C46114618B9D9 -:101410000A4840F28D1103E023B908484FF4C77120 -:1014200000F0C2FF82888DE80A0000212346029165 -:101430000068FFF731FF04B010BD00BF95670008DA -:1014400013460122FFF7E0BF1FB50C46114618B93D -:101450000A484FF4D77103E023B9084840F2AF11AE -:1014600000F0A2FF82888DE80A0000230121029388 -:1014700023460068FFF710FF04B010BD9567000811 -:1014800007B502AB03F8012D0122FFF7DDFF03B022 -:101490005DF804FB03680022197583F8202070476B -:1014A00010B5046863882279C3F3090322B922784E -:1014B000012A18BF43F48063A268E468121BFF2A64 -:1014C00095BF120443F0FF7302F47F0243F48033AC -:1014D00098BF134309B143F4005342681268536044 -:1014E00010BD43681B681A6822F0D60210B51A6056 -:1014F0005A6842F480425A6008220368BDE81040EE -:101500001A70FFF7C7BF0368012110B50446586879 -:1015100001F000FB00212368BDE81040586801F08D -:10152000F9BA000043685A7B53B2002B30B506DA93 -:1015300002F00F021B4B1344C0221A7606E003F19F -:101540006043C02203F5614383F8002343689A7B1C -:1015500053B2002B06DA02F00F02124B1344C022E2 -:101560001A7606E003F16043C02203F5614383F875 -:10157000002350F8045F0C4C6B7B59B203F01F0240 -:101580000123490903FA02F244F82120AA7B51B24F -:1015900002F01F024909934044F82130BDE8304071 -:1015A000FFF7B1BFFCEC00E000E100E043682C492C -:1015B0009A688A42F0B5074687B004D92948512174 -:1015C000294A00F0E5FE29498A4205D91C7B34B13D -:1015D000012C07D000F0F2FE0124254603E0092487 -:1015E000102500E0022502A801F0EEF979686019E3 -:1015F000049A8B684343B2FBF3F603FB162303B94B -:10160000013E0F2E03D91748702100F0CDFE03C90B -:1016100000F0B0F97B684FF0000100221B68013D2B -:1016200066F307110192013C8DF804508DF8071004 -:1016300019688DF8054021F0010119600C491868FE -:1016400001401960019919619A60DA601A6842F0E4 -:1016500001021A60074A59680A405A6007B0F0BD93 -:10166000801A0600AE670008CB670008A08601005C -:10167000001F30FF008000F810B5044654F8043F06 -:1016800093E8030000F080F92046BDE81040FFF722 -:101690003ABF43681B689869C0F3C030704743681D -:1016A0001B681A6822F0D6021A605A6842F4804217 -:1016B0005A60704703685A790AB9012204E01A791E -:1016C0000AB9052200E006221A70704743681B68B9 -:1016D0001A6842F0D20210B51A60026814791CB977 -:1016E000196841F004011960117801290CD1528860 -:1016F000012CC2F309020CBF4FF08071002142F4AB -:1017000090320A435A6010BD0121BDE81040FFF736 -:10171000C7BE38B5436805461C6803681A78013AA5 -:10172000072A5BD8DFE802F0045A5A5A223B5A577C -:10173000A269910701D59A79A2621A793AB9A26988 -:1017400052060FD50522284601211A7008E0012A09 -:1017500008D1A269100605D50622284600211A7074 -:10176000FFF79EFEA369D9063AD525E0A269520784 -:1017700005D5DA681969501CD860636A8B54A3696F -:101780001B0603D528460021FFF78AFEA3695806E9 -:1017900026D528460121BDE83840FFF7A2BEA26940 -:1017A000910705D5DA681969501CD8608B5CA36273 -:1017B000A16911F0100109D0E3692846022143F024 -:1017C0001003E361BDE83840FFF764BEA3691A0661 -:1017D000DCD52846D8E7A369DB0701D400F0EEFD8D -:1017E00038BD43681B689A69D20503D5DA6942F4AB -:1017F0008072DA619A69510503D5DA6942F4806230 -:10180000DA619A69920503D5DA6942F40072DA6105 -:101810000321FFF766BE0000094B70B51C680D463A -:1018200006462046FFF7C6FD2A4631462046FFF70A -:1018300007FE05462046FFF7D3FD284670BD00BFD2 -:10184000182C0008094B70B51C680D46064620464A -:10185000FFF7B0FD2A4631462046FFF711FE054648 -:101860002046FFF7BDFD284670BD00BF182C0008BC -:1018700037B500F07DF81D4801210222FFF700FC7A -:101880001B4801210222FFF7FBFB1A48012102221B -:10189000FFF7F6FB00F06DF800200DF10701FFF7F0 -:1018A000BBFF9DF80750012D1DD114200DF107013C -:1018B000FFF7B2FFA8B1104C237813B101332370A6 -:1018C0000FE09DF80710142021F0060141F00201FD -:1018D0008DF80710FFF7B6FF18B10320257000F050 -:1018E000E7FD012000E0002003B030BD08650008DE -:1018F0001465000820650008FF89002001460148A2 -:10190000FFF7D8BB0865000801460148FFF7D2BBC6 -:1019100014650008B0F1804F08B503D20D4830219E -:1019200000F042FD0C4B98420ED903F580339842EB -:101930000CD9B0F1A04F0BD3084A094BB0F1204F9E -:1019400034BF1846104608BD064808BD064808BD05 -:10195000064808BDEC670008FFFF0040852A000824 -:101960006D2A00089D2A0008B52A0008552A00089B -:101970007047704710B50C46FFF7CCFF01210346B6 -:101980002046BDE81040184710B50C46FFF7C2FFCF -:10199000002103462046BDE81040184738B5104CDA -:1019A00005464FF080512046FFF7E4FF45B154E86B -:1019B000003F43F4807344E80032002A08D0F6E781 -:1019C00054E8003F23F4807344E80032002AF7D142 -:1019D00003484FF08051BDE83840FFF7D5BF00BF46 -:1019E00000700040024B5868C0F34000704700BFD1 -:1019F00000700040024A136843F008031360704708 -:101A000000700040B0F1006F10B504460BD20C48D6 -:101A1000FEF7F0FF2046FFF70CF80A48FFF702F840 -:101A20004FF0FF3010BD084B002053F8042BA242AA -:101A300002D81A68944202D301300B28F5D110BDA8 -:101A40000B680008146800082C6500082DE9F843AD -:101A50000446174699460E46C1B3FFF7D3FF054625 -:101A6000601E3044FFF7CEFF002D804631DB00289A -:101A70002FDB461B01361FB1002031464A46B847CE -:101A80002C4600F0D3FD44451EDCF32000F0E4FDBD -:101A9000114B002133F8140000F016FE09280AD07B -:101AA0000E48FEF7A7FF2046FEF7C3FFFEF732FF02 -:101AB00000F0CAFD0DE02FB1C5F1010031464A46E4 -:101AC0002044B8470134DEE700F0BEFD0120BDE848 -:101AD000F8830020BDE8F8835C6500082D680008E5 -:101AE0002DE9F0410F461646804600F09FFD3E442A -:101AF0003C46F32000F0B0FDB44216D0C7EB080519 -:101B0000254414F8011B284600F014FE0928F3D0E0 -:101B10000848FEF76FFF2846FEF78BFFFEF7FAFE38 -:101B200000F092FD0020BDE8F08100F08DFD012065 -:101B3000BDE8F0814568000808B545F2555000F051 -:101B40006BFE042000F06EFE40F6FF7000F070FEA9 -:101B5000002000F061FE4FF480500121BDE80840F4 -:101B600000F058BD08B500F071FEBDE8084000F077 -:101B700065BE08B57D2000F0E5FF003018BF0120EC -:101B800008BD4900FEF7E4BF2DE9F043012185B00F -:101B900005460020FEF7DCFF042000F051FB01A801 -:101BA000294600F0A7FB01A800F0B3FB08B93B48A9 -:101BB00014E00C353A48FEF735FF02992846FFF746 -:101BC00003F904463748FEF715FF2046FEF731FFBC -:101BD000FEF7A0FE039B9C4204D03348FEF722FF91 -:101BE000012057E0029CDFF8E0803048FEF71AFF42 -:101BF00021462F4A002340460024FFF727FF029F7B -:101C00002C484FEA5709FEF70DFFBC421CD23E1B81 -:101C100029486119B6F5803F28BF4FF48036324617 -:101C2000FFF7B2F908EB040023493246FFF758FFEB -:101C300018B92248FEF7F6FE06E009EB54003946D3 -:101C4000FEF786FF3444E0E71D48FEF7D3FE029816 -:101C5000FEF7EFFE1B48FEF7CDFE1B49029A00205F -:101C600000F002FC04461948FEF7C4FE0398FEF794 -:101C7000E0FE1748FEF7BEFE2046FEF7DAFEFEF74E -:101C800049FE039B9C4204D01248FEF7CBFE022083 -:101C900000E0002005B0BDE8F08300BF56680008F2 -:101CA0007468000891680008A7680008CA680008FE -:101CB000831B0008E1680008008A0020F86800081B -:101CC0000369000811690008008000081A6900080B -:101CD0002D6900083369000810B5022000F0C8FA29 -:101CE000D8B3042000F0C4FA044670B11B48FEF7D4 -:101CF00099FE042000F0B0FA022000F0ADFA4FF493 -:101D00000040BDE8104000F0A7BA1548FEF78AFE73 -:101D1000082000F0A1FA102000F09EFA202000F028 -:101D20009BFA402000F098FA2046FFF72DFF02288A -:101D300007D1082000F084FA102000F081FA00F0AA -:101D400061FB042000F088FA022000F085FA4FF4CD -:101D50000040BDE8104000F073BA10BD7E69000875 -:101D6000B469000810B52548FEF75CFE4FF480000A -:101D7000FFF70AFF10B102283AD809E0202000F04E -:101D80006BFA402000F068FA802000F059FA2FE04A -:101D9000082000F055FA102000F052FA202000F040 -:101DA00067FA20B91648FEF73DFE202007E04020E4 -:101DB00000F05EFA58B91348FEF734FE402000F0F8 -:101DC0003FFA4FF4007000F03BFA00F01BFB0E48A6 -:101DD0000024FEF727FE082000F03EFA102000F055 -:101DE0003BFA202000F038FA402000F035FA00E0FD -:101DF0000124042000F030FA204610BDCF6900080D -:101E0000E9690008226A00085B6A000808B5024810 -:101E1000FEF708FE00F0FDFA956A00081EF0040FB8 -:101E20000CBFEFF30880EFF30980FFF7EFBF7047B7 -:101E300008B50120FEF7F6FC08B900200FE00020ED -:101E4000FEF7F0FC0028F8D00220FEF7EBFC00289B -:101E5000F3D00320FEF7E6FC80F00100C0B200F0F2 -:101E6000010008BD1FB504460E48FEF7DBFD01A9C1 -:101E70000C22204600F026FB01A8FEF7D3FD2046E9 -:101E8000FEF784FE0220FEF7CDFC04460220FEF79A -:101E9000C9FCA04201D000F0B5FA0A2000F008FB0E -:101EA000F4E700BF8B6A00087FB5BF484FF0805150 -:101EB000FFF760FDFFF796FD044630B1FFF79AFD8E -:101EC000BA48FEF7AFFD00F0A4FA4FF08051B648D3 -:101ED000FFF75AFDFEF750FCFEF7D0FCB448FEF7C2 -:101EE000A1FDB448FEF79EFDB348FEF79BFDB34845 -:101EF000FEF798FDB248FEF795FDB248FEF792FD59 -:101F0000B148FEF78FFDFFF7B3FC00F0BBF900F01E -:101F1000DBF921460C2201A800F05BFE00F0D0F9AD -:101F200001A90C2200F0CEFA01A8FEF77BFDA7481C -:101F3000FEF778FDA548FEF775FD4FF4804000F0F0 -:101F400097F990B1A248FEF76DFD4FF4804000F084 -:101F500083F9082000F080F9102000F07DF920209E -:101F600000F07AF9402000F077F9FEF77DFFFEF7E8 -:101F700067FCFFF77DFCFEF7A1FDFEF7D9FDFEF73C -:101F800001FC08B1934803E0FEF744FC10B192480D -:101F9000FFF768FF00F084F9802000F069F918B1BC -:101FA000802000F059F94BE04FF4003000F060F968 -:101FB00020B14FF4003000F04FF943E0FFF738FF55 -:101FC00068B141F288348548FEF72CFDFFF730FFF9 -:101FD00078B1012000F06CFA013CF7D132E0804B7F -:101FE0000CCB013301D0013203D17E48FEF71AFD3C -:101FF00028E0FFF7BEFD48B3FFF7BBFD10B17A48FC -:10200000FEF710FD4FF4007000F032F910B1774880 -:10201000FEF708FD4FF4007000F01EF9102000F0EC -:1020200027F9002800F0F8807148FEF7FBFC082033 -:1020300000F012F9102000F00FF9FFF793FE98B9A5 -:102040006C48A5E76C48FEF7EDFCF6E74FF400702E -:1020500000F00EF90028CFD1082000F0FDF8102084 -:1020600000F0FAF8FFF738FE4FF4005000F000F9E6 -:1020700004464FF4805000F0FBF8400040EA840032 -:10208000C4B24FF4006000F0F3F82043C0B2072858 -:10209000C3B20DD14FF4006000F0DEF84FF4805071 -:1020A00000F0DAF84FF4005000F0D6F853486FE72C -:1020B00001334FF40060DBB2023B052B11D8DFE89F -:1020C00003F00C1003100C1000F0C6F84FF4805011 -:1020D00000F0C2F84FF4005003E000F0BDF84FF4F8 -:1020E000805000F0ADF84648FEF79CFCFFF724FD59 -:1020F000FFF738FDFEF786FF394B42485D681E68E2 -:10210000FEF778FC2846FEF794FC3F48FEF772FC89 -:102110000022930001324FF0FF3103F16043082A9F -:1021200003F56143C3F88010C3F88011F1D1374B38 -:102130004FF4801200241A634FF480625C639C6346 -:102140001A645C6400F01AFC3148012100F0BEFC06 -:1021500021462F4800F0BAFCF320012100F0C2FC18 -:102160002146F32000F0BEFC0320012100F0C6FC54 -:102170002146032000F0C2FC6FF4A050012100F0C2 -:10218000C9FC21466FF4A05000F0C4FC2148012195 -:1021900000F0CCFC1F48214600F0C8FC63B64FF0AD -:1021A000FF3EB546284700BF00700040A06A000807 -:1021B000AD6A0008B46A0008416B0008D86B0008DB -:1021C000596C0008DA6C00085D6D0008B36A0008FD -:1021D000DA6D0008014550FE024550FE076E00080A -:1021E00000800008426E0008556E00086D6E000801 -:1021F0008D6E0008034550FE016F0008044550FE37 -:10220000206F0008326F0008466F00080038024057 -:102210000010E022B379F764082000F029F820B11B -:102220000548FEF7FFFB102003E00448FEF7FAFB29 -:10223000082000F005F815E7B56E0008DB6E000811 -:1022400010B50446002000F0A9FC40EA040100207B -:10225000BDE8104000F092BC10B50446002000F02C -:102260009DFC20EA04010020BDE8104000F086BC7F -:1022700010B50446002000F091FC204214BF01205C -:10228000002010BD08B50120FFF788FB0120FFF7F3 -:10229000EFFF20B90121BDE8084000F06FBC08BD88 -:1022A00008B50648FEF7A6FB002000F077FCFEF715 -:1022B000C0FBBDE80840FEF72DBB00BF4E6F000815 -:1022C000022000F06BBC000008B5FFF7F9FF0449DD -:1022D000884204D00220BDE8084000F04FBC08BD91 -:1022E0008BB8185808B50248FEF79CFB00F08AF836 -:1022F0005A6F00081FB504460C2201A8FEF744FEE1 -:1023000001AB03CB206018686160A060204604B078 -:1023100010BD0068A0F10C035842584170470000FE -:1023200080B51746064610480D461C46FEF762FB70 -:102330003846FEF75FFB0D48FEF75CFB3046FEF7C4 -:1023400059FB0B48FEF756FB2846FEF772FB2CB1F3 -:102350000848FEF74FFB2046FEF74CFB0648FEF709 -:1023600061FBFFF7BFFF00BF776F00085A6D0008E1 -:10237000806F00085B6D0008B36A00081FB506AAED -:1023800052F8044B039200921A462346FFF7C8FF07 -:102390000CB41FB506AA52F8043B03920092014AFE -:1023A000FFF7BEFF826F0008002307B57246009357 -:1023B000014BFFF7E3FF00BF896F00087446064832 -:1023C00008B5FEF717FB2046FEF733FB0348FEF780 -:1023D00029FBFFF787FF00BF916F0008B36A000871 -:1023E000BFF34F8F0549064BCA6802F4E0621343FE -:1023F000CB60BFF34F8F00BFFDE700BF00ED00E0F3 -:102400000400FA0508B5FEF7CFFB00F0AFFBFFF7BD -:10241000E7FF08B5FFF7E4FFF0B50646002401200A -:102420002546034694421DD011F804C000F1010E68 -:10243000BCF1000F03D17355774605460DE001331B -:10244000774606F800C0DBB2FF2B07D102F1FF3C54 -:10245000644506D07355871C754601230134384600 -:10246000E0E770467355F0BD30B5C9B1C0430A44CA -:10247000914213D011F8013B0A4D83EA000404F0A5 -:102480000F0455F8244084EA101080EA131303F077 -:102490000F0355F8233083EA1010E9E7C04330BD3D -:1024A000084630BD7465000800010138FDD1704751 -:1024B00010B504462CB14FF47A70013CFFF7F4FFDD -:1024C000F8E710BD0A2A10B504DC1148BDE8104039 -:1024D000FEF7A8BA30230C461C2204F8023B7823EE -:1024E0004B700F2393400340D340092B01D8303366 -:1024F00002E00F2B02D85733DBB200E02023043A6E -:1025000004F8013B131DECD100238B7210BD00BFFA -:10251000CB6F000808B582684368934203D3044830 -:102520001421FFF741FF0268591C4160D05C08BDCF -:10253000E16F000843688268934210B503D30448F2 -:102540001921FFF731FF02685C1C4460D15410BDB3 -:10255000E16F0008F0B587B00E460746154600212A -:102560000C2268461C4600F034FB00210C2203A814 -:102570000296009700F02DFB684603950594FFF73F -:10258000C9FF06466846FFF7C5FFB042014601D0C5 -:1025900003A808E06846FFF7BDFF0446D8B101284C -:1025A00004D103A83146FFF7C5FFEBE7030608D5C2 -:1025B00004F07F046846FFF7ADFF40EA042000F115 -:1025C0008004A4B20025A542DCDA03A80021FFF7AD -:1025D000B1FF0135F7E7049807B0F0BDFEE7000052 -:1025E0000748084A0849121A08B500F0E7FA0748F0 -:1025F000074A0021121A00F0ECFAFFF755FC00BF61 -:102600000000002014000020F46F000814000020D7 -:10261000008A0120044B9A6809B1104301E022EAC4 -:1026200000009860704700BF002004E0044B1A6966 -:10263000002A04DA034A5A6002F188325A6070476D -:10264000003C024023016745024A136943F00043FE -:1026500013617047003C0240014BD860704700BFD7 -:10266000003C02400E4BDA68D20310D4DA68D1067F -:102670000FD4DA68D2050ED4DA6812F0E00F0CD16C -:10268000DB6813F0020F14BF0820092070470120F7 -:10269000704706207047022070470720704700BF30 -:1026A000003C0240092307B58DF80730FFF7DAFF39 -:1026B0008DF807009DF80730012BF7D09DF8070033 -:1026C00003B05DF804FB000070B5064641B1012976 -:1026D00008D002290CBF4FF400754FF4407503E099 -:1026E0000D4601E04FF48075FFF7DCFF09281ED18D -:1026F0000F4C236923F4407323612169294321612D -:10270000236923F0F8032361236943F002031E4386 -:102710002661236943F480332361FFF7C3FF2369F4 -:1027200023F002032361236923F0F803236170BDC2 -:10273000003C024070B505460E46FFF7B3FF09287E -:1027400013D10A4C236923F4407323612369236165 -:10275000236943F0010323612E70BFF34F8FFFF70E -:10276000A1FF236923F00103236170BD003C0240F7 -:10277000F0B500220E680123934003EA060E9E4541 -:102780002AD1550003230468AB40DB431C4004609E -:102790000C79076804FA05FC013C4CEA0707012C98 -:1027A000076011D884684F791C40AF40846084680A -:1027B00027438760446824EA0E0444608C794768A4 -:1027C0009440A4B23C434460C4682340C360CB79C6 -:1027D000C468AB402343C3600132102ACBD1F0BDA3 -:1027E0000369194214BF01200020704702B909048F -:1027F0008161704701F00703C9089B0000EB81006D -:102800000F219A40994010B5046A24EA010101623F -:10281000016A1143016210BD014B1860704700BF8F -:1028200000300040014B5860704700BF003000404E -:10283000014B9860704700BF003000404AF6AA2262 -:10284000014B1A60704700BF003000404CF6CC428C -:10285000014B1A60704700BF003000400023036046 -:1028600043608360C36003614361836170470023F9 -:1028700043608360C3600360036143618361C3613C -:10288000036243628362C362704700000E490368BB -:102890000A6810B544691C4383691C4322F07F43D6 -:1028A000426823F03003234343EA02630B60C368AA -:1028B00082684C681A43054B23401343026943EA7C -:1028C00002434B6010BD00BF001000A0FEF7E0FF08 -:1028D00090E80C000E491A4383681A43C3681A43F0 -:1028E00003691A4383691A43C3691A43036A1A4383 -:1028F000436A1A43836A1A43C36A10B51A434C6980 -:10290000044B23401343426943EA82434B6110BDA9 -:10291000001000A000008090044B1A6810B142F033 -:10292000010201E022F001021A607047001000A0CD -:10293000024B9A68920600D498617047001000A07C -:10294000024B9A68920600D418617047001000A0EC -:10295000014B1878704700BF201000A0034B9B6804 -:10296000184214BF01200020704700BF001000A0D3 -:10297000014BD860704700BF001000A00F4B002132 -:102980001A6842F001021A6099601A6822F0A8528F -:1029900022F410221A600A4A5A600A4AC3F88420B4 -:1029A00002F18062C3F888201A6822F480221A603B -:1029B000D960C3F88C10C3F89010704700380240FB -:1029C00010300024003000201E4A936803F00C03EE -:1029D000042B10B503D0082B03D01B4B19E01B4B65 -:1029E00017E05168536811F4800F516803F03F03FA -:1029F00018BF164AC1F3881108BF134AB2FBF3F39C -:102A0000104A4B435268C2F3014201325200B3FBF9 -:102A1000F2F30C4A036093680D49C3F30313CC5CD3 -:102A20000368E34043609468C4F382240C5D23FA96 -:102A300004F484609268C2F342328A5CD340C3607B -:102A400010BD00BF003802400024F40040787D0132 -:102A500001000020044B1A6B09B1104301E022EA87 -:102A600000001863704700BF00380240044B5A6BE7 -:102A700009B1104301E022EA00005863704700BF2B -:102A800000380240044B9A6B09B1104301E022EA7E -:102A900000009863704700BF00380240044B1A6C76 -:102AA00009B1104301E022EA00001864704700BF3A -:102AB00000380240044B5A6C09B1104301E022EA8D -:102AC00000005864704700BF00380240044B1A6988 -:102AD00009B1104301E022EA00001861704700BF0D -:102AE00000380240044B5A6909B1104301E022EA60 -:102AF00000005861704700BF00380240044B9A69DB -:102B000009B1104301E022EA00009861704700BF5C -:102B100000380240044B1A6A09B1104301E022EA6E -:102B200000001862704700BF00380240044B5A6A28 -:102B300009B1104301E022EA00005862704700BF6B -:102B4000003802404209084B012A01D11B6804E009 -:102B5000022A01D11B6F00E05B6F00F01F0023FA17 -:102B600000F000F00100704700380240024A536F45 -:102B700043F08073536770470038024082B00023EF -:102B80000193054B0193019B03EB80000190019B96 -:102B9000196002B0704700BF5028004082B0002387 -:102BA0000193054B0193019B03EB80000190019B76 -:102BB000186802B0704700BF50280040431E0A4406 -:102BC00010B5914204D011F8014B03F8014FF8E71A -:102BD00010BD02440346934202D003F8011BFAE7FA -:102BE0007047000014000020242C0008001402404C -:102BF000004000000E0004000014024000800000AD -:102C00000F000400000000000000000000000000B1 -:102C100000000000B46500081C2C0008E42B00082C -:102C2000500000000060004000000001801A060013 -:102C3000005F6000382C00080054014000002000B4 -:102C400000000240200000000500080000000240D3 -:102C50008000000007000800000002401000000093 -:102C60000000000000000240080000000100000019 -:102C700000040240040000000004024001000000C3 -:102C800001000000001802404000000001000000A8 -:102C90000018024008000000000000000018024078 -:102CA0002000000001000000001802401000000099 -:102CB00001000000000C02400001000008000700B5 -:102CC000000C0240000200000900070001424F4FC3 -:102CD000544C4F414445520000000000002A2A0095 -:102CE00000000000000000000047FF004C61747409 -:102CF000696365006943456375626532203230312E -:102D0000362E30322E323738313000506172743AFC -:102D1000206943453430554C314B2D434D333641BA -:102D200000446174653A204A756C20323520323097 -:102D300031362030393A35363A33394702FF7EAAE8 -:102D4000997E5100010592002062016F8247027254 -:102D5000007011000101470B104702804705024730 -:102D6000058003470801470208471120470C024726 -:102D70000B02471740470501470540470C040090E8 -:102D8000470F1047064047050147054047050147E3 -:102D900004800004471080470502002000080080DE -:102DA000470201470908471701470434000C47044C -:102DB00003470D30475970470E801047040200400A -:102DC000470408014702070200404704080147057D -:102DD0002004474103470B03470D1C471080470958 -:102DE000010080470203470908470324472B78471F -:102DF000124007470A40470C040008470E404705A9 -:102E0000014706044704014706044706104711809E -:102E10000747061C470380470C08470A0C30204729 -:102E20000F30E8470C63C1470C0C30470F400105D9 -:102E3000470C03C0470C0C3020470D184702E047F1 -:102E400004020CB10018003847064047080C304710 -:102E50001001074702010001CD72470398470F0C8C -:102E60003020470D3000F9A04708100001C0470589 -:102E7000038047070C30470E3000710547083000CB -:102E800001470F0C3020470F5808470401DCA00011 -:102E900080470363C1470203C020AF5247050C308F -:102EA00047100803470402E0F5470503C047020244 -:102EB000E0050FA01047040C3020471240002020EE -:102EC000470AC001E007A547060C3047110D47032C -:102ED00014470B4047030547031447020C302047B3 -:102EE0000F900004470220470E17CFA24702084761 -:102EF000020C30471030092447022C470D02CD0F39 -:102F0000F047050C3020470F108004470368470343 -:102F1000010547100C30471011073047190C3020BD -:102F2000470D300010470A0147100C30470E30475C -:102F300002052047023447050247100C302047079E -:102F4000044706404704203404ED80000247091C72 -:102F5000000847050C30470F4047050422FC470294 -:102F6000014707A00002C047060C3020470903EACA -:102F7000F52000404706035CA500184708401C8F59 -:102F80000847050C30470A03D05047024047060166 -:102F9000F0554703104706200200F047050C30208B -:102FA0004709284703110001E047032038FC80024D -:102FB000470A1E8F0847050C3047080800404703A2 -:102FC0003029470402BC03D0470AC04703F0470535 -:102FD0000C3020470A1C0F10470829DCA500304799 -:102FE000090E8F0847050C30470C0F470941505513 -:102FF000002047090200F047050C3020470D044728 -:10300000065C76EC80024707084702068047060CFC -:103010003047080247050447030147033CF0470ACD -:1030200001400C00F047050C3020470820470B1CDE -:1030300021FAE732004047100C3047090C470B24B7 -:1030400064F0F7084709028047070C302047141C3A -:1030500031CCE720804703383D44470202804707D0 -:103060000C30470F404705027668FB47024000C01E -:10307000403D4703028047070C302047080C4705B6 -:103080000147053C63DE800010470C0447040C3008 -:10309000470A2047040247052402E8470210470672 -:1030A00001014047020147050C302000A100123CFD -:1030B00020000447021EF060470720001EF0404732 -:1030C000030D72BD400003428EEF47060C304705EA -:1030D00003C0C04704F047024000974703042000A4 -:1030E000F0470401403C470201429C47070C302056 -:1030F00047033A3C200647030CF06018021080009A -:10310000060140024AA500188000077815006047B4 -:10311000020CC96047050C30470503C0104704F096 -:10312000470205090747038001D055470390004037 -:103130001620000142C1096047050C3020470332C8 -:103140003C202C47031CF04030470280000447021B -:10315000204705018839414702020025ACC02047BD -:10316000040C30470503C0044704F001300801098E -:10317000000C4702404705F00F4282010020402525 -:1031800047070C30204703323C20470280001EF0E6 -:1031900040470280E847037C470705781547044409 -:1031A00088002047040C3047040803C80001470288 -:1031B00010F0470371094703964708C01640000303 -:1031C00042150020470202001C0C30200001017A49 -:1031D0003C2047040EF0600008704703A00601DAA7 -:1031E000A047090A007E8800300002C0000C30476A -:1031F000038D0003C04000243000F0470308FB4764 -:103200000240000340F54709014287002047040CB3 -:103210000C30202000013A3C20201004470708040D -:10322000001000045AA102004000A04705603EEFD4 -:10323000A547050C300010070D0803C047020247E0 -:10324000070520470334E001470B1C05A047020295 -:1032500047020C3020040500723C20001047060491 -:1032600050800447022021EEE2020040181047047B -:1032700003601FAAF5470202C0000C300004054796 -:103280000203C0001004470542D8BD2C00103C7654 -:103290007042470240008047030102C02C00F04703 -:1032A000050C3020000800723C2000A02078470365 -:1032B000300010470501DCA302304702804703C0FD -:1032C00001C047080C3000040B470203C000521431 -:1032D0004704300008033C4702343830A300204044 -:1032E0000147048101C2C0470C0A723C204702A07A -:1032F0003BF9014001404705202C0CF040470307F3 -:103300008402470306DEF5E0470A100803C1000106 -:10331000547D69A7004040470402046000F0470262 -:103320008050003C470402BCF1D0104706018047A2 -:1033300002723C20060024017F74400140470306CE -:1033400047034EA50018470215A3C2806002002C57 -:1033500091A0470B0803C047023C2E78A010024002 -:103360004707D055470490003C470201401DF3F049 -:103370004707034703121540040036687B6440305A -:1033800047040CA047020DD1000147020723C247A2 -:1033900002034614ABC420470A08154004023C004F -:1033A0004A58007047040C470203DFE24705803C9F -:1033B00047020266C20BC208470A03BC1047023C20 -:1033C000294B7440002847042000281EF0400002CA -:1033D0001801A3C20004016060AB470C043C470322 -:1033E000164268A0004047074010F0470205080059 -:1033F000803C47021403B4C70010470A03BC100CFA -:103400004702035D7A014706A00E60FEA047023026 -:1034100001A7A3C2000803469E81A020470A043CDE -:103420000004470202C85A0847071E03C0F54705B3 -:10343000803C470201601D03F0470C295047021EE3 -:1034400001D95B10404705800423CCA1024703113A -:1034500023C247041D95E0470B90168047021C319C -:103460004CA547080202C002470480003C47043EC6 -:10347000E1D0470A1B47051F470601C04703062046 -:10348000E8A3024703B325ED0004016640ABC42066 -:1034900047090A4705024706014705267003470406 -:1034A000AFB694010A0242B507C004471064F85051 -:1034B000104708701AA302B10001D04703C00003EF -:1034C000DDB5E0471164005047083443D053002273 -:1034D0008051A0470380D6C76EE1D04709081E5B24 -:1034E000C8104703040AF0404709FE00404702198C -:1034F0008F304002000166A5E0470A0B00121E80D3 -:103500006000142800F04702400007470403F0A0C1 -:103510004080020007B0470301C24E944708018073 -:10352000470506470308F040184702100006200030 -:10353000020C0A001947020B3A81816002E68405F9 -:10354000A020470801470810F04704B7470240048D -:103550003CC0050002801087400101000260140297 -:10356000400447060300080036C9580C0034001A0E -:10357000F04030470218000CA14000FE504A0247BC -:10358000020120288047030480D0A04706010003E1 -:103590000A7A05424702144702F000304702FB0056 -:1035A00004408003F080404704402880000340012D -:1035B00000D080470603000500DE11D84702366CB4 -:1035C0001AF060470308000D8000020C0A40300822 -:1035D000000B3801010002000D85E02047060100C4 -:1035E0000F0052E0404702142800F04703B00700E4 -:1035F00009024702C0054820040007414104470270 -:10360000401602E0470A08F680D2100E47021AF070 -:10361000404703184703200400A54000805011755F -:10362000D04703C067A0140447080B091B28C0201B -:10363000470310F04704C900400E3C38C05A480107 -:1036400000D887B5F8470303AEE8008047080801B3 -:103650009F141447041AF040470280180400201EEB -:103660002200A54130005C037B08084702E021CA24 -:10367000470A050C56788240470310F04703F0E5EF -:103680003C470340C05A40104702A7C30C04000245 -:1036900062DC05470A0D81D6C9504705A54047029F -:1036A00098984702A004301C0540800018E04705A8 -:1036B000600685014709050A32054A4704405A4712 -:1036C0000399933400400201C00A40470210B047FA -:1036D000030847020105470A0581FE51D047040A45 -:1036E000F0403000901000040004027E5F401000A3 -:1036F00050E1B8280400E0400FF812470A0B53E0ED -:103700004A4705F000300070FD3C04000220E00F45 -:1037100048100079098168040002600CC447111444 -:10372000004EA0400240470607C850C047039047DC -:1037300005407EC70008470F020010A040804047A8 -:10374000063BC00542470210D04705235DAF014745 -:103750000E06470744470306470203C850C0180037 -:1037600018033E01006000063DC0471104470542B2 -:1037700047050402C005404702188FBC01204703DB -:103780000E824716100001C000044702674850C06F -:10379000100001E130104703604718300001470274 -:1037A00004470223E005401047020DE047160C478E -:1037B000070547030C0006674850C0F0001017B219 -:1037C000012847020780471008470B08470222E0FC -:1037D0000540204702E7C00147234003E850C147A6 -:1037E00003F0470402662F90471B10470203C005F1 -:1037F000404703D0470401442FB040471D73685031 -:10380000C010470219F56C47020366EED0471E420E -:10381000C00540304702ADE11C47020142AEF080D6 -:10382000470240470E0447090226036850C00007BC -:10383000101DFB08084702068DE800204710384796 -:103840000A0403E0054040003085830C4705084723 -:103850000F04470B040020736850C11047028047D3 -:1038600003C00060208E5420470D04470B04000461 -:1038700042E00540100051E04703804142650DA041 -:10388000470372009011010101470B1047064047A2 -:1038900005014705400002470608471120470680FA -:1038A0004705024712084722010002470404471156 -:1038B00010470640470501470540470316000147EA -:1038C0000604472202002000084702084703104769 -:1038D000243C000C47063047688010470402004033 -:1038E0004704080147030200404704080147043029 -:1038F0002004474D01470604472103008047050285 -:1039000047030C471F1E472D024703404705014749 -:1039100017404705014706044703030147060447CC -:103920000610471D8047050247110C30472C0C300C -:1039300020472B0C30472C0C3020472B0C30470EE7 -:1039400001470C1047100C3020470D02471D0C306A -:10395000472C0C3020472B0C30472C0C3020472BA9 -:103960000C30472C0C3020472B0C30472C0C3020CF -:10397000472B0C30472C0C3020472B0C30471308BA -:103980000004002055470880004247080C302047BB -:10399000120C0020016855404707400200404707CD -:1039A0000C3047150400200F470B02055004470454 -:1039B0000C30204714260168F0C04709661E855464 -:1039C00047050C3047180F470DF08047040C302096 -:1039D00047150168F0C0470A168F0C47050C3047A1 -:1039E000150C47020F470DF08047040C3020471597 -:1039F00001E8F0C0470A1E8F0C47050C3047150C34 -:103A000020200F470B0200F047050C3020471426FA -:103A10006548F0C04709601C8F0C47050C304716FD -:103A200020200F470A460200F047050C30204715BA -:103A30006548F0C047080207548F0C000147030C8B -:103A40003047180F470DF047050C30204712064746 -:103A50000201C8F0C047076047021C8F0C47050CE5 -:103A60003047170C0F470503C01247020401B0F09E -:103A700047050C3020471501DAF0C0470423C04742 -:103A80000307148F0C47050C304718F04703D9B0D3 -:103A9000A4470280470A0C30204714060008F0476C -:103AA0000350A780020040470A0C30471620300F11 -:103AB0004703D900A047040247080C3020471505EA -:103AC000780F80470250A780024703404047070C09 -:103AD00030471524201B0F470528470D0C30204781 -:103AE0001501F80F804703078002470C0C30471779 -:103AF0000D0F4704B020470D0C30204714066179A4 -:103B00000F804703078402470C0C30471504200D33 -:103B10000F47035000A8470D0C302047142004F82D -:103B20000F804703070002470C0C30471506203C66 -:103B30000F47035000B4470440050A088047040CAF -:103B4000302047143501C90F8047030100024702A6 -:103B5000026000E50447050C304715366C1C0F4722 -:103B6000035000284707588047040C30204714149E -:103B700028E80F8047030704020060470214E0545E -:103B800047050C3047166D4CA547035000204704ED -:103B9000C0040A088047040C3020471529415A8088 -:103BA00047038700024702024702A504471B10048F -:103BB0004000F0470301A9A00004800042471F20F5 -:103BC0007808F047020800BFA021404002004047AB -:103BD0001D10042060A54702404702A047020100D3 -:103BE00040020550471C2007605A804703078002A7 -:103BF000470202601E8556471D0340A547035190AA -:103C0000B04702014704F0471D01605A804702D9BE -:103C1000A3840247020800168F0E470E01470D1CB1 -:103C2000004FA54704A0703C4706F0470E01470D22 -:103C30002601E15A80470219B5F43E47041E8F0E53 -:103C4000471C1C20390F47031900B44704028200A7 -:103C5000F1471C2605580F804702F9E3800247030D -:103C600060148F0C471C1C3C2F0F47031000344777 -:103C70000320020200F1471C2061DB0F8047028015 -:103C8000030002470306548F0C471C3C381C0F47A7 -:103C900002180090F03C4703028000F1471D04C861 -:103CA0000F8018003DA5E03E006047021C8F0C47C6 -:103CB0001C3C380C0F47031000283C47040190F1CE -:103CC000471D25CA0F8047029005643E47041DBF6B -:103CD0000C470E04470222CF4A6047050400042027 -:103CE00047031000188F62E1004100421C05A04705 -:103CF0000E04000467FB40404705040036044703F8 -:103D00001000981D6CF1024002002C470210014780 -:103D10000604470208284702100025F03C600042D4 -:103D20004704100232DAC3400040018541410005DA -:103D3000000381005004470A16A804470206067AC9 -:103D40007C424047060402DFD348000459F07281E8 -:103D500047041EE0504710023FE8F24707801C7AF4 -:103D6000E0A5470220080F3001400001C25C05A019 -:103D70000401470D100E62DFFA4707102026FC50A1 -:103D80000100C018E13A01000147026E4713214FBC -:103D9000F54707014221D90740007D000F28110097 -:103DA0000101C026020414471006EBF64707011C68 -:103DB00005FD036100021987F69D000247021F807E -:103DC0000420470809470408000628E01820404757 -:103DD0000408470202DFE3402000D80B656100942D -:103DE0000142B501470A0847040C002E07FA0402F5 -:103DF00047050C00042BCCC36070009187BA890082 -:103E0000CA020021C20020470F3C0010A047020850 -:103E1000470601F0D3410147029FF9510004000217 -:103E2000B680404711014EA0470902E8C342020094 -:103E300070153EC1470306D5A0A047100642D0B971 -:103E4000404707042830A047031805732147036043 -:103E500037A1470F06000E3BDCB16247050647035A -:103E6000DAA00018007C0FBB09006001E035D047E4 -:103E7000020A470810470602CFE0470D0400190F59 -:103E800021194702014247040447080E47040E4720 -:103E9000021BD0470930470304059981720D470280 -:103EA00002470B03000980920447047F60C2470663 -:103EB000040142034005001008018047034003400D -:103EC000470A03000F80174705386CC302470504F3 -:103ED000009E000E0A20100058F0470340471018BB -:103EE00014470403F00347081E3C30A00847029023 -:103EF000E77F21470306EED4A447090100DE80478F -:103F0000030402DA430247073E056EA04703B819CF -:103F1000FF4147024002B689144709090894104737 -:103F2000034040F0024707802C3F5ECB470308A9BF -:103F3000836101470202020F470A0801F747028026 -:103F4000470260C80A0247060E20060CC710470247 -:103F5000781BEB0108084702468F0847094B0016FB -:103F60004047020146387B0201000247053C3C10F5 -:103F70000A47033009807447020363C00F470A20D1 -:103F80000012204703BC7AF947030D47052005DAE4 -:103F90000A200247020772B4080009405E8F081029 -:103FA00047080B0D941A4247033C00A00847050838 -:103FB000001C294B08002020D00541470280000248 -:103FC000C20F470A080B06A590470369CAA02047FD -:103FD000050C0020020C042032801809200008C0C3 -:103FE00047025C8F084709200E1000084703243061 -:103FF0000401470504470220CCA141470218B0C084 -:10400000284704020F470C17900080001429EA0487 -:104010000247050447020378D1408000D005782884 -:1040200047041C8F0847090700D04703401472D982 -:1040300047030147050401CA5B4703110567B84000 -:10404000470242800F47080180000A760080808086 -:1040500000767B802000024703060E14000DA7089F -:10406000180159C573BC006047024C8F0847090509 -:104070000EDF00804703237B470A26CEFF0940005E -:10408000D9E3384147072047080518DA47053CE9D6 -:1040900080200038470603DBF7470338A138014783 -:1040A000040C000800404702350050134703040386 -:1040B000C04702266D7040084047040400047C702D -:1040C00005470308ABA39C20470203010F040040EF -:1040D000014020E0A00147030683D047022428DAEC -:1040E0005012470504002423EC4702800071B17E82 -:1040F000BD40404702418F044703467E0D344702CE -:10410000014706046410A04707020036DAF048476A -:1041100002500743000147040F04470201639CEC6F -:10412000342000098047052065DEA0470901FFF41F -:10413000404000D9A720000A47020700CF044702E9 -:1041400002403401A0470802266650014709437027 -:10415000A04703500FA40020470204010F044704A6 -:104160002FE00220470826760E022247070621ECA0 -:104170004704980FF420470303808F06470201404D -:10418000010A4706154001800C62CC4705C0470470 -:104190003D40E740C10050A7030020470202410F05 -:1041A000044702020284AA47055A1550010020263E -:1041B000FE0847041000804702077CD34A020081B2 -:1041C000D772000C0801E001CF04008000424705CF -:1041D00001800803C047020C41DD20470608002E7D -:1041E00039D00A47030800C02820470220010F04E5 -:1041F00000C002004047040D98523C300240002AA3 -:10420000CF47070C0006036C470430073028010035 -:1042100001C000EF044703029E4705080803C0479A -:10422000021401E024001047061C207000414703DF -:10423000A0C14702404702010F04470437AA010406 -:10424000470219523C30470302DA10023002470697 -:1042500004EE8063470315310028C0470201EF04D4 -:1042600047034035040010470403C0470303F047E9 -:104270000340470506034A1047030803240020476C -:104280000202400F04006047038801218047027248 -:104290003C30470368EE4022004047030600060218 -:1042A0001E2000180031EF32002160470201AF04E8 -:1042B00010470223CDE0470603C14704EF47034000 -:1042C00047051401E04705A5FBE0470306010F047D -:1042D00047036004C0804704723C304703027D807E -:1042E0004702404706036A0A4703019B2FF48347AE -:1042F0000203808F048047060100010E0803C004FA -:104300004702404BF1493000080F280447101405BC -:1043100000044706030008187A3C300447023BCDEE -:10432000F04070001047020447061047028047051E -:1043300003A18A4704028047060803C447050A40D0 -:10434000800070FD24470280470D028047050280EF -:10435000470401805A3C3040000600FA0A404703F7 -:1043600080470201470D0142C04704014047040352 -:104370008F470203C010470238ED0BC10002008DC9 -:1043800028470A010141840001C0278247040680B2 -:1043900047065A3C30400006027A054900054702AC -:1043A00004000E47092A81A047031F938047095044 -:1043B000470203C010002401505F40470270030011 -:1043C000404702304703044706400002800F0008C0 -:1043D000470820007A3C30000E00637AA54047026F -:1043E000804705044705184704C00060008F00049B -:1043F0004709180803C05800042C70E34047029096 -:104400009D0008470E01C00300F04709018B523C94 -:10441000300C002C03FCC34200380008000C470E8F -:10442000020014E0F847043FC04704080803C050E6 -:10443000470201700500300031F1000C00044047D4 -:104440000B40022ECA5047041DD080470319523C2E -:104450003020470202DA470230008090000400203A -:10446000704705784706065415A847080347030315 -:10447000C04702042C6A814000020007470304473A -:10448000052800F3C2BF800003D280C0F0470701B7 -:10449000804702723C3006010C026F504018014701 -:1044A0000306470618180007E2954703400EE0F898 -:1044B000470950000AC0470401DA3C47024010CBCC -:1044C000470402C00544428008470604024702220E -:1044D000470820007840204703016D3447041847FF -:1044E00004014850400100704706071C00084703BC -:1044F00072007011020101470802470608470402D2 -:10450000470A04470220470680470C014706044734 -:104510000401470802C0470310471702470608472F -:104520000B084716024706084704024706084706D5 -:1045300020470680471F0300070004470B7C471DE8 -:1045400003470F6C470CA84705024727204708047C -:1045500047220240C04704080147030200404704C5 -:1045600008014705200447058010474C03470D709C -:10457000471F014703044709D0470340473A024712 -:104580001208470D80470A80000247030247058052 -:10459000470502470608470620472204470D4010FA -:1045A00030C04718067B38470240626D61404703C0 -:1045B00083DC05470430C0471803DBD0470301AB59 -:1045C0002022C0470203C0008047021030C04716B7 -:1045D0000103F8EC00B0018047020162000600838D -:1045E000D6000A00180030C0471703F83001F04722 -:1045F0000903C20D1047021030C047160108805A47 -:1046000000B01400AA49A16040030083C447030C12 -:104610000030C0471702002D01800800064B6C02D5 -:1046200080030003C247041030C0470A80470D14BE -:10463000EC50104702A2EDF540470383CC0547023A -:10464000800030C047193AD8F04703A1E42047035F -:1046500003C2008B01001030C04716010A805F8042 -:104660004708030083C60847032030C047172100CE -:1046700028104708020003C00547031030C0471048 -:1046800002470980470502470483C647025C470288 -:1046900030C0471A10470503C0470303C2470280D2 -:1046A000001030C04717080969470740470383D601 -:1046B0000581470330C04718215D470720470303A2 -:1046C000C0000A47021030C0471708805F18104723 -:1046D00007100083DC4703800030C0470901470E04 -:1046E000227A00804707600003C2470201001030B1 -:1046F000C0471704000A1847024010F12960470616 -:104700000A470330C0471801504703040F00014017 -:1047100047091030C04717043C585010018010F171 -:104720006A06804704014702180030C047183C1051 -:10473000508047020F00414047091030C04717041E -:104740003C5878A0470210F121404703A906F380A6 -:1047500000480030C047183C105847030F00400283 -:10476000800020193FE547028C1030C04717043CF9 -:104770005C470410F121604047028BACE00A47031C -:1047800030C047183C1000C047020F0040028000B4 -:104790002002A7B50A47021030C04717043C5C4707 -:1047A0000410F160470490B788470430C047183CB4 -:1047B0001047040F47050222EFF54702201030C0D2 -:1047C000471708024B9980470210F16047048BAEEF -:1047D000F81B470330C0470901470D0210DA1047A4 -:1047E000030F004047030202A5A50A47021030C08C -:1047F000471708025A470410F168470320A907D158 -:104800009A020C0030C047170122EB470220000F2C -:1048100047048000193EA58002081030C0471708E1 -:10482000826B470410F1284704A93CE00A47033093 -:10483000C04718115D47040F0040470280022A2636 -:10484000800002001030C04717183DC04704169979 -:1048500047058001B50A08470230C04712028C00A4 -:104860000800083C20470303096B47053FC1ED9052 -:1048700047021030C04709056047081C47026047DF -:1048800005018010F1624702060083DE470318002D -:1048900030C0470940470802803047068000010FBA -:1048A0000041C0470303C047041030C047130C7CCD -:1048B00000304703100080800AA13847020300A996 -:1048C000408047020C0030C0471202C278003047D7 -:1048D00003084702C00AA0404704294047030810C4 -:1048E00030DE4710084702404709C060F1804704A6 -:1048F00083C4470530C2470F01140003470A400034 -:10490000F0800008470203C20D0B47021030CE476B -:10491000090847095C470A2A500447030183C44732 -:104920000240470230C247090847062002024702F8 -:10493000804706200045A0344702800003C2094793 -:10494000031030C00700A0470F804E7847072000B3 -:104950002A50470902470230C04709084708B242B7 -:1049600078470905A03C402847081030C04712800E -:104970000E470C024702808083D6470530C04709A6 -:104980000847062000F24000084704D000204703F3 -:10499000024000800003C0470202001030C00747F9 -:1049A00003020F78044702204708540500304706E9 -:1049B00080470A1C000C0030C04704020F08024761 -:1049C000082000B3823A80204706804705804704CC -:1049D00010A20447071041F700280010470380A0E9 -:1049E00047050800080379000447095981A50A40D2 -:1049F000470ADFB44047057147060800608369D065 -:104A0000B04709198D0D904709022483802800447E -:104A1000090AF900A0018047030800083DC080E0B2 -:104A2000018020F5470306005A64A0470A02E7B355 -:104A3000C0400010222FDA11B047050847023C2081 -:104A400071B04702A0F080038047021A74470B023E -:104A5000AA77144703014148504702804704304772 -:104A600004A010002A57A86047035A44850A470942 -:104A700002AA0C022C4702014000D8B00080470473 -:104A8000204703F147030F50F5C047031A64009015 -:104A9000470A1892D428000101406A5010470708BD -:104AA0003DC030470320F5806047035A4488470BD8 -:104AB000341EB46C470223401BD8F0470608203C44 -:104AC0000030470320F08047041A65010B47094076 -:104AD000053EC04704046B804702080020470814C5 -:104AE000470302000347020CC5A8000D4047070812 -:104AF0000807C04704035810470202003C47080853 -:104B00004704404703332CBB0E0080470801128640 -:104B10002800014294DB4703C0470F08470780473E -:104B20000A1E8040470202003B0090008047102888 -:104B3000470660470850A55EE060000208BBDA80C7 -:104B4000E0470701E94E518047061047023C1C0729 -:104B5000470B85068447042A6EF04706744702AA6D -:104B600078F847072847023C0207804709025A00A5 -:104B70005470007047038047070108F46A47042017 -:104B8000F18247020240029DE7470280470702A5E3 -:104B900003424000304703F000024707980B4704E8 -:104BA00020F001C047028002830080014708030F04 -:104BB00052A047030C3E6A47090294DE18470A5484 -:104BC00014850A470AF0983C470303C3604709016C -:104BD000541FD0470990144702904709030F1380D0 -:104BE0004702600C3C4E30000180470614C8470263 -:104BF00001804703E00846005A45E047021847088D -:104C0000F0470503C0470A02BC1CD047062200201B -:104C1000001A7400084709030F13A60447020C3C4E -:104C20004A9810470505000294DD51E000100AF192 -:104C300084470443C48047020C4708F0043C2047E3 -:104C40000203C01010804705020001540909470300 -:104C500005F0A047043C010D0B000C4707030F12A1 -:104C6000806030000C3C4AB180C8470715CA500428 -:104C7000470220F580038003005A65E047020C4795 -:104C800008F004470403C010100014470602BC1CBF -:104C9000084704F0470302001A54000B00044707BA -:104CA000030F178047030C3C5E47028047060294BF -:104CB000CDF9F0470903C4470CF0470503C0470A84 -:104CC00001541F70D0470603470203C0470820471E -:104CD00002030F168047030C3C5A0001C847071413 -:104CE000FAB8E047080183DE47021C4709F0470590 -:104CF00003C0470314470602BC3CB890470983C071 -:104D0000470B02AA17A06047020C3C5A01804706D5 -:104D10000101E8FF71C047050780470401001080CA -:104D20004708AA0404470303C010470A542C084745 -:104D30000604470680470C200400200AA85E1814C9 -:104D400000C047040108F54C704704A76000044701 -:104D500005080008470A0420001002A810008A0075 -:104D600040470404006419F8F0000100A0470A0855 -:104D7000470E083D60000447052000083DC0470479 -:104D80008005E000380004C3FCA04711483C00F057 -:104D90004708083C47062BA342D000043C3E470B83 -:104DA000020F78470360083D6010000180470A0544 -:104DB000804019B006000600C3C4E0470218470748 -:104DC000020F084704083C00F8470B700008470230 -:104DD00001EB404702243C471230083CE070470298 -:104DE00080470440083DE0304702802845E0470402 -:104DF000C3C4A981470F22083C20080014C04705FE -:104E0000083C204703C0242BA002C047023C0101FC -:104E1000084709020F304704083CE001F047070843 -:104E20003C604703204009B047023000C3C4A99842 -:104E30004709020F4705083C2001E04707083C20CE -:104E4000470521EB4047033C0101470D2004470281 -:104E5000183C60008008470405470905E2E0284740 -:104E600002C3C5E0470D3C384702083C2001470516 -:104E7000024705B04703808BA06047033C471308F7 -:104E80003C40F84708083CC047042A01A4404702B8 -:104E900001C3C5A04711083C20F04708083C20B0DA -:104EA00047032050A04703043C471202083CC04778 -:104EB000070100083DE0470501B1404703C3C5A015 -:104EC000184710083C470A083C4702C047033B7C90 -:104ED0002047033C0103470D0E010020083C6047BA -:104EE00003804705083CE047038400012447020291 -:104EF00000AA85A101000C470B028000083C20009D -:104F0000044707083C2010E147020200B447042A86 -:104F10008100080004470272009011030101470854 -:104F20000247060847040247080780044702204753 -:104F30000680470C01470604470401470D10472A25 -:104F40000847160247060847040247060847062096 -:104F5000470680471F024703044729034729A04704 -:104F60002D10471E0200404704080147030200407D -:104F70004704080107804703200447058010474580 -:104F800001470603470D204718024706034703045D -:104F90004709C0470340472B38472108470B4000CB -:104FA00080470A80470502470580470502470518E4 -:104FB000084706202C471101470F04470D400030D9 -:104FC000C0472B1030C0472C30C0472B1030C04793 -:104FD0002C30C0471004471801001030C0470F8024 -:104FE000471C30C0472B1030C0472C30C0472B1017 -:104FF00030C0470E10471D30C0470A0200140B474F -:105000001D1030C0470C28E1471E30C0471B044725 -:105010000F1030C0471B0A471030C0470B20280B29 -:10502000471D1030C0470C14E1471E30C0472B10FD -:1050300030C0472C30C0472533069047031030C09E -:1050400047242031A480470430C0472B1030C0478C -:105050002C30C047253C0247041030C04723048051 -:105060003C14470530C04708804721801030C047B6 -:105070002901400030C047270147031030C0472CAA -:1050800030C0470A80471D0A47021030C047280138 -:10509000470330C0471D0300F047070547031030A2 -:1050A000C0471FF147070D9A470330C0471702A8B2 -:1050B00009704705404706010800041030C0471139 -:1050C00080470501544147041000796047050801F5 -:1050D00000080030C047150447023C47060B404714 -:1050E00003802A81000847021030C04715020007DC -:1050F000C05847041008684704AA85E1994703305F -:10510000C047183C1047050301428047023C470551 -:105110001030C0471707C04A470410086A06C04746 -:1051200002C3C5A0470430C0470F04470530000242 -:105130005A40470503014047033C47051030C0472C -:105140000F0A4707065848470410086B6680470255 -:10515000C3C5E00080470230C0470FF047083C104D -:105160000847040B41428047023C0147041030C00D -:10517000470F10470707C05A38470310002A06C0D8 -:105180004702C3C4E018470330C04711C0470502B7 -:105190005A500147040B41428047023C010147033A -:1051A0001030C0470E50470280470506585E71C058 -:1051B000470210002B66C04702C3C4AF00A04702DD -:1051C00030C04717025A504705034704013C4705C2 -:1051D0001030C047100180470506584870E047026C -:1051E000100028470301C3C4E04702180030C0473D -:1051F0000B083C47090100014098470401000340A7 -:1052000047023C3647041030C0470B083D60470951 -:1052100004470238E0470210002800404702C3C498 -:10522000E7470E3C47021010D9470D04470506C05A -:105230004702080247112000034B470D02470502B1 -:10524000800010081CE0470D0200084704020020FF -:105250004703028047041047041BB842C0470210AE -:1052600080900902470B1C470C01470A073E470381 -:105270001014979598471301E04707016BCA99905E -:1052800047093016B500024713F0470701606999D6 -:1052900090470506804702103488470F08470401ED -:1052A00000204704040002A8100847030A00836492 -:1052B000471580470401E04705020202A85C184731 -:1052C000030A037942804718D9F0470702003C10CF -:1052D00000E04704704704069E901B47110B4B806B -:1052E000E0470707C05C001000040807B04703014F -:1052F000A94F800A470E053203D42008B0024705A3 -:1053000008003C47020A470240A08047042801DD0C -:1053100080470F3003D56A7880024704300007C009 -:105320005C00044703A37847051C95471E3C4702D1 -:10533000A047090E01AD1A470E01604703784708E0 -:1053400017C05C00104708100617900A001847079E -:10535000020F4705A0014C470602900147021000CA -:10536000E04702080B38470534A01B4709920F7825 -:105370004705824D470601470204000C0010470311 -:1053800003BC000447020216950A471103D04702E6 -:105390002047040604003C2047032020F0804704F7 -:1053A00002378009471004001A7881470501041864 -:1053B0003C6047032020F78166800201802CE04793 -:1053C00003023FC04709024703D070002047040290 -:1053D0004706202020F001428080001382990902B4 -:1053E00000443FC04709010004001A8180470B01B7 -:1053F000002020F30366C00012A346054713404770 -:105400000A3C470280003020F047051E81002947F2 -:105410001004021A01E0470604183C60470420F714 -:1054200080E04702101E8CC0470E01470301C009EF -:10543000E02847097008002060F0804400800008E0 -:105440004E810A0080470B02470204021A79F004D9 -:10545000470B200020F3800700800004B4084712A7 -:1054600002D050A04707603C2047032060F08047EF -:1054700002500010038D471104000A50A00847068F -:10548000183C4047032020F7470410001D80470EBA -:1054900001470203C25010A04707229447048060CE -:1054A000F080470202003040C79F470D028000078E -:1054B000C0CAD04707081A9408470201E020F747FE -:1054C00003031030941F0E471003C06047020447C7 -:1054D000050C02940050A0006060F047052001ED2B -:1054E00080471007C1EE01E0804705080A9408478D -:1054F00002010020F7470306100014A547021847D1 -:105500000B01470203C040508447060C01680C5051 -:10551000A0003860F0470502810D1F40470C02478C -:105520000207C0EA00B84705020A0A940100A04732 -:105530000220F74702084000029EE00A471042A8F6 -:10554000103080004047112047040B47100AA85A2A -:10555000001000C0471150471543C0100080470895 -:105560003C470401010F34028047020142000E470C -:10557000100C3C5A01904707083CE04705073140B2 -:105580004702208155E0471123C0003047093C0005 -:10559000080040008000A0000347160C3C5A0001A0 -:1055A0004707083C6071804703512047070E4710AA -:1055B00023C0470201C047073C20008080000FF055 -:1055C000A14047170C3C5E4709083C400190470248 -:1055D0008FA5200280470401471123C010104709FE -:1055E0003C204703010103B8001000402042E09036 -:1055F00047100C3C4A99804707083C404704020783 -:1056000047022000100056879E471023C010104705 -:10561000093C00084702C04702014047031C16B17D -:1056200047110C3C4A984708083CC058470280473D -:1056300007102DD0471143C0470B3C470280004064 -:1056400047040347160C3C4E470201804705083CBF -:10565000C0001001470520470718470E43C2E04726 -:105660000A3C2010470501428047050D47100C3FBA -:10567000EE4709083CE098470602804705011047BD -:105680000C40471A6300294000100020470B704768 -:10569000088047111200A9408018470D40024704B6 -:1056A00008472720014704384712064706180040DC -:1056B000472503004003C20010472701470283D655 -:1056C0000018470E30470401470C04470540471BAC -:1056D00080470B02470402470240470F0F068047EE -:1056E00003083C000147050285470B0447020402FA -:1056F00081C047090102007A804703083D400080CD -:1057000047040102470B074703A804B18A470D2845 -:105710000002083C20014702C0470408403C000842 -:105720008000802A5034024702840281E108470C3D -:10573000064703083C6001E000C047034000183CF6 -:10574000605010148025A04702404702E804B98148 -:10575000470F50083C470B3C20014702402AA0401D -:10576000470201000280C0471050083D607A0001E6 -:10577000804705183D4000A001002AA730470204D9 -:1057800005E804F00A2018470C7000483C4707025F -:10579000700023C000700447022E7B3847030602C6 -:1057A00094E00E4710083CC047090BC140000A476F -:1057B0000222402800054702E826B00147031006F0 -:1057C000B04704807E80470301541847080800B0A2 -:1057D0001908D04705034047022A81470580BEF1DA -:1057E000984702020276803847022295EA01E100DA -:1057F000804704010A75E971F0470401E0400201A5 -:105800002A96E7804705D010470280AF0654300043 -:1058100002017C0B470601470202F43C470480501A -:10582000BC6047020430E6D047048286B01C4703C0 -:10583000053376684703147D4707800101417918D5 -:1058400008C00040A187E0470391E7B180470320EB -:1058500004A1470484128228470209680908804786 -:105860000702BC2A7180470401E047033E82091900 -:10587000470380A5E847030200F20E6C47020A9432 -:105880000139E1C0470714FA01C04705E047033E6C -:1058900094C009470303C14705AF0E4260020209E5 -:1058A00068087047080154087047034000FC47052A -:1058B00015910F470343C4A0470304053A46280542 -:1058C000000A940080470802955E800800040011D9 -:1058D00064470554A8470403C10100180008550A8D -:1058E000470420141C470601470202BC3A70804757 -:1058F0000205008002470303C0001E470343C5ADF5 -:105900009A4703A51F86470302BD6950A04705803B -:10591000470215EC80B04702050121404047028351 -:10592000D6000A470303C0050A4703403604204750 -:1059300002083C20084702804702024703D82F088C -:10594000F04702A8503C002047023C00D59B47038B -:1059500043C4880147020220D2A0644702083CE009 -:10596000504702C047040148D47A701000042475DF -:10597000F847043C15C38A470303C04703100004DB -:1059800003CC004040083C4708040001C019D84738 -:10599000030500BC0020008028B5918A470343C559 -:1059A000A01E001042205220600050283CE0798068 -:1059B000018047040102E8EBB80001800007A16004 -:1059C0000006001407E947021847023D000D0002D7 -:1059D00000050AC03C47047C10F0470702A809D024 -:1059E000A540002CBD746247022030CE8D47044094 -:1059F00021A01B4703A557542802002040F980B07E -:105A0000470401470202955D800847022C3DF7607C -:105A100040200020F4950A470472008011006200C3 -:105A20002F824702010347041000080010100202F1 -:105A3000040022002020000804240447070804472B -:105A4000032004002020202400082C040C0C470311 -:105A50000410100C00100008470308084703083022 -:105A60000030300400222606040800040400044725 -:105A7000042008000C000828000C0008002808007A -:105A80001008082808470228282847020C10101080 -:105A900024000224242C2C0400282C2101080011AD -:105AA0003010200004220222470320202030040866 -:105AB0003010001008000404040400040004470629 -:105AC00008000408080847020847022424040004C8 -:105AD0002C2C282847022101211004083010201006 -:105AE000470220002020080420202000044704044E -:105AF0000800040404040010101010100006081020 -:105B000008080302470204040802042424240400B1 -:105B10002C2C2820020021012100020A200028004C -:105B200008022828203004003030102000024702EC -:105B300004000A02040400140006101010080447B0 -:105B40000208080008020C040C2C02022C2C2C2C3D -:105B50000006282A2A470208220022000C022002FE -:105B600020204702202030300202507040000A0EF0 -:105B70000C080C044703040010080810100212045B -:105B80000002202020080824242400020247040ADE -:105B90000E47030204470206240608082C2428287E -:105BA0000202201030100E0A201028084702280098 -:105BB00020200A08203030180400021A0A040A0AB9 -:105BC0000C040C0C470208082808020C08082808D6 -:105BD0000C02280A280200080802200808002028D1 -:105BE0002010000C1010041008020404280C0408F3 -:105BF00028202121080031303020060E22222220C8 -:105C00000E0620470220040C101010000A47020460 -:105C10000404000C040447020A02470208000608B4 -:105C2000080A02080800060406040206082A284793 -:105C30000321213130060A303020200202202020AA -:105C4000000804002010000600100010000C0400E2 -:105C50000404040616141010100206181008080197 -:105C600047020804040A020404040604082A0C2A51 -:105C7000220A0223232322000822222A200A0228A1 -:105C80002A30100400101000080202000808000862 -:105C9000000C040C0C020618081818040008180858 -:105CA000080A020C0C0C0600080E04220E0A06223A -:105CB0002A22220008222222220C0222222222024E -:105CC0000032001010000240504000020C08080C86 -:105CD00004080A04044703081010121204000247C3 -:105CE00002200E04240424200C0E02000202020CE6 -:105CF0000202020204020602060602080A262A0816 -:105D0000080B303030300608202028284702820057 -:105D10008001030002282828000208082818180E0D -:105D2000091A1A0A080208080C0C0C00020C0022BE -:105D300008000E020A2202060302022202080802DA -:105D40000222020808222030100204101404040861 -:105D500000040008080408080001210200113010A6 -:105D600020080E022202000A084705021000101047 -:105D70000247021000040A0C04040006080802008E -:105D80000A0206000A0A020E0A00060606020004BB -:105D90000A0A0A02020013011130040A003000202E -:105DA00002470508061047030647050208100004CD -:105DB00047031414101000021810181801470208A5 -:105DC00004060200060606020C080A0A0A00080A6F -:105DD00003010320470202200A020A001A0A1A12CB -:105DE0000400081A08080002080808080800080C3F -:105DF0000C0C02040C001810040010180010000213 -:105E0000040606060808060202000A0C0002000248 -:105E100047032000220C020002120002001212129C -:105E200002470240424047020C08080804000204EE -:105E3000040447041012120400124703020004066F -:105E40000602470202020202470202470206040259 -:105E50000406060002000A021A0A000112120032A9 -:105E60000447022008200002080808080200080071 -:105E700008100403121A120847040402020206065C -:105E80000202000422020200060100020002470290 -:105E90002200020A47023A2A321A020404120404B7 -:105EA000470408080400080001010200111010470F -:105EB00002040202024708024702101002001010FA -:105EC0000010000604060602470202020A0006004D -:105ED000080A04020002040400020004080A0A106E -:105EE000020013131302044702024706104702067A -:105EF00047021000060010001047020A470214472C -:105F000003141410040002180018180001101206DF -:105F100016020006060200040008080847020201F3 -:105F2000010147041018100200181A1A0A04000A86 -:105F300002080200020008000847040404020404E6 -:105F40000400100400101012100002160606064786 -:105F50000302470202040808084706100402100062 -:105F600010100200100202020400020200020008E7 -:105F7000080808080002040404044704120204008C -:105F8000121010120200061606024703024706040A -:105F90000004000404001402001818181A0001126A -:105FA0000202020400024702080002080800080278 -:105FB00047050401120212104702101206120002D5 -:105FC00006060006000447022000060120002047C4 -:105FD000041018104702383A320A02040606060472 -:105FE0004702110162003F8247020103470802474E -:105FF00008024706010147062020470620204706E1 -:10600000042047064004470740470E8584470701A0 -:1060100047068047088047060202470602470802F3 -:10602000471E04470804471E0404471E2047072054 -:10603000204706202047060147080147360101474F -:10604000164247084247060202470E212047062013 -:1060500021470610204706141447065050470610E3 -:1060600010470610104706951147061094470690F2 -:106070009047061010470602104706020247260402 -:1060800004472604470804471E20470720204706E8 -:10609000202047060101472E01470801471E4242C2 -:1060A00047070247063020470630304706111147A0 -:1060B00006141047065014470610504706101047A4 -:1060C0000611104706101147069494470610904792 -:1060D0000610104706121047061212470610104706 -:1060E0000710470E04470804472E0404472E2020BB -:1060F0004706212047070147260101470882008003 -:106100000103472052024706305247063030470607 -:106110001010470611104706041547064040471661 -:106120000101470E84470804470E12024706121069 -:1061300047061012470610104706101047060414B1 -:10614000473604470804472E202047060121471EF2 -:10615000014708014716104707121047063012473B -:10616000067272470710470E04470741054707406C -:10617000470E014708014716040447060247080274 -:10618000470E020247061047071010470610104737 -:1061900006101047071047662120470721471601C0 -:1061A0000147161047071010470612124706203005 -:1061B0004706422047074247164140470701470E1E -:1061C000010147260202471602470802470E104700 -:1061D00007101047061010470710475E2047072199 -:1061E000214708110262002F824702010347081B62 -:1061F0001B47041F1B4704041F47101B47051B1B9D -:1062000047041F1B47041F1F47101B1B47041B1B72 -:1062100047041F1F47041F1F47051B470A1B1B4737 -:10622000051B4704040447041F1F47051B470A1B9F -:106230001B4704E4FB4704E4E447041FFF47051B36 -:10624000470AF9194704E4F94704E4E44704F7F777 -:10625000470513470AF9F94704E4F94704E4E4471A -:1062600004F7F74705F3470AF9E9470414FD470423 -:106270001E1C4704F7FF4705E1470AE9E947041CEC -:10628000FD47041E1A4704EFFF4705E1470ADDC931 -:1062900047041EDD47043E3A4704BDBF4705A947F2 -:1062A0000ADDD94704A2FF4704A6A24704BDBD47A3 -:1062B00005B9470AFFDD4704A6FF47048486470463 -:1062C000BDBD470404BD470AFFFD470406FF470460 -:1062D000040447043D3D4704043D470AFFFF4704CB -:1062E00006FF470404044704FDFD470404FD470A74 -:1062F000FBFB4705FB4704244705FDFD4705F94720 -:106300000AFBFB4704FBFB4704FFFB4704FFFF4777 -:1063100005FB470AFB224704FBFB4704DFFB47045E -:10632000FFFF470522470A22024704FBFB4704DF21 -:10633000DB470426FF4705224780028200800103D5 -:1063400047022C2C47041F3E47041F1F4704CCDE86 -:106350004705C8470A3E2C47041F3F47049F1F4775 -:1063600004DEDE4705C8470A3F3E47041F3F470497 -:106370009F9F4704DEDE4705DA470A3736470401A8 -:10638000374704898147045ADA470552470A763667 -:106390004704C1F64704A9814704FAFA4705D247E2 -:1063A0000A74744704C1F44704A9E94704F2FA47A0 -:1063B00005F2470A74744704E5F44704EDED470419 -:1063C000F2FE4705F2470A7474470427744704CF66 -:1063D0008F470476FE470570470A747447041F749C -:1063E00047049F9F470474F6470574470A7464473F -:1063F000041B7447049B9B470476F6470574470AC1 -:1064000060604704197047048999470476F6470588 -:1064100076470A6060470409604704898947047623 -:10642000F6470576470A64644704096C4704818986 -:10643000470476F6470576470A76764704097E478D -:10644000048189470476F6470576470A767647043D -:10645000297E4704C189470476F6470576470A77BF -:106460007647047F7F4704F7FF470477F7470576B1 -:10647000470A776047045F7F4704B7FF470477F711 -:10648000470560470A612047045F7F4704B79F477D -:106490000461F7470540478002110362003F8247CD -:1064A00002010347838282008001034783822244E2 -:1064B000B2010600470000000000000000000000DC -:1064C00000040240000400000A0009000014024019 -:1064D000000400000A000900000C0240000800004F -:1064E0000B00090000080240000400000A00090037 -:1064F0000014024080000000070009000000024074 -:106500000200000001000900001C02402000000001 -:1065100001000000001C0240080000000100000013 -:106520000000024000080000010000000000000818 -:106530000080000800000108008001080000020837 -:10654000000004080000080800000C080000100803 -:10655000000014080000180800001C0800000800D3 -:1065600010001800200028003000380040004800CB -:1065700050005800000000006410B71DC8206E3B9A -:10658000AC30D9269041DC76F4516B6B5861B24D3A -:106590003C7105502083B8ED44930FF0E8A3D6D6A4 -:1065A0008CB361CBB0C2649BD4D2D38678E20AA00C -:1065B0001CF2BDBD4932435F504D49435F4D4147D9 -:1065C00000537475636B20627574746F6E2072650E -:1065D00067697374657220697320696E76616C698E -:1065E000642C20636C656172696E672E004275745D -:1065F000746F6E2069642000697320737475636B17 -:106600002100427574746F6E20776173207075730A -:10661000686564206F6E20626F6F742E20427574FF -:10662000746F6E20636F756E7465723A20004469F2 -:1066300073706C617920627573792D77616974204C -:1066400074696D656F757420657870697265642111 -:10665000004650474120636F6E66696775726174CA -:10666000696F6E206661696C65642E0044697370A1 -:106670006C617920696E697469616C697A656420FE -:106680006166746572200020726574726965732E8C -:1066900000446973706C617920696E697469616C1A -:1066A000697A6174696F6E206661696C65642E0039 -:1066B000435245534554206E6F74206C6F772064AD -:1066C0007572696E672072657365740043444F4E3E -:1066D00045206E6F74206C6F772061667465722040 -:1066E000726573657400435245534554206E6F7450 -:1066F00020686967682061667465722072657365D9 -:10670000740043444F4E45206E6F7420686967687B -:106710002061667465722070726F6772616D6D6959 -:106720006E6700456E61626C696E67203676362052 -:1067300028446973706C61792056444443290045AC -:106740006E61626C696E6720347635202844697307 -:10675000706C617920564444502900446973616229 -:106760006C696E67203476352028446973706C61DB -:10677000792056444450290044697361626C696E03 -:1067800067203676362028446973706C617920560C -:1067900044444329002E2E2F7372632F647269764E -:1067A0006572732F6932632F6932632E63002E2E58 -:1067B0002F7372632F647269766572732F69326307 -:1067C0002F6932635F68616C2E63004661737420C9 -:1067D0004D6F646520506C7573206E6F7420796501 -:1067E0007420737570706F72746564002E2E2F7331 -:1067F00072632F647269766572732F706572697047 -:10680000685F636F6E6669672E63006164647265BA -:1068100073732000206973206F7574736964652039 -:1068200073797374656D20666C6173680066616965 -:106830006C656420746F2065726173652073656395 -:10684000746F72200050726F6772616D20666169AB -:106850006C6564204000496E76616C6964206669ED -:10686000726D7761726520646573637269707469B3 -:106870006F6E2100436865636B73756D6D696E673C -:10688000206669726D7761726520757064617465E8 -:106890000043616C63756C61746564206368656353 -:1068A0006B73756D3A2000496E76616C6964206681 -:1068B00069726D776172652043524320696E20537F -:1068C000504920666C61736821007072765F657252 -:1068D0006173655F6F6C645F6669726D7761726525 -:1068E000007072765F77726974655F6E65775F6658 -:1068F00069726D77617265005765277265206465FE -:10690000616400436865636B73756D6D696E6720C4 -:10691000002062797465730D0A00436865636B73C8 -:10692000756D202D2077616E746564200020676F7F -:106930007420004F757220696E7465726E616C20F0 -:10694000666C61736820636F6E74656E747320612A -:106950007265206261642028636865636B73756D7E -:10696000206661696C656429212054686973206917 -:1069700073207265616C6C792062616421004F75CF -:10698000722070726576696F7573206669726D77B3 -:1069900061726520757064617465206661696C65FB -:1069A000642C2061626F7274696E67207570646117 -:1069B00074652E004E6577206669726D7761726529 -:1069C00020697320617661696C61626C6521004C9D -:1069D0006F6164696E67207265636F766572792096 -:1069E0006669726D77617265004661696C656420E5 -:1069F000746F206C6F6164207265636F7665727965 -:106A0000206669726D776172652C20737472696B90 -:106A100065206F6E652E2054727920616761696E02 -:106A20002E004661696C656420746F206C6F616430 -:106A3000207265636F76657279206669726D776121 -:106A400072652C20737472696B652074776F2E20C9 -:106A500054727920616761696E2E004661696C65C8 -:106A60006420746F206C6F6164207265636F76655B -:106A70007279206669726D776172652C2073747209 -:106A8000696B652074687265652E205341442057F8 -:106A9000415443480048415244204641554C54001B -:106AA00065786974207374616E646279000D0A0DF3 -:106AB0000A0D0A00E29688E29688E29688E29688B5 -:106AC000E29688E29688E295972020E29688E29600 -:106AD00088E29688E29688E29688E29688E2959720 -:106AE00020E29688E29688E29688E29688E2968886 -:106AF000E29688E2959720E29688E29688E2968868 -:106B0000E29688E29688E29688E29688E29597E295 -:106B10009688E29688E29688E29688E29688E296DF -:106B200088E2959720E29688E29688E29688E29637 -:106B300088E29688E29688E29688E29688E29597BF -:106B400000E29688E29688E29594E29590E295902C -:106B5000E29688E29688E29597E29688E29688E245 -:106B60009594E29590E29590E29590E29688E2966F -:106B700088E29597E29688E29688E29594E295906D -:106B8000E29590E29688E29688E29597E29688E20E -:106B90009688E29594E29590E29590E29590E29540 -:106BA00090E2959DE29688E29688E29594E295902F -:106BB000E29590E29688E29688E29597E2959AE2CD -:106BC0009590E29590E29688E29688E29594E29517 -:106BD00090E29590E2959D00E29688E29688E29692 -:106BE00088E29688E29688E29688E29594E2959DFE -:106BF000E29688E29688E29591202020E29688E24B -:106C00009688E29591E29688E29688E29688E296E6 -:106C100088E29688E29688E29594E2959DE29688CD -:106C2000E29688E29688E29688E29688E295972036 -:106C300020E29688E29688E29688E29688E2968834 -:106C4000E29688E29594E2959D202020E29688E2E3 -:106C50009688E2959120202000E29688E29688E2CC -:106C60009594E29590E29590E29688E29688E29576 -:106C700097E29688E29688E29591202020E2968815 -:106C8000E29688E29591E29688E29688E29594E20F -:106C90009590E29590E29688E29688E29597E29642 -:106CA00088E29688E29594E29590E29590E2959D2F -:106CB0002020E29688E29688E29594E29590E2950B -:106CC00090E29688E29688E29597202020E29688C6 -:106CD000E29688E2959120202000E29688E296884C -:106CE000E295912020E29688E29688E29591E295DD -:106CF0009AE29688E29688E29688E29688E29688FA -:106D0000E29688E29594E2959DE29688E29688E282 -:106D10009688E29688E29688E29688E29594E295D3 -:106D20009DE29688E29688E29688E29688E29688C6 -:106D3000E29688E29688E29597E29688E29688E263 -:106D400095912020E29688E29688E2959120202075 -:106D5000E29688E29688E2959120202000E2959ABA -:106D6000E29590E2959D2020E2959AE29590E29539 -:106D70009D20E2959AE29590E29590E29590E295B9 -:106D800090E29590E2959D20E2959AE29590E295A9 -:106D900090E29590E29590E29590E2959D20E295A3 -:106DA0009AE29590E29590E29590E29590E2959026 -:106DB000E29590E2959DE2959AE29590E2959D206C -:106DC00020E2959AE29590E2959D202020E2959A06 -:106DD000E29590E2959D202020004C61737420661E -:106DE00069726D7761726520626F6F74207761736D -:106DF00020737461626C653B20636C656172207303 -:106E00007472696B657300486F6C6420646F776E91 -:106E1000205550202B204241434B202B2053454CE2 -:106E200045435420666F72203520736563732E20AE -:106E3000746F20666F7263652D626F6F742050529D -:106E400046004669726D77617265206973206572CC -:106E500061736564005761746368646F6720636180 -:106E600075736564206120726573657400536F6685 -:106E70007477617265206661696C75726520636103 -:106E8000757365642061207265736574004661697D -:106E90006C656420746F207374617274206669720B -:106EA0006D776172652C20737472696B65207468EC -:106EB0007265652E004661696C656420746F20738D -:106EC00074617274206669726D776172652C2073CB -:106ED0007472696B652074776F2E004661696C650A -:106EE0006420746F207374617274206669726D77A8 -:106EF0006172652C20737472696B65206F6E652EEC -:106F000000466F7263652D626F6F74696E672072E1 -:106F100065636F76657279206D6F64652E2E2E0025 -:106F2000456E61626C696E67207761746368646F37 -:106F30006700426F6F74696E67206669726D776172 -:106F40007265204020002E2E2E0D0A0D0A00426F81 -:106F50006F7420626974733A2000536F66747761AE -:106F60007265206661696C7572653B207265736538 -:106F70007474696E6721004153534552543A20009E -:106F80003A00415353455254004153534552544ED5 -:106F9000002A2A2A20575446200053544D333200E9 -:106FA00053544D3332207065726970686572616C3C -:106FB000206C6962726172792074726970706564A4 -:106FC00020616E206173736572740069746F612053 -:106FD00062756666657220746F6F20736D616C6C8C -:106FE000002E2E2F7372632F7574696C2F736C656E -:046FF0002E6300000C -:106FF400FF00000000010203040102030406070865 -:04700400090000007F -:0400000508000200ED -:00000001FF diff --git a/bin/boot/boot_robert_bb@1478015115.bin b/bin/boot/boot_robert_bb@1478015115.bin deleted file mode 100755 index ad5cb98e67..0000000000 Binary files a/bin/boot/boot_robert_bb@1478015115.bin and /dev/null differ diff --git a/bin/boot/boot_robert_bb@1478015115.hex b/bin/boot/boot_robert_bb@1478015115.hex deleted file mode 100644 index b548adf66e..0000000000 --- a/bin/boot/boot_robert_bb@1478015115.hex +++ /dev/null @@ -1,1798 +0,0 @@ -:020000040800F2 -:1000000000AA0120E1250008DD2500081D1E0008CA -:10001000DD250008DD250008DD25000800000000C2 -:10002000000000000000000000000000DD250008C6 -:10003000DD25000800000000DD250008DD250008A2 -:10004000DD250008DD250008DD250008DD25000888 -:10005000DD250008DD250008DD250008DD25000878 -:10006000DD250008DD250008DD250008DD25000868 -:10007000DD250008DD250008DD250008DD25000858 -:10008000DD250008DD250008DD250008DD25000848 -:10009000DD250008DD250008DD250008DD25000838 -:1000A000DD250008DD250008DD250008DD25000828 -:1000B000DD250008DD250008DD250008DD25000818 -:1000C000DD250008DD250008DD250008DD25000808 -:1000D000DD250008DD250008DD250008DD250008F8 -:1000E000DD250008DD250008DD250008DD250008E8 -:1000F000DD250008DD250008DD250008DD250008D8 -:10010000DD250008DD250008DD250008DD250008C7 -:10011000DD250008DD250008DD250008DD250008B7 -:10012000DD250008DD250008DD250008DD250008A7 -:10013000DD250008DD250008DD250008DD25000897 -:10014000DD250008DD250008DD250008DD25000887 -:10015000DD250008DD250008DD250008DD25000877 -:10016000DD250008DD250008DD250008DD25000867 -:10017000DD250008DD250008DD250008DD25000857 -:10018000DD250008DD250008DD250008DD25000847 -:10019000DD250008DD250008DD250008DD25000837 -:1001A000DD250008DD250008DD250008DD25000827 -:1001B000DD250008DD250008DD25000861070008B1 -:1001C0006D070008DD25000800000000DD2500089F -:1001D000DD250008DD250008DD250008DD250008F7 -:1001E000DD250008DD250008DD250008DD250008E7 -:0801F000DD250008DD250008F3 -:0801F8009C0000000100000062 -:1002000053B94AB9002908BF00281CBF4FF0FF317D -:100210004FF0FF3000F03CB882B0EC462DE90050C2 -:1002200000F01EF8DDF804E002B00CBC704700BF1F -:100230002DE9F041904606460F461D46069C00F00B -:1002400029F808FB01FC8646A8FB002300FB05C536 -:10025000B21A2B4467EB0303C4E90023BDE8F08125 -:100260002DE9F8431D46174680468946089E00F052 -:1002700053F900FB05F3A0FB074507FB0137B8EB7B -:1002800004043D4469EB0505C6E90045BDE8F88373 -:10029000704700BF00292DE9F047C0F2A280002678 -:1002A000002BC0F298808C4690469E461546044628 -:1002B0000F46CBBB8A4256D9B2FA82F33BB1C3F1A7 -:1002C00020029F409D409C4020FA02F21743280CD8 -:1002D000220C1FFA85FEB7FBF0F100FB11770EFB35 -:1002E00001F342EA0747BB4207D97F1980F0018139 -:1002F000BB4240F2FE8002392F44FF1AA4B2B7FB82 -:10030000F0F300FB13770EFB03FE44EA0747BE45FC -:1003100006D97F1980F0EB80BE4540F2E880023BB1 -:1003200043EA0143002203E08B420FD90022134627 -:10033000341C4FF0000518BF0124604265EB4501F5 -:100340005840514000196941BDE8F087B3FA83F283 -:10035000002A40F08380804540F2CD808B42C0F07F -:10036000CA801346E4E712B90123B3FBF2F5B5FAEC -:1003700085F2002A3BD1781B4FEA154E1FFA85FC07 -:100380000122210CB0FBFEF80EFB18000CFB08F359 -:1003900041EA0047BB4208D97F1980F0B080BB42D8 -:1003A00040F2AD80A8F102082F44FF1AA4B2B7FBB7 -:1003B000FEF30EFB13770CFB03FC44EA0747BC4536 -:1003C00006D97F1980F09980BC4540F29680023BA7 -:1003D00043EA0843ACE752426FEA060663EB430385 -:1003E00061E740424FF0FF3661EB410158E795402D -:1003F000C2F1200107FA02F34FEA154ECF4024FA6A -:1004000001F194401FFA85FC1943B7FBFEF24FEA55 -:1004100011480EFB12770CFB02F348EA0747BB4278 -:1004200005D97F1971D2BB426FD9023A2F44FF1A06 -:1004300089B2B7FBFEF80EFB18770CFB08F041EA17 -:100440000743984206D95B1961D298425FD9A8F157 -:1004500002082B44181A48EA024292E7C2F1200728 -:1004600003FA02FE08FA02F5914028FA07F32CFA83 -:1004700007FCF84043EA0E0E08434FEA1E48070CFB -:100480001FFA8EFABCFBF8F908FB19CC0AFB09F13C -:1004900047EA0C4C614507D91CEB0E0C32D2614582 -:1004A00030D9A9F10209F444C1EB0C0C80B2BCFBB9 -:1004B000F8F308FB13CC0AFB03FA40EA0C418A4527 -:1004C00006D911EB0E0125D28A4523D9023B71448E -:1004D00043EA0943CAEB0101A3FB0589494503D35C -:1004E00003D19440444500D2013B002220E7013B68 -:1004F00016E7013901E701231AE7013B68E708F134 -:10050000FF3852E709F1FF39CEE7013A8FE708F1EA -:10051000FF389FE7013BDBE72DE9F043002B40D19B -:100520008A42044615464AD9B2FA82F30F464BB1C5 -:10053000C3F12006994000FA03F402FA03F5F040F3 -:1005400040EA0107290C260C1FFA85FEB7FBF1F0E3 -:1005500001FB10770EFB00F246EA07439A4207D9E7 -:100560005B1980F0EA809A4240F2E78002382B441F -:100570009A1AA4B2B2FBF1F301FB13220EFB03FEA5 -:1005800044EA0242964506D9521980F0DA8096452F -:1005900040F2D780023B43EA004000263146BDE8E6 -:1005A000F0838B4244D8B3FA83F6002E45D18242C1 -:1005B00040F2BF808B42C0F0BC803046EEE712B9FB -:1005C0000125B5FBF2F5B5FA85F2002A7BD14A1B6D -:1005D0002F0C1FFA85FE0126230CB2FBF7F007FB58 -:1005E00010220EFB00FC43EA02418C4507D9491951 -:1005F00080F0A1808C4540F29E8002382944CCEBEB -:100600000101A4B2B1FBF7F307FB13110EFB03FECC -:1006100044EA0144A64506D9641980F09080A645B5 -:1006200040F28D80023B43EA00403146BDE8F08352 -:10063000002630463146BDE8F083C6F12005B340C0 -:1006400002FA06F701FA06F4EA40E94020FA05F555 -:100650001A4325434FEA124C4FEA154E93B2B1FBB1 -:10066000FCF80CFB181103FB08F44EEA01418C4224 -:1006700006D9891869D28C4267D9A8F102081144B9 -:10068000091BADB2B1FBFCF40CFB141103FB04FE1F -:1006900045EA01439E4505D99B1854D29E4552D93F -:1006A000023C134444EA0844CEEB0303A4FB07894D -:1006B0004B4503D351D1B04040454ED20026601E79 -:1006C0003146BDE8F083C2F12006954001FA02F3FD -:1006D00000FA02F42F0CF140F0401FFA85FEB1FB46 -:1006E000F7F6184307FB16110EFB06F24FEA104C03 -:1006F0004CEA01439A4205D95B1929D29A4227D97B -:10070000023E2B449B1A80B2B3FBF7FC07FB1C3361 -:100710000EFB0CF140EA0343994206D95B1919D24A -:10072000994217D9ACF1020C2B445A1A4CEA0646EE -:1007300052E7012032E7013861E7013818E7013B51 -:1007400071E7013B27E7013CACE708F1FF3897E789 -:10075000013ED7E70CF1FF3CE7E7204600261DE706 -:10076000014800F0D6BF00BFE42B0008014801F0AB -:1007700038B800BFE42B0008014800F0F9BD00BF05 -:10078000E42B000837B50024012002F007FA01909D -:100790002546E0B200F046F801AA08B910551DE060 -:1007A000135D042B0BDD174800F03CF9019800F0B5 -:1007B00040F90120002102F0E1F9002020E001339E -:1007C000DBB2042B135509D90F48012500F012F9AB -:1007D000204600F02EF90D4800F024F90134042CD5 -:1007E000D7D1019C3CB10A4800F004F9204600F042 -:1007F00020F900F08FF80120214602F0BFF92846C9 -:1008000003B030BDC1650008ED650008F86500085B -:100810000266000808B500F077FB80F00100C0B266 -:1008200008BD000008B50C22044B02FB003000F0AC -:100830008DFC80F00100C0B208BD00BF842C000810 -:1008400010B5002401F094F8054820440C34017AD6 -:1008500000F062FC302CF7D1BDE8104001F089B8FF -:10086000842C0008044BDA691106FBD59862DA691A -:100870001206FCD5704700BF0048004070B5214DFE -:100880008AB02148082101F075F828464FF48021EC -:100890001E4C01F06FF82B68642623F0010394E8E6 -:1008A00007002B600C348DE8070000216846022207 -:1008B0000B4600F013FC03AB94E8070083E8070045 -:1008C00000211846022200240B4600F007FC06A86F -:1008D00002F07AF8089821460D4A70430023FFF78A -:1008E0008FFCC0F3420320F00F000004000C1843FB -:1008F0000D23B0FBF6F0E8606C60AC602B600AB0D2 -:1009000070BD00BF00480040000C0240B42C00083D -:1009100040420F002DE9F043334D83B02C6800AF07 -:1009200004F12E0304F1270823F00703ADEB030DB8 -:1009300050238DF8003021236E460DF106028DF80C -:10094000013000238DF8023003238DF8033004F1C9 -:1009500021035BBAADF80430244B03F11C0153F8BA -:10096000040B8B4242F8040BF9D11B7804F12309E4 -:1009700006F123001E491370224602F01FF931468A -:100980004A46002001F070FD07F1080304F5927457 -:10099000314643F8040D42461868FE23B4FBF3F3D6 -:1009A000434446F80900073323F00703ADEB030D7A -:1009B00068466E4601F030FD044655206C44FFF752 -:1009C00051FFA64207D016F8010B552808BF00209A -:1009D000FFF748FFF5E75520FFF744FF00230C37EA -:1009E0002B60BD46BDE8F08338000020CC2C000809 -:1009F0003C00002010B5441E14F8012F7AB10849BC -:100A00000B68FF2B0BD80A2A02D1FFF783FFF3E70D -:100A10000D2A1FBF581C5B1808601A71ECE710BD47 -:100A20003800002008B5FFF7E5FFBDE80840FFF7F4 -:100A300071BF1FB50C2201A901F044FD01A8FFF709 -:100A4000D9FF05B05DF804FB07B502A9012201F842 -:100A5000010D042000F096F903B05DF804FB0021BD -:100A600002200A4600F08EB910B540F2F51400F0ED -:100A7000B9F850B1013C04D10548FFF7D3FF204637 -:100A800010BD642001F010FDF1E7012010BD00BF92 -:100A90002E66000810B5074C43F2CD71064848F6A3 -:100AA000B803224601F056FD01462046BDE810403D -:100AB00000F0E4B83C010020E92C000810B500F07B -:100AC00099F8FFF7E7FF08B9154816E0002400F091 -:100AD00039F9FFF7C4FF0120FFF7B6FFFFF7C4FFA6 -:100AE00078B1002103200A4600F04CF90D48FFF7C9 -:100AF00081FF2046FFF79DFF0B48BDE81040FFF740 -:100B000091BFFFF7C7FF0028DED001340B2CE2D1E4 -:100B10000648FFF787FFBDE81040FFF7A0BF00BF02 -:100B2000516600086C660008876600089166000838 -:100B300008B5FFF799FF0120FFF786FFFFF794FF45 -:100B4000002103200A46BDE8084000F01BB9000060 -:100B50002F2307B54A1C58430B4B00EB52001A7861 -:100B6000B0FBF1F0C0B282420AD002A91870042290 -:100B700041F8040D012000F005F90220FFF764FFA1 -:100B800003B05DF804FB00BF0000002007B502A918 -:100B9000042241F8040D012000F0F4F80320FFF7CF -:100BA00053FF03B05DF804FBFFF759BF0C4B014442 -:100BB00030B51C68884207D022689568AD07FCD51F -:100BC00010F8015B1573F5E71B681A68936813F456 -:100BD000C05FFBD10A20BDE8304001F065BC00BF1A -:100BE000342C0008024B1868403000F0AFBA00BF48 -:100BF000342C000838B5204B012200211C6804F178 -:100C00002005284600F03CFA0021284600F052FA60 -:100C100004F1380000F094FA04F1400000F090FA7A -:100C2000012104F12C000A4600F02AFA002104F107 -:100C3000080001220B4600F051FA0021012204F1C4 -:100C400014000B4600F04AFA6068012101F06EFFC3 -:100C50006068002101F06AFF94E8030000F08AFE5A -:100C600023684CF207321A604FF4B8525A601A687F -:100C700042F040021A6038BD342C00082B4B2DE99D -:100C8000F7431C68814601208846E36A04F1200787 -:100C900004F12C060093236B019301F009FC384604 -:100CA000012100F007FA0021304600F003FA01208C -:100CB00001F0FEFB684600F049FA054620B11C48E9 -:100CC0000025FFF7AFFE2CE030460121383400F05C -:100CD000F1F9012001F0ECFB204600F037FA06465E -:100CE00008B114481BE0684600F030FA054608B920 -:100CF000114814E0012001F0DBFB48464146FFF7B4 -:100D000055FF3846314600F0D5F90C480921FFF768 -:100D10004DFF204600F01AFA054610B90848FFF7C3 -:100D200081FE284603B0BDE8F08300BF342C0008E4 -:100D3000B0660008CC660008E6660008B6640008E5 -:100D40000267000808B5022001F0B2FB0A4B1B68DD -:100D500093F8483043B10948FFF764FE012000F0E2 -:100D6000D3FD022001F0A4FB0548FFF75BFE012044 -:100D7000BDE8084000F0C2BD342C0008236700081D -:100D80003F6700080F4BF7B51C6802AD0F46012105 -:100D9000203405F8010D1646204600F08BF964203A -:100DA00001F082FB28460121FFF700FF1EB1384603 -:100DB0003146FFF7FBFE2046002100F07BF903B02F -:100DC000F0BD00BF342C0008F0B50746ADF2044D6D -:100DD0000E460C460025B919B4F5806F6846A1EBA4 -:100DE00004010DD94FF48062A4F5806400F0CCF8C2 -:100DF000284669464FF4806201F036FB0546EAE773 -:100E0000224600F0C1F869462246284601F02CFB34 -:100E10000DF2044DF0BD08B5202001F09FFD002823 -:100E2000FAD108BD08B5022001F098FD0028FAD0DB -:100E30000220BDE8084001F09BBD00B58DB06846BA -:100E400001F015FD0023684600934FF480730A9368 -:100E500035230B9301F03CFDFFF7E4FFFFF7DBFFC9 -:100E60000DB05DF804FB000030B5002495B02448B7 -:100E7000022100F07FFD23480021032220440C348E -:100E80000B4600F02BF9482CF5D101A8002401F005 -:100E9000E5FC102301A802940193172303944FF457 -:100EA0004075059304940694079401F0EFFC01202B -:100EB00001F032FDFFF7C1FF08A801F0D8FC66235E -:100EC00008A808941393129501F002FDFFF7AAFFFA -:100ED00008A801F0CCFC992308A808941393129554 -:100EE00001F0F6FCFFF79EFF4CF2503001F0DCFA07 -:100EF000FFF7A3FF0248022100F046FD15B030BD08 -:100F0000001000A0C064000830B502218DB01C485C -:100F100000F030FD022001F013FD684601F0A7FC4F -:100F20004FF08063684600934FF0407304934FF492 -:100F300040730A93AF230B9301F0CAFCFFF772FFD3 -:100F400001F006FD054601F003FD044601F000FD39 -:100F5000240244EA004040EA0504FFF75CFF094B25 -:100F60009C4204D020460024FFF763FD00E00124EA -:100F70000348022100F008FD20460DB030BD00BF3F -:100F8000001000A020BB190070B515468CB00446B7 -:100F90000E461B48022100F0EDFC681E01F0D0FC5B -:100FA000684601F064FC4FF0806368462544009376 -:100FB0004FF0407304930A2305934FF44063099361 -:100FC0004FF440730A934FF4005308930B230B9391 -:100FD00001F07EFC304601F0ABFCAC4204D001F0E5 -:100FE000B7FC04F8010BF8E7022001F0C1FCFFF7A1 -:100FF00012FF0348022100F0C7FC0CB070BD00BF17 -:10100000001000A0034A136B23F4FF6323F00703CF -:10101000136370470038024000F13F4000F57E0046 -:10102000C0F387200A280AD8054A135C591C1154BA -:101030002BB9044901230A6B834013430B637047A8 -:10104000F48900200038024000F13F4000F57E00A6 -:10105000C0F387200A280DD8074A135C53B1013B1F -:10106000DBB2135433B9054901230A6B834022EAEA -:1010700003030B63704700BFF4890020003802406F -:10108000436813B500930123044600688DF80430CB -:1010900000238DF805208DF807308DF80610FFF736 -:1010A000BBFF2068694601F063FB2068FFF7CCFFB7 -:1010B00002B010BD38B5037A04460D460BB981F075 -:1010C00001052068FFF7A8FF2068A1882A4601F0E3 -:1010D0008DFB2068BDE83840FFF7B6BF13B5044666 -:1010E00040688DF80730009002208DF805208DF8BB -:1010F000040020688DF80610FFF78EFFA27A2068A2 -:10110000218901F077FB2068694601F031FB2068F6 -:10111000FFF79AFF02B010BD002313B504460193F8 -:101120004368006800938DF80710FFF775FF20688B -:10113000694601F01DFB2068FFF786FF02B010BD75 -:10114000036813B10021FFF7E7BF704738B50446C5 -:101150000068FFF761FFA188206801F041FB0546A8 -:101160002068FFF771FF281C18BF012038BD1FB58C -:10117000836804460093C36868460193FFF7E0FF65 -:10118000636902A80293A3690393FFF7D9FF04B030 -:1011900010BD10B504460121083002220023FFF7DC -:1011A0009DFF04F11400012102220023BDE810403C -:1011B000FFF794BF0368DB6910B5044673B9C36ACF -:1011C00063B10A2001F074F92046FFF7D0FF2046F2 -:1011D000E36A01219847142001F06AF92046FFF7DD -:1011E000D8FF2046BDE8104000F0E0B910B5044635 -:1011F00000F04FFA48B10A2001F05AF9204600F0F9 -:1012000048FA80F00100C0B210BD012010BD30B519 -:10121000044687B0C36A00219847A36800210125CE -:1012200000936846E3680A468DF808500193FFF77B -:1012300027FF68460021FFF73DFF6369002103A8EF -:101240000393A3690A468DF814500493FFF718FF1F -:1012500003A80021FFF72EFF07B030BD10B50446EC -:1012600000F00AFA2368DB6933B9E36A4BB1204620 -:10127000BDE81040FFF7CBBF2046BDE81040FFF7A8 -:1012800076BF10BD10B50446FFF7E8FF2046BDE865 -:101290001040FFF78FBF00002DE9F3419846036827 -:1012A00004460E46DB6917460BB900205BE000F0F0 -:1012B000F0F9B8B9256814220021284601F089FC0C -:1012C0009DF828306F8020466B71089B2E71AB60B3 -:1012D000099B85F806802B6100F0ECF92368002259 -:1012E0009A6111E02046FFF7CDFF2046FFF77EFF11 -:1012F0000028DFD1D9E79A69B2F57A7F23DA013283 -:1013000002209A6101F0D4F8204600F0DFF90122B2 -:10131000236883F82020154A0192019A511E0191F9 -:101320001AB193F82020002AF7D1002283F8202058 -:10133000019A42B11D7D05F0FD02012ADBD16B1E31 -:101340005D425D4103E00025204600F0A8F92368D6 -:101350000022204683F82020FFF748FF10B92046DE -:10136000FFF790FF284602B0BDE8F08180841E00A0 -:1013700010B5044618B90E48A92101F015F800214E -:101380002422006801F025FC204600F0CBF8236AF7 -:101390002BB1002104F120000A46FFF771FEE36A39 -:1013A00023B12046BDE81040FFF731BF10BD00BF9C -:1013B0009567000810B5044618B90848B62100F032 -:1013C000F3FF00680368DB690BB9FFF7F3FE2368DE -:1013D0001A68D3690133D36110BD00BF9567000857 -:1013E00008B518B90748BF2100F0DEFF00680268A1 -:1013F000D36933B1013BD3611BB9BDE80840FFF7A6 -:101400002DBF08BD956700081FB50C46114618B9D9 -:101410000A4840F28D1103E023B908484FF4C77120 -:1014200000F0C2FF82888DE80A0000212346029165 -:101430000068FFF731FF04B010BD00BF95670008DA -:1014400013460122FFF7E0BF1FB50C46114618B93D -:101450000A484FF4D77103E023B9084840F2AF11AE -:1014600000F0A2FF82888DE80A0000230121029388 -:1014700023460068FFF710FF04B010BD9567000811 -:1014800007B502AB03F8012D0122FFF7DDFF03B022 -:101490005DF804FB03680022197583F8202070476B -:1014A00010B5046863882279C3F3090322B922784E -:1014B000012A18BF43F48063A268E468121BFF2A64 -:1014C00095BF120443F0FF7302F47F0243F48033AC -:1014D00098BF134309B143F4005342681268536044 -:1014E00010BD43681B681A6822F0D60210B51A6056 -:1014F0005A6842F480425A6008220368BDE81040EE -:101500001A70FFF7C7BF0368012110B50446586879 -:1015100001F000FB00212368BDE81040586801F08D -:10152000F9BA000043685A7B53B2002B30B506DA93 -:1015300002F00F021B4B1344C0221A7606E003F19F -:101540006043C02203F5614383F8002343689A7B1C -:1015500053B2002B06DA02F00F02124B1344C022E2 -:101560001A7606E003F16043C02203F5614383F875 -:10157000002350F8045F0C4C6B7B59B203F01F0240 -:101580000123490903FA02F244F82120AA7B51B24F -:1015900002F01F024909934044F82130BDE8304071 -:1015A000FFF7B1BFFCEC00E000E100E043682C492C -:1015B0009A688A42F0B5074687B004D92948512174 -:1015C000294A00F0E5FE29498A4205D91C7B34B13D -:1015D000012C07D000F0F2FE0124254603E0092487 -:1015E000102500E0022502A801F0EEF979686019E3 -:1015F000049A8B684343B2FBF3F603FB162303B94B -:10160000013E0F2E03D91748702100F0CDFE03C90B -:1016100000F0B0F97B684FF0000100221B68013D2B -:1016200066F307110192013C8DF804508DF8071004 -:1016300019688DF8054021F0010119600C491868FE -:1016400001401960019919619A60DA601A6842F0E4 -:1016500001021A60074A59680A405A6007B0F0BD93 -:10166000801A0600AE670008CB670008A08601005C -:10167000001F30FF008000F810B5044654F8043F06 -:1016800093E8030000F080F92046BDE81040FFF722 -:101690003ABF43681B689869C0F3C030704743681D -:1016A0001B681A6822F0D6021A605A6842F4804217 -:1016B0005A60704703685A790AB9012204E01A791E -:1016C0000AB9052200E006221A70704743681B68B9 -:1016D0001A6842F0D20210B51A60026814791CB977 -:1016E000196841F004011960117801290CD1528860 -:1016F000012CC2F309020CBF4FF08071002142F4AB -:1017000090320A435A6010BD0121BDE81040FFF736 -:10171000C7BE38B5436805461C6803681A78013AA5 -:10172000072A5BD8DFE802F0045A5A5A223B5A577C -:10173000A269910701D59A79A2621A793AB9A26988 -:1017400052060FD50522284601211A7008E0012A09 -:1017500008D1A269100605D50622284600211A7074 -:10176000FFF79EFEA369D9063AD525E0A269520784 -:1017700005D5DA681969501CD860636A8B54A3696F -:101780001B0603D528460021FFF78AFEA3695806E9 -:1017900026D528460121BDE83840FFF7A2BEA26940 -:1017A000910705D5DA681969501CD8608B5CA36273 -:1017B000A16911F0100109D0E3692846022143F024 -:1017C0001003E361BDE83840FFF764BEA3691A0661 -:1017D000DCD52846D8E7A369DB0701D400F0EEFD8D -:1017E00038BD43681B689A69D20503D5DA6942F4AB -:1017F0008072DA619A69510503D5DA6942F4806230 -:10180000DA619A69920503D5DA6942F40072DA6105 -:101810000321FFF766BE0000094B70B51C680D463A -:1018200006462046FFF7C6FD2A4631462046FFF70A -:1018300007FE05462046FFF7D3FD284670BD00BFD2 -:10184000182C0008094B70B51C680D46064620464A -:10185000FFF7B0FD2A4631462046FFF711FE054648 -:101860002046FFF7BDFD284670BD00BF182C0008BC -:1018700037B500F07DF81D4801210222FFF700FC7A -:101880001B4801210222FFF7FBFB1A48012102221B -:10189000FFF7F6FB00F06DF800200DF10701FFF7F0 -:1018A000BBFF9DF80750012D1DD114200DF107013C -:1018B000FFF7B2FFA8B1104C237813B101332370A6 -:1018C0000FE09DF80710142021F0060141F00201FD -:1018D0008DF80710FFF7B6FF18B10320257000F050 -:1018E000E7FD012000E0002003B030BD08650008DE -:1018F0001465000820650008FF89002001460148A2 -:10190000FFF7D8BB0865000801460148FFF7D2BBC6 -:1019100014650008B0F1804F08B503D20D4830219E -:1019200000F042FD0C4B98420ED903F580339842EB -:101930000CD9B0F1A04F0BD3084A094BB0F1204F9E -:1019400034BF1846104608BD064808BD064808BD05 -:10195000064808BDEC670008FFFF0040852A000824 -:101960006D2A00089D2A0008B52A0008552A00089B -:101970007047704710B50C46FFF7CCFF01210346B6 -:101980002046BDE81040184710B50C46FFF7C2FFCF -:10199000002103462046BDE81040184738B5104CDA -:1019A00005464FF080512046FFF7E4FF45B154E86B -:1019B000003F43F4807344E80032002A08D0F6E781 -:1019C00054E8003F23F4807344E80032002AF7D142 -:1019D00003484FF08051BDE83840FFF7D5BF00BF46 -:1019E00000700040024B5868C0F34000704700BFD1 -:1019F00000700040024A136843F008031360704708 -:101A000000700040B0F1006F10B504460BD20C48D6 -:101A1000FEF7F0FF2046FFF70CF80A48FFF702F840 -:101A20004FF0FF3010BD084B002053F8042BA242AA -:101A300002D81A68944202D301300B28F5D110BDA8 -:101A40000B680008146800082C6500082DE9F843AD -:101A50000446174699460E46C1B3FFF7D3FF054625 -:101A6000601E3044FFF7CEFF002D804631DB00289A -:101A70002FDB461B01361FB1002031464A46B847CE -:101A80002C4600F0D3FD44451EDCF32000F0E4FDBD -:101A9000114B002133F8140000F016FE09280AD07B -:101AA0000E48FEF7A7FF2046FEF7C3FFFEF732FF02 -:101AB00000F0CAFD0DE02FB1C5F1010031464A46E4 -:101AC0002044B8470134DEE700F0BEFD0120BDE848 -:101AD000F8830020BDE8F8835C6500082D680008E5 -:101AE0002DE9F0410F461646804600F09FFD3E442A -:101AF0003C46F32000F0B0FDB44216D0C7EB080519 -:101B0000254414F8011B284600F014FE0928F3D0E0 -:101B10000848FEF76FFF2846FEF78BFFFEF7FAFE38 -:101B200000F092FD0020BDE8F08100F08DFD012065 -:101B3000BDE8F0814568000808B545F2555000F051 -:101B40006BFE042000F06EFE40F6FF7000F070FEA9 -:101B5000002000F061FE4FF480500121BDE80840F4 -:101B600000F058BD08B500F071FEBDE8084000F077 -:101B700065BE08B57D2000F0E5FF003018BF0120EC -:101B800008BD4900FEF7E4BF2DE9F043012185B00F -:101B900005460020FEF7DCFF042000F051FB01A801 -:101BA000294600F0A7FB01A800F0B3FB08B93B48A9 -:101BB00014E00C353A48FEF735FF02992846FFF746 -:101BC00003F904463748FEF715FF2046FEF731FFBC -:101BD000FEF7A0FE039B9C4204D03348FEF722FF91 -:101BE000012057E0029CDFF8E0803048FEF71AFF42 -:101BF00021462F4A002340460024FFF727FF029F7B -:101C00002C484FEA5709FEF70DFFBC421CD23E1B81 -:101C100029486119B6F5803F28BF4FF48036324617 -:101C2000FFF7B2F908EB040023493246FFF758FFEB -:101C300018B92248FEF7F6FE06E009EB54003946D3 -:101C4000FEF786FF3444E0E71D48FEF7D3FE029816 -:101C5000FEF7EFFE1B48FEF7CDFE1B49029A00205F -:101C600000F002FC04461948FEF7C4FE0398FEF794 -:101C7000E0FE1748FEF7BEFE2046FEF7DAFEFEF74E -:101C800049FE039B9C4204D01248FEF7CBFE022083 -:101C900000E0002005B0BDE8F08300BF56680008F2 -:101CA0007468000891680008A7680008CA680008FE -:101CB000831B0008E1680008008A0020F86800081B -:101CC0000369000811690008008000081A6900080B -:101CD0002D6900083369000810B5022000F0C8FA29 -:101CE000D8B3042000F0C4FA044670B11B48FEF7D4 -:101CF00099FE042000F0B0FA022000F0ADFA4FF493 -:101D00000040BDE8104000F0A7BA1548FEF78AFE73 -:101D1000082000F0A1FA102000F09EFA202000F028 -:101D20009BFA402000F098FA2046FFF72DFF02288A -:101D300007D1082000F084FA102000F081FA00F0AA -:101D400061FB042000F088FA022000F085FA4FF4CD -:101D50000040BDE8104000F073BA10BD7E69000875 -:101D6000B469000810B52548FEF75CFE4FF480000A -:101D7000FFF70AFF10B102283AD809E0202000F04E -:101D80006BFA402000F068FA802000F059FA2FE04A -:101D9000082000F055FA102000F052FA202000F040 -:101DA00067FA20B91648FEF73DFE202007E04020E4 -:101DB00000F05EFA58B91348FEF734FE402000F0F8 -:101DC0003FFA4FF4007000F03BFA00F01BFB0E48A6 -:101DD0000024FEF727FE082000F03EFA102000F055 -:101DE0003BFA202000F038FA402000F035FA00E0FD -:101DF0000124042000F030FA204610BDCF6900080D -:101E0000E9690008226A00085B6A000808B5024810 -:101E1000FEF708FE00F0FDFA956A00081EF0040FB8 -:101E20000CBFEFF30880EFF30980FFF7EFBF7047B7 -:101E300008B50120FEF7F6FC08B900200FE00020ED -:101E4000FEF7F0FC0028F8D00220FEF7EBFC00289B -:101E5000F3D00320FEF7E6FC80F00100C0B200F0F2 -:101E6000010008BD1FB504460E48FEF7DBFD01A9C1 -:101E70000C22204600F026FB01A8FEF7D3FD2046E9 -:101E8000FEF784FE0220FEF7CDFC04460220FEF79A -:101E9000C9FCA04201D000F0B5FA0A2000F008FB0E -:101EA000F4E700BF8B6A00087FB5BF484FF0805150 -:101EB000FFF760FDFFF796FD044630B1FFF79AFD8E -:101EC000BA48FEF7AFFD00F0A4FA4FF08051B648D3 -:101ED000FFF75AFDFEF750FCFEF7D0FCB448FEF7C2 -:101EE000A1FDB448FEF79EFDB348FEF79BFDB34845 -:101EF000FEF798FDB248FEF795FDB248FEF792FD59 -:101F0000B148FEF78FFDFFF7B3FC00F0BBF900F01E -:101F1000DBF921460C2201A800F05BFE00F0D0F9AD -:101F200001A90C2200F0CEFA01A8FEF77BFDA7481C -:101F3000FEF778FDA548FEF775FD4FF4804000F0F0 -:101F400097F990B1A248FEF76DFD4FF4804000F084 -:101F500083F9082000F080F9102000F07DF920209E -:101F600000F07AF9402000F077F9FEF77DFFFEF7E8 -:101F700067FCFFF77DFCFEF7A1FDFEF7D9FDFEF73C -:101F800001FC08B1934803E0FEF744FC10B192480D -:101F9000FFF768FF00F084F9802000F069F918B1BC -:101FA000802000F059F94BE04FF4003000F060F968 -:101FB00020B14FF4003000F04FF943E0FFF738FF55 -:101FC00068B141F288348548FEF72CFDFFF730FFF9 -:101FD00078B1012000F06CFA013CF7D132E0804B7F -:101FE0000CCB013301D0013203D17E48FEF71AFD3C -:101FF00028E0FFF7BEFD48B3FFF7BBFD10B17A48FC -:10200000FEF710FD4FF4007000F032F910B1774880 -:10201000FEF708FD4FF4007000F01EF9102000F0EC -:1020200027F9002800F0F8807148FEF7FBFC082033 -:1020300000F012F9102000F00FF9FFF793FE98B9A5 -:102040006C48A5E76C48FEF7EDFCF6E74FF400702E -:1020500000F00EF90028CFD1082000F0FDF8102084 -:1020600000F0FAF8FFF738FE4FF4005000F000F9E6 -:1020700004464FF4805000F0FBF8400040EA840032 -:10208000C4B24FF4006000F0F3F82043C0B2072858 -:10209000C3B20DD14FF4006000F0DEF84FF4805071 -:1020A00000F0DAF84FF4005000F0D6F853486FE72C -:1020B00001334FF40060DBB2023B052B11D8DFE89F -:1020C00003F00C1003100C1000F0C6F84FF4805011 -:1020D00000F0C2F84FF4005003E000F0BDF84FF4F8 -:1020E000805000F0ADF84648FEF79CFCFFF724FD59 -:1020F000FFF738FDFEF786FF394B42485D681E68E2 -:10210000FEF778FC2846FEF794FC3F48FEF772FC89 -:102110000022930001324FF0FF3103F16043082A9F -:1021200003F56143C3F88010C3F88011F1D1374B38 -:102130004FF4801200241A634FF480625C639C6346 -:102140001A645C6400F01AFC3148012100F0BEFC06 -:1021500021462F4800F0BAFCF320012100F0C2FC18 -:102160002146F32000F0BEFC0320012100F0C6FC54 -:102170002146032000F0C2FC6FF4A050012100F0C2 -:10218000C9FC21466FF4A05000F0C4FC2148012195 -:1021900000F0CCFC1F48214600F0C8FC63B64FF0AD -:1021A000FF3EB546284700BF00700040A06A000807 -:1021B000AD6A0008B46A0008416B0008D86B0008DB -:1021C000596C0008DA6C00085D6D0008B36A0008FD -:1021D000DA6D0008014550FE024550FE076E00080A -:1021E00000800008426E0008556E00086D6E000801 -:1021F0008D6E0008034550FE016F0008044550FE37 -:10220000206F0008326F0008466F00080038024057 -:102210000010E022B379F764082000F029F820B11B -:102220000548FEF7FFFB102003E00448FEF7FAFB29 -:10223000082000F005F815E7B56E0008DB6E000811 -:1022400010B50446002000F0A9FC40EA040100207B -:10225000BDE8104000F092BC10B50446002000F02C -:102260009DFC20EA04010020BDE8104000F086BC7F -:1022700010B50446002000F091FC204214BF01205C -:10228000002010BD08B50120FFF788FB0120FFF7F3 -:10229000EFFF20B90121BDE8084000F06FBC08BD88 -:1022A00008B50648FEF7A6FB002000F077FCFEF715 -:1022B000C0FBBDE80840FEF72DBB00BF4E6F000815 -:1022C000022000F06BBC000008B5FFF7F9FF0449DD -:1022D000884204D00220BDE8084000F04FBC08BD91 -:1022E0008BB8185808B50248FEF79CFB00F08AF836 -:1022F0005A6F00081FB504460C2201A8FEF744FEE1 -:1023000001AB03CB206018686160A060204604B078 -:1023100010BD0068A0F10C035842584170470000FE -:1023200080B51746064610480D461C46FEF762FB70 -:102330003846FEF75FFB0D48FEF75CFB3046FEF7C4 -:1023400059FB0B48FEF756FB2846FEF772FB2CB1F3 -:102350000848FEF74FFB2046FEF74CFB0648FEF709 -:1023600061FBFFF7BFFF00BF776F00085A6D0008E1 -:10237000806F00085B6D0008B36A00081FB506AAED -:1023800052F8044B039200921A462346FFF7C8FF07 -:102390000CB41FB506AA52F8043B03920092014AFE -:1023A000FFF7BEFF826F0008002307B57246009357 -:1023B000014BFFF7E3FF00BF896F00087446064832 -:1023C00008B5FEF717FB2046FEF733FB0348FEF780 -:1023D00029FBFFF787FF00BF916F0008B36A000871 -:1023E000BFF34F8F0549064BCA6802F4E0621343FE -:1023F000CB60BFF34F8F00BFFDE700BF00ED00E0F3 -:102400000400FA0508B5FEF7CFFB00F0AFFBFFF7BD -:10241000E7FF08B5FFF7E4FFF0B50646002401200A -:102420002546034694421DD011F804C000F1010E68 -:10243000BCF1000F03D17355774605460DE001331B -:10244000774606F800C0DBB2FF2B07D102F1FF3C54 -:10245000644506D07355871C754601230134384600 -:10246000E0E770467355F0BD30B5C9B1C0430A44CA -:10247000914213D011F8013B0A4D83EA000404F0A5 -:102480000F0455F8244084EA101080EA131303F077 -:102490000F0355F8233083EA1010E9E7C04330BD3D -:1024A000084630BD7465000800010138FDD1704751 -:1024B00010B504462CB14FF47A70013CFFF7F4FFDD -:1024C000F8E710BD0A2A10B504DC1148BDE8104039 -:1024D000FEF7A8BA30230C461C2204F8023B7823EE -:1024E0004B700F2393400340D340092B01D8303366 -:1024F00002E00F2B02D85733DBB200E02023043A6E -:1025000004F8013B131DECD100238B7210BD00BFFA -:10251000CB6F000808B582684368934203D3044830 -:102520001421FFF741FF0268591C4160D05C08BDCF -:10253000E16F000843688268934210B503D30448F2 -:102540001921FFF731FF02685C1C4460D15410BDB3 -:10255000E16F0008F0B587B00E460746154600212A -:102560000C2268461C4600F034FB00210C2203A814 -:102570000296009700F02DFB684603950594FFF73F -:10258000C9FF06466846FFF7C5FFB042014601D0C5 -:1025900003A808E06846FFF7BDFF0446D8B101284C -:1025A00004D103A83146FFF7C5FFEBE7030608D5C2 -:1025B00004F07F046846FFF7ADFF40EA042000F115 -:1025C0008004A4B20025A542DCDA03A80021FFF7AD -:1025D000B1FF0135F7E7049807B0F0BDFEE7000052 -:1025E0000748084A0849121A08B500F0E7FA0748F0 -:1025F000074A0021121A00F0ECFAFFF755FC00BF61 -:102600000000002014000020F46F000814000020D7 -:10261000008A0120044B9A6809B1104301E022EAC4 -:1026200000009860704700BF002004E0044B1A6966 -:10263000002A04DA034A5A6002F188325A6070476D -:10264000003C024023016745024A136943F00043FE -:1026500013617047003C0240014BD860704700BFD7 -:10266000003C02400E4BDA68D20310D4DA68D1067F -:102670000FD4DA68D2050ED4DA6812F0E00F0CD16C -:10268000DB6813F0020F14BF0820092070470120F7 -:10269000704706207047022070470720704700BF30 -:1026A000003C0240092307B58DF80730FFF7DAFF39 -:1026B0008DF807009DF80730012BF7D09DF8070033 -:1026C00003B05DF804FB000070B5064641B1012976 -:1026D00008D002290CBF4FF400754FF4407503E099 -:1026E0000D4601E04FF48075FFF7DCFF09281ED18D -:1026F0000F4C236923F4407323612169294321612D -:10270000236923F0F8032361236943F002031E4386 -:102710002661236943F480332361FFF7C3FF2369F4 -:1027200023F002032361236923F0F803236170BDC2 -:10273000003C024070B505460E46FFF7B3FF09287E -:1027400013D10A4C236923F4407323612369236165 -:10275000236943F0010323612E70BFF34F8FFFF70E -:10276000A1FF236923F00103236170BD003C0240F7 -:10277000F0B500220E680123934003EA060E9E4541 -:102780002AD1550003230468AB40DB431C4004609E -:102790000C79076804FA05FC013C4CEA0707012C98 -:1027A000076011D884684F791C40AF40846084680A -:1027B00027438760446824EA0E0444608C794768A4 -:1027C0009440A4B23C434460C4682340C360CB79C6 -:1027D000C468AB402343C3600132102ACBD1F0BDA3 -:1027E0000369194214BF01200020704702B909048F -:1027F0008161704701F00703C9089B0000EB81006D -:102800000F219A40994010B5046A24EA010101623F -:10281000016A1143016210BD014B1860704700BF8F -:1028200000300040014B5860704700BF003000404E -:10283000014B9860704700BF003000404AF6AA2262 -:10284000014B1A60704700BF003000404CF6CC428C -:10285000014B1A60704700BF003000400023036046 -:1028600043608360C36003614361836170470023F9 -:1028700043608360C3600360036143618361C3613C -:10288000036243628362C362704700000E490368BB -:102890000A6810B544691C4383691C4322F07F43D6 -:1028A000426823F03003234343EA02630B60C368AA -:1028B00082684C681A43054B23401343026943EA7C -:1028C00002434B6010BD00BF001000A0FEF7E0FF08 -:1028D00090E80C000E491A4383681A43C3681A43F0 -:1028E00003691A4383691A43C3691A43036A1A4383 -:1028F000436A1A43836A1A43C36A10B51A434C6980 -:10290000044B23401343426943EA82434B6110BDA9 -:10291000001000A000008090044B1A6810B142F033 -:10292000010201E022F001021A607047001000A0CD -:10293000024B9A68920600D498617047001000A07C -:10294000024B9A68920600D418617047001000A0EC -:10295000014B1878704700BF201000A0034B9B6804 -:10296000184214BF01200020704700BF001000A0D3 -:10297000014BD860704700BF001000A00F4B002132 -:102980001A6842F001021A6099601A6822F0A8528F -:1029900022F410221A600A4A5A600A4AC3F88420B4 -:1029A00002F18062C3F888201A6822F480221A603B -:1029B000D960C3F88C10C3F89010704700380240FB -:1029C00010300024003000201E4A936803F00C03EE -:1029D000042B10B503D0082B03D01B4B19E01B4B65 -:1029E00017E05168536811F4800F516803F03F03FA -:1029F00018BF164AC1F3881108BF134AB2FBF3F39C -:102A0000104A4B435268C2F3014201325200B3FBF9 -:102A1000F2F30C4A036093680D49C3F30313CC5CD3 -:102A20000368E34043609468C4F382240C5D23FA96 -:102A300004F484609268C2F342328A5CD340C3607B -:102A400010BD00BF003802400024F40040787D0132 -:102A500001000020044B1A6B09B1104301E022EA87 -:102A600000001863704700BF00380240044B5A6BE7 -:102A700009B1104301E022EA00005863704700BF2B -:102A800000380240044B9A6B09B1104301E022EA7E -:102A900000009863704700BF00380240044B1A6C76 -:102AA00009B1104301E022EA00001864704700BF3A -:102AB00000380240044B5A6C09B1104301E022EA8D -:102AC00000005864704700BF00380240044B1A6988 -:102AD00009B1104301E022EA00001861704700BF0D -:102AE00000380240044B5A6909B1104301E022EA60 -:102AF00000005861704700BF00380240044B9A69DB -:102B000009B1104301E022EA00009861704700BF5C -:102B100000380240044B1A6A09B1104301E022EA6E -:102B200000001862704700BF00380240044B5A6A28 -:102B300009B1104301E022EA00005862704700BF6B -:102B4000003802404209084B012A01D11B6804E009 -:102B5000022A01D11B6F00E05B6F00F01F0023FA17 -:102B600000F000F00100704700380240024A536F45 -:102B700043F08073536770470038024082B00023EF -:102B80000193054B0193019B03EB80000190019B96 -:102B9000196002B0704700BF5028004082B0002387 -:102BA0000193054B0193019B03EB80000190019B76 -:102BB000186802B0704700BF50280040431E0A4406 -:102BC00010B5914204D011F8014B03F8014FF8E71A -:102BD00010BD02440346934202D003F8011BFAE7FA -:102BE0007047000014000020242C0008001402404C -:102BF000004000000E0004000014024000800000AD -:102C00000F000400000000000000000000000000B1 -:102C100000000000B46500081C2C0008E42B00082C -:102C2000500000000060004000000001801A060013 -:102C3000005F6000382C00080054014000002000B4 -:102C400000000240200000000500080000000240D3 -:102C50008000000007000800000002401000000093 -:102C60000000000000000240080000000100000019 -:102C700000040240040000000004024001000000C3 -:102C800001000000001802404000000001000000A8 -:102C90000018024008000000000000000018024078 -:102CA0002000000001000000001802401000000099 -:102CB00001000000000C02400001000008000700B5 -:102CC000000C0240000200000900070001424F4FC3 -:102CD000544C4F414445520000000000002A2A0095 -:102CE00000000000000000000047FF004C61747409 -:102CF000696365006943456375626532203230312E -:102D0000362E30322E323738313000506172743AFC -:102D1000206943453430554C314B2D434D333641BA -:102D200000446174653A204A756C20323520323097 -:102D300031362030393A35363A33394702FF7EAAE8 -:102D4000997E5100010592002062016F8247027254 -:102D5000007011000101470B104702804705024730 -:102D6000058003470801470208471120470C024726 -:102D70000B02471740470501470540470C040090E8 -:102D8000470F1047064047050147054047050147E3 -:102D900004800004471080470502002000080080DE -:102DA000470201470908471701470434000C47044C -:102DB00003470D30475970470E801047040200400A -:102DC000470408014702070200404704080147057D -:102DD0002004474103470B03470D1C471080470958 -:102DE000010080470203470908470324472B78471F -:102DF000124007470A40470C040008470E404705A9 -:102E0000014706044704014706044706104711809E -:102E10000747061C470380470C08470A0C30204729 -:102E20000F30E8470C63C1470C0C30470F400105D9 -:102E3000470C03C0470C0C3020470D184702E047F1 -:102E400004020CB10018003847064047080C304710 -:102E50001001074702010001CD72470398470F0C8C -:102E60003020470D3000F9A04708100001C0470589 -:102E7000038047070C30470E3000710547083000CB -:102E800001470F0C3020470F5808470401DCA00011 -:102E900080470363C1470203C020AF5247050C308F -:102EA00047100803470402E0F5470503C047020244 -:102EB000E0050FA01047040C3020471240002020EE -:102EC000470AC001E007A547060C3047110D47032C -:102ED00014470B4047030547031447020C302047B3 -:102EE0000F900004470220470E17CFA24702084761 -:102EF000020C30471030092447022C470D02CD0F39 -:102F0000F047050C3020470F108004470368470343 -:102F1000010547100C30471011073047190C3020BD -:102F2000470D300010470A0147100C30470E30475C -:102F300002052047023447050247100C302047079E -:102F4000044706404704203404ED80000247091C72 -:102F5000000847050C30470F4047050422FC470294 -:102F6000014707A00002C047060C3020470903EACA -:102F7000F52000404706035CA500184708401C8F59 -:102F80000847050C30470A03D05047024047060166 -:102F9000F0554703104706200200F047050C30208B -:102FA0004709284703110001E047032038FC80024D -:102FB000470A1E8F0847050C3047080800404703A2 -:102FC0003029470402BC03D0470AC04703F0470535 -:102FD0000C3020470A1C0F10470829DCA500304799 -:102FE000090E8F0847050C30470C0F470941505513 -:102FF000002047090200F047050C3020470D044728 -:10300000065C76EC80024707084702068047060CFC -:103010003047080247050447030147033CF0470ACD -:1030200001400C00F047050C3020470820470B1CDE -:1030300021FAE732004047100C3047090C470B24B7 -:1030400064F0F7084709028047070C302047141C3A -:1030500031CCE720804703383D44470202804707D0 -:103060000C30470F404705027668FB47024000C01E -:10307000403D4703028047070C302047080C4705B6 -:103080000147053C63DE800010470C0447040C3008 -:10309000470A2047040247052402E8470210470672 -:1030A00001014047020147050C302000A100123CFD -:1030B00020000447021EF060470720001EF0404732 -:1030C000030D72BD400003428EEF47060C304705EA -:1030D00003C0C04704F047024000974703042000A4 -:1030E000F0470401403C470201429C47070C302056 -:1030F00047033A3C200647030CF06018021080009A -:10310000060140024AA500188000077815006047B4 -:10311000020CC96047050C30470503C0104704F096 -:10312000470205090747038001D055470390004037 -:103130001620000142C1096047050C3020470332C8 -:103140003C202C47031CF04030470280000447021B -:10315000204705018839414702020025ACC02047BD -:10316000040C30470503C0044704F001300801098E -:10317000000C4702404705F00F4282010020402525 -:1031800047070C30204703323C20470280001EF0E6 -:1031900040470280E847037C470705781547044409 -:1031A00088002047040C3047040803C80001470288 -:1031B00010F0470371094703964708C01640000303 -:1031C00042150020470202001C0C30200001017A49 -:1031D0003C2047040EF0600008704703A00601DAA7 -:1031E000A047090A007E8800300002C0000C30476A -:1031F000038D0003C04000243000F0470308FB4764 -:103200000240000340F54709014287002047040CB3 -:103210000C30202000013A3C20201004470708040D -:10322000001000045AA102004000A04705603EEFD4 -:10323000A547050C300010070D0803C047020247E0 -:10324000070520470334E001470B1C05A047020295 -:1032500047020C3020040500723C20001047060491 -:1032600050800447022021EEE2020040181047047B -:1032700003601FAAF5470202C0000C300004054796 -:103280000203C0001004470542D8BD2C00103C7654 -:103290007042470240008047030102C02C00F04703 -:1032A000050C3020000800723C2000A02078470365 -:1032B000300010470501DCA302304702804703C0FD -:1032C00001C047080C3000040B470203C000521431 -:1032D0004704300008033C4702343830A300204044 -:1032E0000147048101C2C0470C0A723C204702A07A -:1032F0003BF9014001404705202C0CF040470307F3 -:103300008402470306DEF5E0470A100803C1000106 -:10331000547D69A7004040470402046000F0470262 -:103320008050003C470402BCF1D0104706018047A2 -:1033300002723C20060024017F74400140470306CE -:1033400047034EA50018470215A3C2806002002C57 -:1033500091A0470B0803C047023C2E78A010024002 -:103360004707D055470490003C470201401DF3F049 -:103370004707034703121540040036687B6440305A -:1033800047040CA047020DD1000147020723C247A2 -:1033900002034614ABC420470A08154004023C004F -:1033A0004A58007047040C470203DFE24705803C9F -:1033B00047020266C20BC208470A03BC1047023C20 -:1033C000294B7440002847042000281EF0400002CA -:1033D0001801A3C20004016060AB470C043C470322 -:1033E000164268A0004047074010F0470205080059 -:1033F000803C47021403B4C70010470A03BC100CFA -:103400004702035D7A014706A00E60FEA047023026 -:1034100001A7A3C2000803469E81A020470A043CDE -:103420000004470202C85A0847071E03C0F54705B3 -:10343000803C470201601D03F0470C295047021EE3 -:1034400001D95B10404705800423CCA1024703113A -:1034500023C247041D95E0470B90168047021C319C -:103460004CA547080202C002470480003C47043EC6 -:10347000E1D0470A1B47051F470601C04703062046 -:10348000E8A3024703B325ED0004016640ABC42066 -:1034900047090A4705024706014705267003470406 -:1034A000AFB694010A0242B507C004471064F85051 -:1034B000104708701AA302B10001D04703C00003EF -:1034C000DDB5E0471164005047083443D053002273 -:1034D0008051A0470380D6C76EE1D04709081E5B24 -:1034E000C8104703040AF0404709FE00404702198C -:1034F0008F304002000166A5E0470A0B00121E80D3 -:103500006000142800F04702400007470403F0A0C1 -:103510004080020007B0470301C24E944708018073 -:10352000470506470308F040184702100006200030 -:10353000020C0A001947020B3A81816002E68405F9 -:10354000A020470801470810F04704B7470240048D -:103550003CC0050002801087400101000260140297 -:10356000400447060300080036C9580C0034001A0E -:10357000F04030470218000CA14000FE504A0247BC -:10358000020120288047030480D0A04706010003E1 -:103590000A7A05424702144702F000304702FB0056 -:1035A00004408003F080404704402880000340012D -:1035B00000D080470603000500DE11D84702366CB4 -:1035C0001AF060470308000D8000020C0A40300822 -:1035D000000B3801010002000D85E02047060100C4 -:1035E0000F0052E0404702142800F04703B00700E4 -:1035F00009024702C0054820040007414104470270 -:10360000401602E0470A08F680D2100E47021AF070 -:10361000404703184703200400A54000805011755F -:10362000D04703C067A0140447080B091B28C0201B -:10363000470310F04704C900400E3C38C05A480107 -:1036400000D887B5F8470303AEE8008047080801B3 -:103650009F141447041AF040470280180400201EEB -:103660002200A54130005C037B08084702E021CA24 -:10367000470A050C56788240470310F04703F0E5EF -:103680003C470340C05A40104702A7C30C04000245 -:1036900062DC05470A0D81D6C9504705A54047029F -:1036A00098984702A004301C0540800018E04705A8 -:1036B000600685014709050A32054A4704405A4712 -:1036C0000399933400400201C00A40470210B047FA -:1036D000030847020105470A0581FE51D047040A45 -:1036E000F0403000901000040004027E5F401000A3 -:1036F00050E1B8280400E0400FF812470A0B53E0ED -:103700004A4705F000300070FD3C04000220E00F45 -:1037100048100079098168040002600CC447111444 -:10372000004EA0400240470607C850C047039047DC -:1037300005407EC70008470F020010A040804047A8 -:10374000063BC00542470210D04705235DAF014745 -:103750000E06470744470306470203C850C0180037 -:1037600018033E01006000063DC0471104470542B2 -:1037700047050402C005404702188FBC01204703DB -:103780000E824716100001C000044702674850C06F -:10379000100001E130104703604718300001470274 -:1037A00004470223E005401047020DE047160C478E -:1037B000070547030C0006674850C0F0001017B219 -:1037C000012847020780471008470B08470222E0FC -:1037D0000540204702E7C00147234003E850C147A6 -:1037E00003F0470402662F90471B10470203C005F1 -:1037F000404703D0470401442FB040471D73685031 -:10380000C010470219F56C47020366EED0471E420E -:10381000C00540304702ADE11C47020142AEF080D6 -:10382000470240470E0447090226036850C00007BC -:10383000101DFB08084702068DE800204710384796 -:103840000A0403E0054040003085830C4705084723 -:103850000F04470B040020736850C11047028047D3 -:1038600003C00060208E5420470D04470B04000461 -:1038700042E00540100051E04703804142650DA041 -:10388000470372009011010101470B1047064047A2 -:1038900005014705400002470608471120470680FA -:1038A0004705024712084722010002470404471156 -:1038B00010470640470501470540470316000147EA -:1038C0000604472202002000084702084703104769 -:1038D000243C000C47063047688010470402004033 -:1038E0004704080147030200404704080147043029 -:1038F0002004474D01470604472103008047050285 -:1039000047030C471F1E472D024703404705014749 -:1039100017404705014706044703030147060447CC -:103920000610471D8047050247110C30472C0C300C -:1039300020472B0C30472C0C3020472B0C30470EE7 -:1039400001470C1047100C3020470D02471D0C306A -:10395000472C0C3020472B0C30472C0C3020472BA9 -:103960000C30472C0C3020472B0C30472C0C3020CF -:10397000472B0C30472C0C3020472B0C30471308BA -:103980000004002055470880004247080C302047BB -:10399000120C0020016855404707400200404707CD -:1039A0000C3047150400200F470B02055004470454 -:1039B0000C30204714260168F0C04709661E855464 -:1039C00047050C3047180F470DF08047040C302096 -:1039D00047150168F0C0470A168F0C47050C3047A1 -:1039E000150C47020F470DF08047040C3020471597 -:1039F00001E8F0C0470A1E8F0C47050C3047150C34 -:103A000020200F470B0200F047050C3020471426FA -:103A10006548F0C04709601C8F0C47050C304716FD -:103A200020200F470A460200F047050C30204715BA -:103A30006548F0C047080207548F0C000147030C8B -:103A40003047180F470DF047050C30204712064746 -:103A50000201C8F0C047076047021C8F0C47050CE5 -:103A60003047170C0F470503C01247020401B0F09E -:103A700047050C3020471501DAF0C0470423C04742 -:103A80000307148F0C47050C304718F04703D9B0D3 -:103A9000A4470280470A0C30204714060008F0476C -:103AA0000350A780020040470A0C30471620300F11 -:103AB0004703D900A047040247080C3020471505EA -:103AC000780F80470250A780024703404047070C09 -:103AD00030471524201B0F470528470D0C30204781 -:103AE0001501F80F804703078002470C0C30471779 -:103AF0000D0F4704B020470D0C30204714066179A4 -:103B00000F804703078402470C0C30471504200D33 -:103B10000F47035000A8470D0C302047142004F82D -:103B20000F804703070002470C0C30471506203C66 -:103B30000F47035000B4470440050A088047040CAF -:103B4000302047143501C90F8047030100024702A6 -:103B5000026000E50447050C304715366C1C0F4722 -:103B6000035000284707588047040C30204714149E -:103B700028E80F8047030704020060470214E0545E -:103B800047050C3047166D4CA547035000204704ED -:103B9000C0040A088047040C3020471529415A8088 -:103BA00047038700024702024702A504471B10048F -:103BB0004000F0470301A9A00004800042471F20F5 -:103BC0007808F047020800BFA021404002004047AB -:103BD0001D10042060A54702404702A047020100D3 -:103BE00040020550471C2007605A804703078002A7 -:103BF000470202601E8556471D0340A547035190AA -:103C0000B04702014704F0471D01605A804702D9BE -:103C1000A3840247020800168F0E470E01470D1CB1 -:103C2000004FA54704A0703C4706F0470E01470D22 -:103C30002601E15A80470219B5F43E47041E8F0E53 -:103C4000471C1C20390F47031900B44704028200A7 -:103C5000F1471C2605580F804702F9E3800247030D -:103C600060148F0C471C1C3C2F0F47031000344777 -:103C70000320020200F1471C2061DB0F8047028015 -:103C8000030002470306548F0C471C3C381C0F47A7 -:103C900002180090F03C4703028000F1471D04C861 -:103CA0000F8018003DA5E03E006047021C8F0C47C6 -:103CB0001C3C380C0F47031000283C47040190F1CE -:103CC000471D25CA0F8047029005643E47041DBF6B -:103CD0000C470E04470222CF4A6047050400042027 -:103CE00047031000188F62E1004100421C05A04705 -:103CF0000E04000467FB40404705040036044703F8 -:103D00001000981D6CF1024002002C470210014780 -:103D10000604470208284702100025F03C600042D4 -:103D20004704100232DAC3400040018541410005DA -:103D3000000381005004470A16A804470206067AC9 -:103D40007C424047060402DFD348000459F07281E8 -:103D500047041EE0504710023FE8F24707801C7AF4 -:103D6000E0A5470220080F3001400001C25C05A019 -:103D70000401470D100E62DFFA4707102026FC50A1 -:103D80000100C018E13A01000147026E4713214FBC -:103D9000F54707014221D90740007D000F28110097 -:103DA0000101C026020414471006EBF64707011C68 -:103DB00005FD036100021987F69D000247021F807E -:103DC0000420470809470408000628E01820404757 -:103DD0000408470202DFE3402000D80B656100942D -:103DE0000142B501470A0847040C002E07FA0402F5 -:103DF00047050C00042BCCC36070009187BA890082 -:103E0000CA020021C20020470F3C0010A047020850 -:103E1000470601F0D3410147029FF9510004000217 -:103E2000B680404711014EA0470902E8C342020094 -:103E300070153EC1470306D5A0A047100642D0B971 -:103E4000404707042830A047031805732147036043 -:103E500037A1470F06000E3BDCB16247050647035A -:103E6000DAA00018007C0FBB09006001E035D047E4 -:103E7000020A470810470602CFE0470D0400190F59 -:103E800021194702014247040447080E47040E4720 -:103E9000021BD0470930470304059981720D470280 -:103EA00002470B03000980920447047F60C2470663 -:103EB000040142034005001008018047034003400D -:103EC000470A03000F80174705386CC302470504F3 -:103ED000009E000E0A20100058F0470340471018BB -:103EE00014470403F00347081E3C30A00847029023 -:103EF000E77F21470306EED4A447090100DE80478F -:103F0000030402DA430247073E056EA04703B819CF -:103F1000FF4147024002B689144709090894104737 -:103F2000034040F0024707802C3F5ECB470308A9BF -:103F3000836101470202020F470A0801F747028026 -:103F4000470260C80A0247060E20060CC710470247 -:103F5000781BEB0108084702468F0847094B0016FB -:103F60004047020146387B0201000247053C3C10F5 -:103F70000A47033009807447020363C00F470A20D1 -:103F80000012204703BC7AF947030D47052005DAE4 -:103F90000A200247020772B4080009405E8F081029 -:103FA00047080B0D941A4247033C00A00847050838 -:103FB000001C294B08002020D00541470280000248 -:103FC000C20F470A080B06A590470369CAA02047FD -:103FD000050C0020020C042032801809200008C0C3 -:103FE00047025C8F084709200E1000084703243061 -:103FF0000401470504470220CCA141470218B0C084 -:10400000284704020F470C17900080001429EA0487 -:104010000247050447020378D1408000D005782884 -:1040200047041C8F0847090700D04703401472D982 -:1040300047030147050401CA5B4703110567B84000 -:10404000470242800F47080180000A760080808086 -:1040500000767B802000024703060E14000DA7089F -:10406000180159C573BC006047024C8F0847090509 -:104070000EDF00804703237B470A26CEFF0940005E -:10408000D9E3384147072047080518DA47053CE9D6 -:1040900080200038470603DBF7470338A138014783 -:1040A000040C000800404702350050134703040386 -:1040B000C04702266D7040084047040400047C702D -:1040C00005470308ABA39C20470203010F040040EF -:1040D000014020E0A00147030683D047022428DAEC -:1040E0005012470504002423EC4702800071B17E82 -:1040F000BD40404702418F044703467E0D344702CE -:10410000014706046410A04707020036DAF048476A -:1041100002500743000147040F04470201639CEC6F -:10412000342000098047052065DEA0470901FFF41F -:10413000404000D9A720000A47020700CF044702E9 -:1041400002403401A0470802266650014709437027 -:10415000A04703500FA40020470204010F044704A6 -:104160002FE00220470826760E022247070621ECA0 -:104170004704980FF420470303808F06470201404D -:10418000010A4706154001800C62CC4705C0470470 -:104190003D40E740C10050A7030020470202410F05 -:1041A000044702020284AA47055A1550010020263E -:1041B000FE0847041000804702077CD34A020081B2 -:1041C000D772000C0801E001CF04008000424705CF -:1041D00001800803C047020C41DD20470608002E7D -:1041E00039D00A47030800C02820470220010F04E5 -:1041F00000C002004047040D98523C300240002AA3 -:10420000CF47070C0006036C470430073028010035 -:1042100001C000EF044703029E4705080803C0479A -:10422000021401E024001047061C207000414703DF -:10423000A0C14702404702010F04470437AA010406 -:10424000470219523C30470302DA10023002470697 -:1042500004EE8063470315310028C0470201EF04D4 -:1042600047034035040010470403C0470303F047E9 -:104270000340470506034A1047030803240020476C -:104280000202400F04006047038801218047027248 -:104290003C30470368EE4022004047030600060218 -:1042A0001E2000180031EF32002160470201AF04E8 -:1042B00010470223CDE0470603C14704EF47034000 -:1042C00047051401E04705A5FBE0470306010F047D -:1042D00047036004C0804704723C304703027D807E -:1042E0004702404706036A0A4703019B2FF48347AE -:1042F0000203808F048047060100010E0803C004FA -:104300004702404BF1493000080F280447101405BC -:1043100000044706030008187A3C300447023BCDEE -:10432000F04070001047020447061047028047051E -:1043300003A18A4704028047060803C447050A40D0 -:10434000800070FD24470280470D028047050280EF -:10435000470401805A3C3040000600FA0A404703F7 -:1043600080470201470D0142C04704014047040352 -:104370008F470203C010470238ED0BC10002008DC9 -:1043800028470A010141840001C0278247040680B2 -:1043900047065A3C30400006027A054900054702AC -:1043A00004000E47092A81A047031F938047095044 -:1043B000470203C010002401505F40470270030011 -:1043C000404702304703044706400002800F0008C0 -:1043D000470820007A3C30000E00637AA54047026F -:1043E000804705044705184704C00060008F00049B -:1043F0004709180803C05800042C70E34047029096 -:104400009D0008470E01C00300F04709018B523C94 -:10441000300C002C03FCC34200380008000C470E8F -:10442000020014E0F847043FC04704080803C050E6 -:10443000470201700500300031F1000C00044047D4 -:104440000B40022ECA5047041DD080470319523C2E -:104450003020470202DA470230008090000400203A -:10446000704705784706065415A847080347030315 -:10447000C04702042C6A814000020007470304473A -:10448000052800F3C2BF800003D280C0F0470701B7 -:10449000804702723C3006010C026F504018014701 -:1044A0000306470618180007E2954703400EE0F898 -:1044B000470950000AC0470401DA3C47024010CBCC -:1044C000470402C00544428008470604024702220E -:1044D000470820007840204703016D3447041847FF -:1044E00004014850400100704706071C00084703BC -:1044F00072007011020101470802470608470402D2 -:10450000470A04470220470680470C014706044734 -:104510000401470802C0470310471702470608472F -:104520000B084716024706084704024706084706D5 -:1045300020470680471F0300070004470B7C471DE8 -:1045400003470F6C470CA84705024727204708047C -:1045500047220240C04704080147030200404704C5 -:1045600008014705200447058010474C03470D709C -:10457000471F014703044709D0470340473A024712 -:104580001208470D80470A80000247030247058052 -:10459000470502470608470620472204470D4010FA -:1045A00030C04718067B38470240626D61404703C0 -:1045B00083DC05470430C0471803DBD0470301AB59 -:1045C0002022C0470203C0008047021030C04716B7 -:1045D0000103F8EC00B0018047020162000600838D -:1045E000D6000A00180030C0471703F83001F04722 -:1045F0000903C20D1047021030C047160108805A47 -:1046000000B01400AA49A16040030083C447030C12 -:104610000030C0471702002D01800800064B6C02D5 -:1046200080030003C247041030C0470A80470D14BE -:10463000EC50104702A2EDF540470383CC0547023A -:10464000800030C047193AD8F04703A1E42047035F -:1046500003C2008B01001030C04716010A805F8042 -:104660004708030083C60847032030C047172100CE -:1046700028104708020003C00547031030C0471048 -:1046800002470980470502470483C647025C470288 -:1046900030C0471A10470503C0470303C2470280D2 -:1046A000001030C04717080969470740470383D601 -:1046B0000581470330C04718215D470720470303A2 -:1046C000C0000A47021030C0471708805F18104723 -:1046D00007100083DC4703800030C0470901470E04 -:1046E000227A00804707600003C2470201001030B1 -:1046F000C0471704000A1847024010F12960470616 -:104700000A470330C0471801504703040F00014017 -:1047100047091030C04717043C585010018010F171 -:104720006A06804704014702180030C047183C1051 -:10473000508047020F00414047091030C04717041E -:104740003C5878A0470210F121404703A906F380A6 -:1047500000480030C047183C105847030F00400283 -:10476000800020193FE547028C1030C04717043CF9 -:104770005C470410F121604047028BACE00A47031C -:1047800030C047183C1000C047020F0040028000B4 -:104790002002A7B50A47021030C04717043C5C4707 -:1047A0000410F160470490B788470430C047183CB4 -:1047B0001047040F47050222EFF54702201030C0D2 -:1047C000471708024B9980470210F16047048BAEEF -:1047D000F81B470330C0470901470D0210DA1047A4 -:1047E000030F004047030202A5A50A47021030C08C -:1047F000471708025A470410F168470320A907D158 -:104800009A020C0030C047170122EB470220000F2C -:1048100047048000193EA58002081030C0471708E1 -:10482000826B470410F1284704A93CE00A47033093 -:10483000C04718115D47040F0040470280022A2636 -:10484000800002001030C04717183DC04704169979 -:1048500047058001B50A08470230C04712028C00A4 -:104860000800083C20470303096B47053FC1ED9052 -:1048700047021030C04709056047081C47026047DF -:1048800005018010F1624702060083DE470318002D -:1048900030C0470940470802803047068000010FBA -:1048A0000041C0470303C047041030C047130C7CCD -:1048B00000304703100080800AA13847020300A996 -:1048C000408047020C0030C0471202C278003047D7 -:1048D00003084702C00AA0404704294047030810C4 -:1048E00030DE4710084702404709C060F1804704A6 -:1048F00083C4470530C2470F01140003470A400034 -:10490000F0800008470203C20D0B47021030CE476B -:10491000090847095C470A2A500447030183C44732 -:104920000240470230C247090847062002024702F8 -:10493000804706200045A0344702800003C2094793 -:10494000031030C00700A0470F804E7847072000B3 -:104950002A50470902470230C04709084708B242B7 -:1049600078470905A03C402847081030C04712800E -:104970000E470C024702808083D6470530C04709A6 -:104980000847062000F24000084704D000204703F3 -:10499000024000800003C0470202001030C00747F9 -:1049A00003020F78044702204708540500304706E9 -:1049B00080470A1C000C0030C04704020F08024761 -:1049C000082000B3823A80204706804705804704CC -:1049D00010A20447071041F700280010470380A0E9 -:1049E00047050800080379000447095981A50A40D2 -:1049F000470ADFB44047057147060800608369D065 -:104A0000B04709198D0D904709022483802800447E -:104A1000090AF900A0018047030800083DC080E0B2 -:104A2000018020F5470306005A64A0470A02E7B355 -:104A3000C0400010222FDA11B047050847023C2081 -:104A400071B04702A0F080038047021A74470B023E -:104A5000AA77144703014148504702804704304772 -:104A600004A010002A57A86047035A44850A470942 -:104A700002AA0C022C4702014000D8B00080470473 -:104A8000204703F147030F50F5C047031A64009015 -:104A9000470A1892D428000101406A5010470708BD -:104AA0003DC030470320F5806047035A4488470BD8 -:104AB000341EB46C470223401BD8F0470608203C44 -:104AC0000030470320F08047041A65010B47094076 -:104AD000053EC04704046B804702080020470814C5 -:104AE000470302000347020CC5A8000D4047070812 -:104AF0000807C04704035810470202003C47080853 -:104B00004704404703332CBB0E0080470801128640 -:104B10002800014294DB4703C0470F08470780473E -:104B20000A1E8040470202003B0090008047102888 -:104B3000470660470850A55EE060000208BBDA80C7 -:104B4000E0470701E94E518047061047023C1C0729 -:104B5000470B85068447042A6EF04706744702AA6D -:104B600078F847072847023C0207804709025A00A5 -:104B70005470007047038047070108F46A47042017 -:104B8000F18247020240029DE7470280470702A5E3 -:104B900003424000304703F000024707980B4704E8 -:104BA00020F001C047028002830080014708030F04 -:104BB00052A047030C3E6A47090294DE18470A5484 -:104BC00014850A470AF0983C470303C3604709016C -:104BD000541FD0470990144702904709030F1380D0 -:104BE0004702600C3C4E30000180470614C8470263 -:104BF00001804703E00846005A45E047021847088D -:104C0000F0470503C0470A02BC1CD047062200201B -:104C1000001A7400084709030F13A60447020C3C4E -:104C20004A9810470505000294DD51E000100AF192 -:104C300084470443C48047020C4708F0043C2047E3 -:104C40000203C01010804705020001540909470300 -:104C500005F0A047043C010D0B000C4707030F12A1 -:104C6000806030000C3C4AB180C8470715CA500428 -:104C7000470220F580038003005A65E047020C4795 -:104C800008F004470403C010100014470602BC1CBF -:104C9000084704F0470302001A54000B00044707BA -:104CA000030F178047030C3C5E47028047060294BF -:104CB000CDF9F0470903C4470CF0470503C0470A84 -:104CC00001541F70D0470603470203C0470820471E -:104CD00002030F168047030C3C5A0001C847071413 -:104CE000FAB8E047080183DE47021C4709F0470590 -:104CF00003C0470314470602BC3CB890470983C071 -:104D0000470B02AA17A06047020C3C5A01804706D5 -:104D10000101E8FF71C047050780470401001080CA -:104D20004708AA0404470303C010470A542C084745 -:104D30000604470680470C200400200AA85E1814C9 -:104D400000C047040108F54C704704A76000044701 -:104D500005080008470A0420001002A810008A0075 -:104D600040470404006419F8F0000100A0470A0855 -:104D7000470E083D60000447052000083DC0470479 -:104D80008005E000380004C3FCA04711483C00F057 -:104D90004708083C47062BA342D000043C3E470B83 -:104DA000020F78470360083D6010000180470A0544 -:104DB000804019B006000600C3C4E0470218470748 -:104DC000020F084704083C00F8470B700008470230 -:104DD00001EB404702243C471230083CE070470298 -:104DE00080470440083DE0304702802845E0470402 -:104DF000C3C4A981470F22083C20080014C04705FE -:104E0000083C204703C0242BA002C047023C0101FC -:104E1000084709020F304704083CE001F047070843 -:104E20003C604703204009B047023000C3C4A99842 -:104E30004709020F4705083C2001E04707083C20CE -:104E4000470521EB4047033C0101470D2004470281 -:104E5000183C60008008470405470905E2E0284740 -:104E600002C3C5E0470D3C384702083C2001470516 -:104E7000024705B04703808BA06047033C471308F7 -:104E80003C40F84708083CC047042A01A4404702B8 -:104E900001C3C5A04711083C20F04708083C20B0DA -:104EA00047032050A04703043C471202083CC04778 -:104EB000070100083DE0470501B1404703C3C5A015 -:104EC000184710083C470A083C4702C047033B7C90 -:104ED0002047033C0103470D0E010020083C6047BA -:104EE00003804705083CE047038400012447020291 -:104EF00000AA85A101000C470B028000083C20009D -:104F0000044707083C2010E147020200B447042A86 -:104F10008100080004470272009011030101470854 -:104F20000247060847040247080780044702204753 -:104F30000680470C01470604470401470D10472A25 -:104F40000847160247060847040247060847062096 -:104F5000470680471F024703044729034729A04704 -:104F60002D10471E0200404704080147030200407D -:104F70004704080107804703200447058010474580 -:104F800001470603470D204718024706034703045D -:104F90004709C0470340472B38472108470B4000CB -:104FA00080470A80470502470580470502470518E4 -:104FB000084706202C471101470F04470D400030D9 -:104FC000C0472B1030C0472C30C0472B1030C04793 -:104FD0002C30C0471004471801001030C0470F8024 -:104FE000471C30C0472B1030C0472C30C0472B1017 -:104FF00030C0470E10471D30C0470A0200140B474F -:105000001D1030C0470C28E1471E30C0471B044725 -:105010000F1030C0471B0A471030C0470B20280B29 -:10502000471D1030C0470C14E1471E30C0472B10FD -:1050300030C0472C30C0472533069047031030C09E -:1050400047242031A480470430C0472B1030C0478C -:105050002C30C047253C0247041030C04723048051 -:105060003C14470530C04708804721801030C047B6 -:105070002901400030C047270147031030C0472CAA -:1050800030C0470A80471D0A47021030C047280138 -:10509000470330C0471D0300F047070547031030A2 -:1050A000C0471FF147070D9A470330C0471702A8B2 -:1050B00009704705404706010800041030C0471139 -:1050C00080470501544147041000796047050801F5 -:1050D00000080030C047150447023C47060B404714 -:1050E00003802A81000847021030C04715020007DC -:1050F000C05847041008684704AA85E1994703305F -:10510000C047183C1047050301428047023C470551 -:105110001030C0471707C04A470410086A06C04746 -:1051200002C3C5A0470430C0470F04470530000242 -:105130005A40470503014047033C47051030C0472C -:105140000F0A4707065848470410086B6680470255 -:10515000C3C5E00080470230C0470FF047083C104D -:105160000847040B41428047023C0147041030C00D -:10517000470F10470707C05A38470310002A06C0D8 -:105180004702C3C4E018470330C04711C0470502B7 -:105190005A500147040B41428047023C010147033A -:1051A0001030C0470E50470280470506585E71C058 -:1051B000470210002B66C04702C3C4AF00A04702DD -:1051C00030C04717025A504705034704013C4705C2 -:1051D0001030C047100180470506584870E047026C -:1051E000100028470301C3C4E04702180030C0473D -:1051F0000B083C47090100014098470401000340A7 -:1052000047023C3647041030C0470B083D60470951 -:1052100004470238E0470210002800404702C3C498 -:10522000E7470E3C47021010D9470D04470506C05A -:105230004702080247112000034B470D02470502B1 -:10524000800010081CE0470D0200084704020020FF -:105250004703028047041047041BB842C0470210AE -:1052600080900902470B1C470C01470A073E470381 -:105270001014979598471301E04707016BCA99905E -:1052800047093016B500024713F0470701606999D6 -:1052900090470506804702103488470F08470401ED -:1052A00000204704040002A8100847030A00836492 -:1052B000471580470401E04705020202A85C184731 -:1052C000030A037942804718D9F0470702003C10CF -:1052D00000E04704704704069E901B47110B4B806B -:1052E000E0470707C05C001000040807B04703014F -:1052F000A94F800A470E053203D42008B0024705A3 -:1053000008003C47020A470240A08047042801DD0C -:1053100080470F3003D56A7880024704300007C009 -:105320005C00044703A37847051C95471E3C4702D1 -:10533000A047090E01AD1A470E01604703784708E0 -:1053400017C05C00104708100617900A001847079E -:10535000020F4705A0014C470602900147021000CA -:10536000E04702080B38470534A01B4709920F7825 -:105370004705824D470601470204000C0010470311 -:1053800003BC000447020216950A471103D04702E6 -:105390002047040604003C2047032020F0804704F7 -:1053A00002378009471004001A7881470501041864 -:1053B0003C6047032020F78166800201802CE04793 -:1053C00003023FC04709024703D070002047040290 -:1053D0004706202020F001428080001382990902B4 -:1053E00000443FC04709010004001A8180470B01B7 -:1053F000002020F30366C00012A346054713404770 -:105400000A3C470280003020F047051E81002947F2 -:105410001004021A01E0470604183C60470420F714 -:1054200080E04702101E8CC0470E01470301C009EF -:10543000E02847097008002060F0804400800008E0 -:105440004E810A0080470B02470204021A79F004D9 -:10545000470B200020F3800700800004B4084712A7 -:1054600002D050A04707603C2047032060F08047EF -:1054700002500010038D471104000A50A00847068F -:10548000183C4047032020F7470410001D80470EBA -:1054900001470203C25010A04707229447048060CE -:1054A000F080470202003040C79F470D028000078E -:1054B000C0CAD04707081A9408470201E020F747FE -:1054C00003031030941F0E471003C06047020447C7 -:1054D000050C02940050A0006060F047052001ED2B -:1054E00080471007C1EE01E0804705080A9408478D -:1054F00002010020F7470306100014A547021847D1 -:105500000B01470203C040508447060C01680C5051 -:10551000A0003860F0470502810D1F40470C02478C -:105520000207C0EA00B84705020A0A940100A04732 -:105530000220F74702084000029EE00A471042A8F6 -:10554000103080004047112047040B47100AA85A2A -:10555000001000C0471150471543C0100080470895 -:105560003C470401010F34028047020142000E470C -:10557000100C3C5A01904707083CE04705073140B2 -:105580004702208155E0471123C0003047093C0005 -:10559000080040008000A0000347160C3C5A0001A0 -:1055A0004707083C6071804703512047070E4710AA -:1055B00023C0470201C047073C20008080000FF055 -:1055C000A14047170C3C5E4709083C400190470248 -:1055D0008FA5200280470401471123C010104709FE -:1055E0003C204703010103B8001000402042E09036 -:1055F00047100C3C4A99804707083C404704020783 -:1056000047022000100056879E471023C010104705 -:10561000093C00084702C04702014047031C16B17D -:1056200047110C3C4A984708083CC058470280473D -:1056300007102DD0471143C0470B3C470280004064 -:1056400047040347160C3C4E470201804705083CBF -:10565000C0001001470520470718470E43C2E04726 -:105660000A3C2010470501428047050D47100C3FBA -:10567000EE4709083CE098470602804705011047BD -:105680000C40471A6300294000100020470B704768 -:10569000088047111200A9408018470D40024704B6 -:1056A00008472720014704384712064706180040DC -:1056B000472503004003C20010472701470283D655 -:1056C0000018470E30470401470C04470540471BAC -:1056D00080470B02470402470240470F0F068047EE -:1056E00003083C000147050285470B0447020402FA -:1056F00081C047090102007A804703083D400080CD -:1057000047040102470B074703A804B18A470D2845 -:105710000002083C20014702C0470408403C000842 -:105720008000802A5034024702840281E108470C3D -:10573000064703083C6001E000C047034000183CF6 -:10574000605010148025A04702404702E804B98148 -:10575000470F50083C470B3C20014702402AA0401D -:10576000470201000280C0471050083D607A0001E6 -:10577000804705183D4000A001002AA730470204D9 -:1057800005E804F00A2018470C7000483C4707025F -:10579000700023C000700447022E7B3847030602C6 -:1057A00094E00E4710083CC047090BC140000A476F -:1057B0000222402800054702E826B00147031006F0 -:1057C000B04704807E80470301541847080800B0A2 -:1057D0001908D04705034047022A81470580BEF1DA -:1057E000984702020276803847022295EA01E100DA -:1057F000804704010A75E971F0470401E0400201A5 -:105800002A96E7804705D010470280AF0654300043 -:1058100002017C0B470601470202F43C470480501A -:10582000BC6047020430E6D047048286B01C4703C0 -:10583000053376684703147D4707800101417918D5 -:1058400008C00040A187E0470391E7B180470320EB -:1058500004A1470484128228470209680908804786 -:105860000702BC2A7180470401E047033E82091900 -:10587000470380A5E847030200F20E6C47020A9432 -:105880000139E1C0470714FA01C04705E047033E6C -:1058900094C009470303C14705AF0E4260020209E5 -:1058A00068087047080154087047034000FC47052A -:1058B00015910F470343C4A0470304053A46280542 -:1058C000000A940080470802955E800800040011D9 -:1058D00064470554A8470403C10100180008550A8D -:1058E000470420141C470601470202BC3A70804757 -:1058F0000205008002470303C0001E470343C5ADF5 -:105900009A4703A51F86470302BD6950A04705803B -:10591000470215EC80B04702050121404047028351 -:10592000D6000A470303C0050A4703403604204750 -:1059300002083C20084702804702024703D82F088C -:10594000F04702A8503C002047023C00D59B47038B -:1059500043C4880147020220D2A0644702083CE009 -:10596000504702C047040148D47A701000042475DF -:10597000F847043C15C38A470303C04703100004DB -:1059800003CC004040083C4708040001C019D84738 -:10599000030500BC0020008028B5918A470343C559 -:1059A000A01E001042205220600050283CE0798068 -:1059B000018047040102E8EBB80001800007A16004 -:1059C0000006001407E947021847023D000D0002D7 -:1059D00000050AC03C47047C10F0470702A809D024 -:1059E000A540002CBD746247022030CE8D47044094 -:1059F00021A01B4703A557542802002040F980B07E -:105A0000470401470202955D800847022C3DF7607C -:105A100040200020F4950A470472008011006200C3 -:105A20002F824702010347041000080010100202F1 -:105A3000040022002020000804240447070804472B -:105A4000032004002020202400082C040C0C470311 -:105A50000410100C00100008470308084703083022 -:105A60000030300400222606040800040400044725 -:105A7000042008000C000828000C0008002808007A -:105A80001008082808470228282847020C10101080 -:105A900024000224242C2C0400282C2101080011AD -:105AA0003010200004220222470320202030040866 -:105AB0003010001008000404040400040004470629 -:105AC00008000408080847020847022424040004C8 -:105AD0002C2C282847022101211004083010201006 -:105AE000470220002020080420202000044704044E -:105AF0000800040404040010101010100006081020 -:105B000008080302470204040802042424240400B1 -:105B10002C2C2820020021012100020A200028004C -:105B200008022828203004003030102000024702EC -:105B300004000A02040400140006101010080447B0 -:105B40000208080008020C040C2C02022C2C2C2C3D -:105B50000006282A2A470208220022000C022002FE -:105B600020204702202030300202507040000A0EF0 -:105B70000C080C044703040010080810100212045B -:105B80000002202020080824242400020247040ADE -:105B90000E47030204470206240608082C2428287E -:105BA0000202201030100E0A201028084702280098 -:105BB00020200A08203030180400021A0A040A0AB9 -:105BC0000C040C0C470208082808020C08082808D6 -:105BD0000C02280A280200080802200808002028D1 -:105BE0002010000C1010041008020404280C0408F3 -:105BF00028202121080031303020060E22222220C8 -:105C00000E0620470220040C101010000A47020460 -:105C10000404000C040447020A02470208000608B4 -:105C2000080A02080800060406040206082A284793 -:105C30000321213130060A303020200202202020AA -:105C4000000804002010000600100010000C0400E2 -:105C50000404040616141010100206181008080197 -:105C600047020804040A020404040604082A0C2A51 -:105C7000220A0223232322000822222A200A0228A1 -:105C80002A30100400101000080202000808000862 -:105C9000000C040C0C020618081818040008180858 -:105CA000080A020C0C0C0600080E04220E0A06223A -:105CB0002A22220008222222220C0222222222024E -:105CC0000032001010000240504000020C08080C86 -:105CD00004080A04044703081010121204000247C3 -:105CE00002200E04240424200C0E02000202020CE6 -:105CF0000202020204020602060602080A262A0816 -:105D0000080B303030300608202028284702820057 -:105D10008001030002282828000208082818180E0D -:105D2000091A1A0A080208080C0C0C00020C0022BE -:105D300008000E020A2202060302022202080802DA -:105D40000222020808222030100204101404040861 -:105D500000040008080408080001210200113010A6 -:105D600020080E022202000A084705021000101047 -:105D70000247021000040A0C04040006080802008E -:105D80000A0206000A0A020E0A00060606020004BB -:105D90000A0A0A02020013011130040A003000202E -:105DA00002470508061047030647050208100004CD -:105DB00047031414101000021810181801470208A5 -:105DC00004060200060606020C080A0A0A00080A6F -:105DD00003010320470202200A020A001A0A1A12CB -:105DE0000400081A08080002080808080800080C3F -:105DF0000C0C02040C001810040010180010000213 -:105E0000040606060808060202000A0C0002000248 -:105E100047032000220C020002120002001212129C -:105E200002470240424047020C08080804000204EE -:105E3000040447041012120400124703020004066F -:105E40000602470202020202470202470206040259 -:105E50000406060002000A021A0A000112120032A9 -:105E60000447022008200002080808080200080071 -:105E700008100403121A120847040402020206065C -:105E80000202000422020200060100020002470290 -:105E90002200020A47023A2A321A020404120404B7 -:105EA000470408080400080001010200111010470F -:105EB00002040202024708024702101002001010FA -:105EC0000010000604060602470202020A0006004D -:105ED000080A04020002040400020004080A0A106E -:105EE000020013131302044702024706104702067A -:105EF00047021000060010001047020A470214472C -:105F000003141410040002180018180001101206DF -:105F100016020006060200040008080847020201F3 -:105F2000010147041018100200181A1A0A04000A86 -:105F300002080200020008000847040404020404E6 -:105F40000400100400101012100002160606064786 -:105F50000302470202040808084706100402100062 -:105F600010100200100202020400020200020008E7 -:105F7000080808080002040404044704120204008C -:105F8000121010120200061606024703024706040A -:105F90000004000404001402001818181A0001126A -:105FA0000202020400024702080002080800080278 -:105FB00047050401120212104702101206120002D5 -:105FC00006060006000447022000060120002047C4 -:105FD000041018104702383A320A02040606060472 -:105FE0004702110162003F8247020103470802474E -:105FF00008024706010147062020470620204706E1 -:10600000042047064004470740470E8584470701A0 -:1060100047068047088047060202470602470802F3 -:10602000471E04470804471E0404471E2047072054 -:10603000204706202047060147080147360101474F -:10604000164247084247060202470E212047062013 -:1060500021470610204706141447065050470610E3 -:1060600010470610104706951147061094470690F2 -:106070009047061010470602104706020247260402 -:1060800004472604470804471E20470720204706E8 -:10609000202047060101472E01470801471E4242C2 -:1060A00047070247063020470630304706111147A0 -:1060B00006141047065014470610504706101047A4 -:1060C0000611104706101147069494470610904792 -:1060D0000610104706121047061212470610104706 -:1060E0000710470E04470804472E0404472E2020BB -:1060F0004706212047070147260101470882008003 -:106100000103472052024706305247063030470607 -:106110001010470611104706041547064040471661 -:106120000101470E84470804470E12024706121069 -:1061300047061012470610104706101047060414B1 -:10614000473604470804472E202047060121471EF2 -:10615000014708014716104707121047063012473B -:10616000067272470710470E04470741054707406C -:10617000470E014708014716040447060247080274 -:10618000470E020247061047071010470610104737 -:1061900006101047071047662120470721471601C0 -:1061A0000147161047071010470612124706203005 -:1061B0004706422047074247164140470701470E1E -:1061C000010147260202471602470802470E104700 -:1061D00007101047061010470710475E2047072199 -:1061E000214708110262002F824702010347081B62 -:1061F0001B47041F1B4704041F47101B47051B1B9D -:1062000047041F1B47041F1F47101B1B47041B1B72 -:1062100047041F1F47041F1F47051B470A1B1B4737 -:10622000051B4704040447041F1F47051B470A1B9F -:106230001B4704E4FB4704E4E447041FFF47051B36 -:10624000470AF9194704E4F94704E4E44704F7F777 -:10625000470513470AF9F94704E4F94704E4E4471A -:1062600004F7F74705F3470AF9E9470414FD470423 -:106270001E1C4704F7FF4705E1470AE9E947041CEC -:10628000FD47041E1A4704EFFF4705E1470ADDC931 -:1062900047041EDD47043E3A4704BDBF4705A947F2 -:1062A0000ADDD94704A2FF4704A6A24704BDBD47A3 -:1062B00005B9470AFFDD4704A6FF47048486470463 -:1062C000BDBD470404BD470AFFFD470406FF470460 -:1062D000040447043D3D4704043D470AFFFF4704CB -:1062E00006FF470404044704FDFD470404FD470A74 -:1062F000FBFB4705FB4704244705FDFD4705F94720 -:106300000AFBFB4704FBFB4704FFFB4704FFFF4777 -:1063100005FB470AFB224704FBFB4704DFFB47045E -:10632000FFFF470522470A22024704FBFB4704DF21 -:10633000DB470426FF4705224780028200800103D5 -:1063400047022C2C47041F3E47041F1F4704CCDE86 -:106350004705C8470A3E2C47041F3F47049F1F4775 -:1063600004DEDE4705C8470A3F3E47041F3F470497 -:106370009F9F4704DEDE4705DA470A3736470401A8 -:10638000374704898147045ADA470552470A763667 -:106390004704C1F64704A9814704FAFA4705D247E2 -:1063A0000A74744704C1F44704A9E94704F2FA47A0 -:1063B00005F2470A74744704E5F44704EDED470419 -:1063C000F2FE4705F2470A7474470427744704CF66 -:1063D0008F470476FE470570470A747447041F749C -:1063E00047049F9F470474F6470574470A7464473F -:1063F000041B7447049B9B470476F6470574470AC1 -:1064000060604704197047048999470476F6470588 -:1064100076470A6060470409604704898947047623 -:10642000F6470576470A64644704096C4704818986 -:10643000470476F6470576470A76764704097E478D -:10644000048189470476F6470576470A767647043D -:10645000297E4704C189470476F6470576470A77BF -:106460007647047F7F4704F7FF470477F7470576B1 -:10647000470A776047045F7F4704B7FF470477F711 -:10648000470560470A612047045F7F4704B79F477D -:106490000461F7470540478002110362003F8247CD -:1064A00002010347838282008001034783822244E2 -:1064B000B2010600470000000000000000000000DC -:1064C00000040240000400000A0009000014024019 -:1064D000000400000A000900000C0240000800004F -:1064E0000B00090000080240000400000A00090037 -:1064F0000014024080000000070009000000024074 -:106500000200000001000900001C02402000000001 -:1065100001000000001C0240080000000100000013 -:106520000000024000080000010000000000000818 -:106530000080000800000108008001080000020837 -:10654000000004080000080800000C080000100803 -:10655000000014080000180800001C0800000800D3 -:1065600010001800200028003000380040004800CB -:1065700050005800000000006410B71DC8206E3B9A -:10658000AC30D9269041DC76F4516B6B5861B24D3A -:106590003C7105502083B8ED44930FF0E8A3D6D6A4 -:1065A0008CB361CBB0C2649BD4D2D38678E20AA00C -:1065B0001CF2BDBD4932435F504D49435F4D4147D9 -:1065C00000537475636B20627574746F6E2072650E -:1065D00067697374657220697320696E76616C698E -:1065E000642C20636C656172696E672E004275745D -:1065F000746F6E2069642000697320737475636B17 -:106600002100427574746F6E20776173207075730A -:10661000686564206F6E20626F6F742E20427574FF -:10662000746F6E20636F756E7465723A20004469F2 -:1066300073706C617920627573792D77616974204C -:1066400074696D656F757420657870697265642111 -:10665000004650474120636F6E66696775726174CA -:10666000696F6E206661696C65642E0044697370A1 -:106670006C617920696E697469616C697A656420FE -:106680006166746572200020726574726965732E8C -:1066900000446973706C617920696E697469616C1A -:1066A000697A6174696F6E206661696C65642E0039 -:1066B000435245534554206E6F74206C6F772064AD -:1066C0007572696E672072657365740043444F4E3E -:1066D00045206E6F74206C6F772061667465722040 -:1066E000726573657400435245534554206E6F7450 -:1066F00020686967682061667465722072657365D9 -:10670000740043444F4E45206E6F7420686967687B -:106710002061667465722070726F6772616D6D6959 -:106720006E6700456E61626C696E67203676362052 -:1067300028446973706C61792056444443290045AC -:106740006E61626C696E6720347635202844697307 -:10675000706C617920564444502900446973616229 -:106760006C696E67203476352028446973706C61DB -:10677000792056444450290044697361626C696E03 -:1067800067203676362028446973706C617920560C -:1067900044444329002E2E2F7372632F647269764E -:1067A0006572732F6932632F6932632E63002E2E58 -:1067B0002F7372632F647269766572732F69326307 -:1067C0002F6932635F68616C2E63004661737420C9 -:1067D0004D6F646520506C7573206E6F7420796501 -:1067E0007420737570706F72746564002E2E2F7331 -:1067F00072632F647269766572732F706572697047 -:10680000685F636F6E6669672E63006164647265BA -:1068100073732000206973206F7574736964652039 -:1068200073797374656D20666C6173680066616965 -:106830006C656420746F2065726173652073656395 -:10684000746F72200050726F6772616D20666169AB -:106850006C6564204000496E76616C6964206669ED -:10686000726D7761726520646573637269707469B3 -:106870006F6E2100436865636B73756D6D696E673C -:10688000206669726D7761726520757064617465E8 -:106890000043616C63756C61746564206368656353 -:1068A0006B73756D3A2000496E76616C6964206681 -:1068B00069726D776172652043524320696E20537F -:1068C000504920666C61736821007072765F657252 -:1068D0006173655F6F6C645F6669726D7761726525 -:1068E000007072765F77726974655F6E65775F6658 -:1068F00069726D77617265005765277265206465FE -:10690000616400436865636B73756D6D696E6720C4 -:10691000002062797465730D0A00436865636B73C8 -:10692000756D202D2077616E746564200020676F7F -:106930007420004F757220696E7465726E616C20F0 -:10694000666C61736820636F6E74656E747320612A -:106950007265206261642028636865636B73756D7E -:10696000206661696C656429212054686973206917 -:1069700073207265616C6C792062616421004F75CF -:10698000722070726576696F7573206669726D77B3 -:1069900061726520757064617465206661696C65FB -:1069A000642C2061626F7274696E67207570646117 -:1069B00074652E004E6577206669726D7761726529 -:1069C00020697320617661696C61626C6521004C9D -:1069D0006F6164696E67207265636F766572792096 -:1069E0006669726D77617265004661696C656420E5 -:1069F000746F206C6F6164207265636F7665727965 -:106A0000206669726D776172652C20737472696B90 -:106A100065206F6E652E2054727920616761696E02 -:106A20002E004661696C656420746F206C6F616430 -:106A3000207265636F76657279206669726D776121 -:106A400072652C20737472696B652074776F2E20C9 -:106A500054727920616761696E2E004661696C65C8 -:106A60006420746F206C6F6164207265636F76655B -:106A70007279206669726D776172652C2073747209 -:106A8000696B652074687265652E205341442057F8 -:106A9000415443480048415244204641554C54001B -:106AA00065786974207374616E646279000D0A0DF3 -:106AB0000A0D0A00E29688E29688E29688E29688B5 -:106AC000E29688E29688E295972020E29688E29600 -:106AD00088E29688E29688E29688E29688E2959720 -:106AE00020E29688E29688E29688E29688E2968886 -:106AF000E29688E2959720E29688E29688E2968868 -:106B0000E29688E29688E29688E29688E29597E295 -:106B10009688E29688E29688E29688E29688E296DF -:106B200088E2959720E29688E29688E29688E29637 -:106B300088E29688E29688E29688E29688E29597BF -:106B400000E29688E29688E29594E29590E295902C -:106B5000E29688E29688E29597E29688E29688E245 -:106B60009594E29590E29590E29590E29688E2966F -:106B700088E29597E29688E29688E29594E295906D -:106B8000E29590E29688E29688E29597E29688E20E -:106B90009688E29594E29590E29590E29590E29540 -:106BA00090E2959DE29688E29688E29594E295902F -:106BB000E29590E29688E29688E29597E2959AE2CD -:106BC0009590E29590E29688E29688E29594E29517 -:106BD00090E29590E2959D00E29688E29688E29692 -:106BE00088E29688E29688E29688E29594E2959DFE -:106BF000E29688E29688E29591202020E29688E24B -:106C00009688E29591E29688E29688E29688E296E6 -:106C100088E29688E29688E29594E2959DE29688CD -:106C2000E29688E29688E29688E29688E295972036 -:106C300020E29688E29688E29688E29688E2968834 -:106C4000E29688E29594E2959D202020E29688E2E3 -:106C50009688E2959120202000E29688E29688E2CC -:106C60009594E29590E29590E29688E29688E29576 -:106C700097E29688E29688E29591202020E2968815 -:106C8000E29688E29591E29688E29688E29594E20F -:106C90009590E29590E29688E29688E29597E29642 -:106CA00088E29688E29594E29590E29590E2959D2F -:106CB0002020E29688E29688E29594E29590E2950B -:106CC00090E29688E29688E29597202020E29688C6 -:106CD000E29688E2959120202000E29688E296884C -:106CE000E295912020E29688E29688E29591E295DD -:106CF0009AE29688E29688E29688E29688E29688FA -:106D0000E29688E29594E2959DE29688E29688E282 -:106D10009688E29688E29688E29688E29594E295D3 -:106D20009DE29688E29688E29688E29688E29688C6 -:106D3000E29688E29688E29597E29688E29688E263 -:106D400095912020E29688E29688E2959120202075 -:106D5000E29688E29688E2959120202000E2959ABA -:106D6000E29590E2959D2020E2959AE29590E29539 -:106D70009D20E2959AE29590E29590E29590E295B9 -:106D800090E29590E2959D20E2959AE29590E295A9 -:106D900090E29590E29590E29590E2959D20E295A3 -:106DA0009AE29590E29590E29590E29590E2959026 -:106DB000E29590E2959DE2959AE29590E2959D206C -:106DC00020E2959AE29590E2959D202020E2959A06 -:106DD000E29590E2959D202020004C61737420661E -:106DE00069726D7761726520626F6F74207761736D -:106DF00020737461626C653B20636C656172207303 -:106E00007472696B657300486F6C6420646F776E91 -:106E1000205550202B204241434B202B2053454CE2 -:106E200045435420666F72203520736563732E20AE -:106E3000746F20666F7263652D626F6F742050529D -:106E400046004669726D77617265206973206572CC -:106E500061736564005761746368646F6720636180 -:106E600075736564206120726573657400536F6685 -:106E70007477617265206661696C75726520636103 -:106E8000757365642061207265736574004661697D -:106E90006C656420746F207374617274206669720B -:106EA0006D776172652C20737472696B65207468EC -:106EB0007265652E004661696C656420746F20738D -:106EC00074617274206669726D776172652C2073CB -:106ED0007472696B652074776F2E004661696C650A -:106EE0006420746F207374617274206669726D77A8 -:106EF0006172652C20737472696B65206F6E652EEC -:106F000000466F7263652D626F6F74696E672072E1 -:106F100065636F76657279206D6F64652E2E2E0025 -:106F2000456E61626C696E67207761746368646F37 -:106F30006700426F6F74696E67206669726D776172 -:106F40007265204020002E2E2E0D0A0D0A00426F81 -:106F50006F7420626974733A2000536F66747761AE -:106F60007265206661696C7572653B207265736538 -:106F70007474696E6721004153534552543A20009E -:106F80003A00415353455254004153534552544ED5 -:106F9000002A2A2A20575446200053544D333200E9 -:106FA00053544D3332207065726970686572616C3C -:106FB000206C6962726172792074726970706564A4 -:106FC00020616E206173736572740069746F612053 -:106FD00062756666657220746F6F20736D616C6C8C -:106FE000002E2E2F7372632F7574696C2F736C656E -:046FF0002E6300000C -:106FF400FF00000000010203040102030406070865 -:04700400090000007F -:0400000508000200ED -:00000001FF diff --git a/bin/boot/boot_robert_evt@1478015115.bin b/bin/boot/boot_robert_evt@1478015115.bin deleted file mode 100755 index e0c8ab83c3..0000000000 Binary files a/bin/boot/boot_robert_evt@1478015115.bin and /dev/null differ diff --git a/bin/boot/boot_robert_evt@1478015115.hex b/bin/boot/boot_robert_evt@1478015115.hex deleted file mode 100644 index a43d388478..0000000000 --- a/bin/boot/boot_robert_evt@1478015115.hex +++ /dev/null @@ -1,1799 +0,0 @@ -:020000040800F2 -:1000000000AA0120E5250008E1250008211E0008BE -:10001000E1250008E1250008E125000800000000B6 -:10002000000000000000000000000000E1250008C2 -:10003000E125000800000000E1250008E125000896 -:10004000E1250008E1250008E1250008E125000878 -:10005000E1250008E1250008E1250008E125000868 -:10006000E1250008E1250008E1250008E125000858 -:10007000E1250008E1250008E1250008E125000848 -:10008000E1250008E1250008E1250008E125000838 -:10009000E1250008E1250008E1250008E125000828 -:1000A000E1250008E1250008E1250008E125000818 -:1000B000E1250008E1250008E1250008E125000808 -:1000C000E1250008E1250008E1250008E1250008F8 -:1000D000E1250008E1250008E1250008E1250008E8 -:1000E000E1250008E1250008E1250008E1250008D8 -:1000F000E1250008E1250008E1250008E1250008C8 -:10010000E1250008E1250008E1250008E1250008B7 -:10011000E1250008E1250008E1250008E1250008A7 -:10012000E1250008E1250008E1250008E125000897 -:10013000E1250008E1250008E1250008E125000887 -:10014000E1250008E1250008E1250008E125000877 -:10015000E1250008E1250008E1250008E125000867 -:10016000E1250008E1250008E1250008E125000857 -:10017000E1250008E1250008E1250008E125000847 -:10018000E1250008E1250008E1250008E125000837 -:10019000E1250008E1250008E1250008E125000827 -:1001A000E1250008E1250008E1250008E125000817 -:1001B000E1250008E1250008E125000861070008A5 -:1001C0006D070008E125000800000000E125000897 -:1001D000E1250008E1250008E1250008E1250008E7 -:1001E000E1250008E1250008E1250008E1250008D7 -:0801F000E1250008E1250008EB -:0801F8009C0000000100000062 -:1002000053B94AB9002908BF00281CBF4FF0FF317D -:100210004FF0FF3000F03CB882B0EC462DE90050C2 -:1002200000F01EF8DDF804E002B00CBC704700BF1F -:100230002DE9F041904606460F461D46069C00F00B -:1002400029F808FB01FC8646A8FB002300FB05C536 -:10025000B21A2B4467EB0303C4E90023BDE8F08125 -:100260002DE9F8431D46174680468946089E00F052 -:1002700053F900FB05F3A0FB074507FB0137B8EB7B -:1002800004043D4469EB0505C6E90045BDE8F88373 -:10029000704700BF00292DE9F047C0F2A280002678 -:1002A000002BC0F298808C4690469E461546044628 -:1002B0000F46CBBB8A4256D9B2FA82F33BB1C3F1A7 -:1002C00020029F409D409C4020FA02F21743280CD8 -:1002D000220C1FFA85FEB7FBF0F100FB11770EFB35 -:1002E00001F342EA0747BB4207D97F1980F0018139 -:1002F000BB4240F2FE8002392F44FF1AA4B2B7FB82 -:10030000F0F300FB13770EFB03FE44EA0747BE45FC -:1003100006D97F1980F0EB80BE4540F2E880023BB1 -:1003200043EA0143002203E08B420FD90022134627 -:10033000341C4FF0000518BF0124604265EB4501F5 -:100340005840514000196941BDE8F087B3FA83F283 -:10035000002A40F08380804540F2CD808B42C0F07F -:10036000CA801346E4E712B90123B3FBF2F5B5FAEC -:1003700085F2002A3BD1781B4FEA154E1FFA85FC07 -:100380000122210CB0FBFEF80EFB18000CFB08F359 -:1003900041EA0047BB4208D97F1980F0B080BB42D8 -:1003A00040F2AD80A8F102082F44FF1AA4B2B7FBB7 -:1003B000FEF30EFB13770CFB03FC44EA0747BC4536 -:1003C00006D97F1980F09980BC4540F29680023BA7 -:1003D00043EA0843ACE752426FEA060663EB430385 -:1003E00061E740424FF0FF3661EB410158E795402D -:1003F000C2F1200107FA02F34FEA154ECF4024FA6A -:1004000001F194401FFA85FC1943B7FBFEF24FEA55 -:1004100011480EFB12770CFB02F348EA0747BB4278 -:1004200005D97F1971D2BB426FD9023A2F44FF1A06 -:1004300089B2B7FBFEF80EFB18770CFB08F041EA17 -:100440000743984206D95B1961D298425FD9A8F157 -:1004500002082B44181A48EA024292E7C2F1200728 -:1004600003FA02FE08FA02F5914028FA07F32CFA83 -:1004700007FCF84043EA0E0E08434FEA1E48070CFB -:100480001FFA8EFABCFBF8F908FB19CC0AFB09F13C -:1004900047EA0C4C614507D91CEB0E0C32D2614582 -:1004A00030D9A9F10209F444C1EB0C0C80B2BCFBB9 -:1004B000F8F308FB13CC0AFB03FA40EA0C418A4527 -:1004C00006D911EB0E0125D28A4523D9023B71448E -:1004D00043EA0943CAEB0101A3FB0589494503D35C -:1004E00003D19440444500D2013B002220E7013B68 -:1004F00016E7013901E701231AE7013B68E708F134 -:10050000FF3852E709F1FF39CEE7013A8FE708F1EA -:10051000FF389FE7013BDBE72DE9F043002B40D19B -:100520008A42044615464AD9B2FA82F30F464BB1C5 -:10053000C3F12006994000FA03F402FA03F5F040F3 -:1005400040EA0107290C260C1FFA85FEB7FBF1F0E3 -:1005500001FB10770EFB00F246EA07439A4207D9E7 -:100560005B1980F0EA809A4240F2E78002382B441F -:100570009A1AA4B2B2FBF1F301FB13220EFB03FEA5 -:1005800044EA0242964506D9521980F0DA8096452F -:1005900040F2D780023B43EA004000263146BDE8E6 -:1005A000F0838B4244D8B3FA83F6002E45D18242C1 -:1005B00040F2BF808B42C0F0BC803046EEE712B9FB -:1005C0000125B5FBF2F5B5FA85F2002A7BD14A1B6D -:1005D0002F0C1FFA85FE0126230CB2FBF7F007FB58 -:1005E00010220EFB00FC43EA02418C4507D9491951 -:1005F00080F0A1808C4540F29E8002382944CCEBEB -:100600000101A4B2B1FBF7F307FB13110EFB03FECC -:1006100044EA0144A64506D9641980F09080A645B5 -:1006200040F28D80023B43EA00403146BDE8F08352 -:10063000002630463146BDE8F083C6F12005B340C0 -:1006400002FA06F701FA06F4EA40E94020FA05F555 -:100650001A4325434FEA124C4FEA154E93B2B1FBB1 -:10066000FCF80CFB181103FB08F44EEA01418C4224 -:1006700006D9891869D28C4267D9A8F102081144B9 -:10068000091BADB2B1FBFCF40CFB141103FB04FE1F -:1006900045EA01439E4505D99B1854D29E4552D93F -:1006A000023C134444EA0844CEEB0303A4FB07894D -:1006B0004B4503D351D1B04040454ED20026601E79 -:1006C0003146BDE8F083C2F12006954001FA02F3FD -:1006D00000FA02F42F0CF140F0401FFA85FEB1FB46 -:1006E000F7F6184307FB16110EFB06F24FEA104C03 -:1006F0004CEA01439A4205D95B1929D29A4227D97B -:10070000023E2B449B1A80B2B3FBF7FC07FB1C3361 -:100710000EFB0CF140EA0343994206D95B1919D24A -:10072000994217D9ACF1020C2B445A1A4CEA0646EE -:1007300052E7012032E7013861E7013818E7013B51 -:1007400071E7013B27E7013CACE708F1FF3897E789 -:10075000013ED7E70CF1FF3CE7E7204600261DE706 -:10076000014800F0E2BF00BFE82B0008014801F09B -:1007700044B800BFE82B0008014800F005BE00BFE8 -:10078000E82B000837B50024012002F009FA019097 -:100790002546E0B200F046F801AA08B910551DE060 -:1007A000135D042B0BDD174800F03CF9019800F0B5 -:1007B00040F90120002102F0E3F9002020E001339C -:1007C000DBB2042B135509D90F48012500F012F9AB -:1007D000204600F02EF90D4800F024F90134042CD5 -:1007E000D7D1019C3CB10A4800F004F9204600F042 -:1007F00020F900F08FF80120214602F0C1F92846C7 -:1008000003B030BDD1650008FD650008086600082A -:100810001266000808B500F083FB80F00100C0B24A -:1008200008BD000008B50C22044B02FB003000F0AC -:1008300099FC80F00100C0B208BD00BF882C000800 -:1008400010B5002401F096F8054820440C34017AD4 -:1008500000F06EFC302CF7D1BDE8104001F08BB8F1 -:10086000882C0008044BDA691106FBD59862DA6916 -:100870001206FCD5704700BF0048004070B5214DFE -:100880008AB02148082101F077F828464FF48021EA -:100890001E4C01F071F82B68642623F0010394E8E4 -:1008A00007002B600C348DE8070000216846022207 -:1008B0000B4600F01FFC03AB94E8070083E8070039 -:1008C00000211846022200240B4600F013FC06A863 -:1008D00002F07CF8089821460D4A70430023FFF788 -:1008E0008FFCC0F3420320F00F000004000C1843FB -:1008F0000D23B0FBF6F0E8606C60AC602B600AB0D2 -:1009000070BD00BF00480040000C0240B82C000839 -:1009100040420F002DE9F043334D83B02C6800AF07 -:1009200004F12E0304F1270823F00703ADEB030DB8 -:1009300050238DF8003021236E460DF106028DF80C -:10094000013000238DF8023003238DF8033004F1C9 -:1009500021035BBAADF80430244B03F11C0153F8BA -:10096000040B8B4242F8040BF9D11B7804F12309E4 -:1009700006F123001E491370224602F021F9314688 -:100980004A46002001F072FD07F1080304F5927455 -:10099000314643F8040D42461868FE23B4FBF3F3D6 -:1009A000434446F80900073323F00703ADEB030D7A -:1009B00068466E4601F032FD044655206C44FFF750 -:1009C00051FFA64207D016F8010B552808BF00209A -:1009D000FFF748FFF5E75520FFF744FF00230C37EA -:1009E0002B60BD46BDE8F08338000020D02C000805 -:1009F0003C00002010B5441E14F8012F7AB10849BC -:100A00000B68FF2B0BD80A2A02D1FFF783FFF3E70D -:100A10000D2A1FBF581C5B1808601A71ECE710BD47 -:100A20003800002008B5FFF7E5FFBDE80840FFF7F4 -:100A300071BF1FB50C2201A901F046FD01A8FFF707 -:100A4000D9FF05B05DF804FB07B502A9012201F842 -:100A5000010D042000F096F903B05DF804FB0021BD -:100A600002200A4600F08EB910B540F2F51400F0ED -:100A7000B9F850B1013C04D10548FFF7D3FF204637 -:100A800010BD642001F012FDF1E7012010BD00BF90 -:100A90003E66000810B5074C43F2CD71064848F693 -:100AA000B803224601F058FD01462046BDE810403B -:100AB00000F0E4B83C010020ED2C000810B500F077 -:100AC00099F8FFF7E7FF08B9154816E0002400F091 -:100AD00039F9FFF7C4FF0120FFF7B6FFFFF7C4FFA6 -:100AE00078B1002103200A4600F04CF90D48FFF7C9 -:100AF00081FF2046FFF79DFF0B48BDE81040FFF740 -:100B000091BFFFF7C7FF0028DED001340B2CE2D1E4 -:100B10000648FFF787FFBDE81040FFF7A0BF00BF02 -:100B2000616600087C66000897660008A1660008F8 -:100B300008B5FFF799FF0120FFF786FFFFF794FF45 -:100B4000002103200A46BDE8084000F01BB9000060 -:100B50002F2307B54A1C58430B4B00EB52001A7861 -:100B6000B0FBF1F0C0B282420AD002A91870042290 -:100B700041F8040D012000F005F90220FFF764FFA1 -:100B800003B05DF804FB00BF0000002007B502A918 -:100B9000042241F8040D012000F0F4F80320FFF7CF -:100BA00053FF03B05DF804FBFFF759BF0C4B014442 -:100BB00030B51C68884207D022689568AD07FCD51F -:100BC00010F8015B1573F5E71B681A68936813F456 -:100BD000C05FFBD10A20BDE8304001F067BC00BF18 -:100BE000382C0008024B1868403000F0BBBA00BF38 -:100BF000382C000838B5204B012200211C6804F174 -:100C00002005284600F048FA0021284600F05EFA48 -:100C100004F1380000F0A0FA04F1400000F09CFA62 -:100C2000012104F12C000A4600F036FA002104F1FB -:100C3000080001220B4600F05DFA0021012204F1B8 -:100C400014000B4600F056FA6068012101F070FFB5 -:100C50006068002101F06CFF94E8030000F08CFE56 -:100C600023684CF207321A604FF4B8525A601A687F -:100C700042F040021A6038BD382C00082B4B2DE999 -:100C8000F7431C68814601208846E36A04F1200787 -:100C900004F12C060093236B019301F00BFC384602 -:100CA000012100F013FA0021304600F00FFA012074 -:100CB00001F000FC684600F055FA054620B11C48DA -:100CC0000025FFF7AFFE2CE030460121383400F05C -:100CD000FDF9012001F0EEFB204600F043FA064644 -:100CE00008B114481BE0684600F03CFA054608B914 -:100CF000114814E0012001F0DDFB48464146FFF7B2 -:100D000055FF3846314600F0E1F90C480921FFF75C -:100D10004DFF204600F026FA054610B90848FFF7B7 -:100D200081FE284603B0BDE8F08300BF382C0008E0 -:100D3000C0660008DC660008F6660008BA640008B1 -:100D40001267000808B5022001F0B4FB0A4B1B68CB -:100D500093F8483043B10948FFF764FE012000F0E2 -:100D6000D9FD022001F0A6FB0548FFF75BFE01203C -:100D7000BDE8084000F0C8BD382C00083367000803 -:100D80004F6700080F4BF7B51C6802AD0F460121F5 -:100D9000203405F8010D1646204600F097F964202E -:100DA00001F084FB28460121FFF700FF1EB1384601 -:100DB0003146FFF7FBFE2046002100F087F903B023 -:100DC000F0BD00BF382C0008F0B50746ADF2044D69 -:100DD0000E460C460025B919B4F5806F6846A1EBA4 -:100DE00004010DD94FF48062A4F5806400F0D8F8B6 -:100DF000284669464FF4806201F038FB0546EAE771 -:100E0000224600F0CDF869462246284601F02EFB26 -:100E10000DF2044DF0BD08B5202001F0A1FD002821 -:100E2000FAD108BD08B5022001F09AFD0028FAD0D9 -:100E30000220BDE8084001F09DBD00B58DB06846B8 -:100E400001F017FD0023684600934FF480730A9366 -:100E500035230B9301F03EFDFFF7E4FFFFF7DBFFC7 -:100E60000DB05DF804FB000030B5002495B02A48B1 -:100E7000022100F081FD294D0021032228190C34A4 -:100E80000B4600F037F9482CF5D1002105F1480058 -:100E900000240A4600F000F9002105F148004FF453 -:100EA000407500F013F901A801F0DAFC102301A845 -:100EB00002940193172303940593049406940794D2 -:100EC00001F0E6FC012001F029FDFFF7B6FF08A8BC -:100ED00001F0CFFC662308A808941393129501F043 -:100EE000F9FCFFF79FFF08A801F0C3FC992308A8AD -:100EF00008941393129501F0EDFCFFF793FF4CF269 -:100F0000503001F0D3FAFFF798FF0348022100F0B8 -:100F10003DFD15B030BD00BF001000A0C464000846 -:100F200030B502218DB01C4800F026FD022001F0F2 -:100F300009FD684601F09DFC4FF080636846009310 -:100F40004FF0407304934FF440730A93AF230B9315 -:100F500001F0C0FCFFF766FF01F0FCFC054601F064 -:100F6000F9FC044601F0F6FC240244EA004040EAA1 -:100F70000504FFF750FF094B9C4204D02046002493 -:100F8000FFF757FD00E001240348022100F0FEFCBA -:100F900020460DB030BD00BF001000A020BB1800DF -:100FA00070B515468CB004460E461B48022100F071 -:100FB000E3FC681E01F0C6FC684601F05AFC4FF0E5 -:100FC00080636846254400934FF0407304930A23DE -:100FD00005934FF4406309934FF440730A934FF421 -:100FE000005308930B230B9301F074FC304601F07F -:100FF000A1FCAC4204D001F0ADFC04F8010BF8E711 -:10100000022001F0B7FCFFF706FF0348022100F0C1 -:10101000BDFC0CB070BD00BF001000A0034A136BF4 -:1010200023F4FF6323F00703136370470038024083 -:1010300000F13F4000F57E00C0F387200A280AD85F -:10104000054A135C591C11542BB9044901230A6B3E -:10105000834013430B637047F4890020003802403B -:1010600000F13F4000F57E00C0F387200A280DD82C -:10107000074A135C53B1013BDBB2135433B9054942 -:1010800001230A6B834022EA03030B63704700BF0E -:10109000F489002000380240436813B5009301230F -:1010A000044600688DF8043000238DF805208DF883 -:1010B00007308DF80610FFF7BBFF2068694601F086 -:1010C00059FB2068FFF7CCFF02B010BD38B5037A9A -:1010D00004460D460BB981F001052068FFF7A8FF13 -:1010E0002068A1882A4601F083FB2068BDE83840CB -:1010F000FFF7B6BF13B5044640688DF8073000907F -:1011000002208DF805208DF8040020688DF8061067 -:10111000FFF78EFFA27A2068218901F06DFB20681D -:10112000694601F027FB2068FFF79AFF02B010BD67 -:10113000002313B5044601934368006800938DF8BB -:101140000710FFF775FF2068694601F013FB206860 -:10115000FFF786FF02B010BD036813B10021FFF74F -:10116000E7BF704738B504460068FFF761FFA18804 -:10117000206801F037FB05462068FFF771FF281C47 -:1011800018BF012038BD1FB5836804460093C368AB -:1011900068460193FFF7E0FF636902A80293A36921 -:1011A0000393FFF7D9FF04B010BD10B50446012129 -:1011B000083002220023FFF79DFF04F114000121F3 -:1011C00002220023BDE81040FFF794BF0368DB69EB -:1011D00010B5044673B9C36A63B10A2001F06AF915 -:1011E0002046FFF7D0FF2046E36A012198471420EC -:1011F00001F060F92046FFF7D8FF2046BDE8104017 -:1012000000F0E0B910B5044600F04FFA48B10A20EA -:1012100001F050F9204600F048FA80F00100C0B219 -:1012200010BD012010BD30B5044687B0C36A00214F -:101230009847A3680021012500936846E3680A46A1 -:101240008DF808500193FFF727FF68460021FFF74C -:101250003DFF6369002103A80393A3690A468DF843 -:1012600014500493FFF718FF03A80021FFF72EFF87 -:1012700007B030BD10B5044600F00AFA2368DB69F8 -:1012800033B9E36A4BB12046BDE81040FFF7CBBF4E -:101290002046BDE81040FFF776BF10BD10B50446EC -:1012A000FFF7E8FF2046BDE81040FFF78FBF0000C2 -:1012B0002DE9F3419846036804460E46DB6917465C -:1012C0000BB900205BE000F0F0F9B8B925681422F2 -:1012D0000021284601F07FFC9DF828306F802046D1 -:1012E0006B71089B2E71AB60099B85F806802B61A2 -:1012F00000F0ECF9236800229A6111E02046FFF724 -:10130000CDFF2046FFF77EFF0028DFD1D9E79A699D -:10131000B2F57A7F23DA013202209A6101F0CAF82D -:10132000204600F0DFF90122236883F82020154AC7 -:101330000192019A511E01911AB193F82020002ABE -:10134000F7D1002283F82020019A42B11D7D05F0DB -:10135000FD02012ADBD16B1E5D425D4103E00025E9 -:10136000204600F0A8F923680022204683F82020B8 -:10137000FFF748FF10B92046FFF790FF284602B05C -:10138000BDE8F08180841E0010B5044618B90E48EF -:10139000A92101F00BF800212422006801F01BFCB8 -:1013A000204600F0CBF8236A2BB1002104F1200085 -:1013B0000A46FFF771FEE36A23B12046BDE81040FC -:1013C000FFF731BF10BD00BFA567000810B5044688 -:1013D00018B90848B62100F0E9FF00680368DB6926 -:1013E0000BB9FFF7F3FE23681A68D3690133D361A1 -:1013F00010BD00BFA567000808B518B90748BF2190 -:1014000000F0D4FF00680268D36933B1013BD361B7 -:101410001BB9BDE80840FFF72DBF08BDA567000850 -:101420001FB50C46114618B90A4840F28D1103E069 -:1014300023B908484FF4C77100F0B8FF82888DE8DF -:101440000A000021234602910068FFF731FF04B033 -:1014500010BD00BFA567000813460122FFF7E0BFDB -:101460001FB50C46114618B90A484FF4D77103E06E -:1014700023B9084840F2AF1100F098FF82888DE848 -:101480000A0000230121029323460068FFF710FFA2 -:1014900004B010BDA567000807B502AB03F8012D25 -:1014A0000122FFF7DDFF03B05DF804FB03680022B3 -:1014B000197583F82020704710B504686388227975 -:1014C000C3F3090322B92278012A18BF43F48063C9 -:1014D000A268E468121BFF2A95BF120443F0FF7351 -:1014E00002F47F0243F4803398BF134309B143F4FD -:1014F000005342681268536010BD43681B681A6845 -:1015000022F0D60210B51A605A6842F480425A603E -:1015100008220368BDE810401A70FFF7C7BF0368D0 -:10152000012110B50446586801F0F6FA002123683D -:10153000BDE81040586801F0EFBA000043685A7BDC -:1015400053B2002B30B506DA02F00F021B4B1344E6 -:10155000C0221A7606E003F16043C02203F561431E -:1015600083F8002343689A7B53B2002B06DA02F01B -:101570000F02124B1344C0221A7606E003F16043B7 -:10158000C02203F5614383F8002350F8045F0C4C3C -:101590006B7B59B203F01F020123490903FA02F2DF -:1015A00044F82120AA7B51B202F01F02490993405E -:1015B00044F82130BDE83040FFF7B1BFFCEC00E05B -:1015C00000E100E043682C499A688A42F0B507467A -:1015D00087B004D929485121294A00F0DBFE294966 -:1015E0008A4205D91C7B34B1012C07D000F0E8FEFB -:1015F0000124254603E00924102500E0022502A865 -:1016000001F0E4F979686019049A8B684343B2FBEE -:10161000F3F603FB162303B9013E0F2E03D9174837 -:10162000702100F0C3FE03C900F0A6F97B684FF0FB -:10163000000100221B68013D66F307110192013C85 -:101640008DF804508DF8071019688DF8054021F0C9 -:10165000010119600C49186801401960019919616C -:101660009A60DA601A6842F001021A60074A596803 -:101670000A405A6007B0F0BD801A0600BE67000835 -:10168000DB670008A0860100001F30FF008000F823 -:1016900010B5044654F8043F93E8030000F076F9CF -:1016A0002046BDE81040FFF73ABF43681B689869C1 -:1016B000C0F3C030704743681B681A6822F0D60236 -:1016C0001A605A6842F480425A60704703685A7937 -:1016D0000AB9012204E01A790AB9052200E00622BB -:1016E0001A70704743681B681A6842F0D20210B53E -:1016F0001A60026814791CB9196841F00401196074 -:10170000117801290CD15288012CC2F309020CBFB7 -:101710004FF08071002142F490320A435A6010BDAC -:101720000121BDE81040FFF7C7BE38B54368054644 -:101730001C6803681A78013A072A5BD8DFE802F0D0 -:10174000045A5A5A223B5A57A269910701D59A79ED -:10175000A2621A793AB9A26952060FD50522284623 -:1017600001211A7008E0012A08D1A269100605D5E6 -:101770000622284600211A70FFF79EFEA369D906AB -:101780003AD525E0A269520705D5DA681969501CD7 -:10179000D860636A8B54A3691B0603D528460021D1 -:1017A000FFF78AFEA369580626D528460121BDE821 -:1017B0003840FFF7A2BEA269910705D5DA6819691A -:1017C000501CD8608B5CA362A16911F0100109D094 -:1017D000E3692846022143F01003E361BDE8384085 -:1017E000FFF764BEA3691A06DCD52846D8E7A369CB -:1017F000DB0701D400F0E4FD38BD43681B689A693B -:10180000D20503D5DA6942F48072DA619A6951052A -:1018100003D5DA6942F48062DA619A69920503D5E8 -:10182000DA6942F40072DA610321FFF766BE000054 -:10183000094B70B51C680D4606462046FFF7C6FDED -:101840002A4631462046FFF707FE05462046FFF7A9 -:10185000D3FD284670BD00BF1C2C0008094B70B595 -:101860001C680D4606462046FFF7B0FD2A46314665 -:101870002046FFF711FE05462046FFF7BDFD28462E -:1018800070BD00BF1C2C000837B500F073F81B4872 -:1018900001210222FFF700FC194801210222FFF773 -:1018A000FBFB00F068F800200DF10701FFF7C0FF17 -:1018B0009DF80750012D1DD114200DF10701FFF7F0 -:1018C000B7FFA8B10F4C237813B1013323700FE099 -:1018D0009DF80710142021F0060141F002018DF857 -:1018E0000710FFF7BBFF18B10320257000F0E2FDE1 -:1018F000012000E0002003B030BD00BF18650008E3 -:1019000030650008FF89002001460148FFF7DEBB73 -:101910001865000870470000B0F1804F08B503D289 -:101920000D48302100F042FD0C4B98420ED903F5D2 -:10193000803398420CD9B0F1A04F0BD3084A094B21 -:10194000B0F1204F34BF1846104608BD064808BD08 -:10195000064808BD064808BDFC670008FFFF0040B8 -:10196000892A0008712A0008A12A0008B92A00085B -:10197000592A00087047704710B50C46FFF7CCFF96 -:10198000012103462046BDE81040184710B50C461B -:10199000FFF7C2FF002103462046BDE8104018476C -:1019A00038B5104C05464FF080512046FFF7E4FF54 -:1019B00045B154E8003F43F4807344E80032002A04 -:1019C00008D0F6E754E8003F23F4807344E800327F -:1019D000002AF7D103484FF08051BDE83840FFF7A7 -:1019E000D5BF00BF00700040024B5868C0F34000F4 -:1019F000704700BF00700040024A136843F00803BC -:101A00001360704700700040B0F1006F10B50446DD -:101A10000BD20C48FEF7EEFF2046FFF70AF80A4803 -:101A2000FFF700F84FF0FF3010BD084B002053F8CF -:101A3000042BA24202D81A68944202D301300B2828 -:101A4000F5D110BD1B680008246800083C6500083B -:101A50002DE9F8430446174699460E46C1B3FFF7F1 -:101A6000D3FF0546601E3044FFF7CEFF002D8046B1 -:101A700031DB00282FDB461B01361FB10020314629 -:101A80004A46B8472C4600F0D3FD44451EDCF320FF -:101A900000F0E4FD114B002133F8140000F016FEB5 -:101AA00009280AD00E48FEF7A5FF2046FEF7C1FF21 -:101AB000FEF730FF00F0CAFD0DE02FB1C5F10100C7 -:101AC00031464A462044B8470134DEE700F0BEFD07 -:101AD0000120BDE8F8830020BDE8F8836C650008AC -:101AE0003D6800082DE9F0410F461646804600F09B -:101AF0009FFD3E443C46F32000F0B0FDB44216D0BA -:101B0000C7EB0805254414F8011B284600F014FE15 -:101B10000928F3D00848FEF76DFF2846FEF789FF35 -:101B2000FEF7F8FE00F092FD0020BDE8F08100F025 -:101B30008DFD0120BDE8F0815568000808B545F22B -:101B4000555000F06BFE042000F06EFE40F6FF7072 -:101B500000F070FE002000F061FE4FF48050012183 -:101B6000BDE8084000F058BD08B500F071FEBDE8C2 -:101B7000084000F065BE08B57D2000F0E5FF0030AC -:101B800018BF012008BD4900FEF7E2BF2DE9F04370 -:101B9000012185B005460020FEF7DAFF042000F0A1 -:101BA00051FB01A8294600F0A7FB01A800F0B3FBF8 -:101BB00008B93B4814E00C353A48FEF733FF029968 -:101BC0002846FFF701F904463748FEF713FF204681 -:101BD000FEF72FFFFEF79EFE039B9C4204D0334886 -:101BE000FEF720FF012057E0029CDFF8E08030483C -:101BF000FEF718FF21462F4A002340460024FFF736 -:101C000027FF029F2C484FEA5709FEF70BFFBC4203 -:101C10001CD23E1B29486119B6F5803F28BF4FF4FE -:101C200080363246FFF7BCF908EB04002349324600 -:101C3000FFF758FF18B92248FEF7F4FE06E009EB5B -:101C400054003946FEF784FF3444E0E71D48FEF7B0 -:101C5000D1FE0298FEF7EDFE1B48FEF7CBFE1B49B6 -:101C6000029A002000F002FC04461948FEF7C2FE6A -:101C70000398FEF7DEFE1748FEF7BCFE2046FEF78F -:101C8000D8FEFEF747FE039B9C4204D01248FEF7A5 -:101C9000C9FE022000E0002005B0BDE8F08300BFCF -:101CA0006668000884680008A1680008B768000832 -:101CB000DA680008871B0008F1680008008A002025 -:101CC00008690008136900082169000800800008FD -:101CD0002A6900083D6900084369000810B5022020 -:101CE00000F0C8FAD8B3042000F0C4FA044670B17A -:101CF0001B48FEF797FE042000F0B0FA022000F027 -:101D0000ADFA4FF40040BDE8104000F0A7BA154806 -:101D1000FEF788FE082000F0A1FA102000F09EFADD -:101D2000202000F09BFA402000F098FA2046FFF7B0 -:101D30002DFF022807D1082000F084FA102000F0BF -:101D400081FA00F061FB042000F088FA022000F024 -:101D500085FA4FF40040BDE8104000F073BA10BDA2 -:101D60008E690008C469000810B52548FEF75AFEC0 -:101D70004FF48000FFF70AFF10B102283AD809E0BB -:101D8000202000F06BFA402000F068FA802000F07C -:101D900059FA2FE0082000F055FA102000F052FA0E -:101DA000202000F067FA20B91648FEF73BFE2020FD -:101DB00007E0402000F05EFA58B91348FEF732FE03 -:101DC000402000F03FFA4FF4007000F03BFA00F0C2 -:101DD0001BFB0E480024FEF725FE082000F03EFA0B -:101DE000102000F03BFA202000F038FA402000F0EC -:101DF00035FA00E00124042000F030FA204610BD3E -:101E0000DF690008F9690008326A00086B6A000897 -:101E100008B50248FEF706FE00F0FDFAA56A0008C4 -:101E20001EF0040F0CBFEFF30880EFF30980FFF7FB -:101E3000EFBF704708B50120FEF7F4FC08B9002099 -:101E40000FE00020FEF7EEFC0028F8D00220FEF79D -:101E5000E9FC0028F3D00320FEF7E4FC80F0010049 -:101E6000C0B200F0010008BD1FB504460E48FEF7E1 -:101E7000D9FD01A90C22204600F026FB01A8FEF79F -:101E8000D1FD2046FEF782FE0220FEF7CBFC044681 -:101E90000220FEF7C7FCA04201D000F0B5FA0A20EC -:101EA00000F008FBF4E700BF9B6A00087FB5BF485D -:101EB0004FF08051FFF760FDFFF796FD044630B10B -:101EC000FFF79AFDBA48FEF7ADFD00F0A4FA4FF017 -:101ED0008051B648FFF75AFDFEF74EFCFEF7CEFCE8 -:101EE000B448FEF79FFDB448FEF79CFDB348FEF7EB -:101EF00099FDB348FEF796FDB248FEF793FDB24850 -:101F0000FEF790FDB148FEF78DFDFFF7BDFC00F038 -:101F1000BBF900F0DBF921460C2201A800F05BFEC2 -:101F200000F0D0F901A90C2200F0CEFA01A8FEF7CA -:101F300079FDA748FEF776FDA548FEF773FD4FF43F -:101F4000804000F097F990B1A248FEF76BFD4FF486 -:101F5000804000F083F9082000F080F9102000F0A4 -:101F60007DF9202000F07AF9402000F077F9FEF7A3 -:101F70007BFFFEF765FCFFF787FCFEF79FFDFEF792 -:101F8000D7FDFEF7FFFB08B1934803E0FEF742FCE4 -:101F900010B19248FFF768FF00F084F9802000F04C -:101FA00069F918B1802000F059F94BE04FF4003086 -:101FB00000F060F920B14FF4003000F04FF943E039 -:101FC000FFF738FF68B141F288348548FEF72AFDF3 -:101FD000FFF730FF78B1012000F06CFA013CF7D137 -:101FE00032E0804B0CCB013301D0013203D17E486B -:101FF000FEF718FD28E0FFF7BEFD48B3FFF7BBFD75 -:1020000010B17A48FEF70EFD4FF4007000F032F97F -:1020100010B17748FEF706FD4FF4007000F01EF98E -:10202000102000F027F9002800F0F8807148FEF732 -:10203000F9FC082000F012F9102000F00FF9FFF76A -:1020400093FE98B96C48A5E76C48FEF7EBFCF6E701 -:102050004FF4007000F00EF90028CFD1082000F0F6 -:10206000FDF8102000F0FAF8FFF738FE4FF40050AA -:1020700000F000F904464FF4805000F0FBF84000F7 -:1020800040EA8400C4B24FF4006000F0F3F820434B -:10209000C0B20728C3B20DD14FF4006000F0DEF8E3 -:1020A0004FF4805000F0DAF84FF4005000F0D6F80A -:1020B00053486FE701334FF40060DBB2023B052B5E -:1020C00011D8DFE803F00C1003100C1000F0C6F874 -:1020D0004FF4805000F0C2F84FF4005003E000F0DD -:1020E000BDF84FF4805000F0ADF84648FEF79AFC7A -:1020F000FFF724FDFFF738FDFEF790FF394B42480C -:102100005D681E68FEF776FC2846FEF792FC3F48A5 -:10211000FEF770FC0022930001324FF0FF3103F113 -:102120006043082A03F56143C3F88010C3F88011A7 -:10213000F1D1374B4FF4801200241A634FF48062C0 -:102140005C639C631A645C6400F01AFC31480121F2 -:1021500000F0BEFC21462F4800F0BAFCF32001211C -:1021600000F0C2FC2146F32000F0BEFC0320012158 -:1021700000F0C6FC2146032000F0C2FC6FF4A05022 -:10218000012100F0C9FC21466FF4A05000F0C4FC0E -:102190002148012100F0CCFC1F48214600F0C8FC7A -:1021A00063B64FF0FF3EB546284700BF00700040C1 -:1021B000B06A0008BD6A0008C46A0008516B0008D4 -:1021C000E86B0008696C0008EA6C00086D6D000897 -:1021D000C36A0008EA6D0008014550FE024550FE42 -:1021E000176E000800800008526E0008656E000837 -:1021F0007D6E00089D6E0008034550FE116F0008BB -:10220000044550FE306F0008426F0008566F00080A -:10221000003802400010E022B379F764082000F093 -:1022200029F820B10548FEF7FDFB102003E0044823 -:10223000FEF7F8FB082000F005F815E7C56E00086A -:10224000EB6E000810B50446002000F0A9FC40EA3F -:1022500004010020BDE8104000F092BC10B5044617 -:10226000002000F09DFC20EA04010020BDE81040A1 -:1022700000F086BC10B50446002000F091FC20421E -:1022800014BF0120002010BD08B50120FFF788FB16 -:102290000120FFF7EFFF20B90121BDE8084000F061 -:1022A0006FBC08BD08B50648FEF7A4FB002000F08F -:1022B00077FCFEF7BEFBBDE80840FEF72BBB00BF76 -:1022C0005E6F0008022000F06BBC000008B5FFF74D -:1022D000F9FF0449884204D00220BDE8084000F01C -:1022E0004FBC08BD8BB8185808B50248FEF79AFBDA -:1022F00000F08AF86A6F00081FB504460C2201A896 -:10230000FEF74EFE01AB03CB206018686160A06051 -:10231000204604B010BD0068A0F10C03584258419B -:102320007047000080B51746064610480D461C460B -:10233000FEF760FB3846FEF75DFB0D48FEF75AFBE3 -:102340003046FEF757FB0B48FEF754FB2846FEF7D6 -:1023500070FB2CB10848FEF74DFB2046FEF74AFB08 -:102360000648FEF75FFBFFF7BFFF00BF876F00085F -:102370006A6D0008906F00086B6D0008C36A000862 -:102380001FB506AA52F8044B039200921A46234640 -:10239000FFF7C8FF0CB41FB506AA52F8043B03921E -:1023A0000092014AFFF7BEFF926F0008002307B5B5 -:1023B00072460093014BFFF7E3FF00BF996F0008DF -:1023C0007446064808B5FEF715FB2046FEF731FBBC -:1023D0000348FEF727FBFFF787FF00BFA16F000848 -:1023E000C36A0008BFF34F8F0549064BCA6802F461 -:1023F000E0621343CB60BFF34F8F00BFFDE700BF28 -:1024000000ED00E00400FA0508B5FEF7CDFB00F092 -:10241000AFFBFFF7E7FF08B5FFF7E4FFF0B50646AF -:10242000002401202546034694421DD011F804C023 -:1024300000F1010EBCF1000F03D17355774605463C -:102440000DE00133774606F800C0DBB2FF2B07D161 -:1024500002F1FF3C644506D07355871C7546012385 -:1024600001343846E0E770467355F0BD30B5C9B168 -:10247000C0430A44914213D011F8013B0A4D83EA4C -:10248000000404F00F0455F8244084EA101080EA98 -:10249000131303F00F0355F8233083EA1010E9E714 -:1024A000C04330BD084630BD8465000800010138D6 -:1024B000FDD1704710B504462CB14FF47A70013C41 -:1024C000FFF7F4FFF8E710BD0A2A10B504DC114845 -:1024D000BDE81040FEF7A6BA30230C461C2204F8D3 -:1024E000023B78234B700F2393400340D340092BCA -:1024F00001D8303302E00F2B02D85733DBB200E0B3 -:102500002023043A04F8013B131DECD100238B7205 -:1025100010BD00BFDB6F000808B5826843689342B6 -:1025200003D304481421FFF741FF0268591C41609E -:10253000D05C08BDF16F000843688268934210B513 -:1025400003D304481921FFF731FF02685C1C446083 -:10255000D15410BDF16F0008F0B587B00E460746A4 -:10256000154600210C2268461C4600F034FB002171 -:102570000C2203A80296009700F02DFB68460395F5 -:102580000594FFF7C9FF06466846FFF7C5FFB0424E -:10259000014601D003A808E06846FFF7BDFF0446E6 -:1025A000D8B1012804D103A83146FFF7C5FFEBE7F6 -:1025B000030608D504F07F046846FFF7ADFF40EA44 -:1025C000042000F18004A4B20025A542DCDA03A8AF -:1025D0000021FFF7B1FF0135F7E7049807B0F0BD20 -:1025E000FEE700000748084A0849121A08B500F03B -:1025F000E7FA0748074A0021121A00F0ECFAFFF741 -:1026000055FC00BF000000201400002004700008EA -:1026100014000020008A0120044B9A6809B110437D -:1026200001E022EA00009860704700BF002004E04B -:10263000044B1A69002A04DA034A5A6002F188320C -:102640005A607047003C024023016745024A136903 -:1026500043F0004313617047003C0240014BD860D7 -:10266000704700BF003C02400E4BDA68D20310D422 -:10267000DA68D1060FD4DA68D2050ED4DA6812F01F -:10268000E00F0CD1DB6813F0020F14BF0820092003 -:1026900070470120704706207047022070470720CE -:1026A000704700BF003C0240092307B58DF8073092 -:1026B000FFF7DAFF8DF807009DF80730012BF7D000 -:1026C0009DF8070003B05DF804FB000070B50646F6 -:1026D00041B1012908D002290CBF4FF400754FF415 -:1026E000407503E00D4601E04FF48075FFF7DCFF15 -:1026F00009281ED10F4C236923F4407323612169FB -:1027000029432161236923F0F8032361236943F0FE -:1027100002031E432661236943F480332361FFF7DC -:10272000C3FF236923F002032361236923F0F80325 -:10273000236170BD003C024070B505460E46FFF7B0 -:10274000B3FF092813D10A4C236923F44073236192 -:1027500023692361236943F0010323612E70BFF3D2 -:102760004F8FFFF7A1FF236923F00103236170BDA1 -:10277000003C0240F0B500220E680123934003EABA -:10278000060E9E452AD1550003230468AB40DB4367 -:102790001C4004600C79076804FA05FC013C4CEA13 -:1027A0000707012C076011D884684F791C40AF409F -:1027B0008460846827438760446824EA0E04446088 -:1027C0008C7947689440A4B23C434460C468234079 -:1027D000C360CB79C468AB402343C3600132102A85 -:1027E000CBD1F0BD0369194214BF0120002070470E -:1027F00002B909048161704701F00703C9089B0011 -:1028000000EB81000F219A40994010B5046A24EA38 -:1028100001010162016A1143016210BD014B1860A0 -:10282000704700BF00300040014B5860704700BF48 -:1028300000300040014B9860704700BF00300040FE -:102840004AF6AA22014B1A60704700BF00300040D0 -:102850004CF6CC42014B1A60704700BF003000407C -:102860000023036043608360C3600361436183614D -:102870007047002343608360C3600360036143616A -:102880008361C361036243628362C3627047000075 -:102890000E4903680A6810B544691C4383691C43E8 -:1028A00022F07F43426823F03003234343EA02636C -:1028B0000B60C36882684C681A43054B234013437E -:1028C000026943EA02434B6010BD00BF001000A044 -:1028D000FEF7E0FF90E80C000E491A4383681A43A4 -:1028E000C3681A4303691A4383691A43C3691A43C5 -:1028F000036A1A43436A1A43836A1A43C36A10B5C8 -:102900001A434C69044B23401343426943EA824310 -:102910004B6110BD001000A000008090044B1A68AD -:1029200010B142F0010201E022F001021A6070478A -:10293000001000A0024B9A68920600D4986170477C -:10294000001000A0024B9A68920600D418617047EC -:10295000001000A0014B1878704700BF201000A0A5 -:10296000034B9B68184214BF01200020704700BF32 -:10297000001000A0014BD860704700BF001000A0FD -:102980000F4B00211A6842F001021A6099601A6820 -:1029900022F0A85222F410221A600A4A5A600A4A07 -:1029A000C3F8842002F18062C3F888201A6822F4F8 -:1029B00080221A60D960C3F88C10C3F89010704759 -:1029C0000038024010300024003000201E4A936876 -:1029D00003F00C03042B10B503D0082B03D01B4BC2 -:1029E00019E01B4B17E05168536811F4800F5168D0 -:1029F00003F03F0318BF164AC1F3881108BF134AFA -:102A0000B2FBF3F3104A4B435268C2F30142013266 -:102A10005200B3FBF2F30C4A036093680D49C3F311 -:102A20000313CC5C0368E34043609468C4F38224DE -:102A30000C5D23FA04F484609268C2F342328A5C2B -:102A4000D340C36010BD00BF003802400024F40032 -:102A500040787D0101000020044B1A6B09B110433E -:102A600001E022EA00001863704700BF003802400E -:102A7000044B5A6B09B1104301E022EA000058638D -:102A8000704700BF00380240044B9A6B09B11043F5 -:102A900001E022EA00009863704700BF003802405E -:102AA000044B1A6C09B1104301E022EA00001864DB -:102AB000704700BF00380240044B5A6C09B1104304 -:102AC00001E022EA00005864704700BF003802406D -:102AD000044B1A6909B1104301E022EA00001861B1 -:102AE000704700BF00380240044B5A6909B11043D7 -:102AF00001E022EA00005861704700BF0038024040 -:102B0000044B9A6909B1104301E022EA0000986180 -:102B1000704700BF00380240044B1A6A09B11043E5 -:102B200001E022EA00001862704700BF003802404E -:102B3000044B5A6A09B1104301E022EA00005862CE -:102B4000704700BF003802404209084B012A01D1FA -:102B50001B6804E0022A01D11B6F00E05B6F00F0EC -:102B60001F0023FA00F000F0010070470038024017 -:102B7000024A536F43F08073536770470038024036 -:102B800082B000230193054B0193019B03EB80006E -:102B90000190019B196002B0704700BF50280040AF -:102BA00082B000230193054B0193019B03EB80004E -:102BB0000190019B186802B0704700BF5028004088 -:102BC000431E0A4410B5914204D011F8014B03F89A -:102BD000014FF8E710BD02440346934202D003F8C8 -:102BE000011BFAE77047000014000020282C0008A1 -:102BF00000140240004000000E00040000140240D7 -:102C0000008000000F000400000000000000000031 -:102C10000000000000000000C4650008202C00082F -:102C2000E82B000850000000006000400000000198 -:102C3000801A0600005F60003C2C00080054014030 -:102C400000002000000002402000000005000800F5 -:102C50000000024080000000070008000000024061 -:102C6000100000000000000000000240080000000A -:102C700001000000000402400400000000040240C3 -:102C800001000000000000000018024008000000E1 -:102C900000000000001802401000000001000000C9 -:102CA000001802402000000001000000001802404F -:102CB0004000000001000000000C02400001000084 -:102CC00008000700000C0240000200000900070095 -:102CD00001424F4F544C4F41444552000000000008 -:102CE000002A2A0000000000000000000047FF004A -:102CF0004C6174746963650069434563756265324C -:102D000020323031362E30322E32373831300050CA -:102D10006172743A206943453430554C314B2D4330 -:102D20004D33364100446174653A204A756C203257 -:102D30003520323031362030393A35363A3339475A -:102D400002FF7EAA997E5100010592002062016F68 -:102D500082470272007011000101470B1047028088 -:102D6000470502470580034708014702084711202D -:102D7000470C02470B0247174047050147054047EC -:102D80000C040090470F10470640470501470540D7 -:102D900047050147048000044710804705020020D2 -:102DA000000800804702014709084717014704341B -:102DB000000C470403470D30475970470E801047F9 -:102DC000040200404704080147020702004047048C -:102DD000080147052004474103470B03470D1C47E3 -:102DE0001080470901008047020347090847032470 -:102DF000472B7847124007470A40470C0400084712 -:102E00000E404705014706044704014706044706EC -:102E1000104711800747061C470380470C08470AE4 -:102E20000C3020470F30E8470C63C1470C0C30478B -:102E30000F400105470C03C0470C0C3020470D180C -:102E40004702E04704020CB100180038470640472B -:102E5000080C30471001074702010001CD724703FB -:102E600098470F0C3020470D3000F9A0470810009C -:102E700001C04705038047070C30470E300071053D -:102E80004708300001470F0C3020470F580847040F -:102E900001DCA00080470363C1470203C020AF529A -:102EA00047050C3047100803470402E0F5470503C7 -:102EB000C0470202E0050FA01047040C3020471263 -:102EC00040002020470AC001E007A547060C304714 -:102ED000110D470314470B404703054703144702EE -:102EE0000C3020470F900004470220470E17CFA256 -:102EF00047020847020C30471030092447022C478C -:102F00000D02CD0FF047050C3020470F108004470D -:102F100003684703010547100C304710110730477D -:102F2000190C3020470D300010470A0147100C30B3 -:102F3000470E304702052047023447050247100C70 -:102F400030204707044706404704203404ED800042 -:102F50000247091C000847050C30470F404705048D -:102F600022FC4702014707A00002C047060C3020A0 -:102F7000470903EAF52000404706035CA50018470F -:102F800008401C8F0847050C30470A03D050470201 -:102F900040470601F0554703104706200200F0475E -:102FA000050C30204709284703110001E0470320A2 -:102FB00038FC8002470A1E8F0847050C3047080876 -:102FC000004047033029470402BC03D0470AC047EA -:102FD00003F047050C3020470A1C0F10470829DC76 -:102FE000A5003047090E8F0847050C30470C0F47E6 -:102FF00009415055002047090200F047050C3020D8 -:10300000470D0447065C76EC800247070847020636 -:103010008047060C30470802470504470301470371 -:103020003CF0470A01400C00F047050C30204708EF -:1030300020470B1C21FAE732004047100C304709AB -:103040000C470B2464F0F7084709028047070C304F -:103050002047141C31CCE720804703383D44470209 -:10306000028047070C30470F404705027668FB4750 -:10307000024000C0403D4703028047070C30204714 -:10308000080C47050147053C63DE800010470C042F -:1030900047040C30470A2047040247052402E8474A -:1030A0000210470601014047020147050C3020008D -:1030B000A100123C20000447021EF06047072000D8 -:1030C0001EF04047030D72BD400003428EEF4706DD -:1030D0000C30470503C0C04704F047024000974743 -:1030E00003042000F0470401403C470201429C4792 -:1030F000070C302047033A3C200647030CF06018C9 -:1031000002108000060140024AA5001880000778DE -:1031100015006047020CC96047050C30470503C025 -:10312000104704F0470205090747038001D05547BF -:10313000039000401620000142C1096047050C3091 -:10314000204703323C202C47031CF04030470280CC -:103150000004470220470501883941470202002543 -:10316000ACC02047040C30470503C0044704F001FD -:1031700030080109000C4702404705F00F42820168 -:103180000020402547070C30204703323C204702EF -:1031900080001EF040470280E847037C470705781F -:1031A0001547044488002047040C3047040803C82E -:1031B0000001470210F0470371094703964708C012 -:1031C0001640000342150020470202001C0C30206C -:1031D0000001017A3C2047040EF0600008704703AC -:1031E000A00601DAA047090A007E8800300002C06C -:1031F000000C3047038D0003C04000243000F0472E -:103200000308FB470240000340F5470901428700DD -:103210002047040C0C30202000013A3C20201004F0 -:1032200047070804001000045AA102004000A0470C -:1032300005603EEFA547050C300010070D0803C0E0 -:1032400047020247070520470334E001470B1C05EE -:10325000A047020247020C3020040500723C200007 -:103260001047060450800447022021EEE20200408D -:103270001810470403601FAAF5470202C0000C3073 -:10328000000405470203C0001004470542D8BD2CC6 -:1032900000103C767042470240008047030102C0A4 -:1032A0002C00F047050C3020000800723C2000A0E4 -:1032B00020784703300010470501DCA302304702A5 -:1032C000804703C001C047080C3000040B470203CD -:1032D000C00052144704300008033C470234383021 -:1032E000A30020400147048101C2C0470C0A723C80 -:1032F000204702A03BF9014001404705202C0CF07B -:10330000404703078402470306DEF5E0470A10083A -:1033100003C10001547D69A70040404704020460D6 -:1033200000F047028050003C470402BCF1D0104737 -:103330000601804702723C20060024017F74400190 -:103340004047030647034EA50018470215A3C28055 -:103350006002002C91A0470B0803C047023C2E7866 -:10336000A01002404707D055470490003C47020197 -:10337000401DF3F047070347031215400400366869 -:103380007B64403047040CA047020DD10001470286 -:103390000723C24702034614ABC420470A0815405E -:1033A00004023C004A58007047040C470203DFE265 -:1033B0004705803C47020266C20BC208470A03BCAD -:1033C0001047023C294B7440002847042000281E67 -:1033D000F04000021801A3C20004016060AB470C7A -:1033E000043C4703164268A0004047074010F047DE -:1033F00002050800803C47021403B4C70010470AC6 -:1034000003BC100C4702035D7A014706A00E60FE64 -:10341000A047023001A7A3C2000803469E81A02056 -:10342000470A043C0004470202C85A0847071E0323 -:10343000C0F54705803C470201601D03F0470C2999 -:103440005047021E01D95B10404705800423CCA1E0 -:103450000247031123C247041D95E0470B901680D5 -:1034600047021C314CA547080202C00247048000F5 -:103470003C47043EE1D0470A1B47051F470601C0F1 -:1034800047030620E8A3024703B325ED00040166C5 -:1034900040ABC42047090A470502470601470526F5 -:1034A00070034704AFB694010A0242B507C004474F -:1034B0001064F850104708701AA302B10001D047F9 -:1034C00003C00003DDB5E0471164005047083443F2 -:1034D000D05300228051A0470380D6C76EE1D04769 -:1034E00009081E5BC8104703040AF0404709FE00A4 -:1034F000404702198F304002000166A5E0470A0BE1 -:1035000000121E806000142800F0470240000747A8 -:103510000403F0A04080020007B0470301C24E94AC -:1035200047080180470506470308F0401847021086 -:1035300000062000020C0A001947020B3A81816044 -:1035400002E68405A020470801470810F04704B7A9 -:10355000470240043CC00500028010874001010082 -:1035600002601402400447060300080036C9580CE4 -:103570000034001AF04030470218000CA14000FE51 -:10358000504A0247020120288047030480D0A04708 -:10359000060100030A7A05424702144702F0003090 -:1035A0004702FB0004408003F0804047044028802D -:1035B0000003400100D080470603000500DE11D85B -:1035C0004702366C1AF060470308000D8000020CB9 -:1035D0000A403008000B3801010002000D85E02090 -:1035E000470601000F0052E0404702142800F04750 -:1035F00003B0070009024702C00548200400074144 -:1036000041044702401602E0470A08F680D2100E35 -:1036100047021AF0404703184703200400A5400062 -:1036200080501175D04703C067A0140447080B09E8 -:103630001B28C020470310F04704C900400E3C3847 -:10364000C05A480100D887B5F8470303AEE80080A8 -:10365000470808019F141447041AF04047028018D5 -:103660000400201E2200A54130005C037B080847AF -:1036700002E021CA470A050C56788240470310F041 -:103680004703F0E53C470340C05A40104702A7C338 -:103690000C04000262DC05470A0D81D6C9504705BB -:1036A000A540470298984702A004301C05408000BE -:1036B00018E04705600685014709050A32054A47B3 -:1036C00004405A470399933400400201C00A40471E -:1036D0000210B047030847020105470A0581FE5161 -:1036E000D047040AF0403000901000040004027E2D -:1036F0005F40100050E1B8280400E0400FF8124786 -:103700000A0B53E04A4705F000300070FD3C04000E -:103710000220E00F48100079098168040002600C63 -:10372000C4471114004EA0400240470607C850C0CD -:103730004703904705407EC70008470F020010A0CE -:1037400040804047063BC00542470210D047052352 -:103750005DAF01470E06470744470306470203C80B -:1037600050C0180018033E01006000063DC047111C -:103770000447054247050402C005404702188FBCB4 -:10378000012047030E824716100001C000044702C3 -:10379000674850C0100001E13010470360471830FF -:1037A0000001470204470223E005401047020DE0F4 -:1037B00047160C47070547030C0006674850C0F042 -:1037C000001017B2012847020780471008470B086E -:1037D000470222E00540204702E7C001472340039B -:1037E000E850C14703F0470402662F90471B10477B -:1037F0000203C005404703D0470401442FB04047AF -:103800001D736850C010470219F56C47020366EE3D -:10381000D0471E42C00540304702ADE11C470201BF -:1038200042AEF080470240470E0447090226036873 -:1038300050C00007101DFB08084702068DE8002055 -:10384000471038470A0403E0054040003085830CE8 -:10385000470508470F04470B040020736850C11048 -:103860004702804703C00060208E5420470D044764 -:103870000B04000442E00540100051E04703804182 -:1038800042650DA0470372009011010101470B1022 -:103890004706404705014705400002470608471113 -:1038A00020470680470502471208472201000247C9 -:1038B00004044711104706404705014705404703E8 -:1038C00016000147060447220200200008470208AC -:1038D00047031047243C000C4706304768801047D8 -:1038E000040200404704080147030200404704085F -:1038F000014704302004474D0147060447210300D7 -:103900008047050247030C471F1E472D024703400F -:1039100047050147174047050147060447030301D0 -:10392000470604470610471D8047050247110C3023 -:10393000472C0C3020472B0C30472C0C3020472BC9 -:103940000C30470E01470C1047100C3020470D0279 -:10395000471D0C30472C0C3020472B0C30472C0CCB -:103960003020472B0C30472C0C3020472B0C304795 -:103970002C0C3020472B0C30472C0C3020472B0CC4 -:1039800030471308000400205547088000424708CC -:103990000C302047120C00200168554047074002B8 -:1039A000004047070C3047150400200F470B020565 -:1039B000500447040C30204714260168F0C0470922 -:1039C000661E855447050C3047180F470DF0804799 -:1039D000040C302047150168F0C0470A168F0C47C9 -:1039E000050C3047150C47020F470DF08047040CBB -:1039F0003020471501E8F0C0470A1E8F0C47050C20 -:103A00003047150C20200F470B0200F047050C3003 -:103A1000204714266548F0C04709601C8F0C4705F5 -:103A20000C30471620200F470A460200F047050CCD -:103A3000302047156548F0C047080207548F0C0036 -:103A40000147030C3047180F470DF047050C302095 -:103A5000471206470201C8F0C047076047021C8FA3 -:103A60000C47050C3047170C0F470503C0124702DF -:103A70000401B0F047050C3020471501DAF0C047CB -:103A80000423C0470307148F0C47050C304718F078 -:103A90004703D9B0A4470280470A0C3020471406D8 -:103AA0000008F0470350A780020040470A0C304747 -:103AB0001620300F4703D900A047040247080C30F6 -:103AC00020471505780F80470250A7800247034022 -:103AD0004047070C30471524201B0F470528470D8A -:103AE0000C3020471501F80F804703078002470C70 -:103AF0000C3047170D0F4704B020470D0C302047FE -:103B0000140661790F804703078402470C0C304785 -:103B10001504200D0F47035000A8470D0C30204717 -:103B2000142004F80F804703070002470C0C3047AD -:103B30001506203C0F47035000B4470440050A080F -:103B40008047040C302047143501C90F804703011A -:103B500000024702026000E50447050C30471536B5 -:103B60006C1C0F47035000284707588047040C304F -:103B70002047141428E80F80470307040200604719 -:103B80000214E05447050C3047166D4CA54703500E -:103B900000204704C0040A088047040C3020471561 -:103BA00029415A8047038700024702024702A504C1 -:103BB000471B10044000F0470301A9A00004800047 -:103BC00042471F207808F047020800BFA02140406C -:103BD000020040471D10042060A54702404702A094 -:103BE0004702010040020550471C2007605A8047E9 -:103BF00003078002470202601E8556471D0340A549 -:103C000047035190B04702014704F0471D01605A35 -:103C1000804702D9A3840247020800168F0E470E80 -:103C200001470D1C004FA54704A0703C4706F04714 -:103C30000E01470D2601E15A80470219B5F43E47AF -:103C4000041E8F0E471C1C20390F47031900B44770 -:103C500004028200F1471C2605580F804702F9E351 -:103C60008002470360148F0C471C1C3C2F0F470336 -:103C7000100034470320020200F1471C2061DB0FD3 -:103C800080470280030002470306548F0C471C3C08 -:103C9000381C0F4702180090F03C4703028000F1E7 -:103CA000471D04C80F8018003DA5E03E0060470294 -:103CB0001C8F0C471C3C380C0F47031000283C4756 -:103CC000040190F1471D25CA0F8047029005643E0C -:103CD00047041DBF0C470E04470222CF4A60470528 -:103CE0000400042047031000188F62E100410042E5 -:103CF0001C05A0470E04000467FB40404705040074 -:103D0000360447031000981D6CF1024002002C4756 -:103D1000021001470604470208284702100025F058 -:103D20003C6000424704100232DAC3400040018583 -:103D300041410005000381005004470A16A80447CA -:103D40000206067A7C424047060402DFD34800049C -:103D500059F0728147041EE0504710023FE8F247D5 -:103D600007801C7AE0A5470220080F3001400001BF -:103D7000C25C05A00401470D100E62DFFA47071070 -:103D80002026FC500100C018E13A01000147026EF4 -:103D90004713214FF54707014221D90740007D0015 -:103DA0000F2811000101C026020414471006EBF68B -:103DB0004707011C05FD036100021987F69D0002FB -:103DC00047021F800420470809470408000628E02E -:103DD000182040470408470202DFE3402000D80BC8 -:103DE000656100940142B501470A0847040C002EA2 -:103DF00007FA040247050C00042BCCC36070009145 -:103E000087BA8900CA020021C20020470F3C001077 -:103E1000A0470208470601F0D3410147029FF9512C -:103E200000040002B680404711014EA0470902E895 -:103E3000C342020070153EC1470306D5A0A047103B -:103E40000642D0B9404707042830A047031805733D -:103E50002147036037A1470F06000E3BDCB16247E4 -:103E600005064703DAA00018007C0FBB09006001BB -:103E7000E035D047020A470810470602CFE0470D59 -:103E80000400190F21194702014247040447080E94 -:103E900047040E47021BD0470930470304059981A8 -:103EA000720D470202470B03000980920447047F0A -:103EB00060C2470604014203400500100801804724 -:103EC00003400340470A03000F80174705386CC3BF -:103ED00002470504009E000E0A20100058F0470318 -:103EE0004047101814470403F00347081E3C30A055 -:103EF00008470290E77F21470306EED4A447090153 -:103F000000DE8047030402DA430247073E056EA045 -:103F10004703B819FF4147024002B689144709090F -:103F200008941047034040F0024707802C3F5ECBC7 -:103F3000470308A9836101470202020F470A0801EB -:103F4000F7470280470260C80A0247060E20060CA7 -:103F5000C7104702781BEB0108084702468F084745 -:103F6000094B00164047020146387B020100024718 -:103F7000053C3C100A47033009807447020363C0C4 -:103F80000F470A200012204703BC7AF947030D4768 -:103F9000052005DA0A200247020772B4080009402A -:103FA0005E8F081047080B0D941A4247033C00A08F -:103FB00008470508001C294B08002020D005414770 -:103FC00002800002C20F470A080B06A5904703694A -:103FD000CAA02047050C0020020C042032801809DA -:103FE000200008C047025C8F084709200E10000817 -:103FF000470324300401470504470220CCA1414770 -:104000000218B0C0284704020F470C179000800028 -:104010001429EA040247050447020378D1408000CE -:10402000D005782847041C8F0847090700D04703AC -:10403000401472D947030147050401CA5B470311C5 -:104040000567B840470242800F47080180000A76A2 -:104050000080808000767B802000024703060E14DB -:10406000000DA708180159C573BC006047024C8FAA -:10407000084709050EDF00804703237B470A26CE49 -:10408000FF094000D9E3384147072047080518DAFF -:1040900047053CE980200038470603DBF747033833 -:1040A000A1380147040C00080040470235005013B6 -:1040B00047030403C04702266D70400840470404CC -:1040C00000047C7005470308ABA39C204702030152 -:1040D0000F040040014020E0A00147030683D047C1 -:1040E000022428DA5012470504002423EC470280FA -:1040F0000071B17EBD40404702418F044703467EB8 -:104100000D344702014706046410A0470702003639 -:10411000DAF0484702500743000147040F04470202 -:1041200001639CEC342000098047052065DEA04730 -:104130000901FFF4404000D9A720000A4702070008 -:10414000CF04470202403401A0470802266650010E -:1041500047094370A04703500FA400204702040101 -:104160000F0447042FE00220470826760E0222475C -:10417000070621EC4704980FF420470303808F06BD -:1041800047020140010A4706154001800C62CC47F6 -:1041900005C047043D40E740C10050A70300204749 -:1041A0000202410F044702020284AA47055A155031 -:1041B00001002026FE0847041000804702077CD338 -:1041C0004A020081D772000C0801E001CF04008090 -:1041D0000042470501800803C047020C41DD20472B -:1041E0000608002E39D00A47030800C028204702DD -:1041F00020010F0400C002004047040D98523C30DB -:104200000240002ACF47070C0006036C4704300722 -:104210003028010001C000EF044703029E47050853 -:104220000803C047021401E024001047061C207058 -:1042300000414703A0C14702404702010F04470461 -:1042400037AA0104470219523C30470302DA100230 -:104250003002470604EE8063470315310028C0474B -:104260000201EF0447034035040010470403C04730 -:104270000303F0470340470506034A1047030803BA -:10428000240020470202400F0400604703880121F8 -:10429000804702723C30470368EE402200404703EB -:1042A000060006021E2000180031EF320021604790 -:1042B0000201AF0410470223CDE0470603C14704C3 -:1042C000EF47034047051401E04705A5FBE047031E -:1042D00006010F0447036004C0804704723C304766 -:1042E00003027D804702404706036A0A4703019B99 -:1042F0002FF483470203808F048047060100010EDC -:104300000803C0044702404BF1493000080F28045D -:104310004710140500044706030008187A3C3004CF -:1043200047023BCDF040700010470204470610479B -:104330000280470503A18A4704028047060803C498 -:1043400047050A40800070FD24470280470D028027 -:1043500047050280470401805A3C3040000600FABD -:104360000A40470380470201470D0142C04704014C -:10437000404704038F470203C010470238ED0BC1CA -:104380000002008D28470A010141840001C02782F4 -:104390004704068047065A3C30400006027A054929 -:1043A0000005470204000E47092A81A047031F9316 -:1043B00080470950470203C010002401505F404766 -:1043C00002700300404702304703044706400002E2 -:1043D000800F0008470820007A3C30000E00637A06 -:1043E000A5404702804705044705184704C0006000 -:1043F000008F00044709180803C05800042C70E31C -:10440000404702909D0008470E01C00300F0470995 -:10441000018B523C300C002C03FCC34200380008D6 -:10442000000C470E020014E0F847043FC0470408A0 -:104430000803C050470201700500300031F1000C44 -:10444000000440470B40022ECA5047041DD080474D -:104450000319523C3020470202DA470230008090B4 -:1044600000040020704705784706065415A8470841 -:1044700003470303C04702042C6A8140000200077F -:1044800047030447052800F3C2BF800003D280C061 -:10449000F0470701804702723C3006010C026F5062 -:1044A000401801470306470618180007E29547031E -:1044B000400EE0F8470950000AC0470401DA3C47C3 -:1044C000024010CB470402C005444280084706045E -:1044D00002470222470820007840204703016D343C -:1044E0004704184704014850400100704706071C64 -:1044F00000084703720070110201014708024706D5 -:1045000008470402470A04470220470680470C0177 -:10451000470604470401470802C047031047170233 -:10452000470608470B084716024706084704024794 -:104530000608470620470680471F03000700044778 -:104540000B7C471D03470F6C470CA8470502472704 -:104550002047080447220240C047040801470302DD -:104560000040470408014705200447058010474CD8 -:1045700003470D70471F014703044709D047034015 -:10458000473A02471208470D80470A800002470356 -:1045900002470580470502470608470620472204D0 -:1045A000470D401030C04718067B38470240626D07 -:1045B0006140470383DC05470430C0471803DBD064 -:1045C000470301AB2022C0470203C000804702100E -:1045D00030C047160103F8EC00B0018047020162C9 -:1045E00000060083D6000A00180030C0471703F801 -:1045F0003001F0470903C20D1047021030C04716C2 -:104600000108805A00B01400AA49A1604003008349 -:10461000C447030C0030C0471702002D018008007A -:10462000064B6C0280030003C247041030C0470AE7 -:1046300080470D14EC50104702A2EDF5404703836C -:10464000CC054702800030C047193AD8F04703A193 -:10465000E420470303C2008B01001030C04716015D -:104660000A805F804708030083C60847032030C0E4 -:104670004717210028104708020003C00547031010 -:1046800030C0471002470980470502470483C647E8 -:10469000025C470230C0471A10470503C0470303B6 -:1046A000C2470280001030C0471708096947074019 -:1046B000470383D60581470330C04718215D47076C -:1046C00020470303C0000A47021030C04717088084 -:1046D0005F18104707100083DC4703800030C04795 -:1046E0000901470E227A00804707600003C2470293 -:1046F00001001030C0471704000A1847024010F1AB -:10470000296047060A470330C04718015047030491 -:104710000F00014047091030C04717043C585010A3 -:10472000018010F16A06804704014702180030C07A -:1047300047183C10508047020F0041404709103095 -:10474000C04717043C5878A0470210F121404703A6 -:10475000A906F38000480030C047183C10584703B2 -:104760000F004002800020193FE547028C1030C046 -:104770004717043C5C470410F121604047028BACB2 -:10478000E00A470330C047183C1000C047020F0042 -:10479000400280002002A7B50A47021030C0471728 -:1047A000043C5C470410F160470490B7884704302C -:1047B000C047183C1047040F47050222EFF5470297 -:1047C000201030C0471708024B9980470210F16053 -:1047D00047048BAEF81B470330C0470901470D0261 -:1047E00010DA1047030F004047030202A5A50A474D -:1047F000021030C0471708025A470410F1684703F7 -:1048000020A907D19A020C0030C047170122EB47BC -:104810000220000F47048000193EA58002081030D6 -:10482000C0471708826B470410F1284704A93CE0F1 -:104830000A470330C04718115D47040F0040470284 -:1048400080022A26800002001030C04717183DC0A1 -:104850004704169947058001B50A08470230C0474A -:1048600012028C000800083C20470303096B47052F -:104870003FC1ED9047021030C04709056047081C52 -:104880004702604705018010F1624702060083DE9F -:104890004703180030C047094047080280304706E8 -:1048A0008000010F0041C0470303C047041030C01F -:1048B00047130C7C00304703100080800AA1384762 -:1048C000020300A9408047020C0030C0471202C218 -:1048D0007800304703084702C00AA0404704294037 -:1048E0004703081030DE4710084702404709C06000 -:1048F000F180470483C4470530C2470F0114000309 -:10490000470A4000F0800008470203C20D0B47022F -:104910001030CE47090847095C470A2A500447036C -:104920000183C4470240470230C2470908470620B6 -:1049300002024702804706200045A034470280005B -:1049400003C20947031030C00700A0470F804E780C -:10495000470720002A50470902470230C04709088C -:104960004708B24278470905A03C40284708103064 -:10497000C04712800E470C024702808083D647054D -:1049800030C047090847062000F24000084704D01D -:1049900000204703024000800003C04702020010CD -:1049A00030C0074703020F78044702204708540528 -:1049B0000030470680470A1C000C0030C047040244 -:1049C0000F080247082000B3823A8020470680473C -:1049D0000580470410A20447071041F70028001083 -:1049E000470380A0470508000803790004470959D8 -:1049F00081A50A40470ADFB4404705714706080011 -:104A0000608369D0B04709198D0D9047090224834E -:104A100080280044090AF900A00180470308000823 -:104A20003DC080E0018020F5470306005A64A0479E -:104A30000A02E7B3C0400010222FDA11B047050880 -:104A400047023C2071B04702A0F080038047021A61 -:104A500074470B02AA77144703014148504702806C -:104A60004704304704A010002A57A86047035A445F -:104A7000850A470902AA0C022C4702014000D8B05F -:104A800000804704204703F147030F50F5C0470358 -:104A90001A640090470A1892D428000101406A5015 -:104AA000104707083DC030470320F5806047035A90 -:104AB0004488470B341EB46C470223401BD8F04790 -:104AC0000608203C0030470320F08047041A6501A7 -:104AD0000B470940053EC04704046B8047020800AD -:104AE00020470814470302000347020CC5A8000D25 -:104AF000404707080807C047040358104702020050 -:104B00003C4708084704404703332CBB0E0080474E -:104B1000080112862800014294DB4703C0470F08B2 -:104B2000470780470A1E8040470202003B00900072 -:104B300080471028470660470850A55EE0600002E5 -:104B400008BBDA80E0470701E94E5180470610476D -:104B5000023C1C07470B85068447042A6EF0470673 -:104B6000744702AA78F847072847023C02078047A3 -:104B700009025A005470007047038047070108F487 -:104B80006A470420F18247020240029DE747028003 -:104B9000470702A503424000304703F000024707E1 -:104BA000980B470420F001C0470280028300800177 -:104BB0004708030F52A047030C3E6A47090294DEE0 -:104BC00018470A5414850A470AF0983C470303C360 -:104BD00060470901541FD0470990144702904709C4 -:104BE000030F13804702600C3C4E300001804706E3 -:104BF00014C8470201804703E00846005A45E047D1 -:104C000002184708F0470503C0470A02BC1CD047FA -:104C100006220020001A7400084709030F13A60497 -:104C200047020C3C4A9810470505000294DD51E00C -:104C300000100AF184470443C48047020C4708F07F -:104C4000043C20470203C0101080470502000154B5 -:104C50000909470305F0A047043C010D0B000C4770 -:104C600007030F12806030000C3C4AB180C8470730 -:104C700015CA5004470220F580038003005A65E0FE -:104C800047020C4708F004470403C0101000144703 -:104C90000602BC1C084704F0470302001A54000B2C -:104CA00000044707030F178047030C3C5E47028050 -:104CB00047060294CDF9F0470903C4470CF04705B5 -:104CC00003C0470A01541F70D0470603470203C0C0 -:104CD0004708204702030F168047030C3C5A000187 -:104CE000C8470714FAB8E047080183DE47021C47AB -:104CF00009F0470503C0470314470602BC3CB890BF -:104D0000470983C0470B02AA17A06047020C3C5A10 -:104D1000018047060101E8FF71C04705078047048D -:104D2000010010804708AA0404470303C010470A83 -:104D3000542C08470604470680470C200400200A2C -:104D4000A85E181400C047040108F54C704704A77A -:104D50006000044705080008470A0420001002A864 -:104D600010008A0040470404006419F8F0000100B4 -:104D7000A0470A08470E083D6000044705200008C8 -:104D80003DC047048005E000380004C3FCA0471183 -:104D9000483C00F04708083C47062BA342D00004DB -:104DA0003C3E470B020F78470360083D601000014E -:104DB00080470A05804019B006000600C3C4E047DA -:104DC00002184707020F084704083C00F8470B7019 -:104DD0000008470201EB404702243C471230083CE0 -:104DE000E070470280470440083DE03047028028D9 -:104DF00045E04704C3C4A981470F22083C200800AE -:104E000014C04705083C204703C0242BA002C0471C -:104E1000023C0101084709020F304704083CE00149 -:104E2000F04707083C604703204009B047023000C4 -:104E3000C3C4A9984709020F4705083C2001E04771 -:104E400007083C20470521EB4047033C0101470D83 -:104E500020044702183C6000800847040547090504 -:104E6000E2E0284702C3C5E0470D3C384702083C52 -:104E700020014705024705B04703808BA060470328 -:104E80003C4713083C40F84708083CC047042A0147 -:104E9000A440470201C3C5A04711083C20F04708C1 -:104EA000083C20B047032050A04703043C471202AF -:104EB000083CC047070100083DE0470501B14047F5 -:104EC00003C3C5A0184710083C470A083C4702C066 -:104ED00047033B7C2047033C0103470D0E010020A4 -:104EE000083C604703804705083CE0470384000115 -:104EF0002447020200AA85A101000C470B02800092 -:104F0000083C2000044707083C2010E1470202004B -:104F1000B447042A8100080004470272009011037C -:104F200001014708024706084704024708078004B2 -:104F3000470220470680470C014706044704014703 -:104F40000D10472A0847160247060847040247067D -:104F500008470620470680471F02470304472903E6 -:104F60004729A0472D10471E02004047040801476B -:104F70000302004047040801078047032004470557 -:104F80008010474501470603470D20471802470692 -:104F9000034703044709C0470340472B384721080C -:104FA000470B400080470A804705024705804705B8 -:104FB00002470518084706202C471101470F0447F0 -:104FC0000D400030C0472B1030C0472C30C0472B5D -:104FD0001030C0472C30C047100447180100103073 -:104FE000C0470F80471C30C0472B1030C0472C30C3 -:104FF000C0472B1030C0470E10471D30C0470A0273 -:1050000000140B471D1030C0470C28E1471E30C06C -:10501000471B04470F1030C0471B0A471030C047DA -:105020000B20280B471D1030C0470C14E1471E30E1 -:10503000C0472B1030C0472C30C04725330690475F -:10504000031030C047242031A480470430C0472BD0 -:105050001030C0472C30C047253C0247041030C0F8 -:10506000472304803C14470530C04708804721800F -:105070001030C0472901400030C0472701470310C6 -:1050800030C0472C30C0470A80471D0A4702103005 -:10509000C0472801470330C0471D0300F0470705FC -:1050A00047031030C0471FF147070D9A470330C030 -:1050B000471702A809704705404706010800041079 -:1050C00030C0471180470501544147041000796002 -:1050D0004705080100080030C047150447023C4757 -:1050E000060B404703802A81000847021030C04762 -:1050F00015020007C05847041008684704AA85E154 -:1051000099470330C047183C1047050301428047C8 -:10511000023C47051030C0471707C04A4704100833 -:105120006A06C04702C3C5A0470430C0470F044702 -:10513000053000025A40470503014047033C47053C -:105140001030C0470F0A4707065848470410086B3D -:1051500066804702C3C5E00080470230C0470FF0B9 -:1051600047083C100847040B41428047023C014776 -:10517000041030C0470F10470707C05A38470310C4 -:10518000002A06C04702C3C4E018470330C04711D5 -:10519000C04705025A500147040B41428047023C78 -:1051A000010147031030C0470E50470280470506F3 -:1051B000585E71C0470210002B66C04702C3C4AFDF -:1051C00000A0470230C04717025A50470503470462 -:1051D000013C47051030C04710018047050658487C -:1051E00070E04702100028470301C3C4E0470218DB -:1051F0000030C0470B083C470901000140984704B4 -:105200000100034047023C3647041030C0470B08FA -:105210003D60470904470238E0470210002800407B -:105220004702C3C4E7470E3C47021010D9470D049C -:10523000470506C04702080247112000034B470DEF -:1052400002470502800010081CE0470D02000847D5 -:10525000040200204703028047041047041BB842A1 -:10526000C047021080900902470B1C470C01470AF7 -:10527000073E47031014979598471301E04707012D -:105280006BCA999047093016B500024713F04707DB -:105290000160699990470506804702103488470FDE -:1052A0000847040100204704040002A8100847032F -:1052B0000A008364471580470401E04705020202A3 -:1052C000A85C1847030A037942804718D9F04707BA -:1052D00002003C1000E04704704704069E901B4704 -:1052E000110B4B80E0470707C05C00100004080763 -:1052F000B0470301A94F800A470E053203D42008A6 -:10530000B002470508003C47020A470240A0804718 -:10531000042801DD80470F3003D56A7880024704F6 -:10532000300007C05C00044703A37847051C95477D -:105330001E3C4702A047090E01AD1A470E01604707 -:105340000378470817C05C00104708100617900A3A -:1053500000184707020F4705A0014C4706029001BD -:1053600047021000E04702080B38470534A01B47EE -:1053700009920F784705824D470601470204000C49 -:105380000010470303BC000447020216950A4711A8 -:1053900003D047022047040604003C204703202096 -:1053A000F080470402378009471004001A788147CB -:1053B000050104183C6047032020F7816680020144 -:1053C000802CE04703023FC04709024703D070002A -:1053D000204704024706202020F00142808000136D -:1053E0008299090200443FC04709010004001A8164 -:1053F00080470B01002020F30366C00012A346057E -:10540000471340470A3C470280003020F047051E02 -:10541000810029471004021A01E0470604183C6085 -:10542000470420F780E04702101E8CC0470E01475A -:105430000301C009E02847097008002060F080449B -:10544000008000084E810A0080470B0247020402D8 -:105450001A79F004470B200020F380070080000435 -:10546000B408471202D050A04707603C20470320F1 -:1054700060F0804702500010038D471104000A506D -:10548000A0084706183C4047032020F747041000B7 -:105490001D80470E01470203C25010A04707229407 -:1054A00047048060F080470202003040C79F470DEC -:1054B00002800007C0CAD04707081A9408470201B3 -:1054C000E020F74703031030941F0E471003C0601D -:1054D00047020447050C02940050A0006060F047AA -:1054E000052001ED80471007C1EE01E08047050867 -:1054F0000A94084702010020F7470306100014A58C -:10550000470218470B01470203C040508447060C6E -:1055100001680C50A0003860F0470502810D1F4063 -:10552000470C02470207C0EA00B84705020A0A947E -:105530000100A0470220F74702084000029EE00A4F -:10554000471042A8103080004047112047040B4705 -:10555000100AA85A001000C0471150471543C01048 -:10556000008047083C470401010F340280470201D4 -:1055700042000E47100C3C5A01904707083CE04798 -:10558000050731404702208155E0471123C0003014 -:1055900047093C00080040008000A0000347160CAB -:1055A0003C5A00014707083C60718047035120477F -:1055B000070E471023C0470201C047073C20008068 -:1055C00080000FF0A14047170C3C5E4709083C40A3 -:1055D000019047028FA5200280470401471123C094 -:1055E000101047093C204703010103B80010004098 -:1055F0002042E09047100C3C4A99804707083C4005 -:105600004704020747022000100056879E471023D8 -:10561000C0101047093C00084702C047020140473C -:10562000031C16B147110C3C4A984708083CC05867 -:105630004702804707102DD0471143C0470B3C4716 -:105640000280004047040347160C3C4E470201808D -:105650004705083CC0001001470520470718470EC2 -:1056600043C2E0470A3C2010470501428047050D30 -:1056700047100C3FEE4709083CE098470602804778 -:10568000050110470C40471A630029400010002014 -:10569000470B7047088047111200A9408018470D3A -:1056A00040024704084727200147043847120647AD -:1056B00006180040472503004003C2001047270199 -:1056C000470283D60018470E30470401470C0447B1 -:1056D0000540471B80470B02470402470240470F23 -:1056E0000F06804703083C000147050285470B046D -:1056F0004702040281C047090102007A804703087B -:105700003D40008047040102470B074703A804B14E -:105710008A470D280002083C20014702C0470408C0 -:10572000403C00088000802A5034024702840281F5 -:10573000E108470C064703083C6001E000C047034E -:105740004000183C605010148025A04702404702DA -:10575000E804B981470F50083C470B3C2001470241 -:10576000402AA040470201000280C0471050083D77 -:10577000607A0001804705183D4000A001002AA77B -:105780003047020405E804F00A2018470C7000486E -:105790003C470702700023C000700447022E7B388C -:1057A0004703060294E00E4710083CC047090BC1AE -:1057B00040000A470222402800054702E826B001BF -:1057C00047031006B04704807E8047030154184702 -:1057D000080800B01908D04705034047022A81474E -:1057E0000580BEF198470202027680384702229572 -:1057F000EA01E100804704010A75E971F0470401FC -:10580000E04002012A96E7804705D010470280AFAA -:105810000654300002017C0B470601470202F43CAB -:1058200047048050BC6047020430E6D047048286BB -:10583000B01C4703053376684703147D4707800192 -:105840000141791808C00040A187E0470391E7B102 -:105850008047032004A14704841282284702096874 -:10586000090880470702BC2A7180470401E047030A -:105870003E820919470380A5E847030200F20E6C37 -:1058800047020A940139E1C0470714FA01C04705ED -:10589000E047033E94C009470303C14705AF0E42EA -:1058A0006002020968087047080154087047034005 -:1058B00000FC470515910F470343C4A047030405A7 -:1058C0003A462805000A940080470802955E800841 -:1058D0000004001164470554A8470403C1010018DF -:1058E0000008550A470420141C470601470202BC61 -:1058F0003A7080470205008002470303C0001E473C -:105900000343C5AD9A4703A51F86470302BD6950EF -:10591000A0470580470215EC80B0470205012140F1 -:1059200040470283D6000A470303C0050A470340E5 -:105930003604204702083C200847028047020247FD -:1059400003D82F08F04702A8503C002047023C0033 -:10595000D59B470343C4880147020220D2A0644775 -:1059600002083CE0504702C047040148D47A701056 -:1059700000042475F847043C15C38A470303C04755 -:105980000310000403CC004040083C470804000119 -:10599000C019D847030500BC0020008028B5918AB3 -:1059A000470343C5A01E001042205220600050282B -:1059B0003CE07980018047040102E8EBB8000180F7 -:1059C0000007A1600006001407E947021847023DDE -:1059D000000D000200050AC03C47047C10F0470798 -:1059E00002A809D0A540002CBD746247022030CE29 -:1059F0008D47044021A01B4703A5575428020020CF -:105A000040F980B0470401470202955D80084702D3 -:105A10002C3DF76040200020F4950A470472008076 -:105A2000110062002F8247020103470410000800A2 -:105A30001010020204002200202000080424044761 -:105A400007080447032004002020202400082C0419 -:105A50000C0C47030410100C001000084703080842 -:105A600047030830003030040022260604080004F2 -:105A700004000447042008000C000828000C00085B -:105A8000002808001008082808470228282847028C -:105A90000C10101024000224242C2C0400282C218B -:105AA00001080011301020000422022247032020A8 -:105AB000203004083010001008000404040400041E -:105AC00000044706080004080808470208470224A3 -:105AD000240400042C2C282847022101211004084A -:105AE0003010201047022000202008042020200031 -:105AF00004470404080004040404001010101010EB -:105B000000060810080803024702040408020424DF -:105B1000242404002C2C2820020021012100020A48 -:105B200020002800080228282030040030301020EF -:105B30000002470204000A020404001400061010C8 -:105B4000100804470208080008020C040C2C02028A -:105B50002C2C2C2C0006282A2A470208220022007E -:105B60000C02200220204702202030300202507018 -:105B700040000A0E0C080C0447030400100808102B -:105B8000100212040002202020080824242400020D -:105B90000247040A0E4703020447020624060808C7 -:105BA0002C2428280202201030100E0A2010280869 -:105BB0004702280020200A08203030180400021A6A -:105BC0000A040A0A0C040C0C470208082808020CF4 -:105BD000080828080C02280A2802000808022008E1 -:105BE000080020282010000C1010041008020404E3 -:105BF000280C040828202121080031303020060E0E -:105C0000222222200E0620470220040C1010100031 -:105C10000A4702040404000C040447020A02470273 -:105C200008000608080A020808000604060402061E -:105C3000082A28470321213130060A30302020026B -:105C40000220202000080400201000060010001090 -:105C5000000C0400040404061614101010020618A8 -:105C60001008080147020804040A02040404060498 -:105C7000082A0C2A220A0223232322000822222A8D -:105C8000200A02282A301004001010000802020026 -:105C900008080008000C040C0C0206180818180468 -:105CA00000081808080A020C0C0C0600080E042252 -:105CB0000E0A06222A22220008222222220C022276 -:105CC0002222220200320010100002405040000246 -:105CD0000C08080C04080A040447030810101212E8 -:105CE0000400024702200E04240424200C0E0200AB -:105CF0000202020C02020202040206020606020866 -:105D00000A262A08080B30303030060820202828C0 -:105D100047028200800103000228282800020808A8 -:105D20002818180E091A1A0A080208080C0C0C0088 -:105D3000020C002208000E020A22020603020222BE -:105D40000208080202220208082220301002041071 -:105D500014040408000400080804080800012102D3 -:105D60000011301020080E022202000A0847050226 -:105D7000100010100247021000040A0C0404000670 -:105D8000080802000A0206000A0A020E0A000606B5 -:105D9000060200040A0A0A02020013011130040A72 -:105DA0000030002002470508061047030647050299 -:105DB00008100004470314141010000218101818DB -:105DC0000147020804060200060606020C080A0A39 -:105DD0000A00080A03010320470202200A020A00FF -:105DE0001A0A1A120400081A08080002080808080B -:105DF0000800080C0C0C02040C0018100400101809 -:105E000000100002040606060808060202000A0C3A -:105E10000002000247032000220C020002120002CE -:105E20000012121202470240424047020C080808C2 -:105E30000400020404044704101212040012470371 -:105E4000020004060602470202020202470202475B -:105E5000020604020406060002000A021A0A0001F1 -:105E60001212003204470220082000020808080825 -:105E70000200080008100403121A12084704040262 -:105E800002020606020200042202020006010002CB -:105E9000000247022200020A47023A2A321A02048A -:105EA0000412040447040808040008000101020069 -:105EB00011101047020402020247080247021010A4 -:105EC000020010100010000604060602470202023B -:105ED0000A000600080A040200020404000200048A -:105EE000080A0A10020013131302044702024706AD -:105EF0001047020647021000060010001047020A71 -:105F00004702144703141410040002180018180064 -:105F10000110120616020006060200040008080816 -:105F200047020201010147041018100200181A1A52 -:105F30000A04000A020802000200080008470404DC -:105F400004020404040010040010101210000216D1 -:105F5000060606470302470202040808084706101F -:105F600004021000101002001002020204000202DB -:105F7000000200080808080800020404040447049A -:105F80001202040012101012020006160602470345 -:105F90000247060400040004040014020018181844 -:105FA0001A0001120202020400024702080002085D -:105FB00008000802470504011202121047021012DD -:105FC0000612000206060006000447022000060131 -:105FD00020002047041018104702383A320A020401 -:105FE000060606044702110162003F8247020103D0 -:105FF00047080247080247060101470620204706D6 -:1060000020204706042047064004470740470E85E6 -:106010008447070147068047088047060202470673 -:1060200002470802471E04470804471E0404471E8F -:106030002047072020470620204706014708014740 -:1060400036010147164247084247060202470E2121 -:106050002047062021470610204706141447065003 -:1060600050470610104706101047069511470610B6 -:106070009447069090470610104706021047060204 -:106080000247260404472604470804471E20470702 -:1060900020204706202047060101472E014708011E -:1060A000471E424247070247063020470630304726 -:1060B00006111147061410470650144706105047A2 -:1060C0000610104706111047061011470694944712 -:1060D0000610904706101047061210470612124786 -:1060E000061010470710470E04470804472E040403 -:1060F000472E202047062120470701472601014758 -:1061000008820080010347205202470630524706AA -:106110003030470610104706111047060415470691 -:10612000404047160101470E84470804470E1202FB -:1061300047061210470610124706101047061010A7 -:1061400047060414473604470804472E2020470614 -:106150000121471E01470801471610470712104743 -:1061600006301247067272470710470E0447074170 -:1061700005470740470E0147080147160404470634 -:1061800002470802470E0202470610470710104751 -:1061900006101047061010470710476621204707D2 -:1061A0002147160101471610470710104706121223 -:1061B00047062030470642204707424716414047DE -:1061C0000701470E0101472602024716024708024F -:1061D000470E104707101047061010470710475E7C -:1061E00020470721214708110262002F8247020140 -:1061F0000347081B1B47041F1B4704041F47101BB2 -:1062000047051B1B47041F1B47041F1F47101B1B71 -:1062100047041B1B47041F1F47041F1F47051B473D -:106220000A1B1B47051B4704040447041F1F47059F -:106230001B470A1B1B4704E4FB4704E4E447041F15 -:10624000FF47051B470AF9194704E4F94704E4E44A -:106250004704F7F7470513470AF9F94704E4F947F4 -:1062600004E4E44704F7F74705F3470AF9E947046C -:1062700014FD47041E1C4704F7FF4705E1470AE9E0 -:10628000E947041CFD47041E1A4704EFFF4705E1D8 -:10629000470ADDC947041EDD47043E3A4704BDBF37 -:1062A0004705A9470ADDD94704A2FF4704A6A2472C -:1062B00004BDBD4705B9470AFFDD4704A6FF4704F3 -:1062C00084864704BDBD470404BD470AFFFD47045B -:1062D00006FF4704040447043D3D4704043D470AC4 -:1062E000FFFF470406FF470404044704FDFD47047D -:1062F00004FD470AFBFB4705FB4704244705FDFD5A -:106300004705F9470AFBFB4704FBFB4704FFFB4734 -:1063100004FFFF4705FB470AFB224704FBFB47043A -:10632000DFFB4704FFFF470522470A22024704FB21 -:10633000FB4704DFDB470426FF4705224780028234 -:106340000080010347022C2C47041F3E47041F1FF7 -:106350004704CCDE4705C8470A3E2C47041F3F4789 -:10636000049F1F4704DEDE4705C8470A3F3E470437 -:106370001F3F47049F9F4704DEDE4705DA470A3781 -:1063800036470401374704898147045ADA470552E2 -:10639000470A76364704C1F64704A9814704FAFA4A -:1063A0004705D2470A74744704C1F44704A9E94772 -:1063B00004F2FA4705F2470A74744704E5F4470407 -:1063C000EDED4704F2FE4705F2470A7474470427CF -:1063D000744704CF8F470476FE470570470A7474EC -:1063E00047041F7447049F9F470474F6470574478A -:1063F0000A746447041B7447049B9B470476F64762 -:106400000574470A60604704197047048999470476 -:1064100076F6470576470A606047040960470489B5 -:1064200089470476F6470576470A64644704096C91 -:1064300047048189470476F6470576470A7676470A -:1064400004097E47048189470476F6470576470AA2 -:1064500076764704297E4704C189470476F64705C6 -:1064600076470A777647047F7F4704F7FF4704772C -:10647000F7470576470A776047045F7F4704B7FF11 -:10648000470477F7470560470A612047045F7F4765 -:1064900004B79F470461F747054047800211036234 -:1064A000003F824702010347838282008001034745 -:1064B00083822244B2010600470000000000000071 -:1064C0000000000000040240000400000A0009006F -:1064D00000140240000400000A000900000C024001 -:1064E000000800000B000900000802400004000042 -:1064F0000A00090000100240040000000200090028 -:1065000000000240020000000100090000100240EB -:106510000080000000000000001C0240200000007D -:10652000010000000000000000000000000000006A -:10653000000C024004000000010000000000000800 -:106540000080000800000108008001080000020827 -:10655000000004080000080800000C0800001008F3 -:10656000000014080000180800001C0800000800C3 -:1065700010001800200028003000380040004800BB -:1065800050005800000000006410B71DC8206E3B8A -:10659000AC30D9269041DC76F4516B6B5861B24D2A -:1065A0003C7105502083B8ED44930FF0E8A3D6D694 -:1065B0008CB361CBB0C2649BD4D2D38678E20AA0FC -:1065C0001CF2BDBD4932435F504D49435F4D4147C9 -:1065D00000537475636B20627574746F6E207265FE -:1065E00067697374657220697320696E76616C697E -:1065F000642C20636C656172696E672E004275744D -:10660000746F6E2069642000697320737475636B06 -:106610002100427574746F6E2077617320707573FA -:10662000686564206F6E20626F6F742E20427574EF -:10663000746F6E20636F756E7465723A20004469E2 -:1066400073706C617920627573792D77616974203C -:1066500074696D656F757420657870697265642101 -:10666000004650474120636F6E66696775726174BA -:10667000696F6E206661696C65642E004469737091 -:106680006C617920696E697469616C697A656420EE -:106690006166746572200020726574726965732E7C -:1066A00000446973706C617920696E697469616C0A -:1066B000697A6174696F6E206661696C65642E0029 -:1066C000435245534554206E6F74206C6F7720649D -:1066D0007572696E672072657365740043444F4E2E -:1066E00045206E6F74206C6F772061667465722030 -:1066F000726573657400435245534554206E6F7440 -:1067000020686967682061667465722072657365C8 -:10671000740043444F4E45206E6F7420686967686B -:106720002061667465722070726F6772616D6D6949 -:106730006E6700456E61626C696E67203676362042 -:1067400028446973706C617920564444432900459C -:106750006E61626C696E67203476352028446973F7 -:10676000706C617920564444502900446973616219 -:106770006C696E67203476352028446973706C61CB -:10678000792056444450290044697361626C696EF3 -:1067900067203676362028446973706C61792056FC -:1067A00044444329002E2E2F7372632F647269763E -:1067B0006572732F6932632F6932632E63002E2E48 -:1067C0002F7372632F647269766572732F693263F7 -:1067D0002F6932635F68616C2E63004661737420B9 -:1067E0004D6F646520506C7573206E6F74207965F1 -:1067F0007420737570706F72746564002E2E2F7321 -:1068000072632F647269766572732F706572697036 -:10681000685F636F6E6669672E63006164647265AA -:1068200073732000206973206F7574736964652029 -:1068300073797374656D20666C6173680066616955 -:106840006C656420746F2065726173652073656385 -:10685000746F72200050726F6772616D206661699B -:106860006C6564204000496E76616C6964206669DD -:10687000726D7761726520646573637269707469A3 -:106880006F6E2100436865636B73756D6D696E672C -:10689000206669726D7761726520757064617465D8 -:1068A0000043616C63756C61746564206368656343 -:1068B0006B73756D3A2000496E76616C6964206671 -:1068C00069726D776172652043524320696E20536F -:1068D000504920666C61736821007072765F657242 -:1068E0006173655F6F6C645F6669726D7761726515 -:1068F000007072765F77726974655F6E65775F6648 -:1069000069726D77617265005765277265206465ED -:10691000616400436865636B73756D6D696E6720B4 -:10692000002062797465730D0A00436865636B73B8 -:10693000756D202D2077616E746564200020676F6F -:106940007420004F757220696E7465726E616C20E0 -:10695000666C61736820636F6E74656E747320611A -:106960007265206261642028636865636B73756D6E -:10697000206661696C656429212054686973206907 -:1069800073207265616C6C792062616421004F75BF -:10699000722070726576696F7573206669726D77A3 -:1069A00061726520757064617465206661696C65EB -:1069B000642C2061626F7274696E67207570646107 -:1069C00074652E004E6577206669726D7761726519 -:1069D00020697320617661696C61626C6521004C8D -:1069E0006F6164696E67207265636F766572792086 -:1069F0006669726D77617265004661696C656420D5 -:106A0000746F206C6F6164207265636F7665727954 -:106A1000206669726D776172652C20737472696B80 -:106A200065206F6E652E2054727920616761696EF2 -:106A30002E004661696C656420746F206C6F616420 -:106A4000207265636F76657279206669726D776111 -:106A500072652C20737472696B652074776F2E20B9 -:106A600054727920616761696E2E004661696C65B8 -:106A70006420746F206C6F6164207265636F76654B -:106A80007279206669726D776172652C20737472F9 -:106A9000696B652074687265652E205341442057E8 -:106AA000415443480048415244204641554C54000B -:106AB00065786974207374616E646279000D0A0DE3 -:106AC0000A0D0A00E29688E29688E29688E29688A5 -:106AD000E29688E29688E295972020E29688E296F0 -:106AE00088E29688E29688E29688E29688E2959710 -:106AF00020E29688E29688E29688E29688E2968876 -:106B0000E29688E2959720E29688E29688E2968857 -:106B1000E29688E29688E29688E29688E29597E285 -:106B20009688E29688E29688E29688E29688E296CF -:106B300088E2959720E29688E29688E29688E29627 -:106B400088E29688E29688E29688E29688E29597AF -:106B500000E29688E29688E29594E29590E295901C -:106B6000E29688E29688E29597E29688E29688E235 -:106B70009594E29590E29590E29590E29688E2965F -:106B800088E29597E29688E29688E29594E295905D -:106B9000E29590E29688E29688E29597E29688E2FE -:106BA0009688E29594E29590E29590E29590E29530 -:106BB00090E2959DE29688E29688E29594E295901F -:106BC000E29590E29688E29688E29597E2959AE2BD -:106BD0009590E29590E29688E29688E29594E29507 -:106BE00090E29590E2959D00E29688E29688E29682 -:106BF00088E29688E29688E29688E29594E2959DEE -:106C0000E29688E29688E29591202020E29688E23A -:106C10009688E29591E29688E29688E29688E296D6 -:106C200088E29688E29688E29594E2959DE29688BD -:106C3000E29688E29688E29688E29688E295972026 -:106C400020E29688E29688E29688E29688E2968824 -:106C5000E29688E29594E2959D202020E29688E2D3 -:106C60009688E2959120202000E29688E29688E2BC -:106C70009594E29590E29590E29688E29688E29566 -:106C800097E29688E29688E29591202020E2968805 -:106C9000E29688E29591E29688E29688E29594E2FF -:106CA0009590E29590E29688E29688E29597E29632 -:106CB00088E29688E29594E29590E29590E2959D1F -:106CC0002020E29688E29688E29594E29590E295FB -:106CD00090E29688E29688E29597202020E29688B6 -:106CE000E29688E2959120202000E29688E296883C -:106CF000E295912020E29688E29688E29591E295CD -:106D00009AE29688E29688E29688E29688E29688E9 -:106D1000E29688E29594E2959DE29688E29688E272 -:106D20009688E29688E29688E29688E29594E295C3 -:106D30009DE29688E29688E29688E29688E29688B6 -:106D4000E29688E29688E29597E29688E29688E253 -:106D500095912020E29688E29688E2959120202065 -:106D6000E29688E29688E2959120202000E2959AAA -:106D7000E29590E2959D2020E2959AE29590E29529 -:106D80009D20E2959AE29590E29590E29590E295A9 -:106D900090E29590E2959D20E2959AE29590E29599 -:106DA00090E29590E29590E29590E2959D20E29593 -:106DB0009AE29590E29590E29590E29590E2959016 -:106DC000E29590E2959DE2959AE29590E2959D205C -:106DD00020E2959AE29590E2959D202020E2959AF6 -:106DE000E29590E2959D202020004C61737420660E -:106DF00069726D7761726520626F6F74207761735D -:106E000020737461626C653B20636C6561722073F2 -:106E10007472696B657300486F6C6420646F776E81 -:106E2000205550202B204241434B202B2053454CD2 -:106E300045435420666F72203520736563732E209E -:106E4000746F20666F7263652D626F6F742050528D -:106E500046004669726D77617265206973206572BC -:106E600061736564005761746368646F6720636170 -:106E700075736564206120726573657400536F6675 -:106E80007477617265206661696C757265206361F3 -:106E9000757365642061207265736574004661696D -:106EA0006C656420746F20737461727420666972FB -:106EB0006D776172652C20737472696B65207468DC -:106EC0007265652E004661696C656420746F20737D -:106ED00074617274206669726D776172652C2073BB -:106EE0007472696B652074776F2E004661696C65FA -:106EF0006420746F207374617274206669726D7798 -:106F00006172652C20737472696B65206F6E652EDB -:106F100000466F7263652D626F6F74696E672072D1 -:106F200065636F76657279206D6F64652E2E2E0015 -:106F3000456E61626C696E67207761746368646F27 -:106F40006700426F6F74696E67206669726D776162 -:106F50007265204020002E2E2E0D0A0D0A00426F71 -:106F60006F7420626974733A2000536F667477619E -:106F70007265206661696C7572653B207265736528 -:106F80007474696E6721004153534552543A20008E -:106F90003A00415353455254004153534552544EC5 -:106FA000002A2A2A20575446200053544D333200D9 -:106FB00053544D3332207065726970686572616C2C -:106FC000206C696272617279207472697070656494 -:106FD00020616E206173736572740069746F612043 -:106FE00062756666657220746F6F20736D616C6C7C -:106FF000002E2E2F7372632F7574696C2F736C655E -:047000002E630000FB -:10700400FF00000000010203040102030406070854 -:04701400090000006F -:0400000508000200ED -:00000001FF diff --git a/bin/boot/boot_silk@1478015115.bin b/bin/boot/boot_silk@1478015115.bin deleted file mode 100755 index 971fd41b81..0000000000 Binary files a/bin/boot/boot_silk@1478015115.bin and /dev/null differ diff --git a/bin/boot/boot_silk@1478015115.hex b/bin/boot/boot_silk@1478015115.hex deleted file mode 100644 index 5fe2f37b47..0000000000 --- a/bin/boot/boot_silk@1478015115.hex +++ /dev/null @@ -1,907 +0,0 @@ -:020000040800F2 -:100000005821012085010008C90100081D170008BA -:10001000C9010008C9010008C9010008000000006A -:10002000000000000000000000000000C9010008FE -:10003000C901000800000000C9010008C90100084A -:10004000C9010008C9010008C9010008C901000868 -:10005000C9010008C9010008C9010008C901000858 -:10006000C9010008C9010008C9010008C901000848 -:10007000C9010008C9010008C9010008C901000838 -:10008000C9010008C9010008C9010008C901000828 -:10009000C9010008C9010008C9010008C901000818 -:1000A000C9010008C9010008C9010008C901000808 -:1000B000C9010008C9010008C9010008F9100008B9 -:1000C00005110008111100081D110008C9010008E0 -:1000D000C9010008C9010008C9010008C9010008D8 -:1000E000C9010008C9010008C9010008C9010008C8 -:1000F000C9010008C9010008C9010008C9010008B8 -:10010000C9010008C9010008C9010008C9010008A7 -:10011000C9010008C9010008C9010008C901000897 -:10012000C9010008C9010008C9010008C901000887 -:10013000C9010008C9010008C9010008C901000877 -:10014000C9010008C9010008C9010008C901000867 -:10015000C9010008C9010008C9010008C901000857 -:100160002911000835110008C9010008C90100085B -:10017000C9010008C9010008C9010008C901000837 -:04018000C9010008A9 -:10018400002103E00A4B5B584350043109480A4BF1 -:1001940042189A42F6D3094A02E0002342F8043B8B -:1001A400074B9A42F9D301F07DFE01F001FB704741 -:1001B4003C380008000000203000002030000020FF -:1001C40054010120FEE7000037B5012002F092FA45 -:1001D400002401902546E0B200F046F801AA08B9CF -:1001E40010551DE0135D042B0BDD174800F07EF95C -:1001F400019800F082F90120002102F06BFA00203E -:1002040020E00133DBB2042B135509D90F4800F069 -:1002140055F9204600F071F90D4800F067F9012501 -:100224000134042CD7D1019C3CB10A4800F046F9B2 -:10023400204600F062F900F0D1F80120214602F0D6 -:1002440049FA284603B030BDB0310008DC3100085B -:10025400E7310008F131000808B500F0A7FB80F091 -:100264000100C0B208BD000008B5054B03EB001344 -:100274005868198901F075FF003018BF012008BDC6 -:100284003829000838B500242546E0B2FFF7ECFF12 -:10029400A04001340543042CEDB2F6D1284638BD04 -:1002A40073B50020014602F06DF80120002102F030 -:1002B40069F80220002102F065F84FF48040012122 -:1002C400104C02F0A3F904F14006684601F040FF27 -:1002D40002238DF80530A37B60688DF80730002574 -:1002E400A3688DF8045069461034009301F0F8FEB9 -:1002F400B442EAD14FF48040294602F087F902B0B3 -:1003040070BD00BF3829000810B5044608484021D4 -:1003140002F0F6FA0028F9D00548214602F0ECFA7A -:100324000348402102F0ECFA0028F9D010BD00BFC8 -:10033400001001402DE9F043DFF89890DFF8988031 -:1003440087B01020012102F061F9484609210722F3 -:1003540001F012FF07210A4606AD404601F00CFFEA -:100364004FF4007345F8183D00240226012748463F -:1003740029468DF806408DF807708DF804608DF8D5 -:10038400056001F0ADFE80234046294600938DF8B8 -:10039400046001F0A5FEADF80C40ADF80E40ADF8D8 -:1003A4001040ADF8144004F1804404F58834084B3F -:1003B400029320460C2302A9ADF8123002F036FA5B -:1003C4002046394602F08CFA07B0BDE8F08300BF3E -:1003D40040420F0000000240000402402DE9F043B7 -:1003E400334D83B02C6804F12E0323F0070300AFD0 -:1003F400ADEB030D50238DF8003021238DF801302F -:1004040000238DF8023003238DF8033004F1210317 -:100414005BBAADF80430274B04F127086E460DF1A2 -:10042400060203F11C0153F8040B42F8040B8B423F -:10043400F9D11B78137004F12309224606F1230035 -:100444001D4902F063FA31464A46002001F0D2FC0D -:1004540007F1080304F5927443F8040D186846F88C -:100464000900FE23B4FBF3F34344073323F00703EB -:10047400ADEB030D31464246684601F093FC044659 -:100484005520FFF741FF6E466C44A64207D016F88C -:10049400010B552808BF0020FFF736FFF5E755206C -:1004A400FFF732FF00230C372B60BD46BDE8F08315 -:1004B40030000020802900083400002010B5441EBC -:1004C40014F8012F7AB108490B68FF2B0BD80A2ABC -:1004D40002D1FFF783FFF3E70D2A1FBF581C5B18F7 -:1004E40008601A71ECE710BD3000002008B5FFF772 -:1004F400E5FFBDE80840FFF771BF1FB50C2201A955 -:1005040001F0A8FC01A8FFF7D9FF05B05DF804FBD2 -:10051400014608B5044802F080F90348022102F0BC -:100524007EF90028F9D008BD003800402DE9F047D5 -:1005340007310733089E099F92464FEAD108DD0828 -:1005440081460024B4420CD004EB0A03122000FBC1 -:100554000380494638442A4602F0D8F90134A944B4 -:10056400F0E7BDE8F087000008B54FF480400121B2 -:1005740002F040F805484FF40071012201F0F7FD44 -:100584000720BDE8084001F055BC00BF000402404C -:1005940008B5072001F04EFC4FF400710648002214 -:1005A40001F0E5FD042001F045FC4FF480400021FA -:1005B400BDE8084002F01EB800040240F8B5074642 -:1005C400FFF7D2FF8020FFF7A3FF0024661C30BA98 -:1005D40090FAA0F0C0B2FFF79BFF122303FB047450 -:1005E4000025605D00BA90FAA0F00135C0B2FFF7B3 -:1005F4008FFF122DF5D10020FFF78AFFA82E344675 -:10060400E4D10020FFF784FFBDE8F840FFF7C0BF46 -:1006140010B5ADF6D83D02AC204600214FF43D6242 -:1006240002F07FF91B238DE8180010214022692372 -:100634000448FFF77BFF2046FFF7C0FF0DF6D83DC7 -:1006440010BD00BF9D2900082DE9F043ADF6DC3D47 -:1006540002AC00214FF43D620546204602F061F9E8 -:100664002C238DE818002B48182120225C231C27FA -:10067400FFF75CFFBE460F23BB402B4023FA07F273 -:10068400254B242101FB0233002202EBC20000F6B9 -:10069400280013F802C020440021082907D00126AD -:1006A4008E4016EA0C0F14BF0126002603E09E18A4 -:1006B400767806F0010686B101EB0E06F6B24FEA33 -:1006C400D6094FF0010806F0070608FA06F610F8F6 -:1006D400098046EA080600F8096001310929DCD1DD -:1006E4000232242AD1D10EF10B0EBEF1740FA7F100 -:1006F4000407C0D10E238DE81800102190226D2329 -:100704000648FFF713FF2046FFF758FF0DF6DC3DC0 -:10071400BDE8F083172B0008272D0008672F000879 -:1007240008B5FFF721FF0020FFF7F2FE0020FFF7D6 -:10073400EFFE0020FFF7ECFEBDE80840FFF728BFFE -:10074400F0B5214B1A685E246043B0FBF1F4944287 -:10075400ADF6DC3D34D002AD00214FF43D622846B5 -:100764001C6002F0DEF81B238DE8280017481021D6 -:1007740040226923FFF7DAFE08238DE82800144895 -:1007840018216A226023FFF7D1FE002000F286714F -:1007940029440022A2420DD002F11903DBB2DF0882 -:1007A400012603F0070306FA03F3CE5D3343CB556A -:1007B4000132EFE712306C28E8D12846FFF7FEFE3D -:1007C4000DF6DC3DF0BD00BF000000209D290008AF -:1007D4002B3000082DE9F843224E234CDFF88C908F -:1007E4004FF48040012101F005FF30460A21052223 -:1007F40001F0C2FC04F10408052230460F2101F087 -:10080400BBFC3046414601F06BFC01254FF40073FC -:100814000027304641466360A6F5E6362572A77286 -:1008240001F05EFC414648462572A772656001F0FE -:1008340057FC304601F06EFF304604F10C0101F024 -:10084400C1FF3046294601F0DCFF484629462A46C6 -:1008540001F08DFC39464FF48040BDE8F84301F0C7 -:10086400C9BE00BF00040240000000200000024096 -:10087400F0B50746ADF2044D0E460C460025B919F5 -:10088400B4F5806FA1EB040168460DD94FF4806282 -:1008940000F0C4F8284669464FF4806201F0AAFAD1 -:1008A400A4F580640546EAE7224600F0B7F8284636 -:1008B4006946224601F09EFA0DF2044DF0BD08B5DA -:1008C400202001F0FDFD0028FAD108BD08B5022062 -:1008D40001F0F6FD0028FAD00220BDE8084001F03E -:1008E400F9BD00B58DB0684601F073FD0023009397 -:1008F4004FF4807368460A9335230B9301F09AFDF5 -:10090400FFF7E4FFFFF7DBFF0DB05DF804FB000029 -:1009140030B5022095B0012101F060FE0024224888 -:100924000021204403220B460C3400F0C3F8482C69 -:10093400F5D101A801F044FD10230024019301A87E -:10094400162305930294039404940694079401F0E7 -:100954004FFD012001F092FDFFF7C3FF08A801F04D -:1009640038FD4FF44075662308A813930894129534 -:1009740001F060FDFFF7AAFF08A801F02AFD992302 -:1009840008A813930894129501F054FDFFF79EFFF5 -:1009940042F6E06001F04EFAFFF7A3FF0220214681 -:1009A40001F01CFE15B030BD8C30000830B50121BB -:1009B4008DB0022001F012FE022001F073FD6846A2 -:1009C40001F007FD4FF0806300934FF040730493F0 -:1009D4004FF4407368460A93AF230B9301F02AFD4A -:1009E400FFF774FF01F066FD054601F063FD044660 -:1009F40001F060FD240244EA004040EA0504FFF7E8 -:100A04005EFF0220002101F0E9FD0348231A584249 -:100A140058410DB030BD00BFC225370070B5154632 -:100A24008CB004460E460220012101F0D7FD681E59 -:100A340001F038FD684601F0CCFC4FF08063009370 -:100A44004FF040730493042305934FF440630993D8 -:100A54004FF440730A934FF40053089368460B23F2 -:100A64000B9301F0E7FC304601F014FD2544AC4241 -:100A740004D001F01FFD04F8010BF8E7022001F097 -:100A840029FDFFF71CFF0220002101F0A7FD0CB097 -:100A940070BD0000024A136B43F0FF0313637047F9 -:100AA40000380240024A136B23F0FF0313637047BC -:100AB4000038024013B504464068009002208DF8C7 -:100AC40004008DF805208DF8061020682189A27A8B -:100AD4008DF8073001F050FB2068694601F000FBF7 -:100AE40002B010BD02460068838823F4E0631B044F -:100AF4001B0C838003889BB243F400730380082398 -:100B04000020517513751076704707B500230091C6 -:100B140069468DF804308DF805308DF8073001F002 -:100B2400DFFA03B05DF804FB10B50B4C342300FB73 -:100B34000344236B7BB10A2001F081F96068A1684A -:100B4400FFF7E3FF20696169FFF7DFFF236B0120F3 -:100B5400BDE81040184710BDD4300008027D082AB3 -:100B640010B5044607D10268938823F4C0631B04BC -:100B74001B0C938084E0434B006803EB8203596BA6 -:100B840001F0CAFB00287BD0237D072B72D8DFE855 -:100B940003F0040B1921323B5A6E237A226803F0C6 -:100BA400FE03138201232DE02268A37A1382637A61 -:100BB4000BB1022326E093889BB243F480639380B5 -:100BC40006231FE0226813889BB243F480731380CA -:100BD400032317E0237A226843F001031382E37AA4 -:100BE400012B01BF138823F480631B041B0C08BF73 -:100BF4001380042306E0226893889BB243F4806345 -:100C04009380052323753BE023682269198A237B9B -:100C1400D154237BE27A0133DBB2591C9142237312 -:100C240007D12268138823F480631B041B0C1380F0 -:100C340026E0934224D12268938823F480631B0422 -:100C44001B0C938013E0237B21692268CB5C138205 -:100C5400237BE17A0133DBB29942237310D1938869 -:100C640023F480631B041B0C93800723CAE72046EC -:100C7400012101E020460021BDE81040FFF732BF0A -:100C8400002010BDD43000082DE9F341184C342362 -:100C940000FB0344236B43B300209847D4F804803B -:100CA400A668009600250127404669468DF8047021 -:100CB4008DF805508DF8075001F012FAB1B22A46AA -:100CC400404601F054FA2669646900943046694646 -:100CD4008DF804708DF805508DF8075001F000FA76 -:100CE4003046A1B22A4601F042FA02B0BDE8F081D2 -:100CF400D43000081C23104A434370B505460F4CFA -:100D0400D058D61801F04AFA342305FB03440021D5 -:100D1400E06901F06FFC337923B92846BDE87040DF -:100D2400FFF7B2BF6068A168FFF7EFFE2069616951 -:100D3400BDE87040FFF7E9BE34010020D43000085C -:100D44002DE9F04F344D1C2303FB005387B01B796E -:100D540007460BB9FFF7E8FE304B342407FB043495 -:100D64000226D4F80490A389A26894F80EB00193E3 -:100D74004FF001084FF0000A484602A902928DF88C -:100D84000C608DF80D608DF80E808DF80FA001F0C9 -:100D9400A7F9019B484619465A4601F0EDF9D4F8E3 -:100DA4001090238B626994F81AB00193484602A903 -:100DB40002928DF80C608DF80D608DF80E808DF820 -:100DC4000FA001F08DF9019B5A461946484601F0DF -:100DD400D3F94146E06901F00DFC02A801F076FA6E -:100DE4000F4A236A029393424FF48063ADF81230A2 -:100DF4004FF01C0384BF626AADF80E2003FB07F4B6 -:100E040002A9285901F0F8F92859012101F06CFAD6 -:100E140007B0BDE8F08F00BF34010020D4300008D3 -:100E2400A08601002DE9F341414C9846237F0546F5 -:100E34000E4617461BB93F484FF4BD7103E020B17D -:100E44003C4840F27B1100F087FF23790BB9002066 -:100E54006AE023681B8B9B0711D49DF82030E37252 -:100E64000025099B277284F80A806672257325750C -:100E74002361E5822068012101F042FA1FE0FFF7B7 -:100E840039FF2846FFF75CFF22682B48138B99072C -:100E9400E3D50138FAD1DAE7227E22B1019A511E54 -:100EA4000191002AF8D100222276019A204902B346 -:100EB4000A7D082A13D14D7D237D082B26D0236873 -:100EC4001A8892B242F480721A809A8892B242F4DA -:100ED40040729A8001222276174A0192DEE7CA8A7A -:100EE400B2F57A7F05D20132CA82012000F0A7FF51 -:100EF400E2E79A8822F4E0621204120C9A801A88BB -:100F040092B242F400721A8008230B7522680A4CCC -:100F1400138B9B0707D5013CFAD12046FFF7EAFE65 -:100F24002046FFF70DFF284602B0BDE8F08100BF60 -:100F3400340100203132000880841E00017D0368E2 -:100F4400082906D19A8822F480721204120C9A801D -:100F540038E09A8A120541BF9A8A22F40062120488 -:100F6400120C48BF9A829A8AD20541BF9A8A22F407 -:100F740080721204120C48BF9A829A8A52051ED5B6 -:100F84009A8A22F480621204120C04299A820AD1E9 -:100F9400032202759A8822F4E0621204120C9A80E9 -:100FA400002303760EE0012909D19A8822F4E06235 -:100FB40012040021120C01759A80017602E00021CE -:100FC400FFF790BD0020704737B5124C124B2360D9 -:100FD40008222275482200238DF8042001250C22C2 -:100FE40001A88DF80520237123762373E37223610E -:100FF4008DF806308DF8075000F090FF492301A8C2 -:101004008DF8043000F08AFF206801F0C7F82577D6 -:1010140003B030BD34010020005C004013B50D4C1A -:10102400227F034622B90C484FF48C7100F094FEE1 -:1010340038B1012200920848084A4FF48D7100F03B -:101044007FFE23790BB9FFF77BFE237901332371EC -:1010540002B010BD340100203132000846320008CD -:101064000C4A117F08B519B90B484FF4957103E088 -:1010740020B1094840F22B1100F06EFE13793BB108 -:10108400013BDBB213711BB9BDE80840FFF732BE68 -:1010940008BD00BF340100203132000837B50D46C9 -:1010A40014460093069B019301212A462346FFF729 -:1010B400B9FE03B030BD07B500930123FFF7EEFF7F -:1010C40003B05DF804FB37B50D4614460093069B48 -:1010D400019300212A462346FFF7A4FE03B030BD46 -:1010E4001FB504AC04F8013D01230094FFF7EBFFA6 -:1010F40004B010BD0148FFF731BD00BF340100202A -:101104000148FFF71BBF00BF340100200148FFF76F -:1011140025BD00BF500100200148FFF70FBF00BFED -:10112400500100200148FFF719BD00BF3401002021 -:101134000148FFF703BF00BF3401002038B50C4657 -:1011440005460020FFF76AFF234681212A46002036 -:10115400FFF7B1FF04460020FFF782FF204638BDA9 -:1011640038B50C4605460020FFF758FF234680217A -:101174002A460020FFF7B4FF04460020FFF770FF63 -:10118400204638BD37B50D460DF107010446FFF77B -:10119400D5FF50B19DF807300121A9401943C9B2C8 -:1011A40020468DF80710FFF7DBFF03B030BD0000C9 -:1011B40013B590200DF10701FFF7C0FF044648B3B3 -:1011C4009DF80740A4F111035C425C4114B3FF2174 -:1011D4004220FFF7C5FFFF214120FFF7C1FF352063 -:1011E4000DF10701FFF7AAFF044620B90B480C4A8A -:1011F400BE2100F0A5FD9DF80710352041F0030144 -:101204008DF80710FFF7ACFF0A201E21FFF7A8FF97 -:101214000028EBD0204602B010BD00BF7232000897 -:101224009032000807B587200DF10701FFF786FF0C -:1012340018B19DF80700C0F3800003B05DF804FB0B -:1012440007B536200DF10701FFF778FF20B19DF8AF -:1012540007301B090B2B01D0002014E077200DF17F -:101264000701FFF76BFF0028F6D09DF8073003F065 -:10127400100303F0FF0023B1FFF7D4FF80F0010057 -:10128400C0B200F0010003B05DF804FB08B53620DD -:101294000121FFF777FF00B1FEE708BD07B5EF2195 -:1012A4007420FFF75DFFFF217520FFF759FF0DF153 -:1012B40007017720FFF742FF0DF107017820FFF7C0 -:1012C4003DFF0A213920FFF74BFF35200421FFF7AA -:1012D40059FF00B1FEE703B05DF804FBB0F1006F05 -:1012E40010B504460BD20C48FFF7E8F82046FFF788 -:1012F40004F90A48FFF7FAF84FF0FF3010BD084B25 -:10130400002053F8042BA24202D81A68944202D354 -:1013140001300B28F5D110BDF0320008AF320008BF -:10132400283100082DE9F8430446174699460E462D -:10133400C1B3FFF7D3FF0546601E3044FFF7CEFF6D -:10134400002D804631DB00282FDB461B01361FB100 -:10135400002031464A46B84700F022FE2C46444558 -:101364001EDCF32000F032FE114B002133F8140090 -:1013740000F064FE09280AD00E48FFF79FF82046C3 -:10138400FFF7BBF8FFF72AF800F018FE0DE02FB1C5 -:10139400C5F10100204431464A46B8470134DEE72E -:1013A40000F00CFE0120BDE8F8830020BDE8F883BE -:1013B40058310008C83200082DE9F0470E4615469A -:1013C4000746984600F0ECFDF32000F0FFFD4FEADD -:1013D400D51934463544AC42C6EB040A20D0B81BB8 -:1013E400204414F8011B00F05FFE09280CD00F48BC -:1013F400FFF764F83846FFF780F8FEF7EFFF00F0D8 -:10140400DDFD0020BDE8F087B8F1000FE3D01AF04D -:101414007F0FE0D14FEADA104946089AC047DAE76D -:1014240000F0CCFD0120BDE8F08700BFE0320008E9 -:1014340008B545F2555000F07DFF042000F080FF10 -:1014440040F6FF7000F082FF002000F073FF4FF4BD -:1014540080500121BDE8084000F096BD08B500F0B9 -:1014640083FFBDE8084000F077BF08B57D2001F098 -:1014740015F9003018BF012008BD4900FFF760B915 -:101484002DE9F041012186B005460020FFF758F907 -:10149400042000F0A3FB03A8294600F003FC03A8E2 -:1014A40000F00FFC08B93C4814E03C480C35FFF749 -:1014B4001DF804992846FFF7DBF904463848FEF77F -:1014C400FDFF2046FFF719F8FEF788FF059B9C42B5 -:1014D40004D03448FFF70AF8012059E0049C32484C -:1014E400FFF704F82146314A31480023FFF71AFF79 -:1014F4003048049FFEF7FAFF4FEA57080024BC4225 -:1015040020D23E1BB6F5803F28BF4FF480362A48D0 -:1015140061193246FFF782FA002304F10060009358 -:1015240000F5804024493246FFF746FF18B92348A6 -:10153400FEF7DCFF06E0344408EB54003946FFF7BD -:10154400FFF8DCE71E48FEF7B9FF0498FEF7D5FF65 -:101554001C48FEF7B3FF1649049A002000F04AFC29 -:1015640004461948FEF7AAFF0598FEF7C6FF174878 -:10157400FEF7A4FF2046FEF7C0FFFEF72FFF059BF2 -:101584009C4204D01248FEF7B1FF022000E0002084 -:1015940006B0BDE8F08100BFF93200081733000837 -:1015A400343300084A3300086D3300087F14000800 -:1015B40000400008803300085101002093330008E4 -:1015C4009E330008AC330008B5330008C833000864 -:1015D400CE33000810B5022000F018FBD8B3042065 -:1015E40000F014FB044670B11B48FEF77FFF042093 -:1015F40000F000FB022000F0FDFA4FF40040BDE8CB -:10160400104000F0F7BA1548FEF770FF082000F00C -:10161400F1FA102000F0EEFA202000F0EBFA40205E -:1016240000F0E8FA2046FFF72BFF022807D1082034 -:1016340000F0D4FA102000F0D1FA00F0A9FB042045 -:1016440000F0D8FA022000F0D5FA4FF40040BDE8CB -:10165400104000F0C3BA10BD193400084F3400081C -:1016640010B52548FEF742FF4FF40010FFF708FFBE -:1016740010B102283AD809E0202000F0BBFA40203B -:1016840000F0B8FA802000F0A9FA2FE0082000F05A -:10169400A5FA102000F0A2FA202000F0B7FA20B931 -:1016A4001648FEF723FF202007E0402000F0AEFAA2 -:1016B40058B91348FEF71AFF402000F08FFA4FF490 -:1016C400007000F08BFA00F063FB0E48FEF70EFF8B -:1016D400082000F08FFA102000F08CFA202000F08F -:1016E40089FA402000F086FA002400E00124042056 -:1016F40000F080FA204610BD6A34000884340008E3 -:10170400BD340008F634000808B50248FEF7EEFEC2 -:1017140000F045FB303500081EF0040F0CBFEFF35A -:101724000880EFF30980FFF7EFBF704708B5012089 -:10173400FEF79AFD08B900200FE00020FEF794FDA3 -:101744000028F8D00220FEF78FFD0028F3D00320F4 -:10175400FEF78AFD80F00100C0B200F0010008BD70 -:101764001FB504461048FEF7C1FE01A90C2220460D -:1017740000F070FB01A8FEF7B9FE2046FEF764FFF7 -:10178400FEF780FD094B20F0040018701C46FEF79C -:1017940079FD237800F0FB00834201D000F0F8FAD1 -:1017A4000A2000F04CFBF2E7263500085101012025 -:1017B40030B585B0FFF76EF94FF08050012100F08D -:1017C40019FF022000F0EAFD012807D1022000F0F1 -:1017D400EFFDA548FEF78AFE00F0E1FA00214FF084 -:1017E400805000F007FFFEF7A5FDA048FEF77EFE3F -:1017F4009F48FEF77BFE9F48FEF778FE9E48FEF763 -:1018040075FE9E48FEF772FE9848FEF76FFEFFF7DE -:10181400DBFB9B48FEF76AFEFFF7CAFC9948FEF71C -:1018240065FE00F0FDF99848FEF760FE00F024FA2A -:1018340000210C2201A801F074F800F019FA01A9A2 -:101844000C2200F007FB01A8FEF750FE8748FEF7C4 -:101854004DFE8648FEF74AFE4FF4804000F0D6F96C -:1018640090B18A48FEF742FE4FF4804000F0C2F97E -:10187400082000F0BFF9102000F0BCF9202000F08F -:10188400B9F9402000F0B6F9FFF7DAFC38B1804826 -:10189400FEF72CFE7F48FEF729FEFFF7FFFCFFF75B -:1018A40037F8FEF7FDFCFEF795FFFEF7B1FEFEF7F5 -:1018B4008BFC08B1784803E0FEF7CEFC10B1774802 -:1018C400FFF74EFF00F0C4F9802000F09FF918B133 -:1018D400802000F08FF949E04FF4003000F096F9D1 -:1018E40020B14FF4003000F085F95BE1FFF71EFFF3 -:1018F40068B16B48FEF7FAFD41F28834FFF716FF32 -:1019040078B1012000F09BFA013CF7D14AE1654B24 -:101914000CCB013301D0013203D16348FEF7E6FD5D -:1019240040E1FFF7A2FD18B3FFF79FFD10B15F4838 -:10193400FEF7DCFD4FF4007000F068F910B15C486C -:10194400FEF7D4FD4FF4007000F054F9102000F0BD -:101954005DF9D0B15748FEF7C9FD082000F04AF9F7 -:10196400102000F047F9FFF77BFEF8B95248A7E7CB -:101974004FF4007000F04AF90028D5D1082000F097 -:1019840039F9102000F036F90EE0082000F03EF995 -:1019940020B14A48FEF7AAFD102003E04848FEF7AC -:1019A400A5FD082000F01AF9FFF714FE4FF40050CB -:1019B40000F02CF904464FF4805000F027F9400061 -:1019C40040EA8400C4B24FF4006000F01FF92043E1 -:1019D400C0B20728C3B20DD14FF4006000F00AF979 -:1019E4004FF4805000F006F94FF4005000F002F973 -:1019F400344865E70133DBB2023B4FF40060052B4A -:101A040011D8DFE803F00C1003100C1000F0F2F80A -:101A14004FF4805000F0EEF84FF4005003E000F073 -:101A2400E9F84FF4805000F0D9F84FF4002000F0AA -:101A3400EDF8002851D04FF4002000F0DBF82248E4 -:101A4400FEF754FD0024FFF7EDFB002840D0042CE2 -:101A540003D11E48FEF74AFD00244FF4FA7000F04B -:101A6400EEF90134EFE700BF3B3500087C3700088E -:101A740048350008593500086A3500087B350008E8 -:101A84008C35000897350008A3350008AC350008EC -:101A9400D93500080A360008014550FE024550FEBB -:101AA400293600080040000864360008773600082C -:101AB4008F360008AF360008034550FED7360008BD -:101AC400FD360008044550FE233700083737000868 -:101AD4003648FEF70BFDFFF7D9FBFFF7A9FCFFF72C -:101AE400BDFCFEF7DFFF324B32485C681D68FEF731 -:101AF400E5FC2046FEF701FD2F48FEF7DFFC00223F -:101B0400930003F1604303F5614301324FF0FF3169 -:101B1400082AC3F88010C3F88011F1D1274B284A52 -:101B2400196B0A401A635A6B22F0C0025A639A6B0B -:101B340022F003029A63196C224A0A401A64596C0F -:101B4400214A0A405A6400F0CBFC2048012100F0ED -:101B540069FD1E48002100F065FDF120012100F01F -:101B64006DFDF120002100F069FD0120014600F027 -:101B740071FD0120002100F06DFD1548012100F0E8 -:101B840075FD1348002100F071FD1248012100F099 -:101B940079FD1048002100F075FD63B64FF0FF3E5B -:101BA400AD4620470C48FEF7A1FCDCE6523700089E -:101BB400004000086137000875370008003802400B -:101BC40000EF9FFF003619E8CC86E8FE0010E02203 -:101BD400FFC9FEF6337F77047D37000810B504464D -:101BE400002000F087FD40EA04010020BDE8104019 -:101BF40000F070BD10B50446002000F07BFD20EA23 -:101C040004010020BDE8104000F064BD10B5044696 -:101C1400002000F06FFD204214BF0120002010BD01 -:101C240008B501214FF0805000F0E4FC012000F0E1 -:101C3400A3FB4FF08050002100F0DCFC0120FFF7F3 -:101C4400E5FF20B90121BDE8084000F043BD08BD0F -:101C540008B50648FEF732FC002000F04BFDFEF705 -:101C64004CFCBDE80840FEF7B9BB00BF9C37000838 -:101C7400022000F03FBD000008B5FFF7F9FF04495A -:101C8400884204D00220BDE8084000F023BD08BD0E -:101C94008BB8185808B50248FEF728FC00F078F80D -:101CA400A83700081FB504460C2201A8FEF7B6FEAB -:101CB40001AB03CB20601868A0602046616004B0CB -:101CC40010BD0068A0F10C03584258417047000051 -:101CD40080B50646174610480D461C46FEF7EEFB37 -:101CE4003846FEF7EBFB0D48FEF7E8FB3046FEF7FF -:101CF400E5FB0B48FEF7E2FB2846FEF7FEFB2CB1A2 -:101D04000848FEF7DBFB2046FEF7D8FB0648FEF743 -:101D1400EDFBFFF7BFFF00BFC537000856350008CD -:101D2400CE370008573500087C3700081FB506AACF -:101D340052F8044B039200921A462346FFF7C8FF59 -:101D44000CB41FB506AA52F8043B03920092014A50 -:101D5400FFF7BEFFD037000807B500230093724693 -:101D6400014BFFF7E3FF00BFD7370008BFF34F8FE6 -:101D74000549064BCA6802F4E0621343CB60BFF323 -:101D84004F8F00BFFDE700BF00ED00E00400FA053F -:101D940008B5FEF7C5FC00F095FCFFF7E7FF08B5B2 -:101DA400FFF7E4FFF0B5064600240120254603466C -:101DB40094421DD011F804C000F1010EBCF1000FD3 -:101DC40003D17355774605460DE00133DBB2FF2B93 -:101DD400774606F800C007D102F1FF3C644506D0FF -:101DE4007355871C7546012301343846E0E7704675 -:101DF4007355F0BD30B5C9B1C0430A44914213D004 -:101E040011F8013B0A4D83EA000404F00F0455F86D -:101E1400244084EA101080EA131303F00F0355F8EA -:101E2400233083EA1010E9E7C04330BD084630BDD3 -:101E340070310008062358430138FDD1704710B5AE -:101E440004462CB14FF47A70FFF7F4FF013CF8E735 -:101E540010BD00000A2A10B504DC1148BDE810408A -:101E6400FEF744BB0C46302304F8023B78234B7046 -:101E74001C220F2393400340D340092B01D8303355 -:101E840002E00F2B02D85733DBB200E02023043AE0 -:101E940004F8013B131DECD100238B7210BD00BF6D -:101EA400233800081949D1F8883043F47003C1F885 -:101EB4008830174B1A68002042F001021A609860BB -:101EC4001A6822F0847222F480321A60114A5A602D -:101ED4001A6822F480221A60D8601A6C0E4842F004 -:101EE40080521A64026842F4404202609A689A601E -:101EF4009A689A609A6842F480529A604FF4C06279 -:101F0400C3F800244FF000638B60704700ED00E0DD -:101F1400003802401030002400700040C27803787A -:101F240010B512B3164AD1684278C943C1F30221ED -:101F3400C1F10404E4B2A240D4B20F220A41817870 -:101F44000A40224303F1604303F561431201D2B214 -:101F540083F8002303780122590903F01F0302FACE -:101F640003F3084A42F8213010BD5A09012103F055 -:101F74001F03994002F12003024A42F8231010BDC6 -:101F840000ED00E000E100E0044B9A6809B1104361 -:101F940001E022EA00009860704700BF002004E0DE -:101FA400044B1A69002ABFBF034A5A6002F18832FF -:101FB4005A607047003C024023016745024A136996 -:101FC40043F0004313617047003C0240014BD8606A -:101FD400704700BF003C02400E4BDA68D20310D4B5 -:101FE400DA68D1060FD4DA68D2050ED4DA6812F0B2 -:101FF400E00F0CD1DB6813F0020F14BF0820092096 -:102004007047012070470620704702207047072060 -:10201400704700BF003C024007B509238DF8073024 -:10202400FFF7DAFF8DF807009DF80730012BF7D092 -:102034009DF8070003B05DF804FB000070B5064688 -:1020440041B1012908D002290CBF4FF400754FF4A7 -:10205400407503E00D4601E04FF48075FFF7DCFFA7 -:1020640009281ED10F4C236923F44073236121698D -:1020740029432161236923F0F8032361236943F091 -:1020840002031E432661236943F480332361FFF76F -:10209400C3FF236923F002032361236923F0F803B8 -:1020A400236170BD003C024070B505460E46FFF743 -:1020B400B3FF092811D1094C236923F44073236128 -:1020C40023692361236943F0010323612E70FFF721 -:1020D400A3FF236923F00103236170BD003C024088 -:1020E400F0B50E6800220123934003EA060E9E45D4 -:1020F4002AD1550003230468AB40DB431C40046031 -:102104000C79076804FA05FC013C4CEA0707012C2A -:10211400076011D884684F791C4084608468AF409C -:1021240027438760446824EA0E0444608C79476836 -:102134009440A4B23C434460C4682340C360CB7958 -:10214400C468AB402343C3600132102ACBD1F0BD35 -:102154004FF6FF7303600023037143718371C371EE -:1021640070470369194214BF0120002070470AB167 -:10217400018370474183704701F00703C90800EBEE -:10218400810010B59B00046A0F21994024EA0101E3 -:102194000162016A9A401143016210BD08B5134BF4 -:1021A400984207D14FF40010012100F05FFA4FF478 -:1021B400001014E00E4B984207D14FF48000012127 -:1021C40000F054FA4FF4800009E00A4B98420BD116 -:1021D4004FF40000012100F049FA4FF400000021FF -:1021E400BDE8084000F042BA08BD00BF00540040FA -:1021F40000580040005C00407FB5868826F03F060A -:102204000446360468460D46360C00F085F9029AF9 -:102214002C48B2FBF0F081B20E43A68023882A4EEC -:1022240023F001031B041B0C23802B68B3420AD840 -:102234005B000131B2FBF3F39BB2032B89B298BF6D -:102244000423218423E0EE884BF6FF718E421BBFEA -:1022540019214B4303EB4303B2FBF3F315BF9BB2CA -:10226400B2FBF3F343F480439BB2C3F30B020AB90A -:1022740043F001034FF4967101FB00F24FF47A71BD -:1022840092FBF1F2013292B243F400432284A3831D -:1022940023886989AA889BB243F001032380238899 -:1022A40023F4816323F002031B040A431B0C13432E -:1022B4009BB223802A89AB8913439BB2238104B048 -:1022C40070BD00BF40420F00A086010041F2883378 -:1022D4000360002383804BF6FF72038143814FF434 -:1022E4008043C28083817047038819B19BB243F055 -:1022F400010303E023F001031B041B0C038070475C -:10230400038819B19BB243F4806303E023F4806330 -:102314001B041B0C03807047838A9AB2038B10B58D -:1023240042EA034321F07F4404EA0300431A58427B -:10233400584110BD014B1860704700BF0030004089 -:10234400014B5860704700BF00300040014B98605B -:10235400704700BF00300040024B4AF6AA221A60C0 -:10236400704700BF00300040024B4CF6CC421A606C -:10237400704700BF00300040014B1860704700BF39 -:1023840020000E4210B9034B19607047024B1960CC -:10239400704700BFA0000E429C000E42034B5B68D6 -:1023A400184214BF01200020704700BF0070004095 -:1023B400024A136843EA80001060704700700040CE -:1023C4000023036043608360C360036143618361EE -:1023D4007047002343608360C3600360036143610B -:1023E4008361C361036243628362C3627047000016 -:1023F4000E4903680A6810B544691C4383691C4389 -:1024040022F07F4323F030034268234343EA02630C -:102414000B60C36882684C681A43054B234013431E -:10242400026943EA02434B6010BD00BF001000A0E4 -:10243400FEF7E0FF90E80C001A4383680D491A4345 -:10244400C3681A4303691A4383691A43C3691A4365 -:10245400036A1A43436A1A43836A1A43C36A10B568 -:102464001A434C69044B23401343426943EA8243B1 -:102474004B6110BD001000A000008090044B1A684E -:1024840010B142F0010201E022F001021A6070472B -:10249400001000A0024B9A68920658BF98617047DA -:1024A400001000A0024B9A68920658BF186170474A -:1024B400001000A0014B1878704700BF201000A046 -:1024C400034B9B68184214BF01200020704700BFD3 -:1024D400001000A0014BD860704700BF001000A09E -:1024E4000B4B1A68002142F001021A6099601A68C5 -:1024F40022F0A85222F410221A60064A5A601A687E -:1025040022F480221A60D960C3F88C10704700BF8F -:1025140000380240103000242C4B9A6802F00C0260 -:1025240010B50C2A38D8DFE802F0373737370737C9 -:102534003737093737371E00254B2EE05968234AB1 -:102544005B6811F4800F03F03F03516814BF204A05 -:10255400204AB2FBF3F31D4A5268C1F388114B437E -:10256400C2F3014213E05968184A5B6811F4800F02 -:1025740003F03F03516814BF154A164AB2FBF3F344 -:10258400124A5268C1F388114B43C2F302720132FA -:102594005200B3FBF2F300E00E4B0C4A0360936865 -:1025A4000D49C3F30313CC5C0368E34043609468B0 -:1025B400C4F382240C5D23FA04F484609268C2F3A9 -:1025C40042328A5CD340C36010BD00BF0038024071 -:1025D40000127A000024F4001E000020044B9A6BC1 -:1025E40009B1104301E022EA00009863704700BF7C -:1025F40000380240044B1A6C09B1104301E022EA8E -:1026040000001864704700BF00380240044B5A6C45 -:1026140009B1104301E022EA00005864704700BF8A -:1026240000380240044B1A6909B1104301E022EA60 -:1026340000001861704700BF00380240044B5A691B -:1026440009B1104301E022EA00005861704700BF5D -:1026540000380240044B9A6909B1104301E022EAB0 -:1026640000009861704700BF00380240044B1A6AAA -:1026740009B1104301E022EA00001862704700BF6C -:1026840000380240044B5A6A09B1104301E022EABF -:1026940000005862704700BF003802404209012A16 -:1026A400074B01D11B6803E0022A0CBF1B6F5B6F51 -:1026B40000F01F0023FA00F000F00100704700BF93 -:1026C40000380240024A536F43F0807353677047E7 -:1026D4000038024082B000230193054B0193019B13 -:1026E40003EB80000190019B196002B0704700BFAA -:1026F4005028004082B000230193054B0193019BB5 -:1027040003EB80000190019B186802B0704700BF82 -:102714005028004008B5254B984207D14FF480500B -:102724000121FFF7AFFF4FF4805039E0204B98426E -:1027340007D14FF480400121FFF798FF4FF4804008 -:1027440009E01C4B98420BD14FF400400121FFF7E4 -:102754008DFF4FF400400021BDE80840FFF786BF1D -:10276400154B984207D14FF400500121FFF78AFF1F -:102774004FF4005014E0114B984207D14FF48010ED -:102784000121FFF77FFF4FF4801009E00C4B9842C2 -:102794000BD14FF400100121FFF774FF4FF4001028 -:1027A4000021BDE80840FFF76DBF08BD00300140BF -:1027B40000380040003C004000340140005001401B -:1027C4000054014003884A8810B503F441540B882F -:1027D40013438A881343CA8813430A8913434A89D3 -:1027E40013438A891343CA89134323439BB2038047 -:1027F400838B23F400631B041B0C83830B8A0382E7 -:1028040010BD038819B19BB243F0400303E023F0E9 -:1028140040031B041B0C03807047818170470389AC -:10282400194214BF01200020704700007FB5038ABD -:10283400CA889BB223F440531343038283890E4610 -:102844009BB223F4B053098923F00C02B3880B43E1 -:1028540071890B439BB213438381838AB2899BB2F0 -:1028640023F440731343838204466846FFF754FEFF -:102874001A4B9C4203D003F580639C4201D1039D13 -:1028840000E0029DA38931681BB2002B4FF01902AE -:1028940002FB05F2B4BF4D008D00B2FBF5F5A28931 -:1028A4006426B5FBF6F00001010912B2002A06FB0A -:1028B4001153ADBF1A01D90032313232B5BFB1FB69 -:1028C400F6F2B2FBF6F302F0070203F00F03B4BF13 -:1028D4001043184380B2208104B070BD0010014041 -:1028E400838919B19BB243F4005303E023F40053EA -:1028F4001B041B0C83817047C1F30801818070475E -:102904000388194214BF01200020704710B5431EEC -:102914000A44914204D011F8014B03F8014FF8E73F -:1029240010BD02440346934202D003F8011BFAE7A8 -:10293400704700001D32000800080240002000001B -:10294400020D000022320008000C024004000000C6 -:102954000302020025320008001C024001000000AE -:10296400070002002C320008001C02400200000094 -:1029740007010200000000000000000001424F4F68 -:10298400544C4F414445520000000000002A2A00E4 -:1029940000000000000000000000000000006000D3 -:1029A40000030018000000000000000060000003A5 -:1029B4000018000000000000000060000003001880 -:1029C4000000000000000000600000030018000088 -:1029D4000000000000006000000300180000000078 -:1029E400E003001F60F800C30718C0070000F80FD9 -:1029F400C07F60FE03F31F18F01F00000C1860C0B6 -:102A04006003061B30181830000006303080E101E6 -:102A14000C0F60180C60000003601800E300180736 -:102A2400C01806C0008001400C006200100380182A -:102A34000380008001C00C0066003003801903800D -:102A4400018001C0FCFF670030038019FFFF018093 -:102A540001C0FCFF630030038019FFFF008001C048 -:102A64000C006000300380190300008001C00C00DA -:102A74006000300380190300008001400C006200F4 -:102A8400100380180380008003601800C300180638 -:102A9400C01806C000800730308081010C0C60181B -:102AA4000C6000800D1860C0000306183038183020 -:102AB4000080F90FC07F00FE03F01F70F01F00803C -:102AC400E103001F00F800C00760C0070080010098 -:102AD4000000000000000000000000800100000071 -:102AE4000000000000000000008001000000000061 -:102AF4000000000000000080010000000000000051 -:102B04000000000000800100000000000000000040 -:102B1400000000000C00060000000060003000000F -:102B240018000300000000C00018000030800100FD -:102B340000000080010C000060C0000000000000E4 -:102B440003060000C060000000000000060300004F -:102B540080310000000000008C010000001B000018 -:102B640000000000D8000000000E0000000000007B -:102B740070000000000E0000000000007000000063 -:102B8400001B000000000000D8000000803100009D -:102B9400000000008C010000C06000000000000084 -:102BA4000603000060C000000000000003060000EF -:102BB4003080010000000080010C000018000300B8 -:102BC400000000C0001800000C00060000000060B7 -:102BD40000300000000000000000000000000000C1 -:102BE40000000000000000000000000000000000E1 -:102BF40000000000000000000000000000000000D1 -:102C040000000000000000000000000000000000C0 -:102C140000000000000000000000000000000000B0 -:102C240000000000000000000000000000000000A0 -:102C34000000000000000080FFFF01000000000011 -:102C4400000000FEFFFF7F00000000000000E07FA6 -:102C54000000FE07000000000000FE010000807F6D -:102C64000000000000801F00000000F801000000C8 -:102C740000F00100000000800F000000007C000054 -:102C8400000000003E000000000F000000000000F3 -:102C9400F00000008003000000000000C0010000FC -:102CA400E0000000000000000007000070000000C9 -:102CB40000000000000E00001800000000000000EA -:102CC400001800000C0000000000000000300000AC -:102CD4000600000000000000006000000300000087 -:102CE4000000000000C0008001000000000000009F -:102CF400008001C00000000000000000000003602C -:102D04000000000000000000000006300000000089 -:102D14000000000000000C10000000000000000093 -:102D24000000087C00FE00FF01C701C701C701C7FE -:102D340001C701C701C701C701C701C701C701C74F -:102D440001FF01FE007C0038003C003E003E0038DC -:102D540000380038003800380038003800380038AF -:102D64000038003800FE00FE00FE007C00FE00FF7C -:102D740001C701C701C001C001E000F00078003CB8 -:102D8400001E000E000F000700FF01FF01FF017C81 -:102D940000FE00FF01C701C701C001C001F80078AF -:102DA40000F800C001C001C001C701C701FF01FE56 -:102DB400007C00E000E000F000F000F800F800F80B -:102DC40000FC00EC00EE00E600FF01FF01FF01E063 -:102DD40000E000E000E000FF00FF00FF0007000744 -:102DE4000007007F00FF00FF01C701C001C001C749 -:102DF40001C701C701FF01FE007C007C00FE00FF4B -:102E040001C701C701070007007700FF00FF01C7E2 -:102E140001C701C701C701C701FF01FE007C00FF14 -:102E240001FF01FF01E000E0007000700070003855 -:102E3400003800380038001C001C001C001C001C5A -:102E4400001C007C00FE00FF01C701C701C701C7C9 -:102E540001FE007C00FE00C701C701C701C701C70E -:102E640001FF01FE007C007C00FE00FF01C701C7DA -:102E740001C701C701C701FF01FE01DC01C001C098 -:102E840001C701C701FF01FE007C00000000000033 -:102E94000000007C00FE00FF01C701C701F001FC37 -:102EA40001CE01C701C701E701FF01DF01CE010720 -:102EB40000070007000700E700F701FF01CF01C783 -:102EC40001C701C701C701C701C701CF01FF01F74E -:102ED40001E70000000000000000007C00FE00FF8D -:102EE40001C701C7010700070007000700C701C7A2 -:102EF40001FF01FE007C00C001C001C001C001CE81 -:102F040001DF01FF01E701C701C701C701C701C70D -:102F140001C701E701FF01DF01CE0100000000004D -:102F24000000007C00FE00FF01C701C701C701FFCC -:102F340001FF010700C701C701FF01FE007C00E09B -:102F440000F000F8003800FE00FE00FE00380038F3 -:102F540000380038003800380038003800380038AD -:102F6400003800000006830100000000000000009B -:102F7400000000068301000000000C0000000000B7 -:102F840000068301000000000C00000000D8783621 -:102F94009B79C0E3D90C8C67DB3C1BF8FD7EBFFD3D -:102FA400E0F7FB1FC6EFFB7E1F98CD66B3CD6036FE -:102FB4009B19C66C18660398FD66B3FD60309B19B7 -:102FC400C66F18660398FD66B3FD60309B19C36F26 -:102FD400186603980D66B30D60369B19C3601866B6 -:102FE40003F8FD7EBFFDECF79B19C36F187E03D871 -:102FF40078369B79CCE399998167183C03180000D3 -:103004000000000000800100000000180000000023 -:103014000000000000000000001800000000000094 -:1030240000000000000000FFFFFFFFFFFFFFFFFFA5 -:10303400FFFFFF010000000000000000000080010D -:1030440000000000000000000000800100000000FB -:1030540000000000000080010000000000000000EB -:10306400000080010000000000000000000080015A -:103074000000000000000000000080FFFFFFFFFFD1 -:10308400FFFFFFFFFFFFFF000004024040000000BD -:1030940006000A00000402400400000002000900C7 -:1030A4000008024000020000090009000008024074 -:1030B400000400000A0009000008024000010000AA -:1030C400080009000000024002000000010009009D -:1030D400005C004000000240000100000800040001 -:1030E4000004024000010000080009000000800004 -:1030F400801A06000040000048490000000000005B -:1031040000000000010003008200070084000700A3 -:1031140001000300020003004000030080000700D8 -:103124008400070000000008004000080080000838 -:1031340000C00008000001080000020800000408A4 -:10314400000006080000080800000A0800000C0837 -:1031540000000E08000008001000180020002800DD -:1031640030003800400048005000580000000000C3 -:103174006410B71DC8206E3BAC30D9269041DC7674 -:10318400F4516B6B5861B24D3C7105502083B8ED1E -:1031940044930FF0E8A3D6D68CB361CBB0C2649B42 -:1031A400D4D2D38678E20AA01CF2BDBD53747563F1 -:1031B4006B20627574746F6E2072656769737465D1 -:1031C4007220697320696E76616C69642C20636C6B -:1031D400656172696E672E00427574746F6E206942 -:1031E400642000697320737475636B2100427574E5 -:1031F400746F6E2077617320707573686564206FD7 -:103204006E20626F6F742E20427574746F6E20632B -:103214006F756E7465723A20004261636B0055707D -:103224000053656C65637400446F776E002E2E2F17 -:103234007372632F647269766572732F6932632EB9 -:1032440063004932432064657669636520494420FC -:103254006F7574206F6620626F756E6473202564C9 -:1032640020286D61783A20256429006F6E002E2E87 -:103274002F7372632F647269766572732F706D6930 -:10328400632F617333373031622E63004661696C9A -:10329400656420746F20737461727420504D4943C7 -:1032A40020313230487A2050574D00206973206F06 -:1032B4007574736964652073797374656D20666CC5 -:1032C400617368006661696C656420746F2065725F -:1032D40061736520736563746F7220006661696C45 -:1032E400656420746F207772697465206164647208 -:1032F4006573732000496E76616C69642066697237 -:103304006D77617265206465736372697074696F47 -:103314006E2100436865636B73756D6D696E67201C -:103324006669726D77617265207570646174650099 -:1033340043616C63756C6174656420636865636B79 -:1033440073756D3A2000496E76616C696420666914 -:10335400726D776172652043524320696E20535029 -:103364004920666C617368210065726173655F6FE3 -:103374006C645F6669726D776172650077726974F7 -:10338400655F6E65775F6669726D77617265005718 -:1033940065277265206465616400436865636B73C7 -:1033A400756D6D696E6720002062797465730D0A0E -:1033B40000436865636B73756D202D2077616E74AF -:1033C4006564200020676F7420004F757220696E59 -:1033D4007465726E616C20666C61736820636F6ED5 -:1033E40074656E7473206172652062616420286361 -:1033F4006865636B73756D206661696C656429210A -:103404002054686973206973207265616C6C79203B -:1034140062616421004F75722070726576696F7500 -:1034240073206669726D776172652075706461746A -:1034340065206661696C65642C2061626F727469D1 -:103444006E67207570646174652E004E6577206622 -:1034540069726D7761726520697320617661696C48 -:1034640061626C6521004C6F6164696E67207265EE -:10347400636F76657279206669726D776172650033 -:103484004661696C656420746F206C6F616420729E -:1034940065636F76657279206669726D77617265AE -:1034A4002C20737472696B65206F6E652E205472C4 -:1034B4007920616761696E2E004661696C656420DC -:1034C400746F206C6F6164207265636F76657279C6 -:1034D400206669726D776172652C20737472696BF2 -:1034E400652074776F2E2054727920616761696E4C -:1034F4002E004661696C656420746F206C6F616492 -:10350400207265636F76657279206669726D776182 -:1035140072652C20737472696B65207468726565BA -:103524002E205341442057415443480048415244BB -:10353400204641554C540065786974207374616E5B -:1035440064627900205F5F5F20205F20205F20207D -:103554005F202020002F205F5F3E3C5F3E7C207C6C -:103564007C207C5F5F005C5F5F205C7C207C7C2037 -:103574007C7C202F202F003C5F5F5F2F7C5F7C7C56 -:103584005F7C7C5F5C5F5C0069326320696E697498 -:10359400656400706D696320696E697465640062B6 -:1035A4006F6F7420626974004C6173742066697271 -:1035B4006D7761726520626F6F7420776173207319 -:1035C4007461626C653B20636C6561722073747214 -:1035D400696B657300504D494320776F6B652066B6 -:1035E400726F6D207374616E6462792064756520F6 -:1035F400746F206368617267657220646973636FB6 -:103604006E6E6563740050757474696E6720504DF6 -:103614004943206261636B20696E746F2073746127 -:103624006E64627900486F6C6420646F776E205515 -:1036340050202B204241434B202B2053454C4543E3 -:103644005420666F72203520736563732E20746F67 -:1036540020666F7263652D626F6F7420505246004E -:103664004669726D77617265206973206572617352 -:103674006564005761746368646F67206361757380 -:103684006564206120726573657400536F66747796 -:10369400617265206661696C75726520636175731A -:1036A40065642061207265736574004661696C65A8 -:1036B4006420746F207374617274206669726D770C -:1036C4006172652C20737472696B6520746872650D -:1036D400652E004661696C656420746F20737461A3 -:1036E4007274206669726D776172652C20737472CE -:1036F400696B652074776F2E004661696C65642080 -:10370400746F207374617274206669726D7761726C -:10371400652C20737472696B65206F6E652E00537F -:10372400687574646F776E207265717565737465FE -:10373400642E0052656D6F766520636861726765FB -:103744007220746F2073687574646F776E005368A9 -:10375400757474696E6720646F776E2E00426F6FA4 -:1037640074696E67206669726D77617265204020A6 -:10377400002E2E2E0D0A0D0A00466F7263652D620F -:103784006F6F74696E67207265636F7665727920F6 -:103794006D6F64652E2E2E00426F6F742062697403 -:1037A400733A2000536F66747761726520666169AD -:1037B4006C7572653B20726573657474696E6721FC -:1037C400004153534552543A20003A004153534563 -:1037D4005254004153534552544E002A2A2A20572A -:1037E4005446200053544D33320053544D33322049 -:1037F4007065726970686572616C206C696272616F -:103804007279207472697070656420616E206173CE -:10381400736572740043524F414B204F4F4D006902 -:10382400746F612062756666657220746F6F2073B1 -:083834006D616C6C00000000E6 -:10383C00FFFFFFFF008400000202000000C0040133 -:10384C000000000000000002100000000700000053 -:10385C00000001020304010203040607080900002A -:04000005080001846A -:00000001FF diff --git a/bin/boot/boot_snowy_bb@1478015115.hex b/bin/boot/boot_snowy_bb@1478015115.hex deleted file mode 100644 index cdc2a70cd4..0000000000 --- a/bin/boot/boot_snowy_bb@1478015115.hex +++ /dev/null @@ -1,874 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008B51A0008A8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0EBFF01F09BFC704707 -:1001E4004436000800000020140000201400002001 -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040021FC002401902546E0B200F049FC01AA3B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F08DFF02A800F0FA -:100234002DFE0120002102F0F5FB00201AE001331D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F071FF02A800F011FE0120214602F006 -:10027400D9FB28460BB030BD482E0008742E000868 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0B1F9003018BF8B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F0AAF991 -:1002C40020460E21052208AE02F0A4F9052220469C -:1002D4000C2102F09FF94FF4005346F81C3D002511 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F03FF94FF417 -:10030400804320463146019302F038F94FF480537C -:100314002046314601934FF4007802F02FF920462D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340025F94FF480632046314601938DF8085027 -:100344008DF80B5002F01AF94FF4807320463146B1 -:1003540001938DF808708DF80B5002F00FF94FF4EB -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F004F939464FF4001002F0F7FAA4F53C -:100384004444164B164E1E60204602F06BFB03A835 -:1003940002F0DFFB4FF4827339463046ADF80E307D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F0A6FB2046394602F0CDFBE8 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F097FE012000F07B -:1003F40045FB022001F091FE0120BDE8084000F019 -:100404002BBB000010B504460548022102F0BEFBD8 -:100414000028F9D021460248BDE8104002F0B4BBE0 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0EAF8012001F06FFE25484FF4C4 -:100444000041002202F0E1F84FF4807100222148BB -:1004540002F0DBF8012001F060FE4FF400411D487A -:10046400012202F0D2F8012001F057FE19484FF49E -:10047400007102F0C4F818B11748184A6A210AE05A -:1004840014484FF4004102F0BAF808B1254404E0DE -:100494001148134A6B2101F0B3FDAC4204D014F8A7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F0ABF808240020FFF7A4FF013CFAD1B6 -:1004C4004FF40071034802F09AF8003018BF01207D -:1004D40038BD00BF001802408D2E0008972E00087A -:1004E400B22E000810B504460B4802F04AFB0A4835 -:1004F400022102F04BFB0028F9D00748214602F004 -:1005040043FB0548012102F041FB0028F9D00248D1 -:1005140002F037FBC0B210BD0054014010B54FF4D7 -:10052400807104460022054802F06FF8642001F04F -:10053400EFFD2046BDE81040FFF764BF00180240FD -:1005440008B50748802102F021FB0028F9D10548AD -:100554004FF480710122BDE8084002F056B800BF94 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0C4FDF1E7012010BD00BFDF -:10059400CF2E000810B5224C638822884FF6FF71D5 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F019F84FF4004100221548FD -:1005E40002F013F8012001F098FD12484FF4004185 -:1005F400012202F00AF840F2E9340E484FF4007187 -:1006040001F0FDFF0028D9D1013C04D10A4800F0D3 -:100614003DFC204610BD642001F07AFDEDE700BFEB -:1006240000000F60F22E000804000F601E2F000867 -:100634003A2F000800180240502F000810B5044655 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0B2FF642001F032FD0020FFF782 -:1006B40019FF4FF48071012204461A4801F0A5FF86 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF6C2F00087D -:100724009B2F000800180240C4350008AA2F0008B7 -:10073400C52F0008CF2F000808B55D235843094B87 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F074BAD5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0E1FE3A4804210122A5 -:1007C40001F023FF00240223029435488DF80830F9 -:1007D400072103230C228DF80930019401F01AFF3C -:1007E40080232F480193294601F0C8FE022C04D02F -:1007F4002C48A1B20C2201F00DFF0134102CF5D1CC -:100804004FF6FB7327480193294601F0B7FE0226F1 -:100814003146013624480C22B6B201F0FBFE102EFC -:10082400F6D14FF6FC7329461F48019301F0A6FE4A -:10083400042100221B481E4D01F0E7FE0A2001F0AE -:1008440067FC01220421174801F0DFFE1E2001F09D -:100854005FFC0120014602F071F80FCD03AC0FC418 -:1008640095E8070084E80700402200210AA802F066 -:100874000EFA02230B9308230C934FF48053129324 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400FBFD0020012101F061FE1AB070BD00BF14 -:1008A40000040240000C024000100240A42C000886 -:1008B400AC2C00080D4B98221A80A3F68A231B88BF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F086FBE7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0D9FB11E05AB2012A0CDCEF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0DAFD1A480621042201F0EF -:1009D40021FE04220921174801F01CFE154800F0ED -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0BBFD0C4800F072FA042000F02E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0CEFD20464D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400BCFD2046BDE8384000F02ABA00140240EC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F0A3FD0446284600F018FACF -:100AC400B4FA84F0400938BDC82C000838B50024B5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F03BFF04F140076568284600F001 -:100B0400DFF9684601F072FD02238DF80530A37BFE -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F02AFD284600F0E1F9BC42CB -:100B3400E3D14FF48040314601F018FF03B0F0BD1B -:100B4400C82C000808B5054B1B781BB90448322192 -:100B540001F062FABDE8084001F0D6BB1A0000209B -:100B640002300008124B1B7870B505460C461BB9C1 -:100B74001048382101F050FA032906D98E0831466D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F0AABBBDE8704001F0BABBDB -:100BB4001A00002002300008024B1A780AB90122F8 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F020FA01214FF4805059706D -:100BE40001F0A0FEBDE8084001F082BB1A0000201D -:100BF4000230000808B5074B1A781AB90648512183 -:100C040001F00AFA002159704FF48050BDE8084001 -:100C140001F088BE1A0000200230000870B50C46AE -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0E7FF002864 -:100CA400F9D00648214601F0DDFF0448402101F057 -:100CB400DDFF0028F9D010BD1C00002000480040D2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F0A7FF31464A46002081 -:100D340001F0CEF907F1080304F5927443F8040DA9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64008FF904465520FFF791FF6E466C44A64266 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020102D00082400002072 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0CEFD20460A21072201F006 -:100DC40029FC06AE072220460B2101F023FC4FF438 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0C3FB4FF40063204631460093A5 -:100E04008DF8048001F0BAFBADF80C50ADF80E502B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F0B6FE284639466B -:100E340001F00CFF204600F05BF8054B1F7006B074 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4004AF901A8FFF7CEFF05B05DF804FB000076 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F006FB4C -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0C5F832 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BD302D000808 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0CFFA2046C8 -:100FE40031462A4601F016FB2046FFF781FF03B085 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0CCFB00287BD0BF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BD302D0008C7 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0FCFB684601F076 -:1011640003FB0F4A236A009393424FF48063ADF864 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F085FA2859012101F02D -:10119400F9FA04B070BD00BF30010020302D000802 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0DAF92046B1B22A4601F051 -:1011D4001CFA2046FFF78CFE02B070BD10B5094C16 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BD302D00081C23104A434370B5D4 -:1012140005460F4CD058D61801F00EFA342305FBBE -:1012240003440021E06901F095FB337923B9284692 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400302D00082DE9F347504C9A4694F8383065 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0CEFE494B1844CF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F06BFA2AE03046FFF79DFF30461A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0E1FEDBE719688B8823F4DB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A40008300008302D000880841E00017D036889 -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F002FF06 -:1014840073788DF8043001A800F0FCFE54F8040CC5 -:1014940001F0D2F8454504F11C0406F1340601D0EC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020582D000830010020DF -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F0A0FD042807D90522009240 -:1014E4000D480E4A40F2351100F08AFD0C4A1344AF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020083000080E30000811 -:10152400302D0008104A08B592F838301BB90F481E -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F06AFD0A4B18441C2390F8880003FB42 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD3001002008300008D4 -:10157400302D000837B50D4614460093069B0193A1 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F047F90DB97A -:10161400029805E0012D01D1039801E000F006FDD8 -:10162400B4EB500F0ED8B0FBF4F000F080FD0028AE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0EAFC01200138C00080B205B030BDD2 -:101654003A300008B0F1006F10B504460BD20C48C4 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:10169400B230000840300008C02D00082DE9F8439E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F02AFE2C464C451FDCF32000F03AFE95 -:10170400154B002133F8140000F06CFE09280BD0AF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F01FFE0DE02FB1C5F10100204410 -:1017340031464246B8470134DDE700F013FE01208C -:10174400BDE8F8830020BDE8F88300BF5B300008E3 -:101754006F30000872300008F02D00087630000861 -:10176400C43500082DE9F0470646234814460D46C3 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400DBFDF32000F0EEFD4FEAD41805EB04095D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F04DFE09280DD01248FFF74AFB2B -:1017C4003046FFF771FB1048FFF760FB00F0CAFDDD -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0BAFD42 -:1017F4000120BDE8F08700BF8E3000086F3000087C -:1018040072300008A2300008C435000808B545F25B -:10181400555000F0DDFF042000F0E0FF40F6FF70BB -:1018240000F0E2FF002000F0D3FF4FF480500121CC -:10183400BDE8084000F07CBD08B500F0E3FFBDE85A -:10184400084000F0D7BF08B57D2001F0D7F800307C -:1018540018BF012008BD4900FEF76EBF4B0803EB1B -:101864005000FEF769BF000070B586B00546FFF76B -:10187400A3F901210020FEF75FFF042000F020FB04 -:1018840003A8294600F06EFB03A800F07AFB08B910 -:1018940028480BE02848FFF7F9FA05F10C000499F1 -:1018A400FFF7D1F9059B984204D02448FFF7EEFADC -:1018B40001203DE0049C2248FFF7E8FA2146214A32 -:1018C40021480023FFF7EAFE049E2048FFF7DEFAD2 -:1018D400002405F1C0411E4B009432460C311A48D5 -:1018E400FFF740FF1B48FFF7B5FA0498FFF7DCFA4F -:1018F4001948FFF7AFFA04991348FFF78FF9054623 -:101904001648FFF7A7FA0598FFF7CEFA1448FFF731 -:10191400A1FA2846FFF7C8FA1248FFF7B7FA059B61 -:101924009D4204D01048FFF7B1FA022000E020469F -:1019340006B070BDBB300008D9300008F63000088E -:10194400193100085B180008004000082C31000819 -:10195400611800083F3100084D3100085631000875 -:1019640069310008C43500086F31000808B5022049 -:1019740000F0BEFAD8B3042000F0BAFA70B11C48E3 -:10198400FFF784FA042000F0A7FA022000F0A4FA7A -:101994004FF40040BDE8084000F09EBA1548FFF738 -:1019A40075FA082000F098FA102000F095FA20202B -:1019B40000F092FA402000F08FFA4FF48010FFF705 -:1019C40053FF022807D1082000F07AFA102000F013 -:1019D40077FA00F04DFB042000F07EFA022000F0BC -:1019E4007BFA4FF40040BDE8084000F069BA08BD36 -:1019F400BA310008F031000810B52548FFF746FA5F -:101A04004FF40010FFF730FF10B102283AD809E074 -:101A1400202000F061FA402000F05EFA802000F0FF -:101A24004FFA2FE0082000F04BFA102000F048FA9B -:101A3400202000F05DFA20B91648FFF727FA20208D -:101A440007E0402000F054FA58B91348FFF71EFA93 -:101A5400402000F035FA4FF4007000F031FA00F045 -:101A640007FB0E48FFF712FA082000F035FA1020A1 -:101A740000F032FA202000F02FFA402000F02CFA77 -:101A8400002400E00124042000F026FA204610BDC2 -:101A94000B320008253200085E3200089732000835 -:101AA40008B50248FFF7F2F900F0E7FAD13200086E -:101AB4001EF0040F0CBFEFF30880EFF30980FFF76B -:101AC400EFBF70471FB504461048FFF7DFF901A9BF -:101AD4000C22204600F037FB01A8FFF7D7F9204677 -:101AE400FEF742FEFEF7F2FF094B20F004001870E7 -:101AF4001C46FEF7EBFF237800F0FB00834201D085 -:101B040000F0B6FA0A2000F008FBF2E7C73200083A -:101B1400690100207FB54FF08050012100F01AFFC9 -:101B2400022000F07DFE012807D1022000F082FE91 -:101B3400BC48FFF7ABF900F0A0FA00214FF0805049 -:101B440000F008FFFFF72CF9B748FFF79FF9B748F3 -:101B5400FFF79CF9B648FFF799F9B648FFF796F9ED -:101B6400B548FFF793F9B548FFF790F9B448FFF784 -:101B74008DF9B448FFF78AF9B348FFF787F9B348FA -:101B8400FFF784F9FFF758FCFEF708FF00F0BAF9F5 -:101B940000F0D0F900210C2201A801F078F800F03F -:101BA400C5F901A90C2200F0CEFA01A8FFF76EF9DD -:101BB4009D48FFF76BF99C48FFF768F94FF48040A4 -:101BC40000F096F990B1A248FFF760F94FF4804015 -:101BD40000F082F9082000F07FF9102000F07CF971 -:101BE400202000F079F9402000F076F9FEF7D0FDCE -:101BF400FEF77AFFFEF746FDFEF7FEFA08B19548B8 -:101C040003E0FEF73DFB10B19348FFF75BFF00F0E4 -:101C14008CF9802000F06CF918B1802000F05CF998 -:101C240056E04FF4003000F063F9044620B14FF45D -:101C3400003000F051F94DE00120FEF731FFB0B162 -:101C44002046FEF72DFF90B18448FFF71FF941F2BB -:101C540088340120FEF724FF98B10020FEF720FF0E -:101C640078B1012000F059FA013CF2D132E07C4B0A -:101C74000CCB013301D0013203D17A48FFF706F9C6 -:101C840028E0FFF7E0FD48B3FFF7DDFD10B176482B -:101C9400FFF7FCF84FF4007000F02AF910B1734814 -:101CA400FFF7F4F84FF4007000F016F9102000F07C -:101CB4001FF9002800F0F0806D48FFF7E7F80820CE -:101CC40000F00AF9102000F007F9FFF795FE98B923 -:101CD40068489AE76848FFF7D9F8F6E74FF40070C8 -:101CE40000F006F90028CFD1082000F0F5F8102004 -:101CF40000F0F2F8FFF73AFE4FF4005000F0F8F865 -:101D040004464FF4805000F0F3F8400040EA8400A9 -:101D1400C4B24FF4006000F0EBF82043C0B20728CF -:101D2400C3B20DD14FF4006000F0D6F84FF48050E8 -:101D340000F0D2F84FF4005000F0CEF84F4864E7BA -:101D44000133DBB2023B4FF40060052B11D8DFE80E -:101D540003F00C1003100C1000F0BEF84FF4805088 -:101D640000F0BAF84FF4005003E000F0B5F84FF477 -:101D7400805000F0A5F8FFF749FDFFF75DFD384BF3 -:101D84003F485D681E68FFF765F82846FFF78CF842 -:101D94003C48FFF75FF80022930003F1604303F52A -:101DA400614301324FF0FF31082AC3F88010C3F8B1 -:101DB4008011F1D1344B4FF4801200241A635C6318 -:101DC4009C631C645C6400F045FD3048012100F014 -:101DD400D9FD21462D4800F0D5FDF120012100F068 -:101DE400DDFD2146F12000F0D9FD0120014600F07F -:101DF400E1FD2146012000F0DDFD2548012100F030 -:101E0400E5FD2146224800F0E1FD2248012100F0D1 -:101E1400E9FD2048214600F0E5FD63B64FF0FF3EA2 -:101E2400B5462847DC320008C4350008E93200080A -:101E340000330008193300084733000876330008DC -:101E4400A6330008D7330008093400083B340008DF -:101E54006C340008014550FE024550FE99340008D8 -:101E640000400008CB340008DE340008F6340008D3 -:101E740016350008034550FE8A350008044550FE17 -:101E8400A9350008BD350008003802400010E022E2 -:101E9400FFC9FEF6337F7704082000F029F820B14B -:101EA4000548FEF7F3FF102003E00448FEF7EEFFB9 -:101EB400082000F005F81DE73E35000864350008E9 -:101EC40010B50446002000F0BDFD40EA04010020E6 -:101ED400BDE8104000F0A6BD10B50446002000F097 -:101EE400B1FD20EA04010020BDE8104000F09ABDD5 -:101EF40010B50446002000F0A5FD204214BF0120C7 -:101F0400002010BD08B54FF08050012100F022FDE3 -:101F1400012000F07FFC0120FFF7EAFF20B9012136 -:101F2400BDE8084000F07EBD08BD7047022000F007 -:101F340089BD000008B5FFF7F9FF054B1968884211 -:101F440004D00220BDE8084000F06CBD08BD00BF0D -:101F54008401000808B50248FEF798FF00F088F8ED -:101F6400C53500081FB504460C2201A8FEF70CFC79 -:101F740001AB03CB20601868A0602046616004B008 -:101F840010BD0068A0F10C0358425841704700008E -:101F940080B50646174610480D461C46FEF75AFF04 -:101FA4003846FEF757FF0D48FEF754FF3046FEF75C -:101FB40051FF0B48FEF74EFF2846FEF775FF2CB184 -:101FC4000848FEF747FF2046FEF744FF0648FEF7A1 -:101FD4005DFFFFF7BFFF00BFE2350008EB350008E7 -:101FE400EE350008EC350008C43500081FB506AA14 -:101FF40052F8044B039200921A462346FFF7C8FF97 -:102004000CB41FB506AA52F8043B03920092014A8D -:10201400FFF7BEFFF035000807B5002300937246B2 -:10202400014BFFF7E3FF00BFF7350008744608B51E -:102034000548FEF70FFF2046FEF736FF0348FEF77C -:1020440025FFFFF787FF00BFFF350008C4350008F0 -:10205400BFF34F8F0449054BCA6802F4E06213438F -:10206400CB60BFF34F8FFEE700ED00E00400FA05FC -:1020740008B5FEF781FBFFF7EBFF08B5FFF7E8FFB4 -:10208400F0B50646002401202546034694421DD09F -:1020940011F804C000F1010EBCF1000F03D1735517 -:1020A400774605460DE00133DBB2FF2B774606F891 -:1020B40000C007D102F1FF3C644506D07355871C6C -:1020C4007546012301343846E0E770467355F0BD88 -:1020D40030B5C9B1C0430A44914213D011F8013B51 -:1020E4000A4D83EA000404F00F0455F8244084EAFE -:1020F400101080EA131303F00F0355F8233083EA1A -:102104001010E9E7C04330BD084630BD082E000872 -:10211400162358430138FDD1704710B504462CB13D -:102124004FF47A70FFF7F4FF013CF8E710BD10B5E7 -:10213400B0FA80F400F027F801280CBFC4F11F00A6 -:10214400C4F1200010BD0A2A10B51BDD0C46302353 -:1021540004F8023B78234B701C220F239340034066 -:10216400D340092B01D8303302E00F2B02D8573368 -:10217400DBB200E02023043A04F8013B131DECD148 -:1021840000238B7210BD00F0AA33A0EB530000F0C3 -:10219400CC3300F0333000EB930000EB101000F070 -:1021A4000F3000EB102000EB104000F03F007047B0 -:1021B4002D4AD2F8883043F47003C2F888302B4B90 -:1021C4001A68002142F001021A6099601A6822F02C -:1021D400847222F480321A60254A5A601A6822F402 -:1021E40080221A60D9601A6C224942F080521A6423 -:1021F4000A6842F440420A609A689A609A689A604F -:102204009A6842F480529A601B4A5A601A6842F0F3 -:1022140080721A601968154A8901FBD5174B40F280 -:1022240003611960936823F003039360936843F098 -:10223400020393600D4B9A6802F00C02082AF9D14C -:102244009A6822F400029A600D4AC3F884201A683E -:1022540042F080621A60054B1B681B01FBD5024BE0 -:102264004FF000629A60704700ED00E000380240D1 -:1022740010300024007000401040010F003C024068 -:1022840000300050C278037810B512B3164AD168F2 -:102294004278C943C1F30221C1F10404E4B2A2406B -:1022A400D4B20F220A4181780A40224303F16043E9 -:1022B40003F561431201D2B283F8002303780122AB -:1022C400590903F01F0302FA03F3084A42F82130C4 -:1022D40010BD5A09012103F01F03994002F12003A4 -:1022E400024A42F8231010BD00ED00E000E100E0D6 -:1022F400014B01229A60704700300240014B186084 -:10230400186870470030024000EB81018842044B9A -:1023140003D050F8042B1A60F8E71868704700BF20 -:1023240000300240014B1868704700BF0030024083 -:10233400044B9A6809B1104301E022EA0000986056 -:10234400704700BF002004E0044B1A69002ABFBF95 -:10235400034A5A6002F188325A607047003C0240D6 -:1023640023016745024A136943F000431361704730 -:10237400003C0240014BD860704700BF003C024063 -:102384000E4BDA68D20310D4DA68D1060FD4DA68B7 -:10239400D2050ED4DA6812F0EF0F0CD1DB6813F01B -:1023A400020F14BF0820092070470120704706203F -:1023B4007047022070470720704700BF003C02406E -:1023C40007B509238DF80730FFF7DAFF8DF807000A -:1023D4009DF80730012BF7D09DF8070003B05DF896 -:1023E40004FB000070B5064641B1012908D002295A -:1023F4000CBF4FF400754FF4407503E00D4601E047 -:102404004FF48075FFF7DCFF09281ED10F4C2369B8 -:1024140023F440732361216929432161236923F053 -:10242400F8032361236943F002031E4326612369F1 -:1024340043F480332361FFF7C3FF236923F00203CE -:102444002361236923F0F803236170BD003C02403B -:1024540070B505460E46FFF7B3FF092811D1094CA4 -:10246400236923F44073236123692361236943F0BF -:10247400010323612E70FFF7A3FF236923F00103F7 -:10248400236170BD003C024070B543688668856A6C -:102494000468416B46EA0302C3681A4303691A439A -:1024A40043691A4383691A43C3691A43036A1A4383 -:1024B400436A1343C26A2B431A43036B1343A200B8 -:1024C40002F120420B43082E136002BF136843F04D -:1024D40040031360B1F5801F816B12D18CB14FF0B2 -:1024E40020435C681E6846F480161E601E6846F42D -:1024F40080761E600E6924F47004013E44EA06549A -:102504005C608C690B6823434C6843EA04138C6851 -:1025140043EA0423CC6843EA04430C69496943EA67 -:10252400045343EA0163B5F5804F536010D1C16B86 -:1025340088690B680343486843EA0013886843EAE0 -:1025440000230869496943EA005343EA016301E04F -:102554006FF07043C2F8043170BD0000800000F1D8 -:10256400204019B1036843F0010302E00268024B02 -:1025740013400360704700BFFEFF0F00F0B50E6804 -:1025840000220123934003EA060E9E452AD15500FA -:1025940003230468AB40DB431C4004600C790768E8 -:1025A40004FA05FC013C4CEA0707012C076011D82A -:1025B40084684F791C4084608468AF4027438760F7 -:1025C400446824EA0E0444608C7947689440A4B2B9 -:1025D4003C434460C4682340C360CB79C468AB40C7 -:1025E4002343C3600132102ACBD1F0BD4FF6FF73F1 -:1025F40003600023037143718371C37170470369DE -:10260400194214BF0120002070470AB101837047AA -:102614004183704701F00703C90800EB810010B53E -:102624009B00046A0F21994024EA01010162016AB6 -:102634009A401143016210BD08B5134B984207D16B -:102644004FF40010012100F0C1F94FF4001014E020 -:102654000E4B984207D14FF48000012100F0B6F9E7 -:102664004FF4800009E00A4B98420BD14FF400006C -:10267400012100F0ABF94FF400000021BDE808404F -:1026840000F0A4B908BD00BF005400400058004049 -:10269400005C00407FB5868826F03F060446360479 -:1026A40068460D46360C00F0F9F8029A2C48B2FB45 -:1026B400F0F081B20E43A68023882A4E23F0010352 -:1026C4001B041B0C23802B68B3420AD85B00013126 -:1026D400B2FBF3F39BB2032B89B298BF042321848A -:1026E40023E0EE884BF6FF718E421BBF19214B434A -:1026F40003EB4303B2FBF3F315BF9BB2B2FBF3F35B -:1027040043F480439BB2C3F30B020AB943F00103C1 -:102714004FF4967101FB00F24FF47A7192FBF1F2DF -:10272400013292B243F400432284A383238869894B -:10273400AA889BB243F001032380238823F4816396 -:1027440023F002031B040A431B0C13439BB2238094 -:102754002A89AB8913439BB2238104B070BD00BFA7 -:1027640040420F00A086010041F288330360002339 -:1027740083804BF6FF72038143814FF48043C28010 -:1027840083817047038819B19BB243F0010303E0CE -:1027940023F001031B041B0C03807047038819B149 -:1027A4009BB243F4806303E023F480631B041B0C9B -:1027B40003807047838A9AB2038B10B542EA0343BD -:1027C40021F07F4404EA0300431A5842584110BDE3 -:1027D400014B1860704700BF00300040014B586047 -:1027E400704700BF00300040014B9860704700BF45 -:1027F40000300040024B4AF6AA221A60704700BF1C -:1028040000300040024B4CF6CC421A60704700BFC7 -:1028140000300040014B1860704700BF20000E429A -:10282400034B5B68184214BF01200020704700BFAF -:1028340000700040064BB0F5402F15BF1A685A6867 -:1028440042EA800042F4402214BF18605A60704784 -:10285400007000400E4B1A68002142F001021A6019 -:1028640099601A6822F0A85222F410221A60094AC8 -:102874005A60094AC3F8842002F18062C3F88820B0 -:102884001A6822F480221A60D960C3F88C10704749 -:102894000038024010300024003000201D4A9368A4 -:1028A40003F00C03042B10B503D0082B03D01A4BF0 -:1028B40018E01A4B16E05168536811F4800F03F0C6 -:1028C4003F03516814BF154A134AB2FBF3F3114A8C -:1028D4005268C2F30142C1F3881101324B435200E2 -:1028E400B3FBF2F30B4A036093680D49C3F303137C -:1028F400CC5C0368E34043609468C4F382240C5DB9 -:1029040023FA04F484609268C2F342328A5CD340AE -:10291400C36010BD003802400024F40040787D01FB -:1029240001000020044B1A6B09B1104301E022EAB4 -:1029340000001863704700BF00380240044B9A6BD4 -:1029440009B1104301E022EA00009863704700BF18 -:1029540000380240044B1A6C09B1104301E022EA2A -:1029640000001864704700BF00380240044B5A6CE2 -:1029740009B1104301E022EA00005864704700BF27 -:1029840000380240044B1A6909B1104301E022EAFD -:1029940000001861704700BF00380240044B5A69B8 -:1029A40009B1104301E022EA00005861704700BFFA -:1029B40000380240044B9A6909B1104301E022EA4D -:1029C40000009861704700BF00380240044B1A6A47 -:1029D40009B1104301E022EA00001862704700BF09 -:1029E40000380240044B5A6A09B1104301E022EA5C -:1029F40000005862704700BF003802404209012AB3 -:102A0400074B01D11B6803E0022A0CBF1B6F5B6FED -:102A140000F01F0023FA00F000F00100704700BF2F -:102A24000038024082B000230193054B0193019BBF -:102A340003EB80000190019B196002B0704700BF56 -:102A44005028004082B000230193054B0193019B61 -:102A540003EB80000190019B186802B0704700BF2F -:102A64005028004008B5254B984207D14FF48050B8 -:102A74000121FFF7B7FF4FF4805039E0204B984213 -:102A840007D14FF480400121FFF7A0FF4FF48040AD -:102A940009E01C4B98420BD14FF400400121FFF791 -:102AA40095FF4FF400400021BDE80840FFF78EBFBA -:102AB400154B984207D14FF400500121FFF792FFC4 -:102AC4004FF4005014E0114B984207D14FF480109A -:102AD4000121FFF787FF4FF4801009E00C4B984267 -:102AE4000BD14FF400100121FFF77CFF4FF40010CD -:102AF4000021BDE80840FFF775BF08BD0030014064 -:102B040000380040003C00400034014000500140C7 -:102B14000054014003884A8810B503F441540B88DB -:102B240013438A881343CA8813430A8913434A897F -:102B340013438A891343CA89134323439BB20380F3 -:102B4400838B23F400631B041B0C83830B8A038293 -:102B540010BD0023038043808380C38003814381AD -:102B64008381C381072303827047038819B19BB211 -:102B740043F0400303E023F040031B041B0C0380D9 -:102B84007047808980B270478181704703891942F8 -:102B940014BF0120002070477FB5038ACA889BB206 -:102BA40023F440531343038283890E469BB223F4D8 -:102BB400B053098923F00C02B3880B4371890B438A -:102BC4009BB213438381838AB2899BB223F44073FB -:102BD4001343838204466846FFF760FE1A4B9C4207 -:102BE40003D003F580639C4201D1039D00E0029D64 -:102BF400A38931681BB2002B4FF0190202FB05F2C6 -:102C0400B4BF4D008D00B2FBF5F5A2896426B5FB77 -:102C1400F6F00001010912B2002A06FB1153ADBF00 -:102C24001A01D90032313232B5BFB1FBF6F2B2FB30 -:102C3400F6F302F0070203F00F03B4BF1043184386 -:102C440080B2208104B070BD00100140838919B1A5 -:102C54009BB243F4005303E023F400531B041B0C06 -:102C640083817047C1F3080181807047038819424A -:102C740014BF01200020704710B5431E0A4491423E -:102C840004D011F8014B03F8014FF8E710BD0244DA -:102C94000346934202D003F8011BFAE77047000091 -:102CA4000400000001030000010000000100000016 -:102CB40003000000010000000F0000000F000000EE -:102CC40000000000EE2F0008001802401000000071 -:102CD40006040000F32F000800180240080000005A -:102CE40006030000F62F000800180240020000004E -:102CF40006010000FD2F0008001802400400000037 -:102D040006020000000000000000000001424F4FD6 -:102D1400544C4F414445520000000000002A2A0050 -:102D2400000000000000000000000000005400400B -:102D340000040240400000000600040000040240B9 -:102D4400000200000900040000002000801A0600B0 -:102D5400004000001F2000000000000000000000F0 -:102D6400005800400014024002000000010004006A -:102D740000140240010000000000040000004000B4 -:102D8400801A0600FFBF000021220000000000009E -:102D940001160008010003008200070084000700F8 -:102DA400010003000200030040000300800007004C -:102DB400840007000001010000000000000000087A -:102DC400004000080080000800C00008000001085E -:102DD40000000208000004080000060800000808BB -:102DE40000000A0800000C0800000E08000008009B -:102DF400100018002000280030003800400048006F -:102E040050005800000000006410B71DC8206E3B3D -:102E1400AC30D9269041DC76F4516B6B5861B24DDD -:102E24003C7105502083B8ED44930FF0E8A3D6D647 -:102E34008CB361CBB0C2649BD4D2D38678E20AA0AF -:102E44001CF2BDBD537475636B20627574746F6E30 -:102E540020726567697374657220697320696E7680 -:102E6400616C69642C20636C656172696E672E0005 -:102E7400427574746F6E206973207075736865642D -:102E840020617420626F6F740069636534306C7004 -:102E94002E630043444F4E45206E6F74206C6F7751 -:102EA40020647572696E672072657365740043529D -:102EB40045534554206E6F742068696768206475B3 -:102EC40072696E6720726573657400446973706C0F -:102ED400617920627573792D776169742074696DE5 -:102EE400656F757420657870697265642100436F3D -:102EF4006E6669677572696E672046504741206641 -:102F0400726F6D2062697473747265616D20696E8D -:102F140020666C6173682E2E2E004E6F204650473B -:102F2400412062697473747265616D20696E2066F4 -:102F34006C6173682E0046616C6C696E6720626117 -:102F4400636B20746F204E56434D2E00465047410C -:102F54002043444F4E452074696D656F7574206538 -:102F640078706972656421004650474120636F6E32 -:102F740066696775726174696F6E206661696C65F4 -:102F8400642E204973207468697320612062696724 -:102F9400626F6172643F00465047412076657273E8 -:102FA400696F6E3A2000446973706C617920696EB0 -:102FB400697469616C697A656420616674657220FC -:102FC4000020726574726965732E00446973706CB5 -:102FD400617920696E697469616C697A6174696F79 -:102FE4006E206661696C65642E004261636B0055F6 -:102FF400700053656C65637400446F776E00637290 -:10300400632E63006932632E630049324320646592 -:1030140076696365204944206F7574206F66206269 -:103024006F756E647320256420286D61783A2025BD -:103034006429006F6E007370692E63002069732029 -:103044006F7574736964652073797374656D206634 -:103054006C6173680D0A0073797374656D5F666CD7 -:103064006173685F657261736528002C2000290D07 -:103074000A006661696C656420746F20657261730F -:103084006520736563746F72200073797374656D62 -:103094005F666C6173685F77726974652800666146 -:1030A400696C656420746F2077726974652061644B -:1030B40064726573732000496E76616C696420667E -:1030C40069726D7761726520646573637269707487 -:1030D400696F6E2100436865636B73756D6D696E0E -:1030E40067206669726D77617265207570646174BA -:1030F4006500496E76616C6964206669726D7761FA -:1031040072652043524320696E2053504920666CF7 -:10311400617368210065726173655F6F6C645F66DB -:1031240069726D776172650077726974655F6E6547 -:10313400775F6669726D7761726500436865636B7A -:1031440073756D6D696E6720002062797465730D07 -:103154000A00436865636B73756D202D2077616E7B -:10316400746564200020676F7420004F75722069B5 -:103174006E7465726E616C20666C61736820636F37 -:103184006E74656E747320617265206261642028B8 -:10319400636865636B73756D206661696C6564292A -:1031A400212054686973206973207265616C6C799D -:1031B4002062616421004F75722070726576696FB8 -:1031C4007573206669726D776172652075706461CC -:1031D4007465206661696C65642C2061626F727429 -:1031E400696E67207570646174652E004E65772082 -:1031F4006669726D776172652069732061766169B1 -:103204006C61626C6521004C6F6164696E67207249 -:1032140065636F76657279206669726D7761726530 -:10322400004661696C656420746F206C6F61642072 -:103234007265636F76657279206669726D77617203 -:10324400652C20737472696B65206F6E652E205433 -:10325400727920616761696E2E004661696C6564EC -:1032640020746F206C6F6164207265636F76657281 -:1032740079206669726D776172652C207374726946 -:103284006B652074776F2E205472792061676169B1 -:103294006E2E004661696C656420746F206C6F61EA -:1032A40064207265636F76657279206669726D77E2 -:1032B4006172652C20737472696B65207468726521 -:1032C400652E2053414420574154434800484152FD -:1032D40044204641554C54006578697420737461E8 -:1032E4006E64627900205F5F5F5F202020202020D1 -:1032F4002020202020202020205F5F002F5C202021 -:103304005F605C20202020202020202020202F27E8 -:103314005F5F605C005C205C2C5C4C5C5F5C20202C -:1033240020205F5F5F202F5C205C2F5C205C2020CE -:103334005F5F20205F5F20205F5F20205F5F202091 -:103344005F5F00205C2F5F5C5F5F205C20202F2785 -:10335400205F20605C205C205C205C205C2F5C2073 -:103364005C2F5C205C2F5C205C2F5C205C2F5C203D -:103374005C002020202F5C205C4C5C205C2F5C20B7 -:103384005C2F5C205C205C205C5F5C205C205C200B -:103394005C5F2F205C5F2F205C205C205C5F5C20E6 -:1033A4005C002020205C20605C5F5F5F5F5C205CD1 -:1033B4005F5C205C5F5C205C5F5F5F5F2F5C205C18 -:1033C4005F5F5F785F5F5F2F275C2F605F5F5F5F8A -:1033D400205C00202020205C2F5F5F5F5F5F2F5CFC -:1033E4002F5F2F5C2F5F2F5C2F5F5F5F2F20205C90 -:1033F4002F5F5F2F2F5F5F2F202020602F5F5F5F85 -:103404002F3E205C0020202020202020202020206F -:1034140020202020202020202020202020202020A8 -:103424002020202020202020202020202020202098 -:103434002F5C5F5F5F2F0020202020202020202091 -:103444002020202020202020202020202020202078 -:103454002020202020202020202020202020202068 -:1034640020205C2F5F5F2F004C61737420666972AB -:103474006D7761726520626F6F742077617320735A -:103484007461626C653B20636C6561722073747255 -:10349400696B657300486F6C6420646F776E2055A8 -:1034A40050202B204241434B20666F7220352073FD -:1034B4006563732E20746F20666F7263652D626F6F -:1034C4006F7420505246004669726D7761726520B0 -:1034D400697320657261736564005761746368641D -:1034E4006F6720636175736564206120726573651D -:1034F4007400536F667477617265206661696C75D8 -:1035040072652063617573656420612072657365FB -:1035140074004661696C656420746F207374617211 -:1035240074206669726D776172652C207374726998 -:103534006B652074687265652E004661696C65640C -:1035440020746F207374617274206669726D776180 -:1035540072652C20737472696B652074776F2E000A -:103564004661696C656420746F20737461727420A1 -:103574006669726D776172652C20737472696B650C -:10358400206F6E652E00466F7263652D626F6F74D7 -:10359400696E67207265636F76657279206D6F64FA -:1035A400652E2E2E00426F6F74696E6720666972F5 -:1035B4006D77617265204020002E2E2E0D0A0D0AB3 -:1035C40000536F667477617265206661696C757209 -:1035D400653B20726573657474696E67210041539D -:1035E400534552543A20002020003A004153534599 -:1035F4005254004153534552544E002A2A2A20570C -:103604005446200053544D33320053544D3332202A -:103614007065726970686572616C206C6962726150 -:103624007279207472697070656420616E206173B0 -:10363400736572740043524F414B204F4F4D00004D -:10364400FF0000000001020304010203040607084E -:043654000900000069 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/boot_snowy_dvt@1478015115.hex b/bin/boot/boot_snowy_dvt@1478015115.hex deleted file mode 100644 index 73c59b6611..0000000000 --- a/bin/boot/boot_snowy_dvt@1478015115.hex +++ /dev/null @@ -1,874 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008B51A0008A8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0EBFF01F09BFC704707 -:1001E4004436000800000020140000201400002001 -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040021FC002401902546E0B200F049FC01AA3B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F08DFF02A800F0FA -:100234002DFE0120002102F0F5FB00201AE001331D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F071FF02A800F011FE0120214602F006 -:10027400D9FB28460BB030BD482E0008742E000868 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0B1F9003018BF8B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F0AAF991 -:1002C40020460E21052208AE02F0A4F9052220469C -:1002D4000C2102F09FF94FF4005346F81C3D002511 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F03FF94FF417 -:10030400804320463146019302F038F94FF480537C -:100314002046314601934FF4007802F02FF920462D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340025F94FF480632046314601938DF8085027 -:100344008DF80B5002F01AF94FF4807320463146B1 -:1003540001938DF808708DF80B5002F00FF94FF4EB -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F004F939464FF4001002F0F7FAA4F53C -:100384004444164B164E1E60204602F06BFB03A835 -:1003940002F0DFFB4FF4827339463046ADF80E307D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F0A6FB2046394602F0CDFBE8 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F097FE012000F07B -:1003F40045FB022001F091FE0120BDE8084000F019 -:100404002BBB000010B504460548022102F0BEFBD8 -:100414000028F9D021460248BDE8104002F0B4BBE0 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0EAF8012001F06FFE25484FF4C4 -:100444000041002202F0E1F84FF4807100222148BB -:1004540002F0DBF8012001F060FE4FF400411D487A -:10046400012202F0D2F8012001F057FE19484FF49E -:10047400007102F0C4F818B11748184A6A210AE05A -:1004840014484FF4004102F0BAF808B1254404E0DE -:100494001148134A6B2101F0B3FDAC4204D014F8A7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F0ABF808240020FFF7A4FF013CFAD1B6 -:1004C4004FF40071034802F09AF8003018BF01207D -:1004D40038BD00BF001802408D2E0008972E00087A -:1004E400B22E000810B504460B4802F04AFB0A4835 -:1004F400022102F04BFB0028F9D00748214602F004 -:1005040043FB0548012102F041FB0028F9D00248D1 -:1005140002F037FBC0B210BD0054014010B54FF4D7 -:10052400807104460022054802F06FF8642001F04F -:10053400EFFD2046BDE81040FFF764BF00180240FD -:1005440008B50748802102F021FB0028F9D10548AD -:100554004FF480710122BDE8084002F056B800BF94 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0C4FDF1E7012010BD00BFDF -:10059400CF2E000810B5224C638822884FF6FF71D5 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F019F84FF4004100221548FD -:1005E40002F013F8012001F098FD12484FF4004185 -:1005F400012202F00AF840F2E9340E484FF4007187 -:1006040001F0FDFF0028D9D1013C04D10A4800F0D3 -:100614003DFC204610BD642001F07AFDEDE700BFEB -:1006240000000F60F22E000804000F601E2F000867 -:100634003A2F000800180240502F000810B5044655 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0B2FF642001F032FD0020FFF782 -:1006B40019FF4FF48071012204461A4801F0A5FF86 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF6C2F00087D -:100724009B2F000800180240C4350008AA2F0008B7 -:10073400C52F0008CF2F000808B55D235843094B87 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F074BAD5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0E1FE3A4804210122A5 -:1007C40001F023FF00240223029435488DF80830F9 -:1007D400072103230C228DF80930019401F01AFF3C -:1007E40080232F480193294601F0C8FE022C04D02F -:1007F4002C48A1B20C2201F00DFF0134102CF5D1CC -:100804004FF6FB7327480193294601F0B7FE0226F1 -:100814003146013624480C22B6B201F0FBFE102EFC -:10082400F6D14FF6FC7329461F48019301F0A6FE4A -:10083400042100221B481E4D01F0E7FE0A2001F0AE -:1008440067FC01220421174801F0DFFE1E2001F09D -:100854005FFC0120014602F071F80FCD03AC0FC418 -:1008640095E8070084E80700402200210AA802F066 -:100874000EFA02230B9308230C934FF48053129324 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400FBFD0020012101F061FE1AB070BD00BF14 -:1008A40000040240000C024000100240A42C000886 -:1008B400AC2C00080D4B98221A80A3F68A231B88BF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F086FBE7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0D9FB11E05AB2012A0CDCEF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0DAFD1A480621042201F0EF -:1009D40021FE04220921174801F01CFE154800F0ED -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0BBFD0C4800F072FA042000F02E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0CEFD20464D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400BCFD2046BDE8384000F02ABA00140240EC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F0A3FD0446284600F018FACF -:100AC400B4FA84F0400938BDC82C000838B50024B5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F03BFF04F140076568284600F001 -:100B0400DFF9684601F072FD02238DF80530A37BFE -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F02AFD284600F0E1F9BC42CB -:100B3400E3D14FF48040314601F018FF03B0F0BD1B -:100B4400C82C000808B5054B1B781BB90448322192 -:100B540001F062FABDE8084001F0D6BB1A0000209B -:100B640002300008124B1B7870B505460C461BB9C1 -:100B74001048382101F050FA032906D98E0831466D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F0AABBBDE8704001F0BABBDB -:100BB4001A00002002300008024B1A780AB90122F8 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F020FA01214FF4805059706D -:100BE40001F0A0FEBDE8084001F082BB1A0000201D -:100BF4000230000808B5074B1A781AB90648512183 -:100C040001F00AFA002159704FF48050BDE8084001 -:100C140001F088BE1A0000200230000870B50C46AE -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0E7FF002864 -:100CA400F9D00648214601F0DDFF0448402101F057 -:100CB400DDFF0028F9D010BD1C00002000480040D2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F0A7FF31464A46002081 -:100D340001F0CEF907F1080304F5927443F8040DA9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64008FF904465520FFF791FF6E466C44A64266 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020102D00082400002072 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0CEFD20460A21072201F006 -:100DC40029FC06AE072220460B2101F023FC4FF438 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0C3FB4FF40063204631460093A5 -:100E04008DF8048001F0BAFBADF80C50ADF80E502B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F0B6FE284639466B -:100E340001F00CFF204600F05BF8054B1F7006B074 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4004AF901A8FFF7CEFF05B05DF804FB000076 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F006FB4C -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0C5F832 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BD302D000808 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0CFFA2046C8 -:100FE40031462A4601F016FB2046FFF781FF03B085 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0CCFB00287BD0BF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BD302D0008C7 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0FCFB684601F076 -:1011640003FB0F4A236A009393424FF48063ADF864 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F085FA2859012101F02D -:10119400F9FA04B070BD00BF30010020302D000802 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0DAF92046B1B22A4601F051 -:1011D4001CFA2046FFF78CFE02B070BD10B5094C16 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BD302D00081C23104A434370B5D4 -:1012140005460F4CD058D61801F00EFA342305FBBE -:1012240003440021E06901F095FB337923B9284692 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400302D00082DE9F347504C9A4694F8383065 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0CEFE494B1844CF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F06BFA2AE03046FFF79DFF30461A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0E1FEDBE719688B8823F4DB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A40008300008302D000880841E00017D036889 -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F002FF06 -:1014840073788DF8043001A800F0FCFE54F8040CC5 -:1014940001F0D2F8454504F11C0406F1340601D0EC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020582D000830010020DF -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F0A0FD042807D90522009240 -:1014E4000D480E4A40F2351100F08AFD0C4A1344AF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020083000080E30000811 -:10152400302D0008104A08B592F838301BB90F481E -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F06AFD0A4B18441C2390F8880003FB42 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD3001002008300008D4 -:10157400302D000837B50D4614460093069B0193A1 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F047F90DB97A -:10161400029805E0012D01D1039801E000F006FDD8 -:10162400B4EB500F0ED8B0FBF4F000F080FD0028AE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0EAFC01200138C00080B205B030BDD2 -:101654003A300008B0F1006F10B504460BD20C48C4 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:10169400B230000840300008C02D00082DE9F8439E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F02AFE2C464C451FDCF32000F03AFE95 -:10170400154B002133F8140000F06CFE09280BD0AF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F01FFE0DE02FB1C5F10100204410 -:1017340031464246B8470134DDE700F013FE01208C -:10174400BDE8F8830020BDE8F88300BF5B300008E3 -:101754006F30000872300008F02D00087630000861 -:10176400C43500082DE9F0470646234814460D46C3 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400DBFDF32000F0EEFD4FEAD41805EB04095D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F04DFE09280DD01248FFF74AFB2B -:1017C4003046FFF771FB1048FFF760FB00F0CAFDDD -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0BAFD42 -:1017F4000120BDE8F08700BF8E3000086F3000087C -:1018040072300008A2300008C435000808B545F25B -:10181400555000F0DDFF042000F0E0FF40F6FF70BB -:1018240000F0E2FF002000F0D3FF4FF480500121CC -:10183400BDE8084000F07CBD08B500F0E3FFBDE85A -:10184400084000F0D7BF08B57D2001F0D7F800307C -:1018540018BF012008BD4900FEF76EBF4B0803EB1B -:101864005000FEF769BF000070B586B00546FFF76B -:10187400A3F901210020FEF75FFF042000F020FB04 -:1018840003A8294600F06EFB03A800F07AFB08B910 -:1018940028480BE02848FFF7F9FA05F10C000499F1 -:1018A400FFF7D1F9059B984204D02448FFF7EEFADC -:1018B40001203DE0049C2248FFF7E8FA2146214A32 -:1018C40021480023FFF7EAFE049E2048FFF7DEFAD2 -:1018D400002405F1C0411E4B009432460C311A48D5 -:1018E400FFF740FF1B48FFF7B5FA0498FFF7DCFA4F -:1018F4001948FFF7AFFA04991348FFF78FF9054623 -:101904001648FFF7A7FA0598FFF7CEFA1448FFF731 -:10191400A1FA2846FFF7C8FA1248FFF7B7FA059B61 -:101924009D4204D01048FFF7B1FA022000E020469F -:1019340006B070BDBB300008D9300008F63000088E -:10194400193100085B180008004000082C31000819 -:10195400611800083F3100084D3100085631000875 -:1019640069310008C43500086F31000808B5022049 -:1019740000F0BEFAD8B3042000F0BAFA70B11C48E3 -:10198400FFF784FA042000F0A7FA022000F0A4FA7A -:101994004FF40040BDE8084000F09EBA1548FFF738 -:1019A40075FA082000F098FA102000F095FA20202B -:1019B40000F092FA402000F08FFA4FF48010FFF705 -:1019C40053FF022807D1082000F07AFA102000F013 -:1019D40077FA00F04DFB042000F07EFA022000F0BC -:1019E4007BFA4FF40040BDE8084000F069BA08BD36 -:1019F400BA310008F031000810B52548FFF746FA5F -:101A04004FF40010FFF730FF10B102283AD809E074 -:101A1400202000F061FA402000F05EFA802000F0FF -:101A24004FFA2FE0082000F04BFA102000F048FA9B -:101A3400202000F05DFA20B91648FFF727FA20208D -:101A440007E0402000F054FA58B91348FFF71EFA93 -:101A5400402000F035FA4FF4007000F031FA00F045 -:101A640007FB0E48FFF712FA082000F035FA1020A1 -:101A740000F032FA202000F02FFA402000F02CFA77 -:101A8400002400E00124042000F026FA204610BDC2 -:101A94000B320008253200085E3200089732000835 -:101AA40008B50248FFF7F2F900F0E7FAD13200086E -:101AB4001EF0040F0CBFEFF30880EFF30980FFF76B -:101AC400EFBF70471FB504461048FFF7DFF901A9BF -:101AD4000C22204600F037FB01A8FFF7D7F9204677 -:101AE400FEF742FEFEF7F2FF094B20F004001870E7 -:101AF4001C46FEF7EBFF237800F0FB00834201D085 -:101B040000F0B6FA0A2000F008FBF2E7C73200083A -:101B1400690100207FB54FF08050012100F01AFFC9 -:101B2400022000F07DFE012807D1022000F082FE91 -:101B3400BC48FFF7ABF900F0A0FA00214FF0805049 -:101B440000F008FFFFF72CF9B748FFF79FF9B748F3 -:101B5400FFF79CF9B648FFF799F9B648FFF796F9ED -:101B6400B548FFF793F9B548FFF790F9B448FFF784 -:101B74008DF9B448FFF78AF9B348FFF787F9B348FA -:101B8400FFF784F9FFF758FCFEF708FF00F0BAF9F5 -:101B940000F0D0F900210C2201A801F078F800F03F -:101BA400C5F901A90C2200F0CEFA01A8FFF76EF9DD -:101BB4009D48FFF76BF99C48FFF768F94FF48040A4 -:101BC40000F096F990B1A248FFF760F94FF4804015 -:101BD40000F082F9082000F07FF9102000F07CF971 -:101BE400202000F079F9402000F076F9FEF7D0FDCE -:101BF400FEF77AFFFEF746FDFEF7FEFA08B19548B8 -:101C040003E0FEF73DFB10B19348FFF75BFF00F0E4 -:101C14008CF9802000F06CF918B1802000F05CF998 -:101C240056E04FF4003000F063F9044620B14FF45D -:101C3400003000F051F94DE00120FEF731FFB0B162 -:101C44002046FEF72DFF90B18448FFF71FF941F2BB -:101C540088340120FEF724FF98B10020FEF720FF0E -:101C640078B1012000F059FA013CF2D132E07C4B0A -:101C74000CCB013301D0013203D17A48FFF706F9C6 -:101C840028E0FFF7E0FD48B3FFF7DDFD10B176482B -:101C9400FFF7FCF84FF4007000F02AF910B1734814 -:101CA400FFF7F4F84FF4007000F016F9102000F07C -:101CB4001FF9002800F0F0806D48FFF7E7F80820CE -:101CC40000F00AF9102000F007F9FFF795FE98B923 -:101CD40068489AE76848FFF7D9F8F6E74FF40070C8 -:101CE40000F006F90028CFD1082000F0F5F8102004 -:101CF40000F0F2F8FFF73AFE4FF4005000F0F8F865 -:101D040004464FF4805000F0F3F8400040EA8400A9 -:101D1400C4B24FF4006000F0EBF82043C0B20728CF -:101D2400C3B20DD14FF4006000F0D6F84FF48050E8 -:101D340000F0D2F84FF4005000F0CEF84F4864E7BA -:101D44000133DBB2023B4FF40060052B11D8DFE80E -:101D540003F00C1003100C1000F0BEF84FF4805088 -:101D640000F0BAF84FF4005003E000F0B5F84FF477 -:101D7400805000F0A5F8FFF749FDFFF75DFD384BF3 -:101D84003F485D681E68FFF765F82846FFF78CF842 -:101D94003C48FFF75FF80022930003F1604303F52A -:101DA400614301324FF0FF31082AC3F88010C3F8B1 -:101DB4008011F1D1344B4FF4801200241A635C6318 -:101DC4009C631C645C6400F045FD3048012100F014 -:101DD400D9FD21462D4800F0D5FDF120012100F068 -:101DE400DDFD2146F12000F0D9FD0120014600F07F -:101DF400E1FD2146012000F0DDFD2548012100F030 -:101E0400E5FD2146224800F0E1FD2248012100F0D1 -:101E1400E9FD2048214600F0E5FD63B64FF0FF3EA2 -:101E2400B5462847DC320008C4350008E93200080A -:101E340000330008193300084733000876330008DC -:101E4400A6330008D7330008093400083B340008DF -:101E54006C340008014550FE024550FE99340008D8 -:101E640000400008CB340008DE340008F6340008D3 -:101E740016350008034550FE8A350008044550FE17 -:101E8400A9350008BD350008003802400010E022E2 -:101E9400FFC9FEF6337F7704082000F029F820B14B -:101EA4000548FEF7F3FF102003E00448FEF7EEFFB9 -:101EB400082000F005F81DE73E35000864350008E9 -:101EC40010B50446002000F0BDFD40EA04010020E6 -:101ED400BDE8104000F0A6BD10B50446002000F097 -:101EE400B1FD20EA04010020BDE8104000F09ABDD5 -:101EF40010B50446002000F0A5FD204214BF0120C7 -:101F0400002010BD08B54FF08050012100F022FDE3 -:101F1400012000F07FFC0120FFF7EAFF20B9012136 -:101F2400BDE8084000F07EBD08BD7047022000F007 -:101F340089BD000008B5FFF7F9FF054B1968884211 -:101F440004D00220BDE8084000F06CBD08BD00BF0D -:101F54008401000808B50248FEF798FF00F088F8ED -:101F6400C53500081FB504460C2201A8FEF70CFC79 -:101F740001AB03CB20601868A0602046616004B008 -:101F840010BD0068A0F10C0358425841704700008E -:101F940080B50646174610480D461C46FEF75AFF04 -:101FA4003846FEF757FF0D48FEF754FF3046FEF75C -:101FB40051FF0B48FEF74EFF2846FEF775FF2CB184 -:101FC4000848FEF747FF2046FEF744FF0648FEF7A1 -:101FD4005DFFFFF7BFFF00BFE2350008EB350008E7 -:101FE400EE350008EC350008C43500081FB506AA14 -:101FF40052F8044B039200921A462346FFF7C8FF97 -:102004000CB41FB506AA52F8043B03920092014A8D -:10201400FFF7BEFFF035000807B5002300937246B2 -:10202400014BFFF7E3FF00BFF7350008744608B51E -:102034000548FEF70FFF2046FEF736FF0348FEF77C -:1020440025FFFFF787FF00BFFF350008C4350008F0 -:10205400BFF34F8F0449054BCA6802F4E06213438F -:10206400CB60BFF34F8FFEE700ED00E00400FA05FC -:1020740008B5FEF781FBFFF7EBFF08B5FFF7E8FFB4 -:10208400F0B50646002401202546034694421DD09F -:1020940011F804C000F1010EBCF1000F03D1735517 -:1020A400774605460DE00133DBB2FF2B774606F891 -:1020B40000C007D102F1FF3C644506D07355871C6C -:1020C4007546012301343846E0E770467355F0BD88 -:1020D40030B5C9B1C0430A44914213D011F8013B51 -:1020E4000A4D83EA000404F00F0455F8244084EAFE -:1020F400101080EA131303F00F0355F8233083EA1A -:102104001010E9E7C04330BD084630BD082E000872 -:10211400162358430138FDD1704710B504462CB13D -:102124004FF47A70FFF7F4FF013CF8E710BD10B5E7 -:10213400B0FA80F400F027F801280CBFC4F11F00A6 -:10214400C4F1200010BD0A2A10B51BDD0C46302353 -:1021540004F8023B78234B701C220F239340034066 -:10216400D340092B01D8303302E00F2B02D8573368 -:10217400DBB200E02023043A04F8013B131DECD148 -:1021840000238B7210BD00F0AA33A0EB530000F0C3 -:10219400CC3300F0333000EB930000EB101000F070 -:1021A4000F3000EB102000EB104000F03F007047B0 -:1021B4002D4AD2F8883043F47003C2F888302B4B90 -:1021C4001A68002142F001021A6099601A6822F02C -:1021D400847222F480321A60254A5A601A6822F402 -:1021E40080221A60D9601A6C224942F080521A6423 -:1021F4000A6842F440420A609A689A609A689A604F -:102204009A6842F480529A601B4A5A601A6842F0F3 -:1022140080721A601968154A8901FBD5174B40F280 -:1022240003611960936823F003039360936843F098 -:10223400020393600D4B9A6802F00C02082AF9D14C -:102244009A6822F400029A600D4AC3F884201A683E -:1022540042F080621A60054B1B681B01FBD5024BE0 -:102264004FF000629A60704700ED00E000380240D1 -:1022740010300024007000401040010F003C024068 -:1022840000300050C278037810B512B3164AD168F2 -:102294004278C943C1F30221C1F10404E4B2A2406B -:1022A400D4B20F220A4181780A40224303F16043E9 -:1022B40003F561431201D2B283F8002303780122AB -:1022C400590903F01F0302FA03F3084A42F82130C4 -:1022D40010BD5A09012103F01F03994002F12003A4 -:1022E400024A42F8231010BD00ED00E000E100E0D6 -:1022F400014B01229A60704700300240014B186084 -:10230400186870470030024000EB81018842044B9A -:1023140003D050F8042B1A60F8E71868704700BF20 -:1023240000300240014B1868704700BF0030024083 -:10233400044B9A6809B1104301E022EA0000986056 -:10234400704700BF002004E0044B1A69002ABFBF95 -:10235400034A5A6002F188325A607047003C0240D6 -:1023640023016745024A136943F000431361704730 -:10237400003C0240014BD860704700BF003C024063 -:102384000E4BDA68D20310D4DA68D1060FD4DA68B7 -:10239400D2050ED4DA6812F0EF0F0CD1DB6813F01B -:1023A400020F14BF0820092070470120704706203F -:1023B4007047022070470720704700BF003C02406E -:1023C40007B509238DF80730FFF7DAFF8DF807000A -:1023D4009DF80730012BF7D09DF8070003B05DF896 -:1023E40004FB000070B5064641B1012908D002295A -:1023F4000CBF4FF400754FF4407503E00D4601E047 -:102404004FF48075FFF7DCFF09281ED10F4C2369B8 -:1024140023F440732361216929432161236923F053 -:10242400F8032361236943F002031E4326612369F1 -:1024340043F480332361FFF7C3FF236923F00203CE -:102444002361236923F0F803236170BD003C02403B -:1024540070B505460E46FFF7B3FF092811D1094CA4 -:10246400236923F44073236123692361236943F0BF -:10247400010323612E70FFF7A3FF236923F00103F7 -:10248400236170BD003C024070B543688668856A6C -:102494000468416B46EA0302C3681A4303691A439A -:1024A40043691A4383691A43C3691A43036A1A4383 -:1024B400436A1343C26A2B431A43036B1343A200B8 -:1024C40002F120420B43082E136002BF136843F04D -:1024D40040031360B1F5801F816B12D18CB14FF0B2 -:1024E40020435C681E6846F480161E601E6846F42D -:1024F40080761E600E6924F47004013E44EA06549A -:102504005C608C690B6823434C6843EA04138C6851 -:1025140043EA0423CC6843EA04430C69496943EA67 -:10252400045343EA0163B5F5804F536010D1C16B86 -:1025340088690B680343486843EA0013886843EAE0 -:1025440000230869496943EA005343EA016301E04F -:102554006FF07043C2F8043170BD0000800000F1D8 -:10256400204019B1036843F0010302E00268024B02 -:1025740013400360704700BFFEFF0F00F0B50E6804 -:1025840000220123934003EA060E9E452AD15500FA -:1025940003230468AB40DB431C4004600C790768E8 -:1025A40004FA05FC013C4CEA0707012C076011D82A -:1025B40084684F791C4084608468AF4027438760F7 -:1025C400446824EA0E0444608C7947689440A4B2B9 -:1025D4003C434460C4682340C360CB79C468AB40C7 -:1025E4002343C3600132102ACBD1F0BD4FF6FF73F1 -:1025F40003600023037143718371C37170470369DE -:10260400194214BF0120002070470AB101837047AA -:102614004183704701F00703C90800EB810010B53E -:102624009B00046A0F21994024EA01010162016AB6 -:102634009A401143016210BD08B5134B984207D16B -:102644004FF40010012100F0C1F94FF4001014E020 -:102654000E4B984207D14FF48000012100F0B6F9E7 -:102664004FF4800009E00A4B98420BD14FF400006C -:10267400012100F0ABF94FF400000021BDE808404F -:1026840000F0A4B908BD00BF005400400058004049 -:10269400005C00407FB5868826F03F060446360479 -:1026A40068460D46360C00F0F9F8029A2C48B2FB45 -:1026B400F0F081B20E43A68023882A4E23F0010352 -:1026C4001B041B0C23802B68B3420AD85B00013126 -:1026D400B2FBF3F39BB2032B89B298BF042321848A -:1026E40023E0EE884BF6FF718E421BBF19214B434A -:1026F40003EB4303B2FBF3F315BF9BB2B2FBF3F35B -:1027040043F480439BB2C3F30B020AB943F00103C1 -:102714004FF4967101FB00F24FF47A7192FBF1F2DF -:10272400013292B243F400432284A383238869894B -:10273400AA889BB243F001032380238823F4816396 -:1027440023F002031B040A431B0C13439BB2238094 -:102754002A89AB8913439BB2238104B070BD00BFA7 -:1027640040420F00A086010041F288330360002339 -:1027740083804BF6FF72038143814FF48043C28010 -:1027840083817047038819B19BB243F0010303E0CE -:1027940023F001031B041B0C03807047038819B149 -:1027A4009BB243F4806303E023F480631B041B0C9B -:1027B40003807047838A9AB2038B10B542EA0343BD -:1027C40021F07F4404EA0300431A5842584110BDE3 -:1027D400014B1860704700BF00300040014B586047 -:1027E400704700BF00300040014B9860704700BF45 -:1027F40000300040024B4AF6AA221A60704700BF1C -:1028040000300040024B4CF6CC421A60704700BFC7 -:1028140000300040014B1860704700BF20000E429A -:10282400034B5B68184214BF01200020704700BFAF -:1028340000700040064BB0F5402F15BF1A685A6867 -:1028440042EA800042F4402214BF18605A60704784 -:10285400007000400E4B1A68002142F001021A6019 -:1028640099601A6822F0A85222F410221A60094AC8 -:102874005A60094AC3F8842002F18062C3F88820B0 -:102884001A6822F480221A60D960C3F88C10704749 -:102894000038024010300024003000201D4A9368A4 -:1028A40003F00C03042B10B503D0082B03D01A4BF0 -:1028B40018E01A4B16E05168536811F4800F03F0C6 -:1028C4003F03516814BF154A134AB2FBF3F3114A8C -:1028D4005268C2F30142C1F3881101324B435200E2 -:1028E400B3FBF2F30B4A036093680D49C3F303137C -:1028F400CC5C0368E34043609468C4F382240C5DB9 -:1029040023FA04F484609268C2F342328A5CD340AE -:10291400C36010BD003802400024F40040787D01FB -:1029240001000020044B1A6B09B1104301E022EAB4 -:1029340000001863704700BF00380240044B9A6BD4 -:1029440009B1104301E022EA00009863704700BF18 -:1029540000380240044B1A6C09B1104301E022EA2A -:1029640000001864704700BF00380240044B5A6CE2 -:1029740009B1104301E022EA00005864704700BF27 -:1029840000380240044B1A6909B1104301E022EAFD -:1029940000001861704700BF00380240044B5A69B8 -:1029A40009B1104301E022EA00005861704700BFFA -:1029B40000380240044B9A6909B1104301E022EA4D -:1029C40000009861704700BF00380240044B1A6A47 -:1029D40009B1104301E022EA00001862704700BF09 -:1029E40000380240044B5A6A09B1104301E022EA5C -:1029F40000005862704700BF003802404209012AB3 -:102A0400074B01D11B6803E0022A0CBF1B6F5B6FED -:102A140000F01F0023FA00F000F00100704700BF2F -:102A24000038024082B000230193054B0193019BBF -:102A340003EB80000190019B196002B0704700BF56 -:102A44005028004082B000230193054B0193019B61 -:102A540003EB80000190019B186802B0704700BF2F -:102A64005028004008B5254B984207D14FF48050B8 -:102A74000121FFF7B7FF4FF4805039E0204B984213 -:102A840007D14FF480400121FFF7A0FF4FF48040AD -:102A940009E01C4B98420BD14FF400400121FFF791 -:102AA40095FF4FF400400021BDE80840FFF78EBFBA -:102AB400154B984207D14FF400500121FFF792FFC4 -:102AC4004FF4005014E0114B984207D14FF480109A -:102AD4000121FFF787FF4FF4801009E00C4B984267 -:102AE4000BD14FF400100121FFF77CFF4FF40010CD -:102AF4000021BDE80840FFF775BF08BD0030014064 -:102B040000380040003C00400034014000500140C7 -:102B14000054014003884A8810B503F441540B88DB -:102B240013438A881343CA8813430A8913434A897F -:102B340013438A891343CA89134323439BB20380F3 -:102B4400838B23F400631B041B0C83830B8A038293 -:102B540010BD0023038043808380C38003814381AD -:102B64008381C381072303827047038819B19BB211 -:102B740043F0400303E023F040031B041B0C0380D9 -:102B84007047808980B270478181704703891942F8 -:102B940014BF0120002070477FB5038ACA889BB206 -:102BA40023F440531343038283890E469BB223F4D8 -:102BB400B053098923F00C02B3880B4371890B438A -:102BC4009BB213438381838AB2899BB223F44073FB -:102BD4001343838204466846FFF760FE1A4B9C4207 -:102BE40003D003F580639C4201D1039D00E0029D64 -:102BF400A38931681BB2002B4FF0190202FB05F2C6 -:102C0400B4BF4D008D00B2FBF5F5A2896426B5FB77 -:102C1400F6F00001010912B2002A06FB1153ADBF00 -:102C24001A01D90032313232B5BFB1FBF6F2B2FB30 -:102C3400F6F302F0070203F00F03B4BF1043184386 -:102C440080B2208104B070BD00100140838919B1A5 -:102C54009BB243F4005303E023F400531B041B0C06 -:102C640083817047C1F3080181807047038819424A -:102C740014BF01200020704710B5431E0A4491423E -:102C840004D011F8014B03F8014FF8E710BD0244DA -:102C94000346934202D003F8011BFAE77047000091 -:102CA4000400000001030000010000000100000016 -:102CB40003000000010000000F0000000F000000EE -:102CC40000000000EE2F0008001802401000000071 -:102CD40006040000F32F000800180240080000005A -:102CE40006030100F62F000800180240020000004D -:102CF40006010100FD2F0008001802400400000036 -:102D040006020100000000000000000001424F4FD5 -:102D1400544C4F414445520000000000002A2A0050 -:102D2400000000000000000000000000005400400B -:102D340000040240400000000600040000040240B9 -:102D4400000200000900040000002000801A0600B0 -:102D5400004000001F2000000000000000000000F0 -:102D6400005800400014024002000000010004006A -:102D740000140240010000000000040000004000B4 -:102D8400801A0600FFBF000021220000000000009E -:102D940001160008010003008200070084000700F8 -:102DA400010003000200030040000300800007004C -:102DB400840007000000010000000000000000087B -:102DC400004000080080000800C00008000001085E -:102DD40000000208000004080000060800000808BB -:102DE40000000A0800000C0800000E08000008009B -:102DF400100018002000280030003800400048006F -:102E040050005800000000006410B71DC8206E3B3D -:102E1400AC30D9269041DC76F4516B6B5861B24DDD -:102E24003C7105502083B8ED44930FF0E8A3D6D647 -:102E34008CB361CBB0C2649BD4D2D38678E20AA0AF -:102E44001CF2BDBD537475636B20627574746F6E30 -:102E540020726567697374657220697320696E7680 -:102E6400616C69642C20636C656172696E672E0005 -:102E7400427574746F6E206973207075736865642D -:102E840020617420626F6F740069636534306C7004 -:102E94002E630043444F4E45206E6F74206C6F7751 -:102EA40020647572696E672072657365740043529D -:102EB40045534554206E6F742068696768206475B3 -:102EC40072696E6720726573657400446973706C0F -:102ED400617920627573792D776169742074696DE5 -:102EE400656F757420657870697265642100436F3D -:102EF4006E6669677572696E672046504741206641 -:102F0400726F6D2062697473747265616D20696E8D -:102F140020666C6173682E2E2E004E6F204650473B -:102F2400412062697473747265616D20696E2066F4 -:102F34006C6173682E0046616C6C696E6720626117 -:102F4400636B20746F204E56434D2E00465047410C -:102F54002043444F4E452074696D656F7574206538 -:102F640078706972656421004650474120636F6E32 -:102F740066696775726174696F6E206661696C65F4 -:102F8400642E204973207468697320612062696724 -:102F9400626F6172643F00465047412076657273E8 -:102FA400696F6E3A2000446973706C617920696EB0 -:102FB400697469616C697A656420616674657220FC -:102FC4000020726574726965732E00446973706CB5 -:102FD400617920696E697469616C697A6174696F79 -:102FE4006E206661696C65642E004261636B0055F6 -:102FF400700053656C65637400446F776E00637290 -:10300400632E63006932632E630049324320646592 -:1030140076696365204944206F7574206F66206269 -:103024006F756E647320256420286D61783A2025BD -:103034006429006F6E007370692E63002069732029 -:103044006F7574736964652073797374656D206634 -:103054006C6173680D0A0073797374656D5F666CD7 -:103064006173685F657261736528002C2000290D07 -:103074000A006661696C656420746F20657261730F -:103084006520736563746F72200073797374656D62 -:103094005F666C6173685F77726974652800666146 -:1030A400696C656420746F2077726974652061644B -:1030B40064726573732000496E76616C696420667E -:1030C40069726D7761726520646573637269707487 -:1030D400696F6E2100436865636B73756D6D696E0E -:1030E40067206669726D77617265207570646174BA -:1030F4006500496E76616C6964206669726D7761FA -:1031040072652043524320696E2053504920666CF7 -:10311400617368210065726173655F6F6C645F66DB -:1031240069726D776172650077726974655F6E6547 -:10313400775F6669726D7761726500436865636B7A -:1031440073756D6D696E6720002062797465730D07 -:103154000A00436865636B73756D202D2077616E7B -:10316400746564200020676F7420004F75722069B5 -:103174006E7465726E616C20666C61736820636F37 -:103184006E74656E747320617265206261642028B8 -:10319400636865636B73756D206661696C6564292A -:1031A400212054686973206973207265616C6C799D -:1031B4002062616421004F75722070726576696FB8 -:1031C4007573206669726D776172652075706461CC -:1031D4007465206661696C65642C2061626F727429 -:1031E400696E67207570646174652E004E65772082 -:1031F4006669726D776172652069732061766169B1 -:103204006C61626C6521004C6F6164696E67207249 -:1032140065636F76657279206669726D7761726530 -:10322400004661696C656420746F206C6F61642072 -:103234007265636F76657279206669726D77617203 -:10324400652C20737472696B65206F6E652E205433 -:10325400727920616761696E2E004661696C6564EC -:1032640020746F206C6F6164207265636F76657281 -:1032740079206669726D776172652C207374726946 -:103284006B652074776F2E205472792061676169B1 -:103294006E2E004661696C656420746F206C6F61EA -:1032A40064207265636F76657279206669726D77E2 -:1032B4006172652C20737472696B65207468726521 -:1032C400652E2053414420574154434800484152FD -:1032D40044204641554C54006578697420737461E8 -:1032E4006E64627900205F5F5F5F202020202020D1 -:1032F4002020202020202020205F5F002F5C202021 -:103304005F605C20202020202020202020202F27E8 -:103314005F5F605C005C205C2C5C4C5C5F5C20202C -:1033240020205F5F5F202F5C205C2F5C205C2020CE -:103334005F5F20205F5F20205F5F20205F5F202091 -:103344005F5F00205C2F5F5C5F5F205C20202F2785 -:10335400205F20605C205C205C205C205C2F5C2073 -:103364005C2F5C205C2F5C205C2F5C205C2F5C203D -:103374005C002020202F5C205C4C5C205C2F5C20B7 -:103384005C2F5C205C205C205C5F5C205C205C200B -:103394005C5F2F205C5F2F205C205C205C5F5C20E6 -:1033A4005C002020205C20605C5F5F5F5F5C205CD1 -:1033B4005F5C205C5F5C205C5F5F5F5F2F5C205C18 -:1033C4005F5F5F785F5F5F2F275C2F605F5F5F5F8A -:1033D400205C00202020205C2F5F5F5F5F5F2F5CFC -:1033E4002F5F2F5C2F5F2F5C2F5F5F5F2F20205C90 -:1033F4002F5F5F2F2F5F5F2F202020602F5F5F5F85 -:103404002F3E205C0020202020202020202020206F -:1034140020202020202020202020202020202020A8 -:103424002020202020202020202020202020202098 -:103434002F5C5F5F5F2F0020202020202020202091 -:103444002020202020202020202020202020202078 -:103454002020202020202020202020202020202068 -:1034640020205C2F5F5F2F004C61737420666972AB -:103474006D7761726520626F6F742077617320735A -:103484007461626C653B20636C6561722073747255 -:10349400696B657300486F6C6420646F776E2055A8 -:1034A40050202B204241434B20666F7220352073FD -:1034B4006563732E20746F20666F7263652D626F6F -:1034C4006F7420505246004669726D7761726520B0 -:1034D400697320657261736564005761746368641D -:1034E4006F6720636175736564206120726573651D -:1034F4007400536F667477617265206661696C75D8 -:1035040072652063617573656420612072657365FB -:1035140074004661696C656420746F207374617211 -:1035240074206669726D776172652C207374726998 -:103534006B652074687265652E004661696C65640C -:1035440020746F207374617274206669726D776180 -:1035540072652C20737472696B652074776F2E000A -:103564004661696C656420746F20737461727420A1 -:103574006669726D776172652C20737472696B650C -:10358400206F6E652E00466F7263652D626F6F74D7 -:10359400696E67207265636F76657279206D6F64FA -:1035A400652E2E2E00426F6F74696E6720666972F5 -:1035B4006D77617265204020002E2E2E0D0A0D0AB3 -:1035C40000536F667477617265206661696C757209 -:1035D400653B20726573657474696E67210041539D -:1035E400534552543A20002020003A004153534599 -:1035F4005254004153534552544E002A2A2A20570C -:103604005446200053544D33320053544D3332202A -:103614007065726970686572616C206C6962726150 -:103624007279207472697070656420616E206173B0 -:10363400736572740043524F414B204F4F4D00004D -:10364400FF0000000001020304010203040607084E -:043654000900000069 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/boot_snowy_evt2@1478015115.hex b/bin/boot/boot_snowy_evt2@1478015115.hex deleted file mode 100644 index 73c59b6611..0000000000 --- a/bin/boot/boot_snowy_evt2@1478015115.hex +++ /dev/null @@ -1,874 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008B51A0008A8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0EBFF01F09BFC704707 -:1001E4004436000800000020140000201400002001 -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040021FC002401902546E0B200F049FC01AA3B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F08DFF02A800F0FA -:100234002DFE0120002102F0F5FB00201AE001331D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F071FF02A800F011FE0120214602F006 -:10027400D9FB28460BB030BD482E0008742E000868 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0B1F9003018BF8B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F0AAF991 -:1002C40020460E21052208AE02F0A4F9052220469C -:1002D4000C2102F09FF94FF4005346F81C3D002511 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F03FF94FF417 -:10030400804320463146019302F038F94FF480537C -:100314002046314601934FF4007802F02FF920462D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340025F94FF480632046314601938DF8085027 -:100344008DF80B5002F01AF94FF4807320463146B1 -:1003540001938DF808708DF80B5002F00FF94FF4EB -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F004F939464FF4001002F0F7FAA4F53C -:100384004444164B164E1E60204602F06BFB03A835 -:1003940002F0DFFB4FF4827339463046ADF80E307D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F0A6FB2046394602F0CDFBE8 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F097FE012000F07B -:1003F40045FB022001F091FE0120BDE8084000F019 -:100404002BBB000010B504460548022102F0BEFBD8 -:100414000028F9D021460248BDE8104002F0B4BBE0 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0EAF8012001F06FFE25484FF4C4 -:100444000041002202F0E1F84FF4807100222148BB -:1004540002F0DBF8012001F060FE4FF400411D487A -:10046400012202F0D2F8012001F057FE19484FF49E -:10047400007102F0C4F818B11748184A6A210AE05A -:1004840014484FF4004102F0BAF808B1254404E0DE -:100494001148134A6B2101F0B3FDAC4204D014F8A7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F0ABF808240020FFF7A4FF013CFAD1B6 -:1004C4004FF40071034802F09AF8003018BF01207D -:1004D40038BD00BF001802408D2E0008972E00087A -:1004E400B22E000810B504460B4802F04AFB0A4835 -:1004F400022102F04BFB0028F9D00748214602F004 -:1005040043FB0548012102F041FB0028F9D00248D1 -:1005140002F037FBC0B210BD0054014010B54FF4D7 -:10052400807104460022054802F06FF8642001F04F -:10053400EFFD2046BDE81040FFF764BF00180240FD -:1005440008B50748802102F021FB0028F9D10548AD -:100554004FF480710122BDE8084002F056B800BF94 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0C4FDF1E7012010BD00BFDF -:10059400CF2E000810B5224C638822884FF6FF71D5 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F019F84FF4004100221548FD -:1005E40002F013F8012001F098FD12484FF4004185 -:1005F400012202F00AF840F2E9340E484FF4007187 -:1006040001F0FDFF0028D9D1013C04D10A4800F0D3 -:100614003DFC204610BD642001F07AFDEDE700BFEB -:1006240000000F60F22E000804000F601E2F000867 -:100634003A2F000800180240502F000810B5044655 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0B2FF642001F032FD0020FFF782 -:1006B40019FF4FF48071012204461A4801F0A5FF86 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF6C2F00087D -:100724009B2F000800180240C4350008AA2F0008B7 -:10073400C52F0008CF2F000808B55D235843094B87 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F074BAD5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0E1FE3A4804210122A5 -:1007C40001F023FF00240223029435488DF80830F9 -:1007D400072103230C228DF80930019401F01AFF3C -:1007E40080232F480193294601F0C8FE022C04D02F -:1007F4002C48A1B20C2201F00DFF0134102CF5D1CC -:100804004FF6FB7327480193294601F0B7FE0226F1 -:100814003146013624480C22B6B201F0FBFE102EFC -:10082400F6D14FF6FC7329461F48019301F0A6FE4A -:10083400042100221B481E4D01F0E7FE0A2001F0AE -:1008440067FC01220421174801F0DFFE1E2001F09D -:100854005FFC0120014602F071F80FCD03AC0FC418 -:1008640095E8070084E80700402200210AA802F066 -:100874000EFA02230B9308230C934FF48053129324 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400FBFD0020012101F061FE1AB070BD00BF14 -:1008A40000040240000C024000100240A42C000886 -:1008B400AC2C00080D4B98221A80A3F68A231B88BF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F086FBE7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0D9FB11E05AB2012A0CDCEF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0DAFD1A480621042201F0EF -:1009D40021FE04220921174801F01CFE154800F0ED -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0BBFD0C4800F072FA042000F02E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0CEFD20464D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400BCFD2046BDE8384000F02ABA00140240EC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F0A3FD0446284600F018FACF -:100AC400B4FA84F0400938BDC82C000838B50024B5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F03BFF04F140076568284600F001 -:100B0400DFF9684601F072FD02238DF80530A37BFE -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F02AFD284600F0E1F9BC42CB -:100B3400E3D14FF48040314601F018FF03B0F0BD1B -:100B4400C82C000808B5054B1B781BB90448322192 -:100B540001F062FABDE8084001F0D6BB1A0000209B -:100B640002300008124B1B7870B505460C461BB9C1 -:100B74001048382101F050FA032906D98E0831466D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F0AABBBDE8704001F0BABBDB -:100BB4001A00002002300008024B1A780AB90122F8 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F020FA01214FF4805059706D -:100BE40001F0A0FEBDE8084001F082BB1A0000201D -:100BF4000230000808B5074B1A781AB90648512183 -:100C040001F00AFA002159704FF48050BDE8084001 -:100C140001F088BE1A0000200230000870B50C46AE -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0E7FF002864 -:100CA400F9D00648214601F0DDFF0448402101F057 -:100CB400DDFF0028F9D010BD1C00002000480040D2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F0A7FF31464A46002081 -:100D340001F0CEF907F1080304F5927443F8040DA9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64008FF904465520FFF791FF6E466C44A64266 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020102D00082400002072 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0CEFD20460A21072201F006 -:100DC40029FC06AE072220460B2101F023FC4FF438 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0C3FB4FF40063204631460093A5 -:100E04008DF8048001F0BAFBADF80C50ADF80E502B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F0B6FE284639466B -:100E340001F00CFF204600F05BF8054B1F7006B074 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4004AF901A8FFF7CEFF05B05DF804FB000076 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F006FB4C -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0C5F832 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BD302D000808 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0CFFA2046C8 -:100FE40031462A4601F016FB2046FFF781FF03B085 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0CCFB00287BD0BF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BD302D0008C7 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0FCFB684601F076 -:1011640003FB0F4A236A009393424FF48063ADF864 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F085FA2859012101F02D -:10119400F9FA04B070BD00BF30010020302D000802 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0DAF92046B1B22A4601F051 -:1011D4001CFA2046FFF78CFE02B070BD10B5094C16 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BD302D00081C23104A434370B5D4 -:1012140005460F4CD058D61801F00EFA342305FBBE -:1012240003440021E06901F095FB337923B9284692 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400302D00082DE9F347504C9A4694F8383065 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0CEFE494B1844CF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F06BFA2AE03046FFF79DFF30461A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0E1FEDBE719688B8823F4DB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A40008300008302D000880841E00017D036889 -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F002FF06 -:1014840073788DF8043001A800F0FCFE54F8040CC5 -:1014940001F0D2F8454504F11C0406F1340601D0EC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020582D000830010020DF -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F0A0FD042807D90522009240 -:1014E4000D480E4A40F2351100F08AFD0C4A1344AF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020083000080E30000811 -:10152400302D0008104A08B592F838301BB90F481E -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F06AFD0A4B18441C2390F8880003FB42 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD3001002008300008D4 -:10157400302D000837B50D4614460093069B0193A1 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F047F90DB97A -:10161400029805E0012D01D1039801E000F006FDD8 -:10162400B4EB500F0ED8B0FBF4F000F080FD0028AE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0EAFC01200138C00080B205B030BDD2 -:101654003A300008B0F1006F10B504460BD20C48C4 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:10169400B230000840300008C02D00082DE9F8439E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F02AFE2C464C451FDCF32000F03AFE95 -:10170400154B002133F8140000F06CFE09280BD0AF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F01FFE0DE02FB1C5F10100204410 -:1017340031464246B8470134DDE700F013FE01208C -:10174400BDE8F8830020BDE8F88300BF5B300008E3 -:101754006F30000872300008F02D00087630000861 -:10176400C43500082DE9F0470646234814460D46C3 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400DBFDF32000F0EEFD4FEAD41805EB04095D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F04DFE09280DD01248FFF74AFB2B -:1017C4003046FFF771FB1048FFF760FB00F0CAFDDD -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0BAFD42 -:1017F4000120BDE8F08700BF8E3000086F3000087C -:1018040072300008A2300008C435000808B545F25B -:10181400555000F0DDFF042000F0E0FF40F6FF70BB -:1018240000F0E2FF002000F0D3FF4FF480500121CC -:10183400BDE8084000F07CBD08B500F0E3FFBDE85A -:10184400084000F0D7BF08B57D2001F0D7F800307C -:1018540018BF012008BD4900FEF76EBF4B0803EB1B -:101864005000FEF769BF000070B586B00546FFF76B -:10187400A3F901210020FEF75FFF042000F020FB04 -:1018840003A8294600F06EFB03A800F07AFB08B910 -:1018940028480BE02848FFF7F9FA05F10C000499F1 -:1018A400FFF7D1F9059B984204D02448FFF7EEFADC -:1018B40001203DE0049C2248FFF7E8FA2146214A32 -:1018C40021480023FFF7EAFE049E2048FFF7DEFAD2 -:1018D400002405F1C0411E4B009432460C311A48D5 -:1018E400FFF740FF1B48FFF7B5FA0498FFF7DCFA4F -:1018F4001948FFF7AFFA04991348FFF78FF9054623 -:101904001648FFF7A7FA0598FFF7CEFA1448FFF731 -:10191400A1FA2846FFF7C8FA1248FFF7B7FA059B61 -:101924009D4204D01048FFF7B1FA022000E020469F -:1019340006B070BDBB300008D9300008F63000088E -:10194400193100085B180008004000082C31000819 -:10195400611800083F3100084D3100085631000875 -:1019640069310008C43500086F31000808B5022049 -:1019740000F0BEFAD8B3042000F0BAFA70B11C48E3 -:10198400FFF784FA042000F0A7FA022000F0A4FA7A -:101994004FF40040BDE8084000F09EBA1548FFF738 -:1019A40075FA082000F098FA102000F095FA20202B -:1019B40000F092FA402000F08FFA4FF48010FFF705 -:1019C40053FF022807D1082000F07AFA102000F013 -:1019D40077FA00F04DFB042000F07EFA022000F0BC -:1019E4007BFA4FF40040BDE8084000F069BA08BD36 -:1019F400BA310008F031000810B52548FFF746FA5F -:101A04004FF40010FFF730FF10B102283AD809E074 -:101A1400202000F061FA402000F05EFA802000F0FF -:101A24004FFA2FE0082000F04BFA102000F048FA9B -:101A3400202000F05DFA20B91648FFF727FA20208D -:101A440007E0402000F054FA58B91348FFF71EFA93 -:101A5400402000F035FA4FF4007000F031FA00F045 -:101A640007FB0E48FFF712FA082000F035FA1020A1 -:101A740000F032FA202000F02FFA402000F02CFA77 -:101A8400002400E00124042000F026FA204610BDC2 -:101A94000B320008253200085E3200089732000835 -:101AA40008B50248FFF7F2F900F0E7FAD13200086E -:101AB4001EF0040F0CBFEFF30880EFF30980FFF76B -:101AC400EFBF70471FB504461048FFF7DFF901A9BF -:101AD4000C22204600F037FB01A8FFF7D7F9204677 -:101AE400FEF742FEFEF7F2FF094B20F004001870E7 -:101AF4001C46FEF7EBFF237800F0FB00834201D085 -:101B040000F0B6FA0A2000F008FBF2E7C73200083A -:101B1400690100207FB54FF08050012100F01AFFC9 -:101B2400022000F07DFE012807D1022000F082FE91 -:101B3400BC48FFF7ABF900F0A0FA00214FF0805049 -:101B440000F008FFFFF72CF9B748FFF79FF9B748F3 -:101B5400FFF79CF9B648FFF799F9B648FFF796F9ED -:101B6400B548FFF793F9B548FFF790F9B448FFF784 -:101B74008DF9B448FFF78AF9B348FFF787F9B348FA -:101B8400FFF784F9FFF758FCFEF708FF00F0BAF9F5 -:101B940000F0D0F900210C2201A801F078F800F03F -:101BA400C5F901A90C2200F0CEFA01A8FFF76EF9DD -:101BB4009D48FFF76BF99C48FFF768F94FF48040A4 -:101BC40000F096F990B1A248FFF760F94FF4804015 -:101BD40000F082F9082000F07FF9102000F07CF971 -:101BE400202000F079F9402000F076F9FEF7D0FDCE -:101BF400FEF77AFFFEF746FDFEF7FEFA08B19548B8 -:101C040003E0FEF73DFB10B19348FFF75BFF00F0E4 -:101C14008CF9802000F06CF918B1802000F05CF998 -:101C240056E04FF4003000F063F9044620B14FF45D -:101C3400003000F051F94DE00120FEF731FFB0B162 -:101C44002046FEF72DFF90B18448FFF71FF941F2BB -:101C540088340120FEF724FF98B10020FEF720FF0E -:101C640078B1012000F059FA013CF2D132E07C4B0A -:101C74000CCB013301D0013203D17A48FFF706F9C6 -:101C840028E0FFF7E0FD48B3FFF7DDFD10B176482B -:101C9400FFF7FCF84FF4007000F02AF910B1734814 -:101CA400FFF7F4F84FF4007000F016F9102000F07C -:101CB4001FF9002800F0F0806D48FFF7E7F80820CE -:101CC40000F00AF9102000F007F9FFF795FE98B923 -:101CD40068489AE76848FFF7D9F8F6E74FF40070C8 -:101CE40000F006F90028CFD1082000F0F5F8102004 -:101CF40000F0F2F8FFF73AFE4FF4005000F0F8F865 -:101D040004464FF4805000F0F3F8400040EA8400A9 -:101D1400C4B24FF4006000F0EBF82043C0B20728CF -:101D2400C3B20DD14FF4006000F0D6F84FF48050E8 -:101D340000F0D2F84FF4005000F0CEF84F4864E7BA -:101D44000133DBB2023B4FF40060052B11D8DFE80E -:101D540003F00C1003100C1000F0BEF84FF4805088 -:101D640000F0BAF84FF4005003E000F0B5F84FF477 -:101D7400805000F0A5F8FFF749FDFFF75DFD384BF3 -:101D84003F485D681E68FFF765F82846FFF78CF842 -:101D94003C48FFF75FF80022930003F1604303F52A -:101DA400614301324FF0FF31082AC3F88010C3F8B1 -:101DB4008011F1D1344B4FF4801200241A635C6318 -:101DC4009C631C645C6400F045FD3048012100F014 -:101DD400D9FD21462D4800F0D5FDF120012100F068 -:101DE400DDFD2146F12000F0D9FD0120014600F07F -:101DF400E1FD2146012000F0DDFD2548012100F030 -:101E0400E5FD2146224800F0E1FD2248012100F0D1 -:101E1400E9FD2048214600F0E5FD63B64FF0FF3EA2 -:101E2400B5462847DC320008C4350008E93200080A -:101E340000330008193300084733000876330008DC -:101E4400A6330008D7330008093400083B340008DF -:101E54006C340008014550FE024550FE99340008D8 -:101E640000400008CB340008DE340008F6340008D3 -:101E740016350008034550FE8A350008044550FE17 -:101E8400A9350008BD350008003802400010E022E2 -:101E9400FFC9FEF6337F7704082000F029F820B14B -:101EA4000548FEF7F3FF102003E00448FEF7EEFFB9 -:101EB400082000F005F81DE73E35000864350008E9 -:101EC40010B50446002000F0BDFD40EA04010020E6 -:101ED400BDE8104000F0A6BD10B50446002000F097 -:101EE400B1FD20EA04010020BDE8104000F09ABDD5 -:101EF40010B50446002000F0A5FD204214BF0120C7 -:101F0400002010BD08B54FF08050012100F022FDE3 -:101F1400012000F07FFC0120FFF7EAFF20B9012136 -:101F2400BDE8084000F07EBD08BD7047022000F007 -:101F340089BD000008B5FFF7F9FF054B1968884211 -:101F440004D00220BDE8084000F06CBD08BD00BF0D -:101F54008401000808B50248FEF798FF00F088F8ED -:101F6400C53500081FB504460C2201A8FEF70CFC79 -:101F740001AB03CB20601868A0602046616004B008 -:101F840010BD0068A0F10C0358425841704700008E -:101F940080B50646174610480D461C46FEF75AFF04 -:101FA4003846FEF757FF0D48FEF754FF3046FEF75C -:101FB40051FF0B48FEF74EFF2846FEF775FF2CB184 -:101FC4000848FEF747FF2046FEF744FF0648FEF7A1 -:101FD4005DFFFFF7BFFF00BFE2350008EB350008E7 -:101FE400EE350008EC350008C43500081FB506AA14 -:101FF40052F8044B039200921A462346FFF7C8FF97 -:102004000CB41FB506AA52F8043B03920092014A8D -:10201400FFF7BEFFF035000807B5002300937246B2 -:10202400014BFFF7E3FF00BFF7350008744608B51E -:102034000548FEF70FFF2046FEF736FF0348FEF77C -:1020440025FFFFF787FF00BFFF350008C4350008F0 -:10205400BFF34F8F0449054BCA6802F4E06213438F -:10206400CB60BFF34F8FFEE700ED00E00400FA05FC -:1020740008B5FEF781FBFFF7EBFF08B5FFF7E8FFB4 -:10208400F0B50646002401202546034694421DD09F -:1020940011F804C000F1010EBCF1000F03D1735517 -:1020A400774605460DE00133DBB2FF2B774606F891 -:1020B40000C007D102F1FF3C644506D07355871C6C -:1020C4007546012301343846E0E770467355F0BD88 -:1020D40030B5C9B1C0430A44914213D011F8013B51 -:1020E4000A4D83EA000404F00F0455F8244084EAFE -:1020F400101080EA131303F00F0355F8233083EA1A -:102104001010E9E7C04330BD084630BD082E000872 -:10211400162358430138FDD1704710B504462CB13D -:102124004FF47A70FFF7F4FF013CF8E710BD10B5E7 -:10213400B0FA80F400F027F801280CBFC4F11F00A6 -:10214400C4F1200010BD0A2A10B51BDD0C46302353 -:1021540004F8023B78234B701C220F239340034066 -:10216400D340092B01D8303302E00F2B02D8573368 -:10217400DBB200E02023043A04F8013B131DECD148 -:1021840000238B7210BD00F0AA33A0EB530000F0C3 -:10219400CC3300F0333000EB930000EB101000F070 -:1021A4000F3000EB102000EB104000F03F007047B0 -:1021B4002D4AD2F8883043F47003C2F888302B4B90 -:1021C4001A68002142F001021A6099601A6822F02C -:1021D400847222F480321A60254A5A601A6822F402 -:1021E40080221A60D9601A6C224942F080521A6423 -:1021F4000A6842F440420A609A689A609A689A604F -:102204009A6842F480529A601B4A5A601A6842F0F3 -:1022140080721A601968154A8901FBD5174B40F280 -:1022240003611960936823F003039360936843F098 -:10223400020393600D4B9A6802F00C02082AF9D14C -:102244009A6822F400029A600D4AC3F884201A683E -:1022540042F080621A60054B1B681B01FBD5024BE0 -:102264004FF000629A60704700ED00E000380240D1 -:1022740010300024007000401040010F003C024068 -:1022840000300050C278037810B512B3164AD168F2 -:102294004278C943C1F30221C1F10404E4B2A2406B -:1022A400D4B20F220A4181780A40224303F16043E9 -:1022B40003F561431201D2B283F8002303780122AB -:1022C400590903F01F0302FA03F3084A42F82130C4 -:1022D40010BD5A09012103F01F03994002F12003A4 -:1022E400024A42F8231010BD00ED00E000E100E0D6 -:1022F400014B01229A60704700300240014B186084 -:10230400186870470030024000EB81018842044B9A -:1023140003D050F8042B1A60F8E71868704700BF20 -:1023240000300240014B1868704700BF0030024083 -:10233400044B9A6809B1104301E022EA0000986056 -:10234400704700BF002004E0044B1A69002ABFBF95 -:10235400034A5A6002F188325A607047003C0240D6 -:1023640023016745024A136943F000431361704730 -:10237400003C0240014BD860704700BF003C024063 -:102384000E4BDA68D20310D4DA68D1060FD4DA68B7 -:10239400D2050ED4DA6812F0EF0F0CD1DB6813F01B -:1023A400020F14BF0820092070470120704706203F -:1023B4007047022070470720704700BF003C02406E -:1023C40007B509238DF80730FFF7DAFF8DF807000A -:1023D4009DF80730012BF7D09DF8070003B05DF896 -:1023E40004FB000070B5064641B1012908D002295A -:1023F4000CBF4FF400754FF4407503E00D4601E047 -:102404004FF48075FFF7DCFF09281ED10F4C2369B8 -:1024140023F440732361216929432161236923F053 -:10242400F8032361236943F002031E4326612369F1 -:1024340043F480332361FFF7C3FF236923F00203CE -:102444002361236923F0F803236170BD003C02403B -:1024540070B505460E46FFF7B3FF092811D1094CA4 -:10246400236923F44073236123692361236943F0BF -:10247400010323612E70FFF7A3FF236923F00103F7 -:10248400236170BD003C024070B543688668856A6C -:102494000468416B46EA0302C3681A4303691A439A -:1024A40043691A4383691A43C3691A43036A1A4383 -:1024B400436A1343C26A2B431A43036B1343A200B8 -:1024C40002F120420B43082E136002BF136843F04D -:1024D40040031360B1F5801F816B12D18CB14FF0B2 -:1024E40020435C681E6846F480161E601E6846F42D -:1024F40080761E600E6924F47004013E44EA06549A -:102504005C608C690B6823434C6843EA04138C6851 -:1025140043EA0423CC6843EA04430C69496943EA67 -:10252400045343EA0163B5F5804F536010D1C16B86 -:1025340088690B680343486843EA0013886843EAE0 -:1025440000230869496943EA005343EA016301E04F -:102554006FF07043C2F8043170BD0000800000F1D8 -:10256400204019B1036843F0010302E00268024B02 -:1025740013400360704700BFFEFF0F00F0B50E6804 -:1025840000220123934003EA060E9E452AD15500FA -:1025940003230468AB40DB431C4004600C790768E8 -:1025A40004FA05FC013C4CEA0707012C076011D82A -:1025B40084684F791C4084608468AF4027438760F7 -:1025C400446824EA0E0444608C7947689440A4B2B9 -:1025D4003C434460C4682340C360CB79C468AB40C7 -:1025E4002343C3600132102ACBD1F0BD4FF6FF73F1 -:1025F40003600023037143718371C37170470369DE -:10260400194214BF0120002070470AB101837047AA -:102614004183704701F00703C90800EB810010B53E -:102624009B00046A0F21994024EA01010162016AB6 -:102634009A401143016210BD08B5134B984207D16B -:102644004FF40010012100F0C1F94FF4001014E020 -:102654000E4B984207D14FF48000012100F0B6F9E7 -:102664004FF4800009E00A4B98420BD14FF400006C -:10267400012100F0ABF94FF400000021BDE808404F -:1026840000F0A4B908BD00BF005400400058004049 -:10269400005C00407FB5868826F03F060446360479 -:1026A40068460D46360C00F0F9F8029A2C48B2FB45 -:1026B400F0F081B20E43A68023882A4E23F0010352 -:1026C4001B041B0C23802B68B3420AD85B00013126 -:1026D400B2FBF3F39BB2032B89B298BF042321848A -:1026E40023E0EE884BF6FF718E421BBF19214B434A -:1026F40003EB4303B2FBF3F315BF9BB2B2FBF3F35B -:1027040043F480439BB2C3F30B020AB943F00103C1 -:102714004FF4967101FB00F24FF47A7192FBF1F2DF -:10272400013292B243F400432284A383238869894B -:10273400AA889BB243F001032380238823F4816396 -:1027440023F002031B040A431B0C13439BB2238094 -:102754002A89AB8913439BB2238104B070BD00BFA7 -:1027640040420F00A086010041F288330360002339 -:1027740083804BF6FF72038143814FF48043C28010 -:1027840083817047038819B19BB243F0010303E0CE -:1027940023F001031B041B0C03807047038819B149 -:1027A4009BB243F4806303E023F480631B041B0C9B -:1027B40003807047838A9AB2038B10B542EA0343BD -:1027C40021F07F4404EA0300431A5842584110BDE3 -:1027D400014B1860704700BF00300040014B586047 -:1027E400704700BF00300040014B9860704700BF45 -:1027F40000300040024B4AF6AA221A60704700BF1C -:1028040000300040024B4CF6CC421A60704700BFC7 -:1028140000300040014B1860704700BF20000E429A -:10282400034B5B68184214BF01200020704700BFAF -:1028340000700040064BB0F5402F15BF1A685A6867 -:1028440042EA800042F4402214BF18605A60704784 -:10285400007000400E4B1A68002142F001021A6019 -:1028640099601A6822F0A85222F410221A60094AC8 -:102874005A60094AC3F8842002F18062C3F88820B0 -:102884001A6822F480221A60D960C3F88C10704749 -:102894000038024010300024003000201D4A9368A4 -:1028A40003F00C03042B10B503D0082B03D01A4BF0 -:1028B40018E01A4B16E05168536811F4800F03F0C6 -:1028C4003F03516814BF154A134AB2FBF3F3114A8C -:1028D4005268C2F30142C1F3881101324B435200E2 -:1028E400B3FBF2F30B4A036093680D49C3F303137C -:1028F400CC5C0368E34043609468C4F382240C5DB9 -:1029040023FA04F484609268C2F342328A5CD340AE -:10291400C36010BD003802400024F40040787D01FB -:1029240001000020044B1A6B09B1104301E022EAB4 -:1029340000001863704700BF00380240044B9A6BD4 -:1029440009B1104301E022EA00009863704700BF18 -:1029540000380240044B1A6C09B1104301E022EA2A -:1029640000001864704700BF00380240044B5A6CE2 -:1029740009B1104301E022EA00005864704700BF27 -:1029840000380240044B1A6909B1104301E022EAFD -:1029940000001861704700BF00380240044B5A69B8 -:1029A40009B1104301E022EA00005861704700BFFA -:1029B40000380240044B9A6909B1104301E022EA4D -:1029C40000009861704700BF00380240044B1A6A47 -:1029D40009B1104301E022EA00001862704700BF09 -:1029E40000380240044B5A6A09B1104301E022EA5C -:1029F40000005862704700BF003802404209012AB3 -:102A0400074B01D11B6803E0022A0CBF1B6F5B6FED -:102A140000F01F0023FA00F000F00100704700BF2F -:102A24000038024082B000230193054B0193019BBF -:102A340003EB80000190019B196002B0704700BF56 -:102A44005028004082B000230193054B0193019B61 -:102A540003EB80000190019B186802B0704700BF2F -:102A64005028004008B5254B984207D14FF48050B8 -:102A74000121FFF7B7FF4FF4805039E0204B984213 -:102A840007D14FF480400121FFF7A0FF4FF48040AD -:102A940009E01C4B98420BD14FF400400121FFF791 -:102AA40095FF4FF400400021BDE80840FFF78EBFBA -:102AB400154B984207D14FF400500121FFF792FFC4 -:102AC4004FF4005014E0114B984207D14FF480109A -:102AD4000121FFF787FF4FF4801009E00C4B984267 -:102AE4000BD14FF400100121FFF77CFF4FF40010CD -:102AF4000021BDE80840FFF775BF08BD0030014064 -:102B040000380040003C00400034014000500140C7 -:102B14000054014003884A8810B503F441540B88DB -:102B240013438A881343CA8813430A8913434A897F -:102B340013438A891343CA89134323439BB20380F3 -:102B4400838B23F400631B041B0C83830B8A038293 -:102B540010BD0023038043808380C38003814381AD -:102B64008381C381072303827047038819B19BB211 -:102B740043F0400303E023F040031B041B0C0380D9 -:102B84007047808980B270478181704703891942F8 -:102B940014BF0120002070477FB5038ACA889BB206 -:102BA40023F440531343038283890E469BB223F4D8 -:102BB400B053098923F00C02B3880B4371890B438A -:102BC4009BB213438381838AB2899BB223F44073FB -:102BD4001343838204466846FFF760FE1A4B9C4207 -:102BE40003D003F580639C4201D1039D00E0029D64 -:102BF400A38931681BB2002B4FF0190202FB05F2C6 -:102C0400B4BF4D008D00B2FBF5F5A2896426B5FB77 -:102C1400F6F00001010912B2002A06FB1153ADBF00 -:102C24001A01D90032313232B5BFB1FBF6F2B2FB30 -:102C3400F6F302F0070203F00F03B4BF1043184386 -:102C440080B2208104B070BD00100140838919B1A5 -:102C54009BB243F4005303E023F400531B041B0C06 -:102C640083817047C1F3080181807047038819424A -:102C740014BF01200020704710B5431E0A4491423E -:102C840004D011F8014B03F8014FF8E710BD0244DA -:102C94000346934202D003F8011BFAE77047000091 -:102CA4000400000001030000010000000100000016 -:102CB40003000000010000000F0000000F000000EE -:102CC40000000000EE2F0008001802401000000071 -:102CD40006040000F32F000800180240080000005A -:102CE40006030100F62F000800180240020000004D -:102CF40006010100FD2F0008001802400400000036 -:102D040006020100000000000000000001424F4FD5 -:102D1400544C4F414445520000000000002A2A0050 -:102D2400000000000000000000000000005400400B -:102D340000040240400000000600040000040240B9 -:102D4400000200000900040000002000801A0600B0 -:102D5400004000001F2000000000000000000000F0 -:102D6400005800400014024002000000010004006A -:102D740000140240010000000000040000004000B4 -:102D8400801A0600FFBF000021220000000000009E -:102D940001160008010003008200070084000700F8 -:102DA400010003000200030040000300800007004C -:102DB400840007000000010000000000000000087B -:102DC400004000080080000800C00008000001085E -:102DD40000000208000004080000060800000808BB -:102DE40000000A0800000C0800000E08000008009B -:102DF400100018002000280030003800400048006F -:102E040050005800000000006410B71DC8206E3B3D -:102E1400AC30D9269041DC76F4516B6B5861B24DDD -:102E24003C7105502083B8ED44930FF0E8A3D6D647 -:102E34008CB361CBB0C2649BD4D2D38678E20AA0AF -:102E44001CF2BDBD537475636B20627574746F6E30 -:102E540020726567697374657220697320696E7680 -:102E6400616C69642C20636C656172696E672E0005 -:102E7400427574746F6E206973207075736865642D -:102E840020617420626F6F740069636534306C7004 -:102E94002E630043444F4E45206E6F74206C6F7751 -:102EA40020647572696E672072657365740043529D -:102EB40045534554206E6F742068696768206475B3 -:102EC40072696E6720726573657400446973706C0F -:102ED400617920627573792D776169742074696DE5 -:102EE400656F757420657870697265642100436F3D -:102EF4006E6669677572696E672046504741206641 -:102F0400726F6D2062697473747265616D20696E8D -:102F140020666C6173682E2E2E004E6F204650473B -:102F2400412062697473747265616D20696E2066F4 -:102F34006C6173682E0046616C6C696E6720626117 -:102F4400636B20746F204E56434D2E00465047410C -:102F54002043444F4E452074696D656F7574206538 -:102F640078706972656421004650474120636F6E32 -:102F740066696775726174696F6E206661696C65F4 -:102F8400642E204973207468697320612062696724 -:102F9400626F6172643F00465047412076657273E8 -:102FA400696F6E3A2000446973706C617920696EB0 -:102FB400697469616C697A656420616674657220FC -:102FC4000020726574726965732E00446973706CB5 -:102FD400617920696E697469616C697A6174696F79 -:102FE4006E206661696C65642E004261636B0055F6 -:102FF400700053656C65637400446F776E00637290 -:10300400632E63006932632E630049324320646592 -:1030140076696365204944206F7574206F66206269 -:103024006F756E647320256420286D61783A2025BD -:103034006429006F6E007370692E63002069732029 -:103044006F7574736964652073797374656D206634 -:103054006C6173680D0A0073797374656D5F666CD7 -:103064006173685F657261736528002C2000290D07 -:103074000A006661696C656420746F20657261730F -:103084006520736563746F72200073797374656D62 -:103094005F666C6173685F77726974652800666146 -:1030A400696C656420746F2077726974652061644B -:1030B40064726573732000496E76616C696420667E -:1030C40069726D7761726520646573637269707487 -:1030D400696F6E2100436865636B73756D6D696E0E -:1030E40067206669726D77617265207570646174BA -:1030F4006500496E76616C6964206669726D7761FA -:1031040072652043524320696E2053504920666CF7 -:10311400617368210065726173655F6F6C645F66DB -:1031240069726D776172650077726974655F6E6547 -:10313400775F6669726D7761726500436865636B7A -:1031440073756D6D696E6720002062797465730D07 -:103154000A00436865636B73756D202D2077616E7B -:10316400746564200020676F7420004F75722069B5 -:103174006E7465726E616C20666C61736820636F37 -:103184006E74656E747320617265206261642028B8 -:10319400636865636B73756D206661696C6564292A -:1031A400212054686973206973207265616C6C799D -:1031B4002062616421004F75722070726576696FB8 -:1031C4007573206669726D776172652075706461CC -:1031D4007465206661696C65642C2061626F727429 -:1031E400696E67207570646174652E004E65772082 -:1031F4006669726D776172652069732061766169B1 -:103204006C61626C6521004C6F6164696E67207249 -:1032140065636F76657279206669726D7761726530 -:10322400004661696C656420746F206C6F61642072 -:103234007265636F76657279206669726D77617203 -:10324400652C20737472696B65206F6E652E205433 -:10325400727920616761696E2E004661696C6564EC -:1032640020746F206C6F6164207265636F76657281 -:1032740079206669726D776172652C207374726946 -:103284006B652074776F2E205472792061676169B1 -:103294006E2E004661696C656420746F206C6F61EA -:1032A40064207265636F76657279206669726D77E2 -:1032B4006172652C20737472696B65207468726521 -:1032C400652E2053414420574154434800484152FD -:1032D40044204641554C54006578697420737461E8 -:1032E4006E64627900205F5F5F5F202020202020D1 -:1032F4002020202020202020205F5F002F5C202021 -:103304005F605C20202020202020202020202F27E8 -:103314005F5F605C005C205C2C5C4C5C5F5C20202C -:1033240020205F5F5F202F5C205C2F5C205C2020CE -:103334005F5F20205F5F20205F5F20205F5F202091 -:103344005F5F00205C2F5F5C5F5F205C20202F2785 -:10335400205F20605C205C205C205C205C2F5C2073 -:103364005C2F5C205C2F5C205C2F5C205C2F5C203D -:103374005C002020202F5C205C4C5C205C2F5C20B7 -:103384005C2F5C205C205C205C5F5C205C205C200B -:103394005C5F2F205C5F2F205C205C205C5F5C20E6 -:1033A4005C002020205C20605C5F5F5F5F5C205CD1 -:1033B4005F5C205C5F5C205C5F5F5F5F2F5C205C18 -:1033C4005F5F5F785F5F5F2F275C2F605F5F5F5F8A -:1033D400205C00202020205C2F5F5F5F5F5F2F5CFC -:1033E4002F5F2F5C2F5F2F5C2F5F5F5F2F20205C90 -:1033F4002F5F5F2F2F5F5F2F202020602F5F5F5F85 -:103404002F3E205C0020202020202020202020206F -:1034140020202020202020202020202020202020A8 -:103424002020202020202020202020202020202098 -:103434002F5C5F5F5F2F0020202020202020202091 -:103444002020202020202020202020202020202078 -:103454002020202020202020202020202020202068 -:1034640020205C2F5F5F2F004C61737420666972AB -:103474006D7761726520626F6F742077617320735A -:103484007461626C653B20636C6561722073747255 -:10349400696B657300486F6C6420646F776E2055A8 -:1034A40050202B204241434B20666F7220352073FD -:1034B4006563732E20746F20666F7263652D626F6F -:1034C4006F7420505246004669726D7761726520B0 -:1034D400697320657261736564005761746368641D -:1034E4006F6720636175736564206120726573651D -:1034F4007400536F667477617265206661696C75D8 -:1035040072652063617573656420612072657365FB -:1035140074004661696C656420746F207374617211 -:1035240074206669726D776172652C207374726998 -:103534006B652074687265652E004661696C65640C -:1035440020746F207374617274206669726D776180 -:1035540072652C20737472696B652074776F2E000A -:103564004661696C656420746F20737461727420A1 -:103574006669726D776172652C20737472696B650C -:10358400206F6E652E00466F7263652D626F6F74D7 -:10359400696E67207265636F76657279206D6F64FA -:1035A400652E2E2E00426F6F74696E6720666972F5 -:1035B4006D77617265204020002E2E2E0D0A0D0AB3 -:1035C40000536F667477617265206661696C757209 -:1035D400653B20726573657474696E67210041539D -:1035E400534552543A20002020003A004153534599 -:1035F4005254004153534552544E002A2A2A20570C -:103604005446200053544D33320053544D3332202A -:103614007065726970686572616C206C6962726150 -:103624007279207472697070656420616E206173B0 -:10363400736572740043524F414B204F4F4D00004D -:10364400FF0000000001020304010203040607084E -:043654000900000069 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/boot_spalding@1478015115.hex b/bin/boot/boot_spalding@1478015115.hex deleted file mode 100644 index 73c59b6611..0000000000 --- a/bin/boot/boot_spalding@1478015115.hex +++ /dev/null @@ -1,874 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008B51A0008A8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0EBFF01F09BFC704707 -:1001E4004436000800000020140000201400002001 -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040021FC002401902546E0B200F049FC01AA3B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F08DFF02A800F0FA -:100234002DFE0120002102F0F5FB00201AE001331D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F071FF02A800F011FE0120214602F006 -:10027400D9FB28460BB030BD482E0008742E000868 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0B1F9003018BF8B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F0AAF991 -:1002C40020460E21052208AE02F0A4F9052220469C -:1002D4000C2102F09FF94FF4005346F81C3D002511 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F03FF94FF417 -:10030400804320463146019302F038F94FF480537C -:100314002046314601934FF4007802F02FF920462D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340025F94FF480632046314601938DF8085027 -:100344008DF80B5002F01AF94FF4807320463146B1 -:1003540001938DF808708DF80B5002F00FF94FF4EB -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F004F939464FF4001002F0F7FAA4F53C -:100384004444164B164E1E60204602F06BFB03A835 -:1003940002F0DFFB4FF4827339463046ADF80E307D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F0A6FB2046394602F0CDFBE8 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F097FE012000F07B -:1003F40045FB022001F091FE0120BDE8084000F019 -:100404002BBB000010B504460548022102F0BEFBD8 -:100414000028F9D021460248BDE8104002F0B4BBE0 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0EAF8012001F06FFE25484FF4C4 -:100444000041002202F0E1F84FF4807100222148BB -:1004540002F0DBF8012001F060FE4FF400411D487A -:10046400012202F0D2F8012001F057FE19484FF49E -:10047400007102F0C4F818B11748184A6A210AE05A -:1004840014484FF4004102F0BAF808B1254404E0DE -:100494001148134A6B2101F0B3FDAC4204D014F8A7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F0ABF808240020FFF7A4FF013CFAD1B6 -:1004C4004FF40071034802F09AF8003018BF01207D -:1004D40038BD00BF001802408D2E0008972E00087A -:1004E400B22E000810B504460B4802F04AFB0A4835 -:1004F400022102F04BFB0028F9D00748214602F004 -:1005040043FB0548012102F041FB0028F9D00248D1 -:1005140002F037FBC0B210BD0054014010B54FF4D7 -:10052400807104460022054802F06FF8642001F04F -:10053400EFFD2046BDE81040FFF764BF00180240FD -:1005440008B50748802102F021FB0028F9D10548AD -:100554004FF480710122BDE8084002F056B800BF94 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0C4FDF1E7012010BD00BFDF -:10059400CF2E000810B5224C638822884FF6FF71D5 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F019F84FF4004100221548FD -:1005E40002F013F8012001F098FD12484FF4004185 -:1005F400012202F00AF840F2E9340E484FF4007187 -:1006040001F0FDFF0028D9D1013C04D10A4800F0D3 -:100614003DFC204610BD642001F07AFDEDE700BFEB -:1006240000000F60F22E000804000F601E2F000867 -:100634003A2F000800180240502F000810B5044655 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0B2FF642001F032FD0020FFF782 -:1006B40019FF4FF48071012204461A4801F0A5FF86 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF6C2F00087D -:100724009B2F000800180240C4350008AA2F0008B7 -:10073400C52F0008CF2F000808B55D235843094B87 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F074BAD5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0E1FE3A4804210122A5 -:1007C40001F023FF00240223029435488DF80830F9 -:1007D400072103230C228DF80930019401F01AFF3C -:1007E40080232F480193294601F0C8FE022C04D02F -:1007F4002C48A1B20C2201F00DFF0134102CF5D1CC -:100804004FF6FB7327480193294601F0B7FE0226F1 -:100814003146013624480C22B6B201F0FBFE102EFC -:10082400F6D14FF6FC7329461F48019301F0A6FE4A -:10083400042100221B481E4D01F0E7FE0A2001F0AE -:1008440067FC01220421174801F0DFFE1E2001F09D -:100854005FFC0120014602F071F80FCD03AC0FC418 -:1008640095E8070084E80700402200210AA802F066 -:100874000EFA02230B9308230C934FF48053129324 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400FBFD0020012101F061FE1AB070BD00BF14 -:1008A40000040240000C024000100240A42C000886 -:1008B400AC2C00080D4B98221A80A3F68A231B88BF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F086FBE7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0D9FB11E05AB2012A0CDCEF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0DAFD1A480621042201F0EF -:1009D40021FE04220921174801F01CFE154800F0ED -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0BBFD0C4800F072FA042000F02E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0CEFD20464D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400BCFD2046BDE8384000F02ABA00140240EC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F0A3FD0446284600F018FACF -:100AC400B4FA84F0400938BDC82C000838B50024B5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F03BFF04F140076568284600F001 -:100B0400DFF9684601F072FD02238DF80530A37BFE -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F02AFD284600F0E1F9BC42CB -:100B3400E3D14FF48040314601F018FF03B0F0BD1B -:100B4400C82C000808B5054B1B781BB90448322192 -:100B540001F062FABDE8084001F0D6BB1A0000209B -:100B640002300008124B1B7870B505460C461BB9C1 -:100B74001048382101F050FA032906D98E0831466D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F0AABBBDE8704001F0BABBDB -:100BB4001A00002002300008024B1A780AB90122F8 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F020FA01214FF4805059706D -:100BE40001F0A0FEBDE8084001F082BB1A0000201D -:100BF4000230000808B5074B1A781AB90648512183 -:100C040001F00AFA002159704FF48050BDE8084001 -:100C140001F088BE1A0000200230000870B50C46AE -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0E7FF002864 -:100CA400F9D00648214601F0DDFF0448402101F057 -:100CB400DDFF0028F9D010BD1C00002000480040D2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F0A7FF31464A46002081 -:100D340001F0CEF907F1080304F5927443F8040DA9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64008FF904465520FFF791FF6E466C44A64266 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020102D00082400002072 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0CEFD20460A21072201F006 -:100DC40029FC06AE072220460B2101F023FC4FF438 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0C3FB4FF40063204631460093A5 -:100E04008DF8048001F0BAFBADF80C50ADF80E502B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F0B6FE284639466B -:100E340001F00CFF204600F05BF8054B1F7006B074 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4004AF901A8FFF7CEFF05B05DF804FB000076 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F006FB4C -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0C5F832 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BD302D000808 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0CFFA2046C8 -:100FE40031462A4601F016FB2046FFF781FF03B085 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0CCFB00287BD0BF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BD302D0008C7 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0FCFB684601F076 -:1011640003FB0F4A236A009393424FF48063ADF864 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F085FA2859012101F02D -:10119400F9FA04B070BD00BF30010020302D000802 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0DAF92046B1B22A4601F051 -:1011D4001CFA2046FFF78CFE02B070BD10B5094C16 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BD302D00081C23104A434370B5D4 -:1012140005460F4CD058D61801F00EFA342305FBBE -:1012240003440021E06901F095FB337923B9284692 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400302D00082DE9F347504C9A4694F8383065 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0CEFE494B1844CF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F06BFA2AE03046FFF79DFF30461A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0E1FEDBE719688B8823F4DB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A40008300008302D000880841E00017D036889 -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F002FF06 -:1014840073788DF8043001A800F0FCFE54F8040CC5 -:1014940001F0D2F8454504F11C0406F1340601D0EC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020582D000830010020DF -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F0A0FD042807D90522009240 -:1014E4000D480E4A40F2351100F08AFD0C4A1344AF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020083000080E30000811 -:10152400302D0008104A08B592F838301BB90F481E -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F06AFD0A4B18441C2390F8880003FB42 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD3001002008300008D4 -:10157400302D000837B50D4614460093069B0193A1 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F047F90DB97A -:10161400029805E0012D01D1039801E000F006FDD8 -:10162400B4EB500F0ED8B0FBF4F000F080FD0028AE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0EAFC01200138C00080B205B030BDD2 -:101654003A300008B0F1006F10B504460BD20C48C4 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:10169400B230000840300008C02D00082DE9F8439E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F02AFE2C464C451FDCF32000F03AFE95 -:10170400154B002133F8140000F06CFE09280BD0AF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F01FFE0DE02FB1C5F10100204410 -:1017340031464246B8470134DDE700F013FE01208C -:10174400BDE8F8830020BDE8F88300BF5B300008E3 -:101754006F30000872300008F02D00087630000861 -:10176400C43500082DE9F0470646234814460D46C3 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400DBFDF32000F0EEFD4FEAD41805EB04095D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F04DFE09280DD01248FFF74AFB2B -:1017C4003046FFF771FB1048FFF760FB00F0CAFDDD -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0BAFD42 -:1017F4000120BDE8F08700BF8E3000086F3000087C -:1018040072300008A2300008C435000808B545F25B -:10181400555000F0DDFF042000F0E0FF40F6FF70BB -:1018240000F0E2FF002000F0D3FF4FF480500121CC -:10183400BDE8084000F07CBD08B500F0E3FFBDE85A -:10184400084000F0D7BF08B57D2001F0D7F800307C -:1018540018BF012008BD4900FEF76EBF4B0803EB1B -:101864005000FEF769BF000070B586B00546FFF76B -:10187400A3F901210020FEF75FFF042000F020FB04 -:1018840003A8294600F06EFB03A800F07AFB08B910 -:1018940028480BE02848FFF7F9FA05F10C000499F1 -:1018A400FFF7D1F9059B984204D02448FFF7EEFADC -:1018B40001203DE0049C2248FFF7E8FA2146214A32 -:1018C40021480023FFF7EAFE049E2048FFF7DEFAD2 -:1018D400002405F1C0411E4B009432460C311A48D5 -:1018E400FFF740FF1B48FFF7B5FA0498FFF7DCFA4F -:1018F4001948FFF7AFFA04991348FFF78FF9054623 -:101904001648FFF7A7FA0598FFF7CEFA1448FFF731 -:10191400A1FA2846FFF7C8FA1248FFF7B7FA059B61 -:101924009D4204D01048FFF7B1FA022000E020469F -:1019340006B070BDBB300008D9300008F63000088E -:10194400193100085B180008004000082C31000819 -:10195400611800083F3100084D3100085631000875 -:1019640069310008C43500086F31000808B5022049 -:1019740000F0BEFAD8B3042000F0BAFA70B11C48E3 -:10198400FFF784FA042000F0A7FA022000F0A4FA7A -:101994004FF40040BDE8084000F09EBA1548FFF738 -:1019A40075FA082000F098FA102000F095FA20202B -:1019B40000F092FA402000F08FFA4FF48010FFF705 -:1019C40053FF022807D1082000F07AFA102000F013 -:1019D40077FA00F04DFB042000F07EFA022000F0BC -:1019E4007BFA4FF40040BDE8084000F069BA08BD36 -:1019F400BA310008F031000810B52548FFF746FA5F -:101A04004FF40010FFF730FF10B102283AD809E074 -:101A1400202000F061FA402000F05EFA802000F0FF -:101A24004FFA2FE0082000F04BFA102000F048FA9B -:101A3400202000F05DFA20B91648FFF727FA20208D -:101A440007E0402000F054FA58B91348FFF71EFA93 -:101A5400402000F035FA4FF4007000F031FA00F045 -:101A640007FB0E48FFF712FA082000F035FA1020A1 -:101A740000F032FA202000F02FFA402000F02CFA77 -:101A8400002400E00124042000F026FA204610BDC2 -:101A94000B320008253200085E3200089732000835 -:101AA40008B50248FFF7F2F900F0E7FAD13200086E -:101AB4001EF0040F0CBFEFF30880EFF30980FFF76B -:101AC400EFBF70471FB504461048FFF7DFF901A9BF -:101AD4000C22204600F037FB01A8FFF7D7F9204677 -:101AE400FEF742FEFEF7F2FF094B20F004001870E7 -:101AF4001C46FEF7EBFF237800F0FB00834201D085 -:101B040000F0B6FA0A2000F008FBF2E7C73200083A -:101B1400690100207FB54FF08050012100F01AFFC9 -:101B2400022000F07DFE012807D1022000F082FE91 -:101B3400BC48FFF7ABF900F0A0FA00214FF0805049 -:101B440000F008FFFFF72CF9B748FFF79FF9B748F3 -:101B5400FFF79CF9B648FFF799F9B648FFF796F9ED -:101B6400B548FFF793F9B548FFF790F9B448FFF784 -:101B74008DF9B448FFF78AF9B348FFF787F9B348FA -:101B8400FFF784F9FFF758FCFEF708FF00F0BAF9F5 -:101B940000F0D0F900210C2201A801F078F800F03F -:101BA400C5F901A90C2200F0CEFA01A8FFF76EF9DD -:101BB4009D48FFF76BF99C48FFF768F94FF48040A4 -:101BC40000F096F990B1A248FFF760F94FF4804015 -:101BD40000F082F9082000F07FF9102000F07CF971 -:101BE400202000F079F9402000F076F9FEF7D0FDCE -:101BF400FEF77AFFFEF746FDFEF7FEFA08B19548B8 -:101C040003E0FEF73DFB10B19348FFF75BFF00F0E4 -:101C14008CF9802000F06CF918B1802000F05CF998 -:101C240056E04FF4003000F063F9044620B14FF45D -:101C3400003000F051F94DE00120FEF731FFB0B162 -:101C44002046FEF72DFF90B18448FFF71FF941F2BB -:101C540088340120FEF724FF98B10020FEF720FF0E -:101C640078B1012000F059FA013CF2D132E07C4B0A -:101C74000CCB013301D0013203D17A48FFF706F9C6 -:101C840028E0FFF7E0FD48B3FFF7DDFD10B176482B -:101C9400FFF7FCF84FF4007000F02AF910B1734814 -:101CA400FFF7F4F84FF4007000F016F9102000F07C -:101CB4001FF9002800F0F0806D48FFF7E7F80820CE -:101CC40000F00AF9102000F007F9FFF795FE98B923 -:101CD40068489AE76848FFF7D9F8F6E74FF40070C8 -:101CE40000F006F90028CFD1082000F0F5F8102004 -:101CF40000F0F2F8FFF73AFE4FF4005000F0F8F865 -:101D040004464FF4805000F0F3F8400040EA8400A9 -:101D1400C4B24FF4006000F0EBF82043C0B20728CF -:101D2400C3B20DD14FF4006000F0D6F84FF48050E8 -:101D340000F0D2F84FF4005000F0CEF84F4864E7BA -:101D44000133DBB2023B4FF40060052B11D8DFE80E -:101D540003F00C1003100C1000F0BEF84FF4805088 -:101D640000F0BAF84FF4005003E000F0B5F84FF477 -:101D7400805000F0A5F8FFF749FDFFF75DFD384BF3 -:101D84003F485D681E68FFF765F82846FFF78CF842 -:101D94003C48FFF75FF80022930003F1604303F52A -:101DA400614301324FF0FF31082AC3F88010C3F8B1 -:101DB4008011F1D1344B4FF4801200241A635C6318 -:101DC4009C631C645C6400F045FD3048012100F014 -:101DD400D9FD21462D4800F0D5FDF120012100F068 -:101DE400DDFD2146F12000F0D9FD0120014600F07F -:101DF400E1FD2146012000F0DDFD2548012100F030 -:101E0400E5FD2146224800F0E1FD2248012100F0D1 -:101E1400E9FD2048214600F0E5FD63B64FF0FF3EA2 -:101E2400B5462847DC320008C4350008E93200080A -:101E340000330008193300084733000876330008DC -:101E4400A6330008D7330008093400083B340008DF -:101E54006C340008014550FE024550FE99340008D8 -:101E640000400008CB340008DE340008F6340008D3 -:101E740016350008034550FE8A350008044550FE17 -:101E8400A9350008BD350008003802400010E022E2 -:101E9400FFC9FEF6337F7704082000F029F820B14B -:101EA4000548FEF7F3FF102003E00448FEF7EEFFB9 -:101EB400082000F005F81DE73E35000864350008E9 -:101EC40010B50446002000F0BDFD40EA04010020E6 -:101ED400BDE8104000F0A6BD10B50446002000F097 -:101EE400B1FD20EA04010020BDE8104000F09ABDD5 -:101EF40010B50446002000F0A5FD204214BF0120C7 -:101F0400002010BD08B54FF08050012100F022FDE3 -:101F1400012000F07FFC0120FFF7EAFF20B9012136 -:101F2400BDE8084000F07EBD08BD7047022000F007 -:101F340089BD000008B5FFF7F9FF054B1968884211 -:101F440004D00220BDE8084000F06CBD08BD00BF0D -:101F54008401000808B50248FEF798FF00F088F8ED -:101F6400C53500081FB504460C2201A8FEF70CFC79 -:101F740001AB03CB20601868A0602046616004B008 -:101F840010BD0068A0F10C0358425841704700008E -:101F940080B50646174610480D461C46FEF75AFF04 -:101FA4003846FEF757FF0D48FEF754FF3046FEF75C -:101FB40051FF0B48FEF74EFF2846FEF775FF2CB184 -:101FC4000848FEF747FF2046FEF744FF0648FEF7A1 -:101FD4005DFFFFF7BFFF00BFE2350008EB350008E7 -:101FE400EE350008EC350008C43500081FB506AA14 -:101FF40052F8044B039200921A462346FFF7C8FF97 -:102004000CB41FB506AA52F8043B03920092014A8D -:10201400FFF7BEFFF035000807B5002300937246B2 -:10202400014BFFF7E3FF00BFF7350008744608B51E -:102034000548FEF70FFF2046FEF736FF0348FEF77C -:1020440025FFFFF787FF00BFFF350008C4350008F0 -:10205400BFF34F8F0449054BCA6802F4E06213438F -:10206400CB60BFF34F8FFEE700ED00E00400FA05FC -:1020740008B5FEF781FBFFF7EBFF08B5FFF7E8FFB4 -:10208400F0B50646002401202546034694421DD09F -:1020940011F804C000F1010EBCF1000F03D1735517 -:1020A400774605460DE00133DBB2FF2B774606F891 -:1020B40000C007D102F1FF3C644506D07355871C6C -:1020C4007546012301343846E0E770467355F0BD88 -:1020D40030B5C9B1C0430A44914213D011F8013B51 -:1020E4000A4D83EA000404F00F0455F8244084EAFE -:1020F400101080EA131303F00F0355F8233083EA1A -:102104001010E9E7C04330BD084630BD082E000872 -:10211400162358430138FDD1704710B504462CB13D -:102124004FF47A70FFF7F4FF013CF8E710BD10B5E7 -:10213400B0FA80F400F027F801280CBFC4F11F00A6 -:10214400C4F1200010BD0A2A10B51BDD0C46302353 -:1021540004F8023B78234B701C220F239340034066 -:10216400D340092B01D8303302E00F2B02D8573368 -:10217400DBB200E02023043A04F8013B131DECD148 -:1021840000238B7210BD00F0AA33A0EB530000F0C3 -:10219400CC3300F0333000EB930000EB101000F070 -:1021A4000F3000EB102000EB104000F03F007047B0 -:1021B4002D4AD2F8883043F47003C2F888302B4B90 -:1021C4001A68002142F001021A6099601A6822F02C -:1021D400847222F480321A60254A5A601A6822F402 -:1021E40080221A60D9601A6C224942F080521A6423 -:1021F4000A6842F440420A609A689A609A689A604F -:102204009A6842F480529A601B4A5A601A6842F0F3 -:1022140080721A601968154A8901FBD5174B40F280 -:1022240003611960936823F003039360936843F098 -:10223400020393600D4B9A6802F00C02082AF9D14C -:102244009A6822F400029A600D4AC3F884201A683E -:1022540042F080621A60054B1B681B01FBD5024BE0 -:102264004FF000629A60704700ED00E000380240D1 -:1022740010300024007000401040010F003C024068 -:1022840000300050C278037810B512B3164AD168F2 -:102294004278C943C1F30221C1F10404E4B2A2406B -:1022A400D4B20F220A4181780A40224303F16043E9 -:1022B40003F561431201D2B283F8002303780122AB -:1022C400590903F01F0302FA03F3084A42F82130C4 -:1022D40010BD5A09012103F01F03994002F12003A4 -:1022E400024A42F8231010BD00ED00E000E100E0D6 -:1022F400014B01229A60704700300240014B186084 -:10230400186870470030024000EB81018842044B9A -:1023140003D050F8042B1A60F8E71868704700BF20 -:1023240000300240014B1868704700BF0030024083 -:10233400044B9A6809B1104301E022EA0000986056 -:10234400704700BF002004E0044B1A69002ABFBF95 -:10235400034A5A6002F188325A607047003C0240D6 -:1023640023016745024A136943F000431361704730 -:10237400003C0240014BD860704700BF003C024063 -:102384000E4BDA68D20310D4DA68D1060FD4DA68B7 -:10239400D2050ED4DA6812F0EF0F0CD1DB6813F01B -:1023A400020F14BF0820092070470120704706203F -:1023B4007047022070470720704700BF003C02406E -:1023C40007B509238DF80730FFF7DAFF8DF807000A -:1023D4009DF80730012BF7D09DF8070003B05DF896 -:1023E40004FB000070B5064641B1012908D002295A -:1023F4000CBF4FF400754FF4407503E00D4601E047 -:102404004FF48075FFF7DCFF09281ED10F4C2369B8 -:1024140023F440732361216929432161236923F053 -:10242400F8032361236943F002031E4326612369F1 -:1024340043F480332361FFF7C3FF236923F00203CE -:102444002361236923F0F803236170BD003C02403B -:1024540070B505460E46FFF7B3FF092811D1094CA4 -:10246400236923F44073236123692361236943F0BF -:10247400010323612E70FFF7A3FF236923F00103F7 -:10248400236170BD003C024070B543688668856A6C -:102494000468416B46EA0302C3681A4303691A439A -:1024A40043691A4383691A43C3691A43036A1A4383 -:1024B400436A1343C26A2B431A43036B1343A200B8 -:1024C40002F120420B43082E136002BF136843F04D -:1024D40040031360B1F5801F816B12D18CB14FF0B2 -:1024E40020435C681E6846F480161E601E6846F42D -:1024F40080761E600E6924F47004013E44EA06549A -:102504005C608C690B6823434C6843EA04138C6851 -:1025140043EA0423CC6843EA04430C69496943EA67 -:10252400045343EA0163B5F5804F536010D1C16B86 -:1025340088690B680343486843EA0013886843EAE0 -:1025440000230869496943EA005343EA016301E04F -:102554006FF07043C2F8043170BD0000800000F1D8 -:10256400204019B1036843F0010302E00268024B02 -:1025740013400360704700BFFEFF0F00F0B50E6804 -:1025840000220123934003EA060E9E452AD15500FA -:1025940003230468AB40DB431C4004600C790768E8 -:1025A40004FA05FC013C4CEA0707012C076011D82A -:1025B40084684F791C4084608468AF4027438760F7 -:1025C400446824EA0E0444608C7947689440A4B2B9 -:1025D4003C434460C4682340C360CB79C468AB40C7 -:1025E4002343C3600132102ACBD1F0BD4FF6FF73F1 -:1025F40003600023037143718371C37170470369DE -:10260400194214BF0120002070470AB101837047AA -:102614004183704701F00703C90800EB810010B53E -:102624009B00046A0F21994024EA01010162016AB6 -:102634009A401143016210BD08B5134B984207D16B -:102644004FF40010012100F0C1F94FF4001014E020 -:102654000E4B984207D14FF48000012100F0B6F9E7 -:102664004FF4800009E00A4B98420BD14FF400006C -:10267400012100F0ABF94FF400000021BDE808404F -:1026840000F0A4B908BD00BF005400400058004049 -:10269400005C00407FB5868826F03F060446360479 -:1026A40068460D46360C00F0F9F8029A2C48B2FB45 -:1026B400F0F081B20E43A68023882A4E23F0010352 -:1026C4001B041B0C23802B68B3420AD85B00013126 -:1026D400B2FBF3F39BB2032B89B298BF042321848A -:1026E40023E0EE884BF6FF718E421BBF19214B434A -:1026F40003EB4303B2FBF3F315BF9BB2B2FBF3F35B -:1027040043F480439BB2C3F30B020AB943F00103C1 -:102714004FF4967101FB00F24FF47A7192FBF1F2DF -:10272400013292B243F400432284A383238869894B -:10273400AA889BB243F001032380238823F4816396 -:1027440023F002031B040A431B0C13439BB2238094 -:102754002A89AB8913439BB2238104B070BD00BFA7 -:1027640040420F00A086010041F288330360002339 -:1027740083804BF6FF72038143814FF48043C28010 -:1027840083817047038819B19BB243F0010303E0CE -:1027940023F001031B041B0C03807047038819B149 -:1027A4009BB243F4806303E023F480631B041B0C9B -:1027B40003807047838A9AB2038B10B542EA0343BD -:1027C40021F07F4404EA0300431A5842584110BDE3 -:1027D400014B1860704700BF00300040014B586047 -:1027E400704700BF00300040014B9860704700BF45 -:1027F40000300040024B4AF6AA221A60704700BF1C -:1028040000300040024B4CF6CC421A60704700BFC7 -:1028140000300040014B1860704700BF20000E429A -:10282400034B5B68184214BF01200020704700BFAF -:1028340000700040064BB0F5402F15BF1A685A6867 -:1028440042EA800042F4402214BF18605A60704784 -:10285400007000400E4B1A68002142F001021A6019 -:1028640099601A6822F0A85222F410221A60094AC8 -:102874005A60094AC3F8842002F18062C3F88820B0 -:102884001A6822F480221A60D960C3F88C10704749 -:102894000038024010300024003000201D4A9368A4 -:1028A40003F00C03042B10B503D0082B03D01A4BF0 -:1028B40018E01A4B16E05168536811F4800F03F0C6 -:1028C4003F03516814BF154A134AB2FBF3F3114A8C -:1028D4005268C2F30142C1F3881101324B435200E2 -:1028E400B3FBF2F30B4A036093680D49C3F303137C -:1028F400CC5C0368E34043609468C4F382240C5DB9 -:1029040023FA04F484609268C2F342328A5CD340AE -:10291400C36010BD003802400024F40040787D01FB -:1029240001000020044B1A6B09B1104301E022EAB4 -:1029340000001863704700BF00380240044B9A6BD4 -:1029440009B1104301E022EA00009863704700BF18 -:1029540000380240044B1A6C09B1104301E022EA2A -:1029640000001864704700BF00380240044B5A6CE2 -:1029740009B1104301E022EA00005864704700BF27 -:1029840000380240044B1A6909B1104301E022EAFD -:1029940000001861704700BF00380240044B5A69B8 -:1029A40009B1104301E022EA00005861704700BFFA -:1029B40000380240044B9A6909B1104301E022EA4D -:1029C40000009861704700BF00380240044B1A6A47 -:1029D40009B1104301E022EA00001862704700BF09 -:1029E40000380240044B5A6A09B1104301E022EA5C -:1029F40000005862704700BF003802404209012AB3 -:102A0400074B01D11B6803E0022A0CBF1B6F5B6FED -:102A140000F01F0023FA00F000F00100704700BF2F -:102A24000038024082B000230193054B0193019BBF -:102A340003EB80000190019B196002B0704700BF56 -:102A44005028004082B000230193054B0193019B61 -:102A540003EB80000190019B186802B0704700BF2F -:102A64005028004008B5254B984207D14FF48050B8 -:102A74000121FFF7B7FF4FF4805039E0204B984213 -:102A840007D14FF480400121FFF7A0FF4FF48040AD -:102A940009E01C4B98420BD14FF400400121FFF791 -:102AA40095FF4FF400400021BDE80840FFF78EBFBA -:102AB400154B984207D14FF400500121FFF792FFC4 -:102AC4004FF4005014E0114B984207D14FF480109A -:102AD4000121FFF787FF4FF4801009E00C4B984267 -:102AE4000BD14FF400100121FFF77CFF4FF40010CD -:102AF4000021BDE80840FFF775BF08BD0030014064 -:102B040000380040003C00400034014000500140C7 -:102B14000054014003884A8810B503F441540B88DB -:102B240013438A881343CA8813430A8913434A897F -:102B340013438A891343CA89134323439BB20380F3 -:102B4400838B23F400631B041B0C83830B8A038293 -:102B540010BD0023038043808380C38003814381AD -:102B64008381C381072303827047038819B19BB211 -:102B740043F0400303E023F040031B041B0C0380D9 -:102B84007047808980B270478181704703891942F8 -:102B940014BF0120002070477FB5038ACA889BB206 -:102BA40023F440531343038283890E469BB223F4D8 -:102BB400B053098923F00C02B3880B4371890B438A -:102BC4009BB213438381838AB2899BB223F44073FB -:102BD4001343838204466846FFF760FE1A4B9C4207 -:102BE40003D003F580639C4201D1039D00E0029D64 -:102BF400A38931681BB2002B4FF0190202FB05F2C6 -:102C0400B4BF4D008D00B2FBF5F5A2896426B5FB77 -:102C1400F6F00001010912B2002A06FB1153ADBF00 -:102C24001A01D90032313232B5BFB1FBF6F2B2FB30 -:102C3400F6F302F0070203F00F03B4BF1043184386 -:102C440080B2208104B070BD00100140838919B1A5 -:102C54009BB243F4005303E023F400531B041B0C06 -:102C640083817047C1F3080181807047038819424A -:102C740014BF01200020704710B5431E0A4491423E -:102C840004D011F8014B03F8014FF8E710BD0244DA -:102C94000346934202D003F8011BFAE77047000091 -:102CA4000400000001030000010000000100000016 -:102CB40003000000010000000F0000000F000000EE -:102CC40000000000EE2F0008001802401000000071 -:102CD40006040000F32F000800180240080000005A -:102CE40006030100F62F000800180240020000004D -:102CF40006010100FD2F0008001802400400000036 -:102D040006020100000000000000000001424F4FD5 -:102D1400544C4F414445520000000000002A2A0050 -:102D2400000000000000000000000000005400400B -:102D340000040240400000000600040000040240B9 -:102D4400000200000900040000002000801A0600B0 -:102D5400004000001F2000000000000000000000F0 -:102D6400005800400014024002000000010004006A -:102D740000140240010000000000040000004000B4 -:102D8400801A0600FFBF000021220000000000009E -:102D940001160008010003008200070084000700F8 -:102DA400010003000200030040000300800007004C -:102DB400840007000000010000000000000000087B -:102DC400004000080080000800C00008000001085E -:102DD40000000208000004080000060800000808BB -:102DE40000000A0800000C0800000E08000008009B -:102DF400100018002000280030003800400048006F -:102E040050005800000000006410B71DC8206E3B3D -:102E1400AC30D9269041DC76F4516B6B5861B24DDD -:102E24003C7105502083B8ED44930FF0E8A3D6D647 -:102E34008CB361CBB0C2649BD4D2D38678E20AA0AF -:102E44001CF2BDBD537475636B20627574746F6E30 -:102E540020726567697374657220697320696E7680 -:102E6400616C69642C20636C656172696E672E0005 -:102E7400427574746F6E206973207075736865642D -:102E840020617420626F6F740069636534306C7004 -:102E94002E630043444F4E45206E6F74206C6F7751 -:102EA40020647572696E672072657365740043529D -:102EB40045534554206E6F742068696768206475B3 -:102EC40072696E6720726573657400446973706C0F -:102ED400617920627573792D776169742074696DE5 -:102EE400656F757420657870697265642100436F3D -:102EF4006E6669677572696E672046504741206641 -:102F0400726F6D2062697473747265616D20696E8D -:102F140020666C6173682E2E2E004E6F204650473B -:102F2400412062697473747265616D20696E2066F4 -:102F34006C6173682E0046616C6C696E6720626117 -:102F4400636B20746F204E56434D2E00465047410C -:102F54002043444F4E452074696D656F7574206538 -:102F640078706972656421004650474120636F6E32 -:102F740066696775726174696F6E206661696C65F4 -:102F8400642E204973207468697320612062696724 -:102F9400626F6172643F00465047412076657273E8 -:102FA400696F6E3A2000446973706C617920696EB0 -:102FB400697469616C697A656420616674657220FC -:102FC4000020726574726965732E00446973706CB5 -:102FD400617920696E697469616C697A6174696F79 -:102FE4006E206661696C65642E004261636B0055F6 -:102FF400700053656C65637400446F776E00637290 -:10300400632E63006932632E630049324320646592 -:1030140076696365204944206F7574206F66206269 -:103024006F756E647320256420286D61783A2025BD -:103034006429006F6E007370692E63002069732029 -:103044006F7574736964652073797374656D206634 -:103054006C6173680D0A0073797374656D5F666CD7 -:103064006173685F657261736528002C2000290D07 -:103074000A006661696C656420746F20657261730F -:103084006520736563746F72200073797374656D62 -:103094005F666C6173685F77726974652800666146 -:1030A400696C656420746F2077726974652061644B -:1030B40064726573732000496E76616C696420667E -:1030C40069726D7761726520646573637269707487 -:1030D400696F6E2100436865636B73756D6D696E0E -:1030E40067206669726D77617265207570646174BA -:1030F4006500496E76616C6964206669726D7761FA -:1031040072652043524320696E2053504920666CF7 -:10311400617368210065726173655F6F6C645F66DB -:1031240069726D776172650077726974655F6E6547 -:10313400775F6669726D7761726500436865636B7A -:1031440073756D6D696E6720002062797465730D07 -:103154000A00436865636B73756D202D2077616E7B -:10316400746564200020676F7420004F75722069B5 -:103174006E7465726E616C20666C61736820636F37 -:103184006E74656E747320617265206261642028B8 -:10319400636865636B73756D206661696C6564292A -:1031A400212054686973206973207265616C6C799D -:1031B4002062616421004F75722070726576696FB8 -:1031C4007573206669726D776172652075706461CC -:1031D4007465206661696C65642C2061626F727429 -:1031E400696E67207570646174652E004E65772082 -:1031F4006669726D776172652069732061766169B1 -:103204006C61626C6521004C6F6164696E67207249 -:1032140065636F76657279206669726D7761726530 -:10322400004661696C656420746F206C6F61642072 -:103234007265636F76657279206669726D77617203 -:10324400652C20737472696B65206F6E652E205433 -:10325400727920616761696E2E004661696C6564EC -:1032640020746F206C6F6164207265636F76657281 -:1032740079206669726D776172652C207374726946 -:103284006B652074776F2E205472792061676169B1 -:103294006E2E004661696C656420746F206C6F61EA -:1032A40064207265636F76657279206669726D77E2 -:1032B4006172652C20737472696B65207468726521 -:1032C400652E2053414420574154434800484152FD -:1032D40044204641554C54006578697420737461E8 -:1032E4006E64627900205F5F5F5F202020202020D1 -:1032F4002020202020202020205F5F002F5C202021 -:103304005F605C20202020202020202020202F27E8 -:103314005F5F605C005C205C2C5C4C5C5F5C20202C -:1033240020205F5F5F202F5C205C2F5C205C2020CE -:103334005F5F20205F5F20205F5F20205F5F202091 -:103344005F5F00205C2F5F5C5F5F205C20202F2785 -:10335400205F20605C205C205C205C205C2F5C2073 -:103364005C2F5C205C2F5C205C2F5C205C2F5C203D -:103374005C002020202F5C205C4C5C205C2F5C20B7 -:103384005C2F5C205C205C205C5F5C205C205C200B -:103394005C5F2F205C5F2F205C205C205C5F5C20E6 -:1033A4005C002020205C20605C5F5F5F5F5C205CD1 -:1033B4005F5C205C5F5C205C5F5F5F5F2F5C205C18 -:1033C4005F5F5F785F5F5F2F275C2F605F5F5F5F8A -:1033D400205C00202020205C2F5F5F5F5F5F2F5CFC -:1033E4002F5F2F5C2F5F2F5C2F5F5F5F2F20205C90 -:1033F4002F5F5F2F2F5F5F2F202020602F5F5F5F85 -:103404002F3E205C0020202020202020202020206F -:1034140020202020202020202020202020202020A8 -:103424002020202020202020202020202020202098 -:103434002F5C5F5F5F2F0020202020202020202091 -:103444002020202020202020202020202020202078 -:103454002020202020202020202020202020202068 -:1034640020205C2F5F5F2F004C61737420666972AB -:103474006D7761726520626F6F742077617320735A -:103484007461626C653B20636C6561722073747255 -:10349400696B657300486F6C6420646F776E2055A8 -:1034A40050202B204241434B20666F7220352073FD -:1034B4006563732E20746F20666F7263652D626F6F -:1034C4006F7420505246004669726D7761726520B0 -:1034D400697320657261736564005761746368641D -:1034E4006F6720636175736564206120726573651D -:1034F4007400536F667477617265206661696C75D8 -:1035040072652063617573656420612072657365FB -:1035140074004661696C656420746F207374617211 -:1035240074206669726D776172652C207374726998 -:103534006B652074687265652E004661696C65640C -:1035440020746F207374617274206669726D776180 -:1035540072652C20737472696B652074776F2E000A -:103564004661696C656420746F20737461727420A1 -:103574006669726D776172652C20737472696B650C -:10358400206F6E652E00466F7263652D626F6F74D7 -:10359400696E67207265636F76657279206D6F64FA -:1035A400652E2E2E00426F6F74696E6720666972F5 -:1035B4006D77617265204020002E2E2E0D0A0D0AB3 -:1035C40000536F667477617265206661696C757209 -:1035D400653B20726573657474696E67210041539D -:1035E400534552543A20002020003A004153534599 -:1035F4005254004153534552544E002A2A2A20570C -:103604005446200053544D33320053544D3332202A -:103614007065726970686572616C206C6962726150 -:103624007279207472697070656420616E206173B0 -:10363400736572740043524F414B204F4F4D00004D -:10364400FF0000000001020304010203040607084E -:043654000900000069 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/boot_spalding_evt@1431479105.hex b/bin/boot/boot_spalding_evt@1431479105.hex deleted file mode 100644 index 7d0a6a601a..0000000000 --- a/bin/boot/boot_spalding_evt@1431479105.hex +++ /dev/null @@ -1,2751 +0,0 @@ -:020000040800F2 -:1000000068200020B501000801020008A9180008B6 -:1000100001020008010200080102000800000000BF -:1000200000000000000000000000000001020008C5 -:10003000010200080000000001020008010200089F -:100040000102000801020008010200080102000884 -:100050000102000801020008010200080102000874 -:100060000102000801020008010200080102000864 -:100070000102000801020008010200080102000854 -:100080000102000801020008010200080102000844 -:100090000102000801020008010200080102000834 -:1000A0000102000801020008010200080102000824 -:1000B000010200080102000801020008C51300083F -:1000C000D1130008DD130008E9130008010200083D -:1000D00001020008010200080102000801020008F4 -:1000E00001020008010200080102000801020008E4 -:1000F00001020008010200080102000801020008D4 -:1001000001020008010200080102000801020008C3 -:1001100001020008010200080102000801020008B3 -:1001200001020008010200080102000801020008A3 -:100130000102000801020008010200080102000893 -:100140000102000801020008010200080102000883 -:100150000102000801020008010200080102000873 -:100160000102000801020008010200080102000863 -:100170000102000801020008010200080102000853 -:040180000102000870 -:1001840041A3525576332E302D6265746131312D81 -:1001940038342D673532376464626500000000002E -:0F01A4000000000035323764646265000000011E -:1001B400002100F004B80C4B5B58435004310B4849 -:1001C4000B4B42189A42FFF4F6AF0A4A00F003B808 -:1001D400002342F8043B084B9A42FFF4F9AF01F0C4 -:1001E4009DFE01F091FB70478CAB000800000020DD -:1001F400140000201400002068000020FFF7FEBF58 -:1002040030B501208BB002F0E5FA002401902546B8 -:10021400E0B200F0BBFB01AB08B9185519E01A5D58 -:10022400042A10DD164800F025FD02A920220198B9 -:1002340001F03CFE02A800F01DFD0120002102F0A7 -:10024400B9FA00201AE00132D2B2052A1A55A8BF21 -:1002540001250134042CDBD1019C54B1094800F080 -:1002640009FD204602A9202201F020FE02A800F088 -:1002740001FD0120214602F09DFA28460BB030BD55 -:1002840048A4000874A4000808B500F087FA80F0B8 -:100294000100C0B208BD000008B54FF480610348F6 -:1002A40002F073F8003018BF012008BD00180240A6 -:1002B4002DE9F0434A4D89B0284600F00BFD28464D -:1002C4000D21052208AE02F06BF8052228460E2106 -:1002D40002F066F84FF4005346F81C3D0024012751 -:1002E4004FF00209284631468DF80A408DF80B403C -:1002F4008DF808908DF8097001F0FEFF4FF48043EB -:1003040028463146019301F0F7FF4FF48053284605 -:10031400314601934FF400788DF808408DF80A4077 -:1003240001F0EAFF284631468DF808408DF80B703D -:10033400CDF8048001F0E0FF4FF480632846314695 -:1003440001938DF808408DF80B4001F0D5FF4FF470 -:1003540080732846314601938DF808708DF80B4060 -:1003640001F0CAFF4FF40043284631460193A5F536 -:1003740044451C4E8DF80A708DF80B4001F0BCFF0B -:1003840039464FF4001002F0B9F9174B3360284690 -:1003940002F030FA03A802F0A4FA4FF44043ADF897 -:1003A4000C3039464FF482733068ADF80E30ADF836 -:1003B4001040ADF81290ADF81470ADF8168001F04D -:1003C40019F803A9ADF818002846ADF81A4002F050 -:1003D40069FA2846394602F090FA09B0BDE8F0837C -:1003E40000180240140000200024F40008B5022084 -:1003F40001F046FD012000F0B7FA022001F040FDB3 -:100404000120BDE8084000F09DBA000010B5044684 -:100414000548022102F07FFA0028F9D0214602485B -:10042400BDE8104002F075BA0054014038B501220D -:1004340004460D4628484FF4807101F0ACFF0120BA -:1004440001F01EFD24484FF40041002201F0A3FFF7 -:100454004FF480710022204801F09DFF012001F03B -:100464000FFD4FF400411C48012201F094FF0120CC -:1004740001F006FD18484FF4007101F086FF18B131 -:100484001648174A68210AE013484FF4004101F066 -:100494007CFF08B1254404E01048124A692101F0A8 -:1004A400A9FCAC4204D014F8010BFFF7AFFFF8E746 -:1004B40009484FF48071012201F06DFF08240020E7 -:1004C400FFF7A4FF013CFAD103484FF40071BDE8E3 -:1004D400384001F05ABF00BF001802408DA4000844 -:1004E40097A40008B2A4000810B54FF48071044624 -:1004F4000022054801F04FFF642001F0BBFC2046B8 -:10050400BDE81040FFF782BF0018024008B5074855 -:10051400802102F000FA0028F9D105484FF48071D7 -:100524000122BDE8084001F036BF00BF005401407D -:100534000018024010B540F2F514FFF7ADFE50B1BB -:10054400013C04D1054800F095FB204610BD642011 -:1005540001F090FCF1E7012010BD00BFCFA400081A -:1005640010B504460420FFF7BFFF2046FFF74EFFF7 -:10057400BDE81040FFF7CABF10B504460120FFF7DD -:10058400B3FFE0B2FFF742FFC4F30720FFF73EFFDB -:10059400C4F30740FFF73AFF200EFFF737FFBDE82B -:1005A4001040FFF7B3BF000010B5FFF781FE1848F5 -:1005B40047F6DE01FFF73AFFFFF718FF002401209A -:1005C400FFF7CEFFFFF7B6FF78B10320FFF78CFFEC -:1005D400FFF79CFF0F4800F063FB204600F06EFB22 -:1005E4000D48BDE8104000F045BB094847F6DE0160 -:1005F4000134FFF71BFF152CE1D1084800F03AFB4A -:100604000220FFF771FFBDE81040FFF77FBF00BF76 -:10061400262A0008F2A400080DA5000817A5000862 -:1006240008B55D224B1C504300EB5300074BB0FB55 -:10063400F1F01A78C0B2824207D01870FFF79CFF1D -:100644000220BDE80840FFF78BBF08BD0000002072 -:1006540008B5FFF791FF0320BDE80840FFF780BF0E -:1006640008B50220FFF740FFBDE80840FFF74EBF82 -:1006740001F1C04102F0C4B970B543489AB000F02A -:1006840029FB424800F026FB414800F023FB414B84 -:1006940093E8030001AC84E803003C48214601F0E0 -:1006A4002BFE3A480421012201F075FE00250223A5 -:1006B400029535488DF80830072103230C228DF864 -:1006C4000930019501F06CFE80232F4801932146E7 -:1006D40001F012FEEBB2022B04D02C48A9B20C227A -:1006E40001F05EFE0135102DF4D14FF6FB7327485F -:1006F4000193214601F000FE0226314601362448CA -:100704000C22B6B201F04CFE102EF6D14FF6FC735B -:1007140021461F48019301F0EFFD042100221B48EC -:100724001D4D01F038FE0A2001F0A4FB0122042132 -:10073400164801F030FE1E2001F09CFB012001460A -:1007440001F0C4FF0FCD03AC0FC495E8070084E8A3 -:100754000700402200210AA802F05BF902230B9350 -:1007640008230C934FF4805312934FF400531393C4 -:100774000AA803AB18930D9601F03CFD002001215B -:1007840001F0AAFD1AB070BD00040240000C024042 -:100794000010024004A300080CA300080E4B98228A -:1007A4001A80A3F68A231B88DBB2512B07D10B4B8B -:1007B4001888C0B2B0F152025042504100E000200B -:1007C40030B1074B1888C0B2B0F1590358425841B0 -:1007D400014BF0221A807047AA0A006022000060D0 -:1007E4002400006002460B460420502100F0C9BDDD -:1007F40002460B460420502100F0D8BD032873B5EF -:100804000E4603D0042804D001F00AFB14251C4C26 -:1008140001E01C4C162528460DF10701FFF7E2FF05 -:1008240008B900202AE02378AEB10BB1013323E0EC -:100834009DF8071021F0060141F0020128468DF8C9 -:100844000710FFF7D5FF0028EBD001250320257002 -:1008540001F016FB11E05AB2012A0CDC9DF80710D6 -:10086400284601F0F9018DF80710FFF7C1FF0028B1 -:10087400D7D0267001E0013B2370012002B070BD87 -:10088400180000201900002073B5254800F022FA52 -:1008940002254FF410730024012621480093694671 -:1008A4008DF804508DF805508DF806608DF80740DA -:1008B40001F022FD1A480621042201F071FD0422F0 -:1008C4000921174801F06CFD154800F01DFA154880 -:1008D40000F000FA42F20C036946124800938DF8C6 -:1008E40005508DF804608DF806408DF8074001F03E -:1008F40003FD0C4800F008FA042000F0DFFC204659 -:100904006946FFF76FFF9DF80050B54205D10320FB -:100914002946FFF773FF284600E0204602B070BD69 -:10092400000402400014024010B50446042000F004 -:10093400C5FC21460420FFF761FF0420BDE81040F8 -:1009440000F0EABC38B5074C0546204600F0C2F971 -:1009540020462A46042101F01EFD2046BDE8384009 -:1009640000F0D2B90014024038B5074C05462046C1 -:1009740000F0B0F920462A46082101F00CFD20467B -:10098400BDE8384000F0C0B90014024038B5094C45 -:1009940004EB0014606800F09DF92189606801F09F -:1009A400F4FC0546606800F0AFF9D5F1010038BFEA -:1009B400002038BD28A3000838B500242546E0B23D -:1009C400FFF7E4FFA04001340543042CEDB2F6D157 -:1009D400284638BD73B54FF480400121154C01F011 -:1009E4008DFE04F1400654F80A0C00F073F96846D1 -:1009F40001F0C2FC02238DF80530237854F80A0C68 -:100A04008DF80730694654F8063C009300258DF8AC -:100A14000450103401F070FC54F81A0C00F074F90E -:100A2400B442E0D14FF48040294601F067FE02B0A1 -:100A340070BD00BF36A3000808B5054B1B781BB971 -:100A44000448322101F0E2F9BDE8084001F012BB8C -:100A54001A0000204AA50008114B1B7870B5054602 -:100A64000C461BB90F48382101F0D0F9032906D9E7 -:100A74008E083146FFF7E0FFB6003544A41B1CB9CD -:100A8400BDE8704001F004BB00231846EA5C013362 -:100A9400A34242EA0020F9D1BDE8704001F0E4BA73 -:100AA4001A0000204AA50008024B1A780AB901224C -:100AB4001A7070471A00002008B5084B1A781AB942 -:100AC40007482B2101F0A2F901214FF480505970FD -:100AD40001F0F0FDBDE8084001F0C0BA1A000020A2 -:100AE4004AA5000808B5074B1A781AB906485121D7 -:100AF40001F08CF9002159704FF48050BDE8084092 -:100B040001F0D8BD1A0000204AA5000870B50C46B3 -:100B14000546A608FFF7D0FF28463146FFF78CFFAD -:100B240004F0030105EB8600FFF796FF0446FFF788 -:100B3400D9FF204670BD70B5A0B00D460646FFF73C -:100B4400BBFF2C467119802CC4EB0101684608D9FF -:100B54008022FFF78DFD68462021FFF76DFF803C62 -:100B6400F0E72246FFF784FD21466846FFF774FF4D -:100B74000446FFF7B7FF204620B070BD2DE9F041D1 -:100B8400284C86B0204600F0A5F84FF480200121BF -:100B940001F0A8FD20460A21072201F001FC06AE5F -:100BA400072220460B2101F0FBFB002501274FF013 -:100BB40002084FF48063204669468DF8065046F8D3 -:100BC400183D8DF807708DF804808DF8058001F0CC -:100BD40093FB4FF400632046694600938DF804802C -:100BE40001F08AFBADF80C50ADF80E50ADF8105082 -:100BF400ADF814500C4D4FF46133029328460C2386 -:100C040002A9ADF8123001F08DFE2846394601F0F4 -:100C1400E1FE204600F078F8044B1F7006B0BDE8F2 -:100C2400F08100BF00080240004800401C00002082 -:100C3400084B1B7810B504465BB10748402101F00E -:100C4400D9FE0028F9D021460348BDE8104001F040 -:100C5400CDBE10BD1C0000200048004008B5FFF7C1 -:100C6400E7FF0348402101F0C5FE0028F9D008BD84 -:100C740000480040094B1B7810B573B1441E14F8AA -:100C8400010F10B1FFF7EAFFF9E70D20FFF7E6FFC8 -:100C94000A20BDE81040FFF7E1BF10BD1C00002092 -:100CA400054B1B7810B533B1441E14F8010F10B175 -:100CB400FFF7D4FFF9E710BD1C0000201FB50C227C -:100CC40001A901F0F3F801A8FFF7EAFF05B05DF808 -:100CD40004FB000000F13F4000F57E00C0F38720D4 -:100CE40008280BD8064A135C591C115433B9054B18 -:100CF40001211A6B01FA00F010431863704700BF1A -:100D04001D0000200038024000F13F4000F57E0045 -:100D1400C0F3872008280ED8074A135C5BB1013B57 -:100D2400DBB213543BB9054B01211A6B01FA00F0F5 -:100D340022EA0000186370471D00002000380240BA -:100D440002460068838823F4E0631B041B0C838041 -:100D540003889BB243F40073038008230020517579 -:100D640013751076704737B504460D46FFF7B2FF8A -:100D74000023204669468DF804308DF805308DF83F -:100D84000730009501F0B8FA2046FFF7BDFF03B025 -:100D940030BD00000A4B10B5342400FB0434236B2F -:100DA4006BB10A2001F06CF86068A168FFF7DBFF03 -:100DB40020696169FFF7D7FF236B0120984710BDB5 -:100DC40070A30008F7B5044616460D461F46FFF704 -:100DD40081FF022001238DF804008DF805008DF8B1 -:100DE40006302046002369468DF80730009501F04F -:100DF40083FA204631463A4601F0D2FA2046FFF7FC -:100E040083FF03B0F0BD0000037D082B10B504463A -:100E140007D10268938823F4C0631B041B0C9380DE -:100E240083E0434A006802EB8303996E01F08CFB74 -:100E340000287AD0237D072B71D8DFE803F0040B58 -:100E44001A22323A596D227A236802F0FE021A827B -:100E5400012305E02368A27A1A82627A12B102237E -:100E6400237562E09A8892B242F480629A800623E3 -:100E7400F6E723681A8892B242F480721A80032338 -:100E8400EEE7237A226843F001031382E37A012B0D -:100E940005D1138823F480631B041B0C13800423E3 -:100EA400DEE723689A8892B242F480629A8005232E -:100EB400D6E72368227B198A23699954237BE27A33 -:100EC4000133DBB2591C9142237307D12268138882 -:100ED40023F480631B041B0C138026E0934224D16B -:100EE4002268938823F480631B041B0C938013E013 -:100EF400217B236922685B5C1382237BE17A0133C3 -:100F0400DBB29942237310D1938823F480631B04CA -:100F14001B0C93800723A3E72046012101E0204610 -:100F24000021BDE81040FFF70BBF002010BD00BF3B -:100F340070A300087FB51D4E1C2303FB0063054608 -:100F44001B790BB9FFF726FF194B342405FB043436 -:100F54006068A168A289A37BFFF734FF228BA37E7C -:100F640020696169FFF72EFFE069012101F0BAFBF6 -:100F7400684601F0C3FA0F4A236A009393424FF480 -:100F84008063ADF80A304FF01C0303FB05F588BFFE -:100F9400626A705988BFADF80620694601F040FACC -:100FA4007059012101F0B8FA04B070BD2800002086 -:100FB40070A30008A086010073B504460E46002500 -:100FC400FFF788FE0123204669468DF80430009619 -:100FD4008DF805508DF8075001F08EF92046B1B216 -:100FE4002A4601F0D8F92046FFF78EFE02B070BD04 -:100FF400094B10B5342400FB0434236B5BB100208F -:1010040098476068A168FFF7D7FF20696169BDE868 -:101014001040FFF7D1BF10BD70A300081C23104A75 -:10102400434370B50546D058D61801F0CBF90D4BA3 -:10103400342405FB04340021E06901F053FB3379C7 -:1010440023B92846BDE87040FFF7D2BF6068A168A5 -:10105400FFF789FE20696169BDE87040FFF783BE30 -:101064002800002070A300082DE9F74F4A4E9B4644 -:1010740096F838300F469246B0461BB9474840F2BE -:10108400951104E0042804D944484FF4CB7100F0CE -:10109400BDFE434B18444FF01C0990F8885009FBDF -:1010A40005F906EB090423790BB9002071E056F821 -:1010B4000900038B980713D49DF830306772E372EC -:1010C40000270D9B84F808A084F80AB027732775BD -:1010D4002361E78256F80900012101F029FA20E092 -:1010E4002846FFF79BFF2846FFF724FF56F8092000 -:1010F4002C48138B9907DFD50138FAD1D5E7227E26 -:1011040022B1019A511E0191002AF8D100222276BF -:10111400019A02B3227D082A13D1677D237D082B0F -:1011240026D023681A8892B242F480721A809A8870 -:1011340092B242F440729A8001222276194A0192B4 -:10114400DFE7E28AB2F57A7F05D20132E28201203A -:1011540000F096FEE2E79A8822F4E0621204120C90 -:101164009A801A8892B242F400721A800823237576 -:101174001C236B4358F803100A4B0A8B920707D5BC -:10118400013BFAD12846FFF749FF2846FFF7D2FE74 -:10119400384603B0BDE8F08F2800002050A50008B1 -:1011A40070A3000880841E00017D0368082906D10D -:1011B4009A8822F480721204120C9A8036E09A8A79 -:1011C400120505D59A8A22F400621204120C9A823E -:1011D4009A8AD20505D59A8A22F480721204120CD6 -:1011E4009A829A8A52051ED59A8A22F4806212043F -:1011F400120C04299A820AD1032202759A8822F4D5 -:10120400E0621204120C9A80002303760EE0012996 -:1012140009D19A8822F4E06212040021120C0175AB -:101224009A80017602E00021FFF78ABD0020704712 -:101234002DE9F3411E4D1F4C0026DFF87C802B68FE -:1012440044F80C3C1C2202FB0682002304F8083CF0 -:1012540013760822227295F828208DF8042001279D -:101264000C2201A8237004F8013C63608DF805206A -:101274008DF806308DF8077000F0BAFE95F8293025 -:101284008DF8043001A800F0B3FE3E4454F80C0C71 -:1012940001F098F8022E05F1340504F11C04CCD1B8 -:1012A400384688F83870FFF7A3FE02B0BDE8F08135 -:1012B40070A300083400002028000020124A13B54F -:1012C40092F83810034621B9104840F2331100F067 -:1012D4009DFD042807D9052200920C480C4A40F2CF -:1012E400351100F087FD0B490B441C2493F888004A -:1012F40004FB002423790BB9FFF71CFE2379013387 -:10130400237102B010BD00BF2800002050A50008C2 -:1013140056A5000870A3000808B5104B93F83820B0 -:101324001AB90F4840F2451104E0042804D90C48C6 -:101334004FF4A37100F06AFD0A4A10441C2290F88D -:10134400880002FB003213793BB1013BDBB213711D -:101354001BB9BDE80840FFF761BE08BD28000020A6 -:1013640050A5000870A3000873B5069C00930E46B0 -:1013740015460194012132462B46FFF775FE02B053 -:1013840070BD07B500930123FFF7EEFF03B05DF8CE -:1013940004FB73B5069C00930E4615460194002188 -:1013A40032462B46FFF760FE02B070BD1FB504AC99 -:1013B40004F8013D01230094FFF7EBFF04B010BDD6 -:1013C4000148FFF721BD00BF280000200148FFF7B6 -:1013D400EBBE00BF280000200148FFF715BD00BF89 -:1013E400440000200148FFF7DFBE00BF4400002096 -:1013F400FFF79ABA30B585B004460D46684601F049 -:1014040015F90DB9029805E0012D01D1039801E009 -:1014140000F006FDB4EB500F0ED8B0FBF4F000F072 -:1014240039FD002802DC0748272103E0082804DDF1 -:101434000448282100F0EAFC01200138C00080B2F1 -:1014440005B030BD82A50008B0F1006F10B50446A8 -:101454000BD20D48FFF724FC2046FFF72FFC0B4866 -:10146400FFF71EFC4FF0FF3010BD094A002352F86D -:10147400041FA14202D851688C4202D301330B2BC2 -:10148400F5D1184610BD00BFFAA5000888A50008CC -:10149400FCA300082DE9F04705462D480F461646E3 -:1014A4009946FFF7FDFB2846FFF708FC2948FFF79C -:1014B400F7FB3846FFF702FC2748FFF7F1FB002F44 -:1014C4003FD02846FFF7C0FF0446781E2844FFF7A4 -:1014D400BBFF002C074637DB002835DBC4EB0008D4 -:1014E40008F101081EB1002041464A46B04700F009 -:1014F400E1FD1A4B012503EB440ABC421FDCF32037 -:1015040000F0EEFD0AEB4503002133F8020C00F075 -:101514001FFE09280BD01248FFF7C2FB2046FFF735 -:10152400CDFB1048FFF7A6FB00F0D2FD0CE01EB186 -:10153400284641464A46B04701340135DDE700F00C -:10154400C7FD0120BDE8F0870020BDE8F08700BF9B -:10155400A3A50008B7A50008BAA5000830A4000890 -:10156400BEA50008EDAA00082DE9F84606461F4866 -:1015740015468A461F46FFF793FB3046FFF79EFB4E -:101584001B48FFF78DFB2846FFF798FB1948FFF728 -:1015940087FB00F08FFDF32000F0A2FD4FEAD51980 -:1015A4000024AC421DD0A0191AF8041000F006FE65 -:1015B40009280DD01048FFF773FB3046FFF77EFB78 -:1015C4000E48FFF757FB00F083FD0020BDE8F886C6 -:1015D4002FB1630603D1E0094946089AB84701349C -:1015E400DFE700F075FD0120BDE8F886D6A5000808 -:1015F400B7A50008BAA50008EAA50008EDAA0008E6 -:1016040008B545F2555000F0ABFF042000F0AEFFE2 -:1016140040F6FF7000F0B0FF002000F0A1FF4FF48F -:1016240080500121BDE8084000F038BD08B500F045 -:10163400B1FFBDE8084000F0A5BF08B57D2001F06A -:10164400A5F8003018BF012008BD4900FEF7E8BF27 -:101654004B0803EB5000FEF7E3BF000070B586B003 -:101664000446FFF721FA01210020FEF7D9FF0420E8 -:1016740000F020FB03A8214600F06EFB03A800F055 -:101684007AFB08B928480BE02848FFF7F3FA04F17D -:101694000C000499FFF74FFA059B984204D02448A4 -:1016A400FFF7E8FA01203DE0049D2248FFF7E2FA43 -:1016B4002946214A21480023FFF7ECFE049E2048D6 -:1016C400FFF7D8FA002504F1C0411E4B00953246BD -:1016D4000C311A48FFF748FF1B48FFF7E1FA04985A -:1016E400FFF7ECFA1948FFF7DBFA04991348FFF700 -:1016F4000DFA04461648FFF7D3FA0598FFF7DEFA09 -:101704001448FFF7CDFA2046FFF7D8FA1248FFF73E -:10171400B1FA059B9C4204D01048FFF7ABFA0220B3 -:1017240000E0284606B070BD03A6000821A6000804 -:101734003EA6000861A600084F1600080000010834 -:1017440074A600085516000887A6000895A6000888 -:101754009EA60008B1A60008EDAA0008B7A60008D6 -:1017640008B5022000F0BEFAD8B3042000F0BAFA9B -:1017740070B11C48FFF77EFA042000F0A7FA02209B -:1017840000F0A4FA4FF40040BDE8084000F09EBA0F -:101794001548FFF76FFA082000F098FA102000F0BF -:1017A40095FA202000F092FA402000F08FFA4FF4CE -:1017B4008010FFF753FF022807D1082000F07AFABF -:1017C400102000F077FA00F04DFB042000F07EFAC0 -:1017D400022000F07BFA4FF40040BDE8084000F01E -:1017E40069BA08BD02A7000838A7000810B5254843 -:1017F400FFF740FA4FF40010FFF730FF10B1022852 -:101804003AD809E0202000F061FA402000F05EFAA6 -:10181400802000F04FFA2FE0082000F04BFA10204F -:1018240000F048FA202000F05DFA20B91648FFF7CE -:1018340021FA202007E0402000F054FA58B9134858 -:10184400FFF718FA402000F035FA4FF4007000F06A -:1018540031FA00F007FB0E48FFF70CFA082000F0FD -:1018640035FA102000F032FA202000F02FFA402040 -:1018740000F02CFA002400E00124042000F026FAF1 -:10188400204610BD53A700086DA70008A6A70008AE -:10189400DFA7000808B50248FFF7ECF900F0E7FA03 -:1018A40019A800081EF0040F0CBFEFF30880EFF333 -:1018B4000980FFF7EFBF70471FB504461048FFF7D4 -:1018C400D9F901A90C22204600F0F0FA01A8FFF78B -:1018D400D1F92046FEF7BCFEFFF76EF8094B20F065 -:1018E400040018701C46FFF767F8237800F0FB002B -:1018F400834201D000F0B6FA0A2000F0C1FAF2E700 -:101904000FA80008640000207FB54FF0805001212B -:1019140000F0E8FE022000F04BFE012807D102206F -:1019240000F050FE9248FFF7A5F900F0A0FA00215C -:101934004FF0805000F0D6FEFFF720F98D48FFF7F6 -:1019440099F98D48FFF796F98C48FFF793F98C487D -:10195400FFF790F98B48FFF78DF98B48FFF78AF969 -:101964008A48FFF787F98A48FFF784F98948FFF71F -:1019740081F98948FFF77EF9FFF75AFCFEF784FFE7 -:1019840000F0BAF900F0D0F900210C2201A801F00E -:1019940040F800F0C5F901A90C2200F087FA01A86B -:1019A400FFF768F97348FFF765F97248FFF762F9C2 -:1019B4004FF4804000F096F990B17848FFF75AF957 -:1019C4004FF4804000F082F9082000F07FF91020E5 -:1019D40000F07CF9202000F079F9402000F076F93D -:1019E400FEF74AFEFEF7F6FFFEF7DEFDFEF708FC03 -:1019F40008B16B4803E0FEF747FC10B16948FFF7F4 -:101A04005BFF00F08CF9802000F06CF918B18020A5 -:101A140000F05CF954E04FF4003000F063F9044640 -:101A240020B14FF4003000F051F92EE10120FEF70F -:101A3400ADFFB0B12046FEF7A9FF90B15A48FFF7B9 -:101A440019F941F288340120FEF7A0FF98B1002073 -:101A5400FEF79CFF78B1012000F012FA013CF2D1AC -:101A640013E1524B0CCB013301D0013203D1504866 -:101A7400FFF700F909E1FFF7E0FD18B3FFF7DDFD1B -:101A840010B14C48FFF7F6F84FF4007000F02AF953 -:101A940010B14948FFF7EEF84FF4007000F016F962 -:101AA400102000F01FF9D0B14448FFF7E3F80820F4 -:101AB40000F00CF9102000F009F9FFF797FEF8B9CF -:101AC4003F489CE74FF4007000F00CF90028D5D192 -:101AD400082000F0FBF8102000F0F8F80EE00820D1 -:101AE40000F000F920B13748FFF7C4F8102003E0F4 -:101AF4003548FFF7BFF8082000F0DCF8FFF730FEA8 -:101B04004FF4005000F0EEF804464FF4805000F01B -:101B1400E9F8400040EA84044FF4006000F0E2F881 -:101B2400E4B22043C3B2072B0DD14FF4006000F0A0 -:101B3400CDF84FF4805000F0C9F84FF4005000F095 -:101B4400C5F822485BE70133DBB2023B4FF4006087 -:101B5400052B41D8DFE803F03C4003403C4000F053 -:101B6400B5F84FF4805000F0B1F84FF4005033E072 -:101B740024A80008EDAA000831A8000848A8000815 -:101B840061A800088FA80008BEA80008EEA80008F5 -:101B94001FA9000851A9000883A90008B4A90008D6 -:101BA400014550FE024550FEE1A90008000001086D -:101BB40013AA000826AA00083EAA00085EAA000884 -:101BC400034550FE86AA0008ACAA0008044550FE4E -:101BD40000F07CF84FF4805000F06CF8FFF710FD33 -:101BE400FFF724FD2B4B2C485D681E68FFF758F85F -:101BF4002846FFF763F82948FFF752F800239A00B4 -:101C040002F1604202F5614201334FF0FF31082BCB -:101C1400C2F88010C2F88011F1D1214B4FF4801228 -:101C240000241A635C639C631C645C6400F0DAFC4B -:101C34001C48012100F06EFD21461A4800F06AFD9F -:101C4400F120012100F072FD2146F12000F06EFD2B -:101C54000120014600F076FD2146012000F072FDCE -:101C64001148012100F07AFD21460F4800F076FD6D -:101C74000E48012100F07EFD0C48214600F07AFD5B -:101C840063B64FF0FF3EB54628470948FEF7F2FF1A -:101C940013E700BF00000108D2AA0008E6AA000862 -:101CA400003802400010E022FFC9FEF6337F7704BB -:101CB400EEAA000810B50446002000F08BFD40EAAF -:101CC40004010020BDE8104000F074BD10B50446C6 -:101CD400002000F07FFD20EA04010020BDE8104050 -:101CE40000F068BD10B50446002000F073FD2042EA -:101CF4000CBF0020012010BD08B54FF08050012119 -:101D040000F0F0FC012000F04DFC0120FFF7EAFF99 -:101D140020B90121BDE8084000F04CBD08BD704762 -:101D2400022000F057BD000008B5FFF7F9FF054B8E -:101D34001968884204D00220BDE8084000F03ABD8A -:101D440008BD00BF8401000808B50248FEF792FFF1 -:101D540000F088F80DAB00081FB504460C2201A85A -:101D6400FEF786FC01AB03CB20601868A060204618 -:101D7400616004B010BD0068B0F10C0358425841D2 -:101D84007047000080B50546174610480E461C46AD -:101D9400FEF786FF3846FEF783FF0D48FEF780FF07 -:101DA4002846FEF77DFF0B48FEF77AFF3046FEF724 -:101DB40085FF2CB10848FEF773FF2046FEF770FF3D -:101DC4000648FEF757FFFFF7BFFF00BF2AAB000826 -:101DD40033AB000836AB000834AB0008EDAA0008AA -:101DE4001FB506AA52F8044B039200921A462346E2 -:101DF400FFF7C8FF0CB41FB506AA52F8043B0392C0 -:101E04000092014AFFF7BEFF38AB000807B5002374 -:101E140000937246014BFFF7E3FF00BF3FAB00089E -:101E2400744608B50548FEF73BFF2046FEF746FF1B -:101E34000348FEF71FFFFFF787FF00BF47AB00080B -:101E4400EDAA0008BFF34F8F044A054BD16801F493 -:101E5400E0610B43D360BFF34F8FFEE700ED00E07A -:101E64000400FA0508B5FEF7FBFBFFF7EBFF08B526 -:101E7400FFF7E8FF1623584301387FF4FDAF70479E -:101E840010B504462CB14FF47A70FFF7F3FF013C10 -:101E9400F8E710BD10B5B0FA80F400F029F8012875 -:101EA4000CBFC4F11F00C4F1200010BD0A2A30B5D4 -:101EB4001DDD0C46302304F8023B78234B700023CD -:101EC4009D00C5F11C050F22AA400240EA40092AE0 -:101ED40001D8303202E00F2A02D85732D2B200E0E1 -:101EE4002022E2540133082BEAD100238B7230BD47 -:101EF40000F0AA33A0EB530000F0CC3300F03330F1 -:101F040000EB930000EB101000F00F3000EB1020FA -:101F140000EB104000F03F00704700002D4BD3F859 -:101F2400882042F47002C3F888202B4B1A680021E1 -:101F340042F001021A6099601A6822F0847222F455 -:101F440080321A60254A5A601A6822F480221A6084 -:101F5400D9601A6C42F080521A64214A116841F423 -:101F6400404111609A689A609A689A609A6842F44B -:101F740080529A601B4A5A601A6842F080721A6052 -:101F84001968154A8901FBD5174B40F203611960A2 -:101F9400936823F003039360936843F00203936010 -:101FA4000D4B9A6802F00C02082AF9D19A6822F4BF -:101FB40000029A600D4AC3F884201A6842F08062D5 -:101FC4001A60054B1B681B01FBD5024B4FF00062E6 -:101FD4009A60704700ED00E00038024010300024A1 -:101FE400007000401040010F003C024000300050DF -:101FF400C278037810B50AB3164A4478D268D2433B -:10200400C2F30222C2F10401C9B204FA01F10F249D -:1020140044FA02F28478C9B222400A4303F16043CD -:1020240003F561431201D2B283F80023037801223D -:10203400590903F01F0302FA03F306E059090122C8 -:1020440003F01F0302FA03F32031034A42F821305C -:1020540010BD00BF00ED00E000E100E0014B0122F3 -:102064009A60704700300240014B1860186870474E -:102074000030024000EB81018842044B03D050F849 -:10208400042B1A60F8E71868704700BF003002405C -:10209400014B1868704700BF00300240044B9A6837 -:1020A40009B1104301E022EA00009860704700BFC4 -:1020B400002004E0044B1A69002A04DA034A5A6037 -:1020C40002F188325A607047003C024023016745A0 -:1020D400024B1A6942F000421A617047003C024008 -:1020E400014BD860704700BF003C02400E4BDA68D9 -:1020F400D00310D4DA68D1060FD4DA68D2050ED42E -:10210400DA6812F0EF0F0CD1DB6813F0020F0CBF8A -:1021140009200820704701207047062070470220DC -:1021240070470720704700BF003C024007B50923F1 -:102134008DF80730FFF7DAFF8DF807009DF80730B8 -:10214400012BF7D09DF8070003B05DF804FB0000F5 -:1021540070B5064641B1012908D0022914BF4FF4D5 -:1021640040754FF4007503E00D4601E04FF48075AF -:10217400FFF7DCFF09281ED10F4C236923F44073B9 -:10218400236121690D432561236923F0F803236149 -:10219400236943F002031E432661236943F4803319 -:1021A4002361FFF7C3FF236923F00203236123693B -:1021B40023F0F803236170BD003C024070B505466E -:1021C4000E46FFF7B3FF092811D1094C236923F404 -:1021D4004073236123692361236943F0010323616D -:1021E4002E70FFF7A3FF236923F00103236170BD61 -:1021F400003C024070B543688568846A0168426B9C -:1022040045EA0306C3681E4303691E4343691E432C -:1022140083691E43C3691E43036A1E43436A3343EF -:1022240043EA0406C36A1E43036B334343EA0206CC -:102234008B0003F12043082D1E6003D11D6845F077 -:1022440040051D60B2F5801F836B12D189B14FF038 -:1022540020425668156845F480151560156845F4E4 -:10226400807515601D6926F47006013D46EA055522 -:1022740055601A689D6915435A6845EA02159A68BB -:1022840045EA0225DA6845EA02451A695B6945EAC6 -:10229400025242EA03624E1C4FF02043B4F5804FD1 -:1022A40043F8262010D1C36B1A68986910435A6802 -:1022B40040EA02109A6840EA02201A695B6940EA1F -:1022C400025242EA036201E06FF07042014B43F8AC -:1022D400212070BD040100A0800000F1204019B14C -:1022E400036843F0010302E00268024B13400360F9 -:1022F400704700BFFEFF0F002DE9F0410F68002377 -:1023040001229A4002EA0705954230D15A0003247B -:1023140006689440E443264006600E79D0F800C075 -:1023240006FA02F8013E48EA0C0C012EC0F800C07F -:1023340014D8866891F805C02640866086680CFA31 -:1023440002FC4CEA06068660466826EA05054560F6 -:102354008D7946689D40ADB235434560C5682C40D3 -:10236400C460CD79C46805FA02F22243C260013325 -:10237400102BC5D1BDE8F0814FF6FF730360002335 -:10238400037143718371C3717047036919420CBFB0 -:102394000020012070470AB10183704741837047D0 -:1023A40001F00703C90800EB810010B59B00046A23 -:1023B4000F21994024EA01010162016A9A40114304 -:1023C400016210BD08B5134B984207D14FF40010B9 -:1023D400012100F0C3F94FF4001014E00E4B9842B1 -:1023E40007D14FF48000012100F0B8F94FF48000C8 -:1023F40009E00A4B98420BD14FF40000012100F090 -:10240400ADF94FF400000021BDE8084000F0A6B982 -:1024140008BD00BF0054004000580040005C00406C -:10242400F0B585B004468688324F68460D4600F004 -:10243400FDF802982A68304B26F03F063604B0FBBC -:10244400F7F7360C1FFA87FC4CEA0606A6802188AB -:1024540021F001010904090C9A4221800DD853008E -:10246400B0FBF3F39BB20CF1010C032B1FFA8CFCB1 -:1024740098BF0423A4F820C022E0E9884BF6FF7338 -:10248400994205D102EB4202B0FBF2F39BB206E0A3 -:1024940019235343B0FBF3F39BB243F48043C3F3D8 -:1024A4000B020AB943F001034FF4967257434FF4F9 -:1024B4007A7297FBF2F70137BFB243F400432784E3 -:1024C400A38323886989AA889BB243F001032380EC -:1024D400238823F4816323F002031B040A431B0CA7 -:1024E40013439BB223802A89AB8913439BB2238174 -:1024F40005B0F0BD40420F00A086010041F28833D0 -:102504000360002383804BF6FF72038143814FF401 -:102514008043C28083817047038819B19BB243F022 -:10252400010303E023F001031B041B0C0380704729 -:10253400038819B19BB243F4806303E023F48063FE -:102544001B041B0C03807047828A008B92B221F01B -:102554007F4342EA00401840431A584258417047AA -:10256400014B1860704700BF00300040014B5860B9 -:10257400704700BF00300040014B9860704700BFB7 -:1025840000300040024B4AF6AA221A60704700BF8E -:1025940000300040024B4CF6CC421A60704700BF3A -:1025A40000300040014B1860704700BF20000E420D -:1025B400034B5B6818420CBF00200120704700BF2A -:1025C40000700040B0F5402F054B04D01A6842EA71 -:1025D4008000186070475A6842F440225A6070477D -:1025E400007000400E4B1A6842F001021A6000228B -:1025F4009A60196821F0A85121F410211960094941 -:1026040059600949C3F8841001F18061C3F8881046 -:10261400196821F480211960DA60C3F88C207047AE -:102624000038024010300024003000201D4B9A680E -:1026340002F00C02042A10B503D0082A03D01A4B66 -:1026440018E01A4B16E059685A6811F4800F59685B -:1026540014BF164B144B02F03F02B3FBF2F3114AC2 -:102664005268C2F30142C1F3881101324B43520054 -:10267400B3FBF2F30B4903608B680D4AC3F30313F6 -:10268400D45C0368E34043608C68C4F38224145D23 -:1026940023FA04F484608968C1F34231525CD34064 -:1026A400C36010BD003802400024F40040787D016E -:1026B40001000020044B1A6B09B1104301E022EA27 -:1026C40000001863704700BF00380240044B9A6B47 -:1026D40009B1104301E022EA00009863704700BF8B -:1026E40000380240044B1A6C09B1104301E022EA9D -:1026F40000001864704700BF00380240044B5A6C55 -:1027040009B1104301E022EA00005864704700BF99 -:1027140000380240044B1A6909B1104301E022EA6F -:1027240000001861704700BF00380240044B5A692A -:1027340009B1104301E022EA00005861704700BF6C -:1027440000380240044B9A6909B1104301E022EABF -:1027540000009861704700BF00380240044B1A6AB9 -:1027640009B1104301E022EA00001862704700BF7B -:1027740000380240044B5A6A09B1104301E022EACE -:1027840000005862704700BF003802404309012B23 -:10279400074A01D1136803E0022B0CBF136F536F78 -:1027A40000F01F0023FA00F000F00100704700BFA2 -:1027B4000038024082B000230193054B0193019B32 -:1027C40003EB80000190019B196002B0704700BFC9 -:1027D4005028004082B000230193054B0193019BD4 -:1027E40003EB80000190019B186802B0704700BFA2 -:1027F4005028004008B5254B984207D14FF480502B -:102804000121FFF7B7FF4FF4805039E0204B984285 -:1028140007D14FF480400121FFF7A0FF4FF480401F -:1028240009E01C4B98420BD14FF400400121FFF703 -:1028340095FF4FF400400021BDE80840FFF78EBF2C -:10284400154B984207D14FF400500121FFF792FF36 -:102854004FF4005014E0114B984207D14FF480100C -:102864000121FFF787FF4FF4801009E00C4B9842D9 -:102874000BD14FF400100121FFF77CFF4FF400103F -:102884000021BDE80840FFF775BF08BD00300140D6 -:1028940000380040003C004000340140005001403A -:1028A400005401400B88028810B54C8823438C885F -:1028B4002343CC8823430C8923434C8923438C89A9 -:1028C4002343CC8902F44152234313439BB2038034 -:1028D400838B23F400631B041B0C83830B8A038206 -:1028E40010BD0023038043808380C3800381438120 -:1028F4008381C381072303827047038819B19BB284 -:1029040043F0400303E023F040031B041B0C03804B -:10291400704781817047038919420CBF0020012050 -:1029240070470000038ACA889BB230B523F4405331 -:102934000D46134303820989AB8882890B43698955 -:1029440092B20B4322F4B05222F00C029BB2134316 -:102954008381838AAA899BB223F44073134385B08D -:10296400838204466846FFF761FE194B9C4203D0FC -:1029740003F580639C4201D1039B00E0029BA28982 -:1029840012B2002A2A684FF01901B4BF5200920013 -:102994005943B1FBF2F16423B1FBF3F212011009C4 -:1029A40003FB1011A08900B2002806DAC9003231F5 -:1029B400B1FBF3F303F0070305E009013231B1FB86 -:1029C400F3F303F00F031A4392B2228105B030BD32 -:1029D40000100140838919B19BB243F4005303E012 -:1029E40023F400531B041B0C83817047C1F30801BB -:1029F40081807047038819420CBF00200120704772 -:102A040010B50023934203D0CC5CC4540133F9E7DE -:102A140010BD02440346934202D003F8011BFAE7B7 -:102A24007047FF004C6174746963650069434563D2 -:102A34007562653220323031342E31322E323730E5 -:102A4400353200506172743A206943453430554C34 -:102A5400314B2D434D33364100446174653A204D6A -:102A6400617920313120323031352031393A3139F0 -:102A74003A35350000FF7EAA997E51000105920087 -:102A84002062016F820000720070110001010000D9 -:102A940000000000000000000010000080000000A2 -:102AA40000000200000000008000000000000000A0 -:102AB4000000010000080000000000000000000009 -:102AC40000000000000000200000000000000000E2 -:102AD40000003400020000000000000000000000BC -:102AE40002000000000000000000000000000000E0 -:102AF4000000000000000000400000000000010091 -:102B04000000000040000000000000000000000081 -:102B140000040000000000000000000000000000AD -:102B2400000000000000000580000000000000001C -:102B3400000000030000000000000000000000008E -:102B44000000000000000000000000000000000081 -:102B540000000000800000000000020000000400EB -:102B640080000002000000000000000000080000D7 -:102B74000000000000000000000000000000000051 -:102B84000000000000010000000000000C00000331 -:102B9400000300000000000000000000000000101E -:102BA4000000000000000000000000000000000021 -:102BB4000000000000000000000000000000000011 -:102BC4000000000000000000000000000000000001 -:102BD40000000000000000000000000000000000F1 -:102BE40000000000000000000000000000000000E1 -:102BF4000000000000000000005800000000000079 -:102C040000000000000000000000000000000200BE -:102C1400000000000E080000000002000000000098 -:102C24000000000000000000202000000000000060 -:102C34000000000000000000000000000000000090 -:102C44000000000000000000000000000000000080 -:102C54000000000000000000000000000000000070 -:102C6400000000000000000000000001000000005F -:102C7400000003000000000100000000000000004C -:102C84000000000000100000000000000000000030 -:102C940000000000000080000003000000000000AD -:102CA4000300800000030000000000000000000892 -:102CB4000000002C000000000000000000000000E4 -:102CC40000000000000000000000000000001800E8 -:102CD4000000000200000000000000000000007876 -:102CE40000100000000000000000000000000000D0 -:102CF4000000400000000000000000000000400050 -:102D040000010000000000000000000400000000BA -:102D140000000000000000000000000000000000AF -:102D2400000001000000000000000000000001009D -:102D34000000000000000000000000081000000077 -:102D440000000000000000000000000000008005FA -:102D5400800000000000000000008000000000006F -:102D64000000000000000008003000000000000027 -:102D740000000C3020008000000000000014280037 -:102D840000000000781800000020735A08000000BA -:102D940000030002000000001DDF0C000000000022 -:102DA4000C30000000000000000000366C00000041 -:102DB400004008FB0000001C025C000080000000D2 -:102DC4002C000000000000E0F100000000000C30C6 -:102DD40020000000000000000000000000002800A7 -:102DE4003808000000000058080000000007000236 -:102DF40000A000001C8F0C00000000000C3000003C -:102E0400000000000000003400000000200008075B -:102E14000000003C3F490000010000003C0000802D -:102E240000000000F110000000000C302000000041 -:102E3400000000000200000000001000000000007C -:102E44002004017C00020000008300020000000650 -:102E5400168F0E00000000000C300000000000007F -:102E640000000000000000001000700B0000003C97 -:102E74003D50020000000100B80000000000000006 -:102E8400F000000000000C302000000000000000F2 -:102E9400001400000000000000E80000000006DE4E -:102EA4003C0200000003840200000266548F0C0000 -:102EB400000000000C3000000000000000000002D0 -:102EC4002C00000000000001000000243BF038004A -:102ED400000500003C00000003C20200F1000000F5 -:102EE40000000C3020000000000000000000000082 -:102EF4000000000001E00000002002FE500000007D -:102F0400D9A38002000000001E8F0E1000000000F4 -:102F14000C30000000000000000000000000000071 -:102F2400001070E30100003C27E000000000B00046 -:102F34003000000000000000F000000000000C3031 -:102F4400200000000000000000000000000000005D -:102F5400800000000000280000000000D9B38002B7 -:102F6400000041C0168F0E00000000000C3000006D -:102F7400000000000000000000000000000030E736 -:102F84002000000000000000000050A080000000AD -:102F940000200000F010000000000C3020000000B1 -:102FA4000000000000000048E00000007000040081 -:102FB4000000000000000000101380020000026006 -:102FC400168F0E00000000000C30000010000000FE -:102FD400000000000258F000000038072C00000038 -:102FE40000000000000008A0B8000000004002003B -:102FF400F000000000000C30200000000000000081 -:1030040000000378A50230000000000000206C00DE -:1030140000000000001023C000C003C01685560045 -:10302400000000000C300000000000000000000060 -:10303400034052201000700B2800000C38000000E0 -:10304400000000C003C00000A060020550000000A2 -:1030540000000C3020000000000000000000014CC3 -:10306400F0C000C000000000003C284E0F800000AB -:1030740000C38002000000029C05A80000000000BC -:103084000C3000000000000000000000001F0F20B2 -:10309400014000900000000400080F000000000040 -:1030A40000000800004294DA5000000000000C30D8 -:1030B400200000000000000A000001C8F0C0004029 -:1030C4000400000A000005CC0F8000000007800205 -:1030D400000001429C80F800000000000C30000059 -:1030E400000000000008000000000F200040000065 -:1030F400000800142C1F0F0000000000B80000009E -:10310400094281F0F000000000000C3020000000B3 -:103114000000000000206548F0C01001000000001D -:10312400001C055E0F80000030058002000001468F -:103134005480F800000000000C3000000000000083 -:103144000000000420200F20100000000000000CEC -:10315400202A0F00000000003800000000020300D5 -:10316400F000000000000C3020000000000000000F -:1031740000060148F0C0000000000000000601DC69 -:103184000F80000000070402000000601580F800B2 -:10319400000000000C3000000000000000000000EF -:1031A40000200F2000000000000000003C300F0051 -:1031B40000009000A800000000000200F0000000E1 -:1031C40000000C302000000000000000000031E886 -:1031D400F0C0000000000010002001C15A8000006F -:1031E40000058402000000001F80F80000000000B9 -:1031F4000C300000000000000000000000000F2060 -:10320400000000000000001C3CECA50000000000D1 -:10321400380000000002C080F000000000000C3004 -:10322400200000000000000010000168F0D0004001 -:1032340000000000102001780F80000078150402BF -:10324400000101401790F800000000000C3000005D -:10325400000000000000000000000F00000000005B -:10326400000000043C100F00000000802800000053 -:10327400002001C0F000000000000C30200000001D -:1032840000000000000631E8555000000000000076 -:10329400000661EA0F80000079E17C3E00000260D4 -:1032A4001F80F800000000000C3000000000000047 -:1032B4000000000000205500002800000000003C31 -:1032C40028200F0000005900F43C00010042C30014 -:1032D400F000000000000C30200000000000000C92 -:1032E4000020040000000000000000000000066848 -:1032F4000840800079E023C000000200408F0200F3 -:10330400000000000C300000000000000008100461 -:1033140020000000000000000000003C23490500DC -:10332400400008A003C000000042000F000000009D -:1033340000000C30201020007A3C20000000001C0B -:10334400F040000000180000000000000000000031 -:103354000003F954000000662FC5F00000000000CF -:103364000C30000000000803C09000002C10F00195 -:10337400004000F70000000000000000000000B062 -:10338400C154000000003D05F000000000000C30B6 -:1033940020000000323C200A001E0200A540280044 -:1033A4000008000A0000000000002800000379FC67 -:1033B40008A001E681AF0400000000000C3000000A -:1033C40000000003C048000000405A012000F0073C -:1033D40000080000000000002000F003C2FC02808E -:1033E4000342C00F0000000000000C302000000069 -:1033F4003A3C204C0000281CF0401010001000043F -:1034040000003800000010008083FE0420C041400A -:1034140000000000000000000C3000000000000369 -:10342400C00000344000F001102000CF000C080060 -:103434000000000030001189FD0000C0036000009E -:103444000000000000000C3020000000323C20008E -:1034540000140008F040000000000000000000001C -:103464000000000000017A002008004781AF04003A -:10347400000000000C30000000000803C00000360B -:103484000010F00000000007000000000000000031 -:1034940000000007EA0000041424010F00000000EB -:1034A40000000C3020000800723C20000000000ADC -:1034B400F0400000008000100000001EF0400000FA -:1034C40038052DFC00000000018F040000000000FE -:1034D4000C3000000B0A0003C00000000000F000E4 -:1034E4000000F1BB020004000000F00000000889A5 -:1034F400BEFC2000A000000F0000000000000C3003 -:1035040020040100723C2000100E380AF0400040F4 -:1035140000000000100020000000004001817A80BB -:103524000001426635800000000000000C300000FD -:1035340000800803C00000020010F0000000000D2D -:103544003C0000340000000000000005C000200022 -:10355400004025A10000000000000C302004000AF7 -:103564007A3C20001000001AF040000000A0040083 -:103574000000001CF04000000000000000001407E0 -:1035840001CC0220000000000C3000040500000300 -:10359400C00010000000F0000000D1F33C00000067 -:1035A4000010F00000001000000000000800010CF2 -:1035B4000008000000000C30200005007A3C2000C8 -:1035C4000000001AF040100050A004000020001C6D -:1035D400F060000001A063C10000100707C0801064 -:1035E400000000000C300000050E0003C0001000B5 -:1035F4000000F0001000D9BB3C0000240000F000E3 -:10360400000000A003C00400000424800080000027 -:1036140000000000000000007A3C200000262A5B25 -:103624004240004000000000001C00ED5A0000046D -:1036340001E000000002000000AF040000000000F0 -:103644000000000000000803C1000014024CE20066 -:10365400404000000000000001CC1A010000F0907E -:10366400000000040000010F000000000000000042 -:10367400028005007A3C200A001E7078AF100040DA -:103684000000000A0020006AA500280000A7EA0044 -:1036940000A00000418F04000000000000000200B0 -:1036A40000800803C008003E2020AF100040000046 -:1036B40000080004015055002000000F6C00008039 -:1036C4000003810F00000000000000000300000060 -:1036D4001A15400C0016024C304230000000000461 -:1036E4004020396AA00010000193382800400003EC -:1036F400C0000000000000000000000000000015F1 -:10370400400400000270B10090000000000C00347E -:103714006370F50010000080002820C06003C00022 -:1037240000000000000000000000000007BC1000C2 -:103734000014287B4240000000000000000000FC50 -:103744000E02000000F5F8000800E0000000000090 -:1037540000000000000000000000043C00200014F1 -:10376400286CD100800000000000001428D00F0055 -:10377400000000FF810000000000000000000000C5 -:10378400000000000000000007BC10000000625BA5 -:1037940042400000001000002014000AF040000025 -:1037A4005801FA010800000000EF040000000000C6 -:1037B400000000000000043C08000000665DE20018 -:1037C400800000F00200E0000000F00000000000B3 -:1037D400C20000000000010F000000000000000013 -:1037E40000000000802950040026647B4240000051 -:1037F40000000000863604000000000000012C8157 -:1038040000000000000000000000000000000000B4 -:103814000000101680040026647DD10080000000A2 -:103824000000001428000000000000097C000000D3 -:103834000400000000000000000000000000000080 -:103844000000000000000258424040000000000058 -:103854002006005AF50000008005F805000000006D -:10386400200A5400000000000000000000000000D6 -:10387400000000000248E200000000000000000018 -:103884000340A000000050C1EC00000000021C0531 -:10389400A0000000000000000000001B0000000069 -:1038A40000000159014040000000000080000000B9 -:1038B400000000007810000000000040000000003C -:1038C4000000000000000000000A000000000000EA -:1038D4000059A7000000000000000E0000000000D6 -:1038E400000008A00000000000000000000000002C -:1038F4000000000000000180000000000000014AF8 -:10390400F0D000000008000000002E1CB1000102ED -:10391400001869550000000240000000000000008B -:1039240000000000008A0000000000002C190F00B5 -:10393400044000070000000038DF7200028000D954 -:1039440042A800080007400000000000000000003A -:103954000280050076A8000A000001C8F0C0280013 -:103964000008000A002003FCAF2328007818029501 -:1039740000B401E017CAF4000000000000000200D7 -:103984000100F4000008000000000F002000000700 -:103994000008201C0150DF00200008C101680082DB -:1039A40000201700A1000000000000000300001F19 -:1039B4003F99C480002061C8F0C0100000C0000C12 -:1039C400001E001CF040300050A03BC1804203C7E1 -:1039D4009DCA50000000000000000300000DD03F0D -:1039E400C00000042C200F003000310F000C000038 -:1039F4000000F000100000A043C0004401E43505BD -:103A040051000000000000000100001A1B191A10E8 -:103A140000060148F0E001000000000001200268F7 -:103A2400A50000800010000000000167BDEA02004C -:103A34000000000000000100001032160200000027 -:103A440000200F0002000007000000042B50550066 -:103A5400010000900000000000202E0F5000000024 -:103A6400000000000000010012DD1000000001E869 -:103A7400F0C0000080900000000005EE200201006C -:103A8400000DE005000A01668E8A020000000000B5 -:103A9400000000000108B6140A40000000000F00F6 -:103AA4000020709B0000000020202000028000897C -:103AB400AC0000040002EF0FA000000000000000B2 -:103AC400000005801640500000000168F0C00000AE -:103AD40080000000000466CEA5000000000D628195 -:103AE4000000000716AA50000000000000000000BB -:103AF40001001A29C860000000000F000000300512 -:103B04003C000E3620F055000000000D400000007F -:103B140000073C0AA00000000000000000000D8F18 -:103B240093DD1600000605E855400000819004006E -:103B3400006039E87002000030057A0100000807CF -:103B44004D8A000000000000000000000D1A32142D -:103B54000020000020205500000091E33C00009468 -:103B64000150F00000000099F80000080346D40F4B -:103B74005000000000000000000008000000000CDD -:103B84000020040000003000999004000036041C5A -:103B9400F0400001000000000000004795EA00002A -:103BA4000000000000000000050000000008000400 -:103BB400200000001000999B3C000C142800F00029 -:103BC400000200800000000000242D0FA00000006F -:103BD40000000000000000000000000000002000C1 -:103BE40000000040000000000020054B70000000B1 -:103BF40000007BC100000003B59205000000000036 -:103C04000000000000000000000000004000000070 -:103C140000400090000000242368F00000000080B1 -:103C240043C00000000724CE000000000000000094 -:103C3400000000000000000000340000000000400C -:103C44000000000A002002FABD03280001E0000081 -:103C540000A00242CF81C00000000000000000006C -:103C6400000000000000002C2800000000400000BC -:103C7400000800240160FD0820000100000000800D -:103C8400006217C3C0040000000000000000000030 -:103C94000000000000000208500010000000000CAA -:103CA4000020641EF0401000000063C10000020305 -:103CB40025AB94000000000000000000000000009C -:103CC400000000000050A00010000000000C002CB8 -:103CD4002400F0001000000043C0000000401793CF -:103CE40080800000000000000000000000000000D0 -:103CF4000000000000000000000000000026064E46 -:103D0400A5000000000000000000014035E8D404D4 -:103D1400000000000000000000000000000000148B -:103D240000000000000000000000000625F055001F -:103D340000000000000000000002A698E00000005F -:103D4400000000000000000000000000000000006F -:103D54000000000000000000000072DCF50000001C -:103D6400000000000010016000EF040000000000EB -:103D7400000000000000000000000000000000003F -:103D8400000000000000001C00F0500000000000D3 -:103D9400000000100000000F000000000000000000 -:103DA40000000000000000000020000000000000EF -:103DB40000000000002066DEA500000000000000F6 -:103DC40000000200018F0410000000000000000049 -:103DD400000000000000003C0000000000000000A3 -:103DE4000000002423D0AA0000000000000000000E -:103DF4000143C00F020000000000000000000000AA -:103E0400000000000000027B100000000000000021 -:103E14000E00701A8002000000000000000A00007A -:103E240027E0540000000000000000000000000033 -:103E340000000000024C2401000800000000000003 -:103E44000050400000000000000000000040240A70 -:103E5400000400000000000000000000000000005A -:103E64000000000000000100000000000000381CF9 -:103E7400F0400000001000000000036007D40000C0 -:103E8400000000000000000000000000000000002E -:103E94000000000002000000000000342400F000D4 -:103EA400000000D00000000001601680000800003F -:103EB40072009011010101000000000000000000E8 -:103EC400000010000000000000400000000000019D -:103ED4000000000000400002000000000000080094 -:103EE40000000000000000000000000000000000CE -:103EF400200000000000008000000000000200001C -:103F040000000000000000000000000000000000AD -:103F14000800000000000000000000000000000095 -:103F2400000000000000000000000000000000008D -:103F3400000000010000000000000400F000000088 -:103F4400000000000000000000000000000000006D -:103F5400000000000000000000000000000000005D -:103F6400000000000000000000000000000000004D -:103F7400000000000000000000000000000000003D -:103F8400000000000000000000000000000000022B -:103F9400000C0000000008000000300000000000D9 -:103FA400000000000000000000000000000000000D -:103FB40000000000000000000000000000000000FD -:103FC40000040000000000001000000000000000D9 -:103FD40000000000000000000000000000000000DD -:103FE4000034000000000000000000000000000099 -:103FF40000000000000000000000000000000000BD -:1040040000000000000000000000000000000000AC -:10401400000000000000000000000000000000009C -:10402400000000000000000000000000000000008C -:10403400000000000000000000000000000000007C -:104044000000000000000000000008000000000064 -:10405400002000000000000000000000000000003C -:10406400000000000000000000000000000000004C -:10407400000000000000000000000000000000003C -:10408400000000000000000000000000000000002C -:1040940000000000000000000000000200000B000F -:1040A400010000000000000C0000000000000000FF -:1040B40000000000000000000000000000000000FC -:1040C4000000000000000000000100800800010062 -:1040D40000020000000800000000000004000000CE -:1040E40000000000000000000000000000000000CC -:1040F40000000000000000000000000000000000BC -:1041040000000000000000000000000000000000AB -:10411400000000000000000000000000000000009B -:10412400000000000000004000000000000100004A -:10413400000000000000000000000000000000007B -:10414400000000000000000000000000000000006B -:104154000000000000000001000000000002040054 -:10416400000000000000000000000000000000004B -:10417400000000000000000000000000000000003B -:1041840000000080000000000002000E000000009B -:1041940000007000000000000000000C300000006F -:1041A400000000000000000000000000000000000B -:1041B40000000000000000000000000000000000FB -:1041C400C00100F000000000000C302000000000DE -:1041D40000000000000000000000000000000000DB -:1041E40000000000000000000000000000000601C4 -:1041F400C0F000000000000C3000000000000000CF -:1042040000000000000000000000000000000000AA -:10421400000000000000000000000000000000F0AA -:1042240000000000000C302000000000000000002E -:10423400000000000000000000000000000000007A -:10424400000000000000000000000001C0F00000B9 -:104254000000000C3000000000000000000000001E -:10426400000000000000000000000000000000004A -:104274000000000000000001428000F00000000087 -:10428400000C3020000000000000000000000000CE -:10429400000000000000000000000000000000001A -:1042A4000000000000036041C0F000000000000CAA -:1042B40030000000000000000000000000000000CA -:1042C40000000000000000000000000000000000EA -:1042D40000000000003C0AF000000000000C302048 -:1042E40000000000000000000000000000000000CA -:1042F40000000000000000000000000000000000BA -:1043040000000021E5F000000000000C3000000077 -:104314000000000000000000000000000000000099 -:104324000000000000000000000000000000000089 -:104334000000000000000000000C3020000000001D -:104344000000000000000000000000000000000069 -:104354000000000000000000000000000000000059 -:10436400000000000000000C30000000000000000D -:104374000000000000000000000000000000000039 -:104384000000000000000000000000000000000029 -:1043940000000000000C30200000000000000000BD -:1043A4000000000000000000000000000000000009 -:1043B40000000000000000000000000000000000F9 -:1043C4000000000C300000000000000000000000AD -:1043D40000000000000000000000000000000000D9 -:1043E40000000000000000000000000000000000C9 -:1043F400000C30200000000000000000000000005D -:1044040000000000000000000000000000000000A8 -:104414000000000000000000000000000000000C8C -:104424003000000000000000000000000000000058 -:104434000000000000000000000000000000000078 -:10444400000000000000000000000000000C30200C -:104454000000000000000000000000000000000058 -:104464000000000000000000000000000000000048 -:1044740000000000000000000000000C30000000FC -:104484000000000000000000000000000000000028 -:104494000000000000000000000000000000000018 -:1044A4000000000000000000000C302000000000AC -:1044B40000000000000000000000000000000000F8 -:1044C40000000000000000000000000000000000E8 -:1044D400000000000000000C30000000000000009C -:1044E40000000000000000000000000000000000C8 -:1044F40000000000000000000000000000000000B8 -:1045040000000000000C302000000000000000004B -:104514000000000000000000000000000000000097 -:104524000000000000000000000000000000000087 -:104534000000000C3000000000000000000000003B -:104544000000000000000000000000000000000067 -:1045540000009000000000000000000000000000C7 -:10456400000C3020000000000000000000000000EB -:104574000000000000000000000000000000000037 -:104584001000000000000000000000000000000C0B -:1045940030000000000000000000000000000000E7 -:1045A4000000000000000000000000000000D00037 -:1045B400000000000000000000000000000C30209B -:1045C40000000000000000000000000000000000E7 -:1045D40000000000000000000000000010000000C7 -:1045E40000000000000000000000000C300000008B -:1045F40000000000000000000000000000000000B7 -:104604000000000000000000000000000000006046 -:104614000000000000000000000C3020000000003A -:104624000000000000000000000000000000000086 -:104634000000000000000000000000000000000076 -:10464400000000000000000C30000000000000002A -:104654000000000000000000000000000000000056 -:104664000000001000100000000000000000000026 -:1046740000000000000C30200000000000000000DA -:104684000000000000000000000000000000000026 -:104694000030008000000000000000000000000066 -:1046A4000000000C300000000000000000000000CA -:1046B40000000000000000000000000000000020D6 -:1046C40000000000000000000000000000000000E6 -:1046D400000C30200000000000000000000000007A -:1046E400000000000000000000000000002800009E -:1046F4000000000000000000000000000000000CAA -:104704003000000000000000000000000000000075 -:1047140000000000000000000000000000000B008A -:10472400143000000000000000000000000C3020E5 -:104734000000000000000000000000000000000075 -:104744000000000000000000000000000D214100F6 -:1047540000000000000000000000000C3000000019 -:10476400000000000000000040DB0000000000002A -:10477400000040034DA5008000108D80140000004F -:104784000000000000000000000C302000000000C9 -:104794000000000000024E5F020000000000000064 -:1047A4000000ED02400000819FA1430200000000D0 -:1047B400000000000000000C3000000000000000B9 -:1047C40000000403D00000000000000000014002CB -:1047D400DBF500010010098014000000002C80802B -:1047E40000000000000C3020000000000000000069 -:1047F4002C785A0A00000000000000000602DAF4D7 -:10480400000280980FA14300000000248B5408008C -:104814000000000C30000000000000000000040054 -:10482400000000000000000000003C3CD00001003B -:1048340000000D001400000002000F000000000042 -:10484400000C3020000000000000000020000000E8 -:1048540000000000000000002061D80202000000F7 -:104864000DA14300000000568F0800000000000C5A -:104874003000000000000000002000400050000054 -:104884000000000000001E00200A000000000900D3 -:10489400140200E000020F0000000000000C3020B1 -:1048A40000000000000000400039D8500000000063 -:1048B400000000001C00EA0A200000000FA14300D1 -:1048C400000000048F0800000000000C300000000D -:1048D400000000000000003B400800000000000051 -:1048E400000000316D0010000000AD801400004293 -:1048F400C0000F0000000000000C30200000000089 -:10490400000000000062F81802000000000000002F -:1049140000046F08000000181D2143000003400636 -:104924008F08000A0000000C300010000000000096 -:1049340000000002500000000000000000000424F9 -:10494400000000100000898014000002C0000F0065 -:1049540080040000000C3020000000000000000073 -:1049640006024CA020000000000000002078000097 -:10497400001000019D214300000340168F08000031 -:104984000000000C3000000000000000080014309B -:104994004000000000000000080200274A850020B3 -:1049A4000000AD001400800140000F000000000072 -:1049B400000C30200000000000000A001E065850C1 -:1049C4000000000000000A000005CC40402900005F -:1049D4001F214300A00000148F0800000000000CF9 -:1049E400300000000000000000003C036A52100088 -:1049F4000000000000000000E94200000000A900DF -:104A0400144A00014000000020000000000C302087 -:104A1400000000000000000C0E624E7A400000000E -:104A24000000000000014B50400000000F21430033 -:104A34000001C29400080000000000000000000013 -:104A4400080821402000000020A0200000000000F1 -:104A54000000003AEB80000138008165000000028C -:104A64004000000000000000000000000000015EA3 -:104A7400A140800000014AA00000000000000000E6 -:104A84000006FA00000000019764000000026000C4 -:104A940000000000100000000000000000D01080A2 -:104AA40040000436EDA500000000000000001C02D8 -:104AB40060CB00810001077F0C000001C4000000EE -:104AC400000008000000000000000C0020448000EA -:104AD400046579A74000000000000000266AEAC3CC -:104AE40042028031C3FF4D050002638000000000D4 -:104AF400000000000000000D0B9E000000000020DC -:104B0400200A0000000000000000447ACF8000006A -:104B140000090581002000024014EC4080000000E0 -:104B240000000000000B0113804000100004F80A8C -:104B340001000000000000102027790008004078E0 -:104B4400A128010801020017FD0400000000000074 -:104B5400000000500BF4200000001C3420A02804A6 -:104B64000000000000000001E0D33840007003E8BA -:104B7400000000000034C8D0000000000000000065 -:104B8400002001120000800020795AA020040000B7 -:104B9400000000000000EA4342000000016C200015 -:104BA40000000016B814000000000000000000001F -:104BB40080DB000000001633CD240000000000005C -:104BC40000000242E00000000008B1E6900000008E -:104BD40062E4A5C0800000000000000000070A3A5B -:104BE400C04000003E004C10000000000000000126 -:104BF4000E2B5C5000000050076590000003400736 -:104C0400CA0600000000000000000000885B4000AD -:104C140000001676607600000000000000200434D6 -:104C2400005F000000580D4294800000441D901065 -:104C3400000000000000000000038857420000004C -:104C44002E74CAB1620000000000004000344E5FC0 -:104C540000000050A3F3E800000202B7B000000017 -:104C64000000000000000000000402800800343B43 -:104C7400E01800000000000008001C22FEA50860E7 -:104C84000000A5810000800343C40AC000000000A6 -:104C9400000000000000003B02800A003E05DA0824 -:104CA4000200000000000A000007DA405829000151 -:104CB400D1280108A000035DCA0420000000000000 -:104CC400000000000854140000003C21D9800000BA -:104CD4000000000000000203EB5A00000201B3E8E8 -:104CE40000000000203E05A00000000000000000BD -:104CF40000018B87A80000002005CF000000000001 -:104D0400000000001400EF4050000518A16E0000E0 -:104D140000014606EF50000000223D080009000093 -:104D2400009F000000002402CB40000000000000AF -:104D34000090003749A109000001D00000000000E4 -:104D4400003CE800000000C03F800021000300F2A6 -:104D5400E000000000637800000000000000000094 -:104D640000026D4241003801C00000000001E0165D -:104D7400C00000000000140A000000000093E000DE -:104D840000000002C0000800000000000000804095 -:104D9400F80A080040399DB31C010012C204ADD0CA -:104DA4000000000024A0000000001C3E00000000E1 -:104DB4001E006C80020000000000000040796B4A75 -:104DC400500000D1FDF70D0000E8406EEEF0000049 -:104DD4000030000000000000003640802000004247 -:104DE4004000000000000000000000027D001000F0 -:104DF40000D10B4000400001E0059200040000C017 -:104E040000000000040500824040001000735A02B4 -:104E1400020040000000001000206C08000040392F -:104E24008BB9400001014007A006000000400000CB -:104E340000000800001C00000020027C700000003C -:104E44000000000000003C61600000000008DDF08C -:104E540000010000401504001000000000000000E4 -:104E640010001C1B200080401C235CA000000000DC -:104E7400000000000E75FAA0400000100FBC208056 -:104E84000002000EA000200000000000000000004E -:104E940000500140000014796E0000000000000082 -:104EA40000003C60000000000058876430000001EE -:104EB400603C0A00000000400000000000039C5F0A -:104EC400800800002E3C4F08200000000000000075 -:104ED40000280000000002B9B5EC200000034604DD -:104EE400C000000002400E8FA00028018054000082 -:104EF4000000363760000000000000000000822E31 -:104F0400703C080000008780D000000366CDEC8070 -:104F1400100002001FDE7080138B9E3E200080A0D4 -:104F24003E06DA80020000000000020754017C7C87 -:104F3400530000001568F50A000142B4FD040000A6 -:104F44000343400000020009905C140000000C417F -:104F54006B0000000000000008013428F058002015 -:104F64000001094014008003601E840080000200D8 -:104F74004000000280070E168000000036704F02C9 -:104F84000000000000000A00A0616E18022805005D -:104F940088782800A0014015981000000020000027 -:104FA400000000050E0003C04000002BDA003100B1 -:104FB400000000000000803ED00A000100000B0049 -:104FC40000000002E41CCC801000C060000000005F -:104FD4000000010283D200000002D8040000000097 -:104FE4000000000140281A0500408010013028000C -:104FF40000014295AC24000000623D000100000065 -:1050040009DC3CC0000CA425FD8000010000000068 -:1050140001001C227A01200400909782940000026F -:10502400C380000000000040358050100000091BC0 -:105034003DC080016006CB0010028000000000002B -:1050440020066F07600000819FE1652800020000D0 -:105054000000000000220D000280204F8098000014 -:1050640000000063EA0020000000000001002C039F -:10507400D0E300000000E5000C000000000100F097 -:10508400000001C03EE80030000B887EA0008000D4 -:10509400006459400001280000000107A0206AC3F1 -:1050A4006300007811B0080800000000C0F500009B -:1050B40000420300A00000000CD4000010001C7685 -:1050C400700000000000000000003E3CFF030000F0 -:1050D4000071BB801400000000000000000002000A -:1050E4000FA0A00000000097008E800020605A806E -:1050F40012000000000000002E7E4F02000000009D -:10510400AB3168000000000000000000014006E030 -:10511400000000030AF40000000002241050000004 -:10512400000000000000023EC800000000F1DDF8AD -:1051340000811000034000000000E36015B020006F -:105144000000089202800000063C7E50000000002F -:10515400000000001E02DE80000000D9DF320100E2 -:10516400000007C00000000002440CA00000000D75 -:105174000A5600000001027C00000000000000004C -:1051840000003C015E5B000000F100000000001024 -:10519400001E8000880002021CC801000008001FD5 -:1051A400A00000001C7000000000000000000000CF -:1051B4002000EEA540000001E00000000008C0252A -:1051C400A806100001624600000000050A1B5000FA -:1051D4000000343AD0E3010000000000000004267F -:1051E400E000000000010BF00000000000000000DF -:1051F4000000006066C2002003800113C0000000AC -:10520400006F5AC352000000000000002000ECA010 -:1052140000000830CFE40840000000000000000057 -:1052240003424505F000000B0BB402881800BC02D1 -:10523400DF0300000000000008000069D0C3112053 -:10524400005005F7FC00800002E7F0200000020295 -:10525400C08AF0000008003380000A071C00FC022A -:105264000000000000000A001E70EEC7422800B1D2 -:10527400ADA1FD20A000000D9000000002434000FD -:10528400000000070BF780003001002BD80000005D -:105294000800000000001C27C92500000000CDF410 -:1052A4000060000027400000000003C04000000030 -:1052B40000050A9200800000803CCB08100000002A -:1052C4000000002006005C000000000001FC20003B -:1052D4000001E2C00000001000002600500300009E -:1052E40008B0294041002E640000000002000000C4 -:1052F4000000AC7C0000000080000B3108040000BA -:10530400000200A0001000002F800003000581802F -:1053140016944100262C000000000000000000004C -:105324007C20000000010000000605420000000F80 -:10533400C0A480000142830020800001000C25806D -:1053440000003401D0A020000400000000001E4230 -:10535400E0AF21000000804280020003C0010A0087 -:105364001400000044A020300009803FA5905000A4 -:1053740000721E5000000000000000003C72EEA508 -:105384004000007013B280000000500C8A05000039 -:105394000142BC00000000000000000000000441C5 -:1053A400F0FF20000000000000001C216B10000032 -:1053B4000008000000000A01C7DE8FF0840002002C -:1053C4004EC0A00000001800000000001C637EF71F -:1053D4000200000000000000206DFC0000000071CD -:1053E4008000000004020786E7F50000014004087D -:1053F400020000000000154000003E40E0C3000031 -:105404000000800100000C00601A00000000000190 -:105414006802182023800FF0840003402CE0012050 -:105424000000005A15500002B472DCE302000400CC -:1054340000000040006BF85A02000018E101680007 -:10544400100142000FF100000000340010100001B0 -:10545400000803C0100E006F40E500003A00000091 -:105464000000000030A000000200CD810800100000 -:10547400040590019000000000C022240008001AD6 -:105484003C380000003F4CA5020000000000000072 -:1054940000006EA0000004000D3B8D881000021C6B -:1054A400D014000000024F0000000000080803C0F0 -:1054B40000000203C8000010000000000000243CAB -:1054C400000000000000008140000000002682006F -:1054D4000C0020007C850000000001523C30000CD0 -:1054E4001669CD8000308000000000280000000014 -:1054F40000000000C3B140000000000D9344000010 -:10550400024424B0000200000F0003C040003C2A03 -:10551400F80408004000000008423C7DC000002060 -:1055240000000FAF6840800267C6D5810000020604 -:1055340005840002800001723C30200020066A00CD -:105544000000400000000A00062FDE0212280000BE -:10555400053E0564A0026216F984800000440DC073 -:1055640042000000000003C14000000000000000F1 -:105574004000000000804002FC0000010008000020 -:1055840000000A00020000000000006624E002009F -:10559400000000723C3020041600000000004000AF -:1055A400000000008002D808000200500000000043 -:1055B400000146400000000001400000000300011B -:1055C400000803C0100800400000001000000B2475 -:1055D4000800000000000000021080815400000058 -:1055E400000200A00000000000000003000D187A73 -:1055F4003C306000002800000010000000040C0093 -:10560400000000000030008015A1550000000005D6 -:10561400C0A000000142800000000000000803C098 -:105624001000000000000000000001300000002015 -:1056340000000000000000803C00000023E5D000D2 -:1056440010000006800000000001805A3C30600019 -:1056540000000000000000300004004A00040000C4 -:105664000000001807A3C30000014077C800000031 -:105674000000000000000280000003C010000021B0 -:105684007C00010000700D240000000000000001F7 -:10569400200000003C0000014436004000000000EF -:1056A400000000000000005A3C3060000073FC4021 -:1056B40000000080080000000000000000028000DC -:1056C40005A3C300006203B7A00020000002BCD001 -:1056D40082000000000003C010001C2C0000000029 -:1056E400000109000000004000000000000000006C -:1056F4003C00000007870050000000005EE000004E -:105704000000007A3C3040002E0000000000000041 -:1057140088000002003800000000000007A3C30056 -:105724000001E764AFA000000000000000000000DA -:10573400080803C05000142B500000000001BD00F5 -:1057440000000000000000000000E0803C000202B5 -:1057540042A5B0F0000000000000000000018152EA -:105764003C30002036077E0A300000009000000024 -:1057740000000000000000199523C300040166D452 -:10578400A0B000000000000000000000080803C0F2 -:105794004000000020A000102000E70000000400EA -:1057A4003050A000001080803C000000029C005299 -:1057B4001000000000000000000019523C320000FC -:1057C400000178A000100000100000002000CE505E -:1057D400400000819523C30000016756C8B0A800AB -:1057E40000003FE0011200001B0003C01000000293 -:1057F400D000002000F8F10008000002E0A580209D -:105804000000E0003C00000543430A0014000000CF -:105814000CF1000280000A723C3020001602C8809D -:1058240022A80071E8000A080000400060288000F7 -:105834001723C30000020006CA0000000400000091 -:1058440000000000000A400001020023CD00000017 -:105854004011FD000000003C205090000000006456 -:105864003C00000002C30500080000000000000026 -:10587400000000184020010016065A100000000025 -:10588400F80000000004CA5040000000056FC30087 -:105894000000069CC50100000072007011020101A5 -:1058A40000000000000000000200000000000008EA -:1058B40000000000020000000000000800000000DA -:1058C4000000200000000000008000000000000034 -:1058D40000000000000001000000000000040000BF -:1058E40000000100000000000000000000000000B3 -:1058F4001000000000000000000000000000000094 -:105904000000000000000000020000000000000889 -:105914000000000000000000000000000000000083 -:105924000000000000000000000000000000000073 -:105934000000000000000000000000000000000063 -:105944000000000000000000000000000000000053 -:105954000000000000000000000000000000000043 -:105964000000000000000000000000000000000033 -:105974000000000000000000000000000000000023 -:105984000000000000000000000000000000000013 -:105994000000000000000000000000000000000003 -:1059A40000000000000000000000000000000000F3 -:1059B4000000000000000000000000000000800063 -:1059C40000000000032000000000000000000000B0 -:1059D40000000000000000000000000000000000C3 -:1059E4000000000000000000000000002000000093 -:1059F400000000000000000000000000000000069D -:105A04008000000000000000000000000000000012 -:105A140000000000000000000000C00000000000C2 -:105A24000100000000000000000000000000000071 -:105A34000000000000000000000000000000000062 -:105A44000000000000000000000000000000000052 -:105A54000000000000000000000000000000000042 -:105A64000000000000000000003000000000000002 -:105A74000000000000000000000000000000000022 -:105A84000000000000000002000000000000000010 -:105A940000000000003000000000000000000000D2 -:105AA40000000000000000000000000000000000F2 -:105AB40000000000000100000004000000000000DD -:105AC4000000002000000040000000000000000072 -:105AD4000000000000000001800000000000000041 -:105AE40000000000000000000000000000000000B2 -:105AF40000000000000000000000000000000000A2 -:105B0400000002000000000000000000000000008F -:105B14000000000000080000000000000000000079 -:105B24000000008000000000000000000000800071 -:105B3400020000000000000000000000000000005F -:105B4400020000000000000000000000000020002F -:105B54000000000000000000000000000000000041 -:105B64000000000000000000000000000000000031 -:105B740000040000000000000000000000000040DD -:105B84001030C00000000000000000000000000011 -:105B94000000000000000000000000000000000001 -:105BA40000000000000000000000000000000030C1 -:105BB400C000000000000000000000000000000021 -:105BC40000000000000000000000000000000000D1 -:105BD4000000000000000000000000001030C000C1 -:105BE40000000000000000000000000000000000B1 -:105BF40000000000000000000000800140000000E0 -:105C0400000000000000000000000030C0000000A0 -:105C14000000000000000000000000000000000080 -:105C24000000000000000000000040000000000030 -:105C340000000000000000001030C0000000000060 -:105C44000000000000000000000000000000000050 -:105C54000000000000780000800000000000000048 -:105C64000000000000000030C00000000000000040 -:105C74000000000000000000000000000000000020 -:105C8400000000080000C000000000000000000048 -:105C9400000000001030C000000000000000000000 -:105CA4000080000000000000000000000000000070 -:105CB40000000000000000000010000000000000D0 -:105CC40000000030C00000000000000000000000E0 -:105CD4000000000000800000000000000000000040 -:105CE4000000000000000028000000000000000088 -:105CF4001030C000000000000000000000000000A0 -:105D0400000000000000000000000000000000008F -:105D140080000000000000000000000000002030AF -:105D2400C0000000000000000000000000000000AF -:105D3400008000000000000000000000000080005F -:105D44000000000010000000000000001030C0003F -:105D5400000000000000000000000000000000003F -:105D6400000000000000000000001000000000001F -:105D7400000000000000000000000030C00000002F -:105D8400000000000000000000000000000000000F -:105D940000000200000000D0D0000000000000005D -:105DA40000000000000000001030C00000000000EF -:105DB40000000000000000000000000000000000DF -:105DC40000000000000000000000000000000000CF -:105DD4000000000000000030C000000000000000CF -:105DE40000000000000000000000000000000000AF -:105DF400000000000000000000000000000000009F -:105E0400000000001030C00000000000000000008E -:105E14000000000000000000000000000000081264 -:105E2400FD800000084000000000000000000000A9 -:105E340000000030C000000000000000000000006E -:105E440000000000000000000000000A02B45FD05F -:105E5400000000000000000000000000000000003E -:105E64001030C0000000000000000000000000002E -:105E74000000000000000000000008135C59B0009E -:105E84000010F13800000000C3D6A000000000306C -:105E9400C00000000000000000000000000000003E -:105EA4000000000000000000017A48D0A000040FA8 -:105EB4000000000000003C14000000001030C0008E -:105EC40000000000000000000000000000000000CE -:105ED4000000000000000B886B59A0014010F1384D -:105EE40060000000C3C4E00000140030C0000000E3 -:105EF400000000000000000000000000000000009E -:105F04000000000002046C10B000400F00002000EC -:105F140000003C00000000041030C000000000003D -:105F2400000000000000000000000000000000006D -:105F340000000A92EAD0A000C010F138000000006E -:105F4400C3C4A98F00000030C0000000000000009E -:105F5400000000000000000000000000000000003D -:105F640002A25959B000800F0040000000003C011B -:105F7400010800001030C000000000000000000014 -:105F840000000000000000000000000000000B887A -:105F94006B50B0000010F13800000000C3C4A0181A -:105FA40000000030C00000000000000000000000FD -:105FB40000000000000000000000000002067C0950 -:105FC400A000000F0040000000803C01000E000013 -:105FD4001030C000000000000000000000000000BD -:105FE400000000000000000000000A92FB80000096 -:105FF4000010F17800000000C3C5E000000000308C -:10600400C0000000000000000000000000000000CC -:10601400000000000000000002A248700000000F11 -:106024000000000000003C00000000001030C00030 -:10603400000000000000000000000000000000005C -:106044000000000000000B8A7B01A0208010F17A80 -:1060540000000000C3C5A00002000030C000000022 -:10606400000000000000000000000000000000002C -:106074000000000002046C010000800F00014000D9 -:1060840000003C00000000001030C00000000000D0 -:1060940000000000000000000000000000000000FC -:1060A40000000A92F90000000010F16366C00000CD -:1060B400AA85E01810000030C000000000000000B5 -:1060C40000000000000000000000000000000000CC -:1060D40002A25B000020000F0041428000002A81E0 -:1060E400000000001030C0000000000000000000AC -:1060F400000000000000000000000000000000009C -:10610400000000000010F17006C00000000008014B -:10611400000C0030C000000000000000000000007F -:10612400000000000000000000000000000000006B -:106134000000000F0040028000000000010802047B -:106144001030C000000000007E7EA0000000083C6B -:10615400C0100000000060000000083D4000000086 -:106164000010F12800000000908D800A000000302B -:10617400C0000000000042BAFC000004003C200003 -:10618400000000036C000800003C00000000020F47 -:1061940000000000000122CFF10A00001030C0000E -:1061A400000040005F32F4602050083D60000001B0 -:1061B40040005C000050083D40000009400AA1284E -:1061C40000000500A93CC10A00140030C000000012 -:1061D4000000FF0E86000012003C00000000400298 -:1061E40040000010003C00500014400AA0000007CA -:1061F40001011527C50000041030C0000000000094 -:106204000E9E56000024083DC0700000800040002F -:106214000020083D4050A000C02A500006C00301E1 -:106224001406110000080030C000000000000FDA5E -:1062340040000020003C00080014C003800000203F -:10624400003C2050A0000005A02802800000280285 -:10625400D580140C1030DC0000000040CC50460403 -:106264000004083D60108000000040000000083D6C -:106274004080A1C0002A500000000000015DC00A57 -:1062840000000030C00000000000CC0B002800001B -:10629400003C20090000000380000000003C203086 -:1062A400A0000005A028000000800001F00A000002 -:1062B4001030CC001E000000000020700002083CDA -:1062C40040100040000000000000083D6000002075 -:1062D400000000000000000001548D9B12000030FB -:1062E400C00000000000000004000000003C200882 -:1062F40000000003C0000100003C0000000000009A -:106304000000000000000142050A00001030C00037 -:1063140000A200020F7000283800000000000020D6 -:106324000000207C0800083D600000200000000000 -:10633400000080300016980002000030C000000009 -:1063440000040F003C405000000000000000000367 -:10635400E43C0000003C00000000000000000007D6 -:1063640000000801AD0040001030C0001E00020211 -:106374000F30007000000000000010000080266450 -:106384000000083D600000200000000000008020A4 -:106394001287B90A01480030C000000200000F0053 -:1063A4000040000000000000D01000F3A66470005C -:1063B400403C2000000000000000000080002D642C -:1063C4009B0002481030C000000000820F3800001B -:1063D400000000000000000000801E640030083C43 -:1063E400600001C0000000000000000000468000C2 -:1063F40000000030C000000000000F08340030002E -:10640400000000000020003380640020003C2000D5 -:1064140000000000000000000001003680000000C1 -:10642400004021E000000000551A766800082C3D69 -:10643400CA000000000000000800083DE0980000C9 -:10644400102831FE06000000A81CA0000000000077 -:10645400050000000200AA025400000003C0C000AE -:106464000000000000000000003C20100000000EAE -:1064740033A143C00000ABC2E000800000400180B3 -:10648400000000000000002802002C3C4E00000127 -:10649400400000000800083CC0001009106831F5F5 -:1064A400C0000500AA9C0000001000000D030C4071 -:1064B400000000001400040003C0000000004000BD -:1064C40000000000003C00008010400E316423C036 -:1064D40001009540F0000004004020A000000000EE -:1064E4005012460000082C3C4A98100002001C0080 -:1064F4000020083CC0000000C000000006D20000DC -:10650400BC1C00010000000014000D000000000A83 -:106514008000000003C01010800000000000003064 -:10652400003C00000000C000000002800000BC022B -:1065340000080000004021C01B0000005076546C8D -:1065440000081C3C4A019000000000000000083CC8 -:10655400400000080000A5A1E004000000000000C5 -:10656400000000000D000B000000F00600280000F1 -:1065740003C01000E000000000000000003C200008 -:1065840000100900A08000000000000000000000CE -:10659400004021A99D0000446610406000002C3C8E -:1065A4005E001000000000000000083C4001900064 -:1065B4000000000002C41000000000000000000001 -:1065C4001C0799000000990AC040000003C00000A5 -:1065D400F000000000000000003C200080020000E9 -:1065E400000002002000000000000000004000C87D -:1065F4001E000000C61A400000001C3C5A000000A7 -:10660400000000000000083C600181C000000000A0 -:106614000000000000000000000000002D0F00003A -:106624000000C93E0000000013C000000044000048 -:1066340000000000003C0000000000000002C00058 -:106644000000000000094000004001E018000200C2 -:10665400815BE06000000AA85A019000000000007D -:106664000000083CE07000000800A37C000000006B -:1066740000000818000000000C0010000000BDD647 -:10668400E440000012A80000F00001000000000037 -:10669400003C000800000000007C00000000000036 -:1066A40001000000004000E81D4000005A7680010F -:1066B4000000000000001000C00000000000000006 -:1066C40000000000000FA1294000030000000000AA -:1066D400000C000000090E200000A007C00080008C -:1066E400000000008000400000000000000000F0F6 -:1066F4000214004FF0A94348000000000000000805 -:106704000003C4001B0000005A3754040000083D75 -:10671400E0000000000000000000183CC050A00091 -:10672400002B5BB000400000A940000100000803FA -:10673400C00008000000500B16207002003C00004E -:106744000000000000000002203C0050A00000A156 -:10675400A5A00206000016808D0E000000000000B7 -:106764001E001400A03F00640050083CE00000013B -:106774004000000070081A940800A001000057317E -:106784006013050083CC000000940000000010009A -:106794000400A00400210012003C2000000040007E -:1067A4000000000202940050A0006040003C202041 -:1067B400010003C2000001040000069D9E000000C9 -:1067C400A07BE002F100083DC0000000C000000012 -:1067D40000001A9408000800C00053E000004000C4 -:1067E40083DC00002008000814F510000000000FEE -:1067F40004010200403C00000000C0000000000250 -:10680400029400000000E00050400000000103C0BA -:106814000F0F0008100215A79A4000402E3A0000FE -:106824000000283CC0000200000000000000183CEA -:1068340040000000000000000206500083DE00005B -:106844000000000303B080000000AD0B002C4200E8 -:10685400003C20000000000000000002003C20007A -:1068640000000000000002E8200003C20000002035 -:1068740014835C1180000040F216266500000000BD -:1068840000800A00000000000008183C4001E000FD -:106894000800A379E7C0200083D4081E0000000389 -:1068A40080A00B000000F00B5668F000000000F020 -:1068B4000580000000000000003C200000000000F3 -:1068C400A080C240000003C00F1050000000000070 -:1068D400000000000072206404000000008000003A -:1068E400000000000000183C6000040000000001EB -:1068F400E7910020A940080000000000000000000B -:1069040000000507A46400000000007005000000FA -:1069140000000000003C0000080000000000002807 -:1069240000001680BF00000000280DE500000000F4 -:1069340001F2A066F10000000078100000000000E1 -:106944000000083C60380000080AA76A06040080BA -:1069540083CC00010000000406F08B0000000076E8 -:106964006425020000000008F0000000000000029E -:10697400503C00080000010AA00247C0000003C008 -:10698400000F140030B1CDB3800000C2D33660389C -:106994000000000000001090000000008000083C8F -:1069A40060000000104F03000040020083CE078007 -:1069B40004000030C0808B000000DB02A400020051 -:1069C40000000000E004000000000002503C200031 -:1069D4000000000F00800328032003C20000000011 -:1069E40000C3C6A0000000000A7300000000283C99 -:1069F400E000000000000000020000426AD0000035 -:106A040000005175E0000000006FA00B0000003C86 -:106A14003E00000000800F0B40000000003C00001E -:106A2400000000000000050020005B70000000056D -:106A3400F0F1C00000010014B01E000000C3C4E562 -:106A440000001400053F80000000283D60000001A4 -:106A540040000000000000416B80A0814407A3E2D5 -:106A640066600540ABDD8B9A0014003C000080009A -:106A74000400050000000000003C0000000040008D -:106A84000000000020024A70A000400150F843C0FA -:106A9400010016818900000400C3C4A981000040DC -:106AA4000037C0000000083DE070000880000000CE -:106AB4000008083D4050A0008423D1E56000000098 -:106AC400202FAB1CA000003C010108000000800F37 -:106AD400C0000000003C00080004C00000000000EA -:106AE400003C205000008802607420000000001464 -:106AF400B710000000C3C4A818000040C230000052 -:106B04000000083DC001E800000000000200083D4C -:106B14004038A408008767F0038000008135E81D31 -:106B24004000003C010100000000C10B0000000017 -:106B3400203C00000000000000000400003C20385D -:106B4400A200000347F4040000800024D9182000A8 -:106B540000C3C5E000000000507336680000083D23 -:106B6400C0000000000000000000409EC1180000AA -:106B74000020F700E04000000A06150E0020003C4B -:106B84000000000000000A0354000000203C0070D4 -:106B94000000000000000000006F69000000000019 -:106BA400F08022400020051CC090000000C3C5A056 -:106BB40000000000000000040000483CC000E000A9 -:106BC400000000000000083C60800000000051ACA0 -:106BD40046C000110016B00F0080003C0000000009 -:106BE4000000000000280000003C2090000000008D -:106BF40000000000003C00100000000AF074424055 -:106C040030800081C00A014000C3C5A718000000FD -:106C14000000200400000A9400108000000000001E -:106C24000000083C60000140000A53F600400000E8 -:106C34000094C7180000003C0100000000000000A0 -:106C44000428000001680F09E000000000000000B3 -:106C5400003C00000000000550A9C3C00000001E55 -:106C6400F080000000AA85E181000C000000366974 -:106C74000004083CE0000008000000000000029549 -:106C84007D000000000AF32560000000007C900AEB -:106C94000000002A81000800000000001400000029 -:106CA400003C2000900000000000000001543B0064 -:106CB400000000005074000000000034E0180000E0 -:106CC40000007200901103010100000000000000A8 -:106CD40000020000000000000800000000020000A4 -:106CE400000000000000000004000020000000007C -:106CF40000008000280000000000000000000001E7 -:106D0400000000000000040000000001000000007A -:106D1400000000000000000000100000000000005F -:106D2400000040000000000000000000000000001F -:106D3400000000000000000000000000000000004F -:106D44000000040008000000000000000000000033 -:106D5400000000000000000000000000000000002F -:106D640000000000000000004000000000000000DF -:106D7400028000000000000000000000000000008D -:106D840000000000000000000000000000000000FF -:106D940000000000000000000000000002000000ED -:106DA40004000000000000000000000000000000DB -:106DB40000000000000000000000000000000000CF -:106DC40000000000000000000000020000000000BD -:106DD40000000000000000000000000000000000AF -:106DE400000000000000000000000000000000009F -:106DF400000000000000000000000000000000008F -:106E0400000000000000000000000000000000007E -:106E1400000000000000000000000000000000006E -:106E2400000000000000000000000000000000005E -:106E3400000000000000000000000000000000004E -:106E4400000000000000000000000000000000003E -:106E5400000000000001000000000000000000002D -:106E6400000000000000000000000000000000001E -:106E7400000000000000000000000000000000000E -:106E840000000000000000000000000000000000FE -:106E940000000000000000000000000000000000EE -:106EA40000000000000000000000000000000000DE -:106EB4000200040000000000000000000000300098 -:106EC40000000000000000000000000000000000BE -:106ED40000000000000000000000000000000300AB -:106EE400028004000000000000000000F000000028 -:106EF400400000000000000000004000000000000E -:106F0400000000000000000000000000000000007D -:106F14000000000000000000000000003800000035 -:106F2400000000000000000000000000000000005D -:106F34000000000000000000000000000000080045 -:106F4400000000000000000000004000800000007D -:106F5400000000000000000000000000000000002D -:106F6400000000000000000000020000000000001B -:106F740000000000000000200000000000000000ED -:106F840000000000000000000000000000000000FD -:106F940000000000000000000000040000000000E9 -:106FA4000000000010000000400030C0000000009D -:106FB40000000000000000000000000000000000CD -:106FC40000000000000000000000000000000000BD -:106FD400003C02000000001030C00000000000006F -:106FE400000000000000000000000000000000009D -:106FF4000000000000000000000000000000203C31 -:1070040016018000000030C00000000000000000F5 -:10701400000000000000000000000000000000006C -:107024000000000000000000000000000029409063 -:107034000000001030C0000000000000000000004C -:10704400000000000000000000000000000000003C -:107054000000000000000000000000168010000086 -:10706400000030C00000000000000000000000002C -:10707400000000000000000000000000000000000C -:107084000000000000000000003C000000000010B0 -:1070940030C00000000000000000000000000000FC -:1070A40000000000000000010000000000000000DB -:1070B400000000000000003C16000000000030C08A -:1070C40000000000000000000000000000000000BC -:1070D40000000000000000000000000000000000AC -:1070E40000000000003C00000000001030C0000060 -:1070F400000000000000000000000000000000008C -:10710400000000000000000000000000000000007B -:107114000000003C16000000000030C00000000029 -:10712400000000000000000000000000000000005B -:10713400000000000000000000000000000000004B -:10714400000000000A00001030C000000000000031 -:10715400000000000000000000000000000000002B -:10716400000000000000000000000000000000001B -:1071740000059B00000030C000000000000000007B -:1071840000000000000000000000000000000000FB -:10719400000000000000000000000000201400F0C7 -:1071A4000000001030C000000000000000000000DB -:1071B40000000000000000000000000000000000CB -:1071C400000000000000000000000028061000007D -:1071D400000030C0000000000000000000000000BB -:1071E400000000000000000000000000000000009B -:1071F4000000000000000000000000050000001076 -:1072040030C000000000000000000000000000008A -:10721400000000000000000000000000000000006A -:10722400000000000000000000080E00000030C054 -:10723400000000000000000000000000000000004A -:10724400000000000000000000000000000000003A -:1072540000000000000000000000001030C000002A -:10726400000000000000000000000000000000001A -:10727400000000000000000000000000000000000A -:107284000000000000000000000030C0000000000A -:1072940000000000000000000000000000000000EA -:1072A400000000000000000000080AA040024000A6 -:1072B400000000000000001030C0000000000000CA -:1072C40000000000000000000000100000000000AA -:1072D40000000000000000002AA168004003000034 -:1072E40000058000000030C0000000000000000025 -:1072F400000000000000000000000000000000008A -:107304003C6D000000000F00400000000000000081 -:107314000A00001030C0000000000000000000005F -:1073240000000000000000000000000000023B78A4 -:107334000000000030F168060000000000000100B9 -:10734400000030C000000000000000000020000029 -:107354000000000000000000000000000000000029 -:1073640000080F000002400020000FD000000010B1 -:1073740030C0000000000000000000000000000019 -:1073840000000000000000000000000000000000F9 -:1073940030F16800400000041EC00000000030C04E -:1073A40000000000000000000000000000000000D9 -:1073B4000000000000000040006B100000080F00F7 -:1073C40000000000000000050000001030C00000B4 -:1073D40000000000000000000000000000000000A9 -:1073E40000000000020043788000000030F16800D3 -:1073F40000040000000D9A00000030C000000000EE -:107404000000000000000000000000000000000078 -:10741400000000023800500000000F00404200004D -:10742400000000000000801030C0000000000000D8 -:107434000000000000000000000000000000000048 -:107444000102397881A0000030F12A664000000072 -:1074540000000001400030C00000000000000000F7 -:107464000000000000000000C00000000000000355 -:10747400FC2B500000000F00404200000000000000 -:107484000000001030C000000000000000000000F8 -:107494000000000000000000000000000002BD48E1 -:1074A400D800002030F12A604000000000000010E5 -:1074B400000030C000000000000000000000800058 -:1074C400000000000000000000000000000000A018 -:1074D40000100F000000000100004FE00000001049 -:1074E40030C00000000000000000000000000000A8 -:1074F40000000000000000000000000001B00010C7 -:1075040030F13800000520015EC00000000030C0EA -:107514000000000000000000000000000000000067 -:1075240008000000000000000000000000080F0335 -:1075340000000000000000000000001030C0000047 -:107544000000000000000000000000000000100027 -:10755400000000000000000001A0000030FFA800AF -:107564000000000000000000000030C00000000027 -:107574000000000000000001683E50800080000010 -:1075840000000410001C10000000000079424000BC -:10759400002D6DE30A00001030C000000000000060 -:1075A4000000000004097DFBF18000800000000061 -:1075B4000500046CF00000000053B4060000001243 -:1075C4009CD00000000030C0000000000000024019 -:1075D40000000000000000000000000000000000A7 -:1075E40021CA01B0000000F0A4000000003E8300A6 -:1075F4000900001030C00000000000053E0000003B -:107604000000000000000000000000000610016EF1 -:1076140000A00000055121400000003E8FA70A0091 -:10762400000030C000000000000000004000040220 -:10763400380E00000000000000000040001FD800C9 -:1076440002000550400280000000000018000010F5 -:1076540030C000000000000000006000010935E1B6 -:1076640018000000000000000000146D500000002D -:10767400055520000000000000050E00000030C089 -:10768400000000000000000000000000017F000175 -:10769400C800000000000000000011D000080F0026 -:1076A4006D440020000282051040001030C000002C -:1076B40000000000000000000000065D000004005F -:1076C400000000000000000080F0000000F17E06D1 -:1076D4008840000296C50C80000030C00000000005 -:1076E4000000000000000000000000000000000096 -:1076F40000000002941C00A20000069D40400020EF -:1077040001003E850E00001030C0000000000000A3 -:10771400000000000000000018A0000000000000AD -:1077240001116C780004000816993B6000100000F9 -:107734007DD00100000030C0000000000000000007 -:107744000002000000000000000000000000002013 -:1077540000FF00000000069740040400001680901B -:107764000000001030C00000000000000000000114 -:107774000000000000E00000000000000242005D84 -:1077840000A0000816996002800014148E90000076 -:10779400000030C0000000000000000000000000F5 -:1077A40003F900E00140000000000401401D10B096 -:1077B4000008069340400000003FA6E19F0000102F -:1077C40030C000000000000000006800000007FA5C -:1077D40000000100000000000003E87D80000000BC -:1077E400169163660000003FED890C00000030C074 -:1077F400000000000000000C000000000000000079 -:1078040000000000000000000000000000200661ED -:107814000366C000400000800000001030C000007B -:1078240000000000002000000000000050000040A4 -:1078340000000000000000005000000016606142DB -:10784400800000014FB00A0C00000000000000009E -:107854000000000000010000000000008080000023 -:1078640000000001694C010140C00FA0C0000000ED -:10787400413FFCD0080000000000000000000000B0 -:107884000000000000000000000000C00000000034 -:10789400000293CB11E000C00FA1E8000003609F39 -:1078A400D6A5000000000000000000000000000059 -:1078B40000000000000000000000000000000000C4 -:1078C4000028000000000C396C001080250057AB24 -:1078D4000804000000000000000000000000000098 -:1078E4000000000000000000000000000500057C0E -:1078F4000180001028BDF406080000801CDB800015 -:107904000000000000000000000000000000000370 -:107914000C7C00F000000000000000002830000093 -:1079240000010E33B74000000032C3B000408000B5 -:1079340000000000000000000000000000098E7B31 -:107944000090880000000100000029EA0080200067 -:10795400283DA74600803030CDF188A30000000008 -:10796400000000000000000000000000000000C053 -:107974000000000000700402BC0A000000000000C7 -:1079840000000000000001F580000000000000007D -:1079940000000000000000000000000000100000D3 -:1079A40000000000021A815A00000000000000607C -:1079B400001000014FC39A00000000000000000006 -:1079C40000000000000000436B7908000000000084 -:1079D400000000403C2A308000000000E802D00093 -:1079E40000000FE180000000000000000000000023 -:1079F400000000000008947A50000000000000001D -:107A04000318AD4E80F000000053A162A8000000EE -:107A140055AF0E0000000000000000000000000050 -:107A2400010000000000B10000000000000200039B -:107A3400E80000A0000000000023862000000000F1 -:107A440010000000000000000000000000000280A0 -:107A54000000000080A00000000000010003E968AD -:107A640018A00000000001E00040000000038C00AA -:107A740000000000000000000000000000000011F1 -:107A84007C08900201000000000008203D4800002E -:107A9400004105B03442400100029ED300000400BE -:107AA400000000000000000000000000000ABD40CB -:107AB4008180014000000000084A3BDA00000144D4 -:107AC400ADB1EF404005000126980000140000000D -:107AD40000000000000000000000000000000000A2 -:107AE400080000000030000291CB00D000000C37E9 -:107AF400EFC2C600000000000D00000000000000FE -:107B04000000000000000000000000001980044094 -:107B1400000000000041696800B000000631B208AE -:107B240020000000000001800000000000000000B0 -:107B340000000B4040000040003F00000000000037 -:107B44000000000006E9B8D000000080A02000007A -:107B54000003FE9B10000000000000000000000570 -:107B640077802800000015CC780000C000000000D9 -:107B7400080001FFF9D000148003B542000002039D -:107B840085FD0E000C000000000000000000B7C2DC -:107B9400000800000000B98020000000000000037D -:107BA400BEEC000020800AF03DC700800000000FFA -:107BB4000002000000000000000080013B142010BF -:107BC40000000000F9E1800000000000000179E9F4 -:107BD4007980008005F50607430000000008004096 -:107BE4000000000000000000000A0AC0400000007D -:107BF400000031F000000000000200000039F880AD -:107C040000000580F44003000000000300000000B1 -:107C14000000000000008205500028080000000059 -:107C240080E0200000000005000029DD9000200411 -:107C340005A5E54001800000000D9E020000000043 -:107C44000000000000050ACE00000003C000D0E0E0 -:107C54000000000000000052901DB09000000807D2 -:107C6400600280500030E7B0000C0000000000000B -:107C74000000060A502E00080803C0C081900000CE -:107C8400000000000110014DF8100000000D344008 -:107C940000280018EE80000800000000000000002A -:107CA40000AF06C028000001603B30000000000067 -:107CB400000000000358B0020000000000000000B3 -:107CC400050FC0B90900000000000000000002A078 -:107CD40052940000010169E950000800000000000E -:107CE400000202DF81840000000001E00000108F28 -:107CF40046978800000000000000000000F3081C04 -:107D04000000000020CE000000000000010000027E -:107D1400D5EF00BA0000000001C28000001780F017 -:107D24008000000000000000000040F350266000C6 -:107D3400000002EE18000000000002800803D6FED6 -:107D4400180CC000000000000300100757D7000003 -:107D5400000000000000000000811E94000050415B -:107D64006C190000004000000200044120100100D2 -:107D7400004040003D4740400016C3FF0800040097 -:107D840000000000000000A1DE402800510B6CCA76 -:107D940000C4014000000000011921C81080014402 -:107DA400001720024020102957D7010014000000BA -:107DB400000000000000000000700003974D7000F8 -:107DC4000000000000000202836A100008000000A6 -:107DD400BC038500040F17EB8F4000000000000077 -:107DE40000000000000000000000966D90000000FC -:107DF40000000000000340EB80A0100000553C0090 -:107E04000000000E3D9D0020000000000000000066 -:107E14000000F68020000241403F71D0088000003D -:107E2400000000400A7C790000000FA03E4340009F -:107E340000210DD90004080000000000000000101B -:107E4400728004000011685A50D0108000000000B5 -:107E54000000027E50C000C00F55046040000020A6 -:107E64007CF8008008000000000000000050074477 -:107E740000000042BC0A700400000000000000037F -:107E84000EEB010400000CBD2064000000000000A3 -:107E940000004000000000000000020A12E0600040 -:107EA4000000155C000A000000000000000A2EDC3F -:107EB400018200000C3BFD460300000000058E001B -:107EC400000000000000000000A002C4000008013F -:107ED4007C0E50080000000000000001683B1900FF -:107EE40000000F008000005000028300100000001A -:107EF400000000000000030A16A00000000014CBDC -:107F040001C0000000000000010290EBD0C000009E -:107F14000F070003010000028CA50C000000000004 -:107F24000000000000A0024000000000000000C0AB -:107F340000000000006000016C2B00E00000000065 -:107F4400A5C000000000000B0000000000000000BD -:107F54000000030A17800000000000000000000079 -:107F64000000000001029559119140180057300695 -:107F740080000000000180000000000000000000FC -:107F840000A006C0000008030E6800000000000006 -:107F940000000000140FD800000000F06822400028 -:107FA400004282DD00000800000000000000030A17 -:107FB40012806000000B1DFE500000000000000055 -:107FC400000029C17180000400D5FCE6C000408196 -:107FD4004C080001480000000000000000A00674E6 -:107FE40020000800142001E000000000000000430D -:107FF400C00058040000000938020000440F03D1F7 -:10800400001C0000000000000000030A13860400A6 -:10801400000015E801E00000000000400003C0C0BB -:10802400900A0000080D6002C000100F5EA59800C1 -:10803400000000000000000000A002C00000420395 -:10804400FC2800900100000000000003C40CF8B1FB -:1080540000400F008143C70000140290080144004F -:10806400000000000000030A13800000400814FE12 -:108074000000010000000000011BD17878A001403D -:108084000F030002C00000168D8000009400000061 -:108094000000000040A0027420000000000010F066 -:1080A400000000000000000081FC0000000300F05C -:1080B40002C00000040F17D0000000000000000000 -:1080C4000000030A1280000000000000198008402C -:1080D40000000000000002F80000002000F102008F -:1080E4000004040F57E580000000000000000000B9 -:1080F400000000000000004002F801D10000000070 -:1081040000100003FC3E0890000000054000002021 -:10811400001001D9890000000000000000000000E8 -:10812400000000003002917F79D0000000000000C0 -:108134000003E8DF38C000001008204000501100A0 -:108144001FE38C000C000000000000000000000091 -:108154000000000000000000000000000000000318 -:10816400FF48F0B00428069F00E000000002EDF094 -:10817400900000000000000000000000000000501B -:108184000000000001E00000000000000001FDDD2F -:1081940080000800169160C00820000167C1080132 -:1081A4004000000000000000000000000004020085 -:1081B400003C09000000000000000000015D010017 -:1081C4000020469F01400800000000099000800044 -:1081D4000000000000000000000000720100047BA9 -:1081E40079E00000000000000000066B19C00008E0 -:1081F400169129C01000000000011E01400000007B -:10820400000000000105080000000000003F3000ED -:108214000000000000000003FF6B7000000846939C -:10822400400700002017C0838000200000000000E9 -:108234000000020556800000000014F919C0000077 -:10824400000000220003F97B0180000816916002FF -:10825400000000A95E9B0000000000000000000078 -:10826400000000002000002316EB70000000000056 -:1082740001000200001F70A0000006910002C0006F -:10828400000FC29080800000000000000000000089 -:1082940000000400021B004FB00000000000028038 -:1082A4000100117899B20000169160004000D0AF2F -:1082B400D695000000000000000000000000001C33 -:1082C400200000100750010000000000000000031F -:1082D400E800000080000EDF6C068600001FDCB0A2 -:1082E4000000000000000000000000000020040066 -:1082F400000006EC00800000000000020003E968B2 -:108304003000000017B97062C000001E9E8180001A -:108314000000000000000000000000000000100247 -:10832400E80CF0005440000002000001400850F046 -:108334000020869340268000201542008800040017 -:108344000000000000000000000000005120695EF1 -:10835400B8C0014000000104000281E08010000068 -:10836400169171C28000011554811F001400000091 -:10837400000000000105080000000001542DF80071 -:108384000000000000000800025A000000000F0175 -:10839400804200000030C6D0180000000000000039 -:1083A40000001205528000000002945EF8000000F4 -:1083B40000000000000006CF01A000001F012B6296 -:1083C400F00400A2DFC01D1C00200142E00B0000ED -:1083D40000000000000002217C303800140000007E -:1083E40000000003F40F7900000043F0A0074020D0 -:1083F4008029A7C108000000028C100000000000C2 -:108404000000000000017DDA700008000000000098 -:108414000103697EB1C000D423D17842401300A97E -:1084240077E9801800000000000000000000000050 -:1084340000000000000008F0000000000000000040 -:10844400003FF09000000FC020000000001680AD37 -:108454009000000000000000000000000000000088 -:10846400000000003810000000000002000014DDCD -:10847400800000040585F942000000A940010C10A9 -:1084840000000000000000000000000000000001E7 -:108494007C3C00901000000000000000F82CB0D0DC -:1084A40000000C29E167000000004F90000040002C -:1084B400000000000000000000000000000014DEC6 -:1084C4000180C80000000070074AF0FBB2E0000819 -:1084D400081BBDC30000000006E00000800000008F -:1084E4000F0000000000000C00000000143F110009 -:1084F4000000000000000001683B7800000004A5B3 -:108504006020000000300200180000000000080095 -:1085140000000000002000000002BD5901C000005E -:10852400000000200102917FB9E100002AD7E44055 -:10853400000011B01C000F00000000000000180033 -:108544000000000000400001542B50820000000095 -:10855400000400016CEE7000000000000002000046 -:108564000020C2E000A0C0000000000000000000E5 -:1085740000000000000295E9101200000000000253 -:10858400000294DC9980000000000060402010008C -:108594000795004C400000000000000000000000AF -:1085A4003C002020980C5000000000000000000057 -:1085B400003F000000000C3D70000B1000061E8000 -:1085C400004800000000000000000000000004005B -:1085D4003108F57D0000000000000000001014DDEB -:1085E40000001400063BA4039000000B84D0080094 -:1085F4000000000000000000000000000000100067 -:10860400A80900F14040000000000203C00000007F -:1086140000400F00802600010003C005080004008C -:108624000000000000400000000000804101E8F864 -:1086340078100140000000000003C1600001014007 -:108644000F0580E241050083CC0581001400000081 -:108654000000000000F0000000000002BC2F00D069 -:10866400000000000280000000000000000000F78D -:10867400FC6200000003C200000140000000000092 -:10868400000006F050000000000015EF1810000074 -:1086940000000100000000000008000000E3B7E350 -:1086A400C0000083DE010000C000007200801100E1 -:1086B40062002F8200000103D7D728080000F7D3F7 -:1086C4000C240200F3F708000002FFFF0000010180 -:1086D400BFBF44042020FBFB04142020EBEF100058 -:1086E4000420FDFD02004004FFFF00000040EFEF06 -:1086F40010200000F7DF28088584F7DF200400013C -:10870400DBEB1C188000E7F700080080F7FF080087 -:108714000202FFF30C0C0200F3F30C080002F7F75B -:1087240008080000F7F708000000FFF71008000031 -:10873400C7EF20000400EFCF31110004EDCE360462 -:108744000000DBF90A0C0000FFF500020000FFFF47 -:1087540000000404FBFB04000000FFFF0000000015 -:10876400EFFF10100000CBEF24242000FFFB0000DB -:108774002020FFFF00082020E5F613190100FDED7D -:1087840002220001FDD52A0A0000DFDD22000000DC -:10879400FBDF04240000FFFB00000000FFFF0404D3 -:1087A4000000FFEB10100000CFDF22020101FFFDEB -:1087B40000000000FFFF08080000FEE6111242005E -:1087C400EFFD02000042F7F50E000202F9FB00047F -:1087D4000000FFFF08002120F7F700082021F3FB29 -:1087E4000C141020EFE718081414F7F50A0A505077 -:1087F400F7F708081010F7F708101010EDE7120843 -:108804009511F7FD02001094FFFD06049090F1F31A -:1088140008001010FFFF00000210BFBF4404020252 -:10882400FBFB14100000EFEF00100000FDFD020040 -:108834000000FFFF00000000EFEF10100404F7FF3A -:1088440008080000F7FF00000000EFFB1C1E0000FA -:10885400E3E50A0A0000F5F50A0A0400F5F10E043E -:108864000004F9F300080000F7FF00000000FFFF18 -:1088740000000000FFEF10102000FFEF0000202098 -:10888400E7ED130B2020FDEC12160101F9E90E0AA5 -:108894000000FDF502020000FDFD02000000FBFBEC -:1088A40004000000FFFF00100000EFEF10100100B3 -:1088B400EFFB04040001FBFF00000000FFFF0A00BF -:1088C4000000F5F4130B0000EDED12004242FFF539 -:1088D4000A080002FDFD02023020FDFD06003030D2 -:1088E400FBFB00041111FFFB14041410EBEF100048 -:1088F4005014DFDF22021050FFFD00001010FFFFB4 -:108904000A081110FCF413121011EDED12029494E4 -:10891400F5F50A021090F9F906021010FFFD08009F -:108924001210F7F7080C1212E3F3181C1010E7E703 -:1089340008080010FFF50A020000F7FF0000040019 -:10894400FDFF02020004FDED12120000FDED020223 -:108954000000F5FD060A0000F1F10A0E0000FDFF1B -:1089640000000000FFFF04040404EBEB10140000FB -:10897400EFFF00000000FDFD02000000FFFF00000B -:108984000000FFEF10100000EFF708082020FFFFA1 -:1089940000022120FDFD0E160001E5E11A10000081 -:1089A400E7FD02080000FDF906060000FDF90200DB -:1089B4000000FFFF0000010100008200800103FFAE -:1089C400FF10000000EFEF10000000FFFF0208009E -:1089D40000E5E51B030000FDFE10005202EFEB1C56 -:1089E4001C3052E3F700023030FFFD02021010F990 -:1089F400F904121110FFEF10100415EFEF001040EE -:108A040040FBFB04000000FFFF00000000FDFD0A26 -:108A1400020101F7F401030000FFEF10108400E7E6 -:108A2400EF08180004F7FF00020000FDFD02061223 -:108A340002FBF914001210EFEB14141012FFEF00F4 -:108A4400001010DFDF22021010FFFD00000414F5F7 -:108A5400FD0A0A0000F6FC01020000EFEF1010000E -:108A640000F7E708100000FDFB06040000FDFD020E -:108A74000A0000E5F71C000400E3EB10180004EF03 -:108A8400FF00000000FFFD02020000FFFF020000E3 -:108A940000FDFD02020000FFFD10020000EBEF10DC -:108AA400142020F7E30C120121F3F10E0A0000F563 -:108AB400FD02100000FFEF14140000EBEB100001A6 -:108AC40000FFFF00000001FDFD02000000FFFF00A9 -:108AD400000000EFFF10101000F7E708001210FF6D -:108AE400FD02023012FDFD040A7272F3E310180055 -:108AF40010EFEF10000000EFFB04060400FBFD0282 -:108B0400024105FDFD00120040EFEF10100000FFD0 -:108B1400EF00000100FFFD0A020001E7E511090072 -:108B240000FFFE00000000FBEF1C140404E3E71048 -:108B3400180200EFEF10120002E9E916120000ED2E -:108B4400EF10000202FFFF00041000FBFB00041002 -:108B540010FFFF02001010FDFD0A081010FFF601BF -:108B6400010010FFFF00100000E7EF18180000EFED -:108B7400E710140000EFEB16100000E9E9161400EA -:108B840000EBEB04100000FFFF00000000DFDF2219 -:108B9400020000FFFD02020000FDF50A080000F6D5 -:108BA400FE01000000FFFF10000000E7E71810219D -:108BB40020EBEF14140021E9EF12120000E5E51C8C -:108BC400140000FBEB00000101FFFF00000000FFA8 -:108BD400FD02020000FFFD02061000FBFD04001070 -:108BE40010FFFB04041212EBFB141C2030E3E31C03 -:108BF400144220E3E31C180042EDE512120000EDDC -:108C0400EF14140000EFFB00004140FFFF000000E0 -:108C140001FDFD02000000FFFF00000101FFFF183D -:108C2400000000E7E712180000EDFD02000000FF5D -:108C3400FF04000000F3F3081C0202EFEF10100021 -:108C440000EFEB14140000EFEF10120200EDED1230 -:108C5400120002EFEF10000000FFFF02001000FD01 -:108C6400FD0A081010E7E711011010FFFE000000D4 -:108C740010FFFF040C0000E7F318000000FFEF10E2 -:108C8400000000F9EB06120000EDFD00000000FFFB -:108C9400FF04000000FBFB00000000FFFD020200D7 -:108CA40000FFF508080000F7FE01050000FBFF04C3 -:108CB400000000FFFB0C142000F3E3140421210046 -:108CC40000110162003F8200000103000000000067 -:108CD4000000001B1B0000000000001F1B00000020 -:108CE400000000041F00000000000000000000005D -:108CF4000000001B000000000000001B1B0000001F -:108D04000000001F1B0000000000001F1F000000E7 -:108D140000000000000000000000001B1B00000019 -:108D24000000001B1B0000000000001F1F000000CB -:108D34000000001F1F000000000000001B000000D6 -:108D44000000001B1B000000000000001B000000CE -:108D540000000004040000000000001F1F000000C9 -:108D6400000000001B0000000000001B1B000000AE -:108D7400000000E4FB000000000000E4E400000048 -:108D84000000001FFF000000000000001B000000A6 -:108D9400000000F919000000000000E4F9000000E0 -:108DA400000000E4E4000000000000F7F700000009 -:108DB4000000000013000000000000F9F9000000AA -:108DC400000000E4F9000000000000E4E4000000FA -:108DD400000000F7F700000000000000F3000000AE -:108DE400000000F9E900000000000014FD0000008C -:108DF4000000001E1C000000000000F7FF0000003F -:108E040000000000E1000000000000E9E9000000AB -:108E14000000001CFD0000000000001E1A000000FD -:108E2400000000EFFF00000000000000E10000006F -:108E3400000000DDC90000000000001EDD0000008D -:108E44000000003E3A000000000000BDBF0000002A -:108E540000000000A9000000000000DDD9000000AF -:108E6400000000A2FF000000000000A6A200000015 -:108E7400000000BDBD00000000000000B9000000BB -:108E8400000000FFDD000000000000A6FF0000005D -:108E94000000008486000000000000BDBD0000004A -:108EA40000000000BD000000000000FFFD00000005 -:108EB40000000006FF0000000000000404000000A1 -:108EC4000000003D3D000000000000003D000000E7 -:108ED400000000FFFF00000000000006FF0000008B -:108EE4000000000404000000000000FDFD0000007C -:108EF40000000000FD000000000000FBFB0000007B -:108F040000000000FB00000000000024000000003E -:108F1400000000FDFD00000000000000F90000005A -:108F2400000000FBFB000000000000FBFB00000051 -:108F3400000000FFFB000000000000FFFF00000035 -:108F440000000000FB000000000000FB2200000005 -:108F5400000000FBFB000000000000DFFB0000003D -:108F6400000000FFFF0000000000000022000000DD -:108F74000000002202000000000000FBFB000000D3 -:108F8400000000DFDB00000000000026FF000000FE -:108F940000000000220000000000000000000000AB -:108FA40000000000000000000000000000000000BD -:108FB40000000000000000000000000000000000AD -:108FC400000000000000000000000000000000009D -:108FD400000000000000000000000000000000008D -:108FE400000000000000000000000000000000007D -:108FF400000000000000000000000000000000006D -:10900400000000000000000000000000000000005C -:10901400000000000000000000000000000000004C -:10902400000000000000000000000000000000003C -:10903400000000000000000000000000000000002C -:10904400000000000000000000000000000000001C -:10905400000000000000000000000000000000000C -:1090640000000000000000000000000000000000FC -:1090740000000000000000000000000000000000EC -:1090840000000000000000000000000000000000DC -:1090940000000000000000000000000000000000CC -:1090A40000000000000000000000000000000000BC -:1090B40000000000000000000000000000000000AC -:1090C400000000000000000000000000008200809A -:1090D40001032C2C0000000000001F3E00000000D3 -:1090E40000009F1F000000000000CCDE0000000014 -:1090F400000000480000000000003E2C00000000BA -:1091040000001F3F0000000000009F9F00000000BF -:109114000000DEDE00000000000000480000000047 -:1091240000003F3E0000000000009F3F00000000E0 -:1091340000009F9F000000000000DEDE0000000031 -:109144000000005A00000000000037360000000054 -:109154000000813700000000000089810000000049 -:1091640000005A5A000000000000005200000000F5 -:109174000000F636000000000000C1F60000000008 -:109184000000A981000000000000FAFA00000000BD -:1091940000000052000000000000F4740000000011 -:1091A4000000C1F4000000000000A9E90000000074 -:1091B4000000F2FA0000000000000072000000004D -:1091C4000000F474000000000000E5F4000000005A -:1091D4000000EDED000000000000F2FE00000000C1 -:1091E4000000007200000000000074740000000021 -:1091F4000000A774000000000000CF8F00000000F2 -:109204000000767E000000000000007000000000F6 -:10921400000074740000000000009F74000000004F -:1092240000009F9F00000000000074760000000012 -:1092340000000074000000000000746400000000DE -:1092440000009B740000000000009B9B00000000D5 -:1092540000007676000000000000007400000000AA -:109264000000606000000000000099700000000031 -:1092740000008999000000000000767600000000DC -:1092840000000076000000000000606000000000A4 -:1092940000008960000000000000898900000000CF -:1092A4000000767600000000000000760000000058 -:1092B40000006464000000000000896C00000000ED -:1092C40000008189000000000000767600000000A4 -:1092D4000000007600000000000076760000000028 -:1092E4000000897E00000000000081890000000069 -:1092F4000000767600000000000000760000000008 -:1093040000007676000000000000A97E0000000046 -:109314000000C18900000000000076760000000013 -:1093240000000076000000000000777600000000D6 -:109334000000FF7F000000000000F7FF00000000B5 -:1093440000007777000000000000007600000000B5 -:1093540000007760000000000000DF7F00000000D4 -:109364000000B7FF00000000000077770000000055 -:109374000000006000000000000061200000000008 -:109384000000DF7F000000000000B79F0000000025 -:1093940000006177000000000000004000000000B1 -:1093A40000000000000000000000000000000000B9 -:1093B40000000000000000000000000000000000A9 -:1093C4000000000000000000000000000000000099 -:1093D4000000000000000000000000000000000089 -:1093E4000000000000000000000000000000000079 -:1093F4000000000000000000000000000000000069 -:109404000000000000000000000000000000000058 -:109414000000000000000000000000000000000048 -:109424000000000000000000000000000000000038 -:109434000000000000000000000000000000000028 -:109444000000000000000000000000000000000018 -:109454000000000000000000000000000000000008 -:1094640000000000000000000000000000000000F8 -:1094740000000000000000000000000000000000E8 -:1094840000000000000000000000000000000000D8 -:1094940000000000000000000000000000000000C8 -:1094A40000000000000000000000000000000000B8 -:1094B40000000000000000000000000000000000A8 -:1094C4000000000000000000000000000000000098 -:1094D40000000000110262002F820000010300005E -:1094E4000020000000000022000000000022000014 -:1094F40000000002000000000022000000000066DE -:10950400000000000066000000000066000000008B -:10951400006200000000004400000000004400005D -:109524000000003700000000004400000000006458 -:10953400000000000076000000000063000000004E -:109544000046000000000066000000000037000034 -:1095540000000046000000000062000000000066F9 -:10956400000000000046000000000026000000008B -:1095740000620000000000260000000000640000FB -:1095840000000066000000000066000000000040CB -:10959400000000000044000000000044000000003F -:1095A400000400000000006600000000000000004D -:1095B40000000066000000000020000000000046DB -:1095C4000000000000200000000000420000000035 -:1095D40000260000000000000000000000620000FF -:1095E40000000064000000000046000000000024A9 -:1095F40000000000004600000000002200000000FF -:1096040000200000000000460000000000200000D0 -:1096140000000002000000000044000000000044BC -:109624000000000000660000000000400000000090 -:1096340000260000000000200000000000020000DE -:1096440000000020000000000002000000000022D2 -:109654000000000000440000000000620000000060 -:10966400000400000000002200000000004400008C -:10967400000000440000000000330000000000442B -:109684000000000000600000000000160000000060 -:1096940000610000000000060000000000660000F9 -:1096A400000000110000000000440000000000223F -:1096B400000000000044000000000044000000001E -:1096C400002200000000004000000000002600000E -:1096D40000000060000000000046000000000004DC -:1096E4000000000000000000000000440000000032 -:1096F40000440000000000000000000000660000BC -:1097040000000000000000000066000000000020CF -:1097140000000000006600000000002200000000BD -:1097240000560000000000230000000000000000BC -:109734000000002200000000006400000000004659 -:1097440000000000002000000000004600000000AF -:10975400002200000000002000000000006600005D -:109764000000002200000000000200000000004091 -:10977400000000000044000000000026000000007B -:109784000040000000000004000000000020000071 -:1097940000000002000000000020000000000002A1 -:1097A400000000000022000000000044000000004F -:1097B4000022000000000000000000000022000061 -:1097C4000000000000000000000000000000003362 -:1097D4000000000000000000000000200000000065 -:1097E400820080010300000032000000000023001A -:1097F4000000000002000000000022000000000041 -:109804001100000000000000000000002200000021 -:109814000000000000000000000000000000220022 -:109824000000000000000000000022000000000012 -:109834002000000000000200000000000000000002 -:109844000000000000000000000000000000000014 -:1098540000000000000000000000220000000000E2 -:1098640000000000000022000000000020000000B2 -:10987400000022000000000022000000000056004A -:1098840000000000230000000000000000000000B1 -:109894002200000000002000000000000200000080 -:1098A4000000200000000000020000000000220070 -:1098B4000000000020000000000022000000000062 -:1098C4002200000000000200000000000000000070 -:1098D4000000000000000000220000000000000062 -:1098E4000000000000000000000020000000000054 -:1098F4000200000000002000000000000200000040 -:1099040000003300000000005400000000002300A9 -:109914000000000000000000000033000000000010 -:1099240011000000000020000000000013000000EF -:1099340000001000000000002100000000003200C0 -:1099440000000000230000000000130000000000DD -:1099540022000000000011000000000000000000D0 -:1099640000003300000000001100000000000000AF -:1099740000000000220000000000000000000000C1 -:10998400320000000000310000000000030000006D -:1099940000000000000000001100000000001100A1 -:1099A40000000000000000000000110000000000A2 -:1099B400320000000000010000000000320000003E -:1099C4000000210000000000020000000000000070 -:1099D4000000000010000000000023000000000050 -:1099E400000000000000330000000000310000000F -:1099F400000002000000000031000000000012001E -:109A040000000000230000000000310000000000FE -:109A1400120000000000010000000000100000001F -:109A240000001100000000000100000000003200EE -:109A340000000000010000000000330000000000EE -:109A440020000000000002000000000020000000D0 -:109A54000000020000000000330000000000550078 -:109A640000000000330000000000100000000000AF -:109A7400330000000000110000000000210000007D -:109A8400000013000000000011000000000031007D -:109A9400000000001200000000001100000000009F -:109AA400110000000000230000000000110000006D -:109AB400000010000000000033000000000011004E -:109AC400000000000100000000002200000000006F -:109AD400000000000000330000000000310000001E -:109AE40000001300000000110362003F8200000127 -:109AF400030000000000000000000000000000005F -:109B04000000000000000000000000000000000051 -:109B14000000000000000000000000000000000041 -:109B24000000000000000000000000000000000031 -:109B34000000000000000000000000000000000021 -:109B44000000000000000000000000000000000011 -:109B54000000000000000000000000000000000001 -:109B640000000000000000000000000000000000F1 -:109B740000000000000000000000000000000000E1 -:109B840000000000000000000000000000000000D1 -:109B940000000000000000000000000000000000C1 -:109BA40000000000000000000000000000000000B1 -:109BB40000000000000000000000000000000000A1 -:109BC4000000000000000000000000000000000091 -:109BD4000000000000000000000000000000000081 -:109BE4000000000000000000000000000000000071 -:109BF4000000000000000000000000000000000061 -:109C04000000000000000000000000000000000050 -:109C14000000000000000000000000000000000040 -:109C24000000000000000000000000000000000030 -:109C34000000000000000000000000000000000020 -:109C44000000000000000000000000000000000010 -:109C54000000000000000000000000000000000000 -:109C640000000000000000000000000000000000F0 -:109C740000000000000000000000000000000000E0 -:109C840000000000000000000000000000000000D0 -:109C940000000000000000000000000000000000C0 -:109CA40000000000000000000000000000000000B0 -:109CB40000000000000000000000000000000000A0 -:109CC4000000000000000000000000000000000090 -:109CD4000000000000000000000000000000000080 -:109CE4000000000000000000000000000000000070 -:109CF4000000000000000000000000000000000060 -:109D0400000000000000000000000000000000004F -:109D1400000000000000000000000000000000003F -:109D2400000000000000000000000000000000002F -:109D3400000000000000000000000000000000001F -:109D4400000000000000000000000000000000000F -:109D540000000000000000000000000000000000FF -:109D640000000000000000000000000000000000EF -:109D740000000000000000000000000000000000DF -:109D840000000000000000000000000000000000CF -:109D940000000000000000000000000000000000BF -:109DA40000000000000000000000000000000000AF -:109DB400000000000000000000000000000000009F -:109DC400000000000000000000000000000000008F -:109DD400000000000000000000000000000000007F -:109DE400000000000000000000000000000000006F -:109DF400000000000000000000000000000000005F -:109E0400000000000000000000000000000000004E -:109E1400000000000000000000000000000000003E -:109E2400000000000000000000000000000000002E -:109E3400000000000000000000000000000000001E -:109E4400000000000000000000000000000000000E -:109E540000000000000000000000000000000000FE -:109E640000000000000000000000000000000000EE -:109E740000000000000000000000000000000000DE -:109E840000000000000000000000000000000000CE -:109E940000000000000000000000000000000000BE -:109EA40000000000000000000000000000000000AE -:109EB400000000000000000000000000000000009E -:109EC400000000000000000000000000000000008E -:109ED400000000000000000000000000000000007E -:109EE400000000000000000000000000000000006E -:109EF4000000008200800103000000000000000058 -:109F0400000000000000000000000000000000004D -:109F1400000000000000000000000000000000003D -:109F2400000000000000000000000000000000002D -:109F3400000000000000000000000000000000001D -:109F4400000000000000000000000000000000000D -:109F540000000000000000000000000000000000FD -:109F640000000000000000000000000000000000ED -:109F740000000000000000000000000000000000DD -:109F840000000000000000000000000000000000CD -:109F940000000000000000000000000000000000BD -:109FA40000000000000000000000000000000000AD -:109FB400000000000000000000000000000000009D -:109FC400000000000000000000000000000000008D -:109FD400000000000000000000000000000000007D -:109FE400000000000000000000000000000000006D -:109FF400000000000000000000000000000000005D -:10A00400000000000000000000000000000000004C -:10A01400000000000000000000000000000000003C -:10A02400000000000000000000000000000000002C -:10A03400000000000000000000000000000000001C -:10A04400000000000000000000000000000000000C -:10A0540000000000000000000000000000000000FC -:10A0640000000000000000000000000000000000EC -:10A0740000000000000000000000000000000000DC -:10A0840000000000000000000000000000000000CC -:10A0940000000000000000000000000000000000BC -:10A0A40000000000000000000000000000000000AC -:10A0B400000000000000000000000000000000009C -:10A0C400000000000000000000000000000000008C -:10A0D400000000000000000000000000000000007C -:10A0E400000000000000000000000000000000006C -:10A0F400000000000000000000000000000000005C -:10A10400000000000000000000000000000000004B -:10A11400000000000000000000000000000000003B -:10A12400000000000000000000000000000000002B -:10A13400000000000000000000000000000000001B -:10A14400000000000000000000000000000000000B -:10A1540000000000000000000000000000000000FB -:10A1640000000000000000000000000000000000EB -:10A1740000000000000000000000000000000000DB -:10A1840000000000000000000000000000000000CB -:10A1940000000000000000000000000000000000BB -:10A1A40000000000000000000000000000000000AB -:10A1B400000000000000000000000000000000009B -:10A1C400000000000000000000000000000000008B -:10A1D400000000000000000000000000000000007B -:10A1E400000000000000000000000000000000006B -:10A1F400000000000000000000000000000000005B -:10A20400000000000000000000000000000000004A -:10A21400000000000000000000000000000000003A -:10A22400000000000000000000000000000000002A -:10A23400000000000000000000000000000000001A -:10A24400000000000000000000000000000000000A -:10A2540000000000000000000000000000000000FA -:10A2640000000000000000000000000000000000EA -:10A2740000000000000000000000000000000000DA -:10A2840000000000000000000000000000000000CA -:10A2940000000000000000000000000000000000BA -:10A2A40000000000000000000000000000000000AA -:10A2B400000000000000000000000000000000009A -:10A2C400000000000000000000000000000000008A -:10A2D400000000000000000000000000000000007A -:10A2E400000000000000000000000000000000006A -:10A2F400000000000000000000002206DE0106004D -:10A30400040000000103000001000000010000003F -:10A3140003000000010000000F0000000F00000017 -:10A324000000000036A500080018024010000000DC -:10A33400060400003BA500080018024008000000C5 -:10A34400060301003EA500080018024002000000B8 -:10A354000601010045A500080018024004000000A1 -:10A36400060201000000000000000000005400404C -:10A374000004024040000000060004000004024003 -:10A38400000200000900040000002000801A0600FA -:10A39400004000001F20000000000000000000003A -:10A3A40000580040001402400200000001000400B4 -:10A3B40000140240010000000000040000004000FE -:10A3C400801A0600FFBF00002122000000000000E8 -:10A3D400F513000801000300820007008400070051 -:10A3E4000100030002000300400003008000070096 -:10A3F40084000700000001000000000000000008C5 -:10A40400004000080080000800C0000800000108A7 -:10A414000000020800000408000006080000080804 -:10A4240000000A0800000C0800000E0800000800E4 -:10A4340010001800200028003000380040004800B8 -:10A4440050005800537475636B20627574746F6E9A -:10A4540020726567697374657220697320696E760A -:10A46400616C69642C20636C656172696E672E008F -:10A47400427574746F6E20697320707573686564B7 -:10A4840020617420626F6F740069636534306C708E -:10A494002E630043444F4E45206E6F74206C6F77DB -:10A4A40020647572696E6720726573657400435227 -:10A4B40045534554206E6F7420686967682064753D -:10A4C40072696E6720726573657400446973706C99 -:10A4D400617920627573792D776169742074696D6F -:10A4E400656F7574206578706972656421004469CC -:10A4F40073706C617920696E697469616C697A65DD -:10A5040064206166746572200020726574726965E6 -:10A51400732E00446973706C617920696E69746983 -:10A52400616C697A6174696F6E206661696C6564D7 -:10A534002E004261636B0055700053656C65637453 -:10A5440000446F776E006372632E63006932632E7A -:10A554006300493243206465766963652049442079 -:10A564006F7574206F6620626F756E647320256446 -:10A5740020286D61783A20256429006F6E0073707D -:10A58400692E6300206973206F7574736964652094 -:10A5940073797374656D20666C6173680D0A00735A -:10A5A400797374656D5F666C6173685F65726173FE -:10A5B4006528002C2000290D0A006661696C656419 -:10A5C40020746F20657261736520736563746F72A4 -:10A5D400200073797374656D5F666C6173685F776F -:10A5E4007269746528006661696C656420746F2003 -:10A5F40077726974652061646472657373200049BD -:10A604006E76616C6964206669726D77617265202B -:10A614006465736372697074696F6E210043686561 -:10A62400636B73756D6D696E67206669726D7761B2 -:10A6340072652075706461746500496E76616C6939 -:10A6440064206669726D77617265204352432069A4 -:10A654006E2053504920666C6173682100657261F5 -:10A6640073655F6F6C645F6669726D7761726500B4 -:10A6740077726974655F6E65775F6669726D77611D -:10A68400726500436865636B73756D6D696E6720F1 -:10A69400002062797465730D0A00436865636B7307 -:10A6A400756D202D2077616E746564200020676FBE -:10A6B4007420004F757220696E7465726E616C202F -:10A6C400666C61736820636F6E74656E7473206169 -:10A6D4007265206261642028636865636B73756DBD -:10A6E400206661696C656429212054686973206956 -:10A6F40073207265616C6C792062616421004F750E -:10A70400722070726576696F7573206669726D77F1 -:10A7140061726520757064617465206661696C6539 -:10A72400642C2061626F7274696E67207570646155 -:10A7340074652E004E6577206669726D7761726567 -:10A7440020697320617661696C61626C6521004CDB -:10A754006F6164696E67207265636F7665727920D4 -:10A764006669726D77617265004661696C65642023 -:10A77400746F206C6F6164207265636F76657279A3 -:10A78400206669726D776172652C20737472696BCF -:10A7940065206F6E652E2054727920616761696E41 -:10A7A4002E004661696C656420746F206C6F61646F -:10A7B400207265636F76657279206669726D776160 -:10A7C40072652C20737472696B652074776F2E2008 -:10A7D40054727920616761696E2E004661696C6507 -:10A7E4006420746F206C6F6164207265636F76659A -:10A7F4007279206669726D776172652C2073747248 -:10A80400696B652074687265652E20534144205736 -:10A81400415443480048415244204641554C540059 -:10A8240065786974207374616E64627900205F5F77 -:10A834005F5F202020202020202020202020202096 -:10A84400205F5F002F5C20205F605C2020202020A0 -:10A854002020202020202F275F5F605C005C205C8C -:10A864002C5C4C5C5F5C202020205F5F5F202F5CB1 -:10A87400205C2F5C205C20205F5F20205F5F202015 -:10A884005F5F20205F5F20205F5F00205C2F5F5CA4 -:10A894005F5F205C20202F27205F20605C205C20ED -:10A8A4005C205C205C2F5C205C2F5C205C2F5C2097 -:10A8B4005C2F5C205C2F5C205C002020202F5C201F -:10A8C4005C4C5C205C2F5C205C2F5C205C205C205A -:10A8D4005C5F5C205C205C205C5F2F205C5F2F2031 -:10A8E4005C205C205C5F5C205C002020205C20609D -:10A8F4005C5F5F5F5F5C205C5F5C205C5F5C205C36 -:10A904005F5F5F5F2F5C205C5F5F5F785F5F5F2FDF -:10A91400275C2F605F5F5F5F205C00202020205C4D -:10A924002F5F5F5F5F5F2F5C2F5F2F5C2F5F2F5C5C -:10A934002F5F5F5F2F20205C2F5F5F2F2F5F5F2FC4 -:10A94400202020602F5F5F5F2F3E205C00202020AE -:10A9540020202020202020202020202020202020F3 -:10A9640020202020202020202020202020202020E3 -:10A9740020202020202020202F5C5F5F5F2F0020DC -:10A9840020202020202020202020202020202020C3 -:10A9940020202020202020202020202020202020B3 -:10A9A400202020202020202020205C2F5F5F2F00EB -:10A9B4004C617374206669726D7761726520626F91 -:10A9C4006F742077617320737461626C653B2063DC -:10A9D4006C65617220737472696B657300486F6C87 -:10A9E4006420646F776E205550202B204241434BE6 -:10A9F40020666F72203520736563732E20746F2078 -:10AA0400666F7263652D626F6F7420505246004604 -:10AA140069726D776172652069732065726173650F -:10AA240064005761746368646F672063617573655C -:10AA340064206120726573657400536F6674776176 -:10AA44007265206661696C757265206361757365F2 -:10AA5400642061207265736574004661696C656485 -:10AA640020746F207374617274206669726D7761EB -:10AA740072652C20737472696B65207468726565E5 -:10AA84002E004661696C656420746F207374617272 -:10AA940074206669726D776172652C2073747269B3 -:10AAA4006B652074776F2E004661696C6564207451 -:10AAB4006F207374617274206669726D7761726558 -:10AAC4002C20737472696B65206F6E652E00426F63 -:10AAD4006F74696E67206669726D77617265204074 -:10AAE40020002E2E2E0D0A0D0A00466F7263652D6E -:10AAF400626F6F74696E67207265636F76657279D1 -:10AB0400206D6F64652E2E2E00536F66747761720C -:10AB140065206661696C7572653B20726573657446 -:10AB240074696E6721004153534552543A20002002 -:10AB340020003A0041535345525400415353455267 -:10AB4400544E002A2A2A20575446200053544D3389 -:10AB5400320053544D3332207065726970686572E7 -:10AB6400616C206C696272617279207472697070B0 -:10AB7400656420616E206173736572740043524F83 -:08AB8400414B204F4F4D000032 -:10AB8C00FF00000000010203040102030406070891 -:04AB9C0009000000AC -:04000005080001B43A -:00000001FF diff --git a/bin/boot/boot_v1_5@1447134832.bin b/bin/boot/boot_v1_5@1447134832.bin deleted file mode 100755 index d014ccbc42..0000000000 Binary files a/bin/boot/boot_v1_5@1447134832.bin and /dev/null differ diff --git a/bin/boot/boot_v1_5@1447134832.hex b/bin/boot/boot_v1_5@1447134832.hex deleted file mode 100644 index 6100a99676..0000000000 --- a/bin/boot/boot_v1_5@1447134832.hex +++ /dev/null @@ -1,1062 +0,0 @@ -:020000040800F2 -:10000000482001208D2C0008ED2F0008ED2F00085E -:10001000ED2F0008ED2F0008ED2F00080000000074 -:10002000000000000000000000000000ED2F0008AC -:10003000ED2F000800000000ED2F0008ED2F000854 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0C2FD3A -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:08016000AFFD05B05DF804FBE2 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:080188009BFD05B05DF804FBCE -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F06CFCDDF820C026 -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F00DFC089B9CE0BA4268 -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00C83F00084B45F7DBEDE70D9907910798BC -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01CF3F0008D2 -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C00BFF34F8F044A054BD16801F4E0610B43E0 -:10191C00D360BFF34F8FFEE700ED00E00400FA0543 -:10192C0010B5044608484021FFF784FC0028F9D084 -:10193C0005482146FFF77AFC03484021FFF77AFC63 -:10194C000028F9D010BD00BF00480040014608B582 -:10195C000448FFF757FC03480221FFF755FC002809 -:10196C00F9D008BD0038004000B9074803680133BE -:10197C0008D04368013305D0C069044BC31A5842E0 -:10198C005841704700207047004000086F57654E63 -:10199C00074B1A69C2F306427F2A07D11869C0F3B4 -:1019AC000C00B0F1FF0358425841704700207047BB -:1019BC000028004000F13F4000F57E00800A082816 -:1019CC000FD8084A52F820305BB1013B42F8203066 -:1019DC003BB9012101FA00F0034B1A6B22EA00001B -:1019EC0018637047200000200038024000F13F408F -:1019FC0000F57E00800A08280DD8074A52F82030DE -:101A0C00591C42F8201033B9012101FA00F0034BA4 -:101A1C001A6B1043186370472000002000380240F6 -:101A2C0010B50C4C2046FFF7E1FF072001F01AFB24 -:101A3C004FF4805100222046FFF776FB042001F082 -:101A4C0011FB2046FFF7B6FF4FF480400021FFF753 -:101A5C0067F810BD0004024010B50B4C01214FF487 -:101A6C008040FFF75DF82046FFF7C0FF20464FF49B -:101A7C0080510122FFF758FB072001F0F3FA2046B2 -:101A8C00FFF798FF10BD00BF00040240F0B55E25C3 -:101A9C006843204BB0FBF1F51A68ADF6DC3D95427E -:101AAC0034D002AC00214FF43D6220461D60FEF79D -:101ABC00CEFA1B238DE81800174810214022692309 -:101ACC0001F0B3FA08238DE818001448602318219C -:101ADC006A2201F0AAFA0DF2FA760DF28E73002248 -:101AEC00AA420DD002F11900C0B2C108012700F0C2 -:101AFC00070007FA00F05F5C013238435854EFE7F7 -:101B0C001233B342EBD1204601F0F9FA0DF6DC3D6D -:101B1C00F0BD00BF00000020FC370008903B00081F -:101B2C002DE9F043ADF6DC3D02AC00214FF43D62F3 -:101B3C0080462046FEF78BFA2C238DE818002022D5 -:101B4C00284818215C2301F070FA1C2215460F2737 -:101B5C00974007EA0807D740234B242101FB07379E -:101B6C000DF50366002117F801C00023082B07D0E0 -:101B7C000120984010EA0C0F0CBF0020012003E05C -:101B8C007818407800F0010078B15819C0B24FEACB -:101B9C00D00E4FF0010900F0070009FA00F016F81A -:101BAC000E9040EA090006F80E000133092BDDD136 -:101BBC000231242906F11206D5D10B35742DA2F170 -:101BCC000402C4D10E238DE81800102190226D233D -:101BDC00064801F02AFA204601F091FA0DF6DC3D98 -:101BEC00BDE8F083EC350008E83200082835000821 -:101BFC0010B5ADF6D83D02AC204600214FF43D6245 -:101C0C00FEF725FA1B238DE81800102140226923CA -:101C1C00044801F00AFA204601F071FA0DF6D83D9D -:101C2C0010BD00BFFC3700082DE9F74F434E444F61 -:101C3C0001214FF48040FEF773FF3046FFF7D6FECC -:101C4C003846404CFFF7D2FE30460D210522FFF7F7 -:101C5C0070FA052230460F21FFF76BFA3046214609 -:101C6C000125FFF70EFA2023384621462360257103 -:101C7C00A5714FF0000A01934FF4805BFFF701FA56 -:101C8C00304621464FF002084FF48049257184F804 -:101C9C0006A0C4F800B0FFF7F4F93046214684F8EA -:101CAC000480C4F80080FFF7ECF921462571A5717A -:101CBC00C4F80090244C3046FFF7E3F92046FFF7B8 -:101CCC0049F920462149FFF76FFA20462946FFF7CC -:101CDC008AFA019B384619465246FFF725FA2A46DE -:101CEC0030464946FFF720FA40462946FEF718FFD2 -:101CFC00A4F5505480232380F92323854FF4F0421C -:101D0C004FF6FF13E3623046A28329462364424612 -:101D1C00A4F820B0FFF70DFA29462046A582FEF75D -:101D2C002FFD3846FFF746FE3046FFF743FE484688 -:101D3C005146FEF7F5FE03B0BDE8F08F00040240FB -:101D4C000008024004000020003800400C00002075 -:101D5C0010B5074C2046FFF749FE20461021FFF72F -:101D6C00DFF92046FFF726FE012001F07BF910BDBC -:101D7C000000024038B50A4B0C2404FB00346068A8 -:101D8C00FFF734FE21896068FFF7C4F905466068E7 -:101D9C00FFF710FED5F1010038BF002038BD00BFA1 -:101DAC00A032000810B5064C2046FFF71FFE204657 -:101DBC001021FFF7B7F92046FFF7FCFD10BD00BF5F -:101DCC0000000240F0B587B0FFF7E2FD002841D1DA -:101DDC000320FFF769F807460420FFF765F8474B27 -:101DEC000A241A68C2F30545C2F30626C2F302138D -:101DFC0002F00F0204FB0322330906F00F0604FB6A -:101E0C0003662B0905F00F0504FB03533C2505FB6A -:101E1C00036305FB0323191A44BF01F5A83101F52F -:101E2C00C07107EB91210320FFF72EF803A8FFF7F1 -:101E3C00A8F903A8FEF704FF01A8FFF7A8F901A968 -:101E4C000020FEF741FF02A8FFF7A7F9002002A926 -:101E5C00FEF7A4FF0A2442F2107501F072F910B9D2 -:101E6C00013DFAD107E0264D01F06BF908B1013DB7 -:101E7C00FAD1013CEFD10020FEF7A2FD00214FF278 -:101E8C000400FFF72DF84FF43C50FFF755F81D4BAD -:101E9C0001201A6842F004021A60FEF791FD1A4BF9 -:101EAC001B781BB1194801F06BF908E01848FEF7D4 -:101EBC0011F9174902461748FEF7EDF970B11648AB -:101ECC0001F05EF90020FEF7FFFF08200121FEF76C -:101EDC00C7FF4FF400400121FFF702F80F4801F053 -:101EEC004FF9084B1A6842F002021A600C4B1A693F -:101EFC0042F004021A6130BF07B0F0BD0028004068 -:101F0C00A086010000700040017AFF1F763900089E -:101F1C008B3900082078FF1F90390008A53900087C -:101F2C0000ED00E0B0F1006F10B504460BD20D4887 -:101F3C0001F01DF9204601F029F90B4801F020F9B8 -:101F4C004FF0FF3010BD094A002352F8041FA14284 -:101F5C0002D851688C4202D30133072BF5D11846B5 -:101F6C0010BD00BFB93A0008B63900087C32000831 -:101F7C0008B5024801F004F9FFF7C2FCCF3900089C -:101F8C0010B504460A480221FFF73EF90028F9D0A3 -:101F9C0007482146FFF736F905480121FFF734F9C8 -:101FAC000028F9D00248FFF72AF9C0B210BD00BFD3 -:101FBC00003001402DE9F04F87B00121814600AF80 -:101FCC000020FFF763FD042001F067F807F10C0017 -:101FDC0049460C2201F0FFF8FD683C690C2DD7F83E -:101FEC0014B013D0994801F0C2F8284601F0CEF88D -:101FFC00974801F0BCF8204601F0C8F8954801F06C -:10200C00B6F8584601F0C2F8934833E0934801F013 -:10201C00B7F84FF480500121FEF776FD09F10C0959 -:10202C00EA46FEF7EFFBA0B06E462546C846802D6B -:10203C000CD941463046802201F0CDF830462021A3 -:10204C00FEF7ECFB803D08F18008F0E725B1304647 -:10205C0041462A4601F0BFF82946304600F0CAFF37 -:10206C00002180464FF48050FEF74EFDD845D546F2 -:10207C0004D07B4801F084F80120E3E088B0684686 -:10208C004946202201F0A7F86846FFF76DFC7860FE -:10209C007448D54601F074F87B682BB9724801F08E -:1020AC006FF804F5404600E02646002E3BD06F4802 -:1020BC00FFF738FF05466E483044FFF733FF002D1D -:1020CC00824630DB00282EDB431B01334FEA430BE7 -:1020DC0000205946FFF7DAFCFEF7C0FB654B0026E3 -:1020EC0003EB45033B6055451BDCF320FEF7CCFBB3 -:1020FC003B68002133F81600FEF7E6FB082806F1D2 -:10210C00010608D05C4801F032F8284601F03EF890 -:10211C0001F025F805E030465946FFF7B7FC0135CC -:10212C00E1E7FEF7A9FB554801F02AF87B680BB1F3 -:10213C004E4B00E0524B7B6063083B600025A54290 -:10214C003CD2661BB6F5803F28BF4FF480362EB1CB -:10215C004C4805EB0901324601F03DF8D7F804A0D4 -:10216C00FEF77CFBF320FEF78FFBAA444FF0000B2D -:10217C00B34519D043490BEB0A0011F80B10FEF7CD -:10218C00DBFB08280DD0404800F0F1FF504600F072 -:10219C00FDFF00F0E4FFFEF76FFB3C4800F0F0FFA2 -:1021AC000CE00BF1010BE3E7FEF766FB3B68214605 -:1021BC0003EB5500FFF76AFC5D44C0E7344800F0C0 -:1021CC00D6FF204600F0E2FF324800F0D9FF002095 -:1021DC00FFF7CAFB20B1304800F0D2FF234D03E0DB -:1021EC002E4800F0CDFF264D4FF480500121A6085B -:1021FC00FEF78AFCFEF706FB31462846FEF70EFB7F -:10220C0004F0030105EB860000F0F4FE0021044607 -:10221C004FF48050FEF778FC214800F0A8FF4046B0 -:10222C0000F0B4FF1F4800F0A2FF204600F0AEFF04 -:10223C0000F095FF444504D01B4800F0A1FF02209C -:10224C0000E000201C37BD46BDE8F08FDA390008ED -:10225C00E3390008ED390008F4390008133A000896 -:10226C00303A0008533A0008663A0008004000086B -:10227C00FF3F0008D83200087E3A0008963A000862 -:10228C000000010844000020A93A0008C23A0008E6 -:10229C00CD3A0008DB3A0008E23A0008073B000898 -:1022AC002C3B00083F3B0008453B000870B54FF441 -:1022BC0080500121FEF740FCFFF774FD0520164C01 -:1022CC00FFF75EFE002C04F1FF341FDCFFF740FD2E -:1022DC00FFF768FD9F20FFF753FEA920FFF750FE84 -:1022EC000546A920FFF74CFE0646A920FFF748FE3D -:1022FC003602044646EA0545FFF72AFD4FF48050A6 -:10230C000021FEF719FC45EA040070BDA920FFF777 -:10231C0037FEC307D6D4D9E70090D00308B50446DE -:10232C00114800F024FF204600F030FF00F017FFAA -:10233C002046FFF7F5FB00F004FF0C4B20F00400E7 -:10234C0018701D4600F0FDFE2B7800F0FB00834258 -:10235C0001D10B2401E000F0C0FE013CF2D04FF49F -:10236C007A7000F07FFEF8E7F03B00084400012093 -:10237C0080B5464B4FF4C06290B01A6001214FF00B -:10238C008050FEF7CDFB0120FEF714FB4048FFF711 -:10239C002DFB4FF480200121FEF7C2FB3C480A21A3 -:1023AC000722FEF7C6FE072239480B21FEF7C1FEB5 -:1023BC0010AF4FF4806347F8383D00244FF001080C -:1023CC000225334839468DF80E408DF80F808DF874 -:1023DC000C508DF80D50FEF754FE4FF400632C4852 -:1023EC00394602938DF80C5004AEFEF74AFE4FF4BA -:1023FC006133049327480C233146ADF81A30ADF8FD -:10240C001440ADF81640ADF81840ADF81C40FEF77E -:10241C00CFFD41461F48FEF7FDFE1D48FFF7CAFAE7 -:10242C001D4800F0A4FE1D4B586F00F0AFFE00F0ED -:10243C0096FE7920FEF7C8FB404502D1184800F003 -:10244C009FFE012000F014FE10B90121FEF71CFDC7 -:10245C004120FEF7B9FB58BB124800F091FE012059 -:10246C00FEF7E0FA47F230544120FEF7ADFBF8B925 -:10247C004FF47A7000F0F6FD013CF5D10A4800F0FB -:10248C007FFEFFF7D1FB094837E100BF003C02405B -:10249C000008024000480040FC3B000800380240A5 -:1024AC000C3C00081C3C0008343C0008114550FE54 -:1024BC004FF48070FEF70AFB0120FEF71FFBFEF7BE -:1024CC00E1FB0020FEF77CFA01214FF48040FEF77F -:1024DC0033FBBB48FFF78AFA3046FEF712FE0024A6 -:1024EC000125B74831468DF815408DF816408DF80A -:1024FC00174004958DF81450FEF7C3FD2246B048E2 -:10250C002946FEF711FEAE48FFF754FAAD4C04F124 -:10251C00300954F8040CFFF769FA3046FEF7F1FD68 -:10252C00236854F8040C4FF000083146022504933C -:10253C008DF814808DF815508DF817800C34FEF73B -:10254C00A0FD54F8100CFFF735FA4C45E1D14FF4CF -:10255C0080404146FEF7F0FA4FF4807000F088FDA1 -:10256C00002800F0CC804FF4807000F08BFDFFF75A -:10257C0029FC954800F004FE0220FEF795FC9349D7 -:10258C00884202D00220FEF77FFC914800F0EFFD5C -:10259C000220FEF789FC00F0F9FD00F0E0FD4FF49D -:1025AC00804000F065FD90B18A4800F0E9FD4FF4E1 -:1025BC00804000F067FD082000F064FD102000F062 -:1025CC0061FD202000F05EFD402000F05BFDFFF778 -:1025DC002BFBFFF70DFB01214FF48050FEF7ACFAFB -:1025EC007748FFF703FA01200146FEF78DFA052123 -:1025FC000A467348FEF79DFD714806210522FEF739 -:10260C0098FD05226E480721FEF793FD0024022554 -:10261C00C0236B4839468DF80C508DF80D50029341 -:10262C008DF80E408DF80F40FEF72BFD20236448EB -:10263C0039468DF80F5002930125FEF722FD102329 -:10264C0039465F4802938DF80C508DF80F50FEF709 -:10265C0018FD6148FEF77EFC4FF48273ADF8123022 -:10266C004FF40073ADF81A305B4807233146ADF8D0 -:10267C002030ADF81040ADF81440ADF81640ADF870 -:10268C001840ADF81C40ADF81E40FEF78DFD2946F4 -:10269C005148FEF7A8FD4A48FFF78CF9FFF782FB7B -:1026AC00AB20FFF76DFCFFF753FB642000F0DAFC66 -:1026BC0021464FF48050FEF73FFAFFF7F7FD28460E -:1026CC00FEF7F2FB25460490E8B2FFF753FB98BBEC -:1026DC0070550135042DF7D1049D3DB13F4800F0F4 -:1026EC0046FD284600F052FD00F039FD0120294638 -:1026FC00FEF7CAFB002C3DD03948FFF70FFE2846E9 -:10270C00FEF764F9012814D12846FEF769F94FF455 -:10271C000060FEF703FC18B1324800F031FDB9E05F -:10272C00314C00F00EFD002800F00481013CF8D182 -:10273C00B0E000F0BDFC1CE7735D042B0ADD2B48F8 -:10274C0000F01EFD049800F021FD01200021FEF791 -:10275C009BFB0FE00133DBB2042B7355B9DD24482E -:10276C0000F005FD284600F011FD224800F008FDA0 -:10277C000124AEE7FFF79AFD1F4B0344012B01D94F -:10278C001E48BAE71E4800F0F2FC0020FEF78CFB56 -:10279C0000F0FCFC00F0E3FC802000F069FC18B1B8 -:1027AC00802000F06FFC73E04FF4003000F060FC10 -:1027BC00044638B34FF4003000F064FC04E100BF71 -:1027CC0000000240A83200086C3C0008708641569C -:1027DC00A83C0008BD3C0008003001402B3D00081F -:1027EC00014550FE513C0008A0860100EA3C00085F -:1027FC00163D0008213D0008EA44DFFF024550FE6B -:10280C00573D00080120FFF7B5FAD8B12046FFF775 -:10281C00B1FAB8B1B74800F0B3FC41F288340120EA -:10282C00FFF7A8FA002800F08B800020FFF7A2FA2F -:10283C00002800F085804FF47A7000F013FC013C06 -:10284C00EDD1C1E00020FFF78FF808B1AA4B00E0F2 -:10285C00AA4B0CCB013301D0013271D1A84800F046 -:10286C008FFCB1E0A74800F08BFC082000F00AFCBC -:10287C00102000F007FC202000F004FC402000F0A9 -:10288C0001FC0024042000F0FDFB002C40F008812A -:10289C009D4832E7FFF77CF8002847D0002001A9BB -:1028AC009A4DFEF759FA344600203946FEF7B8FA2D -:1028BC000FCD0FC40FCD0FC495E80F0084E80F00A7 -:1028CC009DF8092010A901EB82039DF80BE053F849 -:1028DC00343C40F26D1101FB0E3303EB9E030EF101 -:1028EC00010E1EF0030F02D1022A88BF01339DF89E -:1028FC000A409DF80410013C2344182404FB0314E3 -:10290C009DF805103C2202FB04149DF806100320D0 -:10291C0002FB0414FEF7C8FA01190320FEF7B4FAFF -:10292C0004200021FEF7B0FA00F0C2FB784800F05A -:10293C0027FC1EE6774800F023FCFFF743FAF5E787 -:10294C007D20FEF741F970B94FF4007000F090FB58 -:10295C0048B9714800F014FC082000F093FB1020DB -:10296C0000F090FB85E07D20FEF72EF910B16B484E -:10297C0000F006FC4FF4007000F07AFB10B16848D0 -:10298C0000F0FEFB4FF4007000F07CFB102000F018 -:10299C006FFB48B1634800F0F3FB082000F072FBBA -:1029AC00102000F06FFB12E0082000F061FB20B15A -:1029BC005D4800F0E5FB102003E05C4800F0E0FB14 -:1029CC00082000F06AFB54E0594800F0D9FB594844 -:1029DC0000F0D6FB4FF40010FFF7ECFA10B1022810 -:1029EC0028D809E0202000F04DFB402000F04AFBE5 -:1029FC00802000F052FB1DE0082000F04EFB102060 -:102A0C0000F04BFB202000F033FB20B94A4800F0CB -:102A1C00B7FB202009E0402000F02AFB00287FF4BF -:102A2C0021AF464800F0ACFB402000F036FB4FF4E1 -:102A3C0000701AE0012425E7414800F0A1FB0820B2 -:102A4C0000F020FB102000F01DFB202000F01AFBF2 -:102A5C00402000F017FB2046FFF7ACFA022840F0AC -:102A6C00F480082000F019FB102000F016FB00F099 -:102A7C0034FB022000F0FCFA90B1042000F0F8FACC -:102A8C0004460028D8D02F4800F07AFB042000F030 -:102A9C00F9FA022000F0F6FA4FF4004000F0F2FAD6 -:102AAC004FF4005000F0E4FA04464FF4805000F06C -:102ABC00DFFA400040EA84044FF4006000F0D8FADA -:102ACC00E4B22043431C03F0FF03013B4FF40060CE -:102ADC00062B5CD8DFE803F03C383C043C383C0067 -:102AEC0000F0D0FA4FF4805000F0CCFA4FF40050C4 -:102AFC002EE000BF633D0008004000080000010804 -:102B0C00953D0008BF3E0008034550FE50320008BA -:102B1C005C3C00085E3F0008A83D0008B93D000879 -:102B2C00D13D0008F13D0008073E00081D3E00089D -:102B3C00733F0008333E00084D3E0008863E0008F7 -:102B4C002F3F0008F93E000800F09CFA4FF480502B -:102B5C0000F0A3FA45F25550FDF70AFF0420FDF7EB -:102B6C000DFF40F6FF70FDF70FFF0020FDF700FF93 -:102B7C004FF480500121FDF765FEFDF713FFFDF7C3 -:102B8C0009FF0020FEF7F0FE68B9354B0CE000F0B1 -:102B9C0079FA4FF4805000F075FA4FF4005000F0C1 -:102BAC0071FA3048A9E5304B5D6830481E6800F07A -:102BBC00DEFA284600F0EAFA00F0D1FA00F0CFFA7B -:102BCC0000239A0002F16042013302F561424FF09A -:102BDC00FF31082BC2F88010C2F88011F1D1244BC0 -:102BEC0000241C635C639C631C645C64FDF700FF45 -:102BFC0020480121FDF7ACFF21461E48FDF7A8FF38 -:102C0C00F1200121FDF7B0FF2146F120FDF7ACFFCB -:102C1C0001200146FDF7B4FF21460120FDF7B0FF6E -:102C2C0015480121FDF7B8FF21461348FDF7B4FF05 -:102C3C0012480121FDF7BCFF10482146FDF7B8FFF3 -:102C4C0063B64FF0FF3EB5462847042000F01AFA51 -:102C5C00022000F017FA4FF4004000F01EFA1FE7B4 -:102C6C0000000108044550FE004000084A3F0008DF -:102C7C000038024000106022FFC9FE3633590700AD -:102C8C000748084A08B50849121AFDF7D7F907484A -:102C9C00074A0021121AFDF7DAF9FFF769FB00BFAA -:102CAC000000002020000020E03F00082000002051 -:102CBC00480001202DE9F04FDDE909AB0E469C469A -:102CCC0003783B490746C95C01300D07F8D42B2B20 -:102CDC0005D02D2B04D107464FF0FF3301E00746FA -:102CEC000123397830290ED17978782905D132F041 -:102CFC00100103D10237102207E00AB1082A04D1CF -:102D0C000137082201E002B90A2200200021B8464E -:102D1C0017F8015BA5F13004E4B2092C0DD9A5F12B -:102D2C004104192C02D8A5F1370405E0A5F1610482 -:102D3C00192C30D8A5F15704E4B294422BDA4FEA9F -:102D4C00E27900FB09F502FB0155A0FB0201E4B29C -:102D5C00294400194FF0000541EB0501BCF1000FAF -:102D6C00D5D0012B06D182457BEB0104CFDA50463E -:102D7C005946CCE75C1CCAD1DDE90B89444261EBB6 -:102D8C004105444575EB0909C1DADDE90B01404207 -:102D9C0061EB4101BBE70EB1C6F80080DD1700FB0B -:102DAC0005F203FB0122A0FB03011144BDE8F08FE7 -:102DBC004C1200082DE9F04F91B01F9E0C463106C5 -:102DCC0091468046DDE91A231E9D04D5CDE90223E8 -:102DDC004FF0000C1BE0002A73F100071046194657 -:102DEC0002DA504263EB4301002A73F1000ACDE989 -:102DFC00020107DBB70708D416F0040C07D04FF01C -:102E0C00200C04E04FF02D0C01E04FF02B0C16F0D1 -:102E1C004007089714BF704F704FDDE902AB07975E -:102E2C0000271C980137C117CDE90401DDE9042303 -:102E3C000AA93944099150465946CDF804C0FEF709 -:102E4C007FFA07980999835C504601F8013C594672 -:102E5C00DDE90423FEF774FA82468B465AEA0B022C -:102E6C00DDF804C001D0162FDBD116F0080239466C -:102E7C001DD01C9B082B02D0102B0ED01CE0DDE9C2 -:102E8C0002AB1DB95AEA0B0B13D004E05AEA0B0B38 -:102E9C0012D0BD4210DC7B1C0FE0DDE902AB5AEA1C -:102EAC000B0B09D008982B4648B1582208E02B464A -:102EBC0006E02A46012303E02B46002200E078229C -:102ECC001CF1000B1D98C7EB030A18BF4FF0010B48 -:102EDC00BB42A8BF1F46C01B2AEAEA7ACBEB000014 -:102EEC000AB1022700E01746C71B16F0010027EABB -:102EFC00E77701D07F4205E0F60603D5013504BF24 -:102F0C00BA4407463D46002D0BDD2068013D461CAA -:102F1C004E453CBF202608F8006020680130206038 -:102F2C00F1E727EAE7703F1ABBF1000F08D02068E1 -:102F3C00451C4D4538BF08F800C0206801302060A2 -:102F4C008AB12068451C4D453CBF302508F800501F -:102F5C002568681C02354D45206038BF08F80020F4 -:102F6C00226801322260BAF1000F0CD022680AF1FB -:102F7C00FF3A501C48453CBF302008F8020022683C -:102F8C0001322260EFE7DDE902AB5AEA0B0B10D1FC -:102F9C007BB919460DE0236801395A1C4A4505D204 -:102FAC0001F1280568462A5C08F8032023680133E0 -:102FBC0023600029EFD15FB1236801375A1C4A45C1 -:102FCC003CBF202208F80320236801332360F2E77A -:102FDC0011B0BDE8F08F00BFA63F0008B73F000856 -:102FEC001EF0040F0CBFEFF30880EFF30980FEF71F -:102FFC00BFBF7047032970B505460C4606D98E082D -:10300C003146FDF70BFCB6003544A41B14B9FDF793 -:10301C0013FC70BD00231846EA5C0133A34242EA5C -:10302C000020F9D1FDF7F4FB70BD2DE9F041073319 -:10303C00DE08079B4FF0120808FB02320731069F8F -:10304C00054602EBD1080024BC4209D0122000FB3B -:10305C00048029463246FCF7F1FF01343544F3E78E -:10306C00BDE8F0810623584301387FF4FDAF70476B -:10307C0010B504460020FDF717FF20420CBF0020BE -:10308C00012010BD10B504460020FDF70DFF20EA0D -:10309C0004010020FDF7F8FE10BD10B50446002019 -:1030AC00FDF702FF40EA04010020FDF7EDFE10BD24 -:1030BC001FB501A8FEF765F80723029301A80323A7 -:1030CC000393FDF7BDFD6846FEF761F869460020E5 -:1030DC00FDF7FAFD05B05DF804FB08B5FEF7BCFC86 -:1030EC000020FEF733FC0020FEF730FC0020FEF73A -:1030FC002DFCFEF795FCFDF73BFBFEF701FC70B5D4 -:10310C000646FEF7A9FC8020FEF720FCA82420BA76 -:10311C0090FAA0F0C0B2FEF719FC1125705DFEF715 -:10312C0015FC15F1FF35F9D20020FEF70FFC013C20 -:10313C0006F11206EBD12046FEF708FCFEF770FCF8 -:10314C0070BD38B500242546E0B2FEF713FEA04052 -:10315C0001340543042CEDB2F6D1284638BD08B530 -:10316C000D20FEF7DDFB0A20FEF7DAFB08BD10B5DB -:10317C00441E14F8010F10B1FEF7D2FBF9E710BD95 -:10318C0008B5FFF7F4FFFFF7EAFF08BD1FB53023C2 -:10319C008DF8043078238DF80530072399000F2221 -:1031AC008A400240CA40092A01D8303202E00F2A74 -:1031BC0002D85732D2B200E0202201A9CC1A13F166 -:1031CC00FF336272EAD2002308468DF80E30FFF707 -:1031DC00CEFF04B010BD70B504460D464FF48050C0 -:1031EC0001211646FDF7A8FCFEF7DCFD0520FEF7D5 -:1031FC00C7FEA920FEF7C4FEC307FAD4FEF7A8FD4C -:10320C00FEF7D0FD0320FEF7BBFEC5F30740FEF72B -:10321C00B7FEC5F30720FEF7B3FEE8B2FEF7B0FE2B -:10322C002644B44205D0A920FEF7AAFE04F8010BEF -:10323C00F7E7FEF78DFD4FF480500021FDF77CFC85 -:10324C0070BD0000000000001F0000003B000000EB -:10325C005A0000007800000097000000B500000044 -:10326C00D4000000F3000000110100003001000048 -:10327C004E0100000000000800400008008000081B -:10328C0000C000080000010800000208000004084B -:10329C0000000608923F00080008024008000000E9 -:1032AC00973F000800000240040000009A3F00080D -:1032BC000008024040000000A13F0008000002404E -:1032CC0002000000000002400100000000000800A5 -:1032DC001000180020002800300038007C00FE0090 -:1032EC00FF01C701C701C701C701C701C701C7015A -:1032FC00C701C701C701C701C701FF01FE007C0060 -:10330C0038003C003E003E003800380038003800E1 -:10331C003800380038003800380038003800FE001B -:10332C00FE00FE007C00FE00FF01C701C701C001CA -:10333C00C001E000F00078003C001E000E000F0001 -:10334C000700FF01FF01FF017C00FE00FF01C70128 -:10335C00C701C001C001F8007800F800C001C0012D -:10336C00C001C701C701FF01FE007C00E000E000C6 -:10337C00F000F000F800F800F800FC00EC00EE00A3 -:10338C00E600FF01FF01FF01E000E000E000E000CB -:10339C00FF00FF00FF000700070007007F00FF0091 -:1033AC00FF01C701C001C001C701C701C701FF016F -:1033BC00FE007C007C00FE00FF01C701C701070076 -:1033CC0007007700FF00FF01C701C701C701C70154 -:1033DC00C701FF01FE007C00FF01FF01FF01E000BF -:1033EC00E0007000700070003800380038003800C1 -:1033FC001C001C001C001C001C001C007C00FE009F -:10340C00FF01C701C701C701C701FE007C00FE0018 -:10341C00C701C701C701C701C701FF01FE007C003E -:10342C007C00FE00FF01C701C701C701C701C7012E -:10343C00FF01FE01DC01C001C001C701C701FF0192 -:10344C00FE007C0000000000000000007C00FE007C -:10345C00FF01C701C701F001FC01CE01C701C70183 -:10346C00E701FF01DF01CE0107000700070007009D -:10347C00E700F701FF01CF01C701C701C701C70171 -:10348C00C701C701CF01FF01F701E70000000000F1 -:10349C00000000007C00FE00FF01C701C70107000F -:1034AC00070007000700C701C701FF01FE007C00F1 -:1034BC00C001C001C001C001CE01DF01FF01E70165 -:1034CC00C701C701C701C701C701C701E701FF0158 -:1034DC00DF01CE0100000000000000007C00FE00B7 -:1034EC00FF01C701C701C701FF01FF010700C701A9 -:1034FC00C701FF01FE007C00E000F000F80038007E -:10350C00FE00FE00FE00380038003800380038009D -:10351C0038003800380038003800380000000683C6 -:10352C000100000000000000000000000683010004 -:10353C000000000C000000000000068301000000E9 -:10354C00000C00000000D878369B79C0E3D90C8CB5 -:10355C0067DB3C1BF8FD7EBFFDE0F7FB1FC6EFFBF6 -:10356C007E1F98CD66B3CD60369B19C66C1866036A -:10357C0098FD66B3FD60309B19C66F18660398FD05 -:10358C0066B3FD60309B19C36F186603980D66B364 -:10359C000D60369B19C360186603F8FD7EBFFDEC09 -:1035AC00F79B19C36F187E03D878369B79CCE399B7 -:1035BC00998167183C03180000000000000080018E -:1035CC0000000000180000000000000000000000D7 -:1035DC0000001800000000000000000000000000C7 -:1035EC00000C000600000000600030000018000312 -:1035FC0000000000C0001800003080010000000036 -:10360C0080010C000060C0000000000000030600F8 -:10361C0000C06000000000000006030000803100C4 -:10362C0000000000008C010000001B0000000000E6 -:10363C0000D8000000000E00000000000070000028 -:10364C0000000E00000000000070000000001B00D5 -:10365C000000000000D800000080310000000000D5 -:10366C00008C010000C06000000000000006030098 -:10367C000060C00000000000000306000030800164 -:10368C000000000080010C00001800030000000086 -:10369C00C0001800000C00060000000060003000A4 -:1036AC00000000000000000000000000000000000E -:1036BC0000000000000000000000000000000000FE -:1036CC0000000000000000000000000000000000EE -:1036DC0000000000000000000000000000000000DE -:1036EC0000000000000000000000000000000000CE -:1036FC0000000000000000000000000000000000BE -:10370C000000000080FFFF0100000000000000002E -:10371C00FEFFFF7F00000000000000E07F0000FEC5 -:10372C0007000000000000FE010000807F00000088 -:10373C000000801F00000000F80100000000F001F4 -:10374C0000000000800F000000007C000000000062 -:10375C00003E000000000F000000000000F0000020 -:10376C00008003000000000000C0010000E0000029 -:10377C0000000000000007000070000000000000C6 -:10378C0000000E00001800000000000000001800EF -:10379C00000C0000000000000000300000060000DB -:1037AC0000000000000060000003000000000000AA -:1037BC000000C0008001000000000000000080013B -:1037CC00C0000000000000000000000360000000CA -:1037DC0000000000000000063000000000000000A7 -:1037EC000000000C100000000000000000000008A9 -:1037FC000000000000600000030018000000000042 -:10380C000000006000000300180000000000000031 -:10381C0000600000030018000000000000000060C1 -:10382C000000030018000000000000000060000011 -:10383C0003001800000000E003001F60F800C3073D -:10384C0018C0070000F80FC07F60FE03F31F18F0CC -:10385C001F00000C1860C06003061B3018183000E5 -:10386C000006303080E1010C0F60180C6000000382 -:10387C00601800E3001807C01806C0008001400C57 -:10388C00006200100380180380008001C00C0066E9 -:10389C0000300380190380018001C0FCFF670030F9 -:1038AC00038019FFFF018001C0FCFF63003003801F -:1038BC0019FFFF008001C00C006000300380190369 -:1038CC0000008001C00C0060003003801903000070 -:1038DC008001400C006200100380180380008003FC -:1038EC00601800C3001806C01806C00080073030EE -:1038FC008081010C0C60180C6000800D1860C000F9 -:10390C00030618303818300080F90FC07F00FE0312 -:10391C00F01F70F01F0080E103001F00F800C007CB -:10392C0060C00700800100000000000000000000E3 -:10393C0000008001000000000000000000000000FA -:10394C008001000000000000000000000000800169 -:10395C0000000000000000000000000080010000DA -:10396C00000000000000000000004E6F20485720AF -:10397C0056657273696F6E20696E204F54500056F5 -:10398C00335231007573622077616B6575702073EB -:10399C007570706F7274656400456E746572696ED3 -:1039AC0067207374616E64627900206973206F758F -:1039BC0074736964652073797374656D20666C61CA -:1039CC0073680048415244204641554C54004465AC -:1039DC0073636C656E20000A4669726D6C656E20AF -:1039EC00000A5873756D20000A496E76616C696423 -:1039FC00206669726D7761726520646573637269A4 -:103A0C007074696F6E2100436865636B73756D6DBF -:103A1C00696E67206669726D776172652075706476 -:103A2C0061746500496E76616C6964206669726DBB -:103A3C00776172652043524320696E2053504920B0 -:103A4C00666C617368210065726173655F6F6C648D -:103A5C005F6669726D77617265004F6C6420576F99 -:103A6C00726C64206669726D776172652062617335 -:103A7C0065006661696C656420746F2065726173A2 -:103A8C006520736563746F72200077726974655F6B -:103A9C006E65775F6669726D7761726500666169E4 -:103AAC006C656420746F207772697465206164643E -:103ABC0072657373200057652772652064656164B5 -:103ACC0000436865636B73756D6D696E67200020CC -:103ADC006279746573004E657720576F726C642041 -:103AEC006669726D776172652073797374656D5F49 -:103AFC00666C6173685F62617365004F6C6420571C -:103B0C006F726C64206669726D776172652073796F -:103B1C007374656D5F666C6173685F626173650079 -:103B2C00436865636B73756D202D2077616E7465CA -:103B3C0064200020676F7420004F757220696E74CA -:103B4C0065726E616C20666C61736820636F6E7455 -:103B5C00656E7473206172652062616420286368ED -:103B6C0065636B73756D206661696C6564292120D2 -:103B7C0054686973206973207265616C6C7920627A -:103B8C0061642100FFFFFFFFFFFFFFFFFFFFFFFF4F -:103B9C000100000000000000000000800100000097 -:103BAC000000000000000080010000000000000088 -:103BBC0000000080010000000000000000000080F8 -:103BCC000100000000000000000000800100000067 -:103BDC000000000000000080FFFFFFFFFFFFFFFF61 -:103BEC00FFFFFFFF5341442057415443483A200004 -:103BFC005265736574205265676973746572200031 -:103C0C0042726F776E206F757420726573657400E5 -:103C1C005374617274696E67204C5345206F7363E3 -:103C2C00696C6C61746F72004C5345206F736369DF -:103C3C006C6C61746F7220646964206E6F74207395 -:103C4C0074617274005553422077616B6575700016 -:103C5C006C656176696E67207374616E646279005D -:103C6C00205F5F5F5F5F5F202020205F5F0D0A2F6A -:103C7C005F20205F5F2F205F5F2F202F0D0A202FEA -:103C8C00202F2020202F5F20205F5F2F0D0A2F5F19 -:103C9C002F20202020202F5F2F0D0A00426F6F74E1 -:103CAC006C6F616465722076657273696F6E3A2011 -:103CBC00004C617374206669726D77617265206265 -:103CCC006F6F742077617320737461626C653B2035 -:103CDC00636C65617220737472696B6573005374E5 -:103CEC0075636B20627574746F6E2072656769738F -:103CFC0074657220697320696E76616C69642C201E -:103D0C00636C656172696E672E00427574746F6EB8 -:103D1C002069642000697320737475636B21004201 -:103D2C007574746F6E207761732070757368656439 -:103D3C00206F6E20626F6F742E20427574746F6EDC -:103D4C0020636F756E7465723A2000426F6F742039 -:103D5C00626974733A2000486F6C6420646F776EEC -:103D6C00205550202B204241434B20666F7220354A -:103D7C0020736563732E20746F20666F7263652DDC -:103D8C00626F6F7420505246004669726D77617293 -:103D9C00652069732065726173656400426F6F748E -:103DAC00696E67206E6F726D616C6C79005761740F -:103DBC006368646F6720636175736564206120724A -:103DCC006573657400536F66747761726520666104 -:103DDC00696C75726520636175736564206120720E -:103DEC006573657400426F6F74206661696C6564FD -:103DFC002C20737472696B65203300426F6F7420D2 -:103E0C006661696C65642C20737472696B65203211 -:103E1C0000426F6F74206661696C65642C2073744A -:103E2C0072696B652031004C6F6164696E6720723A -:103E3C0065636F76657279206669726D77617265FC -:103E4C00004661696C656420746F206C6F6164203E -:103E5C007265636F76657279206669726D776172CF -:103E6C00652C20737472696B65206F6E652E2054FF -:103E7C00727920616761696E2E004661696C6564B8 -:103E8C0020746F206C6F6164207265636F7665724D -:103E9C0079206669726D776172652C207374726912 -:103EAC006B652074776F2E2054727920616761697D -:103EBC006E2E004661696C656420746F206C6F61B6 -:103ECC0064207265636F76657279206669726D77AE -:103EDC006172652C20737472696B652074687265ED -:103EEC00652E20534144205741544348004F75726E -:103EFC002070726576696F7573206669726D776173 -:103F0C00726520757064617465206661696C6564A6 -:103F1C002C2061626F7274696E67207570646174B5 -:103F2C00652E004E6577206669726D77617265202B -:103F3C00697320617661696C61626C652100426F06 -:103F4C006F74696E67206669726D77617265204067 -:103F5C00200072657475726E696E6720746F2073C1 -:103F6C0074616E64627900466F7263652D626F6F67 -:103F7C0074696E67207265636F76657279206D6FF8 -:103F8C0064652E2E2E004261636B005570005365E4 -:103F9C006C65637400446F776E00303132333435A6 -:103FAC003637383941424344454600303132333498 -:103FBC00353637383961626364656600286E756C16 -:103FCC006C29000000000001020304010203040636 -:043FDC0007080900C9 -:103FE000FFFFFFFF00A000000202000000C004016C -:103FF00000000000000000021000000007000000A8 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/boot_v2_0@1447134832.bin b/bin/boot/boot_v2_0@1447134832.bin deleted file mode 100755 index fcfc20957d..0000000000 Binary files a/bin/boot/boot_v2_0@1447134832.bin and /dev/null differ diff --git a/bin/boot/boot_v2_0@1447134832.hex b/bin/boot/boot_v2_0@1447134832.hex deleted file mode 100644 index e43d08d6d0..0000000000 --- a/bin/boot/boot_v2_0@1447134832.hex +++ /dev/null @@ -1,1060 +0,0 @@ -:020000040800F2 -:1000000048200120712C0008D12F0008D12F0008B2 -:10001000D12F0008D12F0008D12F000800000000C8 -:10002000000000000000000000000000D12F0008C8 -:10003000D12F000800000000D12F0008D12F0008A8 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0B4FD48 -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:08016000A1FD05B05DF804FBF0 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:080188008DFD05B05DF804FBDC -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F05EFCDDF820C034 -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F0FFFB089B9CE0BA4277 -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00A73F00084B45F7DBEDE70D9907910798DD -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01AE3F0008F3 -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C00BFF34F8F044A054BD16801F4E0610B43E0 -:10191C00D360BFF34F8FFEE700ED00E00400FA0543 -:10192C0010B5044608484021FFF784FC0028F9D084 -:10193C0005482146FFF77AFC03484021FFF77AFC63 -:10194C000028F9D010BD00BF00480040014608B582 -:10195C000448FFF757FC03480221FFF755FC002809 -:10196C00F9D008BD0038004000B9074803680133BE -:10197C0008D04368013305D0C069044BC31A5842E0 -:10198C005841704700207047004000086F57654E63 -:10199C00074B1A69C2F306427F2A07D11869C0F3B4 -:1019AC000C00B0F1FF0358425841704700207047BB -:1019BC000028004000F13F4000F57E00800A082816 -:1019CC000FD8084A52F820305BB1013B42F8203066 -:1019DC003BB9012101FA00F0034B1A6B22EA00001B -:1019EC0018637047200000200038024000F13F408F -:1019FC0000F57E00800A08280DD8074A52F82030DE -:101A0C00591C42F8201033B9012101FA00F0034BA4 -:101A1C001A6B1043186370472000002000380240F6 -:101A2C0010B50C4C2046FFF7E1FF072001F00CFB32 -:101A3C004FF4805100222046FFF776FB042001F082 -:101A4C0003FB2046FFF7B6FF4FF480400021FFF761 -:101A5C0067F810BD0004024010B50B4C01214FF487 -:101A6C008040FFF75DF82046FFF7C0FF20464FF49B -:101A7C0080510122FFF758FB072001F0E5FA2046C0 -:101A8C00FFF798FF10BD00BF00040240F0B55E25C3 -:101A9C006843204BB0FBF1F51A68ADF6DC3D95427E -:101AAC0034D002AC00214FF43D6220461D60FEF79D -:101ABC00CEFA1B238DE81800174810214022692309 -:101ACC0001F0A5FA08238DE81800144860231821AA -:101ADC006A2201F09CFA0DF2FA760DF28E73002256 -:101AEC00AA420DD002F11900C0B2C108012700F0C2 -:101AFC00070007FA00F05F5C013238435854EFE7F7 -:101B0C001233B342EBD1204601F0EBFA0DF6DC3D7B -:101B1C00F0BD00BF08000020A03500082F390008D8 -:101B2C002DE9F043ADF6DC3D02AC00214FF43D62F3 -:101B3C0080462046FEF78BFA2C238DE818002022D5 -:101B4C00284818215C2301F062FA1C2215460F2745 -:101B5C00974007EA0807D740234B242101FB07379E -:101B6C000DF50366002117F801C00023082B07D0E0 -:101B7C000120984010EA0C0F0CBF0020012003E05C -:101B8C007818407800F0010078B15819C0B24FEACB -:101B9C00D00E4FF0010900F0070009FA00F016F81A -:101BAC000E9040EA090006F80E000133092BDDD136 -:101BBC000231242906F11206D5D10B35742DA2F170 -:101BCC000402C4D10E238DE81800102190226D233D -:101BDC00064801F01CFA204601F083FA0DF6DC3DB4 -:101BEC00BDE8F083903300088F390008CC32000830 -:101BFC0010B5ADF6D83D02AC204600214FF43D6245 -:101C0C00FEF725FA1B238DE81800102140226923CA -:101C1C00044801F0FCF9204601F063FA0DF6D83DBA -:101C2C0010BD00BFA03500082DE9F74F434E444FBF -:101C3C0001214FF48040FEF773FF3046FFF7D6FECC -:101C4C003846404CFFF7D2FE30460D210522FFF7F7 -:101C5C0070FA052230460F21FFF76BFA3046214609 -:101C6C000125FFF70EFA2023384621462360257103 -:101C7C00A5714FF0000A01934FF4805BFFF701FA56 -:101C8C00304621464FF002084FF48049257184F804 -:101C9C0006A0C4F800B0FFF7F4F93046214684F8EA -:101CAC000480C4F80080FFF7ECF921462571A5717A -:101CBC00C4F80090244C3046FFF7E3F92046FFF7B8 -:101CCC0049F920462149FFF76FFA20462946FFF7CC -:101CDC008AFA019B384619465246FFF725FA2A46DE -:101CEC0030464946FFF720FA40462946FEF718FFD2 -:101CFC00A4F5505480232380F92323854FF4F0421C -:101D0C004FF6FF13E3623046A28329462364424612 -:101D1C00A4F820B0FFF70DFA29462046A582FEF75D -:101D2C002FFD3846FFF746FE3046FFF743FE484688 -:101D3C005146FEF7F5FE03B0BDE8F08F00040240FB -:101D4C000008024000000020003800400C00002079 -:101D5C0010B5074C2046FFF749FE20461021FFF72F -:101D6C00DFF92046FFF726FE012001F06DF910BDCA -:101D7C000000024038B50A4B0C2404FB00346068A8 -:101D8C00FFF734FE21896068FFF7C4F905466068E7 -:101D9C00FFF710FED5F1010038BF002038BD00BFA1 -:101DAC008432000810B5064C2046FFF71FFE204673 -:101DBC001021FFF7B7F92046FFF7FCFD10BD00BF5F -:101DCC0000000240F0B587B0FFF7E2FD002841D1DA -:101DDC000320FFF769F807460420FFF765F8424B2C -:101DEC000A241A68C2F30545C2F30626C2F302138D -:101DFC0002F00F0204FB0322330906F00F0604FB6A -:101E0C0003662B0905F00F0504FB03533C2505FB6A -:101E1C00036305FB0323191A44BF01F5A83101F52F -:101E2C00C07107EB91210320FFF72EF803A8FFF7F1 -:101E3C00A8F903A8FEF704FF01A8FFF7A8F901A968 -:101E4C000020FEF741FF02A8FFF7A7F9002002A926 -:101E5C00FEF7A4FF0A2442F2107501F064F910B9E0 -:101E6C00013DFAD107E0214D01F05DF908B1013DCA -:101E7C00FAD1013CEFD10020FEF7A2FD00214FF278 -:101E8C000400FFF72DF84FF43C50FFF755F8184BB2 -:101E9C0001201A6842F004021A60FEF791FD154BFE -:101EAC001B7813B1144801F05DF9144801F05AF98C -:101EBC000020FFF709F808200121FEF7D1FF0121CE -:101ECC004FF40040FFF70CF80D4801F04BF9084BAC -:101EDC001A6842F002021A600A4B1A6942F00402B4 -:101EEC001A6130BF07B0F0BD00280040A086010089 -:101EFC0000700040017AFF1F1A3700082F370008C6 -:101F0C004437000800ED00E0B0F1006F10B5044656 -:101F1C000BD20D4801F01DF9204601F029F90B48B0 -:101F2C0001F020F94FF0FF3010BD094A002352F8A0 -:101F3C00041FA14202D851688C4202D30133072BF3 -:101F4C00F5D1184610BD00BF5838000855370008A9 -:101F5C006032000808B5024801F004F9FFF7D0FC24 -:101F6C006E37000810B504460A480221FFF74CF9F9 -:101F7C000028F9D007482146FFF744F9054801210C -:101F8C00FFF742F90028F9D00248FFF738F9C0B240 -:101F9C0010BD00BF003001402DE9F04F87B001218A -:101FAC00814600AF0020FFF771FD042001F067F8B7 -:101FBC0007F10C0049460C2201F0FFF8FD683C6962 -:101FCC000C2DD7F814B013D0994801F0C2F828465C -:101FDC0001F0CEF8974801F0BCF8204601F0C8F8A3 -:101FEC00954801F0B6F8584601F0C2F8934833E032 -:101FFC00934801F0B7F84FF480500121FEF784FDAF -:10200C0009F10C09EA46FEF7FDFBA0B06E46254629 -:10201C00C846802D0CD941463046802201F0CDF8BF -:10202C0030462021FEF7FAFB803D08F18008F0E7EE -:10203C0025B1304641462A4601F0BFF829463046C4 -:10204C0000F0CAFF002180464FF48050FEF75CFD83 -:10205C00D845D54604D07B4801F084F80120E3E054 -:10206C0088B068464946202201F0A7F86846FFF779 -:10207C007BFC78607448D54601F074F87B682BB90A -:10208C00724801F06FF804F5404600E02646002E39 -:10209C003BD06F48FFF738FF05466E483044FFF7DA -:1020AC0033FF002D824630DB00282EDB431B01332F -:1020BC004FEA430B00205946FFF7E8FCFEF7CEFB36 -:1020CC00654B002603EB45033B6055451BDCF320B9 -:1020DC00FEF7DAFB3B68002133F81600FEF7F4FB41 -:1020EC00082806F1010608D05C4801F032F82846B1 -:1020FC0001F03EF801F025F805E030465946FFF7AF -:10210C00C5FC0135E1E7FEF7B7FB554801F02AF8AD -:10211C007B680BB14E4B00E0524B7B6063083B601D -:10212C000025A5423CD2661BB6F5803F28BF4FF474 -:10213C0080362EB14C4805EB0901324601F03DF8D2 -:10214C00D7F804A0FEF78AFBF320FEF79DFBAA4408 -:10215C004FF0000BB34519D043490BEB0A0011F8B3 -:10216C000B10FEF7E9FB08280DD0404800F0F1FFFA -:10217C00504600F0FDFF00F0E4FFFEF77DFB3C480D -:10218C0000F0F0FF0CE00BF1010BE3E7FEF774FB42 -:10219C003B68214603EB5500FFF778FC5D44C0E734 -:1021AC00344800F0D6FF204600F0E2FF324800F041 -:1021BC00D9FF0020FFF7D8FB20B1304800F0D2FF48 -:1021CC00234D03E02E4800F0CDFF264D4FF48050F8 -:1021DC000121A608FEF798FCFEF714FB31462846B1 -:1021EC00FEF71CFB04F0030105EB860000F0F4FE87 -:1021FC00002104464FF48050FEF786FC214800F085 -:10220C00A8FF404600F0B4FF1F4800F0A2FF204694 -:10221C0000F0AEFF00F095FF444504D01B4800F0E1 -:10222C00A1FF022000E000201C37BD46BDE8F08F66 -:10223C0079370008823700088C370008933700087C -:10224C00B2370008CF370008F2370008053800080D -:10225C0000400008FF3F0008BC3200081D38000891 -:10226C0035380008000001084400002048380008F8 -:10227C00613800086C3800087A380008813800088A -:10228C00A6380008CB380008DE380008E43800080F -:10229C0070B54FF480500121FEF74EFCFFF782FD24 -:1022AC000520164CFFF75EFE002C04F1FF341FDCFA -:1022BC00FFF74EFDFFF776FD9F20FFF753FEA92099 -:1022CC00FFF750FE0546A920FFF74CFE0646A92055 -:1022DC00FFF748FE3602044646EA0545FFF738FD8F -:1022EC004FF480500021FEF727FC45EA040070BD36 -:1022FC00A920FFF737FEC307D6D4D9E70090D00347 -:10230C0008B50446114800F024FF204600F030FFC9 -:10231C0000F017FF2046FFF703FC00F004FF0C4B06 -:10232C0020F0040018701D4600F0FDFE2B7800F024 -:10233C00FB00834201D10B2401E000F0C0FE013C04 -:10234C00F2D04FF47A7000F07FFEF8E7CF3B000834 -:10235C004400012080B5464B4FF4C06290B01A6027 -:10236C0001214FF08050FEF7DBFB0120FEF722FB32 -:10237C004048FFF73BFB4FF480200121FEF7D0FBD8 -:10238C003C480A210722FEF7D4FE072239480B21CC -:10239C00FEF7CFFE10AF4FF4806347F8383D0024B2 -:1023AC004FF001080225334839468DF80E408DF860 -:1023BC000F808DF80C508DF80D50FEF762FE4FF427 -:1023CC0000632C48394602938DF80C5004AEFEF78E -:1023DC0058FE4FF46133049327480C233146ADF873 -:1023EC001A30ADF81440ADF81640ADF81840ADF801 -:1023FC001C40FEF7DDFD41461F48FEF70BFF1D4854 -:10240C00FFF7D8FA1D4800F0A4FE1D4B586F00F0E2 -:10241C00AFFE00F096FE7920FEF7D6FB404502D1C8 -:10242C00184800F09FFE012000F014FE10B90121A5 -:10243C00FEF72AFD4120FEF7C7FB58BB124800F0FF -:10244C0091FE0120FEF7EEFA47F230544120FEF7E0 -:10245C00BBFBF8B94FF47A7000F0F6FD013CF5D1F6 -:10246C000A4800F07FFEFFF7DFFB094837E100BFA9 -:10247C00003C02400008024000480040DB3B0008E2 -:10248C0000380240EB3B0008FB3B0008133C000803 -:10249C00114550FE4FF48070FEF718FB0120FEF73B -:1024AC002DFBFEF7EFFB0020FEF78AFA01214FF41B -:1024BC008040FEF741FBBB48FFF798FA3046FEF729 -:1024CC0020FE00240125B74831468DF815408DF8C3 -:1024DC0016408DF8174004958DF81450FEF7D1FD79 -:1024EC002246B0482946FEF71FFEAE48FFF762FAB7 -:1024FC00AD4C04F1300954F8040CFFF777FA304670 -:10250C00FEF7FFFD236854F8040C4FF00008314629 -:10251C00022504938DF814808DF815508DF81780D2 -:10252C000C34FEF7AEFD54F8100CFFF743FA4C4593 -:10253C00E1D14FF480404146FEF7FEFA4FF4807033 -:10254C0000F088FD002800F0CC804FF4807000F083 -:10255C008BFDFFF737FC954800F004FE0220FEF7D8 -:10256C00A3FC9349884202D00220FEF78DFC9148CF -:10257C0000F0EFFD0220FEF797FC00F0F9FD00F0F3 -:10258C00E0FD4FF4804000F065FD90B18A4800F00A -:10259C00E9FD4FF4804000F067FD082000F064FD79 -:1025AC00102000F061FD202000F05EFD402000F0C6 -:1025BC005BFDFFF739FBFFF71BFB01214FF480504C -:1025CC00FEF7BAFA7748FFF711FA01200146FEF739 -:1025DC009BFA05210A467348FEF7ABFD71480621AC -:1025EC000522FEF7A6FD05226E480721FEF7A1FD88 -:1025FC0000240225C0236B4839468DF80C508DF809 -:10260C000D5002938DF80E408DF80F40FEF739FDFA -:10261C002023644839468DF80F5002930125FEF7AC -:10262C0030FD102339465F4802938DF80C508DF81D -:10263C000F50FEF726FD6148FEF78CFC4FF48273B9 -:10264C00ADF812304FF40073ADF81A305B48072325 -:10265C003146ADF82030ADF81040ADF81440ADF86F -:10266C001640ADF81840ADF81C40ADF81E40FEF712 -:10267C009BFD29465148FEF7B6FD4A48FFF79AF9EB -:10268C00FFF790FBAB20FFF76DFCFFF761FB6420BD -:10269C0000F0DAFC21464FF48050FEF74DFAFFF7BC -:1026AC00F7FD2846FEF700FC25460490E8B2FFF73C -:1026BC0061FB98BB70550135042DF7D1049D3DB1DC -:1026CC003F4800F046FD284600F052FD00F039FD71 -:1026DC0001202946FEF7D8FB002C3DD03948FFF7E6 -:1026EC000FFE2846FEF772F9012814D12846FEF792 -:1026FC0077F94FF40060FEF711FC18B1324800F086 -:10270C0031FDB9E0314C00F00EFD002800F00481E1 -:10271C00013CF8D1B0E000F0BDFC1CE7735D042B6C -:10272C000ADD2B4800F01EFD049800F021FD01206D -:10273C000021FEF7A9FB0FE00133DBB2042B73552C -:10274C00B9DD244800F005FD284600F011FD2248B3 -:10275C0000F008FD0124AEE7FFF79AFD1F4B034480 -:10276C00012B01D91E48BAE71E4800F0F2FC0020EC -:10277C00FEF79AFB00F0FCFC00F0E3FC802000F07C -:10278C0069FC18B1802000F06FFC73E04FF400304E -:10279C0000F060FC044638B34FF4003000F064FCE9 -:1027AC0004E100BF000002408C3200084B3C0008E2 -:1027BC0070864156873C00089C3C00080030014064 -:1027CC000A3D0008014550FE303C0008A08601007F -:1027DC00C93C0008F53C0008003D0008EA44DFFF56 -:1027EC00024550FE363D00080120FFF7C3FAD8B170 -:1027FC002046FFF7BFFAB8B1B74800F0B3FC41F27E -:10280C0088340120FFF7B6FA002800F08B800020F6 -:10281C00FFF7B0FA002800F085804FF47A7000F0D2 -:10282C0013FC013CEDD1C1E00020FFF79DF808B18D -:10283C00AA4B00E0AA4B0CCB013301D0013271D171 -:10284C00A84800F08FFCB1E0A74800F08BFC0820F2 -:10285C0000F00AFC102000F007FC202000F004FC23 -:10286C00402000F001FC0024042000F0FDFB002CB3 -:10287C0040F008819D4832E7FFF78AF8002847D0DE -:10288C00002001A99A4DFEF767FA3446002039461C -:10289C00FEF7C6FA0FCD0FC40FCD0FC495E80F008D -:1028AC0084E80F009DF8092010A901EB82039DF824 -:1028BC000BE053F8343C40F26D1101FB0E3303EB8B -:1028CC009E030EF1010E1EF0030F02D1022A88BFE7 -:1028DC0001339DF80A409DF80410013C2344182450 -:1028EC0004FB03149DF805103C2202FB04149DF814 -:1028FC000610032002FB0414FEF7D6FA011903207C -:10290C00FEF7C2FA04200021FEF7BEFA00F0C2FB6B -:10291C00784800F027FC1EE6774800F023FCFFF710 -:10292C0051FAF5E77D20FEF74FF970B94FF40070BE -:10293C0000F090FB48B9714800F014FC082000F03E -:10294C0093FB102000F090FB85E07D20FEF73CF916 -:10295C0010B16B4800F006FC4FF4007000F07AFBED -:10296C0010B1684800F0FEFB4FF4007000F07CFBE7 -:10297C00102000F06FFB48B1634800F0F3FB082017 -:10298C0000F072FB102000F06FFB12E0082000F04A -:10299C0061FB20B15D4800F0E5FB102003E05C48D2 -:1029AC0000F0E0FB082000F06AFB54E0594800F00E -:1029BC00D9FB594800F0D6FB4FF40010FFF7ECFAA6 -:1029CC0010B1022828D809E0202000F04DFB40204F -:1029DC0000F04AFB802000F052FB1DE0082000F0C4 -:1029EC004EFB102000F04BFB202000F033FB20B9F5 -:1029FC004A4800F0B7FB202009E0402000F02AFBF9 -:102A0C0000287FF421AF464800F0ACFB402000F0DA -:102A1C0036FB4FF400701AE0012425E7414800F022 -:102A2C00A1FB082000F020FB102000F01DFB202053 -:102A3C0000F01AFB402000F017FB2046FFF7ACFA21 -:102A4C00022840F0F480082000F019FB102000F060 -:102A5C0016FB00F034FB022000F0FCFA90B10420CD -:102A6C0000F0F8FA04460028D8D02F4800F07AFB82 -:102A7C00042000F0F9FA022000F0F6FA4FF40040BE -:102A8C0000F0F2FA4FF4005000F0E4FA04464FF470 -:102A9C00805000F0DFFA400040EA84044FF40060FC -:102AAC0000F0D8FAE4B22043431C03F0FF03013BCF -:102ABC004FF40060062B5CD8DFE803F03C383C0494 -:102ACC003C383C0000F0D0FA4FF4805000F0CCFAC7 -:102ADC004FF400502EE000BF423D000800400008BB -:102AEC0000000108743D00089E3E0008034550FE9E -:102AFC00343200083B3C00083D3F0008873D00088D -:102B0C00983D0008B03D0008D03D0008E63D0008A7 -:102B1C00FC3D0008523F0008123E00082C3E000805 -:102B2C00653E00080E3F0008D83E000800F09CFAF5 -:102B3C004FF4805000F0A3FA45F25550FDF718FF02 -:102B4C000420FDF71BFF40F6FF70FDF71DFF002072 -:102B5C00FDF70EFF4FF480500121FDF773FEFDF7DA -:102B6C0021FFFDF717FF0020FEF7FEFE68B9354B7D -:102B7C000CE000F079FA4FF4805000F075FA4FF445 -:102B8C00005000F071FA3048A9E5304B5D683048D0 -:102B9C001E6800F0DEFA284600F0EAFA00F0D1FADE -:102BAC0000F0CFFA00239A0002F16042013302F5E3 -:102BBC0061424FF0FF31082BC2F88010C2F880112F -:102BCC00F1D1244B00241C635C639C631C645C6427 -:102BDC00FDF70EFF20480121FDF7BAFF21461E48E4 -:102BEC00FDF7B6FFF1200121FDF7BEFF2146F120D4 -:102BFC00FDF7BAFF01200146FDF7C2FF2146012077 -:102C0C00FDF7BEFF15480121FDF7C6FF214613480D -:102C1C00FDF7C2FF12480121FDF7CAFF10482146FB -:102C2C00FDF7C6FF63B64FF0FF3EB54628470420BC -:102C3C0000F01AFA022000F017FA4FF4004000F0EE -:102C4C001EFA1FE700000108044550FE0040000872 -:102C5C00293F00080038024000106022FFC9FE36F0 -:102C6C00335907000748084A08B50849121AFDF7F6 -:102C7C00E5F90748074A0021121AFDF7E8F9FFF7B2 -:102C8C0069FB00BF0000002020000020C03F0008AE -:102C9C0020000020480001202DE9F04FDDE909ABB0 -:102CAC000E469C4603783B490746C95C01300D072C -:102CBC00F8D42B2B05D02D2B04D107464FF0FF3326 -:102CCC0001E007460123397830290ED1797878292B -:102CDC0005D132F0100103D10237102207E00AB1FE -:102CEC00082A04D10137082201E002B90A22002087 -:102CFC000021B84617F8015BA5F13004E4B2092CA9 -:102D0C000DD9A5F14104192C02D8A5F1370405E021 -:102D1C00A5F16104192C30D8A5F15704E4B2944202 -:102D2C002BDA4FEAE27900FB09F502FB0155A0FB17 -:102D3C000201E4B2294400194FF0000541EB0501F2 -:102D4C00BCF1000FD5D0012B06D182457BEB0104E1 -:102D5C00CFDA50465946CCE75C1CCAD1DDE90B8969 -:102D6C00444261EB4105444575EB0909C1DADDE9E3 -:102D7C000B01404261EB4101BBE70EB1C6F800808C -:102D8C00DD1700FB05F203FB0122A0FB030111443C -:102D9C00BDE8F08F4C1200082DE9F04F91B01F9E4A -:102DAC000C46310691468046DDE91A231E9D04D55A -:102DBC00CDE902234FF0000C1BE0002A73F1000751 -:102DCC001046194602DA504263EB4301002A73F1B4 -:102DDC00000ACDE9020107DBB70708D416F0040C92 -:102DEC0007D04FF0200C04E04FF02D0C01E04FF019 -:102DFC002B0C16F04007089714BF704F704FDDE98D -:102E0C0002AB079700271C980137C117CDE90401C5 -:102E1C00DDE904230AA93944099150465946CDF8F5 -:102E2C0004C0FEF78DFA07980999835C504601F8A7 -:102E3C00013C5946DDE90423FEF782FA82468B46B3 -:102E4C005AEA0B02DDF804C001D0162FDBD116F0C4 -:102E5C00080239461DD01C9B082B02D0102B0ED01B -:102E6C001CE0DDE902AB1DB95AEA0B0B13D004E0F0 -:102E7C005AEA0B0B12D0BD4210DC7B1C0FE0DDE9D3 -:102E8C0002AB5AEA0B0B09D008982B4648B15822D2 -:102E9C0008E02B4606E02A46012303E02B460022DD -:102EAC0000E078221CF1000B1D98C7EB030A18BF39 -:102EBC004FF0010BBB42A8BF1F46C01B2AEAEA7A9F -:102ECC00CBEB00000AB1022700E01746C71B16F037 -:102EDC00010027EAE77701D07F4205E0F60603D52B -:102EEC00013504BFBA4407463D46002D0BDD206872 -:102EFC00013D461C4E453CBF202608F8006020686A -:102F0C0001302060F1E727EAE7703F1ABBF1000FB0 -:102F1C0008D02068451C4D4538BF08F800C0206813 -:102F2C00013020608AB12068451C4D453CBF3025DE -:102F3C0008F800502568681C02354D45206038BFE4 -:102F4C0008F80020226801322260BAF1000F0CD080 -:102F5C0022680AF1FF3A501C48453CBF302008F863 -:102F6C000200226801322260EFE7DDE902AB5AEA87 -:102F7C000B0B10D17BB919460DE0236801395A1C93 -:102F8C004A4505D201F1280568462A5C08F8032059 -:102F9C002368013323600029EFD15FB12368013727 -:102FAC005A1C4A453CBF202208F8032023680133F1 -:102FBC002360F2E711B0BDE8F08F00BF853F000839 -:102FCC00963F00081EF0040F0CBFEFF30880EFF3E0 -:102FDC000980FEF7BFBF7047032970B505460C4644 -:102FEC0006D98E083146FDF719FCB6003544A41BF2 -:102FFC0014B9FDF721FC70BD00231846EA5C0133BF -:10300C00A34242EA0020F9D1FDF702FC70BD2DE984 -:10301C00F0410733DE08079B4FF0120808FB023221 -:10302C000731069F054602EBD1080024BC4209D0AB -:10303C00122000FB048029463246FCF7FFFF0134C6 -:10304C003544F3E7BDE8F0810623584301387FF49B -:10305C00FDAF704710B504460020FDF725FF204258 -:10306C000CBF0020012010BD10B504460020FDF758 -:10307C001BFF20EA04010020FDF706FF10BD10B570 -:10308C0004460020FDF710FF40EA04010020FDF784 -:10309C00FBFE10BD1FB501A8FEF773F807230293C2 -:1030AC0001A803230393FDF7CBFD6846FEF76FF8E9 -:1030BC0069460020FDF708FE05B05DF804FB08B575 -:1030CC00FEF7CAFC0020FEF741FC0020FEF73EFC98 -:1030DC000020FEF73BFCFEF7A3FCFDF749FBFEF7D7 -:1030EC000FFC70B50646FEF7B7FC8020FEF72EFCF1 -:1030FC00A82420BA90FAA0F0C0B2FEF727FC112544 -:10310C00705DFEF723FC15F1FF35F9D20020FEF7B8 -:10311C001DFC013C06F11206EBD12046FEF716FC15 -:10312C00FEF77EFC70BD38B500242546E0B2FEF7F4 -:10313C0021FEA04001340543042CEDB2F6D1284603 -:10314C0038BD08B50D20FEF7EBFB0A20FEF7E8FBB7 -:10315C0008BD10B5441E14F8010F10B1FEF7E0FBCA -:10316C00F9E710BD08B5FFF7F4FFFFF7EAFF08BD5C -:10317C001FB530238DF8043078238DF805300723E4 -:10318C0099000F228A400240CA40092A01D83032E5 -:10319C0002E00F2A02D85732D2B200E0202201A955 -:1031AC00CC1A13F1FF336272EAD2002308468DF871 -:1031BC000E30FFF7CEFF04B010BD70B504460D46BF -:1031CC004FF4805001211646FDF7B6FCFEF7EAFDE0 -:1031DC000520FEF7C7FEA920FEF7C4FEC307FAD4EC -:1031EC00FEF7B6FDFEF7DEFD0320FEF7BBFEC5F3D2 -:1031FC000740FEF7B7FEC5F30720FEF7B3FEE8B2B3 -:10320C00FEF7B0FE2644B44205D0A920FEF7AAFE74 -:10321C0004F8010BF7E7FEF79BFD4FF480500021FB -:10322C00FDF78AFC70BD0000000000001F000000CC -:10323C003B0000005A0000007800000097000000DE -:10324C00B5000000D4000000F300000011010000E4 -:10325C00300100004E010000000000080040000892 -:10326C000080000800C000080000010800000208EF -:10327C000000040800000608713F00080008024026 -:10328C0008000000763F0008000002400400000027 -:10329C00793F00080008024040000000803F000811 -:1032AC00000002400200000000000240010000008B -:1032BC000000080010001800200028003000380022 -:1032CC000000068301000000000000000000000068 -:1032DC00068301000000000C0000000000000683C3 -:1032EC0001000000000C00000000D878369B79C06B -:1032FC00E3D90C8C67DB3C1BF8FD7EBFFDE0F7FBD4 -:10330C001FC6EFFB7E1F98CD66B3CD60369B19C6EA -:10331C006C18660398FD66B3FD60309B19C66F1878 -:10332C00660398FD66B3FD60309B19C36F18660386 -:10333C00980D66B30D60369B19C360186603F8FDD3 -:10334C007EBFFDECF79B19C36F187E03D878369BB4 -:10335C0079CCE399998167183C03180000000000B0 -:10336C0000008001000000001800000000000000B8 -:10337C000000000000001800000000000000000029 -:10338C0000000000000C000600000000600030008F -:10339C000018000300000000C0001800003080017D -:1033AC000000000080010C000060C0000000000064 -:1033BC000003060000C060000000000000060300CF -:1033CC000080310000000000008C010000001B0098 -:1033DC000000000000D8000000000E0000000000FB -:1033EC000070000000000E000000000000700000E3 -:1033FC0000001B000000000000D80000008031001D -:10340C0000000000008C010000C060000000000003 -:10341C00000603000060C00000000000000306006E -:10342C00003080010000000080010C000018000337 -:10343C0000000000C0001800000C00060000000096 -:10344C0060003000000000000000000000000000E0 -:10345C000000000000000000000000000000000060 -:10346C000000000000000000000000000000000050 -:10347C000000000000000000000000000000000040 -:10348C000000000000000000000000000000000030 -:10349C000000000000000000000000000000000020 -:1034AC00000000000000000080FFFF010000000091 -:1034BC0000000000FEFFFF7F00000000000000E0A5 -:1034CC007F0000FE07000000000000FE01000080ED -:1034DC007F0000000000801F00000000F8010000C9 -:1034EC000000F00100000000800F000000007C00D4 -:1034FC0000000000003E000000000F000000000073 -:10350C0000F00000008003000000000000C001007B -:10351C0000E0000000000000000007000070000048 -:10352C000000000000000E00001800000000000069 -:10353C0000001800000C000000000000000030002B -:10354C000006000000000000000060000003000006 -:10355C00000000000000C00080010000000000001E -:10356C0000008001C000000000000000000000030B -:10357C0060000000000000000000000630000000A9 -:10358C00000000000000000C100000000000000013 -:10359C00000000080000000000600000030018009C -:1035AC000000000000000060000003001800000094 -:1035BC000000000000600000030018000000000084 -:1035CC000000006000000300180000000000000074 -:1035DC000060000003001800000000E003001F6002 -:1035EC00F800C30718C0070000F80FC07F60FE0387 -:1035FC00F31F18F01F00000C1860C06003061B308E -:10360C00181830000006303080E1010C0F60180CE7 -:10361C0060000003601800E3001807C01806C00023 -:10362C008001400C006200100380180380008001B0 -:10363C00C00C006600300380190380018001C0FCBF -:10364C00FF670030038019FFFF018001C0FCFF639E -:10365C000030038019FFFF008001C00C00600030B7 -:10366C000380190300008001C00C0060003003804F -:10367C00190300008001400C006200100380180345 -:10368C0080008003601800C3001806C01806C00034 -:10369C00800730308081010C0C60180C6000800DAC -:1036AC001860C000030618303818300080F90FC0BD -:1036BC007F00FE03F01F70F01F0080E103001F006D -:1036CC00F800C00760C00700800100000000000087 -:1036DC00000000000000800100000000000000005D -:1036EC00000000008001000000000000000000004D -:1036FC00000080010000000000000000000000003D -:10370C0080010000000000000000000000004E6F6F -:10371C002048572056657273696F6E20696E204F72 -:10372C005450007573622077616B657570207375EA -:10373C0070706F7274656400456E746572696E6743 -:10374C00207374616E64627900206973206F7574E4 -:10375C00736964652073797374656D20666C61732D -:10376C00680048415244204641554C54004465730E -:10377C00636C656E20000A4669726D6C656E200084 -:10378C000A5873756D20000A496E76616C69642065 -:10379C006669726D776172652064657363726970B6 -:1037AC0074696F6E2100436865636B73756D6D6929 -:1037BC006E67206669726D776172652075706461E1 -:1037CC00746500496E76616C6964206669726D7708 -:1037DC006172652043524320696E20535049206624 -:1037EC006C617368210065726173655F6F6C645FF7 -:1037FC006669726D77617265004F6C6420576F72E9 -:10380C006C64206669726D776172652062617365A4 -:10381C00006661696C656420746F20657261736504 -:10382C0020736563746F72200077726974655F6EC4 -:10383C0065775F6669726D77617265006661696C48 -:10384C00656420746F20777269746520616464729A -:10385C006573732000576527726520646561640089 -:10386C00436865636B73756D6D696E6720002062CC -:10387C0079746573004E657720576F726C6420669F -:10388C0069726D776172652073797374656D5F66AB -:10389C006C6173685F62617365004F6C6420576F75 -:1038AC00726C64206669726D7761726520737973CE -:1038BC0074656D5F666C6173685F6261736500430C -:1038CC006865636B73756D202D2077616E7465640C -:1038DC00200020676F7420004F757220696E74652C -:1038EC00726E616C20666C61736820636F6E7465B8 -:1038FC006E74732061726520626164202863686550 -:10390C00636B73756D206661696C65642921205445 -:10391C00686973206973207265616C6C79206261CF -:10392C00642100FFFFFFFFFFFFFFFFFFFFFFFF0111 -:10393C0000000000000000000000800100000000FA -:10394C0000000000000080010000000000000000EA -:10395C000000800100000000000000000000800159 -:10396C0000000000000000000000800100000000CA -:10397C0000000000000080FFFFFFFFFFFFFFFFFFC4 -:10398C00FFFFFF7C00FE00FF01C701C701C701C795 -:10399C0001C701C701C701C701C701C701C701C7DB -:1039AC0001FF01FE007C0038003C003E003E003868 -:1039BC00003800380038003800380038003800383B -:1039CC000038003800FE00FE00FE007C00FE00FF08 -:1039DC0001C701C701C001C001E000F00078003C44 -:1039EC00001E000E000F000700FF01FF01FF017C0D -:1039FC0000FE00FF01C701C701C001C001F800783B -:103A0C0000F800C001C001C001C701C701FF01FEE1 -:103A1C00007C00E000E000F000F000F800F800F896 -:103A2C0000FC00EC00EE00E600FF01FF01FF01E0EE -:103A3C0000E000E000E000FF00FF00FF00070007CF -:103A4C000007007F00FF00FF01C701C001C001C7D4 -:103A5C0001C701C701FF01FE007C007C00FE00FFD6 -:103A6C0001C701C701070007007700FF00FF01C76E -:103A7C0001C701C701C701C701FF01FE007C00FFA0 -:103A8C0001FF01FF01E000E00070007000700038E1 -:103A9C00003800380038001C001C001C001C001CE6 -:103AAC00001C007C00FE00FF01C701C701C701C755 -:103ABC0001FE007C00FE00C701C701C701C701C79A -:103ACC0001FF01FE007C007C00FE00FF01C701C766 -:103ADC0001C701C701C701FF01FE01DC01C001C024 -:103AEC0001C701C701FF01FE007C000000000000BF -:103AFC000000007C00FE00FF01C701C701F001FCC3 -:103B0C0001CE01C701C701E701FF01DF01CE0107AB -:103B1C0000070007000700E700F701FF01CF01C70E -:103B2C0001C701C701C701C701C701CF01FF01F7D9 -:103B3C0001E70000000000000000007C00FE00FF18 -:103B4C0001C701C7010700070007000700C701C72D -:103B5C0001FF01FE007C00C001C001C001C001CE0C -:103B6C0001DF01FF01E701C701C701C701C701C799 -:103B7C0001C701E701FF01DF01CE010000000000D9 -:103B8C000000007C00FE00FF01C701C701C701FF58 -:103B9C0001FF010700C701C701FF01FE007C00E027 -:103BAC0000F000F8003800FE00FE00FE003800387F -:103BBC000038003800380038003800380038003839 -:103BCC000038005341442057415443483A20005296 -:103BDC006573657420526567697374657220004261 -:103BEC00726F776E206F75742072657365740053F5 -:103BFC0074617274696E67204C5345206F736369EE -:103C0C006C6C61746F72004C5345206F7363696CFC -:103C1C006C61746F7220646964206E6F74207374AD -:103C2C00617274005553422077616B657570006C3E -:103C3C00656176696E67207374616E6462790020C9 -:103C4C005F5F5F5F5F5F202020205F5F0D0A2F5F4B -:103C5C0020205F5F2F205F5F2F202F0D0A202F2049 -:103C6C002F2020202F5F20205F5F2F0D0A2F5F2F2A -:103C7C0020202020202F5F2F0D0A00426F6F746CC4 -:103C8C006F616465722076657273696F6E3A20009D -:103C9C004C617374206669726D7761726520626F16 -:103CAC006F742077617320737461626C653B206361 -:103CBC006C65617220737472696B657300537475F3 -:103CCC00636B20627574746F6E20726567697374B0 -:103CDC00657220697320696E76616C69642C20634F -:103CEC006C656172696E672E00427574746F6E201C -:103CFC0069642000697320737475636B21004275CD -:103D0C0074746F6E207761732070757368656420AE -:103D1C006F6E20626F6F742E20427574746F6E20FC -:103D2C00636F756E7465723A2000426F6F74206217 -:103D3C006974733A2000486F6C6420646F776E204E -:103D4C005550202B204241434B20666F722035206A -:103D5C00736563732E20746F20666F7263652D62BA -:103D6C006F6F7420505246004669726D77617265B0 -:103D7C002069732065726173656400426F6F7469AA -:103D8C006E67206E6F726D616C6C79005761746335 -:103D9C0068646F6720636175736564206120726568 -:103DAC0073657400536F6674776172652066616920 -:103DBC006C75726520636175736564206120726532 -:103DCC0073657400426F6F74206661696C65642C56 -:103DDC0020737472696B65203300426F6F742066B8 -:103DEC0061696C65642C20737472696B6520320098 -:103DFC00426F6F74206661696C65642C20737472F9 -:103E0C00696B652031004C6F6164696E6720726567 -:103E1C00636F76657279206669726D776172650081 -:103E2C004661696C656420746F206C6F61642072EC -:103E3C0065636F76657279206669726D77617265FC -:103E4C002C20737472696B65206F6E652E20547212 -:103E5C007920616761696E2E004661696C6564202A -:103E6C00746F206C6F6164207265636F7665727914 -:103E7C00206669726D776172652C20737472696B40 -:103E8C00652074776F2E2054727920616761696E9A -:103E9C002E004661696C656420746F206C6F6164E0 -:103EAC00207265636F76657279206669726D7761D1 -:103EBC0072652C20737472696B6520746872656509 -:103ECC002E20534144205741544348004F757220D3 -:103EDC0070726576696F7573206669726D77617241 -:103EEC006520757064617465206661696C65642C0D -:103EFC002061626F7274696E67207570646174659D -:103F0C002E004E6577206669726D77617265206947 -:103F1C007320617661696C61626C652100426F6F20 -:103F2C0074696E67206669726D77617265204020D6 -:103F3C000072657475726E696E6720746F2073748D -:103F4C00616E64627900466F7263652D626F6F7487 -:103F5C00696E67207265636F76657279206D6F6428 -:103F6C00652E2E2E004261636B0055700053656CFC -:103F7C0065637400446F776E0030313233343536FC -:103F8C0037383941424344454600303132333435B9 -:103F9C003637383961626364656600286E756C6CFF -:103FAC0029000000000001020304010203040607BB -:043FBC0008090000F0 -:103FC00000A0000002020000FFFFFFFF00C004018C -:103FD00000000000000000021000000007000000C8 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/nowatchdog_boot_asterix@1753450013.bin b/bin/boot/nowatchdog_boot_asterix@1753450013.bin deleted file mode 100755 index 32fba9de40..0000000000 Binary files a/bin/boot/nowatchdog_boot_asterix@1753450013.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_asterix@1753450013.elf b/bin/boot/nowatchdog_boot_asterix@1753450013.elf deleted file mode 100755 index 06b1d42760..0000000000 Binary files a/bin/boot/nowatchdog_boot_asterix@1753450013.elf and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_asterix@1753450013.hex b/bin/boot/nowatchdog_boot_asterix@1753450013.hex deleted file mode 100644 index 73d5680b8f..0000000000 --- a/bin/boot/nowatchdog_boot_asterix@1753450013.hex +++ /dev/null @@ -1,1016 +0,0 @@ -:1000000000000420953A0000053B0000A91F0000F5 -:10001000093B00000B3B00000D3B0000000000000E -:100020000000000000000000000000000F3B000086 -:10003000113B000000000000133B0000153B0000D6 -:10004000173B0000173B0000173B0000173B000068 -:10005000173B0000173B0000173B0000173B000058 -:10006000173B0000173B0000173B0000173B000048 -:10007000173B0000173B0000173B0000173B000038 -:10008000173B0000173B0000173B0000173B000028 -:10009000173B0000173B0000173B0000173B000018 -:1000A000173B0000173B0000173B0000173B000008 -:1000B000173B0000173B000000000000000000009C -:1000C000173B0000173B0000173B0000173B0000E8 -:1000D000173B0000173B0000173B0000173B0000D8 -:1000E000173B0000173B0000173B0000000000001A -:1000F00000000000173B000000000000173B00005C -:1001000000000000000000000000000000000000EF -:1001100000000000000000000000000000000000DF -:1001200000000000000000000000000000000000CF -:1001300000000000000000000000000000000000BF -:1001400000000000000000000000000000000000AF -:10015000000000000000000000000000000000009F -:10016000000000000000000000000000000000008F -:10017000000000000000000000000000000000007F -:10018000000000000000000000000000000000006F -:10019000000000000000000000000000000000005F -:1001A000000000000000000000000000000000004F -:1001B000000000000000000000000000000000003F -:1001C000000000000000000000000000000000002F -:1001D000000000000000000000000000000000001F -:1001E000000000000000000000000000000000000F -:1001F00000000000000000000000000000000000FF -:10020000537475636B20627574746F6E20726567CA -:10021000697374657220697320696E76616C6964B4 -:100220002C20636C656172696E672E004275747470 -:100230006F6E2069642000697320737475636B218D -:1002400000427574746F6E207761732070757368E7 -:100250006564206F6E20626F6F742E204275747417 -:100260006F6E20636F756E7465723A200042616331 -:100270006B0055700053656C65637400446F776E56 -:10028000006D783235752E63004A45444543204958 -:10029000443A2000616464726573732000206973BE -:1002A000206F7574736964652073797374656D204C -:1002B000666C6173680073797374656D5F666C61F9 -:1002C00073685F65726173653A2061646472657317 -:1002D00073206E6F7420776F726420616C69676E33 -:1002E00065640073797374656D5F666C6173685FD4 -:1002F00077726974653A2061646472657373206E05 -:100300006F7420776F726420616C69676E6564003A -:10031000496E76616C6964206669726D7761726599 -:10032000206465736372697074696F6E210043683D -:1003300065636B73756D6D696E67206669726D7745 -:10034000617265207570646174650043616C6375EA -:100350006C6174656420636865636B73756D3A20C6 -:1003600000496E76616C6964206669726D776172AE -:10037000652043524320696E2053504920666C61CA -:100380007368210065726173655F6F6C645F666995 -:10039000726D776172650077726974655F6E6577FB -:1003A0005F6669726D7761726500576527726520B7 -:1003B0006465616400436865636B73756D6D696E38 -:1003C0006720002062797465730D0A0043686563D5 -:1003D0006B73756D202D2077616E7465642000202D -:1003E000676F7420004F757220696E7465726E615C -:1003F0006C20666C61736820636F6E74656E7473D5 -:1004000020617265206261642028636865636B7394 -:10041000756D206661696C65642921205468697373 -:10042000206973207265616C6C79206261642100BF -:100430004F75722070726576696F75732066697288 -:100440006D7761726520757064617465206661699D -:100450006C65642C2061626F7274696E67207570C0 -:10046000646174652E004E6577206669726D7761F0 -:10047000726520697320617661696C61626C6521C7 -:10048000004C6F6164696E67207265636F76657298 -:1004900079206669726D77617265004661696C6585 -:1004A0006420746F206C6F6164207265636F766581 -:1004B0007279206669726D776172652C207374722F -:1004C000696B65206F6E652E2054727920616761BB -:1004D000696E2E004661696C656420746F206C6FD4 -:1004E0006164207265636F76657279206669726DEA -:1004F000776172652C20737472696B652074776FF5 -:100500002E2054727920616761696E2E0046616900 -:100510006C656420746F206C6F6164207265636F1A -:1005200076657279206669726D776172652C2073C9 -:100530007472696B652074687265652E205341443E -:100540002057415443480048415244204641554CAD -:1005500054002020205F202020202020205F202009 -:100560002020202020202020205F2020202020006C -:1005700020202F5F5C2020205F5F7C207C5F205F3D -:100580005F5F205F205F285F295F205F5F00202F73 -:10059000205F205C20285F2D3C20205F2F202D5FD6 -:1005A0002920275F7C205C205C202F002F5F2F20DC -:1005B0005C5F5C2F5F5F2F5C5F5F5C5F5F5F7C5F9A -:1005C0007C207C5F2F5F5C5F5C00626F6F742062D9 -:1005D0006974004C617374206669726D776172652D -:1005E00020626F6F742077617320737461626C6531 -:1005F0003B20636C65617220737472696B65730074 -:10060000504D494320696E6974206661696C656468 -:1006100000537475636B20627574746F6E00427260 -:100620006F6B656E20666C61736800486F6C642048 -:10063000646F776E205550202B204241434B202B76 -:100640002053454C45435420666F72203520736516 -:1006500063732E20746F20666F7263652D626F6FF7 -:100660007420505246004669726D77617265206948 -:100670007320657261736564005761746368646FA9 -:1006800067206361757365642061207265736574AA -:1006900000536F667477617265206661696C75726C -:1006A000652063617573656420612072657365748C -:1006B000004661696C656420746F207374617274A4 -:1006C000206669726D776172652C20737472696B34 -:1006D000652074687265652E004661696C656420EA -:1006E000746F207374617274206669726D776172C1 -:1006F000652C20737472696B652074776F2E0046C9 -:1007000061696C656420746F207374617274206613 -:1007100069726D776172652C20737472696B6520E4 -:100720006F6E652E00466F7263652D626F6F746920 -:100730006E67207265636F76657279206D6F646590 -:100740002E2E2E00426F6F74696E67206669726D7F -:1007500077617265204020002E2E2E0D0A0D0A00B2 -:10076000426F6F7420626974733A20004669726D3B -:1007700077617265206C656E6774683A2000436823 -:1007800065636B73756D3A20004153534552543A7B -:1007900020003A00415353455254004153534552AF -:1007A000544E002A2A2A20575446200053544D33D1 -:1007B000320053544D33322070657269706865722F -:1007C000616C206C696272617279207472697070F8 -:1007D000656420616E206173736572740043524FCB -:1007E000414B204F4F4D0052657461696E65642026 -:1007F000726567697374657220435243206661694C -:100800006C65643A20657870656374656420435252 -:100810004320002C20676F742043524320002E2079 -:1008200020436C656172696E6720626F6F746269E4 -:100830007473210069746F61206275666665722049 -:10084000746F6F20736D616C6C002E2E2F2E2E2F07 -:100850002E2E2F2E2E2F74686972645F7061727451 -:10086000792F68616C5F6E6F726469632F6E726658 -:10087000782F68616C2F6E72665F6770696F2E6883 -:10088000002E2E2F2E2E2F2E2E2F2E2E2F746869F7 -:1008900072645F70617274792F68616C5F6E6F72E1 -:1008A0006469632F6E7266782F64726976657273FD -:1008B0002F7372632F6E7266785F717370692E6327 -:1008C000002E2E2F2E2E2F2E2E2F2E2E2F746869B7 -:1008D00072645F70617274792F68616C5F6E6F72A1 -:1008E0006469632F6E7266782F64726976657273BD -:1008F0002F7372632F6E7266785F7370696D2E63EB -:10090000002E2E2F2E2E2F2E2E2F2E2E2F74686976 -:1009100072645F70617274792F68616C5F6E6F7260 -:100920006469632F6E7266782F647269766572737C -:100930002F7372632F6E7266785F7477692E63000F -:100940006D020000000000501C00000003000000C9 -:1009500072020000000000501D00000003000000B3 -:1009600075020000000000501E000000030000009F -:100970007C020000000000501F0000000300000087 -:1009800001424F4F544C4F4144455200000000007B -:10099000002A2A0000000000000000000000000003 -:1009A0000338FDD870470000000000000000000080 -:1009B000000400000000000000000000000000042F -:1009C0000000000000000000000000000000000027 -:1009D0000000000000000000008000042000000073 -:1009E0000000000000000000010E100000000000E8 -:1009F000000000000000803F0000000000C0006018 -:100A000000300000FCF1073E000000C00060003034 -:100A10000000FCE0077F000000C000600030000024 -:100A20000C008663000000C00060003000000C0075 -:100A30008601F001F8C07C603E30E0030C008601C6 -:100A4000FC07FEC3FFE1FF30F80F4E108E030E0EC1 -:100A500007C783E3C1311C1C46100C0F060C03C6EC -:100A600001E380310C184310183E039881CF0066D3 -:100A7000003306BE030038780398F9CF00660033D0 -:100A8000E63F431018600398FFC000660033FE0382 -:100A9000860F0CC003980FC0006600333E000E00A6 -:100AA0000EC0039801CC0066003306300C00C6C0AF -:100AB000070C038601C380310C180C00C6C00F0E52 -:100AC0008F8783C3C1311C1C0C008661FF07FE03A6 -:100AD000FF81FF70F80FFCE0877FF301F8007C00D6 -:100AE0003E60E003FCF1071E030000000000000070 -:100AF0000000803F00000300000000000000000034 -:100B0000010E10000300000000000000008000043F -:100B100020000300000000000000000000000000B2 -:100B200000000000000000000000000400000000C1 -:100B3000000000000000000000040000000000E0D1 -:100B4000FF0F000000000000F8FF7F000000000021 -:100B500000FEFFFF030000000080FFFFFF1F0000FA -:100B60000000E01F00F87F00000000F80F00C0FF49 -:100B700000000000FE030000FE010000807F000076 -:100B800000F0030000C01F000000C0070000C00705 -:100B9000000000800F0000E003000000001F0000C4 -:100BA000E001000000003E0000F000000000007CBA -:100BB0000000F00000000000780000780000000055 -:100BC00000F00000780000000000F000003C300061 -:100BD000000000E001003C7000000000E001001E89 -:100BE0007000700000C003001EF000780000C00319 -:100BF000001EE0007C00008007000FE0013E0000C6 -:100C00008007000FC0011F00008007000FC0830F86 -:100C100000008007000FC0C307000080078007E0C6 -:100C2000C303000080078007F0C103000080078035 -:100C300007F880030000800780077C8007000080A1 -:100C400007C0033E000700008007C0031E000F001E -:100C5000008007E0030E000E00008007F001000096 -:100C60000E00008007F80000000C000080077800EC -:100C7000C07100000080077C00E079001800800748 -:100C80003C00F0FF003C0080073C00F8FF003C0007 -:100C900080073E00F8FF011E0080071E0038CF01CC -:100CA0001E0080071E0000C7010F00C0031F0000C8 -:100CB00000000F00C0030F000000800700E0010FDC -:100CC000000000800700E0010F000000C00300F0FA -:100CD000000F000000C00300F0000F000000C00380 -:100CE0000078000F600000C0030078000FF80000DB -:100CF000C0033C3C000FFE0100C0033F3C009FFFCF -:100D00000300C0C33F1E00FFFF0700C0E30F1E002B -:100D1000FE870F00C0FF031E00F8031F00C0FF0086 -:100D20001E0060003E00807F001E000000FC0000EE -:100D30003E001E000000F80F0018001E000000F02A -:100D40007F0000001E000000E0FF0700001E000002 -:100D50000080FF3F00001E00000000F8FF03003C81 -:100D600000000000C0FF0F003C0000000000FC3F3E -:100D700000780000000000E0FF00F8000000000024 -:100D800000FE03F0010000000000F01FE00300007F -:100D9000000000C07FC007000000000000FFFF0F40 -:100DA000000000000000FCFF1F000000000000F039 -:100DB000FF1F000000000000C0FF0F7C00FE00FFCE -:100DC00001C701C701C701C701C701C701C701C7E3 -:100DD00001C701C701C701C701FF01FE007C003840 -:100DE000003C003E003E0038003800380038003833 -:100DF00000380038003800380038003800FE00FEA7 -:100E000000FE007C00FE00FF01C701C701C001C059 -:100E100001E000F00078003C001E000E000F00070B -:100E200000FF01FF01FF017C00FE00FF01C701C7B9 -:100E300001C001C001F8007800F800C001C001C085 -:100E400001C701C701FF01FE007C00E000E000F0E7 -:100E500000F000F800F800F800FC00EC00EE00E6FE -:100E600000FF01FF01FF01E000E000E000E000FF03 -:100E700000FF00FF000700070007007F00FF00FFE2 -:100E800001C701C001C001C701C701C701FF01FEC1 -:100E9000007C007C00FE00FF01C701C701070007BE -:100EA000007700FF00FF01C701C701C701C701C7E5 -:100EB00001FF01FE007C00FF01FF01FF01E000E0F7 -:100EC0000070007000700038003800380038001CD6 -:100ED000001C001C001C001C001C007C00FE00FF0D -:100EE00001C701C701C701C701FE007C00FE00C7A2 -:100EF00001C701C701C701C701FF01FE007C007CDB -:100F000000FE00FF01C701C701C701C701C701FFFC -:100F100001FE01DC01C001C001C701C701FF01FEE4 -:100F2000007C0000000000000000007C00FE00FFCC -:100F300001C701C701F001FC01CE01C701C701E7EC -:100F400001FF01DF01CE010700070007000700E7EE -:100F500000F701FF01CF01C701C701C701C701C7E2 -:100F600001C701CF01FF01F701E700000000000009 -:100F70000000007C00FE00FF01C701C70107000759 -:100F80000007000700C701C701FF01FE007C00C089 -:100F900001C001C001C001CE01DF01FF01E701C7AF -:100FA00001C701C701C701C701C701E701FF01DF91 -:100FB00001CE0100000000000000007C00FE00FFE8 -:100FC00001C701C701C701FF01FF010700C701C732 -:100FD00001FF01FE007C00E000F000F8003800FE98 -:100FE00000FE00FE003800380038003800380038B5 -:100FF0000038003800380038003800FFFFFFFFFFDE -:10100000FFFFFFFFFFFFFF010000000000000000E6 -:1010100000008001000000000000000000008001CE -:10102000000000000000000000008001000000003F -:10103000000000000000800100000000000000002F -:10104000000080010000000000000000000080FFA0 -:10105000FFFFFFFFFFFFFFFFFFFFFF00000000009B -:101060000338FDD8704700000E0000000F0000009C -:101070000000980107000000004000400000000050 -:101080000701010E01000B06000B0101040201041F -:101090000501040701040C00040D00040E000415F2 -:1010A00000020200030501050A01030C08030D04F8 -:1010B000030810030900030A2A03010103000103C6 -:1010C00010BB0311010312A40313020314540315EC -:1010D000010316540317010123440123900123FA4D -:1010E0000123CE0346040350010304010805000850 -:1010F0000C08080801080001080600080D08080986 -:1011000001080201060000060100060200060300B5 -:1011100006040000000000006410B71DC8206E3BEC -:10112000AC30D9269041DC76F4516B6B5861B24DEE -:101130003C7105502083B8ED44930FF0E8A3D6D658 -:101140008CB361CBB0C2649BD4D2D38678E20AA0C0 -:101150001CF2BDBD00000000000000000000000007 -:101160000338FDD8704700000000000000000000B8 -:101170000338FDD870470000FF000000FF000000AA -:10118000FF000000FF000000FF000000FF00000063 -:081190000338FDD87047000090 -:081198009400000001000000BA -:1011A0000348044B834202D0034B03B118477047F6 -:1011B0002C0100202C010020000000000548064BF7 -:1011C0001B1AD90F01EBA301491002D0034B03B145 -:1011D000184770472C0100202C010020000000005F -:1011E00010B5064C237843B9FFF7DAFF044B13B16F -:1011F0000448AFF300800123237010BD2C010020B0 -:1012000000000000B83D000008B5054B1BB10549C2 -:101210000548AFF30080BDE80840FFF7CFBF00BF2F -:101220000000000030010020B83D0000A3F5803A26 -:10123000704700BF174B002B08BF134B9D46FFF7AD -:10124000F5FF00218B460F461348144A121A02F08C -:1012500069FD0E4B002B00D098470D4B002B00D0A2 -:1012600098470020002104000D000D48002802D0FE -:101270000C48AFF3008002F05DFD2000290000F073 -:10128000E1FE02F03DFD00BF00000800000000008C -:1012900000000000000004202C010020F0020120CA -:1012A0000000000000000000F7B5012001F05AF92D -:1012B0000024019001AD2746E0B200F049F806464F -:1012C000A0B928700134042C05F10105F4D1019C6A -:1012D0003CB1174800F0F6F8204600F012F900F093 -:1012E00087F82146012001F02FF90DE02B78042B1F -:1012F0000DD9104800F0FEF8019800F002F9002125 -:10130000012001F021F90027384603B0F0BD013378 -:10131000DBB2052B2B70D5D1074800F0D3F820465F -:1013200000F0EFF8054800F0E5F83746CAE700BFDF -:1013300041020000000200002C0200003702000001 -:1013400008B500F049FB80F00100C0B208BD000004 -:10135000064B03EB001043688268D3F81035012078 -:1013600090401840B0FA80F04009704740090000F2 -:10137000002110B50C46C8B2FFF7EAFF88400131E2 -:1013800004430429E4B2F6D1204610BD10B5074B42 -:101390000422D3E90141187B01F5E0718000013A94 -:1013A00044F8210003F11003F3D110BD40090000FF -:1013B000074B0422C3F80025C3F81C05D3F81C21F1 -:1013C000012AFBD10022C3F81C21C3F80025704775 -:1013D00000200040054B4FF08052C3F82425012225 -:1013E0009A601B22C3F80C25704700BF0020004004 -:1013F0002DE9F843304E346804F12E0323F007033F -:1014000000AFADEB030D50238DF8003021238DF894 -:10141000013000238DF8023003238DF8033004F1EE -:1014200021035BBAADF80430244B04F127086D4664 -:101430000DF1060203F11C0153F8040B42F8040BF2 -:101440008B42F9D11B78137004F1230922461C4901 -:1014500005F1230002F092FC4A462946002001F0E3 -:10146000B3F804F59273FE22B3FBF2F3434407335F -:1014700023F00703ADEB030D45F80900424629466A -:10148000684601F075F801465520FFF791FF6C465C -:1014900069448C4207D15520FFF78AFF002333604F -:1014A000BD46BDE8F88314F8010B552808BF00209D -:1014B000FFF77EFFEDE700BF480100208009000034 -:1014C0004C01002038B50A4D441E14F8012F12B10A -:1014D0002B68FF2B00D938BD0A2A02D1FFF788FFFD -:1014E000F3E70D2A1FBF591C5B1929601A71ECE73D -:1014F0004801002008B5FFF7E5FFBDE80840FFF709 -:1015000077BF1FB50C2201A901F08CF801A8FFF7E5 -:10151000D9FF05B05DF804FB7FB50DF1070300228C -:101520008DF80700029302A9012304480592CDE932 -:10153000033201F077FE07B05DF804FB00010020E4 -:10154000F8B50733122706460698079C0731DD08D1 -:1015500007FB02F303EBD1031044234407FB0044D1 -:1015600004EBD104314618462A4602F007FC034634 -:101570001233A3422E44F5D1F8BD0000044B0822DB -:10158000C3F80825034B4FF4E07043F001031847FC -:1015900000030050A009000010B5084C4FF4E070A3 -:1015A00044F00104A047064B0822C3F80C254FF471 -:1015B00080702346BDE81040184700BFA009000016 -:1015C000000300502DE9F0410646FFF7D7FF8020C9 -:1015D000FFF7A2FF0025012420BA90FAA0F0C0B2C4 -:1015E000FFF79AFF06EB0508002718F8010B00BA71 -:1015F00090FAA0F00137C0B2FFF78EFF122FF4D19E -:1016000000200134FFF788FFA92C05F11205E3D172 -:101610000020FFF781FFBDE8F041FFF7BDBF0000EC -:1016200010B5ADF6D83D02AC4FF43D620021204626 -:1016300002F078FB1D2300934522702310210548FA -:101640000194FFF77DFF2046FFF7BCFF0DF6D83D64 -:1016500010BD00BFA60900002DE9F04FADF6E43D36 -:1016600004AF03904FF43D620021384602F05AFB6C -:101670004723264800930197452318222321FFF78B -:101680005FFF1C2005464FF024084FF0010C039A21 -:101690000F23834013401E4AC34008FB03230DF66B -:1016A0003806002403F1010913F814B00022082AB7 -:1016B00006D00CFA02F111EA0B0F05D10132F6E760 -:1016C00019F81410C9070FD55119C1F3C40E01F050 -:1016D000070116F80EA00CFA01F1013241EA0A01E5 -:1016E000092A06F80E10E2D10134122C06F1120676 -:1016F000DAD10B35742DA0F10400C8D13846FFF7BC -:1017000061FF0DF6E43DBDE8F08F00BF3C0B00002B -:10171000BB0D000008B5FFF731FF0020FFF7FCFE0E -:101720000020FFF7F9FE0020FFF7F6FEBDE80840B5 -:10173000FFF732BF5E23F0B55843214BB0FBF1F405 -:101740009A68A242ADF6DC3D37D002AD4FF43D625F -:10175000002128469C6002F0E5FA1D2319480093F9 -:101760004522702310210195FFF7EAFE082300931C -:1017700018211548019560236A22FFF7E1FE0DF25A -:101780008E7700214FF0010C781800220DE002F155 -:101790001903C3F3C40603F0070310F806E00CFABC -:1017A00003F343EA0E0383550132A242EFD1123113 -:1017B0006C29E9D12846FFF705FF0DF6DC3DF0BDA9 -:1017C00000010020A6090000FB0F000038B51B4DEA -:1017D0001B481C4B2B621C4A48F20C0303802F232E -:1017E000C2F8603501210723C2F8001540F2114408 -:1017F000C2F80C350023C2F80435C2F80845C2F817 -:101800001435C2F81035C2F82005C2F82415284650 -:10181000C2F82835C2F82C3591601A4605F10C0142 -:1018200001F068FC094B0322C3F80C274FF0A043DA -:10183000C3F810271022C3F8082538BD0001002086 -:101840004C02002040420F0000C001400003005045 -:1018500008B5044801F08AFC034B0022C3F80025B8 -:1018600008BD00BF0001002000C0014070B50C465B -:10187000ADF5806D00254618B4F5806FA6EB040128 -:101880000BD82246684600F0C9F822466946284629 -:1018900000F09AFE0DF5806D70BD4FF480626846D1 -:1018A00000F0BCF828464FF48062694600F08CFED8 -:1018B000A4F580640546DFE707B540F20113013265 -:1018C000ADF8023000238DF800008DF80120684645 -:1018D0000A461946ADF8043001F04CF9044B984221 -:1018E00003D00448162100F0D3FD03B05DF804FBDB -:1018F0000000AD0B8102000007B5013240F2011378 -:101900008DF800008DF8012068460022ADF8023005 -:10191000ADF8042001F02EF9044B984203D004489E -:101920001F2100F0B5FD03B05DF804FB0000AD0B16 -:101930008102000010B513238CB00024CDE90143CF -:1019400011221423CDE9032315211623CDE9051314 -:101950001E4BDFF880C01720CDE907034FF4FA7360 -:10196000CDE909C322460723214601A80B9301F0C4 -:10197000D1F8224621466620FFF7BEFF22462146C7 -:101980009920FFF7B9FF124B4FF40C6043F00103AD -:10199000984722462146B720FFF7AEFF694601224D -:1019A0000520FFF789FF01220DEB02013520FFF72B -:1019B00083FF9DF80130022243F0020369460120B3 -:1019C0008DF80130FFF798FF0CB010BD0402010044 -:1019D000601000000500000313B5032201A99F2039 -:1019E000FFF76AFF9DF805409DF80430094824027E -:1019F00044EA03449DF806301C43FFF763FD20468C -:101A0000FFF77FFDFFF7F4FC0348231A58425841C3 -:101A100002B010BD890200001960C8002DE9F34131 -:101A2000454205F00305954288BFD5B2541B04F02A -:101A3000030806460F4624F003049DB10A4604211C -:101A40000DEB010001F0FEF8184B984203D05D2128 -:101A5000174800F01DFD30462A4601A902F08EF914 -:101A60002F442E445CB13A462146304601F0EAF854 -:101A70000E4B984201D06621EAE727442644B8F18C -:101A8000000F0FD004213A460DEB010001F0DAF807 -:101A9000064B984201D06E21DAE7424601A9304652 -:101AA00002F06CF902B0BDE8F08100BF0000AD0BA0 -:101AB000810200002DE9F0411F4900231A46ACB015 -:101AC00001F1100001F09EFE1C4B984206462ED1FB -:101AD0001B4801F0EBFE1B49932207A802F04EF9C8 -:101AE00007AC00274FF4D648002503202178627800 -:101AF000A3788DF80410CDE9030501A8CDE905050B -:101B00008DF805200E48ADF808802A4602A98DF808 -:101B1000063001F017FFB04209D10137312F04F12F -:101B20000304E1D1064801F0E1FE284601E04FF050 -:101B3000FF302CB0BDE8F081681000000000AD0B54 -:101B40007810000080100000B0F5801F10B504462A -:101B50000BD90F48FFF7B6FC2046FFF7D2FC0D4823 -:101B6000FFF7C8FC4FF0FF3010BD00231A46B4EB5E -:101B7000023F03F101034FEA023105D301F5805121 -:101B80008C4201D21046EFE7FF2BEFD11846EBE76E -:101B9000940200009D0200002DE9F047054616461C -:101BA0009846894641B310F0030705D01C48FFF75B -:101BB000A1FC0020BDE8F087FFF7C6FF0446681EC1 -:101BC0004844FFF7C1FF611C0546F2D0421CF0D02B -:101BD000A0EB040909F101091EB142464946384605 -:101BE000B047104F0223C7F80435C4F1010AAC42D4 -:101BF00004DD0023C7F804350120DBE72303C7F821 -:101C00000835D7F80034DB07FBD500F059F826B1CA -:101C1000424649460AEB0400B0470134E7E700BFFB -:101C2000B602000000E001402DE9F74F10F0030478 -:101C300006460F46984606D01F48FFF75BFC00207B -:101C400003B0BDE8F08FDFF874B0012302F0030A9F -:101C500022F00309CBF80435D5094C4518D3BAF165 -:101C6000000F0FD04FF0FF330193DBF80034DB0798 -:101C7000FBD5524607EB090101A802F07FF8019B52 -:101C800046F809300D4B0022C3F804250120D7E7A0 -:101C9000DBF80034D907FBD53B59A35100F010F80D -:101CA000B8F1000F05D0620603D10C9A2946E0096D -:101CB000C0470434D1E700BFE302000000E0014068 -:101CC0000B4AD2F8003430B58BB10A480023012109 -:101CD000D2F8084501FA03F5254218BF03F5C07490 -:101CE00003F1010318BF42F82400082BF0D130BDE6 -:101CF000000001403546526E4FF080434FF0FF32F6 -:101D0000D3F80004C3F80024C0F340007047490032 -:101D1000FFF710BD2DE9F047012186B005460020F0 -:101D2000FFF708FD042000F0F6FA03A8294600F0AA -:101D30004DFB03A800F06EFB30B93C48FFF7DAFB1F -:101D4000012006B0BDE8F08739480C35FFF7D2FB1B -:101D500004992846FFF78AFD04463648FFF7B2FB90 -:101D60002046FFF7CEFBFFF743FB059B9C4201D0CB -:101D70003148E3E7049CDFF8E0803048FFF7BAFB26 -:101D800021462F4A00234046FFF706FF2D48049FB7 -:101D9000FFF7B0FB0024A1464FEA570AA74217D924 -:101DA0003E1BB6F5803F28BF4FF4803626483246AA -:101DB0002919FFF733FE2449CDF80090002332465D -:101DC00008EB0400FFF730FF38BB2048FFF792FB19 -:101DD0001F48FFF777FB0498FFF793FB1D48FFF7B9 -:101DE00071FB049A1C49002000F0EEFB04461B48DE -:101DF000FFF768FB0598FFF784FB1948FFF762FBC4 -:101E00002046FFF77EFBFFF7F3FA059B9C420BD0C1 -:101E10001448FFF76FFB022093E7344439460AEB7E -:101E20005400FFF787FCB9E700208AE710030000A1 -:101E30002E0300004B030000610300008403000038 -:101E40000F1D0000970300004E020020AA030000AF -:101E5000B5030000C303000000800000CC030000B5 -:101E6000DF030000E503000008B5022000F06BFA74 -:101E7000D8B3042000F067FA70B11C48FFF73AFBB2 -:101E8000042000F054FA022000F051FABDE80840A6 -:101E90004FF4004000F04BBA1548FFF72BFB082029 -:101EA00000F045FA102000F042FA202000F03FFA3E -:101EB000402000F03CFA4FF48010FFF72BFF02287F -:101EC00007D1082000F027FA102000F024FA00F0D3 -:101ED00007FB042000F02BFA022000F028FABDE8EE -:101EE00008404FF4004000F016BA08BD300400006E -:101EF0006604000010B52448FFF7FCFA0020FFF745 -:101F000009FF0138C0B201280ED9202000F00FFAD5 -:101F1000402000F00CFA802000F0FDF9012404209C -:101F200000F005FA204610BD082000F0F4F910205A -:101F300000F0F1F9202000F006FA58B91348FFF735 -:101F4000D9FA202000F0E7F94FF4007000F0E3F92F -:101F500000F0C6FA402000F0F6F920B90C48FFF76F -:101F6000C9FA4020EEE70B48FFF7C4FA082000F05A -:101F7000DEF9102000F0DBF9202000F0D8F9402035 -:101F800000F0D5F90024CAE7810400009B0400009A -:101F9000D40400000D05000008B50248FFF7AAFAB6 -:101FA00000F012FA470500001EF0040F0CBFEFF31B -:101FB0000880EFF30980FFF7EFBF704708B50120F5 -:101FC000FFF7C6F918B9002000F0010008BD002095 -:101FD000FFF7BEF90028F6D00220FFF7B9F9002874 -:101FE000F1D00320FFF7B4F980F00100C0B2EBE7B5 -:101FF0001FB504461148FFF77DFA0C2201A92046BF -:1020000000F010FB01A8FFF775FA2046FFF724FB4C -:10201000FFF7AEF90A4C20F004002070FFF7A8F992 -:10202000237800F0FB00834201D000F059FAFFF75B -:1020300047FE0A2000F0E8FAF0E700BF3D05000087 -:102040004E02012030B585B0FFF73AFEFFF7C2F926 -:102050009648FFF74FFA9648FFF74CFA9548FFF776 -:1020600049FA9548FFF746FA9448FFF743FA8F4834 -:10207000FFF740FA00F071F99148FFF73BFA00F0E2 -:1020800095F90023CDE90133039300F08BF90C227D -:1020900001A900F0C7FA01A8FFF72CFA8348FFF75F -:1020A00029FA8248FFF726FA4FF4804000F04BF9F6 -:1020B00090B18448FFF71EFA4FF4804000F037F9E2 -:1020C000082000F034F9102000F031F9202000F051 -:1020D0002EF9402000F02BF9FFF7ECFC28B17A48EC -:1020E000FFF708FA7948FFF783FFFFF723FCFFF7B4 -:1020F0004DF9FFF76BFBFFF793FAFFF7D5F820B127 -:102100007348FFF7F7F97348EDE7FFF719F920B1C6 -:102110007148FFF7EFF97148E5E700F033F98020E7 -:1021200000F011F920B1802000F001F96C48DAE7E5 -:102130004FF4003000F007F938B14FF4003000F0F0 -:10214000F6F86848FFF7D6F94AE0FFF737FF78B1AD -:102150006548FFF7CFF941F28834FFF72FFF70B1E0 -:10216000FFF7AEFD012000F04FFA013CF5D1E8E7A2 -:102170005E4B1A685B68013312D0013210D0FFF752 -:10218000BBFD88B94FF4007000F0DDF878B9082085 -:1021900000F0CDF8102000F0CAF8FFF765FE23E04C -:1021A0005348FFF7A7F9CCE75248FFF7A3F94FF4DC -:1021B000007000F0C8F810B14F48FFF79BF94FF4DA -:1021C000007000F0B4F8102000F0BDF878B34B4870 -:1021D000FFF790F9082000F0AAF8102000F0A7F807 -:1021E000FFF788FE0028A1D04FF4005000F0ABF8B4 -:1021F00005464FF4805000F0A6F844004FF400600C -:1022000000F0A1F844EA850464B2204340B20728F4 -:102210001DD14FF4006000F08AF84FF4805000F0B8 -:1022200086F84FF4005000F082F835485BE708204C -:1022300000F089F830B13348FFF75CF9102000F066 -:102240006AF8AAE73048FFF755F90820F7E70328AE -:1022500021D020F0040001284FF4006016D000F0D7 -:102260005AF8FFF7F5FA214B28485C681D68FFF71C -:1022700029F92046FFF745F92548FFF723F9FFF72D -:102280001FFD63B64FF0FF3EAD46204700F04FF80C -:102290004FF48050E3E74FF4006000F048F84FF44B -:1022A000805000F044F84FF40050D8E75F0700007A -:1022B00052050000700500008E050000AC0500000E -:1022C000CA050000D305000000060000054550FEC9 -:1022D00011060000014550FE1E060000024550FE9A -:1022E000034550FE250700002B060000008000007B -:1022F000660600007906000091060000B1060000A5 -:10230000044550FED9060000FF0600004407000007 -:1023100058070000704710B50446002000F022F96D -:1023200040EA0401BDE81040002000F00DB910B5EE -:102330000446002000F016F920EA0401BDE8104030 -:10234000002000F001B910B50446002000F00AF9A1 -:102350002040003818BF012010BD4FF0804208B562 -:10236000D2F8043943F48023C2F8043900F0BEF8EF -:102370000120FFF7E8FF20B9BDE80840012100F087 -:10238000E3B808BD08B50648FFF79CF8002000F048 -:10239000E9F8FFF7B6F8BDE80840FFF729B800BF35 -:1023A00060070000022000F0DDB8000008B5FFF76C -:1023B000F9FF0449884204D0BDE80840022000F03B -:1023C000C3B808BD1D86836800BEFDE71FB50C229B -:1023D000044601A8FFF722FB0C48FFF773F80298A8 -:1023E000FFF78FF8FFF704F80948FFF76BF8039839 -:1023F000FFF787F8FEF7FCFF01AB03CB20601868FE -:10240000A0602046616004B010BD00BF6C070000F2 -:102410007E0700000068A0F10C0358425841704745 -:1024200080B50646174610480D461C46FFF74AF889 -:102430003846FFF747F80D48FFF744F83046FFF7F6 -:1024400041F80B48FFF73EF82846FFF75AF82CB141 -:102450000848FFF737F82046FFF734F80648FFF73B -:1024600049F8FFF7B1FF00BF890700006D050000C4 -:10247000920700006E0500005F0700001FB506AC64 -:102480001A4654F8043B00940394FFF7C9FF000078 -:1024900007B5002300937246014BFFF7EFFF00BF23 -:1024A0009B07000007B5034B00937246024BFFF7F2 -:1024B000E5FF00BFB2070000AC070000BFF34F8F7D -:1024C0000549064BCA6802F4E0621343CB60BFF3D0 -:1024D0004F8F00BFFDE700BF00ED00E00400FA05EC -:1024E00008B5FFF717F9FFF7E9FF000038B5124C00 -:1024F0007C222146002000F067F8E36F8342054606 -:1025000018D00E48FEF7DEFF2846FEF7FAFF0C480B -:10251000FEF7D8FFE06FFEF7F4FF0A48FEF7D2FFA0 -:10252000FEF766FF20464FF48072BDE83840002178 -:1025300001F0F8BB38BD00BF00000020E707000035 -:10254000130800001E08000010B5054C7C2244F85A -:1025500020102146002000F037F8E06710BD00BFD2 -:1025600000000020014B53F82000704700000020BD -:102570002DE9F0410546002401202746034602F1DB -:10258000FF38944205EB070C03D18CF80030BDE80E -:10259000F08111F804E0461CBEF1000F05D1EB55A7 -:1025A0000746012301343046EBE70133DBB2FF2B52 -:1025B00005F800E0F6D1A04504D037468CF800308D -:1025C000861CEEE73046E0E730B5C9B10D4DC0439B -:1025D0000A44914201D1C04330BD11F8013B83EA66 -:1025E000000404F00F0455F8244084EA101080EA37 -:1025F000131303F00F0355F8233083EA1010E8E7B4 -:102600000846E9E71411000038B5054D044645F0C9 -:10261000010504B938BD4FF47A40A847013CF8E7FA -:10262000601100000A2A30B403DC30BC1048FEF709 -:1026300061BF0C46302304F8023B78234B701C2208 -:102640000F2505FA02F30340D340092B0AD8303393 -:10265000DBB2043A04F8013B131DF2D1002330BC75 -:102660008B7270470F2B01D85733F1E72023F0E727 -:102670003408000070B546090D4600F01F0419D05B -:10268000012E04D14FF6FF73E340DB0704D4134857 -:102690004FF46C71FFF706FF7EB1012E1CD00F487E -:1026A00040F2B931FFF7FEFE04F5E074002353F861 -:1026B0002430FFDE4FF0FF33E6E74FF0A04202EB9D -:1026C0008402074BD2F800170B4043EA052343F07E -:1026D0000203C2F8003770BD024AF0E74A08000062 -:1026E000F0F8FCFF000300500E4B0238D3F83C26F4 -:1026F000D3F8383610B5072813D8DFE800F0110FEB -:102700000D0B0A080604100EC871100C8871100A0F -:1027100048710A711A0ECA701A0C8A701A0A4A7025 -:102720000B7010BD00900240023807281FD8DFE868 -:1027300000F02927251123211F04CB791B068A7954 -:1027400043EA02434A7943EA02230A7913430E4AD1 -:10275000C2F83C36CB781B068A7843EA02434A78B3 -:1027600043EA02230A781343074AC2F8383670470F -:102770000023E4E70023E5E70023E6E70023EBE797 -:102780000023ECE70023EDE7009002400268154BC0 -:10279000FF2A08BF4FF0FF32C3F824254268FF2A02 -:1027A00008BF4FF0FF32C3F828258268FF2A08BF10 -:1027B0004FF0FF32C3F83025C268FF2A08BF4FF040 -:1027C000FF32C3F834250269FF2A08BF4FF0FF32F9 -:1027D000C3F838254269FF2A08BF4FF0FF32C3F81B -:1027E0003C25704700900240F8B5114D6C6BB4B1B8 -:1027F0004FF47A735C430A23B4FBF3F40D4E0E4F8F -:1028000046F00106D7F8003163B995F839304BB975 -:102810004FF42070B047013CF4D10848F8BD4CF2A9 -:102820005034EBE795F83930002BF6D10448F5E742 -:102830005002012070110000009002400700AD0B13 -:102840000000AD0B0A4B10B500220124C3F800456F -:10285000C3F800211C6038B1FFF7C6FF054B984252 -:1028600004D1054B83F83840024810BD0348FCE70B -:10287000009002400000AD0B500201200700AD0B9C -:102880002DE9F8433D4C994694F830300646884689 -:1028900017461BB93A487F21FFF704FE2EB9384886 -:1028A0008021FFF7FFFD374841E006F06043B3F1B8 -:1028B000005FF8D1B307F6D194F83030012B05D082 -:1028C0004B4558D194F83830002B54D02E4AB9F1EA -:1028D000020F4FF000030BBFC2F81075C2F8047569 -:1028E000C2F81465C2F808650CBFC2F81885C2F8B2 -:1028F0000C8584F8393023680CBF08250425C3B93A -:1029000094F838306BB1204B05F1804505F524353E -:102910000022C3F8002101232B60BDE8F843FFF734 -:1029200063BF0120FFF78EFF184B9842EBD1BDE843 -:10293000F883A3682BB1E660C4F81480E7611448FB -:10294000F5E7114AA660C4F81080A76184F83090BA -:10295000C2F800310123C2F8043394F8382002F0A1 -:10296000FF0012B9FFF76EFFE9E705F1804505F5B5 -:1029700024352B60E3E70748D9E700BF5002012068 -:10298000810800000A00AD0B009002400700AD0B6B -:102990000000AD0B0B00AD0B30B5194D0FCD8DB058 -:1029A00006AC0FC495E8030084E803001549D1F88C -:1029B0002435DBB20093D1F82835DBB20193D1F88E -:1029C0003035DBB20293D1F83435DBB20393D1F862 -:1029D0003835DBB20493D1F83C3506A8DBB2059359 -:1029E000FFF7D4FE0023C1F8003101230B60D1F8BA -:1029F0000031002BFBD06846FFF7C8FE0DB030BD9C -:102A0000781100000090024090F8293070B504461B -:102A100013B190F82A206ABB6068FF286DD0A268C5 -:102A2000FF2A6AD0E268FF2A67D02269FF2A64D0B1 -:102A3000D3B90321FFF71EFEA0680321FFF71AFE9A -:102A4000E0680321FFF716FE20690321FFF712FE5D -:102A50006069FF2802D00321FFF70CFEA069FF2860 -:102A600002D00321FFF706FE94F82A3013B9201D87 -:102A7000FFF78CFE224E636A224D736394F829306F -:102A800086F83A300123C5F80833FFF785FF23683D -:102A9000C5F84035A37F617F227F9B0143EAC103D4 -:102AA0001343E27F43EAC213C5F84435D5F8002644 -:102AB00094F8203022F07F4222F0FF02134394F872 -:102AC000212043EA026394F8222043EA426394F807 -:102AD000232043EA0273C5F8003633685BB194F8EB -:102AE0002830094A5B01DBB282F829334FF40073C6 -:102AF000C2F884315360054870BD0548FCE700BF4B -:102B0000500201200090024000E100E00000AD0B07 -:102B10000400AD0BF8B50F461646054620B910481F -:102B20004FF4DB71FFF7BEFC0E4C94F83030A3B9C4 -:102B3000C4E900764DB90023C4E9023301220A48F2 -:102B400084F8302084F83830F8BD2846FFF75CFF61 -:102B5000054B9842EFD00548F6E70548F4E700BF7B -:102B600081080000500201200000AD0B0400AD0BF5 -:102B70000C00AD0B2DE9F047DFF8C4A09AF8303017 -:102B800005468946164623B928484FF40171FFF7D8 -:102B900089FC9AF83000012846D19AF83830DFF8DD -:102BA000948063B3224F0123C7F80833FFF7F4FE84 -:102BB0006C78B9F1000F29D1AA784FF000098AF892 -:102BC00039902302C7F8009143EA02332A7813436D -:102BD000EA7843EA42332A7943EA82336A7943EA5C -:102BE000C233C7F83436FFF7FFFD404513D12B79C8 -:102BF0004BB90E4840F23621FFF754FC03E0FFF7D3 -:102C000021FE4045CED10B48BDE8F087494620461D -:102C1000FFF78AFDD0E7C7F800911EB1687831460A -:102C2000FFF762FD0448EFE70448EDE78108000084 -:102C3000009002400700AD0B0000AD0B0B00AD0B88 -:102C4000500201200323FFF71BBE30B50546900854 -:102C500040380124844014EA010009D0A85838B152 -:102C60000021A950AA5813B11A6822431A60012002 -:102C700030BD2DE9F341002304460D4601939046F3 -:102C800001AB4FF4A672FFF7E0FF4FF482722946C2 -:102C90002046FFF7DAFF4FF48C720746294620469C -:102CA000FFF7D3FF064630B1B8F1000F1DD017B1C2 -:102CB000D4F83C353E4601AB4FF488722946204695 -:102CC000FFF7C3FF30B1B8F1000F03D016B90FB151 -:102CD000D4F83C3501AB4FF4907229462046FFF7FB -:102CE000B4FF019802B0BDE8F0814646E3E700007A -:102CF00003685A0910B504461CD0012A06D14FF6C4 -:102D0000FF7203F01F03DA40D30704D40D484FF4D9 -:102D10006C71FFF7C7FB236803F01F025B09226099 -:102D20000BD0012B0CD0074840F2B931FFF7BAFBAA -:102D3000002010BD4FF0FF32E3E74FF0A040F8E76E -:102D40000148F6E74A0800000003005007B501906B -:102D500001A8FFF7CDFF019A01239340C0F8083581 -:102D600003B05DF804FB07B5019001A8FFF7C0FFB1 -:102D7000019A01239340C0F80C3503B05DF804FBC1 -:102D8000F7B51746421C1C460646089B0D4618D050 -:102D9000012902D1BBB1FFF7D9FF01A80196FFF7C6 -:102DA000A7FF019B00EB83002402D0F80027074B0C -:102DB00044EA87042C4313401C4344EA4504C0F80A -:102DC000004703B0F0BDFFF7CEFFE6E7F0F8FCFFE9 -:102DD000F8B51A4F012304460D46436164261F438C -:102DE000002202212046FFF744FF18B94020B847CF -:102DF000013EF5D14FF080530022D3F830116A77AD -:102E0000082918D1D3F83431ABB94FF0FF32D4F8D8 -:102E10000805D4F80C15C4F80035C4F80825C4F822 -:102E20000C250922C4F80025C4F80035C4F80805AB -:102E3000C4F80C15F8BDC4F80025FBE7901100009C -:102E4000104B98421BD011D80F4B984217D008D87E -:102E50000E4A0F4B984218BF90420CBF0120002031 -:102E600070470C4BC31A5842584170470A4B98425E -:102E700005D002D8094A0A4BECE70A4BF2E70120D9 -:102E8000704700BF80841E0020A1070090D003007F -:102E900048E8010040420F000024F40000127A00CC -:102EA00000093D000048E8012DE9F3474B69794AE4 -:102EB00090F80490934207460C4600F0D08000F250 -:102EC000B880754A934200F0CD8000F2AB80734A1F -:102ED000934200F0CA80724A934200F0C980714860 -:102EE0004FF4BC71FFF7DEFA4FF08045DFF8D08178 -:102EF000E67E242202FB0983997F66F3000199771D -:102F00003B79217C02FB03839A7F61F341029A772C -:102F10005EBB6269644B9A420CBF4FF0030A4FF0EC -:102F2000000A237E012B94BF002301230093206815 -:102F3000534632460121FFF723FF0096606853464F -:102F400032460121FFF71CFF0096A27EA06853467F -:102F50003146FFF715FF237C83F001030093E068FF -:102F6000534632460121FFF70BFF242303FB09835D -:102F7000E2681A62217C9A7F267F61F341029A7788 -:102F800094F812C0217E94F819E03B6896B9D4E910 -:102F900001902268C3F80825C3F80C95C3F8100502 -:102FA0004FF0FF30C3F86C05C3F81405C3F868652B -:102FB000C3F86465BEF1000218BF01220229C3F8FC -:102FC000C0C5C3F8245559D003295AD0012901D1CD -:102FD00042F00202C3F854250222C3F860253A7970 -:102FE00024214A4358F80220CAB1627C43F30733D4 -:102FF0005201002BD2B247DB03F1604101F5614180 -:1030000081F8002303F01F015B099B0003F160437B -:1030100003F5614301228A40C3F880211A6002B09F -:10302000BDE8F087214A93427FF459AF4FF08055B5 -:103030005CE71F4A93421ED00AD81E4A93423FF4CF -:1030400053AF1D4A93427FF44AAF4FF000454DE71E -:10305000154A93427FF443AF4FF0A05546E74FF037 -:10306000005543E74FF0006540E74FF000753DE73E -:103070004FF080653AE74FF0206537E742F00402F1 -:10308000A8E742F00602A5E70C4903F00F03CA5473 -:10309000C5E700BF80841E0020A1070048E80100AA -:1030A00090D00300C10800000048E80140420F0032 -:1030B0000024F40000093D0000127A0014ED00E045 -:1030C0008C020120431C07B50ED0019001A8FFF728 -:1030D0000FFE019B00EB8300054BD0F80027134047 -:1030E00043F00203C0F8003703B05DF804FB00BFF3 -:1030F000F0F8FCFF2DE9F8430646914698460D4648 -:1031000021B9154840F24B21FFF7CCF93779134C20 -:10311000242303FB07431B7FD3B91DB16869FFF765 -:103120008FFEB8B124217943631844F80190C3F8A5 -:1031300004801DB129463046FFF7B6FE242303FB69 -:10314000074400236377064801232377BDE8F8830B -:103150000448FBE70448F9E7C10800008C0201209D -:103160000000AD0B0C00AD0B0400AD0B2DE9F843D6 -:10317000274C0679242303FB064305461B7F23B90E -:1031800024484FF42B71FFF78DF92B6843F3073375 -:10319000002B0DDB5A09012103F01F03994002F1B6 -:1031A00020031D4A42F82310BFF34F8FBFF36F8FE8 -:1031B00024217143635833B12868184BC0F8083391 -:1031C0002144FFF705FE24272B6807FB0647D3F8A9 -:1031D0000805D3F80C85D3F81095BB7FDB070AD41C -:1031E000FFF770FF4846FFF76DFF4046FFF76AFFA5 -:1031F000386AFFF767FF2B68094A934204BF012230 -:103200005A60242303FB064400232377BDE8F88398 -:103210008C020120C108000000E100E0520108001A -:1032200000F002402DE9F04F6D4F90F80480242308 -:1032300003FB087385B01B7F82460D46164623B9F3 -:10324000684840F29131FFF72DF92B6833B96B686C -:1032500023B1644840F29231FFF724F9AB68002BA8 -:1032600055D1EB68002B4DD1EB88002B52D116F0D5 -:1032700008090AD0242303FB08731B6A013304D016 -:10328000584840F29731FFF70DF9242303FB08F368 -:10329000FA18517F002940F0A180FB5823B116F0A5 -:1032A000140F04BF0123537795E80F00242404FB77 -:1032B000087404F10C0C8CE80F00206A431C04D045 -:1032C000A37F99072CD5FFF741FDD5F800B0BBF1DE -:1032D000000F04D00BF06043B3F1005F09D1D5F8C3 -:1032E00008E0BEF1000F1ED00EF06043B3F1005FA6 -:1032F00019D0242303FB087300223B485A7705B0FA -:10330000BDE8F08F374840F29331FFF7CBF8EB89F7 -:10331000002BA9D033484FF46571FFF7C3F8A6E737 -:10332000FFF721FDD1E7DAF8004016F0010318BFDE -:103330000123C4F8503516F0020318BF0123C4F866 -:10334000403595E80F00C4F844B58DE80F00C4F887 -:103350004815C4F834E5C4F838350023C4F81831EA -:10336000D4F818310723C4F80035242303FB08F3ED -:10337000EC46FB58B9F1000F21D10122226103BBB9 -:10338000D4F81831002BFBD0624640212046FFF7CD -:1033900070FC242303FB0873186A421C04D09B7F33 -:1033A0009B0718D5FFF7DFFCB9F1000F05D12421E9 -:1033B00001FB08712046FFF70BFD0C489FE7002B2F -:1033C000E7D016F0040F4FF0400314BFC4F80833E1 -:1033D000C4F80433F1E7FFF7B9FCE5E704488EE7EA -:1033E0008C020120C10800000A00AD0B0000AD0BEB -:1033F0000B00AD0BCA7B036810B52AB9D1E90042B6 -:10340000C3F80845C3F80C258A68C3F82425027957 -:103410003C20424311488258CAB10A7B43F3073328 -:103420005201002BD2B213DB03F1604101F561417F -:1034300081F8002303F01F015B099B0003F1604347 -:1034400003F5614301228A40C3F880211A6010BD50 -:10345000034903F00F03CA54F9E700BFB00201208B -:1034600014ED00E0D0F8043130B52BB10023C0F8E2 -:103470000431D0F80431012391F82F2022B3002227 -:10348000C0F82421D0F82441C0F81C21D0F81C41F8 -:10349000C0F80821D0F80821002B79D0002281F84B -:1034A0002D2091F82F20002A37D14D6B8C6AA54230 -:1034B0001FBF0523C0F80025C0F80035134683F070 -:1034C000010381F82F3028E0D0F8244114B1C0F86E -:1034D00024210EE0D0F81C216AB3C0F81C41D0F8BA -:1034E0001C214A6BD0F8245101324A6345B1C0F81F -:1034F0002441D0F824210122426181F82F20CBE71A -:103500008C6AA24204D24C6AA25CC0F81C25C3E7B4 -:1035100091F82C201AB10123C361002030BD0A6A42 -:1035200012F0400F4FF0010203D0C26181F82D204C -:10353000F3E74261B0E7D0F80841002CACD0C0F806 -:103540000821D0F80841D0F82441002CBFD14A6BA3 -:103550008C6AA242A0D24C6AD0F81855A5544A6B86 -:103560008C6A0132651EAA424A6308D10A6A54066F -:1035700002D40222C0F80022012202628CE7A24299 -:10358000FAD10A6A520687D5022381F82D30C4E7A2 -:103590000120C3E72DE9F0415FEA50180E461746B7 -:1035A0001C4600F01F051DD0B8F1010F04D14FF6E5 -:1035B000FF73EB40DB0704D416484FF46C71FEF741 -:1035C00071FFB8F1000F10D0B8F1010F1FD01148F2 -:1035D00040F2B931FEF766FF05F5E075002353F8B8 -:1035E0002530FFDE4FF0FF33E3E74FF0A04303EB5E -:1035F0008503094AD3F80017240244EA87040A40E5 -:1036000044EA46041443C3F80047BDE8F081034B85 -:10361000EDE700BF4A080000F0F8FCFF000300508F -:1036200013B53A4B01930023C0F80431D0F80421BC -:10363000C0F82431D0F82421C0F81C31D0F81C2166 -:10364000C0F80831D0F80821C0F800324B6381F887 -:103650002F300123036291F82D209A4218BF836016 -:103660008B6A044673B14B6A1B78C0F81C350B6833 -:1036700003B340F286238B608B682548C4F804337B -:1036800002B010BD91F82C3013B10123C361EEE7F5 -:103690000B6A13F0400F4FF0010316BFC361436183 -:1036A00081F82D30E3E72046FFF7DCFE28B1019BCF -:1036B000013B0193019B002BF5DC91F82F305BB9A6 -:1036C0001348019B002BDBDC0023C4F800350523E5 -:1036D0001048C4F80035D3E7D4F8C434C4F8C4346F -:1036E0008BB10C4A0C4813F0010F08BF104613F0C1 -:1036F000020F02F5803218BF104613F0040F074B7B -:1037000018BF1846DDE70348DBE700BFA0860100CD -:103710000000AD0B0100AD0B0000AE0B0200AE0BC4 -:1037200013B5334B01930023C0F80431D0F80421C2 -:10373000C0F82431D0F82421C0F81C31D0F81C2165 -:10374000C0F80831D0F808214B6381F82F308B6A1C -:10375000012B044617D10B6A5B0614D40223C4F86C -:1037600000320123236291F82D20022A18BF236022 -:103770000B688BB140F286238B608B681D48C4F8C0 -:10378000043302B010BD0123E9E72046FFF76AFECB -:1037900028B1019B013B0193019B002BF5DC91F8C3 -:1037A0002F305BB91348019B002BEADC0023C4F8DF -:1037B000003505231048C4F80035E2E7D4F8C434D6 -:1037C000C4F8C4348BB10C4A0C4813F0010F08BF85 -:1037D000104613F0020F02F5803218BF104613F0A6 -:1037E000040F074B18BF1846DDE70348DBE700BFAF -:1037F000A08601000000AD0B0100AD0B0000AE0B78 -:103800000200AE0B2DE9F047074691469A460C465A -:1038100019B92248B521FEF745FE97F80480204DDE -:103820003C2000FB08F0291891F82E609EBB8E60AA -:1038300045F80090C1F804A081F82D6081F831604E -:1038400081F83060F4B1637B81F83830A37B81F874 -:1038500039309BB9D4E900239A4203D10F48E821BB -:10386000FEF720FE2068062303220021FFF792FEC8 -:103870006068062303220021FFF78CFE21463846AC -:10388000FFF7B8FD3C2303FB08550648012385F8E4 -:103890002E30BDE8F0870448FBE700BF01090000B7 -:1038A000B00201200000AD0B0C00AD0B70B50D4C4B -:1038B00006793C2303FB0643054693F82E30012B83 -:1038C00004D0094840F23111FEF7ECFD2B680522C7 -:1038D000C3F800253C2303FB0644022384F82E3062 -:1038E00070BD00BFB00201200109000070B5124C8C -:1038F00006793C2303FB0643054693F82E3023B993 -:103900000E484FF49F71FEF7CDFD2B680C4AC3F8AB -:103910000823D3F8002222F00302C3F80022002279 -:10392000C3F800253C2303FB0644012384F82E3012 -:1039300084F8302070BD00BFB002012001090000F2 -:10394000864204002DE9F8434D4F06793C2303FBE2 -:103950000673054693F82E30022B0C46904604D091 -:10396000484840F2AE21FEF79DFDE36833B9636835 -:1039700023B1444840F2AF21FEF794FD236933B9E7 -:10398000A36823B13F484FF42C71FEF78BFD4FF035 -:103990003C0909FB06F957F80930A3B92378022B33 -:1039A00008D02378032B0ED136484FF42D71FEF743 -:1039B00079FD08E0334840F2B321FEF773FD57F874 -:1039C0000930002BEDD03C2303FB0673D5F80090A3 -:1039D00093F82D30012B04D12378012B06D12A48EE -:1039E00011E0022B02D12378012BF8D13C25274B83 -:1039F000C9F8083305FB067595F830302BB1AB6874 -:103A00002348C9F80433BDE8F883C8F3800383F082 -:103A10000103DBB2A64685F83030BEE80F0005F1A1 -:103A20000C0CACE80F00DEF80030CCF80030636816 -:103A3000AB62E368C5F820806B626378C9F88835AB -:103A40002378012B17D09BB9C8F340183C2303FB04 -:103A500006732946484683F82C80FFF7E1FD3C2396 -:103A60005E43BA19BB59002BCDD182F83030CAE77A -:103A70004FF00108EAE729464846FFF751FEEEE716 -:103A8000B0020120010900000500AD0B86420400D0 -:103A90000B00AD0B0F49104A104B00F010F8104905 -:103AA000104A114B00F00BF81049114A114B00F06D -:103AB00006F81149114A124B00F001F806E0521ABB -:103AC00003DD043A98588850FBDC704700F03AF860 -:103AD000FDF7B0FB000100202C010020043E000097 -:103AE0002C0100202C010020303E00002C01002081 -:103AF0002C010020303E00002C0100202C01002071 -:103B0000303E0000FEE7FEE7FEE7FEE7FEE7FEE7E9 -:103B1000FEE7FEE7FEE7FEE74FF08053D3F83021E3 -:103B2000082A03BFD3F83401B0FA80F0400900201E -:103B300070470000024AD2F80034002BFBD07047D7 -:103B400000E0014008B54FF08053D3F83021082A37 -:103B50004ED14FF080420021C2F80C11C2F8101172 -:103B6000C2F8381502F54042D3F80414C2F8201503 -:103B7000D3F80814C2F82415D3F80C14C2F8281589 -:103B8000D3F81014C2F82C15D3F81414C2F8301559 -:103B9000D3F81814C2F83415D3F81C14C2F8401521 -:103BA000D3F82014C2F84415D3F82414C2F84815E9 -:103BB000D3F82814C2F84C15D3F82C14C2F85015B9 -:103BC000D3F83014C2F85415D3F83414C2F8601581 -:103BD000D3F83814C2F86415D3F83C14C2F8681549 -:103BE000D3F84014C2F86C15D3F84434C2F87035D9 -:103BF000FFF792FF18B13B4B3B4AC3F88C26FFF707 -:103C00008BFF18B1394BFB22C3F81825FFF784FF4F -:103C100058B14FF080424FF08051D2F8E43ED1F8D5 -:103C2000581261F30303C2F8E43EFFF775FF20B1B9 -:103C30002F4B4FF40072C3F840264FF08053D3F857 -:103C40003031082B09D14FF08043D3F80024D2073C -:103C500044BF6FF00102C3F80024264AD2F888302E -:103C600043F47003C2F88830BFF34F8FBFF36F8FF8 -:103C70004FF08053D3F83021082A0BD1D3F83431D8 -:103C8000042B81BF4FF01023D3F808224FF080435C -:103C9000C3F858254FF01021D1F80C32DB071FD59F -:103CA00015480123C0F80435FFF744FFD1F80C3262 -:103CB00023F00103C1F80C32FFF73CFF0023C0F8EA -:103CC0000435FFF737FFBFF34F8F0A490B4BCA6824 -:103CD00002F4E0621343CB60BFF34F8F00BFFDE7F8 -:103CE00008BD00BF005000404881030000F00040C4 -:103CF0000090024000ED00E000E001400400FA0501 -:103D000008B5064B044613B10021AFF30080044B05 -:103D10001B6803B198472046FEF7FCFA000000003C -:103D2000EC02012002440346934200D1704703F89D -:103D3000011BF9E770B50D4B0D4D5B1B9C10002668 -:103D4000A64209D100F028F80A4D0B4B5B1B9C10D2 -:103D50000026A64205D170BD55F8043B98470136B0 -:103D6000EEE755F8043B98470136F2E7A43D000022 -:103D7000A43D0000A43D0000A83D00000A4491427B -:103D800000F1FF3300D1704710B511F8014B03F873 -:103D9000014F9142F9D110BDF8B500BFF8BC08BC85 -:103DA0009E46704709120000F8B500BFF8BC08BC79 -:0C3DB0009E467047E1110000000000007A -:103DBC00043E0000000100200B000000303E00001B -:103DCC002C01002000000000303E00002C010020DF -:103DDC0000000000303E00002C010020000000001C -:103DEC002C01002071400000F002012000000000B6 -:083DFC00F002012000000000AC -:103E040000F0024000000000FFFFFFFF060000007A -:103E140008000000FFFFFFFFFFFFFFFF0007FF0098 -:0C3E240000093D0000000000000000004C -:020000042000DA -:1000000000000000000000000000000000000000F0 -:1000100000000000000000000000000000000000E0 -:1000200000000000000000000000000000000000D0 -:1000300000000000000000000000000000000000C0 -:1000400000000000000000000000000000000000B0 -:1000500000000000000000000000000000000000A0 -:100060000000000000000000000000000000000090 -:100070000000000000000000000000000000000080 -:100080000000000000000000000000000000000070 -:100090000000000000000000000000000000000060 -:1000A0000000000000000000000000000000000050 -:1000B0000000000000000000000000000000000040 -:1000C0000000000000000000000000000000000030 -:1000D0000000000000000000000000000000000020 -:1000E0000000000000000000000000000000000010 -:1000F0000000000000000000000000000000000000 -:0400000300003A952A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_bb2@1447134832.bin b/bin/boot/nowatchdog_boot_bb2@1447134832.bin deleted file mode 100755 index 06d3acffae..0000000000 Binary files a/bin/boot/nowatchdog_boot_bb2@1447134832.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_bb2@1447134832.hex b/bin/boot/nowatchdog_boot_bb2@1447134832.hex deleted file mode 100644 index dba413e6e6..0000000000 --- a/bin/boot/nowatchdog_boot_bb2@1447134832.hex +++ /dev/null @@ -1,1061 +0,0 @@ -:020000040800F2 -:1000000048200120A52C0008053000080530000814 -:100010000530000805300008053000080000000029 -:100020000000000000000000000000000530000893 -:100030000530000800000000053000080530000809 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0CEFD2E -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:08016000BBFD05B05DF804FBD6 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:08018800A7FD05B05DF804FBC2 -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F078FCDDF820C01A -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F019FC089B9CE0BA425C -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00BD3F00084B45F7DBEDE70D9907910798C7 -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01C43F0008DD -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C0010B5044608484021FFF794FC0028F9D094 -:10191C0005482146FFF78AFC03484021FFF78AFC63 -:10192C000028F9D010BD00BF00480040014608B5A2 -:10193C000448FFF767FC03480221FFF765FC002809 -:10194C00F9D008BD0038004000B9074803680133DE -:10195C0008D04368013305D0C069044BC31A584200 -:10196C005841704700207047004000086F57654E83 -:10197C00074B1A69C2F306427F2A07D11869C0F3D4 -:10198C000C00B0F1FF0358425841704700207047DB -:10199C000028004000F13F4000F57E00800A082836 -:1019AC000FD8084A52F820305BB1013B42F8203086 -:1019BC003BB9012101FA00F0034B1A6B22EA00003B -:1019CC0018637047200000200038024000F13F40AF -:1019DC0000F57E00800A08280DD8074A52F82030FE -:1019EC00591C42F8201033B9012101FA00F0034BC5 -:1019FC001A6B104318637047200000200038024017 -:101A0C0010B50C4C2046FFF7E1FF072001F036FB28 -:101A1C004FF4805100222046FFF786FB042001F092 -:101A2C002DFB2046FFF7B6FF4FF480400021FFF757 -:101A3C0077F810BD0004024010B50B4C01214FF497 -:101A4C008040FFF76DF82046FFF7C0FF20464FF4AB -:101A5C0080510122FFF768FB072001F00FFB2046A5 -:101A6C00FFF798FF10BD00BF0004024008B5FFF758 -:101A7C00E3FF0020FFF75AFF0020FFF757FF00207D -:101A8C00FFF754FFFFF7BCFFFEF772FEBFF34F8F5B -:101A9C00044A054BD16801F4E0610B43D360BFF3FA -:101AAC004F8FFEE700ED00E00400FA05F0B55E256F -:101ABC006843204BB0FBF1F51A68ADF6DC3D95425E -:101ACC0034D002AC00214FF43D6220461D60FEF77D -:101ADC00BEFA1B238DE818001748102140226923F9 -:101AEC0001F0AFFA08238DE8180014486023182180 -:101AFC006A2201F0A6FA0DF2FA760DF28E7300222C -:101B0C00AA420DD002F11900C0B2C108012700F0A1 -:101B1C00070007FA00F05F5C013238435854EFE7D6 -:101B2C001233B342EBD1204601F0E3FA0DF6DC3D63 -:101B3C00F0BD00BF08000020B03500084539000892 -:101B4C002DE9F043ADF6DC3D02AC00214FF43D62D3 -:101B5C0080462046FEF77BFA2C238DE818002022C5 -:101B6C00284818215C2301F06CFA1C2215460F271B -:101B7C00974007EA0807D740234B242101FB07377E -:101B8C000DF50366002117F801C00023082B07D0C0 -:101B9C000120984010EA0C0F0CBF0020012003E03C -:101BAC007818407800F0010078B15819C0B24FEAAB -:101BBC00D00E4FF0010900F0070009FA00F016F8FA -:101BCC000E9040EA090006F80E000133092BDDD116 -:101BDC000231242906F11206D5D10B35742DA2F150 -:101BEC000402C4D10E238DE81800102190226D231D -:101BFC00064801F026FA204601F07BFA0DF6DC3D92 -:101C0C00BDE8F083A0330008A5390008DC320008D9 -:101C1C0010B5ADF6D83D02AC204600214FF43D6224 -:101C2C00FEF715FA1B238DE81800102140226923BA -:101C3C00044801F006FA204601F05BFA0DF6D83D97 -:101C4C0010BD00BFB03500082DE9F74F434E444F8F -:101C5C0001214FF48040FEF763FF3046FFF7B6FEDC -:101C6C003846404CFFF7B2FE30460D210522FFF7F7 -:101C7C0060FA052230460F21FFF75BFA3046214609 -:101C8C000125FFF7FEF920233846214623602571F4 -:101C9C00A5714FF0000A01934FF4805BFFF7F1F947 -:101CAC00304621464FF002084FF48049257184F8E4 -:101CBC0006A0C4F800B0FFF7E4F93046214684F8DA -:101CCC000480C4F80080FFF7DCF921462571A5716A -:101CDC00C4F80090244C3046FFF7D3F92046FFF7A8 -:101CEC0039F920462149FFF75FFA20462946FFF7CC -:101CFC007AFA019B384619465246FFF715FA2A46DE -:101D0C0030464946FFF710FA40462946FEF708FFD1 -:101D1C00A4F5505480232380F92323854FF4F042FB -:101D2C004FF6FF13E3623046A283294623644246F2 -:101D3C00A4F820B0FFF7FDF929462046A582FEF74E -:101D4C001FFD3846FFF726FE3046FFF723FE4846B8 -:101D5C005146FEF7E5FE03B0BDE8F08F00040240EB -:101D6C000008024000000020003800400C00002059 -:101D7C0010B5074C2046FFF729FE20461021FFF72F -:101D8C00CFF92046FFF706FE012001F077F910BDD0 -:101D9C000000024038B50A4B0C2404FB0034606888 -:101DAC00FFF714FE21896068FFF7B4F905466068F7 -:101DBC00FFF7F0FDD5F1010038BF002038BD00BFA2 -:101DCC009432000810B5064C2046FFF7FFFD204664 -:101DDC001021FFF7A7F92046FFF7DCFD10BD00BF6F -:101DEC0000000240F0B587B0FFF7C2FD002841D1DA -:101DFC000320FFF759F807460420FFF755F8474B27 -:101E0C000A241A68C2F30545C2F30626C2F302136C -:101E1C0002F00F0204FB0322330906F00F0604FB49 -:101E2C0003662B0905F00F0504FB03533C2505FB4A -:101E3C00036305FB0323191A44BF01F5A83101F50F -:101E4C00C07107EB91210320FFF71EF803A8FFF7E1 -:101E5C0098F903A8FEF7F4FE01A8FFF798F901A979 -:101E6C000020FEF731FF02A8FFF797F9002002A926 -:101E7C00FEF794FF0A2442F2107501F05CF910B9D8 -:101E8C00013DFAD107E0264D01F055F908B1013DAD -:101E9C00FAD1013CEFD10020FEF792FD00214FF268 -:101EAC000400FFF71DF84FF43C50FFF745F81D4BAD -:101EBC0001201A6842F004021A60FEF781FD1A4BE9 -:101ECC001B781BB1194801F055F908E01848FEF7CA -:101EDC0001F9174902461748FEF7DDF970B11648AB -:101EEC0001F048F90020FEF7EFFF08200121FEF772 -:101EFC00B7FF4FF400400121FEF7F2FF0F4801F04D -:101F0C0039F9084B1A6842F002021A600C4B1A6934 -:101F1C0042F004021A6130BF07B0F0BD0028004047 -:101F2C00A086010000700040017AFF1F2A370008CC -:101F3C003F3700082078FF1F453700085A37000844 -:101F4C0000ED00E0B0F1006F10B504460BD20D4867 -:101F5C0001F007F9204601F013F90B4801F00AF9DA -:101F6C004FF0FF3010BD094A002352F8041FA14264 -:101F7C0002D851688C4202D30133072BF5D1184695 -:101F8C0010BD00BF6E3800086B37000870320008B7 -:101F9C0008B5024801F0EEF800BEFEE784370008F1 -:101FAC0010B504460A480221FFF72EF90028F9D093 -:101FBC0007482146FFF726F905480121FFF724F9C8 -:101FCC000028F9D00248FFF71AF9C0B210BD00BFC3 -:101FDC00003001402DE9F04F87B00121814600AF60 -:101FEC000020FFF763FD042001F063F807F10C00FB -:101FFC0049460C2201F0E9F8FD683C690C2DD7F834 -:10200C0014B013D0994801F0ACF8284601F0B8F898 -:10201C00974801F0A6F8204601F0B2F8954801F077 -:10202C00A0F8584601F0ACF8934833E0934801F01F -:10203C00A1F84FF480500121FEF766FD09F10C095F -:10204C00EA46FEF7DFFBA0B06E462546C846802D5B -:10205C000CD941463046802201F0B7F83046202199 -:10206C00FEF7DCFB803D08F18008F0E725B1304637 -:10207C0041462A4601F0A9F82946304600F0C6FF31 -:10208C00002180464FF48050FEF73EFDD845D546E2 -:10209C0004D07B4801F06EF80120E3E088B068467C -:1020AC004946202201F091F86846FFF74DFC786014 -:1020BC007448D54601F05EF87B682BB9724801F084 -:1020CC0059F804F5404600E02646002E3BD06F48F8 -:1020DC00FFF738FF05466E483044FFF733FF002DFD -:1020EC00824630DB00282EDB431B01334FEA430BC7 -:1020FC0000205946FFF7DAFCFEF7B0FB654B0026D3 -:10210C0003EB45033B6055451BDCF320FEF7BCFBA2 -:10211C003B68002133F81600FEF7D6FB082806F1C1 -:10212C00010608D05C4801F01CF8284601F028F89C -:10213C0001F00FF805E030465946FFF7B7FC0135C2 -:10214C00E1E7FEF799FB554801F014F87B680BB1F9 -:10215C004E4B00E0524B7B6063083B600025A54270 -:10216C003CD2661BB6F5803F28BF4FF480362EB1AB -:10217C004C4805EB0901324601F027F8D7F804A0CA -:10218C00FEF76CFBF320FEF77FFBAA444FF0000B2D -:10219C00B34519D043490BEB0A0011F80B10FEF7AD -:1021AC00CBFB08280DD0404800F0DBFF504600F078 -:1021BC00E7FF00F0CEFFFEF75FFB3C4800F0DAFFD4 -:1021CC000CE00BF1010BE3E7FEF756FB3B682146F5 -:1021DC0003EB5500FFF76AFC5D44C0E7344800F0A0 -:1021EC00C0FF204600F0CCFF324800F0C3FF0020B7 -:1021FC00FFF7AAFB20B1304800F0BCFF234D03E0F1 -:10220C002E4800F0B7FF264D4FF480500121A60850 -:10221C00FEF77AFCFEF7F6FA31462846FEF7FEFA90 -:10222C0004F0030105EB860000F0F0FE00210446EB -:10223C004FF48050FEF768FC214800F092FF4046B6 -:10224C0000F09EFF1F4800F08CFF204600F098FF26 -:10225C0000F07FFF444504D01B4800F08BFF0220A8 -:10226C0000E000201C37BD46BDE8F08F8F3700081A -:10227C0098370008A2370008A9370008C8370008AB -:10228C00E5370008083800081B3800080040000833 -:10229C00FF3F0008CC320008333800084B380008E8 -:1022AC0000000108440000205E3800087738000860 -:1022BC00823800089038000897380008BC380008AD -:1022CC00E1380008F4380008FA38000870B54FF40B -:1022DC0080500121FEF730FCFFF774FD0520164CF1 -:1022EC00FFF75EFE002C04F1FF341FDCFFF740FD0E -:1022FC00FFF768FD9F20FFF753FEA920FFF750FE64 -:10230C000546A920FFF74CFE0646A920FFF748FE1C -:10231C003602044646EA0545FFF72AFD4FF4805085 -:10232C000021FEF709FC45EA040070BDA920FFF767 -:10233C0037FEC307D6D4D9E70090D00308B50446BE -:10234C00114800F00EFF204600F01AFF00F001FFCC -:10235C002046FFF7F5FB00F0EEFE0C4B20F00400DE -:10236C0018701D4600F0E7FE2B7800F0FB0083424E -:10237C0001D10B2401E0FFF779FB013CF2D04FF4C3 -:10238C007A7000F07BFEF8E7E53B00084400012082 -:10239C0080B5464B4FF4C06290B01A6001214FF0EB -:1023AC008050FEF7BDFB0120FEF704FB4048FFF711 -:1023BC000DFB4FF480200121FEF7B2FB3C480A21B3 -:1023CC000722FEF7B6FE072239480B21FEF7B1FEB5 -:1023DC0010AF4FF4806347F8383D00244FF00108EC -:1023EC000225334839468DF80E408DF80F808DF854 -:1023FC000C508DF80D50FEF744FE4FF400632C4842 -:10240C00394602938DF80C5004AEFEF73AFE4FF4A9 -:10241C006133049327480C233146ADF81A30ADF8DC -:10242C001440ADF81640ADF81840ADF81C40FEF75E -:10243C00BFFD41461F48FEF7EDFE1D48FFF7AAFA07 -:10244C001D4800F08EFE1D4B586F00F099FE00F0F9 -:10245C0080FE7920FEF7B8FB404502D1184800F009 -:10246C0089FE012000F010FE10B90121FEF70CFDD1 -:10247C004120FEF7A9FB58BB124800F07BFE01205F -:10248C00FEF7D0FA47F230544120FEF79DFBF8B925 -:10249C004FF47A7000F0F2FD013CF5D10A4800F0DF -:1024AC0069FEFFF7D1FB094837E100BF003C024051 -:1024BC000008024000480040F13B00080038024090 -:1024CC00013C0008113C0008293C0008114550FE55 -:1024DC004FF48070FEF7FAFA0120FEF70FFBFEF7BF -:1024EC00D1FB0020FEF76CFA01214FF48040FEF77F -:1024FC0023FBBB48FFF76AFA3046FEF702FE0024C6 -:10250C000125B74831468DF815408DF816408DF8E9 -:10251C00174004958DF81450FEF7B3FD2246B048D1 -:10252C002946FEF701FEAE48FFF734FAAD4C04F134 -:10253C00300954F8040CFFF749FA3046FEF7E1FD78 -:10254C00236854F8040C4FF000083146022504931C -:10255C008DF814808DF815508DF817800C34FEF71B -:10256C0090FD54F8100CFFF715FA4C45E1D14FF4DF -:10257C0080404146FEF7E0FA4FF4807000F084FD95 -:10258C00002800F0CC804FF4807000F087FDFFF73E -:10259C0029FC954800F0EEFD0220FEF785FC9349DE -:1025AC00884202D00220FEF76FFC914800F0D9FD62 -:1025BC000220FEF779FC00F0E3FD00F0CAFD4FF4B9 -:1025CC00804000F061FD90B18A4800F0D3FD4FF4DB -:1025DC00804000F063FD082000F060FD102000F04A -:1025EC005DFD202000F05AFD402000F057FDFFF764 -:1025FC002BFBFFF70DFB01214FF48050FEF79CFAEB -:10260C007748FFF7E3F901200146FEF77DFA052133 -:10261C000A467348FEF78DFD714806210522FEF728 -:10262C0088FD05226E480721FEF783FD0024022554 -:10263C00C0236B4839468DF80C508DF80D50029321 -:10264C008DF80E408DF80F40FEF71BFD20236448DB -:10265C0039468DF80F5002930125FEF712FD102319 -:10266C0039465F4802938DF80C508DF80F50FEF7E9 -:10267C0008FD6148FEF76EFC4FF48273ADF8123022 -:10268C004FF40073ADF81A305B4807233146ADF8B0 -:10269C002030ADF81040ADF81440ADF81640ADF850 -:1026AC001840ADF81C40ADF81E40FEF77DFD2946E4 -:1026BC005148FEF798FD4A48FFF76CF9FFF782FB8B -:1026CC00AB20FFF76DFCFFF753FB642000F0D6FC4A -:1026DC0021464FF48050FEF72FFAFFF7F7FD2846FE -:1026EC00FEF7E2FB25460490E8B2FFF753FB98BBDC -:1026FC0070550135042DF7D1049D3DB13F4800F0D4 -:10270C0030FD284600F03CFD00F023FD0120294659 -:10271C00FEF7BAFB002C3DD03948FFF70FFE2846D8 -:10272C00FEF754F9012814D12846FEF759F94FF455 -:10273C000060FEF7F3FB18B1324800F01BFDB9E066 -:10274C00314C00F0F8FC002800F00481013CF8D179 -:10275C00B0E000F0B9FC1CE7735D042B0ADD2B48DC -:10276C0000F008FD049800F00BFD01200021FEF79D -:10277C008BFB0FE00133DBB2042B7355B9DD24481E -:10278C0000F0EFFC284600F0FBFC224800F0F2FCC5 -:10279C000124AEE7FFF79AFD1F4B0344012B01D92F -:1027AC001E48BAE71E4800F0DCFC0020FEF77CFB5C -:1027BC0000F0E6FC00F0CDFC802000F065FC18B1C8 -:1027CC00802000F06BFC73E04FF4003000F05CFCF8 -:1027DC00044638B34FF4003000F060FC04E100BF55 -:1027EC00000002409C320008613C00087086415693 -:1027FC009D3C0008B23C000800300140203D000820 -:10280C00014550FE463C0008A0860100DF3C000854 -:10281C000B3D0008163D0008EA44DFFF024550FE60 -:10282C004C3D00080120FFF7B5FAD8B12046FFF760 -:10283C00B1FAB8B1B74800F09DFC41F288340120E0 -:10284C00FFF7A8FA002800F08B800020FFF7A2FA0F -:10285C00002800F085804FF47A7000F00FFC013CEA -:10286C00EDD1C1E00020FFF76FF808B1AA4B00E0F2 -:10287C00AA4B0CCB013301D0013271D1A84800F026 -:10288C0079FCB1E0A74800F075FC082000F006FCCC -:10289C00102000F003FC202000F000FC402000F091 -:1028AC00FDFB0024042000F0F9FB002C40F0088113 -:1028BC009D4832E7FFF75CF8002847D0002001A9BB -:1028CC009A4DFEF749FA344600203946FEF7A8FA2D -:1028DC000FCD0FC40FCD0FC495E80F0084E80F0087 -:1028EC009DF8092010A901EB82039DF80BE053F829 -:1028FC00343C40F26D1101FB0E3303EB9E030EF1E1 -:10290C00010E1EF0030F02D1022A88BF01339DF87D -:10291C000A409DF80410013C2344182404FB0314C2 -:10292C009DF805103C2202FB04149DF806100320B0 -:10293C0002FB0414FEF7B8FA01190320FEF7A4FAFF -:10294C0004200021FEF7A0FA00F0BEFB784800F04E -:10295C0011FC1EE6774800F00DFCFFF743FAF5E793 -:10296C007D20FEF731F970B94FF4007000F08CFB4C -:10297C0048B9714800F0FEFB082000F08FFB1020D6 -:10298C0000F08CFB85E07D20FEF71EF910B16B4842 -:10299C0000F0F0FB4FF4007000F076FB10B16848CB -:1029AC0000F0E8FB4FF4007000F078FB102000F012 -:1029BC006BFB48B1634800F0DDFB082000F06EFBB8 -:1029CC00102000F06BFB12E0082000F05DFB20B142 -:1029DC005D4800F0CFFB102003E05C4800F0CAFB20 -:1029EC00082000F066FB54E0594800F0C3FB59483E -:1029FC0000F0C0FB4FF40010FFF7ECFA10B1022806 -:102A0C0028D809E0202000F049FB402000F046FBCC -:102A1C00802000F04EFB1DE0082000F04AFB102047 -:102A2C0000F047FB202000F02FFB20B94A4800F0B3 -:102A3C00A1FB202009E0402000F026FB00287FF4B9 -:102A4C0021AF464800F096FB402000F032FB4FF4DB -:102A5C0000701AE0012425E7414800F08BFB0820A8 -:102A6C0000F01CFB102000F019FB202000F016FBDE -:102A7C00402000F013FB2046FFF7ACFA022840F090 -:102A8C00F080082000F015FB102000F012FBFEF780 -:102A9C00EDFF022000F0F8FA90B1042000F0F4FAF7 -:102AAC0004460028D8D02F4800F064FB042000F026 -:102ABC00F5FA022000F0F2FA4FF4004000F0EEFAC2 -:102ACC004FF4005000F0E0FA04464FF4805000F050 -:102ADC00DBFA400040EA84044FF4006000F0D4FAC2 -:102AEC00E4B22043431C03F0FF03013B4FF40060AE -:102AFC00062B58D8DFE803F03C383C043C383C004B -:102B0C0000F0CCFA4FF4805000F0C8FA4FF40050AB -:102B1C002EE000BF583D00080040000800000108EE -:102B2C008A3D0008B43E0008034550FE44320008BC -:102B3C00513C0008533F00089D3D0008AE3D000885 -:102B4C00C63D0008E63D0008FC3D0008123E0008AA -:102B5C00683F0008283E0008423E00087B3E000803 -:102B6C00243F0008EE3E000800F098FA4FF4805025 -:102B7C0000F09FFA45F25550FDF7FAFE0420FDF7E0 -:102B8C00FDFE40F6FF70FDF7FFFE0020FDF7F0FEA6 -:102B9C004FF480500121FDF755FE0020FEF7D4FEC6 -:102BAC0068B9354B0CE000F079FA4FF4805000F026 -:102BBC0075FA4FF4005000F071FA3048ADE5304B27 -:102BCC005D6830481E6800F0CCFA284600F0D8FA50 -:102BDC0000F0BFFA00F0BDFA00239A0002F1604247 -:102BEC00013302F561424FF0FF31082BC2F880101F -:102BFC00C2F88011F1D1244B00241C635C639C63EC -:102C0C001C645C64FDF7F4FE20480121FDF7A0FF75 -:102C1C0021461E48FDF79CFFF1200121FDF7A4FF82 -:102C2C002146F120FDF7A0FF01200146FDF7A8FF8A -:102C3C0021460120FDF7A4FF15480121FDF7ACFF4B -:102C4C0021461348FDF7A8FF12480121FDF7B0FFFC -:102C5C0010482146FDF7ACFF63B64FF0FF3EB5467A -:102C6C002847042000F01AFA022000F017FA4FF45B -:102C7C00004000F01EFA23E700000108044550FE56 -:102C8C00004000083F3F000800380240001060225E -:102C9C00FFC9FE36335907000748084A08B50849EA -:102CAC00121AFDF7CBF90748074A0021121AFDF753 -:102CBC00CEF9FFF76DFB00BF0000002020000020C4 -:102CCC00D43F000820000020480001202DE9F04FDF -:102CDC00DDE909AB0E469C4603783B490746C95CC7 -:102CEC0001300D07F8D42B2B05D02D2B04D1074622 -:102CFC004FF0FF3301E007460123397830290ED11C -:102D0C007978782905D132F0100103D102371022DD -:102D1C0007E00AB1082A04D10137082201E002B900 -:102D2C000A2200200021B84617F8015BA5F13004F7 -:102D3C00E4B2092C0DD9A5F14104192C02D8A5F146 -:102D4C00370405E0A5F16104192C30D8A5F157041E -:102D5C00E4B294422BDA4FEAE27900FB09F502FB6C -:102D6C000155A0FB0201E4B2294400194FF0000503 -:102D7C0041EB0501BCF1000FD5D0012B06D18245EA -:102D8C007BEB0104CFDA50465946CCE75C1CCAD128 -:102D9C00DDE90B89444261EB4105444575EB0909BA -:102DAC00C1DADDE90B01404261EB4101BBE70EB139 -:102DBC00C6F80080DD1700FB05F203FB0122A0FB27 -:102DCC0003011144BDE8F08F4C1200082DE9F04FBF -:102DDC0091B01F9E0C46310691468046DDE91A23C0 -:102DEC001E9D04D5CDE902234FF0000C1BE0002AF8 -:102DFC0073F100071046194602DA504263EB4301A7 -:102E0C00002A73F1000ACDE9020107DBB70708D4E9 -:102E1C0016F0040C07D04FF0200C04E04FF02D0CF2 -:102E2C0001E04FF02B0C16F04007089714BF704FC1 -:102E3C00704FDDE902AB079700271C980137C117CB -:102E4C00CDE90401DDE904230AA93944099150466E -:102E5C005946CDF804C0FEF773FA07980999835CBC -:102E6C00504601F8013C5946DDE90423FEF768FAA7 -:102E7C0082468B465AEA0B02DDF804C001D0162FAD -:102E8C00DBD116F0080239461DD01C9B082B02D052 -:102E9C00102B0ED01CE0DDE902AB1DB95AEA0B0B6E -:102EAC0013D004E05AEA0B0B12D0BD4210DC7B1C91 -:102EBC000FE0DDE902AB5AEA0B0B09D008982B4660 -:102ECC0048B1582208E02B4606E02A46012303E0CD -:102EDC002B46002200E078221CF1000B1D98C7EB5A -:102EEC00030A18BF4FF0010BBB42A8BF1F46C01B03 -:102EFC002AEAEA7ACBEB00000AB1022700E0174677 -:102F0C00C71B16F0010027EAE77701D07F4205E0E6 -:102F1C00F60603D5013504BFBA4407463D46002DDD -:102F2C000BDD2068013D461C4E453CBF202608F8B1 -:102F3C000060206801302060F1E727EAE7703F1A53 -:102F4C00BBF1000F08D02068451C4D4538BF08F870 -:102F5C0000C02068013020608AB12068451C4D45B6 -:102F6C003CBF302508F800502568681C02354D45DB -:102F7C00206038BF08F80020226801322260BAF1C4 -:102F8C00000F0CD022680AF1FF3A501C48453CBF98 -:102F9C00302008F80200226801322260EFE7DDE9F8 -:102FAC0002AB5AEA0B0B10D17BB919460DE0236822 -:102FBC0001395A1C4A4505D201F1280568462A5C9C -:102FCC0008F803202368013323600029EFD15FB197 -:102FDC00236801375A1C4A453CBF202208F80320BD -:102FEC00236801332360F2E711B0BDE8F08F00BF16 -:102FFC009B3F0008AC3F00081EF0040F0CBFEFF322 -:10300C000880EFF30980FEF7C3BF7047032970B542 -:10301C0005460C4606D98E083146FDF7FFFBB60077 -:10302C003544A41B14B9FDF707FC70BD00231846EA -:10303C00EA5C0133A34242EA0020F9D1FDF7E8FB38 -:10304C0070BD2DE9F0410733DE08079B4FF01208E5 -:10305C0008FB02320731069F054602EBD10800241B -:10306C00BC4209D0122000FB048029463246FCF7F2 -:10307C00E5FF01343544F3E7BDE8F08106235843FE -:10308C0001387FF4FDAF704710B504460020FDF702 -:10309C000BFF20420CBF0020012010BD10B50446D0 -:1030AC000020FDF701FF20EA04010020FDF7ECFEF3 -:1030BC0010BD10B504460020FDF7F6FE40EA0401F1 -:1030CC000020FDF7E1FE10BD1FB501A8FEF759F871 -:1030DC000723029301A803230393FDF7B1FD684670 -:1030EC00FEF755F869460020FDF7EEFD05B05DF8DA -:1030FC0004FB70B50646FEF79FFC8020FEF716FC1D -:10310C00A82420BA90FAA0F0C0B2FEF70FFC11254B -:10311C00705DFEF70BFC15F1FF35F9D20020FEF7C0 -:10312C0005FC013C06F11206EBD12046FEF7FEFB36 -:10313C00FEF766FC70BD38B500242546E0B2FEF7FC -:10314C0029FEA04001340543042CEDB2F6D12846EB -:10315C0038BD08B50D20FEF7D3FB0A20FEF7D0FBD7 -:10316C0008BD10B5441E14F8010F10B1FEF7C8FBD2 -:10317C00F9E710BD08B5FFF7F4FFFFF7EAFF08BD4C -:10318C001FB530238DF8043078238DF805300723D4 -:10319C0099000F228A400240CA40092A01D83032D5 -:1031AC0002E00F2A02D85732D2B200E0202201A945 -:1031BC00CC1A13F1FF336272EAD2002308468DF861 -:1031CC000E30FFF7CEFF04B010BD70B504460D46AF -:1031DC004FF4805001211646FDF7AEFCFEF7F2FDD0 -:1031EC000520FEF7DDFEA920FEF7DAFEC307FAD4B0 -:1031FC00FEF7BEFDFEF7E6FD0320FEF7D1FEC5F39C -:10320C000740FEF7CDFEC5F30720FEF7C9FEE8B276 -:10321C00FEF7C6FE2644B44205D0A920FEF7C0FE38 -:10322C0004F8010BF7E7FEF7A3FD4FF480500021E3 -:10323C00FDF782FC70BD0000000000001F000000C4 -:10324C003B0000005A0000007800000097000000CE -:10325C00B5000000D4000000F300000011010000D4 -:10326C00300100004E010000000000080040000882 -:10327C000080000800C000080000010800000208DF -:10328C000000040800000608873F00080008024000 -:10329C00080000008C3F0008000002400400000001 -:1032AC008F3F00080008024040000000963F0008D5 -:1032BC00000002400200000000000240010000007B -:1032CC000000080010001800200028003000380012 -:1032DC000000068301000000000000000000000058 -:1032EC00068301000000000C0000000000000683B3 -:1032FC0001000000000C00000000D878369B79C05B -:10330C00E3D90C8C67DB3C1BF8FD7EBFFDE0F7FBC3 -:10331C001FC6EFFB7E1F98CD66B3CD60369B19C6DA -:10332C006C18660398FD66B3FD60309B19C66F1868 -:10333C00660398FD66B3FD60309B19C36F18660376 -:10334C00980D66B30D60369B19C360186603F8FDC3 -:10335C007EBFFDECF79B19C36F187E03D878369BA4 -:10336C0079CCE399998167183C03180000000000A0 -:10337C0000008001000000001800000000000000A8 -:10338C000000000000001800000000000000000019 -:10339C0000000000000C000600000000600030007F -:1033AC000018000300000000C0001800003080016D -:1033BC000000000080010C000060C0000000000054 -:1033CC000003060000C060000000000000060300BF -:1033DC000080310000000000008C010000001B0088 -:1033EC000000000000D8000000000E0000000000EB -:1033FC000070000000000E000000000000700000D3 -:10340C0000001B000000000000D80000008031000C -:10341C0000000000008C010000C0600000000000F3 -:10342C00000603000060C00000000000000306005E -:10343C00003080010000000080010C000018000327 -:10344C0000000000C0001800000C00060000000086 -:10345C0060003000000000000000000000000000D0 -:10346C000000000000000000000000000000000050 -:10347C000000000000000000000000000000000040 -:10348C000000000000000000000000000000000030 -:10349C000000000000000000000000000000000020 -:1034AC000000000000000000000000000000000010 -:1034BC00000000000000000080FFFF010000000081 -:1034CC0000000000FEFFFF7F00000000000000E095 -:1034DC007F0000FE07000000000000FE01000080DD -:1034EC007F0000000000801F00000000F8010000B9 -:1034FC000000F00100000000800F000000007C00C4 -:10350C0000000000003E000000000F000000000062 -:10351C0000F00000008003000000000000C001006B -:10352C0000E0000000000000000007000070000038 -:10353C000000000000000E00001800000000000059 -:10354C0000001800000C000000000000000030001B -:10355C0000060000000000000000600000030000F6 -:10356C00000000000000C00080010000000000000E -:10357C0000008001C00000000000000000000003FB -:10358C006000000000000000000000063000000099 -:10359C00000000000000000C100000000000000003 -:1035AC00000000080000000000600000030018008C -:1035BC000000000000000060000003001800000084 -:1035CC000000000000600000030018000000000074 -:1035DC000000006000000300180000000000000064 -:1035EC000060000003001800000000E003001F60F2 -:1035FC00F800C30718C0070000F80FC07F60FE0377 -:10360C00F31F18F01F00000C1860C06003061B307D -:10361C00181830000006303080E1010C0F60180CD7 -:10362C0060000003601800E3001807C01806C00013 -:10363C008001400C006200100380180380008001A0 -:10364C00C00C006600300380190380018001C0FCAF -:10365C00FF670030038019FFFF018001C0FCFF638E -:10366C000030038019FFFF008001C00C00600030A7 -:10367C000380190300008001C00C0060003003803F -:10368C00190300008001400C006200100380180335 -:10369C0080008003601800C3001806C01806C00024 -:1036AC00800730308081010C0C60180C6000800D9C -:1036BC001860C000030618303818300080F90FC0AD -:1036CC007F00FE03F01F70F01F0080E103001F005D -:1036DC00F800C00760C00700800100000000000077 -:1036EC00000000000000800100000000000000004D -:1036FC00000000008001000000000000000000003D -:10370C00000080010000000000000000000000002C -:10371C0080010000000000000000000000004E6F5F -:10372C002048572056657273696F6E20696E204F62 -:10373C005450004242322E30007573622077616B18 -:10374C0065757020737570706F7274656400456E6A -:10375C00746572696E67207374616E64627900209F -:10376C006973206F757473696465207379737465FC -:10377C006D20666C61736800484152442046415587 -:10378C004C5400446573636C656E20000A46697284 -:10379C006D6C656E20000A5873756D20000A496EB9 -:1037AC0076616C6964206669726D776172652064FC -:1037BC0065736372697074696F6E21004368656329 -:1037CC006B73756D6D696E67206669726D7761726A -:1037DC00652075706461746500496E76616C69640E -:1037EC00206669726D776172652043524320696E61 -:1037FC002053504920666C617368210065726173B7 -:10380C00655F6F6C645F6669726D77617265004F9E -:10381C006C6420576F726C64206669726D7761728C -:10382C00652062617365006661696C656420746F04 -:10383C0020657261736520736563746F7220007705 -:10384C00726974655F6E65775F6669726D776172B8 -:10385C0065006661696C656420746F2077726974A9 -:10386C006520616464726573732000576527726507 -:10387C00206465616400436865636B73756D6D6985 -:10388C006E672000206279746573004E657720574F -:10389C006F726C64206669726D77617265207379E2 -:1038AC007374656D5F666C6173685F6261736500EC -:1038BC004F6C6420576F726C64206669726D77610F -:1038CC0072652073797374656D5F666C6173685F84 -:1038DC006261736500436865636B73756D202D20A1 -:1038EC0077616E746564200020676F7420004F75DB -:1038FC007220696E7465726E616C20666C6173689F -:10390C0020636F6E74656E747320617265206261E2 -:10391C00642028636865636B73756D206661696CE0 -:10392C00656429212054686973206973207265616C -:10393C006C6C79206261642100FFFFFFFFFFFFFFC9 -:10394C00FFFFFFFFFF01000000000000000000006F -:10395C008001000000000000000000008001000059 -:10396C0000000000000000008001000000000000CA -:10397C0000000000800100000000000000000000BA -:10398C0080010000000000000000000080FFFFFF2D -:10399C00FFFFFFFFFFFFFFFFFF7C00FE00FF01C7E3 -:1039AC0001C701C701C701C701C701C701C701C7CB -:1039BC0001C701C701C701FF01FE007C0038003CB4 -:1039CC00003E003E0038003800380038003800381F -:1039DC000038003800380038003800FE00FE00FEC9 -:1039EC00007C00FE00FF01C701C701C001C001E05F -:1039FC0000F00078003C001E000E000F000700FFD6 -:103A0C0001FF01FF017C00FE00FF01C701C701C0DF -:103A1C0001C001F8007800F800C001C001C001C766 -:103A2C0001C701FF01FE007C00E000E000F000F0A7 -:103A3C0000F800F800F800FC00EC00EE00E600FFD7 -:103A4C0001FF01FF01E000E000E000E000FF00FFEB -:103A5C0000FF000700070007007F00FF00FF01C701 -:103A6C0001C001C001C701C701C701FF01FE007CF5 -:103A7C00007C00FE00FF01C701C7010700070077AB -:103A8C0000FF00FF01C701C701C701C701C701FF44 -:103A9C0001FE007C00FF01FF01FF01E000E000706F -:103AAC00007000700038003800380038001C001C12 -:103ABC00001C001C001C001C007C00FE00FF01C749 -:103ACC0001C701C701C701FE007C00FE00C701C78A -:103ADC0001C701C701C701FF01FE007C007C00FE8D -:103AEC0000FF01C701C701C701C701C701FF01FEE4 -:103AFC0001DC01C001C001C701C701FF01FE007C50 -:103B0C000000000000000000007C00FE00FF01C768 -:103B1C0001C701F001FC01CE01C701C701E701FF9C -:103B2C0001DF01CE010700070007000700E700F7DF -:103B3C0001FF01CF01C701C701C701C701C701C7F9 -:103B4C0001CF01FF01F701E70000000000000000B9 -:103B5C00007C00FE00FF01C701C70107000700073A -:103B6C00000700C701C701FF01FE007C00C001C0B7 -:103B7C0001C001C001CE01DF01FF01E701C701C790 -:103B8C0001C701C701C701C701E701FF01DF01CE72 -:103B9C000100000000000000007C00FE00FF01C7D7 -:103BAC0001C701C701FF01FF010700C701C701FFE2 -:103BBC0001FE007C00E000F000F8003800FE00FE82 -:103BCC0000FE003800380038003800380038003863 -:103BDC000038003800380038005341442057415415 -:103BEC0043483A20005265736574205265676973C7 -:103BFC00746572200042726F776E206F757420723C -:103C0C0065736574005374617274696E67204C53EC -:103C1C0045206F7363696C6C61746F72004C534513 -:103C2C00206F7363696C6C61746F722064696420BB -:103C3C006E6F7420737461727400555342207761F7 -:103C4C006B657570006C656176696E672073746165 -:103C5C006E64627900205F5F5F5F5F5F20202020D1 -:103C6C005F5F0D0A2F5F20205F5F2F205F5F2F208B -:103C7C002F0D0A202F202F2020202F5F20205F5F68 -:103C8C002F0D0A2F5F2F20202020202F5F2F0D0AB1 -:103C9C0000426F6F746C6F6164657220766572732D -:103CAC00696F6E3A20004C617374206669726D778F -:103CBC0061726520626F6F74207761732073746119 -:103CCC00626C653B20636C65617220737472696B06 -:103CDC00657300537475636B20627574746F6E201A -:103CEC00726567697374657220697320696E766199 -:103CFC006C69642C20636C656172696E672E00427E -:103D0C007574746F6E206964200069732073747508 -:103D1C00636B2100427574746F6E20776173207031 -:103D2C007573686564206F6E20626F6F742E20420D -:103D3C007574746F6E20636F756E7465723A2000C3 -:103D4C00426F6F7420626974733A2000486F6C6420 -:103D5C0020646F776E205550202B204241434B201E -:103D6C00666F72203520736563732E20746F206626 -:103D7C006F7263652D626F6F7420505246004669F6 -:103D8C00726D776172652069732065726173656409 -:103D9C0000426F6F74696E67206E6F726D616C6C30 -:103DAC0079005761746368646F672063617573652C -:103DBC0064206120726573657400536F667477615B -:103DCC007265206661696C757265206361757365D7 -:103DDC0064206120726573657400426F6F74206695 -:103DEC0061696C65642C20737472696B6520330097 -:103DFC00426F6F74206661696C65642C20737472F9 -:103E0C00696B65203200426F6F74206661696C6566 -:103E1C00642C20737472696B652031004C6F616483 -:103E2C00696E67207265636F766572792066697258 -:103E3C006D77617265004661696C656420746F20F2 -:103E4C006C6F6164207265636F7665727920666948 -:103E5C00726D776172652C20737472696B65206F5B -:103E6C006E652E2054727920616761696E2E004652 -:103E7C0061696C656420746F206C6F61642072657D -:103E8C00636F76657279206669726D776172652CE5 -:103E9C0020737472696B652074776F2E205472795D -:103EAC0020616761696E2E004661696C65642074DF -:103EBC006F206C6F6164207265636F766572792018 -:103ECC006669726D776172652C20737472696B65AB -:103EDC002074687265652E20534144205741544329 -:103EEC0048004F75722070726576696F7573206625 -:103EFC0069726D7761726520757064617465206696 -:103F0C0061696C65642C2061626F7274696E6720E4 -:103F1C007570646174652E004E6577206669726DEC -:103F2C007761726520697320617661696C61626C7E -:103F3C00652100426F6F74696E67206669726D77D8 -:103F4C006172652040200072657475726E696E67CF -:103F5C0020746F207374616E64627900466F7263B3 -:103F6C00652D626F6F74696E67207265636F76651D -:103F7C007279206D6F64652E2E2E004261636B008A -:103F8C0055700053656C65637400446F776E003038 -:103F9C0031323334353637383941424344454600A3 -:103FAC0030313233343536373839616263646566A3 -:103FBC0000286E756C6C29000000000001020304DF -:083FCC000102030406070809C5 -:103FD40000A0000002020000FFFFFFFF00C0040178 -:103FE40000000000000000021000000007000000B4 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/nowatchdog_boot_ev2_4@1447134832.bin b/bin/boot/nowatchdog_boot_ev2_4@1447134832.bin deleted file mode 100755 index 64d5ee48fa..0000000000 Binary files a/bin/boot/nowatchdog_boot_ev2_4@1447134832.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_ev2_4@1447134832.hex b/bin/boot/nowatchdog_boot_ev2_4@1447134832.hex deleted file mode 100644 index cf70359005..0000000000 --- a/bin/boot/nowatchdog_boot_ev2_4@1447134832.hex +++ /dev/null @@ -1,1053 +0,0 @@ -:020000040800F2 -:1000000048200120512C0008B12F0008B12F000812 -:10001000B12F0008B12F0008B12F00080000000028 -:10002000000000000000000000000000B12F0008E8 -:10003000B12F000800000000B12F0008B12F000808 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0A4FD58 -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:0801600091FD05B05DF804FB00 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:080188007DFD05B05DF804FBEC -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F04EFCDDF820C044 -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F0EFFB089B9CE0BA4287 -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00393F00084B45F7DBEDE70D99079107984B -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01403F000861 -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C0010B5044608484021FFF794FC0028F9D094 -:10191C0005482146FFF78AFC03484021FFF78AFC63 -:10192C000028F9D010BD00BF00480040014608B5A2 -:10193C000448FFF767FC03480221FFF765FC002809 -:10194C00F9D008BD0038004000B9074803680133DE -:10195C0008D04368013305D0C069044BC31A584200 -:10196C005841704700207047004000086F57654E83 -:10197C00074B1A69C2F306427F2A07D11869C0F3D4 -:10198C000C00B0F1FF0358425841704700207047DB -:10199C000028004000F13F4000F57E00800A082836 -:1019AC000FD8084A52F820305BB1013B42F8203086 -:1019BC003BB9012101FA00F0034B1A6B22EA00003B -:1019CC0018637047200000200038024000F13F40AF -:1019DC0000F57E00800A08280DD8074A52F82030FE -:1019EC00591C42F8201033B9012101FA00F0034BC5 -:1019FC001A6B104318637047200000200038024017 -:101A0C0010B50C4C2046FFF7E1FF072001F00CFB52 -:101A1C004FF4805100222046FFF786FB042001F092 -:101A2C0003FB2046FFF7B6FF4FF480400021FFF781 -:101A3C0077F810BD0004024010B50B4C01214FF497 -:101A4C008040FFF76DF82046FFF7C0FF20464FF4AB -:101A5C0080510122FFF768FB072001F0E5FA2046D0 -:101A6C00FFF798FF10BD00BF0004024008B5FFF758 -:101A7C00E3FF0020FFF75AFF0020FFF757FF00207D -:101A8C00FFF754FFFFF7BCFFFEF772FEBFF34F8F5B -:101A9C00044A054BD16801F4E0610B43D360BFF3FA -:101AAC004F8FFEE700ED00E00400FA05F0B55E256F -:101ABC006843204BB0FBF1F51A68ADF6DC3D95425E -:101ACC0034D002AC00214FF43D6220461D60FEF77D -:101ADC00BEFA1B238DE818001748102140226923F9 -:101AEC0001F085FA08238DE81800144860231821AA -:101AFC006A2201F07CFA0DF2FA760DF28E73002256 -:101B0C00AA420DD002F11900C0B2C108012700F0A1 -:101B1C00070007FA00F05F5C013238435854EFE7D6 -:101B2C001233B342EBD1204601F0B9FA0DF6DC3D8D -:101B3C00F0BD00BF000000209834000812360008E9 -:101B4C002DE9F043ADF6DC3D02AC00214FF43D62D3 -:101B5C0080462046FEF77BFA2C238DE818002022C5 -:101B6C00284818215C2301F042FA1C2215460F2745 -:101B7C00974007EA0807D740234B242101FB07377E -:101B8C000DF50366002117F801C00023082B07D0C0 -:101B9C000120984010EA0C0F0CBF0020012003E03C -:101BAC007818407800F0010078B15819C0B24FEAAB -:101BBC00D00E4FF0010900F0070009FA00F016F8FA -:101BCC000E9040EA090006F80E000133092BDDD116 -:101BDC000231242906F11206D5D10B35742DA2F150 -:101BEC000402C4D10E238DE81800102190226D231D -:101BFC00064801F0FCF9204601F051FA0DF6DC3DE7 -:101C0C00BDE8F083883200085D3800089D3A000872 -:101C1C0010B5ADF6D83D02AC204600214FF43D6224 -:101C2C00FEF715FA1B238DE81800102140226923BA -:101C3C00044801F0DCF9204601F031FA0DF6D83DEC -:101C4C0010BD00BF983400082DE9F74F434E444FA8 -:101C5C0001214FF48040FEF763FF3046FFF7B6FEDC -:101C6C003846404CFFF7B2FE30460D210522FFF7F7 -:101C7C0060FA052230460F21FFF75BFA3046214609 -:101C8C000125FFF7FEF920233846214623602571F4 -:101C9C00A5714FF0000A01934FF4805BFFF7F1F947 -:101CAC00304621464FF002084FF48049257184F8E4 -:101CBC0006A0C4F800B0FFF7E4F93046214684F8DA -:101CCC000480C4F80080FFF7DCF921462571A5716A -:101CDC00C4F80090244C3046FFF7D3F92046FFF7A8 -:101CEC0039F920462149FFF75FFA20462946FFF7CC -:101CFC007AFA019B384619465246FFF715FA2A46DE -:101D0C0030464946FFF710FA40462946FEF708FFD1 -:101D1C00A4F5505480232380F92323854FF4F042FB -:101D2C004FF6FF13E3623046A283294623644246F2 -:101D3C00A4F820B0FFF7FDF929462046A582FEF74E -:101D4C001FFD3846FFF726FE3046FFF723FE4846B8 -:101D5C005146FEF7E5FE03B0BDE8F08F00040240EB -:101D6C000008024004000020003800400C00002055 -:101D7C0010B5074C2046FFF729FE20461021FFF72F -:101D8C00CFF92046FFF706FE012001F04DF910BDFA -:101D9C000000024038B50A4B0C2404FB0034606888 -:101DAC00FFF714FE21896068FFF7B4F905466068F7 -:101DBC00FFF7F0FDD5F1010038BF002038BD00BFA2 -:101DCC004032000810B5064C2046FFF7FFFD2046B8 -:101DDC001021FFF7A7F92046FFF7DCFD10BD00BF6F -:101DEC0000000240F0B587B0FFF7C2FD002841D1DA -:101DFC000320FFF759F807460420FFF755F8374B37 -:101E0C000A241A68C2F30545C2F30626C2F302136C -:101E1C0002F00F0204FB0322330906F00F0604FB49 -:101E2C0003662B0905F00F0504FB03533C2505FB4A -:101E3C00036305FB0323191A44BF01F5A83101F50F -:101E4C00C07107EB91210320FFF71EF803A8FFF7E1 -:101E5C0098F903A8FEF7F4FE01A8FFF798F901A979 -:101E6C000020FEF731FF02A8FFF797F9002002A926 -:101E7C00FEF794FF0A2442F2107501F032F910B902 -:101E8C00013DFAD107E0164D01F02BF908B1013DE7 -:101E9C00FAD1013CEFD10020FEF792FD114C00214C -:101EAC004FF20400FFF71CF84FF43C50FFF744F8D6 -:101EBC002368012043F004032360FEF781FD0A48E8 -:101ECC0001F02EF9236843F002032360074B1A69D3 -:101EDC0042F004021A6130BF07B0F0BD0028004088 -:101EEC00A0860100007000407236000800ED00E092 -:101EFC00B0F1006F10B504460BD20D4801F007F994 -:101F0C00204601F013F90B4801F00AF94FF0FF30AD -:101F1C0010BD094A002352F8041FA14202D851688F -:101F2C008C4202D30133072BF5D1184610BD00BFEC -:101F3C0086370008833600081C32000808B50248B2 -:101F4C0001F0EEF800BEFEE79C36000810B5044622 -:101F5C000A480221FFF758F90028F9D00748214612 -:101F6C00FFF750F905480121FFF74EF90028F9D089 -:101F7C000248FFF744F9C0B210BD00BF0030014069 -:101F8C002DE9F04F87B00121814600AF0020FFF70B -:101F9C008DFD042001F063F807F10C0049460C227A -:101FAC0001F0E9F8FD683C690C2DD7F814B013D09A -:101FBC00994801F0ACF8284601F0B8F8974801F0C0 -:101FCC00A6F8204601F0B2F8954801F0A0F8584662 -:101FDC0001F0ACF8934833E0934801F0A1F84FF4CA -:101FEC0080500121FEF790FD09F10C09EA46FEF73D -:101FFC0009FCA0B06E462546C846802D0CD941463A -:10200C003046802201F0B7F830462021FEF706FC5E -:10201C00803D08F18008F0E725B1304641462A465C -:10202C0001F0A9F82946304600F0C6FF0021804691 -:10203C004FF48050FEF768FDD845D54604D07B4858 -:10204C0001F06EF80120E3E088B068464946202292 -:10205C0001F091F86846FFF777FC78607448D54634 -:10206C0001F05EF87B682BB9724801F059F804F561 -:10207C00404600E02646002E3BD06F48FFF738FF65 -:10208C0005466E483044FFF733FF002D824630DBA7 -:10209C0000282EDB431B01334FEA430B002059462B -:1020AC00FFF704FDFEF7DAFB654B002603EB450357 -:1020BC003B6055451BDCF320FEF7E6FB3B6800213B -:1020CC0033F81600FEF700FC082806F1010608D0CC -:1020DC005C4801F01CF8284601F028F801F00FF8D4 -:1020EC0005E030465946FFF7E1FC0135E1E7FEF724 -:1020FC00C3FB554801F014F87B680BB14E4B00E064 -:10210C00524B7B6063083B600025A5423CD2661BAA -:10211C00B6F5803F28BF4FF480362EB14C4805EB06 -:10212C000901324601F027F8D7F804A0FEF796FB18 -:10213C00F320FEF7A9FBAA444FF0000BB34519D0CE -:10214C0043490BEB0A0011F80B10FEF7F5FB0828BE -:10215C000DD0404800F0DBFF504600F0E7FF00F0E8 -:10216C00CEFFFEF789FB3C4800F0DAFF0CE00BF1E8 -:10217C00010BE3E7FEF780FB3B68214603EB5500C0 -:10218C00FFF794FC5D44C0E7344800F0C0FF2046E4 -:10219C0000F0CCFF324800F0C3FF0020FFF7D4FB67 -:1021AC0020B1304800F0BCFF234D03E02E4800F076 -:1021BC00B7FF264D4FF480500121A608FEF7A4FC72 -:1021CC00FEF720FB31462846FEF728FB04F00301FE -:1021DC0005EB860000F0F0FE002104464FF4805021 -:1021EC00FEF792FC214800F092FF404600F09EFF63 -:1021FC001F4800F08CFF204600F098FF00F07FFF96 -:10220C00444504D01B4800F08BFF022000E0002066 -:10221C001C37BD46BDE8F08FA7360008B036000865 -:10222C00BA360008C1360008E0360008FD36000852 -:10223C00203700083337000800400008FF3F000833 -:10224C00783200084B37000863370008000001089B -:10225C0044000020763700088F3700089A370008B2 -:10226C00A8370008AF370008D4370008F937000842 -:10227C000C3800081238000870B54FF4805001215A -:10228C00FEF75AFCFFF79EFD0520164CFFF75EFE8D -:10229C00002C04F1FF341FDCFFF76AFDFFF792FD01 -:1022AC009F20FFF753FEA920FFF750FE0546A920FB -:1022BC00FFF74CFE0646A920FFF748FE36020446FF -:1022CC0046EA0545FFF754FD4FF480500021FEF718 -:1022DC0033FC45EA040070BDA920FFF737FEC307A5 -:1022EC00D6D4D9E70090D00308B50446114800F0C5 -:1022FC000EFF204600F01AFF00F001FF2046FFF70A -:10230C001FFC00F0EEFE0C4B20F0040018701D4674 -:10231C0000F0E7FE2B7800F0FB00834201D10B2488 -:10232C0001E0FFF7A3FB013CF2D04FF47A7000F010 -:10233C007BFEF8E7613B00084400012080B5464B6A -:10234C004FF4C06290B01A6001214FF08050FEF73C -:10235C00E7FB0120FEF72EFB4048FFF737FB4FF45D -:10236C0080200121FEF7DCFB3C480A210722FEF706 -:10237C00E0FE072239480B21FEF7DBFE10AF4FF4CD -:10238C00806347F8383D00244FF00108022533489C -:10239C0039468DF80E408DF80F808DF80C508DF865 -:1023AC000D50FEF76EFE4FF400632C483946029335 -:1023BC008DF80C5004AEFEF764FE4FF461330493B9 -:1023CC0027480C233146ADF81A30ADF81440ADF85F -:1023DC001640ADF81840ADF81C40FEF7E9FD41463B -:1023EC001F48FEF717FF1D48FFF7D4FA1D4800F0F1 -:1023FC008EFE1D4B586F00F099FE00F080FE792088 -:10240C00FEF7E2FB404502D1184800F089FE01209E -:10241C0000F010FE10B90121FEF736FD4120FEF749 -:10242C00D3FB58BB124800F07BFE0120FEF7FAFAF2 -:10243C0047F230544120FEF7C7FBF8B94FF47A70DD -:10244C0000F0F2FD013CF5D10A4800F069FEFFF7FF -:10245C00FBFB094837E100BF003C0240000802408A -:10246C00004800406D3B0008003802407D3B0008EE -:10247C008D3B0008A53B0008114550FE4FF48070C1 -:10248C00FEF724FB0120FEF739FBFEF7FBFB0020D7 -:10249C00FEF796FA01214FF48040FEF74DFBBB4846 -:1024AC00FFF794FA3046FEF72CFE00240125B748BE -:1024BC0031468DF815408DF816408DF8174004956F -:1024CC008DF81450FEF7DDFD2246B0482946FEF784 -:1024DC002BFEAE48FFF75EFAAD4C04F1300954F810 -:1024EC00040CFFF773FA3046FEF70BFE236854F822 -:1024FC00040C4FF000083146022504938DF814802B -:10250C008DF815508DF817800C34FEF7BAFD54F881 -:10251C00100CFFF73FFA4C45E1D14FF48040414697 -:10252C00FEF70AFB4FF4807000F084FD002800F0E9 -:10253C00CC804FF4807000F087FDFFF753FC95487A -:10254C0000F0EEFD0220FEF7AFFC9349884202D06A -:10255C000220FEF799FC914800F0D9FD0220FEF70D -:10256C00A3FC00F0E3FD00F0CAFD4FF4804000F046 -:10257C0061FD90B18A4800F0D3FD4FF4804000F02B -:10258C0063FD082000F060FD102000F05DFD2020B0 -:10259C0000F05AFD402000F057FDFFF755FBFFF708 -:1025AC0037FB01214FF48050FEF7C6FA7748FFF74E -:1025BC000DFA01200146FEF7A7FA05210A467348D9 -:1025CC00FEF7B7FD714806210522FEF7B2FD052284 -:1025DC006E480721FEF7ADFD00240225C0236B4891 -:1025EC0039468DF80C508DF80D5002938DF80E4035 -:1025FC008DF80F40FEF745FD2023644839468DF8D1 -:10260C000F5002930125FEF73CFD102339465F481D -:10261C0002938DF80C508DF80F50FEF732FD614887 -:10262C00FEF798FC4FF48273ADF812304FF4007340 -:10263C00ADF81A305B4807233146ADF82030ADF8C1 -:10264C001040ADF81440ADF81640ADF81840ADF898 -:10265C001C40ADF81E40FEF7A7FD29465148FEF779 -:10266C00C2FD4A48FFF796F9FFF7ACFBAB20FFF72A -:10267C006DFCFFF77DFB642000F0D6FC21464FF487 -:10268C008050FEF759FAFFF7F7FD2846FEF70CFCD1 -:10269C0025460490E8B2FFF77DFB98BB70550135D9 -:1026AC00042DF7D1049D3DB13F4800F030FD284684 -:1026BC0000F03CFD00F023FD01202946FEF7E4FB71 -:1026CC00002C3DD03948FFF70FFE2846FEF77EF967 -:1026DC00012814D12846FEF783F94FF40060FEF769 -:1026EC001DFC18B1324800F01BFDB9E0314C00F074 -:1026FC00F8FC002800F00481013CF8D1B0E000F0B7 -:10270C00B9FC1CE7735D042B0ADD2B4800F008FDB7 -:10271C00049800F00BFD01200021FEF7B5FB0FE043 -:10272C000133DBB2042B7355B9DD244800F0EFFC08 -:10273C00284600F0FBFC224800F0F2FC0124AEE736 -:10274C00FFF79AFD1F4B0344012B01D91E48BAE732 -:10275C001E4800F0DCFC0020FEF7A6FB00F0E6FCB7 -:10276C0000F0CDFC802000F065FC18B1802000F05A -:10277C006BFC73E04FF4003000F05CFC044638B3A3 -:10278C004FF4003000F060FC04E100BF0000024098 -:10279C0048320008DD3B000870864156193C0008A1 -:1027AC002E3C0008003001409C3C0008014550FEC6 -:1027BC00C23B0008A08601005B3C0008873C000877 -:1027CC00923C0008EA44DFFF024550FEC83C00087A -:1027DC000120FFF7DFFAD8B12046FFF7DBFAB8B1DA -:1027EC00B74800F09DFC41F288340120FFF7D2FA83 -:1027FC00002800F08B800020FFF7CCFA002800F0B6 -:10280C0085804FF47A7000F00FFC013CEDD1C1E0F3 -:10281C000020FFF799F808B1AA4B00E0AA4B0CCBAB -:10282C00013301D0013271D1A84800F079FCB1E03C -:10283C00A74800F075FC082000F006FC102000F002 -:10284C0003FC202000F000FC402000F0FDFB0024E5 -:10285C00042000F0F9FB002C40F008819D4832E781 -:10286C00FFF786F8002847D0002001A99A4DFEF703 -:10287C0073FA344600203946FEF7D2FA0FCD0FC456 -:10288C000FCD0FC495E80F0084E80F009DF80920C8 -:10289C0010A901EB82039DF80BE053F8343C40F295 -:1028AC006D1101FB0E3303EB9E030EF1010E1EF0B6 -:1028BC00030F02D1022A88BF01339DF80A409DF80C -:1028CC000410013C2344182404FB03149DF8051048 -:1028DC003C2202FB04149DF80610032002FB041496 -:1028EC00FEF7E2FA01190320FEF7CEFA04200021CC -:1028FC00FEF7CAFA00F0BEFB784800F011FC1EE6A9 -:10290C00774800F00DFCFFF76DFAF5E77D20FEF738 -:10291C005BF970B94FF4007000F08CFB48B971484A -:10292C0000F0FEFB082000F08FFB102000F08CFB69 -:10293C0085E07D20FEF748F910B16B4800F0F0FB04 -:10294C004FF4007000F076FB10B1684800F0E8FB23 -:10295C004FF4007000F078FB102000F06BFB48B1D6 -:10296C00634800F0DDFB082000F06EFB102000F047 -:10297C006BFB12E0082000F05DFB20B15D4800F01D -:10298C00CFFB102003E05C4800F0CAFB082000F0ED -:10299C0066FB54E0594800F0C3FB594800F0C0FBFB -:1029AC004FF40010FFF7ECFA10B1022828D809E018 -:1029BC00202000F049FB402000F046FB802000F076 -:1029CC004EFB1DE0082000F04AFB102000F047FBF6 -:1029DC00202000F02FFB20B94A4800F0A1FB20205A -:1029EC0009E0402000F026FB00287FF421AF464888 -:1029FC0000F096FB402000F032FB4FF400701AE020 -:102A0C00012425E7414800F08BFB082000F01CFB5B -:102A1C00102000F019FB202000F016FB402000F0E5 -:102A2C0013FB2046FFF7ACFA022840F0F080082098 -:102A3C0000F015FB102000F012FBFFF717F8022036 -:102A4C0000F0F8FA90B1042000F0F4FA04460028E3 -:102A5C00D8D02F4800F064FB042000F0F5FA0220D7 -:102A6C0000F0F2FA4FF4004000F0EEFA4FF4005090 -:102A7C0000F0E0FA04464FF4805000F0DBFA40001E -:102A8C0040EA84044FF4006000F0D4FAE4B220432E -:102A9C00431C03F0FF03013B4FF40060062B58D896 -:102AAC00DFE803F03C383C043C383C0000F0CCFA46 -:102ABC004FF4805000F0C8FA4FF400502EE000BFE5 -:102ACC00D43C00080040000800000108063D000846 -:102ADC00303E0008034550FEF0310008CD3B0008A5 -:102AEC00CF3E0008193D00082A3D0008423D000871 -:102AFC00623D0008783D00088E3D0008E43E000869 -:102B0C00A43D0008BE3D0008F73D0008A03E0008AB -:102B1C006A3E000800F098FA4FF4805000F09FFADB -:102B2C0045F25550FDF724FF0420FDF727FF40F632 -:102B3C00FF70FDF729FF0020FDF71AFF4FF48050BE -:102B4C000121FDF77FFE0020FEF7FEFE68B9354B34 -:102B5C000CE000F079FA4FF4805000F075FA4FF465 -:102B6C00005000F071FA3048ADE5304B5D683048EC -:102B7C001E6800F0CCFA284600F0D8FA00F0BFFA34 -:102B8C0000F0BDFA00239A0002F16042013302F515 -:102B9C0061424FF0FF31082BC2F88010C2F880114F -:102BAC00F1D1244B00241C635C639C631C645C6447 -:102BBC00FDF71EFF20480121FDF7CAFF21461E48E4 -:102BCC00FDF7C6FFF1200121FDF7CEFF2146F120D4 -:102BDC00FDF7CAFF01200146FDF7D2FF2146012077 -:102BEC00FDF7CEFF15480121FDF7D6FF214613480E -:102BFC00FDF7D2FF12480121FDF7DAFF10482146FC -:102C0C00FDF7D6FF63B64FF0FF3EB54628470420CC -:102C1C0000F01AFA022000F017FA4FF4004000F00E -:102C2C001EFA23E700000108044550FE004000088E -:102C3C00BB3E00080038024000106022FFC9FE367F -:102C4C00335907000748084A08B50849121AFDF716 -:102C5C00F5F90748074A0021121AFDF7F8F9FFF7B2 -:102C6C006DFB00BF0000002020000020503F00083A -:102C7C0020000020480001202DE9F04FDDE909ABD0 -:102C8C000E469C4603783B490746C95C01300D074C -:102C9C00F8D42B2B05D02D2B04D107464FF0FF3346 -:102CAC0001E007460123397830290ED1797878294B -:102CBC0005D132F0100103D10237102207E00AB11E -:102CCC00082A04D10137082201E002B90A220020A7 -:102CDC000021B84617F8015BA5F13004E4B2092CC9 -:102CEC000DD9A5F14104192C02D8A5F1370405E042 -:102CFC00A5F16104192C30D8A5F15704E4B2944223 -:102D0C002BDA4FEAE27900FB09F502FB0155A0FB37 -:102D1C000201E4B2294400194FF0000541EB050112 -:102D2C00BCF1000FD5D0012B06D182457BEB010401 -:102D3C00CFDA50465946CCE75C1CCAD1DDE90B8989 -:102D4C00444261EB4105444575EB0909C1DADDE903 -:102D5C000B01404261EB4101BBE70EB1C6F80080AC -:102D6C00DD1700FB05F203FB0122A0FB030111445C -:102D7C00BDE8F08F4C1200082DE9F04F91B01F9E6A -:102D8C000C46310691468046DDE91A231E9D04D57A -:102D9C00CDE902234FF0000C1BE0002A73F1000771 -:102DAC001046194602DA504263EB4301002A73F1D4 -:102DBC00000ACDE9020107DBB70708D416F0040CB2 -:102DCC0007D04FF0200C04E04FF02D0C01E04FF039 -:102DDC002B0C16F04007089714BF704F704FDDE9AD -:102DEC0002AB079700271C980137C117CDE90401E6 -:102DFC00DDE904230AA93944099150465946CDF816 -:102E0C0004C0FEF79DFA07980999835C504601F8B7 -:102E1C00013C5946DDE90423FEF792FA82468B46C3 -:102E2C005AEA0B02DDF804C001D0162FDBD116F0E4 -:102E3C00080239461DD01C9B082B02D0102B0ED03B -:102E4C001CE0DDE902AB1DB95AEA0B0B13D004E010 -:102E5C005AEA0B0B12D0BD4210DC7B1C0FE0DDE9F3 -:102E6C0002AB5AEA0B0B09D008982B4648B15822F2 -:102E7C0008E02B4606E02A46012303E02B460022FD -:102E8C0000E078221CF1000B1D98C7EB030A18BF59 -:102E9C004FF0010BBB42A8BF1F46C01B2AEAEA7ABF -:102EAC00CBEB00000AB1022700E01746C71B16F057 -:102EBC00010027EAE77701D07F4205E0F60603D54B -:102ECC00013504BFBA4407463D46002D0BDD206892 -:102EDC00013D461C4E453CBF202608F8006020688A -:102EEC0001302060F1E727EAE7703F1ABBF1000FD1 -:102EFC0008D02068451C4D4538BF08F800C0206834 -:102F0C00013020608AB12068451C4D453CBF3025FE -:102F1C0008F800502568681C02354D45206038BF04 -:102F2C0008F80020226801322260BAF1000F0CD0A0 -:102F3C0022680AF1FF3A501C48453CBF302008F883 -:102F4C000200226801322260EFE7DDE902AB5AEAA7 -:102F5C000B0B10D17BB919460DE0236801395A1CB3 -:102F6C004A4505D201F1280568462A5C08F8032079 -:102F7C002368013323600029EFD15FB12368013747 -:102F8C005A1C4A453CBF202208F803202368013311 -:102F9C002360F2E711B0BDE8F08F00BF173F0008C7 -:102FAC00283F00081EF0040F0CBFEFF30880EFF36E -:102FBC000980FEF7C3BF7047032970B505460C4660 -:102FCC0006D98E083146FDF729FCB6003544A41B02 -:102FDC0014B9FDF731FC70BD00231846EA5C0133CF -:102FEC00A34242EA0020F9D1FDF712FC70BD2DE995 -:102FFC00F0410733DE08079B4FF0120808FB023242 -:10300C000731069F054602EBD1080024BC4209D0CB -:10301C00122000FB048029463246FDF70FF80134DC -:10302C003544F3E7BDE8F0810623584301387FF4BB -:10303C00FDAF704710B504460020FDF735FF204268 -:10304C000CBF0020012010BD10B504460020FDF778 -:10305C002BFF20EA04010020FDF716FF10BD10B570 -:10306C0004460020FDF720FF40EA04010020FDF794 -:10307C000BFF10BD1FB501A8FEF783F807230293C1 -:10308C0001A803230393FDF7DBFD6846FEF77FF8E9 -:10309C0069460020FDF718FE05B05DF804FB70B51D -:1030AC000646FEF7C9FC8020FEF740FCA82420BA97 -:1030BC0090FAA0F0C0B2FEF739FC1125705DFEF756 -:1030CC0035FC15F1FF35F9D20020FEF72FFC013C41 -:1030DC0006F11206EBD12046FEF728FCFEF790FC19 -:1030EC0070BD38B500242546E0B2FEF753FEA04073 -:1030FC0001340543042CEDB2F6D1284638BD08B591 -:10310C000D20FEF7FDFB0A20FEF7FAFB08BD10B5FB -:10311C00441E14F8010F10B1FEF7F2FBF9E710BDD5 -:10312C0008B5FFF7F4FFFFF7EAFF08BD1FB5302322 -:10313C008DF8043078238DF80530072399000F2281 -:10314C008A400240CA40092A01D8303202E00F2AD4 -:10315C0002D85732D2B200E0202201A9CC1A13F1C6 -:10316C00FF336272EAD2002308468DF80E30FFF767 -:10317C00CEFF04B010BD70B504460D464FF4805020 -:10318C0001211646FDF7D8FCFEF71CFE0520FEF7C4 -:10319C00DDFEA920FEF7DAFEC307FAD4FEF7E8FD40 -:1031AC00FEF710FE0320FEF7D1FEC5F30740FEF735 -:1031BC00CDFEC5F30720FEF7C9FEE8B2FEF7C6FE4A -:1031CC002644B44205D0A920FEF7C0FE04F8010B3A -:1031DC00F7E7FEF7CDFD4FF480500021FDF7ACFC76 -:1031EC0070BD0000000000001F0000003B0000004C -:1031FC005A0000007800000097000000B5000000A5 -:10320C00D4000000F30000001101000030010000A8 -:10321C004E0100000000000800400008008000087B -:10322C0000C00008000001080000020800000408AB -:10323C0000000608033F00080008024008000000D8 -:10324C00083F000800000240040000000B3F00088B -:10325C000008024040000000123F0008000002403D -:10326C000200000000000240010000000000080005 -:10327C00100018002000280030003800000C000658 -:10328C000000000060003000001800030000000087 -:10329C00C0001800003080010000000080010C000C -:1032AC000060C000000000000003060000C06000C9 -:1032BC000000000000060300008031000000000048 -:1032CC00008C010000001B000000000000D8000072 -:1032DC0000000E00000000000070000000000E0056 -:1032EC00000000000070000000001B000000000047 -:1032FC0000D800000080310000000000008C0100AC -:10330C0000C0600000000000000603000060C00068 -:10331C0000000000000306000030800100000000E7 -:10332C0080010C000018000300000000C000180011 -:10333C00000C0006000000006000300000000000DF -:10334C000000000000000000000000000000000071 -:10335C000000000000000000000000000000000061 -:10336C000000000000000000000000000000000051 -:10337C000000000000000000000000000000000041 -:10338C000000000000000000000000000000000031 -:10339C000000000000000000000000000000000021 -:1033AC0080FFFF010000000000000000FEFFFF7F17 -:1033BC0000000000000000E07F0000FE070000009D -:1033CC00000000FE010000807F0000000000801F54 -:1033DC0000000000F80100000000F00100000000F7 -:1033EC00800F000000007C0000000000003E000088 -:1033FC0000000F000000000000F00000008003003F -:10340C000000000000C0010000E00000000000000F -:10341C0000000700007000000000000000000E001B -:10342C00001800000000000000001800000C000054 -:10343C00000000000000300000060000000000004A -:10344C000000600000030000000000000000C0004D -:10345C00800100000000000000008001C00000009E -:10346C0000000000000000036000000000000000ED -:10347C000000000630000000000000000000000CFE -:10348C001000000000000000000000080000000018 -:10349C000060000003001800000000000000006045 -:1034AC000000030018000000000000000060000095 -:1034BC000300180000000000000000600000030082 -:1034CC00180000000000000000600000030018005D -:1034DC00000000E003001F60F800C30718C00700DD -:1034EC0000F80FC07F60FE03F31F18F01F00000CE4 -:1034FC001860C06003061B3018183000000630300E -:10350C0080E1010C0F60180C60000003601800E3F0 -:10351C00001807C01806C0008001400C00620010A3 -:10352C000380180380008001C00C0066003003800B -:10353C00190380018001C0FCFF670030038019FF74 -:10354C00FF018001C0FCFF630030038019FFFF0006 -:10355C008001C00C00600030038019030000800162 -:10356C00C00C006000300380190300008001400C87 -:10357C00006200100380180380008003601800C3F1 -:10358C00001806C01806C000800730308081010C7E -:10359C000C60180C6000800D1860C0000306183019 -:1035AC003818300080F90FC07F00FE03F01F70F058 -:1035BC001F0080E103001F00F800C00760C0070077 -:1035CC0080010000000000000000000000008001ED -:1035DC00000000000000000000000000800100005E -:1035EC00000000000000000000008001000000004E -:1035FC00000000000000000080010000000000003E -:10360C00000000000000FFFFFFFFFFFFFFFFFFFFB8 -:10361C00FFFF01000000000000000000008001001E -:10362C00000000000000000000800100000000000D -:10363C0000000000008001000000000000000000FD -:10364C00008001000000000000000000008001006C -:10365C0000000000000000000080FFFFFFFFFFFFE4 -:10366C00FFFFFFFFFFFF456E746572696E67207385 -:10367C0074616E64627900206973206F757473696C -:10368C0064652073797374656D20666C6173680072 -:10369C0048415244204641554C5400446573636C78 -:1036AC00656E20000A4669726D6C656E20000A58C2 -:1036BC0073756D20000A496E76616C6964206669C9 -:1036CC00726D776172652064657363726970746979 -:1036DC006F6E2100436865636B73756D6D696E6702 -:1036EC00206669726D7761726520757064617465AE -:1036FC0000496E76616C6964206669726D776172DF -:10370C00652043524320696E2053504920666C61FA -:10371C007368210065726173655F6F6C645F6669C5 -:10372C00726D77617265004F6C6420576F726C64B8 -:10373C00206669726D7761726520626173650066DF -:10374C0061696C656420746F2065726173652073A8 -:10375C006563746F72200077726974655F6E65774C -:10376C005F6669726D77617265006661696C65642C -:10377C0020746F207772697465206164647265735C -:10378C007320005765277265206465616400436887 -:10379C0065636B73756D6D696E672000206279745B -:1037AC006573004E657720576F726C642066697282 -:1037BC006D776172652073797374656D5F666C618A -:1037CC0073685F62617365004F6C6420576F726C35 -:1037DC0064206669726D77617265207379737465A4 -:1037EC006D5F666C6173685F6261736500436865E9 -:1037FC00636B73756D202D2077616E74656420008A -:10380C0020676F7420004F757220696E7465726E3C -:10381C00616C20666C61736820636F6E74656E7486 -:10382C007320617265206261642028636865636B34 -:10383C0073756D206661696C656429212054686913 -:10384C0073206973207265616C6C792062616421EC -:10385C00007C00FE00FF01C701C701C701C701C7FB -:10386C0001C701C701C701C701C701C701C701FFD4 -:10387C0001FE007C0038003C003E003E0038003861 -:10388C00003800380038003800380038003800386C -:10389C00003800FE00FE00FE007C00FE00FF01C7A9 -:1038AC0001C701C001C001E000F00078003C001E1F -:1038BC00000E000F000700FF01FF01FF017C00FE5E -:1038CC0000FF01C701C701C001C001F8007800F872 -:1038DC0000C001C001C001C701C701FF01FE007C8F -:1038EC0000E000E000F000F000F800F800F800FC48 -:1038FC0000EC00EE00E600FF01FF01FF01E000E03C -:10390C0000E000E000FF00FF00FF000700070007D9 -:10391C00007F00FF00FF01C701C001C001C701C744 -:10392C0001C701FF01FE007C007C00FE00FF01C707 -:10393C0001C701070007007700FF00FF01C701C79F -:10394C0001C701C701C701FF01FE007C00FF01FF99 -:10395C0001FF01E000E000700070007000380038DA -:10396C0000380038001C001C001C001C001C001C33 -:10397C00007C00FE00FF01C701C701C701C701FEA3 -:10398C00007C00FE00C701C701C701C701C701FFCA -:10399C0001FE007C007C00FE00FF01C701C701C7CF -:1039AC0001C701C701FF01FE01DC01C001C001C755 -:1039BC0001C701FF01FE007C0000000000000000B8 -:1039CC00007C00FE00FF01C701C701F001FC01CE25 -:1039DC0001C701C701E701FF01DF01CE01070007A5 -:1039EC000007000700E700F701FF01CF01C701C77F -:1039FC0001C701C701C701C701CF01FF01F701E7EB -:103A0C000000000000000000007C00FE00FF01C769 -:103A1C0001C7010700070007000700C701C701FF26 -:103A2C0001FE007C00C001C001C001C001CE01DF5D -:103A3C0001FF01E701C701C701C701C701C701C7E2 -:103A4C0001E701FF01DF01CE0100000000000000D2 -:103A5C00007C00FE00FF01C701C701C701FF01FF89 -:103A6C00010700C701C701FF01FE007C00E000F068 -:103A7C0000F8003800FE00FE00FE00380038003868 -:103A8C00003800380038003800380038003800386A -:103A9C000000000683010000000000000000000090 -:103AAC0000068301000000000C000000000000066E -:103ABC008301000000000C00000000D878369B79D0 -:103ACC00C0E3D90C8C67DB3C1BF8FD7EBFFDE0F737 -:103ADC00FB1FC6EFFB7E1F98CD66B3CD60369B19DE -:103AEC00C66C18660398FD66B3FD60309B19C66FF3 -:103AFC0018660398FD66B3FD60309B19C36F18669A -:103B0C0003980D66B30D60369B19C360186603F8F5 -:103B1C00FD7EBFFDECF79B19C36F187E03D878367A -:103B2C009B79CCE399998167183C0318000000003D -:103B3C0000000080010000000018000000000000E0 -:103B4C000000000000000018000000000000000051 -:103B5C0000000000005341442057415443483A2090 -:103B6C0000526573657420526567697374657220C1 -:103B7C000042726F776E206F757420726573657476 -:103B8C00005374617274696E67204C5345206F73D7 -:103B9C0063696C6C61746F72004C5345206F736376 -:103BAC00696C6C61746F7220646964206E6F742030 -:103BBC007374617274005553422077616B65757034 -:103BCC00006C656176696E67207374616E646279EE -:103BDC0000205F5F5F5F5F5F202020205F5F0D0A2A -:103BEC002F5F20205F5F2F205F5F2F202F0D0A207B -:103BFC002F202F2020202F5F20205F5F2F0D0A2FDA -:103C0C005F2F20202020202F5F2F0D0A00426F6F86 -:103C1C00746C6F616465722076657273696F6E3A4D -:103C2C0020004C617374206669726D776172652037 -:103C3C00626F6F742077617320737461626C653B83 -:103C4C0020636C65617220737472696B65730053C9 -:103C5C007475636B20627574746F6E20726567691E -:103C6C007374657220697320696E76616C69642C5B -:103C7C0020636C656172696E672E00427574746F97 -:103C8C006E2069642000697320737475636B210066 -:103C9C00427574746F6E20776173207075736865EC -:103CAC0064206F6E20626F6F742E20427574746F77 -:103CBC006E20636F756E7465723A2000426F6F747C -:103CCC0020626974733A2000486F6C6420646F77CB -:103CDC006E205550202B204241434B20666F7220A2 -:103CEC003520736563732E20746F20666F72636565 -:103CFC002D626F6F7420505246004669726D776169 -:103D0C0072652069732065726173656400426F6F20 -:103D1C0074696E67206E6F726D616C6C790057619F -:103D2C00746368646F6720636175736564206120D8 -:103D3C00726573657400536F667477617265206683 -:103D4C0061696C75726520636175736564206120AF -:103D5C00726573657400426F6F74206661696C657F -:103D6C00642C20737472696B65203300426F6F741E -:103D7C00206661696C65642C20737472696B6520B4 -:103D8C003200426F6F74206661696C65642C20731D -:103D9C007472696B652031004C6F6164696E6720C9 -:103DAC007265636F76657279206669726D77617280 -:103DBC0065004661696C656420746F206C6F61648A -:103DCC00207265636F76657279206669726D7761B2 -:103DDC0072652C20737472696B65206F6E652E2072 -:103DEC0054727920616761696E2E004661696C6559 -:103DFC006420746F206C6F6164207265636F7665EC -:103E0C007279206669726D776172652C2073747299 -:103E1C00696B652074776F2E20547279206167610D -:103E2C00696E2E004661696C656420746F206C6F3E -:103E3C006164207265636F76657279206669726D54 -:103E4C00776172652C20737472696B65207468726B -:103E5C0065652E20534144205741544348004F750B -:103E6C00722070726576696F7573206669726D77F2 -:103E7C0061726520757064617465206661696C653A -:103E8C00642C2061626F7274696E67207570646156 -:103E9C0074652E004E6577206669726D7761726568 -:103EAC0020697320617661696C61626C65210042E6 -:103EBC006F6F74696E67206669726D7761726520C9 -:103ECC0040200072657475726E696E6720746F2085 -:103EDC007374616E64627900466F7263652D626FF4 -:103EEC006F74696E67207265636F76657279206D89 -:103EFC006F64652E2E2E004261636B00557000536B -:103F0C00656C65637400446F776E00303132333406 -:103F1C003536373839414243444546003031323327 -:103F2C0034353637383961626364656600286E75DE -:103F3C006C6C290000000000010203040102030460 -:043F4C000607080953 -:103F5000FFFFFFFF00A000000202000000C00401FC -:103F60000000000000000002100000000700000038 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/nowatchdog_boot_robert_bb2@1478015115.bin b/bin/boot/nowatchdog_boot_robert_bb2@1478015115.bin deleted file mode 100755 index 5031130cab..0000000000 Binary files a/bin/boot/nowatchdog_boot_robert_bb2@1478015115.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_robert_bb2@1478015115.hex b/bin/boot/nowatchdog_boot_robert_bb2@1478015115.hex deleted file mode 100644 index 1dab34819b..0000000000 --- a/bin/boot/nowatchdog_boot_robert_bb2@1478015115.hex +++ /dev/null @@ -1,1783 +0,0 @@ -:020000040800F2 -:1000000000AA01208925000885250008E51D0008B3 -:1000100085250008852500088525000800000000CA -:10002000000000000000000000000000852500081E -:1000300085250008000000008525000885250008AA -:1000400085250008852500088525000885250008E8 -:1000500085250008852500088525000885250008D8 -:1000600085250008852500088525000885250008C8 -:1000700085250008852500088525000885250008B8 -:1000800085250008852500088525000885250008A8 -:100090008525000885250008852500088525000898 -:1000A0008525000885250008852500088525000888 -:1000B0008525000885250008852500088525000878 -:1000C0008525000885250008852500088525000868 -:1000D0008525000885250008852500088525000858 -:1000E0008525000885250008852500088525000848 -:1000F0008525000885250008852500088525000838 -:100100008525000885250008852500088525000827 -:100110008525000885250008852500088525000817 -:100120008525000885250008852500088525000807 -:1001300085250008852500088525000885250008F7 -:1001400085250008852500088525000885250008E7 -:1001500085250008852500088525000885250008D7 -:1001600085250008852500088525000885250008C7 -:1001700085250008852500088525000885250008B7 -:1001800085250008852500088525000885250008A7 -:100190008525000885250008852500088525000897 -:1001A0008525000885250008852500088525000887 -:1001B00085250008852500088525000861070008B9 -:1001C0006D0700088525000800000000852500084F -:1001D0008525000885250008852500088525000857 -:1001E0008525000885250008852500088525000847 -:0801F0008525000885250008A3 -:0801F8009C0000000100000062 -:1002000053B94AB9002908BF00281CBF4FF0FF317D -:100210004FF0FF3000F03CB882B0EC462DE90050C2 -:1002200000F01EF8DDF804E002B00CBC704700BF1F -:100230002DE9F041904606460F461D46069C00F00B -:1002400029F808FB01FC8646A8FB002300FB05C536 -:10025000B21A2B4467EB0303C4E90023BDE8F08125 -:100260002DE9F8431D46174680468946089E00F052 -:1002700053F900FB05F3A0FB074507FB0137B8EB7B -:1002800004043D4469EB0505C6E90045BDE8F88373 -:10029000704700BF00292DE9F047C0F2A280002678 -:1002A000002BC0F298808C4690469E461546044628 -:1002B0000F46CBBB8A4256D9B2FA82F33BB1C3F1A7 -:1002C00020029F409D409C4020FA02F21743280CD8 -:1002D000220C1FFA85FEB7FBF0F100FB11770EFB35 -:1002E00001F342EA0747BB4207D97F1980F0018139 -:1002F000BB4240F2FE8002392F44FF1AA4B2B7FB82 -:10030000F0F300FB13770EFB03FE44EA0747BE45FC -:1003100006D97F1980F0EB80BE4540F2E880023BB1 -:1003200043EA0143002203E08B420FD90022134627 -:10033000341C4FF0000518BF0124604265EB4501F5 -:100340005840514000196941BDE8F087B3FA83F283 -:10035000002A40F08380804540F2CD808B42C0F07F -:10036000CA801346E4E712B90123B3FBF2F5B5FAEC -:1003700085F2002A3BD1781B4FEA154E1FFA85FC07 -:100380000122210CB0FBFEF80EFB18000CFB08F359 -:1003900041EA0047BB4208D97F1980F0B080BB42D8 -:1003A00040F2AD80A8F102082F44FF1AA4B2B7FBB7 -:1003B000FEF30EFB13770CFB03FC44EA0747BC4536 -:1003C00006D97F1980F09980BC4540F29680023BA7 -:1003D00043EA0843ACE752426FEA060663EB430385 -:1003E00061E740424FF0FF3661EB410158E795402D -:1003F000C2F1200107FA02F34FEA154ECF4024FA6A -:1004000001F194401FFA85FC1943B7FBFEF24FEA55 -:1004100011480EFB12770CFB02F348EA0747BB4278 -:1004200005D97F1971D2BB426FD9023A2F44FF1A06 -:1004300089B2B7FBFEF80EFB18770CFB08F041EA17 -:100440000743984206D95B1961D298425FD9A8F157 -:1004500002082B44181A48EA024292E7C2F1200728 -:1004600003FA02FE08FA02F5914028FA07F32CFA83 -:1004700007FCF84043EA0E0E08434FEA1E48070CFB -:100480001FFA8EFABCFBF8F908FB19CC0AFB09F13C -:1004900047EA0C4C614507D91CEB0E0C32D2614582 -:1004A00030D9A9F10209F444C1EB0C0C80B2BCFBB9 -:1004B000F8F308FB13CC0AFB03FA40EA0C418A4527 -:1004C00006D911EB0E0125D28A4523D9023B71448E -:1004D00043EA0943CAEB0101A3FB0589494503D35C -:1004E00003D19440444500D2013B002220E7013B68 -:1004F00016E7013901E701231AE7013B68E708F134 -:10050000FF3852E709F1FF39CEE7013A8FE708F1EA -:10051000FF389FE7013BDBE72DE9F043002B40D19B -:100520008A42044615464AD9B2FA82F30F464BB1C5 -:10053000C3F12006994000FA03F402FA03F5F040F3 -:1005400040EA0107290C260C1FFA85FEB7FBF1F0E3 -:1005500001FB10770EFB00F246EA07439A4207D9E7 -:100560005B1980F0EA809A4240F2E78002382B441F -:100570009A1AA4B2B2FBF1F301FB13220EFB03FEA5 -:1005800044EA0242964506D9521980F0DA8096452F -:1005900040F2D780023B43EA004000263146BDE8E6 -:1005A000F0838B4244D8B3FA83F6002E45D18242C1 -:1005B00040F2BF808B42C0F0BC803046EEE712B9FB -:1005C0000125B5FBF2F5B5FA85F2002A7BD14A1B6D -:1005D0002F0C1FFA85FE0126230CB2FBF7F007FB58 -:1005E00010220EFB00FC43EA02418C4507D9491951 -:1005F00080F0A1808C4540F29E8002382944CCEBEB -:100600000101A4B2B1FBF7F307FB13110EFB03FECC -:1006100044EA0144A64506D9641980F09080A645B5 -:1006200040F28D80023B43EA00403146BDE8F08352 -:10063000002630463146BDE8F083C6F12005B340C0 -:1006400002FA06F701FA06F4EA40E94020FA05F555 -:100650001A4325434FEA124C4FEA154E93B2B1FBB1 -:10066000FCF80CFB181103FB08F44EEA01418C4224 -:1006700006D9891869D28C4267D9A8F102081144B9 -:10068000091BADB2B1FBFCF40CFB141103FB04FE1F -:1006900045EA01439E4505D99B1854D29E4552D93F -:1006A000023C134444EA0844CEEB0303A4FB07894D -:1006B0004B4503D351D1B04040454ED20026601E79 -:1006C0003146BDE8F083C2F12006954001FA02F3FD -:1006D00000FA02F42F0CF140F0401FFA85FEB1FB46 -:1006E000F7F6184307FB16110EFB06F24FEA104C03 -:1006F0004CEA01439A4205D95B1929D29A4227D97B -:10070000023E2B449B1A80B2B3FBF7FC07FB1C3361 -:100710000EFB0CF140EA0343994206D95B1919D24A -:10072000994217D9ACF1020C2B445A1A4CEA0646EE -:1007300052E7012032E7013861E7013818E7013B51 -:1007400071E7013B27E7013CACE708F1FF3897E789 -:10075000013ED7E70CF1FF3CE7E7204600261DE706 -:10076000014800F0D6BF00BF302B0008014801F05F -:1007700038B800BF302B0008014800F0F9BD00BFB9 -:10078000302B000837B50024012002F0ADF90190AC -:100790002546E0B200F046F801AA08B910551DE060 -:1007A000135D042B0BDD174800F03CF9019800F0B5 -:1007B00040F90120002102F087F9002020E00133F8 -:1007C000DBB2042B135509D90F48012500F012F9AB -:1007D000204600F02EF90D4800F024F90134042CD5 -:1007E000D7D1019C3CB10A4800F004F9204600F042 -:1007F00020F900F08FF80120214602F065F9284623 -:1008000003B030BD0D650008396500084465000877 -:100810004E65000808B500F077FB80F00100C0B21B -:1008200008BD000008B50C22044B02FB003000F0AC -:100830008DFC80F00100C0B208BD00BFD02B0008C5 -:1008400010B5002401F094F8054820440C34017AD6 -:1008500000F062FC302CF7D1BDE8104001F089B8FF -:10086000D02B0008044BDA691106FBD59862DA69CF -:100870001206FCD5704700BF0048004070B5214DFE -:100880008AB02148082101F075F828464FF48021EC -:100890001E4C01F06FF82B68642623F0010394E8E6 -:1008A00007002B600C348DE8070000216846022207 -:1008B0000B4600F013FC03AB94E8070083E8070045 -:1008C00000211846022200240B4600F007FC06A86F -:1008D00002F020F8089821460D4A70430023FFF7E4 -:1008E0008FFCC0F3420320F00F000004000C1843FB -:1008F0000D23B0FBF6F0E8606C60AC602B600AB0D2 -:1009000070BD00BF00480040000C0240002C0008F1 -:1009100040420F002DE9F043334D83B02C6800AF07 -:1009200004F12E0304F1270823F00703ADEB030DB8 -:1009300050238DF8003021236E460DF106028DF80C -:10094000013000238DF8023003238DF8033004F1C9 -:1009500021035BBAADF80430244B03F11C0153F8BA -:10096000040B8B4242F8040BF9D11B7804F12309E4 -:1009700006F123001E491370224602F0C5F83146E5 -:100980004A46002001F044FD07F1080304F5927483 -:10099000314643F8040D42461868FE23B4FBF3F3D6 -:1009A000434446F80900073323F00703ADEB030D7A -:1009B00068466E4601F004FD044655206C44FFF77E -:1009C00051FFA64207D016F8010B552808BF00209A -:1009D000FFF748FFF5E75520FFF744FF00230C37EA -:1009E0002B60BD46BDE8F08338000020182C0008BD -:1009F0003C00002010B5441E14F8012F7AB10849BC -:100A00000B68FF2B0BD80A2A02D1FFF783FFF3E70D -:100A10000D2A1FBF581C5B1808601A71ECE710BD47 -:100A20003800002008B5FFF7E5FFBDE80840FFF7F4 -:100A300071BF1FB50C2201A901F018FD01A8FFF735 -:100A4000D9FF05B05DF804FB07B502A9012201F842 -:100A5000010D042000F096F903B05DF804FB0021BD -:100A600002200A4600F08EB910B540F2F51400F0ED -:100A7000B9F850B1013C04D10548FFF7D3FF204637 -:100A800010BD642001F0E4FCF1E7012010BD00BFBF -:100A90007A65000810B5074C43F2CD71064848F658 -:100AA000B803224601F02AFD01462046BDE8104069 -:100AB00000F0E4B83C010020352C000810B500F02F -:100AC00099F8FFF7E7FF08B9154816E0002400F091 -:100AD00039F9FFF7C4FF0120FFF7B6FFFFF7C4FFA6 -:100AE00078B1002103200A4600F04CF90D48FFF7C9 -:100AF00081FF2046FFF79DFF0B48BDE81040FFF740 -:100B000091BFFFF7C7FF0028DED001340B2CE2D1E4 -:100B10000648FFF787FFBDE81040FFF7A0BF00BF02 -:100B20009D650008B8650008D3650008DD6500080C -:100B300008B5FFF799FF0120FFF786FFFFF794FF45 -:100B4000002103200A46BDE8084000F01BB9000060 -:100B50002F2307B54A1C58430B4B00EB52001A7861 -:100B6000B0FBF1F0C0B282420AD002A91870042290 -:100B700041F8040D012000F005F90220FFF764FFA1 -:100B800003B05DF804FB00BF0000002007B502A918 -:100B9000042241F8040D012000F0F4F80320FFF7CF -:100BA00053FF03B05DF804FBFFF759BF0C4B014442 -:100BB00030B51C68884207D022689568AD07FCD51F -:100BC00010F8015B1573F5E71B681A68936813F456 -:100BD000C05FFBD10A20BDE8304001F039BC00BF46 -:100BE000802B0008024B1868403000F0AFBA00BFFD -:100BF000802B000838B5204B012200211C6804F12D -:100C00002005284600F03CFA0021284600F052FA60 -:100C100004F1380000F094FA04F1400000F090FA7A -:100C2000012104F12C000A4600F02AFA002104F107 -:100C3000080001220B4600F051FA0021012204F1C4 -:100C400014000B4600F04AFA6068012101F014FF1D -:100C50006068002101F010FF94E8030000F08AFEB4 -:100C600023684CF207321A604FF4B8525A601A687F -:100C700042F040021A6038BD802B00082B4B2DE952 -:100C8000F7431C68814601208846E36A04F1200787 -:100C900004F12C060093236B019301F0DDFB384631 -:100CA000012100F007FA0021304600F003FA01208C -:100CB00001F0D2FB684600F049FA054620B11C4815 -:100CC0000025FFF7AFFE2CE030460121383400F05C -:100CD000F1F9012001F0C0FB204600F037FA06468A -:100CE00008B114481BE0684600F030FA054608B920 -:100CF000114814E0012001F0AFFB48464146FFF7E0 -:100D000055FF3846314600F0D5F90C480921FFF768 -:100D10004DFF204600F01AFA054610B90848FFF7C3 -:100D200081FE284603B0BDE8F08300BF802B000899 -:100D3000FC650008186600083266000802640008B6 -:100D40004E66000808B5022001F086FB0A4B1B68BE -:100D500093F8483043B10948FFF764FE012000F0E2 -:100D6000D3FD022001F078FB0548FFF75BFE012070 -:100D7000BDE8084000F0C2BD802B00086F66000887 -:100D80008B6600080F4BF7B51C6802AD0F460121BA -:100D9000203405F8010D1646204600F08BF964203A -:100DA00001F056FB28460121FFF700FF1EB138462F -:100DB0003146FFF7FBFE2046002100F07BF903B02F -:100DC000F0BD00BF802B0008F0B50746ADF2044D22 -:100DD0000E460C460025B919B4F5806F6846A1EBA4 -:100DE00004010DD94FF48062A4F5806400F0CCF8C2 -:100DF000284669464FF4806201F00AFB0546EAE79F -:100E0000224600F0C1F869462246284601F000FB60 -:100E10000DF2044DF0BD08B5202001F045FD00287D -:100E2000FAD108BD08B5022001F03EFD0028FAD035 -:100E30000220BDE8084001F041BD00B58DB0684614 -:100E400001F0BBFC0023684600934FF480730A93C3 -:100E500035230B9301F0E2FCFFF7E4FFFFF7DBFF24 -:100E60000DB05DF804FB000030B5002495B02448B7 -:100E7000022100F07FFD23480021032220440C348E -:100E80000B4600F02BF9482CF5D101A8002401F005 -:100E90008BFC102301A802940193172303944FF4B1 -:100EA0004075059304940694079401F095FC012085 -:100EB00001F0D8FCFFF7C1FF08A801F07EFC662313 -:100EC00008A808941393129501F0A8FCFFF7AAFF55 -:100ED00008A801F072FC992308A8089413931295AE -:100EE00001F09CFCFFF79EFF4CF2503001F0B0FA8D -:100EF000FFF7A3FF0248022100F046FD15B030BD08 -:100F0000001000A00C64000830B502218DB01C4810 -:100F100000F030FD022001F0B9FC684601F04DFC04 -:100F20004FF08063684600934FF0407304934FF492 -:100F300040730A93AF230B9301F070FCFFF772FF2D -:100F400001F0ACFC054601F0A9FC044601F0A6FC4A -:100F5000240244EA004040EA0504FFF75CFF094B25 -:100F60009C4204D020460024FFF763FD00E00124EA -:100F70000348022100F008FD20460DB030BD00BF3F -:100F8000001000A020BB190070B515468CB00446B7 -:100F90000E461B48022100F0EDFC681E01F076FCB5 -:100FA000684601F00AFC4FF08063684625440093D0 -:100FB0004FF0407304930A2305934FF44063099361 -:100FC0004FF440730A934FF4005308930B230B9391 -:100FD00001F024FC304601F051FCAC4204D001F099 -:100FE0005DFC04F8010BF8E7022001F067FCFFF755 -:100FF00012FF0348022100F0C7FC0CB070BD00BF17 -:10100000001000A0034A136B23F4FF6323F00703CF -:10101000136370470038024000F13F4000F57E0046 -:10102000C0F387200A280AD8054A135C591C1154BA -:101030002BB9044901230A6B834013430B637047A8 -:10104000F48900200038024000F13F4000F57E00A6 -:10105000C0F387200A280DD8074A135C53B1013B1F -:10106000DBB2135433B9054901230A6B834022EAEA -:1010700003030B63704700BFF4890020003802406F -:10108000436813B500930123044600688DF80430CB -:1010900000238DF805208DF807308DF80610FFF736 -:1010A000BBFF2068694601F02BFB2068FFF7CCFFEF -:1010B00002B010BD38B5037A04460D460BB981F075 -:1010C00001052068FFF7A8FF2068A1882A4601F0E3 -:1010D00055FB2068BDE83840FFF7B6BF13B504469E -:1010E00040688DF80730009002208DF805208DF8BB -:1010F000040020688DF80610FFF78EFFA27A2068A2 -:10110000218901F03FFB2068694601F0F9FA206867 -:10111000FFF79AFF02B010BD002313B504460193F8 -:101120004368006800938DF80710FFF775FF20688B -:10113000694601F0E5FA2068FFF786FF02B010BDAE -:10114000036813B10021FFF7E7BF704738B50446C5 -:101150000068FFF761FFA188206801F009FB0546E0 -:101160002068FFF771FF281C18BF012038BD1FB58C -:10117000836804460093C36868460193FFF7E0FF65 -:10118000636902A80293A3690393FFF7D9FF04B030 -:1011900010BD10B504460121083002220023FFF7DC -:1011A0009DFF04F11400012102220023BDE810403C -:1011B000FFF794BF0368DB6910B5044673B9C36ACF -:1011C00063B10A2001F048F92046FFF7D0FF20461E -:1011D000E36A01219847142001F03EF92046FFF709 -:1011E000D8FF2046BDE8104000F0E0B910B5044635 -:1011F00000F04FFA48B10A2001F02EF9204600F025 -:1012000048FA80F00100C0B210BD012010BD30B519 -:10121000044687B0C36A00219847A36800210125CE -:1012200000936846E3680A468DF808500193FFF77B -:1012300027FF68460021FFF73DFF6369002103A8EF -:101240000393A3690A468DF814500493FFF718FF1F -:1012500003A80021FFF72EFF07B030BD10B50446EC -:1012600000F00AFA2368DB6933B9E36A4BB1204620 -:10127000BDE81040FFF7CBBF2046BDE81040FFF7A8 -:1012800076BF10BD10B50446FFF7E8FF2046BDE865 -:101290001040FFF78FBF00002DE9F3419846036827 -:1012A00004460E46DB6917460BB900205BE000F0F0 -:1012B000F0F9B8B9256814220021284601F02FFC66 -:1012C0009DF828306F8020466B71089B2E71AB60B3 -:1012D000099B85F806802B6100F0ECF92368002259 -:1012E0009A6111E02046FFF7CDFF2046FFF77EFF11 -:1012F0000028DFD1D9E79A69B2F57A7F23DA013283 -:1013000002209A6101F0A8F8204600F0DFF90122DE -:10131000236883F82020154A0192019A511E0191F9 -:101320001AB193F82020002AF7D1002283F8202058 -:10133000019A42B11D7D05F0FD02012ADBD16B1E31 -:101340005D425D4103E00025204600F0A8F92368D6 -:101350000022204683F82020FFF748FF10B92046DE -:10136000FFF790FF284602B0BDE8F08180841E00A0 -:1013700010B5044618B90E48A92100F0E9FF002174 -:101380002422006801F0CBFB204600F0CBF8236A52 -:101390002BB1002104F120000A46FFF771FEE36A39 -:1013A00023B12046BDE81040FFF731BF10BD00BF9C -:1013B000E166000810B5044618B90848B62100F0E7 -:1013C000C7FF00680368DB690BB9FFF7F3FE23680A -:1013D0001A68D3690133D36110BD00BFE16600080C -:1013E00008B518B90748BF2100F0B2FF00680268CD -:1013F000D36933B1013BD3611BB9BDE80840FFF7A6 -:101400002DBF08BDE16600081FB50C46114618B98E -:101410000A4840F28D1103E023B908484FF4C77120 -:1014200000F096FF82888DE80A0000212346029191 -:101430000068FFF731FF04B010BD00BFE16600088F -:1014400013460122FFF7E0BF1FB50C46114618B93D -:101450000A484FF4D77103E023B9084840F2AF11AE -:1014600000F076FF82888DE80A00002301210293B4 -:1014700023460068FFF710FF04B010BDE1660008C6 -:1014800007B502AB03F8012D0122FFF7DDFF03B022 -:101490005DF804FB03680022197583F8202070476B -:1014A00010B5046863882279C3F3090322B922784E -:1014B000012A18BF43F48063A268E468121BFF2A64 -:1014C00095BF120443F0FF7302F47F0243F48033AC -:1014D00098BF134309B143F4005342681268536044 -:1014E00010BD43681B681A6822F0D60210B51A6056 -:1014F0005A6842F480425A6008220368BDE81040EE -:101500001A70FFF7C7BF0368012110B50446586879 -:1015100001F0A6FA00212368BDE81040586801F0E8 -:101520009FBA000043685A7B53B2002B30B506DAED -:1015300002F00F021B4B1344C0221A7606E003F19F -:101540006043C02203F5614383F8002343689A7B1C -:1015500053B2002B06DA02F00F02124B1344C022E2 -:101560001A7606E003F16043C02203F5614383F875 -:10157000002350F8045F0C4C6B7B59B203F01F0240 -:101580000123490903FA02F244F82120AA7B51B24F -:1015900002F01F024909934044F82130BDE8304071 -:1015A000FFF7B1BFFCEC00E000E100E043682C492C -:1015B0009A688A42F0B5074687B004D92948512174 -:1015C000294A00F0B9FE29498A4205D91C7B34B169 -:1015D000012C07D000F0C6FE0124254603E00924B3 -:1015E000102500E0022502A801F094F9796860193D -:1015F000049A8B684343B2FBF3F603FB162303B94B -:10160000013E0F2E03D91748702100F0A1FE03C937 -:1016100000F0B0F97B684FF0000100221B68013D2B -:1016200066F307110192013C8DF804508DF8071004 -:1016300019688DF8054021F0010119600C491868FE -:1016400001401960019919619A60DA601A6842F0E4 -:1016500001021A60074A59680A405A6007B0F0BD93 -:10166000801A0600FA66000817670008A0860100C5 -:10167000001F30FF008000F810B5044654F8043F06 -:1016800093E8030000F080F92046BDE81040FFF722 -:101690003ABF43681B689869C0F3C030704743681D -:1016A0001B681A6822F0D6021A605A6842F4804217 -:1016B0005A60704703685A790AB9012204E01A791E -:1016C0000AB9052200E006221A70704743681B68B9 -:1016D0001A6842F0D20210B51A60026814791CB977 -:1016E000196841F004011960117801290CD1528860 -:1016F000012CC2F309020CBF4FF08071002142F4AB -:1017000090320A435A6010BD0121BDE81040FFF736 -:10171000C7BE38B5436805461C6803681A78013AA5 -:10172000072A5BD8DFE802F0045A5A5A223B5A577C -:10173000A269910701D59A79A2621A793AB9A26988 -:1017400052060FD50522284601211A7008E0012A09 -:1017500008D1A269100605D50622284600211A7074 -:10176000FFF79EFEA369D9063AD525E0A269520784 -:1017700005D5DA681969501CD860636A8B54A3696F -:101780001B0603D528460021FFF78AFEA3695806E9 -:1017900026D528460121BDE83840FFF7A2BEA26940 -:1017A000910705D5DA681969501CD8608B5CA36273 -:1017B000A16911F0100109D0E3692846022143F024 -:1017C0001003E361BDE83840FFF764BEA3691A0661 -:1017D000DCD52846D8E7A369DB0701D400F0C2FDB9 -:1017E00038BD43681B689A69D20503D5DA6942F4AB -:1017F0008072DA619A69510503D5DA6942F4806230 -:10180000DA619A69920503D5DA6942F40072DA6105 -:101810000321FFF766BE0000094B70B51C680D463A -:1018200006462046FFF7C6FD2A4631462046FFF70A -:1018300007FE05462046FFF7D3FD284670BD00BFD2 -:10184000642B0008094B70B51C680D4606462046FF -:10185000FFF7B0FD2A4631462046FFF711FE054648 -:101860002046FFF7BDFD284670BD00BF642B000871 -:1018700037B500F07DF81D4801210222FFF700FC7A -:101880001B4801210222FFF7FBFB1A48012102221B -:10189000FFF7F6FB00F06DF800200DF10701FFF7F0 -:1018A000BBFF9DF80750012D1DD114200DF107013C -:1018B000FFF7B2FFA8B1104C237813B101332370A6 -:1018C0000FE09DF80710142021F0060141F00201FD -:1018D0008DF80710FFF7B6FF18B10320257000F050 -:1018E000BBFD012000E0002003B030BD54640008BF -:1018F000606400086C640008FF890020014601480C -:10190000FFF7D8BB5464000801460148FFF7D2BB7B -:1019100060640008B0F1804F08B503D20D48302153 -:1019200000F016FD0C4B98420ED903F58033984217 -:101930000CD9B0F1A04F0BD3084A094BB0F1204F9E -:1019400034BF1846104608BD064808BD064808BD05 -:10195000064808BD38670008FFFF0040D12900088D -:10196000B9290008E9290008012A0008A12900086E -:101970007047704710B50C46FFF7CCFF01210346B6 -:101980002046BDE81040184710B50C46FFF7C2FFCF -:10199000002103462046BDE81040184738B5104CDA -:1019A00005464FF080512046FFF7E4FF45B154E86B -:1019B000003F43F4807344E80032002A08D0F6E781 -:1019C00054E8003F23F4807344E80032002AF7D142 -:1019D00003484FF08051BDE83840FFF7D5BF00BF46 -:1019E00000700040024B5868C0F34000704700BFD1 -:1019F00000700040024A136843F008031360704708 -:101A000000700040B0F1006F10B504460BD20C48D6 -:101A1000FEF7F0FF2046FFF70CF80A48FFF702F840 -:101A20004FF0FF3010BD084B002053F8042BA242AA -:101A300002D81A68944202D301300B28F5D110BDA8 -:101A40005767000860670008786400082DE9F843CC -:101A50000446174699460E46C1B3FFF7D3FF054625 -:101A6000601E3044FFF7CEFF002D804631DB00289A -:101A70002FDB461B01361FB1002031464A46B847CE -:101A80002C4600F09BFD44451EDCF32000F0ACFD2D -:101A9000114B002133F8140000F0DEFD09280AD0B4 -:101AA0000E48FEF7A7FF2046FEF7C3FFFEF732FF02 -:101AB00000F092FD0DE02FB1C5F1010031464A461C -:101AC0002044B8470134DEE700F086FD0120BDE880 -:101AD000F8830020BDE8F883A8640008796700084F -:101AE0002DE9F0410F461646804600F067FD3E4462 -:101AF0003C46F32000F078FDB44216D0C7EB080551 -:101B0000254414F8011B284600F0DCFD0928F3D019 -:101B10000848FEF76FFF2846FEF78BFFFEF7FAFE38 -:101B200000F05AFD0020BDE8F08100F055FD0120D5 -:101B3000BDE8F0819167000808B57D2000F0A8FF9E -:101B4000003018BF012008BD4900FFF701B80000B0 -:101B50002DE9F043012185B005460020FEF7F8FF8E -:101B6000042000F047FB01A8294600F097FB01A8DC -:101B700000F0A3FB08B93B4814E00C353A48FEF7E7 -:101B800051FF02992846FFF71FF904463748FEF730 -:101B900031FF2046FEF74DFFFEF7BCFE039B9C4243 -:101BA00004D03348FEF73EFF012057E0029CDFF8E7 -:101BB000E0803048FEF736FF21462F4A002340469A -:101BC0000024FFF743FF029F2C484FEA5709FEF716 -:101BD00029FFBC421CD23E1B29486119B6F5803F43 -:101BE00028BF4FF480363246FFF7CEF908EB0400E9 -:101BF00023493246FFF774FF18B92248FEF712FF57 -:101C000006E009EB54003946FEF7A2FF3444E0E752 -:101C10001D48FEF7EFFE0298FEF70BFF1B48FEF78C -:101C2000E9FE1B49029A002000F0F2FB0446194825 -:101C3000FEF7E0FE0398FEF7FCFE1748FEF7DAFE1B -:101C40002046FEF7F6FEFEF765FE039B9C4204D09D -:101C50001248FEF7E7FE022000E0002005B0BDE8D4 -:101C6000F08300BFA2670008C0670008DD670008B6 -:101C7000F367000816680008491B00082D68000873 -:101C8000008A0020446800084F6800085D6800086A -:101C90000080000866680008796800087F6800080E -:101CA00010B5022000F0BEFAD8B3042000F0BAFA52 -:101CB000044670B11B48FEF7B5FE042000F0A6FAFA -:101CC000022000F0A3FA4FF40040BDE8104000F0FD -:101CD0009DBA1548FEF7A6FE082000F097FA1020DE -:101CE00000F094FA202000F091FA402000F08EFAE3 -:101CF0002046FFF72DFF022807D1082000F07AFACE -:101D0000102000F077FA00F051FB042000F07EFA7A -:101D1000022000F07BFA4FF40040BDE8104000F0D4 -:101D200069BA10BDCA6800080069000810B52548E6 -:101D3000FEF778FE4FF48000FFF70AFF10B102288B -:101D40003AD809E0202000F061FA402000F05EFA65 -:101D5000802000F04FFA2FE0082000F04BFA10200E -:101D600000F048FA202000F05DFA20B91648FEF78E -:101D700059FE202007E0402000F054FA58B91348DB -:101D8000FEF750FE402000F035FA4FF4007000F0EE -:101D900031FA00F00BFB0E480024FEF743FE08204A -:101DA00000F034FA102000F031FA202000F02EFA72 -:101DB000402000F02BFA00E00124042000F026FA75 -:101DC000204610BD1B690008356900086E690008CF -:101DD000A769000808B50248FEF724FE00F05CFA87 -:101DE000E16900081EF0040F0CBFEFF30880EFF369 -:101DF0000980FFF7EFBF704708B50120FEF712FD1D -:101E000008B900200FE00020FEF70CFD0028F8D0F4 -:101E10000220FEF707FD0028F3D00320FEF702FDA5 -:101E200080F00100C0B200F0010008BD1FB50446FB -:101E30000E48FEF7F7FD01A90C22204600F016FB24 -:101E400001A8FEF7EFFD2046FEF7A0FE0220FEF7F8 -:101E5000E9FC04460220FEF7E5FCA04201D000F0B8 -:101E6000A5FA0A2000F0F8FAF4E700BFD7690008E5 -:101E70007FB5BB484FF08051FFF77CFDFFF7B2FD07 -:101E8000044630B1FFF7B6FDB648FEF7CBFD00F0D3 -:101E900094FA4FF08051B248FFF776FDFEF76CFCE4 -:101EA000FEF7ECFCB048FEF7BDFDB048FEF7BAFD0A -:101EB000AF48FEF7B7FDAF48FEF7B4FDAE48FEF7FA -:101EC000B1FDAE48FEF7AEFDAD48FEF7ABFDFFF746 -:101ED000CFFC00F0B1F900F0D1F921460C2201A8A5 -:101EE00000F01DFE00F0C6F901A90C2200F0BEFAB8 -:101EF00001A8FEF797FDA348FEF794FDA148FEF761 -:101F000091FD4FF4804000F08DF990B19E48FEF7AE -:101F100089FD4FF4804000F079F9082000F076F94F -:101F2000102000F073F9202000F070F9402000F03C -:101F30006DF9FEF799FFFEF783FCFFF799FCFEF7BA -:101F4000BDFDFEF7F5FDFEF71DFC08B18F4803E06F -:101F5000FEF760FC10B18E48FFF768FF00F07AF9D9 -:101F6000802000F05FF918B1802000F04FF94BE0BD -:101F70004FF4003000F056F920B14FF4003000F07B -:101F800045F943E0FFF738FF68B141F288348148F2 -:101F9000FEF748FDFFF730FF78B1012000F05CFA52 -:101FA000013CF7D132E07C4B0CCB013301D0013244 -:101FB00003D17A48FEF736FD28E0FFF7BDFD48B3B0 -:101FC000FFF7BAFD10B17648FEF72CFD4FF4007014 -:101FD00000F028F910B17348FEF724FD4FF40070AB -:101FE00000F014F9102000F01DF9002800F0EE8038 -:101FF0006D48FEF717FD082000F008F9102000F0EA -:1020000005F9FFF793FE98B96848A5E76848FEF719 -:1020100009FDF6E74FF4007000F004F90028CFD175 -:10202000082000F0F3F8102000F0F0F8FFF738FE79 -:102030004FF4005000F0F6F804464FF4805000F0E2 -:10204000F1F8400040EA8400C4B24FF4006000F0B0 -:10205000E9F82043C0B20728C3B20DD14FF40060A5 -:1020600000F0D4F84FF4805000F0D0F84FF4005056 -:1020700000F0CCF84F486FE701334FF40060DBB25B -:10208000023B052B11D8DFE803F00C1003100C10F5 -:1020900000F0BCF84FF4805000F0B8F84FF4005056 -:1020A00003E000F0B3F84FF4805000F0A3F8FEF71F -:1020B000A9FF394B40485D681E68FEF79BFC284627 -:1020C000FEF7B7FC3D48FEF795FC00229300013275 -:1020D0004FF0FF3103F16043082A03F56143C3F871 -:1020E0008010C3F88011F1D1354B4FF480120024D9 -:1020F0001A634FF480625C639C631A645C6400F052 -:10210000E3FB3048012100F087FC21462D4800F018 -:1021100083FCF320012100F08BFC2146F32000F02A -:1021200087FC0320012100F08FFC2146032000F0F2 -:102130008BFC6FF4A050012100F092FC21466FF45B -:10214000A05000F08DFC2048012100F095FC1E48B5 -:10215000214600F091FC63B64FF0FF3EB54628479C -:1021600000700040EC690008F9690008006A000886 -:102170008D6A0008246B0008A56B0008266C000817 -:10218000A96C0008FF690008266D0008014550FE93 -:10219000024550FE536D0008008000088E6D000857 -:1021A000A16D0008B96D0008D96D0008034550FE07 -:1021B0004D6E0008044550FE6C6E0008806E0008ED -:1021C000003802400010E022B379F764082000F0E4 -:1021D00029F820B10548FEF725FC102003E004484B -:1021E000FEF720FC082000F005F81FE7016E00084C -:1021F000276E000810B50446002000F075FC40EA88 -:1022000004010020BDE8104000F05EBC10B504469B -:10221000002000F069FC20EA04010020BDE8104025 -:1022200000F052BC10B50446002000F05DFC2042D6 -:1022300014BF0120002010BD08B50120FFF7AEFB40 -:102240000120FFF7EFFF20B90121BDE8084000F0B1 -:102250003BBC08BD08B50648FEF7CCFB002000F0EB -:1022600043FCFEF7E6FBBDE80840FEF753BB00BFAA -:10227000886E0008022000F037BC000008B5FFF7A8 -:10228000F9FF0449884204D00220BDE8084000F06C -:102290001BBC08BD8BB8185800BEFDE71FB504462F -:1022A0000C2201A8FEF770FE01AB03CB206018687A -:1022B0006160A060204604B010BD0068A0F10C036E -:1022C000584258417047000080B5174606461048EE -:1022D0000D461C46FEF78EFB3846FEF78BFB0D487D -:1022E000FEF788FB3046FEF785FB0B48FEF782FBC6 -:1022F0002846FEF79EFB2CB10848FEF77BFB2046E4 -:10230000FEF778FB0648FEF78DFBFFF7C5FF00BF21 -:10231000946E0008A66C00089D6E0008A76C00086B -:10232000FF6900081FB506AA52F8044B03920092F9 -:102330001A462346FFF7C8FF0CB41FB506AA52F889 -:10234000043B03920092014AFFF7BEFF9F6E000814 -:10235000002307B572460093014BFFF7E3FF00BF70 -:10236000A66E00087446064808B5FEF743FB2046F3 -:10237000FEF75FFB0348FEF755FBFFF78DFF00BF3D -:10238000AE6E0008FF690008BFF34F8F0549064B8A -:10239000CA6802F4E0621343CB60BFF34F8F00BF03 -:1023A000FDE700BF00ED00E00400FA0508B5FEF708 -:1023B000FBFB00F081FBFFF7E7FF08B5FFF7E4FF49 -:1023C000F0B50646002401202546034694421DD060 -:1023D00011F804C000F1010EBCF1000F03D17355D8 -:1023E000774605460DE00133774606F800C0DBB2BC -:1023F000FF2B07D102F1FF3C644506D07355871CC3 -:102400007546012301343846E0E770467355F0BD48 -:1024100030B5C9B1C0430A44914213D011F8013B11 -:102420000A4D83EA000404F00F0455F8244084EABE -:10243000101080EA131303F00F0355F8233083EADA -:102440001010E9E7C04330BD084630BDC064000845 -:1024500000010138FDD1704710B504462CB14FF48E -:102460007A70013CFFF7F4FFF8E710BD0A2A10B5B7 -:1024700004DC1148BDE81040FEF7D4BA30230C4606 -:102480001C2204F8023B78234B700F239340034037 -:10249000D340092B01D8303302E00F2B02D8573339 -:1024A000DBB200E02023043A04F8013B131DECD119 -:1024B00000238B7210BD00BFE86E000808B582686B -:1024C0004368934203D304481421FFF741FF026895 -:1024D000591C4160D05C08BDFE6E000843688268EC -:1024E000934210B503D304481921FFF731FF026866 -:1024F0005C1C4460D15410BDFE6E0008F0B587B07E -:102500000E460746154600210C2268461C4600F080 -:1025100006FB00210C2203A80296009700F0FFFAA8 -:10252000684603950594FFF7C9FF06466846FFF71E -:10253000C5FFB042014601D003A808E06846FFF796 -:10254000BDFF0446D8B1012804D103A83146FFF7E6 -:10255000C5FFEBE7030608D504F07F046846FFF7E4 -:10256000ADFF40EA042000F18004A4B20025A5429A -:10257000DCDA03A80021FFF7B1FF0135F7E7049883 -:1025800007B0F0BDFEE700000748084A0849121AE4 -:1025900008B500F0B9FA0748074A0021121A00F0FE -:1025A000BEFAFFF765FC00BF000000201400002009 -:1025B000106F000814000020008A0120044B1A69E3 -:1025C000002A04DA034A5A6002F188325A607047DE -:1025D000003C024023016745024A136943F000436F -:1025E00013617047003C0240014BD860704700BF48 -:1025F000003C02400E4BDA68D20310D4DA68D106F0 -:102600000FD4DA68D2050ED4DA6812F0E00F0CD1DC -:10261000DB6813F0020F14BF082009207047012067 -:10262000704706207047022070470720704700BFA0 -:10263000003C0240092307B58DF80730FFF7DAFFA9 -:102640008DF807009DF80730012BF7D09DF80700A3 -:1026500003B05DF804FB000070B5064641B10129E6 -:1026600008D002290CBF4FF400754FF4407503E009 -:102670000D4601E04FF48075FFF7DCFF09281ED1FD -:102680000F4C236923F4407323612169294321619D -:10269000236923F0F8032361236943F002031E43F7 -:1026A0002661236943F480332361FFF7C3FF236965 -:1026B00023F002032361236923F0F803236170BD33 -:1026C000003C024070B505460E46FFF7B3FF0928EF -:1026D00013D10A4C236923F44073236123692361D6 -:1026E000236943F0010323612E70BFF34F8FFFF77F -:1026F000A1FF236923F00103236170BD003C024068 -:10270000F0B500220E680123934003EA060E9E45B1 -:102710002AD1550003230468AB40DB431C4004600E -:102720000C79076804FA05FC013C4CEA0707012C08 -:10273000076011D884684F791C40AF40846084687A -:1027400027438760446824EA0E0444608C79476814 -:102750009440A4B23C434460C4682340C360CB7936 -:10276000C468AB402343C3600132102ACBD1F0BD13 -:102770000369194214BF01200020704702B90904FF -:102780008161704701F00703C9089B0000EB8100DD -:102790000F219A40994010B5046A24EA01010162B0 -:1027A000016A1143016210BD00230360436083602E -:1027B000C3600361436183617047002343608360AA -:1027C000C3600360036143618361C3610362436269 -:1027D0008362C362704700000E4903680A6810B53F -:1027E00044691C4383691C4322F07F43426823F001 -:1027F0003003234343EA02630B60C36882684C687A -:102800001A43054B23401343026943EA02434B60DA -:1028100010BD00BF001000A0FEF7E0FF90E80C0024 -:102820000E491A4383681A43C3681A4303691A435B -:1028300083691A43C3691A43036A1A43436A1A43F2 -:10284000836A1A43C36A10B51A434C69044B234088 -:102850001343426943EA82434B6110BD001000A05C -:1028600000008090044B1A6810B142F0010201E0B0 -:1028700022F001021A607047001000A0024B9A6813 -:10288000920600D498617047001000A0024B9A682D -:10289000920600D418617047001000A0014B187810 -:1028A000704700BF201000A0034B9B68184214BF64 -:1028B00001200020704700BF001000A0014BD8602D -:1028C000704700BF001000A00F4B00211A6842F0B3 -:1028D00001021A6099601A6822F0A85222F41022AC -:1028E0001A600A4A5A600A4AC3F8842002F18062D8 -:1028F000C3F888201A6822F480221A60D960C3F8CD -:102900008C10C3F89010704700380240103000243B -:10291000003000201E4A936803F00C03042B10B50E -:1029200003D0082B03D01B4B19E01B4B17E0516859 -:10293000536811F4800F516803F03F0318BF164A23 -:10294000C1F3881108BF134AB2FBF3F3104A4B439B -:102950005268C2F3014201325200B3FBF2F30C4A57 -:10296000036093680D49C3F30313CC5C0368E34031 -:1029700043609468C4F382240C5D23FA04F48460F9 -:102980009268C2F342328A5CD340C36010BD00BF7C -:10299000003802400024F40040787D01010000204E -:1029A000044B1A6B09B1104301E022EA00001863DE -:1029B000704700BF00380240044B5A6B09B1104306 -:1029C00001E022EA00005863704700BF003802406F -:1029D000044B9A6B09B1104301E022EA00009863AE -:1029E000704700BF00380240044B1A6C09B1104315 -:1029F00001E022EA00001864704700BF003802407E -:102A0000044B5A6C09B1104301E022EA00005864FB -:102A1000704700BF00380240044B1A6909B11043E7 -:102A200001E022EA00001861704700BF0038024050 -:102A3000044B5A6909B1104301E022EA00005861D1 -:102A4000704700BF00380240044B9A6909B1104337 -:102A500001E022EA00009861704700BF00380240A0 -:102A6000044B1A6A09B1104301E022EA000018621F -:102A7000704700BF00380240044B5A6A09B1104346 -:102A800001E022EA00005862704700BF00380240AF -:102A90004209084B012A01D11B6804E0022A01D136 -:102AA0001B6F00E05B6F00F01F0023FA00F000F0E6 -:102AB0000100704700380240024A536F43F08073B0 -:102AC000536770470038024082B000230193054BE2 -:102AD0000193019B03EB80000190019B196002B000 -:102AE000704700BF5028004082B000230193054B7F -:102AF0000193019B03EB80000190019B186802B0D9 -:102B0000704700BF50280040431E0A4410B5914250 -:102B100004D011F8014B03F8014FF8E710BD02444F -:102B20000346934202D003F8011BFAE77047000006 -:102B300014000020702B0008001402400040000028 -:102B40000E00040000140240008000000F0004008A -:102B50000000000000000000000000000000000075 -:102B600000650008682B0008302B000850000000AA -:102B70000060004000000001801A0600005F600055 -:102B8000842B000800540140000020000000024097 -:102B90002000000005000800000002408000000046 -:102BA00007000800000002401000000000000000C4 -:102BB0000000024008000000010000000004024084 -:102BC00004000000000402400100000001000000B9 -:102BD0000018024040000000010000000018024000 -:102BE0000800000000000000001802402000000063 -:102BF0000100000000180240100000000100000069 -:102C0000000C02400001000008000700000C024018 -:102C1000000200000900070001424F4F544C4F4191 -:102C20004445520000000000002A2A000000000075 -:102C3000000000000047FF004C6174746963650088 -:102C4000694345637562653220323031362E303249 -:102C50002E323738313000506172743A2069434562 -:102C60003430554C314B2D434D3336410044617463 -:102C7000653A204A756C20323520323031362030AA -:102C8000393A35363A33394702FF7EAA997E5100E8 -:102C9000010592002062016F8247027200701100EC -:102CA0000101470B10470280470502470580034793 -:102CB0000801470208471120470C02470B0247173B -:102CC00040470501470540470C040090470F104757 -:102CD00006404705014705404705014704800004B9 -:102CE0004710804705020020000800804702014786 -:102CF0000908471701470434000C470403470D3007 -:102D0000475970470E8010470402004047040801ED -:102D100047020702004047040801470520044741D5 -:102D200003470B03470D1C471080470901008047EC -:102D30000203470908470324472B784712400747F7 -:102D40000A40470C040008470E40470501470604A7 -:102D50004704014706044706104711800747061C31 -:102D6000470380470C08470A0C3020470F30E847DC -:102D70000C63C1470C0C30470F400105470C03C0E2 -:102D8000470C0C3020470D184702E04704020CB1F5 -:102D90000018003847064047080C30471001074725 -:102DA00002010001CD72470398470F0C3020470DF8 -:102DB0003000F9A04708100001C04705038047070D -:102DC0000C30470E300071054708300001470F0CEA -:102DD0003020470F5808470401DCA00080470363F8 -:102DE000C1470203C020AF5247050C30471008030B -:102DF000470402E0F5470503C0470202E0050FA0C3 -:102E00001047040C3020471240002020470AC00120 -:102E1000E007A547060C3047110D470314470B4048 -:102E200047030547031447020C3020470F90000466 -:102E3000470220470E17CFA247020847020C30472F -:102E40001030092447022C470D02CD0FF047050C26 -:102E50003020470F108004470368470301054710DF -:102E60000C30471011073047190C3020470D300047 -:102E700010470A0147100C30470E30470205204723 -:102E8000023447050247100C30204707044706402C -:102E90004704203404ED80000247091C0008470560 -:102EA0000C30470F4047050422FC4702014707A0AA -:102EB0000002C047060C3020470903EAF520004015 -:102EC0004706035CA500184708401C8F0847050CFF -:102ED00030470A03D050470240470601F0554703E8 -:102EE000104706200200F047050C3020470928470C -:102EF00003110001E047032038FC8002470A1E8FBF -:102F00000847050C304708080040470330294704AC -:102F100002BC03D0470AC04703F047050C302047E6 -:102F20000A1C0F10470829DCA5003047090E8F083E -:102F300047050C30470C0F47094150550020470901 -:102F40000200F047050C3020470D0447065C76EC84 -:102F500080024707084702068047060C30470802F0 -:102F600047050447030147033CF0470A01400C00B2 -:102F7000F047050C3020470820470B1C21FAE732A8 -:102F8000004047100C3047090C470B2464F0F70849 -:102F90004709028047070C302047141C31CCE7203A -:102FA000804703383D444702028047070C30470FF3 -:102FB000404705027668FB47024000C0403D47039A -:102FC000028047070C302047080C47050147053CA5 -:102FD00063DE800010470C0447040C30470A20478A -:102FE000040247052402E847021047060101404752 -:102FF000020147050C302000A100123C20000447CC -:10300000021EF060470720001EF04047030D72BD0E -:10301000400003428EEF47060C30470503C0C0470F -:1030200004F047024000974703042000F0470401E2 -:10303000403C470201429C47070C302047033A3C82 -:10304000200647030CF060180210800006014002C1 -:103050004AA500188000077815006047020CC96077 -:1030600047050C30470503C0104704F04702050927 -:103070000747038001D05547039000401620000108 -:1030800042C1096047050C30204703323C202C47E1 -:10309000031CF0403047028000044702204705012E -:1030A0008839414702020025ACC02047040C304754 -:1030B0000503C0044704F00130080109000C470271 -:1030C000404705F00F4282010020402547070C30A1 -:1030D000204703323C20470280001EF04047028018 -:1030E000E847037C470705781547044488002047D4 -:1030F000040C3047040803C80001470210F04703DE -:1031000071094703964708C0164000034215002086 -:10311000470202001C0C30200001017A3C204704C9 -:103120000EF0600008704703A00601DAA047090A04 -:10313000007E8800300002C0000C3047038D000381 -:10314000C04000243000F0470308FB470240000362 -:1031500040F54709014287002047040C0C3020202D -:1031600000013A3C20201004470708040010000426 -:103170005AA102004000A04705603EEFA547050C9C -:10318000300010070D0803C047020247070520471B -:103190000334E001470B1C05A047020247020C3034 -:1031A00020040500723C20001047060450800447AC -:1031B000022021EEE20200401810470403601FAA1B -:1031C000F5470202C0000C30000405470203C000AE -:1031D0001004470542D8BD2C00103C7670424702CF -:1031E00040008047030102C02C00F047050C30204E -:1031F000000800723C2000A02078470330001047F0 -:103200000501DCA302304702804703C001C0470824 -:103210000C3000040B470203C00052144704300076 -:1032200008033C4702343830A300204001470481A2 -:1032300001C2C0470C0A723C204702A03BF9014082 -:1032400001404705202C0CF0404703078402470348 -:1032500006DEF5E0470A100803C10001547D69A7A6 -:10326000004040470402046000F047028050003CE8 -:10327000470402BCF1D010470601804702723C208F -:10328000060024017F7440014047030647034EA512 -:103290000018470215A3C2806002002C91A0470BC2 -:1032A0000803C047023C2E78A01002404707D055C3 -:1032B000470490003C470201401DF3F047070347D5 -:1032C00003121540040036687B64403047040CA0AC -:1032D00047020DD1000147020723C24702034614EB -:1032E000ABC420470A08154004023C004A5800704D -:1032F00047040C470203DFE24705803C47020266B1 -:10330000C20BC208470A03BC1047023C294B744059 -:10331000002847042000281EF04000021801A3C224 -:103320000004016060AB470C043C4703164268A0F0 -:10333000004047074010F04702050800803C470264 -:103340001403B4C70010470A03BC100C4702035D06 -:103350007A014706A00E60FEA047023001A7A3C273 -:10336000000803469E81A020470A043C000447024F -:1033700002C85A0847071E03C0F54705803C4702AC -:1033800001601D03F0470C295047021E01D95B1054 -:10339000404705800423CCA10247031123C2470400 -:1033A0001D95E0470B90168047021C314CA547083D -:1033B0000202C002470480003C47043EE1D0470AB5 -:1033C0001B47051F470601C047030620E8A3024725 -:1033D00003B325ED0004016640ABC42047090A474A -:1033E000050247060147052670034704AFB694015E -:1033F0000A0242B507C004471064F850104708702D -:103400001AA302B10001D04703C00003DDB5E047B5 -:103410001164005047083443D05300228051A04724 -:103420000380D6C76EE1D04709081E5BC81047036A -:10343000040AF0404709FE00404702198F3040025D -:10344000000166A5E0470A0B00121E8060001428E8 -:1034500000F04702400007470403F0A0408002004C -:1034600007B0470301C24E9447080180470506474D -:103470000308F0401847021000062000020C0A0062 -:103480001947020B3A81816002E68405A0204708B3 -:1034900001470810F04704B7470240043CC005004C -:1034A00002801087400101000260140240044706B8 -:1034B0000300080036C9580C0034001AF0403047A9 -:1034C0000218000CA14000FE504A024702012028C9 -:1034D0008047030480D0A047060100030A7A054212 -:1034E0004702144702F000304702FB00044080030B -:1034F000F0804047044028800003400100D080470E -:103500000603000500DE11D84702366C1AF060474A -:103510000308000D8000020C0A403008000B38013F -:10352000010002000D85E020470601000F0052E077 -:10353000404702142800F04703B007000902470281 -:10354000C00548200400074141044702401602E03C -:10355000470A08F680D2100E47021AF040470318B7 -:103560004703200400A5400080501175D04703C0D8 -:1035700067A0140447080B091B28C020470310F05C -:103580004704C900400E3C38C05A480100D887B5EE -:10359000F8470303AEE80080470808019F1414476A -:1035A000041AF040470280180400201E2200A541A2 -:1035B00030005C037B08084702E021CA470A050C7B -:1035C00056788240470310F04703F0E53C4703403C -:1035D000C05A40104702A7C30C04000262DC054732 -:1035E0000A0D81D6C9504705A54047029898470261 -:1035F000A004301C0540800018E0470560068501E6 -:103600004709050A32054A4704405A47039993344B -:1036100000400201C00A40470210B04703084702B9 -:103620000105470A0581FE51D047040AF0403000E9 -:10363000901000040004027E5F40100050E1B828A2 -:103640000400E0400FF812470A0B53E04A4705F028 -:1036500000300070FD3C04000220E00F48100079AB -:10366000098168040002600CC4471114004EA04098 -:103670000240470607C850C04703904705407EC731 -:103680000008470F020010A040804047063BC005DD -:1036900042470210D04705235DAF01470E0647079A -:1036A00044470306470203C850C0180018033E01F0 -:1036B000006000063DC0471104470542470504026B -:1036C000C005404702188FBC012047030E824716F1 -:1036D000100001C000044702674850C0100001E11B -:1036E00030104703604718300001470204470223A7 -:1036F000E005401047020DE047160C470705470359 -:103700000C0006674850C0F0001017B201284702AD -:103710000780471008470B08470222E00540204772 -:1037200002E7C00147234003E850C14703F04704C4 -:1037300002662F90471B10470203C005404703D085 -:10374000470401442FB040471D736850C010470222 -:1037500019F56C47020366EED0471E42C0054030A3 -:103760004702ADE11C47020142AEF08047024047EC -:103770000E0447090226036850C00007101DFB080D -:10378000084702068DE80020471038470A0403E086 -:10379000054040003085830C470508470F04470B60 -:1037A000040020736850C1104702804703C00060C6 -:1037B000208E5420470D04470B04000442E00540CE -:1037C000100051E04703804142650DA0470372009D -:1037D0009011010101470B104706404705014705BD -:1037E0004000024706084711204706804705024768 -:1037F00012084722010002470404471110470640FF -:1038000047050147054047031600014706044722C4 -:10381000020020000847020847031047243C000C20 -:1038200047063047688010470402004047040801FB -:103830004703020040470408014704302004474D75 -:1038400001470604472103008047050247030C4750 -:103850001F1E472D024703404705014717404705F4 -:103860000147060447030301470604470610471DA6 -:103870008047050247110C30472C0C3020472B0C99 -:1038800030472C0C3020472B0C30470E01470C10D2 -:1038900047100C3020470D02471D0C30472C0C30D0 -:1038A00020472B0C30472C0C3020472B0C30472C5A -:1038B0000C3020472B0C30472C0C3020472B0C3081 -:1038C000472C0C3020472B0C3047130800040020F5 -:1038D00055470880004247080C302047120C002052 -:1038E0000168554047074002004047070C30471524 -:1038F0000400200F470B0205500447040C302047FA -:1039000014260168F0C04709661E855447050C302F -:1039100047180F470DF08047040C30204715016809 -:10392000F0C0470A168F0C47050C3047150C4702AC -:103930000F470DF08047040C3020471501E8F0C018 -:10394000470A1E8F0C47050C3047150C20200F47E7 -:103950000B0200F047050C30204714266548F0C0E4 -:103960004709601C8F0C47050C30471620200F4775 -:103970000A460200F047050C302047156548F0C0A4 -:1039800047080207548F0C000147030C3047180FFB -:10399000470DF047050C3020471206470201C8F0DA -:1039A000C047076047021C8F0C47050C3047170CB7 -:1039B0000F470503C01247020401B0F047050C3061 -:1039C00020471501DAF0C0470423C0470307148FCE -:1039D0000C47050C304718F04703D9B0A4470280C4 -:1039E000470A0C30204714060008F0470350A78010 -:1039F000020040470A0C30471620300F4703D90019 -:103A0000A047040247080C3020471505780F80476F -:103A10000250A780024703404047070C3047152457 -:103A2000201B0F470528470D0C3020471501F80FC4 -:103A3000804703078002470C0C3047170D0F4704DF -:103A4000B020470D0C302047140661790F804703E2 -:103A5000078402470C0C30471504200D0F47035014 -:103A600000A8470D0C302047142004F80F804703AE -:103A7000070002470C0C30471506203C0F47035047 -:103A800000B4470440050A088047040C302047145E -:103A90003501C90F8047030100024702026000E5BB -:103AA0000447050C304715366C1C0F47035000289F -:103AB0004707588047040C302047141428E80F802B -:103AC00047030704020060470214E05447050C3026 -:103AD00047166D4CA547035000204704C0040A0850 -:103AE0008047040C3020471529415A80470387003E -:103AF000024702024702A504471B10044000F0479A -:103B00000301A9A00004800042471F207808F04765 -:103B1000020800BFA0214040020040471D100420C1 -:103B200060A54702404702A047020100400205503D -:103B3000471C2007605A8047030780024702026043 -:103B40001E8556471D0340A547035190B04702010B -:103B50004704F0471D01605A804702D9A3840247F9 -:103B6000020800168F0E470E01470D1C004FA54797 -:103B700004A0703C4706F0470E01470D2601E15AAC -:103B800080470219B5F43E47041E8F0E471C1C20C7 -:103B9000390F47031900B44704028200F1471C267D -:103BA00005580F804702F9E38002470360148F0C29 -:103BB000471C1C3C2F0F4703100034470320020210 -:103BC00000F1471C2061DB0F8047028003000247A1 -:103BD0000306548F0C471C3C381C0F4702180090FA -:103BE000F03C4703028000F1471D04C80F80180015 -:103BF0003DA5E03E006047021C8F0C471C3C380C82 -:103C00000F47031000283C47040190F1471D25CAC7 -:103C10000F8047029005643E47041DBF0C470E0409 -:103C2000470222CF4A6047050400042047031000E2 -:103C3000188F62E1004100421C05A0470E040004F9 -:103C400067FB404047050400360447031000981DF9 -:103C50006CF1024002002C470210014706044702A3 -:103C600008284702100025F03C600042470410027B -:103C700032DAC34000400185414100050003810064 -:103C80005004470A16A804470206067A7C424047B9 -:103C9000060402DFD348000459F0728147041EE095 -:103CA000504710023FE8F24707801C7AE0A5470220 -:103CB00020080F3001400001C25C05A00401470D3F -:103CC000100E62DFFA4707102026FC500100C018D2 -:103CD000E13A01000147026E4713214FF547070102 -:103CE0004221D90740007D000F2811000101C026A4 -:103CF000020414471006EBF64707011C05FD03619B -:103D000000021987F69D000247021F800420470821 -:103D100009470408000628E0182040470408470225 -:103D200002DFE3402000D80B656100940142B50139 -:103D3000470A0847040C002E07FA040247050C0046 -:103D4000042BCCC36070009187BA8900CA0200219D -:103D5000C20020470F3C0010A0470208470601F0B0 -:103D6000D3410147029FF95100040002B680404749 -:103D700011014EA0470902E8C342020070153EC17E -:103D8000470306D5A0A047100642D0B94047070414 -:103D90002830A047031805732147036037A1470F58 -:103DA00006000E3BDCB1624705064703DAA00018A7 -:103DB000007C0FBB09006001E035D047020A4708CC -:103DC00010470602CFE0470D0400190F21194702E2 -:103DD000014247040447080E47040E47021BD04720 -:103DE0000930470304059981720D470202470B030E -:103DF000000980920447047F60C247060401420321 -:103E0000400500100801804703400340470A0300B3 -:103E10000F80174705386CC302470504009E000E4B -:103E20000A20100058F047034047101814470403B5 -:103E3000F00347081E3C30A008470290E77F214767 -:103E40000306EED4A447090100DE8047030402DA2A -:103E5000430247073E056EA04703B819FF414702DA -:103E60004002B6891447090908941047034040F0FE -:103E7000024707802C3F5ECB470308A983610147B7 -:103E80000202020F470A0801F7470280470260C892 -:103E90000A0247060E20060CC7104702781BEB01EA -:103EA00008084702468F0847094B001640470201A1 -:103EB00046387B0201000247053C3C100A470330AC -:103EC00009807447020363C00F470A20001220478D -:103ED00003BC7AF947030D47052005DA0A2002479B -:103EE000020772B4080009405E8F081047080B0DE6 -:103EF000941A4247033C00A008470508001C294BC0 -:103F000008002020D005414702800002C20F470A66 -:103F1000080B06A590470369CAA02047050C00209E -:103F2000020C042032801809200008C047025C8F70 -:103F3000084709200E1000084703243004014705F4 -:103F400004470220CCA141470218B0C02847040210 -:103F50000F470C17900080001429EA04024705045B -:103F600047020378D1408000D005782847041C8F91 -:103F70000847090700D04703401472D94703014797 -:103F8000050401CA5B4703110567B8404702428038 -:103F90000F47080180000A760080808000767B80D1 -:103FA0002000024703060E14000DA708180159C58A -:103FB00073BC006047024C8F084709050EDF008084 -:103FC0004703237B470A26CEFF094000D9E3384147 -:103FD00047072047080518DA47053CE980200038E4 -:103FE000470603DBF7470338A1380147040C0008F4 -:103FF000004047023500501347030403C047022620 -:104000006D7040084047040400047C7005470308B5 -:10401000ABA39C20470203010F040040014020E0B5 -:10402000A00147030683D047022428DA501247052F -:1040300004002423EC4702800071B17EBD4040475C -:1040400002418F044703467E0D34470201470604B0 -:104050006410A04707020036DAF0484702500743D1 -:10406000000147040F04470201639CEC342000095F -:104070008047052065DEA0470901FFF4404000D9D4 -:10408000A720000A47020700CF044702024034017C -:10409000A04708022666500147094370A047035015 -:1040A0000FA40020470204010F0447042FE0022060 -:1040B000470826760E022247070621EC4704980F90 -:1040C000F420470303808F0647020140010A470698 -:1040D000154001800C62CC4705C047043D40E740D5 -:1040E000C10050A7030020470202410F044702020B -:1040F0000284AA47055A155001002026FE084704ED -:104100001000804702077CD34A020081D772000C5E -:104110000801E001CF040080004247050180080348 -:10412000C047020C41DD20470608002E39D00A475F -:10413000030800C02820470220010F0400C002002D -:104140004047040D98523C300240002ACF47070CEC -:104150000006036C470430073028010001C000EF5F -:10416000044703029E4705080803C047021401E004 -:1041700024001047061C207000414703A0C14702DD -:10418000404702010F04470437AA010447021952AD -:104190003C30470302DA10023002470604EE806327 -:1041A000470315310028C0470201EF04470340359B -:1041B000040010470403C0470303F04703404705CA -:1041C00006034A1047030803240020470202400F59 -:1041D0000400604703880121804702723C30470396 -:1041E00068EE402200404703060006021E20001829 -:1041F0000031EF32002160470201AF041047022373 -:10420000CDE0470603C14704EF47034047051401CB -:10421000E04705A5FBE0470306010F0447036004E0 -:10422000C0804704723C304703027D80470240470C -:1042300006036A0A4703019B2FF483470203808F1A -:10424000048047060100010E0803C0044702404BEA -:10425000F1493000080F28044710140500044706F0 -:10426000030008187A3C300447023BCDF040700050 -:1042700010470204470610470280470503A18A47FA -:1042800004028047060803C447050A40800070FD09 -:1042900024470280470D02804705028047040180C1 -:1042A0005A3C3040000600FA0A40470380470201AA -:1042B000470D0142C0470401404704038F470203F2 -:1042C000C010470238ED0BC10002008D28470A01DB -:1042D0000141840001C027824704068047065A3CFA -:1042E00030400006027A05490005470204000E47E7 -:1042F000092A81A047031F9380470950470203C042 -:1043000010002401505F4047027003004047023014 -:104310004703044706400002800F000847082000BA -:104320007A3C30000E00637AA540470280470504BE -:104330004705184704C00060008F000447091808AB -:1043400003C05800042C70E3404702909D000847CA -:104350000E01C00300F04709018B523C300C002CC9 -:1043600003FCC34200380008000C470E020014E0B2 -:10437000F847043FC04704080803C05047020170D3 -:104380000500300031F1000C000440470B40022EC4 -:10439000CA5047041DD080470319523C30204702C1 -:1043A00002DA470230008090000400207047057850 -:1043B0004706065415A8470803470303C0470204ED -:1043C0002C6A81400002000747030447052800F3D8 -:1043D000C2BF800003D280C0F0470701804702724D -:1043E0003C3006010C026F50401801470306470697 -:1043F00018180007E2954703400EE0F847095000FF -:104400000AC0470401DA3C47024010CB470402C00F -:10441000054442800847060402470222470820005C -:104420007840204703016D34470418470401485081 -:10443000400100704706071C000847037200701116 -:10444000020101470802470608470402470A0447D9 -:104450000220470680470C0147060447040147082D -:1044600002C0470310471702470608470B084716C4 -:104470000247060847040247060847062047068009 -:10448000471F0300070004470B7C471D03470F6CC1 -:10449000470CA84705024727204708044722024047 -:1044A000C0470408014703020040470408014705CC -:1044B000200447058010474C03470D70471F0147F4 -:1044C00003044709D0470340473A02471208470D03 -:1044D00080470A80000247030247058047050247DC -:1044E0000608470620472204470D401030C04718F1 -:1044F000067B38470240626D6140470383DC054715 -:104500000430C0471803DBD0470301AB2022C0476B -:104510000203C0008047021030C047160103F8ECC8 -:1045200000B001804702016200060083D6000A0045 -:10453000180030C0471703F83001F0470903C20DD7 -:104540001047021030C047160108805A00B014000E -:10455000AA49A16040030083C447030C0030C04750 -:104560001702002D01800800064B6C028003000337 -:10457000C247041030C0470A80470D14EC50104762 -:1045800002A2EDF540470383CC054702800030C00E -:1045900047193AD8F04703A1E420470303C2008B30 -:1045A00001001030C04716010A805F8047080300F1 -:1045B00083C60847032030C047172100281047084A -:1045C000020003C00547031030C0471002470980AE -:1045D000470502470483C647025C470230C0471ABA -:1045E00010470503C0470303C2470280001030C0D4 -:1045F0004717080969470740470383D605814703E2 -:1046000030C04718215D470720470303C0000A4711 -:10461000021030C0471708805F181047071000834A -:10462000DC4703800030C0470901470E227A008032 -:104630004707600003C2470201001030C04717045B -:10464000000A1847024010F1296047060A47033064 -:10465000C0471801504703040F00014047091030BC -:10466000C04717043C585010018010F16A0680477B -:1046700004014702180030C047183C105080470220 -:104680000F00414047091030C04717043C5878A03C -:10469000470210F121404703A906F380004800308B -:1046A000C047183C105847030F00400280002019F3 -:1046B0003FE547028C1030C04717043C5C470410AC -:1046C000F121604047028BACE00A470330C0471835 -:1046D0003C1000C047020F00400280002002A7B536 -:1046E0000A47021030C04717043C5C470410F160D1 -:1046F000470490B788470430C047183C1047040F60 -:1047000047050222EFF54702201030C04717080284 -:104710004B9980470210F16047048BAEF81B4703AA -:1047200030C0470901470D0210DA1047030F00405F -:1047300047030202A5A50A47021030C04717080226 -:104740005A470410F168470320A907D19A020C00C8 -:1047500030C047170122EB470220000F47048000BA -:10476000193EA58002081030C0471708826B470425 -:1047700010F1284704A93CE00A470330C04718114C -:104780005D47040F0040470280022A268000020095 -:104790001030C04717183DC04704169947058001DF -:1047A000B50A08470230C04712028C000800083CD6 -:1047B00020470303096B47053FC1ED9047021030C6 -:1047C000C04709056047081C470260470501801083 -:1047D000F1624702060083DE4703180030C0470934 -:1047E00040470802803047068000010F0041C04763 -:1047F0000303C047041030C047130C7C003047034C -:10480000100080800AA13847020300A940804702B7 -:104810000C0030C0471202C278003047030847023C -:10482000C00AA040470429404703081030DE471063 -:10483000084702404709C060F180470483C4470528 -:1048400030C2470F01140003470A4000F0800008FF -:10485000470203C20D0B47021030CE470908470933 -:104860005C470A2A500447030183C44702404702B9 -:1048700030C2470908470620020247028047062047 -:104880000045A0344702800003C20947031030C02E -:104890000700A0470F804E78470720002A5047099D -:1048A00002470230C04709084708B2427847090565 -:1048B000A03C402847081030C04712800E470C0229 -:1048C0004702808083D6470530C047090847062045 -:1048D00000F24000084704D0002047030240008057 -:1048E0000003C0470202001030C0074703020F78E0 -:1048F00004470220470854050030470680470A1C39 -:10490000000C0030C04704020F080247082000B323 -:10491000823A8020470680470580470410A204475A -:10492000071041F700280010470380A04705080042 -:10493000080379000447095981A50A40470ADFB4F2 -:104940004047057147060800608369D0B0470919E0 -:104950008D0D90470902248380280044090AF9003C -:10496000A0018047030800083DC080E0018020F5D9 -:10497000470306005A64A0470A02E7B3C04000108C -:10498000222FDA11B047050847023C2071B04702D8 -:10499000A0F080038047021A74470B02AA771447DD -:1049A00003014148504702804704304704A01000EB -:1049B0002A57A86047035A44850A470902AA0C02ED -:1049C0002C4702014000D8B000804704204703F183 -:1049D00047030F50F5C047031A640090470A189226 -:1049E000D428000101406A50104707083DC03047F5 -:1049F0000320F5806047035A4488470B341EB46C8B -:104A0000470223401BD8F0470608203C00304703EC -:104A100020F08047041A65010B470940053EC04756 -:104A200004046B8047020800204708144703020073 -:104A30000347020CC5A8000D404707080807C047F8 -:104A400004035810470202003C4708084704404747 -:104A500003332CBB0E008047080112862800014258 -:104A600094DB4703C0470F08470780470A1E804072 -:104A7000470202003B00900080471028470660472D -:104A80000850A55EE060000208BBDA80E04707013D -:104A9000E94E518047061047023C1C07470B85062C -:104AA0008447042A6EF04706744702AA78F847073D -:104AB0002847023C0207804709025A0054700070E0 -:104AC00047038047070108F46A470420F182470240 -:104AD0000240029DE7470280470702A503424000CB -:104AE000304703F000024707980B470420F001C04D -:104AF00047028002830080014708030F52A047034A -:104B00000C3E6A47090294DE18470A5414850A4786 -:104B10000AF0983C470303C360470901541FD0477C -:104B20000990144702904709030F13804702600C55 -:104B30003C4E30000180470614C8470201804703FD -:104B4000E00846005A45E04702184708F0470503C9 -:104B5000C0470A02BC1CD04706220020001A74007D -:104B6000084709030F13A60447020C3C4A98104754 -:104B70000505000294DD51E000100AF1844704436A -:104B8000C48047020C4708F0043C20470203C010D1 -:104B900010804705020001540909470305F0A047AA -:104BA000043C010D0B000C4707030F12806030001E -:104BB0000C3C4AB180C8470715CA5004470220F58B -:104BC00080038003005A65E047020C4708F0044761 -:104BD0000403C010100014470602BC1C084704F070 -:104BE000470302001A54000B00044707030F178005 -:104BF00047030C3C5E47028047060294CDF9F0471C -:104C00000903C4470CF0470503C0470A01541F704D -:104C1000D0470603470203C04708204702030F1688 -:104C20008047030C3C5A0001C8470714FAB8E04714 -:104C3000080183DE47021C4709F0470503C047030C -:104C400014470602BC3CB890470983C0470B02AA30 -:104C500017A06047020C3C5A018047060101E8FF9B -:104C600071C0470507804704010010804708AA0467 -:104C700004470303C010470A542C0847060447069C -:104C800080470C200400200AA85E181400C04704C6 -:104C90000108F54C704704A76000044705080008A8 -:104CA000470A0420001002A810008A0040470404AC -:104CB000006419F8F0000100A0470A08470E083DFB -:104CC00060000447052000083DC047048005E0005F -:104CD000380004C3FCA04711483C00F04708083CDA -:104CE00047062BA342D000043C3E470B020F7847F7 -:104CF0000360083D6010000180470A05804019B03C -:104D000006000600C3C4E04702184707020F084721 -:104D100004083C00F8470B700008470201EB4047CD -:104D200002243C471230083CE070470280470440B0 -:104D3000083DE0304702802845E04704C3C4A9810C -:104D4000470F22083C20080014C04705083C2047B4 -:104D500003C0242BA002C047023C010108470902FE -:104D60000F304704083CE001F04707083C60470368 -:104D7000204009B047023000C3C4A9984709020F78 -:104D80004705083C2001E04707083C20470521EB88 -:104D90004047033C0101470D20044702183C6000D6 -:104DA0008008470405470905E2E0284702C3C5E03B -:104DB000470D3C384702083C20014705024705B033 -:104DC0004703808BA06047033C4713083C40F847EB -:104DD00008083CC047042A01A440470201C3C5A0FB -:104DE0004711083C20F04708083C20B047032050FA -:104DF000A04703043C471202083CC04707010008D3 -:104E00003DE0470501B1404703C3C5A0184710085E -:104E10003C470A083C4702C047033B7C2047033C11 -:104E20000103470D0E010020083C60470380470541 -:104E3000083CE047038400012447020200AA85A140 -:104E400001000C470B028000083C200004470708C3 -:104E50003C2010E147020200B447042A8100080008 -:104E60000447027200901103010147080247060837 -:104E70004704024708078004470220470680470C82 -:104E800001470604470401470D10472A0847160248 -:104E9000470608470402470608470620470680479A -:104EA0001F024703044729034729A0472D10471E27 -:104EB000020040470408014703020040470408017C -:104EC0000780470320044705801047450147060334 -:104ED000470D204718024706034703044709C04708 -:104EE0000340472B38472108470B400080470A8082 -:104EF0004705024705804705024705180847062071 -:104F00002C471101470F04470D400030C0472B10BC -:104F100030C0472C30C0472B1030C0472C30C04722 -:104F20001004471801001030C0470F80471C30C0E4 -:104F3000472B1030C0472C30C0472B1030C0470ED5 -:104F400010471D30C0470A0200140B471D1030C027 -:104F5000470C28E1471E30C0471B04470F1030C0E4 -:104F6000471B0A471030C0470B20280B471D103045 -:104F7000C0470C14E1471E30C0472B1030C0472CEF -:104F800030C0472533069047031030C047242031F6 -:104F9000A480470430C0472B1030C0472C30C04796 -:104FA000253C0247041030C0472304803C144705C9 -:104FB00030C04708804721801030C0472901400099 -:104FC00030C047270147031030C0472C30C0470A84 -:104FD00080471D0A47021030C0472801470330C0F0 -:104FE000471D0300F047070547031030C0471FF176 -:104FF00047070D9A470330C0471702A809704705B5 -:10500000404706010800041030C0471180470501E1 -:10501000544147041000796047050801000800303A -:10502000C047150447023C47060B404703802A81CE -:10503000000847021030C04715020007C058470457 -:105040001008684704AA85E199470330C047183C17 -:105050001047050301428047023C47051030C04716 -:105060001707C04A470410086A06C04702C3C5A014 -:10507000470430C0470F0447053000025A40470537 -:1050800003014047033C47051030C0470F0A47075C -:10509000065848470410086B66804702C3C5E00005 -:1050A00080470230C0470FF047083C100847040B08 -:1050B00041428047023C0147041030C0470F10476F -:1050C0000707C05A38470310002A06C04702C3C466 -:1050D000E018470330C04711C04705025A50014746 -:1050E000040B41428047023C010147031030C04796 -:1050F0000E50470280470506585E71C047021000F7 -:105100002B66C04702C3C4AF00A0470230C0471798 -:10511000025A504705034704013C47051030C04779 -:10512000100180470506584870E0470210002847E4 -:105130000301C3C4E04702180030C0470B083C47D6 -:1051400009010001409847040100034047023C3632 -:1051500047041030C0470B083D6047090447023838 -:10516000E0470210002800404702C3C4E7470E3C56 -:1051700047021010D9470D04470506C04702080230 -:1051800047112000034B470D02470502800010081D -:105190001CE0470D0200084704020020470302807C -:1051A00047041047041BB842C04702108090090210 -:1051B000470B1C470C01470A073E470310149795FD -:1051C00098471301E04707016BCA999047093016C9 -:1051D000B500024713F0470701606999904705063B -:1051E000804702103488470F084704010020470415 -:1051F000040002A8100847030A008364471580478B -:105200000401E04705020202A85C1847030A03797B -:1052100042804718D9F0470702003C1000E04704DD -:10522000704704069E901B47110B4B80E047070711 -:10523000C05C001000040807B0470301A94F800AB2 -:10524000470E053203D42008B002470508003C474A -:10525000020A470240A08047042801DD80470F3042 -:1052600003D56A7880024704300007C05C00044719 -:1052700003A37847051C95471E3C4702A047090E2B -:1052800001AD1A470E0160470378470817C05C005C -:10529000104708100617900A00184707020F470525 -:1052A000A0014C470602900147021000E0470208A7 -:1052B0000B38470534A01B4709920F784705824DEC -:1052C000470601470204000C0010470303BC00041A -:1052D00047020216950A471103D0470220470406E9 -:1052E00004003C2047032020F08047040237800957 -:1052F000471004001A788147050104183C604703F1 -:105300002020F78166800201802CE04703023FC025 -:105310004709024703D070002047040247062020B7 -:1053200020F00142808000138299090200443FC0AE -:105330004709010004001A8180470B01002020F377 -:105340000366C00012A34605471340470A3C4702C4 -:1053500080003020F047051E810029471004021A02 -:1053600001E0470604183C60470420F780E047024C -:10537000101E8CC0470E01470301C009E0284709F1 -:105380007008002060F08044008000084E810A0010 -:1053900080470B02470204021A79F004470B2000F1 -:1053A00020F3800700800004B408471202D050A008 -:1053B0004707603C2047032060F080470250001000 -:1053C000038D471104000A50A0084706183C4047C7 -:1053D000032020F7470410001D80470E01470203F9 -:1053E000C25010A04707229447048060F080470213 -:1053F00002003040C79F470D02800007C0CAD04757 -:1054000007081A9408470201E020F7470303103009 -:10541000941F0E471003C06047020447050C029416 -:105420000050A0006060F047052001ED80471007A4 -:10543000C1EE01E0804705080A94084702010020F8 -:10544000F7470306100014A5470218470B0147024F -:1054500003C040508447060C01680C50A00038601F -:10546000F0470502810D1F40470C02470207C0EAC2 -:1054700000B84705020A0A940100A0470220F74736 -:1054800002084000029EE00A471042A81030800047 -:105490004047112047040B47100AA85A001000C0CB -:1054A000471150471543C010008047083C4704018E -:1054B000010F34028047020142000E47100C3C5A93 -:1054C00001904707083CE04705073140470220812B -:1054D00055E0471123C0003047093C000800400058 -:1054E0008000A0000347160C3C5A00014707083C07 -:1054F0006071804703512047070E471023C04702C1 -:1055000001C047073C20008080000FF0A1404717F2 -:105510000C3C5E4709083C40019047028FA52002E1 -:1055200080470401471123C0101047093C2047035E -:10553000010103B8001000402042E09047100C3CED -:105540004A99804707083C40470402074702200069 -:10555000100056879E471023C0101047093C0008D2 -:105560004702C04702014047031C16B147110C3CDB -:105570004A984708083CC0584702804707102DD07A -:10558000471143C0470B3C47028000404704034794 -:10559000160C3C4E470201804705083CC000100134 -:1055A000470520470718470E43C2E0470A3C201032 -:1055B000470501428047050D47100C3FEE4709089B -:1055C0003CE0984706028047050110470C40471A07 -:1055D0006300294000100020470B704708804711E6 -:1055E0001200A9408018470D4002470408472720B1 -:1055F00001470438471206470618004047250300B4 -:105600004003C20010472701470283D60018470E07 -:1056100030470401470C04470540471B80470B02F5 -:10562000470402470240470F0F06804703083C002B -:105630000147050285470B044702040281C0470960 -:105640000102007A804703083D40008047040102C0 -:10565000470B074703A804B18A470D280002083CFE -:1056600020014702C0470408403C00088000802A0F -:105670005034024702840281E108470C06470308C0 -:105680003C6001E000C047034000183C605010142B -:105690008025A04702404702E804B981470F50081F -:1056A0003C470B3C20014702402AA0404702010032 -:1056B0000280C0471050083D607A000180470518FD -:1056C0003D4000A001002AA73047020405E804F08D -:1056D0000A2018470C7000483C470702700023C09E -:1056E00000700447022E7B384703060294E00E4701 -:1056F00010083CC047090BC140000A47022240285D -:1057000000054702E826B00147031006B0470480B1 -:105710007E80470301541847080800B01908D04795 -:1057200005034047022A81470580BEF198470202DF -:105730000276803847022295EA01E10080470401A1 -:105740000A75E971F0470401E04002012A96E780FA -:105750004705D010470280AF0654300002017C0B91 -:10576000470601470202F43C47048050BC604702F0 -:105770000430E6D047048286B01C470305337668C0 -:105780004703147D470780010141791808C0004094 -:10579000A187E0470391E7B18047032004A14704B4 -:1057A0008412822847020968090880470702BC2A38 -:1057B0007180470401E047033E820919470380A531 -:1057C000E847030200F20E6C47020A940139E1C077 -:1057D000470714FA01C04705E047033E94C0094754 -:1057E0000303C14705AF0E42600202096808704713 -:1057F000080154087047034000FC470515910F4706 -:105800000343C4A0470304053A462805000A940050 -:1058100080470802955E8008000400116447055423 -:10582000A8470403C10100180008550A47042014C2 -:105830001C470601470202BC3A70804702050080FF -:1058400002470303C0001E470343C5AD9A4703A5A3 -:105850001F86470302BD6950A0470580470215EC2B -:1058600080B047020501214040470283D6000A4725 -:105870000303C0050A4703403604204702083C20C2 -:10588000084702804702024703D82F08F04702A8C2 -:10589000503C002047023C00D59B470343C488018D -:1058A00047020220D2A0644702083CE0504702C0F1 -:1058B00047040148D47A701000042475F847043C6A -:1058C00015C38A470303C0470310000403CC0040FC -:1058D00040083C4708040001C019D847030500BC34 -:1058E0000020008028B5918A470343C5A01E001000 -:1058F00042205220600050283CE07980018047041B -:105900000102E8EBB80001800007A1600006001466 -:1059100007E947021847023D000D000200050AC0D2 -:105920003C47047C10F0470702A809D0A540002C92 -:10593000BD746247022030CE8D47044021A01B4732 -:1059400003A557542802002040F980B047040147BE -:105950000202955D800847022C3DF7604020002040 -:10596000F4950A4704720080110062002F824702FA -:105970000103470410000800101002020400220076 -:1059800020200008042404470708044703200400DB -:105990002020202400082C040C0C47030410100CB9 -:1059A000001000084703080847030830003030049F -:1059B000002226060408000404000447042008000E -:1059C0000C000828000C000800280800100808280F -:1059D00008470228282847020C101010240002242F -:1059E000242C2C0400282C21010800113010200048 -:1059F0000422022247032020203004083010001027 -:105A00000800040404040004000447060800040815 -:105A10000808470208470224240400042C2C2828E4 -:105A200047022101211004083010201047022000F5 -:105A30002020080420202000044704040800040457 -:105A400004040010101010100006081008080302CB -:105A50004702040408020424242404002C2C2820D7 -:105A6000020021012100020A200028000802282843 -:105A700020300400303010200002470204000A02E7 -:105A8000040400140006101010080447020808005F -:105A900008020C040C2C02022C2C2C2C0006282AA8 -:105AA0002A470208220022000C022002202047027E -:105AB000202030300202507040000A0E0C080C0406 -:105AC00047030400100808101002120400022020EE -:105AD00020080824242400020247040A0E47030277 -:105AE00004470206240608082C2428280202201055 -:105AF00030100E0A201028084702280020200A082B -:105B0000203030180400021A0A040A0A0C040C0C93 -:105B1000470208082808020C080828080C02280A6E -:105B20002802000808022008080020282010000C85 -:105B30001010041008020404280C04082820212155 -:105B4000080031303020060E222222200E06204787 -:105B50000220040C101010000A4702040404000C78 -:105B6000040447020A02470208000608080A02085D -:105B70000800060406040206082A284703212131EA -:105B800030060A30302020020220202000080400C5 -:105B90002010000600100010000C0400040404068D -:105BA0001614101010020618100808014702080405 -:105BB000040A020404040604082A0C2A220A022306 -:105BC000232322000822222A200A02282A30100435 -:105BD000001010000802020008080008000C040C65 -:105BE0000C0206180818180400081808080A020C05 -:105BF0000C0C0600080E04220E0A06222A2222009D -:105C000008222222220C022222222202003200102A -:105C100010000240504000020C08080C04080A045E -:105C200004470308101012120400024702200E0459 -:105C3000240424200C0E02000202020C02020202C2 -:105C400004020602060602080A262A08080B30305B -:105C500030300608202028284702820080010300F7 -:105C600002282828000208082818180E091A1A0AFB -:105C7000080208080C0C0C00020C002208000E029E -:105C80000A22020603020222020808020222020875 -:105C90000822203010020410140404080004000834 -:105CA00008040808000121020011301020080E022B -:105CB0002202000A084705021000101002470210D5 -:105CC00000040A0C04040006080802000A02060088 -:105CD0000A0A020E0A000606060200040A0A0A025E -:105CE000020013011130040A0030002002470508A9 -:105CF0000610470306470502081000044703141462 -:105D000010100002181018180147020804060200BB -:105D1000060606020C080A0A0A00080A0301032004 -:105D2000470202200A020A001A0A1A120400081A7C -:105D300008080002080808080800080C0C0C0204F7 -:105D40000C001810040010180010000204060606CB -:105D50000808060202000A0C0002000247032000A5 -:105D6000220C02000212000200121212024702402C -:105D7000424047020C0808080400020404044704D7 -:105D80001012120400124703020004060602470222 -:105D9000020202024702024702060402040606004B -:105DA00002000A021A0A00011212003204470220FD -:105DB0000820000208080808020008000810040370 -:105DC000121A120847040402020206060202000424 -:105DD0002202020006010002000247022200020A1B -:105DE00047023A2A321A020404120404470408083B -:105DF0000400080001010200111010470204020211 -:105E0000024708024702101002001010001000069E -:105E100004060602470202020A000600080A0402FB -:105E20000002040400020004080A0A10020013130E -:105E300013020447020247061047020647021000F9 -:105E4000060010001047020A4702144703141410FA -:105E500004000218001818000110120616020006AD -:105E60000602000400080808470202010101470475 -:105E70001018100200181A1A0A04000A0208020078 -:105E8000020008000847040404020404040010048B -:105E90000010101210000216060606470302470201 -:105EA000020408080847061004021000101002003F -:105EB000100202020400020200020008080808089A -:105EC0000002040404044704120204001210101219 -:105ED00002000616060247030247060400040004F7 -:105EE00004001402001818181A0001120202020419 -:105EF00000024702080002080800080247050401E2 -:105F000012021210470210120612000206060006C4 -:105F1000000447022000060120002047041018104A -:105F20004702383A320A0204060606044702110103 -:105F300062003F8247020103470802470802470602 -:105F40000101470620204706202047060420470677 -:105F50004004470740470E858447070147068047AE -:105F6000088047060202470602470802471E044708 -:105F70000804471E0404471E204707202047062028 -:105F800020470601470801473601014716424708E6 -:105F90004247060202470E212047062021470610ED -:105FA00020470614144706505047061010470610A5 -:105FB0001047069511470610944706909047061023 -:105FC000104706021047060202472604044726042B -:105FD000470804471E204707202047062020470681 -:105FE0000101472E01470801471E42424707024769 -:105FF0000630204706303047061111470614104777 -:106000000650144706105047061010470611104757 -:106010000610114706949447061090470610104743 -:106020000612104706121247061010470710470EB7 -:1060300004470804472E0404472E20204706212049 -:1060400047070147260101470882008001034720D6 -:1060500052024706305247063030470610104706B6 -:106060001110470604154706404047160101470E28 -:1060700084470804470E1202470612104706101202 -:106080004706101047061010470604144736044709 -:106090000804472E202047060121471E014708011A -:1060A000471610470712104706301247067272470C -:1060B0000710470E0447074105470740470E0147B1 -:1060C000080147160404470602470802470E020269 -:1060D00047061047071010470610104706101047D4 -:1060E0000710476621204707214716010147161070 -:1060F0004707101047061212470620304706422075 -:1061000047074247164140470701470E010147260E -:106110000202471602470802470E104707101047B1 -:10612000061010470710475E204707212147081136 -:106130000262002F824702010347081B1B47041F0E -:106140001B4704041F47101B47051B1B47041F1B4D -:1061500047041F1F47101B1B47041B1B47041F1F1F -:1061600047041F1F47051B470A1B1B47051B470406 -:10617000040447041F1F47051B470A1B1B4704E471 -:10618000FB4704E4E447041FFF47051B470AF919CE -:106190004704E4F94704E4E44704F7F747051347E5 -:1061A0000AF9F94704E4F94704E4E44704F7F74738 -:1061B00005F3470AF9E9470414FD47041E1C470488 -:1061C000F7FF4705E1470AE9E947041CFD47041EBC -:1061D0001A4704EFFF4705E1470ADDC947041EDD02 -:1061E00047043E3A4704BDBF4705A9470ADDD947E2 -:1061F00004A2FF4704A6A24704BDBD4705B9470A4C -:10620000FFDD4704A6FF470484864704BDBD47045D -:1062100004BD470AFFFD470406FF47040404470482 -:106220003D3D4704043D470AFFFF470406FF47047E -:1062300004044704FDFD470404FD470AFBFB470532 -:10624000FB4704244705FDFD4705F9470AFBFB47CB -:1062500004FBFB4704FFFB4704FFFF4705FB470A1E -:10626000FB224704FBFB4704DFFB4704FFFF470516 -:1062700022470A22024704FBFB4704DFDB470426D0 -:10628000FF470522478002820080010347022C2C31 -:1062900047041F3E47041F1F4704CCDE4705C8477D -:1062A0000A3E2C47041F3F47049F1F4704DEDE477A -:1062B00005C8470A3F3E47041F3F47049F9F4704C6 -:1062C000DEDE4705DA470A373647040137470489D7 -:1062D0008147045ADA470552470A76364704C1F621 -:1062E0004704A9814704FAFA4705D2470A7474475C -:1062F00004C1F44704A9E94704F2FA4705F2470A42 -:1063000074744704E5F44704EDED4704F2FE4705D5 -:10631000F2470A7474470427744704CF8F47047602 -:10632000FE470570470A747447041F7447049F9F13 -:10633000470474F6470574470A746447041B74479E -:10634000049B9B470476F6470574470A6060470440 -:10635000197047048999470476F6470576470A601D -:10636000604704096047048989470476F647057643 -:10637000470A64644704096C47048189470476F638 -:10638000470576470A76764704097E4704818947A0 -:106390000476F6470576470A76764704297E470451 -:1063A000C189470476F6470576470A777647047F22 -:1063B0007F4704F7FF470477F7470576470A77607A -:1063C00047045F7F4704B7FF470477F747056047F7 -:1063D0000A612047045F7F4704B79F470461F7477E -:1063E0000540478002110362003F824702010347D4 -:1063F000838282008001034783822244B201060027 -:1064000047000000000000000000000000040240FF -:10641000000400000A00090000140240000400000B -:106420000A000900000C0240000800000B000900EF -:1064300000080240000400000A00090000140240A5 -:106440008000000007000900000002400200000078 -:1064500001000900001C02402000000001000000B3 -:10646000001C024008000000010000000000024083 -:106470000008000001000000000000080080000883 -:106480000000010800800108000002080000040864 -:106490000000080800000C080000100800001408A4 -:1064A0000000180800001C08000008001000180078 -:1064B00020002800300038004000480050005800FC -:1064C000000000006410B71DC8206E3BAC30D92618 -:1064D0009041DC76F4516B6B5861B24D3C710550C4 -:1064E0002083B8ED44930FF0E8A3D6D68CB361CBEC -:1064F000B0C2649BD4D2D38678E20AA01CF2BDBDA0 -:106500004932435F504D49435F4D414700537475D5 -:10651000636B20627574746F6E2072656769737443 -:10652000657220697320696E76616C69642C2063E2 -:106530006C656172696E672E00427574746F6E20AF -:1065400069642000697320737475636B2100427560 -:1065500074746F6E20776173207075736865642042 -:106560006F6E20626F6F742E20427574746F6E2090 -:10657000636F756E7465723A2000446973706C6164 -:106580007920627573792D776169742074696D65FE -:106590006F75742065787069726564210046504794 -:1065A0004120636F6E66696775726174696F6E20F2 -:1065B0006661696C65642E00446973706C61792052 -:1065C000696E697469616C697A6564206166746575 -:1065D00072200020726574726965732E00446973BD -:1065E000706C617920696E697469616C697A617433 -:1065F000696F6E206661696C65642E004352455375 -:106600004554206E6F74206C6F7720647572696ECC -:10661000672072657365740043444F4E45206E6F6A -:1066200074206C6F77206166746572207265736583 -:106630007400435245534554206E6F742068696757 -:1066400068206166746572207265736574004344E6 -:106650004F4E45206E6F74206869676820616674CC -:1066600065722070726F6772616D6D696E6700454B -:106670006E61626C696E67203676362028446973D5 -:10668000706C617920564444432900456E61626C08 -:10669000696E67203476352028446973706C61799F -:1066A0002056444450290044697361626C696E67E6 -:1066B000203476352028446973706C617920564403 -:1066C0004450290044697361626C696E67203676B4 -:1066D000362028446973706C6179205644444329FC -:1066E000002E2E2F7372632F647269766572732F7A -:1066F0006932632F6932632E63002E2E2F7372630B -:106700002F647269766572732F6932632F69326301 -:106710005F68616C2E630046617374204D6F646521 -:1067200020506C7573206E6F7420796574207375BA -:1067300070706F72746564002E2E2F7372632F64F5 -:106740007269766572732F7065726970685F636FC6 -:106750006E6669672E6300616464726573732000FE -:10676000206973206F75747369646520737973741D -:10677000656D20666C617368006661696C65642094 -:10678000746F20657261736520736563746F722026 -:106790000050726F6772616D206661696C6564207C -:1067A0004000496E76616C6964206669726D77613C -:1067B0007265206465736372697074696F6E21001D -:1067C000436865636B73756D6D696E67206669728A -:1067D0006D77617265207570646174650043616CEA -:1067E00063756C6174656420636865636B73756D54 -:1067F0003A2000496E76616C6964206669726D7733 -:106800006172652043524320696E205350492066CF -:106810006C61736821007072765F65726173655F89 -:106820006F6C645F6669726D776172650070727615 -:106830005F77726974655F6E65775F6669726D77A1 -:106840006172650057652772652064656164004365 -:106850006865636B73756D6D696E67200020627982 -:106860007465730D0A00436865636B73756D202D45 -:106870002077616E746564200020676F7420004F7C -:10688000757220696E7465726E616C20666C6173DE -:106890006820636F6E74656E747320617265206228 -:1068A00061642028636865636B73756D2066616938 -:1068B0006C656429212054686973206973207265AE -:1068C000616C6C792062616421004F757220707276 -:1068D0006576696F7573206669726D776172652080 -:1068E000757064617465206661696C65642C2061F3 -:1068F000626F7274696E67207570646174652E00D2 -:106900004E6577206669726D7761726520697320C4 -:10691000617661696C61626C6521004C6F616469CC -:106920006E67207265636F76657279206669726D35 -:1069300077617265004661696C656420746F206CD4 -:106940006F6164207265636F766572792066697223 -:106950006D776172652C20737472696B65206F6E40 -:10696000652E2054727920616761696E2E00466140 -:10697000696C656420746F206C6F6164207265635C -:106980006F76657279206669726D776172652C2009 -:10699000737472696B652074776F2E20547279203E -:1069A000616761696E2E004661696C656420746F71 -:1069B000206C6F6164207265636F76657279206602 -:1069C00069726D776172652C20737472696B6520D2 -:1069D00074687265652E20534144205741544348E2 -:1069E0000048415244204641554C54006578697432 -:1069F000207374616E646279000D0A0D0A0D0A003D -:106A0000E29688E29688E29688E29688E29688E2A4 -:106A10009688E295972020E29688E29688E296880A -:106A2000E29688E29688E29688E2959720E2968838 -:106A3000E29688E29688E29688E29688E29688E274 -:106A4000959720E29688E29688E29688E29688E218 -:106A50009688E29688E29688E29597E29688E29692 -:106A600088E29688E29688E29688E29688E2959790 -:106A700020E29688E29688E29688E29688E29688F6 -:106A8000E29688E29688E29688E2959700E29688F8 -:106A9000E29688E29594E29590E29590E29688E2FB -:106AA0009688E29597E29688E29688E29594E29538 -:106AB00090E29590E29590E29688E29688E295972A -:106AC000E29688E29688E29594E29590E29590E2CB -:106AD0009688E29688E29597E29688E29688E29513 -:106AE00094E29590E29590E29590E29590E2959DE2 -:106AF000E29688E29688E29594E29590E29590E29B -:106B00009688E29688E29597E2959AE29590E295CA -:106B100090E29688E29688E29594E29590E29590CC -:106B2000E2959D00E29688E29688E29688E2968851 -:106B3000E29688E29688E29594E2959DE29688E254 -:106B40009688E29591202020E29688E29688E29548 -:106B500091E29688E29688E29688E29688E29688A4 -:106B6000E29688E29594E2959DE29688E29688E224 -:106B70009688E29688E29688E295972020E29688A9 -:106B8000E29688E29688E29688E29688E29688E223 -:106B90009594E2959D202020E29688E29688E295E1 -:106BA0009120202000E29688E29688E29594E29572 -:106BB00090E29590E29688E29688E29597E2968830 -:106BC000E29688E29591202020E29688E29688E27B -:106BD0009591E29688E29688E29594E29590E29506 -:106BE00090E29688E29688E29597E29688E2968807 -:106BF000E29594E29590E29590E2959D2020E296B0 -:106C000088E29688E29594E29590E29590E29688E3 -:106C1000E29688E29597202020E29688E29688E224 -:106C2000959120202000E29688E29688E2959120B6 -:106C300020E29688E29688E29591E2959AE296881B -:106C4000E29688E29688E29688E29688E29688E262 -:106C50009594E2959DE29688E29688E29688E2967F -:106C600088E29688E29688E29594E2959DE296887D -:106C7000E29688E29688E29688E29688E29688E232 -:106C80009688E29597E29688E29688E29591202090 -:106C9000E29688E29688E29591202020E29688E2AA -:106CA0009688E2959120202000E2959AE29590E264 -:106CB000959D2020E2959AE29590E2959D20E2959F -:106CC0009AE29590E29590E29590E29590E2959007 -:106CD000E2959D20E2959AE29590E29590E295905A -:106CE000E29590E29590E2959D20E2959AE295904A -:106CF000E29590E29590E29590E29590E29590E28F -:106D0000959DE2959AE29590E2959D2020E2959AD4 -:106D1000E29590E2959D202020E2959AE29590E2FE -:106D2000959D202020004C617374206669726D77F8 -:106D300061726520626F6F74207761732073746174 -:106D4000626C653B20636C65617220737472696B61 -:106D5000657300486F6C6420646F776E2055502017 -:106D60002B204241434B202B2053454C454354207C -:106D7000666F72203520736563732E20746F2066F2 -:106D80006F7263652D626F6F7420505246004669C2 -:106D9000726D7761726520697320657261736564D5 -:106DA000005761746368646F67206361757365641D -:106DB000206120726573657400536F667477617229 -:106DC00065206661696C75726520636175736564C1 -:106DD0002061207265736574004661696C6564208A -:106DE000746F207374617274206669726D7761725A -:106DF000652C20737472696B652074687265652EEA -:106E0000004661696C656420746F207374617274EC -:106E1000206669726D776172652C20737472696B7C -:106E2000652074776F2E004661696C656420746F0D -:106E3000207374617274206669726D776172652C5B -:106E400020737472696B65206F6E652E00466F72D9 -:106E500063652D626F6F74696E67207265636F760C -:106E6000657279206D6F64652E2E2E00426F6F74EF -:106E7000696E67206669726D7761726520402000D7 -:106E80002E2E2E0D0A0D0A00426F6F742062697457 -:106E9000733A20004153534552543A20003A00417E -:106EA0005353455254004153534552544E002A2ADD -:106EB0002A20575446200053544D33320053544D2A -:106EC0003332207065726970686572616C206C691C -:106ED0006272617279207472697070656420616E8B -:106EE000206173736572740069746F6120627566E6 -:106EF00066657220746F6F20736D616C6C002E2E4E -:106F00002F7372632F7574696C2F736C652E630019 -:106F1000FF00000000010203040102030406070849 -:046F20000900000064 -:0400000508000200ED -:00000001FF diff --git a/bin/boot/nowatchdog_boot_robert_bb@1478015115.bin b/bin/boot/nowatchdog_boot_robert_bb@1478015115.bin deleted file mode 100755 index 5031130cab..0000000000 Binary files a/bin/boot/nowatchdog_boot_robert_bb@1478015115.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_robert_bb@1478015115.hex b/bin/boot/nowatchdog_boot_robert_bb@1478015115.hex deleted file mode 100644 index 1dab34819b..0000000000 --- a/bin/boot/nowatchdog_boot_robert_bb@1478015115.hex +++ /dev/null @@ -1,1783 +0,0 @@ -:020000040800F2 -:1000000000AA01208925000885250008E51D0008B3 -:1000100085250008852500088525000800000000CA -:10002000000000000000000000000000852500081E -:1000300085250008000000008525000885250008AA -:1000400085250008852500088525000885250008E8 -:1000500085250008852500088525000885250008D8 -:1000600085250008852500088525000885250008C8 -:1000700085250008852500088525000885250008B8 -:1000800085250008852500088525000885250008A8 -:100090008525000885250008852500088525000898 -:1000A0008525000885250008852500088525000888 -:1000B0008525000885250008852500088525000878 -:1000C0008525000885250008852500088525000868 -:1000D0008525000885250008852500088525000858 -:1000E0008525000885250008852500088525000848 -:1000F0008525000885250008852500088525000838 -:100100008525000885250008852500088525000827 -:100110008525000885250008852500088525000817 -:100120008525000885250008852500088525000807 -:1001300085250008852500088525000885250008F7 -:1001400085250008852500088525000885250008E7 -:1001500085250008852500088525000885250008D7 -:1001600085250008852500088525000885250008C7 -:1001700085250008852500088525000885250008B7 -:1001800085250008852500088525000885250008A7 -:100190008525000885250008852500088525000897 -:1001A0008525000885250008852500088525000887 -:1001B00085250008852500088525000861070008B9 -:1001C0006D0700088525000800000000852500084F -:1001D0008525000885250008852500088525000857 -:1001E0008525000885250008852500088525000847 -:0801F0008525000885250008A3 -:0801F8009C0000000100000062 -:1002000053B94AB9002908BF00281CBF4FF0FF317D -:100210004FF0FF3000F03CB882B0EC462DE90050C2 -:1002200000F01EF8DDF804E002B00CBC704700BF1F -:100230002DE9F041904606460F461D46069C00F00B -:1002400029F808FB01FC8646A8FB002300FB05C536 -:10025000B21A2B4467EB0303C4E90023BDE8F08125 -:100260002DE9F8431D46174680468946089E00F052 -:1002700053F900FB05F3A0FB074507FB0137B8EB7B -:1002800004043D4469EB0505C6E90045BDE8F88373 -:10029000704700BF00292DE9F047C0F2A280002678 -:1002A000002BC0F298808C4690469E461546044628 -:1002B0000F46CBBB8A4256D9B2FA82F33BB1C3F1A7 -:1002C00020029F409D409C4020FA02F21743280CD8 -:1002D000220C1FFA85FEB7FBF0F100FB11770EFB35 -:1002E00001F342EA0747BB4207D97F1980F0018139 -:1002F000BB4240F2FE8002392F44FF1AA4B2B7FB82 -:10030000F0F300FB13770EFB03FE44EA0747BE45FC -:1003100006D97F1980F0EB80BE4540F2E880023BB1 -:1003200043EA0143002203E08B420FD90022134627 -:10033000341C4FF0000518BF0124604265EB4501F5 -:100340005840514000196941BDE8F087B3FA83F283 -:10035000002A40F08380804540F2CD808B42C0F07F -:10036000CA801346E4E712B90123B3FBF2F5B5FAEC -:1003700085F2002A3BD1781B4FEA154E1FFA85FC07 -:100380000122210CB0FBFEF80EFB18000CFB08F359 -:1003900041EA0047BB4208D97F1980F0B080BB42D8 -:1003A00040F2AD80A8F102082F44FF1AA4B2B7FBB7 -:1003B000FEF30EFB13770CFB03FC44EA0747BC4536 -:1003C00006D97F1980F09980BC4540F29680023BA7 -:1003D00043EA0843ACE752426FEA060663EB430385 -:1003E00061E740424FF0FF3661EB410158E795402D -:1003F000C2F1200107FA02F34FEA154ECF4024FA6A -:1004000001F194401FFA85FC1943B7FBFEF24FEA55 -:1004100011480EFB12770CFB02F348EA0747BB4278 -:1004200005D97F1971D2BB426FD9023A2F44FF1A06 -:1004300089B2B7FBFEF80EFB18770CFB08F041EA17 -:100440000743984206D95B1961D298425FD9A8F157 -:1004500002082B44181A48EA024292E7C2F1200728 -:1004600003FA02FE08FA02F5914028FA07F32CFA83 -:1004700007FCF84043EA0E0E08434FEA1E48070CFB -:100480001FFA8EFABCFBF8F908FB19CC0AFB09F13C -:1004900047EA0C4C614507D91CEB0E0C32D2614582 -:1004A00030D9A9F10209F444C1EB0C0C80B2BCFBB9 -:1004B000F8F308FB13CC0AFB03FA40EA0C418A4527 -:1004C00006D911EB0E0125D28A4523D9023B71448E -:1004D00043EA0943CAEB0101A3FB0589494503D35C -:1004E00003D19440444500D2013B002220E7013B68 -:1004F00016E7013901E701231AE7013B68E708F134 -:10050000FF3852E709F1FF39CEE7013A8FE708F1EA -:10051000FF389FE7013BDBE72DE9F043002B40D19B -:100520008A42044615464AD9B2FA82F30F464BB1C5 -:10053000C3F12006994000FA03F402FA03F5F040F3 -:1005400040EA0107290C260C1FFA85FEB7FBF1F0E3 -:1005500001FB10770EFB00F246EA07439A4207D9E7 -:100560005B1980F0EA809A4240F2E78002382B441F -:100570009A1AA4B2B2FBF1F301FB13220EFB03FEA5 -:1005800044EA0242964506D9521980F0DA8096452F -:1005900040F2D780023B43EA004000263146BDE8E6 -:1005A000F0838B4244D8B3FA83F6002E45D18242C1 -:1005B00040F2BF808B42C0F0BC803046EEE712B9FB -:1005C0000125B5FBF2F5B5FA85F2002A7BD14A1B6D -:1005D0002F0C1FFA85FE0126230CB2FBF7F007FB58 -:1005E00010220EFB00FC43EA02418C4507D9491951 -:1005F00080F0A1808C4540F29E8002382944CCEBEB -:100600000101A4B2B1FBF7F307FB13110EFB03FECC -:1006100044EA0144A64506D9641980F09080A645B5 -:1006200040F28D80023B43EA00403146BDE8F08352 -:10063000002630463146BDE8F083C6F12005B340C0 -:1006400002FA06F701FA06F4EA40E94020FA05F555 -:100650001A4325434FEA124C4FEA154E93B2B1FBB1 -:10066000FCF80CFB181103FB08F44EEA01418C4224 -:1006700006D9891869D28C4267D9A8F102081144B9 -:10068000091BADB2B1FBFCF40CFB141103FB04FE1F -:1006900045EA01439E4505D99B1854D29E4552D93F -:1006A000023C134444EA0844CEEB0303A4FB07894D -:1006B0004B4503D351D1B04040454ED20026601E79 -:1006C0003146BDE8F083C2F12006954001FA02F3FD -:1006D00000FA02F42F0CF140F0401FFA85FEB1FB46 -:1006E000F7F6184307FB16110EFB06F24FEA104C03 -:1006F0004CEA01439A4205D95B1929D29A4227D97B -:10070000023E2B449B1A80B2B3FBF7FC07FB1C3361 -:100710000EFB0CF140EA0343994206D95B1919D24A -:10072000994217D9ACF1020C2B445A1A4CEA0646EE -:1007300052E7012032E7013861E7013818E7013B51 -:1007400071E7013B27E7013CACE708F1FF3897E789 -:10075000013ED7E70CF1FF3CE7E7204600261DE706 -:10076000014800F0D6BF00BF302B0008014801F05F -:1007700038B800BF302B0008014800F0F9BD00BFB9 -:10078000302B000837B50024012002F0ADF90190AC -:100790002546E0B200F046F801AA08B910551DE060 -:1007A000135D042B0BDD174800F03CF9019800F0B5 -:1007B00040F90120002102F087F9002020E00133F8 -:1007C000DBB2042B135509D90F48012500F012F9AB -:1007D000204600F02EF90D4800F024F90134042CD5 -:1007E000D7D1019C3CB10A4800F004F9204600F042 -:1007F00020F900F08FF80120214602F065F9284623 -:1008000003B030BD0D650008396500084465000877 -:100810004E65000808B500F077FB80F00100C0B21B -:1008200008BD000008B50C22044B02FB003000F0AC -:100830008DFC80F00100C0B208BD00BFD02B0008C5 -:1008400010B5002401F094F8054820440C34017AD6 -:1008500000F062FC302CF7D1BDE8104001F089B8FF -:10086000D02B0008044BDA691106FBD59862DA69CF -:100870001206FCD5704700BF0048004070B5214DFE -:100880008AB02148082101F075F828464FF48021EC -:100890001E4C01F06FF82B68642623F0010394E8E6 -:1008A00007002B600C348DE8070000216846022207 -:1008B0000B4600F013FC03AB94E8070083E8070045 -:1008C00000211846022200240B4600F007FC06A86F -:1008D00002F020F8089821460D4A70430023FFF7E4 -:1008E0008FFCC0F3420320F00F000004000C1843FB -:1008F0000D23B0FBF6F0E8606C60AC602B600AB0D2 -:1009000070BD00BF00480040000C0240002C0008F1 -:1009100040420F002DE9F043334D83B02C6800AF07 -:1009200004F12E0304F1270823F00703ADEB030DB8 -:1009300050238DF8003021236E460DF106028DF80C -:10094000013000238DF8023003238DF8033004F1C9 -:1009500021035BBAADF80430244B03F11C0153F8BA -:10096000040B8B4242F8040BF9D11B7804F12309E4 -:1009700006F123001E491370224602F0C5F83146E5 -:100980004A46002001F044FD07F1080304F5927483 -:10099000314643F8040D42461868FE23B4FBF3F3D6 -:1009A000434446F80900073323F00703ADEB030D7A -:1009B00068466E4601F004FD044655206C44FFF77E -:1009C00051FFA64207D016F8010B552808BF00209A -:1009D000FFF748FFF5E75520FFF744FF00230C37EA -:1009E0002B60BD46BDE8F08338000020182C0008BD -:1009F0003C00002010B5441E14F8012F7AB10849BC -:100A00000B68FF2B0BD80A2A02D1FFF783FFF3E70D -:100A10000D2A1FBF581C5B1808601A71ECE710BD47 -:100A20003800002008B5FFF7E5FFBDE80840FFF7F4 -:100A300071BF1FB50C2201A901F018FD01A8FFF735 -:100A4000D9FF05B05DF804FB07B502A9012201F842 -:100A5000010D042000F096F903B05DF804FB0021BD -:100A600002200A4600F08EB910B540F2F51400F0ED -:100A7000B9F850B1013C04D10548FFF7D3FF204637 -:100A800010BD642001F0E4FCF1E7012010BD00BFBF -:100A90007A65000810B5074C43F2CD71064848F658 -:100AA000B803224601F02AFD01462046BDE8104069 -:100AB00000F0E4B83C010020352C000810B500F02F -:100AC00099F8FFF7E7FF08B9154816E0002400F091 -:100AD00039F9FFF7C4FF0120FFF7B6FFFFF7C4FFA6 -:100AE00078B1002103200A4600F04CF90D48FFF7C9 -:100AF00081FF2046FFF79DFF0B48BDE81040FFF740 -:100B000091BFFFF7C7FF0028DED001340B2CE2D1E4 -:100B10000648FFF787FFBDE81040FFF7A0BF00BF02 -:100B20009D650008B8650008D3650008DD6500080C -:100B300008B5FFF799FF0120FFF786FFFFF794FF45 -:100B4000002103200A46BDE8084000F01BB9000060 -:100B50002F2307B54A1C58430B4B00EB52001A7861 -:100B6000B0FBF1F0C0B282420AD002A91870042290 -:100B700041F8040D012000F005F90220FFF764FFA1 -:100B800003B05DF804FB00BF0000002007B502A918 -:100B9000042241F8040D012000F0F4F80320FFF7CF -:100BA00053FF03B05DF804FBFFF759BF0C4B014442 -:100BB00030B51C68884207D022689568AD07FCD51F -:100BC00010F8015B1573F5E71B681A68936813F456 -:100BD000C05FFBD10A20BDE8304001F039BC00BF46 -:100BE000802B0008024B1868403000F0AFBA00BFFD -:100BF000802B000838B5204B012200211C6804F12D -:100C00002005284600F03CFA0021284600F052FA60 -:100C100004F1380000F094FA04F1400000F090FA7A -:100C2000012104F12C000A4600F02AFA002104F107 -:100C3000080001220B4600F051FA0021012204F1C4 -:100C400014000B4600F04AFA6068012101F014FF1D -:100C50006068002101F010FF94E8030000F08AFEB4 -:100C600023684CF207321A604FF4B8525A601A687F -:100C700042F040021A6038BD802B00082B4B2DE952 -:100C8000F7431C68814601208846E36A04F1200787 -:100C900004F12C060093236B019301F0DDFB384631 -:100CA000012100F007FA0021304600F003FA01208C -:100CB00001F0D2FB684600F049FA054620B11C4815 -:100CC0000025FFF7AFFE2CE030460121383400F05C -:100CD000F1F9012001F0C0FB204600F037FA06468A -:100CE00008B114481BE0684600F030FA054608B920 -:100CF000114814E0012001F0AFFB48464146FFF7E0 -:100D000055FF3846314600F0D5F90C480921FFF768 -:100D10004DFF204600F01AFA054610B90848FFF7C3 -:100D200081FE284603B0BDE8F08300BF802B000899 -:100D3000FC650008186600083266000802640008B6 -:100D40004E66000808B5022001F086FB0A4B1B68BE -:100D500093F8483043B10948FFF764FE012000F0E2 -:100D6000D3FD022001F078FB0548FFF75BFE012070 -:100D7000BDE8084000F0C2BD802B00086F66000887 -:100D80008B6600080F4BF7B51C6802AD0F460121BA -:100D9000203405F8010D1646204600F08BF964203A -:100DA00001F056FB28460121FFF700FF1EB138462F -:100DB0003146FFF7FBFE2046002100F07BF903B02F -:100DC000F0BD00BF802B0008F0B50746ADF2044D22 -:100DD0000E460C460025B919B4F5806F6846A1EBA4 -:100DE00004010DD94FF48062A4F5806400F0CCF8C2 -:100DF000284669464FF4806201F00AFB0546EAE79F -:100E0000224600F0C1F869462246284601F000FB60 -:100E10000DF2044DF0BD08B5202001F045FD00287D -:100E2000FAD108BD08B5022001F03EFD0028FAD035 -:100E30000220BDE8084001F041BD00B58DB0684614 -:100E400001F0BBFC0023684600934FF480730A93C3 -:100E500035230B9301F0E2FCFFF7E4FFFFF7DBFF24 -:100E60000DB05DF804FB000030B5002495B02448B7 -:100E7000022100F07FFD23480021032220440C348E -:100E80000B4600F02BF9482CF5D101A8002401F005 -:100E90008BFC102301A802940193172303944FF4B1 -:100EA0004075059304940694079401F095FC012085 -:100EB00001F0D8FCFFF7C1FF08A801F07EFC662313 -:100EC00008A808941393129501F0A8FCFFF7AAFF55 -:100ED00008A801F072FC992308A8089413931295AE -:100EE00001F09CFCFFF79EFF4CF2503001F0B0FA8D -:100EF000FFF7A3FF0248022100F046FD15B030BD08 -:100F0000001000A00C64000830B502218DB01C4810 -:100F100000F030FD022001F0B9FC684601F04DFC04 -:100F20004FF08063684600934FF0407304934FF492 -:100F300040730A93AF230B9301F070FCFFF772FF2D -:100F400001F0ACFC054601F0A9FC044601F0A6FC4A -:100F5000240244EA004040EA0504FFF75CFF094B25 -:100F60009C4204D020460024FFF763FD00E00124EA -:100F70000348022100F008FD20460DB030BD00BF3F -:100F8000001000A020BB190070B515468CB00446B7 -:100F90000E461B48022100F0EDFC681E01F076FCB5 -:100FA000684601F00AFC4FF08063684625440093D0 -:100FB0004FF0407304930A2305934FF44063099361 -:100FC0004FF440730A934FF4005308930B230B9391 -:100FD00001F024FC304601F051FCAC4204D001F099 -:100FE0005DFC04F8010BF8E7022001F067FCFFF755 -:100FF00012FF0348022100F0C7FC0CB070BD00BF17 -:10100000001000A0034A136B23F4FF6323F00703CF -:10101000136370470038024000F13F4000F57E0046 -:10102000C0F387200A280AD8054A135C591C1154BA -:101030002BB9044901230A6B834013430B637047A8 -:10104000F48900200038024000F13F4000F57E00A6 -:10105000C0F387200A280DD8074A135C53B1013B1F -:10106000DBB2135433B9054901230A6B834022EAEA -:1010700003030B63704700BFF4890020003802406F -:10108000436813B500930123044600688DF80430CB -:1010900000238DF805208DF807308DF80610FFF736 -:1010A000BBFF2068694601F02BFB2068FFF7CCFFEF -:1010B00002B010BD38B5037A04460D460BB981F075 -:1010C00001052068FFF7A8FF2068A1882A4601F0E3 -:1010D00055FB2068BDE83840FFF7B6BF13B504469E -:1010E00040688DF80730009002208DF805208DF8BB -:1010F000040020688DF80610FFF78EFFA27A2068A2 -:10110000218901F03FFB2068694601F0F9FA206867 -:10111000FFF79AFF02B010BD002313B504460193F8 -:101120004368006800938DF80710FFF775FF20688B -:10113000694601F0E5FA2068FFF786FF02B010BDAE -:10114000036813B10021FFF7E7BF704738B50446C5 -:101150000068FFF761FFA188206801F009FB0546E0 -:101160002068FFF771FF281C18BF012038BD1FB58C -:10117000836804460093C36868460193FFF7E0FF65 -:10118000636902A80293A3690393FFF7D9FF04B030 -:1011900010BD10B504460121083002220023FFF7DC -:1011A0009DFF04F11400012102220023BDE810403C -:1011B000FFF794BF0368DB6910B5044673B9C36ACF -:1011C00063B10A2001F048F92046FFF7D0FF20461E -:1011D000E36A01219847142001F03EF92046FFF709 -:1011E000D8FF2046BDE8104000F0E0B910B5044635 -:1011F00000F04FFA48B10A2001F02EF9204600F025 -:1012000048FA80F00100C0B210BD012010BD30B519 -:10121000044687B0C36A00219847A36800210125CE -:1012200000936846E3680A468DF808500193FFF77B -:1012300027FF68460021FFF73DFF6369002103A8EF -:101240000393A3690A468DF814500493FFF718FF1F -:1012500003A80021FFF72EFF07B030BD10B50446EC -:1012600000F00AFA2368DB6933B9E36A4BB1204620 -:10127000BDE81040FFF7CBBF2046BDE81040FFF7A8 -:1012800076BF10BD10B50446FFF7E8FF2046BDE865 -:101290001040FFF78FBF00002DE9F3419846036827 -:1012A00004460E46DB6917460BB900205BE000F0F0 -:1012B000F0F9B8B9256814220021284601F02FFC66 -:1012C0009DF828306F8020466B71089B2E71AB60B3 -:1012D000099B85F806802B6100F0ECF92368002259 -:1012E0009A6111E02046FFF7CDFF2046FFF77EFF11 -:1012F0000028DFD1D9E79A69B2F57A7F23DA013283 -:1013000002209A6101F0A8F8204600F0DFF90122DE -:10131000236883F82020154A0192019A511E0191F9 -:101320001AB193F82020002AF7D1002283F8202058 -:10133000019A42B11D7D05F0FD02012ADBD16B1E31 -:101340005D425D4103E00025204600F0A8F92368D6 -:101350000022204683F82020FFF748FF10B92046DE -:10136000FFF790FF284602B0BDE8F08180841E00A0 -:1013700010B5044618B90E48A92100F0E9FF002174 -:101380002422006801F0CBFB204600F0CBF8236A52 -:101390002BB1002104F120000A46FFF771FEE36A39 -:1013A00023B12046BDE81040FFF731BF10BD00BF9C -:1013B000E166000810B5044618B90848B62100F0E7 -:1013C000C7FF00680368DB690BB9FFF7F3FE23680A -:1013D0001A68D3690133D36110BD00BFE16600080C -:1013E00008B518B90748BF2100F0B2FF00680268CD -:1013F000D36933B1013BD3611BB9BDE80840FFF7A6 -:101400002DBF08BDE16600081FB50C46114618B98E -:101410000A4840F28D1103E023B908484FF4C77120 -:1014200000F096FF82888DE80A0000212346029191 -:101430000068FFF731FF04B010BD00BFE16600088F -:1014400013460122FFF7E0BF1FB50C46114618B93D -:101450000A484FF4D77103E023B9084840F2AF11AE -:1014600000F076FF82888DE80A00002301210293B4 -:1014700023460068FFF710FF04B010BDE1660008C6 -:1014800007B502AB03F8012D0122FFF7DDFF03B022 -:101490005DF804FB03680022197583F8202070476B -:1014A00010B5046863882279C3F3090322B922784E -:1014B000012A18BF43F48063A268E468121BFF2A64 -:1014C00095BF120443F0FF7302F47F0243F48033AC -:1014D00098BF134309B143F4005342681268536044 -:1014E00010BD43681B681A6822F0D60210B51A6056 -:1014F0005A6842F480425A6008220368BDE81040EE -:101500001A70FFF7C7BF0368012110B50446586879 -:1015100001F0A6FA00212368BDE81040586801F0E8 -:101520009FBA000043685A7B53B2002B30B506DAED -:1015300002F00F021B4B1344C0221A7606E003F19F -:101540006043C02203F5614383F8002343689A7B1C -:1015500053B2002B06DA02F00F02124B1344C022E2 -:101560001A7606E003F16043C02203F5614383F875 -:10157000002350F8045F0C4C6B7B59B203F01F0240 -:101580000123490903FA02F244F82120AA7B51B24F -:1015900002F01F024909934044F82130BDE8304071 -:1015A000FFF7B1BFFCEC00E000E100E043682C492C -:1015B0009A688A42F0B5074687B004D92948512174 -:1015C000294A00F0B9FE29498A4205D91C7B34B169 -:1015D000012C07D000F0C6FE0124254603E00924B3 -:1015E000102500E0022502A801F094F9796860193D -:1015F000049A8B684343B2FBF3F603FB162303B94B -:10160000013E0F2E03D91748702100F0A1FE03C937 -:1016100000F0B0F97B684FF0000100221B68013D2B -:1016200066F307110192013C8DF804508DF8071004 -:1016300019688DF8054021F0010119600C491868FE -:1016400001401960019919619A60DA601A6842F0E4 -:1016500001021A60074A59680A405A6007B0F0BD93 -:10166000801A0600FA66000817670008A0860100C5 -:10167000001F30FF008000F810B5044654F8043F06 -:1016800093E8030000F080F92046BDE81040FFF722 -:101690003ABF43681B689869C0F3C030704743681D -:1016A0001B681A6822F0D6021A605A6842F4804217 -:1016B0005A60704703685A790AB9012204E01A791E -:1016C0000AB9052200E006221A70704743681B68B9 -:1016D0001A6842F0D20210B51A60026814791CB977 -:1016E000196841F004011960117801290CD1528860 -:1016F000012CC2F309020CBF4FF08071002142F4AB -:1017000090320A435A6010BD0121BDE81040FFF736 -:10171000C7BE38B5436805461C6803681A78013AA5 -:10172000072A5BD8DFE802F0045A5A5A223B5A577C -:10173000A269910701D59A79A2621A793AB9A26988 -:1017400052060FD50522284601211A7008E0012A09 -:1017500008D1A269100605D50622284600211A7074 -:10176000FFF79EFEA369D9063AD525E0A269520784 -:1017700005D5DA681969501CD860636A8B54A3696F -:101780001B0603D528460021FFF78AFEA3695806E9 -:1017900026D528460121BDE83840FFF7A2BEA26940 -:1017A000910705D5DA681969501CD8608B5CA36273 -:1017B000A16911F0100109D0E3692846022143F024 -:1017C0001003E361BDE83840FFF764BEA3691A0661 -:1017D000DCD52846D8E7A369DB0701D400F0C2FDB9 -:1017E00038BD43681B689A69D20503D5DA6942F4AB -:1017F0008072DA619A69510503D5DA6942F4806230 -:10180000DA619A69920503D5DA6942F40072DA6105 -:101810000321FFF766BE0000094B70B51C680D463A -:1018200006462046FFF7C6FD2A4631462046FFF70A -:1018300007FE05462046FFF7D3FD284670BD00BFD2 -:10184000642B0008094B70B51C680D4606462046FF -:10185000FFF7B0FD2A4631462046FFF711FE054648 -:101860002046FFF7BDFD284670BD00BF642B000871 -:1018700037B500F07DF81D4801210222FFF700FC7A -:101880001B4801210222FFF7FBFB1A48012102221B -:10189000FFF7F6FB00F06DF800200DF10701FFF7F0 -:1018A000BBFF9DF80750012D1DD114200DF107013C -:1018B000FFF7B2FFA8B1104C237813B101332370A6 -:1018C0000FE09DF80710142021F0060141F00201FD -:1018D0008DF80710FFF7B6FF18B10320257000F050 -:1018E000BBFD012000E0002003B030BD54640008BF -:1018F000606400086C640008FF890020014601480C -:10190000FFF7D8BB5464000801460148FFF7D2BB7B -:1019100060640008B0F1804F08B503D20D48302153 -:1019200000F016FD0C4B98420ED903F58033984217 -:101930000CD9B0F1A04F0BD3084A094BB0F1204F9E -:1019400034BF1846104608BD064808BD064808BD05 -:10195000064808BD38670008FFFF0040D12900088D -:10196000B9290008E9290008012A0008A12900086E -:101970007047704710B50C46FFF7CCFF01210346B6 -:101980002046BDE81040184710B50C46FFF7C2FFCF -:10199000002103462046BDE81040184738B5104CDA -:1019A00005464FF080512046FFF7E4FF45B154E86B -:1019B000003F43F4807344E80032002A08D0F6E781 -:1019C00054E8003F23F4807344E80032002AF7D142 -:1019D00003484FF08051BDE83840FFF7D5BF00BF46 -:1019E00000700040024B5868C0F34000704700BFD1 -:1019F00000700040024A136843F008031360704708 -:101A000000700040B0F1006F10B504460BD20C48D6 -:101A1000FEF7F0FF2046FFF70CF80A48FFF702F840 -:101A20004FF0FF3010BD084B002053F8042BA242AA -:101A300002D81A68944202D301300B28F5D110BDA8 -:101A40005767000860670008786400082DE9F843CC -:101A50000446174699460E46C1B3FFF7D3FF054625 -:101A6000601E3044FFF7CEFF002D804631DB00289A -:101A70002FDB461B01361FB1002031464A46B847CE -:101A80002C4600F09BFD44451EDCF32000F0ACFD2D -:101A9000114B002133F8140000F0DEFD09280AD0B4 -:101AA0000E48FEF7A7FF2046FEF7C3FFFEF732FF02 -:101AB00000F092FD0DE02FB1C5F1010031464A461C -:101AC0002044B8470134DEE700F086FD0120BDE880 -:101AD000F8830020BDE8F883A8640008796700084F -:101AE0002DE9F0410F461646804600F067FD3E4462 -:101AF0003C46F32000F078FDB44216D0C7EB080551 -:101B0000254414F8011B284600F0DCFD0928F3D019 -:101B10000848FEF76FFF2846FEF78BFFFEF7FAFE38 -:101B200000F05AFD0020BDE8F08100F055FD0120D5 -:101B3000BDE8F0819167000808B57D2000F0A8FF9E -:101B4000003018BF012008BD4900FFF701B80000B0 -:101B50002DE9F043012185B005460020FEF7F8FF8E -:101B6000042000F047FB01A8294600F097FB01A8DC -:101B700000F0A3FB08B93B4814E00C353A48FEF7E7 -:101B800051FF02992846FFF71FF904463748FEF730 -:101B900031FF2046FEF74DFFFEF7BCFE039B9C4243 -:101BA00004D03348FEF73EFF012057E0029CDFF8E7 -:101BB000E0803048FEF736FF21462F4A002340469A -:101BC0000024FFF743FF029F2C484FEA5709FEF716 -:101BD00029FFBC421CD23E1B29486119B6F5803F43 -:101BE00028BF4FF480363246FFF7CEF908EB0400E9 -:101BF00023493246FFF774FF18B92248FEF712FF57 -:101C000006E009EB54003946FEF7A2FF3444E0E752 -:101C10001D48FEF7EFFE0298FEF70BFF1B48FEF78C -:101C2000E9FE1B49029A002000F0F2FB0446194825 -:101C3000FEF7E0FE0398FEF7FCFE1748FEF7DAFE1B -:101C40002046FEF7F6FEFEF765FE039B9C4204D09D -:101C50001248FEF7E7FE022000E0002005B0BDE8D4 -:101C6000F08300BFA2670008C0670008DD670008B6 -:101C7000F367000816680008491B00082D68000873 -:101C8000008A0020446800084F6800085D6800086A -:101C90000080000866680008796800087F6800080E -:101CA00010B5022000F0BEFAD8B3042000F0BAFA52 -:101CB000044670B11B48FEF7B5FE042000F0A6FAFA -:101CC000022000F0A3FA4FF40040BDE8104000F0FD -:101CD0009DBA1548FEF7A6FE082000F097FA1020DE -:101CE00000F094FA202000F091FA402000F08EFAE3 -:101CF0002046FFF72DFF022807D1082000F07AFACE -:101D0000102000F077FA00F051FB042000F07EFA7A -:101D1000022000F07BFA4FF40040BDE8104000F0D4 -:101D200069BA10BDCA6800080069000810B52548E6 -:101D3000FEF778FE4FF48000FFF70AFF10B102288B -:101D40003AD809E0202000F061FA402000F05EFA65 -:101D5000802000F04FFA2FE0082000F04BFA10200E -:101D600000F048FA202000F05DFA20B91648FEF78E -:101D700059FE202007E0402000F054FA58B91348DB -:101D8000FEF750FE402000F035FA4FF4007000F0EE -:101D900031FA00F00BFB0E480024FEF743FE08204A -:101DA00000F034FA102000F031FA202000F02EFA72 -:101DB000402000F02BFA00E00124042000F026FA75 -:101DC000204610BD1B690008356900086E690008CF -:101DD000A769000808B50248FEF724FE00F05CFA87 -:101DE000E16900081EF0040F0CBFEFF30880EFF369 -:101DF0000980FFF7EFBF704708B50120FEF712FD1D -:101E000008B900200FE00020FEF70CFD0028F8D0F4 -:101E10000220FEF707FD0028F3D00320FEF702FDA5 -:101E200080F00100C0B200F0010008BD1FB50446FB -:101E30000E48FEF7F7FD01A90C22204600F016FB24 -:101E400001A8FEF7EFFD2046FEF7A0FE0220FEF7F8 -:101E5000E9FC04460220FEF7E5FCA04201D000F0B8 -:101E6000A5FA0A2000F0F8FAF4E700BFD7690008E5 -:101E70007FB5BB484FF08051FFF77CFDFFF7B2FD07 -:101E8000044630B1FFF7B6FDB648FEF7CBFD00F0D3 -:101E900094FA4FF08051B248FFF776FDFEF76CFCE4 -:101EA000FEF7ECFCB048FEF7BDFDB048FEF7BAFD0A -:101EB000AF48FEF7B7FDAF48FEF7B4FDAE48FEF7FA -:101EC000B1FDAE48FEF7AEFDAD48FEF7ABFDFFF746 -:101ED000CFFC00F0B1F900F0D1F921460C2201A8A5 -:101EE00000F01DFE00F0C6F901A90C2200F0BEFAB8 -:101EF00001A8FEF797FDA348FEF794FDA148FEF761 -:101F000091FD4FF4804000F08DF990B19E48FEF7AE -:101F100089FD4FF4804000F079F9082000F076F94F -:101F2000102000F073F9202000F070F9402000F03C -:101F30006DF9FEF799FFFEF783FCFFF799FCFEF7BA -:101F4000BDFDFEF7F5FDFEF71DFC08B18F4803E06F -:101F5000FEF760FC10B18E48FFF768FF00F07AF9D9 -:101F6000802000F05FF918B1802000F04FF94BE0BD -:101F70004FF4003000F056F920B14FF4003000F07B -:101F800045F943E0FFF738FF68B141F288348148F2 -:101F9000FEF748FDFFF730FF78B1012000F05CFA52 -:101FA000013CF7D132E07C4B0CCB013301D0013244 -:101FB00003D17A48FEF736FD28E0FFF7BDFD48B3B0 -:101FC000FFF7BAFD10B17648FEF72CFD4FF4007014 -:101FD00000F028F910B17348FEF724FD4FF40070AB -:101FE00000F014F9102000F01DF9002800F0EE8038 -:101FF0006D48FEF717FD082000F008F9102000F0EA -:1020000005F9FFF793FE98B96848A5E76848FEF719 -:1020100009FDF6E74FF4007000F004F90028CFD175 -:10202000082000F0F3F8102000F0F0F8FFF738FE79 -:102030004FF4005000F0F6F804464FF4805000F0E2 -:10204000F1F8400040EA8400C4B24FF4006000F0B0 -:10205000E9F82043C0B20728C3B20DD14FF40060A5 -:1020600000F0D4F84FF4805000F0D0F84FF4005056 -:1020700000F0CCF84F486FE701334FF40060DBB25B -:10208000023B052B11D8DFE803F00C1003100C10F5 -:1020900000F0BCF84FF4805000F0B8F84FF4005056 -:1020A00003E000F0B3F84FF4805000F0A3F8FEF71F -:1020B000A9FF394B40485D681E68FEF79BFC284627 -:1020C000FEF7B7FC3D48FEF795FC00229300013275 -:1020D0004FF0FF3103F16043082A03F56143C3F871 -:1020E0008010C3F88011F1D1354B4FF480120024D9 -:1020F0001A634FF480625C639C631A645C6400F052 -:10210000E3FB3048012100F087FC21462D4800F018 -:1021100083FCF320012100F08BFC2146F32000F02A -:1021200087FC0320012100F08FFC2146032000F0F2 -:102130008BFC6FF4A050012100F092FC21466FF45B -:10214000A05000F08DFC2048012100F095FC1E48B5 -:10215000214600F091FC63B64FF0FF3EB54628479C -:1021600000700040EC690008F9690008006A000886 -:102170008D6A0008246B0008A56B0008266C000817 -:10218000A96C0008FF690008266D0008014550FE93 -:10219000024550FE536D0008008000088E6D000857 -:1021A000A16D0008B96D0008D96D0008034550FE07 -:1021B0004D6E0008044550FE6C6E0008806E0008ED -:1021C000003802400010E022B379F764082000F0E4 -:1021D00029F820B10548FEF725FC102003E004484B -:1021E000FEF720FC082000F005F81FE7016E00084C -:1021F000276E000810B50446002000F075FC40EA88 -:1022000004010020BDE8104000F05EBC10B504469B -:10221000002000F069FC20EA04010020BDE8104025 -:1022200000F052BC10B50446002000F05DFC2042D6 -:1022300014BF0120002010BD08B50120FFF7AEFB40 -:102240000120FFF7EFFF20B90121BDE8084000F0B1 -:102250003BBC08BD08B50648FEF7CCFB002000F0EB -:1022600043FCFEF7E6FBBDE80840FEF753BB00BFAA -:10227000886E0008022000F037BC000008B5FFF7A8 -:10228000F9FF0449884204D00220BDE8084000F06C -:102290001BBC08BD8BB8185800BEFDE71FB504462F -:1022A0000C2201A8FEF770FE01AB03CB206018687A -:1022B0006160A060204604B010BD0068A0F10C036E -:1022C000584258417047000080B5174606461048EE -:1022D0000D461C46FEF78EFB3846FEF78BFB0D487D -:1022E000FEF788FB3046FEF785FB0B48FEF782FBC6 -:1022F0002846FEF79EFB2CB10848FEF77BFB2046E4 -:10230000FEF778FB0648FEF78DFBFFF7C5FF00BF21 -:10231000946E0008A66C00089D6E0008A76C00086B -:10232000FF6900081FB506AA52F8044B03920092F9 -:102330001A462346FFF7C8FF0CB41FB506AA52F889 -:10234000043B03920092014AFFF7BEFF9F6E000814 -:10235000002307B572460093014BFFF7E3FF00BF70 -:10236000A66E00087446064808B5FEF743FB2046F3 -:10237000FEF75FFB0348FEF755FBFFF78DFF00BF3D -:10238000AE6E0008FF690008BFF34F8F0549064B8A -:10239000CA6802F4E0621343CB60BFF34F8F00BF03 -:1023A000FDE700BF00ED00E00400FA0508B5FEF708 -:1023B000FBFB00F081FBFFF7E7FF08B5FFF7E4FF49 -:1023C000F0B50646002401202546034694421DD060 -:1023D00011F804C000F1010EBCF1000F03D17355D8 -:1023E000774605460DE00133774606F800C0DBB2BC -:1023F000FF2B07D102F1FF3C644506D07355871CC3 -:102400007546012301343846E0E770467355F0BD48 -:1024100030B5C9B1C0430A44914213D011F8013B11 -:102420000A4D83EA000404F00F0455F8244084EABE -:10243000101080EA131303F00F0355F8233083EADA -:102440001010E9E7C04330BD084630BDC064000845 -:1024500000010138FDD1704710B504462CB14FF48E -:102460007A70013CFFF7F4FFF8E710BD0A2A10B5B7 -:1024700004DC1148BDE81040FEF7D4BA30230C4606 -:102480001C2204F8023B78234B700F239340034037 -:10249000D340092B01D8303302E00F2B02D8573339 -:1024A000DBB200E02023043A04F8013B131DECD119 -:1024B00000238B7210BD00BFE86E000808B582686B -:1024C0004368934203D304481421FFF741FF026895 -:1024D000591C4160D05C08BDFE6E000843688268EC -:1024E000934210B503D304481921FFF731FF026866 -:1024F0005C1C4460D15410BDFE6E0008F0B587B07E -:102500000E460746154600210C2268461C4600F080 -:1025100006FB00210C2203A80296009700F0FFFAA8 -:10252000684603950594FFF7C9FF06466846FFF71E -:10253000C5FFB042014601D003A808E06846FFF796 -:10254000BDFF0446D8B1012804D103A83146FFF7E6 -:10255000C5FFEBE7030608D504F07F046846FFF7E4 -:10256000ADFF40EA042000F18004A4B20025A5429A -:10257000DCDA03A80021FFF7B1FF0135F7E7049883 -:1025800007B0F0BDFEE700000748084A0849121AE4 -:1025900008B500F0B9FA0748074A0021121A00F0FE -:1025A000BEFAFFF765FC00BF000000201400002009 -:1025B000106F000814000020008A0120044B1A69E3 -:1025C000002A04DA034A5A6002F188325A607047DE -:1025D000003C024023016745024A136943F000436F -:1025E00013617047003C0240014BD860704700BF48 -:1025F000003C02400E4BDA68D20310D4DA68D106F0 -:102600000FD4DA68D2050ED4DA6812F0E00F0CD1DC -:10261000DB6813F0020F14BF082009207047012067 -:10262000704706207047022070470720704700BFA0 -:10263000003C0240092307B58DF80730FFF7DAFFA9 -:102640008DF807009DF80730012BF7D09DF80700A3 -:1026500003B05DF804FB000070B5064641B10129E6 -:1026600008D002290CBF4FF400754FF4407503E009 -:102670000D4601E04FF48075FFF7DCFF09281ED1FD -:102680000F4C236923F4407323612169294321619D -:10269000236923F0F8032361236943F002031E43F7 -:1026A0002661236943F480332361FFF7C3FF236965 -:1026B00023F002032361236923F0F803236170BD33 -:1026C000003C024070B505460E46FFF7B3FF0928EF -:1026D00013D10A4C236923F44073236123692361D6 -:1026E000236943F0010323612E70BFF34F8FFFF77F -:1026F000A1FF236923F00103236170BD003C024068 -:10270000F0B500220E680123934003EA060E9E45B1 -:102710002AD1550003230468AB40DB431C4004600E -:102720000C79076804FA05FC013C4CEA0707012C08 -:10273000076011D884684F791C40AF40846084687A -:1027400027438760446824EA0E0444608C79476814 -:102750009440A4B23C434460C4682340C360CB7936 -:10276000C468AB402343C3600132102ACBD1F0BD13 -:102770000369194214BF01200020704702B90904FF -:102780008161704701F00703C9089B0000EB8100DD -:102790000F219A40994010B5046A24EA01010162B0 -:1027A000016A1143016210BD00230360436083602E -:1027B000C3600361436183617047002343608360AA -:1027C000C3600360036143618361C3610362436269 -:1027D0008362C362704700000E4903680A6810B53F -:1027E00044691C4383691C4322F07F43426823F001 -:1027F0003003234343EA02630B60C36882684C687A -:102800001A43054B23401343026943EA02434B60DA -:1028100010BD00BF001000A0FEF7E0FF90E80C0024 -:102820000E491A4383681A43C3681A4303691A435B -:1028300083691A43C3691A43036A1A43436A1A43F2 -:10284000836A1A43C36A10B51A434C69044B234088 -:102850001343426943EA82434B6110BD001000A05C -:1028600000008090044B1A6810B142F0010201E0B0 -:1028700022F001021A607047001000A0024B9A6813 -:10288000920600D498617047001000A0024B9A682D -:10289000920600D418617047001000A0014B187810 -:1028A000704700BF201000A0034B9B68184214BF64 -:1028B00001200020704700BF001000A0014BD8602D -:1028C000704700BF001000A00F4B00211A6842F0B3 -:1028D00001021A6099601A6822F0A85222F41022AC -:1028E0001A600A4A5A600A4AC3F8842002F18062D8 -:1028F000C3F888201A6822F480221A60D960C3F8CD -:102900008C10C3F89010704700380240103000243B -:10291000003000201E4A936803F00C03042B10B50E -:1029200003D0082B03D01B4B19E01B4B17E0516859 -:10293000536811F4800F516803F03F0318BF164A23 -:10294000C1F3881108BF134AB2FBF3F3104A4B439B -:102950005268C2F3014201325200B3FBF2F30C4A57 -:10296000036093680D49C3F30313CC5C0368E34031 -:1029700043609468C4F382240C5D23FA04F48460F9 -:102980009268C2F342328A5CD340C36010BD00BF7C -:10299000003802400024F40040787D01010000204E -:1029A000044B1A6B09B1104301E022EA00001863DE -:1029B000704700BF00380240044B5A6B09B1104306 -:1029C00001E022EA00005863704700BF003802406F -:1029D000044B9A6B09B1104301E022EA00009863AE -:1029E000704700BF00380240044B1A6C09B1104315 -:1029F00001E022EA00001864704700BF003802407E -:102A0000044B5A6C09B1104301E022EA00005864FB -:102A1000704700BF00380240044B1A6909B11043E7 -:102A200001E022EA00001861704700BF0038024050 -:102A3000044B5A6909B1104301E022EA00005861D1 -:102A4000704700BF00380240044B9A6909B1104337 -:102A500001E022EA00009861704700BF00380240A0 -:102A6000044B1A6A09B1104301E022EA000018621F -:102A7000704700BF00380240044B5A6A09B1104346 -:102A800001E022EA00005862704700BF00380240AF -:102A90004209084B012A01D11B6804E0022A01D136 -:102AA0001B6F00E05B6F00F01F0023FA00F000F0E6 -:102AB0000100704700380240024A536F43F08073B0 -:102AC000536770470038024082B000230193054BE2 -:102AD0000193019B03EB80000190019B196002B000 -:102AE000704700BF5028004082B000230193054B7F -:102AF0000193019B03EB80000190019B186802B0D9 -:102B0000704700BF50280040431E0A4410B5914250 -:102B100004D011F8014B03F8014FF8E710BD02444F -:102B20000346934202D003F8011BFAE77047000006 -:102B300014000020702B0008001402400040000028 -:102B40000E00040000140240008000000F0004008A -:102B50000000000000000000000000000000000075 -:102B600000650008682B0008302B000850000000AA -:102B70000060004000000001801A0600005F600055 -:102B8000842B000800540140000020000000024097 -:102B90002000000005000800000002408000000046 -:102BA00007000800000002401000000000000000C4 -:102BB0000000024008000000010000000004024084 -:102BC00004000000000402400100000001000000B9 -:102BD0000018024040000000010000000018024000 -:102BE0000800000000000000001802402000000063 -:102BF0000100000000180240100000000100000069 -:102C0000000C02400001000008000700000C024018 -:102C1000000200000900070001424F4F544C4F4191 -:102C20004445520000000000002A2A000000000075 -:102C3000000000000047FF004C6174746963650088 -:102C4000694345637562653220323031362E303249 -:102C50002E323738313000506172743A2069434562 -:102C60003430554C314B2D434D3336410044617463 -:102C7000653A204A756C20323520323031362030AA -:102C8000393A35363A33394702FF7EAA997E5100E8 -:102C9000010592002062016F8247027200701100EC -:102CA0000101470B10470280470502470580034793 -:102CB0000801470208471120470C02470B0247173B -:102CC00040470501470540470C040090470F104757 -:102CD00006404705014705404705014704800004B9 -:102CE0004710804705020020000800804702014786 -:102CF0000908471701470434000C470403470D3007 -:102D0000475970470E8010470402004047040801ED -:102D100047020702004047040801470520044741D5 -:102D200003470B03470D1C471080470901008047EC -:102D30000203470908470324472B784712400747F7 -:102D40000A40470C040008470E40470501470604A7 -:102D50004704014706044706104711800747061C31 -:102D6000470380470C08470A0C3020470F30E847DC -:102D70000C63C1470C0C30470F400105470C03C0E2 -:102D8000470C0C3020470D184702E04704020CB1F5 -:102D90000018003847064047080C30471001074725 -:102DA00002010001CD72470398470F0C3020470DF8 -:102DB0003000F9A04708100001C04705038047070D -:102DC0000C30470E300071054708300001470F0CEA -:102DD0003020470F5808470401DCA00080470363F8 -:102DE000C1470203C020AF5247050C30471008030B -:102DF000470402E0F5470503C0470202E0050FA0C3 -:102E00001047040C3020471240002020470AC00120 -:102E1000E007A547060C3047110D470314470B4048 -:102E200047030547031447020C3020470F90000466 -:102E3000470220470E17CFA247020847020C30472F -:102E40001030092447022C470D02CD0FF047050C26 -:102E50003020470F108004470368470301054710DF -:102E60000C30471011073047190C3020470D300047 -:102E700010470A0147100C30470E30470205204723 -:102E8000023447050247100C30204707044706402C -:102E90004704203404ED80000247091C0008470560 -:102EA0000C30470F4047050422FC4702014707A0AA -:102EB0000002C047060C3020470903EAF520004015 -:102EC0004706035CA500184708401C8F0847050CFF -:102ED00030470A03D050470240470601F0554703E8 -:102EE000104706200200F047050C3020470928470C -:102EF00003110001E047032038FC8002470A1E8FBF -:102F00000847050C304708080040470330294704AC -:102F100002BC03D0470AC04703F047050C302047E6 -:102F20000A1C0F10470829DCA5003047090E8F083E -:102F300047050C30470C0F47094150550020470901 -:102F40000200F047050C3020470D0447065C76EC84 -:102F500080024707084702068047060C30470802F0 -:102F600047050447030147033CF0470A01400C00B2 -:102F7000F047050C3020470820470B1C21FAE732A8 -:102F8000004047100C3047090C470B2464F0F70849 -:102F90004709028047070C302047141C31CCE7203A -:102FA000804703383D444702028047070C30470FF3 -:102FB000404705027668FB47024000C0403D47039A -:102FC000028047070C302047080C47050147053CA5 -:102FD00063DE800010470C0447040C30470A20478A -:102FE000040247052402E847021047060101404752 -:102FF000020147050C302000A100123C20000447CC -:10300000021EF060470720001EF04047030D72BD0E -:10301000400003428EEF47060C30470503C0C0470F -:1030200004F047024000974703042000F0470401E2 -:10303000403C470201429C47070C302047033A3C82 -:10304000200647030CF060180210800006014002C1 -:103050004AA500188000077815006047020CC96077 -:1030600047050C30470503C0104704F04702050927 -:103070000747038001D05547039000401620000108 -:1030800042C1096047050C30204703323C202C47E1 -:10309000031CF0403047028000044702204705012E -:1030A0008839414702020025ACC02047040C304754 -:1030B0000503C0044704F00130080109000C470271 -:1030C000404705F00F4282010020402547070C30A1 -:1030D000204703323C20470280001EF04047028018 -:1030E000E847037C470705781547044488002047D4 -:1030F000040C3047040803C80001470210F04703DE -:1031000071094703964708C0164000034215002086 -:10311000470202001C0C30200001017A3C204704C9 -:103120000EF0600008704703A00601DAA047090A04 -:10313000007E8800300002C0000C3047038D000381 -:10314000C04000243000F0470308FB470240000362 -:1031500040F54709014287002047040C0C3020202D -:1031600000013A3C20201004470708040010000426 -:103170005AA102004000A04705603EEFA547050C9C -:10318000300010070D0803C047020247070520471B -:103190000334E001470B1C05A047020247020C3034 -:1031A00020040500723C20001047060450800447AC -:1031B000022021EEE20200401810470403601FAA1B -:1031C000F5470202C0000C30000405470203C000AE -:1031D0001004470542D8BD2C00103C7670424702CF -:1031E00040008047030102C02C00F047050C30204E -:1031F000000800723C2000A02078470330001047F0 -:103200000501DCA302304702804703C001C0470824 -:103210000C3000040B470203C00052144704300076 -:1032200008033C4702343830A300204001470481A2 -:1032300001C2C0470C0A723C204702A03BF9014082 -:1032400001404705202C0CF0404703078402470348 -:1032500006DEF5E0470A100803C10001547D69A7A6 -:10326000004040470402046000F047028050003CE8 -:10327000470402BCF1D010470601804702723C208F -:10328000060024017F7440014047030647034EA512 -:103290000018470215A3C2806002002C91A0470BC2 -:1032A0000803C047023C2E78A01002404707D055C3 -:1032B000470490003C470201401DF3F047070347D5 -:1032C00003121540040036687B64403047040CA0AC -:1032D00047020DD1000147020723C24702034614EB -:1032E000ABC420470A08154004023C004A5800704D -:1032F00047040C470203DFE24705803C47020266B1 -:10330000C20BC208470A03BC1047023C294B744059 -:10331000002847042000281EF04000021801A3C224 -:103320000004016060AB470C043C4703164268A0F0 -:10333000004047074010F04702050800803C470264 -:103340001403B4C70010470A03BC100C4702035D06 -:103350007A014706A00E60FEA047023001A7A3C273 -:10336000000803469E81A020470A043C000447024F -:1033700002C85A0847071E03C0F54705803C4702AC -:1033800001601D03F0470C295047021E01D95B1054 -:10339000404705800423CCA10247031123C2470400 -:1033A0001D95E0470B90168047021C314CA547083D -:1033B0000202C002470480003C47043EE1D0470AB5 -:1033C0001B47051F470601C047030620E8A3024725 -:1033D00003B325ED0004016640ABC42047090A474A -:1033E000050247060147052670034704AFB694015E -:1033F0000A0242B507C004471064F850104708702D -:103400001AA302B10001D04703C00003DDB5E047B5 -:103410001164005047083443D05300228051A04724 -:103420000380D6C76EE1D04709081E5BC81047036A -:10343000040AF0404709FE00404702198F3040025D -:10344000000166A5E0470A0B00121E8060001428E8 -:1034500000F04702400007470403F0A0408002004C -:1034600007B0470301C24E9447080180470506474D -:103470000308F0401847021000062000020C0A0062 -:103480001947020B3A81816002E68405A0204708B3 -:1034900001470810F04704B7470240043CC005004C -:1034A00002801087400101000260140240044706B8 -:1034B0000300080036C9580C0034001AF0403047A9 -:1034C0000218000CA14000FE504A024702012028C9 -:1034D0008047030480D0A047060100030A7A054212 -:1034E0004702144702F000304702FB00044080030B -:1034F000F0804047044028800003400100D080470E -:103500000603000500DE11D84702366C1AF060474A -:103510000308000D8000020C0A403008000B38013F -:10352000010002000D85E020470601000F0052E077 -:10353000404702142800F04703B007000902470281 -:10354000C00548200400074141044702401602E03C -:10355000470A08F680D2100E47021AF040470318B7 -:103560004703200400A5400080501175D04703C0D8 -:1035700067A0140447080B091B28C020470310F05C -:103580004704C900400E3C38C05A480100D887B5EE -:10359000F8470303AEE80080470808019F1414476A -:1035A000041AF040470280180400201E2200A541A2 -:1035B00030005C037B08084702E021CA470A050C7B -:1035C00056788240470310F04703F0E53C4703403C -:1035D000C05A40104702A7C30C04000262DC054732 -:1035E0000A0D81D6C9504705A54047029898470261 -:1035F000A004301C0540800018E0470560068501E6 -:103600004709050A32054A4704405A47039993344B -:1036100000400201C00A40470210B04703084702B9 -:103620000105470A0581FE51D047040AF0403000E9 -:10363000901000040004027E5F40100050E1B828A2 -:103640000400E0400FF812470A0B53E04A4705F028 -:1036500000300070FD3C04000220E00F48100079AB -:10366000098168040002600CC4471114004EA04098 -:103670000240470607C850C04703904705407EC731 -:103680000008470F020010A040804047063BC005DD -:1036900042470210D04705235DAF01470E0647079A -:1036A00044470306470203C850C0180018033E01F0 -:1036B000006000063DC0471104470542470504026B -:1036C000C005404702188FBC012047030E824716F1 -:1036D000100001C000044702674850C0100001E11B -:1036E00030104703604718300001470204470223A7 -:1036F000E005401047020DE047160C470705470359 -:103700000C0006674850C0F0001017B201284702AD -:103710000780471008470B08470222E00540204772 -:1037200002E7C00147234003E850C14703F04704C4 -:1037300002662F90471B10470203C005404703D085 -:10374000470401442FB040471D736850C010470222 -:1037500019F56C47020366EED0471E42C0054030A3 -:103760004702ADE11C47020142AEF08047024047EC -:103770000E0447090226036850C00007101DFB080D -:10378000084702068DE80020471038470A0403E086 -:10379000054040003085830C470508470F04470B60 -:1037A000040020736850C1104702804703C00060C6 -:1037B000208E5420470D04470B04000442E00540CE -:1037C000100051E04703804142650DA0470372009D -:1037D0009011010101470B104706404705014705BD -:1037E0004000024706084711204706804705024768 -:1037F00012084722010002470404471110470640FF -:1038000047050147054047031600014706044722C4 -:10381000020020000847020847031047243C000C20 -:1038200047063047688010470402004047040801FB -:103830004703020040470408014704302004474D75 -:1038400001470604472103008047050247030C4750 -:103850001F1E472D024703404705014717404705F4 -:103860000147060447030301470604470610471DA6 -:103870008047050247110C30472C0C3020472B0C99 -:1038800030472C0C3020472B0C30470E01470C10D2 -:1038900047100C3020470D02471D0C30472C0C30D0 -:1038A00020472B0C30472C0C3020472B0C30472C5A -:1038B0000C3020472B0C30472C0C3020472B0C3081 -:1038C000472C0C3020472B0C3047130800040020F5 -:1038D00055470880004247080C302047120C002052 -:1038E0000168554047074002004047070C30471524 -:1038F0000400200F470B0205500447040C302047FA -:1039000014260168F0C04709661E855447050C302F -:1039100047180F470DF08047040C30204715016809 -:10392000F0C0470A168F0C47050C3047150C4702AC -:103930000F470DF08047040C3020471501E8F0C018 -:10394000470A1E8F0C47050C3047150C20200F47E7 -:103950000B0200F047050C30204714266548F0C0E4 -:103960004709601C8F0C47050C30471620200F4775 -:103970000A460200F047050C302047156548F0C0A4 -:1039800047080207548F0C000147030C3047180FFB -:10399000470DF047050C3020471206470201C8F0DA -:1039A000C047076047021C8F0C47050C3047170CB7 -:1039B0000F470503C01247020401B0F047050C3061 -:1039C00020471501DAF0C0470423C0470307148FCE -:1039D0000C47050C304718F04703D9B0A4470280C4 -:1039E000470A0C30204714060008F0470350A78010 -:1039F000020040470A0C30471620300F4703D90019 -:103A0000A047040247080C3020471505780F80476F -:103A10000250A780024703404047070C3047152457 -:103A2000201B0F470528470D0C3020471501F80FC4 -:103A3000804703078002470C0C3047170D0F4704DF -:103A4000B020470D0C302047140661790F804703E2 -:103A5000078402470C0C30471504200D0F47035014 -:103A600000A8470D0C302047142004F80F804703AE -:103A7000070002470C0C30471506203C0F47035047 -:103A800000B4470440050A088047040C302047145E -:103A90003501C90F8047030100024702026000E5BB -:103AA0000447050C304715366C1C0F47035000289F -:103AB0004707588047040C302047141428E80F802B -:103AC00047030704020060470214E05447050C3026 -:103AD00047166D4CA547035000204704C0040A0850 -:103AE0008047040C3020471529415A80470387003E -:103AF000024702024702A504471B10044000F0479A -:103B00000301A9A00004800042471F207808F04765 -:103B1000020800BFA0214040020040471D100420C1 -:103B200060A54702404702A047020100400205503D -:103B3000471C2007605A8047030780024702026043 -:103B40001E8556471D0340A547035190B04702010B -:103B50004704F0471D01605A804702D9A3840247F9 -:103B6000020800168F0E470E01470D1C004FA54797 -:103B700004A0703C4706F0470E01470D2601E15AAC -:103B800080470219B5F43E47041E8F0E471C1C20C7 -:103B9000390F47031900B44704028200F1471C267D -:103BA00005580F804702F9E38002470360148F0C29 -:103BB000471C1C3C2F0F4703100034470320020210 -:103BC00000F1471C2061DB0F8047028003000247A1 -:103BD0000306548F0C471C3C381C0F4702180090FA -:103BE000F03C4703028000F1471D04C80F80180015 -:103BF0003DA5E03E006047021C8F0C471C3C380C82 -:103C00000F47031000283C47040190F1471D25CAC7 -:103C10000F8047029005643E47041DBF0C470E0409 -:103C2000470222CF4A6047050400042047031000E2 -:103C3000188F62E1004100421C05A0470E040004F9 -:103C400067FB404047050400360447031000981DF9 -:103C50006CF1024002002C470210014706044702A3 -:103C600008284702100025F03C600042470410027B -:103C700032DAC34000400185414100050003810064 -:103C80005004470A16A804470206067A7C424047B9 -:103C9000060402DFD348000459F0728147041EE095 -:103CA000504710023FE8F24707801C7AE0A5470220 -:103CB00020080F3001400001C25C05A00401470D3F -:103CC000100E62DFFA4707102026FC500100C018D2 -:103CD000E13A01000147026E4713214FF547070102 -:103CE0004221D90740007D000F2811000101C026A4 -:103CF000020414471006EBF64707011C05FD03619B -:103D000000021987F69D000247021F800420470821 -:103D100009470408000628E0182040470408470225 -:103D200002DFE3402000D80B656100940142B50139 -:103D3000470A0847040C002E07FA040247050C0046 -:103D4000042BCCC36070009187BA8900CA0200219D -:103D5000C20020470F3C0010A0470208470601F0B0 -:103D6000D3410147029FF95100040002B680404749 -:103D700011014EA0470902E8C342020070153EC17E -:103D8000470306D5A0A047100642D0B94047070414 -:103D90002830A047031805732147036037A1470F58 -:103DA00006000E3BDCB1624705064703DAA00018A7 -:103DB000007C0FBB09006001E035D047020A4708CC -:103DC00010470602CFE0470D0400190F21194702E2 -:103DD000014247040447080E47040E47021BD04720 -:103DE0000930470304059981720D470202470B030E -:103DF000000980920447047F60C247060401420321 -:103E0000400500100801804703400340470A0300B3 -:103E10000F80174705386CC302470504009E000E4B -:103E20000A20100058F047034047101814470403B5 -:103E3000F00347081E3C30A008470290E77F214767 -:103E40000306EED4A447090100DE8047030402DA2A -:103E5000430247073E056EA04703B819FF414702DA -:103E60004002B6891447090908941047034040F0FE -:103E7000024707802C3F5ECB470308A983610147B7 -:103E80000202020F470A0801F7470280470260C892 -:103E90000A0247060E20060CC7104702781BEB01EA -:103EA00008084702468F0847094B001640470201A1 -:103EB00046387B0201000247053C3C100A470330AC -:103EC00009807447020363C00F470A20001220478D -:103ED00003BC7AF947030D47052005DA0A2002479B -:103EE000020772B4080009405E8F081047080B0DE6 -:103EF000941A4247033C00A008470508001C294BC0 -:103F000008002020D005414702800002C20F470A66 -:103F1000080B06A590470369CAA02047050C00209E -:103F2000020C042032801809200008C047025C8F70 -:103F3000084709200E1000084703243004014705F4 -:103F400004470220CCA141470218B0C02847040210 -:103F50000F470C17900080001429EA04024705045B -:103F600047020378D1408000D005782847041C8F91 -:103F70000847090700D04703401472D94703014797 -:103F8000050401CA5B4703110567B8404702428038 -:103F90000F47080180000A760080808000767B80D1 -:103FA0002000024703060E14000DA708180159C58A -:103FB00073BC006047024C8F084709050EDF008084 -:103FC0004703237B470A26CEFF094000D9E3384147 -:103FD00047072047080518DA47053CE980200038E4 -:103FE000470603DBF7470338A1380147040C0008F4 -:103FF000004047023500501347030403C047022620 -:104000006D7040084047040400047C7005470308B5 -:10401000ABA39C20470203010F040040014020E0B5 -:10402000A00147030683D047022428DA501247052F -:1040300004002423EC4702800071B17EBD4040475C -:1040400002418F044703467E0D34470201470604B0 -:104050006410A04707020036DAF0484702500743D1 -:10406000000147040F04470201639CEC342000095F -:104070008047052065DEA0470901FFF4404000D9D4 -:10408000A720000A47020700CF044702024034017C -:10409000A04708022666500147094370A047035015 -:1040A0000FA40020470204010F0447042FE0022060 -:1040B000470826760E022247070621EC4704980F90 -:1040C000F420470303808F0647020140010A470698 -:1040D000154001800C62CC4705C047043D40E740D5 -:1040E000C10050A7030020470202410F044702020B -:1040F0000284AA47055A155001002026FE084704ED -:104100001000804702077CD34A020081D772000C5E -:104110000801E001CF040080004247050180080348 -:10412000C047020C41DD20470608002E39D00A475F -:10413000030800C02820470220010F0400C002002D -:104140004047040D98523C300240002ACF47070CEC -:104150000006036C470430073028010001C000EF5F -:10416000044703029E4705080803C047021401E004 -:1041700024001047061C207000414703A0C14702DD -:10418000404702010F04470437AA010447021952AD -:104190003C30470302DA10023002470604EE806327 -:1041A000470315310028C0470201EF04470340359B -:1041B000040010470403C0470303F04703404705CA -:1041C00006034A1047030803240020470202400F59 -:1041D0000400604703880121804702723C30470396 -:1041E00068EE402200404703060006021E20001829 -:1041F0000031EF32002160470201AF041047022373 -:10420000CDE0470603C14704EF47034047051401CB -:10421000E04705A5FBE0470306010F0447036004E0 -:10422000C0804704723C304703027D80470240470C -:1042300006036A0A4703019B2FF483470203808F1A -:10424000048047060100010E0803C0044702404BEA -:10425000F1493000080F28044710140500044706F0 -:10426000030008187A3C300447023BCDF040700050 -:1042700010470204470610470280470503A18A47FA -:1042800004028047060803C447050A40800070FD09 -:1042900024470280470D02804705028047040180C1 -:1042A0005A3C3040000600FA0A40470380470201AA -:1042B000470D0142C0470401404704038F470203F2 -:1042C000C010470238ED0BC10002008D28470A01DB -:1042D0000141840001C027824704068047065A3CFA -:1042E00030400006027A05490005470204000E47E7 -:1042F000092A81A047031F9380470950470203C042 -:1043000010002401505F4047027003004047023014 -:104310004703044706400002800F000847082000BA -:104320007A3C30000E00637AA540470280470504BE -:104330004705184704C00060008F000447091808AB -:1043400003C05800042C70E3404702909D000847CA -:104350000E01C00300F04709018B523C300C002CC9 -:1043600003FCC34200380008000C470E020014E0B2 -:10437000F847043FC04704080803C05047020170D3 -:104380000500300031F1000C000440470B40022EC4 -:10439000CA5047041DD080470319523C30204702C1 -:1043A00002DA470230008090000400207047057850 -:1043B0004706065415A8470803470303C0470204ED -:1043C0002C6A81400002000747030447052800F3D8 -:1043D000C2BF800003D280C0F0470701804702724D -:1043E0003C3006010C026F50401801470306470697 -:1043F00018180007E2954703400EE0F847095000FF -:104400000AC0470401DA3C47024010CB470402C00F -:10441000054442800847060402470222470820005C -:104420007840204703016D34470418470401485081 -:10443000400100704706071C000847037200701116 -:10444000020101470802470608470402470A0447D9 -:104450000220470680470C0147060447040147082D -:1044600002C0470310471702470608470B084716C4 -:104470000247060847040247060847062047068009 -:10448000471F0300070004470B7C471D03470F6CC1 -:10449000470CA84705024727204708044722024047 -:1044A000C0470408014703020040470408014705CC -:1044B000200447058010474C03470D70471F0147F4 -:1044C00003044709D0470340473A02471208470D03 -:1044D00080470A80000247030247058047050247DC -:1044E0000608470620472204470D401030C04718F1 -:1044F000067B38470240626D6140470383DC054715 -:104500000430C0471803DBD0470301AB2022C0476B -:104510000203C0008047021030C047160103F8ECC8 -:1045200000B001804702016200060083D6000A0045 -:10453000180030C0471703F83001F0470903C20DD7 -:104540001047021030C047160108805A00B014000E -:10455000AA49A16040030083C447030C0030C04750 -:104560001702002D01800800064B6C028003000337 -:10457000C247041030C0470A80470D14EC50104762 -:1045800002A2EDF540470383CC054702800030C00E -:1045900047193AD8F04703A1E420470303C2008B30 -:1045A00001001030C04716010A805F8047080300F1 -:1045B00083C60847032030C047172100281047084A -:1045C000020003C00547031030C0471002470980AE -:1045D000470502470483C647025C470230C0471ABA -:1045E00010470503C0470303C2470280001030C0D4 -:1045F0004717080969470740470383D605814703E2 -:1046000030C04718215D470720470303C0000A4711 -:10461000021030C0471708805F181047071000834A -:10462000DC4703800030C0470901470E227A008032 -:104630004707600003C2470201001030C04717045B -:10464000000A1847024010F1296047060A47033064 -:10465000C0471801504703040F00014047091030BC -:10466000C04717043C585010018010F16A0680477B -:1046700004014702180030C047183C105080470220 -:104680000F00414047091030C04717043C5878A03C -:10469000470210F121404703A906F380004800308B -:1046A000C047183C105847030F00400280002019F3 -:1046B0003FE547028C1030C04717043C5C470410AC -:1046C000F121604047028BACE00A470330C0471835 -:1046D0003C1000C047020F00400280002002A7B536 -:1046E0000A47021030C04717043C5C470410F160D1 -:1046F000470490B788470430C047183C1047040F60 -:1047000047050222EFF54702201030C04717080284 -:104710004B9980470210F16047048BAEF81B4703AA -:1047200030C0470901470D0210DA1047030F00405F -:1047300047030202A5A50A47021030C04717080226 -:104740005A470410F168470320A907D19A020C00C8 -:1047500030C047170122EB470220000F47048000BA -:10476000193EA58002081030C0471708826B470425 -:1047700010F1284704A93CE00A470330C04718114C -:104780005D47040F0040470280022A268000020095 -:104790001030C04717183DC04704169947058001DF -:1047A000B50A08470230C04712028C000800083CD6 -:1047B00020470303096B47053FC1ED9047021030C6 -:1047C000C04709056047081C470260470501801083 -:1047D000F1624702060083DE4703180030C0470934 -:1047E00040470802803047068000010F0041C04763 -:1047F0000303C047041030C047130C7C003047034C -:10480000100080800AA13847020300A940804702B7 -:104810000C0030C0471202C278003047030847023C -:10482000C00AA040470429404703081030DE471063 -:10483000084702404709C060F180470483C4470528 -:1048400030C2470F01140003470A4000F0800008FF -:10485000470203C20D0B47021030CE470908470933 -:104860005C470A2A500447030183C44702404702B9 -:1048700030C2470908470620020247028047062047 -:104880000045A0344702800003C20947031030C02E -:104890000700A0470F804E78470720002A5047099D -:1048A00002470230C04709084708B2427847090565 -:1048B000A03C402847081030C04712800E470C0229 -:1048C0004702808083D6470530C047090847062045 -:1048D00000F24000084704D0002047030240008057 -:1048E0000003C0470202001030C0074703020F78E0 -:1048F00004470220470854050030470680470A1C39 -:10490000000C0030C04704020F080247082000B323 -:10491000823A8020470680470580470410A204475A -:10492000071041F700280010470380A04705080042 -:10493000080379000447095981A50A40470ADFB4F2 -:104940004047057147060800608369D0B0470919E0 -:104950008D0D90470902248380280044090AF9003C -:10496000A0018047030800083DC080E0018020F5D9 -:10497000470306005A64A0470A02E7B3C04000108C -:10498000222FDA11B047050847023C2071B04702D8 -:10499000A0F080038047021A74470B02AA771447DD -:1049A00003014148504702804704304704A01000EB -:1049B0002A57A86047035A44850A470902AA0C02ED -:1049C0002C4702014000D8B000804704204703F183 -:1049D00047030F50F5C047031A640090470A189226 -:1049E000D428000101406A50104707083DC03047F5 -:1049F0000320F5806047035A4488470B341EB46C8B -:104A0000470223401BD8F0470608203C00304703EC -:104A100020F08047041A65010B470940053EC04756 -:104A200004046B8047020800204708144703020073 -:104A30000347020CC5A8000D404707080807C047F8 -:104A400004035810470202003C4708084704404747 -:104A500003332CBB0E008047080112862800014258 -:104A600094DB4703C0470F08470780470A1E804072 -:104A7000470202003B00900080471028470660472D -:104A80000850A55EE060000208BBDA80E04707013D -:104A9000E94E518047061047023C1C07470B85062C -:104AA0008447042A6EF04706744702AA78F847073D -:104AB0002847023C0207804709025A0054700070E0 -:104AC00047038047070108F46A470420F182470240 -:104AD0000240029DE7470280470702A503424000CB -:104AE000304703F000024707980B470420F001C04D -:104AF00047028002830080014708030F52A047034A -:104B00000C3E6A47090294DE18470A5414850A4786 -:104B10000AF0983C470303C360470901541FD0477C -:104B20000990144702904709030F13804702600C55 -:104B30003C4E30000180470614C8470201804703FD -:104B4000E00846005A45E04702184708F0470503C9 -:104B5000C0470A02BC1CD04706220020001A74007D -:104B6000084709030F13A60447020C3C4A98104754 -:104B70000505000294DD51E000100AF1844704436A -:104B8000C48047020C4708F0043C20470203C010D1 -:104B900010804705020001540909470305F0A047AA -:104BA000043C010D0B000C4707030F12806030001E -:104BB0000C3C4AB180C8470715CA5004470220F58B -:104BC00080038003005A65E047020C4708F0044761 -:104BD0000403C010100014470602BC1C084704F070 -:104BE000470302001A54000B00044707030F178005 -:104BF00047030C3C5E47028047060294CDF9F0471C -:104C00000903C4470CF0470503C0470A01541F704D -:104C1000D0470603470203C04708204702030F1688 -:104C20008047030C3C5A0001C8470714FAB8E04714 -:104C3000080183DE47021C4709F0470503C047030C -:104C400014470602BC3CB890470983C0470B02AA30 -:104C500017A06047020C3C5A018047060101E8FF9B -:104C600071C0470507804704010010804708AA0467 -:104C700004470303C010470A542C0847060447069C -:104C800080470C200400200AA85E181400C04704C6 -:104C90000108F54C704704A76000044705080008A8 -:104CA000470A0420001002A810008A0040470404AC -:104CB000006419F8F0000100A0470A08470E083DFB -:104CC00060000447052000083DC047048005E0005F -:104CD000380004C3FCA04711483C00F04708083CDA -:104CE00047062BA342D000043C3E470B020F7847F7 -:104CF0000360083D6010000180470A05804019B03C -:104D000006000600C3C4E04702184707020F084721 -:104D100004083C00F8470B700008470201EB4047CD -:104D200002243C471230083CE070470280470440B0 -:104D3000083DE0304702802845E04704C3C4A9810C -:104D4000470F22083C20080014C04705083C2047B4 -:104D500003C0242BA002C047023C010108470902FE -:104D60000F304704083CE001F04707083C60470368 -:104D7000204009B047023000C3C4A9984709020F78 -:104D80004705083C2001E04707083C20470521EB88 -:104D90004047033C0101470D20044702183C6000D6 -:104DA0008008470405470905E2E0284702C3C5E03B -:104DB000470D3C384702083C20014705024705B033 -:104DC0004703808BA06047033C4713083C40F847EB -:104DD00008083CC047042A01A440470201C3C5A0FB -:104DE0004711083C20F04708083C20B047032050FA -:104DF000A04703043C471202083CC04707010008D3 -:104E00003DE0470501B1404703C3C5A0184710085E -:104E10003C470A083C4702C047033B7C2047033C11 -:104E20000103470D0E010020083C60470380470541 -:104E3000083CE047038400012447020200AA85A140 -:104E400001000C470B028000083C200004470708C3 -:104E50003C2010E147020200B447042A8100080008 -:104E60000447027200901103010147080247060837 -:104E70004704024708078004470220470680470C82 -:104E800001470604470401470D10472A0847160248 -:104E9000470608470402470608470620470680479A -:104EA0001F024703044729034729A0472D10471E27 -:104EB000020040470408014703020040470408017C -:104EC0000780470320044705801047450147060334 -:104ED000470D204718024706034703044709C04708 -:104EE0000340472B38472108470B400080470A8082 -:104EF0004705024705804705024705180847062071 -:104F00002C471101470F04470D400030C0472B10BC -:104F100030C0472C30C0472B1030C0472C30C04722 -:104F20001004471801001030C0470F80471C30C0E4 -:104F3000472B1030C0472C30C0472B1030C0470ED5 -:104F400010471D30C0470A0200140B471D1030C027 -:104F5000470C28E1471E30C0471B04470F1030C0E4 -:104F6000471B0A471030C0470B20280B471D103045 -:104F7000C0470C14E1471E30C0472B1030C0472CEF -:104F800030C0472533069047031030C047242031F6 -:104F9000A480470430C0472B1030C0472C30C04796 -:104FA000253C0247041030C0472304803C144705C9 -:104FB00030C04708804721801030C0472901400099 -:104FC00030C047270147031030C0472C30C0470A84 -:104FD00080471D0A47021030C0472801470330C0F0 -:104FE000471D0300F047070547031030C0471FF176 -:104FF00047070D9A470330C0471702A809704705B5 -:10500000404706010800041030C0471180470501E1 -:10501000544147041000796047050801000800303A -:10502000C047150447023C47060B404703802A81CE -:10503000000847021030C04715020007C058470457 -:105040001008684704AA85E199470330C047183C17 -:105050001047050301428047023C47051030C04716 -:105060001707C04A470410086A06C04702C3C5A014 -:10507000470430C0470F0447053000025A40470537 -:1050800003014047033C47051030C0470F0A47075C -:10509000065848470410086B66804702C3C5E00005 -:1050A00080470230C0470FF047083C100847040B08 -:1050B00041428047023C0147041030C0470F10476F -:1050C0000707C05A38470310002A06C04702C3C466 -:1050D000E018470330C04711C04705025A50014746 -:1050E000040B41428047023C010147031030C04796 -:1050F0000E50470280470506585E71C047021000F7 -:105100002B66C04702C3C4AF00A0470230C0471798 -:10511000025A504705034704013C47051030C04779 -:10512000100180470506584870E0470210002847E4 -:105130000301C3C4E04702180030C0470B083C47D6 -:1051400009010001409847040100034047023C3632 -:1051500047041030C0470B083D6047090447023838 -:10516000E0470210002800404702C3C4E7470E3C56 -:1051700047021010D9470D04470506C04702080230 -:1051800047112000034B470D02470502800010081D -:105190001CE0470D0200084704020020470302807C -:1051A00047041047041BB842C04702108090090210 -:1051B000470B1C470C01470A073E470310149795FD -:1051C00098471301E04707016BCA999047093016C9 -:1051D000B500024713F0470701606999904705063B -:1051E000804702103488470F084704010020470415 -:1051F000040002A8100847030A008364471580478B -:105200000401E04705020202A85C1847030A03797B -:1052100042804718D9F0470702003C1000E04704DD -:10522000704704069E901B47110B4B80E047070711 -:10523000C05C001000040807B0470301A94F800AB2 -:10524000470E053203D42008B002470508003C474A -:10525000020A470240A08047042801DD80470F3042 -:1052600003D56A7880024704300007C05C00044719 -:1052700003A37847051C95471E3C4702A047090E2B -:1052800001AD1A470E0160470378470817C05C005C -:10529000104708100617900A00184707020F470525 -:1052A000A0014C470602900147021000E0470208A7 -:1052B0000B38470534A01B4709920F784705824DEC -:1052C000470601470204000C0010470303BC00041A -:1052D00047020216950A471103D0470220470406E9 -:1052E00004003C2047032020F08047040237800957 -:1052F000471004001A788147050104183C604703F1 -:105300002020F78166800201802CE04703023FC025 -:105310004709024703D070002047040247062020B7 -:1053200020F00142808000138299090200443FC0AE -:105330004709010004001A8180470B01002020F377 -:105340000366C00012A34605471340470A3C4702C4 -:1053500080003020F047051E810029471004021A02 -:1053600001E0470604183C60470420F780E047024C -:10537000101E8CC0470E01470301C009E0284709F1 -:105380007008002060F08044008000084E810A0010 -:1053900080470B02470204021A79F004470B2000F1 -:1053A00020F3800700800004B408471202D050A008 -:1053B0004707603C2047032060F080470250001000 -:1053C000038D471104000A50A0084706183C4047C7 -:1053D000032020F7470410001D80470E01470203F9 -:1053E000C25010A04707229447048060F080470213 -:1053F00002003040C79F470D02800007C0CAD04757 -:1054000007081A9408470201E020F7470303103009 -:10541000941F0E471003C06047020447050C029416 -:105420000050A0006060F047052001ED80471007A4 -:10543000C1EE01E0804705080A94084702010020F8 -:10544000F7470306100014A5470218470B0147024F -:1054500003C040508447060C01680C50A00038601F -:10546000F0470502810D1F40470C02470207C0EAC2 -:1054700000B84705020A0A940100A0470220F74736 -:1054800002084000029EE00A471042A81030800047 -:105490004047112047040B47100AA85A001000C0CB -:1054A000471150471543C010008047083C4704018E -:1054B000010F34028047020142000E47100C3C5A93 -:1054C00001904707083CE04705073140470220812B -:1054D00055E0471123C0003047093C000800400058 -:1054E0008000A0000347160C3C5A00014707083C07 -:1054F0006071804703512047070E471023C04702C1 -:1055000001C047073C20008080000FF0A1404717F2 -:105510000C3C5E4709083C40019047028FA52002E1 -:1055200080470401471123C0101047093C2047035E -:10553000010103B8001000402042E09047100C3CED -:105540004A99804707083C40470402074702200069 -:10555000100056879E471023C0101047093C0008D2 -:105560004702C04702014047031C16B147110C3CDB -:105570004A984708083CC0584702804707102DD07A -:10558000471143C0470B3C47028000404704034794 -:10559000160C3C4E470201804705083CC000100134 -:1055A000470520470718470E43C2E0470A3C201032 -:1055B000470501428047050D47100C3FEE4709089B -:1055C0003CE0984706028047050110470C40471A07 -:1055D0006300294000100020470B704708804711E6 -:1055E0001200A9408018470D4002470408472720B1 -:1055F00001470438471206470618004047250300B4 -:105600004003C20010472701470283D60018470E07 -:1056100030470401470C04470540471B80470B02F5 -:10562000470402470240470F0F06804703083C002B -:105630000147050285470B044702040281C0470960 -:105640000102007A804703083D40008047040102C0 -:10565000470B074703A804B18A470D280002083CFE -:1056600020014702C0470408403C00088000802A0F -:105670005034024702840281E108470C06470308C0 -:105680003C6001E000C047034000183C605010142B -:105690008025A04702404702E804B981470F50081F -:1056A0003C470B3C20014702402AA0404702010032 -:1056B0000280C0471050083D607A000180470518FD -:1056C0003D4000A001002AA73047020405E804F08D -:1056D0000A2018470C7000483C470702700023C09E -:1056E00000700447022E7B384703060294E00E4701 -:1056F00010083CC047090BC140000A47022240285D -:1057000000054702E826B00147031006B0470480B1 -:105710007E80470301541847080800B01908D04795 -:1057200005034047022A81470580BEF198470202DF -:105730000276803847022295EA01E10080470401A1 -:105740000A75E971F0470401E04002012A96E780FA -:105750004705D010470280AF0654300002017C0B91 -:10576000470601470202F43C47048050BC604702F0 -:105770000430E6D047048286B01C470305337668C0 -:105780004703147D470780010141791808C0004094 -:10579000A187E0470391E7B18047032004A14704B4 -:1057A0008412822847020968090880470702BC2A38 -:1057B0007180470401E047033E820919470380A531 -:1057C000E847030200F20E6C47020A940139E1C077 -:1057D000470714FA01C04705E047033E94C0094754 -:1057E0000303C14705AF0E42600202096808704713 -:1057F000080154087047034000FC470515910F4706 -:105800000343C4A0470304053A462805000A940050 -:1058100080470802955E8008000400116447055423 -:10582000A8470403C10100180008550A47042014C2 -:105830001C470601470202BC3A70804702050080FF -:1058400002470303C0001E470343C5AD9A4703A5A3 -:105850001F86470302BD6950A0470580470215EC2B -:1058600080B047020501214040470283D6000A4725 -:105870000303C0050A4703403604204702083C20C2 -:10588000084702804702024703D82F08F04702A8C2 -:10589000503C002047023C00D59B470343C488018D -:1058A00047020220D2A0644702083CE0504702C0F1 -:1058B00047040148D47A701000042475F847043C6A -:1058C00015C38A470303C0470310000403CC0040FC -:1058D00040083C4708040001C019D847030500BC34 -:1058E0000020008028B5918A470343C5A01E001000 -:1058F00042205220600050283CE07980018047041B -:105900000102E8EBB80001800007A1600006001466 -:1059100007E947021847023D000D000200050AC0D2 -:105920003C47047C10F0470702A809D0A540002C92 -:10593000BD746247022030CE8D47044021A01B4732 -:1059400003A557542802002040F980B047040147BE -:105950000202955D800847022C3DF7604020002040 -:10596000F4950A4704720080110062002F824702FA -:105970000103470410000800101002020400220076 -:1059800020200008042404470708044703200400DB -:105990002020202400082C040C0C47030410100CB9 -:1059A000001000084703080847030830003030049F -:1059B000002226060408000404000447042008000E -:1059C0000C000828000C000800280800100808280F -:1059D00008470228282847020C101010240002242F -:1059E000242C2C0400282C21010800113010200048 -:1059F0000422022247032020203004083010001027 -:105A00000800040404040004000447060800040815 -:105A10000808470208470224240400042C2C2828E4 -:105A200047022101211004083010201047022000F5 -:105A30002020080420202000044704040800040457 -:105A400004040010101010100006081008080302CB -:105A50004702040408020424242404002C2C2820D7 -:105A6000020021012100020A200028000802282843 -:105A700020300400303010200002470204000A02E7 -:105A8000040400140006101010080447020808005F -:105A900008020C040C2C02022C2C2C2C0006282AA8 -:105AA0002A470208220022000C022002202047027E -:105AB000202030300202507040000A0E0C080C0406 -:105AC00047030400100808101002120400022020EE -:105AD00020080824242400020247040A0E47030277 -:105AE00004470206240608082C2428280202201055 -:105AF00030100E0A201028084702280020200A082B -:105B0000203030180400021A0A040A0A0C040C0C93 -:105B1000470208082808020C080828080C02280A6E -:105B20002802000808022008080020282010000C85 -:105B30001010041008020404280C04082820212155 -:105B4000080031303020060E222222200E06204787 -:105B50000220040C101010000A4702040404000C78 -:105B6000040447020A02470208000608080A02085D -:105B70000800060406040206082A284703212131EA -:105B800030060A30302020020220202000080400C5 -:105B90002010000600100010000C0400040404068D -:105BA0001614101010020618100808014702080405 -:105BB000040A020404040604082A0C2A220A022306 -:105BC000232322000822222A200A02282A30100435 -:105BD000001010000802020008080008000C040C65 -:105BE0000C0206180818180400081808080A020C05 -:105BF0000C0C0600080E04220E0A06222A2222009D -:105C000008222222220C022222222202003200102A -:105C100010000240504000020C08080C04080A045E -:105C200004470308101012120400024702200E0459 -:105C3000240424200C0E02000202020C02020202C2 -:105C400004020602060602080A262A08080B30305B -:105C500030300608202028284702820080010300F7 -:105C600002282828000208082818180E091A1A0AFB -:105C7000080208080C0C0C00020C002208000E029E -:105C80000A22020603020222020808020222020875 -:105C90000822203010020410140404080004000834 -:105CA00008040808000121020011301020080E022B -:105CB0002202000A084705021000101002470210D5 -:105CC00000040A0C04040006080802000A02060088 -:105CD0000A0A020E0A000606060200040A0A0A025E -:105CE000020013011130040A0030002002470508A9 -:105CF0000610470306470502081000044703141462 -:105D000010100002181018180147020804060200BB -:105D1000060606020C080A0A0A00080A0301032004 -:105D2000470202200A020A001A0A1A120400081A7C -:105D300008080002080808080800080C0C0C0204F7 -:105D40000C001810040010180010000204060606CB -:105D50000808060202000A0C0002000247032000A5 -:105D6000220C02000212000200121212024702402C -:105D7000424047020C0808080400020404044704D7 -:105D80001012120400124703020004060602470222 -:105D9000020202024702024702060402040606004B -:105DA00002000A021A0A00011212003204470220FD -:105DB0000820000208080808020008000810040370 -:105DC000121A120847040402020206060202000424 -:105DD0002202020006010002000247022200020A1B -:105DE00047023A2A321A020404120404470408083B -:105DF0000400080001010200111010470204020211 -:105E0000024708024702101002001010001000069E -:105E100004060602470202020A000600080A0402FB -:105E20000002040400020004080A0A10020013130E -:105E300013020447020247061047020647021000F9 -:105E4000060010001047020A4702144703141410FA -:105E500004000218001818000110120616020006AD -:105E60000602000400080808470202010101470475 -:105E70001018100200181A1A0A04000A0208020078 -:105E8000020008000847040404020404040010048B -:105E90000010101210000216060606470302470201 -:105EA000020408080847061004021000101002003F -:105EB000100202020400020200020008080808089A -:105EC0000002040404044704120204001210101219 -:105ED00002000616060247030247060400040004F7 -:105EE00004001402001818181A0001120202020419 -:105EF00000024702080002080800080247050401E2 -:105F000012021210470210120612000206060006C4 -:105F1000000447022000060120002047041018104A -:105F20004702383A320A0204060606044702110103 -:105F300062003F8247020103470802470802470602 -:105F40000101470620204706202047060420470677 -:105F50004004470740470E858447070147068047AE -:105F6000088047060202470602470802471E044708 -:105F70000804471E0404471E204707202047062028 -:105F800020470601470801473601014716424708E6 -:105F90004247060202470E212047062021470610ED -:105FA00020470614144706505047061010470610A5 -:105FB0001047069511470610944706909047061023 -:105FC000104706021047060202472604044726042B -:105FD000470804471E204707202047062020470681 -:105FE0000101472E01470801471E42424707024769 -:105FF0000630204706303047061111470614104777 -:106000000650144706105047061010470611104757 -:106010000610114706949447061090470610104743 -:106020000612104706121247061010470710470EB7 -:1060300004470804472E0404472E20204706212049 -:1060400047070147260101470882008001034720D6 -:1060500052024706305247063030470610104706B6 -:106060001110470604154706404047160101470E28 -:1060700084470804470E1202470612104706101202 -:106080004706101047061010470604144736044709 -:106090000804472E202047060121471E014708011A -:1060A000471610470712104706301247067272470C -:1060B0000710470E0447074105470740470E0147B1 -:1060C000080147160404470602470802470E020269 -:1060D00047061047071010470610104706101047D4 -:1060E0000710476621204707214716010147161070 -:1060F0004707101047061212470620304706422075 -:1061000047074247164140470701470E010147260E -:106110000202471602470802470E104707101047B1 -:10612000061010470710475E204707212147081136 -:106130000262002F824702010347081B1B47041F0E -:106140001B4704041F47101B47051B1B47041F1B4D -:1061500047041F1F47101B1B47041B1B47041F1F1F -:1061600047041F1F47051B470A1B1B47051B470406 -:10617000040447041F1F47051B470A1B1B4704E471 -:10618000FB4704E4E447041FFF47051B470AF919CE -:106190004704E4F94704E4E44704F7F747051347E5 -:1061A0000AF9F94704E4F94704E4E44704F7F74738 -:1061B00005F3470AF9E9470414FD47041E1C470488 -:1061C000F7FF4705E1470AE9E947041CFD47041EBC -:1061D0001A4704EFFF4705E1470ADDC947041EDD02 -:1061E00047043E3A4704BDBF4705A9470ADDD947E2 -:1061F00004A2FF4704A6A24704BDBD4705B9470A4C -:10620000FFDD4704A6FF470484864704BDBD47045D -:1062100004BD470AFFFD470406FF47040404470482 -:106220003D3D4704043D470AFFFF470406FF47047E -:1062300004044704FDFD470404FD470AFBFB470532 -:10624000FB4704244705FDFD4705F9470AFBFB47CB -:1062500004FBFB4704FFFB4704FFFF4705FB470A1E -:10626000FB224704FBFB4704DFFB4704FFFF470516 -:1062700022470A22024704FBFB4704DFDB470426D0 -:10628000FF470522478002820080010347022C2C31 -:1062900047041F3E47041F1F4704CCDE4705C8477D -:1062A0000A3E2C47041F3F47049F1F4704DEDE477A -:1062B00005C8470A3F3E47041F3F47049F9F4704C6 -:1062C000DEDE4705DA470A373647040137470489D7 -:1062D0008147045ADA470552470A76364704C1F621 -:1062E0004704A9814704FAFA4705D2470A7474475C -:1062F00004C1F44704A9E94704F2FA4705F2470A42 -:1063000074744704E5F44704EDED4704F2FE4705D5 -:10631000F2470A7474470427744704CF8F47047602 -:10632000FE470570470A747447041F7447049F9F13 -:10633000470474F6470574470A746447041B74479E -:10634000049B9B470476F6470574470A6060470440 -:10635000197047048999470476F6470576470A601D -:10636000604704096047048989470476F647057643 -:10637000470A64644704096C47048189470476F638 -:10638000470576470A76764704097E4704818947A0 -:106390000476F6470576470A76764704297E470451 -:1063A000C189470476F6470576470A777647047F22 -:1063B0007F4704F7FF470477F7470576470A77607A -:1063C00047045F7F4704B7FF470477F747056047F7 -:1063D0000A612047045F7F4704B79F470461F7477E -:1063E0000540478002110362003F824702010347D4 -:1063F000838282008001034783822244B201060027 -:1064000047000000000000000000000000040240FF -:10641000000400000A00090000140240000400000B -:106420000A000900000C0240000800000B000900EF -:1064300000080240000400000A00090000140240A5 -:106440008000000007000900000002400200000078 -:1064500001000900001C02402000000001000000B3 -:10646000001C024008000000010000000000024083 -:106470000008000001000000000000080080000883 -:106480000000010800800108000002080000040864 -:106490000000080800000C080000100800001408A4 -:1064A0000000180800001C08000008001000180078 -:1064B00020002800300038004000480050005800FC -:1064C000000000006410B71DC8206E3BAC30D92618 -:1064D0009041DC76F4516B6B5861B24D3C710550C4 -:1064E0002083B8ED44930FF0E8A3D6D68CB361CBEC -:1064F000B0C2649BD4D2D38678E20AA01CF2BDBDA0 -:106500004932435F504D49435F4D414700537475D5 -:10651000636B20627574746F6E2072656769737443 -:10652000657220697320696E76616C69642C2063E2 -:106530006C656172696E672E00427574746F6E20AF -:1065400069642000697320737475636B2100427560 -:1065500074746F6E20776173207075736865642042 -:106560006F6E20626F6F742E20427574746F6E2090 -:10657000636F756E7465723A2000446973706C6164 -:106580007920627573792D776169742074696D65FE -:106590006F75742065787069726564210046504794 -:1065A0004120636F6E66696775726174696F6E20F2 -:1065B0006661696C65642E00446973706C61792052 -:1065C000696E697469616C697A6564206166746575 -:1065D00072200020726574726965732E00446973BD -:1065E000706C617920696E697469616C697A617433 -:1065F000696F6E206661696C65642E004352455375 -:106600004554206E6F74206C6F7720647572696ECC -:10661000672072657365740043444F4E45206E6F6A -:1066200074206C6F77206166746572207265736583 -:106630007400435245534554206E6F742068696757 -:1066400068206166746572207265736574004344E6 -:106650004F4E45206E6F74206869676820616674CC -:1066600065722070726F6772616D6D696E6700454B -:106670006E61626C696E67203676362028446973D5 -:10668000706C617920564444432900456E61626C08 -:10669000696E67203476352028446973706C61799F -:1066A0002056444450290044697361626C696E67E6 -:1066B000203476352028446973706C617920564403 -:1066C0004450290044697361626C696E67203676B4 -:1066D000362028446973706C6179205644444329FC -:1066E000002E2E2F7372632F647269766572732F7A -:1066F0006932632F6932632E63002E2E2F7372630B -:106700002F647269766572732F6932632F69326301 -:106710005F68616C2E630046617374204D6F646521 -:1067200020506C7573206E6F7420796574207375BA -:1067300070706F72746564002E2E2F7372632F64F5 -:106740007269766572732F7065726970685F636FC6 -:106750006E6669672E6300616464726573732000FE -:10676000206973206F75747369646520737973741D -:10677000656D20666C617368006661696C65642094 -:10678000746F20657261736520736563746F722026 -:106790000050726F6772616D206661696C6564207C -:1067A0004000496E76616C6964206669726D77613C -:1067B0007265206465736372697074696F6E21001D -:1067C000436865636B73756D6D696E67206669728A -:1067D0006D77617265207570646174650043616CEA -:1067E00063756C6174656420636865636B73756D54 -:1067F0003A2000496E76616C6964206669726D7733 -:106800006172652043524320696E205350492066CF -:106810006C61736821007072765F65726173655F89 -:106820006F6C645F6669726D776172650070727615 -:106830005F77726974655F6E65775F6669726D77A1 -:106840006172650057652772652064656164004365 -:106850006865636B73756D6D696E67200020627982 -:106860007465730D0A00436865636B73756D202D45 -:106870002077616E746564200020676F7420004F7C -:10688000757220696E7465726E616C20666C6173DE -:106890006820636F6E74656E747320617265206228 -:1068A00061642028636865636B73756D2066616938 -:1068B0006C656429212054686973206973207265AE -:1068C000616C6C792062616421004F757220707276 -:1068D0006576696F7573206669726D776172652080 -:1068E000757064617465206661696C65642C2061F3 -:1068F000626F7274696E67207570646174652E00D2 -:106900004E6577206669726D7761726520697320C4 -:10691000617661696C61626C6521004C6F616469CC -:106920006E67207265636F76657279206669726D35 -:1069300077617265004661696C656420746F206CD4 -:106940006F6164207265636F766572792066697223 -:106950006D776172652C20737472696B65206F6E40 -:10696000652E2054727920616761696E2E00466140 -:10697000696C656420746F206C6F6164207265635C -:106980006F76657279206669726D776172652C2009 -:10699000737472696B652074776F2E20547279203E -:1069A000616761696E2E004661696C656420746F71 -:1069B000206C6F6164207265636F76657279206602 -:1069C00069726D776172652C20737472696B6520D2 -:1069D00074687265652E20534144205741544348E2 -:1069E0000048415244204641554C54006578697432 -:1069F000207374616E646279000D0A0D0A0D0A003D -:106A0000E29688E29688E29688E29688E29688E2A4 -:106A10009688E295972020E29688E29688E296880A -:106A2000E29688E29688E29688E2959720E2968838 -:106A3000E29688E29688E29688E29688E29688E274 -:106A4000959720E29688E29688E29688E29688E218 -:106A50009688E29688E29688E29597E29688E29692 -:106A600088E29688E29688E29688E29688E2959790 -:106A700020E29688E29688E29688E29688E29688F6 -:106A8000E29688E29688E29688E2959700E29688F8 -:106A9000E29688E29594E29590E29590E29688E2FB -:106AA0009688E29597E29688E29688E29594E29538 -:106AB00090E29590E29590E29688E29688E295972A -:106AC000E29688E29688E29594E29590E29590E2CB -:106AD0009688E29688E29597E29688E29688E29513 -:106AE00094E29590E29590E29590E29590E2959DE2 -:106AF000E29688E29688E29594E29590E29590E29B -:106B00009688E29688E29597E2959AE29590E295CA -:106B100090E29688E29688E29594E29590E29590CC -:106B2000E2959D00E29688E29688E29688E2968851 -:106B3000E29688E29688E29594E2959DE29688E254 -:106B40009688E29591202020E29688E29688E29548 -:106B500091E29688E29688E29688E29688E29688A4 -:106B6000E29688E29594E2959DE29688E29688E224 -:106B70009688E29688E29688E295972020E29688A9 -:106B8000E29688E29688E29688E29688E29688E223 -:106B90009594E2959D202020E29688E29688E295E1 -:106BA0009120202000E29688E29688E29594E29572 -:106BB00090E29590E29688E29688E29597E2968830 -:106BC000E29688E29591202020E29688E29688E27B -:106BD0009591E29688E29688E29594E29590E29506 -:106BE00090E29688E29688E29597E29688E2968807 -:106BF000E29594E29590E29590E2959D2020E296B0 -:106C000088E29688E29594E29590E29590E29688E3 -:106C1000E29688E29597202020E29688E29688E224 -:106C2000959120202000E29688E29688E2959120B6 -:106C300020E29688E29688E29591E2959AE296881B -:106C4000E29688E29688E29688E29688E29688E262 -:106C50009594E2959DE29688E29688E29688E2967F -:106C600088E29688E29688E29594E2959DE296887D -:106C7000E29688E29688E29688E29688E29688E232 -:106C80009688E29597E29688E29688E29591202090 -:106C9000E29688E29688E29591202020E29688E2AA -:106CA0009688E2959120202000E2959AE29590E264 -:106CB000959D2020E2959AE29590E2959D20E2959F -:106CC0009AE29590E29590E29590E29590E2959007 -:106CD000E2959D20E2959AE29590E29590E295905A -:106CE000E29590E29590E2959D20E2959AE295904A -:106CF000E29590E29590E29590E29590E29590E28F -:106D0000959DE2959AE29590E2959D2020E2959AD4 -:106D1000E29590E2959D202020E2959AE29590E2FE -:106D2000959D202020004C617374206669726D77F8 -:106D300061726520626F6F74207761732073746174 -:106D4000626C653B20636C65617220737472696B61 -:106D5000657300486F6C6420646F776E2055502017 -:106D60002B204241434B202B2053454C454354207C -:106D7000666F72203520736563732E20746F2066F2 -:106D80006F7263652D626F6F7420505246004669C2 -:106D9000726D7761726520697320657261736564D5 -:106DA000005761746368646F67206361757365641D -:106DB000206120726573657400536F667477617229 -:106DC00065206661696C75726520636175736564C1 -:106DD0002061207265736574004661696C6564208A -:106DE000746F207374617274206669726D7761725A -:106DF000652C20737472696B652074687265652EEA -:106E0000004661696C656420746F207374617274EC -:106E1000206669726D776172652C20737472696B7C -:106E2000652074776F2E004661696C656420746F0D -:106E3000207374617274206669726D776172652C5B -:106E400020737472696B65206F6E652E00466F72D9 -:106E500063652D626F6F74696E67207265636F760C -:106E6000657279206D6F64652E2E2E00426F6F74EF -:106E7000696E67206669726D7761726520402000D7 -:106E80002E2E2E0D0A0D0A00426F6F742062697457 -:106E9000733A20004153534552543A20003A00417E -:106EA0005353455254004153534552544E002A2ADD -:106EB0002A20575446200053544D33320053544D2A -:106EC0003332207065726970686572616C206C691C -:106ED0006272617279207472697070656420616E8B -:106EE000206173736572740069746F6120627566E6 -:106EF00066657220746F6F20736D616C6C002E2E4E -:106F00002F7372632F7574696C2F736C652E630019 -:106F1000FF00000000010203040102030406070849 -:046F20000900000064 -:0400000508000200ED -:00000001FF diff --git a/bin/boot/nowatchdog_boot_robert_evt@1478015115.bin b/bin/boot/nowatchdog_boot_robert_evt@1478015115.bin deleted file mode 100755 index 000baba678..0000000000 Binary files a/bin/boot/nowatchdog_boot_robert_evt@1478015115.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_robert_evt@1478015115.hex b/bin/boot/nowatchdog_boot_robert_evt@1478015115.hex deleted file mode 100644 index 5183aade98..0000000000 --- a/bin/boot/nowatchdog_boot_robert_evt@1478015115.hex +++ /dev/null @@ -1,1784 +0,0 @@ -:020000040800F2 -:1000000000AA01208D25000889250008E91D0008A7 -:1000100089250008892500088925000800000000BE -:10002000000000000000000000000000892500081A -:10003000892500080000000089250008892500089E -:1000400089250008892500088925000889250008D8 -:1000500089250008892500088925000889250008C8 -:1000600089250008892500088925000889250008B8 -:1000700089250008892500088925000889250008A8 -:100080008925000889250008892500088925000898 -:100090008925000889250008892500088925000888 -:1000A0008925000889250008892500088925000878 -:1000B0008925000889250008892500088925000868 -:1000C0008925000889250008892500088925000858 -:1000D0008925000889250008892500088925000848 -:1000E0008925000889250008892500088925000838 -:1000F0008925000889250008892500088925000828 -:100100008925000889250008892500088925000817 -:100110008925000889250008892500088925000807 -:1001200089250008892500088925000889250008F7 -:1001300089250008892500088925000889250008E7 -:1001400089250008892500088925000889250008D7 -:1001500089250008892500088925000889250008C7 -:1001600089250008892500088925000889250008B7 -:1001700089250008892500088925000889250008A7 -:100180008925000889250008892500088925000897 -:100190008925000889250008892500088925000887 -:1001A0008925000889250008892500088925000877 -:1001B00089250008892500088925000861070008AD -:1001C0006D07000889250008000000008925000847 -:1001D0008925000889250008892500088925000847 -:1001E0008925000889250008892500088925000837 -:0801F00089250008892500089B -:0801F8009C0000000100000062 -:1002000053B94AB9002908BF00281CBF4FF0FF317D -:100210004FF0FF3000F03CB882B0EC462DE90050C2 -:1002200000F01EF8DDF804E002B00CBC704700BF1F -:100230002DE9F041904606460F461D46069C00F00B -:1002400029F808FB01FC8646A8FB002300FB05C536 -:10025000B21A2B4467EB0303C4E90023BDE8F08125 -:100260002DE9F8431D46174680468946089E00F052 -:1002700053F900FB05F3A0FB074507FB0137B8EB7B -:1002800004043D4469EB0505C6E90045BDE8F88373 -:10029000704700BF00292DE9F047C0F2A280002678 -:1002A000002BC0F298808C4690469E461546044628 -:1002B0000F46CBBB8A4256D9B2FA82F33BB1C3F1A7 -:1002C00020029F409D409C4020FA02F21743280CD8 -:1002D000220C1FFA85FEB7FBF0F100FB11770EFB35 -:1002E00001F342EA0747BB4207D97F1980F0018139 -:1002F000BB4240F2FE8002392F44FF1AA4B2B7FB82 -:10030000F0F300FB13770EFB03FE44EA0747BE45FC -:1003100006D97F1980F0EB80BE4540F2E880023BB1 -:1003200043EA0143002203E08B420FD90022134627 -:10033000341C4FF0000518BF0124604265EB4501F5 -:100340005840514000196941BDE8F087B3FA83F283 -:10035000002A40F08380804540F2CD808B42C0F07F -:10036000CA801346E4E712B90123B3FBF2F5B5FAEC -:1003700085F2002A3BD1781B4FEA154E1FFA85FC07 -:100380000122210CB0FBFEF80EFB18000CFB08F359 -:1003900041EA0047BB4208D97F1980F0B080BB42D8 -:1003A00040F2AD80A8F102082F44FF1AA4B2B7FBB7 -:1003B000FEF30EFB13770CFB03FC44EA0747BC4536 -:1003C00006D97F1980F09980BC4540F29680023BA7 -:1003D00043EA0843ACE752426FEA060663EB430385 -:1003E00061E740424FF0FF3661EB410158E795402D -:1003F000C2F1200107FA02F34FEA154ECF4024FA6A -:1004000001F194401FFA85FC1943B7FBFEF24FEA55 -:1004100011480EFB12770CFB02F348EA0747BB4278 -:1004200005D97F1971D2BB426FD9023A2F44FF1A06 -:1004300089B2B7FBFEF80EFB18770CFB08F041EA17 -:100440000743984206D95B1961D298425FD9A8F157 -:1004500002082B44181A48EA024292E7C2F1200728 -:1004600003FA02FE08FA02F5914028FA07F32CFA83 -:1004700007FCF84043EA0E0E08434FEA1E48070CFB -:100480001FFA8EFABCFBF8F908FB19CC0AFB09F13C -:1004900047EA0C4C614507D91CEB0E0C32D2614582 -:1004A00030D9A9F10209F444C1EB0C0C80B2BCFBB9 -:1004B000F8F308FB13CC0AFB03FA40EA0C418A4527 -:1004C00006D911EB0E0125D28A4523D9023B71448E -:1004D00043EA0943CAEB0101A3FB0589494503D35C -:1004E00003D19440444500D2013B002220E7013B68 -:1004F00016E7013901E701231AE7013B68E708F134 -:10050000FF3852E709F1FF39CEE7013A8FE708F1EA -:10051000FF389FE7013BDBE72DE9F043002B40D19B -:100520008A42044615464AD9B2FA82F30F464BB1C5 -:10053000C3F12006994000FA03F402FA03F5F040F3 -:1005400040EA0107290C260C1FFA85FEB7FBF1F0E3 -:1005500001FB10770EFB00F246EA07439A4207D9E7 -:100560005B1980F0EA809A4240F2E78002382B441F -:100570009A1AA4B2B2FBF1F301FB13220EFB03FEA5 -:1005800044EA0242964506D9521980F0DA8096452F -:1005900040F2D780023B43EA004000263146BDE8E6 -:1005A000F0838B4244D8B3FA83F6002E45D18242C1 -:1005B00040F2BF808B42C0F0BC803046EEE712B9FB -:1005C0000125B5FBF2F5B5FA85F2002A7BD14A1B6D -:1005D0002F0C1FFA85FE0126230CB2FBF7F007FB58 -:1005E00010220EFB00FC43EA02418C4507D9491951 -:1005F00080F0A1808C4540F29E8002382944CCEBEB -:100600000101A4B2B1FBF7F307FB13110EFB03FECC -:1006100044EA0144A64506D9641980F09080A645B5 -:1006200040F28D80023B43EA00403146BDE8F08352 -:10063000002630463146BDE8F083C6F12005B340C0 -:1006400002FA06F701FA06F4EA40E94020FA05F555 -:100650001A4325434FEA124C4FEA154E93B2B1FBB1 -:10066000FCF80CFB181103FB08F44EEA01418C4224 -:1006700006D9891869D28C4267D9A8F102081144B9 -:10068000091BADB2B1FBFCF40CFB141103FB04FE1F -:1006900045EA01439E4505D99B1854D29E4552D93F -:1006A000023C134444EA0844CEEB0303A4FB07894D -:1006B0004B4503D351D1B04040454ED20026601E79 -:1006C0003146BDE8F083C2F12006954001FA02F3FD -:1006D00000FA02F42F0CF140F0401FFA85FEB1FB46 -:1006E000F7F6184307FB16110EFB06F24FEA104C03 -:1006F0004CEA01439A4205D95B1929D29A4227D97B -:10070000023E2B449B1A80B2B3FBF7FC07FB1C3361 -:100710000EFB0CF140EA0343994206D95B1919D24A -:10072000994217D9ACF1020C2B445A1A4CEA0646EE -:1007300052E7012032E7013861E7013818E7013B51 -:1007400071E7013B27E7013CACE708F1FF3897E789 -:10075000013ED7E70CF1FF3CE7E7204600261DE706 -:10076000014800F0E2BF00BF342B0008014801F04F -:1007700044B800BF342B0008014800F005BE00BF9C -:10078000342B000837B50024012002F0AFF90190A6 -:100790002546E0B200F046F801AA08B910551DE060 -:1007A000135D042B0BDD174800F03CF9019800F0B5 -:1007B00040F90120002102F089F9002020E00133F6 -:1007C000DBB2042B135509D90F48012500F012F9AB -:1007D000204600F02EF90D4800F024F90134042CD5 -:1007E000D7D1019C3CB10A4800F004F9204600F042 -:1007F00020F900F08FF80120214602F067F9284621 -:1008000003B030BD1D650008496500085465000847 -:100810005E65000808B500F083FB80F00100C0B2FF -:1008200008BD000008B50C22044B02FB003000F0AC -:1008300099FC80F00100C0B208BD00BFD42B0008B5 -:1008400010B5002401F096F8054820440C34017AD4 -:1008500000F06EFC302CF7D1BDE8104001F08BB8F1 -:10086000D42B0008044BDA691106FBD59862DA69CB -:100870001206FCD5704700BF0048004070B5214DFE -:100880008AB02148082101F077F828464FF48021EA -:100890001E4C01F071F82B68642623F0010394E8E4 -:1008A00007002B600C348DE8070000216846022207 -:1008B0000B4600F01FFC03AB94E8070083E8070039 -:1008C00000211846022200240B4600F013FC06A863 -:1008D00002F022F8089821460D4A70430023FFF7E2 -:1008E0008FFCC0F3420320F00F000004000C1843FB -:1008F0000D23B0FBF6F0E8606C60AC602B600AB0D2 -:1009000070BD00BF00480040000C0240042C0008ED -:1009100040420F002DE9F043334D83B02C6800AF07 -:1009200004F12E0304F1270823F00703ADEB030DB8 -:1009300050238DF8003021236E460DF106028DF80C -:10094000013000238DF8023003238DF8033004F1C9 -:1009500021035BBAADF80430244B03F11C0153F8BA -:10096000040B8B4242F8040BF9D11B7804F12309E4 -:1009700006F123001E491370224602F0C7F83146E3 -:100980004A46002001F046FD07F1080304F5927481 -:10099000314643F8040D42461868FE23B4FBF3F3D6 -:1009A000434446F80900073323F00703ADEB030D7A -:1009B00068466E4601F006FD044655206C44FFF77C -:1009C00051FFA64207D016F8010B552808BF00209A -:1009D000FFF748FFF5E75520FFF744FF00230C37EA -:1009E0002B60BD46BDE8F083380000201C2C0008B9 -:1009F0003C00002010B5441E14F8012F7AB10849BC -:100A00000B68FF2B0BD80A2A02D1FFF783FFF3E70D -:100A10000D2A1FBF581C5B1808601A71ECE710BD47 -:100A20003800002008B5FFF7E5FFBDE80840FFF7F4 -:100A300071BF1FB50C2201A901F01AFD01A8FFF733 -:100A4000D9FF05B05DF804FB07B502A9012201F842 -:100A5000010D042000F096F903B05DF804FB0021BD -:100A600002200A4600F08EB910B540F2F51400F0ED -:100A7000B9F850B1013C04D10548FFF7D3FF204637 -:100A800010BD642001F0E6FCF1E7012010BD00BFBD -:100A90008A65000810B5074C43F2CD71064848F648 -:100AA000B803224601F02CFD01462046BDE8104067 -:100AB00000F0E4B83C010020392C000810B500F02B -:100AC00099F8FFF7E7FF08B9154816E0002400F091 -:100AD00039F9FFF7C4FF0120FFF7B6FFFFF7C4FFA6 -:100AE00078B1002103200A4600F04CF90D48FFF7C9 -:100AF00081FF2046FFF79DFF0B48BDE81040FFF740 -:100B000091BFFFF7C7FF0028DED001340B2CE2D1E4 -:100B10000648FFF787FFBDE81040FFF7A0BF00BF02 -:100B2000AD650008C8650008E3650008ED650008CC -:100B300008B5FFF799FF0120FFF786FFFFF794FF45 -:100B4000002103200A46BDE8084000F01BB9000060 -:100B50002F2307B54A1C58430B4B00EB52001A7861 -:100B6000B0FBF1F0C0B282420AD002A91870042290 -:100B700041F8040D012000F005F90220FFF764FFA1 -:100B800003B05DF804FB00BF0000002007B502A918 -:100B9000042241F8040D012000F0F4F80320FFF7CF -:100BA00053FF03B05DF804FBFFF759BF0C4B014442 -:100BB00030B51C68884207D022689568AD07FCD51F -:100BC00010F8015B1573F5E71B681A68936813F456 -:100BD000C05FFBD10A20BDE8304001F03BBC00BF44 -:100BE000842B0008024B1868403000F0BBBA00BFED -:100BF000842B000838B5204B012200211C6804F129 -:100C00002005284600F048FA0021284600F05EFA48 -:100C100004F1380000F0A0FA04F1400000F09CFA62 -:100C2000012104F12C000A4600F036FA002104F1FB -:100C3000080001220B4600F05DFA0021012204F1B8 -:100C400014000B4600F056FA6068012101F016FF0F -:100C50006068002101F012FF94E8030000F08CFEB0 -:100C600023684CF207321A604FF4B8525A601A687F -:100C700042F040021A6038BD842B00082B4B2DE94E -:100C8000F7431C68814601208846E36A04F1200787 -:100C900004F12C060093236B019301F0DFFB38462F -:100CA000012100F013FA0021304600F00FFA012074 -:100CB00001F0D4FB684600F055FA054620B11C4807 -:100CC0000025FFF7AFFE2CE030460121383400F05C -:100CD000FDF9012001F0C2FB204600F043FA064670 -:100CE00008B114481BE0684600F03CFA054608B914 -:100CF000114814E0012001F0B1FB48464146FFF7DE -:100D000055FF3846314600F0E1F90C480921FFF75C -:100D10004DFF204600F026FA054610B90848FFF7B7 -:100D200081FE284603B0BDE8F08300BF842B000895 -:100D30000C66000828660008426600080664000881 -:100D40005E66000808B5022001F088FB0A4B1B68AC -:100D500093F8483043B10948FFF764FE012000F0E2 -:100D6000D9FD022001F07AFB0548FFF75BFE012068 -:100D7000BDE8084000F0C8BD842B00087F6600086D -:100D80009B6600080F4BF7B51C6802AD0F460121AA -:100D9000203405F8010D1646204600F097F964202E -:100DA00001F058FB28460121FFF700FF1EB138462D -:100DB0003146FFF7FBFE2046002100F087F903B023 -:100DC000F0BD00BF842B0008F0B50746ADF2044D1E -:100DD0000E460C460025B919B4F5806F6846A1EBA4 -:100DE00004010DD94FF48062A4F5806400F0D8F8B6 -:100DF000284669464FF4806201F00CFB0546EAE79D -:100E0000224600F0CDF869462246284601F002FB52 -:100E10000DF2044DF0BD08B5202001F047FD00287B -:100E2000FAD108BD08B5022001F040FD0028FAD033 -:100E30000220BDE8084001F043BD00B58DB0684612 -:100E400001F0BDFC0023684600934FF480730A93C1 -:100E500035230B9301F0E4FCFFF7E4FFFFF7DBFF22 -:100E60000DB05DF804FB000030B5002495B02A48B1 -:100E7000022100F081FD294D0021032228190C34A4 -:100E80000B4600F037F9482CF5D1002105F1480058 -:100E900000240A4600F000F9002105F148004FF453 -:100EA000407500F013F901A801F080FC102301A89F -:100EB00002940193172303940593049406940794D2 -:100EC00001F08CFC012001F0CFFCFFF7B6FF08A871 -:100ED00001F075FC662308A808941393129501F09D -:100EE0009FFCFFF79FFF08A801F069FC992308A861 -:100EF00008941393129501F093FCFFF793FF4CF2C3 -:100F0000503001F0A7FAFFF798FF0348022100F0E4 -:100F10003DFD15B030BD00BF001000A010640008FA -:100F200030B502218DB01C4800F026FD022001F0F2 -:100F3000AFFC684601F043FC4FF0806368460093C5 -:100F40004FF0407304934FF440730A93AF230B9315 -:100F500001F066FCFFF766FF01F0A2FC054601F018 -:100F60009FFC044601F09CFC240244EA004040EA55 -:100F70000504FFF750FF094B9C4204D02046002493 -:100F8000FFF757FD00E001240348022100F0FEFCBA -:100F900020460DB030BD00BF001000A020BB1800DF -:100FA00070B515468CB004460E461B48022100F071 -:100FB000E3FC681E01F06CFC684601F000FC4FF099 -:100FC00080636846254400934FF0407304930A23DE -:100FD00005934FF4406309934FF440730A934FF421 -:100FE000005308930B230B9301F01AFC304601F0D9 -:100FF00047FCAC4204D001F053FC04F8010BF8E7C5 -:10100000022001F05DFCFFF706FF0348022100F01B -:10101000BDFC0CB070BD00BF001000A0034A136BF4 -:1010200023F4FF6323F00703136370470038024083 -:1010300000F13F4000F57E00C0F387200A280AD85F -:10104000054A135C591C11542BB9044901230A6B3E -:10105000834013430B637047F4890020003802403B -:1010600000F13F4000F57E00C0F387200A280DD82C -:10107000074A135C53B1013BDBB2135433B9054942 -:1010800001230A6B834022EA03030B63704700BF0E -:10109000F489002000380240436813B5009301230F -:1010A000044600688DF8043000238DF805208DF883 -:1010B00007308DF80610FFF7BBFF2068694601F086 -:1010C00021FB2068FFF7CCFF02B010BD38B5037AD2 -:1010D00004460D460BB981F001052068FFF7A8FF13 -:1010E0002068A1882A4601F04BFB2068BDE8384003 -:1010F000FFF7B6BF13B5044640688DF8073000907F -:1011000002208DF805208DF8040020688DF8061067 -:10111000FFF78EFFA27A2068218901F035FB206855 -:10112000694601F0EFFA2068FFF79AFF02B010BDA0 -:10113000002313B5044601934368006800938DF8BB -:101140000710FFF775FF2068694601F0DBFA206899 -:10115000FFF786FF02B010BD036813B10021FFF74F -:10116000E7BF704738B504460068FFF761FFA18804 -:10117000206801F0FFFA05462068FFF771FF281C80 -:1011800018BF012038BD1FB5836804460093C368AB -:1011900068460193FFF7E0FF636902A80293A36921 -:1011A0000393FFF7D9FF04B010BD10B50446012129 -:1011B000083002220023FFF79DFF04F114000121F3 -:1011C00002220023BDE81040FFF794BF0368DB69EB -:1011D00010B5044673B9C36A63B10A2001F03EF941 -:1011E0002046FFF7D0FF2046E36A012198471420EC -:1011F00001F034F92046FFF7D8FF2046BDE8104043 -:1012000000F0E0B910B5044600F04FFA48B10A20EA -:1012100001F024F9204600F048FA80F00100C0B245 -:1012200010BD012010BD30B5044687B0C36A00214F -:101230009847A3680021012500936846E3680A46A1 -:101240008DF808500193FFF727FF68460021FFF74C -:101250003DFF6369002103A80393A3690A468DF843 -:1012600014500493FFF718FF03A80021FFF72EFF87 -:1012700007B030BD10B5044600F00AFA2368DB69F8 -:1012800033B9E36A4BB12046BDE81040FFF7CBBF4E -:101290002046BDE81040FFF776BF10BD10B50446EC -:1012A000FFF7E8FF2046BDE81040FFF78FBF0000C2 -:1012B0002DE9F3419846036804460E46DB6917465C -:1012C0000BB900205BE000F0F0F9B8B925681422F2 -:1012D0000021284601F025FC9DF828306F8020462B -:1012E0006B71089B2E71AB60099B85F806802B61A2 -:1012F00000F0ECF9236800229A6111E02046FFF724 -:10130000CDFF2046FFF77EFF0028DFD1D9E79A699D -:10131000B2F57A7F23DA013202209A6101F09EF859 -:10132000204600F0DFF90122236883F82020154AC7 -:101330000192019A511E01911AB193F82020002ABE -:10134000F7D1002283F82020019A42B11D7D05F0DB -:10135000FD02012ADBD16B1E5D425D4103E00025E9 -:10136000204600F0A8F923680022204683F82020B8 -:10137000FFF748FF10B92046FFF790FF284602B05C -:10138000BDE8F08180841E0010B5044618B90E48EF -:10139000A92100F0DFFF00212422006801F0C1FB39 -:1013A000204600F0CBF8236A2BB1002104F1200085 -:1013B0000A46FFF771FEE36A23B12046BDE81040FC -:1013C000FFF731BF10BD00BFF166000810B504463D -:1013D00018B90848B62100F0BDFF00680368DB6952 -:1013E0000BB9FFF7F3FE23681A68D3690133D361A1 -:1013F00010BD00BFF166000808B518B90748BF2145 -:1014000000F0A8FF00680268D36933B1013BD361E3 -:101410001BB9BDE80840FFF72DBF08BDF166000805 -:101420001FB50C46114618B90A4840F28D1103E069 -:1014300023B908484FF4C77100F08CFF82888DE80B -:101440000A000021234602910068FFF731FF04B033 -:1014500010BD00BFF166000813460122FFF7E0BF90 -:101460001FB50C46114618B90A484FF4D77103E06E -:1014700023B9084840F2AF1100F06CFF82888DE874 -:101480000A0000230121029323460068FFF710FFA2 -:1014900004B010BDF166000807B502AB03F8012DDA -:1014A0000122FFF7DDFF03B05DF804FB03680022B3 -:1014B000197583F82020704710B504686388227975 -:1014C000C3F3090322B92278012A18BF43F48063C9 -:1014D000A268E468121BFF2A95BF120443F0FF7351 -:1014E00002F47F0243F4803398BF134309B143F4FD -:1014F000005342681268536010BD43681B681A6845 -:1015000022F0D60210B51A605A6842F480425A603E -:1015100008220368BDE810401A70FFF7C7BF0368D0 -:10152000012110B50446586801F09CFA0021236897 -:10153000BDE81040586801F095BA000043685A7B36 -:1015400053B2002B30B506DA02F00F021B4B1344E6 -:10155000C0221A7606E003F16043C02203F561431E -:1015600083F8002343689A7B53B2002B06DA02F01B -:101570000F02124B1344C0221A7606E003F16043B7 -:10158000C02203F5614383F8002350F8045F0C4C3C -:101590006B7B59B203F01F020123490903FA02F2DF -:1015A00044F82120AA7B51B202F01F02490993405E -:1015B00044F82130BDE83040FFF7B1BFFCEC00E05B -:1015C00000E100E043682C499A688A42F0B507467A -:1015D00087B004D929485121294A00F0AFFE294992 -:1015E0008A4205D91C7B34B1012C07D000F0BCFE27 -:1015F0000124254603E00924102500E0022502A865 -:1016000001F08AF979686019049A8B684343B2FB48 -:10161000F3F603FB162303B9013E0F2E03D9174837 -:10162000702100F097FE03C900F0A6F97B684FF027 -:10163000000100221B68013D66F307110192013C85 -:101640008DF804508DF8071019688DF8054021F0C9 -:10165000010119600C49186801401960019919616C -:101660009A60DA601A6842F001021A60074A596803 -:101670000A405A6007B0F0BD801A06000A670008E9 -:1016800027670008A0860100001F30FF008000F8D7 -:1016900010B5044654F8043F93E8030000F076F9CF -:1016A0002046BDE81040FFF73ABF43681B689869C1 -:1016B000C0F3C030704743681B681A6822F0D60236 -:1016C0001A605A6842F480425A60704703685A7937 -:1016D0000AB9012204E01A790AB9052200E00622BB -:1016E0001A70704743681B681A6842F0D20210B53E -:1016F0001A60026814791CB9196841F00401196074 -:10170000117801290CD15288012CC2F309020CBFB7 -:101710004FF08071002142F490320A435A6010BDAC -:101720000121BDE81040FFF7C7BE38B54368054644 -:101730001C6803681A78013A072A5BD8DFE802F0D0 -:10174000045A5A5A223B5A57A269910701D59A79ED -:10175000A2621A793AB9A26952060FD50522284623 -:1017600001211A7008E0012A08D1A269100605D5E6 -:101770000622284600211A70FFF79EFEA369D906AB -:101780003AD525E0A269520705D5DA681969501CD7 -:10179000D860636A8B54A3691B0603D528460021D1 -:1017A000FFF78AFEA369580626D528460121BDE821 -:1017B0003840FFF7A2BEA269910705D5DA6819691A -:1017C000501CD8608B5CA362A16911F0100109D094 -:1017D000E3692846022143F01003E361BDE8384085 -:1017E000FFF764BEA3691A06DCD52846D8E7A369CB -:1017F000DB0701D400F0B8FD38BD43681B689A6967 -:10180000D20503D5DA6942F48072DA619A6951052A -:1018100003D5DA6942F48062DA619A69920503D5E8 -:10182000DA6942F40072DA610321FFF766BE000054 -:10183000094B70B51C680D4606462046FFF7C6FDED -:101840002A4631462046FFF707FE05462046FFF7A9 -:10185000D3FD284670BD00BF682B0008094B70B54A -:101860001C680D4606462046FFF7B0FD2A46314665 -:101870002046FFF711FE05462046FFF7BDFD28462E -:1018800070BD00BF682B000837B500F073F81B4827 -:1018900001210222FFF700FC194801210222FFF773 -:1018A000FBFB00F068F800200DF10701FFF7C0FF17 -:1018B0009DF80750012D1DD114200DF10701FFF7F0 -:1018C000B7FFA8B10F4C237813B1013323700FE099 -:1018D0009DF80710142021F0060141F002018DF857 -:1018E0000710FFF7BBFF18B10320257000F0B6FD0D -:1018F000012000E0002003B030BD00BF6464000898 -:101900007C640008FF89002001460148FFF7DEBB28 -:101910006464000870470000B0F1804F08B503D23E -:101920000D48302100F016FD0C4B98420ED903F5FE -:10193000803398420CD9B0F1A04F0BD3084A094B21 -:10194000B0F1204F34BF1846104608BD064808BD08 -:10195000064808BD064808BD48670008FFFF00406C -:10196000D5290008BD290008ED290008052A00082E -:10197000A52900087047704710B50C46FFF7CCFF4B -:10198000012103462046BDE81040184710B50C461B -:10199000FFF7C2FF002103462046BDE8104018476C -:1019A00038B5104C05464FF080512046FFF7E4FF54 -:1019B00045B154E8003F43F4807344E80032002A04 -:1019C00008D0F6E754E8003F23F4807344E800327F -:1019D000002AF7D103484FF08051BDE83840FFF7A7 -:1019E000D5BF00BF00700040024B5868C0F34000F4 -:1019F000704700BF00700040024A136843F00803BC -:101A00001360704700700040B0F1006F10B50446DD -:101A10000BD20C48FEF7EEFF2046FFF70AF80A4803 -:101A2000FFF700F84FF0FF3010BD084B002053F8CF -:101A3000042BA24202D81A68944202D301300B2828 -:101A4000F5D110BD6767000870670008886400085A -:101A50002DE9F8430446174699460E46C1B3FFF7F1 -:101A6000D3FF0546601E3044FFF7CEFF002D8046B1 -:101A700031DB00282FDB461B01361FB10020314629 -:101A80004A46B8472C4600F09BFD44451EDCF32037 -:101A900000F0ACFD114B002133F8140000F0DEFD26 -:101AA00009280AD00E48FEF7A5FF2046FEF7C1FF21 -:101AB000FEF730FF00F092FD0DE02FB1C5F10100FF -:101AC00031464A462044B8470134DEE700F086FD3F -:101AD0000120BDE8F8830020BDE8F883B864000861 -:101AE000896700082DE9F0410F461646804600F050 -:101AF00067FD3E443C46F32000F078FDB44216D02A -:101B0000C7EB0805254414F8011B284600F0DCFD4E -:101B10000928F3D00848FEF76DFF2846FEF789FF35 -:101B2000FEF7F8FE00F05AFD0020BDE8F08100F05D -:101B300055FD0120BDE8F081A167000808B57D20B2 -:101B400000F0A8FF003018BF012008BD4900FEF7D3 -:101B5000FFBF00002DE9F043012185B005460020BC -:101B6000FEF7F6FF042000F047FB01A8294600F02D -:101B700097FB01A800F0A3FB08B93B4814E00C3523 -:101B80003A48FEF74FFF02992846FFF71DF9044631 -:101B90003748FEF72FFF2046FEF74BFFFEF7BAFE51 -:101BA000039B9C4204D03348FEF73CFF012057E0E2 -:101BB000029CDFF8E0803048FEF734FF21462F4AD0 -:101BC000002340460024FFF743FF029F2C484FEAC2 -:101BD0005709FEF727FFBC421CD23E1B294861195A -:101BE000B6F5803F28BF4FF480363246FFF7D8F96C -:101BF00008EB040023493246FFF774FF18B9224866 -:101C0000FEF710FF06E009EB54003946FEF7A0FF8F -:101C10003444E0E71D48FEF7EDFE0298FEF709FFA9 -:101C20001B48FEF7E7FE1B49029A002000F0F2FB7A -:101C300004461948FEF7DEFE0398FEF7FAFE174841 -:101C4000FEF7D8FE2046FEF7F4FEFEF763FE039B88 -:101C50009C4204D01248FEF7E5FE022000E000207E -:101C600005B0BDE8F08300BFB2670008D067000888 -:101C7000ED67000803680008266800084D1B00088F -:101C80003D680008008A0020546800085F6800086A -:101C90006D68000800800008766800088968000800 -:101CA0008F68000810B5022000F0BEFAD8B30420F7 -:101CB00000F0BAFA044670B11B48FEF7B3FE0420E8 -:101CC00000F0A6FA022000F0A3FA4FF40040BDE8AD -:101CD000104000F09DBA1548FEF7A4FE082000F061 -:101CE00097FA102000F094FA202000F091FA40209A -:101CF00000F08EFA2046FFF72DFF022807D10820BA -:101D000000F07AFA102000F077FA00F051FB04207E -:101D100000F07EFA022000F07BFA4FF40040BDE8AC -:101D2000104000F069BA10BDDA68000810690008B8 -:101D300010B52548FEF776FE4FF48000FFF70AFF46 -:101D400010B102283AD809E0202000F061FA4020C2 -:101D500000F05EFA802000F04FFA2FE0082000F03B -:101D60004BFA102000F048FA202000F05DFA20B96C -:101D70001648FEF757FE202007E0402000F054FAF6 -:101D800058B91348FEF74EFE402000F035FA4FF4E4 -:101D9000007000F031FA00F00BFB0E480024FEF753 -:101DA00041FE082000F034FA102000F031FA202023 -:101DB00000F02EFA402000F02BFA00E0012404206D -:101DC00000F026FA204610BD2B690008456900087E -:101DD0007E690008B769000808B50248FEF722FED0 -:101DE00000F05CFAF16900081EF0040F0CBFEFF37D -:101DF0000880EFF30980FFF7EFBF704708B50120B7 -:101E0000FEF710FD08B900200FE00020FEF70AFDE4 -:101E10000028F8D00220FEF705FD0028F3D00320AB -:101E2000FEF700FD80F00100C0B200F0010008BD27 -:101E30001FB504460E48FEF7F5FD01A90C22204609 -:101E400000F016FB01A8FEF7EDFD2046FEF79EFE12 -:101E50000220FEF7E7FC04460220FEF7E3FCA04266 -:101E600001D000F0A5FA0A2000F0F8FAF4E700BF6C -:101E7000E76900087FB5BB484FF08051FFF77CFD54 -:101E8000FFF7B2FD044630B1FFF7B6FDB648FEF7E6 -:101E9000C9FD00F094FA4FF08051B248FFF776FD8B -:101EA000FEF76AFCFEF7EAFCB048FEF7BBFDB0485F -:101EB000FEF7B8FDAF48FEF7B5FDAF48FEF7B2FD3F -:101EC000AE48FEF7AFFDAE48FEF7ACFDAD48FEF7FD -:101ED000A9FDFFF7D9FC00F0B1F900F0D1F92146D6 -:101EE0000C2201A800F01DFE00F0C6F901A90C2289 -:101EF00000F0BEFA01A8FEF795FDA348FEF792FD9B -:101F0000A148FEF78FFD4FF4804000F08DF990B1AD -:101F10009E48FEF787FD4FF4804000F079F90820D5 -:101F200000F076F9102000F073F9202000F070F92D -:101F3000402000F06DF9FEF797FFFEF781FCFFF7F8 -:101F4000A3FCFEF7BBFDFEF7F3FDFEF71BFC08B19B -:101F50008F4803E0FEF75EFC10B18E48FFF768FF84 -:101F600000F07AF9802000F05FF918B1802000F0CD -:101F70004FF94BE04FF4003000F056F920B14FF428 -:101F8000003000F045F943E0FFF738FF68B141F257 -:101F900088348148FEF746FDFFF730FF78B1012015 -:101FA00000F05CFA013CF7D132E07C4B0CCB013302 -:101FB00001D0013203D17A48FEF734FD28E0FFF763 -:101FC000BDFD48B3FFF7BAFD10B17648FEF72AFD14 -:101FD0004FF4007000F028F910B17348FEF722FDAD -:101FE0004FF4007000F014F9102000F01DF90028E3 -:101FF00000F0EE806D48FEF715FD082000F008F9AE -:10200000102000F005F9FFF793FE98B96848A5E79E -:102010006848FEF707FDF6E74FF4007000F004F99A -:102020000028CFD1082000F0F3F8102000F0F0F8DD -:10203000FFF738FE4FF4005000F0F6F804464FF476 -:10204000805000F0F1F8400040EA8400C4B24FF440 -:10205000006000F0E9F82043C0B20728C3B20DD1F8 -:102060004FF4006000F0D4F84FF4805000F0D0F846 -:102070004FF4005000F0CCF84F486FE701334FF4B5 -:102080000060DBB2023B052B11D8DFE803F00C1037 -:1020900003100C1000F0BCF84FF4805000F0B8F8BA -:1020A0004FF4005003E000F0B3F84FF4805000F01C -:1020B000A3F8FEF7B3FF394B40485D681E68FEF792 -:1020C00099FC2846FEF7B5FC3D48FEF793FC00223C -:1020D000930001324FF0FF3103F16043082A03F50A -:1020E0006143C3F88010C3F88011F1D1354B4FF430 -:1020F000801200241A634FF480625C639C631A644C -:102100005C6400F0E3FB3048012100F087FC2146CD -:102110002D4800F083FCF320012100F08BFC2146C8 -:10212000F32000F087FC0320012100F08FFC214602 -:10213000032000F08BFC6FF4A050012100F092FC12 -:1021400021466FF4A05000F08DFC2048012100F0E2 -:1021500095FC1E48214600F091FC63B64FF0FF3E0F -:10216000B546284700700040FC690008096A00086D -:10217000106A00089D6A0008346B0008B56B0008FF -:10218000366C0008B96C00080F6A0008366D00084C -:10219000014550FE024550FE636D000800800008B6 -:1021A0009E6D0008B16D0008C96D0008E96D00085A -:1021B000034550FE5D6E0008044550FE7C6E00082D -:1021C000906E0008003802400010E022B379F764F6 -:1021D000082000F029F820B10548FEF723FC102064 -:1021E00003E00448FEF71EFC082000F005F81FE796 -:1021F000116E0008376E000810B50446002000F08C -:1022000075FC40EA04010020BDE8104000F05EBC0F -:1022100010B50446002000F069FC20EA040100200B -:10222000BDE8104000F052BC10B50446002000F09C -:102230005DFC204214BF0120002010BD08B5012024 -:10224000FFF7AEFB0120FFF7EFFF20B90121BDE84A -:10225000084000F03BBC08BD08B50648FEF7CAFBC5 -:10226000002000F043FCFEF7E4FBBDE80840FEF769 -:1022700051BB00BF986E0008022000F037BC000080 -:1022800008B5FFF7F9FF0449884204D00220BDE8F1 -:10229000084000F01BBC08BD8BB8185800BEFDE715 -:1022A0001FB504460C2201A8FEF77AFE01AB03CB52 -:1022B000206018686160A060204604B010BD00680E -:1022C000A0F10C03584258417047000080B51746F2 -:1022D000064610480D461C46FEF78CFB3846FEF7B6 -:1022E00089FB0D48FEF786FB3046FEF783FB0B4863 -:1022F000FEF780FB2846FEF79CFB2CB10848FEF752 -:1023000079FB2046FEF776FB0648FEF78BFBFFF7CE -:10231000C5FF00BFA46E0008B66C0008AD6E0008D3 -:10232000B76C00080F6A00081FB506AA52F8044BE4 -:10233000039200921A462346FFF7C8FF0CB41FB55C -:1023400006AA52F8043B03920092014AFFF7BEFF2F -:10235000AF6E0008002307B572460093014BFFF7EC -:10236000E3FF00BFB66E00087446064808B5FEF7E6 -:1023700041FB2046FEF75DFB0348FEF753FBFFF7EA -:102380008DFF00BFBE6E00080F6A0008BFF34F8FBD -:102390000549064BCA6802F4E0621343CB60BFF301 -:1023A0004F8F00BFFDE700BF00ED00E00400FA051D -:1023B00008B5FEF7F9FB00F081FBFFF7E7FF08B572 -:1023C000FFF7E4FFF0B5064600240120254603464A -:1023D00094421DD011F804C000F1010EBCF1000FB1 -:1023E00003D17355774605460DE00133774606F86D -:1023F00000C0DBB2FF2B07D102F1FF3C644506D0E1 -:102400007355871C7546012301343846E0E7704652 -:102410007355F0BD30B5C9B1C0430A44914213D0E1 -:1024200011F8013B0A4D83EA000404F00F0455F84B -:10243000244084EA101080EA131303F00F0355F8C8 -:10244000233083EA1010E9E7C04330BD084630BDB1 -:10245000D064000800010138FDD1704710B5044672 -:102460002CB14FF47A70013CFFF7F4FFF8E710BD90 -:102470000A2A10B504DC1148BDE81040FEF7D2BAB4 -:1024800030230C461C2204F8023B78234B700F23A8 -:1024900093400340D340092B01D8303302E00F2B87 -:1024A00002D85733DBB200E02023043A04F8013BA2 -:1024B000131DECD100238B7210BD00BFF86E000815 -:1024C00008B582684368934203D304481421FFF798 -:1024D00041FF0268591C4160D05C08BD0E6F0008C6 -:1024E00043688268934210B503D304481921FFF76B -:1024F00031FF02685C1C4460D15410BD0E6F0008AF -:10250000F0B587B00E460746154600210C226846F6 -:102510001C4600F006FB00210C2203A8029600973F -:1025200000F0FFFA684603950594FFF7C9FF0646D9 -:102530006846FFF7C5FFB042014601D003A808E096 -:102540006846FFF7BDFF0446D8B1012804D103A8AF -:102550003146FFF7C5FFEBE7030608D504F07F041B -:102560006846FFF7ADFF40EA042000F18004A4B202 -:102570000025A542DCDA03A80021FFF7B1FF0135F1 -:10258000F7E7049807B0F0BDFEE700000748084AE7 -:102590000849121A08B500F0B9FA0748074A00219D -:1025A000121A00F0BEFAFFF765FC00BF0000002021 -:1025B00014000020206F000814000020008A012071 -:1025C000044B1A69002A04DA034A5A6002F188327D -:1025D0005A607047003C024023016745024A136974 -:1025E00043F0004313617047003C0240014BD86048 -:1025F000704700BF003C02400E4BDA68D20310D493 -:10260000DA68D1060FD4DA68D2050ED4DA6812F08F -:10261000E00F0CD1DB6813F0020F14BF0820092073 -:10262000704701207047062070470220704707203E -:10263000704700BF003C0240092307B58DF8073002 -:10264000FFF7DAFF8DF807009DF80730012BF7D070 -:102650009DF8070003B05DF804FB000070B5064666 -:1026600041B1012908D002290CBF4FF400754FF485 -:10267000407503E00D4601E04FF48075FFF7DCFF85 -:1026800009281ED10F4C236923F44073236121696B -:1026900029432161236923F0F8032361236943F06F -:1026A00002031E432661236943F480332361FFF74D -:1026B000C3FF236923F002032361236923F0F80396 -:1026C000236170BD003C024070B505460E46FFF721 -:1026D000B3FF092813D10A4C236923F44073236103 -:1026E00023692361236943F0010323612E70BFF343 -:1026F0004F8FFFF7A1FF236923F00103236170BD12 -:10270000003C0240F0B500220E680123934003EA2A -:10271000060E9E452AD1550003230468AB40DB43D7 -:102720001C4004600C79076804FA05FC013C4CEA83 -:102730000707012C076011D884684F791C40AF400F -:102740008460846827438760446824EA0E044460F8 -:102750008C7947689440A4B23C434460C4682340E9 -:10276000C360CB79C468AB402343C3600132102AF5 -:10277000CBD1F0BD0369194214BF0120002070477E -:1027800002B909048161704701F00703C9089B0081 -:1027900000EB81000F219A40994010B5046A24EAA9 -:1027A00001010162016A1143016210BD002303604F -:1027B00043608360C36003614361836170470023AA -:1027C00043608360C3600360036143618361C361ED -:1027D000036243628362C362704700000E4903686C -:1027E0000A6810B544691C4383691C4322F07F4387 -:1027F000426823F03003234343EA02630B60C3685B -:1028000082684C681A43054B23401343026943EA2C -:1028100002434B6010BD00BF001000A0FEF7E0FFB8 -:1028200090E80C000E491A4383681A43C3681A43A0 -:1028300003691A4383691A43C3691A43036A1A4333 -:10284000436A1A43836A1A43C36A10B51A434C6930 -:10285000044B23401343426943EA82434B6110BD5A -:10286000001000A000008090044B1A6810B142F0E4 -:10287000010201E022F001021A607047001000A07E -:10288000024B9A68920600D498617047001000A02D -:10289000024B9A68920600D418617047001000A09D -:1028A000014B1878704700BF201000A0034B9B68B5 -:1028B000184214BF01200020704700BF001000A084 -:1028C000014BD860704700BF001000A00F4B0021E3 -:1028D0001A6842F001021A6099601A6822F0A85240 -:1028E00022F410221A600A4A5A600A4AC3F8842065 -:1028F00002F18062C3F888201A6822F480221A60EC -:10290000D960C3F88C10C3F89010704700380240AB -:1029100010300024003000201E4A936803F00C039E -:10292000042B10B503D0082B03D01B4B19E01B4B15 -:1029300017E05168536811F4800F516803F03F03AA -:1029400018BF164AC1F3881108BF134AB2FBF3F34C -:10295000104A4B435268C2F3014201325200B3FBAA -:10296000F2F30C4A036093680D49C3F30313CC5C84 -:102970000368E34043609468C4F382240C5D23FA47 -:1029800004F484609268C2F342328A5CD340C3602C -:1029900010BD00BF003802400024F40040787D01E3 -:1029A00001000020044B1A6B09B1104301E022EA38 -:1029B00000001863704700BF00380240044B5A6B98 -:1029C00009B1104301E022EA00005863704700BFDC -:1029D00000380240044B9A6B09B1104301E022EA2F -:1029E00000009863704700BF00380240044B1A6C27 -:1029F00009B1104301E022EA00001864704700BFEB -:102A000000380240044B5A6C09B1104301E022EA3D -:102A100000005864704700BF00380240044B1A6938 -:102A200009B1104301E022EA00001861704700BFBD -:102A300000380240044B5A6909B1104301E022EA10 -:102A400000005861704700BF00380240044B9A698B -:102A500009B1104301E022EA00009861704700BF0D -:102A600000380240044B1A6A09B1104301E022EA1F -:102A700000001862704700BF00380240044B5A6AD9 -:102A800009B1104301E022EA00005862704700BF1C -:102A9000003802404209084B012A01D11B6804E0BA -:102AA000022A01D11B6F00E05B6F00F01F0023FAC8 -:102AB00000F000F00100704700380240024A536FF6 -:102AC00043F08073536770470038024082B00023A0 -:102AD0000193054B0193019B03EB80000190019B47 -:102AE000196002B0704700BF5028004082B0002338 -:102AF0000193054B0193019B03EB80000190019B27 -:102B0000186802B0704700BF50280040431E0A44B6 -:102B100010B5914204D011F8014B03F8014FF8E7CA -:102B200010BD02440346934202D003F8011BFAE7AA -:102B30007047000014000020742B000800140240AD -:102B4000004000000E00040000140240008000005D -:102B50000F00040000000000000000000000000062 -:102B600000000000106500086C2B0008342B0008E2 -:102B7000500000000060004000000001801A0600C4 -:102B8000005F6000882B0008005401400000200016 -:102B90000000024020000000050008000000024084 -:102BA0008000000007000800000002401000000044 -:102BB00000000000000002400800000001000000CA -:102BC0000004024004000000000402400100000074 -:102BD0000000000000180240080000000000000093 -:102BE0000018024010000000010000000018024020 -:102BF000200000000100000000180240400000001A -:102C000001000000000C0240000100000800070065 -:102C1000000C0240000200000900070001424F4F73 -:102C2000544C4F414445520000000000002A2A0045 -:102C300000000000000000000047FF004C617474B9 -:102C400069636500694345637562653220323031DE -:102C5000362E30322E323738313000506172743AAD -:102C6000206943453430554C314B2D434D3336416B -:102C700000446174653A204A756C20323520323048 -:102C800031362030393A35363A33394702FF7EAA99 -:102C9000997E5100010592002062016F8247027205 -:102CA000007011000101470B1047028047050247E1 -:102CB000058003470801470208471120470C0247D7 -:102CC0000B02471740470501470540470C04009099 -:102CD000470F104706404705014705404705014794 -:102CE000048000044710804705020020000800808F -:102CF000470201470908471701470434000C4704FD -:102D000003470D30475970470E80104704020040BA -:102D1000470408014702070200404704080147052D -:102D20002004474103470B03470D1C471080470908 -:102D3000010080470203470908470324472B7847CF -:102D4000124007470A40470C040008470E40470559 -:102D5000014706044704014706044706104711804F -:102D60000747061C470380470C08470A0C302047DA -:102D70000F30E8470C63C1470C0C30470F4001058A -:102D8000470C03C0470C0C3020470D184702E047A2 -:102D900004020CB10018003847064047080C3047C1 -:102DA0001001074702010001CD72470398470F0C3D -:102DB0003020470D3000F9A04708100001C047053A -:102DC000038047070C30470E30007105470830007C -:102DD00001470F0C3020470F5808470401DCA000C2 -:102DE00080470363C1470203C020AF5247050C3040 -:102DF00047100803470402E0F5470503C0470202F5 -:102E0000E0050FA01047040C30204712400020209E -:102E1000470AC001E007A547060C3047110D4703DC -:102E200014470B4047030547031447020C30204763 -:102E30000F900004470220470E17CFA24702084711 -:102E4000020C30471030092447022C470D02CD0FE9 -:102E5000F047050C3020470F1080044703684703F4 -:102E6000010547100C30471011073047190C30206E -:102E7000470D300010470A0147100C30470E30470D -:102E800002052047023447050247100C302047074F -:102E9000044706404704203404ED80000247091C23 -:102EA000000847050C30470F4047050422FC470245 -:102EB000014707A00002C047060C3020470903EA7B -:102EC000F52000404706035CA500184708401C8F0A -:102ED0000847050C30470A03D05047024047060117 -:102EE000F0554703104706200200F047050C30203C -:102EF0004709284703110001E047032038FC8002FE -:102F0000470A1E8F0847050C304708080040470352 -:102F10003029470402BC03D0470AC04703F04705E5 -:102F20000C3020470A1C0F10470829DCA500304749 -:102F3000090E8F0847050C30470C0F4709415055C3 -:102F4000002047090200F047050C3020470D0447D8 -:102F5000065C76EC80024707084702068047060CAD -:102F60003047080247050447030147033CF0470A7E -:102F700001400C00F047050C3020470820470B1C8F -:102F800021FAE732004047100C3047090C470B2468 -:102F900064F0F7084709028047070C302047141CEB -:102FA00031CCE720804703383D4447020280470781 -:102FB0000C30470F404705027668FB47024000C0CF -:102FC000403D4703028047070C302047080C470567 -:102FD0000147053C63DE800010470C0447040C30B9 -:102FE000470A2047040247052402E8470210470623 -:102FF00001014047020147050C302000A100123CAE -:1030000020000447021EF060470720001EF04047E2 -:10301000030D72BD400003428EEF47060C3047059A -:1030200003C0C04704F04702400097470304200054 -:10303000F0470401403C470201429C47070C302006 -:1030400047033A3C200647030CF06018021080004A -:10305000060140024AA50018800007781500604765 -:10306000020CC96047050C30470503C0104704F047 -:10307000470205090747038001D0554703900040E8 -:103080001620000142C1096047050C302047033279 -:103090003C202C47031CF0403047028000044702CC -:1030A000204705018839414702020025ACC020476E -:1030B000040C30470503C0044704F001300801093F -:1030C000000C4702404705F00F42820100204025D6 -:1030D00047070C30204703323C20470280001EF097 -:1030E00040470280E847037C4707057815470444BA -:1030F00088002047040C3047040803C80001470239 -:1031000010F0470371094703964708C016400003B3 -:1031100042150020470202001C0C30200001017AF9 -:103120003C2047040EF0600008704703A00601DA57 -:10313000A047090A007E8800300002C0000C30471A -:10314000038D0003C04000243000F0470308FB4714 -:103150000240000340F54709014287002047040C64 -:103160000C30202000013A3C2020100447070804BE -:10317000001000045AA102004000A04705603EEF85 -:10318000A547050C300010070D0803C04702024791 -:10319000070520470334E001470B1C05A047020246 -:1031A00047020C3020040500723C20001047060442 -:1031B00050800447022021EEE2020040181047042C -:1031C00003601FAAF5470202C0000C300004054747 -:1031D0000203C0001004470542D8BD2C00103C7605 -:1031E0007042470240008047030102C02C00F047B4 -:1031F000050C3020000800723C2000A02078470316 -:10320000300010470501DCA302304702804703C0AD -:1032100001C047080C3000040B470203C0005214E1 -:103220004704300008033C4702343830A3002040F4 -:103230000147048101C2C0470C0A723C204702A02A -:103240003BF9014001404705202C0CF040470307A3 -:103250008402470306DEF5E0470A100803C10001B7 -:10326000547D69A7004040470402046000F0470213 -:103270008050003C470402BCF1D010470601804753 -:1032800002723C20060024017F744001404703067F -:1032900047034EA50018470215A3C2806002002C08 -:1032A00091A0470B0803C047023C2E78A0100240B3 -:1032B0004707D055470490003C470201401DF3F0FA -:1032C0004707034703121540040036687B6440300B -:1032D00047040CA047020DD1000147020723C24753 -:1032E00002034614ABC420470A08154004023C0000 -:1032F0004A58007047040C470203DFE24705803C50 -:1033000047020266C20BC208470A03BC1047023CD0 -:10331000294B7440002847042000281EF04000027A -:103320001801A3C20004016060AB470C043C4703D2 -:10333000164268A0004047074010F0470205080009 -:10334000803C47021403B4C70010470A03BC100CAA -:103350004702035D7A014706A00E60FEA0470230D7 -:1033600001A7A3C2000803469E81A020470A043C8F -:103370000004470202C85A0847071E03C0F5470564 -:10338000803C470201601D03F0470C295047021E94 -:1033900001D95B10404705800423CCA102470311EB -:1033A00023C247041D95E0470B90168047021C314D -:1033B0004CA547080202C002470480003C47043E77 -:1033C000E1D0470A1B47051F470601C047030620F7 -:1033D000E8A3024703B325ED0004016640ABC42017 -:1033E00047090A47050247060147052670034704B7 -:1033F000AFB694010A0242B507C004471064F85002 -:10340000104708701AA302B10001D04703C000039F -:10341000DDB5E0471164005047083443D053002223 -:103420008051A0470380D6C76EE1D04709081E5BD4 -:10343000C8104703040AF0404709FE00404702193C -:103440008F304002000166A5E0470A0B00121E8083 -:103450006000142800F04702400007470403F0A072 -:103460004080020007B0470301C24E944708018024 -:10347000470506470308F0401847021000062000E1 -:10348000020C0A001947020B3A81816002E68405AA -:10349000A020470801470810F04704B7470240043E -:1034A0003CC0050002801087400101000260140248 -:1034B000400447060300080036C9580C0034001ABF -:1034C000F04030470218000CA14000FE504A02476D -:1034D000020120288047030480D0A0470601000392 -:1034E0000A7A05424702144702F000304702FB0007 -:1034F00004408003F08040470440288000034001DE -:1035000000D080470603000500DE11D84702366C64 -:103510001AF060470308000D8000020C0A403008D2 -:10352000000B3801010002000D85E0204706010074 -:103530000F0052E0404702142800F04703B0070094 -:1035400009024702C0054820040007414104470220 -:10355000401602E0470A08F680D2100E47021AF021 -:10356000404703184703200400A540008050117510 -:10357000D04703C067A0140447080B091B28C020CC -:10358000470310F04704C900400E3C38C05A4801B8 -:1035900000D887B5F8470303AEE800804708080164 -:1035A0009F141447041AF040470280180400201E9C -:1035B0002200A54130005C037B08084702E021CAD5 -:1035C000470A050C56788240470310F04703F0E5A0 -:1035D0003C470340C05A40104702A7C30C040002F6 -:1035E00062DC05470A0D81D6C9504705A540470250 -:1035F00098984702A004301C0540800018E0470559 -:10360000600685014709050A32054A4704405A47C2 -:103610000399933400400201C00A40470210B047AA -:10362000030847020105470A0581FE51D047040AF5 -:10363000F0403000901000040004027E5F40100053 -:1036400050E1B8280400E0400FF812470A0B53E09D -:103650004A4705F000300070FD3C04000220E00FF6 -:1036600048100079098168040002600CC4471114F5 -:10367000004EA0400240470607C850C0470390478D -:1036800005407EC70008470F020010A04080404759 -:10369000063BC00542470210D04705235DAF0147F6 -:1036A0000E06470744470306470203C850C01800E8 -:1036B00018033E01006000063DC047110447054263 -:1036C00047050402C005404702188FBC012047038C -:1036D0000E824716100001C000044702674850C020 -:1036E000100001E130104703604718300001470225 -:1036F00004470223E005401047020DE047160C473F -:10370000070547030C0006674850C0F0001017B2C9 -:10371000012847020780471008470B08470222E0AC -:103720000540204702E7C00147234003E850C14756 -:1037300003F0470402662F90471B10470203C005A1 -:10374000404703D0470401442FB040471D736850E1 -:10375000C010470219F56C47020366EED0471E42BF -:10376000C00540304702ADE11C47020142AEF08087 -:10377000470240470E0447090226036850C000076D -:10378000101DFB08084702068DE800204710384747 -:103790000A0403E0054040003085830C47050847D4 -:1037A0000F04470B040020736850C1104702804784 -:1037B00003C00060208E5420470D04470B04000412 -:1037C00042E00540100051E04703804142650DA0F2 -:1037D000470372009011010101470B104706404753 -:1037E00005014705400002470608471120470680AB -:1037F0004705024712084722010002470404471107 -:10380000104706404705014705404703160001479A -:103810000604472202002000084702084703104719 -:10382000243C000C470630476880104704020040E3 -:1038300047040801470302004047040801470430D9 -:103840002004474D01470604472103008047050235 -:1038500047030C471F1E472D0247034047050147FA -:10386000174047050147060447030301470604477D -:103870000610471D8047050247110C30472C0C30BD -:1038800020472B0C30472C0C3020472B0C30470E98 -:1038900001470C1047100C3020470D02471D0C301B -:1038A000472C0C3020472B0C30472C0C3020472B5A -:1038B0000C30472C0C3020472B0C30472C0C302080 -:1038C000472B0C30472C0C3020472B0C304713086B -:1038D0000004002055470880004247080C3020476C -:1038E000120C00200168554047074002004047077E -:1038F0000C3047150400200F470B02055004470405 -:103900000C30204714260168F0C04709661E855414 -:1039100047050C3047180F470DF08047040C302046 -:1039200047150168F0C0470A168F0C47050C304751 -:10393000150C47020F470DF08047040C3020471547 -:1039400001E8F0C0470A1E8F0C47050C3047150CE4 -:1039500020200F470B0200F047050C3020471426AB -:103960006548F0C04709601C8F0C47050C304716AE -:1039700020200F470A460200F047050C302047156B -:103980006548F0C047080207548F0C000147030C3C -:103990003047180F470DF047050C302047120647F7 -:1039A0000201C8F0C047076047021C8F0C47050C96 -:1039B0003047170C0F470503C01247020401B0F04F -:1039C00047050C3020471501DAF0C0470423C047F3 -:1039D0000307148F0C47050C304718F04703D9B084 -:1039E000A4470280470A0C30204714060008F0471D -:1039F0000350A780020040470A0C30471620300FC2 -:103A00004703D900A047040247080C30204715059A -:103A1000780F80470250A780024703404047070CB9 -:103A200030471524201B0F470528470D0C30204731 -:103A30001501F80F804703078002470C0C30471729 -:103A40000D0F4704B020470D0C3020471406617954 -:103A50000F804703078402470C0C30471504200DE4 -:103A60000F47035000A8470D0C302047142004F8DE -:103A70000F804703070002470C0C30471506203C17 -:103A80000F47035000B4470440050A088047040C60 -:103A9000302047143501C90F804703010002470257 -:103AA000026000E50447050C304715366C1C0F47D3 -:103AB000035000284707588047040C30204714144F -:103AC00028E80F8047030704020060470214E0540F -:103AD00047050C3047166D4CA5470350002047049E -:103AE000C0040A088047040C3020471529415A8039 -:103AF00047038700024702024702A504471B100440 -:103B00004000F0470301A9A00004800042471F20A5 -:103B10007808F047020800BFA0214040020040475B -:103B20001D10042060A54702404702A04702010083 -:103B300040020550471C2007605A80470307800257 -:103B4000470202601E8556471D0340A5470351905A -:103B5000B04702014704F0471D01605A804702D96F -:103B6000A3840247020800168F0E470E01470D1C62 -:103B7000004FA54704A0703C4706F0470E01470DD3 -:103B80002601E15A80470219B5F43E47041E8F0E04 -:103B9000471C1C20390F47031900B4470402820058 -:103BA000F1471C2605580F804702F9E380024703BE -:103BB00060148F0C471C1C3C2F0F47031000344728 -:103BC0000320020200F1471C2061DB0F80470280C6 -:103BD000030002470306548F0C471C3C381C0F4758 -:103BE00002180090F03C4703028000F1471D04C812 -:103BF0000F8018003DA5E03E006047021C8F0C4777 -:103C00001C3C380C0F47031000283C47040190F17E -:103C1000471D25CA0F8047029005643E47041DBF1B -:103C20000C470E04470222CF4A60470504000420D7 -:103C300047031000188F62E1004100421C05A047B5 -:103C40000E04000467FB40404705040036044703A8 -:103C50001000981D6CF1024002002C470210014731 -:103C60000604470208284702100025F03C60004285 -:103C70004704100232DAC34000400185414100058B -:103C8000000381005004470A16A804470206067A7A -:103C90007C424047060402DFD348000459F0728199 -:103CA00047041EE0504710023FE8F24707801C7AA5 -:103CB000E0A5470220080F3001400001C25C05A0CA -:103CC0000401470D100E62DFFA4707102026FC5052 -:103CD0000100C018E13A01000147026E4713214F6D -:103CE000F54707014221D90740007D000F28110048 -:103CF0000101C026020414471006EBF64707011C19 -:103D000005FD036100021987F69D000247021F802E -:103D10000420470809470408000628E01820404707 -:103D20000408470202DFE3402000D80B65610094DD -:103D30000142B501470A0847040C002E07FA0402A5 -:103D400047050C00042BCCC36070009187BA890032 -:103D5000CA020021C20020470F3C0010A047020801 -:103D6000470601F0D3410147029FF95100040002C8 -:103D7000B680404711014EA0470902E8C342020045 -:103D800070153EC1470306D5A0A047100642D0B922 -:103D9000404707042830A0470318057321470360F4 -:103DA00037A1470F06000E3BDCB16247050647030B -:103DB000DAA00018007C0FBB09006001E035D04795 -:103DC000020A470810470602CFE0470D0400190F0A -:103DD00021194702014247040447080E47040E47D1 -:103DE000021BD0470930470304059981720D470231 -:103DF00002470B03000980920447047F60C2470614 -:103E000004014203400500100801804703400340BD -:103E1000470A03000F80174705386CC302470504A3 -:103E2000009E000E0A20100058F04703404710186B -:103E300014470403F00347081E3C30A008470290D3 -:103E4000E77F21470306EED4A447090100DE80473F -:103E5000030402DA430247073E056EA04703B81980 -:103E6000FF4147024002B6891447090908941047E8 -:103E7000034040F0024707802C3F5ECB470308A970 -:103E8000836101470202020F470A0801F7470280D7 -:103E9000470260C80A0247060E20060CC7104702F8 -:103EA000781BEB0108084702468F0847094B0016AC -:103EB0004047020146387B0201000247053C3C10A6 -:103EC0000A47033009807447020363C00F470A2082 -:103ED0000012204703BC7AF947030D47052005DA95 -:103EE0000A200247020772B4080009405E8F0810DA -:103EF00047080B0D941A4247033C00A008470508E9 -:103F0000001C294B08002020D005414702800002F8 -:103F1000C20F470A080B06A590470369CAA02047AD -:103F2000050C0020020C042032801809200008C073 -:103F300047025C8F084709200E1000084703243011 -:103F40000401470504470220CCA141470218B0C034 -:103F5000284704020F470C17900080001429EA0438 -:103F60000247050447020378D1408000D005782835 -:103F700047041C8F0847090700D04703401472D933 -:103F800047030147050401CA5B4703110567B840B1 -:103F9000470242800F47080180000A760080808037 -:103FA00000767B802000024703060E14000DA70850 -:103FB000180159C573BC006047024C8F08470905BA -:103FC0000EDF00804703237B470A26CEFF0940000F -:103FD000D9E3384147072047080518DA47053CE987 -:103FE00080200038470603DBF7470338A138014734 -:103FF000040C000800404702350050134703040337 -:10400000C04702266D7040084047040400047C70DD -:1040100005470308ABA39C20470203010F0400409F -:10402000014020E0A00147030683D047022428DA9C -:104030005012470504002423EC4702800071B17E32 -:10404000BD40404702418F044703467E0D3447027E -:10405000014706046410A04707020036DAF048471B -:1040600002500743000147040F04470201639CEC20 -:10407000342000098047052065DEA0470901FFF4D0 -:10408000404000D9A720000A47020700CF0447029A -:1040900002403401A04708022666500147094370D8 -:1040A000A04703500FA40020470204010F04470457 -:1040B0002FE00220470826760E022247070621EC51 -:1040C0004704980FF420470303808F0647020140FE -:1040D000010A4706154001800C62CC4705C0470421 -:1040E0003D40E740C10050A7030020470202410FB6 -:1040F000044702020284AA47055A155001002026EF -:10410000FE0847041000804702077CD34A02008162 -:10411000D772000C0801E001CF040080004247057F -:1041200001800803C047020C41DD20470608002E2D -:1041300039D00A47030800C02820470220010F0495 -:1041400000C002004047040D98523C300240002A53 -:10415000CF47070C0006036C4704300730280100E6 -:1041600001C000EF044703029E4705080803C0474B -:10417000021401E024001047061C20700041470390 -:10418000A0C14702404702010F04470437AA0104B7 -:10419000470219523C30470302DA10023002470648 -:1041A00004EE8063470315310028C0470201EF0485 -:1041B00047034035040010470403C0470303F0479A -:1041C0000340470506034A1047030803240020471D -:1041D0000202400F040060470388012180470272F9 -:1041E0003C30470368EE40220040470306000602C9 -:1041F0001E2000180031EF32002160470201AF0499 -:1042000010470223CDE0470603C14704EF470340B0 -:1042100047051401E04705A5FBE0470306010F042D -:1042200047036004C0804704723C304703027D802E -:104230004702404706036A0A4703019B2FF483475E -:104240000203808F048047060100010E0803C004AA -:104250004702404BF1493000080F2804471014056D -:1042600000044706030008187A3C300447023BCD9F -:10427000F0407000104702044706104702804705CF -:1042800003A18A4704028047060803C447050A4081 -:10429000800070FD24470280470D028047050280A0 -:1042A000470401805A3C3040000600FA0A404703A8 -:1042B00080470201470D0142C04704014047040303 -:1042C0008F470203C010470238ED0BC10002008D7A -:1042D00028470A010141840001C027824704068063 -:1042E00047065A3C30400006027A0549000547025D -:1042F00004000E47092A81A047031F9380470950F5 -:10430000470203C010002401505F404702700300C1 -:10431000404702304703044706400002800F000870 -:10432000470820007A3C30000E00637AA54047021F -:10433000804705044705184704C00060008F00044B -:104340004709180803C05800042C70E34047029046 -:104350009D0008470E01C00300F04709018B523C45 -:10436000300C002C03FCC34200380008000C470E40 -:10437000020014E0F847043FC04704080803C05097 -:10438000470201700500300031F1000C0004404785 -:104390000B40022ECA5047041DD080470319523CDF -:1043A0003020470202DA47023000809000040020EB -:1043B000704705784706065415A8470803470303C6 -:1043C000C04702042C6A81400002000747030447EB -:1043D000052800F3C2BF800003D280C0F047070168 -:1043E000804702723C3006010C026F5040180147B2 -:1043F0000306470618180007E2954703400EE0F849 -:10440000470950000AC0470401DA3C47024010CB7C -:10441000470402C0054442800847060402470222BE -:10442000470820007840204703016D3447041847AF -:1044300004014850400100704706071C000847036C -:104440007200701102010147080247060847040282 -:10445000470A04470220470680470C0147060447E5 -:104460000401470802C047031047170247060847E0 -:104470000B08471602470608470402470608470686 -:1044800020470680471F0300070004470B7C471D99 -:1044900003470F6C470CA84705024727204708042D -:1044A00047220240C0470408014703020040470476 -:1044B00008014705200447058010474C03470D704D -:1044C000471F014703044709D0470340473A0247C3 -:1044D0001208470D80470A80000247030247058003 -:1044E000470502470608470620472204470D4010AB -:1044F00030C04718067B38470240626D6140470371 -:1045000083DC05470430C0471803DBD0470301AB09 -:104510002022C0470203C0008047021030C0471667 -:104520000103F8EC00B0018047020162000600833D -:10453000D6000A00180030C0471703F83001F047D2 -:104540000903C20D1047021030C047160108805AF7 -:1045500000B01400AA49A16040030083C447030CC3 -:104560000030C0471702002D01800800064B6C0286 -:1045700080030003C247041030C0470A80470D146F -:10458000EC50104702A2EDF540470383CC054702EB -:10459000800030C047193AD8F04703A1E420470310 -:1045A00003C2008B01001030C04716010A805F80F3 -:1045B0004708030083C60847032030C0471721007F -:1045C00028104708020003C00547031030C04710F9 -:1045D00002470980470502470483C647025C470239 -:1045E00030C0471A10470503C0470303C247028083 -:1045F000001030C04717080969470740470383D6B2 -:104600000581470330C04718215D47072047030352 -:10461000C0000A47021030C0471708805F181047D3 -:1046200007100083DC4703800030C0470901470EB4 -:10463000227A00804707600003C247020100103061 -:10464000C0471704000A1847024010F129604706C6 -:104650000A470330C0471801504703040F000140C8 -:1046600047091030C04717043C585010018010F122 -:104670006A06804704014702180030C047183C1002 -:10468000508047020F00414047091030C0471704CF -:104690003C5878A0470210F121404703A906F38057 -:1046A00000480030C047183C105847030F00400234 -:1046B000800020193FE547028C1030C04717043CAA -:1046C0005C470410F121604047028BACE00A4703CD -:1046D00030C047183C1000C047020F004002800065 -:1046E0002002A7B50A47021030C04717043C5C47B8 -:1046F0000410F160470490B788470430C047183C65 -:104700001047040F47050222EFF54702201030C082 -:10471000471708024B9980470210F16047048BAE9F -:10472000F81B470330C0470901470D0210DA104754 -:10473000030F004047030202A5A50A47021030C03C -:10474000471708025A470410F168470320A907D108 -:104750009A020C0030C047170122EB470220000FDD -:1047600047048000193EA58002081030C047170892 -:10477000826B470410F1284704A93CE00A47033044 -:10478000C04718115D47040F0040470280022A26E7 -:10479000800002001030C04717183DC0470416992A -:1047A00047058001B50A08470230C04712028C0055 -:1047B0000800083C20470303096B47053FC1ED9003 -:1047C00047021030C04709056047081C4702604790 -:1047D00005018010F1624702060083DE47031800DE -:1047E00030C0470940470802803047068000010F6B -:1047F0000041C0470303C047041030C047130C7C7E -:1048000000304703100080800AA13847020300A946 -:10481000408047020C0030C0471202C27800304787 -:1048200003084702C00AA040470429404703081074 -:1048300030DE4710084702404709C060F180470456 -:1048400083C4470530C2470F01140003470A4000E4 -:10485000F0800008470203C20D0B47021030CE471C -:10486000090847095C470A2A500447030183C447E3 -:104870000240470230C247090847062002024702A9 -:10488000804706200045A0344702800003C2094744 -:10489000031030C00700A0470F804E784707200064 -:1048A0002A50470902470230C04709084708B24268 -:1048B00078470905A03C402847081030C0471280BF -:1048C0000E470C024702808083D6470530C0470957 -:1048D0000847062000F24000084704D000204703A4 -:1048E000024000800003C0470202001030C00747AA -:1048F00003020F780447022047085405003047069A -:1049000080470A1C000C0030C04704020F08024711 -:10491000082000B3823A802047068047058047047C -:1049200010A20447071041F700280010470380A099 -:1049300047050800080379000447095981A50A4082 -:10494000470ADFB44047057147060800608369D015 -:10495000B04709198D0D904709022483802800442F -:10496000090AF900A0018047030800083DC080E063 -:10497000018020F5470306005A64A0470A02E7B306 -:10498000C0400010222FDA11B047050847023C2032 -:1049900071B04702A0F080038047021A74470B02EF -:1049A000AA77144703014148504702804704304723 -:1049B00004A010002A57A86047035A44850A4709F3 -:1049C00002AA0C022C4702014000D8B00080470424 -:1049D000204703F147030F50F5C047031A640090C6 -:1049E000470A1892D428000101406A50104707086E -:1049F0003DC030470320F5806047035A4488470B89 -:104A0000341EB46C470223401BD8F0470608203CF4 -:104A10000030470320F08047041A65010B47094026 -:104A2000053EC04704046B80470208002047081475 -:104A3000470302000347020CC5A8000D40470708C2 -:104A40000807C04704035810470202003C47080803 -:104A50004704404703332CBB0E00804708011286F1 -:104A60002800014294DB4703C0470F0847078047EF -:104A70000A1E8040470202003B0090008047102839 -:104A8000470660470850A55EE060000208BBDA8078 -:104A9000E0470701E94E518047061047023C1C07DA -:104AA000470B85068447042A6EF04706744702AA1E -:104AB00078F847072847023C0207804709025A0056 -:104AC0005470007047038047070108F46A470420C8 -:104AD000F18247020240029DE7470280470702A594 -:104AE00003424000304703F000024707980B470499 -:104AF00020F001C047028002830080014708030FB5 -:104B000052A047030C3E6A47090294DE18470A5434 -:104B100014850A470AF0983C470303C3604709011C -:104B2000541FD0470990144702904709030F138080 -:104B30004702600C3C4E30000180470614C8470213 -:104B400001804703E00846005A45E047021847083D -:104B5000F0470503C0470A02BC1CD04706220020CC -:104B6000001A7400084709030F13A60447020C3CFF -:104B70004A9810470505000294DD51E000100AF143 -:104B800084470443C48047020C4708F0043C204794 -:104B90000203C010108047050200015409094703B1 -:104BA00005F0A047043C010D0B000C4707030F1252 -:104BB000806030000C3C4AB180C8470715CA5004D9 -:104BC000470220F580038003005A65E047020C4746 -:104BD00008F004470403C010100014470602BC1C70 -:104BE000084704F0470302001A54000B000447076B -:104BF000030F178047030C3C5E4702804706029470 -:104C0000CDF9F0470903C4470CF0470503C0470A34 -:104C100001541F70D0470603470203C047082047CE -:104C200002030F168047030C3C5A0001C8470714C3 -:104C3000FAB8E047080183DE47021C4709F0470540 -:104C400003C0470314470602BC3CB890470983C021 -:104C5000470B02AA17A06047020C3C5A0180470686 -:104C60000101E8FF71C0470507804704010010807B -:104C70004708AA0404470303C010470A542C0847F6 -:104C80000604470680470C200400200AA85E18147A -:104C900000C047040108F54C704704A760000447B2 -:104CA00005080008470A0420001002A810008A0026 -:104CB00040470404006419F8F0000100A0470A0806 -:104CC000470E083D60000447052000083DC047042A -:104CD0008005E000380004C3FCA04711483C00F008 -:104CE0004708083C47062BA342D000043C3E470B34 -:104CF000020F78470360083D6010000180470A05F5 -:104D0000804019B006000600C3C4E04702184707F8 -:104D1000020F084704083C00F8470B7000084702E0 -:104D200001EB404702243C471230083CE070470248 -:104D300080470440083DE0304702802845E04704B2 -:104D4000C3C4A981470F22083C20080014C04705AE -:104D5000083C204703C0242BA002C047023C0101AD -:104D6000084709020F304704083CE001F0470708F4 -:104D70003C604703204009B047023000C3C4A998F3 -:104D80004709020F4705083C2001E04707083C207F -:104D9000470521EB4047033C0101470D2004470232 -:104DA000183C60008008470405470905E2E02847F1 -:104DB00002C3C5E0470D3C384702083C20014705C7 -:104DC000024705B04703808BA06047033C471308A8 -:104DD0003C40F84708083CC047042A01A440470269 -:104DE00001C3C5A04711083C20F04708083C20B08B -:104DF00047032050A04703043C471202083CC04729 -:104E0000070100083DE0470501B1404703C3C5A0C5 -:104E1000184710083C470A083C4702C047033B7C40 -:104E20002047033C0103470D0E010020083C60476A -:104E300003804705083CE047038400012447020241 -:104E400000AA85A101000C470B028000083C20004D -:104E5000044707083C2010E147020200B447042A37 -:104E60008100080004470272009011030101470805 -:104E70000247060847040247080780044702204704 -:104E80000680470C01470604470401470D10472AD6 -:104E90000847160247060847040247060847062047 -:104EA000470680471F024703044729034729A047B5 -:104EB0002D10471E0200404704080147030200402E -:104EC0004704080107804703200447058010474531 -:104ED00001470603470D204718024706034703040E -:104EE0004709C0470340472B38472108470B40007C -:104EF00080470A8047050247058047050247051895 -:104F0000084706202C471101470F04470D40003089 -:104F1000C0472B1030C0472C30C0472B1030C04743 -:104F20002C30C0471004471801001030C0470F80D4 -:104F3000471C30C0472B1030C0472C30C0472B10C7 -:104F400030C0470E10471D30C0470A0200140B47FF -:104F50001D1030C0470C28E1471E30C0471B0447D6 -:104F60000F1030C0471B0A471030C0470B20280BDA -:104F7000471D1030C0470C14E1471E30C0472B10AE -:104F800030C0472C30C0472533069047031030C04F -:104F900047242031A480470430C0472B1030C0473D -:104FA0002C30C047253C0247041030C04723048002 -:104FB0003C14470530C04708804721801030C04767 -:104FC0002901400030C047270147031030C0472C5B -:104FD00030C0470A80471D0A47021030C0472801E9 -:104FE000470330C0471D0300F04707054703103053 -:104FF000C0471FF147070D9A470330C0471702A863 -:1050000009704705404706010800041030C04711E9 -:1050100080470501544147041000796047050801A5 -:1050200000080030C047150447023C47060B4047C4 -:1050300003802A81000847021030C047150200078C -:10504000C05847041008684704AA85E1994703300F -:10505000C047183C1047050301428047023C470502 -:105060001030C0471707C04A470410086A06C047F7 -:1050700002C3C5A0470430C0470F044705300002F3 -:105080005A40470503014047033C47051030C047DD -:105090000F0A4707065848470410086B6680470206 -:1050A000C3C5E00080470230C0470FF047083C10FE -:1050B0000847040B41428047023C0147041030C0BE -:1050C000470F10470707C05A38470310002A06C089 -:1050D0004702C3C4E018470330C04711C047050268 -:1050E0005A500147040B41428047023C01014703EB -:1050F0001030C0470E50470280470506585E71C009 -:10510000470210002B66C04702C3C4AF00A047028D -:1051100030C04717025A504705034704013C470572 -:105120001030C047100180470506584870E047021C -:10513000100028470301C3C4E04702180030C047ED -:105140000B083C4709010001409847040100034057 -:1051500047023C3647041030C0470B083D60470902 -:1051600004470238E0470210002800404702C3C449 -:10517000E7470E3C47021010D9470D04470506C00B -:105180004702080247112000034B470D0247050262 -:10519000800010081CE0470D0200084704020020B0 -:1051A0004703028047041047041BB842C04702105F -:1051B00080900902470B1C470C01470A073E470332 -:1051C0001014979598471301E04707016BCA99900F -:1051D00047093016B500024713F047070160699987 -:1051E00090470506804702103488470F084704019E -:1051F00000204704040002A8100847030A00836443 -:10520000471580470401E04705020202A85C1847E1 -:10521000030A037942804718D9F0470702003C107F -:1052200000E04704704704069E901B47110B4B801B -:10523000E0470707C05C001000040807B0470301FF -:10524000A94F800A470E053203D42008B002470553 -:1052500008003C47020A470240A08047042801DDBD -:1052600080470F3003D56A7880024704300007C0BA -:105270005C00044703A37847051C95471E3C470282 -:10528000A047090E01AD1A470E0160470378470891 -:1052900017C05C00104708100617900A001847074F -:1052A000020F4705A0014C4706029001470210007B -:1052B000E04702080B38470534A01B4709920F78D6 -:1052C0004705824D470601470204000C00104703C2 -:1052D00003BC000447020216950A471103D0470297 -:1052E0002047040604003C2047032020F0804704A8 -:1052F00002378009471004001A7881470501041815 -:105300003C6047032020F78166800201802CE04743 -:1053100003023FC04709024703D070002047040240 -:105320004706202020F00142808000138299090264 -:1053300000443FC04709010004001A8180470B0167 -:10534000002020F30366C00012A346054713404720 -:105350000A3C470280003020F047051E81002947A3 -:105360001004021A01E0470604183C60470420F7C5 -:1053700080E04702101E8CC0470E01470301C009A0 -:10538000E02847097008002060F080440080000891 -:105390004E810A0080470B02470204021A79F0048A -:1053A000470B200020F3800700800004B408471258 -:1053B00002D050A04707603C2047032060F08047A0 -:1053C00002500010038D471104000A50A008470640 -:1053D000183C4047032020F7470410001D80470E6B -:1053E00001470203C25010A047072294470480607F -:1053F000F080470202003040C79F470D028000073F -:10540000C0CAD04707081A9408470201E020F747AE -:1054100003031030941F0E471003C0604702044777 -:10542000050C02940050A0006060F047052001EDDB -:1054300080471007C1EE01E0804705080A9408473D -:1054400002010020F7470306100014A54702184781 -:105450000B01470203C040508447060C01680C5002 -:10546000A0003860F0470502810D1F40470C02473D -:105470000207C0EA00B84705020A0A940100A047E3 -:105480000220F74702084000029EE00A471042A8A7 -:10549000103080004047112047040B47100AA85ADB -:1054A000001000C0471150471543C0100080470846 -:1054B0003C470401010F34028047020142000E47BD -:1054C000100C3C5A01904707083CE0470507314063 -:1054D0004702208155E0471123C0003047093C00B6 -:1054E000080040008000A0000347160C3C5A000151 -:1054F0004707083C6071804703512047070E47105B -:1055000023C0470201C047073C20008080000FF005 -:10551000A14047170C3C5E4709083C4001904702F8 -:105520008FA5200280470401471123C010104709AE -:105530003C204703010103B8001000402042E090E6 -:1055400047100C3C4A99804707083C404704020733 -:1055500047022000100056879E471023C0101047B6 -:10556000093C00084702C04702014047031C16B12E -:1055700047110C3C4A984708083CC05847028047EE -:1055800007102DD0471143C0470B3C470280004015 -:1055900047040347160C3C4E470201804705083C70 -:1055A000C0001001470520470718470E43C2E047D7 -:1055B0000A3C2010470501428047050D47100C3F6B -:1055C000EE4709083CE0984706028047050110476E -:1055D0000C40471A6300294000100020470B704719 -:1055E000088047111200A9408018470D4002470467 -:1055F000084727200147043847120647061800408D -:10560000472503004003C20010472701470283D605 -:105610000018470E30470401470C04470540471B5C -:1056200080470B02470402470240470F0F0680479E -:1056300003083C000147050285470B0447020402AA -:1056400081C047090102007A804703083D4000807D -:1056500047040102470B074703A804B18A470D28F6 -:105660000002083C20014702C0470408403C0008F3 -:105670008000802A5034024702840281E108470CEE -:10568000064703083C6001E000C047034000183CA7 -:10569000605010148025A04702404702E804B981F9 -:1056A000470F50083C470B3C20014702402AA040CE -:1056B000470201000280C0471050083D607A000197 -:1056C000804705183D4000A001002AA7304702048A -:1056D00005E804F00A2018470C7000483C47070210 -:1056E000700023C000700447022E7B384703060277 -:1056F00094E00E4710083CC047090BC140000A4720 -:105700000222402800054702E826B00147031006A0 -:10571000B04704807E80470301541847080800B052 -:105720001908D04705034047022A81470580BEF18A -:10573000984702020276803847022295EA01E1008A -:10574000804704010A75E971F0470401E040020155 -:105750002A96E7804705D010470280AF06543000F4 -:1057600002017C0B470601470202F43C47048050CB -:10577000BC6047020430E6D047048286B01C470371 -:10578000053376684703147D470780010141791886 -:1057900008C00040A187E0470391E7B1804703209C -:1057A00004A1470484128228470209680908804737 -:1057B0000702BC2A7180470401E047033E820919B1 -:1057C000470380A5E847030200F20E6C47020A94E3 -:1057D0000139E1C0470714FA01C04705E047033E1D -:1057E00094C009470303C14705AF0E426002020996 -:1057F00068087047080154087047034000FC4705DB -:1058000015910F470343C4A0470304053A462805F2 -:10581000000A940080470802955E80080004001189 -:1058200064470554A8470403C10100180008550A3D -:10583000470420141C470601470202BC3A70804707 -:105840000205008002470303C0001E470343C5ADA5 -:105850009A4703A51F86470302BD6950A0470580EC -:10586000470215EC80B04702050121404047028302 -:10587000D6000A470303C0050A4703403604204701 -:1058800002083C20084702804702024703D82F083D -:10589000F04702A8503C002047023C00D59B47033C -:1058A00043C4880147020220D2A0644702083CE0BA -:1058B000504702C047040148D47A70100004247590 -:1058C000F847043C15C38A470303C047031000048C -:1058D00003CC004040083C4708040001C019D847E9 -:1058E000030500BC0020008028B5918A470343C50A -:1058F000A01E001042205220600050283CE0798019 -:10590000018047040102E8EBB80001800007A160B4 -:105910000006001407E947021847023D000D000287 -:1059200000050AC03C47047C10F0470702A809D0D4 -:10593000A540002CBD746247022030CE8D47044044 -:1059400021A01B4703A557542802002040F980B02E -:10595000470401470202955D800847022C3DF7602D -:1059600040200020F4950A47047200801100620074 -:105970002F824702010347041000080010100202A2 -:1059800004002200202000080424044707080447DC -:10599000032004002020202400082C040C0C4703C2 -:1059A0000410100C001000084703080847030830D3 -:1059B00000303004002226060408000404000447D6 -:1059C000042008000C000828000C0008002808002B -:1059D0001008082808470228282847020C10101031 -:1059E00024000224242C2C0400282C21010800115E -:1059F0003010200004220222470320202030040817 -:105A000030100010080004040404000400044706D9 -:105A10000800040808084702084702242404000478 -:105A20002C2C2828470221012110040830102010B6 -:105A300047022000202008042020200004470404FE -:105A400008000404040400101010101000060810D0 -:105A50000808030247020404080204242424040062 -:105A60002C2C2820020021012100020A20002800FD -:105A7000080228282030040030301020000247029D -:105A800004000A0204040014000610101008044761 -:105A90000208080008020C040C2C02022C2C2C2CEE -:105AA0000006282A2A470208220022000C022002AF -:105AB00020204702202030300202507040000A0EA1 -:105AC0000C080C044703040010080810100212040C -:105AD0000002202020080824242400020247040A8F -:105AE0000E47030204470206240608082C2428282F -:105AF0000202201030100E0A201028084702280049 -:105B000020200A08203030180400021A0A040A0A69 -:105B10000C040C0C470208082808020C0808280886 -:105B20000C02280A28020008080220080800202881 -:105B30002010000C1010041008020404280C0408A3 -:105B400028202121080031303020060E2222222078 -:105B50000E0620470220040C101010000A47020411 -:105B60000404000C040447020A0247020800060865 -:105B7000080A02080800060406040206082A284744 -:105B80000321213130060A3030202002022020205B -:105B9000000804002010000600100010000C040093 -:105BA0000404040616141010100206181008080148 -:105BB00047020804040A020404040604082A0C2A02 -:105BC000220A0223232322000822222A200A022852 -:105BD0002A30100400101000080202000808000813 -:105BE000000C040C0C020618081818040008180809 -:105BF000080A020C0C0C0600080E04220E0A0622EB -:105C00002A22220008222222220C022222222202FE -:105C10000032001010000240504000020C08080C36 -:105C200004080A0404470308101012120400024773 -:105C300002200E04240424200C0E02000202020C96 -:105C40000202020204020602060602080A262A08C6 -:105C5000080B303030300608202028284702820008 -:105C60008001030002282828000208082818180EBE -:105C7000091A1A0A080208080C0C0C00020C00226F -:105C800008000E020A22020603020222020808028B -:105C90000222020808222030100204101404040812 -:105CA0000004000808040808000121020011301057 -:105CB00020080E022202000A0847050210001010F8 -:105CC0000247021000040A0C04040006080802003F -:105CD0000A0206000A0A020E0A000606060200046C -:105CE0000A0A0A02020013011130040A00300020DF -:105CF000024705080610470306470502081000047E -:105D00004703141410100002181018180147020855 -:105D100004060200060606020C080A0A0A00080A1F -:105D200003010320470202200A020A001A0A1A127B -:105D30000400081A08080002080808080800080CEF -:105D40000C0C02040C0018100400101800100002C3 -:105D5000040606060808060202000A0C00020002F9 -:105D600047032000220C020002120002001212124D -:105D700002470240424047020C080808040002049F -:105D80000404470410121204001247030200040620 -:105D9000060247020202020247020247020604020A -:105DA0000406060002000A021A0A0001121200325A -:105DB0000447022008200002080808080200080022 -:105DC00008100403121A120847040402020206060D -:105DD0000202000422020200060100020002470241 -:105DE0002200020A47023A2A321A02040412040468 -:105DF00047040808040008000101020011101047C0 -:105E000002040202024708024702101002001010AA -:105E10000010000604060602470202020A000600FD -:105E2000080A04020002040400020004080A0A101E -:105E3000020013131302044702024706104702062A -:105E400047021000060010001047020A47021447DC -:105E50000314141004000218001818000110120690 -:105E600016020006060200040008080847020201A4 -:105E7000010147041018100200181A1A0A04000A37 -:105E80000208020002000800084704040402040497 -:105E90000400100400101012100002160606064737 -:105EA0000302470202040808084706100402100013 -:105EB0001010020010020202040002020002000898 -:105EC000080808080002040404044704120204003D -:105ED00012101012020006160602470302470604BB -:105EE0000004000404001402001818181A0001121B -:105EF0000202020400024702080002080800080229 -:105F00004705040112021210470210120612000285 -:105F10000606000600044702200006012000204774 -:105F2000041018104702383A320A02040606060422 -:105F30004702110162003F824702010347080247FE -:105F40000802470601014706202047062020470691 -:105F5000042047064004470740470E858447070151 -:105F600047068047088047060202470602470802A4 -:105F7000471E04470804471E0404471E2047072005 -:105F80002047062020470601470801473601014700 -:105F9000164247084247060202470E2120470620C4 -:105FA0002147061020470614144706505047061094 -:105FB00010470610104706951147061094470690A3 -:105FC00090470610104706021047060202472604B3 -:105FD00004472604470804471E2047072020470699 -:105FE000202047060101472E01470801471E424273 -:105FF0004707024706302047063030470611114751 -:106000000614104706501447061050470610104754 -:106010000611104706101147069494470610904742 -:1060200006101047061210470612124706101047B6 -:106030000710470E04470804472E0404472E20206B -:1060400047062120470701472601014708820080B3 -:1060500001034720520247063052470630304706B8 -:106060001010470611104706041547064040471612 -:106070000101470E84470804470E1202470612101A -:106080004706101247061010470610104706041462 -:10609000473604470804472E202047060121471EA3 -:1060A00001470801471610470712104706301247EC -:1060B000067272470710470E04470741054707401D -:1060C000470E014708014716040447060247080225 -:1060D000470E0202470610470710104706101047E8 -:1060E0000610104707104766212047072147160171 -:1060F00001471610470710104706121247062030B6 -:106100004706422047074247164140470701470ECE -:10611000010147260202471602470802470E1047B0 -:1061200007101047061010470710475E2047072149 -:10613000214708110262002F824702010347081B12 -:106140001B47041F1B4704041F47101B47051B1B4D -:1061500047041F1B47041F1F47101B1B47041B1B23 -:1061600047041F1F47041F1F47051B470A1B1B47E8 -:10617000051B4704040447041F1F47051B470A1B50 -:106180001B4704E4FB4704E4E447041FFF47051BE7 -:10619000470AF9194704E4F94704E4E44704F7F728 -:1061A000470513470AF9F94704E4F94704E4E447CB -:1061B00004F7F74705F3470AF9E9470414FD4704D4 -:1061C0001E1C4704F7FF4705E1470AE9E947041C9D -:1061D000FD47041E1A4704EFFF4705E1470ADDC9E2 -:1061E00047041EDD47043E3A4704BDBF4705A947A3 -:1061F0000ADDD94704A2FF4704A6A24704BDBD4754 -:1062000005B9470AFFDD4704A6FF47048486470413 -:10621000BDBD470404BD470AFFFD470406FF470410 -:10622000040447043D3D4704043D470AFFFF47047B -:1062300006FF470404044704FDFD470404FD470A24 -:10624000FBFB4705FB4704244705FDFD4705F947D0 -:106250000AFBFB4704FBFB4704FFFB4704FFFF4728 -:1062600005FB470AFB224704FBFB4704DFFB47040F -:10627000FFFF470522470A22024704FBFB4704DFD2 -:10628000DB470426FF470522478002820080010386 -:1062900047022C2C47041F3E47041F1F4704CCDE37 -:1062A0004705C8470A3E2C47041F3F47049F1F4726 -:1062B00004DEDE4705C8470A3F3E47041F3F470448 -:1062C0009F9F4704DEDE4705DA470A373647040159 -:1062D000374704898147045ADA470552470A763618 -:1062E0004704C1F64704A9814704FAFA4705D24793 -:1062F0000A74744704C1F44704A9E94704F2FA4751 -:1063000005F2470A74744704E5F44704EDED4704C9 -:10631000F2FE4705F2470A7474470427744704CF16 -:106320008F470476FE470570470A747447041F744C -:1063300047049F9F470474F6470574470A746447EF -:10634000041B7447049B9B470476F6470574470A71 -:1063500060604704197047048999470476F6470539 -:1063600076470A60604704096047048989470476D4 -:10637000F6470576470A64644704096C4704818937 -:10638000470476F6470576470A76764704097E473E -:10639000048189470476F6470576470A76764704EE -:1063A000297E4704C189470476F6470576470A7770 -:1063B0007647047F7F4704F7FF470477F747057662 -:1063C000470A776047045F7F4704B7FF470477F7C2 -:1063D000470560470A612047045F7F4704B79F472E -:1063E0000461F7470540478002110362003F82477E -:1063F0000201034783828200800103478382224493 -:10640000B20106004700000000000000000000008C -:1064100000040240000400000A00090000140240C9 -:10642000000400000A000900000C024000080000FF -:106430000B00090000080240000400000A000900E7 -:1064400000100240040000000200090000000240A9 -:10645000020000000100090000100240008000005E -:1064600000000000001C02402000000001000000AD -:10647000000000000000000000000000000C0240CE -:106480000400000001000000000000080080000877 -:106490000000010800800108000002080000040854 -:1064A0000000080800000C08000010080000140894 -:1064B0000000180800001C08000008001000180068 -:1064C00020002800300038004000480050005800EC -:1064D000000000006410B71DC8206E3BAC30D92608 -:1064E0009041DC76F4516B6B5861B24D3C710550B4 -:1064F0002083B8ED44930FF0E8A3D6D68CB361CBDC -:10650000B0C2649BD4D2D38678E20AA01CF2BDBD8F -:106510004932435F504D49435F4D414700537475C5 -:10652000636B20627574746F6E2072656769737433 -:10653000657220697320696E76616C69642C2063D2 -:106540006C656172696E672E00427574746F6E209F -:1065500069642000697320737475636B2100427550 -:1065600074746F6E20776173207075736865642032 -:106570006F6E20626F6F742E20427574746F6E2080 -:10658000636F756E7465723A2000446973706C6154 -:106590007920627573792D776169742074696D65EE -:1065A0006F75742065787069726564210046504784 -:1065B0004120636F6E66696775726174696F6E20E2 -:1065C0006661696C65642E00446973706C61792042 -:1065D000696E697469616C697A6564206166746565 -:1065E00072200020726574726965732E00446973AD -:1065F000706C617920696E697469616C697A617423 -:10660000696F6E206661696C65642E004352455364 -:106610004554206E6F74206C6F7720647572696EBC -:10662000672072657365740043444F4E45206E6F5A -:1066300074206C6F77206166746572207265736573 -:106640007400435245534554206E6F742068696747 -:1066500068206166746572207265736574004344D6 -:106660004F4E45206E6F74206869676820616674BC -:1066700065722070726F6772616D6D696E6700453B -:106680006E61626C696E67203676362028446973C5 -:10669000706C617920564444432900456E61626CF8 -:1066A000696E67203476352028446973706C61798F -:1066B0002056444450290044697361626C696E67D6 -:1066C000203476352028446973706C6179205644F3 -:1066D0004450290044697361626C696E67203676A4 -:1066E000362028446973706C6179205644444329EC -:1066F000002E2E2F7372632F647269766572732F6A -:106700006932632F6932632E63002E2E2F737263FA -:106710002F647269766572732F6932632F693263F1 -:106720005F68616C2E630046617374204D6F646511 -:1067300020506C7573206E6F7420796574207375AA -:1067400070706F72746564002E2E2F7372632F64E5 -:106750007269766572732F7065726970685F636FB6 -:106760006E6669672E6300616464726573732000EE -:10677000206973206F75747369646520737973740D -:10678000656D20666C617368006661696C65642084 -:10679000746F20657261736520736563746F722016 -:1067A0000050726F6772616D206661696C6564206C -:1067B0004000496E76616C6964206669726D77612C -:1067C0007265206465736372697074696F6E21000D -:1067D000436865636B73756D6D696E67206669727A -:1067E0006D77617265207570646174650043616CDA -:1067F00063756C6174656420636865636B73756D44 -:106800003A2000496E76616C6964206669726D7722 -:106810006172652043524320696E205350492066BF -:106820006C61736821007072765F65726173655F79 -:106830006F6C645F6669726D776172650070727605 -:106840005F77726974655F6E65775F6669726D7791 -:106850006172650057652772652064656164004355 -:106860006865636B73756D6D696E67200020627972 -:106870007465730D0A00436865636B73756D202D35 -:106880002077616E746564200020676F7420004F6C -:10689000757220696E7465726E616C20666C6173CE -:1068A0006820636F6E74656E747320617265206218 -:1068B00061642028636865636B73756D2066616928 -:1068C0006C6564292120546869732069732072659E -:1068D000616C6C792062616421004F757220707266 -:1068E0006576696F7573206669726D776172652070 -:1068F000757064617465206661696C65642C2061E3 -:10690000626F7274696E67207570646174652E00C1 -:106910004E6577206669726D7761726520697320B4 -:10692000617661696C61626C6521004C6F616469BC -:106930006E67207265636F76657279206669726D25 -:1069400077617265004661696C656420746F206CC4 -:106950006F6164207265636F766572792066697213 -:106960006D776172652C20737472696B65206F6E30 -:10697000652E2054727920616761696E2E00466130 -:10698000696C656420746F206C6F6164207265634C -:106990006F76657279206669726D776172652C20F9 -:1069A000737472696B652074776F2E20547279202E -:1069B000616761696E2E004661696C656420746F61 -:1069C000206C6F6164207265636F766572792066F2 -:1069D00069726D776172652C20737472696B6520C2 -:1069E00074687265652E20534144205741544348D2 -:1069F0000048415244204641554C54006578697422 -:106A0000207374616E646279000D0A0D0A0D0A002C -:106A1000E29688E29688E29688E29688E29688E294 -:106A20009688E295972020E29688E29688E29688FA -:106A3000E29688E29688E29688E2959720E2968828 -:106A4000E29688E29688E29688E29688E29688E264 -:106A5000959720E29688E29688E29688E29688E208 -:106A60009688E29688E29688E29597E29688E29682 -:106A700088E29688E29688E29688E29688E2959780 -:106A800020E29688E29688E29688E29688E29688E6 -:106A9000E29688E29688E29688E2959700E29688E8 -:106AA000E29688E29594E29590E29590E29688E2EB -:106AB0009688E29597E29688E29688E29594E29528 -:106AC00090E29590E29590E29688E29688E295971A -:106AD000E29688E29688E29594E29590E29590E2BB -:106AE0009688E29688E29597E29688E29688E29503 -:106AF00094E29590E29590E29590E29590E2959DD2 -:106B0000E29688E29688E29594E29590E29590E28A -:106B10009688E29688E29597E2959AE29590E295BA -:106B200090E29688E29688E29594E29590E29590BC -:106B3000E2959D00E29688E29688E29688E2968841 -:106B4000E29688E29688E29594E2959DE29688E244 -:106B50009688E29591202020E29688E29688E29538 -:106B600091E29688E29688E29688E29688E2968894 -:106B7000E29688E29594E2959DE29688E29688E214 -:106B80009688E29688E29688E295972020E2968899 -:106B9000E29688E29688E29688E29688E29688E213 -:106BA0009594E2959D202020E29688E29688E295D1 -:106BB0009120202000E29688E29688E29594E29562 -:106BC00090E29590E29688E29688E29597E2968820 -:106BD000E29688E29591202020E29688E29688E26B -:106BE0009591E29688E29688E29594E29590E295F6 -:106BF00090E29688E29688E29597E29688E29688F7 -:106C0000E29594E29590E29590E2959D2020E2969F -:106C100088E29688E29594E29590E29590E29688D3 -:106C2000E29688E29597202020E29688E29688E214 -:106C3000959120202000E29688E29688E2959120A6 -:106C400020E29688E29688E29591E2959AE296880B -:106C5000E29688E29688E29688E29688E29688E252 -:106C60009594E2959DE29688E29688E29688E2966F -:106C700088E29688E29688E29594E2959DE296886D -:106C8000E29688E29688E29688E29688E29688E222 -:106C90009688E29597E29688E29688E29591202080 -:106CA000E29688E29688E29591202020E29688E29A -:106CB0009688E2959120202000E2959AE29590E254 -:106CC000959D2020E2959AE29590E2959D20E2958F -:106CD0009AE29590E29590E29590E29590E29590F7 -:106CE000E2959D20E2959AE29590E29590E295904A -:106CF000E29590E29590E2959D20E2959AE295903A -:106D0000E29590E29590E29590E29590E29590E27E -:106D1000959DE2959AE29590E2959D2020E2959AC4 -:106D2000E29590E2959D202020E2959AE29590E2EE -:106D3000959D202020004C617374206669726D77E8 -:106D400061726520626F6F74207761732073746164 -:106D5000626C653B20636C65617220737472696B51 -:106D6000657300486F6C6420646F776E2055502007 -:106D70002B204241434B202B2053454C454354206C -:106D8000666F72203520736563732E20746F2066E2 -:106D90006F7263652D626F6F7420505246004669B2 -:106DA000726D7761726520697320657261736564C5 -:106DB000005761746368646F67206361757365640D -:106DC000206120726573657400536F667477617219 -:106DD00065206661696C75726520636175736564B1 -:106DE0002061207265736574004661696C6564207A -:106DF000746F207374617274206669726D7761724A -:106E0000652C20737472696B652074687265652ED9 -:106E1000004661696C656420746F207374617274DC -:106E2000206669726D776172652C20737472696B6C -:106E3000652074776F2E004661696C656420746FFD -:106E4000207374617274206669726D776172652C4B -:106E500020737472696B65206F6E652E00466F72C9 -:106E600063652D626F6F74696E67207265636F76FC -:106E7000657279206D6F64652E2E2E00426F6F74DF -:106E8000696E67206669726D7761726520402000C7 -:106E90002E2E2E0D0A0D0A00426F6F742062697447 -:106EA000733A20004153534552543A20003A00416E -:106EB0005353455254004153534552544E002A2ACD -:106EC0002A20575446200053544D33320053544D1A -:106ED0003332207065726970686572616C206C690C -:106EE0006272617279207472697070656420616E7B -:106EF000206173736572740069746F6120627566D6 -:106F000066657220746F6F20736D616C6C002E2E3D -:106F10002F7372632F7574696C2F736C652E630009 -:106F2000FF00000000010203040102030406070839 -:046F30000900000054 -:0400000508000200ED -:00000001FF diff --git a/bin/boot/nowatchdog_boot_silk@1478015115.bin b/bin/boot/nowatchdog_boot_silk@1478015115.bin deleted file mode 100755 index 6686b878cd..0000000000 Binary files a/bin/boot/nowatchdog_boot_silk@1478015115.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_silk@1478015115.hex b/bin/boot/nowatchdog_boot_silk@1478015115.hex deleted file mode 100644 index 9f3dd00a03..0000000000 --- a/bin/boot/nowatchdog_boot_silk@1478015115.hex +++ /dev/null @@ -1,901 +0,0 @@ -:020000040800F2 -:100000005821012085010008C901000811170008C6 -:10001000C9010008C9010008C9010008000000006A -:10002000000000000000000000000000C9010008FE -:10003000C901000800000000C9010008C90100084A -:10004000C9010008C9010008C9010008C901000868 -:10005000C9010008C9010008C9010008C901000858 -:10006000C9010008C9010008C9010008C901000848 -:10007000C9010008C9010008C9010008C901000838 -:10008000C9010008C9010008C9010008C901000828 -:10009000C9010008C9010008C9010008C901000818 -:1000A000C9010008C9010008C9010008C901000808 -:1000B000C9010008C9010008C9010008F9100008B9 -:1000C00005110008111100081D110008C9010008E0 -:1000D000C9010008C9010008C9010008C9010008D8 -:1000E000C9010008C9010008C9010008C9010008C8 -:1000F000C9010008C9010008C9010008C9010008B8 -:10010000C9010008C9010008C9010008C9010008A7 -:10011000C9010008C9010008C9010008C901000897 -:10012000C9010008C9010008C9010008C901000887 -:10013000C9010008C9010008C9010008C901000877 -:10014000C9010008C9010008C9010008C901000867 -:10015000C9010008C9010008C9010008C901000857 -:100160002911000835110008C9010008C90100085B -:10017000C9010008C9010008C9010008C901000837 -:04018000C9010008A9 -:10018400002103E00A4B5B584350043109480A4BF1 -:1001940042189A42F6D3094A02E0002342F8043B8B -:1001A400074B9A42F9D301F06FFE01F0FBFA704756 -:1001B400E03700080000002030000020300000205C -:1001C40054010120FEE7000037B5012002F074FA63 -:1001D400002401902546E0B200F046F801AA08B9CF -:1001E40010551DE0135D042B0BDD174800F07EF95C -:1001F400019800F082F90120002102F04DFA00205C -:1002040020E00133DBB2042B135509D90F4800F069 -:1002140055F9204600F071F90D4800F067F9012501 -:100224000134042CD7D1019C3CB10A4800F046F9B2 -:10023400204600F062F900F0D1F80120214602F0D6 -:100244002BFA284603B030BD74310008A0310008F1 -:10025400AB310008B531000808B500F0A7FB80F009 -:100264000100C0B208BD000008B5054B03EB001344 -:100274005868198901F067FF003018BF012008BDD4 -:10028400FC28000838B500242546E0B2FFF7ECFF4F -:10029400A04001340543042CEDB2F6D1284638BD04 -:1002A40073B50020014602F04FF80120002102F04E -:1002B4004BF80220002102F047F84FF4804001215E -:1002C400104C02F085F904F14006684601F032FF53 -:1002D40002238DF80530A37B60688DF80730002574 -:1002E400A3688DF8045069461034009301F0EAFEC7 -:1002F400B442EAD14FF48040294602F069F902B0D1 -:1003040070BD00BFFC28000810B504460848402111 -:1003140002F0D8FA0028F9D00548214602F0CEFAB6 -:100324000348402102F0CEFA0028F9D010BD00BFE6 -:10033400001001402DE9F043DFF89890DFF8988031 -:1003440087B01020012102F043F948460921072211 -:1003540001F004FF07210A4606AD404601F0FEFE07 -:100364004FF4007345F8183D00240226012748463F -:1003740029468DF806408DF807708DF804608DF8D5 -:10038400056001F09FFE80234046294600938DF8C6 -:10039400046001F097FEADF80C40ADF80E40ADF8E6 -:1003A4001040ADF8144004F1804404F58834084B3F -:1003B400029320460C2302A9ADF8123002F018FA79 -:1003C4002046394602F06EFA07B0BDE8F08300BF5C -:1003D40040420F0000000240000402402DE9F043B7 -:1003E400334D83B02C6804F12E0323F0070300AFD0 -:1003F400ADEB030D50238DF8003021238DF801302F -:1004040000238DF8023003238DF8033004F1210317 -:100414005BBAADF80430274B04F127086E460DF1A2 -:10042400060203F11C0153F8040B42F8040B8B423F -:10043400F9D11B78137004F12309224606F1230035 -:100444001D4902F045FA31464A46002001F0C4FC39 -:1004540007F1080304F5927443F8040D186846F88C -:100464000900FE23B4FBF3F34344073323F00703EB -:10047400ADEB030D31464246684601F085FC044667 -:100484005520FFF741FF6E466C44A64207D016F88C -:10049400010B552808BF0020FFF736FFF5E755206C -:1004A400FFF732FF00230C372B60BD46BDE8F08315 -:1004B40030000020442900083400002010B5441EF8 -:1004C40014F8012F7AB108490B68FF2B0BD80A2ABC -:1004D40002D1FFF783FFF3E70D2A1FBF581C5B18F7 -:1004E40008601A71ECE710BD3000002008B5FFF772 -:1004F400E5FFBDE80840FFF771BF1FB50C2201A955 -:1005040001F09AFC01A8FFF7D9FF05B05DF804FBE0 -:10051400014608B5044802F062F90348022102F0DA -:1005240060F90028F9D008BD003800402DE9F047F3 -:1005340007310733089E099F92464FEAD108DD0828 -:1005440081460024B4420CD004EB0A03122000FBC1 -:100554000380494638442A4602F0BAF90134A944D2 -:10056400F0E7BDE8F087000008B54FF480400121B2 -:1005740002F022F805484FF40071012201F0E9FD70 -:100584000720BDE8084001F047BC00BF000402405A -:1005940008B5072001F040FC4FF400710648002222 -:1005A40001F0D7FD042001F037FC4FF48040002116 -:1005B400BDE8084002F000B800040240F8B5074660 -:1005C400FFF7D2FF8020FFF7A3FF0024661C30BA98 -:1005D40090FAA0F0C0B2FFF79BFF122303FB047450 -:1005E4000025605D00BA90FAA0F00135C0B2FFF7B3 -:1005F4008FFF122DF5D10020FFF78AFFA82E344675 -:10060400E4D10020FFF784FFBDE8F840FFF7C0BF46 -:1006140010B5ADF6D83D02AC204600214FF43D6242 -:1006240002F061F91B238DE8180010214022692390 -:100634000448FFF77BFF2046FFF7C0FF0DF6D83DC7 -:1006440010BD00BF612900082DE9F043ADF6DC3D83 -:1006540002AC00214FF43D620546204602F043F906 -:100664002C238DE818002B48182120225C231C27FA -:10067400FFF75CFFBE460F23BB402B4023FA07F273 -:10068400254B242101FB0233002202EBC20000F6B9 -:10069400280013F802C020440021082907D00126AD -:1006A4008E4016EA0C0F14BF0126002603E09E18A4 -:1006B400767806F0010686B101EB0E06F6B24FEA33 -:1006C400D6094FF0010806F0070608FA06F610F8F6 -:1006D400098046EA080600F8096001310929DCD1DD -:1006E4000232242AD1D10EF10B0EBEF1740FA7F100 -:1006F4000407C0D10E238DE81800102190226D2329 -:100704000648FFF713FF2046FFF758FF0DF6DC3DC0 -:10071400BDE8F083DB2A0008EB2C00082B2F00082F -:1007240008B5FFF721FF0020FFF7F2FE0020FFF7D6 -:10073400EFFE0020FFF7ECFEBDE80840FFF728BFFE -:10074400F0B5214B1A685E246043B0FBF1F4944287 -:10075400ADF6DC3D34D002AD00214FF43D622846B5 -:100764001C6002F0C0F81B238DE8280017481021F4 -:1007740040226923FFF7DAFE08238DE82800144895 -:1007840018216A226023FFF7D1FE002000F286714F -:1007940029440022A2420DD002F11903DBB2DF0882 -:1007A400012603F0070306FA03F3CE5D3343CB556A -:1007B4000132EFE712306C28E8D12846FFF7FEFE3D -:1007C4000DF6DC3DF0BD00BF0000002061290008EB -:1007D400EF2F00082DE9F843224E234CDFF88C90CC -:1007E4004FF48040012101F0E7FE30460A21052242 -:1007F40001F0B4FC04F10408052230460F2101F095 -:10080400ADFC3046414601F05DFC01254FF4007318 -:100814000027304641466360A6F5E6362572A77286 -:1008240001F050FC414648462572A772656001F00C -:1008340049FC304601F050FF304604F10C0101F050 -:10084400A3FF3046294601F0BEFF484629462A4602 -:1008540001F07FFC39464FF48040BDE8F84301F0D5 -:10086400ABBE00BF000402400000002000000240B4 -:10087400F0B50746ADF2044D0E460C460025B919F5 -:10088400B4F5806FA1EB040168460DD94FF4806282 -:1008940000F0C4F8284669464FF4806201F09CFADF -:1008A400A4F580640546EAE7224600F0B7F8284636 -:1008B4006946224601F090FA0DF2044DF0BD08B5E8 -:1008C400202001F0DFFD0028FAD108BD08B5022080 -:1008D40001F0D8FD0028FAD00220BDE8084001F05C -:1008E400DBBD00B58DB0684601F055FD00230093D3 -:1008F4004FF4807368460A9335230B9301F07CFD13 -:10090400FFF7E4FFFFF7DBFF0DB05DF804FB000029 -:1009140030B5022095B0012101F042FE00242248A6 -:100924000021204403220B460C3400F0C3F8482C69 -:10093400F5D101A801F026FD10230024019301A89C -:10094400162305930294039404940694079401F0E7 -:1009540031FD012001F074FDFFF7C3FF08A801F089 -:100964001AFD4FF44075662308A813930894129552 -:1009740001F042FDFFF7AAFF08A801F00CFD99233E -:1009840008A813930894129501F036FDFFF79EFF13 -:1009940042F6E06001F040FAFFF7A3FF022021468F -:1009A40001F0FEFD15B030BD5030000830B5012116 -:1009B4008DB0022001F0F4FD022001F055FD6846DF -:1009C40001F0E9FC4FF0806300934FF0407304930F -:1009D4004FF4407368460A93AF230B9301F00CFD68 -:1009E400FFF774FF01F048FD054601F045FD04469C -:1009F40001F042FD240244EA004040EA0504FFF706 -:100A04005EFF0220002101F0CBFD0348231A584267 -:100A140058410DB030BD00BFC225370070B5154632 -:100A24008CB004460E460220012101F0B9FD681E77 -:100A340001F01AFD684601F0AEFC4FF080630093AC -:100A44004FF040730493042305934FF440630993D8 -:100A54004FF440730A934FF40053089368460B23F2 -:100A64000B9301F0C9FC304601F0F6FC2544AC427E -:100A740004D001F001FD04F8010BF8E7022001F0B5 -:100A84000BFDFFF71CFF0220002101F089FD0CB0D3 -:100A940070BD0000024A136B43F0FF0313637047F9 -:100AA40000380240024A136B23F0FF0313637047BC -:100AB4000038024013B504464068009002208DF8C7 -:100AC40004008DF805208DF8061020682189A27A8B -:100AD4008DF8073001F042FB2068694601F0F2FA14 -:100AE40002B010BD02460068838823F4E0631B044F -:100AF4001B0C838003889BB243F400730380082398 -:100B04000020517513751076704707B500230091C6 -:100B140069468DF804308DF805308DF8073001F002 -:100B2400D1FA03B05DF804FB10B50B4C342300FB81 -:100B34000344236B7BB10A2001F073F96068A16858 -:100B4400FFF7E3FF20696169FFF7DFFF236B0120F3 -:100B5400BDE81040184710BD98300008027D082AEF -:100B640010B5044607D10268938823F4C0631B04BC -:100B74001B0C938084E0434B006803EB8203596BA6 -:100B840001F0BCFB00287BD0237D072B72D8DFE863 -:100B940003F0040B1921323B5A6E237A226803F0C6 -:100BA400FE03138201232DE02268A37A1382637A61 -:100BB4000BB1022326E093889BB243F480639380B5 -:100BC40006231FE0226813889BB243F480731380CA -:100BD400032317E0237A226843F001031382E37AA4 -:100BE400012B01BF138823F480631B041B0C08BF73 -:100BF4001380042306E0226893889BB243F4806345 -:100C04009380052323753BE023682269198A237B9B -:100C1400D154237BE27A0133DBB2591C9142237312 -:100C240007D12268138823F480631B041B0C1380F0 -:100C340026E0934224D12268938823F480631B0422 -:100C44001B0C938013E0237B21692268CB5C138205 -:100C5400237BE17A0133DBB29942237310D1938869 -:100C640023F480631B041B0C93800723CAE72046EC -:100C7400012101E020460021BDE81040FFF732BF0A -:100C8400002010BD983000082DE9F341184C34239E -:100C940000FB0344236B43B300209847D4F804803B -:100CA400A668009600250127404669468DF8047021 -:100CB4008DF805508DF8075001F004FAB1B22A46B8 -:100CC400404601F046FA2669646900943046694654 -:100CD4008DF804708DF805508DF8075001F0F2F985 -:100CE4003046A1B22A4601F034FA02B0BDE8F081E0 -:100CF400983000081C23104A434370B505460F4C36 -:100D0400D058D61801F03CFA342305FB03440021E3 -:100D1400E06901F051FC337923B92846BDE87040FD -:100D2400FFF7B2BF6068A168FFF7EFFE2069616951 -:100D3400BDE87040FFF7E9BE340100209830000898 -:100D44002DE9F04F344D1C2303FB005387B01B796E -:100D540007460BB9FFF7E8FE304B342407FB043495 -:100D64000226D4F80490A389A26894F80EB00193E3 -:100D74004FF001084FF0000A484602A902928DF88C -:100D84000C608DF80D608DF80E808DF80FA001F0C9 -:100D940099F9019B484619465A4601F0DFF9D4F8FF -:100DA4001090238B626994F81AB00193484602A903 -:100DB40002928DF80C608DF80D608DF80E808DF820 -:100DC4000FA001F07FF9019B5A461946484601F0ED -:100DD400C5F94146E06901F0EFFB02A801F068FAA9 -:100DE4000F4A236A029393424FF48063ADF81230A2 -:100DF4004FF01C0384BF626AADF80E2003FB07F4B6 -:100E040002A9285901F0EAF92859012101F05EFAF2 -:100E140007B0BDE8F08F00BF34010020983000080F -:100E2400A08601002DE9F341414C9846237F0546F5 -:100E34000E4617461BB93F484FF4BD7103E020B17D -:100E44003C4840F27B1100F079FF23790BB9002074 -:100E54006AE023681B8B9B0711D49DF82030E37252 -:100E64000025099B277284F80A806672257325750C -:100E74002361E5822068012101F034FA1FE0FFF7C5 -:100E840039FF2846FFF75CFF22682B48138B99072C -:100E9400E3D50138FAD1DAE7227E22B1019A511E54 -:100EA4000191002AF8D100222276019A204902B346 -:100EB4000A7D082A13D14D7D237D082B26D0236873 -:100EC4001A8892B242F480721A809A8892B242F4DA -:100ED40040729A8001222276174A0192DEE7CA8A7A -:100EE400B2F57A7F05D20132CA82012000F099FF5F -:100EF400E2E79A8822F4E0621204120C9A801A88BB -:100F040092B242F400721A8008230B7522680A4CCC -:100F1400138B9B0707D5013CFAD12046FFF7EAFE65 -:100F24002046FFF70DFF284602B0BDE8F08100BF60 -:100F340034010020F531000880841E00017D03681F -:100F4400082906D19A8822F480721204120C9A801D -:100F540038E09A8A120541BF9A8A22F40062120488 -:100F6400120C48BF9A829A8AD20541BF9A8A22F407 -:100F740080721204120C48BF9A829A8A52051ED5B6 -:100F84009A8A22F480621204120C04299A820AD1E9 -:100F9400032202759A8822F4E0621204120C9A80E9 -:100FA400002303760EE0012909D19A8822F4E06235 -:100FB40012040021120C01759A80017602E00021CE -:100FC400FFF790BD0020704737B5124C124B2360D9 -:100FD40008222275482200238DF8042001250C22C2 -:100FE40001A88DF80520237123762373E37223610E -:100FF4008DF806308DF8075000F082FF492301A8D0 -:101004008DF8043000F07CFF206801F0B9F82577F2 -:1010140003B030BD34010020005C004013B50D4C1A -:10102400227F034622B90C484FF48C7100F086FEEF -:1010340038B1012200920848084A4FF48D7100F03B -:1010440071FE23790BB9FFF77BFE237901332371FA -:1010540002B010BD34010020F53100080A32000846 -:101064000C4A117F08B519B90B484FF4957103E088 -:1010740020B1094840F22B1100F060FE13793BB116 -:10108400013BDBB213711BB9BDE80840FFF732BE68 -:1010940008BD00BF34010020F531000837B50D4606 -:1010A40014460093069B019301212A462346FFF729 -:1010B400B9FE03B030BD07B500930123FFF7EEFF7F -:1010C40003B05DF804FB37B50D4614460093069B48 -:1010D400019300212A462346FFF7A4FE03B030BD46 -:1010E4001FB504AC04F8013D01230094FFF7EBFFA6 -:1010F40004B010BD0148FFF731BD00BF340100202A -:101104000148FFF71BBF00BF340100200148FFF76F -:1011140025BD00BF500100200148FFF70FBF00BFED -:10112400500100200148FFF719BD00BF3401002021 -:101134000148FFF703BF00BF3401002038B50C4657 -:1011440005460020FFF76AFF234681212A46002036 -:10115400FFF7B1FF04460020FFF782FF204638BDA9 -:1011640038B50C4605460020FFF758FF234680217A -:101174002A460020FFF7B4FF04460020FFF770FF63 -:10118400204638BD37B50D460DF107010446FFF77B -:10119400D5FF50B19DF807300121A9401943C9B2C8 -:1011A40020468DF80710FFF7DBFF03B030BD0000C9 -:1011B40013B590200DF10701FFF7C0FF044648B3B3 -:1011C4009DF80740A4F111035C425C4114B3FF2174 -:1011D4004220FFF7C5FFFF214120FFF7C1FF352063 -:1011E4000DF10701FFF7AAFF044620B90B480C4A8A -:1011F400BE2100F097FD9DF80710352041F0030152 -:101204008DF80710FFF7ACFF0A201E21FFF7A8FF97 -:101214000028EBD0204602B010BD00BF36320008D3 -:101224005432000807B587200DF10701FFF786FF48 -:1012340018B19DF80700C0F3800003B05DF804FB0B -:1012440007B536200DF10701FFF778FF20B19DF8AF -:1012540007301B090B2B01D0002014E077200DF17F -:101264000701FFF76BFF0028F6D09DF8073003F065 -:10127400100303F0FF0023B1FFF7D4FF80F0010057 -:10128400C0B200F0010003B05DF804FB08B53620DD -:101294000121FFF777FF00B1FEE708BD07B5EF2195 -:1012A4007420FFF75DFFFF217520FFF759FF0DF153 -:1012B40007017720FFF742FF0DF107017820FFF7C0 -:1012C4003DFF0A213920FFF74BFF35200421FFF7AA -:1012D40059FF00B1FEE703B05DF804FBB0F1006F05 -:1012E40010B504460BD20C48FFF7E8F82046FFF788 -:1012F40004F90A48FFF7FAF84FF0FF3010BD084B25 -:10130400002053F8042BA24202D81A68944202D354 -:1013140001300B28F5D110BDB43200087332000837 -:10132400EC3000082DE9F8430446174699460E466A -:10133400C1B3FFF7D3FF0546601E3044FFF7CEFF6D -:10134400002D804631DB00282FDB461B01361FB100 -:10135400002031464A46B84700F014FE2C46444566 -:101364001EDCF32000F024FE114B002133F814009E -:1013740000F056FE09280AD00E48FFF79FF82046D1 -:10138400FFF7BBF8FFF72AF800F00AFE0DE02FB1D3 -:10139400C5F10100204431464A46B8470134DEE72E -:1013A40000F0FEFD0120BDE8F8830020BDE8F883CD -:1013B4001C3100088C3200082DE9F0470E46154612 -:1013C4000746984600F0DEFDF32000F0F1FD4FEAF9 -:1013D400D51934463544AC42C6EB040A20D0B81BB8 -:1013E400204414F8011B00F051FE09280CD00F48CA -:1013F400FFF764F83846FFF780F8FEF7EFFF00F0D8 -:10140400CFFD0020BDE8F087B8F1000FE3D01AF05B -:101414007F0FE0D14FEADA104946089AC047DAE76D -:1014240000F0BEFD0120BDE8F08700BFA432000833 -:1014340008B545F2555000F06FFF042000F072FF2C -:1014440040F6FF7000F074FF002000F065FF4FF4D9 -:1014540080500121BDE8084000F088BD08B57D201A -:1014640001F0FEF8003018BF012008BD4900FFF765 -:1014740067B900002DE9F041012186B0054600203E -:10148400FFF75EF9042000F0A1FB03A8294600F051 -:10149400FBFB03A800F007FC08B93C4814E03C48F7 -:1014A4000C35FFF723F804992846FFF7E1F90446C1 -:1014B4003848FFF703F82046FFF71FF8FEF78EFFC2 -:1014C400059B9C4204D03448FFF710F8012059E0F2 -:1014D400049C3248FFF70AF82146314A3148002378 -:1014E400FFF720FF3048049FFFF700F84FEA570842 -:1014F4000024BC4220D23E1BB6F5803F28BF4FF4E7 -:1015040080362A4861193246FFF788FA002304F12D -:101514000060009300F5804024493246FFF74CFFF9 -:1015240018B92348FEF7E2FF06E0344408EB540000 -:101534003946FFF705F9DCE71E48FEF7BFFF0498BC -:10154400FEF7DBFF1C48FEF7B9FF1649049A00209A -:1015540000F042FC04461948FEF7B0FF0598FEF778 -:10156400CCFF1748FEF7AAFF2046FEF7C6FFFEF79A -:1015740035FF059B9C4204D01248FEF7B7FF0220BA -:1015840000E0002006B0BDE8F08100BFBD320008D5 -:10159400DB320008F83200080E330008313300084B -:1015A4007114000800400008443300085101002071 -:1015B4005733000862330008703300087933000899 -:1015C4008C3300089233000810B5022000F016FB9B -:1015D400D8B3042000F012FB044670B11B48FEF798 -:1015E40085FF042000F0FEFA022000F0FBFA4FF41D -:1015F4000040BDE8104000F0F5BA1548FEF776FF4C -:10160400082000F0EFFA102000F0ECFA202000F09F -:10161400E9FA402000F0E6FA2046FFF72BFF022803 -:1016240007D1082000F0D2FA102000F0CFFA00F021 -:10163400A1FB042000F0D6FA022000F0D3FA4FF404 -:101644000040BDE8104000F0C1BA10BDDD33000811 -:101654001334000810B52548FEF748FF4FF4001076 -:10166400FFF708FF10B102283AD809E0202000F063 -:10167400B9FA402000F0B6FA802000F0A7FA2FE073 -:10168400082000F0A3FA102000F0A0FA202000F0B7 -:10169400B5FA20B91648FEF729FF202007E04020BC -:1016A40000F0ACFA58B91348FEF720FF402000F0D0 -:1016B4008DFA4FF4007000F089FA00F05BFB0E48DD -:1016C400FEF714FF082000F08DFA102000F08AFACB -:1016D400202000F087FA402000F084FA002400E083 -:1016E4000124042000F07EFA204610BD2E340008A8 -:1016F4004834000881340008BA34000808B50248A8 -:10170400FEF7F4FE00F0BEFAF43400081EF0040FF5 -:101714000CBFEFF30880EFF30980FFF7EFBF7047CA -:1017240008B50120FEF7A0FD08B900200FE0002055 -:10173400FEF79AFD0028F8D00220FEF795FD002858 -:10174400F3D00320FEF790FD80F00100C0B200F05A -:10175400010008BD1FB504461048FEF7C7FE01A9E5 -:101764000C22204600F068FB01A8FEF7BFFE2046CD -:10177400FEF76AFFFEF786FD094B20F0040018709F -:101784001C46FEF77FFD237800F0FB00834201D066 -:1017940000F0F0FA0A2000F044FBF2E7EA34000813 -:1017A4005101012030B585B0FFF774F94FF0805036 -:1017B400012100F001FF022000F0D2FD012807D131 -:1017C400022000F0D7FDA548FEF790FE00F0D9FAFC -:1017D40000214FF0805000F0EFFEFEF7ABFDA04873 -:1017E400FEF784FE9F48FEF781FE9F48FEF77EFECB -:1017F4009E48FEF77BFE9E48FEF778FE9848FEF76B -:1018040075FEFFF7E1FB9B48FEF770FEFFF7D0FC87 -:101814009948FEF76BFE00F0FBF99848FEF766FE68 -:1018240000F022FA00210C2201A801F05CF800F07B -:1018340017FA01A90C2200F0FFFA01A8FEF756FEE0 -:101844008748FEF753FE8648FEF750FE4FF480406B -:1018540000F0D4F990B18A48FEF748FE4FF4804076 -:1018640000F0C0F9082000F0BDF9102000F0BAF92A -:10187400202000F0B7F9402000F0B4F9FFF7E0FCB5 -:1018840038B18048FEF732FE7F48FEF72FFEFFF79F -:1018940005FDFFF73DF8FEF703FDFEF79BFFFEF79E -:1018A400B7FEFEF791FC08B1784803E0FEF7D4FCDC -:1018B40010B17748FFF74EFF00F0C2F9802000F026 -:1018C4009DF918B1802000F08DF949E04FF4003003 -:1018D40000F094F920B14FF4003000F083F959E19D -:1018E400FFF71EFF68B16B48FEF700FE41F2883433 -:1018F400FFF716FF78B1012000F093FA013CF7D10D -:1019040048E1654B0CCB013301D0013203D163486C -:10191400FEF7ECFD3EE1FFF7A1FD18B3FFF79EFDD6 -:1019240010B15F48FEF7E2FD4FF4007000F066F975 -:1019340010B15C48FEF7DAFD4FF4007000F052F984 -:10194400102000F05BF9D0B15748FEF7CFFD082016 -:1019540000F048F9102000F045F9FFF77BFEF8B9D4 -:101964005248A7E74FF4007000F048F90028D5D199 -:10197400082000F037F9102000F034F90EE00820B8 -:1019840000F03CF920B14A48FEF7B0FD102003E016 -:101994004848FEF7ABFD082000F018F9FFF714FEE5 -:1019A4004FF4005000F02AF904464FF4805000F040 -:1019B40025F9400040EA8400C4B24FF4006000F00E -:1019C4001DF92043C0B20728C3B20DD14FF4006003 -:1019D40000F008F94FF4805000F004F94FF400507F -:1019E40000F000F9344865E70133DBB2023B4FF401 -:1019F4000060052B11D8DFE803F00C1003100C1065 -:101A040000F0F0F84FF4805000F0ECF84FF4005080 -:101A140003E000F0E7F84FF4805000F0D7F84FF4FB -:101A2400002000F0EBF8002851D04FF4002000F023 -:101A3400D9F82248FEF75AFD0024FFF7F3FB0028EB -:101A440040D0042C03D11E48FEF750FD00244FF46F -:101A5400FA7000F0E6F90134EFE700BFFF34000844 -:101A6400403700080C3500081D3500082E350008E5 -:101A74003F350008503500085B350008673500081D -:101A8400703500089D350008CE350008014550FE2C -:101A9400024550FEED3500080040000828360008D5 -:101AA4003B3600085336000873360008034550FEE1 -:101AB4009B360008C1360008044550FEE73600088E -:101AC400FB3600083548FEF711FDFFF7DFFBFFF793 -:101AD400AFFCFEF7E7FF324B32485C681D68FEF747 -:101AE400EDFC2046FEF709FD2F48FEF7E7FC002237 -:101AF400930003F1604303F5614301324FF0FF317A -:101B0400082AC3F88010C3F88011F1D1274B284A62 -:101B1400196B0A401A635A6B22F0C0025A639A6B1B -:101B240022F003029A63196C224A0A401A64596C1F -:101B3400214A0A405A6400F0B5FC2048012100F013 -:101B440053FD1E48002100F04FFDF120012100F05B -:101B540057FDF120002100F053FD0120014600F063 -:101B64005BFD0120002100F057FD1548012100F024 -:101B74005FFD1348002100F05BFD1248012100F0D5 -:101B840063FD1048002100F05FFD63B64FF0FF3E97 -:101B9400AD4620470C48FEF7A9FCDEE616370008E0 -:101BA4000040000825370008393700080038024093 -:101BB40000EF9FFF003619E8CC86E8FE0010E02213 -:101BC400FFC9FEF6337F77044137000810B5044699 -:101BD400002000F071FD40EA04010020BDE810403F -:101BE40000F05ABD10B50446002000F065FD20EA5F -:101BF40004010020BDE8104000F04EBD10B50446BD -:101C0400002000F059FD204214BF0120002010BD27 -:101C140008B501214FF0805000F0CEFC012000F007 -:101C24008DFB4FF08050002100F0C6FC0120FFF72F -:101C3400E5FF20B90121BDE8084000F02DBD08BD35 -:101C440008B50648FEF73AFC002000F035FDFEF723 -:101C540054FCBDE80840FEF7C1BB00BF6037000874 -:101C6400022000F029BD000008B5FFF7F9FF044980 -:101C7400884204D00220BDE8084000F00DBD08BD34 -:101C84008BB8185800BEFDE71FB504460C2201A806 -:101C9400FEF7C4FE01AB03CB20601868A0602046A9 -:101CA400616004B010BD0068A0F10C0358425841B3 -:101CB4007047000080B50646174610480D461C467E -:101CC400FEF7FCFB3846FEF7F9FB0D48FEF7F6FB82 -:101CD4003046FEF7F3FB0B48FEF7F0FB2846FEF711 -:101CE4000CFC2CB10848FEF7E9FB2046FEF7E6FBA6 -:101CF4000648FEF7FBFBFFF7C5FF00BF6C37000883 -:101D04001A350008753700081B35000840370008ED -:101D14001FB506AA52F8044B039200921A462346B2 -:101D2400FFF7C8FF0CB41FB506AA52F8043B039290 -:101D34000092014AFFF7BEFF7737000807B500237A -:101D440000937246014BFFF7E3FF00BF7E370008A4 -:101D5400BFF34F8F0549064BCA6802F4E062134390 -:101D6400CB60BFF34F8F00BFFDE700BF00ED00E085 -:101D74000400FA0508B5FEF7D3FC00F085FCFFF774 -:101D8400E7FF08B5FFF7E4FFF0B50646002401209D -:101D94002546034694421DD011F804C000F1010EFB -:101DA400BCF1000F03D17355774605460DE00133AE -:101DB400DBB2FF2B774606F800C007D102F1FF3CE7 -:101DC400644506D07355871C754601230134384693 -:101DD400E0E770467355F0BD30B5C9B1C0430A445D -:101DE400914213D011F8013B0A4D83EA000404F038 -:101DF4000F0455F8244084EA101080EA131303F00A -:101E04000F0355F8233083EA1010E9E7C04330BDCF -:101E1400084630BD34310008062358430138FDD14B -:101E2400704710B504462CB14FF47A70FFF7F4FFF5 -:101E3400013CF8E710BD00000A2A10B504DC114883 -:101E4400BDE81040FEF752BB0C46302304F8023BB9 -:101E540078234B701C220F2393400340D340092B5B -:101E640001D8303302E00F2B02D85733DBB200E045 -:101E74002023043A04F8013B131DECD100238B7298 -:101E840010BD00BFCA3700081949D1F8883043F49F -:101E94007003C1F88830174B1A68002042F0010221 -:101EA4001A6098601A6822F0847222F480321A60F0 -:101EB400114A5A601A6822F480221A60D8601A6C97 -:101EC4000E4842F080521A64026842F440420260B2 -:101ED4009A689A609A689A609A6842F480529A6002 -:101EE4004FF4C062C3F800244FF000638B60704766 -:101EF40000ED00E000380240103000240070004083 -:101F0400C278037810B512B3164AD1684278C9432F -:101F1400C1F30221C1F10404E4B2A240D4B20F22FD -:101F24000A4181780A40224303F1604303F5614387 -:101F34001201D2B283F8002303780122590903F075 -:101F44001F0302FA03F3084A42F8213010BD5A096C -:101F5400012103F01F03994002F12003024A42F8D1 -:101F6400231010BD00ED00E000E100E0044B9A688E -:101F740009B1104301E022EA00009860704700BFF5 -:101F8400002004E0044B1A69002ABFBF034A5A60C8 -:101F940002F188325A607047003C024023016745D1 -:101FA400024A136943F0004313617047003C024046 -:101FB400014BD860704700BF003C02400E4BDA680A -:101FC400D20310D4DA68D1060FD4DA68D2050ED45D -:101FD400DA6812F0E00F0CD1DB6813F0020F14BFC3 -:101FE400082009207047012070470620704702200E -:101FF40070470720704700BF003C024007B5092323 -:102004008DF80730FFF7DAFF8DF807009DF80730E9 -:10201400012BF7D09DF8070003B05DF804FB000026 -:1020240070B5064641B1012908D002290CBF4FF40E -:1020340000754FF4407503E00D4601E04FF48075E0 -:10204400FFF7DCFF09281ED10F4C236923F44073EA -:102054002361216929432161236923F0F803236162 -:10206400236943F002031E432661236943F480334A -:102074002361FFF7C3FF236923F00203236123696C -:1020840023F0F803236170BD003C024070B505469F -:102094000E46FFF7B3FF092811D1094C236923F435 -:1020A4004073236123692361236943F0010323619E -:1020B4002E70FFF7A3FF236923F00103236170BD92 -:1020C400003C0240F0B50E6800220123934003EA6D -:1020D400060E9E452AD1550003230468AB40DB431A -:1020E4001C4004600C79076804FA05FC013C4CEAC6 -:1020F4000707012C076011D884684F791C4084605D -:102104008468AF4027438760446824EA0E0444602F -:102114008C7947689440A4B23C434460C46823402B -:10212400C360CB79C468AB402343C3600132102A37 -:10213400CBD1F0BD4FF6FF730360002303714371ED -:102144008371C37170470369194214BF01200020D1 -:1021540070470AB1018370474183704701F0070358 -:10216400C90800EB810010B59B00046A0F21994057 -:1021740024EA01010162016A9A401143016210BD1F -:1021840008B5134B984207D14FF40010012100F019 -:102194004FFA4FF4001014E00E4B984207D14FF45D -:1021A4008000012100F044FA4FF4800009E00A4B5A -:1021B40098420BD14FF40000012100F039FA4FF49A -:1021C40000000021BDE8084000F032BA08BD00BF9D -:1021D4000054004000580040005C00407FB58688F1 -:1021E40026F03F060446360468460D46360C00F0D9 -:1021F40075F9029A2C48B2FBF0F081B20E43A68026 -:1022040023882A4E23F001031B041B0C23802B6814 -:10221400B3420AD85B000131B2FBF3F39BB2032B48 -:1022240089B298BF0423218423E0EE884BF6FF7122 -:102234008E421BBF19214B4303EB4303B2FBF3F361 -:1022440015BF9BB2B2FBF3F343F480439BB2C3F3D9 -:102254000B020AB943F001034FF4967101FB00F23B -:102264004FF47A7192FBF1F2013292B243F40043DB -:102274002284A38323886989AA889BB243F001033B -:102284002380238823F4816323F002031B040A437D -:102294001B0C13439BB223802A89AB8913439BB243 -:1022A400238104B070BD00BF40420F00A08601002E -:1022B40041F288330360002383804BF6FF7203816D -:1022C40043814FF48043C28083817047038819B1EE -:1022D4009BB243F0010303E023F001031B041B0C36 -:1022E40003807047038819B19BB243F4806303E011 -:1022F40023F480631B041B0C03807047838A9AB207 -:10230400038B10B542EA034321F07F4404EA03003F -:10231400431A5842584110BD014B1860704700BF22 -:1023240000300040014B5860704700BF003000404F -:10233400014B9860704700BF00300040014B1860AB -:10234400704700BF20000E4210B9034B196070475C -:10235400024B1960704700BFA0000E429C000E4261 -:10236400034B5B68184214BF01200020704700BF74 -:1023740000700040024A136843EA8000106070470E -:10238400007000400023036043608360C360036106 -:10239400436183617047002343608360C3600360CB -:1023A400036143618361C361036243628362C36205 -:1023B400704700000E4903680A6810B544691C435D -:1023C40083691C4322F07F4323F030034268234394 -:1023D40043EA02630B60C36882684C681A43054B86 -:1023E40023401343026943EA02434B6010BD00BF1C -:1023F400001000A0FEF7E0FF90E80C001A43836889 -:102404000D491A43C3681A4303691A4383691A437B -:10241400C3691A43036A1A43436A1A43836A1A4311 -:10242400C36A10B51A434C69044B234013434269F1 -:1024340043EA82434B6110BD001000A0000080906D -:10244400044B1A6810B142F0010201E022F00102CB -:102454001A607047001000A0024B9A68920658BF99 -:1024640098617047001000A0024B9A68920658BF0A -:1024740018617047001000A0014B1878704700BF26 -:10248400201000A0034B9B68184214BF01200020B9 -:10249400704700BF001000A0014BD860704700BF18 -:1024A400001000A00B4B1A68002142F001021A60D0 -:1024B40099601A6822F0A85222F410221A60064A7F -:1024C4005A601A6822F480221A60D960C3F88C100A -:1024D400704700BF00380240103000242C4B9A682B -:1024E40002F00C0210B50C2A38D8DFE802F03737B6 -:1024F400373707373737093737371E00254B2EE074 -:102504005968234A5B6811F4800F03F03F03516854 -:1025140014BF204A204AB2FBF3F31D4A5268C1F3A8 -:1025240088114B43C2F3014213E05968184A5B68AF -:1025340011F4800F03F03F03516814BF154A164A83 -:10254400B2FBF3F3124A5268C1F388114B43C2F34E -:10255400027201325200B3FBF2F300E00E4B0C4A5C -:10256400036093680D49C3F30313CC5C0368E34031 -:1025740043609468C4F382240C5D23FA04F48460F9 -:102584009268C2F342328A5CD340C36010BD00BF7C -:102594000038024000127A000024F4001E000020DB -:1025A400044B9A6B09B1104301E022EA00009863DE -:1025B400704700BF00380240044B1A6C09B1104345 -:1025C40001E022EA00001864704700BF00380240AE -:1025D400044B5A6C09B1104301E022EA000058642C -:1025E400704700BF00380240044B1A6909B1104318 -:1025F40001E022EA00001861704700BF0038024081 -:10260400044B5A6909B1104301E022EA0000586101 -:10261400704700BF00380240044B9A6909B1104367 -:1026240001E022EA00009861704700BF00380240D0 -:10263400044B1A6A09B1104301E022EA000018624F -:10264400704700BF00380240044B5A6A09B1104376 -:1026540001E022EA00005862704700BF00380240DF -:102664004209012A074B01D11B6803E0022A0CBF6F -:102674001B6F5B6F00F01F0023FA00F000F00100F5 -:10268400704700BF00380240024A536F43F0807322 -:10269400536770470038024082B000230193054B12 -:1026A4000193019B03EB80000190019B196002B030 -:1026B400704700BF5028004082B000230193054BAF -:1026C4000193019B03EB80000190019B186802B009 -:1026D400704700BF5028004008B5254B984207D1E9 -:1026E4004FF480500121FFF7AFFF4FF4805039E0E1 -:1026F400204B984207D14FF480400121FFF798FF07 -:102704004FF4804009E01C4B98420BD14FF4004039 -:102714000121FFF78DFF4FF400400021BDE8084080 -:10272400FFF786BF154B984207D14FF400500121A3 -:10273400FFF78AFF4FF4005014E0114B984207D181 -:102744004FF480100121FFF77FFF4FF4801009E060 -:102754000C4B98420BD14FF400100121FFF774FF8A -:102764004FF400100021BDE80840FFF76DBF08BD1D -:102774000030014000380040003C0040003401407B -:10278400005001400054014003884A8810B503F406 -:1027940041540B8813438A881343CA8813430A8914 -:1027A40013434A8913438A891343CA89134323432E -:1027B4009BB20380838B23F400631B041B0C838371 -:1027C4000B8A038210BD038819B19BB243F0400306 -:1027D40003E023F040031B041B0C0380704781813A -:1027E40070470389194214BF01200020704700007C -:1027F4007FB5038ACA889BB223F4405313430382F0 -:1028040083890E469BB223F4B053098923F00C024A -:10281400B3880B4371890B439BB213438381838A2F -:10282400B2899BB223F440731343838204466846FF -:10283400FFF754FE1A4B9C4203D003F580639C427D -:1028440001D1039D00E0029DA38931681BB2002BD6 -:102854004FF0190202FB05F2B4BF4D008D00B2FB2C -:10286400F5F5A2896426B5FBF6F00001010912B260 -:10287400002A06FB1153ADBF1A01D900323132329E -:10288400B5BFB1FBF6F2B2FBF6F302F0070203F0B8 -:102894000F03B4BF1043184380B2208104B070BD4D -:1028A40000100140838919B19BB243F4005303E043 -:1028B40023F400531B041B0C83817047C1F30801EC -:1028C400818070470388194214BF0120002070479B -:1028D40010B5431E0A44914204D011F8014B03F889 -:1028E400014FF8E710BD02440346934202D003F8B7 -:1028F400011BFAE770470000E131000800080240BC -:1029040000200000020D0000E6310008000C024027 -:102914000400000003020200E9310008001C024028 -:102924000100000007000200F0310008001C024012 -:102934000200000007010200000000000000000087 -:1029440001424F4F544C4F41444552000000000097 -:10295400002A2A000000000000000000000000001F -:1029640000006000000300180000000000000000E8 -:102974006000000300180000000000000000600078 -:1029840000030018000000000000000060000003C5 -:1029940000180000000000000000600000030018A0 -:1029A40000000000E003001F60F800C30718C00720 -:1029B4000000F80FC07F60FE03F31F18F01F000033 -:1029C4000C1860C06003061B301818300000063075 -:1029D4003080E1010C0F60180C60000003601800E7 -:1029E400E3001807C01806C0008001400C00620014 -:1029F400100380180380008001C00C0066003003BF -:102A040080190380018001C0FCFF67003003801936 -:102A1400FFFF018001C0FCFF630030038019FFFF4A -:102A2400008001C00C0060003003801903000080A6 -:102A340001C00C00600030038019030000800140D5 -:102A44000C006200100380180380008003601800EB -:102A5400C3001806C01806C000800730308081010A -:102A64000C0C60180C6000800D1860C00003061880 -:102A7400303818300080F90FC07F00FE03F01F705B -:102A8400F01F0080E103001F00F800C00760C007CA -:102A94000080010000000000000000000000008031 -:102AA40001000000000000000000000000800100A0 -:102AB4000000000000000000000000800100000091 -:102AC4000000000000000000008001000000000081 -:102AD40000000000000000000C0006000000006080 -:102AE4000030000018000300000000C000180000BF -:102AF4003080010000000080010C000060C0000074 -:102B04000000000003060000C06000000000000098 -:102B14000603000080310000000000008C0100006A -:102B2400001B000000000000D8000000000E0000A0 -:102B34000000000070000000000E00000000000013 -:102B440070000000001B000000000000D80000001E -:102B540080310000000000008C010000C060000013 -:102B6400000000000603000060C000000000000038 -:102B7400030600003080010000000080010C00000A -:102B840018000300000000C0001800000C0006003C -:102B940000000060003000000000000000000000A1 -:102BA4000000000000000000000000000000000021 -:102BB4000000000000000000000000000000000011 -:102BC4000000000000000000000000000000000001 -:102BD40000000000000000000000000000000000F1 -:102BE40000000000000000000000000000000000E1 -:102BF400000000000000000000000080FFFF010052 -:102C040000000000000000FEFFFF7F000000000045 -:102C14000000E07F0000FE07000000000000FE014D -:102C24000000807F0000000000801F00000000F80A -:102C34000100000000F00100000000800F0000000F -:102C4400007C0000000000003E000000000F0000B7 -:102C540000000000F00000008003000000000000FD -:102C6400C0010000E00000000000000000070000B8 -:102C74007000000000000000000E000018000000BA -:102C840000000000001800000C000000000000001C -:102C9400003000000600000000000000006000009A -:102CA400030000000000000000C0008001000000DC -:102CB40000000000008001C00000000000000000CF -:102CC4000000036000000000000000000000063067 -:102CD400000000000000000000000C1000000000D4 -:102CE400000000000000087C00FE00FF01C701C7CF -:102CF40001C701C701C701C701C701C701C701C790 -:102D040001C701C701FF01FE007C0038003C003E02 -:102D1400003E0038003800380038003800380038E9 -:102D2400003800380038003800FE00FE00FE007C49 -:102D340000FE00FF01C701C701C001C001E000F0AF -:102D44000078003C001E000E000F000700FF01FF8A -:102D540001FF017C00FE00FF01C701C701C001C0E3 -:102D640001F8007800F800C001C001C001C701C724 -:102D740001FF01FE007C00E000E000F000F000F83C -:102D840000F800F800FC00EC00EE00E600FF01FF94 -:102D940001FF01E000E000E000E000FF00FF00FFB1 -:102DA400000700070007007F00FF00FF01C701C004 -:102DB40001C001C701C701C701FF01FE007C007CFF -:102DC40000FE00FF01C701C701070007007700FFED -:102DD40000FF01C701C701C701C701C701FF01FE09 -:102DE400007C00FF01FF01FF01E000E000700070C3 -:102DF40000700038003800380038001C001C001C2B -:102E0400001C001C001C007C00FE00FF01C701C761 -:102E140001C701C701FE007C00FE00C701C701C74E -:102E240001C701C701FF01FE007C007C00FE00FF1A -:102E340001C701C701C701C701C701FF01FE01DCCA -:102E440001C001C001C701C701FF01FE007C0000F1 -:102E5400000000000000007C00FE00FF01C701C765 -:102E640001F001FC01CE01C701C701E701FF01DF49 -:102E740001CE010700070007000700E700F701FF84 -:102E840001CF01C701C701C701C701C701C701CFEE -:102E940001FF01F701E70000000000000000007CD2 -:102EA40000FE00FF01C701C7010700070007000774 -:102EB40000C701C701FF01FE007C00C001C001C0C2 -:102EC40001C001CE01DF01FF01E701C701C701C74E -:102ED40001C701C701C701E701FF01DF01CE0100FE -:102EE400000000000000007C00FE00FF01C701C7D5 -:102EF40001C701FF01FF010700C701C701FF01FE70 -:102F0400007C00E000F000F8003800FE00FE00FE47 -:102F140000380038003800380038003800380038ED -:102F2400003800380038000000068301000000006B -:102F340000000000000000068301000000000C00F7 -:102F44000000000000068301000000000C000000E7 -:102F540000D878369B79C0E3D90C8C67DB3C1BF82E -:102F6400FD7EBFFDE0F7FB1FC6EFFB7E1F98CD661D -:102F7400B3CD60369B19C66C18660398FD66B3FD25 -:102F840060309B19C66F18660398FD66B3FD603008 -:102F94009B19C36F186603980D66B30D60369B19B1 -:102FA400C360186603F8FD7EBFFDECF79B19C36F81 -:102FB400187E03D878369B79CCE399998167183CBD -:102FC4000318000000000000008001000000001849 -:102FD40000000000000000000000000000180000D5 -:102FE4000000000000000000000000FFFFFFFFFFE2 -:102FF400FFFFFFFFFFFFFF010000000000000000D3 -:1030040000008001000000000000000000008001BA -:10301400000000000000000000008001000000002B -:10302400000000000000800100000000000000001B -:10303400000080010000000000000000000080FF8C -:10304400FFFFFFFFFFFFFFFFFFFFFF000004024041 -:103054004000000006000A000004024004000000D2 -:1030640002000900000802400002000009000900F3 -:1030740000080240000400000A00090000080240A1 -:1030840000010000080009000000024002000000E6 -:1030940001000900005C0040000002400001000043 -:1030A40008000400000402400001000008000900B8 -:1030B40000008000801A060000400000484900001B -:1030C400000000000000000001000300820007006F -:1030D4008400070001000300020003004000030015 -:1030E400800007008400070000000008004000087A -:1030F4000080000800C00008000001080000020869 -:1031040000000408000006080000080800000A087F -:1031140000000C0800000E08000008001000180051 -:1031240020002800300038004000480050005800BB -:10313400000000006410B71DC8206E3BAC30D926D7 -:103144009041DC76F4516B6B5861B24D3C71055083 -:103154002083B8ED44930FF0E8A3D6D68CB361CBAB -:10316400B0C2649BD4D2D38678E20AA01CF2BDBD5F -:10317400537475636B20627574746F6E2072656727 -:10318400697374657220697320696E76616C696411 -:103194002C20636C656172696E672E0042757474CD -:1031A4006F6E2069642000697320737475636B21EA -:1031B40000427574746F6E20776173207075736844 -:1031C4006564206F6E20626F6F742E204275747474 -:1031D4006F6E20636F756E7465723A20004261638E -:1031E4006B0055700053656C65637400446F776EB3 -:1031F400002E2E2F7372632F647269766572732F9B -:103204006932632E630049324320646576696365DD -:10321400204944206F7574206F6620626F756E6458 -:103224007320256420286D61783A20256429006F75 -:103234006E002E2E2F7372632F647269766572731B -:103244002F706D69632F617333373031622E6300E1 -:103254004661696C656420746F20737461727420B4 -:10326400504D494320313230487A2050574D002088 -:103274006973206F757473696465207379737465F9 -:103284006D20666C617368006661696C65642074A6 -:103294006F20657261736520736563746F722000BB -:1032A4006661696C656420746F2077726974652047 -:1032B400616464726573732000496E76616C69643D -:1032C400206669726D7761726520646573637269E3 -:1032D4007074696F6E2100436865636B73756D6DFF -:1032E400696E67206669726D7761726520757064B6 -:1032F4006174650043616C63756C6174656420631B -:103304006865636B73756D3A2000496E76616C690C -:1033140064206669726D7761726520435243206947 -:103324006E2053504920666C617368210065726198 -:1033340073655F6F6C645F6669726D776172650057 -:1033440077726974655F6E65775F6669726D7761C0 -:10335400726500576527726520646561640043687F -:1033640065636B73756D6D696E6720002062797497 -:1033740065730D0A00436865636B73756D202D20BA -:1033840077616E746564200020676F7420004F7548 -:103394007220696E7465726E616C20666C6173680C -:1033A40020636F6E74656E74732061726520626150 -:1033B400642028636865636B73756D206661696C4E -:1033C40065642921205468697320697320726561DA -:1033D4006C6C792062616421004F75722070726593 -:1033E40076696F7573206669726D77617265207591 -:1033F4007064617465206661696C65642C20616227 -:103404006F7274696E67207570646174652E004E06 -:103414006577206669726D776172652069732061D2 -:103424007661696C61626C6521004C6F6164696EE0 -:1034340067207265636F76657279206669726D774D -:10344400617265004661696C656420746F206C6FFD -:103454006164207265636F76657279206669726D46 -:10346400776172652C20737472696B65206F6E6569 -:103474002E2054727920616761696E2E004661695D -:103484006C656420746F206C6F6164207265636F77 -:1034940076657279206669726D776172652C207326 -:1034A4007472696B652074776F2E20547279206171 -:1034B4006761696E2E004661696C656420746F20D3 -:1034C4006C6F6164207265636F76657279206669DA -:1034D400726D776172652C20737472696B652074E8 -:1034E400687265652E205341442057415443480077 -:1034F40048415244204641554C5400657869742033 -:103504007374616E64627900205F5F5F20205F20C6 -:10351400205F20205F202020002F205F5F3E3C5F43 -:103524003E7C207C7C207C5F5F005C5F5F205C7C59 -:10353400207C7C207C7C202F202F003C5F5F5F2F31 -:103544007C5F7C7C5F7C7C5F5C5F5C0069326320B9 -:10355400696E6974656400706D696320696E69746D -:10356400656400626F6F7420626974004C617374E7 -:10357400206669726D7761726520626F6F7420775F -:10358400617320737461626C653B20636C65617266 -:1035940020737472696B657300504D494320776FD3 -:1035A4006B652066726F6D207374616E646279203E -:1035B40064756520746F2063686172676572206446 -:1035C4006973636F6E6E6563740050757474696EAD -:1035D4006720504D4943206261636B20696E746FAC -:1035E400207374616E64627900486F6C6420646F48 -:1035F400776E205550202B204241434B202B2053E3 -:10360400454C45435420666F7220352073656373BF -:103614002E20746F20666F7263652D626F6F742045 -:10362400505246004669726D776172652069732055 -:10363400657261736564005761746368646F6720C1 -:1036440063617573656420612072657365740053EA -:103654006F667477617265206661696C7572652046 -:1036640063617573656420612072657365740046D7 -:1036740061696C656420746F207374617274206670 -:1036840069726D776172652C20737472696B652041 -:1036940074687265652E004661696C656420746F98 -:1036A400207374617274206669726D776172652C1F -:1036B40020737472696B652074776F2E004661699C -:1036C4006C656420746F207374617274206669720F -:1036D4006D776172652C20737472696B65206F6EEF -:1036E400652E0053687574646F776E20726571750A -:1036F40065737465642E0052656D6F76652063682A -:10370400617267657220746F2073687574646F7773 -:103714006E005368757474696E6720646F776E2EDB -:1037240000426F6F74696E67206669726D776172AB -:1037340065204020002E2E2E0D0A0D0A00466F72C1 -:1037440063652D626F6F74696E67207265636F764F -:10375400657279206D6F64652E2E2E00426F6F7432 -:1037640020626974733A20004153534552543A20FD -:10377400003A004153534552540041535345525467 -:103784004E002A2A2A20575446200053544D3332DF -:103794000053544D333220706572697068657261EC -:1037A4006C206C69627261727920747269707065E0 -:1037B4006420616E206173736572740043524F41DB -:1037C4004B204F4F4D0069746F61206275666665CA -:0C37D4007220746F6F20736D616C6C00CC -:1037E000FFFFFFFF008400000202000000C0040190 -:1037F00000000000000000021000000007000000B0 -:103800000000010203040102030406070809000086 -:04000005080001846A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_snowy_bb@1478015115.hex b/bin/boot/nowatchdog_boot_snowy_bb@1478015115.hex deleted file mode 100644 index 57d3da92da..0000000000 --- a/bin/boot/nowatchdog_boot_snowy_bb@1478015115.hex +++ /dev/null @@ -1,869 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008A51A0008B8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0DBFF01F093FC70471F -:1001E400E83500080000002014000020140000205E -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040001FC002401902546E0B200F049FC01AA5B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F07DFF02A800F00A -:100234002DFE0120002102F0D5FB00201AE001333D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F061FF02A800F011FE0120214602F016 -:10027400B9FB28460BB030BD082E0008342E000808 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0A1F9003018BF9B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F09AF9A1 -:1002C40020460E21052208AE02F094F905222046AC -:1002D4000C2102F08FF94FF4005346F81C3D002521 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F02FF94FF427 -:10030400804320463146019302F028F94FF480538C -:100314002046314601934FF4007802F01FF920463D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340015F94FF480632046314601938DF8085037 -:100344008DF80B5002F00AF94FF4807320463146C1 -:1003540001938DF808708DF80B5002F0FFF84FF4FC -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F0F4F839464FF4001002F0D7FAA4F56D -:100384004444164B164E1E60204602F04BFB03A855 -:1003940002F0BFFB4FF4827339463046ADF80E309D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F086FB2046394602F0ADFB28 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F087FE012000F08B -:1003F40045FB022001F081FE0120BDE8084000F029 -:100404002BBB000010B504460548022102F09EFBF8 -:100414000028F9D021460248BDE8104002F094BB00 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0DAF8012001F05FFE25484FF4E4 -:100444000041002202F0D1F84FF4807100222148CB -:1004540002F0CBF8012001F050FE4FF400411D489A -:10046400012202F0C2F8012001F047FE19484FF4BE -:10047400007102F0B4F818B11748184A6A210AE06A -:1004840014484FF4004102F0AAF808B1254404E0EE -:100494001148134A6B2101F0A3FDAC4204D014F8B7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F09BF808240020FFF7A4FF013CFAD1C6 -:1004C4004FF40071034802F08AF8003018BF01208D -:1004D40038BD00BF001802404D2E0008572E0008FA -:1004E400722E000810B504460B4802F02AFB0A4895 -:1004F400022102F02BFB0028F9D00748214602F024 -:1005040023FB0548012102F021FB0028F9D0024811 -:1005140002F017FBC0B210BD0054014010B54FF4F7 -:10052400807104460022054802F05FF8642001F05F -:10053400DFFD2046BDE81040FFF764BF001802400D -:1005440008B50748802102F001FB0028F9D10548CD -:100554004FF480710122BDE8084002F046B800BFA4 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0B4FDF1E7012010BD00BFEF -:100594008F2E000810B5224C638822884FF6FF7115 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F009F84FF40041002215480D -:1005E40002F003F8012001F088FD12484FF40041A5 -:1005F400012201F0FAFF40F2E9340E484FF4007191 -:1006040001F0EDFF0028D9D1013C04D10A4800F0E3 -:100614003DFC204610BD642001F06AFDEDE700BFFB -:1006240000000F60B22E000804000F60DE2E0008E8 -:10063400FA2E000800180240102F000810B50446D6 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0A2FF642001F022FD0020FFF7A2 -:1006B40019FF4FF48071012204461A4801F095FF96 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF2C2F0008BD -:100724005B2F000800180240843500086A2F000877 -:10073400852F00088F2F000808B55D235843094B07 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F054BAF5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0D1FE3A4804210122B5 -:1007C40001F013FF00240223029435488DF8083009 -:1007D400072103230C228DF80930019401F00AFF4C -:1007E40080232F480193294601F0B8FE022C04D03F -:1007F4002C48A1B20C2201F0FDFE0134102CF5D1DD -:100804004FF6FB7327480193294601F0A7FE022601 -:100814003146013624480C22B6B201F0EBFE102E0C -:10082400F6D14FF6FC7329461F48019301F096FE5A -:10083400042100221B481E4D01F0D7FE0A2001F0BE -:1008440057FC01220421174801F0CFFE1E2001F0BD -:100854004FFC0120014602F051F80FCD03AC0FC448 -:1008640095E8070084E80700402200210AA802F066 -:10087400EEF902230B9308230C934FF48053129345 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400EBFD0020012101F051FE1AB070BD00BF34 -:1008A40000040240000C024000100240642C0008C6 -:1008B4006C2C00080D4B98221A80A3F68A231B88FF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F076FBF7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0C9FB11E05AB2012A0CDCFF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0CAFD1A480621042201F0FF -:1009D40011FE04220921174801F00CFE154800F00D -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0ABFD0C4800F072FA042000F03E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0BEFD20465D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400ACFD2046BDE8384000F02ABA00140240FC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F093FD0446284600F018FADF -:100AC400B4FA84F0400938BD882C000838B50024F5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F01BFF04F140076568284600F021 -:100B0400DFF9684601F062FD02238DF80530A37B0E -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F01AFD284600F0E1F9BC42DB -:100B3400E3D14FF48040314601F0F8FE03B0F0BD3C -:100B4400882C000808B5054B1B781BB904483221D2 -:100B540001F052FABDE8084001F0C6BB1A000020BB -:100B6400C22F0008124B1B7870B505460C461BB902 -:100B74001048382101F040FA032906D98E0831467D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F09ABBBDE8704001F0AABBFB -:100BB4001A000020C22F0008024B1A780AB9012239 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F010FA01214FF4805059707D -:100BE40001F080FEBDE8084001F072BB1A0000204D -:100BF400C22F000808B5074B1A781AB906485121C4 -:100C040001F0FAF9002159704FF48050BDE8084012 -:100C140001F068BE1A000020C22F000870B50C460F -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0C7FF002884 -:100CA400F9D00648214601F0BDFF0448402101F077 -:100CB400BDFF0028F9D010BD1C00002000480040F2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F087FF31464A460020A1 -:100D340001F0BEF907F1080304F5927443F8040DB9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64007FF904465520FFF791FF6E466C44A64276 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020D02C000824000020B3 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0AEFD20460A21072201F026 -:100DC40019FC06AE072220460B2101F013FC4FF458 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0B3FB4FF40063204631460093B5 -:100E04008DF8048001F0AAFBADF80C50ADF80E503B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F096FE284639468B -:100E340001F0ECFE204600F05BF8054B1F7006B095 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4003AF901A8FFF7CEFF05B05DF804FB000086 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F0F6FA5D -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0B5F842 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BDF02C000849 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0BFFA2046D8 -:100FE40031462A4601F006FB2046FFF781FF03B095 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0BCFB00287BD0CF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BDF02C000808 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0DCFB684601F096 -:10116400F3FA0F4A236A009393424FF48063ADF875 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F075FA2859012101F03D -:10119400E9FA04B070BD00BF30010020F02C000853 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0CAF92046B1B22A4601F061 -:1011D4000CFA2046FFF78CFE02B070BD10B5094C26 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BDF02C00081C23104A434370B515 -:1012140005460F4CD058D61801F0FEF9342305FBCF -:1012240003440021E06901F075FB337923B92846B2 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400F02C00082DE9F347504C9A4694F83830A6 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0BEFE494B1844DF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F05BFA2AE03046FFF79DFF30462A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0D1FEDBE719688B8823F4EB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A400C82F0008F02C000880841E00017D03680B -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F0F2FE17 -:1014840073788DF8043001A800F0ECFE54F8040CD5 -:1014940001F0C2F8454504F11C0406F1340601D0FC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020182D0008300100201F -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F090FD042807D90522009250 -:1014E4000D480E4A40F2351100F07AFD0C4A1344BF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020C82F0008CE2F000893 -:10152400F02C0008104A08B592F838301BB90F485F -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F05AFD0A4B18441C2390F8880003FB52 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD30010020C82F000815 -:10157400F02C000837B50D4614460093069B0193E2 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F027F90DB99A -:10161400029805E0012D01D1039801E000F0F6FCE9 -:10162400B4EB500F0ED8B0FBF4F000F070FD0028BE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0DAFC01200138C00080B205B030BDE2 -:10165400FA2F0008B0F1006F10B504460BD20C4805 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:101694007230000800300008802D00082DE9F8435E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F01AFE2C464C451FDCF32000F02AFEB5 -:10170400154B002133F8140000F05CFE09280BD0BF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F00FFE0DE02FB1C5F10100204420 -:1017340031464246B8470134DDE700F003FE01209C -:10174400BDE8F8830020BDE8F88300BF1B30000823 -:101754002F30000832300008B02D00083630000861 -:10176400843500082DE9F0470646234814460D4603 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400CBFDF32000F0DEFD4FEAD41805EB04097D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F03DFE09280DD01248FFF74AFB3B -:1017C4003046FFF771FB1048FFF760FB00F0BAFDED -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0AAFD52 -:1017F4000120BDE8F08700BF4E3000082F300008FC -:1018040032300008623000088435000808B545F21B -:10181400555000F0CDFF042000F0D0FF40F6FF70DB -:1018240000F0D2FF002000F0C3FF4FF480500121EC -:10183400BDE8084000F06CBD08B57D2001F0BEF89D -:10184400003018BF012008BD4900FEF775BF4B08E2 -:1018540003EB5000FEF770BF70B586B00546FFF786 -:10186400ABF901210020FEF767FF042000F01EFB06 -:1018740003A8294600F066FB03A800F072FB08B930 -:1018840028480BE02848FFF701FB05F10C000499F8 -:10189400FFF7D9F9059B984204D02448FFF7F6FADC -:1018A40001203DE0049C2248FFF7F0FA2146214A3A -:1018B40021480023FFF7F2FE049E2048FFF7E6FAD2 -:1018C400002405F1C0411E4B009432460C311A48E5 -:1018D400FFF748FF1B48FFF7BDFA0498FFF7E4FA47 -:1018E4001948FFF7B7FA04991348FFF797F9054623 -:1018F4001648FFF7AFFA0598FFF7D6FA1448FFF732 -:10190400A9FA2846FFF7D0FA1248FFF7BFFA059B59 -:101914009D4204D01048FFF7B9FA022000E02046A7 -:1019240006B070BD7B30000899300008B63000085E -:10193400D93000084D18000800400008EC300008B9 -:1019440053180008FF3000080D3100081631000854 -:1019540029310008843500082F31000808B5022019 -:1019640000F0BCFAD8B3042000F0B8FA70B11C48F7 -:10197400FFF78CFA042000F0A5FA022000F0A2FA86 -:101984004FF40040BDE8084000F09CBA1548FFF74A -:101994007DFA082000F096FA102000F093FA202037 -:1019A40000F090FA402000F08DFA4FF48010FFF719 -:1019B40053FF022807D1082000F078FA102000F025 -:1019C40075FA00F045FB042000F07CFA022000F0D8 -:1019D40079FA4FF40040BDE8084000F067BA08BD4A -:1019E4007A310008B031000810B52548FFF74EFAE7 -:1019F4004FF40010FFF730FF10B102283AD809E085 -:101A0400202000F05FFA402000F05CFA802000F013 -:101A14004DFA2FE0082000F049FA102000F046FAB1 -:101A2400202000F05BFA20B91648FFF72FFA202097 -:101A340007E0402000F052FA58B91348FFF726FA9D -:101A4400402000F033FA4FF4007000F02FFA00F059 -:101A5400FFFA0E48FFF71AFA082000F033FA1020B4 -:101A640000F030FA202000F02DFA402000F02AFA8D -:101A7400002400E00124042000F024FA204610BDD4 -:101A8400CB310008E53100081E3200085732000847 -:101A940008B50248FFF7FAF900BEFEE791320008E4 -:101AA4001EF0040F0CBFEFF30880EFF30980FFF77B -:101AB400EFBF70471FB504461048FFF7E7F901A9C7 -:101AC4000C22204600F02FFB01A8FFF7DFF9204687 -:101AD400FEF74AFEFEF7FAFF094B20F004001870E7 -:101AE4001C46FEF7F3FF237800F0FB00834201D08D -:101AF40000F0AEFA0A2000F000FBF2E7873200089B -:101B0400690100207FB54FF08050012100F002FFF1 -:101B1400022000F065FE012807D1022000F06AFED1 -:101B2400BB48FFF7B3F900F098FA00214FF080505A -:101B340000F0F0FEFFF734F9B648FFF7A7F9B6480E -:101B4400FFF7A4F9B548FFF7A1F9B548FFF79EF9E7 -:101B5400B448FFF79BF9B448FFF798F9B348FFF787 -:101B640095F9B348FFF792F9B248FFF78FF9B248F5 -:101B7400FFF78CF9FFF760FCFEF710FF00F0B8F9EF -:101B840000F0CEF900210C2201A801F060F800F069 -:101B9400C3F901A90C2200F0C6FA01A8FFF776F9EF -:101BA4009C48FFF773F99B48FFF770F94FF48040A6 -:101BB40000F094F990B1A148FFF768F94FF4804020 -:101BC40000F080F9082000F07DF9102000F07AF987 -:101BD400202000F077F9402000F074F9FEF7D8FDDA -:101BE400FEF782FFFEF74EFDFEF706FB08B19448B0 -:101BF40003E0FEF745FB10B19248FFF75BFF00F0EE -:101C04008AF9802000F06AF918B1802000F05AF9AE -:101C140056E04FF4003000F061F9044620B14FF46F -:101C2400003000F04FF94DE00120FEF739FFB0B16C -:101C34002046FEF735FF90B18348FFF727F941F2BC -:101C440088340120FEF72CFF98B10020FEF728FF0E -:101C540078B1012000F051FA013CF2D132E07B4B23 -:101C64000CCB013301D0013203D17948FFF70EF9CF -:101C740028E0FFF7E1FD48B3FFF7DEFD10B175483A -:101C8400FFF704F94FF4007000F028F910B172481E -:101C9400FFF7FCF84FF4007000F014F9102000F086 -:101CA4001DF9002800F0EE806C48FFF7EFF80820DB -:101CB40000F008F9102000F005F9FFF795FE98B937 -:101CC40067489AE76748FFF7E1F8F6E74FF40070D2 -:101CD40000F004F90028CFD1082000F0F3F8102018 -:101CE40000F0F0F8FFF73AFE4FF4005000F0F6F879 -:101CF40004464FF4805000F0F1F8400040EA8400BC -:101D0400C4B24FF4006000F0E9F82043C0B20728E1 -:101D1400C3B20DD14FF4006000F0D4F84FF48050FA -:101D240000F0D0F84FF4005000F0CCF84E4864E7CF -:101D34000133DBB2023B4FF40060052B11D8DFE81E -:101D440003F00C1003100C1000F0BCF84FF480509A -:101D540000F0B8F84FF4005003E000F0B3F84FF48B -:101D6400805000F0A3F8FFF751FD384B3F485D6801 -:101D74001E68FFF76FF82846FFF796F83C48FFF710 -:101D840069F80022930003F1604303F561430132D3 -:101D94004FF0FF31082AC3F88010C3F88011F1D145 -:101DA400344B4FF4801200241A635C639C631C64FC -:101DB4005C6400F02FFD3048012100F0C3FD214692 -:101DC4002D4800F0BFFDF120012100F0C7FD2146A0 -:101DD400F12000F0C3FD0120014600F0CBFD2146B7 -:101DE400012000F0C7FD2548012100F0CFFD214668 -:101DF400224800F0CBFD2248012100F0D3FD204809 -:101E0400214600F0CFFD63B64FF0FF3EB5462847AC -:101E14009C32000884350008A9320008C03200084A -:101E2400D932000807330008363300086633000847 -:101E340097330008C9330008FB3300082C3400082A -:101E4400014550FE024550FE593400080040000888 -:101E54008B3400089E340008B6340008D6340008D9 -:101E6400034550FE4A350008044550FE6935000814 -:101E74007D350008003802400010E022FFC9FEF65C -:101E8400337F7704082000F029F820B10548FEF7D5 -:101E9400FDFF102003E00448FEF7F8FF082000F0DF -:101EA40005F81FE7FE3400082435000810B5044681 -:101EB400002000F0A7FD40EA04010020BDE8104026 -:101EC40000F090BD10B50446002000F09BFD20EA10 -:101ED40004010020BDE8104000F084BD10B50446A4 -:101EE400002000F08FFD204214BF0120002010BD0F -:101EF40008B54FF08050012100F00CFD012000F0E6 -:101F040069FC0120FFF7EAFF20B90121BDE8084080 -:101F140000F068BD08BD7047022000F073BD0000EA -:101F240008B5FFF7F9FF054B1968884204D0022071 -:101F3400BDE8084000F056BD08BD00BF840100089C -:101F440000BEFDE71FB504460C2201A8FEF71CFCE9 -:101F540001AB03CB20601868A0602046616004B028 -:101F640010BD0068A0F10C035842584170470000AE -:101F740080B50646174610480D461C46FEF76AFF14 -:101F84003846FEF767FF0D48FEF764FF3046FEF75C -:101F940061FF0B48FEF75EFF2846FEF785FF2CB174 -:101FA4000848FEF757FF2046FEF754FF0648FEF7A1 -:101FB4006DFFFFF7C5FF00BF853500088E350008AB -:101FC400913500088F350008843500081FB506AA2E -:101FD40052F8044B039200921A462346FFF7C8FFB7 -:101FE4000CB41FB506AA52F8043B03920092014AAE -:101FF400FFF7BEFF9335000807B500230093724630 -:10200400014BFFF7E3FF00BF9A350008744608B59B -:102014000548FEF71FFF2046FEF746FF0348FEF77C -:1020240035FFFFF78DFF00BFA23500088435000897 -:10203400BFF34F8F0449054BCA6802F4E0621343AF -:10204400CB60BFF34F8FFEE700ED00E00400FA051C -:1020540008B5FEF791FBFFF7EBFF08B5FFF7E8FFC4 -:10206400F0B50646002401202546034694421DD0BF -:1020740011F804C000F1010EBCF1000F03D1735537 -:10208400774605460DE00133DBB2FF2B774606F8B1 -:1020940000C007D102F1FF3C644506D07355871C8C -:1020A4007546012301343846E0E770467355F0BDA8 -:1020B40030B5C9B1C0430A44914213D011F8013B71 -:1020C4000A4D83EA000404F00F0455F8244084EA1E -:1020D400101080EA131303F00F0355F8233083EA3A -:1020E4001010E9E7C04330BD084630BDC82D0008D4 -:1020F400162358430138FDD1704710B504462CB15E -:102104004FF47A70FFF7F4FF013CF8E710BD10B507 -:10211400B0FA80F400F027F801280CBFC4F11F00C6 -:10212400C4F1200010BD0A2A10B51BDD0C46302373 -:1021340004F8023B78234B701C220F239340034086 -:10214400D340092B01D8303302E00F2B02D8573388 -:10215400DBB200E02023043A04F8013B131DECD168 -:1021640000238B7210BD00F0AA33A0EB530000F0E3 -:10217400CC3300F0333000EB930000EB101000F090 -:102184000F3000EB102000EB104000F03F007047D0 -:102194002D4AD2F8883043F47003C2F888302B4BB0 -:1021A4001A68002142F001021A6099601A6822F04C -:1021B400847222F480321A60254A5A601A6822F422 -:1021C40080221A60D9601A6C224942F080521A6443 -:1021D4000A6842F440420A609A689A609A689A606F -:1021E4009A6842F480529A601B4A5A601A6842F014 -:1021F40080721A601968154A8901FBD5174B40F2A1 -:1022040003611960936823F003039360936843F0B8 -:10221400020393600D4B9A6802F00C02082AF9D16C -:102224009A6822F400029A600D4AC3F884201A685E -:1022340042F080621A60054B1B681B01FBD5024B00 -:102244004FF000629A60704700ED00E000380240F1 -:1022540010300024007000401040010F003C024088 -:1022640000300050C278037810B512B3164AD16812 -:102274004278C943C1F30221C1F10404E4B2A2408B -:10228400D4B20F220A4181780A40224303F1604309 -:1022940003F561431201D2B283F8002303780122CB -:1022A400590903F01F0302FA03F3084A42F82130E4 -:1022B40010BD5A09012103F01F03994002F12003C4 -:1022C400024A42F8231010BD00ED00E000E100E0F6 -:1022D400014B01229A60704700300240014B1860A4 -:1022E400186870470030024000EB81018842044BBB -:1022F40003D050F8042B1A60F8E71868704700BF41 -:1023040000300240014B1868704700BF00300240A3 -:10231400044B9A6809B1104301E022EA0000986076 -:10232400704700BF002004E0044B1A69002ABFBFB5 -:10233400034A5A6002F188325A607047003C0240F6 -:1023440023016745024A136943F000431361704750 -:10235400003C0240014BD860704700BF003C024083 -:102364000E4BDA68D20310D4DA68D1060FD4DA68D7 -:10237400D2050ED4DA6812F0EF0F0CD1DB6813F03B -:10238400020F14BF0820092070470120704706205F -:102394007047022070470720704700BF003C02408E -:1023A40007B509238DF80730FFF7DAFF8DF807002A -:1023B4009DF80730012BF7D09DF8070003B05DF8B6 -:1023C40004FB000070B5064641B1012908D002297A -:1023D4000CBF4FF400754FF4407503E00D4601E067 -:1023E4004FF48075FFF7DCFF09281ED10F4C2369D9 -:1023F40023F440732361216929432161236923F074 -:10240400F8032361236943F002031E432661236911 -:1024140043F480332361FFF7C3FF236923F00203EE -:102424002361236923F0F803236170BD003C02405B -:1024340070B505460E46FFF7B3FF092811D1094CC4 -:10244400236923F44073236123692361236943F0DF -:10245400010323612E70FFF7A3FF236923F0010317 -:10246400236170BD003C024070B543688668856A8C -:102474000468416B46EA0302C3681A4303691A43BA -:1024840043691A4383691A43C3691A43036A1A43A3 -:10249400436A1343C26A2B431A43036B1343A200D8 -:1024A40002F120420B43082E136002BF136843F06D -:1024B40040031360B1F5801F816B12D18CB14FF0D2 -:1024C40020435C681E6846F480161E601E6846F44D -:1024D40080761E600E6924F47004013E44EA0654BA -:1024E4005C608C690B6823434C6843EA04138C6872 -:1024F40043EA0423CC6843EA04430C69496943EA88 -:10250400045343EA0163B5F5804F536010D1C16BA6 -:1025140088690B680343486843EA0013886843EA00 -:1025240000230869496943EA005343EA016301E06F -:102534006FF07043C2F8043170BD0000800000F1F8 -:10254400204019B1036843F0010302E00268024B22 -:1025540013400360704700BFFEFF0F00F0B50E6824 -:1025640000220123934003EA060E9E452AD155001A -:1025740003230468AB40DB431C4004600C79076808 -:1025840004FA05FC013C4CEA0707012C076011D84A -:1025940084684F791C4084608468AF402743876017 -:1025A400446824EA0E0444608C7947689440A4B2D9 -:1025B4003C434460C4682340C360CB79C468AB40E7 -:1025C4002343C3600132102ACBD1F0BD4FF6FF7311 -:1025D40003600023037143718371C37170470369FE -:1025E400194214BF0120002070470AB101837047CB -:1025F4004183704701F00703C90800EB810010B55F -:102604009B00046A0F21994024EA01010162016AD6 -:102614009A401143016210BD08B5134B984207D18B -:102624004FF40010012100F0B1F94FF4001014E050 -:102634000E4B984207D14FF48000012100F0A6F917 -:102644004FF4800009E00A4B98420BD14FF400008C -:10265400012100F09BF94FF400000021BDE808407F -:1026640000F094B908BD00BF005400400058004079 -:10267400005C00407FB5868826F03F060446360499 -:1026840068460D46360C00F0E9F8029A2C48B2FB75 -:10269400F0F081B20E43A68023882A4E23F0010372 -:1026A4001B041B0C23802B68B3420AD85B00013146 -:1026B400B2FBF3F39BB2032B89B298BF04232184AA -:1026C40023E0EE884BF6FF718E421BBF19214B436A -:1026D40003EB4303B2FBF3F315BF9BB2B2FBF3F37B -:1026E40043F480439BB2C3F30B020AB943F00103E2 -:1026F4004FF4967101FB00F24FF47A7192FBF1F200 -:10270400013292B243F400432284A383238869896B -:10271400AA889BB243F001032380238823F48163B6 -:1027240023F002031B040A431B0C13439BB22380B4 -:102734002A89AB8913439BB2238104B070BD00BFC7 -:1027440040420F00A086010041F288330360002359 -:1027540083804BF6FF72038143814FF48043C28030 -:1027640083817047038819B19BB243F0010303E0EE -:1027740023F001031B041B0C03807047038819B169 -:102784009BB243F4806303E023F480631B041B0CBB -:1027940003807047838A9AB2038B10B542EA0343DD -:1027A40021F07F4404EA0300431A5842584110BD03 -:1027B400014B1860704700BF00300040014B586067 -:1027C400704700BF00300040014B9860704700BF65 -:1027D40000300040014B1860704700BF20000E42DB -:1027E400034B5B68184214BF01200020704700BFF0 -:1027F40000700040064BB0F5402F15BF1A685A68A8 -:1028040042EA800042F4402214BF18605A607047C4 -:10281400007000400E4B1A68002142F001021A6059 -:1028240099601A6822F0A85222F410221A60094A08 -:102834005A60094AC3F8842002F18062C3F88820F0 -:102844001A6822F480221A60D960C3F88C10704789 -:102854000038024010300024003000201D4A9368E4 -:1028640003F00C03042B10B503D0082B03D01A4B30 -:1028740018E01A4B16E05168536811F4800F03F006 -:102884003F03516814BF154A134AB2FBF3F3114ACC -:102894005268C2F30142C1F3881101324B43520022 -:1028A400B3FBF2F30B4A036093680D49C3F30313BC -:1028B400CC5C0368E34043609468C4F382240C5DF9 -:1028C40023FA04F484609268C2F342328A5CD340EF -:1028D400C36010BD003802400024F40040787D013C -:1028E40001000020044B1A6B09B1104301E022EAF5 -:1028F40000001863704700BF00380240044B9A6B15 -:1029040009B1104301E022EA00009863704700BF58 -:1029140000380240044B1A6C09B1104301E022EA6A -:1029240000001864704700BF00380240044B5A6C22 -:1029340009B1104301E022EA00005864704700BF67 -:1029440000380240044B1A6909B1104301E022EA3D -:1029540000001861704700BF00380240044B5A69F8 -:1029640009B1104301E022EA00005861704700BF3A -:1029740000380240044B9A6909B1104301E022EA8D -:1029840000009861704700BF00380240044B1A6A87 -:1029940009B1104301E022EA00001862704700BF49 -:1029A40000380240044B5A6A09B1104301E022EA9C -:1029B40000005862704700BF003802404209012AF3 -:1029C400074B01D11B6803E0022A0CBF1B6F5B6F2E -:1029D40000F01F0023FA00F000F00100704700BF70 -:1029E4000038024082B000230193054B0193019B00 -:1029F40003EB80000190019B196002B0704700BF97 -:102A04005028004082B000230193054B0193019BA1 -:102A140003EB80000190019B186802B0704700BF6F -:102A24005028004008B5254B984207D14FF48050F8 -:102A34000121FFF7B7FF4FF4805039E0204B984253 -:102A440007D14FF480400121FFF7A0FF4FF48040ED -:102A540009E01C4B98420BD14FF400400121FFF7D1 -:102A640095FF4FF400400021BDE80840FFF78EBFFA -:102A7400154B984207D14FF400500121FFF792FF04 -:102A84004FF4005014E0114B984207D14FF48010DA -:102A94000121FFF787FF4FF4801009E00C4B9842A7 -:102AA4000BD14FF400100121FFF77CFF4FF400100D -:102AB4000021BDE80840FFF775BF08BD00300140A4 -:102AC40000380040003C0040003401400050014008 -:102AD4000054014003884A8810B503F441540B881C -:102AE40013438A881343CA8813430A8913434A89C0 -:102AF40013438A891343CA89134323439BB2038034 -:102B0400838B23F400631B041B0C83830B8A0382D3 -:102B140010BD0023038043808380C38003814381ED -:102B24008381C381072303827047038819B19BB251 -:102B340043F0400303E023F040031B041B0C038019 -:102B44007047808980B27047818170470389194238 -:102B540014BF0120002070477FB5038ACA889BB246 -:102B640023F440531343038283890E469BB223F418 -:102B7400B053098923F00C02B3880B4371890B43CA -:102B84009BB213438381838AB2899BB223F440733B -:102B94001343838204466846FFF760FE1A4B9C4247 -:102BA40003D003F580639C4201D1039D00E0029DA4 -:102BB400A38931681BB2002B4FF0190202FB05F206 -:102BC400B4BF4D008D00B2FBF5F5A2896426B5FBB8 -:102BD400F6F00001010912B2002A06FB1153ADBF41 -:102BE4001A01D90032313232B5BFB1FBF6F2B2FB71 -:102BF400F6F302F0070203F00F03B4BF10431843C7 -:102C040080B2208104B070BD00100140838919B1E5 -:102C14009BB243F4005303E023F400531B041B0C46 -:102C240083817047C1F3080181807047038819428A -:102C340014BF01200020704710B5431E0A4491427E -:102C440004D011F8014B03F8014FF8E710BD02441A -:102C54000346934202D003F8011BFAE770470000D1 -:102C64000400000001030000010000000100000056 -:102C740003000000010000000F0000000F0000002E -:102C840000000000AE2F00080018024010000000F1 -:102C940006040000B32F00080018024008000000DA -:102CA40006030000B62F00080018024002000000CE -:102CB40006010000BD2F00080018024004000000B7 -:102CC40006020000000000000000000001424F4F17 -:102CD400544C4F414445520000000000002A2A0091 -:102CE400000000000000000000000000005400404C -:102CF40000040240400000000600040000040240FA -:102D0400000200000900040000002000801A0600F0 -:102D1400004000001F200000000000000000000030 -:102D240000580040001402400200000001000400AA -:102D340000140240010000000000040000004000F4 -:102D4400801A0600FFBF00002122000000000000DE -:102D54000116000801000300820007008400070038 -:102D6400010003000200030040000300800007008C -:102D740084000700000101000000000000000008BA -:102D8400004000080080000800C00008000001089E -:102D940000000208000004080000060800000808FB -:102DA40000000A0800000C0800000E0800000800DB -:102DB40010001800200028003000380040004800AF -:102DC40050005800000000006410B71DC8206E3B7E -:102DD400AC30D9269041DC76F4516B6B5861B24D1E -:102DE4003C7105502083B8ED44930FF0E8A3D6D688 -:102DF4008CB361CBB0C2649BD4D2D38678E20AA0F0 -:102E04001CF2BDBD537475636B20627574746F6E70 -:102E140020726567697374657220697320696E76C0 -:102E2400616C69642C20636C656172696E672E0045 -:102E3400427574746F6E206973207075736865646D -:102E440020617420626F6F740069636534306C7044 -:102E54002E630043444F4E45206E6F74206C6F7791 -:102E640020647572696E67207265736574004352DD -:102E740045534554206E6F742068696768206475F3 -:102E840072696E6720726573657400446973706C4F -:102E9400617920627573792D776169742074696D25 -:102EA400656F757420657870697265642100436F7D -:102EB4006E6669677572696E672046504741206681 -:102EC400726F6D2062697473747265616D20696ECE -:102ED40020666C6173682E2E2E004E6F204650477C -:102EE400412062697473747265616D20696E206635 -:102EF4006C6173682E0046616C6C696E6720626158 -:102F0400636B20746F204E56434D2E00465047414C -:102F14002043444F4E452074696D656F7574206578 -:102F240078706972656421004650474120636F6E72 -:102F340066696775726174696F6E206661696C6534 -:102F4400642E204973207468697320612062696764 -:102F5400626F6172643F0046504741207665727328 -:102F6400696F6E3A2000446973706C617920696EF0 -:102F7400697469616C697A6564206166746572203C -:102F84000020726574726965732E00446973706CF5 -:102F9400617920696E697469616C697A6174696FB9 -:102FA4006E206661696C65642E004261636B005536 -:102FB400700053656C65637400446F776E006372D0 -:102FC400632E63006932632E6300493243206465D3 -:102FD40076696365204944206F7574206F662062AA -:102FE4006F756E647320256420286D61783A2025FE -:102FF4006429006F6E007370692E6300206973206A -:103004006F7574736964652073797374656D206674 -:103014006C6173680D0A0073797374656D5F666C17 -:103024006173685F657261736528002C2000290D47 -:103034000A006661696C656420746F20657261734F -:103044006520736563746F72200073797374656DA2 -:103054005F666C6173685F77726974652800666186 -:10306400696C656420746F2077726974652061648B -:1030740064726573732000496E76616C69642066BE -:1030840069726D77617265206465736372697074C7 -:10309400696F6E2100436865636B73756D6D696E4E -:1030A40067206669726D77617265207570646174FA -:1030B4006500496E76616C6964206669726D77613A -:1030C40072652043524320696E2053504920666C38 -:1030D400617368210065726173655F6F6C645F661C -:1030E40069726D776172650077726974655F6E6588 -:1030F400775F6669726D7761726500436865636BBB -:1031040073756D6D696E6720002062797465730D47 -:103114000A00436865636B73756D202D2077616EBB -:10312400746564200020676F7420004F75722069F5 -:103134006E7465726E616C20666C61736820636F77 -:103144006E74656E747320617265206261642028F8 -:10315400636865636B73756D206661696C6564296A -:10316400212054686973206973207265616C6C79DD -:103174002062616421004F75722070726576696FF8 -:103184007573206669726D7761726520757064610C -:103194007465206661696C65642C2061626F727469 -:1031A400696E67207570646174652E004E657720C2 -:1031B4006669726D776172652069732061766169F1 -:1031C4006C61626C6521004C6F6164696E6720728A -:1031D40065636F76657279206669726D7761726571 -:1031E400004661696C656420746F206C6F616420B3 -:1031F4007265636F76657279206669726D77617244 -:10320400652C20737472696B65206F6E652E205473 -:10321400727920616761696E2E004661696C65642C -:1032240020746F206C6F6164207265636F766572C1 -:1032340079206669726D776172652C207374726986 -:103244006B652074776F2E205472792061676169F1 -:103254006E2E004661696C656420746F206C6F612A -:1032640064207265636F76657279206669726D7722 -:103274006172652C20737472696B65207468726561 -:10328400652E20534144205741544348004841523D -:1032940044204641554C5400657869742073746128 -:1032A4006E64627900205F5F5F5F20202020202011 -:1032B4002020202020202020205F5F002F5C202061 -:1032C4005F605C20202020202020202020202F2729 -:1032D4005F5F605C005C205C2C5C4C5C5F5C20206D -:1032E40020205F5F5F202F5C205C2F5C205C20200F -:1032F4005F5F20205F5F20205F5F20205F5F2020D2 -:103304005F5F00205C2F5F5C5F5F205C20202F27C5 -:10331400205F20605C205C205C205C205C2F5C20B3 -:103324005C2F5C205C2F5C205C2F5C205C2F5C207D -:103334005C002020202F5C205C4C5C205C2F5C20F7 -:103344005C2F5C205C205C205C5F5C205C205C204B -:103354005C5F2F205C5F2F205C205C205C5F5C2026 -:103364005C002020205C20605C5F5F5F5F5C205C11 -:103374005F5C205C5F5C205C5F5F5F5F2F5C205C58 -:103384005F5F5F785F5F5F2F275C2F605F5F5F5FCA -:10339400205C00202020205C2F5F5F5F5F5F2F5C3C -:1033A4002F5F2F5C2F5F2F5C2F5F5F5F2F20205CD0 -:1033B4002F5F5F2F2F5F5F2F202020602F5F5F5FC5 -:1033C4002F3E205C002020202020202020202020B0 -:1033D40020202020202020202020202020202020E9 -:1033E40020202020202020202020202020202020D9 -:1033F4002F5C5F5F5F2F00202020202020202020D2 -:1034040020202020202020202020202020202020B8 -:1034140020202020202020202020202020202020A8 -:1034240020205C2F5F5F2F004C61737420666972EB -:103434006D7761726520626F6F742077617320739A -:103444007461626C653B20636C6561722073747295 -:10345400696B657300486F6C6420646F776E2055E8 -:1034640050202B204241434B20666F72203520733D -:103474006563732E20746F20666F7263652D626FAF -:103484006F7420505246004669726D7761726520F0 -:10349400697320657261736564005761746368645D -:1034A4006F6720636175736564206120726573655D -:1034B4007400536F667477617265206661696C7518 -:1034C400726520636175736564206120726573653C -:1034D40074004661696C656420746F207374617252 -:1034E40074206669726D776172652C2073747269D9 -:1034F4006B652074687265652E004661696C65644D -:1035040020746F207374617274206669726D7761C0 -:1035140072652C20737472696B652074776F2E004A -:103524004661696C656420746F20737461727420E1 -:103534006669726D776172652C20737472696B654C -:10354400206F6E652E00466F7263652D626F6F7417 -:10355400696E67207265636F76657279206D6F643A -:10356400652E2E2E00426F6F74696E672066697235 -:103574006D77617265204020002E2E2E0D0A0D0AF3 -:10358400004153534552543A20002020003A004150 -:103594005353455254004153534552544E002A2A22 -:1035A4002A20575446200053544D33320053544D6F -:1035B4003332207065726970686572616C206C6961 -:1035C4006272617279207472697070656420616ED0 -:1035D400206173736572740043524F414B204F4F07 -:0435E4004D00000096 -:1035E800FF000000000102030401020304060708AB -:0435F80009000000C6 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_snowy_dvt@1478015115.hex b/bin/boot/nowatchdog_boot_snowy_dvt@1478015115.hex deleted file mode 100644 index becfde4b52..0000000000 --- a/bin/boot/nowatchdog_boot_snowy_dvt@1478015115.hex +++ /dev/null @@ -1,869 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008A51A0008B8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0DBFF01F093FC70471F -:1001E400E83500080000002014000020140000205E -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040001FC002401902546E0B200F049FC01AA5B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F07DFF02A800F00A -:100234002DFE0120002102F0D5FB00201AE001333D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F061FF02A800F011FE0120214602F016 -:10027400B9FB28460BB030BD082E0008342E000808 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0A1F9003018BF9B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F09AF9A1 -:1002C40020460E21052208AE02F094F905222046AC -:1002D4000C2102F08FF94FF4005346F81C3D002521 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F02FF94FF427 -:10030400804320463146019302F028F94FF480538C -:100314002046314601934FF4007802F01FF920463D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340015F94FF480632046314601938DF8085037 -:100344008DF80B5002F00AF94FF4807320463146C1 -:1003540001938DF808708DF80B5002F0FFF84FF4FC -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F0F4F839464FF4001002F0D7FAA4F56D -:100384004444164B164E1E60204602F04BFB03A855 -:1003940002F0BFFB4FF4827339463046ADF80E309D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F086FB2046394602F0ADFB28 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F087FE012000F08B -:1003F40045FB022001F081FE0120BDE8084000F029 -:100404002BBB000010B504460548022102F09EFBF8 -:100414000028F9D021460248BDE8104002F094BB00 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0DAF8012001F05FFE25484FF4E4 -:100444000041002202F0D1F84FF4807100222148CB -:1004540002F0CBF8012001F050FE4FF400411D489A -:10046400012202F0C2F8012001F047FE19484FF4BE -:10047400007102F0B4F818B11748184A6A210AE06A -:1004840014484FF4004102F0AAF808B1254404E0EE -:100494001148134A6B2101F0A3FDAC4204D014F8B7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F09BF808240020FFF7A4FF013CFAD1C6 -:1004C4004FF40071034802F08AF8003018BF01208D -:1004D40038BD00BF001802404D2E0008572E0008FA -:1004E400722E000810B504460B4802F02AFB0A4895 -:1004F400022102F02BFB0028F9D00748214602F024 -:1005040023FB0548012102F021FB0028F9D0024811 -:1005140002F017FBC0B210BD0054014010B54FF4F7 -:10052400807104460022054802F05FF8642001F05F -:10053400DFFD2046BDE81040FFF764BF001802400D -:1005440008B50748802102F001FB0028F9D10548CD -:100554004FF480710122BDE8084002F046B800BFA4 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0B4FDF1E7012010BD00BFEF -:100594008F2E000810B5224C638822884FF6FF7115 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F009F84FF40041002215480D -:1005E40002F003F8012001F088FD12484FF40041A5 -:1005F400012201F0FAFF40F2E9340E484FF4007191 -:1006040001F0EDFF0028D9D1013C04D10A4800F0E3 -:100614003DFC204610BD642001F06AFDEDE700BFFB -:1006240000000F60B22E000804000F60DE2E0008E8 -:10063400FA2E000800180240102F000810B50446D6 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0A2FF642001F022FD0020FFF7A2 -:1006B40019FF4FF48071012204461A4801F095FF96 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF2C2F0008BD -:100724005B2F000800180240843500086A2F000877 -:10073400852F00088F2F000808B55D235843094B07 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F054BAF5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0D1FE3A4804210122B5 -:1007C40001F013FF00240223029435488DF8083009 -:1007D400072103230C228DF80930019401F00AFF4C -:1007E40080232F480193294601F0B8FE022C04D03F -:1007F4002C48A1B20C2201F0FDFE0134102CF5D1DD -:100804004FF6FB7327480193294601F0A7FE022601 -:100814003146013624480C22B6B201F0EBFE102E0C -:10082400F6D14FF6FC7329461F48019301F096FE5A -:10083400042100221B481E4D01F0D7FE0A2001F0BE -:1008440057FC01220421174801F0CFFE1E2001F0BD -:100854004FFC0120014602F051F80FCD03AC0FC448 -:1008640095E8070084E80700402200210AA802F066 -:10087400EEF902230B9308230C934FF48053129345 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400EBFD0020012101F051FE1AB070BD00BF34 -:1008A40000040240000C024000100240642C0008C6 -:1008B4006C2C00080D4B98221A80A3F68A231B88FF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F076FBF7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0C9FB11E05AB2012A0CDCFF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0CAFD1A480621042201F0FF -:1009D40011FE04220921174801F00CFE154800F00D -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0ABFD0C4800F072FA042000F03E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0BEFD20465D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400ACFD2046BDE8384000F02ABA00140240FC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F093FD0446284600F018FADF -:100AC400B4FA84F0400938BD882C000838B50024F5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F01BFF04F140076568284600F021 -:100B0400DFF9684601F062FD02238DF80530A37B0E -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F01AFD284600F0E1F9BC42DB -:100B3400E3D14FF48040314601F0F8FE03B0F0BD3C -:100B4400882C000808B5054B1B781BB904483221D2 -:100B540001F052FABDE8084001F0C6BB1A000020BB -:100B6400C22F0008124B1B7870B505460C461BB902 -:100B74001048382101F040FA032906D98E0831467D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F09ABBBDE8704001F0AABBFB -:100BB4001A000020C22F0008024B1A780AB9012239 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F010FA01214FF4805059707D -:100BE40001F080FEBDE8084001F072BB1A0000204D -:100BF400C22F000808B5074B1A781AB906485121C4 -:100C040001F0FAF9002159704FF48050BDE8084012 -:100C140001F068BE1A000020C22F000870B50C460F -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0C7FF002884 -:100CA400F9D00648214601F0BDFF0448402101F077 -:100CB400BDFF0028F9D010BD1C00002000480040F2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F087FF31464A460020A1 -:100D340001F0BEF907F1080304F5927443F8040DB9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64007FF904465520FFF791FF6E466C44A64276 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020D02C000824000020B3 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0AEFD20460A21072201F026 -:100DC40019FC06AE072220460B2101F013FC4FF458 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0B3FB4FF40063204631460093B5 -:100E04008DF8048001F0AAFBADF80C50ADF80E503B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F096FE284639468B -:100E340001F0ECFE204600F05BF8054B1F7006B095 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4003AF901A8FFF7CEFF05B05DF804FB000086 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F0F6FA5D -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0B5F842 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BDF02C000849 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0BFFA2046D8 -:100FE40031462A4601F006FB2046FFF781FF03B095 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0BCFB00287BD0CF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BDF02C000808 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0DCFB684601F096 -:10116400F3FA0F4A236A009393424FF48063ADF875 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F075FA2859012101F03D -:10119400E9FA04B070BD00BF30010020F02C000853 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0CAF92046B1B22A4601F061 -:1011D4000CFA2046FFF78CFE02B070BD10B5094C26 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BDF02C00081C23104A434370B515 -:1012140005460F4CD058D61801F0FEF9342305FBCF -:1012240003440021E06901F075FB337923B92846B2 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400F02C00082DE9F347504C9A4694F83830A6 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0BEFE494B1844DF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F05BFA2AE03046FFF79DFF30462A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0D1FEDBE719688B8823F4EB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A400C82F0008F02C000880841E00017D03680B -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F0F2FE17 -:1014840073788DF8043001A800F0ECFE54F8040CD5 -:1014940001F0C2F8454504F11C0406F1340601D0FC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020182D0008300100201F -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F090FD042807D90522009250 -:1014E4000D480E4A40F2351100F07AFD0C4A1344BF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020C82F0008CE2F000893 -:10152400F02C0008104A08B592F838301BB90F485F -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F05AFD0A4B18441C2390F8880003FB52 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD30010020C82F000815 -:10157400F02C000837B50D4614460093069B0193E2 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F027F90DB99A -:10161400029805E0012D01D1039801E000F0F6FCE9 -:10162400B4EB500F0ED8B0FBF4F000F070FD0028BE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0DAFC01200138C00080B205B030BDE2 -:10165400FA2F0008B0F1006F10B504460BD20C4805 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:101694007230000800300008802D00082DE9F8435E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F01AFE2C464C451FDCF32000F02AFEB5 -:10170400154B002133F8140000F05CFE09280BD0BF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F00FFE0DE02FB1C5F10100204420 -:1017340031464246B8470134DDE700F003FE01209C -:10174400BDE8F8830020BDE8F88300BF1B30000823 -:101754002F30000832300008B02D00083630000861 -:10176400843500082DE9F0470646234814460D4603 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400CBFDF32000F0DEFD4FEAD41805EB04097D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F03DFE09280DD01248FFF74AFB3B -:1017C4003046FFF771FB1048FFF760FB00F0BAFDED -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0AAFD52 -:1017F4000120BDE8F08700BF4E3000082F300008FC -:1018040032300008623000088435000808B545F21B -:10181400555000F0CDFF042000F0D0FF40F6FF70DB -:1018240000F0D2FF002000F0C3FF4FF480500121EC -:10183400BDE8084000F06CBD08B57D2001F0BEF89D -:10184400003018BF012008BD4900FEF775BF4B08E2 -:1018540003EB5000FEF770BF70B586B00546FFF786 -:10186400ABF901210020FEF767FF042000F01EFB06 -:1018740003A8294600F066FB03A800F072FB08B930 -:1018840028480BE02848FFF701FB05F10C000499F8 -:10189400FFF7D9F9059B984204D02448FFF7F6FADC -:1018A40001203DE0049C2248FFF7F0FA2146214A3A -:1018B40021480023FFF7F2FE049E2048FFF7E6FAD2 -:1018C400002405F1C0411E4B009432460C311A48E5 -:1018D400FFF748FF1B48FFF7BDFA0498FFF7E4FA47 -:1018E4001948FFF7B7FA04991348FFF797F9054623 -:1018F4001648FFF7AFFA0598FFF7D6FA1448FFF732 -:10190400A9FA2846FFF7D0FA1248FFF7BFFA059B59 -:101914009D4204D01048FFF7B9FA022000E02046A7 -:1019240006B070BD7B30000899300008B63000085E -:10193400D93000084D18000800400008EC300008B9 -:1019440053180008FF3000080D3100081631000854 -:1019540029310008843500082F31000808B5022019 -:1019640000F0BCFAD8B3042000F0B8FA70B11C48F7 -:10197400FFF78CFA042000F0A5FA022000F0A2FA86 -:101984004FF40040BDE8084000F09CBA1548FFF74A -:101994007DFA082000F096FA102000F093FA202037 -:1019A40000F090FA402000F08DFA4FF48010FFF719 -:1019B40053FF022807D1082000F078FA102000F025 -:1019C40075FA00F045FB042000F07CFA022000F0D8 -:1019D40079FA4FF40040BDE8084000F067BA08BD4A -:1019E4007A310008B031000810B52548FFF74EFAE7 -:1019F4004FF40010FFF730FF10B102283AD809E085 -:101A0400202000F05FFA402000F05CFA802000F013 -:101A14004DFA2FE0082000F049FA102000F046FAB1 -:101A2400202000F05BFA20B91648FFF72FFA202097 -:101A340007E0402000F052FA58B91348FFF726FA9D -:101A4400402000F033FA4FF4007000F02FFA00F059 -:101A5400FFFA0E48FFF71AFA082000F033FA1020B4 -:101A640000F030FA202000F02DFA402000F02AFA8D -:101A7400002400E00124042000F024FA204610BDD4 -:101A8400CB310008E53100081E3200085732000847 -:101A940008B50248FFF7FAF900BEFEE791320008E4 -:101AA4001EF0040F0CBFEFF30880EFF30980FFF77B -:101AB400EFBF70471FB504461048FFF7E7F901A9C7 -:101AC4000C22204600F02FFB01A8FFF7DFF9204687 -:101AD400FEF74AFEFEF7FAFF094B20F004001870E7 -:101AE4001C46FEF7F3FF237800F0FB00834201D08D -:101AF40000F0AEFA0A2000F000FBF2E7873200089B -:101B0400690100207FB54FF08050012100F002FFF1 -:101B1400022000F065FE012807D1022000F06AFED1 -:101B2400BB48FFF7B3F900F098FA00214FF080505A -:101B340000F0F0FEFFF734F9B648FFF7A7F9B6480E -:101B4400FFF7A4F9B548FFF7A1F9B548FFF79EF9E7 -:101B5400B448FFF79BF9B448FFF798F9B348FFF787 -:101B640095F9B348FFF792F9B248FFF78FF9B248F5 -:101B7400FFF78CF9FFF760FCFEF710FF00F0B8F9EF -:101B840000F0CEF900210C2201A801F060F800F069 -:101B9400C3F901A90C2200F0C6FA01A8FFF776F9EF -:101BA4009C48FFF773F99B48FFF770F94FF48040A6 -:101BB40000F094F990B1A148FFF768F94FF4804020 -:101BC40000F080F9082000F07DF9102000F07AF987 -:101BD400202000F077F9402000F074F9FEF7D8FDDA -:101BE400FEF782FFFEF74EFDFEF706FB08B19448B0 -:101BF40003E0FEF745FB10B19248FFF75BFF00F0EE -:101C04008AF9802000F06AF918B1802000F05AF9AE -:101C140056E04FF4003000F061F9044620B14FF46F -:101C2400003000F04FF94DE00120FEF739FFB0B16C -:101C34002046FEF735FF90B18348FFF727F941F2BC -:101C440088340120FEF72CFF98B10020FEF728FF0E -:101C540078B1012000F051FA013CF2D132E07B4B23 -:101C64000CCB013301D0013203D17948FFF70EF9CF -:101C740028E0FFF7E1FD48B3FFF7DEFD10B175483A -:101C8400FFF704F94FF4007000F028F910B172481E -:101C9400FFF7FCF84FF4007000F014F9102000F086 -:101CA4001DF9002800F0EE806C48FFF7EFF80820DB -:101CB40000F008F9102000F005F9FFF795FE98B937 -:101CC40067489AE76748FFF7E1F8F6E74FF40070D2 -:101CD40000F004F90028CFD1082000F0F3F8102018 -:101CE40000F0F0F8FFF73AFE4FF4005000F0F6F879 -:101CF40004464FF4805000F0F1F8400040EA8400BC -:101D0400C4B24FF4006000F0E9F82043C0B20728E1 -:101D1400C3B20DD14FF4006000F0D4F84FF48050FA -:101D240000F0D0F84FF4005000F0CCF84E4864E7CF -:101D34000133DBB2023B4FF40060052B11D8DFE81E -:101D440003F00C1003100C1000F0BCF84FF480509A -:101D540000F0B8F84FF4005003E000F0B3F84FF48B -:101D6400805000F0A3F8FFF751FD384B3F485D6801 -:101D74001E68FFF76FF82846FFF796F83C48FFF710 -:101D840069F80022930003F1604303F561430132D3 -:101D94004FF0FF31082AC3F88010C3F88011F1D145 -:101DA400344B4FF4801200241A635C639C631C64FC -:101DB4005C6400F02FFD3048012100F0C3FD214692 -:101DC4002D4800F0BFFDF120012100F0C7FD2146A0 -:101DD400F12000F0C3FD0120014600F0CBFD2146B7 -:101DE400012000F0C7FD2548012100F0CFFD214668 -:101DF400224800F0CBFD2248012100F0D3FD204809 -:101E0400214600F0CFFD63B64FF0FF3EB5462847AC -:101E14009C32000884350008A9320008C03200084A -:101E2400D932000807330008363300086633000847 -:101E340097330008C9330008FB3300082C3400082A -:101E4400014550FE024550FE593400080040000888 -:101E54008B3400089E340008B6340008D6340008D9 -:101E6400034550FE4A350008044550FE6935000814 -:101E74007D350008003802400010E022FFC9FEF65C -:101E8400337F7704082000F029F820B10548FEF7D5 -:101E9400FDFF102003E00448FEF7F8FF082000F0DF -:101EA40005F81FE7FE3400082435000810B5044681 -:101EB400002000F0A7FD40EA04010020BDE8104026 -:101EC40000F090BD10B50446002000F09BFD20EA10 -:101ED40004010020BDE8104000F084BD10B50446A4 -:101EE400002000F08FFD204214BF0120002010BD0F -:101EF40008B54FF08050012100F00CFD012000F0E6 -:101F040069FC0120FFF7EAFF20B90121BDE8084080 -:101F140000F068BD08BD7047022000F073BD0000EA -:101F240008B5FFF7F9FF054B1968884204D0022071 -:101F3400BDE8084000F056BD08BD00BF840100089C -:101F440000BEFDE71FB504460C2201A8FEF71CFCE9 -:101F540001AB03CB20601868A0602046616004B028 -:101F640010BD0068A0F10C035842584170470000AE -:101F740080B50646174610480D461C46FEF76AFF14 -:101F84003846FEF767FF0D48FEF764FF3046FEF75C -:101F940061FF0B48FEF75EFF2846FEF785FF2CB174 -:101FA4000848FEF757FF2046FEF754FF0648FEF7A1 -:101FB4006DFFFFF7C5FF00BF853500088E350008AB -:101FC400913500088F350008843500081FB506AA2E -:101FD40052F8044B039200921A462346FFF7C8FFB7 -:101FE4000CB41FB506AA52F8043B03920092014AAE -:101FF400FFF7BEFF9335000807B500230093724630 -:10200400014BFFF7E3FF00BF9A350008744608B59B -:102014000548FEF71FFF2046FEF746FF0348FEF77C -:1020240035FFFFF78DFF00BFA23500088435000897 -:10203400BFF34F8F0449054BCA6802F4E0621343AF -:10204400CB60BFF34F8FFEE700ED00E00400FA051C -:1020540008B5FEF791FBFFF7EBFF08B5FFF7E8FFC4 -:10206400F0B50646002401202546034694421DD0BF -:1020740011F804C000F1010EBCF1000F03D1735537 -:10208400774605460DE00133DBB2FF2B774606F8B1 -:1020940000C007D102F1FF3C644506D07355871C8C -:1020A4007546012301343846E0E770467355F0BDA8 -:1020B40030B5C9B1C0430A44914213D011F8013B71 -:1020C4000A4D83EA000404F00F0455F8244084EA1E -:1020D400101080EA131303F00F0355F8233083EA3A -:1020E4001010E9E7C04330BD084630BDC82D0008D4 -:1020F400162358430138FDD1704710B504462CB15E -:102104004FF47A70FFF7F4FF013CF8E710BD10B507 -:10211400B0FA80F400F027F801280CBFC4F11F00C6 -:10212400C4F1200010BD0A2A10B51BDD0C46302373 -:1021340004F8023B78234B701C220F239340034086 -:10214400D340092B01D8303302E00F2B02D8573388 -:10215400DBB200E02023043A04F8013B131DECD168 -:1021640000238B7210BD00F0AA33A0EB530000F0E3 -:10217400CC3300F0333000EB930000EB101000F090 -:102184000F3000EB102000EB104000F03F007047D0 -:102194002D4AD2F8883043F47003C2F888302B4BB0 -:1021A4001A68002142F001021A6099601A6822F04C -:1021B400847222F480321A60254A5A601A6822F422 -:1021C40080221A60D9601A6C224942F080521A6443 -:1021D4000A6842F440420A609A689A609A689A606F -:1021E4009A6842F480529A601B4A5A601A6842F014 -:1021F40080721A601968154A8901FBD5174B40F2A1 -:1022040003611960936823F003039360936843F0B8 -:10221400020393600D4B9A6802F00C02082AF9D16C -:102224009A6822F400029A600D4AC3F884201A685E -:1022340042F080621A60054B1B681B01FBD5024B00 -:102244004FF000629A60704700ED00E000380240F1 -:1022540010300024007000401040010F003C024088 -:1022640000300050C278037810B512B3164AD16812 -:102274004278C943C1F30221C1F10404E4B2A2408B -:10228400D4B20F220A4181780A40224303F1604309 -:1022940003F561431201D2B283F8002303780122CB -:1022A400590903F01F0302FA03F3084A42F82130E4 -:1022B40010BD5A09012103F01F03994002F12003C4 -:1022C400024A42F8231010BD00ED00E000E100E0F6 -:1022D400014B01229A60704700300240014B1860A4 -:1022E400186870470030024000EB81018842044BBB -:1022F40003D050F8042B1A60F8E71868704700BF41 -:1023040000300240014B1868704700BF00300240A3 -:10231400044B9A6809B1104301E022EA0000986076 -:10232400704700BF002004E0044B1A69002ABFBFB5 -:10233400034A5A6002F188325A607047003C0240F6 -:1023440023016745024A136943F000431361704750 -:10235400003C0240014BD860704700BF003C024083 -:102364000E4BDA68D20310D4DA68D1060FD4DA68D7 -:10237400D2050ED4DA6812F0EF0F0CD1DB6813F03B -:10238400020F14BF0820092070470120704706205F -:102394007047022070470720704700BF003C02408E -:1023A40007B509238DF80730FFF7DAFF8DF807002A -:1023B4009DF80730012BF7D09DF8070003B05DF8B6 -:1023C40004FB000070B5064641B1012908D002297A -:1023D4000CBF4FF400754FF4407503E00D4601E067 -:1023E4004FF48075FFF7DCFF09281ED10F4C2369D9 -:1023F40023F440732361216929432161236923F074 -:10240400F8032361236943F002031E432661236911 -:1024140043F480332361FFF7C3FF236923F00203EE -:102424002361236923F0F803236170BD003C02405B -:1024340070B505460E46FFF7B3FF092811D1094CC4 -:10244400236923F44073236123692361236943F0DF -:10245400010323612E70FFF7A3FF236923F0010317 -:10246400236170BD003C024070B543688668856A8C -:102474000468416B46EA0302C3681A4303691A43BA -:1024840043691A4383691A43C3691A43036A1A43A3 -:10249400436A1343C26A2B431A43036B1343A200D8 -:1024A40002F120420B43082E136002BF136843F06D -:1024B40040031360B1F5801F816B12D18CB14FF0D2 -:1024C40020435C681E6846F480161E601E6846F44D -:1024D40080761E600E6924F47004013E44EA0654BA -:1024E4005C608C690B6823434C6843EA04138C6872 -:1024F40043EA0423CC6843EA04430C69496943EA88 -:10250400045343EA0163B5F5804F536010D1C16BA6 -:1025140088690B680343486843EA0013886843EA00 -:1025240000230869496943EA005343EA016301E06F -:102534006FF07043C2F8043170BD0000800000F1F8 -:10254400204019B1036843F0010302E00268024B22 -:1025540013400360704700BFFEFF0F00F0B50E6824 -:1025640000220123934003EA060E9E452AD155001A -:1025740003230468AB40DB431C4004600C79076808 -:1025840004FA05FC013C4CEA0707012C076011D84A -:1025940084684F791C4084608468AF402743876017 -:1025A400446824EA0E0444608C7947689440A4B2D9 -:1025B4003C434460C4682340C360CB79C468AB40E7 -:1025C4002343C3600132102ACBD1F0BD4FF6FF7311 -:1025D40003600023037143718371C37170470369FE -:1025E400194214BF0120002070470AB101837047CB -:1025F4004183704701F00703C90800EB810010B55F -:102604009B00046A0F21994024EA01010162016AD6 -:102614009A401143016210BD08B5134B984207D18B -:102624004FF40010012100F0B1F94FF4001014E050 -:102634000E4B984207D14FF48000012100F0A6F917 -:102644004FF4800009E00A4B98420BD14FF400008C -:10265400012100F09BF94FF400000021BDE808407F -:1026640000F094B908BD00BF005400400058004079 -:10267400005C00407FB5868826F03F060446360499 -:1026840068460D46360C00F0E9F8029A2C48B2FB75 -:10269400F0F081B20E43A68023882A4E23F0010372 -:1026A4001B041B0C23802B68B3420AD85B00013146 -:1026B400B2FBF3F39BB2032B89B298BF04232184AA -:1026C40023E0EE884BF6FF718E421BBF19214B436A -:1026D40003EB4303B2FBF3F315BF9BB2B2FBF3F37B -:1026E40043F480439BB2C3F30B020AB943F00103E2 -:1026F4004FF4967101FB00F24FF47A7192FBF1F200 -:10270400013292B243F400432284A383238869896B -:10271400AA889BB243F001032380238823F48163B6 -:1027240023F002031B040A431B0C13439BB22380B4 -:102734002A89AB8913439BB2238104B070BD00BFC7 -:1027440040420F00A086010041F288330360002359 -:1027540083804BF6FF72038143814FF48043C28030 -:1027640083817047038819B19BB243F0010303E0EE -:1027740023F001031B041B0C03807047038819B169 -:102784009BB243F4806303E023F480631B041B0CBB -:1027940003807047838A9AB2038B10B542EA0343DD -:1027A40021F07F4404EA0300431A5842584110BD03 -:1027B400014B1860704700BF00300040014B586067 -:1027C400704700BF00300040014B9860704700BF65 -:1027D40000300040014B1860704700BF20000E42DB -:1027E400034B5B68184214BF01200020704700BFF0 -:1027F40000700040064BB0F5402F15BF1A685A68A8 -:1028040042EA800042F4402214BF18605A607047C4 -:10281400007000400E4B1A68002142F001021A6059 -:1028240099601A6822F0A85222F410221A60094A08 -:102834005A60094AC3F8842002F18062C3F88820F0 -:102844001A6822F480221A60D960C3F88C10704789 -:102854000038024010300024003000201D4A9368E4 -:1028640003F00C03042B10B503D0082B03D01A4B30 -:1028740018E01A4B16E05168536811F4800F03F006 -:102884003F03516814BF154A134AB2FBF3F3114ACC -:102894005268C2F30142C1F3881101324B43520022 -:1028A400B3FBF2F30B4A036093680D49C3F30313BC -:1028B400CC5C0368E34043609468C4F382240C5DF9 -:1028C40023FA04F484609268C2F342328A5CD340EF -:1028D400C36010BD003802400024F40040787D013C -:1028E40001000020044B1A6B09B1104301E022EAF5 -:1028F40000001863704700BF00380240044B9A6B15 -:1029040009B1104301E022EA00009863704700BF58 -:1029140000380240044B1A6C09B1104301E022EA6A -:1029240000001864704700BF00380240044B5A6C22 -:1029340009B1104301E022EA00005864704700BF67 -:1029440000380240044B1A6909B1104301E022EA3D -:1029540000001861704700BF00380240044B5A69F8 -:1029640009B1104301E022EA00005861704700BF3A -:1029740000380240044B9A6909B1104301E022EA8D -:1029840000009861704700BF00380240044B1A6A87 -:1029940009B1104301E022EA00001862704700BF49 -:1029A40000380240044B5A6A09B1104301E022EA9C -:1029B40000005862704700BF003802404209012AF3 -:1029C400074B01D11B6803E0022A0CBF1B6F5B6F2E -:1029D40000F01F0023FA00F000F00100704700BF70 -:1029E4000038024082B000230193054B0193019B00 -:1029F40003EB80000190019B196002B0704700BF97 -:102A04005028004082B000230193054B0193019BA1 -:102A140003EB80000190019B186802B0704700BF6F -:102A24005028004008B5254B984207D14FF48050F8 -:102A34000121FFF7B7FF4FF4805039E0204B984253 -:102A440007D14FF480400121FFF7A0FF4FF48040ED -:102A540009E01C4B98420BD14FF400400121FFF7D1 -:102A640095FF4FF400400021BDE80840FFF78EBFFA -:102A7400154B984207D14FF400500121FFF792FF04 -:102A84004FF4005014E0114B984207D14FF48010DA -:102A94000121FFF787FF4FF4801009E00C4B9842A7 -:102AA4000BD14FF400100121FFF77CFF4FF400100D -:102AB4000021BDE80840FFF775BF08BD00300140A4 -:102AC40000380040003C0040003401400050014008 -:102AD4000054014003884A8810B503F441540B881C -:102AE40013438A881343CA8813430A8913434A89C0 -:102AF40013438A891343CA89134323439BB2038034 -:102B0400838B23F400631B041B0C83830B8A0382D3 -:102B140010BD0023038043808380C38003814381ED -:102B24008381C381072303827047038819B19BB251 -:102B340043F0400303E023F040031B041B0C038019 -:102B44007047808980B27047818170470389194238 -:102B540014BF0120002070477FB5038ACA889BB246 -:102B640023F440531343038283890E469BB223F418 -:102B7400B053098923F00C02B3880B4371890B43CA -:102B84009BB213438381838AB2899BB223F440733B -:102B94001343838204466846FFF760FE1A4B9C4247 -:102BA40003D003F580639C4201D1039D00E0029DA4 -:102BB400A38931681BB2002B4FF0190202FB05F206 -:102BC400B4BF4D008D00B2FBF5F5A2896426B5FBB8 -:102BD400F6F00001010912B2002A06FB1153ADBF41 -:102BE4001A01D90032313232B5BFB1FBF6F2B2FB71 -:102BF400F6F302F0070203F00F03B4BF10431843C7 -:102C040080B2208104B070BD00100140838919B1E5 -:102C14009BB243F4005303E023F400531B041B0C46 -:102C240083817047C1F3080181807047038819428A -:102C340014BF01200020704710B5431E0A4491427E -:102C440004D011F8014B03F8014FF8E710BD02441A -:102C54000346934202D003F8011BFAE770470000D1 -:102C64000400000001030000010000000100000056 -:102C740003000000010000000F0000000F0000002E -:102C840000000000AE2F00080018024010000000F1 -:102C940006040000B32F00080018024008000000DA -:102CA40006030100B62F00080018024002000000CD -:102CB40006010100BD2F00080018024004000000B6 -:102CC40006020100000000000000000001424F4F16 -:102CD400544C4F414445520000000000002A2A0091 -:102CE400000000000000000000000000005400404C -:102CF40000040240400000000600040000040240FA -:102D0400000200000900040000002000801A0600F0 -:102D1400004000001F200000000000000000000030 -:102D240000580040001402400200000001000400AA -:102D340000140240010000000000040000004000F4 -:102D4400801A0600FFBF00002122000000000000DE -:102D54000116000801000300820007008400070038 -:102D6400010003000200030040000300800007008C -:102D740084000700000001000000000000000008BB -:102D8400004000080080000800C00008000001089E -:102D940000000208000004080000060800000808FB -:102DA40000000A0800000C0800000E0800000800DB -:102DB40010001800200028003000380040004800AF -:102DC40050005800000000006410B71DC8206E3B7E -:102DD400AC30D9269041DC76F4516B6B5861B24D1E -:102DE4003C7105502083B8ED44930FF0E8A3D6D688 -:102DF4008CB361CBB0C2649BD4D2D38678E20AA0F0 -:102E04001CF2BDBD537475636B20627574746F6E70 -:102E140020726567697374657220697320696E76C0 -:102E2400616C69642C20636C656172696E672E0045 -:102E3400427574746F6E206973207075736865646D -:102E440020617420626F6F740069636534306C7044 -:102E54002E630043444F4E45206E6F74206C6F7791 -:102E640020647572696E67207265736574004352DD -:102E740045534554206E6F742068696768206475F3 -:102E840072696E6720726573657400446973706C4F -:102E9400617920627573792D776169742074696D25 -:102EA400656F757420657870697265642100436F7D -:102EB4006E6669677572696E672046504741206681 -:102EC400726F6D2062697473747265616D20696ECE -:102ED40020666C6173682E2E2E004E6F204650477C -:102EE400412062697473747265616D20696E206635 -:102EF4006C6173682E0046616C6C696E6720626158 -:102F0400636B20746F204E56434D2E00465047414C -:102F14002043444F4E452074696D656F7574206578 -:102F240078706972656421004650474120636F6E72 -:102F340066696775726174696F6E206661696C6534 -:102F4400642E204973207468697320612062696764 -:102F5400626F6172643F0046504741207665727328 -:102F6400696F6E3A2000446973706C617920696EF0 -:102F7400697469616C697A6564206166746572203C -:102F84000020726574726965732E00446973706CF5 -:102F9400617920696E697469616C697A6174696FB9 -:102FA4006E206661696C65642E004261636B005536 -:102FB400700053656C65637400446F776E006372D0 -:102FC400632E63006932632E6300493243206465D3 -:102FD40076696365204944206F7574206F662062AA -:102FE4006F756E647320256420286D61783A2025FE -:102FF4006429006F6E007370692E6300206973206A -:103004006F7574736964652073797374656D206674 -:103014006C6173680D0A0073797374656D5F666C17 -:103024006173685F657261736528002C2000290D47 -:103034000A006661696C656420746F20657261734F -:103044006520736563746F72200073797374656DA2 -:103054005F666C6173685F77726974652800666186 -:10306400696C656420746F2077726974652061648B -:1030740064726573732000496E76616C69642066BE -:1030840069726D77617265206465736372697074C7 -:10309400696F6E2100436865636B73756D6D696E4E -:1030A40067206669726D77617265207570646174FA -:1030B4006500496E76616C6964206669726D77613A -:1030C40072652043524320696E2053504920666C38 -:1030D400617368210065726173655F6F6C645F661C -:1030E40069726D776172650077726974655F6E6588 -:1030F400775F6669726D7761726500436865636BBB -:1031040073756D6D696E6720002062797465730D47 -:103114000A00436865636B73756D202D2077616EBB -:10312400746564200020676F7420004F75722069F5 -:103134006E7465726E616C20666C61736820636F77 -:103144006E74656E747320617265206261642028F8 -:10315400636865636B73756D206661696C6564296A -:10316400212054686973206973207265616C6C79DD -:103174002062616421004F75722070726576696FF8 -:103184007573206669726D7761726520757064610C -:103194007465206661696C65642C2061626F727469 -:1031A400696E67207570646174652E004E657720C2 -:1031B4006669726D776172652069732061766169F1 -:1031C4006C61626C6521004C6F6164696E6720728A -:1031D40065636F76657279206669726D7761726571 -:1031E400004661696C656420746F206C6F616420B3 -:1031F4007265636F76657279206669726D77617244 -:10320400652C20737472696B65206F6E652E205473 -:10321400727920616761696E2E004661696C65642C -:1032240020746F206C6F6164207265636F766572C1 -:1032340079206669726D776172652C207374726986 -:103244006B652074776F2E205472792061676169F1 -:103254006E2E004661696C656420746F206C6F612A -:1032640064207265636F76657279206669726D7722 -:103274006172652C20737472696B65207468726561 -:10328400652E20534144205741544348004841523D -:1032940044204641554C5400657869742073746128 -:1032A4006E64627900205F5F5F5F20202020202011 -:1032B4002020202020202020205F5F002F5C202061 -:1032C4005F605C20202020202020202020202F2729 -:1032D4005F5F605C005C205C2C5C4C5C5F5C20206D -:1032E40020205F5F5F202F5C205C2F5C205C20200F -:1032F4005F5F20205F5F20205F5F20205F5F2020D2 -:103304005F5F00205C2F5F5C5F5F205C20202F27C5 -:10331400205F20605C205C205C205C205C2F5C20B3 -:103324005C2F5C205C2F5C205C2F5C205C2F5C207D -:103334005C002020202F5C205C4C5C205C2F5C20F7 -:103344005C2F5C205C205C205C5F5C205C205C204B -:103354005C5F2F205C5F2F205C205C205C5F5C2026 -:103364005C002020205C20605C5F5F5F5F5C205C11 -:103374005F5C205C5F5C205C5F5F5F5F2F5C205C58 -:103384005F5F5F785F5F5F2F275C2F605F5F5F5FCA -:10339400205C00202020205C2F5F5F5F5F5F2F5C3C -:1033A4002F5F2F5C2F5F2F5C2F5F5F5F2F20205CD0 -:1033B4002F5F5F2F2F5F5F2F202020602F5F5F5FC5 -:1033C4002F3E205C002020202020202020202020B0 -:1033D40020202020202020202020202020202020E9 -:1033E40020202020202020202020202020202020D9 -:1033F4002F5C5F5F5F2F00202020202020202020D2 -:1034040020202020202020202020202020202020B8 -:1034140020202020202020202020202020202020A8 -:1034240020205C2F5F5F2F004C61737420666972EB -:103434006D7761726520626F6F742077617320739A -:103444007461626C653B20636C6561722073747295 -:10345400696B657300486F6C6420646F776E2055E8 -:1034640050202B204241434B20666F72203520733D -:103474006563732E20746F20666F7263652D626FAF -:103484006F7420505246004669726D7761726520F0 -:10349400697320657261736564005761746368645D -:1034A4006F6720636175736564206120726573655D -:1034B4007400536F667477617265206661696C7518 -:1034C400726520636175736564206120726573653C -:1034D40074004661696C656420746F207374617252 -:1034E40074206669726D776172652C2073747269D9 -:1034F4006B652074687265652E004661696C65644D -:1035040020746F207374617274206669726D7761C0 -:1035140072652C20737472696B652074776F2E004A -:103524004661696C656420746F20737461727420E1 -:103534006669726D776172652C20737472696B654C -:10354400206F6E652E00466F7263652D626F6F7417 -:10355400696E67207265636F76657279206D6F643A -:10356400652E2E2E00426F6F74696E672066697235 -:103574006D77617265204020002E2E2E0D0A0D0AF3 -:10358400004153534552543A20002020003A004150 -:103594005353455254004153534552544E002A2A22 -:1035A4002A20575446200053544D33320053544D6F -:1035B4003332207065726970686572616C206C6961 -:1035C4006272617279207472697070656420616ED0 -:1035D400206173736572740043524F414B204F4F07 -:0435E4004D00000096 -:1035E800FF000000000102030401020304060708AB -:0435F80009000000C6 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_snowy_evt2@1478015115.hex b/bin/boot/nowatchdog_boot_snowy_evt2@1478015115.hex deleted file mode 100644 index becfde4b52..0000000000 --- a/bin/boot/nowatchdog_boot_snowy_evt2@1478015115.hex +++ /dev/null @@ -1,869 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008A51A0008B8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0DBFF01F093FC70471F -:1001E400E83500080000002014000020140000205E -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040001FC002401902546E0B200F049FC01AA5B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F07DFF02A800F00A -:100234002DFE0120002102F0D5FB00201AE001333D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F061FF02A800F011FE0120214602F016 -:10027400B9FB28460BB030BD082E0008342E000808 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0A1F9003018BF9B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F09AF9A1 -:1002C40020460E21052208AE02F094F905222046AC -:1002D4000C2102F08FF94FF4005346F81C3D002521 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F02FF94FF427 -:10030400804320463146019302F028F94FF480538C -:100314002046314601934FF4007802F01FF920463D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340015F94FF480632046314601938DF8085037 -:100344008DF80B5002F00AF94FF4807320463146C1 -:1003540001938DF808708DF80B5002F0FFF84FF4FC -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F0F4F839464FF4001002F0D7FAA4F56D -:100384004444164B164E1E60204602F04BFB03A855 -:1003940002F0BFFB4FF4827339463046ADF80E309D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F086FB2046394602F0ADFB28 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F087FE012000F08B -:1003F40045FB022001F081FE0120BDE8084000F029 -:100404002BBB000010B504460548022102F09EFBF8 -:100414000028F9D021460248BDE8104002F094BB00 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0DAF8012001F05FFE25484FF4E4 -:100444000041002202F0D1F84FF4807100222148CB -:1004540002F0CBF8012001F050FE4FF400411D489A -:10046400012202F0C2F8012001F047FE19484FF4BE -:10047400007102F0B4F818B11748184A6A210AE06A -:1004840014484FF4004102F0AAF808B1254404E0EE -:100494001148134A6B2101F0A3FDAC4204D014F8B7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F09BF808240020FFF7A4FF013CFAD1C6 -:1004C4004FF40071034802F08AF8003018BF01208D -:1004D40038BD00BF001802404D2E0008572E0008FA -:1004E400722E000810B504460B4802F02AFB0A4895 -:1004F400022102F02BFB0028F9D00748214602F024 -:1005040023FB0548012102F021FB0028F9D0024811 -:1005140002F017FBC0B210BD0054014010B54FF4F7 -:10052400807104460022054802F05FF8642001F05F -:10053400DFFD2046BDE81040FFF764BF001802400D -:1005440008B50748802102F001FB0028F9D10548CD -:100554004FF480710122BDE8084002F046B800BFA4 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0B4FDF1E7012010BD00BFEF -:100594008F2E000810B5224C638822884FF6FF7115 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F009F84FF40041002215480D -:1005E40002F003F8012001F088FD12484FF40041A5 -:1005F400012201F0FAFF40F2E9340E484FF4007191 -:1006040001F0EDFF0028D9D1013C04D10A4800F0E3 -:100614003DFC204610BD642001F06AFDEDE700BFFB -:1006240000000F60B22E000804000F60DE2E0008E8 -:10063400FA2E000800180240102F000810B50446D6 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0A2FF642001F022FD0020FFF7A2 -:1006B40019FF4FF48071012204461A4801F095FF96 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF2C2F0008BD -:100724005B2F000800180240843500086A2F000877 -:10073400852F00088F2F000808B55D235843094B07 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F054BAF5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0D1FE3A4804210122B5 -:1007C40001F013FF00240223029435488DF8083009 -:1007D400072103230C228DF80930019401F00AFF4C -:1007E40080232F480193294601F0B8FE022C04D03F -:1007F4002C48A1B20C2201F0FDFE0134102CF5D1DD -:100804004FF6FB7327480193294601F0A7FE022601 -:100814003146013624480C22B6B201F0EBFE102E0C -:10082400F6D14FF6FC7329461F48019301F096FE5A -:10083400042100221B481E4D01F0D7FE0A2001F0BE -:1008440057FC01220421174801F0CFFE1E2001F0BD -:100854004FFC0120014602F051F80FCD03AC0FC448 -:1008640095E8070084E80700402200210AA802F066 -:10087400EEF902230B9308230C934FF48053129345 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400EBFD0020012101F051FE1AB070BD00BF34 -:1008A40000040240000C024000100240642C0008C6 -:1008B4006C2C00080D4B98221A80A3F68A231B88FF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F076FBF7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0C9FB11E05AB2012A0CDCFF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0CAFD1A480621042201F0FF -:1009D40011FE04220921174801F00CFE154800F00D -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0ABFD0C4800F072FA042000F03E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0BEFD20465D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400ACFD2046BDE8384000F02ABA00140240FC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F093FD0446284600F018FADF -:100AC400B4FA84F0400938BD882C000838B50024F5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F01BFF04F140076568284600F021 -:100B0400DFF9684601F062FD02238DF80530A37B0E -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F01AFD284600F0E1F9BC42DB -:100B3400E3D14FF48040314601F0F8FE03B0F0BD3C -:100B4400882C000808B5054B1B781BB904483221D2 -:100B540001F052FABDE8084001F0C6BB1A000020BB -:100B6400C22F0008124B1B7870B505460C461BB902 -:100B74001048382101F040FA032906D98E0831467D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F09ABBBDE8704001F0AABBFB -:100BB4001A000020C22F0008024B1A780AB9012239 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F010FA01214FF4805059707D -:100BE40001F080FEBDE8084001F072BB1A0000204D -:100BF400C22F000808B5074B1A781AB906485121C4 -:100C040001F0FAF9002159704FF48050BDE8084012 -:100C140001F068BE1A000020C22F000870B50C460F -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0C7FF002884 -:100CA400F9D00648214601F0BDFF0448402101F077 -:100CB400BDFF0028F9D010BD1C00002000480040F2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F087FF31464A460020A1 -:100D340001F0BEF907F1080304F5927443F8040DB9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64007FF904465520FFF791FF6E466C44A64276 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020D02C000824000020B3 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0AEFD20460A21072201F026 -:100DC40019FC06AE072220460B2101F013FC4FF458 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0B3FB4FF40063204631460093B5 -:100E04008DF8048001F0AAFBADF80C50ADF80E503B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F096FE284639468B -:100E340001F0ECFE204600F05BF8054B1F7006B095 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4003AF901A8FFF7CEFF05B05DF804FB000086 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F0F6FA5D -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0B5F842 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BDF02C000849 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0BFFA2046D8 -:100FE40031462A4601F006FB2046FFF781FF03B095 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0BCFB00287BD0CF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BDF02C000808 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0DCFB684601F096 -:10116400F3FA0F4A236A009393424FF48063ADF875 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F075FA2859012101F03D -:10119400E9FA04B070BD00BF30010020F02C000853 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0CAF92046B1B22A4601F061 -:1011D4000CFA2046FFF78CFE02B070BD10B5094C26 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BDF02C00081C23104A434370B515 -:1012140005460F4CD058D61801F0FEF9342305FBCF -:1012240003440021E06901F075FB337923B92846B2 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400F02C00082DE9F347504C9A4694F83830A6 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0BEFE494B1844DF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F05BFA2AE03046FFF79DFF30462A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0D1FEDBE719688B8823F4EB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A400C82F0008F02C000880841E00017D03680B -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F0F2FE17 -:1014840073788DF8043001A800F0ECFE54F8040CD5 -:1014940001F0C2F8454504F11C0406F1340601D0FC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020182D0008300100201F -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F090FD042807D90522009250 -:1014E4000D480E4A40F2351100F07AFD0C4A1344BF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020C82F0008CE2F000893 -:10152400F02C0008104A08B592F838301BB90F485F -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F05AFD0A4B18441C2390F8880003FB52 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD30010020C82F000815 -:10157400F02C000837B50D4614460093069B0193E2 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F027F90DB99A -:10161400029805E0012D01D1039801E000F0F6FCE9 -:10162400B4EB500F0ED8B0FBF4F000F070FD0028BE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0DAFC01200138C00080B205B030BDE2 -:10165400FA2F0008B0F1006F10B504460BD20C4805 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:101694007230000800300008802D00082DE9F8435E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F01AFE2C464C451FDCF32000F02AFEB5 -:10170400154B002133F8140000F05CFE09280BD0BF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F00FFE0DE02FB1C5F10100204420 -:1017340031464246B8470134DDE700F003FE01209C -:10174400BDE8F8830020BDE8F88300BF1B30000823 -:101754002F30000832300008B02D00083630000861 -:10176400843500082DE9F0470646234814460D4603 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400CBFDF32000F0DEFD4FEAD41805EB04097D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F03DFE09280DD01248FFF74AFB3B -:1017C4003046FFF771FB1048FFF760FB00F0BAFDED -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0AAFD52 -:1017F4000120BDE8F08700BF4E3000082F300008FC -:1018040032300008623000088435000808B545F21B -:10181400555000F0CDFF042000F0D0FF40F6FF70DB -:1018240000F0D2FF002000F0C3FF4FF480500121EC -:10183400BDE8084000F06CBD08B57D2001F0BEF89D -:10184400003018BF012008BD4900FEF775BF4B08E2 -:1018540003EB5000FEF770BF70B586B00546FFF786 -:10186400ABF901210020FEF767FF042000F01EFB06 -:1018740003A8294600F066FB03A800F072FB08B930 -:1018840028480BE02848FFF701FB05F10C000499F8 -:10189400FFF7D9F9059B984204D02448FFF7F6FADC -:1018A40001203DE0049C2248FFF7F0FA2146214A3A -:1018B40021480023FFF7F2FE049E2048FFF7E6FAD2 -:1018C400002405F1C0411E4B009432460C311A48E5 -:1018D400FFF748FF1B48FFF7BDFA0498FFF7E4FA47 -:1018E4001948FFF7B7FA04991348FFF797F9054623 -:1018F4001648FFF7AFFA0598FFF7D6FA1448FFF732 -:10190400A9FA2846FFF7D0FA1248FFF7BFFA059B59 -:101914009D4204D01048FFF7B9FA022000E02046A7 -:1019240006B070BD7B30000899300008B63000085E -:10193400D93000084D18000800400008EC300008B9 -:1019440053180008FF3000080D3100081631000854 -:1019540029310008843500082F31000808B5022019 -:1019640000F0BCFAD8B3042000F0B8FA70B11C48F7 -:10197400FFF78CFA042000F0A5FA022000F0A2FA86 -:101984004FF40040BDE8084000F09CBA1548FFF74A -:101994007DFA082000F096FA102000F093FA202037 -:1019A40000F090FA402000F08DFA4FF48010FFF719 -:1019B40053FF022807D1082000F078FA102000F025 -:1019C40075FA00F045FB042000F07CFA022000F0D8 -:1019D40079FA4FF40040BDE8084000F067BA08BD4A -:1019E4007A310008B031000810B52548FFF74EFAE7 -:1019F4004FF40010FFF730FF10B102283AD809E085 -:101A0400202000F05FFA402000F05CFA802000F013 -:101A14004DFA2FE0082000F049FA102000F046FAB1 -:101A2400202000F05BFA20B91648FFF72FFA202097 -:101A340007E0402000F052FA58B91348FFF726FA9D -:101A4400402000F033FA4FF4007000F02FFA00F059 -:101A5400FFFA0E48FFF71AFA082000F033FA1020B4 -:101A640000F030FA202000F02DFA402000F02AFA8D -:101A7400002400E00124042000F024FA204610BDD4 -:101A8400CB310008E53100081E3200085732000847 -:101A940008B50248FFF7FAF900BEFEE791320008E4 -:101AA4001EF0040F0CBFEFF30880EFF30980FFF77B -:101AB400EFBF70471FB504461048FFF7E7F901A9C7 -:101AC4000C22204600F02FFB01A8FFF7DFF9204687 -:101AD400FEF74AFEFEF7FAFF094B20F004001870E7 -:101AE4001C46FEF7F3FF237800F0FB00834201D08D -:101AF40000F0AEFA0A2000F000FBF2E7873200089B -:101B0400690100207FB54FF08050012100F002FFF1 -:101B1400022000F065FE012807D1022000F06AFED1 -:101B2400BB48FFF7B3F900F098FA00214FF080505A -:101B340000F0F0FEFFF734F9B648FFF7A7F9B6480E -:101B4400FFF7A4F9B548FFF7A1F9B548FFF79EF9E7 -:101B5400B448FFF79BF9B448FFF798F9B348FFF787 -:101B640095F9B348FFF792F9B248FFF78FF9B248F5 -:101B7400FFF78CF9FFF760FCFEF710FF00F0B8F9EF -:101B840000F0CEF900210C2201A801F060F800F069 -:101B9400C3F901A90C2200F0C6FA01A8FFF776F9EF -:101BA4009C48FFF773F99B48FFF770F94FF48040A6 -:101BB40000F094F990B1A148FFF768F94FF4804020 -:101BC40000F080F9082000F07DF9102000F07AF987 -:101BD400202000F077F9402000F074F9FEF7D8FDDA -:101BE400FEF782FFFEF74EFDFEF706FB08B19448B0 -:101BF40003E0FEF745FB10B19248FFF75BFF00F0EE -:101C04008AF9802000F06AF918B1802000F05AF9AE -:101C140056E04FF4003000F061F9044620B14FF46F -:101C2400003000F04FF94DE00120FEF739FFB0B16C -:101C34002046FEF735FF90B18348FFF727F941F2BC -:101C440088340120FEF72CFF98B10020FEF728FF0E -:101C540078B1012000F051FA013CF2D132E07B4B23 -:101C64000CCB013301D0013203D17948FFF70EF9CF -:101C740028E0FFF7E1FD48B3FFF7DEFD10B175483A -:101C8400FFF704F94FF4007000F028F910B172481E -:101C9400FFF7FCF84FF4007000F014F9102000F086 -:101CA4001DF9002800F0EE806C48FFF7EFF80820DB -:101CB40000F008F9102000F005F9FFF795FE98B937 -:101CC40067489AE76748FFF7E1F8F6E74FF40070D2 -:101CD40000F004F90028CFD1082000F0F3F8102018 -:101CE40000F0F0F8FFF73AFE4FF4005000F0F6F879 -:101CF40004464FF4805000F0F1F8400040EA8400BC -:101D0400C4B24FF4006000F0E9F82043C0B20728E1 -:101D1400C3B20DD14FF4006000F0D4F84FF48050FA -:101D240000F0D0F84FF4005000F0CCF84E4864E7CF -:101D34000133DBB2023B4FF40060052B11D8DFE81E -:101D440003F00C1003100C1000F0BCF84FF480509A -:101D540000F0B8F84FF4005003E000F0B3F84FF48B -:101D6400805000F0A3F8FFF751FD384B3F485D6801 -:101D74001E68FFF76FF82846FFF796F83C48FFF710 -:101D840069F80022930003F1604303F561430132D3 -:101D94004FF0FF31082AC3F88010C3F88011F1D145 -:101DA400344B4FF4801200241A635C639C631C64FC -:101DB4005C6400F02FFD3048012100F0C3FD214692 -:101DC4002D4800F0BFFDF120012100F0C7FD2146A0 -:101DD400F12000F0C3FD0120014600F0CBFD2146B7 -:101DE400012000F0C7FD2548012100F0CFFD214668 -:101DF400224800F0CBFD2248012100F0D3FD204809 -:101E0400214600F0CFFD63B64FF0FF3EB5462847AC -:101E14009C32000884350008A9320008C03200084A -:101E2400D932000807330008363300086633000847 -:101E340097330008C9330008FB3300082C3400082A -:101E4400014550FE024550FE593400080040000888 -:101E54008B3400089E340008B6340008D6340008D9 -:101E6400034550FE4A350008044550FE6935000814 -:101E74007D350008003802400010E022FFC9FEF65C -:101E8400337F7704082000F029F820B10548FEF7D5 -:101E9400FDFF102003E00448FEF7F8FF082000F0DF -:101EA40005F81FE7FE3400082435000810B5044681 -:101EB400002000F0A7FD40EA04010020BDE8104026 -:101EC40000F090BD10B50446002000F09BFD20EA10 -:101ED40004010020BDE8104000F084BD10B50446A4 -:101EE400002000F08FFD204214BF0120002010BD0F -:101EF40008B54FF08050012100F00CFD012000F0E6 -:101F040069FC0120FFF7EAFF20B90121BDE8084080 -:101F140000F068BD08BD7047022000F073BD0000EA -:101F240008B5FFF7F9FF054B1968884204D0022071 -:101F3400BDE8084000F056BD08BD00BF840100089C -:101F440000BEFDE71FB504460C2201A8FEF71CFCE9 -:101F540001AB03CB20601868A0602046616004B028 -:101F640010BD0068A0F10C035842584170470000AE -:101F740080B50646174610480D461C46FEF76AFF14 -:101F84003846FEF767FF0D48FEF764FF3046FEF75C -:101F940061FF0B48FEF75EFF2846FEF785FF2CB174 -:101FA4000848FEF757FF2046FEF754FF0648FEF7A1 -:101FB4006DFFFFF7C5FF00BF853500088E350008AB -:101FC400913500088F350008843500081FB506AA2E -:101FD40052F8044B039200921A462346FFF7C8FFB7 -:101FE4000CB41FB506AA52F8043B03920092014AAE -:101FF400FFF7BEFF9335000807B500230093724630 -:10200400014BFFF7E3FF00BF9A350008744608B59B -:102014000548FEF71FFF2046FEF746FF0348FEF77C -:1020240035FFFFF78DFF00BFA23500088435000897 -:10203400BFF34F8F0449054BCA6802F4E0621343AF -:10204400CB60BFF34F8FFEE700ED00E00400FA051C -:1020540008B5FEF791FBFFF7EBFF08B5FFF7E8FFC4 -:10206400F0B50646002401202546034694421DD0BF -:1020740011F804C000F1010EBCF1000F03D1735537 -:10208400774605460DE00133DBB2FF2B774606F8B1 -:1020940000C007D102F1FF3C644506D07355871C8C -:1020A4007546012301343846E0E770467355F0BDA8 -:1020B40030B5C9B1C0430A44914213D011F8013B71 -:1020C4000A4D83EA000404F00F0455F8244084EA1E -:1020D400101080EA131303F00F0355F8233083EA3A -:1020E4001010E9E7C04330BD084630BDC82D0008D4 -:1020F400162358430138FDD1704710B504462CB15E -:102104004FF47A70FFF7F4FF013CF8E710BD10B507 -:10211400B0FA80F400F027F801280CBFC4F11F00C6 -:10212400C4F1200010BD0A2A10B51BDD0C46302373 -:1021340004F8023B78234B701C220F239340034086 -:10214400D340092B01D8303302E00F2B02D8573388 -:10215400DBB200E02023043A04F8013B131DECD168 -:1021640000238B7210BD00F0AA33A0EB530000F0E3 -:10217400CC3300F0333000EB930000EB101000F090 -:102184000F3000EB102000EB104000F03F007047D0 -:102194002D4AD2F8883043F47003C2F888302B4BB0 -:1021A4001A68002142F001021A6099601A6822F04C -:1021B400847222F480321A60254A5A601A6822F422 -:1021C40080221A60D9601A6C224942F080521A6443 -:1021D4000A6842F440420A609A689A609A689A606F -:1021E4009A6842F480529A601B4A5A601A6842F014 -:1021F40080721A601968154A8901FBD5174B40F2A1 -:1022040003611960936823F003039360936843F0B8 -:10221400020393600D4B9A6802F00C02082AF9D16C -:102224009A6822F400029A600D4AC3F884201A685E -:1022340042F080621A60054B1B681B01FBD5024B00 -:102244004FF000629A60704700ED00E000380240F1 -:1022540010300024007000401040010F003C024088 -:1022640000300050C278037810B512B3164AD16812 -:102274004278C943C1F30221C1F10404E4B2A2408B -:10228400D4B20F220A4181780A40224303F1604309 -:1022940003F561431201D2B283F8002303780122CB -:1022A400590903F01F0302FA03F3084A42F82130E4 -:1022B40010BD5A09012103F01F03994002F12003C4 -:1022C400024A42F8231010BD00ED00E000E100E0F6 -:1022D400014B01229A60704700300240014B1860A4 -:1022E400186870470030024000EB81018842044BBB -:1022F40003D050F8042B1A60F8E71868704700BF41 -:1023040000300240014B1868704700BF00300240A3 -:10231400044B9A6809B1104301E022EA0000986076 -:10232400704700BF002004E0044B1A69002ABFBFB5 -:10233400034A5A6002F188325A607047003C0240F6 -:1023440023016745024A136943F000431361704750 -:10235400003C0240014BD860704700BF003C024083 -:102364000E4BDA68D20310D4DA68D1060FD4DA68D7 -:10237400D2050ED4DA6812F0EF0F0CD1DB6813F03B -:10238400020F14BF0820092070470120704706205F -:102394007047022070470720704700BF003C02408E -:1023A40007B509238DF80730FFF7DAFF8DF807002A -:1023B4009DF80730012BF7D09DF8070003B05DF8B6 -:1023C40004FB000070B5064641B1012908D002297A -:1023D4000CBF4FF400754FF4407503E00D4601E067 -:1023E4004FF48075FFF7DCFF09281ED10F4C2369D9 -:1023F40023F440732361216929432161236923F074 -:10240400F8032361236943F002031E432661236911 -:1024140043F480332361FFF7C3FF236923F00203EE -:102424002361236923F0F803236170BD003C02405B -:1024340070B505460E46FFF7B3FF092811D1094CC4 -:10244400236923F44073236123692361236943F0DF -:10245400010323612E70FFF7A3FF236923F0010317 -:10246400236170BD003C024070B543688668856A8C -:102474000468416B46EA0302C3681A4303691A43BA -:1024840043691A4383691A43C3691A43036A1A43A3 -:10249400436A1343C26A2B431A43036B1343A200D8 -:1024A40002F120420B43082E136002BF136843F06D -:1024B40040031360B1F5801F816B12D18CB14FF0D2 -:1024C40020435C681E6846F480161E601E6846F44D -:1024D40080761E600E6924F47004013E44EA0654BA -:1024E4005C608C690B6823434C6843EA04138C6872 -:1024F40043EA0423CC6843EA04430C69496943EA88 -:10250400045343EA0163B5F5804F536010D1C16BA6 -:1025140088690B680343486843EA0013886843EA00 -:1025240000230869496943EA005343EA016301E06F -:102534006FF07043C2F8043170BD0000800000F1F8 -:10254400204019B1036843F0010302E00268024B22 -:1025540013400360704700BFFEFF0F00F0B50E6824 -:1025640000220123934003EA060E9E452AD155001A -:1025740003230468AB40DB431C4004600C79076808 -:1025840004FA05FC013C4CEA0707012C076011D84A -:1025940084684F791C4084608468AF402743876017 -:1025A400446824EA0E0444608C7947689440A4B2D9 -:1025B4003C434460C4682340C360CB79C468AB40E7 -:1025C4002343C3600132102ACBD1F0BD4FF6FF7311 -:1025D40003600023037143718371C37170470369FE -:1025E400194214BF0120002070470AB101837047CB -:1025F4004183704701F00703C90800EB810010B55F -:102604009B00046A0F21994024EA01010162016AD6 -:102614009A401143016210BD08B5134B984207D18B -:102624004FF40010012100F0B1F94FF4001014E050 -:102634000E4B984207D14FF48000012100F0A6F917 -:102644004FF4800009E00A4B98420BD14FF400008C -:10265400012100F09BF94FF400000021BDE808407F -:1026640000F094B908BD00BF005400400058004079 -:10267400005C00407FB5868826F03F060446360499 -:1026840068460D46360C00F0E9F8029A2C48B2FB75 -:10269400F0F081B20E43A68023882A4E23F0010372 -:1026A4001B041B0C23802B68B3420AD85B00013146 -:1026B400B2FBF3F39BB2032B89B298BF04232184AA -:1026C40023E0EE884BF6FF718E421BBF19214B436A -:1026D40003EB4303B2FBF3F315BF9BB2B2FBF3F37B -:1026E40043F480439BB2C3F30B020AB943F00103E2 -:1026F4004FF4967101FB00F24FF47A7192FBF1F200 -:10270400013292B243F400432284A383238869896B -:10271400AA889BB243F001032380238823F48163B6 -:1027240023F002031B040A431B0C13439BB22380B4 -:102734002A89AB8913439BB2238104B070BD00BFC7 -:1027440040420F00A086010041F288330360002359 -:1027540083804BF6FF72038143814FF48043C28030 -:1027640083817047038819B19BB243F0010303E0EE -:1027740023F001031B041B0C03807047038819B169 -:102784009BB243F4806303E023F480631B041B0CBB -:1027940003807047838A9AB2038B10B542EA0343DD -:1027A40021F07F4404EA0300431A5842584110BD03 -:1027B400014B1860704700BF00300040014B586067 -:1027C400704700BF00300040014B9860704700BF65 -:1027D40000300040014B1860704700BF20000E42DB -:1027E400034B5B68184214BF01200020704700BFF0 -:1027F40000700040064BB0F5402F15BF1A685A68A8 -:1028040042EA800042F4402214BF18605A607047C4 -:10281400007000400E4B1A68002142F001021A6059 -:1028240099601A6822F0A85222F410221A60094A08 -:102834005A60094AC3F8842002F18062C3F88820F0 -:102844001A6822F480221A60D960C3F88C10704789 -:102854000038024010300024003000201D4A9368E4 -:1028640003F00C03042B10B503D0082B03D01A4B30 -:1028740018E01A4B16E05168536811F4800F03F006 -:102884003F03516814BF154A134AB2FBF3F3114ACC -:102894005268C2F30142C1F3881101324B43520022 -:1028A400B3FBF2F30B4A036093680D49C3F30313BC -:1028B400CC5C0368E34043609468C4F382240C5DF9 -:1028C40023FA04F484609268C2F342328A5CD340EF -:1028D400C36010BD003802400024F40040787D013C -:1028E40001000020044B1A6B09B1104301E022EAF5 -:1028F40000001863704700BF00380240044B9A6B15 -:1029040009B1104301E022EA00009863704700BF58 -:1029140000380240044B1A6C09B1104301E022EA6A -:1029240000001864704700BF00380240044B5A6C22 -:1029340009B1104301E022EA00005864704700BF67 -:1029440000380240044B1A6909B1104301E022EA3D -:1029540000001861704700BF00380240044B5A69F8 -:1029640009B1104301E022EA00005861704700BF3A -:1029740000380240044B9A6909B1104301E022EA8D -:1029840000009861704700BF00380240044B1A6A87 -:1029940009B1104301E022EA00001862704700BF49 -:1029A40000380240044B5A6A09B1104301E022EA9C -:1029B40000005862704700BF003802404209012AF3 -:1029C400074B01D11B6803E0022A0CBF1B6F5B6F2E -:1029D40000F01F0023FA00F000F00100704700BF70 -:1029E4000038024082B000230193054B0193019B00 -:1029F40003EB80000190019B196002B0704700BF97 -:102A04005028004082B000230193054B0193019BA1 -:102A140003EB80000190019B186802B0704700BF6F -:102A24005028004008B5254B984207D14FF48050F8 -:102A34000121FFF7B7FF4FF4805039E0204B984253 -:102A440007D14FF480400121FFF7A0FF4FF48040ED -:102A540009E01C4B98420BD14FF400400121FFF7D1 -:102A640095FF4FF400400021BDE80840FFF78EBFFA -:102A7400154B984207D14FF400500121FFF792FF04 -:102A84004FF4005014E0114B984207D14FF48010DA -:102A94000121FFF787FF4FF4801009E00C4B9842A7 -:102AA4000BD14FF400100121FFF77CFF4FF400100D -:102AB4000021BDE80840FFF775BF08BD00300140A4 -:102AC40000380040003C0040003401400050014008 -:102AD4000054014003884A8810B503F441540B881C -:102AE40013438A881343CA8813430A8913434A89C0 -:102AF40013438A891343CA89134323439BB2038034 -:102B0400838B23F400631B041B0C83830B8A0382D3 -:102B140010BD0023038043808380C38003814381ED -:102B24008381C381072303827047038819B19BB251 -:102B340043F0400303E023F040031B041B0C038019 -:102B44007047808980B27047818170470389194238 -:102B540014BF0120002070477FB5038ACA889BB246 -:102B640023F440531343038283890E469BB223F418 -:102B7400B053098923F00C02B3880B4371890B43CA -:102B84009BB213438381838AB2899BB223F440733B -:102B94001343838204466846FFF760FE1A4B9C4247 -:102BA40003D003F580639C4201D1039D00E0029DA4 -:102BB400A38931681BB2002B4FF0190202FB05F206 -:102BC400B4BF4D008D00B2FBF5F5A2896426B5FBB8 -:102BD400F6F00001010912B2002A06FB1153ADBF41 -:102BE4001A01D90032313232B5BFB1FBF6F2B2FB71 -:102BF400F6F302F0070203F00F03B4BF10431843C7 -:102C040080B2208104B070BD00100140838919B1E5 -:102C14009BB243F4005303E023F400531B041B0C46 -:102C240083817047C1F3080181807047038819428A -:102C340014BF01200020704710B5431E0A4491427E -:102C440004D011F8014B03F8014FF8E710BD02441A -:102C54000346934202D003F8011BFAE770470000D1 -:102C64000400000001030000010000000100000056 -:102C740003000000010000000F0000000F0000002E -:102C840000000000AE2F00080018024010000000F1 -:102C940006040000B32F00080018024008000000DA -:102CA40006030100B62F00080018024002000000CD -:102CB40006010100BD2F00080018024004000000B6 -:102CC40006020100000000000000000001424F4F16 -:102CD400544C4F414445520000000000002A2A0091 -:102CE400000000000000000000000000005400404C -:102CF40000040240400000000600040000040240FA -:102D0400000200000900040000002000801A0600F0 -:102D1400004000001F200000000000000000000030 -:102D240000580040001402400200000001000400AA -:102D340000140240010000000000040000004000F4 -:102D4400801A0600FFBF00002122000000000000DE -:102D54000116000801000300820007008400070038 -:102D6400010003000200030040000300800007008C -:102D740084000700000001000000000000000008BB -:102D8400004000080080000800C00008000001089E -:102D940000000208000004080000060800000808FB -:102DA40000000A0800000C0800000E0800000800DB -:102DB40010001800200028003000380040004800AF -:102DC40050005800000000006410B71DC8206E3B7E -:102DD400AC30D9269041DC76F4516B6B5861B24D1E -:102DE4003C7105502083B8ED44930FF0E8A3D6D688 -:102DF4008CB361CBB0C2649BD4D2D38678E20AA0F0 -:102E04001CF2BDBD537475636B20627574746F6E70 -:102E140020726567697374657220697320696E76C0 -:102E2400616C69642C20636C656172696E672E0045 -:102E3400427574746F6E206973207075736865646D -:102E440020617420626F6F740069636534306C7044 -:102E54002E630043444F4E45206E6F74206C6F7791 -:102E640020647572696E67207265736574004352DD -:102E740045534554206E6F742068696768206475F3 -:102E840072696E6720726573657400446973706C4F -:102E9400617920627573792D776169742074696D25 -:102EA400656F757420657870697265642100436F7D -:102EB4006E6669677572696E672046504741206681 -:102EC400726F6D2062697473747265616D20696ECE -:102ED40020666C6173682E2E2E004E6F204650477C -:102EE400412062697473747265616D20696E206635 -:102EF4006C6173682E0046616C6C696E6720626158 -:102F0400636B20746F204E56434D2E00465047414C -:102F14002043444F4E452074696D656F7574206578 -:102F240078706972656421004650474120636F6E72 -:102F340066696775726174696F6E206661696C6534 -:102F4400642E204973207468697320612062696764 -:102F5400626F6172643F0046504741207665727328 -:102F6400696F6E3A2000446973706C617920696EF0 -:102F7400697469616C697A6564206166746572203C -:102F84000020726574726965732E00446973706CF5 -:102F9400617920696E697469616C697A6174696FB9 -:102FA4006E206661696C65642E004261636B005536 -:102FB400700053656C65637400446F776E006372D0 -:102FC400632E63006932632E6300493243206465D3 -:102FD40076696365204944206F7574206F662062AA -:102FE4006F756E647320256420286D61783A2025FE -:102FF4006429006F6E007370692E6300206973206A -:103004006F7574736964652073797374656D206674 -:103014006C6173680D0A0073797374656D5F666C17 -:103024006173685F657261736528002C2000290D47 -:103034000A006661696C656420746F20657261734F -:103044006520736563746F72200073797374656DA2 -:103054005F666C6173685F77726974652800666186 -:10306400696C656420746F2077726974652061648B -:1030740064726573732000496E76616C69642066BE -:1030840069726D77617265206465736372697074C7 -:10309400696F6E2100436865636B73756D6D696E4E -:1030A40067206669726D77617265207570646174FA -:1030B4006500496E76616C6964206669726D77613A -:1030C40072652043524320696E2053504920666C38 -:1030D400617368210065726173655F6F6C645F661C -:1030E40069726D776172650077726974655F6E6588 -:1030F400775F6669726D7761726500436865636BBB -:1031040073756D6D696E6720002062797465730D47 -:103114000A00436865636B73756D202D2077616EBB -:10312400746564200020676F7420004F75722069F5 -:103134006E7465726E616C20666C61736820636F77 -:103144006E74656E747320617265206261642028F8 -:10315400636865636B73756D206661696C6564296A -:10316400212054686973206973207265616C6C79DD -:103174002062616421004F75722070726576696FF8 -:103184007573206669726D7761726520757064610C -:103194007465206661696C65642C2061626F727469 -:1031A400696E67207570646174652E004E657720C2 -:1031B4006669726D776172652069732061766169F1 -:1031C4006C61626C6521004C6F6164696E6720728A -:1031D40065636F76657279206669726D7761726571 -:1031E400004661696C656420746F206C6F616420B3 -:1031F4007265636F76657279206669726D77617244 -:10320400652C20737472696B65206F6E652E205473 -:10321400727920616761696E2E004661696C65642C -:1032240020746F206C6F6164207265636F766572C1 -:1032340079206669726D776172652C207374726986 -:103244006B652074776F2E205472792061676169F1 -:103254006E2E004661696C656420746F206C6F612A -:1032640064207265636F76657279206669726D7722 -:103274006172652C20737472696B65207468726561 -:10328400652E20534144205741544348004841523D -:1032940044204641554C5400657869742073746128 -:1032A4006E64627900205F5F5F5F20202020202011 -:1032B4002020202020202020205F5F002F5C202061 -:1032C4005F605C20202020202020202020202F2729 -:1032D4005F5F605C005C205C2C5C4C5C5F5C20206D -:1032E40020205F5F5F202F5C205C2F5C205C20200F -:1032F4005F5F20205F5F20205F5F20205F5F2020D2 -:103304005F5F00205C2F5F5C5F5F205C20202F27C5 -:10331400205F20605C205C205C205C205C2F5C20B3 -:103324005C2F5C205C2F5C205C2F5C205C2F5C207D -:103334005C002020202F5C205C4C5C205C2F5C20F7 -:103344005C2F5C205C205C205C5F5C205C205C204B -:103354005C5F2F205C5F2F205C205C205C5F5C2026 -:103364005C002020205C20605C5F5F5F5F5C205C11 -:103374005F5C205C5F5C205C5F5F5F5F2F5C205C58 -:103384005F5F5F785F5F5F2F275C2F605F5F5F5FCA -:10339400205C00202020205C2F5F5F5F5F5F2F5C3C -:1033A4002F5F2F5C2F5F2F5C2F5F5F5F2F20205CD0 -:1033B4002F5F5F2F2F5F5F2F202020602F5F5F5FC5 -:1033C4002F3E205C002020202020202020202020B0 -:1033D40020202020202020202020202020202020E9 -:1033E40020202020202020202020202020202020D9 -:1033F4002F5C5F5F5F2F00202020202020202020D2 -:1034040020202020202020202020202020202020B8 -:1034140020202020202020202020202020202020A8 -:1034240020205C2F5F5F2F004C61737420666972EB -:103434006D7761726520626F6F742077617320739A -:103444007461626C653B20636C6561722073747295 -:10345400696B657300486F6C6420646F776E2055E8 -:1034640050202B204241434B20666F72203520733D -:103474006563732E20746F20666F7263652D626FAF -:103484006F7420505246004669726D7761726520F0 -:10349400697320657261736564005761746368645D -:1034A4006F6720636175736564206120726573655D -:1034B4007400536F667477617265206661696C7518 -:1034C400726520636175736564206120726573653C -:1034D40074004661696C656420746F207374617252 -:1034E40074206669726D776172652C2073747269D9 -:1034F4006B652074687265652E004661696C65644D -:1035040020746F207374617274206669726D7761C0 -:1035140072652C20737472696B652074776F2E004A -:103524004661696C656420746F20737461727420E1 -:103534006669726D776172652C20737472696B654C -:10354400206F6E652E00466F7263652D626F6F7417 -:10355400696E67207265636F76657279206D6F643A -:10356400652E2E2E00426F6F74696E672066697235 -:103574006D77617265204020002E2E2E0D0A0D0AF3 -:10358400004153534552543A20002020003A004150 -:103594005353455254004153534552544E002A2A22 -:1035A4002A20575446200053544D33320053544D6F -:1035B4003332207065726970686572616C206C6961 -:1035C4006272617279207472697070656420616ED0 -:1035D400206173736572740043524F414B204F4F07 -:0435E4004D00000096 -:1035E800FF000000000102030401020304060708AB -:0435F80009000000C6 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_spalding@1478015115.hex b/bin/boot/nowatchdog_boot_spalding@1478015115.hex deleted file mode 100644 index becfde4b52..0000000000 --- a/bin/boot/nowatchdog_boot_spalding@1478015115.hex +++ /dev/null @@ -1,869 +0,0 @@ -:020000040800F2 -:1000000070210020B5010008F9010008A51A0008B8 -:10001000F9010008F9010008F901000800000000DA -:10002000000000000000000000000000F9010008CE -:10003000F901000800000000F9010008F9010008BA -:10004000F9010008F9010008F9010008F9010008A8 -:10005000F9010008F9010008F9010008F901000898 -:10006000F9010008F9010008F9010008F901000888 -:10007000F9010008F9010008F9010008F901000878 -:10008000F9010008F9010008F9010008F901000868 -:10009000F9010008F9010008F9010008F901000858 -:1000A000F9010008F9010008F9010008F901000848 -:1000B000F9010008F9010008F9010008D11500084C -:1000C000DD150008E9150008F5150008F90100081C -:1000D000F9010008F9010008F9010008F901000818 -:1000E000F9010008F9010008F9010008F901000808 -:1000F000F9010008F9010008F9010008F9010008F8 -:10010000F9010008F9010008F9010008F9010008E7 -:10011000F9010008F9010008F9010008F9010008D7 -:10012000F9010008F9010008F9010008F9010008C7 -:10013000F9010008F9010008F9010008F9010008B7 -:10014000F9010008F9010008F9010008F9010008A7 -:10015000F9010008F9010008F9010008F901000897 -:10016000F9010008F9010008F9010008F901000887 -:10017000F9010008F9010008F9010008F901000877 -:04018000F901000879 -:100184008BB8185876342E332D616C706861322D1B -:1001940033342D6738663564383732000000000088 -:0F01A40000000000386635643837320000000173 -:1001B400002103E00A4B5B584350043109480A4BC1 -:1001C40042189A42F6D3094A02E0002342F8043B5B -:1001D400074B9A42F9D301F0DBFF01F093FC70471F -:1001E400E83500080000002014000020140000205E -:1001F4006C010020FEE7000030B501208BB002F056 -:1002040001FC002401902546E0B200F049FC01AA5B -:1002140008B9105519E0135D042B10DD164800F0E1 -:1002240035FE02A92022019801F07DFF02A800F00A -:100234002DFE0120002102F0D5FB00201AE001333D -:10024400DBB2052B135528BF01250134042CDBD167 -:10025400019C54B1094800F019FE204602A920224D -:1002640001F061FF02A800F011FE0120214602F016 -:10027400B9FB28460BB030BD082E0008342E000808 -:1002840008B500F017FB80F00100C0B208BD000003 -:1002940008B54FF48061034802F0A1F9003018BF9B -:1002A400012008BD001802402DE9F0434A4C89B0F2 -:1002B400204600F005FE20460D21052202F09AF9A1 -:1002C40020460E21052208AE02F094F905222046AC -:1002D4000C2102F08FF94FF4005346F81C3D002521 -:1002E40001274FF00209204631468DF80A508DF857 -:1002F4000B508DF808908DF8097002F02FF94FF427 -:10030400804320463146019302F028F94FF480538C -:100314002046314601934FF4007802F01FF920463D -:1003240031468DF808508DF80B70CDF8048002F03A -:1003340015F94FF480632046314601938DF8085037 -:100344008DF80B5002F00AF94FF4807320463146C1 -:1003540001938DF808708DF80B5002F0FFF84FF4FC -:1003640000432046314601938DF80A708DF80B50F6 -:1003740002F0F4F839464FF4001002F0D7FAA4F56D -:100384004444164B164E1E60204602F04BFB03A855 -:1003940002F0BFFB4FF4827339463046ADF80E309D -:1003A400ADF80C50ADF81050ADF81290ADF81470D3 -:1003B400ADF8168001F024F903A9ADF81800204621 -:1003C400ADF81A5002F086FB2046394602F0ADFB28 -:1003D40009B0BDE8F08300BF0018024014000020FB -:1003E4000024F40008B5022001F087FE012000F08B -:1003F40045FB022001F081FE0120BDE8084000F029 -:100404002BBB000010B504460548022102F09EFBF8 -:100414000028F9D021460248BDE8104002F094BB00 -:100424000054014038B5012204460D4629484FF4D2 -:10043400807102F0DAF8012001F05FFE25484FF4E4 -:100444000041002202F0D1F84FF4807100222148CB -:1004540002F0CBF8012001F050FE4FF400411D489A -:10046400012202F0C2F8012001F047FE19484FF4BE -:10047400007102F0B4F818B11748184A6A210AE06A -:1004840014484FF4004102F0AAF808B1254404E0EE -:100494001148134A6B2101F0A3FDAC4204D014F8B7 -:1004A400010BFFF7AFFFF8E70A484FF48071012210 -:1004B40002F09BF808240020FFF7A4FF013CFAD1C6 -:1004C4004FF40071034802F08AF8003018BF01208D -:1004D40038BD00BF001802404D2E0008572E0008FA -:1004E400722E000810B504460B4802F02AFB0A4895 -:1004F400022102F02BFB0028F9D00748214602F024 -:1005040023FB0548012102F021FB0028F9D0024811 -:1005140002F017FBC0B210BD0054014010B54FF4F7 -:10052400807104460022054802F05FF8642001F05F -:10053400DFFD2046BDE81040FFF764BF001802400D -:1005440008B50748802102F001FB0028F9D10548CD -:100554004FF480710122BDE8084002F046B800BFA4 -:10056400005401400018024010B540F2F514FFF7A2 -:100574008FFE50B1013C04D1054800F087FC2046B1 -:1005840010BD642001F0B4FDF1E7012010BD00BFEF -:100594008F2E000810B5224C638822884FF6FF7115 -:1005A400DB438A429BB20BD09A4209D11D4800F02A -:1005B4006DFC1D482188FFF735FF20B1012010BDD7 -:1005C4001A4800F063FC1A4800F060FC19484FF424 -:1005D4008071012202F009F84FF40041002215480D -:1005E40002F003F8012001F088FD12484FF40041A5 -:1005F400012201F0FAFF40F2E9340E484FF4007191 -:1006040001F0EDFF0028D9D1013C04D10A4800F0E3 -:100614003DFC204610BD642001F06AFDEDE700BFFB -:1006240000000F60B22E000804000F60DE2E0008E8 -:10063400FA2E000800180240102F000810B50446D6 -:100644000420FFF76BFF2046FFF7DCFEBDE81040F7 -:10065400FFF776BF10B504460120FFF75FFFE0B255 -:10066400FFF7D0FEC4F30720FFF7CCFEC4F3074026 -:10067400FFF7C8FE200EFFF7C5FEBDE81040FFF7E8 -:100684005FBF000010B5FFF70FFEFFF783FF08B947 -:10069400224830E0224800F0DDFB4FF48071002254 -:1006A400204801F0A2FF642001F022FD0020FFF7A2 -:1006B40019FF4FF48071012204461A4801F095FF96 -:1006C400204600F0F1FB184800F0E0FBFFF78AFE3B -:1006D40000240120FFF7B2FFFFF746FF78B10320A3 -:1006E400FFF71CFFFFF72CFF104800F0B3FB204678 -:1006F40000F0DAFB0E48BDE8104000F0C7BB01343F -:10070400FFF748FF152CE4D10A4800F0BFFB022094 -:10071400FFF704FFBDE81040FFF712BF2C2F0008BD -:100724005B2F000800180240843500086A2F000877 -:10073400852F00088F2F000808B55D235843094B07 -:100744004A1C00EB52001A78B0FBF1F0C0B28242AE -:1007540007D01870FFF77EFF0220BDE80840FFF7BE -:100764006DBF08BD0000002008B5FFF773FF03202C -:10077400BDE80840FFF762BF08B50220FFF7CEFED0 -:10078400BDE80840FFF7DCBE01F1C04102F054BAF5 -:1007940070B543489AB000F093FB424800F090FBD8 -:1007A400414800F08DFB414B93E8030001AD85E81F -:1007B40003003C48294601F0D1FE3A4804210122B5 -:1007C40001F013FF00240223029435488DF8083009 -:1007D400072103230C228DF80930019401F00AFF4C -:1007E40080232F480193294601F0B8FE022C04D03F -:1007F4002C48A1B20C2201F0FDFE0134102CF5D1DD -:100804004FF6FB7327480193294601F0A7FE022601 -:100814003146013624480C22B6B201F0EBFE102E0C -:10082400F6D14FF6FC7329461F48019301F096FE5A -:10083400042100221B481E4D01F0D7FE0A2001F0BE -:1008440057FC01220421174801F0CFFE1E2001F0BD -:100854004FFC0120014602F051F80FCD03AC0FC448 -:1008640095E8070084E80700402200210AA802F066 -:10087400EEF902230B9308230C934FF48053129345 -:100884004FF4005313930AA803AB18930D9601F089 -:10089400EBFD0020012101F051FE1AB070BD00BF34 -:1008A40000040240000C024000100240642C0008C6 -:1008B4006C2C00080D4B98221A80A3F68A231B88FF -:1008C400DBB2512B0CD10A4B1B88DBB2522B07D164 -:1008D400084B1888C0B2A0F159035842584100E0AF -:1008E4000020024BF0221A80704700BFAA0A006061 -:1008F400220000602400006002460B4604205021C0 -:1009040000F045BE02460B460420502100F054BEC0 -:10091400032873B50E4603D0042804D001F076FBF7 -:1009240014241C4D01E01C4D162420460DF1070132 -:10093400FFF7E2FF08B900202AE02B78AEB10BB133 -:10094400013323E09DF8071021F0060141F0020174 -:1009540020468DF80710FFF7D5FF0028EBD00124BF -:1009640003202C7001F0C9FB11E05AB2012A0CDCFF -:100974009DF80710204601F0F9018DF80710FFF7E4 -:10098400C1FF0028D7D02E7001E0013B2B7001205D -:1009940002B070BD180000201900002073B525486E -:1009A40000F08EFA02254FF410730024012621482A -:1009B400009369468DF804508DF805508DF8066053 -:1009C4008DF8074001F0CAFD1A480621042201F0FF -:1009D40011FE04220921174801F00CFE154800F00D -:1009E40087FA154800F06CFA42F20C036946124883 -:1009F40000938DF805508DF804608DF806408DF84D -:100A0400074001F0ABFD0C4800F072FA042000F03E -:100A140057FD20466946FFF76FFF9DF80050B54229 -:100A240005D103202946FFF773FF284600E020463E -:100A340002B070BD000402400014024010B5044628 -:100A4400042000F03DFD21460420FFF761FF04204F -:100A5400BDE8104000F066BD38B5074C0546204699 -:100A640000F02EFA20462A46042101F0BEFD20465D -:100A7400BDE8384000F03CBA0014024038B5074CD9 -:100A84000546204600F01CFA20462A46082101F0BB -:100A9400ACFD2046BDE8384000F02ABA00140240FC -:100AA40038B5094C04EB00146568284600F008FAD0 -:100AB4002189284601F093FD0446284600F018FADF -:100AC400B4FA84F0400938BD882C000838B50024F5 -:100AD4002546E0B2FFF7E4FFA04001340543042CAF -:100AE400EDB2F6D1284638BDF7B54FF48040012168 -:100AF400134C01F01BFF04F140076568284600F021 -:100B0400DFF9684601F062FD02238DF80530A37B0E -:100B14008DF807302846A3680093694600268DF8AF -:100B24000460103401F01AFD284600F0E1F9BC42DB -:100B3400E3D14FF48040314601F0F8FE03B0F0BD3C -:100B4400882C000808B5054B1B781BB904483221D2 -:100B540001F052FABDE8084001F0C6BB1A000020BB -:100B6400C22F0008124B1B7870B505460C461BB902 -:100B74001048382101F040FA032906D98E0831467D -:100B8400FFF7E0FFB6003544A41B6CB12B462C44A0 -:100B94000020A34204D013F8012B42EA0020F8E716 -:100BA400BDE8704001F09ABBBDE8704001F0AABBFB -:100BB4001A000020C22F0008024B1A780AB9012239 -:100BC4001A7070471A00002008B5084B1A781AB931 -:100BD40007482B2101F010FA01214FF4805059707D -:100BE40001F080FEBDE8084001F072BB1A0000204D -:100BF400C22F000808B5074B1A781AB906485121C4 -:100C040001F0FAF9002159704FF48050BDE8084012 -:100C140001F068BE1A000020C22F000870B50C460F -:100C24000546A608FFF7D0FF28463146FFF78AFF9E -:100C340004F0030105EB8600FFF794FF0446FFF779 -:100C4400D9FF204670BD70B5A0B00D460646FFF72B -:100C5400BBFF2C467119802CA1EB0401684608D90E -:100C64008022FFF791FD68462021FFF76BFF803C4F -:100C7400F0E72246FFF788FD21466846FFF772FF3A -:100C84000446FFF7B7FF204620B070BD0A4B1B781F -:100C940010B504467BB10948402101F0C7FF002884 -:100CA400F9D00648214601F0BDFF0448402101F077 -:100CB400BDFF0028F9D010BD1C00002000480040F2 -:100CC4002DE9F043334D83B06C6804F12E0323F017 -:100CD400070300AFADEB030D50238DF80030212343 -:100CE4008DF8013000238DF8023003238DF8033092 -:100CF40004F121035BBAADF80430274B04F1270853 -:100D04006E460DF1060203F11C0153F8040B42F880 -:100D1400040B8B42F9D11B78137004F1230922468A -:100D240006F123001D4901F087FF31464A460020A1 -:100D340001F0BEF907F1080304F5927443F8040DB9 -:100D4400186846F80900FE23B4FBF3F34344073361 -:100D540023F00703ADEB030D31464246684601F02C -:100D64007FF904465520FFF791FF6E466C44A64276 -:100D740007D016F8010B552808BF0020FFF786FF9F -:100D8400F5E75520FFF782FF00230C376B60BD4663 -:100D9400BDE8F0831C000020D02C000824000020B3 -:100DA4002DE9F041274C86B0204600F089F84FF435 -:100DB4008020012101F0AEFD20460A21072201F026 -:100DC40019FC06AE072220460B2101F013FC4FF458 -:100DD400806346F8183D002501274FF0020820469D -:100DE40031468DF806508DF807708DF804808DF823 -:100DF400058001F0B3FB4FF40063204631460093B5 -:100E04008DF8048001F0AAFBADF80C50ADF80E503B -:100E1400ADF81050ADF814500B4D0C4B029328460E -:100E24000C2302A9ADF8123001F096FE284639468B -:100E340001F0ECFE204600F05BF8054B1F7006B095 -:100E4400BDE8F081000802400048004040420F0025 -:100E54001C00002038B50C4B1A781C469AB1451E6C -:100E640015F8012F7AB163680749FF2B0BD80A2ABA -:100E740002D1FFF725FFF3E70D2A1FBF581C5B18AB -:100E840048601A72ECE738BD1C00002008B5054B19 -:100E94001B782BB1FFF7DEFFBDE80840FFF710BF5A -:100EA40008BD00BF1C0000201FB50C2201A901F0E1 -:100EB4003AF901A8FFF7CEFF05B05DF804FB000086 -:100EC40000F13F4000F57E00C0F3872008280AD8CF -:100ED400054A135C591C11542BB9044901230A6BAC -:100EE400834013430B637047240100200038024001 -:100EF40000F13F4000F57E00C0F3872008280DD89C -:100F0400074A135C53B1013BDBB2135433B90549AF -:100F140001230A6B834022EA03030B63704700BF7B -:100F2400240100200038024002460068838823F42C -:100F3400E0631B041B0C838003889BB243F400739F -:100F4400038008230020517513751076704737B558 -:100F540004460D46FFF7B4FF0023204669468DF88A -:100F640004308DF805308DF80730009501F0F6FA5D -:100F74002046FFF7BDFF03B030BD000010B50B4C99 -:100F8400342300FB0344236B7BB10A2001F0B5F842 -:100F94006068A168FFF7DBFF20696169FFF7D7FF8D -:100FA400236B0120BDE81040184710BDF02C000849 -:100FB400F7B5044616460F461D46FFF781FF02218A -:100FC40001238DF804108DF805108DF806302046A5 -:100FD400002369468DF80730009701F0BFFA2046D8 -:100FE40031462A4601F006FB2046FFF781FF03B095 -:100FF400F0BD0000027D082A10B5044607D102683E -:10100400938823F4C0631B041B0C938084E0434B3C -:10101400006803EB8203996E01F0BCFB00287BD0CF -:10102400237D072B72D8DFE803F0040B1921323B30 -:101034005A6E237A226803F0FE03138201232DE003 -:101044002268A37A1382637A0BB1022326E0938881 -:101054009BB243F48063938006231FE022681388C5 -:101064009BB243F480731380032317E0237A22682E -:1010740043F001031382E37A012B01BF138823F4A5 -:1010840080631B041B0C08BF1380042306E0226842 -:1010940093889BB243F480639380052323753BE0DC -:1010A40023682269198A237BD154237BE27A013392 -:1010B400DBB2591C9142237307D12268138823F4AD -:1010C40080631B041B0C138026E0934224D1226806 -:1010D400938823F480631B041B0C938013E0237B0D -:1010E40021692268CB5C1382237BE17A0133DBB272 -:1010F4009942237310D1938823F480631B041B0C3F -:1011040093800723CAE72046012101E020460021FD -:10111400BDE81040FFF708BF002010BDF02C000808 -:101124007FB51D4D1C2303FB005306461B790BB9E9 -:10113400FFF724FF194C342306FB03446068A168BD -:10114400A289A37BFFF734FF228BA37E2069616908 -:10115400FFF72EFFE069012101F0DCFB684601F096 -:10116400F3FA0F4A236A009393424FF48063ADF875 -:101174000A304FF01C0384BF626AADF8062003FBFB -:1011840006F46946285901F075FA2859012101F03D -:10119400E9FA04B070BD00BF30010020F02C000853 -:1011A400A086010073B504460E460025FFF788FEAD -:1011B4000123204669468DF8043000968DF80550C9 -:1011C4008DF8075001F0CAF92046B1B22A4601F061 -:1011D4000CFA2046FFF78CFE02B070BD10B5094C26 -:1011E400342300FB0344236B5BB100209847606801 -:1011F400A168FFF7D7FF20696169BDE81040FFF7D8 -:10120400D1BF10BDF02C00081C23104A434370B515 -:1012140005460F4CD058D61801F0FEF9342305FBCF -:1012240003440021E06901F075FB337923B92846B2 -:10123400BDE87040FFF7D2BF6068A168FFF787FE82 -:1012440020696169BDE87040FFF781BE300100206C -:10125400F02C00082DE9F347504C9A4694F83830A6 -:10126400884691461BB94E4840F2951104E0042883 -:1012740004D94B484FF4CB7100F0BEFE494B1844DF -:101284001C2590F88860754363191B790BB90020FD -:1012940081E063591B8B9F0716D41C2070432718C9 -:1012A40000259DF828300B9A87F8089087F80AA043 -:1012B40087F80980FB723D733D753A61FD822058C1 -:1012C400012101F05BFA2AE03046FFF79DFF30462A -:1012D400FFF726FF6159344B0A8B9007DDD5013B9C -:1012E400FAD1D4E71C2303FB06431B7E23B1019BE5 -:1012F4005A1E0192002BF5D11C2303FB0643002246 -:101304001A76019903F1100239B3117908291AD117 -:1013140055791C2303FB06431B7D082B2BD01C2370 -:101324007343E018E1580A8892B242F480720A804A -:10133400E25893889BB243F440739380012303766D -:10134400194B0193D3E7D188B1F57A7F05D20131E6 -:10135400D180012000F0D1FEDBE719688B8823F4EB -:10136400E0631B041B0C8B800B889BB243F400735B -:101374000B80082313711C237343E1580A4B0A8B17 -:10138400920707D5013BFAD13046FFF73DFF3046BF -:10139400FFF7C6FE284602B0BDE8F0873001002002 -:1013A400C82F0008F02C000880841E00017D03680B -:1013B400082906D19A8822F480721204120C9A80A9 -:1013C40038E09A8A120541BF9A8A22F40062120414 -:1013D400120C48BF9A829A8AD20541BF9A8A22F493 -:1013E40080721204120C48BF9A829A8A52051ED542 -:1013F4009A8A22F480621204120C04299A820AD175 -:10140400032202759A8822F4E0621204120C9A8074 -:10141400002303760EE0012909D19A8822F4E062C0 -:1014240012040021120C01759A80017602E0002159 -:10143400FFF77ABD002070472DE9F3411D4C1E4E85 -:1014440000251E4F56F8283C44F8043C1C2202FB9D -:1014540005720023237013760822227432788DF8E3 -:1014640004204FF001080C2201A82372E371E36009 -:101474008DF805208DF806308DF8078000F0F2FE17 -:1014840073788DF8043001A800F0ECFE54F8040CD5 -:1014940001F0C2F8454504F11C0406F1340601D0FC -:1014A4004546CEE7284687F83850FFF797FE02B046 -:1014B400BDE8F08134010020182D0008300100201F -:1014C40073B5144C94F838200346254622B91248C3 -:1014D40040F2331100F090FD042807D90522009250 -:1014E4000D480E4A40F2351100F07AFD0C4A1344BF -:1014F40093F888401C2303FB04521E46127912B948 -:101504002046FFF70DFE06FB0454237901332371B3 -:1015140002B070BD30010020C82F0008CE2F000893 -:10152400F02C0008104A08B592F838301BB90F485F -:1015340040F2451104E0042804D90C484FF4A37187 -:1015440000F05AFD0A4B18441C2390F8880003FB52 -:10155400002213793BB1013BDBB213711BB9BDE827 -:101564000840FFF751BE08BD30010020C82F000815 -:10157400F02C000837B50D4614460093069B0193E2 -:1015840001212A462346FFF765FE03B030BD07B5A7 -:1015940000930123FFF7EEFF03B05DF804FB37B5BA -:1015A4000D4614460093069B019300212A462346C8 -:1015B400FFF750FE03B030BD1FB504AC04F8013D85 -:1015C40001230094FFF7EBFF04B010BD0148FFF7BF -:1015D40011BD00BF300100200148FFF7E7BE00BF86 -:1015E400300100200148FFF705BD00BF4C01002079 -:1015F4000148FFF7DBBE00BF4C010020FFF71EBA15 -:1016040030B585B004460D46684601F027F90DB99A -:10161400029805E0012D01D1039801E000F0F6FCE9 -:10162400B4EB500F0ED8B0FBF4F000F070FD0028BE -:1016340002DC0748272103E0082804DD04482821A8 -:1016440000F0DAFC01200138C00080B205B030BDE2 -:10165400FA2F0008B0F1006F10B504460BD20C4805 -:10166400FFF7F8FB2046FFF71FFC0A48FFF7F2FBE1 -:101674004FF0FF3010BD084B002053F8042BA2425A -:1016840002D81A68944202D301300B28F5D110BD58 -:101694007230000800300008802D00082DE9F8435E -:1016A40006462A480C4617469846FFF7D3FB3046B1 -:1016B400FFF7FAFB2648FFF7CDFB2046FFF7F4FBC4 -:1016C4002448FFF7C7FBD4B33046FFF7C3FF0546F2 -:1016D400601E3044FFF7BEFF002D814632DB002838 -:1016E40030DB461B01361FB1002031464246B84765 -:1016F40000F01AFE2C464C451FDCF32000F02AFEB5 -:10170400154B002133F8140000F05CFE09280BD0BF -:101714001248FFF79FFB2046FFF7C6FB1048FFF770 -:10172400B5FB00F00FFE0DE02FB1C5F10100204420 -:1017340031464246B8470134DDE700F003FE01209C -:10174400BDE8F8830020BDE8F88300BF1B30000823 -:101754002F30000832300008B02D00083630000861 -:10176400843500082DE9F0470646234814460D4603 -:101774001F46FFF76FFB3046FFF796FB1F48FFF746 -:1017840069FB2046FFF790FB1D48FFF763FB00F061 -:10179400CBFDF32000F0DEFD4FEAD41805EB04097D -:1017A4002C464C45C5EB040A20D0701B204414F889 -:1017B400011B00F03DFE09280DD01248FFF74AFB3B -:1017C4003046FFF771FB1048FFF760FB00F0BAFDED -:1017D4000020BDE8F087002FE3D01AF07F0FE0D19E -:1017E4004FEADA104146089AB847DAE700F0AAFD52 -:1017F4000120BDE8F08700BF4E3000082F300008FC -:1018040032300008623000088435000808B545F21B -:10181400555000F0CDFF042000F0D0FF40F6FF70DB -:1018240000F0D2FF002000F0C3FF4FF480500121EC -:10183400BDE8084000F06CBD08B57D2001F0BEF89D -:10184400003018BF012008BD4900FEF775BF4B08E2 -:1018540003EB5000FEF770BF70B586B00546FFF786 -:10186400ABF901210020FEF767FF042000F01EFB06 -:1018740003A8294600F066FB03A800F072FB08B930 -:1018840028480BE02848FFF701FB05F10C000499F8 -:10189400FFF7D9F9059B984204D02448FFF7F6FADC -:1018A40001203DE0049C2248FFF7F0FA2146214A3A -:1018B40021480023FFF7F2FE049E2048FFF7E6FAD2 -:1018C400002405F1C0411E4B009432460C311A48E5 -:1018D400FFF748FF1B48FFF7BDFA0498FFF7E4FA47 -:1018E4001948FFF7B7FA04991348FFF797F9054623 -:1018F4001648FFF7AFFA0598FFF7D6FA1448FFF732 -:10190400A9FA2846FFF7D0FA1248FFF7BFFA059B59 -:101914009D4204D01048FFF7B9FA022000E02046A7 -:1019240006B070BD7B30000899300008B63000085E -:10193400D93000084D18000800400008EC300008B9 -:1019440053180008FF3000080D3100081631000854 -:1019540029310008843500082F31000808B5022019 -:1019640000F0BCFAD8B3042000F0B8FA70B11C48F7 -:10197400FFF78CFA042000F0A5FA022000F0A2FA86 -:101984004FF40040BDE8084000F09CBA1548FFF74A -:101994007DFA082000F096FA102000F093FA202037 -:1019A40000F090FA402000F08DFA4FF48010FFF719 -:1019B40053FF022807D1082000F078FA102000F025 -:1019C40075FA00F045FB042000F07CFA022000F0D8 -:1019D40079FA4FF40040BDE8084000F067BA08BD4A -:1019E4007A310008B031000810B52548FFF74EFAE7 -:1019F4004FF40010FFF730FF10B102283AD809E085 -:101A0400202000F05FFA402000F05CFA802000F013 -:101A14004DFA2FE0082000F049FA102000F046FAB1 -:101A2400202000F05BFA20B91648FFF72FFA202097 -:101A340007E0402000F052FA58B91348FFF726FA9D -:101A4400402000F033FA4FF4007000F02FFA00F059 -:101A5400FFFA0E48FFF71AFA082000F033FA1020B4 -:101A640000F030FA202000F02DFA402000F02AFA8D -:101A7400002400E00124042000F024FA204610BDD4 -:101A8400CB310008E53100081E3200085732000847 -:101A940008B50248FFF7FAF900BEFEE791320008E4 -:101AA4001EF0040F0CBFEFF30880EFF30980FFF77B -:101AB400EFBF70471FB504461048FFF7E7F901A9C7 -:101AC4000C22204600F02FFB01A8FFF7DFF9204687 -:101AD400FEF74AFEFEF7FAFF094B20F004001870E7 -:101AE4001C46FEF7F3FF237800F0FB00834201D08D -:101AF40000F0AEFA0A2000F000FBF2E7873200089B -:101B0400690100207FB54FF08050012100F002FFF1 -:101B1400022000F065FE012807D1022000F06AFED1 -:101B2400BB48FFF7B3F900F098FA00214FF080505A -:101B340000F0F0FEFFF734F9B648FFF7A7F9B6480E -:101B4400FFF7A4F9B548FFF7A1F9B548FFF79EF9E7 -:101B5400B448FFF79BF9B448FFF798F9B348FFF787 -:101B640095F9B348FFF792F9B248FFF78FF9B248F5 -:101B7400FFF78CF9FFF760FCFEF710FF00F0B8F9EF -:101B840000F0CEF900210C2201A801F060F800F069 -:101B9400C3F901A90C2200F0C6FA01A8FFF776F9EF -:101BA4009C48FFF773F99B48FFF770F94FF48040A6 -:101BB40000F094F990B1A148FFF768F94FF4804020 -:101BC40000F080F9082000F07DF9102000F07AF987 -:101BD400202000F077F9402000F074F9FEF7D8FDDA -:101BE400FEF782FFFEF74EFDFEF706FB08B19448B0 -:101BF40003E0FEF745FB10B19248FFF75BFF00F0EE -:101C04008AF9802000F06AF918B1802000F05AF9AE -:101C140056E04FF4003000F061F9044620B14FF46F -:101C2400003000F04FF94DE00120FEF739FFB0B16C -:101C34002046FEF735FF90B18348FFF727F941F2BC -:101C440088340120FEF72CFF98B10020FEF728FF0E -:101C540078B1012000F051FA013CF2D132E07B4B23 -:101C64000CCB013301D0013203D17948FFF70EF9CF -:101C740028E0FFF7E1FD48B3FFF7DEFD10B175483A -:101C8400FFF704F94FF4007000F028F910B172481E -:101C9400FFF7FCF84FF4007000F014F9102000F086 -:101CA4001DF9002800F0EE806C48FFF7EFF80820DB -:101CB40000F008F9102000F005F9FFF795FE98B937 -:101CC40067489AE76748FFF7E1F8F6E74FF40070D2 -:101CD40000F004F90028CFD1082000F0F3F8102018 -:101CE40000F0F0F8FFF73AFE4FF4005000F0F6F879 -:101CF40004464FF4805000F0F1F8400040EA8400BC -:101D0400C4B24FF4006000F0E9F82043C0B20728E1 -:101D1400C3B20DD14FF4006000F0D4F84FF48050FA -:101D240000F0D0F84FF4005000F0CCF84E4864E7CF -:101D34000133DBB2023B4FF40060052B11D8DFE81E -:101D440003F00C1003100C1000F0BCF84FF480509A -:101D540000F0B8F84FF4005003E000F0B3F84FF48B -:101D6400805000F0A3F8FFF751FD384B3F485D6801 -:101D74001E68FFF76FF82846FFF796F83C48FFF710 -:101D840069F80022930003F1604303F561430132D3 -:101D94004FF0FF31082AC3F88010C3F88011F1D145 -:101DA400344B4FF4801200241A635C639C631C64FC -:101DB4005C6400F02FFD3048012100F0C3FD214692 -:101DC4002D4800F0BFFDF120012100F0C7FD2146A0 -:101DD400F12000F0C3FD0120014600F0CBFD2146B7 -:101DE400012000F0C7FD2548012100F0CFFD214668 -:101DF400224800F0CBFD2248012100F0D3FD204809 -:101E0400214600F0CFFD63B64FF0FF3EB5462847AC -:101E14009C32000884350008A9320008C03200084A -:101E2400D932000807330008363300086633000847 -:101E340097330008C9330008FB3300082C3400082A -:101E4400014550FE024550FE593400080040000888 -:101E54008B3400089E340008B6340008D6340008D9 -:101E6400034550FE4A350008044550FE6935000814 -:101E74007D350008003802400010E022FFC9FEF65C -:101E8400337F7704082000F029F820B10548FEF7D5 -:101E9400FDFF102003E00448FEF7F8FF082000F0DF -:101EA40005F81FE7FE3400082435000810B5044681 -:101EB400002000F0A7FD40EA04010020BDE8104026 -:101EC40000F090BD10B50446002000F09BFD20EA10 -:101ED40004010020BDE8104000F084BD10B50446A4 -:101EE400002000F08FFD204214BF0120002010BD0F -:101EF40008B54FF08050012100F00CFD012000F0E6 -:101F040069FC0120FFF7EAFF20B90121BDE8084080 -:101F140000F068BD08BD7047022000F073BD0000EA -:101F240008B5FFF7F9FF054B1968884204D0022071 -:101F3400BDE8084000F056BD08BD00BF840100089C -:101F440000BEFDE71FB504460C2201A8FEF71CFCE9 -:101F540001AB03CB20601868A0602046616004B028 -:101F640010BD0068A0F10C035842584170470000AE -:101F740080B50646174610480D461C46FEF76AFF14 -:101F84003846FEF767FF0D48FEF764FF3046FEF75C -:101F940061FF0B48FEF75EFF2846FEF785FF2CB174 -:101FA4000848FEF757FF2046FEF754FF0648FEF7A1 -:101FB4006DFFFFF7C5FF00BF853500088E350008AB -:101FC400913500088F350008843500081FB506AA2E -:101FD40052F8044B039200921A462346FFF7C8FFB7 -:101FE4000CB41FB506AA52F8043B03920092014AAE -:101FF400FFF7BEFF9335000807B500230093724630 -:10200400014BFFF7E3FF00BF9A350008744608B59B -:102014000548FEF71FFF2046FEF746FF0348FEF77C -:1020240035FFFFF78DFF00BFA23500088435000897 -:10203400BFF34F8F0449054BCA6802F4E0621343AF -:10204400CB60BFF34F8FFEE700ED00E00400FA051C -:1020540008B5FEF791FBFFF7EBFF08B5FFF7E8FFC4 -:10206400F0B50646002401202546034694421DD0BF -:1020740011F804C000F1010EBCF1000F03D1735537 -:10208400774605460DE00133DBB2FF2B774606F8B1 -:1020940000C007D102F1FF3C644506D07355871C8C -:1020A4007546012301343846E0E770467355F0BDA8 -:1020B40030B5C9B1C0430A44914213D011F8013B71 -:1020C4000A4D83EA000404F00F0455F8244084EA1E -:1020D400101080EA131303F00F0355F8233083EA3A -:1020E4001010E9E7C04330BD084630BDC82D0008D4 -:1020F400162358430138FDD1704710B504462CB15E -:102104004FF47A70FFF7F4FF013CF8E710BD10B507 -:10211400B0FA80F400F027F801280CBFC4F11F00C6 -:10212400C4F1200010BD0A2A10B51BDD0C46302373 -:1021340004F8023B78234B701C220F239340034086 -:10214400D340092B01D8303302E00F2B02D8573388 -:10215400DBB200E02023043A04F8013B131DECD168 -:1021640000238B7210BD00F0AA33A0EB530000F0E3 -:10217400CC3300F0333000EB930000EB101000F090 -:102184000F3000EB102000EB104000F03F007047D0 -:102194002D4AD2F8883043F47003C2F888302B4BB0 -:1021A4001A68002142F001021A6099601A6822F04C -:1021B400847222F480321A60254A5A601A6822F422 -:1021C40080221A60D9601A6C224942F080521A6443 -:1021D4000A6842F440420A609A689A609A689A606F -:1021E4009A6842F480529A601B4A5A601A6842F014 -:1021F40080721A601968154A8901FBD5174B40F2A1 -:1022040003611960936823F003039360936843F0B8 -:10221400020393600D4B9A6802F00C02082AF9D16C -:102224009A6822F400029A600D4AC3F884201A685E -:1022340042F080621A60054B1B681B01FBD5024B00 -:102244004FF000629A60704700ED00E000380240F1 -:1022540010300024007000401040010F003C024088 -:1022640000300050C278037810B512B3164AD16812 -:102274004278C943C1F30221C1F10404E4B2A2408B -:10228400D4B20F220A4181780A40224303F1604309 -:1022940003F561431201D2B283F8002303780122CB -:1022A400590903F01F0302FA03F3084A42F82130E4 -:1022B40010BD5A09012103F01F03994002F12003C4 -:1022C400024A42F8231010BD00ED00E000E100E0F6 -:1022D400014B01229A60704700300240014B1860A4 -:1022E400186870470030024000EB81018842044BBB -:1022F40003D050F8042B1A60F8E71868704700BF41 -:1023040000300240014B1868704700BF00300240A3 -:10231400044B9A6809B1104301E022EA0000986076 -:10232400704700BF002004E0044B1A69002ABFBFB5 -:10233400034A5A6002F188325A607047003C0240F6 -:1023440023016745024A136943F000431361704750 -:10235400003C0240014BD860704700BF003C024083 -:102364000E4BDA68D20310D4DA68D1060FD4DA68D7 -:10237400D2050ED4DA6812F0EF0F0CD1DB6813F03B -:10238400020F14BF0820092070470120704706205F -:102394007047022070470720704700BF003C02408E -:1023A40007B509238DF80730FFF7DAFF8DF807002A -:1023B4009DF80730012BF7D09DF8070003B05DF8B6 -:1023C40004FB000070B5064641B1012908D002297A -:1023D4000CBF4FF400754FF4407503E00D4601E067 -:1023E4004FF48075FFF7DCFF09281ED10F4C2369D9 -:1023F40023F440732361216929432161236923F074 -:10240400F8032361236943F002031E432661236911 -:1024140043F480332361FFF7C3FF236923F00203EE -:102424002361236923F0F803236170BD003C02405B -:1024340070B505460E46FFF7B3FF092811D1094CC4 -:10244400236923F44073236123692361236943F0DF -:10245400010323612E70FFF7A3FF236923F0010317 -:10246400236170BD003C024070B543688668856A8C -:102474000468416B46EA0302C3681A4303691A43BA -:1024840043691A4383691A43C3691A43036A1A43A3 -:10249400436A1343C26A2B431A43036B1343A200D8 -:1024A40002F120420B43082E136002BF136843F06D -:1024B40040031360B1F5801F816B12D18CB14FF0D2 -:1024C40020435C681E6846F480161E601E6846F44D -:1024D40080761E600E6924F47004013E44EA0654BA -:1024E4005C608C690B6823434C6843EA04138C6872 -:1024F40043EA0423CC6843EA04430C69496943EA88 -:10250400045343EA0163B5F5804F536010D1C16BA6 -:1025140088690B680343486843EA0013886843EA00 -:1025240000230869496943EA005343EA016301E06F -:102534006FF07043C2F8043170BD0000800000F1F8 -:10254400204019B1036843F0010302E00268024B22 -:1025540013400360704700BFFEFF0F00F0B50E6824 -:1025640000220123934003EA060E9E452AD155001A -:1025740003230468AB40DB431C4004600C79076808 -:1025840004FA05FC013C4CEA0707012C076011D84A -:1025940084684F791C4084608468AF402743876017 -:1025A400446824EA0E0444608C7947689440A4B2D9 -:1025B4003C434460C4682340C360CB79C468AB40E7 -:1025C4002343C3600132102ACBD1F0BD4FF6FF7311 -:1025D40003600023037143718371C37170470369FE -:1025E400194214BF0120002070470AB101837047CB -:1025F4004183704701F00703C90800EB810010B55F -:102604009B00046A0F21994024EA01010162016AD6 -:102614009A401143016210BD08B5134B984207D18B -:102624004FF40010012100F0B1F94FF4001014E050 -:102634000E4B984207D14FF48000012100F0A6F917 -:102644004FF4800009E00A4B98420BD14FF400008C -:10265400012100F09BF94FF400000021BDE808407F -:1026640000F094B908BD00BF005400400058004079 -:10267400005C00407FB5868826F03F060446360499 -:1026840068460D46360C00F0E9F8029A2C48B2FB75 -:10269400F0F081B20E43A68023882A4E23F0010372 -:1026A4001B041B0C23802B68B3420AD85B00013146 -:1026B400B2FBF3F39BB2032B89B298BF04232184AA -:1026C40023E0EE884BF6FF718E421BBF19214B436A -:1026D40003EB4303B2FBF3F315BF9BB2B2FBF3F37B -:1026E40043F480439BB2C3F30B020AB943F00103E2 -:1026F4004FF4967101FB00F24FF47A7192FBF1F200 -:10270400013292B243F400432284A383238869896B -:10271400AA889BB243F001032380238823F48163B6 -:1027240023F002031B040A431B0C13439BB22380B4 -:102734002A89AB8913439BB2238104B070BD00BFC7 -:1027440040420F00A086010041F288330360002359 -:1027540083804BF6FF72038143814FF48043C28030 -:1027640083817047038819B19BB243F0010303E0EE -:1027740023F001031B041B0C03807047038819B169 -:102784009BB243F4806303E023F480631B041B0CBB -:1027940003807047838A9AB2038B10B542EA0343DD -:1027A40021F07F4404EA0300431A5842584110BD03 -:1027B400014B1860704700BF00300040014B586067 -:1027C400704700BF00300040014B9860704700BF65 -:1027D40000300040014B1860704700BF20000E42DB -:1027E400034B5B68184214BF01200020704700BFF0 -:1027F40000700040064BB0F5402F15BF1A685A68A8 -:1028040042EA800042F4402214BF18605A607047C4 -:10281400007000400E4B1A68002142F001021A6059 -:1028240099601A6822F0A85222F410221A60094A08 -:102834005A60094AC3F8842002F18062C3F88820F0 -:102844001A6822F480221A60D960C3F88C10704789 -:102854000038024010300024003000201D4A9368E4 -:1028640003F00C03042B10B503D0082B03D01A4B30 -:1028740018E01A4B16E05168536811F4800F03F006 -:102884003F03516814BF154A134AB2FBF3F3114ACC -:102894005268C2F30142C1F3881101324B43520022 -:1028A400B3FBF2F30B4A036093680D49C3F30313BC -:1028B400CC5C0368E34043609468C4F382240C5DF9 -:1028C40023FA04F484609268C2F342328A5CD340EF -:1028D400C36010BD003802400024F40040787D013C -:1028E40001000020044B1A6B09B1104301E022EAF5 -:1028F40000001863704700BF00380240044B9A6B15 -:1029040009B1104301E022EA00009863704700BF58 -:1029140000380240044B1A6C09B1104301E022EA6A -:1029240000001864704700BF00380240044B5A6C22 -:1029340009B1104301E022EA00005864704700BF67 -:1029440000380240044B1A6909B1104301E022EA3D -:1029540000001861704700BF00380240044B5A69F8 -:1029640009B1104301E022EA00005861704700BF3A -:1029740000380240044B9A6909B1104301E022EA8D -:1029840000009861704700BF00380240044B1A6A87 -:1029940009B1104301E022EA00001862704700BF49 -:1029A40000380240044B5A6A09B1104301E022EA9C -:1029B40000005862704700BF003802404209012AF3 -:1029C400074B01D11B6803E0022A0CBF1B6F5B6F2E -:1029D40000F01F0023FA00F000F00100704700BF70 -:1029E4000038024082B000230193054B0193019B00 -:1029F40003EB80000190019B196002B0704700BF97 -:102A04005028004082B000230193054B0193019BA1 -:102A140003EB80000190019B186802B0704700BF6F -:102A24005028004008B5254B984207D14FF48050F8 -:102A34000121FFF7B7FF4FF4805039E0204B984253 -:102A440007D14FF480400121FFF7A0FF4FF48040ED -:102A540009E01C4B98420BD14FF400400121FFF7D1 -:102A640095FF4FF400400021BDE80840FFF78EBFFA -:102A7400154B984207D14FF400500121FFF792FF04 -:102A84004FF4005014E0114B984207D14FF48010DA -:102A94000121FFF787FF4FF4801009E00C4B9842A7 -:102AA4000BD14FF400100121FFF77CFF4FF400100D -:102AB4000021BDE80840FFF775BF08BD00300140A4 -:102AC40000380040003C0040003401400050014008 -:102AD4000054014003884A8810B503F441540B881C -:102AE40013438A881343CA8813430A8913434A89C0 -:102AF40013438A891343CA89134323439BB2038034 -:102B0400838B23F400631B041B0C83830B8A0382D3 -:102B140010BD0023038043808380C38003814381ED -:102B24008381C381072303827047038819B19BB251 -:102B340043F0400303E023F040031B041B0C038019 -:102B44007047808980B27047818170470389194238 -:102B540014BF0120002070477FB5038ACA889BB246 -:102B640023F440531343038283890E469BB223F418 -:102B7400B053098923F00C02B3880B4371890B43CA -:102B84009BB213438381838AB2899BB223F440733B -:102B94001343838204466846FFF760FE1A4B9C4247 -:102BA40003D003F580639C4201D1039D00E0029DA4 -:102BB400A38931681BB2002B4FF0190202FB05F206 -:102BC400B4BF4D008D00B2FBF5F5A2896426B5FBB8 -:102BD400F6F00001010912B2002A06FB1153ADBF41 -:102BE4001A01D90032313232B5BFB1FBF6F2B2FB71 -:102BF400F6F302F0070203F00F03B4BF10431843C7 -:102C040080B2208104B070BD00100140838919B1E5 -:102C14009BB243F4005303E023F400531B041B0C46 -:102C240083817047C1F3080181807047038819428A -:102C340014BF01200020704710B5431E0A4491427E -:102C440004D011F8014B03F8014FF8E710BD02441A -:102C54000346934202D003F8011BFAE770470000D1 -:102C64000400000001030000010000000100000056 -:102C740003000000010000000F0000000F0000002E -:102C840000000000AE2F00080018024010000000F1 -:102C940006040000B32F00080018024008000000DA -:102CA40006030100B62F00080018024002000000CD -:102CB40006010100BD2F00080018024004000000B6 -:102CC40006020100000000000000000001424F4F16 -:102CD400544C4F414445520000000000002A2A0091 -:102CE400000000000000000000000000005400404C -:102CF40000040240400000000600040000040240FA -:102D0400000200000900040000002000801A0600F0 -:102D1400004000001F200000000000000000000030 -:102D240000580040001402400200000001000400AA -:102D340000140240010000000000040000004000F4 -:102D4400801A0600FFBF00002122000000000000DE -:102D54000116000801000300820007008400070038 -:102D6400010003000200030040000300800007008C -:102D740084000700000001000000000000000008BB -:102D8400004000080080000800C00008000001089E -:102D940000000208000004080000060800000808FB -:102DA40000000A0800000C0800000E0800000800DB -:102DB40010001800200028003000380040004800AF -:102DC40050005800000000006410B71DC8206E3B7E -:102DD400AC30D9269041DC76F4516B6B5861B24D1E -:102DE4003C7105502083B8ED44930FF0E8A3D6D688 -:102DF4008CB361CBB0C2649BD4D2D38678E20AA0F0 -:102E04001CF2BDBD537475636B20627574746F6E70 -:102E140020726567697374657220697320696E76C0 -:102E2400616C69642C20636C656172696E672E0045 -:102E3400427574746F6E206973207075736865646D -:102E440020617420626F6F740069636534306C7044 -:102E54002E630043444F4E45206E6F74206C6F7791 -:102E640020647572696E67207265736574004352DD -:102E740045534554206E6F742068696768206475F3 -:102E840072696E6720726573657400446973706C4F -:102E9400617920627573792D776169742074696D25 -:102EA400656F757420657870697265642100436F7D -:102EB4006E6669677572696E672046504741206681 -:102EC400726F6D2062697473747265616D20696ECE -:102ED40020666C6173682E2E2E004E6F204650477C -:102EE400412062697473747265616D20696E206635 -:102EF4006C6173682E0046616C6C696E6720626158 -:102F0400636B20746F204E56434D2E00465047414C -:102F14002043444F4E452074696D656F7574206578 -:102F240078706972656421004650474120636F6E72 -:102F340066696775726174696F6E206661696C6534 -:102F4400642E204973207468697320612062696764 -:102F5400626F6172643F0046504741207665727328 -:102F6400696F6E3A2000446973706C617920696EF0 -:102F7400697469616C697A6564206166746572203C -:102F84000020726574726965732E00446973706CF5 -:102F9400617920696E697469616C697A6174696FB9 -:102FA4006E206661696C65642E004261636B005536 -:102FB400700053656C65637400446F776E006372D0 -:102FC400632E63006932632E6300493243206465D3 -:102FD40076696365204944206F7574206F662062AA -:102FE4006F756E647320256420286D61783A2025FE -:102FF4006429006F6E007370692E6300206973206A -:103004006F7574736964652073797374656D206674 -:103014006C6173680D0A0073797374656D5F666C17 -:103024006173685F657261736528002C2000290D47 -:103034000A006661696C656420746F20657261734F -:103044006520736563746F72200073797374656DA2 -:103054005F666C6173685F77726974652800666186 -:10306400696C656420746F2077726974652061648B -:1030740064726573732000496E76616C69642066BE -:1030840069726D77617265206465736372697074C7 -:10309400696F6E2100436865636B73756D6D696E4E -:1030A40067206669726D77617265207570646174FA -:1030B4006500496E76616C6964206669726D77613A -:1030C40072652043524320696E2053504920666C38 -:1030D400617368210065726173655F6F6C645F661C -:1030E40069726D776172650077726974655F6E6588 -:1030F400775F6669726D7761726500436865636BBB -:1031040073756D6D696E6720002062797465730D47 -:103114000A00436865636B73756D202D2077616EBB -:10312400746564200020676F7420004F75722069F5 -:103134006E7465726E616C20666C61736820636F77 -:103144006E74656E747320617265206261642028F8 -:10315400636865636B73756D206661696C6564296A -:10316400212054686973206973207265616C6C79DD -:103174002062616421004F75722070726576696FF8 -:103184007573206669726D7761726520757064610C -:103194007465206661696C65642C2061626F727469 -:1031A400696E67207570646174652E004E657720C2 -:1031B4006669726D776172652069732061766169F1 -:1031C4006C61626C6521004C6F6164696E6720728A -:1031D40065636F76657279206669726D7761726571 -:1031E400004661696C656420746F206C6F616420B3 -:1031F4007265636F76657279206669726D77617244 -:10320400652C20737472696B65206F6E652E205473 -:10321400727920616761696E2E004661696C65642C -:1032240020746F206C6F6164207265636F766572C1 -:1032340079206669726D776172652C207374726986 -:103244006B652074776F2E205472792061676169F1 -:103254006E2E004661696C656420746F206C6F612A -:1032640064207265636F76657279206669726D7722 -:103274006172652C20737472696B65207468726561 -:10328400652E20534144205741544348004841523D -:1032940044204641554C5400657869742073746128 -:1032A4006E64627900205F5F5F5F20202020202011 -:1032B4002020202020202020205F5F002F5C202061 -:1032C4005F605C20202020202020202020202F2729 -:1032D4005F5F605C005C205C2C5C4C5C5F5C20206D -:1032E40020205F5F5F202F5C205C2F5C205C20200F -:1032F4005F5F20205F5F20205F5F20205F5F2020D2 -:103304005F5F00205C2F5F5C5F5F205C20202F27C5 -:10331400205F20605C205C205C205C205C2F5C20B3 -:103324005C2F5C205C2F5C205C2F5C205C2F5C207D -:103334005C002020202F5C205C4C5C205C2F5C20F7 -:103344005C2F5C205C205C205C5F5C205C205C204B -:103354005C5F2F205C5F2F205C205C205C5F5C2026 -:103364005C002020205C20605C5F5F5F5F5C205C11 -:103374005F5C205C5F5C205C5F5F5F5F2F5C205C58 -:103384005F5F5F785F5F5F2F275C2F605F5F5F5FCA -:10339400205C00202020205C2F5F5F5F5F5F2F5C3C -:1033A4002F5F2F5C2F5F2F5C2F5F5F5F2F20205CD0 -:1033B4002F5F5F2F2F5F5F2F202020602F5F5F5FC5 -:1033C4002F3E205C002020202020202020202020B0 -:1033D40020202020202020202020202020202020E9 -:1033E40020202020202020202020202020202020D9 -:1033F4002F5C5F5F5F2F00202020202020202020D2 -:1034040020202020202020202020202020202020B8 -:1034140020202020202020202020202020202020A8 -:1034240020205C2F5F5F2F004C61737420666972EB -:103434006D7761726520626F6F742077617320739A -:103444007461626C653B20636C6561722073747295 -:10345400696B657300486F6C6420646F776E2055E8 -:1034640050202B204241434B20666F72203520733D -:103474006563732E20746F20666F7263652D626FAF -:103484006F7420505246004669726D7761726520F0 -:10349400697320657261736564005761746368645D -:1034A4006F6720636175736564206120726573655D -:1034B4007400536F667477617265206661696C7518 -:1034C400726520636175736564206120726573653C -:1034D40074004661696C656420746F207374617252 -:1034E40074206669726D776172652C2073747269D9 -:1034F4006B652074687265652E004661696C65644D -:1035040020746F207374617274206669726D7761C0 -:1035140072652C20737472696B652074776F2E004A -:103524004661696C656420746F20737461727420E1 -:103534006669726D776172652C20737472696B654C -:10354400206F6E652E00466F7263652D626F6F7417 -:10355400696E67207265636F76657279206D6F643A -:10356400652E2E2E00426F6F74696E672066697235 -:103574006D77617265204020002E2E2E0D0A0D0AF3 -:10358400004153534552543A20002020003A004150 -:103594005353455254004153534552544E002A2A22 -:1035A4002A20575446200053544D33320053544D6F -:1035B4003332207065726970686572616C206C6961 -:1035C4006272617279207472697070656420616ED0 -:1035D400206173736572740043524F414B204F4F07 -:0435E4004D00000096 -:1035E800FF000000000102030401020304060708AB -:0435F80009000000C6 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_spalding_evt@1431479105.hex b/bin/boot/nowatchdog_boot_spalding_evt@1431479105.hex deleted file mode 100644 index d706ceb0d9..0000000000 --- a/bin/boot/nowatchdog_boot_spalding_evt@1431479105.hex +++ /dev/null @@ -1,2745 +0,0 @@ -:020000040800F2 -:1000000068200020B50100080102000899180008C6 -:1000100001020008010200080102000800000000BF -:1000200000000000000000000000000001020008C5 -:10003000010200080000000001020008010200089F -:100040000102000801020008010200080102000884 -:100050000102000801020008010200080102000874 -:100060000102000801020008010200080102000864 -:100070000102000801020008010200080102000854 -:100080000102000801020008010200080102000844 -:100090000102000801020008010200080102000834 -:1000A0000102000801020008010200080102000824 -:1000B000010200080102000801020008C51300083F -:1000C000D1130008DD130008E9130008010200083D -:1000D00001020008010200080102000801020008F4 -:1000E00001020008010200080102000801020008E4 -:1000F00001020008010200080102000801020008D4 -:1001000001020008010200080102000801020008C3 -:1001100001020008010200080102000801020008B3 -:1001200001020008010200080102000801020008A3 -:100130000102000801020008010200080102000893 -:100140000102000801020008010200080102000883 -:100150000102000801020008010200080102000873 -:100160000102000801020008010200080102000863 -:100170000102000801020008010200080102000853 -:040180000102000870 -:1001840041A3525576332E302D6265746131312D81 -:1001940038342D673532376464626500000000002E -:0F01A4000000000035323764646265000000011E -:1001B400002100F004B80C4B5B58435004310B4849 -:1001C4000B4B42189A42FFF4F6AF0A4A00F003B808 -:1001D400002342F8043B084B9A42FFF4F9AF01F0C4 -:1001E4008DFE01F089FB704730AB00080000002051 -:1001F400140000201400002068000020FFF7FEBF58 -:1002040030B501208BB002F0C5FA002401902546D8 -:10021400E0B200F0BBFB01AB08B9185519E01A5D58 -:10022400042A10DD164800F025FD02A920220198B9 -:1002340001F02CFE02A800F01DFD0120002102F0B7 -:1002440099FA00201AE00132D2B2052A1A55A8BF41 -:1002540001250134042CDBD1019C54B1094800F080 -:1002640009FD204602A9202201F010FE02A800F098 -:1002740001FD0120214602F07DFA28460BB030BD75 -:1002840008A4000834A4000808B500F087FA80F038 -:100294000100C0B208BD000008B54FF480610348F6 -:1002A40002F063F8003018BF012008BD00180240B6 -:1002B4002DE9F0434A4D89B0284600F00BFD28464D -:1002C4000D21052208AE02F05BF8052228460E2116 -:1002D40002F056F84FF4005346F81C3D0024012761 -:1002E4004FF00209284631468DF80A408DF80B403C -:1002F4008DF808908DF8097001F0EEFF4FF48043FB -:1003040028463146019301F0E7FF4FF48053284615 -:10031400314601934FF400788DF808408DF80A4077 -:1003240001F0DAFF284631468DF808408DF80B704D -:10033400CDF8048001F0D0FF4FF4806328463146A5 -:1003440001938DF808408DF80B4001F0C5FF4FF480 -:1003540080732846314601938DF808708DF80B4060 -:1003640001F0BAFF4FF40043284631460193A5F546 -:1003740044451C4E8DF80A708DF80B4001F0ACFF1B -:1003840039464FF4001002F099F9174B33602846B0 -:1003940002F010FA03A802F084FA4FF44043ADF8D7 -:1003A4000C3039464FF482733068ADF80E30ADF836 -:1003B4001040ADF81290ADF81470ADF8168001F04D -:1003C40019F803A9ADF818002846ADF81A4002F050 -:1003D40049FA2846394602F070FA09B0BDE8F083BC -:1003E40000180240140000200024F40008B5022084 -:1003F40001F036FD012000F0B7FA022001F030FDD3 -:100404000120BDE8084000F09DBA000010B5044684 -:100414000548022102F05FFA0028F9D0214602487B -:10042400BDE8104002F055BA0054014038B501222D -:1004340004460D4628484FF4807101F09CFF0120CA -:1004440001F00EFD24484FF40041002201F093FF17 -:100454004FF480710022204801F08DFF012001F04B -:10046400FFFC4FF400411C48012201F084FF0120ED -:1004740001F0F6FC18484FF4007101F076FF18B152 -:100484001648174A68210AE013484FF4004101F066 -:100494006CFF08B1254404E01048124A692101F0B8 -:1004A40099FCAC4204D014F8010BFFF7AFFFF8E756 -:1004B40009484FF48071012201F05DFF08240020F7 -:1004C400FFF7A4FF013CFAD103484FF40071BDE8E3 -:1004D400384001F04ABF00BF001802404DA4000894 -:1004E40057A4000872A4000810B54FF480710446A4 -:1004F4000022054801F03FFF642001F0ABFC2046D8 -:10050400BDE81040FFF782BF0018024008B5074855 -:10051400802102F0E0F90028F9D105484FF48071F8 -:100524000122BDE8084001F026BF00BF005401408D -:100534000018024010B540F2F514FFF7ADFE50B1BB -:10054400013C04D1054800F095FB204610BD642011 -:1005540001F080FCF1E7012010BD00BF8FA400086A -:1005640010B504460420FFF7BFFF2046FFF74EFFF7 -:10057400BDE81040FFF7CABF10B504460120FFF7DD -:10058400B3FFE0B2FFF742FFC4F30720FFF73EFFDB -:10059400C4F30740FFF73AFF200EFFF737FFBDE82B -:1005A4001040FFF7B3BF000010B5FFF781FE1848F5 -:1005B40047F6DE01FFF73AFFFFF718FF002401209A -:1005C400FFF7CEFFFFF7B6FF78B10320FFF78CFFEC -:1005D400FFF79CFF0F4800F063FB204600F06EFB22 -:1005E4000D48BDE8104000F045BB094847F6DE0160 -:1005F4000134FFF71BFF152CE1D1084800F03AFB4A -:100604000220FFF771FFBDE81040FFF77FBF00BF76 -:10061400E6290008B2A40008CDA40008D7A4000865 -:1006240008B55D224B1C504300EB5300074BB0FB55 -:10063400F1F01A78C0B2824207D01870FFF79CFF1D -:100644000220BDE80840FFF78BBF08BD0000002072 -:1006540008B5FFF791FF0320BDE80840FFF780BF0E -:1006640008B50220FFF740FFBDE80840FFF74EBF82 -:1006740001F1C04102F0A4B970B543489AB000F04A -:1006840029FB424800F026FB414800F023FB414B84 -:1006940093E8030001AC84E803003C48214601F0E0 -:1006A4001BFE3A480421012201F065FE00250223C5 -:1006B400029535488DF80830072103230C228DF864 -:1006C4000930019501F05CFE80232F4801932146F7 -:1006D40001F002FEEBB2022B04D02C48A9B20C228A -:1006E40001F04EFE0135102DF4D14FF6FB7327486F -:1006F4000193214601F0F0FD0226314601362448DB -:100704000C22B6B201F03CFE102EF6D14FF6FC736B -:1007140021461F48019301F0DFFD042100221B48FC -:100724001D4D01F028FE0A2001F094FB0122042152 -:10073400164801F020FE1E2001F08CFB012001462A -:1007440001F0A4FF0FCD03AC0FC495E8070084E8C3 -:100754000700402200210AA802F03BF902230B9370 -:1007640008230C934FF4805312934FF400531393C4 -:100774000AA803AB18930D9601F02CFD002001216B -:1007840001F09AFD1AB070BD00040240000C024052 -:1007940000100240C4A20008CCA200080E4B98220C -:1007A4001A80A3F68A231B88DBB2512B07D10B4B8B -:1007B4001888C0B2B0F152025042504100E000200B -:1007C40030B1074B1888C0B2B0F1590358425841B0 -:1007D400014BF0221A807047AA0A006022000060D0 -:1007E4002400006002460B460420502100F0C9BDDD -:1007F40002460B460420502100F0D8BD032873B5EF -:100804000E4603D0042804D001F0FAFA14251C4C37 -:1008140001E01C4C162528460DF10701FFF7E2FF05 -:1008240008B900202AE02378AEB10BB1013323E0EC -:100834009DF8071021F0060141F0020128468DF8C9 -:100844000710FFF7D5FF0028EBD001250320257002 -:1008540001F006FB11E05AB2012A0CDC9DF80710E6 -:10086400284601F0F9018DF80710FFF7C1FF0028B1 -:10087400D7D0267001E0013B2370012002B070BD87 -:10088400180000201900002073B5254800F022FA52 -:1008940002254FF410730024012621480093694671 -:1008A4008DF804508DF805508DF806608DF80740DA -:1008B40001F012FD1A480621042201F061FD042210 -:1008C4000921174801F05CFD154800F01DFA154890 -:1008D40000F000FA42F20C036946124800938DF8C6 -:1008E40005508DF804608DF806408DF8074001F03E -:1008F400F3FC0C4800F008FA042000F0DFFC20466A -:100904006946FFF76FFF9DF80050B54205D10320FB -:100914002946FFF773FF284600E0204602B070BD69 -:10092400000402400014024010B50446042000F004 -:10093400C5FC21460420FFF761FF0420BDE81040F8 -:1009440000F0EABC38B5074C0546204600F0C2F971 -:1009540020462A46042101F00EFD2046BDE8384019 -:1009640000F0D2B90014024038B5074C05462046C1 -:1009740000F0B0F920462A46082101F0FCFC20468C -:10098400BDE8384000F0C0B90014024038B5094C45 -:1009940004EB0014606800F09DF92189606801F09F -:1009A400E4FC0546606800F0AFF9D5F1010038BFFA -:1009B400002038BDE8A2000838B500242546E0B27E -:1009C400FFF7E4FFA04001340543042CEDB2F6D157 -:1009D400284638BD73B54FF480400121154C01F011 -:1009E4006DFE04F1400654F80A0C00F073F96846F1 -:1009F40001F0B2FC02238DF80530237854F80A0C78 -:100A04008DF80730694654F8063C009300258DF8AC -:100A14000450103401F060FC54F81A0C00F074F91E -:100A2400B442E0D14FF48040294601F047FE02B0C1 -:100A340070BD00BFF6A2000808B5054B1B781BB9B2 -:100A44000448322101F0D2F9BDE8084001F002BBAC -:100A54001A0000200AA50008114B1B7870B5054642 -:100A64000C461BB90F48382101F0C0F9032906D9F7 -:100A74008E083146FFF7E0FFB6003544A41B1CB9CD -:100A8400BDE8704001F0F4BA00231846EA5C013373 -:100A9400A34242EA0020F9D1BDE8704001F0D4BA83 -:100AA4001A0000200AA50008024B1A780AB901228C -:100AB4001A7070471A00002008B5084B1A781AB942 -:100AC40007482B2101F092F901214FF4805059700D -:100AD40001F0D0FDBDE8084001F0B0BA1A000020D2 -:100AE4000AA5000808B5074B1A781AB90648512117 -:100AF40001F07CF9002159704FF48050BDE80840A2 -:100B040001F0B8BD1A0000200AA5000870B50C4613 -:100B14000546A608FFF7D0FF28463146FFF78CFFAD -:100B240004F0030105EB8600FFF796FF0446FFF788 -:100B3400D9FF204670BD70B5A0B00D460646FFF73C -:100B4400BBFF2C467119802CC4EB0101684608D9FF -:100B54008022FFF78DFD68462021FFF76DFF803C62 -:100B6400F0E72246FFF784FD21466846FFF774FF4D -:100B74000446FFF7B7FF204620B070BD2DE9F041D1 -:100B8400284C86B0204600F0A5F84FF480200121BF -:100B940001F088FD20460A21072201F0F1FB06AE90 -:100BA400072220460B2101F0EBFB002501274FF023 -:100BB40002084FF48063204669468DF8065046F8D3 -:100BC400183D8DF807708DF804808DF8058001F0CC -:100BD40083FB4FF400632046694600938DF804803C -:100BE40001F07AFBADF80C50ADF80E50ADF8105092 -:100BF400ADF814500C4D4FF46133029328460C2386 -:100C040002A9ADF8123001F06DFE2846394601F014 -:100C1400C1FE204600F078F8044B1F7006B0BDE812 -:100C2400F08100BF00080240004800401C00002082 -:100C3400084B1B7810B504465BB10748402101F00E -:100C4400B9FE0028F9D021460348BDE8104001F060 -:100C5400ADBE10BD1C0000200048004008B5FFF7E1 -:100C6400E7FF0348402101F0A5FE0028F9D008BDA4 -:100C740000480040094B1B7810B573B1441E14F8AA -:100C8400010F10B1FFF7EAFFF9E70D20FFF7E6FFC8 -:100C94000A20BDE81040FFF7E1BF10BD1C00002092 -:100CA400054B1B7810B533B1441E14F8010F10B175 -:100CB400FFF7D4FFF9E710BD1C0000201FB50C227C -:100CC40001A901F0E3F801A8FFF7EAFF05B05DF818 -:100CD40004FB000000F13F4000F57E00C0F38720D4 -:100CE40008280BD8064A135C591C115433B9054B18 -:100CF40001211A6B01FA00F010431863704700BF1A -:100D04001D0000200038024000F13F4000F57E0045 -:100D1400C0F3872008280ED8074A135C5BB1013B57 -:100D2400DBB213543BB9054B01211A6B01FA00F0F5 -:100D340022EA0000186370471D00002000380240BA -:100D440002460068838823F4E0631B041B0C838041 -:100D540003889BB243F40073038008230020517579 -:100D640013751076704737B504460D46FFF7B2FF8A -:100D74000023204669468DF804308DF805308DF83F -:100D84000730009501F0A8FA2046FFF7BDFF03B035 -:100D940030BD00000A4B10B5342400FB0434236B2F -:100DA4006BB10A2001F05CF86068A168FFF7DBFF13 -:100DB40020696169FFF7D7FF236B0120984710BDB5 -:100DC40030A30008F7B5044616460D461F46FFF744 -:100DD40081FF022001238DF804008DF805008DF8B1 -:100DE40006302046002369468DF80730009501F04F -:100DF40073FA204631463A4601F0C2FA2046FFF71C -:100E040083FF03B0F0BD0000037D082B10B504463A -:100E140007D10268938823F4C0631B041B0C9380DE -:100E240083E0434A006802EB8303996E01F07CFB84 -:100E340000287AD0237D072B71D8DFE803F0040B58 -:100E44001A22323A596D227A236802F0FE021A827B -:100E5400012305E02368A27A1A82627A12B102237E -:100E6400237562E09A8892B242F480629A800623E3 -:100E7400F6E723681A8892B242F480721A80032338 -:100E8400EEE7237A226843F001031382E37A012B0D -:100E940005D1138823F480631B041B0C13800423E3 -:100EA400DEE723689A8892B242F480629A8005232E -:100EB400D6E72368227B198A23699954237BE27A33 -:100EC4000133DBB2591C9142237307D12268138882 -:100ED40023F480631B041B0C138026E0934224D16B -:100EE4002268938823F480631B041B0C938013E013 -:100EF400217B236922685B5C1382237BE17A0133C3 -:100F0400DBB29942237310D1938823F480631B04CA -:100F14001B0C93800723A3E72046012101E0204610 -:100F24000021BDE81040FFF70BBF002010BD00BF3B -:100F340030A300087FB51D4E1C2303FB0063054648 -:100F44001B790BB9FFF726FF194B342405FB043436 -:100F54006068A168A289A37BFFF734FF228BA37E7C -:100F640020696169FFF72EFFE069012101F09AFB16 -:100F7400684601F0B3FA0F4A236A009393424FF490 -:100F84008063ADF80A304FF01C0303FB05F588BFFE -:100F9400626A705988BFADF80620694601F030FADC -:100FA4007059012101F0A8FA04B070BD2800002096 -:100FB40030A30008A086010073B504460E46002540 -:100FC400FFF788FE0123204669468DF80430009619 -:100FD4008DF805508DF8075001F07EF92046B1B226 -:100FE4002A4601F0C8F92046FFF78EFE02B070BD14 -:100FF400094B10B5342400FB0434236B5BB100208F -:1010040098476068A168FFF7D7FF20696169BDE868 -:101014001040FFF7D1BF10BD30A300081C23104AB5 -:10102400434370B50546D058D61801F0BBF90D4BB3 -:10103400342405FB04340021E06901F033FB3379E7 -:1010440023B92846BDE87040FFF7D2BF6068A168A5 -:10105400FFF789FE20696169BDE87040FFF783BE30 -:101064002800002030A300082DE9F74F4A4E9B4684 -:1010740096F838300F469246B0461BB9474840F2BE -:10108400951104E0042804D944484FF4CB7100F0CE -:10109400ADFE434B18444FF01C0990F8885009FBEF -:1010A40005F906EB090423790BB9002071E056F821 -:1010B4000900038B980713D49DF830306772E372EC -:1010C40000270D9B84F808A084F80AB027732775BD -:1010D4002361E78256F80900012101F019FA20E0A2 -:1010E4002846FFF79BFF2846FFF724FF56F8092000 -:1010F4002C48138B9907DFD50138FAD1D5E7227E26 -:1011040022B1019A511E0191002AF8D100222276BF -:10111400019A02B3227D082A13D1677D237D082B0F -:1011240026D023681A8892B242F480721A809A8870 -:1011340092B242F440729A8001222276194A0192B4 -:10114400DFE7E28AB2F57A7F05D20132E28201203A -:1011540000F086FEE2E79A8822F4E0621204120CA0 -:101164009A801A8892B242F400721A800823237576 -:101174001C236B4358F803100A4B0A8B920707D5BC -:10118400013BFAD12846FFF749FF2846FFF7D2FE74 -:10119400384603B0BDE8F08F2800002010A50008F1 -:1011A40030A3000880841E00017D0368082906D14D -:1011B4009A8822F480721204120C9A8036E09A8A79 -:1011C400120505D59A8A22F400621204120C9A823E -:1011D4009A8AD20505D59A8A22F480721204120CD6 -:1011E4009A829A8A52051ED59A8A22F4806212043F -:1011F400120C04299A820AD1032202759A8822F4D5 -:10120400E0621204120C9A80002303760EE0012996 -:1012140009D19A8822F4E06212040021120C0175AB -:101224009A80017602E00021FFF78ABD0020704712 -:101234002DE9F3411E4D1F4C0026DFF87C802B68FE -:1012440044F80C3C1C2202FB0682002304F8083CF0 -:1012540013760822227295F828208DF8042001279D -:101264000C2201A8237004F8013C63608DF805206A -:101274008DF806308DF8077000F0AAFE95F8293035 -:101284008DF8043001A800F0A3FE3E4454F80C0C81 -:1012940001F088F8022E05F1340504F11C04CCD1C8 -:1012A400384688F83870FFF7A3FE02B0BDE8F08135 -:1012B40030A300083400002028000020124A13B58F -:1012C40092F83810034621B9104840F2331100F067 -:1012D4008DFD042807D9052200920C480C4A40F2DF -:1012E400351100F077FD0B490B441C2493F888005A -:1012F40004FB002423790BB9FFF71CFE2379013387 -:10130400237102B010BD00BF2800002010A5000802 -:1013140016A5000830A3000808B5104B93F8382030 -:101324001AB90F4840F2451104E0042804D90C48C6 -:101334004FF4A37100F05AFD0A4A10441C2290F89D -:10134400880002FB003213793BB1013BDBB213711D -:101354001BB9BDE80840FFF761BE08BD28000020A6 -:1013640010A5000830A3000873B5069C00930E4630 -:1013740015460194012132462B46FFF775FE02B053 -:1013840070BD07B500930123FFF7EEFF03B05DF8CE -:1013940004FB73B5069C00930E4615460194002188 -:1013A40032462B46FFF760FE02B070BD1FB504AC99 -:1013B40004F8013D01230094FFF7EBFF04B010BDD6 -:1013C4000148FFF721BD00BF280000200148FFF7B6 -:1013D400EBBE00BF280000200148FFF715BD00BF89 -:1013E400440000200148FFF7DFBE00BF4400002096 -:1013F400FFF79ABA30B585B004460D46684601F049 -:10140400F5F80DB9029805E0012D01D1039801E02A -:1014140000F0F6FCB4EB500F0ED8B0FBF4F000F083 -:1014240029FD002802DC0748272103E0082804DD01 -:101434000448282100F0DAFC01200138C00080B201 -:1014440005B030BD42A50008B0F1006F10B50446E8 -:101454000BD20D48FFF724FC2046FFF72FFC0B4866 -:10146400FFF71EFC4FF0FF3010BD094A002352F86D -:10147400041FA14202D851688C4202D301330B2BC2 -:10148400F5D1184610BD00BFBAA5000848A500084C -:10149400BCA300082DE9F04705462D480F46164623 -:1014A4009946FFF7FDFB2846FFF708FC2948FFF79C -:1014B400F7FB3846FFF702FC2748FFF7F1FB002F44 -:1014C4003FD02846FFF7C0FF0446781E2844FFF7A4 -:1014D400BBFF002C074637DB002835DBC4EB0008D4 -:1014E40008F101081EB1002041464A46B04700F009 -:1014F400D1FD1A4B012503EB440ABC421FDCF32047 -:1015040000F0DEFD0AEB4503002133F8020C00F085 -:101514000FFE09280BD01248FFF7C2FB2046FFF745 -:10152400CDFB1048FFF7A6FB00F0C2FD0CE01EB196 -:10153400284641464A46B04701340135DDE700F00C -:10154400B7FD0120BDE8F0870020BDE8F08700BFAB -:1015540063A5000877A500087AA50008F0A3000891 -:101564007EA50008ADAA00082DE9F84606461F48E6 -:1015740015468A461F46FFF793FB3046FFF79EFB4E -:101584001B48FFF78DFB2846FFF798FB1948FFF728 -:1015940087FB00F07FFDF32000F092FD4FEAD519A0 -:1015A4000024AC421DD0A0191AF8041000F0F6FD76 -:1015B40009280DD01048FFF773FB3046FFF77EFB78 -:1015C4000E48FFF757FB00F073FD0020BDE8F886D6 -:1015D4002FB1630603D1E0094946089AB84701349C -:1015E400DFE700F065FD0120BDE8F88696A5000858 -:1015F40077A500087AA50008AAA50008ADAA0008E6 -:1016040008B545F2555000F09BFF042000F09EFF02 -:1016140040F6FF7000F0A0FF002000F091FF4FF4AF -:1016240080500121BDE8084000F028BD08B57D20A8 -:1016340001F08CF8003018BF012008BD4900FEF706 -:10164400EFBF4B0803EB5000FEF7EABF70B586B05E -:101654000446FFF729FA01210020FEF7E1FF0420E8 -:1016640000F01EFB03A8214600F066FB03A800F06F -:1016740072FB08B928480BE02848FFF7FBFA04F18D -:101684000C000499FFF757FA059B984204D02448AC -:10169400FFF7F0FA01203DE0049D2248FFF7EAFA43 -:1016A4002946214A21480023FFF7F4FE049E2048DE -:1016B400FFF7E0FA002504F1C0411E4B00953246C5 -:1016C4000C311A48FFF750FF1B48FFF7E9FA04985A -:1016D400FFF7F4FA1948FFF7E3FA04991348FFF700 -:1016E40015FA04461648FFF7DBFA0598FFF7E6FA01 -:1016F4001448FFF7D5FA2046FFF7E0FA1248FFF73F -:10170400B9FA059B9C4204D01048FFF7B3FA0220B3 -:1017140000E0284606B070BDC3A50008E1A5000896 -:10172400FEA5000821A600084116000800000108D3 -:1017340034A600084716000847A6000855A6000866 -:101744005EA6000871A60008ADAA000877A60008E6 -:1017540008B5022000F0BCFAD8B3042000F0B8FAAF -:1017640070B11C48FFF786FA042000F0A5FA0220A5 -:1017740000F0A2FA4FF40040BDE8084000F09CBA23 -:101784001548FFF777FA082000F096FA102000F0C9 -:1017940093FA202000F090FA402000F08DFA4FF4E4 -:1017A4008010FFF753FF022807D1082000F078FAD1 -:1017B400102000F075FA00F045FB042000F07CFADC -:1017C400022000F079FA4FF40040BDE8084000F030 -:1017D40067BA08BDC2A60008F8A6000810B52548D7 -:1017E400FFF748FA4FF40010FFF730FF10B102285A -:1017F4003AD809E0202000F05FFA402000F05CFABB -:10180400802000F04DFA2FE0082000F049FA102063 -:1018140000F046FA202000F05BFA20B91648FFF7E2 -:1018240029FA202007E0402000F052FA58B9134862 -:10183400FFF720FA402000F033FA4FF4007000F074 -:101844002FFA00F0FFFA0E48FFF714FA082000F010 -:1018540033FA102000F030FA202000F02DFA402056 -:1018640000F02AFA002400E00124042000F024FA05 -:10187400204610BD13A700082DA7000866A700087E -:101884009FA7000808B50248FFF7F4F900BEFEE779 -:10189400D9A700081EF0040F0CBFEFF30880EFF384 -:1018A4000980FFF7EFBF70471FB504461048FFF7E4 -:1018B400E1F901A90C22204600F0E8FA01A8FFF79B -:1018C400D9F92046FEF7C4FEFFF776F8094B20F05D -:1018D400040018701C46FFF76FF8237800F0FB0033 -:1018E400834201D000F0AEFA0A2000F0B9FAF2E720 -:1018F400CFA70008640000207FB54FF0805001217D -:1019040000F0D0FE022000F033FE012807D10220AF -:1019140000F038FE9248FFF7ADF900F098FA002184 -:101924004FF0805000F0BEFEFFF728F98D48FFF716 -:10193400A1F98D48FFF79EF98C48FFF79BF98C4875 -:10194400FFF798F98B48FFF795F98B48FFF792F961 -:101954008A48FFF78FF98A48FFF78CF98948FFF71F -:1019640089F98948FFF786F9FFF762FCFEF78CFFD7 -:1019740000F0B8F900F0CEF900210C2201A801F022 -:1019840028F800F0C3F901A90C2200F07FFA01A89D -:10199400FFF770F97348FFF76DF97248FFF76AF9BA -:1019A4004FF4804000F094F990B17848FFF762F961 -:1019B4004FF4804000F080F9082000F07DF91020F9 -:1019C40000F07AF9202000F077F9402000F074F953 -:1019D400FEF752FEFEF7FEFFFEF7E6FDFEF710FCF3 -:1019E40008B16B4803E0FEF74FFC10B16948FFF7FC -:1019F4005BFF00F08AF9802000F06AF918B18020BA -:101A040000F05AF954E04FF4003000F061F9044654 -:101A140020B14FF4003000F04FF92CE10120FEF723 -:101A2400B5FFB0B12046FEF7B1FF90B15A48FFF7B9 -:101A340021F941F288340120FEF7A8FF98B1002073 -:101A4400FEF7A4FF78B1012000F00AFA013CF2D1BC -:101A540011E1524B0CCB013301D0013203D1504878 -:101A6400FFF708F907E1FFF7E1FD18B3FFF7DEFD23 -:101A740010B14C48FFF7FEF84FF4007000F028F95D -:101A840010B14948FFF7F6F84FF4007000F014F96C -:101A9400102000F01DF9D0B14448FFF7EBF80820FE -:101AA40000F00AF9102000F007F9FFF797FEF8B9E3 -:101AB4003F489CE74FF4007000F00AF90028D5D1A4 -:101AC400082000F0F9F8102000F0F6F80EE00820E5 -:101AD40000F0FEF820B13748FFF7CCF8102003E0FF -:101AE4003548FFF7C7F8082000F0DAF8FFF730FEB2 -:101AF4004FF4005000F0ECF804464FF4805000F02E -:101B0400E7F8400040EA84044FF4006000F0E0F895 -:101B1400E4B22043C3B2072B0DD14FF4006000F0B0 -:101B2400CBF84FF4805000F0C7F84FF4005000F0A9 -:101B3400C3F822485BE70133DBB2023B4FF4006099 -:101B4400052B41D8DFE803F03C4003403C4000F063 -:101B5400B3F84FF4805000F0AFF84FF4005033E086 -:101B6400E4A70008ADAA0008F1A7000808A8000827 -:101B740021A800084FA800087EA80008AEA8000805 -:101B8400DFA8000811A9000843A9000874A90008E7 -:101B9400014550FE024550FEA1A9000800000108BD -:101BA400D3A90008E6A90008FEA900081EAA000897 -:101BB400034550FE46AA00086CAA0008044550FEDE -:101BC40000F07AF84FF4805000F06AF8FFF718FD3F -:101BD4002B4B2C485D681E68FFF762F82846FFF718 -:101BE4006DF82948FFF75CF800239A0002F160427F -:101BF40002F5614201334FF0FF31082BC2F8801027 -:101C0400C2F88011F1D1214B4FF4801200241A63E1 -:101C14005C639C631C645C6400F0C4FC1C4801218C -:101C240000F058FD21461A4800F054FDF12001212E -:101C340000F05CFD2146F12000F058FD0120014632 -:101C440000F060FD2146012000F05CFD11480121F7 -:101C540000F064FD21460F4800F060FD0E480121AC -:101C640000F068FD0C48214600F064FD63B64FF0B7 -:101C7400FF3EB54628470948FEF7FCFF15E700BFBD -:101C84000000010892AA0008A6AA00080038024031 -:101C94000010E022FFC9FEF6337F7704AEAA0008E5 -:101CA40010B50446002000F075FD40EA0401002050 -:101CB400BDE8104000F05EBD10B50446002000F001 -:101CC40069FD20EA04010020BDE8104000F052BD87 -:101CD40010B50446002000F05DFD20420CBF00203A -:101CE400012010BD08B54FF08050012100F0DAFC4E -:101CF400012000F037FC0120FFF7EAFF20B90121A1 -:101D0400BDE8084000F036BD08BD7047022000F071 -:101D140041BD000008B5FFF7F9FF054B196888427B -:101D240004D00220BDE8084000F024BD08BD00BF77 -:101D34008401000800BEFDE71FB504460C2201A87B -:101D4400FEF796FC01AB03CB20601868A060204628 -:101D5400616004B010BD0068B0F10C0358425841F2 -:101D64007047000080B50546174610480E461C46CD -:101D7400FEF796FF3846FEF793FF0D48FEF790FFF7 -:101D84002846FEF78DFF0B48FEF78AFF3046FEF724 -:101D940095FF2CB10848FEF783FF2046FEF780FF2D -:101DA4000648FEF767FFFFF7C5FF00BFCDAA00088E -:101DB400D6AA0008D9AA0008D7AA0008ADAA000824 -:101DC4001FB506AA52F8044B039200921A46234602 -:101DD400FFF7C8FF0CB41FB506AA52F8043B0392E0 -:101DE4000092014AFFF7BEFFDBAA000807B50023F3 -:101DF40000937246014BFFF7E3FF00BFE2AA00081D -:101E0400744608B50548FEF74BFF2046FEF756FF1B -:101E14000348FEF72FFFFFF78DFF00BFEAAA000873 -:101E2400ADAA0008BFF34F8F044A054BD16801F4F3 -:101E3400E0610B43D360BFF34F8FFEE700ED00E09A -:101E44000400FA0508B5FEF70BFCFFF7EBFF08B535 -:101E5400FFF7E8FF1623584301387FF4FDAF7047BE -:101E640010B504462CB14FF47A70FFF7F3FF013C30 -:101E7400F8E710BD10B5B0FA80F400F029F8012895 -:101E84000CBFC4F11F00C4F1200010BD0A2A30B5F4 -:101E94001DDD0C46302304F8023B78234B700023ED -:101EA4009D00C5F11C050F22AA400240EA40092A00 -:101EB40001D8303202E00F2A02D85732D2B200E001 -:101EC4002022E2540133082BEAD100238B7230BD67 -:101ED40000F0AA33A0EB530000F0CC3300F0333011 -:101EE40000EB930000EB101000F00F3000EB10201B -:101EF40000EB104000F03F00704700002D4BD3F87A -:101F0400882042F47002C3F888202B4B1A68002101 -:101F140042F001021A6099601A6822F0847222F475 -:101F240080321A60254A5A601A6822F480221A60A4 -:101F3400D9601A6C42F080521A64214A116841F443 -:101F4400404111609A689A609A689A609A6842F46B -:101F540080529A601B4A5A601A6842F080721A6072 -:101F64001968154A8901FBD5174B40F203611960C2 -:101F7400936823F003039360936843F00203936030 -:101F84000D4B9A6802F00C02082AF9D19A6822F4DF -:101F940000029A600D4AC3F884201A6842F08062F5 -:101FA4001A60054B1B681B01FBD5024B4FF0006206 -:101FB4009A60704700ED00E00038024010300024C1 -:101FC400007000401040010F003C024000300050FF -:101FD400C278037810B50AB3164A4478D268D2435B -:101FE400C2F30222C2F10401C9B204FA01F10F24BE -:101FF40044FA02F28478C9B222400A4303F16043EE -:1020040003F561431201D2B283F80023037801225D -:10201400590903F01F0302FA03F306E059090122E8 -:1020240003F01F0302FA03F32031034A42F821307C -:1020340010BD00BF00ED00E000E100E0014B012213 -:102044009A60704700300240014B1860186870476E -:102054000030024000EB81018842044B03D050F869 -:10206400042B1A60F8E71868704700BF003002407C -:10207400014B1868704700BF00300240044B9A6857 -:1020840009B1104301E022EA00009860704700BFE4 -:10209400002004E0044B1A69002A04DA034A5A6057 -:1020A40002F188325A607047003C024023016745C0 -:1020B400024B1A6942F000421A617047003C024028 -:1020C400014BD860704700BF003C02400E4BDA68F9 -:1020D400D00310D4DA68D1060FD4DA68D2050ED44E -:1020E400DA6812F0EF0F0CD1DB6813F0020F0CBFAB -:1020F40009200820704701207047062070470220FD -:1021040070470720704700BF003C024007B5092311 -:102114008DF80730FFF7DAFF8DF807009DF80730D8 -:10212400012BF7D09DF8070003B05DF804FB000015 -:1021340070B5064641B1012908D0022914BF4FF4F5 -:1021440040754FF4007503E00D4601E04FF48075CF -:10215400FFF7DCFF09281ED10F4C236923F44073D9 -:10216400236121690D432561236923F0F803236169 -:10217400236943F002031E432661236943F4803339 -:102184002361FFF7C3FF236923F00203236123695B -:1021940023F0F803236170BD003C024070B505468E -:1021A4000E46FFF7B3FF092811D1094C236923F424 -:1021B4004073236123692361236943F0010323618D -:1021C4002E70FFF7A3FF236923F00103236170BD81 -:1021D400003C024070B543688568846A0168426BBC -:1021E40045EA0306C3681E4303691E4343691E434D -:1021F40083691E43C3691E43036A1E43436A334310 -:1022040043EA0406C36A1E43036B334343EA0206EC -:102214008B0003F12043082D1E6003D11D6845F097 -:1022240040051D60B2F5801F836B12D189B14FF058 -:1022340020425668156845F480151560156845F404 -:10224400807515601D6926F47006013D46EA055542 -:1022540055601A689D6915435A6845EA02159A68DB -:1022640045EA0225DA6845EA02451A695B6945EAE6 -:10227400025242EA03624E1C4FF02043B4F5804FF1 -:1022840043F8262010D1C36B1A68986910435A6822 -:1022940040EA02109A6840EA02201A695B6940EA3F -:1022A400025242EA036201E06FF07042014B43F8CC -:1022B400212070BD040100A0800000F1204019B16C -:1022C400036843F0010302E00268024B1340036019 -:1022D400704700BFFEFF0F002DE9F0410F68002397 -:1022E40001229A4002EA0705954230D15A0003249C -:1022F40006689440E443264006600E79D0F800C096 -:1023040006FA02F8013E48EA0C0C012EC0F800C09F -:1023140014D8866891F805C02640866086680CFA51 -:1023240002FC4CEA06068660466826EA0505456016 -:102334008D7946689D40ADB235434560C5682C40F3 -:10234400C460CD79C46805FA02F22243C260013345 -:10235400102BC5D1BDE8F0814FF6FF730360002355 -:10236400037143718371C3717047036919420CBFD0 -:102374000020012070470AB10183704741837047F0 -:1023840001F00703C90800EB810010B59B00046A43 -:102394000F21994024EA01010162016A9A40114324 -:1023A400016210BD08B5134B984207D14FF40010D9 -:1023B400012100F0B3F94FF4001014E00E4B9842E1 -:1023C40007D14FF48000012100F0A8F94FF48000F8 -:1023D40009E00A4B98420BD14FF40000012100F0B0 -:1023E4009DF94FF400000021BDE8084000F096B9C3 -:1023F40008BD00BF0054004000580040005C00408D -:10240400F0B585B004468688324F68460D4600F024 -:10241400EDF802982A68304B26F03F063604B0FBEC -:10242400F7F7360C1FFA87FC4CEA0606A6802188CB -:1024340021F001010904090C9A4221800DD85300AE -:10244400B0FBF3F39BB20CF1010C032B1FFA8CFCD1 -:1024540098BF0423A4F820C022E0E9884BF6FF7358 -:10246400994205D102EB4202B0FBF2F39BB206E0C3 -:1024740019235343B0FBF3F39BB243F48043C3F3F8 -:102484000B020AB943F001034FF4967257434FF419 -:102494007A7297FBF2F70137BFB243F40043278403 -:1024A400A38323886989AA889BB243F0010323800C -:1024B400238823F4816323F002031B040A431B0CC7 -:1024C40013439BB223802A89AB8913439BB2238194 -:1024D40005B0F0BD40420F00A086010041F28833F0 -:1024E4000360002383804BF6FF72038143814FF422 -:1024F4008043C28083817047038819B19BB243F043 -:10250400010303E023F001031B041B0C0380704749 -:10251400038819B19BB243F4806303E023F480631E -:102524001B041B0C03807047828A008B92B221F03B -:102534007F4342EA00401840431A584258417047CA -:10254400014B1860704700BF00300040014B5860D9 -:10255400704700BF00300040014B9860704700BFD7 -:1025640000300040014B1860704700BF20000E424D -:10257400034B5B6818420CBF00200120704700BF6A -:1025840000700040B0F5402F054B04D01A6842EAB1 -:102594008000186070475A6842F440225A607047BD -:1025A400007000400E4B1A6842F001021A600022CB -:1025B4009A60196821F0A85121F410211960094981 -:1025C40059600949C3F8841001F18061C3F8881087 -:1025D400196821F480211960DA60C3F88C207047EF -:1025E4000038024010300024003000201D4B9A684F -:1025F40002F00C02042A10B503D0082A03D01A4BA7 -:1026040018E01A4B16E059685A6811F4800F59689B -:1026140014BF164B144B02F03F02B3FBF2F3114A02 -:102624005268C2F30142C1F3881101324B43520094 -:10263400B3FBF2F30B4903608B680D4AC3F3031336 -:10264400D45C0368E34043608C68C4F38224145D63 -:1026540023FA04F484608968C1F34231525CD340A4 -:10266400C36010BD003802400024F40040787D01AE -:1026740001000020044B1A6B09B1104301E022EA67 -:1026840000001863704700BF00380240044B9A6B87 -:1026940009B1104301E022EA00009863704700BFCB -:1026A40000380240044B1A6C09B1104301E022EADD -:1026B40000001864704700BF00380240044B5A6C95 -:1026C40009B1104301E022EA00005864704700BFDA -:1026D40000380240044B1A6909B1104301E022EAB0 -:1026E40000001861704700BF00380240044B5A696B -:1026F40009B1104301E022EA00005861704700BFAD -:1027040000380240044B9A6909B1104301E022EAFF -:1027140000009861704700BF00380240044B1A6AF9 -:1027240009B1104301E022EA00001862704700BFBB -:1027340000380240044B5A6A09B1104301E022EA0E -:1027440000005862704700BF003802404309012B63 -:10275400074A01D1136803E0022B0CBF136F536FB8 -:1027640000F01F0023FA00F000F00100704700BFE2 -:102774000038024082B000230193054B0193019B72 -:1027840003EB80000190019B196002B0704700BF09 -:102794005028004082B000230193054B0193019B14 -:1027A40003EB80000190019B186802B0704700BFE2 -:1027B4005028004008B5254B984207D14FF480506B -:1027C4000121FFF7B7FF4FF4805039E0204B9842C6 -:1027D40007D14FF480400121FFF7A0FF4FF4804060 -:1027E40009E01C4B98420BD14FF400400121FFF744 -:1027F40095FF4FF400400021BDE80840FFF78EBF6D -:10280400154B984207D14FF400500121FFF792FF76 -:102814004FF4005014E0114B984207D14FF480104C -:102824000121FFF787FF4FF4801009E00C4B984219 -:102834000BD14FF400100121FFF77CFF4FF400107F -:102844000021BDE80840FFF775BF08BD0030014016 -:1028540000380040003C004000340140005001407A -:10286400005401400B88028810B54C8823438C889F -:102874002343CC8823430C8923434C8923438C89E9 -:102884002343CC8902F44152234313439BB2038074 -:10289400838B23F400631B041B0C83830B8A038246 -:1028A40010BD0023038043808380C3800381438160 -:1028B4008381C381072303827047038819B19BB2C4 -:1028C40043F0400303E023F040031B041B0C03808C -:1028D400704781817047038919420CBF0020012091 -:1028E40070470000038ACA889BB230B523F4405372 -:1028F4000D46134303820989AB8882890B43698996 -:1029040092B20B4322F4B05222F00C029BB2134356 -:102914008381838AAA899BB223F44073134385B0CD -:10292400838204466846FFF761FE194B9C4203D03C -:1029340003F580639C4201D1039B00E0029BA289C2 -:1029440012B2002A2A684FF01901B4BF5200920053 -:102954005943B1FBF2F16423B1FBF3F21201100904 -:1029640003FB1011A08900B2002806DAC900323135 -:10297400B1FBF3F303F0070305E009013231B1FBC6 -:10298400F3F303F00F031A4392B2228105B030BD72 -:1029940000100140838919B19BB243F4005303E052 -:1029A40023F400531B041B0C83817047C1F30801FB -:1029B40081807047038819420CBF002001207047B2 -:1029C40010B50023934203D0CC5CC4540133F9E71F -:1029D40010BD02440346934202D003F8011BFAE7F8 -:1029E4007047FF004C617474696365006943456313 -:1029F4007562653220323031342E31322E32373026 -:102A0400353200506172743A206943453430554C74 -:102A1400314B2D434D33364100446174653A204DAA -:102A2400617920313120323031352031393A313930 -:102A34003A35350000FF7EAA997E510001059200C7 -:102A44002062016F82000072007011000101000019 -:102A540000000000000000000010000080000000E2 -:102A640000000200000000008000000000000000E0 -:102A74000000010000080000000000000000000049 -:102A84000000000000000020000000000000000022 -:102A940000003400020000000000000000000000FC -:102AA4000200000000000000000000000000000020 -:102AB40000000000000000004000000000000100D1 -:102AC40000000000400000000000000000000000C2 -:102AD40000040000000000000000000000000000EE -:102AE400000000000000000580000000000000005D -:102AF40000000003000000000000000000000000CF -:102B040000000000000000000000000000000000C1 -:102B1400000000008000000000000200000004002B -:102B24008000000200000000000000000008000017 -:102B34000000000000000000000000000000000091 -:102B44000000000000010000000000000C00000371 -:102B5400000300000000000000000000000000105E -:102B64000000000000000000000000000000000061 -:102B74000000000000000000000000000000000051 -:102B84000000000000000000000000000000000041 -:102B94000000000000000000000000000000000031 -:102BA4000000000000000000000000000000000021 -:102BB40000000000000000000058000000000000B9 -:102BC40000000000000000000000000000000200FF -:102BD400000000000E0800000000020000000000D9 -:102BE40000000000000000002020000000000000A1 -:102BF40000000000000000000000000000000000D1 -:102C040000000000000000000000000000000000C0 -:102C140000000000000000000000000000000000B0 -:102C2400000000000000000000000001000000009F -:102C3400000003000000000100000000000000008C -:102C44000000000000100000000000000000000070 -:102C540000000000000080000003000000000000ED -:102C640003008000000300000000000000000008D2 -:102C74000000002C00000000000000000000000024 -:102C84000000000000000000000000000000180028 -:102C940000000002000000000000000000000078B6 -:102CA4000010000000000000000000000000000010 -:102CB4000000400000000000000000000000400090 -:102CC40000010000000000000000000400000000FB -:102CD40000000000000000000000000000000000F0 -:102CE40000000100000000000000000000000100DE -:102CF40000000000000000000000000810000000B8 -:102D0400000000000000000000000000000080053A -:102D140080000000000000000000800000000000AF -:102D24000000000000000008003000000000000067 -:102D340000000C3020008000000000000014280077 -:102D440000000000781800000020735A08000000FA -:102D540000030002000000001DDF0C000000000062 -:102D64000C30000000000000000000366C00000081 -:102D7400004008FB0000001C025C00008000000012 -:102D84002C000000000000E0F100000000000C3006 -:102D940020000000000000000000000000002800E7 -:102DA4003808000000000058080000000007000276 -:102DB40000A000001C8F0C00000000000C3000007C -:102DC400000000000000003400000000200008079C -:102DD4000000003C3F490000010000003C0000806E -:102DE40000000000F110000000000C302000000082 -:102DF40000000000020000000000100000000000BD -:102E04002004017C00020000008300020000000690 -:102E1400168F0E00000000000C30000000000000BF -:102E240000000000000000001000700B0000003CD7 -:102E34003D50020000000100B80000000000000046 -:102E4400F000000000000C30200000000000000032 -:102E5400001400000000000000E80000000006DE8E -:102E64003C0200000003840200000266548F0C0040 -:102E7400000000000C300000000000000000000210 -:102E84002C00000000000001000000243BF038008A -:102E9400000500003C00000003C20200F100000035 -:102EA40000000C30200000000000000000000000C2 -:102EB4000000000001E00000002002FE50000000BD -:102EC400D9A38002000000001E8F0E100000000035 -:102ED4000C300000000000000000000000000000B2 -:102EE400001070E30100003C27E000000000B00087 -:102EF4003000000000000000F000000000000C3072 -:102F0400200000000000000000000000000000009D -:102F1400800000000000280000000000D9B38002F7 -:102F2400000041C0168F0E00000000000C300000AD -:102F3400000000000000000000000000000030E776 -:102F44002000000000000000000050A080000000ED -:102F540000200000F010000000000C3020000000F1 -:102F64000000000000000048E000000070000400C1 -:102F74000000000000000000101380020000026046 -:102F8400168F0E00000000000C300000100000003E -:102F9400000000000258F000000038072C00000078 -:102FA40000000000000008A0B8000000004002007B -:102FB400F000000000000C302000000000000000C1 -:102FC40000000378A50230000000000000206C001F -:102FD40000000000001023C000C003C01685560086 -:102FE400000000000C3000000000000000000000A1 -:102FF400034052201000700B2800000C3800000021 -:10300400000000C003C00000A060020550000000E2 -:1030140000000C3020000000000000000000014C03 -:10302400F0C000C000000000003C284E0F800000EB -:1030340000C38002000000029C05A80000000000FC -:103044000C3000000000000000000000001F0F20F2 -:10305400014000900000000400080F000000000080 -:1030640000000800004294DA5000000000000C3018 -:10307400200000000000000A000001C8F0C0004069 -:103084000400000A000005CC0F8000000007800245 -:10309400000001429C80F800000000000C30000099 -:1030A400000000000008000000000F2000400000A5 -:1030B400000800142C1F0F0000000000B8000000DE -:1030C400094281F0F000000000000C3020000000F4 -:1030D4000000000000206548F0C01001000000005E -:1030E400001C055E0F8000003005800200000146D0 -:1030F4005480F800000000000C30000000000000C4 -:103104000000000420200F20100000000000000C2C -:10311400202A0F0000000000380000000002030015 -:10312400F000000000000C3020000000000000004F -:1031340000060148F0C0000000000000000601DCA9 -:103144000F80000000070402000000601580F800F2 -:10315400000000000C30000000000000000000002F -:1031640000200F2000000000000000003C300F0091 -:1031740000009000A800000000000200F000000021 -:1031840000000C302000000000000000000031E8C6 -:10319400F0C0000000000010002001C15A800000AF -:1031A40000058402000000001F80F80000000000F9 -:1031B4000C300000000000000000000000000F20A0 -:1031C400000000000000001C3CECA5000000000012 -:1031D400380000000002C080F000000000000C3045 -:1031E400200000000000000010000168F0D0004042 -:1031F40000000000102001780F8000007815040200 -:10320400000101401790F800000000000C3000009D -:10321400000000000000000000000F00000000009B -:10322400000000043C100F00000000802800000093 -:10323400002001C0F000000000000C30200000005D -:1032440000000000000631E85550000000000000B6 -:10325400000661EA0F80000079E17C3E0000026014 -:103264001F80F800000000000C3000000000000087 -:103274000000000000205500002800000000003C71 -:1032840028200F0000005900F43C00010042C30054 -:10329400F000000000000C30200000000000000CD2 -:1032A4000020040000000000000000000000066888 -:1032B4000840800079E023C000000200408F020033 -:1032C400000000000C3000000000000000081004A2 -:1032D40020000000000000000000003C234905001D -:1032E400400008A003C000000042000F00000000DE -:1032F40000000C30201020007A3C20000000001C4C -:10330400F040000000180000000000000000000071 -:103314000003F954000000662FC5F000000000000F -:103324000C30000000000803C09000002C10F001D5 -:10333400004000F70000000000000000000000B0A2 -:10334400C154000000003D05F000000000000C30F6 -:1033540020000000323C200A001E0200A540280084 -:103364000008000A0000000000002800000379FCA7 -:1033740008A001E681AF0400000000000C3000004A -:1033840000000003C048000000405A012000F0077C -:1033940000080000000000002000F003C2FC0280CE -:1033A4000342C00F0000000000000C3020000000A9 -:1033B4003A3C204C0000281CF0401010001000047F -:1033C40000003800000010008083FE0420C041404B -:1033D40000000000000000000C30000000000003AA -:1033E400C00000344000F001102000CF000C0800A1 -:1033F4000000000030001189FD0000C003600000DF -:103404000000000000000C3020000000323C2000CE -:1034140000140008F040000000000000000000005C -:103424000000000000017A002008004781AF04007A -:10343400000000000C30000000000803C00000364B -:103444000010F00000000007000000000000000071 -:1034540000000007EA0000041424010F000000002B -:1034640000000C3020000800723C20000000000A1C -:10347400F0400000008000100000001EF04000003A -:1034840038052DFC00000000018F0400000000003E -:103494000C3000000B0A0003C00000000000F00024 -:1034A4000000F1BB020004000000F00000000889E5 -:1034B400BEFC2000A000000F0000000000000C3043 -:1034C40020040100723C2000100E380AF040004035 -:1034D40000000000100020000000004001817A80FC -:1034E4000001426635800000000000000C3000003E -:1034F40000800803C00000020010F0000000000D6E -:103504003C0000340000000000000005C000200062 -:10351400004025A10000000000000C302004000A37 -:103524007A3C20001000001AF040000000A00400C3 -:103534000000001CF0400000000000000000140720 -:1035440001CC0220000000000C3000040500000340 -:10355400C00010000000F0000000D1F33C000000A7 -:103564000010F00000001000000000000800010C32 -:103574000008000000000C30200005007A3C200008 -:103584000000001AF040100050A004000020001CAD -:10359400F060000001A063C10000100707C08010A4 -:1035A400000000000C300000050E0003C0001000F5 -:1035B4000000F0001000D9BB3C0000240000F00023 -:1035C400000000A003C00400000424800080000068 -:1035D40000000000000000007A3C200000262A5B66 -:1035E4004240004000000000001C00ED5A000004AE -:1035F40001E000000002000000AF04000000000031 -:103604000000000000000803C1000014024CE200A6 -:10361400404000000000000001CC1A010000F090BE -:10362400000000040000010F000000000000000082 -:10363400028005007A3C200A001E7078AF1000401A -:103644000000000A0020006AA500280000A7EA0084 -:1036540000A00000418F04000000000000000200F0 -:1036640000800803C008003E2020AF100040000086 -:1036740000080004015055002000000F6C00008079 -:103684000003810F000000000000000003000000A0 -:103694001A15400C0016024C3042300000000004A1 -:1036A4004020396AA000100001933828004000032C -:1036B400C000000000000000000000000000001531 -:1036C400400400000270B10090000000000C0034BF -:1036D4006370F50010000080002820C06003C00063 -:1036E40000000000000000000000000007BC100003 -:1036F4000014287B4240000000000000000000FC91 -:103704000E02000000F5F8000800E00000000000D0 -:1037140000000000000000000000043C0020001431 -:10372400286CD100800000000000001428D00F0095 -:10373400000000FF81000000000000000000000005 -:10374400000000000000000007BC10000000625BE5 -:1037540042400000001000002014000AF040000065 -:103764005801FA010800000000EF04000000000006 -:10377400000000000000043C08000000665DE20058 -:10378400800000F00200E0000000F00000000000F3 -:10379400C20000000000010F000000000000000053 -:1037A40000000000802950040026647B4240000091 -:1037B40000000000863604000000000000012C8197 -:1037C40000000000000000000000000000000000F5 -:1037D4000000101680040026647DD10080000000E3 -:1037E4000000001428000000000000097C00000014 -:1037F40004000000000000000000000000000000C1 -:103804000000000000000258424040000000000098 -:103814002006005AF50000008005F80500000000AD -:10382400200A540000000000000000000000000016 -:10383400000000000248E200000000000000000058 -:103844000340A000000050C1EC00000000021C0571 -:10385400A0000000000000000000001B00000000A9 -:1038640000000159014040000000000080000000F9 -:10387400000000007810000000000040000000007C -:103884000000000000000000000A0000000000002A -:103894000059A7000000000000000E000000000016 -:1038A400000008A00000000000000000000000006C -:1038B4000000000000000180000000000000014A38 -:1038C400F0D000000008000000002E1CB10001022E -:1038D40000186955000000024000000000000000CC -:1038E40000000000008A0000000000002C190F00F6 -:1038F400044000070000000038DF7200028000D995 -:1039040042A800080007400000000000000000007A -:103914000280050076A8000A000001C8F0C0280053 -:103924000008000A002003FCAF2328007818029541 -:1039340000B401E017CAF400000000000000020017 -:103944000100F4000008000000000F002000000740 -:103954000008201C0150DF00200008C1016800821B -:1039640000201700A1000000000000000300001F59 -:103974003F99C480002061C8F0C0100000C0000C52 -:10398400001E001CF040300050A03BC1804203C721 -:103994009DCA50000000000000000300000DD03F4D -:1039A400C00000042C200F003000310F000C000078 -:1039B4000000F000100000A043C0004401E43505FD -:1039C40051000000000000000100001A1B191A1029 -:1039D40000060148F0E00100000000000120026838 -:1039E400A50000800010000000000167BDEA02008D -:1039F4000000000000000100001032160200000068 -:103A040000200F0002000007000000042B505500A6 -:103A1400010000900000000000202E0F5000000064 -:103A2400000000000000010012DD1000000001E8A9 -:103A3400F0C0000080900000000005EE20020100AC -:103A4400000DE005000A01668E8A020000000000F5 -:103A5400000000000108B6140A40000000000F0036 -:103A64000020709B000000002020200002800089BC -:103A7400AC0000040002EF0FA000000000000000F2 -:103A8400000005801640500000000168F0C00000EE -:103A940080000000000466CEA5000000000D6281D5 -:103AA4000000000716AA50000000000000000000FB -:103AB40001001A29C860000000000F000000300552 -:103AC4003C000E3620F055000000000D40000000C0 -:103AD40000073C0AA00000000000000000000D8F59 -:103AE40093DD1600000605E85540000081900400AF -:103AF400006039E87002000030057A010000080710 -:103B04004D8A000000000000000000000D1A32146D -:103B14000020000020205500000091E33C000094A8 -:103B24000150F00000000099F80000080346D40F8B -:103B34005000000000000000000008000000000C1D -:103B44000020040000003000999004000036041C9A -:103B5400F0400001000000000000004795EA00006A -:103B64000000000000000000050000000008000440 -:103B7400200000001000999B3C000C142800F00069 -:103B8400000200800000000000242D0FA0000000AF -:103B94000000000000000000000000000000200001 -:103BA40000000040000000000020054B70000000F1 -:103BB40000007BC100000003B59205000000000076 -:103BC40000000000000000000000000040000000B1 -:103BD40000400090000000242368F00000000080F2 -:103BE40043C00000000724CE0000000000000000D5 -:103BF400000000000000000000340000000000404D -:103C04000000000A002002FABD03280001E00000C1 -:103C140000A00242CF81C0000000000000000000AC -:103C2400000000000000002C2800000000400000FC -:103C3400000800240160FD0820000100000000804D -:103C4400006217C3C0040000000000000000000070 -:103C54000000000000000208500010000000000CEA -:103C64000020641EF0401000000063C10000020345 -:103C740025AB9400000000000000000000000000DC -:103C8400000000000050A00010000000000C002CF8 -:103C94002400F0001000000043C00000004017930F -:103CA4008080000000000000000000000000000010 -:103CB4000000000000000000000000000026064E86 -:103CC400A5000000000000000000014035E8D40415 -:103CD40000000000000000000000000000000014CC -:103CE40000000000000000000000000625F0550060 -:103CF40000000000000000000002A698E0000000A0 -:103D040000000000000000000000000000000000AF -:103D14000000000000000000000072DCF50000005C -:103D2400000000000010016000EF0400000000002B -:103D3400000000000000000000000000000000007F -:103D4400000000000000001C00F050000000000013 -:103D5400000000100000000F000000000000000040 -:103D6400000000000000000000200000000000002F -:103D740000000000002066DEA50000000000000036 -:103D840000000200018F0410000000000000000089 -:103D9400000000000000003C0000000000000000E3 -:103DA4000000002423D0AA0000000000000000004E -:103DB4000143C00F020000000000000000000000EA -:103DC400000000000000027B100000000000000062 -:103DD4000E00701A8002000000000000000A0000BB -:103DE40027E0540000000000000000000000000074 -:103DF40000000000024C2401000800000000000044 -:103E04000050400000000000000000000040240AB0 -:103E1400000400000000000000000000000000009A -:103E24000000000000000100000000000000381C39 -:103E3400F0400000001000000000036007D4000000 -:103E4400000000000000000000000000000000006E -:103E54000000000002000000000000342400F00014 -:103E6400000000D00000000001601680000800007F -:103E74007200901101010100000000000000000028 -:103E840000001000000000000040000000000001DD -:103E940000000000004000020000000000000800D4 -:103EA400000000000000000000000000000000000E -:103EB400200000000000008000000000000200005C -:103EC40000000000000000000000000000000000EE -:103ED40008000000000000000000000000000000D6 -:103EE40000000000000000000000000000000000CE -:103EF400000000010000000000000400F0000000C9 -:103F040000000000000000000000000000000000AD -:103F1400000000000000000000000000000000009D -:103F2400000000000000000000000000000000008D -:103F3400000000000000000000000000000000007D -:103F4400000000000000000000000000000000026B -:103F5400000C000000000800000030000000000019 -:103F6400000000000000000000000000000000004D -:103F7400000000000000000000000000000000003D -:103F84000004000000000000100000000000000019 -:103F9400000000000000000000000000000000001D -:103FA40000340000000000000000000000000000D9 -:103FB40000000000000000000000000000000000FD -:103FC40000000000000000000000000000000000ED -:103FD40000000000000000000000000000000000DD -:103FE40000000000000000000000000000000000CD -:103FF40000000000000000000000000000000000BD -:1040040000000000000000000000080000000000A4 -:10401400002000000000000000000000000000007C -:10402400000000000000000000000000000000008C -:10403400000000000000000000000000000000007C -:10404400000000000000000000000000000000006C -:1040540000000000000000000000000200000B004F -:10406400010000000000000C00000000000000003F -:10407400000000000000000000000000000000003C -:1040840000000000000000000001008008000100A2 -:10409400000200000008000000000000040000000E -:1040A400000000000000000000000000000000000C -:1040B40000000000000000000000000000000000FC -:1040C40000000000000000000000000000000000EC -:1040D40000000000000000000000000000000000DC -:1040E400000000000000004000000000000100008B -:1040F40000000000000000000000000000000000BC -:1041040000000000000000000000000000000000AB -:104114000000000000000001000000000002040094 -:10412400000000000000000000000000000000008B -:10413400000000000000000000000000000000007B -:1041440000000080000000000002000E00000000DB -:1041540000007000000000000000000C30000000AF -:10416400000000000000000000000000000000004B -:10417400000000000000000000000000000000003B -:10418400C00100F000000000000C3020000000001E -:10419400000000000000000000000000000000001B -:1041A4000000000000000000000000000000060104 -:1041B400C0F000000000000C30000000000000000F -:1041C40000000000000000000000000000000000EB -:1041D400000000000000000000000000000000F0EB -:1041E40000000000000C302000000000000000006F -:1041F40000000000000000000000000000000000BB -:10420400000000000000000000000001C0F00000F9 -:104214000000000C3000000000000000000000005E -:10422400000000000000000000000000000000008A -:104234000000000000000001428000F000000000C7 -:10424400000C30200000000000000000000000000E -:10425400000000000000000000000000000000005A -:104264000000000000036041C0F000000000000CEA -:10427400300000000000000000000000000000000A -:10428400000000000000000000000000000000002A -:1042940000000000003C0AF000000000000C302088 -:1042A400000000000000000000000000000000000A -:1042B40000000000000000000000000000000000FA -:1042C40000000021E5F000000000000C30000000B8 -:1042D40000000000000000000000000000000000DA -:1042E40000000000000000000000000000000000CA -:1042F4000000000000000000000C3020000000005E -:1043040000000000000000000000000000000000A9 -:104314000000000000000000000000000000000099 -:10432400000000000000000C30000000000000004D -:104334000000000000000000000000000000000079 -:104344000000000000000000000000000000000069 -:1043540000000000000C30200000000000000000FD -:104364000000000000000000000000000000000049 -:104374000000000000000000000000000000000039 -:104384000000000C300000000000000000000000ED -:104394000000000000000000000000000000000019 -:1043A4000000000000000000000000000000000009 -:1043B400000C30200000000000000000000000009D -:1043C40000000000000000000000000000000000E9 -:1043D4000000000000000000000000000000000CCD -:1043E4003000000000000000000000000000000099 -:1043F40000000000000000000000000000000000B9 -:10440400000000000000000000000000000C30204C -:104414000000000000000000000000000000000098 -:104424000000000000000000000000000000000088 -:1044340000000000000000000000000C300000003C -:104444000000000000000000000000000000000068 -:104454000000000000000000000000000000000058 -:104464000000000000000000000C302000000000EC -:104474000000000000000000000000000000000038 -:104484000000000000000000000000000000000028 -:10449400000000000000000C3000000000000000DC -:1044A4000000000000000000000000000000000008 -:1044B40000000000000000000000000000000000F8 -:1044C40000000000000C302000000000000000008C -:1044D40000000000000000000000000000000000D8 -:1044E40000000000000000000000000000000000C8 -:1044F4000000000C3000000000000000000000007C -:1045040000000000000000000000000000000000A7 -:104514000000900000000000000000000000000007 -:10452400000C30200000000000000000000000002B -:104534000000000000000000000000000000000077 -:104544001000000000000000000000000000000C4B -:104554003000000000000000000000000000000027 -:104564000000000000000000000000000000D00077 -:10457400000000000000000000000000000C3020DB -:104584000000000000000000000000000000000027 -:104594000000000000000000000000001000000007 -:1045A40000000000000000000000000C30000000CB -:1045B40000000000000000000000000000000000F7 -:1045C4000000000000000000000000000000006087 -:1045D4000000000000000000000C3020000000007B -:1045E40000000000000000000000000000000000C7 -:1045F40000000000000000000000000000000000B7 -:10460400000000000000000C30000000000000006A -:104614000000000000000000000000000000000096 -:104624000000001000100000000000000000000066 -:1046340000000000000C302000000000000000001A -:104644000000000000000000000000000000000066 -:1046540000300080000000000000000000000000A6 -:104664000000000C3000000000000000000000000A -:104674000000000000000000000000000000002016 -:104684000000000000000000000000000000000026 -:10469400000C3020000000000000000000000000BA -:1046A40000000000000000000000000000280000DE -:1046B4000000000000000000000000000000000CEA -:1046C40030000000000000000000000000000000B6 -:1046D40000000000000000000000000000000B00CB -:1046E400143000000000000000000000000C302026 -:1046F40000000000000000000000000000000000B6 -:104704000000000000000000000000000D21410036 -:1047140000000000000000000000000C3000000059 -:10472400000000000000000040DB0000000000006A -:10473400000040034DA5008000108D80140000008F -:104744000000000000000000000C30200000000009 -:104754000000000000024E5F0200000000000000A4 -:104764000000ED02400000819FA143020000000010 -:10477400000000000000000C3000000000000000F9 -:1047840000000403D000000000000000000140020B -:10479400DBF500010010098014000000002C80806B -:1047A40000000000000C30200000000000000000A9 -:1047B4002C785A0A00000000000000000602DAF417 -:1047C400000280980FA14300000000248B540800CD -:1047D4000000000C30000000000000000000040095 -:1047E400000000000000000000003C3CD00001007C -:1047F40000000D001400000002000F000000000083 -:10480400000C302000000000000000002000000028 -:1048140000000000000000002061D8020200000037 -:104824000DA14300000000568F0800000000000C9A -:104834003000000000000000002000400050000094 -:104844000000000000001E00200A00000000090013 -:10485400140200E000020F0000000000000C3020F1 -:1048640000000000000000400039D85000000000A3 -:10487400000000001C00EA0A200000000FA1430011 -:10488400000000048F0800000000000C300000004D -:10489400000000000000003B400800000000000091 -:1048A400000000316D0010000000AD8014000042D3 -:1048B400C0000F0000000000000C302000000000C9 -:1048C400000000000062F818020000000000000070 -:1048D40000046F08000000181D2143000003400677 -:1048E4008F08000A0000000C3000100000000000D7 -:1048F400000000025000000000000000000004243A -:10490400000000100000898014000002C0000F00A5 -:1049140080040000000C30200000000000000000B3 -:1049240006024CA0200000000000000020780000D7 -:10493400001000019D214300000340168F08000071 -:104944000000000C300000000000000008001430DB -:104954004000000000000000080200274A850020F3 -:104964000000AD001400800140000F0000000000B2 -:10497400000C30200000000000000A001E06585001 -:104984000000000000000A000005CC40402900009F -:104994001F214300A00000148F0800000000000C39 -:1049A400300000000000000000003C036A521000C8 -:1049B4000000000000000000E94200000000A9001F -:1049C400144A00014000000020000000000C3020C8 -:1049D400000000000000000C0E624E7A400000004F -:1049E4000000000000014B50400000000F21430074 -:1049F4000001C29400080000000000000000000054 -:104A0400080821402000000020A020000000000031 -:104A14000000003AEB8000013800816500000002CC -:104A24004000000000000000000000000000015EE3 -:104A3400A140800000014AA0000000000000000026 -:104A44000006FA0000000001976400000002600004 -:104A540000000000100000000000000000D01080E2 -:104A640040000436EDA500000000000000001C0218 -:104A740060CB00810001077F0C000001C40000002E -:104A8400000008000000000000000C00204480002A -:104A9400046579A74000000000000000266AEAC30C -:104AA40042028031C3FF4D05000263800000000014 -:104AB400000000000000000D0B9E0000000000201C -:104AC400200A0000000000000000447ACF800000AB -:104AD40000090581002000024014EC408000000021 -:104AE40000000000000B0113804000100004F80ACD -:104AF4000100000000000010202779000800407821 -:104B0400A128010801020017FD04000000000000B4 -:104B1400000000500BF4200000001C3420A02804E6 -:104B24000000000000000001E0D33840007003E8FA -:104B3400000000000034C8D00000000000000000A5 -:104B4400002001120000800020795AA020040000F7 -:104B5400000000000000EA4342000000016C200055 -:104B640000000016B814000000000000000000005F -:104B740080DB000000001633CD240000000000009C -:104B840000000242E00000000008B1E690000000CE -:104B940062E4A5C0800000000000000000070A3A9B -:104BA400C04000003E004C10000000000000000166 -:104BB4000E2B5C5000000050076590000003400776 -:104BC400CA0600000000000000000000885B4000EE -:104BD4000000167660760000000000000020043417 -:104BE400005F000000580D4294800000441D9010A6 -:104BF400000000000000000000038857420000008D -:104C04002E74CAB1620000000000004000344E5F00 -:104C140000000050A3F3E800000202B7B000000057 -:104C24000000000000000000000402800800343B83 -:104C3400E01800000000000008001C22FEA5086027 -:104C44000000A5810000800343C40AC000000000E6 -:104C5400000000000000003B02800A003E05DA0864 -:104C64000200000000000A000007DA405829000191 -:104C7400D1280108A000035DCA0420000000000040 -:104C8400000000000854140000003C21D9800000FA -:104C94000000000000000203EB5A00000201B3E828 -:104CA40000000000203E05A00000000000000000FD -:104CB40000018B87A80000002005CF000000000041 -:104CC400000000001400EF4050000518A16E000021 -:104CD40000014606EF50000000223D0800090000D4 -:104CE400009F000000002402CB40000000000000F0 -:104CF4000090003749A109000001D0000000000025 -:104D0400003CE800000000C03F800021000300F2E6 -:104D1400E0000000006378000000000000000000D4 -:104D240000026D4241003801C00000000001E0169D -:104D3400C00000000000140A000000000093E0001E -:104D440000000002C00008000000000000008040D5 -:104D5400F80A080040399DB31C010012C204ADD00A -:104D64000000000024A0000000001C3E0000000021 -:104D74001E006C80020000000000000040796B4AB5 -:104D8400500000D1FDF70D0000E8406EEEF0000089 -:104D94000030000000000000003640802000004287 -:104DA4004000000000000000000000027D00100030 -:104DB40000D10B4000400001E0059200040000C057 -:104DC40000000000040500824040001000735A02F5 -:104DD400020040000000001000206C080000403970 -:104DE4008BB9400001014007A0060000004000000C -:104DF40000000800001C00000020027C700000007D -:104E04000000000000003C61600000000008DDF0CC -:104E14000001000040150400100000000000000024 -:104E240010001C1B200080401C235CA0000000001C -:104E3400000000000E75FAA0400000100FBC208096 -:104E44000002000EA000200000000000000000008E -:104E540000500140000014796E00000000000000C2 -:104E640000003C600000000000588764300000012E -:104E7400603C0A00000000400000000000039C5F4A -:104E8400800800002E3C4F082000000000000000B5 -:104E940000280000000002B9B5EC2000000346041D -:104EA400C000000002400E8FA000280180540000C2 -:104EB4000000363760000000000000000000822E71 -:104EC400703C080000008780D000000366CDEC80B1 -:104ED400100002001FDE7080138B9E3E200080A015 -:104EE4003E06DA80020000000000020754017C7CC8 -:104EF400530000001568F50A000142B4FD040000E7 -:104F04000343400000020009905C140000000C41BF -:104F14006B0000000000000008013428F058002055 -:104F24000001094014008003601E84008000020018 -:104F34004000000280070E168000000036704F0209 -:104F44000000000000000A00A0616E18022805009D -:104F540088782800A0014015981000000020000067 -:104F6400000000050E0003C04000002BDA003100F1 -:104F7400000000000000803ED00A000100000B0089 -:104F840000000002E41CCC801000C060000000009F -:104F94000000010283D200000002D80400000000D7 -:104FA4000000000140281A0500408010013028004C -:104FB40000014295AC24000000623D0001000000A5 -:104FC40009DC3CC0000CA425FD80000100000000A9 -:104FD40001001C227A0120040090978294000002B0 -:104FE400C380000000000040358050100000091B01 -:104FF4003DC080016006CB0010028000000000006C -:1050040020066F07600000819FE165280002000010 -:105014000000000000220D000280204F8098000054 -:1050240000000063EA0020000000000001002C03DF -:10503400D0E300000000E5000C000000000100F0D7 -:10504400000001C03EE80030000B887EA000800014 -:10505400006459400001280000000107A0206AC331 -:105064006300007811B0080800000000C0F50000DB -:1050740000420300A00000000CD4000010001C76C5 -:10508400700000000000000000003E3CFF03000030 -:105094000071BB801400000000000000000002004A -:1050A4000FA0A00000000097008E800020605A80AE -:1050B40012000000000000002E7E4F0200000000DD -:1050C400AB3168000000000000000000014006E071 -:1050D400000000030AF40000000002241050000045 -:1050E400000000000000023EC800000000F1DDF8EE -:1050F40000811000034000000000E36015B02000B0 -:105104000000089202800000063C7E50000000006F -:10511400000000001E02DE80000000D9DF32010022 -:10512400000007C00000000002440CA00000000DB5 -:105134000A5600000001027C00000000000000008C -:1051440000003C015E5B000000F100000000001064 -:10515400001E8000880002021CC801000008001F15 -:10516400A00000001C70000000000000000000000F -:105174002000EEA540000001E00000000008C0256A -:10518400A806100001624600000000050A1B50003A -:105194000000343AD0E301000000000000000426BF -:1051A400E000000000010BF000000000000000001F -:1051B4000000006066C2002003800113C0000000EC -:1051C400006F5AC352000000000000002000ECA051 -:1051D40000000830CFE40840000000000000000098 -:1051E40003424505F000000B0BB402881800BC0212 -:1051F400DF0300000000000008000069D0C3112094 -:10520400005005F7FC00800002E7F02000000202D5 -:10521400C08AF0000008003380000A071C00FC026A -:105224000000000000000A001E70EEC7422800B112 -:10523400ADA1FD20A000000D90000000024340003D -:10524400000000070BF780003001002BD80000009D -:105254000800000000001C27C92500000000CDF450 -:105264000060000027400000000003C04000000070 -:1052740000050A9200800000803CCB08100000006A -:105284000000002006005C000000000001FC20007B -:105294000001E2C0000000100000260050030000DE -:1052A40008B0294041002E64000000000200000004 -:1052B4000000AC7C0000000080000B3108040000FA -:1052C400000200A0001000002F8000030005818070 -:1052D40016944100262C000000000000000000008D -:1052E4007C20000000010000000605420000000FC1 -:1052F400C0A480000142830020800001000C2580AE -:1053040000003401D0A020000400000000001E4270 -:10531400E0AF21000000804280020003C0010A00C7 -:105324001400000044A020300009803FA5905000E4 -:1053340000721E5000000000000000003C72EEA548 -:105344004000007013B280000000500C8A05000079 -:105354000142BC0000000000000000000000044105 -:10536400F0FF20000000000000001C216B10000072 -:105374000008000000000A01C7DE8FF0840002006C -:105384004EC0A00000001800000000001C637EF75F -:105394000200000000000000206DFC00000000710D -:1053A4008000000004020786E7F5000001400408BD -:1053B400020000000000154000003E40E0C3000071 -:1053C4000000800100000C00601A000000000001D1 -:1053D4006802182023800FF0840003402CE0012091 -:1053E4000000005A15500002B472DCE3020004000D -:1053F40000000040006BF85A02000018E101680048 -:10540400100142000FF100000000340010100001F0 -:10541400000803C0100E006F40E500003A000000D1 -:105424000000000030A000000200CD810800100040 -:10543400040590019000000000C022240008001A16 -:105444003C380000003F4CA50200000000000000B2 -:1054540000006EA0000004000D3B8D881000021CAB -:10546400D014000000024F0000000000080803C030 -:1054740000000203C8000010000000000000243CEB -:1054840000000000000000814000000000268200AF -:105494000C0020007C850000000001523C30000C10 -:1054A4001669CD8000308000000000280000000054 -:1054B40000000000C3B140000000000D9344000050 -:1054C400024424B0000200000F0003C040003C2A44 -:1054D400F80408004000000008423C7DC0000020A1 -:1054E40000000FAF6840800267C6D5810000020645 -:1054F40005840002800001723C30200020066A000E -:105504000000400000000A00062FDE0212280000FE -:10551400053E0564A0026216F984800000440DC0B3 -:1055240042000000000003C1400000000000000031 -:105534004000000000804002FC0000010008000060 -:1055440000000A00020000000000006624E00200DF -:10555400000000723C3020041600000000004000EF -:10556400000000008002D808000200500000000083 -:10557400000146400000000001400000000300015B -:10558400000803C0100800400000001000000B24B5 -:105594000800000000000000021080815400000098 -:1055A400000200A00000000000000003000D187AB3 -:1055B4003C306000002800000010000000040C00D3 -:1055C400000000000030008015A155000000000517 -:1055D400C0A000000142800000000000000803C0D9 -:1055E4001000000000000000000001300000002056 -:1055F40000000000000000803C00000023E5D00013 -:1056040010000006800000000001805A3C30600059 -:1056140000000000000000300004004A0004000004 -:105624000000001807A3C30000014077C800000071 -:105634000000000000000280000003C010000021F0 -:105644007C00010000700D24000000000000000137 -:10565400200000003C00000144360040000000002F -:10566400000000000000005A3C3060000073FC4061 -:10567400000000800800000000000000000280001C -:1056840005A3C300006203B7A00020000002BCD041 -:1056940082000000000003C010001C2C0000000069 -:1056A40000010900000000400000000000000000AC -:1056B4003C00000007870050000000005EE000008E -:1056C4000000007A3C3040002E0000000000000082 -:1056D40088000002003800000000000007A3C30097 -:1056E4000001E764AFA0000000000000000000001B -:1056F400080803C05000142B500000000001BD0036 -:1057040000000000000000000000E0803C000202F5 -:1057140042A5B0F00000000000000000000181522A -:105724003C30002036077E0A300000009000000064 -:1057340000000000000000199523C300040166D492 -:10574400A0B000000000000000000000080803C032 -:105754004000000020A000102000E700000004002A -:105764003050A000001080803C000000029C0052D9 -:105774001000000000000000000019523C3200003C -:10578400000178A000100000100000002000CE509E -:10579400400000819523C30000016756C8B0A800EB -:1057A40000003FE0011200001B0003C010000002D3 -:1057B400D000002000F8F10008000002E0A58020DD -:1057C4000000E0003C00000543430A001400000010 -:1057D4000CF1000280000A723C3020001602C880DE -:1057E40022A80071E8000A08000040006028800038 -:1057F4001723C30000020006CA00000004000000D2 -:1058040000000000000A400001020023CD00000057 -:105814004011FD000000003C205090000000006496 -:105824003C00000002C30500080000000000000066 -:10583400000000184020010016065A100000000065 -:10584400F80000000004CA5040000000056FC300C7 -:105854000000069CC50100000072007011020101E5 -:10586400000000000000000002000000000000082A -:10587400000000000200000000000008000000001A -:105884000000200000000000008000000000000074 -:1058940000000000000001000000000000040000FF -:1058A40000000100000000000000000000000000F3 -:1058B40010000000000000000000000000000000D4 -:1058C40000000000000000000200000000000008CA -:1058D40000000000000000000000000000000000C4 -:1058E40000000000000000000000000000000000B4 -:1058F40000000000000000000000000000000000A4 -:105904000000000000000000000000000000000093 -:105914000000000000000000000000000000000083 -:105924000000000000000000000000000000000073 -:105934000000000000000000000000000000000063 -:105944000000000000000000000000000000000053 -:105954000000000000000000000000000000000043 -:105964000000000000000000000000000000000033 -:1059740000000000000000000000000000008000A3 -:1059840000000000032000000000000000000000F0 -:105994000000000000000000000000000000000003 -:1059A40000000000000000000000000020000000D3 -:1059B40000000000000000000000000000000006DD -:1059C4008000000000000000000000000000000053 -:1059D40000000000000000000000C0000000000003 -:1059E40001000000000000000000000000000000B2 -:1059F40000000000000000000000000000000000A3 -:105A04000000000000000000000000000000000092 -:105A14000000000000000000000000000000000082 -:105A24000000000000000000003000000000000042 -:105A34000000000000000000000000000000000062 -:105A44000000000000000002000000000000000050 -:105A54000000000000300000000000000000000012 -:105A64000000000000000000000000000000000032 -:105A7400000000000001000000040000000000001D -:105A840000000020000000400000000000000000B2 -:105A94000000000000000001800000000000000081 -:105AA40000000000000000000000000000000000F2 -:105AB40000000000000000000000000000000000E2 -:105AC40000000200000000000000000000000000D0 -:105AD40000000000000800000000000000000000BA -:105AE40000000080000000000000000000008000B2 -:105AF40002000000000000000000000000000000A0 -:105B0400020000000000000000000000000020006F -:105B14000000000000000000000000000000000081 -:105B24000000000000000000000000000000000071 -:105B3400000400000000000000000000000000401D -:105B44001030C00000000000000000000000000051 -:105B54000000000000000000000000000000000041 -:105B64000000000000000000000000000000003001 -:105B7400C000000000000000000000000000000061 -:105B84000000000000000000000000000000000011 -:105B94000000000000000000000000001030C00001 -:105BA40000000000000000000000000000000000F1 -:105BB4000000000000000000000080014000000020 -:105BC400000000000000000000000030C0000000E1 -:105BD40000000000000000000000000000000000C1 -:105BE4000000000000000000000040000000000071 -:105BF40000000000000000001030C00000000000A1 -:105C04000000000000000000000000000000000090 -:105C14000000000000780000800000000000000088 -:105C24000000000000000030C00000000000000080 -:105C34000000000000000000000000000000000060 -:105C4400000000080000C000000000000000000088 -:105C5400000000001030C000000000000000000040 -:105C640000800000000000000000000000000000B0 -:105C74000000000000000000001000000000000010 -:105C840000000030C0000000000000000000000020 -:105C94000000000000800000000000000000000080 -:105CA40000000000000000280000000000000000C8 -:105CB4001030C000000000000000000000000000E0 -:105CC40000000000000000000000000000000000D0 -:105CD40080000000000000000000000000002030F0 -:105CE400C0000000000000000000000000000000F0 -:105CF40000800000000000000000000000008000A0 -:105D04000000000010000000000000001030C0007F -:105D1400000000000000000000000000000000007F -:105D2400000000000000000000001000000000005F -:105D3400000000000000000000000030C00000006F -:105D4400000000000000000000000000000000004F -:105D540000000200000000D0D0000000000000009D -:105D640000000000000000001030C000000000002F -:105D7400000000000000000000000000000000001F -:105D8400000000000000000000000000000000000F -:105D94000000000000000030C0000000000000000F -:105DA40000000000000000000000000000000000EF -:105DB40000000000000000000000000000000000DF -:105DC400000000001030C0000000000000000000CF -:105DD40000000000000000000000000000000812A5 -:105DE400FD800000084000000000000000000000EA -:105DF40000000030C00000000000000000000000AF -:105E040000000000000000000000000A02B45FD09F -:105E1400000000000000000000000000000000007E -:105E24001030C0000000000000000000000000006E -:105E34000000000000000000000008135C59B000DE -:105E44000010F13800000000C3D6A00000000030AC -:105E5400C00000000000000000000000000000007E -:105E64000000000000000000017A48D0A000040FE8 -:105E74000000000000003C14000000001030C000CE -:105E8400000000000000000000000000000000000E -:105E94000000000000000B886B59A0014010F1388D -:105EA40060000000C3C4E00000140030C000000023 -:105EB40000000000000000000000000000000000DE -:105EC4000000000002046C10B000400F000020002D -:105ED40000003C00000000041030C000000000007E -:105EE40000000000000000000000000000000000AE -:105EF40000000A92EAD0A000C010F13800000000AF -:105F0400C3C4A98F00000030C000000000000000DE -:105F1400000000000000000000000000000000007D -:105F240002A25959B000800F0040000000003C015B -:105F3400010800001030C000000000000000000054 -:105F440000000000000000000000000000000B88BA -:105F54006B50B0000010F13800000000C3C4A0185A -:105F640000000030C000000000000000000000003D -:105F740000000000000000000000000002067C0990 -:105F8400A000000F0040000000803C01000E000053 -:105F94001030C000000000000000000000000000FD -:105FA400000000000000000000000A92FB800000D6 -:105FB4000010F17800000000C3C5E00000000030CC -:105FC400C00000000000000000000000000000000D -:105FD400000000000000000002A248700000000F52 -:105FE4000000000000003C00000000001030C00071 -:105FF400000000000000000000000000000000009D -:106004000000000000000B8A7B01A0208010F17AC0 -:1060140000000000C3C5A00002000030C000000062 -:10602400000000000000000000000000000000006C -:106034000000000002046C010000800F0001400019 -:1060440000003C00000000001030C0000000000010 -:10605400000000000000000000000000000000003C -:1060640000000A92F90000000010F16366C000000D -:10607400AA85E01810000030C000000000000000F5 -:10608400000000000000000000000000000000000C -:1060940002A25B000020000F0041428000002A8120 -:1060A400000000001030C0000000000000000000EC -:1060B40000000000000000000000000000000000DC -:1060C400000000000010F17006C00000000008018C -:1060D400000C0030C00000000000000000000000C0 -:1060E40000000000000000000000000000000000AC -:1060F4000000000F004002800000000001080204BC -:106104001030C000000000007E7EA0000000083CAB -:10611400C0100000000060000000083D40000000C6 -:106124000010F12800000000908D800A000000306B -:10613400C0000000000042BAFC000004003C200043 -:10614400000000036C000800003C00000000020F87 -:1061540000000000000122CFF10A00001030C0004E -:10616400000040005F32F4602050083D60000001F0 -:1061740040005C000050083D40000009400AA1288E -:1061840000000500A93CC10A00140030C000000052 -:106194000000FF0E86000012003C000000004002D8 -:1061A40040000010003C00500014400AA00000070A -:1061B40001011527C50000041030C00000000000D4 -:1061C4000E9E56000024083DC07000008000400070 -:1061D4000020083D4050A000C02A500006C0030122 -:1061E4001406110000080030C000000000000FDA9F -:1061F40040000020003C00080014C0038000002080 -:10620400003C2050A0000005A028028000002802C5 -:10621400D580140C1030DC0000000040CC50460443 -:106224000004083D60108000000040000000083DAC -:106234004080A1C0002A500000000000015DC00A97 -:1062440000000030C00000000000CC0B002800005B -:10625400003C20090000000380000000003C2030C6 -:10626400A0000005A028000000800001F00A000042 -:106274001030CC001E000000000020700002083C1A -:1062840040100040000000000000083D60000020B5 -:10629400000000000000000001548D9B120000303B -:1062A400C00000000000000004000000003C2008C2 -:1062B40000000003C0000100003C000000000000DA -:1062C4000000000000000142050A00001030C00078 -:1062D40000A200020F700028380000000000002017 -:1062E4000000207C0800083D600000200000000041 -:1062F400000080300016980002000030C00000004A -:1063040000040F003C4050000000000000000003A7 -:10631400E43C0000003C0000000000000000000716 -:1063240000000801AD0040001030C0001E00020251 -:106334000F30007000000000000010000080266490 -:106344000000083D600000200000000000008020E4 -:106354001287B90A01480030C000000200000F0093 -:106364000040000000000000D01000F3A66470009C -:10637400403C2000000000000000000080002D646C -:106384009B0002481030C000000000820F3800005B -:10639400000000000000000000801E640030083C83 -:1063A400600001C000000000000000000046800002 -:1063B40000000030C000000000000F08340030006E -:1063C400000000000020003380640020003C200016 -:1063D4000000000000000000000100368000000002 -:1063E400004021E000000000551A766800082C3DAA -:1063F400CA000000000000000800083DE09800000A -:10640400102831FE06000000A81CA00000000000B7 -:10641400050000000200AA025400000003C0C000EE -:106424000000000000000000003C20100000000EEE -:1064340033A143C00000ABC2E000800000400180F3 -:10644400000000000000002802002C3C4E00000167 -:10645400400000000800083CC0001009106831F535 -:10646400C0000500AA9C0000001000000D030C40B1 -:10647400000000001400040003C0000000004000FD -:1064840000000000003C00008010400E316423C076 -:1064940001009540F0000004004020A0000000002E -:1064A4005012460000082C3C4A98100002001C00C0 -:1064B4000020083CC0000000C000000006D200001C -:1064C400BC1C00010000000014000D000000000AC4 -:1064D4008000000003C010108000000000000030A5 -:1064E400003C00000000C000000002800000BC026C -:1064F40000080000004021C01B0000005076546CCE -:1065040000081C3C4A019000000000000000083C08 -:10651400400000080000A5A1E00400000000000005 -:10652400000000000D000B000000F0060028000031 -:1065340003C01000E000000000000000003C200048 -:1065440000100900A080000000000000000000000E -:10655400004021A99D0000446610406000002C3CCE -:106564005E001000000000000000083C40019000A4 -:106574000000000002C41000000000000000000041 -:106584001C0799000000990AC040000003C00000E5 -:10659400F000000000000000003C20008002000029 -:1065A400000002002000000000000000004000C8BD -:1065B4001E000000C61A400000001C3C5A000000E7 -:1065C400000000000000083C600181C000000000E1 -:1065D4000000000000000000000000002D0F00007B -:1065E4000000C93E0000000013C000000044000089 -:1065F40000000000003C0000000000000002C00099 -:106604000000000000094000004001E01800020002 -:10661400815BE06000000AA85A01900000000000BD -:106624000000083CE07000000800A37C00000000AB -:1066340000000818000000000C0010000000BDD687 -:10664400E440000012A80000F00001000000000077 -:10665400003C000800000000007C00000000000076 -:1066640001000000004000E81D4000005A7680014F -:106674000000000000001000C00000000000000046 -:1066840000000000000FA1294000030000000000EA -:10669400000C000000090E200000A007C0008000CC -:1066A400000000008000400000000000000000F036 -:1066B4000214004FF0A94348000000000000000845 -:1066C4000003C4001B0000005A3754040000083DB6 -:1066D400E0000000000000000000183CC050A000D2 -:1066E400002B5BB000400000A9400001000008033B -:1066F400C00008000000500B16207002003C00008F -:106704000000000000000002203C0050A00000A196 -:10671400A5A00206000016808D0E000000000000F7 -:106724001E001400A03F00640050083CE00000017B -:106734004000000070081A940800A00100005731BE -:106744006013050083CC00000094000000001000DA -:106754000400A00400210012003C200000004000BE -:106764000000000202940050A0006040003C202081 -:10677400010003C2000001040000069D9E00000009 -:10678400A07BE002F100083DC0000000C000000052 -:1067940000001A9408000800C00053E00000400004 -:1067A40083DC00002008000814F510000000000F2E -:1067B40004010200403C00000000C0000000000290 -:1067C400029400000000E00050400000000103C0FB -:1067D4000F0F0008100215A79A4000402E3A00003F -:1067E4000000283CC0000200000000000000183C2B -:1067F40040000000000000000206500083DE00009C -:106804000000000303B080000000AD0B002C420028 -:10681400003C20000000000000000002003C2000BA -:1068240000000000000002E8200003C20000002075 -:1068340014835C1180000040F216266500000000FD -:1068440000800A00000000000008183C4001E0003D -:106854000800A379E7C0200083D4081E00000003C9 -:1068640080A00B000000F00B5668F000000000F060 -:106874000580000000000000003C20000000000033 -:10688400A080C240000003C00F10500000000000B0 -:10689400000000000072206404000000008000007A -:1068A400000000000000183C60000400000000012B -:1068B400E7910020A940080000000000000000004B -:1068C40000000507A464000000000070050000003B -:1068D40000000000003C0000080000000000002848 -:1068E40000001680BF00000000280DE50000000035 -:1068F40001F2A066F1000000007810000000000022 -:106904000000083C60380000080AA76A06040080FA -:1069140083CC00010000000406F08B000000007628 -:106924006425020000000008F000000000000002DE -:10693400503C00080000010AA00247C0000003C048 -:10694400000F140030B1CDB3800000C2D3366038DC -:106954000000000000001090000000008000083CCF -:1069640060000000104F03000040020083CE078047 -:1069740004000030C0808B000000DB02A400020091 -:1069840000000000E004000000000002503C200071 -:106994000000000F00800328032003C20000000051 -:1069A40000C3C6A0000000000A7300000000283CD9 -:1069B400E000000000000000020000426AD0000075 -:1069C40000005175E0000000006FA00B0000003CC7 -:1069D4003E00000000800F0B40000000003C00005F -:1069E400000000000000050020005B7000000005AE -:1069F400F0F1C00000010014B01E000000C3C4E5A3 -:106A040000001400053F80000000283D60000001E4 -:106A140040000000000000416B80A0814407A3E215 -:106A240066600540ABDD8B9A0014003C00008000DA -:106A34000400050000000000003C000000004000CD -:106A44000000000020024A70A000400150F843C03A -:106A5400010016818900000400C3C4A9810000401C -:106A64000037C0000000083DE0700008800000000E -:106A74000008083D4050A0008423D1E560000000D8 -:106A8400202FAB1CA000003C010108000000800F77 -:106A9400C0000000003C00080004C000000000002A -:106AA400003C2050000088026074200000000014A4 -:106AB400B710000000C3C4A818000040C230000092 -:106AC4000000083DC001E800000000000200083D8D -:106AD4004038A408008767F0038000008135E81D72 -:106AE4004000003C010100000000C10B0000000058 -:106AF400203C00000000000000000400003C20389E -:106B0400A200000347F4040000800024D9182000E8 -:106B140000C3C5E000000000507336680000083D63 -:106B2400C0000000000000000000409EC1180000EA -:106B34000020F700E04000000A06150E0020003C8B -:106B44000000000000000A0354000000203C007014 -:106B54000000000000000000006F69000000000059 -:106B6400F08022400020051CC090000000C3C5A096 -:106B740000000000000000040000483CC000E000E9 -:106B8400000000000000083C60800000000051ACE0 -:106B940046C000110016B00F0080003C0000000049 -:106BA4000000000000280000003C209000000000CD -:106BB40000000000003C00100000000AF074424095 -:106BC40030800081C00A014000C3C5A7180000003E -:106BD4000000200400000A9400108000000000005F -:106BE4000000083C60000140000A53F60040000029 -:106BF4000094C7180000003C0100000000000000E1 -:106C04000428000001680F09E000000000000000F3 -:106C1400003C00000000000550A9C3C00000001E95 -:106C2400F080000000AA85E181000C0000003669B4 -:106C34000004083CE0000008000000000000029589 -:106C44007D000000000AF32560000000007C900A2B -:106C54000000002A81000800000000001400000069 -:106C6400003C2000900000000000000001543B00A4 -:106C7400000000005074000000000034E018000020 -:106C840000007200901103010100000000000000E8 -:106C940000020000000000000800000000020000E4 -:106CA40000000000000000000400002000000000BC -:106CB4000000800028000000000000000000000127 -:106CC40000000000000004000000000100000000BB -:106CD40000000000000000000010000000000000A0 -:106CE4000000400000000000000000000000000060 -:106CF4000000000000000000000000000000000090 -:106D04000000040008000000000000000000000073 -:106D1400000000000000000000000000000000006F -:106D2400000000000000000040000000000000001F -:106D340002800000000000000000000000000000CD -:106D4400000000000000000000000000000000003F -:106D5400000000000000000000000000020000002D -:106D6400040000000000000000000000000000001B -:106D7400000000000000000000000000000000000F -:106D840000000000000000000000020000000000FD -:106D940000000000000000000000000000000000EF -:106DA40000000000000000000000000000000000DF -:106DB40000000000000000000000000000000000CF -:106DC40000000000000000000000000000000000BF -:106DD40000000000000000000000000000000000AF -:106DE400000000000000000000000000000000009F -:106DF400000000000000000000000000000000008F -:106E0400000000000000000000000000000000007E -:106E1400000000000001000000000000000000006D -:106E2400000000000000000000000000000000005E -:106E3400000000000000000000000000000000004E -:106E4400000000000000000000000000000000003E -:106E5400000000000000000000000000000000002E -:106E6400000000000000000000000000000000001E -:106E740002000400000000000000000000003000D8 -:106E840000000000000000000000000000000000FE -:106E940000000000000000000000000000000300EB -:106EA400028004000000000000000000F000000068 -:106EB400400000000000000000004000000000004E -:106EC40000000000000000000000000000000000BE -:106ED4000000000000000000000000003800000076 -:106EE400000000000000000000000000000000009E -:106EF4000000000000000000000000000000080086 -:106F040000000000000000000000400080000000BD -:106F1400000000000000000000000000000000006D -:106F2400000000000000000000020000000000005B -:106F3400000000000000002000000000000000002D -:106F4400000000000000000000000000000000003D -:106F54000000000000000000000004000000000029 -:106F64000000000010000000400030C000000000DD -:106F7400000000000000000000000000000000000D -:106F840000000000000000000000000000000000FD -:106F9400003C02000000001030C0000000000000AF -:106FA40000000000000000000000000000000000DD -:106FB4000000000000000000000000000000203C71 -:106FC40016018000000030C0000000000000000036 -:106FD40000000000000000000000000000000000AD -:106FE40000000000000000000000000000294090A4 -:106FF4000000001030C0000000000000000000008D -:10700400000000000000000000000000000000007C -:1070140000000000000000000000001680100000C6 -:10702400000030C00000000000000000000000006C -:10703400000000000000000000000000000000004C -:107044000000000000000000003C000000000010F0 -:1070540030C000000000000000000000000000003C -:10706400000000000000000100000000000000001B -:10707400000000000000003C16000000000030C0CA -:1070840000000000000000000000000000000000FC -:1070940000000000000000000000000000000000EC -:1070A40000000000003C00000000001030C00000A0 -:1070B40000000000000000000000000000000000CC -:1070C40000000000000000000000000000000000BC -:1070D4000000003C16000000000030C0000000006A -:1070E400000000000000000000000000000000009C -:1070F400000000000000000000000000000000008C -:10710400000000000A00001030C000000000000071 -:10711400000000000000000000000000000000006B -:10712400000000000000000000000000000000005B -:1071340000059B00000030C00000000000000000BB -:10714400000000000000000000000000000000003B -:10715400000000000000000000000000201400F007 -:107164000000001030C0000000000000000000001B -:10717400000000000000000000000000000000000B -:1071840000000000000000000000002806100000BD -:10719400000030C0000000000000000000000000FB -:1071A40000000000000000000000000000000000DB -:1071B40000000000000000000000000500000010B6 -:1071C40030C00000000000000000000000000000CB -:1071D40000000000000000000000000000000000AB -:1071E400000000000000000000080E00000030C095 -:1071F400000000000000000000000000000000008B -:10720400000000000000000000000000000000007A -:1072140000000000000000000000001030C000006A -:10722400000000000000000000000000000000005A -:10723400000000000000000000000000000000004A -:107244000000000000000000000030C0000000004A -:10725400000000000000000000000000000000002A -:10726400000000000000000000080AA040024000E6 -:10727400000000000000001030C00000000000000A -:1072840000000000000000000000100000000000EA -:1072940000000000000000002AA168004003000074 -:1072A40000058000000030C0000000000000000065 -:1072B40000000000000000000000000000000000CA -:1072C4003C6D000000000F004000000000000000C2 -:1072D4000A00001030C000000000000000000000A0 -:1072E40000000000000000000000000000023B78E5 -:1072F4000000000030F168060000000000000100FA -:10730400000030C000000000000000000020000069 -:107314000000000000000000000000000000000069 -:1073240000080F000002400020000FD000000010F1 -:1073340030C0000000000000000000000000000059 -:107344000000000000000000000000000000000039 -:1073540030F16800400000041EC00000000030C08E -:107364000000000000000000000000000000000019 -:107374000000000000000040006B100000080F0037 -:1073840000000000000000050000001030C00000F4 -:1073940000000000000000000000000000000000E9 -:1073A40000000000020043788000000030F1680013 -:1073B40000040000000D9A00000030C0000000002E -:1073C40000000000000000000000000000000000B9 -:1073D400000000023800500000000F00404200008E -:1073E400000000000000801030C000000000000019 -:1073F4000000000000000000000000000000000089 -:107404000102397881A0000030F12A6640000000B2 -:1074140000000001400030C0000000000000000037 -:107424000000000000000000C00000000000000395 -:10743400FC2B500000000F00404200000000000040 -:107444000000001030C00000000000000000000038 -:107454000000000000000000000000000002BD4821 -:10746400D800002030F12A60400000000000001025 -:10747400000030C000000000000000000000800098 -:10748400000000000000000000000000000000A058 -:1074940000100F000000000100004FE00000001089 -:1074A40030C00000000000000000000000000000E8 -:1074B40000000000000000000000000001B0001007 -:1074C40030F13800000520015EC00000000030C02B -:1074D40000000000000000000000000000000000A8 -:1074E40008000000000000000000000000080F0376 -:1074F40000000000000000000000001030C0000088 -:107504000000000000000000000000000000100067 -:10751400000000000000000001A0000030FFA800EF -:107524000000000000000000000030C00000000067 -:107534000000000000000001683E50800080000050 -:1075440000000410001C10000000000079424000FC -:10755400002D6DE30A00001030C0000000000000A0 -:107564000000000004097DFBF180008000000000A1 -:107574000500046CF00000000053B4060000001283 -:107584009CD00000000030C0000000000000024059 -:1075940000000000000000000000000000000000E7 -:1075A40021CA01B0000000F0A4000000003E8300E6 -:1075B4000900001030C00000000000053E0000007B -:1075C4000000000000000000000000000610016E32 -:1075D40000A00000055121400000003E8FA70A00D2 -:1075E400000030C000000000000000004000040261 -:1075F400380E00000000000000000040001FD8000A -:107604000200055040028000000000001800001035 -:1076140030C000000000000000006000010935E1F6 -:1076240018000000000000000000146D500000006D -:10763400055520000000000000050E00000030C0C9 -:10764400000000000000000000000000017F0001B5 -:10765400C800000000000000000011D000080F0066 -:107664006D440020000282051040001030C000006C -:1076740000000000000000000000065D000004009F -:10768400000000000000000080F0000000F17E0611 -:107694008840000296C50C80000030C00000000045 -:1076A40000000000000000000000000000000000D6 -:1076B40000000002941C00A20000069D404000202F -:1076C40001003E850E00001030C0000000000000E4 -:1076D400000000000000000018A0000000000000EE -:1076E40001116C780004000816993B60001000003A -:1076F4007DD00100000030C0000000000000000048 -:107704000002000000000000000000000000002053 -:1077140000FF00000000069740040400001680905B -:107724000000001030C00000000000000000000154 -:107734000000000000E00000000000000242005DC4 -:1077440000A0000816996002800014148E900000B6 -:10775400000030C000000000000000000000000035 -:1077640003F900E00140000000000401401D10B0D6 -:107774000008069340400000003FA6E19F0000106F -:1077840030C000000000000000006800000007FA9C -:1077940000000100000000000003E87D80000000FC -:1077A400169163660000003FED890C00000030C0B4 -:1077B400000000000000000C0000000000000000B9 -:1077C400000000000000000000000000002006612E -:1077D4000366C000400000800000001030C00000BC -:1077E40000000000002000000000000050000040E5 -:1077F400000000000000000050000000166061421C -:10780400800000014FB00A0C0000000000000000DE -:107814000000000000010000000000008080000063 -:1078240000000001694C010140C00FA0C00000002D -:10783400413FFCD0080000000000000000000000F0 -:107844000000000000000000000000C00000000074 -:10785400000293CB11E000C00FA1E8000003609F79 -:10786400D6A5000000000000000000000000000099 -:107874000000000000000000000000000000000004 -:107884000028000000000C396C001080250057AB64 -:1078940008040000000000000000000000000000D8 -:1078A4000000000000000000000000000500057C4E -:1078B4000180001028BDF406080000801CDB800055 -:1078C40000000000000000000000000000000003B1 -:1078D4000C7C00F0000000000000000028300000D4 -:1078E40000010E33B74000000032C3B000408000F6 -:1078F40000000000000000000000000000098E7B72 -:107904000090880000000100000029EA00802000A7 -:10791400283DA74600803030CDF188A30000000048 -:10792400000000000000000000000000000000C093 -:107934000000000000700402BC0A00000000000007 -:1079440000000000000001F58000000000000000BD -:107954000000000000000000000000000010000013 -:1079640000000000021A815A0000000000000060BC -:10797400001000014FC39A00000000000000000046 -:1079840000000000000000436B79080000000000C4 -:10799400000000403C2A308000000000E802D000D3 -:1079A40000000FE180000000000000000000000063 -:1079B400000000000008947A50000000000000005D -:1079C4000318AD4E80F000000053A162A80000002F -:1079D40055AF0E0000000000000000000000000091 -:1079E400010000000000B1000000000000020003DC -:1079F400E80000A000000000002386200000000032 -:107A040010000000000000000000000000000280E0 -:107A14000000000080A00000000000010003E968ED -:107A240018A00000000001E00040000000038C00EA -:107A34000000000000000000000000000000001131 -:107A44007C08900201000000000008203D4800006E -:107A5400004105B03442400100029ED300000400FE -:107A6400000000000000000000000000000ABD400B -:107A74008180014000000000084A3BDA0000014414 -:107A8400ADB1EF404005000126980000140000004D -:107A940000000000000000000000000000000000E2 -:107AA400080000000030000291CB00D000000C3729 -:107AB400EFC2C600000000000D000000000000003E -:107AC40000000000000000000000000019800440D5 -:107AD400000000000041696800B000000631B208EF -:107AE40020000000000001800000000000000000F1 -:107AF40000000B4040000040003F00000000000078 -:107B04000000000006E9B8D000000080A0200000BA -:107B14000003FE9B100000000000000000000005B0 -:107B240077802800000015CC780000C00000000019 -:107B3400080001FFF9D000148003B54200000203DD -:107B440085FD0E000C000000000000000000B7C21C -:107B5400000800000000B9802000000000000003BD -:107B6400BEEC000020800AF03DC700800000000F3A -:107B74000002000000000000000080013B142010FF -:107B840000000000F9E1800000000000000179E934 -:107B94007980008005F506074300000000080040D6 -:107BA4000000000000000000000A0AC040000000BD -:107BB400000031F000000000000200000039F880ED -:107BC40000000580F44003000000000300000000F2 -:107BD400000000000000820550002808000000009A -:107BE40080E0200000000005000029DD9000200452 -:107BF40005A5E54001800000000D9E020000000084 -:107C04000000000000050ACE00000003C000D0E020 -:107C14000000000000000052901DB0900000080712 -:107C2400600280500030E7B0000C0000000000004B -:107C34000000060A502E00080803C0C0819000000E -:107C4400000000000110014DF8100000000D344048 -:107C540000280018EE80000800000000000000006A -:107C640000AF06C028000001603B300000000000A7 -:107C7400000000000358B0020000000000000000F3 -:107C8400050FC0B90900000000000000000002A0B8 -:107C940052940000010169E950000800000000004E -:107CA400000202DF81840000000001E00000108F68 -:107CB40046978800000000000000000000F3081C44 -:107CC4000000000020CE00000000000001000002BF -:107CD400D5EF00BA0000000001C28000001780F058 -:107CE4008000000000000000000040F35026600007 -:107CF400000002EE18000000000002800803D6FE17 -:107D0400180CC000000000000300100757D7000043 -:107D1400000000000000000000811E94000050419B -:107D24006C19000000400000020004412010010012 -:107D3400004040003D4740400016C3FF08000400D7 -:107D440000000000000000A1DE402800510B6CCAB6 -:107D540000C4014000000000011921C81080014442 -:107D6400001720024020102957D7010014000000FA -:107D7400000000000000000000700003974D700038 -:107D84000000000000000202836A100008000000E6 -:107D9400BC038500040F17EB8F40000000000000B7 -:107DA40000000000000000000000966D900000003C -:107DB40000000000000340EB80A0100000553C00D0 -:107DC4000000000E3D9D00200000000000000000A7 -:107DD4000000F68020000241403F71D0088000007E -:107DE400000000400A7C790000000FA03E434000E0 -:107DF40000210DD90004080000000000000000105C -:107E0400728004000011685A50D0108000000000F5 -:107E14000000027E50C000C00F55046040000020E6 -:107E24007CF80080080000000000000000500744B7 -:107E340000000042BC0A70040000000000000003BF -:107E44000EEB010400000CBD2064000000000000E3 -:107E540000004000000000000000020A12E0600080 -:107E64000000155C000A000000000000000A2EDC7F -:107E7400018200000C3BFD460300000000058E005B -:107E8400000000000000000000A002C4000008017F -:107E94007C0E50080000000000000001683B19003F -:107EA40000000F008000005000028300100000005A -:107EB400000000000000030A16A00000000014CB1C -:107EC40001C0000000000000010290EBD0C00000DF -:107ED4000F070003010000028CA50C000000000045 -:107EE4000000000000A0024000000000000000C0EC -:107EF40000000000006000016C2B00E000000000A6 -:107F0400A5C000000000000B0000000000000000FD -:107F14000000030A178000000000000000000000B9 -:107F240000000000010295591191401800573006D5 -:107F3400800000000001800000000000000000003C -:107F440000A006C0000008030E6800000000000046 -:107F540000000000140FD800000000F06822400068 -:107F6400004282DD00000800000000000000030A57 -:107F740012806000000B1DFE500000000000000095 -:107F8400000029C17180000400D5FCE6C0004081D6 -:107F94004C080001480000000000000000A0067426 -:107FA40020000800142001E000000000000000434D -:107FB400C00058040000000938020000440F03D137 -:107FC400001C0000000000000000030A13860400E7 -:107FD400000015E801E00000000000400003C0C0FC -:107FE400900A0000080D6002C000100F5EA5980002 -:107FF400000000000000000000A002C000004203D6 -:10800400FC2800900100000000000003C40CF8B13B -:1080140000400F008143C70000140290080144008F -:10802400000000000000030A13800000400814FE52 -:108034000000010000000000011BD17878A001407D -:108044000F030002C00000168D80000094000000A1 -:108054000000000040A0027420000000000010F0A6 -:10806400000000000000000081FC0000000300F09C -:1080740002C00000040F17D0000000000000000040 -:108084000000030A1280000000000000198008406C -:1080940000000000000002F80000002000F10200CF -:1080A4000004040F57E580000000000000000000F9 -:1080B400000000000000004002F801D100000000B0 -:1080C40000100003FC3E0890000000054000002062 -:1080D400001001D989000000000000000000000029 -:1080E400000000003002917F79D000000000000001 -:1080F4000003E8DF38C000001008204000501100E1 -:108104001FE38C000C0000000000000000000000D1 -:108114000000000000000000000000000000000358 -:10812400FF48F0B00428069F00E000000002EDF0D4 -:10813400900000000000000000000000000000505B -:108144000000000001E00000000000000001FDDD6F -:1081540080000800169160C00820000167C1080172 -:1081640040000000000000000000000000040200C5 -:10817400003C09000000000000000000015D010057 -:108184000020469F01400800000000099000800084 -:108194000000000000000000000000720100047BE9 -:1081A40079E00000000000000000066B19C0000820 -:1081B400169129C01000000000011E0140000000BB -:1081C400000000000105080000000000003F30002E -:1081D4000000000000000003FF6B700000084693DD -:1081E400400700002017C08380002000000000002A -:1081F4000000020556800000000014F919C00000B8 -:10820400000000220003F97B01800008169160023F -:10821400000000A95E9B00000000000000000000B8 -:10822400000000002000002316EB70000000000096 -:1082340001000200001F70A0000006910002C000AF -:10824400000FC290808000000000000000000000C9 -:1082540000000400021B004FB00000000000028078 -:108264000100117899B20000169160004000D0AF6F -:10827400D695000000000000000000000000001C73 -:10828400200000100750010000000000000000035F -:10829400E800000080000EDF6C068600001FDCB0E2 -:1082A40000000000000000000000000000200400A6 -:1082B400000006EC00800000000000020003E968F2 -:1082C4003000000017B97062C000001E9E8180005B -:1082D4000000000000000000000000000000100288 -:1082E400E80CF0005440000002000001400850F087 -:1082F4000020869340268000201542008800040058 -:108304000000000000000000000000005120695E31 -:10831400B8C0014000000104000281E080100000A8 -:10832400169171C28000011554811F0014000000D1 -:10833400000000000105080000000001542DF800B1 -:108344000000000000000800025A000000000F01B5 -:10835400804200000030C6D0180000000000000079 -:1083640000001205528000000002945EF800000034 -:1083740000000000000006CF01A000001F012B62D6 -:10838400F00400A2DFC01D1C00200142E00B00002D -:1083940000000000000002217C30380014000000BE -:1083A40000000003F40F7900000043F0A007402010 -:1083B4008029A7C108000000028C10000000000002 -:1083C4000000000000017DDA7000080000000000D9 -:1083D4000103697EB1C000D423D17842401300A9BF -:1083E40077E9801800000000000000000000000091 -:1083F40000000000000008F0000000000000000081 -:10840400003FF09000000FC020000000001680AD77 -:1084140090000000000000000000000000000000C8 -:10842400000000003810000000000002000014DD0D -:10843400800000040585F942000000A940010C10E9 -:108444000000000000000000000000000000000127 -:108454007C3C00901000000000000000F82CB0D01C -:1084640000000C29E167000000004F90000040006C -:10847400000000000000000000000000000014DE06 -:108484000180C80000000070074AF0FBB2E0000859 -:10849400081BBDC30000000006E0000080000000CF -:1084A4000F0000000000000C00000000143F110049 -:1084B4000000000000000001683B7800000004A5F3 -:1084C40060200000003002001800000000000800D6 -:1084D40000000000002000000002BD5901C000009F -:1084E400000000200102917FB9E100002AD7E44096 -:1084F400000011B01C000F00000000000000180074 -:108504000000000000400001542B508200000000D5 -:10851400000400016CEE7000000000000002000086 -:108524000020C2E000A0C000000000000000000025 -:1085340000000000000295E9101200000000000293 -:10854400000294DC998000000000006040201000CC -:108554000795004C400000000000000000000000EF -:108564003C002020980C5000000000000000000097 -:10857400003F000000000C3D70000B1000061E8040 -:10858400004800000000000000000000000004009B -:108594003108F57D0000000000000000001014DD2B -:1085A40000001400063BA4039000000B84D00800D4 -:1085B40000000000000000000000000000001000A7 -:1085C400A80900F14040000000000203C0000000C0 -:1085D40000400F00802600010003C00508000400CD -:1085E4000000000000400000000000804101E8F8A5 -:1085F40078100140000000000003C1600001014048 -:108604000F0580E241050083CC05810014000000C1 -:108614000000000000F0000000000002BC2F00D0A9 -:10862400000000000280000000000000000000F7CD -:10863400FC6200000003C2000001400000000000D2 -:10864400000006F050000000000015EF18100000B4 -:1086540000000100000000000008000000E3B7E390 -:10866400C0000083DE010000C00000720080110021 -:1086740062002F8200000103D7D728080000F7D337 -:108684000C240200F3F708000002FFFF00000101C0 -:10869400BFBF44042020FBFB04142020EBEF100098 -:1086A4000420FDFD02004004FFFF00000040EFEF46 -:1086B40010200000F7DF28088584F7DF200400017C -:1086C400DBEB1C188000E7F700080080F7FF0800C8 -:1086D4000202FFF30C0C0200F3F30C080002F7F79C -:1086E40008080000F7F708000000FFF71008000072 -:1086F400C7EF20000400EFCF31110004EDCE3604A3 -:108704000000DBF90A0C0000FFF500020000FFFF87 -:1087140000000404FBFB04000000FFFF0000000055 -:10872400EFFF10100000CBEF24242000FFFB00001B -:108734002020FFFF00082020E5F613190100FDEDBD -:1087440002220001FDD52A0A0000DFDD220000001C -:10875400FBDF04240000FFFB00000000FFFF040413 -:108764000000FFEB10100000CFDF22020101FFFD2B -:1087740000000000FFFF08080000FEE6111242009E -:10878400EFFD02000042F7F50E000202F9FB0004BF -:108794000000FFFF08002120F7F700082021F3FB69 -:1087A4000C141020EFE718081414F7F50A0A5050B7 -:1087B400F7F708081010F7F708101010EDE7120883 -:1087C4009511F7FD02001094FFFD06049090F1F35B -:1087D40008001010FFFF00000210BFBF4404020293 -:1087E400FBFB14100000EFEF00100000FDFD020081 -:1087F4000000FFFF00000000EFEF10100404F7FF7B -:1088040008080000F7FF00000000EFFB1C1E00003A -:10881400E3E50A0A0000F5F50A0A0400F5F10E047E -:108824000004F9F300080000F7FF00000000FFFF58 -:1088340000000000FFEF10102000FFEF00002020D8 -:10884400E7ED130B2020FDEC12160101F9E90E0AE5 -:108854000000FDF502020000FDFD02000000FBFB2C -:1088640004000000FFFF00100000EFEF10100100F3 -:10887400EFFB04040001FBFF00000000FFFF0A00FF -:108884000000F5F4130B0000EDED12004242FFF579 -:108894000A080002FDFD02023020FDFD0600303012 -:1088A400FBFB00041111FFFB14041410EBEF100088 -:1088B4005014DFDF22021050FFFD00001010FFFFF4 -:1088C4000A081110FCF413121011EDED1202949425 -:1088D400F5F50A021090F9F906021010FFFD0800E0 -:1088E4001210F7F7080C1212E3F3181C1010E7E744 -:1088F40008080010FFF50A020000F7FF000004005A -:10890400FDFF02020004FDED12120000FDED020263 -:108914000000F5FD060A0000F1F10A0E0000FDFF5B -:1089240000000000FFFF04040404EBEB101400003B -:10893400EFFF00000000FDFD02000000FFFF00004B -:108944000000FFEF10100000EFF708082020FFFFE1 -:1089540000022120FDFD0E160001E5E11A100000C1 -:10896400E7FD02080000FDF906060000FDF902001B -:108974000000FFFF0000010100008200800103FFEE -:10898400FF10000000EFEF10000000FFFF020800DE -:1089940000E5E51B030000FDFE10005202EFEB1C96 -:1089A4001C3052E3F700023030FFFD02021010F9D0 -:1089B400F904121110FFEF10100415EFEF0010402E -:1089C40040FBFB04000000FFFF00000000FDFD0A67 -:1089D400020101F7F401030000FFEF10108400E727 -:1089E400EF08180004F7FF00020000FDFD02061264 -:1089F40002FBF914001210EFEB14141012FFEF0035 -:108A0400001010DFDF22021010FFFD00000414F537 -:108A1400FD0A0A0000F6FC01020000EFEF1010004E -:108A240000F7E708100000FDFB06040000FDFD024E -:108A34000A0000E5F71C000400E3EB10180004EF43 -:108A4400FF00000000FFFD02020000FFFF02000023 -:108A540000FDFD02020000FFFD10020000EBEF101C -:108A6400142020F7E30C120121F3F10E0A0000F5A3 -:108A7400FD02100000FFEF14140000EBEB100001E6 -:108A840000FFFF00000001FDFD02000000FFFF00E9 -:108A9400000000EFFF10101000F7E708001210FFAD -:108AA400FD02023012FDFD040A7272F3E310180095 -:108AB40010EFEF10000000EFFB04060400FBFD02C2 -:108AC400024105FDFD00120040EFEF10100000FF11 -:108AD400EF00000100FFFD0A020001E7E5110900B3 -:108AE40000FFFE00000000FBEF1C140404E3E71089 -:108AF400180200EFEF10120002E9E916120000ED6F -:108B0400EF10000202FFFF00041000FBFB00041042 -:108B140010FFFF02001010FDFD0A081010FFF601FF -:108B2400010010FFFF00100000E7EF18180000EF2D -:108B3400E710140000EFEB16100000E9E91614002A -:108B440000EBEB04100000FFFF00000000DFDF2259 -:108B5400020000FFFD02020000FDF50A080000F615 -:108B6400FE01000000FFFF10000000E7E7181021DD -:108B740020EBEF14140021E9EF12120000E5E51CCC -:108B8400140000FBEB00000101FFFF00000000FFE8 -:108B9400FD02020000FFFD02061000FBFD040010B0 -:108BA40010FFFB04041212EBFB141C2030E3E31C43 -:108BB400144220E3E31C180042EDE512120000ED1C -:108BC400EF14140000EFFB00004140FFFF00000021 -:108BD40001FDFD02000000FFFF00000101FFFF187E -:108BE400000000E7E712180000EDFD02000000FF9E -:108BF400FF04000000F3F3081C0202EFEF10100062 -:108C040000EFEB14140000EFEF10120200EDED1270 -:108C1400120002EFEF10000000FFFF02001000FD41 -:108C2400FD0A081010E7E711011010FFFE00000014 -:108C340010FFFF040C0000E7F318000000FFEF1022 -:108C4400000000F9EB06120000EDFD00000000FF3B -:108C5400FF04000000FBFB00000000FFFD02020017 -:108C640000FFF508080000F7FE01050000FBFF0403 -:108C7400000000FFFB0C142000F3E3140421210086 -:108C840000110162003F82000001030000000000A7 -:108C94000000001B1B0000000000001F1B00000060 -:108CA400000000041F00000000000000000000009D -:108CB4000000001B000000000000001B1B0000005F -:108CC4000000001F1B0000000000001F1F00000028 -:108CD40000000000000000000000001B1B0000005A -:108CE4000000001B1B0000000000001F1F0000000C -:108CF4000000001F1F000000000000001B00000017 -:108D04000000001B1B000000000000001B0000000E -:108D140000000004040000000000001F1F00000009 -:108D2400000000001B0000000000001B1B000000EE -:108D3400000000E4FB000000000000E4E400000088 -:108D44000000001FFF000000000000001B000000E6 -:108D5400000000F919000000000000E4F900000020 -:108D6400000000E4E4000000000000F7F700000049 -:108D74000000000013000000000000F9F9000000EA -:108D8400000000E4F9000000000000E4E40000003A -:108D9400000000F7F700000000000000F3000000EE -:108DA400000000F9E900000000000014FD000000CC -:108DB4000000001E1C000000000000F7FF0000007F -:108DC40000000000E1000000000000E9E9000000EC -:108DD4000000001CFD0000000000001E1A0000003E -:108DE400000000EFFF00000000000000E1000000B0 -:108DF400000000DDC90000000000001EDD000000CE -:108E04000000003E3A000000000000BDBF0000006A -:108E140000000000A9000000000000DDD9000000EF -:108E2400000000A2FF000000000000A6A200000055 -:108E3400000000BDBD00000000000000B9000000FB -:108E4400000000FFDD000000000000A6FF0000009D -:108E54000000008486000000000000BDBD0000008A -:108E640000000000BD000000000000FFFD00000045 -:108E740000000006FF0000000000000404000000E1 -:108E84000000003D3D000000000000003D00000027 -:108E9400000000FFFF00000000000006FF000000CB -:108EA4000000000404000000000000FDFD000000BC -:108EB40000000000FD000000000000FBFB000000BB -:108EC40000000000FB00000000000024000000007F -:108ED400000000FDFD00000000000000F90000009B -:108EE400000000FBFB000000000000FBFB00000092 -:108EF400000000FFFB000000000000FFFF00000076 -:108F040000000000FB000000000000FB2200000045 -:108F1400000000FBFB000000000000DFFB0000007D -:108F2400000000FFFF00000000000000220000001D -:108F34000000002202000000000000FBFB00000013 -:108F4400000000DFDB00000000000026FF0000003E -:108F540000000000220000000000000000000000EB -:108F640000000000000000000000000000000000FD -:108F740000000000000000000000000000000000ED -:108F840000000000000000000000000000000000DD -:108F940000000000000000000000000000000000CD -:108FA40000000000000000000000000000000000BD -:108FB40000000000000000000000000000000000AD -:108FC400000000000000000000000000000000009D -:108FD400000000000000000000000000000000008D -:108FE400000000000000000000000000000000007D -:108FF400000000000000000000000000000000006D -:10900400000000000000000000000000000000005C -:10901400000000000000000000000000000000004C -:10902400000000000000000000000000000000003C -:10903400000000000000000000000000000000002C -:10904400000000000000000000000000000000001C -:10905400000000000000000000000000000000000C -:1090640000000000000000000000000000000000FC -:1090740000000000000000000000000000000000EC -:1090840000000000000000000000000000820080DA -:1090940001032C2C0000000000001F3E0000000013 -:1090A40000009F1F000000000000CCDE0000000054 -:1090B400000000480000000000003E2C00000000FA -:1090C40000001F3F0000000000009F9F0000000000 -:1090D4000000DEDE00000000000000480000000088 -:1090E40000003F3E0000000000009F3F0000000021 -:1090F40000009F9F000000000000DEDE0000000072 -:109104000000005A00000000000037360000000094 -:109114000000813700000000000089810000000089 -:1091240000005A5A00000000000000520000000035 -:109134000000F636000000000000C1F60000000048 -:109144000000A981000000000000FAFA00000000FD -:1091540000000052000000000000F4740000000051 -:109164000000C1F4000000000000A9E900000000B4 -:109174000000F2FA0000000000000072000000008D -:109184000000F474000000000000E5F4000000009A -:109194000000EDED000000000000F2FE0000000001 -:1091A4000000007200000000000074740000000061 -:1091B4000000A774000000000000CF8F0000000032 -:1091C4000000767E00000000000000700000000037 -:1091D400000074740000000000009F740000000090 -:1091E40000009F9F00000000000074760000000053 -:1091F400000000740000000000007464000000001F -:1092040000009B740000000000009B9B0000000015 -:1092140000007676000000000000007400000000EA -:109224000000606000000000000099700000000071 -:10923400000089990000000000007676000000001C -:1092440000000076000000000000606000000000E4 -:10925400000089600000000000008989000000000F -:109264000000767600000000000000760000000098 -:1092740000006464000000000000896C000000002D -:1092840000008189000000000000767600000000E4 -:109294000000007600000000000076760000000068 -:1092A4000000897E000000000000818900000000A9 -:1092B4000000767600000000000000760000000048 -:1092C40000007676000000000000A97E0000000087 -:1092D4000000C18900000000000076760000000054 -:1092E4000000007600000000000077760000000017 -:1092F4000000FF7F000000000000F7FF00000000F6 -:1093040000007777000000000000007600000000F5 -:1093140000007760000000000000DF7F0000000014 -:109324000000B7FF00000000000077770000000095 -:109334000000006000000000000061200000000048 -:109344000000DF7F000000000000B79F0000000065 -:1093540000006177000000000000004000000000F1 -:1093640000000000000000000000000000000000F9 -:1093740000000000000000000000000000000000E9 -:1093840000000000000000000000000000000000D9 -:1093940000000000000000000000000000000000C9 -:1093A40000000000000000000000000000000000B9 -:1093B40000000000000000000000000000000000A9 -:1093C4000000000000000000000000000000000099 -:1093D4000000000000000000000000000000000089 -:1093E4000000000000000000000000000000000079 -:1093F4000000000000000000000000000000000069 -:109404000000000000000000000000000000000058 -:109414000000000000000000000000000000000048 -:109424000000000000000000000000000000000038 -:109434000000000000000000000000000000000028 -:109444000000000000000000000000000000000018 -:109454000000000000000000000000000000000008 -:1094640000000000000000000000000000000000F8 -:1094740000000000000000000000000000000000E8 -:1094840000000000000000000000000000000000D8 -:1094940000000000110262002F820000010300009E -:1094A4000020000000000022000000000022000054 -:1094B400000000020000000000220000000000661E -:1094C40000000000006600000000006600000000CC -:1094D400006200000000004400000000004400009E -:1094E4000000003700000000004400000000006499 -:1094F400000000000076000000000063000000008F -:109504000046000000000066000000000037000074 -:109514000000004600000000006200000000006639 -:1095240000000000004600000000002600000000CB -:10953400006200000000002600000000006400003B -:10954400000000660000000000660000000000400B -:10955400000000000044000000000044000000007F -:10956400000400000000006600000000000000008D -:10957400000000660000000000200000000000461B -:109584000000000000200000000000420000000075 -:10959400002600000000000000000000006200003F -:1095A40000000064000000000046000000000024E9 -:1095B400000000000046000000000022000000003F -:1095C4000020000000000046000000000020000011 -:1095D40000000002000000000044000000000044FD -:1095E40000000000006600000000004000000000D1 -:1095F400002600000000002000000000000200001F -:109604000000002000000000000200000000002212 -:1096140000000000004400000000006200000000A0 -:1096240000040000000000220000000000440000CC -:10963400000000440000000000330000000000446B -:1096440000000000006000000000001600000000A0 -:109654000061000000000006000000000066000039 -:10966400000000110000000000440000000000227F -:10967400000000000044000000000044000000005E -:10968400002200000000004000000000002600004E -:10969400000000600000000000460000000000041C -:1096A4000000000000000000000000440000000072 -:1096B40000440000000000000000000000660000FC -:1096C4000000000000000000006600000000002010 -:1096D40000000000006600000000002200000000FE -:1096E40000560000000000230000000000000000FD -:1096F400000000220000000000640000000000469A -:1097040000000000002000000000004600000000EF -:10971400002200000000002000000000006600009D -:1097240000000022000000000002000000000040D1 -:1097340000000000004400000000002600000000BB -:1097440000400000000000040000000000200000B1 -:1097540000000002000000000020000000000002E1 -:10976400000000000022000000000044000000008F -:1097740000220000000000000000000000220000A1 -:1097840000000000000000000000000000000033A2 -:1097940000000000000000000000002000000000A5 -:1097A400820080010300000032000000000023005A -:1097B4000000000002000000000022000000000081 -:1097C4001100000000000000000000002200000062 -:1097D4000000000000000000000000000000220063 -:1097E4000000000000000000000022000000000053 -:1097F4002000000000000200000000000000000043 -:109804000000000000000000000000000000000054 -:109814000000000000000000000022000000000022 -:1098240000000000000022000000000020000000F2 -:10983400000022000000000022000000000056008A -:1098440000000000230000000000000000000000F1 -:1098540022000000000020000000000002000000C0 -:1098640000002000000000000200000000002200B0 -:1098740000000000200000000000220000000000A2 -:1098840022000000000002000000000000000000B0 -:1098940000000000000000002200000000000000A2 -:1098A4000000000000000000000020000000000094 -:1098B4000200000000002000000000000200000080 -:1098C40000003300000000005400000000002300EA -:1098D4000000000000000000000033000000000051 -:1098E4001100000000002000000000001300000030 -:1098F4000000100000000000210000000000320001 -:10990400000000002300000000001300000000001D -:109914002200000000001100000000000000000010 -:1099240000003300000000001100000000000000EF -:109934000000000022000000000000000000000001 -:1099440032000000000031000000000003000000AD -:1099540000000000000000001100000000001100E1 -:1099640000000000000000000000110000000000E2 -:10997400320000000000010000000000320000007E -:1099840000002100000000000200000000000000B0 -:109994000000000010000000000023000000000090 -:1099A400000000000000330000000000310000004F -:1099B400000002000000000031000000000012005E -:1099C400000000002300000000003100000000003F -:1099D4001200000000000100000000001000000060 -:1099E400000011000000000001000000000032002F -:1099F400000000000100000000003300000000002F -:109A04002000000000000200000000002000000010 -:109A140000000200000000003300000000005500B8 -:109A240000000000330000000000100000000000EF -:109A340033000000000011000000000021000000BD -:109A440000001300000000001100000000003100BD -:109A540000000000120000000000110000000000DF -:109A640011000000000023000000000011000000AD -:109A7400000010000000000033000000000011008E -:109A840000000000010000000000220000000000AF -:109A9400000000000000330000000000310000005E -:109AA40000001300000000110362003F8200000167 -:109AB400030000000000000000000000000000009F -:109AC4000000000000000000000000000000000092 -:109AD4000000000000000000000000000000000082 -:109AE4000000000000000000000000000000000072 -:109AF4000000000000000000000000000000000062 -:109B04000000000000000000000000000000000051 -:109B14000000000000000000000000000000000041 -:109B24000000000000000000000000000000000031 -:109B34000000000000000000000000000000000021 -:109B44000000000000000000000000000000000011 -:109B54000000000000000000000000000000000001 -:109B640000000000000000000000000000000000F1 -:109B740000000000000000000000000000000000E1 -:109B840000000000000000000000000000000000D1 -:109B940000000000000000000000000000000000C1 -:109BA40000000000000000000000000000000000B1 -:109BB40000000000000000000000000000000000A1 -:109BC4000000000000000000000000000000000091 -:109BD4000000000000000000000000000000000081 -:109BE4000000000000000000000000000000000071 -:109BF4000000000000000000000000000000000061 -:109C04000000000000000000000000000000000050 -:109C14000000000000000000000000000000000040 -:109C24000000000000000000000000000000000030 -:109C34000000000000000000000000000000000020 -:109C44000000000000000000000000000000000010 -:109C54000000000000000000000000000000000000 -:109C640000000000000000000000000000000000F0 -:109C740000000000000000000000000000000000E0 -:109C840000000000000000000000000000000000D0 -:109C940000000000000000000000000000000000C0 -:109CA40000000000000000000000000000000000B0 -:109CB40000000000000000000000000000000000A0 -:109CC4000000000000000000000000000000000090 -:109CD4000000000000000000000000000000000080 -:109CE4000000000000000000000000000000000070 -:109CF4000000000000000000000000000000000060 -:109D0400000000000000000000000000000000004F -:109D1400000000000000000000000000000000003F -:109D2400000000000000000000000000000000002F -:109D3400000000000000000000000000000000001F -:109D4400000000000000000000000000000000000F -:109D540000000000000000000000000000000000FF -:109D640000000000000000000000000000000000EF -:109D740000000000000000000000000000000000DF -:109D840000000000000000000000000000000000CF -:109D940000000000000000000000000000000000BF -:109DA40000000000000000000000000000000000AF -:109DB400000000000000000000000000000000009F -:109DC400000000000000000000000000000000008F -:109DD400000000000000000000000000000000007F -:109DE400000000000000000000000000000000006F -:109DF400000000000000000000000000000000005F -:109E0400000000000000000000000000000000004E -:109E1400000000000000000000000000000000003E -:109E2400000000000000000000000000000000002E -:109E3400000000000000000000000000000000001E -:109E4400000000000000000000000000000000000E -:109E540000000000000000000000000000000000FE -:109E640000000000000000000000000000000000EE -:109E740000000000000000000000000000000000DE -:109E840000000000000000000000000000000000CE -:109E940000000000000000000000000000000000BE -:109EA40000000000000000000000000000000000AE -:109EB4000000008200800103000000000000000098 -:109EC400000000000000000000000000000000008E -:109ED400000000000000000000000000000000007E -:109EE400000000000000000000000000000000006E -:109EF400000000000000000000000000000000005E -:109F0400000000000000000000000000000000004D -:109F1400000000000000000000000000000000003D -:109F2400000000000000000000000000000000002D -:109F3400000000000000000000000000000000001D -:109F4400000000000000000000000000000000000D -:109F540000000000000000000000000000000000FD -:109F640000000000000000000000000000000000ED -:109F740000000000000000000000000000000000DD -:109F840000000000000000000000000000000000CD -:109F940000000000000000000000000000000000BD -:109FA40000000000000000000000000000000000AD -:109FB400000000000000000000000000000000009D -:109FC400000000000000000000000000000000008D -:109FD400000000000000000000000000000000007D -:109FE400000000000000000000000000000000006D -:109FF400000000000000000000000000000000005D -:10A00400000000000000000000000000000000004C -:10A01400000000000000000000000000000000003C -:10A02400000000000000000000000000000000002C -:10A03400000000000000000000000000000000001C -:10A04400000000000000000000000000000000000C -:10A0540000000000000000000000000000000000FC -:10A0640000000000000000000000000000000000EC -:10A0740000000000000000000000000000000000DC -:10A0840000000000000000000000000000000000CC -:10A0940000000000000000000000000000000000BC -:10A0A40000000000000000000000000000000000AC -:10A0B400000000000000000000000000000000009C -:10A0C400000000000000000000000000000000008C -:10A0D400000000000000000000000000000000007C -:10A0E400000000000000000000000000000000006C -:10A0F400000000000000000000000000000000005C -:10A10400000000000000000000000000000000004B -:10A11400000000000000000000000000000000003B -:10A12400000000000000000000000000000000002B -:10A13400000000000000000000000000000000001B -:10A14400000000000000000000000000000000000B -:10A1540000000000000000000000000000000000FB -:10A1640000000000000000000000000000000000EB -:10A1740000000000000000000000000000000000DB -:10A1840000000000000000000000000000000000CB -:10A1940000000000000000000000000000000000BB -:10A1A40000000000000000000000000000000000AB -:10A1B400000000000000000000000000000000009B -:10A1C400000000000000000000000000000000008B -:10A1D400000000000000000000000000000000007B -:10A1E400000000000000000000000000000000006B -:10A1F400000000000000000000000000000000005B -:10A20400000000000000000000000000000000004A -:10A21400000000000000000000000000000000003A -:10A22400000000000000000000000000000000002A -:10A23400000000000000000000000000000000001A -:10A24400000000000000000000000000000000000A -:10A2540000000000000000000000000000000000FA -:10A2640000000000000000000000000000000000EA -:10A2740000000000000000000000000000000000DA -:10A2840000000000000000000000000000000000CA -:10A2940000000000000000000000000000000000BA -:10A2A40000000000000000000000000000000000AA -:10A2B400000000000000000000002206DE0106008D -:10A2C4000400000001030000010000000100000080 -:10A2D40003000000010000000F0000000F00000058 -:10A2E40000000000F6A4000800180240100000005E -:10A2F40006040000FBA40008001802400800000047 -:10A3040006030100FEA40008001802400200000039 -:10A314000601010005A50008001802400400000021 -:10A32400060201000000000000000000005400408C -:10A334000004024040000000060004000004024043 -:10A34400000200000900040000002000801A06003A -:10A35400004000001F20000000000000000000007A -:10A3640000580040001402400200000001000400F4 -:10A37400001402400100000000000400000040003E -:10A38400801A0600FFBF0000212200000000000028 -:10A39400F513000801000300820007008400070091 -:10A3A40001000300020003004000030080000700D6 -:10A3B4008400070000000100000000000000000805 -:10A3C400004000080080000800C0000800000108E8 -:10A3D4000000020800000408000006080000080845 -:10A3E40000000A0800000C0800000E080000080025 -:10A3F40010001800200028003000380040004800F9 -:10A4040050005800537475636B20627574746F6EDA -:10A4140020726567697374657220697320696E764A -:10A42400616C69642C20636C656172696E672E00CF -:10A43400427574746F6E20697320707573686564F7 -:10A4440020617420626F6F740069636534306C70CE -:10A454002E630043444F4E45206E6F74206C6F771B -:10A4640020647572696E6720726573657400435267 -:10A4740045534554206E6F7420686967682064757D -:10A4840072696E6720726573657400446973706CD9 -:10A49400617920627573792D776169742074696DAF -:10A4A400656F75742065787069726564210044690C -:10A4B40073706C617920696E697469616C697A651D -:10A4C4006420616674657220002072657472696527 -:10A4D400732E00446973706C617920696E697469C4 -:10A4E400616C697A6174696F6E206661696C656418 -:10A4F4002E004261636B0055700053656C65637494 -:10A5040000446F776E006372632E63006932632EBA -:10A5140063004932432064657669636520494420B9 -:10A524006F7574206F6620626F756E647320256486 -:10A5340020286D61783A20256429006F6E007370BD -:10A54400692E6300206973206F75747369646520D4 -:10A5540073797374656D20666C6173680D0A00739A -:10A56400797374656D5F666C6173685F657261733E -:10A574006528002C2000290D0A006661696C656459 -:10A5840020746F20657261736520736563746F72E4 -:10A59400200073797374656D5F666C6173685F77AF -:10A5A4007269746528006661696C656420746F2043 -:10A5B40077726974652061646472657373200049FD -:10A5C4006E76616C6964206669726D77617265206C -:10A5D4006465736372697074696F6E2100436865A2 -:10A5E400636B73756D6D696E67206669726D7761F3 -:10A5F40072652075706461746500496E76616C697A -:10A6040064206669726D77617265204352432069E4 -:10A614006E2053504920666C617368210065726135 -:10A6240073655F6F6C645F6669726D7761726500F4 -:10A6340077726974655F6E65775F6669726D77615D -:10A64400726500436865636B73756D6D696E672031 -:10A65400002062797465730D0A00436865636B7347 -:10A66400756D202D2077616E746564200020676FFE -:10A674007420004F757220696E7465726E616C206F -:10A68400666C61736820636F6E74656E74732061A9 -:10A694007265206261642028636865636B73756DFD -:10A6A400206661696C656429212054686973206996 -:10A6B40073207265616C6C792062616421004F754E -:10A6C400722070726576696F7573206669726D7732 -:10A6D40061726520757064617465206661696C657A -:10A6E400642C2061626F7274696E67207570646196 -:10A6F40074652E004E6577206669726D77617265A8 -:10A7040020697320617661696C61626C6521004C1B -:10A714006F6164696E67207265636F766572792014 -:10A724006669726D77617265004661696C65642063 -:10A73400746F206C6F6164207265636F76657279E3 -:10A74400206669726D776172652C20737472696B0F -:10A7540065206F6E652E2054727920616761696E81 -:10A764002E004661696C656420746F206C6F6164AF -:10A77400207265636F76657279206669726D7761A0 -:10A7840072652C20737472696B652074776F2E2048 -:10A7940054727920616761696E2E004661696C6547 -:10A7A4006420746F206C6F6164207265636F7665DA -:10A7B4007279206669726D776172652C2073747288 -:10A7C400696B652074687265652E20534144205777 -:10A7D400415443480048415244204641554C54009A -:10A7E40065786974207374616E64627900205F5FB8 -:10A7F4005F5F2020202020202020202020202020D7 -:10A80400205F5F002F5C20205F605C2020202020E0 -:10A814002020202020202F275F5F605C005C205CCC -:10A824002C5C4C5C5F5C202020205F5F5F202F5CF1 -:10A83400205C2F5C205C20205F5F20205F5F202055 -:10A844005F5F20205F5F20205F5F00205C2F5F5CE4 -:10A854005F5F205C20202F27205F20605C205C202D -:10A864005C205C205C2F5C205C2F5C205C2F5C20D7 -:10A874005C2F5C205C2F5C205C002020202F5C205F -:10A884005C4C5C205C2F5C205C2F5C205C205C209A -:10A894005C5F5C205C205C205C5F2F205C5F2F2071 -:10A8A4005C205C205C5F5C205C002020205C2060DD -:10A8B4005C5F5F5F5F5C205C5F5C205C5F5C205C76 -:10A8C4005F5F5F5F2F5C205C5F5F5F785F5F5F2F20 -:10A8D400275C2F605F5F5F5F205C00202020205C8E -:10A8E4002F5F5F5F5F5F2F5C2F5F2F5C2F5F2F5C9D -:10A8F4002F5F5F5F2F20205C2F5F5F2F2F5F5F2F05 -:10A90400202020602F5F5F5F2F3E205C00202020EE -:10A914002020202020202020202020202020202033 -:10A924002020202020202020202020202020202023 -:10A9340020202020202020202F5C5F5F5F2F00201C -:10A944002020202020202020202020202020202003 -:10A9540020202020202020202020202020202020F3 -:10A96400202020202020202020205C2F5F5F2F002B -:10A974004C617374206669726D7761726520626FD1 -:10A984006F742077617320737461626C653B20631C -:10A994006C65617220737472696B657300486F6CC7 -:10A9A4006420646F776E205550202B204241434B26 -:10A9B40020666F72203520736563732E20746F20B8 -:10A9C400666F7263652D626F6F7420505246004645 -:10A9D40069726D7761726520697320657261736550 -:10A9E40064005761746368646F672063617573659D -:10A9F40064206120726573657400536F66747761B7 -:10AA04007265206661696C75726520636175736532 -:10AA1400642061207265736574004661696C6564C5 -:10AA240020746F207374617274206669726D77612B -:10AA340072652C20737472696B6520746872656525 -:10AA44002E004661696C656420746F2073746172B2 -:10AA540074206669726D776172652C2073747269F3 -:10AA64006B652074776F2E004661696C6564207491 -:10AA74006F207374617274206669726D7761726598 -:10AA84002C20737472696B65206F6E652E00426FA3 -:10AA94006F74696E67206669726D776172652040B4 -:10AAA40020002E2E2E0D0A0D0A00466F7263652DAE -:10AAB400626F6F74696E67207265636F7665727911 -:10AAC400206D6F64652E2E2E004153534552543A27 -:10AAD40020002020003A0041535345525400415372 -:10AAE400534552544E002A2A2A20575446200053D4 -:10AAF400544D33320053544D3332207065726970B3 -:10AB0400686572616C206C6962726172792074721A -:10AB1400697070656420616E20617373657274007E -:0CAB240043524F414B204F4F4D000000AA -:10AB3000FF000000000102030401020304060708ED -:04AB40000900000008 -:04000005080001B43A -:00000001FF diff --git a/bin/boot/nowatchdog_boot_v1_5@1447134832.bin b/bin/boot/nowatchdog_boot_v1_5@1447134832.bin deleted file mode 100755 index 6f804d8339..0000000000 Binary files a/bin/boot/nowatchdog_boot_v1_5@1447134832.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_v1_5@1447134832.hex b/bin/boot/nowatchdog_boot_v1_5@1447134832.hex deleted file mode 100644 index 08b654a8b0..0000000000 --- a/bin/boot/nowatchdog_boot_v1_5@1447134832.hex +++ /dev/null @@ -1,1061 +0,0 @@ -:020000040800F2 -:1000000048200120A52C0008053000080530000814 -:100010000530000805300008053000080000000029 -:100020000000000000000000000000000530000893 -:100030000530000800000000053000080530000809 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0CEFD2E -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:08016000BBFD05B05DF804FBD6 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:08018800A7FD05B05DF804FBC2 -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F078FCDDF820C01A -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F019FC089B9CE0BA425C -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C00BC3F00084B45F7DBEDE70D9907910798C8 -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01C33F0008DE -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C0010B5044608484021FFF794FC0028F9D094 -:10191C0005482146FFF78AFC03484021FFF78AFC63 -:10192C000028F9D010BD00BF00480040014608B5A2 -:10193C000448FFF767FC03480221FFF765FC002809 -:10194C00F9D008BD0038004000B9074803680133DE -:10195C0008D04368013305D0C069044BC31A584200 -:10196C005841704700207047004000086F57654E83 -:10197C00074B1A69C2F306427F2A07D11869C0F3D4 -:10198C000C00B0F1FF0358425841704700207047DB -:10199C000028004000F13F4000F57E00800A082836 -:1019AC000FD8084A52F820305BB1013B42F8203086 -:1019BC003BB9012101FA00F0034B1A6B22EA00003B -:1019CC0018637047200000200038024000F13F40AF -:1019DC0000F57E00800A08280DD8074A52F82030FE -:1019EC00591C42F8201033B9012101FA00F0034BC5 -:1019FC001A6B104318637047200000200038024017 -:101A0C0010B50C4C2046FFF7E1FF072001F036FB28 -:101A1C004FF4805100222046FFF786FB042001F092 -:101A2C002DFB2046FFF7B6FF4FF480400021FFF757 -:101A3C0077F810BD0004024010B50B4C01214FF497 -:101A4C008040FFF76DF82046FFF7C0FF20464FF4AB -:101A5C0080510122FFF768FB072001F00FFB2046A5 -:101A6C00FFF798FF10BD00BF0004024008B5FFF758 -:101A7C00E3FF0020FFF75AFF0020FFF757FF00207D -:101A8C00FFF754FFFFF7BCFFFEF772FEBFF34F8F5B -:101A9C00044A054BD16801F4E0610B43D360BFF3FA -:101AAC004F8FFEE700ED00E00400FA05F0B55E256F -:101ABC006843204BB0FBF1F51A68ADF6DC3D95425E -:101ACC0034D002AC00214FF43D6220461D60FEF77D -:101ADC00BEFA1B238DE818001748102140226923F9 -:101AEC0001F0AFFA08238DE8180014486023182180 -:101AFC006A2201F0A6FA0DF2FA760DF28E7300222C -:101B0C00AA420DD002F11900C0B2C108012700F0A1 -:101B1C00070007FA00F05F5C013238435854EFE7D6 -:101B2C001233B342EBD1204601F0E3FA0DF6DC3D63 -:101B3C00F0BD00BF08000020B03500084439000893 -:101B4C002DE9F043ADF6DC3D02AC00214FF43D62D3 -:101B5C0080462046FEF77BFA2C238DE818002022C5 -:101B6C00284818215C2301F06CFA1C2215460F271B -:101B7C00974007EA0807D740234B242101FB07377E -:101B8C000DF50366002117F801C00023082B07D0C0 -:101B9C000120984010EA0C0F0CBF0020012003E03C -:101BAC007818407800F0010078B15819C0B24FEAAB -:101BBC00D00E4FF0010900F0070009FA00F016F8FA -:101BCC000E9040EA090006F80E000133092BDDD116 -:101BDC000231242906F11206D5D10B35742DA2F150 -:101BEC000402C4D10E238DE81800102190226D231D -:101BFC00064801F026FA204601F07BFA0DF6DC3D92 -:101C0C00BDE8F083A0330008A4390008DC320008DA -:101C1C0010B5ADF6D83D02AC204600214FF43D6224 -:101C2C00FEF715FA1B238DE81800102140226923BA -:101C3C00044801F006FA204601F05BFA0DF6D83D97 -:101C4C0010BD00BFB03500082DE9F74F434E444F8F -:101C5C0001214FF48040FEF763FF3046FFF7B6FEDC -:101C6C003846404CFFF7B2FE30460D210522FFF7F7 -:101C7C0060FA052230460F21FFF75BFA3046214609 -:101C8C000125FFF7FEF920233846214623602571F4 -:101C9C00A5714FF0000A01934FF4805BFFF7F1F947 -:101CAC00304621464FF002084FF48049257184F8E4 -:101CBC0006A0C4F800B0FFF7E4F93046214684F8DA -:101CCC000480C4F80080FFF7DCF921462571A5716A -:101CDC00C4F80090244C3046FFF7D3F92046FFF7A8 -:101CEC0039F920462149FFF75FFA20462946FFF7CC -:101CFC007AFA019B384619465246FFF715FA2A46DE -:101D0C0030464946FFF710FA40462946FEF708FFD1 -:101D1C00A4F5505480232380F92323854FF4F042FB -:101D2C004FF6FF13E3623046A283294623644246F2 -:101D3C00A4F820B0FFF7FDF929462046A582FEF74E -:101D4C001FFD3846FFF726FE3046FFF723FE4846B8 -:101D5C005146FEF7E5FE03B0BDE8F08F00040240EB -:101D6C000008024000000020003800400C00002059 -:101D7C0010B5074C2046FFF729FE20461021FFF72F -:101D8C00CFF92046FFF706FE012001F077F910BDD0 -:101D9C000000024038B50A4B0C2404FB0034606888 -:101DAC00FFF714FE21896068FFF7B4F905466068F7 -:101DBC00FFF7F0FDD5F1010038BF002038BD00BFA2 -:101DCC009432000810B5064C2046FFF7FFFD204664 -:101DDC001021FFF7A7F92046FFF7DCFD10BD00BF6F -:101DEC0000000240F0B587B0FFF7C2FD002841D1DA -:101DFC000320FFF759F807460420FFF755F8474B27 -:101E0C000A241A68C2F30545C2F30626C2F302136C -:101E1C0002F00F0204FB0322330906F00F0604FB49 -:101E2C0003662B0905F00F0504FB03533C2505FB4A -:101E3C00036305FB0323191A44BF01F5A83101F50F -:101E4C00C07107EB91210320FFF71EF803A8FFF7E1 -:101E5C0098F903A8FEF7F4FE01A8FFF798F901A979 -:101E6C000020FEF731FF02A8FFF797F9002002A926 -:101E7C00FEF794FF0A2442F2107501F05CF910B9D8 -:101E8C00013DFAD107E0264D01F055F908B1013DAD -:101E9C00FAD1013CEFD10020FEF792FD00214FF268 -:101EAC000400FFF71DF84FF43C50FFF745F81D4BAD -:101EBC0001201A6842F004021A60FEF781FD1A4BE9 -:101ECC001B781BB1194801F055F908E01848FEF7CA -:101EDC0001F9174902461748FEF7DDF970B11648AB -:101EEC0001F048F90020FEF7EFFF08200121FEF772 -:101EFC00B7FF4FF400400121FEF7F2FF0F4801F04D -:101F0C0039F9084B1A6842F002021A600C4B1A6934 -:101F1C0042F004021A6130BF07B0F0BD0028004047 -:101F2C00A086010000700040017AFF1F2A370008CC -:101F3C003F3700082078FF1F443700085937000846 -:101F4C0000ED00E0B0F1006F10B504460BD20D4867 -:101F5C0001F007F9204601F013F90B4801F00AF9DA -:101F6C004FF0FF3010BD094A002352F8041FA14264 -:101F7C0002D851688C4202D30133072BF5D1184695 -:101F8C0010BD00BF6D3800086A37000870320008B9 -:101F9C0008B5024801F0EEF800BEFEE783370008F2 -:101FAC0010B504460A480221FFF72EF90028F9D093 -:101FBC0007482146FFF726F905480121FFF724F9C8 -:101FCC000028F9D00248FFF71AF9C0B210BD00BFC3 -:101FDC00003001402DE9F04F87B00121814600AF60 -:101FEC000020FFF763FD042001F063F807F10C00FB -:101FFC0049460C2201F0E9F8FD683C690C2DD7F834 -:10200C0014B013D0994801F0ACF8284601F0B8F898 -:10201C00974801F0A6F8204601F0B2F8954801F077 -:10202C00A0F8584601F0ACF8934833E0934801F01F -:10203C00A1F84FF480500121FEF766FD09F10C095F -:10204C00EA46FEF7DFFBA0B06E462546C846802D5B -:10205C000CD941463046802201F0B7F83046202199 -:10206C00FEF7DCFB803D08F18008F0E725B1304637 -:10207C0041462A4601F0A9F82946304600F0C6FF31 -:10208C00002180464FF48050FEF73EFDD845D546E2 -:10209C0004D07B4801F06EF80120E3E088B068467C -:1020AC004946202201F091F86846FFF74DFC786014 -:1020BC007448D54601F05EF87B682BB9724801F084 -:1020CC0059F804F5404600E02646002E3BD06F48F8 -:1020DC00FFF738FF05466E483044FFF733FF002DFD -:1020EC00824630DB00282EDB431B01334FEA430BC7 -:1020FC0000205946FFF7DAFCFEF7B0FB654B0026D3 -:10210C0003EB45033B6055451BDCF320FEF7BCFBA2 -:10211C003B68002133F81600FEF7D6FB082806F1C1 -:10212C00010608D05C4801F01CF8284601F028F89C -:10213C0001F00FF805E030465946FFF7B7FC0135C2 -:10214C00E1E7FEF799FB554801F014F87B680BB1F9 -:10215C004E4B00E0524B7B6063083B600025A54270 -:10216C003CD2661BB6F5803F28BF4FF480362EB1AB -:10217C004C4805EB0901324601F027F8D7F804A0CA -:10218C00FEF76CFBF320FEF77FFBAA444FF0000B2D -:10219C00B34519D043490BEB0A0011F80B10FEF7AD -:1021AC00CBFB08280DD0404800F0DBFF504600F078 -:1021BC00E7FF00F0CEFFFEF75FFB3C4800F0DAFFD4 -:1021CC000CE00BF1010BE3E7FEF756FB3B682146F5 -:1021DC0003EB5500FFF76AFC5D44C0E7344800F0A0 -:1021EC00C0FF204600F0CCFF324800F0C3FF0020B7 -:1021FC00FFF7AAFB20B1304800F0BCFF234D03E0F1 -:10220C002E4800F0B7FF264D4FF480500121A60850 -:10221C00FEF77AFCFEF7F6FA31462846FEF7FEFA90 -:10222C0004F0030105EB860000F0F0FE00210446EB -:10223C004FF48050FEF768FC214800F092FF4046B6 -:10224C0000F09EFF1F4800F08CFF204600F098FF26 -:10225C0000F07FFF444504D01B4800F08BFF0220A8 -:10226C0000E000201C37BD46BDE8F08F8E3700081B -:10227C0097370008A1370008A8370008C7370008AF -:10228C00E4370008073800081A3800080040000836 -:10229C00FF3F0008CC320008323800084A380008EA -:1022AC0000000108440000205D3800087638000862 -:1022BC00813800088F38000896380008BB380008B1 -:1022CC00E0380008F3380008F938000870B54FF40E -:1022DC0080500121FEF730FCFFF774FD0520164CF1 -:1022EC00FFF75EFE002C04F1FF341FDCFFF740FD0E -:1022FC00FFF768FD9F20FFF753FEA920FFF750FE64 -:10230C000546A920FFF74CFE0646A920FFF748FE1C -:10231C003602044646EA0545FFF72AFD4FF4805085 -:10232C000021FEF709FC45EA040070BDA920FFF767 -:10233C0037FEC307D6D4D9E70090D00308B50446BE -:10234C00114800F00EFF204600F01AFF00F001FFCC -:10235C002046FFF7F5FB00F0EEFE0C4B20F00400DE -:10236C0018701D4600F0E7FE2B7800F0FB0083424E -:10237C0001D10B2401E0FFF779FB013CF2D04FF4C3 -:10238C007A7000F07BFEF8E7E43B00084400012083 -:10239C0080B5464B4FF4C06290B01A6001214FF0EB -:1023AC008050FEF7BDFB0120FEF704FB4048FFF711 -:1023BC000DFB4FF480200121FEF7B2FB3C480A21B3 -:1023CC000722FEF7B6FE072239480B21FEF7B1FEB5 -:1023DC0010AF4FF4806347F8383D00244FF00108EC -:1023EC000225334839468DF80E408DF80F808DF854 -:1023FC000C508DF80D50FEF744FE4FF400632C4842 -:10240C00394602938DF80C5004AEFEF73AFE4FF4A9 -:10241C006133049327480C233146ADF81A30ADF8DC -:10242C001440ADF81640ADF81840ADF81C40FEF75E -:10243C00BFFD41461F48FEF7EDFE1D48FFF7AAFA07 -:10244C001D4800F08EFE1D4B586F00F099FE00F0F9 -:10245C0080FE7920FEF7B8FB404502D1184800F009 -:10246C0089FE012000F010FE10B90121FEF70CFDD1 -:10247C004120FEF7A9FB58BB124800F07BFE01205F -:10248C00FEF7D0FA47F230544120FEF79DFBF8B925 -:10249C004FF47A7000F0F2FD013CF5D10A4800F0DF -:1024AC0069FEFFF7D1FB094837E100BF003C024051 -:1024BC000008024000480040F03B00080038024091 -:1024CC00003C0008103C0008283C0008114550FE58 -:1024DC004FF48070FEF7FAFA0120FEF70FFBFEF7BF -:1024EC00D1FB0020FEF76CFA01214FF48040FEF77F -:1024FC0023FBBB48FFF76AFA3046FEF702FE0024C6 -:10250C000125B74831468DF815408DF816408DF8E9 -:10251C00174004958DF81450FEF7B3FD2246B048D1 -:10252C002946FEF701FEAE48FFF734FAAD4C04F134 -:10253C00300954F8040CFFF749FA3046FEF7E1FD78 -:10254C00236854F8040C4FF000083146022504931C -:10255C008DF814808DF815508DF817800C34FEF71B -:10256C0090FD54F8100CFFF715FA4C45E1D14FF4DF -:10257C0080404146FEF7E0FA4FF4807000F084FD95 -:10258C00002800F0CC804FF4807000F087FDFFF73E -:10259C0029FC954800F0EEFD0220FEF785FC9349DE -:1025AC00884202D00220FEF76FFC914800F0D9FD62 -:1025BC000220FEF779FC00F0E3FD00F0CAFD4FF4B9 -:1025CC00804000F061FD90B18A4800F0D3FD4FF4DB -:1025DC00804000F063FD082000F060FD102000F04A -:1025EC005DFD202000F05AFD402000F057FDFFF764 -:1025FC002BFBFFF70DFB01214FF48050FEF79CFAEB -:10260C007748FFF7E3F901200146FEF77DFA052133 -:10261C000A467348FEF78DFD714806210522FEF728 -:10262C0088FD05226E480721FEF783FD0024022554 -:10263C00C0236B4839468DF80C508DF80D50029321 -:10264C008DF80E408DF80F40FEF71BFD20236448DB -:10265C0039468DF80F5002930125FEF712FD102319 -:10266C0039465F4802938DF80C508DF80F50FEF7E9 -:10267C0008FD6148FEF76EFC4FF48273ADF8123022 -:10268C004FF40073ADF81A305B4807233146ADF8B0 -:10269C002030ADF81040ADF81440ADF81640ADF850 -:1026AC001840ADF81C40ADF81E40FEF77DFD2946E4 -:1026BC005148FEF798FD4A48FFF76CF9FFF782FB8B -:1026CC00AB20FFF76DFCFFF753FB642000F0D6FC4A -:1026DC0021464FF48050FEF72FFAFFF7F7FD2846FE -:1026EC00FEF7E2FB25460490E8B2FFF753FB98BBDC -:1026FC0070550135042DF7D1049D3DB13F4800F0D4 -:10270C0030FD284600F03CFD00F023FD0120294659 -:10271C00FEF7BAFB002C3DD03948FFF70FFE2846D8 -:10272C00FEF754F9012814D12846FEF759F94FF455 -:10273C000060FEF7F3FB18B1324800F01BFDB9E066 -:10274C00314C00F0F8FC002800F00481013CF8D179 -:10275C00B0E000F0B9FC1CE7735D042B0ADD2B48DC -:10276C0000F008FD049800F00BFD01200021FEF79D -:10277C008BFB0FE00133DBB2042B7355B9DD24481E -:10278C0000F0EFFC284600F0FBFC224800F0F2FCC5 -:10279C000124AEE7FFF79AFD1F4B0344012B01D92F -:1027AC001E48BAE71E4800F0DCFC0020FEF77CFB5C -:1027BC0000F0E6FC00F0CDFC802000F065FC18B1C8 -:1027CC00802000F06BFC73E04FF4003000F05CFCF8 -:1027DC00044638B34FF4003000F060FC04E100BF55 -:1027EC00000002409C320008603C00087086415694 -:1027FC009C3C0008B13C0008003001401F3D000823 -:10280C00014550FE453C0008A0860100DE3C000856 -:10281C000A3D0008153D0008EA44DFFF024550FE62 -:10282C004B3D00080120FFF7B5FAD8B12046FFF761 -:10283C00B1FAB8B1B74800F09DFC41F288340120E0 -:10284C00FFF7A8FA002800F08B800020FFF7A2FA0F -:10285C00002800F085804FF47A7000F00FFC013CEA -:10286C00EDD1C1E00020FFF76FF808B1AA4B00E0F2 -:10287C00AA4B0CCB013301D0013271D1A84800F026 -:10288C0079FCB1E0A74800F075FC082000F006FCCC -:10289C00102000F003FC202000F000FC402000F091 -:1028AC00FDFB0024042000F0F9FB002C40F0088113 -:1028BC009D4832E7FFF75CF8002847D0002001A9BB -:1028CC009A4DFEF749FA344600203946FEF7A8FA2D -:1028DC000FCD0FC40FCD0FC495E80F0084E80F0087 -:1028EC009DF8092010A901EB82039DF80BE053F829 -:1028FC00343C40F26D1101FB0E3303EB9E030EF1E1 -:10290C00010E1EF0030F02D1022A88BF01339DF87D -:10291C000A409DF80410013C2344182404FB0314C2 -:10292C009DF805103C2202FB04149DF806100320B0 -:10293C0002FB0414FEF7B8FA01190320FEF7A4FAFF -:10294C0004200021FEF7A0FA00F0BEFB784800F04E -:10295C0011FC1EE6774800F00DFCFFF743FAF5E793 -:10296C007D20FEF731F970B94FF4007000F08CFB4C -:10297C0048B9714800F0FEFB082000F08FFB1020D6 -:10298C0000F08CFB85E07D20FEF71EF910B16B4842 -:10299C0000F0F0FB4FF4007000F076FB10B16848CB -:1029AC0000F0E8FB4FF4007000F078FB102000F012 -:1029BC006BFB48B1634800F0DDFB082000F06EFBB8 -:1029CC00102000F06BFB12E0082000F05DFB20B142 -:1029DC005D4800F0CFFB102003E05C4800F0CAFB20 -:1029EC00082000F066FB54E0594800F0C3FB59483E -:1029FC0000F0C0FB4FF40010FFF7ECFA10B1022806 -:102A0C0028D809E0202000F049FB402000F046FBCC -:102A1C00802000F04EFB1DE0082000F04AFB102047 -:102A2C0000F047FB202000F02FFB20B94A4800F0B3 -:102A3C00A1FB202009E0402000F026FB00287FF4B9 -:102A4C0021AF464800F096FB402000F032FB4FF4DB -:102A5C0000701AE0012425E7414800F08BFB0820A8 -:102A6C0000F01CFB102000F019FB202000F016FBDE -:102A7C00402000F013FB2046FFF7ACFA022840F090 -:102A8C00F080082000F015FB102000F012FBFEF780 -:102A9C00EDFF022000F0F8FA90B1042000F0F4FAF7 -:102AAC0004460028D8D02F4800F064FB042000F026 -:102ABC00F5FA022000F0F2FA4FF4004000F0EEFAC2 -:102ACC004FF4005000F0E0FA04464FF4805000F050 -:102ADC00DBFA400040EA84044FF4006000F0D4FAC2 -:102AEC00E4B22043431C03F0FF03013B4FF40060AE -:102AFC00062B58D8DFE803F03C383C043C383C004B -:102B0C0000F0CCFA4FF4805000F0C8FA4FF40050AB -:102B1C002EE000BF573D00080040000800000108EF -:102B2C00893D0008B33E0008034550FE44320008BE -:102B3C00503C0008523F00089C3D0008AD3D000889 -:102B4C00C53D0008E53D0008FB3D0008113E0008AE -:102B5C00673F0008273E0008413E00087A3E000807 -:102B6C00233F0008ED3E000800F098FA4FF4805027 -:102B7C0000F09FFA45F25550FDF7FAFE0420FDF7E0 -:102B8C00FDFE40F6FF70FDF7FFFE0020FDF7F0FEA6 -:102B9C004FF480500121FDF755FE0020FEF7D4FEC6 -:102BAC0068B9354B0CE000F079FA4FF4805000F026 -:102BBC0075FA4FF4005000F071FA3048ADE5304B27 -:102BCC005D6830481E6800F0CCFA284600F0D8FA50 -:102BDC0000F0BFFA00F0BDFA00239A0002F1604247 -:102BEC00013302F561424FF0FF31082BC2F880101F -:102BFC00C2F88011F1D1244B00241C635C639C63EC -:102C0C001C645C64FDF7F4FE20480121FDF7A0FF75 -:102C1C0021461E48FDF79CFFF1200121FDF7A4FF82 -:102C2C002146F120FDF7A0FF01200146FDF7A8FF8A -:102C3C0021460120FDF7A4FF15480121FDF7ACFF4B -:102C4C0021461348FDF7A8FF12480121FDF7B0FFFC -:102C5C0010482146FDF7ACFF63B64FF0FF3EB5467A -:102C6C002847042000F01AFA022000F017FA4FF45B -:102C7C00004000F01EFA23E700000108044550FE56 -:102C8C00004000083E3F000800380240001060225F -:102C9C00FFC9FE36335907000748084A08B50849EA -:102CAC00121AFDF7CBF90748074A0021121AFDF753 -:102CBC00CEF9FFF76DFB00BF0000002020000020C4 -:102CCC00D43F000820000020480001202DE9F04FDF -:102CDC00DDE909AB0E469C4603783B490746C95CC7 -:102CEC0001300D07F8D42B2B05D02D2B04D1074622 -:102CFC004FF0FF3301E007460123397830290ED11C -:102D0C007978782905D132F0100103D102371022DD -:102D1C0007E00AB1082A04D10137082201E002B900 -:102D2C000A2200200021B84617F8015BA5F13004F7 -:102D3C00E4B2092C0DD9A5F14104192C02D8A5F146 -:102D4C00370405E0A5F16104192C30D8A5F157041E -:102D5C00E4B294422BDA4FEAE27900FB09F502FB6C -:102D6C000155A0FB0201E4B2294400194FF0000503 -:102D7C0041EB0501BCF1000FD5D0012B06D18245EA -:102D8C007BEB0104CFDA50465946CCE75C1CCAD128 -:102D9C00DDE90B89444261EB4105444575EB0909BA -:102DAC00C1DADDE90B01404261EB4101BBE70EB139 -:102DBC00C6F80080DD1700FB05F203FB0122A0FB27 -:102DCC0003011144BDE8F08F4C1200082DE9F04FBF -:102DDC0091B01F9E0C46310691468046DDE91A23C0 -:102DEC001E9D04D5CDE902234FF0000C1BE0002AF8 -:102DFC0073F100071046194602DA504263EB4301A7 -:102E0C00002A73F1000ACDE9020107DBB70708D4E9 -:102E1C0016F0040C07D04FF0200C04E04FF02D0CF2 -:102E2C0001E04FF02B0C16F04007089714BF704FC1 -:102E3C00704FDDE902AB079700271C980137C117CB -:102E4C00CDE90401DDE904230AA93944099150466E -:102E5C005946CDF804C0FEF773FA07980999835CBC -:102E6C00504601F8013C5946DDE90423FEF768FAA7 -:102E7C0082468B465AEA0B02DDF804C001D0162FAD -:102E8C00DBD116F0080239461DD01C9B082B02D052 -:102E9C00102B0ED01CE0DDE902AB1DB95AEA0B0B6E -:102EAC0013D004E05AEA0B0B12D0BD4210DC7B1C91 -:102EBC000FE0DDE902AB5AEA0B0B09D008982B4660 -:102ECC0048B1582208E02B4606E02A46012303E0CD -:102EDC002B46002200E078221CF1000B1D98C7EB5A -:102EEC00030A18BF4FF0010BBB42A8BF1F46C01B03 -:102EFC002AEAEA7ACBEB00000AB1022700E0174677 -:102F0C00C71B16F0010027EAE77701D07F4205E0E6 -:102F1C00F60603D5013504BFBA4407463D46002DDD -:102F2C000BDD2068013D461C4E453CBF202608F8B1 -:102F3C000060206801302060F1E727EAE7703F1A53 -:102F4C00BBF1000F08D02068451C4D4538BF08F870 -:102F5C0000C02068013020608AB12068451C4D45B6 -:102F6C003CBF302508F800502568681C02354D45DB -:102F7C00206038BF08F80020226801322260BAF1C4 -:102F8C00000F0CD022680AF1FF3A501C48453CBF98 -:102F9C00302008F80200226801322260EFE7DDE9F8 -:102FAC0002AB5AEA0B0B10D17BB919460DE0236822 -:102FBC0001395A1C4A4505D201F1280568462A5C9C -:102FCC0008F803202368013323600029EFD15FB197 -:102FDC00236801375A1C4A453CBF202208F80320BD -:102FEC00236801332360F2E711B0BDE8F08F00BF16 -:102FFC009A3F0008AB3F00081EF0040F0CBFEFF324 -:10300C000880EFF30980FEF7C3BF7047032970B542 -:10301C0005460C4606D98E083146FDF7FFFBB60077 -:10302C003544A41B14B9FDF707FC70BD00231846EA -:10303C00EA5C0133A34242EA0020F9D1FDF7E8FB38 -:10304C0070BD2DE9F0410733DE08079B4FF01208E5 -:10305C0008FB02320731069F054602EBD10800241B -:10306C00BC4209D0122000FB048029463246FCF7F2 -:10307C00E5FF01343544F3E7BDE8F08106235843FE -:10308C0001387FF4FDAF704710B504460020FDF702 -:10309C000BFF20420CBF0020012010BD10B50446D0 -:1030AC000020FDF701FF20EA04010020FDF7ECFEF3 -:1030BC0010BD10B504460020FDF7F6FE40EA0401F1 -:1030CC000020FDF7E1FE10BD1FB501A8FEF759F871 -:1030DC000723029301A803230393FDF7B1FD684670 -:1030EC00FEF755F869460020FDF7EEFD05B05DF8DA -:1030FC0004FB70B50646FEF79FFC8020FEF716FC1D -:10310C00A82420BA90FAA0F0C0B2FEF70FFC11254B -:10311C00705DFEF70BFC15F1FF35F9D20020FEF7C0 -:10312C0005FC013C06F11206EBD12046FEF7FEFB36 -:10313C00FEF766FC70BD38B500242546E0B2FEF7FC -:10314C0029FEA04001340543042CEDB2F6D12846EB -:10315C0038BD08B50D20FEF7D3FB0A20FEF7D0FBD7 -:10316C0008BD10B5441E14F8010F10B1FEF7C8FBD2 -:10317C00F9E710BD08B5FFF7F4FFFFF7EAFF08BD4C -:10318C001FB530238DF8043078238DF805300723D4 -:10319C0099000F228A400240CA40092A01D83032D5 -:1031AC0002E00F2A02D85732D2B200E0202201A945 -:1031BC00CC1A13F1FF336272EAD2002308468DF861 -:1031CC000E30FFF7CEFF04B010BD70B504460D46AF -:1031DC004FF4805001211646FDF7AEFCFEF7F2FDD0 -:1031EC000520FEF7DDFEA920FEF7DAFEC307FAD4B0 -:1031FC00FEF7BEFDFEF7E6FD0320FEF7D1FEC5F39C -:10320C000740FEF7CDFEC5F30720FEF7C9FEE8B276 -:10321C00FEF7C6FE2644B44205D0A920FEF7C0FE38 -:10322C0004F8010BF7E7FEF7A3FD4FF480500021E3 -:10323C00FDF782FC70BD0000000000001F000000C4 -:10324C003B0000005A0000007800000097000000CE -:10325C00B5000000D4000000F300000011010000D4 -:10326C00300100004E010000000000080040000882 -:10327C000080000800C000080000010800000208DF -:10328C000000040800000608863F00080008024001 -:10329C00080000008B3F0008000002400400000002 -:1032AC008E3F00080008024040000000953F0008D7 -:1032BC00000002400200000000000240010000007B -:1032CC000000080010001800200028003000380012 -:1032DC000000068301000000000000000000000058 -:1032EC00068301000000000C0000000000000683B3 -:1032FC0001000000000C00000000D878369B79C05B -:10330C00E3D90C8C67DB3C1BF8FD7EBFFDE0F7FBC3 -:10331C001FC6EFFB7E1F98CD66B3CD60369B19C6DA -:10332C006C18660398FD66B3FD60309B19C66F1868 -:10333C00660398FD66B3FD60309B19C36F18660376 -:10334C00980D66B30D60369B19C360186603F8FDC3 -:10335C007EBFFDECF79B19C36F187E03D878369BA4 -:10336C0079CCE399998167183C03180000000000A0 -:10337C0000008001000000001800000000000000A8 -:10338C000000000000001800000000000000000019 -:10339C0000000000000C000600000000600030007F -:1033AC000018000300000000C0001800003080016D -:1033BC000000000080010C000060C0000000000054 -:1033CC000003060000C060000000000000060300BF -:1033DC000080310000000000008C010000001B0088 -:1033EC000000000000D8000000000E0000000000EB -:1033FC000070000000000E000000000000700000D3 -:10340C0000001B000000000000D80000008031000C -:10341C0000000000008C010000C0600000000000F3 -:10342C00000603000060C00000000000000306005E -:10343C00003080010000000080010C000018000327 -:10344C0000000000C0001800000C00060000000086 -:10345C0060003000000000000000000000000000D0 -:10346C000000000000000000000000000000000050 -:10347C000000000000000000000000000000000040 -:10348C000000000000000000000000000000000030 -:10349C000000000000000000000000000000000020 -:1034AC000000000000000000000000000000000010 -:1034BC00000000000000000080FFFF010000000081 -:1034CC0000000000FEFFFF7F00000000000000E095 -:1034DC007F0000FE07000000000000FE01000080DD -:1034EC007F0000000000801F00000000F8010000B9 -:1034FC000000F00100000000800F000000007C00C4 -:10350C0000000000003E000000000F000000000062 -:10351C0000F00000008003000000000000C001006B -:10352C0000E0000000000000000007000070000038 -:10353C000000000000000E00001800000000000059 -:10354C0000001800000C000000000000000030001B -:10355C0000060000000000000000600000030000F6 -:10356C00000000000000C00080010000000000000E -:10357C0000008001C00000000000000000000003FB -:10358C006000000000000000000000063000000099 -:10359C00000000000000000C100000000000000003 -:1035AC00000000080000000000600000030018008C -:1035BC000000000000000060000003001800000084 -:1035CC000000000000600000030018000000000074 -:1035DC000000006000000300180000000000000064 -:1035EC000060000003001800000000E003001F60F2 -:1035FC00F800C30718C0070000F80FC07F60FE0377 -:10360C00F31F18F01F00000C1860C06003061B307D -:10361C00181830000006303080E1010C0F60180CD7 -:10362C0060000003601800E3001807C01806C00013 -:10363C008001400C006200100380180380008001A0 -:10364C00C00C006600300380190380018001C0FCAF -:10365C00FF670030038019FFFF018001C0FCFF638E -:10366C000030038019FFFF008001C00C00600030A7 -:10367C000380190300008001C00C0060003003803F -:10368C00190300008001400C006200100380180335 -:10369C0080008003601800C3001806C01806C00024 -:1036AC00800730308081010C0C60180C6000800D9C -:1036BC001860C000030618303818300080F90FC0AD -:1036CC007F00FE03F01F70F01F0080E103001F005D -:1036DC00F800C00760C00700800100000000000077 -:1036EC00000000000000800100000000000000004D -:1036FC00000000008001000000000000000000003D -:10370C00000080010000000000000000000000002C -:10371C0080010000000000000000000000004E6F5F -:10372C002048572056657273696F6E20696E204F62 -:10373C0054500056335231007573622077616B65BB -:10374C00757020737570706F7274656400456E745B -:10375C006572696E67207374616E646279002069AA -:10376C0073206F7574736964652073797374656DF8 -:10377C0020666C6173680048415244204641554CA8 -:10378C005400446573636C656E20000A4669726D63 -:10379C006C656E20000A5873756D20000A496E76B0 -:1037AC00616C6964206669726D776172652064650D -:1037BC00736372697074696F6E2100436865636B23 -:1037CC0073756D6D696E67206669726D7761726570 -:1037DC002075706461746500496E76616C69642053 -:1037EC006669726D776172652043524320696E2061 -:1037FC0053504920666C6173682100657261736572 -:10380C005F6F6C645F6669726D77617265004F6C97 -:10381C006420576F726C64206669726D7761726593 -:10382C002062617365006661696C656420746F2049 -:10383C00657261736520736563746F7220007772B3 -:10384C006974655F6E65775F6669726D77617265C5 -:10385C00006661696C656420746F207772697465A9 -:10386C00206164647265737320005765277265204C -:10387C006465616400436865636B73756D6D696E37 -:10388C00672000206279746573004E657720576F4E -:10389C00726C64206669726D7761726520737973DE -:1038AC0074656D5F666C6173685F62617365004F10 -:1038BC006C6420576F726C64206669726D776172EC -:1038CC00652073797374656D5F666C6173685F6294 -:1038DC0061736500436865636B73756D202D20778C -:1038EC00616E746564200020676F7420004F7572E0 -:1038FC0020696E7465726E616C20666C61736820F1 -:10390C00636F6E74656E747320617265206261649E -:10391C002028636865636B73756D206661696C65DF -:10392C006429212054686973206973207265616C65 -:10393C006C79206261642100FFFFFFFFFFFFFFFF36 -:10394C00FFFFFFFF010000000000000000000080EE -:10395C0001000000000000000000008001000000D9 -:10396C0000000000000000800100000000000000CA -:10397C00000000800100000000000000000000803A -:10398C00010000000000000000000080FFFFFFFFAE -:10399C00FFFFFFFFFFFFFFFF7C00FE00FF01C701E1 -:1039AC00C701C701C701C701C701C701C701C701CB -:1039BC00C701C701C701FF01FE007C0038003C00B5 -:1039CC003E003E003800380038003800380038001F -:1039DC0038003800380038003800FE00FE00FE00C9 -:1039EC007C00FE00FF01C701C701C001C001E0005F -:1039FC00F00078003C001E000E000F000700FF01D5 -:103A0C00FF01FF017C00FE00FF01C701C701C001DF -:103A1C00C001F8007800F800C001C001C001C70166 -:103A2C00C701FF01FE007C00E000E000F000F000A8 -:103A3C00F800F800F800FC00EC00EE00E600FF01D6 -:103A4C00FF01FF01E000E000E000E000FF00FF00EC -:103A5C00FF000700070007007F00FF00FF01C70100 -:103A6C00C001C001C701C701C701FF01FE007C00F6 -:103A7C007C00FE00FF01C701C701070007007700AB -:103A8C00FF00FF01C701C701C701C701C701FF0143 -:103A9C00FE007C00FF01FF01FF01E000E000700070 -:103AAC007000700038003800380038001C001C0012 -:103ABC001C001C001C001C007C00FE00FF01C70148 -:103ACC00C701C701C701FE007C00FE00C701C7018A -:103ADC00C701C701C701FF01FE007C007C00FE008E -:103AEC00FF01C701C701C701C701C701FF01FE01E3 -:103AFC00DC01C001C001C701C701FF01FE007C0051 -:103B0C0000000000000000007C00FE00FF01C70167 -:103B1C00C701F001FC01CE01C701C701E701FF019C -:103B2C00DF01CE010700070007000700E700F701DF -:103B3C00FF01CF01C701C701C701C701C701C701F9 -:103B4C00CF01FF01F701E7000000000000000000BA -:103B5C007C00FE00FF01C701C7010700070007003A -:103B6C000700C701C701FF01FE007C00C001C001B6 -:103B7C00C001C001CE01DF01FF01E701C701C70190 -:103B8C00C701C701C701C701E701FF01DF01CE0172 -:103B9C0000000000000000007C00FE00FF01C701D7 -:103BAC00C701C701FF01FF010700C701C701FF01E2 -:103BBC00FE007C00E000F000F8003800FE00FE0083 -:103BCC00FE00380038003800380038003800380063 -:103BDC0038003800380038005341442057415443D2 -:103BEC00483A200052657365742052656769737496 -:103BFC006572200042726F776E206F75742072654B -:103C0C00736574005374617274696E67204C53450C -:103C1C00206F7363696C6C61746F72004C53452038 -:103C2C006F7363696C6C61746F7220646964206E6D -:103C3C006F74207374617274005553422077616BFA -:103C4C00657570006C656176696E67207374616E62 -:103C5C0064627900205F5F5F5F5F5F202020205FE0 -:103C6C005F0D0A2F5F20205F5F2F205F5F2F202FBB -:103C7C000D0A202F202F2020202F5F20205F5F2F68 -:103C8C000D0A2F5F2F20202020202F5F2F0D0A00E0 -:103C9C00426F6F746C6F61646572207665727369C4 -:103CAC006F6E3A20004C617374206669726D776197 -:103CBC00726520626F6F7420776173207374616218 -:103CCC006C653B20636C65617220737472696B6503 -:103CDC007300537475636B20627574746F6E20720D -:103CEC006567697374657220697320696E76616C9F -:103CFC0069642C20636C656172696E672E00427575 -:103D0C0074746F6E2069642000697320737475631A -:103D1C006B2100427574746F6E207761732070751F -:103D2C0073686564206F6E20626F6F742E2042750D -:103D3C0074746F6E20636F756E7465723A200042F6 -:103D4C006F6F7420626974733A2000486F6C642042 -:103D5C00646F776E205550202B204241434B2066D8 -:103D6C006F72203520736563732E20746F20666F1D -:103D7C007263652D626F6F742050524600466972F3 -:103D8C006D7761726520697320657261736564007B -:103D9C00426F6F74696E67206E6F726D616C6C79B7 -:103DAC00005761746368646F672063617573656441 -:103DBC00206120726573657400536F66747761724D -:103DCC0065206661696C75726520636175736564E5 -:103DDC00206120726573657400426F6F7420666198 -:103DEC00696C65642C20737472696B6520330042B6 -:103DFC006F6F74206661696C65642C2073747269D2 -:103E0C006B65203200426F6F74206661696C65646B -:103E1C002C20737472696B652031004C6F6164697E -:103E2C006E67207265636F76657279206669726D54 -:103E3C0077617265004661696C656420746F206CF3 -:103E4C006F6164207265636F766572792066697242 -:103E5C006D776172652C20737472696B65206F6E5F -:103E6C00652E2054727920616761696E2E0046615F -:103E7C00696C656420746F206C6F6164207265637B -:103E8C006F76657279206669726D776172652C2028 -:103E9C00737472696B652074776F2E20547279205D -:103EAC00616761696E2E004661696C656420746F90 -:103EBC00206C6F6164207265636F76657279206621 -:103ECC0069726D776172652C20737472696B6520F1 -:103EDC0074687265652E2053414420574154434801 -:103EEC00004F75722070726576696F757320666904 -:103EFC00726D77617265207570646174652066619E -:103F0C00696C65642C2061626F7274696E672075D0 -:103F1C0070646174652E004E6577206669726D77EA -:103F2C0061726520697320617661696C61626C6590 -:103F3C002100426F6F74696E67206669726D7761DC -:103F4C0072652040200072657475726E696E672010 -:103F5C00746F207374616E64627900466F7263656E -:103F6C002D626F6F74696E67207265636F76657210 -:103F7C0079206D6F64652E2E2E004261636B0055A7 -:103F8C00700053656C65637400446F776E0030315C -:103F9C0032333435363738394142434445460030A4 -:103FAC0031323334353637383961626364656600D3 -:103FBC00286E756C6C2900000000000102030401DE -:083FCC000203040607080900C6 -:103FD40000A0000002020000FFFFFFFF00C0040178 -:103FE40000000000000000021000000007000000B4 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/nowatchdog_boot_v2_0@1447134832.bin b/bin/boot/nowatchdog_boot_v2_0@1447134832.bin deleted file mode 100755 index 60af30713f..0000000000 Binary files a/bin/boot/nowatchdog_boot_v2_0@1447134832.bin and /dev/null differ diff --git a/bin/boot/nowatchdog_boot_v2_0@1447134832.hex b/bin/boot/nowatchdog_boot_v2_0@1447134832.hex deleted file mode 100644 index b42ce400ea..0000000000 --- a/bin/boot/nowatchdog_boot_v2_0@1447134832.hex +++ /dev/null @@ -1,1059 +0,0 @@ -:020000040800F2 -:1000000048200120892C0008E92F0008E92F00086A -:10001000E92F0008E92F0008E92F00080000000080 -:10002000000000000000000000000000E92F0008B0 -:10003000E92F000800000000E92F0008E92F000860 -:080040009C1300000100000008 -:1000480010B50023934203D0CC5CC4540133F9E7C4 -:0200580010BDD9 -:10005A0002440346934202D003F8011BFAE77047B1 -:10006A0010B5C9B20244904206D003461C7801304A -:0C007A008C42F8D1184610BD002010BDCB -:1000860038B504460D4600F028F82946204400F00D -:0600960018F8204638BDF9 -:10009C0038B504460D4600F01DF82946421C204692 -:0600AC00FFF7DDFF38BD87 -:1000B20038B504460D4600F012F82946421C204687 -:0600C20000F0EEF838BD6D -:1000C80038B5054608460C4600F006F82146421C9D -:0800D8002846FFF7B5FF38BD13 -:0E00E0000023C25C0AB10133FBE718467047EB -:1000EE000023C25C1AB18B4201D00133F9E71846E6 -:0200FE00704749 -:1001000030B50023C45C3CB14A1E12F8015F1DB13A -:0C011000AC42FAD10133F5E7184630BDCF -:10011C0030B56FF0004485B00025CDE900454FF0B7 -:10012C0000444FF0FF35CDE90245012302F0C0FD3C -:04013C0005B030BD1D -:100140001FB56FF000420023CDE9002300214FF0DE -:1001500000424FF0FF33CDE902230A220B4602F0A2 -:08016000ADFD05B05DF804FBE4 -:100168001FB56FF000420023CDE9002300214FF0B6 -:1001780000424FF0FF33CDE902230A220B4602F07A -:0801880099FD05B05DF804FBD0 -:040190000020704794 -:02019400FEE784 -:10019600F8B5054608460E46FFF79FFF07462C466C -:1001A6002378013543B1204631463A4600F02FF810 -:0C01B6000028F4D12046F8BD1846F8BD22 -:1001C20030B50023C45C44B14A1E12F8015F15B178 -:0E01D200AC42FAD101E00133F4E7184630BD2B -:1001E000F8B50546084616460F46FFF779FF013079 -:1001F000B04234BF04463446394628462246FFF70B -:1002000023FFA64204D928190021321BFFF725FF3E -:040210002846F8BDC7 -:1002140070B5154604460E46FFF760FF421C314692 -:0E0224002046AA4228BF2A4600F039F870BDD5 -:1002320038B50446CDB2FFF752FF231818460278AC -:10024200013BAA4203D0C4F10100C018F6D138BD67 -:10025200F8B50E4604461746FFF741FF251830460B -:10026200FFF73DFFB84238BF0746314628463A46B7 -:0C027200FFF7E9FE0023EB552046F8BD25 -:10027E00884210B507D80023934203D0CC5CC454F7 -:10028E000133F9E710BD12F1FF3202D38B5C8354B8 -:04029E00F9E710BDAF -:1002A20030B50023934206D0C55CCC5C01332C1BD5 -:0A02B200F8D0204630BD002030BD1A -:1002BC002DE9F04F0F468FB00021541C0D91127890 -:1002CC00804610B117B1017000E0074600214FF0D5 -:1002DC00FF390E460D468A468B46002A00F00882EE -:1002EC000629FDD8DFE801F0041834596090CA00E3 -:1002FC00252A09D0DDF834C00CF10100B84238BF12 -:10030C0008F80C200D9037E0002601214FF0FF3942 -:10031C003546B246B3462FE02B2A13D007D8202AF5 -:10032C0013D0232A40F0DC8145F0080524E02D2A67 -:10033C0005D0302A40F0D48145F010051CE045F082 -:10034C00010519E045F0020516E045F0040513E03F -:10035C00AA48805C400712D5303AD2B26FF0004008 -:10036C00801A4FF00A0C90FBFCF0864202DD4FF035 -:10037C00FF30D1E10CFB062622780134ADE72A2AA6 -:10038C0040F0B0811E68191D002E2278BCBF45F0CC -:10039C00010576420B460134A4E12E2A40F0A481DB -:1003AC0022780421013498E79448B9F1FF3F10F802 -:1003BC0002C008BF4FF000091CF0040F0DD0303AFA -:1003CC00D2B26FF00040801A4FF00A0C90FBFCF098 -:1003DC008145CCDC0CFB0929CEE72A2A07D11A6807 -:1003EC00013422EAE279043314F8012C7CE12D2A41 -:1003FC0040F07A8114F8012B815C01F0040101F0CA -:10040C00FF090029F6D16FE16C2A23D00AD8682A9B -:10041C0012D06A2A40F06A812278062101344FF00A -:10042C00080A5AE7742A23D07A2A40F05F8122788E -:10043C004FF0060A013459E12278682A04D00134BD -:10044C0006214FF0020A48E76278062102344FF089 -:10045C00010A42E722786C2A04D0013406214FF0BD -:10046C00030A3AE76278062102344FF0040A34E7B3 -:10047C002278062101344FF0070A2EE76E2A00F08D -:10048C0009810DD8632A33D005D8582A40F01D8134 -:10049C0045F040053AE1642A2CD0692A2AD014E1AF -:1004AC00732A00F093801DD86F2A43D0702A40F035 -:1004BC000C811A6803F1040C0023CDE9002345F0EC -:1004CC008805102302930396CDF8109005954046AD -:1004DC000DA93A46CDF820C002F06AFCDDF820C028 -:1004EC006346F9E0752A2AD0782A00F00F81ECE0F7 -:1004FC000D995DE00AF1FF32072A13D8DFE802F00C -:10050C000407120A1212120A93F900000BE0B3F955 -:10051C00000008E0073323F0070202F10803D2E9D8 -:10052C00000102E01868C11704330A22CDE900016A -:10053C00029228E0BBF1000F00F0EB8004E0BBF16D -:10054C00000F01D14FF00A0B0AF1FF3245F0800584 -:10055C00072A11D8DFE802F0040610081010100862 -:10056C0018780AE0188808E0073323F0070202F134 -:10057C000803D2E9000102E0186800210433CDE938 -:10058C000001CDF808B00396CDF8109005954046C3 -:10059C000DA93A46089302F00BFC089B9CE0BA426A -:1005AC0004D208EB0201202001F8010C013E114697 -:1005BC00012E01F10102F2DC181D1B68BA420D91EB -:1005CC0038BF08F801300D92034685E0181D0B90DA -:1005DC00B9F1FF3041421A684141099102B9084A08 -:1005EC000023D15C31B9F31AE90723EAE3730CD584 -:1005FC005B420AE0099828B10133F2E74C1200087B -:10060C009B3F00084B45F7DBEDE70D9907910798E9 -:10061C001844401A002809DD0131B942F7D208EB21 -:10062C0001004FF0200C00F801CCF0E7079823EA0A -:10063C00E371013A5B1A01440A920D914A460A98F9 -:10064C0010F8011F0A9089B1099848B1DDF834C03F -:10065C000CF10100B84238BF08F80C100D90EEE711 -:10066C00002A02F1FF3001DD0246EFE70D991A4630 -:10067C0062B1481CC01A1044B84205D2C81A404492 -:10068C004FF0200C00F802C00132F1E7CB1A0D93A9 -:10069C000B9B21E00AF1FF300D9A191D072810D889 -:1006AC00DFE800F004070F0A0F0F0F0A1B681A701F -:1006BC0009E01B681A8006E018680023C0E90023D3 -:1006CC0001E01B681A600B4606E00D98411CB9420C -:1006DC0038BF08F800200D91227800210134FCE588 -:1006EC000221FAE50321F8E50521F6E50621F4E5FA -:1006FC000D98B84202D208F8002003E017B1474425 -:10070C0007F8012C064B984206D930E64FF0100B37 -:10071C001AE74FF0080B17E70FB0BDE8F08F00BFDA -:04072C00FEFFFF7F4E -:100730000CB407B504AB53F8042B0193FFF7BEFDCF -:0A07400003B05DF804EB02B070474F -:10074A0010B50C4613466FF000412246FFF7B1FD83 -:02075A0010BDD0 -:10075C000EB403B503AA52F8041B0192FFF7EFFF86 -:0A076C0002B05DF804EB03B0704723 -:0407760000207047A8 -:10077C00024B5A6F42F080725A6770470038024041 -:10078C00038819B19BB243F0010303E023F001038A -:08079C001B041B0C03807047D5 -:0C081000014B01229A607047003002404A -:0C081C00014B1860186870470030024063 -:1008280000EB81018842044B03D050F8042B1A6076 -:0C083800F8E71868704700BF003002406D -:0C084400014B1868704700BF00300240F4 -:10085000044B9A6809B1104301E022EA0000986055 -:08086000704700BF002004E016 -:10086800044B1A69002A04DA034A5A6002F18832F2 -:0C0878005A607047003C024023016745B5 -:10088400024B1A6942F000421A617047003C024070 -:0C089400014BD860704700BF003C0240E0 -:1008A0000B4BDA68D1030CD4DA68D2060BD4DA68C1 -:1008B00012F0EF0F09D1DB689B074CBF072008201F -:1008C000704701207047052070470620704700BF21 -:0408D000003C0240A6 -:1008D40070B5064631B1012906D0022907D14FF47B -:1008E400007506E00D4604E04FF4807501E04FF416 -:1008F400407500F0B9FB08281ED1104C236923F47D -:100904004073236123691D432561236923F0F803A0 -:100914002361236943F002031E432661236943F4E0 -:100924008033236100F0A0FB236923F002032361D9 -:10093400236923F0F803236170BD00BF003C02402B -:1009440070B505460E4600F08FFB082811D1094CFE -:10095400236923F44073236123692361236943F0EA -:10096400010323612E7000F07FFB236923F0010350 -:08097400236170BD003C02404C -:0C097C00014B1860704700BF00300040C5 -:0C098800014B5860704700BF0030004079 -:0C099400014B9860704700BF003000402D -:1009A000024B4AF6AA221A60704700BF003000408E -:1009B000024B4CF6CC421A60704700BF003000403A -:0C09C000014B1860704700BF20000E4281 -:0C09CC00014B1860704700BFA0000E42F5 -:1009D800034B5B6818420CBF00200120704700BF22 -:0409E800007000405B -:1009EC00024B1A6842EA80001860704700700040A1 -:1009FC000A4B00211A6842F001021A6099601A68C9 -:100A0C0022F0847222F480321A60054A5A601A6805 -:100A1C0022F480221A60D960704700BF003802406F -:040A2C001030002462 -:100A3000064B002201281A701A7002D0042802D036 -:100A400070471870704705221A707047703802405E -:100A50001D4B10B59A6802F00C02042A03D0082A34 -:100A600003D01A4B18E01A4B16E059685A6811F473 -:100A7000800F596814BF164B144B02F03F02B3FBB2 -:100A8000F2F3114AC1F3881152684B43C2F3014299 -:100A900001325200B3FBF2F30B4903608B680D4A3D -:100AA000C3F30313D45C0368E34043608C68C4F36E -:100AB0008224145D23FA04F484608968C1F342310E -:100AC000525CD340C36010BD003802400024F400E3 -:080AD00040787D01A23F0008FF -:100AD80000F44073B3F5407F084B08D1996820F0C3 -:100AE800704221F4F81122F440720A439A601A6F96 -:100AF800C0F30B0010431867704700BF003802406E -:0C0B0800014B1860704700BF3C0E4742D4 -:100B1400044B1A6B09B1104301E022EA0000186388 -:080B2400704700BF00380240D9 -:100B2C00044B1A6C09B1104301E022EA000018646E -:080B3C00704700BF00380240C1 -:100B4400044B5A6C09B1104301E022EA00005864D6 -:080B5400704700BF00380240A9 -:100B5C00044B1A6909B1104301E022EA0000186144 -:080B6C00704700BF0038024091 -:100B7400044B5A6909B1104301E022EA00005861AC -:080B8400704700BF0038024079 -:100B8C00044B9A6909B1104301E022EA0000986114 -:080B9C00704700BF0038024061 -:100BA400044B1A6A09B1104301E022EA00001862FA -:080BB400704700BF0038024049 -:100BBC00044B5A6A09B1104301E022EA0000586262 -:080BCC00704700BF0038024031 -:100BD4004309012B074A01D1136803E0022B0CBF20 -:100BE400136F536F00F01F0023FA00F000F00100B0 -:080BF400704700BF0038024009 -:100BFC00002382B001930D4BDA68520613D44FF0E8 -:100C0C00FF32DA60094BDA68019902F040020131D7 -:100C1C0001910199B1F5803F01D0002AF2D0D8683A -:100C2C00C0F3801000E0012002B0704700280040A3 -:100C3C00024BDA6822F08002DA607047002800402C -:100C4C0038B50F4CCA236362532363620546FFF722 -:100C5C00CFFF88B1A36823F04003A360A2682B6880 -:100C6C001343A360AB68236123696A6843EA0243B8 -:100C7C002361FFF7DDFF0120014BFF225A6238BDD3 -:040C8C0000280040FC -:100C9000002382B001930E4BCA225A6253225A6239 -:100CA000DA6822F0A002DA60094BDA68019902F0F2 -:100CB0002002013101910199B1F5004F01D0002AC4 -:100CC000F2D0D868FF22C0F340105A6202B07047D9 -:040CD00000280040B8 -:100CD400224A2DE9F0410B780C464F788E789268C1 -:100CE40020B912F0400225D1CA7023E012F040026C -:100CF40000D1CA703F0247EA0343E5781E4346EA3F -:100D04000545164CCA23636253236362FFF774FFDD -:100D140058B105F07F3020F0FE402060FFF78CFFD3 -:100D2400FFF7B4FF003018BF01200C4BFF225A62BA -:100D3400BDE8F081184600F012FA8046384600F00B -:100D44000EFA0746304600F00AFAE57840EA054014 -:100D540040EA084545EA0725D3E700BF00280040DC -:100D64000F4B70B51B680C46C3F30542C3F306264C -:100D740003F07F0503F480031B0C0A704E708D7022 -:100D8400CB7058B9104600F0F7F92070304600F0E7 -:100D9400F3F96070284600F0EFF9A07070BD00BF51 -:040DA40000280040E3 -:100DA8002DE9F0410C464B78CA788F780E7878BBDD -:100DB800D90603D523F010030A336370104600F0F8 -:100DC800CCF98046607800F0C8F90446384600F04F -:100DD800C4F940EA463040EA084545EA0425104C83 -:100DE800CA23636253236362FFF704FF58B125F0F7 -:100DF8007F4323F0C0036360FFF71CFFFFF744FF46 -:100E0800003018BF0120064BFF225A62BDE8F0816E -:100E18001D0245EA02453D4345EA4635DFE700BF86 -:040E2800002800405E -:100E2C000E4B70B55B680C46C3F30742C3F3042644 -:100E3C0003F03F05C3F34233CA704E708D700B70D4 -:100E4C0058B9104600F094F9E070304600F090F973 -:100E5C006070284600F08CF9A07070BD002800402E -:100E6C00084B9A6822F4006222F0080209B142F49D -:100E7C0000621043CA225A6253225A62FF229860BF -:080E8C005A6270470028004083 -:100E940082B000230193054B0193019B03EB800077 -:100EA4000190019B196002B0704700BF50280040B8 -:100EB40082B000230193054B0193019B03EB800057 -:100EC4000190019B186802B0704700BF5028004091 -:100ED400044B1A6C22F400321A641A6C104318641E -:080EE400704700BF0028004028 -:100EEC000D4BCA225A6253225A6220F0040200F0BF -:100EFC00040029B199680A439A601A6C104306E001 -:100F0C00996821EA02029A601A6C22EA00001864BD -:0C0F1C00014BFF225A6270470028004081 -:100F2800054BDB6823F088039B049B0C03420CBF32 -:0C0F380000200120704700BF002800408E -:100F4400054B80B2DA6840F0800002F0800262EA69 -:0C0F54000002DA60704700BF0028004077 -:100F600008B5134B98420AD14FF480500121FFF786 -:100F700025FE4FF480500021FFF720FE08BD0D4BE9 -:100F8000984207D14FF480400121FFF70BFE4FF448 -:100F9000804009E0084B984209D14FF400400121FC -:100FA000FFF700FE4FF400400021FFF7FBFD08BDF6 -:0C0FB0000030014000380040003C0040D0 -:100FBC00038ACA889BB223F4405330B513430D46C1 -:100FCC0003820989AB8882890B43698992B20B43EE -:100FDC0022F4B05222F00C029BB213438381838A19 -:100FEC00AA899BB223F44073134385B083820446D1 -:100FFC006846FFF727FD194B9C4203D003F580632D -:10100C009C4201D1039B00E0029BA289192112B2E0 -:10101C005943002A2A684FF06403B4BF520092006F -:10102C00B1FBF2F1B1FBF3F21201100903FB101149 -:10103C00A08900B2002806DAC9003231B1FBF3F303 -:10104C0003F0070305E009013231B1FBF3F303F0C0 -:10105C000F031A4392B2228105B030BD001001403B -:10106C0007B508238DF80730FFF714FC8DF807003F -:10107C009DF80730012BF7D09DF8070003B05DF801 -:02108C0004FB63 -:10108E002DE9F0410F68002301229A4002EA07057C -:10109E00954230D15A00032494400668E44326401A -:1010AE0006600E79D0F800C006FA02F8013E48EA52 -:1010BE000C0C012EC0F800C014D891F805C086683B -:1010CE000CFA02FC2640866086684CEA06068660AC -:1010DE00466826EA050545608D7946689D40ADB2A5 -:1010EE0035434560C5682C40CD79C46005FA02F2DF -:1010FE00C4682243C2600133102BC5D1BDE8F08114 -:10110E004FF6FF7303600023037143718371C37144 -:02111E00704718 -:0C112000036919420CBF00200120704739 -:04112C000183704784 -:041130004183704740 -:0A1134000AB1018370474183704740 -:10113E0001F00703C90800EB81009B000F219940C5 -:10114E009A4010B5046A24EA01010162016A114352 -:04115E00016210BD5D -:1011620000230928DAB203F1010302D90A38C0B216 -:0A117200F7E740EA0210C0B2704730 -:10117C00030903EB830300F00F0000EB4300C0B244 -:02118C007047AA -:0C118E0000217F22FF2380E80E00704744 -:0C119A000023C370037043708370704723 -:0E11A60001230370837043700023C3707047F1 -:1011B4000B8810B54C88028823438C8802F4415272 -:1011C4002343CC8823430C8923434C8923438C89B0 -:1011D4002343CC89234313439BB20380838B23F49F -:0E11E40000631B041B0C83830B8A038210BD67 -:1011F200038819B19BB243F0400303E023F040039C -:081202001B041B0C0380704764 -:06120A00808980B27047EC -:041210008181704721 -:0C121400038919420CBF00200120704724 -:10122000838919B19BB243F4005303E023F40053C4 -:081230001B041B0C83817047B5 -:08123800C1F308018180704739 -:0C124000038819420CBF002001207047F9 -:10124C00202020202020202020282828282820206A -:10125C002020202020202020202020202020202082 -:10126C00481010101010101010101010101010103A -:10127C0004040404040404040404101010101010DA -:10128C001081818181818101010101010101010133 -:10129C0001010101010101010101011010101010E7 -:1012AC001082828282828202020202020202020204 -:1012BC0002020202020202020202021010101020AC -:1012CC000000000000000000000000000000000012 -:1012DC000000000000000000000000000000000002 -:1012EC0000000000000000000000000000000000F2 -:1012FC0000000000000000000000000000000000E2 -:10130C0000000000000000000000000000000000D1 -:10131C0000000000000000000000000000000000C1 -:10132C0000000000000000000000000000000000B1 -:10133C0000000000000000000000000000000000A1 -:10134C004BB942B9002908BF002802D04FF0FF3139 -:10135C00084600F03BB882B0EC462DE9005000F096 -:10136C001DF8DDF804E002B00CBC70472DE9704F9D -:10137C00089E14461D468046894600F029F804FB59 -:10138C0001F3A4FB00AB00FB05329344B8EB0A0855 -:10139C0069EB0B09C6E90089BDE8708F2DE9704F28 -:1013AC00089E14461D468046894600F061F900FBF4 -:1013BC0005F5A0FB04AB04FB0154A344B8EB0A08ED -:1013CC0069EB0B09C6E90089BDE8708F704700BF57 -:1013DC0000292DE9F00FC0F2A1800024002BC0F2EF -:1013EC009880154606460F46002B3FD18A4258D9A5 -:1013FC00B2FA82F34BB1C3F1200201FA03F720FADF -:10140C0002F29D4000FA03F61743290CB7FBF1F2E8 -:10141C0001FB1277A8B200FB02F34FEA164C4CEA20 -:10142C000747BB4209D97F1902F1FF3C80F00581C7 -:10143C00BB4240F20281023A2F44FF1AB7FBF1F390 -:10144C0001FB137100FB03F0B6B246EA014188427E -:10145C0008D9491903F1FF3780F0F180884240F236 -:10146C00EE80023B43EA0242002303E08B420AD99E -:10147C0000231A461046194614B1404261EB410153 -:10148C00BDE8F00F7047B3FA83F8B8F1000F40F0E5 -:10149C0088808B4202D3824200F2E2800023012238 -:1014AC00E8E712B90123B3FBF2F5B5FA85F2002A8D -:1014BC003AD17F1B280C1FFA85FC0123B7FBF0F1F6 -:1014CC0000FB11770CFB01F24FEA164848EA07477C -:1014DC00BA4207D97F1901F1FF3802D2BA4200F2A1 -:1014EC00C4804146BF1AB7FBF0F200FB12700CFB34 -:1014FC0002FCB6B246EA0040844507D9401902F115 -:10150C00FF3702D2844500F2AE803A4642EA0142ED -:10151C00B0E7E443524263EB430362E7404261EBC2 -:10152C0041014FF0FF3459E79540C2F1200927FAE9 -:10153C0009F126FA09F99740280CB1FBF0F800FBE9 -:10154C0018111FFA85FC0CFB08F349EA07094FEA4E -:10155C00194747EA01418B4206FA02F608D94919A4 -:10156C0008F1FF327AD28B4278D9A8F102082944CB -:10157C00C91AB1FBF0F300FB13170CFB03F21FFAB3 -:10158C0089F949EA0747BA4207D97F1903F1FF31B4 -:10159C0060D2BA425ED9023B2F44BF1A43EA0843D9 -:1015AC008CE7C8F1200225FA02F103FA08FC27FAAD -:1015BC0002F320FA02F207FA08F741EA0C0C4FEAA0 -:1015CC001C49B3FBF9F109FB11331FFA8CFA0AFB26 -:1015DC0001FB17433A0C42EA03439B4505FA08F01A -:1015EC0008D913EB0C0301F1FF3235D29B4533D9EB -:1015FC0002396344CBEB0303B3FBF9F209FB12335F -:10160C000AFB02FABFB247EA0347BA4508D917EBFF -:10161C000C0702F1FF331BD2BA4519D9023A6744C1 -:10162C0042EA0145A5FB0001CAEB07078F424FF0C8 -:10163C0000030AD305D02A461CE76246FDE63B466A -:10164C0010E706FA08F68642F5D26A1E002311E767 -:10165C001A46E5E70B46A0E71146CBE7904687E72D -:10166C004346424606E7023A50E702392F4439E72F -:10167C002DE9F00F144605460E46002B43D18A4245 -:10168C0053D9B2FA82F757B1C7F1200620FA06F601 -:10169C0001FA07F302FA07F400FA07F51E43210CCE -:1016AC00B6FBF1F201FB1266A0B200FB02F32F0CA9 -:1016BC0047EA0646B34209D9361902F1FF3780F0E2 -:1016CC00FD80B34240F2FA80023A2644F61AB6FB89 -:1016DC00F1F301FB136100FB03F0ADB245EA0141EC -:1016EC00884208D9091903F1FF3680F0E980884255 -:1016FC0040F2E680023B43EA0242002310461946C0 -:10170C00BDE8F00F70478B424CD8B3FA83F6002E2D -:10171C004FD18B4202D3824200F2DD80BDE8F00F44 -:10172C000023012210461946704712B90124B4FB5C -:10173C00F2F4B4FA84F2002A40F08280091B260CE1 -:10174C00A7B20123B1FBF6F006FB101107FB00F268 -:10175C004FEA154C4CEA01418A4207D9091900F1AC -:10176C00FF3C02D28A4200F2C8806046891AB1FB63 -:10177C00F6F206FB121107FB02F7ADB245EA014582 -:10178C00AF4208D92C1902F1FF3180F09B80A7429F -:10179C0040F29880023A42EA004210461946BDE8EF -:1017AC00F00F704700231A4610461946BDE8F00F9B -:1017BC007047C6F1200522FA05F703FA06F421FA60 -:1017CC0005F301FA06FB20FA05F53C434FEA1448F1 -:1017DC00B3FBF8FC08FB1C331FFA84F909FB0CFA69 -:1017EC0045EA0B0B4FEA1B4545EA03439A4502FABF -:1017FC0006F204D91B190CF1FF356FD3AC46CAEBBA -:10180C000303B3FBF8F508FB153309FB05F91FFAC5 -:10181C008BFB4BEA0347B94504D93F1905F1FF335C -:10182C0062D31D4645EA0C4CACFB0223C9EB0707FF -:10183C009F424FF000054AD346D062462B465DE7E7 -:10184C009440C2F1200921FA09FC914020FA09F9CF -:10185C00260CBCFBF6F806FB18CCA7B207FB08F36A -:10186C0049EA01094FEA194141EA0C4C634500FA77 -:10187C0002F509D91CEB040C08F1FF323BD263458D -:10188C0039D9A8F10208A444C3EB0C0CBCFBF6F349 -:10189C0006FB13C107FB03F21FFA89F949EA014160 -:1018AC008A4207D9091903F1FF3022D28A4220D982 -:1018BC00023B2144891A43EA084343E73A4605E7C9 -:1018CC00334618E70A4666E7B0409042B5D20CF1B1 -:1018DC00FF32002312E7334632460FE79A458DD983 -:1018EC00ACF1020C23448AE7B9459AD9023D27444E -:1018FC0098E70346DEE79046C6E70238214435E711 -:10190C0010B5044608484021FFF794FC0028F9D094 -:10191C0005482146FFF78AFC03484021FFF78AFC63 -:10192C000028F9D010BD00BF00480040014608B5A2 -:10193C000448FFF767FC03480221FFF765FC002809 -:10194C00F9D008BD0038004000B9074803680133DE -:10195C0008D04368013305D0C069044BC31A584200 -:10196C005841704700207047004000086F57654E83 -:10197C00074B1A69C2F306427F2A07D11869C0F3D4 -:10198C000C00B0F1FF0358425841704700207047DB -:10199C000028004000F13F4000F57E00800A082836 -:1019AC000FD8084A52F820305BB1013B42F8203086 -:1019BC003BB9012101FA00F0034B1A6B22EA00003B -:1019CC0018637047200000200038024000F13F40AF -:1019DC0000F57E00800A08280DD8074A52F82030FE -:1019EC00591C42F8201033B9012101FA00F0034BC5 -:1019FC001A6B104318637047200000200038024017 -:101A0C0010B50C4C2046FFF7E1FF072001F028FB36 -:101A1C004FF4805100222046FFF786FB042001F092 -:101A2C001FFB2046FFF7B6FF4FF480400021FFF765 -:101A3C0077F810BD0004024010B50B4C01214FF497 -:101A4C008040FFF76DF82046FFF7C0FF20464FF4AB -:101A5C0080510122FFF768FB072001F001FB2046B3 -:101A6C00FFF798FF10BD00BF0004024008B5FFF758 -:101A7C00E3FF0020FFF75AFF0020FFF757FF00207D -:101A8C00FFF754FFFFF7BCFFFEF772FEBFF34F8F5B -:101A9C00044A054BD16801F4E0610B43D360BFF3FA -:101AAC004F8FFEE700ED00E00400FA05F0B55E256F -:101ABC006843204BB0FBF1F51A68ADF6DC3D95425E -:101ACC0034D002AC00214FF43D6220461D60FEF77D -:101ADC00BEFA1B238DE818001748102140226923F9 -:101AEC0001F0A1FA08238DE818001448602318218E -:101AFC006A2201F098FA0DF2FA760DF28E7300223A -:101B0C00AA420DD002F11900C0B2C108012700F0A1 -:101B1C00070007FA00F05F5C013238435854EFE7D6 -:101B2C001233B342EBD1204601F0D5FA0DF6DC3D71 -:101B3C00F0BD00BF08000020943500080E370008E7 -:101B4C002DE9F043ADF6DC3D02AC00214FF43D62D3 -:101B5C0080462046FEF77BFA2C238DE818002022C5 -:101B6C00284818215C2301F05EFA1C2215460F2729 -:101B7C00974007EA0807D740234B242101FB07377E -:101B8C000DF50366002117F801C00023082B07D0C0 -:101B9C000120984010EA0C0F0CBF0020012003E03C -:101BAC007818407800F0010078B15819C0B24FEAAB -:101BBC00D00E4FF0010900F0070009FA00F016F8FA -:101BCC000E9040EA090006F80E000133092BDDD116 -:101BDC000231242906F11206D5D10B35742DA2F150 -:101BEC000402C4D10E238DE81800102190226D231D -:101BFC00064801F018FA204601F06DFA0DF6DC3DAE -:101C0C00BDE8F0838433000883390008C032000833 -:101C1C0010B5ADF6D83D02AC204600214FF43D6224 -:101C2C00FEF715FA1B238DE81800102140226923BA -:101C3C00044801F0F8F9204601F04DFA0DF6D83DB4 -:101C4C0010BD00BF943500082DE9F74F434E444FAB -:101C5C0001214FF48040FEF763FF3046FFF7B6FEDC -:101C6C003846404CFFF7B2FE30460D210522FFF7F7 -:101C7C0060FA052230460F21FFF75BFA3046214609 -:101C8C000125FFF7FEF920233846214623602571F4 -:101C9C00A5714FF0000A01934FF4805BFFF7F1F947 -:101CAC00304621464FF002084FF48049257184F8E4 -:101CBC0006A0C4F800B0FFF7E4F93046214684F8DA -:101CCC000480C4F80080FFF7DCF921462571A5716A -:101CDC00C4F80090244C3046FFF7D3F92046FFF7A8 -:101CEC0039F920462149FFF75FFA20462946FFF7CC -:101CFC007AFA019B384619465246FFF715FA2A46DE -:101D0C0030464946FFF710FA40462946FEF708FFD1 -:101D1C00A4F5505480232380F92323854FF4F042FB -:101D2C004FF6FF13E3623046A283294623644246F2 -:101D3C00A4F820B0FFF7FDF929462046A582FEF74E -:101D4C001FFD3846FFF726FE3046FFF723FE4846B8 -:101D5C005146FEF7E5FE03B0BDE8F08F00040240EB -:101D6C000008024000000020003800400C00002059 -:101D7C0010B5074C2046FFF729FE20461021FFF72F -:101D8C00CFF92046FFF706FE012001F069F910BDDE -:101D9C000000024038B50A4B0C2404FB0034606888 -:101DAC00FFF714FE21896068FFF7B4F905466068F7 -:101DBC00FFF7F0FDD5F1010038BF002038BD00BFA2 -:101DCC007832000810B5064C2046FFF7FFFD204680 -:101DDC001021FFF7A7F92046FFF7DCFD10BD00BF6F -:101DEC0000000240F0B587B0FFF7C2FD002841D1DA -:101DFC000320FFF759F807460420FFF755F8424B2C -:101E0C000A241A68C2F30545C2F30626C2F302136C -:101E1C0002F00F0204FB0322330906F00F0604FB49 -:101E2C0003662B0905F00F0504FB03533C2505FB4A -:101E3C00036305FB0323191A44BF01F5A83101F50F -:101E4C00C07107EB91210320FFF71EF803A8FFF7E1 -:101E5C0098F903A8FEF7F4FE01A8FFF798F901A979 -:101E6C000020FEF731FF02A8FFF797F9002002A926 -:101E7C00FEF794FF0A2442F2107501F04EF910B9E6 -:101E8C00013DFAD107E0214D01F047F908B1013DC0 -:101E9C00FAD1013CEFD10020FEF792FD00214FF268 -:101EAC000400FFF71DF84FF43C50FFF745F8184BB2 -:101EBC0001201A6842F004021A60FEF781FD154BEE -:101ECC001B7813B1144801F047F9144801F044F998 -:101EDC000020FEF7F9FF08200121FEF7C1FF0121C8 -:101EEC004FF40040FEF7FCFF0D4801F035F9084BAC -:101EFC001A6842F002021A600A4B1A6942F0040294 -:101F0C001A6130BF07B0F0BD00280040A086010068 -:101F1C0000700040017AFF1F6E37000883370008FD -:101F2C009837000800ED00E0B0F1006F10B50446E2 -:101F3C000BD20D4801F007F9204601F013F90B48BC -:101F4C0001F00AF94FF0FF3010BD094A002352F896 -:101F5C00041FA14202D851688C4202D30133072BD3 -:101F6C00F5D1184610BD00BFAC380008A9370008E1 -:101F7C005432000808B5024801F0EEF800BEFEE746 -:101F8C00C237000810B504460A480221FFF73CF995 -:101F9C000028F9D007482146FFF734F905480121FC -:101FAC00FFF732F90028F9D00248FFF728F9C0B240 -:101FBC0010BD00BF003001402DE9F04F87B001216A -:101FCC00814600AF0020FFF771FD042001F063F89B -:101FDC0007F10C0049460C2201F0E9F8FD683C6958 -:101FEC000C2DD7F814B013D0994801F0ACF8284652 -:101FFC0001F0B8F8974801F0A6F8204601F0B2F8C5 -:10200C00954801F0A0F8584601F0ACF8934833E03D -:10201C00934801F0A1F84FF480500121FEF774FDB4 -:10202C0009F10C09EA46FEF7EDFBA0B06E46254619 -:10203C00C846802D0CD941463046802201F0B7F8B5 -:10204C0030462021FEF7EAFB803D08F18008F0E7DE -:10205C0025B1304641462A4601F0A9F829463046BA -:10206C0000F0C6FF002180464FF48050FEF74CFD77 -:10207C00D845D54604D07B4801F06EF80120E3E04A -:10208C0088B068464946202201F091F86846FFF76F -:10209C005BFC78607448D54601F05EF87B682BB920 -:1020AC00724801F059F804F5404600E02646002E2F -:1020BC003BD06F48FFF738FF05466E483044FFF7BA -:1020CC0033FF002D824630DB00282EDB431B01330F -:1020DC004FEA430B00205946FFF7E8FCFEF7BEFB26 -:1020EC00654B002603EB45033B6055451BDCF32099 -:1020FC00FEF7CAFB3B68002133F81600FEF7E4FB41 -:10210C00082806F1010608D05C4801F01CF82846A6 -:10211C0001F028F801F00FF805E030465946FFF7BA -:10212C00C5FC0135E1E7FEF7A7FB554801F014F8B3 -:10213C007B680BB14E4B00E0524B7B6063083B60FD -:10214C000025A5423CD2661BB6F5803F28BF4FF454 -:10215C0080362EB14C4805EB0901324601F027F8C8 -:10216C00D7F804A0FEF77AFBF320FEF78DFBAA4408 -:10217C004FF0000BB34519D043490BEB0A0011F893 -:10218C000B10FEF7D9FB08280DD0404800F0DBFF00 -:10219C00504600F0E7FF00F0CEFFFEF76DFB3C4829 -:1021AC0000F0DAFF0CE00BF1010BE3E7FEF764FB48 -:1021BC003B68214603EB5500FFF778FC5D44C0E714 -:1021CC00344800F0C0FF204600F0CCFF324800F04D -:1021DC00C3FF0020FFF7B8FB20B1304800F0BCFF74 -:1021EC00234D03E02E4800F0B7FF264D4FF48050EE -:1021FC000121A608FEF788FCFEF704FB31462846B1 -:10220C00FEF70CFB04F0030105EB860000F0F0FE7A -:10221C00002104464FF48050FEF776FC214800F074 -:10222C0092FF404600F09EFF1F4800F08CFF2046B6 -:10223C0000F098FF00F07FFF444504D01B4800F0ED -:10224C008BFF022000E000201C37BD46BDE8F08F5C -:10225C00CD370008D6370008E0370008E73700080C -:10226C00063800082338000846380008593800089A -:10227C0000400008FF3F0008B03200087138000829 -:10228C008938000800000108440000209C38000830 -:10229C00B5380008C0380008CE380008D53800081A -:1022AC00FA3800081F39000832390008383900089C -:1022BC0070B54FF480500121FEF73EFCFFF782FD14 -:1022CC000520164CFFF75EFE002C04F1FF341FDCDA -:1022DC00FFF74EFDFFF776FD9F20FFF753FEA92079 -:1022EC00FFF750FE0546A920FFF74CFE0646A92035 -:1022FC00FFF748FE3602044646EA0545FFF738FD6F -:10230C004FF480500021FEF717FC45EA040070BD25 -:10231C00A920FFF737FEC307D6D4D9E70090D00326 -:10232C0008B50446114800F00EFF204600F01AFFD5 -:10233C0000F001FF2046FFF703FC00F0EEFE0C4B13 -:10234C0020F0040018701D4600F0E7FE2B7800F01A -:10235C00FB00834201D10B2401E0FFF787FB013C1A -:10236C00F2D04FF47A7000F07BFEF8E7C33B000824 -:10237C004400012080B5464B4FF4C06290B01A6007 -:10238C0001214FF08050FEF7CBFB0120FEF712FB32 -:10239C004048FFF71BFB4FF480200121FEF7C0FBE8 -:1023AC003C480A210722FEF7C4FE072239480B21BC -:1023BC00FEF7BFFE10AF4FF4806347F8383D0024A2 -:1023CC004FF001080225334839468DF80E408DF840 -:1023DC000F808DF80C508DF80D50FEF752FE4FF417 -:1023EC0000632C48394602938DF80C5004AEFEF76E -:1023FC0048FE4FF46133049327480C233146ADF863 -:10240C001A30ADF81440ADF81640ADF81840ADF8E0 -:10241C001C40FEF7CDFD41461F48FEF7FBFE1D4854 -:10242C00FFF7B8FA1D4800F08EFE1D4B586F00F0F8 -:10243C0099FE00F080FE7920FEF7C6FB404502D1E4 -:10244C00184800F089FE012000F010FE10B901219F -:10245C00FEF71AFD4120FEF7B7FB58BB124800F0FF -:10246C007BFE0120FEF7DEFA47F230544120FEF7E6 -:10247C00ABFBF8B94FF47A7000F0F2FD013CF5D1EA -:10248C000A4800F069FEFFF7DFFB094837E100BF9F -:10249C00003C02400008024000480040CF3B0008CE -:1024AC0000380240DF3B0008EF3B0008073C000807 -:1024BC00114550FE4FF48070FEF708FB0120FEF72B -:1024CC001DFBFEF7DFFB0020FEF77AFA01214FF42B -:1024DC008040FEF731FBBB48FFF778FA3046FEF739 -:1024EC0010FE00240125B74831468DF815408DF8B3 -:1024FC0016408DF8174004958DF81450FEF7C1FD69 -:10250C002246B0482946FEF70FFEAE48FFF742FAC6 -:10251C00AD4C04F1300954F8040CFFF757FA30466F -:10252C00FEF7EFFD236854F8040C4FF00008314619 -:10253C00022504938DF814808DF815508DF81780B2 -:10254C000C34FEF79EFD54F8100CFFF723FA4C45A3 -:10255C00E1D14FF480404146FEF7EEFA4FF4807023 -:10256C0000F084FD002800F0CC804FF4807000F067 -:10257C0087FDFFF737FC954800F0EEFD0220FEF7D3 -:10258C0093FC9349884202D00220FEF77DFC9148CF -:10259C0000F0D9FD0220FEF787FC00F0E3FD00F00F -:1025AC00CAFD4FF4804000F061FD90B18A4800F004 -:1025BC00D3FD4FF4804000F063FD082000F060FD77 -:1025CC00102000F05DFD202000F05AFD402000F0AE -:1025DC0057FDFFF739FBFFF71BFB01214FF4805030 -:1025EC00FEF7AAFA7748FFF7F1F901200146FEF74A -:1025FC008BFA05210A467348FEF79BFD71480621AC -:10260C000522FEF796FD05226E480721FEF791FD87 -:10261C0000240225C0236B4839468DF80C508DF8E8 -:10262C000D5002938DF80E408DF80F40FEF729FDEA -:10263C002023644839468DF80F5002930125FEF78C -:10264C0020FD102339465F4802938DF80C508DF80D -:10265C000F50FEF716FD6148FEF77CFC4FF48273B9 -:10266C00ADF812304FF40073ADF81A305B48072305 -:10267C003146ADF82030ADF81040ADF81440ADF84F -:10268C001640ADF81840ADF81C40ADF81E40FEF7F2 -:10269C008BFD29465148FEF7A6FD4A48FFF77AF90B -:1026AC00FFF790FBAB20FFF76DFCFFF761FB64209D -:1026BC0000F0D6FC21464FF48050FEF73DFAFFF7B0 -:1026CC00F7FD2846FEF7F0FB25460490E8B2FFF72D -:1026DC0061FB98BB70550135042DF7D1049D3DB1BC -:1026EC003F4800F030FD284600F03CFD00F023FD93 -:1026FC0001202946FEF7C8FB002C3DD03948FFF7D6 -:10270C000FFE2846FEF762F9012814D12846FEF781 -:10271C0067F94FF40060FEF701FC18B1324800F085 -:10272C001BFDB9E0314C00F0F8FC002800F00481EE -:10273C00013CF8D1B0E000F0B9FC1CE7735D042B50 -:10274C000ADD2B4800F008FD049800F00BFD012079 -:10275C000021FEF799FB0FE00133DBB2042B73551C -:10276C00B9DD244800F0EFFC284600F0FBFC2248C1 -:10277C0000F0F2FC0124AEE7FFF79AFD1F4B034477 -:10278C00012B01D91E48BAE71E4800F0DCFC0020E2 -:10279C00FEF78AFB00F0E6FC00F0CDFC802000F098 -:1027AC0065FC18B1802000F06BFC73E04FF4003036 -:1027BC0000F05CFC044638B34FF4003000F060FCD1 -:1027CC0004E100BF00000240803200083F3C0008DA -:1027DC00708641567B3C0008903C0008003001405C -:1027EC00FE3C0008014550FE243C0008A086010078 -:1027FC00BD3C0008E93C0008F43C0008EA44DFFF5B -:10280C00024550FE2A3D00080120FFF7C3FAD8B15B -:10281C002046FFF7BFFAB8B1B74800F09DFC41F273 -:10282C0088340120FFF7B6FA002800F08B800020D6 -:10283C00FFF7B0FA002800F085804FF47A7000F0B2 -:10284C000FFC013CEDD1C1E00020FFF77DF808B191 -:10285C00AA4B00E0AA4B0CCB013301D0013271D151 -:10286C00A84800F079FCB1E0A74800F075FC0820FE -:10287C0000F006FC102000F003FC202000F000FC0F -:10288C00402000F0FDFB0024042000F0F9FB002C9C -:10289C0040F008819D4832E7FFF76AF8002847D0DE -:1028AC00002001A99A4DFEF757FA3446002039460C -:1028BC00FEF7B6FA0FCD0FC40FCD0FC495E80F007D -:1028CC0084E80F009DF8092010A901EB82039DF804 -:1028DC000BE053F8343C40F26D1101FB0E3303EB6B -:1028EC009E030EF1010E1EF0030F02D1022A88BFC7 -:1028FC0001339DF80A409DF80410013C2344182430 -:10290C0004FB03149DF805103C2202FB04149DF8F3 -:10291C000610032002FB0414FEF7C6FA011903206B -:10292C00FEF7B2FA04200021FEF7AEFA00F0BEFB6F -:10293C00784800F011FC1EE6774800F00DFCFFF71C -:10294C0051FAF5E77D20FEF73FF970B94FF40070AE -:10295C0000F08CFB48B9714800F0FEFB082000F039 -:10296C008FFB102000F08CFB85E07D20FEF72CF90E -:10297C0010B16B4800F0F0FB4FF4007000F076FBE8 -:10298C0010B1684800F0E8FB4FF4007000F078FBE1 -:10299C00102000F06BFB48B1634800F0DDFB082011 -:1029AC0000F06EFB102000F06BFB12E0082000F032 -:1029BC005DFB20B15D4800F0CFFB102003E05C48CC -:1029CC0000F0CAFB082000F066FB54E0594800F008 -:1029DC00C3FB594800F0C0FB4FF40010FFF7ECFAB2 -:1029EC0010B1022828D809E0202000F049FB402033 -:1029FC0000F046FB802000F04EFB1DE0082000F0AC -:102A0C004AFB102000F047FB202000F02FFB20B9E0 -:102A1C004A4800F0A1FB202009E0402000F026FBF2 -:102A2C0000287FF421AF464800F096FB402000F0D0 -:102A3C0032FB4FF400701AE0012425E7414800F006 -:102A4C008BFB082000F01CFB102000F019FB202051 -:102A5C0000F016FB402000F013FB2046FFF7ACFA09 -:102A6C00022840F0F080082000F015FB102000F048 -:102A7C0012FBFEF7FBFF022000F0F8FA90B10420E5 -:102A8C0000F0F4FA04460028D8D02F4800F064FB7C -:102A9C00042000F0F5FA022000F0F2FA4FF40040A6 -:102AAC0000F0EEFA4FF4005000F0E0FA04464FF458 -:102ABC00805000F0DBFA400040EA84044FF40060E0 -:102ACC0000F0D4FAE4B22043431C03F0FF03013BB3 -:102ADC004FF40060062B58D8DFE803F03C383C0478 -:102AEC003C383C0000F0CCFA4FF4805000F0C8FAAF -:102AFC004FF400502EE000BF363D000800400008A7 -:102B0C0000000108683D0008923E0008034550FE95 -:102B1C00283200082F3C0008313F00087B3D00089C -:102B2C008C3D0008A43D0008C43D0008DA3D0008B7 -:102B3C00F03D0008463F0008063E0008203E000815 -:102B4C00593E0008023F0008CC3E000800F098FAFD -:102B5C004FF4805000F09FFA45F25550FDF708FFF6 -:102B6C000420FDF70BFF40F6FF70FDF70DFF002072 -:102B7C00FDF7FEFE4FF480500121FDF763FE0020AF -:102B8C00FEF7E2FE68B9354B0CE000F079FA4FF431 -:102B9C00805000F075FA4FF4005000F071FA304894 -:102BAC00ADE5304B5D6830481E6800F0CCFA284625 -:102BBC0000F0D8FA00F0BFFA00F0BDFA00239A003A -:102BCC0002F16042013302F561424FF0FF31082BF4 -:102BDC00C2F88010C2F88011F1D1244B00241C6380 -:102BEC005C639C631C645C64FDF702FF204801215C -:102BFC00FDF7AEFF21461E48FDF7AAFFF12001218B -:102C0C00FDF7B2FF2146F120FDF7AEFF0120014692 -:102C1C00FDF7B6FF21460120FDF7B2FF1548012153 -:102C2C00FDF7BAFF21461348FDF7B6FF1248012104 -:102C3C00FDF7BEFF10482146FDF7BAFF63B64FF013 -:102C4C00FF3EB5462847042000F01AFA022000F097 -:102C5C0017FA4FF4004000F01EFA23E700000108B9 -:102C6C00044550FE004000081D3F0008003802409B -:102C7C0000106022FFC9FE36335907000748084A86 -:102C8C0008B50849121AFDF7D9F90748074A002177 -:102C9C00121AFDF7DCF9FFF76DFB00BF00000020F6 -:102CAC0020000020B43F0008200000204800012034 -:102CBC002DE9F04FDDE909AB0E469C4603783B4904 -:102CCC000746C95C01300D07F8D42B2B05D02D2BF2 -:102CDC0004D107464FF0FF3301E007460123397852 -:102CEC0030290ED17978782905D132F0100103D131 -:102CFC000237102207E00AB1082A04D10137082252 -:102D0C0001E002B90A2200200021B84617F8015B45 -:102D1C00A5F13004E4B2092C0DD9A5F14104192C0C -:102D2C0002D8A5F1370405E0A5F16104192C30D8BF -:102D3C00A5F15704E4B294422BDA4FEAE27900FB96 -:102D4C0009F502FB0155A0FB0201E4B2294400196C -:102D5C004FF0000541EB0501BCF1000FD5D0012B64 -:102D6C0006D182457BEB0104CFDA50465946CCE7BD -:102D7C005C1CCAD1DDE90B89444261EB4105444539 -:102D8C0075EB0909C1DADDE90B01404261EB410148 -:102D9C00BBE70EB1C6F80080DD1700FB05F203FBA4 -:102DAC000122A0FB03011144BDE8F08F4C12000876 -:102DBC002DE9F04F91B01F9E0C463106914680468E -:102DCC00DDE91A231E9D04D5CDE902234FF0000C3A -:102DDC001BE0002A73F100071046194602DA504234 -:102DEC0063EB4301002A73F1000ACDE9020107DB12 -:102DFC00B70708D416F0040C07D04FF0200C04E0F1 -:102E0C004FF02D0C01E04FF02B0C16F040070897FB -:102E1C0014BF704F704FDDE902AB079700271C9869 -:102E2C000137C117CDE90401DDE904230AA93944AE -:102E3C00099150465946CDF804C0FEF781FA07981F -:102E4C000999835C504601F8013C5946DDE904239D -:102E5C00FEF776FA82468B465AEA0B02DDF804C07E -:102E6C0001D0162FDBD116F0080239461DD01C9B61 -:102E7C00082B02D0102B0ED01CE0DDE902AB1DB9E3 -:102E8C005AEA0B0B13D004E05AEA0B0B12D0BD42DA -:102E9C0010DC7B1C0FE0DDE902AB5AEA0B0B09D00E -:102EAC0008982B4648B1582208E02B4606E02A46E3 -:102EBC00012303E02B46002200E078221CF1000BDA -:102ECC001D98C7EB030A18BF4FF0010BBB42A8BFFC -:102EDC001F46C01B2AEAEA7ACBEB00000AB1022794 -:102EEC0000E01746C71B16F0010027EAE77701D070 -:102EFC007F4205E0F60603D5013504BFBA44074608 -:102F0C003D46002D0BDD2068013D461C4E453CBF67 -:102F1C00202608F80060206801302060F1E727EADD -:102F2C00E7703F1ABBF1000F08D02068451C4D45D7 -:102F3C0038BF08F800C02068013020608AB12068D2 -:102F4C00451C4D453CBF302508F800502568681CD1 -:102F5C0002354D45206038BF08F800202268013248 -:102F6C002260BAF1000F0CD022680AF1FF3A501C13 -:102F7C0048453CBF302008F802002268013222602C -:102F8C00EFE7DDE902AB5AEA0B0B10D17BB919461E -:102F9C000DE0236801395A1C4A4505D201F1280578 -:102FAC0068462A5C08F80320236801332360002953 -:102FBC00EFD15FB1236801375A1C4A453CBF202230 -:102FCC0008F80320236801332360F2E711B0BDE851 -:102FDC00F08F00BF793F00088A3F00081EF0040FF5 -:102FEC000CBFEFF30880EFF30980FEF7C3BF704707 -:102FFC00032970B505460C4606D98E083146FDF7F7 -:10300C000DFCB6003544A41B14B9FDF715FC70BDBE -:10301C0000231846EA5C0133A34242EA0020F9D1AE -:10302C00FDF7F6FB70BD2DE9F0410733DE08079B79 -:10303C004FF0120808FB02320731069F054602EBDF -:10304C00D1080024BC4209D0122000FB0480294680 -:10305C003246FCF7F3FF01343544F3E7BDE8F08169 -:10306C000623584301387FF4FDAF704710B5044672 -:10307C000020FDF719FF20420CBF0020012010BDDD -:10308C0010B504460020FDF70FFF20EA04010020D4 -:10309C00FDF7FAFE10BD10B504460020FDF704FF45 -:1030AC0040EA04010020FDF7EFFE10BD1FB501A89A -:1030BC00FEF767F80723029301A803230393FDF798 -:1030CC00BFFD6846FEF763F869460020FDF7FCFD7E -:1030DC0005B05DF804FB70B50646FEF7ADFC80202C -:1030EC00FEF724FCA82420BA90FAA0F0C0B2FEF798 -:1030FC001DFC1125705DFEF719FC15F1FF35F9D299 -:10310C000020FEF713FC013C06F11206EBD1204621 -:10311C00FEF70CFCFEF774FC70BD38B50024254698 -:10312C00E0B2FEF737FEA04001340543042CEDB2AB -:10313C00F6D1284638BD08B50D20FEF7E1FB0A2074 -:10314C00FEF7DEFB08BD10B5441E14F8010F10B1DC -:10315C00FEF7D6FBF9E710BD08B5FFF7F4FFFFF754 -:10316C00EAFF08BD1FB530238DF8043078238DF8A5 -:10317C000530072399000F228A400240CA40092AD1 -:10318C0001D8303202E00F2A02D85732D2B200E016 -:10319C00202201A9CC1A13F1FF336272EAD2002368 -:1031AC0008468DF80E30FFF7CEFF04B010BD70B599 -:1031BC0004460D464FF4805001211646FDF7BCFC29 -:1031CC00FEF700FE0520FEF7DDFEA920FEF7DAFE75 -:1031DC00C307FAD4FEF7CCFDFEF7F4FD0320FEF78F -:1031EC00D1FEC5F30740FEF7CDFEC5F30720FEF771 -:1031FC00C9FEE8B2FEF7C6FE2644B44205D0A920AB -:10320C00FEF7C0FE04F8010BF7E7FEF7B1FD4FF433 -:10321C0080500021FDF790FC70BD00000000000004 -:10322C001F0000003B0000005A0000007800000066 -:10323C0097000000B5000000D4000000F30000006F -:10324C0011010000300100004E01000000000008D8 -:10325C00004000080080000800C0000800000108C1 -:10326C00000002080000040800000608653F000882 -:10327C0000080240080000006A3F000800000240FD -:10328C00040000006D3F00080008024040000000F0 -:10329C00743F0008000002400200000000000240E1 -:1032AC000100000000000800100018002000280099 -:1032BC003000380000000683010000000000000010 -:1032CC0000000000068301000000000C000000005C -:1032DC000000068301000000000C00000000D878FC -:1032EC00369B79C0E3D90C8C67DB3C1BF8FD7EBFA9 -:1032FC00FDE0F7FB1FC6EFFB7E1F98CD66B3CD60DC -:10330C00369B19C66C18660398FD66B3FD60309B3E -:10331C0019C66F18660398FD66B3FD60309B19C320 -:10332C006F186603980D66B30D60369B19C3601851 -:10333C006603F8FD7EBFFDECF79B19C36F187E0387 -:10334C00D878369B79CCE399998167183C0318009F -:10335C0000000000000080010000000018000000C8 -:10336C000000000000000000000018000000000039 -:10337C000000000000000000000C0006000000002F -:10338C00600030000018000300000000C0001800AE -:10339C00003080010000000080010C000060C000C3 -:1033AC00000000000003060000C0600000000000E8 -:1033BC00000603000080310000000000008C0100BA -:1033CC0000001B000000000000D8000000000E00F0 -:1033DC00000000000070000000000E000000000063 -:1033EC000070000000001B000000000000D800006E -:1033FC000080310000000000008C010000C0600063 -:10340C0000000000000603000060C0000000000087 -:10341C0000030600003080010000000080010C0059 -:10342C000018000300000000C0001800000C00068B -:10343C0000000000600030000000000000000000F0 -:10344C000000000000000000000000000000000070 -:10345C000000000000000000000000000000000060 -:10346C000000000000000000000000000000000050 -:10347C000000000000000000000000000000000040 -:10348C000000000000000000000000000000000030 -:10349C0000000000000000000000000080FFFF01A1 -:1034AC000000000000000000FEFFFF7F0000000095 -:1034BC00000000E07F0000FE07000000000000FE9E -:1034CC00010000807F0000000000801F0000000051 -:1034DC00F80100000000F00100000000800F000067 -:1034EC0000007C0000000000003E000000000F0007 -:1034FC000000000000F0000000800300000000004D -:10350C0000C0010000E00000000000000000070007 -:10351C00007000000000000000000E000018000009 -:10352C000000000000001800000C0000000000006B -:10353C0000003000000600000000000000006000E9 -:10354C0000030000000000000000C000800100002B -:10355C000000000000008001C0000000000000001E -:10356C0000000003600000000000000000000006E6 -:10357C0030000000000000000000000C10000000F3 -:10358C0000000000000000080000000000600000C7 -:10359C0003001800000000000000006000000300A1 -:1035AC00180000000000000000600000030018007C -:1035BC000000000000000060000003001800000084 -:1035CC00000000000060000003001800000000E094 -:1035DC0003001F60F800C30718C0070000F80FC0F5 -:1035EC007F60FE03F31F18F01F00000C1860C06012 -:1035FC0003061B30181830000006303080E1010C37 -:10360C000F60180C60000003601800E3001807C07E -:10361C001806C0008001400C0062001003801803E3 -:10362C0080008001C00C006600300380190380010B -:10363C008001C0FCFF670030038019FFFF0180018F -:10364C00C0FCFF630030038019FFFF008001C00C39 -:10365C00006000300380190300008001C00C006082 -:10366C0000300380190300008001400C0062001040 -:10367C000380180380008003601800C3001806C084 -:10368C001806C000800730308081010C0C60180CCB -:10369C006000800D1860C000030618303818300028 -:1036AC0080F90FC07F00FE03F01F70F01F0080E157 -:1036BC0003001F00F800C00760C007008001000075 -:1036CC00000000000000000000008001000000006D -:1036DC00000000000000000080010000000000005D -:1036EC00000000000000800100000000000000004D -:1036FC00000000008001000000000000000000003D -:10370C000000FFFFFFFFFFFFFFFFFFFFFFFF0100B8 -:10371C00000000000000000000800100000000001C -:10372C00000000000080010000000000000000000C -:10373C00008001000000000000000000008001007B -:10374C0000000000000000000080010000000000EC -:10375C00000000000080FFFFFFFFFFFFFFFFFFFFE7 -:10376C00FFFF4E6F2048572056657273696F6E20AD -:10377C00696E204F5450007573622077616B6575CC -:10378C007020737570706F7274656400456E74652B -:10379C0072696E67207374616E646279002069735C -:1037AC00206F7574736964652073797374656D200B -:1037BC00666C6173680048415244204641554C5434 -:1037CC0000446573636C656E20000A4669726D6C0B -:1037DC00656E20000A5873756D20000A496E76617B -:1037EC006C6964206669726D7761726520646573BB -:1037FC006372697074696F6E2100436865636B73E3 -:10380C00756D6D696E67206669726D776172652082 -:10381C0075706461746500496E76616C69642066CC -:10382C0069726D776172652043524320696E205333 -:10383C00504920666C617368210065726173655F25 -:10384C006F6C645F6669726D77617265004F6C6452 -:10385C0020576F726C64206669726D776172652097 -:10386C0062617365006661696C656420746F2065C4 -:10387C007261736520736563746F7220007772696F -:10388C0074655F6E65775F6669726D7761726500EE -:10389C006661696C656420746F2077726974652049 -:1038AC0061646472657373200057652772652064C8 -:1038BC0065616400436865636B73756D6D696E67F4 -:1038CC002000206279746573004E657720576F7203 -:1038DC006C64206669726D7761726520737973749C -:1038EC00656D5F666C6173685F62617365004F6CD8 -:1038FC006420576F726C64206669726D77617265B3 -:10390C002073797374656D5F666C6173685F626157 -:10391C00736500436865636B73756D202D2077614B -:10392C006E746564200020676F7420004F757220E0 -:10393C00696E7465726E616C20666C61736820636D -:10394C006F6E74656E7473206172652062616420A1 -:10395C0028636865636B73756D206661696C65645B -:10396C0029212054686973206973207265616C6C1D -:10397C00792062616421007C00FE00FF01C701C751 -:10398C0001C701C701C701C701C701C701C701C7EB -:10399C0001C701C701FF01FE007C0038003C003E5E -:1039AC00003E003800380038003800380038003845 -:1039BC00003800380038003800FE00FE00FE007CA5 -:1039CC0000FE00FF01C701C701C001C001E000F00B -:1039DC000078003C001E000E000F000700FF01FFE6 -:1039EC0001FF017C00FE00FF01C701C701C001C03F -:1039FC0001F8007800F800C001C001C001C701C780 -:103A0C0001FF01FE007C00E000E000F000F000F897 -:103A1C0000F800F800FC00EC00EE00E600FF01FFEF -:103A2C0001FF01E000E000E000E000FF00FF00FF0C -:103A3C00000700070007007F00FF00FF01C701C05F -:103A4C0001C001C701C701C701FF01FE007C007C5A -:103A5C0000FE00FF01C701C701070007007700FF48 -:103A6C0000FF01C701C701C701C701C701FF01FE64 -:103A7C00007C00FF01FF01FF01E000E0007000701E -:103A8C0000700038003800380038001C001C001C86 -:103A9C00001C001C001C007C00FE00FF01C701C7BD -:103AAC0001C701C701FE007C00FE00C701C701C7AA -:103ABC0001C701C701FF01FE007C007C00FE00FF76 -:103ACC0001C701C701C701C701C701FF01FE01DC26 -:103ADC0001C001C001C701C701FF01FE007C00004D -:103AEC00000000000000007C00FE00FF01C701C7C1 -:103AFC0001F001FC01CE01C701C701E701FF01DFA5 -:103B0C0001CE010700070007000700E700F701FFDF -:103B1C0001CF01C701C701C701C701C701C701CF49 -:103B2C0001FF01F701E70000000000000000007C2D -:103B3C0000FE00FF01C701C70107000700070007CF -:103B4C0000C701C701FF01FE007C00C001C001C01D -:103B5C0001C001CE01DF01FF01E701C701C701C7A9 -:103B6C0001C701C701C701E701FF01DF01CE010059 -:103B7C00000000000000007C00FE00FF01C701C730 -:103B8C0001C701FF01FF010700C701C701FF01FECB -:103B9C00007C00E000F000F8003800FE00FE00FEA3 -:103BAC000038003800380038003800380038003849 -:103BBC0000380038003800534144205741544348E2 -:103BCC003A20005265736574205265676973746599 -:103BDC0072200042726F776E206F7574207265735D -:103BEC006574005374617274696E67204C53452080 -:103BFC006F7363696C6C61746F72004C5345206F0A -:103C0C007363696C6C61746F7220646964206E6F8D -:103C1C0074207374617274005553422077616B6524 -:103C2C007570006C656176696E67207374616E6483 -:103C3C00627900205F5F5F5F5F5F202020205F5F05 -:103C4C000D0A2F5F20205F5F2F205F5F2F202F0D2D -:103C5C000A202F202F2020202F5F20205F5F2F0D88 -:103C6C000A2F5F2F20202020202F5F2F0D0A0042CB -:103C7C006F6F746C6F616465722076657273696FB7 -:103C8C006E3A20004C617374206669726D776172B4 -:103C9C006520626F6F742077617320737461626C3E -:103CAC00653B20636C65617220737472696B65731C -:103CBC0000537475636B20627574746F6E2072653B -:103CCC0067697374657220697320696E76616C69BB -:103CDC00642C20636C656172696E672E004275748A -:103CEC00746F6E2069642000697320737475636B44 -:103CFC002100427574746F6E207761732070757338 -:103D0C00686564206F6E20626F6F742E204275742C -:103D1C00746F6E20636F756E7465723A2000426F1B -:103D2C006F7420626974733A2000486F6C6420646D -:103D3C006F776E205550202B204241434B20666FED -:103D4C0072203520736563732E20746F20666F723A -:103D5C0063652D626F6F7420505246004669726D18 -:103D6C0077617265206973206572617365640042C6 -:103D7C006F6F74696E67206E6F726D616C6C790019 -:103D8C005761746368646F67206361757365642041 -:103D9C006120726573657400536F66747761726528 -:103DAC00206661696C75726520636175736564204A -:103DBC006120726573657400426F6F74206661696F -:103DCC006C65642C20737472696B65203300426FD0 -:103DDC006F74206661696C65642C20737472696BF6 -:103DEC0065203200426F6F74206661696C65642CCB -:103DFC0020737472696B652031004C6F6164696E5D -:103E0C0067207265636F76657279206669726D776B -:103E1C00617265004661696C656420746F206C6F1B -:103E2C006164207265636F76657279206669726D64 -:103E3C00776172652C20737472696B65206F6E6587 -:103E4C002E2054727920616761696E2E004661697B -:103E5C006C656420746F206C6F6164207265636F95 -:103E6C0076657279206669726D776172652C207344 -:103E7C007472696B652074776F2E2054727920618F -:103E8C006761696E2E004661696C656420746F20F1 -:103E9C006C6F6164207265636F76657279206669F8 -:103EAC00726D776172652C20737472696B65207406 -:103EBC00687265652E205341442057415443480095 -:103ECC004F75722070726576696F757320666972B2 -:103EDC006D776172652075706461746520666169C7 -:103EEC006C65642C2061626F7274696E67207570EA -:103EFC00646174652E004E6577206669726D77611A -:103F0C00726520697320617661696C61626C6521F0 -:103F1C0000426F6F74696E67206669726D776172AB -:103F2C00652040200072657475726E696E6720742E -:103F3C006F207374616E64627900466F7263652DD5 -:103F4C00626F6F74696E67207265636F76657279E4 -:103F5C00206D6F64652E2E2E004261636B005570D0 -:103F6C000053656C65637400446F776E00303132BA -:103F7C0033343536373839414243444546003031C5 -:103F8C0032333435363738396162636465660028FC -:103F9C006E756C6C29000000000001020304010224 -:083FAC000304060708090000E8 -:103FB40000A0000002020000FFFFFFFF00C0040198 -:103FC40000000000000000021000000007000000D4 -:040000050800134C90 -:00000001FF diff --git a/bin/boot/wscript b/bin/boot/wscript deleted file mode 100644 index e7cef80c19..0000000000 --- a/bin/boot/wscript +++ /dev/null @@ -1,27 +0,0 @@ -def configure(cfg): - variant = '' - if cfg.options.nowatchdog: - variant = 'nowatchdog_' - - if cfg.options.board in ('snowy_bb2', 'snowy_s3', 'spalding_bb2'): - bootloader_board = 'snowy_dvt' - elif cfg.is_silk(): - bootloader_board = 'silk' - elif cfg.is_cutts(): - bootloader_board = 'robert_bb' - else: - bootloader_board = cfg.options.board - - boot_file_pattern = '%sboot_%s@*.hex' % (variant, bootloader_board) - boot_node = cfg.path.ant_glob(boot_file_pattern) - - if not boot_node: - cfg.end_msg('Not found') - elif len(boot_node) > 1: - cfg.fatal('Multiple bootloader binaries matching "%s": %s' % ( - boot_file_pattern, ', '.join(str(n) for n in boot_node))) - else: - cfg.env.BOOTLOADER_HEX = boot_node[0].abspath() - cfg.end_msg(cfg.env.BOOTLOADER_HEX) - -# vim:filetype=python diff --git a/bin/prf/recovery_bb2-3.7-prf-alpha9.bin b/bin/prf/recovery_bb2-3.7-prf-alpha9.bin deleted file mode 100644 index 26929a10d4..0000000000 Binary files a/bin/prf/recovery_bb2-3.7-prf-alpha9.bin and /dev/null differ diff --git a/bin/prf/recovery_ev2_4-3.7-prf-alpha9.bin b/bin/prf/recovery_ev2_4-3.7-prf-alpha9.bin deleted file mode 100644 index dc1799056a..0000000000 Binary files a/bin/prf/recovery_ev2_4-3.7-prf-alpha9.bin and /dev/null differ diff --git a/bin/prf/recovery_snowy_dvt_v3.0.2-prf.bin b/bin/prf/recovery_snowy_dvt_v3.0.2-prf.bin deleted file mode 100644 index d8872be12a..0000000000 Binary files a/bin/prf/recovery_snowy_dvt_v3.0.2-prf.bin and /dev/null differ diff --git a/bin/prf/recovery_snowy_evt2_v3.0.2-prf.bin b/bin/prf/recovery_snowy_evt2_v3.0.2-prf.bin deleted file mode 100644 index 61d4d6b6cd..0000000000 Binary files a/bin/prf/recovery_snowy_evt2_v3.0.2-prf.bin and /dev/null differ diff --git a/bin/prf/recovery_snowy_s3_v3.0.3-prf.bin b/bin/prf/recovery_snowy_s3_v3.0.3-prf.bin deleted file mode 100644 index 6d6456e845..0000000000 Binary files a/bin/prf/recovery_snowy_s3_v3.0.3-prf.bin and /dev/null differ diff --git a/bin/prf/recovery_spalding_v3.2.0-prf5.bin b/bin/prf/recovery_spalding_v3.2.0-prf5.bin deleted file mode 100644 index e2899b9084..0000000000 Binary files a/bin/prf/recovery_spalding_v3.2.0-prf5.bin and /dev/null differ diff --git a/bin/prf/recovery_v1_5-3.7-prf-alpha9.bin b/bin/prf/recovery_v1_5-3.7-prf-alpha9.bin deleted file mode 100644 index 0c66908dcc..0000000000 Binary files a/bin/prf/recovery_v1_5-3.7-prf-alpha9.bin and /dev/null differ diff --git a/bin/prf/recovery_v2_0-3.7-prf-alpha9.bin b/bin/prf/recovery_v2_0-3.7-prf-alpha9.bin deleted file mode 100644 index aef34c0800..0000000000 Binary files a/bin/prf/recovery_v2_0-3.7-prf-alpha9.bin and /dev/null differ diff --git a/boards/asterix/Kconfig b/boards/asterix/Kconfig new file mode 100644 index 0000000000..d6c3508f21 --- /dev/null +++ b/boards/asterix/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_ASTERIX + bool + default y + select BOARD_FAMILY_ASTERIX diff --git a/boards/asterix/Kconfig.defconfig b/boards/asterix/Kconfig.defconfig new file mode 100644 index 0000000000..90fc53715a --- /dev/null +++ b/boards/asterix/Kconfig.defconfig @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FAMILY_NAME + default "asterix" diff --git a/boards/asterix/asterix.yml b/boards/asterix/asterix.yml new file mode 100644 index 0000000000..4395475674 --- /dev/null +++ b/boards/asterix/asterix.yml @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +runners: + - openocd + - nrfutil diff --git a/boards/asterix/defconfig b/boards/asterix/defconfig new file mode 100644 index 0000000000..a849c34272 --- /dev/null +++ b/boards/asterix/defconfig @@ -0,0 +1,80 @@ +# asterix board configuration + +CONFIG_SOC_NRF52=y + +CONFIG_HAL_NORDIC=y +CONFIG_HAL_NORDIC_NRF52840=y + +CONFIG_PLATFORM_FLINT=y + +CONFIG_SCREEN_COLOR_DEPTH_BITS_1=y + +CONFIG_I2C=y +CONFIG_I2C_NRF5=y + +CONFIG_UART=y +CONFIG_UART_NRF5=y + +CONFIG_BACKLIGHT=y +CONFIG_BACKLIGHT_PWM=y + +CONFIG_VIBE=y +CONFIG_VIBE_DRV2604=y + +CONFIG_PMIC=y +CONFIG_PMIC_NPM1300=y + +CONFIG_AMBIENT_LIGHT=y +CONFIG_AMBIENT_LIGHT_OPT3001=y + +CONFIG_MIC=y +CONFIG_MIC_NRF5=y + +CONFIG_SPEAKER=y +CONFIG_SPEAKER_DA7212=y + +CONFIG_TEMPERATURE=y +CONFIG_TEMPERATURE_STUB=y + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_SHARP_LS013B7DH01_NRF5=y + +CONFIG_FLASH=y +CONFIG_FLASH_GD25LQ255E=y + +CONFIG_OTP=y +CONFIG_OTP_FLASH=y + +CONFIG_BATTERY=y +CONFIG_BATTERY_PMIC=y + +CONFIG_PRESSURE=y +CONFIG_PRESSURE_BMP390=y + +CONFIG_ACCEL_LSM6DSO=y +CONFIG_MAG=y +CONFIG_MAG_MMC5603NJ=y + +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NRF5=y + +CONFIG_EXTI=y +CONFIG_EXTI_NRF5=y + +CONFIG_GPIO=y +CONFIG_GPIO_NRF5=y + +CONFIG_PWM=y +CONFIG_PWM_NRF5=y + +CONFIG_RTC=y +CONFIG_RTC_NRF5=y + +CONFIG_RNG=y +CONFIG_RNG_STUB=y + +CONFIG_CPUMODE=y +CONFIG_CPUMODE_STUB=y + +CONFIG_ACCEL_SENSITIVITY=y +CONFIG_ORIENTATION_MANAGER=y diff --git a/boards/asterix/support/openocd.cfg b/boards/asterix/support/openocd.cfg new file mode 100644 index 0000000000..639dd54eda --- /dev/null +++ b/boards/asterix/support/openocd.cfg @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +source [find interface/cmsis-dap.cfg] +transport select swd + +source [find target/nrf52.cfg] + +adapter speed 10000 +reset_config none + +$_TARGETNAME configure -rtos FreeRTOS +$_TARGETNAME configure -event gdb-attach { + echo "Halting target because GDB is attaching..." + halt +} +$_TARGETNAME configure -event gdb-detach { + echo "Resuming target because GDB is detaching..." + resume +} diff --git a/boards/getafix/Kconfig b/boards/getafix/Kconfig new file mode 100644 index 0000000000..5727419a81 --- /dev/null +++ b/boards/getafix/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_GETAFIX + bool + default y + select BOARD_FAMILY_GETAFIX diff --git a/boards/getafix/Kconfig.defconfig b/boards/getafix/Kconfig.defconfig new file mode 100644 index 0000000000..2a85155b80 --- /dev/null +++ b/boards/getafix/Kconfig.defconfig @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FAMILY_NAME + default "getafix" diff --git a/boards/getafix/Kconfig.dvt b/boards/getafix/Kconfig.dvt new file mode 100644 index 0000000000..3f4eb3c360 --- /dev/null +++ b/boards/getafix/Kconfig.dvt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_GETAFIX_DVT + bool + default y diff --git a/boards/getafix/Kconfig.dvt2 b/boards/getafix/Kconfig.dvt2 new file mode 100644 index 0000000000..44e208bb05 --- /dev/null +++ b/boards/getafix/Kconfig.dvt2 @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_GETAFIX_DVT2 + bool + default y diff --git a/boards/getafix/Kconfig.evt b/boards/getafix/Kconfig.evt new file mode 100644 index 0000000000..9459bca960 --- /dev/null +++ b/boards/getafix/Kconfig.evt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_GETAFIX_EVT + bool + default y diff --git a/boards/getafix/defconfig b/boards/getafix/defconfig new file mode 100644 index 0000000000..937f5b8c26 --- /dev/null +++ b/boards/getafix/defconfig @@ -0,0 +1,85 @@ +# getafix board configuration + +CONFIG_SOC_SF32LB52=y + +CONFIG_HAL_SIFLI=y +CONFIG_HAL_SIFLI_SF32LB52=y + +CONFIG_PLATFORM_GABBRO=y + +CONFIG_PBLBOOT=y + +CONFIG_SCREEN_COLOR_DEPTH_BITS_8=y + +CONFIG_I2C=y +CONFIG_I2C_SF32LB=y + +CONFIG_UART=y +CONFIG_UART_SF32LB=y + +CONFIG_BACKLIGHT=y +CONFIG_BACKLIGHT_AW9364E=y +CONFIG_VIBE=y + +CONFIG_PMIC=y +CONFIG_PMIC_NPM1300=y + +CONFIG_AMBIENT_LIGHT=y +CONFIG_AMBIENT_LIGHT_W1160=y + +CONFIG_MIC=y +CONFIG_MIC_SF32LB=y + +CONFIG_TEMPERATURE=y +CONFIG_TEMPERATURE_SF32LB=y + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_JDI_SF32LB=y + +CONFIG_FLASH=y +CONFIG_FLASH_GD25Q256E=y + +CONFIG_OTP=y +CONFIG_OTP_FLASH=y + +CONFIG_BATTERY=y +CONFIG_BATTERY_PMIC=y + +CONFIG_TOUCH=y +CONFIG_TOUCH_CST816=y + +CONFIG_ACCEL_LIS2DW12=y +CONFIG_ACCEL_LIS2DW12_WK_DUR_DEFAULT=1 +CONFIG_ACCEL_LIS2DW12_WK_THS_MIN=1 +CONFIG_ACCEL_LIS2DW12_WK_THS_MAX=40 +CONFIG_ACCEL_LIS2DW12_WK_THS_DEFAULT=16 +CONFIG_ACCEL_LIS2DW12_SCALE_MG=4000 +CONFIG_ACCEL_LIS2DW12_FIFO_THRESHOLD=32 +CONFIG_MAG=y +CONFIG_MAG_MMC5603NJ=y + +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_SF32LB=y + +CONFIG_EXTI=y +CONFIG_EXTI_SF32LB=y + +CONFIG_GPIO=y +CONFIG_GPIO_SF32LB=y + +CONFIG_PWM=y +CONFIG_PWM_SF32LB=y + +CONFIG_RTC=y +CONFIG_RTC_SF32LB=y + +CONFIG_RNG=y +CONFIG_RNG_SF32LB=y + +CONFIG_CPUMODE=y +CONFIG_CPUMODE_SF32LB=y + +CONFIG_ACCEL_SENSITIVITY=y +CONFIG_APP_SCALING=y +CONFIG_ORIENTATION_MANAGER=y +CONFIG_MODDABLE_XS=y diff --git a/boards/getafix/defconfig.dvt b/boards/getafix/defconfig.dvt new file mode 100644 index 0000000000..54ba3d0c27 --- /dev/null +++ b/boards/getafix/defconfig.dvt @@ -0,0 +1,3 @@ +# getafix dvt board revision configuration + +CONFIG_VIBE_AW8623X=y diff --git a/boards/getafix/defconfig.dvt2 b/boards/getafix/defconfig.dvt2 new file mode 100644 index 0000000000..a7a4262aea --- /dev/null +++ b/boards/getafix/defconfig.dvt2 @@ -0,0 +1,3 @@ +# getafix dvt2 board revision configuration + +CONFIG_VIBE_AW86225=y diff --git a/boards/getafix/defconfig.evt b/boards/getafix/defconfig.evt new file mode 100644 index 0000000000..2a234d2a3b --- /dev/null +++ b/boards/getafix/defconfig.evt @@ -0,0 +1,4 @@ +# getafix evt board revision configuration + +CONFIG_VIBE_AW8623X=y +CONFIG_ACCEL_LIS2DW12_DISABLE_ADDR_PULLUP=y diff --git a/boards/getafix/getafix.yml b/boards/getafix/getafix.yml new file mode 100644 index 0000000000..61173cc357 --- /dev/null +++ b/boards/getafix/getafix.yml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +revisions: + - evt + - dvt + - dvt2 + +runners: + - sftool diff --git a/boards/obelix/Kconfig b/boards/obelix/Kconfig new file mode 100644 index 0000000000..0125fa99f2 --- /dev/null +++ b/boards/obelix/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_OBELIX + bool + default y + select BOARD_FAMILY_OBELIX diff --git a/boards/obelix/Kconfig.bb2 b/boards/obelix/Kconfig.bb2 new file mode 100644 index 0000000000..6c45a09ebe --- /dev/null +++ b/boards/obelix/Kconfig.bb2 @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_OBELIX_BB2 + bool + default y diff --git a/boards/obelix/Kconfig.defconfig b/boards/obelix/Kconfig.defconfig new file mode 100644 index 0000000000..1f34714f76 --- /dev/null +++ b/boards/obelix/Kconfig.defconfig @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FAMILY_NAME + default "obelix" diff --git a/boards/obelix/Kconfig.dvt b/boards/obelix/Kconfig.dvt new file mode 100644 index 0000000000..c7b31f6d38 --- /dev/null +++ b/boards/obelix/Kconfig.dvt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_OBELIX_DVT + bool + default y diff --git a/boards/obelix/Kconfig.pvt b/boards/obelix/Kconfig.pvt new file mode 100644 index 0000000000..3854bce530 --- /dev/null +++ b/boards/obelix/Kconfig.pvt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_OBELIX_PVT + bool + default y diff --git a/boards/obelix/defconfig b/boards/obelix/defconfig new file mode 100644 index 0000000000..969caf4de5 --- /dev/null +++ b/boards/obelix/defconfig @@ -0,0 +1,93 @@ +# obelix board configuration + +CONFIG_SOC_SF32LB52=y + +CONFIG_HAL_SIFLI=y +CONFIG_HAL_SIFLI_SF32LB52=y + +CONFIG_PLATFORM_EMERY=y + +CONFIG_PBLBOOT=y + +CONFIG_SCREEN_COLOR_DEPTH_BITS_8=y + +CONFIG_I2C=y +CONFIG_I2C_SF32LB=y + +CONFIG_UART=y +CONFIG_UART_SF32LB=y + +CONFIG_BACKLIGHT=y +CONFIG_BACKLIGHT_AW2016=y +CONFIG_VIBE=y +CONFIG_VIBE_AW86225=y + +CONFIG_PMIC=y +CONFIG_PMIC_NPM1300=y + +CONFIG_AMBIENT_LIGHT=y +CONFIG_AMBIENT_LIGHT_W1160=y + +CONFIG_MIC=y +CONFIG_MIC_SF32LB=y + +CONFIG_SPEAKER=y +CONFIG_SPEAKER_SF32LB=y + +CONFIG_TEMPERATURE=y +CONFIG_TEMPERATURE_SF32LB=y + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_JDI_SF32LB=y + +CONFIG_FLASH=y +CONFIG_FLASH_GD25Q256E=y + +CONFIG_OTP=y +CONFIG_OTP_FLASH=y + +CONFIG_BATTERY=y +CONFIG_BATTERY_PMIC=y + +CONFIG_TOUCH=y +CONFIG_TOUCH_CST816=y + +CONFIG_HRM=y +CONFIG_HRM_GH3X2X=y + +CONFIG_ACCEL_LIS2DW12=y +CONFIG_ACCEL_LIS2DW12_WK_DUR_DEFAULT=1 +CONFIG_ACCEL_LIS2DW12_WK_THS_MIN=1 +CONFIG_ACCEL_LIS2DW12_WK_THS_MAX=40 +CONFIG_ACCEL_LIS2DW12_WK_THS_DEFAULT=16 +CONFIG_ACCEL_LIS2DW12_SCALE_MG=4000 +CONFIG_ACCEL_LIS2DW12_FIFO_THRESHOLD=32 +CONFIG_MAG=y +CONFIG_MAG_MMC5603NJ=y + +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_SF32LB=y + +CONFIG_EXTI=y +CONFIG_EXTI_SF32LB=y + +CONFIG_GPIO=y +CONFIG_GPIO_SF32LB=y + +CONFIG_PWM=y +CONFIG_PWM_SF32LB=y + +CONFIG_RTC=y +CONFIG_RTC_SF32LB=y + +CONFIG_RNG=y +CONFIG_RNG_SF32LB=y + +CONFIG_CPUMODE=y +CONFIG_CPUMODE_SF32LB=y + +CONFIG_DYNAMIC_BACKLIGHT=y +CONFIG_ACCEL_SENSITIVITY=y +CONFIG_APP_SCALING=y +CONFIG_ORIENTATION_MANAGER=y +CONFIG_MODDABLE_XS=y diff --git a/boards/obelix/defconfig.bb2 b/boards/obelix/defconfig.bb2 new file mode 100644 index 0000000000..67d9f178d4 --- /dev/null +++ b/boards/obelix/defconfig.bb2 @@ -0,0 +1,4 @@ +# obelix bb2 board revision configuration + +CONFIG_IS_BIGBOARD=y +CONFIG_ACCEL_LIS2DW12_DISABLE_ADDR_PULLUP=y diff --git a/boards/obelix/defconfig.dvt b/boards/obelix/defconfig.dvt new file mode 100644 index 0000000000..172b709209 --- /dev/null +++ b/boards/obelix/defconfig.dvt @@ -0,0 +1,3 @@ +# obelix dvt board revision configuration + +CONFIG_ACCEL_LIS2DW12_DISABLE_ADDR_PULLUP=y diff --git a/boards/obelix/defconfig.pvt b/boards/obelix/defconfig.pvt new file mode 100644 index 0000000000..7a2a0e5baf --- /dev/null +++ b/boards/obelix/defconfig.pvt @@ -0,0 +1 @@ +# obelix pvt board revision configuration diff --git a/boards/obelix/obelix.yml b/boards/obelix/obelix.yml new file mode 100644 index 0000000000..22af6a4da3 --- /dev/null +++ b/boards/obelix/obelix.yml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +revisions: + - bb2 + - dvt + - pvt + +runners: + - sftool diff --git a/boards/qemu_emery/Kconfig b/boards/qemu_emery/Kconfig new file mode 100644 index 0000000000..3668d9d46d --- /dev/null +++ b/boards/qemu_emery/Kconfig @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_QEMU_EMERY + bool + default y diff --git a/boards/qemu_emery/Kconfig.defconfig b/boards/qemu_emery/Kconfig.defconfig new file mode 100644 index 0000000000..30e26a7faf --- /dev/null +++ b/boards/qemu_emery/Kconfig.defconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FAMILY_NAME + default "qemu_emery" + +config QEMU_MACHINE + default "pebble-emery" diff --git a/boards/qemu_emery/defconfig b/boards/qemu_emery/defconfig new file mode 100644 index 0000000000..8241ae5fe6 --- /dev/null +++ b/boards/qemu_emery/defconfig @@ -0,0 +1,60 @@ +# qemu_emery board configuration + +CONFIG_SOC_QEMU=y +CONFIG_CORTEX_M33=y + +CONFIG_PLATFORM_EMERY=y + +CONFIG_SCREEN_COLOR_DEPTH_BITS_8=y + +CONFIG_UART=y +CONFIG_UART_QEMU=y + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_QEMU=y + +CONFIG_FLASH=y +CONFIG_FLASH_QEMU=y + +CONFIG_BATTERY=y +CONFIG_BATTERY_QEMU=y + +CONFIG_TOUCH=y +CONFIG_TOUCH_QEMU=y + +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_QEMU=y + +CONFIG_EXTI=y +CONFIG_EXTI_QEMU=y + +CONFIG_BACKLIGHT=y +CONFIG_BACKLIGHT_QEMU=y +CONFIG_BACKLIGHT_QEMU_COLOR=y + +CONFIG_GPIO=y +CONFIG_GPIO_QEMU=y + +CONFIG_PWM=y +CONFIG_PWM_QEMU=y + +CONFIG_RTC=y +CONFIG_RTC_QEMU=y + +CONFIG_RNG=y +CONFIG_RNG_STUB=y + +CONFIG_TEMPERATURE=y +CONFIG_TEMPERATURE_STUB=y + +CONFIG_CPUMODE=y +CONFIG_CPUMODE_STUB=y + +CONFIG_MIC=y +CONFIG_MIC_QEMU=y + +CONFIG_SPEAKER=y +CONFIG_SPEAKER_QEMU=y + +CONFIG_APP_SCALING=y +CONFIG_MODDABLE_XS=y diff --git a/boards/qemu_flint/Kconfig b/boards/qemu_flint/Kconfig new file mode 100644 index 0000000000..9325595309 --- /dev/null +++ b/boards/qemu_flint/Kconfig @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_QEMU_FLINT + bool + default y diff --git a/boards/qemu_flint/Kconfig.defconfig b/boards/qemu_flint/Kconfig.defconfig new file mode 100644 index 0000000000..5243dba93d --- /dev/null +++ b/boards/qemu_flint/Kconfig.defconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FAMILY_NAME + default "qemu_flint" + +config QEMU_MACHINE + default "pebble-flint" diff --git a/boards/qemu_flint/defconfig b/boards/qemu_flint/defconfig new file mode 100644 index 0000000000..64989eabd7 --- /dev/null +++ b/boards/qemu_flint/defconfig @@ -0,0 +1,53 @@ +# qemu_flint board configuration + +CONFIG_SOC_QEMU=y +CONFIG_CORTEX_M4=y + +CONFIG_PLATFORM_FLINT=y + +CONFIG_SCREEN_COLOR_DEPTH_BITS_1=y + +CONFIG_UART=y +CONFIG_UART_QEMU=y + +CONFIG_BACKLIGHT=y +CONFIG_BACKLIGHT_QEMU=y + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_QEMU=y + +CONFIG_FLASH=y +CONFIG_FLASH_QEMU=y + +CONFIG_BATTERY=y +CONFIG_BATTERY_QEMU=y + +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_QEMU=y + +CONFIG_EXTI=y +CONFIG_EXTI_QEMU=y + +CONFIG_GPIO=y +CONFIG_GPIO_QEMU=y + +CONFIG_PWM=y +CONFIG_PWM_QEMU=y + +CONFIG_RTC=y +CONFIG_RTC_QEMU=y + +CONFIG_RNG=y +CONFIG_RNG_STUB=y + +CONFIG_TEMPERATURE=y +CONFIG_TEMPERATURE_STUB=y + +CONFIG_CPUMODE=y +CONFIG_CPUMODE_STUB=y + +CONFIG_MIC=y +CONFIG_MIC_QEMU=y + +CONFIG_SPEAKER=y +CONFIG_SPEAKER_QEMU=y diff --git a/boards/qemu_gabbro/Kconfig b/boards/qemu_gabbro/Kconfig new file mode 100644 index 0000000000..08e42953c7 --- /dev/null +++ b/boards/qemu_gabbro/Kconfig @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_QEMU_GABBRO + bool + default y diff --git a/boards/qemu_gabbro/Kconfig.defconfig b/boards/qemu_gabbro/Kconfig.defconfig new file mode 100644 index 0000000000..8ca0e56bff --- /dev/null +++ b/boards/qemu_gabbro/Kconfig.defconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FAMILY_NAME + default "qemu_gabbro" + +config QEMU_MACHINE + default "pebble-gabbro" diff --git a/boards/qemu_gabbro/defconfig b/boards/qemu_gabbro/defconfig new file mode 100644 index 0000000000..7ded6692d4 --- /dev/null +++ b/boards/qemu_gabbro/defconfig @@ -0,0 +1,56 @@ +# qemu_gabbro board configuration + +CONFIG_SOC_QEMU=y +CONFIG_CORTEX_M33=y + +CONFIG_PLATFORM_GABBRO=y + +CONFIG_SCREEN_COLOR_DEPTH_BITS_8=y + +CONFIG_UART=y +CONFIG_UART_QEMU=y + +CONFIG_BACKLIGHT=y +CONFIG_BACKLIGHT_QEMU=y + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_QEMU=y + +CONFIG_FLASH=y +CONFIG_FLASH_QEMU=y + +CONFIG_BATTERY=y +CONFIG_BATTERY_QEMU=y + +CONFIG_TOUCH=y +CONFIG_TOUCH_QEMU=y + +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_QEMU=y + +CONFIG_EXTI=y +CONFIG_EXTI_QEMU=y + +CONFIG_GPIO=y +CONFIG_GPIO_QEMU=y + +CONFIG_PWM=y +CONFIG_PWM_QEMU=y + +CONFIG_RTC=y +CONFIG_RTC_QEMU=y + +CONFIG_RNG=y +CONFIG_RNG_STUB=y + +CONFIG_TEMPERATURE=y +CONFIG_TEMPERATURE_STUB=y + +CONFIG_CPUMODE=y +CONFIG_CPUMODE_STUB=y + +CONFIG_MIC=y +CONFIG_MIC_QEMU=y + +CONFIG_APP_SCALING=y +CONFIG_MODDABLE_XS=y diff --git a/checkers/MutexChecker.cpp b/checkers/MutexChecker.cpp deleted file mode 100644 index b7f65d6b7f..0000000000 --- a/checkers/MutexChecker.cpp +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" - -using namespace clang; -using namespace ento; - -namespace std { - void terminate( void ) _NOEXCEPT { - abort(); - } -} - -/* This analyzer suffers from the major limitation that most of the mutexes in Pebble are globals, - * so all symbols and MemRegions refering to the mutexes are invalidated every time an unknown - * function is called. This analyzer instead associates mutexes with the declaration of their - * variables, which has the obvious limitation of not catching when mutexes are passed as - * arguments (which fortunately never? happens in pebble). - */ - -class MutexState { - private: - bool locked; - bool recursive; - unsigned lockCount; - public: - MutexState(bool isLocked, bool isRecursive, unsigned startCount) - : - locked(isLocked), - recursive(isRecursive), - lockCount(startCount) - {} - - MutexState getLocked() const { - if (recursive) { - if (locked) { - // Preserve the first lock function (it should be the last one to unlock) - return MutexState(true, true, lockCount + 1); - } - else { - return MutexState(true, true, lockCount + 1); - } - } - else { - return MutexState(true, false, 0); - } - } - - MutexState getUnlocked(void) const { - if (recursive) { - // If lockCount is one, we unlock - return MutexState(lockCount > 1, true, lockCount - 1); - } - else { - return MutexState(false, false, 0); - } - } - - bool isLocked(void) const { - return locked; - } - - bool isRecursive(void) const { - return recursive; - } - - bool operator==(const MutexState &other) const { - return locked == other.locked && recursive == other.recursive && - lockCount == other.lockCount; - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddBoolean(locked); - ID.AddBoolean(recursive); - ID.AddInteger(lockCount); - } -}; - -// Map mutex declarations to state info -REGISTER_MAP_WITH_PROGRAMSTATE(MutexMap, const Decl *, MutexState); - -// Hold an ordered list of the mutexes to catch lock order reversal -REGISTER_LIST_WITH_PROGRAMSTATE(MutexList, const Decl *); - -namespace { - class MutexChecker : public Checker { - std::unique_ptr NoUnlockBugType; - std::unique_ptr DoubleLockBugType; - std::unique_ptr DoubleUnlockBugType; - std::unique_ptr TooManyUnlocksBugType; - std::unique_ptr UnlockNoLockBugType; - std::unique_ptr LockReversalBugType; - - void reportError(const std::unique_ptr &bugType, StringRef msg, CheckerContext &C) const { - ExplodedNode *endNode = C.generateSink(); - if (!endNode) { - return; - } - BugReport *bug = new BugReport(*bugType, msg, endNode); - C.emitReport(bug); - } - - ProgramStateRef lockMutex(const Decl *mutexDecl, const MutexState *curMutex, - ProgramStateRef state, bool recursive = false) const { - - state = state->add(mutexDecl); - if (curMutex) { - MutexState lockedMutex = curMutex->getLocked(); - return state->set(mutexDecl, lockedMutex); - } - else { - MutexState newMutex(true, recursive, recursive ? 1 : 0); - return state->set(mutexDecl, newMutex); - } - } - - const Decl * getMutexDecl(const Expr *argExpr) const { - const Expr *strippedExpr = argExpr->IgnoreParenCasts(); - const DeclRefExpr *ref = dyn_cast(strippedExpr); - if (ref) { - return ref->getDecl(); - } - // If it wasn't a DeclRef maybe it was a member? - const MemberExpr *member = dyn_cast(strippedExpr); - if (member) { - return member->getMemberDecl(); - } - - return NULL; - } - - void handleLock(StringRef funcName, const CallEvent &call, CheckerContext &C) const { - const Decl *mutexDecl = getMutexDecl(call.getArgExpr(0)); - - if (!mutexDecl) { - return; - } - - ProgramStateRef state = C.getState(); - - const MutexState *curMutex = state->get(mutexDecl); - - if (funcName.equals("mutex_lock") || funcName.equals("mutex_lock_with_lr")) { - if (curMutex) { - if (curMutex->isLocked()) { - reportError(DoubleLockBugType, "This lock was already locked", C); - return; - } - } - state = lockMutex(mutexDecl, curMutex, state); - C.addTransition(state); - } - else if (funcName.equals("mutex_lock_with_timeout")) { - if (curMutex) { - if (curMutex->isLocked()) { - reportError(DoubleLockBugType, "This lock was already locked", C); - return; - } - } - // diverge into two states, one where we get the mutex and one - // where we don't - ProgramStateRef lockedState, timeoutState; - - DefinedSVal retVal = call.getReturnValue().castAs(); - std::tie(lockedState, timeoutState) = state->assume(retVal); - - lockedState = lockMutex(mutexDecl, curMutex, lockedState); - - C.addTransition(lockedState); - C.addTransition(timeoutState); - } - else if (funcName.equals("mutex_lock_recursive")) { - state = lockMutex(mutexDecl, curMutex, state, true); - C.addTransition(state); - } - else if (funcName.equals("mutex_lock_recursive_with_timeout") || - funcName.equals("mutex_lock_recursive_with_timeout_and_lr")) { - ProgramStateRef lockedState, timeoutState; - - DefinedSVal retVal = call.getReturnValue().castAs(); - std::tie(lockedState, timeoutState) = state->assume(retVal); - - lockedState = lockMutex(mutexDecl, curMutex, lockedState, true); - - C.addTransition(lockedState); - C.addTransition(timeoutState); - } - } - - void handleUnlock(StringRef funcName, const CallEvent &call, CheckerContext &C) const { - if (!(funcName.equals("mutex_unlock") || funcName.equals("mutex_unlock_recursive"))) { - return; - } - ProgramStateRef state = C.getState(); - - const Decl *mutexDecl = getMutexDecl(call.getArgExpr(0)); - const MutexState *curMutex = state->get(mutexDecl); - - // If it isn't in the map, we never locked it - if (!curMutex) { - reportError(UnlockNoLockBugType, "Mutex was never locked", C); - return; - } - // If it is in the map but unlocked, it was unlocked twice - if (!curMutex->isLocked()) { - if (curMutex->isRecursive()) { - reportError(TooManyUnlocksBugType, "Recursive mutex already fully unlocked", C); - } - else { - reportError(DoubleUnlockBugType, "Mutex already unlocked", C); - } - return; - } - - const Decl *lastDecl = state->get().getHead(); - - if (mutexDecl != lastDecl) { - reportError(LockReversalBugType, "This was not the most recently acquired lock", C); - return; - } - - state = state->set(state->get().getTail()); - state = state->set(mutexDecl, curMutex->getUnlocked()); - C.addTransition(state); - } - - public: - MutexChecker(void) - : NoUnlockBugType(new BugType(this, "Failure to call unlock", "Pebble Mutex Plugin")), - DoubleLockBugType(new BugType(this, "Double Lock", "Pebble Mutex Plugin")), - DoubleUnlockBugType(new BugType(this, "Double Unlock", "Pebble Mutex Plugin")), - TooManyUnlocksBugType(new BugType(this, "More unlocks than locks", "Pebble Mutex Plugin")), - UnlockNoLockBugType(new BugType(this, "Unlock called before lock", "Pebble Mutex Plugin")), - LockReversalBugType(new BugType(this, "Lock order reversal", "Pebble Mutex Plugin")) - {} - - void checkPostCall(const CallEvent &call, CheckerContext &C) const { - const IdentifierInfo *identInfo = call.getCalleeIdentifier(); - if(!identInfo) { - return; - } - StringRef funcName = identInfo->getName(); - if (funcName.startswith("mutex_lock")) { - handleLock(funcName, call, C); - } - else if (funcName.startswith("mutex_unlock")) { - handleUnlock(funcName, call, C); - } - } - - void checkEndFunction(CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - if (C.inTopFrame()) { - // This path ends once this function ends - for (auto mutexPair : state->get()) { - if (mutexPair.second.isLocked()) { - reportError(NoUnlockBugType, "Mutex still locked at end of path", C); - return; - } - } - } - } - }; -} - -extern "C" const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING; - -extern "C" void clang_registerCheckers(CheckerRegistry ®istry) { - registry.addChecker("pebble.MutexChecker", "Checker for use of mutex_lock()/mutex_unlock()"); -} diff --git a/checkers/SyscallSecurityChecker.cpp b/checkers/SyscallSecurityChecker.cpp deleted file mode 100644 index 396c2db6ee..0000000000 --- a/checkers/SyscallSecurityChecker.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSet.h" - -using namespace clang; -using namespace ento; - -namespace std { - void terminate( void ) _NOEXCEPT { - abort(); - } -} - -// Need to specialize for any custom types used in traits -// Look in include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h -namespace clang { - namespace ento { - template <> struct ProgramStatePartialTrait { - typedef const FunctionDecl * data_type; - - static inline data_type MakeData(void *const* p) { - return p ? (const FunctionDecl *)*p : data_type(); - } - - static inline void *MakeVoidPtr(data_type d) { - return const_cast(d); - } - }; - } -} - -/* Ultimately this would work better / be more thorough if it made use of the Analyzer's - * taint checking, but there is no infrastructure to remove taint at the moment. - */ - -REGISTER_TRAIT_WITH_PROGRAMSTATE(CurrentSyscallState, const FunctionDecl *); -// Args are tracked by their MemRegion -REGISTER_SET_WITH_PROGRAMSTATE(TaintedArgsState, const MemRegion *); - -namespace { - class SyscallSecurityChecker : - public Checker< eval::Call, check::PreCall, check::Location, check::Bind, check::EndFunction > { - - std::unique_ptr NoBoundsCheckBugType; - std::unique_ptr UnsafeCallBugType; - - llvm::StringSet<> unsafeFunctions {}; - - const FunctionDecl * getCurrentSyscall(const ProgramStateRef state) const { - return state->get(); - } - - bool inSyscall(const ProgramStateRef state) const { - return !!getCurrentSyscall(state); - } - - ProgramStateRef setCurrentSyscall(const ProgramStateRef state, const FunctionDecl *FD) const { - return state->set(FD); - } - - bool isValTainted(const SVal &arg, const ProgramStateRef state) const { - const MemRegion *MR = arg.getAsRegion(); - if (!MR) { - return false; - } - const MemRegion *baseMR = MR->getBaseRegion(); - - return state->contains(baseMR); - } - - void reportUnsanitizedUse(const SVal &arg, const ProgramStateRef state, CheckerContext &C) const { - ExplodedNode *errNode = C.generateSink(); - if (!errNode) { - // Already reported an error here - return; - } - BugReport *R = new BugReport(*NoBoundsCheckBugType, - "Used an unsanitized argument from syscall", errNode); - R->markInteresting(arg); - C.emitReport(R); - } - - public: - SyscallSecurityChecker(void) - : NoBoundsCheckBugType(new BugType(this, "Failed to check bounds", "Pebble Syscall Plugin")), - UnsafeCallBugType(new BugType(this, "Syscall used dangerous function", "Pebble Syscall Plugin")) - { - StringRef funcs[] = { "task_malloc", "task_zalloc", "task_calloc", "app_malloc", "app_zalloc", "app_calloc" }; - - // It would be more efficient to look up the IdentifierInfos for each of these and compare against that - for (StringRef func : funcs) { - unsafeFunctions.insert(func); - } - } - - bool evalCall(const CallExpr *call, CheckerContext &C) const { - if (!C.getCalleeName(call).equals("syscall_internal_elevate_privilege")) { - return false; - } - // Always return true from syscall_internal_elevate_privilege - // so the analyzer always thinks privileges have been elevated - - ProgramStateRef state = C.getState(); - - SVal ret = C.getSValBuilder().makeTruthVal(true); - state = state->BindExpr(call, C.getLocationContext(), ret); - - C.addTransition(state); - return true; - } - - void checkPreCall(const CallEvent &call, CheckerContext &C) const { - const IdentifierInfo *identInfo = call.getCalleeIdentifier(); - if(!identInfo) { - return; - } - StringRef funcName = identInfo->getName(); - - ProgramStateRef state = C.getState(); - - if (funcName.equals("syscall_internal_elevate_privilege")) { - const LocationContext *LCtx = C.getLocationContext(); - const FunctionDecl *FD = dyn_cast(LCtx->getDecl()); - if (!FD) { - llvm::errs() << "Privileges elevated outside of function?\n"; - return; - } - - ExplodedNode *pred = NULL; - - // If we're not at the top level, we generate two new transitions, one for the current syscall - // executing normally, and one which simulates execution starting at this syscall. - // This is important, because if a syscall is called by another function, the syscall - // will not be treated as an entry point by the analyzer. - - if (!C.inTopFrame()) { - C.addTransition(state); - state = C.getStateManager().getInitialState(LCtx); - // Get the first node in the state graph - pred = C.getPredecessor(); - while (pred->getFirstPred()) { - pred = pred->getFirstPred(); - } - } - - for (unsigned i = 0; i < FD->getNumParams(); i++) { - // We only care about tracking pointer arguments - const ParmVarDecl *ParamDecl = FD->getParamDecl(i); - if (ParamDecl->getType()->isPointerType()) { - // Find the MemRegion associated with the parameter. - // Seems very roundabout, but it works... - // Remember to look at state->getRegion - Loc lValue = state->getLValue(ParamDecl, LCtx); - SVal valRegion = state->getSVal(lValue); - if (valRegion == UnknownVal()) { - llvm::errs() << "Failed to get argument SymbolRef\n"; - continue; - } - const MemRegion *MR = valRegion.getAsRegion(); - if (!MR) { - llvm::errs() << "No region for ptr argument\n"; - continue; - } - state = state->add(MR); - } - } - state = setCurrentSyscall(state, FD); - C.addTransition(state, pred, nullptr); - } - else if (inSyscall(state)) { - if (funcName.equals("syscall_assert_userspace_buffer")) { - const MemRegion *MR = call.getArgSVal(0).getAsRegion(); - - state = state->remove(MR); - } - else if (funcName.equals("memory_layout_is_cstring_in_region") || - funcName.equals("memory_layout_is_pointer_in_region")) { - const MemRegion *MR = call.getArgSVal(1).getAsRegion(); - - state = state->remove(MR); - } - // Make sure the syscall isn't calling an unsafe function - else if (unsafeFunctions.count(funcName)) { - ExplodedNode *errNode = C.generateSink(); - if (!errNode) { - // Already reported an error here - return; - } - BugReport *R = new BugReport(*UnsafeCallBugType, - "This function shouldn't be called from privileged code", errNode); - C.emitReport(R); - return; - } - else { // Any other function, just want to make sure it isn't getting the unsanitized args - for (unsigned i = 0; i < call.getNumArgs(); i++) { - SVal argVal = call.getArgSVal(i); - if (isValTainted(argVal, state)) { - reportUnsanitizedUse(argVal, state, C); - return; - } - } - } - C.addTransition(state); - } - } - - void checkLocation(SVal loc, bool isLoad, const Stmt *S, CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - if (isValTainted(loc, state)) { - reportUnsanitizedUse(loc, state, C); - } - } - - void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - if (isValTainted(val, state)) { - reportUnsanitizedUse(val, state, C); - } - } - - void checkEndFunction(CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - const Decl *D = C.getLocationContext()->getDecl(); - const FunctionDecl *FD = dyn_cast(D); - if (!FD) { - // Not sure why this would ever be the case... - llvm::errs() << "Path ended outside of function?\n"; - return; - } - - if (FD != getCurrentSyscall(state)) { - return; - } - - // Since we are effectively emulating every syscall as an entry point from - // the analyzer's perspective, once the syscall is done, end the path. - C.generateSink(); - } - }; -} - -extern "C" const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING; - -extern "C" void clang_registerCheckers(CheckerRegistry ®istry) { - registry.addChecker("pebble.SyscallSecurityChecker", "Checker that makes sure pointer arguments to syscalls are sanitized"); -} diff --git a/checkers/test-programs/mutex-test.c b/checkers/test-programs/mutex-test.c deleted file mode 100644 index 8964497ea2..0000000000 --- a/checkers/test-programs/mutex-test.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -struct Mutex; -struct RecursiveMutex; - -typedef struct Mutex * restrict mutex_t; -typedef struct RecursiveMutex * recursive_mutex_t; - -extern void mutex_lock(mutex_t); -extern void mutex_unlock(mutex_t); - -extern bool mutex_lock_with_timeout(mutex_t); - -extern void mutex_lock_recursive(recursive_mutex_t); -extern void mutex_unlock_recursive(recursive_mutex_t); - -static mutex_t global_lock = 0; -static mutex_t global_lock2; -mutex_t recursive_lock; - -void nounlock() { - mutex_lock(global_lock); -} - -void nolock() { - mutex_unlock(global_lock); -} - -void normal() { - mutex_lock(global_lock); - mutex_unlock(global_lock); -} - -struct handle { - mutex_t m; -} m_wrapper; - -extern int do_stuff(struct handle * h); - -void structthing(struct handle * h) { - mutex_lock(h->m); - do_stuff(h); - mutex_unlock(h->m); -} - -extern int do_stuff2(); - -void stuff() { - mutex_lock(global_lock); - - do_stuff2(); - - mutex_unlock(global_lock); -} - -void stuff2() { - mutex_lock(m_wrapper.m); - - do_stuff2(); - - mutex_unlock(m_wrapper.m); -} - -void nest2() { - mutex_lock(global_lock); - printf("blah %p", global_lock); - mutex_unlock(global_lock); -} - -void nest() { - nest2(); -} - -void cond(void *glob_ptr) { - mutex_lock(global_lock); - - while (glob_ptr) { - printf("blah %p", glob_ptr); - } - - mutex_unlock(global_lock); -} - -void timeout() { - mutex_lock_with_timeout(global_lock); - - mutex_unlock(global_lock); -} - -void good_timeout() { - if (mutex_lock_with_timeout(global_lock)) { - mutex_unlock(global_lock); - } -} - -void stupid_timeout() { - if (!mutex_lock_with_timeout(global_lock)) { - mutex_unlock(global_lock); - } -} - -void reversal() { - mutex_lock(global_lock); - mutex_lock(global_lock2); - - mutex_unlock(global_lock); - mutex_unlock(global_lock2); -} - - -// Trying to repro the false positives unsuccessfully... -extern bool decision(); - -inline void __attribute__((always_inline)) locker() { - mutex_lock(global_lock); -} - -inline void __attribute__((always_inline)) unlocker() { - mutex_unlock(global_lock); -} - -static inline void __attribute__((always_inline)) lock_wrap() { - locker(); - if (decision()) { - unlocker(); - } -} - -static inline void __attribute__((always_inline)) unlock_wrap() { - unlocker(); -} - -void lockme() { - lock_wrap(); - unlock_wrap(); -} diff --git a/checkers/test-programs/syscall-test.c b/checkers/test-programs/syscall-test.c deleted file mode 100644 index 29d8b2343b..0000000000 --- a/checkers/test-programs/syscall-test.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -extern bool syscall_internal_elevate_privilege(); -extern void syscall_assert_userspace_buffer(const void * check_buffer, int size); - -extern void * app_malloc(unsigned size); - -void do_stuff(void * buffer, int size) { - strncpy(buffer, "Woooooooo", size); -} - -void good_syscall(void * buffer, int size) { - syscall_internal_elevate_privilege(); - - syscall_assert_userspace_buffer(buffer, size); - - do_stuff(buffer, size); -} - -void bad_syscall(void * buffer, int size) { - syscall_internal_elevate_privilege(); - do_stuff(buffer, size); -} - -void stupid_syscall(void * buffer, int size) { - void * stupid = (char *)buffer + 1; - syscall_internal_elevate_privilege(); - do_stuff(stupid, size); -} - -void not_syscall(void * buffer, int size) { - do_stuff(buffer, size); -} - -void nested_syscall(void * buffer, int size) { - syscall_internal_elevate_privilege(); - syscall_assert_userspace_buffer(buffer, size); - bad_syscall(buffer, size); - good_syscall(buffer, size); -} - -void bad_nested_syscall(void * buffer, int size) { - syscall_internal_elevate_privilege(); - bad_syscall(buffer, size); -} - -void hidden_bad_syscall(void * buffer, int size) { - syscall_internal_elevate_privilege(); - do_stuff(buffer, size); -} - -void if_syscall(void * buffer, int size) { - if (syscall_internal_elevate_privilege()) { - syscall_assert_userspace_buffer(buffer, size); - } - do_stuff(buffer, size); -} - -void wrapper() { - void * buffer = NULL; - int size = 0; - - good_syscall(buffer, size); - // This tests to make sure analysis continues through good_syscall - hidden_bad_syscall(buffer, size); -} - -bool cond(const char *font_key) { - return &cond == font_key; -} - -void conditional_syscall(const char *font_key) { - syscall_internal_elevate_privilege(); - - if (font_key) { - if (!cond(font_key)) { - do_stuff(font_key, 5); - } - } -} - -void store_syscall(char * buf, int size) { - syscall_internal_elevate_privilege(); - - buf[0] = 'a'; - char * new = buf; - - do_stuff(new, size); - -} - -void load_syscall(char * buf, int size) { - syscall_internal_elevate_privilege(); - - char test = buf[0]; - - do_stuff(&test, size); -} - -void bind_syscall(char * buf, int size) { - syscall_internal_elevate_privilege(); - - char * new = buf; - - do_stuff(new, size); -} - -void malloc_syscall() { - syscall_internal_elevate_privilege(); - void *buf = app_malloc(5); - - syscall_assert_userspace_buffer(buf, 5); - - do_stuff(buf, 5); -} diff --git a/checkers/wscript b/checkers/wscript deleted file mode 100644 index 7614f78ebb..0000000000 --- a/checkers/wscript +++ /dev/null @@ -1,70 +0,0 @@ -from os import path - -def options(opt): - opt.load('compiler_cxx') - opt.add_option('--checker', action='store', default='all') - -def configure(conf): - conf.env.CXX = 'clang++' - conf.load('compiler_cxx') - - conf.env.append_value('DEFINES', ['__STDC_CONSTANT_MACROS', - '__STDC_LIMIT_MACROS']) - - conf.check_cfg(msg='Checking for llvm config', - path='llvm-config', - package='', - args='--cxxflags --ldflags --libs --system-libs', - uselib_store='LLVM') - - clang_libs = ['clang', - 'clangARCMigrate', - 'clangAST', - 'clangASTMatchers', - 'clangAnalysis', - 'clangApplyReplacements', - 'clangBasic', - 'clangCodeGen', - 'clangDriver', - 'clangDynamicASTMatchers', - 'clangEdit', - 'clangFormat', - 'clangFrontend', - 'clangFrontendTool', - 'clangIndex', - 'clangLex', - 'clangParse', - 'clangQuery', - 'clangRename', - 'clangRewrite', - 'clangRewriteFrontend', - 'clangSema', - 'clangSerialization', - 'clangStaticAnalyzerCheckers', - 'clangStaticAnalyzerCore', - 'clangStaticAnalyzerFrontend', - 'clangTooling', - 'clangToolingCore'] - - conf.check_cxx(msg='Checking for clang++', - uselib_store='CLANG', - use=['LLVM'], - lib=clang_libs) - -def build(bld): - checkers = [] - - if bld.options.checker == 'all': - checkers = bld.path.ant_glob('*.cpp') - else: - checkers = [ bld.path.make_node(bld.options.checker) ] - - for checker in checkers: - source = [ checker ] - target = checker.change_ext('.dylib') - bld.shlib(source=source, - target=target, - use=['CLANG', 'LLVM'], - cppflags=['-fno-rtti', '-std=c++11', '-fPIC']) - -# vim:filetype=python diff --git a/docs/Makefile b/docs/Makefile index 2852d7aa6d..002375c331 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,16 +1,5 @@ -# Copyright 2025 Core Devices LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2025 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 # Minimal makefile for Sphinx documentation # diff --git a/docs/conf.py b/docs/conf.py index 189d7026c1..d68b29fccf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,16 +1,5 @@ -# Copyright 2025 Core Devices LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2025 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 # Configuration file for the Sphinx documentation builder. # diff --git a/docs/development/building_fw.md b/docs/development/building_fw.md new file mode 100644 index 0000000000..125eb7128c --- /dev/null +++ b/docs/development/building_fw.md @@ -0,0 +1,49 @@ +# Building firmware + +Before building, make sure you've configured {doc}`./waf <../development/options>`. Then, run the following: + +```shell +./waf build +``` + +## Loading firmware with a firmware development kit + +Before attempting to flash, check the documentation for each {doc}`board <../boards/index>` on how to prepare and connect your watch for programming. + +You can flash the built firmware by running: + +```shell +./pbl flash +``` + +In some cases, you may have to specify the `--tty $SERIAL_ADAPTER` option where `$SERIAL_ADAPTER` is the path for your serial adapter, e.g. `/dev/ttyACM0`, `/dev/tty.usbmodem1102`, etc. + +If flashing for the first time, you will also need to flash resources. +Some boards support direct resource programming by passing the `--resources` option. +The alternative is to flash while the firmware is running via the serial port using: + +```shell +./pbl image_resources --tty $SERIAL_ADAPTER +``` + +When both firmware and resources are flashed, you should observe the watch booting into the main application. +You can also see the logs by opening the console: + +```shell +./pbl console --tty $SERIAL_ADAPTER +``` + +Try sending `help` to get a list of available console commands. + +## Loading firmware via Bluetooth + +If you don't have a firmware development kit, you may bundle a `.pbz` file for sideloading +onto your sealed watch: + +```shell +./waf bundle +``` + +The resulting `.pbz` file will be located in the `build/` directory. Transfer this file +to the device paired to your watch, then, in the Pebble app, enable `Settings -> Show debug options`. +Go back to the Devices tab, tap your watch, then `Firmware Update Debug -> Sideload FW`, and select the `.pbz` file. diff --git a/docs/development/getting_started.md b/docs/development/getting_started.md new file mode 100644 index 0000000000..071b5430b0 --- /dev/null +++ b/docs/development/getting_started.md @@ -0,0 +1,144 @@ +# Prerequisites + +Follow this guide to: + +- Set up a command-line PebbleOS development environment +- Get the source code + +## PebbleOS SDK + +Install the [PebbleOS SDK](https://github.com/coredevices/PebbleOS-SDK), which +bundles the ARM GNU toolchain, Pebble QEMU, and other tools: + +```shell +curl -LsSf https://github.com/coredevices/PebbleOS-SDK/releases/latest/download/pebbleos-sdk-installer.sh | sh +``` + +## System-level dependencies + +A series of system-level dependencies are required. +Follow the next steps to install them. + +:::::{tab-set} +:sync-group: os + +::::{tab-item} Ubuntu 24.04 LTS +:sync: ubuntu + +1. Update first: + +```shell +sudo apt update +``` + +2. Install required dependencies + +```shell +sudo apt install \ + bison \ + clang \ + flex \ + gcc \ + gcc-multilib \ + gettext \ + git \ + gperf \ + libfreetype6-dev \ + libglib2.0-dev \ + libgtk-3-dev \ + libncurses-dev \ + librsvg2-bin \ + make \ + openocd \ + python3-dev \ + python3-venv +``` + +:::: + +::::{tab-item} Fedora 44 + +1. Upgrade first: + +```shell +sudo dnf upgrade --refresh +``` + +2. Install required dependencies + +```shell +sudo dnf install \ + clang \ + dash \ + freetype-devel \ + gcc \ + glib2-devel \ + gtk3-devel \ + librsvg2-tools \ + nodejs \ + python-devel +``` + +:::: + +::::{tab-item} macOS + +1. Install [brew](https://brew.sh/). + +2. Install dependencies: + +```shell +brew install librsvg python openocd +``` + +3. Link `brew` Python: + +```shell +brew link python@3 +``` + +:::: + +::::: + +## Get the source code + +You can clone the PebbleOS repository by running: + +```shell +git clone --recurse-submodules https://github.com/coredevices/pebbleos +``` + +Once cloned, enter the `pebbleos` directory before continuing: + +```shell +cd pebbleos +``` + +## Python dependencies + +A series of additional Python dependencies are also required. +Follow the next steps to install them in a [Python virtual environment](https://docs.python.org/3/library/venv.html). + +1. Create a new virtual environment: + +```shell +python3 -m venv .venv +``` + +2. Activate the virtual environment: + +```shell +source .venv/bin/activate +``` + +```{tip} +Remember to activate the virtual environment before every time you start working! +``` + +3. Install dependencies + +```shell +pip install -r requirements.txt +``` + diff --git a/docs/development/moddable.md b/docs/development/moddable.md new file mode 100644 index 0000000000..7ae5dc0712 --- /dev/null +++ b/docs/development/moddable.md @@ -0,0 +1,26 @@ +# Moddable JS Engine + +PebbleOS supports the [Moddable SDK](https://github.com/pebble-dev/moddable)'s XS JavaScript engine. +The Moddable integration provides a lightweight sandbox for runningJavaScript applications and watchfaces on Pebble hardware. + +## Building with Moddable + +Moddable is enabled by default on platforms whose board defconfig sets +`CONFIG_MODDABLE_XS=y`. You can override this at configure time by +passing `-DCONFIG_MODDABLE_XS=y` or `-DCONFIG_MODDABLE_XS=n` to +`./waf configure`. PRF (recovery) builds always disable the engine. + +## Installable applications + +The JavaScript is stored in a resource. A small native C application bootstraps JavaScript execution by calling `moddable_createMachine`. + +The JavaScript itself is a Moddable SDK **mod**, which is a precompiled collection of modules. +The modules are compiled using the `mcrun` tool in the Moddable SDK. See Moddable's [Pebble Examples repository](https://github.com/moddable-OpenSource/pebble-examples) for further details. +The Pebble resource containing the precompiled mod is currently loaded into RAM for execution; eventually, it should be executed directly from flash (this is what the Moddable SDK does on most platforms). + +## Sandbox + +The JavaScript in installable applications runs inside a lightweight sandbox, which can limit access to certain JavaScript global variables and modules. + +Installable applications can be either a normal application or a watchface. +The sandbox prevents watchfaces from subscribing to the Pebble hardware buttons (which is slightly redundant since PebbleOS also blocks them — but PebbleOS blocks them silently, whereas the sandbox throws an exception, which is more clear to developers). diff --git a/docs/development/options.md b/docs/development/options.md index e08734faa4..4da1191747 100644 --- a/docs/development/options.md +++ b/docs/development/options.md @@ -1,34 +1,82 @@ # Configuration Options -When configuring the build (`./waf configure ...`) there are several options you can enable or tune. +When configuring the build (`./pbl configure ...`) there are several options you can enable or tune. Below you will find a list of the most relevant ones. +## Choosing your target + +There are a number of target boards to choose from when building PebbleOS. You can do so by +using the (`--board`) flag followed by: + +:`asterix`: (Core Devices) Pebble 2 Duo +:`obelix@bb2`, `obelix@dvt`, `obelix@pvt`: (Core Devices) Pebble Time 2 +:`getafix@evt`, `getafix@dvt`, `getafix@dvt2`: (Core Devices) Pebble Round 2 +:`qemu_emery`, `qemu_flint`, `qemu_gabbro`: dedicated QEMU targets (see {doc}`qemu`) + +Keep in mind that some targets may not currently compile as-is. + +## Variant + +:`--variant`: + Build variant, `normal` (main firmware) or `prf` (recovery firmware). + +## Release build + +:`-DCONFIG_RELEASE=y`: + Build a release-mode firmware. Strips debug aids, enables shipping + defaults (e.g. Memfault crash reporting), and reduces battery usage + compared to a debug build. Pass this to `./waf configure`. + ## Main features -:`--nojs`: - Disable Javascript support +:`-DCONFIG_MODDABLE_XS=y` / `-DCONFIG_MODDABLE_XS=n`: + Force the Moddable SDK's XS JavaScript engine on or off, overriding + the board defconfig. Pass to `./waf configure`. See {doc}`moddable`. + PRF (recovery) builds always disable the engine regardless of this + value. + +## Manufacturing + +:`-DCONFIG_MFG=y`: + Enable manufacturing-only functionality in the PRF build. ## Debugging -:`--nowatchdog`: +:`-DCONFIG_NO_WATCHDOG=y`: Disable watchdog -:`--nostop`: - Disable STOP mode (STM32 specific) +:`-DCONFIG_NOSTOP=y`: + Disable STOP mode -:`--nosleep`: - Disable sleep mode (STM32 specific) +:`-DCONFIG_NOSLEEP=y`: + Disable sleep mode ## Flashing -:`--openocd-jtag`: - Choose alternative flash/debug probe when using OpenOCD runner. +The `flash`, `run` and `debug` commands talk to a connected device through a +*runner*. Each board declares its supported runners in its board manifest +(e.g. `boards//.yml`) and the first one is used by default. For +the OpenOCD runner, the probe and target configuration lives in the board's +`support/openocd.cfg`. + +:`--runner`: + Override the board's default runner for `flash`/`run`/`debug`. + +:`--tty`: + Serial port used by the `sftool` runner. + +:`--resources`: + Also flash system resources alongside the firmware (`sftool` runner). ## Logging -:`--log-level`: - Default log level. +:`-DCONFIG_DEFAULT_LOG_LEVEL_=y`: + Default log level, where `` is one of `ERROR`, `WARNING`, + `INFO`, `DEBUG` (default) or `DEBUG_VERBOSE`. -:`--nohash`: +:`-DCONFIG_LOG_HASHED=n`: Disable log messages hashing. This will increase ROM usage, but will not require a dictionary file to decode logs. + +These and many more options can also be browsed and changed interactively with +`./waf menuconfig` after configuring. diff --git a/docs/development/prf.md b/docs/development/prf.md deleted file mode 100644 index c4fac18cd6..0000000000 --- a/docs/development/prf.md +++ /dev/null @@ -1,40 +0,0 @@ -# PRF - -PRF (Pebble Recovery Firmware) is a special firmware image available for recovery purposes. -It allows connecting from a phone to, for example, flash a new firmware image even if the main firmware is broken or unavailable. - -## Building - -Once a project is configured, PRF image can be built by running: - -```shell -./waf build_prf -``` - -## Flashing - -The PRF image can be flashed directly into the application area by running: - -```shell -./waf flash_prf -``` - -This is useful when developing PRF features because the watch will boot directly to PRF. -To flash it to the external flash, so it can be used regularly, run: - -```shell -./waf image_recovery -``` - -Append `--tty` option if needed. -In such case, PRF will need to be copied to the main flash to run. -This can be done by pressing {kbd}`BACK` + {kbd}`UP` + {kbd}`MIDDLE` when -booting, or holding {kbd}`BACK` for 7-10s while in the main application. - -## Console - -You can interact with PRF console by running: - -```shell -./waf console_prf -``` diff --git a/docs/development/qemu.md b/docs/development/qemu.md index 8ef9c85a24..ff40f28cf7 100644 --- a/docs/development/qemu.md +++ b/docs/development/qemu.md @@ -1,100 +1,38 @@ # QEMU ```{important} -QEMU is only available for STM32 targets +Some platforms have dedicated QEMU board targets: `qemu_emery`, `qemu_flint`, +and `qemu_gabbro`. ``` ## Getting QEMU -The same QEMU binary found in the SDK can be used to build and develop the firmware. -Below you can also find a detailed guide on how to build it from source if you need to do so (e.g. on Apple Silicon). - -### Building from source - -1. Install OS-level pre-requisites: - -:::::{tab-set} -:sync-group: os - -::::{tab-item} Ubuntu 24.04 LTS -:sync: ubuntu - -```shell -sudo apt install autoconf libglib2.0-dev libpixman-1-dev -``` - -:::: - -::::{tab-item} macOS -:sync: macos - -```shell -brew install autoconf glib pixman -``` - -:::: -::::: - -2. Install `pyenv` following [this guide](https://github.com/pyenv/pyenv?tab=readme-ov-file#installation) (steps A-D). -3. Install Python 2.7: - -```shell -pyenv install 2.7 -``` - -4. Activate Python 2.7 on the current shell: - -```shell -pyenv local 2.7 -``` - -5. Clone QEMU - -```shell -git clone --recurse-submodules https://github.com/pebble-dev/qemu -cd qemu -``` - -6. Configure QEMU: - -```shell -./configure \ - --disable-werror \ - --enable-debug \ - --target-list="arm-softmmu" \ - --extra-cflags=-DSTM32_UART_NO_BAUD_DELAY -``` - -7. Build QEMU: - -```shell -make -``` - -8. Make sure to make it available on your `PATH`: - -```shell -export PATH=$PWD/arm-softmmu:$PATH -``` +The QEMU binary ships with the [PebbleOS SDK](https://github.com/coredevices/PebbleOS-SDK). ## Build The steps here are similar that of real hardware: ```shell -./waf configure --board=$BOARD --qemu -./waf build -./waf qemu_image_spi +./pbl configure --board=$BOARD +./pbl build ``` -where `$BOARD` is any STM32 based board. +where `$BOARD` is one of the dedicated QEMU boards (`qemu_emery`, +`qemu_flint`, `qemu_gabbro`). ## Run You can launch QEMU with the built image using: ```shell -./waf qemu +./pbl qemu +``` + +The flash image is rebuilt by default on every launch. To keep the existing flash image (e.g. to preserve stored apps), pass `--keep-flash-image`: + +```shell +./pbl qemu --keep-flash-image ``` ## Console @@ -102,7 +40,7 @@ You can launch QEMU with the built image using: You can launch a console using: ```shell -./waf qemu_console +./pbl console ``` ## Debug @@ -110,5 +48,5 @@ You can launch a console using: You can debug with GDB using: ```shell -./waf qemu_gdb +./pbl debug ``` diff --git a/docs/getting_started.md b/docs/getting_started.md deleted file mode 100644 index e39d6f401e..0000000000 --- a/docs/getting_started.md +++ /dev/null @@ -1,179 +0,0 @@ -# 🚀 Getting Started - -Follow this guide to: - -- Set up a command-line PebbleOS development environment -- Get the source code -- Build, flash, and run PebbleOS on a watch with programming port access - -## Pre-requisites - -First download the Arm GNU toolchain `arm-none-eabi` 14.2.Rel1 from [here](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads). -Make sure to make it available on your path `PATH` and then check GCC version is reported correctly: - -```shell -$ arm-none-eabi-gcc --version -arm-none-eabi-gcc (Arm GNU Toolchain 14.2.Rel1 (Build arm-14.52)) 14.2.1 20241119 -Copyright (C) 2024 Free Software Foundation, Inc. -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -``` - -A series of system-level dependencies are required. -Follow the next steps to install them. - -:::::{tab-set} -:sync-group: os - -::::{tab-item} Ubuntu 24.04 LTS -:sync: ubuntu - -1. Update package list: - -```shell -sudo apt update -``` - -2. Install required dependencies - -```shell -sudo apt install clang gcc gcc-multilib git gettext python3-dev python3-venv openocd -``` - -:::: - -::::{tab-item} macOS -:sync: macos - -1. Install [brew](https://brew.sh/). - -2. Install dependencies: - -```shell -brew install python openocd -``` - -3. Link `brew` Python: - -```shell -brew link python@3 -``` - -:::: - -::::: - -If building with Javascript support enabled (default), install Emscripten: - -:::::{tab-set} -:sync-group: os - -::::{tab-item} Ubuntu 24.04 LTS -:sync: ubuntu - -1. Install Emscripten SDK as detailed [here](https://emscripten.org/docs/getting_started/downloads.html). - Pick version `4.0.7` instead of `latest` when running `./emsdk install` or `./emsdk activate`. - To conveniently access Emscripten SDK tools, the activate command will offer some suggestions. - It is recommended to follow them. -:::: - -::::{tab-item} macOS -:sync: macos - -1. Install Emscripten using `brew`: - -```shell -brew install emscripten -``` - -Note that `brew` does not seem to offer all Emscripten versions. -Versions 4.0.x should work fine. -If `brew` versions cause issues, consider using [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) instead. - -:::: -::::: - -## Get the source code - -You can clone the PebbleOS repository by running: - -```shell -git clone --recurse-submodules https://github.com/coredevices/pebbleos -``` - -Once cloned, enter the `pebbleos` directory before continuing: - -```shell -cd pebbleos -``` - -## Python dependencies - -A series of additional Python dependencies are also required. -Follow the next steps to install them in a [Python virtual environment](https://docs.python.org/3/library/venv.html). - -1. Create a new virtual environment: - -```shell -python3 -m venv .venv -``` - -2. Activate the virtual environment: - -```shell -source .venv/bin/activate -``` - -```{tip} -Remember to activate the virtual environment before every time you start working! -``` - -3. Install dependencies - -```shell -pip install -r requirements.txt -``` - -## Building - -1. Configure the project: - -```shell -./waf configure --board $BOARD -``` - -where `$BOARD` is any of the supported boards, e.g. `asterix` (Core 2 Duo), `snowy_bb2` (Pebble Time), ... - -2. Build: - -```shell -./waf build -``` - -## Flashing - -Before attempting to flash, check the documentation for each {doc}`board ` on how to prepare and connect your watch for programming. - -You can flash the built firmware (including pre-compiled bootloader) by running: - -```shell -./waf flash -``` - -If flashing for the first time, your watch will reboot into PRF or a _sad watch_ state if PRF is missing, indicating that resources need to be flashed: - -```shell -./waf image_resources --tty $SERIAL_ADAPTER -``` - -where `$SERIAL_ADAPTER` is the path for your serial adapter, e.g. `/dev/ttyACM0`, `/dev/tty.usbmodem1102`, etc. -If using a board with a built-in FTDI programmer, the `--tty` argument can be removed. - -At this point you should observe the watch booting into the main application. -You can also see the logs by opening the console: - -```shell -./waf console --tty $SERIAL_ADAPTER -``` - -Try sending `help` to get a list of available console commands. diff --git a/docs/index.md b/docs/index.md index 924db30071..a4cd2bd47b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,7 +28,7 @@ PebbleOS is a lightweight, power-efficient operating system originally developed It supports custom watchfaces and apps using C and JavaScript. Optimized for memory-in-pixel (MIP) displays and long battery life, it features Bluetooth sync, a timeline interface, and a strong developer ecosystem through an open SDK. -```{button-ref} getting_started +```{button-ref} development/getting_started :ref-type: doc :color: primary :class: sd-rounded-pill float-left @@ -49,19 +49,17 @@ Get started :gutter: 3 :::{grid-item-card} -:link: getting_started +:link: development/getting_started :link-type: doc -:class-header: bg-light -🚀 Getting Started +🚀 Prerequisites ^^^ -Learn how to build PebbleOS from source! +Set up your environment to build PebbleOS from source! ::: :::{grid-item-card} :link: https://github.com/coredevices/pebbleos -:class-header: bg-light ⌚ Browse the sources ^^^ @@ -72,7 +70,6 @@ Browse the PebbleOS sources! :::{grid-item-card} :link: reference/external :link-type: doc -:class-header: bg-light 📖 Reference ^^^ @@ -82,17 +79,15 @@ Learn more about PebbleOS: podcasts, developer documents and more! :::: -```{toctree} -:hidden: -getting_started.md -``` ```{toctree} :hidden: :caption: 🛠️ Development +development/getting_started.md development/options.md -development/prf.md +development/building_fw.md development/qemu.md +development/moddable.md ``` ```{toctree} diff --git a/docs/legacy/porting-bluetooth.md b/docs/legacy/porting-bluetooth.md index 4f59f92c42..1a953587f5 100644 --- a/docs/legacy/porting-bluetooth.md +++ b/docs/legacy/porting-bluetooth.md @@ -163,7 +163,6 @@ Implemented both by classic and BLE. Used for factory test. Probably needs to exist for production test in the future, but * bt_driver_test_selftest -- implemented -* bt_driver_test_mfi_chip_selftest -- not implemented on LE-only * hc_endpoint_logging_set_level -- not implemented on TI * hc_endpoint_logging_get_level -- not implemented on TI * bt_driver_core_dump -- not implemented on TI diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..f0847516db --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1769318308, + "narHash": "sha256-Mjx6p96Pkefks3+aA+72lu1xVehb6mv2yTUUqmSet6Q=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "1cd347bf3355fce6c64ab37d3967b4a2cb4b878c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..872fed6f87 --- /dev/null +++ b/flake.nix @@ -0,0 +1,154 @@ +{ + description = "Development environment for PebbleOS"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + }; + + outputs = + { self, nixpkgs }: + let + sdkVersion = "0.1.6"; + sdkBundles = { + aarch64-darwin = { + osArch = "darwin-aarch64"; + sha256 = "d389ecf084c168f6b18edfec972be8790521e81750d6fbfc352275206839bec2"; + }; + aarch64-linux = { + osArch = "linux-aarch64"; + sha256 = "3c5f87380d7498ccaaa4ade45c105e97fa39aa71dec6b615d924e490978acb60"; + }; + x86_64-linux = { + osArch = "linux-x86_64"; + sha256 = "a93d33f479154f96351590709af57ef79465ebee07c06f5e264a086eb1ca55b6"; + }; + }; + forSupportedSystems = nixpkgs.lib.genAttrs (builtins.attrNames sdkBundles); + in + { + devShells = forSupportedSystems ( + system: + let + pkgs = import nixpkgs { inherit system; }; + bundle = sdkBundles.${system}; + pebbleos-sdk = pkgs.stdenv.mkDerivation { + pname = "pebbleos-sdk"; + version = sdkVersion; + src = pkgs.fetchurl { + url = "https://github.com/coredevices/PebbleOS-SDK/releases/download/v${sdkVersion}/pebbleos-sdk-${sdkVersion}-${bundle.osArch}.tar.gz"; + sha256 = bundle.sha256; + }; + + nativeBuildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ + pkgs.autoPatchelfHook + ]; + buildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux (with pkgs; [ + # arm-none-eabi host binaries (matches nixpkgs gcc-arm-embedded) + ncurses6 + ncurses5 # aarch64-linux toolchain gdb links ABI-5 ncurses/tinfo + libxcrypt-legacy + xz + zstd + # qemu-pebble host binaries + glib + pixman + zlib + stdenv.cc.cc.lib + SDL2 + libpng + alsa-lib + libpulseaudio + # sftool host binary + systemdLibs + ]); + + dontConfigure = true; + dontBuild = true; + dontStrip = true; + + installPhase = '' + runHook preInstall + bash install.sh --prefix "$out" --defaults --force + # gdb-py variants need a Python 3.8 not packaged in nixpkgs. + rm -f "$out"/arm-none-eabi/bin/arm-none-eabi-gdb-py \ + "$out"/arm-none-eabi/bin/arm-none-eabi-gdb-add-index-py + # Surface every SDK binary under $out/bin so PATH inclusion picks them up. + mkdir -p "$out/bin" + for d in arm-none-eabi/bin qemu/bin sftool; do + [ -d "$out/$d" ] || continue + for f in "$out/$d"/*; do + [ -f "$f" ] && [ -x "$f" ] && ln -sf "$f" "$out/bin/$(basename "$f")" + done + done + runHook postInstall + ''; + }; + in + { + default = pkgs.mkShellNoCC { + hardeningDisable = [ "fortify" ]; # waf expects unoptimized builds + nativeBuildInputs = with pkgs; [ + pkg-config + ]; + buildInputs = with pkgs; [ + pebbleos-sdk + gettext + git + librsvg + nodejs + openocd + protobuf + python313 + ] ++ lib.optionals stdenv.isLinux [ + # multilib clang (i686 sysroot for -m32 test builds) is x86-only + (if stdenv.hostPlatform.isx86_64 then clang_multi else clang) + gcc + # Required for Moddable build + dash + glib + gtk3 + ]; + shellHook = '' + # Ensure that apple command line tools are installed on macOS + ${pkgs.lib.optionalString pkgs.stdenv.isDarwin '' + # Verify Apple Command Line Tools are installed + if ! /usr/bin/xcrun --find clang &> /dev/null; then + echo "❌ Error: Apple Command Line Tools not found!" + echo " Please install with: xcode-select --install" + exit 1 + fi + echo "✓ Apple CLT found: $(/usr/bin/clang --version | head -1)" + + # Moddable's mac/tools.mk generates launcher scripts via + # `echo '...\n...'` and depends on `\n` being expanded. macOS + # /bin/sh (bash 3.2, XSI-compliant in POSIX mode) does this, + # but Nix's bash 5.x — picked up as `sh` via PATH — does not, + # producing scripts with a malformed shebang. Pin make's SHELL + # to /bin/sh so the recipe runs under the expected shell. + export MAKEFLAGS="SHELL=/bin/sh" + ''} + # Disable pyenv to avoid conflicts + export PYENV_VERSION=system + unset PYENV_ROOT + + # Prepare the python venv + export VENV_DIR=".venv" + if [ ! -d "$VENV_DIR" ]; then + echo "Creating virtual environment..." + python -m venv "$VENV_DIR" + source "$VENV_DIR/bin/activate" + if [ -f "requirements.txt" ]; then + echo "Installing Python dependencies..." + pip install -r requirements.txt + fi + else + source "$VENV_DIR/bin/activate" + fi + + echo "Python virtual environment activated." + ''; + }; + } + ); + }; +} diff --git a/include/bluetooth/analytics.h b/include/bluetooth/analytics.h new file mode 100644 index 0000000000..6bcbf32e2c --- /dev/null +++ b/include/bluetooth/analytics.h @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "util/attributes.h" + +#include +#include + +#define NUM_LE_CHANNELS 37 + +typedef struct PACKED LEChannelMap { + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + uint8_t byte4; +} LEChannelMap; + +bool bt_driver_analytics_get_connection_quality(const BTDeviceInternal *address, + uint8_t *link_quality_out, int8_t *rssi_out); + +bool bt_driver_analytics_collect_ble_parameters(const BTDeviceInternal *addr, + LEChannelMap *le_chan_map_res); + +void bt_driver_analytics_external_collect_chip_specific_parameters(void); + +void bt_driver_analytics_external_collect_bt_chip_heartbeat(void); + +//! Returns true iff there are connection event stats to report +bool bt_driver_analytics_get_conn_event_stats(SlaveConnEventStats *stats); diff --git a/include/bluetooth/bas.h b/include/bluetooth/bas.h new file mode 100644 index 0000000000..1746402a2e --- /dev/null +++ b/include/bluetooth/bas.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Sends the battery measurement to all subscribed & connected devices. +void bt_driver_bas_handle_update(uint8_t percent); diff --git a/src/include/bluetooth/bluetooth_types.h b/include/bluetooth/bluetooth_types.h similarity index 94% rename from src/include/bluetooth/bluetooth_types.h rename to include/bluetooth/bluetooth_types.h index 82a880c4e3..c3dc8e800b 100644 --- a/src/include/bluetooth/bluetooth_types.h +++ b/include/bluetooth/bluetooth_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/include/bluetooth/bonding_sync.h b/include/bluetooth/bonding_sync.h similarity index 76% rename from src/include/bluetooth/bonding_sync.h rename to include/bluetooth/bonding_sync.h index 19e2e089e1..9cb54802c3 100644 --- a/src/include/bluetooth/bonding_sync.h +++ b/include/bluetooth/bonding_sync.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/bluetooth/bt_driver_advert.h b/include/bluetooth/bt_driver_advert.h new file mode 100644 index 0000000000..50fc2d46f1 --- /dev/null +++ b/include/bluetooth/bt_driver_advert.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include + +bool bt_driver_advert_advertising_enable(uint32_t min_interval_ms, uint32_t max_interval_ms); + +void bt_driver_advert_advertising_disable(void); + +bool bt_driver_advert_client_get_tx_power(int8_t *tx_power); + +bool bt_driver_advert_set_advertising_data(const BLEAdData *ad_data); diff --git a/include/bluetooth/bt_driver_comm.h b/include/bluetooth/bt_driver_comm.h new file mode 100644 index 0000000000..eaace7d52d --- /dev/null +++ b/include/bluetooth/bt_driver_comm.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef struct CommSession CommSession; + +//! Figures out the optimal thread to execute `bt_driver_run_send_next_job` on +//! and schedules a job to do so +bool bt_driver_comm_schedule_send_next_job(CommSession *session); + +//! @return The PebbleTask that is used with bt_driver_comm_schedule_send_next_job() to perform +//! the sending of pending data. +bool bt_driver_comm_is_current_task_send_next_task(void); + +extern void bt_driver_run_send_next_job(CommSession *session, bool is_callback); diff --git a/include/bluetooth/conn_event_stats.h b/include/bluetooth/conn_event_stats.h new file mode 100644 index 0000000000..b967ec422a --- /dev/null +++ b/include/bluetooth/conn_event_stats.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef struct SlaveConnEventStats { + uint32_t num_conn_events; // BLE Connection Events that have elapsed + uint32_t num_conn_events_skipped; // The number of events the controller never tried to listen for + uint32_t num_sync_errors; // Events where slave did not see a packet from Master + uint32_t num_type_errors; + uint32_t num_len_errors; + uint32_t num_crc_errors; // Events that ended due to a packet CRC error + uint32_t num_mic_errors; // Events that ended due to a packet MIC error +} SlaveConnEventStats; diff --git a/include/bluetooth/dis.h b/include/bluetooth/dis.h new file mode 100644 index 0000000000..45e37c9b1d --- /dev/null +++ b/include/bluetooth/dis.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "util/attributes.h" + +// The reason the headers that define these lengths aren't included is because this header +// is included by the various number of bt_driver implementations. They don't know what "mfg" +// is, etc. +// NOTE: These sizes are asserted in a .c file to be in sync with the FW +#define MODEL_NUMBER_LEN (10) // MFG_HW_VERSION_SIZE + 1 +#define MANUFACTURER_LEN (18) // sizeof("Pebble Technology") +#define SERIAL_NUMBER_LEN (13) // MFG_SERIAL_NUMBER_SIZE + 1 +#define FW_REVISION_LEN (32) // FW_METADATA_VERSION_TAG_BYTES) +#define SW_REVISION_LEN (8) // Fmt: xx.xxx\0 + +typedef struct PACKED DisInfo { + char model_number[MODEL_NUMBER_LEN]; + char manufacturer[MANUFACTURER_LEN]; + char serial_number[SERIAL_NUMBER_LEN]; + char fw_revision[FW_REVISION_LEN]; + char sw_revision[SW_REVISION_LEN]; +} DisInfo; diff --git a/src/include/bluetooth/gap_le_connect.h b/include/bluetooth/gap_le_connect.h similarity index 86% rename from src/include/bluetooth/gap_le_connect.h rename to include/bluetooth/gap_le_connect.h index e101f39b8f..5f470e11d1 100644 --- a/src/include/bluetooth/gap_le_connect.h +++ b/include/bluetooth/gap_le_connect.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/include/bluetooth/gap_le_device_name.h b/include/bluetooth/gap_le_device_name.h new file mode 100644 index 0000000000..da356de950 --- /dev/null +++ b/include/bluetooth/gap_le_device_name.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "comm/ble/gap_le_connection.h" + +//! Bluetooth LE GAP Device name APIs +void bt_driver_gap_le_device_name_request_all(void); +void bt_driver_gap_le_device_name_request(const BTDeviceInternal *address); + +//! The caller is expected to have implemented: +//! ctx will be kernel_free()'d +void bt_driver_store_device_name_kernelbg_cb(void *ctx); diff --git a/include/bluetooth/gap_le_scan.h b/include/bluetooth/gap_le_scan.h new file mode 100644 index 0000000000..f40e22824a --- /dev/null +++ b/include/bluetooth/gap_le_scan.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "comm/ble/gap_le_scan.h" + +//! Returns true on success, false on failure +bool bt_driver_start_le_scan(bool active_scan, bool use_white_list_filter, bool filter_dups, + uint16_t scan_interval_ms, uint16_t scan_window_ms); + +//! Returns true on success, false on failure +bool bt_driver_stop_le_scan(void); + +extern void bt_driver_cb_le_scan_handle_report(const GAPLERawAdReport *data, int length); diff --git a/src/include/bluetooth/gatt.h b/include/bluetooth/gatt.h similarity index 87% rename from src/include/bluetooth/gatt.h rename to include/bluetooth/gatt.h index 6ace2d2229..bb0d6fb802 100644 --- a/src/include/bluetooth/gatt.h +++ b/include/bluetooth/gatt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -113,7 +100,8 @@ void bt_driver_gatt_acknowledge_indication(uint32_t connection_id, uint32_t tran // TODO: This will probably need to be changed for the Dialog chip (doesn't have transaction ids) void bt_driver_gatt_respond_read_subscription(uint32_t transaction_id, uint16_t response_code); -void bt_driver_gatt_send_changed_indication(uint32_t connection_id, const ATTHandleRange *data); +void bt_driver_gatt_send_changed_indication(const BTDeviceInternal *device, + const ATTHandleRange *data); BTErrno bt_driver_gatt_write_without_response(GAPLEConnection *connection, diff --git a/include/bluetooth/gatt_discovery.h b/include/bluetooth/gatt_discovery.h new file mode 100644 index 0000000000..46a0429966 --- /dev/null +++ b/include/bluetooth/gatt_discovery.h @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include + +#include + +typedef struct GAPLEConnection GAPLEConnection; +typedef struct GATTService GATTService; +typedef struct GATTServiceNode GATTServiceNode; + +BTErrno bt_driver_gatt_start_discovery_range( + const GAPLEConnection *connection, const ATTHandleRange *data); +BTErrno bt_driver_gatt_stop_discovery(GAPLEConnection *connection); + +//! It's possible we are disconnected or the stack gets torn down while in the +//! middle of a discovery. This routine gets invoked if the connection gets +//! torn down or goes away so that the implementation can clean up any tracking +//! it has waiting for a discovery to complete +void bt_driver_gatt_handle_discovery_abandoned(void); + +//! gatt_service_discovery callbacks +//! cb returns true iff the driver completed, false if a discovery retry was initiated +extern bool bt_driver_cb_gatt_client_discovery_complete(GAPLEConnection *connection, BTErrno errno); +extern void bt_driver_cb_gatt_client_discovery_handle_indication( + GAPLEConnection *connection, GATTService *service_discovered, BTErrno error); diff --git a/src/include/bluetooth/gatt_service_types.h b/include/bluetooth/gatt_service_types.h similarity index 84% rename from src/include/bluetooth/gatt_service_types.h rename to include/bluetooth/gatt_service_types.h index cabc906b60..753105c36c 100644 --- a/src/include/bluetooth/gatt_service_types.h +++ b/include/bluetooth/gatt_service_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/bluetooth/hci_types.h b/include/bluetooth/hci_types.h new file mode 100644 index 0000000000..b57a6192fb --- /dev/null +++ b/include/bluetooth/hci_types.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef enum { + HciStatusCode_Success = 0x00, + HciStatusCode_UnknownConnectionIdentifier = 0x02, + HciStatusCode_VS_Base = 0x50, + HciStatusCode_Max = UINT16_MAX +} HciStatusCode; + +#ifndef __clang__ +_Static_assert(sizeof(HciStatusCode) == 2, "packed structs expect the status code to be 2 bytes!"); +#endif + +// disconnect reasons are just status codes +typedef HciStatusCode HciDisconnectReason; diff --git a/include/bluetooth/hrm_service.h b/include/bluetooth/hrm_service.h new file mode 100644 index 0000000000..69792ba120 --- /dev/null +++ b/include/bluetooth/hrm_service.h @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include + +typedef struct { + uint16_t bpm; + bool is_on_wrist; +} BleHrmServiceMeasurement; + +//! @return True if the BT driver lib supports exposing the GATT HRM service. +bool bt_driver_is_hrm_service_supported(void); + +//! Adds or removes the HRM service from the GATT database, notifying any connected devices +//! by sending a "Service Changed" indication for the mutated handle range. +void bt_driver_hrm_service_enable(bool enable); + +//! Sends the Heart Rate Measurement to all subscribed & connected devices. +void bt_driver_hrm_service_handle_measurement(const BleHrmServiceMeasurement *measurement, + const BTDeviceInternal *permitted_devices, + size_t num_permitted_devices); + +//! Called when a connected device (un)subscribes to the GATT HRM service's "Heart Rate Measurement" +//! characteristic. +extern void bt_driver_cb_hrm_service_update_subscription(const BTDeviceInternal *device, + bool is_subscribed); diff --git a/include/bluetooth/id.h b/include/bluetooth/id.h new file mode 100644 index 0000000000..bc9d6e8a81 --- /dev/null +++ b/include/bluetooth/id.h @@ -0,0 +1,29 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +void bt_driver_id_set_local_device_name(const char device_name[BT_DEVICE_NAME_BUFFER_SIZE]); + +void bt_driver_id_copy_local_identity_address(BTDeviceAddress *addr_out); + +//! Configures the local address that the BT driver should use "on-air". +//! @note This address and the identity address are different things! +//! @note bt_lock() is held when this call is made. +//! @param allow_cycling True if the controller is allowed to cycle the address (implies address +//! pinning is *not* used!) +//! @param pinned_address The address to use, or NULL for "don't care". +void bt_driver_set_local_address(bool allow_cycling, + const BTDeviceAddress *pinned_address); + +//! Copies a human-readable string of freeform info that uniquely identifies the Bluetooth chip. +//! Used by MFG for part tracking purposes. +//! @param[out] dest Buffer into which to copy the info. +//! @param[in] dest_size Size of dest in bytes. +void bt_driver_id_copy_chip_info_string(char *dest, size_t dest_size); + +//! Generates a new private resolvable address using the current IRK (as passed with the +//! bt_driver_start() call when setting up the stack). +bool bt_driver_id_generate_private_resolvable_address(BTDeviceAddress *address_out); diff --git a/include/bluetooth/init.h b/include/bluetooth/init.h new file mode 100644 index 0000000000..4b21c26d95 --- /dev/null +++ b/include/bluetooth/init.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" + +#include +#include + +#include + +typedef struct PACKED BTDriverConfig { + SM128BitKey root_keys[SMRootKeyTypeNum]; + DisInfo dis_info; + BTDeviceAddress identity_addr; + bool is_hrm_supported_and_enabled; +} BTDriverConfig; + +//! Function that performs one-time initialization of the BT Driver. +//! The main FW is expected to call this once at boot. +void bt_driver_init(void); + +//! Starts the Bluetooth stack. +//! @return True if the stack started successfully. +bool bt_driver_start(BTDriverConfig *config); + +//! Stops the Bluetooth stack. +//! @return True if the stack stopped successfully. +void bt_driver_stop(void); + +//! Powers down the BT controller if has yet to be used +void bt_driver_power_down_controller_on_boot(void); + +//! Invoked by the BT driver each time the host (re-)synchronizes with the controller. +//! Consumers can use this to refresh controller state that gets wiped on a host reset +//! (e.g. advertising data and parameters). +extern void bt_driver_handle_host_resynced(void); diff --git a/include/bluetooth/mtu.h b/include/bluetooth/mtu.h new file mode 100644 index 0000000000..94b90cda84 --- /dev/null +++ b/include/bluetooth/mtu.h @@ -0,0 +1,7 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// See NimBLE BLE_ATT_PREFERRED_MTU setting +#define ATT_MAX_SUPPORTED_MTU 256 \ No newline at end of file diff --git a/include/bluetooth/pairability.h b/include/bluetooth/pairability.h new file mode 100644 index 0000000000..96c65c42c7 --- /dev/null +++ b/include/bluetooth/pairability.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +void bt_driver_le_pairability_set_enabled(bool enabled); diff --git a/src/include/bluetooth/pairing_confirm.h b/include/bluetooth/pairing_confirm.h similarity index 77% rename from src/include/bluetooth/pairing_confirm.h rename to include/bluetooth/pairing_confirm.h index 6db6328beb..0285ce3c6b 100644 --- a/src/include/bluetooth/pairing_confirm.h +++ b/include/bluetooth/pairing_confirm.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/include/bluetooth/pebble_bt.h b/include/bluetooth/pebble_bt.h similarity index 80% rename from src/include/bluetooth/pebble_bt.h rename to include/bluetooth/pebble_bt.h index eaca330c2a..4dff3683be 100644 --- a/src/include/bluetooth/pebble_bt.h +++ b/include/bluetooth/pebble_bt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/include/bluetooth/pebble_pairing_service.h b/include/bluetooth/pebble_pairing_service.h similarity index 95% rename from src/include/bluetooth/pebble_pairing_service.h rename to include/bluetooth/pebble_pairing_service.h index 2f5e45127b..b916e565f7 100644 --- a/src/include/bluetooth/pebble_pairing_service.h +++ b/include/bluetooth/pebble_pairing_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/bluetooth/qemu_transport.h b/include/bluetooth/qemu_transport.h new file mode 100644 index 0000000000..6744edc9ff --- /dev/null +++ b/include/bluetooth/qemu_transport.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +//! Called by the QEMU serial driver whenever Pebble Protocol data is received. +void qemu_transport_handle_received_data(const uint8_t *data, uint32_t length); + +//! Called by qemu version of comm_init() to tell ISPP that it is connected +void qemu_transport_set_connected(bool is_connected); +void qemu_transport_close_session(); + +bool qemu_transport_is_connected(void); diff --git a/src/include/bluetooth/responsiveness.h b/include/bluetooth/responsiveness.h similarity index 79% rename from src/include/bluetooth/responsiveness.h rename to include/bluetooth/responsiveness.h index ab77cbf879..8f8cdf7b02 100644 --- a/src/include/bluetooth/responsiveness.h +++ b/include/bluetooth/responsiveness.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/include/bluetooth/sm_types.h b/include/bluetooth/sm_types.h similarity index 75% rename from src/include/bluetooth/sm_types.h rename to include/bluetooth/sm_types.h index 73ec458e3e..2326b8daf0 100644 --- a/src/include/bluetooth/sm_types.h +++ b/include/bluetooth/sm_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/bluetooth/temp.h b/include/bluetooth/temp.h new file mode 100644 index 0000000000..b22496367e --- /dev/null +++ b/include/bluetooth/temp.h @@ -0,0 +1,7 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Should be called when a BT Classic disconnection occurs +void reconnect_android_update_disconnect_time(void); diff --git a/src/include/logging/binary_logging.h b/include/logging/binary_logging.h similarity index 91% rename from src/include/logging/binary_logging.h rename to include/logging/binary_logging.h index ffe72a2f1e..815ef0ef07 100644 --- a/src/include/logging/binary_logging.h +++ b/include/logging/binary_logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/include/logging/log_hashing.h b/include/logging/log_hashing.h similarity index 94% rename from src/include/logging/log_hashing.h rename to include/logging/log_hashing.h index d8d294a7ef..f01573adda 100644 --- a/src/include/logging/log_hashing.h +++ b/include/logging/log_hashing.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /************************************************************************************************ * New Logging @@ -59,6 +46,9 @@ * internally consistent. * - .log_string * which is a list of representing the log strings from the source code. + * Entries of the form "MODULE::", emitted by PBL_LOG_MODULE_DEFINE / + * PBL_LOG_MODULE_DECLARE, are metadata mapping a source file to its log module; they are + * never referenced by a token. * * Note: this code must be compiled with -Os or the codesize will explode! * @@ -87,7 +77,7 @@ #include #include "util/attributes.h" -#define NEW_LOG_VERSION "0101" +#define NEW_LOG_VERSION "0102" #define LOG_STRINGS_SECTION_ADDRESS 0xC0000000 diff --git a/src/fw/services/common/accel_manager.h b/include/pbl/services/accel_manager.h similarity index 78% rename from src/fw/services/common/accel_manager.h rename to include/pbl/services/accel_manager.h index bfd35a3da5..6bed0c4e11 100644 --- a/src/fw/services/common/accel_manager.h +++ b/include/pbl/services/accel_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -24,14 +11,14 @@ #include -#define ACCEL_LOG_DEBUG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_ACCEL, LOG_LEVEL_DEBUG, fmt, ## args) - typedef void (*AccelDataReadyCallback)(void *context); typedef struct AccelManagerState AccelManagerState; -#if PLATFORM_ASTERIX || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) static const unsigned int ACCEL_MAX_SAMPLES_PER_UPDATE = 26 * 2; // wake every 2 seconds -- lsm6dso is 26Hz +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) +static const unsigned int ACCEL_MAX_SAMPLES_PER_UPDATE = 26 * 2; // wake every 2 seconds -- FIXME(GETAFIX): review #else static const unsigned int ACCEL_MAX_SAMPLES_PER_UPDATE = 25; #endif @@ -40,6 +27,10 @@ static const unsigned int ACCEL_MAX_SAMPLES_PER_UPDATE = 25; void accel_manager_init(void); void accel_manager_enable(bool on); +// Enable or disable the kernel's shake subscription used for the motion backlight feature. +// When disabled, the accelerometer shake detection is only active if apps have subscribed. +void accel_manager_set_motion_backlight_enabled(bool enabled); + // Peek interface /////////////////////////////////////////////////////////// @@ -83,12 +74,14 @@ bool sys_accel_manager_consume_samples(AccelManagerState *state, uint32_t sample // Functions for internal use /////////////////////////////////////////////////////////// -bool accel_manager_run_selftest(void); -bool gyro_manager_run_selftest(void); - // Set whether the accelerometer should be in a sensitive state in order to trigger an accel tap // event from any small movements void accel_enable_high_sensitivity(bool high_sensitivity); +// Update the motion sensitivity based on user preference (0-100%) +// Only available on Asterix/Obelix platforms +// 100 = most sensitive, 0 = least sensitive +void accel_manager_update_sensitivity(uint8_t sensitivity_percent); + // lightweight call to determine if the watch is idle bool accel_is_idle(void); diff --git a/include/pbl/services/accel_manager_types.h b/include/pbl/services/accel_manager_types.h new file mode 100644 index 0000000000..30f96c2056 --- /dev/null +++ b/include/pbl/services/accel_manager_types.h @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +//! Valid accelerometer sampling rates, in Hz +typedef enum { + //! 10 HZ sampling rate + ACCEL_SAMPLING_10HZ = 10, + //! 25 HZ sampling rate [Default] + ACCEL_SAMPLING_25HZ = 25, + //! 50 HZ sampling rate + ACCEL_SAMPLING_50HZ = 50, + //! 100 HZ sampling rate + ACCEL_SAMPLING_100HZ = 100, +} AccelSamplingRate; + +//! A single accelerometer sample for all three axes +typedef struct __attribute__((__packed__)) { + //! acceleration along the x axis + int16_t x; + //! acceleration along the y axis + int16_t y; + //! acceleration along the z axis + int16_t z; +} AccelRawData; + +//! A single accelerometer sample for all three axes including timestamp and +//! vibration rumble status. +typedef struct __attribute__((__packed__)) AccelData { + //! acceleration along the x axis + int16_t x; + //! acceleration along the y axis + int16_t y; + //! acceleration along the z axis + int16_t z; + + //! true if the watch vibrated when this sample was collected + bool did_vibrate; + + //! timestamp, in milliseconds + uint64_t timestamp; +} AccelData; diff --git a/src/fw/services/normal/activity/activity.h b/include/pbl/services/activity/activity.h similarity index 95% rename from src/fw/services/normal/activity/activity.h rename to include/pbl/services/activity/activity.h index 818461db4b..4efa9d812c 100644 --- a/src/fw/services/normal/activity/activity.h +++ b/include/pbl/services/activity/activity.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -63,9 +50,20 @@ typedef struct PACKED HeartRatePreferences { uint8_t zone3_threshold; } HeartRatePreferences; +// HRM measurement interval options +typedef enum { + HRMonitoringInterval_10Min = 0, + HRMonitoringInterval_30Min, + HRMonitoringInterval_1Hour, + HRMonitoringInterval_Disabled, + HRMonitoringIntervalCount, +} HRMonitoringInterval; + // Activity HRM Settings Struct, for storing to prefs typedef struct PACKED ActivityHRMSettings { bool enabled; + uint8_t measurement_interval; // HRMonitoringInterval value + bool activity_tracking_enabled; // HR tracking during detected activities (walk/run) } ActivityHRMSettings; // Default values, taken from http://www.cdc.gov/nchs/fastats/body-measurements.htm @@ -96,6 +94,8 @@ typedef struct PACKED ActivityHRMSettings { #define ACTIVITY_HRM_DEFAULT_PREFERENCES { \ .enabled = true, \ + .measurement_interval = HRMonitoringInterval_10Min, \ + .activity_tracking_enabled = false, \ } // We consider values outside of this range to be invalid @@ -410,6 +410,22 @@ uint8_t activity_prefs_heart_get_zone3_threshold(void); //! Return true if the HRM is enabled, false if not bool activity_prefs_heart_rate_is_enabled(void); +#ifdef CONFIG_HRM +//! Get the HRM measurement interval setting +//! @return the current HRMonitoringInterval value +HRMonitoringInterval activity_prefs_get_hrm_measurement_interval(void); + +//! Set the HRM measurement interval +//! @param interval the desired HRMonitoringInterval value +void activity_prefs_set_hrm_measurement_interval(HRMonitoringInterval interval); + +//! Return true if HR tracking during detected activities (walk/run) is enabled +bool activity_prefs_hrm_activity_tracking_is_enabled(void); + +//! Enable or disable HR tracking during detected activities (walk/run) +void activity_prefs_set_hrm_activity_tracking_enabled(bool enabled); +#endif + //! Get the current and (optionally) historical values for a given metric. The caller passes //! in a pointer to an array that will be filled in with the results (current value for today at //! index 0, yesterday's at index 1, etc.) diff --git a/src/fw/services/normal/activity/activity_algorithm.h b/include/pbl/services/activity/activity_algorithm.h similarity index 94% rename from src/fw/services/normal/activity/activity_algorithm.h rename to include/pbl/services/activity/activity_algorithm.h index 2298f4cd40..fe3d94621c 100644 --- a/src/fw/services/normal/activity/activity_algorithm.h +++ b/include/pbl/services/activity/activity_algorithm.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include #include "applib/accel_service.h" -#include "services/normal/activity/activity.h" +#include "pbl/services/activity/activity.h" #define ACTIVITY_ALGORITHM_MAX_SAMPLES 25 diff --git a/include/pbl/services/activity/activity_calculators.h b/include/pbl/services/activity/activity_calculators.h new file mode 100644 index 0000000000..81a3759430 --- /dev/null +++ b/include/pbl/services/activity/activity_calculators.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +// ------------------------------------------------------------------------------------------------ +// Compute distance (in millimeters) covered by the taking the given number of steps in the given +// amount of time. +uint32_t activity_private_compute_distance_mm(uint32_t steps, uint32_t ms); + + +// ------------------------------------------------------------------------------------------------ +// Compute active calories (in calories, not kcalories) covered by going the given distance in +// the given amount of time. +uint32_t activity_private_compute_active_calories(uint32_t distance_mm, uint32_t ms); + +// ------------------------------------------------------------------------------------------------ +// Compute resting calories (in calories, not kcalories) within the elapsed time given +uint32_t activity_private_compute_resting_calories(uint32_t elapsed_minutes); diff --git a/src/fw/services/normal/activity/activity_insights.h b/include/pbl/services/activity/activity_insights.h similarity index 84% rename from src/fw/services/normal/activity/activity_insights.h rename to include/pbl/services/activity/activity_insights.h index 29ddcd7b59..a5d2dd9d07 100644 --- a/src/fw/services/normal/activity/activity_insights.h +++ b/include/pbl/services/activity/activity_insights.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -107,9 +94,6 @@ void activity_insights_test_push_summary_pins(void); //! Used by test apps: Pushes the 2 rewards to the watch void activity_insights_test_push_rewards(void); -//! Used by test apps: Pushes the day 1, 4 and 10 insights -void activity_insights_test_push_day_insights(void); - //! Used by test apps: Pushes a run and a walk notification void activity_insights_test_push_walk_run_sessions(void); diff --git a/src/fw/services/normal/activity/activity_private.h b/include/pbl/services/activity/activity_private.h similarity index 91% rename from src/fw/services/normal/activity/activity_private.h rename to include/pbl/services/activity/activity_private.h index 0d10b1ab30..d4bcb80735 100644 --- a/src/fw/services/normal/activity/activity_private.h +++ b/include/pbl/services/activity/activity_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -22,8 +9,8 @@ #include "applib/event_service_client.h" #include "kernel/events.h" #include "os/mutex.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/settings/settings_file.h" #include "system/hexdump.h" #include "system/logging.h" #include "util/attributes.h" @@ -32,7 +19,7 @@ #include #define ACTIVITY_LOG_DEBUG(fmt, args...) \ - PBL_LOG_D(LOG_DOMAIN_ACTIVITY, LOG_LEVEL_DEBUG, fmt, ## args) + PBL_LOG_D_DBG(LOG_DOMAIN_ACTIVITY, fmt, ## args) #define ACTIVITY_HEXDUMP(data, length) \ PBL_HEXDUMP_D(LOG_DOMAIN_DATA_ACTIVITY, LOG_LEVEL_DEBUG, data, length) @@ -64,25 +51,29 @@ typedef uint16_t ActivityScalarStore; // part of the next day's sleep #define ACTIVITY_LAST_SLEEP_MINUTE_OF_DAY (21 * MINUTES_PER_HOUR) -// Default HeartRate sampling period (Must take a sample every X seconds by default) -#define ACTIVITY_DEFAULT_HR_PERIOD_SEC (10 * SECONDS_PER_MINUTE) +// Default HeartRate sampling ON time +#define ACTIVITY_DEFAULT_HR_ON_TIME_SEC (60) -// Default HeartRate sampling ON time (Stays on for X seconds every -// ACTIVITY_DEFAULT_HR_PERIOD_SEC seconds) -#define ACTIVITY_DEFAULT_HR_ON_TIME_SEC (SECONDS_PER_MINUTE) +// Turn off the HR device after we've received X good quality samples +#define ACTIVITY_MIN_NUM_GOOD_SAMPLES_SHORT_CIRCUIT (10) -// Turn off the HR device after we've received X number of thresholded samples -#define ACTIVITY_MIN_NUM_SAMPLES_SHORT_CIRCUIT (15) +// Turn off the HR device after we've received X excellent quality samples +#define ACTIVITY_MIN_NUM_EXCELLENT_SAMPLES_SHORT_CIRCUIT (5) // The minimum number of samples needed before we can approximate the user's HR zone -#define ACTIVITY_MIN_NUM_SAMPLES_FOR_HR_ZONE (10) - -#define ACTIVITY_MIN_HR_QUALITY_THRESH (HRMQuality_Good) +#define ACTIVITY_MIN_NUM_SAMPLES_FOR_HR_ZONE (5) // HRM Subscription values during ON and OFF periods #define ACTIVITY_HRM_SUBSCRIPTION_ON_PERIOD_SEC (1) #define ACTIVITY_HRM_SUBSCRIPTION_OFF_PERIOD_SEC (SECONDS_PER_DAY) +// After this many seconds without an HRM event, the cached worn-status is considered stale and +// the sleep algorithm falls back to its accel-based not-worn heuristics. Sized to comfortably +// cover the default HRMonitoringInterval_10Min cycle (~11 min) — at the longer 30/60-min +// intervals the cache will simply expire between bursts and sleep detection won't lean on a +// stale on-wrist reading. +#define ACTIVITY_HRM_OFFWRIST_STALE_SEC (15 * SECONDS_PER_MINUTE) + // Max number of stored HR samples to compute the median #define ACTIVITY_MAX_HR_SAMPLES (3 * SECONDS_PER_MINUTE) @@ -320,12 +311,15 @@ typedef struct { // (from time_get_uptime_seconds) uint16_t num_samples; // number of samples in the past minute - uint16_t num_quality_samples; // number of samples in the past minute that have met our - // quality threshold ACTIVITY_MIN_HR_QUALITY_THRESH - // NOTE: Used to short circuit - // our HR polling when enough samples have been taken + uint16_t num_good_quality_samples; // number of samples in the past minute with good quality + uint16_t num_excellent_samples; // number of samples in the past minute with excellent quality uint8_t samples[ACTIVITY_MAX_HR_SAMPLES]; // HR Samples stored uint8_t weights[ACTIVITY_MAX_HR_SAMPLES]; // HR Sample Weights + + // Worn status from the most recent HRM BPM event. last_quality_event_utc is 0 if we've never + // received one. Used by sleep tracking to suppress detection while the watch is off-wrist. + time_t last_quality_event_utc; + bool last_quality_was_offwrist; } ActivityHRSupport; typedef struct { @@ -524,6 +518,16 @@ void activity_metrics_prv_reset_hr_stats(void); void activity_metrics_prv_add_median_hr_sample(PebbleHRMEvent *hrm_event, time_t now_utc, time_t now_uptime); +//! Record the worn status reported by the HRM. Called once per BPM event. +//! @param[in] now_utc current UTC time +//! @param[in] is_offwrist true if the event's HRMQuality was HRMQuality_OffWrist +void activity_metrics_prv_set_hrm_worn_status(time_t now_utc, bool is_offwrist); + +//! Returns true if the HRM has recently reported the watch is off-wrist. The most recent BPM +//! event must have been HRMQuality_OffWrist and must have arrived within the last +//! ACTIVITY_HRM_OFFWRIST_STALE_SEC seconds, otherwise this returns false. +bool activity_metrics_prv_is_hrm_offwrist(time_t now_utc); + //! Returns the number of steps the user has taken so far today (since midnight) uint32_t activity_metrics_prv_get_steps(void); diff --git a/src/fw/services/normal/activity/health_util.h b/include/pbl/services/activity/health_util.h similarity index 91% rename from src/fw/services/normal/activity/health_util.h rename to include/pbl/services/activity/health_util.h index 866276511a..d90b70b450 100644 --- a/src/fw/services/normal/activity/health_util.h +++ b/include/pbl/services/activity/health_util.h @@ -1,26 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/ui/layer.h" -#include "apps/system_apps/timeline/text_node.h" +#include "apps/system/timeline/text_node.h" #include #include +#include //! The maximum number of text nodes needed in a text node container #define MAX_TEXT_NODES 5 diff --git a/include/pbl/services/activity/hr_util.h b/include/pbl/services/activity/hr_util.h new file mode 100644 index 0000000000..11ba8e8ebe --- /dev/null +++ b/include/pbl/services/activity/hr_util.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef enum HRZone { + HRZone_Zone0, + HRZone_Zone1, + HRZone_Zone2, + HRZone_Zone3, + + HRZoneCount, + HRZone_Max = HRZone_Zone3, +} HRZone; + +//! Returns the HR Zone for a given BPM +HRZone hr_util_get_hr_zone(int bpm); + +//! Returns whether the BPM should be considered elevated +bool hr_util_is_elevated(int bpm); diff --git a/src/fw/services/normal/activity/insights_settings.h b/include/pbl/services/activity/insights_settings.h similarity index 90% rename from src/fw/services/normal/activity/insights_settings.h rename to include/pbl/services/activity/insights_settings.h index 5f5aa02924..c4dc2fbf01 100644 --- a/src/fw/services/normal/activity/insights_settings.h +++ b/include/pbl/services/activity/insights_settings.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "activity.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "util/attributes.h" #define ACTIVITY_INSIGHTS_SETTINGS_SLEEP_REWARD "sleep_reward" diff --git a/include/pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h b/include/pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h new file mode 100644 index 0000000000..defff900bf --- /dev/null +++ b/include/pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/time/time.h" +#include "kraepelin_algorithm.h" + +// We divide the raw light sensor reading by this factor before storing it into AlgDlsMinuteData +#define ALG_RAW_LIGHT_SENSOR_DIVIDE_BY 16 + +// Nap constraints, also used by unit tests +// A sleep session in this range is always considered "primary" (not nap) sleep +// ... if it ends after this minute in the evening +#define ALG_PRIMARY_EVENING_MINUTE (21 * MINUTES_PER_HOUR) // 9pm +// ... or starts before this minute in the morning +#define ALG_PRIMARY_MORNING_MINUTE (12 * MINUTES_PER_HOUR) // 12pm + +// A sleep session outside of the primary range is considered a nap if it is less than +// this duration, otherwise it is considered a primary sleep session +#define ALG_MAX_NAP_MINUTES (3 * MINUTES_PER_HOUR) + +// Max number of hours of past data we process to figure out sleep for "today". If a sleep +// cycle *ends* after midnight today, then we still count it as today's sleep. That means the +// start of the sleep cycle could have started more than 24 hours ago. +#define ALG_SLEEP_HISTORY_HOURS_FOR_TODAY 36 diff --git a/src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.h b/include/pbl/services/activity/kraepelin/kraepelin_algorithm.h similarity index 94% rename from src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.h rename to include/pbl/services/activity/kraepelin/kraepelin_algorithm.h index a8c6909434..1c91214e67 100644 --- a/src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.h +++ b/include/pbl/services/activity/kraepelin/kraepelin_algorithm.h @@ -125,7 +125,10 @@ uint32_t kalg_analyze_finish_epoch(KAlgState *state); // @param[in] steps number of steps taken in the last minute // @param[in] vmc VMC for the last minute // @param[in] orientation average orientation for the last minute -// @param[in] plugged_in true if watch is plugged into charger +// @param[in] definitely_not_worn true if the watch is definitely not being worn this minute +// (caller passes "plugged into charger" OR'd with any other definite +// not-worn hints such as a recent HRM off-wrist reading). Treated as a +// hard "not worn" signal for sleep detection. // @param[in] resting_calories number of resting calories burned in the last minute // @param[in] active_calories number of active calories burned in the last minute // @param[in] distance_mm distance covered in millimeters in the last minute @@ -133,7 +136,8 @@ uint32_t kalg_analyze_finish_epoch(KAlgState *state); // session that it finds. // @param[in] context passed to the sessions_cb void kalg_activities_update(KAlgState *state, time_t utc_now, uint16_t steps, uint16_t vmc, - uint8_t orientation, bool plugged_in, uint32_t resting_calories, + uint8_t orientation, bool definitely_not_worn, + uint32_t resting_calories, uint32_t active_calories, uint32_t distance_mm, bool shutting_down, KAlgActivitySessionCallback sessions_cb, void *context); diff --git a/src/fw/services/normal/activity/workout_service.h b/include/pbl/services/activity/workout_service.h similarity index 80% rename from src/fw/services/normal/activity/workout_service.h rename to include/pbl/services/activity/workout_service.h index 37e1cda6a5..743f23afc3 100644 --- a/src/fw/services/normal/activity/workout_service.h +++ b/include/pbl/services/activity/workout_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/normal/alarms/alarm.h b/include/pbl/services/alarms/alarm.h similarity index 81% rename from src/fw/services/normal/alarms/alarm.h rename to include/pbl/services/alarms/alarm.h index dc709cfb0e..bdc702984c 100644 --- a/src/fw/services/normal/alarms/alarm.h +++ b/include/pbl/services/alarms/alarm.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -53,6 +40,14 @@ typedef enum AlarmType { AlarmTypeCount, } AlarmType; +//! Built-in alarm tones, played on speaker hardware when sound is enabled. +typedef enum AlarmTone { + AlarmTone_Reveille = 0, + AlarmTone_Beacon, + AlarmTone_Bell, + AlarmTone_Chime, +} AlarmTone; + typedef struct AlarmInfo { int hour; // + +#define PBL_ANALYTICS_KEY(key_name) PBL_ANALYTICS_KEY__##key_name + +enum pbl_analytics_key { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) \ + PBL_ANALYTICS_KEY(key), +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) \ + PBL_ANALYTICS_KEY(key), +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) \ + PBL_ANALYTICS_KEY(key), +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) \ + PBL_ANALYTICS_KEY(key), +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) \ + PBL_ANALYTICS_KEY(key), +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) \ + PBL_ANALYTICS_KEY(key), + #include "analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + PBL_ANALYTICS_KEY_COUNT, +}; + +void pbl_analytics_init(void); + +void sys_pbl_analytics_set_signed(enum pbl_analytics_key key, int32_t signed_value); + +void sys_pbl_analytics_set_unsigned(enum pbl_analytics_key key, uint32_t unsigned_value); + +void sys_pbl_analytics_set_string(enum pbl_analytics_key key, const char *value); + +void sys_pbl_analytics_timer_start(enum pbl_analytics_key key); + +void sys_pbl_analytics_timer_stop(enum pbl_analytics_key key); + +void sys_pbl_analytics_add(enum pbl_analytics_key key, int32_t amount); + +#define PBL_ANALYTICS_SET_SIGNED(key_name, signed_value) \ + sys_pbl_analytics_set_signed(PBL_ANALYTICS_KEY(key_name), signed_value) + +#define PBL_ANALYTICS_SET_UNSIGNED(key_name, unsigned_value) \ + sys_pbl_analytics_set_unsigned(PBL_ANALYTICS_KEY(key_name), unsigned_value) + +#define PBL_ANALYTICS_SET_STRING(key_name, value) \ + sys_pbl_analytics_set_string(PBL_ANALYTICS_KEY(key_name), value) + +#define PBL_ANALYTICS_TIMER_START(key_name) \ + sys_pbl_analytics_timer_start(PBL_ANALYTICS_KEY(key_name)) + +#define PBL_ANALYTICS_TIMER_STOP(key_name) \ + sys_pbl_analytics_timer_stop(PBL_ANALYTICS_KEY(key_name)) + +#define PBL_ANALYTICS_ADD(key_name, amount) \ + sys_pbl_analytics_add(PBL_ANALYTICS_KEY(key_name), amount) diff --git a/include/pbl/services/analytics/backend.h b/include/pbl/services/analytics/backend.h new file mode 100644 index 0000000000..6db2a66814 --- /dev/null +++ b/include/pbl/services/analytics/backend.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "analytics.h" + +#include + +struct pbl_analytics_backend_ops { + void (*set_signed)(enum pbl_analytics_key key, int32_t signed_value); + void (*set_unsigned)(enum pbl_analytics_key key, uint32_t unsigned_value); + void (*set_string)(enum pbl_analytics_key key, const char *value); + void (*timer_start)(enum pbl_analytics_key key); + void (*timer_stop)(enum pbl_analytics_key key); + void (*add)(enum pbl_analytics_key key, int32_t amount); +}; \ No newline at end of file diff --git a/include/pbl/services/animation_service.h b/include/pbl/services/animation_service.h new file mode 100644 index 0000000000..52421c1682 --- /dev/null +++ b/include/pbl/services/animation_service.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include "kernel/pebble_tasks.h" + +//! @file animation_service.h +//! Manage the system resources used by the applib/animation module. + +//! Register the timer to fire in N ms. When it fires, the animation_private_timer_callback() +//! will be called and passed the AnimationState for that task. +void animation_service_timer_schedule(uint32_t ms); + +//! Acknowledge that we received an event sent by the animation timer +void animation_service_timer_event_received(void); + +//! Destroy the animation resoures used by the given task. Called by the process_manager when a +// process exits +void animation_service_cleanup(PebbleTask task); diff --git a/include/pbl/services/app_cache.h b/include/pbl/services/app_cache.h new file mode 100644 index 0000000000..4143bc3165 --- /dev/null +++ b/include/pbl/services/app_cache.h @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_install_types.h" +#include "system/status_codes.h" + +#include +#include + +//! @file app_cache.c +//! AppCache +//! +//! The AppCache keeps track of a cache of the applications that have binaries that reside on the +//! watch. When an app's binaries are removed from the watch, the entry with the same AppInstallId +//! is removed from the AppCache. +//! +//! When the app storage space has run out, a call to the app cache will retrieve the entry that +//! needs to be removed. + +//! Initializes the AppCache +void app_cache_init(void); + +//! Adds a blank entry with the given AppInstallId and total size to the AppCache +status_t app_cache_add_entry(AppInstallId app_id, uint32_t total_size); + +//! Removes an entry with the given AppInstallId from the AppCache +status_t app_cache_remove_entry(AppInstallId app_id); + +//! Checks whether an entry with the given AppInstallId is in the AppCache. +bool app_cache_entry_exists(AppInstallId app_id); + +//! Increments data stored about an entry with the given AppInstallId in the AppCache +status_t app_cache_app_launched(AppInstallId app_id); + +//! Ask the app cache to free up n bytes in case other parts of the system need room in the +//! filesystem +status_t app_cache_free_up_space(uint32_t bytes_needed); + +//! Clears the entire AppCache +//! NOTE: Must be called from PebbleTask_KernelBackground +void app_cache_flush(void); diff --git a/include/pbl/services/app_fetch_endpoint.h b/include/pbl/services/app_fetch_endpoint.h new file mode 100644 index 0000000000..ab09731f71 --- /dev/null +++ b/include/pbl/services/app_fetch_endpoint.h @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/uuid.h" +#include "kernel/events.h" +#include "process_management/app_install_types.h" + +typedef enum { + AppFetchResultSuccess, + AppFetchResultTimeoutError, + AppFetchResultGeneralFailure, + AppFetchResultPhoneBusy, + AppFetchResultUUIDInvalid, + AppFetchResultNoBluetooth, + AppFetchResultPutBytesFailure, + AppFetchResultNoData, + AppFetchResultUserCancelled, + AppFetchResultIncompatibleJSFailure, +} AppFetchResult; + +typedef struct { + AppFetchResult error; + AppInstallId id; +} AppFetchError; + +void app_fetch_binaries(const Uuid *uuid, AppInstallId app_id, bool has_worker); + +//! @param app_id The AppInstallId of the fetch to be cancelled. +//! NOTE: If `app_id` is INSTALL_ID_INVALID, it will cancel the fetch regardless of AppInstallId +void app_fetch_cancel(AppInstallId app_id); + +//! @param app_id The AppInstallId of the fetch to be cancelled. +//! NOTE: If `app_id` is INSTALL_ID_INVALID, it will cancel the fetch regardless of AppInstallId +//! NOTE: Must be called from PebbleTask_KernelBackground +void app_fetch_cancel_from_system_task(AppInstallId app_id); + +bool app_fetch_in_progress(void); + +//! Put Bytes handler. Used for keeping track of progress and cleanup events +void app_fetch_put_bytes_event_handler(PebblePutBytesEvent *pb_event); + +AppFetchError app_fetch_get_previous_error(void); diff --git a/include/pbl/services/app_glances/app_glance_service.h b/include/pbl/services/app_glances/app_glance_service.h new file mode 100644 index 0000000000..91d69320bc --- /dev/null +++ b/include/pbl/services/app_glances/app_glance_service.h @@ -0,0 +1,49 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/blob_db/app_glance_db_private.h" +#include "pbl/services/timeline/attribute.h" +#include "util/attributes.h" +#include "util/time/time.h" +#include "util/uuid.h" + +typedef enum AppGlanceSliceType { + AppGlanceSliceType_IconAndSubtitle = 0, + + AppGlanceSliceTypeCount +} AppGlanceSliceType; + +//! We name this "internal" so it won't conflict with the AppGlanceSlice struct we export in the SDK +#if UNITTEST +// Memory comparisons in unit tests won't work unless we pack the struct +typedef struct PACKED AppGlanceSliceInternal { +#else +typedef struct AppGlanceSliceInternal { +#endif + AppGlanceSliceType type; + time_t expiration_time; + union { + //! Add more structs to this union as we introduce new app glance slice types + struct { + uint32_t icon_resource_id; + char template_string[ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1]; + } icon_and_subtitle; + }; +} AppGlanceSliceInternal; + +typedef struct AppGlance { + size_t num_slices; + AppGlanceSliceInternal slices[APP_GLANCE_DB_MAX_SLICES_PER_GLANCE]; +} AppGlance; + +//! Initializes an AppGlance. +void app_glance_service_init_glance(AppGlance *glance); + +//! Initializes the app glance service. +void app_glance_service_init(void); + +//! Returns true if the current slice was successfully copied to slice_out. +//! Returns false if all slices in the glance have expired or if an error occurred. +bool app_glance_service_get_current_slice(const Uuid *app_uuid, AppGlanceSliceInternal *slice_out); diff --git a/src/fw/services/normal/app_inbox_service.h b/include/pbl/services/app_inbox_service.h similarity index 88% rename from src/fw/services/normal/app_inbox_service.h rename to include/pbl/services/app_inbox_service.h index 248e0ff004..ed2533f33c 100644 --- a/src/fw/services/normal/app_inbox_service.h +++ b/include/pbl/services/app_inbox_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/app_message/app_message_sender.h b/include/pbl/services/app_message/app_message_sender.h new file mode 100644 index 0000000000..eebcce5157 --- /dev/null +++ b/include/pbl/services/app_message/app_message_sender.h @@ -0,0 +1,66 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/app_outbox_service.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_send_queue.h" + +#include + +//! This module uses AppOutbox to get Pebble Protocol outbound messages from the app. +//! It does not keep any static state inside this module, all the state is stored by the app outbox +//! service. It's really just a piece of glue code between app_outbox.c and session_send_queue.c + + +//! Enum that "inherits" from AppOutboxStatus and defines app-message-sender-specific status +//! values in the user range: +typedef enum { + AppMessageSenderErrorSuccess = AppOutboxStatusSuccess, + AppMessageSenderErrorDisconnected = AppOutboxStatusConsumerDoesNotExist, + AppMessageSenderErrorDataTooShort = AppOutboxStatusUserRangeStart, + AppMessageSenderErrorEndpointDisallowed, + + NumAppMessageSenderError, +} AppMessageSenderError; + +_Static_assert((NumAppMessageSenderError - 1) <= AppOutboxStatusUserRangeEnd, + "AppMessageSenderError value can't be bigger than AppOutboxStatusUserRangeEnd"); + +//! @note This is the data structure for the `consumer_data` of the AppOutboxMessage. +//! app_message_sender.c assumes this struct is always contained within the AppOutboxMessage +//! struct. +typedef struct { + SessionSendQueueJob send_queue_job; + + CommSession *session; + PebbleProtocolHeader header; + + size_t consumed_length; +} AppMessageSendJob; + +_Static_assert(offsetof(AppMessageSendJob, send_queue_job) == 0, + "send_queue_job must be first member, due to the way session_send_queue.c works"); + +//! Structure of `data` in outbox_message (in app's memory space) +//! @note None of these fields can be trusted / used as is, they need to be sanitized. +typedef struct { + //! Can be NULL to "auto select" the session based on the UUID of the running app. + CommSession *session; + + //! Padding for future use + uint8_t padding[6]; + + uint16_t endpoint_id; + uint8_t payload[]; +} AppMessageAppOutboxData; + +#if !UNITTEST +_Static_assert(sizeof(AppMessageAppOutboxData) <= 12, + "Can't grow AppMessageAppOutboxData beyond 12 bytes, can break apps!"); +#endif + +//! To be called once during boot. This registers this module with app_outbox_service. +void app_message_sender_init(void); diff --git a/src/fw/services/normal/app_outbox_service.h b/include/pbl/services/app_outbox_service.h similarity index 88% rename from src/fw/services/normal/app_outbox_service.h rename to include/pbl/services/app_outbox_service.h index fab3e702e7..515ffc570b 100644 --- a/src/fw/services/normal/app_outbox_service.h +++ b/include/pbl/services/app_outbox_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/audio_endpoint.h b/include/pbl/services/audio_endpoint.h new file mode 100644 index 0000000000..89dd255d24 --- /dev/null +++ b/include/pbl/services/audio_endpoint.h @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +//! Endpoint for transferring audio data between the watch and phone +//! https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=491698 + +//! Session identifier passed to endpoint functions +typedef uint16_t AudioEndpointSessionId; +#define AUDIO_ENDPOINT_SESSION_INVALID_ID (0) + +//! Function signature of the callback to handle stop transfer message received from phone +typedef void (*AudioEndpointStopTransferCallback)(AudioEndpointSessionId session_id); + +//! Create a session for transferring audio data from watch to phone +//! @param stop_transfer Callback to handle stop transfer message received from phone. +//! @return Session identifier to pass to other endpoint functions +AudioEndpointSessionId audio_endpoint_setup_transfer(AudioEndpointStopTransferCallback stop_transfer); + +//! Add a frame of audio data to session's internal buffer +//! @param session_id Session identifier returned by audio_endpoint_start_transfer +//! @param frame Pointer to frame of encoded audio data +//! @param frame_size Size of frame of encoded audio data in bytes +void audio_endpoint_add_frame(AudioEndpointSessionId session_id, uint8_t *frame, + uint8_t frame_size); + +//! Stop transferring audio data from watch to phone +//! @param session_id Session identifier returned by audio_endpoint_setup_transfer +void audio_endpoint_stop_transfer(AudioEndpointSessionId session_id); + +//! Cancel a transfer session without sending a stop transfer message +//! @param session_id Session identifier returned by audio_endpoint_setup_transfer +void audio_endpoint_cancel_transfer(AudioEndpointSessionId session_id); diff --git a/include/pbl/services/audio_endpoint_private.h b/include/pbl/services/audio_endpoint_private.h new file mode 100644 index 0000000000..a0e6702527 --- /dev/null +++ b/include/pbl/services/audio_endpoint_private.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" + +#include +#include + +typedef enum { + MsgIdDataTransfer = 0x02, + MsgIdStopTransfer = 0x03, +} MsgId; + +typedef struct PACKED { + MsgId msg_id; + AudioEndpointSessionId session_id; + uint8_t frame_count; + uint8_t frames[]; +} DataTransferMsg; + +typedef struct PACKED { + MsgId msg_id; + AudioEndpointSessionId session_id; +} StopTransferMsg; diff --git a/include/pbl/services/battery/battery_curve.h b/include/pbl/services/battery/battery_curve.h new file mode 100644 index 0000000000..2214c5c9d2 --- /dev/null +++ b/include/pbl/services/battery/battery_curve.h @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include +#include + +// Handles battery mV <-> % conversion + +typedef enum { + BATTERY_CURVE_COMPENSATE_STATUS_LED, + BATTERY_CURVE_COMPENSATE_COUNT +} BatteryCurveVoltageCompensationKey; + +//! Set compensation value to be applied to battery voltage when calculating percentage charge. +//! For example, if an LED is constantly on, the voltage being measured is going to drop due to the +//! internal resistance of the battery. +void battery_curve_set_compensation(BatteryCurveVoltageCompensationKey key, int mv); + +void battery_curve_set_full_voltage(uint16_t voltage); + +#if UNITTEST +//! Restore the discharge curve mutated by battery_curve_set_full_voltage(). +//! For test isolation only; not built into production firmware. +void battery_curve_reset_for_tests(void); +#endif + +//! Returns the corresponding battery percentage as a ratio32. +uint32_t battery_curve_sample_ratio32_charge_percent(uint32_t battery_mv, bool is_charging); + +uint32_t battery_curve_lookup_percent_by_voltage(uint32_t battery_mv, bool is_charging); + +int32_t battery_curve_lookup_percent_with_scaling_factor( + int battery_mv, bool is_charging, uint32_t scaling_factor); + +uint32_t battery_curve_get_hours_remaining(uint32_t percent_remaining); + +uint32_t battery_curve_get_percent_remaining(uint32_t hours); + +// This is used by unit tests and QEMU +uint32_t battery_curve_lookup_voltage_by_percent(uint32_t percent, bool is_charging); diff --git a/include/pbl/services/battery/battery_monitor.h b/include/pbl/services/battery/battery_monitor.h new file mode 100644 index 0000000000..4d4306813a --- /dev/null +++ b/include/pbl/services/battery/battery_monitor.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/new_timer/new_timer.h" +#include + +// The battery monitor handles power state and associated service control, in response to battery +// state changes. This includes low power and critical modes. + +void battery_monitor_init(void); +void battery_monitor_handle_state_change_event(PreciseBatteryChargeState state); + +// Use the battery state to determine if UI elements should be locked out +// because the battery is too low +bool battery_monitor_critical_lockout(void); + +// For unit tests +TimerID battery_monitor_get_standby_timer_id(void); diff --git a/src/fw/services/common/battery/battery_state.h b/include/pbl/services/battery/battery_state.h similarity index 75% rename from src/fw/services/common/battery/battery_state.h rename to include/pbl/services/battery/battery_state.h index 1346733968..e8ea5c1d04 100644 --- a/src/fw/services/common/battery/battery_state.h +++ b/include/pbl/services/battery/battery_state.h @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include #include @@ -75,5 +62,8 @@ void battery_state_reset_filter(void); // Get the last recorded voltage uint16_t battery_state_get_voltage(void); +// Get the last recorded temperature (mC) +int32_t battery_state_get_temperature(void); + // For unit tests TimerID battery_state_get_periodic_timer_id(void); diff --git a/src/fw/services/normal/blob_db/api.h b/include/pbl/services/blob_db/api.h similarity index 90% rename from src/fw/services/normal/blob_db/api.h rename to include/pbl/services/blob_db/api.h index 72e1c68db3..8c84edb882 100644 --- a/src/fw/services/normal/blob_db/api.h +++ b/include/pbl/services/blob_db/api.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -48,6 +35,7 @@ typedef enum PACKED { BlobDBIdWatchAppPrefs = 0x09, BlobDBIdHealth = 0x0A, BlobDBIdAppGlance = 0x0B, + BlobDBIdSettings = 0x0C, NumBlobDBs, } BlobDBId; _Static_assert(sizeof(BlobDBId) == 1, "BlobDBId is larger than 1 byte"); @@ -122,6 +110,12 @@ typedef BlobDBDirtyItem *(*BlobDBGetDirtyListImpl)(void); //! \returns S_SUCCESS if the item was marked synced, an error code otherwise typedef status_t (*BlobDBMarkSyncedImpl)(const uint8_t *key, int key_len); +//! Implements the Compact API. Reclaims unused space in the underlying +//! settings file. Note that this function should be blocking; only blob DBs +//! backed by a settings_file need to implement this. +//! \returns S_SUCCESS on success, an error code otherwise +typedef status_t (*BlobDBCompactImpl)(void); + //! Emits a Blob DB event. //! \param type The type of event to emit //! \param db_id the ID of the blob DB @@ -132,6 +126,12 @@ void blob_db_event_put(BlobDBEventType type, BlobDBId db_id, const uint8_t *key, //! Call the BlobDBInitImpl for all the databases void blob_db_init_dbs(void); +//! Call the BlobDBCompactImpl for every database that implements one. Used to +//! reclaim space in growable settings_file-backed databases. Must be called +//! after blob_db_init_dbs(). Safe to call from a system task callback; do not +//! call from the kernel main loop as compaction performs disk I/O. +void blob_db_compact_growable_dbs(void); + //! Call the BlobDBIsDirtyImpl for each database, and fill the 'ids' list //! with all the dirty DB ids //! \param[out] ids an array of BlobDbIds of size NumBlobDBs or more. diff --git a/include/pbl/services/blob_db/api_types.h b/include/pbl/services/blob_db/api_types.h new file mode 100644 index 0000000000..306660fc9f --- /dev/null +++ b/include/pbl/services/blob_db/api_types.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef enum BlobDBEventType { + BlobDBEventTypeInsert, + BlobDBEventTypeDelete, + BlobDBEventTypeFlush, +} BlobDBEventType; diff --git a/include/pbl/services/blob_db/app_db.h b/include/pbl/services/blob_db/app_db.h new file mode 100644 index 0000000000..00819c3b4f --- /dev/null +++ b/include/pbl/services/blob_db/app_db.h @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "util/uuid.h" +#include "process_management/app_install_manager.h" +#include "process_management/pebble_process_info.h" +#include "system/status_codes.h" +#include "util/attributes.h" +#include "util/list.h" + + +//! App database entry for BlobDB. First pass is very basic. The list will expand as more features +//! and requirements are implemented. +typedef struct PACKED { + Uuid uuid; + uint32_t info_flags; + uint32_t icon_resource_id; + Version app_version; + Version sdk_version; + GColor8 app_face_bg_color; + uint8_t template_id; + char name[APP_NAME_SIZE_BYTES]; +} AppDBEntry; + +//! Used in app_db_enumerate_entries +typedef void(*AppDBEnumerateCb)(AppInstallId install_id, AppDBEntry *entry, void *data); + +/* AppDB Functions */ + +int32_t app_db_get_next_unique_id(void); + +AppInstallId app_db_get_install_id_for_uuid(const Uuid *uuid); + +status_t app_db_get_app_entry_for_uuid(const Uuid *uuid, AppDBEntry *entry); + +status_t app_db_get_app_entry_for_install_id(AppInstallId app_id, AppDBEntry *entry); + +void app_db_enumerate_entries(AppDBEnumerateCb cb, void *data); + +/* AppDB AppInstallId Implementation */ + +bool app_db_exists_install_id(AppInstallId app_id); + +/* BlobDB Implementation */ + +void app_db_init(void); + +status_t app_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int app_db_get_len(const uint8_t *key, int key_len); + +status_t app_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t app_db_delete(const uint8_t *key, int key_len); + +status_t app_db_flush(void); + +status_t app_db_compact(void); + +/* TEST */ +AppInstallId app_db_check_next_unique_id(void); diff --git a/include/pbl/services/blob_db/app_glance_db.h b/include/pbl/services/blob_db/app_glance_db.h new file mode 100644 index 0000000000..ee2742211d --- /dev/null +++ b/include/pbl/services/blob_db/app_glance_db.h @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/app_glances/app_glance_service.h" +#include "system/status_codes.h" +#include "util/attributes.h" +#include "util/time/time.h" +#include "util/uuid.h" + +#include + +// ------------------------------------------------------------------------------------------------- +// AppGlanceDB Implementation + +status_t app_glance_db_insert_glance(const Uuid *uuid, const AppGlance *glance); + +status_t app_glance_db_read_glance(const Uuid *uuid, AppGlance *glance_out); + +status_t app_glance_db_read_creation_time(const Uuid *uuid, time_t *time_out); + +status_t app_glance_db_delete_glance(const Uuid *uuid); + +// ------------------------------------------------------------------------------------------------- +// BlobDB API Implementation + +void app_glance_db_init(void); + +status_t app_glance_db_flush(void); + +//! Compact and shrink the on-disk settings file. Forces growable files that +//! grew before the growable change landed (or under heavy load) to drop back +//! toward the initial allocation. +status_t app_glance_db_compact(void); + +status_t app_glance_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int app_glance_db_get_len(const uint8_t *key, int key_len); + +status_t app_glance_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t app_glance_db_delete(const uint8_t *key, int key_len); diff --git a/include/pbl/services/blob_db/app_glance_db_private.h b/include/pbl/services/blob_db/app_glance_db_private.h new file mode 100644 index 0000000000..6c14190b40 --- /dev/null +++ b/include/pbl/services/blob_db/app_glance_db_private.h @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/attribute_private.h" +#include "util/attributes.h" + +#define APP_GLANCE_DB_CURRENT_VERSION (1) + +//! This number is reduced for unit tests to avoid creating large glance payloads in the unit tests +#if UNITTEST +#define APP_GLANCE_DB_MAX_SLICES_PER_GLANCE (2) +#else +#define APP_GLANCE_DB_MAX_SLICES_PER_GLANCE (8) +#endif + +#define APP_GLANCE_DB_MAX_NUM_APP_GLANCES (50) + +typedef struct PACKED SerializedAppGlanceHeader { + uint8_t version; + uint32_t creation_time; + uint8_t data[]; // Serialized slices +} SerializedAppGlanceHeader; + +typedef struct PACKED SerializedAppGlanceSliceHeader { + uint16_t total_size; + uint8_t type; + uint8_t num_attributes; + uint8_t data[]; // Serialized attributes +} SerializedAppGlanceSliceHeader; + +//! The minimum size of an AppGlanceSliceType_IconAndSubtitle slice is the size of the header plus +//! the expiration_time because the icon and subtitle are optional +#define APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MIN_SIZE \ + (sizeof(SerializedAppGlanceSliceHeader) + sizeof(SerializedAttributeHeader) + sizeof(uint32_t)) +//! The maximum size of an AppGlanceSliceType_IconAndSubtitle slice is the size of the header plus +//! the expiration_time, icon resource ID, and subtitle string attributes (+1 added for null char) +#define APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MAX_SIZE \ + (sizeof(SerializedAppGlanceSliceHeader) + (sizeof(SerializedAttributeHeader) * 3) + \ + sizeof(uint32_t) + sizeof(uint32_t) + ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1) + +#define APP_GLANCE_DB_SLICE_MIN_SIZE (APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MIN_SIZE) +#define APP_GLANCE_DB_SLICE_MAX_SIZE (APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MAX_SIZE) diff --git a/include/pbl/services/blob_db/contacts_db.h b/include/pbl/services/blob_db/contacts_db.h new file mode 100644 index 0000000000..3af56c7901 --- /dev/null +++ b/include/pbl/services/blob_db/contacts_db.h @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "system/status_codes.h" +#include "util/attributes.h" +#include "util/uuid.h" + +typedef struct PACKED { + Uuid uuid; + uint32_t flags; + uint8_t num_attributes; + uint8_t num_addresses; + uint8_t data[]; // Serialized attributes followed by serialized addresses +} SerializedContact; + +//! Given a contact's uuid, return the serialized data for that contact. This should probably only +//! be called by the contacts service. You probably want contacts_get_contact_by_uuid() instead +//! @param uuid The contact's uuid. +//! @param contact_out A pointer to the serialized contact data, NULL if the contact isn't found. +//! @return The length of the data[] field. +//! @note The caller must cleanup with contacts_db_free_serialized_contact(). +int contacts_db_get_serialized_contact(const Uuid *uuid, SerializedContact **contact_out); + +//! Frees the serialized contact data returned by contacts_db_get_serialized_contact(). +void contacts_db_free_serialized_contact(SerializedContact *contact); + + +/////////////////////////////////////////// +// BlobDB Boilerplate (see blob_db/api.h) +/////////////////////////////////////////// + +void contacts_db_init(void); + +status_t contacts_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int contacts_db_get_len(const uint8_t *key, int key_len); + +status_t contacts_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t contacts_db_delete(const uint8_t *key, int key_len); + +status_t contacts_db_flush(void); + +status_t contacts_db_compact(void); diff --git a/include/pbl/services/blob_db/endpoint.h b/include/pbl/services/blob_db/endpoint.h new file mode 100644 index 0000000000..c2aef3e061 --- /dev/null +++ b/include/pbl/services/blob_db/endpoint.h @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "endpoint_private.h" + +//! Send a write message for the given blob db item. +//! @returns the blob db transaction token +BlobDBToken blob_db_endpoint_send_write(BlobDBId db_id, + time_t last_updated, + const void *key, + int key_len, + const void *val, + int val_len); + +//! Send a WB message for the given blob db item. +//! @returns the blob db transaction token +BlobDBToken blob_db_endpoint_send_writeback(BlobDBId db_id, + time_t last_updated, + const void *key, + int key_len, + const void *val, + int val_len); + +//! Indicate that blob db sync is done for a given db id +void blob_db_endpoint_send_sync_done(BlobDBId db_id); diff --git a/src/fw/services/normal/blob_db/endpoint_private.h b/include/pbl/services/blob_db/endpoint_private.h similarity index 77% rename from src/fw/services/normal/blob_db/endpoint_private.h rename to include/pbl/services/blob_db/endpoint_private.h index 05302dfba8..acc776c924 100644 --- a/src/fw/services/normal/blob_db/endpoint_private.h +++ b/include/pbl/services/blob_db/endpoint_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -56,12 +43,16 @@ typedef enum PACKED { BLOB_DB_COMMAND_WRITE = 0x08, BLOB_DB_COMMAND_WRITEBACK = 0x09, BLOB_DB_COMMAND_SYNC_DONE = 0x0A, + BLOB_DB_COMMAND_VERSION = 0x0B, + BLOB_DB_COMMAND_DIRTY_ALL = 0x0C, + BLOB_DB_COMMAND_INSERT_WITH_TIMESTAMP = 0x0D, // Response commands BLOB_DB_COMMAND_DIRTY_DBS_RESPONSE = BLOB_DB_COMMAND_DIRTY_DBS | RESPONSE_MASK, BLOB_DB_COMMAND_START_SYNC_RESPONSE = BLOB_DB_COMMAND_START_SYNC | RESPONSE_MASK, BLOB_DB_COMMAND_WRITE_RESPONSE = BLOB_DB_COMMAND_WRITE | RESPONSE_MASK, BLOB_DB_COMMAND_WRITEBACK_RESPONSE = BLOB_DB_COMMAND_WRITEBACK | RESPONSE_MASK, BLOB_DB_COMMAND_SYNC_DONE_RESPONSE = BLOB_DB_COMMAND_SYNC_DONE | RESPONSE_MASK, + BLOB_DB_COMMAND_VERSION_RESPONSE = BLOB_DB_COMMAND_VERSION | RESPONSE_MASK, } BlobDBCommand; _Static_assert(sizeof(BlobDBCommand) == 1, "BlobDBCommand is larger than 1 byte"); diff --git a/include/pbl/services/blob_db/health_db.h b/include/pbl/services/blob_db/health_db.h new file mode 100644 index 0000000000..272fcbbb20 --- /dev/null +++ b/include/pbl/services/blob_db/health_db.h @@ -0,0 +1,49 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/activity/activity.h" +#include "system/status_codes.h" +#include "util/attributes.h" + + +//! Get the typical metric value for a given day. +//! If you want "typical steps" you probably want health_db_get_typical_step_averages +bool health_db_get_typical_value(ActivityMetric metric, + DayInWeek day, + int32_t *value_out); + +//! Get the average metric value over the last month +bool health_db_get_monthly_average_value(ActivityMetric metric, + int32_t *value_out); + +//! Often referred to as "typical steps" +bool health_db_get_typical_step_averages(DayInWeek day, + ActivityMetricAverages *averages); + + + +//! For test / debug purposes only +bool health_db_set_typical_values(ActivityMetric metric, + DayInWeek day, + uint16_t *values, + int num_values); + +/////////////////////////////////////////// +// BlobDB Boilerplate (see blob_db/api.h) +/////////////////////////////////////////// + +void health_db_init(void); + +status_t health_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int health_db_get_len(const uint8_t *key, int key_len); + +status_t health_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t health_db_delete(const uint8_t *key, int key_len); + +status_t health_db_flush(void); + +status_t health_db_compact(void); diff --git a/src/fw/services/normal/blob_db/ios_notif_pref_db.h b/include/pbl/services/blob_db/ios_notif_pref_db.h similarity index 76% rename from src/fw/services/normal/blob_db/ios_notif_pref_db.h rename to include/pbl/services/blob_db/ios_notif_pref_db.h index 99089627b5..9816c0279e 100644 --- a/src/fw/services/normal/blob_db/ios_notif_pref_db.h +++ b/include/pbl/services/blob_db/ios_notif_pref_db.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "api.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/item.h" #include "system/status_codes.h" @@ -70,6 +57,8 @@ status_t ios_notif_pref_db_delete(const uint8_t *key, int key_len); status_t ios_notif_pref_db_flush(void); +status_t ios_notif_pref_db_compact(void); + status_t ios_notif_pref_db_is_dirty(bool *is_dirty_out); BlobDBDirtyItem* ios_notif_pref_db_get_dirty_list(void); diff --git a/include/pbl/services/blob_db/notif_db.h b/include/pbl/services/blob_db/notif_db.h new file mode 100644 index 0000000000..bf68f28948 --- /dev/null +++ b/include/pbl/services/blob_db/notif_db.h @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "system/status_codes.h" +#include "pbl/services/timeline/item.h" + +/////////////////////////////////////////// +// BlobDB Boilerplate (see blob_db/api.h) +/////////////////////////////////////////// + +void notif_db_init(void); + +status_t notif_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int notif_db_get_len(const uint8_t *key, int key_len); + +status_t notif_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t notif_db_delete(const uint8_t *key, int key_len); + +status_t notif_db_flush(void); diff --git a/src/fw/services/normal/blob_db/pin_db.h b/include/pbl/services/blob_db/pin_db.h similarity index 76% rename from src/fw/services/normal/blob_db/pin_db.h rename to include/pbl/services/blob_db/pin_db.h index 9b9777e196..e0d9afaff8 100644 --- a/src/fw/services/normal/blob_db/pin_db.h +++ b/include/pbl/services/blob_db/pin_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "timeline_item_storage.h" #include "system/status_codes.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" #include "util/iterator.h" #include @@ -71,6 +58,8 @@ status_t pin_db_delete(const uint8_t *key, int key_len); status_t pin_db_flush(void); +status_t pin_db_compact(void); + status_t pin_db_is_dirty(bool *is_dirty_out); BlobDBDirtyItem* pin_db_get_dirty_list(void); diff --git a/include/pbl/services/blob_db/prefs_db.h b/include/pbl/services/blob_db/prefs_db.h new file mode 100644 index 0000000000..0c7c5252a7 --- /dev/null +++ b/include/pbl/services/blob_db/prefs_db.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "system/status_codes.h" + +/////////////////////////////////////////// +// BlobDB Boilerplate (see blob_db/api.h) +/////////////////////////////////////////// + +void prefs_db_init(void); + +status_t prefs_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int prefs_db_get_len(const uint8_t *key, int key_len); + +status_t prefs_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t prefs_db_delete(const uint8_t *key, int key_len); + +status_t prefs_db_flush(void); diff --git a/src/fw/services/normal/blob_db/reminder_db.h b/include/pbl/services/blob_db/reminder_db.h similarity index 83% rename from src/fw/services/normal/blob_db/reminder_db.h rename to include/pbl/services/blob_db/reminder_db.h index 9457ed75be..cd5307297f 100644 --- a/src/fw/services/normal/blob_db/reminder_db.h +++ b/include/pbl/services/blob_db/reminder_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "timeline_item_storage.h" #include "system/status_codes.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" // reminderdb specific @@ -86,6 +73,8 @@ status_t reminder_db_delete(const uint8_t *key, int key_len); status_t reminder_db_flush(void); +status_t reminder_db_compact(void); + status_t reminder_db_is_dirty(bool *is_dirty_out); BlobDBDirtyItem* reminder_db_get_dirty_list(void); diff --git a/include/pbl/services/blob_db/settings_blob_db.h b/include/pbl/services/blob_db/settings_blob_db.h new file mode 100644 index 0000000000..4b1446ce99 --- /dev/null +++ b/include/pbl/services/blob_db/settings_blob_db.h @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "api.h" + +//! Settings BlobDB - wraps SettingsFile to provide BlobDB interface +//! +//! This allows settings to sync using the existing BlobDB protocol, +//! so the mobile app can reuse its BlobDB sync implementation. +//! +//! Only whitelisted settings are synced (see settings_blob_db.c for list). + +//! Initialize the settings BlobDB +void settings_blob_db_init(void); + +//! Insert/update a setting +status_t settings_blob_db_insert(const uint8_t *key, int key_len, + const uint8_t *val, int val_len); + +//! Get the length of a setting value +int settings_blob_db_get_len(const uint8_t *key, int key_len); + +//! Read a setting value +status_t settings_blob_db_read(const uint8_t *key, int key_len, + uint8_t *val_out, int val_len); + +//! Delete a setting +status_t settings_blob_db_delete(const uint8_t *key, int key_len); + +//! Get list of dirty (unsynced) settings +BlobDBDirtyItem *settings_blob_db_get_dirty_list(void); + +//! Mark a setting as synced +status_t settings_blob_db_mark_synced(const uint8_t *key, int key_len); + +//! Check if there are dirty settings +status_t settings_blob_db_is_dirty(bool *is_dirty_out); + +//! Flush settings to disk +status_t settings_blob_db_flush(void); + +//! Mark all whitelisted settings as dirty (unsynced) +//! This triggers a full sync of all settings to the phone +status_t settings_blob_db_mark_all_dirty(void); + +//! Insert/update a setting only if the incoming timestamp is newer or equal +//! @param key the setting key +//! @param key_len length of the key +//! @param val the value to insert +//! @param val_len length of the value +//! @param timestamp the timestamp of the incoming data +//! @return S_SUCCESS if inserted, E_INVALID_OPERATION if watch data is newer (stale) +status_t settings_blob_db_insert_with_timestamp(const uint8_t *key, int key_len, + const uint8_t *val, int val_len, + time_t timestamp); + +//! Check if the connected phone supports Settings BlobDB sync +//! @return true if phone advertises settings_sync_support capability +bool settings_blob_db_phone_supports_sync(void); diff --git a/include/pbl/services/blob_db/sync.h b/include/pbl/services/blob_db/sync.h new file mode 100644 index 0000000000..e0d285eda9 --- /dev/null +++ b/include/pbl/services/blob_db/sync.h @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "api.h" +#include "endpoint.h" + +#include "pbl/services/regular_timer.h" + +typedef enum { + BlobDBSyncSessionStateIdle = 0, + BlobDBSyncSessionStateWaitingForAck = 1, +} BlobDBSyncSessionState; + +typedef enum { + BlobDBSyncSessionTypeDB, + BlobDBSyncSessionTypeRecord, +} BlobDBSyncSessionType; + +typedef struct { + ListNode node; + BlobDBSyncSessionState state; + BlobDBId db_id; + BlobDBDirtyItem *dirty_list; + RegularTimerInfo timeout_timer; + RegularTimerInfo abandon_timer; + BlobDBToken current_token; + BlobDBSyncSessionType session_type; +} BlobDBSyncSession; + +//! Start sync-ing a blobdb. +//! @param db_id the BlobDBId of the database to sync +status_t blob_db_sync_db(BlobDBId db_id); + +//! Start sync-ing a key within a blobdb. +//! @param db_id the BlobDBId of the database to sync +//! @param key the key to sync +//! @param key_len the length of the key to sync +status_t blob_db_sync_record(BlobDBId db_id, const void *key, int key_len, time_t last_updated); + +//! Get the sync session for a given ID. Will NOT return sessions for individual records +//! returns NULL if no sync is in progress +BlobDBSyncSession *blob_db_sync_get_session_for_id(BlobDBId db_id); + +//! Get the sync session currently waiting for a response with the given token +//! return NULL if no sync is in progress +BlobDBSyncSession *blob_db_sync_get_session_for_token(BlobDBToken token); + +//! Mark current item as synced and sync the next one +void blob_db_sync_next(BlobDBSyncSession *session); + +//! Cancel the sync in progress. Pending items will be synced next time. +void blob_db_sync_cancel(BlobDBSyncSession *session); diff --git a/include/pbl/services/blob_db/sync_util.h b/include/pbl/services/blob_db/sync_util.h new file mode 100644 index 0000000000..8dafda18c5 --- /dev/null +++ b/include/pbl/services/blob_db/sync_util.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/blob_db/api.h" +#include "pbl/services/settings/settings_file.h" + +// Caution: CommonTimelineItemHeader .flags & .status are stored inverted and not auto-restored +// by the underlying db API. If .flags or .status is used from a CommonTimelineItemHeader below, +// be very careful. + +//! A settings file each callback which checks if the there are dirty records in the file +//! @param context The address of a bool which will get set +bool sync_util_is_dirty_cb(SettingsFile *file, SettingsRecordInfo *info, void *context); + +//! A settings file each callback which builds a BlobDBDirtyItem list +//! @param context The address of an empty dirty list which will get built +bool sync_util_build_dirty_list_cb(SettingsFile *file, SettingsRecordInfo *info, void *context); diff --git a/src/fw/services/normal/blob_db/timeline_item_storage.h b/include/pbl/services/blob_db/timeline_item_storage.h similarity index 79% rename from src/fw/services/normal/blob_db/timeline_item_storage.h rename to include/pbl/services/blob_db/timeline_item_storage.h index 887ea9bb14..9d646a99f1 100644 --- a/src/fw/services/normal/blob_db/timeline_item_storage.h +++ b/include/pbl/services/blob_db/timeline_item_storage.h @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "util/uuid.h" -#include "services/common/regular_timer.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/settings/settings_file.h" #include "os/mutex.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" typedef struct { SettingsFile file; @@ -44,6 +31,9 @@ void timeline_item_storage_init(TimelineItemStorage *storage, void timeline_item_storage_deinit(TimelineItemStorage *storage); +//! Compact and shrink the underlying settings file. +status_t timeline_item_storage_compact(TimelineItemStorage *storage); + bool timeline_item_storage_exists_with_parent(TimelineItemStorage *storage, const Uuid *parent_id); status_t timeline_item_storage_flush(TimelineItemStorage *storage); diff --git a/include/pbl/services/blob_db/util.h b/include/pbl/services/blob_db/util.h new file mode 100644 index 0000000000..72c2ad40ac --- /dev/null +++ b/include/pbl/services/blob_db/util.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "api.h" + +void blob_db_util_free_dirty_list(BlobDBDirtyItem *dirty_list); diff --git a/include/pbl/services/blob_db/watch_app_prefs_db.h b/include/pbl/services/blob_db/watch_app_prefs_db.h new file mode 100644 index 0000000000..4b9628a13d --- /dev/null +++ b/include/pbl/services/blob_db/watch_app_prefs_db.h @@ -0,0 +1,47 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "apps/system/send_text/prefs.h" +#include "apps/system/reminders/reminder_prefs.h" +#include "pbl/services/weather/weather_service_private.h" +#include "system/status_codes.h" + +// Reads the Send Text app prefs from the db +// @return Pointer to new \ref SerializedSendTextPrefs, NULL on failure +// @note task_free() must be called on the pointer when done with the memory +SerializedSendTextPrefs *watch_app_prefs_get_send_text(void); + +// Reads the Weather app location ordering from the db +// @return pointer to new \ref SerializedWeatherAppPrefs, NULL on failure +// @note use weather_app_prefs_destroy_weather() to free memory allocated by this method +SerializedWeatherAppPrefs *watch_app_prefs_get_weather(void); + +// Reads the Reminder App prefs from the db +// @return pointer to new \ref SerializedRemindersAppPrefs, NULL on failure +// @note task_free() must be called on the pointer when done with the memory +SerializedReminderAppPrefs *watch_app_prefs_get_reminder(void); + +// Frees memory allocated from watch_app_prefs_get_weather() +void watch_app_prefs_destroy_weather(SerializedWeatherAppPrefs *prefs); + +/////////////////////////////////////////// +// BlobDB Boilerplate (see blob_db/api.h) +/////////////////////////////////////////// + +void watch_app_prefs_db_init(void); + +status_t watch_app_prefs_db_insert(const uint8_t *key, int key_len, const uint8_t *val, + int val_len); + +int watch_app_prefs_db_get_len(const uint8_t *key, int key_len); + +status_t watch_app_prefs_db_read(const uint8_t *key, int key_len, uint8_t *val_out, + int val_out_len); + +status_t watch_app_prefs_db_delete(const uint8_t *key, int key_len); + +status_t watch_app_prefs_db_flush(void); + +status_t watch_app_prefs_db_compact(void); diff --git a/include/pbl/services/blob_db/weather_db.h b/include/pbl/services/blob_db/weather_db.h new file mode 100644 index 0000000000..7a26b746e2 --- /dev/null +++ b/include/pbl/services/blob_db/weather_db.h @@ -0,0 +1,68 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_types.h" +#include "system/status_codes.h" +#include "util/attributes.h" +#include "util/pstring.h" +#include "util/time/time.h" +#include "util/uuid.h" + +#include + +#define WEATHER_DB_CURRENT_VERSION (3) + +typedef Uuid WeatherDBKey; + +typedef struct PACKED { + uint8_t version; + int16_t current_temp; + WeatherType current_weather_type; + int16_t today_high_temp; + int16_t today_low_temp; + WeatherType tomorrow_weather_type; + int16_t tomorrow_high_temp; + int16_t tomorrow_low_temp; + time_t last_update_time_utc; + bool is_current_location; + SerializedArray pstring16s; +} WeatherDBEntry; + +typedef enum WeatherDbStringIndex { + WeatherDbStringIndex_LocationName, + WeatherDbStringIndex_ShortPhrase, + WeatherDbStringIndexCount, +} WeatherDbStringIndex; + +#define MIN_ENTRY_SIZE (sizeof(WeatherDBEntry)) +#define MAX_ENTRY_SIZE (MIN_ENTRY_SIZE + \ + WEATHER_SERVICE_MAX_WEATHER_LOCATION_BUFFER_SIZE + \ + WEATHER_SERVICE_MAX_SHORT_PHRASE_BUFFER_SIZE) + +// Memory ownership: pointer to key and entry must not be saved, as they become invalid after +// the callback finishes +typedef void (*WeatherDBIteratorCallback)(WeatherDBKey *key, WeatherDBEntry *entry, void *context); + +// ------------------------------------------------------------------------------------ +// WeatherDB functions +status_t weather_db_for_each(WeatherDBIteratorCallback cb, void *context); + +// ------------------------------------------------------------------------------------ +// BlobDB Implementation + +void weather_db_init(void); + +status_t weather_db_flush(void); + +status_t weather_db_compact(void); + +status_t weather_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); + +int weather_db_get_len(const uint8_t *key, int key_len); + +status_t weather_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); + +status_t weather_db_delete(const uint8_t *key, int key_len); diff --git a/include/pbl/services/bluetooth/ble_bas.h b/include/pbl/services/bluetooth/ble_bas.h new file mode 100644 index 0000000000..638974dc3f --- /dev/null +++ b/include/pbl/services/bluetooth/ble_bas.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +void ble_bas_init(void); + +void ble_bas_deinit(void); \ No newline at end of file diff --git a/include/pbl/services/bluetooth/ble_hrm.h b/include/pbl/services/bluetooth/ble_hrm.h new file mode 100644 index 0000000000..7bb785f040 --- /dev/null +++ b/include/pbl/services/bluetooth/ble_hrm.h @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "util/time/time.h" + +#define BLE_HRM_REMINDER_POPUP_DELAY_MINS (2 * MINUTES_PER_HOUR) + +typedef struct GAPLEConnection GAPLEConnection; + +typedef struct BLEHRMSharingRequest BLEHRMSharingRequest; + +//! Called by the ble_hrm_sharing_popup upon the user's action to grant or decline the sharing. +//! @note Also cleans up the sharing_request object. +void ble_hrm_handle_sharing_request_response(bool is_granted, + BLEHRMSharingRequest *sharing_request); + +bool ble_hrm_is_supported_and_enabled(void); + +bool ble_hrm_is_sharing_to_connection(const GAPLEConnection *connection); + +bool ble_hrm_is_sharing(void); + +void ble_hrm_revoke_sharing_permission_for_connection(GAPLEConnection *connection); + +void ble_hrm_revoke_all(void); + +void ble_hrm_handle_activity_prefs_heart_rate_is_enabled(bool is_enabled); + +void ble_hrm_handle_disconnection(GAPLEConnection *connection); + +void ble_hrm_init(void); + +void ble_hrm_deinit(void); diff --git a/include/pbl/services/bluetooth/bluetooth_ctl.h b/include/pbl/services/bluetooth/bluetooth_ctl.h new file mode 100644 index 0000000000..7e05e3e0da --- /dev/null +++ b/include/pbl/services/bluetooth/bluetooth_ctl.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef enum { + BtCtlModeOverrideNone, + BtCtlModeOverrideStop, + BtCtlModeOverrideRun +} BtCtlModeOverride; + +void bt_ctl_init(void); + +//! returns the airplane mode state +bool bt_ctl_is_airplane_mode_on(void); + +//! Returns whether the bluetooth stack is supposed to be up and running (but might not because it's +//! still starting or in the middle of resetting). +bool bt_ctl_is_bluetooth_active(void); + +//! Returns whether the bluetooth stack is up and running or not. +bool bt_ctl_is_bluetooth_running(void); + +// The following three functions are used for setting the flags that define the state of +// the bluetooth stack. + +//! Sets the airplane mode flag. The flag is persisted across reboots +void bt_ctl_set_airplane_mode_async(bool enabled); + +//! Sets enable flag (used by the runlevel system). +void bt_ctl_set_enabled(bool enabled); + +//! Sets the override mode used to stop and start the bluetooth independent of the airplane mode. +void bt_ctl_set_override_mode(BtCtlModeOverride override); + +//! Reset bluetoosh using sequential calls to comm_stop() and comm_start() +void bt_ctl_reset_bluetooth(void); diff --git a/include/pbl/services/bluetooth/bluetooth_persistent_storage.h b/include/pbl/services/bluetooth/bluetooth_persistent_storage.h new file mode 100644 index 0000000000..dd8fe4a57c --- /dev/null +++ b/include/pbl/services/bluetooth/bluetooth_persistent_storage.h @@ -0,0 +1,165 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/comm_session/session_remote_version.h" + +#include +#include +#include +#include + +//! @file bluetooth_persistent_storage.h +//! Future file-based database for Bluetooth related credentials, cached data, etc. +//! The idea is to replace the deprecated, registry-based "remote_prefs.c" and "known_device_list.c" +//! For now this module contains temporary implementations that use the legacy registry. + +typedef enum { + BtPersistBondingOpInvalid = -1, + BtPersistBondingOpDidAdd, + BtPersistBondingOpDidChange, + BtPersistBondingOpWillDelete, +} BtPersistBondingOp; + +typedef enum { + BtPersistBondingTypeBTClassic, + BtPersistBondingTypeBLE, + BtPersistBondingNumTypes +} BtPersistBondingType; + + +//! Signature of function that handles changes in the pairing database +typedef void (*BtPersistBondingChangeHandler)(BTBondingID affected_bonding, + BtPersistBondingOp operation); + +typedef void (*BtPersistBondingDBEachBLE)(BTDeviceInternal *device, SMIdentityResolvingKey *irk, + const char *name, BTBondingID *id, void *context); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! BLE Pairing Info + +bool bt_persistent_storage_has_pinned_ble_pairings(void); + +bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *address); + +bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out); + +BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *pairing_info, + bool is_gateway, const char *device_name, + bool requires_address_pinning, + uint8_t flags); + +bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name); + +void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID); + +void bt_persistent_storage_delete_ble_pairing_by_addr(const BTDeviceInternal *device); + +bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding, + SMIdentityResolvingKey *IRK_out, + BTDeviceInternal *device_out, + char *name_out); + +bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device, + SMIdentityResolvingKey *IRK_out, + char name_out[BT_DEVICE_NAME_BUFFER_SIZE]); + +//! Returns the first ANCS supported bonding that is found +//! The case of having multiple supported ANCS bondings isn't handled well yet. +//! When this happens this could easily be changed to a for_each_ancs_supported_bonding(cb) +BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void); + +//! Returns true if the bondings is BLE and supports ANCS +bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding); + +//! Returns true if there exists a BLE bonding which supports ANCS +bool bt_persistent_storage_has_ble_ancs_bonding(void); + +//! Returns true if the active gateway uses BLE +//! [PG]: This will currently always return false until PPoGATT is supported +bool bt_persistent_storage_has_active_ble_gateway_bonding(void); + +//! Runs the callback for each BLE pairing +//! The callback is NOT allowed to aquire the bt_lock() (or we could deadlock). +void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context); + +//! Registers all the existing BLE bondings with the BT driver lib. +void bt_persistent_storage_register_existing_ble_bondings(void); + +BTCCCDID bt_persistent_storage_store_cccd(const BleCCCD *cccd); + +bool bt_persistent_storage_delete_cccd(const BTDeviceInternal *peer, uint16_t chr_val_handle); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Local Device Info + +//! Updates the active gateway (the gateway which implements PP) +//! This bonding is used for BT Classic reconnection as well +//! @param bonding The desired active gateway +void bt_persistent_storage_set_active_gateway(BTBondingID bonding); + +//! Returns false if no active gateway exists, true if one does exist +//! bonding_out and type_out are only valid when this function returns true; +bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out, + BtPersistBondingType *type_out); + +//! Returns true when the active gateway is changed until a sync happens +bool bt_persistent_storage_is_unfaithful(void); + +//! Marks the device as being unfaithful +void bt_persistent_storage_set_unfaithful(bool is_unfaithful); + +//! Copies the BLE Encryption Root (ER) or Identity Root (IR) keys out of storage +//! @param key_out Storage into which ER or IR should be copied. +//! @param key_type The type of key to copy +//! @return true if ER and IR are copied, false if there are no keys have been found to copy. +bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out); + +//! Stores new BLE Encryption Root (ER) and Identity Root (IR) keys +void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in); + +//! @param local_device_name_out Storage for the local device name. +//! @param max_size Size of the local_device_name_out buffer +//! @return true if there is a valid local device name stored, otherwise false (a zero-length string +bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size); + +//! Stores the customized local device name +//! @param local_device_name The device name to store +//! @param size The size of the string +void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t max_size); + +//! Retrieve the airplane mode setting +//! @return the stored airplane mode flag +bool bt_persistent_storage_get_airplane_mode_enabled(void); + +//! Store the airplane mode setting +//! @param the airplane mode state to be saved +void bt_persistent_storage_set_airplane_mode_enabled(bool enable); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Remote Device Info + +//! Retrieve the most recent system session capabilities +//! @param capabilities_out Storage for system session capabilities +//! @note It's preferable to use \ref comm_session_get_capabilities when possible +void bt_persistent_storage_get_cached_system_capabilities( + PebbleProtocolCapabilities *capabilities_out); + +//! Store the most recent system session capabilities +//! @param capabilities The capability flags to be saved (cache will be cleared if NULL) +void bt_persistent_storage_set_cached_system_capabilities( + const PebbleProtocolCapabilities *capabilities); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Common + +void bt_persistent_storage_init(void); + +//! This will not delete the local device info, only pairings +void bt_persistent_storage_delete_all_pairings(void); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Unit testing +int bt_persistent_storage_get_raw_data(const void *key, size_t key_len, + void *data_out, size_t buf_len); diff --git a/include/pbl/services/bluetooth/bluetooth_persistent_storage_debug.h b/include/pbl/services/bluetooth/bluetooth_persistent_storage_debug.h new file mode 100644 index 0000000000..a55419c514 --- /dev/null +++ b/include/pbl/services/bluetooth/bluetooth_persistent_storage_debug.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include + +#define DISPLAY_BUF_LEN 160 + +void bluetooth_persistent_storage_debug_dump_ble_pairing_info( + char *display_buf, const SMPairingInfo *info); + +void bluetooth_persistent_storage_debug_dump_classic_pairing_info( + char *display_buf, BTDeviceAddress *addr, char *device_name, SM128BitKey *link_key, + uint8_t platform_bits); + +void bluetooth_persistent_storage_debug_dump_root_keys(SM128BitKey *irk, SM128BitKey *erk); diff --git a/src/fw/services/normal/bluetooth/bluetooth_persistent_storage_v2_impl.h b/include/pbl/services/bluetooth/bluetooth_persistent_storage_v2_impl.h similarity index 83% rename from src/fw/services/normal/bluetooth/bluetooth_persistent_storage_v2_impl.h rename to include/pbl/services/bluetooth/bluetooth_persistent_storage_v2_impl.h index 3081011d96..fad0a841db 100644 --- a/src/fw/services/normal/bluetooth/bluetooth_persistent_storage_v2_impl.h +++ b/include/pbl/services/bluetooth/bluetooth_persistent_storage_v2_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/bluetooth/dis.h b/include/pbl/services/bluetooth/dis.h new file mode 100644 index 0000000000..1168c300b2 --- /dev/null +++ b/include/pbl/services/bluetooth/dis.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef struct DisInfo DisInfo; + +void dis_get_info(DisInfo *info); diff --git a/include/pbl/services/bluetooth/local_addr.h b/include/pbl/services/bluetooth/local_addr.h new file mode 100644 index 0000000000..261e6c8744 --- /dev/null +++ b/include/pbl/services/bluetooth/local_addr.h @@ -0,0 +1,29 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" + +typedef struct BTDeviceAddress BTDeviceAddress; + +//! Pauses cycling of local Private Resolvable Address (ref counted). +//! As long as the cycling is paused, the address that is used "on air" will be stable for the +//! duration that the BT stack is up (so the address can be expected to have changed after rebooting +//! or resetting the stack). +//! In case the local address is currently pinned, this function will be a no-op. +void bt_local_addr_pause_cycling(void); + +//! Resumes cycling of local Private Resolvable Address (ref counted). +//! In case the local address is currently pinned, this function will be a no-op. +void bt_local_addr_resume_cycling(void); + +//! Called by BT driver to indicate what the local address was that was used during the pairing +//! and pinning was requested. See comment in the implementation for more details. +void bt_local_addr_pin(const BTDeviceAddress *addr); + +//! Handler for bonding changes (deletions primarily). +void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op); + +//! Called during the BT stack initialization. +void bt_local_addr_init(void); diff --git a/include/pbl/services/bluetooth/local_id.h b/include/pbl/services/bluetooth/local_id.h new file mode 100644 index 0000000000..0b164ed7b4 --- /dev/null +++ b/include/pbl/services/bluetooth/local_id.h @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +//! Called by bl_ctl right after the stack starts, to configure the local device name and address. +void bt_local_id_configure_driver(void); + +//! Sets a new device name, overriding the existing (default) one. +//! The name will be truncated to BT_DEVICE_NAME_BUFFER_SIZE - 1 characters. +void bt_local_id_set_device_name(const char *device_name); + +//! Copies the name of the local device into the given buffer. +//! @param is_le Only consumed if the device used is dual mode. If so, +// this changes the name returned +void bt_local_id_copy_device_name(char name_out[BT_DEVICE_NAME_BUFFER_SIZE], bool is_le); + +//! Copies the address of the local device. +void bt_local_id_copy_address(BTDeviceAddress *addr_out); + +//! Copies a hex-formatted string representation ("0x000000000000") of the device address into the +//! given buffer. The buffer should be at least BT_ADDR_FMT_BUFFER_SIZE_BYTES bytes in size to fit +//! the address string. +//! If there is no local address known, the string "Unknown" will be copied into the buffer. +void bt_local_id_copy_address_hex_string(char addr_hex_str_out[BT_ADDR_FMT_BUFFER_SIZE_BYTES]); + +//! Copies a MAC-formatted string representation ("00:00:00:00:00:00") of the device address into +//! the given buffer. The buffer should be at least BT_ADDR_FMT_BUFFER_SIZE_BYTES bytes in size to +//! fit the address string. +void bt_local_id_copy_address_mac_string(char addr_mac_str_out[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]); + +//! Generates a BTDeviceAddress from the serial number of the watch +void bt_local_id_generate_address_from_serial(BTDeviceAddress *addr_out); diff --git a/include/pbl/services/bluetooth/pairability.h b/include/pbl/services/bluetooth/pairability.h new file mode 100644 index 0000000000..d3065c0998 --- /dev/null +++ b/include/pbl/services/bluetooth/pairability.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Reference counted request to allow us to be discovered and paired with over BLE. +void bt_pairability_use(void); + +//! Reference counted request to allow us to be discovered and paired with over BLE. +void bt_pairability_use_ble(void); + +//! Reference counted request to allow us to be discovered and paired with over BLE for a specific +//! period, after which bt_pairability_release_ble will be called automatically. +void bt_pairability_use_ble_for_period(uint16_t duration_secs); + +//! Reference counted request to disallow us to be discovered and paired with over BLE. +void bt_pairability_release(void); + +//! Reference counted request to disallow us to be discovered and paired with over BLE. +void bt_pairability_release_ble(void); + +//! Evaluates whether there are any bondings to gateways. If there are none, make the system +//! discoverable and pairable. +void bt_pairability_update_due_to_bonding_change(void); + +void bt_pairability_init(void); diff --git a/include/pbl/services/bluetooth/pp_ble_control.h b/include/pbl/services/bluetooth/pp_ble_control.h new file mode 100644 index 0000000000..55bb536806 --- /dev/null +++ b/include/pbl/services/bluetooth/pp_ble_control.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/comm_session/session.h" + +void pp_ble_control_protocol_msg_callback(CommSession* session, + const uint8_t *data, + unsigned int length); diff --git a/include/pbl/services/boot_splash.h b/include/pbl/services/boot_splash.h new file mode 100644 index 0000000000..075a375e9e --- /dev/null +++ b/include/pbl/services/boot_splash.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Start the boot splash screen. +//! On platforms with software-rendered splash (PBLBOOT), this shows +//! an animated splash with a spinning indicator. On other platforms, this +//! calls the display driver's splash screen function +void boot_splash_start(void); + +//! Stop the boot splash screen. +//! This should be called when the compositor is ready to take over the display. +//! On platforms with animated splash, this stops the animation task. +//! On other platforms, this is a no-op. +void boot_splash_stop(void); diff --git a/src/fw/services/common/clock.h b/include/pbl/services/clock.h similarity index 85% rename from src/fw/services/common/clock.h rename to include/pbl/services/clock.h index e26e0286c9..075010c5f9 100644 --- a/src/fw/services/common/clock.h +++ b/include/pbl/services/clock.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -116,6 +103,25 @@ bool clock_timezone_source_is_manual(void); //! false means the phone's timezone will be used void clock_set_manual_timezone_source(bool manual); +//! @internal +//! Checks the time source. If the source is manual, the user must set the time from +//! the settings menu, if the source is automatic the time will be set by the phone. +//! @return true if time is set manually, false if the time is set by the phone +bool clock_time_source_is_manual(void); + +//! @internal +//! Sets the time source. If the source is manual, the user must set the time from +//! the settings menu, if the source is automatic the time will be set by the phone. +//! @param manual True means time is set manually on the watch, +//! false means the phone's time will be used +void clock_set_manual_time_source(bool manual); + +//! @internal +//! Sends a request to the phone to send its current time. +//! The phone should respond with a set UTC + timezone message (sub-command 0x03). +//! Does nothing if there is no active system session (phone not connected). +void clock_request_time_from_phone(void); + //! @internal //! If timezone is set, copies the current timezone long name (e.g. America/Chicago) //! to buffer region_name. @@ -132,12 +138,15 @@ int16_t clock_get_timezone_region_id(void); //! @param region_id the index of the selected timezone in terms of the timezone database void clock_set_timezone_by_region_id(uint16_t region_id); +//! @internal +//! Sets the current UTC time and fires a time-change event. +//! @param utc_time the new UTC epoch timestamp to set +void clock_set_time(time_t utc_time); + //! Converts a (day, hour, minute) specification to a UTC timestamp occurring in the future //! Always returns a timestamp for the next occurring instance, //! example: specifying TODAY@14:30 when it is 14:40 will return a timestamp for 7 days from //! now at 14:30 -//! @note This function does not support Daylight Saving Time (DST) changes, events scheduled -//! during a DST change will be off by an hour. //! @param day WeekDay day of week including support for specifying TODAY //! @param hour hour specified in 24-hour format [0-23] //! @param minute minute [0-59] @@ -174,6 +183,11 @@ void clock_get_until_time_without_fulltime(char *buffer, int buf_size, time_t ti //! Get the date in MM/DD format size_t clock_get_date(char *buffer, int buf_size, time_t timestamp); +//! Same as \ref clock_get_date, but takes a pre-computed `struct tm` instead of +//! a `time_t`. Useful in tick handlers, where the framework already passes a +//! `struct tm` and an extra `localtime_r` round-trip would be wasteful. +size_t clock_get_date_tm(char *buffer, int buf_size, const struct tm *time_tm); + //! Get the day date in DD format size_t clock_get_day_date(char *buffer, int buf_size, time_t timestamp); diff --git a/include/pbl/services/comm_session/app_session_capabilities.h b/include/pbl/services/comm_session/app_session_capabilities.h new file mode 100644 index 0000000000..e74005598e --- /dev/null +++ b/include/pbl/services/comm_session/app_session_capabilities.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/comm_session/session.h" +#include "util/uuid.h" + +//! @param capability The capability to check for. +//! @returns True if the session for the current application supports the capability of interest. +//! If the session is currently not connected, it will use cached data. If no cache exists +//! and the session is not connected, false will be returned. +bool comm_session_current_app_session_cache_has_capability(CommSessionCapability capability); + +//! Removes the cached app session capabilities for app with specified uuid. +void comm_session_app_session_capabilities_evict(const Uuid *app_uuid); + +void comm_session_app_session_capabilities_init(void); diff --git a/include/pbl/services/comm_session/default_kernel_sender.h b/include/pbl/services/comm_session/default_kernel_sender.h new file mode 100644 index 0000000000..ecda82570b --- /dev/null +++ b/include/pbl/services/comm_session/default_kernel_sender.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +void comm_default_kernel_sender_init(void); diff --git a/include/pbl/services/comm_session/meta_endpoint.h b/include/pbl/services/comm_session/meta_endpoint.h new file mode 100644 index 0000000000..aa9aac2d3f --- /dev/null +++ b/include/pbl/services/comm_session/meta_endpoint.h @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/comm_session/session.h" +#include "util/attributes.h" + +#include + +typedef enum { + MetaResponseCodeNoError = 0x0, + MetaResponseCodeCorruptedMessage = 0xd0, + MetaResponseCodeDisallowed = 0xdd, + MetaResponseCodeUnhandled = 0xdc, +} MetaResponseCode; + +typedef struct MetaResponseInfo { + CommSession *session; + struct PACKED { + //! @see MetaResponseCode + uint8_t error_code; + uint16_t endpoint_id; + } payload; +} MetaResponseInfo; + +//! Sends out a response for the "meta" endpoint, asynchronously on KernelBG. +//! @note The endpoint_id must be set in Little Endian byte order. This function will take care +//! of swapping it to the correct endianness. +void meta_endpoint_send_response_async(const MetaResponseInfo *meta_response_info); diff --git a/include/pbl/services/comm_session/protocol.h b/include/pbl/services/comm_session/protocol.h new file mode 100644 index 0000000000..47daa8ed97 --- /dev/null +++ b/include/pbl/services/comm_session/protocol.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +typedef struct PACKED { + uint16_t length; + uint16_t endpoint_id; +} PebbleProtocolHeader; + +#define COMM_PRIVATE_MAX_INBOUND_PAYLOAD_SIZE 2044 +#define COMM_PUBLIC_MAX_INBOUND_PAYLOAD_SIZE 144 +// TODO: If we have memory to spare, let's crank this up to improve data spooling +#define COMM_MAX_OUTBOUND_PAYLOAD_SIZE 656 diff --git a/src/fw/services/common/comm_session/session.h b/include/pbl/services/comm_session/session.h similarity index 91% rename from src/fw/services/common/comm_session/session.h rename to include/pbl/services/comm_session/session.h index 52fd0e3611..719f44a4ae 100644 --- a/src/fw/services/common/comm_session/session.h +++ b/include/pbl/services/comm_session/session.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -59,6 +46,7 @@ typedef enum { CommSessionRemindersAppSupport = 1 << 12, CommSessionWorkoutAppSupport = 1 << 13, CommSessionSmoothFwInstallProgressSupport = 1 << 14, + CommSessionSettingsSyncSupport = 1 << 23, CommSessionOutOfRange } CommSessionCapability; diff --git a/include/pbl/services/comm_session/session_analytics.h b/include/pbl/services/comm_session/session_analytics.h new file mode 100644 index 0000000000..229c133395 --- /dev/null +++ b/include/pbl/services/comm_session/session_analytics.h @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef struct CommSession CommSession; + +typedef enum { + CommSessionCloseReason_UnderlyingDisconnection = 0, + CommSessionCloseReason_ClosedRemotely = 1, + CommSessionCloseReason_ClosedLocally = 2, + CommSessionCloseReason_TransportSpecificBegin = 100, + CommSessionCloseReason_TransportSpecificEnd = 255, +} CommSessionCloseReason; + +typedef enum { + CommSessionTransportType_PlainSPP = 0, + CommSessionTransportType_iAP = 1, + CommSessionTransportType_PPoGATT = 2, + CommSessionTransportType_QEMU = 3, + CommSessionTransportType_PULSE = 4, +} CommSessionTransportType; + +//! Assumes bt_lock() is held by the caller. +CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session); + +void comm_session_analytics_open_session(CommSession *session); + +void comm_session_analytics_close_session(CommSession *session, CommSessionCloseReason reason); \ No newline at end of file diff --git a/include/pbl/services/comm_session/session_internal.h b/include/pbl/services/comm_session/session_internal.h new file mode 100644 index 0000000000..b23897c45c --- /dev/null +++ b/include/pbl/services/comm_session/session_internal.h @@ -0,0 +1,49 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "drivers/rtc.h" + +#include "session_receive_router.h" +#include "session_transport.h" + +#include "pbl/services/regular_timer.h" +#include "util/list.h" + +#include + +typedef struct SessionSendQueueJob SessionSendQueueJob; + +//! Data structure representing a Pebble Protocol communication session. +//! There can be multiple. For example, with the iAP transport, the Pebble app has a session and +//! 3rd party apps share another separate session as well. With PPoGATT, the Pebble app has its own +//! session, but each 3rd party app has its own session as well. +typedef struct CommSession { + ListNode node; + + //! The underlying transport responsible for actually sending and receiving the Pebble Protocol + //! data. This can be SPP, iAP (see ispp.c), PPoGATT (see ppogatt.c) or QEMU (qemu_transport.c). + Transport *transport; + + //! Set of function pointers that the session uses to call back to the transport. + const TransportImplementation *transport_imp; + + //! True if a Kernel BG callback has been scheduled to call transport_imp->send_next() + bool is_send_next_call_pending; + + //! True if the session is a system session (connected to the Pebble mobile app). + TransportDestination destination; + + // Extensions supported by the mobile endpoint, see + // https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=491698 + CommSessionCapability protocol_capabilities; + + //! The send queue of this session. See session_send_queue.c + SessionSendQueueJob *send_queue_head; + + ReceiveRouter recv_router; + + //! Absolute number of ticks since session opened. + RtcTicks open_ticks; +} CommSession; diff --git a/src/fw/services/common/comm_session/session_receive_router.h b/include/pbl/services/comm_session/session_receive_router.h similarity index 87% rename from src/fw/services/common/comm_session/session_receive_router.h rename to include/pbl/services/comm_session/session_receive_router.h index efce402139..56054d8796 100644 --- a/src/fw/services/common/comm_session/session_receive_router.h +++ b/include/pbl/services/comm_session/session_receive_router.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/protocol.h" +#include "pbl/services/comm_session/protocol.h" #include #include diff --git a/include/pbl/services/comm_session/session_remote_os.h b/include/pbl/services/comm_session/session_remote_os.h new file mode 100644 index 0000000000..b35c2d10ee --- /dev/null +++ b/include/pbl/services/comm_session/session_remote_os.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef enum { + RemoteBitmaskOS = 0x7, // bits 0 - 2 +} RemoteBitmask; + +typedef enum { + RemoteOSUnknown = 0, + RemoteOSiOS = 1, + RemoteOSAndroid = 2, + RemoteOSX = 3, + RemoteOSLinux = 4, + RemoteOSWindows = 5, +} RemoteOS; diff --git a/include/pbl/services/comm_session/session_remote_version.h b/include/pbl/services/comm_session/session_remote_version.h new file mode 100644 index 0000000000..f069fa7d50 --- /dev/null +++ b/include/pbl/services/comm_session/session_remote_version.h @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" +#include +#include + +typedef struct CommSession CommSession; + +// Capabilities are a bitfield set by passing the capabilities character array in +// system_versions.c. The corresponding mobile applications return an integer +// field indicating which endpoints it has support for over the deprecated ones. +typedef struct PACKED { + union { + struct PACKED { + bool run_state_support:1; + bool infinite_log_dumping_support:1; + bool extended_music_service:1; + bool extended_notification_service:1; + bool lang_pack_support:1; + bool app_message_8k_support:1; + bool activity_insights_support:1; + bool voice_api_support:1; + bool send_text_support:1; + bool notification_filtering_support:1; + bool unread_coredump_support:1; + bool weather_app_support:1; + bool reminders_app_support:1; + bool workout_app_support:1; + bool smooth_fw_install_progress_support:1; + bool custom_vibe_pattern_support:1; + uint8_t javascript_bytecode_version_appended: 1; + uint8_t more_padded_bits:4; + bool continue_fw_install_across_disconnect_support: 1; + bool blob_db_version_support: 1; + bool settings_sync_support: 1; // Phone supports Settings BlobDB sync + }; + uint64_t flags; + }; +} PebbleProtocolCapabilities; + +void session_remote_version_start_requests(CommSession *session); diff --git a/src/fw/services/common/comm_session/session_send_buffer.h b/include/pbl/services/comm_session/session_send_buffer.h similarity index 82% rename from src/fw/services/common/comm_session/session_send_buffer.h rename to include/pbl/services/comm_session/session_send_buffer.h index 9e017ac75d..67b1605308 100644 --- a/src/fw/services/common/comm_session/session_send_buffer.h +++ b/include/pbl/services/comm_session/session_send_buffer.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" typedef struct SendBuffer SendBuffer; diff --git a/src/fw/services/common/comm_session/session_send_queue.h b/include/pbl/services/comm_session/session_send_queue.h similarity index 80% rename from src/fw/services/common/comm_session/session_send_queue.h rename to include/pbl/services/comm_session/session_send_queue.h index 9da2a75756..3b3f14b2dc 100644 --- a/src/fw/services/common/comm_session/session_send_queue.h +++ b/include/pbl/services/comm_session/session_send_queue.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" #include "util/list.h" #include diff --git a/src/fw/services/common/comm_session/session_transport.h b/include/pbl/services/comm_session/session_transport.h similarity index 91% rename from src/fw/services/common/comm_session/session_transport.h rename to include/pbl/services/comm_session/session_transport.h index 6906b8232f..9606cb1ea8 100644 --- a/src/fw/services/common/comm_session/session_transport.h +++ b/include/pbl/services/comm_session/session_transport.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_analytics.h" #include "util/uuid.h" diff --git a/src/fw/services/common/compositor/compositor.h b/include/pbl/services/compositor/compositor.h similarity index 86% rename from src/fw/services/common/compositor/compositor.h rename to include/pbl/services/compositor/compositor.h index 88769b4367..41954b95ab 100644 --- a/src/fw/services/common/compositor/compositor.h +++ b/include/pbl/services/compositor/compositor.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -133,3 +120,13 @@ void compositor_freeze(void); //! Resuming allowing new frames to be pushed to the compositor, undoes the effects of //! compositor_freeze. void compositor_unfreeze(void); + +//! Copy app FB into the given region of the system framebuffer, scaling or centering the app +//! framebuffer content in the destination as needed based on user preference. +//! If the update_rect points off the edge of the screen, the region updated will be clipped as needed. +//! If copy_relative_to_origin is false, update_rect will be copied/filled starting from the origin of the +//! app framebuffer. If true, it is relative to the region being updated will be copied/filled. +void compositor_scaled_app_fb_copy(const GRect update_rect, bool copy_relative_to_origin); + +//! Extended version of compositor_scaled_app_fb_copy which allows an Y offset for the source to be specified. +void compositor_scaled_app_fb_copy_offset(const GRect update_rect, bool copy_relative_to_origin, int16_t offset_y); diff --git a/include/pbl/services/compositor/compositor_display.h b/include/pbl/services/compositor/compositor_display.h new file mode 100644 index 0000000000..1f48f81027 --- /dev/null +++ b/include/pbl/services/compositor/compositor_display.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! @file compositor_display.h +//! +//! This module handles copying the framebuffer content to the display driver. + +void compositor_display_update(void (*handle_update_complete_cb)(void)); + +bool compositor_display_update_in_progress(void); diff --git a/include/pbl/services/compositor/compositor_private.h b/include/pbl/services/compositor/compositor_private.h new file mode 100644 index 0000000000..73daf2be27 --- /dev/null +++ b/include/pbl/services/compositor/compositor_private.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/graphics.h" + +//! @file compositor_private.h +//! +//! Useful helpful function to help out implementing compositor animations + +//! Trigger the app framebuffer to be copied to the system framebuffer +void compositor_render_app(void); + +//! Trigger the modal window to be rendered to the system framebuffer +void compositor_render_modal(void); + +//! A GPathDrawFilledCallback that can be used to fill pixels with the app's framebuffer +void compositor_app_framebuffer_fill_callback(GContext *ctx, int16_t y, + Fixed_S16_3 x_range_begin, Fixed_S16_3 x_range_end, + Fixed_S16_3 delta_begin, Fixed_S16_3 delta_end, + void *user_data); diff --git a/src/fw/services/common/compositor/compositor_transitions.h b/include/pbl/services/compositor/compositor_transitions.h similarity index 78% rename from src/fw/services/common/compositor/compositor_transitions.h rename to include/pbl/services/compositor/compositor_transitions.h index 77ea7221e1..33f4e05e6f 100644 --- a/src/fw/services/common/compositor/compositor_transitions.h +++ b/include/pbl/services/compositor/compositor_transitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,16 +8,14 @@ #include "default/compositor_slide_transitions.h" #include "default/compositor_shutter_transitions.h" #include "legacy/compositor_app_slide_transitions.h" -#if PLATFORM_SILK || PLATFORM_ASTERIX +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 # include "legacy/compositor_modal_slide_transitions.h" #else # include "default/compositor_modal_transitions.h" # include "default/compositor_port_hole_transitions.h" # include "default/compositor_round_flip_transitions.h" #endif -#if CAPABILITY_HAS_TIMELINE_PEEK #include "default/compositor_peek_transitions.h" -#endif #include "applib/graphics/gdraw_command_sequence.h" diff --git a/include/pbl/services/compositor/default/compositor_dot_transitions.h b/include/pbl/services/compositor/default/compositor_dot_transitions.h new file mode 100644 index 0000000000..d1ac4e244a --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_dot_transitions.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +// These numbers approximate the visuals shown in the videos from the design team +#define STATIC_DOT_ANIMATION_DURATION_MS 233 +#define DOT_ANIMATION_STROKE_WIDTH 12 + +void compositor_dot_transitions_collapsing_ring_animation_update(GContext *ctx, + uint32_t distance_normalized, + GColor outer_ring_color, + GColor inner_ring_color); + +const CompositorTransition* compositor_dot_transition_timeline_get(bool timeline_is_future, + bool timeline_is_destination); + +const CompositorTransition* compositor_dot_transition_app_fetch_get(void); diff --git a/include/pbl/services/compositor/default/compositor_launcher_app_transitions.h b/include/pbl/services/compositor/default/compositor_launcher_app_transitions.h new file mode 100644 index 0000000000..9bc7f49f4d --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_launcher_app_transitions.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +#include "apps/system/launcher/launcher.h" + +//! @file compositor_launcher_app_transitions.h +//! Allows a user to create and configure compositor transition animations between the launcher +//! and apps. + +//! @param app_is_destination Whether the animation should reflect the app as the destination +//! @return \ref CompositorTransition for the resulting animation +const CompositorTransition *compositor_launcher_app_transition_get(bool app_is_destination); diff --git a/include/pbl/services/compositor/default/compositor_modal_transitions.h b/include/pbl/services/compositor/default/compositor_modal_transitions.h new file mode 100644 index 0000000000..ca2dd3536b --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_modal_transitions.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +//! @file compositor_modal_transitions.h +//! Allows a user to create and configure compositor transition animations for modals. + +//! @param modal_is_destination Whether the animation should animate to the modal or not +//! @return \ref CompositorTransition for the requested modal animation +const CompositorTransition* compositor_modal_transition_to_modal_get(bool modal_is_destination); diff --git a/include/pbl/services/compositor/default/compositor_peek_transitions.h b/include/pbl/services/compositor/default/compositor_peek_transitions.h new file mode 100644 index 0000000000..c2b3232fe4 --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_peek_transitions.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor_transitions.h" + +const CompositorTransition *compositor_peek_transition_timeline_get(void); diff --git a/include/pbl/services/compositor/default/compositor_port_hole_transitions.h b/include/pbl/services/compositor/default/compositor_port_hole_transitions.h new file mode 100644 index 0000000000..ce67f9ed6a --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_port_hole_transitions.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +#define PORT_HOLE_TRANSITION_DURATION_MS (6 * ANIMATION_TARGET_FRAME_INTERVAL_MS) + +const CompositorTransition *compositor_port_hole_transition_app_get( + CompositorTransitionDirection direction); + +void compositor_port_hole_transition_draw_outer_ring(GContext *ctx, int16_t pixels, + GColor ring_color); diff --git a/include/pbl/services/compositor/default/compositor_round_flip_transitions.h b/include/pbl/services/compositor/default/compositor_round_flip_transitions.h new file mode 100644 index 0000000000..f8f3d4be05 --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_round_flip_transitions.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +// Animation in design video lasts this many frames +#define ROUND_FLIP_ANIMATION_DURATION_MS (6 * ANIMATION_TARGET_FRAME_INTERVAL_MS) + +void compositor_round_flip_transitions_flip_animation_update(GContext *ctx, + uint32_t distance_normalized, + CompositorTransitionDirection dir, + GColor flip_lid_color); + +const CompositorTransition *compositor_round_flip_transition_get(bool flip_to_the_right); diff --git a/include/pbl/services/compositor/default/compositor_shutter_transitions.h b/include/pbl/services/compositor/default/compositor_shutter_transitions.h new file mode 100644 index 0000000000..9f56c33d84 --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_shutter_transitions.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +// The length of first "section" of the animation, where the old app is moved off of the screen. +#define SHUTTER_TRANSITION_FIRST_DURATION_MS (2 * ANIMATION_TARGET_FRAME_INTERVAL_MS) +// The length of second "section" of the animation, where the new app is moved in. +#define SHUTTER_TRANSITION_SECOND_DURATION_MS (4 * ANIMATION_TARGET_FRAME_INTERVAL_MS) +// Total length of the animation. +#define SHUTTER_TRANSITION_DURATION_MS (SHUTTER_TRANSITION_FIRST_DURATION_MS + \ + SHUTTER_TRANSITION_SECOND_DURATION_MS) + +const CompositorTransition *compositor_shutter_transition_get( + CompositorTransitionDirection direction, GColor color); diff --git a/include/pbl/services/compositor/default/compositor_slide_transitions.h b/include/pbl/services/compositor/default/compositor_slide_transitions.h new file mode 100644 index 0000000000..0d32dd14d0 --- /dev/null +++ b/include/pbl/services/compositor/default/compositor_slide_transitions.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +const CompositorTransition *compositor_slide_transition_timeline_get(bool timeline_is_future, + bool timeline_is_destination, + bool timeline_is_empty); diff --git a/include/pbl/services/compositor/legacy/compositor_app_slide_transitions.h b/include/pbl/services/compositor/legacy/compositor_app_slide_transitions.h new file mode 100644 index 0000000000..7d31df3eff --- /dev/null +++ b/include/pbl/services/compositor/legacy/compositor_app_slide_transitions.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +void compositor_app_slide_transitions_animation_update(GContext *ctx, + uint32_t distance_normalized, + CompositorTransitionDirection dir); + +const CompositorTransition *compositor_app_slide_transition_get(bool flip_to_the_right); diff --git a/include/pbl/services/compositor/legacy/compositor_modal_slide_transitions.h b/include/pbl/services/compositor/legacy/compositor_modal_slide_transitions.h new file mode 100644 index 0000000000..ca2dd3536b --- /dev/null +++ b/include/pbl/services/compositor/legacy/compositor_modal_slide_transitions.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/compositor/compositor.h" + +//! @file compositor_modal_transitions.h +//! Allows a user to create and configure compositor transition animations for modals. + +//! @param modal_is_destination Whether the animation should animate to the modal or not +//! @return \ref CompositorTransition for the requested modal animation +const CompositorTransition* compositor_modal_transition_to_modal_get(bool modal_is_destination); diff --git a/include/pbl/services/compositor/screenshot_pp.h b/include/pbl/services/compositor/screenshot_pp.h new file mode 100644 index 0000000000..af4e924c5f --- /dev/null +++ b/include/pbl/services/compositor/screenshot_pp.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include "pbl/services/comm_session/session.h" + +//! Callback for handling a screenshot request message from the client +void screenshot_protocol_msg_callback(CommSession *session, const uint8_t* data, unsigned int length); diff --git a/src/fw/services/normal/contacts/attributes_address.h b/include/pbl/services/contacts/attributes_address.h similarity index 88% rename from src/fw/services/normal/contacts/attributes_address.h rename to include/pbl/services/contacts/attributes_address.h index 5615feebdf..7b2f365b05 100644 --- a/src/fw/services/normal/contacts/attributes_address.h +++ b/include/pbl/services/contacts/attributes_address.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" #include #include diff --git a/include/pbl/services/contacts/contacts.h b/include/pbl/services/contacts/contacts.h new file mode 100644 index 0000000000..d7b63475ab --- /dev/null +++ b/include/pbl/services/contacts/contacts.h @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "attributes_address.h" + +#include "util/attributes.h" +#include "util/uuid.h" + +typedef struct { + Uuid id; + uint32_t flags; + AttributeList attr_list; + AddressList addr_list; +} Contact; + +//! Lookup a contact given its uuid. Will return NULL if no contact is found. +//! The contact must be freed with contacts_free_contact(). +Contact* contacts_get_contact_by_uuid(const Uuid *uuid); + +//! Frees a contact +void contacts_free_contact(Contact *contact); diff --git a/include/pbl/services/cron.h b/include/pbl/services/cron.h new file mode 100644 index 0000000000..69ed242f36 --- /dev/null +++ b/include/pbl/services/cron.h @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/events.h" + +//! @file cron.h +//! Wall-clock based timer system. Designed for use in things such as alarms, calendar events, etc. +//! Properly handles DST, etc. +//! This file is for controlling the service itself. The actual job API is in + +//! Initialize the cron service. +void cron_service_init(void); + +//! Adjust all cron jobs, as the wall clock has changed. +//! This means DST and/or time zone may have changed! +void cron_service_handle_clock_change(PebbleSetTimeEvent *set_time_info); + +#if UNITTEST +// ----------------------------------------------------------------------------- +// For testing: + +//! Remove all jobs. +void cron_clear_all_jobs(void); + +//! Clean up the cron service. +void cron_service_deinit(void); + +//! The number of registered cron jobs. +uint32_t cron_service_get_job_count(void); + +//! Run the cron timers if they've fired. +void cron_service_wakeup(void); +#endif diff --git a/src/fw/services/normal/data_logging/data_logging_service.h b/include/pbl/services/data_logging/data_logging_service.h similarity index 85% rename from src/fw/services/normal/data_logging/data_logging_service.h rename to include/pbl/services/data_logging/data_logging_service.h index b647f125af..62b70e58cb 100644 --- a/src/fw/services/normal/data_logging/data_logging_service.h +++ b/include/pbl/services/data_logging/data_logging_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -39,9 +26,8 @@ typedef enum { DlsSystemTagActivityAccelSamples = 82, DlsSystemTagActivitySession = 84, DlsSystemTagProtobufLogSession = 85, -#ifdef MEMFAULT DlsSystemTagMemfaultChunksSession = 86, -#endif + DlsSystemTagAnalyticsNativeHeartbeat = 87, } DlsSystemTag; //! Init the data logging service. Called by the system at boot time. diff --git a/include/pbl/services/data_logging/dls_endpoint.h b/include/pbl/services/data_logging/dls_endpoint.h new file mode 100644 index 0000000000..99f7a0919b --- /dev/null +++ b/include/pbl/services/data_logging/dls_endpoint.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "dls_private.h" + +#include + +void dls_endpoint_init(void); + +void dls_endpoint_close_session(uint8_t session_id); + +bool dls_endpoint_send_data(DataLoggingSession *logging_session, const uint8_t *data, unsigned int num_bytes); + +bool dls_endpoint_open_session(DataLoggingSession *logging_session); + diff --git a/src/fw/services/normal/data_logging/dls_list.h b/include/pbl/services/data_logging/dls_list.h similarity index 82% rename from src/fw/services/normal/data_logging/dls_list.h rename to include/pbl/services/data_logging/dls_list.h index 5d649243bf..5c80bc8bda 100644 --- a/src/fw/services/normal/data_logging/dls_list.h +++ b/include/pbl/services/data_logging/dls_list.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/normal/data_logging/dls_private.h b/include/pbl/services/data_logging/dls_private.h similarity index 92% rename from src/fw/services/normal/data_logging/dls_private.h rename to include/pbl/services/data_logging/dls_private.h index 71452b75b1..c2570f00ec 100644 --- a/src/fw/services/normal/data_logging/dls_private.h +++ b/include/pbl/services/data_logging/dls_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,7 +8,7 @@ #include "flash_region/flash_region.h" #include "kernel/pebble_tasks.h" #include "os/mutex.h" -#include "services/common/comm_session/protocol.h" +#include "pbl/services/comm_session/protocol.h" #include "system/hexdump.h" #include "util/attributes.h" #include "util/shared_circular_buffer.h" diff --git a/src/fw/services/normal/data_logging/dls_storage.h b/include/pbl/services/data_logging/dls_storage.h similarity index 76% rename from src/fw/services/normal/data_logging/dls_storage.h rename to include/pbl/services/data_logging/dls_storage.h index 9f28c270e7..4eacdcb791 100644 --- a/src/fw/services/normal/data_logging/dls_storage.h +++ b/include/pbl/services/data_logging/dls_storage.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/data_logging/dls_private.h" +#include "pbl/services/data_logging/dls_private.h" //! @file dls_storage.h //! diff --git a/include/pbl/services/debounced_connection_service.h b/include/pbl/services/debounced_connection_service.h new file mode 100644 index 0000000000..758277bec8 --- /dev/null +++ b/include/pbl/services/debounced_connection_service.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/events.h" +#include + +void debounced_connection_service_init(void); + +bool debounced_connection_service_is_connected(void); + +void debounced_connection_service_handle_event(PebbleCommSessionEvent *e); diff --git a/src/fw/services/common/ecompass.h b/include/pbl/services/ecompass.h similarity index 83% rename from src/fw/services/common/ecompass.h rename to include/pbl/services/ecompass.h index 0eb6e81e22..8f631ef88b 100644 --- a/src/fw/services/common/ecompass.h +++ b/include/pbl/services/ecompass.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/battery/battery_monitor.h" +#include "pbl/services/battery/battery_monitor.h" #include diff --git a/include/pbl/services/event_service.h b/include/pbl/services/event_service.h new file mode 100644 index 0000000000..2b308a4567 --- /dev/null +++ b/include/pbl/services/event_service.h @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/events.h" +#include "process_management/app_manager.h" + +typedef void (*EventServiceAddSubscriberCallback)(PebbleTask task); +typedef void (*EventServiceRemoveSubscriberCallback)(PebbleTask task); + +//! Call once during system startup +void event_service_system_init(void); + +//! Called from app task for each event type the app wants notifications for +void event_service_init(PebbleEventType type, EventServiceAddSubscriberCallback start_cb, + EventServiceRemoveSubscriberCallback stop_cb); +bool event_service_is_running(PebbleEventType event_type); +void event_service_handle_event(PebbleEvent *e); + +//! Subscribe to an event. This can only be called from the Kernel Main task +void event_service_subscribe_from_kernel_main(PebbleSubscriptionEvent *subscription); + +void event_service_handle_subscription(PebbleSubscriptionEvent *subscription); +void event_service_clear_process_subscriptions(PebbleTask task); + +//! Claim a buffer. This means it won't automatically get cleaned up +//! If you claim a buffer you must free it with event_service_free_claimed_buffer() +void* event_service_claim_buffer(PebbleEvent *e); +//! This function expects the pointer returned by event_service_claim_buffer +void event_service_free_claimed_buffer(void *ref); + +//! Returns true if `buf` is a kernel-allocated buffer currently tracked by the +//! event service (i.e. one that was attached to an in-flight PebbleEvent). +//! Use this from syscalls that dereference an event's embedded data pointer to +//! make sure the caller didn't fabricate it. +bool event_service_is_known_buffer(const void *buf); diff --git a/src/fw/services/common/evented_timer.h b/include/pbl/services/evented_timer.h similarity index 75% rename from src/fw/services/common/evented_timer.h rename to include/pbl/services/evented_timer.h index 3b61d9e5c3..41a41912b5 100644 --- a/src/fw/services/common/evented_timer.h +++ b/include/pbl/services/evented_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/filesystem/app_file.h b/include/pbl/services/filesystem/app_file.h new file mode 100644 index 0000000000..2181c6ae9b --- /dev/null +++ b/include/pbl/services/filesystem/app_file.h @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! Consistent naming of per-app files. +//! +//! All files which are specific to an app are named with a consistent scheme +//! which identifies the files as belonging to the app. This is done by +//! prefixing the filename with a string based on the AppInstallId. Filenames +//! take the format printf("@%08x/%s", (uint32_t)app_id, suffix) to form a +//! pseudo-directory structure. +//! +//! The prefix is fixed-length to make it simple to generate, parse and +//! identify. +#pragma once + +#include +#include + +#include "process_management/app_install_types.h" + +// The suffix starts at offset 10 in the filename +// '@' + "XXXXXXXX" + '/' : (1 + 8 + 1 = 10) +#define APP_FILE_NAME_PREFIX_LENGTH (10) + +//! Make an app-file name from the given app_id and suffix string. +//! +//! @param suffix_len strlen(suffix) +//! +//! @note buffer_len must be > APP_FILE_NAME_PREFIX_LENGTH + suffix_len to fit +//! the full file name including NULL-terminator. +void app_file_name_make(char * restrict buffer, size_t buffer_len, + AppInstallId app_id, const char * restrict suffix, + size_t suffix_len); + +//! Checks whether the given filename is an app file. +bool is_app_file_name(const char *filename); + +//! Checks whether the given filename is an app resource file (suffix = "res") +bool is_app_resource_file_name(const char *filename); + +//! Parses an app-file name to get the AppInstallId. +//! Assumes the file is indeed an app-file +AppInstallId app_file_parse_app_id(const char *filename); + +//! Parses an app-file name to get the AppInstallId. +//! +//! @returns INSTALL_ID_INVALID if the filename is not an app-file. +AppInstallId app_file_get_app_id(const char *filename); diff --git a/src/fw/services/normal/filesystem/flash_translation.h b/include/pbl/services/filesystem/flash_translation.h similarity index 82% rename from src/fw/services/normal/filesystem/flash_translation.h rename to include/pbl/services/filesystem/flash_translation.h index fa65cf4091..f09b8bd11d 100644 --- a/src/fw/services/normal/filesystem/flash_translation.h +++ b/include/pbl/services/filesystem/flash_translation.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/normal/filesystem/pfs.h b/include/pbl/services/filesystem/pfs.h similarity index 93% rename from src/fw/services/normal/filesystem/pfs.h rename to include/pbl/services/filesystem/pfs.h index 30c95e3601..f09fdeb5ad 100644 --- a/src/fw/services/normal/filesystem/pfs.h +++ b/include/pbl/services/filesystem/pfs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/firmware_update.h b/include/pbl/services/firmware_update.h new file mode 100644 index 0000000000..c8ac13066b --- /dev/null +++ b/include/pbl/services/firmware_update.h @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "kernel/events.h" + +void firmware_update_init(void); + +unsigned int firmware_update_get_percent_progress(void); + +void firmware_update_event_handler(PebbleSystemMessageEvent* event); +void firmware_update_pb_event_handler(PebblePutBytesEvent *event); + +typedef enum { + FirmwareUpdateStopped = 0, + FirmwareUpdateRunning = 1, + FirmwareUpdateCancelled = 2, + FirmwareUpdateFailed = 3, +} FirmwareUpdateStatus; + +FirmwareUpdateStatus firmware_update_current_status(void); + +bool firmware_update_is_in_progress(void); diff --git a/include/pbl/services/get_bytes/get_bytes.h b/include/pbl/services/get_bytes/get_bytes.h new file mode 100644 index 0000000000..a778cd6965 --- /dev/null +++ b/include/pbl/services/get_bytes/get_bytes.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Different types of objects that can be transferred over GetBytes +typedef enum { + GetBytesObjectUnknown = 0x00, + GetBytesObjectCoredump = 0x01, + GetBytesObjectFile = 0x02, + GetBytesObjectFlash = 0x03 +} GetBytesObjectType; + +// Possible values for GetBytesRspObjectInfo.error_code +typedef enum { + GET_BYTES_OK = 0, + GET_BYTES_MALFORMED_COMMAND = 1, + GET_BYTES_ALREADY_IN_PROGRESS = 2, + GET_BYTES_DOESNT_EXIST = 3, + GET_BYTES_CORRUPTED = 4, +} GetBytesInfoErrorCode; diff --git a/src/fw/services/common/get_bytes/get_bytes_private.h b/include/pbl/services/get_bytes/get_bytes_private.h similarity index 79% rename from src/fw/services/common/get_bytes/get_bytes_private.h rename to include/pbl/services/get_bytes/get_bytes_private.h index 2f9e4479c4..c12c502d9c 100644 --- a/src/fw/services/common/get_bytes/get_bytes_private.h +++ b/include/pbl/services/get_bytes/get_bytes_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -22,7 +9,7 @@ #include #include "kernel/core_dump_private.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" #include "util/attributes.h" // This matches the entry we put into protocol_endpoints_table.h diff --git a/include/pbl/services/get_bytes/get_bytes_storage.h b/include/pbl/services/get_bytes/get_bytes_storage.h new file mode 100644 index 0000000000..a77d02bbb6 --- /dev/null +++ b/include/pbl/services/get_bytes/get_bytes_storage.h @@ -0,0 +1,55 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "pbl/services/get_bytes/get_bytes.h" + +typedef enum { + GetBytesStorageTypeUnknown, + GetBytesStorageTypeCoredump, + GetBytesStorageTypeFile, + GetBytesStorageTypeFlash +} GetBytesStorageType; + +struct GetBytesStorageImplementation; +typedef struct GetBytesStorageImplementation GetBytesStorageImplementation; + +typedef struct { + // A struct full of function pointers that implements a storage API + const GetBytesStorageImplementation *impl; + + //! A void pointer that the GetBytesStorageImplementation is free to stash stuff into + void *impl_data; + + //! The offset into the storage we've initialized. Updated by pb_storage_append. pb_storage_init + //! may set this to a non-zero value. + uint32_t current_offset; +} GetBytesStorage; + +// info used by the setup routines depending on the implementation +typedef struct { + //! Used by GetBytesStorageTypeFile + char *filename; + //! Used by GetBytesStorageTypeFlash + uint32_t flash_start_addr; + uint32_t flash_len; + //! Used by GetBytesStorageTypeCoredump + bool only_get_new_coredump; +} GetBytesStorageInfo; + +//! Set up the storage. This may include allocating memory, open a file descriptor, etc. +bool gb_storage_setup(GetBytesStorage *storage, GetBytesObjectType object_type, + GetBytesStorageInfo *info); + +//! Retrieve the size of the object that is to be sent +GetBytesInfoErrorCode gb_storage_get_size(GetBytesStorage *storage, uint32_t *size); + +//! Read a chunk of the object into a buffer +bool gb_storage_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); + +//! Cleanup the storage. +void gb_storage_cleanup(GetBytesStorage *storage, bool successful); diff --git a/include/pbl/services/get_bytes/get_bytes_storage_coredump.h b/include/pbl/services/get_bytes/get_bytes_storage_coredump.h new file mode 100644 index 0000000000..d69bf6f626 --- /dev/null +++ b/include/pbl/services/get_bytes/get_bytes_storage_coredump.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "get_bytes_storage.h" + +bool gb_storage_coredump_setup(GetBytesStorage *storage, GetBytesObjectType object_type, + GetBytesStorageInfo *info); + +GetBytesInfoErrorCode gb_storage_coredump_get_size(GetBytesStorage *storage, uint32_t *size); + +bool gb_storage_coredump_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); + +void gb_storage_coredump_cleanup(GetBytesStorage *storage, bool successful); diff --git a/include/pbl/services/get_bytes/get_bytes_storage_file.h b/include/pbl/services/get_bytes/get_bytes_storage_file.h new file mode 100644 index 0000000000..e1f6ebf683 --- /dev/null +++ b/include/pbl/services/get_bytes/get_bytes_storage_file.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "get_bytes_storage.h" + +bool gb_storage_file_setup(GetBytesStorage *storage, GetBytesObjectType object_type, + GetBytesStorageInfo *info); + +GetBytesInfoErrorCode gb_storage_file_get_size(GetBytesStorage *storage, uint32_t *size); + +bool gb_storage_file_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); + +void gb_storage_file_cleanup(GetBytesStorage *storage, bool successful); diff --git a/include/pbl/services/get_bytes/get_bytes_storage_flash.h b/include/pbl/services/get_bytes/get_bytes_storage_flash.h new file mode 100644 index 0000000000..f09aa6e0e3 --- /dev/null +++ b/include/pbl/services/get_bytes/get_bytes_storage_flash.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "get_bytes_storage.h" + +bool gb_storage_flash_setup(GetBytesStorage *storage, GetBytesObjectType object_type, + GetBytesStorageInfo *info); + +GetBytesInfoErrorCode gb_storage_flash_get_size(GetBytesStorage *storage, uint32_t *size); + +bool gb_storage_flash_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); + +void gb_storage_flash_cleanup(GetBytesStorage *storage, bool successful); diff --git a/include/pbl/services/health_sync_endpoint.h b/include/pbl/services/health_sync_endpoint.h new file mode 100644 index 0000000000..a996e92320 --- /dev/null +++ b/include/pbl/services/health_sync_endpoint.h @@ -0,0 +1,4 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once diff --git a/src/fw/services/common/hrm/hrm_manager.h b/include/pbl/services/hrm/hrm_manager.h similarity index 79% rename from src/fw/services/common/hrm/hrm_manager.h rename to include/pbl/services/hrm/hrm_manager.h index ae6385fafa..3eeb9ba01b 100644 --- a/src/fw/services/common/hrm/hrm_manager.h +++ b/include/pbl/services/hrm/hrm_manager.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/accel_manager_types.h" +#include "pbl/services/accel_manager_types.h" #include "process_management/app_install_types.h" #include "util/time/time.h" @@ -25,11 +12,8 @@ #include typedef enum { - HRMQuality_NoAccel = -2, HRMQuality_OffWrist = -1, - - HRMQuality_NoSignal = 0, - HRMQuality_Worst, + HRMQuality_Worst = 0, HRMQuality_Poor, HRMQuality_Acceptable, HRMQuality_Good, @@ -39,25 +23,27 @@ typedef enum { typedef enum { HRMFeatureShift_BPM = 0, HRMFeatureShift_HRV = 1, - HRMFeatureShift_LEDCurrent = 2, - HRMFeatureShift_Diagnostics = 3, - + HRMFeatureShift_SpO2 = 2, +#ifdef CONFIG_MFG + HRMFeatureShift_CTR = 3, + HRMFeatureShift_Leakage = 4, +#endif HRMFeatureShiftMax } HRMFeatureShift; typedef enum { HRMFeature_BPM = (1 << HRMFeatureShift_BPM), //!< Collect heartrate BPM. HRMFeature_HRV = (1 << HRMFeatureShift_HRV), //!< Collect heartrate variability. - HRMFeature_LEDCurrent = (1 << HRMFeatureShift_LEDCurrent), //!< Collect the LED current - //!< consumption (uA). This should not - //!< be made public by the HRM service, - //!< and should only be used internally - HRMFeature_Diagnostics = (1 << 3), //!< Collect PPG & Accel data + HRMFeature_SpO2 = (1 << HRMFeatureShift_SpO2), //!< Collect blood oxygen saturation. +#ifdef CONFIG_MFG + HRMFeature_CTR = (1 << HRMFeatureShift_CTR), //!< Collect ppg CTR test data. + HRMFeature_Leakage = (1 << HRMFeatureShift_Leakage), //!< Collect ppg leakage test data. +#endif HRMFeatureMax } HRMFeature; // Hold enough data for 2s worth of samples just in case we miss a handshake -#define HRM_MANAGER_ACCEL_RATE_MILLIHZ (12500) +#define HRM_MANAGER_ACCEL_RATE_MILLIHZ (25000) #define HRM_MANAGER_MAX_ACCEL_SAMPLES ((2 * HRM_MANAGER_ACCEL_RATE_MILLIHZ) / 1000) // When an app exits, we change its subscription (if any) to expire in this many seconds @@ -149,28 +135,23 @@ void hrm_manager_enable(bool on); //------------------------------------------------------------------------------ // The driver needs to provide new data to the service and needs to pull accel data. -#define MAX_PPG_SAMPLES 20 - -typedef struct { - int num_samples; - uint8_t indexes[MAX_PPG_SAMPLES]; - uint16_t ppg[MAX_PPG_SAMPLES]; - uint16_t tia[MAX_PPG_SAMPLES]; -} HRMPPGData; - //! HRMData will contain all HRM information that is currently available from the device. typedef struct { - uint16_t led_current_ua; + HRMFeature features; uint8_t hrm_bpm; HRMQuality hrm_quality; uint16_t hrv_ppi_ms; HRMQuality hrv_quality; - uint8_t hrm_status; - - HRMAccelData accel_data; - HRMPPGData ppg_data; + + uint8_t spo2_percent; + HRMQuality spo2_quality; + +#ifdef CONFIG_MFG + double ctr[6]; + double leakage[6]; +#endif } HRMData; //! Callback used by HRM Driver to indicate that new data is available. diff --git a/src/fw/services/common/hrm/hrm_manager_private.h b/include/pbl/services/hrm/hrm_manager_private.h similarity index 75% rename from src/fw/services/common/hrm/hrm_manager_private.h rename to include/pbl/services/hrm/hrm_manager_private.h index 7eb17c4d28..1a79914e0e 100644 --- a/src/fw/services/common/hrm/hrm_manager_private.h +++ b/include/pbl/services/hrm/hrm_manager_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -24,7 +11,7 @@ #include "kernel/events.h" #include "os/mutex.h" #include "process_management/app_install_types.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "util/list.h" #include "util/circular_buffer.h" @@ -36,9 +23,6 @@ typedef void (*HRMSubscriberCallback)(PebbleHRMEvent *event, void *context); // right after turning it on #define HRM_SENSOR_SPIN_UP_SEC 20 -// We should never get a BPM reading lower than this -#define HRM_SENSOR_MIN_VALID_BPM_READING 20 - typedef struct AccelServiceState AccelServiceState; typedef struct HRMSubscriberState { @@ -56,7 +40,7 @@ typedef struct HRMSubscriberState { bool sent_expiration_event; // true after we've sent a HRMEvent_SubscriptionExpiring event HRMFeature features; // what features the subscriber is interested in - RtcTicks last_valid_ticks; // tick count the last time this subscriber received valid HR reading + RtcTicks last_valid_bpm_ticks; // tick count the last time this subscriber received valid HR reading } HRMSubscriberState; // HRM manager expects to be update at 1Hz. To the system task, we can currently @@ -65,12 +49,15 @@ typedef struct HRMSubscriberState { #define NUM_EVENTS_TO_QUEUE (8) #define EVENT_STORAGE_SIZE (sizeof(PebbleHRMEvent) * NUM_EVENTS_TO_QUEUE) -#define HRM_MANAGER_ACCEL_MANAGER_SAMPLES_PER_UPDATE 2 +#define HRM_MANAGER_ACCEL_MANAGER_SAMPLES_PER_UPDATE 4 // After every HRM_CHECK_SENSOR_DISABLE_COUNT calls to hrm_manager_new_data_cb(), we check to see // if we should disable the sensor. #define HRM_CHECK_SENSOR_DISABLE_COUNT 10 +// After this many consecutive hrm_enable failures, stop trying until reboot +#define HRM_MAX_ENABLE_FAILURES 3 + struct HRMManagerState { PebbleRecursiveMutex *lock; ListNode *subscribers; @@ -91,16 +78,12 @@ struct HRMManagerState { TimerID update_enable_timer_id; // used for re-enabling the HRM sensor uint8_t check_disable_counter; // increments to HRM_CHECK_SENSOR_DISABLE_COUNT + uint8_t enable_failure_count; // counts consecutive hrm_enable failures, stops retrying after max bool enabled_run_level; // True if the current run_level (LowPower, Stationary, // Normal, etc.) allows the sensor to be turned on bool enabled_charging_state; // Ture if we aren't plugged in / charging - // These variables used to keep track of the sensor reading validity. - bool sensor_stable; // True after we receive the first good reading after power-on or off-wrist - bool off_wrist_when_stable; // true if sensor said off-wrist when first stablized - RtcTicks sensor_start_ticks; // tick count last time sensor was powered on, or last - // off-wrist. 0 if still off-wrist or off. }; //! Subscription for KernelBG or KernelMain clients. diff --git a/include/pbl/services/i18n/i18n.h b/include/pbl/services/i18n/i18n.h new file mode 100644 index 0000000000..f9b860e11c --- /dev/null +++ b/include/pbl/services/i18n/i18n.h @@ -0,0 +1,67 @@ +/* SPDX-FileCopyrightText: 2000 Citrus Project */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#pragma once + +#include +#include +#include "util/list.h" + +#define ISO_LOCALE_LENGTH 6 +#define LOCALE_NAME_LENGTH 30 + +typedef struct { + ListNode node; //!< Linked list node + const void *owner; //!< pointer to owner object + uint32_t original_hash; //!< hashed original string + char *original_string; //!< original string. Stored following translated_string below + char translated_string[]; //!< i18n'ed string. Storage for original string comes after this +} I18nString; + +//! macro used to tag strings for extractions. Needed when we +//! can't call i18n_get directly (i.e constant initializers) +#define i18n_noop(string) (string) + +//! macro used to tag strings for extractions. Needed when we +//! can't call i18n_ctx_get directly (i.e constant initializers) +//! The resulting string should be used with i18n_get instead of i18n_ctx_get. +#define i18n_ctx_noop(ctx, string) (ctx "\4" string) + +//! Look up and return i18n'ed string (or original string if not found) +//! Tags it as owned by owner +//! NOTE: Currently, we don't do reference counting, so bad things will happen if the caller +//! calls i18n_get() on the same string more than once and assumes that any of those return +//! pointers will still be valid after i18n_free() is called on one of them. +const char *i18n_get(const char *string, const void *owner); + +#define i18n_ctx_get(ctx, string, owner) i18n_get(i18n_ctx_noop(ctx, string), owner) + +//! Look up an i18n'ed string and copy it into a provided buffer. +void i18n_get_with_buffer(const char *string, char *buffer, size_t length); + +#define i18n_ctx_get_with_buffer(ctx, string, buffer, length) \ + i18n_get_with_buffer(i18n_ctx_noop(ctx, string), buffer, length) + +//! Look up an i18n'ed string and return the length of it. +size_t i18n_get_length(const char *string); + +#define i18n_ctx_get_length(ctx, string) i18n_get_length(i18n_ctx_noop(ctx, string)) + +//! Free an i18n'ed string and it's associated metadata +void i18n_free(const char *string, const void *owner); + +#define i18n_ctx_free(ctx, string, owner) i18n_free(i18n_ctx_noop(ctx, string), owner) + +//! Free all i18n'ed strings associated with owner +void i18n_free_all(const void *owner); +void i18n_set_resource(uint32_t resource_id); + +//! return the ISO language string for the currently installed language +char *i18n_get_locale(void); + +//! return the version number for the currently installed language +uint16_t i18n_get_version(void); + +char *i18n_get_lang_name(void); + +void i18n_enable(bool enable); diff --git a/src/fw/services/common/i18n/mo.h b/include/pbl/services/i18n/mo.h similarity index 100% rename from src/fw/services/common/i18n/mo.h rename to include/pbl/services/i18n/mo.h diff --git a/include/pbl/services/idle_watchdog.h b/include/pbl/services/idle_watchdog.h new file mode 100644 index 0000000000..119d204804 --- /dev/null +++ b/include/pbl/services/idle_watchdog.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Auto-shutdown when idle in PRF to increase the changes of getting Pebbles shipped +//! that have some level of battery charge in them. + +//! Initialize event subscriptions and start the watchdog. Called once at boot from KernelMain. +void prf_idle_watchdog_init(void); + +//! Start (or restart) the watchdog timer. Can be called from any task. +void prf_idle_watchdog_start(void); + +//! Stop the watchdog timer. Can be called from any task. +void prf_idle_watchdog_stop(void); diff --git a/include/pbl/services/imu/units.h b/include/pbl/services/imu/units.h new file mode 100644 index 0000000000..79481874bf --- /dev/null +++ b/include/pbl/services/imu/units.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef enum { + //! The positive direction along the X axis goes toward the right + //! of the watch. + AXIS_X = 0, + //! The positive direction along the Y axis goes toward the top + //! of the watch. + AXIS_Y = 1, + //! The positive direction along the Z axis goes vertically out of + //! the watchface. + AXIS_Z = 2, +} IMUCoordinateAxis; diff --git a/include/pbl/services/legacy/persist_map.h b/include/pbl/services/legacy/persist_map.h new file mode 100644 index 0000000000..85ea89ab27 --- /dev/null +++ b/include/pbl/services/legacy/persist_map.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/uuid.h" +#include "system/status_codes.h" + +/* persist_map is an intermediate mapping until app install ids are reimplemented. + * This is an id, uuid record based solution with the id as a positive native int. + */ + +int persist_map_get_size(); + +int persist_map_add_uuid(const Uuid *uuid); + +int persist_map_get_id(const Uuid *uuid); + +int persist_map_auto_id(const Uuid *uuid); + +int persist_map_get_uuid(int id, Uuid *uuid); + +status_t persist_map_init(); + +//! Dump the persist map to LOG_LEVEL_INFO +void persist_map_dump(void); diff --git a/include/pbl/services/light.h b/include/pbl/services/light.h new file mode 100644 index 0000000000..ffb775e9e5 --- /dev/null +++ b/include/pbl/services/light.h @@ -0,0 +1,91 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include "shell/prefs.h" + +//! @file light.h +//! @addtogroup UI +//! @{ +//! @addtogroup Light Light +//! \brief Controlling Pebble's backlight +//! +//! The Light API provides you with functions to turn on Pebble’s backlight or +//! put it back into automatic control. You can trigger the backlight and schedule a timer +//! to automatically disable the backlight after a short delay, which is the preferred +//! method of interacting with the backlight. +//! @{ +//! +//! @internal +//! to be called when starting up to initialize variables correctly +void light_init(void); + +//! @internal +//! to be called by the launcher on a button down event +void light_button_pressed(void); + +//! @internal +//! to be called by the launcher on a button up event +void light_button_released(void); + +//! @copydoc app_light_enable +void light_enable(bool enable); + +//! @internal +//! light_enable that adheres to user's backlight setting. +void light_enable_respect_settings(bool enable); + +//! @copydoc app_light_enable_interaction +//! if light_enable was called (backlight was forced on), +//! then do nothing +void light_enable_interaction(void); + +//! Reset the state if an app overrode the usual state machine using light_enable() +void light_reset_user_controlled(void); + +//! @copydoc app_light_set_color_rgb888 +//! rgb is a packed 0x00RRGGBB value (8 bits per channel). No-op on +//! platforms without a color backlight. +void light_set_color_rgb888(uint32_t rgb); + +//! @copydoc app_light_set_system_color +//! No-op on platforms without a color backlight. +void light_set_system_color(void); + +//! Request that the system color take precedence over any app override. +//! While the refcount is non-zero, the LED is forced to the user default +//! color even if an app has set an override. Used by notifications and +//! other modals so they display in neutral white without permanently +//! clearing the underlying app's color. +void light_system_color_request(void); + +//! Release a system color request. When the refcount returns to zero, any +//! app override is re-applied. +void light_system_color_release(void); + +//! @internal +void light_toggle_enabled(void); + +//! @internal +void light_toggle_ambient_sensor_enabled(void); + +//! @internal +void light_toggle_dynamic_intensity_enabled(void); + +//! Switches for temporary disabling backlight (ie: low power mode) +void light_allow(bool allowed); + +//! Get the current active backlight brightness as a percentage (0-100) +//! This returns the actual current brightness, which may differ from the +//! configured brightness when dynamic backlight is enabled. +uint8_t light_get_current_brightness_percent(void); + +//! @return true if the backlight is currently on in any form (on, timed, or +//! fading out). Returns false only when the backlight is fully off. +bool light_is_on(void); + +//! @} // group Light +//! @} // group UI diff --git a/include/pbl/services/music.h b/include/pbl/services/music.h new file mode 100644 index 0000000000..4a7e4ae330 --- /dev/null +++ b/include/pbl/services/music.h @@ -0,0 +1,101 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include + +#define MUSIC_BUFFER_LENGTH 64 + +typedef enum { + MusicPlayStateUnknown, + MusicPlayStatePlaying, + MusicPlayStatePaused, + MusicPlayStateForwarding, + MusicPlayStateRewinding, + MusicPlayStateInvalid = 0xFF, +} MusicPlayState; + +typedef enum { + MusicCommandPlay, + MusicCommandPause, + MusicCommandTogglePlayPause, + MusicCommandNextTrack, + MusicCommandPreviousTrack, + MusicCommandVolumeUp, + MusicCommandVolumeDown, + MusicCommandAdvanceRepeatMode, + MusicCommandAdvanceShuffleMode, + MusicCommandSkipForward, + MusicCommandSkipBackward, + MusicCommandLike, + MusicCommandDislike, + MusicCommandBookmark, + + NumMusicCommand, +} MusicCommand; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface to Music app + +//! Copy out the current now playing fields into the parameters. We'll assume you've provided +//! buffers that are at least MUSIC_BUFFER_LENGTH in size. +void music_get_now_playing(char *title, char *artist, char *album); + +//! @return True if the music service has Now Playing metadata. +bool music_has_now_playing(void); + +//! Copy out the name of the current player. We'll assume you've provided +//! buffers that are at least MUSIC_BUFFER_LENGTH in size. +//! @return True if the name was copied successfully, or false if there was no name available. +bool music_get_player_name(char *player_name_out); + +//! @return The milliseconds since the track position was last updated. +uint32_t music_get_ms_since_pos_last_updated(void); + +//! Retrieve the position in the current track in the given pointers (which must not be null). +void music_get_pos(uint32_t *track_pos_ms, uint32_t *track_length_ms); + +//! @return The current playback rate percentage. +int32_t music_get_playback_rate_percent(void); + +//! @return The volume percentage. +uint8_t music_get_volume_percent(void); + +//! Retrieve the current playback state. +MusicPlayState music_get_playback_state(void); + +//! @return True if the service supports reporting of the player's playback state. +//! @see music_get_playback_state +bool music_is_playback_state_reporting_supported(void); + +//! @return True if the service support reporting of the playback progress. +//! @see music_get_pos +bool music_is_progress_reporting_supported(void); + +//! @return True if the service supports reporting of the current volume. +//! @see music_get_volume_percent +bool music_is_volume_reporting_supported(void); + +//! Sends the command to the server. Commands are "unreliable", they are sent at "best effort". +//! @param command The command to send. +//! @see music_is_command_supported +void music_command_send(MusicCommand command); + +//! @param The command to test. +//! @return True if the command is supported by the connected server. +bool music_is_command_supported(MusicCommand command); + +//! @return True if playback needs to be started manually by the user from the phone. +bool music_needs_user_to_start_playback_on_phone(void); + +//! Puts the underlying connection in a reduced latency mode, for better responsiveness. +void music_request_reduced_latency(bool reduced_latency); + +//! Puts the underlying connection in a low latency mode, for the best responsiveness. +void music_request_low_latency_for_period(uint32_t period_seconds); + +//! For testing purposes. +const char * music_get_connected_server_debug_name(void); diff --git a/include/pbl/services/music_endpoint.h b/include/pbl/services/music_endpoint.h new file mode 100644 index 0000000000..3295ac6f51 --- /dev/null +++ b/include/pbl/services/music_endpoint.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/events.h" + +void music_endpoint_handle_mobile_app_info_event(const PebbleRemoteAppInfoEvent *app_info_event); + +void music_endpoint_handle_mobile_app_event(const PebbleCommSessionEvent *app_event); diff --git a/include/pbl/services/music_endpoint_types.h b/include/pbl/services/music_endpoint_types.h new file mode 100644 index 0000000000..1f01f54f35 --- /dev/null +++ b/include/pbl/services/music_endpoint_types.h @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "util/attributes.h" + +typedef enum { + // Watch -> Phone + MusicEndpointCmdIDTogglePlayPause = 0x1, + MusicEndpointCmdIDPause = 0x2, + MusicEndpointCmdIDPlay = 0x3, + MusicEndpointCmdIDNextTrack = 0x4, + MusicEndpointCmdIDPreviousTrack = 0x5, + MusicEndpointCmdIDVolumeUp = 0x6, + MusicEndpointCmdIDVolumeDown = 0x7, + MusicEndpointCmdIDGetAllInfo = 0x8, + + // Phone -> Watch + MusicEndpointCmdIDNowPlayingInfoResponse = 0x10, + MusicEndpointCmdIDPlayStateInfoResponse = 0x11, + MusicEndpointCmdIDVolumeInfoResponse = 0x12, + MusicEndpointCmdIDPlayerInfoResponse = 0x13, + + MusicEndpointCmdIDInvalid = 0xff, +} MusicEndpointCmdID; + +typedef enum { + MusicEndpointPlaybackStatePaused = 0, + MusicEndpointPlaybackStatePlaying = 1, + MusicEndpointPlaybackStateRewinding = 2, + MusicEndpointPlaybackStateForwarding = 3, + MusicEndpointPlaybackStateUnknown = 4, +} MusicEndpointPlaybackState; + +typedef enum { + MusicEndpointShuffleModeUnknown = 0, + MusicEndpointShuffleModeOff = 1, + MusicEndpointShuffleModeOn = 2, +} MusicEndpointShuffleMode; + +typedef enum { + MusicEndpointRepeatModeUnknown = 0, + MusicEndpointRepeatModeOff = 1, + MusicEndpointRepeatModeOne = 2, + MusicEndpointRepeatModeAll = 3, +} MusicEndpointRepeatMode; + +typedef struct PACKED { + uint8_t play_state; + int32_t track_pos_ms; + int32_t play_rate; + uint8_t play_shuffle_mode; + uint8_t play_repeat_mode; +} MusicEndpointPlayStateInfo; diff --git a/src/fw/services/normal/music_internal.h b/include/pbl/services/music_internal.h similarity index 85% rename from src/fw/services/normal/music_internal.h rename to include/pbl/services/music_internal.h index a88ea1b09a..abe019d278 100644 --- a/src/fw/services/normal/music_internal.h +++ b/include/pbl/services/music_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/common/new_timer/new_timer.h b/include/pbl/services/new_timer/new_timer.h similarity index 86% rename from src/fw/services/common/new_timer/new_timer.h rename to include/pbl/services/new_timer/new_timer.h index 8c706f23a9..dc031d5e52 100644 --- a/src/fw/services/common/new_timer/new_timer.h +++ b/include/pbl/services/new_timer/new_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/new_timer/new_timer_service.h b/include/pbl/services/new_timer/new_timer_service.h new file mode 100644 index 0000000000..56eb92991d --- /dev/null +++ b/include/pbl/services/new_timer/new_timer_service.h @@ -0,0 +1,7 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +void new_timer_service_init(void); + diff --git a/include/pbl/services/notifications/action_chaining_window.h b/include/pbl/services/notifications/action_chaining_window.h new file mode 100644 index 0000000000..5b20ead4e1 --- /dev/null +++ b/include/pbl/services/notifications/action_chaining_window.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/window_stack.h" +#include "pbl/services/timeline/item.h" + +typedef void (*ActionChainingMenuSelectCb)(Window *chaining_window, + TimelineItemAction *action, void *context); +typedef void (*ActionChainingMenuClosedCb)(void *context); + +void action_chaining_window_push(WindowStack *window_stack, const char *title, + TimelineItemActionGroup *action_group, + ActionChainingMenuSelectCb select_cb, + void *select_cb_context, + ActionChainingMenuClosedCb closed_cb, + void *closed_cb_context); diff --git a/include/pbl/services/notifications/alerts.h b/include/pbl/services/notifications/alerts.h new file mode 100644 index 0000000000..90cfa123c3 --- /dev/null +++ b/include/pbl/services/notifications/alerts.h @@ -0,0 +1,32 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "pbl/services/notifications/notification_types.h" + +typedef enum AlertType { + AlertInvalid = NotificationInvalid, + AlertMobile = NotificationMobile, + AlertPhoneCall = NotificationPhoneCall, + AlertOther = NotificationOther, + AlertReminder = NotificationReminder +} AlertType; + +// Service to determine how and if the user gets alerted on a call/notification + +//! Call this function before alerting the user in any notification/call for the alerts service +//! to handle analytics operations. +void alerts_incoming_alert_analytics(); + +bool alerts_should_notify_for_type(AlertType type); + +bool alerts_should_enable_backlight_for_type(AlertType type); + +bool alerts_should_vibrate_for_type(AlertType type); + +//! When vibrating for an incoming notification, call this function to prevent multiple vibes +//! within a short period of time. +void alerts_set_notification_vibe_timestamp(); diff --git a/include/pbl/services/notifications/alerts_preferences.h b/include/pbl/services/notifications/alerts_preferences.h new file mode 100644 index 0000000000..24744dee57 --- /dev/null +++ b/include/pbl/services/notifications/alerts_preferences.h @@ -0,0 +1,77 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "alerts_private.h" + +typedef enum FirstUseSource { + FirstUseSourceManualDNDActionMenu = 0, + FirstUseSourceManualDNDSettingsMenu, + FirstUseSourceSmartDND, + FirstUseSourceDismiss +} FirstUseSource; + +typedef enum MuteBitfield { + MuteBitfield_None = 0b00000000, + MuteBitfield_Always = 0b01111111, + MuteBitfield_Weekdays = 0b00111110, + MuteBitfield_Weekends = 0b01000001, +} MuteBitfield; + +typedef enum { + DndNotificationModeHide = 0, + DndNotificationModeShow = 1, +} DndNotificationMode; + +//! Set notification display mode when DND is active +//! @param mode The display mode (Show or Hide) +void alerts_preferences_dnd_set_show_notifications(DndNotificationMode mode); + +//! @return The notification display mode when DND is active +DndNotificationMode alerts_preferences_dnd_get_show_notifications(void); + +//! Set whether the backlight should turn on with motion when DND is active +//! @param enable true to allow motion backlight, false to suppress it +void alerts_preferences_dnd_set_motion_backlight(bool enable); + +//! @return Whether motion backlight is enabled when DND is active +bool alerts_preferences_dnd_get_motion_backlight(void); + +//! Set whether tap / double-tap gestures should turn on the backlight when DND is active +//! @param enable true to allow touch backlight, false to suppress it +void alerts_preferences_dnd_set_touch_backlight(bool enable); + +//! @return Whether touch backlight is enabled when DND is active +bool alerts_preferences_dnd_get_touch_backlight(void); + +//! Set whether the speaker should be muted while DND is active. +//! @param enable true to mute the speaker during DND, false to allow audio +void alerts_preferences_dnd_set_mute_speaker(bool enable); + +//! @return Whether the speaker is muted while DND is active +bool alerts_preferences_dnd_get_mute_speaker(void); + +//! Set the always-on speaker mute. When set, the speaker is silenced +//! regardless of DND state. +//! @param muted true to mute the speaker, false to allow audio +void alerts_preferences_set_speaker_muted(bool muted); + +//! @return Whether the speaker is always-on muted +bool alerts_preferences_get_speaker_muted(void); + +//! Set the system-wide speaker volume cap. Per-playback volumes are scaled +//! by this value before being applied to the audio hardware. +//! @param volume Volume cap, 0-100. Values outside the range are clamped. +void alerts_preferences_set_speaker_volume(uint8_t volume); + +//! @return The system-wide speaker volume cap (0-100). Defaults to 100. +uint8_t alerts_preferences_get_speaker_volume(void); + +//! Checks whether a given "first use" dialog has been shown and sets it as complete +//! @param source The "first use" bit to check +//! @return true if the dialog has already been shown, false otherwise +bool alerts_preferences_check_and_set_first_use_complete(FirstUseSource source); diff --git a/include/pbl/services/notifications/alerts_preferences_private.h b/include/pbl/services/notifications/alerts_preferences_private.h new file mode 100644 index 0000000000..6072cea648 --- /dev/null +++ b/include/pbl/services/notifications/alerts_preferences_private.h @@ -0,0 +1,96 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/events.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/notification_types.h" +#include "pbl/services/vibes/vibe_intensity.h" +#include "util/time/time.h" +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_score_info.h" + +#define NOTIF_WINDOW_TIMEOUT_INFINITE ((uint32_t)~0) +#define NOTIF_WINDOW_TIMEOUT_DEFAULT (3 * MS_PER_MINUTE) + +void alerts_preferences_init(void); + +AlertMask alerts_preferences_get_alert_mask(void); + +void alerts_preferences_set_alert_mask(AlertMask mask); + +AlertMask alerts_preferences_dnd_get_mask(void); + +void alerts_preferences_dnd_set_mask(AlertMask mask); + +uint32_t alerts_preferences_get_notification_window_timeout_ms(void); + +void alerts_preferences_set_notification_window_timeout_ms(uint32_t timeout_ms); + +bool alerts_preferences_get_notification_alternative_design(void); + +void alerts_preferences_set_notification_alternative_design(bool alternative); + +bool alerts_preferences_get_notification_vibe_delay(void); + +void alerts_preferences_set_notification_vibe_delay(bool delay); + +bool alerts_preferences_get_notification_backlight(void); + +void alerts_preferences_set_notification_backlight(bool enable); + +typedef enum { + NotificationStatusBarStyle_Default = 0, + NotificationStatusBarStyle_Bold = 1, + NotificationStatusBarStyle_LargeBold = 2, + NotificationStatusBarStyleCount, +} NotificationStatusBarStyle; + +NotificationStatusBarStyle alerts_preferences_get_notification_status_bar_style(void); + +void alerts_preferences_set_notification_status_bar_style(NotificationStatusBarStyle style); + +bool alerts_preferences_get_vibrate(void); + +void alerts_preferences_set_vibrate(bool enable); + +VibeIntensity alerts_preferences_get_vibe_intensity(void); + +void alerts_preferences_set_vibe_intensity(VibeIntensity intensity); + +VibeScoreId alerts_preferences_get_vibe_score_for_client(VibeClient client); + +void alerts_preferences_set_vibe_score_for_client(VibeClient client, VibeScoreId id); + +bool alerts_preferences_dnd_is_manually_enabled(void); + +void alerts_preferences_dnd_set_manually_enabled(bool enable); + +void alerts_preferences_dnd_get_schedule(DoNotDisturbScheduleType type, + DoNotDisturbSchedule *schedule_out); + +void alerts_preferences_dnd_set_schedule(DoNotDisturbScheduleType type, + const DoNotDisturbSchedule *schedule); + +bool alerts_preferences_dnd_is_schedule_enabled(DoNotDisturbScheduleType type); + +void alerts_preferences_dnd_set_schedule_enabled(DoNotDisturbScheduleType type, bool enable); + +bool alerts_preferences_dnd_is_smart_enabled(void); + +void alerts_preferences_dnd_set_smart_enabled(bool enable); + +//! Lock the alerts preferences mutex. Must be paired with alerts_preferences_unlock(). +void alerts_preferences_lock(void); + +//! Unlock the alerts preferences mutex. Must be paired with alerts_preferences_lock(). +void alerts_preferences_unlock(void); + +//! Process a BlobDB event for notification preferences. For BlobDBEventTypeInsert +//! events, this method will update the internal global copy of that preference based on the +//! new value that was placed into the backing store. +//! @param[in] event pointer to the blob DB event +void alerts_preferences_handle_blob_db_event(PebbleBlobDBEvent *event); + diff --git a/include/pbl/services/notifications/alerts_private.h b/include/pbl/services/notifications/alerts_private.h new file mode 100644 index 0000000000..2c3e674217 --- /dev/null +++ b/include/pbl/services/notifications/alerts_private.h @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/notifications/alerts.h" + +typedef enum AlertMask { + AlertMaskAllOff = 0, + AlertMaskPhoneCalls = NotificationPhoneCall, + AlertMaskOther = NotificationOther, + AlertMaskAllOnLegacy = + NotificationMobile | NotificationPhoneCall | NotificationOther, + AlertMaskAllOn = + NotificationMobile | NotificationPhoneCall | NotificationOther | NotificationReminder +} AlertMask; + +bool alerts_get_vibrate(void); + +AlertMask alerts_get_mask(void); + +AlertMask alerts_get_dnd_mask(void); + +uint32_t alerts_get_notification_window_timeout_ms(void); + +void alerts_set_vibrate(bool enable); + +void alerts_set_mask(AlertMask mask); + +void alerts_set_dnd_mask(AlertMask mask); + +void alerts_set_notification_window_timeout_ms(uint32_t timeout_ms); + +void alerts_init(void); diff --git a/include/pbl/services/notifications/ancs/ancs_filtering.h b/include/pbl/services/notifications/ancs/ancs_filtering.h new file mode 100644 index 0000000000..fc6d59dc00 --- /dev/null +++ b/include/pbl/services/notifications/ancs/ancs_filtering.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" + +//! Updates the entry in the notif pref db for a given app +//! @param app_notif_prefs The existing prefs for the app we want to update. These prefs could be +//! updated in the process +//! @param app_id ID of the app we're recording +//! @param display_name Display name of the app we're recording +//! @param title Title attribute from the notification associated with the app we're recording +//! (fallback in the event that we don't have a display name such as in the case of Apple Pay) +void ancs_filtering_record_app(iOSNotifPrefs **app_notif_prefs, + const ANCSAttribute *app_id, + const ANCSAttribute *display_name, + const ANCSAttribute *title); + +//! Returns true if a given app is muted for the current day +//! @param app_notif_prefs Prefs for the given app loaded from the notif pref db +//! @return true if the given app is muted +bool ancs_filtering_is_muted(const iOSNotifPrefs *app_notif_prefs); + +//! Returns the mute type for an app +//! @param app_notif_prefs Prefs for the given app loaded from the notif pref db +//! @return MuteBitfield which is the mute type of the app +uint8_t ancs_filtering_get_mute_type(const iOSNotifPrefs *app_notif_prefs); + +//! Returns true when the app's rules say to filter a notification. +//! @param app_notif_prefs Prefs for the given app loaded from the notif pref db +//! @param title Notification title +//! @param subtitle Notification subtitle, matched as part of the title field +//! @param body Notification body/message +bool ancs_filtering_matches_rules(const iOSNotifPrefs *app_notif_prefs, + const ANCSAttribute *title, + const ANCSAttribute *subtitle, + const ANCSAttribute *body); diff --git a/include/pbl/services/notifications/ancs/ancs_item.h b/include/pbl/services/notifications/ancs/ancs_item.h new file mode 100644 index 0000000000..f9cf774ce6 --- /dev/null +++ b/include/pbl/services/notifications/ancs/ancs_item.h @@ -0,0 +1,33 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "ancs_notifications_util.h" + +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/timeline/timeline.h" + +//! Creates a new timeline item from ANCS data +//! @param notif_attributes ANCS Notification attributes +//! @param app_attributes ANCS App attributes (namely, the display name) +//! @param app_metadata The icon and color associated with the app +//! @param notif_prefs iOS notification prefs for this notification +//! @param timestamp Time the notification occured +//! @param properties Additional ANCS properties (category, flags, etc) +//! @return The newly created timeline item +TimelineItem *ancs_item_create_and_populate(ANCSAttribute *notif_attributes[], + ANCSAttribute *app_attributes[], + const ANCSAppMetadata *app_metadata, + iOSNotifPrefs *notif_prefs, + time_t timestamp, + ANCSProperty properties); + +//! Replaces the dismiss action of an existing timeline item with the ancs negative action +//! @param item The timeline item to update +//! @param uid The uid of the ANCS notification we're using the dismiss action from +//! @param attr_action_neg The negative action from the ANCS notification to use as the new +//! dismiss action +void ancs_item_update_dismiss_action(TimelineItem *item, uint32_t uid, + const ANCSAttribute *attr_action_neg); diff --git a/include/pbl/services/notifications/ancs/ancs_known_apps.h b/include/pbl/services/notifications/ancs/ancs_known_apps.h new file mode 100644 index 0000000000..db758bd33d --- /dev/null +++ b/include/pbl/services/notifications/ancs/ancs_known_apps.h @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +// @nolint +// please don't change these values manually, they are derived from the spreadsheet +// "Notification Colors" + +#define APP(id, icon, color) { id, icon, color } + + APP(IOS_CALENDAR_APP_ID, TIMELINE_RESOURCE_TIMELINE_CALENDAR, GColorRedARGB8), + APP(IOS_FACETIME_APP_ID, TIMELINE_RESOURCE_NOTIFICATION_FACETIME, GColorIslamicGreenARGB8), + APP(IOS_MAIL_APP_ID, TIMELINE_RESOURCE_GENERIC_EMAIL, GColorVividCeruleanARGB8), + APP(IOS_PHONE_APP_ID, TIMELINE_RESOURCE_DURING_PHONE_CALL, GColorPictonBlueARGB8), + APP(IOS_REMINDERS_APP_ID, TIMELINE_RESOURCE_NOTIFICATION_REMINDER, GColorFollyARGB8), + APP(IOS_SMS_APP_ID, TIMELINE_RESOURCE_GENERIC_SMS, GColorIslamicGreenARGB8), + APP("com.atebits.Tweetie2", TIMELINE_RESOURCE_NOTIFICATION_TWITTER, GColorVividCeruleanARGB8), + APP("com.burbn.instagram", TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM, GColorCobaltBlueARGB8), + APP("com.facebook.Facebook", TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK, GColorCobaltBlueARGB8), + APP("com.facebook.Messenger", TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER, GColorBlueMoonARGB8), + APP("com.getpebble.pebbletime", TIMELINE_RESOURCE_NOTIFICATION_FLAG, GColorOrangeARGB8), + APP("com.google.calendar", TIMELINE_RESOURCE_TIMELINE_CALENDAR, GColorVeryLightBlueARGB8), + APP("com.google.Gmail", TIMELINE_RESOURCE_NOTIFICATION_GMAIL, GColorRedARGB8), + APP("com.google.hangouts", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS, GColorJaegerGreenARGB8), + APP("com.google.inbox", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX, GColorBlueMoonARGB8), + APP("com.microsoft.Office.Outlook", TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK, GColorCobaltBlueARGB8), + APP("com.orchestra.v2", TIMELINE_RESOURCE_NOTIFICATION_MAILBOX, GColorVividCeruleanARGB8), + APP("com.skype.skype", TIMELINE_RESOURCE_NOTIFICATION_SKYPE, GColorVividCeruleanARGB8), + APP("com.tapbots.Tweetbot3", TIMELINE_RESOURCE_NOTIFICATION_TWITTER, GColorVividCeruleanARGB8), + APP("com.toyopagroup.picaboo", TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT, GColorIcterineARGB8), + APP("com.yahoo.Aerogram", TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL, GColorIndigoARGB8), + APP("jp.naver.line", TIMELINE_RESOURCE_NOTIFICATION_LINE, GColorIslamicGreenARGB8), + APP("net.whatsapp.WhatsApp", TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP, GColorIslamicGreenARGB8), + APP("ph.telegra.Telegraph", TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM, GColorVividCeruleanARGB8), + APP("com.blackberry.bbm1", TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER, GColorDarkGrayARGB8), + APP("com.getpebble.pebbletime.enterprise", TIMELINE_RESOURCE_NOTIFICATION_FLAG, GColorOrangeARGB8), + APP("com.google.GoogleMobile", TIMELINE_RESOURCE_NOTIFICATION_GENERIC, GColorBlueMoonARGB8), + APP("com.google.ios.youtube", TIMELINE_RESOURCE_NOTIFICATION_YOUTUBE, GColorRedARGB8), + APP("com.hipchat.ios", TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT, GColorCobaltBlueARGB8), + APP("com.iwilab.KakaoTalk", TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK, GColorYellowARGB8), + APP("com.kik.chat", TIMELINE_RESOURCE_NOTIFICATION_KIK, GColorIslamicGreenARGB8), + APP("com.tencent.xin", TIMELINE_RESOURCE_NOTIFICATION_WECHAT, GColorKellyGreenARGB8), + APP("com.viber", TIMELINE_RESOURCE_NOTIFICATION_VIBER, GColorVividVioletARGB8), + APP("com.amazon.Amazon", TIMELINE_RESOURCE_NOTIFICATION_AMAZON, GColorChromeYellowARGB8), + APP("com.google.Maps", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS, GColorBlueMoonARGB8), + APP("com.google.photos", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS, GColorBlueMoonARGB8), + APP("com.apple.mobileslideshow", TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS, GColorBlueMoonARGB8), + APP("com.linkedin.LinkedIn", TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN, GColorCobaltBlueARGB8), + APP("com.tinyspeck.chatlyio", TIMELINE_RESOURCE_NOTIFICATION_SLACK, GColorFollyARGB8), + APP("com.automattic.beeper", TIMELINE_RESOURCE_NOTIFICATION_BEEPER, GColorVividVioletARGB8), + APP("xyz.blueskyweb.app", TIMELINE_RESOURCE_NOTIFICATION_BLUESKY, GColorVividCeruleanARGB8), + APP("com.hammerandchisel.discord", TIMELINE_RESOURCE_NOTIFICATION_DISCORD, GColorIndigoARGB8), + APP("com.duolingo.DuolingoMobile", TIMELINE_RESOURCE_NOTIFICATION_DUOLINGO, GColorGreenARGB8), + APP("im.vector.app", TIMELINE_RESOURCE_NOTIFICATION_ELEMENT, GColorMediumAquamarineARGB8), + APP("io.element.elementx", TIMELINE_RESOURCE_NOTIFICATION_ELEMENT, GColorDarkGreenARGB8), + APP("com.google.Dynamite", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_CHAT, GColorGreenARGB8), + APP("com.google.tasks", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_TASKS, GColorBlueMoonARGB8), + APP("io.robbie.HomeAssistant", TIMELINE_RESOURCE_NOTIFICATION_HOME_ASSISTANT, GColorVividCeruleanARGB8), + APP("com.valvesoftware.Steam", TIMELINE_RESOURCE_NOTIFICATION_STEAM, GColorCobaltBlueARGB8), + APP("com.microsoft.skype.teams", TIMELINE_RESOURCE_NOTIFICATION_TEAMS, GColorIndigoARGB8), + APP("com.burbn.barcelona", TIMELINE_RESOURCE_NOTIFICATION_THREADS, GColorDarkGrayARGB8), + APP("com.ubnt.protect", TIMELINE_RESOURCE_NOTIFICATION_UNIFI_PROTECT, GColorBlueMoonARGB8), + APP("us.zoom.videomeetings", TIMELINE_RESOURCE_NOTIFICATION_ZOOM, GColorVividCeruleanARGB8), + APP("com.ebay.iphone", TIMELINE_RESOURCE_NOTIFICATION_EBAY, GColorRedARGB8), + APP("org.whispersystems.signal", TIMELINE_RESOURCE_NOTIFICATION_SIGNAL, GColorBlueMoonARGB8), + APP("tv.twitch", TIMELINE_RESOURCE_NOTIFICATION_TWITCH, GColorVividVioletARGB8), + APP("com.airmailapp.iphone", TIMELINE_RESOURCE_NOTIFICATION_AIRMAIL, GColorBlueMoonARGB8), + APP("com.reddit.Reddit", TIMELINE_RESOURCE_NOTIFICATION_REDDIT, GColorOrangeARGB8), + APP("com.foursquare.robin", TIMELINE_RESOURCE_NOTIFICATION_SWARM, GColorOrangeARGB8), + APP("com.tplink.tapo", TIMELINE_RESOURCE_NOTIFICATION_TAPO, GColorVividCeruleanARGB8), + APP("com.revolut.revolut", TIMELINE_RESOURCE_PAY_BILL, GColorDarkGrayARGB8), + APP("com.transferwise.Transferwise", TIMELINE_RESOURCE_PAY_BILL, GColorGreenARGB8), + APP("de.no26.Number26", TIMELINE_RESOURCE_PAY_BILL, GColorCadetBlueARGB8), + APP("com.bunq.ios", TIMELINE_RESOURCE_PAY_BILL, GColorVividCeruleanARGB8), + +#undef APP diff --git a/include/pbl/services/notifications/ancs/ancs_notifications.h b/include/pbl/services/notifications/ancs/ancs_notifications.h new file mode 100644 index 0000000000..547483ea1a --- /dev/null +++ b/include/pbl/services/notifications/ancs/ancs_notifications.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" +#include "pbl/services/timeline/item.h" + + +void ancs_notifications_handle_message(uint32_t uid, + ANCSProperty properties, + ANCSAttribute **notif_attributes, + ANCSAttribute **app_attributes); + +void ancs_notifications_handle_notification_removed(uint32_t ancs_uid, ANCSProperty properties); diff --git a/include/pbl/services/notifications/ancs/ancs_notifications_util.h b/include/pbl/services/notifications/ancs/ancs_notifications_util.h new file mode 100644 index 0000000000..3479d437da --- /dev/null +++ b/include/pbl/services/notifications/ancs/ancs_notifications_util.h @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gtypes.h" +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" +#include "util/attributes.h" +#include "util/time/time.h" + +#define IOS_PHONE_APP_ID "com.apple.mobilephone" +#define IOS_CALENDAR_APP_ID "com.apple.mobilecal" +#define IOS_REMINDERS_APP_ID "com.apple.reminders" +#define IOS_MAIL_APP_ID "com.apple.mobilemail" +#define IOS_SMS_APP_ID "com.apple.MobileSMS" +#define IOS_FACETIME_APP_ID "com.apple.facetime" + +typedef struct PACKED ANCSAppMetadata { + const char *app_id; + uint32_t icon_id; +#if PBL_COLOR + uint8_t app_color; +#endif + bool is_blocked:1; // #include @@ -94,7 +81,7 @@ void do_not_disturb_handle_calendar_event(PebbleCalendarEvent *e); void do_not_disturb_manual_toggle_with_dialog(void); #if UNITTEST -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" TimerID get_dnd_timer_id(void); void set_dnd_timer_id(TimerID id); #endif diff --git a/include/pbl/services/notifications/do_not_disturb_toggle.h b/include/pbl/services/notifications/do_not_disturb_toggle.h new file mode 100644 index 0000000000..1ce8e3f86a --- /dev/null +++ b/include/pbl/services/notifications/do_not_disturb_toggle.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/action_toggle.h" + +void do_not_disturb_toggle_push(ActionTogglePrompt prompt, bool set_exit_reason); diff --git a/include/pbl/services/notifications/notification_constants.h b/include/pbl/services/notifications/notification_constants.h new file mode 100644 index 0000000000..e953026468 --- /dev/null +++ b/include/pbl/services/notifications/notification_constants.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gtypes.h" + +#define SMS_REPLY_COLOR GColorIslamicGreen + +// Notif pref db key for send text +#define SEND_TEXT_NOTIF_PREF_KEY "com.pebble.sendText" + +// Notif pref db keys for incoming call reply +#define ANDROID_PHONE_KEY "com.pebble.android.phone" +#define IOS_PHONE_KEY "com.apple.mobilephone" diff --git a/src/fw/services/normal/notifications/notification_storage.h b/include/pbl/services/notifications/notification_storage.h similarity index 82% rename from src/fw/services/normal/notifications/notification_storage.h rename to include/pbl/services/notifications/notification_storage.h index 93fd4e4feb..4ad10b3e57 100644 --- a/src/fw/services/normal/notifications/notification_storage.h +++ b/include/pbl/services/notifications/notification_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/notifications/notification_storage_private.h b/include/pbl/services/notifications/notification_storage_private.h new file mode 100644 index 0000000000..bd5d19127e --- /dev/null +++ b/include/pbl/services/notifications/notification_storage_private.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Notification storage file size +#define NOTIFICATION_STORAGE_FILE_SIZE (30 * 1024) + +//! Minimum increment of space to free up when compressing. +//! The higher the value, the less often we need to compress, +//! but we will lose more notifications +#define NOTIFICATION_STORAGE_MINIMUM_INCREMENT_SIZE (NOTIFICATION_STORAGE_FILE_SIZE / 4) + diff --git a/include/pbl/services/notifications/notification_types.h b/include/pbl/services/notifications/notification_types.h new file mode 100644 index 0000000000..7013d09524 --- /dev/null +++ b/include/pbl/services/notifications/notification_types.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/uuid.h" + +//! This list is shared by notifications and reminders. +typedef enum { + NotificationInvalid = 0, + NotificationMobile = (1 << 0), + NotificationPhoneCall = (1 << 1), + NotificationOther = (1 << 2), + NotificationReminder = (1 << 3) +} NotificationType; + +//! Type and Id for the notification or reminder. +typedef struct { + NotificationType type; + Uuid id; +} NotificationInfo; diff --git a/include/pbl/services/notifications/notifications.h b/include/pbl/services/notifications/notifications.h new file mode 100644 index 0000000000..3722f080cf --- /dev/null +++ b/include/pbl/services/notifications/notifications.h @@ -0,0 +1,51 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/timeline/item.h" + +#include +#include + +typedef enum { + ActionResultTypeSuccess, + ActionResultTypeFailure, + ActionResultTypeChaining, + ActionResultTypeDoResponse, + ActionResultTypeSuccessANCSDismiss, +} ActionResultType; + +typedef struct { + Uuid id; + ActionResultType type; + AttributeList attr_list; + TimelineItemActionGroup action_group; +} PebbleSysNotificationActionResult; + + +void notifications_init(void); + +//! Feedback for the result of an invoke action command +void notifications_handle_notification_action_result( + PebbleSysNotificationActionResult *action_result); + +//! Add a notification. +void notifications_handle_notification_added(Uuid *notification_id); + +//! Handle a notification getting acted upon on the phone +void notifications_handle_notification_acted_upon(Uuid *notification_id); + +//! Remove a notification +void notifications_handle_notification_removed(Uuid *notification_id); + +//! Notify of remove command from ANCS. Notification will be kept in history +void notifications_handle_ancs_notification_removed(uint32_t ancs_uid); + +//! Migration hook for notifications +//! Called with the GMT offset of the new timezone +void notifications_migrate_timezone(const int new_tz_offset); + +//! Inserts a new notification into notification storage and notifies the system of the new item +//! @param notification Pointer to the notification to add +void notifications_add_notification(TimelineItem *notification); diff --git a/include/pbl/services/orientation_manager.h b/include/pbl/services/orientation_manager.h new file mode 100644 index 0000000000..151381445c --- /dev/null +++ b/include/pbl/services/orientation_manager.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifdef CONFIG_ORIENTATION_MANAGER +#include "shell/prefs.h" + +void orientation_handle_prefs_changed(void); + +void orientation_manager_enable(bool on); +#endif \ No newline at end of file diff --git a/include/pbl/services/persist.h b/include/pbl/services/persist.h new file mode 100644 index 0000000000..048fe293a2 --- /dev/null +++ b/include/pbl/services/persist.h @@ -0,0 +1,47 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Persist service +//! +//! The persist service manages persistent app key-value stores. A persistent +//! store is simply a SettingsFile identified by the app's UUID. The service +//! manages the creation, opening and deletion of persist stores so that an app +//! and its worker can both access the same file through a single file handle +//! and SettingsFile state object. +//! +//! The persist service makes no attempt to make SettingsFile reentrant; it is +//! the caller's responsibility to enforce mutual exclusion and prevent +//! concurrent access to the SettingsFile. + +#include +#include + +#include "util/uuid.h" +#include "process_management/app_install_types.h" +#include "system/status_codes.h" + +typedef struct SettingsFile SettingsFile; + + +//! Initialize the persist service. +void persist_service_init(void); + +//! Get the per-app persistent storage capacity in bytes. +size_t persist_service_get_max_size(void); + +//! Lock and get the persist store for the given app. +SettingsFile *persist_service_lock_and_get_store(const Uuid *uuid); + +//! Unlock the given persist store. +void persist_service_unlock_store(SettingsFile *store); + +//! Call during each process's startup. +void persist_service_client_open(const Uuid *uuid); + +//! Call once after proces exits to clean it up. +void persist_service_client_close(const Uuid *uuid); + +//! Deletes the app's persist file. +status_t persist_service_delete_file(const Uuid *uuid); diff --git a/include/pbl/services/phone_call.h b/include/pbl/services/phone_call.h new file mode 100644 index 0000000000..7e6ed18171 --- /dev/null +++ b/include/pbl/services/phone_call.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/events.h" + +void phone_call_service_init(void); + +void phone_call_answer(void); + +void phone_call_decline(void); diff --git a/include/pbl/services/phone_call_util.h b/include/pbl/services/phone_call_util.h new file mode 100644 index 0000000000..34fa58d058 --- /dev/null +++ b/include/pbl/services/phone_call_util.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef struct PebblePhoneCaller { + char *number; + char *name; +} PebblePhoneCaller; + +//! Creates a new caller to pass as part of a phone event +//! @param number The phone number for this caller +//! @param name The name of the caller +//! @return Pointer to new caller +PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name); + +//! Destroys a caller previously created with \ref phone_call_util_create_caller +//! @param caller The caller to free +void phone_call_util_destroy_caller(PebblePhoneCaller *caller); diff --git a/include/pbl/services/phone_pp.h b/include/pbl/services/phone_pp.h new file mode 100644 index 0000000000..41394ef08d --- /dev/null +++ b/include/pbl/services/phone_pp.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + + +void pp_answer_call(uint32_t cookie); + +void pp_decline_call(uint32_t cookie); + +void pp_get_phone_state(void); + +//! Enables or disables handling the Get Phone State responses. +//! This is part of a work-around to ignore for stray requests that can be in flight after the phone +//! call has been declined by the user from the Pebble. +void pp_get_phone_state_set_enabled(bool enabled); diff --git a/include/pbl/services/ping.h b/include/pbl/services/ping.h new file mode 100644 index 0000000000..35d915d444 --- /dev/null +++ b/include/pbl/services/ping.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// If a ping is due to be sent, send it. This should be called when we are already sending other +// data to the phone anyways in order to minimize the number of times we have to wake up the phone. +// It will return without doing anything if a minimum amount of time (currently 1 hour) +// has not elapsed since the last ping was sent out. +void ping_send_if_due(void); diff --git a/src/fw/services/common/poll_remote.h b/include/pbl/services/poll_remote.h similarity index 75% rename from src/fw/services/common/poll_remote.h rename to include/pbl/services/poll_remote.h index 108ea7c4f6..8a3d27c540 100644 --- a/src/fw/services/common/poll_remote.h +++ b/include/pbl/services/poll_remote.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file poll_remote.h //! @brief Subsystem to send a "poll services" message at regular intervals to the iOS app. @@ -22,7 +9,7 @@ //! @author martijn #pragma once -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" #include typedef enum { diff --git a/include/pbl/services/powermode_service.h b/include/pbl/services/powermode_service.h new file mode 100644 index 0000000000..6a1114fdb5 --- /dev/null +++ b/include/pbl/services/powermode_service.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Initialize the power mode service. +void powermode_service_init(void); + +//! Enable or disable the power mode service. When disabled, request and +//! release calls are no-ops. +void powermode_service_set_enabled(bool enabled); + +//! Request high-performance CPU mode. Must be paired with a +//! powermode_service_release_hp() call. +void powermode_service_request_hp(void); + +//! Release a previously requested high-performance mode. The CPU will +//! return to low-power mode only when all clients have released. +void powermode_service_release_hp(void); diff --git a/include/pbl/services/prf_update.h b/include/pbl/services/prf_update.h new file mode 100644 index 0000000000..6e7a1320d0 --- /dev/null +++ b/include/pbl/services/prf_update.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +void check_prf_update(void); diff --git a/include/pbl/services/process_management/app_order_storage.h b/include/pbl/services/process_management/app_order_storage.h new file mode 100644 index 0000000000..55ee594d5e --- /dev/null +++ b/include/pbl/services/process_management/app_order_storage.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_install_manager.h" +#include "util/attributes.h" + +typedef struct PACKED AppMenuOrderStorage { + uint8_t list_length; + AppInstallId id_list[]; +} AppMenuOrderStorage; + +void app_order_storage_init(void); + +#if UNITTEST +//! Reset app_order_storage state for testing - clears cached "file missing" flag. +//! Not built into production firmware (tests only). +void app_order_storage_reset_for_tests(void); +#endif + +//! Returns an AppMenuOrderStorage struct on the kernel heap +AppMenuOrderStorage *app_order_read_order(void); + +//! Writes a list of UUID's to the order file +void write_uuid_list_to_file(const Uuid *uuid_list, uint8_t count); diff --git a/src/fw/services/normal/process_management/app_storage.h b/include/pbl/services/process_management/app_storage.h similarity index 81% rename from src/fw/services/normal/process_management/app_storage.h rename to include/pbl/services/process_management/app_storage.h index 2e69307529..3715dd2b4d 100644 --- a/src/fw/services/normal/process_management/app_storage.h +++ b/include/pbl/services/process_management/app_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/process_management/process_loader_flash.h b/include/pbl/services/process_management/process_loader_flash.h new file mode 100644 index 0000000000..dd0b300805 --- /dev/null +++ b/include/pbl/services/process_management/process_loader_flash.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +bool process_loader_load_from_flash(PebbleTask task, const ProcessConfig *config, + const PebbleProcessMd *app_md) { +} diff --git a/src/fw/services/normal/protobuf_log/protobuf_log.h b/include/pbl/services/protobuf_log/protobuf_log.h similarity index 90% rename from src/fw/services/normal/protobuf_log/protobuf_log.h rename to include/pbl/services/protobuf_log/protobuf_log.h index 413d5bc0db..15096d79c5 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log.h +++ b/include/pbl/services/protobuf_log/protobuf_log.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,8 +7,8 @@ //! using the protobuf schema defined at src/fw/idl/nanopb/*.proto and sent to the phone via //! data logging. -#include "services/common/hrm/hrm_manager.h" -#include "services/normal/data_logging/dls_private.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/data_logging/dls_private.h" #include "system/version.h" #include "util/uuid.h" diff --git a/include/pbl/services/protobuf_log/protobuf_log_activity_sessions.h b/include/pbl/services/protobuf_log/protobuf_log_activity_sessions.h new file mode 100644 index 0000000000..7afd77d159 --- /dev/null +++ b/include/pbl/services/protobuf_log/protobuf_log_activity_sessions.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "protobuf_log.h" + +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/activity/activity.h" + +#include +#include + +ProtobufLogRef protobuf_log_activity_sessions_create(void); + +bool protobuf_log_activity_sessions_add(ProtobufLogRef ref, time_t sample_utc, + ActivitySession *session); + +bool protobuf_log_activity_sessions_decode(pebble_pipeline_Event *event_in, + ActivitySession *session_out); diff --git a/include/pbl/services/protobuf_log/protobuf_log_hr.h b/include/pbl/services/protobuf_log/protobuf_log_hr.h new file mode 100644 index 0000000000..55324b4512 --- /dev/null +++ b/include/pbl/services/protobuf_log/protobuf_log_hr.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "protobuf_log.h" + +#include "pbl/services/hrm/hrm_manager.h" + +#include +#include +#include + +ProtobufLogRef protobuf_log_hr_create(ProtobufLogTransportCB transport); + +bool protobuf_log_hr_add_sample(ProtobufLogRef ref, time_t sample_utc, uint8_t bpm, + HRMQuality quality); diff --git a/include/pbl/services/protobuf_log/protobuf_log_private.h b/include/pbl/services/protobuf_log/protobuf_log_private.h new file mode 100644 index 0000000000..9ef4fe956e --- /dev/null +++ b/include/pbl/services/protobuf_log/protobuf_log_private.h @@ -0,0 +1,36 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pb.h" +#include "pb_decode.h" +#include "pb_encode.h" + +#include "protobuf_log.h" + +#include "util/attributes.h" + +#include + +// This fixed size header is placed at the beginning of the buffer, before the protobuf +// encoded message +typedef struct PACKED { + uint16_t msg_size; +} PLogMessageHdr; + +// Internal structure of a protobuf log session +typedef struct PLogSession { + // TODO Change comments from MeasurementSet to MeasurementSet/Events + + ProtobufLogConfig config; + uint8_t *msg_buffer; // allocated buffer for the final record: PLogMessageHdr + Payload + uint8_t *data_buffer; // allocated buffer for the encoded data blob. (e.g. MeasurementSet) + // We form the MeasurementSet first, and then after it's complete we + // copy it into msg_buffer inside of a Payload + size_t max_msg_size; // max # of bytes to use in the allocated buffer + size_t max_data_size; // max allowed size of the encoded data blob + pb_ostream_t data_stream; // output stream we are writing the data blob to + time_t start_utc; // UTC time when session was created + ProtobufLogTransportCB transport; +} PLogSession; diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_test.h b/include/pbl/services/protobuf_log/protobuf_log_test.h similarity index 89% rename from src/fw/services/normal/protobuf_log/protobuf_log_test.h rename to include/pbl/services/protobuf_log/protobuf_log_test.h index ddcb1ad8c4..8ab4012750 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log_test.h +++ b/include/pbl/services/protobuf_log/protobuf_log_test.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "protobuf_log.h" -#include "services/normal/activity/activity.h" +#include "pbl/services/activity/activity.h" #include diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_util.h b/include/pbl/services/protobuf_log/protobuf_log_util.h similarity index 75% rename from src/fw/services/normal/protobuf_log/protobuf_log_util.h rename to include/pbl/services/protobuf_log/protobuf_log_util.h index cb056083f5..a70cd315c7 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log_util.h +++ b/include/pbl/services/protobuf_log/protobuf_log_util.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/put_bytes/put_bytes.h b/include/pbl/services/put_bytes/put_bytes.h new file mode 100644 index 0000000000..2056365740 --- /dev/null +++ b/include/pbl/services/put_bytes/put_bytes.h @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +typedef struct PebbleCommSessionEvent PebbleCommSessionEvent; + +typedef enum { + ObjectUnknown = 0x00, + ObjectFirmware = 0x01, + ObjectRecovery = 0x02, + ObjectSysResources = 0x03, + ObjectAppResources = 0x04, + ObjectWatchApp = 0x05, + ObjectFile = 0x06, + ObjectWatchWorker = 0x07, + NumObjects +} PutBytesObjectType; + +typedef struct PbInstallStatus { + uint32_t num_bytes_written; + uint32_t crc_of_bytes; +} PbInstallStatus; + +void put_bytes_init(void); + +//! Tells put_bytes to clean up instantly. If put_bytes is already cleaned up, this is a no-op. +//! Any future messages sent by clients will be NACK'ed appropriately. +//! NOTE: Must be called from the KernelBackground task. +void put_bytes_cancel(void); + +//! Reset all put bytes state. Only useful for unit tests. +void put_bytes_deinit(void); + +//! Sets an initialization timeout for put_bytes. +//! If the phone doesn't send any data within the specified timeout, +//! put_bytes raises a timeout event. +void put_bytes_expect_init(uint32_t timeout_ms); + +//! Informs Put Bytes when the Pebble app disconnects to the Pebble, to make +//! it cancel any on-going transaction. +void put_bytes_handle_comm_session_event(const PebbleCommSessionEvent *app_event); diff --git a/src/fw/services/common/put_bytes/put_bytes_storage.h b/include/pbl/services/put_bytes/put_bytes_storage.h similarity index 84% rename from src/fw/services/common/put_bytes/put_bytes_storage.h rename to include/pbl/services/put_bytes/put_bytes_storage.h index 9712845aca..719a33f381 100644 --- a/src/fw/services/common/put_bytes/put_bytes_storage.h +++ b/include/pbl/services/put_bytes/put_bytes_storage.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include -#include "services/common/put_bytes/put_bytes.h" +#include "pbl/services/put_bytes/put_bytes.h" struct PutBytesStorageImplementation; typedef struct PutBytesStorageImplementation PutBytesStorageImplementation; diff --git a/include/pbl/services/put_bytes/put_bytes_storage_file.h b/include/pbl/services/put_bytes/put_bytes_storage_file.h new file mode 100644 index 0000000000..492dbb2720 --- /dev/null +++ b/include/pbl/services/put_bytes/put_bytes_storage_file.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "put_bytes_storage.h" + +bool pb_storage_file_init(PutBytesStorage *storage, PutBytesObjectType object_type, + uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset); + +uint32_t pb_storage_file_get_max_size(PutBytesObjectType object_type); + +void pb_storage_file_write(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, + uint32_t length); + +uint32_t pb_storage_file_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type); + +void pb_storage_file_deinit(PutBytesStorage *storage, bool is_success); diff --git a/include/pbl/services/put_bytes/put_bytes_storage_internal.h b/include/pbl/services/put_bytes/put_bytes_storage_internal.h new file mode 100644 index 0000000000..5fb8063ed7 --- /dev/null +++ b/include/pbl/services/put_bytes/put_bytes_storage_internal.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "put_bytes_storage.h" + +#include + +typedef struct PutBytesStorageImplementation { + bool (*init)(PutBytesStorage *storage, PutBytesObjectType object_type, + uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset); + + uint32_t (*get_max_size)(PutBytesObjectType object_type); + + void (*write)(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, uint32_t length); + + uint32_t (*calculate_crc)(PutBytesStorage *storage, PutBytesCrcType crc_type); + + void (*deinit)(PutBytesStorage *storage, bool is_success); +} PutBytesStorageImplementation; diff --git a/include/pbl/services/put_bytes/put_bytes_storage_raw.h b/include/pbl/services/put_bytes/put_bytes_storage_raw.h new file mode 100644 index 0000000000..6151a4f73f --- /dev/null +++ b/include/pbl/services/put_bytes/put_bytes_storage_raw.h @@ -0,0 +1,24 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "put_bytes_storage.h" + +#include + +bool pb_storage_raw_init(PutBytesStorage *storage, PutBytesObjectType object_type, + uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset); + +uint32_t pb_storage_raw_get_max_size(PutBytesObjectType object_type); + +void pb_storage_raw_write(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, + uint32_t length); + +uint32_t pb_storage_raw_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type); + + +void pb_storage_raw_deinit(PutBytesStorage *storage, bool is_success); + + +bool pb_storage_raw_get_status(PutBytesObjectType obj_type, PbInstallStatus *status); diff --git a/include/pbl/services/registry_endpoint.h b/include/pbl/services/registry_endpoint.h new file mode 100644 index 0000000000..0765bc349d --- /dev/null +++ b/include/pbl/services/registry_endpoint.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/comm_session/protocol.h" + +typedef enum { + RegistryEndpointIdSystem = 5000, + RegistryEndpointIdFactory = 5001, +} RegistryEndpointId; + +void registry_endpoint_callback(CommSession *session, const uint8_t* data, unsigned int length_bytes); + +void factory_registry_endpoint_callback(CommSession *session, const uint8_t* data, unsigned int length_bytes); diff --git a/src/fw/services/common/regular_timer.h b/include/pbl/services/regular_timer.h similarity index 83% rename from src/fw/services/common/regular_timer.h rename to include/pbl/services/regular_timer.h index 737f9e9534..c253387111 100644 --- a/src/fw/services/common/regular_timer.h +++ b/include/pbl/services/regular_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/runlevel.def b/include/pbl/services/runlevel.def new file mode 100644 index 0000000000..24b0940b22 --- /dev/null +++ b/include/pbl/services/runlevel.def @@ -0,0 +1,11 @@ +// Run Level definitions +// +// To add a new runlevel definition, add a new line with RUNLEVEL(number, name). + +RUNLEVEL(0, BareMinimum) +RUNLEVEL(1, LowPower) +RUNLEVEL(2, Stationary) +RUNLEVEL(3, FirmwareUpdate) +RUNLEVEL(4, Normal) + +// vim:filetype=c diff --git a/src/fw/services/runlevel.h b/include/pbl/services/runlevel.h similarity index 84% rename from src/fw/services/runlevel.h rename to include/pbl/services/runlevel.h index d41f368897..9a9a7f89ed 100644 --- a/src/fw/services/runlevel.h +++ b/include/pbl/services/runlevel.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/runlevel_impl.h b/include/pbl/services/runlevel_impl.h new file mode 100644 index 0000000000..e0b25a7255 --- /dev/null +++ b/include/pbl/services/runlevel_impl.h @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// Definitions used to implement runlevels. +// +// The set of runlevels is defined in the runlevel.def X-Macro file. These +// definitions are used to construct two enums, RunLevel (in runlevel.h) and +// RunLevelBit (in this header). +// +// The set of runlevels for which a service should be enabled is defined by +// bitwise-OR-ing the RunLevelBit constants for every runlevel that the service +// should be enabled in to form an enable-mask. Testing whether a service should +// be enabled for a given runlevel is simply +// (enable_mask & (1 << runlevel) != 0). +// +// The RunLevelBit constants take the form R_ to minimize visual clutter +// when defining enable-masks. Since this header is only included in the source +// files for which enable-masks are defined, the potential for namespace +// pollution is minimized. + +#define RUNLEVEL(number, name) _Static_assert( \ + 0 <= number && number <= 31, \ + "The numeric value of runlevel " #name " (" #number ")" \ + " is out of range. Only runlevels in the range 0 <= level <= 31" \ + " are supported."); +#include "runlevel.def" +#undef RUNLEVEL + +typedef enum RunLevelBit { +#define RUNLEVEL(number, name) R_##name = (1 << number), +#include "runlevel.def" +#undef RUNLEVEL +} RunLevelBit; + +struct ServiceRunLevelSetting { + void (*set_enable_fn)(bool); + RunLevelBit enable_mask; +}; diff --git a/include/pbl/services/send_text_service.h b/include/pbl/services/send_text_service.h new file mode 100644 index 0000000000..77fa8e19d7 --- /dev/null +++ b/include/pbl/services/send_text_service.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Currently this file serves as a cache for the existence of the SEND_TEXT_NOTIF_PREF_KEY, and +//! a reply action within that key. +//! This is required because a user can have a supported mobile app but not a supported carrier, +//! and in that case we don't want to show the app in the launcher. +//! We cache the existence of this key so that the launcher isn't slowed down by flash reads + +void send_text_service_init(void); + +bool send_text_service_is_send_text_supported(void); diff --git a/include/pbl/services/services.h b/include/pbl/services/services.h new file mode 100644 index 0000000000..167ffaaa04 --- /dev/null +++ b/include/pbl/services/services.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Initialize services that our kernel depends on +void services_early_init(void); + +void services_init(void); diff --git a/include/pbl/services/services_common.h b/include/pbl/services/services_common.h new file mode 100644 index 0000000000..e5c354b59c --- /dev/null +++ b/include/pbl/services/services_common.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/runlevel.h" + +void services_common_init(void); + +void services_common_set_runlevel(RunLevel runlevel); diff --git a/include/pbl/services/services_normal.h b/include/pbl/services/services_normal.h new file mode 100644 index 0000000000..546b82675b --- /dev/null +++ b/include/pbl/services/services_normal.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/runlevel.h" + +void services_normal_early_init(void); + +void services_normal_init(void); + +void services_normal_set_runlevel(RunLevel runlevel); diff --git a/include/pbl/services/settings/settings_file.h b/include/pbl/services/settings/settings_file.h new file mode 100644 index 0000000000..69f3caaa59 --- /dev/null +++ b/include/pbl/services/settings/settings_file.h @@ -0,0 +1,203 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "settings_raw_iter.h" + +#include + +// Deleted records have their key stick around for at least DELETED_LIFETIME +// before they can be garbage collected from the file in which they are +// contained, that way they have time to propegate to all devices we end up +// syncronizing with. For more information, refer to the sync protocol proposal: +// https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=26837564 +// +// FIXME: See PBL-18945 +#define DELETED_LIFETIME (0 * SECONDS_PER_DAY) + +//! A SettingsFile is just a simple binary key-value store. Keys can be strings, +//! uint32_ts, or arbitrary bytes. Values are similarilly flexible. All +//! operations are atomic, so a reboot in the middle of changing the value for a +//! key will always either complete, returning the new value upon reboot, or +//! will just return the old value. +//! It also supports bidirection syncronization between the phone & watch, +//! using timestamps to resolve conflicts. +//! Note that although all operations are atomic, they are not thread-safe. If +//! you will be accessing a SettingsFile from multiple threads, make sure you +//! use locks! + +// NOTE: These fields are internal, modify them at your own risk! +typedef struct SettingsFile { + SettingsRawIter iter; + char *name; + + //! Maximum total space which can be used by this settings_file before a + //! compaction will be forced. (Must be >= max_used_space) + int max_space_total; + + //! Maximum space that can be used by valid records within this settings_file. + //! Once this has been exceeded, attempting to add more keys or values will + //! fail. + int max_used_space; + + //! The current allocation budget for physical file size. For growable files, + //! this starts at a small initial value and grows toward max_used_space on + //! demand. For non-growable files, this equals max_used_space. + int alloc_used_space; + + //! The floor for alloc_used_space. Compact will not shrink below this. For + //! growable files this is the initial_alloc_size requested at open time; + //! for non-growable files this equals max_used_space (no shrink). + int min_alloc_used_space; + + //! Amount of space in the settings_file that is currently dead, i.e. + //! has been written to with some data, but that data is no longer valid. + //! (overwritten records get added to this) + int dead_space; + + //! Amount of space in the settings_file that is currently used by valid + //! records. + int used_space; + + //! When this file as a whole was last_modified. + //! Defined as records.max(&:last_modified) + uint32_t last_modified; + + //! The position of the current record in the iteration (if any). Necessary + //! so that clients can read other records in the middle of iteration (i.e. + //! settings_file_each()/settings_file_rewrite()), without messing up the + //! state of the iteration. Set to 0 if not in use. + int cur_record_pos; +} SettingsFile; + + +//! max_used_space should be >= 5317 for persist files to make sure we can +//! always fit all of the records in the worst case (if the programmer stored +//! nothing but booleans). +//! Note: If the settings file already exists, the max_used_space parameter is +//! ignored. We could change this if the need arises. +status_t settings_file_open(SettingsFile *file, const char *name, + int max_used_space); +status_t settings_file_open_growable(SettingsFile *file, const char *name, + int max_used_space, int initial_alloc_size); +void settings_file_close(SettingsFile *file); + +bool settings_file_exists(SettingsFile *file, const void *key, size_t key_len); +status_t settings_file_delete(SettingsFile *file, + const void *key, size_t key_len); + +int settings_file_get_len(SettingsFile *file, const void *key, size_t key_len); +//! val_out_len must exactly match the length of the record on disk. +status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len, + void *val_out, size_t val_out_len); +status_t settings_file_set(SettingsFile *file, const void *key, size_t key_len, + const void *val, size_t val_len); + +//! Set a record with a specific timestamp instead of the current time. +//! This is useful when rewriting files and preserving original timestamps. +//! @param file the settings_file in which to set the value +//! @param key the key to set +//! @param key_len the length of the key +//! @param val the value to set +//! @param val_len the length of the value +//! @param timestamp the timestamp to use for the record +status_t settings_file_set_with_timestamp(SettingsFile *file, const void *key, size_t key_len, + const void *val, size_t val_len, uint32_t timestamp); + +//! Mark a record as synced. The flag will remain until the record is overwritten +//! @param file the settings_file that contains the record +//! @param key the key to the settings file. Note: keys can be up to 127 bytes +//! @param key_len the length of the key +status_t settings_file_mark_synced(SettingsFile *file, const void *key, size_t key_len); + +//! Mark all records as dirty (not synced) by rewriting the file. +//! This is used to trigger a full sync of all settings. +//! @param file the settings_file to mark dirty +//! @note This rewrites the entire file, which can be slow for large files. +status_t settings_file_mark_all_dirty(SettingsFile *file); + +//! Callback invoked when a setting is changed via settings_file_set +//! @param file the settings file that was modified +//! @param key the key that was set +//! @param key_len the length of the key +//! @param last_modified the timestamp of the change +typedef void (*SettingsFileChangeCallback)(SettingsFile *file, const void *key, int key_len, + time_t last_modified); + +//! Register a callback to be invoked when settings change +//! @param callback the callback to register (NULL to unregister) +void settings_file_set_change_callback(SettingsFileChangeCallback callback); + +//! set a byte in a setting. This can only be used a byte at a time to guarantee +//! atomicity. Do not use to modify several bytes in a row! +//! Note that only the reset bits will be applied (it writes flash directly) +status_t settings_file_set_byte( + SettingsFile *file, const void *key, size_t key_len, + size_t offset, uint8_t byte); + + + + ////////////////// + // Each/rewrite // +////////////////// +typedef void (*SettingsFileGetter)(SettingsFile *file, + void *buf, size_t buf_len); + +typedef struct { + uint32_t last_modified; + SettingsFileGetter get_key; + int key_len; + SettingsFileGetter get_val; + int val_len; + bool dirty; // has the dirty flag set +} SettingsRecordInfo; + +//! Callback used for using settings_file_each. +//! The bool returned is used to control the iteration. +//! - If a callback returns true, the iteration continues +//! - If a callback returns false, the ieration stops. +typedef bool (*SettingsFileEachCallback)(SettingsFile *file, + SettingsRecordInfo *info, + void *context); +//! Calls cb for each and every entry within the given file. +//! Note that you cannot modify the settings file while iterating. If you want +//! to do this, try settings_file_rewrite instead. (you can read other entries +//! without fault). +status_t settings_file_each(SettingsFile *file, SettingsFileEachCallback cb, + void *context); + + +typedef void (*SettingsFileRewriteCallback)(SettingsFile *old_file, + SettingsFile *new_file, + SettingsRecordInfo *info, + void *context); +//! Opens a new SettingsFile with the same name as the original SettingsFile, +//! in overwrite mode. This new file is passed into the given +//! SettingsFileRewriteCallback, which is called for each entry within the +//! original file. If you desire to preserve a key/value pair, you must write +//! it to the new file. +status_t settings_file_rewrite(SettingsFile *file, + SettingsFileRewriteCallback cb, + void *context); + + +//! Callback used for using settings_file_rewrite_filtered. +//! The bool returned is used to control whether or not the record is included in the file +//! after compaction. This callback is not allowed to use any other settings_file calls. +//! - If callback returns true, the record is included +//! - If callback returns false, the record is not included +typedef bool (*SettingsFileRewriteFilterCallback)(void *key, size_t key_len, void *value, + size_t value_len, void *context); + +//! Opens a new SettingsFile with the same name as the original SettingsFile, +//! in overwrite mode. Any records from the old file which pass through the filter_cb with +//! a true result are included into the new file. This call is much faster than using +//! settings_file_rewrite if all you are doing is excluding specific records from the old file. +status_t settings_file_rewrite_filtered(SettingsFile *file, + SettingsFileRewriteFilterCallback filter_cb, void *context); + +//! Compact the file: rewrite all live records and, for growable files, drop +//! alloc_used_space toward min_alloc_used_space. Useful for shrinking growable +//! settings files that grew under load and have since had records removed. +status_t settings_file_compact(SettingsFile *file); diff --git a/src/fw/services/normal/settings/settings_raw_iter.h b/include/pbl/services/settings/settings_raw_iter.h similarity index 89% rename from src/fw/services/normal/settings/settings_raw_iter.h rename to include/pbl/services/settings/settings_raw_iter.h index 72e76cd6c1..3381c84f54 100644 --- a/src/fw/services/normal/settings/settings_raw_iter.h +++ b/include/pbl/services/settings/settings_raw_iter.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -125,11 +112,20 @@ int settings_raw_iter_get_resumed_record_pos(SettingsRawIter *iter); void settings_raw_iter_read_key(SettingsRawIter *iter, uint8_t *key); void settings_raw_iter_read_val(SettingsRawIter *iter, uint8_t *val, int val_len); +//! Read key and value into a single contiguous buffer in one PFS call. +//! key_val_out must be at least key_len + val_len bytes. Key occupies the first +//! key_len bytes, val the next val_len bytes. +void settings_raw_iter_read_key_val(SettingsRawIter *iter, uint8_t *key_val_out); + //! Write (over top of) the header/key/val for the current record. void settings_raw_iter_write_header(SettingsRawIter *iter, SettingsRecordHeader *hdr); void settings_raw_iter_write_key(SettingsRawIter *iter, const uint8_t *key); void settings_raw_iter_write_val(SettingsRawIter *iter, const uint8_t *val); +//! Write key and value from a single contiguous buffer in one PFS call. +//! Layout matches settings_raw_iter_read_key_val. +void settings_raw_iter_write_key_val(SettingsRawIter *iter, const uint8_t *key_val); + //! Write a byte in place for the current record void settings_raw_iter_write_byte(SettingsRawIter *iter, int offset, uint8_t byte); diff --git a/src/fw/services/common/shared_prf_storage/shared_prf_storage.h b/include/pbl/services/shared_prf_storage/shared_prf_storage.h similarity index 88% rename from src/fw/services/common/shared_prf_storage/shared_prf_storage.h rename to include/pbl/services/shared_prf_storage/shared_prf_storage.h index c94c7b7e47..992c4382f6 100644 --- a/src/fw/services/common/shared_prf_storage/shared_prf_storage.h +++ b/include/pbl/services/shared_prf_storage/shared_prf_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/shared_prf_storage/shared_prf_storage_debug.h b/include/pbl/services/shared_prf_storage/shared_prf_storage_debug.h new file mode 100644 index 0000000000..17c6567f64 --- /dev/null +++ b/include/pbl/services/shared_prf_storage/shared_prf_storage_debug.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +void shared_prf_storage_dump_contents(void); diff --git a/include/pbl/services/shared_prf_storage/v2_sprf/shared_prf_storage_private.h b/include/pbl/services/shared_prf_storage/v2_sprf/shared_prf_storage_private.h new file mode 100644 index 0000000000..7a12ee9835 --- /dev/null +++ b/include/pbl/services/shared_prf_storage/v2_sprf/shared_prf_storage_private.h @@ -0,0 +1,71 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" + +#include +#include + +//! Used to version the struct if we have to add additional fields in the future. +//! 1: Added BLE and BT Classic pairing data +//! 2: Added getting started is complete bit +//! 3: Added remote Rand, remote EDIV, local DIV, local EDIV, is_..._valid flags, local device name +#define SHARED_PRF_STORAGE_VERSION 3 + +typedef struct PACKED { + // Remote device name + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + + // DIV / EDIV that was handed to the remote with our LTK (used when Pebble is Slave): + uint16_t local_ediv; + uint16_t local_div; + + // Remote encryption info (used when Pebble is Master): + SMLongTermKey ltk; + uint64_t rand; + uint16_t ediv; + + // Remote identity info (used when Pebble is Slave): + SMIdentityResolvingKey irk; + BTDeviceInternal identity; + + // Remote signature key: + SM128BitKey csrk; + + //! True if local_div and local_ediv are valid + bool is_local_encryption_info_valid:1; + + //! True if ltk, rand and ediv are valid + bool is_remote_encryption_info_valid:1; + + //! True if irk and identity are valid + bool is_remote_identity_info_valid:1; + + //! True if csrk is valid + //! @note Since iOS 9, CSRK is no longer exchanged. + bool is_remote_signing_info_valid:1; +} BLEPairingData; + +typedef struct PACKED { + BTDeviceAddress address; + SM128BitKey link_key; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + uint8_t platform_bits; +} BTClassicPairingData; + +typedef struct PACKED { + uint32_t version; + + // Customized local device name, or zero-length string if the default device name should be used + char local_device_name[BT_DEVICE_NAME_BUFFER_SIZE]; + + SM128BitKey root_keys[SMRootKeyTypeNum]; // ER and IR key + + // We rely on these two pieces of data being adjacent to each other + BLEPairingData ble_data; + BTClassicPairingData bt_classic_data; + + bool getting_started_is_complete; +} SharedPRFData; diff --git a/include/pbl/services/shared_prf_storage/v3_sprf/shared_prf_storage_private.h b/include/pbl/services/shared_prf_storage/v3_sprf/shared_prf_storage_private.h new file mode 100644 index 0000000000..adc098f228 --- /dev/null +++ b/include/pbl/services/shared_prf_storage/v3_sprf/shared_prf_storage_private.h @@ -0,0 +1,144 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" + +#include +#include + +#include + +#define SPRF_PAGE_IDX_INVALID ((uint16_t)~0) + +#define SPRF_MAX_NUM_PAGES_MULT(num) ((num) * 3 / 4) + +typedef enum { + SprfValidFields_LocalEncryptionInfoValid = (1 << 0), + SprfValidFields_RemoteEncryptionInfoValid = (1 << 1), + SprfValidFields_RemoteIdentityInfoValid = (1 << 2), + SprfValidFields_RemoteSigningInfoValid = (1 << 3), +} SprfValidFields; + +#ifndef __clang__ +_Static_assert(sizeof(SprfValidFields) == 1, "SprfValidFields unexpected size"); +#endif + + +typedef enum { + SprfMagic_ValidEntry = 0x46525053, + SprfMagic_UnpopulatedEntry = 0xFFFFFFFF, + SprfMagic_InvalidatedEntry = 0x0 +} SprfMagic; + +_Static_assert(sizeof(SprfMagic) == 4, "SprfMagic unexpected size"); + +//! This is the struct written out to the Shared PRF flash region +//! +//! It's composed of seven sub entries: +//! root_keys: Root keys (only identity since that is all Dialog needs) +//! ble_pairing_data: The pairing info for the device most recently paired to the watch +//! ble_pairing_name: The name of the device most recently paired to the watch +//! pinned_address: Pinned address of the device most recently paired to watch (may be empty) +//! getting_started: Captures whether or not we have gone through onboarding +//! local_name: Not used yet, but saved for future proofing +//! main_fw_scratch: A region for normal fw to stash info in the future if needed +//! +//! Each entry, or field, has its own crc which is written once the write of the field is complete. +//! @NOTE: The CRC _must_ be the first member of a field. There are static asserts to catch +//! this for current, please add a static assert for this if you create a new field +//! +//! A field is 'valid' iff a CRC of its contents matches the crc in flash +//! A field is 'unpopulated' if a memcmp of its contents is all 0xff. +//! A field is 'deleted' if its header has the value of SprfMagic_InvalidatedEntry +//! A field is 'corrupted' or 'partially written' if the content CRC does not match the field CRC +//! +//! On flash, there is a rolling list of entries. If a field above needs to be rewritten, +//! 'valid' entries must be copied from the current flash area to the next adjacent one. +//! +//! The shared PRF struct itself is defined as 256 bytes. Flash architectures have sectors +//! which are some 2^n multiple so this size pretty much guarantees that a divisible number +//! of structs can fit in the region allocated + +typedef struct PACKED SprfRootKeys { + uint32_t crc; + SM128BitKey keys[SMRootKeyTypeNum]; +} SprfRootKeys; +_Static_assert(offsetof(SprfRootKeys, crc) == 0, "crc must be the first field"); + +typedef struct PACKED SprfBlePairingData { + uint32_t crc; // CRC over the 'pairing_data' struct ('name' through 'fields') + + // local encryption data + SMLongTermKey l_ltk; // 16 byte key + uint64_t l_rand; + uint16_t l_ediv; + + // remote encryption data + uint16_t r_ediv; + SMLongTermKey r_ltk; + uint64_t r_rand; + + SMIdentityResolvingKey irk; // 16 byte key + SMConnectionSignatureResolvingKey csrk; // 16 byte key + BTDeviceInternal identity; + + SprfValidFields fields:8; + bool is_mitm_protection_enabled; + bool requires_address_pinning; + + //! Added in SPRF_CUR_VERSION 2. In SPRF_CUR_VERSION 1, this field is always 0x00. + uint8_t flags; +} SprfBlePairingData; +_Static_assert(offsetof(SprfBlePairingData, crc) == 0, "crc must be the first field"); + +typedef struct PACKED SprfBlePairingName { + uint32_t crc; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; +} SprfBlePairingName; +_Static_assert(offsetof(SprfBlePairingName, crc) == 0, "crc must be the first field"); + +typedef struct PACKED SprfPinnedAddress { + uint32_t crc; + BTDeviceAddress pinned_address; + uint8_t rsvd[2]; +} SprfPinnedAddress; +_Static_assert(offsetof(SprfPinnedAddress, crc) == 0, "crc must be the first field"); + +typedef struct PACKED SprfGettingStarted { + uint32_t crc; + bool is_complete; + uint8_t rsvd[3]; +} SprfGettingStarted; +_Static_assert(offsetof(SprfGettingStarted, crc) == 0, "crc must be the first field"); + +typedef struct PACKED SprfLocalName { + // Not used today, but in the future we could replace 'Pebble XXXX' with + // a user friendly name, 'Chris' Pebble' + uint32_t crc; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; +} SprfLocalName; +_Static_assert(offsetof(SprfLocalName, crc) == 0, "crc must be the first field"); + +typedef struct PACKED SharedPRFData { + SprfMagic magic; + uint8_t version; + uint8_t rsvd[3]; + + SprfRootKeys root_keys; + SprfBlePairingData ble_pairing_data; + SprfBlePairingName ble_pairing_name; + SprfPinnedAddress pinned_address; + SprfGettingStarted getting_started; + SprfLocalName local_name; + + // Occasions have arisen in the past where a region in sharedPRF that + // main FW can stash info related to a pairing. That is the intent of this region. + struct PACKED { + uint8_t rsvd[44]; + } main_fw_scratch; +} SharedPRFData; + +_Static_assert(BT_DEVICE_NAME_BUFFER_SIZE == 20, "Changing the length will break SharedPRF"); +_Static_assert(sizeof(SharedPRFData) == 256, "SharedPRFData does not match expected size"); diff --git a/include/pbl/services/speaker/note_sequence.h b/include/pbl/services/speaker/note_sequence.h new file mode 100644 index 0000000000..9616b786a6 --- /dev/null +++ b/include/pbl/services/speaker/note_sequence.h @@ -0,0 +1,81 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" + +#include +#include + +typedef enum { + SpeakerWaveformSine = 0, + SpeakerWaveformSquare, + SpeakerWaveformTriangle, + SpeakerWaveformSawtooth, + SpeakerWaveformCount +} SpeakerWaveform; + +//! A single note in a sequence. +//! midi_note: MIDI note number (0-127, 60=C4). 0 = rest (silence). +//! waveform: SpeakerWaveform value. +//! duration_ms: Note duration in ms (max 10000). +//! velocity: Volume 0-127 (0 = use global volume). +typedef struct PACKED { + uint8_t midi_note; + uint8_t waveform; + uint16_t duration_ms; + uint8_t velocity; + uint8_t reserved; +} SpeakerNote; + +typedef struct { + const SpeakerNote *notes; + uint32_t num_notes; + uint32_t current_note; + uint32_t samples_remaining; + uint32_t phase_acc; // 16.16 fixed-point phase accumulator + uint32_t phase_inc; // per-sample phase increment + uint8_t current_waveform; + uint8_t current_velocity; + bool active; +} NoteSequenceState; + +//! Initialize a note sequence player. +//! @param s State to initialize +//! @param notes Array of notes to play +//! @param count Number of notes +//! @param sample_rate Output sample rate in Hz (e.g. 16000) +void note_seq_init(NoteSequenceState *s, const SpeakerNote *notes, uint32_t count, + uint32_t sample_rate); + +//! Fill output buffer with synthesized PCM samples. +//! @param s Note sequence state +//! @param out Output buffer for 16-bit PCM samples +//! @param max_samples Maximum number of samples to generate +//! @return Number of samples actually written. 0 means sequence is done. +uint32_t note_seq_fill(NoteSequenceState *s, int16_t *out, uint32_t max_samples); + +//! Clean up note sequence state. +void note_seq_deinit(NoteSequenceState *s); + +//! @internal Shared synth primitives used by track_player for waveform voices. +//! These are not part of the public SDK. + +//! Compute the 16.16 fixed-point phase increment per output sample for a +//! MIDI note at the given sample rate. Returns 0 for rests (midi_note == 0) or +//! out-of-range values. +uint32_t note_phase_inc(uint8_t midi_note, uint32_t sample_rate); + +//! Synthesize one 16-bit signed PCM sample for a given waveform at the given +//! 16.16 phase accumulator value. phase_inc is the per-sample increment for +//! the same accumulator and is used by the square wave generator to apply +//! PolyBLEP anti-aliasing at the discontinuities. velocity scales the +//! amplitude (1..127); velocity 0 means no per-sample scaling (master volume +//! is applied downstream). +int16_t note_synth_sample(uint8_t waveform, uint32_t phase_acc, uint32_t phase_inc, + uint8_t velocity); + +//! Return the MIDI-note frequency in 16.8 fixed-point Hz (i.e. freq_hz * 256). +//! Used by track_player to compute sample-playback pitch ratios. +uint32_t note_midi_freq_x256(uint8_t midi_note); diff --git a/include/pbl/services/speaker/speaker_finish_reason.h b/include/pbl/services/speaker/speaker_finish_reason.h new file mode 100644 index 0000000000..910e2e590f --- /dev/null +++ b/include/pbl/services/speaker/speaker_finish_reason.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Reason reported when speaker playback ends. +typedef enum { + SpeakerFinishReasonDone = 0, //!< Playback completed naturally + SpeakerFinishReasonStopped, //!< Playback was stopped by the app + SpeakerFinishReasonPreempted, //!< Preempted by higher priority source + SpeakerFinishReasonError, //!< An error occurred +} SpeakerFinishReason; diff --git a/include/pbl/services/speaker/speaker_pcm_format.h b/include/pbl/services/speaker/speaker_pcm_format.h new file mode 100644 index 0000000000..5b4e986d65 --- /dev/null +++ b/include/pbl/services/speaker/speaker_pcm_format.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! PCM audio format for speaker streaming. +//! Bit layout: bit0 = sample rate (0=8kHz, 1=16kHz), bit1 = bit depth (0=8-bit, 1=16-bit). +//! All formats are mono signed PCM (8-bit samples are signed [-128,127], not unsigned). +typedef enum { + SpeakerPcmFormat_8kHz_8bit = 0, //!< 8kHz 8-bit signed (1 byte/sample) + SpeakerPcmFormat_16kHz_8bit = 1, //!< 16kHz 8-bit signed (1 byte/sample) + SpeakerPcmFormat_8kHz_16bit = 2, //!< 8kHz 16-bit signed little-endian (2 bytes/sample) + SpeakerPcmFormat_16kHz_16bit = 3, //!< 16kHz 16-bit signed little-endian (2 bytes/sample) + SpeakerPcmFormatCount +} SpeakerPcmFormat; diff --git a/include/pbl/services/speaker/speaker_service.h b/include/pbl/services/speaker/speaker_service.h new file mode 100644 index 0000000000..02ff103962 --- /dev/null +++ b/include/pbl/services/speaker/speaker_service.h @@ -0,0 +1,121 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/speaker/speaker_finish_reason.h" +#include "pbl/services/speaker/speaker_pcm_format.h" +#include "pbl/services/speaker/track.h" +#include "kernel/pebble_tasks.h" + +#include +#include + +typedef enum { + SpeakerPriorityApp = 0, + SpeakerPriorityNotification, + SpeakerPriorityCritical +} SpeakerPriority; + +typedef enum { + SpeakerStateIdle = 0, + SpeakerStatePlaying, + SpeakerStateDraining, // stream closing, playing remaining buffered data +} SpeakerState; + +typedef enum { + SpeakerSourceNone = 0, + SpeakerSourceNoteSeq, + SpeakerSourceStream, + SpeakerSourceTracks, + SpeakerSourceTone, +} SpeakerSourceType; + +//! Initialize the speaker service. Called once at boot. +void speaker_service_init(void); + +//! Play a note sequence on the speaker. +//! @param notes Array of notes (copied internally) +//! @param num_notes Number of notes +//! @param pri Priority level +//! @param vol Volume (0-100) +//! @return true if playback started, false if preempted by higher priority +bool speaker_service_play_note_seq(const SpeakerNote *notes, uint32_t num_notes, + SpeakerPriority pri, uint8_t vol); + +//! Play a single tone at an exact frequency on the speaker. +//! @param freq_hz Tone frequency in Hz (0 = silence/rest) +//! @param duration_ms Tone duration in milliseconds (max 10000) +//! @param waveform Waveform to use (SpeakerWaveform value) +//! @param velocity Per-note amplitude scale 0-127 (0 = use master volume) +//! @param pri Priority level +//! @param vol Volume (0-100) +//! @return true if playback started, false if preempted by higher priority +bool speaker_service_play_tone(uint16_t freq_hz, uint16_t duration_ms, + uint8_t waveform, uint8_t velocity, + SpeakerPriority pri, uint8_t vol); + +//! Play N monophonic tracks in parallel, mixed together. +//! Track arrays and any sample data are copied into kernel memory. +//! @param tracks Array of tracks. For each, its notes array and (optional) +//! sample + sample data are copied. +//! @param num_tracks Number of tracks (<= SPEAKER_MAX_TRACKS). +//! @param pri Priority level. +//! @param vol Volume (0-100). +//! @return true if playback started; false on invalid args, exceeded limits, +//! allocation failure, or priority blocked. +bool speaker_service_play_tracks(const SpeakerTrack *tracks, uint32_t num_tracks, + SpeakerPriority pri, uint8_t vol); + +//! Ask the service to post PEBBLE_SPEAKER_EVENT with the finish reason to the +//! given task whenever playback ends. +void speaker_service_register_finish(PebbleTask task); + +//! Max number of parallel tracks for polyphony. +#define SPEAKER_MAX_TRACKS 4 + +//! Max total sample-data bytes per speaker_service_play_tracks call. +#define SPEAKER_MAX_SAMPLE_BYTES_TOTAL (16 * 1024) + +//! Open a PCM stream for writing. +//! @param pri Priority level +//! @param vol Volume (0-100) +//! @param fmt PCM format (sample rate and bit depth) +//! @return true if stream opened, false if blocked by higher priority +bool speaker_service_stream_open(SpeakerPriority pri, uint8_t vol, SpeakerPcmFormat fmt); + +//! Write PCM data to the active stream. +//! @param data Source buffer +//! @param num_bytes Number of bytes to write +//! @return Number of bytes actually written (backpressure) +uint32_t speaker_service_stream_write(const void *data, uint32_t num_bytes); + +//! Close the active stream. Remaining buffered data will be drained. +void speaker_service_stream_close(void); + +//! Stop any active playback immediately. +void speaker_service_stop(void); + +//! Set playback volume. +//! @param vol Volume 0-100 +void speaker_service_set_volume(uint8_t vol); + +//! Get current speaker state. +SpeakerState speaker_service_get_state(void); + +//! Stop any playback initiated by the given task. Called on app exit. +void speaker_service_stop_for_task(PebbleTask task); + +//! Set the task that owns the current playback session. +void speaker_service_set_owner_task(PebbleTask task); + +//! @return true if the speaker is currently muted (always-on mute, or +//! the DND-scoped mute while DND is active). +bool speaker_service_is_muted(void); + +//! Notify the speaker service that the audio preferences (mute or system +//! volume cap) have changed. If a source is currently playing, the audio +//! output volume is re-applied immediately so the change takes effect on +//! live playback. +void speaker_service_handle_audio_prefs_changed(void); diff --git a/include/pbl/services/speaker/track.h b/include/pbl/services/speaker/track.h new file mode 100644 index 0000000000..95d0963bd8 --- /dev/null +++ b/include/pbl/services/speaker/track.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/speaker/speaker_pcm_format.h" + +#include +#include + +//! A raw PCM sample that can be pitch-shifted when played by a track. +//! data: mono signed PCM in the given format. +//! num_bytes: size of data in bytes. +//! format: sample rate + bit depth (see SpeakerPcmFormat). +//! base_midi_note: the MIDI note at which the sample plays unshifted (e.g. 60 = C4). +//! Notes above/below this value are produced by resampling. +//! loop: if true, the sample restarts from the beginning each time it runs out, +//! and keeps playing until the owning note's duration elapses. +typedef struct { + const void *data; + uint32_t num_bytes; + SpeakerPcmFormat format; + uint8_t base_midi_note; + bool loop; +} SpeakerSample; + +//! A single monophonic voice. Multiple tracks are mixed together by +//! speaker_play_tracks() to produce polyphony. +//! notes: array of notes to play sequentially. +//! num_notes: length of the notes array. +//! sample: if non-NULL, notes are played by pitch-shifting this sample; +//! note.waveform is ignored. If NULL, notes use their waveform field. +typedef struct { + const SpeakerNote *notes; + uint32_t num_notes; + const SpeakerSample *sample; +} SpeakerTrack; diff --git a/include/pbl/services/stationary.h b/include/pbl/services/stationary.h new file mode 100644 index 0000000000..382aaf1e52 --- /dev/null +++ b/include/pbl/services/stationary.h @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include "applib/accel_service.h" +#include "kernel/event_loop.h" + +//! Set up a timer that will check the position of the watch every minute to see +//! if any motion has occured +void stationary_init(void); + +//! Stationary mode should only be enabled when the user settings allow for it and when +//! the charger is not connected +bool stationary_get_enabled(void); + +//! Set whether the stationary module is enabled. When disabled, all operations will end, we ensure +//! that we are in a normal state, and the watch will not be able to enter stationary mode +void stationary_set_enabled(bool enabled); + +//! Set whether the stationary service is allowed to be enabled for the current runlevel +void stationary_run_level_enable(bool allow); + +//! If the stationary module is enabled and currently in stationary mode, then +//! we are put into a normal state. Call this if the system is about to do something that will +//! probably require user interaction, like an alarm going off. +void stationary_wake_up(void); + +//! Called by our event service system when there is a battery connection change +void stationary_handle_battery_connection_change_event(void); diff --git a/src/fw/services/common/system_task.h b/include/pbl/services/system_task.h similarity index 76% rename from src/fw/services/common/system_task.h rename to include/pbl/services/system_task.h index ca57895bc6..8ab1b13358 100644 --- a/src/fw/services/common/system_task.h +++ b/include/pbl/services/system_task.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file system_task.h //! diff --git a/include/pbl/services/tick_timer.h b/include/pbl/services/tick_timer.h new file mode 100644 index 0000000000..2292ffffb8 --- /dev/null +++ b/include/pbl/services/tick_timer.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "kernel/pebble_tasks.h" + +void tick_timer_add_subscriber(PebbleTask task); +void tick_timer_remove_subscriber(PebbleTask task); diff --git a/include/pbl/services/timeline/actions_endpoint.h b/include/pbl/services/timeline/actions_endpoint.h new file mode 100644 index 0000000000..e3d072cc5f --- /dev/null +++ b/include/pbl/services/timeline/actions_endpoint.h @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "attribute.h" +#include "item.h" + +#include "pbl/services/comm_session/session.h" +#include "util/uuid.h" + +#include +#include + +//! Sends a request to the phone asking it to invoke an action +//! @param id UUID of the pin/notification +//! @param type Type of the pin/notification +//! @param action_id The id of the action that is being invoked +//! @param attributes The list of attributes +//! @param do_async True = perform send on KernelBG, False = perform send on current task +void timeline_action_endpoint_invoke_action(const Uuid *id, TimelineItemActionType type, + uint8_t action_id, const AttributeList *attributes, + bool do_async); + +//! Handles messages from the phone sent to the timeline action endpoint +void timeline_action_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, + size_t length); diff --git a/include/pbl/services/timeline/alarm_layout.h b/include/pbl/services/timeline/alarm_layout.h new file mode 100644 index 0000000000..c91f559f7d --- /dev/null +++ b/include/pbl/services/timeline/alarm_layout.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "item.h" +#include "layout_layer.h" +#include "timeline_layout.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" + +typedef struct { + TimelineLayout timeline_layout; +} AlarmLayout; + +LayoutLayer *alarm_layout_create(const LayoutLayerConfig *config); + +bool alarm_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/attribute.h b/include/pbl/services/timeline/attribute.h similarity index 95% rename from src/fw/services/normal/timeline/attribute.h rename to include/pbl/services/timeline/attribute.h index ad9a12ae34..b451388fef 100644 --- a/src/fw/services/normal/timeline/attribute.h +++ b/include/pbl/services/timeline/attribute.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -112,7 +99,7 @@ typedef enum { AttributeIdMetricNames = 41, //! (StringList) Metric values for Generic pins to display numeric data AttributeIdMetricValues = 42, - //! (Uint32List) Metric icons, casted to TimelineResourceId (uin16_t) on use + //! (Uint32List) Metric icons, casted to TimelineResourceId (uint16_t) on use AttributeIdMetricIcons = 43, //! (uint8_t) Health activity that the item is from AttributeIdHealthActivityType = 44, @@ -124,6 +111,12 @@ typedef enum { AttributeIdSubtitleTemplateString = 47, //! Generic icon. AttributeIdIcon = 48, + //! (Uint32List) Custom vibration pattern for a notification, used with vibes_enqueue_custom_pattern + AttributeIdVibrationPattern = 49, + //! (uint32_t) Timestamp when the mute should expire. + AttributeIdMuteExpiration = 50, + //! (StringList) Notification filtering rules encoded as a byte array. + AttributeIdNotificationFilteringRules = 51, NumAttributeIds, } AttributeId; diff --git a/src/fw/services/normal/timeline/attribute_group.h b/include/pbl/services/timeline/attribute_group.h similarity index 81% rename from src/fw/services/normal/timeline/attribute_group.h rename to include/pbl/services/timeline/attribute_group.h index 08ad4cb4f6..f19070fba3 100644 --- a/src/fw/services/normal/timeline/attribute_group.h +++ b/include/pbl/services/timeline/attribute_group.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" #include #include diff --git a/include/pbl/services/timeline/attribute_private.h b/include/pbl/services/timeline/attribute_private.h new file mode 100644 index 0000000000..c268c83fcf --- /dev/null +++ b/include/pbl/services/timeline/attribute_private.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" + +#include + +typedef struct PACKED { + uint8_t id; + uint16_t length; +} SerializedAttributeHeader; diff --git a/src/fw/services/normal/timeline/attributes_actions.h b/include/pbl/services/timeline/attributes_actions.h similarity index 89% rename from src/fw/services/normal/timeline/attributes_actions.h rename to include/pbl/services/timeline/attributes_actions.h index f8aa1a0962..a69b81134c 100644 --- a/src/fw/services/normal/timeline/attributes_actions.h +++ b/include/pbl/services/timeline/attributes_actions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/timeline/calendar.h b/include/pbl/services/timeline/calendar.h new file mode 100644 index 0000000000..1ad3657bc4 --- /dev/null +++ b/include/pbl/services/timeline/calendar.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "event.h" + +//! This module puts events that report the current state of calendar events. +//! The states are: +//! - "no calendar events ongoing" +//! - "one or more calendar events ongoing" +//! Not every calendar event start / stop produces an event, but every transition is guarenteed +//! to put an event. + + +const TimelineEventImpl *calendar_get_event_service(void); + +//! Used to determine if there is currently an event going on, used for Smart DND +bool calendar_event_is_ongoing(void); + +#if UNITTEST +#include "pbl/services/new_timer/new_timer.h" +TimerID get_calendar_timer_id(void); +void set_calendar_timer_id(TimerID id); +#endif diff --git a/include/pbl/services/timeline/calendar_layout.h b/include/pbl/services/timeline/calendar_layout.h new file mode 100644 index 0000000000..6b83c184a1 --- /dev/null +++ b/include/pbl/services/timeline/calendar_layout.h @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "item.h" +#include "layout_layer.h" +#include "timeline_layout.h" + +#include "applib/graphics/gdraw_command_image.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/ui/ui.h" + +typedef enum { + CalendarRecurringTypeNone = 0, + CalendarRecurringTypeRecurring, +} CalendarRecurringType; + +typedef struct { + TimelineLayout timeline_layout; + TextLayer date_layer; + char day_date_buffer[TIME_STRING_DAY_DATE_LENGTH]; +} CalendarLayout; + +LayoutLayer *calendar_layout_create(const LayoutLayerConfig *config); + +bool calendar_layout_verify(bool existing_attributes[]); diff --git a/include/pbl/services/timeline/calendar_layout_resources.h b/include/pbl/services/timeline/calendar_layout_resources.h new file mode 100644 index 0000000000..0ddddc8541 --- /dev/null +++ b/include/pbl/services/timeline/calendar_layout_resources.h @@ -0,0 +1,33 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gdraw_command_image.h" +#include "applib/graphics/gdraw_command_private.h" +#include "util/attributes.h" +#include "util/size.h" + +#define START_ICON_POINTS { { 0, -2 }, { 9, 4 }, { 0, 10 } } + +typedef struct PACKED { + struct { + GDrawCommandImage image; + }; + GDrawCommand command; + GPoint points[STATIC_ARRAY_LENGTH(GPoint, START_ICON_POINTS)]; +} CalendarStartIcon; + +extern CalendarStartIcon g_calendar_start_icon; + +#define END_ICON_POINTS { { 0, 0 }, { 10, 0 }, { 10, 8 }, { 0, 8 } } + +typedef struct PACKED { + struct { + GDrawCommandImage image; + }; + GDrawCommand command; + GPoint points[STATIC_ARRAY_LENGTH(GPoint, END_ICON_POINTS)]; +} CalendarEndIcon; + +extern CalendarEndIcon g_calendar_end_icon; diff --git a/src/fw/services/normal/timeline/event.h b/include/pbl/services/timeline/event.h similarity index 89% rename from src/fw/services/normal/timeline/event.h rename to include/pbl/services/timeline/event.h index 493eb000a9..e9a1f3f74b 100644 --- a/src/fw/services/normal/timeline/event.h +++ b/include/pbl/services/timeline/event.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/pin_db.h" +#include "pbl/services/blob_db/pin_db.h" #define TIMELINE_EVENT_DELTA_INFINITE (INT32_MAX) diff --git a/include/pbl/services/timeline/generic_layout.h b/include/pbl/services/timeline/generic_layout.h new file mode 100644 index 0000000000..8cd26821ef --- /dev/null +++ b/include/pbl/services/timeline/generic_layout.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "item.h" +#include "layout_layer.h" +#include "timeline_layout.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/ui/bitmap_layer.h" + +typedef struct { + TimelineLayout timeline_layout; +} GenericLayout; + +LayoutLayer *generic_layout_create(const LayoutLayerConfig *config); + +bool generic_layout_verify(bool existing_attributes[]); diff --git a/include/pbl/services/timeline/health_layout.h b/include/pbl/services/timeline/health_layout.h new file mode 100644 index 0000000000..d40beca034 --- /dev/null +++ b/include/pbl/services/timeline/health_layout.h @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "item.h" +#include "layout_layer.h" +#include "timeline_layout.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "util/attributes.h" + +#define HEALTH_METRIC_BUFFER_LENGTH 128 + +//! The different types of health cards the app shows +typedef enum HealthCardType { + HealthCardType_Activity = 0, + HealthCardType_Sleep, + HealthCardTypeCount +} HealthCardType; + +//! Shared with insights to allow the app to launch into the appropriate card +typedef struct PACKED HealthLaunchArgs { + union { + struct { + HealthCardType card_type:8; //!< Tells us if we need to launch into an activity or sleep card + }; + uint32_t args; + }; +} HealthLaunchArgs; + +typedef enum { + ActivitySessionMetric_Duration = 0, + ActivitySessionMetric_Calories, + ActivitySessionMetric_Distance, + ActivitySessionMetric_Pace, + ActivitySessionMetricCount, +} ActivitySessionMetric; + +typedef struct { + KinoLayer *icon_layer; + const char *value_fmt; +} MetricData; + +typedef struct { + TimelineLayout timeline_layout; + MetricData metric_data[ActivitySessionMetricCount]; +} HealthLayout; + +LayoutLayer *health_layout_create(const LayoutLayerConfig *config); + +bool health_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/item.h b/include/pbl/services/timeline/item.h similarity index 95% rename from src/fw/services/normal/timeline/item.h rename to include/pbl/services/timeline/item.h index 08da6cd8b5..33f8eefcb7 100644 --- a/src/fw/services/normal/timeline/item.h +++ b/include/pbl/services/timeline/item.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/normal/timeline/layout_layer.h b/include/pbl/services/timeline/layout_layer.h similarity index 91% rename from src/fw/services/normal/timeline/layout_layer.h rename to include/pbl/services/timeline/layout_layer.h index ec3985274a..06b9c820fc 100644 --- a/src/fw/services/normal/timeline/layout_layer.h +++ b/include/pbl/services/timeline/layout_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/normal/timeline/layout_node.h b/include/pbl/services/timeline/layout_node.h similarity index 90% rename from src/fw/services/normal/timeline/layout_node.h rename to include/pbl/services/timeline/layout_node.h index 910a540e1f..520441e2df 100644 --- a/src/fw/services/normal/timeline/layout_node.h +++ b/include/pbl/services/timeline/layout_node.h @@ -1,28 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/text.h" #include "applib/ui/kino/kino_layer.h" -#include "apps/system_apps/timeline/text_node.h" +#include "apps/system/timeline/text_node.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/layout_layer.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/layout_layer.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "util/attributes.h" diff --git a/include/pbl/services/timeline/metricgroup.h b/include/pbl/services/timeline/metricgroup.h new file mode 100644 index 0000000000..365cd29b77 --- /dev/null +++ b/include/pbl/services/timeline/metricgroup.h @@ -0,0 +1,36 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "attribute.h" +#include "timeline_resources.h" + +typedef struct MetricGroup { + StringList *names; + StringList *values; + Uint32List *icons; + int num_items; + int max_num_items; + size_t max_item_string_size; +} MetricGroup; + +//! Create a metric group +//! @param max_num_items max number of items able to be added to the group +//! @param max_item_string_size max length of any string in the group, name and value +//! @return newly allocated MetricGroup +MetricGroup *metric_group_create(int max_num_items, size_t max_item_string_size); + +//! Destroy a metric group +//! @param metric_group MetricGroup to destroy +void metric_group_destroy(MetricGroup *metric_group); + +//! Adds an item to a metric group +//! @param metric_group MetricGroup to add an item to +//! @param name_i18n i18n key of the name string +//! @param value value field string +//! @param icon TimelineResourceId icon id +//! @param i18n_owner i18n owner to use +//! @return true if the item was added, false otherwise +bool metric_group_add_item(MetricGroup *metric_group, const char *name_i18n, const char *value, + TimelineResourceId icon, void *i18n_owner); diff --git a/include/pbl/services/timeline/notification_jumboji_table.h b/include/pbl/services/timeline/notification_jumboji_table.h new file mode 100644 index 0000000000..75407cc137 --- /dev/null +++ b/include/pbl/services/timeline/notification_jumboji_table.h @@ -0,0 +1,49 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/fonts/codepoint.h" +#include "resource/resource_ids.auto.h" + +#include + +typedef struct { + Codepoint codepoint; + ResourceId resource_id; +#if UNITTEST + const char *string; + const char *resource_name; +#endif +} EmojiEntry; + +#if UNITTEST +#define EMOJI_ENTRY(string, codepoint, resource_id) \ + { codepoint, resource_id, string, #resource_id } +#else +#define EMOJI_ENTRY(string, codepoint, resource_id) \ + { codepoint, resource_id } +#endif + +// Codepoint sorted table of supported Jumboji +#define JUMBOJI_TABLE(ENTRY) { \ + ENTRY("♥️", 0x02665, RESOURCE_ID_EMOJI_HEART_LARGE), \ + ENTRY("❤️", 0x02764, RESOURCE_ID_EMOJI_HEART_LARGE), \ + ENTRY("👍", 0x1f44d, RESOURCE_ID_EMOJI_THUMBS_UP_LARGE), \ + ENTRY("💙", 0x1f499, RESOURCE_ID_EMOJI_HEART_LARGE), \ + ENTRY("💚", 0x1f49a, RESOURCE_ID_EMOJI_HEART_LARGE), \ + ENTRY("💛", 0x1f49b, RESOURCE_ID_EMOJI_HEART_LARGE), \ + ENTRY("💜", 0x1f49c, RESOURCE_ID_EMOJI_HEART_LARGE), \ + ENTRY("😀", 0x1f600, RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE), \ + ENTRY("😁", 0x1f601, RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE), \ + ENTRY("😂", 0x1f602, RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE), \ + ENTRY("😃", 0x1f603, RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE), \ + ENTRY("😄", 0x1f604, RESOURCE_ID_EMOJI_BIG_SMILE_LARGE), \ + ENTRY("😉", 0x1f609, RESOURCE_ID_EMOJI_WINK_LARGE), \ + ENTRY("😊", 0x1f60a, RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE), \ + ENTRY("😍", 0x1f60d, RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE), \ + ENTRY("😘", 0x1f618, RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE), \ + ENTRY("😜", 0x1f61c, RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE), \ + ENTRY("😞", 0x1f61e, RESOURCE_ID_EMOJI_SAD_LARGE), \ + ENTRY("😟", 0x1f61f, RESOURCE_ID_EMOJI_SAD_LARGE), \ +} diff --git a/src/fw/services/normal/timeline/notification_layout.h b/include/pbl/services/timeline/notification_layout.h similarity index 88% rename from src/fw/services/normal/timeline/notification_layout.h rename to include/pbl/services/timeline/notification_layout.h index 5592861a6c..915d473b4b 100644 --- a/src/fw/services/normal/timeline/notification_layout.h +++ b/include/pbl/services/timeline/notification_layout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -25,8 +12,8 @@ #include "applib/graphics/perimeter.h" #include "applib/ui/bitmap_layer.h" #include "applib/ui/kino/kino_layer.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/timeline_resources.h" //! Banner height of notification and reminder layouts (excluding status bar) //! Rectangular banner is the same size on both the top and bottom @@ -94,9 +81,7 @@ typedef struct { typedef struct { TimelineItem *item; -#if !PLATFORM_TINTIN bool show_notification_timestamp; -#endif } NotificationLayoutInfo; typedef struct { @@ -105,9 +90,7 @@ typedef struct { AppResourceInfo icon_res_info; LayoutColors colors; NotificationLayoutInfo info; -#if !PLATFORM_TINTIN KinoLayer *detail_icon_layer; //!< Not common, so not inline with the layout -#endif const NotificationStyle *style; GTextNode *view_node; GSize view_size; diff --git a/include/pbl/services/timeline/peek.h b/include/pbl/services/timeline/peek.h new file mode 100644 index 0000000000..1c2957b651 --- /dev/null +++ b/include/pbl/services/timeline/peek.h @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "event.h" + +//! Default time at which the Timeline Peek will show an event before it starts. +//! This setting is user configurable. +#define TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S (10 * SECONDS_PER_MINUTE) + +//! Time at which the Timeline Peek will hide an event after it starts. +//! This settings is not user configurable. +#define TIMELINE_PEEK_HIDE_AFTER_TIME_S (10 * SECONDS_PER_MINUTE) + +//! TimelinePeek event subtypes which signify the relation between now and the event timestamp +typedef enum TimelinePeekTimeType { + TimelinePeekTimeType_None = 0, + //! The event is next, but not immediately, specifically > show_before_time_s + TimelinePeekTimeType_SomeTimeNext, + //! The event will start almost immediately, specifically <= show_before_time_s, and should be + //! presented to the user + TimelinePeekTimeType_ShowWillStart, + //! The event has started moments ago, specifically < TIMELINE_PEEK_HIDE_AFTER_TIME_S, + //! and should be presented to the user + TimelinePeekTimeType_ShowStarted, + //! The event is ongoing and will end and has already elapsed >= TIMELINE_PEEK_HIDE_AFTER_TIME_S + TimelinePeekTimeType_WillEnd, +} TimelinePeekTimeType; + +const TimelineEventImpl *timeline_peek_get_event_service(void); + +//! Sets the show before timing of timeline peek. +//! @param before_time_s The amount of time before event start the peek should be visible. +void timeline_peek_set_show_before_time(unsigned int before_time_s); diff --git a/include/pbl/services/timeline/reminders.h b/include/pbl/services/timeline/reminders.h new file mode 100644 index 0000000000..505251b4ad --- /dev/null +++ b/include/pbl/services/timeline/reminders.h @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "system/status_codes.h" +#include "pbl/services/timeline/item.h" +#include "pbl/services/new_timer/new_timer.h" + +typedef TimelineItem Reminder; +typedef TimelineItemId ReminderId; + +//! Set the reminder timer to the next stored reminder chronologically +//! @return S_SUCCESS or appropriate error +status_t reminders_update_timer(void); + +//! Insert a reminder to be popped up at a certain time +//! @param reminder pointer to the reminder to be inserted +//! @return S_SUCCESS or appropriate error +status_t reminders_insert(Reminder *reminder); + +//! Initialize the reminders so they can be activated on the watch +//! @return S_SUCCESS or appropriate error +status_t reminders_init(void); + +//! Delete a reminder +//! @param reminder_id pointer to an Id of the reminder to be deleted +//! @return S_SUCCESS or appropriate error +status_t reminders_delete(ReminderId *reminder_id); + +//! @return True if the reminder can snooze for a non-zero amount of time, false otherwise. +bool reminders_can_snooze(Reminder *reminder); + +//! Snooze a reminder +//! @param reminder Pointer to the reminder to snooze +//! @return S_SUCCESS, E_INVALID_OPERATION if cannot snooze, or some other error otherwise +status_t reminders_snooze(Reminder *reminder); + +//! Creates an event to alert the system that a reminder has been removed +//! @param reminder_id Pointer to the uuid of the removed reminder +void reminders_handle_reminder_removed(const Uuid *reminder_id); + +//! Creates an event to alert the system that a triggered reminder has changed +//! @param reminder_id Pointer to the uuid of the updated reminder +void reminders_handle_reminder_updated(const Uuid *reminder_id); diff --git a/include/pbl/services/timeline/sports_layout.h b/include/pbl/services/timeline/sports_layout.h new file mode 100644 index 0000000000..99eef56f6d --- /dev/null +++ b/include/pbl/services/timeline/sports_layout.h @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "item.h" +#include "layout_layer.h" +#include "timeline_layout.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/ui/bitmap_layer.h" + +typedef enum { + GameStatePreGame = 0, + GameStateInGame, + GameStatePostGame +} GameState; + +typedef struct { + TimelineLayout timeline_layout; + GameState state; +} SportsLayout; + +LayoutLayer *sports_layout_create(const LayoutLayerConfig *config); + +bool sports_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/swap_layer.h b/include/pbl/services/timeline/swap_layer.h similarity index 86% rename from src/fw/services/normal/timeline/swap_layer.h rename to include/pbl/services/timeline/swap_layer.h index 72f47d0f2e..4ed20e6af4 100644 --- a/src/fw/services/normal/timeline/swap_layer.h +++ b/include/pbl/services/timeline/swap_layer.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/ui/layer.h" #include "applib/ui/click.h" #include "applib/ui/property_animation.h" #include "applib/graphics/gpath.h" -#include "services/normal/timeline/layout_layer.h" +#include "pbl/services/timeline/layout_layer.h" struct Window; struct SwapLayer; diff --git a/include/pbl/services/timeline/timeline.h b/include/pbl/services/timeline/timeline.h new file mode 100644 index 0000000000..fc6187fa0d --- /dev/null +++ b/include/pbl/services/timeline/timeline.h @@ -0,0 +1,159 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include "pbl/services/timeline/item.h" +#include "system/status_codes.h" +#include "util/iterator.h" + +struct TimelineNode; +typedef struct TimelineNode TimelineNode; + +typedef enum { + TimelineIterDirectionPast, + TimelineIterDirectionFuture, +} TimelineIterDirection; + +typedef struct { + TimelineNode *node; + int index; + time_t start_time; + TimelineIterDirection direction; + TimelineItem pin; + bool show_all_day_events; + time_t midnight; // midnight at iter_init + time_t current_day; // midnight of the current pin +} TimelineIterState; + +//! initialize the timeline (builds the list of TimelineNodes) +status_t timeline_init(TimelineNode **timeline); + +//! Add a timeline pin we've created to the timeline. +//! Call \ref timeline_destroy_item after this in order to free up the memory used by the item. +//! @return true on success, false otherwise +bool timeline_add(TimelineItem *item); + +bool timeline_add_missed_call_pin(TimelineItem *pin, uint32_t uid); + +//! Remove a timeline pin we've added to the timeline +//! @return true on success, false otherwise +bool timeline_remove(const Uuid *id); + +//! Check whether a timeline pin exists +//! @return true if it does, false otherwise +bool timeline_exists(Uuid *id); + +//! Enables bulk action mode for ancs actions to avoid filling the event queue +void timeline_enable_ancs_bulk_action_mode(bool enable); + +//! Returns whether or not bulk actoin mode is enabled for ancs actions +bool timeline_is_bulk_ancs_action_mode_enabled(void); + +//! invokes a timelineitem's action. This can end up triggering a bluetooth message. +void timeline_invoke_action(const TimelineItem *item, const TimelineItemAction *action, + const AttributeList *attributes); + +TimelineIterDirection timeline_direction_for_item(TimelineItem *item, + TimelineNode *timeline, time_t now); + +bool timeline_nodes_equal(TimelineNode *a, TimelineNode *b); + +//! Get the UUID of the originator of a timeline item. For pins and notifications, this +//! returns the first parent_id of the item, which is the app's UUID (for pins) or source ID +//! (for notifications). For reminders, it will return the parent_id of the parent, which is the +//! app UUID of the pin that created the reminder. +//! @param [out] id pointer to storage for returned uuid. Set to UUID_INVALID when false +//! is returned +//! @return true if success, false on failure +bool timeline_get_originator_id(const TimelineItem *item, Uuid *id); + +//! Timeline item time comparator which sorts items as they would appear in Timeline with the +//! exception of all day events. +//! @param new_common The common that the resulting value is in reference to. +//! @param old_common The other common that is being compared against. +//! @param direction The Timeline direction. +//! @return < 0 if new_common should be before old_common, > 0 if new_common should be after +//! old_common, and 0 if equal in priority. +int timeline_item_time_comparator(CommonTimelineItemHeader *new_common, + CommonTimelineItemHeader *old_common, + TimelineIterDirection direction); + +//! Whether a Timeline item should show up in the Timeline direction with the exception of all +//! day events. +//! @param common The common header of the item to consider. +//! @param direction The Timeline direction. +//! @return true if the item would show up, false otherwise. +bool timeline_item_should_show(CommonTimelineItemHeader *header, TimelineIterDirection direction); + +/////////////////////////////////// +//! Timeline Iterator functions +/////////////////////////////////// + +status_t timeline_iter_init(Iterator *iter, TimelineIterState *iter_state, TimelineNode **timeline, + TimelineIterDirection direction, time_t timestamp); + +// Copy an iterator's contents into another one +void timeline_iter_copy_state(TimelineIterState *dst_state, TimelineIterState *src_state, + Iterator *dst_iter, Iterator *src_iter); + +void timeline_iter_deinit(Iterator *iter, TimelineIterState *iter_state, TimelineNode **head); + +//! refresh the pin at the current timeline iterator. Does a fairly naive refresh, i.e. does not +//! correctly place the pin in the timeline if the timestamp changes +void timeline_iter_refresh_pin(TimelineIterState *iter_state); + +//! Remove a timeline item from the iterator list +void timeline_iter_remove_node(TimelineNode **timeline, TimelineNode *node); + +//! Remove a timeline item from the iterator list +//! @return true if a node exists and was removed, false otherwise +bool timeline_iter_remove_node_with_id(TimelineNode **timeline, Uuid *key); + +/////////////////////////////////// +//! Timeline datasource functions +/////////////////////////////////// + +// ed429c16-f674-4220-95da-454f303f15e2 +#define UUID_NOTIFICATIONS_DATA_SOURCE {0xed, 0x42, 0x9c, 0x16, 0xf6, 0x74, 0x42, 0x20, 0x95, \ + 0xda, 0x45, 0x4f, 0x30, 0x3f, 0x15, 0xe2} + +// 6c6c6fc2-1912-4d25-8396-3547d1dfac5b +#define UUID_CALENDAR_DATA_SOURCE {0x6c, 0x6c, 0x6f, 0xc2, 0x19, 0x12, 0x4d, 0x25, 0x83, \ + 0x96, 0x35, 0x47, 0xd1, 0xdf, 0xac, 0x5b} + +// 61b22bc8-1e29-460d-a236-3fe409a439ff +#define UUID_WEATHER_DATA_SOURCE {0x61, 0xb2, 0x2b, 0xc8, 0x1e, 0x29, 0x46, 0xd, 0xa2, \ + 0x36, 0x3f, 0xe4, 0x9, 0xa4, 0x39, 0xff} + +// 42a07217-5491-4267-904a-d02a156752b6 +#define UUID_REMINDERS_DATA_SOURCE {0x42, 0xa0, 0x72, 0x17, 0x54, 0x91, 0x42, 0x67, \ + 0x90, 0x4a, 0xd0, 0x2a, 0x15, 0x67, 0x52, 0xb6} + +// UUID: 67a32d95-ef69-46d4-a0b9-854cc62f97f9 +#define UUID_ALARMS_DATA_SOURCE {0x67, 0xa3, 0x2d, 0x95, 0xef, 0x69, 0x46, 0xd4, \ + 0xa0, 0xb9, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9} + +// UUID: 36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c +#define UUID_HEALTH_DATA_SOURCE {0x36, 0xd8, 0xc6, 0xed, 0x4c, 0x83, 0x4f, 0xa1, \ + 0xa9, 0xe2, 0x8f, 0x12, 0xdc, 0x94, 0x1f, 0x8c} + +// UUID: fef82c82-7176-4e22-88de-35a3fc18d43f +#define UUID_WORKOUT_DATA_SOURCE {0xfe, 0xf8, 0x2c, 0x82, 0x71, 0x76, 0x4e, 0x22, \ + 0x88, 0xde, 0x35, 0xa3, 0xfc, 0x18, 0xd4, 0x3f} + +// UUID: 0863fc6a-66c5-4f62-ab8a-82ed00a98b5d +#define UUID_SEND_TEXT_DATA_SOURCE {0x08, 0x63, 0xfc, 0x6a, 0x66, 0xc5, 0x4f, 0x62, \ + 0xab, 0x8a, 0x82, 0xed, 0x00, 0xa9, 0x8b, 0x5d} + +// UUID: 0f71aaba-5814-4b5c-96e2-c9828c9734cb +// Special UUID that allows the watch to send SMS messages to a specific phone number +#define UUID_SEND_SMS {0x0f, 0x71, 0xaa, 0xba, 0x58, 0x14, 0x4b, 0x5c, \ + 0x96, 0xe2, 0xc9, 0x82, 0x8c, 0x97, 0x34, 0xcb} + +// UUID: 68010669-4b38-4751-ad04-067f1d8d2ab5 +#define UUID_INTERCOM_DATA_SOURCE {0x68, 0x01, 0x06, 0x69, 0x4b, 0x38, 0x47, 0x51, \ + 0xad, 0x04, 0x06, 0x7f, 0x1d, 0x8d, 0x2a, 0xb5} + +//! Get the name of a non-app, i.e. "private" datasource like Weather or Calendar +//! return NULL if parent_id is not a private data source, otherwise the name of the source +const char *timeline_get_private_data_source(Uuid *parent_id); diff --git a/src/fw/services/normal/timeline/timeline_actions.h b/include/pbl/services/timeline/timeline_actions.h similarity index 82% rename from src/fw/services/normal/timeline/timeline_actions.h rename to include/pbl/services/timeline/timeline_actions.h index cb9c36e514..5186118bc1 100644 --- a/src/fw/services/normal/timeline/timeline_actions.h +++ b/include/pbl/services/timeline/timeline_actions.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/ui/action_menu_window_private.h" -#include "services/normal/notifications/notification_types.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/notifications/notification_types.h" +#include "pbl/services/timeline/item.h" typedef enum TimelineItemActionSource { TimelineItemActionSourceModalNotification, diff --git a/src/fw/services/normal/timeline/timeline_layout.h b/include/pbl/services/timeline/timeline_layout.h similarity index 88% rename from src/fw/services/normal/timeline/timeline_layout.h rename to include/pbl/services/timeline/timeline_layout.h index af947ced9d..450ef8d3c0 100644 --- a/src/fw/services/normal/timeline/timeline_layout.h +++ b/include/pbl/services/timeline/timeline_layout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "layout_layer.h" @@ -26,10 +13,10 @@ #include "applib/graphics/text.h" #include "applib/ui/status_bar_layer.h" #include "applib/ui/kino/kino_layer.h" -#include "apps/system_apps/timeline/text_node.h" -#include "services/common/clock.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_resources.h" +#include "apps/system/timeline/text_node.h" +#include "pbl/services/clock.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_resources.h" #define TIMELINE_MAX_BOX_HEIGHT 2500 #define TIMELINE_TOP_MARGIN 10 diff --git a/include/pbl/services/timeline/timeline_layout_animations.h b/include/pbl/services/timeline/timeline_layout_animations.h new file mode 100644 index 0000000000..a073fc96b8 --- /dev/null +++ b/include/pbl/services/timeline/timeline_layout_animations.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "timeline_layout.h" + +void timeline_layout_transition_pin_to_card(TimelineLayout *pin_timeline_layout, + TimelineLayout *card_timeline_layout); + +void timeline_layout_transition_card_to_pin(TimelineLayout *card_timeline_layout, + TimelineLayout *pin_timeline_layout); + +Animation *timeline_layout_create_up_down_animation( + TimelineLayout *layout, const GRect *from, const GRect *to, const GRect *icon_from, + const GRect *icon_to, uint32_t duration, InterpolateInt64Function interpolate); diff --git a/src/fw/services/normal/timeline/timeline_resources.h b/include/pbl/services/timeline/timeline_resources.h similarity index 85% rename from src/fw/services/normal/timeline/timeline_resources.h rename to include/pbl/services/timeline/timeline_resources.h index 667df9b075..f53e2438d2 100644 --- a/src/fw/services/normal/timeline/timeline_resources.h +++ b/include/pbl/services/timeline/timeline_resources.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "applib/graphics/gdraw_command_image.h" #include "util/uuid.h" -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) #include "resource/timeline_resource_ids.auto.h" #else typedef uint32_t TimelineResourceId; diff --git a/include/pbl/services/timeline/weather_layout.h b/include/pbl/services/timeline/weather_layout.h new file mode 100644 index 0000000000..6bd0665393 --- /dev/null +++ b/include/pbl/services/timeline/weather_layout.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "item.h" +#include "layout_layer.h" +#include "timeline_layout.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "apps/system/timeline/text_node.h" + +typedef enum { + WeatherTimeType_None = 0, + WeatherTimeType_Pin, +} WeatherTimeType; + +typedef struct { + TimelineLayout timeline_layout; +} WeatherLayout; + +LayoutLayer *weather_layout_create(const LayoutLayerConfig *config); + +bool weather_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timezone_database.h b/include/pbl/services/timezone_database.h similarity index 84% rename from src/fw/services/normal/timezone_database.h rename to include/pbl/services/timezone_database.h index 4465b658aa..0ec22af342 100644 --- a/src/fw/services/normal/timezone_database.h +++ b/include/pbl/services/timezone_database.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/touch/gesture_event.h b/include/pbl/services/touch/gesture_event.h new file mode 100644 index 0000000000..069f326335 --- /dev/null +++ b/include/pbl/services/touch/gesture_event.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Gesture event type +typedef enum GestureEventType { + GestureEvent_Tap, + GestureEvent_DoubleTap, +} GestureEventType; + +//! Gesture event data, carried directly in PebbleGestureEvent +typedef struct GestureEvent { + GestureEventType type:8; + int16_t x; + int16_t y; +} GestureEvent; \ No newline at end of file diff --git a/include/pbl/services/touch/touch.h b/include/pbl/services/touch/touch.h new file mode 100644 index 0000000000..366e6f2797 --- /dev/null +++ b/include/pbl/services/touch/touch.h @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "touch_event.h" +#include "gesture_event.h" + +#include + +typedef enum TouchState { + TouchState_FingerUp, + TouchState_FingerDown, +} TouchState; + +typedef enum TouchGesture { + TouchGesture_Tap, + TouchGesture_DoubleTap, +} TouchGesture; + +void touch_init(void); + +//! Enable or disable the kernel's touch subscription used for the touch backlight feature. +//! When disabled, the touch sensor is only active if apps have subscribed to touch events. +void touch_set_backlight_enabled(bool enabled); + +//! @return true if at least one subscriber is currently registered for touch events. +bool touch_has_app_subscribers(void); + +//! Globally enable or disable touch. When disabled: +//! - The sensor is powered down, even if subscribers exist. +//! - touch_handle_update() drops incoming events at the source. +//! - touch_service_is_enabled() returns false to apps. +//! Subscribers remain subscribed and resume receiving events when re-enabled. +//! Intended to back a user-facing setting (e.g. "water mode") — the shell +//! pref system persists the value and calls this on boot. +void touch_service_set_globally_enabled(bool enabled); + +//! @return the current value of the global touch-enabled flag. +bool touch_service_is_globally_enabled(void); + +//! Pass a touch update to the service (called by the touch driver) +//! @param touch_state whether or not the screen is touched +//! @param x x position of touch +//! @param y y position of touch +void touch_handle_update(TouchState touch_state, int16_t x, int16_t y); + +//! Handle a gesture update (called by the touch driver) +//! @param gesture gesture that was detected +//! @param x x position of gesture (if applicable) +//! @param y y position of gesture +void touch_handle_gesture(TouchGesture gesture, int16_t x, int16_t y); + +//! Reset the touch service. +void touch_reset(void); + +//! Set whether the display is rotated 180° (left-hand mode). When rotated, +//! incoming touch coordinates are mirrored to match the rotated framebuffer +//! before being dispatched to subscribers. +void touch_set_rotated(bool rotated); diff --git a/include/pbl/services/touch/touch_client.h b/include/pbl/services/touch/touch_client.h new file mode 100644 index 0000000000..dd7006e739 --- /dev/null +++ b/include/pbl/services/touch/touch_client.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "touch_event.h" + +#include +#include + +//! Touch event callback +//! @param event Touch event +//! @param context callback context +typedef void (*TouchEventHandler)(const TouchEvent *event, void *context); + +//! Dispatch touch events to specified handler +//! @param touch_idx index of touch for which to dispatch events +//! @param event_handler callback to dispatch touch events to +//! @param context callback context +void touch_dispatch_touch_events(TouchIdx touch_idx, TouchEventHandler event_handler, + void *context); diff --git a/include/pbl/services/touch/touch_event.h b/include/pbl/services/touch/touch_event.h new file mode 100644 index 0000000000..1b3acc55ee --- /dev/null +++ b/include/pbl/services/touch/touch_event.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Touch event type +typedef enum TouchEventType { + TouchEvent_Touchdown, + TouchEvent_Liftoff, + TouchEvent_PositionUpdate, +} TouchEventType; + +//! Touch event data, carried directly in PebbleTouchEvent +typedef struct TouchEvent { + TouchEventType type:8; + int16_t x; + int16_t y; +} TouchEvent; diff --git a/include/pbl/services/vibe_pattern.h b/include/pbl/services/vibe_pattern.h new file mode 100644 index 0000000000..2958d952de --- /dev/null +++ b/include/pbl/services/vibe_pattern.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include + +void vibes_init(); + +int32_t vibes_get_vibe_strength(void); +int32_t vibes_get_default_vibe_strength(void); +void vibes_set_default_vibe_strength(int32_t vibe_strength_default); + +void vibe_service_set_enabled(bool enable); diff --git a/include/pbl/services/vibes/vibe_client.h b/include/pbl/services/vibes/vibe_client.h new file mode 100644 index 0000000000..0a46c1e61f --- /dev/null +++ b/include/pbl/services/vibes/vibe_client.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "vibe_score.h" + +typedef enum VibeClient { + VibeClient_Notifications = 0, + VibeClient_PhoneCalls, + VibeClient_Alarms, + VibeClient_AlarmsLPM, + VibeClient_Hourly, + VibeClient_OnDisconnect, +} VibeClient; + +// Returns the appropriate vibe score for the client. +// This is determined from alert preferences. +VibeScore *vibe_client_get_score(VibeClient client); diff --git a/include/pbl/services/vibes/vibe_intensity.h b/include/pbl/services/vibes/vibe_intensity.h new file mode 100644 index 0000000000..e6d34ca1c5 --- /dev/null +++ b/include/pbl/services/vibes/vibe_intensity.h @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef enum VibeIntensity { + VibeIntensityLow, + VibeIntensityMedium, + VibeIntensityHigh, + VibeIntensityNum, +} VibeIntensity; + +#define DEFAULT_VIBE_INTENSITY VibeIntensityHigh + +void vibe_intensity_init(void); + +//! Returns the corresponding strength for the given level of intensity. +//! The strength corresponds to a percentage of the max strength, ie in the range [0,100]. +uint8_t get_strength_for_intensity(VibeIntensity intensity); + +//! Sets the intensity of ALL vibrations (not just notifications) +void vibe_intensity_set(VibeIntensity intensity); + +//! Gets the current vibe intensity +VibeIntensity vibe_intensity_get(void); + +//! Returns a string representation of the provided vibe intensity. +//! @param intensity The intensity for which to get a string representation +//! @return A string representation of the provided intensity, or NULL if the intensity is invalid +const char *vibe_intensity_get_string_for_intensity(VibeIntensity intensity); + +//! Gets the next intensity in the vibe intensity cycle +//! @param intensity Input intensity for which to get the next intensity +//! @return The next vibe intensity in the cycle +VibeIntensity vibe_intensity_cycle_next(VibeIntensity intensity); diff --git a/src/fw/services/normal/vibes/vibe_score.h b/include/pbl/services/vibes/vibe_score.h similarity index 75% rename from src/fw/services/normal/vibes/vibe_score.h rename to include/pbl/services/vibes/vibe_score.h index 890601ebbc..bef0765047 100644 --- a/src/fw/services/normal/vibes/vibe_score.h +++ b/include/pbl/services/vibes/vibe_score.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/include/pbl/services/vibes/vibe_score_info.h b/include/pbl/services/vibes/vibe_score_info.h new file mode 100644 index 0000000000..575c5d1c6a --- /dev/null +++ b/include/pbl/services/vibes/vibe_score_info.h @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/vibes/vibe_client.h" + +#include +#include + +#define VIBE_DEF(identifier, enum_name, name_str, alert_types_arg, res_id)\ + VibeScoreId_##enum_name = identifier, +typedef enum VibeScoreId { + VibeScoreId_Invalid = 0, + #include "vibes.def" +} VibeScoreId; +#undef VIBE_DEF + +#ifdef CONFIG_BOARD_FAMILY_ASTERIX +#define DEFAULT_VIBE_SCORE_NOTIFS (VibeScoreId_StandardShortPulseHigh) +#define DEFAULT_VIBE_SCORE_INCOMING_CALLS (VibeScoreId_Pulse) +#define DEFAULT_VIBE_SCORE_ALARMS (VibeScoreId_Reveille) +#define DEFAULT_VIBE_SCORE_HOURLY (VibeScoreId_Disabled) +#define DEFAULT_VIBE_SCORE_ON_DISCONNECT (VibeScoreId_Disabled) +#else +#define DEFAULT_VIBE_SCORE_NOTIFS (VibeScoreId_NudgeNudge) +#define DEFAULT_VIBE_SCORE_INCOMING_CALLS (VibeScoreId_Pulse) +#define DEFAULT_VIBE_SCORE_ALARMS (VibeScoreId_Reveille) +#define DEFAULT_VIBE_SCORE_HOURLY (VibeScoreId_Disabled) +#define DEFAULT_VIBE_SCORE_ON_DISCONNECT (VibeScoreId_Disabled) +#endif + +// Returns the ResourceId for the VibeScore represented by this id. +// If the id does not exist, the ResourceId of the first vibe in S_VIBE_MAP is returned +uint32_t vibe_score_info_get_resource_id(VibeScoreId id); + +// Returns the name of the VibeScore represented by this id +// If the id does not exist, the name of the first vibe in S_VIBE_MAP is returned +const char *vibe_score_info_get_name(VibeScoreId id); + +// Returns the next vibe score playable by the client from the array defined by vibes.def +// Wraps around and continues searching if the end of the array is reached +// Returns current_id if there is no next vibe score +VibeScoreId vibe_score_info_cycle_next(VibeClient client, VibeScoreId curr_id); + +// Checks if the vibe score id exists and if the associated VibeScoreInfo contains a valid +// resource_id +bool vibe_score_info_is_valid(VibeScoreId id); diff --git a/include/pbl/services/vibes/vibes.def b/include/pbl/services/vibes/vibes.def new file mode 100644 index 0000000000..538037ca43 --- /dev/null +++ b/include/pbl/services/vibes/vibes.def @@ -0,0 +1,15 @@ +// IDs must be monotonically increasing, i.e. they must never be reused +// ID 0 is reserved for "Invalid". Do not use! + +VIBE_DEF(1, Disabled, i18n_ctx_noop("NotifVibe", "Disabled"), (AlertType_Notifications | AlertType_Calls), RESOURCE_ID_INVALID) +VIBE_DEF(2, StandardShortPulseLow, i18n_noop("Standard - Low"), AlertType_Notifications, RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW) +VIBE_DEF(3, StandardLongPulseLow, i18n_noop("Standard - Low"), (AlertType_Calls | AlertType_Alarms), RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW) +VIBE_DEF(4, StandardShortPulseHigh, i18n_noop("Standard - High"), AlertType_Notifications, RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH) +VIBE_DEF(5, StandardLongPulseHigh, i18n_noop("Standard - High"), (AlertType_Calls | AlertType_Alarms), RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH) +VIBE_DEF(8, Pulse, i18n_noop("Pulse"), AlertType_All, RESOURCE_ID_VIBE_SCORE_PULSE) +VIBE_DEF(9, NudgeNudge, i18n_noop("Nudge Nudge"), AlertType_All, RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE) +VIBE_DEF(10, Jackhammer, i18n_noop("Jackhammer"), AlertType_All, RESOURCE_ID_VIBE_SCORE_JACKHAMMER) +VIBE_DEF(11, Reveille, "Reveille", AlertType_Alarms, RESOURCE_ID_VIBE_SCORE_REVEILLE) +VIBE_DEF(12, Mario, "Mario", AlertType_All, RESOURCE_ID_VIBE_SCORE_MARIO) +VIBE_DEF(13, AlarmsLPM, "ALARMS LPM", AlertType_AlarmsLPM, RESOURCE_ID_VIBE_SCORE_ALARM_LPM) +VIBE_DEF(14, Gentle, i18n_noop("Gentle"), AlertType_Alarms, RESOURCE_ID_VIBE_SCORE_GENTLE) diff --git a/src/fw/services/normal/voice/transcription.h b/include/pbl/services/voice/transcription.h similarity index 85% rename from src/fw/services/normal/voice/transcription.h rename to include/pbl/services/voice/transcription.h index 2d56ef48cf..9d8f4718d7 100644 --- a/src/fw/services/normal/voice/transcription.h +++ b/include/pbl/services/voice/transcription.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/normal/voice/voice.h b/include/pbl/services/voice/voice.h similarity index 87% rename from src/fw/services/normal/voice/voice.h rename to include/pbl/services/voice/voice.h index 3d5bd31da6..917b9b3d86 100644 --- a/src/fw/services/normal/voice/voice.h +++ b/include/pbl/services/voice/voice.h @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include "kernel/pebble_tasks.h" -#include "services/normal/audio_endpoint.h" -#include "services/normal/voice_endpoint.h" -#include "services/normal/voice/transcription.h" +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/voice_endpoint.h" +#include "pbl/services/voice/transcription.h" #include "applib/graphics/utf8.h" diff --git a/include/pbl/services/voice/voice_speex.h b/include/pbl/services/voice/voice_speex.h new file mode 100644 index 0000000000..ab60beb444 --- /dev/null +++ b/include/pbl/services/voice/voice_speex.h @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2025 Joshua Jun */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include + +#include "pbl/services/voice_endpoint.h" + +/** + * @brief Initialize the Speex encoder + * @return true if successful, false otherwise + */ +bool voice_speex_init(void); + +/** + * @brief Deinitialize the Speex encoder + */ +void voice_speex_deinit(void); + +/** + * @brief Get transfer info for audio endpoint + * @param info Pointer to AudioTransferInfoSpeex structure to fill + */ +void voice_speex_get_transfer_info(AudioTransferInfoSpeex *info); + +/** + * @brief Get the frame size in samples + * @return frame size in samples, or 0 if not initialized + */ +int voice_speex_get_frame_size(void); + +/** + * @brief Get the frame buffer for audio input + * @return pointer to frame buffer, or NULL if not initialized + */ +int16_t *voice_speex_get_frame_buffer(void); + +/** + * @brief Get the frame buffer size in bytes + * @return frame buffer size in bytes, or 0 if not initialized + */ +size_t voice_speex_get_frame_buffer_size(void); + +/** + * @brief Encode a frame of audio samples + * @param samples Pointer to 16-bit audio samples (processed in-place) + * @param encoded_data Buffer to store encoded data + * @param max_encoded_size Maximum size of encoded data buffer + * @return number of encoded bytes, or -1 on error + */ +int voice_speex_encode_frame(int16_t *samples, uint8_t *encoded_data, size_t max_encoded_size); + +/** + * @brief Check if Speex encoder is initialized + * @return true if initialized, false otherwise + */ +bool voice_speex_is_initialized(void); diff --git a/include/pbl/services/voice_endpoint.h b/include/pbl/services/voice_endpoint.h new file mode 100644 index 0000000000..fdc3440900 --- /dev/null +++ b/include/pbl/services/voice_endpoint.h @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/voice/transcription.h" +#include "util/attributes.h" +#include "util/uuid.h" + +#include +#include + +typedef enum { + VoiceEndpointSessionTypeDictation = 0x01, + VoiceEndpointSessionTypeCommand = 0x02, // Not used yet + VoiceEndpointSessionTypeNLP = 0x03, + + VoiceEndpointSessionTypeCount, +} VoiceEndpointSessionType; + +typedef enum { + VoiceEndpointResultSuccess = 0x00, + VoiceEndpointResultFailServiceUnavailable = 0x01, + VoiceEndpointResultFailTimeout = 0x02, + VoiceEndpointResultFailRecognizerError = 0x03, + VoiceEndpointResultFailInvalidRecognizerResponse = 0x04, + VoiceEndpointResultFailDisabled = 0x05, + VoiceEndpointResultFailInvalidMessage = 0x06, +} VoiceEndpointResult; + +// Sent before Speex encoded data +typedef struct PACKED { + char version[20]; + uint32_t sample_rate; + uint16_t bit_rate; + uint8_t bitstream_version; + uint16_t frame_size; +} AudioTransferInfoSpeex; + +//! Called by the voice service to set up a dictation or command recognition session +void voice_endpoint_setup_session(VoiceEndpointSessionType session_type, + AudioEndpointSessionId session_id, AudioTransferInfoSpeex *info, Uuid *app_uuid); diff --git a/include/pbl/services/voice_endpoint_private.h b/include/pbl/services/voice_endpoint_private.h new file mode 100644 index 0000000000..f2011fe722 --- /dev/null +++ b/include/pbl/services/voice_endpoint_private.h @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/voice_endpoint.h" +#include "util/attributes.h" +#include "util/generic_attribute.h" + +// Shared message definitions with unit test + +typedef enum { + MsgIdSessionSetup = 0x01, + MsgIdDictationResult = 0x02, + MsgIdNLPResult = 0x03, +} MsgId; + +// Attribute ID definitions +typedef enum { + VEAttributeIdInvalid = 0x00, + VEAttributeIdAudioTransferInfoSpeex = 0x01, + VEAttributeIdTranscription = 0x02, + VEAttributeIdAppUuid = 0x03, + VEAttributeIdReminder = 0x04, + VEAttributeIdTimestamp = 0x05, +} VEAttributeId; + +// Sent and received by watch. Result is only sent by phone. + +typedef union PACKED { + struct { + uint32_t app_initiated:1; + }; + uint32_t all; +} VEFlags; + +typedef struct PACKED { + MsgId msg_id:8; + VEFlags flags; + VoiceEndpointSessionType session_type:8; + AudioEndpointSessionId session_id; + GenericAttributeList attr_list; +} SessionSetupMsg; + +typedef struct PACKED { + MsgId msg_id:8; + VEFlags flags; + VoiceEndpointSessionType session_type:8; + VoiceEndpointResult result:8; +} SessionSetupResultMsg; + +typedef struct PACKED { + MsgId msg_id:8; + VEFlags flags; + AudioEndpointSessionId session_id; + VoiceEndpointResult result:8; + GenericAttributeList attr_list; +} VoiceSessionResultMsg; diff --git a/include/pbl/services/wakeup.h b/include/pbl/services/wakeup.h new file mode 100644 index 0000000000..3317e377b5 --- /dev/null +++ b/include/pbl/services/wakeup.h @@ -0,0 +1,66 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "pbl/services/new_timer/new_timer.h" + +//! @internal +//! Event window is (in seconds) a reserved amount of time each wakeup_event receives +//! in which other wakeup events cannot be scheduled +#define WAKEUP_EVENT_WINDOW 60 +//! @internal +//! Number of wakeup events allowed per application (UUID) +#define MAX_WAKEUP_EVENTS_PER_APP 8 +//! @internal +//! Reduced event window or gap for catching up on missed events due to a time change +//! or the service being disabled by the system (Power saving mode). +#define WAKEUP_CATCHUP_WINDOW (WAKEUP_EVENT_WINDOW / 2) + +//! WakeupId is an identifier for a wakeup event +typedef int32_t WakeupId; + +//! WakeupInfo is used to pass the wakeup event id and reason +//! to the application that requested the wakeup event +typedef struct { + WakeupId wakeup_id; //!< Identifier (Timestamp) of the wakeup event + int32_t wakeup_reason; //!< App provided reason for the wakeup event +} WakeupInfo; + +//! @internal +//! This function initializes the wakeup service. +//! Triggers a popup notification for any apps that missed a +//! wakeup_event while the Pebble was off and specified +//! notify_if_missed while scheduling the event. +//! Deletes all expired wakeup_events from "wakeup" settings_file and +//! schedules the next wakeup_event using a new_timer +void wakeup_init(void); + +//! @internal +//! This function enables and disables the wakeup service. +void wakeup_enable(bool enabled); + +//! @internal +//! This function enables unit testing of the current wakeup event +TimerID wakeup_get_current(void); + +//! @internal +//! This function is used for testing and gets the next scheduled wakeup id +WakeupId wakeup_get_next_scheduled(void); + +//! @internal +//! This function is used for migrating wakeup events after a timezone set +void wakeup_migrate_timezone(int utc_diff); + +//! @internal +//! This function is called for significant time changes (>15s, timezone, DST). +//! It rewrites the wakeup file to delete past events and show missed event popups. +void wakeup_handle_significant_clock_change(void); + +//! @internal +//! This function is called for all time changes (including small RTC calibrations). +//! It reschedules the wakeup timer without deleting events or showing popups. +void wakeup_handle_clock_change(void); diff --git a/src/fw/services/normal/weather/weather_service.h b/include/pbl/services/weather/weather_service.h similarity index 80% rename from src/fw/services/normal/weather/weather_service.h rename to include/pbl/services/weather/weather_service.h index 3862140da3..743d639376 100644 --- a/src/fw/services/normal/weather/weather_service.h +++ b/include/pbl/services/weather/weather_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,8 +10,8 @@ //! requests for data are made from the watch. Clients that wish to subscribe to weather database //! changed events should use the PEBBLE_WEATHER_CHANGED_EVENT (see events.h) -#include "services/normal/blob_db/weather_db.h" -#include "services/normal/weather/weather_types.h" +#include "pbl/services/blob_db/weather_db.h" +#include "pbl/services/weather/weather_types.h" #include "util/list.h" #include "util/time/time.h" diff --git a/include/pbl/services/weather/weather_service_private.h b/include/pbl/services/weather/weather_service_private.h new file mode 100644 index 0000000000..1e5f252da2 --- /dev/null +++ b/include/pbl/services/weather/weather_service_private.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" +#include "util/uuid.h" + +#define PREF_KEY_WEATHER_APP "weatherApp" + +typedef struct PACKED SerializedWeatherAppPrefs { + uint8_t num_locations; + Uuid locations[]; +} SerializedWeatherAppPrefs; diff --git a/src/fw/services/normal/weather/weather_type_tuples.def b/include/pbl/services/weather/weather_type_tuples.def similarity index 100% rename from src/fw/services/normal/weather/weather_type_tuples.def rename to include/pbl/services/weather/weather_type_tuples.def diff --git a/include/pbl/services/weather/weather_types.h b/include/pbl/services/weather/weather_types.h new file mode 100644 index 0000000000..b20e72db21 --- /dev/null +++ b/include/pbl/services/weather/weather_types.h @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Weather Types +//! +//! This file contains all the types for Weather Locations and Weather Data. +//! The weather_timestamp_utcs of all hourly data is exactly on the hour. +//! The weather_timestamp_utcs of all daily data is at midnight of the day. + +#include "applib/graphics/gtypes.h" +#include "resource/timeline_resource_ids.auto.h" + +// Do NOT add entries here. See weather_type_tuples.def +// TODO (PBL-36438): use proper enum naming +typedef enum { +#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id)\ + WeatherType_##id = numeric_id, +#include "weather_type_tuples.def" +} WeatherType; + +// ------------------------------------------------------------------------------------ +const char *weather_type_get_name(WeatherType weather_type); + +GColor weather_type_get_bg_color(WeatherType weather_type); + +GColor weather_type_get_text_color(WeatherType weather_type); + +TimelineResourceId weather_type_get_timeline_resource_id(WeatherType weather_type); diff --git a/include/pebbleos/chip_id.h b/include/pebbleos/chip_id.h new file mode 100644 index 0000000000..07c06e9b39 --- /dev/null +++ b/include/pebbleos/chip_id.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +/* + * chip_id.h + * + * This file specifies IDs for the different processors on our multi-processor devices. + * The IDs are used to differenetiate the source of system logs, core dumps, etc. + * + * The IDs must be unique within a platform and must fit in 2 bits. + * If we build a device with more than 4 log/core dump producing processors, this will need to be + * addressed. + */ + +#define CORE_ID_MAIN_MCU 0 +#define CORE_ID_BLE 1 diff --git a/include/pebbleos/core_dump_structs.h b/include/pebbleos/core_dump_structs.h new file mode 100644 index 0000000000..1d56ec175c --- /dev/null +++ b/include/pebbleos/core_dump_structs.h @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +/* + * core_dump_structs.h + * + * This file specifies core_dump structures previously defined in fw/kernel/core_dump_private.h + * This is so the Dialog BLE core_dump code can use the same structures. + * + */ + +#include "portmacro.h" +#include "util/attributes.h" + +// Structure of thread info stored within a CORE_DUMP_CHUNK_KEY_THREAD chunk in the core dump +#define CORE_DUMP_THREAD_NAME_SIZE 16 +typedef struct PACKED { + int8_t name[CORE_DUMP_THREAD_NAME_SIZE]; // Name, includes null termination + uint32_t id; // thread id + uint8_t running; // true if this thread is running + uint32_t registers[portCANONICAL_REG_COUNT]; // registers [r0-r12, sp, lr, pc, xpsr] +} CoreDumpThreadInfo; + +// Structure of extra registers stored within a CORE_DUMP_CHUNK_KEY_EXTRA_REG chunk in the +// core dump +typedef struct PACKED { + uint32_t msp; + uint32_t psp; + uint8_t primask; + uint8_t basepri; + uint8_t faultmask; + uint8_t control; +} CoreDumpExtraRegInfo; + + + +// We save all the important registers on entry to core_dump_reset() in a structure of this type +// on the core_dump_reset() stack and save a pointer to it in the s_saved_registers global. +// IMPORTANT!: There is assembly code near the top of core_dump_reset() that makes assumptions +// about the order and packing of this structure. +typedef struct PACKED { + uint32_t core_reg[portCANONICAL_REG_COUNT]; + CoreDumpExtraRegInfo extra_reg; +} CoreDumpSavedRegisters; diff --git a/include/pebbleos/cron.h b/include/pebbleos/cron.h new file mode 100644 index 0000000000..6517e15572 --- /dev/null +++ b/include/pebbleos/cron.h @@ -0,0 +1,128 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "util/list.h" + +//! @file cron.h +//! Wall-clock based timer system. Designed for use in things such as alarms, calendar events, etc. +//! Properly handles DST, etc. + +typedef struct CronJob CronJob; + +typedef void (*CronJobCallback)(CronJob *job, void* data); + +//! Matches any possible value. +#define CRON_MINUTE_ANY (-1) +#define CRON_HOUR_ANY (-1) +#define CRON_MDAY_ANY (-1) +#define CRON_MONTH_ANY (-1) + +#define WDAY_SUNDAY (1 << 0) +#define WDAY_MONDAY (1 << 1) +#define WDAY_TUESDAY (1 << 2) +#define WDAY_WEDNESDAY (1 << 3) +#define WDAY_THURSDAY (1 << 4) +#define WDAY_FRIDAY (1 << 5) +#define WDAY_SATURDAY (1 << 6) + +#define WDAY_WEEKDAYS (WDAY_MONDAY | WDAY_TUESDAY | WDAY_WEDNESDAY | WDAY_THURSDAY | WDAY_FRIDAY) +#define WDAY_WEEKENDS (WDAY_SUNDAY | WDAY_SATURDAY) +#define WDAY_ANY (WDAY_WEEKENDS | WDAY_WEEKDAYS) + +struct CronJob { + //! internal, no touchy + ListNode list_node; + + //! Cached execution timestamp in UTC. + //! This is set by `cron_job_schedule`, and is required to never be changed once the job has been + //! added. + time_t cached_execute_time; + + //! Callback that is called when the job fires. + CronJobCallback cb; + void* cb_data; + + //! Occasionally, the system gets a clock change event for various reasons: + //! - User changed time-zones or a DST transition happened + //! - User changed the time + //! - Phone sent the current time and was different from ours, so we took theirs. + //! In the first case, the cron job's execute time will always be recalculated. + //! In the other two, we see if the time difference from the old time is >= this. + //! If it is, then we'll recalculate. Otherwise, we leave the calculated time alone. + //! In this way, 0 will always recalculate, and UINT32_MAX will never recalculate. + //! + //! Recalculating would essentially mean that a job that was "skipped over" will not fire until + //! the next match. If recalculation is not done, but the job was skipped over, it will fire + //! instantly. + //! + //! This value is specified in seconds. + uint32_t clock_change_tolerance; + + int8_t minute; //!< 0-59, or CRON_MINUTE_ANY + int8_t hour; //!< 0-23, or CRON_HOUR_ANY + int8_t mday; //!< 0-30, or CRON_MDAY_ANY + int8_t month; //!< 0-11, or CRON_MONTH_ANY + + //! Seconds to offset the cron execution time applied after regular cron job time calculation. + //! For example, a cron scheduled for Monday at 0:15 with an offset of negative 30min will fire + //! on Sunday at 23:45. + int32_t offset_seconds; + + union { + uint8_t flags; + + struct { + //! This should be any combination of WDAY_*. If zero, acts like WDAY_ANY. + uint8_t wday : 7; + + //! If this flag is set, the resulting execution time may be equal to the local epoch. + //! Having it set could be used for some event that must happen at the specified time even if + //! that time is right now. + bool may_be_instant : 1; + }; + }; +}; + +//! Add a cron job. This will make the service hold a reference to the specified job, so it must +//! not leave scope or be destroyed until it is unscheduled. +//! The job only gets scheduled once. For re-scheduling, you can call this on the job again. +//! @params job pointer to the CronJob struct to be scheduled. +//! @returns time_t for when the job is destined to go off. +time_t cron_job_schedule(CronJob *job); + +//! Schedule a cron job to run after another cron job. +//! This will make the service hold a reference to the new job, so it must +//! not leave scope or be destroyed until it is unscheduled. +//! @param job pointer to the CronJob after which we want our job to run. job must be scheduled. +//! @params new_job pointer to the CronJob struct to be scheduled. new_job must be unscheduled. +//! @returns time_t for when the job is destined to go off. +//! @note This API makes no guarantee that the two jobs will be scheduled back to back, +//! only that new_job will have the same scheduled time as job and that it will trigger +//! strictly after job. +time_t cron_job_schedule_after(CronJob *new_job, CronJob *job); + +//! Remove a scheduled cron job. +//! @params job pointer to the CronJob struct to be unscheduled. +//! @return true if the job was successfully removed (false may indicate no job was +//! scheduled at all or the cb is currently executing) +bool cron_job_unschedule(CronJob *job); + +//! Check if a cron job is scheduled. +//! @params job pointer to the CronJob struct to be checked for being scheduled. +//! @returns true if scheduled or pending deletion, false otherwise +bool cron_job_is_scheduled(CronJob *job); + +//! Calculate cron job's destined execution time, from the current time. +//! @params job pointer to the CronJob struct to get the execution time for. +//! @returns time_t for when the job is destined to go off. +time_t cron_job_get_execute_time(const CronJob *job); + +//! Calculate cron job's destined execution time if it were scheduled at the given time. +//! @params job pointer to the CronJob struct to get the execution time for. +//! @params local_epoch the epoch for getting the job's execution time. +//! @returns time_t for when the job is destined to go off. +time_t cron_job_get_execute_time_from_epoch(const CronJob *job, time_t local_epoch); diff --git a/include/pebbleos/firmware_metadata.h b/include/pebbleos/firmware_metadata.h new file mode 100644 index 0000000000..d380fa80e7 --- /dev/null +++ b/include/pebbleos/firmware_metadata.h @@ -0,0 +1,119 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +/* + * firmware_metadata.h + * + * This file specifies the Firmware Metadata structure used in the .elf & .bin files to + * identify the build info, etc. + */ + +#include "util/attributes.h" + +#include +#include + + +#define FW_METADATA_CURRENT_STRUCT_VERSION 0x1 +#define FW_METADATA_VERSION_SHORT_BYTES 8 +#define FW_METADATA_VERSION_TAG_BYTES 32 + +// NOTE: When adding new platforms, if they use the legacy defective CRC, the list in +// tools/fw_binary_info.py needs to be updated with the platform value. +typedef enum FirmwareMetadataPlatform { + FirmwareMetadataPlatformUnknown = 0, + FirmwareMetadataPlatformPebbleOneEV1 = 1, + FirmwareMetadataPlatformPebbleOneEV2 = 2, + FirmwareMetadataPlatformPebbleOneEV2_3 = 3, + FirmwareMetadataPlatformPebbleOneEV2_4 = 4, + FirmwareMetadataPlatformPebbleOnePointFive = 5, + FirmwareMetadataPlatformPebbleTwoPointZero = 6, + FirmwareMetadataPlatformPebbleSnowyEVT2 = 7, + FirmwareMetadataPlatformPebbleSnowyDVT = 8, + FirmwareMetadataPlatformPebbleSpaldingEVT = 9, + FirmwareMetadataPlatformPebbleBobbyDVT = 10, + FirmwareMetadataPlatformPebbleSpalding = 11, + FirmwareMetadataPlatformPebbleSilkEVT = 12, + FirmwareMetadataPlatformPebbleRobertEVT = 13, + FirmwareMetadataPlatformPebbleSilk = 14, + FirmwareMetadataPlatformPebbleAsterix = 15, + FirmwareMetadataPlatformPebbleObelixEVT = 16, + FirmwareMetadataPlatformPebbleObelixDVT = 17, + FirmwareMetadataPlatformPebbleObelixPVT = 18, + FirmwareMetadataPlatformPebbleGetafixEVT = 19, + FirmwareMetadataPlatformPebbleGetafixDVT = 20, + FirmwareMetadataPlatformPebbleGetafixDVT2 = 21, + + FirmwareMetadataPlatformPebbleOneBigboard = 0xff, + FirmwareMetadataPlatformPebbleOneBigboard2 = 0xfe, + FirmwareMetadataPlatformPebbleSnowyBigboard = 0xfd, + FirmwareMetadataPlatformPebbleSnowyBigboard2 = 0xfc, + FirmwareMetadataPlatformPebbleSpaldingBigboard = 0xfb, + FirmwareMetadataPlatformPebbleSilkBigboard = 0xfa, + FirmwareMetadataPlatformPebbleRobertBigboard = 0xf9, + FirmwareMetadataPlatformPebbleSilkBigboard2 = 0xf8, + FirmwareMetadataPlatformPebbleRobertBigboard2 = 0xf7, + FirmwareMetadataPlatformPebbleFlintEmu = 0xf6, + FirmwareMetadataPlatformPebbleEmeryEmu = 0xf5, + FirmwareMetadataPlatformPebbleObelixBigboard = 0xf4, + FirmwareMetadataPlatformPebbleObelixBigboard2 = 0xf3, + FirmwareMetadataPlatformPebbleGabbroEmu = 0xf2, +} FirmwareMetadataPlatform; + +// WARNING: changes in this struct must be reflected in: +// - iOS/PebblePrivateKit/PebblePrivateKit/PBBundle.m + +struct PACKED FirmwareMetadata { + uint32_t version_timestamp; + char version_tag[FW_METADATA_VERSION_TAG_BYTES]; + char version_short[FW_METADATA_VERSION_SHORT_BYTES]; + bool is_recovery_firmware:1; + bool is_ble_firmware:1; + bool is_dual_slot:1; + bool is_slot_0:1; + uint8_t reserved:4; + uint8_t hw_platform; + //! This should be the last field, since we put the meta data struct at the end of the fw binary. + uint8_t metadata_version; +}; +typedef struct FirmwareMetadata FirmwareMetadata; + +_Static_assert(sizeof(struct FirmwareMetadata) == (sizeof(uint32_t) + + FW_METADATA_VERSION_SHORT_BYTES + FW_METADATA_VERSION_TAG_BYTES + sizeof(uint8_t) + + sizeof(uint8_t) + sizeof(uint8_t)), + "FirmwareMetadata bitfields not packed correctly"); + + +// Shared defines. Let's not duplicate this everywhere. + +#ifdef CONFIG_RECOVERY_FW + #define FIRMWARE_METADATA_IS_RECOVERY_FIRMWARE (true) +#else + #define FIRMWARE_METADATA_IS_RECOVERY_FIRMWARE (false) +#endif + +#ifdef CONFIG_BOARD_ASTERIX + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleAsterix) +#elif defined(CONFIG_BOARD_OBELIX_DVT) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleObelixDVT) +#elif defined(CONFIG_BOARD_OBELIX_PVT) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleObelixPVT) +#elif defined(CONFIG_BOARD_OBELIX_BB2) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleObelixBigboard2) +#elif defined(CONFIG_BOARD_GETAFIX_EVT) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleGetafixEVT) +#elif defined(CONFIG_BOARD_GETAFIX_DVT) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleGetafixDVT) +#elif defined(CONFIG_BOARD_GETAFIX_DVT2) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleGetafixDVT2) +#elif defined(CONFIG_BOARD_QEMU_EMERY) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleEmeryEmu) +#elif defined(CONFIG_BOARD_QEMU_FLINT) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleFlintEmu) +#elif defined(CONFIG_BOARD_QEMU_GABBRO) + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleGabbroEmu) +#else + #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformUnknown) +#endif diff --git a/pbl b/pbl new file mode 100755 index 0000000000..4f0b4dd7da --- /dev/null +++ b/pbl @@ -0,0 +1,790 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +"""PebbleOS developer CLI. + +Hosts the operational commands (flash, qemu, console, debug, screenshot, +openocd, reset, bork, image_*, make_lang, pack_lang, pack_all_langs) that used +to live in the root ``wscript``but are not build steps, and provides thin +pass-through wrappers for the everyday ``./waf`` commands so there is a single entry point. + +Build state is read directly from the configured build dir +(``build/c4che/_cache.py``); no waf process is spawned for operational +commands. Run ``./waf configure --board BOARD`` first. +""" + +import argparse +import importlib.util +import os +import platform +import shlex +import string +import subprocess +import sys + +REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) +BUILD_DIR = os.path.join(REPO_ROOT, "build") +LANG_DIR_REL = "resources/normal/base/lang" + +# Commands forwarded verbatim to ./waf (no build env / waflib needed). +WAF_PASSTHROUGH = ("configure", "build", "bundle", "clean", "test", "waf") + +# QEMU SDL decorations per board. The first entry is used as the default. +QEMU_DECORATIONS = { + "qemu_emery": ["pt2-br", "pt2-sb"], + "qemu_flint": ["p2d-bk", "p2d-wh"], + "qemu_gabbro": ["pr2-bk20", "pr2-gd14"], +} +QEMU_DECORATION_CHOICES = sorted({d for ds in QEMU_DECORATIONS.values() for d in ds}) + ["none"] + +# Set by setup() from --dry-run. +_DRY_RUN = False + + +# --- Colored output ------------------------------------------------------- + +def _pprint(color, msg): + codes = {"RED": "31", "GREEN": "32", "YELLOW": "33", "CYAN": "36"} + sys.stdout.write("\033[%sm%s\033[0m\n" % (codes.get(color, "0"), msg)) + + +def _fatal(msg): + _pprint("RED", msg) + sys.exit(1) + + +def _run_shell(cmd): + """Run a shell command string, honoring --dry-run.""" + if _DRY_RUN: + _pprint("YELLOW", "[dry-run] " + cmd) + return 0 + return subprocess.call(cmd, shell=True) + + +def _run_waf(*args): + """Run a ./waf subcommand for build steps that pbl orchestrates but does + not perform itself (e.g. the QEMU flash images).""" + if _DRY_RUN: + _pprint("YELLOW", "[dry-run] ./waf " + " ".join(args)) + return 0 + return subprocess.call([sys.executable, "waf"] + list(args), cwd=REPO_ROOT) + + +# --- Configured build env ------------------------------------------------- + +class Env: + """Mimics ``ctx.env``: attribute/item access returns ``[]`` when missing, + matching waf's ConfigSet semantics that the runner helpers rely on.""" + + def __init__(self, data): + self._data = data + + def __getattr__(self, key): + return self._data.get(key, []) + + def __getitem__(self, key): + return self._data.get(key, []) + + def __contains__(self, key): + return key in self._data + + +def load_env(): + cache = os.path.join(BUILD_DIR, "c4che", "_cache.py") + if not os.path.isfile(cache): + _fatal("Build not configured -- run ./waf configure --board BOARD first.") + spec = importlib.util.spec_from_file_location("_waf_cache", cache) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + data = {k: v for k, v in vars(mod).items() if not k.startswith("__")} + return Env(data) + + +# --- runner reuse --------------------------------------------------------- + +# Populated by setup() with the self-contained tools.runners package. +runners = None + + +def setup(options): + """Bootstrap for operational commands: resolve build env, import the + runner package, and activate an installed SDK. Returns the build env.""" + global runners, _DRY_RUN + + _DRY_RUN = getattr(options, "dry_run", False) + + # The tools/ helpers live under the repo root. + if REPO_ROOT not in sys.path: + sys.path.insert(0, REPO_ROOT) + + import tools.runners as _runners + + runners = _runners + + # Prefer an installed PebbleOS SDK's binaries (toolchain, QEMU, sftool). + try: + from waftools.pebble_sdk_locator import activate_sdk + activate_sdk(REPO_ROOT) + except Exception: + pass + + return load_env() + + +# --- Artifact paths (relative to repo root; cwd is REPO_ROOT) -------------- + +def fw_bin(): + return "build/src/fw/tintin_fw.bin" + + +def fw_hex(): + return "build/src/fw/tintin_fw.hex" + + +def fw_elf(): + return "build/src/fw/tintin_fw.elf" + + +def pbpack(): + return "build/system_resources.pbpack" + + +def make_runner(env, options): + selected = getattr(options, "runner", None) or env.RUNNER + if not selected: + _fatal("No runner available for board {}".format(env.BOARD)) + + supported = env.SUPPORTED_RUNNERS + if not supported: + supported = [env.RUNNER] if env.RUNNER else [] + + if selected not in supported: + _fatal( + "Board {} does not support runner {}. Supported runners: {}".format( + env.BOARD, selected, ", ".join(supported) or "none" + ) + ) + + resources_file = None + if getattr(options, "resources", False) and env.VARIANT != "prf": + resources_file = pbpack() + + cfg = runners.RunnerConfig( + board_dir=os.path.join("boards", env.BOARD_NAME), + soc=env.CONFIG_SOC, + hex_file=fw_hex(), + elf_file=fw_elf(), + resources_file=resources_file, + dry_run=getattr(options, "dry_run", False), + ) + + try: + return runners.create(selected, cfg, options) + except runners.RunnerError as e: + _fatal(str(e)) + + +def _run_runner(env, options, command): + try: + make_runner(env, options).run(command) + except runners.RunnerError as e: + _fatal(str(e)) + + +# --- Shared helpers (moved from wscript) ----------------------------------- + +class FirmwareTooLargeException(Exception): + pass + + +def _check_firmware_image_size(env, path): + BYTES_PER_K = 1024 + firmware_size = os.path.getsize(path) + if env.CONFIG_SOC_NRF52: + if env.VARIANT == "prf" and not env.CONFIG_MFG: + max_firmware_size = 512 * BYTES_PER_K + else: + max_firmware_size = (1024 - 32) * BYTES_PER_K + elif env.CONFIG_SOC_SF32LB52: + if env.VARIANT == "prf" and not env.CONFIG_MFG: + max_firmware_size = 576 * BYTES_PER_K + else: + max_firmware_size = 3072 * BYTES_PER_K + elif env.CONFIG_QEMU: + max_firmware_size = 4096 * BYTES_PER_K + else: + _fatal("Cannot check firmware size against unknown micro family") + + if firmware_size > max_firmware_size: + raise FirmwareTooLargeException( + "Firmware is too large! Size is 0x%x should be less than 0x%x" + % (firmware_size, max_firmware_size) + ) + return "%d / %d bytes used (%d free)" % ( + firmware_size, max_firmware_size, max_firmware_size - firmware_size) + + +def _is_pulse_everywhere(env): + return bool(env.CONFIG_PULSE_EVERYWHERE) + + +def _get_pulse_flash_tool(env, options): + if env.CONFIG_SOC_SF32LB52 and not options.force_pulse: + return "sftool_flash_imaging" + if _is_pulse_everywhere(env) or options.force_pulse: + return "pulse_flash_imaging" + return "pulse_legacy_flash_imaging" + + +def run_arm_gdb(elf_path, cmd_str="", target_server_port=3333): + from tools.gdb_driver import find_gdb_path + arm_none_eabi_path = find_gdb_path() + if arm_none_eabi_path is None: + _fatal("pebble-gdb not found!") + os.system('{} {} {} --ex="target remote :{}"'.format( + arm_none_eabi_path, elf_path, cmd_str, target_server_port)) + + +# --- Operational commands -------------------------------------------------- + +def cmd_flash(env, options): + _check_firmware_image_size(env, fw_bin()) + _run_runner(env, options, "flash") + + +def cmd_console(env, options): + if env.CONFIG_QEMU: + tty = "socket://%s" % (options.qemu_host or "localhost:12345") + else: + tty = options.tty + if not tty: + _fatal("Error: --tty not specified") + + if _is_pulse_everywhere(env): + inner = "%s ./tools/pulse_console.py -t %s" % (sys.executable, tty) + elif env.CONFIG_QEMU: + inner = "%s ./tools/log_hashing/miniterm_co.py %s" % (sys.executable, tty) + else: + baudrate = options.baudrate or 230400 + # Force RTS de-asserted: on some boards RTS resets the SoC. + inner = "%s ./tools/log_hashing/miniterm_co.py %s %d --rts 0" % ( + sys.executable, tty, baudrate) + + if options.reconnect: + _run_shell("%s ./tools/console_keepalive.py -t %s -- %s" % (sys.executable, tty, inner)) + else: + _run_shell(inner) + + +def cmd_debug(env, options): + if env.CONFIG_QEMU: + import pexpect + cmd_line = "%s ./tools/qemu/qemu_gdb_proxy.py --port=1233 --target=localhost:1234" % sys.executable + proc = pexpect.spawn(cmd_line, logfile=sys.stdout, encoding="utf-8") + proc.expect(["Connected to target", pexpect.TIMEOUT], timeout=10) + run_arm_gdb(fw_elf(), target_server_port=1233) + return + + _run_runner(env, options, "debug") + + +def _qemu_launch(env, options): + qemu_bin = os.getenv("PEBBLE_QEMU_BIN") + if not qemu_bin or not (os.path.isfile(qemu_bin) and os.access(qemu_bin, os.X_OK)): + qemu_bin = "qemu-pebble" + + qemu_machine = env.CONFIG_QEMU_MACHINE + if not qemu_machine or qemu_machine == "unknown": + raise Exception("Board type '{}' not supported by QEMU".format(env.BOARD)) + + spi_flash_args = ["-drive", "if=mtd,format=raw,file=build/qemu_spi_flash.bin"] + + # Load firmware as kernel (ELF for proper vector table handling). + has_audio = env.CONFIG_PLATFORM_EMERY or env.CONFIG_PLATFORM_FLINT + if has_audio: + audio_driver = "coreaudio" if platform.system() == "Darwin" else "sdl" + machine_dep_args = ["-machine", "%s,audiodev=snd0" % qemu_machine, + "-audiodev", "%s,id=snd0" % audio_driver, + "-kernel", fw_elf()] + spi_flash_args + else: + machine_dep_args = ["-machine", qemu_machine, + "-kernel", fw_elf()] + spi_flash_args + + decoration = options.qemu_decoration + if decoration is None: + decoration = QEMU_DECORATIONS.get(env.BOARD, [None])[0] + if decoration and decoration != "none": + display_type = "sdl,decoration=%s" % decoration + else: + display_type = "sdl" + machine_dep_args.extend(["-display", "%s,show-cursor=on" % display_type]) + + mon_sock = os.path.join(BUILD_DIR, "qemu-mon.sock") + if os.path.exists(mon_sock): + os.unlink(mon_sock) + + cmd_line = ( + shlex.quote(qemu_bin) + " " + "-rtc base=localtime " + "-monitor stdio " + "-monitor unix:{mon_sock},server=on,wait=off " + "-s " + "-serial file:uart1.log " + "-serial tcp::12344,server=on,wait=off " # pebble-tool + "-serial tcp::12345,server=on,wait=off " # console + ).format(mon_sock=shlex.quote(mon_sock)) + " ".join(machine_dep_args) + _pprint("CYAN", "QEMU command: {}".format(cmd_line)) + _run_shell(cmd_line) + + +def cmd_qemu(env, options): + # The flash images are build artifacts, so let waf produce them. Always + # rebuild the micro-flash image; by default rebuild the SPI flash too + # (--keep-flash-image keeps the stored apps/data from a previous run). + if _run_waf("qemu_image_micro") != 0: + _fatal("Failed to build QEMU micro flash image") + spi_flash = os.path.join(BUILD_DIR, "qemu_spi_flash.bin") + if not options.keep_flash_image or not os.path.isfile(spi_flash): + if _run_waf("qemu_image_spi") != 0: + _fatal("Failed to build QEMU SPI flash image") + _qemu_launch(env, options) + + +def cmd_screenshot(env, options): + import socket + + sock_path = os.path.join(BUILD_DIR, "qemu-mon.sock") + if not os.path.exists(sock_path): + _fatal("QEMU monitor socket not found at {} -- is './pbl qemu' " + "running?".format(sock_path)) + + out_path = options.screenshot_output + if not out_path: + out_path = os.path.join(BUILD_DIR, "screenshot.png") + if not out_path.lower().endswith(".png"): + _fatal("--screenshot-output must end with .png") + + if os.path.exists(out_path): + os.unlink(out_path) + + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: + sock.settimeout(5) + sock.connect(sock_path) + + def read_until_prompt(): + buf = b"" + while b"(qemu) " not in buf: + chunk = sock.recv(4096) + if not chunk: + break + buf += chunk + return buf + + read_until_prompt() + sock.sendall("screendump {} -f png\n".format(out_path).encode()) + response = read_until_prompt().decode(errors="replace") + + if not os.path.exists(out_path) or os.path.getsize(out_path) == 0: + _fatal("QEMU did not write screenshot to {}\nMonitor response:\n{}" + .format(out_path, response)) + + _pprint("CYAN", "Wrote screenshot to {}".format(out_path)) + + +def cmd_openocd(env, options): + """Starts openocd and leaves it running, resetting the board to improve the + chances of attaching successfully.""" + _run_runner(env, options, "debugserver") + + +def cmd_reset(env, options): + """Resets a connected device.""" + _run_runner(env, options, "reset") + + +def cmd_run(env, options): + """Starts or resumes execution on a connected device.""" + _run_runner(env, options, "run") + + +def cmd_bork(env, options): + """Resets and wipes a connected device.""" + _run_runner(env, options, "erase") + + +def cmd_image_resources(env, options): + tty = options.tty + if tty is None: + _fatal("Error: --tty not specified") + + pbpack_path = os.path.join(BUILD_DIR, "system_resources.pbpack") + tool_name = _get_pulse_flash_tool(env, options) + _pprint("CYAN", 'Writing pbpack "%s" to tty %s' % (pbpack_path, tty)) + + ret = _run_shell("%s ./tools/%s.py -t %s -p resources %s" + % (sys.executable, tool_name, tty, pbpack_path)) + if ret != 0: + _fatal("Imaging failed") + + +def cmd_image_recovery(env, options): + tty = options.tty + if tty is None: + _fatal("Error: --tty not specified") + + tool_name = _get_pulse_flash_tool(env, options) + recovery_bin_path = options.file or fw_bin() + _pprint("CYAN", 'Writing recovery bin "%s" to tty %s' % (recovery_bin_path, tty)) + + ret = _run_shell("%s ./tools/%s.py -t %s -p firmware %s" + % (sys.executable, tool_name, tty, recovery_bin_path)) + if ret != 0: + _fatal("Imaging failed") + + +# --- Language pack commands --- + +# Template for a new language's lang_map.json (only the strings entry is +# populated; fonts are added by hand once the language needs extended glyphs). +FILE_LANG_MAP = string.Template(""" +{ + "strings": { + "lang": "${lang}", + "name": "STRINGS", + "file": "tintin.po" + }, + "fonts": [{ + "name": "GOTHIC_14_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_14_BOLD_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_18_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_18_BOLD_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_24_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_24_BOLD_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_28_EXTENDED", + "file": "" + }, { + "name": "GOTHIC_28_BOLD_EXTENDED", + "file": "" + }, { + "name": "BITHAM_18_LIGHT_SUBSET_EXTENDED", + "file": "" + }, { + "name": "BITHAM_30_BLACK_EXTENDED", + "file": "" + }, { + "name": "BITHAM_34_LIGHT_SUBSET_EXTENDED", + "file": "" + }, { + "name": "BITHAM_34_MEDIUM_NUMBERS_EXTENDED", + "file": "" + }, { + "name": "BITHAM_42_BOLD_EXTENDED", + "file": "" + }, { + "name": "BITHAM_42_LIGHT_EXTENDED", + "file": "" + }, { + "name": "BITHAM_42_MEDIUM_NUMBERS_EXTENDED", + "file": "" + }, { + "name": "ROBOTO_CONDENSED_21_EXTENDED", + "file": "" + }, { + "name": "ROBOTO_BOLD_SUBSET_49_EXTENDED", + "file": "" + }, { + "name": "DROID_SERIF_28_BOLD_EXTENDED", + "file": "" + }], + "images": [] +} +""") + + +def _ensure_lang_imports(): + """Put tools/ on sys.path so the reused resource/font modules resolve. + Inserted ahead of REPO_ROOT so 'resources' binds to tools/resources rather + than the firmware resources/ namespace dir.""" + tools = os.path.join(REPO_ROOT, "tools") + if tools not in sys.path: + sys.path.insert(0, tools) + + +def _pack_lang_code(env, lang_code): + import json + from collections import OrderedDict + + import pbpack + from resources.resource_map.resource_generator_font import FontResourceGenerator + + lang_path = os.path.join(REPO_ROOT, LANG_DIR_REL, lang_code) + build_path = os.path.join(BUILD_DIR, LANG_DIR_REL, lang_code) + + with open(os.path.join(lang_path, "lang_map.json")) as f: + resource_map = json.load(f) + + resource_data = OrderedDict() + + if not os.path.exists(build_path): + os.makedirs(build_path) + + # The "strings" entry, if any, holds the compiled .mo translation catalog. + strings = resource_map["strings"] + name = strings["name"] + ui_codepoints_path = None + + if strings["file"] == "": + resource_data[name] = b"" + else: + po_path = os.path.join(lang_path, strings["file"]) + + # Warn (don't fail) if the catalog still has untranslated strings. + if subprocess.check_output(["msgattrib", "--untranslated", po_path]) != b"": + _pprint("RED", "Warning: This PO file contains untranslated strings!") + + mo_path = os.path.join(build_path, strings["file"] + "." + name + ".mo") + os.system("msgfmt -c -v -o {} {}".format(mo_path, po_path)) + _pprint("CYAN", "Created mo at {}".format(mo_path)) + + with open(mo_path, "rb") as f: + resource_data[name] = f.read() + + ui_codepoints_path = os.path.join(build_path, "codepoints.json") + os.system("{} ./tools/generate_codepoint_requirements.py {} --output={}".format( + sys.executable, po_path, ui_codepoints_path)) + + # Build a resource for each font entry, reusing the normal resource build's + # font generator. + for entry in resource_map["fonts"]: + name = entry["name"] + + if "alias" in entry: + # Aliases reuse another resource's bytes; the pbpack dedups them. + _pprint("CYAN", "Aliasing {} to {}".format(entry["alias"], name)) + resource_data[name] = resource_data[entry["alias"]] + elif entry["file"] == "": + _pprint("CYAN", "Building empty resource {}".format(name)) + resource_data[name] = b"" + else: + _pprint("CYAN", "Building font resource {}".format(name)) + + # Make the entry look like a normal resource_map.json font entry. + entry["type"] = "font" + d = FontResourceGenerator.font_definition_from_dict(env, entry) + + if d.character_list is None: + # Default to the codepoints the UI needs unless overridden. + d.character_list = ui_codepoints_path + else: + d.character_list = os.path.join(lang_path, d.character_list) + + font_path = os.path.join(lang_path, entry["file"]) + resource_data[name] = FontResourceGenerator.build_font_data(font_path, d) + + lp = pbpack.ResourcePack(False) + for r in resource_data.values(): + lp.add_resource(r) + + language_pack = os.path.join(build_path, lang_code + ".pbl") + with open(language_pack, "wb+") as lp_file: + lp.serialize(lp_file) + + _pprint("CYAN", "Created language pack at {}".format(language_pack)) + + +def cmd_make_lang(env, options): + """Generate a new language's translation files and update existing ones.""" + import polib + + _ensure_lang_imports() + + lang_code = options.lang + lang_path = os.path.join(REPO_ROOT, LANG_DIR_REL, lang_code) + pot_path = os.path.join(REPO_ROOT, "build/src/fw/tintin.pot") + if not os.path.exists(pot_path): + _fatal("Error: could not find tintin.pot. Please run ./pbl build first") + + po_path = os.path.join(lang_path, "tintin.po") + msginit_cmd = ["msginit", "-l", lang_code, "--no-translator", "-i", pot_path] + if not os.path.exists(lang_path): + # New language: create the folder and seed lang_map.json from template. + os.mkdir(lang_path) + with open(os.path.join(lang_path, "lang_map.json"), "w") as f: + f.write(FILE_LANG_MAP.substitute({"lang": lang_code})) + + if not os.path.exists(po_path): + msginit_cmd += ["-o", po_path] + subprocess.call(msginit_cmd) + else: + # Preserve the existing header, regenerate, then merge in new strings. + po_file_header = str(polib.pofile(po_path).metadata_as_entry()) + with open(po_path, "w") as po_file: + po_file.write(po_file_header) + + import tempfile + new_po_file = tempfile.NamedTemporaryFile(delete=True) + msginit_cmd += ["-o", new_po_file.name] + subprocess.call(msginit_cmd) + subprocess.call(["msgmerge", "--lang=" + lang_code, "--update", + po_path, new_po_file.name]) + + +def cmd_pack_lang(env, options): + """Generate the language pack (pbpack) for a single language.""" + _ensure_lang_imports() + _pack_lang_code(env, options.lang) + + +def cmd_pack_all_langs(env, options): + """Generate language packs (pbpacks) for every language.""" + _ensure_lang_imports() + lang_root = os.path.join(REPO_ROOT, LANG_DIR_REL) + for lang_code in sorted(next(os.walk(lang_root))[1]): + _pack_lang_code(env, lang_code) + + +OPERATIONAL = { + "flash": cmd_flash, + "console": cmd_console, + "debug": cmd_debug, + "qemu": cmd_qemu, + "screenshot": cmd_screenshot, + "openocd": cmd_openocd, + "reset": cmd_reset, + "run": cmd_run, + "bork": cmd_bork, + "image_resources": cmd_image_resources, + "image_recovery": cmd_image_recovery, + "make_lang": cmd_make_lang, + "pack_lang": cmd_pack_lang, + "pack_all_langs": cmd_pack_all_langs, +} + + +# --- Argument parsing ------------------------------------------------------ + +def build_parser(): + parser = argparse.ArgumentParser( + prog="pbl", description="PebbleOS developer CLI") + sub = parser.add_subparsers(dest="command", metavar="") + + dry = argparse.ArgumentParser(add_help=False) + dry.add_argument("--dry-run", action="store_true", + help="Print the runner command instead of executing it") + + runner = argparse.ArgumentParser(add_help=False) + runner.add_argument("-r", "--runner", help="Override the board's default runner") + + # Runner-specific arguments (e.g. --tty for sftool) are contributed by the + # runners themselves, mirroring Zephyr's west do_add_parser(). + import tools.runners as _runners + runner_args = argparse.ArgumentParser(add_help=False) + _runners.register_args(runner_args) + + p = sub.add_parser("flash", parents=[dry, runner, runner_args], + help="Flash firmware to a device") + p.add_argument("--resources", action="store_true", + help="Also flash system resources alongside the firmware") + + p = sub.add_parser("console", help="Open the serial console") + p.add_argument("--tty", help="tty to use for the console") + p.add_argument("--baudrate", type=int, help="Baudrate for the target uart") + p.add_argument("--qemu-host", default="localhost:12345", + help="host:port for the emulator console connection") + p.add_argument("--reconnect", action="store_true", + help="Wrap the console in a keep-alive driver that reconnects") + + sub.add_parser("debug", parents=[runner, runner_args], help="Attach gdb to the target") + + p = sub.add_parser("qemu", help="Launch the firmware under QEMU") + p.add_argument("--keep-flash-image", action="store_true", + help="Keep the existing QEMU SPI flash image instead of rebuilding it") + p.add_argument("--qemu-decoration", default=None, choices=QEMU_DECORATION_CHOICES, + help="SDL decoration for QEMU (defaults to the per-board default)") + + p = sub.add_parser("screenshot", help="Capture a screenshot of running QEMU") + p.add_argument("--screenshot-output", default=None, + help="Output path (must end in .png). Defaults to build/screenshot.png") + + sub.add_parser("openocd", parents=[dry, runner, runner_args], + help="Start openocd and leave it running") + sub.add_parser("reset", parents=[dry, runner, runner_args], + help="Reset a connected device") + sub.add_parser("run", parents=[dry, runner, runner_args], + help="Start or resume a connected device") + sub.add_parser("bork", parents=[dry, runner, runner_args], + help="Reset and wipe a connected device") + + p = sub.add_parser("image_resources", parents=[dry], + help="UART-image system resources") + p.add_argument("--tty", help="tty for serial imaging") + p.add_argument("--force-pulse", action="store_true", + help="Force PULSE-based flashing even on SF32LB52") + + p = sub.add_parser("image_recovery", parents=[dry], + help="UART-image recovery firmware") + p.add_argument("--tty", help="tty for serial imaging") + p.add_argument("--file", help="Recovery binary to flash (defaults to built firmware)") + p.add_argument("--force-pulse", action="store_true", + help="Force PULSE-based flashing even on SF32LB52") + + p = sub.add_parser("make_lang", + help="Generate/update a language's translation files") + p.add_argument("--lang", default="en_US", help="Language to package (isocode)") + + p = sub.add_parser("pack_lang", help="Generate the pbpack for one language") + p.add_argument("--lang", default="en_US", help="Language to package (isocode)") + + sub.add_parser("pack_all_langs", help="Generate pbpacks for all languages") + + # ./waf pass-through wrappers. + for name in WAF_PASSTHROUGH: + sp = sub.add_parser(name, help="Forward to ./waf %s" % ( + name if name != "waf" else "")) + sp.add_argument("args", nargs=argparse.REMAINDER) + + return parser + + +def main(argv): + # Pass-through commands forward verbatim to ./waf (handled before argparse so + # flags like --board reach waf untouched). + if argv and argv[0] in WAF_PASSTHROUGH: + cmd, rest = argv[0], argv[1:] + waf_cmd = [] if cmd == "waf" else [cmd] + return subprocess.call([sys.executable, "waf"] + waf_cmd + rest, cwd=REPO_ROOT) + + parser = build_parser() + options = parser.parse_args(argv) + + if not options.command: + parser.print_help() + return 1 + + # Absolutize user-supplied path options before changing cwd. + for attr in ("file", "screenshot_output"): + val = getattr(options, attr, None) + if val: + setattr(options, attr, os.path.abspath(val)) + + os.chdir(REPO_ROOT) + env = setup(options) + OPERATIONAL[options.command](env, options) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/platform/README.md b/platform/README.md deleted file mode 100644 index 4addb652be..0000000000 --- a/platform/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Each platform should be independent from one another. - -For more information about the bootloader design: -https://pebbletechnology.atlassian.net/wiki/display/DEV/Bootloader+Contract diff --git a/platform/asterix/boot/boot-bin-update.sh b/platform/asterix/boot/boot-bin-update.sh deleted file mode 100755 index 168a5206a8..0000000000 --- a/platform/asterix/boot/boot-bin-update.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -o errexit -o xtrace - -cd "${0%/*}" - -reloutdir="../../../bin/boot" -OUTDIR=`cd "${reloutdir}"; pwd` -BOARDS=(asterix) -# Use commit timestamp, same as the one compiled into the bootloader binary -VERSION=`git log -1 --format=%ct HEAD` - -# Clear out old versions of the bootloader binaries -for board in ${BOARDS[*]}; do - git rm --ignore-unmatch ${OUTDIR}/{nowatchdog_,}boot_${board}@*.{hex,elf,bin} || true -done - -# Build all bootloader variants and copy them into OUTDIR -build_and_copy () { - local variant="$1"; - shift; - - for board in ${BOARDS[*]}; do - ./waf configure --board=${board} $@ build --progress - for ext in hex elf bin; do - mv build/tintin_boot.${ext} \ - "${OUTDIR}/${variant}boot_${board}@${VERSION}.${ext}" - done - done -} - -build_and_copy "" -build_and_copy nowatchdog_ --nowatchdog diff --git a/platform/asterix/boot/desym.sh b/platform/asterix/boot/desym.sh deleted file mode 100755 index febc86638f..0000000000 --- a/platform/asterix/boot/desym.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -arm-none-eabi-addr2line --exe=build/tintin_boot.elf $1 diff --git a/platform/asterix/boot/flash b/platform/asterix/boot/flash deleted file mode 100755 index 18cf4bbda2..0000000000 --- a/platform/asterix/boot/flash +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -o errexit - -SCRIPTDIR="${0%/*}" -IMGFILE="${SCRIPTDIR}/build/tintin_boot.hex" -IMGFILE="$(realpath $IMGFILE)" - -if [ ! -f "${IMGFILE}" ]; then - echo "Cannot find bootloader binary at '${IMGFILE}'." - echo "Try running './waf build'" - exit 1 -fi - -cd ../../.. # grab the toplevel openocd.cfg - -OPENOCD_SCRIPT=" -init -reset halt -flash write_image erase \"${IMGFILE}\" -reset -shutdown -" - -openocd -f openocd.cfg -c "${OPENOCD_SCRIPT}" - -# vim:filetype=sh diff --git a/platform/asterix/boot/src/board/board.h b/platform/asterix/boot/src/board/board.h deleted file mode 100644 index 8d5cc322e5..0000000000 --- a/platform/asterix/boot/src/board/board.h +++ /dev/null @@ -1,32 +0,0 @@ -#define BOARD_UART_TX_PIN 27 - -#define BOARD_QSPI_SCK_PIN 19 -#define BOARD_QSPI_SCN_PIN 17 -#define BOARD_QSPI_IO0_PIN 20 -#define BOARD_QSPI_IO1_PIN 21 -#define BOARD_QSPI_IO2_PIN 22 -#define BOARD_QSPI_IO3_PIN 23 - -#define BOARD_DISP_DISP_PORT NRF_P0 -#define BOARD_DISP_DISP_PIN 4 -#define BOARD_DISP_CS_PORT NRF_P1 -#define BOARD_DISP_CS_PIN 3 -#define BOARD_DISP_SCK_PIN 6 -#define BOARD_DISP_MOSI_PIN 8 -#define BOARD_DISP_EXTCOMIN_PWM NRF_PWM0 -#define BOARD_DISP_EXTCOMIN_PIN 47 - -#define BOARD_BUTTON_BACK_PORT NRF_P0 -#define BOARD_BUTTON_BACK_PIN 28 -#define BOARD_BUTTON_UP_PORT NRF_P0 -#define BOARD_BUTTON_UP_PIN 29 -#define BOARD_BUTTON_SELECT_PORT NRF_P0 -#define BOARD_BUTTON_SELECT_PIN 30 -#define BOARD_BUTTON_DOWN_PORT NRF_P0 -#define BOARD_BUTTON_DOWN_PIN 31 - -#define BOARD_PMIC_I2C 1 -#define BOARD_PMIC_I2C_SCL_PIN 14 -#define BOARD_PMIC_I2C_SDA_PIN 15 - -#define BOARD_FLASH_JEDEC_ID 0xc86019 diff --git a/platform/asterix/boot/src/boot_tests.c b/platform/asterix/boot/src/boot_tests.c deleted file mode 100644 index fc3d00380e..0000000000 --- a/platform/asterix/boot/src/boot_tests.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "boot_tests.h" - -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/flash.h" -#include "system/bootbits.h" -#include "system/retained.h" -#include "util/misc.h" - -#include -#include -#include - -static const int STUCK_BUTTON_THRESHOLD = 5; - -bool is_button_stuck(void) { - // We store how many times each button has been pressed on previous boots in this - // rtc backup register. Every time when we boot without that button pressed that - // counter gets cleared. If the byte reaches 5, return a failure. - - uint32_t button_counter_register = retained_read(STUCK_BUTTON_REGISTER); - uint8_t* button_counter = (uint8_t*) (&button_counter_register); - bool result = false; - - for (int button_id = 0; button_id < NUM_BUTTONS; button_id++) { - if (!button_is_pressed(button_id)) { - button_counter[button_id] = 0; - continue; - } - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_putstr("Stuck button register is invalid, clearing."); - dbgserial_print_hex(button_counter_register); - - retained_write(STUCK_BUTTON_REGISTER, 0); - return false; - } - - button_counter[button_id] += 1; - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_print("Button id "); - dbgserial_print_hex(button_id); - dbgserial_putstr("is stuck!"); - result = true; - } - } - - if (button_counter_register != 0) { - dbgserial_print("Button was pushed on boot. Button counter: "); - dbgserial_print_hex(button_counter_register); - dbgserial_newline(); - } - - retained_write(STUCK_BUTTON_REGISTER, button_counter_register); - return result; -} - -bool is_flash_broken(void) { - return !flash_sanity_check(); -} diff --git a/platform/asterix/boot/src/boot_tests.h b/platform/asterix/boot/src/boot_tests.h deleted file mode 100644 index 7e336ce823..0000000000 --- a/platform/asterix/boot/src/boot_tests.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -bool is_button_stuck(void); -bool is_flash_broken(void); diff --git a/platform/asterix/boot/src/drivers/button.c b/platform/asterix/boot/src/drivers/button.c deleted file mode 100644 index 5a111b20b9..0000000000 --- a/platform/asterix/boot/src/drivers/button.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "drivers/button.h" - -#include - -#include - -typedef struct { - const char *name; - NRF_GPIO_Type *port; - uint32_t pin; - uint8_t pull; -} ButtonConfig; - -static const ButtonConfig BOARD_CONFIG_BUTTON[] = { - [BUTTON_ID_BACK] = {"Back", BOARD_BUTTON_BACK_PORT, BOARD_BUTTON_BACK_PIN, - GPIO_PIN_CNF_PULL_Pullup}, - [BUTTON_ID_UP] = {"Up", BOARD_BUTTON_UP_PORT, BOARD_BUTTON_UP_PIN, - GPIO_PIN_CNF_PULL_Pullup}, - [BUTTON_ID_SELECT] = {"Select", BOARD_BUTTON_SELECT_PORT, BOARD_BUTTON_SELECT_PIN, - GPIO_PIN_CNF_PULL_Pullup}, - [BUTTON_ID_DOWN] = {"Down", BOARD_BUTTON_DOWN_PORT, BOARD_BUTTON_DOWN_PIN, - GPIO_PIN_CNF_PULL_Pullup}, -}; - -static void initialize_button(const ButtonConfig *config) -{ - config->port->PIN_CNF[config->pin] = - ((GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | - (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | - (config->pull << GPIO_PIN_CNF_PULL_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)); -} - -bool button_is_pressed(ButtonId id) -{ - const ButtonConfig *config = &BOARD_CONFIG_BUTTON[id]; - return (config->port->IN & (1 << config->pin)) == 0; -} - -uint8_t button_get_state_bits(void) -{ - uint8_t button_state = 0x00; - for (int i = 0; i < NUM_BUTTONS; ++i) { - button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i; - } - return button_state; -} - -void button_init(void) -{ - for (int i = 0; i < NUM_BUTTONS; ++i) { - initialize_button(&BOARD_CONFIG_BUTTON[i]); - } -} diff --git a/platform/asterix/boot/src/drivers/button.h b/platform/asterix/boot/src/drivers/button.h deleted file mode 100644 index 7664a4444b..0000000000 --- a/platform/asterix/boot/src/drivers/button.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "button_id.h" - -#include -#include - -void button_init(void); - -bool button_is_pressed(ButtonId id); -uint8_t button_get_state_bits(void); diff --git a/platform/asterix/boot/src/drivers/button_id.h b/platform/asterix/boot/src/drivers/button_id.h deleted file mode 100644 index 2577df414d..0000000000 --- a/platform/asterix/boot/src/drivers/button_id.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -//! @addtogroup UI -//! @{ -//! @addtogroup Clicks -//! \brief Dealing with button input -//! @{ - -//! Button ID values -//! @see \ref click_recognizer_get_button_id() -typedef enum { - //! Back button - BUTTON_ID_BACK = 0, - //! Up button - BUTTON_ID_UP, - //! Select (middle) button - BUTTON_ID_SELECT, - //! Down button - BUTTON_ID_DOWN, - //! Total number of buttons - NUM_BUTTONS -} ButtonId; - -//! @} // end addtogroup Clicks -//! @} // end addtogroup UI diff --git a/platform/asterix/boot/src/drivers/dbgserial.c b/platform/asterix/boot/src/drivers/dbgserial.c deleted file mode 100644 index f7a9c0503c..0000000000 --- a/platform/asterix/boot/src/drivers/dbgserial.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "drivers/dbgserial.h" - -#include "util/cobs.h" -#include "util/crc32.h" -#include "util/misc.h" -#include "util/net.h" - -#include -#include - -#include - -#define MAX_MESSAGE (256) -#define FRAME_DELIMITER '\x55' -#define PULSE_TRANSPORT_PUSH (0x5021) -#define PULSE_PROTOCOL_LOGGING (0x0003) - -typedef struct __attribute__((__packed__)) PulseFrame { - net16 protocol; - unsigned char information[]; -} PulseFrame; - -typedef struct __attribute__((__packed__)) PushPacket { - net16 protocol; - net16 length; - unsigned char information[]; -} PushPacket; - -static const unsigned char s_message_header[] = { - // Message type: text - 1, - // Source filename - 'B', 'O', 'O', 'T', 'L', 'O', 'A', 'D', 'E', 'R', 0, 0, 0, 0, 0, 0, - // Log level and task - '*', '*', - // Timestamp - 0, 0, 0, 0, 0, 0, 0, 0, - // Line number - 0, 0, -}; - -static size_t s_message_length = 0; -static unsigned char s_message_buffer[MAX_MESSAGE]; - -void dbgserial_init(void) { - NRF_UART0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M; - NRF_UART0->TASKS_STARTTX = 1; - NRF_UART0->PSELTXD = BOARD_UART_TX_PIN; -} - -static void prv_putchar(uint8_t c) { - NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled; - NRF_UART0->TXD = c; - while (NRF_UART0->EVENTS_TXDRDY != 1) {} - NRF_UART0->EVENTS_TXDRDY = 0; - NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Disabled; -} - -void dbgserial_print(const char* str) { - - for (; *str && s_message_length < MAX_MESSAGE; ++str) { - if (*str == '\n') { - dbgserial_newline(); - } else if (*str != '\r') { - s_message_buffer[s_message_length++] = *str; - } - } - -} - -void dbgserial_newline(void) { - uint32_t crc; - size_t raw_length = sizeof(PulseFrame) + sizeof(PushPacket) + - sizeof(s_message_header) + s_message_length + sizeof(crc); - unsigned char raw_packet[raw_length]; - - PulseFrame *frame = (PulseFrame *)raw_packet; - frame->protocol = hton16(PULSE_TRANSPORT_PUSH); - - PushPacket *transport = (PushPacket *)frame->information; - transport->protocol = hton16(PULSE_PROTOCOL_LOGGING); - transport->length = hton16(sizeof(PushPacket) + sizeof(s_message_header) + - s_message_length); - - unsigned char *app = transport->information; - memcpy(app, s_message_header, sizeof(s_message_header)); - memcpy(&app[sizeof(s_message_header)], s_message_buffer, - s_message_length); - - crc = crc32(CRC32_INIT, raw_packet, raw_length - sizeof(crc)); - memcpy(&raw_packet[raw_length - sizeof(crc)], &crc, sizeof(crc)); - - unsigned char cooked_packet[MAX_SIZE_AFTER_COBS_ENCODING(raw_length)]; - size_t cooked_length = cobs_encode(cooked_packet, raw_packet, raw_length); - - prv_putchar(FRAME_DELIMITER); - for (size_t i = 0; i < cooked_length; ++i) { - if (cooked_packet[i] == FRAME_DELIMITER) { - prv_putchar('\0'); - } else { - prv_putchar(cooked_packet[i]); - } - } - prv_putchar(FRAME_DELIMITER); - - s_message_length = 0; -} - -void dbgserial_putstr(const char* str) { - dbgserial_print(str); - - dbgserial_newline(); -} - -void dbgserial_print_hex(uint32_t value) { - char buf[12]; - itoa_hex(value, buf, sizeof(buf)); - dbgserial_print(buf); -} diff --git a/platform/asterix/boot/src/drivers/dbgserial.h b/platform/asterix/boot/src/drivers/dbgserial.h deleted file mode 100644 index ca43f87c63..0000000000 --- a/platform/asterix/boot/src/drivers/dbgserial.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include - -void dbgserial_init(void); - -void dbgserial_putstr(const char* str); - -void dbgserial_newline(void); - -//! Like dbgserial_putstr, but without a terminating newline -void dbgserial_print(const char* str); - -void dbgserial_print_hex(uint32_t value); - -void dbgserial_putstr_fmt(char* buffer, unsigned int buffer_size, const char* fmt, ...) - __attribute__((format(printf, 3, 4))); diff --git a/platform/asterix/boot/src/drivers/display.h b/platform/asterix/boot/src/drivers/display.h deleted file mode 100644 index 745652feff..0000000000 --- a/platform/asterix/boot/src/drivers/display.h +++ /dev/null @@ -1,20 +0,0 @@ -#include - -void display_init(void); - -void display_deinit(void); - -void display_boot_splash(void); - -void display_error_code(uint32_t); - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void); - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator); diff --git a/platform/asterix/boot/src/drivers/display/resources/dead_face.xbm b/platform/asterix/boot/src/drivers/display/resources/dead_face.xbm deleted file mode 100644 index 3f7bbf3316..0000000000 --- a/platform/asterix/boot/src/drivers/display/resources/dead_face.xbm +++ /dev/null @@ -1,57 +0,0 @@ -#define dead_face_width 69 -#define dead_face_height 71 -static const unsigned char dead_face_bits[] = { - 0x00, 0x00, 0x00, 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xf8, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, - 0x00, 0x00, 0xe0, 0x1f, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf8, - 0x0f, 0x00, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00, - 0xfe, 0x01, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, - 0x00, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, - 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, - 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, - 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x3c, 0x70, - 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x1e, 0x70, 0x00, 0x70, 0x00, - 0x00, 0xc0, 0x03, 0x00, 0x1e, 0xf0, 0x00, 0x78, 0x00, 0x00, 0xc0, 0x03, - 0x00, 0x1e, 0xe0, 0x00, 0x7c, 0x00, 0x00, 0x80, 0x07, 0x00, 0x0f, 0xe0, - 0x01, 0x3e, 0x00, 0x00, 0x80, 0x07, 0x00, 0x0f, 0xc0, 0x01, 0x1f, 0x00, - 0x00, 0x80, 0x07, 0x00, 0x0f, 0xc0, 0x83, 0x0f, 0x00, 0x00, 0x80, 0x07, - 0x00, 0x0f, 0xc0, 0xc3, 0x07, 0x00, 0x00, 0x80, 0x07, 0x80, 0x07, 0xe0, - 0xc3, 0x03, 0x00, 0x00, 0x80, 0x07, 0x80, 0x07, 0xf0, 0xc1, 0x03, 0x00, - 0x00, 0x80, 0x07, 0x80, 0x07, 0xf8, 0x80, 0x03, 0x00, 0x00, 0x80, 0x07, - 0x80, 0x07, 0x7c, 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0x3e, - 0x00, 0x07, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0x1e, 0x00, 0x0f, 0x00, - 0x00, 0x80, 0x07, 0xe0, 0x03, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x80, 0x07, - 0xf0, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x80, 0x07, 0xf8, 0x00, 0x00, - 0x00, 0x0c, 0x00, 0x00, 0x80, 0x07, 0x78, 0x00, 0xc0, 0x71, 0x00, 0x00, - 0x00, 0x80, 0x07, 0x7c, 0x00, 0xe0, 0x79, 0x00, 0x18, 0x00, 0x80, 0x07, - 0x3c, 0x00, 0xf0, 0xff, 0x00, 0x3c, 0x00, 0x80, 0x07, 0x3c, 0x00, 0xf8, - 0xff, 0x00, 0x3c, 0x00, 0x80, 0x07, 0x3e, 0x00, 0xf8, 0xff, 0x01, 0x1e, - 0x00, 0x80, 0x07, 0x1e, 0x00, 0x38, 0xcf, 0x01, 0x1e, 0x00, 0x80, 0x07, - 0x1e, 0x00, 0x00, 0xc7, 0x01, 0x0f, 0x00, 0xc0, 0x03, 0x1f, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0xc0, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x80, 0x07, - 0x00, 0xe0, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0xe0, 0x01, - 0x0f, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0xc0, 0x03, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xc0, 0x03, - 0x00, 0x78, 0x00, 0x0f, 0x60, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x78, 0x00, - 0x0f, 0xf8, 0x00, 0x00, 0xc0, 0x03, 0x3c, 0x3c, 0x00, 0x0f, 0xfe, 0x01, - 0x00, 0xc0, 0x03, 0x3f, 0x3c, 0x00, 0x9f, 0xff, 0x03, 0x00, 0xc0, 0xc3, - 0x3f, 0x1e, 0x00, 0xff, 0xff, 0x07, 0x00, 0xc0, 0xe3, 0x0f, 0x1e, 0x00, - 0xfe, 0x87, 0x0f, 0x00, 0xc0, 0xff, 0x03, 0x1e, 0x00, 0xf8, 0x03, 0x1f, - 0x00, 0xc0, 0xff, 0x00, 0x1e, 0x00, 0x60, 0x00, 0x3e, 0x00, 0x80, 0x7f, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x3e, 0x00, 0x1e, 0x00, - 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xf0, - 0x7f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x07, 0x00, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0xff, 0x0f, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, - 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x00, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x03, 0xf0, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf0, 0x1f, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, - 0x7f, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xf0, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0xff, 0x0f }; diff --git a/platform/asterix/boot/src/drivers/display/resources/empty_bar.xbm b/platform/asterix/boot/src/drivers/display/resources/empty_bar.xbm deleted file mode 100644 index ace1c01445..0000000000 --- a/platform/asterix/boot/src/drivers/display/resources/empty_bar.xbm +++ /dev/null @@ -1,11 +0,0 @@ -#define empty_bar_width 96 -#define empty_bar_height 8 -static const unsigned char empty_bar_bits[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; diff --git a/platform/asterix/boot/src/drivers/display/resources/hex_digits.h b/platform/asterix/boot/src/drivers/display/resources/hex_digits.h deleted file mode 100644 index febbc19b00..0000000000 --- a/platform/asterix/boot/src/drivers/display/resources/hex_digits.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -// hex_digits_bits is indexed on the digit: -// ie: hex_digits_bits[0] is the bits to display 0 on -// the screen stored in xbm format -static const uint8_t hex_digits_bits[][36] = { - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x38, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, - 0x0E, 0x00, 0x0F, 0x00, 0x07, 0x00, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xF8, 0x00, 0x78, 0x00, 0xF8, 0x00, 0xC0, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xE0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0xF8, 0x00, - 0xF8, 0x00, 0xFC, 0x00, 0xEC, 0x00, 0xEE, 0x00, 0xE6, 0x00, 0xFF, 0x01, - 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, - }, - { - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, - 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC0, 0x01, 0xC0, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, - 0x07, 0x00, 0x77, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, 0x00, - 0x70, 0x00, 0x70, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0xFE, 0x00, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x01, 0xDC, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xF0, 0x01, 0xFC, 0x01, 0xCE, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01, - }, - { - 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0xE7, 0x00, 0xF7, 0x01, - 0xFF, 0x01, 0xCF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xCF, 0x01, 0xFF, 0x01, 0xF7, 0x01, 0xE7, 0x00, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, - 0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xCE, 0x01, 0xDF, 0x01, - 0xFF, 0x01, 0xE7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFF, 0x01, - 0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, - 0xFE, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - } -}; diff --git a/platform/asterix/boot/src/drivers/display/resources/pebbleos_logo.xbm b/platform/asterix/boot/src/drivers/display/resources/pebbleos_logo.xbm deleted file mode 100644 index 287f8b5d35..0000000000 --- a/platform/asterix/boot/src/drivers/display/resources/pebbleos_logo.xbm +++ /dev/null @@ -1,37 +0,0 @@ -#define pebbleos_logo_width 112 -#define pebbleos_logo_height 29 -static const unsigned char pebbleos_logo_bits[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0xfc, 0xf1, - 0x07, 0x3e, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, - 0xfc, 0xe0, 0x07, 0x7f, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, - 0x00, 0x00, 0x0c, 0x00, 0x86, 0x63, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, - 0x00, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x86, 0x01, 0xf0, 0x01, 0xf8, 0xc0, - 0x7c, 0x60, 0x3e, 0x30, 0xe0, 0x03, 0x0c, 0x00, 0x86, 0x01, 0xfc, 0x07, - 0xfe, 0xc3, 0xff, 0xe1, 0xff, 0x30, 0xf8, 0x0f, 0x4e, 0x10, 0x8e, 0x03, - 0x0e, 0x0e, 0x07, 0xc7, 0x83, 0xe3, 0xc1, 0x31, 0x1c, 0x1c, 0x46, 0x10, - 0x0c, 0x0f, 0x06, 0x0c, 0x03, 0xc6, 0x01, 0xe3, 0x80, 0x31, 0x0c, 0x18, - 0x43, 0x10, 0x18, 0x3e, 0x03, 0x98, 0x81, 0xcf, 0x00, 0x66, 0x00, 0x33, - 0x06, 0xbe, 0x03, 0x00, 0x38, 0x78, 0x03, 0x98, 0xf9, 0xcf, 0x00, 0x66, - 0x00, 0x33, 0xe6, 0x3f, 0x43, 0x10, 0x18, 0x60, 0x03, 0x98, 0xff, 0xc0, - 0x00, 0x66, 0x00, 0x33, 0xfe, 0x03, 0x86, 0x0f, 0x0c, 0xc0, 0x03, 0x98, - 0x0f, 0xc0, 0x00, 0x66, 0x00, 0x33, 0x3e, 0x00, 0x0e, 0x00, 0x0e, 0xc0, - 0x03, 0x98, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x06, 0x30, 0x0c, 0x00, - 0xc6, 0xc0, 0x07, 0x0c, 0x03, 0x86, 0x01, 0xc3, 0x80, 0x31, 0x0c, 0x18, - 0x0c, 0x00, 0xc6, 0xc0, 0x0f, 0x0e, 0x8f, 0x87, 0x83, 0xc3, 0xc1, 0x31, - 0x1c, 0x1c, 0x0c, 0x00, 0x86, 0x61, 0xff, 0x07, 0xfe, 0x03, 0xff, 0x81, - 0xff, 0x70, 0xf8, 0x0f, 0xfc, 0xe0, 0x87, 0x7f, 0xf3, 0x01, 0xf8, 0x00, - 0x7c, 0x00, 0x3e, 0x60, 0xe0, 0x03, 0xfc, 0xf1, 0x07, 0x1e, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, - 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x04, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; diff --git a/platform/asterix/boot/src/drivers/display/sharp_ls013b7dh01.c b/platform/asterix/boot/src/drivers/display/sharp_ls013b7dh01.c deleted file mode 100644 index 37d12b2443..0000000000 --- a/platform/asterix/boot/src/drivers/display/sharp_ls013b7dh01.c +++ /dev/null @@ -1,330 +0,0 @@ -#include "drivers/dbgserial.h" - -#include -#include -#include - -#include - -#include -#include - -// Bootloader images -#include "drivers/display/resources/hex_digits.h" -#include "drivers/display/resources/dead_face.xbm" -#include "drivers/display/resources/empty_bar.xbm" -#include "drivers/display/resources/pebbleos_logo.xbm" - -#define DISP_COLS 144 -#define DISP_ROWS 168 -#define PBL_DISP_SHAPE_RECT - -#define DISP_LINE_BYTES (DISP_COLS / 8) -#define DISP_LINE_WORDS (((DISP_COLS - 1) / 32) + 1) - -// GPIO constants -#define DISP_MODE_STATIC (0x00) -#define DISP_MODE_WRITE (0x80) -#define DISP_MODE_CLEAR (0x20) - -static uint16_t s_extcomin_period; - -static nrfx_spim_t spim = NRFX_SPIM_INSTANCE(3); -static nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG( - BOARD_DISP_SCK_PIN, BOARD_DISP_MOSI_PIN, NRF_SPIM_PIN_NOT_CONNECTED, - NRF_SPIM_PIN_NOT_CONNECTED -); - -static void prv_enable_chip_select(void) { - //GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); - BOARD_DISP_CS_PORT->OUTSET = (1 << BOARD_DISP_CS_PIN); - // required setup time > 3us - NRFX_DELAY_US(7); -} - -static void prv_disable_chip_select(void) { - // delay while last byte is emitted by the SPI peripheral - NRFX_DELAY_US(7); - - //GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); - BOARD_DISP_CS_PORT->OUTCLR = (1 << BOARD_DISP_CS_PIN); - // hold time > 1us - // produce a delay 4ms - NRFX_DELAY_US(4); -} - -//! These functions needed to be called around any commands that -//! are sent to the display. NOINLINE only for code size savings. -static void prv_enable_display_access(void) { - prv_enable_chip_select(); -} - -static void prv_disable_display_access(void) { - prv_disable_chip_select(); -} - -//! Write a single byte synchronously to the display. This is the only practical -//! way to write to the display in the bootloader since we don't have interrupts. -static void prv_display_write_byte(uint8_t d) { - nrfx_spim_xfer_desc_t desc = NRFX_SPIM_XFER_TX(&d, 1); - nrfx_spim_xfer(&spim, &desc, 0); - // Block until the tx buffer is empty - //SPI_I2S_SendData(DISP_SPI, d); - //while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) {} -} - -// Since all these values are constant we can save code space -// by storing the initialized struct in memory rather than -// needing to copy in each value -/* -static SPI_InitTypeDef s_disp_spi_init = { - .SPI_Direction = SPI_Direction_1Line_Tx, // Write-only SPI - .SPI_Mode = SPI_Mode_Master, - .SPI_DataSize = SPI_DataSize_8b, - .SPI_CPOL = SPI_CPOL_Low, - .SPI_CPHA = SPI_CPHA_1Edge, - .SPI_NSS = SPI_NSS_Soft, - // We want the SPI clock to run at 2MHz - .SPI_BaudRatePrescaler = DISPLAY_PERIPH_PRESCALER, - // MSB order allows us to write pixels out without reversing bytes, but command bytes - // have to be reversed - .SPI_FirstBit = SPI_FirstBit_MSB, - .SPI_CRCPolynomial = 7 // default -}; -*/ - -static void prv_display_start(void) { - // Hold LCD on - // GPIO_WriteBit(DISP_LCD_GPIO, DISP_PIN_LCD, Bit_SET); - BOARD_DISP_DISP_PORT->OUTSET = (1 << BOARD_DISP_DISP_PIN); -} - -// Clear-all mode is entered by sending 0x04 to the panel -void display_clear(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_CLEAR); - prv_display_write_byte(0x00); - - prv_disable_display_access(); -} - -//! Static mode is entered by sending 0x00 to the panel -//! This stops any further updates being registered by -//! the display, preventing corruption on shutdown / boot -static void prv_display_enter_static(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_STATIC); - prv_display_write_byte(0x00); - prv_display_write_byte(0x00); - - prv_disable_display_access(); -} - -// Helper to reverse command bytes -static uint8_t prv_reverse_bits(uint8_t input) { - uint8_t result; - __asm__ ("rev %[result], %[input]\n\t" - "rbit %[result], %[result]" - : [result] "=r" (result) - : [input] "r" (input)); - return result; -} - -static void prv_display_start_write(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_WRITE); -} - -static void prv_display_write_line(uint8_t line_addr, const uint8_t *line) { - // 1-indexed (ugh) 8bit line address (1-168) - prv_display_write_byte(prv_reverse_bits(line_addr + 1)); - - for (int i = 0; i < DISP_LINE_BYTES; ++i) { - prv_display_write_byte(prv_reverse_bits(line[i])); - } - - prv_display_write_byte(0x00); -} - -static void prv_display_end_write(void) { - prv_display_write_byte(0x00); - - - prv_disable_display_access(); -} - -// Round a bit offset to a byte offset -static unsigned prv_round_to_byte(unsigned x) { - return (x + 7) >> 3; -} - -// Draw bitmap onto buffer. -static void prv_draw_bitmap(const uint8_t *bitmap, unsigned x_offset, unsigned y_offset, - unsigned width, unsigned height, - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - // Need to convert offsets to bytes for the horizontal dimensions - x_offset = prv_round_to_byte(x_offset); - width = prv_round_to_byte(width); - - for (unsigned i = 0; i < height; i++) { - memcpy(buffer[i + y_offset] + x_offset, bitmap + i * width, width); - } -} - -static void prv_display_buffer(uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - prv_display_start_write(); - for (int i = 0; i < DISP_ROWS; i++) { - prv_display_write_line(i, buffer[i]); - } - prv_display_end_write(); -} - -void display_boot_splash(void) { - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - // Draw black - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(pebbleos_logo_bits, 16, 69, pebbleos_logo_width, pebbleos_logo_height, buffer); - - prv_display_buffer(buffer); -} - -static void prv_set_bit(uint8_t x, uint8_t y, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - buffer[y][x / 8] |= (1 << (x % 8)); -} - -static void prv_render_char(unsigned digit, uint8_t x_offset_bits, uint8_t y_offset, - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - const unsigned char_rows = 18, char_cols = 9; - const uint8_t * char_data = hex_digits_bits[digit]; - - // Each character requires 2 bytes of storage - for (unsigned y = 0; y < char_rows; y++) { - unsigned cur_y = y_offset + y; - uint8_t first_byte = char_data[2 * y]; - - for (unsigned x = 0; x < char_cols; x++) { - bool pixel; - if (x < 8) { // Pixel is in first byte - pixel = first_byte & (1 << x); - } - else { // Last pixel is in second byte - pixel = char_data[2 * y + 1] & 1; - } - - // The buffer is already all black, so just set the white pixels - if (pixel) { - prv_set_bit(x_offset_bits + x, cur_y, buffer); - } - } - } -} - -static void prv_draw_code(uint32_t code, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - const unsigned y_offset = 116; // beneath sad face, above url - unsigned x_offset = 28; // Aligned with sad face - - // Extract and print digits - for (int i = 7; i >= 0; i--) { - // Mask off 4 bits at a time - uint32_t mask = (0xf << (i * 4)); - unsigned digit = ((code & mask) >> (i * 4)); - prv_render_char(digit, x_offset, y_offset, buffer); - - // Each character is 9px wide plus 2px of padding - x_offset += 11; - } -} - -void display_error_code(uint32_t code) { - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(dead_face_bits, (140 - dead_face_width) / 2, 24, dead_face_width, dead_face_height, buffer); - - prv_draw_code(code, buffer); - - prv_display_buffer(buffer); -} - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void) { - prv_display_enter_static(); -} - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator) { - // Dimensions for progress bar - const unsigned x_offset = 24, y_offset = 106, - inner_bar_width = 94, inner_bar_height = 6; - - static unsigned s_prev_num_pixels = -1; - // Calculate number of pixels to fill in - unsigned num_pixels = inner_bar_width * numerator / denominator; - - if (num_pixels == s_prev_num_pixels) { - return; - } - s_prev_num_pixels = num_pixels; - - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(pebbleos_logo_bits, 16, 69, pebbleos_logo_width, pebbleos_logo_height, buffer); - - - prv_draw_bitmap(empty_bar_bits, x_offset, y_offset, empty_bar_width, empty_bar_height, buffer); - - for (unsigned y = 0; y < inner_bar_height; y++) { - for (unsigned x = 0; x < num_pixels; x++) { - // Add 1 to offsets so we don't write into outer box - prv_set_bit(x + x_offset + 1, y_offset + y + 1, buffer); - } - } - - prv_display_buffer(buffer); -} - -void display_init(void) { - config.frequency = NRFX_MHZ_TO_HZ(1); - - // PWM: 120Hz, 100us pulse - s_extcomin_period = ((100U * 125000UL) / 1000000UL) | (1U << 15U); - nrf_pwm_pin_set(BOARD_DISP_EXTCOMIN_PWM, 0, BOARD_DISP_EXTCOMIN_PIN); - nrf_pwm_enable(BOARD_DISP_EXTCOMIN_PWM); - nrf_pwm_configure(BOARD_DISP_EXTCOMIN_PWM, NRF_PWM_CLK_125kHz, NRF_PWM_MODE_UP, 125000 / 120); - nrf_pwm_loop_set(BOARD_DISP_EXTCOMIN_PWM, 0); - nrf_pwm_decoder_set(BOARD_DISP_EXTCOMIN_PWM, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO); - nrf_pwm_seq_ptr_set(BOARD_DISP_EXTCOMIN_PWM, 0, &s_extcomin_period); - nrf_pwm_seq_cnt_set(BOARD_DISP_EXTCOMIN_PWM, 0, 1); - nrf_pwm_seq_refresh_set(BOARD_DISP_EXTCOMIN_PWM, 0, 0); - nrf_pwm_seq_end_delay_set(BOARD_DISP_EXTCOMIN_PWM, 0, 0); - nrf_pwm_task_trigger(BOARD_DISP_EXTCOMIN_PWM, NRF_PWM_TASK_SEQSTART0); - - nrfx_spim_init(&spim, &config, NULL, NULL); - - BOARD_DISP_CS_PORT->PIN_CNF[BOARD_DISP_CS_PIN] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | - (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | - (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); - BOARD_DISP_DISP_PORT->PIN_CNF[BOARD_DISP_DISP_PIN] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | - (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | - (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); - prv_display_start(); -} - -void display_deinit(void) { - nrfx_spim_uninit(&spim); - nrf_pwm_disable(BOARD_DISP_EXTCOMIN_PWM); -} diff --git a/platform/asterix/boot/src/drivers/flash.h b/platform/asterix/boot/src/drivers/flash.h deleted file mode 100644 index daf782ce7b..0000000000 --- a/platform/asterix/boot/src/drivers/flash.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -//! Configure the micro's peripherals to communicate with the flash chip -void flash_init(void); - -//! Read 1 or more bytes starting at the specified 24bit address into -//! the provided buffer. This function does no range checking, so it is -//! currently possible to run off the end of the flash. -//! -//! @param buffer A byte-buffer that will be used to store the data -//! read from flash. -//! @param start_addr The address of the first byte to be read from flash. -//! @param buffer_size The total number of bytes to be read from flash. -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -//! Check if we can talk to the flash. -//! @return true if the CFI table can be queried. -bool flash_sanity_check(void); - -//! Get the checksum of a region of flash -uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t length); diff --git a/platform/asterix/boot/src/drivers/flash/flash_crc.c b/platform/asterix/boot/src/drivers/flash/flash_crc.c deleted file mode 100644 index 0344f72998..0000000000 --- a/platform/asterix/boot/src/drivers/flash/flash_crc.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "drivers/flash.h" - -#include "util/crc32.h" - -#define CRC_CHUNK_SIZE 1024 - -uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t num_bytes) { - uint8_t buffer[CRC_CHUNK_SIZE]; - - uint32_t crc = CRC32_INIT; - - while (num_bytes > CRC_CHUNK_SIZE) { - flash_read_bytes(buffer, flash_addr, CRC_CHUNK_SIZE); - crc = crc32(crc, buffer, CRC_CHUNK_SIZE); - - num_bytes -= CRC_CHUNK_SIZE; - flash_addr += CRC_CHUNK_SIZE; - } - - flash_read_bytes(buffer, flash_addr, num_bytes); - return crc32(crc, buffer, num_bytes); -} diff --git a/platform/asterix/boot/src/drivers/flash/mx25u.c b/platform/asterix/boot/src/drivers/flash/mx25u.c deleted file mode 100644 index 17d3cea526..0000000000 --- a/platform/asterix/boot/src/drivers/flash/mx25u.c +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include -#include -#include - -#define SPI_NOR_CMD_RDID 0x9F -#define SPI_NOR_CMD_RDPD 0xAB -#define SPI_NOR_CMD_ENRST 0x66 -#define SPI_NOR_CMD_RST 0x99 -#define SPI_NOR_CMD_EN4B 0xB7 -#define SPI_NOR_CMD_RDSR1 0x05 -#define SPI_NOR_CMD_RDSR2 0x35 -#define SPI_NOR_CMD_WRSR 0x01 - -static void prv_read_register(uint8_t instruction, uint8_t *data, uint32_t length) { - nrfx_err_t err; - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(instruction, length + 1); - instr.io2_level = true; - instr.io3_level = true; - err = nrfx_qspi_cinstr_xfer(&instr, NULL, data); - PBL_ASSERTN(err == NRFX_SUCCESS); -} - -static void prv_write_register(uint8_t instruction, const uint8_t *data, uint32_t length) { - nrfx_err_t err; - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(instruction, length + 1); - instr.io2_level = true; - instr.io3_level = true; - err = nrfx_qspi_cinstr_xfer(&instr, data, NULL); - PBL_ASSERTN(err == NRFX_SUCCESS); -} - -void flash_init(void) { - uint8_t sr[2]; - nrfx_qspi_config_t config = - NRFX_QSPI_DEFAULT_CONFIG(BOARD_QSPI_SCK_PIN, BOARD_QSPI_SCN_PIN, BOARD_QSPI_IO0_PIN, - BOARD_QSPI_IO1_PIN, BOARD_QSPI_IO2_PIN, BOARD_QSPI_IO3_PIN); - - config.prot_if.readoc = NRF_QSPI_READOC_READ4IO; - config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP4O; - config.phy_if.sck_freq = NRF_QSPI_FREQ_DIV4; - config.prot_if.addrmode = NRF_QSPI_ADDRMODE_32BIT; - - nrfx_qspi_init(&config, NULL, NULL); - - // reset the flash - prv_write_register(SPI_NOR_CMD_ENRST, NULL, 0); - prv_write_register(SPI_NOR_CMD_RST, NULL, 0); - NRFX_DELAY_US(35); - - // enable 4 byte addressing - prv_write_register(SPI_NOR_CMD_EN4B, NULL, 0); - - // enable QE bit - prv_read_register(SPI_NOR_CMD_RDSR1, &sr[0], 1); - prv_read_register(SPI_NOR_CMD_RDSR2, &sr[1], 1); - sr[1] |= (1 << 1); - prv_write_register(SPI_NOR_CMD_WRSR, sr, 2); -} - -bool flash_sanity_check(void) { - uint8_t buf[3]; - uint32_t id; - - prv_read_register(SPI_NOR_CMD_RDID, buf, sizeof(buf)); - - id = buf[0] << 16 | buf[1] << 8 | buf[2]; - dbgserial_print("JEDEC ID: "); - dbgserial_print_hex(id); - dbgserial_newline(); - - return (id == BOARD_FLASH_JEDEC_ID); -} - -void flash_read_bytes(uint8_t *buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - uint8_t __attribute__((aligned(4))) b_buf[4]; - uint8_t buf_pre; - uint8_t buf_suf; - uint32_t buf_mid; - nrfx_err_t err; - - buf_pre = (4U - (uint8_t)((uint32_t)buffer_ptr % 4U)) % 4U; - if (buf_pre > buffer_size) { - buf_pre = buffer_size; - } - - buf_suf = (uint8_t)((buffer_size - buf_pre) % 4U); - buf_mid = buffer_size - buf_pre - buf_suf; - - if (buf_pre != 0U) { - err = nrfx_qspi_read(b_buf, 4U, start_addr); - PBL_ASSERTN(err == NRFX_SUCCESS); - - memcpy(buffer_ptr, b_buf, buf_pre); - start_addr += buf_pre; - buffer_ptr = ((uint8_t *)buffer_ptr) + buf_pre; - } - - if (buf_mid != 0U) { - err = nrfx_qspi_read(buffer_ptr, buf_mid, start_addr); - PBL_ASSERTN(err == NRFX_SUCCESS); - - start_addr += buf_mid; - buffer_ptr = ((uint8_t *)buffer_ptr) + buf_mid; - } - - if (buf_suf != 0U) { - err = nrfx_qspi_read(b_buf, 4U, start_addr); - PBL_ASSERTN(err == NRFX_SUCCESS); - - memcpy(buffer_ptr, b_buf, buf_suf); - } -} diff --git a/platform/asterix/boot/src/drivers/pmic.c b/platform/asterix/boot/src/drivers/pmic.c deleted file mode 100644 index 38648f8254..0000000000 --- a/platform/asterix/boot/src/drivers/pmic.c +++ /dev/null @@ -1,260 +0,0 @@ -#include -#include -#include - -#define SYSTEM_BASE 0x01U -#define VBUSIN_BASE 0x02U -#define CHARGER_BASE 0x03U -#define BUCK_BASE 0x04U -#define ADC_BASE 0x05U -#define GPIOS_BASE 0x06U -#define TIMER_BASE 0x07U -#define LDSW_BASE 0x08U -#define SHIP_BASE 0x0BU -#define ERRLOG_BASE 0x0EU - -// SYSTEM -#define TESTACCESS 0x23U -#define TESTACCESS_VAL0 0x44U -#define TESTACCESS_VAL1 0x90U -#define TESTACCESS_VAL2 0xFAU -#define TESTACCESS_VAL3 0xCEU - -// VBUSIN -#define VBUSINILIMSTARTUP 0x02U -#define VBUSLIM_500MA 0x00U - -// CHARGER -#define TASKRELEASEERROR 0x0U - -#define TASKCLEARCHGERR 0x1U - -#define BCHGENABLESET 0x04U -#define ENABLECHARGING_ENABLECHG 0x01U - -#define BCHGENABLECLR 0x05U -#define ENABLECHARGING_DISABLECHG 0x1U - -#define BCHGISETMSB 0x08U -#define BCHGISETLSB 0x09U - -#define BCHGISETDISCHARGEMSB 0x0AU - -#define BCHGVTERM 0x0CU -#define BCHGVTERMNORM_4V20 0x8U - -#define BCHGVTERMR 0x0DU -#define BCHGVTERMREDUCED_4V00 0x4U - -#define BCHGDEBUG 0x46U -#define BCHGDEBUG_DISABLEBATTERYDETECT 0x04U - -#define BCHGVBATLOWCHARGE 0x50U -#define BCHGVBATLOWCHARGE_ENABLEVBATLOWCHARGE 0x01U - -#define NTCCOLD 0x10U -#define NTCCOLDLSB 0x11U -#define NTCCOOL 0x12U -#define NTCCOOLLSB 0x13U -#define NTCWARM 0x14U -#define NTCWARMLSB 0x15U -#define NTCHOT 0x16U -#define NTCHOTLSB 0x17U - -// BUCK -#define BUCK2ENASET 0x2 - -#define BUCK1PWMCLR 0x5U -#define BUCK2PWMCLR 0x7U -#define BUCKPWMCLR_SET 0x01U - -#define BUCKENCTRL 0xC -#define BUCKVRETCTRL 0xD -#define BUCKPWMCTRL 0xE - -#define BUCKCTRL0 0x15U - -// ADC -#define ADCNTCRSEL 0x0AU -#define ADCNTCRSEL_10K 0x1U - -// GPIOS -#define GPIOMODE0 0x0U -#define GPIOMODE1 0x1U -#define GPIOMODE2 0x2U -#define GPIOMODE3 0x3U -#define GPIOMODE4 0x4U - -#define GPIOMODE_GPIINPUT 0U -#define GPIOMODE_GPIEVENTFALL 4U -#define GPIOMODE_GPOIRQ 5U - -#define GPIOPUEN0 0xAU -#define GPIOOPENDRAIN0 0x14U - -// TIMER -#define TIMERCLR 0x01U -#define TIMERCLR_TASKTIMERDIS 0x01U - -// LDO -#define TASKLDSW1SET 0x00U -#define TASKLDSW2SET 0x02U - -#define LDSW1GPISEL 0x05U -#define LDSW2GPISEL 0x06U - -#define LDSW1LDOSEL 0x08U -#define LDSW2LDOSEL 0x09U -#define LDSWLDOSEL_LDO 0x01U - -#define LDSW1VOUTSEL 0x0CU -#define LDSW2VOUTSEL 0x0DU -#define LDSWVOUTSEL_1V8 0x08U - -// SHIP -#define TASKSHPHLDCONFIGSTROBE 0x1U -#define LPRESETCFG 0x6U - -// ERRLOG -#define SCRATCH0 0x1U -#define SCRATCH0_BOOTTIMEREN 0x01U - -static const nrfx_twi_t twi = NRFX_TWI_INSTANCE(BOARD_PMIC_I2C); -static const nrfx_twi_config_t config = - NRFX_TWI_DEFAULT_CONFIG(BOARD_PMIC_I2C_SCL_PIN, BOARD_PMIC_I2C_SDA_PIN); - -static int prv_pmic_write(uint8_t base, uint8_t reg, uint8_t val) { - nrfx_err_t err; - uint8_t data[3]; - nrfx_twi_xfer_desc_t xfer = NRFX_TWI_XFER_DESC_TX(0x6b, data, sizeof(data)); - - data[0] = base; - data[1] = reg; - data[2] = val; - - err = nrfx_twi_xfer(&twi, &xfer, 0); - if (err != NRFX_SUCCESS) { - return -1; - } - - return 0; -} - -struct pmic_reg { - uint8_t base; - uint8_t reg; - uint8_t val; -}; - -int pmic_init(void) { - int ret; - nrfx_err_t err; - - err = nrfx_twi_init(&twi, &config, NULL, NULL); - if (err != NRFX_SUCCESS) { - return -1; - } - - nrfx_twi_enable(&twi); - - const struct pmic_reg regs[] = { - // Turn off any watchdog / boot timer right away. - { TIMER_BASE, TIMERCLR, TIMERCLR_TASKTIMERDIS }, - { ERRLOG_BASE, SCRATCH0, 0x00 /* contains boot timer bit */ }, - - // Make sure right away that we can reset the device if needed. - { SHIP_BASE, LPRESETCFG, 0 }, - { SHIP_BASE, TASKSHPHLDCONFIGSTROBE, 1 }, - - // NOTE: BUCK1/2 voltage is set by VSET resistors, and it should never be changed - // in software. Also, anomaly 27 would trigger if BUCKnSWCTRLSEL is set to SWCTRL - // while BUCKnNORMVOUT and BUCKnVOUTSTATUS are equal, which is our case. - - // Enable BUCK2 (powers LCD) just in case. - { BUCK_BASE, BUCK2ENASET, 1 }, - // Request automatic mode for BUCK1/2 - { BUCK_BASE, BUCK1PWMCLR, 1 }, - { BUCK_BASE, BUCK2PWMCLR, 1 }, - // Disable any type of control via GPIOs - { BUCK_BASE, BUCKENCTRL, 0 }, - { BUCK_BASE, BUCKVRETCTRL, 0 }, - { BUCK_BASE, BUCKPWMCTRL, 0 }, - // BUCK1/2 auto switching between PFM/PWM, no pull-downs - { BUCK_BASE, BUCKCTRL0, 0 }, - - // Configure charger (TODO: values are board/battery dependent) - // - Thermistor: 10K NTC - // - Termination voltage: 4.2V - // - Reduced termination voltage (for warm region): 4.00V - // - Charge current limit of 64 mA (standard charging, 0.5C) - // - Discharge current limit of 200 mA (increase current measurement accuracy) - // - Release charger from error state if applicable (but do not clear - // safety timers) -- this doesn't happen in a loop because after we - // fail to boot three times, we will sit at sadwatch until a button is - // pressed - // - Set COLD/COOL/WARM/HOT tresholds to 0/10/45/45 degrees Celsius - // - Disable battery detection (so that C0 hw behaves as D0, ie, no battery detection) - // This is a no-op on D0 hw. - // - Enable battery charging if voltage is low (recommended when using a battery with PCM) - // - Enable charging - { VBUSIN_BASE, VBUSINILIMSTARTUP, VBUSLIM_500MA }, // should be default, but 'reset value from OTP, value listed in this table may not be correct' - { CHARGER_BASE, BCHGENABLECLR, ENABLECHARGING_DISABLECHG }, - { ADC_BASE, ADCNTCRSEL, ADCNTCRSEL_10K }, - { CHARGER_BASE, BCHGVTERM, BCHGVTERMNORM_4V20 }, - { CHARGER_BASE, BCHGVTERMR, BCHGVTERMREDUCED_4V00 }, - { CHARGER_BASE, BCHGISETMSB, 16 }, - { CHARGER_BASE, BCHGISETLSB, 0 }, - { CHARGER_BASE, BCHGISETDISCHARGEMSB, 42 }, - { CHARGER_BASE, TASKCLEARCHGERR, 1 }, - { CHARGER_BASE, TASKRELEASEERROR, 1 }, - { CHARGER_BASE, NTCCOLD, 0xBBU }, - { CHARGER_BASE, NTCCOLDLSB, 0x01U }, - { CHARGER_BASE, NTCCOOL, 0xA4U }, - { CHARGER_BASE, NTCCOOLLSB, 0x02U }, - { CHARGER_BASE, NTCWARM, 0x54U }, - { CHARGER_BASE, NTCWARMLSB, 0x01U }, - { CHARGER_BASE, NTCHOT, 0x54U }, - { CHARGER_BASE, NTCHOTLSB, 0x01U }, - // Allow using some test registers on nPM1300 C0 hw: - // - BCHGDEBUG - // - BCHGVBATLOWCHARGE - { SYSTEM_BASE, TESTACCESS, TESTACCESS_VAL0 }, - { SYSTEM_BASE, TESTACCESS, TESTACCESS_VAL1 }, - { SYSTEM_BASE, TESTACCESS, TESTACCESS_VAL2 }, - { SYSTEM_BASE, TESTACCESS, TESTACCESS_VAL3 }, - { CHARGER_BASE, BCHGDEBUG, BCHGDEBUG_DISABLEBATTERYDETECT }, - { CHARGER_BASE, BCHGVBATLOWCHARGE, BCHGVBATLOWCHARGE_ENABLEVBATLOWCHARGE }, - { CHARGER_BASE, BCHGENABLESET, ENABLECHARGING_ENABLECHG }, - - // LDO1 as LDO @ 1.8V (powers the DA7212 ... do not back power it through I/O pins, and it must always be on because sensors share I2C bus with it!) - { LDSW_BASE, LDSW1GPISEL, 0 }, - { LDSW_BASE, LDSW1VOUTSEL, LDSWVOUTSEL_1V8 }, - { LDSW_BASE, LDSW1LDOSEL, LDSWLDOSEL_LDO }, - { LDSW_BASE, TASKLDSW1SET, 0x01U }, - - // LDO2 as LDO @ 1.8V (powers the QSPI flash) - { LDSW_BASE, LDSW2GPISEL, 0 }, - { LDSW_BASE, LDSW2VOUTSEL, LDSWVOUTSEL_1V8 }, - { LDSW_BASE, LDSW2LDOSEL, LDSWLDOSEL_LDO }, - { LDSW_BASE, TASKLDSW2SET, 0x01U }, - - // Firmware will set up GPIOs as desired; set up everything as an input - // now to avoid drive fights in case it was previously set strangely. - { GPIOS_BASE, GPIOMODE0, GPIOMODE_GPIINPUT }, - { GPIOS_BASE, GPIOMODE1, GPIOMODE_GPIINPUT }, - { GPIOS_BASE, GPIOMODE2, GPIOMODE_GPIINPUT }, - { GPIOS_BASE, GPIOMODE3, GPIOMODE_GPIINPUT }, - { GPIOS_BASE, GPIOMODE4, GPIOMODE_GPIINPUT }, - }; - - for (unsigned int i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) { - ret = prv_pmic_write(regs[i].base, regs[i].reg, regs[i].val); - if (ret != 0) { - return ret; - } - } - - nrfx_twi_disable(&twi); - - return 0; -} diff --git a/platform/asterix/boot/src/drivers/pmic.h b/platform/asterix/boot/src/drivers/pmic.h deleted file mode 100644 index 46fb2572a0..0000000000 --- a/platform/asterix/boot/src/drivers/pmic.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -int pmic_init(void); diff --git a/platform/asterix/boot/src/drivers/system_flash.c b/platform/asterix/boot/src/drivers/system_flash.c deleted file mode 100644 index b06e7be10f..0000000000 --- a/platform/asterix/boot/src/drivers/system_flash.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "drivers/system_flash.h" - -#include - -#include "drivers/dbgserial.h" -#include "drivers/watchdog.h" -#include "util/misc.h" - -#define FLASH_SECTOR_SIZE 0x1000 -#define FLASH_SECTOR_COUNT 256 - -static inline bool addr_is_word_aligned(uint32_t addr) { return (addr & 0x3u) == 0u; } - -int prv_get_sector_num_for_address(uint32_t address) { - if (address > FLASH_SECTOR_COUNT * FLASH_SECTOR_SIZE) { - dbgserial_print("address "); - dbgserial_print_hex(address); - dbgserial_putstr(" is outside system flash"); - return -1; - } - for (size_t i = 0; i < FLASH_SECTOR_COUNT - 1; ++i) { - if (FLASH_SECTOR_SIZE * i <= address && address < FLASH_SECTOR_SIZE * (i + 1)) { - return i; - } - } - return FLASH_SECTOR_COUNT - 1; -} - -bool system_flash_erase(uint32_t address, size_t length, SystemFlashProgressCb progress_callback, - void *progress_context) { - if (length == 0) { - // Nothing to do - return true; - } - - if (!addr_is_word_aligned(address)) { - dbgserial_putstr("system_flash_erase: address not word aligned"); - return false; - } - - int first_sector = prv_get_sector_num_for_address(address); - int last_sector = prv_get_sector_num_for_address(address + length - 1); - - if (first_sector < 0 || last_sector < 0) { - return false; - } - int count = last_sector - first_sector + 1; - if (progress_callback) { - progress_callback(0, count, progress_context); - } - - nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE); - for (int sector = first_sector; sector <= last_sector; ++sector) { - nrf_nvmc_page_erase_start(NRF_NVMC, sector * FLASH_SECTOR_SIZE); - while (!nrf_nvmc_ready_check(NRF_NVMC)) { - // Wait for the erase to complete - } - - watchdog_kick(); - - if (progress_callback) { - progress_callback(sector - first_sector + 1, count, progress_context); - } - } - nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_READONLY); - return true; -} - -bool system_flash_write(uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - uint32_t aligned; - uint8_t last; - - if (!addr_is_word_aligned(address)) { - dbgserial_putstr("system_flash_write: address not word aligned"); - return false; - } - - last = length % 4U; - aligned = length - last; - - nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_WRITE); - - const uint8_t *data_array = data; - for (uint32_t i = 0; i < aligned; i += 4) { - while (!nrf_nvmc_ready_check(NRF_NVMC)) { - // Wait for the write to be ready - } - - nrf_nvmc_word_write(address + i, *(uint32_t *)&data_array[i]); - - watchdog_kick(); - - if (progress_callback && i % 128 == 0) { - progress_callback(i / 128, aligned / 128, progress_context); - } - } - - if (last != 0U) { - uint32_t val = 0xFFFFFFFFU; - - while (!nrf_nvmc_ready_check(NRF_NVMC)) { - // Wait for the write to be ready - } - - memcpy(&val, &data_array[aligned], last); - nrf_nvmc_word_write(address + aligned, val); - } - - nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_READONLY); - - return true; -} diff --git a/platform/asterix/boot/src/drivers/system_flash.h b/platform/asterix/boot/src/drivers/system_flash.h deleted file mode 100644 index 95eeb81d4c..0000000000 --- a/platform/asterix/boot/src/drivers/system_flash.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef void (*SystemFlashProgressCb)( - uint32_t progress, uint32_t total, void *context); - -// Erase the sectors of flash which lie within the given address range. -// -// If the address range overlaps even one single byte of a sector, the entire -// sector is erased. -// -// If progress_callback is not NULL, it is called at the beginning of the erase -// process and after each sector is erased. The rational number (progress/total) -// increases monotonically as the sector erasue procedure progresses. -// -// Returns true if successful, false if an error occurred. -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); - -// Write data into flash. The flash must already be erased. -// -// If progress_callback is not NULL, it is called at the beginning of the -// writing process and periodically thereafter. The rational number -// (progress/total) increases monotonically as the data is written. -// -// Returns true if successful, false if an error occurred. -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); diff --git a/platform/asterix/boot/src/drivers/watchdog.c b/platform/asterix/boot/src/drivers/watchdog.c deleted file mode 100644 index f68d3fd9ee..0000000000 --- a/platform/asterix/boot/src/drivers/watchdog.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "drivers/watchdog.h" - -#include -#include - -#define WDT_INTERVAL_SECONDS 8 - -/* On nRF52840, the watchdog can only be disabled by a power-on reset. We - * don't really want the watchdog to be running during the bootloader: if - * the bootloader hangs, there is precious little we can do about it, and we - * don't want the watchdog to interrupt long-running operations like erasing - * microflash, or reading / writing QSPI flash. - * - * The upshot of this is that, even if we are running on a no-watchdog - * build, we must continually kick the watchdog, lest it bite, since the - * watchdog could have been configured from the previous boot! - */ - -void watchdog_init(void) { - /* Allow us to be debugged, but keep the WDT ticking when the CPU is - * asleep for normal reasons. This is the reset value, as well, but it's - * always good to be sure before we do anything that we can't take back. - */ - NRF_WDT->CONFIG = (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos) | - (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos); - NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos; - /* WDT expiration: 8s */ - NRF_WDT->CRV = 32768 * WDT_INTERVAL_SECONDS; - NRF_WDT->TASKS_START = 1; - // NOTE: at this point WDT can no longer be stopped, it will even survive - // a system reset! -} - -void watchdog_kick(void) { - if (NRF_WDT->RUNSTATUS) { - // In theory, only RR0 should be enabled. But in case someone else has - // enabled other RRs out from under us, we had better kick all of them. - for (int i = 0; i < 8; i++) { - if (NRF_WDT->RREN & (1 << i)) { - NRF_WDT->RR[i] = WDT_RR_RR_Reload; - } - } - } -} - -bool watchdog_check_clear_reset_flag(void) { - uint32_t reason = nrfx_reset_reason_get(); - nrfx_reset_reason_clear(0xFFFFFFFF); - return (reason & NRFX_RESET_REASON_DOG_MASK) != 0; -} diff --git a/platform/asterix/boot/src/drivers/watchdog.h b/platform/asterix/boot/src/drivers/watchdog.h deleted file mode 100644 index 367bb78f75..0000000000 --- a/platform/asterix/boot/src/drivers/watchdog.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -void watchdog_init(void); -void watchdog_kick(void); - -bool watchdog_check_clear_reset_flag(void); diff --git a/platform/asterix/boot/src/firmware.h b/platform/asterix/boot/src/firmware.h deleted file mode 100644 index 914920be19..0000000000 --- a/platform/asterix/boot/src/firmware.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -extern char __fw_start[0x1000]; -#define FIRMWARE_BASE ((uint32_t)(__fw_start)) diff --git a/platform/asterix/boot/src/flash_region.h b/platform/asterix/boot/src/flash_region.h deleted file mode 100644 index c80b9bb802..0000000000 --- a/platform/asterix/boot/src/flash_region.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// Scratch space for firmware images (normal and recovery). -// We assume this is 64k aligned... -#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN 0x0100000 -#define FLASH_REGION_FIRMWARE_SLOT_1_END 0x01FFFFF // 1024k - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x0000000 -#define FLASH_REGION_SAFE_FIRMWARE_END 0x007FFFF // 512k diff --git a/platform/asterix/boot/src/fw_copy.c b/platform/asterix/boot/src/fw_copy.c deleted file mode 100644 index aa0254294e..0000000000 --- a/platform/asterix/boot/src/fw_copy.c +++ /dev/null @@ -1,212 +0,0 @@ -#include "fw_copy.h" - -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/system_flash.h" -#include "firmware.h" -#include "flash_region.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/reset.h" -#include "util/crc32.h" -#include "util/misc.h" -#include "util/delay.h" - -#include -#include - -#define MAX_CHUNK_SIZE 65536 - -static bool check_valid_firmware_crc(uint32_t flash_address, FirmwareDescription* desc) { - dbgserial_putstr("Checksumming firmware update"); - const uint32_t crc = flash_calculate_checksum(flash_address, desc->firmware_length); - dbgserial_print("Calculated checksum: "); - dbgserial_print_hex(crc); - dbgserial_newline(); - return crc == desc->checksum; -} - -static void prv_display_erase_progress( - uint32_t progress, uint32_t total, void *ctx) { - (void)ctx; - display_firmware_update_progress(progress, total * 2); -} - -static bool erase_old_firmware(uint32_t firmware_length) { - dbgserial_putstr("erase_old_firmware"); - return system_flash_erase( - FIRMWARE_BASE, firmware_length, prv_display_erase_progress, 0); -} - -static void prv_display_write_progress( - uint32_t progress, uint32_t total, void *ctx) { - (void)ctx; - display_firmware_update_progress(progress/2 + total/2, total); -} - -static bool write_new_firmware(uint32_t flash_new_fw_start, - uint32_t firmware_length) { - dbgserial_putstr("write_new_firmware"); - // We can't just read the flash like memory, so we gotta lift everything ourselves. - // buffer is static so it goes in BSS, since stack is only 8192 bytes - static uint8_t buffer[MAX_CHUNK_SIZE]; - uint32_t chunk_size; - for (uint32_t i = 0; i < firmware_length; i += chunk_size) { - chunk_size = MIN(MAX_CHUNK_SIZE, firmware_length - i); - flash_read_bytes(buffer, flash_new_fw_start + i, chunk_size); - if (!system_flash_write(FIRMWARE_BASE + i, buffer, chunk_size, NULL, NULL)) { - dbgserial_putstr("We're dead"); - return false; - } - prv_display_write_progress(i + chunk_size, firmware_length, NULL); - } - return true; -} - -static bool check_firmware_crc(FirmwareDescription* firmware_description) { - dbgserial_print("Checksumming "); - dbgserial_print_hex(firmware_description->firmware_length); - dbgserial_print(" bytes\r\n"); - - const uint32_t crc = crc32(CRC32_INIT, (const uint8_t *)FIRMWARE_BASE, - firmware_description->firmware_length); - - dbgserial_print("Checksum - wanted "); - dbgserial_print_hex(firmware_description->checksum); - dbgserial_print(" got "); - dbgserial_print_hex(crc); - dbgserial_newline(); - - return crc == firmware_description->checksum; -} - -typedef enum UpdateFirmwareResult { - UPDATE_FW_SUCCESS = 0, - UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED = 1, - UPDATE_FW_ERROR_MICRO_FLASH_MANGLED = 2 -} UpdateFirmwareResult; - -static UpdateFirmwareResult update_fw(uint32_t flash_address) { - display_firmware_update_progress(0, 1); - boot_bit_set(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - - FirmwareDescription firmware_description = - firmware_storage_read_firmware_description(flash_address); - - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - dbgserial_putstr("Invalid firmware description!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - if (!check_valid_firmware_crc( - flash_address + sizeof(FirmwareDescription), &firmware_description)) { - dbgserial_putstr("Invalid firmware CRC in SPI flash!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - erase_old_firmware(firmware_description.firmware_length); - - write_new_firmware( - flash_address + sizeof(FirmwareDescription), - firmware_description.firmware_length); - - if (!check_firmware_crc(&firmware_description)) { - dbgserial_putstr( - "Our internal flash contents are bad (checksum failed)! " - "This is really bad!"); - return UPDATE_FW_ERROR_MICRO_FLASH_MANGLED; - } - - return UPDATE_FW_SUCCESS; -} - -void check_update_fw(void) { - if (!boot_bit_test(BOOT_BIT_NEW_FW_AVAILABLE)) { - return; - } - - if (boot_bit_test(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS)) { - dbgserial_putstr("Our previous firmware update failed, aborting update."); - - // Pretend like the new firmware bit wasn't set afterall. We'll just run the - // previous code, whether that was normal firmware or the recovery firmware. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_clear(BOOT_BIT_NEW_FW_INSTALLED); - return; - } - - - dbgserial_putstr("New firmware is available!"); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - - UpdateFirmwareResult result = update_fw(FLASH_REGION_FIRMWARE_SLOT_1_BEGIN); - switch (result) { - case UPDATE_FW_SUCCESS: - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - // Our firmware update failed in a way that didn't break our previous - // firmware. Just run the previous code, whether that was normal firmware - // or the recovery firmware. - break; - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // We've broken our internal flash when trying to update our normal - // firmware. Fall back immediately to the recovery firmare. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - system_reset(); - return; - } - - // Done, we're ready to boot. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_set(BOOT_BIT_NEW_FW_INSTALLED); -} - -bool switch_to_recovery_fw() { - dbgserial_putstr("Loading recovery firmware"); - - UpdateFirmwareResult result = update_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - bool recovery_fw_ok = true; - switch (result) { - case UPDATE_FW_SUCCESS: - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // Keep us booting into recovery firmware. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - - if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to load recovery firmware, strike one. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO)) { - dbgserial_putstr("Failed to load recovery firmware, strike two. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else { - dbgserial_putstr("Failed to load recovery firmware, strike three. SAD WATCH"); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - recovery_fw_ok = false; - } - break; - } - - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - return recovery_fw_ok; -} diff --git a/platform/asterix/boot/src/fw_copy.h b/platform/asterix/boot/src/fw_copy.h deleted file mode 100644 index 782e2c6f93..0000000000 --- a/platform/asterix/boot/src/fw_copy.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -void check_update_fw(void); - -//! @return false if we've failed to load recovery mode too many times and we should just sadwatch -bool switch_to_recovery_fw(void); diff --git a/platform/asterix/boot/src/git_version.auto.h.in b/platform/asterix/boot/src/git_version.auto.h.in deleted file mode 100644 index f5a14b1a0a..0000000000 --- a/platform/asterix/boot/src/git_version.auto.h.in +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#define GIT_TIMESTAMP @TIMESTAMP@ -#define GIT_TAG "@TAG@" -#define GIT_MAJOR_VERSION @MAJOR_VERSION@ -#define GIT_MINOR_VERSION @MINOR_VERSION@ -#define GIT_PATCH_VERSION @PATCH_VERSION@ -#define GIT_REVISION "@COMMIT@" diff --git a/platform/asterix/boot/src/hardfault_handler.c b/platform/asterix/boot/src/hardfault_handler.c deleted file mode 100644 index 4a3d8b4d51..0000000000 --- a/platform/asterix/boot/src/hardfault_handler.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "drivers/dbgserial.h" -#include "system/die.h" -#include "system/reset.h" - -static void prv_hard_fault_handler_c(unsigned int *hardfault_args) { - (void)hardfault_args; - dbgserial_putstr("HARD FAULT"); - -#ifdef NO_WATCHDOG - reset_due_to_software_failure(); -#else - system_hard_reset(); -#endif -} - -void HardFault_Handler(void) { - // Grab the stack pointer, shove it into a register and call - // the c function above. - __asm("tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "b %0\n" :: "i" (prv_hard_fault_handler_c)); -} diff --git a/platform/asterix/boot/src/linker.ld b/platform/asterix/boot/src/linker.ld deleted file mode 100644 index 38e8460f13..0000000000 --- a/platform/asterix/boot/src/linker.ld +++ /dev/null @@ -1,25 +0,0 @@ -MEMORY -{ - /* Retained, 256 bytes */ - RETAINED (rw): ORIGIN = 0x20000000, LENGTH = 256 - - FLASH (rx) : ORIGIN = 0x00000000, LENGTH =32K - FLASH_FW (rx) : ORIGIN = 0x00008000, LENGTH = 992K - RAM (rw) : ORIGIN = 0x20000100, LENGTH = 0x3ff00 -} - -SECTIONS -{ - .retained : ALIGN(4) { - __retained_start = .; - *(.retained) - __retained_end = .; - } > RETAINED - - .flash_fw : ALIGN(4) { - __fw_start = .; - } > FLASH_FW -} - -INCLUDE "../../../../third_party/hal_nordic/nrfx/mdk/nrf_common.ld" - diff --git a/platform/asterix/boot/src/main.c b/platform/asterix/boot/src/main.c deleted file mode 100644 index a6c842e78a..0000000000 --- a/platform/asterix/boot/src/main.c +++ /dev/null @@ -1,358 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const uint8_t SELECT_BUTTON_MASK = 0x4; - -static void prv_get_fw_reset_vector(void **reset_handler, void **initial_stack_pointer) { - uintptr_t **fw_vector_table = (uintptr_t **)FIRMWARE_BASE; // Defined in wscript - *initial_stack_pointer = (void *)fw_vector_table[0]; - *reset_handler = (void *)fw_vector_table[1]; -} - -static void __attribute__((noreturn)) jump_to_fw(void) { - void *initial_stack_pointer, *reset_handler; - prv_get_fw_reset_vector(&reset_handler, &initial_stack_pointer); - - dbgserial_print("Booting firmware @ "); - dbgserial_print_hex((uintptr_t)reset_handler); - dbgserial_print("...\r\n\r\n"); - - // Give it one last kick to give user firmware a known amount of time to - // start up. - watchdog_kick(); - - // The Cortex-M user guide states that the reset values for the core registers - // are as follows: - // R0-R12 = Unknown - // MSP = VECTOR_TABLE[0] (main stack pointer) - // PSP = Unknown (process stack pointer) - // LR = 0xFFFFFFFF - // PC = VECTOR_TABLE[1] - // PRIMASK = 0x0 - // FAULTMASK = 0x0 - // BASEPRI = 0x0 - // CONTROL = 0x0 - // - // Attempt to put the processor into as close to the reset state as possible - // before passing control to the firmware. - // - // No attempt is made to set CONTROL to zero as it should already be set to - // the reset value when this code executes. - __asm volatile( - "cpsie if\n" // Clear PRIMASK and FAULTMASK - "mov lr, 0xFFFFFFFF\n" - "mov sp, %[initial_sp]\n" - "bx %[reset_handler]\n" - : - : [initial_sp] "r"(initial_stack_pointer), [reset_handler] "r"(reset_handler)); - __builtin_unreachable(); -} - -static bool check_and_increment_reset_loop_detection_bits(void) { - uint8_t counter = (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_THREE) << 2) | - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_TWO) << 1) | - boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_ONE); - - if (counter == 7) { - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE); - return true; - } - - switch (++counter) { - case 1: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 2: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 3: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 4: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_THREE); - break; - case 5: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 6: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 7: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - default: - PBL_CROAK("reset loop boot bits overrun"); - break; - } - return false; -} - -static bool check_for_recovery_start_failure() { - return boot_bit_test(BOOT_BIT_RECOVERY_START_IN_PROGRESS); -} - -static bool check_for_fw_start_failure() { - bool watchdog_reset = watchdog_check_clear_reset_flag(); - - // Add more failure conditions here. - if (!watchdog_reset && !boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - // We're good, we're just starting normally. - PBL_LOG_VERBOSE("We're good, we're just starting normally."); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return false; - } - - // We failed to start our firmware successfully! - if (watchdog_reset) { - dbgserial_putstr("Watchdog caused a reset"); - } - if (boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - dbgserial_putstr("Software failure caused a reset"); - } - - // Clean up after the last failure. - boot_bit_clear(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - - // We have a "three strikes" algorithm: if the watch fails three times, return true - // to tell the parent we should load the recovery firmware. A reset for any other reason - // will reset this algorithm. - if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) { - // Yikes, our firmware is screwed. Boot into recovery mode. - dbgserial_putstr("Failed to start firmware, strike three."); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return true; - } else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to start firmware, strike two."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - } else { - dbgserial_putstr("Failed to start firmware, strike one."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - } - - return false; -} - -static bool prv_prf_button_combination_is_pressed(void) { - return (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK) && - button_is_pressed(BUTTON_ID_SELECT) && !button_is_pressed(BUTTON_ID_DOWN)); -} - -static bool check_force_boot_recovery(void) { - if (boot_bit_test(BOOT_BIT_FORCE_PRF)) { - boot_bit_clear(BOOT_BIT_FORCE_PRF); - return true; - } - - if (prv_prf_button_combination_is_pressed()) { - dbgserial_putstr("Hold down UP + BACK + SELECT for 5 secs. to force-boot PRF"); - for (int i = 0; i < 5000; ++i) { - if (!prv_prf_button_combination_is_pressed()) { - // stop waiting if not held down any longer - return false; - } - watchdog_kick(); - delay_ms(1); - } - - return true; - } - - void *reset_vector, *initial_sp; - prv_get_fw_reset_vector(&reset_vector, &initial_sp); - if ((uintptr_t)reset_vector == 0xffffffff || (uintptr_t)initial_sp == 0xffffffff) { - dbgserial_putstr("Firmware is erased"); - return true; - } - return false; -} - -static void sad_watch(uint32_t error_code) { - dbgserial_putstr("SAD WATCH"); - - char error_code_buffer[12]; - itoa_hex(error_code, error_code_buffer, sizeof(error_code_buffer)); - dbgserial_putstr(error_code_buffer); - - display_error_code(error_code); - - static uint8_t prev_button_state = 0; - prev_button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - while (1) { - // See if we should restart - uint8_t button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - if (button_state != prev_button_state) { - system_reset(); - } - watchdog_kick(); - delay_ms(10); - } -} - -int main(void) { - int ret; - - watchdog_kick(); - - dbgserial_init(); - - dbgserial_putstr(""); - dbgserial_putstr(" _ _ _ "); - dbgserial_putstr(" /_\\ __| |_ ___ _ _(_)_ __"); - dbgserial_putstr(" / _ \\ (_-< _/ -_) '_| \\ \\ /"); - dbgserial_putstr("/_/ \\_\\/__/\\__\\___|_| |_/_\\_\\"); - dbgserial_putstr(""); - - boot_bit_init(); - - dbgserial_putstr("boot bit"); - - boot_version_write(); - - // Write the bootloader version to serial-out - { - char bootloader_version_str[12]; - memset(bootloader_version_str, 0, 12); - itoa_hex(boot_version_read(), bootloader_version_str, 12); - dbgserial_putstr(bootloader_version_str); - } - dbgserial_putstr(""); - dbgserial_putstr(""); - - if (boot_bit_test(BOOT_BIT_FW_STABLE)) { - dbgserial_putstr("Last firmware boot was stable; clear strikes"); - - boot_bit_clear(BOOT_BIT_FW_STABLE); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - } - - ret = pmic_init(); - if (ret != 0) { - dbgserial_putstr("PMIC init failed"); - sad_watch(ERROR_PMIC_INIT); - } - - flash_init(); - button_init(); - display_init(); - display_boot_splash(); - -#ifdef DISPLAY_DEMO_LOOP - while (1) { - for (int i = 0; i < 92; ++i) { - display_firmware_update_progress(i, 91); - delay_us(80000); - } - - for (uint32_t i = 0; i <= 0xf; ++i) { - display_error_code(i * 0x11111111); - delay_us(200000); - } - for (uint32_t i = 0; i < 8; ++i) { - for (uint32_t j = 1; j <= 0xf; ++j) { - display_error_code(j << (i * 4)); - delay_us(200000); - } - } - display_error_code(0x01234567); - delay_us(200000); - display_error_code(0x89abcdef); - delay_us(200000); - display_error_code(0xcafebabe); - delay_us(200000); - display_error_code(0xfeedface); - delay_us(200000); - display_error_code(0x8badf00d); - delay_us(200000); - display_error_code(0xbad1ce40); - delay_us(200000); - display_error_code(0xbeefcace); - delay_us(200000); - display_error_code(0x0defaced); - delay_us(200000); - display_error_code(0xd15ea5e5); - delay_us(200000); - display_error_code(0xdeadbeef); - delay_us(200000); - display_boot_splash(); - delay_us(1000000); - } -#endif - - if (is_button_stuck()) { - dbgserial_putstr("Stuck button"); - sad_watch(ERROR_STUCK_BUTTON); - } - - if (is_flash_broken()) { - dbgserial_putstr("Broken flash"); - sad_watch(ERROR_BAD_SPI_FLASH); - } - - boot_bit_dump(); - - // If the recovery firmware crashed at start-up, the watch is now a - // $150 brick. That's life! - if (check_for_recovery_start_failure()) { - boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - sad_watch(ERROR_CANT_LOAD_FW); - } - - bool force_boot_recovery_mode = check_force_boot_recovery(); - if (force_boot_recovery_mode) { - dbgserial_putstr("Force-booting recovery mode..."); - } - - if (force_boot_recovery_mode || check_for_fw_start_failure()) { - if (!switch_to_recovery_fw()) { - // We've failed to load recovery mode too many times. - sad_watch(ERROR_CANT_LOAD_FW); - } - } else { - check_update_fw(); - } - - if (check_and_increment_reset_loop_detection_bits()) { - sad_watch(ERROR_RESET_LOOP); - } - - display_deinit(); - -#ifndef NO_WATCHDOG - watchdog_init(); -#endif - - jump_to_fw(); - - return 0; -} - -// Stubs for libg_s.a, which is our libc implementation from nano-newlib -void _exit(int status) {} diff --git a/platform/asterix/boot/src/pebble_errors.h b/platform/asterix/boot/src/pebble_errors.h deleted file mode 100644 index 2b4cf150db..0000000000 --- a/platform/asterix/boot/src/pebble_errors.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -static const uint32_t ERROR_NO_ACTIVE_ERROR = 0; - -// FW Errors -static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501; -static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502; -static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503; -static const uint32_t ERROR_RESET_LOOP = 0xfe504504; -static const uint32_t ERROR_PMIC_INIT = 0xfe504505; - -// BT Errors -static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510; -static const uint32_t ERROR_CANT_START_LSE = 0xfe504511; -static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512; - -static const uint32_t ERROR_LOW_BATTERY = 0xfe504520; diff --git a/platform/asterix/boot/src/stubs.c b/platform/asterix/boot/src/stubs.c deleted file mode 100644 index c613223d61..0000000000 --- a/platform/asterix/boot/src/stubs.c +++ /dev/null @@ -1,46 +0,0 @@ -/* ref: https://sourceware.org/newlib/libc.html#Stubs */ - -#include -#include - -void _close_r(void) -{ -} -void _lseek_r(void) -{ -} -void _read_r(void) -{ -} -void _write_r(void) -{ -} - -int _fstat(int file, struct stat *st) -{ - (void)file; - - st->st_mode = S_IFCHR; - return 0; -} - -int _isatty(int file) -{ - (void)file; - - return 1; -} - -int _getpid(void) -{ - return 1; -} - -int _kill(int pid, int sig) -{ - (void)pid; - (void)sig; - - errno = EINVAL; - return -1; -} \ No newline at end of file diff --git a/platform/asterix/boot/src/system/bootbits.c b/platform/asterix/boot/src/system/bootbits.c deleted file mode 100644 index a075697802..0000000000 --- a/platform/asterix/boot/src/system/bootbits.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "drivers/dbgserial.h" -#include "system/bootbits.h" -#include "system/retained.h" - -#include - -#include -#include - -#include - -static const uint32_t s_bootloader_timestamp = GIT_TIMESTAMP; - -void boot_bit_init(void) { - /* FIXME: compute region to be enabled based on __retained_start (or use nrfx helpers) */ - NRF_POWER->RAM[0].POWERSET |= POWER_RAM_POWER_S2RETENTION_On << POWER_RAM_POWER_S2RETENTION_Pos; - - retained_init(); - - if (!boot_bit_test(BOOT_BIT_INITIALIZED)) { - retained_write(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED); - } -} - -void boot_bit_set(BootBitValue bit) { - uint32_t current_value = retained_read(RTC_BKP_BOOTBIT_DR); - current_value |= bit; - retained_write(RTC_BKP_BOOTBIT_DR, current_value); -} - -void boot_bit_clear(BootBitValue bit) { - uint32_t current_value = retained_read(RTC_BKP_BOOTBIT_DR); - current_value &= ~bit; - retained_write(RTC_BKP_BOOTBIT_DR, current_value); -} - -bool boot_bit_test(BootBitValue bit) { - uint32_t current_value = retained_read(RTC_BKP_BOOTBIT_DR); - return (current_value & bit); -} - -void boot_bit_dump(void) { - dbgserial_print("Boot bits: "); - dbgserial_print_hex(retained_read(RTC_BKP_BOOTBIT_DR)); - dbgserial_newline(); -} - -void boot_version_write(void) { - if (boot_version_read() == s_bootloader_timestamp) { - return; - } - retained_write(BOOTLOADER_VERSION_REGISTER, s_bootloader_timestamp); -} - -uint32_t boot_version_read(void) { - return retained_read(BOOTLOADER_VERSION_REGISTER); -} diff --git a/platform/asterix/boot/src/system/bootbits.h b/platform/asterix/boot/src/system/bootbits.h deleted file mode 100644 index c123a58701..0000000000 --- a/platform/asterix/boot/src/system/bootbits.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -typedef enum BootBitValue { - BOOT_BIT_INITIALIZED = 0x1 << 0, - BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1, - BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2, - BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3, - BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6, - BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7, - BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, //!< Bootloader enter standby immediately after reset. - BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9, - BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10, - BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11, - BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12, - BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13, - BOOT_BIT_FW_STABLE = 0x1 << 14, - BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15, - BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16, - BOOT_BIT_FORCE_PRF = 0x1 << 17, - BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18, - BOOT_BIT_SHUTDOWN_REQUESTED = 0x1 << 19, //!< Bootloader hard power-off instead of jumping to fw. -} BootBitValue; - -void boot_bit_init(); -void boot_bit_set(BootBitValue bit); -void boot_bit_clear(BootBitValue bit); -bool boot_bit_test(BootBitValue bit); - -// Dump the contents through dbgserial -void boot_bit_dump(void); - -void boot_version_write(void); -uint32_t boot_version_read(void); diff --git a/platform/asterix/boot/src/system/die.c b/platform/asterix/boot/src/system/die.c deleted file mode 100644 index 987b0d8a7a..0000000000 --- a/platform/asterix/boot/src/system/die.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "drivers/dbgserial.h" -#include "system/reset.h" -#include "system/passert.h" - -void reset_due_to_software_failure(void) { -#if defined(NO_WATCHDOG) - // Don't reset right away, leave it in a state we can inspect - - while (1) { - BREAKPOINT; - } -#endif - - dbgserial_putstr("Software failure; resetting!"); - system_reset(); -} diff --git a/platform/asterix/boot/src/system/die.h b/platform/asterix/boot/src/system/die.h deleted file mode 100644 index a8480e8c3c..0000000000 --- a/platform/asterix/boot/src/system/die.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -//! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were -//! able shut everything down nicely before rebooting. -void reset_due_to_software_failure(void) __attribute__((noreturn)); diff --git a/platform/asterix/boot/src/system/firmware_storage.c b/platform/asterix/boot/src/system/firmware_storage.c deleted file mode 100644 index d29c5a2e10..0000000000 --- a/platform/asterix/boot/src/system/firmware_storage.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "firmware_storage.h" - -#include "drivers/flash.h" -#include - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { - FirmwareDescription firmware_description; - flash_read_bytes((uint8_t*)&firmware_description, firmware_start_address, - sizeof(FirmwareDescription)); - dbgserial_print("Firmware length: "); - dbgserial_print_hex(firmware_description.firmware_length); - dbgserial_newline(); - dbgserial_print("Checksum: "); - dbgserial_print_hex(firmware_description.checksum); - dbgserial_newline(); - return firmware_description; -} - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc) { - return desc->description_length == sizeof(FirmwareDescription); -} diff --git a/platform/asterix/boot/src/system/firmware_storage.h b/platform/asterix/boot/src/system/firmware_storage.h deleted file mode 100644 index 1518a3189a..0000000000 --- a/platform/asterix/boot/src/system/firmware_storage.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -//! @file firmware_storage.h -//! Utilities for reading a firmware image stored in flash. - -#include -#include - -typedef struct __attribute__((__packed__)) FirmwareDescription { - uint32_t description_length; - uint32_t firmware_length; - uint32_t checksum; -} FirmwareDescription; - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address); - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc); diff --git a/platform/asterix/boot/src/system/logging.h b/platform/asterix/boot/src/system/logging.h deleted file mode 100644 index dbd6953151..0000000000 --- a/platform/asterix/boot/src/system/logging.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "drivers/dbgserial.h" -#include "system/die.h" - -#include -#include -#include -#include -#include - -#ifndef __FILE_NAME__ -#define __FILE_NAME__ __FILE__ -#endif - -#define LOG_LEVEL_ALWAYS 0 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_WARNING 50 -#define LOG_LEVEL_INFO 100 -#define LOG_LEVEL_DEBUG 200 -#define LOG_LEVEL_DEBUG_VERBOSE 255 - -#ifndef STRINGIFY - #define STRINGIFY_NX(a) #a - #define STRINGIFY(a) STRINGIFY_NX(a) -#endif // STRINGIFY - -#define STATUS_STRING(s) STRINGIFY(s) - -#ifdef PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) \ - do { \ - char _pbl_log_buffer[128]; \ - dbgserial_putstr_fmt(_pbl_log_buffer, sizeof(_pbl_log_buffer), \ - __FILE_NAME__ ":" STRINGIFY(__LINE__) "> " fmt, ## args); \ - } while (0) - - #ifdef VERBOSE_LOGGING - - #define PBL_LOG_VERBOSE(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) - - #else // VERBOSE_LOGGING - #define PBL_LOG_VERBOSE(fmt, args...) - #endif // VERBOSE_LOGGING - -#else // PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) - #define PBL_LOG_VERBOSE(fmt, args...) -#endif // PBL_LOG_ENABLED diff --git a/platform/asterix/boot/src/system/passert.c b/platform/asterix/boot/src/system/passert.c deleted file mode 100644 index 0ea8328947..0000000000 --- a/platform/asterix/boot/src/system/passert.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "passert.h" - -#include "system/die.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#include -#include -#include -#include -#include - -static __attribute__((noreturn)) void handle_passert_failed_vargs(const char* filename, int line_number, - uintptr_t lr, const char* expr, const char* fmt, va_list fmt_args) { - (void)lr; - (void)fmt_args; - - dbgserial_print("ASSERT: "); - dbgserial_print(expr); - dbgserial_print(" "); - dbgserial_print(filename); - dbgserial_print(":"); - dbgserial_print_hex(line_number); - if (fmt) { - dbgserial_print(" "); - dbgserial_print(fmt); - } - dbgserial_putstr(""); - - reset_due_to_software_failure(); -} - -static __attribute__((noreturn)) void handle_passert_failed(const char* filename, int line_number, - uintptr_t lr, const char *expr, const char* fmt, ...) { - va_list fmt_args; - va_start(fmt_args, fmt); - - handle_passert_failed_vargs(filename, line_number, lr, expr, fmt, fmt_args); - - va_end(fmt_args); -} - -void passert_failed(const char* filename, int line_number, const char* message, ...) { - va_list fmt_args; - va_start(fmt_args, message); - - handle_passert_failed_vargs(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERT", message, fmt_args); - - va_end(fmt_args); -} - -void passert_failed_no_message(const char* filename, int line_number) { - handle_passert_failed(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERTN", NULL); -} - -void wtf(void) { - uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); - dbgserial_print("*** WTF "); - dbgserial_print_hex(saved_lr); - dbgserial_putstr(""); - reset_due_to_software_failure(); -} - -//! Assert function called by the STM peripheral library's -//! 'assert_param' method. See stm32f2xx_conf.h for more information. -void assert_failed(uint8_t* file, uint32_t line) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - - handle_passert_failed((const char*) file, line, saved_lr, "STM32", "STM32 peripheral library tripped an assert"); -} - -extern void command_dump_malloc_kernel(void); - -void croak_oom(const char *filename, int line_number, const char *fmt, ...) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - -#ifdef MALLOC_INSTRUMENTATION - command_dump_malloc_kernel(); -#endif - - va_list fmt_args; - va_start(fmt_args, fmt); - - handle_passert_failed_vargs(filename, line_number, saved_lr, "CROAK OOM", fmt, fmt_args); - - va_end(fmt_args); -} diff --git a/platform/asterix/boot/src/system/passert.h b/platform/asterix/boot/src/system/passert.h deleted file mode 100644 index 486235da46..0000000000 --- a/platform/asterix/boot/src/system/passert.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "logging.h" - - -void passert_failed(const char* filename, int line_number, const char* message, ...) - __attribute__((noreturn)); - -#define PBL_ASSERT(expr, ...) \ - do { \ - if (!(expr)) { \ - passert_failed(__FILE_NAME__, __LINE__, __VA_ARGS__); \ - } \ - } while (0) - -#define PBL_ASSERTN(expr) \ - do { \ - if (!(expr)) { \ - passert_failed_no_message(__FILE_NAME__, __LINE__); \ - } \ - } while (0) - -void passert_failed_no_message(const char* filename, int line_number) - __attribute__((noreturn)); - -void wtf(void) __attribute__((noreturn)); - -#define WTF wtf() - -// Insert a compiled-in breakpoint -#define BREAKPOINT __asm("bkpt") - -#define PBL_ASSERT_PRIVILEGED() -#define PBL_ASSERT_TASK(task) - -#define PBL_CROAK(fmt, args...) \ - passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args) diff --git a/platform/asterix/boot/src/system/reset.c b/platform/asterix/boot/src/system/reset.c deleted file mode 100644 index 5ad5f2e84f..0000000000 --- a/platform/asterix/boot/src/system/reset.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "system/reset.h" - -#include "drivers/display.h" - -#include - -void system_reset(void) { - display_prepare_for_reset(); - system_hard_reset(); -} - -void system_hard_reset(void) { - NVIC_SystemReset(); - __builtin_unreachable(); -} diff --git a/platform/asterix/boot/src/system/reset.h b/platform/asterix/boot/src/system/reset.h deleted file mode 100644 index d4004280ab..0000000000 --- a/platform/asterix/boot/src/system/reset.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -//! Reset nicely after shutting down system services. Does not set the reboot_reason other than -//! calling reboot_reason_set_restarted_safely just before the reset occurs. -void system_reset(void)__attribute__((noreturn)); - -//! The final stage in the reset process. -void system_hard_reset(void) __attribute__((noreturn)); diff --git a/platform/asterix/boot/src/system/retained.c b/platform/asterix/boot/src/system/retained.c deleted file mode 100644 index 02939ad3da..0000000000 --- a/platform/asterix/boot/src/system/retained.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "drivers/dbgserial.h" - -#include "retained.h" -#include "util/crc32.h" - -static uint32_t __attribute__((section(".retained"))) retained[256 / 4]; - -void retained_init() -{ - uint32_t crc32_computed = crc32(0, retained, NRF_RETAINED_REGISTER_CRC * 4); - if (crc32_computed != retained[NRF_RETAINED_REGISTER_CRC]) { - dbgserial_print("Retained register CRC failed: expected CRC "); - dbgserial_print_hex(crc32_computed); - dbgserial_print(", got CRC "); - dbgserial_print_hex(retained[NRF_RETAINED_REGISTER_CRC]); - dbgserial_print(". Clearing bootbits!"); - dbgserial_newline(); - memset(retained, 0, sizeof(retained)); - } -} - -void retained_write(uint8_t id, uint32_t value) -{ - retained[id] = value; - uint32_t crc32_computed = crc32(0, retained, NRF_RETAINED_REGISTER_CRC * 4); - retained[NRF_RETAINED_REGISTER_CRC] = crc32_computed; -} - -uint32_t retained_read(uint8_t id) -{ - return retained[id]; -} diff --git a/platform/asterix/boot/src/system/retained.h b/platform/asterix/boot/src/system/retained.h deleted file mode 100644 index c50a5d5fe0..0000000000 --- a/platform/asterix/boot/src/system/retained.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -#define RTC_BKP_BOOTBIT_DR 0 -#define STUCK_BUTTON_REGISTER 1 -#define BOOTLOADER_VERSION_REGISTER 2 -#define CURRENT_TIME_REGISTER 3 -#define CURRENT_INTERVAL_TICKS_REGISTER 4 -#define REBOOT_REASON_REGISTER_1 5 -#define REBOOT_REASON_REGISTER_2 6 -#define REBOOT_REASON_STUCK_TASK_PC 7 -#define REBOOT_REASON_STUCK_TASK_LR 8 -#define REBOOT_REASON_STUCK_TASK_CALLBACK 9 -#define REBOOT_REASON_MUTEX_LR 10 -#define REBOOT_REASON_MUTEX_PC 11 // Deprecated -#define SLOT_OF_LAST_LAUNCHED_APP 19 -#define NRF_RETAINED_REGISTER_CRC 31 - -void retained_init(); -void retained_write(uint8_t id, uint32_t value); -uint32_t retained_read(uint8_t id); diff --git a/platform/asterix/boot/src/util/cobs.c b/platform/asterix/boot/src/util/cobs.c deleted file mode 100644 index d366d0a964..0000000000 --- a/platform/asterix/boot/src/util/cobs.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "cobs.h" - -size_t cobs_encode(void *dst_ptr, const void *src_ptr, size_t length) { - const char *src = src_ptr; - char *dst = dst_ptr; - uint8_t code = 0x01; - size_t code_idx = 0; - size_t dst_idx = 1; - - for (size_t src_idx = 0; src_idx < length; ++src_idx) { - if (src[src_idx] == '\0') { - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } else { - dst[dst_idx++] = src[src_idx]; - code++; - if (code == 0xff) { - if (src_idx == length - 1) { - // Special case: the final encoded block is 254 bytes long with no - // zero after it. While it's technically a valid encoding if a - // trailing zero is appended, it causes the output to be one byte - // longer than it needs to be. This violates consistent overhead - // contract and could overflow a carefully sized buffer. - break; - } - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } - } - } - dst[code_idx] = code; - return dst_idx; -} diff --git a/platform/asterix/boot/src/util/cobs.h b/platform/asterix/boot/src/util/cobs.h deleted file mode 100644 index 66f98f320d..0000000000 --- a/platform/asterix/boot/src/util/cobs.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -//! An implementation of Consistent Overhead Byte Stuffing -//! -//! http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf -//! http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing - -//! Evaluates to the offset required when encoding in-place -#define COBS_OVERHEAD(n) (((n) + 253) / 254) -//! Evaluates to the maximum buffer size required to hold n bytes of data -//! after COBS encoding. -#define MAX_SIZE_AFTER_COBS_ENCODING(n) ((n) + COBS_OVERHEAD(n)) - -//! COBS-encode a buffer out to another buffer. -//! -//! @param [out] dst destination buffer. The buffer must be at least -//! MAX_SIZE_AFTER_COBS_ENCODING(length) bytes long. -//! @param [in] src source buffer -//! @param length length of src -size_t cobs_encode(void * restrict dst, const void * restrict src, - size_t length); diff --git a/platform/asterix/boot/src/util/crc32.c b/platform/asterix/boot/src/util/crc32.c deleted file mode 100644 index 29e6179a6d..0000000000 --- a/platform/asterix/boot/src/util/crc32.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "util/crc32.h" - -// Nybble-wide table driven CRC-32 algorithm -// -// A compromise between speed and size, this algorithm uses a lookup table to -// calculate the CRC four bits at a time with a size cost of only 64 bytes. By -// contrast, a byte-wide algorithm requires a lookup table sixteen times larger! -// -// The lookup table is generated by the crc32_lut.py - -static const uint32_t s_lookup_table[] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, -}; - -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) { - if (data == 0) { - return 0; - } - const uint8_t * restrict bytes = data; - - crc ^= 0xffffffff; - while (length--) { - crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf]; - crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf]; - bytes++; - } - crc ^= 0xffffffff; - return crc; -} diff --git a/platform/asterix/boot/src/util/crc32.h b/platform/asterix/boot/src/util/crc32.h deleted file mode 100644 index af67427e6c..0000000000 --- a/platform/asterix/boot/src/util/crc32.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -//! \file -//! Calculate the CRC-32 checksum of data. -//! -//! The checksum is the standard CRC-32 used by zlib, PNG and others. -//! The model parameters for the algorithm, as described in A Painless Guide to -//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are: -//! Name: "CRC-32" -//! Width: 32 -//! Poly: 04C11DB7 -//! Init: FFFFFFFF -//! RefIn: True -//! RefOut: True -//! XorOut: FFFFFFFF -//! Check: CBF43926 - -#include -#include - -//! Update a running CRC-32 checksum with the bytes of data and return the -//! updated CRC-32. If data is NULL, the function returns the required initial -//! value for the CRC. -//! -//! This function is drop-in compatible with zlib's crc32 function. -//! -//! \par Usage -//! \code -//! uint32_t crc = crc32(0, NULL, 0); -//! while (read_buffer(data, length)) { -//! crc = crc32(crc, data, length); -//! } -//! \endcode -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length); - -//! The initial CRC register value for a standard CRC-32 checksum. -//! -//! It is the same value as is returned by the `crc32` function when data is -//! NULL. -//! -//! \code -//! assert(CRC32_INIT == crc32(0, NULL, 0)); -//! \endcode -#define CRC32_INIT (0) - -//! The residue constant of the CRC-32 algorithm. -//! -//! If the CRC-32 value of a message is appended (little-endian) onto the -//! end of the message, the CRC-32 of the concatenated message and CRC will be -//! equal to CRC32_RESIDUE if the message has not been corrupted in transit. -#define CRC32_RESIDUE (0x2144DF1C) diff --git a/platform/asterix/boot/src/util/delay.c b/platform/asterix/boot/src/util/delay.c deleted file mode 100644 index 5224980a6e..0000000000 --- a/platform/asterix/boot/src/util/delay.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "delay.h" - -#include - -void delay_us(uint32_t us) { - NRFX_DELAY_US(us); -} - -void delay_ms(uint32_t millis) { - // delay_us(millis*1000) is not used because a long delay could easily - // overflow the veriable. Without the outer loop, a delay of even five - // seconds would overflow. - while (millis--) { - delay_us(1000); - } -} diff --git a/platform/asterix/boot/src/util/delay.h b/platform/asterix/boot/src/util/delay.h deleted file mode 100644 index 7a51f8424a..0000000000 --- a/platform/asterix/boot/src/util/delay.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -//! Carefully timed spinloop that allows one to delay at a microsecond -//! granularity. -void delay_us(uint32_t us); - -//! Waits for a certain amount of milliseconds by busy-waiting. -//! -//! @param millis The number of milliseconds to wait for -void delay_ms(uint32_t millis); diff --git a/platform/asterix/boot/src/util/misc.c b/platform/asterix/boot/src/util/misc.c deleted file mode 100644 index 7181ffe905..0000000000 --- a/platform/asterix/boot/src/util/misc.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "misc.h" - -#include "drivers/dbgserial.h" - -#include - -void itoa_hex(uint32_t num, char *buffer, int buffer_length) { - if (buffer_length < 11) { - dbgserial_putstr("itoa buffer too small"); - return; - } - *buffer++ = '0'; - *buffer++ = 'x'; - - for (int i = 7; i >= 0; --i) { - uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4); - - char c; - if (digit < 0xa) { - c = '0' + digit; - } else if (digit < 0x10) { - c = 'a' + (digit - 0xa); - } else { - c = ' '; - } - - *buffer++ = c; - } - *buffer = '\0'; -} diff --git a/platform/asterix/boot/src/util/misc.h b/platform/asterix/boot/src/util/misc.h deleted file mode 100644 index adb02e2899..0000000000 --- a/platform/asterix/boot/src/util/misc.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000) - -#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0])) - -//! Find the log base two of a number rounded up -int ceil_log_two(uint32_t n); - -//! Convert num to a hex string and put in buffer -void itoa_hex(uint32_t num, char *buffer, int buffer_length); diff --git a/platform/asterix/boot/src/util/net.h b/platform/asterix/boot/src/util/net.h deleted file mode 100644 index 5613914aab..0000000000 --- a/platform/asterix/boot/src/util/net.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include - -// When compiling test, the host OS might have conflicting defines for this: -#undef ntohs -#undef htons -#undef ntohl -#undef htonl -#undef ltohs -#undef ltohl - -static inline uint16_t ntohs(uint16_t v) { - // return ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8); - return __builtin_bswap16(v); -} - -static inline uint16_t htons(uint16_t v) { - return ntohs(v); -} - -static inline uint32_t ntohl(uint32_t v) { - // return ((v & 0x000000ff) << 24) | - // ((v & 0x0000ff00) << 8) | - // ((v & 0x00ff0000) >> 8) | - // ((v & 0xff000000) >> 24); - return __builtin_bswap32(v); -} - -static inline uint32_t htonl(uint32_t v) { - return ntohl(v); -} - -#define ltohs(v) (v) -#define ltohl(v) (v) - -// Types for values in network byte-order. They are wrapped in structs so that -// the compiler will disallow implicit casting of these types to or from -// integral types. This way it is a compile error to try using variables of -// these types without first performing a byte-order conversion. -// There is no overhead for wrapping the values in structs. -typedef struct net16 { - uint16_t v; -} net16; - -typedef struct net32 { - uint32_t v; -} net32; - -static inline uint16_t ntoh16(net16 net) { - return ntohs(net.v); -} - -static inline net16 hton16(uint16_t v) { - return (net16){ htons(v) }; -} - -static inline uint32_t ntoh32(net32 net) { - return ntohl(net.v); -} - -static inline net32 hton32(uint32_t v) { - return (net32){ htonl(v) }; -} diff --git a/platform/asterix/boot/vendor/wscript b/platform/asterix/boot/vendor/wscript deleted file mode 100644 index a924c72232..0000000000 --- a/platform/asterix/boot/vendor/wscript +++ /dev/null @@ -1,39 +0,0 @@ -def configure(conf): - conf.env.append_unique('DEFINES', 'NRF52840_XXAA') - conf.env.append_unique('DEFINES', 'NRF_CONFIG_NFCT_PINS_AS_GPIOS') - conf.env.append_unique('DEFINES', 'NRFX_QSPI_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_SPIM_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_SPIM3_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TWI_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TWI1_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_UARTE_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_UARTE0_ENABLED=1') - -def build(bld): - bld.recurse('../../../../third_party/cmsis_core') - - micro_sources = bld.path.parent.parent.parent.parent.ant_glob(f'third_party/hal_nordic/nrfx/**/*.c', excl = ['**/system_nrf*.c', '**/startup_nrf_common.c', '**/nrfx_twi_twim.c']) - - nrfx_basedir = "../../../../third_party/hal_nordic" - - micro_sources += [ f"{nrfx_basedir}/nrfx/mdk/system_nrf52840.c" ] - micro_sources += [ f"{nrfx_basedir}/nrfx/mdk/gcc_startup_nrf52840.S" ] - - micro_includes = [ - nrfx_basedir, - f'{nrfx_basedir}/nrfx', - f'{nrfx_basedir}/nrfx/hal', - f'{nrfx_basedir}/nrfx/mdk', - f'{nrfx_basedir}/nrfx/drivers/include', - f'{nrfx_basedir}/nrfx/templates', - ] - - # micro_includes += ['../../src/fw/board/boards/board_asterix'] - - bld.stlib(source=micro_sources, - target='hal_nordic', - use=['cmsis_core'], - includes=micro_includes, - export_includes=micro_includes) - -# vim:filetype=python diff --git a/platform/asterix/boot/waf b/platform/asterix/boot/waf deleted file mode 100755 index e968b63643..0000000000 --- a/platform/asterix/boot/waf +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python3 -# encoding: latin-1 -# Thomas Nagy, 2005-2018 -# -""" -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -""" - -import os, sys, inspect - -VERSION="2.1.4" -REVISION="72787ce48f227ac42c4b0da24e780694" -GIT="89cd97a8d823d797297592ad751beb678806f339" -INSTALL='' -C1='#.' -C2='#-' -C3='#+' -cwd = os.getcwd() -join = os.path.join - - -WAF='waf' -def b(x): - return x -if sys.hexversion>0x300000f: - WAF='waf3' - def b(x): - return x.encode() - -def err(m): - print(('\033[91mError: %s\033[0m' % m)) - sys.exit(1) - -def unpack_wafdir(dir, src): - f = open(src,'rb') - c = 'corrupt archive (%d)' - while 1: - line = f.readline() - if not line: err('run waf-light from a folder containing waflib') - if line == b('#==>\n'): - txt = f.readline() - if not txt: err(c % 1) - if f.readline() != b('#<==\n'): err(c % 2) - break - if not txt: err(c % 3) - txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) - - import shutil, tarfile - try: shutil.rmtree(dir) - except OSError: pass - try: - for x in ('Tools', 'extras'): - os.makedirs(join(dir, 'waflib', x)) - except OSError: - err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) - - os.chdir(dir) - tmp = 't.bz2' - t = open(tmp,'wb') - try: t.write(txt) - finally: t.close() - - try: - t = tarfile.open(tmp) - except: - try: - os.system('bunzip2 t.bz2') - t = tarfile.open('t') - tmp = 't' - except: - os.chdir(cwd) - try: shutil.rmtree(dir) - except OSError: pass - err("Waf cannot be unpacked, check that bzip2 support is present") - - try: - for x in t: - if hasattr(tarfile, 'data_filter'): - t.extract(x, filter='data') - else: - t.extract(x) - finally: - t.close() - - for x in ('Tools', 'extras'): - os.chmod(join('waflib',x), 493) - - if sys.hexversion<0x300000f: - sys.path = [join(dir, 'waflib')] + sys.path - import fixpy2 - fixpy2.fixdir(dir) - - os.remove(tmp) - os.chdir(cwd) - - try: dir = unicode(dir, 'mbcs') - except: pass - try: - from ctypes import windll - windll.kernel32.SetFileAttributesW(dir, 2) - except: - pass - -def test(dir): - try: - os.stat(join(dir, 'waflib')) - return os.path.abspath(dir) - except OSError: - pass - -def find_lib(): - src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) - base, name = os.path.split(src) - - #devs use $WAFDIR - w=test(os.environ.get('WAFDIR', '')) - if w: return w - - #waf-light - if name.endswith('waf-light'): - w = test(base) - if w: return w - for dir in sys.path: - if test(dir): - return dir - err('waf-light requires waflib -> export WAFDIR=/folder') - - dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) - for i in (INSTALL,'/usr','/usr/local','/opt'): - w = test(i + '/lib/' + dirname) - if w: return w - - #waf-local - dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) - w = test(dir) - if w: return w - - #unpack - unpack_wafdir(dir, src) - return dir - -wafdir = find_lib() -sys.path.insert(0, wafdir) - -if __name__ == '__main__': - - from waflib import Scripting - Scripting.waf_entry_point(cwd, VERSION, wafdir) - -#==> -#BZh91AY&SYfyzqPM"B#.0u@b)\_mȀ#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+m&q.=إlԵ=sNѥJJ2&)M]VEv y˂oUѱĪ5F=#v妛=8l}5E;c{] pra@;z}aYwcwz#+#+@#+c@Me(2۶Yl#.C.MM#-h3E;XvÀP( J(c*(D+A@AJ퓽Q@sG/D6ټ;@mJ彸R-}=OXz6trނz۽޽{3yofgwؖo+ϵ޷vyVݾAA_]* j=QZngצ7z:]7$٣"úT#+TT)#-#-e{5蹴:Q]^kۗeIZBH5B7>#.Oo8;<#+-^zh˧tϝw;wp鶅}6b\duӓ6[[.ӳs;}y]uu>o{vӰnݻO;f4Q魳VwRLw&ɶ9͛vr{ӌvܧ{Zl\{u,iTo/X#+wzsm;]#-JE#+@TGU ow\dٝ1=ݶ^{qٕV9O^=oUޡк.ƭ#+ jƀ#+wg7%y7xkǣFknU|vk^nz3f#-_VXMD@{S}vꫳ7V+{z=ý^y5Cjݝ4}{n;ko|m֐׻G_.hNOea{ͬ m#+#.s(o[;OSUO]w[OgS{m@ ]cwew6s8k]l`S뽬=Kv˰績nFà=hV 7v<:Ӡe#-.'7 :ݣ#+O=Jwc#+4%"G,sxۺs>6۶wJ@(Pݚm5N܎9;dc-Ӹ{}t|Z'_i#+&#+44hȘLFI&Д̐4mFzOS@ @&M*~4?J~$@Sh4=@h#+#+#+#+#+H$D&e3"'ڌTLOMCiGI` #! =R!&&SLڧ$P5#-=C 4#+#+#+#+#+I #+M#+ L=hh##-)4#+0#+$Dɦ@ 4'CAOʏҟz#+#+#+qS<ʅ ZwQ6+RL#.TT@OPD/ӑk+{w ⨵xqs1ĉ,a)̽{S1UY:p"#.BP@Ch7CNNxz9Io2fiTb"<x\I%:¬eZƅpq$(Blەڭ6bfZhQVEh"*(@PK>0#`Pb(bY E QzkjԘ,fjI4$a$Y*635%M(Ҋmabf)(MSQEc`ж&4KD!L1@cCLYSh%j(M4Yi#- (bFPZ&liM(MP46ZF0"ji&lF,fd̀AMJImMfZLLPjU4PS# *FЈTYm,)6`f#.b1(IL#lhLab!!e#&F$S2)ee3D% dbFK) 6$єA& IF(āF"Q3)YS0Lh͈&#i-%@h HIPbM5%R,0CJaL0I(Y-$I1"E؀YR6dMdTDʉE(͑&mbHEE#-)Jd%Ȧb l XP‘PeF4SMMjBX#6c Qd44hX$J6FR LAD!&ZfZQTQ4ĎԋLB4(VPIQM1&i,XSlFMblIdS&R"٢4أMJ,dSi#.,QE*I4I&4HLl$F4DFe6Q2) e"d4ȂSd1J"L6 Li,D4Ld6$MaH!EFbACA,j,4hM(bԑI$3I#)E,ѥ14PhM)CbUi(MLє1M2"+AM)f1Ie!e6EEmd!E6"&Sk6jb 1Q1ƪ*6L4B$Ѥm!Z6MFD6$YL!*HF Lf[3Z(ĤڊBR,,Y 2i!lHѩB+d"TT6lf35lh"M%a*ek%Yl)fF#.))QbZC%6ѫFJѪɢZH[D$U&4El2ѵ0, E 1d+HFѴĬb" MkfBe3RMTR$M6hKY)Me-bV-,5MM!m )lXh)l1Tl!b41$Ch1"c&mfK2UI4FLJ,l#-R1(RX٦hh M16ARJ (1!2QCLI5Ć `f,PQ%IFʔ#-P0&6a)dZ2EQѳ)$ѐ4d4,؈ɣF642FbF22R X 6,1iefhԥ< ɵ cJId*ICČPHdLkؔ 4ME1M(ڲ6hT2H*Q DERF&LBăII I# EJFV1h#-&I4(lSDFE͋!IJ1iJJV-ƨh2QAb 5&0#B()HR)ILh5EJEMؤIM3(kH@d؆lڈEL&J4ZLIE%dJlCFTe*4 L66J*MXF"Chɂ5SS63M25IBI5QcDjJŲX1h"0dEC+DTmQi5 hLLSdF%#-LLlm5&6[DR-Dlld)M+bhضŭ&c)D%ĵ6iT53I FZ,أ,#j+bɶRehlCLE&4XڪDʅDĔF*HZMTU0ImLմcZISC-lmMMԚ%X2"4f&(6͒ݛ,EdQB6mi6Xps"[\#8q}o _(~L~#-?(jƆ4{: bvU ˥7\Xp!tosF]%iH*&*qnh/aIe;Lu*L k(U+RP8&ql-+YSmӻ5rNk0"M,OFܜ#B6;#.JL Uy%E0()vN zzmj,U,'RHLYh#")͓*P*RFI"#Gk] :F1,#.2yl2*FoM#-|"0Q30\,.9ZUPF'E|h r*bܼY*w"a)hR).Y*/5֔ 2,#+GܹͅaZ4A1GF[16Z6d}ܗp::Hc7j[J$UcZhJW6REE\ۯ]|E*#.+kLW(!pS)Sߕ(QϞ0PqX%&,n$%{<^UJw\ӔoxIoR#[a@q]v7(\)"+J3Ar*MDV)Bw팺d2 F6KkM7)!Iե`xQJ+=1kbUru֍\C相4R_xM>FH!oXzdXnta1KF>R"60L(V"iTeN$iӀkRũ/*5ĥG?.wEpu00\l`~ ^8JP9O#-g2||#+@pXp>Ҭ{S0dAQ rґÜ8ΘqH\^/WaMX#- `8FW_fU9Zp N3&~ /L1ONm&DPz^$#m-|Qm#-Om#CF,Pc9k9]Gmj7al2561AP#zFE#-*x72.aTiFoMo]$ /*z'Fm w'j(TG/Poᬯ]yiU(//'NP:cc%xr@ӻGyZۍol\MRꈈbh?OƧҙ}so G ;kLgًlOOپ$;SJ 0Т8rOmѡ#- M+U@bR +:ZW7.b|jw2%v}V}*<.GrɕS:zDoc[$5#-JB <SQ\@υ|HmiKΐ J#.RRuoFSmǿJ26Ҡhj'6ʴOb\eojbk?Iqa,Fep&*鹦~wC L#.Jb#6,E"*ִ߽0o~5i#.n.PmӞ Jn;YSr'uJgI<׎k˼#.9]}|$*^1ueLs::dMgwYa83/4kM"&埨և|ug#VmQa9p1AEZK:`TYL*P[oeU!̚Z(Ovɓ썋p;\t XYR6r3T?G詵<(t}W|+8#-H^ :kv<\/N땺7l6GI>_UH:vl7U:u1 DL5:1OvxaB(76!L .D1\;tU-n&~N0D~|Ȫojx*“|P%rtȏG?T1-׬΍6#.0Lܞֆ3y=;i#k\j5[~tzöTn\4# u蕼5?9ѷZ;9P忶̼[/򓼰1j*D-'L}uTׇ#R|&s :MWSk>(4ykϽ0ou} H!:7'1DE5=0^=f`cʾ[BLZYx19F2\(1&DD<Ҥ%H8`,M$ˇcvW2JE>~Z`i PexRh:NDJdL`IĦ>6<ՕS#dd)ru/՚[R}2}?q+ ;;sMYBGɾ)օZ{(:xaj8p;y1yq̿[Ë|`{|(8VzGljof[R)9:sIǪ# U Kd`=k#-onW6%c¨fμsdo(`4,;t)UQ9c:N#.WA@HKᘡ6D$DgG E QAi$ MlxQJj6J8M b?_ngo<9aݑȷ(+'1̲UM#-6〰KOw߲~mł~:p!K[rr'mJ&ծM7TʔmX6s018QͥYX"Sհ_=*_-P-MZ(: 0R]s~SNhcX+ztqC1OoWg:imf=P2*/AAvKfln6JРDbcL`Mm"ь`\EX]#.=զb5ɤ}:t$0ͪ&-!ZUsLN:,wJ%4,w1]a; qN~6n1ަP&u4~٬Fפ*{#-GbR&iwxAm1as>^n5,?HJ:tֱD1󻌏?U& TTM2`)H6YXlT FfJPF^>XUv R7R듮Հǿq !HTߞ3qRB Gws)h1Op`G(δv1 & ٗӁIH.Pu%l0SFg˚o-1'S}֯(zW=p4M=TouDvLl6'dЃꄆk0yìf#-rc#NMZc㾪#{c_Nqv4F,rםuCe=cPȰQ+,(C-}/`o*y06@3x\1F MжF2EP >m(&"XhdϩS8>Cyb =AȽWyKxtq(ōGf^E_#.(?-lVa6S(ӹ\d{ ڷ$bŴ9."ڛQtFt!vSݜurƺGw>Μ_{6w*bLxDC aFTlR.ն.vx"V n/=6g#-ĸdﮍϩp/: p=;-c@nO=gEbk]ܬ[ekl;xE!dUqtbueC]t$,V-,+}v+6Y!{mҶ#-XŻAz"Z)>&Ue@u ßX" }TAo:&<YU^muaf#+C_#+/A$:~o,f GmR2ZbR?*$l_~t㟇(՝H iz~#.?Rl\$R\ӣөïA!NC>1Ƙa\P! @H_#+a[6}>|k$6#-w&ʓ\1~8p5~mw%L#+Žܱ|iU7t!CDRI>VB!#+ΟujxRFzLj>l%>^}:얒=c:D#u7+Ǭ8tT&1ToŒw&u*d&d.w_S PYIq&Bx椙eb`f͞nW2|jw.[4ʂ7R/!r@#+@L6$tL?J쳠mj#-5L\>)nHL럷|f &`An YΗ嫩OMǝYY(g|__Ѿ-Z=^r_Fp(O9ä0map'&6[Pwpw"R߅}?I! lk6"/kн7}~'걅"Œ$&i0x:5r!\>1x#-XɲQHM#ҕW1R`&$4} T 8)}=XuNnnvvG^HX#.@z -I |NSr~*Y;>a*^ #-8d7wPcCl5#-o;e5#- )@U[Dd"b$#.Jk!gZȚ1.>(pL!m5*P7sIp8:2j VIX![^vƯX[0H]hݬv 8JW|8˟5?%"tgƹO,ôbOrb5)كul`qPs}`$5𣬖Ws9gUjBrgEj(RKod(0D\"-TD$TvLR'O+|6}%z_q\q;'T'AɓYi0%IbM{#8~`qӏ\#.zQIc5sfzlF^1oޜ4Yc/#.~b!Iz?KЛJ;DZLS}IorVB37/e켫3=NYt_341\v(s=41]#+pqk 5Bwn#++[b/]̻]֞;#1#snZ*Jgq-3-⾜jtn>k#+DhMD)$MSXY2h=`ҡq)E(he364 ':ZEhbp6ţSӋ ýzc`ި К4Pna ,w)[!&KZ]o4\0pPE8U&aWTCyp2b2#+{hwc^Κ[q̋Rt':K[*L/QrNG#.@E`9AjJ;ɋp[l3]_\1&k[p괌`ߣ6hN%7斍6Ƙ"[gLE"V&rg tT7.ěܺƑ4p5#-5)ƫ`0h?{" X1,q`͔.@#.4`=z}w}Xv/fA IL(WM^}*߶܌L1bkۻ]չnIWo#+l4Y 1cymxŹE^H$gͺ|pq$Ϸˋu^ޱETVbW#TQLj`|]CepsgtwC 1k/yj|dMy_:y^/|8\x"xO}s(Zl$<`_KFlu8dN)Gv/KVR&tDaR(j5mʷܾƟ"tEm Ɓ2!qw"F)dXUK@md d ::a#-Iu6#FJEQc޵ !`XY] +J:c(IHގ؅)ڊSjŚ *07XL5ա#@F5j# CLDezϯݻ4$_pド 7B[u[SGbK(7ivg@lX/xQ;D#ǘg08#-qKnM6Bi?'h+yb9iȦ`b":|s|X;z"]eBqXт:\Ϝ p7lR!6<ע#-Iu NݮXkz#S!KXR+M7E+_f˲>ʋ28NY\&!>Pr~C#.6^tmkTkp|&R@_5,R~όMlFe7jy?F~dpL< e0NsvEۉ" ğ3a޳?&lf=͈m&*S1pJӷic8.,g_"uh@#.v@3֑y"/9/ϛ^|sVj0tfۗ&Ti"1E#-:#-7+«xهʣ'?#.ԘxQ+*09ǿ#+ތ"B(Rl//L/3A#+1?lݻmcnT&-,x:䛛Mht9+tPZf!XJjgmVM`XAdQEҤ\KhUHhb0h+#-1C,!K]S40(3 אfmj (#-rɶCb59H @O =:~۠xrMhw#F)j6".u(pUl4 mG8YAY8t` 6.mWPK~f>+4Le!+_68i¼{S?>*Y[nȧS⊑s P}bry^%j~pF]ap5q@ "-k]]rfggG%wmr~Faլ߯W-Uk:axaSRJX8!G?tzr~DqXW5̦ꨈjiw@wxC8(|: ET];~>^+CC||-4ޖBMBb3E3jK&1*X_sѦ_k[Ң#+R)wRPi/4eOZ {#ݲ;3MtkQƐHS#.X5Ӗ-'ͫY%;f?5}zV (瑉"ȤPΐx=-ԏ+b%Gh&Tw@O{4mkk{$bA6BU LU20S~= ;nm6#E,OD!otѮ+wt (x~:;t";s; EJpN[2<"e|̈aw%},h>A/EkH.ӛU;ǿl5J9D#-Vp+{%u|j1,Ϻǒ`ǍzC+~J[aAWJ2pgz e5 "&]+vnst?ɻ#6{Z ~NCab/RRNQF7DN$_mfk #ҡ$:x?>lV-,dcq*$|KKdZD0EozW!AĩBvy3TűW&#-^ߵ"3ܩrҎC_ɟmdc/ IJēEo𢸍2A1BA0U&#+5yZܪ(agG!eufly) CebK4 PcCUR(StH7PKHQQʓcЌ?,#-e8tCrrh#."DzN%1o#+U o?iiٲ s_}[+2Mܹ]>{Ծ~U{9:$kocYCV<Ce/-1h+rpV}O\O_oރoۅ>ͣN߆\y+6×U3^nhTݯ/.NCdEãOla[zcTV^Ygl(b^oyÚ/0"?jz?H9\ _tW'O#-UNEq}c5K AC-r-taWU9?+,e]Œ4st&q9o4ݿ#ƙ_CV}eǎ-z:T#5P컚5s19:$Vdn*k's>*+RycXߦơxT[~'K./+fo_P4Ɨ;}&#-ُ*z]{>wsyD.({w::pz_Ww`^=D|Ot짒hw}q$ÿ l5!H n h l,#+(iP-AR^ -s`IBfH&Qx Zj]]I_DW]3>yqߧ^_#-,,5\A(mjT /V$F%N/'kHyeIࢂ"a)n${ml< n&aN#+ps4P|ܯVꪔG+(!ID'1hύVqjի B`:ǿG't$7#.N߷4W4H~xpG^}kwgcJkYuWU%N-ö22j#.#$pw]mC^N҉a o9E5~.NzX7ojNǣ{ k?KnT֥[5y55poimƿ>ً$ayB(+GJr)=;cFmw1[-DNG%cQ!2xhǖs̥PQEmp! Vse"1EPdQZa+H)YlJGUZڌF0T-ЃMHAty c] D܄0RA#.4lF9Mcm#58Vt-'e)ms8$|3D"*?63#+!¯]oYH.%#.#.H**q=9qG#-֖$ثFuFFZF1h.Yw*ĐRI*9%{{Bn{.DO>p@ՀSoA NFWodj"=@gx}{}I{~E-ӣBb)^/+tNćYW3%.]A0/H#Ǩ=zY5)R#cމ!aAg*H^W_WD"k8rۢޜg64ksL&\׮!>}i#.ZB<ƻ!W$‘ n]AqM4.5U.BI,ʪoj# fupyp$wU'#."0s~H譇G툃 5w{mnA.V{7+k}PYN͓:1G'֙)ma!]}s&Vxp ^Czzeo>%Q!U($HV:ETn#r#.5#+F<`Ozo%ѕQQHPK*)b25y}R^ߏWH{XU~߬>? me#.#.X<Qs(^#.A3#pkvH)1=jo\[1A'PzZ*0d+DU3ceL2Р*0be#.h/]M|["FcVգsS&!}fw0 o*̩H86Do9 W2Zի!Vcmș)ޛ62A4ab0jBd^7@XllX -#.TL$wg5ŊĦ)ER.j(,Ps/$la1fnx0iGHr> [AJXf}`>cjQ#+Oц:~#|Vy=p`Q$XUD qnnEtH^ZQO|tUdNG#"DCLȱ:EAWHh'3[P6qzL=5Cv#.'E0ltY"0)ExL2\*cB#+JSM@Qo" 6J3%QJx&A.;I5h #+̓BpቖDNؠŁx*;HT+ɖcarR4R2N#-T:+R,)BLF0JeVA#AuŔHBw |@m(#-(׷o/|޾=xaEQѥȈ҃5Eb'¬SUkx՘ij3V4Qb@h{LYqÊB.>}U@Dmcn_ZMN0" 7HrDuAda%a-0vI<4nu#+=\<(D C%s?F5e1mqcy,#-PQFڦ7 [o=&/Jz,T>;柠!uDݺfiZ"13BX-vE_FWDM"%PAiT]6ZFЧpY8B:>L(dΜ#-[oY3qp)׍qKEXx(m!%[cd&&HrS.#-E˧w17yX=>3#-ag9w+iT' '6x*L5㝤}jbmVfX|FHGZ1HWXzu.ٺ ^ drz:TnTnq0X6c"oLF]Tǫ㼁 -Z "Rn*gv)(լXq\o~(D΋2@88B/Wl*B0Yʮhbׯ [A:_*2b9:C'dv7H-cx9{(] @(#Bþ҆bw6o#-:#-1OQ_ȣF:Gԝ`6~ad(۶KsG)+kw!xlK0@;]0oJ6~s?2@PwT˃@n{YErظ>]ydC> @Z4$`n#+ۈpKXgD+q?<vJSHiiW?'{Lۇ^LҀdB#B̠A8?뻔mdwA<{cbOPcѭ}~Bqq`-2 3y!܍0#â-sqlF??j%`R8 <9yofM刾g5OLH4B~b)㏭noϡwp|Mb F]P;N&:J{F`M^̿\aەMftqdʊcu28c"l(.Bm0JɋaGhZ+R\Ô!@νpdGU^`(anC0sYLdUVgnȷL4=>L+8|Y.=S^-`l5IrT> +Ae#4$s;N(F./_#-koռNȆ7Hdݙye[X^aϞjBBP\yuh4tWA \pk A%S9o3'Ѧy^H[BG5$(dW/`hAT%gH!ĽG\Zjm .r+sd*Sdݔ<7ImPsbmC#Ꮌn]'pk4]mϿ~tusJ,r/DN5r蘘 ty3=c/3Eor %EG?qNtLEEJ,9rKr~  T~h==5P`O^ב IsXę2 u0rR?M3#-:?_2Y&",gxpg05V+-'}{mU*V +󸶘CGR#-͎؅`A+s, `MQgE+#.S[Jo~$Iʢ}b^j~8y$*GV7SYte>s?f5uÔw\S5qCktC];e-(QbS2njޔ!at`ΏdZJ2b<#-Gk]|ZNwe4?[{/gq1$7j65?`sZZ0ZFQ-M=#.EѠ6gk]Q߂,Gq=W5z4/mc n|nvllτ$BroN)pN0 rEE(܃@J)rZRLz[+7NP>Sw9K߅&o۠#9#-?(0v}\tl޷?>|Nvly!Z7cU44#.-IDhW#-[LGY^"#-9zOW=cH&?[ӕߕѫ(z+/2>ZU.pgT9.P(uœKjb+51UutZO/ݥ聰,g#.ŤrxKVYJx8X_/-i@V6Ua*mU2TYB#.,j䔍]K_$ l5EٍycZY$'+#-O3/*BAFo^($0'/qcMN6Fʃ496QxYuB"#-nϮta##;FP}{l6ƨp%,ű4z+yZpۖ}YGg1F ryO.O|0ay/{>߷pLl纕ig6dV{D,T͗uy4 I&fw[.*ĔXI%o ]0g1^afp.а~v#+hdkřMc 6.rWNl'4ԳGt  ~KBI7lզ//z NQ97vS ][!5SEk#.BˮͦF][X==R+=p2dN!gSv_4`.9w+;=1͐x9Nꬁa9򤣦R-Ŀ[aY売Cqw?)P:"`ןg> uM+#+9;7j7KJ3$4njDBYM_{YeRC(I #+[Z֕.N#rߪ\u>{~5Prhٿ|N2}t6~`8OoV;<-r||P6Q۫}󭀥qVY_cHn{cϡyt=th,xMKW\E$#.csFeiDmm8U6T~'T*+7jʱrĺX[_ Ӯ b*dܦ~ht[( >.qY_j8t(TFτEBI,vbuHDUptG$#.r2OѳuV7 CqlQG[LYYlM2NFH= jTMM%s^Upc5yd4ٳ{zo~NaݷXU*Z ײA4i9rӪ-P1#-J箢U ]56z-cS1Ac|adpbZIa\kԖ2-yj6blM˱dnbA NH{tDžbscǔ^2(TE#.Yr6smT 5k;xyMc]yw9.U ʧng,Ֆ5m5Tbј{d[S/Mzưo|/bLU#-&'HF2[Cc'-2KWz7*=&#-bMY17*YQH,2UPWU#.Y@宁7nhl2|.d AXONƣ]rC?r_\AzGnÎ9B[=twe(){&!ηTMS*P4_|"ągɍ^$|b>GOԎ'vG][J^TvMEeW9m2fŨXEYB(MWtn-rtq#+Y{3~T\e8nLzuXqëjL¢~3#-Ģ5)0ܸz`"ܭx]|۳<Ѵ|jZF$E6颚,깁5h=pR9TbO=obS(GQbauMG KM)U;c#-aG>1;Yi %|Asu9/#-x͇U)¨ehMq`9#.?ܣ$.(osy;{w=M o- +#-0T&^o&vΥ(b4SHi{b_e5Zτ*,Yaɾ`|r[~9,vY~#DqGF#-@ $zqO+{Jljbq!?T>_r:=T#.K _43M_VO+5:,TVDsF6Z7'6ϙ]QN i mCx*;^i#.eOlYL=7b]'EKpsQ:J9֙~Xb8>#.m+}xA%yF{oo=`fʐ[~k1g{>hEf0 #.`;#.@RLOeFC%v9 @85ń`te%ru;TbpljB2#.ཱུ1ʻRtdr(ÿ=[trY l =@x$(j<"vBta1!7_ں486S62PǫuKgZ>#-kct_D>}^tcÓT )YF(zfbԓNH-8@uBGS/ s!Bc#-X/CF0| +Z+Fj˻dӯ4TkP?$Qn]I ̋YïE NuCp \5B<&4#-7ypj҂,9ğ}sRz_T(^{EF4NmH͚`w G]^0:#BsF=`ѱϐYnZ|>U".d/ߐ¬`Hv=$ۥMqY)#Qmc'4q"lJԋI~'!d/*+7 H -J*8['!Sr@L2S=.oALهg-skaCi["˝Bs_ƺNb2V+ѪFP"F~t^[ȩlSŮҺ6)H󚠠_z>yU[ъ<}<}[g='QxΑ<˒غg:tᢰ4 C*M`1NAo$E<E$}s(yy^㡞&f xҵavĘbDp]jvb3{lF}O#uP& 8[ 3wO98 ^dMиASxpo;]<iq`+_ٮ- +Qj!=0881^;HT&F ?Nq 0 )J<eFIv("Y4-H#.juR"\X '`&DHK2Hmjމʅ<اi'^`ӇB /EwDCP%Ϳ>SN9FX8rB3ۑyi>ũ/{QԫuZ;,E@/MPy70,͂5\ S~N@@gf6P$r|2@Be%שR4sLj̐q#+P˜ŏ٫o:3]+cԨ(PF*r%VqhOOw! muN"x>:੕I%k{{i{ZC=՞qB֯_~0 {:-nxjfxoOc#-#.Qv HB;ߘD+|r&V+ysoQVU`< c*|EYbh^xRgHģӓMñEI-yp_apZlYEIDW,4 Fkj +ɥ4n:6db v*`rhP@3c4at,Ͼ <p|Uv'tOp)f\x#+0 *#+S#+t"5Iz ITsC&4÷զfB#.3 |j-dx}*D)$QI~_6Whؔ+IM@2#.Pk<?V;e!a@P؏@׽wԉ.2}wmclZ,SM_#..6(;% 3(V'J/HNDH̫&Sh1$Yz #Ff {'|"&|#+Oߟ|;l*E6WO *C!QnWCTO#-nVHFM .$?#+蝻Zt 5-Ń;bI;τ>҈j#.r9V뫂]\}n[x2~znnimuC>t3Z0>8+>B$iXXiRύ4>\3gD}o)˩!3P1nH?An::yװE-:V@UVk5a_OIW3q*ЃsVs94/(KvZ#Gh`ݖ#.cj2ly38@8 ߦL9䁶'a\Կf@4#.2,#-N ͝l~O C P~[d#.32ϕT9o_n<0 ´wrPϡ%&<:'RCR 啦.*;!Lh lKQj,:Ђe\'sۭˉhC)u\af2vثs} ]ҵֈ` qmh[*Dq#.:SH{(߆h[po㲅iGTqtEʮolݘ*H( -$Z㤽#L5}MLH+n^UOƮc"g?s 9T;H#.;o#-p8$,M'kiȂQXQGU>\.!ҼO}We`Cx0oavenX룺wjj+$ҹn2֡{oQ9F\(Px"ޥ"UYZ޿kêߕpa99.zrRH2dGy@$P~h_e 0!H7Hqb -7Ctȩ]lP8[#-eqI׉{ iӂA;u[pc#.bôǁg8#+b(j#.Nj'cӎ,J^wDG}0I#.0H@&(d~AA:TVP)jI_#+y>z<6甩ĜTp;dx"%4jPB!=s o@Onՙ|TZ@EßPxJrU冗X@YCr`Ҏ$J 99@M]%$%s6Jᜀt#v3@2+Ŏc!tޑЃUr5#-:;R~ziJ^7zQC* @d [\?.|')#."N_tR}9uZm_T1'ʘ=G*H-t&_ӎs;i$KDF2|B $4weC4[;A0jR%`JzoWcC#- 9q5Ա&!H!=E,nw)&&ϖ.kѕoh/+A#-(Vq=ΣsdUbN)b{l=5BVvLŦ#- 9ky6@/&0>%ĨceUB|8"m7%E>0l@/#+LYuIPB@f 0Mj/fR$=g=Qgƹ-efIK/E:!uĒY>Qs;|U̫7v"^4RRZ"B_̊rO&Wa;e/ [|u'?0]5eGgd(g:#-&Mvn=5NUZV5ީKPV+]oa vCCN9^dyj]2R?4Ntp" |#{{R"ґ`%b:zLYLzdKRy|= jZ**6]xr^,~ұ{b1$VV(JTO|{xe=\c Dv38#+v1h)S^!돻|?G<~FF=#Ο{bh$Tu8 ;|ZP[R gf'"M&<A-JmW!/):т9w IP?)ϯ,TURևr}(9![8~Gc?jxM<0o6Dݖp ^GOn^oArx;410LA恈;Qpd57!)6w9Skϣ(x5MW!)` .Y;=ЧS%F#+䡠#+5â"ab+AudD#+HF0[j,>P—QA[&E72"\)]@`P@gYRJh;]Cҁ&ģMK͘A1,8H#+q%/[ G$APC8BW~< ,겴J#-XQ}5>5wLP({kpB;X8]( scflS8wрC ߲K!T/{Ldf7٘eswtT;ymGXBW{E>T(oQ-?#-U#+m`>+2GGoa_eMQ-dW^>5usEBp3a7[5H)%N[d<֮'^1/' ڈҡ4Zo]YG%M6mQ0b12뀒29Pa)*3Tj }]ע2Y902yeeWӺLh;<-ig3^n@~6 h_#+6OWa! @9ߕMů?-߻m׾bi,K5a)cϪ`UUFTF?,狼 滍h#-]6+7M,Δ!ײ3,շ#.B#+j#FxB(SdWigݹ5=G٠ԛ#.alq6}T:j8#.}P& gaF]]h£Jݴ7psI"8`CJm)*@(lUeYVHaةFvM6QaF@vz_fq*Birq)RYIƹ"5ay9Y'9HBOP.|"D,ֲݹF?~FpWo |:=#.9CA)G{8wC* #-bսMj[ck\sj-rם84i#-1았h4J]5dU)#.QZfyĎēӼ7EGÂHrI! 5ʲ$f4xA$F];Bϯ&{47pV<:8ϧutH-hV*RJfO_-3iP<^_PEܚ4 zʸ5[b$RJK_#.#.Lƕ`³b"OL͟5Y@-@*(EȻlj򯀨j%,BTN#-;6Iٻ'@۴Ք IO%@xFL0 6*±}oW񏭧=Bd,D[[6X);ԼJ<m$>}rCwyzoH*)tWmvv[eTmH 0Q1ACS8qAx&^&v8uY{cs #$l=#+j |J*)sTt،Ax0p8fG<@`P!slS,Y#-F! __;HtH)m//a$x{"O#.5rc\Vz#YA}禅4VaE *C(4^)`s5R$\5FG80G8N#.PP`ȢPBI#+[@]<$|kW\ :WqG;:ڛA{^qMܼN( #+s`nȦ#+48: b+7eۼyf_qD{ٱTC8wJ ]xEߩI(0[ʉ0p#Beu*_figEu&N1lKfr:npB=U#.n>Ӛ$XIƷYQ}:U&!7R6^Ã#+,;2R2 anlu %Gࣔ B@q^"&XsN R`c$CqB]m;#-K;FW-#4MeRi4Kbޘ/nl#fhfWC%lGͦՒD!HTsC9M͙qk:laOz50|(#+(kƇYiSdUd݅b:tA}[k~ Ա&k@#-!$RfCht侁0Nsҗ|?;#8%<~:?% =o]2}4NH`!ĔiXٌٯ?/$tܺQ/@; #+o(#+U$,i#+#+ ]H(=QO,Y|RƋ[;1|bWi~4?7]#0 M6{&Ҕ9_^ߎTwWu^#.h?YL&/:t#댖K$?i_fn HCG!Hn$ @ A*T#.Cj'I3"*y$U#.7k\*m3|H]+*{E;aa &ѕA9:q/.fglg#.tѳ?(Z~9 *ggcOs:$J)yPY~#-~ zⳛؕNi) P#+,F IJaO`)xoGI@#{ #+=#=}+LOײC]e'%-]tV7<GElߠn&MZiR #.m1ѦK87G#- lUH>>{|.a/1/V6NWRljTHNN#A._ٴyNZZ'o$#+=<뉙/#_PÒhË?b3ҥ ,'q:%UZUDE Nz Ey=eWqK05d8"zֈU<<}zG77>wN2'sN}V;D&#;wQ}$rwCsE{xq#'.\Tb\QC;@O3ǪvޭQ[rZ#.,R~D@5-_k 36Drh #-@lykC.'@4v@^e t#.^ޘbՑ DS/j[?Ӟw#-_<4 ؟@eڣXbI_9o(()HV9}NkK:1udk$I0*P)>.'mWaLD8n9݀b#.OP04t3n.ǂ  A"(a~=8 lN~B#+|{UVh>WО}cOM2)Ҫ.AxoǷ>T#+u$#+vV8'oXE^keom"EuMQ˟#+RL%i)qA#+(JOucG#+pPP/^Hc4BA#+]?]ޫƭ ]ÇN`(Dnu#+_'ko84!СU>b>W@>w@-pBQa*)J9/jQ^=7@<{eC5Ž,$);/򾙽i'2j\I~z\§T\c}Ep\8Qޏ.Yp״\<(s!@\NbfsT ?N8Jey#-jo^#mvJKt/J}H9~CҙՕF!W6\P߽ϯP~+ۜ&<28GW?~pG|j͍6b#+Si*whkeS,pUnTsw{r3tvn+qQ$2eC0R/﹔2å)H>/o9Gn:$WoUOrѤaiVZV5`H4Qt5lK$$5l,.<βc6l" ˥ ww#.zP?)_Zo_lzd&}\h/h*+=}Sqqxu5x\L1?l|]`*Ƽ|S=Ht)#}O#.k1Zb(;gFK^ ^թrUt{+F4F+*Yʜ#-\jxMÀՠ.ɲ!b1c7UOG>E$ g%CڛjIHLIƽpĨ)Δ#J r oS!\V86NftK,]xkT|cki-gǪv"c0C{mvUN_D_5nqtn*gRf;>oEȦ"/rQhQzS+0}sX(:8 {\[=w(~O nwt*{yʇ͋%=G4CmG|gxV X'G@2xhS/CX[)YGuIWN8pɖ,M43-|3 mfWpYR8 4]~5N=CY"=:6i#\3~~o,#.0m/pqEGЅv|6}V=j)>Ҝ$]C"pgTwLʟ韺m4#& gW_#-!Ӟeå[S^cPx'A9ae,%$fт*M  ؍K$ÃDCE~k3r޿x?.utzb3l*Nu]D 9 YD!yh|_2ӒHʄ\I\yA qJ`zG$6Gz#-#+#-JzyKFRObe TrEM|ĂRQ1}f`|!3Ryox}\٭֔,q\"LDk@b$D6$k,0#.SpDQLG+PC(uU3#.5ٻNxXۡo#-E`GBm-#+K;#.&M6ޗ¦b:>!ʮ5J1xĐj o8$zMyw_Ɍ B#+ޠ#+@x  oxSWu@\}|A$T" f#.+έI =N&$lwzhѮ##- =#!#TbE\sˍmq%#+ ypsAN#4hNWdHh#%}7J~88\줝/D1\TOiu *$b$&1eUE5J. G#+-PP#+)\#_=*DK/3( СJ z#+M6Y\S#+|b܂dɊmD $`'})yXY:@e63sD5K?Ɯ8x_)<(z!r{yEe{' 0WT όI^WkݖZFU5)SIo{oSFoqM|,țPnY[σ0v8: "+!u=Eq!)A"ysA]K)Cƌ+on7Ghl:E:FIC;W)|@P&J#+R.^Xj#LZs&J z[hc;jH׎_,?Ĉ4~DNPRyZgզFA P:lidiweJ|G@'M&\>cN6MR2WΡGs|Y6Pn!f1Ctd?cNaJ#.;˄ƑS{ޏy}iJDig%>M(5*`{yM+?Pȿ$,~靏ϫIV([*xH=[`a}47\8[ftEe;HTLB9Gvg{:q&] 2b9YQ#.Mv `2Ǘ6tV[]QL6hi.|q򐄎WT3ݣdᠭXyK0R"SB~enxWNKq=_:8;-#-!1(fqc݂#uKՎ$D#.+$v?C0!8D]$Dt4C'U#-+΋IVb "hr:dz--9Wkm-6Kp+!}l!ˇ Fߣ\z0H&T=GF9[)s3Q#wI+Nx_P~}TptTpc#-&j캢:=*d=UO#-#+.qޯăBDMWs2-·NV'3 Q#+C;Xtc#./a++pZ#.#.菢ۃ_'9V, jՆp@u[PrJdѾOM͛l1cK]=t:vhu ;G(Y)ב+6ی4ߧ#bMfl#+Aj2#kZjEf=u9\ՃV}k57먖V=Z#-.#-e\>#-;U ]q,A[_;4(+OxgIt9.M x nIX6d1TrU}#+pZ#.bmh6m3X4iя(p}]G#+#.TҵGs];3m(U P I!_?τHB|el zrJQ *b+UPIu,zJG]6%7#-S `* 00tFbYvѢ}|L8;,6 nP(f8 8#.&hm;RD`˻W3jrQMwZU>ǪGbѓó_E8#0Li/@3U6]@hwh)>_o"ؾ(#-alVT.+w'ȝjZ6s69=O P6HA>mnf{נrDuSjxW[=ʶnq~\5d-#.#+XH)8&\B6&#.{\pޒ6+`#+P#-]#-Lgqe`-N-I#(ӳR=Z'66s%涇S,%lSى>#7cZ5P]ؗDXb D|=]6Mh!f K#àQ?Ka93JaꂝDCLs QX$ Ö(L:IQ@SRqO!f= {w!v;:Ngl[%JoѵGCg~^'3allu,"H Xt.Ҕ`BĶl"1vkpC#.$aihloBEZuOzW _"C)Ïd=os`@\⁍Kc![\ꇳ#+vXHW6#+wI!v${BقO~: J F:6,#-,ćzdx_?elmmkM}eK5CO"@Due $^=_+dowZzW, Gy6;MQ[=$'i>gǠuZj_D[)'Q T%@K/]/ئQsy'rz`E\ajvDWpqZsAkN:D X3stKBwI$2lJؑ?^C r?g|kI=C|  |N :>YzYN3$v{ao~#.BNtK;z??b# ҆T|sRjy;CsJUF5PTVnM\kZ'*.gʏz\[Be$#6(Hx;Gt_p#-$Mr?WcFEDd`\Ke9`?E@b#+$̆qݼ?GQiS\(4!AJd@ئz~BQMN>c#+@D|hd$GB$%W 7 aE81eATr#.vA:08 [#.h0?.jdst M.͐'/,~@XSQ#.D[⿛ߴ77C@e&PtRPEz͖l`b0 UD0G?86 O XiZ*93dJn0#.AғT"M1uX?]ydcpJ=kP#-WN>/&4cer@huגHTiRb]jiZiM~6yE%9aBtTXK<ǖ__M#-46LGb!im 2E'BYf` RAI|ɒtn5вVT%tqȣ0hNJbF1-Q6i޴z#-8ayF'=tPRpwdm+.^Jq3xZś\CHdNUdDߢq)P.n$@R-KX9WKáϪ>:/eЂJJ֏кpXŴ\b#M@/*>KAQ/ډp@}>kO;s=`,!"@D?>܎;|MXӘ[7UiKC߿>!>(<@$dl=VHïzӓ#.sC83giLOM7<~KR_l.Kid]MDf Hz${U*y}/j¦q 6P#+r?E>SȭMzFtjBe7KH);s4?! CTF2 "!g^m f9HG- %xSoW76)D^#g<q{$.Ow?4W"hrOqQIB:ozq8B|1p*4PTU1x>{:;TiOzz+omU[TP1W0QAGD6%#.(r[#-> Ćߧ>[$%m}_lCK[OMI@nqJv;Q̝!vKJO5l=;#-7#΃>#.9hakE]~L xLo6;R9xxuwwf~hBK D,"#+l&#.!,#.u%QRA8y8W7w6EݧyهZ hC/A܅ kwޟ}RNU 8vauA:@@`)Gz!}7wD+Qt=mcoB4Nc@!A`wmxOQ$>EfRbK~jHNz#.ާyGJ59y0i?`9ZZfczfB#J/BB*$#7C3f!@.* ˂^B#-tUO9QH}$nB=tɆ,ϳ/TdAmڤ#+U!5HD{ YRmFo;wGticvD;HB;R~P?t3{9z|$x#-)B0>p/Y9tAYTv $Kh^XE45ܩq}&Fh~^?VM#+PCB?~2b~{?;/nHJ{\]*sH-ʏ[* Fڝ SyI@9#+Nc8H.b&?_֑@q]zǯKej"E "ޖ &f@ \z #+ ll0x)Јxg^^#+Av_}U0讧i%:'ۻհ{T/\MӜƤpI%* :`r t8#-[P@D$ #-~ 4xzp>^ڪI6#+UP!h漸||bpSR"#(#5W#+X/ <#_5sf>BT;,0kC۵!}dj/=p_벓WNhmFp=;9 =Aݐq.dH%VeU-Ëó˔tu],'>o#--' v⥍'J䩜?1}F#.DlV BBͽwPߠg(O>րзMe#AkLRyc[^ڦ#.&*-/6s[7|p$dTEiDߌ_o)s3M0l넷srgo{ޚxu -Qύ7SdSX-,ICcbT~Ah3\ !AERT2ܙ6 BGv*aSR@˩Cz+!#-/vpM:}闅`X;4=GaշFn9'۫-psbgxCJ8׉2*DdOM#+[q[#+nb<2B`Y7#-t,2D.#-1Pt!YW|{Ha@#.$QdIuCxݺOpѹxm#W͕7[mB2Ό'dpֱx.Rx"5Z77JF敧^a^B'WM%d@42}'HT#.cvZ,AI$;g}pJ뵇zTX~!s1 !.waa"X"MW۷#9&wl?g;~i8GyI?jn=@(a"=wBHD wu+KyϖDeȿXA 4-+U k:mh*l ACZK6E.fy?ۋOC)Ospң#.~OW~gQ&šR#-u1.Q`|C~iZ|3goyg'YG )>J6ϺSIdsml$H~/@A*q/9Uqh!!eC:U1u<M;t=RkhĚvZ+ ]'xH1"x ̨ZȪ#+d7Ҕ mB6nnWc^.̯pzm`rF<@mֵgމJ@s{櫸-CW#-"%wm̖& s#Щ}%/Z˒.K|ijF+,D5`PejM:lRne`*~b 4a5'З485ТNLTR\)I?c(f@M!Ml^ ccb3KLAfO2JapGXo᪚X91WĞƭ䕷p?0YR)OG1MDqR*wԝ6w9s/ y[z~@Y#-i^30BA_O#.dѮr4gMuqa/癖T,ӕLou~Hs [ͯ \9wr#.<#-Jvw3t#+T[_ـU{_Ui!pWiJ;&?@̔nC5~=Q[wa䞦9'/!`?}񁟉q*}4 Y#+ P#.D'^bMI)`$n3?@X|FD=#.6BE/>r0(pe돹Ҏ$蠟#.Ջŏ&{go.y?$eꝗ}כ_*OK?GO^x\+N<~RD13F%E^[ud s_[{k*k0(662uKC,4u2ot0SQXNLg5n#-,2^0qp:)0L]Pdr۫8l.ax%KK~Q/?FSqs8.#.Sw=#f߷`;`N؂ -OZsj[xP;Sw|w-umMnsኞ9-@Aa4T _fhg4ΘE˥7ykAJヿ-0ݖbuXL ,#+>^\7T0AJ%1~o&ٟGY+_V37UqƳOT}wƘj6Q9DK'ǂSc/Cw7w9N#.3P;:۲_<!"~D~8DpCK "D\w}ʫKf): n\x~ ra'Lj-Qo;Q`S^[ڨDi.zylkǝ+:VeH=Gۀ^#Xg`k=s5zCl" aJ#+d?bBٴ7DZRw-b]C:ÏJVw85-!q+MSggAHL|csw.ݘd̆A.t#-{P?k-!ò9Ƙ=>-S!/D3\r>[zV"QGA,g.Hv`9a$`tsMS薭zph5T''sy,h"TalzLT0#-Y%)Vs()DT/Oq|{ ǁe >" L'V M#.Bjn~-ۄ#L?#.8롡9E_|,2jMO>yK7@?5LAth[Ҫ3S#$c#-˼v;M$eYy!ᛇG`S@P~#lRbF6Oz\β9նIUI:*``:[u<@U(%fǴ,fe䦿t=N914?/ [v[eIjwtPaM{#-.k.>DŽ*3AfXWKu6/ G| #->˪-!vs,oBwNbH@owHS-\\`?_`2i7W:CO6IJUBMY*gBRb"X\UꢃAj2c.rI3DcR2.ьR?̓"ͳ4fbo[J0ްn@ln?1~䮰AԯCOaQj۳>0ȗ8B$cAĄPwNPoqwi.vHῖ73=  Y C5QIs45#-h b+r dP+mXHSк|s(Wޛr(Fs;!ò]SbT$ ɲpش}9<#+ӵ#-C]RYX9a1MA&ØlM11u{! J#.J&vv q5BZ΢wp/ܹ%\2a}Gnm#+dޞXޖV<*o9B#.n3ɑ }öʼMzcIAxyQ8_+͞ +pd^)CB:{;dY<זtݎ;&h7Zuo*S)t;ZYw[,ζa74E#v8bBm6$>5e*Zmnz:m9#+p(yOz9Ǔg^42OXyX*R`'ЭAg$S;N§{"feCڦ?v}fSz0 $TۯgvvzE..%HUD|sFxc,{J(//)1 [7pAk7y\L:\ g9;͖ K]փ%qg#+fx`RA"@sэ`TBe8ufjWC.oDNsz #.=(==AbMTjnz,r#+rxdG檈p@ᒔM9yG{t'Sl* zw]Nŵ $#-&;6/r%'ckّgNNR-k9 d!iXP;!ǒj<&&H v^B,tǐĄHoCj6;Y"T 87 2wq3jHflNJ; 64×fVA8S#N ,No"Eh,D(EF#.R4-+УΜEr]9%Цf%L̼feʳ,̒UٞKé#-Vֽ[8\c˗ɪbIJ|jX5OI+͞v^'Ֆ-_B88m_uPlEV,W^OXk&b9M [t/='aAb_ :["zC45D=` ;MmSLQGRdLȉB3y#.Jd&a<Uy@cN>#.F#-#-#-/SFq֡esHwRWhX~#+tDTj-*㶐3{iŵ7x#.YI!v#+pJP80-6^S)#-mZ#- Eır}x# l}AP`,*R#-Dv)P8#+z!b @D#+#+2%gK?1͉  B:~UwV.0rnp!x V3鄍"#-yʏ,6>9؈7qobt'e8EhB ]l@$ @a,}m3eFB ש(@>2_LR>M־܎:m4w#+\C*0@4d`EW|E#.H,`B(!G#.kV*eknd@K~'$nvdG(ijI#+\R:#-a)`c4 C#+C`x(b4+iR"ELbޮzm^75ؔnJ^عcsr0L$CXsNZ,2*?#.͊0H4 BZ?_NP:@m&dc"x{}sU-!%%4_r昱 K>g /vE.Ϧmn8d:D=x?fӹGH"2&ǰ0$Yө·+'V9^BG{Vky꯬|Ts2S/%s`(`e\#.25LXIDJ MҧC4 1@1"v@;&2^?y~NU4}zD YǀA<ZmmrUU񽰅.י]m*=<6}Q_ ۳orfޚxAׯL8C̖ɫn lq7Rs$Pgdbg5cYҘQ|OЉ*T͢ #.mOm&SQ"5>P Ew Ud@bD@#+WwU*ÙhuHYw[-'{x"؛_3f3$oR,Ax#.ݯem>l!IK#)QB =k@WpbZ mJ-Y(Q%IcYYX*KlkI&Ti)jVkl\KIj:꺡Ӏ'8 AAIzh| hLLӹˆD[؟WU̅ ]6M,F"7/C|(InQY)o8d:gYTƢ0$ED42V tM#-%hkIb/X_Jșfw#+)U>p@E#.f`zoI;#.2iڂEᢁ+*D`X-uA~A΋ٜ]`@4!8@(' ** H,#+mr-EjԕnS6U3ƙd9%oo~'zsr8U!$@5Q}__X7qŹM%20OcЃƽ;)"m$>Fi{}=?P'ˆC!":/b*0)#+B4P@#+!DSҪ7w}},dmZuiC1D\?~4d>>O#.#-<#Ņ<Ka]J&2;H[ԝy'] nbd |1`rǖy'[nK(#-O[AOއ fQC7vZ1D(jkc$bwgyۥĥӘBˬwaoS㬠?L[eu{zߟ#+D9AwslQݰݏ1q:R>JZal4.aC|dso .\kP~zt.lc jtnuCh`2L>!%422eo?b Dx;Z” ؝ZqdVC)G#.wf!!28:b I J7SȀB")*T=Tu'{sO)gJ͒_.??R(Nɣ W~*ϣ&䫻ܢhkoDY3@2zv8-v28C6r@F^nԐ#+뚾Dhp/`~p9@/R&p "<)4eʺ/Z xCLQv[.!v_~#.x |.^11T.W ɱ1MPXpe^3xdr-[;#k΅{^1&~-\Q#.{@ueK#-?nURUxr8H[dFP%;G|uZk4= H()}QIeX1zg^MW@g#-~0 vVE&۾}jvڷevujS%)>#+036pSuafXgVv}x ,L:pC #-ϹƽǶ#--pEfGvw#.!p|}C#.\˙ kllvT6d!u9,C4O"qW ϘwdO#S`x@ВHn [fyD|#eCM6p.nnCbT"Aa a0GCjcf@9vպ ^,IRhDד%ϒZb^=ku9016V <:WỷƎ|9N҉ qh0jnd!9>}02%xƹB|HX\:FȌy4Y,-c:l&dC|/if2#LV,GWC{~wNd۰Luzt#P!0#-7b0]SӾ&-k9[z޳P|c84CVMH?_/s"'f3BKĶBC#-FTƒ)ABDI>p&&ftCФLBhջ' xLwX[I)gn:\/Y/с{a^).u))h"W$RƍuNF#+zy#.p?鬙|;.cn. #.J`x`5<3X]˜[!`#`7I&ooig[&4S$.wy}3|ߪ#+ Q&P*#+-BmR´z ]?92&,DA #Ctcήj\sf:'3AQ4QKC}>u9NXff`;~ո`V%[j;ie.x/n&ӵl/QE>z7@Eg[lmdNv)?lr9I"Kq11zGy"@Z4Klfk%[m##+ 4-C;#-a w4@QJxV @HX&;D6+83DAfw!eWhXEU*#.@#+4:mM7c#!d(oYEu0&D}FG  Pv2–Ž,f7LZ켤[rSeeHV#.;FEAsdW[&'0"3|dG%duӱF(L}摔ӆ#C Ouh Hxrd8'#. PF^ۻ,wqyg#.0&(}eKLsשw~xd #+c,Rl`9f.>mLy>Î6tx'5|aXwtѩjwve 2#ٜHo;Y.EF7F=!J1ro7e(dNy`nw^fҍ֊#+QanX(bshI=k@Pafb5BƎ$`%b&eX;;0e-@r1#+权G^N7DPA##+!FBsn91wLMZ]ZCۂ5jA$Eԕq9# ڲ0mG?96L[U)dqts/⨌^q2{6:H'd2⡊r v@nxN(N#LSߵh$J<[rhh^*-sZFd$(h]V#>KZ!H{$"`Df_k<<+C!SL$hILe+L,iMQi3PJL%*1Q4ԡbPZX׷nhd֓IE)B13LJ4"RDFY)4dIE0AhDFFlb&5Dd)2i6f49vIxX @L(|-ٍ|?~0hG){ŒL7Z1&heG^X`I-~&(GLuN]hHWpiq.*C.) 6#.f]q$8h}.']kZ)7"qdNNu`\GA6cFU2#.Ti&EEdIR35h6KLQȞ:u311Ek2A,?Gg LR߃;>#-{3TTޯ#.ϐiWL׻n39S1%Jgrgr_$F0&>z'ӽBCnSᐻTUS`%w #-%e~vn]GzgסbsH3uUXuj0 T(U$I#+ܙ1 ?#+Jtv%Y;6gDm>x1Jw#ۚdq;Ï19|xu,g![UO֮7TMag]ڍo#.{bq3obN2]Ha+VTCuCN(*iÞ1QA:Wŭc(ӯvF]stih!9-FK6A]LQJz $=2𒕇Nȭή٢aAiR{#-`@1"gϺ#,٭h(vFE &z=XgS{ #+ۅ&xWnC#+#+P(# :sMD׳>gA8]rii:ҪC3}{fJhޚUFP+F8/?DoJJ<#+]薪U:,ؙu5'YɼG̅MYo.? ` iANEq+DlQm2#QP9vڔDө=O;#Za\kr1-ֆٓUŒf#-3#-s:LQ桦ek/9r8t,M5TԦq[ޑĭo˭S1V2AfĬn>d8̭=7KŘ4mYEMpS1R~n,+mfyp"5mS'S2s%gn^3ut[ۗXVjr:C/hr]um1 5#0MZ;}$9ܑUN81* L\Q#O<T}`es_%4#-pj`|$;0Iґ0W4U2ѹM6W7:ri(Iz.Ѻs}#.3>-jYE aH$Wɭ ]-o W.{01H#-d8Mej#˯i̹Ԟz[Q3#wqV44.QoDJNb1\>*u7q3;mz|B7}1mCr#;B~t%%١H\XFv·38pbѫ%QiK#!zB4q[Ι%QNJڴ&F Lc R-ki5(}m\}Ch7jҹ#.a#+H RK\3d`ݑ/MD MT^ aE1d})y[6ۍr6+8g!v{QZ0V/Q ;d3$! Ed5\nn^SL&˝,ߎ]̕O,Kr6pE&.HNLܢ7L:8FMb퍴oTsٵr$q favfS.Lt4׹"S΂Bb%0MTvCu !W#. qIQ ̾[pD& MrإDaakZ9[>#-PЛ.#.J%S"ML>?#ڳMZ0lJQJaaB>s!]390B P!أ;x.E-Vtߒ#!#-nޞpsN-j95Y9Ŵ`۬}x7[*OfLF!ȟڣ kuaD)&&;rje×5< ̇#-[4M۸JlwZj:٦o:{[Tц!My|oZG3Ԋ-͡q2U(*bOӽ$yἜRب6=:uNBI82-ZAĺ6,LmI$,Giܛm{lvSZq8hAN(3Qu29'.efpBd!$җ"UW#-n`:9Q2Mv˔l7;v#-:[!͎ @:4cC;Nlh}%`V#[A8ю0`pGEjh7#+4aZiLv0"8o|ӓ2sY59e IJȠ Avn͓]{"J@qb fa#.ͦ!1!6^lЛ#-20hGZ q1dZ EhɺqoP6K.f&3PSBpz{#Z[x8o2A8TX$GaJt=#-Hǥo3duSq5(7TP#K2jJ3"q,#.5pQ #-N8HPhC3K5l3p>L6.-Hurf"2eIYH. t!(F+ݍ27C`!d2ppvbIvgN1lf2\f$#-Dhݐ4l:S1`e뎕5r@Z6Sp#..] mѻs5 &HGM&Iĸ -;#-;MA)#6l9p 8pNNM#-Ψ #R|#.$B(bf5(h…#JDg0C>\ w+$$A2HA A>UN2 9#+AtR~:F@ JP $&@0-YyLB976<6! Rؒ|iuT|3Jhss81] u,ťdL]vmR^Xb:(yq.\gd鲶Mr0\`uA㉷yĻ2fU! +gji;#-?P5o.qĐHzHUֈmF@;a0A@(PE,Xb_ZDN׫R!5C]K1TFE.P#+!#.|]M>yrɂ#+DU,Zv.Ʒ]ٗ7]7lWO/˰x C'"16.Gz'#+8#.(ęy!~$CPj(fpE>&X0#-痏nC^o?-$\?3<ܟ}69n#l#dޅ$1"؅H4ejm Ft e Lfkv M1ȅ4bޢUJhtpas0Y#$<;,JJ!h?K1O~yy˖%.3#.٠G|ha 2#."1l[cFkEDD6 2Ѩ%dI !J#>ci*Wbv.Id #+MI(`v"=]Y cBK#.N)E#+ Hn '9n #X:vLvQ83IIF53pE­rh&E՜,$z>i:/&GNz{nhEGK]7ѡ5-j[(Bb554CT\oC6BTLAc98tg7Bn{i,39g-ȑ[q.NBiIxt9+PunguHTJH2c-rgB,5AGN2I< \9)'p #-PNt;= Ҧx#.d29Yuv3c exaLhS(҃cS!N T$ 2+#-\ F "?@"T˟;kֳ2!_̓>1Qdd&MPj@d Ň?񹂤{f`mJvCbbFJ=Ms0# #pYlӗ͖| 0EK[#-[pM#-8kSk;( i&<> U" ~5u N8Υ>?CBoȢ#.[OkWjvKQ&n‰YDMmr,&+{ծTie-FŤE4%dQ)f#.Qj#.Ij3#-Mf4ĕmjkjhQYuj6Vjѩ)m)I[jiUjWr%F~퉲(ɭHͲn]ihRMJ6מvIiBfcS[#.Mե1FeRڙ45uf+j]Q1*&&ٵ V.?ʬӗRVc|:K#+G(^7 9#+KtڽkÊ(=Z0KT @tN+),DF13J"I*0H$R @A#+&y2ZB"4{[;5M7u)j魱W6*mFOjS0QM)@jkYllƖ(RJMRlٛY-Қ)R|V`"JZʢm%Qf6f5&hi1A3Ck(-aԕbڔ RJS%RVLk%҈mElhY#.RcAIJddԳU6j#Z)bei2eɓEJJmͶ{ڵݫiYmYjCIoyk͚ڔգUOeW5Yh)lJU֬k_QRX׸ۓȭ Qb\ [pm3=uǎ m`0SRX#-OAf)zIRdyREՍr;lfޙ|C#-#-J~έ`qxRVvOc«w)5e;;$d$`Qrس!BFrdܴ5X6Hj#.+-1VRD13T520+IEI1IoM5\6pmC#-y¸#-Tz-GSp|<~P:N35P p͡UYSh]%LE_8Ю7yzg֍T 1ۉvg;+Ů E 'o6{݌՞[F$#" Ùb|sl~]K-m *$RFgA,} #W x*&^۩$RQ )EU! Bw5jvگk+IkSKӧv␚s" q. )첊ݝ'$z_1㮈m3gۻ%P`]e=]Qs}KwI̚/K_u WMғk 2_Q&Y8#.P:U2h3q#+-(MP0+RmZE*i4]*ɉ,ŨVF5R[J,XoAU"T`(Z("#+( 'kYƖI]L(AKP 2Ӊ;"FCX3iߘh%lc0WZ¢Ȗ5BpRiϣ4,~X(']W4AE#iJNWHl#HL8dTR\kAPRȹ!rblAcF@m{JVi퍦EF!j P*2Jl#-[|ɡ1ڨoF¬Fq8J셫zTB#+:+eeFޫ|M-uRE"ȥƀ"B,eE0>ަ#+a$e#-s``#+#+:30#+ђ#+P\2L>ئwFXn"Q#-DW |v%( "Hd8 ܠ{Bu%9be0/|OyARӟ?Ԙ!;LOBf5ߣPϬ:v#-F}"lq bQR|ǡ!}azC "H T@( *" 0/"!\V3n0DYq0~73%t`Q̅[~TGyԝGc7/=7ğ`7'=FXNOQiɬN$dTCDh!~%6ރ=‰dR(Z"4v.xQˋ ԨaA3K}c#+ߢ>b[x{?LH#m+[#-9eߗƏ`bT܉!$DP#ae-d~]QdmI %0:8/ƵDp%9`L$aY .#.:{~#.XAiH6sb;I66f UxኒeJ|m0tHjLK0VIkc#4.h|^{ۮvl`EPm#-fVJr{wonT󵹪64i .+jHAbek7 i%8H%&7-׻x6*D#rcB HXj1kL4I)+>L䱼xaEXVmVz=eXeU1wW5\r[bL{ZUJX+e&̈XR"K'+PApjӬ3L tgR0e..3u,PyAH\؇oD28#-@ 8Z)YXwj"mDn,e>! :PP2;h|imOUI 0؞\݌pܗݲg+Lʞ+nnQfue7^LXhbP.O))cn+r9=Cxx-#-$#+e#+:шM?VF5*Y*6jTcMD/UF5wjUKp L% Yt#-KR$OO"ôR(*B"BJ~gBkPEyK8x},`yHhyQ#.E} ZJ#.W7i*078  Ci}J$D-U]Vɰpp9AoUQ A_mWܷ/KTR,s\ךEVԆ6ץu]W"B*k,VD]^te*9=\M}R#J4\T-\d1(*Y It ޙTA5CP#+E>88Jv%RYfz>w-*PӻJ4ҕ5Xچm564fy+cAFIQ,O."yM@wb \ wj#.).՝F0wrZ#-(}?'}#FiBdߪj!y0YV%0mM+Jd+D.نCQC[͐#8=ɉDDaVdUC`,r%PJ];D#+9τ?%&$@mubQ0nz|i(u'#-Y:DcMҸ2P~a8Beʌ9LQ0%&JiMm-Ih&3#^ 6er-,J-S2lؤSY&V,mSm,Ym]b X҈mL%k4M-bض A$DHBM0`QQu [-a5p#DF6E]Jټ£kҵ-A'"vYDN"nlT6(K~kK{mHZ5#+62)#+{PFBA2*-4eQaH Y`2?n|<\ 1kԪ=<#+Nq@J"#+|@D6#y!ĄJQ*^!t4#yxE<˟2XflHhGwT6JXF̻ >D##+Xdf d-p[܏z^dM#+g&J+'a'FxA@8-͸ZNv#.ƞ`%j58whar1hѲ?<)uW#.>-M=8A;/6W4' N9OY Y8FH$Qm͸k?QE/6 p)ߑg%x*gOnC|__*#N#.)%#.d=O:Z|lW(62"Y) _+#-T@w(@Ɔ1bi|m#G#.T2ʥc|߯bLcD]blN9anPN$mW XcI( 0J6RH КKȯC6kT^ͳ EĈ)Bc*1ThwF'T*cu)"KDL(r@GN3#L$vEHϿ櫎8KK98wƆ\M$(#"aIb!xFu:#-!p"cyLha!e?B;^'XO%J~ݵKɪd6)hYH,L Ro`P# #+l=~staqoL8B9 ?,p#+0:1ݾl雰R3&Htmj RX@2QHK'e'EEꄤ RP,J,VQX~^J]7ytJB<[HHc-]g((|DCT)#DQ]eK#+)vs2jQCB_Ć' h2FZ8٤[HazwTyS-m"c*ZmT6sl-޴wkXC(zTMuq0#|7r_|1똊jr#+KB#-3ZPd-)rމ 5,9# 8@#-$W=ƙj]SpDM҂4t356SxuND3U #-tF!tʬmPd40jʥ=vEH=JlmE:KRM.MWC\7 Lkd 'eQaM T=ϟLY65ЁYT`#+I#+گYn]T^5Zޖ)DTyePykL#+Y:xzgު#+{ a/v>u1c (,#.^9p:BGOB]@nYGC#+rG8Cj|#-:v]!i}gz}眦oKO*۪z)l A0 !h)\HlzU&K/HlHtDTJ1!(0O7P[CEƒ`M@CET "w_1 QE0-Hشl+BlMHѓ~#.mu Lf7mgֹt̼jKUT I-1xsvԚ`MKdc3DDBB /{&؄D)H֩P#Xl圲Ȕfb,#.YujY2IYl#.`G&į=.B4K y1 !f#k.MהiLRBcevJM$ˮUo#-IN\eʵ;8V%PϠ)UM+VY5huFJBR'd\I{`b$jTlS5Ы6ntkmF0Va7fZf`_eZLآ'XT$*t (̊+ğ'~_~mVJ)|I}c=<|Xd!Ђ {+kRd( [bay*C5B!V#-Z.1ŗnŐ"0#H2|#-,nk\O'S3AbsAŰJ+QlG?&xf,vM=,s*4#I6!Y\Zr&TfBqTr#-pKL>1fE@ ~rb,Uwe:_Ymʻu<#+~ߘ^@\x0{8ɴP n܌ZkaF4 #+,ˆЃRjPHVmtE#_#G;5**Ջwwn#! bȘǧDD5i@,A6b{MCmJ"Q67H1ᅸDa4ʜ͌c#+fL hc3 jco.q## o C+vH!p @67v:ʇ%ekEko=mMI/Qu[f#+Œq(ɷ,<#L'<։,]%c)Y<]Od#-Y`ㅳ#blY[f7Īl)b #.X"LZ)NlpL̘>|#+ǩNewbIe>nO\tz7i6NVS!YJcBnF& jMVopPJqKƵ!L:*B芩#/ C!`&/ =zϟ5L0FH0DN[`uAH܎#.),-nZP|%; 0X] BE) DH!A$LAm: #."ИTAE4`iT`ۢSob:r:`vGoo: FBd}2ÊN~#P5Cc襥!3Ht|31G}("SlZVH]=˺Y8gmRrr5x;]uّʲcԻaKoCCmc4^1Cfmlޕ]sǡ 0l;4pЩ:kEoƆX, ɒ>D@ʦl!bn^t8#6"n: k3 o49:"Hdnƻ;:M#.=sPX_$>ZqeDMlH$(#Hx]WdȂ!IN jpN,l=V ّOc{q}k FMvmu~lkm.#%>آER@<6rOQ4;W;x*+n[&;-kιtmͻL:V<3LHjjwuXJMzZM&vm*7B,BKI#+4!7XQQHQ3[EjaeQ#R0#+"I.p)#+6r]5s ʮ׵ F.T1R|[e’nMufQR/TK*4ZѬmfmiYL%R#jfM%WoVH~0DE$U2#+X,>÷|}yiy9+Ԁ66BS)X o-@@,jpkIkFWPF?,kp_7(|cɯ\BpA^*6`*DTb=Y! lyGg#-k4)ec+iLH1'ftpI<ܛ0Qqu'.o]^^u [Ǭ9{w5Ai]%}NFЦ6d͇0s"\ӲKPցxMðhtKEH *Ӹ #-1.gEXd^w\+ a9 (Tٜl6C T&*ᢦp*ΚBr|M(!\pj"rVdPR[|JcI@2lB^  6 殴0x rOՁ @[_ʈpL_#+Ed$ (mʫJU2 %.ST 5U2 ɑ?0\+qA!LEkjEɼm=xw}k=t^]IjS[4 g8Z8ʍRKh5jR ՅZ#+ (0RD#+!BTi/Dbf Mp$oXF. "t g{^0!81D`zeBFK[|4d uP9hd~?ȼ07C&zN'#.@ /-_0^wBxt9<>%cNA@YvoLFѱ Y~1:>TAUuިX(#ŷ0覙#-2 h!;ex'uYѲ]t*0CZxJ#- TRK؀"3H#d6 *Hl="xgw2X7sL*["PKF썶oF N3\l?d!~>RktŪ[JZ]URI* D 3v~OfIА4pRհYZ7~>,R*ȰC#-ݑ#.J%5QwlJss/76$l)Ԟ6f(E3ca.m\.q]t.Wwno,o'n]dۼڦ֌:srM%jRgr2d4Ӛ:k)p]YQhZ۔i#.AH#+nlUܞv"Dvp;Od8#+;OxxB$E j"4 's,-PSul4P>(8 `} P *}{/#.Ҵ}E$ ~ώqn;6#+z+2HH緫ZV-EwV.]x"s*` ,"@ID2"{Ot/+MVWzhlR57-Ȩ6#.6D\("9P\5;'o1;3 )􈦓GÔ.ҡbJ!?懺6&qwH X(ĊV@R#+łGˢ@1c;FPɚm#.~}ٙ g3 $D߳kZmWqP@-L꧜!_dQ9nPx*m#-J=wCw;=}u>Y'#.<3o\ax`UMOgr2#- #+V$J`^HOu`i]]mqI'?U8~X-ڈu$Y@w#+Z =Zl?k XQ ԑ,I1P`."DP. Z@GL6ٵ]${mZ#+ekDWAx4c0f+M#-1UaPБjQ*\mxX(&eIvlUWy kyՂOoPʫBЁ"X`\0h6<.SL$yc]ɣ2Lj R,6Phم`!Q@bTZڭ[knn\Mh?OrqߗA?D``""Ϛ'XM͟8(y tQ8x?2/bP)41ǂ:S$[bDR!'쐘g(F?JƌdJeAGam!Xt4"#- !%Kŭw4WiyfQf-M*{71T F-sDCku?S~xsT1@f5ʒIUbQukY6.j~k|5vŕ܅;䆰Ģγ=9PD'Iy943cb;SDQȵDž벉 YOzv4 PRUU֠d8<aM2tkPwnOXVZaÒ&cۂZF}=ԥt+$'aO9!PHb^qZ7I$S6DžvA.S,-]SUOHcbbtoSRzs7׆0=:A$x́NffLJq "8#>Cn>ҁ=#-3Kp9/mˆ:ێ(7r1rqBx)l|&[hM2{AˆC;hTUI|Һsu>HAo4zXހPQlt;ZQt;Q*@ j1+cELE"#R0(@=)VnqI!/ƎAZo)D܄W8_̬~˜zAF؊qݳ2U#+!0Ev@1?NWpS$dKˬJc4:ݚi(ߙOt }~U"#- M>M&?NbaG#.QDPQ]J`,_gm<3(ʒHǻ3MQ5IӗLhb#M5#%Bfɰ¢,b?4sC,{crkm376ƶstd Ҋi3ͼaiw/LwˎҠBɱ(!^8Ъ8%'4 dG* 1hHf)tmx--M Vc%a{8i˃`j'ΩvO"zH]@.[t}7frL?c:R`ۊ]mh7ӄ u'kEPjM7@0XLdq#- 9%#.(+L 0RĕfTUrW+ntt#6ٚïbd  +lƚMc vI/B*% |j6H) ƒO#jirJrU#-DUi1QYfl3sT#.DW FQEh9iIa3&7ҥRG*62pB.f0Χ ]٤EfL%B#.D3rz#U%\mpXoE+0$WE nT:(񩤆b o0a)PY#+06)-#.c#-*JFB/co$"$2siDª!VaMD-Ha㐎( e+So=k^m=7c۠5`VT' UZtx&(Ю`1@Qk9ƌ hi8EJ"dBC[SR1F׊fk*<X4f^Phr#f 6!Zդ"ȍ084[!IL0ҹ҃A&LM:W$azV#.+ұ53V5vdjP†C 5҉z]=WȀͼX2if)l(`6)bH(]aֹdA"|"(ZƐhQ^ݘkA`0bШhk45Ed{f}o\RL Q59ED0ڼ.{2:b1(ۆB7ޱh+9٢[Ueb-Ke6468$P܃%T\T!š" (@ K `#.6$2*LHؘ@/i,܀HB.?"kWW.⹋wpݼO#-Q^wWK~\OW#;w[毯i(-ءjYlI|]Cjn8tΏF#+$$B>{}+WiV'ŭ!ە#'6gwZ6괚%41RS@K٥ "CQgz1|/ȅID\R+':DQX%f9Pi #`fH؛$7omu҇9GwJ .P0n@b‘fAF(SR˄:A-%MhggNC60F:/%* -&mvwƸJCrlدxk}HILԜ!YVl5|#-5Bgtyy#L_Hp3m:o_ F(>利 >\rq%C˂PTPᓡ%g&~φ_+ءLM3 4}ߌSzG{S)t v]hv`@~o.$a7V(FsWCbO1347#.zDO1|2a#-4z(u PPxiL״RDz:r/EtmGVR tvY#+M}kYmФxEĪvf[dP-CmA~#&3$U)"*TI.ZRQgoR\FHE,@0àcqb6E~XksS#-%n݆,&)(21" l(&kӖ[Fb[wkoMn^*+mk!ljU]7+rۛ[;TU&[rUucUoꋡkX6 o'6E~8!УB#($=ɿ$/eX<4pE"؀ӊṔ̈Z0xxUfZo#.#+_ϳU?y>t&L1SW2$]wq6zm`6U#+A#+[UZ6#.ʡձ[Ȗn )2V*.CjjYFlIklfD?>@K+uR/';:QT+L=("e/U*zkJ(Q KAA_vL;SI$TOY+&t:`=v H*emk)Z ͒Ie#. Q$eI%)AI32Bm*VV2ԴjeJd  {-l[C2lFFHp(41fPA#j#fQA (ŦL E#.Q c"7#.*#-qX1*B*6 `aJZQ@D!D#."E0$n_%PAj,`ş&w (;KBnӻ1u+[vZ5]MjmImch$m8T7D B3ry:}W}U-R{$>+-ߡ!L9U>҉@S#.hC7RK 7 6-rrbx98( CT?˺{{H20Pb_[a#+YHA)wDE`E0n2tW.JfPmwjEcZڎW ծ[agډw?ά*cIe+"}M9"Vna!%y"TlKy{?C`{ՁU)}(zvTT&J\r\K5ilQ߄moi쵁͜px~ &~;ddǪn>ַN'4&GdqwYGh&/VŠ`#-p Q\/akF{Z4@{ETR!#+ҋ` =X:bh:՘1U,`[N$w;ju0OS[6(K Gl52kgoۼڮ_V^&9gsP=ĵ2=ʷA9Ql. 1WD]2NC=vŤ{N=,}2j'ZV')bQ$覓J`MّlT'$ xƼkÐиA5"CwL=v'K4zzBH3nJHDF7(/%-D!YMo[]FrIvqĝBFz;x[tH*d΅]\.6=c#^IFQc]K=%#-zV3Ĺq<ԯw#-ZgP Ǖ.RL8۰JkBHyRv4"$D'/OHܙ:,3Tt 9aZL#-xy)#Cg6*%Q9jJig! Ehq+.]pid#-]f+2 ڧ+]4Rw[Ai3lC92I'/i5Ľ%}Qd:]{Τ9d3'8zs:y&tAx`/lY0Lu%[1#ɥnHHilTf@%= pT:y5жoLQFy#+ݫ8є^PXS9J`]#+#fZ4BcGl7cY7r:(eIyNJDTU0k-IesAIfnpk B9#.)B>|#-t.1 іZxYRdB8Aor!ף]AǓ0<^ Gmz3#-c1Jz616#.&4yOܨvf 0,"!04;#-0!8` QнAo0"4D| dn*uhlG@4:RB(XK3ݻDʄ4 iumsi՜G몼l@BҒPPR9M9uI##-V k*U`rw1xATrLW*0R]'AǶ[Pq]u쪍tR;ϛ`q yH؛q̚{3w)qg#+L5<+E#-)bR ե#+ϣ1Uh/4[:̖=#.Ӹ=g DX\ኆ#.yd4T[:Cˑ,!+qGq`Xxb(Qm?tM`a) @)su筵VGK1m#-]`EfadR崈Ab: vp |#-f<`t.,18b.z!'ɚ!X"tdȍ[GjюFZ$WaF &DG{cMU \l`nxdoЮ5)!PF#n761$:%-%D ClBl5{}d$I#-Ԏ ؋wyjODS  x@C#.ƶՒ2VdA#.#h6((Go#+Y(EHMVwuAFjkl(2h)ٔ-S*4dMDFٌZ1fЦ&(46JFQc$)*SdSTцL6#-D &5kUr̹홨fg;PS.Λ4Z>#+s6~$$gAD86LJVu][A- 2#+6:jI#-I݄Ķ1)!``ޚIwv Ac$G.`}A{@0bzr#-גI tu{AAlx=]z4q ۨ/_WV66K]am;C3U-[ETdIDI&/m65)&(640(HT,%d@#w hxeBk(dQl͂`SaxJQjf##-.V4Cq}t`ɌS$Rb) h=_\ƴ`HZLH/>I-C4o:;pqo:wuNS*&t5[}t9f"<1!a BQcD#."RTB(Ig-dPj4 =$bl$(82dRRK“17-q&;k{z'r6m$G:f6r1JN2Er&IL`d:LK% eU,х#.*g#+P8J)d#.KH[͒DI#ACå44@+m-=({;8˯/ ?C㻯gQ=%#-z$#!?FϪZW\12 UC5+a9d`# Tԕ &*PS?YLm1X#IC5jMWs"!Ym䢍fJ)R͵ZPS|KQ|k֙&#-7 Ҷ]/`mirZ|t~7C6a$25dllp*5%4&M#-AS/өq\i2Ћ:vl'46)Хto{ "/״۟DN37}2oCx@wABRdc"#b+6=Ga( 0^$G33=Bb}Am IDG4Š~킩[~U~׿#.mI%z@Gq1/#-qPhh'tx*=,f/_8R3>^Ɲ԰WMp#G:ݕ 8C!uCc^=f#4_@OOIg4 :{/1 K,+}a#Ё'4ESNO/mFmnd[wofXqBKB\+kf%)df~xN#.f^J*(VAn?iB%p+ɖv܋w.H`URb2Bt7ڲ0(A]kA\(&'=13 /Ha\eXfǝm#-bZPC*a,R῁[iؒ4ԍ}\pƓ6kƠ"Jc3#+ Liv1C|e1#.`uSQ ' 04hݭܤ*d!!̐I:80Haww#.7P'MHHQ#.ӴSH`Al0e7t6"3ô0S!20ɼ窚j"e0/ J!3-}oqU/0LgF2Â^z۸pNrIvE00,0nߚDLYI1^:iS4d,Li:% ZZBB<⤚xDKΡM[[4E̫ӊ4̡&\es nDм,9u@oqppJВ-s凉$E;1FGSk9XN|e(͆t{OA5YF=#-;;-#.t9&+LFe%oݑ=̙,u`LCpÃ78ڃdԟw5͍%nWGp=]sr*΅ii+BFčW'@47y%64..0E(Xw0t8:tc,iA,;؆+_ dhZΉ -3f#.r c,#+-#.7xzfM+K(y̐>&a@I *ǒ+tll(ބca*-)E\6vl?=nMرBp%Q2g w𡨕&%0vJf[Qv3lQ'(J4Kg)g|7>Cw ld9>>K3"#.kU<6Ѧ%b&&]Y[,qS%##.Ti moX,.̓師a-kzmTBRBlUh4(LI[_&L;$t M7#+L;vvZ //SJ 1 Hq!Yc;LYI ,e JJBSx1MX(`L9bbmh ˁ=1#-mTV w:bnVG)p.8g:c3Ό:N,#-2oeg{*YĐO\lN2A˫& ݻ&oK q`kCTڦD-YYffu!eRf4smHHdhXt`AnC: XHIj(D{ XWJjwnٔZ#.Rtҡ4#.5CE[vKAK8Bݙh6"#-@`l34&C! 1nI#-'@)]D͆f\¦@c`q#+๖*P K#.A TP`綋.T1fKF(d9#6.r*AoDA@a1(kXZEWF90 h3h@0KYact𵅱P0;#-I50J8,,ǀ0SCc'X$ds61aE jm)ĸYGt&X&CMC#."b 'O/>1mA5TUew6oDCӶOp2 |!,"B@M*PEO#.l9+Q\VV1N /V5]/?>#+܆OSy x\ql6̎\ڛAR4SD3hfHC r>)G׻nڹ*=֢/ ܢ%|)WHs=Jh(Gfv0k=vD~^NW Q'v\fR'W(6+m(%iD&ONl~#4ytw:YR_FWZ:mv*l#.9sm[畝g1Y7z^H߅^PIEGF\x3S *}vCد o×C@sF:wuiv"pd&Ӻ]p3wfM0*vZ0b#i`#c#.I-jwn#-p\&X 8K$)AمFƬ-4l~U#-HE#-ҞQus\vӔ1\5aBD{R,Q 'gl~=al1xkpAjcbV[4h1Y#-#.44[թeѢ;CLd4D͡hN"Po(O_@TrȃHeOThۇlfڒ?({/l~$h1+$ khdA-aNKM8`wvܶlOVU)f!P|\KtgL.غD,@#-!#-)OiQN滒BN+NT8i|1z(Ř݌6C-|MM1Xؔ|M3ǦSZ43vߏW.(*amE%[kI"KjoԛdVVeeƑD.@KJ(n\0b16*'dQH.#+46e[#-^],V8;B(H2;/?/ܦXߖf#6}8ݻ@˕uC'~]Rr+{ū{H5J pDd2h#-" #-A*Hi.ZDKYP+uDr+`Fa (o#+ILj*x;uZd#. tzB)H*R&܊`9ZON7L#+0J^,2 FCG- ԦAΫ#nsM@KKP|MW!jRCh㈤l)ؙۅj'iḠjW.>tl#.SVRmfAD&-3o:9$!Ni * 5Jp¶5bVƗkcUՓ(܀#+(,lE*Ł7əu5x;qaz"/]əw%YAبփZbZa1`3[&2E2'  vlx Q\xu!#+#++KE Bd|Hx0a'WV/Q:< ȬbDAAIF|#.#.pмBĐiHTxьV(׷vކ" ҈2t#.!o~gϯoo|,JNh /#H`Rޯsa(OQx#.$d'g7 OpnSJЍm&C  %53_`[T<Я@vEno`n2Eب0 ;0`Hddxhe]&io*<{|l!e-*%nyhx-1w^-V%_kve?rE6q~ϑ(+0=IY}F]➥huib#.TٶQhZld**R66$ifٳj#Q~%#.) 1jj6QLUD06!#+2)'cpH]+aMFma$$-MsdScT+f1QebQlRIzʭ֥MM~]4!{qDMsWq=ó"IீA6l!FOz/mLdI~˨p ôGaw 4,Be=r2o0$XܟIUyƀJɮ&Sd*BbȃY?GVm qK"76;A22РpnBfuZ (H/D(JuM$![RmKiwWd*(#-ͷn쒹NA%#.xʔYVD!,]lm}]# \f 5W4&cBٚVAm!Q,XT\#+{hӰ5y,+PuO}bopȇs3EeFB #+@HSᒀƶr93E(~ 20U {ɝ52&CbR!Tflyp`.D##+l pKs}݁vI1t IOCmg& ccżBӓݒ8_ ѝb#--xcMf#.v}yAG)ļ.#-Lo~a$#MfUsQ Є2"IQGBsGi[ҚC@#.:(ޖ3w3;-\(uKFGACM4lh0#-6;t(h{Ʉeӽj̅G|5ɇNeJXbApC GGd 9]#+eC#+gAF/hm&dF+#-2PLJ(ZoWivM_nuui/Iu5f0 g:c1~N`qwmRaA%k٦v,\Q67@J%hf R$Ǿ`|#-k1*cXiA'dWέmڃp8Ti/0i+GqmtTPcpkCliFLctñ,#..0XżRUGA!!yCKD4/=zEn>&#-ڟa#+ۿv#-V.kh06#+Ih#+V?Wo?Oz}e??z9͏p??W}~?Z?RB0m)H~>gne?)MX?#+*"S+\m4 T*Y40v~9wh@3>ol_vָJ#+Hd,\LJ?zjo4 Cmh#+̢<Gvu:DzLd܍]D)Yl)/@ȭ UфK.*G,Q۬1ym^#-((X⽫#+#4wl*;ǖ>6ivϷb7[JN3^}[#-#.2JS=[efH#-?Da߃S%+;*Ύf>Aj'\R3U4u4H6/&a6h4m8ui4JqUj*:X7Y>8=]4CVM3*č&ޤ4A#-b7PXglT:͕S[K|D"?D?@1To0X" d{%v Eb(}gi2~Ȗc5Ied~pU}BShwtL&_htuo&؅hlXS-XhOQ8hoh$q%y_=7 #-#.'THZH-E5QoJ5{-[bV@4Zw݂u.$9bCfI#-#-!?^YS\( -$26Eo33ocVUy {ZZ7?Aq/H#.ORP_4TH8!'wfbKV}[/uЛ+8GvB?R]xr$ςr/'kTe~Nl?sT#.?aőp7J0{(WQ6g# 3H!*\oR4ZJqTE3oYw=N gAX7 ^[ c~qQ-y[Na ;fL5HK}<5$aO|=)ۜi{8ˣ7F2H_<8[_7/Ɗ)„#-[3 -#<== -#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEjH6y+TsMRfVzL+XRG6xXHc13IpUFAmczp/cACgkQG6xXHc13\nIpUETQ//V9rcegq5fXQ4wJpTMVvKwtldiObdr7R/ZfMHgHvp6LFQ3XwCWsh+Qqcp\nXWAmQNtPfgxQcfGnGHEupp6d1XsLGHWi/zqFQx43qE6XIBxnNAO9SKLTRlJrWuUX\nq09jkP7qrV96SVKqpFf5bIS9Pb0ZtvFsnawIBgWZY8Da//W9eDCBSE0cD0lAhsCE\nwTBIO9avqRlaxCbGUr0UF+p3T4A5Qv2qVFfX6qGCKvTMcg9/uSUU3LTn4FUXVBeQ\nwJmBzZN1IRH5t54b1PHjCjGkLkD6Wr7cqecjEyPvJUzqad75MBLrj4qR+QtzMb40\noWX5zg2QwVcdWOtbR6R0toYE2d2PgvgSHtyvcNutIKlcOm8Me2xzFy8KS1hhK3go\n3cdlz57onWftgAyU0OKTz8FXlPDnQOtzANLRTSd8oaPceF/FdHTPVZNLMJlmvBC8\n7CHP245o+iYEiM0BfV2CM4w6pmXwK9kU5DHlLN9oM0jd6jTYntP3+zwqTRw9drg6\nMDXijWLPFiNMUegz6isOrLnilHtjss3GSQ5SC39fmy94QoqI0k9wQRSQt2adZj7v\nSlWE4Cm/png5L2bLFg4cykDRKmnD0NFjWkNb5fprUM7Xnm4TfgUSCusPQe8gox2L\nGj/8L+0OIq5+1eN/7CVwf+Wmu7c3+yYtXlEzNVK8wkX8lz15TBM=\n=GL1q\n-----END PGP SIGNATURE-----\n diff --git a/platform/asterix/boot/waftools/binary_header.py b/platform/asterix/boot/waftools/binary_header.py deleted file mode 100644 index 827c11e5ef..0000000000 --- a/platform/asterix/boot/waftools/binary_header.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import binascii - -from waflib import Task, TaskGen, Utils, Node, Errors - - -class binary_header(Task.Task): - """ - Create a header file containing an array with contents from a binary file. - """ - - def run(self): - if getattr(self.generator, 'hex', False): - # Input file is hexadecimal ASCII characters with whitespace - text = self.inputs[0].read( - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - # Strip all whitespace so that binascii is happy - text = ''.join(text.split()) - code = binascii.unhexlify(text) - else: - code = self.inputs[0].read('rb') - - array_name = getattr(self.generator, 'array_name', None) - if not array_name: - array_name = re.sub(r'[^A-Za-z0-9]', '_', self.inputs[0].name) - - output = ['#pragma once', '#include '] - output += ['static const uint8_t %s[] = {' % array_name] - line = [] - for n, b in enumerate(code): - line += ['0x%.2x,' % ord(b)] - if n % 16 == 15: - output += [''.join(line)] - line = [] - if line: - output += [''.join(line)] - output += ['};', ''] - - self.outputs[0].write( - '\n'.join(output), - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - self.generator.bld.raw_deps[self.uid()] = self.dep_vars = 'array_name' - - if getattr(self.generator, 'chmod', None): - os.chmod(self.outputs[0].abspath(), self.generator.chmod) - - -@TaskGen.feature('binary_header') -@TaskGen.before_method('process_source', 'process_rule') -def process_binary_header(self): - """ - Define a transformation that substitutes the contents of *source* files to - *target* files:: - - def build(bld): - bld( - features='binary_header', - source='foo.bin', - target='foo.auto.h', - array_name='s_some_array' - ) - bld( - features='binary_header', - source='bar.hex', - target='bar.auto.h', - hex=True - ) - - If the *hex* parameter is True, the *source* files are read in an ASCII - hexadecimal format, where each byte is represented by a pair of hexadecimal - digits with optional whitespace. If *hex* is False or not specified, the - file is treated as a raw binary file. - - The name of the array variable defaults to the source file name with all - characters that are invaid C identifiers replaced with underscores. The name - can be explicitly specified by setting the *array_name* parameter. - - This method overrides the processing by - :py:meth:`waflib.TaskGen.process_source`. - """ - - src = Utils.to_list(getattr(self, 'source', [])) - if isinstance(src, Node.Node): - src = [src] - tgt = Utils.to_list(getattr(self, 'target', [])) - if isinstance(tgt, Node.Node): - tgt = [tgt] - if len(src) != len(tgt): - raise Errors.WafError('invalid number of source/target for %r' % self) - - for x, y in zip(src, tgt): - if not x or not y: - raise Errors.WafError('null source or target for %r' % self) - a, b = None, None - - if isinstance(x, str) and isinstance(y, str) and x == y: - a = self.path.find_node(x) - b = self.path.get_bld().make_node(y) - if not os.path.isfile(b.abspath()): - b.sig = None - b.parent.mkdir() - else: - if isinstance(x, str): - a = self.path.find_resource(x) - elif isinstance(x, Node.Node): - a = x - if isinstance(y, str): - b = self.path.find_or_declare(y) - elif isinstance(y, Node.Node): - b = y - - if not a: - raise Errors.WafError('could not find %r for %r' % (x, self)) - - has_constraints = False - tsk = self.create_task('binary_header', a, b) - for k in ('after', 'before', 'ext_in', 'ext_out'): - val = getattr(self, k, None) - if val: - has_constraints = True - setattr(tsk, k, val) - - tsk.before = [k for k in ('c', 'cxx') if k in Task.classes] - - self.source = [] diff --git a/platform/asterix/boot/waftools/gitinfo.py b/platform/asterix/boot/waftools/gitinfo.py deleted file mode 100644 index 1641d7af7e..0000000000 --- a/platform/asterix/boot/waftools/gitinfo.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re - -import waflib.Context -import waflib.Logs - -def get_git_revision(ctx): - commit = ctx.cmd_and_log(['git', 'rev-parse', '--short', 'HEAD'], quiet=waflib.Context.BOTH).strip() - timestamp = ctx.cmd_and_log(['git', 'log', '-1', '--format=%ct', 'HEAD'], quiet=waflib.Context.BOTH).strip() - - try: - tag = ctx.cmd_and_log(['git', 'describe'], quiet=waflib.Context.BOTH).strip() - except Exception: - tag = "v9.9.9-dev" - waflib.Logs.warn(f'Git tag not found, using {tag}') - - # Validate that git tag follows the required form: - # See https://github.com/pebble/tintin/wiki/Firmware,-PRF-&-Bootloader-Versions - # Note: version_regex.groups() returns sequence ('0', '0', '0', 'suffix'): - version_regex = re.search(r"^v(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:(?:-)(.+))?$", tag) - if not version_regex: - raise ValueError(f'Invalid tag: {tag}') - - # Get version numbers from version_regex.groups() sequence and replace None values with 0 - # e.g. v2-beta11 => ('2', None, None, 'beta11') => ('2', '0', '0') - version = [x if x else '0' for x in version_regex.groups()] - - # Used for pebble_pipeline payload, generate a string that contains everything after minor. - # Force include patch as 0 if it doesn't exist. - patch_verbose = str(version[2]) - str_after_patch = version[3] - if (str_after_patch): - patch_verbose += '-' + str_after_patch - - return {'TAG': tag, - 'COMMIT': commit, - 'TIMESTAMP': timestamp, - 'MAJOR_VERSION': version[0], - 'MINOR_VERSION': version[1], - 'PATCH_VERSION': version[2], - 'MAJOR_MINOR_PATCH_STRING' : ".".join(version[0:3]), - "PATCH_VERBOSE_STRING": patch_verbose} diff --git a/platform/asterix/boot/waftools/ldscript.py b/platform/asterix/boot/waftools/ldscript.py deleted file mode 100644 index 9fecd7f336..0000000000 --- a/platform/asterix/boot/waftools/ldscript.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Utils, Errors -from waflib.TaskGen import after, feature - - -@after('apply_link') -@feature('cprogram', 'cshlib') -def process_ldscript(self): - if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': - return - - node = self.path.find_resource(self.ldscript) - if not node: - raise Errors.WafError('could not find %r' % self.ldscript) - self.link_task.env.append_value('LINKFLAGS', '-T%s' % node.abspath()) - self.link_task.dep_nodes.append(node) diff --git a/platform/asterix/boot/waftools/objcopy.py b/platform/asterix/boot/waftools/objcopy.py deleted file mode 100644 index b7a3082b4c..0000000000 --- a/platform/asterix/boot/waftools/objcopy.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Grygoriy Fuchedzhy 2010 - -""" -Support for converting linked targets to ihex, srec or binary files using -objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' -feature. The 'objcopy' feature uses the following attributes: - -objcopy_bfdname Target object format name (eg. ihex, srec, binary). -Defaults to ihex. -objcopy_target File name used for objcopy output. This defaults to the -target name with objcopy_bfdname as extension. -objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. -objcopy_flags Additional flags passed to objcopy. -""" - -from waflib.Utils import def_attrs -from waflib import Task -from waflib.TaskGen import feature, after_method - - -class objcopy(Task.Task): - run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' - color = 'CYAN' - - -@feature('objcopy') -@after_method('apply_link') -def objcopy(self): - def_attrs(self, - objcopy_bfdname='ihex', - objcopy_target=None, - objcopy_install_path="${PREFIX}/firmware", - objcopy_flags='') - - link_output = self.link_task.outputs[0] - if not self.objcopy_target: - self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name - task = self.create_task('objcopy', - src=link_output, - tgt=self.path.find_or_declare(self.objcopy_target)) - - task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) - try: - task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) - except AttributeError: - pass - - if self.objcopy_install_path: - self.bld.install_files(self.objcopy_install_path, - task.outputs[0], - env=task.env.derive()) - - -def configure(ctx): - objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) - - -def objcopy_simple(task, mode): - return task.exec_command('arm-none-eabi-objcopy -S -R .stack -R .priv_bss' - ' -R .bss -R .retained -O %s "%s" "%s"' % - (mode, task.inputs[0].abspath(), task.outputs[0].abspath())) - - -def objcopy_simple_bin(task): - return objcopy_simple(task, 'binary') diff --git a/platform/asterix/boot/wscript b/platform/asterix/boot/wscript deleted file mode 100644 index 9586796067..0000000000 --- a/platform/asterix/boot/wscript +++ /dev/null @@ -1,99 +0,0 @@ -import os -import sys -import waflib.Logs - -sys.path.append(os.path.abspath('waftools')) -import objcopy -import gitinfo - - -def options(opt): - opt.load('compiler_c') - opt.add_option('--board', action='store', default='asterix', - help='Which board to build for ' - '(asterix)', - choices=['asterix']) - opt.add_option('--nowatchdog', action='store_true', - help='Do not enable watchdog timer or reset upon failure') - opt.add_option('--loglevel', type='int', default=0, - help='Set the logging verbosity [default: %default]') - opt.add_option('--displaydemo', action='store_true', - help='Make the bootloader infinitely loop through boot splash and error codes') - - -def configure(conf): - CPU_FLAGS = [ - '-mcpu=cortex-m4', '-mthumb', - '-mfpu=fpv4-sp-d16', '-mfloat-abi=softfp', - ] - OPT_FLAGS = [ '-Os' ,'-g' ] - C_FLAGS = [ - '-std=c11', '-ffunction-sections', - '-Wall', '-Wextra', '-Werror', '-Wpointer-arith', - '-Wno-unused-parameter', '-Wno-missing-field-initializers', - '-Wno-error=unused-function', '-Wno-error=unused-variable', - '-Wno-error=unused-parameter', '-Wno-error=unused-but-set-variable', - '-Wno-packed-bitfield-compat' - ] - - conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True) - conf.env.AS = conf.env.CC - for tool in 'ar objcopy'.split(): - conf.find_program('arm-none-eabi-' + tool, var=tool.upper(), - mandatory=True) - conf.env.BOARD = conf.options.board - conf.env.append_value('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS) - conf.env.append_value('DEFINES', [ - '_REENT_SMALL=1', - 'USE_STDPERIPH_DRIVER=1', - 'BOARD_{}=1'.format(conf.options.board.upper()) - ]) - - conf.env.append_unique('LINKFLAGS', - ['-Wl,--print-memory-usage', '-Wl,--gc-sections', '-specs=nano.specs'] - + CPU_FLAGS + OPT_FLAGS) - - if conf.options.nowatchdog: - conf.env.append_value('DEFINES', 'NO_WATCHDOG') - - if conf.options.displaydemo: - conf.env.append_value('DEFINES', 'DISPLAY_DEMO_LOOP') - - conf.env.BOOTLOADER_LENGTH = '16384' - conf.env.append_value( - 'DEFINES', 'BOOTLOADER_LENGTH=' + conf.env.BOOTLOADER_LENGTH) - - conf.load('gcc gas objcopy ldscript') - conf.load('binary_header') - - conf.recurse('vendor') - - -def build(bld): - if bld.cmd == 'install': - raise Exception("install isn't a supported command. Did you mean flash?") - - linkflags = ['-Wl,-Map,asterix_boot.map'] - - sources = ( - bld.path.ant_glob('src/*.S') + - bld.path.ant_glob('src/**/*.c')) - - bld(features='subst', - source='src/git_version.auto.h.in', - target='src/git_version.auto.h', - **gitinfo.get_git_revision(bld)) - - bld.recurse('vendor') - bld(features='c asm cprogram objcopy', - source=sources, - includes=['src', 'src/board'], - target='tintin_boot.elf', - ldscript='src/linker.ld', - linkflags=linkflags, - objcopy_bfdname='ihex', - objcopy_target='tintin_boot.hex', - use='hal_nordic', - lib=['gcc']) - bld(rule=objcopy.objcopy_simple_bin, source='tintin_boot.elf', target='tintin_boot.bin') -# vim:filetype=python diff --git a/platform/platform_capabilities.py b/platform/platform_capabilities.py deleted file mode 100644 index 52f7f2e4ce..0000000000 --- a/platform/platform_capabilities.py +++ /dev/null @@ -1,374 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Errors - -# Each set of boards and their capabilities. -# To use these, import as follows: -# ``` -# from platform_capabilities import get_capability_dict -# ``` - -# Master key set. Any capabilities that are added to any platform have to be added here. -# Once added, add the capability to ALL capability dictionaries with the appropriate value. - -JAVASCRIPT_BYTECODE_VERSION = 1 - -master_capability_set = { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_ALS_OPT3001', - 'HAS_ALS_W1160', - 'HAS_APPLE_MFI', - 'HAS_APP_GLANCES', - 'HAS_BUILTIN_HRM', - 'HAS_CORE_NAVIGATION4', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_GLYPH_BITMAP_CACHING', - 'HAS_HARDWARE_PANIC_SCREEN', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_LED', - 'HAS_MAGNETOMETER', - 'HAS_MAPPABLE_FLASH', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_SPRF_V3', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_TOUCHSCREEN', - 'HAS_VIBE_SCORES', - 'HAS_VIBE_DRV2604', - 'HAS_WEATHER', - 'USE_PARALLEL_FLASH', - 'HAS_PUTBYTES_PREACKING', - 'HAS_FLASH_OTP', - 'HAS_VIBE_AW86225', - 'NEEDS_FIRM_579_STATS', - 'HAS_PBLBOOT', -} - -board_capability_dicts = [ - { - 'boards': ['bb2', 'ev2_4', 'v1_5'], - 'capabilities': - { - 'HAS_APPLE_MFI', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_MAGNETOMETER', - }, - }, - { - 'boards': ['v2_0'], - 'capabilities': - { - 'HAS_APPLE_MFI', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_LED', - 'HAS_MAGNETOMETER', - }, - }, - { - 'boards': ['snowy_evt2'], - 'capabilities': - { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APPLE_MFI', - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_MAGNETOMETER', - 'HAS_MAPPABLE_FLASH', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'USE_PARALLEL_FLASH', - 'HAS_WEATHER', - }, - }, - { - 'boards': ['snowy_bb2', 'snowy_dvt', 'snowy_s3'], - 'capabilities': - { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APPLE_MFI', - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_HARDWARE_PANIC_SCREEN', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_MAGNETOMETER', - 'HAS_MAPPABLE_FLASH', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'USE_PARALLEL_FLASH', - 'HAS_WEATHER', - }, - }, - { - 'boards': ['spalding_bb2'], - 'capabilities': - { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_HARDWARE_PANIC_SCREEN', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_MAGNETOMETER', - 'HAS_MAPPABLE_FLASH', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_TEMPERATURE', - 'HAS_VIBE_SCORES', - 'USE_PARALLEL_FLASH', - 'HAS_WEATHER', - }, - }, - { - 'boards': ['spalding_evt', 'spalding'], - 'capabilities': - { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_DEFECTIVE_FW_CRC', - 'HAS_HARDWARE_PANIC_SCREEN', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_MAGNETOMETER', - 'HAS_MAPPABLE_FLASH', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_TEMPERATURE', - 'HAS_VIBE_SCORES', - 'USE_PARALLEL_FLASH', - 'HAS_WEATHER', - }, - }, - { - 'boards': ['silk_bb', 'silk_evt', 'silk_bb2', 'silk'], - 'capabilities': - { - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APP_GLANCES', - 'HAS_BUILTIN_HRM', - 'HAS_CORE_NAVIGATION4', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - # 'HAS_MAPPABLE_FLASH' -- TODO: PBL-33860 verify memory-mappable flash works on silk before activating - 'HAS_MICROPHONE', - # 'USE_PARALLEL_FLASH' -- FIXME hack to get the "modern" flash layout. Fix when we add support for new flash - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_SPRF_V3', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'HAS_WEATHER', - 'HAS_PUTBYTES_PREACKING' - }, - }, - { - 'boards': ['robert_bb', 'robert_bb2', 'robert_evt'], - 'capabilities': - { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APP_GLANCES', - 'HAS_BUILTIN_HRM', - 'HAS_CORE_NAVIGATION4', - 'HAS_GLYPH_BITMAP_CACHING', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_MAGNETOMETER', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_SPRF_V3', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'HAS_WEATHER', - 'HAS_PUTBYTES_PREACKING' - } - }, - { - 'boards': ['cutts_bb'], - 'capabilities': - { - 'COMPOSITOR_USES_DMA', - 'HAS_ACCESSORY_CONNECTOR', - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_GLYPH_BITMAP_CACHING', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_MAGNETOMETER', - 'HAS_MASKING', - 'HAS_MICROPHONE', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_SPRF_V3', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'HAS_WEATHER', - 'HAS_PUTBYTES_PREACKING', - 'HAS_TOUCHSCREEN' - } - }, - { - 'boards': [ 'asterix' ], - 'capabilities': - { - 'HAS_ALS_OPT3001', - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - # 'HAS_MAPPABLE_FLASH' -- TODO: PBL-33860 verify memory-mappable flash works on silk before activating - 'HAS_MICROPHONE', - # 'USE_PARALLEL_FLASH' -- FIXME hack to get the "modern" flash layout. Fix when we add support for new flash - 'HAS_SDK_SHELL4', - 'HAS_SPRF_V3', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'HAS_WEATHER', - 'HAS_PUTBYTES_PREACKING', - 'HAS_MAGNETOMETER', - 'HAS_VIBE_DRV2604', - 'HAS_PMIC', - 'HAS_FLASH_OTP', - 'NEEDS_FIRM_579_STATS', - }, - }, - { - 'boards': [ 'obelix', 'obelix_bb' ], - 'capabilities': - { - 'HAS_APP_GLANCES', - 'HAS_CORE_NAVIGATION4', - 'HAS_HEALTH_TRACKING', - 'HAS_JAVASCRIPT', - 'HAS_LAUNCHER4', - 'HAS_PMIC', - 'HAS_SDK_SHELL4', - 'HAS_SPRF_V3', - 'HAS_TEMPERATURE', - 'HAS_TIMELINE_PEEK', - 'HAS_VIBE_SCORES', - 'HAS_WEATHER', - 'HAS_PUTBYTES_PREACKING', - 'HAS_VIBE_AW86225', - 'HAS_FLASH_OTP', - 'HAS_MICROPHONE', - 'HAS_TOUCHSCREEN', - 'HAS_BUILTIN_HRM', - 'HAS_ALS_W1160', - 'HAS_MAGNETOMETER', - 'HAS_PBLBOOT', - }, - }, -] - -# Run through again and make sure all sets include only valid keys defined in -# `master_capability_set` -boards_seen = set() - -for board_dict in board_capability_dicts: - capabilities_of_board = board_dict['capabilities'] - boards = board_dict['boards'] - - # Check for duplicate boards using the intersection of boards already seen and the boards - # in the dict we are operating on. After the check, add the ones seen to the set - duped_boards = boards_seen.intersection(boards) - if duped_boards: - raise ValueError('There are multiple capability sets for the boards {!r}' - .format(duped_boards)) - boards_seen.update(boards) - - # Check for capabilities that aren't in the master_capability_set - unknown_capabilities = capabilities_of_board - master_capability_set - if unknown_capabilities: - raise ValueError('The capability set for boards {!r} contains unknown ' - 'capabilities {!r}'.format(boards, unknown_capabilities)) - - -def get_capability_dict(ctx, board): - capabilities_of_board = None - # Find capability set for board - for capability_dict in board_capability_dicts: - if board in capability_dict['boards']: - capabilities_of_board = capability_dict['capabilities'] - - if not capabilities_of_board: - raise KeyError('Capability set for board: "{}" is missing or undefined'.format(board)) - - # Overrides - # If you want the capabilities to change depending on the configure/build environment, add - # them here. - - if ctx.env.QEMU: - # Disable smartstraps on QEMU builds - capabilities_of_board.discard('HAS_ACCESSORY_CONNECTOR') - - if ctx.env.NOJS: - capabilities_of_board.discard('HAS_JAVASCRIPT') - - # End overrides section - - false_capabilities = master_capability_set - capabilities_of_board - cp_dict = {key: True for key in capabilities_of_board} - cp_dict.update({key: False for key in false_capabilities}) - - # inject expected JS bytecode version - if cp_dict.get('HAS_JAVASCRIPT', False): - cp_dict['JAVASCRIPT_BYTECODE_VERSION'] = JAVASCRIPT_BYTECODE_VERSION - - return cp_dict diff --git a/platform/robert/Cutts_UL1K_framebuffer_144x168_bitmap.fpga b/platform/robert/Cutts_UL1K_framebuffer_144x168_bitmap.fpga deleted file mode 100644 index 4ac859a644..0000000000 Binary files a/platform/robert/Cutts_UL1K_framebuffer_144x168_bitmap.fpga and /dev/null differ diff --git a/platform/robert/Robert_UL1K_bootloader_bitmap.fpga b/platform/robert/Robert_UL1K_bootloader_bitmap.fpga deleted file mode 100644 index c2a6219221..0000000000 Binary files a/platform/robert/Robert_UL1K_bootloader_bitmap.fpga and /dev/null differ diff --git a/platform/robert/Robert_UL1K_framebuffer_bitmap.fpga b/platform/robert/Robert_UL1K_framebuffer_bitmap.fpga deleted file mode 100644 index b6e6d44663..0000000000 Binary files a/platform/robert/Robert_UL1K_framebuffer_bitmap.fpga and /dev/null differ diff --git a/platform/robert/boot/boot-bin-update.sh b/platform/robert/boot/boot-bin-update.sh deleted file mode 100755 index 69aa665d90..0000000000 --- a/platform/robert/boot/boot-bin-update.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -o errexit -o xtrace - -cd "${0%/*}" - -reloutdir="../../../bin/boot" -OUTDIR=`cd "${reloutdir}"; pwd` -BOARDS=(robert_evt robert_bb robert_bb2) -# Use commit timestamp, same as the one compiled into the bootloader binary -VERSION=`git log -1 --format=%ct HEAD` - -# Clear out old versions of the bootloader binaries -for board in ${BOARDS[*]}; do - git rm --ignore-unmatch ${OUTDIR}/{nowatchdog_,}boot_${board}@*.{hex,elf,bin} || true -done - -# Build all bootloader variants and copy them into OUTDIR -build_and_copy () { - local variant="$1"; - shift; - - for board in ${BOARDS[*]}; do - ./waf distclean configure --board=${board} $@ build --progress - for ext in hex elf bin; do - mv build/robert_boot.${ext} \ - "${OUTDIR}/${variant}boot_${board}@${VERSION}.${ext}" - done - done -} - -build_and_copy "" -build_and_copy nowatchdog_ --nowatchdog diff --git a/platform/robert/boot/flash b/platform/robert/boot/flash deleted file mode 100755 index ef86e8e098..0000000000 --- a/platform/robert/boot/flash +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -o errexit - -SCRIPTDIR="${0%/*}" -IMGFILE="${SCRIPTDIR}/build/robert_boot.hex" - -if [ ! -f "${IMGFILE}" ]; then - echo "Cannot find bootloader binary at '${IMGFILE}'." - echo "Try running './waf build'" - exit 1 -fi - -OPENOCD_SCRIPT=" -init -reset halt -flash write_image erase \"${IMGFILE}\" -reset -shutdown -" - -openocd -f openocd.cfg -c "${OPENOCD_SCRIPT}" - -# vim:filetype=sh diff --git a/platform/robert/boot/git_version.auto.h.in b/platform/robert/boot/git_version.auto.h.in deleted file mode 100644 index f5a14b1a0a..0000000000 --- a/platform/robert/boot/git_version.auto.h.in +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#define GIT_TIMESTAMP @TIMESTAMP@ -#define GIT_TAG "@TAG@" -#define GIT_MAJOR_VERSION @MAJOR_VERSION@ -#define GIT_MINOR_VERSION @MINOR_VERSION@ -#define GIT_PATCH_VERSION @PATCH_VERSION@ -#define GIT_REVISION "@COMMIT@" diff --git a/platform/robert/boot/openocd_swd_ftdi.cfg b/platform/robert/boot/openocd_swd_ftdi.cfg deleted file mode 100644 index 546c23a6a9..0000000000 --- a/platform/robert/boot/openocd_swd_ftdi.cfg +++ /dev/null @@ -1,24 +0,0 @@ -interface ftdi -ftdi_vid_pid 0x0403 0x6011 0x0403 0x7894 - -# output value, direction (1 for output, 0 for input) -ftdi_layout_init 0x1848 0x185b -ftdi_layout_signal SWD_EN -data 0 -oe 0 -ftdi_layout_signal SWDIO_OE -data 0 -oe 0 -ftdi_layout_signal nSRST -data 0x0040 -oe 0x0040 - -# Red + Green LED (inverted output: low is on) -# TX LED (GREEN) -ftdi_layout_signal LED -ndata 0x0080 -oe 0x0080 -# RX LED (RED) -ftdi_layout_signal LED2 -ndata 0x0020 -oe 0x0020 - -transport select swd - -# Note: This works around issue where reset_config does not actually toggle this line. It would be -# nice to figure out what the actual issue with 'reset_config' is at some point -proc init_reset { mode } { - ftdi_set_signal nSRST 0 - sleep 100 - ftdi_set_signal nSRST z -} diff --git a/platform/robert/boot/src/board/board.h b/platform/robert/boot/src/board/board.h deleted file mode 100644 index c9b8d55836..0000000000 --- a/platform/robert/boot/src/board/board.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "display.h" - -#include "drivers/button_id.h" - -#include "stm32f7xx.h" - -#include -#include - -#define GPIO_Port_NULL ((GPIO_TypeDef *) 0) -#define GPIO_Pin_NULL ((uint16_t)0x0000) - -// This is generated in order to faciliate the check within the IRQ_MAP macro below -enum { -#define IRQ_DEF(num, irq) IS_VALID_IRQ__##irq, -#include "irq_stm32f7.def" -#undef IRQ_DEF -}; - -//! Creates a trampoline to the interrupt handler defined within the driver -#define IRQ_MAP(irq, handler, device) \ - void irq##_IRQHandler(void) { \ - handler(device); \ - } \ - _Static_assert(IS_VALID_IRQ__##irq || true, "(See comment below)") -/* - * The above static assert checks that the requested IRQ is valid by checking that the enum - * value (generated above) is declared. The static assert itself will not trip, but you will get - * a compilation error from that line if the IRQ does not exist within irq_stm32*.def. - */ - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} InputConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - bool active_high; ///< Pin is active high or active low -} OutputConfig; - -//! Alternate function pin configuration -//! Used to configure a pin for use by a peripheral -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - const uint16_t gpio_pin_source; ///< One of GPIO_PinSourceX. - const uint8_t gpio_af; ///< One of GPIO_AF_X -} AfConfig; - -typedef struct { - InputConfig input; - GPIOPuPd_TypeDef pupd; -} ButtonConfig; - -// Button Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ButtonConfig buttons[NUM_BUTTONS]; -} BoardConfigButton; - -// Power Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - //! Voltage rail control lines - const OutputConfig rail_4V5_ctrl; - const OutputConfig rail_6V6_ctrl; -} BoardConfigPower; - -typedef struct { - OutputConfig reset_gpio; -} BoardConfigFlash; - -typedef struct { - const OutputConfig power_en; //< Enable power supply to the accessory connector. -} BoardConfigAccessory; - -typedef const struct SPIBus SPIBus; -typedef const struct SPISlavePort SPISlavePort; -typedef const struct I2CBus I2CBus; -typedef const struct I2CSlavePort I2CSlavePort; -typedef const struct ICE40LPDevice ICE40LPDevice; - -void board_init(void); - -#include "board_definitions.h" diff --git a/platform/robert/boot/src/board/board_definitions.h b/platform/robert/boot/src/board/board_definitions.h deleted file mode 100644 index 1a57168e3c..0000000000 --- a/platform/robert/boot/src/board/board_definitions.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 -#include "board_robert_bb.h" -#elif BOARD_ROBERT_EVT -#include "board_robert_evt.h" -#else -#error "Unknown board definition" -#endif diff --git a/platform/robert/boot/src/board/board_robert_bb.c b/platform/robert/boot/src/board/board_robert_bb.c deleted file mode 100644 index 3568b41509..0000000000 --- a/platform/robert/boot/src/board/board_robert_bb.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/i2c/i2c_definitions.h" -#include "drivers/i2c/i2c_hal_definitions.h" -#include "drivers/display/ice40lp_definitions.h" -#include "util/misc.h" - -// -// iCE40LP configuration -// - -static ICE40LPDevice ICE40LP_DEVICE = { - .spi = { - .periph = SPI6, - .rcc_bit = RCC_APB2Periph_SPI6, - .clk = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_5, - .gpio_pin_source = GPIO_PinSource5, - .gpio_af = GPIO_AF8_SPI6 - }, - .mosi = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF8_SPI6 - }, - .scs = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_4, - .active_high = false - } - }, - - .creset = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - .cdone = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_2, - }, - .busy = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_0, - }, - - .use_6v6_rail = true, -}; - -ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE; - - -// I2C DEVICES - -static I2CBusState I2C_PMIC_MAG_BUS_STATE = {}; -static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = { - .i2c = I2C4, - .clock_ctrl = RCC_APB1Periph_I2C4, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C4_EV_IRQn, - .er_irq_channel = I2C4_ER_IRQn, -}; - -static const I2CBus I2C_PMIC_MAG_BUS = { - .state = &I2C_PMIC_MAG_BUS_STATE, - .hal = &I2C_PMIC_MAG_BUS_HAL, - .scl_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_14, - .gpio_pin_source = GPIO_PinSource14, - .gpio_af = GPIO_AF4_I2C4 - }, - .sda_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_15, - .gpio_pin_source = GPIO_PinSource15, - .gpio_af = GPIO_AF4_I2C4 - }, - .name = "I2C_PMIC_MAG" -}; - -static const I2CSlavePort I2C_SLAVE_MAX14690 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x50 -}; - -I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690; - -IRQ_MAP(I2C4_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS); -IRQ_MAP(I2C4_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS); - - -void board_init(void) { - i2c_init(&I2C_PMIC_MAG_BUS); -} diff --git a/platform/robert/boot/src/board/board_robert_bb.h b/platform/robert/boot/src/board/board_robert_bb.h deleted file mode 100644 index 7d8970c60f..0000000000 --- a/platform/robert/boot/src/board/board_robert_bb.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// ---------------------------------------------- -// Board definitions for Robert BB (C2 Bigboard) -// ---------------------------------------------- -// - -#include "util/size.h" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_6, - }, - .pupd = GPIO_PuPd_UP - }, - [BUTTON_ID_UP] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_3, - }, - .pupd = GPIO_PuPd_NOPULL - }, - [BUTTON_ID_SELECT] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_5, - }, - .pupd = GPIO_PuPd_UP - }, - [BUTTON_ID_DOWN] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_4, - }, - .pupd = GPIO_PuPd_UP - }, - }, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .rail_4V5_ctrl = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_5, - .active_high = true, - }, - .rail_6V6_ctrl = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - -}; - -static const BoardConfigFlash BOARD_CONFIG_FLASH = { -}; - -static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = { - .power_en = { GPIOA, GPIO_Pin_11, true }, -}; - -typedef enum { - QSpiPin_CS, - QSpiPin_SCLK, - QSpiPin_DQ0, - QSpiPin_DQ1, - QSpiPin_DQ2, - QSpiPin_DQ3, - QSpiPinCount, -} QSpiPin; - -static const AfConfig BOARD_CONFIG_FLASH_PINS[] = { - [QSpiPin_CS] = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_SCLK] = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ0] = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ1] = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ2] = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ3] = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF9_QUADSPI, - }, -}; - -extern I2CSlavePort * const I2C_MAX14690; -extern ICE40LPDevice * const ICE40LP; diff --git a/platform/robert/boot/src/board/board_robert_evt.c b/platform/robert/boot/src/board/board_robert_evt.c deleted file mode 100644 index 418c706482..0000000000 --- a/platform/robert/boot/src/board/board_robert_evt.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/i2c/i2c_definitions.h" -#include "drivers/i2c/i2c_hal_definitions.h" -#include "drivers/display/ice40lp_definitions.h" -#include "util/misc.h" - -// -// iCE40LP configuration -// - -static ICE40LPDevice ICE40LP_DEVICE = { - .spi = { - .periph = SPI6, - .rcc_bit = RCC_APB2Periph_SPI6, - .clk = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_5, - .gpio_pin_source = GPIO_PinSource5, - .gpio_af = GPIO_AF8_SPI6 - }, - .mosi = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF8_SPI6 - }, - .scs = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_4, - .active_high = false - } - }, - - .creset = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - .cdone = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_2, - }, - .busy = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_0, - }, - - .use_6v6_rail = false, -}; - -ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE; - - -// I2C DEVICES - -static I2CBusState I2C_PMIC_MAG_BUS_STATE = {}; -static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = { - .i2c = I2C4, - .clock_ctrl = RCC_APB1Periph_I2C4, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C4_EV_IRQn, - .er_irq_channel = I2C4_ER_IRQn, -}; - -static const I2CBus I2C_PMIC_MAG_BUS = { - .state = &I2C_PMIC_MAG_BUS_STATE, - .hal = &I2C_PMIC_MAG_BUS_HAL, - .scl_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_14, - .gpio_pin_source = GPIO_PinSource14, - .gpio_af = GPIO_AF4_I2C4 - }, - .sda_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_15, - .gpio_pin_source = GPIO_PinSource15, - .gpio_af = GPIO_AF4_I2C4 - }, - .name = "I2C_PMIC_MAG" -}; - -static const I2CSlavePort I2C_SLAVE_MAX14690 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x50 -}; - -I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690; - -IRQ_MAP(I2C4_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS); -IRQ_MAP(I2C4_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS); - - -void board_init(void) { - i2c_init(&I2C_PMIC_MAG_BUS); -} diff --git a/platform/robert/boot/src/board/board_robert_evt.h b/platform/robert/boot/src/board/board_robert_evt.h deleted file mode 100644 index 65ae8a9494..0000000000 --- a/platform/robert/boot/src/board/board_robert_evt.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// ---------------------------------------------- -// Board definitions for Robert EVT -// ---------------------------------------------- -// - -#include "util/size.h" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_3, - }, - .pupd = GPIO_PuPd_NOPULL, - }, - [BUTTON_ID_UP] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_4, - }, - .pupd = GPIO_PuPd_UP, - }, - [BUTTON_ID_SELECT] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_5, - }, - .pupd = GPIO_PuPd_UP, - }, - [BUTTON_ID_DOWN] = { - .input = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_6, - }, - .pupd = GPIO_PuPd_UP, - }, - }, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .rail_4V5_ctrl = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_5, - .active_high = true, - }, - - .rail_6V6_ctrl = { GPIO_Port_NULL }, -}; - -static const BoardConfigFlash BOARD_CONFIG_FLASH = { - .reset_gpio = { - .gpio = GPIOE, - .gpio_pin = GPIO_Pin_15, - .active_high = false, - }, -}; - -static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = { - .power_en = { GPIOD, GPIO_Pin_2, true }, -}; - -typedef enum { - QSpiPin_CS, - QSpiPin_SCLK, - QSpiPin_DQ0, - QSpiPin_DQ1, - QSpiPin_DQ2, - QSpiPin_DQ3, - QSpiPinCount, -} QSpiPin; - -static const AfConfig BOARD_CONFIG_FLASH_PINS[] = { - [QSpiPin_CS] = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_SCLK] = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ0] = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ1] = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ2] = { - .gpio = GPIOE, - .gpio_pin = GPIO_Pin_2, - .gpio_pin_source = GPIO_PinSource2, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ3] = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF9_QUADSPI, - }, -}; - -extern I2CSlavePort * const I2C_MAX14690; -extern ICE40LPDevice * const ICE40LP; diff --git a/platform/robert/boot/src/board/display.h b/platform/robert/boot/src/board/display.h deleted file mode 100644 index 1ef59ffaf5..0000000000 --- a/platform/robert/boot/src/board/display.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISP_COLS 200 -#define DISP_ROWS 228 - -#define DISPLAY_FRAMEBUFFER_BYTES (DISP_COLS * DISP_ROWS) - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 -#define DISPLAY_ORIENTATION_ROTATED_180 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1 - -#define PBL_BW 0 -#define PBL_RECT 1 - -#define PBL_ROUND 0 -#define PBL_COLOR 1 diff --git a/platform/robert/boot/src/boot_tests.c b/platform/robert/boot/src/boot_tests.c deleted file mode 100644 index 9517804f57..0000000000 --- a/platform/robert/boot/src/boot_tests.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "boot_tests.h" - -#include "board/board.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/flash.h" -#include "system/bootbits.h" -#include "system/rtc_registers.h" -#include "util/misc.h" - -#include "stm32f7xx.h" - -#include -#include -#include - -static const int STUCK_BUTTON_THRESHOLD = 5; - -bool boot_test_is_button_stuck(void) { - // We store how many times each button has been pressed on previous boots in this - // rtc backup register. Every time when we boot without that button pressed that - // counter gets cleared. If the byte reaches 5, return a failure. - - uint32_t button_counter_register = RTC_ReadBackupRegister(STUCK_BUTTON_REGISTER); - uint8_t* button_counter = (uint8_t*) (&button_counter_register); - bool result = false; - - for (int button_id = 0; button_id < NUM_BUTTONS; button_id++) { - if (!button_is_pressed(button_id)) { - button_counter[button_id] = 0; - continue; - } - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_putstr("Stuck button register is invalid, clearing."); - dbgserial_print_hex(button_counter_register); - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, 0); - return false; - } - - button_counter[button_id] += 1; - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_print("Button id "); - dbgserial_print_hex(button_id); - dbgserial_putstr("is stuck!"); - result = true; - } - } - - if (button_counter_register != 0) { - dbgserial_print("Button was pushed on boot. Button counter: "); - dbgserial_print_hex(button_counter_register); - dbgserial_newline(); - } - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, button_counter_register); - return result; -} - -bool boot_test_is_flash_broken(void) { - return !flash_sanity_check(); -} diff --git a/platform/robert/boot/src/boot_tests.h b/platform/robert/boot/src/boot_tests.h deleted file mode 100644 index 099c04628e..0000000000 --- a/platform/robert/boot/src/boot_tests.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool boot_test_is_button_stuck(void); -bool boot_test_is_flash_broken(void); diff --git a/platform/robert/boot/src/drivers/button.c b/platform/robert/boot/src/drivers/button.c deleted file mode 100644 index d2f407ce2a..0000000000 --- a/platform/robert/boot/src/drivers/button.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/button.h" - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" - -static void prv_initialize_button(const ButtonConfig* config) { - // Configure the pin itself - gpio_input_init_pull_up_down(&config->input, config->pupd); -} - -bool button_is_pressed(ButtonId id) { - const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id]; - return !gpio_input_read(&button_config->input); -} - -uint8_t button_get_state_bits(void) { - uint8_t button_state = 0x00; - for (int i = 0; i < NUM_BUTTONS; ++i) { - button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i; - } - return button_state; -} - -void button_init(void) { - periph_config_acquire_lock(); - - for (int i = 0; i < NUM_BUTTONS; ++i) { - prv_initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]); - } - - periph_config_release_lock(); -} diff --git a/platform/robert/boot/src/drivers/button.h b/platform/robert/boot/src/drivers/button.h deleted file mode 100644 index fae7e2dfca..0000000000 --- a/platform/robert/boot/src/drivers/button.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "button_id.h" - -#include -#include - -void button_init(void); - -bool button_is_pressed(ButtonId id); -uint8_t button_get_state_bits(void); diff --git a/platform/robert/boot/src/drivers/button_id.h b/platform/robert/boot/src/drivers/button_id.h deleted file mode 100644 index 02aa1c75d8..0000000000 --- a/platform/robert/boot/src/drivers/button_id.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @addtogroup UI -//! @{ -//! @addtogroup Clicks -//! \brief Dealing with button input -//! @{ - -//! Button ID values -//! @see \ref click_recognizer_get_button_id() -typedef enum { - //! Back button - BUTTON_ID_BACK = 0, - //! Up button - BUTTON_ID_UP, - //! Select (middle) button - BUTTON_ID_SELECT, - //! Down button - BUTTON_ID_DOWN, - //! Total number of buttons - NUM_BUTTONS -} ButtonId; - -//! @} // end addtogroup Clicks -//! @} // end addtogroup UI diff --git a/platform/robert/boot/src/drivers/dbgserial.c b/platform/robert/boot/src/drivers/dbgserial.c deleted file mode 100644 index d5146ddb24..0000000000 --- a/platform/robert/boot/src/drivers/dbgserial.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dbgserial.h" - -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "stm32f7haxx_rcc.h" -#include "stm32f7haxx_gpio.h" -#include "util/attributes.h" -#include "util/cobs.h" -#include "util/crc32.h" -#include "util/net.h" -#include "util/misc.h" - -#include -#include - -#define MAX_MESSAGE (256) -#define FRAME_DELIMITER '\x55' -#define PULSE_TRANSPORT_PUSH (0x5021) -#define PULSE_PROTOCOL_LOGGING (0x0003) - -static const int SERIAL_BAUD_RATE = 1000000; -static USART_TypeDef *const DBGSERIAL_UART = USART3; - -typedef struct PACKED PulseFrame { - net16 protocol; - unsigned char information[]; -} PulseFrame; - -typedef struct PACKED PushPacket { - net16 protocol; - net16 length; - unsigned char information[]; -} PushPacket; - -static const unsigned char s_message_header[] = { - // Message type: text - 1, - // Source filename - 'B', 'O', 'O', 'T', 'L', 'O', 'A', 'D', 'E', 'R', 0, 0, 0, 0, 0, 0, - // Log level and task - '*', '*', - // Timestamp - 0, 0, 0, 0, 0, 0, 0, 0, - // Line number - 0, 0, -}; - -static size_t s_message_length = 0; -static unsigned char s_message_buffer[MAX_MESSAGE]; - -void dbgserial_init(void) { - // Enable GPIO and UART3 peripheral clocks - periph_config_enable(GPIOD, RCC_AHB1Periph_GPIOD); - periph_config_enable(DBGSERIAL_UART, RCC_APB1Periph_USART3); - - DBGSERIAL_UART->CR1 &= ~USART_CR1_UE; - - AfConfig tx_cfg = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF7_USART3 - }; - - gpio_af_init(&tx_cfg, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - - AfConfig rx_cfg = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF7_USART3 - }; - - gpio_af_init(&rx_cfg, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - - // configure the UART peripheral control registers and baud rate - // - 8-bit word length - // - no parity - // - RX / TX enabled - // - 1 stop bit - // - no flow control - - const int k_div_precision = 100; - - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - - // calculate the baud rate value - const uint64_t scaled_apbclock = k_div_precision * clocks.PCLK1_Frequency; - const uint32_t div = (scaled_apbclock / SERIAL_BAUD_RATE); - const uint32_t brr = (div & 0xFFF0) | ((div & 0xF) >> 1); - - DBGSERIAL_UART->BRR = brr / k_div_precision; - DBGSERIAL_UART->CR2 = 0; - DBGSERIAL_UART->CR3 = 0; - DBGSERIAL_UART->CR1 = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE; -} - -static void prv_putchar(uint8_t c) { - while ((DBGSERIAL_UART->ISR & USART_ISR_TXE) == 0) continue; - DBGSERIAL_UART->TDR = c; - while ((DBGSERIAL_UART->ISR & USART_ISR_TXE) == 0) continue; -} - -void dbgserial_print(const char* str) { - for (; *str && s_message_length < MAX_MESSAGE; ++str) { - if (*str == '\n') { - dbgserial_newline(); - } else if (*str != '\r') { - s_message_buffer[s_message_length++] = *str; - } - } -} - -void dbgserial_newline(void) { - uint32_t crc; - size_t raw_length = sizeof(PulseFrame) + sizeof(PushPacket) + - sizeof(s_message_header) + s_message_length + sizeof(crc); - unsigned char raw_packet[raw_length]; - - PulseFrame *frame = (PulseFrame *)raw_packet; - frame->protocol = hton16(PULSE_TRANSPORT_PUSH); - - PushPacket *transport = (PushPacket *)frame->information; - transport->protocol = hton16(PULSE_PROTOCOL_LOGGING); - transport->length = hton16(sizeof(PushPacket) + sizeof(s_message_header) + - s_message_length); - - unsigned char *app = transport->information; - memcpy(app, s_message_header, sizeof(s_message_header)); - memcpy(&app[sizeof(s_message_header)], s_message_buffer, - s_message_length); - - crc = crc32(CRC32_INIT, raw_packet, raw_length - sizeof(crc)); - memcpy(&raw_packet[raw_length - sizeof(crc)], &crc, sizeof(crc)); - - unsigned char cooked_packet[MAX_SIZE_AFTER_COBS_ENCODING(raw_length)]; - size_t cooked_length = cobs_encode(cooked_packet, raw_packet, raw_length); - - prv_putchar(FRAME_DELIMITER); - for (size_t i = 0; i < cooked_length; ++i) { - if (cooked_packet[i] == FRAME_DELIMITER) { - prv_putchar('\0'); - } else { - prv_putchar(cooked_packet[i]); - } - } - prv_putchar(FRAME_DELIMITER); - - s_message_length = 0; -} - -void dbgserial_putstr(const char* str) { - dbgserial_print(str); - - dbgserial_newline(); -} - -void dbgserial_print_hex(uint32_t value) { - char buf[12]; - itoa_hex(value, buf, sizeof(buf)); - dbgserial_print(buf); -} diff --git a/platform/robert/boot/src/drivers/dbgserial.h b/platform/robert/boot/src/drivers/dbgserial.h deleted file mode 100644 index 68cdcb3938..0000000000 --- a/platform/robert/boot/src/drivers/dbgserial.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void dbgserial_init(void); - -void dbgserial_putstr(const char* str); - -void dbgserial_newline(void); - -//! Like dbgserial_putstr, but without a terminating newline -void dbgserial_print(const char* str); - -void dbgserial_print_hex(uint32_t value); - -void dbgserial_putstr_fmt(char* buffer, unsigned int buffer_size, const char* fmt, ...) - __attribute__((format(printf, 3, 4))); diff --git a/platform/robert/boot/src/drivers/display.h b/platform/robert/boot/src/drivers/display.h deleted file mode 100644 index bd0028f337..0000000000 --- a/platform/robert/boot/src/drivers/display.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -void display_init(void); - -void display_boot_splash(void); - -void display_error_code(uint32_t); - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void); - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator); diff --git a/platform/robert/boot/src/drivers/display/boot_fpga.c b/platform/robert/boot/src/drivers/display/boot_fpga.c deleted file mode 100644 index 3d44ce835a..0000000000 --- a/platform/robert/boot/src/drivers/display/boot_fpga.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/display.h" - -#include - -#include "board/board.h" -#include "drivers/dbgserial.h" -#include "drivers/display/bootloader_fpga_bitstream.auto.h" -#include "drivers/display/ice40lp.h" -#include "drivers/display/ice40lp_definitions.h" -#include "system/passert.h" -#include "util/delay.h" -#include "util/misc.h" -#include "util/sle.h" - -#define CMD_NULL (0) -#define CMD_SET_PARAMETER (1) -#define CMD_DISPLAY_OFF (2) -#define CMD_DISPLAY_ON (3) -#define CMD_DRAW_SCENE (4) -#define CMD_RESET_RELEASE (8) -#define CMD_RESET_ASSERT (9) - -#define SCENE_BLACK (0) -#define SCENE_SPLASH (1) -#define SCENE_UPDATE (2) -#define SCENE_ERROR (3) - -#define UPDATE_PROGRESS_MAX (47) - -static uint8_t s_decoded_fpga_image[35000]; // the FPGA image is currently ~30k - -static bool prv_reset_fpga(void) { - const uint32_t length = sle_decode(s_fpga_bitstream, sizeof(s_fpga_bitstream), - s_decoded_fpga_image, sizeof(s_decoded_fpga_image)); - return display_program(s_decoded_fpga_image, length); -} - -static bool prv_wait_busy(void) { - // The display should come out of busy within 35 milliseconds; - // it is a waste of time to wait more than twice that. - int timeout = 50 * 10; - while (display_busy()) { - if (timeout-- == 0) { - dbgserial_putstr("Display busy-wait timeout expired!"); - return false; - } - delay_us(100); - } - return true; -} - -static void prv_screen_on(void) { - display_write_cmd(CMD_DISPLAY_ON, NULL, 0); -} - -static void prv_screen_off(void) { - display_write_cmd(CMD_DISPLAY_OFF, NULL, 0); -} - -static void prv_draw_scene(uint8_t scene) { - display_write_cmd(CMD_DRAW_SCENE, &scene, sizeof(scene)); -} - -static void prv_set_parameter(uint32_t param) { - display_write_cmd(CMD_SET_PARAMETER, (uint8_t *)¶m, sizeof(param)); -} - -#ifdef DISPLAY_DEMO_LOOP -static void prv_play_demo_loop(void) { - while (1) { - for (int i = 0; i <= UPDATE_PROGRESS_MAX; ++i) { - display_firmware_update_progress(i, UPDATE_PROGRESS_MAX); - delay_ms(80); - } - - for (uint32_t i = 0; i <= 0xf; ++i) { - display_error_code(i * 0x11111111); - delay_ms(200); - } - for (uint32_t i = 0; i < 8; ++i) { - for (uint32_t j = 1; j <= 0xf; ++j) { - display_error_code(j << (i * 4)); - delay_ms(200); - } - } - display_error_code(0x01234567); - delay_ms(200); - display_error_code(0x89abcdef); - delay_ms(200); - display_error_code(0xcafebabe); - delay_ms(200); - display_error_code(0xfeedface); - delay_ms(200); - display_error_code(0x8badf00d); - delay_ms(200); - display_error_code(0xbad1ce40); - delay_ms(200); - display_error_code(0xbeefcace); - delay_ms(200); - display_error_code(0x0defaced); - delay_ms(200); - display_error_code(0xd15ea5e5); - delay_ms(200); - display_error_code(0xdeadbeef); - delay_ms(200); - display_boot_splash(); - delay_ms(1000); - } -} -#endif - -void display_init(void) { - display_start(); - if (!prv_reset_fpga()) { - dbgserial_putstr("FPGA configuration failed."); - return; - } - - // enable the power rails - display_power_enable(); - - // start with the screen off - prv_screen_off(); - - // Work around an issue which some boards exhibit where the FPGA ring - // oscillator can start up with higher harmonics, massively overclocking the - // design and causing malfunction. When this occurrs, the draw-scene command - // will not work, asserting BUSY indefinitely but never updating the display. - // Other commands such as display-on and display-off are less affected by the - // overclocking, so the display can be turned on while the FPGA is in this - // state, showing only garbage. - // FPGA malfunction can be detected in software. In an attempt to restore - // proper functioning, the FPGA can be reset and reconfigured in the hopes - // that the ring oscillator will start up and oscillate without any higher - // harmonics. Bootloader release 03 attempts to mitigate this problem by - // delaying oscillator startup until after configuration completes. Time will - // tell whether this actually fixes things. - for (int retries = 0; retries <= 10; ++retries) { - prv_draw_scene(SCENE_SPLASH); - if (prv_wait_busy()) { - prv_screen_on(); - dbgserial_print("Display initialized after "); - dbgserial_print_hex(retries); - dbgserial_putstr(" retries."); -#ifdef DISPLAY_DEMO_LOOP - prv_play_demo_loop(); -#endif - return; - } - - if (!prv_reset_fpga()) { - dbgserial_putstr("FPGA configuration failed."); - return; - } - } - - // It's taken too many attempts and the FPGA still isn't behaving. Give up on - // showing the splash screen and keep the screen off so that the user doesn't - // see a broken-looking staticky screen on boot. - dbgserial_putstr("Display initialization failed."); - prv_screen_off(); -} - -void display_boot_splash(void) { - prv_wait_busy(); - prv_draw_scene(SCENE_SPLASH); - // Don't turn the screen on until the boot-splash is fully drawn. - prv_wait_busy(); - prv_screen_on(); -} - -void display_firmware_update_progress( - uint32_t numerator, uint32_t denominator) { - static uint8_t last_bar_fill = UINT8_MAX; - // Scale progress to the number of pixels in the progress bar, - // rounding half upwards. - uint8_t bar_fill = - ((numerator * UPDATE_PROGRESS_MAX) + ((denominator+1)/2)) / denominator; - // Don't waste time and power redrawing the same screen repeatedly. - if (bar_fill != last_bar_fill) { - last_bar_fill = bar_fill; - prv_set_parameter(bar_fill); - prv_draw_scene(SCENE_UPDATE); - } -} - -void display_error_code(uint32_t error_code) { - prv_set_parameter(error_code); - prv_draw_scene(SCENE_ERROR); -} - -void display_prepare_for_reset(void) { - prv_screen_off(); -} diff --git a/platform/robert/boot/src/drivers/display/ice40lp.c b/platform/robert/boot/src/drivers/display/ice40lp.c deleted file mode 100644 index b96b90b522..0000000000 --- a/platform/robert/boot/src/drivers/display/ice40lp.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ice40lp.h" - -#include "board/board.h" -#include "drivers/dbgserial.h" -#include "drivers/display/ice40lp_definitions.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/delay.h" - -#include "stm32f7xx.h" -#include "misc.h" - -#include - - - -static void prv_spi_init(void) { - // Configure the GPIO (SCLK, MOSI - no MISO since the SPI is TX-only) - gpio_af_init(&ICE40LP->spi.clk, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&ICE40LP->spi.mosi, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL); - - // Reset the SPI peripheral and enable the clock - RCC_APB2PeriphResetCmd(ICE40LP->spi.rcc_bit, ENABLE); - RCC_APB2PeriphResetCmd(ICE40LP->spi.rcc_bit, DISABLE); - periph_config_enable(ICE40LP->spi.periph, ICE40LP->spi.rcc_bit); - - // Configure CR1 first: - // * TX-only mode (BIDIMODE | BIDIOE) - // * software control NSS pin (SSM | SSI) - // * master mode (MSTR) - // * clock polarity high / 2nd edge (CPOL | CPHA) - ICE40LP->spi.periph->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | - SPI_CR1_MSTR | SPI_CR1_CPOL | SPI_CR1_CPHA; - - // Configure CR2: - // * 8-bit data size (DS[4:0] == 0b0111) - // * 1/4 RX threshold (for 8-bit transfers) - ICE40LP->spi.periph->CR2 = SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2 | SPI_CR2_FRXTH; - - // enable the SPI - ICE40LP->spi.periph->CR1 |= SPI_CR1_SPE; -} - -static void prv_spi_write(const uint8_t *data, uint32_t len) { - for (uint32_t i = 0; i < len; ++i) { - // Wait until we can transmit. - while (!(ICE40LP->spi.periph->SR & SPI_SR_TXE)) continue; - - // Write a byte. STM32F7 needs to access as 8 bits in order to actually do 8 bits. - *(volatile uint8_t*)&ICE40LP->spi.periph->DR = data[i]; - } - - // Wait until the TX FIFO is empty plus an extra little bit for the shift-register. - while (((ICE40LP->spi.periph->SR & SPI_SR_FTLVL) >> __builtin_ctz(SPI_SR_FTLVL)) > 0) continue; - delay_us(10); -} - -bool display_busy(void) { - return gpio_input_read(&ICE40LP->busy); -} - -void display_start(void) { - // Configure SCS before CRESET and before configuring the SPI so that we don't end up with the - // FPGA in the "SPI Master Configuration Interface" on bigboards which don't have NVCM. If we end - // up in this mode, the FPGA will drive the clock and put the SPI peripheral in a bad state. - gpio_output_init(&ICE40LP->spi.scs, GPIO_OType_PP, GPIO_Speed_25MHz); - gpio_output_set(&ICE40LP->spi.scs, false); - gpio_input_init(&ICE40LP->cdone); - gpio_input_init(&ICE40LP->busy); - gpio_output_init(&ICE40LP->creset, GPIO_OType_OD, GPIO_Speed_25MHz); - - prv_spi_init(); -} - -bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size) { - InputConfig creset_input = { - .gpio = ICE40LP->creset.gpio, - .gpio_pin = ICE40LP->creset.gpio_pin, - }; - - delay_ms(1); - - gpio_output_set(&ICE40LP->spi.scs, true); // SCS asserted (low) - gpio_output_set(&ICE40LP->creset, false); // CRESET LOW - - delay_ms(1); - - if (gpio_input_read(&creset_input)) { - dbgserial_putstr("CRESET not low during reset"); - return false; - } - - gpio_output_set(&ICE40LP->creset, true); // CRESET -> HIGH - - delay_ms(1); - - if (gpio_input_read(&ICE40LP->cdone)) { - dbgserial_putstr("CDONE not low after reset"); - return false; - } - - if (!gpio_input_read(&creset_input)) { - dbgserial_putstr("CRESET not high after reset"); - return false; - } - - delay_ms(1); - - // Program the FPGA - prv_spi_write(fpga_bitstream, bitstream_size); - - // Set SCS high so that we don't process any of these clocks as commands. - gpio_output_set(&ICE40LP->spi.scs, false); // SCS not asserted (high) - - // 49+ SCLK cycles to tell FPGA we're done configuration. - static const uint8_t spi_zeros[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - prv_spi_write(spi_zeros, sizeof(spi_zeros)); - - if (!gpio_input_read(&ICE40LP->cdone)) { - dbgserial_putstr("CDONE not high after programming"); - return false; - } - return true; -} - -void display_power_enable(void) { - // The display requires us to wait 1ms between each power rail coming up. The PMIC - // initialization brings up the 3.2V rail (VLCD on the display, LD02 on the PMIC) for us, but - // we still need to wait before turning on the subsequent rails. - delay_ms(2); - - if (ICE40LP->use_6v6_rail) { - dbgserial_putstr("Enabling 6v6 (Display VDDC)"); - set_6V6_power_state(true); - - delay_ms(2); - } - - dbgserial_putstr("Enabling 4v5 (Display VDDP)"); - set_4V5_power_state(true); -} - -void display_power_disable(void) { - dbgserial_putstr("Disabling 4v5 (Display VDDP)"); - set_4V5_power_state(false); - - delay_ms(2); - - if (ICE40LP->use_6v6_rail) { - dbgserial_putstr("Disabling 6v6 (Display VDDC)"); - set_6V6_power_state(false); - - delay_ms(2); - } -} - -void display_write_cmd(uint8_t cmd, uint8_t *arg, uint32_t arg_len) { - gpio_output_set(&ICE40LP->spi.scs, true); // SCS asserted (low) - delay_us(100); - - prv_spi_write(&cmd, sizeof(cmd)); - if (arg_len) { - prv_spi_write(arg, arg_len); - } - - gpio_output_set(&ICE40LP->spi.scs, false); // SCS not asserted (high) -} diff --git a/platform/robert/boot/src/drivers/display/ice40lp.h b/platform/robert/boot/src/drivers/display/ice40lp.h deleted file mode 100644 index 52da914a32..0000000000 --- a/platform/robert/boot/src/drivers/display/ice40lp.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -bool display_busy(void); -void display_start(void); -bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size); -void display_write_cmd(uint8_t cmd, uint8_t *arg, uint32_t arg_len); -void display_power_enable(void); -void display_power_disable(void); diff --git a/platform/robert/boot/src/drivers/display/ice40lp_definitions.h b/platform/robert/boot/src/drivers/display/ice40lp_definitions.h deleted file mode 100644 index ac1ff158c5..0000000000 --- a/platform/robert/boot/src/drivers/display/ice40lp_definitions.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include -#include - -typedef const struct ICE40LPDevice { - struct { - SPI_TypeDef *periph; - uint32_t rcc_bit; - AfConfig clk; - AfConfig mosi; - OutputConfig scs; - } spi; - - OutputConfig creset; - InputConfig cdone; - InputConfig busy; - - bool use_6v6_rail; -} ICE40LPDevice; diff --git a/platform/robert/boot/src/drivers/exti.h b/platform/robert/boot/src/drivers/exti.h deleted file mode 100644 index 02706d8651..0000000000 --- a/platform/robert/boot/src/drivers/exti.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -typedef enum { - ExtiTrigger_Rising, - ExtiTrigger_Falling, - ExtiTrigger_RisingFalling -} ExtiTrigger; - -//! See section 12.2.5 "External interrupt/event line mapping" in the STM32F2 reference manual -typedef enum { - ExtiLineOther_RTCAlarm = 17, - ExtiLineOther_RTCWakeup = 22 -} ExtiLineOther; - -typedef void (*ExtiHandlerCallback)(void); - -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb); -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger); - -static inline void exti_enable(ExtiConfig config); -static inline void exti_disable(ExtiConfig config); - -void exti_enable_other(ExtiLineOther); -void exti_disable_other(ExtiLineOther); - -#include "exti.inl.h" diff --git a/platform/robert/boot/src/drivers/exti.inl.h b/platform/robert/boot/src/drivers/exti.inl.h deleted file mode 100644 index a16576c7ae..0000000000 --- a/platform/robert/boot/src/drivers/exti.inl.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! @file exti.inl.h -//! -//! Helper functions intended to be inlined into the calling code. - -static inline void exti_enable(ExtiConfig config) { - exti_enable_other(config.exti_line); -} - -static inline void exti_disable(ExtiConfig config) { - exti_disable_other(config.exti_line); -} diff --git a/platform/robert/boot/src/drivers/flash.h b/platform/robert/boot/src/drivers/flash.h deleted file mode 100644 index ccec06292b..0000000000 --- a/platform/robert/boot/src/drivers/flash.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Configure the micro's peripherals to communicate with the flash chip -void flash_init(void); - -//! Read 1 or more bytes starting at the specified 24bit address into -//! the provided buffer. This function does no range checking, so it is -//! currently possible to run off the end of the flash. -//! -//! @param buffer A byte-buffer that will be used to store the data -//! read from flash. -//! @param start_addr The address of the first byte to be read from flash. -//! @param buffer_size The total number of bytes to be read from flash. -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -//! Check if we can talk to the flash. -//! @return true if the CFI table can be queried. -bool flash_sanity_check(void); - -//! Get the checksum of a region of flash -uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t length); diff --git a/platform/robert/boot/src/drivers/flash/flash_crc.c b/platform/robert/boot/src/drivers/flash/flash_crc.c deleted file mode 100644 index cdcb8cf146..0000000000 --- a/platform/robert/boot/src/drivers/flash/flash_crc.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/flash.h" - -#include "util/crc32.h" - -#define CRC_CHUNK_SIZE 1024 - -uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t num_bytes) { - uint8_t buffer[CRC_CHUNK_SIZE]; - - uint32_t crc = CRC32_INIT; - - while (num_bytes > CRC_CHUNK_SIZE) { - flash_read_bytes(buffer, flash_addr, CRC_CHUNK_SIZE); - crc = crc32(crc, buffer, CRC_CHUNK_SIZE); - - num_bytes -= CRC_CHUNK_SIZE; - flash_addr += CRC_CHUNK_SIZE; - } - - flash_read_bytes(buffer, flash_addr, num_bytes); - return crc32(crc, buffer, num_bytes); -} diff --git a/platform/robert/boot/src/drivers/flash/mt25q.c b/platform/robert/boot/src/drivers/flash/mt25q.c deleted file mode 100644 index ab3f1b8a4a..0000000000 --- a/platform/robert/boot/src/drivers/flash/mt25q.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "board/board.h" -#include "drivers/flash.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "util/delay.h" - -#include "stm32f7haxx_qspi.h" - -#define MT25Q_FASTREAD_DUMMYCYCLES 10 - -typedef enum MT25QCommand { - // SPI/QSPI Commands - MT25QCommand_FastRead = 0x0B, // FAST_READ - MT25QCommand_QSPIEnable = 0x35, // QPI - MT25QCommand_ResetEnable = 0x66, // RSTEN - MT25QCommand_Reset = 0x99, // RST - - // QSPI only commands - MT25QCommand_QSPI_ID = 0xAF, // QPIID -} MT25QCommand; - -// Helpful Enums -typedef enum { - QSPIFlag_Retain = 0, - QSPIFlag_ClearTC = 1, -} QSPIFlag; - -static void prv_enable_qspi_clock(void) { - periph_config_enable(QUADSPI, RCC_AHB3Periph_QSPI); -} - -static void prv_disable_qspi_clock(void) { - periph_config_disable(QUADSPI, RCC_AHB3Periph_QSPI); -} - -static void prv_set_num_data_bytes(uint32_t length) { - // From the docs: QSPI_DataLength: Number of data to be retrieved, value+1. - // so 0 is 1 byte, so we substract 1 from the length. -1 is read the entire flash length. - QSPI_SetDataLength(length - 1); -} - -static void prv_wait_for_qspi_transfer_complete(QSPIFlag actions) { - while (QSPI_GetFlagStatus(QSPI_FLAG_TC) == RESET) { } - - if (actions == QSPIFlag_ClearTC) { - QSPI_ClearFlag(QSPI_FLAG_TC); - } -} - -static void prv_wait_for_qspi_not_busy(void) { - while (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) != RESET) { } -} - -static void prv_quad_enable() { - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_1Line; - qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_QSPIEnable; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - prv_wait_for_qspi_not_busy(); -} - -static void prv_flash_reset(void) { - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_ResetEnable; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_Reset; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - delay_us(50000); // 50ms reset if busy with an erase! - - // Return the flash to Quad SPI mode, all our commands are quad-spi and it'll just cause - // problems/bugs for someone if it comes back in single spi mode - prv_quad_enable(); -} - -#include "system/passert.h" -static bool prv_flash_check_whoami(void) { - const unsigned int num_whoami_bytes = 3; - - prv_set_num_data_bytes(num_whoami_bytes); - - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Read; - qspi_com_config.QSPI_ComConfig_DMode = QSPI_ComConfig_DMode_4Line; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_QSPI_ID; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - uint32_t read_whoami = 0; - for (unsigned int i = 0; i < num_whoami_bytes; ++i) { - read_whoami |= QSPI_ReceiveData8() << (8 * i); - } - - prv_wait_for_qspi_not_busy(); - -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 - const uint32_t expected_whoami = 0x19BB20; -#elif BOARD_ROBERT_EVT - const uint32_t expected_whoami = 0x18BB20; -#else -#error "Unsupported board" -#endif - if (read_whoami == expected_whoami) { - return true; - } else { - dbgserial_print_hex(read_whoami); - return false; - } -} - -void flash_init(void) { - prv_enable_qspi_clock(); - // init GPIOs - for (unsigned i = 0; i < QSpiPinCount; ++i) { - gpio_af_init(&BOARD_CONFIG_FLASH_PINS[i], GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_NOPULL); - } - if (BOARD_CONFIG_FLASH.reset_gpio.gpio) { - gpio_output_init(&BOARD_CONFIG_FLASH.reset_gpio, GPIO_OType_PP, GPIO_Speed_2MHz); - gpio_output_set(&BOARD_CONFIG_FLASH.reset_gpio, false); - } - - // Init QSPI peripheral - QSPI_InitTypeDef qspi_config; - QSPI_StructInit(&qspi_config); - qspi_config.QSPI_SShift = QSPI_SShift_HalfCycleShift; - qspi_config.QSPI_Prescaler = 0; - qspi_config.QSPI_CKMode = QSPI_CKMode_Mode0; - qspi_config.QSPI_CSHTime = QSPI_CSHTime_1Cycle; - qspi_config.QSPI_FSize = 23; // 2^24 = 16MB. -> 24 - 1 = 23 - qspi_config.QSPI_FSelect = QSPI_FSelect_1; - qspi_config.QSPI_DFlash = QSPI_DFlash_Disable; - QSPI_Init(&qspi_config); - - QSPI_Cmd(ENABLE); - - // Must call quad_enable first, all commands are QSPI - prv_quad_enable(); - - // Reset the flash to stop any program's or erase in progress from before reboot - prv_flash_reset(); - - prv_disable_qspi_clock(); -} - -bool flash_sanity_check(void) { - prv_enable_qspi_clock(); - - bool result = prv_flash_check_whoami(); - - prv_disable_qspi_clock(); - - return result; -} - -void flash_read_bytes(uint8_t *buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - prv_enable_qspi_clock(); - - prv_set_num_data_bytes(buffer_size); - - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Read; - qspi_com_config.QSPI_ComConfig_DMode = QSPI_ComConfig_DMode_4Line; - qspi_com_config.QSPI_ComConfig_DummyCycles = MT25Q_FASTREAD_DUMMYCYCLES; - qspi_com_config.QSPI_ComConfig_ADMode = QSPI_ComConfig_ADMode_4Line; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_ADSize = QSPI_ComConfig_ADSize_24bit; - qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_FastRead; - QSPI_ComConfig_Init(&qspi_com_config); - - QSPI_SetAddress(start_addr); - - uint8_t *write_ptr = buffer_ptr; - for (unsigned i = 0; i < buffer_size; ++i) { - write_ptr[i] = QSPI_ReceiveData8(); - } - - QSPI_ClearFlag(QSPI_FLAG_TC); - prv_wait_for_qspi_not_busy(); - - prv_disable_qspi_clock(); -} diff --git a/platform/robert/boot/src/drivers/gpio.c b/platform/robert/boot/src/drivers/gpio.c deleted file mode 100644 index bbbb09a5ab..0000000000 --- a/platform/robert/boot/src/drivers/gpio.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" - -#include -#include - -#define MAX_GPIO (11) - -#define GPIO_EN_MASK ((RCC_AHB1ENR_GPIOKEN << 1) - 1) - -void gpio_disable_all(void) { - RCC->AHB1ENR &= ~GPIO_EN_MASK; -} - -static void prv_init_common(const InputConfig *input_config, GPIO_InitTypeDef *gpio_init) { - gpio_use(input_config->gpio); - GPIO_Init(input_config->gpio, gpio_init); - gpio_release(input_config->gpio); -} - -static uint8_t s_gpio_clock_count[MAX_GPIO]; - -void gpio_use(GPIO_TypeDef* GPIOx) { - uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - if ((idx < MAX_GPIO) && !(s_gpio_clock_count[idx]++)) { - SET_BIT(RCC->AHB1ENR, (0x1 << idx)); - } -} - -void gpio_release(GPIO_TypeDef* GPIOx) { - uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - if ((idx < MAX_GPIO) && s_gpio_clock_count[idx] && !(--s_gpio_clock_count[idx])) { - CLEAR_BIT(RCC->AHB1ENR, (0x1 << idx)); - } -} - -void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed) { - GPIO_InitTypeDef init = { - .GPIO_Pin = pin_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(pin_config->gpio); - GPIO_Init(pin_config->gpio, &init); - gpio_release(pin_config->gpio); -} - -void gpio_output_set(const OutputConfig *pin_config, bool asserted) { - if (!pin_config->active_high) { - asserted = !asserted; - } - gpio_use(pin_config->gpio); - GPIO_WriteBit(pin_config->gpio, pin_config->gpio_pin, - asserted? Bit_SET : Bit_RESET); - gpio_release(pin_config->gpio); -} - -void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = pupd - }; - - gpio_use(af_config->gpio); - GPIO_PinAFConfig(af_config->gpio, af_config->gpio_pin_source, - af_config->gpio_af); - GPIO_Init(af_config->gpio, &init); - gpio_release(af_config->gpio); -} - -void gpio_af_configure_low_power(const AfConfig *af_config) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AN, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(af_config->gpio); - GPIO_Init(af_config->gpio, &init); - gpio_release(af_config->gpio); -} - -void gpio_af_configure_fixed_output(const AfConfig *af_config, bool asserted) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(af_config->gpio); - GPIO_Init(af_config->gpio, &init); - GPIO_WriteBit(af_config->gpio, af_config->gpio_pin, - asserted? Bit_SET : Bit_RESET); - gpio_release(af_config->gpio); -} - -void gpio_input_init(const InputConfig *input_config) { - if (input_config->gpio == NULL) { - return; - } - - gpio_input_init_pull_up_down(input_config, GPIO_PuPd_NOPULL); -} - -void gpio_input_init_pull_up_down(const InputConfig *input_config, GPIOPuPd_TypeDef pupd) { - GPIO_InitTypeDef gpio_init = { - .GPIO_Mode = GPIO_Mode_IN, - .GPIO_PuPd = pupd, - .GPIO_Pin = input_config->gpio_pin - }; - - prv_init_common(input_config, &gpio_init); -} - -bool gpio_input_read(const InputConfig *input_config) { - gpio_use(input_config->gpio); - uint8_t bit = GPIO_ReadInputDataBit(input_config->gpio, input_config->gpio_pin); - gpio_release(input_config->gpio); - - return bit != 0; -} - -void gpio_analog_init(const InputConfig *input_config) { - GPIO_InitTypeDef gpio_init = { - .GPIO_Pin = input_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AN, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - prv_init_common(input_config, &gpio_init); -} diff --git a/platform/robert/boot/src/drivers/gpio.h b/platform/robert/boot/src/drivers/gpio.h deleted file mode 100644 index 350a42b37d..0000000000 --- a/platform/robert/boot/src/drivers/gpio.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "stm32f7xx.h" - -#include "board/board.h" - -void gpio_disable_all(void); - -void gpio_use(GPIO_TypeDef* GPIOx); -void gpio_release(GPIO_TypeDef* GPIOx); - -//! Initialize a GPIO as an output. -//! -//! @param pin_config the BOARD_CONFIG pin configuration struct -//! @param otype the output type of the pin (GPIO_OType_PP or GPIO_OType_OD) -//! @param speed the output slew rate -//! @note The slew rate should be set as low as possible for the -//! pin function to minimize ringing and RF interference. -void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed); - -//! Assert or deassert the output pin. -//! -//! Asserting the output drives the pin high if pin_config.active_high -//! is true, and drives it low if pin_config.active_high is false. -void gpio_output_set(const OutputConfig *pin_config, bool asserted); - -//! Configure a GPIO alternate function. -//! -//! @param pin_config the BOARD_CONFIG pin configuration struct -//! @param otype the output type of the pin (GPIO_OType_PP or GPIO_OType_OD) -//! @param speed the output slew rate -//! @param pupd pull-up or pull-down configuration -//! @note The slew rate should be set as low as possible for the -//! pin function to minimize ringing and RF interference. -void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd); - -//! Configure a GPIO alternate function pin to minimize power consumption. -//! -//! Once a pin has been configured for low power, it is no longer -//! connected to its alternate function. \ref gpio_af_init will need to -//! be called again on the pin in order to configure it in alternate -//! function mode again. -void gpio_af_configure_low_power(const AfConfig *af_config); - -//! Configure a GPIO alternate function pin to drive a constant output. -//! -//! Once a pin has been configured as a fixed output, it is no longer -//! connected to its alternate function. \ref gpio_af_init will need to -//! be called again on the pin in order to configure it in alternate -//! function mode again. -void gpio_af_configure_fixed_output(const AfConfig *af_config, bool asserted); - -//! Configure all GPIOs in the system to optimize for power consumption. -//! At poweron most GPIOs can be configured as analog inputs instead of the -//! default digital input. This allows digital filtering logic to be shut down, -//! saving quite a bit of power. -void gpio_init_all(void); - -//! Configure gpios as inputs (suitable for things like exti lines) -void gpio_input_init(const InputConfig *input_cfg); - -//! Configure gpio as an input with internal pull up/pull down configured. -void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd); - -//! @return bool the current state of the GPIO pin -bool gpio_input_read(const InputConfig *input_cfg); - -//! Configure gpios as analog inputs. Useful for unused GPIOs as this is their lowest power state. -void gpio_analog_init(const InputConfig *input_cfg); diff --git a/platform/robert/boot/src/drivers/i2c.h b/platform/robert/boot/src/drivers/i2c.h deleted file mode 100644 index 1096d7174b..0000000000 --- a/platform/robert/boot/src/drivers/i2c.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include -#include - -//! Start using the I2C bus to which \a slave is connected -//! Must be called before any other reads or writes to the slave are performed -//! @param slave I2C slave reference, which will identify the bus to use -void i2c_use(I2CSlavePort *slave); - -//! Stop using the I2C bus to which \a slave is connected -//! Call when done communicating with the slave -//! @param slave I2C slave reference, which will identify the bus to release -void i2c_release(I2CSlavePort *slave); - -//! Reset the slave -//! Will cycle the power to and re-initialize the bus to which \a slave is connected, if this is -//! supported for the bus. -//! @param slave I2C slave reference, which will identify the bus to be reset -void i2c_reset(I2CSlavePort *slave); - -//! Manually bang out the clock on the bus to which \a slave is connected until the data line -//! recovers for a period or we timeout waiting for it to recover -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave reference, which will identify the bus to be recovered -//! @return true if the data line recovered, false otherwise -bool i2c_bitbang_recovery(I2CSlavePort *slave); - -//! Read the value of a register -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave to communicate with -//! @param register_address Address of register to read -//! @param result Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_register(I2CSlavePort *slave, uint8_t register_address, uint8_t *result); - -//! Read a sequence of registers starting from \a register_address_start -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave to communicate with -//! @param register_address_start Address of first register to read -//! @param read_size Number of bytes to read -//! @param result_buffer Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_register_block(I2CSlavePort *slave, uint8_t register_address_start, - uint32_t read_size, uint8_t* result_buffer); - -//! Read a block of data without sending a register address before doing so. -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave to communicate with -//! @param read_size Number of bytes to read -//! @param result_buffer Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_block(I2CSlavePort *slave, uint32_t read_size, uint8_t* result_buffer); - -//! Write to a register -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave to communicate with -//! @param register_address Address of register to write to -//! @param value Data value to write -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_register(I2CSlavePort *slave, uint8_t register_address, uint8_t value); - -//! Write to a sequence of registers starting from \a register_address_start -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave to communicate with -//! @param register_address_start Address of first register to read -//! @param write_size Number of bytes to write -//! @param buffer Pointer to source buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_start, - uint32_t write_size, const uint8_t* buffer); - -//! Write a block of data without sending a register address before doing so. -//! Must not be called before \ref i2c_use has been called for the slave -//! @param slave I2C slave to communicate with -//! @param write_size Number of bytes to write -//! @param buffer Pointer to source buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer); diff --git a/platform/robert/boot/src/drivers/i2c/i2c.c b/platform/robert/boot/src/drivers/i2c/i2c.c deleted file mode 100644 index 55495b1a36..0000000000 --- a/platform/robert/boot/src/drivers/i2c/i2c.c +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/i2c.h" -#include "i2c_definitions.h" -#include "i2c_hal.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "system/passert.h" -#include "system/logging.h" -#include "util/delay.h" -#include "util/size.h" - -#include - -#include "stm32f7xx.h" - -#define I2C_ERROR_TIMEOUT_MS (1000) -#define I2C_TIMEOUT_ATTEMPTS_MAX (2 * 1000 * 1000) - -// MFI NACKs while busy. We delay ~1ms between retries so this is approximately a 1000ms timeout. -// The longest operation of the MFi chip is "start signature generation", which seems to take -// 223-224 NACKs, but sometimes for unknown reasons it can take much longer. -#define I2C_NACK_COUNT_MAX (1000) - -typedef enum { - Read, - Write -} TransferDirection; - -typedef enum { - SendRegisterAddress, // Send a register address, followed by a repeat start for reads - NoRegisterAddress // Do not send a register address -} TransferType; - -/*----------------SEMAPHORE/LOCKING FUNCTIONS--------------------------*/ - -static bool prv_semaphore_take(I2CBusState *bus) { - return true; -} - -static bool prv_semaphore_wait(I2CBusState *bus) { - bus->busy = true; - volatile uint32_t timeout_attempts = I2C_TIMEOUT_ATTEMPTS_MAX; - while ((timeout_attempts-- > 0) && (bus->busy)) {}; - bus->busy = false; - return (timeout_attempts != 0); -} - -static void prv_semaphore_give(I2CBusState *bus) { - bus->busy = false; -} - -static void prv_semaphore_give_from_isr(I2CBusState *bus) { - bus->busy = false; - return; -} - -/*-------------------BUS/PIN CONFIG FUNCTIONS--------------------------*/ - -static void prv_rail_ctl(I2CBus *bus, bool enable) { - bus->rail_ctl_fn(bus, enable); - if (enable) { - // wait for the bus supply to stabilize and the peripherals to start up. - // the MFI chip requires its reset pin to be stable for at least 10ms from startup. - delay_ms(20); - } -} - -//! Power down I2C bus power supply -//! Always lock bus and peripheral config access before use -static void prv_bus_rail_power_down(I2CBus *bus) { - if (!bus->rail_ctl_fn) { - return; - } - prv_rail_ctl(bus, false); - - // Drain through pull-ups - OutputConfig out_scl = { - .gpio = bus->scl_gpio.gpio, - .gpio_pin = bus->scl_gpio.gpio_pin, - .active_high = true - }; - gpio_output_init(&out_scl, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz); - gpio_output_set(&out_scl, false); - - OutputConfig out_sda = { - .gpio = bus->sda_gpio.gpio, - .gpio_pin = bus->sda_gpio.gpio_pin, - .active_high = true - }; - gpio_output_init(&out_sda, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz); - gpio_output_set(&out_sda, false); -} - -//! Configure bus pins for use by I2C peripheral -//! Lock bus and peripheral config access before configuring pins -static void prv_bus_pins_cfg_i2c(I2CBus *bus) { - gpio_af_init(&bus->scl_gpio, GPIO_OType_OD, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&bus->sda_gpio, GPIO_OType_OD, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); -} - -static void prv_bus_pins_cfg_input(I2CBus *bus) { - InputConfig in_scl = { - .gpio = bus->scl_gpio.gpio, - .gpio_pin = bus->scl_gpio.gpio_pin, - }; - gpio_input_init(&in_scl); - - InputConfig in_sda = { - .gpio = bus->sda_gpio.gpio, - .gpio_pin = bus->sda_gpio.gpio_pin, - }; - gpio_input_init(&in_sda); -} - -//! Power up I2C bus power supply -//! Always lock bus and peripheral config access before use -static void prv_bus_rail_power_up(I2CBus *bus) { - if (!bus->rail_ctl_fn) { - return; - } - - static const uint32_t MIN_STOP_TIME_MS = 10; - delay_ms(MIN_STOP_TIME_MS); - - prv_bus_pins_cfg_input(bus); - - prv_rail_ctl(bus, true); -} - -//! Configure the bus pins, enable the peripheral clock and initialize the I2C peripheral. -//! Always lock the bus and peripheral config access before enabling it -static void prv_bus_enable(I2CBus *bus) { - // Don't power up rail if the bus is already in use (enable can be called to reset bus) - if (bus->state->user_count == 0) { - prv_bus_rail_power_up(bus); - } - - prv_bus_pins_cfg_i2c(bus); - - i2c_hal_enable(bus); -} - -//! De-initialize and gate the clock to the peripheral -//! Power down rail if the bus supports that and no devices are using it -//! Always lock the bus and peripheral config access before disabling it -static void prv_bus_disable(I2CBus *bus) { - i2c_hal_disable(bus); - - // Do not de-power rail if there are still devices using bus (just reset peripheral and pin - // configuration during a bus reset) - if (bus->state->user_count == 0) { - prv_bus_rail_power_down(bus); - } else { - prv_bus_pins_cfg_input(bus); - } -} - -//! Perform a soft reset of the bus -//! Always lock the bus before reset -static void prv_bus_reset(I2CBus *bus) { - prv_bus_disable(bus); - prv_bus_enable(bus); -} - -/*---------------INIT/USE/RELEASE/RESET FUNCTIONS----------------------*/ - -void i2c_init(I2CBus *bus) { - PBL_ASSERTN(bus); - - *bus->state = (I2CBusState) {}; - - i2c_hal_init(bus); - - if (bus->rail_gpio.gpio) { - gpio_output_init(&bus->rail_gpio, GPIO_OType_PP, GPIO_Speed_2MHz); - } - prv_bus_rail_power_down(bus); -} - -void i2c_use(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - - if (slave->bus->state->user_count == 0) { - prv_bus_enable(slave->bus); - } - slave->bus->state->user_count++; -} - -void i2c_release(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - if (slave->bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted release of disabled bus %s", slave->bus->name); - return; - } - - slave->bus->state->user_count--; - if (slave->bus->state->user_count == 0) { - prv_bus_disable(slave->bus); - } -} - -void i2c_reset(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - - if (slave->bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted reset of disabled bus %s when still in use by " - "another bus", slave->bus->name); - return; - } - - PBL_LOG(LOG_LEVEL_WARNING, "Resetting I2C bus %s", slave->bus->name); - - // decrement user count for reset so that if this user is the only user, the - // bus will be powered down during the reset - slave->bus->state->user_count--; - - // Reset and reconfigure bus and pins - prv_bus_reset(slave->bus); - - // Restore user count - slave->bus->state->user_count++; -} - -bool i2c_bitbang_recovery(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - - static const int MAX_TOGGLE_COUNT = 10; - static const int TOGGLE_DELAY = 10; - - if (slave->bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted bitbang recovery on disabled bus %s", slave->bus->name); - return false; - } - - InputConfig in_sda = { - .gpio = slave->bus->sda_gpio.gpio, - .gpio_pin = slave->bus->sda_gpio.gpio_pin, - }; - gpio_input_init(&in_sda); - - OutputConfig out_scl = { - .gpio = slave->bus->scl_gpio.gpio, - .gpio_pin = slave->bus->scl_gpio.gpio_pin, - .active_high = true - }; - gpio_output_init(&out_scl, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz); - gpio_output_set(&out_scl, true); - - bool recovered = false; - for (int i = 0; i < MAX_TOGGLE_COUNT; ++i) { - gpio_output_set(&out_scl, false); - delay_ms(TOGGLE_DELAY); - gpio_output_set(&out_scl, true); - delay_ms(TOGGLE_DELAY); - - if (gpio_input_read(&in_sda)) { - recovered = true; - break; - } - } - if (recovered) { - PBL_LOG(LOG_LEVEL_DEBUG, "I2C Bus %s recovered", slave->bus->name); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "I2C Bus %s still hung after bitbang reset", slave->bus->name); - } - - prv_bus_pins_cfg_i2c(slave->bus); - prv_bus_reset(slave->bus); - - return recovered; -} - -/*--------------------DATA TRANSFER FUNCTIONS--------------------------*/ - -//! Wait a short amount of time for busy bit to clear -static bool prv_wait_for_not_busy(I2CBus *bus) { - static const int WAIT_DELAY = 10; // milliseconds - - if (i2c_hal_is_busy(bus)) { - delay_ms(WAIT_DELAY); - if (i2c_hal_is_busy(bus)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timed out waiting for bus %s to become non-busy", bus->name); - return false; - } - } - - return true; -} - -//! Set up and start a transfer to a bus, wait for it to finish and clean up after the transfer -//! has completed -static bool prv_do_transfer(I2CBus *bus, TransferDirection direction, uint16_t device_address, - uint8_t register_address, uint32_t size, uint8_t *data, - TransferType type) { - if (bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted access to disabled bus %s", bus->name); - return false; - } - - // If bus is busy (it shouldn't be as this function waits for the bus to report a non-idle state - // before exiting) reset the bus and wait for it to become not-busy - // Exit if bus remains busy. User module should reset the I2C module at this point - if (i2c_hal_is_busy(bus)) { - prv_bus_reset(bus); - - if (!prv_wait_for_not_busy(bus)) { - // Bus did not recover after reset - PBL_LOG(LOG_LEVEL_ERROR, "I2C bus did not recover after reset (%s)", bus->name); - return false; - } - } - - // Take binary semaphore so that next take will block - PBL_ASSERT(prv_semaphore_take(bus->state), "Could not acquire semaphore token"); - - // Set up transfer - bus->state->transfer = (I2CTransfer) { - .device_address = device_address, - .register_address = register_address, - .direction = direction, - .type = type, - .size = size, - .idx = 0, - .data = data, - }; - - i2c_hal_init_transfer(bus); - - bus->state->transfer_nack_count = 0; - - bool result = false; - bool complete = false; - do { - i2c_hal_start_transfer(bus); - - // Wait on semaphore until it is released by interrupt or a timeout occurs - if (prv_semaphore_wait(bus->state)) { - if ((bus->state->transfer_event == I2CTransferEvent_TransferComplete) || - (bus->state->transfer_event == I2CTransferEvent_Error)) { - // Track the max transfer duration so we can keep tabs on the MFi chip's nacking behavior - - if (bus->state->transfer_event == I2CTransferEvent_Error) { - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error on bus %s", bus->name); - } - complete = true; - result = (bus->state->transfer_event == I2CTransferEvent_TransferComplete); - } else if (bus->state->transfer_nack_count < I2C_NACK_COUNT_MAX) { - // NACK received after start condition sent: the MFI chip NACKs start conditions whilst it - // is busy - // Retry start condition after a short delay. - // A NACK count is incremented for each NACK received, so that legitimate NACK - // errors cause the transfer to be aborted (after the NACK count max has been reached). - - bus->state->transfer_nack_count++; - - // Wait 1-2ms: - delay_ms(2); - - } else { - // Too many NACKs received, abort transfer - i2c_hal_abort_transfer(bus); - complete = true; - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error: too many NACKs received on bus %s", bus->name); - break; - } - - } else { - // Timeout, abort transfer - i2c_hal_abort_transfer(bus); - complete = true; - PBL_LOG(LOG_LEVEL_ERROR, "Transfer timed out on bus %s", bus->name); - break; - } - } while (!complete); - - // Return semaphore token so another transfer can be started - prv_semaphore_give(bus->state); - - // Wait for bus to to clear the busy flag before a new transfer starts - // Theoretically a transfer could complete successfully, but the busy flag never clears, - // which would cause the next transfer to fail - if (!prv_wait_for_not_busy(bus)) { - // Reset I2C bus if busy flag does not clear - prv_bus_reset(bus); - } - - return result; -} - -bool i2c_read_register(I2CSlavePort *slave, uint8_t register_address, uint8_t *result) { - return i2c_read_register_block(slave, register_address, 1, result); -} - -bool i2c_read_register_block(I2CSlavePort *slave, uint8_t register_address_start, - uint32_t read_size, uint8_t* result_buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(result_buffer); - // Do transfer locks the bus - bool result = prv_do_transfer(slave->bus, Read, slave->address, register_address_start, read_size, - result_buffer, SendRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Read failed on bus %s", slave->bus->name); - } - - return result; -} - -bool i2c_read_block(I2CSlavePort *slave, uint32_t read_size, uint8_t* result_buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(result_buffer); - - bool result = prv_do_transfer(slave->bus, Read, slave->address, 0, read_size, result_buffer, - NoRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Block read failed on bus %s", slave->bus->name); - } - - return result; -} - -bool i2c_write_register(I2CSlavePort *slave, uint8_t register_address, uint8_t value) { - return i2c_write_register_block(slave, register_address, 1, &value); -} - -bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_start, - uint32_t write_size, const uint8_t* buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(buffer); - // Do transfer locks the bus - bool result = prv_do_transfer(slave->bus, Write, slave->address, register_address_start, - write_size, (uint8_t*)buffer, SendRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Write failed on bus %s", slave->bus->name); - } - - return result; -} - -bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(buffer); - - // Do transfer locks the bus - bool result = prv_do_transfer(slave->bus, Write, slave->address, 0, write_size, (uint8_t*)buffer, - NoRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Block write failed on bus %s", slave->bus->name); - } - - return result; -} - -/*----------------------HAL INTERFACE--------------------------------*/ - -void i2c_handle_transfer_event(I2CBus *bus, I2CTransferEvent event) { - bus->state->transfer_event = event; - prv_semaphore_give_from_isr(bus->state); -} diff --git a/platform/robert/boot/src/drivers/i2c/i2c_definitions.h b/platform/robert/boot/src/drivers/i2c/i2c_definitions.h deleted file mode 100644 index 65be11bbd9..0000000000 --- a/platform/robert/boot/src/drivers/i2c/i2c_definitions.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum I2CTransferEvent { - I2CTransferEvent_Timeout, - I2CTransferEvent_TransferComplete, - I2CTransferEvent_NackReceived, - I2CTransferEvent_Error, -} I2CTransferEvent; - -typedef enum { - I2CTransferDirection_Read, - I2CTransferDirection_Write -} I2CTransferDirection; - -typedef enum { - // Send a register address, followed by a repeat start for reads - I2CTransferType_SendRegisterAddress, - - // Do not send a register address; used for block writes/reads - I2CTransferType_NoRegisterAddress -} I2CTransferType; - -typedef enum I2CTransferState { - I2CTransferState_WriteAddressTx, - I2CTransferState_WriteRegAddress, - I2CTransferState_RepeatStart, - I2CTransferState_WriteAddressRx, - I2CTransferState_WaitForData, - I2CTransferState_ReadData, - I2CTransferState_WriteData, - I2CTransferState_EndWrite, - I2CTransferState_Complete, -} I2CTransferState; - -typedef struct I2CTransfer { - I2CTransferState state; - uint16_t device_address; - I2CTransferDirection direction; - I2CTransferType type; - uint8_t register_address; - uint32_t size; - uint32_t idx; - uint8_t *data; -} I2CTransfer; - -typedef struct I2CBusState { - I2CTransfer transfer; - I2CTransferEvent transfer_event; - int transfer_nack_count; - int user_count; - volatile bool busy; -} I2CBusState; - -struct I2CBus { - I2CBusState *const state; - const struct I2CBusHal *const hal; - AfConfig scl_gpio; ///< Alternate Function configuration for SCL pin - AfConfig sda_gpio; ///< Alternate Function configuration for SDA pin - OutputConfig rail_gpio; ///< Control pin for rail - void (* const rail_ctl_fn)(I2CBus *device, bool enabled); ///< Control function for this rail. - const char *name; //! Device ID for logging purposes -}; - -struct I2CSlavePort { - const I2CBus *bus; - uint16_t address; -}; - -//! Initialize the I2C driver. -void i2c_init(I2CBus *bus); - -//! Transfer event handler implemented in i2c.c and called by HAL implementation -void i2c_handle_transfer_event(I2CBus *device, I2CTransferEvent event); - -#define I2C_DEBUG(fmt, args...) \ - PBL_LOG_COLOR_D(LOG_DOMAIN_I2C, LOG_LEVEL_DEBUG, LOG_COLOR_LIGHT_MAGENTA, fmt, ## args) diff --git a/platform/robert/boot/src/drivers/i2c/i2c_hal.c b/platform/robert/boot/src/drivers/i2c/i2c_hal.c deleted file mode 100644 index c171c46eb9..0000000000 --- a/platform/robert/boot/src/drivers/i2c/i2c_hal.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "i2c_hal.h" -#include "i2c_definitions.h" -#include "i2c_hal_definitions.h" - -#include "drivers/periph_config.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" - -#include "stm32f7xx.h" - -#define I2C_IRQ_PRIORITY (0xc) -#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000) -#define I2C_FAST_MODE_CLOCK_SPEED_MAX (400000) -#define I2C_FAST_MODE_PLUS_CLOCK_SPEED_MAX (1000000) - -#define TIMINGR_MASK_PRESC (0x0F) -#define TIMINGR_MASK_SCLH (0xFF) -#define TIMINGR_MASK_SCLL (0xFF) - -#define CR1_CLEAR_MASK (0x00CFE0FF) - -#define CR2_CLEAR_MASK (0x07FF7FFF) -#define CR2_NBYTES_OFFSET (16) -#define CR2_TRANSFER_SETUP_MASK (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | \ - I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | \ - I2C_CR2_STOP) - -typedef union PACKED TIMINGR { - struct { - int32_t SCLL:8; - int32_t SCLH:8; - int32_t SDADEL:4; - int32_t SCLDEL:4; - int32_t reserved:4; - int32_t PRESC:4; - }; - int32_t reg; -} TIMINGR; - -static void prv_i2c_deinit(I2CBus *bus) { - // Reset the clock to the peripheral - RCC_APB1PeriphResetCmd(bus->hal->clock_ctrl, ENABLE); - RCC_APB1PeriphResetCmd(bus->hal->clock_ctrl, DISABLE); -} - -void i2c_hal_init(I2CBus *bus) { - NVIC_SetPriority(bus->hal->ev_irq_channel, I2C_IRQ_PRIORITY); - NVIC_SetPriority(bus->hal->er_irq_channel, I2C_IRQ_PRIORITY); - NVIC_EnableIRQ(bus->hal->ev_irq_channel); - NVIC_EnableIRQ(bus->hal->er_irq_channel); - prv_i2c_deinit(bus); -} - -static void prv_i2c_init(I2C_TypeDef *i2c, TIMINGR timingr) { - // Soft reset of the state machine and status bits by disabling the peripheral. - // Note: PE must be low for 3 APB cycles after this is done for the reset to be successful - i2c->CR1 &= ~I2C_CR1_PE; - - i2c->CR1 &= ~CR1_CLEAR_MASK; - - // Set the timing register - i2c->TIMINGR = timingr.reg; - - // I2C only used as a master; disable slave address acknowledgement - i2c->OAR1 = 0; - i2c->OAR2 = 0; - - // Enable i2c Peripheral; clear any configured interrupt bits; use analog filter - i2c->CR1 |= I2C_CR1_PE; - - // Clear CR2, making it ready for the next transaction - i2c->CR2 &= ~CR2_CLEAR_MASK; -} - -void i2c_hal_enable(I2CBus *bus) { - // We don't need to support Fast Mode Plus yet, so make sure the desired clock speed is less than - // the maximum Fast Mode clock speed. - // When Fast Mode support is added the duty-cycle settings will probably have to be re-thought. - PBL_ASSERT(bus->hal->clock_speed <= I2C_FAST_MODE_CLOCK_SPEED_MAX, - "Fast Mode Plus not yet supported"); - - uint32_t duty_cycle_low = 1; - uint32_t duty_cycle_high = 1; - if (bus->hal->clock_speed > I2C_NORMAL_MODE_CLOCK_SPEED_MAX) { // Fast mode - if (bus->hal->duty_cycle == I2CDutyCycle_16_9) { - duty_cycle_low = 16; - duty_cycle_high = 9; - } else if (bus->hal->duty_cycle == I2CDutyCycle_2) { - duty_cycle_low = 2; - duty_cycle_high = 1; - } else { - WTF; // It might be possible to encode a duty cycle differently from the legacy I2C, if it's - // ever necessary. Currently it's not, so just maintain the previous implementation - } - } - - RCC_ClocksTypeDef rcc_clocks; - RCC_GetClocksFreq(&rcc_clocks); - - uint32_t prescaler = rcc_clocks.PCLK1_Frequency / - (bus->hal->clock_speed * (duty_cycle_low + duty_cycle_high)); - if ((rcc_clocks.PCLK1_Frequency % - (bus->hal->clock_speed * (duty_cycle_low + duty_cycle_high))) == 0) { - // Prescaler is PRESC + 1. This subtracts one so that exact dividers are correct, but if there - // is an integer remainder, the prescaler will ensure that the clock frequency is within spec. - prescaler -= 1; - } - // Make sure all the values fit in their corresponding fields - PBL_ASSERTN((duty_cycle_low <= TIMINGR_MASK_SCLL) && - (duty_cycle_high <= TIMINGR_MASK_SCLH) && - (prescaler <= TIMINGR_MASK_PRESC)); - - periph_config_enable(bus->hal->i2c, bus->hal->clock_ctrl); - - // We currently don't need to worry about the other TIMINGR fields (they come out to 0), but might - // need to revisit this if we ever need FM+ speeds. - TIMINGR timingr = { - .PRESC = prescaler, - .SCLH = duty_cycle_high - 1, // Duty cycle high is SCLH + 1 - .SCLL = duty_cycle_low - 1, // Duty cycle low is SCLL + 1 - }; - prv_i2c_init(bus->hal->i2c, timingr); -} - -void i2c_hal_disable(I2CBus *bus) { - periph_config_disable(bus->hal->i2c, bus->hal->clock_ctrl); - prv_i2c_deinit(bus); -} - -bool i2c_hal_is_busy(I2CBus *bus) { - return ((bus->hal->i2c->ISR & I2C_ISR_BUSY) != 0); -} - -static void prv_disable_all_interrupts(I2CBus *bus) { - bus->hal->i2c->CR1 &= ~(I2C_CR1_TXIE | - I2C_CR1_RXIE | - I2C_CR1_TCIE | - I2C_CR1_NACKIE | - I2C_CR1_ERRIE); -} - -void i2c_hal_abort_transfer(I2CBus *bus) { - // Disable all interrupts on the bus - prv_disable_all_interrupts(bus); - // Generate a stop condition - bus->hal->i2c->CR2 |= I2C_CR2_STOP; -} - -void i2c_hal_init_transfer(I2CBus *bus) { - I2CTransfer *transfer = &bus->state->transfer; - - if (transfer->type == I2CTransferType_SendRegisterAddress) { - transfer->state = I2CTransferState_WriteRegAddress; - } else { - if (transfer->direction == I2CTransferDirection_Read) { - transfer->state = I2CTransferState_ReadData; - } else { - transfer->state = I2CTransferState_WriteData; - } - } -} - -static void prv_enable_interrupts(I2CBus *bus) { - bus->hal->i2c->CR1 |= I2C_CR1_ERRIE | // Enable error interrupt - I2C_CR1_NACKIE | // Enable NACK interrupt - I2C_CR1_TCIE | // Enable transfer complete interrupt - I2C_CR1_TXIE; // Enable transmit interrupt - if (bus->state->transfer.direction == I2CTransferDirection_Read) { - bus->hal->i2c->CR1 |= I2C_CR1_RXIE; // Enable receive interrupt - } -} - -static void prv_resume_transfer(I2CBus *bus, bool generate_start) { - const I2CTransfer *transfer = &bus->state->transfer; - uint32_t cr2_value = transfer->device_address & I2C_CR2_SADD; - - if ((transfer->direction == I2CTransferDirection_Read) && - (transfer->state != I2CTransferState_WriteRegAddress)) { - cr2_value |= I2C_CR2_RD_WRN; - } - - const uint32_t remaining = bus->state->transfer.size - bus->state->transfer.idx; - if (remaining > UINT8_MAX) { - cr2_value |= I2C_CR2_RELOAD; - cr2_value |= I2C_CR2_NBYTES; - } else { - cr2_value |= (remaining << CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES; - } - - if (generate_start) { - cr2_value |= I2C_CR2_START; - } - - bus->hal->i2c->CR2 = cr2_value; -} - -void i2c_hal_start_transfer(I2CBus *bus) { - prv_enable_interrupts(bus); - if (bus->state->transfer.state == I2CTransferState_WriteRegAddress) { - // For writes, we'll reload with the payload once we send the address. Otherwise, we'd need to - // send a repeated start, which we don't want to do. - const bool reload = bus->state->transfer.direction == I2CTransferDirection_Write; - bus->hal->i2c->CR2 = (bus->state->transfer.device_address & I2C_CR2_SADD) | - (1 << CR2_NBYTES_OFFSET) | - (reload ? I2C_CR2_RELOAD : 0) | - I2C_CR2_START; - } else { - prv_resume_transfer(bus, true /* generate_start */); - } -} - -/*------------------------INTERRUPT FUNCTIONS--------------------------*/ -static void prv_end_transfer_irq(I2CBus *bus, I2CTransferEvent event) { - prv_disable_all_interrupts(bus); - - // Generate stop condition - bus->hal->i2c->CR2 |= I2C_CR2_STOP; - bus->state->transfer.state = I2CTransferState_Complete; - - i2c_handle_transfer_event(bus, event); -} - -//! Handle an IRQ event on the specified \a bus -static void prv_event_irq_handler(I2CBus *bus) { - I2C_TypeDef *i2c = bus->hal->i2c; - I2CTransfer *transfer = &bus->state->transfer; - switch (transfer->state) { - case I2CTransferState_WriteRegAddress: - if ((i2c->ISR & I2C_ISR_TXIS) != 0) { - i2c->TXDR = transfer->register_address; - } - if ((transfer->direction == I2CTransferDirection_Read) && (i2c->ISR & I2C_ISR_TC)) { - // done writing the register address for a read request - start a read request - transfer->state = I2CTransferState_ReadData; - prv_resume_transfer(bus, true /* generate_start */); - } else if ((transfer->direction == I2CTransferDirection_Write) && (i2c->ISR & I2C_ISR_TCR)) { - // done writing the register address for a write request - "reload" the write payload - transfer->state = I2CTransferState_WriteData; - prv_resume_transfer(bus, false /* !generate_start */); - } - if ((i2c->ISR & I2C_ISR_NACKF) != 0) { - i2c->ICR |= I2C_ICR_NACKCF; - i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived); - return; - } - break; - - case I2CTransferState_ReadData: - if ((i2c->ISR & I2C_ISR_RXNE) != 0) { - transfer->data[transfer->idx++] = i2c->RXDR; - } - if ((i2c->ISR & I2C_ISR_TCR) != 0) { - prv_resume_transfer(bus, false /* !generate_start */); - } - if ((i2c->ISR & I2C_ISR_TC) != 0) { - prv_end_transfer_irq(bus, I2CTransferEvent_TransferComplete); - return; - } - break; - - case I2CTransferState_WriteData: - if ((i2c->ISR & I2C_ISR_TXIS) != 0) { - i2c->TXDR = transfer->data[transfer->idx++]; - } - if ((i2c->ISR & I2C_ISR_NACKF) != 0) { - i2c->ICR |= I2C_ICR_NACKCF; - return i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived); - } - if ((i2c->ISR & I2C_ISR_TCR) != 0) { - prv_resume_transfer(bus, false /* !generate_start */); - } - if ((i2c->ISR & I2C_ISR_TC) != 0) { - prv_end_transfer_irq(bus, I2CTransferEvent_TransferComplete); - return; - } - break; - - case I2CTransferState_Complete: - if (i2c->ISR & I2C_ISR_TXE) { - // We seem to get a spurious interrupt after the last byte is sent - // There is no bit to specifically disable this interrupt and the interrupt may have already - // been pended when we would disable it, so just handle it silently. - break; - } - // Fallthrough - - // These extra states were defined for the F4 implementation but are not necessary for the F7, - // because the interrupt scheme is a lot nicer. - case I2CTransferState_RepeatStart: - case I2CTransferState_EndWrite: - case I2CTransferState_WaitForData: - case I2CTransferState_WriteAddressRx: - case I2CTransferState_WriteAddressTx: - default: - WTF; - } -} - -static void prv_error_irq_handler(I2CBus *bus) { - I2C_TypeDef *i2c = bus->hal->i2c; - if ((i2c->ISR & I2C_ISR_BERR) != 0) { - i2c->ICR |= I2C_ICR_BERRCF; - } - if ((i2c->ISR & I2C_ISR_OVR) != 0) { - i2c->ICR |= I2C_ICR_OVRCF; - } - if ((i2c->ISR & I2C_ISR_ARLO) != 0) { - i2c->ICR |= I2C_ICR_ARLOCF; - } - prv_end_transfer_irq(bus, I2CTransferEvent_Error); -} - -void i2c_hal_event_irq_handler(I2CBus *bus) { - prv_event_irq_handler(bus); -} - -void i2c_hal_error_irq_handler(I2CBus *bus) { - prv_error_irq_handler(bus); -} diff --git a/platform/robert/boot/src/drivers/i2c/i2c_hal.h b/platform/robert/boot/src/drivers/i2c/i2c_hal.h deleted file mode 100644 index 1853dfa644..0000000000 --- a/platform/robert/boot/src/drivers/i2c/i2c_hal.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include - -void i2c_hal_init(I2CBus *bus); - -void i2c_hal_enable(I2CBus *bus); - -void i2c_hal_disable(I2CBus *bus); - -bool i2c_hal_is_busy(I2CBus *bus); - -void i2c_hal_abort_transfer(I2CBus *bus); - -void i2c_hal_init_transfer(I2CBus *bus); - -void i2c_hal_start_transfer(I2CBus *bus); diff --git a/platform/robert/boot/src/drivers/i2c/i2c_hal_definitions.h b/platform/robert/boot/src/drivers/i2c/i2c_hal_definitions.h deleted file mode 100644 index 3143f2a557..0000000000 --- a/platform/robert/boot/src/drivers/i2c/i2c_hal_definitions.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum I2CDutyCycle { - I2CDutyCycle_16_9, - I2CDutyCycle_2 -} I2CDutyCycle; - -typedef struct I2CBusHal { - I2C_TypeDef *const i2c; - uint32_t clock_ctrl; ///< Peripheral clock control flag - uint32_t clock_speed; ///< Bus clock speed - I2CDutyCycle duty_cycle; ///< Bus clock duty cycle in fast mode - IRQn_Type ev_irq_channel; ///< I2C Event interrupt (One of X_IRQn). For example, I2C1_EV_IRQn. - IRQn_Type er_irq_channel; ///< I2C Error interrupt (One of X_IRQn). For example, I2C1_ER_IRQn. -} I2CBusHal; - -void i2c_hal_event_irq_handler(I2CBus *device); -void i2c_hal_error_irq_handler(I2CBus *device); diff --git a/platform/robert/boot/src/drivers/max14690_pmic.c b/platform/robert/boot/src/drivers/max14690_pmic.c deleted file mode 100644 index 3713fc320b..0000000000 --- a/platform/robert/boot/src/drivers/max14690_pmic.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* This file should probably go in the stm32f4 folder */ - -#include "drivers/pmic.h" - -#include "board/board.h" -#include "drivers/dbgserial.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/delay.h" - -#include "stm32f7xx.h" - -#include - -#define MAX14690_ADDR (0x50) - -#define MAX14690_WHOAMI (0x01) - -//! The addresses of the registers that we can read using i2c -typedef enum PmicRegisters { - PmicRegisters_CHIP_ID = 0x00, - PmicRegisters_CHIP_REV = 0x01, - PmicRegisters_STATUSA = 0x02, - PmicRegisters_STATUSB = 0x03, - PmicRegisters_INTA = 0x05, - PmicRegisters_INTB = 0x06, - PmicRegisters_INT_MASK_A = 0x07, - PmicRegisters_INT_MASK_B = 0x08, - PmicRegisters_CHG_CNTL_A = 0x0A, - PmicRegisters_BUCK1_CONFIG = 0x0D, - PmicRegisters_BUCK2_CONFIG = 0x0F, - PmicRegisters_LDO1_CONFIG = 0x12, - PmicRegisters_LDO2_CONFIG = 0x14, - PmicRegisters_LDO3_CONFIG = 0x16, - PmicRegisters_MON_CFG = 0x19, - PmicRegisters_PWR_CFG = 0x1F -} PmicRegisters; - -//! The different power rails that our PMIC controls -typedef enum PmicRail { - PmicRail_BUCK1, //!< 1.2V - PmicRail_BUCK2, //!< 1.8V - PmicRail_LDO1, //!< 2.0V - Auto - RTC - PmicRail_LDO2, //!< 3.2V - Manual - FPGA - - //! snowy_bb: 2.5V - Manual - MFi, Magnetometer - //! snowy_evt: 1.8V - Manual - MFi - PmicRail_LDO3 -} PmicRail; - -//! Gives configuration information for reading a given rail through the monitor pin. -typedef struct { - const char* name; //!< Name for the rail. - - //! What ratio we need to divide by in order to bring it into the range we can sense. We can - //! only read between 0 and 1.8Vs, so we need to use the PMIC hardware to divide it down before - //! sending it to us. Valid values are 1-4. - uint8_t ratio; - - //! The binary value we need to put in the register to select the rail. - uint8_t source_config; -} PmicMonConfig; - -// Using the Binary constants GCC extension here, supported in GCC and Clang -// https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html -static const PmicMonConfig MON_CONFIG[] = { - { "+VBAT", 3, 0b001 }, // 3:1 -}; - -static const int PMIC_MON_CONFIG_VBAT_INDEX = 0; - -/* Private Function Definitions */ -static bool prv_is_alive(void); -static void prv_set_pin_config(void); - -//! Request that the rail be used or released. Internally refcounted per rail so you don't have -//! to worry about turning this off on another client. -static bool prv_update_rail_state(PmicRail rail, bool enable); - -static void prv_mon_config_lock(void) { -} - -static void prv_mon_config_unlock(void) { -} - -static bool prv_read_register(uint8_t register_address, uint8_t *result) { - i2c_use(I2C_MAX14690); - bool rv = i2c_read_register(I2C_MAX14690, register_address, result); - i2c_release(I2C_MAX14690); - return (rv); -} - - -static bool prv_write_register(uint8_t register_address, uint8_t value) { - i2c_use(I2C_MAX14690); - bool rv = i2c_write_register(I2C_MAX14690, register_address, value); - i2c_release(I2C_MAX14690); - return (rv); -} - -/* Public Functions */ -bool pmic_init(void) { - prv_set_pin_config(); - - if (!prv_is_alive()) { - return false; - } - - // Power up 3.2V rail - prv_update_rail_state(PmicRail_LDO2, true); - - return true; -} - -static bool prv_update_rail_state(PmicRail rail, bool enable) { - static int8_t s_ldo2_ref_count = 0; - static int8_t s_ldo3_ref_count = 0; - - int8_t *ref_count; - uint8_t rail_control_reg = 0; - - if (rail == PmicRail_LDO2) { - rail_control_reg = PmicRegisters_LDO2_CONFIG; - ref_count = &s_ldo2_ref_count; - } else if (rail == PmicRail_LDO3) { - rail_control_reg = PmicRegisters_LDO3_CONFIG; - ref_count = &s_ldo3_ref_count; - } else { - WTF; - } - - uint8_t register_value; - bool success = prv_read_register(rail_control_reg, ®ister_value); - - if (!success) { - // Failed to read the current register value - return false; - } - - if (enable) { - if (*ref_count) { - (*ref_count)++; - return true; - } else { - // Set the register byte to XXXXX01X to enable the rail, mask and set - register_value = (register_value & ~0x06) | 0x02; - - success = prv_write_register(rail_control_reg, register_value); - - if (success) { - // We enabled the rail! - *ref_count = 1; - - // We need to wait a bit for the rail to stabilize before continuing to use the device. - // It takes 2.6ms for the LDO rails to ramp. - delay_ms(3); - - return true; - } - return false; - } - } else { - if (*ref_count <= 1) { - // Set the register byte to XXXXX00X to disable the rail, just mask - register_value = (register_value & ~0x06); - - success = prv_write_register(rail_control_reg, register_value); - - if (success) { - // We disabled the rail! - *ref_count = 0; - return true; - } - return false; - } else { - (*ref_count)--; - return true; - } - } -} - -bool pmic_power_off(void) { - bool ret = prv_write_register(PmicRegisters_PWR_CFG, 0xB2); - - if (ret) { - // Goodbye cruel world. The PMIC should be removing our power at any time now. - - while (1) {} - __builtin_unreachable(); - } - - return false; -} - -static bool prv_set_mon_config_register(uint8_t value) { - return prv_write_register(PmicRegisters_MON_CFG, value); -} - -static bool prv_set_mon_config(const PmicMonConfig *config) { - const uint8_t ratio_config = 4 - config->ratio; // 4:1 is 0b00, 1:1 is 0b11. - - const uint8_t register_value = (ratio_config << 4) | config->source_config; - bool result = prv_set_mon_config_register(register_value); - - // Need to wait a short period of time for the reading to settle due to capacitance on the line. - delay_us(200); - - return result; -} - -bool pmic_enable_battery_measure(void) { - prv_mon_config_lock(); - - return prv_set_mon_config(&MON_CONFIG[PMIC_MON_CONFIG_VBAT_INDEX]); - - // Don't prv_unlock, we don't want anyone else mucking with the mon config until - // pmic_disable_battery_measure is called. -} - -bool pmic_disable_battery_measure(void) { - bool result = prv_set_mon_config_register(0); - - // Releases the lock that was previously aquired in pmic_enable_battery_measure. - prv_mon_config_unlock(); - - return result; -} - -bool pmic_set_charger_state(bool enable) { - // Defaults to ON - // Default value is 0xF7 - const uint8_t register_value = enable ? 0xf7 : 0xf6; - - bool result = prv_write_register(PmicRegisters_CHG_CNTL_A, register_value); - - return result; -} - - -bool pmic_is_charging(void) { - uint8_t val; - if (!prv_read_register(PmicRegisters_STATUSA, &val)) { - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are charging - return true; - } - - uint8_t chgstat = val & 0x07; // Mask off only charging status - - if (chgstat == 0x02 || // Pre-charge in progress - chgstat == 0x03 || // Fast charge, CC - chgstat == 0x04 || // Fast charge, CV - chgstat == 0x05) { // Maintain charge - return true; - } else { - return false; - } -} - -bool pmic_is_usb_connected(void) { - uint8_t val; - if (!prv_read_register(PmicRegisters_STATUSB, &val)) { - return false; - } - - bool usb_connected = (val >> 3) & 1; - - return usb_connected; -} - -void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision) { - prv_read_register(PmicRegisters_CHIP_ID, chip_id); - prv_read_register(PmicRegisters_CHIP_REV, chip_revision); -} - - -/* Private Function Implementations */ -static bool prv_is_alive(void) { - uint8_t val; - prv_read_register(0x00, &val); - - if (val == MAX14690_WHOAMI) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found the max14690"); - return true; - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Error reading max14690 WHOAMI byte"); - return false; - } -} - -static void prv_set_pin_config(void) { - periph_config_acquire_lock(); - - // Initialize the GPIOs for the 4V5 & 6V6 rails - gpio_output_init(&BOARD_CONFIG_POWER.rail_4V5_ctrl, GPIO_OType_OD, GPIO_Speed_50MHz); - if (BOARD_CONFIG_POWER.rail_6V6_ctrl.gpio) { - gpio_output_init(&BOARD_CONFIG_POWER.rail_6V6_ctrl, GPIO_OType_OD, GPIO_Speed_50MHz); - } - gpio_output_init(&BOARD_CONFIG_ACCESSORY.power_en, GPIO_OType_OD, GPIO_Speed_50MHz); - - periph_config_release_lock(); -} - -void set_4V5_power_state(bool enabled) { - gpio_output_set(&BOARD_CONFIG_POWER.rail_4V5_ctrl, enabled); -} - -void set_6V6_power_state(bool enabled) { - if (BOARD_CONFIG_POWER.rail_6V6_ctrl.gpio) { - gpio_output_set(&BOARD_CONFIG_POWER.rail_6V6_ctrl, enabled); - } -} diff --git a/platform/robert/boot/src/drivers/periph_config.c b/platform/robert/boot/src/drivers/periph_config.c deleted file mode 100644 index 15df2780b5..0000000000 --- a/platform/robert/boot/src/drivers/periph_config.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/periph_config.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "stm32f7xx.h" - -#define PERIPH_CONFIG_DEBUG 0 - -#if PERIPH_CONFIG_DEBUG -#define PERIPH_CONFIG_LOG(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) -#else -#define PERIPH_CONFIG_LOG(fmt, args...) -#endif - -typedef void (*ClockCmd)(uint32_t periph, FunctionalState state); - -#if PERIPH_CONFIG_DEBUG -static const char *prv_string_for_cmd(ClockCmd cmd) { - if (cmd == RCC_APB1PeriphClockCmd) { - return "APB1"; - } else if (cmd == RCC_APB2PeriphClockCmd) { - return "APB2"; - } else if (cmd == RCC_AHB1PeriphClockCmd) { - return "AHB1"; - } else if (cmd == RCC_AHB2PeriphClockCmd) { - return "AHB2"; - } else { - return NULL; - } -} -#endif - -// F(S)MC is the only AHB3 peripheral -#ifdef FMC_R_BASE -#define AHB3_BASE FMC_R_BASE -#else -#define AHB3_BASE FSMC_R_BASE -#endif - -_Static_assert(APB1PERIPH_BASE < APB2PERIPH_BASE, "Clock mapping assumptions don't hold"); -_Static_assert(APB2PERIPH_BASE < AHB1PERIPH_BASE, "Clock mapping assumptions don't hold"); -_Static_assert(AHB1PERIPH_BASE < AHB2PERIPH_BASE, "Clock mapping assumptions don't hold"); -_Static_assert(AHB2PERIPH_BASE < AHB3_BASE, "Clock mapping assumptions don't hold"); - -// Note: this works only with peripheral (<...>Typedef_t *) defines, not with RCC defines -static ClockCmd prv_get_clock_cmd(uintptr_t periph_addr) { - PBL_ASSERTN(periph_addr >= APB1PERIPH_BASE); - if (periph_addr < APB2PERIPH_BASE) { - return RCC_APB1PeriphClockCmd; - } else if (periph_addr < AHB1PERIPH_BASE) { - return RCC_APB2PeriphClockCmd; - } else if (periph_addr < AHB2PERIPH_BASE) { - return RCC_AHB1PeriphClockCmd; - } else if (periph_addr < AHB3_BASE) { - return RCC_AHB2PeriphClockCmd; - } else { - return RCC_AHB3PeriphClockCmd; - } -} - -void periph_config_init(void) { -} - -void periph_config_acquire_lock(void) { -} - -void periph_config_release_lock(void) { -} - -void periph_config_enable(void *periph, uint32_t rcc_bit) { - ClockCmd clock_cmd = prv_get_clock_cmd((uintptr_t)periph); -#if PERIPH_CONFIG_DEBUG - if (prv_string_for_cmd(clock_cmd)) - PERIPH_CONFIG_LOG("Enabling clock %s", prv_string_for_cmd(clock_cmd)); -#endif - clock_cmd(rcc_bit, ENABLE); -} - -void periph_config_disable(void *periph, uint32_t rcc_bit) { - ClockCmd clock_cmd = prv_get_clock_cmd((uintptr_t)periph); -#if PERIPH_CONFIG_DEBUG - if (prv_string_for_cmd(clock_cmd)) - PERIPH_CONFIG_LOG("Disabling clock %s", prv_string_for_cmd(clock_cmd)); -#endif - clock_cmd(rcc_bit, DISABLE); -} diff --git a/platform/robert/boot/src/drivers/periph_config.h b/platform/robert/boot/src/drivers/periph_config.h deleted file mode 100644 index 80b6c75b04..0000000000 --- a/platform/robert/boot/src/drivers/periph_config.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void periph_config_init(void); -void periph_config_acquire_lock(void); -void periph_config_release_lock(void); -void periph_config_enable(void *periph, uint32_t rcc_bit); -void periph_config_disable(void *periph, uint32_t rcc_bit); diff --git a/platform/robert/boot/src/drivers/pmic.h b/platform/robert/boot/src/drivers/pmic.h deleted file mode 100644 index 2ab8d01b1c..0000000000 --- a/platform/robert/boot/src/drivers/pmic.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Initialize the PMIC driver. Call this once at startup. -bool pmic_init(void); - -//! Tell the PMIC to power off the board and enter a standby-like state. All components will -//! have their power removed (except for the RTC so we'll still keep time) and the PMIC itself -//! will monitor the buttons for when to wake up. -bool pmic_power_off(void); - -//! Enable the battery monitor portion of the PMIC. Remember to turn this off with -//! pmic_disable_battery_measure when immediate readings aren't required. -bool pmic_enable_battery_measure(void); - -//! Disable the battery monitor portion of the PMIC. -bool pmic_disable_battery_measure(void); - -//! Enable and disable the charging portion of the PMIC. -bool pmic_set_charger_state(bool enable); - -//! @return true if the PMIC thinks we're charging (adding additional charge to the battery). -//! Note that once we hit full charge we'll no longer be charging, which is a different state -//! that pmic_is_usb_connected. -bool pmic_is_charging(void); - -//! @return true if a usb-ish charger cable is currently connected. -bool pmic_is_usb_connected(void); - -//! Read information about the chip for tracking purposes. -void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision); - -//! Enables the LDO3 power rail. Used for the MFi/Magnetometer on snowy_bb, MFi on snowy_evt. -void set_ldo3_power_state(bool enabled); - -//! Enables the 4.5V power rail. Used for the display on snowy. -void set_4V5_power_state(bool enabled); - -//! Enables the 6.6V power rail. Used for the display on snowy. -void set_6V6_power_state(bool enabled); - -//! Enables power to the accessory connector. -void set_accessory_power_state(bool enabled); diff --git a/platform/robert/boot/src/drivers/pwr.c b/platform/robert/boot/src/drivers/pwr.c deleted file mode 100644 index 54f9e69e65..0000000000 --- a/platform/robert/boot/src/drivers/pwr.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/pwr.h" - -#include "drivers/periph_config.h" - -#include "stm32f7xx.h" - -void pwr_access_backup_domain(bool enable_access) { - periph_config_enable(PWR, RCC_APB1Periph_PWR); - if (enable_access) { - __atomic_or_fetch(&PWR->CR1, PWR_CR1_DBP, __ATOMIC_RELAXED); - } else { - __atomic_and_fetch(&PWR->CR1, ~PWR_CR1_DBP, __ATOMIC_RELAXED); - } - periph_config_disable(PWR, RCC_APB1Periph_PWR); -} - - -bool pwr_did_boot_from_standby(void) { - bool result = (PWR->CSR1 & PWR_CSR1_SBF) != 0; - return result; -} - -void pwr_clear_boot_from_standby_flag(void) { - PWR->CR1 |= PWR_CR1_CSBF; -} diff --git a/platform/robert/boot/src/drivers/pwr.h b/platform/robert/boot/src/drivers/pwr.h deleted file mode 100644 index 22373cf618..0000000000 --- a/platform/robert/boot/src/drivers/pwr.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void pwr_access_backup_domain(bool enable_access); - -bool pwr_did_boot_from_standby(void); - -void pwr_clear_boot_from_standby_flag(void); diff --git a/platform/robert/boot/src/drivers/system_flash.c b/platform/robert/boot/src/drivers/system_flash.c deleted file mode 100644 index 4f77a986dc..0000000000 --- a/platform/robert/boot/src/drivers/system_flash.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/system_flash.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#include "stm32f7xx.h" - -static uint16_t s_sectors[] = { - FLASH_Sector_0, FLASH_Sector_1, FLASH_Sector_2, FLASH_Sector_3, - FLASH_Sector_4, FLASH_Sector_5, FLASH_Sector_6, FLASH_Sector_7, - FLASH_Sector_8, FLASH_Sector_9, FLASH_Sector_10, FLASH_Sector_11 -}; -static uint32_t s_sector_addresses[] = { - ADDR_FLASH_SECTOR_0, ADDR_FLASH_SECTOR_1, ADDR_FLASH_SECTOR_2, ADDR_FLASH_SECTOR_3, - ADDR_FLASH_SECTOR_4, ADDR_FLASH_SECTOR_5, ADDR_FLASH_SECTOR_6, ADDR_FLASH_SECTOR_7, - ADDR_FLASH_SECTOR_8, ADDR_FLASH_SECTOR_9, ADDR_FLASH_SECTOR_10, ADDR_FLASH_SECTOR_11 -}; - -static int prv_get_sector_num_for_address(uint32_t address) { - if (address < s_sector_addresses[0]) { - dbgserial_print("address "); - dbgserial_print_hex(address); - dbgserial_putstr(" is outside system flash"); - return -1; - } - for (size_t i=0; i < ARRAY_LENGTH(s_sector_addresses)-1; ++i) { - if (s_sector_addresses[i] <= address - && address < s_sector_addresses[i+1]) { - return i; - } - } - return ARRAY_LENGTH(s_sector_addresses)-1; -} - -bool system_flash_erase(uint32_t address, size_t length, SystemFlashProgressCb progress_callback, - void *progress_context) { - if (length == 0) { - // Nothing to do - return true; - } - - int first_sector = prv_get_sector_num_for_address(address); - int last_sector = prv_get_sector_num_for_address(address + length - 1); - if (first_sector < 0 || last_sector < 0) { - return false; - } - int count = last_sector - first_sector + 1; - if (progress_callback) { - progress_callback(0, count, progress_context); - } - - FLASH_Unlock(); - for (int sector = first_sector; sector <= last_sector; ++sector) { - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - if (FLASH_EraseSector(s_sectors[sector], VoltageRange_1) != FLASH_COMPLETE) { - dbgserial_print("failed to erase sector "); - dbgserial_print_hex(sector); - dbgserial_newline(); - FLASH_Lock(); - return false; - } - if (progress_callback) { - progress_callback(sector - first_sector + 1, count, progress_context); - } - } - FLASH_Lock(); - return true; -} - -bool system_flash_write(uint32_t address, const void *data, size_t length) { - // enable programming of flash - FLASH_Unlock(); - // clear errors - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - - const uint8_t *data_array = data; - for (uint32_t i = 0; i < length; ++i) { - // wait till the previous operation finished - if (FLASH_ProgramByte(address + i, data_array[i]) != FLASH_COMPLETE) { - dbgserial_print("Program failed @"); - dbgserial_print_hex(address + i); - dbgserial_newline(); - FLASH_Lock(); - return false; - } - } - - // disable programming of flash - FLASH_Lock(); - return true; -} diff --git a/platform/robert/boot/src/drivers/system_flash.h b/platform/robert/boot/src/drivers/system_flash.h deleted file mode 100644 index 4cb065f8e5..0000000000 --- a/platform/robert/boot/src/drivers/system_flash.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "stm32f7xx.h" - -#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 32 Kbytes */ -#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08008000) /* Base @ of Sector 1, 32 Kbytes */ -#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08010000) /* Base @ of Sector 2, 32 Kbytes */ -#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x08018000) /* Base @ of Sector 3, 32 Kbytes */ -#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08020000) /* Base @ of Sector 4, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08040000) /* Base @ of Sector 5, 256 Kbytes */ -#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08080000) /* Base @ of Sector 6, 256 Kbytes */ -#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x080C0000) /* Base @ of Sector 7, 256 Kbytes */ -#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08100000) /* Base @ of Sector 8, 256 Kbytes */ -#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x08140000) /* Base @ of Sector 9, 256 Kbytes */ -#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x08180000) /* Base @ of Sector 10, 256 Kbytes */ -#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x081C0000) /* Base @ of Sector 11, 256 Kbytes */ - -typedef void (*SystemFlashProgressCb)(uint32_t progress, uint32_t total, void *context); - -// Erase the sectors of flash which lie within the given address range. -// -// If the address range overlaps even one single byte of a sector, the entire -// sector is erased. -// -// If progress_callback is not NULL, it is called at the beginning of the erase -// process and after each sector is erased. The rational number (progress/total) -// increases monotonically as the sector erasue procedure progresses. -// -// Returns true if successful, false if an error occurred. -bool system_flash_erase(uint32_t address, size_t length, SystemFlashProgressCb progress_callback, - void *progress_context); - -// Write data into flash. The flash must already be erased. -// -// If progress_callback is not NULL, it is called at the beginning of the -// writing process and periodically thereafter. The rational number -// (progress/total) increases monotonically as the data is written. -// -// Returns true if successful, false if an error occurred. -bool system_flash_write(uint32_t address, const void *data, size_t length); diff --git a/platform/robert/boot/src/drivers/watchdog.c b/platform/robert/boot/src/drivers/watchdog.c deleted file mode 100644 index 5f5467ac5f..0000000000 --- a/platform/robert/boot/src/drivers/watchdog.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/watchdog.h" - -#include "stm32f7xx.h" - -void watchdog_init(void) { - IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); - - IWDG_SetPrescaler(IWDG_Prescaler_64); // ~8 seconds - IWDG_SetReload(0xfff); - - IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); - - DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP, ENABLE); -} - -void watchdog_start(void) { - IWDG_Enable(); - IWDG_ReloadCounter(); -} - -bool watchdog_check_reset_flag(void) { - return RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET; -} diff --git a/platform/robert/boot/src/drivers/watchdog.h b/platform/robert/boot/src/drivers/watchdog.h deleted file mode 100644 index d42fec728d..0000000000 --- a/platform/robert/boot/src/drivers/watchdog.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void watchdog_init(void); -void watchdog_start(void); - -bool watchdog_check_reset_flag(void); diff --git a/platform/robert/boot/src/firmware.h b/platform/robert/boot/src/firmware.h deleted file mode 100644 index 7961120f33..0000000000 --- a/platform/robert/boot/src/firmware.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "stm32f7xx.h" - -extern const uint32_t __BOOTLOADER_size__[]; - -#define BOOTLOADER_SIZE ((uint32_t) __BOOTLOADER_size__) -#define FIRMWARE_BASE (FLASH_BASE + BOOTLOADER_SIZE) diff --git a/platform/robert/boot/src/flash_region.h b/platform/robert/boot/src/flash_region.h deleted file mode 100644 index 42f9f34c0f..0000000000 --- a/platform/robert/boot/src/flash_region.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define PAGE_SIZE_BYTES (0x100) - -#define SECTOR_SIZE_BYTES (0x10000) -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define SUBSECTOR_SIZE_BYTES (0x1000) -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) - - -// A bit of preprocessor magic to help with automatically calculating flash region addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_DEF(MACRO, arg) \ - MACRO(FIRMWARE_SCRATCH, 0x200000 /* 2048k */, arg) \ - MACRO(SYSTEM_RESOURCES_BANK_0, 0x100000 /* 1024k */, arg) \ - MACRO(SYSTEM_RESOURCES_BANK_1, 0x100000 /* 1024k */, arg) \ - MACRO(SAFE_FIRMWARE, 0x080000 /* 512k */, arg) \ - MACRO(DEBUG_DB, 0x020000 /* 128k */, arg) \ - MACRO(MFG_INFO, 0x020000 /* 128k */, arg) \ - MACRO(FILESYSTEM, 0xB30000 /* 11456k */, arg) \ - MACRO(RSVD, 0x00F000 /* 60k */, arg) \ - MACRO(SHARED_PRF_STORAGE, 0x001000 /* 4k */, arg) - -#include "flash_region_def_helper.h" - - -// Flash region _BEGIN and _END addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_FIRMWARE_SCRATCH_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SCRATCH) -#define FLASH_REGION_FIRMWARE_SCRATCH_END FLASH_REGION_END_ADDR(FIRMWARE_SCRATCH) - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN FLASH_REGION_START_ADDR(SAFE_FIRMWARE) -#define FLASH_REGION_SAFE_FIRMWARE_END FLASH_REGION_END_ADDR(SAFE_FIRMWARE) - -#define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO) -#define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO) - -#define BOARD_NOR_FLASH_SIZE FLASH_REGION_START_ADDR(_COUNT) - - -// Static asserts to make sure everything worked out -////////////////////////////////////////////////////////////////////////////// - -// make sure all the sizes are multiples of the subsector size (4k) -FLASH_REGION_SIZE_CHECK(SUBSECTOR_SIZE_BYTES) - -// make sure the total size is what we expect (16MB for robert) -_Static_assert(BOARD_NOR_FLASH_SIZE == 0x1000000, "Flash size should be 16MB"); diff --git a/platform/robert/boot/src/flash_region_def_helper.h b/platform/robert/boot/src/flash_region_def_helper.h deleted file mode 100644 index 6a455c819a..0000000000 --- a/platform/robert/boot/src/flash_region_def_helper.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -enum { -#define FLASH_REGION_LIST(name, size, arg) FlashRegion_##name, - FLASH_REGION_DEF(FLASH_REGION_LIST, NULL) - FlashRegion__COUNT -}; - -#define FLASH_REGION_ADDR_HELPER(name, size, tgt) \ - + (FlashRegion_##name < (tgt) ? (size) : 0) - -// These macros add up all the sizes of the flash regions that come before (and including in the -// case of the _END_ADDR macro) the specified one to determine the proper flash address value. -#define FLASH_REGION_START_ADDR(region) \ - ((0) FLASH_REGION_DEF(FLASH_REGION_ADDR_HELPER, FlashRegion_##region)) -#define FLASH_REGION_END_ADDR(region) \ - ((0) FLASH_REGION_DEF(FLASH_REGION_ADDR_HELPER, FlashRegion_##region + 1)) - -// Checks that all regions are a multiple of the specified size (usually sector or subsector size) -#define FLASH_REGION_SIZE_CHECK_HELPER(name, size, arg) \ - && ((size) % (arg) == 0) -#define FLASH_REGION_SIZE_CHECK(size) \ - _Static_assert((1) FLASH_REGION_DEF(FLASH_REGION_SIZE_CHECK_HELPER, size), "Invalid region size"); diff --git a/platform/robert/boot/src/fw_copy.c b/platform/robert/boot/src/fw_copy.c deleted file mode 100644 index adeb0c6745..0000000000 --- a/platform/robert/boot/src/fw_copy.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fw_copy.h" - -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/system_flash.h" -#include "firmware.h" -#include "flash_region.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/reset.h" -#include "util/crc32.h" -#include "util/misc.h" -#include "util/delay.h" - -#include -#include - -#define MAX_CHUNK_SIZE 65536 - -static bool prv_check_valid_firmware_crc(uint32_t flash_address, FirmwareDescription* desc) { - dbgserial_putstr("Checksumming firmware update"); - const uint32_t crc = flash_calculate_checksum(flash_address, desc->firmware_length); - dbgserial_print("Calculated checksum: "); - dbgserial_print_hex(crc); - dbgserial_newline(); - return crc == desc->checksum; -} - -static void prv_display_erase_progress(uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress, total * 2); -} - -static bool prv_erase_old_firmware(uint32_t firmware_length) { - dbgserial_putstr("prv_erase_old_firmware"); - return system_flash_erase(FIRMWARE_BASE, firmware_length, prv_display_erase_progress, 0); -} - -static void prv_display_write_progress(uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress/2 + total/2, total); -} - -static bool prv_write_new_firmware(uint32_t flash_new_fw_start, uint32_t firmware_length) { - dbgserial_putstr("prv_write_new_firmware"); - // We can't just read the flash like memory, so we gotta lift everything ourselves. - // buffer is static so it goes in BSS, since stack is only 8192 bytes - static uint8_t buffer[MAX_CHUNK_SIZE]; - uint32_t chunk_size; - for (uint32_t i = 0; i < firmware_length; i += chunk_size) { - chunk_size = MIN(MAX_CHUNK_SIZE, firmware_length - i); - flash_read_bytes(buffer, flash_new_fw_start + i, chunk_size); - if (!system_flash_write(FIRMWARE_BASE + i, buffer, chunk_size)) { - dbgserial_putstr("We're dead"); - return false; - } - prv_display_write_progress(i, firmware_length, NULL); - } - return true; -} - -static bool prv_check_firmware_crc(FirmwareDescription* firmware_description) { - dbgserial_print("Checksumming "); - dbgserial_print_hex(firmware_description->firmware_length); - dbgserial_print(" bytes\r\n"); - - const uint32_t crc = crc32(CRC32_INIT, (const uint8_t *)FIRMWARE_BASE, - firmware_description->firmware_length); - - dbgserial_print("Checksum - wanted "); - dbgserial_print_hex(firmware_description->checksum); - dbgserial_print(" got "); - dbgserial_print_hex(crc); - dbgserial_newline(); - - return crc == firmware_description->checksum; -} - -typedef enum UpdateFirmwareResult { - UPDATE_FW_SUCCESS = 0, - UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED = 1, - UPDATE_FW_ERROR_MICRO_FLASH_MANGLED = 2 -} UpdateFirmwareResult; - -static UpdateFirmwareResult prv_update_fw(uint32_t flash_address) { - display_firmware_update_progress(0, 1); - boot_bit_set(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - - FirmwareDescription firmware_description = - firmware_storage_read_firmware_description(flash_address); - - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - dbgserial_putstr("Invalid firmware description!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - if (!prv_check_valid_firmware_crc(flash_address + sizeof(FirmwareDescription), - &firmware_description)) { - dbgserial_putstr("Invalid firmware CRC in SPI flash!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - prv_erase_old_firmware(firmware_description.firmware_length); - - prv_write_new_firmware(flash_address + sizeof(FirmwareDescription), - firmware_description.firmware_length); - - if (!prv_check_firmware_crc(&firmware_description)) { - dbgserial_putstr( - "Our internal flash contents are bad (checksum failed)! " - "This is really bad!"); - return UPDATE_FW_ERROR_MICRO_FLASH_MANGLED; - } - - return UPDATE_FW_SUCCESS; -} - -void fw_copy_check_update_fw(void) { - if (!boot_bit_test(BOOT_BIT_NEW_FW_AVAILABLE)) { - return; - } - - if (boot_bit_test(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS)) { - dbgserial_putstr("Our previous firmware update failed, aborting update."); - - // Pretend like the new firmware bit wasn't set afterall. We'll just run the - // previous code, whether that was normal firmware or the recovery firmware. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_clear(BOOT_BIT_NEW_FW_INSTALLED); - return; - } - - - dbgserial_putstr("New firmware is available!"); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - - UpdateFirmwareResult result = prv_update_fw(FLASH_REGION_FIRMWARE_SCRATCH_BEGIN); - switch (result) { - case UPDATE_FW_SUCCESS: - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - // Our firmware update failed in a way that didn't break our previous - // firmware. Just run the previous code, whether that was normal firmware - // or the recovery firmware. - break; - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // We've broken our internal flash when trying to update our normal - // firmware. Fall back immediately to the recovery firmare. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - system_reset(); - return; - } - - // Done, we're ready to boot. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_set(BOOT_BIT_NEW_FW_INSTALLED); -} - -bool fw_copy_switch_to_recovery_fw() { - dbgserial_putstr("Loading recovery firmware"); - - UpdateFirmwareResult result = prv_update_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - bool recovery_fw_ok = true; - switch (result) { - case UPDATE_FW_SUCCESS: - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // Keep us booting into recovery firmware. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - - if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to load recovery firmware, strike one. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO)) { - dbgserial_putstr("Failed to load recovery firmware, strike two. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else { - dbgserial_putstr("Failed to load recovery firmware, strike three. SAD WATCH"); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - recovery_fw_ok = false; - } - break; - } - - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - return recovery_fw_ok; -} diff --git a/platform/robert/boot/src/fw_copy.h b/platform/robert/boot/src/fw_copy.h deleted file mode 100644 index 2a67a7bcc8..0000000000 --- a/platform/robert/boot/src/fw_copy.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void fw_copy_check_update_fw(void); - -//! @return false if we've failed to load recovery mode too many times and we should just sadwatch -bool fw_copy_switch_to_recovery_fw(void); diff --git a/platform/robert/boot/src/git_version.auto.h.in b/platform/robert/boot/src/git_version.auto.h.in deleted file mode 100644 index f5a14b1a0a..0000000000 --- a/platform/robert/boot/src/git_version.auto.h.in +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#define GIT_TIMESTAMP @TIMESTAMP@ -#define GIT_TAG "@TAG@" -#define GIT_MAJOR_VERSION @MAJOR_VERSION@ -#define GIT_MINOR_VERSION @MINOR_VERSION@ -#define GIT_PATCH_VERSION @PATCH_VERSION@ -#define GIT_REVISION "@COMMIT@" diff --git a/platform/robert/boot/src/hardfault_handler.c b/platform/robert/boot/src/hardfault_handler.c deleted file mode 100644 index 28b76be3d9..0000000000 --- a/platform/robert/boot/src/hardfault_handler.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/die.h" -#include "system/reset.h" - -static void prv_hard_fault_handler_c(unsigned int *hardfault_args) { - dbgserial_putstr("HARD FAULT"); - -#ifdef NO_WATCHDOG - reset_due_to_software_failure(); -#else - system_hard_reset(); -#endif -} - -void HardFault_Handler(void) { - // Grab the stack pointer, shove it into a register and call - // the c function above. - __asm("tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "b %0\n" :: "i" (prv_hard_fault_handler_c)); -} diff --git a/platform/robert/boot/src/irq_stm32f7.def b/platform/robert/boot/src/irq_stm32f7.def deleted file mode 100644 index 4ba0dc703e..0000000000 --- a/platform/robert/boot/src/irq_stm32f7.def +++ /dev/null @@ -1,109 +0,0 @@ -IRQ_DEF(0, WWDG) // Window WatchDog -IRQ_DEF(1, PVD) // PVD through EXTI Line detection -IRQ_DEF(2, TAMP_STAMP) // Tamper and TimeStamps through the EXTI line -IRQ_DEF(3, RTC_WKUP) // RTC Wakeup through the EXTI line -IRQ_DEF(4, FLASH) // FLASH -IRQ_DEF(5, RCC) // RCC -IRQ_DEF(6, EXTI0) // EXTI Line0 -IRQ_DEF(7, EXTI1) // EXTI Line1 -IRQ_DEF(8, EXTI2) // EXTI Line2 -IRQ_DEF(9, EXTI3) // EXTI Line3 -IRQ_DEF(10, EXTI4) // EXTI Line4 -IRQ_DEF(11, DMA1_Stream0) // DMA1 Stream 0 -IRQ_DEF(12, DMA1_Stream1) // DMA1 Stream 1 -IRQ_DEF(13, DMA1_Stream2) // DMA1 Stream 2 -IRQ_DEF(14, DMA1_Stream3) // DMA1 Stream 3 -IRQ_DEF(15, DMA1_Stream4) // DMA1 Stream 4 -IRQ_DEF(16, DMA1_Stream5) // DMA1 Stream 5 -IRQ_DEF(17, DMA1_Stream6) // DMA1 Stream 6 -IRQ_DEF(18, ADC) // ADC1, ADC2 and ADC3s -IRQ_DEF(19, CAN1_TX) // CAN1 TX -IRQ_DEF(20, CAN1_RX0) // CAN1 RX0 -IRQ_DEF(21, CAN1_RX1) // CAN1 RX1 -IRQ_DEF(22, CAN1_SCE) // CAN1 SCE -IRQ_DEF(23, EXTI9_5) // External Line[9:5]s -IRQ_DEF(24, TIM1_BRK_TIM9) // TIM1 Break and TIM9 -IRQ_DEF(25, TIM1_UP_TIM10) // TIM1 Update and TIM10 -IRQ_DEF(26, TIM1_TRG_COM_TIM11) // TIM1 Trigger and Commutation and TIM11 -IRQ_DEF(27, TIM1_CC) // TIM1 Capture Compare -IRQ_DEF(28, TIM2) // TIM2 -IRQ_DEF(29, TIM3) // TIM3 -IRQ_DEF(30, TIM4) // TIM4 -IRQ_DEF(31, I2C1_EV) // I2C1 Event -IRQ_DEF(32, I2C1_ER) // I2C1 Error -IRQ_DEF(33, I2C2_EV) // I2C2 Event -IRQ_DEF(34, I2C2_ER) // I2C2 Error -IRQ_DEF(35, SPI1) // SPI1 -IRQ_DEF(36, SPI2) // SPI2 -IRQ_DEF(37, USART1) // USART1 -IRQ_DEF(38, USART2) // USART2 -IRQ_DEF(39, USART3) // USART3 -IRQ_DEF(40, EXTI15_10) // External Line[15:10]s -IRQ_DEF(41, RTC_Alarm) // RTC Alarm (A and B) through EXTI Line -IRQ_DEF(42, OTG_FS_WKUP) // USB OTG FS Wakeup through EXTI line -IRQ_DEF(43, TIM8_BRK_TIM12) // TIM8 Break and TIM12 -IRQ_DEF(44, TIM8_UP_TIM13) // TIM8 Update and TIM13 -IRQ_DEF(45, TIM8_TRG_COM_TIM14) // TIM8 Trigger and Commutation and TIM14 -IRQ_DEF(46, TIM8_CC) // TIM8 Capture Compare -IRQ_DEF(47, DMA1_Stream7) // DMA1 Stream7 -IRQ_DEF(48, FMC) // FMC -IRQ_DEF(49, SDMMC1) // SDMMC1 -IRQ_DEF(50, TIM5) // TIM5 -IRQ_DEF(51, SPI3) // SPI3 -IRQ_DEF(52, UART4) // UART4 -IRQ_DEF(53, UART5) // UART5 -IRQ_DEF(54, TIM6_DAC) // TIM6 and DAC1&2 underrun errors -IRQ_DEF(55, TIM7) // TIM7 -IRQ_DEF(56, DMA2_Stream0) // DMA2 Stream 0 -IRQ_DEF(57, DMA2_Stream1) // DMA2 Stream 1 -IRQ_DEF(58, DMA2_Stream2) // DMA2 Stream 2 -IRQ_DEF(59, DMA2_Stream3) // DMA2 Stream 3 -IRQ_DEF(60, DMA2_Stream4) // DMA2 Stream 4 -IRQ_DEF(61, ETH) // Ethernet -IRQ_DEF(62, ETH_WKUP) // Ethernet Wakeup through EXTI line -IRQ_DEF(63, CAN2_TX) // CAN2 TX -IRQ_DEF(64, CAN2_RX0) // CAN2 RX0 -IRQ_DEF(65, CAN2_RX1) // CAN2 RX1 -IRQ_DEF(66, CAN2_SCE) // CAN2 SCE -IRQ_DEF(67, OTG_FS) // USB OTG FS -IRQ_DEF(68, DMA2_Stream5) // DMA2 Stream 5 -IRQ_DEF(69, DMA2_Stream6) // DMA2 Stream 6 -IRQ_DEF(70, DMA2_Stream7) // DMA2 Stream 7 -IRQ_DEF(71, USART6) // USART6 -IRQ_DEF(72, I2C3_EV) // I2C3 event -IRQ_DEF(73, I2C3_ER) // I2C3 error -IRQ_DEF(74, OTG_HS_EP1_OUT) // USB OTG HS End Point 1 Out -IRQ_DEF(75, OTG_HS_EP1_IN) // USB OTG HS End Point 1 In -IRQ_DEF(76, OTG_HS_WKUP) // USB OTG HS Wakeup through EXTI -IRQ_DEF(77, OTG_HS) // USB OTG HS -IRQ_DEF(78, DCMI) // DCMI -IRQ_DEF(79, CRYP) // CRYP crypto -IRQ_DEF(80, HASH_RNG) // Hash and Rng -IRQ_DEF(81, FPU) // FPU -IRQ_DEF(82, UART7) // UART7 -IRQ_DEF(83, UART8) // UART8 -IRQ_DEF(84, SPI4) // SPI4 -IRQ_DEF(85, SPI5) // SPI5 -IRQ_DEF(86, SPI6) // SPI6 -IRQ_DEF(87, SAI1) // SAI1 -IRQ_DEF(88, LTDC) // LTDC -IRQ_DEF(89, LTDC_ER) // LTDC_ER -IRQ_DEF(90, DMA2D) // DMA2D -IRQ_DEF(91, SAI2) // SAI2 -IRQ_DEF(92, QUADSPI) // Quad SPI -IRQ_DEF(93, LPTIM1) // LP TIM1 -IRQ_DEF(94, CEC) // HDMI-CEC -IRQ_DEF(95, I2C4_EV) // I2C4 Event -IRQ_DEF(96, I2C4_ER) // I2C4 Error -IRQ_DEF(97, SPDIF_RX) // SPDIF-RX -IRQ_DEF(99, DFSDM0) // DFSDM Filter1 -IRQ_DEF(100, DFSDM1) // DFSDM Filter2 -IRQ_DEF(101, DFSDM2) // DFSDM Filter3 -IRQ_DEF(102, DFSDM3) // DFSDM Filter4 -IRQ_DEF(103, SDMMC2) // SDMMC2 -IRQ_DEF(104, CAN3_TX) // CAN3 TX -IRQ_DEF(105, CAN3_RX0) // CAN3 RX0 -IRQ_DEF(106, CAN3_RX1) // CAN3 RX1 -IRQ_DEF(107, CAN3_SCE) // CAN3 SCE -IRQ_DEF(108, JPEG) // JPEG -IRQ_DEF(109, MDIOS) // MDIO Slave diff --git a/platform/robert/boot/src/main.c b/platform/robert/boot/src/main.c deleted file mode 100644 index 119373d735..0000000000 --- a/platform/robert/boot/src/main.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "board/board.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "drivers/pwr.h" -#include "drivers/watchdog.h" -#include "boot_tests.h" -#include "firmware.h" -#include "fw_copy.h" -#include "pebble_errors.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" -#include "util/delay.h" -#include "util/misc.h" - -#include "stm32f7xx.h" - -static void prv_get_fw_reset_vector(void **reset_handler, - void **initial_stack_pointer) { - void** fw_vector_table = (void**) FIRMWARE_BASE; - - *initial_stack_pointer = fw_vector_table[0]; - *reset_handler = fw_vector_table[1]; -} - -static void prv_hw_reset(void) { - // Disable all interrupts, just in case. - for (int i = 0; i < 8; ++i) { - // Interrupt Clear-Enable Register - NVIC->ICER[i] = 0xFFFFFFFF; - // Interrupt Clear-Pending Register - NVIC->ICPR[i] = 0xFFFFFFFF; - } - - // Set the peripheral clock enable registers to their reset values as - // specified in the reference manual. - RCC->AHB1ENR = 0x00100000; - RCC->AHB2ENR = 0; - RCC->AHB3ENR = 0; - RCC->APB1ENR = 0x00000400; // Reserved bit needs to be set to enable RTC! - RCC->APB2ENR = 0; - - // Reset most peripherals used by the bootloader. We want to minimize the - // chances that the firmware unintentionally relies on some state that the - // bootloader leaves behind. This includes disabling the PLL. - // GPIOs are not reset here: resetting them would change their output values, - // which could unintentionally turn of e.g. PMIC power rails. - // The backup domain is not reset; that would be foolish. - const uint32_t ahb1_periphs = - RCC_AHB1Periph_CRC | RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_DMA2 - | RCC_AHB1Periph_DMA2D | RCC_AHB1Periph_ETHMAC | RCC_AHB1Periph_OTGHS; - const uint32_t ahb2_periphs = - RCC_AHB2Periph_DCMI | RCC_AHB2Periph_JPEG | RCC_AHB2Periph_CRYP | RCC_AHB2Periph_HASH - | RCC_AHB2Periph_RNG | RCC_AHB2Periph_OTGFS; - const uint32_t ahb3_periphs = RCC_AHB3Periph_FMC | RCC_AHB3Periph_QSPI; - const uint32_t apb1_periphs = - RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4| RCC_APB1Periph_TIM5 - | RCC_APB1Periph_TIM6 | RCC_APB1Periph_TIM7 | RCC_APB1Periph_TIM12 | RCC_APB1Periph_TIM13 - | RCC_APB1Periph_TIM14 | RCC_APB1Periph_LPTIM1 | RCC_APB1Periph_WWDG | RCC_APB1Periph_CAN3 - | RCC_APB1Periph_SPI2 | RCC_APB1Periph_SPI3 | RCC_APB1Periph_SPDIFRX | RCC_APB1Periph_USART2 - | RCC_APB1Periph_USART3 | RCC_APB1Periph_UART4 | RCC_APB1Periph_UART5 | RCC_APB1Periph_I2C1 - | RCC_APB1Periph_I2C2 | RCC_APB1Periph_I2C3 | RCC_APB1Periph_I2C4 | RCC_APB1Periph_CAN1 - | RCC_APB1Periph_CAN2 | RCC_APB1Periph_CEC | RCC_APB1Periph_PWR | RCC_APB1Periph_DAC - | RCC_APB1Periph_UART7 | RCC_APB1Periph_UART8; - const uint32_t apb2_periphs = - RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART1 - | RCC_APB2Periph_USART6 | RCC_APB2Periph_SDMMC2 | RCC_APB2Periph_ADC | RCC_APB2Periph_SDMMC1 - | RCC_APB2Periph_SPI1 | RCC_APB2Periph_SPI4 | RCC_APB2Periph_SYSCFG - | RCC_APB2Periph_TIM9 | RCC_APB2Periph_TIM10 | RCC_APB2Periph_TIM11 - | RCC_APB2Periph_SPI5 | RCC_APB2Periph_SPI6 | RCC_APB2Periph_SAI1 | RCC_APB2Periph_SAI2 - | RCC_APB2Periph_DFSDM | RCC_APB2Periph_MDIO | RCC_APB2Periph_LTDC; - RCC_DeInit(); - RCC_AHB1PeriphResetCmd(ahb1_periphs, ENABLE); - RCC_AHB1PeriphResetCmd(ahb1_periphs, DISABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, ENABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, DISABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, ENABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, DISABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, ENABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, DISABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, ENABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, DISABLE); -} - -static void __attribute__((noreturn)) prv_jump_to_fw(void) { - void *initial_stack_pointer, *reset_handler; - prv_get_fw_reset_vector(&reset_handler, &initial_stack_pointer); - - dbgserial_print("Booting firmware @ "); - dbgserial_print_hex((uintptr_t)reset_handler); - dbgserial_print("...\r\n\r\n"); - - prv_hw_reset(); - - // The Cortex-M user guide states that the reset values for the core registers - // are as follows: - // R0-R12 = Unknown - // MSP = VECTOR_TABLE[0] (main stack pointer) - // PSP = Unknown (process stack pointer) - // LR = 0xFFFFFFFF - // PC = VECTOR_TABLE[1] - // PRIMASK = 0x0 - // FAULTMASK = 0x0 - // BASEPRI = 0x0 - // CONTROL = 0x0 - // - // Attempt to put the processor into as close to the reset state as possible - // before passing control to the firmware. - // - // No attempt is made to set CONTROL to zero as it should already be set to - // the reset value when this code executes. - __asm volatile ( - "cpsie if\n" // Clear PRIMASK and FAULTMASK - "mov lr, 0xFFFFFFFF\n" - "mov sp, %[initial_sp]\n" - "bx %[reset_handler]\n" - : : [initial_sp] "r" (initial_stack_pointer), - [reset_handler] "r" (reset_handler) - ); - __builtin_unreachable(); -} - -static bool prv_check_and_increment_reset_loop_detection_bits(void) { - uint8_t counter = - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_THREE) << 2) | - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_TWO) << 1) | - boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_ONE); - - if (counter == 7) { - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE); - return true; - } - - switch (++counter) { - case 1: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 2: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 3: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 4: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_THREE); - break; - case 5: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 6: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 7: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - default: - PBL_CROAK("reset loop boot bits overrun"); - break; - } - return false; -} - -static bool prv_check_for_recovery_start_failure() { - return boot_bit_test(BOOT_BIT_RECOVERY_START_IN_PROGRESS); -} - -static bool prv_check_for_fw_start_failure() { - // Add more failure conditions here. - if (!watchdog_check_reset_flag() && !boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - // We're good, we're just starting normally. - PBL_LOG_VERBOSE("We're good, we're just starting normally."); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return false; - } - - // We failed to start our firmware successfully! - if (watchdog_check_reset_flag()) { - dbgserial_putstr("Watchdog caused a reset"); - } - if (boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - dbgserial_putstr("Software failure caused a reset"); - } - - // Clean up after the last failure. - boot_bit_clear(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - - // We have a "three strikes" algorithm: if the watch fails three times, return true - // to tell the parent we should load the recovery firmware. A reset for any other reason - // will reset this algorithm. - if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) { - // Yikes, our firmware is screwed. Boot into recovery mode. - dbgserial_putstr("Failed to start firmware, strike three."); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return true; - } else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to start firmware, strike two."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - } else { - dbgserial_putstr("Failed to start firmware, strike one."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - } - - return false; -} - -static bool prv_prf_button_combination_is_pressed(void) { - return (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK) - && button_is_pressed(BUTTON_ID_SELECT) && !button_is_pressed(BUTTON_ID_DOWN)); -} - -static bool prv_check_force_boot_recovery(void) { - if (boot_bit_test(BOOT_BIT_FORCE_PRF)) { - boot_bit_clear(BOOT_BIT_FORCE_PRF); - return true; - } - - if (prv_prf_button_combination_is_pressed()) { - dbgserial_putstr("Hold down UP + BACK + SELECT for 5 secs. to force-boot PRF"); - for (int i = 0; i < 5000; ++i) { - if (!prv_prf_button_combination_is_pressed()) { - // stop waiting if not held down any longer - return false; - } - delay_ms(1); - } - - return true; - } - - void *reset_vector, *initial_sp; - prv_get_fw_reset_vector(&reset_vector, &initial_sp); - if ((uintptr_t)reset_vector == 0xffffffff || - (uintptr_t)initial_sp == 0xffffffff) { - dbgserial_putstr("Firmware is erased"); - return true; - } - return false; -} - -static void prv_sad_watch(uint32_t error_code) { - dbgserial_putstr("SAD WATCH"); - - char error_code_buffer[12]; - itoa_hex(error_code, error_code_buffer, sizeof(error_code_buffer)); - dbgserial_putstr(error_code_buffer); - - display_error_code(error_code); - - bool prev_select_state = button_is_pressed(BUTTON_ID_SELECT); - while (1) { - // See if we should restart - bool select_state = button_is_pressed(BUTTON_ID_SELECT); - if (select_state != prev_select_state) { - system_reset(); - } - delay_ms(10); - } -} - -static void prv_check_and_handle_resuming_from_standby(void) { - periph_config_enable(PWR, RCC_APB1Periph_PWR); - if (pwr_did_boot_from_standby()) { - // We just woke up from standby. For some reason this leaves the system in a funny state, - // so clear the flag and reboot again to really clear things up. - - pwr_clear_boot_from_standby_flag(); - dbgserial_putstr("exit standby"); - system_hard_reset(); - } - periph_config_disable(PWR, RCC_APB1Periph_PWR); -} - -static void prv_print_bootloader_version(void) { - char bootloader_version_str[12]; - memset(bootloader_version_str, 0, 12); - itoa_hex(boot_version_read(), bootloader_version_str, 12); - dbgserial_putstr(bootloader_version_str); - dbgserial_putstr(""); - dbgserial_putstr(""); -} - -int main(void) { - prv_check_and_handle_resuming_from_standby(); - - board_init(); - - dbgserial_init(); - - dbgserial_putstr("\r\n\r\n\r\n"); - dbgserial_putstr("██████╗ ██████╗ ██████╗ ███████╗██████╗ ████████╗"); - dbgserial_putstr("██╔══██╗██╔═══██╗██╔══██╗██╔════╝██╔══██╗╚══██╔══╝"); - dbgserial_putstr("██████╔╝██║ ██║██████╔╝█████╗ ██████╔╝ ██║ "); - dbgserial_putstr("██╔══██╗██║ ██║██╔══██╗██╔══╝ ██╔══██╗ ██║ "); - dbgserial_putstr("██║ ██║╚██████╔╝██████╔╝███████╗██║ ██║ ██║ "); - dbgserial_putstr("╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ "); - - // Enable the 3.2V rail for the benefit of the FPGA and display - pmic_init(); - - boot_bit_init(); - - boot_version_write(); - - prv_print_bootloader_version(); - - if (boot_bit_test(BOOT_BIT_FW_STABLE)) { - dbgserial_putstr("Last firmware boot was stable; clear strikes"); - - boot_bit_clear(BOOT_BIT_FW_STABLE); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - } - - flash_init(); - button_init(); - pmic_init(); - display_init(); - - display_boot_splash(); - - if (boot_test_is_button_stuck()) { - prv_sad_watch(ERROR_STUCK_BUTTON); - } - - if (boot_test_is_flash_broken()) { - prv_sad_watch(ERROR_BAD_SPI_FLASH); - } - - boot_bit_dump(); - - // If the recovery firmware crashed at start-up, the watch is now a $199 brick. That's life! - if (prv_check_for_recovery_start_failure()) { - boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - prv_sad_watch(ERROR_CANT_LOAD_FW); - } - - bool force_boot_recovery_mode = prv_check_force_boot_recovery(); - if (force_boot_recovery_mode) { - dbgserial_putstr("Force-booting recovery mode..."); - } - - if (force_boot_recovery_mode || prv_check_for_fw_start_failure()) { - if (!fw_copy_switch_to_recovery_fw()) { - // We've failed to load recovery mode too many times. - prv_sad_watch(ERROR_CANT_LOAD_FW); - } - } else { - fw_copy_check_update_fw(); - } - - if (prv_check_and_increment_reset_loop_detection_bits()) { - prv_sad_watch(ERROR_RESET_LOOP); - } - -#if !NO_WATCHDOG - dbgserial_putstr("Enabling watchdog"); - watchdog_init(); - watchdog_start(); -#endif - - gpio_disable_all(); - - prv_jump_to_fw(); -} - -// Stubs for libg_s.a, which is our libc implementation from nano-newlib -void _exit(int status) { -} diff --git a/platform/robert/boot/src/pebble_errors.h b/platform/robert/boot/src/pebble_errors.h deleted file mode 100644 index 1278bf459b..0000000000 --- a/platform/robert/boot/src/pebble_errors.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -static const uint32_t ERROR_NO_ACTIVE_ERROR = 0; - -// FW Errors -static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501; -static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502; -static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503; -static const uint32_t ERROR_RESET_LOOP = 0xfe504504; - -// BT Errors -static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510; -static const uint32_t ERROR_CANT_START_LSE = 0xfe504511; -static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512; - -static const uint32_t ERROR_LOW_BATTERY = 0xfe504520; diff --git a/platform/robert/boot/src/stm32f_flash_boot.ld b/platform/robert/boot/src/stm32f_flash_boot.ld deleted file mode 100644 index 3219c14167..0000000000 --- a/platform/robert/boot/src/stm32f_flash_boot.ld +++ /dev/null @@ -1,147 +0,0 @@ -__Stack_Size = 8192; -PROVIDE ( _Stack_Size = __Stack_Size ) ; - -__Stack_Init = _estack - __Stack_Size ; -PROVIDE ( _Stack_Init = __Stack_Init ) ; - -/* -There will be a link error if there is not this amount of RAM free at the end. -*/ -_Minimum_Stack_Size = 0x100 ; - -MEMORY -{ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K -} - -__BOOTLOADER_size__ = LENGTH(FLASH); - -__end_heap = ORIGIN(RAM) + LENGTH(RAM); -PROVIDE(_heap_end = __end_heap); - -SECTIONS -{ - /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* for some STRx devices, the beginning of the startup code is stored in the .flashtext section, which goes to FLASH */ - .flashtext : - { - . = ALIGN(4); - *(.flashtext) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* Exception handling sections. "contains index entries for section unwinding" */ - .ARM.exidx : - { - . = ALIGN(4); - *(.ARM.exidx) - . = ALIGN(4); - } >FLASH - - /* the program code is stored in the .text section, which goes to Flash */ - .text : - { - . = ALIGN(4); - - *(.text) /* remaining code */ - *(.text.*) /* remaining code */ - *(.rodata) /* read-only data (constants) */ - *(.rodata*) - *(.constdata) /* read-only data (constants) */ - *(.constdata*) - *(.glue_7) - *(.glue_7t) - *(i.*) - - . = ALIGN(4); - } >FLASH - - /* This is the initialized data section - The program executes knowing that the data is in the RAM - but the loader puts the initial values in the FLASH (inidata). - It is one task of the startup to copy the initial values from FLASH to RAM. */ - .data : { - . = ALIGN(4); - /* This is used by the startup in order to initialize the .data secion */ - __data_start = .; - - *(.data) - *(.data.*) - - . = ALIGN(4); - __data_end = .; /* This is used by the startup in order to initialize the .data secion */ - } >RAM AT>FLASH - __data_load_start = LOADADDR(.data); - - /* This is the uninitialized data section */ - .bss (NOLOAD) : { - . = ALIGN(4); - __bss_start = .; /* This is used by the startup in order to initialize the .bss secion */ - - *(.bss) - *(.bss.*) - *(COMMON) - - . = ALIGN(4); - __bss_end = .; /* This is used by the startup in order to initialize the .bss secion */ - } >RAM - - .stack (NOLOAD) : { - . = ALIGN(8); - _sstack = .; - . = . + __Stack_Size; - . = ALIGN(8); - _estack = .; - } >RAM - - /* after that it's only debugging information. */ - - /* remove the debugging information from the standard libraries */ - DISCARD : { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} diff --git a/platform/robert/boot/src/system/bootbits.c b/platform/robert/boot/src/system/bootbits.c deleted file mode 100644 index 34c8e526c1..0000000000 --- a/platform/robert/boot/src/system/bootbits.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "drivers/pwr.h" -#include "system/bootbits.h" -#include "system/rtc_registers.h" - -#include "git_version.auto.h" - -#include "stm32f7xx.h" - -#include -#include - -static const uint32_t s_bootloader_timestamp = GIT_TIMESTAMP; - -void boot_bit_init(void) { - pwr_access_backup_domain(true); - - if (!boot_bit_test(BOOT_BIT_INITIALIZED)) { - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED); - } -} - -void boot_bit_set(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value |= bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -void boot_bit_clear(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value &= ~bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -bool boot_bit_test(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - return (current_value & bit); -} - -void boot_bit_dump(void) { - dbgserial_print("Boot bits: "); - dbgserial_print_hex(RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR)); - dbgserial_newline(); -} - -void boot_version_write(void) { - if (boot_version_read() == s_bootloader_timestamp) { - return; - } - RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, s_bootloader_timestamp); -} - -uint32_t boot_version_read(void) { - return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER); -} diff --git a/platform/robert/boot/src/system/bootbits.h b/platform/robert/boot/src/system/bootbits.h deleted file mode 100644 index a2612651f0..0000000000 --- a/platform/robert/boot/src/system/bootbits.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum BootBitValue { - BOOT_BIT_INITIALIZED = 0x1 << 0, - BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1, - BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2, - BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3, - BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6, - BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7, - BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, //!< Bootloader enter standby immediately after reset. - BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9, - BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10, - BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11, - BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12, - BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13, - BOOT_BIT_FW_STABLE = 0x1 << 14, - BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15, - BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16, - BOOT_BIT_FORCE_PRF = 0x1 << 17, - BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18, - BOOT_BIT_SHUTDOWN_REQUESTED = 0x1 << 19, //!< Bootloader hard power-off instead of jumping to fw. -} BootBitValue; - -void boot_bit_init(); -void boot_bit_set(BootBitValue bit); -void boot_bit_clear(BootBitValue bit); -bool boot_bit_test(BootBitValue bit); - -// Dump the contents through dbgserial -void boot_bit_dump(void); - -void boot_version_write(void); -uint32_t boot_version_read(void); diff --git a/platform/robert/boot/src/system/die.c b/platform/robert/boot/src/system/die.c deleted file mode 100644 index 344797aae1..0000000000 --- a/platform/robert/boot/src/system/die.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "die.h" -#include "drivers/dbgserial.h" -#include "system/reset.h" -#include "system/passert.h" - -NORETURN reset_due_to_software_failure(void) { -#if defined(NO_WATCHDOG) - // Don't reset right away, leave it in a state we can inspect - - while (1) { - BREAKPOINT; - } -#endif - - dbgserial_putstr("Software failure; resetting!"); - system_reset(); -} diff --git a/platform/robert/boot/src/system/die.h b/platform/robert/boot/src/system/die.h deleted file mode 100644 index 3bb19e99cf..0000000000 --- a/platform/robert/boot/src/system/die.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" - -//! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were -//! able shut everything down nicely before rebooting. -NORETURN reset_due_to_software_failure(void); diff --git a/platform/robert/boot/src/system/firmware_storage.c b/platform/robert/boot/src/system/firmware_storage.c deleted file mode 100644 index 586ebf1649..0000000000 --- a/platform/robert/boot/src/system/firmware_storage.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "firmware_storage.h" - -#include "drivers/flash.h" - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { - FirmwareDescription firmware_description; - flash_read_bytes((uint8_t*)&firmware_description, firmware_start_address, - sizeof(FirmwareDescription)); - return firmware_description; -} - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc) { - return desc->description_length == sizeof(FirmwareDescription); -} diff --git a/platform/robert/boot/src/system/firmware_storage.h b/platform/robert/boot/src/system/firmware_storage.h deleted file mode 100644 index e05c44b208..0000000000 --- a/platform/robert/boot/src/system/firmware_storage.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file firmware_storage.h -//! Utilities for reading a firmware image stored in flash. - -#include -#include - -typedef struct __attribute__((__packed__)) FirmwareDescription { - uint32_t description_length; - uint32_t firmware_length; - uint32_t checksum; -} FirmwareDescription; - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address); - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc); diff --git a/platform/robert/boot/src/system/logging.h b/platform/robert/boot/src/system/logging.h deleted file mode 100644 index 16ae00b884..0000000000 --- a/platform/robert/boot/src/system/logging.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/dbgserial.h" - -#include -#include -#include -#include -#include - -#ifndef __FILE_NAME__ -#define __FILE_NAME__ __FILE__ -#endif - -#define LOG_LEVEL_ALWAYS 0 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_WARNING 50 -#define LOG_LEVEL_INFO 100 -#define LOG_LEVEL_DEBUG 200 -#define LOG_LEVEL_DEBUG_VERBOSE 255 - -#ifndef STRINGIFY - #define STRINGIFY_NX(a) #a - #define STRINGIFY(a) STRINGIFY_NX(a) -#endif // STRINGIFY - -#define STATUS_STRING(s) STRINGIFY(s) - -#ifdef PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) \ - do { \ - char _pbl_log_buffer[128]; \ - dbgserial_putstr(__FILE_NAME__ ":" STRINGIFY(__LINE__) "> " fmt); \ - } while (0) - - #ifdef VERBOSE_LOGGING - - #define PBL_LOG_VERBOSE(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) - - #else // VERBOSE_LOGGING - #define PBL_LOG_VERBOSE(fmt, args...) - #endif // VERBOSE_LOGGING - -#else // PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) - #define PBL_LOG_VERBOSE(fmt, args...) -#endif // PBL_LOG_ENABLED diff --git a/platform/robert/boot/src/system/passert.c b/platform/robert/boot/src/system/passert.c deleted file mode 100644 index 4dea6e6995..0000000000 --- a/platform/robert/boot/src/system/passert.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "passert.h" - -#include "drivers/dbgserial.h" -#include "system/die.h" -#include "util/attributes.h" - -#include -#include -#include -#include -#include - -static NORETURN prv_handle_passert_failed_vargs(const char* filename, int line_number, - uintptr_t lr, const char* expr, const char* fmt, va_list fmt_args) { - dbgserial_print("ASSERT: "); - dbgserial_print(expr); - dbgserial_print(" "); - dbgserial_print(filename); - dbgserial_print(":"); - dbgserial_print_hex(line_number); - if (fmt) { - dbgserial_print(" "); - dbgserial_print(fmt); - } - dbgserial_putstr(""); - - reset_due_to_software_failure(); -} - -static NORETURN prv_handle_passert_failed(const char* filename, int line_number, - uintptr_t lr, const char *expr, const char* fmt, ...) { - va_list fmt_args; - va_start(fmt_args, fmt); - - prv_handle_passert_failed_vargs(filename, line_number, lr, expr, fmt, fmt_args); - - va_end(fmt_args); -} - -void passert_failed(const char* filename, int line_number, const char* message, ...) { - va_list fmt_args; - va_start(fmt_args, message); - - prv_handle_passert_failed_vargs(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERT", message, fmt_args); - - va_end(fmt_args); -} - -void passert_failed_no_message(const char* filename, int line_number) { - prv_handle_passert_failed(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERTN", NULL); -} - -void wtf(void) { - uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); - dbgserial_print("*** WTF "); - dbgserial_print_hex(saved_lr); - dbgserial_putstr(""); - reset_due_to_software_failure(); -} - -//! Assert function called by the STM peripheral library's -//! 'assert_param' method. See stm32f2xx_conf.h for more information. -void assert_failed(uint8_t* file, uint32_t line) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - - prv_handle_passert_failed((const char*) file, line, saved_lr, "STM32", "STM32 peripheral library " - "tripped an assert"); -} diff --git a/platform/robert/boot/src/system/passert.h b/platform/robert/boot/src/system/passert.h deleted file mode 100644 index 781f297dab..0000000000 --- a/platform/robert/boot/src/system/passert.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "logging.h" - - -void passert_failed(const char* filename, int line_number, const char* message, ...) - __attribute__((noreturn)); - -#define PBL_ASSERT(expr, ...) \ - do { \ - if (!(expr)) { \ - passert_failed(__FILE_NAME__, __LINE__, __VA_ARGS__); \ - } \ - } while (0) - -#define PBL_ASSERTN(expr) \ - do { \ - if (!(expr)) { \ - passert_failed_no_message(__FILE_NAME__, __LINE__); \ - } \ - } while (0) - -void passert_failed_no_message(const char* filename, int line_number) - __attribute__((noreturn)); - -void wtf(void) __attribute__((noreturn)); - -#define WTF wtf() - -// Insert a compiled-in breakpoint -#define BREAKPOINT __asm("bkpt") - -#define PBL_ASSERT_PRIVILEGED() -#define PBL_ASSERT_TASK(task) - -#define PBL_CROAK(fmt, args...) \ - passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args) diff --git a/platform/robert/boot/src/system/reset.c b/platform/robert/boot/src/system/reset.c deleted file mode 100644 index c4a709c499..0000000000 --- a/platform/robert/boot/src/system/reset.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "system/reset.h" - -#include "drivers/display.h" - -#include "stm32f7xx.h" - -void system_reset(void) { - display_prepare_for_reset(); - - // Clear the reset reason since it will no longer - // apply after this bootloader reset - RCC_ClearFlag(); - - system_hard_reset(); -} - -void system_hard_reset(void) { - NVIC_SystemReset(); - __builtin_unreachable(); -} diff --git a/platform/robert/boot/src/system/reset.h b/platform/robert/boot/src/system/reset.h deleted file mode 100644 index 6b5ba28099..0000000000 --- a/platform/robert/boot/src/system/reset.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Reset nicely after shutting down system services. Does not set the reboot_reason other than -//! calling reboot_reason_set_restarted_safely just before the reset occurs. -void system_reset(void)__attribute__((noreturn)); - -//! The final stage in the reset process. -void system_hard_reset(void) __attribute__((noreturn)); diff --git a/platform/robert/boot/src/system/rtc_registers.h b/platform/robert/boot/src/system/rtc_registers.h deleted file mode 100644 index 2b77ef4c01..0000000000 --- a/platform/robert/boot/src/system/rtc_registers.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0 -#define STUCK_BUTTON_REGISTER RTC_BKP_DR1 -#define BOOTLOADER_VERSION_REGISTER RTC_BKP_DR2 -#define CURRENT_TIME_REGISTER RTC_BKP_DR3 -#define CURRENT_INTERVAL_TICKS_REGISTER RTC_BKP_DR4 -#define REBOOT_REASON_REGISTER_1 RTC_BKP_DR5 -#define REBOOT_REASON_REGISTER_2 RTC_BKP_DR6 -#define REBOOT_REASON_STUCK_TASK_PC RTC_BKP_DR7 -#define REBOOT_REASON_STUCK_TASK_LR RTC_BKP_DR8 -#define REBOOT_REASON_STUCK_TASK_CALLBACK RTC_BKP_DR9 -#define REBOOT_REASON_MUTEX_LR RTC_BKP_DR10 -#define REBOOT_REASON_MUTEX_PC RTC_BKP_DR11 // Deprecated -#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19 diff --git a/platform/robert/boot/src/util/attributes.h b/platform/robert/boot/src/util/attributes.h deleted file mode 100644 index 8cd71d62d5..0000000000 --- a/platform/robert/boot/src/util/attributes.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if defined(__clang__) -#define GCC_ONLY(x) -#else -#define GCC_ONLY(x) x -#endif - -// Function attributes -#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST))) - -#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST) - -#define ALWAYS_INLINE __attribute__((__always_inline__)) inline -#define NOINLINE __attribute__((__noinline__)) -#define NORETURN __attribute__((__noreturn__)) void -#define NAKED_FUNC __attribute__((__naked__)) -#define OPTIMIZE_FUNC(LVL) __attribute__((__optimize__(LVL))) -#define CONST_FUNC __attribute__((__const__)) -#define PURE_FUNC __attribute__((__pure__)) - -// Variable attributes -#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC))) - -// Structure attributes -#define PACKED __attribute__((__packed__)) - -// General attributes -#define USED __attribute__((__used__)) -#define UNUSED __attribute__((__unused__)) -#define WEAK __attribute__((__weak__)) -#define ALIAS(sym) __attribute__((__weak__, __alias__(sym))) -#define SECTION(SEC) GCC_ONLY(__attribute__((__section__(SEC)))) -#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__))) diff --git a/platform/robert/boot/src/util/cobs.c b/platform/robert/boot/src/util/cobs.c deleted file mode 100644 index 3579543d52..0000000000 --- a/platform/robert/boot/src/util/cobs.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "cobs.h" - -size_t cobs_encode(void *dst_ptr, const void *src_ptr, size_t length) { - const char *src = src_ptr; - char *dst = dst_ptr; - uint8_t code = 0x01; - size_t code_idx = 0; - size_t dst_idx = 1; - - for (size_t src_idx = 0; src_idx < length; ++src_idx) { - if (src[src_idx] == '\0') { - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } else { - dst[dst_idx++] = src[src_idx]; - code++; - if (code == 0xff) { - if (src_idx == length - 1) { - // Special case: the final encoded block is 254 bytes long with no - // zero after it. While it's technically a valid encoding if a - // trailing zero is appended, it causes the output to be one byte - // longer than it needs to be. This violates consistent overhead - // contract and could overflow a carefully sized buffer. - break; - } - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } - } - } - dst[code_idx] = code; - return dst_idx; -} diff --git a/platform/robert/boot/src/util/cobs.h b/platform/robert/boot/src/util/cobs.h deleted file mode 100644 index 7194512e53..0000000000 --- a/platform/robert/boot/src/util/cobs.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! An implementation of Consistent Overhead Byte Stuffing -//! -//! http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf -//! http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing - -//! Evaluates to the offset required when encoding in-place -#define COBS_OVERHEAD(n) (((n) + 253) / 254) -//! Evaluates to the maximum buffer size required to hold n bytes of data -//! after COBS encoding. -#define MAX_SIZE_AFTER_COBS_ENCODING(n) ((n) + COBS_OVERHEAD(n)) - -//! COBS-encode a buffer out to another buffer. -//! -//! @param [out] dst destination buffer. The buffer must be at least -//! MAX_SIZE_AFTER_COBS_ENCODING(length) bytes long. -//! @param [in] src source buffer -//! @param length length of src -size_t cobs_encode(void * restrict dst, const void * restrict src, - size_t length); diff --git a/platform/robert/boot/src/util/crc32.c b/platform/robert/boot/src/util/crc32.c deleted file mode 100644 index 177b54de8e..0000000000 --- a/platform/robert/boot/src/util/crc32.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/crc32.h" - -// Nybble-wide table driven CRC-32 algorithm -// -// A compromise between speed and size, this algorithm uses a lookup table to -// calculate the CRC four bits at a time with a size cost of only 64 bytes. By -// contrast, a byte-wide algorithm requires a lookup table sixteen times larger! -// -// The lookup table is generated by the crc32_lut.py - -static const uint32_t s_lookup_table[] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, -}; - -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) { - if (data == 0) { - return 0; - } - const uint8_t * restrict bytes = data; - - crc ^= 0xffffffff; - while (length--) { - crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf]; - crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf]; - bytes++; - } - crc ^= 0xffffffff; - return crc; -} diff --git a/platform/robert/boot/src/util/crc32.h b/platform/robert/boot/src/util/crc32.h deleted file mode 100644 index 80ac71ebae..0000000000 --- a/platform/robert/boot/src/util/crc32.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! \file -//! Calculate the CRC-32 checksum of data. -//! -//! The checksum is the standard CRC-32 used by zlib, PNG and others. -//! The model parameters for the algorithm, as described in A Painless Guide to -//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are: -//! Name: "CRC-32" -//! Width: 32 -//! Poly: 04C11DB7 -//! Init: FFFFFFFF -//! RefIn: True -//! RefOut: True -//! XorOut: FFFFFFFF -//! Check: CBF43926 - -#include -#include - -//! Update a running CRC-32 checksum with the bytes of data and return the -//! updated CRC-32. If data is NULL, the function returns the required initial -//! value for the CRC. -//! -//! This function is drop-in compatible with zlib's crc32 function. -//! -//! \par Usage -//! \code -//! uint32_t crc = crc32(0, NULL, 0); -//! while (read_buffer(data, length)) { -//! crc = crc32(crc, data, length); -//! } -//! \endcode -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length); - -//! The initial CRC register value for a standard CRC-32 checksum. -//! -//! It is the same value as is returned by the `crc32` function when data is -//! NULL. -//! -//! \code -//! assert(CRC32_INIT == crc32(0, NULL, 0)); -//! \endcode -#define CRC32_INIT (0) - -//! The residue constant of the CRC-32 algorithm. -//! -//! If the CRC-32 value of a message is appended (little-endian) onto the -//! end of the message, the CRC-32 of the concatenated message and CRC will be -//! equal to CRC32_RESIDUE if the message has not been corrupted in transit. -#define CRC32_RESIDUE (0x2144DF1C) diff --git a/platform/robert/boot/src/util/delay.c b/platform/robert/boot/src/util/delay.c deleted file mode 100644 index 1f32d12f26..0000000000 --- a/platform/robert/boot/src/util/delay.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "delay.h" -#include "util/attributes.h" - -#include "stm32f7xx.h" - -#include - -void delay_us(uint32_t us) { - // Empirically (measured on a C2 bb), 1 loop = 1 cycle. (sysclk @ - // 16MHz, I-Cache disabled) Alignment of code will have some impact on how - // long this actually takes - uint32_t delay_loops = us * 16; - - __asm volatile ( - "spinloop: \n" - " subs %[delay_loops], #1 \n" - " bne spinloop \n" - : [delay_loops] "+r" (delay_loops) // read-write operand - : - : "cc" - ); -} - -void delay_ms(uint32_t millis) { - // delay_us(millis*1000) is not used because a long delay could easily - // overflow the veriable. Without the outer loop, a delay of even five - // seconds would overflow. - while (millis--) { - delay_us(1000); - } -} diff --git a/platform/robert/boot/src/util/delay.h b/platform/robert/boot/src/util/delay.h deleted file mode 100644 index d0a4c00f12..0000000000 --- a/platform/robert/boot/src/util/delay.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Carefully timed spinloop that allows one to delay at a microsecond -//! granularity. -void delay_us(uint32_t us); - -void delay_ms(uint32_t millis); diff --git a/platform/robert/boot/src/util/math.c b/platform/robert/boot/src/util/math.c deleted file mode 100644 index 14e0f303bd..0000000000 --- a/platform/robert/boot/src/util/math.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/math.h" - -#include -#include - -int ceil_log_two(uint32_t n) { - // clz stands for Count Leading Zeroes. We use it to find the MSB - int msb = 31 - __builtin_clz(n); - // popcount counts the number of set bits in a word (1's) - bool power_of_two = __builtin_popcount(n) == 1; - // if not exact power of two, use the next power of two - // we want to err on the side of caution and want to - // always round up - return ((power_of_two) ? msb : (msb + 1)); -} diff --git a/platform/robert/boot/src/util/math.h b/platform/robert/boot/src/util/math.h deleted file mode 100644 index 052d82bba1..0000000000 --- a/platform/robert/boot/src/util/math.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define ABS(a) (((a) > 0) ? (a) : -1 * (a)) -#define CLIP(n, min, max) ((n) < (min) ? (min) : ((n) > (max) ? (max) : (n))) -#define ROUND(num, denom) (((num) + ((denom) / 2))/(denom)) -#define WITHIN(n, min, max) ((n) >= (min) && (n) <= (max)) -#define RANGE_WITHIN(n_min, n_max, min, max) ((n_min) >= (min) && (n_max) <= (max)) - -// Divide num by denom, rounding up (ceil(0.5) is 1.0, and ceil(-0.5) is 0.0) -// ex. 3, 4 (ie. 3/4) : returns 1 -// ex. -3, 4 : returns 0 -#define DIVIDE_CEIL(num, denom) ((num + (denom - 1)) / denom) - -// Round value up to the next increment of modulus -// ex. val = 152 mod = 32 : returns 160 -// val = -32 mod = 90 : returns -90 -#define ROUND_TO_MOD_CEIL(val, mod) \ - ((val >= 0) ? \ - ((((val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)) : \ - -((((-val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod))) - -/* - * find the log base two of a number rounded up - */ -int ceil_log_two(uint32_t n); - -/* - * The -Wtype-limits flag generated an error with the previous IS_SIGNED maco. - * If an unsigned number was passed in the macro would check if the unsigned number was less than 0. - */ -//! Determine whether a variable is signed or not. -//! @param var The variable to evaluate. -//! @return true if the variable is signed. -#define IS_SIGNED(var) (__builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned char), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned short), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned int), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned long), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned long long), false, true))))) \ -) - -/** - * Compute the next backoff interval using a bounded binary expoential backoff formula. - * - * @param[in,out] attempt The number of retries performed so far. This count will be incremented - * by the function. - * @param[in] initial_value The inital backoff interval. Subsequent backoff attempts will be this - * number multiplied by a power of 2. - * @param[in] max_value The maximum backoff interval that returned by the function. - * @return The next backoff interval. - */ -uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value); - -//! Find the greatest common divisor of two numbers. -uint32_t gcd(uint32_t a, uint32_t b); diff --git a/platform/robert/boot/src/util/misc.c b/platform/robert/boot/src/util/misc.c deleted file mode 100644 index fbb8db908f..0000000000 --- a/platform/robert/boot/src/util/misc.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "misc.h" - -#include "drivers/dbgserial.h" - -#include - -void itoa_hex(uint32_t num, char *buffer, int buffer_length) { - if (buffer_length < 11) { - dbgserial_putstr("itoa buffer too small"); - return; - } - *buffer++ = '0'; - *buffer++ = 'x'; - - for (int i = 7; i >= 0; --i) { - uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4); - - char c; - if (digit < 0xa) { - c = '0' + digit; - } else if (digit < 0x10) { - c = 'a' + (digit - 0xa); - } else { - c = ' '; - } - - *buffer++ = c; - } - *buffer = '\0'; -} diff --git a/platform/robert/boot/src/util/misc.h b/platform/robert/boot/src/util/misc.h deleted file mode 100644 index 89e73e867d..0000000000 --- a/platform/robert/boot/src/util/misc.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000) - -#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0])) - -//! Convert num to a hex string and put in buffer -void itoa_hex(uint32_t num, char *buffer, int buffer_length); diff --git a/platform/robert/boot/src/util/net.h b/platform/robert/boot/src/util/net.h deleted file mode 100644 index 3d975cff3e..0000000000 --- a/platform/robert/boot/src/util/net.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// When compiling test, the host OS might have conflicting defines for this: -#undef ntohs -#undef htons -#undef ntohl -#undef htonl -#undef ltohs -#undef ltohl - -static inline uint16_t ntohs(uint16_t v) { - // return ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8); - return __builtin_bswap16(v); -} - -static inline uint16_t htons(uint16_t v) { - return ntohs(v); -} - -static inline uint32_t ntohl(uint32_t v) { - // return ((v & 0x000000ff) << 24) | - // ((v & 0x0000ff00) << 8) | - // ((v & 0x00ff0000) >> 8) | - // ((v & 0xff000000) >> 24); - return __builtin_bswap32(v); -} - -static inline uint32_t htonl(uint32_t v) { - return ntohl(v); -} - -#define ltohs(v) (v) -#define ltohl(v) (v) - -// Types for values in network byte-order. They are wrapped in structs so that -// the compiler will disallow implicit casting of these types to or from -// integral types. This way it is a compile error to try using variables of -// these types without first performing a byte-order conversion. -// There is no overhead for wrapping the values in structs. -typedef struct net16 { - uint16_t v; -} net16; - -typedef struct net32 { - uint32_t v; -} net32; - -static inline uint16_t ntoh16(net16 net) { - return ntohs(net.v); -} - -static inline net16 hton16(uint16_t v) { - return (net16){ htons(v) }; -} - -static inline uint32_t ntoh32(net32 net) { - return ntohl(net.v); -} - -static inline net32 hton32(uint32_t v) { - return (net32){ htonl(v) }; -} diff --git a/platform/robert/boot/src/util/size.h b/platform/robert/boot/src/util/size.h deleted file mode 100644 index 6bad013784..0000000000 --- a/platform/robert/boot/src/util/size.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Calculate the length of an array, based on the size of the element type. -//! @param array The array to be evaluated. -//! @return The length of the array. -#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0])) - -//! Calculate the length of a literal array based on the size of the given type -//! This is usable in contexts that require compile time constants -//! @param type Type of the elements -//! @param array Literal definition of the array -//! @return Length of the array in bytes -#define STATIC_ARRAY_LENGTH(type, array) (sizeof((type[]) array) / sizeof(type)) - -#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) diff --git a/platform/robert/boot/src/util/sle.c b/platform/robert/boot/src/util/sle.c deleted file mode 100644 index 44f5f7c63a..0000000000 --- a/platform/robert/boot/src/util/sle.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sle.h" - -#include "system/passert.h" - -// See waftools/sparse_length_encoding.py for more info on SLE encoding/decoding - -typedef struct { - const uint8_t *data; - uint32_t index; - uint32_t length; -} ReadByteStream; - -typedef struct { - uint8_t *data; - uint32_t index; - uint32_t length; -} WriteByteStream; - -static uint8_t prv_byte_stream_read(ReadByteStream *stream) { - PBL_ASSERTN(stream->index < stream->length); - return stream->data[stream->index++]; -} - -static void prv_byte_stream_write(WriteByteStream *stream, uint8_t data) { - PBL_ASSERTN(stream->index < stream->length); - stream->data[stream->index++] = data; -} - -uint32_t sle_decode(const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) { - ReadByteStream in_stream = { - .data = in, - .length = in_len - }; - WriteByteStream out_stream = { - .data = out, - .length = out_len - }; - - const uint8_t escape = prv_byte_stream_read(&in_stream); - while (true) { - const uint8_t byte = prv_byte_stream_read(&in_stream); - if (byte != escape) { - // simply write the byte into the output stream - prv_byte_stream_write(&out_stream, byte); - continue; - } - - // read the escape code - const uint8_t code = prv_byte_stream_read(&in_stream); - if (code == 0) { - // end of stream - break; - } else if (code == 1) { - // literal escape byte - prv_byte_stream_write(&out_stream, escape); - } else { - // a sequence of zeros - uint16_t count; - if ((code & 0x80) == 0) { - // the count is only 1 byte (1-127) - count = code; - } else { - // the count is 2 bytes - count = (((uint16_t)(code & 0x7f) << 8) | prv_byte_stream_read(&in_stream)) + 0x80; - } - for (int i = 0; i < count; ++i) { - prv_byte_stream_write(&out_stream, 0); - } - } - } - return out_stream.index; -} diff --git a/platform/robert/boot/src/util/sle.h b/platform/robert/boot/src/util/sle.h deleted file mode 100644 index 9eb1958113..0000000000 --- a/platform/robert/boot/src/util/sle.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -uint32_t sle_decode(const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len); diff --git a/platform/robert/boot/src/vector_table.c b/platform/robert/boot/src/vector_table.c deleted file mode 100644 index c861e10044..0000000000 --- a/platform/robert/boot/src/vector_table.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - - -extern int main(void); - -//! These symbols are defined in the linker script for use in initializing -//! the data sections. uint8_t since we do arithmetic with section lengths. -//! These are arrays to avoid the need for an & when dealing with linker symbols. -extern uint8_t __data_load_start[]; -extern uint8_t __data_start[]; -extern uint8_t __data_end[]; -extern uint8_t __bss_start[]; -extern uint8_t __bss_end[]; -extern uint8_t _estack[]; - -__attribute__((__noreturn__)) void Reset_Handler(void) { - memcpy(__data_start, __data_load_start, __data_end - __data_start); - - // Clear the bss section, assumes .bss goes directly after .data - memset(__bss_start, 0, __bss_end - __bss_start); - - main(); - - __builtin_unreachable(); -} - -__attribute__((__noreturn__)) void Default_Handler(void) { - // This handler is only called if we haven't defined a specific - // handler for the interrupt. This means the interrupt is unexpected, - // so we loop infinitely to preserve the system state for examination - // by a debugger - while (true) {} -} - - -// All these functions are weak references to the Default_Handler, -// so if we define a handler in elsewhere in the firmware, these -// will be overriden -#define ALIAS(sym) __attribute__((__weak__, __alias__(sym))) -ALIAS("Default_Handler") void NMI_Handler(void); -ALIAS("Default_Handler") void HardFault_Handler(void); -ALIAS("Default_Handler") void MemManage_Handler(void); -ALIAS("Default_Handler") void BusFault_Handler(void); -ALIAS("Default_Handler") void UsageFault_Handler(void); -ALIAS("Default_Handler") void SVC_Handler(void); -ALIAS("Default_Handler") void DebugMon_Handler(void); -ALIAS("Default_Handler") void PendSV_Handler(void); -ALIAS("Default_Handler") void SysTick_Handler(void); - -// External Interrupts -#define IRQ_DEF(idx, irq) ALIAS("Default_Handler") void irq##_IRQHandler(void); -#include "irq_stm32f7.def" -#undef IRQ_DEF - - -__attribute__((__section__(".isr_vector"))) const void * const vector_table[] = { - _estack, - Reset_Handler, - NMI_Handler, - HardFault_Handler, - MemManage_Handler, - BusFault_Handler, - UsageFault_Handler, - 0, - 0, - 0, - 0, - SVC_Handler, - DebugMon_Handler, - 0, - PendSV_Handler, - SysTick_Handler, - - // External Interrupts -#define IRQ_DEF(idx, irq) [idx + 16] = irq##_IRQHandler, -#include "irq_stm32f7.def" -#undef IRQ_DEF -}; diff --git a/platform/robert/boot/test/test_system_flash.c b/platform/robert/boot/test/test_system_flash.c deleted file mode 100644 index caeaaa10a1..0000000000 --- a/platform/robert/boot/test/test_system_flash.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "clar.h" - -#include "drivers/system_flash.h" - -#define KiB *1024 - -// Set bits n..0 in a bit-vector (zero-indexed) -#define BITS(n) ((((uint32_t)(1 << ((n) + 1)))) - 1) -// Set bits y..x in a bit-vector (x <= y; x, y >= 0) -#define BITS_BETWEEN(x, y) (BITS(y) & ~BITS(x-1)) - -// Yo dawg, I heard you like tests so I put tests in your tests so you can test -// your tests while you test! -void test_system_flash__bit_range_macros(void) { - cl_assert_equal_i(0b1, BITS(0)); - cl_assert_equal_i(0b00011111, BITS(4)); - cl_assert_equal_i(0b00111111, BITS_BETWEEN(0, 5)); - cl_assert_equal_i(0b00111000, BITS_BETWEEN(3, 5)); - cl_assert_equal_i(0b00010000, BITS_BETWEEN(4, 4)); -} - -// Flash memory is organized into twelve sectors of unequal sizes. -// Sectors 0-3 are 16 KiB. Sector 4 is 64 KiB. -// The remaining sectors are 128 KiB. - -// Bitset of sectors that have been "erased" -static uint32_t erased_sector; -static bool flash_locked, flash_flags_set; -static FLASH_Status return_status; -uint8_t *flash_written_data; -bool *flash_written_flag; -void *source_buffer; -uint32_t flash_data_start, flash_data_length; -bool callback_called; - -void test_system_flash__initialize(void) { - erased_sector = 0; - flash_locked = true; - flash_flags_set = false; - return_status = FLASH_COMPLETE; - flash_written_data = NULL; - flash_written_flag = NULL; - flash_data_start = 0; - flash_data_length = 0; - callback_called = false; -} - -void test_system_flash__cleanup(void) { - free(flash_written_data); - free(flash_written_flag); -} - -void test_system_flash__erase_zero_bytes(void) { - cl_assert(system_flash_erase(FLASH_BASE, 0, NULL, NULL)); - cl_assert_equal_i(0, erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte_in_middle_of_sector(void) { - cl_assert(system_flash_erase(FLASH_BASE + 12345, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_some_sectors_from_beginning(void) { - cl_assert(system_flash_erase(FLASH_BASE, 128 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 4), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_full_flash(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1024 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 7), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_sector_0(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_16KB_sectors(void) { - cl_assert(system_flash_erase(FLASH_BASE, 48 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 2), erased_sector); - cl_assert(flash_locked); -} - -void callback_is_called_cb(uint32_t num, uint32_t den, void *context) { - callback_called = true; - cl_assert_equal_i(8675309, (uintptr_t)context); -} - -void test_system_flash__callback_is_called(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, callback_is_called_cb, - (void *)8675309)); - cl_assert(callback_called); - cl_assert(flash_locked); -} - -void test_system_flash__handle_erase_error(void) { - return_status = FLASH_ERROR_OPERATION; - cl_assert(!system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert(flash_locked); -} - -void error_in_middle_cb(uint32_t num, uint32_t den, void *context) { - int *countdown = context; - if ((*countdown)-- == 0) { - return_status = FLASH_ERROR_OPERATION; - } -} - -void test_system_flash__handle_erase_error_mid_operation(void) { - int countdown = 3; - cl_assert(!system_flash_erase(FLASH_BASE, 512 KiB, error_in_middle_cb, - &countdown)); - cl_assert(flash_locked); - cl_assert_(countdown <= 0, "Callback not called enough times"); -} - - -void malloc_flash_data(uint32_t size) { - flash_data_length = size; - flash_written_data = malloc(size * sizeof(uint8_t)); - cl_assert(flash_written_data); - flash_written_flag = malloc(size * sizeof(bool)); - cl_assert(flash_written_flag); -} - -void assert_flash_unwritten(uint32_t start, uint32_t length) { - for (uint32_t i = 0; i < length; ++i) { - cl_assert(flash_written_flag[start + i] == false); - } -} - -void test_system_flash__write_simple(void) { - const char testdata[] = "The quick brown fox jumps over the lazy dog."; - malloc_flash_data(100); - flash_data_start = FLASH_BASE; - cl_assert(system_flash_write( - FLASH_BASE + 10, testdata, sizeof(testdata), callback_is_called_cb, - (void *)8675309)); - cl_assert(flash_locked); - cl_assert_equal_s(testdata, (const char *)&flash_written_data[10]); - assert_flash_unwritten(0, 10); - assert_flash_unwritten(10 + sizeof(testdata), 90 - sizeof(testdata)); - cl_assert(callback_called); -} - -void test_system_flash__write_error(void) { - return_status = FLASH_ERROR_OPERATION; - malloc_flash_data(10); - flash_data_start = FLASH_BASE; - cl_assert(!system_flash_write(FLASH_BASE, "abc", 3, NULL, NULL)); - cl_assert(flash_locked); - assert_flash_unwritten(0, 10); -} - -extern void FLASH_Lock(void) { - flash_locked = true; -} - -extern void FLASH_Unlock(void) { - flash_locked = false; -} - -extern void FLASH_ClearFlag(uint32_t FLASH_FLAG) { - flash_flags_set = false; -} - -extern FLASH_Status FLASH_EraseSector(uint32_t sector, uint8_t voltage_range) { - // Pretty sure FLASH_Sector_N defines are simply 8*N, at least for the first - // twelve sectors. - cl_assert_(!flash_locked, "Attempted to erase a locked flash"); - cl_assert_(IS_FLASH_SECTOR(sector), "Sector number out of range"); - cl_assert(IS_VOLTAGERANGE(voltage_range)); - cl_check_(flash_flags_set == false, "Forgot to clear flags before erasing"); - cl_check_((erased_sector & (1 << sector/8)) == 0, - "Re-erasing an already erased sector"); - flash_flags_set = true; - if (return_status == FLASH_COMPLETE) { - erased_sector |= (1 << sector/8); - } - return return_status; -} - -extern FLASH_Status FLASH_ProgramByte(uint32_t address, uint8_t data) { - cl_assert_(!flash_locked, "Attempted to write to a locked flash"); - cl_assert_(address >= flash_data_start && - address < flash_data_start + flash_data_length, - "Address out of range"); - cl_assert_(flash_written_flag[address - flash_data_start] == false, - "Overwriting an already-written byte"); - if (return_status == FLASH_COMPLETE) { - flash_written_data[address - flash_data_start] = data; - } - return return_status; -} - -extern void dbgserial_print(char *str) { - fprintf(stderr, "%s", str); -} - -extern void dbgserial_print_hex(uint32_t num) { - fprintf(stderr, "0x%.08x", num); -} - -extern void dbgserial_putstr(char *str) { - fprintf(stderr, "%s\n", str); -} diff --git a/platform/robert/boot/vendor/CMSIS/Include/arm_common_tables.h b/platform/robert/boot/vendor/CMSIS/Include/arm_common_tables.h deleted file mode 100644 index d5d72417bd..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/arm_common_tables.h +++ /dev/null @@ -1,136 +0,0 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2014 ARM Limited. All rights reserved. -* -* $Date: 19. October 2015 -* $Revision: V.1.4.5 a -* -* Project: CMSIS DSP Library -* Title: arm_common_tables.h -* -* Description: This file has extern declaration for common tables like Bitreverse, reciprocal etc which are used across different functions -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -------------------------------------------------------------------- */ - -#ifndef _ARM_COMMON_TABLES_H -#define _ARM_COMMON_TABLES_H - -#include "arm_math.h" - -extern const uint16_t armBitRevTable[1024]; -extern const q15_t armRecipTableQ15[64]; -extern const q31_t armRecipTableQ31[64]; -/* extern const q31_t realCoefAQ31[1024]; */ -/* extern const q31_t realCoefBQ31[1024]; */ -extern const float32_t twiddleCoef_16[32]; -extern const float32_t twiddleCoef_32[64]; -extern const float32_t twiddleCoef_64[128]; -extern const float32_t twiddleCoef_128[256]; -extern const float32_t twiddleCoef_256[512]; -extern const float32_t twiddleCoef_512[1024]; -extern const float32_t twiddleCoef_1024[2048]; -extern const float32_t twiddleCoef_2048[4096]; -extern const float32_t twiddleCoef_4096[8192]; -#define twiddleCoef twiddleCoef_4096 -extern const q31_t twiddleCoef_16_q31[24]; -extern const q31_t twiddleCoef_32_q31[48]; -extern const q31_t twiddleCoef_64_q31[96]; -extern const q31_t twiddleCoef_128_q31[192]; -extern const q31_t twiddleCoef_256_q31[384]; -extern const q31_t twiddleCoef_512_q31[768]; -extern const q31_t twiddleCoef_1024_q31[1536]; -extern const q31_t twiddleCoef_2048_q31[3072]; -extern const q31_t twiddleCoef_4096_q31[6144]; -extern const q15_t twiddleCoef_16_q15[24]; -extern const q15_t twiddleCoef_32_q15[48]; -extern const q15_t twiddleCoef_64_q15[96]; -extern const q15_t twiddleCoef_128_q15[192]; -extern const q15_t twiddleCoef_256_q15[384]; -extern const q15_t twiddleCoef_512_q15[768]; -extern const q15_t twiddleCoef_1024_q15[1536]; -extern const q15_t twiddleCoef_2048_q15[3072]; -extern const q15_t twiddleCoef_4096_q15[6144]; -extern const float32_t twiddleCoef_rfft_32[32]; -extern const float32_t twiddleCoef_rfft_64[64]; -extern const float32_t twiddleCoef_rfft_128[128]; -extern const float32_t twiddleCoef_rfft_256[256]; -extern const float32_t twiddleCoef_rfft_512[512]; -extern const float32_t twiddleCoef_rfft_1024[1024]; -extern const float32_t twiddleCoef_rfft_2048[2048]; -extern const float32_t twiddleCoef_rfft_4096[4096]; - - -/* floating-point bit reversal tables */ -#define ARMBITREVINDEXTABLE__16_TABLE_LENGTH ((uint16_t)20 ) -#define ARMBITREVINDEXTABLE__32_TABLE_LENGTH ((uint16_t)48 ) -#define ARMBITREVINDEXTABLE__64_TABLE_LENGTH ((uint16_t)56 ) -#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208 ) -#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440 ) -#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448 ) -#define ARMBITREVINDEXTABLE1024_TABLE_LENGTH ((uint16_t)1800) -#define ARMBITREVINDEXTABLE2048_TABLE_LENGTH ((uint16_t)3808) -#define ARMBITREVINDEXTABLE4096_TABLE_LENGTH ((uint16_t)4032) - -extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE__16_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE__32_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE__64_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable128[ARMBITREVINDEXTABLE_128_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable256[ARMBITREVINDEXTABLE_256_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable512[ARMBITREVINDEXTABLE_512_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE1024_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE2048_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE4096_TABLE_LENGTH]; - -/* fixed-point bit reversal tables */ -#define ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH ((uint16_t)12 ) -#define ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH ((uint16_t)24 ) -#define ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH ((uint16_t)56 ) -#define ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH ((uint16_t)112 ) -#define ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH ((uint16_t)240 ) -#define ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH ((uint16_t)480 ) -#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992 ) -#define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984) -#define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032) - -extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH]; - -/* Tables for Fast Math Sine and Cosine */ -extern const float32_t sinTable_f32[FAST_MATH_TABLE_SIZE + 1]; -extern const q31_t sinTable_q31[FAST_MATH_TABLE_SIZE + 1]; -extern const q15_t sinTable_q15[FAST_MATH_TABLE_SIZE + 1]; - -#endif /* ARM_COMMON_TABLES_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/arm_const_structs.h b/platform/robert/boot/vendor/CMSIS/Include/arm_const_structs.h deleted file mode 100644 index 54595f55d4..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/arm_const_structs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2014 ARM Limited. All rights reserved. -* -* $Date: 19. March 2015 -* $Revision: V.1.4.5 -* -* Project: CMSIS DSP Library -* Title: arm_const_structs.h -* -* Description: This file has constant structs that are initialized for -* user convenience. For example, some can be given as -* arguments to the arm_cfft_f32() function. -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -------------------------------------------------------------------- */ - -#ifndef _ARM_CONST_STRUCTS_H -#define _ARM_CONST_STRUCTS_H - -#include "arm_math.h" -#include "arm_common_tables.h" - - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096; - - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096; - - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096; - -#endif diff --git a/platform/robert/boot/vendor/CMSIS/Include/arm_math.h b/platform/robert/boot/vendor/CMSIS/Include/arm_math.h deleted file mode 100644 index 580cbbde60..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/arm_math.h +++ /dev/null @@ -1,7154 +0,0 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2015 ARM Limited. All rights reserved. -* -* $Date: 20. October 2015 -* $Revision: V1.4.5 b -* -* Project: CMSIS DSP Library -* Title: arm_math.h -* -* Description: Public header file for CMSIS DSP Library -* -* Target Processor: Cortex-M7/Cortex-M4/Cortex-M3/Cortex-M0 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. - * -------------------------------------------------------------------- */ - -/** - \mainpage CMSIS DSP Software Library - * - * Introduction - * ------------ - * - * This user manual describes the CMSIS DSP software library, - * a suite of common signal processing functions for use on Cortex-M processor based devices. - * - * The library is divided into a number of functions each covering a specific category: - * - Basic math functions - * - Fast math functions - * - Complex math functions - * - Filters - * - Matrix functions - * - Transforms - * - Motor control functions - * - Statistical functions - * - Support functions - * - Interpolation functions - * - * The library has separate functions for operating on 8-bit integers, 16-bit integers, - * 32-bit integer and 32-bit floating-point values. - * - * Using the Library - * ------------ - * - * The library installer contains prebuilt versions of the libraries in the Lib folder. - * - arm_cortexM7lfdp_math.lib (Little endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfdp_math.lib (Big endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7lfsp_math.lib (Little endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfsp_math.lib (Big endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7l_math.lib (Little endian on Cortex-M7) - * - arm_cortexM7b_math.lib (Big endian on Cortex-M7) - * - arm_cortexM4lf_math.lib (Little endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4bf_math.lib (Big endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4l_math.lib (Little endian on Cortex-M4) - * - arm_cortexM4b_math.lib (Big endian on Cortex-M4) - * - arm_cortexM3l_math.lib (Little endian on Cortex-M3) - * - arm_cortexM3b_math.lib (Big endian on Cortex-M3) - * - arm_cortexM0l_math.lib (Little endian on Cortex-M0 / CortexM0+) - * - arm_cortexM0b_math.lib (Big endian on Cortex-M0 / CortexM0+) - * - * The library functions are declared in the public file arm_math.h which is placed in the Include folder. - * Simply include this file and link the appropriate library in the application and begin calling the library functions. The Library supports single - * public header file arm_math.h for Cortex-M7/M4/M3/M0/M0+ with little endian and big endian. Same header file will be used for floating point unit(FPU) variants. - * Define the appropriate pre processor MACRO ARM_MATH_CM7 or ARM_MATH_CM4 or ARM_MATH_CM3 or - * ARM_MATH_CM0 or ARM_MATH_CM0PLUS depending on the target processor in the application. - * - * Examples - * -------- - * - * The library ships with a number of examples which demonstrate how to use the library functions. - * - * Toolchain Support - * ------------ - * - * The library has been developed and tested with MDK-ARM version 5.14.0.0 - * The library is being tested in GCC and IAR toolchains and updates on this activity will be made available shortly. - * - * Building the Library - * ------------ - * - * The library installer contains a project file to re build libraries on MDK-ARM Tool chain in the CMSIS\\DSP_Lib\\Source\\ARM folder. - * - arm_cortexM_math.uvprojx - * - * - * The libraries can be built by opening the arm_cortexM_math.uvprojx project in MDK-ARM, selecting a specific target, and defining the optional pre processor MACROs detailed above. - * - * Pre-processor Macros - * ------------ - * - * Each library project have differant pre-processor macros. - * - * - UNALIGNED_SUPPORT_DISABLE: - * - * Define macro UNALIGNED_SUPPORT_DISABLE, If the silicon does not support unaligned memory access - * - * - ARM_MATH_BIG_ENDIAN: - * - * Define macro ARM_MATH_BIG_ENDIAN to build the library for big endian targets. By default library builds for little endian targets. - * - * - ARM_MATH_MATRIX_CHECK: - * - * Define macro ARM_MATH_MATRIX_CHECK for checking on the input and output sizes of matrices - * - * - ARM_MATH_ROUNDING: - * - * Define macro ARM_MATH_ROUNDING for rounding on support functions - * - * - ARM_MATH_CMx: - * - * Define macro ARM_MATH_CM4 for building the library on Cortex-M4 target, ARM_MATH_CM3 for building library on Cortex-M3 target - * and ARM_MATH_CM0 for building library on Cortex-M0 target, ARM_MATH_CM0PLUS for building library on Cortex-M0+ target, and - * ARM_MATH_CM7 for building the library on cortex-M7. - * - * - __FPU_PRESENT: - * - * Initialize macro __FPU_PRESENT = 1 when building on FPU supported Targets. Enable this macro for M4bf and M4lf libraries - * - *
    - * CMSIS-DSP in ARM::CMSIS Pack - * ----------------------------- - * - * The following files relevant to CMSIS-DSP are present in the ARM::CMSIS Pack directories: - * |File/Folder |Content | - * |------------------------------|------------------------------------------------------------------------| - * |\b CMSIS\\Documentation\\DSP | This documentation | - * |\b CMSIS\\DSP_Lib | Software license agreement (license.txt) | - * |\b CMSIS\\DSP_Lib\\Examples | Example projects demonstrating the usage of the library functions | - * |\b CMSIS\\DSP_Lib\\Source | Source files for rebuilding the library | - * - *
    - * Revision History of CMSIS-DSP - * ------------ - * Please refer to \ref ChangeLog_pg. - * - * Copyright Notice - * ------------ - * - * Copyright (C) 2010-2015 ARM Limited. All rights reserved. - */ - - -/** - * @defgroup groupMath Basic Math Functions - */ - -/** - * @defgroup groupFastMath Fast Math Functions - * This set of functions provides a fast approximation to sine, cosine, and square root. - * As compared to most of the other functions in the CMSIS math library, the fast math functions - * operate on individual values and not arrays. - * There are separate functions for Q15, Q31, and floating-point data. - * - */ - -/** - * @defgroup groupCmplxMath Complex Math Functions - * This set of functions operates on complex data vectors. - * The data in the complex arrays is stored in an interleaved fashion - * (real, imag, real, imag, ...). - * In the API functions, the number of samples in a complex array refers - * to the number of complex values; the array contains twice this number of - * real values. - */ - -/** - * @defgroup groupFilters Filtering Functions - */ - -/** - * @defgroup groupMatrix Matrix Functions - * - * This set of functions provides basic matrix math operations. - * The functions operate on matrix data structures. For example, - * the type - * definition for the floating-point matrix structure is shown - * below: - *
    - *     typedef struct
    - *     {
    - *       uint16_t numRows;     // number of rows of the matrix.
    - *       uint16_t numCols;     // number of columns of the matrix.
    - *       float32_t *pData;     // points to the data of the matrix.
    - *     } arm_matrix_instance_f32;
    - * 
    - * There are similar definitions for Q15 and Q31 data types. - * - * The structure specifies the size of the matrix and then points to - * an array of data. The array is of size numRows X numCols - * and the values are arranged in row order. That is, the - * matrix element (i, j) is stored at: - *
    - *     pData[i*numCols + j]
    - * 
    - * - * \par Init Functions - * There is an associated initialization function for each type of matrix - * data structure. - * The initialization function sets the values of the internal structure fields. - * Refer to the function arm_mat_init_f32(), arm_mat_init_q31() - * and arm_mat_init_q15() for floating-point, Q31 and Q15 types, respectively. - * - * \par - * Use of the initialization function is optional. However, if initialization function is used - * then the instance structure cannot be placed into a const data section. - * To place the instance structure in a const data - * section, manually initialize the data structure. For example: - *
    - * arm_matrix_instance_f32 S = {nRows, nColumns, pData};
    - * arm_matrix_instance_q31 S = {nRows, nColumns, pData};
    - * arm_matrix_instance_q15 S = {nRows, nColumns, pData};
    - * 
    - * where nRows specifies the number of rows, nColumns - * specifies the number of columns, and pData points to the - * data array. - * - * \par Size Checking - * By default all of the matrix functions perform size checking on the input and - * output matrices. For example, the matrix addition function verifies that the - * two input matrices and the output matrix all have the same number of rows and - * columns. If the size check fails the functions return: - *
    - *     ARM_MATH_SIZE_MISMATCH
    - * 
    - * Otherwise the functions return - *
    - *     ARM_MATH_SUCCESS
    - * 
    - * There is some overhead associated with this matrix size checking. - * The matrix size checking is enabled via the \#define - *
    - *     ARM_MATH_MATRIX_CHECK
    - * 
    - * within the library project settings. By default this macro is defined - * and size checking is enabled. By changing the project settings and - * undefining this macro size checking is eliminated and the functions - * run a bit faster. With size checking disabled the functions always - * return ARM_MATH_SUCCESS. - */ - -/** - * @defgroup groupTransforms Transform Functions - */ - -/** - * @defgroup groupController Controller Functions - */ - -/** - * @defgroup groupStats Statistics Functions - */ -/** - * @defgroup groupSupport Support Functions - */ - -/** - * @defgroup groupInterpolation Interpolation Functions - * These functions perform 1- and 2-dimensional interpolation of data. - * Linear interpolation is used for 1-dimensional data and - * bilinear interpolation is used for 2-dimensional data. - */ - -/** - * @defgroup groupExamples Examples - */ -#ifndef _ARM_MATH_H -#define _ARM_MATH_H - -/* ignore some GCC warnings */ -#if defined ( __GNUC__ ) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - -#define __CMSIS_GENERIC /* disable NVIC and Systick functions */ - -#if defined(ARM_MATH_CM7) - #include "core_cm7.h" -#elif defined (ARM_MATH_CM4) - #include "core_cm4.h" -#elif defined (ARM_MATH_CM3) - #include "core_cm3.h" -#elif defined (ARM_MATH_CM0) - #include "core_cm0.h" - #define ARM_MATH_CM0_FAMILY -#elif defined (ARM_MATH_CM0PLUS) - #include "core_cm0plus.h" - #define ARM_MATH_CM0_FAMILY -#else - #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0" -#endif - -#undef __CMSIS_GENERIC /* enable NVIC and Systick functions */ -#include "string.h" -#include "math.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - - /** - * @brief Macros required for reciprocal calculation in Normalized LMS - */ - -#define DELTA_Q31 (0x100) -#define DELTA_Q15 0x5 -#define INDEX_MASK 0x0000003F -#ifndef PI -#define PI 3.14159265358979f -#endif - - /** - * @brief Macros required for SINE and COSINE Fast math approximations - */ - -#define FAST_MATH_TABLE_SIZE 512 -#define FAST_MATH_Q31_SHIFT (32 - 10) -#define FAST_MATH_Q15_SHIFT (16 - 10) -#define CONTROLLER_Q31_SHIFT (32 - 9) -#define TABLE_SIZE 256 -#define TABLE_SPACING_Q31 0x400000 -#define TABLE_SPACING_Q15 0x80 - - /** - * @brief Macros required for SINE and COSINE Controller functions - */ - /* 1.31(q31) Fixed value of 2/360 */ - /* -1 to +1 is divided into 360 values so total spacing is (2/360) */ -#define INPUT_SPACING 0xB60B61 - - /** - * @brief Macro for Unaligned Support - */ -#ifndef UNALIGNED_SUPPORT_DISABLE - #define ALIGN4 -#else - #if defined (__GNUC__) - #define ALIGN4 __attribute__((aligned(4))) - #else - #define ALIGN4 __align(4) - #endif -#endif /* #ifndef UNALIGNED_SUPPORT_DISABLE */ - - /** - * @brief Error status returned by some functions in the library. - */ - - typedef enum - { - ARM_MATH_SUCCESS = 0, /**< No error */ - ARM_MATH_ARGUMENT_ERROR = -1, /**< One or more arguments are incorrect */ - ARM_MATH_LENGTH_ERROR = -2, /**< Length of data buffer is incorrect */ - ARM_MATH_SIZE_MISMATCH = -3, /**< Size of matrices is not compatible with the operation. */ - ARM_MATH_NANINF = -4, /**< Not-a-number (NaN) or infinity is generated */ - ARM_MATH_SINGULAR = -5, /**< Generated by matrix inversion if the input matrix is singular and cannot be inverted. */ - ARM_MATH_TEST_FAILURE = -6 /**< Test Failed */ - } arm_status; - - /** - * @brief 8-bit fractional data type in 1.7 format. - */ - typedef int8_t q7_t; - - /** - * @brief 16-bit fractional data type in 1.15 format. - */ - typedef int16_t q15_t; - - /** - * @brief 32-bit fractional data type in 1.31 format. - */ - typedef int32_t q31_t; - - /** - * @brief 64-bit fractional data type in 1.63 format. - */ - typedef int64_t q63_t; - - /** - * @brief 32-bit floating-point type definition. - */ - typedef float float32_t; - - /** - * @brief 64-bit floating-point type definition. - */ - typedef double float64_t; - - /** - * @brief definition to read/write two 16 bit values. - */ -#if defined __CC_ARM - #define __SIMD32_TYPE int32_t __packed - #define CMSIS_UNUSED __attribute__((unused)) - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __SIMD32_TYPE int32_t - #define CMSIS_UNUSED __attribute__((unused)) - -#elif defined __GNUC__ - #define __SIMD32_TYPE int32_t - #define CMSIS_UNUSED __attribute__((unused)) - -#elif defined __ICCARM__ - #define __SIMD32_TYPE int32_t __packed - #define CMSIS_UNUSED - -#elif defined __CSMC__ - #define __SIMD32_TYPE int32_t - #define CMSIS_UNUSED - -#elif defined __TASKING__ - #define __SIMD32_TYPE __unaligned int32_t - #define CMSIS_UNUSED - -#else - #error Unknown compiler -#endif - -#define __SIMD32(addr) (*(__SIMD32_TYPE **) & (addr)) -#define __SIMD32_CONST(addr) ((__SIMD32_TYPE *)(addr)) -#define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr)) -#define __SIMD64(addr) (*(int64_t **) & (addr)) - -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) - /** - * @brief definition to pack two 16 bit values. - */ -#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | \ - (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) ) -#define __PKHTB(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0xFFFF0000) | \ - (((int32_t)(ARG2) >> ARG3) & (int32_t)0x0000FFFF) ) - -#endif - - - /** - * @brief definition to pack four 8 bit values. - */ -#ifndef ARM_MATH_BIG_ENDIAN - -#define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v0) << 0) & (int32_t)0x000000FF) | \ - (((int32_t)(v1) << 8) & (int32_t)0x0000FF00) | \ - (((int32_t)(v2) << 16) & (int32_t)0x00FF0000) | \ - (((int32_t)(v3) << 24) & (int32_t)0xFF000000) ) -#else - -#define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v3) << 0) & (int32_t)0x000000FF) | \ - (((int32_t)(v2) << 8) & (int32_t)0x0000FF00) | \ - (((int32_t)(v1) << 16) & (int32_t)0x00FF0000) | \ - (((int32_t)(v0) << 24) & (int32_t)0xFF000000) ) - -#endif - - - /** - * @brief Clips Q63 to Q31 values. - */ - static __INLINE q31_t clip_q63_to_q31( - q63_t x) - { - return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? - ((0x7FFFFFFF ^ ((q31_t) (x >> 63)))) : (q31_t) x; - } - - /** - * @brief Clips Q63 to Q15 values. - */ - static __INLINE q15_t clip_q63_to_q15( - q63_t x) - { - return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? - ((0x7FFF ^ ((q15_t) (x >> 63)))) : (q15_t) (x >> 15); - } - - /** - * @brief Clips Q31 to Q7 values. - */ - static __INLINE q7_t clip_q31_to_q7( - q31_t x) - { - return ((q31_t) (x >> 24) != ((q31_t) x >> 23)) ? - ((0x7F ^ ((q7_t) (x >> 31)))) : (q7_t) x; - } - - /** - * @brief Clips Q31 to Q15 values. - */ - static __INLINE q15_t clip_q31_to_q15( - q31_t x) - { - return ((q31_t) (x >> 16) != ((q31_t) x >> 15)) ? - ((0x7FFF ^ ((q15_t) (x >> 31)))) : (q15_t) x; - } - - /** - * @brief Multiplies 32 X 64 and returns 32 bit result in 2.30 format. - */ - - static __INLINE q63_t mult32x64( - q63_t x, - q31_t y) - { - return ((((q63_t) (x & 0x00000000FFFFFFFF) * y) >> 32) + - (((q63_t) (x >> 32) * y))); - } - -/* - #if defined (ARM_MATH_CM0_FAMILY) && defined ( __CC_ARM ) - #define __CLZ __clz - #endif - */ -/* note: function can be removed when all toolchain support __CLZ for Cortex-M0 */ -#if defined (ARM_MATH_CM0_FAMILY) && ((defined (__ICCARM__)) ) - static __INLINE uint32_t __CLZ( - q31_t data); - - static __INLINE uint32_t __CLZ( - q31_t data) - { - uint32_t count = 0; - uint32_t mask = 0x80000000; - - while((data & mask) == 0) - { - count += 1u; - mask = mask >> 1u; - } - - return (count); - } -#endif - - /** - * @brief Function to Calculates 1/in (reciprocal) value of Q31 Data type. - */ - - static __INLINE uint32_t arm_recip_q31( - q31_t in, - q31_t * dst, - q31_t * pRecipTable) - { - q31_t out; - uint32_t tempVal; - uint32_t index, i; - uint32_t signBits; - - if(in > 0) - { - signBits = ((uint32_t) (__CLZ( in) - 1)); - } - else - { - signBits = ((uint32_t) (__CLZ(-in) - 1)); - } - - /* Convert input sample to 1.31 format */ - in = (in << signBits); - - /* calculation of index for initial approximated Val */ - index = (uint32_t)(in >> 24); - index = (index & INDEX_MASK); - - /* 1.31 with exp 1 */ - out = pRecipTable[index]; - - /* calculation of reciprocal value */ - /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) - { - tempVal = (uint32_t) (((q63_t) in * out) >> 31); - tempVal = 0x7FFFFFFFu - tempVal; - /* 1.31 with exp 1 */ - /* out = (q31_t) (((q63_t) out * tempVal) >> 30); */ - out = clip_q63_to_q31(((q63_t) out * tempVal) >> 30); - } - - /* write output */ - *dst = out; - - /* return num of signbits of out = 1/in value */ - return (signBits + 1u); - } - - - /** - * @brief Function to Calculates 1/in (reciprocal) value of Q15 Data type. - */ - static __INLINE uint32_t arm_recip_q15( - q15_t in, - q15_t * dst, - q15_t * pRecipTable) - { - q15_t out = 0; - uint32_t tempVal = 0; - uint32_t index = 0, i = 0; - uint32_t signBits = 0; - - if(in > 0) - { - signBits = ((uint32_t)(__CLZ( in) - 17)); - } - else - { - signBits = ((uint32_t)(__CLZ(-in) - 17)); - } - - /* Convert input sample to 1.15 format */ - in = (in << signBits); - - /* calculation of index for initial approximated Val */ - index = (uint32_t)(in >> 8); - index = (index & INDEX_MASK); - - /* 1.15 with exp 1 */ - out = pRecipTable[index]; - - /* calculation of reciprocal value */ - /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) - { - tempVal = (uint32_t) (((q31_t) in * out) >> 15); - tempVal = 0x7FFFu - tempVal; - /* 1.15 with exp 1 */ - out = (q15_t) (((q31_t) out * tempVal) >> 14); - /* out = clip_q31_to_q15(((q31_t) out * tempVal) >> 14); */ - } - - /* write output */ - *dst = out; - - /* return num of signbits of out = 1/in value */ - return (signBits + 1); - } - - - /* - * @brief C custom defined intrinisic function for only M0 processors - */ -#if defined(ARM_MATH_CM0_FAMILY) - static __INLINE q31_t __SSAT( - q31_t x, - uint32_t y) - { - int32_t posMax, negMin; - uint32_t i; - - posMax = 1; - for (i = 0; i < (y - 1); i++) - { - posMax = posMax * 2; - } - - if(x > 0) - { - posMax = (posMax - 1); - - if(x > posMax) - { - x = posMax; - } - } - else - { - negMin = -posMax; - - if(x < negMin) - { - x = negMin; - } - } - return (x); - } -#endif /* end of ARM_MATH_CM0_FAMILY */ - - - /* - * @brief C custom defined intrinsic function for M3 and M0 processors - */ -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) - - /* - * @brief C custom defined QADD8 for M3 and M0 processors - */ - static __INLINE uint32_t __QADD8( - uint32_t x, - uint32_t y) - { - q31_t r, s, t, u; - - r = __SSAT(((((q31_t)x << 24) >> 24) + (((q31_t)y << 24) >> 24)), 8) & (int32_t)0x000000FF; - s = __SSAT(((((q31_t)x << 16) >> 24) + (((q31_t)y << 16) >> 24)), 8) & (int32_t)0x000000FF; - t = __SSAT(((((q31_t)x << 8) >> 24) + (((q31_t)y << 8) >> 24)), 8) & (int32_t)0x000000FF; - u = __SSAT(((((q31_t)x ) >> 24) + (((q31_t)y ) >> 24)), 8) & (int32_t)0x000000FF; - - return ((uint32_t)((u << 24) | (t << 16) | (s << 8) | (r ))); - } - - - /* - * @brief C custom defined QSUB8 for M3 and M0 processors - */ - static __INLINE uint32_t __QSUB8( - uint32_t x, - uint32_t y) - { - q31_t r, s, t, u; - - r = __SSAT(((((q31_t)x << 24) >> 24) - (((q31_t)y << 24) >> 24)), 8) & (int32_t)0x000000FF; - s = __SSAT(((((q31_t)x << 16) >> 24) - (((q31_t)y << 16) >> 24)), 8) & (int32_t)0x000000FF; - t = __SSAT(((((q31_t)x << 8) >> 24) - (((q31_t)y << 8) >> 24)), 8) & (int32_t)0x000000FF; - u = __SSAT(((((q31_t)x ) >> 24) - (((q31_t)y ) >> 24)), 8) & (int32_t)0x000000FF; - - return ((uint32_t)((u << 24) | (t << 16) | (s << 8) | (r ))); - } - - - /* - * @brief C custom defined QADD16 for M3 and M0 processors - */ - static __INLINE uint32_t __QADD16( - uint32_t x, - uint32_t y) - { -/* q31_t r, s; without initialisation 'arm_offset_q15 test' fails but 'intrinsic' tests pass! for armCC */ - q31_t r = 0, s = 0; - - r = __SSAT(((((q31_t)x << 16) >> 16) + (((q31_t)y << 16) >> 16)), 16) & (int32_t)0x0000FFFF; - s = __SSAT(((((q31_t)x ) >> 16) + (((q31_t)y ) >> 16)), 16) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined SHADD16 for M3 and M0 processors - */ - static __INLINE uint32_t __SHADD16( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = (((((q31_t)x << 16) >> 16) + (((q31_t)y << 16) >> 16)) >> 1) & (int32_t)0x0000FFFF; - s = (((((q31_t)x ) >> 16) + (((q31_t)y ) >> 16)) >> 1) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined QSUB16 for M3 and M0 processors - */ - static __INLINE uint32_t __QSUB16( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = __SSAT(((((q31_t)x << 16) >> 16) - (((q31_t)y << 16) >> 16)), 16) & (int32_t)0x0000FFFF; - s = __SSAT(((((q31_t)x ) >> 16) - (((q31_t)y ) >> 16)), 16) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined SHSUB16 for M3 and M0 processors - */ - static __INLINE uint32_t __SHSUB16( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = (((((q31_t)x << 16) >> 16) - (((q31_t)y << 16) >> 16)) >> 1) & (int32_t)0x0000FFFF; - s = (((((q31_t)x ) >> 16) - (((q31_t)y ) >> 16)) >> 1) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined QASX for M3 and M0 processors - */ - static __INLINE uint32_t __QASX( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = __SSAT(((((q31_t)x << 16) >> 16) - (((q31_t)y ) >> 16)), 16) & (int32_t)0x0000FFFF; - s = __SSAT(((((q31_t)x ) >> 16) + (((q31_t)y << 16) >> 16)), 16) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined SHASX for M3 and M0 processors - */ - static __INLINE uint32_t __SHASX( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = (((((q31_t)x << 16) >> 16) - (((q31_t)y ) >> 16)) >> 1) & (int32_t)0x0000FFFF; - s = (((((q31_t)x ) >> 16) + (((q31_t)y << 16) >> 16)) >> 1) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined QSAX for M3 and M0 processors - */ - static __INLINE uint32_t __QSAX( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = __SSAT(((((q31_t)x << 16) >> 16) + (((q31_t)y ) >> 16)), 16) & (int32_t)0x0000FFFF; - s = __SSAT(((((q31_t)x ) >> 16) - (((q31_t)y << 16) >> 16)), 16) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined SHSAX for M3 and M0 processors - */ - static __INLINE uint32_t __SHSAX( - uint32_t x, - uint32_t y) - { - q31_t r, s; - - r = (((((q31_t)x << 16) >> 16) + (((q31_t)y ) >> 16)) >> 1) & (int32_t)0x0000FFFF; - s = (((((q31_t)x ) >> 16) - (((q31_t)y << 16) >> 16)) >> 1) & (int32_t)0x0000FFFF; - - return ((uint32_t)((s << 16) | (r ))); - } - - - /* - * @brief C custom defined SMUSDX for M3 and M0 processors - */ - static __INLINE uint32_t __SMUSDX( - uint32_t x, - uint32_t y) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y ) >> 16)) - - ((((q31_t)x ) >> 16) * (((q31_t)y << 16) >> 16)) )); - } - - /* - * @brief C custom defined SMUADX for M3 and M0 processors - */ - static __INLINE uint32_t __SMUADX( - uint32_t x, - uint32_t y) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y ) >> 16)) + - ((((q31_t)x ) >> 16) * (((q31_t)y << 16) >> 16)) )); - } - - - /* - * @brief C custom defined QADD for M3 and M0 processors - */ - static __INLINE int32_t __QADD( - int32_t x, - int32_t y) - { - return ((int32_t)(clip_q63_to_q31((q63_t)x + (q31_t)y))); - } - - - /* - * @brief C custom defined QSUB for M3 and M0 processors - */ - static __INLINE int32_t __QSUB( - int32_t x, - int32_t y) - { - return ((int32_t)(clip_q63_to_q31((q63_t)x - (q31_t)y))); - } - - - /* - * @brief C custom defined SMLAD for M3 and M0 processors - */ - static __INLINE uint32_t __SMLAD( - uint32_t x, - uint32_t y, - uint32_t sum) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y << 16) >> 16)) + - ((((q31_t)x ) >> 16) * (((q31_t)y ) >> 16)) + - ( ((q31_t)sum ) ) )); - } - - - /* - * @brief C custom defined SMLADX for M3 and M0 processors - */ - static __INLINE uint32_t __SMLADX( - uint32_t x, - uint32_t y, - uint32_t sum) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y ) >> 16)) + - ((((q31_t)x ) >> 16) * (((q31_t)y << 16) >> 16)) + - ( ((q31_t)sum ) ) )); - } - - - /* - * @brief C custom defined SMLSDX for M3 and M0 processors - */ - static __INLINE uint32_t __SMLSDX( - uint32_t x, - uint32_t y, - uint32_t sum) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y ) >> 16)) - - ((((q31_t)x ) >> 16) * (((q31_t)y << 16) >> 16)) + - ( ((q31_t)sum ) ) )); - } - - - /* - * @brief C custom defined SMLALD for M3 and M0 processors - */ - static __INLINE uint64_t __SMLALD( - uint32_t x, - uint32_t y, - uint64_t sum) - { -/* return (sum + ((q15_t) (x >> 16) * (q15_t) (y >> 16)) + ((q15_t) x * (q15_t) y)); */ - return ((uint64_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y << 16) >> 16)) + - ((((q31_t)x ) >> 16) * (((q31_t)y ) >> 16)) + - ( ((q63_t)sum ) ) )); - } - - - /* - * @brief C custom defined SMLALDX for M3 and M0 processors - */ - static __INLINE uint64_t __SMLALDX( - uint32_t x, - uint32_t y, - uint64_t sum) - { -/* return (sum + ((q15_t) (x >> 16) * (q15_t) y)) + ((q15_t) x * (q15_t) (y >> 16)); */ - return ((uint64_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y ) >> 16)) + - ((((q31_t)x ) >> 16) * (((q31_t)y << 16) >> 16)) + - ( ((q63_t)sum ) ) )); - } - - - /* - * @brief C custom defined SMUAD for M3 and M0 processors - */ - static __INLINE uint32_t __SMUAD( - uint32_t x, - uint32_t y) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y << 16) >> 16)) + - ((((q31_t)x ) >> 16) * (((q31_t)y ) >> 16)) )); - } - - - /* - * @brief C custom defined SMUSD for M3 and M0 processors - */ - static __INLINE uint32_t __SMUSD( - uint32_t x, - uint32_t y) - { - return ((uint32_t)(((((q31_t)x << 16) >> 16) * (((q31_t)y << 16) >> 16)) - - ((((q31_t)x ) >> 16) * (((q31_t)y ) >> 16)) )); - } - - - /* - * @brief C custom defined SXTB16 for M3 and M0 processors - */ - static __INLINE uint32_t __SXTB16( - uint32_t x) - { - return ((uint32_t)(((((q31_t)x << 24) >> 24) & (q31_t)0x0000FFFF) | - ((((q31_t)x << 8) >> 8) & (q31_t)0xFFFF0000) )); - } - -#endif /* defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) */ - - - /** - * @brief Instance structure for the Q7 FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - q7_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q7_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - } arm_fir_instance_q7; - - /** - * @brief Instance structure for the Q15 FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - } arm_fir_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - } arm_fir_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - } arm_fir_instance_f32; - - - /** - * @brief Processing function for the Q7 FIR filter. - * @param[in] S points to an instance of the Q7 FIR filter structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_q7( - const arm_fir_instance_q7 * S, - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q7 FIR filter. - * @param[in,out] S points to an instance of the Q7 FIR structure. - * @param[in] numTaps Number of filter coefficients in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of samples that are processed. - */ - void arm_fir_init_q7( - arm_fir_instance_q7 * S, - uint16_t numTaps, - q7_t * pCoeffs, - q7_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 FIR filter. - * @param[in] S points to an instance of the Q15 FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_q15( - const arm_fir_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the fast Q15 FIR filter for Cortex-M3 and Cortex-M4. - * @param[in] S points to an instance of the Q15 FIR filter structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_fast_q15( - const arm_fir_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 FIR filter. - * @param[in,out] S points to an instance of the Q15 FIR filter structure. - * @param[in] numTaps Number of filter coefficients in the filter. Must be even and greater than or equal to 4. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of samples that are processed at a time. - * @return The function returns ARM_MATH_SUCCESS if initialization was successful or ARM_MATH_ARGUMENT_ERROR if - * numTaps is not a supported value. - */ - arm_status arm_fir_init_q15( - arm_fir_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 FIR filter. - * @param[in] S points to an instance of the Q31 FIR filter structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_q31( - const arm_fir_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the fast Q31 FIR filter for Cortex-M3 and Cortex-M4. - * @param[in] S points to an instance of the Q31 FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_fast_q31( - const arm_fir_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 FIR filter. - * @param[in,out] S points to an instance of the Q31 FIR structure. - * @param[in] numTaps Number of filter coefficients in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of samples that are processed at a time. - */ - void arm_fir_init_q31( - arm_fir_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the floating-point FIR filter. - * @param[in] S points to an instance of the floating-point FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_f32( - const arm_fir_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point FIR filter. - * @param[in,out] S points to an instance of the floating-point FIR filter structure. - * @param[in] numTaps Number of filter coefficients in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of samples that are processed at a time. - */ - void arm_fir_init_f32( - arm_fir_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q15 Biquad cascade filter. - */ - typedef struct - { - int8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - q15_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */ - q15_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages. */ - int8_t postShift; /**< Additional shift, in bits, applied to each output sample. */ - } arm_biquad_casd_df1_inst_q15; - - /** - * @brief Instance structure for the Q31 Biquad cascade filter. - */ - typedef struct - { - uint32_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - q31_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */ - q31_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages. */ - uint8_t postShift; /**< Additional shift, in bits, applied to each output sample. */ - } arm_biquad_casd_df1_inst_q31; - - /** - * @brief Instance structure for the floating-point Biquad cascade filter. - */ - typedef struct - { - uint32_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float32_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */ - float32_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_casd_df1_inst_f32; - - - /** - * @brief Processing function for the Q15 Biquad cascade filter. - * @param[in] S points to an instance of the Q15 Biquad cascade structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df1_q15( - const arm_biquad_casd_df1_inst_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 Biquad cascade filter. - * @param[in,out] S points to an instance of the Q15 Biquad cascade structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] postShift Shift to be applied to the output. Varies according to the coefficients format - */ - void arm_biquad_cascade_df1_init_q15( - arm_biquad_casd_df1_inst_q15 * S, - uint8_t numStages, - q15_t * pCoeffs, - q15_t * pState, - int8_t postShift); - - - /** - * @brief Fast but less precise processing function for the Q15 Biquad cascade filter for Cortex-M3 and Cortex-M4. - * @param[in] S points to an instance of the Q15 Biquad cascade structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df1_fast_q15( - const arm_biquad_casd_df1_inst_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 Biquad cascade filter - * @param[in] S points to an instance of the Q31 Biquad cascade structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df1_q31( - const arm_biquad_casd_df1_inst_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Fast but less precise processing function for the Q31 Biquad cascade filter for Cortex-M3 and Cortex-M4. - * @param[in] S points to an instance of the Q31 Biquad cascade structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df1_fast_q31( - const arm_biquad_casd_df1_inst_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 Biquad cascade filter. - * @param[in,out] S points to an instance of the Q31 Biquad cascade structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] postShift Shift to be applied to the output. Varies according to the coefficients format - */ - void arm_biquad_cascade_df1_init_q31( - arm_biquad_casd_df1_inst_q31 * S, - uint8_t numStages, - q31_t * pCoeffs, - q31_t * pState, - int8_t postShift); - - - /** - * @brief Processing function for the floating-point Biquad cascade filter. - * @param[in] S points to an instance of the floating-point Biquad cascade structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df1_f32( - const arm_biquad_casd_df1_inst_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point Biquad cascade filter. - * @param[in,out] S points to an instance of the floating-point Biquad cascade structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - */ - void arm_biquad_cascade_df1_init_f32( - arm_biquad_casd_df1_inst_f32 * S, - uint8_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Instance structure for the floating-point matrix structure. - */ - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - float32_t *pData; /**< points to the data of the matrix. */ - } arm_matrix_instance_f32; - - - /** - * @brief Instance structure for the floating-point matrix structure. - */ - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - float64_t *pData; /**< points to the data of the matrix. */ - } arm_matrix_instance_f64; - - /** - * @brief Instance structure for the Q15 matrix structure. - */ - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - q15_t *pData; /**< points to the data of the matrix. */ - } arm_matrix_instance_q15; - - /** - * @brief Instance structure for the Q31 matrix structure. - */ - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - q31_t *pData; /**< points to the data of the matrix. */ - } arm_matrix_instance_q31; - - - /** - * @brief Floating-point matrix addition. - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_add_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15 matrix addition. - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_add_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst); - - - /** - * @brief Q31 matrix addition. - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_add_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point, complex, matrix multiplication. - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_cmplx_mult_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15, complex, matrix multiplication. - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_cmplx_mult_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst, - q15_t * pScratch); - - - /** - * @brief Q31, complex, matrix multiplication. - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_cmplx_mult_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix transpose. - * @param[in] pSrc points to the input matrix - * @param[out] pDst points to the output matrix - * @return The function returns either ARM_MATH_SIZE_MISMATCH - * or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_trans_f32( - const arm_matrix_instance_f32 * pSrc, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15 matrix transpose. - * @param[in] pSrc points to the input matrix - * @param[out] pDst points to the output matrix - * @return The function returns either ARM_MATH_SIZE_MISMATCH - * or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_trans_q15( - const arm_matrix_instance_q15 * pSrc, - arm_matrix_instance_q15 * pDst); - - - /** - * @brief Q31 matrix transpose. - * @param[in] pSrc points to the input matrix - * @param[out] pDst points to the output matrix - * @return The function returns either ARM_MATH_SIZE_MISMATCH - * or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_trans_q31( - const arm_matrix_instance_q31 * pSrc, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix multiplication - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_mult_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15 matrix multiplication - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @param[in] pState points to the array for storing intermediate results - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_mult_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst, - q15_t * pState); - - - /** - * @brief Q15 matrix multiplication (fast variant) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @param[in] pState points to the array for storing intermediate results - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_mult_fast_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst, - q15_t * pState); - - - /** - * @brief Q31 matrix multiplication - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_mult_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Q31 matrix multiplication (fast variant) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_mult_fast_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix subtraction - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_sub_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15 matrix subtraction - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_sub_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst); - - - /** - * @brief Q31 matrix subtraction - * @param[in] pSrcA points to the first input matrix structure - * @param[in] pSrcB points to the second input matrix structure - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_sub_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix scaling. - * @param[in] pSrc points to the input matrix - * @param[in] scale scale factor - * @param[out] pDst points to the output matrix - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_scale_f32( - const arm_matrix_instance_f32 * pSrc, - float32_t scale, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15 matrix scaling. - * @param[in] pSrc points to input matrix - * @param[in] scaleFract fractional portion of the scale factor - * @param[in] shift number of bits to shift the result by - * @param[out] pDst points to output matrix - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_scale_q15( - const arm_matrix_instance_q15 * pSrc, - q15_t scaleFract, - int32_t shift, - arm_matrix_instance_q15 * pDst); - - - /** - * @brief Q31 matrix scaling. - * @param[in] pSrc points to input matrix - * @param[in] scaleFract fractional portion of the scale factor - * @param[in] shift number of bits to shift the result by - * @param[out] pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - arm_status arm_mat_scale_q31( - const arm_matrix_instance_q31 * pSrc, - q31_t scaleFract, - int32_t shift, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Q31 matrix initialization. - * @param[in,out] S points to an instance of the floating-point matrix structure. - * @param[in] nRows number of rows in the matrix. - * @param[in] nColumns number of columns in the matrix. - * @param[in] pData points to the matrix data array. - */ - void arm_mat_init_q31( - arm_matrix_instance_q31 * S, - uint16_t nRows, - uint16_t nColumns, - q31_t * pData); - - - /** - * @brief Q15 matrix initialization. - * @param[in,out] S points to an instance of the floating-point matrix structure. - * @param[in] nRows number of rows in the matrix. - * @param[in] nColumns number of columns in the matrix. - * @param[in] pData points to the matrix data array. - */ - void arm_mat_init_q15( - arm_matrix_instance_q15 * S, - uint16_t nRows, - uint16_t nColumns, - q15_t * pData); - - - /** - * @brief Floating-point matrix initialization. - * @param[in,out] S points to an instance of the floating-point matrix structure. - * @param[in] nRows number of rows in the matrix. - * @param[in] nColumns number of columns in the matrix. - * @param[in] pData points to the matrix data array. - */ - void arm_mat_init_f32( - arm_matrix_instance_f32 * S, - uint16_t nRows, - uint16_t nColumns, - float32_t * pData); - - - - /** - * @brief Instance structure for the Q15 PID Control. - */ - typedef struct - { - q15_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ -#ifdef ARM_MATH_CM0_FAMILY - q15_t A1; - q15_t A2; -#else - q31_t A1; /**< The derived gain A1 = -Kp - 2Kd | Kd.*/ -#endif - q15_t state[3]; /**< The state array of length 3. */ - q15_t Kp; /**< The proportional gain. */ - q15_t Ki; /**< The integral gain. */ - q15_t Kd; /**< The derivative gain. */ - } arm_pid_instance_q15; - - /** - * @brief Instance structure for the Q31 PID Control. - */ - typedef struct - { - q31_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ - q31_t A1; /**< The derived gain, A1 = -Kp - 2Kd. */ - q31_t A2; /**< The derived gain, A2 = Kd . */ - q31_t state[3]; /**< The state array of length 3. */ - q31_t Kp; /**< The proportional gain. */ - q31_t Ki; /**< The integral gain. */ - q31_t Kd; /**< The derivative gain. */ - } arm_pid_instance_q31; - - /** - * @brief Instance structure for the floating-point PID Control. - */ - typedef struct - { - float32_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ - float32_t A1; /**< The derived gain, A1 = -Kp - 2Kd. */ - float32_t A2; /**< The derived gain, A2 = Kd . */ - float32_t state[3]; /**< The state array of length 3. */ - float32_t Kp; /**< The proportional gain. */ - float32_t Ki; /**< The integral gain. */ - float32_t Kd; /**< The derivative gain. */ - } arm_pid_instance_f32; - - - - /** - * @brief Initialization function for the floating-point PID Control. - * @param[in,out] S points to an instance of the PID structure. - * @param[in] resetStateFlag flag to reset the state. 0 = no change in state 1 = reset the state. - */ - void arm_pid_init_f32( - arm_pid_instance_f32 * S, - int32_t resetStateFlag); - - - /** - * @brief Reset function for the floating-point PID Control. - * @param[in,out] S is an instance of the floating-point PID Control structure - */ - void arm_pid_reset_f32( - arm_pid_instance_f32 * S); - - - /** - * @brief Initialization function for the Q31 PID Control. - * @param[in,out] S points to an instance of the Q15 PID structure. - * @param[in] resetStateFlag flag to reset the state. 0 = no change in state 1 = reset the state. - */ - void arm_pid_init_q31( - arm_pid_instance_q31 * S, - int32_t resetStateFlag); - - - /** - * @brief Reset function for the Q31 PID Control. - * @param[in,out] S points to an instance of the Q31 PID Control structure - */ - - void arm_pid_reset_q31( - arm_pid_instance_q31 * S); - - - /** - * @brief Initialization function for the Q15 PID Control. - * @param[in,out] S points to an instance of the Q15 PID structure. - * @param[in] resetStateFlag flag to reset the state. 0 = no change in state 1 = reset the state. - */ - void arm_pid_init_q15( - arm_pid_instance_q15 * S, - int32_t resetStateFlag); - - - /** - * @brief Reset function for the Q15 PID Control. - * @param[in,out] S points to an instance of the q15 PID Control structure - */ - void arm_pid_reset_q15( - arm_pid_instance_q15 * S); - - - /** - * @brief Instance structure for the floating-point Linear Interpolate function. - */ - typedef struct - { - uint32_t nValues; /**< nValues */ - float32_t x1; /**< x1 */ - float32_t xSpacing; /**< xSpacing */ - float32_t *pYData; /**< pointer to the table of Y values */ - } arm_linear_interp_instance_f32; - - /** - * @brief Instance structure for the floating-point bilinear interpolation function. - */ - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - float32_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_f32; - - /** - * @brief Instance structure for the Q31 bilinear interpolation function. - */ - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - q31_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_q31; - - /** - * @brief Instance structure for the Q15 bilinear interpolation function. - */ - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - q15_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_q15; - - /** - * @brief Instance structure for the Q15 bilinear interpolation function. - */ - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - q7_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_q7; - - - /** - * @brief Q7 vector multiplication. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_mult_q7( - q7_t * pSrcA, - q7_t * pSrcB, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q15 vector multiplication. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_mult_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q31 vector multiplication. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_mult_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Floating-point vector multiplication. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_mult_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q15 CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q15_t *pTwiddle; /**< points to the Sin twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix2_instance_q15; - -/* Deprecated */ - arm_status arm_cfft_radix2_init_q15( - arm_cfft_radix2_instance_q15 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix2_q15( - const arm_cfft_radix2_instance_q15 * S, - q15_t * pSrc); - - - /** - * @brief Instance structure for the Q15 CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q15_t *pTwiddle; /**< points to the twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix4_instance_q15; - -/* Deprecated */ - arm_status arm_cfft_radix4_init_q15( - arm_cfft_radix4_instance_q15 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix4_q15( - const arm_cfft_radix4_instance_q15 * S, - q15_t * pSrc); - - /** - * @brief Instance structure for the Radix-2 Q31 CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q31_t *pTwiddle; /**< points to the Twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix2_instance_q31; - -/* Deprecated */ - arm_status arm_cfft_radix2_init_q31( - arm_cfft_radix2_instance_q31 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix2_q31( - const arm_cfft_radix2_instance_q31 * S, - q31_t * pSrc); - - /** - * @brief Instance structure for the Q31 CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q31_t *pTwiddle; /**< points to the twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix4_instance_q31; - -/* Deprecated */ - void arm_cfft_radix4_q31( - const arm_cfft_radix4_instance_q31 * S, - q31_t * pSrc); - -/* Deprecated */ - arm_status arm_cfft_radix4_init_q31( - arm_cfft_radix4_instance_q31 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the floating-point CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - float32_t *pTwiddle; /**< points to the Twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - float32_t onebyfftLen; /**< value of 1/fftLen. */ - } arm_cfft_radix2_instance_f32; - -/* Deprecated */ - arm_status arm_cfft_radix2_init_f32( - arm_cfft_radix2_instance_f32 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix2_f32( - const arm_cfft_radix2_instance_f32 * S, - float32_t * pSrc); - - /** - * @brief Instance structure for the floating-point CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - float32_t *pTwiddle; /**< points to the Twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - float32_t onebyfftLen; /**< value of 1/fftLen. */ - } arm_cfft_radix4_instance_f32; - -/* Deprecated */ - arm_status arm_cfft_radix4_init_f32( - arm_cfft_radix4_instance_f32 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix4_f32( - const arm_cfft_radix4_instance_f32 * S, - float32_t * pSrc); - - /** - * @brief Instance structure for the fixed-point CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - const q15_t *pTwiddle; /**< points to the Twiddle factor table. */ - const uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t bitRevLength; /**< bit reversal table length. */ - } arm_cfft_instance_q15; - -void arm_cfft_q15( - const arm_cfft_instance_q15 * S, - q15_t * p1, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the fixed-point CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - const q31_t *pTwiddle; /**< points to the Twiddle factor table. */ - const uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t bitRevLength; /**< bit reversal table length. */ - } arm_cfft_instance_q31; - -void arm_cfft_q31( - const arm_cfft_instance_q31 * S, - q31_t * p1, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the floating-point CFFT/CIFFT function. - */ - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - const float32_t *pTwiddle; /**< points to the Twiddle factor table. */ - const uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t bitRevLength; /**< bit reversal table length. */ - } arm_cfft_instance_f32; - - void arm_cfft_f32( - const arm_cfft_instance_f32 * S, - float32_t * p1, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the Q15 RFFT/RIFFT function. - */ - typedef struct - { - uint32_t fftLenReal; /**< length of the real FFT. */ - uint8_t ifftFlagR; /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */ - uint8_t bitReverseFlagR; /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */ - uint32_t twidCoefRModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - q15_t *pTwiddleAReal; /**< points to the real twiddle factor table. */ - q15_t *pTwiddleBReal; /**< points to the imag twiddle factor table. */ - const arm_cfft_instance_q15 *pCfft; /**< points to the complex FFT instance. */ - } arm_rfft_instance_q15; - - arm_status arm_rfft_init_q15( - arm_rfft_instance_q15 * S, - uint32_t fftLenReal, - uint32_t ifftFlagR, - uint32_t bitReverseFlag); - - void arm_rfft_q15( - const arm_rfft_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst); - - /** - * @brief Instance structure for the Q31 RFFT/RIFFT function. - */ - typedef struct - { - uint32_t fftLenReal; /**< length of the real FFT. */ - uint8_t ifftFlagR; /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */ - uint8_t bitReverseFlagR; /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */ - uint32_t twidCoefRModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - q31_t *pTwiddleAReal; /**< points to the real twiddle factor table. */ - q31_t *pTwiddleBReal; /**< points to the imag twiddle factor table. */ - const arm_cfft_instance_q31 *pCfft; /**< points to the complex FFT instance. */ - } arm_rfft_instance_q31; - - arm_status arm_rfft_init_q31( - arm_rfft_instance_q31 * S, - uint32_t fftLenReal, - uint32_t ifftFlagR, - uint32_t bitReverseFlag); - - void arm_rfft_q31( - const arm_rfft_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst); - - /** - * @brief Instance structure for the floating-point RFFT/RIFFT function. - */ - typedef struct - { - uint32_t fftLenReal; /**< length of the real FFT. */ - uint16_t fftLenBy2; /**< length of the complex FFT. */ - uint8_t ifftFlagR; /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */ - uint8_t bitReverseFlagR; /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */ - uint32_t twidCoefRModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - float32_t *pTwiddleAReal; /**< points to the real twiddle factor table. */ - float32_t *pTwiddleBReal; /**< points to the imag twiddle factor table. */ - arm_cfft_radix4_instance_f32 *pCfft; /**< points to the complex FFT instance. */ - } arm_rfft_instance_f32; - - arm_status arm_rfft_init_f32( - arm_rfft_instance_f32 * S, - arm_cfft_radix4_instance_f32 * S_CFFT, - uint32_t fftLenReal, - uint32_t ifftFlagR, - uint32_t bitReverseFlag); - - void arm_rfft_f32( - const arm_rfft_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst); - - /** - * @brief Instance structure for the floating-point RFFT/RIFFT function. - */ -typedef struct - { - arm_cfft_instance_f32 Sint; /**< Internal CFFT structure. */ - uint16_t fftLenRFFT; /**< length of the real sequence */ - float32_t * pTwiddleRFFT; /**< Twiddle factors real stage */ - } arm_rfft_fast_instance_f32 ; - -arm_status arm_rfft_fast_init_f32 ( - arm_rfft_fast_instance_f32 * S, - uint16_t fftLen); - -void arm_rfft_fast_f32( - arm_rfft_fast_instance_f32 * S, - float32_t * p, float32_t * pOut, - uint8_t ifftFlag); - - /** - * @brief Instance structure for the floating-point DCT4/IDCT4 function. - */ - typedef struct - { - uint16_t N; /**< length of the DCT4. */ - uint16_t Nby2; /**< half of the length of the DCT4. */ - float32_t normalize; /**< normalizing factor. */ - float32_t *pTwiddle; /**< points to the twiddle factor table. */ - float32_t *pCosFactor; /**< points to the cosFactor table. */ - arm_rfft_instance_f32 *pRfft; /**< points to the real FFT instance. */ - arm_cfft_radix4_instance_f32 *pCfft; /**< points to the complex FFT instance. */ - } arm_dct4_instance_f32; - - - /** - * @brief Initialization function for the floating-point DCT4/IDCT4. - * @param[in,out] S points to an instance of floating-point DCT4/IDCT4 structure. - * @param[in] S_RFFT points to an instance of floating-point RFFT/RIFFT structure. - * @param[in] S_CFFT points to an instance of floating-point CFFT/CIFFT structure. - * @param[in] N length of the DCT4. - * @param[in] Nby2 half of the length of the DCT4. - * @param[in] normalize normalizing factor. - * @return arm_status function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if fftLenReal is not a supported transform length. - */ - arm_status arm_dct4_init_f32( - arm_dct4_instance_f32 * S, - arm_rfft_instance_f32 * S_RFFT, - arm_cfft_radix4_instance_f32 * S_CFFT, - uint16_t N, - uint16_t Nby2, - float32_t normalize); - - - /** - * @brief Processing function for the floating-point DCT4/IDCT4. - * @param[in] S points to an instance of the floating-point DCT4/IDCT4 structure. - * @param[in] pState points to state buffer. - * @param[in,out] pInlineBuffer points to the in-place input and output buffer. - */ - void arm_dct4_f32( - const arm_dct4_instance_f32 * S, - float32_t * pState, - float32_t * pInlineBuffer); - - - /** - * @brief Instance structure for the Q31 DCT4/IDCT4 function. - */ - typedef struct - { - uint16_t N; /**< length of the DCT4. */ - uint16_t Nby2; /**< half of the length of the DCT4. */ - q31_t normalize; /**< normalizing factor. */ - q31_t *pTwiddle; /**< points to the twiddle factor table. */ - q31_t *pCosFactor; /**< points to the cosFactor table. */ - arm_rfft_instance_q31 *pRfft; /**< points to the real FFT instance. */ - arm_cfft_radix4_instance_q31 *pCfft; /**< points to the complex FFT instance. */ - } arm_dct4_instance_q31; - - - /** - * @brief Initialization function for the Q31 DCT4/IDCT4. - * @param[in,out] S points to an instance of Q31 DCT4/IDCT4 structure. - * @param[in] S_RFFT points to an instance of Q31 RFFT/RIFFT structure - * @param[in] S_CFFT points to an instance of Q31 CFFT/CIFFT structure - * @param[in] N length of the DCT4. - * @param[in] Nby2 half of the length of the DCT4. - * @param[in] normalize normalizing factor. - * @return arm_status function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if N is not a supported transform length. - */ - arm_status arm_dct4_init_q31( - arm_dct4_instance_q31 * S, - arm_rfft_instance_q31 * S_RFFT, - arm_cfft_radix4_instance_q31 * S_CFFT, - uint16_t N, - uint16_t Nby2, - q31_t normalize); - - - /** - * @brief Processing function for the Q31 DCT4/IDCT4. - * @param[in] S points to an instance of the Q31 DCT4 structure. - * @param[in] pState points to state buffer. - * @param[in,out] pInlineBuffer points to the in-place input and output buffer. - */ - void arm_dct4_q31( - const arm_dct4_instance_q31 * S, - q31_t * pState, - q31_t * pInlineBuffer); - - - /** - * @brief Instance structure for the Q15 DCT4/IDCT4 function. - */ - typedef struct - { - uint16_t N; /**< length of the DCT4. */ - uint16_t Nby2; /**< half of the length of the DCT4. */ - q15_t normalize; /**< normalizing factor. */ - q15_t *pTwiddle; /**< points to the twiddle factor table. */ - q15_t *pCosFactor; /**< points to the cosFactor table. */ - arm_rfft_instance_q15 *pRfft; /**< points to the real FFT instance. */ - arm_cfft_radix4_instance_q15 *pCfft; /**< points to the complex FFT instance. */ - } arm_dct4_instance_q15; - - - /** - * @brief Initialization function for the Q15 DCT4/IDCT4. - * @param[in,out] S points to an instance of Q15 DCT4/IDCT4 structure. - * @param[in] S_RFFT points to an instance of Q15 RFFT/RIFFT structure. - * @param[in] S_CFFT points to an instance of Q15 CFFT/CIFFT structure. - * @param[in] N length of the DCT4. - * @param[in] Nby2 half of the length of the DCT4. - * @param[in] normalize normalizing factor. - * @return arm_status function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if N is not a supported transform length. - */ - arm_status arm_dct4_init_q15( - arm_dct4_instance_q15 * S, - arm_rfft_instance_q15 * S_RFFT, - arm_cfft_radix4_instance_q15 * S_CFFT, - uint16_t N, - uint16_t Nby2, - q15_t normalize); - - - /** - * @brief Processing function for the Q15 DCT4/IDCT4. - * @param[in] S points to an instance of the Q15 DCT4 structure. - * @param[in] pState points to state buffer. - * @param[in,out] pInlineBuffer points to the in-place input and output buffer. - */ - void arm_dct4_q15( - const arm_dct4_instance_q15 * S, - q15_t * pState, - q15_t * pInlineBuffer); - - - /** - * @brief Floating-point vector addition. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_add_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q7 vector addition. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_add_q7( - q7_t * pSrcA, - q7_t * pSrcB, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q15 vector addition. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_add_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q31 vector addition. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_add_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Floating-point vector subtraction. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_sub_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q7 vector subtraction. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_sub_q7( - q7_t * pSrcA, - q7_t * pSrcB, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q15 vector subtraction. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_sub_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q31 vector subtraction. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in each vector - */ - void arm_sub_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Multiplies a floating-point vector by a scalar. - * @param[in] pSrc points to the input vector - * @param[in] scale scale factor to be applied - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_scale_f32( - float32_t * pSrc, - float32_t scale, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Multiplies a Q7 vector by a scalar. - * @param[in] pSrc points to the input vector - * @param[in] scaleFract fractional portion of the scale value - * @param[in] shift number of bits to shift the result by - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_scale_q7( - q7_t * pSrc, - q7_t scaleFract, - int8_t shift, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Multiplies a Q15 vector by a scalar. - * @param[in] pSrc points to the input vector - * @param[in] scaleFract fractional portion of the scale value - * @param[in] shift number of bits to shift the result by - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_scale_q15( - q15_t * pSrc, - q15_t scaleFract, - int8_t shift, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Multiplies a Q31 vector by a scalar. - * @param[in] pSrc points to the input vector - * @param[in] scaleFract fractional portion of the scale value - * @param[in] shift number of bits to shift the result by - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_scale_q31( - q31_t * pSrc, - q31_t scaleFract, - int8_t shift, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q7 vector absolute value. - * @param[in] pSrc points to the input buffer - * @param[out] pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - */ - void arm_abs_q7( - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Floating-point vector absolute value. - * @param[in] pSrc points to the input buffer - * @param[out] pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - */ - void arm_abs_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q15 vector absolute value. - * @param[in] pSrc points to the input buffer - * @param[out] pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - */ - void arm_abs_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Q31 vector absolute value. - * @param[in] pSrc points to the input buffer - * @param[out] pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - */ - void arm_abs_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Dot product of floating-point vectors. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] result output result returned here - */ - void arm_dot_prod_f32( - float32_t * pSrcA, - float32_t * pSrcB, - uint32_t blockSize, - float32_t * result); - - - /** - * @brief Dot product of Q7 vectors. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] result output result returned here - */ - void arm_dot_prod_q7( - q7_t * pSrcA, - q7_t * pSrcB, - uint32_t blockSize, - q31_t * result); - - - /** - * @brief Dot product of Q15 vectors. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] result output result returned here - */ - void arm_dot_prod_q15( - q15_t * pSrcA, - q15_t * pSrcB, - uint32_t blockSize, - q63_t * result); - - - /** - * @brief Dot product of Q31 vectors. - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] result output result returned here - */ - void arm_dot_prod_q31( - q31_t * pSrcA, - q31_t * pSrcB, - uint32_t blockSize, - q63_t * result); - - - /** - * @brief Shifts the elements of a Q7 vector a specified number of bits. - * @param[in] pSrc points to the input vector - * @param[in] shiftBits number of bits to shift. A positive value shifts left; a negative value shifts right. - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_shift_q7( - q7_t * pSrc, - int8_t shiftBits, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Shifts the elements of a Q15 vector a specified number of bits. - * @param[in] pSrc points to the input vector - * @param[in] shiftBits number of bits to shift. A positive value shifts left; a negative value shifts right. - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_shift_q15( - q15_t * pSrc, - int8_t shiftBits, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Shifts the elements of a Q31 vector a specified number of bits. - * @param[in] pSrc points to the input vector - * @param[in] shiftBits number of bits to shift. A positive value shifts left; a negative value shifts right. - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_shift_q31( - q31_t * pSrc, - int8_t shiftBits, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Adds a constant offset to a floating-point vector. - * @param[in] pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_offset_f32( - float32_t * pSrc, - float32_t offset, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Adds a constant offset to a Q7 vector. - * @param[in] pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_offset_q7( - q7_t * pSrc, - q7_t offset, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Adds a constant offset to a Q15 vector. - * @param[in] pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_offset_q15( - q15_t * pSrc, - q15_t offset, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Adds a constant offset to a Q31 vector. - * @param[in] pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_offset_q31( - q31_t * pSrc, - q31_t offset, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Negates the elements of a floating-point vector. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_negate_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Negates the elements of a Q7 vector. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_negate_q7( - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Negates the elements of a Q15 vector. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_negate_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Negates the elements of a Q31 vector. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] blockSize number of samples in the vector - */ - void arm_negate_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Copies the elements of a floating-point vector. - * @param[in] pSrc input pointer - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_copy_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Copies the elements of a Q7 vector. - * @param[in] pSrc input pointer - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_copy_q7( - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Copies the elements of a Q15 vector. - * @param[in] pSrc input pointer - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_copy_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Copies the elements of a Q31 vector. - * @param[in] pSrc input pointer - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_copy_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Fills a constant value into a floating-point vector. - * @param[in] value input value to be filled - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_fill_f32( - float32_t value, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Fills a constant value into a Q7 vector. - * @param[in] value input value to be filled - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_fill_q7( - q7_t value, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Fills a constant value into a Q15 vector. - * @param[in] value input value to be filled - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_fill_q15( - q15_t value, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Fills a constant value into a Q31 vector. - * @param[in] value input value to be filled - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_fill_q31( - q31_t value, - q31_t * pDst, - uint32_t blockSize); - - -/** - * @brief Convolution of floating-point sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the location where the output result is written. Length srcALen+srcBLen-1. - */ - void arm_conv_f32( - float32_t * pSrcA, - uint32_t srcALen, - float32_t * pSrcB, - uint32_t srcBLen, - float32_t * pDst); - - - /** - * @brief Convolution of Q15 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - * @param[in] pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - */ - void arm_conv_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - -/** - * @brief Convolution of Q15 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the location where the output result is written. Length srcALen+srcBLen-1. - */ - void arm_conv_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - - /** - * @brief Convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - */ - void arm_conv_fast_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - - /** - * @brief Convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - * @param[in] pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - */ - void arm_conv_fast_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Convolution of Q31 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - */ - void arm_conv_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - - /** - * @brief Convolution of Q31 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - */ - void arm_conv_fast_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - - /** - * @brief Convolution of Q7 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - * @param[in] pScratch1 points to scratch buffer(of type q15_t) of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer (of type q15_t) of size min(srcALen, srcBLen). - */ - void arm_conv_opt_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Convolution of Q7 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length srcALen+srcBLen-1. - */ - void arm_conv_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst); - - - /** - * @brief Partial convolution of floating-point sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_f32( - float32_t * pSrcA, - uint32_t srcALen, - float32_t * pSrcB, - uint32_t srcBLen, - float32_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q15 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @param[in] pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Partial convolution of Q15 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_fast_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @param[in] pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_fast_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Partial convolution of Q31 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q31 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_fast_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q7 sequences - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @param[in] pScratch1 points to scratch buffer(of type q15_t) of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer (of type q15_t) of size min(srcALen, srcBLen). - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_opt_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - uint32_t firstIndex, - uint32_t numPoints, - q15_t * pScratch1, - q15_t * pScratch2); - - -/** - * @brief Partial convolution of Q7 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - arm_status arm_conv_partial_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Instance structure for the Q15 FIR decimator. - */ - typedef struct - { - uint8_t M; /**< decimation factor. */ - uint16_t numTaps; /**< number of coefficients in the filter. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - } arm_fir_decimate_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR decimator. - */ - typedef struct - { - uint8_t M; /**< decimation factor. */ - uint16_t numTaps; /**< number of coefficients in the filter. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - } arm_fir_decimate_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR decimator. - */ - typedef struct - { - uint8_t M; /**< decimation factor. */ - uint16_t numTaps; /**< number of coefficients in the filter. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - } arm_fir_decimate_instance_f32; - - - /** - * @brief Processing function for the floating-point FIR decimator. - * @param[in] S points to an instance of the floating-point FIR decimator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_decimate_f32( - const arm_fir_decimate_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point FIR decimator. - * @param[in,out] S points to an instance of the floating-point FIR decimator structure. - * @param[in] numTaps number of coefficients in the filter. - * @param[in] M decimation factor. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * blockSize is not a multiple of M. - */ - arm_status arm_fir_decimate_init_f32( - arm_fir_decimate_instance_f32 * S, - uint16_t numTaps, - uint8_t M, - float32_t * pCoeffs, - float32_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 FIR decimator. - * @param[in] S points to an instance of the Q15 FIR decimator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_decimate_q15( - const arm_fir_decimate_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 FIR decimator (fast variant) for Cortex-M3 and Cortex-M4. - * @param[in] S points to an instance of the Q15 FIR decimator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_decimate_fast_q15( - const arm_fir_decimate_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 FIR decimator. - * @param[in,out] S points to an instance of the Q15 FIR decimator structure. - * @param[in] numTaps number of coefficients in the filter. - * @param[in] M decimation factor. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * blockSize is not a multiple of M. - */ - arm_status arm_fir_decimate_init_q15( - arm_fir_decimate_instance_q15 * S, - uint16_t numTaps, - uint8_t M, - q15_t * pCoeffs, - q15_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 FIR decimator. - * @param[in] S points to an instance of the Q31 FIR decimator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_decimate_q31( - const arm_fir_decimate_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Processing function for the Q31 FIR decimator (fast variant) for Cortex-M3 and Cortex-M4. - * @param[in] S points to an instance of the Q31 FIR decimator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_decimate_fast_q31( - arm_fir_decimate_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 FIR decimator. - * @param[in,out] S points to an instance of the Q31 FIR decimator structure. - * @param[in] numTaps number of coefficients in the filter. - * @param[in] M decimation factor. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * blockSize is not a multiple of M. - */ - arm_status arm_fir_decimate_init_q31( - arm_fir_decimate_instance_q31 * S, - uint16_t numTaps, - uint8_t M, - q31_t * pCoeffs, - q31_t * pState, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q15 FIR interpolator. - */ - typedef struct - { - uint8_t L; /**< upsample factor. */ - uint16_t phaseLength; /**< length of each polyphase filter component. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length L*phaseLength. */ - q15_t *pState; /**< points to the state variable array. The array is of length blockSize+phaseLength-1. */ - } arm_fir_interpolate_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR interpolator. - */ - typedef struct - { - uint8_t L; /**< upsample factor. */ - uint16_t phaseLength; /**< length of each polyphase filter component. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length L*phaseLength. */ - q31_t *pState; /**< points to the state variable array. The array is of length blockSize+phaseLength-1. */ - } arm_fir_interpolate_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR interpolator. - */ - typedef struct - { - uint8_t L; /**< upsample factor. */ - uint16_t phaseLength; /**< length of each polyphase filter component. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length L*phaseLength. */ - float32_t *pState; /**< points to the state variable array. The array is of length phaseLength+numTaps-1. */ - } arm_fir_interpolate_instance_f32; - - - /** - * @brief Processing function for the Q15 FIR interpolator. - * @param[in] S points to an instance of the Q15 FIR interpolator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_interpolate_q15( - const arm_fir_interpolate_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 FIR interpolator. - * @param[in,out] S points to an instance of the Q15 FIR interpolator structure. - * @param[in] L upsample factor. - * @param[in] numTaps number of filter coefficients in the filter. - * @param[in] pCoeffs points to the filter coefficient buffer. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * the filter length numTaps is not a multiple of the interpolation factor L. - */ - arm_status arm_fir_interpolate_init_q15( - arm_fir_interpolate_instance_q15 * S, - uint8_t L, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 FIR interpolator. - * @param[in] S points to an instance of the Q15 FIR interpolator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_interpolate_q31( - const arm_fir_interpolate_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 FIR interpolator. - * @param[in,out] S points to an instance of the Q31 FIR interpolator structure. - * @param[in] L upsample factor. - * @param[in] numTaps number of filter coefficients in the filter. - * @param[in] pCoeffs points to the filter coefficient buffer. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * the filter length numTaps is not a multiple of the interpolation factor L. - */ - arm_status arm_fir_interpolate_init_q31( - arm_fir_interpolate_instance_q31 * S, - uint8_t L, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the floating-point FIR interpolator. - * @param[in] S points to an instance of the floating-point FIR interpolator structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_interpolate_f32( - const arm_fir_interpolate_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point FIR interpolator. - * @param[in,out] S points to an instance of the floating-point FIR interpolator structure. - * @param[in] L upsample factor. - * @param[in] numTaps number of filter coefficients in the filter. - * @param[in] pCoeffs points to the filter coefficient buffer. - * @param[in] pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * the filter length numTaps is not a multiple of the interpolation factor L. - */ - arm_status arm_fir_interpolate_init_f32( - arm_fir_interpolate_instance_f32 * S, - uint8_t L, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - uint32_t blockSize); - - - /** - * @brief Instance structure for the high precision Q31 Biquad cascade filter. - */ - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - q63_t *pState; /**< points to the array of state coefficients. The array is of length 4*numStages. */ - q31_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - uint8_t postShift; /**< additional shift, in bits, applied to each output sample. */ - } arm_biquad_cas_df1_32x64_ins_q31; - - - /** - * @param[in] S points to an instance of the high precision Q31 Biquad cascade filter structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cas_df1_32x64_q31( - const arm_biquad_cas_df1_32x64_ins_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @param[in,out] S points to an instance of the high precision Q31 Biquad cascade filter structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] postShift shift to be applied to the output. Varies according to the coefficients format - */ - void arm_biquad_cas_df1_32x64_init_q31( - arm_biquad_cas_df1_32x64_ins_q31 * S, - uint8_t numStages, - q31_t * pCoeffs, - q63_t * pState, - uint8_t postShift); - - - /** - * @brief Instance structure for the floating-point transposed direct form II Biquad cascade filter. - */ - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float32_t *pState; /**< points to the array of state coefficients. The array is of length 2*numStages. */ - float32_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_cascade_df2T_instance_f32; - - /** - * @brief Instance structure for the floating-point transposed direct form II Biquad cascade filter. - */ - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float32_t *pState; /**< points to the array of state coefficients. The array is of length 4*numStages. */ - float32_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_cascade_stereo_df2T_instance_f32; - - /** - * @brief Instance structure for the floating-point transposed direct form II Biquad cascade filter. - */ - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float64_t *pState; /**< points to the array of state coefficients. The array is of length 2*numStages. */ - float64_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_cascade_df2T_instance_f64; - - - /** - * @brief Processing function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in] S points to an instance of the filter data structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df2T_f32( - const arm_biquad_cascade_df2T_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the floating-point transposed direct form II Biquad cascade filter. 2 channels - * @param[in] S points to an instance of the filter data structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_stereo_df2T_f32( - const arm_biquad_cascade_stereo_df2T_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in] S points to an instance of the filter data structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of samples to process. - */ - void arm_biquad_cascade_df2T_f64( - const arm_biquad_cascade_df2T_instance_f64 * S, - float64_t * pSrc, - float64_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in,out] S points to an instance of the filter data structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - */ - void arm_biquad_cascade_df2T_init_f32( - arm_biquad_cascade_df2T_instance_f32 * S, - uint8_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Initialization function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in,out] S points to an instance of the filter data structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - */ - void arm_biquad_cascade_stereo_df2T_init_f32( - arm_biquad_cascade_stereo_df2T_instance_f32 * S, - uint8_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Initialization function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in,out] S points to an instance of the filter data structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] pCoeffs points to the filter coefficients. - * @param[in] pState points to the state buffer. - */ - void arm_biquad_cascade_df2T_init_f64( - arm_biquad_cascade_df2T_instance_f64 * S, - uint8_t numStages, - float64_t * pCoeffs, - float64_t * pState); - - - /** - * @brief Instance structure for the Q15 FIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of filter stages. */ - q15_t *pState; /**< points to the state variable array. The array is of length numStages. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numStages. */ - } arm_fir_lattice_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of filter stages. */ - q31_t *pState; /**< points to the state variable array. The array is of length numStages. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numStages. */ - } arm_fir_lattice_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of filter stages. */ - float32_t *pState; /**< points to the state variable array. The array is of length numStages. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numStages. */ - } arm_fir_lattice_instance_f32; - - - /** - * @brief Initialization function for the Q15 FIR lattice filter. - * @param[in] S points to an instance of the Q15 FIR lattice structure. - * @param[in] numStages number of filter stages. - * @param[in] pCoeffs points to the coefficient buffer. The array is of length numStages. - * @param[in] pState points to the state buffer. The array is of length numStages. - */ - void arm_fir_lattice_init_q15( - arm_fir_lattice_instance_q15 * S, - uint16_t numStages, - q15_t * pCoeffs, - q15_t * pState); - - - /** - * @brief Processing function for the Q15 FIR lattice filter. - * @param[in] S points to an instance of the Q15 FIR lattice structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_fir_lattice_q15( - const arm_fir_lattice_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 FIR lattice filter. - * @param[in] S points to an instance of the Q31 FIR lattice structure. - * @param[in] numStages number of filter stages. - * @param[in] pCoeffs points to the coefficient buffer. The array is of length numStages. - * @param[in] pState points to the state buffer. The array is of length numStages. - */ - void arm_fir_lattice_init_q31( - arm_fir_lattice_instance_q31 * S, - uint16_t numStages, - q31_t * pCoeffs, - q31_t * pState); - - - /** - * @brief Processing function for the Q31 FIR lattice filter. - * @param[in] S points to an instance of the Q31 FIR lattice structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of samples to process. - */ - void arm_fir_lattice_q31( - const arm_fir_lattice_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - -/** - * @brief Initialization function for the floating-point FIR lattice filter. - * @param[in] S points to an instance of the floating-point FIR lattice structure. - * @param[in] numStages number of filter stages. - * @param[in] pCoeffs points to the coefficient buffer. The array is of length numStages. - * @param[in] pState points to the state buffer. The array is of length numStages. - */ - void arm_fir_lattice_init_f32( - arm_fir_lattice_instance_f32 * S, - uint16_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Processing function for the floating-point FIR lattice filter. - * @param[in] S points to an instance of the floating-point FIR lattice structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] blockSize number of samples to process. - */ - void arm_fir_lattice_f32( - const arm_fir_lattice_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q15 IIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of stages in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numStages+blockSize. */ - q15_t *pkCoeffs; /**< points to the reflection coefficient array. The array is of length numStages. */ - q15_t *pvCoeffs; /**< points to the ladder coefficient array. The array is of length numStages+1. */ - } arm_iir_lattice_instance_q15; - - /** - * @brief Instance structure for the Q31 IIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of stages in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numStages+blockSize. */ - q31_t *pkCoeffs; /**< points to the reflection coefficient array. The array is of length numStages. */ - q31_t *pvCoeffs; /**< points to the ladder coefficient array. The array is of length numStages+1. */ - } arm_iir_lattice_instance_q31; - - /** - * @brief Instance structure for the floating-point IIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of stages in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numStages+blockSize. */ - float32_t *pkCoeffs; /**< points to the reflection coefficient array. The array is of length numStages. */ - float32_t *pvCoeffs; /**< points to the ladder coefficient array. The array is of length numStages+1. */ - } arm_iir_lattice_instance_f32; - - - /** - * @brief Processing function for the floating-point IIR lattice filter. - * @param[in] S points to an instance of the floating-point IIR lattice structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_iir_lattice_f32( - const arm_iir_lattice_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point IIR lattice filter. - * @param[in] S points to an instance of the floating-point IIR lattice structure. - * @param[in] numStages number of stages in the filter. - * @param[in] pkCoeffs points to the reflection coefficient buffer. The array is of length numStages. - * @param[in] pvCoeffs points to the ladder coefficient buffer. The array is of length numStages+1. - * @param[in] pState points to the state buffer. The array is of length numStages+blockSize-1. - * @param[in] blockSize number of samples to process. - */ - void arm_iir_lattice_init_f32( - arm_iir_lattice_instance_f32 * S, - uint16_t numStages, - float32_t * pkCoeffs, - float32_t * pvCoeffs, - float32_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 IIR lattice filter. - * @param[in] S points to an instance of the Q31 IIR lattice structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_iir_lattice_q31( - const arm_iir_lattice_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 IIR lattice filter. - * @param[in] S points to an instance of the Q31 IIR lattice structure. - * @param[in] numStages number of stages in the filter. - * @param[in] pkCoeffs points to the reflection coefficient buffer. The array is of length numStages. - * @param[in] pvCoeffs points to the ladder coefficient buffer. The array is of length numStages+1. - * @param[in] pState points to the state buffer. The array is of length numStages+blockSize. - * @param[in] blockSize number of samples to process. - */ - void arm_iir_lattice_init_q31( - arm_iir_lattice_instance_q31 * S, - uint16_t numStages, - q31_t * pkCoeffs, - q31_t * pvCoeffs, - q31_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 IIR lattice filter. - * @param[in] S points to an instance of the Q15 IIR lattice structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - */ - void arm_iir_lattice_q15( - const arm_iir_lattice_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - -/** - * @brief Initialization function for the Q15 IIR lattice filter. - * @param[in] S points to an instance of the fixed-point Q15 IIR lattice structure. - * @param[in] numStages number of stages in the filter. - * @param[in] pkCoeffs points to reflection coefficient buffer. The array is of length numStages. - * @param[in] pvCoeffs points to ladder coefficient buffer. The array is of length numStages+1. - * @param[in] pState points to state buffer. The array is of length numStages+blockSize. - * @param[in] blockSize number of samples to process per call. - */ - void arm_iir_lattice_init_q15( - arm_iir_lattice_instance_q15 * S, - uint16_t numStages, - q15_t * pkCoeffs, - q15_t * pvCoeffs, - q15_t * pState, - uint32_t blockSize); - - - /** - * @brief Instance structure for the floating-point LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - float32_t mu; /**< step size that controls filter coefficient updates. */ - } arm_lms_instance_f32; - - - /** - * @brief Processing function for floating-point LMS filter. - * @param[in] S points to an instance of the floating-point LMS filter structure. - * @param[in] pSrc points to the block of input data. - * @param[in] pRef points to the block of reference data. - * @param[out] pOut points to the block of output data. - * @param[out] pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_f32( - const arm_lms_instance_f32 * S, - float32_t * pSrc, - float32_t * pRef, - float32_t * pOut, - float32_t * pErr, - uint32_t blockSize); - - - /** - * @brief Initialization function for floating-point LMS filter. - * @param[in] S points to an instance of the floating-point LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] pCoeffs points to the coefficient buffer. - * @param[in] pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_init_f32( - arm_lms_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - float32_t mu, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q15 LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q15_t mu; /**< step size that controls filter coefficient updates. */ - uint32_t postShift; /**< bit shift applied to coefficients. */ - } arm_lms_instance_q15; - - - /** - * @brief Initialization function for the Q15 LMS filter. - * @param[in] S points to an instance of the Q15 LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] pCoeffs points to the coefficient buffer. - * @param[in] pState points to the state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - */ - void arm_lms_init_q15( - arm_lms_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - q15_t mu, - uint32_t blockSize, - uint32_t postShift); - - - /** - * @brief Processing function for Q15 LMS filter. - * @param[in] S points to an instance of the Q15 LMS filter structure. - * @param[in] pSrc points to the block of input data. - * @param[in] pRef points to the block of reference data. - * @param[out] pOut points to the block of output data. - * @param[out] pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_q15( - const arm_lms_instance_q15 * S, - q15_t * pSrc, - q15_t * pRef, - q15_t * pOut, - q15_t * pErr, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q31 LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q31_t mu; /**< step size that controls filter coefficient updates. */ - uint32_t postShift; /**< bit shift applied to coefficients. */ - } arm_lms_instance_q31; - - - /** - * @brief Processing function for Q31 LMS filter. - * @param[in] S points to an instance of the Q15 LMS filter structure. - * @param[in] pSrc points to the block of input data. - * @param[in] pRef points to the block of reference data. - * @param[out] pOut points to the block of output data. - * @param[out] pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_q31( - const arm_lms_instance_q31 * S, - q31_t * pSrc, - q31_t * pRef, - q31_t * pOut, - q31_t * pErr, - uint32_t blockSize); - - - /** - * @brief Initialization function for Q31 LMS filter. - * @param[in] S points to an instance of the Q31 LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] pCoeffs points to coefficient buffer. - * @param[in] pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - */ - void arm_lms_init_q31( - arm_lms_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - q31_t mu, - uint32_t blockSize, - uint32_t postShift); - - - /** - * @brief Instance structure for the floating-point normalized LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - float32_t mu; /**< step size that control filter coefficient updates. */ - float32_t energy; /**< saves previous frame energy. */ - float32_t x0; /**< saves previous input sample. */ - } arm_lms_norm_instance_f32; - - - /** - * @brief Processing function for floating-point normalized LMS filter. - * @param[in] S points to an instance of the floating-point normalized LMS filter structure. - * @param[in] pSrc points to the block of input data. - * @param[in] pRef points to the block of reference data. - * @param[out] pOut points to the block of output data. - * @param[out] pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_norm_f32( - arm_lms_norm_instance_f32 * S, - float32_t * pSrc, - float32_t * pRef, - float32_t * pOut, - float32_t * pErr, - uint32_t blockSize); - - - /** - * @brief Initialization function for floating-point normalized LMS filter. - * @param[in] S points to an instance of the floating-point LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] pCoeffs points to coefficient buffer. - * @param[in] pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_norm_init_f32( - arm_lms_norm_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - float32_t mu, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q31 normalized LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q31_t mu; /**< step size that controls filter coefficient updates. */ - uint8_t postShift; /**< bit shift applied to coefficients. */ - q31_t *recipTable; /**< points to the reciprocal initial value table. */ - q31_t energy; /**< saves previous frame energy. */ - q31_t x0; /**< saves previous input sample. */ - } arm_lms_norm_instance_q31; - - - /** - * @brief Processing function for Q31 normalized LMS filter. - * @param[in] S points to an instance of the Q31 normalized LMS filter structure. - * @param[in] pSrc points to the block of input data. - * @param[in] pRef points to the block of reference data. - * @param[out] pOut points to the block of output data. - * @param[out] pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_norm_q31( - arm_lms_norm_instance_q31 * S, - q31_t * pSrc, - q31_t * pRef, - q31_t * pOut, - q31_t * pErr, - uint32_t blockSize); - - - /** - * @brief Initialization function for Q31 normalized LMS filter. - * @param[in] S points to an instance of the Q31 normalized LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] pCoeffs points to coefficient buffer. - * @param[in] pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - */ - void arm_lms_norm_init_q31( - arm_lms_norm_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - q31_t mu, - uint32_t blockSize, - uint8_t postShift); - - - /** - * @brief Instance structure for the Q15 normalized LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< Number of coefficients in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q15_t mu; /**< step size that controls filter coefficient updates. */ - uint8_t postShift; /**< bit shift applied to coefficients. */ - q15_t *recipTable; /**< Points to the reciprocal initial value table. */ - q15_t energy; /**< saves previous frame energy. */ - q15_t x0; /**< saves previous input sample. */ - } arm_lms_norm_instance_q15; - - - /** - * @brief Processing function for Q15 normalized LMS filter. - * @param[in] S points to an instance of the Q15 normalized LMS filter structure. - * @param[in] pSrc points to the block of input data. - * @param[in] pRef points to the block of reference data. - * @param[out] pOut points to the block of output data. - * @param[out] pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - */ - void arm_lms_norm_q15( - arm_lms_norm_instance_q15 * S, - q15_t * pSrc, - q15_t * pRef, - q15_t * pOut, - q15_t * pErr, - uint32_t blockSize); - - - /** - * @brief Initialization function for Q15 normalized LMS filter. - * @param[in] S points to an instance of the Q15 normalized LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] pCoeffs points to coefficient buffer. - * @param[in] pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - */ - void arm_lms_norm_init_q15( - arm_lms_norm_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - q15_t mu, - uint32_t blockSize, - uint8_t postShift); - - - /** - * @brief Correlation of floating-point sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - */ - void arm_correlate_f32( - float32_t * pSrcA, - uint32_t srcALen, - float32_t * pSrcB, - uint32_t srcBLen, - float32_t * pDst); - - - /** - * @brief Correlation of Q15 sequences - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @param[in] pScratch points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - */ - void arm_correlate_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch); - - - /** - * @brief Correlation of Q15 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - */ - - void arm_correlate_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - - /** - * @brief Correlation of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - */ - - void arm_correlate_fast_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - - /** - * @brief Correlation of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @param[in] pScratch points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - */ - void arm_correlate_fast_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch); - - - /** - * @brief Correlation of Q31 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - */ - void arm_correlate_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - - /** - * @brief Correlation of Q31 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - */ - void arm_correlate_fast_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - - /** - * @brief Correlation of Q7 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @param[in] pScratch1 points to scratch buffer(of type q15_t) of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] pScratch2 points to scratch buffer (of type q15_t) of size min(srcALen, srcBLen). - */ - void arm_correlate_opt_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Correlation of Q7 sequences. - * @param[in] pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - */ - void arm_correlate_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst); - - - /** - * @brief Instance structure for the floating-point sparse FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - float32_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_f32; - - /** - * @brief Instance structure for the Q31 sparse FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - q31_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_q31; - - /** - * @brief Instance structure for the Q15 sparse FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - q15_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_q15; - - /** - * @brief Instance structure for the Q7 sparse FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - q7_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - q7_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_q7; - - - /** - * @brief Processing function for the floating-point sparse FIR filter. - * @param[in] S points to an instance of the floating-point sparse FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] pScratchIn points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_sparse_f32( - arm_fir_sparse_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - float32_t * pScratchIn, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point sparse FIR filter. - * @param[in,out] S points to an instance of the floating-point sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] pCoeffs points to the array of filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - */ - void arm_fir_sparse_init_f32( - arm_fir_sparse_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 sparse FIR filter. - * @param[in] S points to an instance of the Q31 sparse FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] pScratchIn points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_sparse_q31( - arm_fir_sparse_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - q31_t * pScratchIn, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 sparse FIR filter. - * @param[in,out] S points to an instance of the Q31 sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] pCoeffs points to the array of filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - */ - void arm_fir_sparse_init_q31( - arm_fir_sparse_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 sparse FIR filter. - * @param[in] S points to an instance of the Q15 sparse FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] pScratchIn points to a temporary buffer of size blockSize. - * @param[in] pScratchOut points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_sparse_q15( - arm_fir_sparse_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - q15_t * pScratchIn, - q31_t * pScratchOut, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 sparse FIR filter. - * @param[in,out] S points to an instance of the Q15 sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] pCoeffs points to the array of filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - */ - void arm_fir_sparse_init_q15( - arm_fir_sparse_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q7 sparse FIR filter. - * @param[in] S points to an instance of the Q7 sparse FIR structure. - * @param[in] pSrc points to the block of input data. - * @param[out] pDst points to the block of output data - * @param[in] pScratchIn points to a temporary buffer of size blockSize. - * @param[in] pScratchOut points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - */ - void arm_fir_sparse_q7( - arm_fir_sparse_instance_q7 * S, - q7_t * pSrc, - q7_t * pDst, - q7_t * pScratchIn, - q31_t * pScratchOut, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q7 sparse FIR filter. - * @param[in,out] S points to an instance of the Q7 sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] pCoeffs points to the array of filter coefficients. - * @param[in] pState points to the state buffer. - * @param[in] pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - */ - void arm_fir_sparse_init_q7( - arm_fir_sparse_instance_q7 * S, - uint16_t numTaps, - q7_t * pCoeffs, - q7_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - - /** - * @brief Floating-point sin_cos function. - * @param[in] theta input value in degrees - * @param[out] pSinVal points to the processed sine output. - * @param[out] pCosVal points to the processed cos output. - */ - void arm_sin_cos_f32( - float32_t theta, - float32_t * pSinVal, - float32_t * pCosVal); - - - /** - * @brief Q31 sin_cos function. - * @param[in] theta scaled input value in degrees - * @param[out] pSinVal points to the processed sine output. - * @param[out] pCosVal points to the processed cosine output. - */ - void arm_sin_cos_q31( - q31_t theta, - q31_t * pSinVal, - q31_t * pCosVal); - - - /** - * @brief Floating-point complex conjugate. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - */ - void arm_cmplx_conj_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t numSamples); - - /** - * @brief Q31 complex conjugate. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - */ - void arm_cmplx_conj_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q15 complex conjugate. - * @param[in] pSrc points to the input vector - * @param[out] pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - */ - void arm_cmplx_conj_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); - - - /** - * @brief Floating-point complex magnitude squared - * @param[in] pSrc points to the complex input vector - * @param[out] pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - */ - void arm_cmplx_mag_squared_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q31 complex magnitude squared - * @param[in] pSrc points to the complex input vector - * @param[out] pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - */ - void arm_cmplx_mag_squared_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q15 complex magnitude squared - * @param[in] pSrc points to the complex input vector - * @param[out] pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - */ - void arm_cmplx_mag_squared_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); - - - /** - * @ingroup groupController - */ - - /** - * @defgroup PID PID Motor Control - * - * A Proportional Integral Derivative (PID) controller is a generic feedback control - * loop mechanism widely used in industrial control systems. - * A PID controller is the most commonly used type of feedback controller. - * - * This set of functions implements (PID) controllers - * for Q15, Q31, and floating-point data types. The functions operate on a single sample - * of data and each call to the function returns a single processed value. - * S points to an instance of the PID control data structure. in - * is the input sample value. The functions return the output value. - * - * \par Algorithm: - *
    -   *    y[n] = y[n-1] + A0 * x[n] + A1 * x[n-1] + A2 * x[n-2]
    -   *    A0 = Kp + Ki + Kd
    -   *    A1 = (-Kp ) - (2 * Kd )
    -   *    A2 = Kd  
    - * - * \par - * where \c Kp is proportional constant, \c Ki is Integral constant and \c Kd is Derivative constant - * - * \par - * \image html PID.gif "Proportional Integral Derivative Controller" - * - * \par - * The PID controller calculates an "error" value as the difference between - * the measured output and the reference input. - * The controller attempts to minimize the error by adjusting the process control inputs. - * The proportional value determines the reaction to the current error, - * the integral value determines the reaction based on the sum of recent errors, - * and the derivative value determines the reaction based on the rate at which the error has been changing. - * - * \par Instance Structure - * The Gains A0, A1, A2 and state variables for a PID controller are stored together in an instance data structure. - * A separate instance structure must be defined for each PID Controller. - * There are separate instance structure declarations for each of the 3 supported data types. - * - * \par Reset Functions - * There is also an associated reset function for each data type which clears the state array. - * - * \par Initialization Functions - * There is also an associated initialization function for each data type. - * The initialization function performs the following operations: - * - Initializes the Gains A0, A1, A2 from Kp,Ki, Kd gains. - * - Zeros out the values in the state buffer. - * - * \par - * Instance structure cannot be placed into a const data section and it is recommended to use the initialization function. - * - * \par Fixed-Point Behavior - * Care must be taken when using the fixed-point versions of the PID Controller functions. - * In particular, the overflow and saturation behavior of the accumulator used in each function must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup PID - * @{ - */ - - /** - * @brief Process function for the floating-point PID Control. - * @param[in,out] S is an instance of the floating-point PID Control structure - * @param[in] in input sample to process - * @return out processed output sample. - */ - static __INLINE float32_t arm_pid_f32( - arm_pid_instance_f32 * S, - float32_t in) - { - float32_t out; - - /* y[n] = y[n-1] + A0 * x[n] + A1 * x[n-1] + A2 * x[n-2] */ - out = (S->A0 * in) + - (S->A1 * S->state[0]) + (S->A2 * S->state[1]) + (S->state[2]); - - /* Update state */ - S->state[1] = S->state[0]; - S->state[0] = in; - S->state[2] = out; - - /* return to application */ - return (out); - - } - - /** - * @brief Process function for the Q31 PID Control. - * @param[in,out] S points to an instance of the Q31 PID Control structure - * @param[in] in input sample to process - * @return out processed output sample. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 64-bit accumulator. - * The accumulator has a 2.62 format and maintains full precision of the intermediate multiplication results but provides only a single guard bit. - * Thus, if the accumulator result overflows it wraps around rather than clip. - * In order to avoid overflows completely the input signal must be scaled down by 2 bits as there are four additions. - * After all multiply-accumulates are performed, the 2.62 accumulator is truncated to 1.32 format and then saturated to 1.31 format. - */ - static __INLINE q31_t arm_pid_q31( - arm_pid_instance_q31 * S, - q31_t in) - { - q63_t acc; - q31_t out; - - /* acc = A0 * x[n] */ - acc = (q63_t) S->A0 * in; - - /* acc += A1 * x[n-1] */ - acc += (q63_t) S->A1 * S->state[0]; - - /* acc += A2 * x[n-2] */ - acc += (q63_t) S->A2 * S->state[1]; - - /* convert output to 1.31 format to add y[n-1] */ - out = (q31_t) (acc >> 31u); - - /* out += y[n-1] */ - out += S->state[2]; - - /* Update state */ - S->state[1] = S->state[0]; - S->state[0] = in; - S->state[2] = out; - - /* return to application */ - return (out); - } - - - /** - * @brief Process function for the Q15 PID Control. - * @param[in,out] S points to an instance of the Q15 PID Control structure - * @param[in] in input sample to process - * @return out processed output sample. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using a 64-bit internal accumulator. - * Both Gains and state variables are represented in 1.15 format and multiplications yield a 2.30 result. - * The 2.30 intermediate results are accumulated in a 64-bit accumulator in 34.30 format. - * There is no risk of internal overflow with this approach and the full precision of intermediate multiplications is preserved. - * After all additions have been performed, the accumulator is truncated to 34.15 format by discarding low 15 bits. - * Lastly, the accumulator is saturated to yield a result in 1.15 format. - */ - static __INLINE q15_t arm_pid_q15( - arm_pid_instance_q15 * S, - q15_t in) - { - q63_t acc; - q15_t out; - -#ifndef ARM_MATH_CM0_FAMILY - __SIMD32_TYPE *vstate; - - /* Implementation of PID controller */ - - /* acc = A0 * x[n] */ - acc = (q31_t) __SMUAD((uint32_t)S->A0, (uint32_t)in); - - /* acc += A1 * x[n-1] + A2 * x[n-2] */ - vstate = __SIMD32_CONST(S->state); - acc = (q63_t)__SMLALD((uint32_t)S->A1, (uint32_t)*vstate, (uint64_t)acc); -#else - /* acc = A0 * x[n] */ - acc = ((q31_t) S->A0) * in; - - /* acc += A1 * x[n-1] + A2 * x[n-2] */ - acc += (q31_t) S->A1 * S->state[0]; - acc += (q31_t) S->A2 * S->state[1]; -#endif - - /* acc += y[n-1] */ - acc += (q31_t) S->state[2] << 15; - - /* saturate the output */ - out = (q15_t) (__SSAT((acc >> 15), 16)); - - /* Update state */ - S->state[1] = S->state[0]; - S->state[0] = in; - S->state[2] = out; - - /* return to application */ - return (out); - } - - /** - * @} end of PID group - */ - - - /** - * @brief Floating-point matrix inverse. - * @param[in] src points to the instance of the input floating-point matrix structure. - * @param[out] dst points to the instance of the output floating-point matrix structure. - * @return The function returns ARM_MATH_SIZE_MISMATCH, if the dimensions do not match. - * If the input matrix is singular (does not have an inverse), then the algorithm terminates and returns error status ARM_MATH_SINGULAR. - */ - arm_status arm_mat_inverse_f32( - const arm_matrix_instance_f32 * src, - arm_matrix_instance_f32 * dst); - - - /** - * @brief Floating-point matrix inverse. - * @param[in] src points to the instance of the input floating-point matrix structure. - * @param[out] dst points to the instance of the output floating-point matrix structure. - * @return The function returns ARM_MATH_SIZE_MISMATCH, if the dimensions do not match. - * If the input matrix is singular (does not have an inverse), then the algorithm terminates and returns error status ARM_MATH_SINGULAR. - */ - arm_status arm_mat_inverse_f64( - const arm_matrix_instance_f64 * src, - arm_matrix_instance_f64 * dst); - - - - /** - * @ingroup groupController - */ - - /** - * @defgroup clarke Vector Clarke Transform - * Forward Clarke transform converts the instantaneous stator phases into a two-coordinate time invariant vector. - * Generally the Clarke transform uses three-phase currents Ia, Ib and Ic to calculate currents - * in the two-phase orthogonal stator axis Ialpha and Ibeta. - * When Ialpha is superposed with Ia as shown in the figure below - * \image html clarke.gif Stator current space vector and its components in (a,b). - * and Ia + Ib + Ic = 0, in this condition Ialpha and Ibeta - * can be calculated using only Ia and Ib. - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html clarkeFormula.gif - * where Ia and Ib are the instantaneous stator phases and - * pIalpha and pIbeta are the two coordinates of time invariant vector. - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Clarke transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup clarke - * @{ - */ - - /** - * - * @brief Floating-point Clarke transform - * @param[in] Ia input three-phase coordinate a - * @param[in] Ib input three-phase coordinate b - * @param[out] pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] pIbeta points to output two-phase orthogonal vector axis beta - */ - static __INLINE void arm_clarke_f32( - float32_t Ia, - float32_t Ib, - float32_t * pIalpha, - float32_t * pIbeta) - { - /* Calculate pIalpha using the equation, pIalpha = Ia */ - *pIalpha = Ia; - - /* Calculate pIbeta using the equation, pIbeta = (1/sqrt(3)) * Ia + (2/sqrt(3)) * Ib */ - *pIbeta = ((float32_t) 0.57735026919 * Ia + (float32_t) 1.15470053838 * Ib); - } - - - /** - * @brief Clarke transform for Q31 version - * @param[in] Ia input three-phase coordinate a - * @param[in] Ib input three-phase coordinate b - * @param[out] pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] pIbeta points to output two-phase orthogonal vector axis beta - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the addition, hence there is no risk of overflow. - */ - static __INLINE void arm_clarke_q31( - q31_t Ia, - q31_t Ib, - q31_t * pIalpha, - q31_t * pIbeta) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - - /* Calculating pIalpha from Ia by equation pIalpha = Ia */ - *pIalpha = Ia; - - /* Intermediate product is calculated by (1/(sqrt(3)) * Ia) */ - product1 = (q31_t) (((q63_t) Ia * 0x24F34E8B) >> 30); - - /* Intermediate product is calculated by (2/sqrt(3) * Ib) */ - product2 = (q31_t) (((q63_t) Ib * 0x49E69D16) >> 30); - - /* pIbeta is calculated by adding the intermediate products */ - *pIbeta = __QADD(product1, product2); - } - - /** - * @} end of clarke group - */ - - /** - * @brief Converts the elements of the Q7 vector to Q31 vector. - * @param[in] pSrc input pointer - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_q7_to_q31( - q7_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - - /** - * @ingroup groupController - */ - - /** - * @defgroup inv_clarke Vector Inverse Clarke Transform - * Inverse Clarke transform converts the two-coordinate time invariant vector into instantaneous stator phases. - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html clarkeInvFormula.gif - * where pIa and pIb are the instantaneous stator phases and - * Ialpha and Ibeta are the two coordinates of time invariant vector. - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Clarke transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup inv_clarke - * @{ - */ - - /** - * @brief Floating-point Inverse Clarke transform - * @param[in] Ialpha input two-phase orthogonal vector axis alpha - * @param[in] Ibeta input two-phase orthogonal vector axis beta - * @param[out] pIa points to output three-phase coordinate a - * @param[out] pIb points to output three-phase coordinate b - */ - static __INLINE void arm_inv_clarke_f32( - float32_t Ialpha, - float32_t Ibeta, - float32_t * pIa, - float32_t * pIb) - { - /* Calculating pIa from Ialpha by equation pIa = Ialpha */ - *pIa = Ialpha; - - /* Calculating pIb from Ialpha and Ibeta by equation pIb = -(1/2) * Ialpha + (sqrt(3)/2) * Ibeta */ - *pIb = -0.5f * Ialpha + 0.8660254039f * Ibeta; - } - - - /** - * @brief Inverse Clarke transform for Q31 version - * @param[in] Ialpha input two-phase orthogonal vector axis alpha - * @param[in] Ibeta input two-phase orthogonal vector axis beta - * @param[out] pIa points to output three-phase coordinate a - * @param[out] pIb points to output three-phase coordinate b - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the subtraction, hence there is no risk of overflow. - */ - static __INLINE void arm_inv_clarke_q31( - q31_t Ialpha, - q31_t Ibeta, - q31_t * pIa, - q31_t * pIb) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - - /* Calculating pIa from Ialpha by equation pIa = Ialpha */ - *pIa = Ialpha; - - /* Intermediate product is calculated by (1/(2*sqrt(3)) * Ia) */ - product1 = (q31_t) (((q63_t) (Ialpha) * (0x40000000)) >> 31); - - /* Intermediate product is calculated by (1/sqrt(3) * pIb) */ - product2 = (q31_t) (((q63_t) (Ibeta) * (0x6ED9EBA1)) >> 31); - - /* pIb is calculated by subtracting the products */ - *pIb = __QSUB(product2, product1); - } - - /** - * @} end of inv_clarke group - */ - - /** - * @brief Converts the elements of the Q7 vector to Q15 vector. - * @param[in] pSrc input pointer - * @param[out] pDst output pointer - * @param[in] blockSize number of samples to process - */ - void arm_q7_to_q15( - q7_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - - /** - * @ingroup groupController - */ - - /** - * @defgroup park Vector Park Transform - * - * Forward Park transform converts the input two-coordinate vector to flux and torque components. - * The Park transform can be used to realize the transformation of the Ialpha and the Ibeta currents - * from the stationary to the moving reference frame and control the spatial relationship between - * the stator vector current and rotor flux vector. - * If we consider the d axis aligned with the rotor flux, the diagram below shows the - * current vector and the relationship from the two reference frames: - * \image html park.gif "Stator current space vector and its component in (a,b) and in the d,q rotating reference frame" - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html parkFormula.gif - * where Ialpha and Ibeta are the stator vector components, - * pId and pIq are rotor vector components and cosVal and sinVal are the - * cosine and sine values of theta (rotor flux position). - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Park transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup park - * @{ - */ - - /** - * @brief Floating-point Park transform - * @param[in] Ialpha input two-phase vector coordinate alpha - * @param[in] Ibeta input two-phase vector coordinate beta - * @param[out] pId points to output rotor reference frame d - * @param[out] pIq points to output rotor reference frame q - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * - * The function implements the forward Park transform. - * - */ - static __INLINE void arm_park_f32( - float32_t Ialpha, - float32_t Ibeta, - float32_t * pId, - float32_t * pIq, - float32_t sinVal, - float32_t cosVal) - { - /* Calculate pId using the equation, pId = Ialpha * cosVal + Ibeta * sinVal */ - *pId = Ialpha * cosVal + Ibeta * sinVal; - - /* Calculate pIq using the equation, pIq = - Ialpha * sinVal + Ibeta * cosVal */ - *pIq = -Ialpha * sinVal + Ibeta * cosVal; - } - - - /** - * @brief Park transform for Q31 version - * @param[in] Ialpha input two-phase vector coordinate alpha - * @param[in] Ibeta input two-phase vector coordinate beta - * @param[out] pId points to output rotor reference frame d - * @param[out] pIq points to output rotor reference frame q - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the addition and subtraction, hence there is no risk of overflow. - */ - static __INLINE void arm_park_q31( - q31_t Ialpha, - q31_t Ibeta, - q31_t * pId, - q31_t * pIq, - q31_t sinVal, - q31_t cosVal) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - q31_t product3, product4; /* Temporary variables used to store intermediate results */ - - /* Intermediate product is calculated by (Ialpha * cosVal) */ - product1 = (q31_t) (((q63_t) (Ialpha) * (cosVal)) >> 31); - - /* Intermediate product is calculated by (Ibeta * sinVal) */ - product2 = (q31_t) (((q63_t) (Ibeta) * (sinVal)) >> 31); - - - /* Intermediate product is calculated by (Ialpha * sinVal) */ - product3 = (q31_t) (((q63_t) (Ialpha) * (sinVal)) >> 31); - - /* Intermediate product is calculated by (Ibeta * cosVal) */ - product4 = (q31_t) (((q63_t) (Ibeta) * (cosVal)) >> 31); - - /* Calculate pId by adding the two intermediate products 1 and 2 */ - *pId = __QADD(product1, product2); - - /* Calculate pIq by subtracting the two intermediate products 3 from 4 */ - *pIq = __QSUB(product4, product3); - } - - /** - * @} end of park group - */ - - /** - * @brief Converts the elements of the Q7 vector to floating-point vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q7_to_float( - q7_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @ingroup groupController - */ - - /** - * @defgroup inv_park Vector Inverse Park transform - * Inverse Park transform converts the input flux and torque components to two-coordinate vector. - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html parkInvFormula.gif - * where pIalpha and pIbeta are the stator vector components, - * Id and Iq are rotor vector components and cosVal and sinVal are the - * cosine and sine values of theta (rotor flux position). - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Park transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup inv_park - * @{ - */ - - /** - * @brief Floating-point Inverse Park transform - * @param[in] Id input coordinate of rotor reference frame d - * @param[in] Iq input coordinate of rotor reference frame q - * @param[out] pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] pIbeta points to output two-phase orthogonal vector axis beta - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - */ - static __INLINE void arm_inv_park_f32( - float32_t Id, - float32_t Iq, - float32_t * pIalpha, - float32_t * pIbeta, - float32_t sinVal, - float32_t cosVal) - { - /* Calculate pIalpha using the equation, pIalpha = Id * cosVal - Iq * sinVal */ - *pIalpha = Id * cosVal - Iq * sinVal; - - /* Calculate pIbeta using the equation, pIbeta = Id * sinVal + Iq * cosVal */ - *pIbeta = Id * sinVal + Iq * cosVal; - } - - - /** - * @brief Inverse Park transform for Q31 version - * @param[in] Id input coordinate of rotor reference frame d - * @param[in] Iq input coordinate of rotor reference frame q - * @param[out] pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] pIbeta points to output two-phase orthogonal vector axis beta - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the addition, hence there is no risk of overflow. - */ - static __INLINE void arm_inv_park_q31( - q31_t Id, - q31_t Iq, - q31_t * pIalpha, - q31_t * pIbeta, - q31_t sinVal, - q31_t cosVal) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - q31_t product3, product4; /* Temporary variables used to store intermediate results */ - - /* Intermediate product is calculated by (Id * cosVal) */ - product1 = (q31_t) (((q63_t) (Id) * (cosVal)) >> 31); - - /* Intermediate product is calculated by (Iq * sinVal) */ - product2 = (q31_t) (((q63_t) (Iq) * (sinVal)) >> 31); - - - /* Intermediate product is calculated by (Id * sinVal) */ - product3 = (q31_t) (((q63_t) (Id) * (sinVal)) >> 31); - - /* Intermediate product is calculated by (Iq * cosVal) */ - product4 = (q31_t) (((q63_t) (Iq) * (cosVal)) >> 31); - - /* Calculate pIalpha by using the two intermediate products 1 and 2 */ - *pIalpha = __QSUB(product1, product2); - - /* Calculate pIbeta by using the two intermediate products 3 and 4 */ - *pIbeta = __QADD(product4, product3); - } - - /** - * @} end of Inverse park group - */ - - - /** - * @brief Converts the elements of the Q31 vector to floating-point vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q31_to_float( - q31_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @ingroup groupInterpolation - */ - - /** - * @defgroup LinearInterpolate Linear Interpolation - * - * Linear interpolation is a method of curve fitting using linear polynomials. - * Linear interpolation works by effectively drawing a straight line between two neighboring samples and returning the appropriate point along that line - * - * \par - * \image html LinearInterp.gif "Linear interpolation" - * - * \par - * A Linear Interpolate function calculates an output value(y), for the input(x) - * using linear interpolation of the input values x0, x1( nearest input values) and the output values y0 and y1(nearest output values) - * - * \par Algorithm: - *
    -   *       y = y0 + (x - x0) * ((y1 - y0)/(x1-x0))
    -   *       where x0, x1 are nearest values of input x
    -   *             y0, y1 are nearest values to output y
    -   * 
    - * - * \par - * This set of functions implements Linear interpolation process - * for Q7, Q15, Q31, and floating-point data types. The functions operate on a single - * sample of data and each call to the function returns a single processed value. - * S points to an instance of the Linear Interpolate function data structure. - * x is the input sample value. The functions returns the output value. - * - * \par - * if x is outside of the table boundary, Linear interpolation returns first value of the table - * if x is below input range and returns last value of table if x is above range. - */ - - /** - * @addtogroup LinearInterpolate - * @{ - */ - - /** - * @brief Process function for the floating-point Linear Interpolation Function. - * @param[in,out] S is an instance of the floating-point Linear Interpolation structure - * @param[in] x input sample to process - * @return y processed output sample. - * - */ - static __INLINE float32_t arm_linear_interp_f32( - arm_linear_interp_instance_f32 * S, - float32_t x) - { - float32_t y; - float32_t x0, x1; /* Nearest input values */ - float32_t y0, y1; /* Nearest output values */ - float32_t xSpacing = S->xSpacing; /* spacing between input values */ - int32_t i; /* Index variable */ - float32_t *pYData = S->pYData; /* pointer to output table */ - - /* Calculation of index */ - i = (int32_t) ((x - S->x1) / xSpacing); - - if(i < 0) - { - /* Iniatilize output for below specified range as least output value of table */ - y = pYData[0]; - } - else if((uint32_t)i >= S->nValues) - { - /* Iniatilize output for above specified range as last output value of table */ - y = pYData[S->nValues - 1]; - } - else - { - /* Calculation of nearest input values */ - x0 = S->x1 + i * xSpacing; - x1 = S->x1 + (i + 1) * xSpacing; - - /* Read of nearest output values */ - y0 = pYData[i]; - y1 = pYData[i + 1]; - - /* Calculation of output */ - y = y0 + (x - x0) * ((y1 - y0) / (x1 - x0)); - - } - - /* returns output value */ - return (y); - } - - - /** - * - * @brief Process function for the Q31 Linear Interpolation Function. - * @param[in] pYData pointer to Q31 Linear Interpolation table - * @param[in] x input sample to process - * @param[in] nValues number of table values - * @return y processed output sample. - * - * \par - * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. - * This function can support maximum of table size 2^12. - * - */ - static __INLINE q31_t arm_linear_interp_q31( - q31_t * pYData, - q31_t x, - uint32_t nValues) - { - q31_t y; /* output */ - q31_t y0, y1; /* Nearest output values */ - q31_t fract; /* fractional part */ - int32_t index; /* Index to read nearest output values */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - index = ((x & (q31_t)0xFFF00000) >> 20); - - if(index >= (int32_t)(nValues - 1)) - { - return (pYData[nValues - 1]); - } - else if(index < 0) - { - return (pYData[0]); - } - else - { - /* 20 bits for the fractional part */ - /* shift left by 11 to keep fract in 1.31 format */ - fract = (x & 0x000FFFFF) << 11; - - /* Read two nearest output values from the index in 1.31(q31) format */ - y0 = pYData[index]; - y1 = pYData[index + 1]; - - /* Calculation of y0 * (1-fract) and y is in 2.30 format */ - y = ((q31_t) ((q63_t) y0 * (0x7FFFFFFF - fract) >> 32)); - - /* Calculation of y0 * (1-fract) + y1 *fract and y is in 2.30 format */ - y += ((q31_t) (((q63_t) y1 * fract) >> 32)); - - /* Convert y to 1.31 format */ - return (y << 1u); - } - } - - - /** - * - * @brief Process function for the Q15 Linear Interpolation Function. - * @param[in] pYData pointer to Q15 Linear Interpolation table - * @param[in] x input sample to process - * @param[in] nValues number of table values - * @return y processed output sample. - * - * \par - * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. - * This function can support maximum of table size 2^12. - * - */ - static __INLINE q15_t arm_linear_interp_q15( - q15_t * pYData, - q31_t x, - uint32_t nValues) - { - q63_t y; /* output */ - q15_t y0, y1; /* Nearest output values */ - q31_t fract; /* fractional part */ - int32_t index; /* Index to read nearest output values */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - index = ((x & (int32_t)0xFFF00000) >> 20); - - if(index >= (int32_t)(nValues - 1)) - { - return (pYData[nValues - 1]); - } - else if(index < 0) - { - return (pYData[0]); - } - else - { - /* 20 bits for the fractional part */ - /* fract is in 12.20 format */ - fract = (x & 0x000FFFFF); - - /* Read two nearest output values from the index */ - y0 = pYData[index]; - y1 = pYData[index + 1]; - - /* Calculation of y0 * (1-fract) and y is in 13.35 format */ - y = ((q63_t) y0 * (0xFFFFF - fract)); - - /* Calculation of (y0 * (1-fract) + y1 * fract) and y is in 13.35 format */ - y += ((q63_t) y1 * (fract)); - - /* convert y to 1.15 format */ - return (q15_t) (y >> 20); - } - } - - - /** - * - * @brief Process function for the Q7 Linear Interpolation Function. - * @param[in] pYData pointer to Q7 Linear Interpolation table - * @param[in] x input sample to process - * @param[in] nValues number of table values - * @return y processed output sample. - * - * \par - * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. - * This function can support maximum of table size 2^12. - */ - static __INLINE q7_t arm_linear_interp_q7( - q7_t * pYData, - q31_t x, - uint32_t nValues) - { - q31_t y; /* output */ - q7_t y0, y1; /* Nearest output values */ - q31_t fract; /* fractional part */ - uint32_t index; /* Index to read nearest output values */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - if (x < 0) - { - return (pYData[0]); - } - index = (x >> 20) & 0xfff; - - if(index >= (nValues - 1)) - { - return (pYData[nValues - 1]); - } - else - { - /* 20 bits for the fractional part */ - /* fract is in 12.20 format */ - fract = (x & 0x000FFFFF); - - /* Read two nearest output values from the index and are in 1.7(q7) format */ - y0 = pYData[index]; - y1 = pYData[index + 1]; - - /* Calculation of y0 * (1-fract ) and y is in 13.27(q27) format */ - y = ((y0 * (0xFFFFF - fract))); - - /* Calculation of y1 * fract + y0 * (1-fract) and y is in 13.27(q27) format */ - y += (y1 * fract); - - /* convert y to 1.7(q7) format */ - return (q7_t) (y >> 20); - } - } - - /** - * @} end of LinearInterpolate group - */ - - /** - * @brief Fast approximation to the trigonometric sine function for floating-point data. - * @param[in] x input value in radians. - * @return sin(x). - */ - float32_t arm_sin_f32( - float32_t x); - - - /** - * @brief Fast approximation to the trigonometric sine function for Q31 data. - * @param[in] x Scaled input value in radians. - * @return sin(x). - */ - q31_t arm_sin_q31( - q31_t x); - - - /** - * @brief Fast approximation to the trigonometric sine function for Q15 data. - * @param[in] x Scaled input value in radians. - * @return sin(x). - */ - q15_t arm_sin_q15( - q15_t x); - - - /** - * @brief Fast approximation to the trigonometric cosine function for floating-point data. - * @param[in] x input value in radians. - * @return cos(x). - */ - float32_t arm_cos_f32( - float32_t x); - - - /** - * @brief Fast approximation to the trigonometric cosine function for Q31 data. - * @param[in] x Scaled input value in radians. - * @return cos(x). - */ - q31_t arm_cos_q31( - q31_t x); - - - /** - * @brief Fast approximation to the trigonometric cosine function for Q15 data. - * @param[in] x Scaled input value in radians. - * @return cos(x). - */ - q15_t arm_cos_q15( - q15_t x); - - - /** - * @ingroup groupFastMath - */ - - - /** - * @defgroup SQRT Square Root - * - * Computes the square root of a number. - * There are separate functions for Q15, Q31, and floating-point data types. - * The square root function is computed using the Newton-Raphson algorithm. - * This is an iterative algorithm of the form: - *
    -   *      x1 = x0 - f(x0)/f'(x0)
    -   * 
    - * where x1 is the current estimate, - * x0 is the previous estimate, and - * f'(x0) is the derivative of f() evaluated at x0. - * For the square root function, the algorithm reduces to: - *
    -   *     x0 = in/2                         [initial guess]
    -   *     x1 = 1/2 * ( x0 + in / x0)        [each iteration]
    -   * 
    - */ - - - /** - * @addtogroup SQRT - * @{ - */ - - /** - * @brief Floating-point square root function. - * @param[in] in input value. - * @param[out] pOut square root of input value. - * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if - * in is negative value and returns zero output for negative values. - */ - static __INLINE arm_status arm_sqrt_f32( - float32_t in, - float32_t * pOut) - { - if(in >= 0.0f) - { - -#if (__FPU_USED == 1) && defined ( __CC_ARM ) - *pOut = __sqrtf(in); -#elif (__FPU_USED == 1) && (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) - *pOut = __builtin_sqrtf(in); -#elif (__FPU_USED == 1) && defined(__GNUC__) - *pOut = __builtin_sqrtf(in); -#elif (__FPU_USED == 1) && defined ( __ICCARM__ ) && (__VER__ >= 6040000) - __ASM("VSQRT.F32 %0,%1" : "=t"(*pOut) : "t"(in)); -#else - *pOut = sqrtf(in); -#endif - - return (ARM_MATH_SUCCESS); - } - else - { - *pOut = 0.0f; - return (ARM_MATH_ARGUMENT_ERROR); - } - } - - - /** - * @brief Q31 square root function. - * @param[in] in input value. The range of the input value is [0 +1) or 0x00000000 to 0x7FFFFFFF. - * @param[out] pOut square root of input value. - * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if - * in is negative value and returns zero output for negative values. - */ - arm_status arm_sqrt_q31( - q31_t in, - q31_t * pOut); - - - /** - * @brief Q15 square root function. - * @param[in] in input value. The range of the input value is [0 +1) or 0x0000 to 0x7FFF. - * @param[out] pOut square root of input value. - * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if - * in is negative value and returns zero output for negative values. - */ - arm_status arm_sqrt_q15( - q15_t in, - q15_t * pOut); - - /** - * @} end of SQRT group - */ - - - /** - * @brief floating-point Circular write function. - */ - static __INLINE void arm_circularWrite_f32( - int32_t * circBuffer, - int32_t L, - uint16_t * writeOffset, - int32_t bufferInc, - const int32_t * src, - int32_t srcInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t wOffset; - - /* Copy the value of Index pointer that points - * to the current location where the input samples to be copied */ - wOffset = *writeOffset; - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the input sample to the circular buffer */ - circBuffer[wOffset] = *src; - - /* Update the input pointer */ - src += srcInc; - - /* Circularly update wOffset. Watch out for positive and negative value */ - wOffset += bufferInc; - if(wOffset >= L) - wOffset -= L; - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *writeOffset = (uint16_t)wOffset; - } - - - - /** - * @brief floating-point Circular Read function. - */ - static __INLINE void arm_circularRead_f32( - int32_t * circBuffer, - int32_t L, - int32_t * readOffset, - int32_t bufferInc, - int32_t * dst, - int32_t * dst_base, - int32_t dst_length, - int32_t dstInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t rOffset, dst_end; - - /* Copy the value of Index pointer that points - * to the current location from where the input samples to be read */ - rOffset = *readOffset; - dst_end = (int32_t) (dst_base + dst_length); - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the sample from the circular buffer to the destination buffer */ - *dst = circBuffer[rOffset]; - - /* Update the input pointer */ - dst += dstInc; - - if(dst == (int32_t *) dst_end) - { - dst = dst_base; - } - - /* Circularly update rOffset. Watch out for positive and negative value */ - rOffset += bufferInc; - - if(rOffset >= L) - { - rOffset -= L; - } - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *readOffset = rOffset; - } - - - /** - * @brief Q15 Circular write function. - */ - static __INLINE void arm_circularWrite_q15( - q15_t * circBuffer, - int32_t L, - uint16_t * writeOffset, - int32_t bufferInc, - const q15_t * src, - int32_t srcInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t wOffset; - - /* Copy the value of Index pointer that points - * to the current location where the input samples to be copied */ - wOffset = *writeOffset; - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the input sample to the circular buffer */ - circBuffer[wOffset] = *src; - - /* Update the input pointer */ - src += srcInc; - - /* Circularly update wOffset. Watch out for positive and negative value */ - wOffset += bufferInc; - if(wOffset >= L) - wOffset -= L; - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *writeOffset = (uint16_t)wOffset; - } - - - /** - * @brief Q15 Circular Read function. - */ - static __INLINE void arm_circularRead_q15( - q15_t * circBuffer, - int32_t L, - int32_t * readOffset, - int32_t bufferInc, - q15_t * dst, - q15_t * dst_base, - int32_t dst_length, - int32_t dstInc, - uint32_t blockSize) - { - uint32_t i = 0; - int32_t rOffset, dst_end; - - /* Copy the value of Index pointer that points - * to the current location from where the input samples to be read */ - rOffset = *readOffset; - - dst_end = (int32_t) (dst_base + dst_length); - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the sample from the circular buffer to the destination buffer */ - *dst = circBuffer[rOffset]; - - /* Update the input pointer */ - dst += dstInc; - - if(dst == (q15_t *) dst_end) - { - dst = dst_base; - } - - /* Circularly update wOffset. Watch out for positive and negative value */ - rOffset += bufferInc; - - if(rOffset >= L) - { - rOffset -= L; - } - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *readOffset = rOffset; - } - - - /** - * @brief Q7 Circular write function. - */ - static __INLINE void arm_circularWrite_q7( - q7_t * circBuffer, - int32_t L, - uint16_t * writeOffset, - int32_t bufferInc, - const q7_t * src, - int32_t srcInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t wOffset; - - /* Copy the value of Index pointer that points - * to the current location where the input samples to be copied */ - wOffset = *writeOffset; - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the input sample to the circular buffer */ - circBuffer[wOffset] = *src; - - /* Update the input pointer */ - src += srcInc; - - /* Circularly update wOffset. Watch out for positive and negative value */ - wOffset += bufferInc; - if(wOffset >= L) - wOffset -= L; - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *writeOffset = (uint16_t)wOffset; - } - - - /** - * @brief Q7 Circular Read function. - */ - static __INLINE void arm_circularRead_q7( - q7_t * circBuffer, - int32_t L, - int32_t * readOffset, - int32_t bufferInc, - q7_t * dst, - q7_t * dst_base, - int32_t dst_length, - int32_t dstInc, - uint32_t blockSize) - { - uint32_t i = 0; - int32_t rOffset, dst_end; - - /* Copy the value of Index pointer that points - * to the current location from where the input samples to be read */ - rOffset = *readOffset; - - dst_end = (int32_t) (dst_base + dst_length); - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the sample from the circular buffer to the destination buffer */ - *dst = circBuffer[rOffset]; - - /* Update the input pointer */ - dst += dstInc; - - if(dst == (q7_t *) dst_end) - { - dst = dst_base; - } - - /* Circularly update rOffset. Watch out for positive and negative value */ - rOffset += bufferInc; - - if(rOffset >= L) - { - rOffset -= L; - } - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *readOffset = rOffset; - } - - - /** - * @brief Sum of the squares of the elements of a Q31 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_power_q31( - q31_t * pSrc, - uint32_t blockSize, - q63_t * pResult); - - - /** - * @brief Sum of the squares of the elements of a floating-point vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_power_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - - /** - * @brief Sum of the squares of the elements of a Q15 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_power_q15( - q15_t * pSrc, - uint32_t blockSize, - q63_t * pResult); - - - /** - * @brief Sum of the squares of the elements of a Q7 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_power_q7( - q7_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - - /** - * @brief Mean value of a Q7 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_mean_q7( - q7_t * pSrc, - uint32_t blockSize, - q7_t * pResult); - - - /** - * @brief Mean value of a Q15 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_mean_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - - /** - * @brief Mean value of a Q31 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_mean_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - - /** - * @brief Mean value of a floating-point vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_mean_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - - /** - * @brief Variance of the elements of a floating-point vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_var_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - - /** - * @brief Variance of the elements of a Q31 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_var_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - - /** - * @brief Variance of the elements of a Q15 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_var_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - - /** - * @brief Root Mean Square of the elements of a floating-point vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_rms_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - - /** - * @brief Root Mean Square of the elements of a Q31 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_rms_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - - /** - * @brief Root Mean Square of the elements of a Q15 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_rms_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - - /** - * @brief Standard deviation of the elements of a floating-point vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_std_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - - /** - * @brief Standard deviation of the elements of a Q31 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_std_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - - /** - * @brief Standard deviation of the elements of a Q15 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output value. - */ - void arm_std_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - - /** - * @brief Floating-point complex magnitude - * @param[in] pSrc points to the complex input vector - * @param[out] pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - */ - void arm_cmplx_mag_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q31 complex magnitude - * @param[in] pSrc points to the complex input vector - * @param[out] pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - */ - void arm_cmplx_mag_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q15 complex magnitude - * @param[in] pSrc points to the complex input vector - * @param[out] pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - */ - void arm_cmplx_mag_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q15 complex dot product - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] numSamples number of complex samples in each vector - * @param[out] realResult real part of the result returned here - * @param[out] imagResult imaginary part of the result returned here - */ - void arm_cmplx_dot_prod_q15( - q15_t * pSrcA, - q15_t * pSrcB, - uint32_t numSamples, - q31_t * realResult, - q31_t * imagResult); - - - /** - * @brief Q31 complex dot product - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] numSamples number of complex samples in each vector - * @param[out] realResult real part of the result returned here - * @param[out] imagResult imaginary part of the result returned here - */ - void arm_cmplx_dot_prod_q31( - q31_t * pSrcA, - q31_t * pSrcB, - uint32_t numSamples, - q63_t * realResult, - q63_t * imagResult); - - - /** - * @brief Floating-point complex dot product - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[in] numSamples number of complex samples in each vector - * @param[out] realResult real part of the result returned here - * @param[out] imagResult imaginary part of the result returned here - */ - void arm_cmplx_dot_prod_f32( - float32_t * pSrcA, - float32_t * pSrcB, - uint32_t numSamples, - float32_t * realResult, - float32_t * imagResult); - - - /** - * @brief Q15 complex-by-real multiplication - * @param[in] pSrcCmplx points to the complex input vector - * @param[in] pSrcReal points to the real input vector - * @param[out] pCmplxDst points to the complex output vector - * @param[in] numSamples number of samples in each vector - */ - void arm_cmplx_mult_real_q15( - q15_t * pSrcCmplx, - q15_t * pSrcReal, - q15_t * pCmplxDst, - uint32_t numSamples); - - - /** - * @brief Q31 complex-by-real multiplication - * @param[in] pSrcCmplx points to the complex input vector - * @param[in] pSrcReal points to the real input vector - * @param[out] pCmplxDst points to the complex output vector - * @param[in] numSamples number of samples in each vector - */ - void arm_cmplx_mult_real_q31( - q31_t * pSrcCmplx, - q31_t * pSrcReal, - q31_t * pCmplxDst, - uint32_t numSamples); - - - /** - * @brief Floating-point complex-by-real multiplication - * @param[in] pSrcCmplx points to the complex input vector - * @param[in] pSrcReal points to the real input vector - * @param[out] pCmplxDst points to the complex output vector - * @param[in] numSamples number of samples in each vector - */ - void arm_cmplx_mult_real_f32( - float32_t * pSrcCmplx, - float32_t * pSrcReal, - float32_t * pCmplxDst, - uint32_t numSamples); - - - /** - * @brief Minimum value of a Q7 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] result is output pointer - * @param[in] index is the array index of the minimum value in the input buffer. - */ - void arm_min_q7( - q7_t * pSrc, - uint32_t blockSize, - q7_t * result, - uint32_t * index); - - - /** - * @brief Minimum value of a Q15 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output pointer - * @param[in] pIndex is the array index of the minimum value in the input buffer. - */ - void arm_min_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult, - uint32_t * pIndex); - - - /** - * @brief Minimum value of a Q31 vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output pointer - * @param[out] pIndex is the array index of the minimum value in the input buffer. - */ - void arm_min_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult, - uint32_t * pIndex); - - - /** - * @brief Minimum value of a floating-point vector. - * @param[in] pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] pResult is output pointer - * @param[out] pIndex is the array index of the minimum value in the input buffer. - */ - void arm_min_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult, - uint32_t * pIndex); - - -/** - * @brief Maximum value of a Q7 vector. - * @param[in] pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] pResult maximum value returned here - * @param[out] pIndex index of maximum value returned here - */ - void arm_max_q7( - q7_t * pSrc, - uint32_t blockSize, - q7_t * pResult, - uint32_t * pIndex); - - -/** - * @brief Maximum value of a Q15 vector. - * @param[in] pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] pResult maximum value returned here - * @param[out] pIndex index of maximum value returned here - */ - void arm_max_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult, - uint32_t * pIndex); - - -/** - * @brief Maximum value of a Q31 vector. - * @param[in] pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] pResult maximum value returned here - * @param[out] pIndex index of maximum value returned here - */ - void arm_max_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult, - uint32_t * pIndex); - - -/** - * @brief Maximum value of a floating-point vector. - * @param[in] pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] pResult maximum value returned here - * @param[out] pIndex index of maximum value returned here - */ - void arm_max_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult, - uint32_t * pIndex); - - - /** - * @brief Q15 complex-by-complex multiplication - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - */ - void arm_cmplx_mult_cmplx_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t numSamples); - - - /** - * @brief Q31 complex-by-complex multiplication - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - */ - void arm_cmplx_mult_cmplx_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t numSamples); - - - /** - * @brief Floating-point complex-by-complex multiplication - * @param[in] pSrcA points to the first input vector - * @param[in] pSrcB points to the second input vector - * @param[out] pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - */ - void arm_cmplx_mult_cmplx_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t numSamples); - - - /** - * @brief Converts the elements of the floating-point vector to Q31 vector. - * @param[in] pSrc points to the floating-point input vector - * @param[out] pDst points to the Q31 output vector - * @param[in] blockSize length of the input vector - */ - void arm_float_to_q31( - float32_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the floating-point vector to Q15 vector. - * @param[in] pSrc points to the floating-point input vector - * @param[out] pDst points to the Q15 output vector - * @param[in] blockSize length of the input vector - */ - void arm_float_to_q15( - float32_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the floating-point vector to Q7 vector. - * @param[in] pSrc points to the floating-point input vector - * @param[out] pDst points to the Q7 output vector - * @param[in] blockSize length of the input vector - */ - void arm_float_to_q7( - float32_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q31 vector to Q15 vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q31_to_q15( - q31_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q31 vector to Q7 vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q31_to_q7( - q31_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q15 vector to floating-point vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q15_to_float( - q15_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q15 vector to Q31 vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q15_to_q31( - q15_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q15 vector to Q7 vector. - * @param[in] pSrc is input pointer - * @param[out] pDst is output pointer - * @param[in] blockSize is the number of samples to process - */ - void arm_q15_to_q7( - q15_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @ingroup groupInterpolation - */ - - /** - * @defgroup BilinearInterpolate Bilinear Interpolation - * - * Bilinear interpolation is an extension of linear interpolation applied to a two dimensional grid. - * The underlying function f(x, y) is sampled on a regular grid and the interpolation process - * determines values between the grid points. - * Bilinear interpolation is equivalent to two step linear interpolation, first in the x-dimension and then in the y-dimension. - * Bilinear interpolation is often used in image processing to rescale images. - * The CMSIS DSP library provides bilinear interpolation functions for Q7, Q15, Q31, and floating-point data types. - * - * Algorithm - * \par - * The instance structure used by the bilinear interpolation functions describes a two dimensional data table. - * For floating-point, the instance structure is defined as: - *
    -   *   typedef struct
    -   *   {
    -   *     uint16_t numRows;
    -   *     uint16_t numCols;
    -   *     float32_t *pData;
    -   * } arm_bilinear_interp_instance_f32;
    -   * 
    - * - * \par - * where numRows specifies the number of rows in the table; - * numCols specifies the number of columns in the table; - * and pData points to an array of size numRows*numCols values. - * The data table pTable is organized in row order and the supplied data values fall on integer indexes. - * That is, table element (x,y) is located at pTable[x + y*numCols] where x and y are integers. - * - * \par - * Let (x, y) specify the desired interpolation point. Then define: - *
    -   *     XF = floor(x)
    -   *     YF = floor(y)
    -   * 
    - * \par - * The interpolated output point is computed as: - *
    -   *  f(x, y) = f(XF, YF) * (1-(x-XF)) * (1-(y-YF))
    -   *           + f(XF+1, YF) * (x-XF)*(1-(y-YF))
    -   *           + f(XF, YF+1) * (1-(x-XF))*(y-YF)
    -   *           + f(XF+1, YF+1) * (x-XF)*(y-YF)
    -   * 
    - * Note that the coordinates (x, y) contain integer and fractional components. - * The integer components specify which portion of the table to use while the - * fractional components control the interpolation processor. - * - * \par - * if (x,y) are outside of the table boundary, Bilinear interpolation returns zero output. - */ - - /** - * @addtogroup BilinearInterpolate - * @{ - */ - - - /** - * - * @brief Floating-point bilinear interpolation. - * @param[in,out] S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate. - * @param[in] Y interpolation coordinate. - * @return out interpolated value. - */ - static __INLINE float32_t arm_bilinear_interp_f32( - const arm_bilinear_interp_instance_f32 * S, - float32_t X, - float32_t Y) - { - float32_t out; - float32_t f00, f01, f10, f11; - float32_t *pData = S->pData; - int32_t xIndex, yIndex, index; - float32_t xdiff, ydiff; - float32_t b1, b2, b3, b4; - - xIndex = (int32_t) X; - yIndex = (int32_t) Y; - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(xIndex < 0 || xIndex > (S->numRows - 1) || yIndex < 0 || yIndex > (S->numCols - 1)) - { - return (0); - } - - /* Calculation of index for two nearest points in X-direction */ - index = (xIndex - 1) + (yIndex - 1) * S->numCols; - - - /* Read two nearest points in X-direction */ - f00 = pData[index]; - f01 = pData[index + 1]; - - /* Calculation of index for two nearest points in Y-direction */ - index = (xIndex - 1) + (yIndex) * S->numCols; - - - /* Read two nearest points in Y-direction */ - f10 = pData[index]; - f11 = pData[index + 1]; - - /* Calculation of intermediate values */ - b1 = f00; - b2 = f01 - f00; - b3 = f10 - f00; - b4 = f00 - f01 - f10 + f11; - - /* Calculation of fractional part in X */ - xdiff = X - xIndex; - - /* Calculation of fractional part in Y */ - ydiff = Y - yIndex; - - /* Calculation of bi-linear interpolated output */ - out = b1 + b2 * xdiff + b3 * ydiff + b4 * xdiff * ydiff; - - /* return to application */ - return (out); - } - - - /** - * - * @brief Q31 bilinear interpolation. - * @param[in,out] S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate in 12.20 format. - * @param[in] Y interpolation coordinate in 12.20 format. - * @return out interpolated value. - */ - static __INLINE q31_t arm_bilinear_interp_q31( - arm_bilinear_interp_instance_q31 * S, - q31_t X, - q31_t Y) - { - q31_t out; /* Temporary output */ - q31_t acc = 0; /* output */ - q31_t xfract, yfract; /* X, Y fractional parts */ - q31_t x1, x2, y1, y2; /* Nearest output values */ - int32_t rI, cI; /* Row and column indices */ - q31_t *pYData = S->pData; /* pointer to output table values */ - uint32_t nCols = S->numCols; /* num of rows */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - rI = ((X & (q31_t)0xFFF00000) >> 20); - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - cI = ((Y & (q31_t)0xFFF00000) >> 20); - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) - { - return (0); - } - - /* 20 bits for the fractional part */ - /* shift left xfract by 11 to keep 1.31 format */ - xfract = (X & 0x000FFFFF) << 11u; - - /* Read two nearest output values from the index */ - x1 = pYData[(rI) + (int32_t)nCols * (cI) ]; - x2 = pYData[(rI) + (int32_t)nCols * (cI) + 1]; - - /* 20 bits for the fractional part */ - /* shift left yfract by 11 to keep 1.31 format */ - yfract = (Y & 0x000FFFFF) << 11u; - - /* Read two nearest output values from the index */ - y1 = pYData[(rI) + (int32_t)nCols * (cI + 1) ]; - y2 = pYData[(rI) + (int32_t)nCols * (cI + 1) + 1]; - - /* Calculation of x1 * (1-xfract ) * (1-yfract) and acc is in 3.29(q29) format */ - out = ((q31_t) (((q63_t) x1 * (0x7FFFFFFF - xfract)) >> 32)); - acc = ((q31_t) (((q63_t) out * (0x7FFFFFFF - yfract)) >> 32)); - - /* x2 * (xfract) * (1-yfract) in 3.29(q29) and adding to acc */ - out = ((q31_t) ((q63_t) x2 * (0x7FFFFFFF - yfract) >> 32)); - acc += ((q31_t) ((q63_t) out * (xfract) >> 32)); - - /* y1 * (1 - xfract) * (yfract) in 3.29(q29) and adding to acc */ - out = ((q31_t) ((q63_t) y1 * (0x7FFFFFFF - xfract) >> 32)); - acc += ((q31_t) ((q63_t) out * (yfract) >> 32)); - - /* y2 * (xfract) * (yfract) in 3.29(q29) and adding to acc */ - out = ((q31_t) ((q63_t) y2 * (xfract) >> 32)); - acc += ((q31_t) ((q63_t) out * (yfract) >> 32)); - - /* Convert acc to 1.31(q31) format */ - return ((q31_t)(acc << 2)); - } - - - /** - * @brief Q15 bilinear interpolation. - * @param[in,out] S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate in 12.20 format. - * @param[in] Y interpolation coordinate in 12.20 format. - * @return out interpolated value. - */ - static __INLINE q15_t arm_bilinear_interp_q15( - arm_bilinear_interp_instance_q15 * S, - q31_t X, - q31_t Y) - { - q63_t acc = 0; /* output */ - q31_t out; /* Temporary output */ - q15_t x1, x2, y1, y2; /* Nearest output values */ - q31_t xfract, yfract; /* X, Y fractional parts */ - int32_t rI, cI; /* Row and column indices */ - q15_t *pYData = S->pData; /* pointer to output table values */ - uint32_t nCols = S->numCols; /* num of rows */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - rI = ((X & (q31_t)0xFFF00000) >> 20); - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - cI = ((Y & (q31_t)0xFFF00000) >> 20); - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) - { - return (0); - } - - /* 20 bits for the fractional part */ - /* xfract should be in 12.20 format */ - xfract = (X & 0x000FFFFF); - - /* Read two nearest output values from the index */ - x1 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI) ]; - x2 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI) + 1]; - - /* 20 bits for the fractional part */ - /* yfract should be in 12.20 format */ - yfract = (Y & 0x000FFFFF); - - /* Read two nearest output values from the index */ - y1 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI + 1) ]; - y2 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI + 1) + 1]; - - /* Calculation of x1 * (1-xfract ) * (1-yfract) and acc is in 13.51 format */ - - /* x1 is in 1.15(q15), xfract in 12.20 format and out is in 13.35 format */ - /* convert 13.35 to 13.31 by right shifting and out is in 1.31 */ - out = (q31_t) (((q63_t) x1 * (0xFFFFF - xfract)) >> 4u); - acc = ((q63_t) out * (0xFFFFF - yfract)); - - /* x2 * (xfract) * (1-yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) x2 * (0xFFFFF - yfract)) >> 4u); - acc += ((q63_t) out * (xfract)); - - /* y1 * (1 - xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y1 * (0xFFFFF - xfract)) >> 4u); - acc += ((q63_t) out * (yfract)); - - /* y2 * (xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y2 * (xfract)) >> 4u); - acc += ((q63_t) out * (yfract)); - - /* acc is in 13.51 format and down shift acc by 36 times */ - /* Convert out to 1.15 format */ - return ((q15_t)(acc >> 36)); - } - - - /** - * @brief Q7 bilinear interpolation. - * @param[in,out] S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate in 12.20 format. - * @param[in] Y interpolation coordinate in 12.20 format. - * @return out interpolated value. - */ - static __INLINE q7_t arm_bilinear_interp_q7( - arm_bilinear_interp_instance_q7 * S, - q31_t X, - q31_t Y) - { - q63_t acc = 0; /* output */ - q31_t out; /* Temporary output */ - q31_t xfract, yfract; /* X, Y fractional parts */ - q7_t x1, x2, y1, y2; /* Nearest output values */ - int32_t rI, cI; /* Row and column indices */ - q7_t *pYData = S->pData; /* pointer to output table values */ - uint32_t nCols = S->numCols; /* num of rows */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - rI = ((X & (q31_t)0xFFF00000) >> 20); - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - cI = ((Y & (q31_t)0xFFF00000) >> 20); - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) - { - return (0); - } - - /* 20 bits for the fractional part */ - /* xfract should be in 12.20 format */ - xfract = (X & (q31_t)0x000FFFFF); - - /* Read two nearest output values from the index */ - x1 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI) ]; - x2 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI) + 1]; - - /* 20 bits for the fractional part */ - /* yfract should be in 12.20 format */ - yfract = (Y & (q31_t)0x000FFFFF); - - /* Read two nearest output values from the index */ - y1 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI + 1) ]; - y2 = pYData[((uint32_t)rI) + nCols * ((uint32_t)cI + 1) + 1]; - - /* Calculation of x1 * (1-xfract ) * (1-yfract) and acc is in 16.47 format */ - out = ((x1 * (0xFFFFF - xfract))); - acc = (((q63_t) out * (0xFFFFF - yfract))); - - /* x2 * (xfract) * (1-yfract) in 2.22 and adding to acc */ - out = ((x2 * (0xFFFFF - yfract))); - acc += (((q63_t) out * (xfract))); - - /* y1 * (1 - xfract) * (yfract) in 2.22 and adding to acc */ - out = ((y1 * (0xFFFFF - xfract))); - acc += (((q63_t) out * (yfract))); - - /* y2 * (xfract) * (yfract) in 2.22 and adding to acc */ - out = ((y2 * (yfract))); - acc += (((q63_t) out * (xfract))); - - /* acc in 16.47 format and down shift by 40 to convert to 1.7 format */ - return ((q7_t)(acc >> 40)); - } - - /** - * @} end of BilinearInterpolate group - */ - - -/* SMMLAR */ -#define multAcc_32x32_keep32_R(a, x, y) \ - a = (q31_t) (((((q63_t) a) << 32) + ((q63_t) x * y) + 0x80000000LL ) >> 32) - -/* SMMLSR */ -#define multSub_32x32_keep32_R(a, x, y) \ - a = (q31_t) (((((q63_t) a) << 32) - ((q63_t) x * y) + 0x80000000LL ) >> 32) - -/* SMMULR */ -#define mult_32x32_keep32_R(a, x, y) \ - a = (q31_t) (((q63_t) x * y + 0x80000000LL ) >> 32) - -/* SMMLA */ -#define multAcc_32x32_keep32(a, x, y) \ - a += (q31_t) (((q63_t) x * y) >> 32) - -/* SMMLS */ -#define multSub_32x32_keep32(a, x, y) \ - a -= (q31_t) (((q63_t) x * y) >> 32) - -/* SMMUL */ -#define mult_32x32_keep32(a, x, y) \ - a = (q31_t) (((q63_t) x * y ) >> 32) - - -#if defined ( __CC_ARM ) - /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) - #define LOW_OPTIMIZATION_ENTER \ - _Pragma ("push") \ - _Pragma ("O1") - #else - #define LOW_OPTIMIZATION_ENTER - #endif - - /* Exit low optimization region - place directly after end of function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) - #define LOW_OPTIMIZATION_EXIT \ - _Pragma ("pop") - #else - #define LOW_OPTIMIZATION_EXIT - #endif - - /* Enter low optimization region - place directly above function definition */ - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - - /* Exit low optimization region - place directly after end of function definition */ - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define LOW_OPTIMIZATION_ENTER - #define LOW_OPTIMIZATION_EXIT - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__GNUC__) - #define LOW_OPTIMIZATION_ENTER __attribute__(( optimize("-O1") )) - #define LOW_OPTIMIZATION_EXIT - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__ICCARM__) - /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) - #define LOW_OPTIMIZATION_ENTER \ - _Pragma ("optimize=low") - #else - #define LOW_OPTIMIZATION_ENTER - #endif - - /* Exit low optimization region - place directly after end of function definition */ - #define LOW_OPTIMIZATION_EXIT - - /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER \ - _Pragma ("optimize=low") - #else - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - #endif - - /* Exit low optimization region - place directly after end of function definition */ - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__CSMC__) - #define LOW_OPTIMIZATION_ENTER - #define LOW_OPTIMIZATION_EXIT - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__TASKING__) - #define LOW_OPTIMIZATION_ENTER - #define LOW_OPTIMIZATION_EXIT - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#endif - - -#ifdef __cplusplus -} -#endif - - -#if defined ( __GNUC__ ) -#pragma GCC diagnostic pop -#endif - -#endif /* _ARM_MATH_H */ - -/** - * - * End of file. - */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/cmsis_armcc.h b/platform/robert/boot/vendor/CMSIS/Include/cmsis_armcc.h deleted file mode 100644 index f2bb66a09a..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/cmsis_armcc.h +++ /dev/null @@ -1,734 +0,0 @@ -/**************************************************************************//** - * @file cmsis_armcc.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#ifndef __CMSIS_ARMCC_H -#define __CMSIS_ARMCC_H - - -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677) - #error "Please use ARM Compiler Toolchain V4.0.677 or later!" -#endif - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ - */ - -/* intrinsic void __enable_irq(); */ -/* intrinsic void __disable_irq(); */ - -/** - \brief Get Control Register - \details Returns the content of the Control Register. - \return Control Register value - */ -__STATIC_INLINE uint32_t __get_CONTROL(void) -{ - register uint32_t __regControl __ASM("control"); - return(__regControl); -} - - -/** - \brief Set Control Register - \details Writes the given value to the Control Register. - \param [in] control Control Register value to set - */ -__STATIC_INLINE void __set_CONTROL(uint32_t control) -{ - register uint32_t __regControl __ASM("control"); - __regControl = control; -} - - -/** - \brief Get IPSR Register - \details Returns the content of the IPSR Register. - \return IPSR Register value - */ -__STATIC_INLINE uint32_t __get_IPSR(void) -{ - register uint32_t __regIPSR __ASM("ipsr"); - return(__regIPSR); -} - - -/** - \brief Get APSR Register - \details Returns the content of the APSR Register. - \return APSR Register value - */ -__STATIC_INLINE uint32_t __get_APSR(void) -{ - register uint32_t __regAPSR __ASM("apsr"); - return(__regAPSR); -} - - -/** - \brief Get xPSR Register - \details Returns the content of the xPSR Register. - \return xPSR Register value - */ -__STATIC_INLINE uint32_t __get_xPSR(void) -{ - register uint32_t __regXPSR __ASM("xpsr"); - return(__regXPSR); -} - - -/** - \brief Get Process Stack Pointer - \details Returns the current value of the Process Stack Pointer (PSP). - \return PSP Register value - */ -__STATIC_INLINE uint32_t __get_PSP(void) -{ - register uint32_t __regProcessStackPointer __ASM("psp"); - return(__regProcessStackPointer); -} - - -/** - \brief Set Process Stack Pointer - \details Assigns the given value to the Process Stack Pointer (PSP). - \param [in] topOfProcStack Process Stack Pointer value to set - */ -__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) -{ - register uint32_t __regProcessStackPointer __ASM("psp"); - __regProcessStackPointer = topOfProcStack; -} - - -/** - \brief Get Main Stack Pointer - \details Returns the current value of the Main Stack Pointer (MSP). - \return MSP Register value - */ -__STATIC_INLINE uint32_t __get_MSP(void) -{ - register uint32_t __regMainStackPointer __ASM("msp"); - return(__regMainStackPointer); -} - - -/** - \brief Set Main Stack Pointer - \details Assigns the given value to the Main Stack Pointer (MSP). - \param [in] topOfMainStack Main Stack Pointer value to set - */ -__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) -{ - register uint32_t __regMainStackPointer __ASM("msp"); - __regMainStackPointer = topOfMainStack; -} - - -/** - \brief Get Priority Mask - \details Returns the current state of the priority mask bit from the Priority Mask Register. - \return Priority Mask value - */ -__STATIC_INLINE uint32_t __get_PRIMASK(void) -{ - register uint32_t __regPriMask __ASM("primask"); - return(__regPriMask); -} - - -/** - \brief Set Priority Mask - \details Assigns the given value to the Priority Mask Register. - \param [in] priMask Priority Mask - */ -__STATIC_INLINE void __set_PRIMASK(uint32_t priMask) -{ - register uint32_t __regPriMask __ASM("primask"); - __regPriMask = (priMask); -} - - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - -/** - \brief Enable FIQ - \details Enables FIQ interrupts by clearing the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -#define __enable_fault_irq __enable_fiq - - -/** - \brief Disable FIQ - \details Disables FIQ interrupts by setting the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -#define __disable_fault_irq __disable_fiq - - -/** - \brief Get Base Priority - \details Returns the current value of the Base Priority register. - \return Base Priority register value - */ -__STATIC_INLINE uint32_t __get_BASEPRI(void) -{ - register uint32_t __regBasePri __ASM("basepri"); - return(__regBasePri); -} - - -/** - \brief Set Base Priority - \details Assigns the given value to the Base Priority register. - \param [in] basePri Base Priority value to set - */ -__STATIC_INLINE void __set_BASEPRI(uint32_t basePri) -{ - register uint32_t __regBasePri __ASM("basepri"); - __regBasePri = (basePri & 0xFFU); -} - - -/** - \brief Set Base Priority with condition - \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__STATIC_INLINE void __set_BASEPRI_MAX(uint32_t basePri) -{ - register uint32_t __regBasePriMax __ASM("basepri_max"); - __regBasePriMax = (basePri & 0xFFU); -} - - -/** - \brief Get Fault Mask - \details Returns the current value of the Fault Mask register. - \return Fault Mask register value - */ -__STATIC_INLINE uint32_t __get_FAULTMASK(void) -{ - register uint32_t __regFaultMask __ASM("faultmask"); - return(__regFaultMask); -} - - -/** - \brief Set Fault Mask - \details Assigns the given value to the Fault Mask register. - \param [in] faultMask Fault Mask value to set - */ -__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) -{ - register uint32_t __regFaultMask __ASM("faultmask"); - __regFaultMask = (faultMask & (uint32_t)1); -} - -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ - - -#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) - -/** - \brief Get FPSCR - \details Returns the current value of the Floating Point Status/Control register. - \return Floating Point Status/Control register value - */ -__STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - register uint32_t __regfpscr __ASM("fpscr"); - return(__regfpscr); -#else - return(0U); -#endif -} - - -/** - \brief Set FPSCR - \details Assigns the given value to the Floating Point Status/Control register. - \param [in] fpscr Floating Point Status/Control value to set - */ -__STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - register uint32_t __regfpscr __ASM("fpscr"); - __regfpscr = (fpscr); -#endif -} - -#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ - - - -/*@} end of CMSIS_Core_RegAccFunctions */ - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/** - \brief No Operation - \details No Operation does nothing. This instruction can be used for code alignment purposes. - */ -#define __NOP __nop - - -/** - \brief Wait For Interrupt - \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. - */ -#define __WFI __wfi - - -/** - \brief Wait For Event - \details Wait For Event is a hint instruction that permits the processor to enter - a low-power state until one of a number of events occurs. - */ -#define __WFE __wfe - - -/** - \brief Send Event - \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. - */ -#define __SEV __sev - - -/** - \brief Instruction Synchronization Barrier - \details Instruction Synchronization Barrier flushes the pipeline in the processor, - so that all instructions following the ISB are fetched from cache or memory, - after the instruction has been completed. - */ -#define __ISB() do {\ - __schedule_barrier();\ - __isb(0xF);\ - __schedule_barrier();\ - } while (0U) - -/** - \brief Data Synchronization Barrier - \details Acts as a special kind of Data Memory Barrier. - It completes when all explicit memory accesses before this instruction complete. - */ -#define __DSB() do {\ - __schedule_barrier();\ - __dsb(0xF);\ - __schedule_barrier();\ - } while (0U) - -/** - \brief Data Memory Barrier - \details Ensures the apparent order of the explicit memory operations before - and after the instruction, without ensuring their completion. - */ -#define __DMB() do {\ - __schedule_barrier();\ - __dmb(0xF);\ - __schedule_barrier();\ - } while (0U) - -/** - \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. - \param [in] value Value to reverse - \return Reversed value - */ -#define __REV __rev - - -/** - \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. - \param [in] value Value to reverse - \return Reversed value - */ -#ifndef __NO_EMBEDDED_ASM -__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value) -{ - rev16 r0, r0 - bx lr -} -#endif - -/** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. - \param [in] value Value to reverse - \return Reversed value - */ -#ifndef __NO_EMBEDDED_ASM -__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value) -{ - revsh r0, r0 - bx lr -} -#endif - - -/** - \brief Rotate Right in unsigned value (32 bit) - \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] value Value to rotate - \param [in] value Number of Bits to rotate - \return Rotated value - */ -#define __ROR __ror - - -/** - \brief Breakpoint - \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. - */ -#define __BKPT(value) __breakpoint(value) - - -/** - \brief Reverse bit order of value - \details Reverses the bit order of the given value. - \param [in] value Value to reverse - \return Reversed value - */ -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - #define __RBIT __rbit -#else -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ - return(result); -} -#endif - - -/** - \brief Count leading zeros - \details Counts the number of leading zeros of a data value. - \param [in] value Value to count the leading zeros - \return number of leading zeros in value - */ -#define __CLZ __clz - - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - -/** - \brief LDR Exclusive (8 bit) - \details Executes a exclusive LDR instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) - #define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr)) -#else - #define __LDREXB(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint8_t ) __ldrex(ptr)) _Pragma("pop") -#endif - - -/** - \brief LDR Exclusive (16 bit) - \details Executes a exclusive LDR instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) - #define __LDREXH(ptr) ((uint16_t) __ldrex(ptr)) -#else - #define __LDREXH(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint16_t) __ldrex(ptr)) _Pragma("pop") -#endif - - -/** - \brief LDR Exclusive (32 bit) - \details Executes a exclusive LDR instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) - #define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr)) -#else - #define __LDREXW(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint32_t ) __ldrex(ptr)) _Pragma("pop") -#endif - - -/** - \brief STR Exclusive (8 bit) - \details Executes a exclusive STR instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) - #define __STREXB(value, ptr) __strex(value, ptr) -#else - #define __STREXB(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") -#endif - - -/** - \brief STR Exclusive (16 bit) - \details Executes a exclusive STR instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) - #define __STREXH(value, ptr) __strex(value, ptr) -#else - #define __STREXH(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") -#endif - - -/** - \brief STR Exclusive (32 bit) - \details Executes a exclusive STR instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) - #define __STREXW(value, ptr) __strex(value, ptr) -#else - #define __STREXW(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") -#endif - - -/** - \brief Remove the exclusive lock - \details Removes the exclusive lock which is created by LDREX. - */ -#define __CLREX __clrex - - -/** - \brief Signed Saturate - \details Saturates a signed value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (1..32) - \return Saturated value - */ -#define __SSAT __ssat - - -/** - \brief Unsigned Saturate - \details Saturates an unsigned value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (0..31) - \return Saturated value - */ -#define __USAT __usat - - -/** - \brief Rotate Right with Extend (32 bit) - \details Moves each bit of a bitstring right by one bit. - The carry input is shifted in at the left end of the bitstring. - \param [in] value Value to rotate - \return Rotated value - */ -#ifndef __NO_EMBEDDED_ASM -__attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint32_t value) -{ - rrx r0, r0 - bx lr -} -#endif - - -/** - \brief LDRT Unprivileged (8 bit) - \details Executes a Unprivileged LDRT instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -#define __LDRBT(ptr) ((uint8_t ) __ldrt(ptr)) - - -/** - \brief LDRT Unprivileged (16 bit) - \details Executes a Unprivileged LDRT instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -#define __LDRHT(ptr) ((uint16_t) __ldrt(ptr)) - - -/** - \brief LDRT Unprivileged (32 bit) - \details Executes a Unprivileged LDRT instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -#define __LDRT(ptr) ((uint32_t ) __ldrt(ptr)) - - -/** - \brief STRT Unprivileged (8 bit) - \details Executes a Unprivileged STRT instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -#define __STRBT(value, ptr) __strt(value, ptr) - - -/** - \brief STRT Unprivileged (16 bit) - \details Executes a Unprivileged STRT instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -#define __STRHT(value, ptr) __strt(value, ptr) - - -/** - \brief STRT Unprivileged (32 bit) - \details Executes a Unprivileged STRT instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -#define __STRT(value, ptr) __strt(value, ptr) - -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ - -#define __SADD8 __sadd8 -#define __QADD8 __qadd8 -#define __SHADD8 __shadd8 -#define __UADD8 __uadd8 -#define __UQADD8 __uqadd8 -#define __UHADD8 __uhadd8 -#define __SSUB8 __ssub8 -#define __QSUB8 __qsub8 -#define __SHSUB8 __shsub8 -#define __USUB8 __usub8 -#define __UQSUB8 __uqsub8 -#define __UHSUB8 __uhsub8 -#define __SADD16 __sadd16 -#define __QADD16 __qadd16 -#define __SHADD16 __shadd16 -#define __UADD16 __uadd16 -#define __UQADD16 __uqadd16 -#define __UHADD16 __uhadd16 -#define __SSUB16 __ssub16 -#define __QSUB16 __qsub16 -#define __SHSUB16 __shsub16 -#define __USUB16 __usub16 -#define __UQSUB16 __uqsub16 -#define __UHSUB16 __uhsub16 -#define __SASX __sasx -#define __QASX __qasx -#define __SHASX __shasx -#define __UASX __uasx -#define __UQASX __uqasx -#define __UHASX __uhasx -#define __SSAX __ssax -#define __QSAX __qsax -#define __SHSAX __shsax -#define __USAX __usax -#define __UQSAX __uqsax -#define __UHSAX __uhsax -#define __USAD8 __usad8 -#define __USADA8 __usada8 -#define __SSAT16 __ssat16 -#define __USAT16 __usat16 -#define __UXTB16 __uxtb16 -#define __UXTAB16 __uxtab16 -#define __SXTB16 __sxtb16 -#define __SXTAB16 __sxtab16 -#define __SMUAD __smuad -#define __SMUADX __smuadx -#define __SMLAD __smlad -#define __SMLADX __smladx -#define __SMLALD __smlald -#define __SMLALDX __smlaldx -#define __SMUSD __smusd -#define __SMUSDX __smusdx -#define __SMLSD __smlsd -#define __SMLSDX __smlsdx -#define __SMLSLD __smlsld -#define __SMLSLDX __smlsldx -#define __SEL __sel -#define __QADD __qadd -#define __QSUB __qsub - -#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ - ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) - -#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ - ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) - -#define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \ - ((int64_t)(ARG3) << 32U) ) >> 32U)) - -#endif /* (__CORTEX_M >= 0x04) */ -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#endif /* __CMSIS_ARMCC_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/cmsis_armcc_V6.h b/platform/robert/boot/vendor/CMSIS/Include/cmsis_armcc_V6.h deleted file mode 100644 index d714e9b059..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/cmsis_armcc_V6.h +++ /dev/null @@ -1,1800 +0,0 @@ -/**************************************************************************//** - * @file cmsis_armcc_V6.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#ifndef __CMSIS_ARMCC_V6_H -#define __CMSIS_ARMCC_V6_H - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ - */ - -/** - \brief Enable IRQ Interrupts - \details Enables IRQ interrupts by clearing the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __enable_irq(void) -{ - __ASM volatile ("cpsie i" : : : "memory"); -} - - -/** - \brief Disable IRQ Interrupts - \details Disables IRQ interrupts by setting the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __disable_irq(void) -{ - __ASM volatile ("cpsid i" : : : "memory"); -} - - -/** - \brief Get Control Register - \details Returns the content of the Control Register. - \return Control Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_CONTROL(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, control" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get Control Register (non-secure) - \details Returns the content of the non-secure Control Register when in secure mode. - \return non-secure Control Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_CONTROL_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Control Register - \details Writes the given value to the Control Register. - \param [in] control Control Register value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_CONTROL(uint32_t control) -{ - __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Control Register (non-secure) - \details Writes the given value to the non-secure Control Register when in secure state. - \param [in] control Control Register value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_CONTROL_NS(uint32_t control) -{ - __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); -} -#endif - - -/** - \brief Get IPSR Register - \details Returns the content of the IPSR Register. - \return IPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_IPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get IPSR Register (non-secure) - \details Returns the content of the non-secure IPSR Register when in secure state. - \return IPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_IPSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Get APSR Register - \details Returns the content of the APSR Register. - \return APSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_APSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get APSR Register (non-secure) - \details Returns the content of the non-secure APSR Register when in secure state. - \return APSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_APSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Get xPSR Register - \details Returns the content of the xPSR Register. - \return xPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_xPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get xPSR Register (non-secure) - \details Returns the content of the non-secure xPSR Register when in secure state. - \return xPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_xPSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Get Process Stack Pointer - \details Returns the current value of the Process Stack Pointer (PSP). - \return PSP Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psp" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get Process Stack Pointer (non-secure) - \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. - \return PSP Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSP_NS(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Process Stack Pointer - \details Assigns the given value to the Process Stack Pointer (PSP). - \param [in] topOfProcStack Process Stack Pointer value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) -{ - __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : "sp"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Process Stack Pointer (non-secure) - \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. - \param [in] topOfProcStack Process Stack Pointer value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) -{ - __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : "sp"); -} -#endif - - -/** - \brief Get Main Stack Pointer - \details Returns the current value of the Main Stack Pointer (MSP). - \return MSP Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_MSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msp" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get Main Stack Pointer (non-secure) - \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. - \return MSP Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSP_NS(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Main Stack Pointer - \details Assigns the given value to the Main Stack Pointer (MSP). - \param [in] topOfMainStack Main Stack Pointer value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) -{ - __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : "sp"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Main Stack Pointer (non-secure) - \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. - \param [in] topOfMainStack Main Stack Pointer value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) -{ - __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : "sp"); -} -#endif - - -/** - \brief Get Priority Mask - \details Returns the current state of the priority mask bit from the Priority Mask Register. - \return Priority Mask value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PRIMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, primask" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get Priority Mask (non-secure) - \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. - \return Priority Mask value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PRIMASK_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Priority Mask - \details Assigns the given value to the Priority Mask Register. - \param [in] priMask Priority Mask - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) -{ - __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Priority Mask (non-secure) - \details Assigns the given value to the non-secure Priority Mask Register when in secure state. - \param [in] priMask Priority Mask - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) -{ - __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); -} -#endif - - -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - -/** - \brief Enable FIQ - \details Enables FIQ interrupts by clearing the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __enable_fault_irq(void) -{ - __ASM volatile ("cpsie f" : : : "memory"); -} - - -/** - \brief Disable FIQ - \details Disables FIQ interrupts by setting the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __disable_fault_irq(void) -{ - __ASM volatile ("cpsid f" : : : "memory"); -} - - -/** - \brief Get Base Priority - \details Returns the current value of the Base Priority register. - \return Base Priority register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_BASEPRI(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, basepri" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get Base Priority (non-secure) - \details Returns the current value of the non-secure Base Priority register when in secure state. - \return Base Priority register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_BASEPRI_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Base Priority - \details Assigns the given value to the Base Priority register. - \param [in] basePri Base Priority value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_BASEPRI(uint32_t value) -{ - __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Base Priority (non-secure) - \details Assigns the given value to the non-secure Base Priority register when in secure state. - \param [in] basePri Base Priority value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_NS(uint32_t value) -{ - __ASM volatile ("MSR basepri_ns, %0" : : "r" (value) : "memory"); -} -#endif - - -/** - \brief Set Base Priority with condition - \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) -{ - __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Base Priority with condition (non_secure) - \details Assigns the given value to the non-secure Base Priority register when in secure state only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_MAX_NS(uint32_t value) -{ - __ASM volatile ("MSR basepri_max_ns, %0" : : "r" (value) : "memory"); -} -#endif - - -/** - \brief Get Fault Mask - \details Returns the current value of the Fault Mask register. - \return Fault Mask register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FAULTMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get Fault Mask (non-secure) - \details Returns the current value of the non-secure Fault Mask register when in secure state. - \return Fault Mask register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FAULTMASK_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Fault Mask - \details Assigns the given value to the Fault Mask register. - \param [in] faultMask Fault Mask value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) -{ - __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); -} - - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Fault Mask (non-secure) - \details Assigns the given value to the non-secure Fault Mask register when in secure state. - \param [in] faultMask Fault Mask value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) -{ - __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); -} -#endif - - -#endif /* ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ - - -#if (__ARM_ARCH_8M__ == 1U) - -/** - \brief Get Process Stack Pointer Limit - \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). - \return PSPLIM Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PSPLIM(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psplim" : "=r" (result) ); - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ -/** - \brief Get Process Stack Pointer Limit (non-secure) - \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. - \return PSPLIM Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSPLIM_NS(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Process Stack Pointer Limit - \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). - \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) -{ - __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); -} - - -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ -/** - \brief Set Process Stack Pointer (non-secure) - \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. - \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) -{ - __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); -} -#endif - - -/** - \brief Get Main Stack Pointer Limit - \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). - \return MSPLIM Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_MSPLIM(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msplim" : "=r" (result) ); - - return(result); -} - - -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ -/** - \brief Get Main Stack Pointer Limit (non-secure) - \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. - \return MSPLIM Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSPLIM_NS(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); - return(result); -} -#endif - - -/** - \brief Set Main Stack Pointer Limit - \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). - \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) -{ - __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); -} - - -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ -/** - \brief Set Main Stack Pointer Limit (non-secure) - \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. - \param [in] MainStackPtrLimit Main Stack Pointer value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) -{ - __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); -} -#endif - -#endif /* (__ARM_ARCH_8M__ == 1U) */ - - -#if ((__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=4 */ - -/** - \brief Get FPSCR - \details eturns the current value of the Floating Point Status/Control register. - \return Floating Point Status/Control register value - */ -#define __get_FPSCR __builtin_arm_get_fpscr -#if 0 -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} -#endif - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get FPSCR (non-secure) - \details Returns the current value of the non-secure Floating Point Status/Control register when in secure state. - \return Floating Point Status/Control register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FPSCR_NS(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMRS %0, fpscr_ns" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} -#endif - - -/** - \brief Set FPSCR - \details Assigns the given value to the Floating Point Status/Control register. - \param [in] fpscr Floating Point Status/Control value to set - */ -#define __set_FPSCR __builtin_arm_set_fpscr -#if 0 -__attribute__((always_inline)) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} -#endif - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set FPSCR (non-secure) - \details Assigns the given value to the non-secure Floating Point Status/Control register when in secure state. - \param [in] fpscr Floating Point Status/Control value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FPSCR_NS(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMSR fpscr_ns, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} -#endif - -#endif /* ((__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ - - - -/*@} end of CMSIS_Core_RegAccFunctions */ - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/* Define macros for porting to both thumb1 and thumb2. - * For thumb1, use low register (r0-r7), specified by constraint "l" - * Otherwise, use general registers, specified by constraint "r" */ -#if defined (__thumb__) && !defined (__thumb2__) -#define __CMSIS_GCC_OUT_REG(r) "=l" (r) -#define __CMSIS_GCC_USE_REG(r) "l" (r) -#else -#define __CMSIS_GCC_OUT_REG(r) "=r" (r) -#define __CMSIS_GCC_USE_REG(r) "r" (r) -#endif - -/** - \brief No Operation - \details No Operation does nothing. This instruction can be used for code alignment purposes. - */ -#define __NOP __builtin_arm_nop - -/** - \brief Wait For Interrupt - \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. - */ -#define __WFI __builtin_arm_wfi - - -/** - \brief Wait For Event - \details Wait For Event is a hint instruction that permits the processor to enter - a low-power state until one of a number of events occurs. - */ -#define __WFE __builtin_arm_wfe - - -/** - \brief Send Event - \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. - */ -#define __SEV __builtin_arm_sev - - -/** - \brief Instruction Synchronization Barrier - \details Instruction Synchronization Barrier flushes the pipeline in the processor, - so that all instructions following the ISB are fetched from cache or memory, - after the instruction has been completed. - */ -#define __ISB() __builtin_arm_isb(0xF); - -/** - \brief Data Synchronization Barrier - \details Acts as a special kind of Data Memory Barrier. - It completes when all explicit memory accesses before this instruction complete. - */ -#define __DSB() __builtin_arm_dsb(0xF); - - -/** - \brief Data Memory Barrier - \details Ensures the apparent order of the explicit memory operations before - and after the instruction, without ensuring their completion. - */ -#define __DMB() __builtin_arm_dmb(0xF); - - -/** - \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. - \param [in] value Value to reverse - \return Reversed value - */ -#define __REV __builtin_bswap32 - - -/** - \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. - \param [in] value Value to reverse - \return Reversed value - */ -#define __REV16 __builtin_bswap16 /* ToDo: ARMCC_V6: check if __builtin_bswap16 could be used */ -#if 0 -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} -#endif - - -/** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. - \param [in] value Value to reverse - \return Reversed value - */ - /* ToDo: ARMCC_V6: check if __builtin_bswap16 could be used */ -__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) -{ - int32_t result; - - __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief Rotate Right in unsigned value (32 bit) - \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] op1 Value to rotate - \param [in] op2 Number of Bits to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) -{ - return (op1 >> op2) | (op1 << (32U - op2)); -} - - -/** - \brief Breakpoint - \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. - */ -#define __BKPT(value) __ASM volatile ("bkpt "#value) - - -/** - \brief Reverse bit order of value - \details Reverses the bit order of the given value. - \param [in] value Value to reverse - \return Reversed value - */ - /* ToDo: ARMCC_V6: check if __builtin_arm_rbit is supported */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); -#else - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ -#endif - return(result); -} - - -/** - \brief Count leading zeros - \details Counts the number of leading zeros of a data value. - \param [in] value Value to count the leading zeros - \return number of leading zeros in value - */ -#define __CLZ __builtin_clz - - -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - -/** - \brief LDR Exclusive (8 bit) - \details Executes a exclusive LDR instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -#define __LDREXB (uint8_t)__builtin_arm_ldrex - - -/** - \brief LDR Exclusive (16 bit) - \details Executes a exclusive LDR instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -#define __LDREXH (uint16_t)__builtin_arm_ldrex - - -/** - \brief LDR Exclusive (32 bit) - \details Executes a exclusive LDR instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -#define __LDREXW (uint32_t)__builtin_arm_ldrex - - -/** - \brief STR Exclusive (8 bit) - \details Executes a exclusive STR instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#define __STREXB (uint32_t)__builtin_arm_strex - - -/** - \brief STR Exclusive (16 bit) - \details Executes a exclusive STR instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#define __STREXH (uint32_t)__builtin_arm_strex - - -/** - \brief STR Exclusive (32 bit) - \details Executes a exclusive STR instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#define __STREXW (uint32_t)__builtin_arm_strex - - -/** - \brief Remove the exclusive lock - \details Removes the exclusive lock which is created by LDREX. - */ -#define __CLREX __builtin_arm_clrex - - -/** - \brief Signed Saturate - \details Saturates a signed value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (1..32) - \return Saturated value - */ -/*#define __SSAT __builtin_arm_ssat*/ -#define __SSAT(ARG1,ARG2) \ -({ \ - int32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Unsigned Saturate - \details Saturates an unsigned value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (0..31) - \return Saturated value - */ -#define __USAT __builtin_arm_usat -#if 0 -#define __USAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) -#endif - - -/** - \brief Rotate Right with Extend (32 bit) - \details Moves each bit of a bitstring right by one bit. - The carry input is shifted in at the left end of the bitstring. - \param [in] value Value to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief LDRT Unprivileged (8 bit) - \details Executes a Unprivileged LDRT instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *ptr) -{ - uint32_t result; - - __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (16 bit) - \details Executes a Unprivileged LDRT instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *ptr) -{ - uint32_t result; - - __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (32 bit) - \details Executes a Unprivileged LDRT instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *ptr) -{ - uint32_t result; - - __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); - return(result); -} - - -/** - \brief STRT Unprivileged (8 bit) - \details Executes a Unprivileged STRT instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) -{ - __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (16 bit) - \details Executes a Unprivileged STRT instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) -{ - __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (32 bit) - \details Executes a Unprivileged STRT instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *ptr) -{ - __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); -} - -#endif /* ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ - - -#if (__ARM_ARCH_8M__ == 1U) - -/** - \brief Load-Acquire (8 bit) - \details Executes a LDAB instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDAB(volatile uint8_t *ptr) -{ - uint32_t result; - - __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint8_t) result); -} - - -/** - \brief Load-Acquire (16 bit) - \details Executes a LDAH instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDAH(volatile uint16_t *ptr) -{ - uint32_t result; - - __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint16_t) result); -} - - -/** - \brief Load-Acquire (32 bit) - \details Executes a LDA instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDA(volatile uint32_t *ptr) -{ - uint32_t result; - - __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); - return(result); -} - - -/** - \brief Store-Release (8 bit) - \details Executes a STLB instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STLB(uint8_t value, volatile uint8_t *ptr) -{ - __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); -} - - -/** - \brief Store-Release (16 bit) - \details Executes a STLH instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STLH(uint16_t value, volatile uint16_t *ptr) -{ - __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); -} - - -/** - \brief Store-Release (32 bit) - \details Executes a STL instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volatile uint32_t *ptr) -{ - __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); -} - - -/** - \brief Load-Acquire Exclusive (8 bit) - \details Executes a LDAB exclusive instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -#define __LDAEXB (uint8_t)__builtin_arm_ldaex - - -/** - \brief Load-Acquire Exclusive (16 bit) - \details Executes a LDAH exclusive instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -#define __LDAEXH (uint16_t)__builtin_arm_ldaex - - -/** - \brief Load-Acquire Exclusive (32 bit) - \details Executes a LDA exclusive instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -#define __LDAEX (uint32_t)__builtin_arm_ldaex - - -/** - \brief Store-Release Exclusive (8 bit) - \details Executes a STLB exclusive instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#define __STLEXB (uint32_t)__builtin_arm_stlex - - -/** - \brief Store-Release Exclusive (16 bit) - \details Executes a STLH exclusive instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#define __STLEXH (uint32_t)__builtin_arm_stlex - - -/** - \brief Store-Release Exclusive (32 bit) - \details Executes a STL exclusive instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -#define __STLEX (uint32_t)__builtin_arm_stlex - -#endif /* (__ARM_ARCH_8M__ == 1U) */ - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -#if (__ARM_FEATURE_DSP == 1U) /* ToDo: ARMCC_V6: This should be ARCH >= ARMv7-M + SIMD */ - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#define __SSAT16(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -#define __USAT16(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -#define __PKHBT(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -#define __PKHTB(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - if (ARG3 == 0) \ - __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ - else \ - __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) -{ - int32_t result; - - __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#endif /* (__ARM_FEATURE_DSP == 1U) */ -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#endif /* __CMSIS_ARMCC_V6_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/cmsis_gcc.h b/platform/robert/boot/vendor/CMSIS/Include/cmsis_gcc.h deleted file mode 100644 index d868f2e64b..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/cmsis_gcc.h +++ /dev/null @@ -1,1373 +0,0 @@ -/**************************************************************************//** - * @file cmsis_gcc.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#ifndef __CMSIS_GCC_H -#define __CMSIS_GCC_H - -/* ignore some GCC warnings */ -#if defined ( __GNUC__ ) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ - */ - -/** - \brief Enable IRQ Interrupts - \details Enables IRQ interrupts by clearing the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) -{ - __ASM volatile ("cpsie i" : : : "memory"); -} - - -/** - \brief Disable IRQ Interrupts - \details Disables IRQ interrupts by setting the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) -{ - __ASM volatile ("cpsid i" : : : "memory"); -} - - -/** - \brief Get Control Register - \details Returns the content of the Control Register. - \return Control Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, control" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Control Register - \details Writes the given value to the Control Register. - \param [in] control Control Register value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) -{ - __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); -} - - -/** - \brief Get IPSR Register - \details Returns the content of the IPSR Register. - \return IPSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get APSR Register - \details Returns the content of the APSR Register. - \return APSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get xPSR Register - \details Returns the content of the xPSR Register. - - \return xPSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get Process Stack Pointer - \details Returns the current value of the Process Stack Pointer (PSP). - \return PSP Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Process Stack Pointer - \details Assigns the given value to the Process Stack Pointer (PSP). - \param [in] topOfProcStack Process Stack Pointer value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) -{ - __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); -} - - -/** - \brief Get Main Stack Pointer - \details Returns the current value of the Main Stack Pointer (MSP). - \return MSP Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Main Stack Pointer - \details Assigns the given value to the Main Stack Pointer (MSP). - - \param [in] topOfMainStack Main Stack Pointer value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) -{ - __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); -} - - -/** - \brief Get Priority Mask - \details Returns the current state of the priority mask bit from the Priority Mask Register. - \return Priority Mask value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, primask" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Priority Mask - \details Assigns the given value to the Priority Mask Register. - \param [in] priMask Priority Mask - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) -{ - __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); -} - - -#if (__CORTEX_M >= 0x03U) - -/** - \brief Enable FIQ - \details Enables FIQ interrupts by clearing the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) -{ - __ASM volatile ("cpsie f" : : : "memory"); -} - - -/** - \brief Disable FIQ - \details Disables FIQ interrupts by setting the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) -{ - __ASM volatile ("cpsid f" : : : "memory"); -} - - -/** - \brief Get Base Priority - \details Returns the current value of the Base Priority register. - \return Base Priority register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, basepri" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Base Priority - \details Assigns the given value to the Base Priority register. - \param [in] basePri Base Priority value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) -{ - __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); -} - - -/** - \brief Set Base Priority with condition - \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) -{ - __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); -} - - -/** - \brief Get Fault Mask - \details Returns the current value of the Fault Mask register. - \return Fault Mask register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Fault Mask - \details Assigns the given value to the Fault Mask register. - \param [in] faultMask Fault Mask value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) -{ - __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); -} - -#endif /* (__CORTEX_M >= 0x03U) */ - - -#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) - -/** - \brief Get FPSCR - \details Returns the current value of the Floating Point Status/Control register. - \return Floating Point Status/Control register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - /* Empty asm statement works as a scheduling barrier */ - __ASM volatile (""); - __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} - - -/** - \brief Set FPSCR - \details Assigns the given value to the Floating Point Status/Control register. - \param [in] fpscr Floating Point Status/Control value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - /* Empty asm statement works as a scheduling barrier */ - __ASM volatile (""); - __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} - -#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ - - - -/*@} end of CMSIS_Core_RegAccFunctions */ - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/* Define macros for porting to both thumb1 and thumb2. - * For thumb1, use low register (r0-r7), specified by constraint "l" - * Otherwise, use general registers, specified by constraint "r" */ -#if defined (__thumb__) && !defined (__thumb2__) -#define __CMSIS_GCC_OUT_REG(r) "=l" (r) -#define __CMSIS_GCC_USE_REG(r) "l" (r) -#else -#define __CMSIS_GCC_OUT_REG(r) "=r" (r) -#define __CMSIS_GCC_USE_REG(r) "r" (r) -#endif - -/** - \brief No Operation - \details No Operation does nothing. This instruction can be used for code alignment purposes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __NOP(void) -{ - __ASM volatile ("nop"); -} - - -/** - \brief Wait For Interrupt - \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. - */ -__attribute__((always_inline)) __STATIC_INLINE void __WFI(void) -{ - __ASM volatile ("wfi"); -} - - -/** - \brief Wait For Event - \details Wait For Event is a hint instruction that permits the processor to enter - a low-power state until one of a number of events occurs. - */ -__attribute__((always_inline)) __STATIC_INLINE void __WFE(void) -{ - __ASM volatile ("wfe"); -} - - -/** - \brief Send Event - \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. - */ -__attribute__((always_inline)) __STATIC_INLINE void __SEV(void) -{ - __ASM volatile ("sev"); -} - - -/** - \brief Instruction Synchronization Barrier - \details Instruction Synchronization Barrier flushes the pipeline in the processor, - so that all instructions following the ISB are fetched from cache or memory, - after the instruction has been completed. - */ -__attribute__((always_inline)) __STATIC_INLINE void __ISB(void) -{ - __ASM volatile ("isb 0xF":::"memory"); -} - - -/** - \brief Data Synchronization Barrier - \details Acts as a special kind of Data Memory Barrier. - It completes when all explicit memory accesses before this instruction complete. - */ -__attribute__((always_inline)) __STATIC_INLINE void __DSB(void) -{ - __ASM volatile ("dsb 0xF":::"memory"); -} - - -/** - \brief Data Memory Barrier - \details Ensures the apparent order of the explicit memory operations before - and after the instruction, without ensuring their completion. - */ -__attribute__((always_inline)) __STATIC_INLINE void __DMB(void) -{ - __ASM volatile ("dmb 0xF":::"memory"); -} - - -/** - \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV(uint32_t value) -{ -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) - return __builtin_bswap32(value); -#else - uint32_t result; - - __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -#endif -} - - -/** - \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) -{ -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - return (short)__builtin_bswap16(value); -#else - int32_t result; - - __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -#endif -} - - -/** - \brief Rotate Right in unsigned value (32 bit) - \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] value Value to rotate - \param [in] value Number of Bits to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) -{ - return (op1 >> op2) | (op1 << (32U - op2)); -} - - -/** - \brief Breakpoint - \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. - */ -#define __BKPT(value) __ASM volatile ("bkpt "#value) - - -/** - \brief Reverse bit order of value - \details Reverses the bit order of the given value. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); -#else - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ -#endif - return(result); -} - - -/** - \brief Count leading zeros - \details Counts the number of leading zeros of a data value. - \param [in] value Value to count the leading zeros - \return number of leading zeros in value - */ -#define __CLZ __builtin_clz - - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - -/** - \brief LDR Exclusive (8 bit) - \details Executes a exclusive LDR instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDR Exclusive (16 bit) - \details Executes a exclusive LDR instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDR Exclusive (32 bit) - \details Executes a exclusive LDR instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); - return(result); -} - - -/** - \brief STR Exclusive (8 bit) - \details Executes a exclusive STR instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) -{ - uint32_t result; - - __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); - return(result); -} - - -/** - \brief STR Exclusive (16 bit) - \details Executes a exclusive STR instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) -{ - uint32_t result; - - __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); - return(result); -} - - -/** - \brief STR Exclusive (32 bit) - \details Executes a exclusive STR instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); - return(result); -} - - -/** - \brief Remove the exclusive lock - \details Removes the exclusive lock which is created by LDREX. - */ -__attribute__((always_inline)) __STATIC_INLINE void __CLREX(void) -{ - __ASM volatile ("clrex" ::: "memory"); -} - - -/** - \brief Signed Saturate - \details Saturates a signed value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (1..32) - \return Saturated value - */ -#define __SSAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Unsigned Saturate - \details Saturates an unsigned value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (0..31) - \return Saturated value - */ -#define __USAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Rotate Right with Extend (32 bit) - \details Moves each bit of a bitstring right by one bit. - The carry input is shifted in at the left end of the bitstring. - \param [in] value Value to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief LDRT Unprivileged (8 bit) - \details Executes a Unprivileged LDRT instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (16 bit) - \details Executes a Unprivileged LDRT instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (32 bit) - \details Executes a Unprivileged LDRT instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*addr) ); - return(result); -} - - -/** - \brief STRT Unprivileged (8 bit) - \details Executes a Unprivileged STRT instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *addr) -{ - __ASM volatile ("strbt %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (16 bit) - \details Executes a Unprivileged STRT instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *addr) -{ - __ASM volatile ("strht %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (32 bit) - \details Executes a Unprivileged STRT instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *addr) -{ - __ASM volatile ("strt %1, %0" : "=Q" (*addr) : "r" (value) ); -} - -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#define __SSAT16(ARG1,ARG2) \ -({ \ - int32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -#define __USAT16(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -#define __PKHBT(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -#define __PKHTB(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - if (ARG3 == 0) \ - __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ - else \ - __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) -{ - int32_t result; - - __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#endif /* (__CORTEX_M >= 0x04) */ -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#if defined ( __GNUC__ ) -#pragma GCC diagnostic pop -#endif - -#endif /* __CMSIS_GCC_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cm0.h b/platform/robert/boot/vendor/CMSIS/Include/core_cm0.h deleted file mode 100644 index fdee521a87..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cm0.h +++ /dev/null @@ -1,798 +0,0 @@ -/**************************************************************************//** - * @file core_cm0.h - * @brief CMSIS Cortex-M0 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CM0_H_GENERIC -#define __CORE_CM0_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup Cortex_M0 - @{ - */ - -/* CMSIS CM0 definitions */ -#define __CM0_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM0_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM0_CMSIS_VERSION ((__CM0_CMSIS_VERSION_MAIN << 16U) | \ - __CM0_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x00U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - This core does not support an FPU at all -*/ -#define __FPU_USED 0U - -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM0_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_CM0_H_DEPENDANT -#define __CORE_CM0_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __CM0_REV - #define __CM0_REV 0x0000U - #warning "__CM0_REV not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 2U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group Cortex_M0 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t _reserved0:1; /*!< bit: 0 Reserved */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[1U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[31U]; - __IOM uint32_t ICER[1U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[31U]; - __IOM uint32_t ISPR[1U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[31U]; - __IOM uint32_t ICPR[1U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[31U]; - uint32_t RESERVED4[64U]; - __IOM uint32_t IP[8U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ -} NVIC_Type; - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - uint32_t RESERVED0; - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - uint32_t RESERVED1; - __IOM uint32_t SHP[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Cortex-M0 Core Debug Registers (DCB registers, SHCSR, and DFSR) are only accessible over DAP and not via processor. - Therefore they are not covered by the Cortex-M0 header file. - @{ - */ -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M0 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ - - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/* Interrupt Priorities are WORD accessible only under ARMv6M */ -/* The following MACROS handle generation of the register offset and byte masks */ -#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) -#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) -#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | - (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); - } - else - { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | - (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - SCB_AIRCR_SYSRESETREQ_Msk); - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM0_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cm0plus.h b/platform/robert/boot/vendor/CMSIS/Include/core_cm0plus.h deleted file mode 100644 index 7614450d3d..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cm0plus.h +++ /dev/null @@ -1,914 +0,0 @@ -/**************************************************************************//** - * @file core_cm0plus.h - * @brief CMSIS Cortex-M0+ Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CM0PLUS_H_GENERIC -#define __CORE_CM0PLUS_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup Cortex-M0+ - @{ - */ - -/* CMSIS CM0+ definitions */ -#define __CM0PLUS_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM0PLUS_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM0PLUS_CMSIS_VERSION ((__CM0PLUS_CMSIS_VERSION_MAIN << 16U) | \ - __CM0PLUS_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x00U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - This core does not support an FPU at all -*/ -#define __FPU_USED 0U - -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM0PLUS_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_CM0PLUS_H_DEPENDANT -#define __CORE_CM0PLUS_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __CM0PLUS_REV - #define __CM0PLUS_REV 0x0000U - #warning "__CM0PLUS_REV not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0U - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __VTOR_PRESENT - #define __VTOR_PRESENT 0U - #warning "__VTOR_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 2U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group Cortex-M0+ */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core MPU Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ -#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[1U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[31U]; - __IOM uint32_t ICER[1U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[31U]; - __IOM uint32_t ISPR[1U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[31U]; - __IOM uint32_t ICPR[1U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[31U]; - uint32_t RESERVED4[64U]; - __IOM uint32_t IP[8U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ -} NVIC_Type; - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ -#if (__VTOR_PRESENT == 1U) - __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ -#else - uint32_t RESERVED0; -#endif - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - uint32_t RESERVED1; - __IOM uint32_t SHP[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -#if (__VTOR_PRESENT == 1U) -/* SCB Interrupt Control State Register Definitions */ -#define SCB_VTOR_TBLOFF_Pos 8U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0xFFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ -#endif - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - -#if (__MPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** - \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register Definitions */ -#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register Definitions */ -#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register Definitions */ -#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register Definitions */ -#define MPU_RBAR_ADDR_Pos 8U /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0xFFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register Definitions */ -#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Cortex-M0+ Core Debug Registers (DCB registers, SHCSR, and DFSR) are only accessible over DAP and not via processor. - Therefore they are not covered by the Cortex-M0+ header file. - @{ - */ -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M0+ Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ - -#if (__MPU_PRESENT == 1U) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/* Interrupt Priorities are WORD accessible only under ARMv6M */ -/* The following MACROS handle generation of the register offset and byte masks */ -#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) -#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) -#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | - (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); - } - else - { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | - (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - SCB_AIRCR_SYSRESETREQ_Msk); - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM0PLUS_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cm3.h b/platform/robert/boot/vendor/CMSIS/Include/core_cm3.h deleted file mode 100644 index 34ed84c1ec..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cm3.h +++ /dev/null @@ -1,1763 +0,0 @@ -/**************************************************************************//** - * @file core_cm3.h - * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CM3_H_GENERIC -#define __CORE_CM3_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup Cortex_M3 - @{ - */ - -/* CMSIS CM3 definitions */ -#define __CM3_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM3_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16U) | \ - __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x03U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - This core does not support an FPU at all -*/ -#define __FPU_USED 0U - -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM3_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_CM3_H_DEPENDANT -#define __CORE_CM3_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __CM3_REV - #define __CM3_REV 0x0200U - #warning "__CM3_REV not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0U - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group Cortex_M3 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core Debug Register - - Core MPU Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - -#define APSR_Q_Pos 27U /*!< APSR: Q Position */ -#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ -#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ - -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ -#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[24U]; - __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[24U]; - __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[24U]; - __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[24U]; - __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ - uint32_t RESERVED4[56U]; - __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ - uint32_t RESERVED5[644U]; - __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ -} NVIC_Type; - -/* Software Triggered Interrupt Register Definitions */ -#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ -#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ - __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ - __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ - __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ - __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ - __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ - __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ - __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ - __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ - __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ - __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ - __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ - uint32_t RESERVED0[5U]; - __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ -#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Vector Table Offset Register Definitions */ -#if (__CM3_REV < 0x0201U) /* core r2p1 */ -#define SCB_VTOR_TBLBASE_Pos 29U /*!< SCB VTOR: TBLBASE Position */ -#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ - -#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ -#else -#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ -#endif - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ -#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ -#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ -#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ - -#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ -#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ -#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ - -#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ -#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ -#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ - -#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ -#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ - -#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ -#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ - -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ -#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ - -#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ -#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ - -#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ -#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ - -#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ -#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ - -#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ -#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ - -#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ -#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ - -#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ -#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ - -#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ -#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ - -#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ -#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ - -#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ -#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ - -/* SCB Configurable Fault Status Register Definitions */ -#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ -#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ - -#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ -#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ - -#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ -#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ - -/* SCB Hard Fault Status Register Definitions */ -#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ -#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ - -#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ -#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ - -#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ -#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ - -/* SCB Debug Fault Status Register Definitions */ -#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ -#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ - -#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ -#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ - -#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ -#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ - -#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ -#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ - -#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ -#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) - \brief Type definitions for the System Control and ID Register not in the SCB - @{ - */ - -/** - \brief Structure type to access the System Control and ID Register not in the SCB. - */ -typedef struct -{ - uint32_t RESERVED0[1U]; - __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ -#if ((defined __CM3_REV) && (__CM3_REV >= 0x200U)) - __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ -#else - uint32_t RESERVED1[1U]; -#endif -} SCnSCB_Type; - -/* Interrupt Controller Type Register Definitions */ -#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ -#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ - -/* Auxiliary Control Register Definitions */ - -#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ -#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ - -#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ -#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ - -#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ -#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ - -/*@} end of group CMSIS_SCnotSCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) - \brief Type definitions for the Instrumentation Trace Macrocell (ITM) - @{ - */ - -/** - \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). - */ -typedef struct -{ - __OM union - { - __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ - __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ - __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ - } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ - uint32_t RESERVED0[864U]; - __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ - uint32_t RESERVED1[15U]; - __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ - uint32_t RESERVED2[15U]; - __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ - uint32_t RESERVED3[29U]; - __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ - __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ - __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ - uint32_t RESERVED4[43U]; - __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ - __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ - uint32_t RESERVED5[6U]; - __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ - __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ - __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ - __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ - __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ - __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ - __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ - __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ - __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ - __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ - __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ - __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ -} ITM_Type; - -/* ITM Trace Privilege Register Definitions */ -#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ - -/* ITM Trace Control Register Definitions */ -#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ -#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ - -#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ -#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ - -#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ -#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ - -#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ -#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ - -#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ -#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ - -#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ -#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ - -#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ -#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ - -#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ -#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ - -#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ -#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ - -/* ITM Integration Write Register Definitions */ -#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ -#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ - -/* ITM Integration Read Register Definitions */ -#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ -#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ - -/* ITM Integration Mode Control Register Definitions */ -#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ -#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ - -/* ITM Lock Status Register Definitions */ -#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ -#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ - -#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ -#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ - -#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ -#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ - -/*@}*/ /* end of group CMSIS_ITM */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) - \brief Type definitions for the Data Watchpoint and Trace (DWT) - @{ - */ - -/** - \brief Structure type to access the Data Watchpoint and Trace Register (DWT). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ - __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ - __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ - __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ - __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ - __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ - __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ - __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ - __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ - __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ - __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ - uint32_t RESERVED0[1U]; - __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ - __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ - __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ - uint32_t RESERVED1[1U]; - __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ - __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ - __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ - uint32_t RESERVED2[1U]; - __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ - __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ - __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ -} DWT_Type; - -/* DWT Control Register Definitions */ -#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ -#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ - -#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ -#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ - -#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ -#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ - -#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ -#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ - -#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ -#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ - -#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ -#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ - -#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ -#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ - -#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ -#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ - -#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ -#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ - -#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ -#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ - -#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ -#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ - -#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ -#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ - -#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ -#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ - -#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ -#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ - -#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ -#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ - -#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ -#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ - -#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ -#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ - -#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ -#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ - -/* DWT CPI Count Register Definitions */ -#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ -#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ - -/* DWT Exception Overhead Count Register Definitions */ -#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ -#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ - -/* DWT Sleep Count Register Definitions */ -#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ -#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ - -/* DWT LSU Count Register Definitions */ -#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ -#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ - -/* DWT Folded-instruction Count Register Definitions */ -#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ -#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ - -/* DWT Comparator Mask Register Definitions */ -#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ -#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ - -/* DWT Comparator Function Register Definitions */ -#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ -#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ - -#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ -#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ - -#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ -#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ - -#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ -#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ - -#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ -#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ - -#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ -#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ - -#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ -#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ - -#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ -#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ - -#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ -#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ - -/*@}*/ /* end of group CMSIS_DWT */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_TPI Trace Port Interface (TPI) - \brief Type definitions for the Trace Port Interface (TPI) - @{ - */ - -/** - \brief Structure type to access the Trace Port Interface Register (TPI). - */ -typedef struct -{ - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ - __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ - uint32_t RESERVED0[2U]; - __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ - uint32_t RESERVED1[55U]; - __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ - uint32_t RESERVED2[131U]; - __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ - __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ - __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ - uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ - __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ - __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ - uint32_t RESERVED4[1U]; - __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ - __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ - __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ - uint32_t RESERVED5[39U]; - __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ - __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ - uint32_t RESERVED7[8U]; - __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ - __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ -} TPI_Type; - -/* TPI Asynchronous Clock Prescaler Register Definitions */ -#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ -#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ - -/* TPI Selected Pin Protocol Register Definitions */ -#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ -#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ - -/* TPI Formatter and Flush Status Register Definitions */ -#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ -#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ - -#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ -#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ - -#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ -#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ - -#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ -#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ - -/* TPI Formatter and Flush Control Register Definitions */ -#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ -#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ - -#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ -#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ - -/* TPI TRIGGER Register Definitions */ -#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ -#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ - -/* TPI Integration ETM Data Register Definitions (FIFO0) */ -#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ -#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ - -#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ -#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ - -#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ -#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ - -#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ -#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ - -#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ -#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ - -#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ -#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ - -#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ -#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ - -/* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ - -/* TPI Integration ITM Data Register Definitions (FIFO1) */ -#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ -#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ - -#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ -#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ - -#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ -#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ - -#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ -#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ - -#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ -#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ - -#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ -#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ - -#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ -#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ - -/* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ - -/* TPI Integration Mode Control Register Definitions */ -#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ - -/* TPI DEVID Register Definitions */ -#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ -#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ - -#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ -#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ - -#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ -#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ - -#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ -#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ - -#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ -#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ - -#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ -#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ - -/* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ -#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ - -/*@}*/ /* end of group CMSIS_TPI */ - - -#if (__MPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** - \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ - __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ - __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ - __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ - __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register Definitions */ -#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register Definitions */ -#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register Definitions */ -#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register Definitions */ -#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register Definitions */ -#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Type definitions for the Core Debug Registers - @{ - */ - -/** - \brief Structure type to access the Core Debug Register (CoreDebug). - */ -typedef struct -{ - __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ - __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ - __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ - __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ -} CoreDebug_Type; - -/* Debug Halting Control and Status Register Definitions */ -#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ -#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ - -#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ -#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ - -#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ -#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ - -#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ -#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ - -#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ -#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ - -#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ -#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ - -#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ -#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ - -#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ -#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ - -#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ -#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ - -#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ -#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ - -#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ -#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ - -#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ -#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ - -/* Debug Core Register Selector Register Definitions */ -#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ -#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ - -#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ -#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ - -/* Debug Exception and Monitor Control Register Definitions */ -#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ -#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ - -#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ -#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ - -#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ -#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ - -#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ -#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ - -#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ -#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ - -#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ -#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ - -#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ -#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ - -#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ -#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ - -#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ -#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ - -#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ -#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ - -#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ -#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ - -#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ -#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ - -#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ -#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ - -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M3 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ -#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ -#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ -#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ -#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ -#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ -#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ - -#if (__MPU_PRESENT == 1U) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Debug Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/** - \brief Set Priority Grouping - \details Sets the priority grouping field using the required unlock sequence. - The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. - Only values from 0..7 are used. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Priority grouping field. - */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) -{ - uint32_t reg_value; - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - - reg_value = SCB->AIRCR; /* read old register configuration */ - reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ - reg_value = (reg_value | - ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ - SCB->AIRCR = reg_value; -} - - -/** - \brief Get Priority Grouping - \details Reads the priority grouping field from the NVIC Interrupt Controller. - \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). - */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) -{ - return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); -} - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not active. - \return 1 Interrupt status is active. - */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } - else - { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief Encode Priority - \details Encodes the priority for an interrupt with the given priority group, - preemptive priority value, and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Used priority group. - \param [in] PreemptPriority Preemptive priority value (starting from 0). - \param [in] SubPriority Subpriority value (starting from 0). - \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). - */ -__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - return ( - ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | - ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) - ); -} - - -/** - \brief Decode Priority - \details Decodes an interrupt priority value with a given priority group to - preemptive priority value and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. - \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). - \param [in] PriorityGroup Used priority group. - \param [out] pPreemptPriority Preemptive priority value (starting from 0). - \param [out] pSubPriority Subpriority value (starting from 0). - */ -__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); - *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | - SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - -/* ##################################### Debug In/Output function ########################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_core_DebugFunctions ITM Functions - \brief Functions that access the ITM debug interface. - @{ - */ - -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ - - -/** - \brief ITM Send Character - \details Transmits a character via the ITM channel 0, and - \li Just returns when no debugger is connected that has booked the output. - \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. - \param [in] ch Character to transmit. - \returns Character to transmit. - */ -__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) -{ - if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ - ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ - { - while (ITM->PORT[0U].u32 == 0UL) - { - __NOP(); - } - ITM->PORT[0U].u8 = (uint8_t)ch; - } - return (ch); -} - - -/** - \brief ITM Receive Character - \details Inputs a character via the external variable \ref ITM_RxBuffer. - \return Received character. - \return -1 No character pending. - */ -__STATIC_INLINE int32_t ITM_ReceiveChar (void) -{ - int32_t ch = -1; /* no character available */ - - if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) - { - ch = ITM_RxBuffer; - ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ - } - - return (ch); -} - - -/** - \brief ITM Check Character - \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. - \return 0 No character available. - \return 1 Character available. - */ -__STATIC_INLINE int32_t ITM_CheckChar (void) -{ - - if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) - { - return (0); /* no character available */ - } - else - { - return (1); /* character available */ - } -} - -/*@} end of CMSIS_core_DebugFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM3_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cm4.h b/platform/robert/boot/vendor/CMSIS/Include/core_cm4.h deleted file mode 100644 index 01cb73bf3c..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cm4.h +++ /dev/null @@ -1,1937 +0,0 @@ -/**************************************************************************//** - * @file core_cm4.h - * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CM4_H_GENERIC -#define __CORE_CM4_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup Cortex_M4 - @{ - */ - -/* CMSIS CM4 definitions */ -#define __CM4_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM4_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ - __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x04U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. -*/ -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1U - #else - #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ -#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM4_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_CM4_H_DEPENDANT -#define __CORE_CM4_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __CM4_REV - #define __CM4_REV 0x0000U - #warning "__CM4_REV not defined in device header file; using default!" - #endif - - #ifndef __FPU_PRESENT - #define __FPU_PRESENT 0U - #warning "__FPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0U - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group Cortex_M4 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core Debug Register - - Core MPU Register - - Core FPU Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ - uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ - uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - -#define APSR_Q_Pos 27U /*!< APSR: Q Position */ -#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ - -#define APSR_GE_Pos 16U /*!< APSR: GE Position */ -#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ - uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ - uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ -#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ - -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ -#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ - uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ -#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ - -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ -#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[24U]; - __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[24U]; - __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[24U]; - __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[24U]; - __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ - uint32_t RESERVED4[56U]; - __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ - uint32_t RESERVED5[644U]; - __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ -} NVIC_Type; - -/* Software Triggered Interrupt Register Definitions */ -#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ -#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ - __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ - __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ - __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ - __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ - __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ - __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ - __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ - __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ - __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ - __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ - __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ - uint32_t RESERVED0[5U]; - __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ -#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Vector Table Offset Register Definitions */ -#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ -#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ -#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ -#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ - -#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ -#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ -#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ - -#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ -#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ -#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ - -#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ -#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ - -#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ -#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ - -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ -#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ - -#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ -#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ - -#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ -#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ - -#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ -#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ - -#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ -#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ - -#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ -#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ - -#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ -#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ - -#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ -#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ - -#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ -#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ - -#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ -#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ - -/* SCB Configurable Fault Status Register Definitions */ -#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ -#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ - -#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ -#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ - -#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ -#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ - -/* SCB Hard Fault Status Register Definitions */ -#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ -#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ - -#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ -#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ - -#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ -#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ - -/* SCB Debug Fault Status Register Definitions */ -#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ -#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ - -#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ -#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ - -#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ -#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ - -#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ -#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ - -#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ -#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) - \brief Type definitions for the System Control and ID Register not in the SCB - @{ - */ - -/** - \brief Structure type to access the System Control and ID Register not in the SCB. - */ -typedef struct -{ - uint32_t RESERVED0[1U]; - __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ - __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ -} SCnSCB_Type; - -/* Interrupt Controller Type Register Definitions */ -#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ -#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ - -/* Auxiliary Control Register Definitions */ -#define SCnSCB_ACTLR_DISOOFP_Pos 9U /*!< ACTLR: DISOOFP Position */ -#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ - -#define SCnSCB_ACTLR_DISFPCA_Pos 8U /*!< ACTLR: DISFPCA Position */ -#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ - -#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ -#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ - -#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ -#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ - -#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ -#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ - -/*@} end of group CMSIS_SCnotSCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) - \brief Type definitions for the Instrumentation Trace Macrocell (ITM) - @{ - */ - -/** - \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). - */ -typedef struct -{ - __OM union - { - __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ - __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ - __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ - } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ - uint32_t RESERVED0[864U]; - __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ - uint32_t RESERVED1[15U]; - __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ - uint32_t RESERVED2[15U]; - __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ - uint32_t RESERVED3[29U]; - __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ - __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ - __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ - uint32_t RESERVED4[43U]; - __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ - __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ - uint32_t RESERVED5[6U]; - __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ - __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ - __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ - __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ - __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ - __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ - __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ - __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ - __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ - __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ - __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ - __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ -} ITM_Type; - -/* ITM Trace Privilege Register Definitions */ -#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ - -/* ITM Trace Control Register Definitions */ -#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ -#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ - -#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ -#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ - -#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ -#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ - -#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ -#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ - -#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ -#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ - -#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ -#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ - -#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ -#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ - -#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ -#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ - -#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ -#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ - -/* ITM Integration Write Register Definitions */ -#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ -#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ - -/* ITM Integration Read Register Definitions */ -#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ -#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ - -/* ITM Integration Mode Control Register Definitions */ -#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ -#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ - -/* ITM Lock Status Register Definitions */ -#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ -#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ - -#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ -#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ - -#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ -#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ - -/*@}*/ /* end of group CMSIS_ITM */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) - \brief Type definitions for the Data Watchpoint and Trace (DWT) - @{ - */ - -/** - \brief Structure type to access the Data Watchpoint and Trace Register (DWT). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ - __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ - __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ - __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ - __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ - __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ - __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ - __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ - __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ - __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ - __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ - uint32_t RESERVED0[1U]; - __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ - __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ - __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ - uint32_t RESERVED1[1U]; - __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ - __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ - __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ - uint32_t RESERVED2[1U]; - __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ - __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ - __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ -} DWT_Type; - -/* DWT Control Register Definitions */ -#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ -#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ - -#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ -#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ - -#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ -#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ - -#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ -#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ - -#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ -#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ - -#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ -#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ - -#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ -#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ - -#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ -#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ - -#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ -#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ - -#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ -#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ - -#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ -#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ - -#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ -#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ - -#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ -#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ - -#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ -#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ - -#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ -#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ - -#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ -#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ - -#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ -#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ - -#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ -#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ - -/* DWT CPI Count Register Definitions */ -#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ -#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ - -/* DWT Exception Overhead Count Register Definitions */ -#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ -#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ - -/* DWT Sleep Count Register Definitions */ -#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ -#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ - -/* DWT LSU Count Register Definitions */ -#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ -#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ - -/* DWT Folded-instruction Count Register Definitions */ -#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ -#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ - -/* DWT Comparator Mask Register Definitions */ -#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ -#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ - -/* DWT Comparator Function Register Definitions */ -#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ -#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ - -#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ -#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ - -#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ -#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ - -#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ -#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ - -#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ -#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ - -#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ -#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ - -#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ -#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ - -#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ -#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ - -#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ -#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ - -/*@}*/ /* end of group CMSIS_DWT */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_TPI Trace Port Interface (TPI) - \brief Type definitions for the Trace Port Interface (TPI) - @{ - */ - -/** - \brief Structure type to access the Trace Port Interface Register (TPI). - */ -typedef struct -{ - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ - __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ - uint32_t RESERVED0[2U]; - __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ - uint32_t RESERVED1[55U]; - __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ - uint32_t RESERVED2[131U]; - __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ - __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ - __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ - uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ - __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ - __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ - uint32_t RESERVED4[1U]; - __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ - __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ - __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ - uint32_t RESERVED5[39U]; - __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ - __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ - uint32_t RESERVED7[8U]; - __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ - __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ -} TPI_Type; - -/* TPI Asynchronous Clock Prescaler Register Definitions */ -#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ -#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ - -/* TPI Selected Pin Protocol Register Definitions */ -#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ -#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ - -/* TPI Formatter and Flush Status Register Definitions */ -#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ -#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ - -#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ -#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ - -#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ -#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ - -#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ -#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ - -/* TPI Formatter and Flush Control Register Definitions */ -#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ -#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ - -#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ -#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ - -/* TPI TRIGGER Register Definitions */ -#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ -#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ - -/* TPI Integration ETM Data Register Definitions (FIFO0) */ -#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ -#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ - -#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ -#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ - -#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ -#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ - -#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ -#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ - -#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ -#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ - -#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ -#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ - -#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ -#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ - -/* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ - -/* TPI Integration ITM Data Register Definitions (FIFO1) */ -#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ -#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ - -#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ -#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ - -#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ -#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ - -#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ -#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ - -#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ -#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ - -#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ -#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ - -#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ -#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ - -/* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ - -/* TPI Integration Mode Control Register Definitions */ -#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ - -/* TPI DEVID Register Definitions */ -#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ -#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ - -#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ -#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ - -#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ -#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ - -#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ -#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ - -#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ -#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ - -#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ -#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ - -/* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ -#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ - -/*@}*/ /* end of group CMSIS_TPI */ - - -#if (__MPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** - \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ - __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ - __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ - __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ - __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register Definitions */ -#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register Definitions */ -#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register Definitions */ -#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register Definitions */ -#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register Definitions */ -#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -#if (__FPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_FPU Floating Point Unit (FPU) - \brief Type definitions for the Floating Point Unit (FPU) - @{ - */ - -/** - \brief Structure type to access the Floating Point Unit (FPU). - */ -typedef struct -{ - uint32_t RESERVED0[1U]; - __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ - __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ - __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ - __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ - __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ -} FPU_Type; - -/* Floating-Point Context Control Register Definitions */ -#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ -#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ - -#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ -#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ - -#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ -#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ - -#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ -#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ - -#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ -#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ - -#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ -#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ - -#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ -#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ - -#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ -#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ - -#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ -#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ - -/* Floating-Point Context Address Register Definitions */ -#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ -#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ - -/* Floating-Point Default Status Control Register Definitions */ -#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ -#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ - -#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ -#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ - -#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ -#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ - -#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ -#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ - -/* Media and FP Feature Register 0 Definitions */ -#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ -#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ - -#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ -#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ - -#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ -#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ - -#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ -#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ - -#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ -#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ - -#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ -#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ - -#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ -#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ - -#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ -#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ - -/* Media and FP Feature Register 1 Definitions */ -#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ -#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ - -#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ -#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ - -#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ -#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ - -#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ -#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ - -/*@} end of group CMSIS_FPU */ -#endif - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Type definitions for the Core Debug Registers - @{ - */ - -/** - \brief Structure type to access the Core Debug Register (CoreDebug). - */ -typedef struct -{ - __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ - __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ - __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ - __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ -} CoreDebug_Type; - -/* Debug Halting Control and Status Register Definitions */ -#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ -#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ - -#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ -#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ - -#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ -#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ - -#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ -#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ - -#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ -#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ - -#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ -#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ - -#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ -#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ - -#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ -#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ - -#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ -#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ - -#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ -#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ - -#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ -#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ - -#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ -#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ - -/* Debug Core Register Selector Register Definitions */ -#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ -#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ - -#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ -#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ - -/* Debug Exception and Monitor Control Register Definitions */ -#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ -#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ - -#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ -#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ - -#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ -#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ - -#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ -#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ - -#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ -#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ - -#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ -#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ - -#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ -#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ - -#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ -#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ - -#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ -#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ - -#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ -#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ - -#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ -#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ - -#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ -#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ - -#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ -#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ - -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M4 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ -#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ -#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ -#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ -#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ -#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ -#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ - -#if (__MPU_PRESENT == 1U) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -#if (__FPU_PRESENT == 1U) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Debug Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/** - \brief Set Priority Grouping - \details Sets the priority grouping field using the required unlock sequence. - The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. - Only values from 0..7 are used. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Priority grouping field. - */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) -{ - uint32_t reg_value; - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - - reg_value = SCB->AIRCR; /* read old register configuration */ - reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ - reg_value = (reg_value | - ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ - SCB->AIRCR = reg_value; -} - - -/** - \brief Get Priority Grouping - \details Reads the priority grouping field from the NVIC Interrupt Controller. - \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). - */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) -{ - return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); -} - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not active. - \return 1 Interrupt status is active. - */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } - else - { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief Encode Priority - \details Encodes the priority for an interrupt with the given priority group, - preemptive priority value, and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Used priority group. - \param [in] PreemptPriority Preemptive priority value (starting from 0). - \param [in] SubPriority Subpriority value (starting from 0). - \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). - */ -__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - return ( - ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | - ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) - ); -} - - -/** - \brief Decode Priority - \details Decodes an interrupt priority value with a given priority group to - preemptive priority value and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. - \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). - \param [in] PriorityGroup Used priority group. - \param [out] pPreemptPriority Preemptive priority value (starting from 0). - \param [out] pSubPriority Subpriority value (starting from 0). - */ -__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); - *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | - SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - -/* ##################################### Debug In/Output function ########################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_core_DebugFunctions ITM Functions - \brief Functions that access the ITM debug interface. - @{ - */ - -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ - - -/** - \brief ITM Send Character - \details Transmits a character via the ITM channel 0, and - \li Just returns when no debugger is connected that has booked the output. - \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. - \param [in] ch Character to transmit. - \returns Character to transmit. - */ -__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) -{ - if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ - ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ - { - while (ITM->PORT[0U].u32 == 0UL) - { - __NOP(); - } - ITM->PORT[0U].u8 = (uint8_t)ch; - } - return (ch); -} - - -/** - \brief ITM Receive Character - \details Inputs a character via the external variable \ref ITM_RxBuffer. - \return Received character. - \return -1 No character pending. - */ -__STATIC_INLINE int32_t ITM_ReceiveChar (void) -{ - int32_t ch = -1; /* no character available */ - - if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) - { - ch = ITM_RxBuffer; - ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ - } - - return (ch); -} - - -/** - \brief ITM Check Character - \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. - \return 0 No character available. - \return 1 Character available. - */ -__STATIC_INLINE int32_t ITM_CheckChar (void) -{ - - if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) - { - return (0); /* no character available */ - } - else - { - return (1); /* character available */ - } -} - -/*@} end of CMSIS_core_DebugFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM4_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cm7.h b/platform/robert/boot/vendor/CMSIS/Include/core_cm7.h deleted file mode 100644 index 20963c148c..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cm7.h +++ /dev/null @@ -1,2512 +0,0 @@ -/**************************************************************************//** - * @file core_cm7.h - * @brief CMSIS Cortex-M7 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CM7_H_GENERIC -#define __CORE_CM7_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup Cortex_M7 - @{ - */ - -/* CMSIS CM7 definitions */ -#define __CM7_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM7_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __CM7_CMSIS_VERSION ((__CM7_CMSIS_VERSION_MAIN << 16U) | \ - __CM7_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x07U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. -*/ -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1U - #else - #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #if (__FPU_PRESENT == 1U) - #define __FPU_USED 1U - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0U - #endif - #else - #define __FPU_USED 0U - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ -#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM7_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_CM7_H_DEPENDANT -#define __CORE_CM7_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __CM7_REV - #define __CM7_REV 0x0000U - #warning "__CM7_REV not defined in device header file; using default!" - #endif - - #ifndef __FPU_PRESENT - #define __FPU_PRESENT 0U - #warning "__FPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0U - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __ICACHE_PRESENT - #define __ICACHE_PRESENT 0U - #warning "__ICACHE_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __DCACHE_PRESENT - #define __DCACHE_PRESENT 0U - #warning "__DCACHE_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __DTCM_PRESENT - #define __DTCM_PRESENT 0U - #warning "__DTCM_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 3U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group Cortex_M7 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core Debug Register - - Core MPU Register - - Core FPU Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ - uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ - uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - -#define APSR_Q_Pos 27U /*!< APSR: Q Position */ -#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ - -#define APSR_GE_Pos 16U /*!< APSR: GE Position */ -#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ - uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ - uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ -#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ - -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ -#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ - uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ -#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ - -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ -#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[24U]; - __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[24U]; - __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[24U]; - __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[24U]; - __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ - uint32_t RESERVED4[56U]; - __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ - uint32_t RESERVED5[644U]; - __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ -} NVIC_Type; - -/* Software Triggered Interrupt Register Definitions */ -#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ -#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - __IOM uint8_t SHPR[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ - __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ - __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ - __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ - __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ - __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ - __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ - __IM uint32_t ID_PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ - __IM uint32_t ID_DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ - __IM uint32_t ID_AFR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ - __IM uint32_t ID_MFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ - __IM uint32_t ID_ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ - uint32_t RESERVED0[1U]; - __IM uint32_t CLIDR; /*!< Offset: 0x078 (R/ ) Cache Level ID register */ - __IM uint32_t CTR; /*!< Offset: 0x07C (R/ ) Cache Type register */ - __IM uint32_t CCSIDR; /*!< Offset: 0x080 (R/ ) Cache Size ID Register */ - __IOM uint32_t CSSELR; /*!< Offset: 0x084 (R/W) Cache Size Selection Register */ - __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ - uint32_t RESERVED3[93U]; - __OM uint32_t STIR; /*!< Offset: 0x200 ( /W) Software Triggered Interrupt Register */ - uint32_t RESERVED4[15U]; - __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ - __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ - __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 1 */ - uint32_t RESERVED5[1U]; - __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ - uint32_t RESERVED6[1U]; - __OM uint32_t ICIMVAU; /*!< Offset: 0x258 ( /W) I-Cache Invalidate by MVA to PoU */ - __OM uint32_t DCIMVAC; /*!< Offset: 0x25C ( /W) D-Cache Invalidate by MVA to PoC */ - __OM uint32_t DCISW; /*!< Offset: 0x260 ( /W) D-Cache Invalidate by Set-way */ - __OM uint32_t DCCMVAU; /*!< Offset: 0x264 ( /W) D-Cache Clean by MVA to PoU */ - __OM uint32_t DCCMVAC; /*!< Offset: 0x268 ( /W) D-Cache Clean by MVA to PoC */ - __OM uint32_t DCCSW; /*!< Offset: 0x26C ( /W) D-Cache Clean by Set-way */ - __OM uint32_t DCCIMVAC; /*!< Offset: 0x270 ( /W) D-Cache Clean and Invalidate by MVA to PoC */ - __OM uint32_t DCCISW; /*!< Offset: 0x274 ( /W) D-Cache Clean and Invalidate by Set-way */ - uint32_t RESERVED7[6U]; - __IOM uint32_t ITCMCR; /*!< Offset: 0x290 (R/W) Instruction Tightly-Coupled Memory Control Register */ - __IOM uint32_t DTCMCR; /*!< Offset: 0x294 (R/W) Data Tightly-Coupled Memory Control Registers */ - __IOM uint32_t AHBPCR; /*!< Offset: 0x298 (R/W) AHBP Control Register */ - __IOM uint32_t CACR; /*!< Offset: 0x29C (R/W) L1 Cache Control Register */ - __IOM uint32_t AHBSCR; /*!< Offset: 0x2A0 (R/W) AHB Slave Control Register */ - uint32_t RESERVED8[1U]; - __IOM uint32_t ABFSR; /*!< Offset: 0x2A8 (R/W) Auxiliary Bus Fault Status Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ -#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Vector Table Offset Register Definitions */ -#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ -#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ -#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: Branch prediction enable bit Position */ -#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: Branch prediction enable bit Mask */ - -#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: Instruction cache enable bit Position */ -#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: Instruction cache enable bit Mask */ - -#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: Cache enable bit Position */ -#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: Cache enable bit Mask */ - -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ -#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ - -#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ -#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ -#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ - -#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ -#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ -#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ - -#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ -#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ - -#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ -#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ - -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ -#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ - -#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ -#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ - -#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ -#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ - -#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ -#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ - -#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ -#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ - -#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ -#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ - -#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ -#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ - -#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ -#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ - -#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ -#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ - -#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ -#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ - -/* SCB Configurable Fault Status Register Definitions */ -#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ -#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ - -#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ -#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ - -#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ -#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ - -/* SCB Hard Fault Status Register Definitions */ -#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ -#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ - -#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ -#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ - -#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ -#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ - -/* SCB Debug Fault Status Register Definitions */ -#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ -#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ - -#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ -#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ - -#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ -#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ - -#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ -#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ - -#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ -#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ - -/* SCB Cache Level ID Register Definitions */ -#define SCB_CLIDR_LOUU_Pos 27U /*!< SCB CLIDR: LoUU Position */ -#define SCB_CLIDR_LOUU_Msk (7UL << SCB_CLIDR_LOUU_Pos) /*!< SCB CLIDR: LoUU Mask */ - -#define SCB_CLIDR_LOC_Pos 24U /*!< SCB CLIDR: LoC Position */ -#define SCB_CLIDR_LOC_Msk (7UL << SCB_CLIDR_LOC_Pos) /*!< SCB CLIDR: LoC Mask */ - -/* SCB Cache Type Register Definitions */ -#define SCB_CTR_FORMAT_Pos 29U /*!< SCB CTR: Format Position */ -#define SCB_CTR_FORMAT_Msk (7UL << SCB_CTR_FORMAT_Pos) /*!< SCB CTR: Format Mask */ - -#define SCB_CTR_CWG_Pos 24U /*!< SCB CTR: CWG Position */ -#define SCB_CTR_CWG_Msk (0xFUL << SCB_CTR_CWG_Pos) /*!< SCB CTR: CWG Mask */ - -#define SCB_CTR_ERG_Pos 20U /*!< SCB CTR: ERG Position */ -#define SCB_CTR_ERG_Msk (0xFUL << SCB_CTR_ERG_Pos) /*!< SCB CTR: ERG Mask */ - -#define SCB_CTR_DMINLINE_Pos 16U /*!< SCB CTR: DminLine Position */ -#define SCB_CTR_DMINLINE_Msk (0xFUL << SCB_CTR_DMINLINE_Pos) /*!< SCB CTR: DminLine Mask */ - -#define SCB_CTR_IMINLINE_Pos 0U /*!< SCB CTR: ImInLine Position */ -#define SCB_CTR_IMINLINE_Msk (0xFUL /*<< SCB_CTR_IMINLINE_Pos*/) /*!< SCB CTR: ImInLine Mask */ - -/* SCB Cache Size ID Register Definitions */ -#define SCB_CCSIDR_WT_Pos 31U /*!< SCB CCSIDR: WT Position */ -#define SCB_CCSIDR_WT_Msk (1UL << SCB_CCSIDR_WT_Pos) /*!< SCB CCSIDR: WT Mask */ - -#define SCB_CCSIDR_WB_Pos 30U /*!< SCB CCSIDR: WB Position */ -#define SCB_CCSIDR_WB_Msk (1UL << SCB_CCSIDR_WB_Pos) /*!< SCB CCSIDR: WB Mask */ - -#define SCB_CCSIDR_RA_Pos 29U /*!< SCB CCSIDR: RA Position */ -#define SCB_CCSIDR_RA_Msk (1UL << SCB_CCSIDR_RA_Pos) /*!< SCB CCSIDR: RA Mask */ - -#define SCB_CCSIDR_WA_Pos 28U /*!< SCB CCSIDR: WA Position */ -#define SCB_CCSIDR_WA_Msk (1UL << SCB_CCSIDR_WA_Pos) /*!< SCB CCSIDR: WA Mask */ - -#define SCB_CCSIDR_NUMSETS_Pos 13U /*!< SCB CCSIDR: NumSets Position */ -#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos) /*!< SCB CCSIDR: NumSets Mask */ - -#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U /*!< SCB CCSIDR: Associativity Position */ -#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos) /*!< SCB CCSIDR: Associativity Mask */ - -#define SCB_CCSIDR_LINESIZE_Pos 0U /*!< SCB CCSIDR: LineSize Position */ -#define SCB_CCSIDR_LINESIZE_Msk (7UL /*<< SCB_CCSIDR_LINESIZE_Pos*/) /*!< SCB CCSIDR: LineSize Mask */ - -/* SCB Cache Size Selection Register Definitions */ -#define SCB_CSSELR_LEVEL_Pos 1U /*!< SCB CSSELR: Level Position */ -#define SCB_CSSELR_LEVEL_Msk (7UL << SCB_CSSELR_LEVEL_Pos) /*!< SCB CSSELR: Level Mask */ - -#define SCB_CSSELR_IND_Pos 0U /*!< SCB CSSELR: InD Position */ -#define SCB_CSSELR_IND_Msk (1UL /*<< SCB_CSSELR_IND_Pos*/) /*!< SCB CSSELR: InD Mask */ - -/* SCB Software Triggered Interrupt Register Definitions */ -#define SCB_STIR_INTID_Pos 0U /*!< SCB STIR: INTID Position */ -#define SCB_STIR_INTID_Msk (0x1FFUL /*<< SCB_STIR_INTID_Pos*/) /*!< SCB STIR: INTID Mask */ - -/* SCB D-Cache Invalidate by Set-way Register Definitions */ -#define SCB_DCISW_WAY_Pos 30U /*!< SCB DCISW: Way Position */ -#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos) /*!< SCB DCISW: Way Mask */ - -#define SCB_DCISW_SET_Pos 5U /*!< SCB DCISW: Set Position */ -#define SCB_DCISW_SET_Msk (0x1FFUL << SCB_DCISW_SET_Pos) /*!< SCB DCISW: Set Mask */ - -/* SCB D-Cache Clean by Set-way Register Definitions */ -#define SCB_DCCSW_WAY_Pos 30U /*!< SCB DCCSW: Way Position */ -#define SCB_DCCSW_WAY_Msk (3UL << SCB_DCCSW_WAY_Pos) /*!< SCB DCCSW: Way Mask */ - -#define SCB_DCCSW_SET_Pos 5U /*!< SCB DCCSW: Set Position */ -#define SCB_DCCSW_SET_Msk (0x1FFUL << SCB_DCCSW_SET_Pos) /*!< SCB DCCSW: Set Mask */ - -/* SCB D-Cache Clean and Invalidate by Set-way Register Definitions */ -#define SCB_DCCISW_WAY_Pos 30U /*!< SCB DCCISW: Way Position */ -#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos) /*!< SCB DCCISW: Way Mask */ - -#define SCB_DCCISW_SET_Pos 5U /*!< SCB DCCISW: Set Position */ -#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */ - -/* Instruction Tightly-Coupled Memory Control Register Definitions */ -#define SCB_ITCMCR_SZ_Pos 3U /*!< SCB ITCMCR: SZ Position */ -#define SCB_ITCMCR_SZ_Msk (0xFUL << SCB_ITCMCR_SZ_Pos) /*!< SCB ITCMCR: SZ Mask */ - -#define SCB_ITCMCR_RETEN_Pos 2U /*!< SCB ITCMCR: RETEN Position */ -#define SCB_ITCMCR_RETEN_Msk (1UL << SCB_ITCMCR_RETEN_Pos) /*!< SCB ITCMCR: RETEN Mask */ - -#define SCB_ITCMCR_RMW_Pos 1U /*!< SCB ITCMCR: RMW Position */ -#define SCB_ITCMCR_RMW_Msk (1UL << SCB_ITCMCR_RMW_Pos) /*!< SCB ITCMCR: RMW Mask */ - -#define SCB_ITCMCR_EN_Pos 0U /*!< SCB ITCMCR: EN Position */ -#define SCB_ITCMCR_EN_Msk (1UL /*<< SCB_ITCMCR_EN_Pos*/) /*!< SCB ITCMCR: EN Mask */ - -/* Data Tightly-Coupled Memory Control Register Definitions */ -#define SCB_DTCMCR_SZ_Pos 3U /*!< SCB DTCMCR: SZ Position */ -#define SCB_DTCMCR_SZ_Msk (0xFUL << SCB_DTCMCR_SZ_Pos) /*!< SCB DTCMCR: SZ Mask */ - -#define SCB_DTCMCR_RETEN_Pos 2U /*!< SCB DTCMCR: RETEN Position */ -#define SCB_DTCMCR_RETEN_Msk (1UL << SCB_DTCMCR_RETEN_Pos) /*!< SCB DTCMCR: RETEN Mask */ - -#define SCB_DTCMCR_RMW_Pos 1U /*!< SCB DTCMCR: RMW Position */ -#define SCB_DTCMCR_RMW_Msk (1UL << SCB_DTCMCR_RMW_Pos) /*!< SCB DTCMCR: RMW Mask */ - -#define SCB_DTCMCR_EN_Pos 0U /*!< SCB DTCMCR: EN Position */ -#define SCB_DTCMCR_EN_Msk (1UL /*<< SCB_DTCMCR_EN_Pos*/) /*!< SCB DTCMCR: EN Mask */ - -/* AHBP Control Register Definitions */ -#define SCB_AHBPCR_SZ_Pos 1U /*!< SCB AHBPCR: SZ Position */ -#define SCB_AHBPCR_SZ_Msk (7UL << SCB_AHBPCR_SZ_Pos) /*!< SCB AHBPCR: SZ Mask */ - -#define SCB_AHBPCR_EN_Pos 0U /*!< SCB AHBPCR: EN Position */ -#define SCB_AHBPCR_EN_Msk (1UL /*<< SCB_AHBPCR_EN_Pos*/) /*!< SCB AHBPCR: EN Mask */ - -/* L1 Cache Control Register Definitions */ -#define SCB_CACR_FORCEWT_Pos 2U /*!< SCB CACR: FORCEWT Position */ -#define SCB_CACR_FORCEWT_Msk (1UL << SCB_CACR_FORCEWT_Pos) /*!< SCB CACR: FORCEWT Mask */ - -#define SCB_CACR_ECCEN_Pos 1U /*!< SCB CACR: ECCEN Position */ -#define SCB_CACR_ECCEN_Msk (1UL << SCB_CACR_ECCEN_Pos) /*!< SCB CACR: ECCEN Mask */ - -#define SCB_CACR_SIWT_Pos 0U /*!< SCB CACR: SIWT Position */ -#define SCB_CACR_SIWT_Msk (1UL /*<< SCB_CACR_SIWT_Pos*/) /*!< SCB CACR: SIWT Mask */ - -/* AHBS Control Register Definitions */ -#define SCB_AHBSCR_INITCOUNT_Pos 11U /*!< SCB AHBSCR: INITCOUNT Position */ -#define SCB_AHBSCR_INITCOUNT_Msk (0x1FUL << SCB_AHBPCR_INITCOUNT_Pos) /*!< SCB AHBSCR: INITCOUNT Mask */ - -#define SCB_AHBSCR_TPRI_Pos 2U /*!< SCB AHBSCR: TPRI Position */ -#define SCB_AHBSCR_TPRI_Msk (0x1FFUL << SCB_AHBPCR_TPRI_Pos) /*!< SCB AHBSCR: TPRI Mask */ - -#define SCB_AHBSCR_CTL_Pos 0U /*!< SCB AHBSCR: CTL Position*/ -#define SCB_AHBSCR_CTL_Msk (3UL /*<< SCB_AHBPCR_CTL_Pos*/) /*!< SCB AHBSCR: CTL Mask */ - -/* Auxiliary Bus Fault Status Register Definitions */ -#define SCB_ABFSR_AXIMTYPE_Pos 8U /*!< SCB ABFSR: AXIMTYPE Position*/ -#define SCB_ABFSR_AXIMTYPE_Msk (3UL << SCB_ABFSR_AXIMTYPE_Pos) /*!< SCB ABFSR: AXIMTYPE Mask */ - -#define SCB_ABFSR_EPPB_Pos 4U /*!< SCB ABFSR: EPPB Position*/ -#define SCB_ABFSR_EPPB_Msk (1UL << SCB_ABFSR_EPPB_Pos) /*!< SCB ABFSR: EPPB Mask */ - -#define SCB_ABFSR_AXIM_Pos 3U /*!< SCB ABFSR: AXIM Position*/ -#define SCB_ABFSR_AXIM_Msk (1UL << SCB_ABFSR_AXIM_Pos) /*!< SCB ABFSR: AXIM Mask */ - -#define SCB_ABFSR_AHBP_Pos 2U /*!< SCB ABFSR: AHBP Position*/ -#define SCB_ABFSR_AHBP_Msk (1UL << SCB_ABFSR_AHBP_Pos) /*!< SCB ABFSR: AHBP Mask */ - -#define SCB_ABFSR_DTCM_Pos 1U /*!< SCB ABFSR: DTCM Position*/ -#define SCB_ABFSR_DTCM_Msk (1UL << SCB_ABFSR_DTCM_Pos) /*!< SCB ABFSR: DTCM Mask */ - -#define SCB_ABFSR_ITCM_Pos 0U /*!< SCB ABFSR: ITCM Position*/ -#define SCB_ABFSR_ITCM_Msk (1UL /*<< SCB_ABFSR_ITCM_Pos*/) /*!< SCB ABFSR: ITCM Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) - \brief Type definitions for the System Control and ID Register not in the SCB - @{ - */ - -/** - \brief Structure type to access the System Control and ID Register not in the SCB. - */ -typedef struct -{ - uint32_t RESERVED0[1U]; - __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ - __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ -} SCnSCB_Type; - -/* Interrupt Controller Type Register Definitions */ -#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ -#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ - -/* Auxiliary Control Register Definitions */ -#define SCnSCB_ACTLR_DISITMATBFLUSH_Pos 12U /*!< ACTLR: DISITMATBFLUSH Position */ -#define SCnSCB_ACTLR_DISITMATBFLUSH_Msk (1UL << SCnSCB_ACTLR_DISITMATBFLUSH_Pos) /*!< ACTLR: DISITMATBFLUSH Mask */ - -#define SCnSCB_ACTLR_DISRAMODE_Pos 11U /*!< ACTLR: DISRAMODE Position */ -#define SCnSCB_ACTLR_DISRAMODE_Msk (1UL << SCnSCB_ACTLR_DISRAMODE_Pos) /*!< ACTLR: DISRAMODE Mask */ - -#define SCnSCB_ACTLR_FPEXCODIS_Pos 10U /*!< ACTLR: FPEXCODIS Position */ -#define SCnSCB_ACTLR_FPEXCODIS_Msk (1UL << SCnSCB_ACTLR_FPEXCODIS_Pos) /*!< ACTLR: FPEXCODIS Mask */ - -#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ -#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ - -#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ -#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ - -/*@} end of group CMSIS_SCnotSCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) - \brief Type definitions for the Instrumentation Trace Macrocell (ITM) - @{ - */ - -/** - \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). - */ -typedef struct -{ - __OM union - { - __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ - __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ - __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ - } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ - uint32_t RESERVED0[864U]; - __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ - uint32_t RESERVED1[15U]; - __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ - uint32_t RESERVED2[15U]; - __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ - uint32_t RESERVED3[29U]; - __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ - __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ - __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ - uint32_t RESERVED4[43U]; - __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ - __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ - uint32_t RESERVED5[6U]; - __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ - __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ - __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ - __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ - __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ - __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ - __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ - __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ - __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ - __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ - __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ - __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ -} ITM_Type; - -/* ITM Trace Privilege Register Definitions */ -#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ - -/* ITM Trace Control Register Definitions */ -#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ -#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ - -#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ -#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ - -#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ -#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ - -#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ -#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ - -#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ -#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ - -#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ -#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ - -#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ -#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ - -#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ -#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ - -#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ -#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ - -/* ITM Integration Write Register Definitions */ -#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ -#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ - -/* ITM Integration Read Register Definitions */ -#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ -#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ - -/* ITM Integration Mode Control Register Definitions */ -#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ -#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ - -/* ITM Lock Status Register Definitions */ -#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ -#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ - -#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ -#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ - -#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ -#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ - -/*@}*/ /* end of group CMSIS_ITM */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) - \brief Type definitions for the Data Watchpoint and Trace (DWT) - @{ - */ - -/** - \brief Structure type to access the Data Watchpoint and Trace Register (DWT). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ - __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ - __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ - __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ - __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ - __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ - __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ - __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ - __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ - __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ - __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ - uint32_t RESERVED0[1U]; - __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ - __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ - __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ - uint32_t RESERVED1[1U]; - __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ - __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ - __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ - uint32_t RESERVED2[1U]; - __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ - __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ - __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ - uint32_t RESERVED3[981U]; - __OM uint32_t LAR; /*!< Offset: 0xFB0 ( W) Lock Access Register */ - __IM uint32_t LSR; /*!< Offset: 0xFB4 (R ) Lock Status Register */ -} DWT_Type; - -/* DWT Control Register Definitions */ -#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ -#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ - -#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ -#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ - -#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ -#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ - -#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ -#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ - -#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ -#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ - -#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ -#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ - -#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ -#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ - -#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ -#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ - -#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ -#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ - -#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ -#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ - -#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ -#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ - -#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ -#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ - -#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ -#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ - -#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ -#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ - -#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ -#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ - -#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ -#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ - -#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ -#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ - -#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ -#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ - -/* DWT CPI Count Register Definitions */ -#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ -#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ - -/* DWT Exception Overhead Count Register Definitions */ -#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ -#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ - -/* DWT Sleep Count Register Definitions */ -#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ -#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ - -/* DWT LSU Count Register Definitions */ -#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ -#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ - -/* DWT Folded-instruction Count Register Definitions */ -#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ -#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ - -/* DWT Comparator Mask Register Definitions */ -#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ -#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ - -/* DWT Comparator Function Register Definitions */ -#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ -#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ - -#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ -#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ - -#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ -#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ - -#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ -#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ - -#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ -#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ - -#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ -#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ - -#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ -#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ - -#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ -#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ - -#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ -#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ - -/*@}*/ /* end of group CMSIS_DWT */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_TPI Trace Port Interface (TPI) - \brief Type definitions for the Trace Port Interface (TPI) - @{ - */ - -/** - \brief Structure type to access the Trace Port Interface Register (TPI). - */ -typedef struct -{ - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ - __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ - uint32_t RESERVED0[2U]; - __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ - uint32_t RESERVED1[55U]; - __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ - uint32_t RESERVED2[131U]; - __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ - __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ - __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ - uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ - __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ - __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ - uint32_t RESERVED4[1U]; - __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ - __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ - __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ - uint32_t RESERVED5[39U]; - __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ - __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ - uint32_t RESERVED7[8U]; - __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ - __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ -} TPI_Type; - -/* TPI Asynchronous Clock Prescaler Register Definitions */ -#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ -#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ - -/* TPI Selected Pin Protocol Register Definitions */ -#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ -#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ - -/* TPI Formatter and Flush Status Register Definitions */ -#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ -#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ - -#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ -#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ - -#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ -#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ - -#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ -#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ - -/* TPI Formatter and Flush Control Register Definitions */ -#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ -#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ - -#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ -#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ - -/* TPI TRIGGER Register Definitions */ -#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ -#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ - -/* TPI Integration ETM Data Register Definitions (FIFO0) */ -#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ -#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ - -#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ -#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ - -#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ -#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ - -#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ -#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ - -#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ -#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ - -#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ -#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ - -#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ -#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ - -/* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ - -/* TPI Integration ITM Data Register Definitions (FIFO1) */ -#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ -#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ - -#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ -#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ - -#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ -#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ - -#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ -#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ - -#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ -#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ - -#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ -#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ - -#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ -#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ - -/* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ - -/* TPI Integration Mode Control Register Definitions */ -#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ - -/* TPI DEVID Register Definitions */ -#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ -#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ - -#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ -#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ - -#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ -#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ - -#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ -#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ - -#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ -#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ - -#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ -#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ - -/* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ -#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ - -/*@}*/ /* end of group CMSIS_TPI */ - - -#if (__MPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** - \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ - __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ - __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ - __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ - __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register Definitions */ -#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register Definitions */ -#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register Definitions */ -#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register Definitions */ -#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register Definitions */ -#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -#if (__FPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_FPU Floating Point Unit (FPU) - \brief Type definitions for the Floating Point Unit (FPU) - @{ - */ - -/** - \brief Structure type to access the Floating Point Unit (FPU). - */ -typedef struct -{ - uint32_t RESERVED0[1U]; - __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ - __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ - __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ - __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ - __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ - __IM uint32_t MVFR2; /*!< Offset: 0x018 (R/ ) Media and FP Feature Register 2 */ -} FPU_Type; - -/* Floating-Point Context Control Register Definitions */ -#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ -#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ - -#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ -#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ - -#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ -#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ - -#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ -#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ - -#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ -#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ - -#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ -#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ - -#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ -#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ - -#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ -#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ - -#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ -#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ - -/* Floating-Point Context Address Register Definitions */ -#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ -#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ - -/* Floating-Point Default Status Control Register Definitions */ -#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ -#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ - -#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ -#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ - -#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ -#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ - -#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ -#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ - -/* Media and FP Feature Register 0 Definitions */ -#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ -#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ - -#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ -#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ - -#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ -#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ - -#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ -#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ - -#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ -#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ - -#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ -#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ - -#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ -#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ - -#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ -#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ - -/* Media and FP Feature Register 1 Definitions */ -#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ -#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ - -#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ -#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ - -#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ -#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ - -#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ -#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ - -/* Media and FP Feature Register 2 Definitions */ - -/*@} end of group CMSIS_FPU */ -#endif - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Type definitions for the Core Debug Registers - @{ - */ - -/** - \brief Structure type to access the Core Debug Register (CoreDebug). - */ -typedef struct -{ - __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ - __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ - __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ - __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ -} CoreDebug_Type; - -/* Debug Halting Control and Status Register Definitions */ -#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ -#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ - -#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ -#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ - -#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ -#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ - -#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ -#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ - -#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ -#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ - -#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ -#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ - -#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ -#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ - -#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ -#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ - -#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ -#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ - -#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ -#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ - -#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ -#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ - -#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ -#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ - -/* Debug Core Register Selector Register Definitions */ -#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ -#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ - -#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ -#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ - -/* Debug Exception and Monitor Control Register Definitions */ -#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ -#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ - -#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ -#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ - -#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ -#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ - -#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ -#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ - -#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ -#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ - -#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ -#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ - -#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ -#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ - -#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ -#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ - -#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ -#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ - -#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ -#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ - -#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ -#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ - -#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ -#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ - -#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ -#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ - -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M4 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ -#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ -#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ -#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ -#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ -#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ -#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ - -#if (__MPU_PRESENT == 1U) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -#if (__FPU_PRESENT == 1U) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Debug Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/** - \brief Set Priority Grouping - \details Sets the priority grouping field using the required unlock sequence. - The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. - Only values from 0..7 are used. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Priority grouping field. - */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) -{ - uint32_t reg_value; - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - - reg_value = SCB->AIRCR; /* read old register configuration */ - reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ - reg_value = (reg_value | - ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ - SCB->AIRCR = reg_value; -} - - -/** - \brief Get Priority Grouping - \details Reads the priority grouping field from the NVIC Interrupt Controller. - \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). - */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) -{ - return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); -} - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not active. - \return 1 Interrupt status is active. - */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHPR[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } - else - { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return(((uint32_t)SCB->SHPR[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief Encode Priority - \details Encodes the priority for an interrupt with the given priority group, - preemptive priority value, and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Used priority group. - \param [in] PreemptPriority Preemptive priority value (starting from 0). - \param [in] SubPriority Subpriority value (starting from 0). - \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). - */ -__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - return ( - ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | - ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) - ); -} - - -/** - \brief Decode Priority - \details Decodes an interrupt priority value with a given priority group to - preemptive priority value and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. - \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). - \param [in] PriorityGroup Used priority group. - \param [out] pPreemptPriority Preemptive priority value (starting from 0). - \param [out] pSubPriority Subpriority value (starting from 0). - */ -__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); - *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | - SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - -/* ########################## FPU functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_FpuFunctions FPU Functions - \brief Function that provides FPU type. - @{ - */ - -/** - \brief get FPU type - \details returns the FPU type - \returns - - \b 0: No FPU - - \b 1: Single precision FPU - - \b 2: Double + Single precision FPU - */ -__STATIC_INLINE uint32_t SCB_GetFPUType(void) -{ - uint32_t mvfr0; - - mvfr0 = SCB->MVFR0; - if ((mvfr0 & 0x00000FF0UL) == 0x220UL) - { - return 2UL; /* Double + Single precision FPU */ - } - else if ((mvfr0 & 0x00000FF0UL) == 0x020UL) - { - return 1UL; /* Single precision FPU */ - } - else - { - return 0UL; /* No FPU */ - } -} - - -/*@} end of CMSIS_Core_FpuFunctions */ - - - -/* ########################## Cache functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_CacheFunctions Cache Functions - \brief Functions that configure Instruction and Data cache. - @{ - */ - -/* Cache Size ID Register Macros */ -#define CCSIDR_WAYS(x) (((x) & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos) -#define CCSIDR_SETS(x) (((x) & SCB_CCSIDR_NUMSETS_Msk ) >> SCB_CCSIDR_NUMSETS_Pos ) - - -/** - \brief Enable I-Cache - \details Turns on I-Cache - */ -__STATIC_INLINE void SCB_EnableICache (void) -{ - #if (__ICACHE_PRESENT == 1U) - __DSB(); - __ISB(); - SCB->ICIALLU = 0UL; /* invalidate I-Cache */ - SCB->CCR |= (uint32_t)SCB_CCR_IC_Msk; /* enable I-Cache */ - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Disable I-Cache - \details Turns off I-Cache - */ -__STATIC_INLINE void SCB_DisableICache (void) -{ - #if (__ICACHE_PRESENT == 1U) - __DSB(); - __ISB(); - SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk; /* disable I-Cache */ - SCB->ICIALLU = 0UL; /* invalidate I-Cache */ - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Invalidate I-Cache - \details Invalidates I-Cache - */ -__STATIC_INLINE void SCB_InvalidateICache (void) -{ - #if (__ICACHE_PRESENT == 1U) - __DSB(); - __ISB(); - SCB->ICIALLU = 0UL; - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Enable D-Cache - \details Turns on D-Cache - */ -__STATIC_INLINE void SCB_EnableDCache (void) -{ - #if (__DCACHE_PRESENT == 1U) - uint32_t ccsidr; - uint32_t sets; - uint32_t ways; - - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); - - ccsidr = SCB->CCSIDR; - - /* invalidate D-Cache */ - sets = (uint32_t)(CCSIDR_SETS(ccsidr)); - do { - ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); - do { - SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) | - ((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk) ); - #if defined ( __CC_ARM ) - __schedule_barrier(); - #endif - } while (ways--); - } while(sets--); - __DSB(); - - SCB->CCR |= (uint32_t)SCB_CCR_DC_Msk; /* enable D-Cache */ - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Disable D-Cache - \details Turns off D-Cache - */ -__STATIC_INLINE void SCB_DisableDCache (void) -{ - #if (__DCACHE_PRESENT == 1U) - uint32_t ccsidr; - uint32_t sets; - uint32_t ways; - - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); - - ccsidr = SCB->CCSIDR; - - SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */ - - /* clean & invalidate D-Cache */ - sets = (uint32_t)(CCSIDR_SETS(ccsidr)); - do { - ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); - do { - SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) | - ((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk) ); - #if defined ( __CC_ARM ) - __schedule_barrier(); - #endif - } while (ways--); - } while(sets--); - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Invalidate D-Cache - \details Invalidates D-Cache - */ -__STATIC_INLINE void SCB_InvalidateDCache (void) -{ - #if (__DCACHE_PRESENT == 1U) - uint32_t ccsidr; - uint32_t sets; - uint32_t ways; - - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); - - ccsidr = SCB->CCSIDR; - - /* invalidate D-Cache */ - sets = (uint32_t)(CCSIDR_SETS(ccsidr)); - do { - ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); - do { - SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) | - ((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk) ); - #if defined ( __CC_ARM ) - __schedule_barrier(); - #endif - } while (ways--); - } while(sets--); - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Clean D-Cache - \details Cleans D-Cache - */ -__STATIC_INLINE void SCB_CleanDCache (void) -{ - #if (__DCACHE_PRESENT == 1U) - uint32_t ccsidr; - uint32_t sets; - uint32_t ways; - - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); - - ccsidr = SCB->CCSIDR; - - /* clean D-Cache */ - sets = (uint32_t)(CCSIDR_SETS(ccsidr)); - do { - ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); - do { - SCB->DCCSW = (((sets << SCB_DCCSW_SET_Pos) & SCB_DCCSW_SET_Msk) | - ((ways << SCB_DCCSW_WAY_Pos) & SCB_DCCSW_WAY_Msk) ); - #if defined ( __CC_ARM ) - __schedule_barrier(); - #endif - } while (ways--); - } while(sets--); - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief Clean & Invalidate D-Cache - \details Cleans and Invalidates D-Cache - */ -__STATIC_INLINE void SCB_CleanInvalidateDCache (void) -{ - #if (__DCACHE_PRESENT == 1U) - uint32_t ccsidr; - uint32_t sets; - uint32_t ways; - - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); - - ccsidr = SCB->CCSIDR; - - /* clean & invalidate D-Cache */ - sets = (uint32_t)(CCSIDR_SETS(ccsidr)); - do { - ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); - do { - SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) | - ((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk) ); - #if defined ( __CC_ARM ) - __schedule_barrier(); - #endif - } while (ways--); - } while(sets--); - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief D-Cache Invalidate by address - \details Invalidates D-Cache for the given address - \param[in] addr address (aligned to 32-byte boundary) - \param[in] dsize size of memory block (in number of bytes) -*/ -__STATIC_INLINE void SCB_InvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) -{ - #if (__DCACHE_PRESENT == 1U) - int32_t op_size = dsize; - uint32_t op_addr = (uint32_t)addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ - - __DSB(); - - while (op_size > 0) { - SCB->DCIMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; - } - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief D-Cache Clean by address - \details Cleans D-Cache for the given address - \param[in] addr address (aligned to 32-byte boundary) - \param[in] dsize size of memory block (in number of bytes) -*/ -__STATIC_INLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize) -{ - #if (__DCACHE_PRESENT == 1) - int32_t op_size = dsize; - uint32_t op_addr = (uint32_t) addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ - - __DSB(); - - while (op_size > 0) { - SCB->DCCMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; - } - - __DSB(); - __ISB(); - #endif -} - - -/** - \brief D-Cache Clean and Invalidate by address - \details Cleans and invalidates D_Cache for the given address - \param[in] addr address (aligned to 32-byte boundary) - \param[in] dsize size of memory block (in number of bytes) -*/ -__STATIC_INLINE void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) -{ - #if (__DCACHE_PRESENT == 1U) - int32_t op_size = dsize; - uint32_t op_addr = (uint32_t) addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ - - __DSB(); - - while (op_size > 0) { - SCB->DCCIMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; - } - - __DSB(); - __ISB(); - #endif -} - - -/*@} end of CMSIS_Core_CacheFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - -/* ##################################### Debug In/Output function ########################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_core_DebugFunctions ITM Functions - \brief Functions that access the ITM debug interface. - @{ - */ - -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ - - -/** - \brief ITM Send Character - \details Transmits a character via the ITM channel 0, and - \li Just returns when no debugger is connected that has booked the output. - \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. - \param [in] ch Character to transmit. - \returns Character to transmit. - */ -__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) -{ - if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ - ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ - { - while (ITM->PORT[0U].u32 == 0UL) - { - __NOP(); - } - ITM->PORT[0U].u8 = (uint8_t)ch; - } - return (ch); -} - - -/** - \brief ITM Receive Character - \details Inputs a character via the external variable \ref ITM_RxBuffer. - \return Received character. - \return -1 No character pending. - */ -__STATIC_INLINE int32_t ITM_ReceiveChar (void) -{ - int32_t ch = -1; /* no character available */ - - if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) - { - ch = ITM_RxBuffer; - ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ - } - - return (ch); -} - - -/** - \brief ITM Check Character - \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. - \return 0 No character available. - \return 1 Character available. - */ -__STATIC_INLINE int32_t ITM_CheckChar (void) -{ - - if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) - { - return (0); /* no character available */ - } - else - { - return (1); /* character available */ - } -} - -/*@} end of CMSIS_core_DebugFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CM7_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cmFunc.h b/platform/robert/boot/vendor/CMSIS/Include/core_cmFunc.h deleted file mode 100644 index ca319a55cb..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cmFunc.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************//** - * @file core_cmFunc.h - * @brief CMSIS Cortex-M Core Function Access Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMFUNC_H -#define __CORE_CMFUNC_H - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@} end of CMSIS_Core_RegAccFunctions */ - -#endif /* __CORE_CMFUNC_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cmInstr.h b/platform/robert/boot/vendor/CMSIS/Include/core_cmInstr.h deleted file mode 100644 index a0a506458d..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cmInstr.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************//** - * @file core_cmInstr.h - * @brief CMSIS Cortex-M Core Instruction Access Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMINSTR_H -#define __CORE_CMINSTR_H - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - -#endif /* __CORE_CMINSTR_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_cmSimd.h b/platform/robert/boot/vendor/CMSIS/Include/core_cmSimd.h deleted file mode 100644 index 4d76bf9018..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_cmSimd.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************//** - * @file core_cmSimd.h - * @brief CMSIS Cortex-M SIMD Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMSIMD_H -#define __CORE_CMSIMD_H - -#ifdef __cplusplus - extern "C" { -#endif - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CMSIMD_H */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_sc000.h b/platform/robert/boot/vendor/CMSIS/Include/core_sc000.h deleted file mode 100644 index ea16bf3efa..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_sc000.h +++ /dev/null @@ -1,926 +0,0 @@ -/**************************************************************************//** - * @file core_sc000.h - * @brief CMSIS SC000 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_SC000_H_GENERIC -#define __CORE_SC000_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup SC000 - @{ - */ - -/* CMSIS SC000 definitions */ -#define __SC000_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __SC000_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __SC000_CMSIS_VERSION ((__SC000_CMSIS_VERSION_MAIN << 16U) | \ - __SC000_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_SC (000U) /*!< Cortex secure core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - This core does not support an FPU at all -*/ -#define __FPU_USED 0U - -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_SC000_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_SC000_H_DEPENDANT -#define __CORE_SC000_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __SC000_REV - #define __SC000_REV 0x0000U - #warning "__SC000_REV not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0U - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 2U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group SC000 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core MPU Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t _reserved0:1; /*!< bit: 0 Reserved */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[1U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[31U]; - __IOM uint32_t ICER[1U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[31U]; - __IOM uint32_t ISPR[1U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[31U]; - __IOM uint32_t ICPR[1U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[31U]; - uint32_t RESERVED4[64U]; - __IOM uint32_t IP[8U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ -} NVIC_Type; - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - uint32_t RESERVED0[1U]; - __IOM uint32_t SHP[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ - uint32_t RESERVED1[154U]; - __IOM uint32_t SFCR; /*!< Offset: 0x290 (R/W) Security Features Control Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) - \brief Type definitions for the System Control and ID Register not in the SCB - @{ - */ - -/** - \brief Structure type to access the System Control and ID Register not in the SCB. - */ -typedef struct -{ - uint32_t RESERVED0[2U]; - __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ -} SCnSCB_Type; - -/* Auxiliary Control Register Definitions */ -#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ -#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ - -/*@} end of group CMSIS_SCnotSCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - -#if (__MPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** - \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register Definitions */ -#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register Definitions */ -#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register Definitions */ -#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register Definitions */ -#define MPU_RBAR_ADDR_Pos 8U /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0xFFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register Definitions */ -#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief SC000 Core Debug Registers (DCB registers, SHCSR, and DFSR) are only accessible over DAP and not via processor. - Therefore they are not covered by the SC000 header file. - @{ - */ -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of SC000 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ - -#if (__MPU_PRESENT == 1U) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/* Interrupt Priorities are WORD accessible only under ARMv6M */ -/* The following MACROS handle generation of the register offset and byte masks */ -#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) -#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) -#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | - (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); - } - else - { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | - (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - SCB_AIRCR_SYSRESETREQ_Msk); - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_SC000_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/CMSIS/Include/core_sc300.h b/platform/robert/boot/vendor/CMSIS/Include/core_sc300.h deleted file mode 100644 index 820cef4f83..0000000000 --- a/platform/robert/boot/vendor/CMSIS/Include/core_sc300.h +++ /dev/null @@ -1,1745 +0,0 @@ -/**************************************************************************//** - * @file core_sc300.h - * @brief CMSIS SC300 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_SC300_H_GENERIC -#define __CORE_SC300_H_GENERIC - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/** - \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
    - Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
    - Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
    - Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** - \ingroup SC3000 - @{ - */ - -/* CMSIS SC300 definitions */ -#define __SC300_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __SC300_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ -#define __SC300_CMSIS_VERSION ((__SC300_CMSIS_VERSION_MAIN << 16U) | \ - __SC300_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_SC (300U) /*!< Cortex secure core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif - -/** __FPU_USED indicates whether an FPU is used or not. - This core does not support an FPU at all -*/ -#define __FPU_USED 0U - -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #if defined __ARM_PCS_VFP - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#elif defined ( __CSMC__ ) - #if ( __CSMC__ & 0x400U) - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #endif - -#endif - -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_SC300_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_SC300_H_DEPENDANT -#define __CORE_SC300_H_DEPENDANT - -#ifdef __cplusplus - extern "C" { -#endif - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __SC300_REV - #define __SC300_REV 0x0000U - #warning "__SC300_REV not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0U - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0U - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/* following defines should be used for structure members */ -#define __IM volatile const /*! Defines 'read only' structure member permissions */ -#define __OM volatile /*! Defines 'write only' structure member permissions */ -#define __IOM volatile /*! Defines 'read / write' structure member permissions */ - -/*@} end of group SC300 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core Debug Register - - Core MPU Register - ******************************************************************************/ -/** - \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** - \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { - uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - -/* APSR Register Definitions */ -#define APSR_N_Pos 31U /*!< APSR: N Position */ -#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ - -#define APSR_Z_Pos 30U /*!< APSR: Z Position */ -#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ - -#define APSR_C_Pos 29U /*!< APSR: C Position */ -#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ - -#define APSR_V_Pos 28U /*!< APSR: V Position */ -#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ - -#define APSR_Q_Pos 27U /*!< APSR: Q Position */ -#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ - - -/** - \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - -/* IPSR Register Definitions */ -#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ -#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ - - -/** - \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - -/* xPSR Register Definitions */ -#define xPSR_N_Pos 31U /*!< xPSR: N Position */ -#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ - -#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ -#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ - -#define xPSR_C_Pos 29U /*!< xPSR: C Position */ -#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ - -#define xPSR_V_Pos 28U /*!< xPSR: V Position */ -#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ - -#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ -#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ - -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ - -#define xPSR_T_Pos 24U /*!< xPSR: T Position */ -#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ - -#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ -#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ - - -/** - \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/* CONTROL Register Definitions */ -#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ -#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ - -#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ -#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ - -/*@} end of group CMSIS_CORE */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** - \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[24U]; - __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[24U]; - __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[24U]; - __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[24U]; - __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ - uint32_t RESERVED4[56U]; - __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ - uint32_t RESERVED5[644U]; - __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ -} NVIC_Type; - -/* Software Triggered Interrupt Register Definitions */ -#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ -#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ - -/*@} end of group CMSIS_NVIC */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** - \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ - __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ - __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ - __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ - __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ - __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ - __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ - __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ - __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ - __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ - __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ - __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ - __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ - __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ - uint32_t RESERVED0[5U]; - __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ - uint32_t RESERVED1[129U]; - __IOM uint32_t SFCR; /*!< Offset: 0x290 (R/W) Security Features Control Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ -#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Vector Table Offset Register Definitions */ -#define SCB_VTOR_TBLBASE_Pos 29U /*!< SCB VTOR: TBLBASE Position */ -#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ - -#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ -#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ -#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ -#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ - -#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ -#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ -#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ - -#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ -#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ -#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ - -#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ -#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ - -#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ -#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ - -#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ -#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ - -#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ -#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ - -#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ -#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ - -#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ -#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ - -#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ -#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ - -#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ -#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ - -#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ -#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ - -#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ -#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ - -#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ -#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ - -#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ -#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ - -/* SCB Configurable Fault Status Register Definitions */ -#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ -#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ - -#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ -#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ - -#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ -#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ - -/* SCB Hard Fault Status Register Definitions */ -#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ -#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ - -#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ -#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ - -#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ -#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ - -/* SCB Debug Fault Status Register Definitions */ -#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ -#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ - -#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ -#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ - -#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ -#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ - -#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ -#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ - -#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ -#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) - \brief Type definitions for the System Control and ID Register not in the SCB - @{ - */ - -/** - \brief Structure type to access the System Control and ID Register not in the SCB. - */ -typedef struct -{ - uint32_t RESERVED0[1U]; - __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ - uint32_t RESERVED1[1U]; -} SCnSCB_Type; - -/* Interrupt Controller Type Register Definitions */ -#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ -#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ - -/*@} end of group CMSIS_SCnotSCB */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** - \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) - \brief Type definitions for the Instrumentation Trace Macrocell (ITM) - @{ - */ - -/** - \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). - */ -typedef struct -{ - __OM union - { - __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ - __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ - __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ - } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ - uint32_t RESERVED0[864U]; - __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ - uint32_t RESERVED1[15U]; - __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ - uint32_t RESERVED2[15U]; - __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ - uint32_t RESERVED3[29U]; - __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ - __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ - __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ - uint32_t RESERVED4[43U]; - __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ - __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ - uint32_t RESERVED5[6U]; - __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ - __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ - __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ - __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ - __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ - __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ - __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ - __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ - __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ - __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ - __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ - __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ -} ITM_Type; - -/* ITM Trace Privilege Register Definitions */ -#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ - -/* ITM Trace Control Register Definitions */ -#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ -#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ - -#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ -#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ - -#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ -#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ - -#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ -#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ - -#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ -#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ - -#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ -#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ - -#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ -#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ - -#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ -#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ - -#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ -#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ - -/* ITM Integration Write Register Definitions */ -#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ -#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ - -/* ITM Integration Read Register Definitions */ -#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ -#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ - -/* ITM Integration Mode Control Register Definitions */ -#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ -#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ - -/* ITM Lock Status Register Definitions */ -#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ -#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ - -#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ -#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ - -#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ -#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ - -/*@}*/ /* end of group CMSIS_ITM */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) - \brief Type definitions for the Data Watchpoint and Trace (DWT) - @{ - */ - -/** - \brief Structure type to access the Data Watchpoint and Trace Register (DWT). - */ -typedef struct -{ - __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ - __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ - __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ - __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ - __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ - __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ - __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ - __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ - __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ - __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ - __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ - uint32_t RESERVED0[1U]; - __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ - __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ - __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ - uint32_t RESERVED1[1U]; - __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ - __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ - __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ - uint32_t RESERVED2[1U]; - __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ - __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ - __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ -} DWT_Type; - -/* DWT Control Register Definitions */ -#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ -#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ - -#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ -#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ - -#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ -#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ - -#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ -#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ - -#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ -#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ - -#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ -#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ - -#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ -#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ - -#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ -#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ - -#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ -#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ - -#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ -#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ - -#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ -#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ - -#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ -#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ - -#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ -#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ - -#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ -#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ - -#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ -#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ - -#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ -#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ - -#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ -#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ - -#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ -#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ - -/* DWT CPI Count Register Definitions */ -#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ -#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ - -/* DWT Exception Overhead Count Register Definitions */ -#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ -#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ - -/* DWT Sleep Count Register Definitions */ -#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ -#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ - -/* DWT LSU Count Register Definitions */ -#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ -#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ - -/* DWT Folded-instruction Count Register Definitions */ -#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ -#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ - -/* DWT Comparator Mask Register Definitions */ -#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ -#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ - -/* DWT Comparator Function Register Definitions */ -#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ -#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ - -#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ -#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ - -#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ -#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ - -#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ -#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ - -#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ -#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ - -#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ -#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ - -#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ -#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ - -#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ -#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ - -#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ -#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ - -/*@}*/ /* end of group CMSIS_DWT */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_TPI Trace Port Interface (TPI) - \brief Type definitions for the Trace Port Interface (TPI) - @{ - */ - -/** - \brief Structure type to access the Trace Port Interface Register (TPI). - */ -typedef struct -{ - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ - __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ - uint32_t RESERVED0[2U]; - __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ - uint32_t RESERVED1[55U]; - __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ - uint32_t RESERVED2[131U]; - __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ - __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ - __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ - uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ - __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ - __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ - uint32_t RESERVED4[1U]; - __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ - __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ - __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ - uint32_t RESERVED5[39U]; - __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ - __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ - uint32_t RESERVED7[8U]; - __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ - __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ -} TPI_Type; - -/* TPI Asynchronous Clock Prescaler Register Definitions */ -#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ -#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ - -/* TPI Selected Pin Protocol Register Definitions */ -#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ -#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ - -/* TPI Formatter and Flush Status Register Definitions */ -#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ -#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ - -#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ -#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ - -#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ -#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ - -#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ -#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ - -/* TPI Formatter and Flush Control Register Definitions */ -#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ -#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ - -#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ -#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ - -/* TPI TRIGGER Register Definitions */ -#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ -#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ - -/* TPI Integration ETM Data Register Definitions (FIFO0) */ -#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ -#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ - -#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ -#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ - -#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ -#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ - -#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ -#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ - -#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ -#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ - -#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ -#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ - -#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ -#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ - -/* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ - -/* TPI Integration ITM Data Register Definitions (FIFO1) */ -#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ -#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ - -#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ -#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ - -#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ -#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ - -#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ -#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ - -#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ -#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ - -#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ -#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ - -#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ -#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ - -/* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ - -/* TPI Integration Mode Control Register Definitions */ -#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ - -/* TPI DEVID Register Definitions */ -#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ -#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ - -#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ -#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ - -#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ -#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ - -#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ -#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ - -#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ -#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ - -#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ -#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ - -/* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ -#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ - -/*@}*/ /* end of group CMSIS_TPI */ - - -#if (__MPU_PRESENT == 1U) -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** - \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ - __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ - __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ - __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ - __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ - __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register Definitions */ -#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register Definitions */ -#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register Definitions */ -#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register Definitions */ -#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register Definitions */ -#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Type definitions for the Core Debug Registers - @{ - */ - -/** - \brief Structure type to access the Core Debug Register (CoreDebug). - */ -typedef struct -{ - __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ - __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ - __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ - __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ -} CoreDebug_Type; - -/* Debug Halting Control and Status Register Definitions */ -#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ -#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ - -#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ -#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ - -#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ -#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ - -#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ -#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ - -#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ -#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ - -#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ -#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ - -#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ -#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ - -#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ -#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ - -#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ -#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ - -#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ -#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ - -#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ -#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ - -#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ -#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ - -/* Debug Core Register Selector Register Definitions */ -#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ -#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ - -#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ -#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ - -/* Debug Exception and Monitor Control Register Definitions */ -#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ -#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ - -#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ -#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ - -#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ -#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ - -#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ -#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ - -#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ -#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ - -#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ -#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ - -#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ -#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ - -#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ -#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ - -#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ -#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ - -#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ -#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ - -#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ -#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ - -#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ -#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ - -#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ -#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ - -/*@} end of group CMSIS_CoreDebug */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_bitfield Core register bit field macros - \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). - @{ - */ - -/** - \brief Mask and shift a bit field value for use in a register bit range. - \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. - \return Masked and shifted value. -*/ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) - -/** - \brief Mask and shift a register value to extract a bit filed value. - \param[in] field Name of the register bit field. - \param[in] value Value of register. - \return Masked and shifted bit field value. -*/ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) - -/*@} end of group CMSIS_core_bitfield */ - - -/** - \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M3 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ -#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ -#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ -#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ -#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ -#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ -#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ - -#if (__MPU_PRESENT == 1U) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Debug Functions - - Core Register Access Functions - ******************************************************************************/ -/** - \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/** - \brief Set Priority Grouping - \details Sets the priority grouping field using the required unlock sequence. - The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. - Only values from 0..7 are used. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Priority grouping field. - */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) -{ - uint32_t reg_value; - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - - reg_value = SCB->AIRCR; /* read old register configuration */ - reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ - reg_value = (reg_value | - ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ - SCB->AIRCR = reg_value; -} - - -/** - \brief Get Priority Grouping - \details Reads the priority grouping field from the NVIC Interrupt Controller. - \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). - */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) -{ - return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); -} - - -/** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); -} - - -/** - \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. - \return 0 Interrupt status is not active. - \return 1 Interrupt status is active. - */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); -} - - -/** - \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if ((int32_t)(IRQn) < 0) - { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } - else - { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); - } -} - - -/** - \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. - \param [in] IRQn Interrupt number. - \return Interrupt Priority. - Value is aligned automatically to the implemented priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if ((int32_t)(IRQn) < 0) - { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); - } - else - { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); - } -} - - -/** - \brief Encode Priority - \details Encodes the priority for an interrupt with the given priority group, - preemptive priority value, and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - \param [in] PriorityGroup Used priority group. - \param [in] PreemptPriority Preemptive priority value (starting from 0). - \param [in] SubPriority Subpriority value (starting from 0). - \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). - */ -__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - return ( - ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | - ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) - ); -} - - -/** - \brief Decode Priority - \details Decodes an interrupt priority value with a given priority group to - preemptive priority value and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. - \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). - \param [in] PriorityGroup Used priority group. - \param [out] pPreemptPriority Preemptive priority value (starting from 0). - \param [out] pSubPriority Subpriority value (starting from 0). - */ -__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); - SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); - - *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); - *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); -} - - -/** - \brief System Reset - \details Initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | - SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ - __DSB(); /* Ensure completion of memory access */ - - for(;;) /* wait until reset */ - { - __NOP(); - } -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0U) - -/** - \brief System Tick Configuration - \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - \param [in] ticks Number of ticks between two interrupts. - \return 0 Function succeeded. - \return 1 Function failed. - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) - { - return (1UL); /* Reload value impossible */ - } - - SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0UL); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - -/* ##################################### Debug In/Output function ########################################### */ -/** - \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_core_DebugFunctions ITM Functions - \brief Functions that access the ITM debug interface. - @{ - */ - -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ - - -/** - \brief ITM Send Character - \details Transmits a character via the ITM channel 0, and - \li Just returns when no debugger is connected that has booked the output. - \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. - \param [in] ch Character to transmit. - \returns Character to transmit. - */ -__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) -{ - if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ - ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ - { - while (ITM->PORT[0U].u32 == 0UL) - { - __NOP(); - } - ITM->PORT[0U].u8 = (uint8_t)ch; - } - return (ch); -} - - -/** - \brief ITM Receive Character - \details Inputs a character via the external variable \ref ITM_RxBuffer. - \return Received character. - \return -1 No character pending. - */ -__STATIC_INLINE int32_t ITM_ReceiveChar (void) -{ - int32_t ch = -1; /* no character available */ - - if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) - { - ch = ITM_RxBuffer; - ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ - } - - return (ch); -} - - -/** - \brief ITM Check Character - \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. - \return 0 No character available. - \return 1 Character available. - */ -__STATIC_INLINE int32_t ITM_CheckChar (void) -{ - - if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) - { - return (0); /* no character available */ - } - else - { - return (1); /* character available */ - } -} - -/*@} end of CMSIS_core_DebugFunctions */ - - - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_SC300_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ diff --git a/platform/robert/boot/vendor/wscript b/platform/robert/boot/vendor/wscript deleted file mode 100644 index 46350bad00..0000000000 --- a/platform/robert/boot/vendor/wscript +++ /dev/null @@ -1,17 +0,0 @@ -def configure(conf): - conf.env.append_unique('DEFINES', 'STM32F779xx') - -def build(bld): - stm32_sources = bld.path.ant_glob('stm32f7haxx_stdperiph/*.c') - - includes = ['.', - 'CMSIS/Include', - 'CMSIS/Device/ST/STM32F7xx/Include', - 'stm32f7haxx_stdperiph'] - - bld.stlib(source=stm32_sources, - target='stm32_stdlib', - includes=includes, - export_includes=includes) - -# vim:filetype=python diff --git a/platform/robert/boot/waf b/platform/robert/boot/waf deleted file mode 100755 index 4b322f1a7e..0000000000 --- a/platform/robert/boot/waf +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python -# encoding: ISO8859-1 -# Thomas Nagy, 2005-2016 - -""" -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -""" - -import os, sys, inspect - -VERSION="1.8.19" -REVISION="b1fc8f7baef51bd2db4c2971909a568d" -GIT="22213cd8abbd141bda40667f7ca2a48f2d6ad785" -INSTALL='' -C1='#5' -C2='#/' -C3='#,' -cwd = os.getcwd() -join = os.path.join - - -WAF='waf' -def b(x): - return x -if sys.hexversion>0x300000f: - WAF='waf3' - def b(x): - return x.encode() - -def err(m): - print(('\033[91mError: %s\033[0m' % m)) - sys.exit(1) - -def unpack_wafdir(dir, src): - f = open(src,'rb') - c = 'corrupt archive (%d)' - while 1: - line = f.readline() - if not line: err('run waf-light from a folder containing waflib') - if line == b('#==>\n'): - txt = f.readline() - if not txt: err(c % 1) - if f.readline() != b('#<==\n'): err(c % 2) - break - if not txt: err(c % 3) - txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) - - import shutil, tarfile - try: shutil.rmtree(dir) - except OSError: pass - try: - for x in ('Tools', 'extras'): - os.makedirs(join(dir, 'waflib', x)) - except OSError: - err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) - - os.chdir(dir) - tmp = 't.bz2' - t = open(tmp,'wb') - try: t.write(txt) - finally: t.close() - - try: - t = tarfile.open(tmp) - except: - try: - os.system('bunzip2 t.bz2') - t = tarfile.open('t') - tmp = 't' - except: - os.chdir(cwd) - try: shutil.rmtree(dir) - except OSError: pass - err("Waf cannot be unpacked, check that bzip2 support is present") - - try: - for x in t: t.extract(x) - finally: - t.close() - - for x in ('Tools', 'extras'): - os.chmod(join('waflib',x), 493) - - if sys.hexversion<0x300000f: - sys.path = [join(dir, 'waflib')] + sys.path - import fixpy2 - fixpy2.fixdir(dir) - - os.remove(tmp) - os.chdir(cwd) - - try: dir = unicode(dir, 'mbcs') - except: pass - try: - from ctypes import windll - windll.kernel32.SetFileAttributesW(dir, 2) - except: - pass - -def test(dir): - try: - os.stat(join(dir, 'waflib')) - return os.path.abspath(dir) - except OSError: - pass - -def find_lib(): - src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) - base, name = os.path.split(src) - - #devs use $WAFDIR - w=test(os.environ.get('WAFDIR', '')) - if w: return w - - #waf-light - if name.endswith('waf-light'): - w = test(base) - if w: return w - err('waf-light requires waflib -> export WAFDIR=/folder') - - dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) - for i in (INSTALL,'/usr','/usr/local','/opt'): - w = test(i + '/lib/' + dirname) - if w: return w - - #waf-local - dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) - w = test(dir) - if w: return w - - #unpack - unpack_wafdir(dir, src) - return dir - -wafdir = find_lib() -sys.path.insert(0, wafdir) - -if __name__ == '__main__': - - from waflib import Scripting - Scripting.waf_entry_point(cwd, VERSION, wafdir) - -#==> -#BZh91AY&SYmEKQ#/#,Y #%H4#,`(br}#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,ӭul]Z}i{l4ﱮ_plkLϱdzvݺ{]xԴRq楯y\e hgEm.ǻuw׺]2]yk={[yw@=ggkWe}}}owNz#,#,#,#,y#,x->#,<*{WLaOl5nۧF#,m6 PPu(QB#5JJeGc@#,P6bAnׯZ>H'{p#/KvPZ6tj>ޏ{zl_7Z}ۡMZlζˮ83oM<k=bWGӪUrz5O{y뽇z#/o3PKhF;l5#5v꽍٦f{%ՎڻsgR˒}}ށ#/#//Z"i@[sǭ^s +ހ>Bq*KxG5_/=='}>9믽e6mvun{E{6u.#/N»^FM>O}ӈXL|RZu.9.;/nۯ7rj;3ek|Pܧ;{U)+Kw^|#,=u #,=^|vާvqw{]8tz#Hۘ@^vlλzދg|6%WWݶx};e>&{o=}{ok:{}kL}>>ya۽v֊ﰣqM_^/:5x_-IڝynO@}o+LUS4Z:=ua+W{Sofw[{\7v_|\V:/nR+wц#/_wہ@mg#,#,:;5kGvsɮW"}t@x6wuNVvFH.SyUr^N/FϧOЦq^;N!yNēN{ovkwV]vzskNۮo6So#|w M @#,ɠ#/iOD=CjAxJhD@i2hj6SD4'#,#,#,4#,#,A!! Abh'Sh 6D='67#,#,#,#,#,'QI4jrVL*iH ?#/7R Ye˩jҵZ*1m#,-BIX(Ȃ![*E#,|R‚  FVmkjL@ ı &D4c6QSR%%(ҊmELXXMSQTDJ 4M-J̠I,hM0F)6"JmIJZS"@ %#5`2)lQmT#/A&()%҉YFiUI6d̙4lUZff6mKM5-M2jadS$djFB*M&LZH-h!`3M1lb`4HA31#/I((lR@Kɡ#2f)2Ac%4TZ#/5*ECI4l(Q&ȥ1PlMe2&FR)6T2(EBH6"X5 "J*5D3b $Ԗ)1Hd3Fa-S J!*M M%`Ĕ,0l#M#FJ!e$ ),0ɥjih#/K264ed&bIZHXCeA̠ƣfSKS[M!3HDjL@IK-,QQD‚#/&$Ȉ4 #$4P!3-EaJ!&B6Qli&I% EȶF̆L&ʓL1*1 PDTSb(2bJE*I!e2,Jiɲf&ɣ&B`B5$)YIY6J@!%(E3A2H0M*6BeȔbLE#/&60FTi#/*%k,Qe"f&DJ,5JJ256h(0IS@Hb)cIlFe3K*Mjdcal#5(Fhemv4%J2SXEbQITD%KAQV"Ɩ!e) RF*&,k&Cc)Ebd5-*c$&,32Ye#k*ERX$)dԈɦVD5 [%YYJSL+f(i5KMD%ƱX3hH3`FѶѱQ3L5E!4jQHmbY05Rhe1%h5(ږm$5!bVA,#ei6F&l3)#5I6ԲZRɶmZ4fk32IKYRSJ( C5J4Qb- 3ѵ$JME2iIFL#/%0ŠE,AieHi)e6hbĄTZ, bcl3K1`2FLlBK!IlI&h42S2T4Rma3`#5TX1S6RBlE543 3hFhɩ"aK-$L؅3R2I҈Jԙ6lei+#dfMBT)Fj[,شIlbTQfJfdѩH$FY1Қ#@Y6Jhbb&I F5BTmm1I2 &h K#5e$*50ie24´mIkhЙfHA ,#/RJQliI&k2*HI"*ƆMEc&&VJQ%4ET*Je)F`$Qdd06KdԛjIEJj&CI3dRTXiH- h#53BFV&#JKEhƲEd6%`1#hTh1E& VZSTj-V1Y-f(#(1B$4lVmhҚHe#5&ѐ66hTZI5f͐Ī1h,UMIH()DF`ƒ-dԖIeM‘QIFkTeCI$Z66Z[$Il%E$ѭFhڡK&ZeJ2bFL`lMlTȨ1$I2$jMfV?5K& |RZdI-I/E򻴖^|ưTƪ?,S^‰bObM4d7%č3m5T;%v>zyg̳',uc/eLY]Z1, tu-'e)QjoQmnfN1jqq=c,’6r6=)`S5KJ|9WY[#nei-M(@ _Q eRFƶh&O9Q 6Y6?݀45ȤU5vZQX?haR^ԛ2*2dXaZ3#,#57AOS(C (`)UAҼurKe f>Dӧ&9%CE%OUm@QE{zY]J4}]0n(iJn@rpe˒nN銃}K[lU ,D(FR-"MTFzUiz``ΙO-Li6J@`@hԨOV<ՀژD{q`CU}]l]~>ٛVp򅧆o;J0)jeN8TLhj0CfB(t\÷%v3ISʉf0 09!Κچ{z XZ%l(Oa"O– :>0@fPeJ#5WɗRcݿZ6G"_7v$#}_zF: e.W7"^*P$csWLViУ?.]u61^9d5˖ƫ)84mw {_K幣U\XQd$a됻 (hdSC"ʌ&/_W xoW{J_7*#5yWMu4"1SJBVbcXjOW#k"ȆkP?V"0KElz'< "1Ҷd< !w뼮m9u#EsHsAuBzZŎPwQ&қPTD׾XIraivdF90 L"Kv6m-82Q`"#5(@%.}UbdtA30"+it!cE0ꆀ6v,MP$Xb T}uV HCZ*SHuqf?Nyaڻ:uf/ji80jN Ho/9`>/,)B)A:+v#W*|u &7DjBQN84K&d#5*mg|͟QNo (|(s_|V'rI3mc+-IEMT}[@0Ž$‰hJZX|*&dFڐmĎȎ0Ggk;e x ǜnoqO.w􇞘7p3=FP'HOhhq{2oҿѯ%B҄:?_ͺCGʊo.WwD+:y/"]O{{X*Rpkq*(V1)"Mݴf솘PcM1AīpҤV=A=Fn.=oeH!a$hN|dm]-єL@:;֔?݌v3ܥD#PbӮZ:4@I*h=(<+4|#5B_x >4]GH |]#5b".x#/.Sθtaaؔ7lDݶ1m,8Hѓr.>y#/A; o͘HjEAhlWâ(v\v01:e9J0GIܕ[O6{0)JjU׍.agmAg@I#5Bމ`Ga-{_#/[%sO>ѻ5F31 8 971Fmz(-|4bdj~.QʤX=_oxpU5W,{#ֳ'Ti<P8):YmMbw@ @a=xեd}y+Mub#5n3M6~R-١\x@p8\6irt{PAFhSC1bR`^;4l)WZvEeCL;&#/Fҧt^yyxq.K3ᘝQI:d$Zg7B=0JBJq}>4iD%RRǒ"[CoqE1{~Y>kZ;xZoLhVR9H`Mux7cOrJD2Ǎ: uvOreDe$:3w׈X *B,J⡜^0swÓXF;n^߇\T#5pLt"3t8ǧњMH(1BP0Ajfӻ}s[{C}>7M/e-oe#5#5ּ dSR{^{}i#,$ NjGG5IO?˖WgLg]E*; z/o/bwA^?%g%zn|ö.?#}$q=^En Fcvf,#/,gg!#5~Pi::К:䲦QiW9kdUWkB#/IMvg(:0"p8H/F@:JgêUבe;tʙ3۹C~ۏzM,Tw6j)!kv|CP.QGB|r=*D2,Ѯw3J?JwHMS֥XTj,)<'i~Gu>w9w5Lf8$ZLf4.ҜLVsR`Y ALjg)œBj~oJ4}vZZU):g{u8Px󘳜le:o]<{ǽY/?35_1)4:m˿Cu (b1#/4c-;2ǣ1T]cFzfY#5FmUSZ6I+dU^S#/4O D=[q1UOֱZ~3"Yc|4oZ0]yQQF#/6#/AAz`eTT,UQPzkLCh%jVMP-R.dNdMdJ;i]\Q?8Uؖ#/ yJRN5Z+H5Z:||jnṽhq֎٘atI#&Zċ# Cm).#/6Git/iP-~Z0:~J+JmM}/sڻ^i@%CAThݰ|Ƌ[4wpUϧ#稺f\bLcBV8q^ &[>ܵk6l<9^K#/Sm/;)tVE,iw-#54&7ߺutdwi#/>Z~ә]wD|}>#uɝMgYYRTYqfб*qojf+?gmK&LC}W$3ӫ4e)ޫbՓ|Ʃڲ>u?#/$i[S߹ԉ̓|Ӡ#/Sw?{F._Y$Fl;timvCO+)~:*xd!4W'߶Q1o؄IfOz6k.&܂ws$D9812ͻs#On6؛*3ל[LhW"#1|#,TMEMqovs6]/q\<$AC}%LSM$V9l{zk;O~)tW\kרUoL|^ӑssٍeݛanʑ j@zA63W{xa$++o}Z@e]ҡ3T?مSU5an~bPI /;#5 ުR9K8ǣ9ѽ(Kh-Gs(k8Aqj wǔUsǓόUy_'~+~Xg]I#/LxBw3>gGBBq]!w]Ϝ]vgNM4aB9s5xsG YY$z@3^Gt+%y ݽ,|RT$ICY;gX$MC٢vgdK9oIV]JvsȵPRѮa3Q٬C >B0TUBEe([(hG\IIުKD+QYf_m#/t};x#Zַ>c8K|MPP和Dd!,IK惐eNMbd];>U!yzwgЪP[@?@L$NLjUPX[7ev4PiϛiyЏI'iq vy#/z8Zj wz{&|IZXDF ޏyT,c@#,`>nfkX~8~%ec< Ui۠Go _;#/[Daw "ŋ>gΤٵ]'nefqU őz='U.a6|vOsӤQɚo$!U,U}Z#W`u80HE[^ۏ`//]#/tP^(wQnV%tb(mݽpL'o 5's/@ VM^%-baݗb~c -tP#51UO,P&w̯m?buY뵮9T?M,xg ڡF΃ӮݷXGU^eku'TIAHk+~F!Ivs^Qs@M=#/fQL"SL+ܡKSiԣsOǃ:NVʹSsAPT%B'? u'.B驱Ph`1վ@vh7)wy*ޑI$5!e̍r&mCC(W&HLRiBY{Nl͡3)G54n@;7覧͑)uVfe5IDj߄!@hG4hf6DX9FF)ĻnZCDKmIyնa4A?g X< Z3޴Ic#/Hc?XXv[٠*뉣QְƟ{wN4FLdIAۃ3[BwE f찲8pVC #5L5i8.^Tiݬ40\s\ll=lpvM#/>W{l3㢏̶WTxTs+•S{EP~n^&¶P.XZ9IѮ=SF9g!8yW4`yt17,/+i'}nߔl#:>s򚧘l;B`idVF8qtyG65#/+oE{fw pp(&dqӍ|fI˿1 Ucv餑&`q|kL 5XQpj23(!;A|Y#5mJ:!ӷkMV1Ǔ&a/'kòԉ*NuDL3J?g<@dCD#5j3xXTin:2.\ iQccyH,la%Bt0?q.C`B=u"1v b*pe6o!!&[3F a6?>6L\Auͷ.ӪR|n [#5 h;2JASģ6.fJfZ%+|bcNG.g]w$93?5J7*Wkjr {o;XZwwٷ n3@\Hfŏ]Zdj!v5n>Os5_FjE[7sn(D㿁㩁۲f}o(}˥/o3M#/*\Sc"]teL(Dv5yCeKyl!8UdI 06`BB[Ce؁x\NX?~ZWxV8gE/ptv61+- Ku!+5D7KaA٦qd=3`QSObYuvL 7` _3ldZ+C|9ϻ{}2%\zmoM[ j=4q0HYa8B󺻼4;,iJH,XLֿo?Gߜ=r8Le%0(5aHRP+>{jEDEAa zV %YbT}~Z 9; < 9F*PO~+fcW%0HXf0fd<wTuH6\E0i]*ꂝD\Ll`M6WŶu=&0t"|"nLMov=E'8HSϷ!#N^ Zi[<.6P*#57ePS7bPG戎R֟2T4`@8jLLFK"f 噛#5`g-TbB®]Mt:ˮ^6-Jh?1%0")" GJ.ϯwySICQ(`a k5U4[5-Qaɋ#/XEV\ !]V"/z(~r#5+jO AK]c٫jv={[vdIj=qFRUxoͥpUswU+nn:.zӖe~<P11gM7*_'0J7(#5H0Q車W2rՓuܹh) OntI [_ٿ]A8VTP[ԓNq|tK[yVW6NᡗMHƑ @ dJ/5뱱^j-/Fd" REH4I#/ I.aϳ=u:jc5VRk/Iv޼scˁ#sDOGMF)Hcp"MUoWuuG#){Sd>?rdr6&foٲj[!J!bSB%Mk6{fp#/2ѲgZwʏ/80n\9P$=Xu,[&i /RdOʪ)(d.Z(QET(0QϵL:+wڌgw9!JiKۥsfN~\n6$<>).X! &}b 2 zl vLTd$yD}^gNьRuƖ8Tݮq/}/)$^sfl*rʴ̋U,^;\Mt0se#5k,6EUPq'=q#^D>lF)NBxSqF* Y$/#/2& ܲda=ݭ,\&f*Zj!;19TuCBs U.$ڌr\H͹-inDSU<*.;`X&cf֡ ndLNk8HQr>zehS30gc`L#~m!##ǁ/FTo]ltQ~Pbi걭 #/QmaՁ-;ݾҌ[C%߁Ijr#o"'R PxW+ۓi"#]1BF]ȌĽ4=9xOsz_;DU}>*6mc>N+~>ų3f_:ʹؽgDK;#5 EQx4{hyOfEʼnb|_'AC9<9ztFBӖP!׭Lk;w鷕#5iYŽ5VGW3a14 QcziIP%4Qhը%BF*hҪB-IWNX0; ]sW{ws"[A!!Bb3vL%LHfʖ%If&ңkvlQY\vq0Ι &1 =Ű^[-?MnzMve4.Z)M!t}^Mzߣϻ1DF"wh#52ObS&H'{_LxAN+Q dD>{]Lv0Q0$3~rBEXڌxgbdHudY#5gEkK^]JIm\%%Q.?SV%l"eHh#5[*]11CHOџ0xjg/$(o%_#5U0}q_^?3: [46?SL[13N?$y%1@zq m"z_ ׸n"=xM *3 IU+N_F,#I@*".N#5èPCdQ.q.CBk݅gbkdE=l|DU)I0 J#·MjjfuW/$㇮C?>Tb-߻񧮿ڸsǞ>8[%hy*M"MyJNm>ֿ*Ei|Xgav1t=H:oR<5{-G"j Q&j(*#,Jo|ezmf~?þ)頬M''V7q,RveSw28/Vd$/@T\cL3cv-EأL>4#,%3#5O=Q[MW9et=c_[*[شE͜ϥzd aB#5L445>KW*Yz=RL?ܲ\*wy~|ά܏N& ^ ?.T8#/Z5{ąco0Ns :9t "uO|U&;l}I~ޚG-Dǯ, ooc|HxGDy!&B1d8W_?uٓf!*O@|+N/?ugYny^9̜S:gwiDIM~4 LFڟ{!V4 A2IbBpVl#Y.tcHLJ~ z0:* #5|KTR#, TDDEW LoϗMea%'_\Zn2Sh2P, A1 Ҍ]oFi31-t1zԿͳff{{>th|tܻXgKzuGqs#/xT{s 3Gƚ/P?^tiʼrgQuBU~TrO~Cȷx8OM wkrWD_)u^xͧwD͟<ӻzs˟=m5;z4\5^-m7~}7VGifƜףɻ#/+%B{bq9N+Ž:ނ,9?fg=|Ż#/JGFju~~=vl?0zJOtVyl4S\,ZIp=%>^.S]ZUU:k;9]1Ǝ|vnjgmJyݗIͮ\2+ׯ#5U<8Ig朗ѽSIR$Mx~O6{!׿?y- +d_v:'=ʢAPe ʝ^luo>änJbۣ̚ڏ՟g՛B\z*zƿW51/U[dXhݹgW=9µYg^Y}O]/n{kv+Mrd܄ZK\68j˝qpGtKװ3fٶaȗ|~lmٺOywXo?[|MB-^]>&>D(hwsZIP؝FFU!d9SqCH"L$⟐_ *l>@EZ25fGi ߻_~z#9spXo뭼;ϏofolWECT|_~˳s+\~_x~;t~Ia&^7ͫ/=f8|EW.vծ|'ӏi̝S|Rzۻlرu+ZtPgJP9[V1qB T/;WR؞/ݩtT벙^ǮeJKNWfTSsv'k)s Q)cy#EI'!i!Z9/?#/^k#5 >_;*kM;:'Sf<)>8r߄1dsh!r˦~rl}5սwTb(c9HJYW=K`Բy5|Vr]:vwG/#t5/#/pp#IȃU^^J.Ew˻/$Wa\qZ̹%`Ϟ*;GA֏utaV$CcE(8#5*"jDF 6Ξ[k[ `Ҏ LXVaHhYSIU X0#@Ӷk,06Y8h $mE=MX޳!uX~1&~Zj]'hkQXغ913S%R(ȝ t{t/9h#08$øJ:QFk1}KIPc 0i`QV M@4 D A<wofIxS-gol,)~tgҷ1Q/4Wh?9YlZ%C̷a_#/z~ӮDՋu~-2]%<{nw;mx'=i1i5B}#5>fŮ>_^ZiBB D8%kw?Afowqע}Y?iI)B1x^@?O%>?㪥4N=/gu~>w祑kݸ5z(}spG|?~Ao{8VHm0acb!h22!In0KJإ%IH@H2)#,iuF$ZLڛ&ɮ^ĎS)jr7h(UX`ȣH*V&efeX6<+-iNF<+ړ{̈́f#/IEANivyN=QR29DInB pcK J )"%桶2)@lƃ2#/e&:E'(<)LRۊX-7օ̤J#5%F#50 (lR>7MiƗs6B⢷f /ZUV jPX`- ed?@Q'Ѹ.j.DGqrhs#/1>=j0}'tP0sM8#/$Ҿ0,.e/~hs7Q8m5_:{dltTmg"vSRi vIn7]#/CThb0IDZ^1fe!i?#8^[50!5;<(.#v;)|o2{\nXƖTM ڭEU6?SQױfhTU[wBۤ/BBW&/^a6yMRFjnH$ohq#/<ڔ%赵&r;_56UȇV1'}}CnL頀 Dڤldq!b?ӗM608;"·L!O7ztwO_DɋcꀠTH(Qk(VML ]Ƃ^1a[Tu] r2Ifq|(x8V3]*0be#5MxX0g⭖g1@aٲ=h;_FC4Fݣ*%i'eCK9ntlV Ƒ:iC9*i3b4PK5j&JQ@z+F ,#Q6k`bJ.^ )ivr6Qk0wc+z;EAhk Z4!5M)4ܦ &?}ʪ;CS^_kw3^cr*>V)mItҁHlu~&|f7TΘ77lӀDc#5DxS &21 1l'bF֓qB2,Ter9qUTМjdą9SL̅A Za|C;Ү-jތe]J1@l*Cr[9P@ tДC0]#áfӳAqCI*V&sb-^,\y`tZiAȤ GbvHƈ鐔ORkYkӿwTGgǫ{1J6O)Wj63srZ0ɩ4NX#I(9NlGqVdcLprvaGQ#5&Ӌ0ԄL#f#/j̐ ;d3,X nbIQp.6Aal#f*U2es]כW]9wP$$`ݚbVMw+^" 7+]](҆R mdGդtD(4x8)#5 Ȓ45lˌH#/ao|!BiNb!8#/oDJ[ˀ%U;3a/8k fA#/;~doa]78:ɰ`BLvP|lkx ГZͩu4f}]6ɲ,&M+wn#/ 8,F r7QJ--vmktQ6v5#5',jv+z΃+GcvsҴXqW/!X:X1qfn\Hk ƻ\ٜLOpsn\..LҪZ%'frfODHa;#/bL^v\#/7->6WoS-~oFqR2>De^F/K }`sD!!&|=mw%O~k esQ?"0J+Uny\(qUF̳Sls]$:dsu%̧_#5sL3ϋ{7f|2SD錙49i[>&02@rCwf˙YmsDz;A:vY1b]2CDv!Roqb|)ipg'ZlGL#/{Oqg=oX[å@cCY2Kdu}ވk;95[\A|+/wb$-2 ex2f*)ї-q2\3bBb`Eof/\d#/$] PqA;H;3i3ϵ@)DۜsLTHJ&=S^;y3ԞK#/a#J('Zen Zoܲ[^o(5do]h=OhG&{#5dZgHz뺁CZ$VfS%{i n˧(0G˫)'|Yxhv!Ƽ1׌sF[Huh劥xZ_)DijL< yc̯EOON4Z&NFCe5$?S?D3ʎN6v.:!H<]27|j$Ӂ0T$=̓,agG>Dމ@'.'_%暷 Ugn=-Mro^bZkI&t:p2-{n}n10cb(_Fձ+V-[}ʲP̍ Gv3^1'b/Mфnev;J)lʗqµwv]έjf;ÿQ_'.=Ӌ+~ݑjaߋgXXl#5ya(\Cvw3qf2N"^JH!և˂S'* {\q#/3ӆ_gP]<_篵Y:p"&-P&5*ZLʙᐙET4 ˩:GX_sB;2וZ~gqwFGF"[1jjuI&VڛzClg* ] ]jPP-~#/Sa[f<ҋr9bvzg\|Q h!^Mmc۸.Hn%V3ONc3i{=լ{|6RZoc.i.(aU%E;·aQ&-FL4aW*$MV !ɏI9GתbDd:ޤ-WjjW^6U;KWUvQ.*J#,ܑ$s\8' {ǧ̿=s./8<W=Z8l`;1)qZzΨ{Q‘N'{#,߮Fěnh;m1M#ˎq(VU qC%I<͙4sH51$1Ȑ'lLq-wqVa4&n2/Go\\t%*2L$m,<#5S2[s} AR&9rD,(K賨!LFFvP!fyni&aF,=x)Re޹=5"^#?#eO-eQ~,x-L lgWHח̣}܈Inh{aЌؚTyV驵0avD^f~MQNAWG~ݎQ3])#/l\|H.f|`khd7}ggXVX%AEV } /AFsY~aG6q;J*89Bʏ7oz<}*ɴQmqI88ZTD[CNVFܳk%E^V4[w \3:jRk)rP௼m9e7ƫKpWW>]4v2YsuJ*rO$pzoƼv'fʔ#HUi_}HDƚc1I=RYSeY8=XlY$]|txJeĴN 6h_Tvt޿/>J1"U5(n2*KCz׍_[bQA]#fqiׇUyNOS瞝q 7u'#5y$ rd53v{-q14Ltpo6Yu"2WΪ9VBc[#5U;elaW[Sk\Vd.an5ĝ$5jwǺ&6Kxkk7qӶd"`|HE3=k}ZǾ3NX^X\$ΞHi%F裣|3bg\#uNI{w>Y7Nnu6L55ʅvY nGF9Ն).<9ZYyByjrOpe{#鰞b\A<`STOJYL.gBVAeUbU2no]`D{TwjuD4<梺c.甴}O>kJb~n9FI럿O#YB*4/92W^my$L{7D.ɢ98}Ǘ#:HJka#//w]3mWQUpͦdE^sz0E-+aMk!J7YO+O++^A & S؞Q#/ԝ2G#5BAhwl*Nn-z!>+5S]S5Ot#5Tt>ϺSPׅz>74"x0ݍ zЍXn9~]Vro.iG\\\$yȼAw2 T܋ NW4fFٿWJﺙafZF z$?CYײew$ |<._a¹XGFdx!+x#5B:&adΓ Rª hR#5DMhDc7,O=P1e0|eɇUlޞ"^UPԛzn=c&˗M%;>^X4}sIڹG{"Bo.ZcJš&Bd[ }ά!#5%PVoWA-\c#,1D^뢊nyz_Nn/vtw~rk$cyco`]]~.Ŗ2Ooģ529#/K%yܟZ<ⒾeB;sni[%uՑuDz D]S1 +Fa  #1騃4u2#/w@kS.sϞwWCtp/Yk͗xGb;;.;EǴwΡ;a'/[ʩ ܷBvǑ(c|7㟨]b։CdYTc<7WӋUw=Z0knDqqZ7PlRjsTտI,e 51fnM<YdIu[\gWnV$Q>եTbL/b0׈֟O]MznKҿӪܼ yWAF.#5{/~ mqoG:7R<:ߣ2cAu^N!Cڊ\Ā C E|HY9(Kʿ%#/'F/;̝j>`_dG2/^3=͎ڮtìl5JP?HK3O2Y*ݻLl72R o(Lb5~CNw{E.H^:;tBz<&TcK#/vGPfef|zm$爄r{pr>ISyYCd5)cы{R\٭3Dɾd7F?H\Z H\ u?5Ud!z!ҘIhN#/$ۘtaql1Vd>C0JDm"+&-X(f#%!D 1&H+PiQc7`; sJmb U֭U"@1,>*qp۹'jWZR4?XlFHN/FT ylW#/2_Db0=KE*on~3}S*Br{o=tͶa#/J*#4W0SFq߇J6HGmօǕ=#ŀV{y`w_(tbGrX|Ulo,;gxaD,#5Jύ>GwG#$Ѓ-#,x1w@P[wpIM=SRMݡA~q18D7CVKmռN̓a*RhlWfQF=ڦ9.w:3t)IgR FݍpцVLMva5# ?3TւqBR7t)mG(eA0 ygDߔ_Y*k~+Q_*KNNJ=L 4qRhKnf=`{NV5tr~]O#@tFCP 4 &"Ӣ{tHwaP7v[J/y]TM8L -1>hfkbRMdRsӚmex:#/^,󄑢A{%dyЪ] WY0kNRF9WdoaK|X" 2CɔPQM ~㙴a#5bI#5Wcj9e[ncVtSr72A!t~T%IiLY-ٳttiq\;oNN/+#/zTR8 )#/#/_#/#/z1/ggsԃ( l%supIY GA6x=W#" ;MN]ߑ;|K4!/@CCWW|3bі뭈71|16c.<5o OCIf;#)a,~~X3qvXgj3+՜;s1sOu'GNBˬG?šQ,]XLI,uH|i8J(M}wA/Wl#/s3GJ+M %L\h婍LU2klg&jiXLPiVq!o8s +#/ZלLVN|fñ}/7MA)(c5iEIc#5m99ʉxm4l4jA8MA#5hӷҷp&V &E&u<"Jl =>M|4"RszH?WXR.O.Z MP0Ja"6._o/<1*I aʪTz36;MX}c! D&akV~tӬ 0|e\gn$#5q:3c|I&D?frd9 BMtܿu-}8A^ZO `xCQV-@F); Hb{cو_c9E#,ZI @EL0*"ï.%%R zG1iEJID*Ζͭ3<6f",`R`Su&gX@'q(&52I|]c/c3|!ٹ<"fD]q?l6%[#/^!j 9qL=M(apRjMTZXxe1cHI~({^z-#5?GJ(9ވ?rgCi>.s`?Q;#λcrwyo iV,;mGJl)],x3A##/%i!aHlu#58 sV;8P7(hWPa)/zU#/;he#5O3š 5!#FoտeC[:12 p:D>F͇(wQga(0š{z+BC0QEjun#/,YѠ$d^O39:iHPYET, q<35wmG&#/3pV` I$#5**P!*Y_AD}#/B<XԔ "B(T{#/y#/Pyg0[F p8mymp΢H]! ?9r':"MP:1H70Nl`;(Άq̣PKD}|U&>ul*4T۬%qk^80 <'OH<,u#)+#ԁH \^*pEN>>#L(1#(.UԸO{ɹ5DV% R] (׬^YO sУS7ś`CwȢ*ᩈwńNqV4Ϗ=9P a=JJC#,AH=a ȼHН}=QՂ)tEkag=L+ M)DN$[$!!&A #xK",Mσ3rr4&z8J VɛXX#[`Z;#5dtSO+ks!u~HWZu' e*:*#5Cr 2'oh(:ϲ`Lw@,7kW*/-SKUBZ#5SB6$W(M%C8/:좐fgR<#/1ThzʮelUdߜBDH$ mTK=hA0i*zM[ufr7 63PA9 N-X󹌊t;TP1m2RH(XxM(!2J97>SXc$T#r8"pyK}hG4!'))dt-vE؅[rCq[aҸ2.ˁ8#/(6 mJUKfQL:kȳ>~N\&Ϳ2BV~c7n#/ɌyC4 FDA'iT68|#52"),(5#)Pp 9QHmt,\tvvG.K} vCEΎ1d4>?P=M!5HS|z~x!ˣ47F'q0t,#[A1,Xwl#5{؋KK9lwܷ#,!I'D:u!zs,ykl;@#5LqZE ̬#/u;)Cnuf5W~"&9)#A-3aF@J2}yB;Zv`Yԃmţֺ,%)1D5$:0lTLؚ57q=%$7#,zۘ4zmSS߆t;J,uBuR!"TIȃ5~#y5$!t$cClzEu]u&fr^j`KJ E#,6z~ M: A2ANvl1vkB>܀سp?tL“712>9X+s:ŀx~ϘdcD>Xv~Beo{lP3\yEzC~UdM"aiK(S&Rif챇mOt<#5VZ:\XJ%o|~&b A+: @#50Y3zta/da1A_?!!a/ t]a&ex@=IXtQBׂeuүʥ>bmEoOwYmuw'.pQ̆!3ss#u1ygW[#saI $(HB?%6 LOya1ZDoHj/a|bB/P2w3ur æ;qHqKD[8VޱY~G#52y_~eu]fWx /}?30_}R6-bjy;#/`o~Vև(uDY_s j&8c_yfeG4ʊ$zj1rVaDkZWG-g2mjg}ag+%޳6aw1rNNs5311P`Kty!woő`N1/(Kk*>$>@~1[+9RqX̰r#,&sCR"&H<Lj㷯{(ػ.qAc?.hD d(|m7#/@OmLoO`^.޲]p0ttWG)xty(kawu+u-j߮(:l ^uyɢ`Y-Amz5yT禇˫ѥ"ߗ '9i#5|f"Ώ޸] 1z1[ĺ|ZӒ"kmYlk=x|DYe-bBfGC\".}yfn޽5Ϝvc1#, Eb"P>!#޳9NgLOp޶f` 'e3h 愀)9⬣u49s"➺_Mُ75L."H{upUg:z8]}ߚ2۝!@3'#5|#/y;bs*/ΠCxo.^Nʔ:2t7OĆ!8Kےri(ۈMgUb6vLtOt*ddtjC~yRX~Lw$j«JϜf.(|v5|cw1Y%;;v{]S0{#5O~aSFҺ}#5 SSa:; b4͑M0v8Lu_zvZ9J =T*;Rk ᖦR^4p#5~zHڞ$Op|-a{#aPt΢j+V3ZmY·EېHD۴BdBi]k%K~fQx & ۴P18G $jŋ%{jYBD^i/80(OD,|-$ʽ.kstӑM@/( 4(@vχ$葠9#ƍ_#5tM4aG.q"I";Z~˰#5]5/CKKSdixj!q mAV-#b09PUphnkYB.'Aly! wmA/ҳ4޿Yw611>o7?\/M<#5g"ѠMSfZtY>"Y:u7R#9D/;+:L20;.RU]>McNnI#/O峿,M61}^'[X* J|wsiH޴V<>O+7NEȄ*jI@JH8;׬su 0b.TStǪJ+3Ndl%fg kDI!{H_wۆ϶>e(nv"Vm@{y OAׄy \ޏv,TN-e{ed:*gsC;'J~lHg ͮEW7u+luSfMYL`fe#/k8 uq凛9,x$YGCMp@pkI_luPaw=Hw>ɩ;qx,CʍXL!T2jve.l: esj52"X~O{{ay}ۣѷnVKJ3lʺ5NҖ1,i{lӅڳ ucNi0tl_6Κ#{Ag<{]^sS)Zijތ@]ۻ}ٿ<0U&7ߵ7Ȅu0$/ *gsᚾ}+ 97uE4J ﭱeO"&ܵ*NaĚcU:bxx*Gsf&#/f2J&fEVfE*8R|ZV2r8MͫKp`}7'˵UQy9ͦQ#/(8zXiKe(UW0~y^(:zKkǒ dVjNJ~nNE^P4Ǝ#5BGeS i)lO>hfjjf )t~3ߘ BS/jn+o%CK,kUE.ЇHVD/$ۻD|v>} 1(ǝqEٰe% u2i,Ѹ.'gb9YV#5k6#5*t0#/2RJ~G`jM]beqKAJ`bi8!#/w=+-W8#,pCۈ\w_c]gѿ_tyC9YHfQ5(s68e6;iDv7:Lӊ:CÎ}y!!T}E#/nE Q&Ga #/=4!Kki7s;cQ(>=9\eV,,Y9i@{}6_w0RDXy0h~=O6{k1|=9Xƶd{<ȽRi##5;7PR*&vlVO^55q&Y_f_#5mڕ+ FoӜpيvcgUrR~y"ij!4f䋝dKm@(wa=Ǔ&Q\'9݂-ߏGlM,]_y?dCW6#/nP<W6쭲)9.9>sl:颅\`gvՇ9>Y0k|`6U%b[ya>,q+Ci1by3?T..>)xquzIo`5qY"c0c5-]lj1{bٚccGN=wFz^`#59HmjTDN#~<&}n. Eg: %{½(a#/8knXŖS4AT(qݞ2^M}Pm͵LtU#3mk$䂄ƿǡvȺ_=77n A FQWbr%)pr17 X۟~:S~:kQ˴88̄q0k13%u0#Pè!(##nj럁f](h0rt^DYU{U jz e~0^+cxXuFOUYbpw *wIsϠZ.7A&ATi$tny[_qsKk*9&`e>U\7&GI8~_MY+YcQTkC+g.U՘.45םgS{w3߶ nԷU@\l#5Z,,ndqrK[߈j[%&LPI~ba-#׶,Ps0e^8;='=a ӣM9YlV= ;J͸jV}g~f;P>w맶ZGVOA@%[?GxQD>FL+ЙDBq8;:#/y>1}~G6SA`''jYA"aKQ=eks=Z>'2ٟY*v}7W=;cU%b.qJX\@"j.6IN$6$RgRDuOA<ޤ?GbALx*UVa|OR<G7kn#/$5FSq#P-"#/ )"bNc{\`pRK$a-":\Nk"G:,dLXTJ#/p/hQh:H+P#/ #5ݙyտȓD@ޒx͉.tJQBa2Ddݠx#5V}xT1+\kgDƋFhU<C{JXqտ: (8<&'[_}fD8RH?]; F#y4IO}r$Ւ9J>pm9P{T)LHqDBD"S.ŹSHB3B= 8?#/IJ7ysRT4S4RƳrlm4UQ֫lm ϟgzag?q88?qGzNs,Xiu9r4 w`],͈C-~F*^=C$%Rff{6nkKIk}/eʆ(&?)#,Z`TS3|sVL[Wwt$N ҵ~aA|!K4i#,,*!5@Dهgp]8ԂXd#eJӥaE"E|4:#/_Qqi,BT<@#,6?i`>|v#aGdC1 >J 46}"*֦rNP Kb]; #,GD6d6Qbf(h=4xė9F)R 02!IY"{Owy_#,Ț># Fm~n5UB"{i{Ty7, ?_9Xf&f4eSu0b. Xr!_7G=/#,tal `6#$3<mFX =.Y%z#5nU[u<hqkrI F5+||ǷHIT|U3z5#zZ&`\SXD"x NģuEiVeCMfD!cGX@8l|߯,2"H~xِ,"s0F3ͮX>r6WUtvμwCŢ$G$I#`~ xa>Dyq#,u(KC!%px_GK7?h(w|4fȂdBދX}AɃ8)ID8nHtHU]O<'Wp))BCx6,M^#yk#OF#/Sf%;S-QSנg#,qOHPF!d㲀~0#//\n566ןf@x$X^X)gH#,QTCP>C?3OZ}@pH9#+9 ~~pm> ^F4w_lr  $({];7paެ{E;#Dc;x$dl6Aç]qwAр}g"D<#,;4| p-9сP$!!|4ȳ֧Kpx('B&Ex'7AjXGt" :dCp#5 )t_dc+ }eV=(~t3yxh *(k-A[06bz$y\U=@f{8-@qͲ4dGH^}3DIVWǻ?> ۦrܫk|[nz$F$A‰`(20*u4Qf#5B߅44ʽ~TN;:)1Jf &aQ$&$0buq#&nO5NǨǠm}i-#,ďOg{C1 xX#/_Ϭ<*B2 v;DjvKg''ÝX0>+ۿAS#,6nrt`9+R>SAv%V^}ML7 i3ҷ?)*绶'mYC^cCh:iH#/3D"G[#/B31tF®(wfCOj6ryf-`"÷خ[#,9R|;,p\q6{˼ *{cx۰}F`0")*F >#,d1EɊne)}a2}̄9hhX\Wl1CWlhYy홤wNA$D"DQ D]~b!.̅Qg}Mw u5@bIrI2qDf̦BLHzѽ`.~P+gP}BQp="#5 P~ X2b`hXPR@D0!#,bE&Idzj4jZ`RoaC5A#U+)'Qҡ@ m&S3oy[;skZفB#bA#eSmw^xˮ[Q{{>!9諁$:`xZ]kupA>@%|( B#,U4ð)^ 8lBB)#56 %1:hfȁ|⥅o =ve^EyѢA 7'^Sq2m80-lBɣ4{^w[F#,fm= zwwXʕ!^$;g"ظn骔UQ|7+?nC .Ij^THn}`cboZߜ16}ثKL #,$ &Oq0Y J<$ 2d#T@s}h"r#GSBPJfXu2rrٱH&*7 "?P>'п#;OZp!b÷㛁$! #Ux37^7Sɰ%by bȱ;PFKdZ*v$exzOgG#5M .JGD|̢RN%ي+.ĩ%#/ `i1;&(rE2# q8D\k\qxe~`9hTȉ\#y0nxhoHAL#,T7#/ l-#,@c?R(C.L-zt fǨu׾M" 겦#5.DՏ X9r:P׈{;JEX`&@Yo񳛹4^m@1K I2mN/2Eh0(^I},#/P $4) zj&D);&$M/9ѣD&$Zf12:6A{Wn88JnG`Tv)$+W <L!{+>U#WtŊ2-#5KW iLY_ٲ#l #ݟŵqnsTD?aȞԭ[i#/ڡ+Хff,x s&ЂAxno++'corX9oڗO>_õD~G!W*z&*|O[|vKyyZo.q26 j A ,L~AF hinݴKbDɌJ1V4J#4(P+mu6IX^F *4S g#/܄+xpKf=8 $Fh B(f~}BcY$&1t4AebʍZpqy :0G o~HwtGR*ts k*VLv ;/7Ly{ȃHDS.W?=;~c~/V@;Z߃lFQkgxګy/3bl~Ve!/f= rj-"N Wڐ2t3%'3@@D' Et@?c.ӿuQ챗#/7dr*!9Nc͏qF;O7BbqaH̀>>tɧK71n젤WP0-PBB$R$R#5"Fd(;wI2dxDrdgq!w 3g׶[)6hJYDlDt#>j)P =n7*ہA'PI%l$d 4h@+!vksZ~S!MONZ3h>"#,R]"AHZig#/y!-p=3UE#qVyO;#,&U^#,V#/ A/Ei$ж4H,-+[ VFl/F%D @jT#_US$hd8eb퍯\%:LPKw~=oRJ]fY0nEl{8lZ:{V*_8T50O~>_.ќx`KtymG"k#/&j-ҥYa]XR*,$3QGeR;tḍ]>mtNxPI_:wMg9y9+7=i<ь1!υ#51E㓶NZwmAߝar;<邺2 !(O~gƓX;0!sG;)`ۧw\#/s%yOnpȇmdE,0;% c-{U3G꫃~s|m΋fO5qow\;>Ð1 kc}_XL:nWWwgL:Vϛk{ީq:n={r7 \;=T ̈P1󰡼؈Q#,("0cՒ^7R@̄J PwASkw^ElUN٠BxFsd4(V*r.g]zBeY.Oqzydh'̬>Si(ab`&@@ rQyW:Ф5ܒe2W&.֊cfJ3,x~? <4#YXaET0PJA 1QL{8#,p}郛!^^~c^t'E{(,$!amaTbj;[!tvK.}ڿ[w}ƭΓjCY]4߼]MC7m;Sxl³d Fn`((YM" ]VӌA&LJ/ޕu[5Re,oo-]cU!%%TN!!Qn" z`m̦6~I| 0‰! +(yr.zy KKcDw}Q&;,CbٺE \(B,B&ðvȆIg1G&6)QI'72YĎ{o믉|]T-4d_. 01˳vyhzt,@zl qLB3v۠ztjZEYߕ/5Nzᎍ.ԣ)mk{Ui;QiL<4h6qcWPr̜"#Ӑm!wh^Ǝ\a[YNZ)lx4S ^ds>!4b]n&CF2g47(e׼4,J'j=f)c M8M|9ewCF $dIk6l~zɫR{!6R`jӼ'D!22$.3HQ^5#yq3ө)kyz,|cFwu  ;#/|4 >8Hkݡ9\x:GՔj+ܗ7G"&(pzc&ǡ q9#s os2,m#/haX}ԃ9oVqwx4z/s9L6mt^J$9^s b;xb\eRwX-H/Sd @3çif׃>Ooqc-,#/M,~QÉ 6ԐiN<Ҧ2 1ٴ׽d$ nyJD{Z$` L}\]/b`ȉ(ܤ&QD^"@g0Ghj{0e! \(Bg@= IMmu±)HHv@xd>dj;ɣ`DCɡK-!fBAG1iLi:6/b-j~`33)JcXCt3(Hb0#,] ;x8!=f#5E!SNٍ9}^1#5 !`v#/s 3n%%={#:1@9s( SCgU@,lKd\SU3X712Vpne.S`a2=͂!{dk2Fz9.ХaO%PȷL#5d@gHzNcpd#:7p)%l9{^Ž23^2l#/Fq[*.IeRPY.s>Şcm]ꮪ]yv$:W.DES1;|8)5sZNcʹ>۠Cndm#5U$53:glf-r%a=MXȵF%F#, q.&gE;-(j;2a!ljofrBgH"-3$(z}&[iu&yŋ]D >͌:?A!=0|xJ""VE ږ,KINw]#/f^z(>%.X`**,F"Y*( BQ^(h\!:.m6#9 ãǾ0?Ū? I5}* }zvgk)4ڷճdz=JHl=-)#,iϠ&jRQd) M"HAHNQlƀ9J\iFFMlZ)r3O:9q۹x .^<,~.T4=('k{r5{Ue\saR| " % A& s=$O}4#,TfD@7) @;S(hBDwsҥIäyk1EAPpV Eʀ?A&F71FkpLhc M`|{LȪM~eh:BH #,=fG@ 2cSuQhbXXP32[ jFO#,kV[tGɀ+@`@ $E.(A0 $'!:aDD$E"DW.)$[u[O.';tUթIlNXk#5iB LBɑ6SnW,jw^K{"oK)%4[ݼz\175K˻5k.Ά74b>tk&OR*x hD޿32FRbkͫm`\ q3K`Z6YEEb?)'Ov+EcBu‰QCț0JEj+jNIA,У3Z$`8r$RU"qQv Dd"F,Ft#۳UHA1&OӃ_J 'e#T0ɮ\;ҒhEd$rHJntBt)%A~,D Ǯiu.0Z]]mr852/6݃ȓ8A4Ra#5\K!1#/#/8 U^!%W/N/us@T{0徨(oɻnj좍AѳL˗e!ə}g(!EBRuvqܜNrA5OM&t#,(3}D~@ BT"j̢,'IחQm 94RsfnKc@wxum>ga,eTJ$hdTwi #/mFEJ$*Z̅*-Նʊ͚*؂N ' mʓ8l|a2 Ȩ(V#,@[YLopBT`3#/ U$̌i(-;#,% X8e:b=sz#/|U_ GFLM67L(sCIFEh 6ZEWZ92銯ڶD_n@V@۴Sc :hg=9ҭAm*]%]%#,ܿ;sD*;6Oۼ<6ޯd͚̪*JekrY+JfœEƵt-q+*+8(n#, @A]}z>v[it .7|^ z*,Ă$N"}v>:ٻW6ځ9!u.$@ 2[p,)Й@e*?K#5)Q`+T:n;N9Xh)Q\X/$&IeTi'T ?:x1#/o / (gT{hbn#/bDgdAcDHӹ2l`zݞaW\0ڂDvvudC!q Y:@iŢ6AU#/o{d䀶x$ U 6$cLǬzsE#5PǛ36pBAtʼnaDp@oO " $(⒢T;5<Ҟ1gN\ހ܄#ŏ~RBֈ)eb]VAHz$ #5DHx2#,B1X#,d==vߟsMjX3 vN#5QHFO~ms`j#/6#}@d#,  `|dSdo(ߞ$P8"[PO|RMamC. sgBL̒eZmr#bҤGHJ8,Gt>4g#xVcmֿ[DDL.z `tx~{Ss{.7;g֜<ԲZF[Q0I~1^zT<-`OdΙX۲X-Sdc(: MrwGuVlZn uc:K=VuAڨFjJ5C!+B1O;#5<>?f×˻щ\^6&3Xi[}^i8 66 `ڎ̄G;8{-75 hZ]zэ修Z[[',͵&ff@ J0bc詪&h 66mM0xC"o)b/rޙ/f_#,`#3Fk k FfiɓX,K/@lgp½v79isj.uCqѤFa" L(8A(2p5S(SMUJ,b1d;v%D%&~ԄȢ)q=DZ CDAF`I=h}j#/PwL!!sDNj1;-9#,rFT͜ CG糐xM}`z| jFB*)ZP|*o/FGHqB:lN0$=HDg3vkRwL qd[ 2!}c+v@VMAF6(,O#/ũ)dR%EԳDuKk^/xLj9bԲ! M;K SIyy %l+[ھhjFRȫQ5S b4Z9^PmU{.\^oߛt2D.E#U #E0AuQ- YJCGB!#/>#/'Qtp#,#,r#ɍC C7;i=h%[,9pQ9lGJejĐ3&޵@i߱M +e~+I%KL*dž|v[H'$'ӁzSbI"jn]h;ݭo#CT7!Q4#,#/$&HYJnBGA8Xn%(.db$P(a }h62`4LT5Bކ٣I <}O*-2<[Yi"mmĄV#,^a_ #/\vHPK#,I:RD#C.#X7`q/Ȣj֙" wbwXD#v;ᶖCdXS 449DGC4zwo5mx30ᤆZt6]Od34Q/|r2oD,Hu#/}g#cZ1C%ґ5)$6߀n!4ZʻI e@̝\\6NF¶n$)pjJ~ճr6~~3С׎hPsÅ jʌ4G9(!EUC377nA /N6A Q]\| B)L\aa{ylj -" ꄄTe,9[3CH&%V YAU*#,EV$:ĒA̻F$*$,#2\c 3֓=<ͨl3PA50t,-(jىI0̋#,o)MD2 lF#55`RWG'V/yeoˋ$;T2.; P O,HȅEDk-6PPrQ'HŴ^S܂}̶IMKue%#/OwQ9o̒$J@9:qi#,,F($/d~F6#poK%EKF T+ntN"s-qE(Y? B+!)Dl>W}&ϙk2/;I3z߅jwHeSRp0{&q"TI[\Q bD BNKQPa0NmjpvTs#n#/~s_{`sp#,*/Z2)Mzw0doO*TW48:%(Xr5gY3Bnc1o|p#/7[sQ!A9.%Zo-wJ,i#5#,U@W@t`?)@M5B2K '2/ZK@K:E ȍ #54 ITpǝh8ٜO&WM8Z\a,a#/ROKl=a<VN-ſI+p5AI$ Fh>k;5y2z/m5Qvbʢ'G0 c#, ϗ;4 A6e0!4ǥ.ƃ&Ǝ%"n3MI#5iI#/ #5 E4c=[&h&AYb  @#5 Z}d @쑿ih`^6wnɧU>0G{`H@ GYn#,5ӫMc=&1ƀwNGG"a樌^oq)8?L`OAM gdw!B*B"svYƉl6-oȹunIKmj6Ԅ`EUE$A #5i#,zȑ|:iC$[X7zЀm TuVzqHD!2@H%2RkHS$̖blZL2d6T!#0)6IJ̑ۦ&Mi4hRYS)#0(#/ID*hR$IIbb QPZ4M(EiKQ( Fe1!QMgz;8,09.ۜX&A~u=Jُf̈x=;p%S*F9.e :y~)W/te#/D!$t2X-q1zMF߅7y9,碓칷[BJG6k˥gd$|>/}!a h\#y,P!p ^F#, @9/rcP,SiZMa4L$$,qpx}miTx0QʙL1?bχ F=D؄}DM ! Zť=c;Ե}"DcqC‘[oZ}>rd/F t* J^faH1ՋZ 1d~杁{?Wݭ۵-ѵcH%#V i8$i m M(#%j7N.""3]{DưAarPOezAiB|J{L fev^`G$YK#5<@؏#@e?o-t;͔@kb<!C $ba#/}CogZIvƤP`M2!F Ӆ-̭;Z#Xe$|2}?VlfIԆ)ZQWo a31Mf.2^By51-,.KWZ1fޞ1`9۩LKКB$mJ 6tAIΉ>wwws!YL\.i T0R0JtɞOK7?z~#˭;"F`dά#5C0jv cTX~ܷ1Rogr3% LB 2>t#/<;;, 7xdC@p5Uƈڜy(a\rmzW2ZBA Wo0q52vس/uyh ;v[$33u,fwcbRd,vrh L_;l\;Nq#,X(c(He5-1”qDW;4Xa&(~]OuێLsx)ns/x.>Y㦫kL:Cؑ26I7= c-lvۅ%ޓ*oNdΦD@ߙ9;6.n%G=a읿n ˇbs'\R]j)XqMC#弗q.gx70ln6i¸q-n#ll#/j&(]&M7K}KJRrAa`"m86vcpLc+э\Zsl\#/eS!(BfkKZエk**)jݔJfGVXnC#B(Bۂ i#5'Ks \ ϕ1GQmPrIYM1C8CG2>TP.e[FPaN+img`r5U/V@Cd&^,I53S Qv9 Ta%3 8][PJc$d"mKJPTÔ3 31zCmΕ`LowB\G,A(Y+;tcI7rX<5iaNF:tt4 6XKe:G #5&$Y< kCEe9ziDSQHn!s(aMd&U2LTB@O6fRTQv(bȰ2P40dzݘphU-`J8T-!jYlY)7P10`aVe6IO$a`J^AX6ҌM +dLRٲ[,Ա2evgrxVE0`QhcgYfΔ:]֩ZRz LiM+3w- /с!+)e#, 8:Ha <>V-gnK`lErץ}Ď`G+PP}R9AsI`TiBf9h'Vm#,@ ΆEܝ3#/L3dJ׼сLMmH) #/#H. UV8(|WA]GDXj(L :#,߱ou)&Z7-m^%-OnL6CCۅo4Xn74"/|#-J:?ike$#3 93 }hELȆ ǠpqD` #/AĶ@ǝh۲MQf&GcrV HurU1.I8ɽ0(v)7Fk6&¥$,3HB*و@c8pvxI,afл/Y%z`XFuCTk@ ,׫z6/#5&i`pW5M4f3cm,Oq5#;pd0r\A᭍ʡJ,&0 "B @BEM )h RJU#5#/Wx;wV춌M!>cNTzqB#A!!ApV^;U KEj431[>q9"_ADpX[d.Ћih#5tݺR͎Aid-+X#dk`(o [FFK #5Mݧ`b̸;bmMv*|.㩴8KdB$1СU{n:kemi-m&@U P8C#/yPJ,Ъ1:z E[g?P;q: E]vM+okl%ɦcI]wfsuQmy#mg}Ƨpg,rHȳ"Q#/F2>Uq B0#/7>Gއa$RQ+F%E-35PhE#/ I#,U&aY_hS߷qLC]N*~Mž˦|}95v Kى#M h$FYDҠro!{M@H q:-'e #fu/V*(~RH)#,RZ ;#,v6hSXd&AUh*1"F#tJ4&i#,\% īmXԸD1!pHO3pL\e&EK1dfwGbv#/]! Bj l].t$ q.[YD*"Ix,؊(FXB`>d0A |A$& ?mFƆ~#o] 35o]Ȋæ!Ͻ k@nmpF_ol==1ɶW3yQKc+Ch laoZ4cXEz:ҙǥ VP[M3.)dCa074#53#3fi&;dѱ!6=.٢@.I9`5#/#`keL;CڠҖS3:g"K` A=:j4Y%v~g3j qv6K:AF%X)r)\d #,T0L1  C#/ 9Z$#5YE03#/nh/[棈 ? RD^NFe ejpH] 'fn ?Q?TpΠH5$L]YG=#,Z@P)^ d#,NqNU4QUUU#54FH[hbդU~+Ȓ8}ϸdhs#5}#5PO`8:ubrakwG#/dA, q2enjMtuiub;EF1p1\PitR 1;K4yYZ:ݭt &)U pQPYbȅ13ˏ5B"Q(D еfK-g)\:;X3BQ2qo#"찘J|!ֵ̕+;vՙ{o(Q4,mM/tQ6e)gL}`dCR MN2Jc`Vwvf˗\ ,,C "( Ő9xokeAE #,PJ` F2<݈ Jte.%qZȹ4R&nĉ@KB҅8Ѕ٦#5:zzٟ55]ݒm[}F.KawsK\fηs]Nh6Wi+rݤů;5;Ow^o5jB-TjCh*i#/#/ii"V#,n^62I I6V6ԢX1b"C0 fl#/+[IQY$AG?I{%"Й1}`NM.Ģ-"THlob ;;L'^aKZYL?yb<@#/NtFLJmz׫=TKy#,UEz,qP H|{LK8~L/B`BjԂ~#5lBfhZ4XR#/BDd7Qfv[M)a3ߥ}9-X6kP%,jT5\n 3v'ݬJs\mje2+@̐3p zϱk#l r*+xg"DevJ9g 끑t/Ѕ(7$txkӡ{ƤkTcEnLRY&mfvc-LԄr(|[[孠޺fҢYiElJK?__&m#|)DgvJJƶrQmo#/ԈY5PH%A@$E 5-%&i0jIk)6ͦV̵SE*Kb#EL-0Pl2%I)B2)(5&T5)YJjE(%i6jɵ4,TIYZPlH$e)3jdK61&V"FԔԦ*֥L4MJUK**|ݭMi)*M6mTi*͖cZlZj5jUPCIF9/HpnNoMYHe{20cB'~へ hY$=!^#/)Du/1_n'=Pk4#5.,=,kg]b٠3mezˣ>2A1`ydE,u}8MCFlt+YE Q(fIQ֚M$DDjXU#/ #,;{dkX',5vK60&Ӓv~#, 1"|uE.y!Jӯ2Cst!\\:q1"v_ B*750"t^+A#/tySyjzMI!4#o{a^E\^iYOW<|:xPӷrNA4btG R]Sc#Ng#>n,DB!L8r$JI֥ [JYkmGmȶ+jY>!S@`FeAB*\B#0}ז׃d>ɷ#1@ð3;x]ܩq@"Z#4ŖW_x ,Bkd+!ՁE:A#/b`5|ܤ(B1UmrͲ%6W]6M5jX,IkQlh?#,a#,AP"P7!Ba1Na3BUk0+"A0f6Ŀ&/``i4` `3c0-*m#,AV4Jg?+U4;㺣,7"I ψ>;vI#,%Ch#/;ǑOYyvǺ.uQ!«6FnCw7iD=wyG"T"q#pvv- @Y^F#Kj(h bXTe[1nXo:2 B?PCq#/e؃=_0[=CmVKbRj,muUKji+Z5FŘTD$A% !&#,3=wpAWVJPe$" %#59#:BaXLhcAُw53a Osarz( 8%cQ:GU,!Hw< 1HR**+vQhc10*5ÅhmXp[,&#/ǟqM+1y~N,HVRhx;;R# 8Jf՚0n(c<1Ыr#,*MTI%F8Y0z$it6uuOb{_zi|N&M~hֈb-"jM$%"m%21֛2hJK?qeR%XQM)i6LKfJƛYM-d։Pj"ZQU-*kU)h5z#/iO?6d"f#cD5$AJ Zj5kZn[2@$dHbuC~H_ro[W(MԿꄉ ˔uV,#/K>av\#,2~:(۰`ܧ.Hw`w0WAhDMzӊ A2=PU*PQ/j[(qXO"< ̕mB$!=L=|⪐h"jD98'ݕ2337}&$ziDk*4 @<{G#/gA'1#,bjIķXjP3v<䱬N Hw0lḆ\\(Hġk<!pI0Z~g!G i|@4/Myl&"Nk[FYD0uű[dBdbe^^07 sC+_} S .6i@11f&K>^&m5F~RE AN䥂(#bKw>d6kG:htt#,=_݌p2ɼnHcT=1,2&,KQCOun4T$Y`UD)-SƓ~˼S.(۠XcQq-' ˆAydfe@v`bqLq m]*HJJdݝ-tvw"\++6-Ky7͕E\P3HoVLƢ"3wkMm*o#/'Eq8A-H0z ] ,ӌQU̜<e͟B'0rwdj*yK5^P @3)c޷ԺWRa0;aDȢ߅o}g%.̡T BIn[FRP#[$Y Ti$kҢf/vAW<rG$bBLCh~Ņ'6A5F#,7Oߝ(F[$&ZJcnB?ja䥅Jk.91-ax}LQ#/fξ@kjGRРٱ]`B@F(iR@HE(f#5w:S tEvoοP1u5鬙kbD(uv9t` xD$6!UdE:`VAA%ZK\Gep^j<}]j~hu]vxPD^QZJW 6B #,Ƅ6% 1(2*TUF (PH}WCȢP;{kT `SU(^W{1\*&GaW0 n(w##57xnmĬc[p(#5a;@#:/2(_R(Ǧ9Z6IN0JlC*Dh) 6L"lsEg6f%YSd WL ƞB]0ȉۭDHٓ/mZhl0#/s`UD7wD01l2{TEO8 pB@u"T#,/-֗77KcI괌#d=^ix=KZ#/'6᭕FsLu8&:h)#,y, *]ZRjZ6eL֨RU["W]ڹ!(`)(Oݸc!#/ܑcVN}M<DחnOd`:g3!#T%T@DgD47:͟v ^}r G1+>irC'*YC(9$ 2idHA-"g7{=ـ/B6.W_E+ Q!yV`dvʗRڝ՞vӽl[zt֯ћSM_uҕĝVxțFFRs$h1j$_b7蒤=? Ǖhr[F*&k㱐xjV#~HReulLеd#/04孭Io#|Nz>P 4ǖ" R7%C2m﹄G#`#5H_8FHTը,\J v,nVi6ilUu[7e4\Jm}Rb`@E9q`@:DoU<Գ5Dz"*45!>MI3lAlcZ<#/rģI-{l>@ kQ LUMFa8֬bE,4#/SչW#/6MJ16߸7pl#/_p6h^on,ES`[`]Զ#5hXUջDA RF~VZ*֏3%Hk)JFl(3TqMP90yEAug#/TC~z,^G"`=T#/*UJo6M]ҫ!~ԡ{aM dpAlMH?.xS>Cŧ1M|,d50n>03hkľEFo5qIE.8<<4fji 4cCU5ٔ.!CF^2nNmEv>a0cc󱱗mi`Q00!OeSiAL]-#%/F 8]T(I8.0ayExѭퟧfpuꛧ<#5Ԥ%q y+ҜcQMBBniIłd8Γ#5<0H:@i}᤿5K`S#/$S}aS.=qP@o "ˆZKd8("|hUlCHMq2J .Vɼm*6mIZ*K][r׍hifِ§v֒Ml#, BAB2l@PKG ;Ȩ)Jkauv`Jl5YpMZҔջ-Zl8P76XjbH2@'%|my3D[kF.>/=e5)ʄ!EV񫩕I?P%X2ض-1YƣTmTͣEE3FVe$RŴ%kfv#5$9 Aﰡ:,֭{{#/&Մky_W|vm#/O:Oͷr]VÁP"#,H@rM*+~&\J[[v"V)O7]&ki)&m&VқVTW[KIQM!6^ A~jIJU G&0A F;JCay8 XEV)!P\7JW#5#/xaN"W]0mhP{5>o>O~#5?^H)AE~֕84}&mf,FE[TlcUcJY 2$hNJnLc,zdǿaCsb-PZR#5(P !gꮤM;:>$Q8du<^wᲩ[5]mY`[hƂQg{42-?⠈#;fu`vHO*Qt?0T!L3KܺnE2-!`yf@<`MmcTm-_}-HGLJ&12g#,KEdχQlՍZ5\յuX1nǝ>pD  <0?a?C_XZQA&_yqV0%h BKF@Xͅ؁Pl4$ڶvվ5IQUMoڼ݉ƮJ(DeR{%!a#ZawiZ{ҥcl‚L#5X*id-Y*  f c Š(T(`4t6L磻\EC2q4b( 1T _IP#/JKpc4a`sqcfL&CsWРqc0Ԗnݚ3@9@zDTQ1#,2vIihqX=nGo7ggHUy78~XC8GG!HC{z:\9pXɟW𶖬1] q{8띩q8 (΀TǍPUA v&ɷ䘎YJ蒤HB e#Ixm 2#5?Uy,)ji-: [G0Kl@(`-YEl8<WH#^y>;u茒i~6a!gtDž+Qˮ645A@>AEU&+t\զ[MJ!VwPDw&V3u6\JհfӱF{ͅwٸI -U#4櫆I!g#55jLRg AέR&HzM!;H@01B"fou֭mikʟnջl&4:d'@k:dG)R0 L0)A(#5l`|SCX @#/YfU4֦i/#,1۳0VQ#,umlʢԙ` *`T#0FJRRj0'P"$P prꃎs[S>5\,AL~>47H΄x²gWLlf-mEwc~#,b BO!{?=^"E#,e08g+`_}CyfHC I4>7z8blfh J 兡 #>,-j#6 `j#/$Ւ1W&h8$B(rA#/ڲ0@Ed&1<#5b#NMX; AMX`]Lw淈-GXjW/*ⶫW5Rk;fN={nj0#h~bH$G("c\<. Hk_*-jFi,%!Q*6TbIE#,5AE@w@ALZ`m6]CAX46Y7OmwMf#/bubF(yC?4Y53AgO6zƖlvA[>pZJ*m5FvC$MUtHF 0cKOxn]!p#/2Z}G%R,N 2MOBPZ Lڽϰtvc#5vbz"'/#/gyA 1>FTWlAv FE"HRD'#,#,C"XS#5`y`~#ި$ BﺌP>du`B#4趿nU 'oBx|WӗJ."5'ᱽ}P9JH( o#M-6h%w̰:wf79 Ӥ*cL@gТ\i`z\^!![$f4XBKLȭ*`&Y4n"Z}_$?/x*ۖ&o#/hHba !d;_/Q}h7"/:x/mۙUT`s6]Y3P{4֚;VBEe0:vr`4#/'8eFjqEb",q#/#+L m-0fQ2grI !.L; HNUkbf CǯFsSطR' Kv+`ǔ7={9 lGf-2>#ݺxZœxnǟSeI|>:lP#5.^)Iz npLMf f揇l5^.Jud!3@8)PhȘz#/QO@'1k:#/s>;YZ*ovfaUAݞ@J8EuBscŚ.oAMı @ipn"RVeA`T#p@~K?|v=0 iL$DXݖY` (4m#5h(>1m#/?9MI#5BHj}_5}J#/~ʭѭnDFFrٚ@7<*Veevvxw$NIY|h"'d_UwH0KU#/+g~Z}/0#/M:14=ه=.<4-Pr<ΜD1mz-jq admC\j0լ-KT\c# ^%#5I#5jwx<C3hl&W-7Ao@4PCrA)FP%DE!] O*܆~ҿ׽m OTۍ@>]`*t[6pbZ`RڡoB7#/]}ٜf hq0X>i Nm(v$El2[Bf,Di4ND$119#,(`P腤䡬GV:XiǓF}#,cm6&0hT"m#/,)^FJ&{Xg&AkcG6L~cA*u6(&B-#/4cl[n1gM#m>Jash(˰֚b…tDTѤpEНUHOID6<yp4t=(YeU$9H|m'#,:hF [s(ؘ;0fȈi-*@4!BQ bJ"4l`^)"4fjܑi%0**Mw-ݛ^M]WbkF Qd`!GQ#5?w#,YCBd1#,.&OϺP6$-ِ#,.v~O2E@_3#5#/((B2 nH B"ȢDFh, B'A9^H;y{m# BR!KFyhˆ7wRNևuדxŜcn*sjG%⋱tRSRHC)gvRKʉ#/0-2coPDS{n)ZƸK޸k!`6w/ȟG]!I.6Aȿx_N!q; HHDF*;2 yg!"^gUݾt48Y/#/H"L(pDE2%t`Y{OGwR*mTXt\T*#,;5k79O.|`Jٕ[Yì"hFb"TY@hZўS~'c5nZUI'(i(0%$٫o}6rQBT@C!U5AtKQV[OW4m淍_nkžWbW[k5shx;Zɵ!nk`[%B KV#/p^ud5\W’,b#HaN@{4T8"|@\Gؙb4c@I#,GĊ #,z@?ˀEKomW$4P!S)J#?q @>%X)ԉn"ȁUEl﵍%٥mw#5tX.BT%pZj-j&ںY$#5jK6V#5vؑDNDQ$VHAT} #,#,tP*'UOFAGB&q #5!zMX=`YH'J^`d:>BbEcdR@l14щ-3DJI2VŶVJU*ũcXf^hik2@ 91ȑ>)pM#/1<1HEd%T5Ah GGX"W#5)`#/eldE1T:UF44D"22*T #,fiL"'k&%Y;7 O®׽^PD AF B c#5Fևh.*W N0^6shhyKk#/EU)^}#,1 6۱!77q)2um:;9tM`,#5Y!VF@A1ީ-$<8kbHe.#,m“$Fb{]5LrەFU\\ֻ+kA.qY DbVU6dX|?fm8A凔}crB'Ä͸=PnO12R_'v۸ϯo. eu={pliܓ;:7OKt k/)΅-ۏ[/}E/,L'rB9]tMߊkGWtYA3[5lJڇEyF]!%#,WN-b2 ttUuĶCLK7ZXn?v#,:ÙN-Q67ʲ*pD5]NuDTSLEHEmy,[xvvb)WbOdbL(D2Swȴ;KOy7cқcP4с*mWϒq[kwy@co{T<#1܇Q'wb@뾽ʎu~HR*WOv5>#F#5wT 'RN rld5T=< Ax@2#5Շ&Gc'!c#/Xej#/*ԕ 9A%A\=Z?|4G(="LȻC=G/Kߞ<>7p%RhCِLs;IH)Y.$gF\m>Ӝ>OzһP{M9jj?/#/ۿk7UwFe1w16 X'#, JķukL9w*Gn|Pz il0;8~kFds$.jS$2*!Sږ3o`b#4@R 8D悚yvvb;8=qIzӾ.ThnAҦq<3G:M^OKp| 9#/5U #5h*N5Qu}X5EGmae"5#/PA8N) FToA\p Sh%V*RXs)fVjrʌ̕|Lvl*C_mPX =;ֈ缹e!M,-7R3u3ey@((̈gPy {ʤjPׂ%:f,ҭ&CcѮd҆#/&B\H֖N,a$߾Mn!: ، ;$7j0~jxdjl#/~T~?"%>=Lo25!*}D*sfҳ1fsWD<=^HQU:@ZXCVFѱDw*I0 65'm_okV-C0x5EƔtZJF08 D@fޭQAksmͨviGyYUSq"T'+AB`daLbBQVd{&0dUCw6=>rFaFvO9"}(Z]`@U#Dj#2Yj2ZL3^vEޓ')c !b3+nxK#c7l܁AAlȱ65љIs{)FmbmjZ#/Hj0$_u*qԊw#5Fu(3ƈ)a)pM,(PcfHSK21w̢xpk3 iY.lo! RƱ@+\ФIR7vmoĨ#/)l41lBM[:J6jje*XaLV1.ۗKG]Ȍ3Ek:@awm hqi2iq<˵aQٔ~nUJ5bࣄBc9GpF2 QU#cDF2#/BRhl;1F%c4-]Fipy+`zWdpTa#jP˲ԣr۰ꠃ? GҦfn*jR-yZzpmW\8@se-:1s[u,-J`qu6i.aZ JF$go$i-2FHFK3*ӌ!^5EIPQo#/:p0G 0%2Ȑ۾lDqJs;*ǎ}+?_X~;h4n>ξwz_3+#,B6iJ>X"B*Q)#5UHk2I=(e!eSgy[i T2۳ $UUFJ2*{џ/PE+ICcj=¾C^A"qm!JP@AJ&a28b̈́[Մzl uqFN!ݽx5i]tjaQp%-T3=gu2^? ;QppjUZ#/5G4AoD.F#5v~YzOgM#55n 5/ )"kI}ծ[1)4H)-A_r]jB#5PFR>eπ`'qx!B*Oۘ\3W@d}^j@j" QjxoC]+L"kVRQE4\#/4weٓ$X#,4w$l&1HmM$P* Xj%%ݛ&czzmF:*KHƑ#55Q#5E#,8'5 6FK^P{7g_y-Fޭ"bTiYDqbفl.g.jR4/ d$1A`Q<,1:5$m|ڬ 8tDn`*LCq- "I$PٷdÊgb(HѨ7$ǡi;M#/\W_78ڕ(,/d"B_60 h*sa-n]MA}RЗ0 T%|#, p{ W(4n%Ԣ[xc%~~>OjI:?SQ*):#se#8ba~X2aWJӭzTZ64.#51B}%s]k^v-ai=v䜭r4i>4"< f׸"5,:㻁;˶Lg&؛>f;}J`8$DW5Oا#/˝E`V@)2g 611}Xwh7!#QT7vuI3>_b7Olbwr>^BIDI(%B(jt큃 }'‘n,,).D~G~ǭn~MLFB P,+rc`6'^]hl0UqQ$*}_m[x`fo\e#/1IqokB%W*Y5X6Gg|]821w:*DyG7pl 9R%T:+\~q5gFԲ40bj"Bl3 2&#,LfڟZV) l!4vW"ihbE`BFȄTIx6;8Gxk9ɢK7Pe=#,˳RƘȭhRĹ6sLvA8M'&y DZ(`nQ?i``P̃"7VhͤB[BңS4,UMFY-ms=rkܱQ dg[YE`ٙNcQW gC #SAc՛f&3F|N.c(Fu0\;95`.iа;eC&:V3+:)t[C;Β[.59'LÙSs\Iu`B2 #/H%@ڙju >r+RfCe2g=mtyBCi fE2#,7 @杯̇jXH8΍.Q0tWF;btcir8M(e#?h0._҃ ̖57Ha< $t3 Slti#,sUP& mN5:[V-Pd+к0psW).(Ki 5fP`dYc0;1 \Zg;0SC#,ʩ&;X){(zphKbK&2_6S9`ʌwLx51X0͓~G36 x4~F(ky%\}GGѼ_kp9kIXOb\8WL3f G xıuړ #5tښbo["l]D!*~ ƹȴ7NP٨]4[ u,w1`GBmXcyӌ0fE4QHPz!hEAFH"PMO6;S]-2m)\g`unsEv#D" 3Q4"6#/K2DbHbj0Iz#/UO/wHRYu,DJb(Ů#,1a"/eHڂhC3biRCiQ p abb_B֊*EsI`dl!LZ6؈X&݄z}'яuF TTEAGnP]f^=`*;EHH) 5#,E~[ײUAs}* &ɸU$_.h"%Q v@PO.M31<+"ƒbP28dTLc`4kxKbT[A! d A5%TABJJ :#, >l|si(A:nKwLCdd&`R, N{ӳ|B|&>~Fp)~嚈Ά3FPp RKs1VDD 9Y~И]caS&J%u'CAMM iLx)A!H"Cɩ#,0BX ؂lHa>6LNpN=AU7#5h|>#,t"qvl"HjTv#5d:cDA\r,#/r}MB~e0a##K5 N"i1XY:Ed<饤CM"!,Fهa4š}C;PC-`k9NP+Z-sN ; ÷gl۔I6j*,YphYh[QU#,E% 6آj21pA-a]Z06pM NNJJRS,s#,&àN|P#,:v`R)eZҠA! .d|yl C7Y0ӮepLC8P$ E`AAU=|M#Ɖa-@!R.B6#,Rj#5~'$ceY>QŜ+4Stx_Rk ߅?ɨ|>>o*̌>k,"{M 3Ah'ĖoN憨m4kʫ@(#,$HE*oh侒 ZDbl *|d,)_vX.bv-$HL\VaI)"th5x^5^o+|MET"6uۨ &KQD Q底66@J *p$lO_wy$@! 9@eDM"!5H#,ok",H1FItI܂HKJ$*ehYfKfm_c}W)TCQ4e,ҐIOEP|f7@,)I#,Oc_T6bldMڊ*eF{j@B {(#,D"D-A5uwI⯐A5Cxt{Ġ"WDLp1 $NJ3)S T½`Ϟf)B%A{iq2 F1MCXSyL@A|Am6$ɭR)6t )sV7T"J=Z`JH(HeXOnKݨZ} @xRW69>Zؖ0l(Cmw_~C+CaXe-#"T44x d`;:`&lkrܒ}}*+|˱\ڍZ[[ƣErG6#Qd8$-#,,&OQϢIu#r"ĺ33AZMM ( phX}tf]JC`aP%LipCq5u]a%AB0Xskn^o*IbݚUҦԡ{V+*TQvך+A#,Ac)H&5h`[R4fZnNERDBNεm!XK=ۗૂYgUf48/1aU3qyjI벩"/PXL*&J8z0lcM=#/fB("y%!^K BA"5#/:8$Q; 6_c MZ׎_k|iinۦJ -~?Prgr8_`L9`~٤qʿDB6 ʇryc;ݦ"jIb8h#ߎ'\:-i#,e#CaU݁hz:9(dj2*&5趙m߶F [v3äB4Db'RmI*B#BC_`N"Rº#,m2ha/b"fg;Q۬;h-O5zwhi∤A c+qz)\ X7 ut %)`a_χ~O787v]b"jy65M\?9J<BU!pE!#ZWnwi|y$1y2IRCl`ݐG(Ay4!z#,63]ꥂ?ߞi\pxqF_TK,5()A@|n&8t00QaS,7tQy>*m4ؚ=({.eoD'4TCVzID%%-Wnmk^}IQTZDh#/1(мfӬ"5a+O(~^ K?ď Ki @r19Nac#56K*%*I}0$&Pk##=#5'e".hpH!!7E qFO$v' zj*bECkf˛%jFjro%Wؽh53_Zȣh#,wւw!=~({f80m֟x;.x#/gȤ?EBZv#/l'{pWi.#/ZXw솯S#l1zev񉒩~yAS>ӶVJszKpYk#DZ! ^G,$ }YOu68'L*ϫϣo3͏~WՑRs;jb/5Y_V7|{~.p ڋ -#<== -#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2\n\niQIcBAABCgAGBQJWpU3oAAoJEGelZe39+Q5kv04P/iyvALGAg2Z8oICEDjFkEXWW\nh2CMGLItAhqb3xNeV8WUMMpY4MbRRpN6cU/SPmt+as4oVn2pozca93eWD7yOxukK\n10seOyLTBamS0Wf+BNr6jYXZRQ2N7inc2p6AD75pMOFSg2HeIeQJ0aUIAxNeeojZ\nmUiLYMdtcrF1Kh7KWZAkYSbIAEjJeobLqk2oY7UyqKcODc4RtZJV1InnO4DItEWD\nnd3F5kkVMw4pwYAXaikmCXYBKHXdF5w82KxqEjrAWSoULipX0BVCsSbQ2L0UOs5h\nKXUS4M7AaYKyCcO17E7CnVXaW+vOVyGEECxtSaExWgK5MvYHIGE1OFvb12PkUvUY\nc7CiBxk6X5eZkPyxgxDj20r8zNQVGZ8jDI8Wg08yTAl8+09qCtkE8gGMdNeHYwX8\n2xDH+A3+19022ZZdyO2t5+2AzU6Kkl1qTPKaXJWFRtr8ApD45Y4D3/GAsTNqdOMi\nWeh1XvqQdHjm9rEoJX8aBXShzCMCNhmZalbUhrdzQY6/hnl0PqnlPtyvtkjCvWoF\nXLF6q8YV/ZtqCc36vePZ6lpUQB6FG3g6fhMGraT2VOmT3TROcG17pqIz5y9+85xy\nVSaDc82uHlyzIsZ7vuhV6d9x4yXnFkjMAogCJv6mitFbQsd+LtXYkU+2Zq6wOoEp\ndLLfK0Km4Vs9FYAUbuUi\n=7D7V\n-----END PGP SIGNATURE-----\n diff --git a/platform/robert/boot/waftools/binary_header.py b/platform/robert/boot/waftools/binary_header.py deleted file mode 100644 index 09790729fe..0000000000 --- a/platform/robert/boot/waftools/binary_header.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import binascii - -import sparse_length_encoding - -from waflib import Task, TaskGen, Utils, Node, Errors - -class binary_header(Task.Task): - """ - Create a header file containing an array with contents from a binary file. - """ - - def run(self): - if getattr(self.generator, 'hex', False): - # Input file is hexadecimal ASCII characters with whitespace - text = self.inputs[0].read( - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - # Strip all whitespace so that binascii is happy - text = ''.join(text.split()) - code = binascii.unhexlify(text) - else: - code = self.inputs[0].read('rb') - - array_name = getattr(self.generator, 'array_name', None) - if not array_name: - array_name = re.sub(r'[^A-Za-z0-9]', '_', self.inputs[0].name) - - if getattr(self.generator, 'compressed', False): - encoded_code = ''.join(sparse_length_encoding.encode(code)) - # verify that it was encoded correctly - if ''.join(sparse_length_encoding.decode(encoded_code)) != code: - raise Errors.WafError('encoding error') - code = encoded_code - - output = ['#pragma once', '#include '] - output += ['static const uint8_t %s[] = {' % array_name] - line = [] - for n, b in enumerate(code): - line += ['0x%.2x,' % ord(b)] - if n % 16 == 15: - output += [''.join(line)] - line = [] - if line: - output += [''.join(line)] - output += ['};', ''] - - self.outputs[0].write( - '\n'.join(output), - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - self.generator.bld.raw_deps[self.uid()] = self.dep_vars = 'array_name' - - if getattr(self.generator, 'chmod', None): - os.chmod(self.outputs[0].abspath(), self.generator.chmod) - - -@TaskGen.feature('binary_header') -@TaskGen.before_method('process_source', 'process_rule') -def process_binary_header(self): - """ - Define a transformation that substitutes the contents of *source* files to - *target* files:: - - def build(bld): - bld( - features='binary_header', - source='foo.bin', - target='foo.auto.h', - array_name='s_some_array', - compressed=True - ) - bld( - features='binary_header', - source='bar.hex', - target='bar.auto.h', - hex=True - ) - - If the *hex* parameter is True, the *source* files are read in an ASCII - hexadecimal format, where each byte is represented by a pair of hexadecimal - digits with optional whitespace. If *hex* is False or not specified, the - file is treated as a raw binary file. - - If the *compressed* parameter is True, the *source* files are compressed with - sparse length encoding (see waftools/sparse_length_encoding.py). - - The name of the array variable defaults to the source file name with all - characters that are invaid C identifiers replaced with underscores. The name - can be explicitly specified by setting the *array_name* parameter. - - This method overrides the processing by - :py:meth:`waflib.TaskGen.process_source`. - """ - - src = Utils.to_list(getattr(self, 'source', [])) - if isinstance(src, Node.Node): - src = [src] - tgt = Utils.to_list(getattr(self, 'target', [])) - if isinstance(tgt, Node.Node): - tgt = [tgt] - if len(src) != len(tgt): - raise Errors.WafError('invalid number of source/target for %r' % self) - - for x, y in zip(src, tgt): - if not x or not y: - raise Errors.WafError('null source or target for %r' % self) - a, b = None, None - - if isinstance(x, str) and isinstance(y, str) and x == y: - a = self.path.find_node(x) - b = self.path.get_bld().make_node(y) - if not os.path.isfile(b.abspath()): - b.sig = None - b.parent.mkdir() - else: - if isinstance(x, str): - a = self.path.find_resource(x) - elif isinstance(x, Node.Node): - a = x - if isinstance(y, str): - b = self.path.find_or_declare(y) - elif isinstance(y, Node.Node): - b = y - - if not a: - raise Errors.WafError('could not find %r for %r' % (x, self)) - - has_constraints = False - tsk = self.create_task('binary_header', a, b) - for k in ('after', 'before', 'ext_in', 'ext_out'): - val = getattr(self, k, None) - if val: - has_constraints = True - setattr(tsk, k, val) - - tsk.before = [k for k in ('c', 'cxx') if k in Task.classes] - - self.source = [] - diff --git a/platform/robert/boot/waftools/gitinfo.py b/platform/robert/boot/waftools/gitinfo.py deleted file mode 100644 index 8f8937aa17..0000000000 --- a/platform/robert/boot/waftools/gitinfo.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re - -import waflib.Context -import waflib.Logs - - -def get_git_revision(ctx): - try: - tag = ctx.cmd_and_log(['git', 'describe'], quiet=waflib.Context.BOTH).strip() - commit = ctx.cmd_and_log(['git', 'rev-parse', '--short', 'HEAD'], - quiet=waflib.Context.BOTH).strip() - timestamp = ctx.cmd_and_log(['git', 'log', '-1', '--format=%ct', 'HEAD'], - quiet=waflib.Context.BOTH).strip() - except Exception: - waflib.Logs.warn('get_git_version: unable to determine git revision') - tag, commit, timestamp = ("?", "?", "1") - # Validate that git tag follows the required form: - # See https://github.com/pebble/tintin/wiki/Firmware,-PRF-&-Bootloader-Versions - # Note: version_regex.groups() returns sequence ('0', '0', '0', 'suffix'): - version_regex = re.search("^v(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:(?:-)(.+))?$", tag) - if version_regex: - # Get version numbers from version_regex.groups() sequence and replace None values with 0 - # e.g. v2-beta11 => ('2', None, None, 'beta11') => ('2', '0', '0') - version = [x if x else '0' for x in version_regex.groups()[:3]] - else: - waflib.Logs.warn('get_git_revision: Invalid git tag! ' - 'Must follow this form: `v0[.0[.0]][-suffix]`') - version = ['0', '0', '0', 'unknown'] - return {'TAG': tag, - 'COMMIT': commit, - 'TIMESTAMP': timestamp, - 'MAJOR_VERSION': version[0], - 'MINOR_VERSION': version[1], - 'PATCH_VERSION': version[2]} diff --git a/platform/robert/boot/waftools/ldscript.py b/platform/robert/boot/waftools/ldscript.py deleted file mode 100644 index 9fecd7f336..0000000000 --- a/platform/robert/boot/waftools/ldscript.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Utils, Errors -from waflib.TaskGen import after, feature - - -@after('apply_link') -@feature('cprogram', 'cshlib') -def process_ldscript(self): - if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': - return - - node = self.path.find_resource(self.ldscript) - if not node: - raise Errors.WafError('could not find %r' % self.ldscript) - self.link_task.env.append_value('LINKFLAGS', '-T%s' % node.abspath()) - self.link_task.dep_nodes.append(node) diff --git a/platform/robert/boot/waftools/objcopy.py b/platform/robert/boot/waftools/objcopy.py deleted file mode 100644 index 4385f2aa88..0000000000 --- a/platform/robert/boot/waftools/objcopy.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Grygoriy Fuchedzhy 2010 - -""" -Support for converting linked targets to ihex, srec or binary files using -objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' -feature. The 'objcopy' feature uses the following attributes: - -objcopy_bfdname Target object format name (eg. ihex, srec, binary). -Defaults to ihex. -objcopy_target File name used for objcopy output. This defaults to the -target name with objcopy_bfdname as extension. -objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. -objcopy_flags Additional flags passed to objcopy. -""" - -from waflib.Utils import def_attrs -from waflib import Task -from waflib.TaskGen import feature, after_method - - -class objcopy(Task.Task): - run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' - color = 'CYAN' - - -@feature('objcopy') -@after_method('apply_link') -def objcopy(self): - def_attrs(self, - objcopy_bfdname='ihex', - objcopy_target=None, - objcopy_install_path="${PREFIX}/firmware", - objcopy_flags='') - - link_output = self.link_task.outputs[0] - - if not self.objcopy_target: - self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name - elif isinstance(self.objcopy_target, str): - self.objcopy_target = self.path.find_or_declare(self.objcopy_target) - - task = self.create_task('objcopy', - src=link_output, - tgt=self.objcopy_target) - - task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) - try: - task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) - except AttributeError: - pass - - if self.objcopy_install_path: - self.bld.install_files(self.objcopy_install_path, - task.outputs[0], - env=task.env.derive()) - - -def configure(ctx): - objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) - - -def objcopy_simple(task, mode): - return task.exec_command('arm-none-eabi-objcopy -S -R .stack -R .priv_bss' - ' -R .bss -O %s "%s" "%s"' % - (mode, task.inputs[0].abspath(), task.outputs[0].abspath())) - - -def objcopy_simple_bin(task): - return objcopy_simple(task, 'binary') diff --git a/platform/robert/boot/waftools/sparse_length_encoding.py b/platform/robert/boot/waftools/sparse_length_encoding.py deleted file mode 100755 index 452da5ec6e..0000000000 --- a/platform/robert/boot/waftools/sparse_length_encoding.py +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -""" -Sparse Length Encoding - -A variant of run-length encoding which is tuned specifically to encode binary -data with long runs of zeroes interspersed with random (poorly-compressible) -data. - -The format is fairly simple. The encoded data is a stream of octets (bytes) -beginning with a one-octet header. This header octet is the 'escape byte' that -indicates to the decoder that it and the following octets should be treated -specially. The encoder selects this escape byte to be an octet which occurs -least frequently (or not at all) in the decoded data. - -The following octets of the encoded data are emitted literally until an escape -byte is encountered. The escape byte marks the start of an 'escape sequence' -comprised of the escape byte itself and one or two following bytes. - - - The escape byte followed by 0x00 indicates the end of input. - - The escape byte followed by 0x01 means 'emit a literal escape byte' - - The escape byte followed by a byte "b" between 0x02 and 0x7f inclusive means - 'emit b zeroes'. This two-byte sequence can encode a run of length 2-127. - - The escape byte followed by a byte "b" equal to or greater than 0x80 - (i.e. with the MSB set) means 'take the next byte "c" and emit - ((b & 0x7f) << 8 | c)+0x80 zeroes'. This three-byte sequence can encode a run - of length 128-32895. - -The minimum overhead for this encoding scheme is three bytes: header and -end-of-input escape sequence. -""" - -from collections import Counter -from itertools import groupby - -_MAX_COUNT = 0x807F # max is ((0x7F << 8) | (0xFF) + 0x80 - - -def encode(source): - # Analyze the source data to select the escape byte. To keep things simple, we don't allow 0 to - # be the escape character. - source = bytes(source) - frequency = Counter({chr(n): 0 for n in range(1, 256)}) - frequency.update(source) - # most_common() doesn't define what happens if there's a tie in frequency. Let's always pick - # the lowest value of that frequency to make the encoding predictable. - occurences = frequency.most_common() - escape = min(x[0] for x in occurences if x[1] == occurences[-1][1]) - yield escape - for b, g in groupby(source): - if b == b'\0': - # this is a run of zeros - count = len(list(g)) - while count >= 0x80: - # encode the number of zeros using two bytes - unit = min(count, _MAX_COUNT) - count -= unit - unit -= 0x80 - yield escape - yield chr(((unit >> 8) & 0x7F) | 0x80) - yield chr(unit & 0xFF) - if count == 1: - # can't encode a length of 1 zero, so just emit it directly - yield b - elif 1 < count < 0x80: - # encode the number of zeros using one byte - yield escape - yield chr(count) - elif count < 0: - raise Exception('Encoding malfunctioned') - else: - # simply insert the characters (and escape the escape character) - for _ in g: - yield b - if b == escape: - yield b'\1' - yield escape - yield b'\0' - - -def decode(stream): - stream = iter(stream) - escape = next(stream) - while True: - char = next(stream) - if char == escape: - code = next(stream) - if code == b'\0': - return - elif code == b'\1': - yield escape - else: - if ord(code) & 0x80 == 0: - count = ord(code) - else: - count = (((ord(code) & 0x7f) << 8) | ord(next(stream))) + 0x80 - assert(count <= _MAX_COUNT) - for _ in xrange(count): - yield b'\0' - else: - yield char - - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - # run unit tests - import unittest - - class TestSparseLengthEncoding(unittest.TestCase): - def test_empty(self): - raw_data = '' - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x01\x00') - - def test_no_zeros(self): - raw_data = '\x02\xff\xef\x99' - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x02\xff\xef\x99\x01\x00') - - def test_one_zero(self): - raw_data = '\x00' - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x00\x01\x00') - - def test_small_number_of_zeros(self): - # under 0x80 zeros - raw_data = '\0' * 0x0040 - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x01\x40\x01\x00') - self.assertEquals(decoded_data, raw_data) - - def test_medium_number_of_zeros(self): - # between 0x80 and 0x807f zeros - raw_data = '\0' * 0x1800 - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x01\x97\x80\x01\x00') - self.assertEquals(decoded_data, raw_data) - - def test_remainder_one(self): - # leaves a remainder of 1 zero - raw_data = '\0' * (0x807f + 1) - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x01\xff\xff\x00\x01\x00') - self.assertEquals(decoded_data, raw_data) - - def test_remainder_under_128(self): - # leaves a remainder of 100 zeros - raw_data = '\0' * (0x807f + 100) - encoded_data = ''.join(encode(raw_data)) - decoded_data = ''.join(decode(encoded_data)) - self.assertEquals(encoded_data, '\x01\x01\xff\xff\x01\x64\x01\x00') - self.assertEquals(decoded_data, raw_data) - - unittest.main() - elif len(sys.argv) == 2: - # encode the specified file - data = open(sys.argv[1], 'rb').read() - encoded = ''.join(encode(data)) - if ''.join(decode(encoded)) != data: - raise Exception('Invalid encoding') - sys.stdout.write(''.join(encode(f))) - else: - raise Exception('Invalid arguments') diff --git a/platform/robert/boot/wscript b/platform/robert/boot/wscript deleted file mode 100644 index ce22742dd4..0000000000 --- a/platform/robert/boot/wscript +++ /dev/null @@ -1,158 +0,0 @@ -# Build script for the robert bootloader -# -# This bootloader is different than the other bootloaders that are in the platform/boot tree -# because it's still in development, so it's built as part of the normal firmware build, including -# using the environments and waftools that are part of the firmware build. At a later point, we'll -# want to split this out again to be more like the other bootloaders. - -import sys -import os -from waflib import Logs - -sys.path.append(os.path.abspath('waftools')) -import gitinfo -import objcopy - -waf_dir = sys.path[0] -sys.path.append(os.path.abspath('waftools')) -sys.path.append(os.path.join(waf_dir, '../../../tools')) - -def options(opt): - opt.load('gcc') - opt.add_option('--board', action='store', - choices=['robert_bb', 'robert_bb2', 'cutts_bb', 'robert_evt'], - help='Which board to build for (robert_bb, robert_bb2, cutts_bb, robert_evt)') - opt.add_option('--nowatchdog', action='store_true', - help='Disable automatic reboots when watchdog fires') - opt.add_option('--display_test', action='store_true', help='Enables the diplsay test loop') - - -def configure(conf): - conf.env.BOARD = conf.options.board if conf.options.board != 'cutts_bb' else 'robert_bb' - - # Find our binary tools - conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True) - conf.env.AS = conf.env.CC - conf.find_program('arm-none-eabi-gcc-ar', var='AR', mandatory=True) - - conf.load('gcc') - conf.load('binary_header') - - for tool in 'ar objcopy'.split(): - conf.find_program('arm-none-eabi-' + tool, var=tool.upper(), - mandatory=True) - - # Set up our compiler configuration - CPU_FLAGS = ['-mcpu=cortex-m7', '-mthumb'] - OPT_FLAGS = ['-Os', '-g'] - C_FLAGS = [ - '-std=c11', '-ffunction-sections', - '-Wall', '-Wextra', '-Werror', '-Wpointer-arith', - '-Wno-unused-parameter', '-Wno-missing-field-initializers', - '-Wno-error=unused-function', '-Wno-error=unused-variable', - '-Wno-error=unused-parameter', '-Wno-error=unused-but-set-variable', - '-Wno-packed-bitfield-compat' - ] - LINK_FLAGS = ['-Wl,--gc-sections', '-specs=nano.specs'] - - conf.env.append_unique('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS) - conf.env.append_unique('LINKFLAGS', LINK_FLAGS + CPU_FLAGS + OPT_FLAGS) - - if conf.options.nowatchdog: - conf.env.append_unique('DEFINES', 'NO_WATCHDOG') - - if conf.options.display_test: - conf.env.append_unique('DEFINES', 'DISPLAY_DEMO_LOOP') - - conf.env.append_unique('DEFINES', [ - '_REENT_SMALL=1', - 'USE_STDPERIPH_DRIVER=1', - 'BOARD_{}=1'.format(conf.env.BOARD.upper()) - ]) - - - # Load up other waftools that we need - conf.load('objcopy ldscript', tooldir='waftools') - - conf.recurse('vendor') - - -def size_bootloader(ctx): - """prints size information of the firmware""" - - elf = ctx.path.get_bld().find_node('robert_boot.elf') - if elf is None: - ctx.fatal('No bootloader ELF found for size') - - bin = ctx.path.get_bld().find_node('robert_boot.hex') - if bin is None: - ctx.fatal('No bootloader BIN found for size') - - import binutils - text, data, bss = binutils.size(elf.abspath()) - total = text + data - output = ('{:>7} {:>7} {:>7} {:>7} {:>7} filename\n' - '{:7} {:7} {:7} {:7} {:7x} robert_boot.elf'. - format('text', 'data', 'bss', 'dec', 'hex', text, data, bss, total, total)) - Logs.pprint('ORANGE', '\n' + output) - - MAX_SIZE = 32 * 1024 - - if total > MAX_SIZE: - ctx.fatal('Bootloader is too large! Size is 0x%x should be less than 0x%x' - % (total, MAX_SIZE)) - else: - Logs.pprint('ORANGE', 'Bootloader: {}/{} bytes used ({} free)\n'.format(total, MAX_SIZE, - MAX_SIZE - total)) - - -def build(bld): - elf_node = bld.path.get_bld().make_node('robert_boot.elf') - - bld.recurse('vendor') - - linkflags = ['-Wl,-Map,robert_boot.map'] - - sources = ['src/*.c', - 'src/drivers/**/*.c', - 'src/system/**/*.c', - 'src/util/**/*.c'] - - if bld.env.BOARD in ('robert_bb', 'robert_bb2'): - sources.append('src/board/board_robert_bb.c') - elif bld.env.BOARD in ('robert_evt',): - sources.append('src/board/board_robert_evt.c') - else: - bld.fatal("Invalid board set %s" % bld.env.BOARD) - - includes = ['src'] - - _generate_fpga_header(bld) - - bld(features='subst', - source='src/git_version.auto.h.in', - target='src/git_version.auto.h', - **gitinfo.get_git_revision(bld)) - - bld.program(features="objcopy", - source=bld.path.ant_glob(sources), - includes=includes, - target=elf_node, - ldscript='src/stm32f_flash_boot.ld', - linkflags=linkflags, - objcopy_bfdname='ihex', - objcopy_target=elf_node.change_ext('.hex'), - use=['stm32_stdlib']) - bld(rule=objcopy.objcopy_simple_bin, source='robert_boot.elf', target='robert_boot.bin') - - bld.add_post_fun(size_bootloader) - - -def _generate_fpga_header(bld): - bld(features='binary_header', - source='../Robert_UL1K_bootloader_bitmap.fpga', - target='src/drivers/display/bootloader_fpga_bitstream.auto.h', - array_name='s_fpga_bitstream', - compressed=True) - -# vim:filetype=python diff --git a/platform/silk/boot/boot-bin-update.sh b/platform/silk/boot/boot-bin-update.sh deleted file mode 100755 index 58a2a69b1c..0000000000 --- a/platform/silk/boot/boot-bin-update.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -o errexit -o xtrace - -cd "${0%/*}" - -reloutdir="../../../bin/boot" -OUTDIR=`cd "${reloutdir}"; pwd` -BOARDS=(silk) -# Use commit timestamp, same as the one compiled into the bootloader binary -VERSION=`git log -1 --format=%ct HEAD` - -# Clear out old versions of the bootloader binaries -for board in ${BOARDS[*]}; do - git rm --ignore-unmatch ${OUTDIR}/{nowatchdog_,}boot_${board}@*.{hex,elf,bin} || true -done - -# Build all bootloader variants and copy them into OUTDIR -build_and_copy () { - local variant="$1"; - shift; - - for board in ${BOARDS[*]}; do - ./waf configure --board=${board} $@ build --progress - for ext in hex elf bin; do - mv build/tintin_boot.${ext} \ - "${OUTDIR}/${variant}boot_${board}@${VERSION}.${ext}" - done - done -} - -build_and_copy "" -build_and_copy nowatchdog_ --nowatchdog diff --git a/platform/silk/boot/desym.sh b/platform/silk/boot/desym.sh deleted file mode 100755 index febc86638f..0000000000 --- a/platform/silk/boot/desym.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -arm-none-eabi-addr2line --exe=build/tintin_boot.elf $1 diff --git a/platform/silk/boot/flash b/platform/silk/boot/flash deleted file mode 100755 index 4315ee951a..0000000000 --- a/platform/silk/boot/flash +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -o errexit - -SCRIPTDIR="${0%/*}" -IMGFILE="${SCRIPTDIR}/build/tintin_boot.hex" - -if [ ! -f "${IMGFILE}" ]; then - echo "Cannot find bootloader binary at '${IMGFILE}'." - echo "Try running './waf build'" - exit 1 -fi - -OPENOCD_SCRIPT=" -init -reset halt -flash write_image erase \"${IMGFILE}\" -reset -shutdown -" - -openocd -f openocd.cfg -c "${OPENOCD_SCRIPT}" - -# vim:filetype=sh diff --git a/platform/silk/boot/gdb b/platform/silk/boot/gdb deleted file mode 100755 index 66523a95d3..0000000000 --- a/platform/silk/boot/gdb +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -TARGET="target extended-remote | openocd -f openocd.cfg \ - -c \"gdb_port pipe; init; reset halt;\"" - -arm-none-eabi-gdb build/tintin_boot.elf \ - -ex "${TARGET}" \ - -ex "break boot_main" \ - -ex "continue" - -# vim:filetype=sh diff --git a/platform/silk/boot/openocd_swd_ftdi.cfg b/platform/silk/boot/openocd_swd_ftdi.cfg deleted file mode 100644 index df04805a31..0000000000 --- a/platform/silk/boot/openocd_swd_ftdi.cfg +++ /dev/null @@ -1,24 +0,0 @@ -interface ftdi -ftdi_vid_pid 0x0403 0x6011 0x0403 0x7893 - -# output value, direction (1 for output, 0 for input) -ftdi_layout_init 0x1848 0x185b -ftdi_layout_signal SWD_EN -data 0 -oe 0 -ftdi_layout_signal SWDIO_OE -data 0 -oe 0 -ftdi_layout_signal nSRST -data 0x0040 -oe 0x0040 - -# Red + Green LED (inverted output: low is on) -# TX LED (GREEN) -ftdi_layout_signal LED -ndata 0x0080 -oe 0x0080 -# RX LED (RED) -ftdi_layout_signal LED2 -ndata 0x0020 -oe 0x0020 - -transport select swd - -# Note: This works around issue where reset_config does not actually toggle this line. It would be -# nice to figure out what the actual issue with 'reset_config' is at some point -proc init_reset { mode } { - ftdi_set_signal nSRST 0 - sleep 100 - ftdi_set_signal nSRST z -} diff --git a/platform/silk/boot/run-tests.sh b/platform/silk/boot/run-tests.sh deleted file mode 100755 index 603fca60d5..0000000000 --- a/platform/silk/boot/run-tests.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit - -cd "${0%/*}" - -CLAR_DIR=`cd ../../../tools/clar; pwd` - -clar () { - local test_name=${1:?}; shift; - local test_suite=${1:?}; shift; - local test_dir="build/test/${test_name}" - - mkdir -p "${test_dir}" - - python "${CLAR_DIR}/clar.py" \ - --file="${test_suite}" \ - --clar-path="${CLAR_DIR}" \ - "${test_dir}" - - gcc -o "${test_dir}/do_test" \ - -I"${test_dir}" -Isrc \ - -Ivendor/STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/Libraries/CMSIS/Include \ - -Ivendor/STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include \ - -Ivendor/STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/Libraries/STM32F4xx_StdPeriph_Driver/inc - -DMICRO_FAMILY_STM32F4 \ - -ffunction-sections \ - -Wl,-dead_strip \ - "${test_dir}/clar_main.c" "${test_suite}" $@ - # If running on a platform with GNU ld, - # replace -Wl,-dead_strip with -Wl,--gc-sections - - echo "Running test ${test_suite}..." - "${test_dir}/do_test" -} - -clar system_flash_algo test/test_system_flash.c \ - src/drivers/system_flash.c diff --git a/platform/silk/boot/src/board/board.h b/platform/silk/boot/src/board/board.h deleted file mode 100644 index 05b00a6f69..0000000000 --- a/platform/silk/boot/src/board/board.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "display.h" - -#include "drivers/button_id.h" - -#include "stm32f4xx_gpio.h" - -#include -#include - -#define GPIO_Port_NULL ((GPIO_TypeDef *) 0) -#define GPIO_Pin_NULL ((uint16_t)0x0000) - - -typedef struct { - //! One of EXTI_PortSourceGPIOX - uint8_t exti_port_source; - - //! Value between 0-15 - uint8_t exti_line; -} ExtiConfig; - -typedef struct { - const char* const name; ///< Name for debugging purposes. - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - ExtiConfig exti; - GPIOPuPd_TypeDef pull; -} ButtonConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} ButtonComConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - bool active_high; ///< Pin is active high or active low -} OutputConfig; - -//! Alternate function pin configuration -//! Used to configure a pin for use by a peripheral -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - const uint16_t gpio_pin_source; ///< One of GPIO_PinSourceX. - const uint8_t gpio_af; ///< One of GPIO_AF_X -} AfConfig; - -typedef struct { - I2C_TypeDef *const i2c; - AfConfig i2c_scl; ///< Alternate Function configuration for SCL pin - AfConfig i2c_sda; ///< Alternate Function configuration for SDA pin - uint32_t clock_ctrl; ///< Peripheral clock control flag - uint32_t clock_speed; ///< Bus clock speed - uint32_t duty_cycle; ///< Bus clock duty cycle in fast mode - const uint8_t ev_irq_channel; ///< I2C Event interrupt (One of X_IRQn). For example, I2C1_EV_IRQn. - const uint8_t er_irq_channel; ///< I2C Error interrupt (One of X_IRQn). For example, I2C1_ER_IRQn. - void (* const rail_cfg_fn)(void); //! Configure function for pins on this rail. - void (* const rail_ctl_fn)(bool enabled); //! Control function for this rail. -} I2cBusConfig; - -typedef enum I2cDevice { - I2C_DEVICE_AS3701B, -} I2cDevice; - -typedef struct { - // I2C Configuration - ///////////////////////////////////////////////////////////////////////////// - const I2cBusConfig *i2c_bus_configs; - const uint8_t i2c_bus_count; - const uint8_t *i2c_device_map; - const uint8_t i2c_device_count; -} BoardConfig; - -// Button Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ButtonConfig buttons[NUM_BUTTONS]; - const ButtonComConfig button_com; -} BoardConfigButton; - -#include "board_definitions.h" diff --git a/platform/silk/boot/src/board/board_definitions.h b/platform/silk/boot/src/board/board_definitions.h deleted file mode 100644 index 8a45d9b4bb..0000000000 --- a/platform/silk/boot/src/board/board_definitions.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if BOARD_SILK -#include "board_silk.h" -#else -#error "Unknown board definition" -#endif diff --git a/platform/silk/boot/src/board/board_silk.h b/platform/silk/boot/src/board/board_silk.h deleted file mode 100644 index 726cc2ecf3..0000000000 --- a/platform/silk/boot/src/board/board_silk.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/misc.h" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -#define USE_PARALLEL_FLASH 1 // FIXME PBL-28872: Hack to get the "modern" flash layout. - // Fix when we add support for new flash - -#define BOARD_I2C_BUS_COUNT (ARRAY_LENGTH(SILK_I2C_BUS_CONFIGS)) - -static const I2cBusConfig SILK_I2C_BUS_CONFIGS[] = { - // PMIC I2c - [0] = { - .i2c = I2C3, - .i2c_scl = { GPIOA, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF_I2C3 }, - .i2c_sda = { GPIOB, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF9_I2C3 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_16_9, - .clock_ctrl = RCC_APB1Periph_I2C3, - .ev_irq_channel = I2C3_EV_IRQn, - .er_irq_channel = I2C3_ER_IRQn, - }, -}; - -static const uint8_t SILK_I2C_DEVICE_MAP[] = { - [I2C_DEVICE_AS3701B] = 0, -}; - -static const BoardConfig BOARD_CONFIG = { - .i2c_bus_configs = SILK_I2C_BUS_CONFIGS, - .i2c_bus_count = BOARD_I2C_BUS_COUNT, - .i2c_device_map = SILK_I2C_DEVICE_MAP, - .i2c_device_count = ARRAY_LENGTH(SILK_I2C_DEVICE_MAP), -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = - { "Back", GPIOC, GPIO_Pin_13, { EXTI_PortSourceGPIOC, 13 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = - { "Up", GPIOD, GPIO_Pin_2, { EXTI_PortSourceGPIOD, 2 }, GPIO_PuPd_DOWN }, - [BUTTON_ID_SELECT] = - { "Select", GPIOH, GPIO_Pin_0, { EXTI_PortSourceGPIOH, 0 }, GPIO_PuPd_DOWN }, - [BUTTON_ID_DOWN] = - { "Down", GPIOH, GPIO_Pin_1, { EXTI_PortSourceGPIOH, 1 }, GPIO_PuPd_DOWN }, - }, - - .button_com = { 0 }, -}; - -typedef enum { - QSpiPin_CS, - QSpiPin_SCLK, - QSpiPin_DQ0, - QSpiPin_DQ1, - QSpiPin_DQ2, - QSpiPin_DQ3, - QSpiPinCount, -} QSpiPin; - -static const AfConfig BOARD_CONFIG_FLASH_PINS[] = { - [QSpiPin_CS] = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF10_QUADSPI, - }, - [QSpiPin_SCLK] = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_2, - .gpio_pin_source = GPIO_PinSource2, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ0] = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ1] = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ2] = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF9_QUADSPI, - }, - [QSpiPin_DQ3] = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF9_QUADSPI, - }, -}; diff --git a/platform/silk/boot/src/board/display.h b/platform/silk/boot/src/board/display.h deleted file mode 100644 index e03d8f793b..0000000000 --- a/platform/silk/boot/src/board/display.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISP_COLS 144 -#define DISP_ROWS 168 -#define PBL_DISP_SHAPE_RECT diff --git a/platform/silk/boot/src/boot_tests.c b/platform/silk/boot/src/boot_tests.c deleted file mode 100644 index 8212e2c075..0000000000 --- a/platform/silk/boot/src/boot_tests.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "boot_tests.h" - -#include "board/board.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/flash.h" -#include "system/bootbits.h" -#include "system/rtc_registers.h" -#include "util/misc.h" - -#include "stm32f4xx.h" - -#include -#include -#include - -static const int STUCK_BUTTON_THRESHOLD = 5; - -bool is_button_stuck(void) { - // We store how many times each button has been pressed on previous boots in this - // rtc backup register. Every time when we boot without that button pressed that - // counter gets cleared. If the byte reaches 5, return a failure. - - uint32_t button_counter_register = RTC_ReadBackupRegister(STUCK_BUTTON_REGISTER); - uint8_t* button_counter = (uint8_t*) (&button_counter_register); - bool result = false; - - for (int button_id = 0; button_id < NUM_BUTTONS; button_id++) { - if (!button_is_pressed(button_id)) { - button_counter[button_id] = 0; - continue; - } - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_putstr("Stuck button register is invalid, clearing."); - dbgserial_print_hex(button_counter_register); - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, 0); - return false; - } - - button_counter[button_id] += 1; - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_print("Button id "); - dbgserial_print_hex(button_id); - dbgserial_putstr("is stuck!"); - result = true; - } - } - - if (button_counter_register != 0) { - dbgserial_print("Button was pushed on boot. Button counter: "); - dbgserial_print_hex(button_counter_register); - dbgserial_newline(); - } - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, button_counter_register); - return result; -} - -bool is_flash_broken(void) { - return !flash_sanity_check(); -} diff --git a/platform/silk/boot/src/boot_tests.h b/platform/silk/boot/src/boot_tests.h deleted file mode 100644 index 1f95ffeeb7..0000000000 --- a/platform/silk/boot/src/boot_tests.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool is_button_stuck(void); -bool is_flash_broken(void); diff --git a/platform/silk/boot/src/drivers/button.c b/platform/silk/boot/src/drivers/button.c deleted file mode 100644 index e92383afd3..0000000000 --- a/platform/silk/boot/src/drivers/button.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/button.h" - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" - -static void initialize_button_common(void) { - if (!BOARD_CONFIG_BUTTON.button_com.gpio) { - // This board doesn't use a button common pin. - return; - } - - // Configure BUTTON_COM to drive low. When the button - // is pressed this pin will be connected to the pin for the - // button. - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_BUTTON.button_com.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(BOARD_CONFIG_BUTTON.button_com.gpio, &GPIO_InitStructure); - GPIO_WriteBit(BOARD_CONFIG_BUTTON.button_com.gpio, BOARD_CONFIG_BUTTON.button_com.gpio_pin, 0); -} - -static void initialize_button(const ButtonConfig* config) { - // Configure the pin itself - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_PuPd = config->pull; - GPIO_InitStructure.GPIO_Pin = config->gpio_pin; - GPIO_Init(config->gpio, &GPIO_InitStructure); -} - -bool button_is_pressed(ButtonId id) { - const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id]; - uint8_t bit = GPIO_ReadInputDataBit(button_config->gpio, button_config->gpio_pin); - return bit; -} - -uint8_t button_get_state_bits(void) { - uint8_t button_state = 0x00; - for (int i = 0; i < NUM_BUTTONS; ++i) { - button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i; - } - return button_state; -} - -void button_init(void) { - // Need to disable button wakeup functionality - // or the buttons don't register input - PWR_WakeUpPinCmd(PWR_WakeUp_Pin1, DISABLE); - PWR_WakeUpPinCmd(PWR_WakeUp_Pin2, DISABLE); - PWR_WakeUpPinCmd(PWR_WakeUp_Pin3, DISABLE); - - periph_config_enable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG); - - initialize_button_common(); - for (int i = 0; i < NUM_BUTTONS; ++i) { - initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]); - } - - periph_config_disable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG); -} diff --git a/platform/silk/boot/src/drivers/button.h b/platform/silk/boot/src/drivers/button.h deleted file mode 100644 index fae7e2dfca..0000000000 --- a/platform/silk/boot/src/drivers/button.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "button_id.h" - -#include -#include - -void button_init(void); - -bool button_is_pressed(ButtonId id); -uint8_t button_get_state_bits(void); diff --git a/platform/silk/boot/src/drivers/button_id.h b/platform/silk/boot/src/drivers/button_id.h deleted file mode 100644 index 02aa1c75d8..0000000000 --- a/platform/silk/boot/src/drivers/button_id.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @addtogroup UI -//! @{ -//! @addtogroup Clicks -//! \brief Dealing with button input -//! @{ - -//! Button ID values -//! @see \ref click_recognizer_get_button_id() -typedef enum { - //! Back button - BUTTON_ID_BACK = 0, - //! Up button - BUTTON_ID_UP, - //! Select (middle) button - BUTTON_ID_SELECT, - //! Down button - BUTTON_ID_DOWN, - //! Total number of buttons - NUM_BUTTONS -} ButtonId; - -//! @} // end addtogroup Clicks -//! @} // end addtogroup UI diff --git a/platform/silk/boot/src/drivers/dbgserial.c b/platform/silk/boot/src/drivers/dbgserial.c deleted file mode 100644 index bdf889a63c..0000000000 --- a/platform/silk/boot/src/drivers/dbgserial.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" - -#include "drivers/periph_config.h" - -#include "drivers/gpio.h" - -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_usart.h" -#include "util/attributes.h" -#include "util/cobs.h" -#include "util/crc32.h" -#include "util/misc.h" -#include "util/misc.h" -#include "util/net.h" - -#include - -#define MAX_MESSAGE (256) -#define FRAME_DELIMITER '\x55' -#define PULSE_TRANSPORT_PUSH (0x5021) -#define PULSE_PROTOCOL_LOGGING (0x0003) - -static const int SERIAL_BAUD_RATE = 1000000; - -typedef struct PACKED PulseFrame { - net16 protocol; - unsigned char information[]; -} PulseFrame; - -typedef struct PACKED PushPacket { - net16 protocol; - net16 length; - unsigned char information[]; -} PushPacket; - -static const unsigned char s_message_header[] = { - // Message type: text - 1, - // Source filename - 'B', 'O', 'O', 'T', 'L', 'O', 'A', 'D', 'E', 'R', 0, 0, 0, 0, 0, 0, - // Log level and task - '*', '*', - // Timestamp - 0, 0, 0, 0, 0, 0, 0, 0, - // Line number - 0, 0, -}; - -static size_t s_message_length = 0; -static unsigned char s_message_buffer[MAX_MESSAGE]; - - -void dbgserial_init(void) { - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - - // Enable GPIO and UART3 peripheral clocks - periph_config_enable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_USART1); - - // USART_OverSampling8Cmd(USART3, ENABLE); - - /* Connect PXx to USARTx_Tx*/ - GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); - - /* Connect PXx to USARTx_Rx*/ - GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1); - - /* Configure USART Tx as alternate function */ - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Configure USART Rx as alternate function */ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; - GPIO_Init(GPIOB, &GPIO_InitStructure); - - /* USART configuration */ - USART_InitStructure.USART_BaudRate = SERIAL_BAUD_RATE; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(USART1, &USART_InitStructure); - - /* Enable USART */ - USART_Cmd(USART1, ENABLE); -} - -static void prv_putchar(uint8_t c) { - while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) continue; - USART_SendData(USART1, c); - while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) continue; -} - -void dbgserial_print(const char* str) { - for (; *str && s_message_length < MAX_MESSAGE; ++str) { - if (*str == '\n') { - dbgserial_newline(); - } else if (*str != '\r') { - s_message_buffer[s_message_length++] = *str; - } - } -} - -void dbgserial_newline(void) { - uint32_t crc; - size_t raw_length = sizeof(PulseFrame) + sizeof(PushPacket) + - sizeof(s_message_header) + s_message_length + sizeof(crc); - unsigned char raw_packet[raw_length]; - - PulseFrame *frame = (PulseFrame *)raw_packet; - frame->protocol = hton16(PULSE_TRANSPORT_PUSH); - - PushPacket *transport = (PushPacket *)frame->information; - transport->protocol = hton16(PULSE_PROTOCOL_LOGGING); - transport->length = hton16(sizeof(PushPacket) + sizeof(s_message_header) + - s_message_length); - - unsigned char *app = transport->information; - memcpy(app, s_message_header, sizeof(s_message_header)); - memcpy(&app[sizeof(s_message_header)], s_message_buffer, - s_message_length); - - crc = crc32(CRC32_INIT, raw_packet, raw_length - sizeof(crc)); - memcpy(&raw_packet[raw_length - sizeof(crc)], &crc, sizeof(crc)); - - unsigned char cooked_packet[MAX_SIZE_AFTER_COBS_ENCODING(raw_length)]; - size_t cooked_length = cobs_encode(cooked_packet, raw_packet, raw_length); - - prv_putchar(FRAME_DELIMITER); - for (size_t i = 0; i < cooked_length; ++i) { - if (cooked_packet[i] == FRAME_DELIMITER) { - prv_putchar('\0'); - } else { - prv_putchar(cooked_packet[i]); - } - } - prv_putchar(FRAME_DELIMITER); - - s_message_length = 0; -} - -void dbgserial_putstr(const char* str) { - dbgserial_print(str); - - dbgserial_newline(); -} - -void dbgserial_print_hex(uint32_t value) { - char buf[12]; - itoa_hex(value, buf, sizeof(buf)); - dbgserial_print(buf); -} diff --git a/platform/silk/boot/src/drivers/dbgserial.h b/platform/silk/boot/src/drivers/dbgserial.h deleted file mode 100644 index 5cf5d7dd02..0000000000 --- a/platform/silk/boot/src/drivers/dbgserial.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void dbgserial_init(void); - -void dbgserial_putstr(const char* str); - -void dbgserial_newline(void); - -//! Like dbgserial_putstr, but without a terminating newline -void dbgserial_print(const char* str); - -void dbgserial_print_hex(uint32_t value); - -void dbgserial_putstr_fmt(char* buffer, unsigned int buffer_size, const char* fmt, ...) - __attribute__((format(printf, 3, 4))); diff --git a/platform/silk/boot/src/drivers/display.h b/platform/silk/boot/src/drivers/display.h deleted file mode 100644 index bd0028f337..0000000000 --- a/platform/silk/boot/src/drivers/display.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -void display_init(void); - -void display_boot_splash(void); - -void display_error_code(uint32_t); - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void); - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator); diff --git a/platform/silk/boot/src/drivers/display/resources/dead_face.xbm b/platform/silk/boot/src/drivers/display/resources/dead_face.xbm deleted file mode 100644 index 0637edd47f..0000000000 --- a/platform/silk/boot/src/drivers/display/resources/dead_face.xbm +++ /dev/null @@ -1,47 +0,0 @@ -#define dead_face_width 92 -#define dead_face_height 44 -static const unsigned char dead_face_bits[] = { - 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x30, 0x00, - 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, - 0x00, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0c, 0x00, - 0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, - 0x00, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, - 0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, - 0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, - 0x00, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, - 0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, - 0x00, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0c, 0x00, - 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, - 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, - 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, - 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, - 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; diff --git a/platform/silk/boot/src/drivers/display/resources/empty_bar.xbm b/platform/silk/boot/src/drivers/display/resources/empty_bar.xbm deleted file mode 100644 index ace1c01445..0000000000 --- a/platform/silk/boot/src/drivers/display/resources/empty_bar.xbm +++ /dev/null @@ -1,11 +0,0 @@ -#define empty_bar_width 96 -#define empty_bar_height 8 -static const unsigned char empty_bar_bits[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; diff --git a/platform/silk/boot/src/drivers/display/resources/error_url.xbm b/platform/silk/boot/src/drivers/display/resources/error_url.xbm deleted file mode 100644 index 5a1920b112..0000000000 --- a/platform/silk/boot/src/drivers/display/resources/error_url.xbm +++ /dev/null @@ -1,20 +0,0 @@ -#define error_url_width 109 -#define error_url_height 14 -static const unsigned char error_url_bits[] = { - 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x78, 0x36, 0x9b, 0x79, 0xc0, - 0xe3, 0xd9, 0x0c, 0x8c, 0x67, 0xdb, 0x3c, 0x1b, 0xf8, 0xfd, 0x7e, 0xbf, - 0xfd, 0xe0, 0xf7, 0xfb, 0x1f, 0xc6, 0xef, 0xfb, 0x7e, 0x1f, 0x98, 0xcd, - 0x66, 0xb3, 0xcd, 0x60, 0x36, 0x9b, 0x19, 0xc6, 0x6c, 0x18, 0x66, 0x03, - 0x98, 0xfd, 0x66, 0xb3, 0xfd, 0x60, 0x30, 0x9b, 0x19, 0xc6, 0x6f, 0x18, - 0x66, 0x03, 0x98, 0xfd, 0x66, 0xb3, 0xfd, 0x60, 0x30, 0x9b, 0x19, 0xc3, - 0x6f, 0x18, 0x66, 0x03, 0x98, 0x0d, 0x66, 0xb3, 0x0d, 0x60, 0x36, 0x9b, - 0x19, 0xc3, 0x60, 0x18, 0x66, 0x03, 0xf8, 0xfd, 0x7e, 0xbf, 0xfd, 0xec, - 0xf7, 0x9b, 0x19, 0xc3, 0x6f, 0x18, 0x7e, 0x03, 0xd8, 0x78, 0x36, 0x9b, - 0x79, 0xcc, 0xe3, 0x99, 0x99, 0x81, 0x67, 0x18, 0x3c, 0x03, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }; diff --git a/platform/silk/boot/src/drivers/display/resources/hex_digits.h b/platform/silk/boot/src/drivers/display/resources/hex_digits.h deleted file mode 100644 index 7b86f0ef91..0000000000 --- a/platform/silk/boot/src/drivers/display/resources/hex_digits.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// hex_digits_bits is indexed on the digit: -// ie: hex_digits_bits[0] is the bits to display 0 on -// the screen stored in xbm format -static const uint8_t hex_digits_bits[][36] = { - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x38, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, - 0x0E, 0x00, 0x0F, 0x00, 0x07, 0x00, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xF8, 0x00, 0x78, 0x00, 0xF8, 0x00, 0xC0, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xE0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0xF8, 0x00, - 0xF8, 0x00, 0xFC, 0x00, 0xEC, 0x00, 0xEE, 0x00, 0xE6, 0x00, 0xFF, 0x01, - 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, - }, - { - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, - 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC0, 0x01, 0xC0, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, - 0x07, 0x00, 0x77, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, 0x00, - 0x70, 0x00, 0x70, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0xFE, 0x00, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x01, 0xDC, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xF0, 0x01, 0xFC, 0x01, 0xCE, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01, - }, - { - 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0xE7, 0x00, 0xF7, 0x01, - 0xFF, 0x01, 0xCF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xCF, 0x01, 0xFF, 0x01, 0xF7, 0x01, 0xE7, 0x00, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, - 0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xCE, 0x01, 0xDF, 0x01, - 0xFF, 0x01, 0xE7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFF, 0x01, - 0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, - 0xFE, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - } -}; diff --git a/platform/silk/boot/src/drivers/display/resources/pebble_logo.xbm b/platform/silk/boot/src/drivers/display/resources/pebble_logo.xbm deleted file mode 100644 index 10ed47ff9b..0000000000 --- a/platform/silk/boot/src/drivers/display/resources/pebble_logo.xbm +++ /dev/null @@ -1,35 +0,0 @@ -#define pebble_logo_width 105 -#define pebble_logo_height 27 -static const unsigned char pebble_logo_bits[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, - 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, - 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0xE0, - 0x03, 0x00, 0x1F, 0x60, 0xF8, 0x00, 0xC3, 0x07, 0x18, 0xC0, 0x07, 0x00, - 0x00, 0xF8, 0x0F, 0xC0, 0x7F, 0x60, 0xFE, 0x03, 0xF3, 0x1F, 0x18, 0xF0, - 0x1F, 0x00, 0x00, 0x0C, 0x18, 0x60, 0xC0, 0x60, 0x03, 0x06, 0x1B, 0x30, - 0x18, 0x18, 0x30, 0x00, 0x00, 0x06, 0x30, 0x30, 0x80, 0xE1, 0x01, 0x0C, - 0x0F, 0x60, 0x18, 0x0C, 0x60, 0x00, 0x00, 0x03, 0x60, 0x18, 0x00, 0xE3, - 0x00, 0x18, 0x07, 0xC0, 0x18, 0x06, 0xC0, 0x00, 0x80, 0x01, 0x40, 0x0C, - 0x00, 0x62, 0x00, 0x10, 0x03, 0x80, 0x18, 0x03, 0x80, 0x00, 0x80, 0x01, - 0xC0, 0x0C, 0x00, 0x66, 0x00, 0x30, 0x03, 0x80, 0x19, 0x03, 0x80, 0x01, - 0x80, 0x01, 0xC0, 0xFC, 0xFF, 0x67, 0x00, 0x30, 0x03, 0x80, 0x19, 0xFF, - 0xFF, 0x01, 0x80, 0x01, 0xC0, 0xFC, 0xFF, 0x63, 0x00, 0x30, 0x03, 0x80, - 0x19, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0xC0, 0x0C, 0x00, 0x60, 0x00, 0x30, - 0x03, 0x80, 0x19, 0x03, 0x00, 0x00, 0x80, 0x01, 0xC0, 0x0C, 0x00, 0x60, - 0x00, 0x30, 0x03, 0x80, 0x19, 0x03, 0x00, 0x00, 0x80, 0x01, 0x40, 0x0C, - 0x00, 0x62, 0x00, 0x10, 0x03, 0x80, 0x18, 0x03, 0x80, 0x00, 0x80, 0x03, - 0x60, 0x18, 0x00, 0xC3, 0x00, 0x18, 0x06, 0xC0, 0x18, 0x06, 0xC0, 0x00, - 0x80, 0x07, 0x30, 0x30, 0x80, 0x81, 0x01, 0x0C, 0x0C, 0x60, 0x18, 0x0C, - 0x60, 0x00, 0x80, 0x0D, 0x18, 0x60, 0xC0, 0x00, 0x03, 0x06, 0x18, 0x30, - 0x38, 0x18, 0x30, 0x00, 0x80, 0xF9, 0x0F, 0xC0, 0x7F, 0x00, 0xFE, 0x03, - 0xF0, 0x1F, 0x70, 0xF0, 0x1F, 0x00, 0x80, 0xE1, 0x03, 0x00, 0x1F, 0x00, - 0xF8, 0x00, 0xC0, 0x07, 0x60, 0xC0, 0x07, 0x00, 0x80, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; diff --git a/platform/silk/boot/src/drivers/display/sharp_ls013b7dh01.c b/platform/silk/boot/src/drivers/display/sharp_ls013b7dh01.c deleted file mode 100644 index b6063e00ae..0000000000 --- a/platform/silk/boot/src/drivers/display/sharp_ls013b7dh01.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/display.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" -#include "drivers/dbgserial.h" -#include "util/attributes.h" -#include "util/delay.h" - -#include "stm32f4xx_dma.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_spi.h" - -#include -#include - -// Bootloader images -#include "drivers/display/resources/hex_digits.h" -#include "drivers/display/resources/dead_face.xbm" -#include "drivers/display/resources/empty_bar.xbm" -#include "drivers/display/resources/error_url.xbm" -#include "drivers/display/resources/pebble_logo.xbm" - -#define DISP_LINE_BYTES (DISP_COLS / 8) -#define DISP_LINE_WORDS (((DISP_COLS - 1) / 32) + 1) - -// GPIO constants -#define DISP_SPI (SPI2) -#define DISP_GPIO (GPIOB) -#define DISPLAY_SPI_CLOCK (RCC_APB1Periph_SPI2) -#define DISP_PIN_SCS (GPIO_Pin_9) -#define DISP_PINSOURCE_SCS (GPIO_PinSource9) -#define DISP_PIN_SCLK (GPIO_Pin_10) -#define DISP_PINSOURCE_SCKL (GPIO_PinSource10) -#define DISP_PIN_SI (GPIO_Pin_15) -#define DISP_PINSOURCE_SI (GPIO_PinSource15) -#define DISP_LCD_GPIO (GPIOA) -#define DISP_PIN_LCD (GPIO_Pin_0) -#define DISP_PINSOURCE_LCD (GPIO_PinSource0) -#define DISP_MODE_STATIC (0x00) -#define DISP_MODE_WRITE (0x80) -#define DISP_MODE_CLEAR (0x20) - -// The bootloader leaves SYSCLK at defaults (connected to HSI at 16 Mhz), -// and there are no prescalers on any of the buses. Since the display -// can handle a max of 2 Mhz, we want to divide by 8 -#define DISPLAY_PERIPH_PRESCALER (SPI_BaudRatePrescaler_8) - -static void prv_enable_display_spi_clock() { - periph_config_enable(RCC_APB1PeriphClockCmd, DISPLAY_SPI_CLOCK); -} - -static void prv_disable_display_spi_clock() { - periph_config_disable(RCC_APB1PeriphClockCmd, DISPLAY_SPI_CLOCK); -} - -static void prv_enable_chip_select(void) { - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); - // required setup time > 3us - delay_us(7); -} - -static void prv_disable_chip_select(void) { - // delay while last byte is emitted by the SPI peripheral - delay_us(7); - - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); - // hold time > 1us - // produce a delay 4ms - delay_us(4); -} - -//! These functions needed to be called around any commands that -//! are sent to the display. NOINLINE only for code size savings. -static NOINLINE void prv_enable_display_access(void) { - prv_enable_display_spi_clock(); - prv_enable_chip_select(); -} - -static NOINLINE void prv_disable_display_access(void) { - prv_disable_chip_select(); - prv_disable_display_spi_clock(); -} - -//! Write a single byte synchronously to the display. This is the only practical -//! way to write to the display in the bootloader since we don't have interrupts. -static void prv_display_write_byte(uint8_t d) { - // Block until the tx buffer is empty - SPI_I2S_SendData(DISP_SPI, d); - while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) {} -} - -// Since all these values are constant we can save code space -// by storing the initialized struct in memory rather than -// needing to copy in each value -static GPIO_InitTypeDef s_disp_gpio_init = { - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = GPIO_Speed_50MHz, - .GPIO_Pin = DISP_PIN_SCLK | DISP_PIN_SI -}; - -static SPI_InitTypeDef s_disp_spi_init = { - .SPI_Direction = SPI_Direction_1Line_Tx, // Write-only SPI - .SPI_Mode = SPI_Mode_Master, - .SPI_DataSize = SPI_DataSize_8b, - .SPI_CPOL = SPI_CPOL_Low, - .SPI_CPHA = SPI_CPHA_1Edge, - .SPI_NSS = SPI_NSS_Soft, - // We want the SPI clock to run at 2MHz - .SPI_BaudRatePrescaler = DISPLAY_PERIPH_PRESCALER, - // MSB order allows us to write pixels out without reversing bytes, but command bytes - // have to be reversed - .SPI_FirstBit = SPI_FirstBit_MSB, - .SPI_CRCPolynomial = 7 // default -}; - -static void prv_display_start(void) { - // Setup alternate functions - GPIO_PinAFConfig(DISP_GPIO, DISP_PINSOURCE_SCKL, GPIO_AF_SPI2); - GPIO_PinAFConfig(DISP_GPIO, DISP_PINSOURCE_SI, GPIO_AF_SPI2); - - // Init display GPIOs - GPIO_Init(DISP_GPIO, &s_disp_gpio_init); - - // Init CS - s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT; - s_disp_gpio_init.GPIO_OType = GPIO_OType_PP; - s_disp_gpio_init.GPIO_Pin = DISP_PIN_SCS; - GPIO_Init(DISP_GPIO, &s_disp_gpio_init); - - // Init LCD Control - s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT; - s_disp_gpio_init.GPIO_OType = GPIO_OType_PP; - s_disp_gpio_init.GPIO_Pin = DISP_PIN_LCD; - GPIO_Init(DISP_LCD_GPIO, &s_disp_gpio_init); - - // Set up a SPI bus on SPI2 - SPI_I2S_DeInit(DISP_SPI); - SPI_Init(DISP_SPI, &s_disp_spi_init); - - SPI_Cmd(DISP_SPI, ENABLE); - - - // Hold LCD on - GPIO_WriteBit(DISP_LCD_GPIO, DISP_PIN_LCD, Bit_SET); -} - -// Clear-all mode is entered by sending 0x04 to the panel -void display_clear(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_CLEAR); - prv_display_write_byte(0x00); - - prv_disable_display_access(); -} - -//! Static mode is entered by sending 0x00 to the panel -//! This stops any further updates being registered by -//! the display, preventing corruption on shutdown / boot -static void prv_display_enter_static(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_STATIC); - prv_display_write_byte(0x00); - prv_display_write_byte(0x00); - - prv_disable_display_access(); -} - -// Helper to reverse command bytes -static uint8_t prv_reverse_bits(uint8_t input) { - uint8_t result; - __asm__ ("rev %[result], %[input]\n\t" - "rbit %[result], %[result]" - : [result] "=r" (result) - : [input] "r" (input)); - return result; -} - -static void prv_display_start_write(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_WRITE); -} - -static void prv_display_write_line(uint8_t line_addr, const uint8_t *line) { - // 1-indexed (ugh) 8bit line address (1-168) - prv_display_write_byte(prv_reverse_bits(line_addr + 1)); - - for (int i = 0; i < DISP_LINE_BYTES; ++i) { - prv_display_write_byte(prv_reverse_bits(line[i])); - } - - prv_display_write_byte(0x00); -} - -static void prv_display_end_write(void) { - prv_display_write_byte(0x00); - - - prv_disable_display_access(); -} - -// Round a bit offset to a byte offset -static unsigned prv_round_to_byte(unsigned x) { - return (x + 7) >> 3; -} - -// Draw bitmap onto buffer. -static void prv_draw_bitmap(const uint8_t *bitmap, unsigned x_offset, unsigned y_offset, - unsigned width, unsigned height, - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - // Need to convert offsets to bytes for the horizontal dimensions - x_offset = prv_round_to_byte(x_offset); - width = prv_round_to_byte(width); - - for (unsigned i = 0; i < height; i++) { - memcpy(buffer[i + y_offset] + x_offset, bitmap + i * width, width); - } -} - -static void prv_display_buffer(uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - prv_display_start_write(); - for (int i = 0; i < DISP_ROWS; i++) { - prv_display_write_line(i, buffer[i]); - } - prv_display_end_write(); -} - -void display_boot_splash(void) { - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - // Draw black - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(pebble_logo_bits, 16, 64, pebble_logo_width, pebble_logo_height, buffer); - - prv_display_buffer(buffer); -} - -static void prv_set_bit(uint8_t x, uint8_t y, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - buffer[y][x / 8] |= (1 << (x % 8)); -} - -static void prv_render_char(unsigned digit, uint8_t x_offset_bits, uint8_t y_offset, - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - const unsigned char_rows = 18, char_cols = 9; - const uint8_t * char_data = hex_digits_bits[digit]; - - // Each character requires 2 bytes of storage - for (unsigned y = 0; y < char_rows; y++) { - unsigned cur_y = y_offset + y; - uint8_t first_byte = char_data[2 * y]; - - for (unsigned x = 0; x < char_cols; x++) { - bool pixel; - if (x < 8) { // Pixel is in first byte - pixel = first_byte & (1 << x); - } - else { // Last pixel is in second byte - pixel = char_data[2 * y + 1] & 1; - } - - // The buffer is already all black, so just set the white pixels - if (pixel) { - prv_set_bit(x_offset_bits + x, cur_y, buffer); - } - } - } -} - -static void prv_draw_code(uint32_t code, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - const unsigned y_offset = 116; // beneath sad face, above url - unsigned x_offset = 28; // Aligned with sad face - - // Extract and print digits - for (int i = 7; i >= 0; i--) { - // Mask off 4 bits at a time - uint32_t mask = (0xf << (i * 4)); - unsigned digit = ((code & mask) >> (i * 4)); - prv_render_char(digit, x_offset, y_offset, buffer); - - // Each character is 9px wide plus 2px of padding - x_offset += 11; - } -} - -void display_error_code(uint32_t code) { - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(dead_face_bits, 24, 32, dead_face_width, dead_face_height, buffer); - - prv_draw_code(code, buffer); - - prv_draw_bitmap(error_url_bits, 16, 144, error_url_width, error_url_height, buffer); - - prv_display_buffer(buffer); -} - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void) { - prv_display_enter_static(); -} - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator) { - // Dimensions for progress bar - const unsigned x_offset = 24, y_offset = 106, - inner_bar_width = 94, inner_bar_height = 6; - - static unsigned s_prev_num_pixels = -1; - // Calculate number of pixels to fill in - unsigned num_pixels = inner_bar_width * numerator / denominator; - - if (num_pixels == s_prev_num_pixels) { - return; - } - s_prev_num_pixels = num_pixels; - - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(pebble_logo_bits, 16, 64, pebble_logo_width, pebble_logo_height, buffer); - - - prv_draw_bitmap(empty_bar_bits, x_offset, y_offset, empty_bar_width, empty_bar_height, buffer); - - for (unsigned y = 0; y < inner_bar_height; y++) { - for (unsigned x = 0; x < num_pixels; x++) { - // Add 1 to offsets so we don't write into outer box - prv_set_bit(x + x_offset + 1, y_offset + y + 1, buffer); - } - } - - prv_display_buffer(buffer); -} - -void display_init(void) { - prv_enable_display_spi_clock(); - prv_display_start(); - prv_disable_display_spi_clock(); -} diff --git a/platform/silk/boot/src/drivers/exti.h b/platform/silk/boot/src/drivers/exti.h deleted file mode 100644 index 02706d8651..0000000000 --- a/platform/silk/boot/src/drivers/exti.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -typedef enum { - ExtiTrigger_Rising, - ExtiTrigger_Falling, - ExtiTrigger_RisingFalling -} ExtiTrigger; - -//! See section 12.2.5 "External interrupt/event line mapping" in the STM32F2 reference manual -typedef enum { - ExtiLineOther_RTCAlarm = 17, - ExtiLineOther_RTCWakeup = 22 -} ExtiLineOther; - -typedef void (*ExtiHandlerCallback)(void); - -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb); -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger); - -static inline void exti_enable(ExtiConfig config); -static inline void exti_disable(ExtiConfig config); - -void exti_enable_other(ExtiLineOther); -void exti_disable_other(ExtiLineOther); - -#include "exti.inl.h" diff --git a/platform/silk/boot/src/drivers/exti.inl.h b/platform/silk/boot/src/drivers/exti.inl.h deleted file mode 100644 index a16576c7ae..0000000000 --- a/platform/silk/boot/src/drivers/exti.inl.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! @file exti.inl.h -//! -//! Helper functions intended to be inlined into the calling code. - -static inline void exti_enable(ExtiConfig config) { - exti_enable_other(config.exti_line); -} - -static inline void exti_disable(ExtiConfig config) { - exti_disable_other(config.exti_line); -} diff --git a/platform/silk/boot/src/drivers/flash.h b/platform/silk/boot/src/drivers/flash.h deleted file mode 100644 index ccec06292b..0000000000 --- a/platform/silk/boot/src/drivers/flash.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Configure the micro's peripherals to communicate with the flash chip -void flash_init(void); - -//! Read 1 or more bytes starting at the specified 24bit address into -//! the provided buffer. This function does no range checking, so it is -//! currently possible to run off the end of the flash. -//! -//! @param buffer A byte-buffer that will be used to store the data -//! read from flash. -//! @param start_addr The address of the first byte to be read from flash. -//! @param buffer_size The total number of bytes to be read from flash. -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -//! Check if we can talk to the flash. -//! @return true if the CFI table can be queried. -bool flash_sanity_check(void); - -//! Get the checksum of a region of flash -uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t length); diff --git a/platform/silk/boot/src/drivers/flash/flash_crc.c b/platform/silk/boot/src/drivers/flash/flash_crc.c deleted file mode 100644 index cdcb8cf146..0000000000 --- a/platform/silk/boot/src/drivers/flash/flash_crc.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/flash.h" - -#include "util/crc32.h" - -#define CRC_CHUNK_SIZE 1024 - -uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t num_bytes) { - uint8_t buffer[CRC_CHUNK_SIZE]; - - uint32_t crc = CRC32_INIT; - - while (num_bytes > CRC_CHUNK_SIZE) { - flash_read_bytes(buffer, flash_addr, CRC_CHUNK_SIZE); - crc = crc32(crc, buffer, CRC_CHUNK_SIZE); - - num_bytes -= CRC_CHUNK_SIZE; - flash_addr += CRC_CHUNK_SIZE; - } - - flash_read_bytes(buffer, flash_addr, num_bytes); - return crc32(crc, buffer, num_bytes); -} diff --git a/platform/silk/boot/src/drivers/flash/mx25u.c b/platform/silk/boot/src/drivers/flash/mx25u.c deleted file mode 100644 index 0cec57d48d..0000000000 --- a/platform/silk/boot/src/drivers/flash/mx25u.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "board/board.h" -#include "drivers/flash.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "util/delay.h" - -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_qspi.h" - -#define MX25U_FASTREAD_DUMMYCYCLES 4 - -typedef enum MX25UCommand { - // SPI/QSPI Commands - MX25UCommand_FastRead = 0x0B, // FAST_READ - MX25UCommand_QSPIEnable = 0x35, // QPI - MX25UCommand_ResetEnable = 0x66, // RSTEN - MX25UCommand_Reset = 0x99, // RST - - // QSPI only commands - MX25UCommand_QSPI_ID = 0xAF, // QPIID -} MX25UCommand; - -// Helpful Enums -typedef enum { - QSPIFlag_Retain = 0, - QSPIFlag_ClearTC = 1, -} QSPIFlag; - -static void prv_enable_qspi_clock(void) { - periph_config_enable(RCC_AHB3PeriphClockCmd, RCC_AHB3Periph_QSPI); -} - -static void prv_disable_qspi_clock(void) { - periph_config_disable(RCC_AHB3PeriphClockCmd, RCC_AHB3Periph_QSPI); -} - -static void prv_set_num_data_bytes(uint32_t length) { - // From the docs: QSPI_DataLength: Number of data to be retrieved, value+1. - // so 0 is 1 byte, so we substract 1 from the length. -1 is read the entire flash length. - QSPI_SetDataLength(length - 1); -} - -static void prv_wait_for_qspi_transfer_complete(QSPIFlag actions) { - while (QSPI_GetFlagStatus(QSPI_FLAG_TC) == RESET) { } - - if (actions == QSPIFlag_ClearTC) { - QSPI_ClearFlag(QSPI_FLAG_TC); - } -} - -static void prv_wait_for_qspi_not_busy(void) { - while (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) != RESET) { } -} - -static void prv_quad_enable() { - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_1Line; - qspi_com_config.QSPI_ComConfig_Ins = MX25UCommand_QSPIEnable; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - prv_wait_for_qspi_not_busy(); -} - -static void prv_flash_reset(void) { - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_Ins = MX25UCommand_ResetEnable; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_Ins = MX25UCommand_Reset; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - delay_us(12000); // 12ms reset if busy with an erase! - - // Return the flash to Quad SPI mode, all our commands are quad-spi and it'll just cause - // problems/bugs for someone if it comes back in single spi mode - prv_quad_enable(); -} - -static bool prv_flash_check_whoami(void) { - const unsigned int num_whoami_bytes = 3; - - prv_set_num_data_bytes(num_whoami_bytes); - - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Read; - qspi_com_config.QSPI_ComConfig_DMode = QSPI_ComConfig_DMode_4Line; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_Ins = MX25UCommand_QSPI_ID; - QSPI_ComConfig_Init(&qspi_com_config); - - prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC); - - uint32_t read_whoami = 0; - for (unsigned int i = 0; i < num_whoami_bytes; ++i) { - read_whoami |= QSPI_ReceiveData8() << (8 * i); - } - - prv_wait_for_qspi_not_busy(); - - const uint32_t expected_whoami = 0x3725c2; - if (read_whoami == expected_whoami) { - return true; - } else { - return false; - } -} - -void flash_init(void) { - prv_enable_qspi_clock(); - // init GPIOs - for (unsigned i = 0; i < QSpiPinCount; ++i) { - gpio_af_init(&BOARD_CONFIG_FLASH_PINS[i], GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_NOPULL); - } - - // Init QSPI peripheral - QSPI_InitTypeDef qspi_config; - QSPI_StructInit(&qspi_config); - qspi_config.QSPI_SShift = QSPI_SShift_HalfCycleShift; - qspi_config.QSPI_Prescaler = 0x0; - qspi_config.QSPI_CKMode = QSPI_CKMode_Mode0; - qspi_config.QSPI_CSHTime = QSPI_CSHTime_1Cycle; - qspi_config.QSPI_FSize = 22; // 2^23 = 8MB. -> 23 - 1 = 22 - qspi_config.QSPI_FSelect = QSPI_FSelect_1; - qspi_config.QSPI_DFlash = QSPI_DFlash_Disable; - QSPI_Init(&qspi_config); - - QSPI_Cmd(ENABLE); - - // Must call quad_enable first, all commands are QSPI - prv_quad_enable(); - - // Reset the flash to stop any program's or erase in progress from before reboot - prv_flash_reset(); - - prv_disable_qspi_clock(); -} - -bool flash_sanity_check(void) { - prv_enable_qspi_clock(); - - bool result = prv_flash_check_whoami(); - - prv_disable_qspi_clock(); - - return result; -} - -void flash_read_bytes(uint8_t *buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - prv_enable_qspi_clock(); - - prv_set_num_data_bytes(buffer_size); - - QSPI_ComConfig_InitTypeDef qspi_com_config; - QSPI_ComConfig_StructInit(&qspi_com_config); - qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Read; - qspi_com_config.QSPI_ComConfig_DMode = QSPI_ComConfig_DMode_4Line; - qspi_com_config.QSPI_ComConfig_DummyCycles = MX25U_FASTREAD_DUMMYCYCLES; - qspi_com_config.QSPI_ComConfig_ADMode = QSPI_ComConfig_ADMode_4Line; - qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line; - qspi_com_config.QSPI_ComConfig_ADSize = QSPI_ComConfig_ADSize_24bit; - qspi_com_config.QSPI_ComConfig_Ins = MX25UCommand_FastRead; - QSPI_ComConfig_Init(&qspi_com_config); - - QSPI_SetAddress(start_addr); - - uint8_t *write_ptr = buffer_ptr; - for (unsigned i = 0; i < buffer_size; ++i) { - write_ptr[i] = QSPI_ReceiveData8(); - } - - QSPI_ClearFlag(QSPI_FLAG_TC); - prv_wait_for_qspi_not_busy(); - - prv_disable_qspi_clock(); -} diff --git a/platform/silk/boot/src/drivers/gpio.c b/platform/silk/boot/src/drivers/gpio.c deleted file mode 100644 index 73f2a378e0..0000000000 --- a/platform/silk/boot/src/drivers/gpio.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" - -#include - -#define GPIO_EN_MASK ((RCC_AHB1ENR_GPIOHEN << 1) - 1) - -void gpio_enable_all(void) { - RCC->AHB1ENR |= GPIO_EN_MASK; -} - -void gpio_disable_all(void) { - RCC->AHB1ENR &= ~GPIO_EN_MASK; -} - -void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype, GPIOSpeed_TypeDef speed, - GPIOPuPd_TypeDef pupd) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = pupd - }; - - GPIO_PinAFConfig(af_config->gpio, af_config->gpio_pin_source, af_config->gpio_af); - GPIO_Init(af_config->gpio, &init); -} diff --git a/platform/silk/boot/src/drivers/gpio.h b/platform/silk/boot/src/drivers/gpio.h deleted file mode 100644 index 1a5bea576b..0000000000 --- a/platform/silk/boot/src/drivers/gpio.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "stm32f4xx_gpio.h" - -#include "board/board.h" - -void gpio_enable_all(void); -void gpio_disable_all(void); -void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype, GPIOSpeed_TypeDef speed, - GPIOPuPd_TypeDef pupd); diff --git a/platform/silk/boot/src/drivers/i2c.c b/platform/silk/boot/src/drivers/i2c.c deleted file mode 100644 index 52c3bc4d13..0000000000 --- a/platform/silk/boot/src/drivers/i2c.c +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/misc.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" -#include "system/passert.h" -#include "system/logging.h" -#include "util/delay.h" - -#include - -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_i2c.h" - -#define portBASE_TYPE int -#define pdFALSE 0 -#define portEND_SWITCHING_ISR(expr) (void)(expr) - -#define I2C_ERROR_TIMEOUT_MS (1000) -#define I2C_TIMEOUT_ATTEMPTS_MAX (2 * 1000 * 1000) -#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000) -#define I2C_NACK_COUNT_MAX (1000) // MFI NACKs while busy. We delay ~1ms between retries so this is approximately a 1s timeout - -#define I2C_READ_WRITE_BIT (0x01) - -typedef struct I2cTransfer { - uint8_t device_address; - bool read_not_write; //True for read, false for write - uint8_t register_address; - uint8_t size; - uint8_t idx; - uint8_t *data; - enum TransferState { - TRANSFER_STATE_WRITE_ADDRESS_TX, - TRANSFER_STATE_WRITE_REG_ADDRESS, - TRANSFER_STATE_REPEAT_START, - TRANSFER_STATE_WRITE_ADDRESS_RX, - TRANSFER_STATE_WAIT_FOR_DATA, - TRANSFER_STATE_READ_DATA, - TRANSFER_STATE_WRITE_DATA, - TRANSFER_STATE_END_WRITE, - - TRANSFER_STATE_INVALID, - } state; - bool result; - uint16_t nack_count; -}I2cTransfer; - -typedef struct I2cBus{ - I2C_TypeDef *i2c; - uint8_t user_count; - I2cTransfer transfer; - volatile bool busy; -}I2cBus; - -static I2cBus i2c_buses[BOARD_I2C_BUS_COUNT]; - -static uint32_t s_guard_events[] = { - I2C_EVENT_MASTER_MODE_SELECT, - I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, - I2C_EVENT_MASTER_BYTE_TRANSMITTED, - I2C_EVENT_MASTER_MODE_SELECT, - I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, - I2C_EVENT_MASTER_BYTE_RECEIVED, - I2C_EVENT_MASTER_BYTE_TRANSMITTING, - I2C_EVENT_MASTER_BYTE_TRANSMITTED, -}; - -static bool s_initialized = false; - -/*----------------SEMAPHORE/LOCKING FUNCTIONS--------------------------*/ - -static void bus_lock(I2cBus *bus) { -} - -static void bus_unlock(I2cBus *bus) { -} - -static bool semaphore_take(I2cBus *bus) { - return true; -} - -static bool semaphore_wait(I2cBus *bus) { - bus->busy = true; - volatile uint32_t timeout_attempts = I2C_TIMEOUT_ATTEMPTS_MAX; - while ((timeout_attempts-- > 0) && (bus->busy)); - bus->busy = false; - return (timeout_attempts != 0); -} - -static void semaphore_give(I2cBus *bus) { -} - -/*-------------------BUS/PIN CONFIG FUNCTIONS--------------------------*/ - -//! Configure bus power supply control pin as output -//! Lock bus and peripheral config access before configuring pins -void i2c_bus_rail_ctl_config(OutputConfig pin_config) { - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = pin_config.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &GPIO_InitStructure); -} - -//! Configure bus pins for use by I2C peripheral -//! Lock bus and peripheral config access before configuring pins -static void bus_pin_cfg_i2c(AfConfig pin_config) { - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = pin_config.gpio_pin; - gpio_init_struct.GPIO_Mode = GPIO_Mode_AF; - gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz; - gpio_init_struct.GPIO_OType = GPIO_OType_OD; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &gpio_init_struct); - - GPIO_PinAFConfig(pin_config.gpio, pin_config.gpio_pin_source, pin_config.gpio_af); -} - -//! Configure bus pin as input -//! Lock bus and peripheral config access before use -static void bus_pin_cfg_input(AfConfig pin_config) { - // Configure pin as high impedance input - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = pin_config.gpio_pin; - gpio_init_struct.GPIO_Mode = GPIO_Mode_IN; - gpio_init_struct.GPIO_Speed = GPIO_Speed_2MHz; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &gpio_init_struct); -} - -//! Configure bus pin as output -//! Lock bus and peripheral config access before use -static void bus_pin_cfg_output(AfConfig pin_config, bool pin_state) { - // Configure pin as output - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = pin_config.gpio_pin; - gpio_init_struct.GPIO_Mode = GPIO_Mode_OUT; - gpio_init_struct.GPIO_Speed = GPIO_Speed_2MHz; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &gpio_init_struct); - - // Set bit high or low - GPIO_WriteBit(pin_config.gpio, pin_config.gpio_pin, (pin_state) ? Bit_SET : Bit_RESET); -} - -//! Power down I2C bus power supply -//! Always lock bus and peripheral config access before use -static void bus_rail_power_down(uint8_t bus_idx) { - if (BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn == NULL) { - return; - } - - BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn(false); - - // Drain through pull-ups - bus_pin_cfg_output(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl, false); - bus_pin_cfg_output(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda, false); -} - -//! Power up I2C bus power supply -//! Always lock bus and peripheral config access before use -static void bus_rail_power_up(uint8_t bus_idx) { - if (BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn == NULL) { - return; - } - - // check that at least enough time has elapsed since the last turn-off - // TODO: is this necessary in bootloader? - static const uint32_t MIN_STOP_TIME_MS = 10; - delay_ms(MIN_STOP_TIME_MS); - - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl); - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda); - - BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn(true); -} - -//! Initialize the I2C peripheral -//! Lock the bus and peripheral config access before initialization -static void bus_init(uint8_t bus_idx) { - // Initialize peripheral - I2C_InitTypeDef i2c_init_struct; - I2C_StructInit(&i2c_init_struct); - - if (BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_speed > I2C_NORMAL_MODE_CLOCK_SPEED_MAX) { //Fast mode - i2c_init_struct.I2C_DutyCycle = BOARD_CONFIG.i2c_bus_configs[bus_idx].duty_cycle; - } - i2c_init_struct.I2C_ClockSpeed = BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_speed; - i2c_init_struct.I2C_Ack = I2C_Ack_Enable; - - I2C_Init(i2c_buses[bus_idx].i2c, &i2c_init_struct); - I2C_Cmd(i2c_buses[bus_idx].i2c, ENABLE); -} - -//! Configure the bus pins, enable the peripheral clock and initialize the I2C peripheral. -//! Always lock the bus and peripheral config access before enabling it -static void bus_enable(uint8_t bus_idx) { - // Don't power up rail if the bus is already in use (enable can be called to reset bus) - if (i2c_buses[bus_idx].user_count == 0) { - bus_rail_power_up(bus_idx); - } - - bus_pin_cfg_i2c(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl); - bus_pin_cfg_i2c(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda); - - // Enable peripheral clock - periph_config_enable(RCC_APB1PeriphClockCmd, BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_ctrl); - - bus_init(bus_idx); -} - -//! De-initialize and gate the clock to the peripheral -//! Power down rail if the bus supports that and no devices are using it -//! Always lock the bus and peripheral config access before disabling it -static void bus_disable(uint8_t bus_idx) { - I2C_DeInit(i2c_buses[bus_idx].i2c); - - periph_config_disable(RCC_APB1PeriphClockCmd, BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_ctrl); - - // Do not de-power rail if there are still devices using bus (just reset peripheral and pin configuration during a bus reset) - if (i2c_buses[bus_idx].user_count == 0) { - bus_rail_power_down(bus_idx); - } - else { - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl); - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda); - } -} - -//! Perform a soft reset of the bus -//! Always lock the bus before reset -static void bus_reset(uint8_t bus_idx) { - bus_disable(bus_idx); - bus_enable(bus_idx); -} - -/*---------------INIT/USE/RELEASE/RESET FUNCTIONS----------------------*/ - -void i2c_init(void) { - for (uint32_t i = 0; i < ARRAY_LENGTH(i2c_buses); i++) { - i2c_buses[i].i2c = BOARD_CONFIG.i2c_bus_configs[i].i2c; - i2c_buses[i].user_count = 0; - i2c_buses[i].busy = false; - i2c_buses[i].transfer.idx = 0; - i2c_buses[i].transfer.size = 0; - i2c_buses[i].transfer.data = NULL; - i2c_buses[i].transfer.state = TRANSFER_STATE_INVALID; - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = BOARD_CONFIG.i2c_bus_configs[i].ev_irq_channel; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0c; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - NVIC_InitStructure.NVIC_IRQChannel = BOARD_CONFIG.i2c_bus_configs[i].er_irq_channel; - NVIC_Init(&NVIC_InitStructure); - - I2C_DeInit(i2c_buses[i].i2c); - } - - s_initialized = true; - - for (uint32_t i = 0; i < ARRAY_LENGTH(i2c_buses); i++) { - if (BOARD_CONFIG.i2c_bus_configs[i].rail_cfg_fn) { - BOARD_CONFIG.i2c_bus_configs[i].rail_cfg_fn(); - } - if (BOARD_CONFIG.i2c_bus_configs[i].rail_ctl_fn) { - bus_rail_power_down(i); - } - } -} - -void i2c_use(I2cDevice device_id) { - PBL_ASSERTN(s_initialized); - PBL_ASSERT(device_id < BOARD_CONFIG.i2c_device_count, "I2C device ID out of bounds %d (max: %d)", - device_id, BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - bus_lock(bus); - - if (bus->user_count == 0) { - bus_enable(bus_idx); - } - bus->user_count++; - - bus_unlock(bus); -} - -void i2c_release(I2cDevice device_id) { - PBL_ASSERTN(s_initialized); - PBL_ASSERTN(device_id < BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - bus_lock(bus); - - if (bus->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted release of disabled bus %d by device %d", bus_idx, device_id); - bus_unlock(bus); - return; - } - - bus->user_count--; - if (bus->user_count == 0) { - bus_disable(bus_idx); - } - - bus_unlock(bus); -} - -void i2c_reset(I2cDevice device_id) { - PBL_ASSERTN(s_initialized); - PBL_ASSERTN(device_id < BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - // Take control of bus; only one task may use bus at a time - bus_lock(bus); - - if (bus->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted reset of disabled bus %d by device %d", bus_idx, device_id); - bus_unlock(bus); - return; - } - - PBL_LOG(LOG_LEVEL_WARNING, "Resetting I2C bus %" PRId8, bus_idx); - - // decrement user count for reset so that if this user is the only user, the - // bus will be powered down during the reset - bus->user_count--; - - // Reset and reconfigure bus and pins - bus_reset(bus_idx); - - //Restore user count - bus->user_count++; - - bus_unlock(bus); -} - -/*--------------------DATA TRANSFER FUNCTIONS--------------------------*/ - -//! Wait a short amount of time for busy bit to clear -static bool wait_for_busy_clear(uint8_t bus_idx) { - unsigned int attempts = I2C_TIMEOUT_ATTEMPTS_MAX; - while((i2c_buses[bus_idx].i2c->SR2 & I2C_SR2_BUSY) != 0) { - --attempts; - if (!attempts) { - return false; - } - } - - return true; -} - -//! Abort the transfer -//! Should only be called when the bus is locked -static void abort_transfer(I2cBus *bus) { - // Disable all interrupts on the bus - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); - // Generate a stop condition - bus->i2c->CR1 |= I2C_CR1_STOP; - bus->transfer.state = TRANSFER_STATE_INVALID; -} - -//! Set up and start a transfer to a device, wait for it to finish and clean up after the transfer has completed -static bool do_transfer(I2cDevice device_id, bool read_not_write, uint8_t device_address, uint8_t register_address, uint8_t size, uint8_t *data) { - PBL_ASSERTN(s_initialized); - PBL_ASSERTN(device_id < BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - // Take control of bus; only one task may use bus at a time - bus_lock(bus); - - if (bus->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted access to disabled bus %d by device %d", bus_idx, device_id); - bus_unlock(bus); - return false; - } - - // If bus is busy (it shouldn't be as this function waits for the bus to report a non-idle state - // before exiting) reset the bus and wait for it to become not-busy - // Exit if bus remains busy. User module should reset the I2C module at this point - if((bus->i2c->SR2 & I2C_SR2_BUSY) != 0) { - bus_reset(bus_idx); - - if (!wait_for_busy_clear(bus_idx)) { - // Bus did not recover after reset - bus_unlock(bus); - return false; - } - } - - // Take binary semaphore so that next take will block - PBL_ASSERT(semaphore_take(bus), "Could not acquire semaphore token"); - - // Set up transfer - bus->transfer.device_address = device_address; - bus->transfer.register_address = register_address; - bus->transfer.read_not_write = read_not_write; - bus->transfer.size = size; - bus->transfer.idx = 0; - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_TX; - bus->transfer.data = data; - bus->transfer.nack_count = 0; - - // Ack received bytes - I2C_AcknowledgeConfig(bus->i2c, ENABLE); - - bool result = false; - - do { - // Generate start event - bus->i2c->CR1 |= I2C_CR1_START; - //Enable event and error interrupts - bus->i2c->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN; - - // Wait on semaphore until it is released by interrupt or a timeout occurs - if (semaphore_wait(bus)) { - - if (bus->transfer.state == TRANSFER_STATE_INVALID) { - // Transfer is complete - result = bus->transfer.result; - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error on bus %" PRId8, bus_idx); - } - - } else if (bus->transfer.nack_count < I2C_NACK_COUNT_MAX) { - // NACK received after start condition sent: the MFI chip NACKs start conditions whilst it is busy - // Retry start condition after a short delay. - // A NACK count is incremented for each NACK received, so that legitimate NACK - // errors cause the transfer to be aborted (after the NACK count max has been reached). - - bus->transfer.nack_count++; - - delay_ms(1); - - } else { - // Too many NACKs received, abort transfer - abort_transfer(bus); - break; - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error: too many NACKs received on bus %" PRId8, bus_idx); - } - - } else { - // Timeout, abort transfer - abort_transfer(bus); - break; - PBL_LOG(LOG_LEVEL_ERROR, "Transfer timed out on bus %" PRId8, bus_idx); - } - } while (bus->transfer.state != TRANSFER_STATE_INVALID); - - // Return semaphore token so another transfer can be started - semaphore_give(bus); - - // Wait for bus to to clear the busy flag before a new transfer starts - // Theoretically a transfer could complete successfully, but the busy flag never clears, - // which would cause the next transfer to fail - if (!wait_for_busy_clear(bus_idx)) { - // Reset I2C bus if busy flag does not clear - bus_reset(bus_idx); - } - - bus_unlock(bus); - - return result; -} - -bool i2c_read_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, uint8_t *result) { - return i2c_read_register_block(device_id, i2c_device_address, register_address, 1, result); -} - -bool i2c_read_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t - register_address_start, uint8_t read_size, uint8_t* result_buffer) { - // Do transfer locks the bus - bool result = do_transfer(device_id, true, i2c_device_address, register_address_start, read_size, result_buffer); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Read failed on bus %" PRId8, BOARD_CONFIG.i2c_device_map[device_id]); - } - - return result; -} - -bool i2c_write_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, - uint8_t value) { - return i2c_write_register_block(device_id, i2c_device_address, register_address, 1, &value); -} - -bool i2c_write_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t - register_address_start, uint8_t write_size, const uint8_t* buffer) { - // Do transfer locks the bus - bool result = do_transfer(device_id, false, i2c_device_address, register_address_start, write_size, (uint8_t*)buffer); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Write failed on bus %" PRId8, BOARD_CONFIG.i2c_device_map[device_id]); - } - - return result; -} - -/*------------------------INTERRUPT FUNCTIONS--------------------------*/ - -//! End a transfer and disable further interrupts -//! Only call from interrupt functions -static portBASE_TYPE end_transfer_irq(I2cBus *bus, bool result) { - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); - bus->i2c->CR1 |= I2C_CR1_STOP; - bus->transfer.result = result; - bus->transfer.state = TRANSFER_STATE_INVALID; - - bus->busy = false; - return pdFALSE; -} - -//! Pause a transfer, disabling interrupts during the pause -//! Only call from interrupt functions -static portBASE_TYPE pause_transfer_irq(I2cBus *bus) { - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); - bus->busy = false; - return pdFALSE; -} - -//! Handle an IRQ event on the specified \a bus -static portBASE_TYPE irq_event_handler(I2cBus *bus) { - if (bus->transfer.state == TRANSFER_STATE_INVALID) { - - // Disable interrupts if spurious interrupt received - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN); - return pdFALSE; - } - - // Check that the expected event occurred - if (I2C_CheckEvent(bus->i2c, s_guard_events[bus->transfer.state]) == ERROR) { - // Ignore interrupt - A spurious byte transmitted event as well as an interrupt with no - // discernible event associated with it occur after repeat start events are generated - return pdFALSE; - } - portBASE_TYPE should_context_switch = pdFALSE; - - switch (bus->transfer.state) { - case TRANSFER_STATE_WRITE_ADDRESS_TX: - // Write the i2c device address to the bus to select it in write mode. - bus->i2c->DR = bus->transfer.device_address & ~I2C_READ_WRITE_BIT; - bus->transfer.state = TRANSFER_STATE_WRITE_REG_ADDRESS; - break; - - case TRANSFER_STATE_WRITE_REG_ADDRESS: - // Write the register address - bus->i2c->DR = bus->transfer.register_address; - - if (bus->transfer.read_not_write) { - bus->transfer.state = TRANSFER_STATE_REPEAT_START; - } else { - // Enable TXE interrupt for writing - bus->i2c->CR2 |= I2C_CR2_ITBUFEN; - bus->transfer.state = TRANSFER_STATE_WRITE_DATA; - } - break; - - case TRANSFER_STATE_REPEAT_START: - // Generate a repeat start - bus->i2c->CR1 |= I2C_CR1_START; - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_RX; - break; - - case TRANSFER_STATE_WRITE_ADDRESS_RX: - // Write the I2C device address again, but this time in read mode. - bus->i2c->DR = bus->transfer.device_address | I2C_READ_WRITE_BIT; - if (bus->transfer.size == 1) { - // Last byte, we want to NACK this one to tell the slave to stop sending us bytes. - bus->i2c->CR1 &= ~I2C_CR1_ACK; - } - bus->transfer.state = TRANSFER_STATE_WAIT_FOR_DATA; - break; - - case TRANSFER_STATE_WAIT_FOR_DATA: - //This state just ensures that the transition to receive mode event happened - - // Enable RXNE interrupt for writing - bus->i2c->CR2 |= I2C_CR2_ITBUFEN; - bus->transfer.state = TRANSFER_STATE_READ_DATA; - break; - - case TRANSFER_STATE_READ_DATA: - bus->transfer.data[bus->transfer.idx] = bus->i2c->DR; - bus->transfer.idx++; - - if (bus->transfer.idx + 1 == bus->transfer.size) { - // Last byte, we want to NACK this one to tell the slave to stop sending us bytes. - bus->i2c->CR1 &= ~I2C_CR1_ACK; - } - else if (bus->transfer.idx == bus->transfer.size) { - // End transfer after all bytes have been received - bus->i2c->CR2 &= ~I2C_CR2_ITBUFEN; - should_context_switch = end_transfer_irq(bus, true); - break; - } - - break; - - case TRANSFER_STATE_WRITE_DATA: - bus->i2c->DR = bus->transfer.data[bus->transfer.idx]; - bus->transfer.idx++; - if (bus->transfer.idx == bus->transfer.size) { - bus->i2c->CR2 &= ~I2C_CR2_ITBUFEN; - bus->transfer.state = TRANSFER_STATE_END_WRITE; - break; - } - break; - - case TRANSFER_STATE_END_WRITE: - // End transfer after all bytes have been sent - should_context_switch = end_transfer_irq(bus, true); - break; - - default: - // Abort transfer from invalid state - should never reach here (state machine logic broken) - should_context_switch = end_transfer_irq(bus, false); - break; - } - - return should_context_switch; -} - -//! Handle error interrupt on the specified \a bus -static portBASE_TYPE irq_error_handler(I2cBus *bus) { - if (bus->transfer.state == TRANSFER_STATE_INVALID) { - - // Disable interrupts if spurious interrupt received - bus->i2c->CR2 &= ~I2C_CR2_ITERREN; - return pdFALSE; - } - - // Data overrun and bus errors can only really be handled by terminating the transfer and - // trying to recover bus to an idle state. Each error will be logged. In each case a stop - // condition will be sent and then we will wait on the busy flag to clear (if it doesn't, - // a soft reset of the bus will be performed (handled in wait i2c_do_transfer). - - if ((bus->i2c->SR1 & I2C_SR1_OVR) != 0) { - bus->i2c->SR1 &= ~I2C_SR1_OVR; - - // Data overrun - PBL_LOG(LOG_LEVEL_ERROR, "Data overrun during I2C transaction; Bus: 0x%p", bus->i2c); - } - if ((bus->i2c->SR1 & I2C_SR1_BERR) != 0) { - bus->i2c->SR1 &= ~I2C_SR1_BERR; - - // Bus error: invalid start or stop condition detected - PBL_LOG(LOG_LEVEL_ERROR, "Bus error detected during I2C transaction; Bus: 0x%p", bus->i2c); - } - if ((bus->i2c->SR1 & I2C_SR1_AF) != 0) { - bus->i2c->SR1 &= ~I2C_SR1_AF; - - // NACK received. - // - // The MFI chip will cause NACK errors during read operations after writing a start bit (first start - // or repeat start indicating that it is busy. The transfer must be paused and the start condition sent - // again after a delay and the state machine set back a step. - // - // If the NACK is received after any other action log an error and abort the transfer - - - if (bus->transfer.state == TRANSFER_STATE_WAIT_FOR_DATA) { - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_RX; - return pause_transfer_irq(bus); - } - else if (bus->transfer.state == TRANSFER_STATE_WRITE_REG_ADDRESS){ - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_TX; - return pause_transfer_irq(bus); - } - else { - PBL_LOG(LOG_LEVEL_ERROR, "NACK received during I2C transfer; Bus: 0x%p", bus->i2c); - } - } - - return end_transfer_irq(bus, false); - -} - -void I2C1_EV_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_event_handler(&i2c_buses[0])); -} - -void I2C1_ER_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_error_handler(&i2c_buses[0])); -} - -void I2C2_EV_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_event_handler(&i2c_buses[1])); -} - -void I2C2_ER_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_error_handler(&i2c_buses[1])); -} - -void I2C3_EV_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_event_handler(&i2c_buses[0])); -} - -void I2C3_ER_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_error_handler(&i2c_buses[0])); -} - -/*------------------------COMMAND FUNCTIONS--------------------------*/ - -void command_power_2v5(char *arg) { - // Intentionally ignore the s_running_count and make it so! - // This is intended for low level electrical test only - if(!strcmp("on", arg)) { - bus_rail_power_up(1); - } else { - bus_rail_power_down(1); - } -} diff --git a/platform/silk/boot/src/drivers/i2c.h b/platform/silk/boot/src/drivers/i2c.h deleted file mode 100644 index ebbfefd9ae..0000000000 --- a/platform/silk/boot/src/drivers/i2c.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include -#include - -//! Initialize the I2C driver. Must be called before first use -void i2c_init(void); - -//! Start using the I2C bus connected to the device specified by \a device_id -//! Must be called before any other use of the bus is performed -//! @param device_id ID of device -void i2c_use(I2cDevice device_id); - -//! Stop using the I2C bus connected to the device specified by \a device_id -//! Call when done with the bus -//! @param device_id ID of device -void i2c_release(I2cDevice device_id); - -//! Reset the bus -//! Will re-initialize the bus and cycle the power to the bus if this is -//! supported for the bus the device specified by \a device_id is connected to) -//! @param device_id ID of device, this will identify the bus to be reset -void i2c_reset(I2cDevice device_id); - -//! Manually bang out the clock on the bus specified by \a device_id for a period -//! of time or until the data line recovers -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device, this will identify the bus to be recovered -//! @return true if the data line recovered, false otherwise -bool i2c_bitbang_recovery(I2cDevice device_id); - -//! Read the value of a register -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address Address of register to read -//! @param result Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, uint8_t *result); - -//! Read a sequence of registers starting from \a register_address_start -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address_start Address of first register to read -//! @param read_size Number of bytes to read -//! @param result_buffer Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address_start, uint8_t read_size, uint8_t* result_buffer); - -//! Write to a register -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address Address of register to write to -//! @param value Data value to write -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, uint8_t value); - -//! Write to a sequence of registers starting from \a register_address_start -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address_start Address of first register to read -//! @param write_size Number of bytes to write -//! @param buffer Pointer to source buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address_start, uint8_t write_size, const uint8_t* buffer); diff --git a/platform/silk/boot/src/drivers/i2c_private.c b/platform/silk/boot/src/drivers/i2c_private.c deleted file mode 100644 index 114ab7e7e8..0000000000 --- a/platform/silk/boot/src/drivers/i2c_private.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "drivers/pmic.h" -#include "drivers/gpio.h" -#include "util/delay.h" -#include "board/board.h" - -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_i2c.h" - -void silk_rail_ctl_fn(bool enable) { - // NYI - return; -} - -void silk_rail_cfg_fn(void) { - // NYI - return; -} diff --git a/platform/silk/boot/src/drivers/periph_config.h b/platform/silk/boot/src/drivers/periph_config.h deleted file mode 100644 index 06a7123a79..0000000000 --- a/platform/silk/boot/src/drivers/periph_config.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "stm32f4xx.h" - -typedef void (*ClockCmd)(uint32_t periph, FunctionalState state); - -static inline void periph_config_enable(ClockCmd clock_cmd, uint32_t periph) { - clock_cmd(periph, ENABLE); -} - -static inline void periph_config_disable(ClockCmd clock_cmd, uint32_t periph) { - clock_cmd(periph, DISABLE); -} diff --git a/platform/silk/boot/src/drivers/pmic.h b/platform/silk/boot/src/drivers/pmic.h deleted file mode 100644 index 6395913dde..0000000000 --- a/platform/silk/boot/src/drivers/pmic.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Initialize the PMIC driver. Call this once at startup. -bool pmic_init(void); - -//! Whether or not the usb charger is detected by the pmic. -bool pmic_is_usb_connected(void); - -//! Returns true if this boot was caused by a charger event from standby, but -//! there is no charger connected. -bool pmic_boot_due_to_charger_disconnect(void); - -//! Enter what we consider to be a "powered off" state, which is the as3701 -//! standby state, where we keep the RTC powered. -bool pmic_power_off(void); - -//! Fully shut down the as3701, which does not power the RTC. -bool pmic_full_power_off(void); diff --git a/platform/silk/boot/src/drivers/pmic/as3701b.c b/platform/silk/boot/src/drivers/pmic/as3701b.c deleted file mode 100644 index ce65d03a6a..0000000000 --- a/platform/silk/boot/src/drivers/pmic/as3701b.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/pmic.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "system/passert.h" - -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_adc.h" - -typedef enum { - PmicRegisters_SD1_VOLTAGE = 0x01, - PmicRegisters_LDO1_VOLTAGE = 0x02, - PmicRegisters_LDO2_VOLTAGE = 0x03, - - PmicRegisters_GPIO1_CNTL = 0x09, - PmicRegisters_GPIO2_CNTL = 0x0a, - PmicRegisters_GPIO3_CNTL = 0x0b, - PmicRegisters_GPIO4_CNTL = 0x0c, - PmicRegisters_GPIO5_CNTL = 0x0d, - PmicRegisters_GPIO_SIG_OUT = 0x20, - PmicRegisters_GPIO_SIG_IN = 0x21, - - PmicRegisters_REG1_VOLTAGE = 0x22, - PmicRegisters_REG2_VOLTAGE = 0x23, - PmicRegisters_REG_CNTL = 0x24, - - PmicRegisters_GPIO_CNTL1 = 0x25, - PmicRegisters_GPIO_CNTL2 = 0x26, - PmicRegisters_SD_CNTL1 = 0x30, - - PmicRegisters_BATT_VOLTAGE_MON = 0x32, - PmicRegisters_STARTUP_CNTL = 0x33, - PmicRegisters_REFERENCE_CNTL = 0x35, - PmicRegisters_RESET_CNTL = 0x36, - PmicRegisters_OVERTEMP_CNTL = 0x37, - PmicRegisters_REG_STANDBY_MOD1 = 0x39, - - PmicRegisters_PWM_CNTL_L = 0x41, - PmicRegisters_PWM_CNTL_H = 0x42, - - PmicRegisters_CURR1_VAL = 0x43, - PmicRegisters_CURR2_VAL = 0x44, - - PmicRegisters_REG_STATUS = 0x73, - PmicRegisters_INT_MASK_1 = 0x74, - PmicRegisters_INT_MASK_2 = 0x75, - PmicRegisters_INT_STATUS_1 = 0x77, - PmicRegisters_INT_STATUS_2 = 0x78, - PmicRegisters_CHARGE_CNTL = 0x80, - PmicRegisters_CHARGE_VOLTAGE_CNTL = 0x81, - PmicRegisters_CHARGE_CURRENT_CNTL = 0x82, - PmicRegisters_CHARGE_CONFIG_1 = 0x83, - PmicRegisters_CHARGE_CONFIG_2 = 0x84, - PmicRegisters_CHARGE_SUPERVISION = 0x85, - PmicRegisters_CHARGE_STATUS_1 = 0x86, - PmicRegisters_CHARGE_STATUS_2 = 0x87, - - PmicRegisters_LOCK_REG = 0x8e, - - PmicRegisters_CHIP_ID = 0x90, - PmicRegisters_CHIP_REV = 0x91, - - PmicRegisters_FUSE_5 = 0xa5, - PmicRegisters_FUSE_6 = 0xa6, - PmicRegisters_FUSE_7 = 0xa7, - PmicRegisters_FUSE_8 = 0xa8, - PmicRegisters_FUSE_9 = 0xa9, - PmicRegisters_FUSE_10 = 0xaa, - PmicRegisters_FUSE_11 = 0xab, - PmicRegisters_FUSE_12 = 0xac, - PmicRegisters_FUSE_13 = 0xad, - PmicRegisters_FUSE_14 = 0xae, - PmicRegisters_FUSE_15 = 0xaf, -} PmicRegisters; - -// These are values for the reset_reason field of the ResetControl register. -// None of these values should ever be changed, as conversions are done on -// readings dont directly out of the ResetControl register. -// See Figure 79 of the AS3701B datasheet for more information. -typedef enum { - PmicResetReason_PowerUpFromScratch = 0x00, //!< Battery or charger insertion from scratch - PmicResetReason_ResVoltFall = 0x01, //!< Battery voltage drop below 2.75V - - PmicResetReason_ForcedReset = 0x02, //!< sw force_reset - - PmicResetReason_OnPulledHigh = 0x03, //!< Force sw power_off, ON pulled high - PmicResetReason_Charger = 0x04, //!< Forced sw power_off, Charger detected. - - PmicResetReason_XRES = 0x05, //!< External trigger through XRES - PmicResetReason_OverTemperature = 0x06, //!< Reset caused by overtemperature - - PmicResetReason_OnKeyHold = 0x08, //!< Reset for holding down on key - - PmicResetReason_StandbyInterrupt = 0x0b, //!< Reset for interrupt in standby - PmicResetReason_StandbyOnPulledHigh = 0x0c, //!< Reset for ON pulled high in standby - - PmicResetReason_Unknown, -} PmicResetReason; - -static void prv_start_120hz_clock(void); - -static bool prv_init_gpio(void) { - return true; -} - -//! Interrupt masks for InterruptStatus1 and InterruptMask1 registers -enum PmicInt1 { - PmicInt1_Trickle = (1 << 0), //!< Trickle charge - PmicInt1_NoBat = (1 << 1), //!< Battery detached - PmicInt1_Resume = (1 << 2), //!< Resuming charge on drop after full - PmicInt1_EOC = (1 << 3), //!< End of charge - PmicInt1_ChDet = (1 << 4), //!< Charger detected - PmicInt1_OnKey = (1 << 5), //!< On Key held - PmicInt1_OvTemp = (1 << 6), //!< Set when 110deg is exceeded - PmicInt1_LowBat = (1 << 7), //!< Low Battery detected. Set when BSUP drops below ResVoltFall -}; - -enum PmicRail { - PmicRail_SD1, //!< 1.8V - PmicRail_LDO1, //!< 3.0V - PmicRail_LDO2, //!< 2.0V -}; - -#define AS3701B_CHIP_ID 0x11 -#define AS3701B_WRITE_ADDR 0x80 -#define AS3701B_READ_ADDR 0x81 - -static bool prv_read_register(uint8_t register_address, uint8_t *result) { - i2c_use(I2C_DEVICE_AS3701B); - bool rv = i2c_read_register(I2C_DEVICE_AS3701B, AS3701B_READ_ADDR, register_address, result); - i2c_release(I2C_DEVICE_AS3701B); - return rv; -} - -static bool prv_write_register(uint8_t register_address, uint8_t value) { - i2c_use(I2C_DEVICE_AS3701B); - bool rv = i2c_write_register(I2C_DEVICE_AS3701B, AS3701B_WRITE_ADDR, register_address, value); - i2c_release(I2C_DEVICE_AS3701B); - return rv; -} - -static bool prv_register_set_bit(uint8_t register_address, uint8_t bit) { - uint8_t val; - if (!prv_read_register(register_address, &val)) { - return false; - } - - val |= (1 << bit); - return prv_write_register(register_address, val); -} - -static bool prv_register_clear_bit(uint8_t register_address, uint8_t bit) { - uint8_t val; - if (!prv_read_register(register_address, &val)) { - return false; - } - - val &= ~(1 << bit); - return prv_write_register(register_address, val); -} - -// Read the interrupt status registers to clear pending bits. -static void prv_clear_pending_interrupts(void) { - uint8_t throwaway_read_result; - prv_read_register(PmicRegisters_INT_STATUS_1, &throwaway_read_result); - prv_read_register(PmicRegisters_INT_STATUS_2, &throwaway_read_result); -} - -// Set up 120Hz clock which is used for VCOM. -// Slowest possible setting, with divisor of 16 and high/low duration of 256us. -void prv_start_120hz_clock(void) { - const uint8_t pwm_high_low_time_us = (256 - 1); - prv_write_register(PmicRegisters_PWM_CNTL_H, pwm_high_low_time_us); - prv_write_register(PmicRegisters_PWM_CNTL_L, pwm_high_low_time_us); - - bool success = false; - uint8_t ref_cntl; - if (prv_read_register(PmicRegisters_REFERENCE_CNTL, &ref_cntl)) { - ref_cntl |= 0x3; // Divisor of 16 - prv_write_register(PmicRegisters_REFERENCE_CNTL, ref_cntl); - - // Enable PWM Output on GPIO2 (Fig. 64) - // Bits 6-4: Mode, 0x1 = Output - // Bits 0-3: iosf, 0xe = PWM - uint8_t val = (1 << 4) | 0x0e; - success = prv_write_register(PmicRegisters_GPIO2_CNTL, val); - } - PBL_ASSERT(success, "Failed to start PMIC 120Hz PWM"); -} - -static bool prv_is_alive(void) { - uint8_t chip_id; - if (!prv_read_register(PmicRegisters_CHIP_ID, &chip_id)) { - return false; - } - const bool found = (chip_id == AS3701B_CHIP_ID); - if (found) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found the as3701b"); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Error: read as3701b whoami byte 0x%x, expecting 0x11", chip_id); - } - return found; -} - -bool pmic_init(void) { - prv_init_gpio(); - if (!prv_is_alive()) { - return false; - } - prv_start_120hz_clock(); - return true; -} - -bool pmic_is_usb_connected(void) { - uint8_t status; - if (!prv_read_register(PmicRegisters_CHARGE_STATUS_2, &status)) { - return false; - } - // ChargerStatus2 (Fig. 98) - // Bit 2: Charger detected - return !!(status & (1 << 2)); -} - -static PmicResetReason prv_reset_reason(void) { - uint8_t val; - if (!prv_read_register(PmicRegisters_RESET_CNTL, &val)) { - return PmicResetReason_Unknown; - } - return (val & 0xf0) >> 4; -} - -// If the pmic indicates that we were reset due to a charger interrupt, but the -// charger is currently disconnected, then we know we were woken by a disconnect event. -bool pmic_boot_due_to_charger_disconnect(void) { - if (prv_reset_reason() != PmicResetReason_StandbyInterrupt) { - return false; - } - - uint8_t int_status; - if (!prv_read_register(PmicRegisters_INT_STATUS_1, &int_status)) { - return false; - } - - return ((int_status & PmicInt1_ChDet) && !pmic_is_usb_connected()); -} - -// This is a hard power off, resulting in all rails being disabled. -bool pmic_full_power_off(void) { - // ResetControl (Fig. 79) - // Bit 1: power_off - Start a reset cycle, and wait for ON or charger to complete the reset. - if (prv_register_set_bit(PmicRegisters_RESET_CNTL, 1)) { - while (1) {} - __builtin_unreachable(); - } - return false; -} - - -// On the as3701b, a power_off will cut power to all rails. We want to keep the -// RTC alive, so rather than performing a sw_power_off, enter the pmic's standby -// mode, powering down all but LDO2. -bool pmic_power_off(void) { - // Only enable interrupts that should be able to wake us out of standby - // - Wake on charger detect - const uint8_t int_mask = ~((uint8_t)PmicInt1_ChDet); - prv_write_register(PmicRegisters_INT_MASK_1, int_mask); - prv_write_register(PmicRegisters_INT_MASK_2, 0xff); - - // Clear interrupt status so we're not woken immediately (read the regs) - prv_clear_pending_interrupts(); - - // Set Reg_Standby_mod1 to specify which rails to turn off / keep on - // - SD1, LDO1 off - // - LDO2 on - // - Disable regulator pulldowns - prv_write_register(PmicRegisters_REG_STANDBY_MOD1, 0xa); - - // Set standby_mode_on (bit 4) in ReferenceControl to 1 (See Fig. 78) - if (prv_register_set_bit(PmicRegisters_REFERENCE_CNTL, 4)) { - while (1) {} - __builtin_unreachable(); - } - return false; -} - -void set_ldo3_power_state(bool enabled) { -} - -void set_4V5_power_state(bool enabled) { -} - -void set_6V6_power_state(bool enabled) { -} diff --git a/platform/silk/boot/src/drivers/system_flash.c b/platform/silk/boot/src/drivers/system_flash.c deleted file mode 100644 index 8e88d74436..0000000000 --- a/platform/silk/boot/src/drivers/system_flash.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/system_flash.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#include "stm32f4xx_flash.h" - -static uint16_t s_sectors[] = { - FLASH_Sector_0, FLASH_Sector_1, FLASH_Sector_2, FLASH_Sector_3, - FLASH_Sector_4, FLASH_Sector_5, FLASH_Sector_6, FLASH_Sector_7, - FLASH_Sector_8, FLASH_Sector_9, FLASH_Sector_10, FLASH_Sector_11 -}; -static uint32_t s_sector_addresses[] = { - ADDR_FLASH_SECTOR_0, ADDR_FLASH_SECTOR_1, ADDR_FLASH_SECTOR_2, ADDR_FLASH_SECTOR_3, - ADDR_FLASH_SECTOR_4, ADDR_FLASH_SECTOR_5, ADDR_FLASH_SECTOR_6, ADDR_FLASH_SECTOR_7, - ADDR_FLASH_SECTOR_8, ADDR_FLASH_SECTOR_9, ADDR_FLASH_SECTOR_10, ADDR_FLASH_SECTOR_11 -}; - -int prv_get_sector_num_for_address(uint32_t address) { - if (address < s_sector_addresses[0]) { - dbgserial_print("address "); - dbgserial_print_hex(address); - dbgserial_putstr(" is outside system flash"); - return -1; - } - for (size_t i=0; i < ARRAY_LENGTH(s_sector_addresses)-1; ++i) { - if (s_sector_addresses[i] <= address - && address < s_sector_addresses[i+1]) { - return i; - } - } - return ARRAY_LENGTH(s_sector_addresses)-1; -} - -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - if (length == 0) { - // Nothing to do - return true; - } - - int first_sector = prv_get_sector_num_for_address(address); - int last_sector = prv_get_sector_num_for_address(address + length - 1); - if (first_sector < 0 || last_sector < 0) { - return false; - } - int count = last_sector - first_sector + 1; - if (progress_callback) { - progress_callback(0, count, progress_context); - } - - FLASH_Unlock(); - for (int sector = first_sector; sector <= last_sector; ++sector) { - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - if (FLASH_EraseSector( - s_sectors[sector], VoltageRange_1) != FLASH_COMPLETE) { - dbgserial_print("failed to erase sector "); - dbgserial_print_hex(sector); - dbgserial_newline(); - FLASH_Lock(); - return false; - } - if (progress_callback) { - progress_callback(sector - first_sector + 1, count, progress_context); - } - } - FLASH_Lock(); - return true; -} - -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - FLASH_Unlock(); - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); - - const uint8_t *data_array = data; - for (uint32_t i = 0; i < length; ++i) { - if (FLASH_ProgramByte(address + i, data_array[i]) != FLASH_COMPLETE) { - dbgserial_print("failed to write address "); - dbgserial_print_hex(address); - dbgserial_newline(); - FLASH_Lock(); - return false; - } - if (progress_callback && i % 128 == 0) { - progress_callback(i/128, length/128, progress_context); - } - } - FLASH_Lock(); - return true; -} diff --git a/platform/silk/boot/src/drivers/system_flash.h b/platform/silk/boot/src/drivers/system_flash.h deleted file mode 100644 index c4290daa55..0000000000 --- a/platform/silk/boot/src/drivers/system_flash.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "stm32f4xx_flash.h" - -#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */ -#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */ - -typedef void (*SystemFlashProgressCb)( - uint32_t progress, uint32_t total, void *context); - -// Erase the sectors of flash which lie within the given address range. -// -// If the address range overlaps even one single byte of a sector, the entire -// sector is erased. -// -// If progress_callback is not NULL, it is called at the beginning of the erase -// process and after each sector is erased. The rational number (progress/total) -// increases monotonically as the sector erasue procedure progresses. -// -// Returns true if successful, false if an error occurred. -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); - -// Write data into flash. The flash must already be erased. -// -// If progress_callback is not NULL, it is called at the beginning of the -// writing process and periodically thereafter. The rational number -// (progress/total) increases monotonically as the data is written. -// -// Returns true if successful, false if an error occurred. -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); diff --git a/platform/silk/boot/src/drivers/watchdog.c b/platform/silk/boot/src/drivers/watchdog.c deleted file mode 100644 index 9e8e268602..0000000000 --- a/platform/silk/boot/src/drivers/watchdog.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/watchdog.h" - -#include "stm32f4xx_dbgmcu.h" -#include "stm32f4xx_iwdg.h" -#include "stm32f4xx_rcc.h" - -void watchdog_init(void) { - IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); - - IWDG_SetPrescaler(IWDG_Prescaler_64); // ~8 seconds - IWDG_SetReload(0xfff); - - IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); - - DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP, ENABLE); -} - -void watchdog_start(void) { - IWDG_Enable(); - IWDG_ReloadCounter(); -} - -bool watchdog_check_reset_flag(void) { - return RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET; -} diff --git a/platform/silk/boot/src/drivers/watchdog.h b/platform/silk/boot/src/drivers/watchdog.h deleted file mode 100644 index d42fec728d..0000000000 --- a/platform/silk/boot/src/drivers/watchdog.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void watchdog_init(void); -void watchdog_start(void); - -bool watchdog_check_reset_flag(void); diff --git a/platform/silk/boot/src/firmware.h b/platform/silk/boot/src/firmware.h deleted file mode 100644 index a57140343a..0000000000 --- a/platform/silk/boot/src/firmware.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "stm32f4xx.h" - -#define FIRMWARE_BASE (FLASH_BASE + (BOOTLOADER_LENGTH)) diff --git a/platform/silk/boot/src/flash_region.h b/platform/silk/boot/src/flash_region.h deleted file mode 100644 index d8b427e5ec..0000000000 --- a/platform/silk/boot/src/flash_region.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Scratch space for firmware images (normal and recovery). -// We assume this is 64k aligned... -#define FLASH_REGION_FIRMWARE_SCRATCH_BEGIN 0x000000 -#define FLASH_REGION_FIRMWARE_SCRATCH_END 0x100000 // 1024k - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x200000 -#define FLASH_REGION_SAFE_FIRMWARE_END 0x280000 // 512k diff --git a/platform/silk/boot/src/fw_copy.c b/platform/silk/boot/src/fw_copy.c deleted file mode 100644 index b47287730e..0000000000 --- a/platform/silk/boot/src/fw_copy.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fw_copy.h" - -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/system_flash.h" -#include "firmware.h" -#include "flash_region.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/reset.h" -#include "util/crc32.h" -#include "util/misc.h" -#include "util/delay.h" - -#include -#include - -#define MAX_CHUNK_SIZE 65536 - -static bool check_valid_firmware_crc(uint32_t flash_address, FirmwareDescription* desc) { - dbgserial_putstr("Checksumming firmware update"); - const uint32_t crc = flash_calculate_checksum(flash_address, desc->firmware_length); - dbgserial_print("Calculated checksum: "); - dbgserial_print_hex(crc); - dbgserial_newline(); - return crc == desc->checksum; -} - -static void prv_display_erase_progress( - uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress, total * 2); -} - -static bool erase_old_firmware(uint32_t firmware_length) { - dbgserial_putstr("erase_old_firmware"); - return system_flash_erase( - FIRMWARE_BASE, firmware_length, prv_display_erase_progress, 0); -} - -static void prv_display_write_progress( - uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress/2 + total/2, total); -} - -static bool write_new_firmware(uint32_t flash_new_fw_start, - uint32_t firmware_length) { - dbgserial_putstr("write_new_firmware"); - // We can't just read the flash like memory, so we gotta lift everything ourselves. - // buffer is static so it goes in BSS, since stack is only 8192 bytes - static uint8_t buffer[MAX_CHUNK_SIZE]; - uint32_t chunk_size; - for (uint32_t i = 0; i < firmware_length; i += chunk_size) { - chunk_size = MIN(MAX_CHUNK_SIZE, firmware_length - i); - flash_read_bytes(buffer, flash_new_fw_start + i, chunk_size); - if (!system_flash_write(FIRMWARE_BASE + i, buffer, chunk_size, NULL, NULL)) { - dbgserial_putstr("We're dead"); - return false; - } - prv_display_write_progress(i + chunk_size, firmware_length, NULL); - } - return true; -} - -static bool check_firmware_crc(FirmwareDescription* firmware_description) { - dbgserial_print("Checksumming "); - dbgserial_print_hex(firmware_description->firmware_length); - dbgserial_print(" bytes\r\n"); - - const uint32_t crc = crc32(CRC32_INIT, (const uint8_t *)FIRMWARE_BASE, - firmware_description->firmware_length); - - dbgserial_print("Checksum - wanted "); - dbgserial_print_hex(firmware_description->checksum); - dbgserial_print(" got "); - dbgserial_print_hex(crc); - dbgserial_newline(); - - return crc == firmware_description->checksum; -} - -typedef enum UpdateFirmwareResult { - UPDATE_FW_SUCCESS = 0, - UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED = 1, - UPDATE_FW_ERROR_MICRO_FLASH_MANGLED = 2 -} UpdateFirmwareResult; - -static UpdateFirmwareResult update_fw(uint32_t flash_address) { - display_firmware_update_progress(0, 1); - boot_bit_set(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - - FirmwareDescription firmware_description = - firmware_storage_read_firmware_description(flash_address); - - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - dbgserial_putstr("Invalid firmware description!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - if (!check_valid_firmware_crc( - flash_address + sizeof(FirmwareDescription), &firmware_description)) { - dbgserial_putstr("Invalid firmware CRC in SPI flash!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - erase_old_firmware(firmware_description.firmware_length); - - write_new_firmware( - flash_address + sizeof(FirmwareDescription), - firmware_description.firmware_length); - - if (!check_firmware_crc(&firmware_description)) { - dbgserial_putstr( - "Our internal flash contents are bad (checksum failed)! " - "This is really bad!"); - return UPDATE_FW_ERROR_MICRO_FLASH_MANGLED; - } - - return UPDATE_FW_SUCCESS; -} - -void check_update_fw(void) { - if (!boot_bit_test(BOOT_BIT_NEW_FW_AVAILABLE)) { - return; - } - - if (boot_bit_test(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS)) { - dbgserial_putstr("Our previous firmware update failed, aborting update."); - - // Pretend like the new firmware bit wasn't set afterall. We'll just run the - // previous code, whether that was normal firmware or the recovery firmware. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_clear(BOOT_BIT_NEW_FW_INSTALLED); - return; - } - - - dbgserial_putstr("New firmware is available!"); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - - UpdateFirmwareResult result = update_fw(FLASH_REGION_FIRMWARE_SCRATCH_BEGIN); - switch (result) { - case UPDATE_FW_SUCCESS: - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - // Our firmware update failed in a way that didn't break our previous - // firmware. Just run the previous code, whether that was normal firmware - // or the recovery firmware. - break; - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // We've broken our internal flash when trying to update our normal - // firmware. Fall back immediately to the recovery firmare. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - system_reset(); - return; - } - - // Done, we're ready to boot. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_set(BOOT_BIT_NEW_FW_INSTALLED); -} - -bool switch_to_recovery_fw() { - dbgserial_putstr("Loading recovery firmware"); - - UpdateFirmwareResult result = update_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - bool recovery_fw_ok = true; - switch (result) { - case UPDATE_FW_SUCCESS: - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // Keep us booting into recovery firmware. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - - if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to load recovery firmware, strike one. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO)) { - dbgserial_putstr("Failed to load recovery firmware, strike two. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else { - dbgserial_putstr("Failed to load recovery firmware, strike three. SAD WATCH"); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - recovery_fw_ok = false; - } - break; - } - - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - return recovery_fw_ok; -} diff --git a/platform/silk/boot/src/fw_copy.h b/platform/silk/boot/src/fw_copy.h deleted file mode 100644 index aab6d1e7a7..0000000000 --- a/platform/silk/boot/src/fw_copy.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void check_update_fw(void); - -//! @return false if we've failed to load recovery mode too many times and we should just sadwatch -bool switch_to_recovery_fw(void); diff --git a/platform/silk/boot/src/git_version.auto.h.in b/platform/silk/boot/src/git_version.auto.h.in deleted file mode 100644 index f5a14b1a0a..0000000000 --- a/platform/silk/boot/src/git_version.auto.h.in +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#define GIT_TIMESTAMP @TIMESTAMP@ -#define GIT_TAG "@TAG@" -#define GIT_MAJOR_VERSION @MAJOR_VERSION@ -#define GIT_MINOR_VERSION @MINOR_VERSION@ -#define GIT_PATCH_VERSION @PATCH_VERSION@ -#define GIT_REVISION "@COMMIT@" diff --git a/platform/silk/boot/src/hardfault_handler.c b/platform/silk/boot/src/hardfault_handler.c deleted file mode 100644 index 28b76be3d9..0000000000 --- a/platform/silk/boot/src/hardfault_handler.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/die.h" -#include "system/reset.h" - -static void prv_hard_fault_handler_c(unsigned int *hardfault_args) { - dbgserial_putstr("HARD FAULT"); - -#ifdef NO_WATCHDOG - reset_due_to_software_failure(); -#else - system_hard_reset(); -#endif -} - -void HardFault_Handler(void) { - // Grab the stack pointer, shove it into a register and call - // the c function above. - __asm("tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "b %0\n" :: "i" (prv_hard_fault_handler_c)); -} diff --git a/platform/silk/boot/src/main.c b/platform/silk/boot/src/main.c deleted file mode 100644 index 79b2b39a96..0000000000 --- a/platform/silk/boot/src/main.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "board/board.h" -#include "boot_tests.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "drivers/watchdog.h" -#include "firmware.h" -#include "fw_copy.h" -#include "pebble_errors.h" -#include "stm32f4xx.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" -#include "util/delay.h" - - -static const uint8_t SELECT_BUTTON_MASK = 0x4; - -static void prv_get_fw_reset_vector(void **reset_handler, - void **initial_stack_pointer) { - uintptr_t** fw_vector_table = (uintptr_t**) FIRMWARE_BASE; // Defined in wscript - *initial_stack_pointer = (void *)fw_vector_table[0]; - *reset_handler = (void *)fw_vector_table[1]; -} - -static void __attribute__((noreturn)) jump_to_fw(void) { - void *initial_stack_pointer, *reset_handler; - prv_get_fw_reset_vector(&reset_handler, &initial_stack_pointer); - - dbgserial_print("Booting firmware @ "); - dbgserial_print_hex((uintptr_t)reset_handler); - dbgserial_print("...\r\n\r\n"); - - // Disable all interrupts, just in case. - for (int i = 0; i < 8; ++i) { - // Interrupt Clear-Enable Register - NVIC->ICER[i] = 0xFFFFFFFF; - // Interrupt Clear-Pending Register - NVIC->ICPR[i] = 0xFFFFFFFF; - } - - // Disable all of the peripheral clocks we may have on, but don't touch any of - // the reserved bits. The reset values in the stm32f412 reference manual are - // not to be trusted, so make sure those bits never change. - RCC->AHB1ENR &= ~(0x006010ff); - RCC->AHB2ENR &= ~(0x000000c0); - RCC->AHB3ENR &= ~(0x00000003); - RCC->APB1ENR &= ~(0x17e6c9ff); - RCC->APB2ENR &= ~(0x01177933); - - // Reset most peripherals used by the bootloader. We want to minimize the - // chances that the firmware unintentionally relies on some state that the - // bootloader leaves behind. This includes disabling the PLL. - // GPIOs are not reset here: resetting them would change their output values, - // which could unintentionally turn of e.g. PMIC power rails. - // The backup domain is not reset; that would be foolish. - const uint32_t ahb1_periphs = - RCC_AHB1Periph_CRC | RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_DMA2 - | RCC_AHB1Periph_DMA2D | RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_OTG_HS; - const uint32_t ahb2_periphs = - RCC_AHB2Periph_DCMI | RCC_AHB2Periph_CRYP | RCC_AHB2Periph_HASH - | RCC_AHB2Periph_RNG | RCC_AHB2Periph_OTG_FS; - const uint32_t ahb3_periphs = RCC_AHB3Periph_FSMC; - const uint32_t apb1_periphs = - RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4 - | RCC_APB1Periph_TIM5 | RCC_APB1Periph_TIM6 | RCC_APB1Periph_TIM7 - | RCC_APB1Periph_TIM12 | RCC_APB1Periph_TIM13 | RCC_APB1Periph_TIM14 - | RCC_APB1Periph_WWDG | RCC_APB1Periph_SPI2 | RCC_APB1Periph_SPI3 - | RCC_APB1Periph_USART2 | RCC_APB1Periph_USART3 | RCC_APB1Periph_UART4 - | RCC_APB1Periph_UART5 | RCC_APB1Periph_I2C1 | RCC_APB1Periph_I2C2 - | RCC_APB1Periph_I2C3 | RCC_APB1Periph_CAN1 | RCC_APB1Periph_CAN2 - | RCC_APB1Periph_PWR | RCC_APB1Periph_DAC | RCC_APB1Periph_UART7 - | RCC_APB1Periph_UART8; - const uint32_t apb2_periphs = - RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART1 | - RCC_APB2Periph_USART6 | RCC_APB2Periph_ADC | RCC_APB2Periph_ADC1 | - RCC_APB2Periph_ADC2 | RCC_APB2Periph_ADC3 | RCC_APB2Periph_SDIO | - RCC_APB2Periph_SPI1 | RCC_APB2Periph_SPI4 | RCC_APB2Periph_SYSCFG | - RCC_APB2Periph_TIM9 | RCC_APB2Periph_TIM10 | RCC_APB2Periph_TIM11 | - RCC_APB2Periph_SPI5 | RCC_APB2Periph_SPI6 | RCC_APB2Periph_SAI1 | - RCC_APB2Periph_LTDC; - RCC_DeInit(); - RCC_AHB1PeriphResetCmd(ahb1_periphs, ENABLE); - RCC_AHB1PeriphResetCmd(ahb1_periphs, DISABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, ENABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, DISABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, ENABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, DISABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, ENABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, DISABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, ENABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, DISABLE); - - // The Cortex-M user guide states that the reset values for the core registers - // are as follows: - // R0-R12 = Unknown - // MSP = VECTOR_TABLE[0] (main stack pointer) - // PSP = Unknown (process stack pointer) - // LR = 0xFFFFFFFF - // PC = VECTOR_TABLE[1] - // PRIMASK = 0x0 - // FAULTMASK = 0x0 - // BASEPRI = 0x0 - // CONTROL = 0x0 - // - // Attempt to put the processor into as close to the reset state as possible - // before passing control to the firmware. - // - // No attempt is made to set CONTROL to zero as it should already be set to - // the reset value when this code executes. - __asm volatile ( - "cpsie if\n" // Clear PRIMASK and FAULTMASK - "mov lr, 0xFFFFFFFF\n" - "mov sp, %[initial_sp]\n" - "bx %[reset_handler]\n" - : : [initial_sp] "r" (initial_stack_pointer), - [reset_handler] "r" (reset_handler) - ); - __builtin_unreachable(); -} - -static bool check_and_increment_reset_loop_detection_bits(void) { - uint8_t counter = - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_THREE) << 2) | - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_TWO) << 1) | - boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_ONE); - - if (counter == 7) { - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE); - return true; - } - - switch (++counter) { - case 1: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 2: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 3: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 4: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_THREE); - break; - case 5: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 6: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 7: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - default: - PBL_CROAK("reset loop boot bits overrun"); - break; - } - return false; -} - -static bool check_for_recovery_start_failure() { - return boot_bit_test(BOOT_BIT_RECOVERY_START_IN_PROGRESS); -} - -static bool check_for_fw_start_failure() { - // Add more failure conditions here. - if (!watchdog_check_reset_flag() && !boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - // We're good, we're just starting normally. - PBL_LOG_VERBOSE("We're good, we're just starting normally."); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return false; - } - - // We failed to start our firmware successfully! - if (watchdog_check_reset_flag()) { - dbgserial_putstr("Watchdog caused a reset"); - } - if (boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - dbgserial_putstr("Software failure caused a reset"); - } - - // Clean up after the last failure. - boot_bit_clear(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - - // We have a "three strikes" algorithm: if the watch fails three times, return true - // to tell the parent we should load the recovery firmware. A reset for any other reason - // will reset this algorithm. - if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) { - // Yikes, our firmware is screwed. Boot into recovery mode. - dbgserial_putstr("Failed to start firmware, strike three."); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return true; - } else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to start firmware, strike two."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - } else { - dbgserial_putstr("Failed to start firmware, strike one."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - } - - return false; -} - -static bool prv_prf_button_combination_is_pressed(void) { - return (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK) - && button_is_pressed(BUTTON_ID_SELECT) && !button_is_pressed(BUTTON_ID_DOWN)); -} - -static bool check_force_boot_recovery(void) { - if (boot_bit_test(BOOT_BIT_FORCE_PRF)) { - boot_bit_clear(BOOT_BIT_FORCE_PRF); - return true; - } - - if (prv_prf_button_combination_is_pressed()) { - dbgserial_putstr("Hold down UP + BACK + SELECT for 5 secs. to force-boot PRF"); - for (int i = 0; i < 5000; ++i) { - if (!prv_prf_button_combination_is_pressed()) { - // stop waiting if not held down any longer - return false; - } - delay_ms(1); - } - - return true; - } - - void *reset_vector, *initial_sp; - prv_get_fw_reset_vector(&reset_vector, &initial_sp); - if ((uintptr_t)reset_vector == 0xffffffff || - (uintptr_t)initial_sp == 0xffffffff) { - dbgserial_putstr("Firmware is erased"); - return true; - } - return false; -} - -static void sad_watch(uint32_t error_code) { - dbgserial_putstr("SAD WATCH"); - - char error_code_buffer[12]; - itoa_hex(error_code, error_code_buffer, sizeof(error_code_buffer)); - dbgserial_putstr(error_code_buffer); - - display_error_code(error_code); - - static uint8_t prev_button_state = 0; - prev_button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - while (1) { - // See if we should restart - uint8_t button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - if (button_state != prev_button_state) { - system_reset(); - } - delay_ms(10); - } -} - -static void check_and_handle_resuming_from_standby(void) { - periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_PWR); - if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) { - // We just woke up from standby. For some reason this leaves the system in a funny state, - // so clear the flag and reboot again to really clear things up. - - PWR_ClearFlag(PWR_FLAG_SB); - dbgserial_putstr("exit standby"); - system_hard_reset(); - } - periph_config_disable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_PWR); -} - -void boot_main(void) { - gpio_enable_all(); - - check_and_handle_resuming_from_standby(); - - dbgserial_init(); - - dbgserial_putstr(""); - dbgserial_putstr(" ___ _ _ _ "); - dbgserial_putstr("/ __><_>| || |__"); - dbgserial_putstr("\\__ \\| || || / /"); - dbgserial_putstr("<___/|_||_||_\\_\\"); - dbgserial_putstr(""); - - - // PMIC requires I2C - i2c_init(); - - dbgserial_putstr("i2c inited"); - - pmic_init(); - - dbgserial_putstr("pmic inited"); - - boot_bit_init(); - - dbgserial_putstr("boot bit"); - - boot_version_write(); - - // Write the bootloader version to serial-out - { - char bootloader_version_str[12]; - memset(bootloader_version_str, 0, 12); - itoa_hex(boot_version_read(), bootloader_version_str, 12); - dbgserial_putstr(bootloader_version_str); - } - dbgserial_putstr(""); - dbgserial_putstr(""); - - if (boot_bit_test(BOOT_BIT_FW_STABLE)) { - dbgserial_putstr("Last firmware boot was stable; clear strikes"); - - boot_bit_clear(BOOT_BIT_FW_STABLE); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - } - - if (pmic_boot_due_to_charger_disconnect()) { - dbgserial_putstr("PMIC woke from standby due to charger disconnect"); - dbgserial_putstr("Putting PMIC back into standby"); - pmic_power_off(); - } - - flash_init(); - button_init(); - display_init(); - display_boot_splash(); - -#ifdef DISPLAY_DEMO_LOOP - while (1) { - for (int i=0; i < 92; ++i) { - display_firmware_update_progress(i, 91); - delay_us(80000); - } - - for (uint32_t i=0; i <= 0xf; ++i) { - display_error_code(i * 0x11111111); - delay_us(200000); - } - for (uint32_t i=0; i < 8; ++i) { - for (uint32_t j=1; j<=0xf; ++j) { - display_error_code(j << (i*4)); - delay_us(200000); - } - } - display_error_code(0x01234567); - delay_us(200000); - display_error_code(0x89abcdef); - delay_us(200000); - display_error_code(0xcafebabe); - delay_us(200000); - display_error_code(0xfeedface); - delay_us(200000); - display_error_code(0x8badf00d); - delay_us(200000); - display_error_code(0xbad1ce40); - delay_us(200000); - display_error_code(0xbeefcace); - delay_us(200000); - display_error_code(0x0defaced); - delay_us(200000); - display_error_code(0xd15ea5e5); - delay_us(200000); - display_error_code(0xdeadbeef); - delay_us(200000); - display_boot_splash(); - delay_us(1000000); - } -#endif - - if (is_button_stuck()) { - sad_watch(ERROR_STUCK_BUTTON); - } - - if (is_flash_broken()) { - sad_watch(ERROR_BAD_SPI_FLASH); - } - - boot_bit_dump(); - - // If the recovery firmware crashed at start-up, the watch is now a - // $150 brick. That's life! - if (check_for_recovery_start_failure()) { - boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - sad_watch(ERROR_CANT_LOAD_FW); - } - - bool force_boot_recovery_mode = check_force_boot_recovery(); - if (force_boot_recovery_mode) { - dbgserial_putstr("Force-booting recovery mode..."); - } - - if (force_boot_recovery_mode || check_for_fw_start_failure()) { - if (!switch_to_recovery_fw()) { - // We've failed to load recovery mode too many times. - sad_watch(ERROR_CANT_LOAD_FW); - } - } else { - check_update_fw(); - } - - if (check_and_increment_reset_loop_detection_bits()) { - sad_watch(ERROR_RESET_LOOP); - } - - if (boot_bit_test(BOOT_BIT_SHUTDOWN_REQUESTED)) { - boot_bit_clear(BOOT_BIT_SHUTDOWN_REQUESTED); - dbgserial_putstr("Shutdown requested."); - // A full shutdown has been requested instead of booting. - // This needs to be done prior to watchdog init: we may need to spin on the charger status. - // It is OK to spin indefinitely here. We are connected to a charger and won't die. - - int i = 0; - while (pmic_is_usb_connected()) { - if (i == 4) { - dbgserial_putstr("Remove charger to shutdown"); - i = 0; - } - delay_ms(500); - ++i; - } - - dbgserial_putstr("Shutting down."); - pmic_full_power_off(); - } - - watchdog_init(); -#ifndef NO_WATCHDOG - watchdog_start(); -#endif - - gpio_disable_all(); - jump_to_fw(); -} diff --git a/platform/silk/boot/src/pebble_errors.h b/platform/silk/boot/src/pebble_errors.h deleted file mode 100644 index 1278bf459b..0000000000 --- a/platform/silk/boot/src/pebble_errors.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -static const uint32_t ERROR_NO_ACTIVE_ERROR = 0; - -// FW Errors -static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501; -static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502; -static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503; -static const uint32_t ERROR_RESET_LOOP = 0xfe504504; - -// BT Errors -static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510; -static const uint32_t ERROR_CANT_START_LSE = 0xfe504511; -static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512; - -static const uint32_t ERROR_LOW_BATTERY = 0xfe504520; diff --git a/platform/silk/boot/src/stm32f_flash_boot.ld.in b/platform/silk/boot/src/stm32f_flash_boot.ld.in deleted file mode 100644 index 251733c9ff..0000000000 --- a/platform/silk/boot/src/stm32f_flash_boot.ld.in +++ /dev/null @@ -1,180 +0,0 @@ -/* -Linker script for STM32F2xx_1024K_128K -*/ - -/* include the common STM32F2xx sub-script */ - -/* Common part of the linker scripts for STM32 devices*/ - -/* default stack sizes. -These are used by the startup in order to allocate stacks for the different modes. -*/ - -__Stack_Size = 8192; - -PROVIDE ( _Stack_Size = __Stack_Size ) ; - -__Stack_Init = _estack - __Stack_Size ; - -/*"PROVIDE" allows to easily override these values from an object file or the commmand line.*/ -PROVIDE ( _Stack_Init = __Stack_Init ) ; - -/* -There will be a link error if there is not this amount of RAM free at the end. -*/ -_Minimum_Stack_Size = 0x100 ; - -/* include the memory spaces definitions sub-script */ -/* -Linker subscript for STM32F2xx definitions with 1024K Flash and 1024K External SRAM */ - -/* Memory Spaces Definitions */ - -MEMORY -{ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 256K - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_LENGTH@ -} - -__end_heap = ORIGIN(RAM) + LENGTH(RAM); -PROVIDE(_heap_end = __end_heap); - -/* include the sections management sub-script for FLASH mode */ - -/* Sections Definitions */ - -SECTIONS -{ - /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* for some STRx devices, the beginning of the startup code is stored in the .flashtext section, which goes to FLASH */ - .flashtext : - { - . = ALIGN(4); - *(.flashtext) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* Exception handling sections. "contains index entries for section unwinding" */ - .ARM.exidx : - { - . = ALIGN(4); - *(.ARM.exidx) - . = ALIGN(4); - } >FLASH - - /* the program code is stored in the .text section, which goes to Flash */ - .text : - { - . = ALIGN(4); - - *(.text) /* remaining code */ - *(.text.*) /* remaining code */ - *(.rodata) /* read-only data (constants) */ - *(.rodata*) - *(.constdata) /* read-only data (constants) */ - *(.constdata*) - *(.glue_7) - *(.glue_7t) - *(i.*) - - . = ALIGN(4); - _etext = .; - _sidata = _etext; - } >FLASH - - /* This is the initialized data section - The program executes knowing that the data is in the RAM - but the loader puts the initial values in the FLASH (inidata). - It is one task of the startup to copy the initial values from FLASH to RAM. */ - .data : AT ( _sidata ) - { - . = ALIGN(4); - /* This is used by the startup in order to initialize the .data secion */ - _sdata = .; - - *(.data) - *(.data.*) - - . = ALIGN(4); - _edata = .; /* This is used by the startup in order to initialize the .data secion */ - } >RAM - - /* This is the uninitialized data section */ - .bss : - { - . = ALIGN(4); - _sbss = .; /* This is used by the startup in order to initialize the .bss secion */ - - *(.bss) - *(.bss.*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* This is used by the startup in order to initialize the .bss secion */ - } >RAM - - .stack : - { - . = ALIGN(8); - _sstack = .; - . = . + __Stack_Size; - . = ALIGN(8); - _endstack = .; - } >RAM - - PROVIDE ( _estack = _endstack); - PROVIDE ( end = _endstack ); - PROVIDE ( _end = _endstack ); - PROVIDE ( _heap_start = _endstack ); - - /* after that it's only debugging information. */ - - /* remove the debugging information from the standard libraries */ - DISCARD : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} diff --git a/platform/silk/boot/src/system/bootbits.c b/platform/silk/boot/src/system/bootbits.c deleted file mode 100644 index 654ea65387..0000000000 --- a/platform/silk/boot/src/system/bootbits.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/bootbits.h" -#include "system/rtc_registers.h" - -#include "git_version.auto.h" - -#include "stm32f4xx.h" - -#include -#include - -static const uint32_t s_bootloader_timestamp = GIT_TIMESTAMP; - -void boot_bit_init(void) { - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); - PWR_BackupAccessCmd(ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, DISABLE); - - if (!boot_bit_test(BOOT_BIT_INITIALIZED)) { - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED); - } -} - -void boot_bit_set(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value |= bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -void boot_bit_clear(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value &= ~bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -bool boot_bit_test(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - return (current_value & bit); -} - -void boot_bit_dump(void) { - dbgserial_print("Boot bits: "); - dbgserial_print_hex(RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR)); - dbgserial_newline(); -} - -void boot_version_write(void) { - if (boot_version_read() == s_bootloader_timestamp) { - return; - } - RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, s_bootloader_timestamp); -} - -uint32_t boot_version_read(void) { - return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER); -} diff --git a/platform/silk/boot/src/system/bootbits.h b/platform/silk/boot/src/system/bootbits.h deleted file mode 100644 index a2612651f0..0000000000 --- a/platform/silk/boot/src/system/bootbits.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum BootBitValue { - BOOT_BIT_INITIALIZED = 0x1 << 0, - BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1, - BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2, - BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3, - BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6, - BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7, - BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, //!< Bootloader enter standby immediately after reset. - BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9, - BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10, - BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11, - BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12, - BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13, - BOOT_BIT_FW_STABLE = 0x1 << 14, - BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15, - BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16, - BOOT_BIT_FORCE_PRF = 0x1 << 17, - BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18, - BOOT_BIT_SHUTDOWN_REQUESTED = 0x1 << 19, //!< Bootloader hard power-off instead of jumping to fw. -} BootBitValue; - -void boot_bit_init(); -void boot_bit_set(BootBitValue bit); -void boot_bit_clear(BootBitValue bit); -bool boot_bit_test(BootBitValue bit); - -// Dump the contents through dbgserial -void boot_bit_dump(void); - -void boot_version_write(void); -uint32_t boot_version_read(void); diff --git a/platform/silk/boot/src/system/die.c b/platform/silk/boot/src/system/die.c deleted file mode 100644 index dd5fdb6367..0000000000 --- a/platform/silk/boot/src/system/die.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/reset.h" -#include "system/passert.h" - -#include "misc.h" - -void reset_due_to_software_failure(void) { -#if defined(NO_WATCHDOG) - // Don't reset right away, leave it in a state we can inspect - - while (1) { - BREAKPOINT; - } -#endif - - dbgserial_putstr("Software failure; resetting!"); - system_reset(); -} diff --git a/platform/silk/boot/src/system/die.h b/platform/silk/boot/src/system/die.h deleted file mode 100644 index 8ddffee0e9..0000000000 --- a/platform/silk/boot/src/system/die.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were -//! able shut everything down nicely before rebooting. -void reset_due_to_software_failure(void) __attribute__((noreturn)); diff --git a/platform/silk/boot/src/system/firmware_storage.c b/platform/silk/boot/src/system/firmware_storage.c deleted file mode 100644 index 586ebf1649..0000000000 --- a/platform/silk/boot/src/system/firmware_storage.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "firmware_storage.h" - -#include "drivers/flash.h" - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { - FirmwareDescription firmware_description; - flash_read_bytes((uint8_t*)&firmware_description, firmware_start_address, - sizeof(FirmwareDescription)); - return firmware_description; -} - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc) { - return desc->description_length == sizeof(FirmwareDescription); -} diff --git a/platform/silk/boot/src/system/firmware_storage.h b/platform/silk/boot/src/system/firmware_storage.h deleted file mode 100644 index e05c44b208..0000000000 --- a/platform/silk/boot/src/system/firmware_storage.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file firmware_storage.h -//! Utilities for reading a firmware image stored in flash. - -#include -#include - -typedef struct __attribute__((__packed__)) FirmwareDescription { - uint32_t description_length; - uint32_t firmware_length; - uint32_t checksum; -} FirmwareDescription; - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address); - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc); diff --git a/platform/silk/boot/src/system/logging.h b/platform/silk/boot/src/system/logging.h deleted file mode 100644 index c28b44ca12..0000000000 --- a/platform/silk/boot/src/system/logging.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/dbgserial.h" -#include "system/die.h" - -#include -#include -#include -#include -#include - -#ifndef __FILE_NAME__ -#define __FILE_NAME__ __FILE__ -#endif - -#define LOG_LEVEL_ALWAYS 0 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_WARNING 50 -#define LOG_LEVEL_INFO 100 -#define LOG_LEVEL_DEBUG 200 -#define LOG_LEVEL_DEBUG_VERBOSE 255 - -#ifndef STRINGIFY - #define STRINGIFY_NX(a) #a - #define STRINGIFY(a) STRINGIFY_NX(a) -#endif // STRINGIFY - -#define STATUS_STRING(s) STRINGIFY(s) - -#ifdef PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) \ - do { \ - char _pbl_log_buffer[128]; \ - dbgserial_putstr_fmt(_pbl_log_buffer, sizeof(_pbl_log_buffer), \ - __FILE_NAME__ ":" STRINGIFY(__LINE__) "> " fmt, ## args); \ - } while (0) - - #ifdef VERBOSE_LOGGING - - #define PBL_LOG_VERBOSE(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) - - #else // VERBOSE_LOGGING - #define PBL_LOG_VERBOSE(fmt, args...) - #endif // VERBOSE_LOGGING - -#else // PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) - #define PBL_LOG_VERBOSE(fmt, args...) -#endif // PBL_LOG_ENABLED diff --git a/platform/silk/boot/src/system/passert.c b/platform/silk/boot/src/system/passert.c deleted file mode 100644 index 0b5f8b138b..0000000000 --- a/platform/silk/boot/src/system/passert.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "passert.h" - -#include "system/die.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#include -#include -#include -#include -#include - -static __attribute__((noreturn)) void handle_passert_failed_vargs(const char* filename, int line_number, - uintptr_t lr, const char* expr, const char* fmt, va_list fmt_args) { - dbgserial_print("ASSERT: "); - dbgserial_print(expr); - dbgserial_print(" "); - dbgserial_print(filename); - dbgserial_print(":"); - dbgserial_print_hex(line_number); - if (fmt) { - dbgserial_print(" "); - dbgserial_print(fmt); - } - dbgserial_putstr(""); - - reset_due_to_software_failure(); -} - -static __attribute__((noreturn)) void handle_passert_failed(const char* filename, int line_number, - uintptr_t lr, const char *expr, const char* fmt, ...) { - va_list fmt_args; - va_start(fmt_args, fmt); - - handle_passert_failed_vargs(filename, line_number, lr, expr, fmt, fmt_args); - - va_end(fmt_args); -} - -void passert_failed(const char* filename, int line_number, const char* message, ...) { - va_list fmt_args; - va_start(fmt_args, message); - - handle_passert_failed_vargs(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERT", message, fmt_args); - - va_end(fmt_args); -} - -void passert_failed_no_message(const char* filename, int line_number) { - handle_passert_failed(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERTN", NULL); -} - -void wtf(void) { - uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); - dbgserial_print("*** WTF "); - dbgserial_print_hex(saved_lr); - dbgserial_putstr(""); - reset_due_to_software_failure(); -} - -//! Assert function called by the STM peripheral library's -//! 'assert_param' method. See stm32f2xx_conf.h for more information. -void assert_failed(uint8_t* file, uint32_t line) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - - handle_passert_failed((const char*) file, line, saved_lr, "STM32", "STM32 peripheral library tripped an assert"); -} - -extern void command_dump_malloc_kernel(void); - -void croak_oom(const char *filename, int line_number, const char *fmt, ...) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - -#ifdef MALLOC_INSTRUMENTATION - command_dump_malloc_kernel(); -#endif - - va_list fmt_args; - va_start(fmt_args, fmt); - - handle_passert_failed_vargs(filename, line_number, saved_lr, "CROAK OOM", fmt, fmt_args); - - va_end(fmt_args); -} diff --git a/platform/silk/boot/src/system/passert.h b/platform/silk/boot/src/system/passert.h deleted file mode 100644 index 781f297dab..0000000000 --- a/platform/silk/boot/src/system/passert.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "logging.h" - - -void passert_failed(const char* filename, int line_number, const char* message, ...) - __attribute__((noreturn)); - -#define PBL_ASSERT(expr, ...) \ - do { \ - if (!(expr)) { \ - passert_failed(__FILE_NAME__, __LINE__, __VA_ARGS__); \ - } \ - } while (0) - -#define PBL_ASSERTN(expr) \ - do { \ - if (!(expr)) { \ - passert_failed_no_message(__FILE_NAME__, __LINE__); \ - } \ - } while (0) - -void passert_failed_no_message(const char* filename, int line_number) - __attribute__((noreturn)); - -void wtf(void) __attribute__((noreturn)); - -#define WTF wtf() - -// Insert a compiled-in breakpoint -#define BREAKPOINT __asm("bkpt") - -#define PBL_ASSERT_PRIVILEGED() -#define PBL_ASSERT_TASK(task) - -#define PBL_CROAK(fmt, args...) \ - passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args) diff --git a/platform/silk/boot/src/system/reset.c b/platform/silk/boot/src/system/reset.c deleted file mode 100644 index 56e5039652..0000000000 --- a/platform/silk/boot/src/system/reset.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "system/reset.h" - -#include "drivers/display.h" - -#include "stm32f4xx.h" - -void system_reset(void) { - display_prepare_for_reset(); - - // Clear the reset reason since it will no longer - // apply after this bootloader reset - RCC_ClearFlag(); - - system_hard_reset(); -} - -void system_hard_reset(void) { - NVIC_SystemReset(); - __builtin_unreachable(); -} diff --git a/platform/silk/boot/src/system/reset.h b/platform/silk/boot/src/system/reset.h deleted file mode 100644 index 6b5ba28099..0000000000 --- a/platform/silk/boot/src/system/reset.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Reset nicely after shutting down system services. Does not set the reboot_reason other than -//! calling reboot_reason_set_restarted_safely just before the reset occurs. -void system_reset(void)__attribute__((noreturn)); - -//! The final stage in the reset process. -void system_hard_reset(void) __attribute__((noreturn)); diff --git a/platform/silk/boot/src/system/rtc_registers.h b/platform/silk/boot/src/system/rtc_registers.h deleted file mode 100644 index 2b77ef4c01..0000000000 --- a/platform/silk/boot/src/system/rtc_registers.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0 -#define STUCK_BUTTON_REGISTER RTC_BKP_DR1 -#define BOOTLOADER_VERSION_REGISTER RTC_BKP_DR2 -#define CURRENT_TIME_REGISTER RTC_BKP_DR3 -#define CURRENT_INTERVAL_TICKS_REGISTER RTC_BKP_DR4 -#define REBOOT_REASON_REGISTER_1 RTC_BKP_DR5 -#define REBOOT_REASON_REGISTER_2 RTC_BKP_DR6 -#define REBOOT_REASON_STUCK_TASK_PC RTC_BKP_DR7 -#define REBOOT_REASON_STUCK_TASK_LR RTC_BKP_DR8 -#define REBOOT_REASON_STUCK_TASK_CALLBACK RTC_BKP_DR9 -#define REBOOT_REASON_MUTEX_LR RTC_BKP_DR10 -#define REBOOT_REASON_MUTEX_PC RTC_BKP_DR11 // Deprecated -#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19 diff --git a/platform/silk/boot/src/util/attributes.h b/platform/silk/boot/src/util/attributes.h deleted file mode 100644 index 8cd71d62d5..0000000000 --- a/platform/silk/boot/src/util/attributes.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if defined(__clang__) -#define GCC_ONLY(x) -#else -#define GCC_ONLY(x) x -#endif - -// Function attributes -#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST))) - -#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST) - -#define ALWAYS_INLINE __attribute__((__always_inline__)) inline -#define NOINLINE __attribute__((__noinline__)) -#define NORETURN __attribute__((__noreturn__)) void -#define NAKED_FUNC __attribute__((__naked__)) -#define OPTIMIZE_FUNC(LVL) __attribute__((__optimize__(LVL))) -#define CONST_FUNC __attribute__((__const__)) -#define PURE_FUNC __attribute__((__pure__)) - -// Variable attributes -#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC))) - -// Structure attributes -#define PACKED __attribute__((__packed__)) - -// General attributes -#define USED __attribute__((__used__)) -#define UNUSED __attribute__((__unused__)) -#define WEAK __attribute__((__weak__)) -#define ALIAS(sym) __attribute__((__weak__, __alias__(sym))) -#define SECTION(SEC) GCC_ONLY(__attribute__((__section__(SEC)))) -#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__))) diff --git a/platform/silk/boot/src/util/cobs.c b/platform/silk/boot/src/util/cobs.c deleted file mode 100644 index 3579543d52..0000000000 --- a/platform/silk/boot/src/util/cobs.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "cobs.h" - -size_t cobs_encode(void *dst_ptr, const void *src_ptr, size_t length) { - const char *src = src_ptr; - char *dst = dst_ptr; - uint8_t code = 0x01; - size_t code_idx = 0; - size_t dst_idx = 1; - - for (size_t src_idx = 0; src_idx < length; ++src_idx) { - if (src[src_idx] == '\0') { - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } else { - dst[dst_idx++] = src[src_idx]; - code++; - if (code == 0xff) { - if (src_idx == length - 1) { - // Special case: the final encoded block is 254 bytes long with no - // zero after it. While it's technically a valid encoding if a - // trailing zero is appended, it causes the output to be one byte - // longer than it needs to be. This violates consistent overhead - // contract and could overflow a carefully sized buffer. - break; - } - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } - } - } - dst[code_idx] = code; - return dst_idx; -} diff --git a/platform/silk/boot/src/util/cobs.h b/platform/silk/boot/src/util/cobs.h deleted file mode 100644 index 7194512e53..0000000000 --- a/platform/silk/boot/src/util/cobs.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! An implementation of Consistent Overhead Byte Stuffing -//! -//! http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf -//! http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing - -//! Evaluates to the offset required when encoding in-place -#define COBS_OVERHEAD(n) (((n) + 253) / 254) -//! Evaluates to the maximum buffer size required to hold n bytes of data -//! after COBS encoding. -#define MAX_SIZE_AFTER_COBS_ENCODING(n) ((n) + COBS_OVERHEAD(n)) - -//! COBS-encode a buffer out to another buffer. -//! -//! @param [out] dst destination buffer. The buffer must be at least -//! MAX_SIZE_AFTER_COBS_ENCODING(length) bytes long. -//! @param [in] src source buffer -//! @param length length of src -size_t cobs_encode(void * restrict dst, const void * restrict src, - size_t length); diff --git a/platform/silk/boot/src/util/crc32.c b/platform/silk/boot/src/util/crc32.c deleted file mode 100644 index 177b54de8e..0000000000 --- a/platform/silk/boot/src/util/crc32.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/crc32.h" - -// Nybble-wide table driven CRC-32 algorithm -// -// A compromise between speed and size, this algorithm uses a lookup table to -// calculate the CRC four bits at a time with a size cost of only 64 bytes. By -// contrast, a byte-wide algorithm requires a lookup table sixteen times larger! -// -// The lookup table is generated by the crc32_lut.py - -static const uint32_t s_lookup_table[] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, -}; - -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) { - if (data == 0) { - return 0; - } - const uint8_t * restrict bytes = data; - - crc ^= 0xffffffff; - while (length--) { - crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf]; - crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf]; - bytes++; - } - crc ^= 0xffffffff; - return crc; -} diff --git a/platform/silk/boot/src/util/crc32.h b/platform/silk/boot/src/util/crc32.h deleted file mode 100644 index 80ac71ebae..0000000000 --- a/platform/silk/boot/src/util/crc32.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! \file -//! Calculate the CRC-32 checksum of data. -//! -//! The checksum is the standard CRC-32 used by zlib, PNG and others. -//! The model parameters for the algorithm, as described in A Painless Guide to -//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are: -//! Name: "CRC-32" -//! Width: 32 -//! Poly: 04C11DB7 -//! Init: FFFFFFFF -//! RefIn: True -//! RefOut: True -//! XorOut: FFFFFFFF -//! Check: CBF43926 - -#include -#include - -//! Update a running CRC-32 checksum with the bytes of data and return the -//! updated CRC-32. If data is NULL, the function returns the required initial -//! value for the CRC. -//! -//! This function is drop-in compatible with zlib's crc32 function. -//! -//! \par Usage -//! \code -//! uint32_t crc = crc32(0, NULL, 0); -//! while (read_buffer(data, length)) { -//! crc = crc32(crc, data, length); -//! } -//! \endcode -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length); - -//! The initial CRC register value for a standard CRC-32 checksum. -//! -//! It is the same value as is returned by the `crc32` function when data is -//! NULL. -//! -//! \code -//! assert(CRC32_INIT == crc32(0, NULL, 0)); -//! \endcode -#define CRC32_INIT (0) - -//! The residue constant of the CRC-32 algorithm. -//! -//! If the CRC-32 value of a message is appended (little-endian) onto the -//! end of the message, the CRC-32 of the concatenated message and CRC will be -//! equal to CRC32_RESIDUE if the message has not been corrupted in transit. -#define CRC32_RESIDUE (0x2144DF1C) diff --git a/platform/silk/boot/src/util/delay.c b/platform/silk/boot/src/util/delay.c deleted file mode 100644 index f678efd5ee..0000000000 --- a/platform/silk/boot/src/util/delay.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "delay.h" -#include "stm32f4xx.h" -#include "util/attributes.h" - -#include - -// Calculated using the formula from the firmware's delay_init: -// ceil( NS_PER_US / ( clock_period * INSTRUCTIONS_PER_LOOP ) -// where NS_PER_US = 1000 and INSTRUCTIONS_PER_LOOP = 3, -// and clock_period = 62.5 -#define LOOPS_PER_US (6) - -void NOINLINE delay_us(uint32_t us) { - uint32_t delay_loops = us * LOOPS_PER_US; - - __asm volatile ( - "spinloop: \n" - " subs %[delay_loops], #1 \n" - " bne spinloop \n" - : [delay_loops] "+r" (delay_loops) // read-write operand - : - : "cc" - ); -} - -void delay_ms(uint32_t millis) { - // delay_us(millis*1000) is not used because a long delay could easily - // overflow the veriable. Without the outer loop, a delay of even five - // seconds would overflow. - while (millis--) { - delay_us(1000); - } -} diff --git a/platform/silk/boot/src/util/delay.h b/platform/silk/boot/src/util/delay.h deleted file mode 100644 index 371ffd7cd7..0000000000 --- a/platform/silk/boot/src/util/delay.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Carefully timed spinloop that allows one to delay at a microsecond -//! granularity. -void delay_us(uint32_t us); - -//! Waits for a certain amount of milliseconds by busy-waiting. -//! -//! @param millis The number of milliseconds to wait for -void delay_ms(uint32_t millis); diff --git a/platform/silk/boot/src/util/misc.c b/platform/silk/boot/src/util/misc.c deleted file mode 100644 index fbb8db908f..0000000000 --- a/platform/silk/boot/src/util/misc.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "misc.h" - -#include "drivers/dbgserial.h" - -#include - -void itoa_hex(uint32_t num, char *buffer, int buffer_length) { - if (buffer_length < 11) { - dbgserial_putstr("itoa buffer too small"); - return; - } - *buffer++ = '0'; - *buffer++ = 'x'; - - for (int i = 7; i >= 0; --i) { - uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4); - - char c; - if (digit < 0xa) { - c = '0' + digit; - } else if (digit < 0x10) { - c = 'a' + (digit - 0xa); - } else { - c = ' '; - } - - *buffer++ = c; - } - *buffer = '\0'; -} diff --git a/platform/silk/boot/src/util/misc.h b/platform/silk/boot/src/util/misc.h deleted file mode 100644 index c10d175459..0000000000 --- a/platform/silk/boot/src/util/misc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000) - -#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0])) - -//! Find the log base two of a number rounded up -int ceil_log_two(uint32_t n); - -//! Convert num to a hex string and put in buffer -void itoa_hex(uint32_t num, char *buffer, int buffer_length); diff --git a/platform/silk/boot/src/util/net.h b/platform/silk/boot/src/util/net.h deleted file mode 100644 index 3d975cff3e..0000000000 --- a/platform/silk/boot/src/util/net.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// When compiling test, the host OS might have conflicting defines for this: -#undef ntohs -#undef htons -#undef ntohl -#undef htonl -#undef ltohs -#undef ltohl - -static inline uint16_t ntohs(uint16_t v) { - // return ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8); - return __builtin_bswap16(v); -} - -static inline uint16_t htons(uint16_t v) { - return ntohs(v); -} - -static inline uint32_t ntohl(uint32_t v) { - // return ((v & 0x000000ff) << 24) | - // ((v & 0x0000ff00) << 8) | - // ((v & 0x00ff0000) >> 8) | - // ((v & 0xff000000) >> 24); - return __builtin_bswap32(v); -} - -static inline uint32_t htonl(uint32_t v) { - return ntohl(v); -} - -#define ltohs(v) (v) -#define ltohl(v) (v) - -// Types for values in network byte-order. They are wrapped in structs so that -// the compiler will disallow implicit casting of these types to or from -// integral types. This way it is a compile error to try using variables of -// these types without first performing a byte-order conversion. -// There is no overhead for wrapping the values in structs. -typedef struct net16 { - uint16_t v; -} net16; - -typedef struct net32 { - uint32_t v; -} net32; - -static inline uint16_t ntoh16(net16 net) { - return ntohs(net.v); -} - -static inline net16 hton16(uint16_t v) { - return (net16){ htons(v) }; -} - -static inline uint32_t ntoh32(net32 net) { - return ntohl(net.v); -} - -static inline net32 hton32(uint32_t v) { - return (net32){ htonl(v) }; -} diff --git a/platform/silk/boot/test/test_system_flash.c b/platform/silk/boot/test/test_system_flash.c deleted file mode 100644 index caeaaa10a1..0000000000 --- a/platform/silk/boot/test/test_system_flash.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "clar.h" - -#include "drivers/system_flash.h" - -#define KiB *1024 - -// Set bits n..0 in a bit-vector (zero-indexed) -#define BITS(n) ((((uint32_t)(1 << ((n) + 1)))) - 1) -// Set bits y..x in a bit-vector (x <= y; x, y >= 0) -#define BITS_BETWEEN(x, y) (BITS(y) & ~BITS(x-1)) - -// Yo dawg, I heard you like tests so I put tests in your tests so you can test -// your tests while you test! -void test_system_flash__bit_range_macros(void) { - cl_assert_equal_i(0b1, BITS(0)); - cl_assert_equal_i(0b00011111, BITS(4)); - cl_assert_equal_i(0b00111111, BITS_BETWEEN(0, 5)); - cl_assert_equal_i(0b00111000, BITS_BETWEEN(3, 5)); - cl_assert_equal_i(0b00010000, BITS_BETWEEN(4, 4)); -} - -// Flash memory is organized into twelve sectors of unequal sizes. -// Sectors 0-3 are 16 KiB. Sector 4 is 64 KiB. -// The remaining sectors are 128 KiB. - -// Bitset of sectors that have been "erased" -static uint32_t erased_sector; -static bool flash_locked, flash_flags_set; -static FLASH_Status return_status; -uint8_t *flash_written_data; -bool *flash_written_flag; -void *source_buffer; -uint32_t flash_data_start, flash_data_length; -bool callback_called; - -void test_system_flash__initialize(void) { - erased_sector = 0; - flash_locked = true; - flash_flags_set = false; - return_status = FLASH_COMPLETE; - flash_written_data = NULL; - flash_written_flag = NULL; - flash_data_start = 0; - flash_data_length = 0; - callback_called = false; -} - -void test_system_flash__cleanup(void) { - free(flash_written_data); - free(flash_written_flag); -} - -void test_system_flash__erase_zero_bytes(void) { - cl_assert(system_flash_erase(FLASH_BASE, 0, NULL, NULL)); - cl_assert_equal_i(0, erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte_in_middle_of_sector(void) { - cl_assert(system_flash_erase(FLASH_BASE + 12345, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_some_sectors_from_beginning(void) { - cl_assert(system_flash_erase(FLASH_BASE, 128 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 4), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_full_flash(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1024 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 7), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_sector_0(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_16KB_sectors(void) { - cl_assert(system_flash_erase(FLASH_BASE, 48 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 2), erased_sector); - cl_assert(flash_locked); -} - -void callback_is_called_cb(uint32_t num, uint32_t den, void *context) { - callback_called = true; - cl_assert_equal_i(8675309, (uintptr_t)context); -} - -void test_system_flash__callback_is_called(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, callback_is_called_cb, - (void *)8675309)); - cl_assert(callback_called); - cl_assert(flash_locked); -} - -void test_system_flash__handle_erase_error(void) { - return_status = FLASH_ERROR_OPERATION; - cl_assert(!system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert(flash_locked); -} - -void error_in_middle_cb(uint32_t num, uint32_t den, void *context) { - int *countdown = context; - if ((*countdown)-- == 0) { - return_status = FLASH_ERROR_OPERATION; - } -} - -void test_system_flash__handle_erase_error_mid_operation(void) { - int countdown = 3; - cl_assert(!system_flash_erase(FLASH_BASE, 512 KiB, error_in_middle_cb, - &countdown)); - cl_assert(flash_locked); - cl_assert_(countdown <= 0, "Callback not called enough times"); -} - - -void malloc_flash_data(uint32_t size) { - flash_data_length = size; - flash_written_data = malloc(size * sizeof(uint8_t)); - cl_assert(flash_written_data); - flash_written_flag = malloc(size * sizeof(bool)); - cl_assert(flash_written_flag); -} - -void assert_flash_unwritten(uint32_t start, uint32_t length) { - for (uint32_t i = 0; i < length; ++i) { - cl_assert(flash_written_flag[start + i] == false); - } -} - -void test_system_flash__write_simple(void) { - const char testdata[] = "The quick brown fox jumps over the lazy dog."; - malloc_flash_data(100); - flash_data_start = FLASH_BASE; - cl_assert(system_flash_write( - FLASH_BASE + 10, testdata, sizeof(testdata), callback_is_called_cb, - (void *)8675309)); - cl_assert(flash_locked); - cl_assert_equal_s(testdata, (const char *)&flash_written_data[10]); - assert_flash_unwritten(0, 10); - assert_flash_unwritten(10 + sizeof(testdata), 90 - sizeof(testdata)); - cl_assert(callback_called); -} - -void test_system_flash__write_error(void) { - return_status = FLASH_ERROR_OPERATION; - malloc_flash_data(10); - flash_data_start = FLASH_BASE; - cl_assert(!system_flash_write(FLASH_BASE, "abc", 3, NULL, NULL)); - cl_assert(flash_locked); - assert_flash_unwritten(0, 10); -} - -extern void FLASH_Lock(void) { - flash_locked = true; -} - -extern void FLASH_Unlock(void) { - flash_locked = false; -} - -extern void FLASH_ClearFlag(uint32_t FLASH_FLAG) { - flash_flags_set = false; -} - -extern FLASH_Status FLASH_EraseSector(uint32_t sector, uint8_t voltage_range) { - // Pretty sure FLASH_Sector_N defines are simply 8*N, at least for the first - // twelve sectors. - cl_assert_(!flash_locked, "Attempted to erase a locked flash"); - cl_assert_(IS_FLASH_SECTOR(sector), "Sector number out of range"); - cl_assert(IS_VOLTAGERANGE(voltage_range)); - cl_check_(flash_flags_set == false, "Forgot to clear flags before erasing"); - cl_check_((erased_sector & (1 << sector/8)) == 0, - "Re-erasing an already erased sector"); - flash_flags_set = true; - if (return_status == FLASH_COMPLETE) { - erased_sector |= (1 << sector/8); - } - return return_status; -} - -extern FLASH_Status FLASH_ProgramByte(uint32_t address, uint8_t data) { - cl_assert_(!flash_locked, "Attempted to write to a locked flash"); - cl_assert_(address >= flash_data_start && - address < flash_data_start + flash_data_length, - "Address out of range"); - cl_assert_(flash_written_flag[address - flash_data_start] == false, - "Overwriting an already-written byte"); - if (return_status == FLASH_COMPLETE) { - flash_written_data[address - flash_data_start] = data; - } - return return_status; -} - -extern void dbgserial_print(char *str) { - fprintf(stderr, "%s", str); -} - -extern void dbgserial_print_hex(uint32_t num) { - fprintf(stderr, "0x%.08x", num); -} - -extern void dbgserial_putstr(char *str) { - fprintf(stderr, "%s\n", str); -} diff --git a/platform/silk/boot/vendor/STM32F4xx_DSP_StdPeriph_Lib_V1.7.0RC1/Libraries/CMSIS/Include/arm_math.h b/platform/silk/boot/vendor/STM32F4xx_DSP_StdPeriph_Lib_V1.7.0RC1/Libraries/CMSIS/Include/arm_math.h deleted file mode 100644 index 6dd430dd80..0000000000 --- a/platform/silk/boot/vendor/STM32F4xx_DSP_StdPeriph_Lib_V1.7.0RC1/Libraries/CMSIS/Include/arm_math.h +++ /dev/null @@ -1,7556 +0,0 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2015 ARM Limited. All rights reserved. -* -* $Date: 19. March 2015 -* $Revision: V.1.4.5 -* -* Project: CMSIS DSP Library -* Title: arm_math.h -* -* Description: Public header file for CMSIS DSP Library -* -* Target Processor: Cortex-M7/Cortex-M4/Cortex-M3/Cortex-M0 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. - * -------------------------------------------------------------------- */ - -/** - \mainpage CMSIS DSP Software Library - * - * Introduction - * ------------ - * - * This user manual describes the CMSIS DSP software library, - * a suite of common signal processing functions for use on Cortex-M processor based devices. - * - * The library is divided into a number of functions each covering a specific category: - * - Basic math functions - * - Fast math functions - * - Complex math functions - * - Filters - * - Matrix functions - * - Transforms - * - Motor control functions - * - Statistical functions - * - Support functions - * - Interpolation functions - * - * The library has separate functions for operating on 8-bit integers, 16-bit integers, - * 32-bit integer and 32-bit floating-point values. - * - * Using the Library - * ------------ - * - * The library installer contains prebuilt versions of the libraries in the Lib folder. - * - arm_cortexM7lfdp_math.lib (Little endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfdp_math.lib (Big endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7lfsp_math.lib (Little endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfsp_math.lib (Big endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7l_math.lib (Little endian on Cortex-M7) - * - arm_cortexM7b_math.lib (Big endian on Cortex-M7) - * - arm_cortexM4lf_math.lib (Little endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4bf_math.lib (Big endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4l_math.lib (Little endian on Cortex-M4) - * - arm_cortexM4b_math.lib (Big endian on Cortex-M4) - * - arm_cortexM3l_math.lib (Little endian on Cortex-M3) - * - arm_cortexM3b_math.lib (Big endian on Cortex-M3) - * - arm_cortexM0l_math.lib (Little endian on Cortex-M0 / CortexM0+) - * - arm_cortexM0b_math.lib (Big endian on Cortex-M0 / CortexM0+) - * - * The library functions are declared in the public file arm_math.h which is placed in the Include folder. - * Simply include this file and link the appropriate library in the application and begin calling the library functions. The Library supports single - * public header file arm_math.h for Cortex-M7/M4/M3/M0/M0+ with little endian and big endian. Same header file will be used for floating point unit(FPU) variants. - * Define the appropriate pre processor MACRO ARM_MATH_CM7 or ARM_MATH_CM4 or ARM_MATH_CM3 or - * ARM_MATH_CM0 or ARM_MATH_CM0PLUS depending on the target processor in the application. - * - * Examples - * -------- - * - * The library ships with a number of examples which demonstrate how to use the library functions. - * - * Toolchain Support - * ------------ - * - * The library has been developed and tested with MDK-ARM version 5.14.0.0 - * The library is being tested in GCC and IAR toolchains and updates on this activity will be made available shortly. - * - * Building the Library - * ------------ - * - * The library installer contains a project file to re build libraries on MDK-ARM Tool chain in the CMSIS\\DSP_Lib\\Source\\ARM folder. - * - arm_cortexM_math.uvprojx - * - * - * The libraries can be built by opening the arm_cortexM_math.uvprojx project in MDK-ARM, selecting a specific target, and defining the optional pre processor MACROs detailed above. - * - * Pre-processor Macros - * ------------ - * - * Each library project have differant pre-processor macros. - * - * - UNALIGNED_SUPPORT_DISABLE: - * - * Define macro UNALIGNED_SUPPORT_DISABLE, If the silicon does not support unaligned memory access - * - * - ARM_MATH_BIG_ENDIAN: - * - * Define macro ARM_MATH_BIG_ENDIAN to build the library for big endian targets. By default library builds for little endian targets. - * - * - ARM_MATH_MATRIX_CHECK: - * - * Define macro ARM_MATH_MATRIX_CHECK for checking on the input and output sizes of matrices - * - * - ARM_MATH_ROUNDING: - * - * Define macro ARM_MATH_ROUNDING for rounding on support functions - * - * - ARM_MATH_CMx: - * - * Define macro ARM_MATH_CM4 for building the library on Cortex-M4 target, ARM_MATH_CM3 for building library on Cortex-M3 target - * and ARM_MATH_CM0 for building library on Cortex-M0 target, ARM_MATH_CM0PLUS for building library on Cortex-M0+ target, and - * ARM_MATH_CM7 for building the library on cortex-M7. - * - * - __FPU_PRESENT: - * - * Initialize macro __FPU_PRESENT = 1 when building on FPU supported Targets. Enable this macro for M4bf and M4lf libraries - * - *
    - * CMSIS-DSP in ARM::CMSIS Pack - * ----------------------------- - * - * The following files relevant to CMSIS-DSP are present in the ARM::CMSIS Pack directories: - * |File/Folder |Content | - * |------------------------------|------------------------------------------------------------------------| - * |\b CMSIS\\Documentation\\DSP | This documentation | - * |\b CMSIS\\DSP_Lib | Software license agreement (license.txt) | - * |\b CMSIS\\DSP_Lib\\Examples | Example projects demonstrating the usage of the library functions | - * |\b CMSIS\\DSP_Lib\\Source | Source files for rebuilding the library | - * - *
    - * Revision History of CMSIS-DSP - * ------------ - * Please refer to \ref ChangeLog_pg. - * - * Copyright Notice - * ------------ - * - * Copyright (C) 2010-2015 ARM Limited. All rights reserved. - */ - - -/** - * @defgroup groupMath Basic Math Functions - */ - -/** - * @defgroup groupFastMath Fast Math Functions - * This set of functions provides a fast approximation to sine, cosine, and square root. - * As compared to most of the other functions in the CMSIS math library, the fast math functions - * operate on individual values and not arrays. - * There are separate functions for Q15, Q31, and floating-point data. - * - */ - -/** - * @defgroup groupCmplxMath Complex Math Functions - * This set of functions operates on complex data vectors. - * The data in the complex arrays is stored in an interleaved fashion - * (real, imag, real, imag, ...). - * In the API functions, the number of samples in a complex array refers - * to the number of complex values; the array contains twice this number of - * real values. - */ - -/** - * @defgroup groupFilters Filtering Functions - */ - -/** - * @defgroup groupMatrix Matrix Functions - * - * This set of functions provides basic matrix math operations. - * The functions operate on matrix data structures. For example, - * the type - * definition for the floating-point matrix structure is shown - * below: - *
    - *     typedef struct
    - *     {
    - *       uint16_t numRows;     // number of rows of the matrix.
    - *       uint16_t numCols;     // number of columns of the matrix.
    - *       float32_t *pData;     // points to the data of the matrix.
    - *     } arm_matrix_instance_f32;
    - * 
    - * There are similar definitions for Q15 and Q31 data types. - * - * The structure specifies the size of the matrix and then points to - * an array of data. The array is of size numRows X numCols - * and the values are arranged in row order. That is, the - * matrix element (i, j) is stored at: - *
    - *     pData[i*numCols + j]
    - * 
    - * - * \par Init Functions - * There is an associated initialization function for each type of matrix - * data structure. - * The initialization function sets the values of the internal structure fields. - * Refer to the function arm_mat_init_f32(), arm_mat_init_q31() - * and arm_mat_init_q15() for floating-point, Q31 and Q15 types, respectively. - * - * \par - * Use of the initialization function is optional. However, if initialization function is used - * then the instance structure cannot be placed into a const data section. - * To place the instance structure in a const data - * section, manually initialize the data structure. For example: - *
    - * arm_matrix_instance_f32 S = {nRows, nColumns, pData};
    - * arm_matrix_instance_q31 S = {nRows, nColumns, pData};
    - * arm_matrix_instance_q15 S = {nRows, nColumns, pData};
    - * 
    - * where nRows specifies the number of rows, nColumns - * specifies the number of columns, and pData points to the - * data array. - * - * \par Size Checking - * By default all of the matrix functions perform size checking on the input and - * output matrices. For example, the matrix addition function verifies that the - * two input matrices and the output matrix all have the same number of rows and - * columns. If the size check fails the functions return: - *
    - *     ARM_MATH_SIZE_MISMATCH
    - * 
    - * Otherwise the functions return - *
    - *     ARM_MATH_SUCCESS
    - * 
    - * There is some overhead associated with this matrix size checking. - * The matrix size checking is enabled via the \#define - *
    - *     ARM_MATH_MATRIX_CHECK
    - * 
    - * within the library project settings. By default this macro is defined - * and size checking is enabled. By changing the project settings and - * undefining this macro size checking is eliminated and the functions - * run a bit faster. With size checking disabled the functions always - * return ARM_MATH_SUCCESS. - */ - -/** - * @defgroup groupTransforms Transform Functions - */ - -/** - * @defgroup groupController Controller Functions - */ - -/** - * @defgroup groupStats Statistics Functions - */ -/** - * @defgroup groupSupport Support Functions - */ - -/** - * @defgroup groupInterpolation Interpolation Functions - * These functions perform 1- and 2-dimensional interpolation of data. - * Linear interpolation is used for 1-dimensional data and - * bilinear interpolation is used for 2-dimensional data. - */ - -/** - * @defgroup groupExamples Examples - */ -#ifndef _ARM_MATH_H -#define _ARM_MATH_H - -#define __CMSIS_GENERIC /* disable NVIC and Systick functions */ - -#if defined(ARM_MATH_CM7) - #include "core_cm7.h" -#elif defined (ARM_MATH_CM4) - #include "core_cm4.h" -#elif defined (ARM_MATH_CM3) - #include "core_cm3.h" -#elif defined (ARM_MATH_CM0) - #include "core_cm0.h" -#define ARM_MATH_CM0_FAMILY - #elif defined (ARM_MATH_CM0PLUS) -#include "core_cm0plus.h" - #define ARM_MATH_CM0_FAMILY -#else - #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0" -#endif - -#undef __CMSIS_GENERIC /* enable NVIC and Systick functions */ -#include "string.h" -#include "math.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - - /** - * @brief Macros required for reciprocal calculation in Normalized LMS - */ - -#define DELTA_Q31 (0x100) -#define DELTA_Q15 0x5 -#define INDEX_MASK 0x0000003F -#ifndef PI -#define PI 3.14159265358979f -#endif - - /** - * @brief Macros required for SINE and COSINE Fast math approximations - */ - -#define FAST_MATH_TABLE_SIZE 512 -#define FAST_MATH_Q31_SHIFT (32 - 10) -#define FAST_MATH_Q15_SHIFT (16 - 10) -#define CONTROLLER_Q31_SHIFT (32 - 9) -#define TABLE_SIZE 256 -#define TABLE_SPACING_Q31 0x400000 -#define TABLE_SPACING_Q15 0x80 - - /** - * @brief Macros required for SINE and COSINE Controller functions - */ - /* 1.31(q31) Fixed value of 2/360 */ - /* -1 to +1 is divided into 360 values so total spacing is (2/360) */ -#define INPUT_SPACING 0xB60B61 - - /** - * @brief Macro for Unaligned Support - */ -#ifndef UNALIGNED_SUPPORT_DISABLE - #define ALIGN4 -#else - #if defined (__GNUC__) - #define ALIGN4 __attribute__((aligned(4))) - #else - #define ALIGN4 __align(4) - #endif -#endif /* #ifndef UNALIGNED_SUPPORT_DISABLE */ - - /** - * @brief Error status returned by some functions in the library. - */ - - typedef enum - { - ARM_MATH_SUCCESS = 0, /**< No error */ - ARM_MATH_ARGUMENT_ERROR = -1, /**< One or more arguments are incorrect */ - ARM_MATH_LENGTH_ERROR = -2, /**< Length of data buffer is incorrect */ - ARM_MATH_SIZE_MISMATCH = -3, /**< Size of matrices is not compatible with the operation. */ - ARM_MATH_NANINF = -4, /**< Not-a-number (NaN) or infinity is generated */ - ARM_MATH_SINGULAR = -5, /**< Generated by matrix inversion if the input matrix is singular and cannot be inverted. */ - ARM_MATH_TEST_FAILURE = -6 /**< Test Failed */ - } arm_status; - - /** - * @brief 8-bit fractional data type in 1.7 format. - */ - typedef int8_t q7_t; - - /** - * @brief 16-bit fractional data type in 1.15 format. - */ - typedef int16_t q15_t; - - /** - * @brief 32-bit fractional data type in 1.31 format. - */ - typedef int32_t q31_t; - - /** - * @brief 64-bit fractional data type in 1.63 format. - */ - typedef int64_t q63_t; - - /** - * @brief 32-bit floating-point type definition. - */ - typedef float float32_t; - - /** - * @brief 64-bit floating-point type definition. - */ - typedef double float64_t; - - /** - * @brief definition to read/write two 16 bit values. - */ -#if defined __CC_ARM - #define __SIMD32_TYPE int32_t __packed - #define CMSIS_UNUSED __attribute__((unused)) -#elif defined __ICCARM__ - #define __SIMD32_TYPE int32_t __packed - #define CMSIS_UNUSED -#elif defined __GNUC__ - #define __SIMD32_TYPE int32_t - #define CMSIS_UNUSED __attribute__((unused)) -#elif defined __CSMC__ /* Cosmic */ - #define __SIMD32_TYPE int32_t - #define CMSIS_UNUSED -#elif defined __TASKING__ - #define __SIMD32_TYPE __unaligned int32_t - #define CMSIS_UNUSED -#else - #error Unknown compiler -#endif - -#define __SIMD32(addr) (*(__SIMD32_TYPE **) & (addr)) -#define __SIMD32_CONST(addr) ((__SIMD32_TYPE *)(addr)) - -#define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr)) - -#define __SIMD64(addr) (*(int64_t **) & (addr)) - -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) - /** - * @brief definition to pack two 16 bit values. - */ -#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | \ - (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) ) -#define __PKHTB(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0xFFFF0000) | \ - (((int32_t)(ARG2) >> ARG3) & (int32_t)0x0000FFFF) ) - -#endif - - - /** - * @brief definition to pack four 8 bit values. - */ -#ifndef ARM_MATH_BIG_ENDIAN - -#define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v0) << 0) & (int32_t)0x000000FF) | \ - (((int32_t)(v1) << 8) & (int32_t)0x0000FF00) | \ - (((int32_t)(v2) << 16) & (int32_t)0x00FF0000) | \ - (((int32_t)(v3) << 24) & (int32_t)0xFF000000) ) -#else - -#define __PACKq7(v0,v1,v2,v3) ( (((int32_t)(v3) << 0) & (int32_t)0x000000FF) | \ - (((int32_t)(v2) << 8) & (int32_t)0x0000FF00) | \ - (((int32_t)(v1) << 16) & (int32_t)0x00FF0000) | \ - (((int32_t)(v0) << 24) & (int32_t)0xFF000000) ) - -#endif - - - /** - * @brief Clips Q63 to Q31 values. - */ - static __INLINE q31_t clip_q63_to_q31( - q63_t x) - { - return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? - ((0x7FFFFFFF ^ ((q31_t) (x >> 63)))) : (q31_t) x; - } - - /** - * @brief Clips Q63 to Q15 values. - */ - static __INLINE q15_t clip_q63_to_q15( - q63_t x) - { - return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? - ((0x7FFF ^ ((q15_t) (x >> 63)))) : (q15_t) (x >> 15); - } - - /** - * @brief Clips Q31 to Q7 values. - */ - static __INLINE q7_t clip_q31_to_q7( - q31_t x) - { - return ((q31_t) (x >> 24) != ((q31_t) x >> 23)) ? - ((0x7F ^ ((q7_t) (x >> 31)))) : (q7_t) x; - } - - /** - * @brief Clips Q31 to Q15 values. - */ - static __INLINE q15_t clip_q31_to_q15( - q31_t x) - { - return ((q31_t) (x >> 16) != ((q31_t) x >> 15)) ? - ((0x7FFF ^ ((q15_t) (x >> 31)))) : (q15_t) x; - } - - /** - * @brief Multiplies 32 X 64 and returns 32 bit result in 2.30 format. - */ - - static __INLINE q63_t mult32x64( - q63_t x, - q31_t y) - { - return ((((q63_t) (x & 0x00000000FFFFFFFF) * y) >> 32) + - (((q63_t) (x >> 32) * y))); - } - - -//#if defined (ARM_MATH_CM0_FAMILY) && defined ( __CC_ARM ) -//#define __CLZ __clz -//#endif - -//note: function can be removed when all toolchain support __CLZ for Cortex-M0 -#if defined (ARM_MATH_CM0_FAMILY) && ((defined (__ICCARM__)) ) - - static __INLINE uint32_t __CLZ( - q31_t data); - - - static __INLINE uint32_t __CLZ( - q31_t data) - { - uint32_t count = 0; - uint32_t mask = 0x80000000; - - while((data & mask) == 0) - { - count += 1u; - mask = mask >> 1u; - } - - return (count); - - } - -#endif - - /** - * @brief Function to Calculates 1/in (reciprocal) value of Q31 Data type. - */ - - static __INLINE uint32_t arm_recip_q31( - q31_t in, - q31_t * dst, - q31_t * pRecipTable) - { - - uint32_t out, tempVal; - uint32_t index, i; - uint32_t signBits; - - if(in > 0) - { - signBits = __CLZ(in) - 1; - } - else - { - signBits = __CLZ(-in) - 1; - } - - /* Convert input sample to 1.31 format */ - in = in << signBits; - - /* calculation of index for initial approximated Val */ - index = (uint32_t) (in >> 24u); - index = (index & INDEX_MASK); - - /* 1.31 with exp 1 */ - out = pRecipTable[index]; - - /* calculation of reciprocal value */ - /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) - { - tempVal = (q31_t) (((q63_t) in * out) >> 31u); - tempVal = 0x7FFFFFFF - tempVal; - /* 1.31 with exp 1 */ - //out = (q31_t) (((q63_t) out * tempVal) >> 30u); - out = (q31_t) clip_q63_to_q31(((q63_t) out * tempVal) >> 30u); - } - - /* write output */ - *dst = out; - - /* return num of signbits of out = 1/in value */ - return (signBits + 1u); - - } - - /** - * @brief Function to Calculates 1/in (reciprocal) value of Q15 Data type. - */ - static __INLINE uint32_t arm_recip_q15( - q15_t in, - q15_t * dst, - q15_t * pRecipTable) - { - - uint32_t out = 0, tempVal = 0; - uint32_t index = 0, i = 0; - uint32_t signBits = 0; - - if(in > 0) - { - signBits = __CLZ(in) - 17; - } - else - { - signBits = __CLZ(-in) - 17; - } - - /* Convert input sample to 1.15 format */ - in = in << signBits; - - /* calculation of index for initial approximated Val */ - index = in >> 8; - index = (index & INDEX_MASK); - - /* 1.15 with exp 1 */ - out = pRecipTable[index]; - - /* calculation of reciprocal value */ - /* running approximation for two iterations */ - for (i = 0; i < 2; i++) - { - tempVal = (q15_t) (((q31_t) in * out) >> 15); - tempVal = 0x7FFF - tempVal; - /* 1.15 with exp 1 */ - out = (q15_t) (((q31_t) out * tempVal) >> 14); - } - - /* write output */ - *dst = out; - - /* return num of signbits of out = 1/in value */ - return (signBits + 1); - - } - - - /* - * @brief C custom defined intrinisic function for only M0 processors - */ -#if defined(ARM_MATH_CM0_FAMILY) - - static __INLINE q31_t __SSAT( - q31_t x, - uint32_t y) - { - int32_t posMax, negMin; - uint32_t i; - - posMax = 1; - for (i = 0; i < (y - 1); i++) - { - posMax = posMax * 2; - } - - if(x > 0) - { - posMax = (posMax - 1); - - if(x > posMax) - { - x = posMax; - } - } - else - { - negMin = -posMax; - - if(x < negMin) - { - x = negMin; - } - } - return (x); - - - } - -#endif /* end of ARM_MATH_CM0_FAMILY */ - - - - /* - * @brief C custom defined intrinsic function for M3 and M0 processors - */ -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) - - /* - * @brief C custom defined QADD8 for M3 and M0 processors - */ - static __INLINE q31_t __QADD8( - q31_t x, - q31_t y) - { - - q31_t sum; - q7_t r, s, t, u; - - r = (q7_t) x; - s = (q7_t) y; - - r = __SSAT((q31_t) (r + s), 8); - s = __SSAT(((q31_t) (((x << 16) >> 24) + ((y << 16) >> 24))), 8); - t = __SSAT(((q31_t) (((x << 8) >> 24) + ((y << 8) >> 24))), 8); - u = __SSAT(((q31_t) ((x >> 24) + (y >> 24))), 8); - - sum = - (((q31_t) u << 24) & 0xFF000000) | (((q31_t) t << 16) & 0x00FF0000) | - (((q31_t) s << 8) & 0x0000FF00) | (r & 0x000000FF); - - return sum; - - } - - /* - * @brief C custom defined QSUB8 for M3 and M0 processors - */ - static __INLINE q31_t __QSUB8( - q31_t x, - q31_t y) - { - - q31_t sum; - q31_t r, s, t, u; - - r = (q7_t) x; - s = (q7_t) y; - - r = __SSAT((r - s), 8); - s = __SSAT(((q31_t) (((x << 16) >> 24) - ((y << 16) >> 24))), 8) << 8; - t = __SSAT(((q31_t) (((x << 8) >> 24) - ((y << 8) >> 24))), 8) << 16; - u = __SSAT(((q31_t) ((x >> 24) - (y >> 24))), 8) << 24; - - sum = - (u & 0xFF000000) | (t & 0x00FF0000) | (s & 0x0000FF00) | (r & - 0x000000FF); - - return sum; - } - - /* - * @brief C custom defined QADD16 for M3 and M0 processors - */ - - /* - * @brief C custom defined QADD16 for M3 and M0 processors - */ - static __INLINE q31_t __QADD16( - q31_t x, - q31_t y) - { - - q31_t sum; - q31_t r, s; - - r = (q15_t) x; - s = (q15_t) y; - - r = __SSAT(r + s, 16); - s = __SSAT(((q31_t) ((x >> 16) + (y >> 16))), 16) << 16; - - sum = (s & 0xFFFF0000) | (r & 0x0000FFFF); - - return sum; - - } - - /* - * @brief C custom defined SHADD16 for M3 and M0 processors - */ - static __INLINE q31_t __SHADD16( - q31_t x, - q31_t y) - { - - q31_t sum; - q31_t r, s; - - r = (q15_t) x; - s = (q15_t) y; - - r = ((r >> 1) + (s >> 1)); - s = ((q31_t) ((x >> 17) + (y >> 17))) << 16; - - sum = (s & 0xFFFF0000) | (r & 0x0000FFFF); - - return sum; - - } - - /* - * @brief C custom defined QSUB16 for M3 and M0 processors - */ - static __INLINE q31_t __QSUB16( - q31_t x, - q31_t y) - { - - q31_t sum; - q31_t r, s; - - r = (q15_t) x; - s = (q15_t) y; - - r = __SSAT(r - s, 16); - s = __SSAT(((q31_t) ((x >> 16) - (y >> 16))), 16) << 16; - - sum = (s & 0xFFFF0000) | (r & 0x0000FFFF); - - return sum; - } - - /* - * @brief C custom defined SHSUB16 for M3 and M0 processors - */ - static __INLINE q31_t __SHSUB16( - q31_t x, - q31_t y) - { - - q31_t diff; - q31_t r, s; - - r = (q15_t) x; - s = (q15_t) y; - - r = ((r >> 1) - (s >> 1)); - s = (((x >> 17) - (y >> 17)) << 16); - - diff = (s & 0xFFFF0000) | (r & 0x0000FFFF); - - return diff; - } - - /* - * @brief C custom defined QASX for M3 and M0 processors - */ - static __INLINE q31_t __QASX( - q31_t x, - q31_t y) - { - - q31_t sum = 0; - - sum = - ((sum + - clip_q31_to_q15((q31_t) ((q15_t) (x >> 16) + (q15_t) y))) << 16) + - clip_q31_to_q15((q31_t) ((q15_t) x - (q15_t) (y >> 16))); - - return sum; - } - - /* - * @brief C custom defined SHASX for M3 and M0 processors - */ - static __INLINE q31_t __SHASX( - q31_t x, - q31_t y) - { - - q31_t sum; - q31_t r, s; - - r = (q15_t) x; - s = (q15_t) y; - - r = ((r >> 1) - (y >> 17)); - s = (((x >> 17) + (s >> 1)) << 16); - - sum = (s & 0xFFFF0000) | (r & 0x0000FFFF); - - return sum; - } - - - /* - * @brief C custom defined QSAX for M3 and M0 processors - */ - static __INLINE q31_t __QSAX( - q31_t x, - q31_t y) - { - - q31_t sum = 0; - - sum = - ((sum + - clip_q31_to_q15((q31_t) ((q15_t) (x >> 16) - (q15_t) y))) << 16) + - clip_q31_to_q15((q31_t) ((q15_t) x + (q15_t) (y >> 16))); - - return sum; - } - - /* - * @brief C custom defined SHSAX for M3 and M0 processors - */ - static __INLINE q31_t __SHSAX( - q31_t x, - q31_t y) - { - - q31_t sum; - q31_t r, s; - - r = (q15_t) x; - s = (q15_t) y; - - r = ((r >> 1) + (y >> 17)); - s = (((x >> 17) - (s >> 1)) << 16); - - sum = (s & 0xFFFF0000) | (r & 0x0000FFFF); - - return sum; - } - - /* - * @brief C custom defined SMUSDX for M3 and M0 processors - */ - static __INLINE q31_t __SMUSDX( - q31_t x, - q31_t y) - { - - return ((q31_t) (((q15_t) x * (q15_t) (y >> 16)) - - ((q15_t) (x >> 16) * (q15_t) y))); - } - - /* - * @brief C custom defined SMUADX for M3 and M0 processors - */ - static __INLINE q31_t __SMUADX( - q31_t x, - q31_t y) - { - - return ((q31_t) (((q15_t) x * (q15_t) (y >> 16)) + - ((q15_t) (x >> 16) * (q15_t) y))); - } - - /* - * @brief C custom defined QADD for M3 and M0 processors - */ - static __INLINE q31_t __QADD( - q31_t x, - q31_t y) - { - return clip_q63_to_q31((q63_t) x + y); - } - - /* - * @brief C custom defined QSUB for M3 and M0 processors - */ - static __INLINE q31_t __QSUB( - q31_t x, - q31_t y) - { - return clip_q63_to_q31((q63_t) x - y); - } - - /* - * @brief C custom defined SMLAD for M3 and M0 processors - */ - static __INLINE q31_t __SMLAD( - q31_t x, - q31_t y, - q31_t sum) - { - - return (sum + ((q15_t) (x >> 16) * (q15_t) (y >> 16)) + - ((q15_t) x * (q15_t) y)); - } - - /* - * @brief C custom defined SMLADX for M3 and M0 processors - */ - static __INLINE q31_t __SMLADX( - q31_t x, - q31_t y, - q31_t sum) - { - - return (sum + ((q15_t) (x >> 16) * (q15_t) (y)) + - ((q15_t) x * (q15_t) (y >> 16))); - } - - /* - * @brief C custom defined SMLSDX for M3 and M0 processors - */ - static __INLINE q31_t __SMLSDX( - q31_t x, - q31_t y, - q31_t sum) - { - - return (sum - ((q15_t) (x >> 16) * (q15_t) (y)) + - ((q15_t) x * (q15_t) (y >> 16))); - } - - /* - * @brief C custom defined SMLALD for M3 and M0 processors - */ - static __INLINE q63_t __SMLALD( - q31_t x, - q31_t y, - q63_t sum) - { - - return (sum + ((q15_t) (x >> 16) * (q15_t) (y >> 16)) + - ((q15_t) x * (q15_t) y)); - } - - /* - * @brief C custom defined SMLALDX for M3 and M0 processors - */ - static __INLINE q63_t __SMLALDX( - q31_t x, - q31_t y, - q63_t sum) - { - - return (sum + ((q15_t) (x >> 16) * (q15_t) y)) + - ((q15_t) x * (q15_t) (y >> 16)); - } - - /* - * @brief C custom defined SMUAD for M3 and M0 processors - */ - static __INLINE q31_t __SMUAD( - q31_t x, - q31_t y) - { - - return (((x >> 16) * (y >> 16)) + - (((x << 16) >> 16) * ((y << 16) >> 16))); - } - - /* - * @brief C custom defined SMUSD for M3 and M0 processors - */ - static __INLINE q31_t __SMUSD( - q31_t x, - q31_t y) - { - - return (-((x >> 16) * (y >> 16)) + - (((x << 16) >> 16) * ((y << 16) >> 16))); - } - - - /* - * @brief C custom defined SXTB16 for M3 and M0 processors - */ - static __INLINE q31_t __SXTB16( - q31_t x) - { - - return ((((x << 24) >> 24) & 0x0000FFFF) | - (((x << 8) >> 8) & 0xFFFF0000)); - } - - -#endif /* defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) */ - - - /** - * @brief Instance structure for the Q7 FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - q7_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q7_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - } arm_fir_instance_q7; - - /** - * @brief Instance structure for the Q15 FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - } arm_fir_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - } arm_fir_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of filter coefficients in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - } arm_fir_instance_f32; - - - /** - * @brief Processing function for the Q7 FIR filter. - * @param[in] *S points to an instance of the Q7 FIR filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_q7( - const arm_fir_instance_q7 * S, - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q7 FIR filter. - * @param[in,out] *S points to an instance of the Q7 FIR structure. - * @param[in] numTaps Number of filter coefficients in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of samples that are processed. - * @return none - */ - void arm_fir_init_q7( - arm_fir_instance_q7 * S, - uint16_t numTaps, - q7_t * pCoeffs, - q7_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 FIR filter. - * @param[in] *S points to an instance of the Q15 FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_q15( - const arm_fir_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Processing function for the fast Q15 FIR filter for Cortex-M3 and Cortex-M4. - * @param[in] *S points to an instance of the Q15 FIR filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_fast_q15( - const arm_fir_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q15 FIR filter. - * @param[in,out] *S points to an instance of the Q15 FIR filter structure. - * @param[in] numTaps Number of filter coefficients in the filter. Must be even and greater than or equal to 4. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of samples that are processed at a time. - * @return The function returns ARM_MATH_SUCCESS if initialization was successful or ARM_MATH_ARGUMENT_ERROR if - * numTaps is not a supported value. - */ - - arm_status arm_fir_init_q15( - arm_fir_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - uint32_t blockSize); - - /** - * @brief Processing function for the Q31 FIR filter. - * @param[in] *S points to an instance of the Q31 FIR filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_q31( - const arm_fir_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Processing function for the fast Q31 FIR filter for Cortex-M3 and Cortex-M4. - * @param[in] *S points to an instance of the Q31 FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_fast_q31( - const arm_fir_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q31 FIR filter. - * @param[in,out] *S points to an instance of the Q31 FIR structure. - * @param[in] numTaps Number of filter coefficients in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of samples that are processed at a time. - * @return none. - */ - void arm_fir_init_q31( - arm_fir_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - uint32_t blockSize); - - /** - * @brief Processing function for the floating-point FIR filter. - * @param[in] *S points to an instance of the floating-point FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_f32( - const arm_fir_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the floating-point FIR filter. - * @param[in,out] *S points to an instance of the floating-point FIR filter structure. - * @param[in] numTaps Number of filter coefficients in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of samples that are processed at a time. - * @return none. - */ - void arm_fir_init_f32( - arm_fir_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q15 Biquad cascade filter. - */ - typedef struct - { - int8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - q15_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */ - q15_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages. */ - int8_t postShift; /**< Additional shift, in bits, applied to each output sample. */ - - } arm_biquad_casd_df1_inst_q15; - - - /** - * @brief Instance structure for the Q31 Biquad cascade filter. - */ - typedef struct - { - uint32_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - q31_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */ - q31_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages. */ - uint8_t postShift; /**< Additional shift, in bits, applied to each output sample. */ - - } arm_biquad_casd_df1_inst_q31; - - /** - * @brief Instance structure for the floating-point Biquad cascade filter. - */ - typedef struct - { - uint32_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float32_t *pState; /**< Points to the array of state coefficients. The array is of length 4*numStages. */ - float32_t *pCoeffs; /**< Points to the array of coefficients. The array is of length 5*numStages. */ - - - } arm_biquad_casd_df1_inst_f32; - - - - /** - * @brief Processing function for the Q15 Biquad cascade filter. - * @param[in] *S points to an instance of the Q15 Biquad cascade structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df1_q15( - const arm_biquad_casd_df1_inst_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q15 Biquad cascade filter. - * @param[in,out] *S points to an instance of the Q15 Biquad cascade structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] postShift Shift to be applied to the output. Varies according to the coefficients format - * @return none - */ - - void arm_biquad_cascade_df1_init_q15( - arm_biquad_casd_df1_inst_q15 * S, - uint8_t numStages, - q15_t * pCoeffs, - q15_t * pState, - int8_t postShift); - - - /** - * @brief Fast but less precise processing function for the Q15 Biquad cascade filter for Cortex-M3 and Cortex-M4. - * @param[in] *S points to an instance of the Q15 Biquad cascade structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df1_fast_q15( - const arm_biquad_casd_df1_inst_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 Biquad cascade filter - * @param[in] *S points to an instance of the Q31 Biquad cascade structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df1_q31( - const arm_biquad_casd_df1_inst_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Fast but less precise processing function for the Q31 Biquad cascade filter for Cortex-M3 and Cortex-M4. - * @param[in] *S points to an instance of the Q31 Biquad cascade structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df1_fast_q31( - const arm_biquad_casd_df1_inst_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q31 Biquad cascade filter. - * @param[in,out] *S points to an instance of the Q31 Biquad cascade structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] postShift Shift to be applied to the output. Varies according to the coefficients format - * @return none - */ - - void arm_biquad_cascade_df1_init_q31( - arm_biquad_casd_df1_inst_q31 * S, - uint8_t numStages, - q31_t * pCoeffs, - q31_t * pState, - int8_t postShift); - - /** - * @brief Processing function for the floating-point Biquad cascade filter. - * @param[in] *S points to an instance of the floating-point Biquad cascade structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df1_f32( - const arm_biquad_casd_df1_inst_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the floating-point Biquad cascade filter. - * @param[in,out] *S points to an instance of the floating-point Biquad cascade structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @return none - */ - - void arm_biquad_cascade_df1_init_f32( - arm_biquad_casd_df1_inst_f32 * S, - uint8_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Instance structure for the floating-point matrix structure. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - float32_t *pData; /**< points to the data of the matrix. */ - } arm_matrix_instance_f32; - - - /** - * @brief Instance structure for the floating-point matrix structure. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - float64_t *pData; /**< points to the data of the matrix. */ - } arm_matrix_instance_f64; - - /** - * @brief Instance structure for the Q15 matrix structure. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - q15_t *pData; /**< points to the data of the matrix. */ - - } arm_matrix_instance_q15; - - /** - * @brief Instance structure for the Q31 matrix structure. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows of the matrix. */ - uint16_t numCols; /**< number of columns of the matrix. */ - q31_t *pData; /**< points to the data of the matrix. */ - - } arm_matrix_instance_q31; - - - - /** - * @brief Floating-point matrix addition. - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_add_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - /** - * @brief Q15 matrix addition. - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_add_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst); - - /** - * @brief Q31 matrix addition. - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_add_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - /** - * @brief Floating-point, complex, matrix multiplication. - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_cmplx_mult_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - /** - * @brief Q15, complex, matrix multiplication. - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_cmplx_mult_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst, - q15_t * pScratch); - - /** - * @brief Q31, complex, matrix multiplication. - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_cmplx_mult_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix transpose. - * @param[in] *pSrc points to the input matrix - * @param[out] *pDst points to the output matrix - * @return The function returns either ARM_MATH_SIZE_MISMATCH - * or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_trans_f32( - const arm_matrix_instance_f32 * pSrc, - arm_matrix_instance_f32 * pDst); - - - /** - * @brief Q15 matrix transpose. - * @param[in] *pSrc points to the input matrix - * @param[out] *pDst points to the output matrix - * @return The function returns either ARM_MATH_SIZE_MISMATCH - * or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_trans_q15( - const arm_matrix_instance_q15 * pSrc, - arm_matrix_instance_q15 * pDst); - - /** - * @brief Q31 matrix transpose. - * @param[in] *pSrc points to the input matrix - * @param[out] *pDst points to the output matrix - * @return The function returns either ARM_MATH_SIZE_MISMATCH - * or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_trans_q31( - const arm_matrix_instance_q31 * pSrc, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix multiplication - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_mult_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - /** - * @brief Q15 matrix multiplication - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @param[in] *pState points to the array for storing intermediate results - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_mult_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst, - q15_t * pState); - - /** - * @brief Q15 matrix multiplication (fast variant) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @param[in] *pState points to the array for storing intermediate results - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_mult_fast_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst, - q15_t * pState); - - /** - * @brief Q31 matrix multiplication - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_mult_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - /** - * @brief Q31 matrix multiplication (fast variant) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_mult_fast_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Floating-point matrix subtraction - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_sub_f32( - const arm_matrix_instance_f32 * pSrcA, - const arm_matrix_instance_f32 * pSrcB, - arm_matrix_instance_f32 * pDst); - - /** - * @brief Q15 matrix subtraction - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_sub_q15( - const arm_matrix_instance_q15 * pSrcA, - const arm_matrix_instance_q15 * pSrcB, - arm_matrix_instance_q15 * pDst); - - /** - * @brief Q31 matrix subtraction - * @param[in] *pSrcA points to the first input matrix structure - * @param[in] *pSrcB points to the second input matrix structure - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_sub_q31( - const arm_matrix_instance_q31 * pSrcA, - const arm_matrix_instance_q31 * pSrcB, - arm_matrix_instance_q31 * pDst); - - /** - * @brief Floating-point matrix scaling. - * @param[in] *pSrc points to the input matrix - * @param[in] scale scale factor - * @param[out] *pDst points to the output matrix - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_scale_f32( - const arm_matrix_instance_f32 * pSrc, - float32_t scale, - arm_matrix_instance_f32 * pDst); - - /** - * @brief Q15 matrix scaling. - * @param[in] *pSrc points to input matrix - * @param[in] scaleFract fractional portion of the scale factor - * @param[in] shift number of bits to shift the result by - * @param[out] *pDst points to output matrix - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_scale_q15( - const arm_matrix_instance_q15 * pSrc, - q15_t scaleFract, - int32_t shift, - arm_matrix_instance_q15 * pDst); - - /** - * @brief Q31 matrix scaling. - * @param[in] *pSrc points to input matrix - * @param[in] scaleFract fractional portion of the scale factor - * @param[in] shift number of bits to shift the result by - * @param[out] *pDst points to output matrix structure - * @return The function returns either - * ARM_MATH_SIZE_MISMATCH or ARM_MATH_SUCCESS based on the outcome of size checking. - */ - - arm_status arm_mat_scale_q31( - const arm_matrix_instance_q31 * pSrc, - q31_t scaleFract, - int32_t shift, - arm_matrix_instance_q31 * pDst); - - - /** - * @brief Q31 matrix initialization. - * @param[in,out] *S points to an instance of the floating-point matrix structure. - * @param[in] nRows number of rows in the matrix. - * @param[in] nColumns number of columns in the matrix. - * @param[in] *pData points to the matrix data array. - * @return none - */ - - void arm_mat_init_q31( - arm_matrix_instance_q31 * S, - uint16_t nRows, - uint16_t nColumns, - q31_t * pData); - - /** - * @brief Q15 matrix initialization. - * @param[in,out] *S points to an instance of the floating-point matrix structure. - * @param[in] nRows number of rows in the matrix. - * @param[in] nColumns number of columns in the matrix. - * @param[in] *pData points to the matrix data array. - * @return none - */ - - void arm_mat_init_q15( - arm_matrix_instance_q15 * S, - uint16_t nRows, - uint16_t nColumns, - q15_t * pData); - - /** - * @brief Floating-point matrix initialization. - * @param[in,out] *S points to an instance of the floating-point matrix structure. - * @param[in] nRows number of rows in the matrix. - * @param[in] nColumns number of columns in the matrix. - * @param[in] *pData points to the matrix data array. - * @return none - */ - - void arm_mat_init_f32( - arm_matrix_instance_f32 * S, - uint16_t nRows, - uint16_t nColumns, - float32_t * pData); - - - - /** - * @brief Instance structure for the Q15 PID Control. - */ - typedef struct - { - q15_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ -#ifdef ARM_MATH_CM0_FAMILY - q15_t A1; - q15_t A2; -#else - q31_t A1; /**< The derived gain A1 = -Kp - 2Kd | Kd.*/ -#endif - q15_t state[3]; /**< The state array of length 3. */ - q15_t Kp; /**< The proportional gain. */ - q15_t Ki; /**< The integral gain. */ - q15_t Kd; /**< The derivative gain. */ - } arm_pid_instance_q15; - - /** - * @brief Instance structure for the Q31 PID Control. - */ - typedef struct - { - q31_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ - q31_t A1; /**< The derived gain, A1 = -Kp - 2Kd. */ - q31_t A2; /**< The derived gain, A2 = Kd . */ - q31_t state[3]; /**< The state array of length 3. */ - q31_t Kp; /**< The proportional gain. */ - q31_t Ki; /**< The integral gain. */ - q31_t Kd; /**< The derivative gain. */ - - } arm_pid_instance_q31; - - /** - * @brief Instance structure for the floating-point PID Control. - */ - typedef struct - { - float32_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ - float32_t A1; /**< The derived gain, A1 = -Kp - 2Kd. */ - float32_t A2; /**< The derived gain, A2 = Kd . */ - float32_t state[3]; /**< The state array of length 3. */ - float32_t Kp; /**< The proportional gain. */ - float32_t Ki; /**< The integral gain. */ - float32_t Kd; /**< The derivative gain. */ - } arm_pid_instance_f32; - - - - /** - * @brief Initialization function for the floating-point PID Control. - * @param[in,out] *S points to an instance of the PID structure. - * @param[in] resetStateFlag flag to reset the state. 0 = no change in state 1 = reset the state. - * @return none. - */ - void arm_pid_init_f32( - arm_pid_instance_f32 * S, - int32_t resetStateFlag); - - /** - * @brief Reset function for the floating-point PID Control. - * @param[in,out] *S is an instance of the floating-point PID Control structure - * @return none - */ - void arm_pid_reset_f32( - arm_pid_instance_f32 * S); - - - /** - * @brief Initialization function for the Q31 PID Control. - * @param[in,out] *S points to an instance of the Q15 PID structure. - * @param[in] resetStateFlag flag to reset the state. 0 = no change in state 1 = reset the state. - * @return none. - */ - void arm_pid_init_q31( - arm_pid_instance_q31 * S, - int32_t resetStateFlag); - - - /** - * @brief Reset function for the Q31 PID Control. - * @param[in,out] *S points to an instance of the Q31 PID Control structure - * @return none - */ - - void arm_pid_reset_q31( - arm_pid_instance_q31 * S); - - /** - * @brief Initialization function for the Q15 PID Control. - * @param[in,out] *S points to an instance of the Q15 PID structure. - * @param[in] resetStateFlag flag to reset the state. 0 = no change in state 1 = reset the state. - * @return none. - */ - void arm_pid_init_q15( - arm_pid_instance_q15 * S, - int32_t resetStateFlag); - - /** - * @brief Reset function for the Q15 PID Control. - * @param[in,out] *S points to an instance of the q15 PID Control structure - * @return none - */ - void arm_pid_reset_q15( - arm_pid_instance_q15 * S); - - - /** - * @brief Instance structure for the floating-point Linear Interpolate function. - */ - typedef struct - { - uint32_t nValues; /**< nValues */ - float32_t x1; /**< x1 */ - float32_t xSpacing; /**< xSpacing */ - float32_t *pYData; /**< pointer to the table of Y values */ - } arm_linear_interp_instance_f32; - - /** - * @brief Instance structure for the floating-point bilinear interpolation function. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - float32_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_f32; - - /** - * @brief Instance structure for the Q31 bilinear interpolation function. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - q31_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_q31; - - /** - * @brief Instance structure for the Q15 bilinear interpolation function. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - q15_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_q15; - - /** - * @brief Instance structure for the Q15 bilinear interpolation function. - */ - - typedef struct - { - uint16_t numRows; /**< number of rows in the data table. */ - uint16_t numCols; /**< number of columns in the data table. */ - q7_t *pData; /**< points to the data table. */ - } arm_bilinear_interp_instance_q7; - - - /** - * @brief Q7 vector multiplication. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_mult_q7( - q7_t * pSrcA, - q7_t * pSrcB, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Q15 vector multiplication. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_mult_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Q31 vector multiplication. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_mult_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Floating-point vector multiplication. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_mult_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t blockSize); - - - - - - - /** - * @brief Instance structure for the Q15 CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q15_t *pTwiddle; /**< points to the Sin twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix2_instance_q15; - -/* Deprecated */ - arm_status arm_cfft_radix2_init_q15( - arm_cfft_radix2_instance_q15 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix2_q15( - const arm_cfft_radix2_instance_q15 * S, - q15_t * pSrc); - - - - /** - * @brief Instance structure for the Q15 CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q15_t *pTwiddle; /**< points to the twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix4_instance_q15; - -/* Deprecated */ - arm_status arm_cfft_radix4_init_q15( - arm_cfft_radix4_instance_q15 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix4_q15( - const arm_cfft_radix4_instance_q15 * S, - q15_t * pSrc); - - /** - * @brief Instance structure for the Radix-2 Q31 CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q31_t *pTwiddle; /**< points to the Twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix2_instance_q31; - -/* Deprecated */ - arm_status arm_cfft_radix2_init_q31( - arm_cfft_radix2_instance_q31 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix2_q31( - const arm_cfft_radix2_instance_q31 * S, - q31_t * pSrc); - - /** - * @brief Instance structure for the Q31 CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - q31_t *pTwiddle; /**< points to the twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - } arm_cfft_radix4_instance_q31; - -/* Deprecated */ - void arm_cfft_radix4_q31( - const arm_cfft_radix4_instance_q31 * S, - q31_t * pSrc); - -/* Deprecated */ - arm_status arm_cfft_radix4_init_q31( - arm_cfft_radix4_instance_q31 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the floating-point CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - float32_t *pTwiddle; /**< points to the Twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - float32_t onebyfftLen; /**< value of 1/fftLen. */ - } arm_cfft_radix2_instance_f32; - -/* Deprecated */ - arm_status arm_cfft_radix2_init_f32( - arm_cfft_radix2_instance_f32 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix2_f32( - const arm_cfft_radix2_instance_f32 * S, - float32_t * pSrc); - - /** - * @brief Instance structure for the floating-point CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - uint8_t ifftFlag; /**< flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform. */ - uint8_t bitReverseFlag; /**< flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output. */ - float32_t *pTwiddle; /**< points to the Twiddle factor table. */ - uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t twidCoefModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - uint16_t bitRevFactor; /**< bit reversal modifier that supports different size FFTs with the same bit reversal table. */ - float32_t onebyfftLen; /**< value of 1/fftLen. */ - } arm_cfft_radix4_instance_f32; - -/* Deprecated */ - arm_status arm_cfft_radix4_init_f32( - arm_cfft_radix4_instance_f32 * S, - uint16_t fftLen, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - -/* Deprecated */ - void arm_cfft_radix4_f32( - const arm_cfft_radix4_instance_f32 * S, - float32_t * pSrc); - - /** - * @brief Instance structure for the fixed-point CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - const q15_t *pTwiddle; /**< points to the Twiddle factor table. */ - const uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t bitRevLength; /**< bit reversal table length. */ - } arm_cfft_instance_q15; - -void arm_cfft_q15( - const arm_cfft_instance_q15 * S, - q15_t * p1, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the fixed-point CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - const q31_t *pTwiddle; /**< points to the Twiddle factor table. */ - const uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t bitRevLength; /**< bit reversal table length. */ - } arm_cfft_instance_q31; - -void arm_cfft_q31( - const arm_cfft_instance_q31 * S, - q31_t * p1, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the floating-point CFFT/CIFFT function. - */ - - typedef struct - { - uint16_t fftLen; /**< length of the FFT. */ - const float32_t *pTwiddle; /**< points to the Twiddle factor table. */ - const uint16_t *pBitRevTable; /**< points to the bit reversal table. */ - uint16_t bitRevLength; /**< bit reversal table length. */ - } arm_cfft_instance_f32; - - void arm_cfft_f32( - const arm_cfft_instance_f32 * S, - float32_t * p1, - uint8_t ifftFlag, - uint8_t bitReverseFlag); - - /** - * @brief Instance structure for the Q15 RFFT/RIFFT function. - */ - - typedef struct - { - uint32_t fftLenReal; /**< length of the real FFT. */ - uint8_t ifftFlagR; /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */ - uint8_t bitReverseFlagR; /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */ - uint32_t twidCoefRModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - q15_t *pTwiddleAReal; /**< points to the real twiddle factor table. */ - q15_t *pTwiddleBReal; /**< points to the imag twiddle factor table. */ - const arm_cfft_instance_q15 *pCfft; /**< points to the complex FFT instance. */ - } arm_rfft_instance_q15; - - arm_status arm_rfft_init_q15( - arm_rfft_instance_q15 * S, - uint32_t fftLenReal, - uint32_t ifftFlagR, - uint32_t bitReverseFlag); - - void arm_rfft_q15( - const arm_rfft_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst); - - /** - * @brief Instance structure for the Q31 RFFT/RIFFT function. - */ - - typedef struct - { - uint32_t fftLenReal; /**< length of the real FFT. */ - uint8_t ifftFlagR; /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */ - uint8_t bitReverseFlagR; /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */ - uint32_t twidCoefRModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - q31_t *pTwiddleAReal; /**< points to the real twiddle factor table. */ - q31_t *pTwiddleBReal; /**< points to the imag twiddle factor table. */ - const arm_cfft_instance_q31 *pCfft; /**< points to the complex FFT instance. */ - } arm_rfft_instance_q31; - - arm_status arm_rfft_init_q31( - arm_rfft_instance_q31 * S, - uint32_t fftLenReal, - uint32_t ifftFlagR, - uint32_t bitReverseFlag); - - void arm_rfft_q31( - const arm_rfft_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst); - - /** - * @brief Instance structure for the floating-point RFFT/RIFFT function. - */ - - typedef struct - { - uint32_t fftLenReal; /**< length of the real FFT. */ - uint16_t fftLenBy2; /**< length of the complex FFT. */ - uint8_t ifftFlagR; /**< flag that selects forward (ifftFlagR=0) or inverse (ifftFlagR=1) transform. */ - uint8_t bitReverseFlagR; /**< flag that enables (bitReverseFlagR=1) or disables (bitReverseFlagR=0) bit reversal of output. */ - uint32_t twidCoefRModifier; /**< twiddle coefficient modifier that supports different size FFTs with the same twiddle factor table. */ - float32_t *pTwiddleAReal; /**< points to the real twiddle factor table. */ - float32_t *pTwiddleBReal; /**< points to the imag twiddle factor table. */ - arm_cfft_radix4_instance_f32 *pCfft; /**< points to the complex FFT instance. */ - } arm_rfft_instance_f32; - - arm_status arm_rfft_init_f32( - arm_rfft_instance_f32 * S, - arm_cfft_radix4_instance_f32 * S_CFFT, - uint32_t fftLenReal, - uint32_t ifftFlagR, - uint32_t bitReverseFlag); - - void arm_rfft_f32( - const arm_rfft_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst); - - /** - * @brief Instance structure for the floating-point RFFT/RIFFT function. - */ - -typedef struct - { - arm_cfft_instance_f32 Sint; /**< Internal CFFT structure. */ - uint16_t fftLenRFFT; /**< length of the real sequence */ - float32_t * pTwiddleRFFT; /**< Twiddle factors real stage */ - } arm_rfft_fast_instance_f32 ; - -arm_status arm_rfft_fast_init_f32 ( - arm_rfft_fast_instance_f32 * S, - uint16_t fftLen); - -void arm_rfft_fast_f32( - arm_rfft_fast_instance_f32 * S, - float32_t * p, float32_t * pOut, - uint8_t ifftFlag); - - /** - * @brief Instance structure for the floating-point DCT4/IDCT4 function. - */ - - typedef struct - { - uint16_t N; /**< length of the DCT4. */ - uint16_t Nby2; /**< half of the length of the DCT4. */ - float32_t normalize; /**< normalizing factor. */ - float32_t *pTwiddle; /**< points to the twiddle factor table. */ - float32_t *pCosFactor; /**< points to the cosFactor table. */ - arm_rfft_instance_f32 *pRfft; /**< points to the real FFT instance. */ - arm_cfft_radix4_instance_f32 *pCfft; /**< points to the complex FFT instance. */ - } arm_dct4_instance_f32; - - /** - * @brief Initialization function for the floating-point DCT4/IDCT4. - * @param[in,out] *S points to an instance of floating-point DCT4/IDCT4 structure. - * @param[in] *S_RFFT points to an instance of floating-point RFFT/RIFFT structure. - * @param[in] *S_CFFT points to an instance of floating-point CFFT/CIFFT structure. - * @param[in] N length of the DCT4. - * @param[in] Nby2 half of the length of the DCT4. - * @param[in] normalize normalizing factor. - * @return arm_status function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if fftLenReal is not a supported transform length. - */ - - arm_status arm_dct4_init_f32( - arm_dct4_instance_f32 * S, - arm_rfft_instance_f32 * S_RFFT, - arm_cfft_radix4_instance_f32 * S_CFFT, - uint16_t N, - uint16_t Nby2, - float32_t normalize); - - /** - * @brief Processing function for the floating-point DCT4/IDCT4. - * @param[in] *S points to an instance of the floating-point DCT4/IDCT4 structure. - * @param[in] *pState points to state buffer. - * @param[in,out] *pInlineBuffer points to the in-place input and output buffer. - * @return none. - */ - - void arm_dct4_f32( - const arm_dct4_instance_f32 * S, - float32_t * pState, - float32_t * pInlineBuffer); - - /** - * @brief Instance structure for the Q31 DCT4/IDCT4 function. - */ - - typedef struct - { - uint16_t N; /**< length of the DCT4. */ - uint16_t Nby2; /**< half of the length of the DCT4. */ - q31_t normalize; /**< normalizing factor. */ - q31_t *pTwiddle; /**< points to the twiddle factor table. */ - q31_t *pCosFactor; /**< points to the cosFactor table. */ - arm_rfft_instance_q31 *pRfft; /**< points to the real FFT instance. */ - arm_cfft_radix4_instance_q31 *pCfft; /**< points to the complex FFT instance. */ - } arm_dct4_instance_q31; - - /** - * @brief Initialization function for the Q31 DCT4/IDCT4. - * @param[in,out] *S points to an instance of Q31 DCT4/IDCT4 structure. - * @param[in] *S_RFFT points to an instance of Q31 RFFT/RIFFT structure - * @param[in] *S_CFFT points to an instance of Q31 CFFT/CIFFT structure - * @param[in] N length of the DCT4. - * @param[in] Nby2 half of the length of the DCT4. - * @param[in] normalize normalizing factor. - * @return arm_status function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if N is not a supported transform length. - */ - - arm_status arm_dct4_init_q31( - arm_dct4_instance_q31 * S, - arm_rfft_instance_q31 * S_RFFT, - arm_cfft_radix4_instance_q31 * S_CFFT, - uint16_t N, - uint16_t Nby2, - q31_t normalize); - - /** - * @brief Processing function for the Q31 DCT4/IDCT4. - * @param[in] *S points to an instance of the Q31 DCT4 structure. - * @param[in] *pState points to state buffer. - * @param[in,out] *pInlineBuffer points to the in-place input and output buffer. - * @return none. - */ - - void arm_dct4_q31( - const arm_dct4_instance_q31 * S, - q31_t * pState, - q31_t * pInlineBuffer); - - /** - * @brief Instance structure for the Q15 DCT4/IDCT4 function. - */ - - typedef struct - { - uint16_t N; /**< length of the DCT4. */ - uint16_t Nby2; /**< half of the length of the DCT4. */ - q15_t normalize; /**< normalizing factor. */ - q15_t *pTwiddle; /**< points to the twiddle factor table. */ - q15_t *pCosFactor; /**< points to the cosFactor table. */ - arm_rfft_instance_q15 *pRfft; /**< points to the real FFT instance. */ - arm_cfft_radix4_instance_q15 *pCfft; /**< points to the complex FFT instance. */ - } arm_dct4_instance_q15; - - /** - * @brief Initialization function for the Q15 DCT4/IDCT4. - * @param[in,out] *S points to an instance of Q15 DCT4/IDCT4 structure. - * @param[in] *S_RFFT points to an instance of Q15 RFFT/RIFFT structure. - * @param[in] *S_CFFT points to an instance of Q15 CFFT/CIFFT structure. - * @param[in] N length of the DCT4. - * @param[in] Nby2 half of the length of the DCT4. - * @param[in] normalize normalizing factor. - * @return arm_status function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if N is not a supported transform length. - */ - - arm_status arm_dct4_init_q15( - arm_dct4_instance_q15 * S, - arm_rfft_instance_q15 * S_RFFT, - arm_cfft_radix4_instance_q15 * S_CFFT, - uint16_t N, - uint16_t Nby2, - q15_t normalize); - - /** - * @brief Processing function for the Q15 DCT4/IDCT4. - * @param[in] *S points to an instance of the Q15 DCT4 structure. - * @param[in] *pState points to state buffer. - * @param[in,out] *pInlineBuffer points to the in-place input and output buffer. - * @return none. - */ - - void arm_dct4_q15( - const arm_dct4_instance_q15 * S, - q15_t * pState, - q15_t * pInlineBuffer); - - /** - * @brief Floating-point vector addition. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_add_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Q7 vector addition. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_add_q7( - q7_t * pSrcA, - q7_t * pSrcB, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Q15 vector addition. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_add_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Q31 vector addition. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_add_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Floating-point vector subtraction. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_sub_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Q7 vector subtraction. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_sub_q7( - q7_t * pSrcA, - q7_t * pSrcB, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Q15 vector subtraction. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_sub_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Q31 vector subtraction. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_sub_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Multiplies a floating-point vector by a scalar. - * @param[in] *pSrc points to the input vector - * @param[in] scale scale factor to be applied - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_scale_f32( - float32_t * pSrc, - float32_t scale, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Multiplies a Q7 vector by a scalar. - * @param[in] *pSrc points to the input vector - * @param[in] scaleFract fractional portion of the scale value - * @param[in] shift number of bits to shift the result by - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_scale_q7( - q7_t * pSrc, - q7_t scaleFract, - int8_t shift, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Multiplies a Q15 vector by a scalar. - * @param[in] *pSrc points to the input vector - * @param[in] scaleFract fractional portion of the scale value - * @param[in] shift number of bits to shift the result by - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_scale_q15( - q15_t * pSrc, - q15_t scaleFract, - int8_t shift, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Multiplies a Q31 vector by a scalar. - * @param[in] *pSrc points to the input vector - * @param[in] scaleFract fractional portion of the scale value - * @param[in] shift number of bits to shift the result by - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_scale_q31( - q31_t * pSrc, - q31_t scaleFract, - int8_t shift, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Q7 vector absolute value. - * @param[in] *pSrc points to the input buffer - * @param[out] *pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_abs_q7( - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Floating-point vector absolute value. - * @param[in] *pSrc points to the input buffer - * @param[out] *pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_abs_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Q15 vector absolute value. - * @param[in] *pSrc points to the input buffer - * @param[out] *pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_abs_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Q31 vector absolute value. - * @param[in] *pSrc points to the input buffer - * @param[out] *pDst points to the output buffer - * @param[in] blockSize number of samples in each vector - * @return none. - */ - - void arm_abs_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Dot product of floating-point vectors. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] *result output result returned here - * @return none. - */ - - void arm_dot_prod_f32( - float32_t * pSrcA, - float32_t * pSrcB, - uint32_t blockSize, - float32_t * result); - - /** - * @brief Dot product of Q7 vectors. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] *result output result returned here - * @return none. - */ - - void arm_dot_prod_q7( - q7_t * pSrcA, - q7_t * pSrcB, - uint32_t blockSize, - q31_t * result); - - /** - * @brief Dot product of Q15 vectors. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] *result output result returned here - * @return none. - */ - - void arm_dot_prod_q15( - q15_t * pSrcA, - q15_t * pSrcB, - uint32_t blockSize, - q63_t * result); - - /** - * @brief Dot product of Q31 vectors. - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] blockSize number of samples in each vector - * @param[out] *result output result returned here - * @return none. - */ - - void arm_dot_prod_q31( - q31_t * pSrcA, - q31_t * pSrcB, - uint32_t blockSize, - q63_t * result); - - /** - * @brief Shifts the elements of a Q7 vector a specified number of bits. - * @param[in] *pSrc points to the input vector - * @param[in] shiftBits number of bits to shift. A positive value shifts left; a negative value shifts right. - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_shift_q7( - q7_t * pSrc, - int8_t shiftBits, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Shifts the elements of a Q15 vector a specified number of bits. - * @param[in] *pSrc points to the input vector - * @param[in] shiftBits number of bits to shift. A positive value shifts left; a negative value shifts right. - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_shift_q15( - q15_t * pSrc, - int8_t shiftBits, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Shifts the elements of a Q31 vector a specified number of bits. - * @param[in] *pSrc points to the input vector - * @param[in] shiftBits number of bits to shift. A positive value shifts left; a negative value shifts right. - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_shift_q31( - q31_t * pSrc, - int8_t shiftBits, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Adds a constant offset to a floating-point vector. - * @param[in] *pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_offset_f32( - float32_t * pSrc, - float32_t offset, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Adds a constant offset to a Q7 vector. - * @param[in] *pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_offset_q7( - q7_t * pSrc, - q7_t offset, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Adds a constant offset to a Q15 vector. - * @param[in] *pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_offset_q15( - q15_t * pSrc, - q15_t offset, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Adds a constant offset to a Q31 vector. - * @param[in] *pSrc points to the input vector - * @param[in] offset is the offset to be added - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_offset_q31( - q31_t * pSrc, - q31_t offset, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Negates the elements of a floating-point vector. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_negate_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Negates the elements of a Q7 vector. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_negate_q7( - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Negates the elements of a Q15 vector. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_negate_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Negates the elements of a Q31 vector. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] blockSize number of samples in the vector - * @return none. - */ - - void arm_negate_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - /** - * @brief Copies the elements of a floating-point vector. - * @param[in] *pSrc input pointer - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_copy_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Copies the elements of a Q7 vector. - * @param[in] *pSrc input pointer - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_copy_q7( - q7_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Copies the elements of a Q15 vector. - * @param[in] *pSrc input pointer - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_copy_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Copies the elements of a Q31 vector. - * @param[in] *pSrc input pointer - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_copy_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - /** - * @brief Fills a constant value into a floating-point vector. - * @param[in] value input value to be filled - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_fill_f32( - float32_t value, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Fills a constant value into a Q7 vector. - * @param[in] value input value to be filled - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_fill_q7( - q7_t value, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Fills a constant value into a Q15 vector. - * @param[in] value input value to be filled - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_fill_q15( - q15_t value, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Fills a constant value into a Q31 vector. - * @param[in] value input value to be filled - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_fill_q31( - q31_t value, - q31_t * pDst, - uint32_t blockSize); - -/** - * @brief Convolution of floating-point sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the location where the output result is written. Length srcALen+srcBLen-1. - * @return none. - */ - - void arm_conv_f32( - float32_t * pSrcA, - uint32_t srcALen, - float32_t * pSrcB, - uint32_t srcBLen, - float32_t * pDst); - - - /** - * @brief Convolution of Q15 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @param[in] *pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] *pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - * @return none. - */ - - - void arm_conv_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - -/** - * @brief Convolution of Q15 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the location where the output result is written. Length srcALen+srcBLen-1. - * @return none. - */ - - void arm_conv_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - /** - * @brief Convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @return none. - */ - - void arm_conv_fast_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - /** - * @brief Convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @param[in] *pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] *pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - * @return none. - */ - - void arm_conv_fast_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - - - /** - * @brief Convolution of Q31 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @return none. - */ - - void arm_conv_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - /** - * @brief Convolution of Q31 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @return none. - */ - - void arm_conv_fast_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - - /** - * @brief Convolution of Q7 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @param[in] *pScratch1 points to scratch buffer(of type q15_t) of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] *pScratch2 points to scratch buffer (of type q15_t) of size min(srcALen, srcBLen). - * @return none. - */ - - void arm_conv_opt_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - - - /** - * @brief Convolution of Q7 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length srcALen+srcBLen-1. - * @return none. - */ - - void arm_conv_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst); - - - /** - * @brief Partial convolution of floating-point sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_f32( - float32_t * pSrcA, - uint32_t srcALen, - float32_t * pSrcB, - uint32_t srcBLen, - float32_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - /** - * @brief Partial convolution of Q15 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @param[in] * pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] * pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints, - q15_t * pScratch1, - q15_t * pScratch2); - - -/** - * @brief Partial convolution of Q15 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - /** - * @brief Partial convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_fast_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @param[in] * pScratch1 points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] * pScratch2 points to scratch buffer of size min(srcALen, srcBLen). - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_fast_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - uint32_t firstIndex, - uint32_t numPoints, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Partial convolution of Q31 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q31 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_fast_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - /** - * @brief Partial convolution of Q7 sequences - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @param[in] *pScratch1 points to scratch buffer(of type q15_t) of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] *pScratch2 points to scratch buffer (of type q15_t) of size min(srcALen, srcBLen). - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_opt_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - uint32_t firstIndex, - uint32_t numPoints, - q15_t * pScratch1, - q15_t * pScratch2); - - -/** - * @brief Partial convolution of Q7 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data - * @param[in] firstIndex is the first output sample to start with. - * @param[in] numPoints is the number of output points to be computed. - * @return Returns either ARM_MATH_SUCCESS if the function completed correctly or ARM_MATH_ARGUMENT_ERROR if the requested subset is not in the range [0 srcALen+srcBLen-2]. - */ - - arm_status arm_conv_partial_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - uint32_t firstIndex, - uint32_t numPoints); - - - - /** - * @brief Instance structure for the Q15 FIR decimator. - */ - - typedef struct - { - uint8_t M; /**< decimation factor. */ - uint16_t numTaps; /**< number of coefficients in the filter. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - } arm_fir_decimate_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR decimator. - */ - - typedef struct - { - uint8_t M; /**< decimation factor. */ - uint16_t numTaps; /**< number of coefficients in the filter. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - - } arm_fir_decimate_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR decimator. - */ - - typedef struct - { - uint8_t M; /**< decimation factor. */ - uint16_t numTaps; /**< number of coefficients in the filter. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - - } arm_fir_decimate_instance_f32; - - - - /** - * @brief Processing function for the floating-point FIR decimator. - * @param[in] *S points to an instance of the floating-point FIR decimator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - * @return none - */ - - void arm_fir_decimate_f32( - const arm_fir_decimate_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point FIR decimator. - * @param[in,out] *S points to an instance of the floating-point FIR decimator structure. - * @param[in] numTaps number of coefficients in the filter. - * @param[in] M decimation factor. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * blockSize is not a multiple of M. - */ - - arm_status arm_fir_decimate_init_f32( - arm_fir_decimate_instance_f32 * S, - uint16_t numTaps, - uint8_t M, - float32_t * pCoeffs, - float32_t * pState, - uint32_t blockSize); - - /** - * @brief Processing function for the Q15 FIR decimator. - * @param[in] *S points to an instance of the Q15 FIR decimator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - * @return none - */ - - void arm_fir_decimate_q15( - const arm_fir_decimate_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Processing function for the Q15 FIR decimator (fast variant) for Cortex-M3 and Cortex-M4. - * @param[in] *S points to an instance of the Q15 FIR decimator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - * @return none - */ - - void arm_fir_decimate_fast_q15( - const arm_fir_decimate_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - - /** - * @brief Initialization function for the Q15 FIR decimator. - * @param[in,out] *S points to an instance of the Q15 FIR decimator structure. - * @param[in] numTaps number of coefficients in the filter. - * @param[in] M decimation factor. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * blockSize is not a multiple of M. - */ - - arm_status arm_fir_decimate_init_q15( - arm_fir_decimate_instance_q15 * S, - uint16_t numTaps, - uint8_t M, - q15_t * pCoeffs, - q15_t * pState, - uint32_t blockSize); - - /** - * @brief Processing function for the Q31 FIR decimator. - * @param[in] *S points to an instance of the Q31 FIR decimator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - * @return none - */ - - void arm_fir_decimate_q31( - const arm_fir_decimate_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Processing function for the Q31 FIR decimator (fast variant) for Cortex-M3 and Cortex-M4. - * @param[in] *S points to an instance of the Q31 FIR decimator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of input samples to process per call. - * @return none - */ - - void arm_fir_decimate_fast_q31( - arm_fir_decimate_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 FIR decimator. - * @param[in,out] *S points to an instance of the Q31 FIR decimator structure. - * @param[in] numTaps number of coefficients in the filter. - * @param[in] M decimation factor. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * blockSize is not a multiple of M. - */ - - arm_status arm_fir_decimate_init_q31( - arm_fir_decimate_instance_q31 * S, - uint16_t numTaps, - uint8_t M, - q31_t * pCoeffs, - q31_t * pState, - uint32_t blockSize); - - - - /** - * @brief Instance structure for the Q15 FIR interpolator. - */ - - typedef struct - { - uint8_t L; /**< upsample factor. */ - uint16_t phaseLength; /**< length of each polyphase filter component. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length L*phaseLength. */ - q15_t *pState; /**< points to the state variable array. The array is of length blockSize+phaseLength-1. */ - } arm_fir_interpolate_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR interpolator. - */ - - typedef struct - { - uint8_t L; /**< upsample factor. */ - uint16_t phaseLength; /**< length of each polyphase filter component. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length L*phaseLength. */ - q31_t *pState; /**< points to the state variable array. The array is of length blockSize+phaseLength-1. */ - } arm_fir_interpolate_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR interpolator. - */ - - typedef struct - { - uint8_t L; /**< upsample factor. */ - uint16_t phaseLength; /**< length of each polyphase filter component. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length L*phaseLength. */ - float32_t *pState; /**< points to the state variable array. The array is of length phaseLength+numTaps-1. */ - } arm_fir_interpolate_instance_f32; - - - /** - * @brief Processing function for the Q15 FIR interpolator. - * @param[in] *S points to an instance of the Q15 FIR interpolator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_interpolate_q15( - const arm_fir_interpolate_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 FIR interpolator. - * @param[in,out] *S points to an instance of the Q15 FIR interpolator structure. - * @param[in] L upsample factor. - * @param[in] numTaps number of filter coefficients in the filter. - * @param[in] *pCoeffs points to the filter coefficient buffer. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * the filter length numTaps is not a multiple of the interpolation factor L. - */ - - arm_status arm_fir_interpolate_init_q15( - arm_fir_interpolate_instance_q15 * S, - uint8_t L, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - uint32_t blockSize); - - /** - * @brief Processing function for the Q31 FIR interpolator. - * @param[in] *S points to an instance of the Q15 FIR interpolator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_interpolate_q31( - const arm_fir_interpolate_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q31 FIR interpolator. - * @param[in,out] *S points to an instance of the Q31 FIR interpolator structure. - * @param[in] L upsample factor. - * @param[in] numTaps number of filter coefficients in the filter. - * @param[in] *pCoeffs points to the filter coefficient buffer. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * the filter length numTaps is not a multiple of the interpolation factor L. - */ - - arm_status arm_fir_interpolate_init_q31( - arm_fir_interpolate_instance_q31 * S, - uint8_t L, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the floating-point FIR interpolator. - * @param[in] *S points to an instance of the floating-point FIR interpolator structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_interpolate_f32( - const arm_fir_interpolate_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the floating-point FIR interpolator. - * @param[in,out] *S points to an instance of the floating-point FIR interpolator structure. - * @param[in] L upsample factor. - * @param[in] numTaps number of filter coefficients in the filter. - * @param[in] *pCoeffs points to the filter coefficient buffer. - * @param[in] *pState points to the state buffer. - * @param[in] blockSize number of input samples to process per call. - * @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_LENGTH_ERROR if - * the filter length numTaps is not a multiple of the interpolation factor L. - */ - - arm_status arm_fir_interpolate_init_f32( - arm_fir_interpolate_instance_f32 * S, - uint8_t L, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - uint32_t blockSize); - - /** - * @brief Instance structure for the high precision Q31 Biquad cascade filter. - */ - - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - q63_t *pState; /**< points to the array of state coefficients. The array is of length 4*numStages. */ - q31_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - uint8_t postShift; /**< additional shift, in bits, applied to each output sample. */ - - } arm_biquad_cas_df1_32x64_ins_q31; - - - /** - * @param[in] *S points to an instance of the high precision Q31 Biquad cascade filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cas_df1_32x64_q31( - const arm_biquad_cas_df1_32x64_ins_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @param[in,out] *S points to an instance of the high precision Q31 Biquad cascade filter structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] postShift shift to be applied to the output. Varies according to the coefficients format - * @return none - */ - - void arm_biquad_cas_df1_32x64_init_q31( - arm_biquad_cas_df1_32x64_ins_q31 * S, - uint8_t numStages, - q31_t * pCoeffs, - q63_t * pState, - uint8_t postShift); - - - - /** - * @brief Instance structure for the floating-point transposed direct form II Biquad cascade filter. - */ - - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float32_t *pState; /**< points to the array of state coefficients. The array is of length 2*numStages. */ - float32_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_cascade_df2T_instance_f32; - - - - /** - * @brief Instance structure for the floating-point transposed direct form II Biquad cascade filter. - */ - - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float32_t *pState; /**< points to the array of state coefficients. The array is of length 4*numStages. */ - float32_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_cascade_stereo_df2T_instance_f32; - - - - /** - * @brief Instance structure for the floating-point transposed direct form II Biquad cascade filter. - */ - - typedef struct - { - uint8_t numStages; /**< number of 2nd order stages in the filter. Overall order is 2*numStages. */ - float64_t *pState; /**< points to the array of state coefficients. The array is of length 2*numStages. */ - float64_t *pCoeffs; /**< points to the array of coefficients. The array is of length 5*numStages. */ - } arm_biquad_cascade_df2T_instance_f64; - - - /** - * @brief Processing function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in] *S points to an instance of the filter data structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df2T_f32( - const arm_biquad_cascade_df2T_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Processing function for the floating-point transposed direct form II Biquad cascade filter. 2 channels - * @param[in] *S points to an instance of the filter data structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_stereo_df2T_f32( - const arm_biquad_cascade_stereo_df2T_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Processing function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in] *S points to an instance of the filter data structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_biquad_cascade_df2T_f64( - const arm_biquad_cascade_df2T_instance_f64 * S, - float64_t * pSrc, - float64_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in,out] *S points to an instance of the filter data structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @return none - */ - - void arm_biquad_cascade_df2T_init_f32( - arm_biquad_cascade_df2T_instance_f32 * S, - uint8_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Initialization function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in,out] *S points to an instance of the filter data structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @return none - */ - - void arm_biquad_cascade_stereo_df2T_init_f32( - arm_biquad_cascade_stereo_df2T_instance_f32 * S, - uint8_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - - /** - * @brief Initialization function for the floating-point transposed direct form II Biquad cascade filter. - * @param[in,out] *S points to an instance of the filter data structure. - * @param[in] numStages number of 2nd order stages in the filter. - * @param[in] *pCoeffs points to the filter coefficients. - * @param[in] *pState points to the state buffer. - * @return none - */ - - void arm_biquad_cascade_df2T_init_f64( - arm_biquad_cascade_df2T_instance_f64 * S, - uint8_t numStages, - float64_t * pCoeffs, - float64_t * pState); - - - - /** - * @brief Instance structure for the Q15 FIR lattice filter. - */ - - typedef struct - { - uint16_t numStages; /**< number of filter stages. */ - q15_t *pState; /**< points to the state variable array. The array is of length numStages. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numStages. */ - } arm_fir_lattice_instance_q15; - - /** - * @brief Instance structure for the Q31 FIR lattice filter. - */ - - typedef struct - { - uint16_t numStages; /**< number of filter stages. */ - q31_t *pState; /**< points to the state variable array. The array is of length numStages. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numStages. */ - } arm_fir_lattice_instance_q31; - - /** - * @brief Instance structure for the floating-point FIR lattice filter. - */ - - typedef struct - { - uint16_t numStages; /**< number of filter stages. */ - float32_t *pState; /**< points to the state variable array. The array is of length numStages. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numStages. */ - } arm_fir_lattice_instance_f32; - - /** - * @brief Initialization function for the Q15 FIR lattice filter. - * @param[in] *S points to an instance of the Q15 FIR lattice structure. - * @param[in] numStages number of filter stages. - * @param[in] *pCoeffs points to the coefficient buffer. The array is of length numStages. - * @param[in] *pState points to the state buffer. The array is of length numStages. - * @return none. - */ - - void arm_fir_lattice_init_q15( - arm_fir_lattice_instance_q15 * S, - uint16_t numStages, - q15_t * pCoeffs, - q15_t * pState); - - - /** - * @brief Processing function for the Q15 FIR lattice filter. - * @param[in] *S points to an instance of the Q15 FIR lattice structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - void arm_fir_lattice_q15( - const arm_fir_lattice_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q31 FIR lattice filter. - * @param[in] *S points to an instance of the Q31 FIR lattice structure. - * @param[in] numStages number of filter stages. - * @param[in] *pCoeffs points to the coefficient buffer. The array is of length numStages. - * @param[in] *pState points to the state buffer. The array is of length numStages. - * @return none. - */ - - void arm_fir_lattice_init_q31( - arm_fir_lattice_instance_q31 * S, - uint16_t numStages, - q31_t * pCoeffs, - q31_t * pState); - - - /** - * @brief Processing function for the Q31 FIR lattice filter. - * @param[in] *S points to an instance of the Q31 FIR lattice structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_fir_lattice_q31( - const arm_fir_lattice_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - -/** - * @brief Initialization function for the floating-point FIR lattice filter. - * @param[in] *S points to an instance of the floating-point FIR lattice structure. - * @param[in] numStages number of filter stages. - * @param[in] *pCoeffs points to the coefficient buffer. The array is of length numStages. - * @param[in] *pState points to the state buffer. The array is of length numStages. - * @return none. - */ - - void arm_fir_lattice_init_f32( - arm_fir_lattice_instance_f32 * S, - uint16_t numStages, - float32_t * pCoeffs, - float32_t * pState); - - /** - * @brief Processing function for the floating-point FIR lattice filter. - * @param[in] *S points to an instance of the floating-point FIR lattice structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_fir_lattice_f32( - const arm_fir_lattice_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Instance structure for the Q15 IIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of stages in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numStages+blockSize. */ - q15_t *pkCoeffs; /**< points to the reflection coefficient array. The array is of length numStages. */ - q15_t *pvCoeffs; /**< points to the ladder coefficient array. The array is of length numStages+1. */ - } arm_iir_lattice_instance_q15; - - /** - * @brief Instance structure for the Q31 IIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of stages in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numStages+blockSize. */ - q31_t *pkCoeffs; /**< points to the reflection coefficient array. The array is of length numStages. */ - q31_t *pvCoeffs; /**< points to the ladder coefficient array. The array is of length numStages+1. */ - } arm_iir_lattice_instance_q31; - - /** - * @brief Instance structure for the floating-point IIR lattice filter. - */ - typedef struct - { - uint16_t numStages; /**< number of stages in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numStages+blockSize. */ - float32_t *pkCoeffs; /**< points to the reflection coefficient array. The array is of length numStages. */ - float32_t *pvCoeffs; /**< points to the ladder coefficient array. The array is of length numStages+1. */ - } arm_iir_lattice_instance_f32; - - /** - * @brief Processing function for the floating-point IIR lattice filter. - * @param[in] *S points to an instance of the floating-point IIR lattice structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_iir_lattice_f32( - const arm_iir_lattice_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @brief Initialization function for the floating-point IIR lattice filter. - * @param[in] *S points to an instance of the floating-point IIR lattice structure. - * @param[in] numStages number of stages in the filter. - * @param[in] *pkCoeffs points to the reflection coefficient buffer. The array is of length numStages. - * @param[in] *pvCoeffs points to the ladder coefficient buffer. The array is of length numStages+1. - * @param[in] *pState points to the state buffer. The array is of length numStages+blockSize-1. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_iir_lattice_init_f32( - arm_iir_lattice_instance_f32 * S, - uint16_t numStages, - float32_t * pkCoeffs, - float32_t * pvCoeffs, - float32_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q31 IIR lattice filter. - * @param[in] *S points to an instance of the Q31 IIR lattice structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_iir_lattice_q31( - const arm_iir_lattice_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q31 IIR lattice filter. - * @param[in] *S points to an instance of the Q31 IIR lattice structure. - * @param[in] numStages number of stages in the filter. - * @param[in] *pkCoeffs points to the reflection coefficient buffer. The array is of length numStages. - * @param[in] *pvCoeffs points to the ladder coefficient buffer. The array is of length numStages+1. - * @param[in] *pState points to the state buffer. The array is of length numStages+blockSize. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_iir_lattice_init_q31( - arm_iir_lattice_instance_q31 * S, - uint16_t numStages, - q31_t * pkCoeffs, - q31_t * pvCoeffs, - q31_t * pState, - uint32_t blockSize); - - - /** - * @brief Processing function for the Q15 IIR lattice filter. - * @param[in] *S points to an instance of the Q15 IIR lattice structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_iir_lattice_q15( - const arm_iir_lattice_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - -/** - * @brief Initialization function for the Q15 IIR lattice filter. - * @param[in] *S points to an instance of the fixed-point Q15 IIR lattice structure. - * @param[in] numStages number of stages in the filter. - * @param[in] *pkCoeffs points to reflection coefficient buffer. The array is of length numStages. - * @param[in] *pvCoeffs points to ladder coefficient buffer. The array is of length numStages+1. - * @param[in] *pState points to state buffer. The array is of length numStages+blockSize. - * @param[in] blockSize number of samples to process per call. - * @return none. - */ - - void arm_iir_lattice_init_q15( - arm_iir_lattice_instance_q15 * S, - uint16_t numStages, - q15_t * pkCoeffs, - q15_t * pvCoeffs, - q15_t * pState, - uint32_t blockSize); - - /** - * @brief Instance structure for the floating-point LMS filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - float32_t mu; /**< step size that controls filter coefficient updates. */ - } arm_lms_instance_f32; - - /** - * @brief Processing function for floating-point LMS filter. - * @param[in] *S points to an instance of the floating-point LMS filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[in] *pRef points to the block of reference data. - * @param[out] *pOut points to the block of output data. - * @param[out] *pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_f32( - const arm_lms_instance_f32 * S, - float32_t * pSrc, - float32_t * pRef, - float32_t * pOut, - float32_t * pErr, - uint32_t blockSize); - - /** - * @brief Initialization function for floating-point LMS filter. - * @param[in] *S points to an instance of the floating-point LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] *pCoeffs points to the coefficient buffer. - * @param[in] *pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_init_f32( - arm_lms_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - float32_t mu, - uint32_t blockSize); - - /** - * @brief Instance structure for the Q15 LMS filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q15_t mu; /**< step size that controls filter coefficient updates. */ - uint32_t postShift; /**< bit shift applied to coefficients. */ - } arm_lms_instance_q15; - - - /** - * @brief Initialization function for the Q15 LMS filter. - * @param[in] *S points to an instance of the Q15 LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] *pCoeffs points to the coefficient buffer. - * @param[in] *pState points to the state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - * @return none. - */ - - void arm_lms_init_q15( - arm_lms_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - q15_t mu, - uint32_t blockSize, - uint32_t postShift); - - /** - * @brief Processing function for Q15 LMS filter. - * @param[in] *S points to an instance of the Q15 LMS filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[in] *pRef points to the block of reference data. - * @param[out] *pOut points to the block of output data. - * @param[out] *pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_q15( - const arm_lms_instance_q15 * S, - q15_t * pSrc, - q15_t * pRef, - q15_t * pOut, - q15_t * pErr, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q31 LMS filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q31_t mu; /**< step size that controls filter coefficient updates. */ - uint32_t postShift; /**< bit shift applied to coefficients. */ - - } arm_lms_instance_q31; - - /** - * @brief Processing function for Q31 LMS filter. - * @param[in] *S points to an instance of the Q15 LMS filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[in] *pRef points to the block of reference data. - * @param[out] *pOut points to the block of output data. - * @param[out] *pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_q31( - const arm_lms_instance_q31 * S, - q31_t * pSrc, - q31_t * pRef, - q31_t * pOut, - q31_t * pErr, - uint32_t blockSize); - - /** - * @brief Initialization function for Q31 LMS filter. - * @param[in] *S points to an instance of the Q31 LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] *pCoeffs points to coefficient buffer. - * @param[in] *pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - * @return none. - */ - - void arm_lms_init_q31( - arm_lms_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - q31_t mu, - uint32_t blockSize, - uint32_t postShift); - - /** - * @brief Instance structure for the floating-point normalized LMS filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - float32_t mu; /**< step size that control filter coefficient updates. */ - float32_t energy; /**< saves previous frame energy. */ - float32_t x0; /**< saves previous input sample. */ - } arm_lms_norm_instance_f32; - - /** - * @brief Processing function for floating-point normalized LMS filter. - * @param[in] *S points to an instance of the floating-point normalized LMS filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[in] *pRef points to the block of reference data. - * @param[out] *pOut points to the block of output data. - * @param[out] *pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_norm_f32( - arm_lms_norm_instance_f32 * S, - float32_t * pSrc, - float32_t * pRef, - float32_t * pOut, - float32_t * pErr, - uint32_t blockSize); - - /** - * @brief Initialization function for floating-point normalized LMS filter. - * @param[in] *S points to an instance of the floating-point LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] *pCoeffs points to coefficient buffer. - * @param[in] *pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_norm_init_f32( - arm_lms_norm_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - float32_t mu, - uint32_t blockSize); - - - /** - * @brief Instance structure for the Q31 normalized LMS filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - q31_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q31_t mu; /**< step size that controls filter coefficient updates. */ - uint8_t postShift; /**< bit shift applied to coefficients. */ - q31_t *recipTable; /**< points to the reciprocal initial value table. */ - q31_t energy; /**< saves previous frame energy. */ - q31_t x0; /**< saves previous input sample. */ - } arm_lms_norm_instance_q31; - - /** - * @brief Processing function for Q31 normalized LMS filter. - * @param[in] *S points to an instance of the Q31 normalized LMS filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[in] *pRef points to the block of reference data. - * @param[out] *pOut points to the block of output data. - * @param[out] *pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_norm_q31( - arm_lms_norm_instance_q31 * S, - q31_t * pSrc, - q31_t * pRef, - q31_t * pOut, - q31_t * pErr, - uint32_t blockSize); - - /** - * @brief Initialization function for Q31 normalized LMS filter. - * @param[in] *S points to an instance of the Q31 normalized LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] *pCoeffs points to coefficient buffer. - * @param[in] *pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - * @return none. - */ - - void arm_lms_norm_init_q31( - arm_lms_norm_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - q31_t mu, - uint32_t blockSize, - uint8_t postShift); - - /** - * @brief Instance structure for the Q15 normalized LMS filter. - */ - - typedef struct - { - uint16_t numTaps; /**< Number of coefficients in the filter. */ - q15_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */ - q15_t mu; /**< step size that controls filter coefficient updates. */ - uint8_t postShift; /**< bit shift applied to coefficients. */ - q15_t *recipTable; /**< Points to the reciprocal initial value table. */ - q15_t energy; /**< saves previous frame energy. */ - q15_t x0; /**< saves previous input sample. */ - } arm_lms_norm_instance_q15; - - /** - * @brief Processing function for Q15 normalized LMS filter. - * @param[in] *S points to an instance of the Q15 normalized LMS filter structure. - * @param[in] *pSrc points to the block of input data. - * @param[in] *pRef points to the block of reference data. - * @param[out] *pOut points to the block of output data. - * @param[out] *pErr points to the block of error data. - * @param[in] blockSize number of samples to process. - * @return none. - */ - - void arm_lms_norm_q15( - arm_lms_norm_instance_q15 * S, - q15_t * pSrc, - q15_t * pRef, - q15_t * pOut, - q15_t * pErr, - uint32_t blockSize); - - - /** - * @brief Initialization function for Q15 normalized LMS filter. - * @param[in] *S points to an instance of the Q15 normalized LMS filter structure. - * @param[in] numTaps number of filter coefficients. - * @param[in] *pCoeffs points to coefficient buffer. - * @param[in] *pState points to state buffer. - * @param[in] mu step size that controls filter coefficient updates. - * @param[in] blockSize number of samples to process. - * @param[in] postShift bit shift applied to coefficients. - * @return none. - */ - - void arm_lms_norm_init_q15( - arm_lms_norm_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - q15_t mu, - uint32_t blockSize, - uint8_t postShift); - - /** - * @brief Correlation of floating-point sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @return none. - */ - - void arm_correlate_f32( - float32_t * pSrcA, - uint32_t srcALen, - float32_t * pSrcB, - uint32_t srcBLen, - float32_t * pDst); - - - /** - * @brief Correlation of Q15 sequences - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @param[in] *pScratch points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @return none. - */ - void arm_correlate_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch); - - - /** - * @brief Correlation of Q15 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @return none. - */ - - void arm_correlate_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - /** - * @brief Correlation of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @return none. - */ - - void arm_correlate_fast_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst); - - - - /** - * @brief Correlation of Q15 sequences (fast version) for Cortex-M3 and Cortex-M4. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @param[in] *pScratch points to scratch buffer of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @return none. - */ - - void arm_correlate_fast_opt_q15( - q15_t * pSrcA, - uint32_t srcALen, - q15_t * pSrcB, - uint32_t srcBLen, - q15_t * pDst, - q15_t * pScratch); - - /** - * @brief Correlation of Q31 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @return none. - */ - - void arm_correlate_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - /** - * @brief Correlation of Q31 sequences (fast version) for Cortex-M3 and Cortex-M4 - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @return none. - */ - - void arm_correlate_fast_q31( - q31_t * pSrcA, - uint32_t srcALen, - q31_t * pSrcB, - uint32_t srcBLen, - q31_t * pDst); - - - - /** - * @brief Correlation of Q7 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @param[in] *pScratch1 points to scratch buffer(of type q15_t) of size max(srcALen, srcBLen) + 2*min(srcALen, srcBLen) - 2. - * @param[in] *pScratch2 points to scratch buffer (of type q15_t) of size min(srcALen, srcBLen). - * @return none. - */ - - void arm_correlate_opt_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst, - q15_t * pScratch1, - q15_t * pScratch2); - - - /** - * @brief Correlation of Q7 sequences. - * @param[in] *pSrcA points to the first input sequence. - * @param[in] srcALen length of the first input sequence. - * @param[in] *pSrcB points to the second input sequence. - * @param[in] srcBLen length of the second input sequence. - * @param[out] *pDst points to the block of output data Length 2 * max(srcALen, srcBLen) - 1. - * @return none. - */ - - void arm_correlate_q7( - q7_t * pSrcA, - uint32_t srcALen, - q7_t * pSrcB, - uint32_t srcBLen, - q7_t * pDst); - - - /** - * @brief Instance structure for the floating-point sparse FIR filter. - */ - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - float32_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_f32; - - /** - * @brief Instance structure for the Q31 sparse FIR filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - q31_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - q31_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_q31; - - /** - * @brief Instance structure for the Q15 sparse FIR filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - q15_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - q15_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_q15; - - /** - * @brief Instance structure for the Q7 sparse FIR filter. - */ - - typedef struct - { - uint16_t numTaps; /**< number of coefficients in the filter. */ - uint16_t stateIndex; /**< state buffer index. Points to the oldest sample in the state buffer. */ - q7_t *pState; /**< points to the state buffer array. The array is of length maxDelay+blockSize-1. */ - q7_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps.*/ - uint16_t maxDelay; /**< maximum offset specified by the pTapDelay array. */ - int32_t *pTapDelay; /**< points to the array of delay values. The array is of length numTaps. */ - } arm_fir_sparse_instance_q7; - - /** - * @brief Processing function for the floating-point sparse FIR filter. - * @param[in] *S points to an instance of the floating-point sparse FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] *pScratchIn points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_sparse_f32( - arm_fir_sparse_instance_f32 * S, - float32_t * pSrc, - float32_t * pDst, - float32_t * pScratchIn, - uint32_t blockSize); - - /** - * @brief Initialization function for the floating-point sparse FIR filter. - * @param[in,out] *S points to an instance of the floating-point sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] *pCoeffs points to the array of filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] *pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - * @return none - */ - - void arm_fir_sparse_init_f32( - arm_fir_sparse_instance_f32 * S, - uint16_t numTaps, - float32_t * pCoeffs, - float32_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - /** - * @brief Processing function for the Q31 sparse FIR filter. - * @param[in] *S points to an instance of the Q31 sparse FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] *pScratchIn points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_sparse_q31( - arm_fir_sparse_instance_q31 * S, - q31_t * pSrc, - q31_t * pDst, - q31_t * pScratchIn, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q31 sparse FIR filter. - * @param[in,out] *S points to an instance of the Q31 sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] *pCoeffs points to the array of filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] *pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - * @return none - */ - - void arm_fir_sparse_init_q31( - arm_fir_sparse_instance_q31 * S, - uint16_t numTaps, - q31_t * pCoeffs, - q31_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - /** - * @brief Processing function for the Q15 sparse FIR filter. - * @param[in] *S points to an instance of the Q15 sparse FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] *pScratchIn points to a temporary buffer of size blockSize. - * @param[in] *pScratchOut points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_sparse_q15( - arm_fir_sparse_instance_q15 * S, - q15_t * pSrc, - q15_t * pDst, - q15_t * pScratchIn, - q31_t * pScratchOut, - uint32_t blockSize); - - - /** - * @brief Initialization function for the Q15 sparse FIR filter. - * @param[in,out] *S points to an instance of the Q15 sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] *pCoeffs points to the array of filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] *pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - * @return none - */ - - void arm_fir_sparse_init_q15( - arm_fir_sparse_instance_q15 * S, - uint16_t numTaps, - q15_t * pCoeffs, - q15_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - /** - * @brief Processing function for the Q7 sparse FIR filter. - * @param[in] *S points to an instance of the Q7 sparse FIR structure. - * @param[in] *pSrc points to the block of input data. - * @param[out] *pDst points to the block of output data - * @param[in] *pScratchIn points to a temporary buffer of size blockSize. - * @param[in] *pScratchOut points to a temporary buffer of size blockSize. - * @param[in] blockSize number of input samples to process per call. - * @return none. - */ - - void arm_fir_sparse_q7( - arm_fir_sparse_instance_q7 * S, - q7_t * pSrc, - q7_t * pDst, - q7_t * pScratchIn, - q31_t * pScratchOut, - uint32_t blockSize); - - /** - * @brief Initialization function for the Q7 sparse FIR filter. - * @param[in,out] *S points to an instance of the Q7 sparse FIR structure. - * @param[in] numTaps number of nonzero coefficients in the filter. - * @param[in] *pCoeffs points to the array of filter coefficients. - * @param[in] *pState points to the state buffer. - * @param[in] *pTapDelay points to the array of offset times. - * @param[in] maxDelay maximum offset time supported. - * @param[in] blockSize number of samples that will be processed per block. - * @return none - */ - - void arm_fir_sparse_init_q7( - arm_fir_sparse_instance_q7 * S, - uint16_t numTaps, - q7_t * pCoeffs, - q7_t * pState, - int32_t * pTapDelay, - uint16_t maxDelay, - uint32_t blockSize); - - - /* - * @brief Floating-point sin_cos function. - * @param[in] theta input value in degrees - * @param[out] *pSinVal points to the processed sine output. - * @param[out] *pCosVal points to the processed cos output. - * @return none. - */ - - void arm_sin_cos_f32( - float32_t theta, - float32_t * pSinVal, - float32_t * pCcosVal); - - /* - * @brief Q31 sin_cos function. - * @param[in] theta scaled input value in degrees - * @param[out] *pSinVal points to the processed sine output. - * @param[out] *pCosVal points to the processed cosine output. - * @return none. - */ - - void arm_sin_cos_q31( - q31_t theta, - q31_t * pSinVal, - q31_t * pCosVal); - - - /** - * @brief Floating-point complex conjugate. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - * @return none. - */ - - void arm_cmplx_conj_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t numSamples); - - /** - * @brief Q31 complex conjugate. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - * @return none. - */ - - void arm_cmplx_conj_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t numSamples); - - /** - * @brief Q15 complex conjugate. - * @param[in] *pSrc points to the input vector - * @param[out] *pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - * @return none. - */ - - void arm_cmplx_conj_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); - - - - /** - * @brief Floating-point complex magnitude squared - * @param[in] *pSrc points to the complex input vector - * @param[out] *pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - * @return none. - */ - - void arm_cmplx_mag_squared_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t numSamples); - - /** - * @brief Q31 complex magnitude squared - * @param[in] *pSrc points to the complex input vector - * @param[out] *pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - * @return none. - */ - - void arm_cmplx_mag_squared_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t numSamples); - - /** - * @brief Q15 complex magnitude squared - * @param[in] *pSrc points to the complex input vector - * @param[out] *pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - * @return none. - */ - - void arm_cmplx_mag_squared_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); - - - /** - * @ingroup groupController - */ - - /** - * @defgroup PID PID Motor Control - * - * A Proportional Integral Derivative (PID) controller is a generic feedback control - * loop mechanism widely used in industrial control systems. - * A PID controller is the most commonly used type of feedback controller. - * - * This set of functions implements (PID) controllers - * for Q15, Q31, and floating-point data types. The functions operate on a single sample - * of data and each call to the function returns a single processed value. - * S points to an instance of the PID control data structure. in - * is the input sample value. The functions return the output value. - * - * \par Algorithm: - *
    -   *    y[n] = y[n-1] + A0 * x[n] + A1 * x[n-1] + A2 * x[n-2]
    -   *    A0 = Kp + Ki + Kd
    -   *    A1 = (-Kp ) - (2 * Kd )
    -   *    A2 = Kd  
    - * - * \par - * where \c Kp is proportional constant, \c Ki is Integral constant and \c Kd is Derivative constant - * - * \par - * \image html PID.gif "Proportional Integral Derivative Controller" - * - * \par - * The PID controller calculates an "error" value as the difference between - * the measured output and the reference input. - * The controller attempts to minimize the error by adjusting the process control inputs. - * The proportional value determines the reaction to the current error, - * the integral value determines the reaction based on the sum of recent errors, - * and the derivative value determines the reaction based on the rate at which the error has been changing. - * - * \par Instance Structure - * The Gains A0, A1, A2 and state variables for a PID controller are stored together in an instance data structure. - * A separate instance structure must be defined for each PID Controller. - * There are separate instance structure declarations for each of the 3 supported data types. - * - * \par Reset Functions - * There is also an associated reset function for each data type which clears the state array. - * - * \par Initialization Functions - * There is also an associated initialization function for each data type. - * The initialization function performs the following operations: - * - Initializes the Gains A0, A1, A2 from Kp,Ki, Kd gains. - * - Zeros out the values in the state buffer. - * - * \par - * Instance structure cannot be placed into a const data section and it is recommended to use the initialization function. - * - * \par Fixed-Point Behavior - * Care must be taken when using the fixed-point versions of the PID Controller functions. - * In particular, the overflow and saturation behavior of the accumulator used in each function must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup PID - * @{ - */ - - /** - * @brief Process function for the floating-point PID Control. - * @param[in,out] *S is an instance of the floating-point PID Control structure - * @param[in] in input sample to process - * @return out processed output sample. - */ - - - static __INLINE float32_t arm_pid_f32( - arm_pid_instance_f32 * S, - float32_t in) - { - float32_t out; - - /* y[n] = y[n-1] + A0 * x[n] + A1 * x[n-1] + A2 * x[n-2] */ - out = (S->A0 * in) + - (S->A1 * S->state[0]) + (S->A2 * S->state[1]) + (S->state[2]); - - /* Update state */ - S->state[1] = S->state[0]; - S->state[0] = in; - S->state[2] = out; - - /* return to application */ - return (out); - - } - - /** - * @brief Process function for the Q31 PID Control. - * @param[in,out] *S points to an instance of the Q31 PID Control structure - * @param[in] in input sample to process - * @return out processed output sample. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 64-bit accumulator. - * The accumulator has a 2.62 format and maintains full precision of the intermediate multiplication results but provides only a single guard bit. - * Thus, if the accumulator result overflows it wraps around rather than clip. - * In order to avoid overflows completely the input signal must be scaled down by 2 bits as there are four additions. - * After all multiply-accumulates are performed, the 2.62 accumulator is truncated to 1.32 format and then saturated to 1.31 format. - */ - - static __INLINE q31_t arm_pid_q31( - arm_pid_instance_q31 * S, - q31_t in) - { - q63_t acc; - q31_t out; - - /* acc = A0 * x[n] */ - acc = (q63_t) S->A0 * in; - - /* acc += A1 * x[n-1] */ - acc += (q63_t) S->A1 * S->state[0]; - - /* acc += A2 * x[n-2] */ - acc += (q63_t) S->A2 * S->state[1]; - - /* convert output to 1.31 format to add y[n-1] */ - out = (q31_t) (acc >> 31u); - - /* out += y[n-1] */ - out += S->state[2]; - - /* Update state */ - S->state[1] = S->state[0]; - S->state[0] = in; - S->state[2] = out; - - /* return to application */ - return (out); - - } - - /** - * @brief Process function for the Q15 PID Control. - * @param[in,out] *S points to an instance of the Q15 PID Control structure - * @param[in] in input sample to process - * @return out processed output sample. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using a 64-bit internal accumulator. - * Both Gains and state variables are represented in 1.15 format and multiplications yield a 2.30 result. - * The 2.30 intermediate results are accumulated in a 64-bit accumulator in 34.30 format. - * There is no risk of internal overflow with this approach and the full precision of intermediate multiplications is preserved. - * After all additions have been performed, the accumulator is truncated to 34.15 format by discarding low 15 bits. - * Lastly, the accumulator is saturated to yield a result in 1.15 format. - */ - - static __INLINE q15_t arm_pid_q15( - arm_pid_instance_q15 * S, - q15_t in) - { - q63_t acc; - q15_t out; - -#ifndef ARM_MATH_CM0_FAMILY - __SIMD32_TYPE *vstate; - - /* Implementation of PID controller */ - - /* acc = A0 * x[n] */ - acc = (q31_t) __SMUAD(S->A0, in); - - /* acc += A1 * x[n-1] + A2 * x[n-2] */ - vstate = __SIMD32_CONST(S->state); - acc = __SMLALD(S->A1, (q31_t) *vstate, acc); - -#else - /* acc = A0 * x[n] */ - acc = ((q31_t) S->A0) * in; - - /* acc += A1 * x[n-1] + A2 * x[n-2] */ - acc += (q31_t) S->A1 * S->state[0]; - acc += (q31_t) S->A2 * S->state[1]; - -#endif - - /* acc += y[n-1] */ - acc += (q31_t) S->state[2] << 15; - - /* saturate the output */ - out = (q15_t) (__SSAT((acc >> 15), 16)); - - /* Update state */ - S->state[1] = S->state[0]; - S->state[0] = in; - S->state[2] = out; - - /* return to application */ - return (out); - - } - - /** - * @} end of PID group - */ - - - /** - * @brief Floating-point matrix inverse. - * @param[in] *src points to the instance of the input floating-point matrix structure. - * @param[out] *dst points to the instance of the output floating-point matrix structure. - * @return The function returns ARM_MATH_SIZE_MISMATCH, if the dimensions do not match. - * If the input matrix is singular (does not have an inverse), then the algorithm terminates and returns error status ARM_MATH_SINGULAR. - */ - - arm_status arm_mat_inverse_f32( - const arm_matrix_instance_f32 * src, - arm_matrix_instance_f32 * dst); - - - /** - * @brief Floating-point matrix inverse. - * @param[in] *src points to the instance of the input floating-point matrix structure. - * @param[out] *dst points to the instance of the output floating-point matrix structure. - * @return The function returns ARM_MATH_SIZE_MISMATCH, if the dimensions do not match. - * If the input matrix is singular (does not have an inverse), then the algorithm terminates and returns error status ARM_MATH_SINGULAR. - */ - - arm_status arm_mat_inverse_f64( - const arm_matrix_instance_f64 * src, - arm_matrix_instance_f64 * dst); - - - - /** - * @ingroup groupController - */ - - - /** - * @defgroup clarke Vector Clarke Transform - * Forward Clarke transform converts the instantaneous stator phases into a two-coordinate time invariant vector. - * Generally the Clarke transform uses three-phase currents Ia, Ib and Ic to calculate currents - * in the two-phase orthogonal stator axis Ialpha and Ibeta. - * When Ialpha is superposed with Ia as shown in the figure below - * \image html clarke.gif Stator current space vector and its components in (a,b). - * and Ia + Ib + Ic = 0, in this condition Ialpha and Ibeta - * can be calculated using only Ia and Ib. - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html clarkeFormula.gif - * where Ia and Ib are the instantaneous stator phases and - * pIalpha and pIbeta are the two coordinates of time invariant vector. - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Clarke transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup clarke - * @{ - */ - - /** - * - * @brief Floating-point Clarke transform - * @param[in] Ia input three-phase coordinate a - * @param[in] Ib input three-phase coordinate b - * @param[out] *pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] *pIbeta points to output two-phase orthogonal vector axis beta - * @return none. - */ - - static __INLINE void arm_clarke_f32( - float32_t Ia, - float32_t Ib, - float32_t * pIalpha, - float32_t * pIbeta) - { - /* Calculate pIalpha using the equation, pIalpha = Ia */ - *pIalpha = Ia; - - /* Calculate pIbeta using the equation, pIbeta = (1/sqrt(3)) * Ia + (2/sqrt(3)) * Ib */ - *pIbeta = - ((float32_t) 0.57735026919 * Ia + (float32_t) 1.15470053838 * Ib); - - } - - /** - * @brief Clarke transform for Q31 version - * @param[in] Ia input three-phase coordinate a - * @param[in] Ib input three-phase coordinate b - * @param[out] *pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] *pIbeta points to output two-phase orthogonal vector axis beta - * @return none. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the addition, hence there is no risk of overflow. - */ - - static __INLINE void arm_clarke_q31( - q31_t Ia, - q31_t Ib, - q31_t * pIalpha, - q31_t * pIbeta) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - - /* Calculating pIalpha from Ia by equation pIalpha = Ia */ - *pIalpha = Ia; - - /* Intermediate product is calculated by (1/(sqrt(3)) * Ia) */ - product1 = (q31_t) (((q63_t) Ia * 0x24F34E8B) >> 30); - - /* Intermediate product is calculated by (2/sqrt(3) * Ib) */ - product2 = (q31_t) (((q63_t) Ib * 0x49E69D16) >> 30); - - /* pIbeta is calculated by adding the intermediate products */ - *pIbeta = __QADD(product1, product2); - } - - /** - * @} end of clarke group - */ - - /** - * @brief Converts the elements of the Q7 vector to Q31 vector. - * @param[in] *pSrc input pointer - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_q7_to_q31( - q7_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - - - /** - * @ingroup groupController - */ - - /** - * @defgroup inv_clarke Vector Inverse Clarke Transform - * Inverse Clarke transform converts the two-coordinate time invariant vector into instantaneous stator phases. - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html clarkeInvFormula.gif - * where pIa and pIb are the instantaneous stator phases and - * Ialpha and Ibeta are the two coordinates of time invariant vector. - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Clarke transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup inv_clarke - * @{ - */ - - /** - * @brief Floating-point Inverse Clarke transform - * @param[in] Ialpha input two-phase orthogonal vector axis alpha - * @param[in] Ibeta input two-phase orthogonal vector axis beta - * @param[out] *pIa points to output three-phase coordinate a - * @param[out] *pIb points to output three-phase coordinate b - * @return none. - */ - - - static __INLINE void arm_inv_clarke_f32( - float32_t Ialpha, - float32_t Ibeta, - float32_t * pIa, - float32_t * pIb) - { - /* Calculating pIa from Ialpha by equation pIa = Ialpha */ - *pIa = Ialpha; - - /* Calculating pIb from Ialpha and Ibeta by equation pIb = -(1/2) * Ialpha + (sqrt(3)/2) * Ibeta */ - *pIb = -0.5 * Ialpha + (float32_t) 0.8660254039 *Ibeta; - - } - - /** - * @brief Inverse Clarke transform for Q31 version - * @param[in] Ialpha input two-phase orthogonal vector axis alpha - * @param[in] Ibeta input two-phase orthogonal vector axis beta - * @param[out] *pIa points to output three-phase coordinate a - * @param[out] *pIb points to output three-phase coordinate b - * @return none. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the subtraction, hence there is no risk of overflow. - */ - - static __INLINE void arm_inv_clarke_q31( - q31_t Ialpha, - q31_t Ibeta, - q31_t * pIa, - q31_t * pIb) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - - /* Calculating pIa from Ialpha by equation pIa = Ialpha */ - *pIa = Ialpha; - - /* Intermediate product is calculated by (1/(2*sqrt(3)) * Ia) */ - product1 = (q31_t) (((q63_t) (Ialpha) * (0x40000000)) >> 31); - - /* Intermediate product is calculated by (1/sqrt(3) * pIb) */ - product2 = (q31_t) (((q63_t) (Ibeta) * (0x6ED9EBA1)) >> 31); - - /* pIb is calculated by subtracting the products */ - *pIb = __QSUB(product2, product1); - - } - - /** - * @} end of inv_clarke group - */ - - /** - * @brief Converts the elements of the Q7 vector to Q15 vector. - * @param[in] *pSrc input pointer - * @param[out] *pDst output pointer - * @param[in] blockSize number of samples to process - * @return none. - */ - void arm_q7_to_q15( - q7_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - - - /** - * @ingroup groupController - */ - - /** - * @defgroup park Vector Park Transform - * - * Forward Park transform converts the input two-coordinate vector to flux and torque components. - * The Park transform can be used to realize the transformation of the Ialpha and the Ibeta currents - * from the stationary to the moving reference frame and control the spatial relationship between - * the stator vector current and rotor flux vector. - * If we consider the d axis aligned with the rotor flux, the diagram below shows the - * current vector and the relationship from the two reference frames: - * \image html park.gif "Stator current space vector and its component in (a,b) and in the d,q rotating reference frame" - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html parkFormula.gif - * where Ialpha and Ibeta are the stator vector components, - * pId and pIq are rotor vector components and cosVal and sinVal are the - * cosine and sine values of theta (rotor flux position). - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Park transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup park - * @{ - */ - - /** - * @brief Floating-point Park transform - * @param[in] Ialpha input two-phase vector coordinate alpha - * @param[in] Ibeta input two-phase vector coordinate beta - * @param[out] *pId points to output rotor reference frame d - * @param[out] *pIq points to output rotor reference frame q - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * @return none. - * - * The function implements the forward Park transform. - * - */ - - static __INLINE void arm_park_f32( - float32_t Ialpha, - float32_t Ibeta, - float32_t * pId, - float32_t * pIq, - float32_t sinVal, - float32_t cosVal) - { - /* Calculate pId using the equation, pId = Ialpha * cosVal + Ibeta * sinVal */ - *pId = Ialpha * cosVal + Ibeta * sinVal; - - /* Calculate pIq using the equation, pIq = - Ialpha * sinVal + Ibeta * cosVal */ - *pIq = -Ialpha * sinVal + Ibeta * cosVal; - - } - - /** - * @brief Park transform for Q31 version - * @param[in] Ialpha input two-phase vector coordinate alpha - * @param[in] Ibeta input two-phase vector coordinate beta - * @param[out] *pId points to output rotor reference frame d - * @param[out] *pIq points to output rotor reference frame q - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * @return none. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the addition and subtraction, hence there is no risk of overflow. - */ - - - static __INLINE void arm_park_q31( - q31_t Ialpha, - q31_t Ibeta, - q31_t * pId, - q31_t * pIq, - q31_t sinVal, - q31_t cosVal) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - q31_t product3, product4; /* Temporary variables used to store intermediate results */ - - /* Intermediate product is calculated by (Ialpha * cosVal) */ - product1 = (q31_t) (((q63_t) (Ialpha) * (cosVal)) >> 31); - - /* Intermediate product is calculated by (Ibeta * sinVal) */ - product2 = (q31_t) (((q63_t) (Ibeta) * (sinVal)) >> 31); - - - /* Intermediate product is calculated by (Ialpha * sinVal) */ - product3 = (q31_t) (((q63_t) (Ialpha) * (sinVal)) >> 31); - - /* Intermediate product is calculated by (Ibeta * cosVal) */ - product4 = (q31_t) (((q63_t) (Ibeta) * (cosVal)) >> 31); - - /* Calculate pId by adding the two intermediate products 1 and 2 */ - *pId = __QADD(product1, product2); - - /* Calculate pIq by subtracting the two intermediate products 3 from 4 */ - *pIq = __QSUB(product4, product3); - } - - /** - * @} end of park group - */ - - /** - * @brief Converts the elements of the Q7 vector to floating-point vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q7_to_float( - q7_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @ingroup groupController - */ - - /** - * @defgroup inv_park Vector Inverse Park transform - * Inverse Park transform converts the input flux and torque components to two-coordinate vector. - * - * The function operates on a single sample of data and each call to the function returns the processed output. - * The library provides separate functions for Q31 and floating-point data types. - * \par Algorithm - * \image html parkInvFormula.gif - * where pIalpha and pIbeta are the stator vector components, - * Id and Iq are rotor vector components and cosVal and sinVal are the - * cosine and sine values of theta (rotor flux position). - * \par Fixed-Point Behavior - * Care must be taken when using the Q31 version of the Park transform. - * In particular, the overflow and saturation behavior of the accumulator used must be considered. - * Refer to the function specific documentation below for usage guidelines. - */ - - /** - * @addtogroup inv_park - * @{ - */ - - /** - * @brief Floating-point Inverse Park transform - * @param[in] Id input coordinate of rotor reference frame d - * @param[in] Iq input coordinate of rotor reference frame q - * @param[out] *pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] *pIbeta points to output two-phase orthogonal vector axis beta - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * @return none. - */ - - static __INLINE void arm_inv_park_f32( - float32_t Id, - float32_t Iq, - float32_t * pIalpha, - float32_t * pIbeta, - float32_t sinVal, - float32_t cosVal) - { - /* Calculate pIalpha using the equation, pIalpha = Id * cosVal - Iq * sinVal */ - *pIalpha = Id * cosVal - Iq * sinVal; - - /* Calculate pIbeta using the equation, pIbeta = Id * sinVal + Iq * cosVal */ - *pIbeta = Id * sinVal + Iq * cosVal; - - } - - - /** - * @brief Inverse Park transform for Q31 version - * @param[in] Id input coordinate of rotor reference frame d - * @param[in] Iq input coordinate of rotor reference frame q - * @param[out] *pIalpha points to output two-phase orthogonal vector axis alpha - * @param[out] *pIbeta points to output two-phase orthogonal vector axis beta - * @param[in] sinVal sine value of rotation angle theta - * @param[in] cosVal cosine value of rotation angle theta - * @return none. - * - * Scaling and Overflow Behavior: - * \par - * The function is implemented using an internal 32-bit accumulator. - * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. - * There is saturation on the addition, hence there is no risk of overflow. - */ - - - static __INLINE void arm_inv_park_q31( - q31_t Id, - q31_t Iq, - q31_t * pIalpha, - q31_t * pIbeta, - q31_t sinVal, - q31_t cosVal) - { - q31_t product1, product2; /* Temporary variables used to store intermediate results */ - q31_t product3, product4; /* Temporary variables used to store intermediate results */ - - /* Intermediate product is calculated by (Id * cosVal) */ - product1 = (q31_t) (((q63_t) (Id) * (cosVal)) >> 31); - - /* Intermediate product is calculated by (Iq * sinVal) */ - product2 = (q31_t) (((q63_t) (Iq) * (sinVal)) >> 31); - - - /* Intermediate product is calculated by (Id * sinVal) */ - product3 = (q31_t) (((q63_t) (Id) * (sinVal)) >> 31); - - /* Intermediate product is calculated by (Iq * cosVal) */ - product4 = (q31_t) (((q63_t) (Iq) * (cosVal)) >> 31); - - /* Calculate pIalpha by using the two intermediate products 1 and 2 */ - *pIalpha = __QSUB(product1, product2); - - /* Calculate pIbeta by using the two intermediate products 3 and 4 */ - *pIbeta = __QADD(product4, product3); - - } - - /** - * @} end of Inverse park group - */ - - - /** - * @brief Converts the elements of the Q31 vector to floating-point vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q31_to_float( - q31_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - /** - * @ingroup groupInterpolation - */ - - /** - * @defgroup LinearInterpolate Linear Interpolation - * - * Linear interpolation is a method of curve fitting using linear polynomials. - * Linear interpolation works by effectively drawing a straight line between two neighboring samples and returning the appropriate point along that line - * - * \par - * \image html LinearInterp.gif "Linear interpolation" - * - * \par - * A Linear Interpolate function calculates an output value(y), for the input(x) - * using linear interpolation of the input values x0, x1( nearest input values) and the output values y0 and y1(nearest output values) - * - * \par Algorithm: - *
    -   *       y = y0 + (x - x0) * ((y1 - y0)/(x1-x0))
    -   *       where x0, x1 are nearest values of input x
    -   *             y0, y1 are nearest values to output y
    -   * 
    - * - * \par - * This set of functions implements Linear interpolation process - * for Q7, Q15, Q31, and floating-point data types. The functions operate on a single - * sample of data and each call to the function returns a single processed value. - * S points to an instance of the Linear Interpolate function data structure. - * x is the input sample value. The functions returns the output value. - * - * \par - * if x is outside of the table boundary, Linear interpolation returns first value of the table - * if x is below input range and returns last value of table if x is above range. - */ - - /** - * @addtogroup LinearInterpolate - * @{ - */ - - /** - * @brief Process function for the floating-point Linear Interpolation Function. - * @param[in,out] *S is an instance of the floating-point Linear Interpolation structure - * @param[in] x input sample to process - * @return y processed output sample. - * - */ - - static __INLINE float32_t arm_linear_interp_f32( - arm_linear_interp_instance_f32 * S, - float32_t x) - { - - float32_t y; - float32_t x0, x1; /* Nearest input values */ - float32_t y0, y1; /* Nearest output values */ - float32_t xSpacing = S->xSpacing; /* spacing between input values */ - int32_t i; /* Index variable */ - float32_t *pYData = S->pYData; /* pointer to output table */ - - /* Calculation of index */ - i = (int32_t) ((x - S->x1) / xSpacing); - - if(i < 0) - { - /* Iniatilize output for below specified range as least output value of table */ - y = pYData[0]; - } - else if((uint32_t)i >= S->nValues) - { - /* Iniatilize output for above specified range as last output value of table */ - y = pYData[S->nValues - 1]; - } - else - { - /* Calculation of nearest input values */ - x0 = S->x1 + i * xSpacing; - x1 = S->x1 + (i + 1) * xSpacing; - - /* Read of nearest output values */ - y0 = pYData[i]; - y1 = pYData[i + 1]; - - /* Calculation of output */ - y = y0 + (x - x0) * ((y1 - y0) / (x1 - x0)); - - } - - /* returns output value */ - return (y); - } - - /** - * - * @brief Process function for the Q31 Linear Interpolation Function. - * @param[in] *pYData pointer to Q31 Linear Interpolation table - * @param[in] x input sample to process - * @param[in] nValues number of table values - * @return y processed output sample. - * - * \par - * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. - * This function can support maximum of table size 2^12. - * - */ - - - static __INLINE q31_t arm_linear_interp_q31( - q31_t * pYData, - q31_t x, - uint32_t nValues) - { - q31_t y; /* output */ - q31_t y0, y1; /* Nearest output values */ - q31_t fract; /* fractional part */ - int32_t index; /* Index to read nearest output values */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - index = ((x & 0xFFF00000) >> 20); - - if(index >= (int32_t)(nValues - 1)) - { - return (pYData[nValues - 1]); - } - else if(index < 0) - { - return (pYData[0]); - } - else - { - - /* 20 bits for the fractional part */ - /* shift left by 11 to keep fract in 1.31 format */ - fract = (x & 0x000FFFFF) << 11; - - /* Read two nearest output values from the index in 1.31(q31) format */ - y0 = pYData[index]; - y1 = pYData[index + 1u]; - - /* Calculation of y0 * (1-fract) and y is in 2.30 format */ - y = ((q31_t) ((q63_t) y0 * (0x7FFFFFFF - fract) >> 32)); - - /* Calculation of y0 * (1-fract) + y1 *fract and y is in 2.30 format */ - y += ((q31_t) (((q63_t) y1 * fract) >> 32)); - - /* Convert y to 1.31 format */ - return (y << 1u); - - } - - } - - /** - * - * @brief Process function for the Q15 Linear Interpolation Function. - * @param[in] *pYData pointer to Q15 Linear Interpolation table - * @param[in] x input sample to process - * @param[in] nValues number of table values - * @return y processed output sample. - * - * \par - * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. - * This function can support maximum of table size 2^12. - * - */ - - - static __INLINE q15_t arm_linear_interp_q15( - q15_t * pYData, - q31_t x, - uint32_t nValues) - { - q63_t y; /* output */ - q15_t y0, y1; /* Nearest output values */ - q31_t fract; /* fractional part */ - int32_t index; /* Index to read nearest output values */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - index = ((x & 0xFFF00000) >> 20u); - - if(index >= (int32_t)(nValues - 1)) - { - return (pYData[nValues - 1]); - } - else if(index < 0) - { - return (pYData[0]); - } - else - { - /* 20 bits for the fractional part */ - /* fract is in 12.20 format */ - fract = (x & 0x000FFFFF); - - /* Read two nearest output values from the index */ - y0 = pYData[index]; - y1 = pYData[index + 1u]; - - /* Calculation of y0 * (1-fract) and y is in 13.35 format */ - y = ((q63_t) y0 * (0xFFFFF - fract)); - - /* Calculation of (y0 * (1-fract) + y1 * fract) and y is in 13.35 format */ - y += ((q63_t) y1 * (fract)); - - /* convert y to 1.15 format */ - return (y >> 20); - } - - - } - - /** - * - * @brief Process function for the Q7 Linear Interpolation Function. - * @param[in] *pYData pointer to Q7 Linear Interpolation table - * @param[in] x input sample to process - * @param[in] nValues number of table values - * @return y processed output sample. - * - * \par - * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. - * This function can support maximum of table size 2^12. - */ - - - static __INLINE q7_t arm_linear_interp_q7( - q7_t * pYData, - q31_t x, - uint32_t nValues) - { - q31_t y; /* output */ - q7_t y0, y1; /* Nearest output values */ - q31_t fract; /* fractional part */ - uint32_t index; /* Index to read nearest output values */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - if (x < 0) - { - return (pYData[0]); - } - index = (x >> 20) & 0xfff; - - - if(index >= (nValues - 1)) - { - return (pYData[nValues - 1]); - } - else - { - - /* 20 bits for the fractional part */ - /* fract is in 12.20 format */ - fract = (x & 0x000FFFFF); - - /* Read two nearest output values from the index and are in 1.7(q7) format */ - y0 = pYData[index]; - y1 = pYData[index + 1u]; - - /* Calculation of y0 * (1-fract ) and y is in 13.27(q27) format */ - y = ((y0 * (0xFFFFF - fract))); - - /* Calculation of y1 * fract + y0 * (1-fract) and y is in 13.27(q27) format */ - y += (y1 * fract); - - /* convert y to 1.7(q7) format */ - return (y >> 20u); - - } - - } - /** - * @} end of LinearInterpolate group - */ - - /** - * @brief Fast approximation to the trigonometric sine function for floating-point data. - * @param[in] x input value in radians. - * @return sin(x). - */ - - float32_t arm_sin_f32( - float32_t x); - - /** - * @brief Fast approximation to the trigonometric sine function for Q31 data. - * @param[in] x Scaled input value in radians. - * @return sin(x). - */ - - q31_t arm_sin_q31( - q31_t x); - - /** - * @brief Fast approximation to the trigonometric sine function for Q15 data. - * @param[in] x Scaled input value in radians. - * @return sin(x). - */ - - q15_t arm_sin_q15( - q15_t x); - - /** - * @brief Fast approximation to the trigonometric cosine function for floating-point data. - * @param[in] x input value in radians. - * @return cos(x). - */ - - float32_t arm_cos_f32( - float32_t x); - - /** - * @brief Fast approximation to the trigonometric cosine function for Q31 data. - * @param[in] x Scaled input value in radians. - * @return cos(x). - */ - - q31_t arm_cos_q31( - q31_t x); - - /** - * @brief Fast approximation to the trigonometric cosine function for Q15 data. - * @param[in] x Scaled input value in radians. - * @return cos(x). - */ - - q15_t arm_cos_q15( - q15_t x); - - - /** - * @ingroup groupFastMath - */ - - - /** - * @defgroup SQRT Square Root - * - * Computes the square root of a number. - * There are separate functions for Q15, Q31, and floating-point data types. - * The square root function is computed using the Newton-Raphson algorithm. - * This is an iterative algorithm of the form: - *
    -   *      x1 = x0 - f(x0)/f'(x0)
    -   * 
    - * where x1 is the current estimate, - * x0 is the previous estimate, and - * f'(x0) is the derivative of f() evaluated at x0. - * For the square root function, the algorithm reduces to: - *
    -   *     x0 = in/2                         [initial guess]
    -   *     x1 = 1/2 * ( x0 + in / x0)        [each iteration]
    -   * 
    - */ - - - /** - * @addtogroup SQRT - * @{ - */ - - /** - * @brief Floating-point square root function. - * @param[in] in input value. - * @param[out] *pOut square root of input value. - * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if - * in is negative value and returns zero output for negative values. - */ - - static __INLINE arm_status arm_sqrt_f32( - float32_t in, - float32_t * pOut) - { - if(in >= 0.0f) - { - -// #if __FPU_USED -#if (__FPU_USED == 1) && defined ( __CC_ARM ) - *pOut = __sqrtf(in); -#else - *pOut = sqrtf(in); -#endif - - return (ARM_MATH_SUCCESS); - } - else - { - *pOut = 0.0f; - return (ARM_MATH_ARGUMENT_ERROR); - } - - } - - - /** - * @brief Q31 square root function. - * @param[in] in input value. The range of the input value is [0 +1) or 0x00000000 to 0x7FFFFFFF. - * @param[out] *pOut square root of input value. - * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if - * in is negative value and returns zero output for negative values. - */ - arm_status arm_sqrt_q31( - q31_t in, - q31_t * pOut); - - /** - * @brief Q15 square root function. - * @param[in] in input value. The range of the input value is [0 +1) or 0x0000 to 0x7FFF. - * @param[out] *pOut square root of input value. - * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if - * in is negative value and returns zero output for negative values. - */ - arm_status arm_sqrt_q15( - q15_t in, - q15_t * pOut); - - /** - * @} end of SQRT group - */ - - - - - - - /** - * @brief floating-point Circular write function. - */ - - static __INLINE void arm_circularWrite_f32( - int32_t * circBuffer, - int32_t L, - uint16_t * writeOffset, - int32_t bufferInc, - const int32_t * src, - int32_t srcInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t wOffset; - - /* Copy the value of Index pointer that points - * to the current location where the input samples to be copied */ - wOffset = *writeOffset; - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the input sample to the circular buffer */ - circBuffer[wOffset] = *src; - - /* Update the input pointer */ - src += srcInc; - - /* Circularly update wOffset. Watch out for positive and negative value */ - wOffset += bufferInc; - if(wOffset >= L) - wOffset -= L; - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *writeOffset = wOffset; - } - - - - /** - * @brief floating-point Circular Read function. - */ - static __INLINE void arm_circularRead_f32( - int32_t * circBuffer, - int32_t L, - int32_t * readOffset, - int32_t bufferInc, - int32_t * dst, - int32_t * dst_base, - int32_t dst_length, - int32_t dstInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t rOffset, dst_end; - - /* Copy the value of Index pointer that points - * to the current location from where the input samples to be read */ - rOffset = *readOffset; - dst_end = (int32_t) (dst_base + dst_length); - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the sample from the circular buffer to the destination buffer */ - *dst = circBuffer[rOffset]; - - /* Update the input pointer */ - dst += dstInc; - - if(dst == (int32_t *) dst_end) - { - dst = dst_base; - } - - /* Circularly update rOffset. Watch out for positive and negative value */ - rOffset += bufferInc; - - if(rOffset >= L) - { - rOffset -= L; - } - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *readOffset = rOffset; - } - - /** - * @brief Q15 Circular write function. - */ - - static __INLINE void arm_circularWrite_q15( - q15_t * circBuffer, - int32_t L, - uint16_t * writeOffset, - int32_t bufferInc, - const q15_t * src, - int32_t srcInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t wOffset; - - /* Copy the value of Index pointer that points - * to the current location where the input samples to be copied */ - wOffset = *writeOffset; - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the input sample to the circular buffer */ - circBuffer[wOffset] = *src; - - /* Update the input pointer */ - src += srcInc; - - /* Circularly update wOffset. Watch out for positive and negative value */ - wOffset += bufferInc; - if(wOffset >= L) - wOffset -= L; - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *writeOffset = wOffset; - } - - - - /** - * @brief Q15 Circular Read function. - */ - static __INLINE void arm_circularRead_q15( - q15_t * circBuffer, - int32_t L, - int32_t * readOffset, - int32_t bufferInc, - q15_t * dst, - q15_t * dst_base, - int32_t dst_length, - int32_t dstInc, - uint32_t blockSize) - { - uint32_t i = 0; - int32_t rOffset, dst_end; - - /* Copy the value of Index pointer that points - * to the current location from where the input samples to be read */ - rOffset = *readOffset; - - dst_end = (int32_t) (dst_base + dst_length); - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the sample from the circular buffer to the destination buffer */ - *dst = circBuffer[rOffset]; - - /* Update the input pointer */ - dst += dstInc; - - if(dst == (q15_t *) dst_end) - { - dst = dst_base; - } - - /* Circularly update wOffset. Watch out for positive and negative value */ - rOffset += bufferInc; - - if(rOffset >= L) - { - rOffset -= L; - } - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *readOffset = rOffset; - } - - - /** - * @brief Q7 Circular write function. - */ - - static __INLINE void arm_circularWrite_q7( - q7_t * circBuffer, - int32_t L, - uint16_t * writeOffset, - int32_t bufferInc, - const q7_t * src, - int32_t srcInc, - uint32_t blockSize) - { - uint32_t i = 0u; - int32_t wOffset; - - /* Copy the value of Index pointer that points - * to the current location where the input samples to be copied */ - wOffset = *writeOffset; - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the input sample to the circular buffer */ - circBuffer[wOffset] = *src; - - /* Update the input pointer */ - src += srcInc; - - /* Circularly update wOffset. Watch out for positive and negative value */ - wOffset += bufferInc; - if(wOffset >= L) - wOffset -= L; - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *writeOffset = wOffset; - } - - - - /** - * @brief Q7 Circular Read function. - */ - static __INLINE void arm_circularRead_q7( - q7_t * circBuffer, - int32_t L, - int32_t * readOffset, - int32_t bufferInc, - q7_t * dst, - q7_t * dst_base, - int32_t dst_length, - int32_t dstInc, - uint32_t blockSize) - { - uint32_t i = 0; - int32_t rOffset, dst_end; - - /* Copy the value of Index pointer that points - * to the current location from where the input samples to be read */ - rOffset = *readOffset; - - dst_end = (int32_t) (dst_base + dst_length); - - /* Loop over the blockSize */ - i = blockSize; - - while(i > 0u) - { - /* copy the sample from the circular buffer to the destination buffer */ - *dst = circBuffer[rOffset]; - - /* Update the input pointer */ - dst += dstInc; - - if(dst == (q7_t *) dst_end) - { - dst = dst_base; - } - - /* Circularly update rOffset. Watch out for positive and negative value */ - rOffset += bufferInc; - - if(rOffset >= L) - { - rOffset -= L; - } - - /* Decrement the loop counter */ - i--; - } - - /* Update the index pointer */ - *readOffset = rOffset; - } - - - /** - * @brief Sum of the squares of the elements of a Q31 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_power_q31( - q31_t * pSrc, - uint32_t blockSize, - q63_t * pResult); - - /** - * @brief Sum of the squares of the elements of a floating-point vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_power_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - /** - * @brief Sum of the squares of the elements of a Q15 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_power_q15( - q15_t * pSrc, - uint32_t blockSize, - q63_t * pResult); - - /** - * @brief Sum of the squares of the elements of a Q7 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_power_q7( - q7_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - /** - * @brief Mean value of a Q7 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_mean_q7( - q7_t * pSrc, - uint32_t blockSize, - q7_t * pResult); - - /** - * @brief Mean value of a Q15 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - void arm_mean_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - /** - * @brief Mean value of a Q31 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - void arm_mean_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - /** - * @brief Mean value of a floating-point vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - void arm_mean_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - /** - * @brief Variance of the elements of a floating-point vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_var_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - /** - * @brief Variance of the elements of a Q31 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_var_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - /** - * @brief Variance of the elements of a Q15 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_var_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - /** - * @brief Root Mean Square of the elements of a floating-point vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_rms_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - /** - * @brief Root Mean Square of the elements of a Q31 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_rms_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - /** - * @brief Root Mean Square of the elements of a Q15 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_rms_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - /** - * @brief Standard deviation of the elements of a floating-point vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_std_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult); - - /** - * @brief Standard deviation of the elements of a Q31 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_std_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult); - - /** - * @brief Standard deviation of the elements of a Q15 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output value. - * @return none. - */ - - void arm_std_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult); - - /** - * @brief Floating-point complex magnitude - * @param[in] *pSrc points to the complex input vector - * @param[out] *pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - * @return none. - */ - - void arm_cmplx_mag_f32( - float32_t * pSrc, - float32_t * pDst, - uint32_t numSamples); - - /** - * @brief Q31 complex magnitude - * @param[in] *pSrc points to the complex input vector - * @param[out] *pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - * @return none. - */ - - void arm_cmplx_mag_q31( - q31_t * pSrc, - q31_t * pDst, - uint32_t numSamples); - - /** - * @brief Q15 complex magnitude - * @param[in] *pSrc points to the complex input vector - * @param[out] *pDst points to the real output vector - * @param[in] numSamples number of complex samples in the input vector - * @return none. - */ - - void arm_cmplx_mag_q15( - q15_t * pSrc, - q15_t * pDst, - uint32_t numSamples); - - /** - * @brief Q15 complex dot product - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] numSamples number of complex samples in each vector - * @param[out] *realResult real part of the result returned here - * @param[out] *imagResult imaginary part of the result returned here - * @return none. - */ - - void arm_cmplx_dot_prod_q15( - q15_t * pSrcA, - q15_t * pSrcB, - uint32_t numSamples, - q31_t * realResult, - q31_t * imagResult); - - /** - * @brief Q31 complex dot product - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] numSamples number of complex samples in each vector - * @param[out] *realResult real part of the result returned here - * @param[out] *imagResult imaginary part of the result returned here - * @return none. - */ - - void arm_cmplx_dot_prod_q31( - q31_t * pSrcA, - q31_t * pSrcB, - uint32_t numSamples, - q63_t * realResult, - q63_t * imagResult); - - /** - * @brief Floating-point complex dot product - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[in] numSamples number of complex samples in each vector - * @param[out] *realResult real part of the result returned here - * @param[out] *imagResult imaginary part of the result returned here - * @return none. - */ - - void arm_cmplx_dot_prod_f32( - float32_t * pSrcA, - float32_t * pSrcB, - uint32_t numSamples, - float32_t * realResult, - float32_t * imagResult); - - /** - * @brief Q15 complex-by-real multiplication - * @param[in] *pSrcCmplx points to the complex input vector - * @param[in] *pSrcReal points to the real input vector - * @param[out] *pCmplxDst points to the complex output vector - * @param[in] numSamples number of samples in each vector - * @return none. - */ - - void arm_cmplx_mult_real_q15( - q15_t * pSrcCmplx, - q15_t * pSrcReal, - q15_t * pCmplxDst, - uint32_t numSamples); - - /** - * @brief Q31 complex-by-real multiplication - * @param[in] *pSrcCmplx points to the complex input vector - * @param[in] *pSrcReal points to the real input vector - * @param[out] *pCmplxDst points to the complex output vector - * @param[in] numSamples number of samples in each vector - * @return none. - */ - - void arm_cmplx_mult_real_q31( - q31_t * pSrcCmplx, - q31_t * pSrcReal, - q31_t * pCmplxDst, - uint32_t numSamples); - - /** - * @brief Floating-point complex-by-real multiplication - * @param[in] *pSrcCmplx points to the complex input vector - * @param[in] *pSrcReal points to the real input vector - * @param[out] *pCmplxDst points to the complex output vector - * @param[in] numSamples number of samples in each vector - * @return none. - */ - - void arm_cmplx_mult_real_f32( - float32_t * pSrcCmplx, - float32_t * pSrcReal, - float32_t * pCmplxDst, - uint32_t numSamples); - - /** - * @brief Minimum value of a Q7 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *result is output pointer - * @param[in] index is the array index of the minimum value in the input buffer. - * @return none. - */ - - void arm_min_q7( - q7_t * pSrc, - uint32_t blockSize, - q7_t * result, - uint32_t * index); - - /** - * @brief Minimum value of a Q15 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output pointer - * @param[in] *pIndex is the array index of the minimum value in the input buffer. - * @return none. - */ - - void arm_min_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult, - uint32_t * pIndex); - - /** - * @brief Minimum value of a Q31 vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output pointer - * @param[out] *pIndex is the array index of the minimum value in the input buffer. - * @return none. - */ - void arm_min_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult, - uint32_t * pIndex); - - /** - * @brief Minimum value of a floating-point vector. - * @param[in] *pSrc is input pointer - * @param[in] blockSize is the number of samples to process - * @param[out] *pResult is output pointer - * @param[out] *pIndex is the array index of the minimum value in the input buffer. - * @return none. - */ - - void arm_min_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult, - uint32_t * pIndex); - -/** - * @brief Maximum value of a Q7 vector. - * @param[in] *pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] *pResult maximum value returned here - * @param[out] *pIndex index of maximum value returned here - * @return none. - */ - - void arm_max_q7( - q7_t * pSrc, - uint32_t blockSize, - q7_t * pResult, - uint32_t * pIndex); - -/** - * @brief Maximum value of a Q15 vector. - * @param[in] *pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] *pResult maximum value returned here - * @param[out] *pIndex index of maximum value returned here - * @return none. - */ - - void arm_max_q15( - q15_t * pSrc, - uint32_t blockSize, - q15_t * pResult, - uint32_t * pIndex); - -/** - * @brief Maximum value of a Q31 vector. - * @param[in] *pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] *pResult maximum value returned here - * @param[out] *pIndex index of maximum value returned here - * @return none. - */ - - void arm_max_q31( - q31_t * pSrc, - uint32_t blockSize, - q31_t * pResult, - uint32_t * pIndex); - -/** - * @brief Maximum value of a floating-point vector. - * @param[in] *pSrc points to the input buffer - * @param[in] blockSize length of the input vector - * @param[out] *pResult maximum value returned here - * @param[out] *pIndex index of maximum value returned here - * @return none. - */ - - void arm_max_f32( - float32_t * pSrc, - uint32_t blockSize, - float32_t * pResult, - uint32_t * pIndex); - - /** - * @brief Q15 complex-by-complex multiplication - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - * @return none. - */ - - void arm_cmplx_mult_cmplx_q15( - q15_t * pSrcA, - q15_t * pSrcB, - q15_t * pDst, - uint32_t numSamples); - - /** - * @brief Q31 complex-by-complex multiplication - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - * @return none. - */ - - void arm_cmplx_mult_cmplx_q31( - q31_t * pSrcA, - q31_t * pSrcB, - q31_t * pDst, - uint32_t numSamples); - - /** - * @brief Floating-point complex-by-complex multiplication - * @param[in] *pSrcA points to the first input vector - * @param[in] *pSrcB points to the second input vector - * @param[out] *pDst points to the output vector - * @param[in] numSamples number of complex samples in each vector - * @return none. - */ - - void arm_cmplx_mult_cmplx_f32( - float32_t * pSrcA, - float32_t * pSrcB, - float32_t * pDst, - uint32_t numSamples); - - /** - * @brief Converts the elements of the floating-point vector to Q31 vector. - * @param[in] *pSrc points to the floating-point input vector - * @param[out] *pDst points to the Q31 output vector - * @param[in] blockSize length of the input vector - * @return none. - */ - void arm_float_to_q31( - float32_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - /** - * @brief Converts the elements of the floating-point vector to Q15 vector. - * @param[in] *pSrc points to the floating-point input vector - * @param[out] *pDst points to the Q15 output vector - * @param[in] blockSize length of the input vector - * @return none - */ - void arm_float_to_q15( - float32_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Converts the elements of the floating-point vector to Q7 vector. - * @param[in] *pSrc points to the floating-point input vector - * @param[out] *pDst points to the Q7 output vector - * @param[in] blockSize length of the input vector - * @return none - */ - void arm_float_to_q7( - float32_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q31 vector to Q15 vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q31_to_q15( - q31_t * pSrc, - q15_t * pDst, - uint32_t blockSize); - - /** - * @brief Converts the elements of the Q31 vector to Q7 vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q31_to_q7( - q31_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - /** - * @brief Converts the elements of the Q15 vector to floating-point vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q15_to_float( - q15_t * pSrc, - float32_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q15 vector to Q31 vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q15_to_q31( - q15_t * pSrc, - q31_t * pDst, - uint32_t blockSize); - - - /** - * @brief Converts the elements of the Q15 vector to Q7 vector. - * @param[in] *pSrc is input pointer - * @param[out] *pDst is output pointer - * @param[in] blockSize is the number of samples to process - * @return none. - */ - void arm_q15_to_q7( - q15_t * pSrc, - q7_t * pDst, - uint32_t blockSize); - - - /** - * @ingroup groupInterpolation - */ - - /** - * @defgroup BilinearInterpolate Bilinear Interpolation - * - * Bilinear interpolation is an extension of linear interpolation applied to a two dimensional grid. - * The underlying function f(x, y) is sampled on a regular grid and the interpolation process - * determines values between the grid points. - * Bilinear interpolation is equivalent to two step linear interpolation, first in the x-dimension and then in the y-dimension. - * Bilinear interpolation is often used in image processing to rescale images. - * The CMSIS DSP library provides bilinear interpolation functions for Q7, Q15, Q31, and floating-point data types. - * - * Algorithm - * \par - * The instance structure used by the bilinear interpolation functions describes a two dimensional data table. - * For floating-point, the instance structure is defined as: - *
    -   *   typedef struct
    -   *   {
    -   *     uint16_t numRows;
    -   *     uint16_t numCols;
    -   *     float32_t *pData;
    -   * } arm_bilinear_interp_instance_f32;
    -   * 
    - * - * \par - * where numRows specifies the number of rows in the table; - * numCols specifies the number of columns in the table; - * and pData points to an array of size numRows*numCols values. - * The data table pTable is organized in row order and the supplied data values fall on integer indexes. - * That is, table element (x,y) is located at pTable[x + y*numCols] where x and y are integers. - * - * \par - * Let (x, y) specify the desired interpolation point. Then define: - *
    -   *     XF = floor(x)
    -   *     YF = floor(y)
    -   * 
    - * \par - * The interpolated output point is computed as: - *
    -   *  f(x, y) = f(XF, YF) * (1-(x-XF)) * (1-(y-YF))
    -   *           + f(XF+1, YF) * (x-XF)*(1-(y-YF))
    -   *           + f(XF, YF+1) * (1-(x-XF))*(y-YF)
    -   *           + f(XF+1, YF+1) * (x-XF)*(y-YF)
    -   * 
    - * Note that the coordinates (x, y) contain integer and fractional components. - * The integer components specify which portion of the table to use while the - * fractional components control the interpolation processor. - * - * \par - * if (x,y) are outside of the table boundary, Bilinear interpolation returns zero output. - */ - - /** - * @addtogroup BilinearInterpolate - * @{ - */ - - /** - * - * @brief Floating-point bilinear interpolation. - * @param[in,out] *S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate. - * @param[in] Y interpolation coordinate. - * @return out interpolated value. - */ - - - static __INLINE float32_t arm_bilinear_interp_f32( - const arm_bilinear_interp_instance_f32 * S, - float32_t X, - float32_t Y) - { - float32_t out; - float32_t f00, f01, f10, f11; - float32_t *pData = S->pData; - int32_t xIndex, yIndex, index; - float32_t xdiff, ydiff; - float32_t b1, b2, b3, b4; - - xIndex = (int32_t) X; - yIndex = (int32_t) Y; - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(xIndex < 0 || xIndex > (S->numRows - 1) || yIndex < 0 - || yIndex > (S->numCols - 1)) - { - return (0); - } - - /* Calculation of index for two nearest points in X-direction */ - index = (xIndex - 1) + (yIndex - 1) * S->numCols; - - - /* Read two nearest points in X-direction */ - f00 = pData[index]; - f01 = pData[index + 1]; - - /* Calculation of index for two nearest points in Y-direction */ - index = (xIndex - 1) + (yIndex) * S->numCols; - - - /* Read two nearest points in Y-direction */ - f10 = pData[index]; - f11 = pData[index + 1]; - - /* Calculation of intermediate values */ - b1 = f00; - b2 = f01 - f00; - b3 = f10 - f00; - b4 = f00 - f01 - f10 + f11; - - /* Calculation of fractional part in X */ - xdiff = X - xIndex; - - /* Calculation of fractional part in Y */ - ydiff = Y - yIndex; - - /* Calculation of bi-linear interpolated output */ - out = b1 + b2 * xdiff + b3 * ydiff + b4 * xdiff * ydiff; - - /* return to application */ - return (out); - - } - - /** - * - * @brief Q31 bilinear interpolation. - * @param[in,out] *S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate in 12.20 format. - * @param[in] Y interpolation coordinate in 12.20 format. - * @return out interpolated value. - */ - - static __INLINE q31_t arm_bilinear_interp_q31( - arm_bilinear_interp_instance_q31 * S, - q31_t X, - q31_t Y) - { - q31_t out; /* Temporary output */ - q31_t acc = 0; /* output */ - q31_t xfract, yfract; /* X, Y fractional parts */ - q31_t x1, x2, y1, y2; /* Nearest output values */ - int32_t rI, cI; /* Row and column indices */ - q31_t *pYData = S->pData; /* pointer to output table values */ - uint32_t nCols = S->numCols; /* num of rows */ - - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - rI = ((X & 0xFFF00000) >> 20u); - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - cI = ((Y & 0xFFF00000) >> 20u); - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) - { - return (0); - } - - /* 20 bits for the fractional part */ - /* shift left xfract by 11 to keep 1.31 format */ - xfract = (X & 0x000FFFFF) << 11u; - - /* Read two nearest output values from the index */ - x1 = pYData[(rI) + nCols * (cI)]; - x2 = pYData[(rI) + nCols * (cI) + 1u]; - - /* 20 bits for the fractional part */ - /* shift left yfract by 11 to keep 1.31 format */ - yfract = (Y & 0x000FFFFF) << 11u; - - /* Read two nearest output values from the index */ - y1 = pYData[(rI) + nCols * (cI + 1)]; - y2 = pYData[(rI) + nCols * (cI + 1) + 1u]; - - /* Calculation of x1 * (1-xfract ) * (1-yfract) and acc is in 3.29(q29) format */ - out = ((q31_t) (((q63_t) x1 * (0x7FFFFFFF - xfract)) >> 32)); - acc = ((q31_t) (((q63_t) out * (0x7FFFFFFF - yfract)) >> 32)); - - /* x2 * (xfract) * (1-yfract) in 3.29(q29) and adding to acc */ - out = ((q31_t) ((q63_t) x2 * (0x7FFFFFFF - yfract) >> 32)); - acc += ((q31_t) ((q63_t) out * (xfract) >> 32)); - - /* y1 * (1 - xfract) * (yfract) in 3.29(q29) and adding to acc */ - out = ((q31_t) ((q63_t) y1 * (0x7FFFFFFF - xfract) >> 32)); - acc += ((q31_t) ((q63_t) out * (yfract) >> 32)); - - /* y2 * (xfract) * (yfract) in 3.29(q29) and adding to acc */ - out = ((q31_t) ((q63_t) y2 * (xfract) >> 32)); - acc += ((q31_t) ((q63_t) out * (yfract) >> 32)); - - /* Convert acc to 1.31(q31) format */ - return (acc << 2u); - - } - - /** - * @brief Q15 bilinear interpolation. - * @param[in,out] *S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate in 12.20 format. - * @param[in] Y interpolation coordinate in 12.20 format. - * @return out interpolated value. - */ - - static __INLINE q15_t arm_bilinear_interp_q15( - arm_bilinear_interp_instance_q15 * S, - q31_t X, - q31_t Y) - { - q63_t acc = 0; /* output */ - q31_t out; /* Temporary output */ - q15_t x1, x2, y1, y2; /* Nearest output values */ - q31_t xfract, yfract; /* X, Y fractional parts */ - int32_t rI, cI; /* Row and column indices */ - q15_t *pYData = S->pData; /* pointer to output table values */ - uint32_t nCols = S->numCols; /* num of rows */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - rI = ((X & 0xFFF00000) >> 20); - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - cI = ((Y & 0xFFF00000) >> 20); - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) - { - return (0); - } - - /* 20 bits for the fractional part */ - /* xfract should be in 12.20 format */ - xfract = (X & 0x000FFFFF); - - /* Read two nearest output values from the index */ - x1 = pYData[(rI) + nCols * (cI)]; - x2 = pYData[(rI) + nCols * (cI) + 1u]; - - - /* 20 bits for the fractional part */ - /* yfract should be in 12.20 format */ - yfract = (Y & 0x000FFFFF); - - /* Read two nearest output values from the index */ - y1 = pYData[(rI) + nCols * (cI + 1)]; - y2 = pYData[(rI) + nCols * (cI + 1) + 1u]; - - /* Calculation of x1 * (1-xfract ) * (1-yfract) and acc is in 13.51 format */ - - /* x1 is in 1.15(q15), xfract in 12.20 format and out is in 13.35 format */ - /* convert 13.35 to 13.31 by right shifting and out is in 1.31 */ - out = (q31_t) (((q63_t) x1 * (0xFFFFF - xfract)) >> 4u); - acc = ((q63_t) out * (0xFFFFF - yfract)); - - /* x2 * (xfract) * (1-yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) x2 * (0xFFFFF - yfract)) >> 4u); - acc += ((q63_t) out * (xfract)); - - /* y1 * (1 - xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y1 * (0xFFFFF - xfract)) >> 4u); - acc += ((q63_t) out * (yfract)); - - /* y2 * (xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y2 * (xfract)) >> 4u); - acc += ((q63_t) out * (yfract)); - - /* acc is in 13.51 format and down shift acc by 36 times */ - /* Convert out to 1.15 format */ - return (acc >> 36); - - } - - /** - * @brief Q7 bilinear interpolation. - * @param[in,out] *S points to an instance of the interpolation structure. - * @param[in] X interpolation coordinate in 12.20 format. - * @param[in] Y interpolation coordinate in 12.20 format. - * @return out interpolated value. - */ - - static __INLINE q7_t arm_bilinear_interp_q7( - arm_bilinear_interp_instance_q7 * S, - q31_t X, - q31_t Y) - { - q63_t acc = 0; /* output */ - q31_t out; /* Temporary output */ - q31_t xfract, yfract; /* X, Y fractional parts */ - q7_t x1, x2, y1, y2; /* Nearest output values */ - int32_t rI, cI; /* Row and column indices */ - q7_t *pYData = S->pData; /* pointer to output table values */ - uint32_t nCols = S->numCols; /* num of rows */ - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - rI = ((X & 0xFFF00000) >> 20); - - /* Input is in 12.20 format */ - /* 12 bits for the table index */ - /* Index value calculation */ - cI = ((Y & 0xFFF00000) >> 20); - - /* Care taken for table outside boundary */ - /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) - { - return (0); - } - - /* 20 bits for the fractional part */ - /* xfract should be in 12.20 format */ - xfract = (X & 0x000FFFFF); - - /* Read two nearest output values from the index */ - x1 = pYData[(rI) + nCols * (cI)]; - x2 = pYData[(rI) + nCols * (cI) + 1u]; - - - /* 20 bits for the fractional part */ - /* yfract should be in 12.20 format */ - yfract = (Y & 0x000FFFFF); - - /* Read two nearest output values from the index */ - y1 = pYData[(rI) + nCols * (cI + 1)]; - y2 = pYData[(rI) + nCols * (cI + 1) + 1u]; - - /* Calculation of x1 * (1-xfract ) * (1-yfract) and acc is in 16.47 format */ - out = ((x1 * (0xFFFFF - xfract))); - acc = (((q63_t) out * (0xFFFFF - yfract))); - - /* x2 * (xfract) * (1-yfract) in 2.22 and adding to acc */ - out = ((x2 * (0xFFFFF - yfract))); - acc += (((q63_t) out * (xfract))); - - /* y1 * (1 - xfract) * (yfract) in 2.22 and adding to acc */ - out = ((y1 * (0xFFFFF - xfract))); - acc += (((q63_t) out * (yfract))); - - /* y2 * (xfract) * (yfract) in 2.22 and adding to acc */ - out = ((y2 * (yfract))); - acc += (((q63_t) out * (xfract))); - - /* acc in 16.47 format and down shift by 40 to convert to 1.7 format */ - return (acc >> 40); - - } - - /** - * @} end of BilinearInterpolate group - */ - - -//SMMLAR -#define multAcc_32x32_keep32_R(a, x, y) \ - a = (q31_t) (((((q63_t) a) << 32) + ((q63_t) x * y) + 0x80000000LL ) >> 32) - -//SMMLSR -#define multSub_32x32_keep32_R(a, x, y) \ - a = (q31_t) (((((q63_t) a) << 32) - ((q63_t) x * y) + 0x80000000LL ) >> 32) - -//SMMULR -#define mult_32x32_keep32_R(a, x, y) \ - a = (q31_t) (((q63_t) x * y + 0x80000000LL ) >> 32) - -//SMMLA -#define multAcc_32x32_keep32(a, x, y) \ - a += (q31_t) (((q63_t) x * y) >> 32) - -//SMMLS -#define multSub_32x32_keep32(a, x, y) \ - a -= (q31_t) (((q63_t) x * y) >> 32) - -//SMMUL -#define mult_32x32_keep32(a, x, y) \ - a = (q31_t) (((q63_t) x * y ) >> 32) - - -#if defined ( __CC_ARM ) //Keil - -//Enter low optimization region - place directly above function definition - #ifdef ARM_MATH_CM4 - #define LOW_OPTIMIZATION_ENTER \ - _Pragma ("push") \ - _Pragma ("O1") - #else - #define LOW_OPTIMIZATION_ENTER - #endif - -//Exit low optimization region - place directly after end of function definition - #ifdef ARM_MATH_CM4 - #define LOW_OPTIMIZATION_EXIT \ - _Pragma ("pop") - #else - #define LOW_OPTIMIZATION_EXIT - #endif - -//Enter low optimization region - place directly above function definition - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - -//Exit low optimization region - place directly after end of function definition - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__ICCARM__) //IAR - -//Enter low optimization region - place directly above function definition - #ifdef ARM_MATH_CM4 - #define LOW_OPTIMIZATION_ENTER \ - _Pragma ("optimize=low") - #else - #define LOW_OPTIMIZATION_ENTER - #endif - -//Exit low optimization region - place directly after end of function definition - #define LOW_OPTIMIZATION_EXIT - -//Enter low optimization region - place directly above function definition - #ifdef ARM_MATH_CM4 - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER \ - _Pragma ("optimize=low") - #else - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - #endif - -//Exit low optimization region - place directly after end of function definition - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__GNUC__) - - #define LOW_OPTIMIZATION_ENTER __attribute__(( optimize("-O1") )) - - #define LOW_OPTIMIZATION_EXIT - - #define IAR_ONLY_LOW_OPTIMIZATION_ENTER - - #define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__CSMC__) // Cosmic - -#define LOW_OPTIMIZATION_ENTER -#define LOW_OPTIMIZATION_EXIT -#define IAR_ONLY_LOW_OPTIMIZATION_ENTER -#define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#elif defined(__TASKING__) // TASKING - -#define LOW_OPTIMIZATION_ENTER -#define LOW_OPTIMIZATION_EXIT -#define IAR_ONLY_LOW_OPTIMIZATION_ENTER -#define IAR_ONLY_LOW_OPTIMIZATION_EXIT - -#endif - - -#ifdef __cplusplus -} -#endif - - -#endif /* _ARM_MATH_H */ - -/** - * - * End of file. - */ diff --git a/platform/silk/boot/vendor/wscript b/platform/silk/boot/vendor/wscript deleted file mode 100644 index d8ef21720a..0000000000 --- a/platform/silk/boot/vendor/wscript +++ /dev/null @@ -1,26 +0,0 @@ -def configure(conf): - conf.env.append_unique('DEFINES', 'STM32F412xG') - -def build(bld): - stm32_basedir = 'STM32F4xx_DSP_StdPeriph_Lib_V1.7.0RC1/' - stm32_srcdirs = [stm32_basedir + subpath for subpath in ( - '', 'Libraries/STM32F4xx_StdPeriph_Driver/src', - 'Libraries/CMSIS/CM4/CoreSupport')] - stm32_sources = sum( - [bld.path.ant_glob('%s/*.c' % d, excl=['**/stm32f4xx_fmc.c']) - for d in stm32_srcdirs], []) - - stm32_incpath_base = stm32_basedir + 'Libraries/' - stm32_includes = [ stm32_incpath_base + subpath for subpath in ( - 'CMSIS/Include', 'CMSIS/Device/ST/STM32F4xx/Include', - 'STM32F4xx_StdPeriph_Driver/inc')] - - stm32_includes += ['stm32_conf'] - - bld.stlib(source=stm32_sources, - cflags='-fno-lto', - target='stm32_stdlib', - includes=stm32_includes, - export_includes=stm32_includes) - -# vim:filetype=python diff --git a/platform/silk/boot/waf b/platform/silk/boot/waf deleted file mode 100755 index 4d6635a842..0000000000 --- a/platform/silk/boot/waf +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# encoding: ISO8859-1 -# Thomas Nagy, 2005-2014 - -""" -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -""" - -import os, sys, inspect - -VERSION="1.8.5" -REVISION="69c046f2063dbb528ecf1ab20607ba25" -INSTALL='' -C1='#:' -C2='#5' -C3='#/' -cwd = os.getcwd() -join = os.path.join - - -WAF='waf' -def b(x): - return x -if sys.hexversion>0x300000f: - WAF='waf3' - def b(x): - return x.encode() - -def err(m): - print(('\033[91mError: %s\033[0m' % m)) - sys.exit(1) - -def unpack_wafdir(dir, src): - f = open(src,'rb') - c = 'corrupt archive (%d)' - while 1: - line = f.readline() - if not line: err('run waf-light from a folder containing waflib') - if line == b('#==>\n'): - txt = f.readline() - if not txt: err(c % 1) - if f.readline() != b('#<==\n'): err(c % 2) - break - if not txt: err(c % 3) - txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) - - import shutil, tarfile - try: shutil.rmtree(dir) - except OSError: pass - try: - for x in ('Tools', 'extras'): - os.makedirs(join(dir, 'waflib', x)) - except OSError: - err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) - - os.chdir(dir) - tmp = 't.bz2' - t = open(tmp,'wb') - try: t.write(txt) - finally: t.close() - - try: - t = tarfile.open(tmp) - except: - try: - os.system('bunzip2 t.bz2') - t = tarfile.open('t') - tmp = 't' - except: - os.chdir(cwd) - try: shutil.rmtree(dir) - except OSError: pass - err("Waf cannot be unpacked, check that bzip2 support is present") - - try: - for x in t: t.extract(x) - finally: - t.close() - - for x in ('Tools', 'extras'): - os.chmod(join('waflib',x), 493) - - if sys.hexversion<0x300000f: - sys.path = [join(dir, 'waflib')] + sys.path - import fixpy2 - fixpy2.fixdir(dir) - - os.remove(tmp) - os.chdir(cwd) - - try: dir = unicode(dir, 'mbcs') - except: pass - try: - from ctypes import windll - windll.kernel32.SetFileAttributesW(dir, 2) - except: - pass - -def test(dir): - try: - os.stat(join(dir, 'waflib')) - return os.path.abspath(dir) - except OSError: - pass - -def find_lib(): - src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) - base, name = os.path.split(src) - - #devs use $WAFDIR - w=test(os.environ.get('WAFDIR', '')) - if w: return w - - #waf-light - if name.endswith('waf-light'): - w = test(base) - if w: return w - err('waf-light requires waflib -> export WAFDIR=/folder') - - dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) - for i in (INSTALL,'/usr','/usr/local','/opt'): - w = test(i + '/lib/' + dirname) - if w: return w - - #waf-local - dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) - w = test(dir) - if w: return w - - #unpack - unpack_wafdir(dir, src) - return dir - -wafdir = find_lib() -sys.path.insert(0, wafdir) - -if __name__ == '__main__': - - from waflib import Scripting - Scripting.waf_entry_point(cwd, VERSION, wafdir) - -#==> -#BZh91AY&SY=y `"Ѐ#/!2Xa>w#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/Ir,iW5*C]sQvl7s_{E}x}ޥζ=╅m۶.l=ݻShz|.ۛxo9v>֝|4vZ[֌n=#/#/#/#/̀Km`{R=`(ݻ{מGCVnW:FREj#:1PIT=Rj*hZ(y{ϭqRwupl2鮻vn޹O- -}ݭ[a[˚-nvmG'Kt}ķ٣_c> #:#/]P(^u^{!#5W{ l]3s:#/UPKcJhdu{=xvlӮ<޼s{{TdUW>͝nup;Ƿ:ԶwzHh#5{(ހsZG[tw!#/Iq4y]Rov{gϝos[݌{fyUvw}\zewu86{_p-zׄxJqe9(h=P@m{}r[|uz۳܇^}z7=#/s6z>A@#:Gl*#5-UkjNjTr=tw1ɮovNE`pto[۰ҡ־>#/w#/#/o%|{^sݷ۽﮽_w;ڧ(NEu==ڀ{d7r$;z^;k#/;{Y˳(hy{wsZ\o6vc_ouuz޻on-`bPsGUiZ}{ ͮ%wק{RUkTS= `3{#:to7׸^sέ/A@ATk;}o_Z}Vnuw; 5y#5==ɛ/>.`:[;-};L#5mz9_wwV>Ӷƈ\\ݒa{]n&{+zeW֚y{Jfy0ѧ=n_}{]AOSﶫHn3싼]SH #/ #/h L#LM4E@z@ #5#54A#/@A0'##:(Sz<#5#/#/#/#/#/H SdhM&=?J5?&ʞ(O#:4#5h#/#/z$A&!~bSA<6#/#5#/#/#54b#/#/ 4#/14&4hLL##ž=M M44yCA& S1&F?ȆȧoTb(~d#/@_3~@G|֗mcA5j)",F #/,P"ߙe~Ŋ=be\L^'SuRĒ`Ejs1fO0ݘ`4 8$ Do%%ljaJH97. ӅqY3ZJucĵ1uJUcqjHY*_3,lB#:70f%]ֶUb#/VAQKED#/$1FlD(F"T(%DdTEADE\H ı &D4c6$RDJ)[IP%4mJADI&dJ% hI)R$%#:A0e$Hj2͊X#:IE4fţQm*RJbd2EE+X0͙jɔbT5I%HYI5̍HhԄlfJ4 E)T͌LQ3Jbi e6 #/CL"QadH)bY,"d!"RR&H6 j 1l,L(%iD,LTlMe2&F2JQLZBDbL!$#:lFlH6AHbL3b $Ԛ)#"&dFiL1)*1&i-$ɢS͢4(Њ!JHFh43T@)E%!6ih#5Kdlh3H $*$ʂfmP(̦R#5 BfƘHԙ)3#52RFʕL QdA%()@02CL"AaZhf̲,#:RMdFdPY(il b(llfj# JfiLBCaS&cjlReFLIB$,Jiɥ$6MFLC dXmd$"Id2M4#)-%*4TmȔƒf(i1 !eF,ME#V&ZLIdD RQc(!UK[h)-dZJ#5S%2LcY-h2%)EbQSf24VVK(MbEF̨(-j"JPM$k1PPȦd62J"б2f5-,HTXfe*F#:5"lK*2mդBek5)Jib2"ƴiQ%Q2X3F*-m %lmQ"E!PhՊa&BMbJ)FDԪ5$F5!cTɕZ֚aQșYffI-E-%3SJZU+%T ښmMi45YJQjiFF"FHFA!h)`Sj6Ee6ғM$i)R)bZYR hJdƲ41BPرfHcԖfY2Ȧ`RLX016"(ؒQjf6b0hɱbaj4P(B6XFb4)0Va$IM%#)d!IDI&J!5QS3)IYV4HfIDle#fiʚ6-e6hIL*ejFLd6D1Q&J!"Ѩm4dJ#:M"LlRD bIR#:d4!FFM,+AIب10h)DՍc)#:M%Z6CDYfڦ42j-bJhiJ4*Y%IJRj؃TE%QQX&fbF[!fA-3% уi0j%2čT4e-QƲB (R6%  QIIJ#:-)Si5mfbZ +EjJ,Sdэ$&6J`у6V6-bi(e#:&BL,ڍhkImJ3fiebUS,UMIeFQ4eMHTQY+&(&Rm)FhФj)(hf,"MEchP-R٫&21bEVlZ-dֱYTM#i&0l,H٥DPLđ$I2"B Hc+4K-%3Ka")5pAILTD4OEpwQT*r'tF|ힴtd7ɅhiY#5`:1vD_<#/vǟe㌺vk,DGXA"!v ݩL#=XB`V#:4/֘!ۭkdRTx}+qWd]> d!cA'$#5Hi- `CK.m" "nc #5ؓLQn#:ʺ--Jޥ%`aqR^6eDOE_lvv֌* R#:y:Pt+qj%YR9imDHM˿3*86e4t #58]%I3o7w+4&TX~\ $^7<DLPU QKڛG#5ﯷ^Ĺ{R*ckcm 1-5݊4dP|>w(h#qCM*Vߺt\w]uj. H(J#5@D٭ dXTZcʟ=0Zh摜N`6JFb?^1@«I(j}m|c[J~1 PYO:Oٜ3vvR]cÂ&B#5t%}oh%jv6 }91ΩBQX#5:t,XZ2kՍqe[IXR/=/G0IQ:kE5ƔE-bMvAzHiLgg¨Y3LsfYr#Q7Č'HqI22Q/^Io]v~.ܻX^9ɼW.V5_?JY0Q_Pd,^[uvcE ᤤ>uIcBEЀ^uE !BKIIe/y9s㌣~a"4"1S4J6nUb+Œ7/Jm} p|#50#@6scQ9daҶd``ADVÿK۩2F6v,&Au`@z!bZXi!B P)#5d@-Q4,JgoeYRLH]ͼHx%D 6ZqIv4hlENt+<{jL(@XYCC5;!{l-U 8{%pX.cX>>(vK#:(8B) l`gC'0m6aRC/ߕ|7Nl8]Y? $ӂEs¯Y| Xm @=}Tm؍\&rZf[LNꔂ=omBKTWf!Ώ'&gva-wRcJ4}GTW qk|zCi i=Bp#:P} Qk]xb:*xVMܼTՈȠ\]\;m3FSjBщrecs;0rx*KNWqPR0TPs[SLJGd(v!҆=aEqR JGcH#~DӿXi^Z#5ndz!& +M#5jdb:F[(~D4<XJ wPcwwg|Xl+"ϻ!("ͯe³1-eQC,xޝfenݱ>f zie9J@["Lį/<'W=9#:}ٔ=u,`: kvsM}zUƂl,y$()5ǎy`2LHJD]#:#:F=E#:N 'O:퉯mO1~+>;Ԣ =8\6irt{PAFh2T]^3f ti\.ie*uPk( pK(,z<ƣ'0)N1ouv$k{#5bb))Dގ-FBdá#I֣{ajC2!qOTe[$SK Ogmx*∬"; )Az*ۺ7ccLõ\.~GIσ~tl$[(-s;rBv·2w)L媷q,WB"t'Ňݩv#z1s!ǡ[DZ(sSQsƦͿ5(߻3]}1,#5_$쫺#5;#:%oӋxIN$>`MYTSt#4DFIm/Z?",ä-}_ғs9-8vwFǹҡ5ի#5^S5bzk^c`ήn~i(8*0Eb{L#oVb?[&5K42/ci҂7dBU"s}h)s!تq=b#5ndt!R6H&ޛ$'u0rU8˟U+ܱN.0}!3 InrMMݒ3a#:XDHn70wPC(Quf*aѣ#5wg^x¬(fU6tUx2%8u2|M(@V`Xd麩ĵVΨ6#5sxa?FE(Ϯ{,D|ud#5z(1|ܼSVԠ-en-4:jjM4tF"s$hA`c.tDz^%IJUNjÎλH I'x!3\=S؜]%_WϞvӾ$Scc$9dvY}[30W:ȗYo=ؼ.8C_%?vQsi]z\P ֖GR}&8)h(i3U<L1ά&}(#[Q.GK?}Mqڀ667u-ؾҺL;m6W-|(%魽{aoTG%,G妏4uʭxQRtL?>VvsΈ^tiЪct|p (E1gٗZͪ24k`8{W'R#:tJ*LKCA5vdw9KJ\byZQL֩rq&#/Z@]Z~o@%5*g22XP԰#:巫LH_ jTǮ QoCtޙZY{C G9D()$iwCL_'s%imLvdQpŨE_#:!AQd78-y+'׌}+|}Xg̽)2`Ǎ˹|΃Jo㮺LR/d:?'kiűtP~ys5x({X5' 0R4]m^F)<;GxkpmlUc5R߇Y;gX$Mbryc{1Eq.4RU'u:[+Xc&hQ~mʇ0o]oٲ40T*aF1k,:Q:wBQP~5Sk)M+0iu=UƋZ?yKeJn-a9aFؾym2k 4W+BKxEl\爙ʠK+i#}zNeR&#5!2ksz;9mAv^ʳb(/ޯ#/'s+W{|x5oUM/+|u})]R\zk0+//~#:<}sF۩67Wtě$adħ%NE:hZ@r(=e/ E/o#51M{k)xhi?u bOês/oyٖ @™rߟ5{_~rZI :jK'TZkoCGh>jϭ5vF||2 #/ܫ_fW1!-88g5|btx;9?a4$u^<#5-|b].ZCQh_~-L0.=GTvgQInl 4S3Lff|Hch2*yM1Ba\E2kIC'j$tO]9#?f-ŅYQ43iIav%.e+N#5$3t%[ 1E#:BZL5#Y٢hq53{X$AT! `q7oSCCӮu&$N < !x#5c8㚰B,+*EɰB#/R}%0JVy!^|qz< DrJ)63RJ6L?_\yRO$j0GX#:l縆#5PFbin!{>W> l`31n #:t/E ~+ijj˦oOb_#qyAyZl"m8&/5l |@y0\/h@i2sf^vofmpUrcXI&L&ۑ.r#:b=+J.#5Kdzr~czUwly F9pt~#5+a2:W܎a-<>+zcm2GAA$j3J'_9.<`7eM 9ij{pIY(5}**>ﷃ[E$ (bKJb^ "wMwUr4đݻ[*fX+|WpL1 "VYX!hz1!YKmQEĀ {*V\"iGNkĒ wH8!j(#:)9$`v< q7K8Xp#@Xf0frΜrxd88>8ه4#:Œ*#D$[0oZ d0&7( wyy%CSZ)yG_Nc \CL릉7]F|I(#:y4Q%fvTMhj}̑Ճ{M$t#:wfrv9h-i 󅦌DC,u51|T+fsBRąE#:qnF@%lPA C%@m=dQXP_4&nw]?'jXYîɀq tlk5*kc_&5kxQ,m* BI fHs$_cOQa;FX'o #5nB!t-z |d=ꩵkLw33qEI ク8 #/Ib".(lZpKlӃ!pJ&ߔP!@rK5۱zvbC4Ʋ$@-s#7\3"\ո6ܹ2$F((vHloUk#5 >BC?i`?x2)c.D0¦NH7pȖ>}o?YȠ@+jLׯ9P4(^%;unbe0Ok,Mz5j0[ͤZ-ktڙzۯ^H`]F+% 끢ZEI>ꜷ 8BttK$Xj{PLUC%*OQOy6Soin#"Eot1-E]C#2ꢦm(|?u'';77cѹ۩-M!Pj\ph;pIސ'WWr@wf9BE1.+Qcri_?E Wl}>LÎ>InG1)!0j 4N_/ՍQ$ 1ו_;n.WLrY^Pmݻ~Q?I=ҿӿl3|=mgb7DwrWA EPԴ{kG~lEBb3vL%+1"Kf&ңkMID#:#:J*PS%BЭwKg*-LzpM!` >sEeEWwm췍o^=!h#;i4?'+Xpp+?8!w{a߶濕qS`UX[CNv{˺0*8AB 0?Xu[:BER=|& :HdERs#:~VмkmUEIqAj"S aE벖d?C?oͻ6'wύ[ʨr(Bh#G~$ʟ%и2NZ#(R= 00_?ϛ˕drjptK%XGw(@Wsʩ9IUA֯yV>ٟ:M^O#:8Qˊ3;Us@ٖGÄuzN (wgQwS)=nhF1]U}VZcF|#/l8h7Kfy&rCCE͜*ce7x<%QpjsJovsDa)pܚ)xZ%Thd 3#p&WIFw#5lPogz4I*޾]Ϟ5귚PW\[;!YoSßӧYߧ,dNQn\*VSUO]u\wͱc5CTȔ׳_'.6ӾhZ4=E>]UExӿUk,<_*;mɣgNG˸EG7*춘z*OHe6SR_+h9S!FϬm'{9vW|bg Ph{A| MRJ>¨ugre*딡1ŮihxRH0vV~8Qն;=h__`.dʛC[#g(SjE3W|wiFNLix?eTo >Ͽ=t?=)Ϸ}Gv(la.I{+?6McdgݨeWWZq|M#5Öǧ룧^oOk1 )8IRq..8$҈1YWچծ `\"0'|XaL[@LF##5<?G{>/ѳYyHM݇u3Lv+Y2׫fOZPy˼nWe>ǿOgu{.X@:9?qߝk+g>v>ƍS-j߅'[%ig^;3owhbi2Mۺ#/>~~5m=p!?W}Cԍgm|J۷)pz/}6GUʒeUG~,GU^0FOjWw#5G 1ݦMZ#: ݋ L6θ5-gUzt2V:1)]RhVqȶ#5HmU+bz*x]'RǛTꮌnK߮_J%Nq؜M6$(A?l%CsB۔ _4ENB#5˪Z2[O#5>jral}Z z#5jMs;4TCmQ|Gl!!͇g>|'9])SheYr4gj&c)}.bݑޱ?qu}Ӫʆ\hJѢ%?6WkZ%O:>q?p߫t1/jq뢚kzл{klO!PM$J@-HUGң/kyz=}kY\Ξa+jNz0m!dbы/<), Z@]/whp'O_袸ʺ=}?ފn~:}Y#:O9 TZG#5O'KpE9IY#5?c]"VYaҷ%h]u(  @CZ SZS.S-ܦmMd&0v i#lKL攵EjYS5nxy-Ń"6##:eŃc²֔Ї+ړ{76`F`-cU5O2˷Ny^Hӣ0&8F'r(` D4lE4 D ,@8b[jARE)@lƖd>&:EJDQxS"2`qKa Bk( (EIui0l" T=TV҉ӕD;`X`- ejP~AB3;)nn&֤iխ,2#=y|̢PnOU0G{QƬ:3}|[xao?#576cꉫ}SUWKHjUI䱞i#5Aͳ퉻V,rCu#5Sz#5LmKzgN T=ǶcsdJ5xz+]fǏFNtlS&Wwwwwe\7UM}IAuY1ISTYW|gpTz:44( @aG&DN=Nr@GzY}UL+#-aJ7I(Aaa BM=|aWqX2[#GS.#5rxN G4a -a)VKp(Pb E#5h<ˁeF#Հ"D_^OLjٔZ²۟qZ7'HD:ks['??sy9R"a1D#/n#&yvPتq!#/aWw{,M:cN~ y|vkH[5=Fb8HV/y͚'sn;wI%u̫#5[Cc<4ԡ-oB ֶ?4fyf(p&BZcUߛ8Έ (YU#5[he†U3U Q/b%?ǚdzH³8!'L=߅o^z?_Lɶ0#:,FD4\(VA0z&uA/T#/Ut0fS_V-[)#Z5c Q$NViGXSp##3 9%w-QC(EUE7Nٴ\ qyU@Q۽`DF)e`hp_G 5EGK8< U ίfdD6KELAAeMDAbrQeh)P5ڔ4w*pEت%u±&vN.8iCFKm#-x󼕦bZȑ 72)(xZ}JvtlS0-BtSNQ,<'o:ElmIvi]{{vBA%}c1CzWtѧM74JKڍ]n.x<">BQa(2`\#:3Q 9m1g’ĚM|ˌ/G#5&Dpi2$HFOGtg\#:WEwƍNpyct sRưั -(Qcb Izv!ח J1iHp4G4DAFO}yh_i#O꾊ygӇk~Z"#a/IrҠm>&;L.Iq*)4Wr+BPP3JB2W_`:er9q UTМk]ћ306hWoH33L- ީ@Q.E4[Z|e .׷;9#:AhJDwt1;CGL$E:Z571xq2QfĢxF:2#:(EN,=J|޳kg9ѕ:lCbsΰĶP"SƮ,llff\M1Q F8l1%N}wuCbŻLwIgv_GBeG ,{aW'jfVq8X<#5b s4 %Ҹc#c#5cnFT{fV#526!U*!"Le ڂ;3S+)|qیb 7XҚG3n4"TeY4ui1L%iq}76!L8t2a ps.j+77ٓ"5J.0ͅ☒u@Y.:73b24&Bmw.Bf.d+l:f#N?N&~ Rފi#aZ{ 8Y}ezO- 8r?#5Z./,R_} ȼNWȮƪ}%g0QoS;䚜d(NL4b^13#3(tUb˿ $&f>iW^[o1#˿/fçz|<$[b<pg3/ZVSB2#9MZX3UDZĜ}ה-bxdK81ˇĺ!lO#5<͌aV7u)SWY>F4cw6*:$ᕶ;e3=}4&3RRn}юL3v.PEgvGߡȦY1,$24&/o#54HskW ͽFy@ݓ[jpxhl^X#mZΘHe+A ݻa(΅T`pǡ]EpǼsݞ]i4b##:u7G#WV~pP}o[A]jUeu#jbYtQQ#:#5(/60]ڶ}] g]$p/^ODyаHe#:5вkn7z#6U6x|";3,Q3M>=S@c(÷YebIwC2tJ?ZYz5hk8ㆢywDh#53Ǹ5-sòEGI:pX<'-NXj;/麔[hUv~ɖ&]n/92l娍%&:-1}t$NFQwȺe#:4V(H'Zen0-7QY-f|Hg]IC$qgj_S'ݨ^</]wJ5^%2VޙfeI^G`z{pW 8hv!Ƽ1׍dv.?dȽִ}ug2xir3Y12֚?=2R$m ;trYCOǎw (m9{ TG#;= NgpYQ!d^XA<(r1w>hףtd1k/M2C9&Meaϭ56L:8}ۮB[|mR2J6O+9ZWty -QO(r#5OuĦ/7mL-#:죵XۋȒǧ74mF"!!+ӉAJkd|OW}aY]-j[-r\Ԯ6ÞʕLf`ADŽTWˏU4#?5VL;q K#5Jwp[|7i0qRG<6PvX?e7gpM1=}'*L(8p9q#5/IB]wc ']N@Dȅoʍtȓe k0R PM3A|cj~gqwFGF"[1:ʥo}ސ[]u>3 mtW!N#5~#5Sa7Y0y^Q!ȢZG*'fsWBhɻk"˒tՌie/1*XpygGX],pdh7:zʣtRYN'򇟀,h6Xͱ3[,s.:eĢYm#ٝȢ^g0R%؎7$ĝtK]Ugzh;ISjFȾsYd'.Kڃ['/fܢN{8Hʈ#WXF0ꙖRhulJᮤLsrDZ4/5-v%nGuo09t0lZ?;4k>XF,r3N4Gtvtԉx)&lf#mݠ?qv|`EfšZբh#.u&:C륥_ra}nG?nV&Oo'nb ˑe#:R#:薫"PyB{v42|4Բİ):veEˤO#MoulÞ<,vvQ[t~P5_ZbVOB1OqSgO_#5bI*-vS-:c|ھ|yw {?,ro]5^o&K·t(=!#5W?䑆b[v*FH܏r3E[jjOZƱכO0"I Dz7J5?+-TOJ#:$!?mcUyR=#*goyizAF"oR˦ǧ_o]㤨žw+/0*U^x-ªW\.E#:ۚw *a(K?6P/`բUIJYDաe-0sY*QA]=*׋*'-9}-eι[YD3;a#:9]4Ndb7 뾫|g7> ry1?S82xvBcgdGmZk<y|g\vs[<볪Wx#?ug5L#5/;tQ|<ވ1Yn߯!߾<|qľKf켹A|MB4;*~>q>yywnj=N!&<*Un-UUXMvWN4Ӕ^{s#Aj2R֘jayDS>,-:x$B\ r!u0I;0Yg;:.W[_nV#5 wLxh"9G6*h_hhu*T}6{1a]*W9^xkzeM=o'8HmƘɩk(nSagr,4#:Rԑ٪Mz p}&MJE6 (^2X0IuOݷ<۩GsTS_k=#: n*H\)L$tczcDT*OxCL69%O]jc9oV1:=B5q- Ch^#:k,">noM`D}^cV"tB^1<%ul[_㬠O2Ol.|Ag"B1yGI&#:=[ޢXX]SDp@|}56)RFuIɔSN0֠o$\ {f;QݠǦaUp~[J8v7)V5 }c`}g_~ޙjw-txUQ?V:1[ڭLPmtaUBA`vUVN.Ztͷ7c®etrµL#5Q#54B+8+Ǘ|_%>ݞ/;Nж~VsY`Fyss< ^j5a#5iij ]/<#:O`z;$2*k7FY#ntkիVt(t\RB=;[F%~=7GI*yRx=I#[< :]9 RPiix#H SpVo⹤\XJ^\hǔ'4(ŽPmֺ)hK.#D[?O_3Ӈga 2KorBGN/Uwsk$cyc莊z}l`KQɼD?6oC~69R[`'r}(|;txI]M!hGYM,ՅMJڳD&2"n'+ɀu#]TLϪvK#5ؙ2eS)krD:W䎮Nw%#5'8ic KƝ4B(}>^[@)Ut^(ǣM%֙CWo571A h3*AG*wjO~W`58Y_zsmSןq>VqwnhI7oK[e]ح&޵[M6{|nWpVW޻w=Sߎ6{p9ˣYyLNCUu* {(q"3E {NFρ|N#s\i9O){E=?Xrd7%(gdD³7fBOw#:7[M(Y$EV羔4jd$;Ao)~#JAj.Fh>jȜL*ϢtBzslFtc]1z{jwl!N#ԡ/*",w{[qj-FuwH"ȗLy.zR*#5)E@vc,U\}[ShSlxY^\hR =s՝e\}]XX8ȡh1{Հ8{[YUiaX:0R E"_.ރBVP+nRKe.*sFfr1#50x]DrSvZмs̉ݙѳ2٧wB4Gߩ/޿G|~Ѷ;=>#cw?9^G1dg=rt#:Oy)tAWS6)??3S^)(% 4Q'IEEC8NV|a/VoI% ,:#5*pE1Bs~tt/%Ԛ@~}~l4Z%^i"&M!(.f#5[vgZ!odM_wǫtR#ako>[;`m9!})6Eܶ$kk&f#5AC٠y: Z@K(Z4rBhүUhC,i*L"S8 Քqw"˶5ڭZ?],6pi$C@(7^ Ux7˜۟{t6U#5P}tVpPHY#5yBP-xc3Y+`@Z}CݛKykzE2I=*#5;ZOUqxspth6؞v-MrJ"O7riS" \BmMT@+gA?WjCi &Γ|pl{DԠV4sm+sp@%a.C}'$(s\lo~gD;@↷=eA87J~$Q3DFۃJ'f&NAtd#5aڄDՁd'B!#5PN|,ֱ|H tCǪ3f(VnSDE ʂѕ9nE ( Ӥ#:W-QyP2[籆9pd}śu&n|*M#:WzO=%"kvo7] h2QQ_%X=h]{늌ݗ{ӷ9]f#/#5$PS}L.pX#_Y(=ʶpgyp H;#:+tV=j Ȃ6pA#:]D6!X:zDJgwG_bK!br߮B#:bzApwLo!7  )cPwpzC@jA}nI:>5?P}Pzh0!a$Y9&.ǎ9n/$uECNM$ PkM EBy[jI0mp\-4qĠjY-l|NM#HȰ5QC:6ȼ|.efCKVF4yi#51>=+aXXIQnrQzZKۻkȒ zlMlPՆ д0bK< ӌx\nךs 0< f^((M ~eWzH)߉6 b۳Ə;m iݍSr49j(VFDT,&m(ɱY-Ѻ4Zj;чqsӮqӶ JvkEmم$.繠d\;='0K#5D/4K>ܞtΫ#[{FB}Ud/XD#5РS9#:㑯.quk^gVt4ߺܱ2i>#51Ȅcc13&bHԙɝ<8ͦ7LP+ V K4I"Nԙp,FV ]`/:{( KȎaui2r`OSdM|PxD?妼Su"V1߂9 *..4Ĥ SwO|YCnrf @YVx3+Birk&$2#h*Ӝt^v;TZB)À##:)+Ade8l0h@9v%$S$I9XuIZq)3u;1L.>~,ԂS&zwزci*m5F5ΒMti!wSm ';u8X5q7P;٩Ɨ=?Chh]bJuӾoqًE3}[7M*]B6 Fh#5@Z.6qT8Y3tjuJO#5m){57MZQ@RSiϿ_R-MIA6Lm)u_kڲWcP`Ek(xQ_>J_=~L!<9k15@[LwFMw_s;u^^X\b Ӹ;jO#:~ZjqTd$ߧ$Ʉ&~]\2J\#53s_,N3Xfxq &ZS>^QXCDC a80`3|q )ogDݪi@Pt81d Z;PAg`rUį^獒Gլ8]>]XIT3csc7!?HaD3Cq\{Y`b pgqF{;Uf:W$$)vα}]7y,-==tļ:)DI'>I "j8/`sIIkQ}ɣ<ߕtTՠH6&Ggj`DP},8b3I&ѪukR!9 Esob Ɏ W 3A~?,a6ͽŎd&y>#:ɢ@0OZ!a<s r qIe$Pkk)Xn-R혫dd#5=RRQ ,tDAe4, -@nMl6 Aд.s0g٠ lH)t&SuS@C$@ J u>}0ev1xQFjhV*?v? []7_ޒ~=Q' &a͂=?){Y#/p2,8 GMh')jMSŧxTTxm V&фzcq]gQ#sXވ`jmWJ5NPb"ŔDu ږpCW/E8HfQƞϧ}Dt- *nyV#:%pBDwIJD]]#:ʘi.^.w Mk,)?nS<aMs8)ط7~yagf,  >~EuR-0)oHH68P)@4!aQY6٥4yᄏֈTTbUBͼs#5h)jRYȠ)0$jRH)HCe}#5qwǦq89NsC37ś`.%B)ᭈwi/V4χ=hG 詆BI <3cAFPr9PT5(=#5Z-Vhem,qUEti=F8 537|4MOJP#:C3vQw_esx 6$D|@t|H㍺(qݺˎ3$¨QFQ),Be%,GMq8FFJL#岒R4UݕvQZ(BZU|s|y-u؍=ɞ%/$B(%w+ثvQt;dYϡߓW so̐1o_kK;L{q7MD3'fr@πjȤD@Ҵ#5Hl_]=bMuR,P# s1FDhr_,\wwаx# XM~xjtLb#5 #5}@N"U4"NO{N]_.z 5 N(,Ύ1$usjl*S¸p#:iP.dc5)b--,hFݿr4wT琝ͤBO)4 +tj+]-#/v({,zD #5_g`4Dy؛vu[YqδM;Kw:LtS#A27J6 A5rF+:YWlƽ\˝RN}.! ۦpD8ؖK#:) /j]g[U1f*ϡߒmw=-c':9.UàUu9xp<#/U7U4w6V#/^}7Lq}ȲF%s!M8zM\"δƺg#(ĪnWo)cCI2C#5MxOOQ)$Dc$ ~Ǣ)<jcza?4?NH8:T?1KEqtڮ[5ԩzp) "U.>enoaJ44 r9`&&)Dnçy<;NyLz_&A$z%%?(Rc0E 1+9|>_ͥS^y0j9\^@s_蒪]$ g@Cxn =ս~O"}xx\ E>LK#']=}l샺[sӯs$|7Ӡ]outԟpY"yعl۲i{cqۮ1:#:nOʗк_ M>cԥfb xwˡmbPCdbL#:tSw†IF|EhɌǃd2NdJQ*Y7[x֕QT{㖂Q/v~:Q;9|" )h(ttD:QV_ #5{6-%}ZA:j30[_1=3N1SY~}aF6P2B 9P=֖WT9MbrUqJ'_w_Z:9:~ZVKڊ0.tҧ1&~Av0Ð>mVlG fn7U&f9W-uz1s6$F]?qvѮ;p1YV#5&mh wߔ/BeoBiL?cJ ~47$(dªyEjybQ]p'6 c#5җ~ʽX!I w껁}U/;SH:הa:G$do)Ҭ /M,L{q9x*vN`[._69Ciz#]7- lJ+ȪG '1k |+ O/2̝HGuyÿGዼYF2)qDŧ' M}ДzhD$W+OcJʩ+<29yj+@Ο)7Dc);#:1+Kc:HrްyIaygE9?oXlAiV\~:Vh)s.Gg)?di>mGK<5`O/D(-_ZA~#5;?7[}+4h_c:i^#:Di롣TgZx|3?V'֒0j/#:D4aN$WO6]4z 6vQ=oztST"pm3VDڻ4B#:OlDi]WXdvyCoMIGڻ̈́xQ`yöw`WfpksL'84G0@,޻IĝViL5}6FDEɷjEI>b7QӎI<}j 0I&8 #/΋eulcLg9xф4'CLDt2W9o*5V~#:i+hfVr9d855.%芌|j+߿KM0R{B6al?;?}&ݜQ[#:yGn>,Beb6̰18)L4{>%sD#:]d5({ܺY#:,")@,fE#:kI (s*7 X (``WҔg'q4yusVxxgrLWoę7*|H†ҔT"@/óowѺu\\fN\@TlT,EbK\kfdhDWA+Gr#5v9G:5ao~$\x;Cgtit$v+!6>>W_ 7WP*gc-b"qa⎕Uâ>*Jγ}a.N6&rGD#x}2sAwi6묍V2U ;ҨY$S+.HL!R2j0+m t "D'*T=ݐoi Iy|l/g*4:#:,}U9vUK4$%q ޹ޛbgMCƔAY]BrOVSJ>wst]liS fS|PL ?"I Ex["Sazuy!6irF?rWQ[_?b~6JS^8D9w&rĮ-HՍ+%^ڔxG}guWIҥRt&w)/im|SWD(L#+ Jl1hg-2iA{~FyL#:q&tgF:精c&CV<*gٯ^Ԡs˫R>L&*k|S؞ Y02UޱOGs;'%2T꾪+U0+4Qa*U/8Md|مR+xj6j7: [8BcuRk_izOC߅D@hE#:li$`fU :k>s]5M:s=ŵ >%%$uo9 ͊YN̲L5`d.#:S#/[r>ڀvwq5@sWF*z+џ_VEw"t[D$~c(9B@۟Ni8?[5)7mpD(h^}~<1vg%'@0|9܊3F_?2m! h󋱐]jM1]穸f5~Q%ٷ#:$E/MžMR_I2[*,3)1#: -&0پOW,JhRLh<}XUcY $w\LO^eVR"ĭ($aɀўIhmeCH.jvn{7/T;g~MPM5VxΫ486qlPC:+?@oX?G)iֻ6![roE3vu]#:m ĮyeM9=Sm]S!i}rg0 t]2wɵΤ!%0PbC9Aaas*d}a)Yڧˍ03dRc1W#5nT?]Z ::.%LdN0 aj0۩nL,Š9HŲ٪t8-!,eeڔ,՘'1]Kt#:-uB4TF?arC]˭Ӟa{֒#BBd%Pl ii#/+=Ñ|ywե1#6'#5qS/#:ZT i۔yg3P:i&CW،ov9+:ƗM映]~RB)rZ:[Jȸ*O.`ı{17 DAomzt7W[*u]Ц t;;2+^1&I-8Zq8Md#mڻ'eעonެ-`ox!{!ӪX݄#xd6m$49)%RFXQn9[3jy;7$>ЮZp¬Ko2hgf oc\^nv.ɇr 0z<WT#:I=ƺ1~eUgYt(%n\fSI~.AOcLMoTd )\G:KvǖDYr,v:&8i5RPwC3g$1Ajrޫ\{XV@w2,©g6|^[U1~D]7cql&h#Z@?[?pGKYq0vaI瞛>}D+];7MPlW082O3NfocP}cJ}nUڵA%6\X"ݯ#/Js;9Cҍ8+O?g~%~\WR[w5E#:iV71?bx]Me=˿~qjo׏8D~]3ʐw`X a)Rno%?GoVQ* #5}i aEUWˉeUM#/;)bQdn$R"eAlu{>.԰& bNC&7.a}^#/Q#5kp"#f^V4v@DӴ`UxBc#:(Хq[-BG_@Q;GORk/O;|=NOԾtQ#5a#5"逅<>DS@:0{=nj[}z>#5A!Ϲ0j`p S0ѥŏ&vOD[^Zz YBl'u˜#>bOtWFހ4k%$΁L:(Eh^d?Bߙ46Zy@<O6SA`#O"}1&9H$LfRQZzk4#/\P(cD&Ms_akZpD3RfOu&EOİ  NRMHu$d$[c˫)sA#:cR쩄>E>5#/ k ưcChL]{&Z~ p;C6$ M(@APajWc. qR#/ {clČ:nd $tDV-u$FGd}P>#/!#/|oW#5i."yLl'/qIxCu4C+!sH@g?7H7#/E!RK뼡4/+5AeV6 xt׀M ,Iבg(tdxdէ8gxc}F㳌 huq$7sxcQ'aٙL;;əWR>chő >pb*e?m=: (8~;ek=,Y(9X}ITHɠv r%E#/n:( T#/2ƞeUB4B3XGQg#52\c0G\q5).iOޒN6tEg7n$ww59MQ_q'Hozùזk0Ctn4óY#5dXqFO#5;K|Ob#5O䄵LNxR}}o#:]}iي@>#50[Wwt$Nҵy#/v#:ԑbdHW[HzT DM}vܝMdK b#:9JӥaQDmM;5NC~W䨰*JUEX"<CID3Dи?_J ~N^$B%RBS =o8'S`dsC8CdYu^APjѐ~)Op޲XOQU:Υ,߂AT #/8РU0N%ffjG3;gMG'ʠ1m};B!<C|^Ga,]L1aH 7^g&D ʔ;kdYb1§Ťj" nXG_Û溫]8sm$%5Ro^#yk"ueU1eX(K[kn 9!3\i#5zDrnS1>qr6Aor}E~,#5PoߦiH BS(97kx2k Ԓ*}.5jpyArd_`{@YXIR9p9}ZYd+ >CEN4#5.K6/#:ۖcXRd|#},>A7.7Jc#/y QLz"z"#(r],C `o#7Z4AX#/~}, C Cװ 'sB y8É~@E~yݭLǂvB<耆h@{UϮ y c:o1/*]Ynl]3 ;=rO#:#Pu}-*ځ5>V71/`0F=ȝA<#/$hopp#/,4j|x0b3q9lz#:`{Gh$!z~WW1EZ"%mV{ַ#/{̊7yC:_GajGuFsy?Dt=P> ! 2nfh:p|0mWyx{Jz08J_bn$5-SCNJ6F, >SC;񸳁=})K-[rNNdމ>+1#57ӚHo:0CI4yZGp[ЙP-t:aٜ#:usZNFxcʉqdPB:P>lQ,gz$Bw.ڎHJWHf7Dč#52bD㳩BC+>!g1bFhKJ/dvf}AL@p 'L".$BB $C"|5[TUYVQuǰ;}G@py@Yfi.f #/a;va3ŀ"4UJ,y{~mMW tV7'K{P"lR?궴UeƆr 1ӍfD;ȑzI8R8M Nږ={"dhT۽ _k@\Gn<@'#:>Ȇ'$tEDRT0#:|q>.}EɈe)}a=79FC Hxw;o]{I3` D5C?/Xz?#?N} 00{]"gC M1|.[v=]Fe31Ǽ뵝ڹkZјECI!FЛM`ti$l2BCZ]ku%p̻O h1#/_w$5MvS'}iWc{ZQY5]m|r׶f2#:B:#5%R#/nh$vAeR.3Dј{#50Rmg䢀[chL`hlUflLh&Y3C-dd0iH`3iA|pCtQ9\t[}=. Tu/2ldo`}_R4?$iB 9#51";2I(m_ >#5NP O0b!p@/E? Q@}ȜFuՒjJ L,!}Ɲ!`)ʄ)O#:٧ڏ*%Hy6>;YQ1d$XHQdǗm=#:?#,O~ ?:) ui1C"'~=D|Zn) p 0`DDDME(G(l6M`3tݫz+͞]-bDnR&&L+7"ӗJ`}]=oxw>ts3VJX,"b88:Ǩ}##:tvԯHuMd"nʘٰG^,#:W(*c\|)}U)_ 3@͢ݾS&UTj|-V{íB0#:cF#vm1htwD#-"Uh B]"i\&]Shs'u?(daݵXrm߱t<|4ËӇLi9_pw,u/aIwn\nN塤D) HppmX@'3VO_BƧJ!ma#AxAt^ܭ3G]B9~r(gz[+ G *+!=`yCd7+8EϛkL,ϙ''[碵{P{طA0c#/t``m盄C=`X-~?3]$1i#"L#:OYcftL"r7])9;xn[_u*oil1~>_ס~)Z4Izc^ #%lu`dAPHTR, nFO0s1S|J/OZ>!EK!By#/P~= 8Gw(~ML$>|x՛b!a6*F?uJ#;1vX(f#:ʫ3e!EZ\{bsz [ucU$d*Ƙ7R`X)s@:6;#/"v|q!#5&7g8HJT(5ԍĴ-/@?&owj P?r`RA?'!<;wϲd'6yGi'^%߶u˜t̶O)畺Њײ$E! iw4"d%$ F +M3K+Vh &R`Z,-XaX(D#/?mO|ᱛq[ONanAAP:Bkŋ.U&1r&0$݋*7A.L("#5(*̹72XN08@ށy|'Ma}Ąhv>XHDS9{L r(t #: JBT#eMI.ԡ5r=J5?x%TRZEC^ɨR;jNfC#/!#DBa0w1nRPg1 ΕJuY9&ExVQ82i >l?m/LP2BECP #:O UBHmXKUzl/nE4+yB i܂x͛={i #:1ũ3LR3ʩ#: 74N JIaD5_"Rpw)!Ur&&^#5N.#9jt͠ 2AHU$H ԅYHn9,)Gq(1PB=~#:(ALpt8^㿚OD+ND {$} M>82c(zo#: E*oڥ %պ-);Fn 89Yn^K6.2']0Y2D#!+YGԵ@XQA)3E'fb膟6O%FhIOҸ#:'J'.JI"fS")rn"+!#5ɇk!.~ͪ!b}،+ډ|pEu LSH"z A8Ӌt vw=圧#/N&U]LJyƣ3|b$*ےSY#/̫^-_D?F$#:q*?bhKM<__fTeqUzWu7cK$f(Dpĺ#cSeB.eE_[aʉT4#,sb4BU R)G559j#5#:VZ h.|-U#5*;ʚѝeE񋌄+\o0ɭ;- F%r)SFZh*Y^c xC:Zd0%K}(44#5q|1SSNyySR4 A5J-T#5DJg퉿wI xTBgS *:Cco`"6_6:"+pȒrg3vH4"R@j`G,RUO0Xa !W+PXR M6ߔo/,6iO#wz\N#Wj#NæQ#5aWnGȇ(#/.NW(Z7RBuPTscr=#:gvX#:'f*a !Hw;Nly s;x"X??}>D#/5eHwÖw[ѓ!_O"Qij:4&sC*.GmV&9L.؉M: 4Wx쏭^:)YEnp94Ղ*f~b/iyma`&i%ezRROv#/RLiiA@ɔ<5?sww|?}t`rc!l#:[Y UZ{aHY#/YhC`G]G#?b;v4S"c#5N]0`nh`nY^A}$<,=р2H#nܝl5bۭ `c﹜M0{j.0#/BdE"%hcGqhaUQUQF䍸U<6s 3JFlZ'h;}>`!#/HL=l< M j&#/@ qQxW**n +vs5{c5ٕbLgGM܅L@@a0ET0P2LvQL㭄EL8>.dQr3k\/zxRkp0Sk@+Vžv70ޅc'd)sf]mo)٢4N=O#5#:̏v.g`t̺$U&i\4CV1q1ZIfk ,4u/e[w-ĚWW/=[e,kŬU!%* IGҾ8AZPț!,_8$I?U0 m4BVTM@Z\#skaLm"`âH=I{qHY԰J,'nÉm')ZI,(Q܉_͌ 1U^L&ÀHeIsJ+#: 1EşY0P Ƿʪ#}K/`<l qM\DEgbf!'w-PJ3њͩ0SuOm&Is:?0.C#/lrz_%H[K.cʫU4*CtAן|/kݔ;8SgN+ΌɍݷՏ qxָR'Λ.&^4ڂ'kIhpݱ8ssⶳo75Sc.phb%:0b42fg4-!9f#:4Ti&#:,ǯnr̪%\YpR7#5#5 7xWSMNZw^.KlߒF KsZu6XE-vˉ #5$s<ò#/inܦ#NWW##:1hJs,~Bv3L>eCA!HB)B,AXwbrme -!67&6w)A -sbB M#5ٽ@_u51;DB[msSzMц2/2ysѬY͠#:IlLiT e|jT۔K'\z#|`x#:H;3Cyisi9NKѹH=˃#c 7-"ea2$tP:zÆs43Y[cfd˙cs3+s12KCm؃˜"XA`}RpΈ#5a6ǬՕwhC&w\c?^5\t뤱1Cnd%Qvvּym`?.+VL3#5֦V <1R #:g#;+=ڎPTOoqAS3ę1'=iHI>9y%@.'2c[EYce߼*`F/ #/pv"iuWWE2=OtՎD=!48(Ҋ"8؏@$k#5-p)3g@qTTy 6#:2!*gh`>_%LN7.Yjldzq BF P&|VZN6Ż!rޭG &6#5,f{{"С8B.#/ԚùWsfÊ)4cNygקs=1hp~09#5o 8+=c:0LrF ,D643Fd҂ Jj&qqyMx+#5S72)0E Ξմ#533bV;JžPԷN L,)I83.*1Y'43=Mtװs69#/{< QFcs2ƋA+rpo#:7ܱQsN=w٠#ƐtjJ|8K%VUUVqˀe#:tgʁtj(IsŶN(c9m1#:-;,i(k}+ H$fhJBzK"`LhY#5bBG]c8jvIB {7C8&&9Fvs4tӷNB[&+#:t(ѝȎE5Ye5c+מ؆uB-[ҊEœ&Mkqq$RctbDd0b1!HtOH#: }~6 UB#:0>՟w3Al9?S;*z~ꨈ+2BH@RBY$7~܀{NJtB (16YI^bt#/EpFjy2~k5[D*yH[\a qRROPGg'IQJBw ΁}@^^։\ >b#dAz2 VDD1R ('RႰ B bQ@2S2ZTP פy[1Z%Q }f4#0LWM ET9$K*P*#/_Y3i:2Qm,läYb2x[0Jn\8#/C\^'@A#/"P$A o8dU_(0*1D(0M>C_;`LDTFZ(@B՚Wue#5a)a@,#/\hvS!W,J@`E-AD`[]z\^\tu 5uw\csIY\ur{Ih9D_6Jf\4]3cIm}\Ѷl|pCX2A #/0WuOmm݋s8KHa'+שa=\'M|X7B#jy﷗x$>n(R@>""H_w~#5L Ial(("b}D(EBu=;Vw]]3v4#:T) -?,x'{mZb 4DU'gsȦ$ )Q@3| H߿%OI-HSׄPZmPȹKJ.Cx bcEcwͶ t6,hgb`٬MJ0`&̌Ő#5(^eӕ8kI$x:=~>rsP|n~Xye\T헝mWahV7? #:!?*"(:CKHxܡmFQ#/#:̽˦P)_QD3!ڐXM 2!f/ #55@cXj#5TT6-8ɭm` U"mƙs#:Rz 7LذL@;ijey%n2TQNJIa EK~#:]3A툻:k5_AE~=R#5( `ӻ1v\F"#5oNE"2)E$`B#/H0 H~M3 +\jCv&%/Q!!#/E@eᾛ\fS#/u1R4R:A3d@ӤRd;vzjG§Sv~LV!D嗽MQ], g}N /ylJ;9! l'9 緞I+.Gwѣ=1 ۭ'` U7-f0[WBk#:r7%S#5aMߋ=y㩗ػy6çj'NDAM};~:ѫCd:pG[3{ljz-ye38yt, 5;z_1XcMmҞ|\3ݺf=;Z1!LF!4M5>/F5Ye\DzΣ#:Xem#:+MRVեZz+A<736Ԙrfr:#/JQR#Q!.ZhÆ ,UMɉ(ro<0,#/`37gMH0L=i{p1&ewF k#:{-CU=u}ˋīwe5#:'4-#5PđnV#54 lID7*tNv~8w#YR.-ۖvKˋ;,M*VR `agk*‰#:ϨNg:mJnI #:"$]=1#/M1KB1zǕ-3UgOU{v=9^+D!6ySJ04vAk+$ QvD;+B2{#:-K\ڊ s:K"0@k6pk;R1b #phWQ ~$_+3>|SpCEj~`W$NtVFC*Q3Ʀ*\3ec<3pJ]eDq#5#:7STʄsh9M  @a9Ipowo(>g.c>Rh1;q d% >7UU'=;Y qF#/.VE+iw#5^(3ICau4 NtA&N@FA4} tEZRDJ(hib $^RTj#5YY D(!R(< fP+ d`Cbgfl #/~*j;#:ͅՊ[b 'fE#/hi5eum~VF*Ny/x,/.=F"d ]==`ce>PkȅEپX.L=r"q#Z+x5CS taƮ:GdNG#5C?48HP$.[ 2܏\/7<݂=J< 5 Utl~ixPI!AivIYNJ~^Qp^*YEPiid\LDuXý[bF10=unw~1BuaaLC!]E`⦹ ."R fŀ,Om&([#5"6Hqt50H ,rOlGQ?.sA{N}4V%A#/{:ʖ L#D#:b/( ׭qHt5c Pޟ,1#:{6SuRogO^-2̒D#5Dm24KTwi|OA&AMX~i͇tvKJB8ȣl5R0lV3N' bh0eV"Pi鞍tgvkXR >B5WP# 25ZaX" `X4YLcWn;޾t-6HܗNKQXp(1T^uA6ƗJ$I8WpHb;U[;73!zpO #:xF3NqYz 3!^R; :mz_96ݳ$Kcڣ8֒+L)$y=Թ0_hYVg5'c؂wd0 =%̄XDX2KxD֋[5mUk Z*6 "#5HPϯie 1`ҿ#VIen`>nYBH#5*wM;mRa LmHfI,&ش)2V"IMX$kۦ&Jh٥)VY&Q3"#/14|Ab#:DI),6$h%%)LQ1ՐeM#%Q#5 )A@Efr;5ۥNP)~B`#Ʒk_ƛ)dhCqgYE#P%ré!LVߖ`H-2h+.$Ms9oQ?<}/%8<(~ØO˅դphҟ-zYt z0$}g=?ʉShl1#:n#/\2#e+tc"E`)iZKfJLLH1QF(Fhd쭼en_Nᕭsd[fhyI,~_M{ޥv#//W$F0'?<(a<׸'x,GGqD!bY%'UQau`3.ˏcc\vGa{t6#:!;UM!B$!LX031`f/#/dEBYQ.;|=Fg~Pxq$8Z*V+φci~-Ci'hp-vG R jOϑ/oCfLX^f0qCǷxYtS#:Jy\icZRإuiS<%X*%OL)D)tLKix B܍a0fZaeDW‡K|\.jpG'2KSO{SCOkߙ1.Ltw;.#53#/K)L393NN{&ppe$y#ynH|hbuS5o% >7{PbTU#:ڍBA!&#5gS'lb:|]^oݖ982ULɾ!cX _;lT;.q#/X(c1ENb#5#:SHvhLQǼb Ps[qÃur"u;'q21I6$D#7= c-n ?#.KD, Ȅ#,<#5#:`Z"ZQ]"%BD lB!#5GNx q6Xp`ڏm6#51;ydyiiku$#4 s )hEKޖXrxF eu=(lb0U2Ih!9Bh1iЄ5Gb JG3o,x(w"y*&)R4IFAF٭f{Wma@QCtHWA D#5"JC+"[T vZ_/>]K4̢e0ϥNG:#pWȸi;Ce@UD剫wR͎Sn8sttUb`\0(q9×9סusB*ݯji[js89r"H$cGPVӬvf[_%II@"c5#//D/2!itkF`t=)Qj16L ޿ 7X#/T]H4h6,KLƒ-ꅐ%H Jp;QO xwl#5GF9s9@RAzK܋"?: XЄ5.o7EO_Wzj=yXB&}.\2 ,Jd+fDcbo!,xAn Ezml](F@`]ZiH9[R==?pgF2,#5bf|b%yc-OIA]6(RmX%E-35RcZ-H^#N

    A-ԧwWzKĺ=Oypv$#_phl"#:J ƪ#5@le'' @E8EO0 iAO/*HF^}loݐnag]T]Ed]tJ4&Ib b9hm#/MT cbL)gWIPh=A`0M1r(R&#ILgK1jM覆$5l {q y: qlYTήyd¤Rq,- JR7*dȖW=bnB#5d!1N6(Qd#/C(hp K^;2]! Bj l].z}B#5"OE2^(!" .!#_N.`rRv$xI#5K#/0Fv!)F#n49ƕjb9mk2cؘtg,hѥk(/#3t Xx93 VPIݮce3!CưӪ F]N`~4 q5H~}gh|9AprL!#0i`l32spmPn4碅h7;j5EQgrl @2ذ4)!E lb]ta:#:#/ Ld@)t]#: 3 #/BDVܡs0ӝ>@\P/!B'F!e봋|[_%ԋP0zəBXT\>p?8XM;6IG#/$\2 5=1j=  uܻm5!GC`&n%N!jyc:ubs7%-vCvZ8lUXʃ'YN " dڧJIaj#:MvaU0<Bn0k&*c*rT2 "3Jj|`9M:Bz9LSNyG#:7N4×cR#FrF,g[h5#NL1#|az(RpZr-`L{#5X>bƛ{bֵ̕+zk&gI#:& ʑ60%mSc+$Ȇ.)s ]3lHq̵ȸnS~/oEa !9< JDj LC$ф~ŭh#$s4,y&m:mu#:X|ky)!1gA{qg2P"!lDJ ssta1qyFjPWZIX7+Bם҉ח5!kv $f2R$p#:)lԅkkkSfI6kbRfZ$ 0ň?Gw0PH@hԆi*5 #/aI=ڑhM{*Z`2%HQ9a(|#5N0^ =d;޽–Cvo$@\Eq_!I 1ɜ²hlRlQTWcii65#5m& |渠v0~m(C1@F#G^'O!t4㭋}ٺ,omm PšeTHJKs\-i0Bv!!a0Tb]O5mӘbSXTtD:Ɔ':ڳmQ5J.Kܴ]J!#C/#5M֣ ଆG/nE] ,B"Ru^J~GEdMܼUDHD+N/8Gʽ. <4o?f~lI .jWӲ3Xڌi,%I뮪 $o(Zkkbim%R1)K)Mc(JeeRLQEm,^wTewtՑ>zkzv(Dc)*ĪYj5SIoZWLMZƤ5 MF&yזi2K(ɲVvֳmNjtZTήwFl+vTuI*Mqʵ)o+R3+IX],sxnݏFi;(,a޳0ra؋Є@AD2b Xf*CA:8bb" cn/3m1ݽYV }vvBF0)eTMuC |ҥ ҵ#5~sCQ(n#:wMN]~Hv##5P *=$7?=*1(QAdYER?c^"Ey:Z<-'JLCT}e"! 16,2%P&Xؠ"մl*UR4,4j&W+Dh5#tȗ#XpId#9iq#5`FY !mh #0\L\4֞JۿTLF#5u#/5:_jl{d#/~2R"sօ!^#/#C*ԋD1+beMTLYps"pE `:wԃwe$X1#5bd"r2wೠ6c eI #hĩm3C~+h>@-2`Nv(4{/5|-I!?t҆q,B o+oQ:.h\,DɭuCKFV.d"uv{[c nԥ'͎vEC\r:nJA}dDdNW `cZRNRJA7+8^+dj5!&KƬ[EוQo(v"=#:2idJf/y3rJ^\fSCh7dU X09ιG@QudinKިyuERR,7D+^u\)hvGn9`v)L#:} anbg@i1$pl`C&$ԳUKS&igSPEzןc IAɚGkpdHxp&bK+1NBFJiKm/wQ,!ymZ{ZXNwV6t(I #ϔƩۆgn!7TDtreʾ1KMZ#5yl<^$XU=xԞ3D0iGQ;: 2Pisr ȅ#:Ʀe=(9jUD}z#/!ɱoVyG X7/#yBH$(D9F'Ŋ"c8ػ=9* Hg4Uc0sI_D@+ݎJ`S82UHv]2/vF"GW&StTnʘR44(ܩJN4QJ ^0yO/V#:xl{׽N '>}}կۛ9!#/̶tfb*oi!zY7_x?oؘ46tQO@.I~#+쀕֓@ޢg&HCw`7P[(&s `1Y9`#:).ã;[%%A/%0j"sP!(WmiLH B4*b#:݄*)LBˮ@iF(j#:H](AMH=מۓf>nH7GS[xv<9GRMq;"i-#^ )M3|<{oJW5UbC,9\j&/hE[#,T,l,)닛D5#$̡F#:1%$CB1pI8eaFS|&zX#:2 Ӊ鶺E}d`@P\::uS|q 8!CiHC> }3@6!팰P6J*(8@(#:#5|hA#5v~ q$cxefCH n7~kߣcSMw]QI$BPҨo98Q10#Vw _Tg!{@U,:bvD-o~q Ѩ AI:X=~;&G&]Lm #:!=#kTPU#:?%hwUmoK#5!$@'@#/Lͤ7ס<}Vs/Dt?_U|^0f\ſzնmQ`!dDo]~A&w)#5mC#/(**# 67^Ɩb1#5j~L35!&:@oC>p?H<'==-#/ 0 mY\5V5UI]0F#:C1xb *" \VSn]`Ta qicHr'Tt0m41@mlo 9A Ot=OW'C{nhVCy)J vBw }XKaH!;>|~6YXejZ+{ C-ytx)꧜髏F#ӵ@!!1ўipS+R_Q}4Q1bUI仆Y)3R#:TB(TҿgQ K0#/݁JQ8¿T6&V)LR #:fV]fPr%. .R$Ɋ,H"b]22EYUr_^-c#544KTA #:VAe$Fd 9 eUYzԒ "5b c.I.+@M*oT;r#:4x#cAmnZL^ѓԺPk p#7 ٜ-3:īF%oui0k#/aʎUb6moR#:l V69) ) &]pz#/#5bxƒ2"Y~cu'UyуE8&,~G~+563-Pj3&؍㭵wk˳]ݖf%#5Lzo[χGFnjG*jA=ѽ>%AcX_H?"$F0AbMo6:6X±[u**m7(! hQ6B YT$#/\8E Nz\@`ru{4\zSטA (H?`" VP&/U[s{}8b W5)X*))ҥL0B2GcZ͙cbFybd1CLY4 $UTI#:I6*elKm v#/4ZPw0u5Z0F1#/PF1 i#/@1pAK0@EvKd{j*#55܉pk!%?lL``Dz@#57z7,8JCܾ"fq5<{fΉ>"a3ɨN#:()4zj;}þҫ|q v]ffA.\ةQ#/}18:qur!`@~h\+$-]Rb&֍YKZiYO%t,[!Eә!A$^r=~{w&}g0$,CX#S ¯]59voNv,lk̡.7MYNFևTf@Nܕqm~v``e4a ęw!Q~6ysYp0Hex|{`U'~ٚ8zX:=tL뤨Gʺq&DǢi=ZaddG1/ލ^}`IsMt屐'6CϚk@Tb-# BԚI#jijM֌fkMؓBTT6ٙTْ*(SRjcY[6kIb6l5hikHk-6k[A3SZ0i9nƉER*TQ)@Qjݛm\k-DX&='W #/0#:H#FP?3 ˔쯯}dd\ @!oKL#:)\#I┇Tl^z#5_cgpq$;BHlє:ѵUS#: nPߎw gݍ1#6BCe<9țj@L(aC>,(S0RP9HzJX,F$il]Y-%eq|o{1JFzSݶl|50~*=rJ3Am\glGb[4p.EX$(iRDxE(ƴ@}=Hv}w)ÕHJP4+[JHnYUIZdlʶ4n2Ƞ0T͌8|hi!u*L8pFYL>5Kd֋׃TQ.}=+{1iH>?NAq8i@*Եr\F߫+ACTV3 -#5Hbw0Y!܇L#:Oz.vM27_8'8)+%}٠y]"=Wld#5K:zXغHN#5W_Jk56 ]53F-K%29TO2bdēʱfd)D~[XVK/e"y$>:B|v#Cؘqp)#?#V-Y5+o`PgSP5S睙V;JD~6`{M1։l2%Cs ;9=JI7#::^%Юq ٲ1i#5p`?#0ܲ(E#5Tu؆Wh%0a3#5\U6'Da:R=$~r +[_4{Gqm[PC4X|B;QC!0|#5T>Yg:Y*9 O%YH:4q>fİrisY/DA2M#5-b'O"0'ƟwӁ찭42];Ctsӗq @=QE!azuU:NPjkֱj-lmTZZVjk2xӸcr#5(с\x Gѹ83DnY=E#5+j#/]#:$:P"7Vj=.YmUpA> *h}CǗa y$2٦4?|I#:f#/t΀qSP^!A1 I;2p0PdMWP@PAݕ3bוQ{7AzDE!%DmQ_Z%@5P܂RGХY)#:[mY?wU6" Ѷ[JX)e g/G\T6I4̸l48IF\4 ".T ##[6=C*ha"n1]0I@>4[%⛂\X61i¢4=Jjd.@X+/m6+,;W[a3XEE[IeXq9H0H"P%!2RP3d%ha*!hM5,YU B#i.#:h#54#/epLhc33~칱0LA Rea- ^Yj&J n4ϵ׷C&*ž"<d?df3emZ*`l FH"I=Jm*U0*%8v*|Zaޮ2J=1)rHOnG~׉|&5fK*f)_Ndѵ[3 f(D'ptW|,&h" "7rKcHsFpÝ'nVz?I{is{ wV#5#5!)`ϼgUNkٜz<#:I,K0^PM)0\U`SP!d*'BTM#BX3§CkUdō#/#5 z^Xwdj<Xz#/?S0>#:sP@o6U-mFgeѰ`2#/"|#:ta&8\iQ)'omxdemֹIj6K]WvfnQ]\4VE 6k*wUK`!PJ"BTRDVYE p(%xǷ:ZnLjR|p\!NRVR՗KZ.݈, R*W{ņ+5X*XT$JTnڣZ+xS6qڡV`̦ƱLmEmhhhI$-4mIQUBC#:#5ÍFI2#&0`VAQ Kq?^iv{2{{N'JV*iZlmo̭WV_nJ)IJVeFђVݦVlM5Wm6?;GdiROBRd9 1T 9d!!k#/ŵÁɶH⃁^USwt0d6[B>٩G*rl~RT"bH~v,at2ŋ![ ƫ1za PBի2=$ad^W#'KwՄf# oϿ9ca䕣e[᫅u25@6OBD:>gQ8PpM–s",.uNwn ښ hF4g[C([9}VlۊGNXdwSi,(Vn6'_Pe!<{Ii{MB5NCh}"h DaFE B[|~V~$Qo|%kWlߺjƪD#:`*gyA?9qE@" kp+Ń/d3mҲV^W5ڱUtc#:0@@kAAF  bc$AR!uMޭ} mIQy$d rl#:QI*쑋2ԊLIڅ{E+#:#Ii1&Z3VEm=X@c0c(#:*$DXPJiy#5r64BZ#:\#:/\шX1A"1J o. W\H&6L7^=LM4p!cZhMۿw#/CT{$"#5:ۊ&$B#//8,vM#߫lU *GCKAA?T># ʕ"{Q>c>i? Q3^+ijO7j#w}3.19hMՀ F cƊNwf-E{bHBixJ6{,}04~%A`QKSIly#/7$E[F1[M/b?D]bney;;}FIsj/2|d: CUroO#5@! ##5#: #5%\j%A<-9U}#5wGV vZ#\ .bY *miuyʅ99#5LMi ,h-சmί7uuͻtx9lK۶cVimEX٦kLQi]Kweugu殡͋EFݦ ql_b{ #5p1A{wGdt#:_uE = Au<tz~5:S!?;SnEA)D`ꦽ{Kzk#:~P^~HPA><wiS`EHc$jL/WoV|,0u9Fױ %lkl9G"'BBINu]ԳQFn\t劊1+7ΪU5I0v_mS E#5 ;r#PJT#5eF^Uv6򧴕V-eKރS43#:(o")1P L`RТ+#/j{+N#/#/*ekY-e/o6:+ԙ6ŶGy0Jqrp>}@ 4Lҙ6qE$#/ˇQ֦|5ȡ)X#:z]ܪ8`ɐIp@.-K3K e&\K#lRU#51H $Y#/ATT 5L N@&p\ ƈ'M+ds9-W(YTp@%((#: CX&;نL$c k)#Ӻ j#!#/숪|!E>xTDZ(T)H#5FvY5q3#/ħ6y&'رFҪJ&di5J}RFi4\g#r!c#I(#:6$cO#M3-<mS1$"wyh P[qarF܃UV9nٹL7@@#5gT@N#:#/X!IV{h*$QN#:luV@z?%68B ^(/n+l,d"'̡#" N(j:*Hzx]ow×Ff0Dj@CҦ76FKafRΛo#5ΒJT g,L5lj9|kVff?d]'#5^0 ЊЪyp Z|_4?/*'] f0niCB۶Dkqj<|UQr.1C*會u =1~jL['IL8]a #: F1t2MaDV$X5i*V1MDC>$FXyu2aI3.LU7x>Yt#:\#:!J; G=Zf1i;m8\)j$j>Ǐs~j)3L01u[4h!$4ݳ !C%9+!{ w騯OxfF6Ɗ#/BܩHd b=%Hq'rGԡL{&߯Ng SYW p@QVvq#5zSAžE HN5a&@SDI~F8NID iHHB#:n˳ h#:l`8Csfa$#5QHTA ^D=412{+Hʪ1m{=#5X*6``@+2jkn31 r<;']2(O/ kknS 䉿!i!lzԜb2NR_* 0ppkv(5Sl0JhZJ88#5#5Lb'6PAd+×"amAgme՗ C0vC7zVI$1Cpt&Xt r[ֆ4cesyOaEU[sA zL:gK"۸c+s Xamy Z]zBS4Y L(h#$ 0EF#5(#:H! #/@hw-WT7|}8ϴ)oC$btqA:.ż740NRmv7!Ja%O^RJT^XY:GimşG#xQgd8ɒOHbtz n% 3WKBnboOc^c@S#:" ogT#/@aD!Hv2BŌ59o# ;=U#*hIKB1Cz(GGL9cmnqX֐#:Q=Pji/#>;z T*#/т5NxC@` [8 $:_)AAS9:fvM>!ÐE6 ȡ]MЬBzQ/hHb@~з|/^qgl?)l~#/uNib#/(B~kd#5~8uD,/D*>T2JdGWXr}8ͮ#53BB΄n%j{vF[CܘPDh@G-FZhf@PHZ`$ &͔C`Âby6Ȟl,qPZ 馠XZ4#:~IFk6ҚZjlYEjld C>P!>}A~R1$ 4a#5Ɉf̒Xl5)SHRR hܢO!g_'>ߺRI폘UtT֍Q冉3JPH(xќy@ZplMCa* ]tIH=G6)M*E)jQ6A$+@DCtFX8@j5C6<#:!eChD 2eQ2UZtA+0nF HD,X'iȁUaXGUm*n5!D`СU|6[N͏Zo(j5!.ƃ(LMmLyMbRd|:۶(*J#5jXd#̅1X1b\/#AC:AHffA:Ε@qwT7HkS#%qd`2@4jκ՘;!D`27nѢ.r0$yJ`R3j4j#5 b#:f5(ȒiwL#5RTM|#/z] 1vIT7sA 1G,P#:-@4h YLiM {j'ݦ&?mh֤cg &P)@XRUx=tXh9ƚfMUTL3dXzjhaA&Tk`STط7'C0l=,z`h#:2_l =XP(*nLR#/l (G/:pPf[VۥMCQVD@ٓJ5Ĺ!`FԂ٤D#Ih7#/4Ѕ#:J#Hp`#5en`m h~Ei^*DjT^ěc 7IPcµF/WξDP[QVƅ,1d#)l{,5咭êO/BȈ)#/M#/.v?+}(\kDFBQ\xQVP$AR0RS`нq= <#:C 4PL H?uQ)qȜM ]ClVxg:؂WWKk{n:A=gMD`Aj$1Ql_Qs~yvжMLcix!IkEJ@m)0ִ|z,[E&-u9pSq-/gyXavk24G V4-x3W zn9X`gR1a\̒1>vw=B1 Wj xiS|?kՎKIkyi06A'3I! 0kp"HY~ @);#:0_5#/rt}SMפE^ ofaķ" >=ؑZF Do#6RŤ*$hZ6SNJչiU81CMA@-#:a )T6xo'&Λ)S)!9MTAt^bUx]ni=.PCU!Б**^9rQTm#: HiV$A(nɎQ«ih17S `x;_sB> ]>O4ѕ#/Y#/p<#5*^=r"H߿y(Wt{─|9U#/jDB|Dp"ȁ!LVwT#:F#:$YQhR^[xX E I QkQ5|"YP7lAE@dD#/ u?CԪ'WArH&=E@+\KUD"WC ̞A:)zf͋ !+ HF6R"4CfjeC-j6M4#:#/"PX0<Vjd#:#/ɌDlD aK`aQlt̀а5w %h ,nd(6#59,"#:bahhE$ *#:(b0#:/Os#5ZT,vڮ׮SAQA\#kCW M%*OM$_%aO_3Z\)^@{X$U|Co-#5! ]!(17f&&( 2V$Ҳ2 }#5[tDI o"70e@?lX}W۶%撝[K33z j5ȊAZ"%\r!2Y$ՓfJ(e՜5w&ͬRR({^m0c8$z~{PCˮoϧ4St#q; oTeY^gHI&z3K9#5:$>pGNG!`M8@Wm D3Lt"B<12v#:)m5BٸB99\Ct`!4/17 i2yYxmF]!%#/tFFf"i5'N2gg8ųtStff;)Td>\yxi"z~; :1#3Hm0L<{&Gnv@Sy>cch:=7Uܗѹj!1ruOB_,~]iC386)Hfvp@@mWP7hEiՁ/j<>y_4:VʧT['KxNH%*?;Zd&Hcڝ[Rϯԭ&yBɳJs_^m>'1N:-33U'wb`u֎&QoEEd//!Mt)!, 8C=*RN8M{1]{ή &9@4m#5Th2s6c^WFG >ZΧ|-|y[79)#:񓩫b#5ߧI% v5 &dTQm`ąi> mcM ֌ F#3Ljz=Ƥ^<$Big5:c ^!a*kk>#:h{ kku L>ٱ2--O#:u=vĎ/溹λ"PD/=Q$d?nd03V0x5P#544U1iN(z--X7\]p X ,#/ՓbT`ffK-CzI1e#V\楌Fy89vH3fEΰ֧C3xr&i6ډRmHXK.ԑNµ j8:5H##:AJtꛦ]PwE{wF0RFT. fpjYf8d5&]Jf6z|k;jVFڻm48&Ap0Lb֙MPš[8J6je*XaM0laXj,R7/<]-0Bb&!D9 hrH4v1kYx #5\C@(8#cTnL'TfA%n "A)FU-81A[#:+aXbX z6񬭃^Yái=r=.4:(e*#2 *-7b$`GګzpMW~p02p@}In9i?Kۭe)4l!|&0#:ՙR4'NM#M#, LJ^2zx֑|#:uի} i9Ìa%M}؈ ĚmQ#5Ǘd?UdF#:˺60`oyPQHIvV6mr۵ʩ$}CWx]TsA;*?Ku68xG?TЌ>i[DG4$U֘}EG> [Ed%vCF#epNE! M#㢁>@7O!aPKGX'z2YtRg N"ȡ#:kE-F"ovTKiYRY #/20yx$0_z\M MDȈL#zX59eg1Qte;Hٱq u`bбc@O2,`5k#5;{w{B|k,5#`9;YRgKbz-Xjӽ)DB$&FE`^ly|#8AI8-lHsŮ/_>󊢼kvc&bTb#e5ŕҋހ<2S#:Vѡ4̀ta E0^vX crjH6[%)s#/2"ޑ!I$=v҇5P@F$EgQ#/n(9=7;hoJߧ6EΨ;Hc#5AҰt2 5MF C+ DԒ,]bc6~Öfr0.xʯ6u(Eu㮺TRjeRy/:M&-z^4>l޵7*}Yr'YAPz'ا#5GU'M߸L!#/͞ΥClde[Q.!E~_sth*Ҍ*Q<:O#/"$$!!#: o?H/mRFOE#5*=8i!"&׺%ɧ P1~H3~0r+]~'8T<T=USOmb+[-,\LTTpd #:ÃCvt o|AYܥV1pUP*⣇c<8>mK#LC* Rq E*kj5KX#:O)VdAoNp=MYDk 0b͔7/5mmҾN((6֌ƗS)r#K88#5K_:#50 aX+N6t(ȇ:+M*#5GXsC͠vݠ%,*M9#56dM3R i rN=G"i246tQ`S3:ui4@䅞ZFD[4=se6 ݙ7#u>gV6f6" ZJLnF5j6X%]hŌ]K8|foz:¼P{5:r1y#5iIu5uT$ M-ⓛCA6,naOW.'.YNˮ "*>Ϯjq }.oFKÎ9ecd0c&*9l!6R#/arp?;o-͑C"[._q4rFVfa^eK#:p\ {YqU00}|\#5@m9ƒoqi5oO`@B*Y|/(Ȱ|(3ŭof^II 0Qi#5]٩ÛCcgMPٽ"``8Ct%J%#5T:fHH@_,8kG*ƺ'DsF3{i ot$ 5OuwqwcJ#A5<d  S&XVi~W87F"PyhEAFH"PԚmaך Gpyɠ2m3J meM%#5Br LbrlV}wė D0@̘"102KHnr f&"TK.#nגT$\mv5b"4kr6n&d2!R,"r))*MP5I#/`6]Q* H9@S@)eQ͌\M!ԧ8 P+tL  #:j} >xK`EDT4v!7,={F>Ȫ#5EF! ªT^+8guga/^u\îg\OЬbM|,n%jk) z'u4_#Ɠ9}xwCx#7~sn>)L.Q ʕij ~r2=E3=a:A$N<6Tá= 4mtR`MRpAUjIAgR"V(nވbm.N柩+1T-Z%k16_]Q'bL>DI3dEMa3N`4J}e@'I$L辻܇#:%tRaI |#5$v}ʼ2uEA ç hŘRȱX2EcxK݅J祹i]BXF2<(Q#/ TuA#:XS[#/h&b1= 6#5![r/"(X-AFFR[2Hc xC{"X!Dr4Tp RKs1U!eyؚHW2]Pr(OWXMu<88#5TU9΂1)1.`0ǐ#/Ms!6ND睳;`=Wc!qBqnf|HSu{YwIHt(%6!$J#:=u8^I|̷SwK_|(5BETiC](ɝ+'wD $RT~dI'ȂЊh` 䒘 Hb1 1PwAٖ!;d>HC~ߓy}4UJ4mK2h%k?t4Pgƙ3"b  " Ϗ\|:(2˃Z.u_X̦7f)H#5jq#:h"Ys(PF+xPAMZAy;Y㤳Ho4#y[(D@R1H1&SLf*meCFͩktJMZ:nͮ}Q_@z:d8AF9#/uZ#/G8M$0b$H h#d{!Ǡ@;"_L}\K>zŒZ~c,Q# -ٮQAMX΢(4W!߈-- xJmk#:PsΛ6S Nδt \!Ƴ8}̍4ۤNq3QYk# H -5,)u5,~Œg>X 5ԡljDᚥ0R޳Om4mUrhgG2#LQ~E(p<ʩ hRRv6I] R d(%#/H*}<{9&nhvL`G8A@ A)]dz>cƍSz^#`z@"W׳bttX0TНT^#5#lN`O?R4a/i÷˶ޜU*As̤"u NT\rhkYk&~gzs )"F,9xKF5+*#5灸 ;@B8H%.Y)"*1d2 ;aƪmX~ B4Ae Wv-QL1ʛiE?q#:@IQ1o|yz$#/8,3D`=FMb;YAd"A0BBgcI X5dL$}ߦ6/TQbQli*6Kw"ȼO*@M?$%-v{h7\IdhEY:j`E2"k6*EjKMoWB/iToFO}_a"I䯠 lpoٸt4$#5ݺ- 觉 !y#3c%$Obz%~X5dy4B½`φfؑ ePnFD7$;8`ABB'R{ֈ0T,I7RB&hMT Bͷ&QLJ=;`MPP!졠?9<#0azZ ix;JάܴlN~ -2$O"^Jߣm 2_Ǐ]58Ѷ6 ǫ#/v׫/Îf#5b,jJ#5XɵFol(h#:Te,HXO:d U,<覉?-BʡAp\pBwAHXQ (H[F6+KiHYiy$51A EGOQ #x#/ށG]ɪ=2Wn`\4̅0 kM҅ @7 6LJB$D#/27k\έo13KD>`V~"A-X\AόPG8r+#:mH#:D@W١a7R(,%LipCt셶+(mHUivjfeWs*5\YRo;Ϋ /nʋ*%0AEQm)[2MwV搥Br-#:εm!BIK=»)?ۅ\%ҥX$8#:Ԟ;M [*ĬX[|#5 c" &v(V55hc#:}j͟8qPU]zqfHNZFQh)6j"A I%d 0}]Roߒ-9@dЙs8~ܙtwK(Hb^ƕm؈ }vJ8b,~y N/Ϯucbkp|Xes|q`׈4f^_E<0W\6p{SMU\E Dܿ[l,ӵ?qB/}#5"Ș&DanM 0!{7ɉ/bTVq̏RQ#:]/lIʿmdR*L pl{ω`:f/jIb83G3zZYR4?[xn-; گZ΍,q'"Nng ^Pj#5nuQ2 tȶ,S#d5 xa!@3ad.<1\άhP4q#/pyLn!!rc `Z vDGM15@;`-?!YϼQl8E~ݠD?#5SHԞ@G,P;2dōb^*o-}4#: *ֱ1P4O}_g?~~ͧ?Oz?߫ߓ_{gW?_~.ߣs@hPChWgT Y?*@CH$#_i{;dL\0s5NQE MME8b.!pg5HJ]gFP{Zc3e< `Po?eGIZT^&?T)CB@g'M@Iqִ8M1(cQJ?ZaTg~*5hY.]$DC#5B;x?@`wpZeX h=0#5]f3MtjnJ82t#E/51ul$ 9 WqiWmģ/./L-.4R:c'?ٗf'e#:f,Y`/<Ո5޸+n0DcF+f]dWLpbuW{w{h1󍗳vѫ…IHB cx拔9YrZOYgpYQFp#:㾚|uܛ P#uR5/n[)ݿc .$dm p8劀>#:ȂAzd#:PbH#:#5C5YU̒ {}w!9=z;,ڑhdIUYAM PPUS%k%*4#:@9K~A?YEhs;Z$dCL9_@_Kz&OyC7yQp0#:!>a|$@`DS{l'mEVKjFmvWlkʭ~(-H@#566uwL`ƶ8oNvϑۭiNjtK4tbq(>bPȹ$Ե_%;~ ( %OL[m #:dP-zZ#5[.ikoO/(M:tZ(^ۊ+'k˪ _GMһxT>yrD;}tN]@&2S?xGu,4N*%bx:r:JƲ^(,+DCmԔ2'>B̚ta'3\߱OH#:/> -#<== -#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1\n\niQIcBAABCgAGBQJUja5YAAoJEGelZe39+Q5kZ0kP/RK2odJp1mbvHst3fceud4Dw\n69ymSDzBJKsLyl5oPl6RGbSXxGFnyd0eFuewUcJPVsNLObSfaXIA/AJdHAtZ8y9t\nbN51jD99jBGO+teMvjK93EFIb4P7cv8PA/LhQq3NUy2zrj2dN+TFjXx6+PU7BKrz\nTT0jTbXinNSF6Jr4SEqFrY/dpzs+IKSAEHP0BmPcMxmDU/uCCt27RhTgjQYaLLGi\nY7ZzhBxUk8PgIIpSucOUMQx/xbMvgbSRNAYVqqhBlnHjgPAbFY55d+k4uvlu2TJX\nmFXcOhB9dZbr9xuFyUD3fvfBQ6OZzrKfgNsiqqeTLVehDEaYexZ96eoDJXZawyjU\njer1p7qxKqscTKLsyJWbmjTAxElgmlLY2+LB9LRYqXT4tUbcPiahHo2djcdVprDI\nnEZlUx12uCHoLmKBAMtD2ha2uTXtkl8GU44zB2oOQU8hoXtRITWYyF6Q9qlLp9qw\nosuQoQ+6ovT83q8OLnBUmGbVYa4GFab2dq4rMXX91o3hMQ806xQTaijUlZCFGMD0\nxa10IC0udzGOMNPL749zlKOaK68LUbrGPiy65dBjN3i9zU0itYTSPLS+wu9GdY3m\ndR96fFsGRNcceHAXuqMl+nxtJVE8sMNO1VRle1sGUqfBw4jUnJ6CvShXsZQuAeZj\nSzBEpSFrNVqBi/BdbcZ7\n=HKUv\n-----END PGP SIGNATURE-----\n diff --git a/platform/silk/boot/waftools/binary_header.py b/platform/silk/boot/waftools/binary_header.py deleted file mode 100644 index 827c11e5ef..0000000000 --- a/platform/silk/boot/waftools/binary_header.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import binascii - -from waflib import Task, TaskGen, Utils, Node, Errors - - -class binary_header(Task.Task): - """ - Create a header file containing an array with contents from a binary file. - """ - - def run(self): - if getattr(self.generator, 'hex', False): - # Input file is hexadecimal ASCII characters with whitespace - text = self.inputs[0].read( - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - # Strip all whitespace so that binascii is happy - text = ''.join(text.split()) - code = binascii.unhexlify(text) - else: - code = self.inputs[0].read('rb') - - array_name = getattr(self.generator, 'array_name', None) - if not array_name: - array_name = re.sub(r'[^A-Za-z0-9]', '_', self.inputs[0].name) - - output = ['#pragma once', '#include '] - output += ['static const uint8_t %s[] = {' % array_name] - line = [] - for n, b in enumerate(code): - line += ['0x%.2x,' % ord(b)] - if n % 16 == 15: - output += [''.join(line)] - line = [] - if line: - output += [''.join(line)] - output += ['};', ''] - - self.outputs[0].write( - '\n'.join(output), - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - self.generator.bld.raw_deps[self.uid()] = self.dep_vars = 'array_name' - - if getattr(self.generator, 'chmod', None): - os.chmod(self.outputs[0].abspath(), self.generator.chmod) - - -@TaskGen.feature('binary_header') -@TaskGen.before_method('process_source', 'process_rule') -def process_binary_header(self): - """ - Define a transformation that substitutes the contents of *source* files to - *target* files:: - - def build(bld): - bld( - features='binary_header', - source='foo.bin', - target='foo.auto.h', - array_name='s_some_array' - ) - bld( - features='binary_header', - source='bar.hex', - target='bar.auto.h', - hex=True - ) - - If the *hex* parameter is True, the *source* files are read in an ASCII - hexadecimal format, where each byte is represented by a pair of hexadecimal - digits with optional whitespace. If *hex* is False or not specified, the - file is treated as a raw binary file. - - The name of the array variable defaults to the source file name with all - characters that are invaid C identifiers replaced with underscores. The name - can be explicitly specified by setting the *array_name* parameter. - - This method overrides the processing by - :py:meth:`waflib.TaskGen.process_source`. - """ - - src = Utils.to_list(getattr(self, 'source', [])) - if isinstance(src, Node.Node): - src = [src] - tgt = Utils.to_list(getattr(self, 'target', [])) - if isinstance(tgt, Node.Node): - tgt = [tgt] - if len(src) != len(tgt): - raise Errors.WafError('invalid number of source/target for %r' % self) - - for x, y in zip(src, tgt): - if not x or not y: - raise Errors.WafError('null source or target for %r' % self) - a, b = None, None - - if isinstance(x, str) and isinstance(y, str) and x == y: - a = self.path.find_node(x) - b = self.path.get_bld().make_node(y) - if not os.path.isfile(b.abspath()): - b.sig = None - b.parent.mkdir() - else: - if isinstance(x, str): - a = self.path.find_resource(x) - elif isinstance(x, Node.Node): - a = x - if isinstance(y, str): - b = self.path.find_or_declare(y) - elif isinstance(y, Node.Node): - b = y - - if not a: - raise Errors.WafError('could not find %r for %r' % (x, self)) - - has_constraints = False - tsk = self.create_task('binary_header', a, b) - for k in ('after', 'before', 'ext_in', 'ext_out'): - val = getattr(self, k, None) - if val: - has_constraints = True - setattr(tsk, k, val) - - tsk.before = [k for k in ('c', 'cxx') if k in Task.classes] - - self.source = [] diff --git a/platform/silk/boot/waftools/gitinfo.py b/platform/silk/boot/waftools/gitinfo.py deleted file mode 100644 index 8f8937aa17..0000000000 --- a/platform/silk/boot/waftools/gitinfo.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re - -import waflib.Context -import waflib.Logs - - -def get_git_revision(ctx): - try: - tag = ctx.cmd_and_log(['git', 'describe'], quiet=waflib.Context.BOTH).strip() - commit = ctx.cmd_and_log(['git', 'rev-parse', '--short', 'HEAD'], - quiet=waflib.Context.BOTH).strip() - timestamp = ctx.cmd_and_log(['git', 'log', '-1', '--format=%ct', 'HEAD'], - quiet=waflib.Context.BOTH).strip() - except Exception: - waflib.Logs.warn('get_git_version: unable to determine git revision') - tag, commit, timestamp = ("?", "?", "1") - # Validate that git tag follows the required form: - # See https://github.com/pebble/tintin/wiki/Firmware,-PRF-&-Bootloader-Versions - # Note: version_regex.groups() returns sequence ('0', '0', '0', 'suffix'): - version_regex = re.search("^v(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:(?:-)(.+))?$", tag) - if version_regex: - # Get version numbers from version_regex.groups() sequence and replace None values with 0 - # e.g. v2-beta11 => ('2', None, None, 'beta11') => ('2', '0', '0') - version = [x if x else '0' for x in version_regex.groups()[:3]] - else: - waflib.Logs.warn('get_git_revision: Invalid git tag! ' - 'Must follow this form: `v0[.0[.0]][-suffix]`') - version = ['0', '0', '0', 'unknown'] - return {'TAG': tag, - 'COMMIT': commit, - 'TIMESTAMP': timestamp, - 'MAJOR_VERSION': version[0], - 'MINOR_VERSION': version[1], - 'PATCH_VERSION': version[2]} diff --git a/platform/silk/boot/waftools/ldscript.py b/platform/silk/boot/waftools/ldscript.py deleted file mode 100644 index 9fecd7f336..0000000000 --- a/platform/silk/boot/waftools/ldscript.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Utils, Errors -from waflib.TaskGen import after, feature - - -@after('apply_link') -@feature('cprogram', 'cshlib') -def process_ldscript(self): - if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': - return - - node = self.path.find_resource(self.ldscript) - if not node: - raise Errors.WafError('could not find %r' % self.ldscript) - self.link_task.env.append_value('LINKFLAGS', '-T%s' % node.abspath()) - self.link_task.dep_nodes.append(node) diff --git a/platform/silk/boot/waftools/objcopy.py b/platform/silk/boot/waftools/objcopy.py deleted file mode 100644 index 97c4894dd7..0000000000 --- a/platform/silk/boot/waftools/objcopy.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Grygoriy Fuchedzhy 2010 - -""" -Support for converting linked targets to ihex, srec or binary files using -objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' -feature. The 'objcopy' feature uses the following attributes: - -objcopy_bfdname Target object format name (eg. ihex, srec, binary). -Defaults to ihex. -objcopy_target File name used for objcopy output. This defaults to the -target name with objcopy_bfdname as extension. -objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. -objcopy_flags Additional flags passed to objcopy. -""" - -from waflib.Utils import def_attrs -from waflib import Task -from waflib.TaskGen import feature, after_method - - -class objcopy(Task.Task): - run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' - color = 'CYAN' - - -@feature('objcopy') -@after_method('apply_link') -def objcopy(self): - def_attrs(self, - objcopy_bfdname='ihex', - objcopy_target=None, - objcopy_install_path="${PREFIX}/firmware", - objcopy_flags='') - - link_output = self.link_task.outputs[0] - if not self.objcopy_target: - self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name - task = self.create_task('objcopy', - src=link_output, - tgt=self.path.find_or_declare(self.objcopy_target)) - - task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) - try: - task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) - except AttributeError: - pass - - if self.objcopy_install_path: - self.bld.install_files(self.objcopy_install_path, - task.outputs[0], - env=task.env.derive()) - - -def configure(ctx): - objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) - - -def objcopy_simple(task, mode): - return task.exec_command('arm-none-eabi-objcopy -S -R .stack -R .priv_bss' - ' -R .bss -O %s "%s" "%s"' % - (mode, task.inputs[0].abspath(), task.outputs[0].abspath())) - - -def objcopy_simple_bin(task): - return objcopy_simple(task, 'binary') diff --git a/platform/silk/boot/wscript b/platform/silk/boot/wscript deleted file mode 100644 index c37b8e260d..0000000000 --- a/platform/silk/boot/wscript +++ /dev/null @@ -1,102 +0,0 @@ -import os -import sys -import waflib.Logs - -sys.path.append(os.path.abspath('waftools')) -import objcopy -import gitinfo - - -def options(opt): - opt.load('compiler_c') - opt.add_option('--board', action='store', default='silk', - help='Which board to build for ' - '(silk)', - choices=['silk']) - opt.add_option('--nowatchdog', action='store_true', - help='Do not enable watchdog timer or reset upon failure') - opt.add_option('--loglevel', type='int', default=0, - help='Set the logging verbosity [default: %default]') - opt.add_option('--displaydemo', action='store_true', - help='Make the bootloader infinitely loop through boot splash and error codes') - - -def configure(conf): - CPU_FLAGS = [ - '-mcpu=cortex-m4', '-mthumb', - '-mfpu=fpv4-sp-d16', '-mfloat-abi=softfp', - ] - OPT_FLAGS = [ '-Os' ,'-g' ] - C_FLAGS = [ - '-std=c11', '-ffunction-sections', - '-Wall', '-Wextra', '-Werror', '-Wpointer-arith', - '-Wno-unused-parameter', '-Wno-missing-field-initializers', - '-Wno-error=unused-function', '-Wno-error=unused-variable', - '-Wno-error=unused-parameter', '-Wno-error=unused-but-set-variable', - '-Wno-packed-bitfield-compat' - ] - - conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True) - conf.env.AS = conf.env.CC - for tool in 'ar objcopy'.split(): - conf.find_program('arm-none-eabi-' + tool, var=tool.upper(), - mandatory=True) - conf.env.BOARD = conf.options.board - conf.env.append_value('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS) - conf.env.append_value('DEFINES', [ - '_REENT_SMALL=1', - 'USE_STDPERIPH_DRIVER=1', - 'BOARD_{}=1'.format(conf.options.board.upper()) - ]) - - conf.env.append_unique('LINKFLAGS', - ['-Wl,--cref', '-Wl,--gc-sections', '-specs=nano.specs'] - + CPU_FLAGS + OPT_FLAGS) - - if conf.options.nowatchdog: - conf.env.append_value('DEFINES', 'NO_WATCHDOG') - - if conf.options.displaydemo: - conf.env.append_value('DEFINES', 'DISPLAY_DEMO_LOOP') - - conf.env.BOOTLOADER_LENGTH = '16384' - conf.env.append_value( - 'DEFINES', 'BOOTLOADER_LENGTH=' + conf.env.BOOTLOADER_LENGTH) - - conf.load('gcc gas objcopy ldscript') - conf.load('binary_header') - - conf.recurse('vendor') - - -def build(bld): - if bld.cmd == 'install': - raise Exception("install isn't a supported command. Did you mean flash?") - - linkflags = ['-Wl,-Map,silk_boot.map'] - - sources = ( - bld.path.ant_glob('src/*.S') + - bld.path.ant_glob('src/**/*.c')) - - bld(features='subst', - source='src/git_version.auto.h.in', - target='src/git_version.auto.h', - **gitinfo.get_git_revision(bld)) - - bld.recurse('vendor') - bld(features='subst', - source='src/stm32f_flash_boot.ld.in', - target='src/stm32f_flash_boot.ld') - bld(features='c asm cprogram objcopy', - source=sources, - includes='src', - target='tintin_boot.elf', - ldscript='src/stm32f_flash_boot.ld', - linkflags=linkflags, - objcopy_bfdname='ihex', - objcopy_target='tintin_boot.hex', - use='stm32_stdlib', - lib=['gcc']) - bld(rule=objcopy.objcopy_simple_bin, source='tintin_boot.elf', target='tintin_boot.bin') -# vim:filetype=python diff --git a/platform/snowy/Snowy_LP1K_framebuffer.fpga b/platform/snowy/Snowy_LP1K_framebuffer.fpga deleted file mode 100644 index 348bd4fba0..0000000000 Binary files a/platform/snowy/Snowy_LP1K_framebuffer.fpga and /dev/null differ diff --git a/platform/snowy/Spalding_LP1K_framebuffer.fpga b/platform/snowy/Spalding_LP1K_framebuffer.fpga deleted file mode 100644 index 28eb2bbda8..0000000000 Binary files a/platform/snowy/Spalding_LP1K_framebuffer.fpga and /dev/null differ diff --git a/platform/snowy/Spalding_UL1K_framebuffer.fpga b/platform/snowy/Spalding_UL1K_framebuffer.fpga deleted file mode 100644 index 6d2235d146..0000000000 Binary files a/platform/snowy/Spalding_UL1K_framebuffer.fpga and /dev/null differ diff --git a/platform/snowy/boot/.gitignore b/platform/snowy/boot/.gitignore deleted file mode 100644 index 6c2c3a248c..0000000000 --- a/platform/snowy/boot/.gitignore +++ /dev/null @@ -1,50 +0,0 @@ -build -xcode - -.ycm* - -.*project -*.*project -*.*workspace -.settings - -*.bin -*.bin.orig -*.map -build/*.elf -*.o - -cscope.* -TAGS - -*.swp -*.swo -*.swn - -.waf* -.lock* - -*.pyc -*.gch - -qemu_serial.txt -uart*.log - -serial_dump.txt - -a.out -tools/font/ttf -roundrect.h -log.txt -openocd.log -.DS_Store -tools/bitmaps/*.h -tools/bitmaps/*.pbi - -dist -compile_commands.json -*.auto -loghash_dict.pickle -analyze_mcu_flash_usage_treemap.jsonp -**/.idea -**/CMakeLists.txt diff --git a/platform/snowy/boot/boot-bin-update.sh b/platform/snowy/boot/boot-bin-update.sh deleted file mode 100755 index 791188c3e1..0000000000 --- a/platform/snowy/boot/boot-bin-update.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -o errexit -o xtrace - -cd "${0%/*}" - -reloutdir="../../../bin/boot" -OUTDIR=`cd "${reloutdir}"; pwd` -BOARDS=(snowy_bb snowy_dvt snowy_evt2) -# Use commit timestamp, same as the one compiled into the bootloader binary -VERSION=`git log -1 --format=%ct HEAD` - -# Clear out old versions of the bootloader binaries -for board in ${BOARDS[*]}; do - git rm ${OUTDIR}/{nowatchdog_,}boot_${board}@*.{hex,elf} || true -done - -# Build all bootloader variants and copy them into OUTDIR -build_and_copy () { - local variant="$1"; - shift; - - for board in ${BOARDS[*]}; do - pypy ./waf configure --board=${board} $@ build --progress - for ext in hex elf; do - mv build/snowy_boot.${ext} \ - "${OUTDIR}/${variant}boot_${board}@${VERSION}.${ext}" - done - done -} - -build_and_copy "" -build_and_copy nowatchdog_ --nowatchdog diff --git a/platform/snowy/boot/desym.sh b/platform/snowy/boot/desym.sh deleted file mode 100755 index c8462022b2..0000000000 --- a/platform/snowy/boot/desym.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -arm-none-eabi-addr2line --exe=build/snowy_boot.elf $1 diff --git a/platform/snowy/boot/flash b/platform/snowy/boot/flash deleted file mode 100755 index 2642b4d15e..0000000000 --- a/platform/snowy/boot/flash +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -o errexit - -SCRIPTDIR="${0%/*}" -IMGFILE="${SCRIPTDIR}/build/snowy_boot.hex" - -if [ ! -f "${IMGFILE}" ]; then - echo "Cannot find bootloader binary at '${IMGFILE}'." - echo "Try running './waf build'" - exit 1 -fi - -OPENOCD_SCRIPT=" -init -reset halt -flash write_image erase \"${IMGFILE}\" -reset -shutdown -" - -openocd -f openocd.cfg -c "${OPENOCD_SCRIPT}" - -# vim:filetype=sh diff --git a/platform/snowy/boot/gdb b/platform/snowy/boot/gdb deleted file mode 100755 index 2f402872c2..0000000000 --- a/platform/snowy/boot/gdb +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -TARGET="target extended-remote | openocd -f openocd.cfg \ - -c \"gdb_port pipe; log_output openocd.log; init; reset halt;\"" - -arm-none-eabi-gdb build/snowy_boot.elf \ - -ex "${TARGET}" \ - -ex "break boot_main" \ - -ex "continue" - -# vim:filetype=sh diff --git a/platform/snowy/boot/openocd_bb2_ftdi.cfg b/platform/snowy/boot/openocd_bb2_ftdi.cfg deleted file mode 100644 index 06b30eaef9..0000000000 --- a/platform/snowy/boot/openocd_bb2_ftdi.cfg +++ /dev/null @@ -1,11 +0,0 @@ -interface ftdi -#ftdi_device_desc "Dual RS232-HS" -ftdi_vid_pid 0x0403 0x6010 0x0403 0x6011 - -# output value, direction (1 for output, 0 for input) -ftdi_layout_init 0x1848 0x185b -ftdi_layout_signal nTRST -data 0x0010 -oe 0x0010 -ftdi_layout_signal nSRST -data 0x0040 -oe 0x0040 - -# Red + Green LED (inverted output: low is on) -ftdi_layout_signal LED -ndata 0x1800 -oe 0x1800 diff --git a/platform/snowy/boot/run-tests.sh b/platform/snowy/boot/run-tests.sh deleted file mode 100755 index 347447adde..0000000000 --- a/platform/snowy/boot/run-tests.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit - -cd "${0%/*}" - -CLAR_DIR=`cd ../../../tools/clar; pwd` - -clar () { - local test_name=${1:?}; shift; - local test_suite=${1:?}; shift; - local test_dir="build/test/${test_name}" - - mkdir -p "${test_dir}" - - python "${CLAR_DIR}/clar.py" \ - --file="${test_suite}" \ - --clar-path="${CLAR_DIR}" \ - "${test_dir}" - - gcc -o "${test_dir}/do_test" \ - -I"${test_dir}" -Isrc \ - -Ivendor/STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/Libraries/CMSIS/Include \ - -Ivendor/STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/Libraries/CMSIS/Device/ST/STM32F4xx/Include \ - -Ivendor/STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/Libraries/STM32F4xx_StdPeriph_Driver/inc \ - -DMICRO_FAMILY_STM32F4 -DSTM32F429_439xx \ - -ffunction-sections \ - -Wl,-dead_strip \ - "${test_dir}/clar_main.c" "${test_suite}" $@ - # If running on a platform with GNU ld, - # replace -Wl,-dead_strip with -Wl,--gc-sections - - echo "Running test ${test_suite}..." - "${test_dir}/do_test" -} - -clar system_flash_algo test/test_system_flash.c \ - src/drivers/stm32_common/system_flash.c diff --git a/platform/snowy/boot/spalding-bin-update.sh b/platform/snowy/boot/spalding-bin-update.sh deleted file mode 100755 index 0d146b5418..0000000000 --- a/platform/snowy/boot/spalding-bin-update.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -o errexit -o xtrace - -cd "${0%/*}" - -reloutdir="../../../bin/boot" -OUTDIR=`cd "${reloutdir}"; pwd` -# Use commit timestamp, same as the one compiled into the bootloader binary -VERSION=`git log -1 --format=%ct HEAD` - -# Clear out old versions of the bootloader binaries -git rm ${OUTDIR}/{nowatchdog_,}boot_spalding@*.{hex,elf} || true - -# Build all bootloader variants and copy them into OUTDIR -build_and_copy () { - local variant="$1"; - shift; - - pypy ./waf configure --board=spalding $@ build --progress - for ext in hex elf; do - mv build/snowy_boot.${ext} \ - "${OUTDIR}/${variant}boot_spalding@${VERSION}.${ext}" - done -} - -build_and_copy "" -build_and_copy nowatchdog_ --nowatchdog diff --git a/platform/snowy/boot/src/board/board.h b/platform/snowy/boot/src/board/board.h deleted file mode 100644 index d5b98445ee..0000000000 --- a/platform/snowy/boot/src/board/board.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button_id.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_gpio.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_gpio.h" -#endif - -#include -#include - -#define GPIO_Port_NULL ((GPIO_TypeDef *) 0) -#define GPIO_Pin_NULL ((uint16_t)0x0000) - - -typedef struct { - //! One of EXTI_PortSourceGPIOX - uint8_t exti_port_source; - - //! Value between 0-15 - uint8_t exti_line; -} ExtiConfig; - -typedef struct { - const char* const name; ///< Name for debugging purposes. - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - ExtiConfig exti; - GPIOPuPd_TypeDef pull; -} ButtonConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} ButtonComConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} InputConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint16_t gpio_pin; ///< One of GPIO_Pin_* - const uint8_t adc_channel; ///< One of ADC_Channel_* -} ADCInputConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - bool active_high; ///< Pin is active high or active low -} OutputConfig; - -//! Alternate function pin configuration -//! Used to configure a pin for use by a peripheral -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - const uint16_t gpio_pin_source; ///< One of GPIO_PinSourceX. - const uint8_t gpio_af; ///< One of GPIO_AF_X -} AfConfig; - -typedef struct { - int i2c_address; - int axes_offsets[3]; - bool axes_inverts[3]; -} AccelConfig; - -typedef struct { - int i2c_address; - int axes_offsets[3]; - bool axes_inverts[3]; -} MagConfig; - -typedef struct { - I2C_TypeDef *const i2c; - AfConfig i2c_scl; ///< Alternate Function configuration for SCL pin - AfConfig i2c_sda; ///< Alternate Function configuration for SDA pin - uint32_t clock_ctrl; ///< Peripheral clock control flag - uint32_t clock_speed; ///< Bus clock speed - uint32_t duty_cycle; ///< Bus clock duty cycle in fast mode - const uint8_t ev_irq_channel; ///< I2C Event interrupt (One of X_IRQn). For example, I2C1_EV_IRQn. - const uint8_t er_irq_channel; ///< I2C Error interrupt (One of X_IRQn). For example, I2C1_ER_IRQn. - void (* const rail_cfg_fn)(void); //! Configure function for pins on this rail. - void (* const rail_ctl_fn)(bool enabled); //! Control function for this rail. -} I2cBusConfig; - -typedef enum I2cDevice { - I2C_DEVICE_LIS3DH = 0, - I2C_DEVICE_MAG3110, - I2C_DEVICE_MFI, - I2C_DEVICE_LED_CONTROLLER, - I2C_DEVICE_MAX14690, -} I2cDevice; - -typedef struct { - AfConfig i2s_ck; - AfConfig i2s_sd; - DMA_Stream_TypeDef *dma_stream; - uint32_t dma_channel; - uint32_t dma_channel_irq; - uint32_t dma_clock_ctrl; - SPI_TypeDef *spi; - uint32_t spi_clock_ctrl; - - //! Pin we use to control power to the microphone. Only used on certain boards. - OutputConfig mic_gpio_power; -} MicConfig; - -typedef enum { - OptionNotPresent = 0, // FIXME - OptionActiveLowOpenDrain, - OptionActiveHigh -} PowerCtl5VOptions; - -typedef enum { - BacklightPinNoPwm = 0, - BacklightPinPwm, - BacklightIssiI2C -} BacklightOptions; - -typedef enum { - VibePinNoPwm = 0, - VibePinPwm, -} VibeOptions; - -typedef struct { - TIM_TypeDef* const peripheral; ///< A TIMx peripheral - const uint32_t config_clock; ///< One of RCC_APB1Periph_TIMx. For example, RCC_APB1Periph_TIM3. - void (* const init)(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); ///< One of TIM_OCxInit - void (* const preload)(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); ///< One of TIM_OCxPreloadConfig -} TimerConfig; - -typedef enum { - CC2564A = 0, - CC2564B, -} BluetoothController; - -typedef struct { - // I2C Configuration - ///////////////////////////////////////////////////////////////////////////// - const I2cBusConfig *i2c_bus_configs; - const uint8_t i2c_bus_count; - const uint8_t *i2c_device_map; - const uint8_t i2c_device_count; - - // Audio Configuration - ///////////////////////////////////////////////////////////////////////////// - const bool has_mic; - const MicConfig mic_config; - - // Ambient Light Configuration - ///////////////////////////////////////////////////////////////////////////// - const bool has_ambient_light_sensor; - const uint32_t ambient_light_dark_threshold; - const OutputConfig photo_en; - const ADCInputConfig light_level; - - // Debug Serial Configuration - ///////////////////////////////////////////////////////////////////////////// - const ExtiConfig dbgserial_int; - - // Accessory Configuration - ///////////////////////////////////////////////////////////////////////////// - //! Enable power supply to the accessory connector. - const OutputConfig accessory_power_en; - const AfConfig accessory_rxtx_afcfg; - USART_TypeDef* const accessory_uart; - const ExtiConfig accessory_exti; - - // Bluetooth Configuration - ///////////////////////////////////////////////////////////////////////////// - const BluetoothController bt_controller; - const OutputConfig bt_shutdown; - const OutputConfig bt_cts_int; - const ExtiConfig bt_cts_exti; - - const OutputConfig mfi_reset_pin; - - // Display Configuration - ///////////////////////////////////////////////////////////////////////////// - const OutputConfig lcd_com; //!< This needs to be pulsed regularly to keep the sharp display fresh. - - const ExtiConfig cdone_int; - const ExtiConfig intn_int; - - //! Controls power to the sharp display - const PowerCtl5VOptions power_5v0_options; - const OutputConfig power_ctl_5v0; - - const BacklightOptions backlight_options; - const OutputConfig backlight_ctl; - const TimerConfig backlight_timer; - const AfConfig backlight_afcfg; - -} BoardConfig; - -// Button Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ButtonConfig buttons[NUM_BUTTONS]; - const ButtonComConfig button_com; -} BoardConfigButton; - -// Power Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ExtiConfig pmic_int; - - //! Analog voltage of the battery read through an ADC. - const ADCInputConfig battery_vmon; - //! Tells us if the USB cable plugged in. - const InputConfig vusb_stat; - const ExtiConfig vusb_exti; - //! Tells us whether the charger thinks we're charging or not. - const InputConfig chg_stat; - //! Tell the charger to use 2x current to charge faster (MFG only). - const OutputConfig chg_fast; - //! Enable the charger. We may want to disable this in MFG, normally it's always on. - const OutputConfig chg_en; - - //! Interrupt that fires when the USB cable is plugged in - const bool has_vusb_interrupt; - - const bool wake_on_usb_power; - - const int charging_status_led_voltage_compensation; - - //! Percentage for watch only mode - const uint8_t low_power_threshold; -} BoardConfigPower; - -typedef struct { - const AccelConfig accel_config; - const ExtiConfig accel_ints[2]; -} BoardConfigAccel; - -typedef struct { - const MagConfig mag_config; - const ExtiConfig mag_int; -} BoardConfigMag; - -typedef struct { - const VibeOptions vibe_options; - const OutputConfig vibe_ctl; - const OutputConfig vibe_pwm; - const TimerConfig vibe_timer; - const AfConfig vibe_afcfg; -} BoardConfigVibe; - - -#include "board_definitions.h" diff --git a/platform/snowy/boot/src/board/board_definitions.h b/platform/snowy/boot/src/board/board_definitions.h deleted file mode 100644 index ebc7e8baa6..0000000000 --- a/platform/snowy/boot/src/board/board_definitions.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if BOARD_SNOWY_BB -#include "board_snowy_bb.h" // prototypes for Snowy bigboard -#elif BOARD_SNOWY_EVT -#include "board_snowy_evt.h" // prototypes for Snowy EVT -#elif BOARD_SNOWY_EVT2 -#include "board_snowy_evt2.h" // prototypes for Snowy EVT2 -#elif BOARD_SPALDING -#include "board_snowy_evt2.h" // Close enough -#else -#error "Unknown board definition" -#endif diff --git a/platform/snowy/boot/src/board/board_snowy_bb.h b/platform/snowy/boot/src/board/board_snowy_bb.h deleted file mode 100644 index 190c9c8d1c..0000000000 --- a/platform/snowy/boot/src/board/board_snowy_bb.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/misc.h" - -#define USE_PARALLEL_FLASH -#define HAS_ACCESSORY_CONNECTOR -#define BOARD_HAS_PMIC - -#define BOARD_I2C_BUS_COUNT (ARRAY_LENGTH(SNOWY_BB_I2C_BUS_CONFIGS)) - -extern void snowy_i2c_rail_1_ctl_fn(bool enable); - -static const I2cBusConfig SNOWY_BB_I2C_BUS_CONFIGS[] = { - // labelled 1V8_I2C on the schematic - [0] = { - .i2c = I2C1, - .i2c_scl = { GPIOB, GPIO_Pin_6, GPIO_PinSource6, GPIO_AF_I2C1 }, - .i2c_sda = { GPIOB, GPIO_Pin_9, GPIO_PinSource9, GPIO_AF_I2C1 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_16_9, - .clock_ctrl = RCC_APB1Periph_I2C1, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, - }, - // labelled 2V5_I2C on the schematic - [1] = { - .i2c = I2C2, - .i2c_scl = { GPIOF, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF_I2C2 }, - .i2c_sda = { GPIOF, GPIO_Pin_0, GPIO_PinSource0, GPIO_AF_I2C2 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, - .rail_ctl_fn = snowy_i2c_rail_1_ctl_fn - } -}; - -static const uint8_t SNOWY_BB_I2C_DEVICE_MAP[] = { - [I2C_DEVICE_LIS3DH] = 0, - [I2C_DEVICE_MAG3110] = 1, - [I2C_DEVICE_MFI] = 1, - [I2C_DEVICE_MAX14690] = 0 -}; - -static const BoardConfig BOARD_CONFIG = { - .i2c_bus_configs = SNOWY_BB_I2C_BUS_CONFIGS, - .i2c_bus_count = BOARD_I2C_BUS_COUNT, - .i2c_device_map = SNOWY_BB_I2C_DEVICE_MAP, - .i2c_device_count = ARRAY_LENGTH(SNOWY_BB_I2C_DEVICE_MAP), - - .has_ambient_light_sensor = true, - .ambient_light_dark_threshold = 3000, - .photo_en = { GPIOA, GPIO_Pin_3, true }, - .light_level = { GPIOA, GPIO_Pin_2, ADC_Channel_2 }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 12 }, - - .accessory_power_en = { GPIOF, GPIO_Pin_13, true }, - .accessory_rxtx_afcfg = { GPIOE, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF_UART8 }, - .accessory_uart = UART8, - .accessory_exti = { EXTI_PortSourceGPIOE, 0 }, - - .bt_controller = CC2564A, - .bt_shutdown = { GPIOC, GPIO_Pin_8, false}, - .bt_cts_int = { GPIOA, GPIO_Pin_11, false}, - .bt_cts_exti = { EXTI_PortSourceGPIOA, 11 }, - - .mfi_reset_pin = { GPIOF, GPIO_Pin_11 }, - - // Only used with Sharp displays - .lcd_com = { 0 }, - - .cdone_int = { EXTI_PortSourceGPIOG, 9 }, - .intn_int = { EXTI_PortSourceGPIOG, 10 }, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .backlight_options = BacklightPinPwm, - .backlight_ctl = { GPIOB, GPIO_Pin_14, true }, - .backlight_timer = { - .peripheral = TIM12, - .config_clock = RCC_APB1Periph_TIM12, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .backlight_afcfg = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF_TIM12 }, - - .has_mic = true, - .mic_config = { - .i2s_ck = { GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_SPI2 }, - .i2s_sd = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .dma_stream = DMA1_Stream3, - .dma_channel = DMA_Channel_0, - .dma_channel_irq = DMA1_Stream3_IRQn, - .dma_clock_ctrl = RCC_AHB1Periph_DMA1, - .spi = SPI2, - .spi_clock_ctrl = RCC_APB1Periph_SPI2 - }, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_SELECT] = { "Select", GPIOG, GPIO_Pin_1, { EXTI_PortSourceGPIOG, 1 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_DOWN] = { "Down", GPIOG, GPIO_Pin_2, { EXTI_PortSourceGPIOG, 2 }, GPIO_PuPd_NOPULL }, - }, - - .button_com = { 0 }, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOG, 7 }, - - .battery_vmon = { GPIOA, GPIO_Pin_1, ADC_Channel_1 }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 5, -}; - -static const BoardConfigVibe BOARD_CONFIG_VIBE = { - .vibe_options = VibePinPwm, - .vibe_ctl = { GPIOF, GPIO_Pin_4, true }, - .vibe_pwm = { GPIOB, GPIO_Pin_8, true }, - .vibe_timer = { - .peripheral = TIM10, - .config_clock = RCC_APB2Periph_TIM10, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .vibe_afcfg = { GPIOB, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF_TIM10 }, -}; diff --git a/platform/snowy/boot/src/board/board_snowy_evt.h b/platform/snowy/boot/src/board/board_snowy_evt.h deleted file mode 100644 index 0201991262..0000000000 --- a/platform/snowy/boot/src/board/board_snowy_evt.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/misc.h" -#include "drivers/accel.h" -#include "drivers/imu/bmi160/bmi160.h" - -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_exti.h" -#include "stm32f4xx_syscfg.h" -#include "stm32f4xx_i2c.h" - -#define USE_PARALLEL_FLASH -#define HAS_ACCESSORY_CONNECTOR -#define BOARD_HAS_PMIC - -#define BOARD_I2C_BUS_COUNT (ARRAY_LENGTH(SNOWY_EVT_I2C_BUS_CONFIGS)) - -extern void snowy_i2c_rail_1_ctl_fn(bool enable); - -static const I2cBusConfig SNOWY_EVT_I2C_BUS_CONFIGS[] = { - // Listed as I2C_PMIC on the schematic, runs at 1.8V - [0] = { - .i2c = I2C1, - .i2c_scl = { GPIOB, GPIO_Pin_6, GPIO_PinSource6, GPIO_AF_I2C1 }, - .i2c_sda = { GPIOB, GPIO_Pin_9, GPIO_PinSource9, GPIO_AF_I2C1 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_16_9, - .clock_ctrl = RCC_APB1Periph_I2C1, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, - }, - // Listed as I2C_MFI on the schematic, runs at 1.8V - [1] = { - .i2c = I2C2, - .i2c_scl = { GPIOF, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF_I2C2 }, - .i2c_sda = { GPIOF, GPIO_Pin_0, GPIO_PinSource0, GPIO_AF_I2C2 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, - .rail_ctl_fn = snowy_i2c_rail_1_ctl_fn - } -}; - -static const uint8_t SNOWY_EVT_I2C_DEVICE_MAP[] = { - [I2C_DEVICE_MFI] = 1, - [I2C_DEVICE_MAX14690] = 0 -}; - -static const BoardConfig BOARD_CONFIG = { - .i2c_bus_configs = SNOWY_EVT_I2C_BUS_CONFIGS, - .i2c_bus_count = BOARD_I2C_BUS_COUNT, - .i2c_device_map = SNOWY_EVT_I2C_DEVICE_MAP, - .i2c_device_count = ARRAY_LENGTH(SNOWY_EVT_I2C_DEVICE_MAP), - - .has_ambient_light_sensor = true, - .ambient_light_dark_threshold = 3000, - .photo_en = { GPIOA, GPIO_Pin_3, true }, - .light_level = { GPIOA, GPIO_Pin_2, ADC_Channel_2 }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 12 }, - - .accessory_power_en = { GPIOF, GPIO_Pin_13, true }, - .accessory_rxtx_afcfg = { GPIOE, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF_UART8 }, - .accessory_uart = UART8, - .accessory_exti = { EXTI_PortSourceGPIOE, 0 }, - - .bt_controller = CC2564B, - .bt_shutdown = { GPIOC, GPIO_Pin_8, false}, - .bt_cts_int = { GPIOA, GPIO_Pin_11, false}, - .bt_cts_exti = { EXTI_PortSourceGPIOA, 11 }, - - .mfi_reset_pin = { GPIOF, GPIO_Pin_11 }, - - // Only used with Sharp displays - .lcd_com = { 0 }, - - .cdone_int = { EXTI_PortSourceGPIOG, 9 }, - .intn_int = { EXTI_PortSourceGPIOG, 10 }, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .backlight_options = BacklightPinPwm, - .backlight_ctl = { GPIOB, GPIO_Pin_14, true }, - .backlight_timer = { - .peripheral = TIM12, - .config_clock = RCC_APB1Periph_TIM12, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .backlight_afcfg = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF_TIM12 }, - - .has_mic = true, - .mic_config = { - .i2s_ck = { GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_SPI2 }, - .i2s_sd = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .dma_stream = DMA1_Stream3, - .dma_channel = DMA_Channel_0, - .dma_channel_irq = DMA1_Stream3_IRQn, - .dma_clock_ctrl = RCC_AHB1Periph_DMA1, - .spi = SPI2, - .spi_clock_ctrl = RCC_APB1Periph_SPI2, - - .mic_gpio_power = { GPIOF, GPIO_Pin_5, true } - }, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOG, GPIO_Pin_1, { EXTI_PortSourceGPIOG, 1 }, GPIO_PuPd_UP }, - [BUTTON_ID_UP] = { "Up", GPIOG, GPIO_Pin_2, { EXTI_PortSourceGPIOG, 2 }, GPIO_PuPd_UP }, - [BUTTON_ID_SELECT] = { "Select", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_UP }, - [BUTTON_ID_DOWN] = { "Down", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_NOPULL }, - }, - - .button_com = { 0 }, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOG, 7 }, - - .battery_vmon = { GPIOA, GPIO_Pin_1, ADC_Channel_1 }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 5, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - // FIXME: We no longer use i2c for our accel, this will need work. - .accel_config = { - .i2c_address = 0x32, - - .axes_offsets[ACCEL_AXIS_X] = 1, - .axes_offsets[ACCEL_AXIS_Y] = 0, - .axes_offsets[ACCEL_AXIS_Z] = 2, - .axes_inverts[ACCEL_AXIS_X] = true, - .axes_inverts[ACCEL_AXIS_Y] = false, - .axes_inverts[ACCEL_AXIS_Z] = true, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOG, 5 }, - [1] = { EXTI_PortSourceGPIOG, 6 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - // FIXME: We need to talk to the compass through the accel now as it's hanging of the accel's i2c bus. - // This will need work. - .mag_config = { - .i2c_address = 0x0e << 1, - - // FIXME: ? - .axes_offsets[ACCEL_AXIS_X] = 1, - .axes_offsets[ACCEL_AXIS_Y] = 0, - .axes_offsets[ACCEL_AXIS_Z] = 2, - .axes_inverts[ACCEL_AXIS_X] = false, - .axes_inverts[ACCEL_AXIS_Y] = true, - .axes_inverts[ACCEL_AXIS_Z] = true, - }, - .mag_int = { EXTI_PortSourceGPIOF, 14 }, -}; - -static const BoardConfigVibe BOARD_CONFIG_VIBE = { - .vibe_options = VibePinPwm, - .vibe_ctl = { GPIOF, GPIO_Pin_4, true }, - .vibe_pwm = { GPIOB, GPIO_Pin_8, true }, - .vibe_timer = { - .peripheral = TIM10, - .config_clock = RCC_APB2Periph_TIM10, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .vibe_afcfg = { GPIOB, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF_TIM10 }, -}; diff --git a/platform/snowy/boot/src/board/board_snowy_evt2.h b/platform/snowy/boot/src/board/board_snowy_evt2.h deleted file mode 100644 index f11df6dcc6..0000000000 --- a/platform/snowy/boot/src/board/board_snowy_evt2.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/misc.h" - -#define USE_PARALLEL_FLASH -#define HAS_ACCESSORY_CONNECTOR -#define BOARD_HAS_PMIC - -#define BOARD_I2C_BUS_COUNT (ARRAY_LENGTH(SNOWY_EVT2_I2C_BUS_CONFIGS)) - -extern void snowy_i2c_rail_1_ctl_fn(bool enable); - -static const I2cBusConfig SNOWY_EVT2_I2C_BUS_CONFIGS[] = { - // Listed as I2C_PMIC_MAG on the schematic, runs at 1.8V - [0] = { - .i2c = I2C1, - .i2c_scl = { GPIOB, GPIO_Pin_6, GPIO_PinSource6, GPIO_AF_I2C1 }, - .i2c_sda = { GPIOB, GPIO_Pin_9, GPIO_PinSource9, GPIO_AF_I2C1 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_16_9, - .clock_ctrl = RCC_APB1Periph_I2C1, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, - }, - // Listed as I2C_MFI on the schematic, runs at 1.8V - [1] = { - .i2c = I2C2, - .i2c_scl = { GPIOF, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF_I2C2 }, - .i2c_sda = { GPIOF, GPIO_Pin_0, GPIO_PinSource0, GPIO_AF_I2C2 }, - .clock_speed = 400000, - .duty_cycle = I2C_DutyCycle_2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, - .rail_ctl_fn = snowy_i2c_rail_1_ctl_fn - } -}; - -static const uint8_t SNOWY_EVT2_I2C_DEVICE_MAP[] = { - [I2C_DEVICE_MAG3110] = 0, - [I2C_DEVICE_MFI] = 1, - [I2C_DEVICE_MAX14690] = 0 -}; - -static const BoardConfig BOARD_CONFIG = { - .i2c_bus_configs = SNOWY_EVT2_I2C_BUS_CONFIGS, - .i2c_bus_count = BOARD_I2C_BUS_COUNT, - .i2c_device_map = SNOWY_EVT2_I2C_DEVICE_MAP, - .i2c_device_count = ARRAY_LENGTH(SNOWY_EVT2_I2C_DEVICE_MAP), - - .has_ambient_light_sensor = true, - .ambient_light_dark_threshold = 3000, - .photo_en = { GPIOA, GPIO_Pin_3, true }, - .light_level = { GPIOA, GPIO_Pin_2, ADC_Channel_2 }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 12 }, - - .accessory_power_en = { GPIOF, GPIO_Pin_13, true }, - .accessory_rxtx_afcfg = { GPIOE, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF_UART8 }, - .accessory_uart = UART8, - .accessory_exti = { EXTI_PortSourceGPIOE, 0 }, - - .bt_controller = CC2564B, - .bt_shutdown = { GPIOB, GPIO_Pin_12, false}, - .bt_cts_int = { GPIOA, GPIO_Pin_11, false}, - .bt_cts_exti = { EXTI_PortSourceGPIOA, 11 }, - - // Only used with Sharp displays - .lcd_com = { 0 }, - - .cdone_int = { EXTI_PortSourceGPIOG, 9 }, - .intn_int = { EXTI_PortSourceGPIOG, 10 }, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .backlight_options = BacklightPinPwm, - .backlight_ctl = { GPIOB, GPIO_Pin_14, true }, - .backlight_timer = { - .peripheral = TIM12, - .config_clock = RCC_APB1Periph_TIM12, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .backlight_afcfg = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF_TIM12 }, - - .has_mic = true, - .mic_config = { - .i2s_ck = { GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_SPI2 }, - .i2s_sd = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .dma_stream = DMA1_Stream3, - .dma_channel = DMA_Channel_0, - .dma_channel_irq = DMA1_Stream3_IRQn, - .dma_clock_ctrl = RCC_AHB1Periph_DMA1, - .spi = SPI2, - .spi_clock_ctrl = RCC_APB1Periph_SPI2, - - .mic_gpio_power = { GPIOF, GPIO_Pin_5, true } - }, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_UP }, - [BUTTON_ID_SELECT] = { "Select", GPIOG, GPIO_Pin_1, { EXTI_PortSourceGPIOG, 1 }, GPIO_PuPd_UP }, - [BUTTON_ID_DOWN] = { "Down", GPIOG, GPIO_Pin_2, { EXTI_PortSourceGPIOG, 2 }, GPIO_PuPd_UP }, - }, - - .button_com = { 0 }, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOG, 7 }, - - .battery_vmon = { GPIOA, GPIO_Pin_1, ADC_Channel_1 }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 5, -}; - -static const BoardConfigVibe BOARD_CONFIG_VIBE = { - .vibe_options = VibePinPwm, - .vibe_ctl = { GPIOF, GPIO_Pin_4, true }, - .vibe_pwm = { GPIOB, GPIO_Pin_8, true }, - .vibe_timer = { - .peripheral = TIM10, - .config_clock = RCC_APB2Periph_TIM10, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .vibe_afcfg = { GPIOB, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF_TIM10 }, -}; diff --git a/platform/snowy/boot/src/boot_tests.c b/platform/snowy/boot/src/boot_tests.c deleted file mode 100644 index 5f3bc2857d..0000000000 --- a/platform/snowy/boot/src/boot_tests.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "boot_tests.h" - -#include "board/board.h" -#include "drivers/button.h" -#include "drivers/flash.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/rtc_registers.h" -#include "util/misc.h" - -#include "stm32f4xx.h" - -#include -#include -#include - -static const int STUCK_BUTTON_THRESHOLD = 5; - -bool is_button_stuck(void) { - // We store how many times each button has been pressed on previous boots in this - // rtc backup register. Every time when we boot without that button pressed that - // counter gets cleared. If the byte reaches 5, return a failure. - - uint32_t button_counter_register = RTC_ReadBackupRegister(STUCK_BUTTON_REGISTER); - uint8_t* button_counter = (uint8_t*) (&button_counter_register); - bool result = false; - - for (int button_id = 0; button_id < NUM_BUTTONS; button_id++) { - if (!button_is_pressed(button_id)) { - button_counter[button_id] = 0; - continue; - } - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_putstr("Stuck button register is invalid, clearing."); - char buffer[32]; - itoa(button_counter_register, buffer, sizeof(buffer)); - dbgserial_putstr(buffer); - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, 0); - return false; - } - - button_counter[button_id] += 1; - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - PBL_LOG(LOG_LEVEL_ERROR, "Button id %u is stuck!", button_id); - result = true; - } - } - - if (button_counter_register != 0) { - dbgserial_putstr("Button is pushed at boot"); - char buffer[32]; - itoa(button_counter_register, buffer, sizeof(buffer)); - dbgserial_putstr(buffer); - } - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, button_counter_register); - return result; -} - -bool is_flash_broken(void) { - return !flash_sanity_check(); -} diff --git a/platform/snowy/boot/src/boot_tests.h b/platform/snowy/boot/src/boot_tests.h deleted file mode 100644 index 1f95ffeeb7..0000000000 --- a/platform/snowy/boot/src/boot_tests.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool is_button_stuck(void); -bool is_flash_broken(void); diff --git a/platform/snowy/boot/src/drivers/button.h b/platform/snowy/boot/src/drivers/button.h deleted file mode 100644 index 27fbefb2a5..0000000000 --- a/platform/snowy/boot/src/drivers/button.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "button_id.h" - -#include -#include - -void button_init(void); - -bool button_is_pressed(ButtonId id); -uint8_t button_get_state_bits(void); - -void button_interrupt_handler(void* button_id); - -bool button_selftest(void); diff --git a/platform/snowy/boot/src/drivers/button_id.h b/platform/snowy/boot/src/drivers/button_id.h deleted file mode 100644 index 02aa1c75d8..0000000000 --- a/platform/snowy/boot/src/drivers/button_id.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @addtogroup UI -//! @{ -//! @addtogroup Clicks -//! \brief Dealing with button input -//! @{ - -//! Button ID values -//! @see \ref click_recognizer_get_button_id() -typedef enum { - //! Back button - BUTTON_ID_BACK = 0, - //! Up button - BUTTON_ID_UP, - //! Select (middle) button - BUTTON_ID_SELECT, - //! Down button - BUTTON_ID_DOWN, - //! Total number of buttons - NUM_BUTTONS -} ButtonId; - -//! @} // end addtogroup Clicks -//! @} // end addtogroup UI diff --git a/platform/snowy/boot/src/drivers/crc.h b/platform/snowy/boot/src/drivers/crc.h deleted file mode 100644 index 55e5dea4d9..0000000000 --- a/platform/snowy/boot/src/drivers/crc.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void crc_init(void); - -/* - * calculate the CRC32 for a stream of bytes. - * NOTE: not safe to call from ISR - */ -uint32_t crc_calculate_bytes(const uint8_t* data, unsigned int data_length); - -/* - * calculate the CRC32 for a stream of bytes from flash - * NOTE: not safe to call from ISR - */ -uint32_t crc_calculate_flash(uint32_t address, unsigned int num_bytes); - -/* - * calculate a 8-bit CRC of a given byte sequence. Note that this is not using - * the standard CRC-8 polynomial, because the standard polynomial isn't very - * good. - */ -uint8_t crc8_calculate_bytes(const uint8_t *data, unsigned int data_length); - -void crc_calculate_incremental_start(void); - -void crc_calculate_incremental_stop(void); diff --git a/platform/snowy/boot/src/drivers/dbgserial.h b/platform/snowy/boot/src/drivers/dbgserial.h deleted file mode 100644 index d729da77f9..0000000000 --- a/platform/snowy/boot/src/drivers/dbgserial.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void dbgserial_init(void); - -void dbgserial_putchar(uint8_t c); - -//! Version of dbgserial_putchar that may return before the character is finished writing. -//! Use if you don't need a guarantee that your character will be written. -void dbgserial_putchar_lazy(uint8_t c); - -void dbgserial_putstr(const char* str); - -//! Like dbgserial_putstr, but without a terminating newline -void dbgserial_print(const char* str); - -void dbgserial_print_hex(uint32_t value); - -void dbgserial_putstr_fmt(char* buffer, unsigned int buffer_size, const char* fmt, ...) - __attribute__((format(printf, 3, 4))); diff --git a/platform/snowy/boot/src/drivers/display.h b/platform/snowy/boot/src/drivers/display.h deleted file mode 100644 index bd0028f337..0000000000 --- a/platform/snowy/boot/src/drivers/display.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -void display_init(void); - -void display_boot_splash(void); - -void display_error_code(uint32_t); - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void); - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator); diff --git a/platform/snowy/boot/src/drivers/display/ice40lp.c b/platform/snowy/boot/src/drivers/display/ice40lp.c deleted file mode 100644 index 3aaaae3a67..0000000000 --- a/platform/snowy/boot/src/drivers/display/ice40lp.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ice40lp.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/spi.h" -#include "drivers/pmic.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/delay.h" - -#include "stm32f4xx.h" -#include "misc.h" - -#include - -// We want the SPI clock to run at 16 by default -const uint32_t SPI_DEFAULT_MHZ = 16; -static uint32_t s_spi_clock_hz; - -bool display_busy(void) { - bool busy = GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_BUSY); - return busy; -} - -static void prv_configure_spi(uint32_t spi_clock_hz) { - // Set up a SPI bus on SPI6 - SPI_InitTypeDef spi_cfg; - SPI_I2S_DeInit(DISP_SPI); - SPI_StructInit(&spi_cfg); - spi_cfg.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - spi_cfg.SPI_Mode = SPI_Mode_Master; - spi_cfg.SPI_DataSize = SPI_DataSize_8b; - spi_cfg.SPI_CPOL = SPI_CPOL_High; - spi_cfg.SPI_CPHA = SPI_CPHA_2Edge; - spi_cfg.SPI_NSS = SPI_NSS_Soft; - spi_cfg.SPI_BaudRatePrescaler = spi_find_prescaler(spi_clock_hz, DISPLAY_SPI_CLOCK_PERIPH); - spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB; - SPI_Init(DISP_SPI, &spi_cfg); - - SPI_Cmd(DISP_SPI, ENABLE); -} - -void display_start(void) { - // Enable the GPIOG clock; this is required before configuring the pins - gpio_use(DISP_GPIO); - - GPIO_PinAFConfig(DISP_GPIO, GPIO_PINSOURCE_SCK, GPIO_AF_SPI6); // SCK - GPIO_PinAFConfig(DISP_GPIO, GPIO_PINSOURCE_MOSI, GPIO_AF_SPI6); // MOSI - GPIO_PinAFConfig(DISP_GPIO, GPIO_PINSOURCE_MISO, GPIO_AF_SPI6); // MOSI - - GPIO_InitTypeDef gpio_cfg; - gpio_cfg.GPIO_OType = GPIO_OType_PP; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Mode = GPIO_Mode_AF; - gpio_cfg.GPIO_Speed = GPIO_Speed_25MHz; - gpio_cfg.GPIO_Pin = DISP_PIN_SCLK; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - gpio_cfg.GPIO_Pin = DISP_PIN_SI; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - gpio_cfg.GPIO_Pin = DISP_PIN_SO; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - gpio_cfg.GPIO_Mode = GPIO_Mode_IN; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_UP; - gpio_cfg.GPIO_Pin = DISP_PIN_CDONE; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - gpio_cfg.GPIO_Mode = GPIO_Mode_IN; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Pin = DISP_PIN_BUSY; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - gpio_cfg.GPIO_Mode = GPIO_Mode_OUT; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Pin = DISP_PIN_SCS; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - gpio_cfg.GPIO_OType = GPIO_OType_OD; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Pin = DISP_PIN_CRESET; - GPIO_Init(DISP_GPIO, &gpio_cfg); - - RCC_APB2PeriphClockCmd(DISPLAY_SPI_CLOCK, ENABLE); - - s_spi_clock_hz = MHZ_TO_HZ(SPI_DEFAULT_MHZ); - prv_configure_spi(s_spi_clock_hz); -} - -bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size) { - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); - - // wait a bit. - delay_ms(1); - - GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_RESET); // CRESET LOW - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); // SCS LOW - - delay_ms(1); - - GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_SET); // CRESET -> HIGH - - delay_ms(1); - - PBL_ASSERT(!GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CDONE), "CDONE not low during reset"); - PBL_ASSERT(GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CRESET), "CRESET not high during reset"); - - // Program the FPGA - for (unsigned int i = 0; i < bitstream_size; ++i) { - display_write_byte(fpga_bitstream[i]); - } - - // Set SCS high so that we don't process any of these clocks as commands. - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); // SCS -> HIGH - - // Send dummy clocks - for (unsigned int i = 0; i < 8; ++i) { - display_write_byte(0x00); - } - - if (!GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CDONE)) { - PBL_LOG(LOG_LEVEL_WARNING, "CDONE not high after programming!"); - return false; - } - return true; -} - -void display_power_enable(void) { - // The display requires us to wait 1ms between each power rail coming up. The PMIC - // initialization brings up the 3.2V rail (VLCD on the display, LD02 on the PMIC) for us, but - // we still need to wait before turning on the subsequent rails. - delay_ms(2); - - PBL_LOG(LOG_LEVEL_DEBUG, "Enabling 6v6 (Display VDDC)"); - set_6V6_power_state(true); - - delay_ms(2); - - PBL_LOG(LOG_LEVEL_DEBUG, "Enabling 4v5 (Display VDDP)"); - set_4V5_power_state(true); -} - -void display_power_disable(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "Disabling 4v5 (Display VDDP)"); - set_4V5_power_state(false); - - delay_ms(2); - - PBL_LOG(LOG_LEVEL_DEBUG, "Disabling 6v6 (Display VDDC)"); - set_6V6_power_state(false); - - delay_ms(2); -} - -//! -//! Write a single byte synchronously to the display. Use this -//! sparingly, as it will tie up the micro duing the write. -//! -void display_write_byte(uint8_t d) { - // Block until the tx buffer is empty - while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) continue; - SPI_I2S_SendData(DISP_SPI, d); -} - -uint8_t display_write_and_read_byte(uint8_t d) { - SPI_I2S_ReceiveData(DISP_SPI); - while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) continue; - SPI_I2S_SendData(DISP_SPI, d); - while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_RXNE)) continue; - return SPI_I2S_ReceiveData(DISP_SPI); -} diff --git a/platform/snowy/boot/src/drivers/display/ice40lp.h b/platform/snowy/boot/src/drivers/display/ice40lp.h deleted file mode 100644 index 870f9b35d2..0000000000 --- a/platform/snowy/boot/src/drivers/display/ice40lp.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/gpio.h" - -#include - -// GPIO constants -#define DISP_SPI SPI6 -#define DISP_GPIO GPIOG - -#define DISPLAY_SPI_CLOCK_PERIPH SpiPeriphClockAPB2 -#define DISPLAY_SPI_CLOCK RCC_APB2Periph_SPI6 -#define DISP_PIN_SCS GPIO_Pin_8 -#define DISP_PIN_CDONE GPIO_Pin_9 -#define DISP_PIN_BUSY GPIO_Pin_10 -#define DISP_PIN_SO GPIO_Pin_12 -#define DISP_PIN_SCLK GPIO_Pin_13 -#define DISP_PIN_SI GPIO_Pin_14 -#define DISP_PIN_CRESET GPIO_Pin_15 - -#define GPIO_PINSOURCE_SCK GPIO_PinSource13 -#define GPIO_PINSOURCE_MOSI GPIO_PinSource14 -#define GPIO_PINSOURCE_MISO GPIO_PinSource12 - - -bool display_busy(void); -void display_start(void); -bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size); -void display_write_byte(uint8_t d); -uint8_t display_write_and_read_byte(uint8_t d); -void display_power_enable(void); -void display_power_disable(void); diff --git a/platform/snowy/boot/src/drivers/display/snowy_boot_fpga.c b/platform/snowy/boot/src/drivers/display/snowy_boot_fpga.c deleted file mode 100644 index 20f88e9e2a..0000000000 --- a/platform/snowy/boot/src/drivers/display/snowy_boot_fpga.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/display.h" - -#include - -#include "board/board.h" -#include "drivers/dbgserial.h" -#include "drivers/display/bootloader_fpga_bitstream.auto.h" -#include "drivers/display/ice40lp.h" -#include "drivers/flash/s29vs.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "drivers/spi.h" -#include "flash_region.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_spi.h" -#include "system/passert.h" -#include "util/delay.h" -#include "util/misc.h" - - -#define CMD_NULL (0) -#define CMD_SET_PARAMETER (1) -#define CMD_DISPLAY_OFF (2) -#define CMD_DISPLAY_ON (3) -#define CMD_DRAW_SCENE (4) -#define CMD_RESET_RELEASE (8) -#define CMD_RESET_ASSERT (9) - -#define SCENE_BLACK (0) -#define SCENE_SPLASH (1) -#define SCENE_UPDATE (2) -#define SCENE_ERROR (3) - -#define UPDATE_PROGRESS_MAX (93) - -// The FPGA bitstream stored in NVCM may be missing or defective; a replacement -// bitstream may be stored in the MFG info flash region, prefixed with a -// four-byte header. The header is composed of the bitstream length followed by -// its complement (all bits inverted). -#define FPGA_BITSTREAM_FLASH_ADDR (FMC_BANK_1_BASE_ADDRESS + \ - FLASH_REGION_MFG_INFO_BEGIN + 0x10000) - -struct __attribute__((packed)) FlashBitstream { - uint16_t len; - uint16_t len_complement; - uint8_t bitstream[0]; -}; - -static bool prv_wait_programmed(void) { - // The datasheet lists the typical NVCM configuration time as 56 ms. - // Something is wrong if it takes more than twice that time. - int timeout = 100 * 10; - while (GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CDONE) == 0) { - if (timeout-- == 0) { - dbgserial_putstr("FPGA CDONE timeout expired!"); - return false; - } - delay_us(100); - } - return true; -} - -static bool prv_reset_into_nvcm(void) { - // Reset the FPGA and wait for it to program itself via NVCM. - // NVCM configuration is initiated by pulling CRESET high while SCS is high. - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); - // CRESET needs to be low for at least 200 ns - GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_RESET); - delay_ms(1); - GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_SET); - return prv_wait_programmed(); -} - -static bool prv_reset_fpga(void) { -#ifdef BLANK_FPGA - return display_program(s_fpga_bitstream, sizeof(s_fpga_bitstream)); -#endif - - const struct FlashBitstream *bitstream = (void *)FPGA_BITSTREAM_FLASH_ADDR; - // Work around GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38341 - uint16_t len_complement_complement = ~bitstream->len_complement; - if (bitstream->len != 0xffff && bitstream->len == len_complement_complement) { - dbgserial_putstr("Configuring FPGA from bitstream in flash..."); - if (display_program(bitstream->bitstream, bitstream->len)) { - return true; - } - } else { - // Fall back to NVCM. - dbgserial_putstr("No FPGA bitstream in flash."); - } - dbgserial_putstr("Falling back to NVCM."); - return prv_reset_into_nvcm(); -} - -static void prv_start_command(uint8_t cmd) { - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); - delay_us(100); - display_write_byte(cmd); -} - -static void prv_send_command_arg(uint8_t arg) { - display_write_byte(arg); -} - -static void prv_end_command(void) { - while (SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_BSY)) continue; - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); -} - -static bool prv_wait_busy(void) { - // The display should come out of busy within 35 milliseconds; - // it is a waste of time to wait more than twice that. - int timeout = 50 * 10; - while (display_busy()) { - if (timeout-- == 0) { - dbgserial_putstr("Display busy-wait timeout expired!"); - return false; - } - delay_us(100); - } - return true; -} - -static void prv_screen_on(void) { - prv_start_command(CMD_DISPLAY_ON); - prv_end_command(); -} - -static void prv_screen_off(void) { - prv_start_command(CMD_DISPLAY_OFF); - prv_end_command(); -} - -void prv_draw_scene(uint8_t scene) { - prv_start_command(CMD_DRAW_SCENE); - prv_send_command_arg(scene); - prv_end_command(); -} -void prv_set_parameter(uint32_t param) { - prv_start_command(CMD_SET_PARAMETER); - // Send in little-endian byte order - prv_send_command_arg(param & 0xff); - prv_send_command_arg((param >> 8) & 0xff); - prv_send_command_arg((param >> 16) & 0xff); - prv_send_command_arg((param >> 24) & 0xff); - prv_end_command(); -} - -static uint8_t prv_read_version(void) { - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); - delay_us(100); - - uint8_t version_num = display_write_and_read_byte(0); - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); - return version_num; -} - -void display_init(void) { - display_start(); - bool program_success = prv_reset_fpga(); - if (!program_success) { - dbgserial_putstr("FPGA configuration failed. Is this a bigboard?"); - // Don't waste time trying to get the FPGA unstuck if it's not configured. - // It's just going to waste time and frustrate bigboard users. - return; - } - - dbgserial_print("FPGA version: "); - dbgserial_print_hex(prv_read_version()); - dbgserial_putstr(""); - - // enable the power rails - display_power_enable(); - -#ifdef TEST_FPGA_RESET_COMMAND -#define ASSERT_BUSY_IS(state) \ - dbgserial_putstr(GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_BUSY) == state? \ - "Yes" : "No") - - // Test out the FPGA soft-reset capability present in release-03 of the FPGA. - dbgserial_putstr("FPGA soft-reset test"); - - dbgserial_print("Precondition: BUSY asserted during scene draw? "); - prv_draw_scene(SCENE_BLACK); - ASSERT_BUSY_IS(Bit_SET); - - dbgserial_print("Is BUSY cleared after the reset command? "); - prv_start_command(CMD_RESET_ASSERT); - prv_end_command(); - ASSERT_BUSY_IS(Bit_RESET); - - dbgserial_print("Are draw-scene commands ineffectual while in reset? "); - prv_draw_scene(SCENE_BLACK); - ASSERT_BUSY_IS(Bit_RESET); - - dbgserial_print("Does releasing reset allow draw-scene commands " - "to function again? "); - prv_start_command(CMD_RESET_RELEASE); - prv_end_command(); - prv_draw_scene(SCENE_BLACK); - ASSERT_BUSY_IS(Bit_SET); - - dbgserial_print("Does the draw-scene command complete? "); - dbgserial_putstr(prv_wait_busy()? "Yes" : "No"); -#endif - - // Work around an issue which some boards exhibit where the FPGA ring - // oscillator can start up with higher harmonics, massively overclocking the - // design and causing malfunction. When this occurrs, the draw-scene command - // will not work, asserting BUSY indefinitely but never updating the display. - // Other commands such as display-on and display-off are less affected by the - // overclocking, so the display can be turned on while the FPGA is in this - // state, showing only garbage. - // FPGA malfunction can be detected in software. In an attempt to restore - // proper functioning, the FPGA can be reset and reconfigured in the hopes - // that the ring oscillator will start up and oscillate without any higher - // harmonics. Bootloader release 03 attempts to mitigate this problem by - // delaying oscillator startup until after configuration completes. Time will - // tell whether this actually fixes things. - for (int retries = 0; retries <= 20; ++retries) { - prv_draw_scene(SCENE_SPLASH); - if (prv_wait_busy()) { - prv_screen_on(); - dbgserial_print("Display initialized after "); - dbgserial_print_hex(retries); - dbgserial_putstr(" retries."); - return; - } - - prv_reset_fpga(); - } - - // It's taken too many attempts and the FPGA still isn't behaving. Give up on - // showing the splash screen and keep the screen off so that the user doesn't - // see a broken-looking staticky screen on boot. - dbgserial_putstr("Display initialization failed."); - prv_screen_off(); -} - -void display_boot_splash(void) { - prv_wait_busy(); - prv_draw_scene(SCENE_SPLASH); - // Don't turn the screen on until the boot-splash is fully drawn. - prv_wait_busy(); - prv_screen_on(); -} - -void display_firmware_update_progress( - uint32_t numerator, uint32_t denominator) { - static uint8_t last_bar_fill = UINT8_MAX; - // Scale progress to the number of pixels in the progress bar, - // rounding half upwards. - uint8_t bar_fill = - ((numerator * UPDATE_PROGRESS_MAX) + ((denominator+1)/2)) / denominator; - // Don't waste time and power redrawing the same screen repeatedly. - if (bar_fill != last_bar_fill) { - last_bar_fill = bar_fill; - prv_set_parameter(bar_fill); - prv_draw_scene(SCENE_UPDATE); - } -} - -void display_error_code(uint32_t error_code) { - prv_set_parameter(error_code); - prv_draw_scene(SCENE_ERROR); -} - -void display_prepare_for_reset(void) { - prv_screen_off(); -} diff --git a/platform/snowy/boot/src/drivers/exti.h b/platform/snowy/boot/src/drivers/exti.h deleted file mode 100644 index 02706d8651..0000000000 --- a/platform/snowy/boot/src/drivers/exti.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -typedef enum { - ExtiTrigger_Rising, - ExtiTrigger_Falling, - ExtiTrigger_RisingFalling -} ExtiTrigger; - -//! See section 12.2.5 "External interrupt/event line mapping" in the STM32F2 reference manual -typedef enum { - ExtiLineOther_RTCAlarm = 17, - ExtiLineOther_RTCWakeup = 22 -} ExtiLineOther; - -typedef void (*ExtiHandlerCallback)(void); - -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb); -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger); - -static inline void exti_enable(ExtiConfig config); -static inline void exti_disable(ExtiConfig config); - -void exti_enable_other(ExtiLineOther); -void exti_disable_other(ExtiLineOther); - -#include "exti.inl.h" diff --git a/platform/snowy/boot/src/drivers/exti.inl.h b/platform/snowy/boot/src/drivers/exti.inl.h deleted file mode 100644 index a16576c7ae..0000000000 --- a/platform/snowy/boot/src/drivers/exti.inl.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! @file exti.inl.h -//! -//! Helper functions intended to be inlined into the calling code. - -static inline void exti_enable(ExtiConfig config) { - exti_enable_other(config.exti_line); -} - -static inline void exti_disable(ExtiConfig config) { - exti_disable_other(config.exti_line); -} diff --git a/platform/snowy/boot/src/drivers/flash.h b/platform/snowy/boot/src/drivers/flash.h deleted file mode 100644 index 88bee7f16d..0000000000 --- a/platform/snowy/boot/src/drivers/flash.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -/** - * Configure the micro's peripherals to communicate with the flash - * chip. - */ -void flash_init(void); - -/** - * Read 1 or more bytes starting at the specified 24bit address into - * the provided buffer. This function does no range checking, so it is - * currently possible to run off the end of the flash. - * - * @param buffer A byte-buffer that will be used to store the data - * read from flash. - * @param start_addr The address of the first byte to be read from flash. - * @param buffer_size The total number of bytes to be read from flash. - */ -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -//! Check if we can talk to the flash. -//! @return trie if the CFI table can be queried. -bool flash_sanity_check(void); diff --git a/platform/snowy/boot/src/drivers/flash/s29vs.c b/platform/snowy/boot/src/drivers/flash/s29vs.c deleted file mode 100644 index 38a39ea817..0000000000 --- a/platform/snowy/boot/src/drivers/flash/s29vs.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "drivers/flash.h" -#include "drivers/flash/s29vs.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "stm32f4xx_gpio.h" -#include "util/delay.h" - - -//! @param sector_address The address of the start of the sector to write the command to. -//! @param cmd The command to write. -static void flash_s29vs_issue_command(FlashAddress sector_address, S29VSCommand cmd) { - // The offset in the sector we write the first part of commands to. Note that this is a 16-bit - // word aligned address as opposed to a byte address. - static const uint32_t COMMAND_ADDRESS = 0x555; - - ((__IO uint16_t*) (FMC_BANK_1_BASE_ADDRESS + sector_address))[COMMAND_ADDRESS] = cmd; -} - -static uint16_t flash_s29vs_read_short(FlashAddress addr) { - return *((__IO uint16_t*)(FMC_BANK_1_BASE_ADDRESS + addr)); -} - -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { - memcpy(buffer, (void*)(FMC_BANK_1_BASE_ADDRESS + start_addr), buffer_size); -} - - -static void flash_s29vs_software_reset(void) { - flash_s29vs_issue_command(0, S29VSCommand_SoftwareReset); -} - -void flash_init(void) { - gpio_use(GPIOB); - gpio_use(GPIOD); - gpio_use(GPIOE); - - // Configure the reset pin (D2) - GPIO_InitTypeDef gpio_init = { - .GPIO_Pin = GPIO_Pin_2, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = GPIO_Speed_100MHz, - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - GPIO_Init(GPIOD, &gpio_init); - - GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET); - - // Configure pins relating to the FMC peripheral (30 pins!) - - // B7 - FMC AVD - FMC Address Valid aka Latch - // D0-D1, D8-D15, E2-15 - FMC A, AD - FMC Address and Address/Data lines - // D2 - Reset - GPIO Reset line - // D3 - FMC CLK - // D4 - FMC OE - FMC Output Enable - // D5 - FMC WE - FMC Write Enable - // D6 - FMC RDY - FMC Ready line - // D7 - FMC CE - FMC Chip Enable - - gpio_init = (GPIO_InitTypeDef) { - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = GPIO_Speed_100MHz, - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_FMC); - gpio_init.GPIO_Pin = GPIO_Pin_7; - GPIO_Init(GPIOB, &gpio_init); - - for (uint8_t pin_source = 0; pin_source < 16; ++pin_source) { - if (pin_source == 2) { - continue; - } - GPIO_PinAFConfig(GPIOD, pin_source, GPIO_AF_FMC); - } - gpio_init.GPIO_Pin = GPIO_Pin_All & (~GPIO_Pin_2); - GPIO_Init(GPIOD, &gpio_init); - - for (uint8_t pin_source = 2; pin_source < 16; ++pin_source) { - GPIO_PinAFConfig(GPIOE, pin_source, GPIO_AF_FMC); - } - gpio_init.GPIO_Pin = GPIO_Pin_All & (~GPIO_Pin_0) & (~GPIO_Pin_1); - GPIO_Init(GPIOE, &gpio_init); - - // We have configured the pins, lets perform a full HW reset to put the chip - // in a good state - GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_RESET); - delay_us(10); // only needs to be 50ns according to data sheet - GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET); - delay_us(30); // need 200ns + 10us before CE can be pulled low - - RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FMC, ENABLE); - - // Setup default config for async - // Configure the FMC peripheral itself - FMC_NORSRAMTimingInitTypeDef nor_timing_init = { - // time between address write and address latch (AVD high) - // tAAVDS on datasheet, min 4 ns - // - // AVD low time - // tAVDP on datasheet, min 6 ns - .FMC_AddressSetupTime = 1, - - // time between AVD high (address is available) and OE low (memory can write) - // tAVDO on the datasheet, min 4 ns - .FMC_AddressHoldTime = 1, - - // time between OE low (memory can write) and valid data being available - // tOE on datasheet, max 15 ns - // 13 cycles is the default configuration in the component's configuration register - // Setup to 3 for async - .FMC_DataSetupTime = 3, - - // Time between chip selects - // not on the datasheet, picked a random safe number - .FMC_BusTurnAroundDuration = 1, - - .FMC_CLKDivision = 15, // Not used for async NOR - .FMC_DataLatency = 15, // Not used for async NOR - .FMC_AccessMode = FMC_AccessMode_A // Only used for ExtendedMode == FMC_ExtendedMode_Enable, which we don't use - }; - - FMC_NORSRAMInitTypeDef nor_init = { - .FMC_Bank = FMC_Bank1_NORSRAM1, - .FMC_DataAddressMux = FMC_DataAddressMux_Enable, - .FMC_MemoryType = FMC_MemoryType_NOR, - .FMC_MemoryDataWidth = FMC_NORSRAM_MemoryDataWidth_16b, - .FMC_BurstAccessMode = FMC_BurstAccessMode_Disable, - .FMC_AsynchronousWait = FMC_AsynchronousWait_Disable, - .FMC_WaitSignalPolarity = FMC_WaitSignalPolarity_Low, - .FMC_WrapMode = FMC_WrapMode_Disable, - .FMC_WaitSignalActive = FMC_WaitSignalActive_BeforeWaitState, - .FMC_WriteOperation = FMC_WriteOperation_Enable, - .FMC_WaitSignal = FMC_WaitSignal_Enable, - .FMC_ExtendedMode = FMC_ExtendedMode_Disable, - .FMC_WriteBurst = FMC_WriteBurst_Disable, - .FMC_ContinousClock = FMC_CClock_SyncOnly, - .FMC_ReadWriteTimingStruct = &nor_timing_init - }; - - FMC_NORSRAMInit(&nor_init); - - // Re-enable NOR - FMC_NORSRAMCmd(FMC_Bank1_NORSRAM1, ENABLE); -} - -bool flash_sanity_check(void) { - // Check that the first words of the CFI table are 'Q' 'R' 'Y'. - // This will work on any flash memory, regardless of the manufacturer. - flash_s29vs_issue_command(0, S29VSCommand_CFIEntry); - bool ok = (flash_s29vs_read_short(0x20) & 0xff) == 'Q'; - ok = ok && (flash_s29vs_read_short(0x22) & 0xff) == 'R'; - ok = ok && (flash_s29vs_read_short(0x24) & 0xff) == 'Y'; - flash_s29vs_software_reset(); - return ok; -} diff --git a/platform/snowy/boot/src/drivers/flash/s29vs.h b/platform/snowy/boot/src/drivers/flash/s29vs.h deleted file mode 100644 index 2eb8c315ba..0000000000 --- a/platform/snowy/boot/src/drivers/flash/s29vs.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "drivers/flash.h" - -//! An address in the flash address spac -typedef uint32_t FlashAddress; - -//! This is the memory mapped region that's mapped to the parallel flash. -static const uintptr_t FMC_BANK_1_BASE_ADDRESS = 0x60000000; - -//! This is the unit that we use for erasing -static const uint32_t SECTOR_SIZE_BYTES = 0x20000; // 128kb -//! This is the unit that we use for writing -static const uint32_t PAGE_SIZE_BYTES = 64; - -//! Different commands we can send to the flash -typedef enum S29VSCommand { - S29VSCommand_WriteBufferLoad = 0x25, - S29VSCommand_BufferToFlash = 0x29, - S29VSCommand_ReadStatusRegister = 0x70, - S29VSCommand_ClearStatusRegister = 0x71, - S29VSCommand_EraseSetup = 0x80, - S29VSCommand_DeviceIDEntry = 0x90, - S29VSCommand_CFIEntry = 0x98, - S29VSCommand_ConfigureRegisterEntry = 0xD0, - S29VSCommand_SoftwareReset = 0xF0 -} S29VSCommand; - -//! Arguments to the S29VSCommand_EraseSetup command -typedef enum S29VSCommandEraseAguments { - S29VSCommandEraseAguments_ChipErase = 0x10, - S29VSCommandEraseAguments_SectorErase = 0x30 -} S29VSCommandEraseAguments; - -//! The bitset stored in the status register, see flash_s29vs_read_status_register -typedef enum S29VSStatusBit { - S29VSStatusBit_BankStatus = 0x00, - S29VSStatusBit_SectorLockStatus = 0x01, - S29VSStatusBit_ProgramSuspended = 0x02, - // 0x04 is unused - S29VSStatusBit_ProgramStatus = 0x10, - S29VSStatusBit_EraseStatus = 0x20, - S29VSStatusBit_EraseSuspended = 0x40, - S29VSStatusBit_DeviceReady = 0x80, -} S29VSStatusBit; diff --git a/platform/snowy/boot/src/drivers/gpio.h b/platform/snowy/boot/src/drivers/gpio.h deleted file mode 100644 index 3ba8fa3e3f..0000000000 --- a/platform/snowy/boot/src/drivers/gpio.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_gpio.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_gpio.h" -#endif - -#include "board/board.h" - -void gpio_use(GPIO_TypeDef* GPIOx); -void gpio_release(GPIO_TypeDef* GPIOx); - -//! Initialize a GPIO as an output. -//! -//! @param pin_config the BOARD_CONFIG pin configuration struct -//! @param otype the output type of the pin (GPIO_OType_PP or GPIO_OType_OD) -//! @param speed the output slew rate -//! @note The slew rate should be set as low as possible for the -//! pin function to minimize ringing and RF interference. -void gpio_output_init(OutputConfig pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed); - -//! Assert or deassert the output pin. -//! -//! Asserting the output drives the pin high if pin_config.active_high -//! is true, and drives it low if pin_config.active_high is false. -void gpio_output_set(OutputConfig pin_config, bool asserted); - -//! Configure a GPIO alternate function. -//! -//! @param pin_config the BOARD_CONFIG pin configuration struct -//! @param otype the output type of the pin (GPIO_OType_PP or GPIO_OType_OD) -//! @param speed the output slew rate -//! @param pupd pull-up or pull-down configuration -//! @note The slew rate should be set as low as possible for the -//! pin function to minimize ringing and RF interference. -void gpio_af_init(AfConfig af_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd); diff --git a/platform/snowy/boot/src/drivers/i2c.h b/platform/snowy/boot/src/drivers/i2c.h deleted file mode 100644 index ebbfefd9ae..0000000000 --- a/platform/snowy/boot/src/drivers/i2c.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include -#include - -//! Initialize the I2C driver. Must be called before first use -void i2c_init(void); - -//! Start using the I2C bus connected to the device specified by \a device_id -//! Must be called before any other use of the bus is performed -//! @param device_id ID of device -void i2c_use(I2cDevice device_id); - -//! Stop using the I2C bus connected to the device specified by \a device_id -//! Call when done with the bus -//! @param device_id ID of device -void i2c_release(I2cDevice device_id); - -//! Reset the bus -//! Will re-initialize the bus and cycle the power to the bus if this is -//! supported for the bus the device specified by \a device_id is connected to) -//! @param device_id ID of device, this will identify the bus to be reset -void i2c_reset(I2cDevice device_id); - -//! Manually bang out the clock on the bus specified by \a device_id for a period -//! of time or until the data line recovers -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device, this will identify the bus to be recovered -//! @return true if the data line recovered, false otherwise -bool i2c_bitbang_recovery(I2cDevice device_id); - -//! Read the value of a register -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address Address of register to read -//! @param result Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, uint8_t *result); - -//! Read a sequence of registers starting from \a register_address_start -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address_start Address of first register to read -//! @param read_size Number of bytes to read -//! @param result_buffer Pointer to destination buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_read_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address_start, uint8_t read_size, uint8_t* result_buffer); - -//! Write to a register -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address Address of register to write to -//! @param value Data value to write -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, uint8_t value); - -//! Write to a sequence of registers starting from \a register_address_start -//! Must not be called before \ref i2c_use has been called for the device -//! @param device_id ID of device to communicate with -//! @param i2c_device_address Device bus address -//! @param register_address_start Address of first register to read -//! @param write_size Number of bytes to write -//! @param buffer Pointer to source buffer -//! @return true if transfer succeeded, false if error occurred -bool i2c_write_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address_start, uint8_t write_size, const uint8_t* buffer); diff --git a/platform/snowy/boot/src/drivers/periph_config.h b/platform/snowy/boot/src/drivers/periph_config.h deleted file mode 100644 index 5ee10b20ab..0000000000 --- a/platform/snowy/boot/src/drivers/periph_config.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx.h" -#endif - -typedef void (*ClockCmd)(uint32_t periph, FunctionalState state); - -static inline void periph_config_init(void) {} -static inline void periph_config_acquire_lock(void) {} -static inline void periph_config_release_lock(void) {} - -static inline void periph_config_enable(ClockCmd clock_cmd, uint32_t periph) { - clock_cmd(periph, ENABLE); -} - -static inline void periph_config_disable(ClockCmd clock_cmd, uint32_t periph) { - clock_cmd(periph, DISABLE); -} diff --git a/platform/snowy/boot/src/drivers/pmic.h b/platform/snowy/boot/src/drivers/pmic.h deleted file mode 100644 index ab72653aca..0000000000 --- a/platform/snowy/boot/src/drivers/pmic.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Initialize the PMIC driver. Call this once at startup. -bool pmic_init(void); - -//! Tell the PMIC to power off the board and enter a standby-like state. All components will -//! have their power removed (except for the RTC so we'll still keep time) and the PMIC itself -//! will monitor the buttons for when to wake up. -bool pmic_power_off(void); - -//! Enable the battery monitor portion of the PMIC. Remember to turn this off with -//! pmic_disable_battery_measure when immediate readings aren't required. -bool pmic_enable_battery_measure(void); - -//! Disable the battery monitor portion of the PMIC. -bool pmic_disable_battery_measure(void); - -//! Enable and disable the charging portion of the PMIC. -bool pmic_set_charger_state(bool enable); - -//! @return true if the PMIC thinks we're charging (adding additional charge to the battery). -//! Note that once we hit full charge we'll no longer be charging, which is a different state -//! that pmic_is_usb_connected. -bool pmic_is_charging(void); - -//! @return true if a usb-ish charger cable is currently connected. -bool pmic_is_usb_connected(void); - -//! Read information about the chip for tracking purposes. -void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision); - -// FIXME: The following functions are unrelated to the PMIC and should be moved to the -// display/accessory connector drivers once we have them. - -//! Enables the LDO3 power rail. Used for the MFi/Magnetometer on snowy_bb, MFi on snowy_evt. -void set_ldo3_power_state(bool enabled); - -//! Enables the 4.5V power rail. Used for the display on snowy. -void set_4V5_power_state(bool enabled); - -//! Enables the 6.6V power rail. Used for the display on snowy. -void set_6V6_power_state(bool enabled); - -//! Enables power to the accessory connector. -void set_accessory_power_state(bool enabled); diff --git a/platform/snowy/boot/src/drivers/pmic/max14690_pmic.c b/platform/snowy/boot/src/drivers/pmic/max14690_pmic.c deleted file mode 100644 index 806c7873f2..0000000000 --- a/platform/snowy/boot/src/drivers/pmic/max14690_pmic.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* This file should probably go in the stm32f4 folder */ - -#include "drivers/pmic.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/exti.h" -#include "drivers/periph_config.h" -#include "drivers/display/ice40lp.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/delay.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_adc.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_adc.h" -#endif - -#include - -/* PMIC Bus Information */ -#define MAX14690_ADDR 0x50 - -//! The addresses of the registers that we can read using i2c -typedef enum PmicRegisters { - PmicRegisters_CHIP_ID = 0x00, - PmicRegisters_CHIP_REV = 0x01, - PmicRegisters_STATUSA = 0x02, - PmicRegisters_STATUSB = 0x03, - PmicRegisters_INTA = 0x05, - PmicRegisters_INTB = 0x06, - PmicRegisters_INT_MASK_A = 0x07, - PmicRegisters_INT_MASK_B = 0x08, - PmicRegisters_CHG_CNTL_A = 0x0A, - PmicRegisters_BUCK1_CONFIG = 0x0D, - PmicRegisters_BUCK2_CONFIG = 0x0F, - PmicRegisters_LDO1_CONFIG = 0x12, - PmicRegisters_LDO2_CONFIG = 0x14, - PmicRegisters_LDO3_CONFIG = 0x16, - PmicRegisters_MON_CFG = 0x19, - PmicRegisters_HAND_SHK = 0x1D, - PmicRegisters_PWR_CFG = 0x1F -} PmicRegisters; - -//! The different power rails that our PMIC controls -typedef enum PmicRail { - PmicRail_BUCK1, //!< 1.2V - PmicRail_BUCK2, //!< 1.8V - PmicRail_LDO1, //!< 2.0V - Auto - RTC - PmicRail_LDO2, //!< 3.2V - Manual - FPGA - - //! snowy_bb: 2.5V - Manual - MFi, Magnetometer - //! snowy_evt: 1.8V - Manual - MFi - PmicRail_LDO3 -} PmicRail; - -//! Gives configuration information for reading a given rail through the monitor pin. -typedef struct { - const char* name; //!< Name for the rail. - - //! What ratio we need to divide by in order to bring it into the range we can sense. We can - //! only read between 0 and 1.8Vs, so we need to use the PMIC hardware to divide it down before - //! sending it to us. Valid values are 1-4. - uint8_t ratio; - - //! The binary value we need to put in the register to select the rail. - uint8_t source_config; -} PmicMonConfig; - -// Using the Binary constants GCC extension here, supported in GCC and Clang -// https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html -static const PmicMonConfig MON_CONFIG[] = { - { "+VBAT", 3, 0b001 }, // 3:1 - -// We only care about non-battery rails in MFG where we have the command_pmic_rails function. -#ifdef RECOVERY_FW - { "+VSYS", 4, 0b010 }, // 4:1 - { "+1V2", 1, 0b011 }, // 1:1, BUCK1 - { "+1V8", 2, 0b100 }, // 2:1, BUCK2 - { "+2V0_RTC", 2, 0b101 }, // 2:1, LDO1 - { "+3V2", 2, 0b110 }, // 2:1, LDO2 -#ifdef BOARD_SNOWY_BB - { "+2V5", 2, 0b111 }, // 2:1, LDO3 -#else - { "+1V8_MFI_MIC", 2, 0b111 }, // 2:1, LDO3 -#endif // BOARD_SNOWY_BB -#endif // RECOVERY_FW -}; - -static const int PMIC_MON_CONFIG_VBAT_INDEX = 0; - -/* Private Function Definitions */ -static bool prv_is_alive(void); -static bool prv_set_pin_config(void); - -//! Request that the rail be used or released. Internally refcounted per rail so you don't have -//! to worry about turning this off on another client. -static bool prv_update_rail_state(PmicRail rail, bool enable); - -static void prv_mon_config_lock(void) { -} - -static void prv_mon_config_unlock(void) { -} - -static bool prv_read_register(uint8_t register_address, uint8_t *result) { - return i2c_read_register(I2C_DEVICE_MAX14690, MAX14690_ADDR, register_address, result); -} - - -static bool prv_write_register(uint8_t register_address, uint8_t value) { - return i2c_write_register(I2C_DEVICE_MAX14690, MAX14690_ADDR, register_address, value); -} - -/* Public Functions */ -bool pmic_init(void) { - if (!prv_set_pin_config()) { - return false; - } - - if (!prv_is_alive()) { - return false; - } - - // If not written to whithin 5 seconds of power-on the PMIC will shut down. - //i2c_write_register(I2C_DEVICE_MAX14690, MAX14690_ADDR, PmicRegisters_HAND_SHK, 0x01); - - // Power up 3.2V rail - prv_update_rail_state(PmicRail_LDO2, true); - - return true; -} - -static bool prv_update_rail_state(PmicRail rail, bool enable) { - static int8_t s_ldo2_ref_count = 0; - static int8_t s_ldo3_ref_count = 0; - - int8_t *ref_count; - uint8_t rail_control_reg; - - if (rail == PmicRail_LDO2) { - rail_control_reg = PmicRegisters_LDO2_CONFIG; - ref_count = &s_ldo2_ref_count; - } else if (rail == PmicRail_LDO3) { - rail_control_reg = PmicRegisters_LDO3_CONFIG; - ref_count = &s_ldo3_ref_count; - } else { - WTF; - } - - uint8_t register_value; - bool success = prv_read_register(rail_control_reg, ®ister_value); - - if (!success) { - // Failed to read the current register value - return false; - } - - if (enable) { - if (*ref_count) { - (*ref_count)++; - return true; - } else { - // Set the register byte to XXXXX01X to enable the rail, mask and set - register_value = (register_value & ~0x06) | 0x02; - - success = prv_write_register(rail_control_reg, register_value); - - if (success) { - // We enabled the rail! - *ref_count = 1; - - // We need to wait a bit for the rail to stabilize before continuing to use the device. - // It takes 2.6ms for the LDO rails to ramp. - delay_ms(3); - - return true; - } - return false; - } - } else { - if (*ref_count <= 1) { - // Set the register byte to XXXXX00X to disable the rail, just mask - register_value = (register_value & ~0x06); - - success = prv_write_register(rail_control_reg, register_value); - - if (success) { - // We disabled the rail! - *ref_count = 0; - return true; - } - return false; - } else { - (*ref_count)--; - return true; - } - } -} - -bool pmic_power_off(void) { - bool ret = prv_write_register(PmicRegisters_PWR_CFG, 0xB2); - - if (ret) { - // Goodbye cruel world. The PMIC should be removing our power at any time now. - - while(1); - __builtin_unreachable(); - } - - return false; -} - -static bool prv_set_mon_config_register(uint8_t value) { - return prv_write_register(PmicRegisters_MON_CFG, value); -} - -static bool prv_set_mon_config(const PmicMonConfig *config) { - const uint8_t ratio_config = 4 - config->ratio; // 4:1 is 0b00, 1:1 is 0b11. - - const uint8_t register_value = (ratio_config << 4) | config->source_config; - bool result = prv_set_mon_config_register(register_value); - - // Need to wait a short period of time for the reading to settle due to capacitance on the line. - delay_us(200); - - return result; -} - -bool pmic_enable_battery_measure(void) { - prv_mon_config_lock(); - - return prv_set_mon_config(&MON_CONFIG[PMIC_MON_CONFIG_VBAT_INDEX]); - - // Don't prv_unlock, we don't want anyone else mucking with the mon config until - // pmic_disable_battery_measure is called. -} - -bool pmic_disable_battery_measure(void) { - bool result = prv_set_mon_config_register(0); - - // Releases the lock that was previously aquired in pmic_enable_battery_measure. - prv_mon_config_unlock(); - - return result; -} - -bool pmic_set_charger_state(bool enable) { - // Defaults to ON - // Default value is 0xF7 - const uint8_t register_value = enable ? 0xf7 : 0xf6; - - bool result = prv_write_register(PmicRegisters_CHG_CNTL_A, register_value); - - return result; -} - - -bool pmic_is_charging(void) { - uint8_t val; - if (!prv_read_register(PmicRegisters_STATUSA, &val)) { - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are charging - return true; - } - - uint8_t chgstat = val & 0x07; - - // TODO: Confirm that all of these values == our definition of charging - if (chgstat == 0x02 || chgstat == 0x03 || chgstat == 0x04 || - chgstat == 0x05 || chgstat == 0x06) { - return true; - } else { - return false; - } -} - -bool pmic_is_usb_connected(void) { - // TODO: Uncomment when there is i2c support in the bootloader - uint8_t val; - if (!prv_read_register(PmicRegisters_STATUSB, &val)) { - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are connected to a USB cable - return true; - } - - bool usb_connected = (val >> 3) & 1; - - return usb_connected; -} - -void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision) { - prv_read_register(PmicRegisters_CHIP_ID, chip_id); - prv_read_register(PmicRegisters_CHIP_REV, chip_revision); -} - - -/* Private Function Implementations */ -static bool prv_is_alive(void) { - uint8_t val; - prv_read_register(0x00, &val); - - if (val == 0x01) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found the max14690"); - return true; - } else { - PBL_LOG(LOG_LEVEL_DEBUG, - "Error: read max14690 whomai byte 0x%x, expecting 0x%x", val, 0x01); - return false; - } -} - -static bool prv_set_pin_config(void) { - periph_config_acquire_lock(); - gpio_use(GPIOB); - - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_9; - gpio_init_struct.GPIO_Mode = GPIO_Mode_AF; - gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz; - gpio_init_struct.GPIO_OType = GPIO_OType_OD; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(GPIOB, &gpio_init_struct); - - // I2C config - GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); - GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1); - - gpio_release(GPIOB); - - // Initialize the GPIOs for the 4V5, 6V6, and accessory rails - gpio_use(GPIOF); - gpio_init_struct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_13; - gpio_init_struct.GPIO_Mode = GPIO_Mode_OUT; - gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz; - gpio_init_struct.GPIO_OType = GPIO_OType_PP; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - - GPIO_Init(GPIOF, &gpio_init_struct); - gpio_release(GPIOF); - - periph_config_release_lock(); - - // FIXME: We should probably turn this on on-demand instead of leaving it on all the time. - i2c_use(I2C_DEVICE_MAX14690); - - return true; -} - -void set_ldo3_power_state(bool enabled) { - i2c_use(I2C_DEVICE_MAX14690); - prv_update_rail_state(PmicRail_LDO3, enabled); - i2c_release(I2C_DEVICE_MAX14690); -} - -void set_4V5_power_state(bool enabled) { - gpio_use(GPIOF); - GPIO_WriteBit(GPIOF, GPIO_Pin_2, enabled?Bit_SET:Bit_RESET); - gpio_release(GPIOF); -} - -void set_6V6_power_state(bool enabled) { - gpio_use(GPIOF); - GPIO_WriteBit(GPIOF, GPIO_Pin_3, enabled?Bit_SET:Bit_RESET); - gpio_release(GPIOF); -} - -void set_accessory_power_state(bool enabled) { - gpio_use(GPIOF); - GPIO_WriteBit(GPIOF, GPIO_Pin_13, enabled?Bit_SET:Bit_RESET); - gpio_release(GPIOF); -} diff --git a/platform/snowy/boot/src/drivers/spi.h b/platform/snowy/boot/src/drivers/spi.h deleted file mode 100644 index c835d5c2a4..0000000000 --- a/platform/snowy/boot/src/drivers/spi.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum { - SpiPeriphClockAPB1, - SpiPeriphClockAPB2 -} SpiPeriphClock; - -//! @internal -//! Get the nearest SPI prescaler. Updates bus_frequency with the actual frequency -//! @param bus_frequency the desired bus frequency -//! @param periph_clock The peripheral clock that is used. -uint16_t spi_find_prescaler(uint32_t bus_frequency, SpiPeriphClock periph_clock); diff --git a/platform/snowy/boot/src/drivers/stm32_common/button.c b/platform/snowy/boot/src/drivers/stm32_common/button.c deleted file mode 100644 index 0b79a52a3e..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/button.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/button.h" - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" - -static void initialize_button_common(void) { - if (!BOARD_CONFIG_BUTTON.button_com.gpio) { - // This board doesn't use a button common pin. - return; - } - - // Configure BUTTON_COM to drive low. When the button - // is pressed this pin will be connected to the pin for the - // button. - gpio_use(BOARD_CONFIG_BUTTON.button_com.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_BUTTON.button_com.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(BOARD_CONFIG_BUTTON.button_com.gpio, &GPIO_InitStructure); - GPIO_WriteBit(BOARD_CONFIG_BUTTON.button_com.gpio, BOARD_CONFIG_BUTTON.button_com.gpio_pin, 0); - - gpio_release(BOARD_CONFIG_BUTTON.button_com.gpio); -} - -static void initialize_button(const ButtonConfig* config) { - // Configure the pin itself - gpio_use(config->gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_PuPd = config->pull; - GPIO_InitStructure.GPIO_Pin = config->gpio_pin; - GPIO_Init(config->gpio, &GPIO_InitStructure); - - gpio_release(config->gpio); -} - -bool button_is_pressed(ButtonId id) { - const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id]; - gpio_use(button_config->gpio); - uint8_t bit = GPIO_ReadInputDataBit(button_config->gpio, button_config->gpio_pin); - gpio_release(button_config->gpio); - return !bit; -} - -uint8_t button_get_state_bits(void) { - uint8_t button_state = 0x00; - for (int i = 0; i < NUM_BUTTONS; ++i) { - button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i; - } - return button_state; -} - -void button_init(void) { - periph_config_acquire_lock(); - - periph_config_enable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG); - - initialize_button_common(); - for (int i = 0; i < NUM_BUTTONS; ++i) { - initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]); - } - - periph_config_disable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG); - - periph_config_release_lock(); -} - -bool button_selftest(void) { - return button_get_state_bits() == 0; -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/crc.c b/platform/snowy/boot/src/drivers/stm32_common/crc.c deleted file mode 100644 index f082c02370..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/crc.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/crc.h" - -#include "drivers/flash.h" -#include "drivers/periph_config.h" -#include "system/passert.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_crc.h" -#include "stm32f2xx_rcc.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_crc.h" -#include "stm32f4xx_rcc.h" -#endif - -#include - -static bool s_initialized = false; -static bool s_clock_running = false; - -static void enable_crc_clock(void) { - // save the state so that if stop mode interrupts things, we resume cleanly - s_clock_running = true; - - periph_config_enable(RCC_AHB1PeriphClockCmd, RCC_AHB1Periph_CRC); -} - -static void disable_crc_clock(void) { - // save the state so that if stop mode interrupts things, we resume cleanly - s_clock_running = false; - - periph_config_disable(RCC_AHB1PeriphClockCmd, RCC_AHB1Periph_CRC); -} - -void crc_init(void) { - if (s_initialized) { - return; - } - - s_initialized = true; -} - -void crc_calculate_incremental_start(void) { - PBL_ASSERTN(s_initialized); - - enable_crc_clock(); - CRC_ResetDR(); -} - -static void crc_calculate_incremental_words(const uint32_t* data, unsigned int data_length) { - PBL_ASSERTN(s_initialized); - - CRC_CalcBlockCRC((uint32_t*) data, data_length); -} - -static uint32_t crc_calculate_incremental_remaining_bytes(const uint8_t* data, unsigned int data_length) { - PBL_ASSERTN(s_initialized); - uint32_t crc_value; - - if (data_length >= 4) { - const unsigned int num_words = data_length / 4; - crc_calculate_incremental_words((uint32_t*) data, num_words); - - data += num_words * 4; - data_length -= num_words * 4; - } - - if (data_length) { - uint32_t last_word = 0; - for (unsigned int i = 0; i < data_length; ++i) { - last_word = (last_word << 8) | data[i]; - } - crc_value = CRC_CalcCRC(last_word); - } else { - crc_value = CRC_GetCRC(); - } - - return crc_value; -} - -void crc_calculate_incremental_stop(void) { - PBL_ASSERTN(s_initialized); - - disable_crc_clock(); -} - -uint32_t crc_calculate_bytes(const uint8_t* data, unsigned int data_length) { - crc_calculate_incremental_start(); - - // First calculate the CRC of the whole words, since the hardware works 4 - // bytes at a time. - uint32_t* data_words = (uint32_t*) data; - const unsigned int num_words = data_length / 4; - crc_calculate_incremental_words(data_words, num_words); - - const unsigned int num_remaining_bytes = data_length % 4; - const uint32_t res = crc_calculate_incremental_remaining_bytes(data + (num_words * 4), num_remaining_bytes); - crc_calculate_incremental_stop(); - - return (res); -} - -uint32_t crc_calculate_flash(uint32_t address, unsigned int num_bytes) { - crc_calculate_incremental_start(); - const unsigned int chunk_size = 128; - - uint8_t buffer[chunk_size]; - while (num_bytes > chunk_size) { - - flash_read_bytes(buffer, address, chunk_size); - crc_calculate_incremental_words((const uint32_t*) buffer, chunk_size / 4); - - num_bytes -= chunk_size; - address += chunk_size; - } - - flash_read_bytes(buffer, address, num_bytes); - const uint32_t res = crc_calculate_incremental_remaining_bytes(buffer, num_bytes); - crc_calculate_incremental_stop(); - - return (res); -} - -uint8_t crc8_calculate_bytes(const uint8_t *data, unsigned int data_len) { - // Optimal polynomial chosen based on - // http://users.ece.cmu.edu/~koopman/roses/dsn04/koopman04_crc_poly_embedded.pdf - // Note that this is different than the standard CRC-8 polynomial, because the - // standard CRC-8 polynomial is not particularly good. - - // nibble lookup table for (x^8 + x^5 + x^3 + x^2 + x + 1) - static const uint8_t lookup_table[] = - { 0, 47, 94, 113, 188, 147, 226, 205, 87, 120, 9, 38, 235, 196, - 181, 154 }; - - uint16_t crc = 0; - for (int i = data_len * 2; i > 0; i--) { - uint8_t nibble = data[(i - 1)/ 2]; - if (i % 2 == 0) { - nibble >>= 4; - } - int index = nibble ^ (crc >> 4); - crc = lookup_table[index & 0xf] ^ (crc << 4); - } - return crc; -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/dbgserial.c b/platform/snowy/boot/src/drivers/stm32_common/dbgserial.c deleted file mode 100644 index a61451b447..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/dbgserial.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" - -#include "drivers/periph_config.h" -#include "system/passert.h" - -#include "drivers/gpio.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_usart.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_usart.h" -#endif -#include "util/attributes.h" -#include "util/cobs.h" -#include "util/crc32.h" -#include "util/net.h" -#include "util/misc.h" - -#include -#include -#include - -#define MAX_MESSAGE (256) -#define FRAME_DELIMITER '\x55' -#define PULSE_TRANSPORT_PUSH (0x5021) -#define PULSE_PROTOCOL_LOGGING (0x0003) - -static bool s_initialized; - -static const int SERIAL_BAUD_RATE = 1000000; - -typedef struct PACKED PulseFrame { - net16 protocol; - unsigned char information[]; -} PulseFrame; - -typedef struct PACKED PushPacket { - net16 protocol; - net16 length; - unsigned char information[]; -} PushPacket; - -static const unsigned char s_message_header[] = { - // Message type: text - 1, - // Source filename - 'B', 'O', 'O', 'T', 'L', 'O', 'A', 'D', 'E', 'R', 0, 0, 0, 0, 0, 0, - // Log level and task - '*', '*', - // Timestamp - 0, 0, 0, 0, 0, 0, 0, 0, - // Line number - 0, 0, -}; - -static size_t s_message_length = 0; -static unsigned char s_message_buffer[MAX_MESSAGE]; - - -void dbgserial_init(void) { - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - - periph_config_acquire_lock(); - - /* Enable GPIO and UART3 peripheral clocks */ - gpio_use(GPIOC); - periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_USART3); - - //USART_OverSampling8Cmd(USART3, ENABLE); - - /* Connect PXx to USARTx_Tx*/ - GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3); - - /* Connect PXx to USARTx_Rx*/ - GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3); - - /* Configure USART Tx as alternate function */ - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; - GPIO_Init(GPIOC, &GPIO_InitStructure); - - /* Configure USART Rx as alternate function */ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; - GPIO_Init(GPIOC, &GPIO_InitStructure); - - /* USART configuration */ - USART_InitStructure.USART_BaudRate = SERIAL_BAUD_RATE; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(USART3, &USART_InitStructure); - - /* Enable USART */ - USART_Cmd(USART3, ENABLE); - - periph_config_release_lock(); - gpio_release(GPIOC); - - s_initialized = true; -} - -static void prv_putchar(uint8_t c) { - if (!s_initialized) { - return; - } - - while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) continue; - USART_SendData(USART3, c); - while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) continue; -} - -static void prv_flush(void) { - uint32_t crc; - size_t raw_length = sizeof(PulseFrame) + sizeof(PushPacket) + - sizeof(s_message_header) + s_message_length + sizeof(crc); - unsigned char raw_packet[raw_length]; - - PulseFrame *frame = (PulseFrame *)raw_packet; - frame->protocol = hton16(PULSE_TRANSPORT_PUSH); - - PushPacket *transport = (PushPacket *)frame->information; - transport->protocol = hton16(PULSE_PROTOCOL_LOGGING); - transport->length = hton16(sizeof(PushPacket) + sizeof(s_message_header) + - s_message_length); - - unsigned char *app = transport->information; - memcpy(app, s_message_header, sizeof(s_message_header)); - memcpy(&app[sizeof(s_message_header)], s_message_buffer, - s_message_length); - - crc = crc32(CRC32_INIT, raw_packet, raw_length - sizeof(crc)); - memcpy(&raw_packet[raw_length - sizeof(crc)], &crc, sizeof(crc)); - - unsigned char cooked_packet[MAX_SIZE_AFTER_COBS_ENCODING(raw_length)]; - size_t cooked_length = cobs_encode(cooked_packet, raw_packet, raw_length); - - prv_putchar(FRAME_DELIMITER); - for (size_t i = 0; i < cooked_length; ++i) { - if (cooked_packet[i] == FRAME_DELIMITER) { - prv_putchar('\0'); - } else { - prv_putchar(cooked_packet[i]); - } - } - prv_putchar(FRAME_DELIMITER); - - s_message_length = 0; -} - -void dbgserial_putstr(const char* str) { - if (!s_initialized) { - return; - } - - dbgserial_print(str); - prv_flush(); -} - -void dbgserial_print(const char* str) { - if (!s_initialized) { - return; - } - - for (; *str && s_message_length < MAX_MESSAGE; ++str) { - if (*str == '\n') { - prv_flush(); - } else if (*str != '\r') { - s_message_buffer[s_message_length++] = *str; - } - } -} - -void dbgserial_print_hex(uint32_t value) { - char buf[12]; - itoa(value, buf, sizeof(buf)); - dbgserial_print(buf); -} - diff --git a/platform/snowy/boot/src/drivers/stm32_common/gpio.c b/platform/snowy/boot/src/drivers/stm32_common/gpio.c deleted file mode 100644 index 2346cb60ee..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/gpio.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" - -#include - -#define MAX_GPIO (9) - - -static uint8_t s_gpio_clock_count[MAX_GPIO]; - -void gpio_use(GPIO_TypeDef* GPIOx) { - uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - if((idx < MAX_GPIO) && !(s_gpio_clock_count[idx]++)) { - SET_BIT(RCC->AHB1ENR, (0x1 << idx)); - } -} - -void gpio_release(GPIO_TypeDef* GPIOx) { - uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - if((idx < MAX_GPIO) && s_gpio_clock_count[idx] && !(--s_gpio_clock_count[idx])) { - CLEAR_BIT(RCC->AHB1ENR, (0x1 << idx)); - } -} - -void gpio_output_init(OutputConfig pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed) { - GPIO_InitTypeDef init = { - .GPIO_Pin = pin_config.gpio_pin, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(pin_config.gpio); - GPIO_Init(pin_config.gpio, &init); - gpio_release(pin_config.gpio); -} - -void gpio_output_set(OutputConfig pin_config, bool asserted) { - if (!pin_config.active_high) { - asserted = !asserted; - } - gpio_use(pin_config.gpio); - GPIO_WriteBit(pin_config.gpio, pin_config.gpio_pin, - asserted? Bit_SET : Bit_RESET); - gpio_release(pin_config.gpio); -} - -void gpio_af_init(AfConfig af_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config.gpio_pin, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = pupd - }; - - gpio_use(af_config.gpio); - GPIO_Init(af_config.gpio, &init); - GPIO_PinAFConfig(af_config.gpio, af_config.gpio_pin_source, - af_config.gpio_af); - gpio_release(af_config.gpio); -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/i2c.c b/platform/snowy/boot/src/drivers/stm32_common/i2c.c deleted file mode 100644 index 0551120dd0..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/i2c.c +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/misc.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" -#include "system/passert.h" -#include "system/logging.h" -#include "util/delay.h" - -#include - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_i2c.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_i2c.h" -#include "drivers/pmic.h" -#endif - -#define portBASE_TYPE int -#define pdFALSE 0 -#define portEND_SWITCHING_ISR(expr) (void)(expr) - -#define I2C_ERROR_TIMEOUT_MS (1000) -#define I2C_TIMEOUT_ATTEMPTS_MAX (2 * 1000 * 1000) -#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000) -#define I2C_NACK_COUNT_MAX (1000) // MFI NACKs while busy. We delay ~1ms between retries so this is approximately a 1s timeout - -#define I2C_READ_WRITE_BIT (0x01) - -typedef struct I2cTransfer { - uint8_t device_address; - bool read_not_write; //True for read, false for write - uint8_t register_address; - uint8_t size; - uint8_t idx; - uint8_t *data; - enum TransferState { - TRANSFER_STATE_WRITE_ADDRESS_TX, - TRANSFER_STATE_WRITE_REG_ADDRESS, - TRANSFER_STATE_REPEAT_START, - TRANSFER_STATE_WRITE_ADDRESS_RX, - TRANSFER_STATE_WAIT_FOR_DATA, - TRANSFER_STATE_READ_DATA, - TRANSFER_STATE_WRITE_DATA, - TRANSFER_STATE_END_WRITE, - - TRANSFER_STATE_INVALID, - } state; - bool result; - uint16_t nack_count; -}I2cTransfer; - -typedef struct I2cBus{ - I2C_TypeDef *i2c; - uint8_t user_count; - I2cTransfer transfer; - volatile bool busy; -}I2cBus; - -static I2cBus i2c_buses[BOARD_I2C_BUS_COUNT]; - -static uint32_t s_guard_events[] = { - I2C_EVENT_MASTER_MODE_SELECT, - I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, - I2C_EVENT_MASTER_BYTE_TRANSMITTED, - I2C_EVENT_MASTER_MODE_SELECT, - I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, - I2C_EVENT_MASTER_BYTE_RECEIVED, - I2C_EVENT_MASTER_BYTE_TRANSMITTING, - I2C_EVENT_MASTER_BYTE_TRANSMITTED, -}; - -static bool s_initialized = false; - -/*----------------SEMAPHORE/LOCKING FUNCTIONS--------------------------*/ - -static void bus_lock(I2cBus *bus) { -} - -static void bus_unlock(I2cBus *bus) { -} - -static bool semaphore_take(I2cBus *bus) { - return true; -} - -static bool semaphore_wait(I2cBus *bus) { - bus->busy = true; - volatile uint32_t timeout_attempts = I2C_TIMEOUT_ATTEMPTS_MAX; - while ((timeout_attempts-- > 0) && (bus->busy)); - bus->busy = false; - return (timeout_attempts != 0); -} - -static void semaphore_give(I2cBus *bus) { -} - -/*-------------------BUS/PIN CONFIG FUNCTIONS--------------------------*/ - -//! Configure bus power supply control pin as output -//! Lock bus and peripheral config access before configuring pins -void i2c_bus_rail_ctl_config(OutputConfig pin_config) { - gpio_use(pin_config.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = pin_config.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &GPIO_InitStructure); - - gpio_release(pin_config.gpio); -} - -//! Configure bus pins for use by I2C peripheral -//! Lock bus and peripheral config access before configuring pins -static void bus_pin_cfg_i2c(AfConfig pin_config) { - gpio_use(pin_config.gpio); - - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = pin_config.gpio_pin; - gpio_init_struct.GPIO_Mode = GPIO_Mode_AF; - gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz; - gpio_init_struct.GPIO_OType = GPIO_OType_OD; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &gpio_init_struct); - - GPIO_PinAFConfig(pin_config.gpio, pin_config.gpio_pin_source, pin_config.gpio_af); - - gpio_release(pin_config.gpio); -} - -//! Configure bus pin as input -//! Lock bus and peripheral config access before use -static void bus_pin_cfg_input(AfConfig pin_config) { - gpio_use(pin_config.gpio); - - // Configure pin as high impedance input - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = pin_config.gpio_pin; - gpio_init_struct.GPIO_Mode = GPIO_Mode_IN; - gpio_init_struct.GPIO_Speed = GPIO_Speed_2MHz; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &gpio_init_struct); - - gpio_release(pin_config.gpio); -} - -//! Configure bus pin as output -//! Lock bus and peripheral config access before use -static void bus_pin_cfg_output(AfConfig pin_config, bool pin_state) { - gpio_use(pin_config.gpio); - - // Configure pin as output - GPIO_InitTypeDef gpio_init_struct; - gpio_init_struct.GPIO_Pin = pin_config.gpio_pin; - gpio_init_struct.GPIO_Mode = GPIO_Mode_OUT; - gpio_init_struct.GPIO_Speed = GPIO_Speed_2MHz; - gpio_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(pin_config.gpio, &gpio_init_struct); - - // Set bit high or low - GPIO_WriteBit(pin_config.gpio, pin_config.gpio_pin, (pin_state) ? Bit_SET : Bit_RESET); - - gpio_release(pin_config.gpio); -} - -//! Power down I2C bus power supply -//! Always lock bus and peripheral config access before use -static void bus_rail_power_down(uint8_t bus_idx) { - if (BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn == NULL) { - return; - } - - BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn(false); - - // Drain through pull-ups - bus_pin_cfg_output(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl, false); - bus_pin_cfg_output(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda, false); -} - -//! Power up I2C bus power supply -//! Always lock bus and peripheral config access before use -static void bus_rail_power_up(uint8_t bus_idx) { - if (BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn == NULL) { - return; - } - - // check that at least enough time has elapsed since the last turn-off - // TODO: is this necessary in bootloader? - static const uint32_t MIN_STOP_TIME_MS = 10; - delay_ms(MIN_STOP_TIME_MS); - - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl); - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda); - - BOARD_CONFIG.i2c_bus_configs[bus_idx].rail_ctl_fn(true); -} - -//! Initialize the I2C peripheral -//! Lock the bus and peripheral config access before initialization -static void bus_init(uint8_t bus_idx) { - // Initialize peripheral - I2C_InitTypeDef i2c_init_struct; - I2C_StructInit(&i2c_init_struct); - - if (BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_speed > I2C_NORMAL_MODE_CLOCK_SPEED_MAX) { //Fast mode - i2c_init_struct.I2C_DutyCycle = BOARD_CONFIG.i2c_bus_configs[bus_idx].duty_cycle; - } - i2c_init_struct.I2C_ClockSpeed = BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_speed; - i2c_init_struct.I2C_Ack = I2C_Ack_Enable; - - I2C_Init(i2c_buses[bus_idx].i2c, &i2c_init_struct); - I2C_Cmd(i2c_buses[bus_idx].i2c, ENABLE); -} - -//! Configure the bus pins, enable the peripheral clock and initialize the I2C peripheral. -//! Always lock the bus and peripheral config access before enabling it -static void bus_enable(uint8_t bus_idx) { - // Don't power up rail if the bus is already in use (enable can be called to reset bus) - if (i2c_buses[bus_idx].user_count == 0) { - bus_rail_power_up(bus_idx); - } - - bus_pin_cfg_i2c(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl); - bus_pin_cfg_i2c(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda); - - // Enable peripheral clock - periph_config_acquire_lock(); - periph_config_enable(RCC_APB1PeriphClockCmd, BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_ctrl); - periph_config_release_lock(); - - bus_init(bus_idx); -} - -//! De-initialize and gate the clock to the peripheral -//! Power down rail if the bus supports that and no devices are using it -//! Always lock the bus and peripheral config access before disabling it -static void bus_disable(uint8_t bus_idx) { - I2C_DeInit(i2c_buses[bus_idx].i2c); - - periph_config_acquire_lock(); - periph_config_disable(RCC_APB1PeriphClockCmd, BOARD_CONFIG.i2c_bus_configs[bus_idx].clock_ctrl); - periph_config_release_lock(); - - // Do not de-power rail if there are still devices using bus (just reset peripheral and pin configuration during a bus reset) - if (i2c_buses[bus_idx].user_count == 0) { - bus_rail_power_down(bus_idx); - } - else { - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_scl); - bus_pin_cfg_input(BOARD_CONFIG.i2c_bus_configs[bus_idx].i2c_sda); - } -} - -//! Perform a soft reset of the bus -//! Always lock the bus before reset -static void bus_reset(uint8_t bus_idx) { - bus_disable(bus_idx); - bus_enable(bus_idx); -} - -/*---------------INIT/USE/RELEASE/RESET FUNCTIONS----------------------*/ - -void i2c_init(void) { - for (uint32_t i = 0; i < ARRAY_LENGTH(i2c_buses); i++) { - i2c_buses[i].i2c = BOARD_CONFIG.i2c_bus_configs[i].i2c; - i2c_buses[i].user_count = 0; - i2c_buses[i].busy = false; - i2c_buses[i].transfer.idx = 0; - i2c_buses[i].transfer.size = 0; - i2c_buses[i].transfer.data = NULL; - i2c_buses[i].transfer.state = TRANSFER_STATE_INVALID; - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = BOARD_CONFIG.i2c_bus_configs[i].ev_irq_channel; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0c; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - NVIC_InitStructure.NVIC_IRQChannel = BOARD_CONFIG.i2c_bus_configs[i].er_irq_channel; - NVIC_Init(&NVIC_InitStructure); - - I2C_DeInit(i2c_buses[i].i2c); - } - - s_initialized = true; - - for (uint32_t i = 0; i < ARRAY_LENGTH(i2c_buses); i++) { - if (BOARD_CONFIG.i2c_bus_configs[i].rail_cfg_fn) { - BOARD_CONFIG.i2c_bus_configs[i].rail_cfg_fn(); - } - if (BOARD_CONFIG.i2c_bus_configs[i].rail_ctl_fn) { - bus_rail_power_down(i); - } - } -} - -void i2c_use(I2cDevice device_id) { - PBL_ASSERTN(s_initialized); - PBL_ASSERT(device_id < BOARD_CONFIG.i2c_device_count, "I2C device ID out of bounds %d (max: %d)", - device_id, BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - bus_lock(bus); - - if (bus->user_count == 0) { - bus_enable(bus_idx); - } - bus->user_count++; - - bus_unlock(bus); -} - -void i2c_release(I2cDevice device_id) { - PBL_ASSERTN(s_initialized); - PBL_ASSERTN(device_id < BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - bus_lock(bus); - - if (bus->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted release of disabled bus %d by device %d", bus_idx, device_id); - bus_unlock(bus); - return; - } - - bus->user_count--; - if (bus->user_count == 0) { - bus_disable(bus_idx); - } - - bus_unlock(bus); -} - -void i2c_reset(I2cDevice device_id) { - PBL_ASSERTN(s_initialized); - PBL_ASSERTN(device_id < BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - // Take control of bus; only one task may use bus at a time - bus_lock(bus); - - if (bus->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted reset of disabled bus %d by device %d", bus_idx, device_id); - bus_unlock(bus); - return; - } - - PBL_LOG(LOG_LEVEL_WARNING, "Resetting I2C bus %" PRId8, bus_idx); - - // decrement user count for reset so that if this user is the only user, the - // bus will be powered down during the reset - bus->user_count--; - - // Reset and reconfigure bus and pins - bus_reset(bus_idx); - - //Restore user count - bus->user_count++; - - bus_unlock(bus); -} - -/*--------------------DATA TRANSFER FUNCTIONS--------------------------*/ - -//! Wait a short amount of time for busy bit to clear -static bool wait_for_busy_clear(uint8_t bus_idx) { - unsigned int attempts = I2C_TIMEOUT_ATTEMPTS_MAX; - while((i2c_buses[bus_idx].i2c->SR2 & I2C_SR2_BUSY) != 0) { - --attempts; - if (!attempts) { - return false; - } - } - - return true; -} - -//! Abort the transfer -//! Should only be called when the bus is locked -static void abort_transfer(I2cBus *bus) { - // Disable all interrupts on the bus - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); - // Generate a stop condition - bus->i2c->CR1 |= I2C_CR1_STOP; - bus->transfer.state = TRANSFER_STATE_INVALID; -} - -//! Set up and start a transfer to a device, wait for it to finish and clean up after the transfer has completed -static bool do_transfer(I2cDevice device_id, bool read_not_write, uint8_t device_address, uint8_t register_address, uint8_t size, uint8_t *data) { - PBL_ASSERTN(s_initialized); - PBL_ASSERTN(device_id < BOARD_CONFIG.i2c_device_count); - - uint8_t bus_idx = BOARD_CONFIG.i2c_device_map[device_id]; - I2cBus *bus = &i2c_buses[bus_idx]; - - // Take control of bus; only one task may use bus at a time - bus_lock(bus); - - if (bus->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted access to disabled bus %d by device %d", bus_idx, device_id); - bus_unlock(bus); - return false; - } - - // If bus is busy (it shouldn't be as this function waits for the bus to report a non-idle state - // before exiting) reset the bus and wait for it to become not-busy - // Exit if bus remains busy. User module should reset the I2C module at this point - if((bus->i2c->SR2 & I2C_SR2_BUSY) != 0) { - bus_reset(bus_idx); - - if (!wait_for_busy_clear(bus_idx)) { - // Bus did not recover after reset - bus_unlock(bus); - return false; - } - } - - // Take binary semaphore so that next take will block - PBL_ASSERT(semaphore_take(bus), "Could not acquire semaphore token"); - - // Set up transfer - bus->transfer.device_address = device_address; - bus->transfer.register_address = register_address; - bus->transfer.read_not_write = read_not_write; - bus->transfer.size = size; - bus->transfer.idx = 0; - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_TX; - bus->transfer.data = data; - bus->transfer.nack_count = 0; - - // Ack received bytes - I2C_AcknowledgeConfig(bus->i2c, ENABLE); - - bool result = false; - - do { - // Generate start event - bus->i2c->CR1 |= I2C_CR1_START; - //Enable event and error interrupts - bus->i2c->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN; - - // Wait on semaphore until it is released by interrupt or a timeout occurs - if (semaphore_wait(bus)) { - - if (bus->transfer.state == TRANSFER_STATE_INVALID) { - // Transfer is complete - result = bus->transfer.result; - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error on bus %" PRId8, bus_idx); - } - - } else if (bus->transfer.nack_count < I2C_NACK_COUNT_MAX) { - // NACK received after start condition sent: the MFI chip NACKs start conditions whilst it is busy - // Retry start condition after a short delay. - // A NACK count is incremented for each NACK received, so that legitimate NACK - // errors cause the transfer to be aborted (after the NACK count max has been reached). - - bus->transfer.nack_count++; - - delay_ms(1); - - } else { - // Too many NACKs received, abort transfer - abort_transfer(bus); - break; - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error: too many NACKs received on bus %" PRId8, bus_idx); - } - - } else { - // Timeout, abort transfer - abort_transfer(bus); - break; - PBL_LOG(LOG_LEVEL_ERROR, "Transfer timed out on bus %" PRId8, bus_idx); - } - } while (bus->transfer.state != TRANSFER_STATE_INVALID); - - // Return semaphore token so another transfer can be started - semaphore_give(bus); - - // Wait for bus to to clear the busy flag before a new transfer starts - // Theoretically a transfer could complete successfully, but the busy flag never clears, - // which would cause the next transfer to fail - if (!wait_for_busy_clear(bus_idx)) { - // Reset I2C bus if busy flag does not clear - bus_reset(bus_idx); - } - - bus_unlock(bus); - - return result; -} - -bool i2c_read_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, uint8_t *result) { - return i2c_read_register_block(device_id, i2c_device_address, register_address, 1, result); -} - -bool i2c_read_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t - register_address_start, uint8_t read_size, uint8_t* result_buffer) { -#if defined(TARGET_QEMU) - PBL_LOG(LOG_LEVEL_DEBUG, "i2c reads on QEMU not supported"); - return false; -#endif - - // Do transfer locks the bus - bool result = do_transfer(device_id, true, i2c_device_address, register_address_start, read_size, result_buffer); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Read failed on bus %" PRId8, BOARD_CONFIG.i2c_device_map[device_id]); - } - - return result; -} - -bool i2c_write_register(I2cDevice device_id, uint8_t i2c_device_address, uint8_t register_address, - uint8_t value) { - return i2c_write_register_block(device_id, i2c_device_address, register_address, 1, &value); -} - -bool i2c_write_register_block(I2cDevice device_id, uint8_t i2c_device_address, uint8_t - register_address_start, uint8_t write_size, const uint8_t* buffer) { -#if defined(TARGET_QEMU) - PBL_LOG(LOG_LEVEL_DEBUG, "i2c writes on QEMU not supported"); - return false; -#endif - - // Do transfer locks the bus - bool result = do_transfer(device_id, false, i2c_device_address, register_address_start, write_size, (uint8_t*)buffer); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Write failed on bus %" PRId8, BOARD_CONFIG.i2c_device_map[device_id]); - } - - return result; -} - -/*------------------------INTERRUPT FUNCTIONS--------------------------*/ - -//! End a transfer and disable further interrupts -//! Only call from interrupt functions -static portBASE_TYPE end_transfer_irq(I2cBus *bus, bool result) { - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); - bus->i2c->CR1 |= I2C_CR1_STOP; - bus->transfer.result = result; - bus->transfer.state = TRANSFER_STATE_INVALID; - - bus->busy = false; - return pdFALSE; -} - -//! Pause a transfer, disabling interrupts during the pause -//! Only call from interrupt functions -static portBASE_TYPE pause_transfer_irq(I2cBus *bus) { - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); - bus->busy = false; - return pdFALSE; -} - -//! Handle an IRQ event on the specified \a bus -static portBASE_TYPE irq_event_handler(I2cBus *bus) { - if (bus->transfer.state == TRANSFER_STATE_INVALID) { - - // Disable interrupts if spurious interrupt received - bus->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN); - return pdFALSE; - } - - // Check that the expected event occurred - if (I2C_CheckEvent(bus->i2c, s_guard_events[bus->transfer.state]) == ERROR) { - // Ignore interrupt - A spurious byte transmitted event as well as an interrupt with no - // discernible event associated with it occur after repeat start events are generated - return pdFALSE; - } - portBASE_TYPE should_context_switch = pdFALSE; - - switch (bus->transfer.state) { - case TRANSFER_STATE_WRITE_ADDRESS_TX: - // Write the i2c device address to the bus to select it in write mode. - bus->i2c->DR = bus->transfer.device_address & ~I2C_READ_WRITE_BIT; - bus->transfer.state = TRANSFER_STATE_WRITE_REG_ADDRESS; - break; - - case TRANSFER_STATE_WRITE_REG_ADDRESS: - // Write the register address - bus->i2c->DR = bus->transfer.register_address; - - if (bus->transfer.read_not_write) { - bus->transfer.state = TRANSFER_STATE_REPEAT_START; - } else { - // Enable TXE interrupt for writing - bus->i2c->CR2 |= I2C_CR2_ITBUFEN; - bus->transfer.state = TRANSFER_STATE_WRITE_DATA; - } - break; - - case TRANSFER_STATE_REPEAT_START: - // Generate a repeat start - bus->i2c->CR1 |= I2C_CR1_START; - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_RX; - break; - - case TRANSFER_STATE_WRITE_ADDRESS_RX: - // Write the I2C device address again, but this time in read mode. - bus->i2c->DR = bus->transfer.device_address | I2C_READ_WRITE_BIT; - if (bus->transfer.size == 1) { - // Last byte, we want to NACK this one to tell the slave to stop sending us bytes. - bus->i2c->CR1 &= ~I2C_CR1_ACK; - } - bus->transfer.state = TRANSFER_STATE_WAIT_FOR_DATA; - break; - - case TRANSFER_STATE_WAIT_FOR_DATA: - //This state just ensures that the transition to receive mode event happened - - // Enable RXNE interrupt for writing - bus->i2c->CR2 |= I2C_CR2_ITBUFEN; - bus->transfer.state = TRANSFER_STATE_READ_DATA; - break; - - case TRANSFER_STATE_READ_DATA: - bus->transfer.data[bus->transfer.idx] = bus->i2c->DR; - bus->transfer.idx++; - - if (bus->transfer.idx + 1 == bus->transfer.size) { - // Last byte, we want to NACK this one to tell the slave to stop sending us bytes. - bus->i2c->CR1 &= ~I2C_CR1_ACK; - } - else if (bus->transfer.idx == bus->transfer.size) { - // End transfer after all bytes have been received - bus->i2c->CR2 &= ~I2C_CR2_ITBUFEN; - should_context_switch = end_transfer_irq(bus, true); - break; - } - - break; - - case TRANSFER_STATE_WRITE_DATA: - bus->i2c->DR = bus->transfer.data[bus->transfer.idx]; - bus->transfer.idx++; - if (bus->transfer.idx == bus->transfer.size) { - bus->i2c->CR2 &= ~I2C_CR2_ITBUFEN; - bus->transfer.state = TRANSFER_STATE_END_WRITE; - break; - } - break; - - case TRANSFER_STATE_END_WRITE: - // End transfer after all bytes have been sent - should_context_switch = end_transfer_irq(bus, true); - break; - - default: - // Abort transfer from invalid state - should never reach here (state machine logic broken) - should_context_switch = end_transfer_irq(bus, false); - break; - } - - return should_context_switch; -} - -//! Handle error interrupt on the specified \a bus -static portBASE_TYPE irq_error_handler(I2cBus *bus) { - if (bus->transfer.state == TRANSFER_STATE_INVALID) { - - // Disable interrupts if spurious interrupt received - bus->i2c->CR2 &= ~I2C_CR2_ITERREN; - return pdFALSE; - } - - // Data overrun and bus errors can only really be handled by terminating the transfer and - // trying to recover bus to an idle state. Each error will be logged. In each case a stop - // condition will be sent and then we will wait on the busy flag to clear (if it doesn't, - // a soft reset of the bus will be performed (handled in wait i2c_do_transfer). - - if ((bus->i2c->SR1 & I2C_SR1_OVR) != 0) { - bus->i2c->SR1 &= ~I2C_SR1_OVR; - - // Data overrun - PBL_LOG(LOG_LEVEL_ERROR, "Data overrun during I2C transaction; Bus: 0x%p", bus->i2c); - } - if ((bus->i2c->SR1 & I2C_SR1_BERR) != 0) { - bus->i2c->SR1 &= ~I2C_SR1_BERR; - - // Bus error: invalid start or stop condition detected - PBL_LOG(LOG_LEVEL_ERROR, "Bus error detected during I2C transaction; Bus: 0x%p", bus->i2c); - } - if ((bus->i2c->SR1 & I2C_SR1_AF) != 0) { - bus->i2c->SR1 &= ~I2C_SR1_AF; - - // NACK received. - // - // The MFI chip will cause NACK errors during read operations after writing a start bit (first start - // or repeat start indicating that it is busy. The transfer must be paused and the start condition sent - // again after a delay and the state machine set back a step. - // - // If the NACK is received after any other action log an error and abort the transfer - - - if (bus->transfer.state == TRANSFER_STATE_WAIT_FOR_DATA) { - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_RX; - return pause_transfer_irq(bus); - } - else if (bus->transfer.state == TRANSFER_STATE_WRITE_REG_ADDRESS){ - bus->transfer.state = TRANSFER_STATE_WRITE_ADDRESS_TX; - return pause_transfer_irq(bus); - } - else { - PBL_LOG(LOG_LEVEL_ERROR, "NACK received during I2C transfer; Bus: 0x%p", bus->i2c); - } - } - - return end_transfer_irq(bus, false); - -} - -void I2C1_EV_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_event_handler(&i2c_buses[0])); -} - -void I2C1_ER_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_error_handler(&i2c_buses[0])); -} - -void I2C2_EV_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_event_handler(&i2c_buses[1])); -} - -void I2C2_ER_IRQHandler(void) { - portEND_SWITCHING_ISR(irq_error_handler(&i2c_buses[1])); -} - -/*------------------------COMMAND FUNCTIONS--------------------------*/ - -void command_power_2v5(char *arg) { - // Intentionally ignore the s_running_count and make it so! - // This is intended for low level electrical test only - if(!strcmp("on", arg)) { - bus_rail_power_up(1); - } else { - bus_rail_power_down(1); - } -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/i2c_private.c b/platform/snowy/boot/src/drivers/stm32_common/i2c_private.c deleted file mode 100644 index 194b181707..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/i2c_private.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "drivers/pmic.h" -#include "drivers/gpio.h" -#include "util/delay.h" -#include "board/board.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_i2c.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_i2c.h" -#include "drivers/pmic.h" -#endif - -extern void i2c_bus_rail_ctl_config(OutputConfig pin_config); - -static void do_rail_power(bool up, GPIO_TypeDef* const gpio, const uint32_t gpio_pin, const bool active_high) { - if (up) { - gpio_use(gpio); - // enable the bus supply - GPIO_WriteBit(gpio, gpio_pin, active_high ? Bit_SET : Bit_RESET); - - // wait for the bus supply to stabilize and the peripherals to start up. - // the MFI chip requires its reset pin to be stable for at least 10ms from startup. - delay_ms(20); - gpio_release(gpio); - } else { - gpio_use(gpio); - // disable the bus supply - GPIO_WriteBit(gpio, gpio_pin, active_high ? Bit_RESET : Bit_SET); - gpio_release(gpio); - } -} - -// SNOWY -///////// -void snowy_i2c_rail_1_ctl_fn(bool enable) { - set_ldo3_power_state(enable); -} - -// bb2 -///////// -void bb2_rail_ctl_fn(bool enable) { - do_rail_power(enable, GPIOH, GPIO_Pin_0, true); -} - -void bb2_rail_cfg_fn(void) { - i2c_bus_rail_ctl_config((OutputConfig){ GPIOH, GPIO_Pin_0, true}); -} - -// v1_5 -///////// -void v1_5_rail_ctl_fn(bool enable) { - do_rail_power(enable, GPIOH, GPIO_Pin_0, true); -} - -void v1_5_rail_cfg_fn(void) { - i2c_bus_rail_ctl_config((OutputConfig){ GPIOH, GPIO_Pin_0, true}); -} - -// v2_0 -///////// -void v2_0_rail_ctl_fn(bool enable) { - do_rail_power(enable, GPIOH, GPIO_Pin_0, true); -} - -void v2_0_rail_cfg_fn(void) { - i2c_bus_rail_ctl_config((OutputConfig){ GPIOH, GPIO_Pin_0, true}); -} - -// ev2_4 -///////// -void ev2_4_rail_ctl_fn(bool enable) { - do_rail_power(enable, GPIOH, GPIO_Pin_0, true); -} - -void ev2_4_rail_cfg_fn(void) { - i2c_bus_rail_ctl_config((OutputConfig){ GPIOH, GPIO_Pin_0, true}); -} - -// bigboard -//////////// -void bigboard_rail_ctl_fn(bool enable) { - do_rail_power(enable, GPIOC, GPIO_Pin_5, true); -} - -void bigboard_rail_cfg_fn(void) { - i2c_bus_rail_ctl_config((OutputConfig){ GPIOC, GPIO_Pin_5, true}); -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/spi.c b/platform/snowy/boot/src/drivers/stm32_common/spi.c deleted file mode 100644 index 604eaaa868..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/spi.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/spi.h" - -#include "util/misc.h" -#include "system/passert.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_rcc.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_rcc.h" -#endif - -// Deduced by looking at the prescalers in stm32f2xx_spi.h -#define SPI_FREQ_LOG_TO_PRESCALER(LG) (((LG) - 1) * 0x8) - -uint16_t spi_find_prescaler(uint32_t bus_frequency, SpiPeriphClock periph_clock) { - // Get the clocks - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - - uint32_t clock = 0; - // Find which peripheral clock we belong to - if (periph_clock == SpiPeriphClockAPB1) { - clock = clocks.PCLK1_Frequency; - } else if (periph_clock == SpiPeriphClockAPB2) { - clock = clocks.PCLK2_Frequency; - } else { - WTF; - } - - int lg; - if (bus_frequency > (clock / 2)) { - lg = 1; // Underclock to the highest possible frequency - } else { - uint32_t divisor = clock / bus_frequency; - lg = ceil_log_two(divisor); - } - - // Prescalers only exists for values in [2 - 256] range - PBL_ASSERTN(lg > 0); - PBL_ASSERTN(lg < 9); - - // return prescaler - return (SPI_FREQ_LOG_TO_PRESCALER(lg)); -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/system_flash.c b/platform/snowy/boot/src/drivers/stm32_common/system_flash.c deleted file mode 100644 index 30471f1a15..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/system_flash.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/system_flash.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_flash.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_flash.h" -#endif - -static uint16_t s_sectors[] = { - FLASH_Sector_0, FLASH_Sector_1, FLASH_Sector_2, FLASH_Sector_3, - FLASH_Sector_4, FLASH_Sector_5, FLASH_Sector_6, FLASH_Sector_7, - FLASH_Sector_8, FLASH_Sector_9, FLASH_Sector_10, FLASH_Sector_11 }; -static uint32_t s_sector_addresses[] = { - ADDR_FLASH_SECTOR_0, ADDR_FLASH_SECTOR_1, ADDR_FLASH_SECTOR_2, - ADDR_FLASH_SECTOR_3, ADDR_FLASH_SECTOR_4, ADDR_FLASH_SECTOR_5, - ADDR_FLASH_SECTOR_6, ADDR_FLASH_SECTOR_7, ADDR_FLASH_SECTOR_8, - ADDR_FLASH_SECTOR_9, ADDR_FLASH_SECTOR_10, ADDR_FLASH_SECTOR_11 }; - -int prv_get_sector_num_for_address(uint32_t address) { - if (address < s_sector_addresses[0]) { - dbgserial_print("address "); - dbgserial_print_hex(address); - dbgserial_print(" is outside system flash\r\n"); - return -1; - } - for (size_t i=0; i < ARRAY_LENGTH(s_sector_addresses)-1; ++i) { - if (s_sector_addresses[i] <= address - && address < s_sector_addresses[i+1]) { - return i; - } - } - return ARRAY_LENGTH(s_sector_addresses)-1; -} - -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - dbgserial_print("system_flash_erase("); - dbgserial_print_hex(address); - dbgserial_print(", "); - dbgserial_print_hex(length); - dbgserial_print(")\r\n"); - - if (length == 0) { - // Nothing to do - return true; - } - - int first_sector = prv_get_sector_num_for_address(address); - int last_sector = prv_get_sector_num_for_address(address + length - 1); - if (first_sector < 0 || last_sector < 0) { - return false; - } - int count = last_sector - first_sector + 1; - if (progress_callback) { - progress_callback(0, count, progress_context); - } - - FLASH_Unlock(); - for (int sector = first_sector; sector <= last_sector; ++sector) { - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - if (FLASH_EraseSector( - s_sectors[sector], VoltageRange_1) != FLASH_COMPLETE) { - dbgserial_print("failed to erase sector "); - dbgserial_print_hex(sector); - dbgserial_putstr(""); - FLASH_Lock(); - return false; - } - if (progress_callback) { - progress_callback(sector - first_sector + 1, count, progress_context); - } - } - FLASH_Lock(); - return true; -} - -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - dbgserial_print("system_flash_write("); - dbgserial_print_hex(address); - dbgserial_print(", "); - dbgserial_print_hex(length); - dbgserial_print(")\r\n"); - - FLASH_Unlock(); - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); - - const uint8_t *data_array = data; - for (uint32_t i = 0; i < length; ++i) { - if (FLASH_ProgramByte(address + i, data_array[i]) != FLASH_COMPLETE) { - dbgserial_print("failed to write address "); - dbgserial_print_hex(address); - dbgserial_putstr(""); - FLASH_Lock(); - return false; - } - if (progress_callback && i % 128 == 0) { - progress_callback(i/128, length/128, progress_context); - } - } - FLASH_Lock(); - return true; -} - -uint32_t system_flash_read(uint32_t address) { - uint32_t data = *(volatile uint32_t*) address; - return data; -} diff --git a/platform/snowy/boot/src/drivers/stm32_common/watchdog.c b/platform/snowy/boot/src/drivers/stm32_common/watchdog.c deleted file mode 100644 index 0bacbf0ab8..0000000000 --- a/platform/snowy/boot/src/drivers/stm32_common/watchdog.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/watchdog.h" - -#include "util/bitset.h" -#include "system/logging.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_dbgmcu.h" -#include "stm32f2xx_iwdg.h" -#include "stm32f2xx_rcc.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_dbgmcu.h" -#include "stm32f4xx_iwdg.h" -#include "stm32f4xx_rcc.h" -#endif - -#include - -void watchdog_init(void) { - IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); - - IWDG_SetPrescaler(IWDG_Prescaler_64); // ~8 seconds - IWDG_SetReload(0xfff); - - IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); - - DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP, ENABLE); -} - -void watchdog_start(void) { - IWDG_Enable(); - watchdog_feed(); -} - -// This behaves differently from the bootloader and the firmware. -void watchdog_feed(void) { - IWDG_ReloadCounter(); -} - -bool watchdog_check_reset_flag(void) { - return RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET; -} - -void watchdog_clear_reset_flag(void) { - RCC_ClearFlag(); -} diff --git a/platform/snowy/boot/src/drivers/system_flash.h b/platform/snowy/boot/src/drivers/system_flash.h deleted file mode 100644 index 71567faf4d..0000000000 --- a/platform/snowy/boot/src/drivers/system_flash.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx_flash.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx_flash.h" -#endif - -#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */ -#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */ - -// stm32f2xx only has 512k of system flash, these sectors don't exist -#if defined(MICRO_FAMILY_STM32F4) -#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */ -#endif - -typedef void (*SystemFlashProgressCb)( - uint32_t progress, uint32_t total, void *context); - -// Erase the sectors of flash which lie within the given address range. -// -// If the address range overlaps even one single byte of a sector, the entire -// sector is erased. -// -// If progress_callback is not NULL, it is called at the beginning of the erase -// process and after each sector is erased. The rational number (progress/total) -// increases monotonically as the sector erasue procedure progresses. -// -// Returns true if successful, false if an error occurred. -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); - -// Write data into flash. The flash must already be erased. -// -// If progress_callback is not NULL, it is called at the beginning of the -// writing process and periodically thereafter. The rational number -// (progress/total) increases monotonically as the data is written. -// -// Returns true if successful, false if an error occurred. -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); - -uint32_t system_flash_read(uint32_t address); diff --git a/platform/snowy/boot/src/drivers/watchdog.h b/platform/snowy/boot/src/drivers/watchdog.h deleted file mode 100644 index 21acfce3a7..0000000000 --- a/platform/snowy/boot/src/drivers/watchdog.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void watchdog_init(void); -void watchdog_start(void); - -void watchdog_feed(void); - -bool watchdog_check_reset_flag(void); -void watchdog_clear_reset_flag(void); diff --git a/platform/snowy/boot/src/firmware.h b/platform/snowy/boot/src/firmware.h deleted file mode 100644 index a57140343a..0000000000 --- a/platform/snowy/boot/src/firmware.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "stm32f4xx.h" - -#define FIRMWARE_BASE (FLASH_BASE + (BOOTLOADER_LENGTH)) diff --git a/platform/snowy/boot/src/flash_region.h b/platform/snowy/boot/src/flash_region.h deleted file mode 100644 index b6367ff14f..0000000000 --- a/platform/snowy/boot/src/flash_region.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define SECTOR_SIZE_BYTES 0x20000 -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define SUBSECTOR_SIZE_BYTES 0x20000 -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) - -// Filesystem layout -/////////////////////////////////////// - -// Space for our flash logs -// NOTE: This range of memory is actually in the special "bottom boot" area of our flash chip -// where the erase sectors are smaller (32k instead of 128k everywhere else). -#define FLASH_REGION_DEBUG_DB_BEGIN 0x0 -#define FLASH_REGION_DEBUG_DB_END 0x20000 // 128k - -// Regions after this point are in standard, 128kb sized sectors. - -// 768kb gap here. We should save some space for non-filesystem things. It also aligns the -// subsequent sectors nicely. - -// 1 128kb sector for storing mfg info, see fw/mfg/snowy/mfg_info.c -#define FLASH_REGION_MFG_INFO_BEGIN 0x0e0000 -#define FLASH_REGION_MFG_INFO_END 0x100000 - -// Scratch space for firmware images (normal and recovery). -#define FLASH_REGION_FIRMWARE_SCRATCH_BEGIN 0x100000 -#define FLASH_REGION_FIRMWARE_SCRATCH_END 0x200000 // 1024k - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x200000 -#define FLASH_REGION_SAFE_FIRMWARE_END 0x300000 // 1024k - -#define FLASH_REGION_NEXT_SYSTEM_RESOURCES_BEGIN 0x300000 -#define FLASH_REGION_NEXT_SYSTEM_RESOURCES_END 0x380000 // 512k - -#define FLASH_REGION_SYSTEM_RESOURCES_BEGIN 0x380000 -#define FLASH_REGION_SYSTEM_RESOURCES_END 0x400000 // 512k - -// FIXME: The addresses below here are hacky work arounds and hopefully not the final place -// for these things. Hopefully many of them can move to the filesystem. Everything above here -// should be pretty stable. - -#define REGISTRY_FLASH_BEGIN 0x400000 // -#define REGISTRY_FLASH_END 0x420000 // 128k - -#define FACTORY_REGISTRY_FLASH_BEGIN 0x420000 // -#define FACTORY_REGISTRY_FLASH_END 0x440000 // 128k - -#define FLASH_REGION_UNUSED0_BEGIN 0x440000 -#define FLASH_REGION_UNUSED0_END 0x480000 // 256k - -#define FLASH_REGION_FILESYSTEM_BEGIN 0x0480000 -#define FLASH_REGION_FILESYSTEM_END 0x1000000 // 8mb+, aka the rest! - -// Constants used for testing flash interface -// NOTE: This purposely overlaps the file system region since the flash test requires a non-critical -// region to operate on. Data in this region will get corrupted and will not get restored after the -// test runs. Any data in this region will have to be manually restored or reinitialized. -#define FLASH_TEST_ADDR_START 0x0800000 // 8MB -#define FLASH_TEST_ADDR_END 0x1000000 // 16MB -#define FLASH_TEST_ADDR_MSK 0x1FFFFFF // test all bits in the 16MB range - - -#if ((FLASH_REGION_FILESYSTEM_BEGIN > FLASH_TEST_ADDR_START) || (FLASH_REGION_FILESYSTEM_END < FLASH_TEST_ADDR_END)) -#error "ERROR: Flash Test space not withing expected range" -#endif - -// 0x1000000 is the end of the SPI flash address space. - -// FIXME: These regions are used by code paths that are never executed on this flash part. Give -// them bogus values so things still compile -#define FLASH_REGION_APP_BEGIN 0x1000000 -#define FLASH_REGION_APP_END 0x1020000 - -#define FLASH_REGION_APP_RESOURCES_BEGIN 0x1020000 -#define FLASH_REGION_APP_RESOURCES_END 0x1040000 - -#define FLASH_REGION_MIGRATION_BEGIN 0x1040000 -#define FLASH_REGION_MIGRATION_END 0x1060000 diff --git a/platform/snowy/boot/src/fw_copy.c b/platform/snowy/boot/src/fw_copy.c deleted file mode 100644 index cd70453762..0000000000 --- a/platform/snowy/boot/src/fw_copy.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fw_copy.h" - -#include "drivers/crc.h" -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash/s29vs.h" -#include "drivers/system_flash.h" -#include "firmware.h" -#include "flash_region.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/reset.h" -#include "util/misc.h" -#include "util/delay.h" - -#include -#include - -static bool check_valid_firmware_crc(uint32_t flash_address, FirmwareDescription* desc) { - dbgserial_putstr("Checksumming firmware update"); - uint32_t crc = crc_calculate_flash(flash_address, desc->firmware_length); - return crc == desc->checksum; -} - -static void prv_display_erase_progress( - uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress, total * 2); -} - -static bool erase_old_firmware(uint32_t firmware_length) { - dbgserial_putstr("erase_old_firmware"); - return system_flash_erase( - FIRMWARE_BASE, firmware_length, prv_display_erase_progress, 0); -} - -static void prv_display_write_progress( - uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress/2 + total/2, total); -} - -static bool write_new_firmware( - uint32_t firmware_start_address, uint32_t firmware_length) { - dbgserial_putstr("write_new_firmware"); - return system_flash_write( - FIRMWARE_BASE, (void *)(FMC_BANK_1_BASE_ADDRESS + firmware_start_address), - firmware_length, prv_display_write_progress, 0); -} - -static bool check_firmware_crc(FirmwareDescription* firmware_description) { - dbgserial_print("Checksumming "); - dbgserial_print_hex(firmware_description->firmware_length); - dbgserial_print(" bytes\r\n"); - - uint32_t calculated_crc = crc_calculate_bytes( - (const uint8_t*) FIRMWARE_BASE, firmware_description->firmware_length); - - dbgserial_print("Checksum - wanted "); - dbgserial_print_hex(firmware_description->checksum); - dbgserial_print(" got "); - dbgserial_print_hex(calculated_crc); - dbgserial_putstr(""); - - return calculated_crc == firmware_description->checksum; -} - -typedef enum UpdateFirmwareResult { - UPDATE_FW_SUCCESS = 0, - UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED = 1, - UPDATE_FW_ERROR_MICRO_FLASH_MANGLED = 2 -} UpdateFirmwareResult; - -static UpdateFirmwareResult update_fw(uint32_t flash_address) { - crc_init(); - - display_firmware_update_progress(0, 1); - boot_bit_set(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - - FirmwareDescription firmware_description = - firmware_storage_read_firmware_description(flash_address); - - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - dbgserial_putstr("Invalid firmware description!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - if (!check_valid_firmware_crc( - flash_address + sizeof(FirmwareDescription), &firmware_description)) { - dbgserial_putstr("Invalid firmware CRC in SPI flash!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - erase_old_firmware(firmware_description.firmware_length); - - write_new_firmware( - flash_address + sizeof(FirmwareDescription), - firmware_description.firmware_length); - - if (!check_firmware_crc(&firmware_description)) { - dbgserial_putstr( - "Our internal flash contents are bad (checksum failed)! " - "This is really bad!"); - return UPDATE_FW_ERROR_MICRO_FLASH_MANGLED; - } - - return UPDATE_FW_SUCCESS; -} - -void check_update_fw(void) { - if (!boot_bit_test(BOOT_BIT_NEW_FW_AVAILABLE)) { - return; - } - - if (boot_bit_test(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS)) { - dbgserial_putstr("Our previous firmware update failed, aborting update."); - - // Pretend like the new firmware bit wasn't set afterall. We'll just run the - // previous code, whether that was normal firmware or the recovery firmware. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_clear(BOOT_BIT_NEW_FW_INSTALLED); - return; - } - - - dbgserial_putstr("New firmware is available!"); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - - UpdateFirmwareResult result = update_fw(FLASH_REGION_FIRMWARE_SCRATCH_BEGIN); - switch (result) { - case UPDATE_FW_SUCCESS: - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - // Our firmware update failed in a way that didn't break our previous - // firmware. Just run the previous code, whether that was normal firmware - // or the recovery firmware. - break; - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // We've broken our internal flash when trying to update our normal - // firmware. Fall back immediately to the recovery firmare. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - system_reset(); - return; - } - - // Done, we're ready to boot. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_set(BOOT_BIT_NEW_FW_INSTALLED); -} - -bool switch_to_recovery_fw() { - dbgserial_putstr("Loading recovery firmware"); - - UpdateFirmwareResult result = update_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - bool recovery_fw_ok = true; - switch (result) { - case UPDATE_FW_SUCCESS: - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // Keep us booting into recovery firmware. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - - if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to load recovery firmware, strike one. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO)) { - dbgserial_putstr("Failed to load recovery firmware, strike two. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else { - dbgserial_putstr("Failed to load recovery firmware, strike three. SAD WATCH"); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - recovery_fw_ok = false; - } - break; - } - - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - return recovery_fw_ok; -} diff --git a/platform/snowy/boot/src/fw_copy.h b/platform/snowy/boot/src/fw_copy.h deleted file mode 100644 index 7e8c9b6305..0000000000 --- a/platform/snowy/boot/src/fw_copy.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void check_update_fw(void); - -//! @return false if we've failed to load recovery mode too many times and we should just sadwatch -bool switch_to_recovery_fw(void); - diff --git a/platform/snowy/boot/src/git_version.auto.h.in b/platform/snowy/boot/src/git_version.auto.h.in deleted file mode 100644 index 92c500ceb9..0000000000 --- a/platform/snowy/boot/src/git_version.auto.h.in +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#define GIT_TIMESTAMP @TIMESTAMP@ -#define GIT_TAG "@TAG@" -#define GIT_MAJOR_VERSION @MAJOR_VERSION@ -#define GIT_MINOR_VERSION @MINOR_VERSION@ -#define GIT_PATCH_VERSION @PATCH_VERSION@ -#define GIT_REVISION "@COMMIT@" - diff --git a/platform/snowy/boot/src/hardfault_handler.c b/platform/snowy/boot/src/hardfault_handler.c deleted file mode 100644 index 8fbf09cacf..0000000000 --- a/platform/snowy/boot/src/hardfault_handler.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "util/misc.h" -#include "system/die.h" -#include "system/reset.h" - -#include "misc.h" -#include "stm32f4xx.h" - -#include -#include -#include - -void hard_fault_handler_c(unsigned int* hardfault_args) { - dbgserial_putstr("HARD FAULT"); - -#ifdef NO_WATCHDOG - __BKPT(); - while (1) continue; -#else - system_hard_reset(); -#endif -} - -void HardFault_Handler(void) { - // Grab the stack pointer, shove it into a register and call - // the c function above. - __asm("TST LR, #4\n" - "ITE EQ\n" - "MRSEQ R0, MSP\n" - "MRSNE R0, PSP\n" - "B hard_fault_handler_c\n"); -} diff --git a/platform/snowy/boot/src/main.c b/platform/snowy/boot/src/main.c deleted file mode 100644 index 0b890727f2..0000000000 --- a/platform/snowy/boot/src/main.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "board/board.h" -#include "boot_tests.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "drivers/watchdog.h" -#include "firmware.h" -#include "fw_copy.h" -#include "pebble_errors.h" -#include "stm32f4xx.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" -#include "util/delay.h" - - -static const uint8_t SELECT_BUTTON_MASK = 0x4; - -static void prv_get_fw_reset_vector(void **reset_handler, - void **initial_stack_pointer) { - void** fw_vector_table = (void**) FIRMWARE_BASE; // Defined in wscript - *initial_stack_pointer = fw_vector_table[0]; - *reset_handler = fw_vector_table[1]; -} - -static void __attribute__((noreturn)) jump_to_fw(void) { - void *initial_stack_pointer, *reset_handler; - prv_get_fw_reset_vector(&reset_handler, &initial_stack_pointer); - - dbgserial_print("Booting firmware @ "); - dbgserial_print_hex((uintptr_t)reset_handler); - dbgserial_print("...\r\n\r\n"); - - // Disable all interrupts, just in case. - for (int i = 0; i < 8; ++i) { - // Interrupt Clear-Enable Register - NVIC->ICER[i] = 0xFFFFFFFF; - // Interrupt Clear-Pending Register - NVIC->ICPR[i] = 0xFFFFFFFF; - } - - // Set the peripheral clock enable registers to their reset values as - // specified in the datasheet. - RCC->AHB1ENR = 0x00100000; // Core-coupled memory is enabled at reset - RCC->AHB2ENR = 0; - RCC->AHB3ENR = 0; - RCC->APB1ENR = 0; - RCC->APB2ENR = 0; - - // Reset most peripherals used by the bootloader. We want to minimize the - // chances that the firmware unintentionally relies on some state that the - // bootloader leaves behind. This includes disabling the PLL. - // GPIOs are not reset here: resetting them would change their output values, - // which could unintentionally turn of e.g. PMIC power rails. - // The backup domain is not reset; that would be foolish. - const uint32_t ahb1_periphs = - RCC_AHB1Periph_CRC | RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_DMA2 - | RCC_AHB1Periph_DMA2D | RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_OTG_HS; - const uint32_t ahb2_periphs = - RCC_AHB2Periph_DCMI | RCC_AHB2Periph_CRYP | RCC_AHB2Periph_HASH - | RCC_AHB2Periph_RNG | RCC_AHB2Periph_OTG_FS; - const uint32_t ahb3_periphs = RCC_AHB3Periph_FMC; - const uint32_t apb1_periphs = - RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4 - | RCC_APB1Periph_TIM5 | RCC_APB1Periph_TIM6 | RCC_APB1Periph_TIM7 - | RCC_APB1Periph_TIM12 | RCC_APB1Periph_TIM13 | RCC_APB1Periph_TIM14 - | RCC_APB1Periph_WWDG | RCC_APB1Periph_SPI2 | RCC_APB1Periph_SPI3 - | RCC_APB1Periph_USART2 | RCC_APB1Periph_USART3 | RCC_APB1Periph_UART4 - | RCC_APB1Periph_UART5 | RCC_APB1Periph_I2C1 | RCC_APB1Periph_I2C2 - | RCC_APB1Periph_I2C3 | RCC_APB1Periph_CAN1 | RCC_APB1Periph_CAN2 - | RCC_APB1Periph_PWR | RCC_APB1Periph_DAC | RCC_APB1Periph_UART7 - | RCC_APB1Periph_UART8; - const uint32_t apb2_periphs = - RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART1 | - RCC_APB2Periph_USART6 | RCC_APB2Periph_ADC | RCC_APB2Periph_ADC1 | - RCC_APB2Periph_ADC2 | RCC_APB2Periph_ADC3 | RCC_APB2Periph_SDIO | - RCC_APB2Periph_SPI1 | RCC_APB2Periph_SPI4 | RCC_APB2Periph_SYSCFG | - RCC_APB2Periph_TIM9 | RCC_APB2Periph_TIM10 | RCC_APB2Periph_TIM11 | - RCC_APB2Periph_SPI5 | RCC_APB2Periph_SPI6 | RCC_APB2Periph_SAI1 | - RCC_APB2Periph_LTDC; - RCC_DeInit(); - RCC_AHB1PeriphResetCmd(ahb1_periphs, ENABLE); - RCC_AHB1PeriphResetCmd(ahb1_periphs, DISABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, ENABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, DISABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, ENABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, DISABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, ENABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, DISABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, ENABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, DISABLE); - - // The Cortex-M user guide states that the reset values for the core registers - // are as follows: - // R0-R12 = Unknown - // MSP = VECTOR_TABLE[0] (main stack pointer) - // PSP = Unknown (process stack pointer) - // LR = 0xFFFFFFFF - // PC = VECTOR_TABLE[1] - // PRIMASK = 0x0 - // FAULTMASK = 0x0 - // BASEPRI = 0x0 - // CONTROL = 0x0 - // - // Attempt to put the processor into as close to the reset state as possible - // before passing control to the firmware. - // - // No attempt is made to set CONTROL to zero as it should already be set to - // the reset value when this code executes. - __asm volatile ( - "cpsie if\n" // Clear PRIMASK and FAULTMASK - "mov lr, 0xFFFFFFFF\n" - "mov sp, %[initial_sp]\n" - "bx %[reset_handler]\n" - : : [initial_sp] "r" (initial_stack_pointer), - [reset_handler] "r" (reset_handler) - ); - __builtin_unreachable(); -} - -static bool check_and_increment_reset_loop_detection_bits(void) { - uint8_t counter = - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_THREE) << 2) | - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_TWO) << 1) | - boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_ONE); - - if (counter == 7) { - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE); - return true; - } - - switch (++counter) { - case 1: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 2: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 3: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 4: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_THREE); - break; - case 5: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 6: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 7: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - default: - PBL_CROAK("reset loop boot bits overrun"); - break; - } - return false; -} - -static bool check_for_recovery_start_failure() { - return boot_bit_test(BOOT_BIT_RECOVERY_START_IN_PROGRESS); -} - -static bool check_for_fw_start_failure() { - // Add more failure conditions here. - if (!watchdog_check_reset_flag() && !boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - // We're good, we're just starting normally. - PBL_LOG_VERBOSE("We're good, we're just starting normally."); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return false; - } - - // We failed to start our firmware successfully! - if (watchdog_check_reset_flag()) { - dbgserial_putstr("Watchdog caused a reset"); - } - if (boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - dbgserial_putstr("Software failure caused a reset"); - } - - // Clean up after the last failure. - boot_bit_clear(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - - // We have a "three strikes" algorithm: if the watch fails three times, return true - // to tell the parent we should load the recovery firmware. A reset for any other reason - // will reset this algorithm. - if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) { - // Yikes, our firmware is screwed. Boot into recovery mode. - dbgserial_putstr("Failed to start firmware, strike three."); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return true; - } else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to start firmware, strike two."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - } else { - dbgserial_putstr("Failed to start firmware, strike one."); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - } - - return false; -} - -static bool check_force_boot_recovery(void) { - if (boot_bit_test(BOOT_BIT_FORCE_PRF)) { - boot_bit_clear(BOOT_BIT_FORCE_PRF); - return true; - } - - if (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK)) { - dbgserial_putstr("Hold down UP + BACK for 5 secs. to force-boot PRF"); - for (int i = 0; i < 5000; ++i) { - if (!(button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK))) { - // stop waiting if not held down any longer - return false; - } - delay_ms(1); - } - - return true; - } - - void *reset_vector, *initial_sp; - prv_get_fw_reset_vector(&reset_vector, &initial_sp); - if ((uintptr_t)reset_vector == 0xffffffff || - (uintptr_t)initial_sp == 0xffffffff) { - dbgserial_putstr("Firmware is erased"); - return true; - } - return false; -} - -static void sad_watch(uint32_t error_code) { - dbgserial_putstr("SAD WATCH"); - - char error_code_buffer[12]; - itoa(error_code, error_code_buffer, sizeof(error_code_buffer)); - dbgserial_putstr(error_code_buffer); - - display_error_code(error_code); - - static uint8_t prev_button_state = 0; - prev_button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - while (1) { - // See if we should restart - uint8_t button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - if (button_state != prev_button_state) { - system_reset(); - } - delay_ms(10); - } -} - -static void check_and_handle_resuming_from_standby(void) { - periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_PWR); - if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) { - // We just woke up from standby. For some reason this leaves the system in a funny state, - // so clear the flag and reboot again to really clear things up. - - PWR_ClearFlag(PWR_FLAG_SB); - dbgserial_putstr("exit standby"); - system_hard_reset(); - } - periph_config_disable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_PWR); -} - -void boot_main(void) { - check_and_handle_resuming_from_standby(); - - dbgserial_init(); - - dbgserial_putstr(""); - dbgserial_putstr(" ____ __"); - dbgserial_putstr("/\\ _`\\ /'__`\\"); - dbgserial_putstr("\\ \\,\\L\\_\\ ___ /\\ \\/\\ \\ __ __ __ __ __"); - dbgserial_putstr(" \\/_\\__ \\ /' _ `\\ \\ \\ \\ \\/\\ \\/\\ \\/\\ \\/\\ \\/\\ \\"); - dbgserial_putstr(" /\\ \\L\\ \\/\\ \\/\\ \\ \\ \\_\\ \\ \\ \\_/ \\_/ \\ \\ \\_\\ \\"); - dbgserial_putstr(" \\ `\\____\\ \\_\\ \\_\\ \\____/\\ \\___x___/'\\/`____ \\"); - dbgserial_putstr(" \\/_____/\\/_/\\/_/\\/___/ \\/__//__/ `/___/> \\"); - dbgserial_putstr(" /\\___/"); - dbgserial_putstr(" \\/__/"); - - // PMIC requires I2C - i2c_init(); - // Enable the 3.2V rail for the benefit of the FPGA and display - pmic_init(); - - boot_bit_init(); - boot_version_write(); - - // Write the bootloader version to serial-out - { - char bootloader_version_str[12]; - memset(bootloader_version_str, 0, 12); - itoa(boot_version_read(), bootloader_version_str, 12); - dbgserial_putstr(bootloader_version_str); - } - dbgserial_putstr(""); - dbgserial_putstr(""); - - if (boot_bit_test(BOOT_BIT_FW_STABLE)) { - dbgserial_putstr("Last firmware boot was stable; clear strikes"); - - boot_bit_clear(BOOT_BIT_FW_STABLE); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - } - - flash_init(); - button_init(); - display_init(); - -#ifdef DISPLAY_DEMO_LOOP - while (1) { - for (int i=0; i < 92; ++i) { - display_firmware_update_progress(i, 91); - delay_us(80000); - } - - for (uint32_t i=0; i <= 0xf; ++i) { - display_error_code(i * 0x11111111); - delay_us(200000); - } - for (uint32_t i=0; i < 8; ++i) { - for (uint32_t j=1; j<=0xf; ++j) { - display_error_code(j << (i*4)); - delay_us(200000); - } - } - display_error_code(0x01234567); - delay_us(200000); - display_error_code(0x89abcdef); - delay_us(200000); - display_error_code(0xcafebabe); - delay_us(200000); - display_error_code(0xfeedface); - delay_us(200000); - display_error_code(0x8badf00d); - delay_us(200000); - display_error_code(0xbad1ce40); - delay_us(200000); - display_error_code(0xbeefcace); - delay_us(200000); - display_error_code(0x0defaced); - delay_us(200000); - display_error_code(0xd15ea5e5); - delay_us(200000); - display_error_code(0xdeadbeef); - delay_us(200000); - display_boot_splash(); - delay_us(1000000); - } -#endif - - if (is_button_stuck()) { - sad_watch(ERROR_STUCK_BUTTON); - } - - if (is_flash_broken()) { - sad_watch(ERROR_BAD_SPI_FLASH); - } - - boot_bit_dump(); - - // If the recovery firmware crashed at start-up, the watch is now a - // $150 brick. That's life! - if (check_for_recovery_start_failure()) { - boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - sad_watch(ERROR_CANT_LOAD_FW); - } - - bool force_boot_recovery_mode = check_force_boot_recovery(); - if (force_boot_recovery_mode) { - dbgserial_putstr("Force-booting recovery mode..."); - } - - if (force_boot_recovery_mode || check_for_fw_start_failure()) { - if (!switch_to_recovery_fw()) { - // We've failed to load recovery mode too many times. - sad_watch(ERROR_CANT_LOAD_FW); - } - } else { - check_update_fw(); - } - - if (check_and_increment_reset_loop_detection_bits()) { - sad_watch(ERROR_RESET_LOOP); - } - - watchdog_init(); -#ifndef NO_WATCHDOG - watchdog_start(); -#endif - - jump_to_fw(); -} diff --git a/platform/snowy/boot/src/pebble_errors.h b/platform/snowy/boot/src/pebble_errors.h deleted file mode 100644 index 1278bf459b..0000000000 --- a/platform/snowy/boot/src/pebble_errors.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -static const uint32_t ERROR_NO_ACTIVE_ERROR = 0; - -// FW Errors -static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501; -static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502; -static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503; -static const uint32_t ERROR_RESET_LOOP = 0xfe504504; - -// BT Errors -static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510; -static const uint32_t ERROR_CANT_START_LSE = 0xfe504511; -static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512; - -static const uint32_t ERROR_LOW_BATTERY = 0xfe504520; diff --git a/platform/snowy/boot/src/stm32f_flash_boot.ld.in b/platform/snowy/boot/src/stm32f_flash_boot.ld.in deleted file mode 100644 index de9d4ee469..0000000000 --- a/platform/snowy/boot/src/stm32f_flash_boot.ld.in +++ /dev/null @@ -1,180 +0,0 @@ -/* -Linker script for STM32F2xx_1024K_128K -*/ - -/* include the common STM32F2xx sub-script */ - -/* Common part of the linker scripts for STM32 devices*/ - -/* default stack sizes. -These are used by the startup in order to allocate stacks for the different modes. -*/ - -__Stack_Size = 8192; - -PROVIDE ( _Stack_Size = __Stack_Size ) ; - -__Stack_Init = _estack - __Stack_Size ; - -/*"PROVIDE" allows to easily override these values from an object file or the commmand line.*/ -PROVIDE ( _Stack_Init = __Stack_Init ) ; - -/* -There will be a link error if there is not this amount of RAM free at the end. -*/ -_Minimum_Stack_Size = 0x100 ; - -/* include the memory spaces definitions sub-script */ -/* -Linker subscript for STM32F2xx definitions with 1024K Flash and 1024K External SRAM */ - -/* Memory Spaces Definitions */ - -MEMORY -{ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_LENGTH@ -} - -__end_heap = ORIGIN(RAM) + LENGTH(RAM); -PROVIDE(_heap_end = __end_heap); - -/* include the sections management sub-script for FLASH mode */ - -/* Sections Definitions */ - -SECTIONS -{ - /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* for some STRx devices, the beginning of the startup code is stored in the .flashtext section, which goes to FLASH */ - .flashtext : - { - . = ALIGN(4); - *(.flashtext) /* Startup code */ - . = ALIGN(4); - } >FLASH - - /* Exception handling sections. "contains index entries for section unwinding" */ - .ARM.exidx : - { - . = ALIGN(4); - *(.ARM.exidx) - . = ALIGN(4); - } >FLASH - - /* the program code is stored in the .text section, which goes to Flash */ - .text : - { - . = ALIGN(4); - - *(.text) /* remaining code */ - *(.text.*) /* remaining code */ - *(.rodata) /* read-only data (constants) */ - *(.rodata*) - *(.constdata) /* read-only data (constants) */ - *(.constdata*) - *(.glue_7) - *(.glue_7t) - *(i.*) - - . = ALIGN(4); - _etext = .; - _sidata = _etext; - } >FLASH - - /* This is the initialized data section - The program executes knowing that the data is in the RAM - but the loader puts the initial values in the FLASH (inidata). - It is one task of the startup to copy the initial values from FLASH to RAM. */ - .data : AT ( _sidata ) - { - . = ALIGN(4); - /* This is used by the startup in order to initialize the .data secion */ - _sdata = .; - - *(.data) - *(.data.*) - - . = ALIGN(4); - _edata = .; /* This is used by the startup in order to initialize the .data secion */ - } >RAM - - /* This is the uninitialized data section */ - .bss : - { - . = ALIGN(4); - _sbss = .; /* This is used by the startup in order to initialize the .bss secion */ - - *(.bss) - *(.bss.*) - *(COMMON) - - . = ALIGN(4); - _ebss = .; /* This is used by the startup in order to initialize the .bss secion */ - } >RAM - - .stack : - { - . = ALIGN(8); - _sstack = .; - . = . + __Stack_Size; - . = ALIGN(8); - _endstack = .; - } >RAM - - PROVIDE ( _estack = _endstack); - PROVIDE ( end = _endstack ); - PROVIDE ( _end = _endstack ); - PROVIDE ( _heap_start = _endstack ); - - /* after that it's only debugging information. */ - - /* remove the debugging information from the standard libraries */ - DISCARD : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} diff --git a/platform/snowy/boot/src/system/LICENSE b/platform/snowy/boot/src/system/LICENSE deleted file mode 100644 index e3b14321e7..0000000000 --- a/platform/snowy/boot/src/system/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2009, Atmel Corporation - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -- Redistributions of source code must retain the above copyright notice, -this list of conditions and the disclaimer below. - -Atmel's name may not be used to endorse or promote products derived from -this software without specific prior written permission. - -DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE -DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/platform/snowy/boot/src/system/bootbits.c b/platform/snowy/boot/src/system/bootbits.c deleted file mode 100644 index 9c30b2c0d9..0000000000 --- a/platform/snowy/boot/src/system/bootbits.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "system/bootbits.h" - -#include "system/logging.h" -#include "system/rtc_registers.h" -#include "util/version.h" - -#include "stm32f4xx.h" - -#include -#include - -void boot_bit_init(void) { - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); - PWR_BackupAccessCmd(ENABLE); // Disable write-protect on RTC_BKP_x registers - - if (!boot_bit_test(BOOT_BIT_INITIALIZED)) { - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED); - } -} - -void boot_bit_set(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value |= bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -void boot_bit_clear(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value &= ~bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -bool boot_bit_test(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - return (current_value & bit); -} - -void boot_bit_dump(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "0x%"PRIx32, RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR)); -} - -uint32_t boot_bits_get(void) { - return RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); -} - -void command_boot_bits_get(void) { - char buffer[32]; - dbgserial_putstr_fmt(buffer, sizeof(buffer), "bootbits: 0x%"PRIu32, boot_bits_get()); -} - -void boot_version_write(void) { - if (boot_version_read() == TINTIN_METADATA.version_timestamp) { - return; - } - RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, TINTIN_METADATA.version_timestamp); -} - -uint32_t boot_version_read(void) { - return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER); -} diff --git a/platform/snowy/boot/src/system/bootbits.h b/platform/snowy/boot/src/system/bootbits.h deleted file mode 100644 index abaa932fb1..0000000000 --- a/platform/snowy/boot/src/system/bootbits.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum BootBitValue { - BOOT_BIT_INITIALIZED = 0x1 << 0, - BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1, - BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2, - BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3, - BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6, - BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7, - BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, - BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9, - BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10, - BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11, - BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12, - BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13, - BOOT_BIT_FW_STABLE = 0x1 << 14, - BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15, - BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16, - BOOT_BIT_FORCE_PRF = 0x1 << 17, - BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18 -} BootBitValue; - -void boot_bit_init(); -void boot_bit_set(BootBitValue bit); -void boot_bit_clear(BootBitValue bit); -bool boot_bit_test(BootBitValue bit); - -// Dump the contents to PBL_LOG -void boot_bit_dump(void); -uint32_t boot_bits_get(void); - -void boot_version_write(void); -uint32_t boot_version_read(void); diff --git a/platform/snowy/boot/src/system/die.c b/platform/snowy/boot/src/system/die.c deleted file mode 100644 index dd5fdb6367..0000000000 --- a/platform/snowy/boot/src/system/die.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/reset.h" -#include "system/passert.h" - -#include "misc.h" - -void reset_due_to_software_failure(void) { -#if defined(NO_WATCHDOG) - // Don't reset right away, leave it in a state we can inspect - - while (1) { - BREAKPOINT; - } -#endif - - dbgserial_putstr("Software failure; resetting!"); - system_reset(); -} diff --git a/platform/snowy/boot/src/system/die.h b/platform/snowy/boot/src/system/die.h deleted file mode 100644 index 8ddffee0e9..0000000000 --- a/platform/snowy/boot/src/system/die.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were -//! able shut everything down nicely before rebooting. -void reset_due_to_software_failure(void) __attribute__((noreturn)); diff --git a/platform/snowy/boot/src/system/firmware_storage.c b/platform/snowy/boot/src/system/firmware_storage.c deleted file mode 100644 index b3b90212d6..0000000000 --- a/platform/snowy/boot/src/system/firmware_storage.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "firmware_storage.h" - -#include "drivers/flash.h" - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { - FirmwareDescription firmware_description; - flash_read_bytes((uint8_t*) &firmware_description, firmware_start_address, sizeof(FirmwareDescription)); - return firmware_description; -} - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc) { - return desc->description_length == sizeof(FirmwareDescription); -} diff --git a/platform/snowy/boot/src/system/firmware_storage.h b/platform/snowy/boot/src/system/firmware_storage.h deleted file mode 100644 index e05c44b208..0000000000 --- a/platform/snowy/boot/src/system/firmware_storage.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file firmware_storage.h -//! Utilities for reading a firmware image stored in flash. - -#include -#include - -typedef struct __attribute__((__packed__)) FirmwareDescription { - uint32_t description_length; - uint32_t firmware_length; - uint32_t checksum; -} FirmwareDescription; - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address); - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc); diff --git a/platform/snowy/boot/src/system/logging.h b/platform/snowy/boot/src/system/logging.h deleted file mode 100644 index 1165038963..0000000000 --- a/platform/snowy/boot/src/system/logging.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/dbgserial.h" -#include "system/die.h" - -#include -#include -#include -#include -#include - -#ifndef __FILE_NAME__ -#define __FILE_NAME__ __FILE__ -#endif - -#define LOG_LEVEL_ALWAYS 0 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_WARNING 50 -#define LOG_LEVEL_INFO 100 -#define LOG_LEVEL_DEBUG 200 -#define LOG_LEVEL_DEBUG_VERBOSE 255 - -#ifndef STRINGIFY - #define STRINGIFY_NX(a) #a - #define STRINGIFY(a) STRINGIFY_NX(a) -#endif // STRINGIFY - -#define STATUS_STRING(s) STRINGIFY(s) - -#ifdef PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) \ - dbgserial_putstr(__FILE_NAME__ ":" STRINGIFY(__LINE__) "> " fmt); - /* - do { \ - char _pbl_log_buffer[128]; \ - dbgserial_putstr_fmt(_pbl_log_buffer, sizeof(_pbl_log_buffer), \ - __FILE_NAME__ ":" STRINGIFY(__LINE__) "> " fmt, ## args); \ - } while (0) - */ - - #ifdef VERBOSE_LOGGING - - #define PBL_LOG_VERBOSE(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) - - #else // VERBOSE_LOGGING - #define PBL_LOG_VERBOSE(fmt, args...) - #endif // VERBOSE_LOGGING - -#else // PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, args...) - #define PBL_LOG_VERBOSE(fmt, args...) -#endif // PBL_LOG_ENABLED diff --git a/platform/snowy/boot/src/system/passert.c b/platform/snowy/boot/src/system/passert.c deleted file mode 100644 index 0b5f8b138b..0000000000 --- a/platform/snowy/boot/src/system/passert.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "passert.h" - -#include "system/die.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#include -#include -#include -#include -#include - -static __attribute__((noreturn)) void handle_passert_failed_vargs(const char* filename, int line_number, - uintptr_t lr, const char* expr, const char* fmt, va_list fmt_args) { - dbgserial_print("ASSERT: "); - dbgserial_print(expr); - dbgserial_print(" "); - dbgserial_print(filename); - dbgserial_print(":"); - dbgserial_print_hex(line_number); - if (fmt) { - dbgserial_print(" "); - dbgserial_print(fmt); - } - dbgserial_putstr(""); - - reset_due_to_software_failure(); -} - -static __attribute__((noreturn)) void handle_passert_failed(const char* filename, int line_number, - uintptr_t lr, const char *expr, const char* fmt, ...) { - va_list fmt_args; - va_start(fmt_args, fmt); - - handle_passert_failed_vargs(filename, line_number, lr, expr, fmt, fmt_args); - - va_end(fmt_args); -} - -void passert_failed(const char* filename, int line_number, const char* message, ...) { - va_list fmt_args; - va_start(fmt_args, message); - - handle_passert_failed_vargs(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERT", message, fmt_args); - - va_end(fmt_args); -} - -void passert_failed_no_message(const char* filename, int line_number) { - handle_passert_failed(filename, line_number, - (uintptr_t)__builtin_return_address(0), "ASSERTN", NULL); -} - -void wtf(void) { - uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); - dbgserial_print("*** WTF "); - dbgserial_print_hex(saved_lr); - dbgserial_putstr(""); - reset_due_to_software_failure(); -} - -//! Assert function called by the STM peripheral library's -//! 'assert_param' method. See stm32f2xx_conf.h for more information. -void assert_failed(uint8_t* file, uint32_t line) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - - handle_passert_failed((const char*) file, line, saved_lr, "STM32", "STM32 peripheral library tripped an assert"); -} - -extern void command_dump_malloc_kernel(void); - -void croak_oom(const char *filename, int line_number, const char *fmt, ...) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - -#ifdef MALLOC_INSTRUMENTATION - command_dump_malloc_kernel(); -#endif - - va_list fmt_args; - va_start(fmt_args, fmt); - - handle_passert_failed_vargs(filename, line_number, saved_lr, "CROAK OOM", fmt, fmt_args); - - va_end(fmt_args); -} diff --git a/platform/snowy/boot/src/system/passert.h b/platform/snowy/boot/src/system/passert.h deleted file mode 100644 index c46945a1ae..0000000000 --- a/platform/snowy/boot/src/system/passert.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "logging.h" - - -void passert_failed(const char* filename, int line_number, const char* message, ...) - __attribute__((noreturn)); - -#define PBL_ASSERT(expr, ...) \ - do { \ - if (!(expr)) { \ - passert_failed(__FILE_NAME__, __LINE__, __VA_ARGS__); \ - } \ - } while (0) - -#define PBL_ASSERTN(expr) \ - do { \ - if (!(expr)) { \ - passert_failed_no_message(__FILE_NAME__, __LINE__); \ - } \ - } while (0) - -void passert_failed_no_message(const char* filename, int line_number) - __attribute__((noreturn)); - -void wtf(void) __attribute__((noreturn)); - -#define WTF wtf() - -// Insert a compiled-in breakpoint -#define BREAKPOINT __asm("bkpt") - -#define PBL_ASSERT_PRIVILEGED() -#define PBL_ASSERT_TASK(task) - -extern void command_dump_malloc(void); - -#define PBL_ASSERT_OOM(expr) \ - do { \ - if (!(expr)) { \ - croak_oom(__FILE_NAME__, __LINE__, "Failed to allocate <%s> with size %u", #expr, sizeof(*expr)); \ - } \ - } while (0) - -#define PBL_CROAK(fmt, args...) \ - passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args) - -void croak_oom(const char *filename, int line_number, const char *fmt, ...) __attribute__((noreturn)); - -#define PBL_CROAK_OOM(fmt, args...) \ - croak_oom(__FILE_NAME__, __LINE__, fmt, ## args) diff --git a/platform/snowy/boot/src/system/reset.c b/platform/snowy/boot/src/system/reset.c deleted file mode 100644 index b44cbcd5f0..0000000000 --- a/platform/snowy/boot/src/system/reset.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "system/reset.h" - -#include "drivers/display.h" - -#if defined(MICRO_FAMILY_STM32F2) -#include "stm32f2xx.h" -#elif defined(MICRO_FAMILY_STM32F4) -#include "stm32f4xx.h" -#endif - -void system_reset(void) { - display_prepare_for_reset(); - system_hard_reset(); -} - -void system_hard_reset(void) { - NVIC_SystemReset(); - __builtin_unreachable(); -} diff --git a/platform/snowy/boot/src/system/reset.h b/platform/snowy/boot/src/system/reset.h deleted file mode 100644 index c1ad79b79f..0000000000 --- a/platform/snowy/boot/src/system/reset.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Shut down system services but don't actually reset. -void system_reset_prepare(bool skip_bt_teardown); - -//! Reset nicely after shutting down system services. Does not set the reboot_reason other than -//! calling reboot_reason_set_restarted_safely just before the reset occurs. -void system_reset(void)__attribute__((noreturn)); - -//! Same as system_reset() but usable as a callback. -void system_reset_callback(void *); - -//! The final stage in the reset process. -void system_hard_reset(void) __attribute__((noreturn)); diff --git a/platform/snowy/boot/src/system/rtc_registers.h b/platform/snowy/boot/src/system/rtc_registers.h deleted file mode 100644 index d86c325dfd..0000000000 --- a/platform/snowy/boot/src/system/rtc_registers.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0 -#define STUCK_BUTTON_REGISTER RTC_BKP_DR1 -#define BOOTLOADER_VERSION_REGISTER RTC_BKP_DR2 -#define CURRENT_TIME_REGISTER RTC_BKP_DR3 -#define CURRENT_INTERVAL_TICKS_REGISTER RTC_BKP_DR4 -#define REBOOT_REASON_REGISTER_1 RTC_BKP_DR5 -#define REBOOT_REASON_REGISTER_2 RTC_BKP_DR6 -#define REBOOT_REASON_STUCK_TASK_PC RTC_BKP_DR7 -#define REBOOT_REASON_STUCK_TASK_LR RTC_BKP_DR8 -#define REBOOT_REASON_STUCK_TASK_CALLBACK RTC_BKP_DR9 -#define REBOOT_REASON_MUTEX_LR RTC_BKP_DR10 -#define REBOOT_REASON_MUTEX_PC RTC_BKP_DR11 // Deprecated -#define MAG_XY_CORRECTION_VALS RTC_BKP_DR17 -#define MAG_Z_CORRECTION_VAL RTC_BKP_DR18 -#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19 diff --git a/platform/snowy/boot/src/system/syscalls.c b/platform/snowy/boot/src/system/syscalls.c deleted file mode 100644 index b2b847bd81..0000000000 --- a/platform/snowy/boot/src/system/syscalls.c +++ /dev/null @@ -1,162 +0,0 @@ -/* ---------------------------------------------------------------------------- - * ATMEL Microcontroller Software Support - * ---------------------------------------------------------------------------- - * Copyright (c) 2009, Atmel Corporation - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the disclaimer below. - * - * Atmel's name may not be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE - * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - */ - -/** - * \file syscalls.c - * - * Implementation of newlib syscall. - * - */ - -/*---------------------------------------------------------------------------- - * Headers - *----------------------------------------------------------------------------*/ - - -#include -#include -#include -#include -#include - -#include "drivers/dbgserial.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/misc.h" - -/*---------------------------------------------------------------------------- - * Exported functions - *----------------------------------------------------------------------------*/ -extern void _kill( int pid, int sig ) ; -extern int _getpid ( void ) ; - -void __malloc_lock (struct _reent *r) { -} - -void __malloc_unlock (struct _reent *r) { -} - -extern caddr_t _sbrk ( int incr ) { - return (caddr_t) -1; -} - -extern int link( char *old, char *new ) -{ - (void)old; - (void)new; - return -1 ; -} - -extern int _close( int file ) -{ - (void)file; - return -1 ; -} - -extern int _open(const char *name, int flags, int mode) { - (void) name; - (void) flags; - (void) mode; - - return -1; -} - -extern int _fstat( int file, struct stat *st ) -{ - (void)file; - st->st_mode = S_IFCHR ; - - return 0 ; -} - -extern int _isatty( int file ) -{ - (void)file; - return 1 ; -} - -extern int _lseek( int file, int ptr, int dir ) -{ - (void)file; - (void)ptr; - (void)dir; - return 0 ; -} - -extern int _read(int file, char *ptr, int len) -{ - (void)file; - (void)ptr; - (void)len; - return 0 ; -} - -extern int _write( int file, char *ptr, int len ) { - (void) file; - (void) ptr; - (void) len; - - return 0; -} - -extern void _exit( int status ) -{ - PBL_CROAK("_exit"); - for ( ; ; ) ; -} - -extern void _kill( int pid, int sig ) -{ - PBL_CROAK("_kill"); - return; -} - -extern int _getpid ( void ) -{ - return -1 ; -} - -// For compiling with GCC 4.8 Toolchain -// Wrapper for undefined __aeabi_memcpy and __aeabi_memcpy4 -// these functions cannot be aliased, as memcpy returns int, these have no return -#if __GNUC__ >= 4 && __GNUC_MINOR__ > 7 // 4.7 - -void __aeabi_memcpy(void *dest, const void *src, size_t n){ - __builtin_memcpy (dest, src, n); -} - -void __aeabi_memcpy4(void *dest, const void *src, size_t n){ - __builtin_memcpy (dest, src, n); -} - -#endif - -/* extern void _tzset_r(struct _reent *r) { */ -/* (void) r; */ -/* } */ diff --git a/platform/snowy/boot/src/util/attributes.h b/platform/snowy/boot/src/util/attributes.h deleted file mode 100644 index 8cd71d62d5..0000000000 --- a/platform/snowy/boot/src/util/attributes.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if defined(__clang__) -#define GCC_ONLY(x) -#else -#define GCC_ONLY(x) x -#endif - -// Function attributes -#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST))) - -#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST) - -#define ALWAYS_INLINE __attribute__((__always_inline__)) inline -#define NOINLINE __attribute__((__noinline__)) -#define NORETURN __attribute__((__noreturn__)) void -#define NAKED_FUNC __attribute__((__naked__)) -#define OPTIMIZE_FUNC(LVL) __attribute__((__optimize__(LVL))) -#define CONST_FUNC __attribute__((__const__)) -#define PURE_FUNC __attribute__((__pure__)) - -// Variable attributes -#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC))) - -// Structure attributes -#define PACKED __attribute__((__packed__)) - -// General attributes -#define USED __attribute__((__used__)) -#define UNUSED __attribute__((__unused__)) -#define WEAK __attribute__((__weak__)) -#define ALIAS(sym) __attribute__((__weak__, __alias__(sym))) -#define SECTION(SEC) GCC_ONLY(__attribute__((__section__(SEC)))) -#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__))) diff --git a/platform/snowy/boot/src/util/bitset.h b/platform/snowy/boot/src/util/bitset.h deleted file mode 100644 index db806de3b8..0000000000 --- a/platform/snowy/boot/src/util/bitset.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! @file util/bitset.h -//! -//! Helper functions for dealing with a bitsets of various widths. - -#pragma once - -#include -#include - -#include "system/passert.h" - -static inline void bitset8_set(uint8_t* bitset, unsigned int index) { - bitset[index / 8] |= (1 << (index % 8)); -} - -static inline void bitset8_clear(uint8_t* bitset, unsigned int index) { - bitset[index / 8] &= ~(1 << (index % 8)); -} - -static inline void bitset8_update(uint8_t* bitset, unsigned int index, bool value) { - if (value) { - bitset8_set(bitset, index); - } else { - bitset8_clear(bitset, index); - } -} - -static inline bool bitset8_get(const uint8_t* bitset, unsigned int index) { - return bitset[index / 8] & (1 << (index % 8)); -} - -static inline void bitset16_set(uint16_t* bitset, unsigned int index) { - bitset[index / 16] |= (1 << (index % 16)); -} - -static inline void bitset16_clear(uint16_t* bitset, unsigned int index) { - bitset[index / 16] &= ~(1 << (index % 16)); -} - -static inline void bitset16_update(uint16_t* bitset, unsigned int index, bool value) { - if (value) { - bitset16_set(bitset, index); - } else { - bitset16_clear(bitset, index); - } -} - -static inline bool bitset16_get(const uint16_t* bitset, unsigned int index) { - return bitset[index / 16] & (1 << (index % 16)); -} - -static inline void bitset32_set(uint32_t* bitset, unsigned int index) { - bitset[index / 32] |= (1 << (index % 32)); -} - -static inline void bitset32_clear(uint32_t* bitset, unsigned int index) { - bitset[index / 32] &= ~(1 << (index % 32)); -} - -static inline void bitset32_clear_all(uint32_t* bitset, unsigned int width) { - if (width > 32) { - // TODO: revisit - WTF; - } - *bitset &= ~((1 << (width + 1)) - 1); -} - -static inline void bitset32_update(uint32_t* bitset, unsigned int index, bool value) { - if (value) { - bitset32_set(bitset, index); - } else { - bitset32_clear(bitset, index); - } -} - -static inline bool bitset32_get(const uint32_t* bitset, unsigned int index) { - return bitset[index / 32] & (1 << (index % 32)); -} - -#ifdef __arm__ -#define rotl32(x, shift) \ -__asm__ volatile ("ror %0,%0,%1" : "+r" (src) : "r" (32 - (shift)) :); -#else -#define rotl32(x, shift) \ -uint32_t s = shift % 32; \ -{x = ((x << (s)) | x >> (32 - (s)));} -#endif diff --git a/platform/snowy/boot/src/util/cobs.c b/platform/snowy/boot/src/util/cobs.c deleted file mode 100644 index 3579543d52..0000000000 --- a/platform/snowy/boot/src/util/cobs.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "cobs.h" - -size_t cobs_encode(void *dst_ptr, const void *src_ptr, size_t length) { - const char *src = src_ptr; - char *dst = dst_ptr; - uint8_t code = 0x01; - size_t code_idx = 0; - size_t dst_idx = 1; - - for (size_t src_idx = 0; src_idx < length; ++src_idx) { - if (src[src_idx] == '\0') { - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } else { - dst[dst_idx++] = src[src_idx]; - code++; - if (code == 0xff) { - if (src_idx == length - 1) { - // Special case: the final encoded block is 254 bytes long with no - // zero after it. While it's technically a valid encoding if a - // trailing zero is appended, it causes the output to be one byte - // longer than it needs to be. This violates consistent overhead - // contract and could overflow a carefully sized buffer. - break; - } - dst[code_idx] = code; - code_idx = dst_idx++; - code = 0x01; - } - } - } - dst[code_idx] = code; - return dst_idx; -} diff --git a/platform/snowy/boot/src/util/cobs.h b/platform/snowy/boot/src/util/cobs.h deleted file mode 100644 index 7194512e53..0000000000 --- a/platform/snowy/boot/src/util/cobs.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! An implementation of Consistent Overhead Byte Stuffing -//! -//! http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf -//! http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing - -//! Evaluates to the offset required when encoding in-place -#define COBS_OVERHEAD(n) (((n) + 253) / 254) -//! Evaluates to the maximum buffer size required to hold n bytes of data -//! after COBS encoding. -#define MAX_SIZE_AFTER_COBS_ENCODING(n) ((n) + COBS_OVERHEAD(n)) - -//! COBS-encode a buffer out to another buffer. -//! -//! @param [out] dst destination buffer. The buffer must be at least -//! MAX_SIZE_AFTER_COBS_ENCODING(length) bytes long. -//! @param [in] src source buffer -//! @param length length of src -size_t cobs_encode(void * restrict dst, const void * restrict src, - size_t length); diff --git a/platform/snowy/boot/src/util/crc32.c b/platform/snowy/boot/src/util/crc32.c deleted file mode 100644 index 177b54de8e..0000000000 --- a/platform/snowy/boot/src/util/crc32.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/crc32.h" - -// Nybble-wide table driven CRC-32 algorithm -// -// A compromise between speed and size, this algorithm uses a lookup table to -// calculate the CRC four bits at a time with a size cost of only 64 bytes. By -// contrast, a byte-wide algorithm requires a lookup table sixteen times larger! -// -// The lookup table is generated by the crc32_lut.py - -static const uint32_t s_lookup_table[] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, -}; - -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) { - if (data == 0) { - return 0; - } - const uint8_t * restrict bytes = data; - - crc ^= 0xffffffff; - while (length--) { - crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf]; - crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf]; - bytes++; - } - crc ^= 0xffffffff; - return crc; -} diff --git a/platform/snowy/boot/src/util/crc32.h b/platform/snowy/boot/src/util/crc32.h deleted file mode 100644 index 80ac71ebae..0000000000 --- a/platform/snowy/boot/src/util/crc32.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! \file -//! Calculate the CRC-32 checksum of data. -//! -//! The checksum is the standard CRC-32 used by zlib, PNG and others. -//! The model parameters for the algorithm, as described in A Painless Guide to -//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are: -//! Name: "CRC-32" -//! Width: 32 -//! Poly: 04C11DB7 -//! Init: FFFFFFFF -//! RefIn: True -//! RefOut: True -//! XorOut: FFFFFFFF -//! Check: CBF43926 - -#include -#include - -//! Update a running CRC-32 checksum with the bytes of data and return the -//! updated CRC-32. If data is NULL, the function returns the required initial -//! value for the CRC. -//! -//! This function is drop-in compatible with zlib's crc32 function. -//! -//! \par Usage -//! \code -//! uint32_t crc = crc32(0, NULL, 0); -//! while (read_buffer(data, length)) { -//! crc = crc32(crc, data, length); -//! } -//! \endcode -uint32_t crc32(uint32_t crc, const void * restrict data, size_t length); - -//! The initial CRC register value for a standard CRC-32 checksum. -//! -//! It is the same value as is returned by the `crc32` function when data is -//! NULL. -//! -//! \code -//! assert(CRC32_INIT == crc32(0, NULL, 0)); -//! \endcode -#define CRC32_INIT (0) - -//! The residue constant of the CRC-32 algorithm. -//! -//! If the CRC-32 value of a message is appended (little-endian) onto the -//! end of the message, the CRC-32 of the concatenated message and CRC will be -//! equal to CRC32_RESIDUE if the message has not been corrupted in transit. -#define CRC32_RESIDUE (0x2144DF1C) diff --git a/platform/snowy/boot/src/util/delay.c b/platform/snowy/boot/src/util/delay.c deleted file mode 100644 index 800711d10c..0000000000 --- a/platform/snowy/boot/src/util/delay.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "delay.h" - -#include - -void delay_us(uint32_t us) { - // Empirically (measured on a bb2), 1 loop = ~47ns. (sysclk @ - // 64MHz, prefetch disabled) Alignment of code will have some impact on how - // long this actually takes - uint32_t delay_loops = us * 22; - - __asm volatile ( - "spinloop: \n" - " subs %[delay_loops], #1 \n" - " bne spinloop \n" - : [delay_loops] "+r" (delay_loops) // read-write operand - : - : "cc" - ); -} - -void delay_ms(uint32_t millis) { - // delay_us(millis*1000) is not used because a long delay could easily - // overflow the veriable. Without the outer loop, a delay of even five - // seconds would overflow. - while (millis--) { - delay_us(1000); - } -} diff --git a/platform/snowy/boot/src/util/delay.h b/platform/snowy/boot/src/util/delay.h deleted file mode 100644 index 371ffd7cd7..0000000000 --- a/platform/snowy/boot/src/util/delay.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Carefully timed spinloop that allows one to delay at a microsecond -//! granularity. -void delay_us(uint32_t us); - -//! Waits for a certain amount of milliseconds by busy-waiting. -//! -//! @param millis The number of milliseconds to wait for -void delay_ms(uint32_t millis); diff --git a/platform/snowy/boot/src/util/misc.c b/platform/snowy/boot/src/util/misc.c deleted file mode 100644 index 316df39469..0000000000 --- a/platform/snowy/boot/src/util/misc.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "misc.h" -#include "system/logging.h" -#include "system/passert.h" - -#include -#include -#include - -int32_t sign_extend(uint32_t a, int bits) { - if (bits == 32) { - return a; - } - - // http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend - int const m = 1U << (bits - 1); // mask can be pre-computed if b is fixed - - a = a & ((1U << bits) - 1); // (Skip this if bits in x above position b are already zero.) - return (a ^ m) - m; -} - -int32_t serial_distance32(uint32_t a, uint32_t b) { - return serial_distance(a, b, 32); -} - -int32_t serial_distance(uint32_t a, uint32_t b, int bits) { - // See https://en.wikipedia.org/wiki/Serial_Number_Arithmetic - const int64_t a_minus_b = a - b; - const int64_t b_minus_a = b - a; - const bool a_is_earlier_than_b = (a < b && b_minus_a < (1 << (bits - 1))) || (a > b && a_minus_b > (1 << (bits - 1))); - return sign_extend(a_is_earlier_than_b ? -a_minus_b : b_minus_a, bits); -} - -int ceil_log_two(uint32_t n) { - // clz stands for Count Leading Zeroes. We use it to find the MSB - int msb = 31 - __builtin_clz(n); - // popcount counts the number of set bits in a word (1's) - bool power_of_two = __builtin_popcount(n) == 1; - // if not exact power of two, use the next power of two - // we want to err on the side of caution and want to - // always round up - return ((power_of_two) ? msb : (msb + 1)); -} - -uint8_t count_bits_set(uint8_t *bitset_bytes, int num_bits) { - uint8_t num_bits_set = 0; - int num_bytes = (num_bits + 7) / 8; - if ((num_bits % 8) != 0) { - bitset_bytes[num_bytes] &= ((0x1 << (num_bits)) - 1); - } - - for (int i = 0; i < num_bytes; i++) { - num_bits_set += __builtin_popcount(bitset_bytes[i]); - } - - return (num_bits_set); -} - -void itoa(uint32_t num, char *buffer, int buffer_length) { - if (buffer_length < 11) { - PBL_LOG(LOG_LEVEL_WARNING, "ito buffer too small"); - return; - } - *buffer++ = '0'; - *buffer++ = 'x'; - - for (int i = 7; i >= 0; --i) { - uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4); - - char c; - if (digit < 0xa) { - c = '0' + digit; - } else if (digit < 0x10) { - c = 'a' + (digit - 0xa); - } else { - c = ' '; - } - - *buffer++ = c; - } - *buffer = '\0'; -} - -static int8_t ascii_hex_to_int(const uint8_t c) { - if (isdigit(c)) return c - '0'; - if (isupper(c)) return (c - 'A') + 10; - if (islower(c)) return (c - 'a') + 10; - - return -1; -} - -static uint8_t ascii_hex_to_uint(const uint8_t msb, const uint8_t lsb) { - return 16 * ascii_hex_to_int(msb) + ascii_hex_to_int(lsb); -} - -uintptr_t str_to_address(const char *address_str) { - unsigned int address_str_length = strlen(address_str); - - if (address_str_length < 3) return -1; // Invalid address, must be at least 0x[0-9a-f]+ - if (address_str[0] != '0' || address_str[1] != 'x') return -1; // Incorrect address prefix. - - address_str += 2; - - uintptr_t address = 0; - for (; *address_str; ++address_str) { - int8_t c = ascii_hex_to_int(*address_str); - if (c == -1) return -1; // Unexpected character - - address = (address * 16) + c; - } - - return address; -} - -bool convert_bt_addr_hex_str_to_bd_addr(const char *hex_str, uint8_t *bd_addr, const unsigned int bd_addr_size) { - const int len = strlen(hex_str); - if (len != 12) { - return false; - } - - uint8_t* src = (uint8_t*) hex_str; - uint8_t* dest = bd_addr + bd_addr_size - 1; - - for (unsigned int i = 0; i < bd_addr_size; ++i, src += 2, --dest) { - *dest = ascii_hex_to_uint(src[0], src[1]); - } - - return true; -} - -uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value) { - if (*attempt > 31) { - return max_value; - } - uint32_t backoff_multiplier = 0x1 << (*attempt)++; - uint32_t next_value = initial_value * backoff_multiplier; - return MIN(next_value, max_value); -} - -// Based on DJB2 Hash -uint32_t hash(const uint8_t *bytes, const uint32_t length) { - uint32_t hash = 5381; - - if (length == 0) { - return hash; - } - - uint8_t c; - const uint8_t *last_byte = bytes + length; - while (bytes++ != last_byte) { - c = *bytes; - hash = ((hash << 5) + hash) + c; - } - return hash; -} - -const char *bool_to_str(bool b) { - if (b) { - return "yes"; - } else { - return "no"; - } -} - -// Override libgcc's table-driven __builtin_popcount implementation -#ifdef __arm__ -int __popcountsi2(uint32_t val) { - // Adapted from http://www.sciencezero.org/index.php?title=ARM%3a_Count_ones_%28bit_count%29 - uint32_t tmp; - __asm("and %[tmp], %[val], #0xaaaaaaaa\n\t" - "sub %[val], %[val], %[tmp], lsr #1\n\t" - - "and %[tmp], %[val], #0xcccccccc\n\t" - "and %[val], %[val], #0x33333333\n\t" - "add %[val], %[val], %[tmp], lsr #2\n\t" - - "add %[val], %[val], %[val], lsr #4\n\t" - "and %[val], %[val], #0x0f0f0f0f\n\t" - - "add %[val], %[val], %[val], lsr #8\n\t" - "add %[val], %[val], %[val], lsr #16\n\t" - "and %[val], %[val], #63\n\t" - : [val] "+l" (val), [tmp] "=&l" (tmp)); - return val; -} -#endif // __arm__ diff --git a/platform/snowy/boot/src/util/misc.h b/platform/snowy/boot/src/util/misc.h deleted file mode 100644 index 880b81fb17..0000000000 --- a/platform/snowy/boot/src/util/misc.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) -#define ABS(a) (((a) > 0) ? (a) : -1 * (a)) -#define CLIP(n, min, max) ((n)<(min)?(min):((n)>(max)?(max):(n))) - -#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000) - -#define KiBYTES(k) ((k) * 1024) // Bytes to Kibibytes -#define MiBYTES(m) ((m) * 1024 * 1024) // Bytes to Mebibytes -#define EiBYTES(e) ((e) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) // Bytes to Exbibytes - -// Stolen from http://stackoverflow.com/a/8488201 -#define GET_FILE_NAME(file) (strrchr(file, '/') ? (strrchr(file, '/') + 1) : (file)) - -#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0])) - -#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) - -static inline void swap16(int16_t *a, int16_t *b) { - int16_t t = *a; - *a = *b; - *b = t; -} - -int32_t sign_extend(uint32_t a, int bits); - -//! Calculates the distance (end - start), taking a roll-over into account as good as it can get. -int32_t serial_distance32(uint32_t start, uint32_t end); - -//! Calculates the distance (end - start), taking a roll-over into account as good as it can get. -//! @param bits the number of bits that are valid in start and end. -int32_t serial_distance(uint32_t start, uint32_t end, int bits); - -void itoa(uint32_t num, char *buffer, int buffer_length); - -/* - * find the log base two of a number rounded up - */ -int ceil_log_two(uint32_t n); - -//! Count the number of bits that are set to 1 in a multi-byte bitset. -//! @param bitset_bytes The bytes of the bitset -//! @param num_bits The width of the bitset -//! @note this function zeroes out any bits in the last byte if there -//! are more bits than num_bits. -uint8_t count_bits_set(uint8_t *bitset_bytes, int num_bits); - -uintptr_t str_to_address(const char *address_str); - -uint32_t hash(const uint8_t *bytes, const uint32_t length); - -const char *bool_to_str(bool b); - -//! @param hex 12-digit hex string representing a BT address -//! @param addr Points to a SS1 BD_ADDR_t as defined in BTBTypes.h -//! @return True on success -bool convert_bt_addr_hex_str_to_bd_addr(const char *hex_str, uint8_t *bd_addr, const unsigned int bd_addr_size); - -/** - * Compute the next backoff interval using a bounded binary expoential backoff formula. - * - * @param[in,out] attempt The number of retries performed so far. This count will be incremented by - * the function. - * @param[in] initial_value The inital backoff interval. Subsequent backoff attempts will be this - * number multiplied by a power of 2. - * @param[in] max_value The maximum backoff interval that returned by the function. - * @return The next backoff interval. - */ -uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value); - -/* - * The -Wtype-limits flag generated an error with the previous IS_SIGNED maco. - * If an unsigned number was passed in the macro would check if the unsigned number was less than 0. - */ -#define IS_SIGNED(var) (__builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned char), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned short), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned int), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned long), false, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(__typeof__(var), unsigned long long), false, true))))) \ -) diff --git a/platform/snowy/boot/src/util/net.h b/platform/snowy/boot/src/util/net.h deleted file mode 100644 index 3d975cff3e..0000000000 --- a/platform/snowy/boot/src/util/net.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// When compiling test, the host OS might have conflicting defines for this: -#undef ntohs -#undef htons -#undef ntohl -#undef htonl -#undef ltohs -#undef ltohl - -static inline uint16_t ntohs(uint16_t v) { - // return ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8); - return __builtin_bswap16(v); -} - -static inline uint16_t htons(uint16_t v) { - return ntohs(v); -} - -static inline uint32_t ntohl(uint32_t v) { - // return ((v & 0x000000ff) << 24) | - // ((v & 0x0000ff00) << 8) | - // ((v & 0x00ff0000) >> 8) | - // ((v & 0xff000000) >> 24); - return __builtin_bswap32(v); -} - -static inline uint32_t htonl(uint32_t v) { - return ntohl(v); -} - -#define ltohs(v) (v) -#define ltohl(v) (v) - -// Types for values in network byte-order. They are wrapped in structs so that -// the compiler will disallow implicit casting of these types to or from -// integral types. This way it is a compile error to try using variables of -// these types without first performing a byte-order conversion. -// There is no overhead for wrapping the values in structs. -typedef struct net16 { - uint16_t v; -} net16; - -typedef struct net32 { - uint32_t v; -} net32; - -static inline uint16_t ntoh16(net16 net) { - return ntohs(net.v); -} - -static inline net16 hton16(uint16_t v) { - return (net16){ htons(v) }; -} - -static inline uint32_t ntoh32(net32 net) { - return ntohl(net.v); -} - -static inline net32 hton32(uint32_t v) { - return (net32){ htonl(v) }; -} diff --git a/platform/snowy/boot/src/util/version.c b/platform/snowy/boot/src/util/version.c deleted file mode 100644 index 14c1cf0225..0000000000 --- a/platform/snowy/boot/src/util/version.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "drivers/crc.h" -#include "drivers/flash.h" -#include "flash_region.h" -#include "git_version.auto.h" -#include "system/firmware_storage.h" -#include "system/passert.h" -#include "version.h" - -//! The linker inserts the build id as an "elf external note" structure: -struct ElfExternalNote { - uint32_t name_length; - uint32_t data_length; - uint32_t type; // NT_GNU_BUILD_ID = 3 - uint8_t data[]; // concatenated name ('GNU') + data (build id) -}; - -//! This symbol and its contents are provided by the linker script, see the -//! .note.gnu.build-id section in src/fw/stm32f2xx_flash_fw.ld -extern const struct ElfExternalNote TINTIN_BUILD_ID; - -const FirmwareMetadata TINTIN_METADATA __attribute__ ((section (".pbl_fw_version"))) = { - .version_timestamp = GIT_TIMESTAMP, - .version_tag = GIT_TAG, - .version_short = GIT_REVISION, - -#ifdef RECOVERY_FW - .is_recovery_firmware = true, -#else - .is_recovery_firmware = false, -#endif - -#if defined(BOARD_BIGBOARD) - .hw_platform = FirmwareMetadataPlatformPebbleOneBigboard, -#elif defined(BOARD_BB2) - .hw_platform = FirmwareMetadataPlatformPebbleOneBigboard2, -#elif defined(BOARD_V2_0) - .hw_platform = FirmwareMetadataPlatformPebbleTwoPointZero, -#elif defined(BOARD_V1_5) - .hw_platform = FirmwareMetadataPlatformPebbleOnePointFive, -#elif defined(BOARD_EV2_4) - .hw_platform = FirmwareMetadataPlatformPebbleOneEV2_4, -#else - .hw_platform = FirmwareMetadataPlatformUnknown, -#endif - - .metadata_version = FW_METADATA_CURRENT_STRUCT_VERSION, -}; - -bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata) { - if (out_metadata == NULL) { - return false; - } - memcpy(out_metadata, &TINTIN_METADATA, sizeof(FirmwareMetadata)); - return true; -} - -static bool prv_version_copy_flash_fw_metadata(FirmwareMetadata *out_metadata, uint32_t flash_address) { - if (out_metadata == NULL) { - return false; - } - - FirmwareDescription firmware_description = firmware_storage_read_firmware_description(flash_address); - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - memset(out_metadata, 0, sizeof(FirmwareMetadata)); - return false; - } - // The FirmwareMetadata is stored at the end of the binary: - uint32_t offset = firmware_description.description_length + firmware_description.firmware_length - sizeof(FirmwareMetadata); - flash_read_bytes((uint8_t*)out_metadata, flash_address + offset, sizeof(FirmwareMetadata)); - return true; -} - -bool version_copy_recovery_fw_metadata(FirmwareMetadata *out_metadata) { - return prv_version_copy_flash_fw_metadata(out_metadata, FLASH_REGION_SAFE_FIRMWARE_BEGIN); -} - -bool version_copy_update_fw_metadata(FirmwareMetadata *out_metadata) { - return prv_version_copy_flash_fw_metadata(out_metadata, FLASH_REGION_FIRMWARE_SCRATCH_BEGIN); -} - -bool version_copy_recovery_fw_version(char* dest, const int dest_len_bytes) { - FirmwareMetadata out_metadata; - bool success = version_copy_recovery_fw_metadata(&out_metadata); - if (success) { - strncpy(dest, out_metadata.version_tag, dest_len_bytes); - } - return success; -} - -bool version_is_prf_installed(void) { - FirmwareDescription firmware_description = - firmware_storage_read_firmware_description(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - return false; - } - - uint32_t flash_address = FLASH_REGION_SAFE_FIRMWARE_BEGIN + firmware_description.description_length; - uint32_t crc = crc_calculate_flash(flash_address, firmware_description.firmware_length); - return crc == firmware_description.checksum; -} - -const uint8_t * version_get_build_id(size_t *out_len) { - if (out_len) { - *out_len = TINTIN_BUILD_ID.data_length; - } - return &TINTIN_BUILD_ID.data[TINTIN_BUILD_ID.name_length]; -} - -void version_copy_build_id_hex_string(char *buffer, size_t buffer_bytes_left) { - size_t build_id_bytes_left; - const uint8_t *build_id = version_get_build_id(&build_id_bytes_left); - while (buffer_bytes_left >= 3 /* 2 hex digits, plus zero terminator */ - && build_id_bytes_left > 0) { - snprintf(buffer, buffer_bytes_left, "%02x", *build_id); - - buffer += 2; - buffer_bytes_left -= 2; - - build_id += 1; - build_id_bytes_left -= 1; - } -} - -static void version_fw_version_to_major_minor(unsigned int *major, unsigned int *minor, - char *version_str) { - // read in the two X's (vX.X) - sscanf(version_str, "v%u.%u", major, minor); -} - -//! Compares its two arguments for order. Returns a negative integer, zero, or a positive integer -//! if the first argument is less than, equal to, or greater than the second. -static int8_t prv_version_compare_fw_version_tags(char *fw1_version, char *fw2_version) { - unsigned int major1, minor1, major2, minor2; - version_fw_version_to_major_minor(&major1, &minor1, fw1_version); - version_fw_version_to_major_minor(&major2, &minor2, fw2_version); - - if (major1 != major2) { // do the major versions differ? - return (major1 - major2); - } - - if (minor1 != minor2) { // do the minor versions differ? - return (minor1 - minor2); - } - - return (0); // versions are the same -} - -bool version_fw_downgrade_detected(void) { - FirmwareMetadata running_meta_data, update_meta_data; - version_copy_running_fw_metadata(&running_meta_data); - version_copy_update_fw_metadata(&update_meta_data); - - int rv = prv_version_compare_fw_version_tags(update_meta_data.version_tag, - running_meta_data.version_tag); - - // return true is the new firmware to be updated to is a version less than the old one. - return (rv < 0); -} diff --git a/platform/snowy/boot/src/util/version.h b/platform/snowy/boot/src/util/version.h deleted file mode 100644 index 10babea82c..0000000000 --- a/platform/snowy/boot/src/util/version.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define FW_METADATA_CURRENT_STRUCT_VERSION 0x1 -#define FW_METADATA_VERSION_SHORT_BYTES 8 -#define FW_METADATA_VERSION_TAG_BYTES 32 - -typedef enum { - FirmwareMetadataPlatformUnknown = 0, - FirmwareMetadataPlatformPebbleOneEV1 = 1, - FirmwareMetadataPlatformPebbleOneEV2 = 2, - FirmwareMetadataPlatformPebbleOneEV2_3 = 3, - FirmwareMetadataPlatformPebbleOneEV2_4 = 4, - FirmwareMetadataPlatformPebbleOnePointFive = 5, - FirmwareMetadataPlatformPebbleTwoPointZero = 6, - FirmwareMetadataPlatformPebbleOneBigboard = 0xff, - FirmwareMetadataPlatformPebbleOneBigboard2 = 0xfe, -} FirmwareMetadataPlatform; - -// WARNING: changes in this struct must be reflected in: -// - iOS/PebblePrivateKit/PebblePrivateKit/PBBundle.m - -struct FirmwareMetadata { - uint32_t version_timestamp; - char version_tag[FW_METADATA_VERSION_TAG_BYTES]; - char version_short[FW_METADATA_VERSION_SHORT_BYTES]; - const bool is_recovery_firmware; - const uint8_t hw_platform; - const uint8_t metadata_version; //!< This should be the last field, since we put the meta data struct at the end of the fw binary. -} __attribute__((__packed__)); -typedef struct FirmwareMetadata FirmwareMetadata; - -extern const FirmwareMetadata TINTIN_METADATA; - -//! Copies the version metadata of the running firmware in the provided struct. -//! @param[out] out_metadata pointer to a FirmwareMetadata to which to copy the version metadata -//! @returns true if it successfully copied the version metadata. -bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata); - -//! Copies the version metadata of the recovery firmware in the provided struct. -//! If there is no valid metadata available, the struct will be wiped to be all zeroes. -//! @param[out] out_metadata pointer to a FirmwareMetadata to which to copy the version metadata -//! @returns true if it successfully copied the version metadata. -bool version_copy_recovery_fw_metadata(FirmwareMetadata *out_metadata); - -//! Copies the version metadata of the update firmware located in -//! FLASH_REGION_FIRMWARE_SCRATCH_BEGIN into the provided struct. -//! If there is no valid metadata available, the struct will be wiped to be all zeroes. -//! @param[out] out_metadata pointer to a FirmwareMetadata to which to copy the version metadata -//! @returns true if it successfully copied the version metadata. -bool version_copy_update_fw_metadata(FirmwareMetadata *out_metadata); - -//! Read recovery version_short from flash and copy to dest; copy at most -//! dest_len_bytes - 1 before being null-terminated via strncpy() -//! -//! @param dest: char[dest_len_bytes] -//! @returns true on success, false otherwise -bool version_copy_recovery_fw_version(char* dest, const int dest_len_bytes); - -//! Checks to see if a valid PRF is installed with a correct checksum. -//! @return true if a PRF is installed, false otherwise. -bool version_is_prf_installed(void); - -//! @return Pointer to the GNU build id data. This is a hash of the firmware -//! that is generated by the linker and uniquely identifies the binary. -//! @param[out] out_len The length of the build id in bytes. -const uint8_t * version_get_build_id(size_t *out_len); - -//! Copies a hex C-string of the build id into the supplied buffer. -//! Get the build id from an elf, using `arm-none-eabi-readelf -n tintin_fw.elf` -//! @param[out] buffer The buffer into which the string should be copied. -//! @param max_length The length of buffer. -void version_copy_build_id_hex_string(char *buffer, size_t max_length); - -//! Checks the firmware stored in FLASH_REGION_FIRMWARE_SCRATCH_BEGIN and compares it to the -//! currently running firmware. -//! @returns true if a downgrade is detected. -bool version_fw_downgrade_detected(void); diff --git a/platform/snowy/boot/test/test_system_flash.c b/platform/snowy/boot/test/test_system_flash.c deleted file mode 100644 index c7a25e8223..0000000000 --- a/platform/snowy/boot/test/test_system_flash.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "clar.h" - -#include "drivers/system_flash.h" - -#define KiB *1024 - -// Set bits n..0 in a bit-vector (zero-indexed) -#define BITS(n) ((((uint32_t)(1<<((n)+1)))) - 1) -// Set bits y..x in a bit-vector (x <= y; x, y >= 0) -#define BITS_BETWEEN(x,y) (BITS(y) & ~BITS(x-1)) - -// Yo dawg, I heard you like tests so I put tests in your tests so you can test -// your tests while you test! -void test_system_flash__bit_range_macros(void) { - cl_assert_equal_i(0b1, BITS(0)); - cl_assert_equal_i(0b00011111, BITS(4)); - cl_assert_equal_i(0b00111111, BITS_BETWEEN(0,5)); - cl_assert_equal_i(0b00111000, BITS_BETWEEN(3,5)); - cl_assert_equal_i(0b00010000, BITS_BETWEEN(4,4)); -} - -// Flash memory is organized into twelve sectors of unequal sizes. -// Sectors 0-3 are 16 KiB. Sector 4 is 64 KiB. -// The remaining sectors are 128 KiB. - -// Bitset of sectors that have been "erased" -static uint32_t erased_sector; -static bool flash_locked, flash_flags_set; -static FLASH_Status return_status; -uint8_t *flash_written_data; -bool *flash_written_flag; -void *source_buffer; -uint32_t flash_data_start, flash_data_length; -bool callback_called; - -void test_system_flash__initialize(void) { - erased_sector = 0; - flash_locked = true; - flash_flags_set = false; - return_status = FLASH_COMPLETE; - flash_written_data = NULL; - flash_written_flag = NULL; - flash_data_start = 0; - flash_data_length = 0; - callback_called = false; -} - -void test_system_flash__cleanup(void) { - free(flash_written_data); - free(flash_written_flag); -} - -void test_system_flash__erase_zero_bytes(void) { - cl_assert(system_flash_erase(FLASH_BASE, 0, NULL, NULL)); - cl_assert_equal_i(0, erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte_in_middle_of_sector(void) { - cl_assert(system_flash_erase(FLASH_BASE + 12345, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_some_sectors_from_beginning(void) { - cl_assert(system_flash_erase(FLASH_BASE, 128 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 4), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_full_flash(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1024 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 11), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_sector_0(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_16KB_sectors(void) { - cl_assert(system_flash_erase(FLASH_BASE, 48 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 2), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_sectors_1_thru_10(void) { - cl_assert(system_flash_erase(FLASH_BASE + 25 KiB, 871 KiB - 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(1, 10), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_sectors_1_thru_11(void) { - cl_assert(system_flash_erase(FLASH_BASE + 25 KiB, 871 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(1, 10), erased_sector); - cl_assert(flash_locked); -} - -void callback_is_called_cb(uint32_t num, uint32_t den, void *context) { - callback_called = true; - cl_assert_equal_i(8675309, (uintptr_t)context); -} - -void test_system_flash__callback_is_called(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, callback_is_called_cb, - (void *)8675309)); - cl_assert(callback_called); - cl_assert(flash_locked); -} - -void test_system_flash__handle_erase_error(void) { - return_status = FLASH_ERROR_OPERATION; - cl_assert(!system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert(flash_locked); -} - -void error_in_middle_cb(uint32_t num, uint32_t den, void *context) { - int *countdown = context; - if ((*countdown)-- == 0) { - return_status = FLASH_ERROR_OPERATION; - } -} - -void test_system_flash__handle_erase_error_mid_operation(void) { - int countdown = 3; - cl_assert(!system_flash_erase(FLASH_BASE, 512 KiB, error_in_middle_cb, - &countdown)); - cl_assert(flash_locked); - cl_assert_(countdown <= 0, "Callback not called enough times"); -} - - -void malloc_flash_data(uint32_t size) { - flash_data_length = size; - flash_written_data = malloc(size * sizeof(uint8_t)); - cl_assert(flash_written_data); - flash_written_flag = malloc(size * sizeof(bool)); - cl_assert(flash_written_flag); -} - -void assert_flash_unwritten(uint32_t start, uint32_t length) { - for (uint32_t i = 0; i < length; ++i) { - cl_assert(flash_written_flag[start + i] == false); - } -} - -void test_system_flash__write_simple(void) { - const char testdata[] = "The quick brown fox jumps over the lazy dog."; - malloc_flash_data(100); - flash_data_start = FLASH_BASE; - cl_assert(system_flash_write( - FLASH_BASE + 10, testdata, sizeof(testdata), callback_is_called_cb, - (void *)8675309)); - cl_assert(flash_locked); - cl_assert_equal_s(testdata, (const char *)&flash_written_data[10]); - assert_flash_unwritten(0, 10); - assert_flash_unwritten(10 + sizeof(testdata), 90 - sizeof(testdata)); - cl_assert(callback_called); -} - -void test_system_flash__write_error(void) { - return_status = FLASH_ERROR_OPERATION; - malloc_flash_data(10); - flash_data_start = FLASH_BASE; - cl_assert(!system_flash_write(FLASH_BASE, "abc", 3, NULL, NULL)); - cl_assert(flash_locked); - assert_flash_unwritten(0, 10); -} - -extern void FLASH_Lock(void) { - flash_locked = true; -} - -extern void FLASH_Unlock(void) { - flash_locked = false; -} - -extern void FLASH_ClearFlag(uint32_t FLASH_FLAG) { - flash_flags_set = false; -} - -extern FLASH_Status FLASH_EraseSector(uint32_t sector, uint8_t voltage_range) { - // Pretty sure FLASH_Sector_N defines are simply 8*N, at least for the first - // twelve sectors. - cl_assert_(!flash_locked, "Attempted to erase a locked flash"); - cl_assert_(IS_FLASH_SECTOR(sector), "Sector number out of range"); - cl_assert(IS_VOLTAGERANGE(voltage_range)); - cl_check_(flash_flags_set == false, "Forgot to clear flags before erasing"); - cl_check_((erased_sector & (1 << sector/8)) == 0, - "Re-erasing an already erased sector"); - flash_flags_set = true; - if (return_status == FLASH_COMPLETE) { - erased_sector |= (1 << sector/8); - } - return return_status; -} - -extern FLASH_Status FLASH_ProgramByte(uint32_t address, uint8_t data) { - cl_assert_(!flash_locked, "Attempted to write to a locked flash"); - cl_assert_(address >= flash_data_start && - address < flash_data_start + flash_data_length, - "Address out of range"); - cl_assert_(flash_written_flag[address - flash_data_start] == false, - "Overwriting an already-written byte"); - if (return_status == FLASH_COMPLETE) { - flash_written_data[address - flash_data_start] = data; - } - return return_status; -} - -extern void dbgserial_print(char *str) { - fprintf(stderr, "%s", str); -} - -extern void dbgserial_print_hex(uint32_t num) { - fprintf(stderr, "0x%.08x", num); -} - -extern void dbgserial_putstr(char *str) { - fprintf(stderr, "%s\n", str); -} - diff --git a/platform/snowy/boot/vendor/wscript b/platform/snowy/boot/vendor/wscript deleted file mode 100644 index d8255b55c8..0000000000 --- a/platform/snowy/boot/vendor/wscript +++ /dev/null @@ -1,25 +0,0 @@ -def configure(conf): - conf.env.append_unique('DEFINES', 'STM32F429_439xx') - -def build(bld): - stm32_basedir = 'STM32F4xx_DSP_StdPeriph_Lib_V1.3.0/' - stm32_srcdirs = [stm32_basedir + subpath for subpath in ( - '', 'Libraries/STM32F4xx_StdPeriph_Driver/src', - 'Libraries/CMSIS/CM3/CoreSupport')] - stm32_sources = sum( - [bld.path.ant_glob('%s/*.c' % d, excl=['**/stm32f4xx_fsmc.c']) - for d in stm32_srcdirs], []) - - stm32_incpath_base = stm32_basedir + 'Libraries/' - stm32_includes = [ stm32_incpath_base + subpath for subpath in ( - 'CMSIS/Include', 'CMSIS/Device/ST/STM32F4xx/Include', - 'STM32F4xx_StdPeriph_Driver/inc')] - - stm32_includes += ['stm32_conf'] - - bld.stlib(source=stm32_sources, - target='stm32_stdlib', - includes=stm32_includes, - export_includes=stm32_includes) - -# vim:filetype=python diff --git a/platform/snowy/boot/waf b/platform/snowy/boot/waf deleted file mode 100755 index 4d6635a842..0000000000 --- a/platform/snowy/boot/waf +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# encoding: ISO8859-1 -# Thomas Nagy, 2005-2014 - -""" -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -""" - -import os, sys, inspect - -VERSION="1.8.5" -REVISION="69c046f2063dbb528ecf1ab20607ba25" -INSTALL='' -C1='#:' -C2='#5' -C3='#/' -cwd = os.getcwd() -join = os.path.join - - -WAF='waf' -def b(x): - return x -if sys.hexversion>0x300000f: - WAF='waf3' - def b(x): - return x.encode() - -def err(m): - print(('\033[91mError: %s\033[0m' % m)) - sys.exit(1) - -def unpack_wafdir(dir, src): - f = open(src,'rb') - c = 'corrupt archive (%d)' - while 1: - line = f.readline() - if not line: err('run waf-light from a folder containing waflib') - if line == b('#==>\n'): - txt = f.readline() - if not txt: err(c % 1) - if f.readline() != b('#<==\n'): err(c % 2) - break - if not txt: err(c % 3) - txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) - - import shutil, tarfile - try: shutil.rmtree(dir) - except OSError: pass - try: - for x in ('Tools', 'extras'): - os.makedirs(join(dir, 'waflib', x)) - except OSError: - err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) - - os.chdir(dir) - tmp = 't.bz2' - t = open(tmp,'wb') - try: t.write(txt) - finally: t.close() - - try: - t = tarfile.open(tmp) - except: - try: - os.system('bunzip2 t.bz2') - t = tarfile.open('t') - tmp = 't' - except: - os.chdir(cwd) - try: shutil.rmtree(dir) - except OSError: pass - err("Waf cannot be unpacked, check that bzip2 support is present") - - try: - for x in t: t.extract(x) - finally: - t.close() - - for x in ('Tools', 'extras'): - os.chmod(join('waflib',x), 493) - - if sys.hexversion<0x300000f: - sys.path = [join(dir, 'waflib')] + sys.path - import fixpy2 - fixpy2.fixdir(dir) - - os.remove(tmp) - os.chdir(cwd) - - try: dir = unicode(dir, 'mbcs') - except: pass - try: - from ctypes import windll - windll.kernel32.SetFileAttributesW(dir, 2) - except: - pass - -def test(dir): - try: - os.stat(join(dir, 'waflib')) - return os.path.abspath(dir) - except OSError: - pass - -def find_lib(): - src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) - base, name = os.path.split(src) - - #devs use $WAFDIR - w=test(os.environ.get('WAFDIR', '')) - if w: return w - - #waf-light - if name.endswith('waf-light'): - w = test(base) - if w: return w - err('waf-light requires waflib -> export WAFDIR=/folder') - - dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) - for i in (INSTALL,'/usr','/usr/local','/opt'): - w = test(i + '/lib/' + dirname) - if w: return w - - #waf-local - dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) - w = test(dir) - if w: return w - - #unpack - unpack_wafdir(dir, src) - return dir - -wafdir = find_lib() -sys.path.insert(0, wafdir) - -if __name__ == '__main__': - - from waflib import Scripting - Scripting.waf_entry_point(cwd, VERSION, wafdir) - -#==> -#BZh91AY&SY=y `"Ѐ#/!2Xa>w#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/Ir,iW5*C]sQvl7s_{E}x}ޥζ=╅m۶.l=ݻShz|.ۛxo9v>֝|4vZ[֌n=#/#/#/#/̀Km`{R=`(ݻ{מGCVnW:FREj#:1PIT=Rj*hZ(y{ϭqRwupl2鮻vn޹O- -}ݭ[a[˚-nvmG'Kt}ķ٣_c> #:#/]P(^u^{!#5W{ l]3s:#/UPKcJhdu{=xvlӮ<޼s{{TdUW>͝nup;Ƿ:ԶwzHh#5{(ހsZG[tw!#/Iq4y]Rov{gϝos[݌{fyUvw}\zewu86{_p-zׄxJqe9(h=P@m{}r[|uz۳܇^}z7=#/s6z>A@#:Gl*#5-UkjNjTr=tw1ɮovNE`pto[۰ҡ־>#/w#/#/o%|{^sݷ۽﮽_w;ڧ(NEu==ڀ{d7r$;z^;k#/;{Y˳(hy{wsZ\o6vc_ouuz޻on-`bPsGUiZ}{ ͮ%wק{RUkTS= `3{#:to7׸^sέ/A@ATk;}o_Z}Vnuw; 5y#5==ɛ/>.`:[;-};L#5mz9_wwV>Ӷƈ\\ݒa{]n&{+zeW֚y{Jfy0ѧ=n_}{]AOSﶫHn3싼]SH #/ #/h L#LM4E@z@ #5#54A#/@A0'##:(Sz<#5#/#/#/#/#/H SdhM&=?J5?&ʞ(O#:4#5h#/#/z$A&!~bSA<6#/#5#/#/#54b#/#/ 4#/14&4hLL##ž=M M44yCA& S1&F?ȆȧoTb(~d#/@_3~@G|֗mcA5j)",F #/,P"ߙe~Ŋ=be\L^'SuRĒ`Ejs1fO0ݘ`4 8$ Do%%ljaJH97. ӅqY3ZJucĵ1uJUcqjHY*_3,lB#:70f%]ֶUb#/VAQKED#/$1FlD(F"T(%DdTEADE\H ı &D4c6$RDJ)[IP%4mJADI&dJ% hI)R$%#:A0e$Hj2͊X#:IE4fţQm*RJbd2EE+X0͙jɔbT5I%HYI5̍HhԄlfJ4 E)T͌LQ3Jbi e6 #/CL"QadH)bY,"d!"RR&H6 j 1l,L(%iD,LTlMe2&F2JQLZBDbL!$#:lFlH6AHbL3b $Ԛ)#"&dFiL1)*1&i-$ɢS͢4(Њ!JHFh43T@)E%!6ih#5Kdlh3H $*$ʂfmP(̦R#5 BfƘHԙ)3#52RFʕL QdA%()@02CL"AaZhf̲,#:RMdFdPY(il b(llfj# JfiLBCaS&cjlReFLIB$,Jiɥ$6MFLC dXmd$"Id2M4#)-%*4TmȔƒf(i1 !eF,ME#V&ZLIdD RQc(!UK[h)-dZJ#5S%2LcY-h2%)EbQSf24VVK(MbEF̨(-j"JPM$k1PPȦd62J"б2f5-,HTXfe*F#:5"lK*2mդBek5)Jib2"ƴiQ%Q2X3F*-m %lmQ"E!PhՊa&BMbJ)FDԪ5$F5!cTɕZ֚aQșYffI-E-%3SJZU+%T ښmMi45YJQjiFF"FHFA!h)`Sj6Ee6ғM$i)R)bZYR hJdƲ41BPرfHcԖfY2Ȧ`RLX016"(ؒQjf6b0hɱbaj4P(B6XFb4)0Va$IM%#)d!IDI&J!5QS3)IYV4HfIDle#fiʚ6-e6hIL*ejFLd6D1Q&J!"Ѩm4dJ#:M"LlRD bIR#:d4!FFM,+AIب10h)DՍc)#:M%Z6CDYfڦ42j-bJhiJ4*Y%IJRj؃TE%QQX&fbF[!fA-3% уi0j%2čT4e-QƲB (R6%  QIIJ#:-)Si5mfbZ +EjJ,Sdэ$&6J`у6V6-bi(e#:&BL,ڍhkImJ3fiebUS,UMIeFQ4eMHTQY+&(&Rm)FhФj)(hf,"MEchP-R٫&21bEVlZ-dֱYTM#i&0l,H٥DPLđ$I2"B Hc+4K-%3Ka")5pAILTD4OEpwQT*r'tF|ힴtd7ɅhiY#5`:1vD_<#/vǟe㌺vk,DGXA"!v ݩL#=XB`V#:4/֘!ۭkdRTx}+qWd]> d!cA'$#5Hi- `CK.m" "nc #5ؓLQn#:ʺ--Jޥ%`aqR^6eDOE_lvv֌* R#:y:Pt+qj%YR9imDHM˿3*86e4t #58]%I3o7w+4&TX~\ $^7<DLPU QKڛG#5ﯷ^Ĺ{R*ckcm 1-5݊4dP|>w(h#qCM*Vߺt\w]uj. H(J#5@D٭ dXTZcʟ=0Zh摜N`6JFb?^1@«I(j}m|c[J~1 PYO:Oٜ3vvR]cÂ&B#5t%}oh%jv6 }91ΩBQX#5:t,XZ2kՍqe[IXR/=/G0IQ:kE5ƔE-bMvAzHiLgg¨Y3LsfYr#Q7Č'HqI22Q/^Io]v~.ܻX^9ɼW.V5_?JY0Q_Pd,^[uvcE ᤤ>uIcBEЀ^uE !BKIIe/y9s㌣~a"4"1S4J6nUb+Œ7/Jm} p|#50#@6scQ9daҶd``ADVÿK۩2F6v,&Au`@z!bZXi!B P)#5d@-Q4,JgoeYRLH]ͼHx%D 6ZqIv4hlENt+<{jL(@XYCC5;!{l-U 8{%pX.cX>>(vK#:(8B) l`gC'0m6aRC/ߕ|7Nl8]Y? $ӂEs¯Y| Xm @=}Tm؍\&rZf[LNꔂ=omBKTWf!Ώ'&gva-wRcJ4}GTW qk|zCi i=Bp#:P} Qk]xb:*xVMܼTՈȠ\]\;m3FSjBщrecs;0rx*KNWqPR0TPs[SLJGd(v!҆=aEqR JGcH#~DӿXi^Z#5ndz!& +M#5jdb:F[(~D4<XJ wPcwwg|Xl+"ϻ!("ͯe³1-eQC,xޝfenݱ>f zie9J@["Lį/<'W=9#:}ٔ=u,`: kvsM}zUƂl,y$()5ǎy`2LHJD]#:#:F=E#:N 'O:퉯mO1~+>;Ԣ =8\6irt{PAFh2T]^3f ti\.ie*uPk( pK(,z<ƣ'0)N1ouv$k{#5bb))Dގ-FBdá#I֣{ajC2!qOTe[$SK Ogmx*∬"; )Az*ۺ7ccLõ\.~GIσ~tl$[(-s;rBv·2w)L媷q,WB"t'Ňݩv#z1s!ǡ[DZ(sSQsƦͿ5(߻3]}1,#5_$쫺#5;#:%oӋxIN$>`MYTSt#4DFIm/Z?",ä-}_ғs9-8vwFǹҡ5ի#5^S5bzk^c`ήn~i(8*0Eb{L#oVb?[&5K42/ci҂7dBU"s}h)s!تq=b#5ndt!R6H&ޛ$'u0rU8˟U+ܱN.0}!3 InrMMݒ3a#:XDHn70wPC(Quf*aѣ#5wg^x¬(fU6tUx2%8u2|M(@V`Xd麩ĵVΨ6#5sxa?FE(Ϯ{,D|ud#5z(1|ܼSVԠ-en-4:jjM4tF"s$hA`c.tDz^%IJUNjÎλH I'x!3\=S؜]%_WϞvӾ$Scc$9dvY}[30W:ȗYo=ؼ.8C_%?vQsi]z\P ֖GR}&8)h(i3U<L1ά&}(#[Q.GK?}Mqڀ667u-ؾҺL;m6W-|(%魽{aoTG%,G妏4uʭxQRtL?>VvsΈ^tiЪct|p (E1gٗZͪ24k`8{W'R#:tJ*LKCA5vdw9KJ\byZQL֩rq&#/Z@]Z~o@%5*g22XP԰#:巫LH_ jTǮ QoCtޙZY{C G9D()$iwCL_'s%imLvdQpŨE_#:!AQd78-y+'׌}+|}Xg̽)2`Ǎ˹|΃Jo㮺LR/d:?'kiűtP~ys5x({X5' 0R4]m^F)<;GxkpmlUc5R߇Y;gX$Mbryc{1Eq.4RU'u:[+Xc&hQ~mʇ0o]oٲ40T*aF1k,:Q:wBQP~5Sk)M+0iu=UƋZ?yKeJn-a9aFؾym2k 4W+BKxEl\爙ʠK+i#}zNeR&#5!2ksz;9mAv^ʳb(/ޯ#/'s+W{|x5oUM/+|u})]R\zk0+//~#:<}sF۩67Wtě$adħ%NE:hZ@r(=e/ E/o#51M{k)xhi?u bOês/oyٖ @™rߟ5{_~rZI :jK'TZkoCGh>jϭ5vF||2 #/ܫ_fW1!-88g5|btx;9?a4$u^<#5-|b].ZCQh_~-L0.=GTvgQInl 4S3Lff|Hch2*yM1Ba\E2kIC'j$tO]9#?f-ŅYQ43iIav%.e+N#5$3t%[ 1E#:BZL5#Y٢hq53{X$AT! `q7oSCCӮu&$N < !x#5c8㚰B,+*EɰB#/R}%0JVy!^|qz< DrJ)63RJ6L?_\yRO$j0GX#:l縆#5PFbin!{>W> l`31n #:t/E ~+ijj˦oOb_#qyAyZl"m8&/5l |@y0\/h@i2sf^vofmpUrcXI&L&ۑ.r#:b=+J.#5Kdzr~czUwly F9pt~#5+a2:W܎a-<>+zcm2GAA$j3J'_9.<`7eM 9ij{pIY(5}**>ﷃ[E$ (bKJb^ "wMwUr4đݻ[*fX+|WpL1 "VYX!hz1!YKmQEĀ {*V\"iGNkĒ wH8!j(#:)9$`v< q7K8Xp#@Xf0frΜrxd88>8ه4#:Œ*#D$[0oZ d0&7( wyy%CSZ)yG_Nc \CL릉7]F|I(#:y4Q%fvTMhj}̑Ճ{M$t#:wfrv9h-i 󅦌DC,u51|T+fsBRąE#:qnF@%lPA C%@m=dQXP_4&nw]?'jXYîɀq tlk5*kc_&5kxQ,m* BI fHs$_cOQa;FX'o #5nB!t-z |d=ꩵkLw33qEI ク8 #/Ib".(lZpKlӃ!pJ&ߔP!@rK5۱zvbC4Ʋ$@-s#7\3"\ո6ܹ2$F((vHloUk#5 >BC?i`?x2)c.D0¦NH7pȖ>}o?YȠ@+jLׯ9P4(^%;unbe0Ok,Mz5j0[ͤZ-ktڙzۯ^H`]F+% 끢ZEI>ꜷ 8BttK$Xj{PLUC%*OQOy6Soin#"Eot1-E]C#2ꢦm(|?u'';77cѹ۩-M!Pj\ph;pIސ'WWr@wf9BE1.+Qcri_?E Wl}>LÎ>InG1)!0j 4N_/ՍQ$ 1ו_;n.WLrY^Pmݻ~Q?I=ҿӿl3|=mgb7DwrWA EPԴ{kG~lEBb3vL%+1"Kf&ңkMID#:#:J*PS%BЭwKg*-LzpM!` >sEeEWwm췍o^=!h#;i4?'+Xpp+?8!w{a߶濕qS`UX[CNv{˺0*8AB 0?Xu[:BER=|& :HdERs#:~VмkmUEIqAj"S aE벖d?C?oͻ6'wύ[ʨr(Bh#G~$ʟ%и2NZ#(R= 00_?ϛ˕drjptK%XGw(@Wsʩ9IUA֯yV>ٟ:M^O#:8Qˊ3;Us@ٖGÄuzN (wgQwS)=nhF1]U}VZcF|#/l8h7Kfy&rCCE͜*ce7x<%QpjsJovsDa)pܚ)xZ%Thd 3#p&WIFw#5lPogz4I*޾]Ϟ5귚PW\[;!YoSßӧYߧ,dNQn\*VSUO]u\wͱc5CTȔ׳_'.6ӾhZ4=E>]UExӿUk,<_*;mɣgNG˸EG7*춘z*OHe6SR_+h9S!FϬm'{9vW|bg Ph{A| MRJ>¨ugre*딡1ŮihxRH0vV~8Qն;=h__`.dʛC[#g(SjE3W|wiFNLix?eTo >Ͽ=t?=)Ϸ}Gv(la.I{+?6McdgݨeWWZq|M#5Öǧ룧^oOk1 )8IRq..8$҈1YWچծ `\"0'|XaL[@LF##5<?G{>/ѳYyHM݇u3Lv+Y2׫fOZPy˼nWe>ǿOgu{.X@:9?qߝk+g>v>ƍS-j߅'[%ig^;3owhbi2Mۺ#/>~~5m=p!?W}Cԍgm|J۷)pz/}6GUʒeUG~,GU^0FOjWw#5G 1ݦMZ#: ݋ L6θ5-gUzt2V:1)]RhVqȶ#5HmU+bz*x]'RǛTꮌnK߮_J%Nq؜M6$(A?l%CsB۔ _4ENB#5˪Z2[O#5>jral}Z z#5jMs;4TCmQ|Gl!!͇g>|'9])SheYr4gj&c)}.bݑޱ?qu}Ӫʆ\hJѢ%?6WkZ%O:>q?p߫t1/jq뢚kzл{klO!PM$J@-HUGң/kyz=}kY\Ξa+jNz0m!dbы/<), Z@]/whp'O_袸ʺ=}?ފn~:}Y#:O9 TZG#5O'KpE9IY#5?c]"VYaҷ%h]u(  @CZ SZS.S-ܦmMd&0v i#lKL攵EjYS5nxy-Ń"6##:eŃc²֔Ї+ړ{76`F`-cU5O2˷Ny^Hӣ0&8F'r(` D4lE4 D ,@8b[jARE)@lƖd>&:EJDQxS"2`qKa Bk( (EIui0l" T=TV҉ӕD;`X`- ejP~AB3;)nn&֤iխ,2#=y|̢PnOU0G{QƬ:3}|[xao?#576cꉫ}SUWKHjUI䱞i#5Aͳ퉻V,rCu#5Sz#5LmKzgN T=ǶcsdJ5xz+]fǏFNtlS&Wwwwwe\7UM}IAuY1ISTYW|gpTz:44( @aG&DN=Nr@GzY}UL+#-aJ7I(Aaa BM=|aWqX2[#GS.#5rxN G4a -a)VKp(Pb E#5h<ˁeF#Հ"D_^OLjٔZ²۟qZ7'HD:ks['??sy9R"a1D#/n#&yvPتq!#/aWw{,M:cN~ y|vkH[5=Fb8HV/y͚'sn;wI%u̫#5[Cc<4ԡ-oB ֶ?4fyf(p&BZcUߛ8Έ (YU#5[he†U3U Q/b%?ǚdzH³8!'L=߅o^z?_Lɶ0#:,FD4\(VA0z&uA/T#/Ut0fS_V-[)#Z5c Q$NViGXSp##3 9%w-QC(EUE7Nٴ\ qyU@Q۽`DF)e`hp_G 5EGK8< U ίfdD6KELAAeMDAbrQeh)P5ڔ4w*pEت%u±&vN.8iCFKm#-x󼕦bZȑ 72)(xZ}JvtlS0-BtSNQ,<'o:ElmIvi]{{vBA%}c1CzWtѧM74JKڍ]n.x<">BQa(2`\#:3Q 9m1g’ĚM|ˌ/G#5&Dpi2$HFOGtg\#:WEwƍNpyct sRưั -(Qcb Izv!ח J1iHp4G4DAFO}yh_i#O꾊ygӇk~Z"#a/IrҠm>&;L.Iq*)4Wr+BPP3JB2W_`:er9q UTМk]ћ306hWoH33L- ީ@Q.E4[Z|e .׷;9#:AhJDwt1;CGL$E:Z571xq2QfĢxF:2#:(EN,=J|޳kg9ѕ:lCbsΰĶP"SƮ,llff\M1Q F8l1%N}wuCbŻLwIgv_GBeG ,{aW'jfVq8X<#5b s4 %Ҹc#c#5cnFT{fV#526!U*!"Le ڂ;3S+)|qیb 7XҚG3n4"TeY4ui1L%iq}76!L8t2a ps.j+77ٓ"5J.0ͅ☒u@Y.:73b24&Bmw.Bf.d+l:f#N?N&~ Rފi#aZ{ 8Y}ezO- 8r?#5Z./,R_} ȼNWȮƪ}%g0QoS;䚜d(NL4b^13#3(tUb˿ $&f>iW^[o1#˿/fçz|<$[b<pg3/ZVSB2#9MZX3UDZĜ}ה-bxdK81ˇĺ!lO#5<͌aV7u)SWY>F4cw6*:$ᕶ;e3=}4&3RRn}юL3v.PEgvGߡȦY1,$24&/o#54HskW ͽFy@ݓ[jpxhl^X#mZΘHe+A ݻa(΅T`pǡ]EpǼsݞ]i4b##:u7G#WV~pP}o[A]jUeu#jbYtQQ#:#5(/60]ڶ}] g]$p/^ODyаHe#:5вkn7z#6U6x|";3,Q3M>=S@c(÷YebIwC2tJ?ZYz5hk8ㆢywDh#53Ǹ5-sòEGI:pX<'-NXj;/麔[hUv~ɖ&]n/92l娍%&:-1}t$NFQwȺe#:4V(H'Zen0-7QY-f|Hg]IC$qgj_S'ݨ^</]wJ5^%2VޙfeI^G`z{pW 8hv!Ƽ1׍dv.?dȽִ}ug2xir3Y12֚?=2R$m ;trYCOǎw (m9{ TG#;= NgpYQ!d^XA<(r1w>hףtd1k/M2C9&Meaϭ56L:8}ۮB[|mR2J6O+9ZWty -QO(r#5OuĦ/7mL-#:죵XۋȒǧ74mF"!!+ӉAJkd|OW}aY]-j[-r\Ԯ6ÞʕLf`ADŽTWˏU4#?5VL;q K#5Jwp[|7i0qRG<6PvX?e7gpM1=}'*L(8p9q#5/IB]wc ']N@Dȅoʍtȓe k0R PM3A|cj~gqwFGF"[1:ʥo}ސ[]u>3 mtW!N#5~#5Sa7Y0y^Q!ȢZG*'fsWBhɻk"˒tՌie/1*XpygGX],pdh7:zʣtRYN'򇟀,h6Xͱ3[,s.:eĢYm#ٝȢ^g0R%؎7$ĝtK]Ugzh;ISjFȾsYd'.Kڃ['/fܢN{8Hʈ#WXF0ꙖRhulJᮤLsrDZ4/5-v%nGuo09t0lZ?;4k>XF,r3N4Gtvtԉx)&lf#mݠ?qv|`EfšZբh#.u&:C륥_ra}nG?nV&Oo'nb ˑe#:R#:薫"PyB{v42|4Բİ):veEˤO#MoulÞ<,vvQ[t~P5_ZbVOB1OqSgO_#5bI*-vS-:c|ھ|yw {?,ro]5^o&K·t(=!#5W?䑆b[v*FH܏r3E[jjOZƱכO0"I Dz7J5?+-TOJ#:$!?mcUyR=#*goyizAF"oR˦ǧ_o]㤨žw+/0*U^x-ªW\.E#:ۚw *a(K?6P/`բUIJYDաe-0sY*QA]=*׋*'-9}-eι[YD3;a#:9]4Ndb7 뾫|g7> ry1?S82xvBcgdGmZk<y|g\vs[<볪Wx#?ug5L#5/;tQ|<ވ1Yn߯!߾<|qľKf켹A|MB4;*~>q>yywnj=N!&<*Un-UUXMvWN4Ӕ^{s#Aj2R֘jayDS>,-:x$B\ r!u0I;0Yg;:.W[_nV#5 wLxh"9G6*h_hhu*T}6{1a]*W9^xkzeM=o'8HmƘɩk(nSagr,4#:Rԑ٪Mz p}&MJE6 (^2X0IuOݷ<۩GsTS_k=#: n*H\)L$tczcDT*OxCL69%O]jc9oV1:=B5q- Ch^#:k,">noM`D}^cV"tB^1<%ul[_㬠O2Ol.|Ag"B1yGI&#:=[ޢXX]SDp@|}56)RFuIɔSN0֠o$\ {f;QݠǦaUp~[J8v7)V5 }c`}g_~ޙjw-txUQ?V:1[ڭLPmtaUBA`vUVN.Ztͷ7c®etrµL#5Q#54B+8+Ǘ|_%>ݞ/;Nж~VsY`Fyss< ^j5a#5iij ]/<#:O`z;$2*k7FY#ntkիVt(t\RB=;[F%~=7GI*yRx=I#[< :]9 RPiix#H SpVo⹤\XJ^\hǔ'4(ŽPmֺ)hK.#D[?O_3Ӈga 2KorBGN/Uwsk$cyc莊z}l`KQɼD?6oC~69R[`'r}(|;txI]M!hGYM,ՅMJڳD&2"n'+ɀu#]TLϪvK#5ؙ2eS)krD:W䎮Nw%#5'8ic KƝ4B(}>^[@)Ut^(ǣM%֙CWo571A h3*AG*wjO~W`58Y_zsmSןq>VqwnhI7oK[e]ح&޵[M6{|nWpVW޻w=Sߎ6{p9ˣYyLNCUu* {(q"3E {NFρ|N#s\i9O){E=?Xrd7%(gdD³7fBOw#:7[M(Y$EV羔4jd$;Ao)~#JAj.Fh>jȜL*ϢtBzslFtc]1z{jwl!N#ԡ/*",w{[qj-FuwH"ȗLy.zR*#5)E@vc,U\}[ShSlxY^\hR =s՝e\}]XX8ȡh1{Հ8{[YUiaX:0R E"_.ރBVP+nRKe.*sFfr1#50x]DrSvZмs̉ݙѳ2٧wB4Gߩ/޿G|~Ѷ;=>#cw?9^G1dg=rt#:Oy)tAWS6)??3S^)(% 4Q'IEEC8NV|a/VoI% ,:#5*pE1Bs~tt/%Ԛ@~}~l4Z%^i"&M!(.f#5[vgZ!odM_wǫtR#ako>[;`m9!})6Eܶ$kk&f#5AC٠y: Z@K(Z4rBhүUhC,i*L"S8 Քqw"˶5ڭZ?],6pi$C@(7^ Ux7˜۟{t6U#5P}tVpPHY#5yBP-xc3Y+`@Z}CݛKykzE2I=*#5;ZOUqxspth6؞v-MrJ"O7riS" \BmMT@+gA?WjCi &Γ|pl{DԠV4sm+sp@%a.C}'$(s\lo~gD;@↷=eA87J~$Q3DFۃJ'f&NAtd#5aڄDՁd'B!#5PN|,ֱ|H tCǪ3f(VnSDE ʂѕ9nE ( Ӥ#:W-QyP2[籆9pd}śu&n|*M#:WzO=%"kvo7] h2QQ_%X=h]{늌ݗ{ӷ9]f#/#5$PS}L.pX#_Y(=ʶpgyp H;#:+tV=j Ȃ6pA#:]D6!X:zDJgwG_bK!br߮B#:bzApwLo!7  )cPwpzC@jA}nI:>5?P}Pzh0!a$Y9&.ǎ9n/$uECNM$ PkM EBy[jI0mp\-4qĠjY-l|NM#HȰ5QC:6ȼ|.efCKVF4yi#51>=+aXXIQnrQzZKۻkȒ zlMlPՆ д0bK< ӌx\nךs 0< f^((M ~eWzH)߉6 b۳Ə;m iݍSr49j(VFDT,&m(ɱY-Ѻ4Zj;чqsӮqӶ JvkEmم$.繠d\;='0K#5D/4K>ܞtΫ#[{FB}Ud/XD#5РS9#:㑯.quk^gVt4ߺܱ2i>#51Ȅcc13&bHԙɝ<8ͦ7LP+ V K4I"Nԙp,FV ]`/:{( KȎaui2r`OSdM|PxD?妼Su"V1߂9 *..4Ĥ SwO|YCnrf @YVx3+Birk&$2#h*Ӝt^v;TZB)À##:)+Ade8l0h@9v%$S$I9XuIZq)3u;1L.>~,ԂS&zwزci*m5F5ΒMti!wSm ';u8X5q7P;٩Ɨ=?Chh]bJuӾoqًE3}[7M*]B6 Fh#5@Z.6qT8Y3tjuJO#5m){57MZQ@RSiϿ_R-MIA6Lm)u_kڲWcP`Ek(xQ_>J_=~L!<9k15@[LwFMw_s;u^^X\b Ӹ;jO#:~ZjqTd$ߧ$Ʉ&~]\2J\#53s_,N3Xfxq &ZS>^QXCDC a80`3|q )ogDݪi@Pt81d Z;PAg`rUį^獒Gլ8]>]XIT3csc7!?HaD3Cq\{Y`b pgqF{;Uf:W$$)vα}]7y,-==tļ:)DI'>I "j8/`sIIkQ}ɣ<ߕtTՠH6&Ggj`DP},8b3I&ѪukR!9 Esob Ɏ W 3A~?,a6ͽŎd&y>#:ɢ@0OZ!a<s r qIe$Pkk)Xn-R혫dd#5=RRQ ,tDAe4, -@nMl6 Aд.s0g٠ lH)t&SuS@C$@ J u>}0ev1xQFjhV*?v? []7_ޒ~=Q' &a͂=?){Y#/p2,8 GMh')jMSŧxTTxm V&фzcq]gQ#sXވ`jmWJ5NPb"ŔDu ږpCW/E8HfQƞϧ}Dt- *nyV#:%pBDwIJD]]#:ʘi.^.w Mk,)?nS<aMs8)ط7~yagf,  >~EuR-0)oHH68P)@4!aQY6٥4yᄏֈTTbUBͼs#5h)jRYȠ)0$jRH)HCe}#5qwǦq89NsC37ś`.%B)ᭈwi/V4χ=hG 詆BI <3cAFPr9PT5(=#5Z-Vhem,qUEti=F8 537|4MOJP#:C3vQw_esx 6$D|@t|H㍺(qݺˎ3$¨QFQ),Be%,GMq8FFJL#岒R4UݕvQZ(BZU|s|y-u؍=ɞ%/$B(%w+ثvQt;dYϡߓW so̐1o_kK;L{q7MD3'fr@πjȤD@Ҵ#5Hl_]=bMuR,P# s1FDhr_,\wwаx# XM~xjtLb#5 #5}@N"U4"NO{N]_.z 5 N(,Ύ1$usjl*S¸p#:iP.dc5)b--,hFݿr4wT琝ͤBO)4 +tj+]-#/v({,zD #5_g`4Dy؛vu[YqδM;Kw:LtS#A27J6 A5rF+:YWlƽ\˝RN}.! ۦpD8ؖK#:) /j]g[U1f*ϡߒmw=-c':9.UàUu9xp<#/U7U4w6V#/^}7Lq}ȲF%s!M8zM\"δƺg#(ĪnWo)cCI2C#5MxOOQ)$Dc$ ~Ǣ)<jcza?4?NH8:T?1KEqtڮ[5ԩzp) "U.>enoaJ44 r9`&&)Dnçy<;NyLz_&A$z%%?(Rc0E 1+9|>_ͥS^y0j9\^@s_蒪]$ g@Cxn =ս~O"}xx\ E>LK#']=}l샺[sӯs$|7Ӡ]outԟpY"yعl۲i{cqۮ1:#:nOʗк_ M>cԥfb xwˡmbPCdbL#:tSw†IF|EhɌǃd2NdJQ*Y7[x֕QT{㖂Q/v~:Q;9|" )h(ttD:QV_ #5{6-%}ZA:j30[_1=3N1SY~}aF6P2B 9P=֖WT9MbrUqJ'_w_Z:9:~ZVKڊ0.tҧ1&~Av0Ð>mVlG fn7U&f9W-uz1s6$F]?qvѮ;p1YV#5&mh wߔ/BeoBiL?cJ ~47$(dªyEjybQ]p'6 c#5җ~ʽX!I w껁}U/;SH:הa:G$do)Ҭ /M,L{q9x*vN`[._69Ciz#]7- lJ+ȪG '1k |+ O/2̝HGuyÿGዼYF2)qDŧ' M}ДzhD$W+OcJʩ+<29yj+@Ο)7Dc);#:1+Kc:HrްyIaygE9?oXlAiV\~:Vh)s.Gg)?di>mGK<5`O/D(-_ZA~#5;?7[}+4h_c:i^#:Di롣TgZx|3?V'֒0j/#:D4aN$WO6]4z 6vQ=oztST"pm3VDڻ4B#:OlDi]WXdvyCoMIGڻ̈́xQ`yöw`WfpksL'84G0@,޻IĝViL5}6FDEɷjEI>b7QӎI<}j 0I&8 #/΋eulcLg9xф4'CLDt2W9o*5V~#:i+hfVr9d855.%芌|j+߿KM0R{B6al?;?}&ݜQ[#:yGn>,Beb6̰18)L4{>%sD#:]d5({ܺY#:,")@,fE#:kI (s*7 X (``WҔg'q4yusVxxgrLWoę7*|H†ҔT"@/óowѺu\\fN\@TlT,EbK\kfdhDWA+Gr#5v9G:5ao~$\x;Cgtit$v+!6>>W_ 7WP*gc-b"qa⎕Uâ>*Jγ}a.N6&rGD#x}2sAwi6묍V2U ;ҨY$S+.HL!R2j0+m t "D'*T=ݐoi Iy|l/g*4:#:,}U9vUK4$%q ޹ޛbgMCƔAY]BrOVSJ>wst]liS fS|PL ?"I Ex["Sazuy!6irF?rWQ[_?b~6JS^8D9w&rĮ-HՍ+%^ڔxG}guWIҥRt&w)/im|SWD(L#+ Jl1hg-2iA{~FyL#:q&tgF:精c&CV<*gٯ^Ԡs˫R>L&*k|S؞ Y02UޱOGs;'%2T꾪+U0+4Qa*U/8Md|مR+xj6j7: [8BcuRk_izOC߅D@hE#:li$`fU :k>s]5M:s=ŵ >%%$uo9 ͊YN̲L5`d.#:S#/[r>ڀvwq5@sWF*z+џ_VEw"t[D$~c(9B@۟Ni8?[5)7mpD(h^}~<1vg%'@0|9܊3F_?2m! h󋱐]jM1]穸f5~Q%ٷ#:$E/MžMR_I2[*,3)1#: -&0پOW,JhRLh<}XUcY $w\LO^eVR"ĭ($aɀўIhmeCH.jvn{7/T;g~MPM5VxΫ486qlPC:+?@oX?G)iֻ6![roE3vu]#:m ĮyeM9=Sm]S!i}rg0 t]2wɵΤ!%0PbC9Aaas*d}a)Yڧˍ03dRc1W#5nT?]Z ::.%LdN0 aj0۩nL,Š9HŲ٪t8-!,eeڔ,՘'1]Kt#:-uB4TF?arC]˭Ӟa{֒#BBd%Pl ii#/+=Ñ|ywե1#6'#5qS/#:ZT i۔yg3P:i&CW،ov9+:ƗM映]~RB)rZ:[Jȸ*O.`ı{17 DAomzt7W[*u]Ц t;;2+^1&I-8Zq8Md#mڻ'eעonެ-`ox!{!ӪX݄#xd6m$49)%RFXQn9[3jy;7$>ЮZp¬Ko2hgf oc\^nv.ɇr 0z<WT#:I=ƺ1~eUgYt(%n\fSI~.AOcLMoTd )\G:KvǖDYr,v:&8i5RPwC3g$1Ajrޫ\{XV@w2,©g6|^[U1~D]7cql&h#Z@?[?pGKYq0vaI瞛>}D+];7MPlW082O3NfocP}cJ}nUڵA%6\X"ݯ#/Js;9Cҍ8+O?g~%~\WR[w5E#:iV71?bx]Me=˿~qjo׏8D~]3ʐw`X a)Rno%?GoVQ* #5}i aEUWˉeUM#/;)bQdn$R"eAlu{>.԰& bNC&7.a}^#/Q#5kp"#f^V4v@DӴ`UxBc#:(Хq[-BG_@Q;GORk/O;|=NOԾtQ#5a#5"逅<>DS@:0{=nj[}z>#5A!Ϲ0j`p S0ѥŏ&vOD[^Zz YBl'u˜#>bOtWFހ4k%$΁L:(Eh^d?Bߙ46Zy@<O6SA`#O"}1&9H$LfRQZzk4#/\P(cD&Ms_akZpD3RfOu&EOİ  NRMHu$d$[c˫)sA#:cR쩄>E>5#/ k ưcChL]{&Z~ p;C6$ M(@APajWc. qR#/ {clČ:nd $tDV-u$FGd}P>#/!#/|oW#5i."yLl'/qIxCu4C+!sH@g?7H7#/E!RK뼡4/+5AeV6 xt׀M ,Iבg(tdxdէ8gxc}F㳌 huq$7sxcQ'aٙL;;əWR>chő >pb*e?m=: (8~;ek=,Y(9X}ITHɠv r%E#/n:( T#/2ƞeUB4B3XGQg#52\c0G\q5).iOޒN6tEg7n$ww59MQ_q'Hozùזk0Ctn4óY#5dXqFO#5;K|Ob#5O䄵LNxR}}o#:]}iي@>#50[Wwt$Nҵy#/v#:ԑbdHW[HzT DM}vܝMdK b#:9JӥaQDmM;5NC~W䨰*JUEX"<CID3Dи?_J ~N^$B%RBS =o8'S`dsC8CdYu^APjѐ~)Op޲XOQU:Υ,߂AT #/8РU0N%ffjG3;gMG'ʠ1m};B!<C|^Ga,]L1aH 7^g&D ʔ;kdYb1§Ťj" nXG_Û溫]8sm$%5Ro^#yk"ueU1eX(K[kn 9!3\i#5zDrnS1>qr6Aor}E~,#5PoߦiH BS(97kx2k Ԓ*}.5jpyArd_`{@YXIR9p9}ZYd+ >CEN4#5.K6/#:ۖcXRd|#},>A7.7Jc#/y QLz"z"#(r],C `o#7Z4AX#/~}, C Cװ 'sB y8É~@E~yݭLǂvB<耆h@{UϮ y c:o1/*]Ynl]3 ;=rO#:#Pu}-*ځ5>V71/`0F=ȝA<#/$hopp#/,4j|x0b3q9lz#:`{Gh$!z~WW1EZ"%mV{ַ#/{̊7yC:_GajGuFsy?Dt=P> ! 2nfh:p|0mWyx{Jz08J_bn$5-SCNJ6F, >SC;񸳁=})K-[rNNdމ>+1#57ӚHo:0CI4yZGp[ЙP-t:aٜ#:usZNFxcʉqdPB:P>lQ,gz$Bw.ڎHJWHf7Dč#52bD㳩BC+>!g1bFhKJ/dvf}AL@p 'L".$BB $C"|5[TUYVQuǰ;}G@py@Yfi.f #/a;va3ŀ"4UJ,y{~mMW tV7'K{P"lR?궴UeƆr 1ӍfD;ȑzI8R8M Nږ={"dhT۽ _k@\Gn<@'#:>Ȇ'$tEDRT0#:|q>.}EɈe)}a=79FC Hxw;o]{I3` D5C?/Xz?#?N} 00{]"gC M1|.[v=]Fe31Ǽ뵝ڹkZјECI!FЛM`ti$l2BCZ]ku%p̻O h1#/_w$5MvS'}iWc{ZQY5]m|r׶f2#:B:#5%R#/nh$vAeR.3Dј{#50Rmg䢀[chL`hlUflLh&Y3C-dd0iH`3iA|pCtQ9\t[}=. Tu/2ldo`}_R4?$iB 9#51";2I(m_ >#5NP O0b!p@/E? Q@}ȜFuՒjJ L,!}Ɲ!`)ʄ)O#:٧ڏ*%Hy6>;YQ1d$XHQdǗm=#:?#,O~ ?:) ui1C"'~=D|Zn) p 0`DDDME(G(l6M`3tݫz+͞]-bDnR&&L+7"ӗJ`}]=oxw>ts3VJX,"b88:Ǩ}##:tvԯHuMd"nʘٰG^,#:W(*c\|)}U)_ 3@͢ݾS&UTj|-V{íB0#:cF#vm1htwD#-"Uh B]"i\&]Shs'u?(daݵXrm߱t<|4ËӇLi9_pw,u/aIwn\nN塤D) HppmX@'3VO_BƧJ!ma#AxAt^ܭ3G]B9~r(gz[+ G *+!=`yCd7+8EϛkL,ϙ''[碵{P{طA0c#/t``m盄C=`X-~?3]$1i#"L#:OYcftL"r7])9;xn[_u*oil1~>_ס~)Z4Izc^ #%lu`dAPHTR, nFO0s1S|J/OZ>!EK!By#/P~= 8Gw(~ML$>|x՛b!a6*F?uJ#;1vX(f#:ʫ3e!EZ\{bsz [ucU$d*Ƙ7R`X)s@:6;#/"v|q!#5&7g8HJT(5ԍĴ-/@?&owj P?r`RA?'!<;wϲd'6yGi'^%߶u˜t̶O)畺Њײ$E! iw4"d%$ F +M3K+Vh &R`Z,-XaX(D#/?mO|ᱛq[ONanAAP:Bkŋ.U&1r&0$݋*7A.L("#5(*̹72XN08@ށy|'Ma}Ąhv>XHDS9{L r(t #: JBT#eMI.ԡ5r=J5?x%TRZEC^ɨR;jNfC#/!#DBa0w1nRPg1 ΕJuY9&ExVQ82i >l?m/LP2BECP #:O UBHmXKUzl/nE4+yB i܂x͛={i #:1ũ3LR3ʩ#: 74N JIaD5_"Rpw)!Ur&&^#5N.#9jt͠ 2AHU$H ԅYHn9,)Gq(1PB=~#:(ALpt8^㿚OD+ND {$} M>82c(zo#: E*oڥ %պ-);Fn 89Yn^K6.2']0Y2D#!+YGԵ@XQA)3E'fb膟6O%FhIOҸ#:'J'.JI"fS")rn"+!#5ɇk!.~ͪ!b}،+ډ|pEu LSH"z A8Ӌt vw=圧#/N&U]LJyƣ3|b$*ےSY#/̫^-_D?F$#:q*?bhKM<__fTeqUzWu7cK$f(Dpĺ#cSeB.eE_[aʉT4#,sb4BU R)G559j#5#:VZ h.|-U#5*;ʚѝeE񋌄+\o0ɭ;- F%r)SFZh*Y^c xC:Zd0%K}(44#5q|1SSNyySR4 A5J-T#5DJg퉿wI xTBgS *:Cco`"6_6:"+pȒrg3vH4"R@j`G,RUO0Xa !W+PXR M6ߔo/,6iO#wz\N#Wj#NæQ#5aWnGȇ(#/.NW(Z7RBuPTscr=#:gvX#:'f*a !Hw;Nly s;x"X??}>D#/5eHwÖw[ѓ!_O"Qij:4&sC*.GmV&9L.؉M: 4Wx쏭^:)YEnp94Ղ*f~b/iyma`&i%ezRROv#/RLiiA@ɔ<5?sww|?}t`rc!l#:[Y UZ{aHY#/YhC`G]G#?b;v4S"c#5N]0`nh`nY^A}$<,=р2H#nܝl5bۭ `c﹜M0{j.0#/BdE"%hcGqhaUQUQF䍸U<6s 3JFlZ'h;}>`!#/HL=l< M j&#/@ qQxW**n +vs5{c5ٕbLgGM܅L@@a0ET0P2LvQL㭄EL8>.dQr3k\/zxRkp0Sk@+Vžv70ޅc'd)sf]mo)٢4N=O#5#:̏v.g`t̺$U&i\4CV1q1ZIfk ,4u/e[w-ĚWW/=[e,kŬU!%* IGҾ8AZPț!,_8$I?U0 m4BVTM@Z\#skaLm"`âH=I{qHY԰J,'nÉm')ZI,(Q܉_͌ 1U^L&ÀHeIsJ+#: 1EşY0P Ƿʪ#}K/`<l qM\DEgbf!'w-PJ3њͩ0SuOm&Is:?0.C#/lrz_%H[K.cʫU4*CtAן|/kݔ;8SgN+ΌɍݷՏ qxָR'Λ.&^4ڂ'kIhpݱ8ssⶳo75Sc.phb%:0b42fg4-!9f#:4Ti&#:,ǯnr̪%\YpR7#5#5 7xWSMNZw^.KlߒF KsZu6XE-vˉ #5$s<ò#/inܦ#NWW##:1hJs,~Bv3L>eCA!HB)B,AXwbrme -!67&6w)A -sbB M#5ٽ@_u51;DB[msSzMц2/2ysѬY͠#:IlLiT e|jT۔K'\z#|`x#:H;3Cyisi9NKѹH=˃#c 7-"ea2$tP:zÆs43Y[cfd˙cs3+s12KCm؃˜"XA`}RpΈ#5a6ǬՕwhC&w\c?^5\t뤱1Cnd%Qvvּym`?.+VL3#5֦V <1R #:g#;+=ڎPTOoqAS3ę1'=iHI>9y%@.'2c[EYce߼*`F/ #/pv"iuWWE2=OtՎD=!48(Ҋ"8؏@$k#5-p)3g@qTTy 6#:2!*gh`>_%LN7.Yjldzq BF P&|VZN6Ż!rޭG &6#5,f{{"С8B.#/ԚùWsfÊ)4cNygקs=1hp~09#5o 8+=c:0LrF ,D643Fd҂ Jj&qqyMx+#5S72)0E Ξմ#533bV;JžPԷN L,)I83.*1Y'43=Mtװs69#/{< QFcs2ƋA+rpo#:7ܱQsN=w٠#ƐtjJ|8K%VUUVqˀe#:tgʁtj(IsŶN(c9m1#:-;,i(k}+ H$fhJBzK"`LhY#5bBG]c8jvIB {7C8&&9Fvs4tӷNB[&+#:t(ѝȎE5Ye5c+מ؆uB-[ҊEœ&Mkqq$RctbDd0b1!HtOH#: }~6 UB#:0>՟w3Al9?S;*z~ꨈ+2BH@RBY$7~܀{NJtB (16YI^bt#/EpFjy2~k5[D*yH[\a qRROPGg'IQJBw ΁}@^^։\ >b#dAz2 VDD1R ('RႰ B bQ@2S2ZTP פy[1Z%Q }f4#0LWM ET9$K*P*#/_Y3i:2Qm,läYb2x[0Jn\8#/C\^'@A#/"P$A o8dU_(0*1D(0M>C_;`LDTFZ(@B՚Wue#5a)a@,#/\hvS!W,J@`E-AD`[]z\^\tu 5uw\csIY\ur{Ih9D_6Jf\4]3cIm}\Ѷl|pCX2A #/0WuOmm݋s8KHa'+שa=\'M|X7B#jy﷗x$>n(R@>""H_w~#5L Ial(("b}D(EBu=;Vw]]3v4#:T) -?,x'{mZb 4DU'gsȦ$ )Q@3| H߿%OI-HSׄPZmPȹKJ.Cx bcEcwͶ t6,hgb`٬MJ0`&̌Ő#5(^eӕ8kI$x:=~>rsP|n~Xye\T헝mWahV7? #:!?*"(:CKHxܡmFQ#/#:̽˦P)_QD3!ڐXM 2!f/ #55@cXj#5TT6-8ɭm` U"mƙs#:Rz 7LذL@;ijey%n2TQNJIa EK~#:]3A툻:k5_AE~=R#5( `ӻ1v\F"#5oNE"2)E$`B#/H0 H~M3 +\jCv&%/Q!!#/E@eᾛ\fS#/u1R4R:A3d@ӤRd;vzjG§Sv~LV!D嗽MQ], g}N /ylJ;9! l'9 緞I+.Gwѣ=1 ۭ'` U7-f0[WBk#:r7%S#5aMߋ=y㩗ػy6çj'NDAM};~:ѫCd:pG[3{ljz-ye38yt, 5;z_1XcMmҞ|\3ݺf=;Z1!LF!4M5>/F5Ye\DzΣ#:Xem#:+MRVեZz+A<736Ԙrfr:#/JQR#Q!.ZhÆ ,UMɉ(ro<0,#/`37gMH0L=i{p1&ewF k#:{-CU=u}ˋīwe5#:'4-#5PđnV#54 lID7*tNv~8w#YR.-ۖvKˋ;,M*VR `agk*‰#:ϨNg:mJnI #:"$]=1#/M1KB1zǕ-3UgOU{v=9^+D!6ySJ04vAk+$ QvD;+B2{#:-K\ڊ s:K"0@k6pk;R1b #phWQ ~$_+3>|SpCEj~`W$NtVFC*Q3Ʀ*\3ec<3pJ]eDq#5#:7STʄsh9M  @a9Ipowo(>g.c>Rh1;q d% >7UU'=;Y qF#/.VE+iw#5^(3ICau4 NtA&N@FA4} tEZRDJ(hib $^RTj#5YY D(!R(< fP+ d`Cbgfl #/~*j;#:ͅՊ[b 'fE#/hi5eum~VF*Ny/x,/.=F"d ]==`ce>PkȅEپX.L=r"q#Z+x5CS taƮ:GdNG#5C?48HP$.[ 2܏\/7<݂=J< 5 Utl~ixPI!AivIYNJ~^Qp^*YEPiid\LDuXý[bF10=unw~1BuaaLC!]E`⦹ ."R fŀ,Om&([#5"6Hqt50H ,rOlGQ?.sA{N}4V%A#/{:ʖ L#D#:b/( ׭qHt5c Pޟ,1#:{6SuRogO^-2̒D#5Dm24KTwi|OA&AMX~i͇tvKJB8ȣl5R0lV3N' bh0eV"Pi鞍tgvkXR >B5WP# 25ZaX" `X4YLcWn;޾t-6HܗNKQXp(1T^uA6ƗJ$I8WpHb;U[;73!zpO #:xF3NqYz 3!^R; :mz_96ݳ$Kcڣ8֒+L)$y=Թ0_hYVg5'c؂wd0 =%̄XDX2KxD֋[5mUk Z*6 "#5HPϯie 1`ҿ#VIen`>nYBH#5*wM;mRa LmHfI,&ش)2V"IMX$kۦ&Jh٥)VY&Q3"#/14|Ab#:DI),6$h%%)LQ1ՐeM#%Q#5 )A@Efr;5ۥNP)~B`#Ʒk_ƛ)dhCqgYE#P%ré!LVߖ`H-2h+.$Ms9oQ?<}/%8<(~ØO˅դphҟ-zYt z0$}g=?ʉShl1#:n#/\2#e+tc"E`)iZKfJLLH1QF(Fhd쭼en_Nᕭsd[fhyI,~_M{ޥv#//W$F0'?<(a<׸'x,GGqD!bY%'UQau`3.ˏcc\vGa{t6#:!;UM!B$!LX031`f/#/dEBYQ.;|=Fg~Pxq$8Z*V+φci~-Ci'hp-vG R jOϑ/oCfLX^f0qCǷxYtS#:Jy\icZRإuiS<%X*%OL)D)tLKix B܍a0fZaeDW‡K|\.jpG'2KSO{SCOkߙ1.Ltw;.#53#/K)L393NN{&ppe$y#ynH|hbuS5o% >7{PbTU#:ڍBA!&#5gS'lb:|]^oݖ982ULɾ!cX _;lT;.q#/X(c1ENb#5#:SHvhLQǼb Ps[qÃur"u;'q21I6$D#7= c-n ?#.KD, Ȅ#,<#5#:`Z"ZQ]"%BD lB!#5GNx q6Xp`ڏm6#51;ydyiiku$#4 s )hEKޖXrxF eu=(lb0U2Ih!9Bh1iЄ5Gb JG3o,x(w"y*&)R4IFAF٭f{Wma@QCtHWA D#5"JC+"[T vZ_/>]K4̢e0ϥNG:#pWȸi;Ce@UD剫wR͎Sn8sttUb`\0(q9×9סusB*ݯji[js89r"H$cGPVӬvf[_%II@"c5#//D/2!itkF`t=)Qj16L ޿ 7X#/T]H4h6,KLƒ-ꅐ%H Jp;QO xwl#5GF9s9@RAzK܋"?: XЄ5.o7EO_Wzj=yXB&}.\2 ,Jd+fDcbo!,xAn Ezml](F@`]ZiH9[R==?pgF2,#5bf|b%yc-OIA]6(RmX%E-35RcZ-H^#N

    A-ԧwWzKĺ=Oypv$#_phl"#:J ƪ#5@le'' @E8EO0 iAO/*HF^}loݐnag]T]Ed]tJ4&Ib b9hm#/MT cbL)gWIPh=A`0M1r(R&#ILgK1jM覆$5l {q y: qlYTήyd¤Rq,- JR7*dȖW=bnB#5d!1N6(Qd#/C(hp K^;2]! Bj l].z}B#5"OE2^(!" .!#_N.`rRv$xI#5K#/0Fv!)F#n49ƕjb9mk2cؘtg,hѥk(/#3t Xx93 VPIݮce3!CưӪ F]N`~4 q5H~}gh|9AprL!#0i`l32spmPn4碅h7;j5EQgrl @2ذ4)!E lb]ta:#:#/ Ld@)t]#: 3 #/BDVܡs0ӝ>@\P/!B'F!e봋|[_%ԋP0zəBXT\>p?8XM;6IG#/$\2 5=1j=  uܻm5!GC`&n%N!jyc:ubs7%-vCvZ8lUXʃ'YN " dڧJIaj#:MvaU0<Bn0k&*c*rT2 "3Jj|`9M:Bz9LSNyG#:7N4×cR#FrF,g[h5#NL1#|az(RpZr-`L{#5X>bƛ{bֵ̕+zk&gI#:& ʑ60%mSc+$Ȇ.)s ]3lHq̵ȸnS~/oEa !9< JDj LC$ф~ŭh#$s4,y&m:mu#:X|ky)!1gA{qg2P"!lDJ ssta1qyFjPWZIX7+Bם҉ח5!kv $f2R$p#:)lԅkkkSfI6kbRfZ$ 0ň?Gw0PH@hԆi*5 #/aI=ڑhM{*Z`2%HQ9a(|#5N0^ =d;޽–Cvo$@\Eq_!I 1ɜ²hlRlQTWcii65#5m& |渠v0~m(C1@F#G^'O!t4㭋}ٺ,omm PšeTHJKs\-i0Bv!!a0Tb]O5mӘbSXTtD:Ɔ':ڳmQ5J.Kܴ]J!#C/#5M֣ ଆG/nE] ,B"Ru^J~GEdMܼUDHD+N/8Gʽ. <4o?f~lI .jWӲ3Xڌi,%I뮪 $o(Zkkbim%R1)K)Mc(JeeRLQEm,^wTewtՑ>zkzv(Dc)*ĪYj5SIoZWLMZƤ5 MF&yזi2K(ɲVvֳmNjtZTήwFl+vTuI*Mqʵ)o+R3+IX],sxnݏFi;(,a޳0ra؋Є@AD2b Xf*CA:8bb" cn/3m1ݽYV }vvBF0)eTMuC |ҥ ҵ#5~sCQ(n#:wMN]~Hv##5P *=$7?=*1(QAdYER?c^"Ey:Z<-'JLCT}e"! 16,2%P&Xؠ"մl*UR4,4j&W+Dh5#tȗ#XpId#9iq#5`FY !mh #0\L\4֞JۿTLF#5u#/5:_jl{d#/~2R"sօ!^#/#C*ԋD1+beMTLYps"pE `:wԃwe$X1#5bd"r2wೠ6c eI #hĩm3C~+h>@-2`Nv(4{/5|-I!?t҆q,B o+oQ:.h\,DɭuCKFV.d"uv{[c nԥ'͎vEC\r:nJA}dDdNW `cZRNRJA7+8^+dj5!&KƬ[EוQo(v"=#:2idJf/y3rJ^\fSCh7dU X09ιG@QudinKިyuERR,7D+^u\)hvGn9`v)L#:} anbg@i1$pl`C&$ԳUKS&igSPEzןc IAɚGkpdHxp&bK+1NBFJiKm/wQ,!ymZ{ZXNwV6t(I #ϔƩۆgn!7TDtreʾ1KMZ#5yl<^$XU=xԞ3D0iGQ;: 2Pisr ȅ#:Ʀe=(9jUD}z#/!ɱoVyG X7/#yBH$(D9F'Ŋ"c8ػ=9* Hg4Uc0sI_D@+ݎJ`S82UHv]2/vF"GW&StTnʘR44(ܩJN4QJ ^0yO/V#:xl{׽N '>}}կۛ9!#/̶tfb*oi!zY7_x?oؘ46tQO@.I~#+쀕֓@ޢg&HCw`7P[(&s `1Y9`#:).ã;[%%A/%0j"sP!(WmiLH B4*b#:݄*)LBˮ@iF(j#:H](AMH=מۓf>nH7GS[xv<9GRMq;"i-#^ )M3|<{oJW5UbC,9\j&/hE[#,T,l,)닛D5#$̡F#:1%$CB1pI8eaFS|&zX#:2 Ӊ鶺E}d`@P\::uS|q 8!CiHC> }3@6!팰P6J*(8@(#:#5|hA#5v~ q$cxefCH n7~kߣcSMw]QI$BPҨo98Q10#Vw _Tg!{@U,:bvD-o~q Ѩ AI:X=~;&G&]Lm #:!=#kTPU#:?%hwUmoK#5!$@'@#/Lͤ7ס<}Vs/Dt?_U|^0f\ſzնmQ`!dDo]~A&w)#5mC#/(**# 67^Ɩb1#5j~L35!&:@oC>p?H<'==-#/ 0 mY\5V5UI]0F#:C1xb *" \VSn]`Ta qicHr'Tt0m41@mlo 9A Ot=OW'C{nhVCy)J vBw }XKaH!;>|~6YXejZ+{ C-ytx)꧜髏F#ӵ@!!1ўipS+R_Q}4Q1bUI仆Y)3R#:TB(TҿgQ K0#/݁JQ8¿T6&V)LR #:fV]fPr%. .R$Ɋ,H"b]22EYUr_^-c#544KTA #:VAe$Fd 9 eUYzԒ "5b c.I.+@M*oT;r#:4x#cAmnZL^ѓԺPk p#7 ٜ-3:īF%oui0k#/aʎUb6moR#:l V69) ) &]pz#/#5bxƒ2"Y~cu'UyуE8&,~G~+563-Pj3&؍㭵wk˳]ݖf%#5Lzo[χGFnjG*jA=ѽ>%AcX_H?"$F0AbMo6:6X±[u**m7(! hQ6B YT$#/\8E Nz\@`ru{4\zSטA (H?`" VP&/U[s{}8b W5)X*))ҥL0B2GcZ͙cbFybd1CLY4 $UTI#:I6*elKm v#/4ZPw0u5Z0F1#/PF1 i#/@1pAK0@EvKd{j*#55܉pk!%?lL``Dz@#57z7,8JCܾ"fq5<{fΉ>"a3ɨN#:()4zj;}þҫ|q v]ffA.\ةQ#/}18:qur!`@~h\+$-]Rb&֍YKZiYO%t,[!Eә!A$^r=~{w&}g0$,CX#S ¯]59voNv,lk̡.7MYNFևTf@Nܕqm~v``e4a ęw!Q~6ysYp0Hex|{`U'~ٚ8zX:=tL뤨Gʺq&DǢi=ZaddG1/ލ^}`IsMt屐'6CϚk@Tb-# BԚI#jijM֌fkMؓBTT6ٙTْ*(SRjcY[6kIb6l5hikHk-6k[A3SZ0i9nƉER*TQ)@Qjݛm\k-DX&='W #/0#:H#FP?3 ˔쯯}dd\ @!oKL#:)\#I┇Tl^z#5_cgpq$;BHlє:ѵUS#: nPߎw gݍ1#6BCe<9țj@L(aC>,(S0RP9HzJX,F$il]Y-%eq|o{1JFzSݶl|50~*=rJ3Am\glGb[4p.EX$(iRDxE(ƴ@}=Hv}w)ÕHJP4+[JHnYUIZdlʶ4n2Ƞ0T͌8|hi!u*L8pFYL>5Kd֋׃TQ.}=+{1iH>?NAq8i@*Եr\F߫+ACTV3 -#5Hbw0Y!܇L#:Oz.vM27_8'8)+%}٠y]"=Wld#5K:zXغHN#5W_Jk56 ]53F-K%29TO2bdēʱfd)D~[XVK/e"y$>:B|v#Cؘqp)#?#V-Y5+o`PgSP5S睙V;JD~6`{M1։l2%Cs ;9=JI7#::^%Юq ٲ1i#5p`?#0ܲ(E#5Tu؆Wh%0a3#5\U6'Da:R=$~r +[_4{Gqm[PC4X|B;QC!0|#5T>Yg:Y*9 O%YH:4q>fİrisY/DA2M#5-b'O"0'ƟwӁ찭42];Ctsӗq @=QE!azuU:NPjkֱj-lmTZZVjk2xӸcr#5(с\x Gѹ83DnY=E#5+j#/]#:$:P"7Vj=.YmUpA> *h}CǗa y$2٦4?|I#:f#/t΀qSP^!A1 I;2p0PdMWP@PAݕ3bוQ{7AzDE!%DmQ_Z%@5P܂RGХY)#:[mY?wU6" Ѷ[JX)e g/G\T6I4̸l48IF\4 ".T ##[6=C*ha"n1]0I@>4[%⛂\X61i¢4=Jjd.@X+/m6+,;W[a3XEE[IeXq9H0H"P%!2RP3d%ha*!hM5,YU B#i.#:h#54#/epLhc33~칱0LA Rea- ^Yj&J n4ϵ׷C&*ž"<d?df3emZ*`l FH"I=Jm*U0*%8v*|Zaޮ2J=1)rHOnG~׉|&5fK*f)_Ndѵ[3 f(D'ptW|,&h" "7rKcHsFpÝ'nVz?I{is{ wV#5#5!)`ϼgUNkٜz<#:I,K0^PM)0\U`SP!d*'BTM#BX3§CkUdō#/#5 z^Xwdj<Xz#/?S0>#:sP@o6U-mFgeѰ`2#/"|#:ta&8\iQ)'omxdemֹIj6K]WvfnQ]\4VE 6k*wUK`!PJ"BTRDVYE p(%xǷ:ZnLjR|p\!NRVR՗KZ.݈, R*W{ņ+5X*XT$JTnڣZ+xS6qڡV`̦ƱLmEmhhhI$-4mIQUBC#:#5ÍFI2#&0`VAQ Kq?^iv{2{{N'JV*iZlmo̭WV_nJ)IJVeFђVݦVlM5Wm6?;GdiROBRd9 1T 9d!!k#/ŵÁɶH⃁^USwt0d6[B>٩G*rl~RT"bH~v,at2ŋ![ ƫ1za PBի2=$ad^W#'KwՄf# oϿ9ca䕣e[᫅u25@6OBD:>gQ8PpM–s",.uNwn ښ hF4g[C([9}VlۊGNXdwSi,(Vn6'_Pe!<{Ii{MB5NCh}"h DaFE B[|~V~$Qo|%kWlߺjƪD#:`*gyA?9qE@" kp+Ń/d3mҲV^W5ڱUtc#:0@@kAAF  bc$AR!uMޭ} mIQy$d rl#:QI*쑋2ԊLIڅ{E+#:#Ii1&Z3VEm=X@c0c(#:*$DXPJiy#5r64BZ#:\#:/\шX1A"1J o. W\H&6L7^=LM4p!cZhMۿw#/CT{$"#5:ۊ&$B#//8,vM#߫lU *GCKAA?T># ʕ"{Q>c>i? Q3^+ijO7j#w}3.19hMՀ F cƊNwf-E{bHBixJ6{,}04~%A`QKSIly#/7$E[F1[M/b?D]bney;;}FIsj/2|d: CUroO#5@! ##5#: #5%\j%A<-9U}#5wGV vZ#\ .bY *miuyʅ99#5LMi ,h-சmί7uuͻtx9lK۶cVimEX٦kLQi]Kweugu殡͋EFݦ ql_b{ #5p1A{wGdt#:_uE = Au<tz~5:S!?;SnEA)D`ꦽ{Kzk#:~P^~HPA><wiS`EHc$jL/WoV|,0u9Fױ %lkl9G"'BBINu]ԳQFn\t劊1+7ΪU5I0v_mS E#5 ;r#PJT#5eF^Uv6򧴕V-eKރS43#:(o")1P L`RТ+#/j{+N#/#/*ekY-e/o6:+ԙ6ŶGy0Jqrp>}@ 4Lҙ6qE$#/ˇQ֦|5ȡ)X#:z]ܪ8`ɐIp@.-K3K e&\K#lRU#51H $Y#/ATT 5L N@&p\ ƈ'M+ds9-W(YTp@%((#: CX&;نL$c k)#Ӻ j#!#/숪|!E>xTDZ(T)H#5FvY5q3#/ħ6y&'رFҪJ&di5J}RFi4\g#r!c#I(#:6$cO#M3-<mS1$"wyh P[qarF܃UV9nٹL7@@#5gT@N#:#/X!IV{h*$QN#:luV@z?%68B ^(/n+l,d"'̡#" N(j:*Hzx]ow×Ff0Dj@CҦ76FKafRΛo#5ΒJT g,L5lj9|kVff?d]'#5^0 ЊЪyp Z|_4?/*'] f0niCB۶Dkqj<|UQr.1C*會u =1~jL['IL8]a #: F1t2MaDV$X5i*V1MDC>$FXyu2aI3.LU7x>Yt#:\#:!J; G=Zf1i;m8\)j$j>Ǐs~j)3L01u[4h!$4ݳ !C%9+!{ w騯OxfF6Ɗ#/BܩHd b=%Hq'rGԡL{&߯Ng SYW p@QVvq#5zSAžE HN5a&@SDI~F8NID iHHB#:n˳ h#:l`8Csfa$#5QHTA ^D=412{+Hʪ1m{=#5X*6``@+2jkn31 r<;']2(O/ kknS 䉿!i!lzԜb2NR_* 0ppkv(5Sl0JhZJ88#5#5Lb'6PAd+×"amAgme՗ C0vC7zVI$1Cpt&Xt r[ֆ4cesyOaEU[sA zL:gK"۸c+s Xamy Z]zBS4Y L(h#$ 0EF#5(#:H! #/@hw-WT7|}8ϴ)oC$btqA:.ż740NRmv7!Ja%O^RJT^XY:GimşG#xQgd8ɒOHbtz n% 3WKBnboOc^c@S#:" ogT#/@aD!Hv2BŌ59o# ;=U#*hIKB1Cz(GGL9cmnqX֐#:Q=Pji/#>;z T*#/т5NxC@` [8 $:_)AAS9:fvM>!ÐE6 ȡ]MЬBzQ/hHb@~з|/^qgl?)l~#/uNib#/(B~kd#5~8uD,/D*>T2JdGWXr}8ͮ#53BB΄n%j{vF[CܘPDh@G-FZhf@PHZ`$ &͔C`Âby6Ȟl,qPZ 馠XZ4#:~IFk6ҚZjlYEjld C>P!>}A~R1$ 4a#5Ɉf̒Xl5)SHRR hܢO!g_'>ߺRI폘UtT֍Q冉3JPH(xќy@ZplMCa* ]tIH=G6)M*E)jQ6A$+@DCtFX8@j5C6<#:!eChD 2eQ2UZtA+0nF HD,X'iȁUaXGUm*n5!D`СU|6[N͏Zo(j5!.ƃ(LMmLyMbRd|:۶(*J#5jXd#̅1X1b\/#AC:AHffA:Ε@qwT7HkS#%qd`2@4jκ՘;!D`27nѢ.r0$yJ`R3j4j#5 b#:f5(ȒiwL#5RTM|#/z] 1vIT7sA 1G,P#:-@4h YLiM {j'ݦ&?mh֤cg &P)@XRUx=tXh9ƚfMUTL3dXzjhaA&Tk`STط7'C0l=,z`h#:2_l =XP(*nLR#/l (G/:pPf[VۥMCQVD@ٓJ5Ĺ!`FԂ٤D#Ih7#/4Ѕ#:J#Hp`#5en`m h~Ei^*DjT^ěc 7IPcµF/WξDP[QVƅ,1d#)l{,5咭êO/BȈ)#/M#/.v?+}(\kDFBQ\xQVP$AR0RS`нq= <#:C 4PL H?uQ)qȜM ]ClVxg:؂WWKk{n:A=gMD`Aj$1Ql_Qs~yvжMLcix!IkEJ@m)0ִ|z,[E&-u9pSq-/gyXavk24G V4-x3W zn9X`gR1a\̒1>vw=B1 Wj xiS|?kՎKIkyi06A'3I! 0kp"HY~ @);#:0_5#/rt}SMפE^ ofaķ" >=ؑZF Do#6RŤ*$hZ6SNJչiU81CMA@-#:a )T6xo'&Λ)S)!9MTAt^bUx]ni=.PCU!Б**^9rQTm#: HiV$A(nɎQ«ih17S `x;_sB> ]>O4ѕ#/Y#/p<#5*^=r"H߿y(Wt{─|9U#/jDB|Dp"ȁ!LVwT#:F#:$YQhR^[xX E I QkQ5|"YP7lAE@dD#/ u?CԪ'WArH&=E@+\KUD"WC ̞A:)zf͋ !+ HF6R"4CfjeC-j6M4#:#/"PX0<Vjd#:#/ɌDlD aK`aQlt̀а5w %h ,nd(6#59,"#:bahhE$ *#:(b0#:/Os#5ZT,vڮ׮SAQA\#kCW M%*OM$_%aO_3Z\)^@{X$U|Co-#5! ]!(17f&&( 2V$Ҳ2 }#5[tDI o"70e@?lX}W۶%撝[K33z j5ȊAZ"%\r!2Y$ՓfJ(e՜5w&ͬRR({^m0c8$z~{PCˮoϧ4St#q; oTeY^gHI&z3K9#5:$>pGNG!`M8@Wm D3Lt"B<12v#:)m5BٸB99\Ct`!4/17 i2yYxmF]!%#/tFFf"i5'N2gg8ųtStff;)Td>\yxi"z~; :1#3Hm0L<{&Gnv@Sy>cch:=7Uܗѹj!1ruOB_,~]iC386)Hfvp@@mWP7hEiՁ/j<>y_4:VʧT['KxNH%*?;Zd&Hcڝ[Rϯԭ&yBɳJs_^m>'1N:-33U'wb`u֎&QoEEd//!Mt)!, 8C=*RN8M{1]{ή &9@4m#5Th2s6c^WFG >ZΧ|-|y[79)#:񓩫b#5ߧI% v5 &dTQm`ąi> mcM ֌ F#3Ljz=Ƥ^<$Big5:c ^!a*kk>#:h{ kku L>ٱ2--O#:u=vĎ/溹λ"PD/=Q$d?nd03V0x5P#544U1iN(z--X7\]p X ,#/ՓbT`ffK-CzI1e#V\楌Fy89vH3fEΰ֧C3xr&i6ډRmHXK.ԑNµ j8:5H##:AJtꛦ]PwE{wF0RFT. fpjYf8d5&]Jf6z|k;jVFڻm48&Ap0Lb֙MPš[8J6je*XaM0laXj,R7/<]-0Bb&!D9 hrH4v1kYx #5\C@(8#cTnL'TfA%n "A)FU-81A[#:+aXbX z6񬭃^Yái=r=.4:(e*#2 *-7b$`GګzpMW~p02p@}In9i?Kۭe)4l!|&0#:ՙR4'NM#M#, LJ^2zx֑|#:uի} i9Ìa%M}؈ ĚmQ#5Ǘd?UdF#:˺60`oyPQHIvV6mr۵ʩ$}CWx]TsA;*?Ku68xG?TЌ>i[DG4$U֘}EG> [Ed%vCF#epNE! M#㢁>@7O!aPKGX'z2YtRg N"ȡ#:kE-F"ovTKiYRY #/20yx$0_z\M MDȈL#zX59eg1Qte;Hٱq u`bбc@O2,`5k#5;{w{B|k,5#`9;YRgKbz-Xjӽ)DB$&FE`^ly|#8AI8-lHsŮ/_>󊢼kvc&bTb#e5ŕҋހ<2S#:Vѡ4̀ta E0^vX crjH6[%)s#/2"ޑ!I$=v҇5P@F$EgQ#/n(9=7;hoJߧ6EΨ;Hc#5AҰt2 5MF C+ DԒ,]bc6~Öfr0.xʯ6u(Eu㮺TRjeRy/:M&-z^4>l޵7*}Yr'YAPz'ا#5GU'M߸L!#/͞ΥClde[Q.!E~_sth*Ҍ*Q<:O#/"$$!!#: o?H/mRFOE#5*=8i!"&׺%ɧ P1~H3~0r+]~'8T<T=USOmb+[-,\LTTpd #:ÃCvt o|AYܥV1pUP*⣇c<8>mK#LC* Rq E*kj5KX#:O)VdAoNp=MYDk 0b͔7/5mmҾN((6֌ƗS)r#K88#5K_:#50 aX+N6t(ȇ:+M*#5GXsC͠vݠ%,*M9#56dM3R i rN=G"i246tQ`S3:ui4@䅞ZFD[4=se6 ݙ7#u>gV6f6" ZJLnF5j6X%]hŌ]K8|foz:¼P{5:r1y#5iIu5uT$ M-ⓛCA6,naOW.'.YNˮ "*>Ϯjq }.oFKÎ9ecd0c&*9l!6R#/arp?;o-͑C"[._q4rFVfa^eK#:p\ {YqU00}|\#5@m9ƒoqi5oO`@B*Y|/(Ȱ|(3ŭof^II 0Qi#5]٩ÛCcgMPٽ"``8Ct%J%#5T:fHH@_,8kG*ƺ'DsF3{i ot$ 5OuwqwcJ#A5<d  S&XVi~W87F"PyhEAFH"PԚmaך Gpyɠ2m3J meM%#5Br LbrlV}wė D0@̘"102KHnr f&"TK.#nגT$\mv5b"4kr6n&d2!R,"r))*MP5I#/`6]Q* H9@S@)eQ͌\M!ԧ8 P+tL  #:j} >xK`EDT4v!7,={F>Ȫ#5EF! ªT^+8guga/^u\îg\OЬbM|,n%jk) z'u4_#Ɠ9}xwCx#7~sn>)L.Q ʕij ~r2=E3=a:A$N<6Tá= 4mtR`MRpAUjIAgR"V(nވbm.N柩+1T-Z%k16_]Q'bL>DI3dEMa3N`4J}e@'I$L辻܇#:%tRaI |#5$v}ʼ2uEA ç hŘRȱX2EcxK݅J祹i]BXF2<(Q#/ TuA#:XS[#/h&b1= 6#5![r/"(X-AFFR[2Hc xC{"X!Dr4Tp RKs1U!eyؚHW2]Pr(OWXMu<88#5TU9΂1)1.`0ǐ#/Ms!6ND睳;`=Wc!qBqnf|HSu{YwIHt(%6!$J#:=u8^I|̷SwK_|(5BETiC](ɝ+'wD $RT~dI'ȂЊh` 䒘 Hb1 1PwAٖ!;d>HC~ߓy}4UJ4mK2h%k?t4Pgƙ3"b  " Ϗ\|:(2˃Z.u_X̦7f)H#5jq#:h"Ys(PF+xPAMZAy;Y㤳Ho4#y[(D@R1H1&SLf*meCFͩktJMZ:nͮ}Q_@z:d8AF9#/uZ#/G8M$0b$H h#d{!Ǡ@;"_L}\K>zŒZ~c,Q# -ٮQAMX΢(4W!߈-- xJmk#:PsΛ6S Nδt \!Ƴ8}̍4ۤNq3QYk# H -5,)u5,~Œg>X 5ԡljDᚥ0R޳Om4mUrhgG2#LQ~E(p<ʩ hRRv6I] R d(%#/H*}<{9&nhvL`G8A@ A)]dz>cƍSz^#`z@"W׳bttX0TНT^#5#lN`O?R4a/i÷˶ޜU*As̤"u NT\rhkYk&~gzs )"F,9xKF5+*#5灸 ;@B8H%.Y)"*1d2 ;aƪmX~ B4Ae Wv-QL1ʛiE?q#:@IQ1o|yz$#/8,3D`=FMb;YAd"A0BBgcI X5dL$}ߦ6/TQbQli*6Kw"ȼO*@M?$%-v{h7\IdhEY:j`E2"k6*EjKMoWB/iToFO}_a"I䯠 lpoٸt4$#5ݺ- 觉 !y#3c%$Obz%~X5dy4B½`φfؑ ePnFD7$;8`ABB'R{ֈ0T,I7RB&hMT Bͷ&QLJ=;`MPP!졠?9<#0azZ ix;JάܴlN~ -2$O"^Jߣm 2_Ǐ]58Ѷ6 ǫ#/v׫/Îf#5b,jJ#5XɵFol(h#:Te,HXO:d U,<覉?-BʡAp\pBwAHXQ (H[F6+KiHYiy$51A EGOQ #x#/ށG]ɪ=2Wn`\4̅0 kM҅ @7 6LJB$D#/27k\έo13KD>`V~"A-X\AόPG8r+#:mH#:D@W١a7R(,%LipCt셶+(mHUivjfeWs*5\YRo;Ϋ /nʋ*%0AEQm)[2MwV搥Br-#:εm!BIK=»)?ۅ\%ҥX$8#:Ԟ;M [*ĬX[|#5 c" &v(V55hc#:}j͟8qPU]zqfHNZFQh)6j"A I%d 0}]Roߒ-9@dЙs8~ܙtwK(Hb^ƕm؈ }vJ8b,~y N/Ϯucbkp|Xes|q`׈4f^_E<0W\6p{SMU\E Dܿ[l,ӵ?qB/}#5"Ș&DanM 0!{7ɉ/bTVq̏RQ#:]/lIʿmdR*L pl{ω`:f/jIb83G3zZYR4?[xn-; گZ΍,q'"Nng ^Pj#5nuQ2 tȶ,S#d5 xa!@3ad.<1\άhP4q#/pyLn!!rc `Z vDGM15@;`-?!YϼQl8E~ݠD?#5SHԞ@G,P;2dōb^*o-}4#: *ֱ1P4O}_g?~~ͧ?Oz?߫ߓ_{gW?_~.ߣs@hPChWgT Y?*@CH$#_i{;dL\0s5NQE MME8b.!pg5HJ]gFP{Zc3e< `Po?eGIZT^&?T)CB@g'M@Iqִ8M1(cQJ?ZaTg~*5hY.]$DC#5B;x?@`wpZeX h=0#5]f3MtjnJ82t#E/51ul$ 9 WqiWmģ/./L-.4R:c'?ٗf'e#:f,Y`/<Ո5޸+n0DcF+f]dWLpbuW{w{h1󍗳vѫ…IHB cx拔9YrZOYgpYQFp#:㾚|uܛ P#uR5/n[)ݿc .$dm p8劀>#:ȂAzd#:PbH#:#5C5YU̒ {}w!9=z;,ڑhdIUYAM PPUS%k%*4#:@9K~A?YEhs;Z$dCL9_@_Kz&OyC7yQp0#:!>a|$@`DS{l'mEVKjFmvWlkʭ~(-H@#566uwL`ƶ8oNvϑۭiNjtK4tbq(>bPȹ$Ե_%;~ ( %OL[m #:dP-zZ#5[.ikoO/(M:tZ(^ۊ+'k˪ _GMһxT>yrD;}tN]@&2S?xGu,4N*%bx:r:JƲ^(,+DCmԔ2'>B̚ta'3\߱OH#:/> -#<== -#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1\n\niQIcBAABCgAGBQJUja5YAAoJEGelZe39+Q5kZ0kP/RK2odJp1mbvHst3fceud4Dw\n69ymSDzBJKsLyl5oPl6RGbSXxGFnyd0eFuewUcJPVsNLObSfaXIA/AJdHAtZ8y9t\nbN51jD99jBGO+teMvjK93EFIb4P7cv8PA/LhQq3NUy2zrj2dN+TFjXx6+PU7BKrz\nTT0jTbXinNSF6Jr4SEqFrY/dpzs+IKSAEHP0BmPcMxmDU/uCCt27RhTgjQYaLLGi\nY7ZzhBxUk8PgIIpSucOUMQx/xbMvgbSRNAYVqqhBlnHjgPAbFY55d+k4uvlu2TJX\nmFXcOhB9dZbr9xuFyUD3fvfBQ6OZzrKfgNsiqqeTLVehDEaYexZ96eoDJXZawyjU\njer1p7qxKqscTKLsyJWbmjTAxElgmlLY2+LB9LRYqXT4tUbcPiahHo2djcdVprDI\nnEZlUx12uCHoLmKBAMtD2ha2uTXtkl8GU44zB2oOQU8hoXtRITWYyF6Q9qlLp9qw\nosuQoQ+6ovT83q8OLnBUmGbVYa4GFab2dq4rMXX91o3hMQ806xQTaijUlZCFGMD0\nxa10IC0udzGOMNPL749zlKOaK68LUbrGPiy65dBjN3i9zU0itYTSPLS+wu9GdY3m\ndR96fFsGRNcceHAXuqMl+nxtJVE8sMNO1VRle1sGUqfBw4jUnJ6CvShXsZQuAeZj\nSzBEpSFrNVqBi/BdbcZ7\n=HKUv\n-----END PGP SIGNATURE-----\n diff --git a/platform/snowy/boot/waftools/binary_header.py b/platform/snowy/boot/waftools/binary_header.py deleted file mode 100644 index e8256534a8..0000000000 --- a/platform/snowy/boot/waftools/binary_header.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import binascii - -from waflib import Task, TaskGen, Utils, Node, Errors - -class binary_header(Task.Task): - """ - Create a header file containing an array with contents from a binary file. - """ - - def run(self): - if getattr(self.generator, 'hex', False): - # Input file is hexadecimal ASCII characters with whitespace - text = self.inputs[0].read( - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - # Strip all whitespace so that binascii is happy - text = ''.join(text.split()) - code = binascii.unhexlify(text) - else: - code = self.inputs[0].read('rb') - - array_name = getattr(self.generator, 'array_name', None) - if not array_name: - array_name = re.sub(r'[^A-Za-z0-9]', '_', self.inputs[0].name) - - output = ['#pragma once', '#include '] - output += ['static const uint8_t %s[] = {' % array_name] - line = [] - for n, b in enumerate(code): - line += ['0x%.2x,' % ord(b)] - if n % 16 == 15: - output += [''.join(line)] - line = [] - if line: - output += [''.join(line)] - output += ['};', ''] - - self.outputs[0].write( - '\n'.join(output), - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - self.generator.bld.raw_deps[self.uid()] = self.dep_vars = 'array_name' - - if getattr(self.generator, 'chmod', None): - os.chmod(self.outputs[0].abspath(), self.generator.chmod) - - -@TaskGen.feature('binary_header') -@TaskGen.before_method('process_source', 'process_rule') -def process_binary_header(self): - """ - Define a transformation that substitutes the contents of *source* files to - *target* files:: - - def build(bld): - bld( - features='binary_header', - source='foo.bin', - target='foo.auto.h', - array_name='s_some_array' - ) - bld( - features='binary_header', - source='bar.hex', - target='bar.auto.h', - hex=True - ) - - If the *hex* parameter is True, the *source* files are read in an ASCII - hexadecimal format, where each byte is represented by a pair of hexadecimal - digits with optional whitespace. If *hex* is False or not specified, the - file is treated as a raw binary file. - - The name of the array variable defaults to the source file name with all - characters that are invaid C identifiers replaced with underscores. The name - can be explicitly specified by setting the *array_name* parameter. - - This method overrides the processing by - :py:meth:`waflib.TaskGen.process_source`. - """ - - src = Utils.to_list(getattr(self, 'source', [])) - if isinstance(src, Node.Node): - src = [src] - tgt = Utils.to_list(getattr(self, 'target', [])) - if isinstance(tgt, Node.Node): - tgt = [tgt] - if len(src) != len(tgt): - raise Errors.WafError('invalid number of source/target for %r' % self) - - for x, y in zip(src, tgt): - if not x or not y: - raise Errors.WafError('null source or target for %r' % self) - a, b = None, None - - if isinstance(x, str) and isinstance(y, str) and x == y: - a = self.path.find_node(x) - b = self.path.get_bld().make_node(y) - if not os.path.isfile(b.abspath()): - b.sig = None - b.parent.mkdir() - else: - if isinstance(x, str): - a = self.path.find_resource(x) - elif isinstance(x, Node.Node): - a = x - if isinstance(y, str): - b = self.path.find_or_declare(y) - elif isinstance(y, Node.Node): - b = y - - if not a: - raise Errors.WafError('could not find %r for %r' % (x, self)) - - has_constraints = False - tsk = self.create_task('binary_header', a, b) - for k in ('after', 'before', 'ext_in', 'ext_out'): - val = getattr(self, k, None) - if val: - has_constraints = True - setattr(tsk, k, val) - - tsk.before = [k for k in ('c', 'cxx') if k in Task.classes] - - self.source = [] diff --git a/platform/snowy/boot/waftools/file_name_c_define.py b/platform/snowy/boot/waftools/file_name_c_define.py deleted file mode 100644 index b16bd42f8c..0000000000 --- a/platform/snowy/boot/waftools/file_name_c_define.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Define a __FILE_NAME__ macro to expand to the filename of the C/C++ source, -stripping the other path components. -""" -from waflib.TaskGen import feature, after_method - -@feature('c') -@after_method('create_compiled_task') -def file_name_c_define(self): - for task in self.tasks: - task.env.append_value( - 'DEFINES', '__FILE_NAME__="%s"' % task.inputs[0].name) diff --git a/platform/snowy/boot/waftools/gitinfo.py b/platform/snowy/boot/waftools/gitinfo.py deleted file mode 100644 index 25e49fbe50..0000000000 --- a/platform/snowy/boot/waftools/gitinfo.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re - -import waflib.Context -import waflib.Logs - -def get_git_revision(ctx): - try: - tag = ctx.cmd_and_log(['git', 'describe'], quiet=waflib.Context.BOTH).strip() - commit = ctx.cmd_and_log(['git', 'rev-parse', '--short', 'HEAD'], quiet=waflib.Context.BOTH).strip() - timestamp = ctx.cmd_and_log(['git', 'log', '-1', '--format=%ct', 'HEAD'], quiet=waflib.Context.BOTH).strip() - except Exception: - waflib.Logs.warn('get_git_version: unable to determine git revision') - tag, commit, timestamp = ("?", "?", "1") - # Validate that git tag follows the required form: - # See https://github.com/pebble/tintin/wiki/Firmware,-PRF-&-Bootloader-Versions - # Note: version_regex.groups() returns sequence ('0', '0', '0', 'suffix'): - version_regex = re.search("^v(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:(?:-)(.+))?$", tag) - if version_regex: - # Get version numbers from version_regex.groups() sequence and replace None values with 0 - # e.g. v2-beta11 => ('2', None, None, 'beta11') => ('2', '0', '0') - version = [x if x else '0' for x in version_regex.groups()[:3]] - else: - waflib.Logs.warn('get_git_revision: Invalid git tag! ' - 'Must follow this form: `v0[.0[.0]][-suffix]`') - version = ['0', '0', '0', 'unknown'] - return {'TAG': tag, - 'COMMIT': commit, - 'TIMESTAMP': timestamp, - 'MAJOR_VERSION': version[0], - 'MINOR_VERSION': version[1], - 'PATCH_VERSION': version[2]} diff --git a/platform/snowy/boot/waftools/ldscript.py b/platform/snowy/boot/waftools/ldscript.py deleted file mode 100644 index 1a3626f8b6..0000000000 --- a/platform/snowy/boot/waftools/ldscript.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Utils, Errors -from waflib.TaskGen import after, feature - -@after('apply_link') -@feature('cprogram', 'cshlib') -def process_ldscript(self): - if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': - return - - node = self.path.find_resource(self.ldscript) - if not node: - raise Errors.WafError('could not find %r' % self.ldscript) - self.link_task.env.append_value('LINKFLAGS', '-T%s' % node.abspath()) - self.link_task.dep_nodes.append(node) diff --git a/platform/snowy/boot/waftools/objcopy.py b/platform/snowy/boot/waftools/objcopy.py deleted file mode 100644 index c99fdc7e20..0000000000 --- a/platform/snowy/boot/waftools/objcopy.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Grygoriy Fuchedzhy 2010 - -""" -Support for converting linked targets to ihex, srec or binary files using -objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' -feature. The 'objcopy' feature uses the following attributes: - -objcopy_bfdname Target object format name (eg. ihex, srec, binary). - Defaults to ihex. -objcopy_target File name used for objcopy output. This defaults to the - target name with objcopy_bfdname as extension. -objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. -objcopy_flags Additional flags passed to objcopy. -""" - -from waflib.Utils import def_attrs -from waflib import Task -from waflib.TaskGen import feature, after_method - -class objcopy(Task.Task): - run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' - color = 'CYAN' - -@feature('objcopy') -@after_method('apply_link') -def objcopy(self): - def_attrs(self, - objcopy_bfdname = 'ihex', - objcopy_target = None, - objcopy_install_path = "${PREFIX}/firmware", - objcopy_flags = '') - - link_output = self.link_task.outputs[0] - if not self.objcopy_target: - self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name - task = self.create_task('objcopy', - src=link_output, - tgt=self.path.find_or_declare(self.objcopy_target)) - - task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) - try: - task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) - except AttributeError: - pass - - if self.objcopy_install_path: - self.bld.install_files(self.objcopy_install_path, - task.outputs[0], - env=task.env.derive()) - -def configure(ctx): - objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) diff --git a/platform/snowy/boot/wscript b/platform/snowy/boot/wscript deleted file mode 100644 index 3d9d5a70e4..0000000000 --- a/platform/snowy/boot/wscript +++ /dev/null @@ -1,134 +0,0 @@ -import os -import sys - -import waflib.Logs - -sys.path.append(os.path.abspath('waftools')) - -import gitinfo - -def options(opt): - opt.load('compiler_c') - opt.add_option('--board', action='store', default='snowy_bb', - help='Which board to build for ' - '(snowy_bb|snowy_evt|snowy_evt2|spalding)', - choices=[ 'snowy_bb', - 'snowy_bb2', # Alias for snowy_evt2 - 'snowy_dvt', # Alias for snowy_evt2 - 'snowy_s3', - 'snowy_evt', - 'snowy_evt2', - 'spalding']) - opt.add_option('--nowatchdog', action='store_true', - help='Do not enable watchdog timer or reset upon failure') - opt.add_option('--loglevel', type='int', default=0, - help='Set the logging verbosity [default: %default]') - opt.add_option('--big-bootloader', action='store_true', - help='Build for boards with an unprogrammed FPGA, loading ' - 'and launching firmware at 0x08010000') - -def configure(conf): - if conf.options.board in ('snowy_bb2', 'snowy_dvt', 'snowy_s3'): - conf.options.board = 'snowy_evt2' - - CPU_FLAGS = [ - '-mcpu=cortex-m4', '-mthumb', - '-mfpu=fpv4-sp-d16', '-mfloat-abi=softfp', - ] - OPT_FLAGS = [ '-Os' ,'-g' ] - C_FLAGS = [ - '-std=c11', '-ffunction-sections', - '-Wall', '-Wextra', '-Werror', '-Wpointer-arith', - '-Wno-unused-parameter', '-Wno-missing-field-initializers', - '-Wno-error=unused-function', '-Wno-error=unused-variable', - '-Wno-error=unused-parameter', '-Wno-error=unused-but-set-variable', - '-Wno-packed-bitfield-compat' - ] - - conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True) - conf.env.AS = conf.env.CC - for tool in 'ar objcopy'.split(): - conf.find_program('arm-none-eabi-' + tool, var=tool.upper(), - mandatory=True) - conf.env.BOARD = conf.options.board - conf.env.MICRO_FAMILY = 'STM32F4' - conf.env.append_unique('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS) - conf.env.append_unique('DEFINES', [ - '_REENT_SMALL=1', - 'USE_STDPERIPH_DRIVER=1', - 'BOARD_' + conf.options.board.upper(), - 'MICRO_FAMILY_' + conf.env.MICRO_FAMILY, - ]) - conf.env.append_unique('LINKFLAGS', - ['-Wl,--cref', '-Wl,--gc-sections', '-specs=nano.specs'] - + CPU_FLAGS + OPT_FLAGS) - - if conf.options.nowatchdog: - conf.env.append_unique('DEFINES', 'NO_WATCHDOG') - - if conf.options.loglevel >= 1: - conf.env.append_unique('DEFINES', 'PBL_LOG_ENABLED') - if conf.options.loglevel >= 2: - conf.env.append_unique('DEFINES', 'VERBOSE_LOGGING') - - if conf.options.big_bootloader: - conf.env.append_unique('DEFINES', 'BLANK_FPGA') - conf.env.BOOTLOADER_LENGTH = '65536' - else: - conf.env.BOOTLOADER_LENGTH = '16384' - conf.env.append_unique( - 'DEFINES', 'BOOTLOADER_LENGTH=' + conf.env.BOOTLOADER_LENGTH) - - conf.load('gcc gas objcopy ldscript') - conf.load('binary_header') - conf.load('file_name_c_define') - - conf.recurse('vendor') - -def build(bld): - if bld.cmd == 'install': - raise Exception("install isn't a supported command. Did you mean flash?") - - linkflags = ['-Wl,-Map,snowy_boot.map'] - sources = ( - bld.path.ant_glob('src/*.S') + bld.path.ant_glob('src/**/*.c') ) - - - bld(features='subst', - source='src/git_version.auto.h.in', - target='src/git_version.auto.h', - **gitinfo.get_git_revision(bld)) - _generate_fpga_header(bld) - bld.recurse('vendor') - bld(features='subst', - source='src/stm32f_flash_boot.ld.in', - target='src/stm32f_flash_boot.ld') - bld(features='c asm cprogram objcopy', - source=sources, - includes='src', - target='snowy_boot.elf', - ldscript='src/stm32f_flash_boot.ld', - linkflags=linkflags, - objcopy_bfdname='ihex', - objcopy_target='snowy_boot.hex', - use='stm32_stdlib') - -def _generate_fpga_header(bld): - # This is temporary. The bootloader FPGA image will not need to be included - # in the bootloader once it is burned into the FPGA's NVCM. - if bld.env.BOARD in ('snowy_bb', 'snowy_evt'): - fpga_src = 'snowy_bb1_boot.fpga' - elif bld.env.BOARD in ('snowy_evt2',): - fpga_src = 'snowy_boot.fpga' - elif bld.env.BOARD in ('spalding',): - fpga_src = 'spalding_boot.fpga' - else: - fpga_src = None - - if fpga_src: - bld(features='binary_header', - source='../' + fpga_src, - target='src/drivers/display/bootloader_fpga_bitstream.auto.h', - array_name='s_fpga_bitstream') - -# vim:filetype=python diff --git a/platform/snowy/snowy_bb1_boot.fpga b/platform/snowy/snowy_bb1_boot.fpga deleted file mode 100644 index 1b9dc187bd..0000000000 Binary files a/platform/snowy/snowy_bb1_boot.fpga and /dev/null differ diff --git a/platform/snowy/snowy_boot.fpga b/platform/snowy/snowy_boot.fpga deleted file mode 100644 index 9a65d47fa0..0000000000 Binary files a/platform/snowy/snowy_boot.fpga and /dev/null differ diff --git a/platform/snowy/snowy_framebuffer_evt.fpga b/platform/snowy/snowy_framebuffer_evt.fpga deleted file mode 100644 index c152088832..0000000000 Binary files a/platform/snowy/snowy_framebuffer_evt.fpga and /dev/null differ diff --git a/platform/snowy/spalding_boot.fpga b/platform/snowy/spalding_boot.fpga deleted file mode 100644 index c77ba2ae28..0000000000 Binary files a/platform/snowy/spalding_boot.fpga and /dev/null differ diff --git a/platform/tintin/boot/boot-bin-update.sh b/platform/tintin/boot/boot-bin-update.sh deleted file mode 100755 index f24fffc219..0000000000 --- a/platform/tintin/boot/boot-bin-update.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -o errexit -o xtrace - -cd "${0%/*}" - -reloutdir="../../../bin/boot" -OUTDIR=`cd "${reloutdir}"; pwd` -BOARDS=(bb2 ev2_4 v1_5 v2_0) -# Use commit timestamp, same as the one compiled into the bootloader binary -VERSION=`git log -1 --format=%ct HEAD` - -# Clear out old versions of the bootloader binaries -for board in ${BOARDS[*]}; do - git rm --ignore-unmatch ${OUTDIR}/{nowatchdog_,}boot_${board}@*.{hex,elf,bin} || true -done - -# Build all bootloader variants and copy them into OUTDIR -build_and_copy () { - local variant="$1"; - shift; - - for board in ${BOARDS[*]}; do - ./waf configure --board=${board} $@ build --progress - for ext in hex elf bin; do - mv build/tintin_boot.${ext} \ - "${OUTDIR}/${variant}boot_${board}@${VERSION}.${ext}" - done - done -} - -build_and_copy "" -build_and_copy nowatchdog_ --nowatchdog diff --git a/platform/tintin/boot/desym.sh b/platform/tintin/boot/desym.sh deleted file mode 100755 index febc86638f..0000000000 --- a/platform/tintin/boot/desym.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -arm-none-eabi-addr2line --exe=build/tintin_boot.elf $1 diff --git a/platform/tintin/boot/flash b/platform/tintin/boot/flash deleted file mode 100755 index 4315ee951a..0000000000 --- a/platform/tintin/boot/flash +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -o errexit - -SCRIPTDIR="${0%/*}" -IMGFILE="${SCRIPTDIR}/build/tintin_boot.hex" - -if [ ! -f "${IMGFILE}" ]; then - echo "Cannot find bootloader binary at '${IMGFILE}'." - echo "Try running './waf build'" - exit 1 -fi - -OPENOCD_SCRIPT=" -init -reset halt -flash write_image erase \"${IMGFILE}\" -reset -shutdown -" - -openocd -f openocd.cfg -c "${OPENOCD_SCRIPT}" - -# vim:filetype=sh diff --git a/platform/tintin/boot/gdb b/platform/tintin/boot/gdb deleted file mode 100755 index 66523a95d3..0000000000 --- a/platform/tintin/boot/gdb +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -TARGET="target extended-remote | openocd -f openocd.cfg \ - -c \"gdb_port pipe; init; reset halt;\"" - -arm-none-eabi-gdb build/tintin_boot.elf \ - -ex "${TARGET}" \ - -ex "break boot_main" \ - -ex "continue" - -# vim:filetype=sh diff --git a/platform/tintin/boot/libc/LICENSE b/platform/tintin/boot/libc/LICENSE deleted file mode 100644 index 0e6a28c404..0000000000 --- a/platform/tintin/boot/libc/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -Copyright Patrick Powell 1995 - -This code is based on code written by Patrick Powell (papowell@astart.com) -It may be used for any purpose as long as this notice remains intact on all -source code distributions - -Copyright (c) 2008 Holger Weiss. - -This version of the code is maintained by Holger Weiss . -My changes to the code may freely be used, modified and/or redistributed for -any purpose. It would be nice if additions and fixes to this file (including -trivial code cleanups) would be sent back in order to let me include them in -the version available at . -However, this is not a requirement for using or redistributing (possibly -modified) versions of this file, nor is leaving this notice intact mandatory. - diff --git a/platform/tintin/boot/libc/ctype_ptr.c b/platform/tintin/boot/libc/ctype_ptr.c deleted file mode 100644 index e0f07cf049..0000000000 --- a/platform/tintin/boot/libc/ctype_ptr.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: (sorta) -// int isalpha(int c); -// int isupper(int c); -// int islower(int c); -// int isdigit(int c); -// int isxdigit(int c); -// int isspace(int c); -// int ispunct(int c); -// int isalnum(int c); -// int isprint(int c); -// int isgraph(int c); -// int iscntrl(int c); -// int isascii(int c); - -#include - -// Ordered for unsigned char -const unsigned char __ctype_data[256] = { - // ASCII set - // These values can't change. Look at ctype.h for information on these macros. -// +00 +01 +02 +03 +04 +05 +06 +07 - _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, // 00 - _CCT, _CCT|_CSP, _CCT|_CSP, _CCT|_CSP, _CCT|_CSP, _CCT|_CSP, _CCT, _CCT, // 08 - _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, // 10 - _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, _CCT, // 18 - _CSP|_CPR, _CPU, _CPU, _CPU, _CPU, _CPU, _CPU, _CPU, // 20 - _CPU, _CPU, _CPU, _CPU, _CPU, _CPU, _CPU, _CPU, // 28 - _CNU, _CNU, _CNU, _CNU, _CNU, _CNU, _CNU, _CNU, // 30 - _CNU, _CNU, _CPU, _CPU, _CPU, _CPU, _CPU, _CPU, // 38 - _CPU, _CUP|_CHX, _CUP|_CHX, _CUP|_CHX, _CUP|_CHX, _CUP|_CHX, _CUP|_CHX, _CUP, // 40 - _CUP, _CUP, _CUP, _CUP, _CUP, _CUP, _CUP, _CUP, // 48 - _CUP, _CUP, _CUP, _CUP, _CUP, _CUP, _CUP, _CUP, // 50 - _CUP, _CUP, _CUP, _CPU, _CPU, _CPU, _CPU, _CPU, // 58 - _CPU, _CLO|_CHX, _CLO|_CHX, _CLO|_CHX, _CLO|_CHX, _CLO|_CHX, _CLO|_CHX, _CLO, // 60 - _CLO, _CLO, _CLO, _CLO, _CLO, _CLO, _CLO, _CLO, // 68 - _CLO, _CLO, _CLO, _CLO, _CLO, _CLO, _CLO, _CLO, // 70 - _CLO, _CLO, _CLO, _CPU, _CPU, _CPU, _CPU, _CCT, // 78 - // Non-ASCII set - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -}; diff --git a/platform/tintin/boot/libc/include/ctype.h b/platform/tintin/boot/libc/include/ctype.h deleted file mode 100644 index 5e52974ef9..0000000000 --- a/platform/tintin/boot/libc/include/ctype.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Casts to int ensure that the return value is int, as spec indicates - -// Uppercase -#define _CUP (1 << 0) -// Lowercase -#define _CLO (1 << 1) -// Number -#define _CNU (1 << 2) -// Space -#define _CSP (1 << 3) -// Punctuation -#define _CPU (1 << 4) -// Control -#define _CCT (1 << 5) -// Printable (only for ' ') -#define _CPR (1 << 6) -// Hex character -#define _CHX (1 << 7) - -// ctype flag table for all char values -extern const unsigned char __ctype_data[256]; - -// Cast to unsigned char to prevent overflow and to map signed char to our range. -// This lets us cut out 128 bytes of data from __ctype_data -#define __ctype_get(C) (__ctype_data[(unsigned char)(C)]) -#define __ctype_check(C, FLG) ((int)(__ctype_get(C) & (FLG))) - -#define isalpha(C) __ctype_check(C, _CUP|_CLO) -#define isupper(C) __ctype_check(C, _CUP) -#define islower(C) __ctype_check(C, _CLO) -#define isdigit(C) __ctype_check(C, _CNU) -#define isxdigit(C) __ctype_check(C, _CHX|_CNU) -#define isspace(C) __ctype_check(C, _CSP) -#define ispunct(C) __ctype_check(C, _CPU) -#define isalnum(C) __ctype_check(C, _CUP|_CLO|_CNU) -#define isprint(C) __ctype_check(C, _CUP|_CLO|_CNU|_CPU|_CPR) -#define isgraph(C) __ctype_check(C, _CUP|_CLO|_CNU|_CPU) -#define iscntrl(C) __ctype_check(C, _CCT) - -#define isascii(C) ((int)(((unsigned char)(C)) < 0x80)) -#define toascii(C) ((int)(((unsigned char)(C)) & 0x7F)) - -// __typeof__ use is to prevent double-evaluation -#define toupper(C) \ - ({ __typeof__(C) X = (C); \ - islower(X) ? (int)X - 'a' + 'A' : (int)X; }) - -#define tolower(C) \ - ({ __typeof__(C) X = (C); \ - isupper(X) ? (int)X - 'A' + 'a' : (int)X; }) diff --git a/platform/tintin/boot/libc/include/inttypes.h b/platform/tintin/boot/libc/include/inttypes.h deleted file mode 100644 index 7a887ef493..0000000000 --- a/platform/tintin/boot/libc/include/inttypes.h +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// There's no way to check this from the preprocessor, and this header isn't freestanding, so... -// If we ever upgrade GCC, we may need to re-evaluate these two. -// You can check the types by running `arm-none-eabi-gcc -dM -E - - -int abs(int j) __attribute__((__const__)); -long labs(long j) __attribute__((__const__)); - -int atoi(const char *nptr) __attribute__((__pure__)); -long int atol(const char *nptr) __attribute__((__pure__)); -long int strtol(const char * restrict nptr, char ** restrict endptr, int base); diff --git a/platform/tintin/boot/libc/include/string.h b/platform/tintin/boot/libc/include/string.h deleted file mode 100644 index 9c553be4f2..0000000000 --- a/platform/tintin/boot/libc/include/string.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define __need_size_t -#define __need_NULL -#include - -int memcmp(const void *s1, const void *s2, size_t n); -void *memcpy(void * restrict s1, const void * restrict s2, size_t n); -void *memmove(void *s1, const void *s2, size_t n); -void *memset(void *s, int c, size_t n); -void *memchr(const void *s, int c, size_t n); -char *strcat(char * restrict s1, const char * restrict s2); -char *strncat(char * restrict s1, const char * restrict s2, size_t n); -size_t strlen(const char *s); -size_t strnlen(const char *s, size_t maxlen); -char *strcpy(char * restrict s1, const char * restrict s2); -char *strncpy(char * restrict s1, const char * restrict s2, size_t n); -int strcmp(const char *s1, const char *s2); -int strncmp(const char *s1, const char *s2, size_t n); -char *strchr(const char *s, int c); -char *strrchr(const char *s, int c); -size_t strcspn(const char *s1, const char *s2); -size_t strspn(const char *s1, const char *s2); -char *strstr(const char *s1, const char *s2); - -int atoi(const char *nptr) __attribute__((__pure__)); -long int atol(const char *nptr) __attribute__((__pure__)); -long int strtol(const char * restrict nptr, char ** restrict endptr, int base); diff --git a/platform/tintin/boot/libc/string/atoi.c b/platform/tintin/boot/libc/string/atoi.c deleted file mode 100644 index 1f80e6a48a..0000000000 --- a/platform/tintin/boot/libc/string/atoi.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// int atoi(const char *nptr); -// long int atol(const char *nptr); -/////////////////////////////////////// - -#include -#include -#include -#include - -intmax_t strtoX_core(const char * restrict nptr, char ** restrict endptr, int base, bool do_errors, - intmax_t max, intmax_t min); - -int atoi(const char *nptr) { - return strtoX_core(nptr, NULL, 10, false, INT_MAX, INT_MIN); -} - -long int atol(const char *nptr) { - return strtoX_core(nptr, NULL, 10, false, INT_MAX, INT_MIN); -} diff --git a/platform/tintin/boot/libc/string/memchr.c b/platform/tintin/boot/libc/string/memchr.c deleted file mode 100644 index 80d754f56d..0000000000 --- a/platform/tintin/boot/libc/string/memchr.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// void *memchr(const void *s, int c, size_t n); - -#include - -void *memchr(const void *s, int c, size_t n) { - const unsigned char *p = (const unsigned char*)s; - unsigned char ch = (unsigned char)c; - for (size_t i = 0; i < n; i++) { - if (p[i] == ch) { - return (void*)&p[i]; - } - } - return NULL; -} diff --git a/platform/tintin/boot/libc/string/memcmp.c b/platform/tintin/boot/libc/string/memcmp.c deleted file mode 100644 index ef6a610323..0000000000 --- a/platform/tintin/boot/libc/string/memcmp.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// int memcmp(const void *s1, const void *s2, size_t n); - -#include - -int memcmp(const void *s1, const void *s2, size_t n) { - const unsigned char *p1 = (const unsigned char*)s1; - const unsigned char *p2 = (const unsigned char*)s2; - while (n--) { - int diff = *p1 - *p2; - if (diff) { - return diff; - } - p1++; - p2++; - } - return 0; -} diff --git a/platform/tintin/boot/libc/string/memcpy.c b/platform/tintin/boot/libc/string/memcpy.c deleted file mode 100644 index 8e0ea089f6..0000000000 --- a/platform/tintin/boot/libc/string/memcpy.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// void *memcpy(void *s1, const void *s2, size_t n); -// void *memmove(void *s1, const void *s2, size_t n); -/////////////////////////////////////// - -#include -#include - -void *memcpy(void * restrict s1, const void * restrict s2, size_t n) { - char *dest = (char*)s1; - const char *src = (const char*)s2; - while (n--) { - *dest++ = *src++; - } - return s1; -} - -void *memmove(void * restrict s1, const void * restrict s2, size_t n) { - char *dest = (char*)s1; - const char *src = (const char*)s2; - if (dest <= src) { - while (n--) { - *dest++ = *src++; - } - } else { - while (n--) { - dest[n] = src[n]; - } - } - return s1; -} diff --git a/platform/tintin/boot/libc/string/memset.c b/platform/tintin/boot/libc/string/memset.c deleted file mode 100644 index 63c080a7d5..0000000000 --- a/platform/tintin/boot/libc/string/memset.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// void *memset(void *s, int c, size_t n); -/////////////////////////////////////// - -#include - -void *memset(void *s, int c, size_t n) { - unsigned char *p = (unsigned char*)s; - while (n--) { - *p++ = (unsigned char)c; - } - return s; -} diff --git a/platform/tintin/boot/libc/string/strcat.c b/platform/tintin/boot/libc/string/strcat.c deleted file mode 100644 index e3b8349be0..0000000000 --- a/platform/tintin/boot/libc/string/strcat.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// char *strcat(char *s1, const char *s2); -// char *strncat(char *s1, const char *s2, size_t n); -/////////////////////////////////////// -// Notes: -// Tuned for code size. - -#include - -char *strcat(char * restrict s1, const char * restrict s2) { - char *rc = s1; - s1 += strlen(s1); - strcpy(s1, s2); - return rc; -} - -char *strncat(char * restrict s1, const char * restrict s2, size_t n) { - char * rc = s1; - s1 += strlen(s1); - - size_t strn = strlen(s2); - if (strn > n) { - strn = n; - } - memcpy(s1, s2, strn); - s1[strn] = '\0'; - return rc; -} diff --git a/platform/tintin/boot/libc/string/strchr.c b/platform/tintin/boot/libc/string/strchr.c deleted file mode 100644 index 53955db11a..0000000000 --- a/platform/tintin/boot/libc/string/strchr.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// char *strchr(const char *s1, int c); -// char *strrchr(const char *s1, int c); -/////////////////////////////////////// - -#include -#include - -char *strchr(const char *s, int c) { - size_t n = strlen(s) + 1; - return memchr(s, c, n); -} - -char *strrchr(const char *s, int c) { - char ch = (char)c; - size_t i = strlen(s); - do { - if (s[i] == ch) { - return (char*)&s[i]; - } - } while (i--); - return NULL; -} diff --git a/platform/tintin/boot/libc/string/strcmp.c b/platform/tintin/boot/libc/string/strcmp.c deleted file mode 100644 index bc39158334..0000000000 --- a/platform/tintin/boot/libc/string/strcmp.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// int strcmp(const char *s1, const char *s2); -// int strncmp(const char *s1, const char *s2, size_t n); -/////////////////////////////////////// -// Notes: -// Tuned for code size. - -#include -#include - -int strcmp(const char *s1, const char *s2) { - size_t n = strlen(s1) + 1; - return memcmp(s1, s2, n); -} - -int strncmp(const char *s1, const char *s2, size_t n) { - size_t strn = strlen(s1) + 1; - if (strn < n) { - n = strn; - } - return memcmp(s1, s2, n); -} diff --git a/platform/tintin/boot/libc/string/strcpy.c b/platform/tintin/boot/libc/string/strcpy.c deleted file mode 100644 index 4e61825e5f..0000000000 --- a/platform/tintin/boot/libc/string/strcpy.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// char *strcpy(char *s1, const char *s2); -// char *strncpy(char *s1, const char *s2, size_t n); -/////////////////////////////////////// -// Notes: -// Tuned for code size. - -#include -#include - -char *strcpy(char * restrict s1, const char * restrict s2) { - size_t n = strlen(s2) + 1; - return memcpy(s1, s2, n); -} - -char *strncpy(char * restrict s1, const char * restrict s2, size_t n) { - char *rc = s1; - size_t strn = strlen(s2) + 1; - if (strn > n) { - strn = n; - } - memcpy(s1, s2, strn); - if (n > strn) { - memset(s1 + strn, '\0', n - strn); - } - return rc; -} diff --git a/platform/tintin/boot/libc/string/strlen.c b/platform/tintin/boot/libc/string/strlen.c deleted file mode 100644 index 92f810a65f..0000000000 --- a/platform/tintin/boot/libc/string/strlen.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// size_t strlen(const char *s); -// size_t strnlen(const char *s, size_t maxlen); -/////////////////////////////////////// - -#include - -size_t strlen(const char *s) { - size_t len = 0; - while (*s++) { - len++; - } - return len; -} - -size_t strnlen(const char *s, size_t maxlen) { - size_t len = 0; - while (*s++ && maxlen--) { - len++; - } - return len; -} diff --git a/platform/tintin/boot/libc/string/strspn.c b/platform/tintin/boot/libc/string/strspn.c deleted file mode 100644 index e5e92184cf..0000000000 --- a/platform/tintin/boot/libc/string/strspn.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// size_t strcspn(const char *s1, const char *s2); -// size_t strspn(const char *s1, const char *s2); - -#include -#include - -size_t strcspn(const char *s1, const char *s2) { - size_t len = 0; - const char *p; - while (s1[len]) { - p = s2; - while (*p) { - if (s1[len] == *p++) { - return len; - } - } - len++; - } - return len; -} - -size_t strspn(const char *s1, const char *s2) { - size_t len = 0; - const char *p; - while (s1[len]) { - p = s2; - while (*p) { - if (s1[len] == *p) { - break; - } - p++; - } - if (!*p) { - return len; - } - len++; - } - return len; -} diff --git a/platform/tintin/boot/libc/string/strstr.c b/platform/tintin/boot/libc/string/strstr.c deleted file mode 100644 index c53d3307c9..0000000000 --- a/platform/tintin/boot/libc/string/strstr.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// char *strstr(const char *s1, const char *s2); -/////////////////////////////////////// -// Notes: -// Tuned for code size. - -#include -#include - -char *strstr(const char *s1, const char *s2) { - size_t len = strlen(s2); - while (*s1) { - if (strncmp(s1, s2, len) == 0) { - return (char*)s1; - } - s1++; - } - return NULL; -} diff --git a/platform/tintin/boot/libc/string/strtol.c b/platform/tintin/boot/libc/string/strtol.c deleted file mode 100644 index 645857562c..0000000000 --- a/platform/tintin/boot/libc/string/strtol.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// long int strtol(const char *nptr, char **endptr, int base); - -#include -#include -#include -#include -#include - -intmax_t strtoX_core(const char * restrict nptr, char ** restrict endptr, int base, bool do_errors, - intmax_t max, intmax_t min) { - intmax_t value = 0; - int sign = 1; - while (isspace((int)*nptr)) { - nptr++; - } - switch (*nptr) { - case '+': sign = 1; nptr++; break; - case '-': sign = -1; nptr++; break; - } - if (nptr[0] == '0' && nptr[1] == 'x' && (base == 0 || base == 16)) { - base = 16; - nptr += 2; - } else if (nptr[0] == '0' && (base == 0 || base == 8)) { - base = 8; - nptr++; - } else if (base == 0) { - base = 10; - } - // We break on '\0' anyways - for (;;) { - char ch = *nptr; - if (ch >= '0' && ch <= '9') { - ch = ch - '0'; - } else if (ch >= 'A' && ch <= 'Z') { - ch = ch - 'A' + 10; - } else if (ch >= 'a' && ch <= 'z') { - ch = ch - 'a' + 10; - } else { // This will catch '\0' - break; - } - if (ch >= base) { - break; - } - value *= base; - value += ch; - if (do_errors) { - if ((sign > 0) && (value > max)) { - value = max; - } else if ((sign < 0) && (-value < min)) { - value = -min; - } - } - nptr++; - } - if (endptr) { - *endptr = (char*)nptr; - } - return value * sign; -} - -long int strtol(const char * restrict nptr, char ** restrict endptr, int base) { - return strtoX_core(nptr, endptr, base, true, INT_MAX, INT_MIN); -} diff --git a/platform/tintin/boot/libc/stub/setlocale.c b/platform/tintin/boot/libc/stub/setlocale.c deleted file mode 100644 index e0e9dd72f4..0000000000 --- a/platform/tintin/boot/libc/stub/setlocale.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/////////////////////////////////////// -// Implements: -// char *setlocale(int category, const char *locale); -/////////////////////////////////////// -// Notes: -// Simplicity references this, but we don't want to implement it. - -#include -char *setlocale(int category, const char *locale) { - return NULL; -} diff --git a/platform/tintin/boot/libc/vsprintf.c b/platform/tintin/boot/libc/vsprintf.c deleted file mode 100644 index f8de325cd4..0000000000 --- a/platform/tintin/boot/libc/vsprintf.c +++ /dev/null @@ -1,726 +0,0 @@ -/////////////////////////////////////// -// Implements: -// int vsnprintf(char *str, size_t size, const char *format, va_list args) -// int vsprintf(char *buf, const char *fmt, va_list args) -// int vsniprintf(char *str, size_t size, const char *format, va_list args) -// int sniprintf(char *dst, size_t size, const char *fmt, ...) -// int snprintf(char *dst, size_t size, const char *fmt, ...) -// int sprintf(char *dst, const char *fmt, ...) -/////////////////////////////////////// -// Exports to apps: -// vsnprintf - -/* - * Copyright (c) 1995 Patrick Powell. - * - * This code is based on code written by Patrick Powell . - * It may be used for any purpose as long as this notice remains intact on all - * source code distributions. - - * Copyright (c) 2008 Holger Weiss. - * - * This version of the code is maintained by Holger Weiss . - * My changes to the code may freely be used, modified and/or redistributed for - * any purpose. It would be nice if additions and fixes to this file (including - * trivial code cleanups) would be sent back in order to let me include them in - * the version available at . - * However, this is not a requirement for using or redistributing (possibly - * modified) versions of this file, nor is leaving this notice intact mandatory. - */ - -/* - * History - * - * 2015-07-07 Alex Marshall - * - * More cleanup, removed vestigial quote flag support. - * - * 2015-07-06 Alex Marshall - * - * Cleaned up code style things, modified various things to be more compliant - * with C99 standard, and changed some small things for compatibility with - * newlib's sprintf implementation. - * - * 2009-03-05 Hector Martin "marcan" - * - * Hacked up and removed a lot of stuff including floating-point support, - * a bunch of ifs and defines, locales, and tests - * - * 2008-01-20 Holger Weiss for C99-snprintf 1.1: - * - * Fixed the detection of infinite floating point values on IRIX (and - * possibly other systems) and applied another few minor cleanups. - * - * 2008-01-06 Holger Weiss for C99-snprintf 1.0: - * - * Added a lot of new features, fixed many bugs, and incorporated various - * improvements done by Andrew Tridgell , Russ Allbery - * , Hrvoje Niksic , Damien Miller - * , and others for the Samba, INN, Wget, and OpenSSH - * projects. The additions include: support the "e", "E", "g", "G", and - * "F" conversion specifiers (and use conversion style "f" or "F" for the - * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", - * "t", and "z" length modifiers; support the "#" flag and the (non-C99) - * "'" flag; use localeconv(3) (if available) to get both the current - * locale's decimal point character and the separator between groups of - * digits; fix the handling of various corner cases of field width and - * precision specifications; fix various floating point conversion bugs; - * handle infinite and NaN floating point values; don't attempt to write to - * the output buffer (which may be NULL) if a size of zero was specified; - * check for integer overflow of the field width, precision, and return - * values and during the floating point conversion; use the OUTCHAR() macro - * instead of a function for better performance; provide asprintf(3) and - * vasprintf(3) functions; add new test cases. The replacement functions - * have been renamed to use an "rpl_" prefix, the function calls in the - * main project (and in this file) must be redefined accordingly for each - * replacement function which is needed (by using Autoconf or other means). - * Various other minor improvements have been applied and the coding style - * was cleaned up for consistency. - * - * 2007-07-23 Holger Weiss for Mutt 1.5.13: - * - * C99 compliant snprintf(3) and vsnprintf(3) functions return the number - * of characters that would have been written to a sufficiently sized - * buffer (excluding the '\0'). The original code simply returned the - * length of the resulting output string, so that's been fixed. - * - * 1998-03-05 Michael Elkins for Mutt 0.90.8: - * - * The original code assumed that both snprintf(3) and vsnprintf(3) were - * missing. Some systems only have snprintf(3) but not vsnprintf(3), so - * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. - * - * 1998-01-27 Thomas Roessler for Mutt 0.89i: - * - * The PGP code was using unsigned hexadecimal formats. Unfortunately, - * unsigned formats simply didn't work. - * - * 1997-10-22 Brandon Long for Mutt 0.87.1: - * - * Ok, added some minimal floating point support, which means this probably - * requires libm on most operating systems. Don't yet support the exponent - * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just - * wasn't being exercised in ways which showed it, so that's been fixed. - * Also, formatted the code to Mutt conventions, and removed dead code left - * over from the original. Also, there is now a builtin-test, run with: - * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf - * - * 1996-09-15 Brandon Long for Mutt 0.43: - * - * This was ugly. It is still ugly. I opted out of floating point - * numbers, but the formatter understands just about everything from the - * normal C string format, at least as far as I can tell from the Solaris - * 2.5 printf(3S) man page. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "pblibc_private.h" - -// Not using 64-bit division for Dialog Bluetooth. This saves *gobs* of memory. -#ifdef ARCH_NO_NATIVE_LONG_DIVIDE - #define CONVERT_VALUE_TYPE uint32_t -#else - #define CONVERT_VALUE_TYPE UINTMAX_T -#endif - -#define VA_START(ap, last) va_start(ap, last) -#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */ - -// These are the correct sizes for everything on our target platform -// Doesn't matter much for building on the target, but it makes unit tests easier. -#define ULLONG uint64_t -#define UINTMAX_T uint64_t -#define LLONG int64_t -#define INTMAX_T int64_t -#define UINTPTR_T uint32_t -#define PTRDIFF_T int32_t -#define UPTRDIFF_T uint32_t -#define SSIZE_T int32_t -#define SIZE_T uint32_t - -// amarshall: Changed this to be max for uint64, as we can't ever do uint128 anyways! -/* - * Buffer size to hold the octal string representation of UINT64_MAX without - * nul-termination ("1777777777777777777777"). - */ -#ifdef MAX_CONVERT_LENGTH -#undef MAX_CONVERT_LENGTH -#endif -#define MAX_CONVERT_LENGTH 22 - -/* Format read states. */ -#define PRINT_S_DEFAULT 0 -#define PRINT_S_FLAGS 1 -#define PRINT_S_WIDTH 2 -#define PRINT_S_DOT 3 -#define PRINT_S_PRECISION 4 -#define PRINT_S_MOD 5 -#define PRINT_S_CONV 6 - -/* Format flags. */ -#define PRINT_F_MINUS (1 << 0) -#define PRINT_F_PLUS (1 << 1) -#define PRINT_F_SPACE (1 << 2) -#define PRINT_F_NUM (1 << 3) -#define PRINT_F_ZERO (1 << 4) -#define PRINT_F_UP (1 << 6) -#define PRINT_F_UNSIGNED (1 << 7) -#define PRINT_F_TYPE_G (1 << 8) -#define PRINT_F_TYPE_E (1 << 9) - -/* Conversion flags. */ -#define PRINT_C_CHAR 1 -#define PRINT_C_SHORT 2 -#define PRINT_C_LONG 3 -#define PRINT_C_LLONG 4 -#define PRINT_C_SIZE 6 -#define PRINT_C_PTRDIFF 7 -#define PRINT_C_INTMAX 8 - -#ifndef MAX -# define MAX(x, y) ((x >= y) ? x : y) -#endif - -#ifndef CHARTOINT -# define CHARTOINT(ch) (ch - '0') -#endif - -#ifndef ISDIGIT -# define ISDIGIT(ch) isdigit(ch) -#endif - -#define OUTCHAR(str, len, size, ch) do { \ - if ((len) + 1 < size) { \ - str[len] = ch; \ - } \ - (len)++; \ -} while (0) - -static int prv_convert(CONVERT_VALUE_TYPE value, char *buf, size_t size, int base, int caps) { - const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; - size_t pos = 0; - - /* We return an unterminated buffer with the digits in reverse order. */ - do { - buf[pos++] = digits[value % base]; - value /= base; - } while (value != 0 && pos < size); - - return (int)pos; -} - -static void fmtstr(char *str, size_t *len, size_t size, const char *value, int width, int precision, - int flags) { - int padlen, strln; /* Amount to pad. */ - int noprecision = (precision == -1); - - if (value == NULL) { /* We're forgiving. */ - value = "(null)"; - } - - /* If a precision was specified, don't read the string past it. */ - for (strln = 0; value[strln] != '\0' && (noprecision || strln < precision); strln++) { - continue; - } - - if ((padlen = width - strln) < 0) { - padlen = 0; - } - if (flags & PRINT_F_MINUS) { /* Left justify. */ - padlen = -padlen; - } - - while (padlen > 0) { /* Leading spaces. */ - OUTCHAR(str, *len, size, ' '); - padlen--; - } - while (*value != '\0' && (noprecision || precision-- > 0)) { - OUTCHAR(str, *len, size, *value); - value++; - } - while (padlen < 0) { /* Trailing spaces. */ - OUTCHAR(str, *len, size, ' '); - padlen++; - } -} - -// spadj:0x44 + reg:0x24 bytes of stack -static void fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width, - int precision, int flags) { - UINTMAX_T uvalue; - char iconvert[MAX_CONVERT_LENGTH]; - char sign = '\0'; - char hexprefix = '\0'; - int spadlen = 0; /* Amount to space pad. */ - int zpadlen = 0; /* Amount to zero pad. */ - int pos; - bool noprecision = (precision == -1); - - if (flags & PRINT_F_UNSIGNED) { - uvalue = value; - } else { - uvalue = (value >= 0) ? value : -value; - if (value < 0) { - sign = '-'; - } else if (flags & PRINT_F_PLUS) { /* Do a sign. */ - sign = '+'; - } else if (flags & PRINT_F_SPACE) { - sign = ' '; - } - } - - pos = prv_convert(uvalue, iconvert, sizeof(iconvert), base, flags & PRINT_F_UP); - - // amarshall: modified to support zero precision with octal alternate form - if (flags & PRINT_F_NUM) { - /* - * C99 says: "The result is converted to an `alternative form'. - * For `o' conversion, it increases the precision, if and only - * if necessary, to force the first digit of the result to be a - * zero (if the value and precision are both 0, a single 0 is - * printed). For `x' (or `X') conversion, a nonzero result has - * `0x' (or `0X') prefixed to it." (7.19.6.1, 6) - */ - switch (base) { - case 8: - if (precision == 0 && uvalue == 0) { - precision = 1; - } else if (uvalue != 0 && precision <= pos) { - precision = pos + 1; - } - break; - case 16: - if (uvalue != 0) { - hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x'; - } - break; - } - } - - zpadlen = precision - pos; - if (zpadlen < 0) { - zpadlen = 0; - } - - spadlen = width /* Minimum field width. */ - - MAX(precision, pos) /* Number of integer digits. */ - - ((sign != 0) ? 1 : 0) /* Will we print a sign? */ - - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ - if (spadlen < 0) { - spadlen = 0; - } - - /* - * C99 says: "If the `0' and `-' flags both appear, the `0' flag is - * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a - * precision is specified, the `0' flag is ignored." (7.19.6.1, 6) - */ - if (flags & PRINT_F_MINUS) { /* Left justify. */ - spadlen = -spadlen; - } else if (flags & PRINT_F_ZERO && noprecision) { - zpadlen += spadlen; - spadlen = 0; - } - while (spadlen > 0) { /* Leading spaces. */ - OUTCHAR(str, *len, size, ' '); - spadlen--; - } - if (sign != '\0') { /* Sign. */ - OUTCHAR(str, *len, size, sign); - } - if (hexprefix != '\0') { /* A "0x" or "0X" prefix. */ - OUTCHAR(str, *len, size, '0'); - OUTCHAR(str, *len, size, hexprefix); - } - while (zpadlen > 0) { /* Leading zeros. */ - OUTCHAR(str, *len, size, '0'); - zpadlen--; - } - // amarshall: Support zero precision - if (uvalue == 0 && precision == 0) { - pos = 0; - } - while (pos > 0) { /* The actual digits. */ - pos--; - OUTCHAR(str, *len, size, iconvert[pos]); - } - while (spadlen < 0) { /* Trailing spaces. */ - OUTCHAR(str, *len, size, ' '); - spadlen++; - } -} - -// spadj:0x28 + reg:0x20 bytes of stack -int vsnprintf(char *str, size_t size, const char *format, va_list args) { - INTMAX_T value; - unsigned char cvalue; - const char *strvalue; - size_t len = 0; - int base = 0; - int cflags = 0; - int flags = 0; - int width = 0; - int precision = -1; - int state = PRINT_S_DEFAULT; - char ch = *format++; - - /* - * C99 says: "If `n' is zero, nothing is written, and `s' may be a null - * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer - * even if a size larger than zero was specified. At least NetBSD's - * snprintf(3) does the same, as well as other versions of this file. - * (Though some of these versions will write to a non-NULL buffer even - * if a size of zero was specified, which violates the standard.) - */ - if (str == NULL && size != 0) { - size = 0; - } - if (size > 0) { - str[0] = '\0'; - } - - while (ch != '\0') { - switch (state) { - case PRINT_S_DEFAULT: - if (ch == '%') { - state = PRINT_S_FLAGS; - // amarshall: moved these up here to make maintaining the state machine code easier. - base = cflags = flags = width = 0; - precision = -1; - } else { - OUTCHAR(str, len, size, ch); - } - ch = *format++; - break; - case PRINT_S_FLAGS: - switch (ch) { - case '-': - flags |= PRINT_F_MINUS; - break; - case '+': - flags |= PRINT_F_PLUS; - break; - case ' ': - flags |= PRINT_F_SPACE; - break; - case '#': - flags |= PRINT_F_NUM; - break; - case '0': - flags |= PRINT_F_ZERO; - break; - default: - state = PRINT_S_WIDTH; - break; - } - if (state != PRINT_S_WIDTH) { - ch = *format++; - } - break; - case PRINT_S_WIDTH: - if (ISDIGIT(ch)) { - ch = CHARTOINT(ch); - if (width > (INT_MAX - ch) / 10) { - // amarshall: simplified error handling - return -1; - } - width = 10 * width + ch; - ch = *format++; - } else if (ch == '*') { - /* - * C99 says: "A negative field width argument is - * taken as a `-' flag followed by a positive - * field width." (7.19.6.1, 5) - */ - if ((width = va_arg(args, int)) < 0) { - flags |= PRINT_F_MINUS; - width = -width; - } - ch = *format++; - state = PRINT_S_DOT; - } else { - state = PRINT_S_DOT; - } - break; - case PRINT_S_DOT: - if (ch == '.') { - state = PRINT_S_PRECISION; - ch = *format++; - } else { - state = PRINT_S_MOD; - } - break; - case PRINT_S_PRECISION: - if (precision == -1) { - precision = 0; - } - if (ISDIGIT(ch)) { - ch = CHARTOINT(ch); - if (precision > (INT_MAX - ch) / 10) { - // amarshall: simplified error handling - return -1; - } - precision = 10 * precision + ch; - ch = *format++; - } else if (ch == '*') { - /* - * C99 says: "A negative precision argument is - * taken as if the precision were omitted." - * (7.19.6.1, 5) - */ - if ((precision = va_arg(args, int)) < 0) { - precision = 0; // amarshall: It actually means as if you did '%.d', not '%d' - } - ch = *format++; - state = PRINT_S_MOD; - } else if (ch == '-') { // amarshall: added support for negative immediates in precision - /* - * C99 says: "A negative precision argument is - * taken as if the precision were omitted." - * (7.19.6.1, 5) - */ - precision = 0; - while (isdigit(ch = *format++)) {} - state = PRINT_S_MOD; - } else { - state = PRINT_S_MOD; - } - break; - case PRINT_S_MOD: - switch (ch) { - case 'h': - ch = *format++; - if (ch == 'h') { // hh - ch = *format++; - cflags = PRINT_C_CHAR; - } else { - cflags = PRINT_C_SHORT; - } - break; - case 'l': - ch = *format++; - if (ch == 'l') { // ll - ch = *format++; - cflags = PRINT_C_LLONG; - } else { - cflags = PRINT_C_LONG; - } - break; - case 'j': - cflags = PRINT_C_INTMAX; - ch = *format++; - break; - case 't': - cflags = PRINT_C_PTRDIFF; - ch = *format++; - break; - case 'z': - cflags = PRINT_C_SIZE; - ch = *format++; - break; - } - state = PRINT_S_CONV; - break; - case PRINT_S_CONV: - switch (ch) { - case 'd': - /* FALLTHROUGH */ - case 'i': - /* amarshall: Changed to machine-independent types - * This will not affect native builds, useful for unit tests. - */ - switch (cflags) { - case PRINT_C_CHAR: - value = (int8_t)va_arg(args, int); - break; - case PRINT_C_SHORT: - value = (int16_t)va_arg(args, int); - break; - case PRINT_C_LONG: - value = (int32_t)va_arg(args, int32_t); - break; - case PRINT_C_LLONG: - value = (LLONG)va_arg(args, LLONG); - break; - case PRINT_C_SIZE: - value = (SSIZE_T)va_arg(args, SSIZE_T); - break; - case PRINT_C_INTMAX: - value = (INTMAX_T)va_arg(args, INTMAX_T); - break; - case PRINT_C_PTRDIFF: - value = (PTRDIFF_T)va_arg(args, PTRDIFF_T); - break; - default: - value = (int32_t)va_arg(args, int); - break; - } - fmtint(str, &len, size, value, 10, width, - precision, flags); - break; - case 'X': - flags |= PRINT_F_UP; - /* FALLTHROUGH */ - case 'x': - base = 16; - /* FALLTHROUGH */ - case 'o': - if (base == 0) - base = 8; - /* FALLTHROUGH */ - case 'u': - if (base == 0) - base = 10; - flags |= PRINT_F_UNSIGNED; - /* amarshall: Changed to machine-independent types - * This will not affect native builds, useful for unit tests. - */ - switch (cflags) { - case PRINT_C_CHAR: - value = (uint8_t)va_arg(args, unsigned int); - break; - case PRINT_C_SHORT: - value = (uint16_t)va_arg(args, unsigned int); - break; - case PRINT_C_LONG: - value = (uint32_t)va_arg(args, uint32_t); - break; - case PRINT_C_LLONG: - value = (ULLONG)va_arg(args, ULLONG); - break; - case PRINT_C_SIZE: - value = (SIZE_T)va_arg(args, SIZE_T); - break; - case PRINT_C_INTMAX: - value = (UINTMAX_T)va_arg(args, UINTMAX_T); - break; - case PRINT_C_PTRDIFF: - value = (UPTRDIFF_T)va_arg(args, UPTRDIFF_T); - break; - default: - value = (uint32_t)va_arg(args, unsigned int); - break; - } - fmtint(str, &len, size, value, base, width, - precision, flags); - break; - case 'c': - while (width > 1) { // Leading spaces - OUTCHAR(str, len, size, ' '); - width--; - } - cvalue = va_arg(args, int); - OUTCHAR(str, len, size, cvalue); - break; - case 's': - strvalue = va_arg(args, char *); - fmtstr(str, &len, size, strvalue, width, - precision, flags); - break; - case 'p': - /* - * C99 says: "The value of the pointer is - * converted to a sequence of printing - * characters, in an implementation-defined - * manner." (C99: 7.19.6.1, 8) - */ - strvalue = va_arg(args, void *); - // amarshall: Changed this to act like newlib (where it's basically %#x) - flags |= PRINT_F_NUM; - flags |= PRINT_F_UNSIGNED; - fmtint(str, &len, size, - (UINTPTR_T)strvalue, 16, width, - precision, flags); - break; - case 'n': - /* amarshall: Changed to machine-independent types - * This will not affect native builds, useful for unit tests. - */ - // amarshall: Removed pointer variables to try to improve stack footprint - switch (cflags) { - case PRINT_C_CHAR: - *(int8_t*)va_arg(args, int8_t*) = len; - break; - case PRINT_C_SHORT: - *(int16_t*)va_arg(args, int16_t*) = len; - break; - case PRINT_C_LONG: - *(int32_t*)va_arg(args, int32_t*) = len; - break; - case PRINT_C_LLONG: - *(LLONG*)va_arg(args, LLONG*) = len; - break; - case PRINT_C_SIZE: - /* - * C99 says that with the "z" length - * modifier, "a following `n' conversion - * specifier applies to a pointer to a - * signed integer type corresponding to - * size_t argument." (7.19.6.1, 7) - */ - *(SSIZE_T*)va_arg(args, SSIZE_T*) = len; - break; - case PRINT_C_INTMAX: - *(INTMAX_T*)va_arg(args, INTMAX_T*) = len; - break; - case PRINT_C_PTRDIFF: - *(PTRDIFF_T*)va_arg(args, PTRDIFF_T*) = len; - break; - default: - *(int32_t*)va_arg(args, int32_t*) = len; - break; - } - break; - case '%': // Print a "%" character verbatim. - default: // amarshall: newlib's behavior is to just print the character. - OUTCHAR(str, len, size, ch); - break; - } - ch = *format++; - state = PRINT_S_DEFAULT; - break; - } - } - if (len < size) { - str[len] = '\0'; - } else if (size > 0) { - str[size - 1] = '\0'; - } - - if (len >= INT_MAX) { - return -1; - } - return (int)len; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Additional sprintf variants - -int vsprintf(char *buf, const char *fmt, va_list args) { - return vsnprintf(buf, INT_MAX, fmt, args); -} - -int snprintf(char *dst, size_t size, const char *fmt, ...) { - int ret; - va_list arg; - va_start(arg, fmt); - ret = vsnprintf(dst, size, fmt, arg); - va_end(arg); - return ret; -} - -int sprintf(char *dst, const char *fmt, ...) { - int ret; - va_list arg; - va_start(arg, fmt); - ret = vsprintf(dst, fmt, arg); - va_end(arg); - return ret; -} diff --git a/platform/tintin/boot/libc/wscript b/platform/tintin/boot/libc/wscript deleted file mode 100644 index 893cea9805..0000000000 --- a/platform/tintin/boot/libc/wscript +++ /dev/null @@ -1,15 +0,0 @@ -def build(bld): - # Collect all the files in the libc directory - source_dirs = ['string', 'math', 'stub'] - sources = sum([bld.path.ant_glob('%s/*.c' % d) for d in source_dirs], []) - sources.extend(bld.path.ant_glob('*.c')) - - # Build the libc directory using firmware environment - bld.objects(source=sources, - target='pblibc', - includes=['include', '.'], - cflags=['-fno-lto'], - export_includes='include', - use='') - -# vim:filetype=python diff --git a/platform/tintin/boot/openocd_bb2_ftdi.cfg b/platform/tintin/boot/openocd_bb2_ftdi.cfg deleted file mode 100644 index 06b30eaef9..0000000000 --- a/platform/tintin/boot/openocd_bb2_ftdi.cfg +++ /dev/null @@ -1,11 +0,0 @@ -interface ftdi -#ftdi_device_desc "Dual RS232-HS" -ftdi_vid_pid 0x0403 0x6010 0x0403 0x6011 - -# output value, direction (1 for output, 0 for input) -ftdi_layout_init 0x1848 0x185b -ftdi_layout_signal nTRST -data 0x0010 -oe 0x0010 -ftdi_layout_signal nSRST -data 0x0040 -oe 0x0040 - -# Red + Green LED (inverted output: low is on) -ftdi_layout_signal LED -ndata 0x1800 -oe 0x1800 diff --git a/platform/tintin/boot/run-tests.sh b/platform/tintin/boot/run-tests.sh deleted file mode 100755 index 4b3147f830..0000000000 --- a/platform/tintin/boot/run-tests.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit - -cd "${0%/*}" - -CLAR_DIR=`cd ../../../tools/clar; pwd` - -clar () { - local test_name=${1:?}; shift; - local test_suite=${1:?}; shift; - local test_dir="build/test/${test_name}" - - mkdir -p "${test_dir}" - - python "${CLAR_DIR}/clar.py" \ - --file="${test_suite}" \ - --clar-path="${CLAR_DIR}" \ - "${test_dir}" - - gcc -o "${test_dir}/do_test" \ - -I"${test_dir}" -Isrc \ - -Ivendor/STM32F2xx_StdPeriph_Lib_V1.1.0/Libraries/CMSIS/Include \ - -Ivendor/STM32F2xx_StdPeriph_Lib_V1.1.0/Libraries/CMSIS/Device/ST/STM32F2xx/Include \ - -Ivendor/STM32F2xx_StdPeriph_Lib_V1.1.0/Libraries/STM32F2xx_StdPeriph_Driver/inc \ - -DMICRO_FAMILY_STM32F2 \ - -ffunction-sections \ - -Wl,-dead_strip \ - "${test_dir}/clar_main.c" "${test_suite}" $@ - # If running on a platform with GNU ld, - # replace -Wl,-dead_strip with -Wl,--gc-sections - - echo "Running test ${test_suite}..." - "${test_dir}/do_test" -} - -clar system_flash_algo test/test_system_flash.c \ - src/drivers/stm32_common/system_flash.c diff --git a/platform/tintin/boot/src/board/board.h b/platform/tintin/boot/src/board/board.h deleted file mode 100644 index 3ea1f61faa..0000000000 --- a/platform/tintin/boot/src/board/board.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "display.h" - -#include "drivers/button_id.h" - -#include "stm32f2xx_gpio.h" - -#include -#include - -#define GPIO_Port_NULL ((GPIO_TypeDef *) 0) -#define GPIO_Pin_NULL ((uint16_t)0x0000) - -typedef struct { - const char* const name; ///< Name for debugging purposes. - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} ButtonConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} ButtonComConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} InputConfig; - -// Power Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const InputConfig vusb_stat; - const bool wake_on_usb_power; -} BoardConfigPower; - -// Button Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ButtonConfig buttons[NUM_BUTTONS]; - const ButtonComConfig button_com; -} BoardConfigButton; - -#include "board_definitions.h" diff --git a/platform/tintin/boot/src/board/board_bb2.h b/platform/tintin/boot/src/board/board_bb2.h deleted file mode 100644 index 62a5628433..0000000000 --- a/platform/tintin/boot/src/board/board_bb2.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button.h" -#include "util/misc.h" - -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_syscfg.h" - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_13, - }, - .wake_on_usb_power = true -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, -}; diff --git a/platform/tintin/boot/src/board/board_definitions.h b/platform/tintin/boot/src/board/board_definitions.h deleted file mode 100644 index 9c7532ac0a..0000000000 --- a/platform/tintin/boot/src/board/board_definitions.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// FIXME: PBL-21049 Fix platform abstraction and board definition scheme -#if BOARD_EV2_4 -#include "board_ev2_4.h" // shipped as Pebble 1.0 -#elif BOARD_BB2 -#include "board_bb2.h" -#elif BOARD_V1_5 -#include "board_v1_5.h" // prototypes for Pebble 1.3/Pebble 1.5 -#elif BOARD_V2_0 -#include "board_v2_0.h" // prototypes for Pebble 2.0 -#else -#error "Unknown board definition" -#endif diff --git a/platform/tintin/boot/src/board/board_ev2_4.h b/platform/tintin/boot/src/board/board_ev2_4.h deleted file mode 100644 index 26ccce94ea..0000000000 --- a/platform/tintin/boot/src/board/board_ev2_4.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button.h" -#include "util/misc.h" - -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_syscfg.h" - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_12, - }, - .wake_on_usb_power = false -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 } -}; diff --git a/platform/tintin/boot/src/board/board_v1_5.h b/platform/tintin/boot/src/board/board_v1_5.h deleted file mode 100644 index 62a5628433..0000000000 --- a/platform/tintin/boot/src/board/board_v1_5.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button.h" -#include "util/misc.h" - -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_syscfg.h" - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_13, - }, - .wake_on_usb_power = true -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, -}; diff --git a/platform/tintin/boot/src/board/board_v2_0.h b/platform/tintin/boot/src/board/board_v2_0.h deleted file mode 100644 index 62a5628433..0000000000 --- a/platform/tintin/boot/src/board/board_v2_0.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button.h" -#include "util/misc.h" - -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_syscfg.h" - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_13, - }, - .wake_on_usb_power = true -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, -}; diff --git a/platform/tintin/boot/src/board/display.h b/platform/tintin/boot/src/board/display.h deleted file mode 100644 index e03d8f793b..0000000000 --- a/platform/tintin/boot/src/board/display.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISP_COLS 144 -#define DISP_ROWS 168 -#define PBL_DISP_SHAPE_RECT diff --git a/platform/tintin/boot/src/boot_tests.c b/platform/tintin/boot/src/boot_tests.c deleted file mode 100644 index 6fc6c15050..0000000000 --- a/platform/tintin/boot/src/boot_tests.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "boot_tests.h" - -#include "board/board.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/flash.h" -#include "system/bootbits.h" -#include "system/rtc_registers.h" -#include "util/misc.h" - -#include "stm32f2xx.h" - -#include -#include -#include - -static const int STUCK_BUTTON_THRESHOLD = 5; - -bool is_button_stuck(void) { - // We store how many times each button has been pressed on previous boots in this - // rtc backup register. Every time when we boot without that button pressed that - // counter gets cleared. If the byte reaches 5, return a failure. - - uint32_t button_counter_register = RTC_ReadBackupRegister(STUCK_BUTTON_REGISTER); - uint8_t* button_counter = (uint8_t*) (&button_counter_register); - bool result = false; - - for (int button_id = 0; button_id < NUM_BUTTONS; button_id++) { - if (!button_is_pressed(button_id)) { - button_counter[button_id] = 0; - continue; - } - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_putstr("Stuck button register is invalid, clearing."); - dbgserial_print_hex(button_counter_register); - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, 0); - return false; - } - - button_counter[button_id] += 1; - - if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) { - dbgserial_print("Button id "); - dbgserial_print_hex(button_id); - dbgserial_putstr("is stuck!"); - result = true; - } - } - - if (button_counter_register != 0) { - dbgserial_print("Button was pushed on boot. Button counter: "); - dbgserial_print_hex(button_counter_register); - dbgserial_newline(); - } - - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, button_counter_register); - return result; -} - -bool is_flash_broken(void) { - return !flash_sanity_check(); -} diff --git a/platform/tintin/boot/src/boot_tests.h b/platform/tintin/boot/src/boot_tests.h deleted file mode 100644 index 1f95ffeeb7..0000000000 --- a/platform/tintin/boot/src/boot_tests.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool is_button_stuck(void); -bool is_flash_broken(void); diff --git a/platform/tintin/boot/src/drivers/button.h b/platform/tintin/boot/src/drivers/button.h deleted file mode 100644 index fae7e2dfca..0000000000 --- a/platform/tintin/boot/src/drivers/button.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "button_id.h" - -#include -#include - -void button_init(void); - -bool button_is_pressed(ButtonId id); -uint8_t button_get_state_bits(void); diff --git a/platform/tintin/boot/src/drivers/button_id.h b/platform/tintin/boot/src/drivers/button_id.h deleted file mode 100644 index 02aa1c75d8..0000000000 --- a/platform/tintin/boot/src/drivers/button_id.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @addtogroup UI -//! @{ -//! @addtogroup Clicks -//! \brief Dealing with button input -//! @{ - -//! Button ID values -//! @see \ref click_recognizer_get_button_id() -typedef enum { - //! Back button - BUTTON_ID_BACK = 0, - //! Up button - BUTTON_ID_UP, - //! Select (middle) button - BUTTON_ID_SELECT, - //! Down button - BUTTON_ID_DOWN, - //! Total number of buttons - NUM_BUTTONS -} ButtonId; - -//! @} // end addtogroup Clicks -//! @} // end addtogroup UI diff --git a/platform/tintin/boot/src/drivers/crc.h b/platform/tintin/boot/src/drivers/crc.h deleted file mode 100644 index fde63031b6..0000000000 --- a/platform/tintin/boot/src/drivers/crc.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -/* - * calculate the CRC32 for a stream of bytes. - * NOTE: not safe to call from ISR - */ -uint32_t crc_calculate_bytes(const uint8_t* data, unsigned int data_length); - -/* - * calculate the CRC32 for a stream of bytes from flash - * NOTE: not safe to call from ISR - */ -uint32_t crc_calculate_flash(uint32_t address, unsigned int num_bytes); diff --git a/platform/tintin/boot/src/drivers/dbgserial.h b/platform/tintin/boot/src/drivers/dbgserial.h deleted file mode 100644 index 5cf5d7dd02..0000000000 --- a/platform/tintin/boot/src/drivers/dbgserial.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void dbgserial_init(void); - -void dbgserial_putstr(const char* str); - -void dbgserial_newline(void); - -//! Like dbgserial_putstr, but without a terminating newline -void dbgserial_print(const char* str); - -void dbgserial_print_hex(uint32_t value); - -void dbgserial_putstr_fmt(char* buffer, unsigned int buffer_size, const char* fmt, ...) - __attribute__((format(printf, 3, 4))); diff --git a/platform/tintin/boot/src/drivers/display.h b/platform/tintin/boot/src/drivers/display.h deleted file mode 100644 index bd0028f337..0000000000 --- a/platform/tintin/boot/src/drivers/display.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -void display_init(void); - -void display_boot_splash(void); - -void display_error_code(uint32_t); - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void); - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator); diff --git a/platform/tintin/boot/src/drivers/display/resources/dead_face.xbm b/platform/tintin/boot/src/drivers/display/resources/dead_face.xbm deleted file mode 100644 index 0637edd47f..0000000000 --- a/platform/tintin/boot/src/drivers/display/resources/dead_face.xbm +++ /dev/null @@ -1,47 +0,0 @@ -#define dead_face_width 92 -#define dead_face_height 44 -static const unsigned char dead_face_bits[] = { - 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x30, 0x00, - 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, - 0x00, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0c, 0x00, - 0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, - 0x00, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, - 0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, - 0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, - 0x00, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, - 0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, - 0x00, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0c, 0x00, - 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, - 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, - 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, - 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, - 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }; diff --git a/platform/tintin/boot/src/drivers/display/resources/empty_bar.xbm b/platform/tintin/boot/src/drivers/display/resources/empty_bar.xbm deleted file mode 100644 index ace1c01445..0000000000 --- a/platform/tintin/boot/src/drivers/display/resources/empty_bar.xbm +++ /dev/null @@ -1,11 +0,0 @@ -#define empty_bar_width 96 -#define empty_bar_height 8 -static const unsigned char empty_bar_bits[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; diff --git a/platform/tintin/boot/src/drivers/display/resources/error_url.xbm b/platform/tintin/boot/src/drivers/display/resources/error_url.xbm deleted file mode 100644 index 5a1920b112..0000000000 --- a/platform/tintin/boot/src/drivers/display/resources/error_url.xbm +++ /dev/null @@ -1,20 +0,0 @@ -#define error_url_width 109 -#define error_url_height 14 -static const unsigned char error_url_bits[] = { - 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x78, 0x36, 0x9b, 0x79, 0xc0, - 0xe3, 0xd9, 0x0c, 0x8c, 0x67, 0xdb, 0x3c, 0x1b, 0xf8, 0xfd, 0x7e, 0xbf, - 0xfd, 0xe0, 0xf7, 0xfb, 0x1f, 0xc6, 0xef, 0xfb, 0x7e, 0x1f, 0x98, 0xcd, - 0x66, 0xb3, 0xcd, 0x60, 0x36, 0x9b, 0x19, 0xc6, 0x6c, 0x18, 0x66, 0x03, - 0x98, 0xfd, 0x66, 0xb3, 0xfd, 0x60, 0x30, 0x9b, 0x19, 0xc6, 0x6f, 0x18, - 0x66, 0x03, 0x98, 0xfd, 0x66, 0xb3, 0xfd, 0x60, 0x30, 0x9b, 0x19, 0xc3, - 0x6f, 0x18, 0x66, 0x03, 0x98, 0x0d, 0x66, 0xb3, 0x0d, 0x60, 0x36, 0x9b, - 0x19, 0xc3, 0x60, 0x18, 0x66, 0x03, 0xf8, 0xfd, 0x7e, 0xbf, 0xfd, 0xec, - 0xf7, 0x9b, 0x19, 0xc3, 0x6f, 0x18, 0x7e, 0x03, 0xd8, 0x78, 0x36, 0x9b, - 0x79, 0xcc, 0xe3, 0x99, 0x99, 0x81, 0x67, 0x18, 0x3c, 0x03, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }; diff --git a/platform/tintin/boot/src/drivers/display/resources/hex_digits.h b/platform/tintin/boot/src/drivers/display/resources/hex_digits.h deleted file mode 100644 index 7b86f0ef91..0000000000 --- a/platform/tintin/boot/src/drivers/display/resources/hex_digits.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// hex_digits_bits is indexed on the digit: -// ie: hex_digits_bits[0] is the bits to display 0 on -// the screen stored in xbm format -static const uint8_t hex_digits_bits[][36] = { - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x38, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, - 0x0E, 0x00, 0x0F, 0x00, 0x07, 0x00, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xF8, 0x00, 0x78, 0x00, 0xF8, 0x00, 0xC0, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xE0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0xF8, 0x00, - 0xF8, 0x00, 0xFC, 0x00, 0xEC, 0x00, 0xEE, 0x00, 0xE6, 0x00, 0xFF, 0x01, - 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, - }, - { - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, - 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC0, 0x01, 0xC0, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, - 0x07, 0x00, 0x77, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, 0x00, - 0x70, 0x00, 0x70, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0xFE, 0x00, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x01, 0xDC, 0x01, 0xC0, 0x01, - 0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xF0, 0x01, 0xFC, 0x01, 0xCE, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01, - }, - { - 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0xE7, 0x00, 0xF7, 0x01, - 0xFF, 0x01, 0xCF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xCF, 0x01, 0xFF, 0x01, 0xF7, 0x01, 0xE7, 0x00, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, - 0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xCE, 0x01, 0xDF, 0x01, - 0xFF, 0x01, 0xE7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, - 0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01, - }, - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00, - 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFF, 0x01, - 0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00, - }, - { - 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, - 0xFE, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, - } -}; diff --git a/platform/tintin/boot/src/drivers/display/resources/pebble_logo.xbm b/platform/tintin/boot/src/drivers/display/resources/pebble_logo.xbm deleted file mode 100644 index 95353dd5a1..0000000000 --- a/platform/tintin/boot/src/drivers/display/resources/pebble_logo.xbm +++ /dev/null @@ -1,35 +0,0 @@ -#define pebble_logo_width 105 -#define pebble_logo_height 27 -static const unsigned char pebble_logo_bits[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, - 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, - 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0xE0, - 0x03, 0x00, 0x1F, 0x60, 0xF8, 0x00, 0xC3, 0x07, 0x18, 0xC0, 0x07, 0x00, - 0x00, 0xF8, 0x0F, 0xC0, 0x7F, 0x60, 0xFE, 0x03, 0xF3, 0x1F, 0x18, 0xF0, - 0x1F, 0x00, 0x00, 0x0C, 0x18, 0x60, 0xC0, 0x60, 0x03, 0x06, 0x1B, 0x30, - 0x18, 0x18, 0x30, 0x00, 0x00, 0x06, 0x30, 0x30, 0x80, 0xE1, 0x01, 0x0C, - 0x0F, 0x60, 0x18, 0x0C, 0x60, 0x00, 0x00, 0x03, 0x60, 0x18, 0x00, 0xE3, - 0x00, 0x18, 0x07, 0xC0, 0x18, 0x06, 0xC0, 0x00, 0x80, 0x01, 0x40, 0x0C, - 0x00, 0x62, 0x00, 0x10, 0x03, 0x80, 0x18, 0x03, 0x80, 0x00, 0x80, 0x01, - 0xC0, 0x0C, 0x00, 0x66, 0x00, 0x30, 0x03, 0x80, 0x19, 0x03, 0x80, 0x01, - 0x80, 0x01, 0xC0, 0xFC, 0xFF, 0x67, 0x00, 0x30, 0x03, 0x80, 0x19, 0xFF, - 0xFF, 0x01, 0x80, 0x01, 0xC0, 0xFC, 0xFF, 0x63, 0x00, 0x30, 0x03, 0x80, - 0x19, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0xC0, 0x0C, 0x00, 0x60, 0x00, 0x30, - 0x03, 0x80, 0x19, 0x03, 0x00, 0x00, 0x80, 0x01, 0xC0, 0x0C, 0x00, 0x60, - 0x00, 0x30, 0x03, 0x80, 0x19, 0x03, 0x00, 0x00, 0x80, 0x01, 0x40, 0x0C, - 0x00, 0x62, 0x00, 0x10, 0x03, 0x80, 0x18, 0x03, 0x80, 0x00, 0x80, 0x03, - 0x60, 0x18, 0x00, 0xC3, 0x00, 0x18, 0x06, 0xC0, 0x18, 0x06, 0xC0, 0x00, - 0x80, 0x07, 0x30, 0x30, 0x80, 0x81, 0x01, 0x0C, 0x0C, 0x60, 0x18, 0x0C, - 0x60, 0x00, 0x80, 0x0D, 0x18, 0x60, 0xC0, 0x00, 0x03, 0x06, 0x18, 0x30, - 0x38, 0x18, 0x30, 0x00, 0x80, 0xF9, 0x0F, 0xC0, 0x7F, 0x00, 0xFE, 0x03, - 0xF0, 0x1F, 0x70, 0xF0, 0x1F, 0x00, 0x80, 0xE1, 0x03, 0x00, 0x1F, 0x00, - 0xF8, 0x00, 0xC0, 0x07, 0x60, 0xC0, 0x07, 0x00, 0x80, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; diff --git a/platform/tintin/boot/src/drivers/display/sharp_ls013b7dh01.c b/platform/tintin/boot/src/drivers/display/sharp_ls013b7dh01.c deleted file mode 100644 index 684827fe5c..0000000000 --- a/platform/tintin/boot/src/drivers/display/sharp_ls013b7dh01.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/display.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" -#include "drivers/dbgserial.h" -#include "util/attributes.h" -#include "util/delay.h" - -#include "stm32f2xx_dma.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_spi.h" - -#include -#include - -// Bootloader images -#include "drivers/display/resources/hex_digits.h" -#include "drivers/display/resources/dead_face.xbm" -#include "drivers/display/resources/empty_bar.xbm" -#include "drivers/display/resources/error_url.xbm" -#include "drivers/display/resources/pebble_logo.xbm" - -#define DISP_LINE_BYTES (DISP_COLS / 8) -#define DISP_LINE_WORDS (((DISP_COLS - 1) / 32) + 1) - -// GPIO constants -#define DISP_SPI (SPI2) -#define DISP_GPIO (GPIOB) -#define PWR_CTL_GPIO (GPIOC) -#define PWR_CTL_PIN (GPIO_Pin_5) -#define DISPLAY_SPI_CLOCK (RCC_APB1Periph_SPI2) -#define DISP_PIN_VCOM (GPIO_Pin_1) -#define DISP_PINSOURCE_VCOM (GPIO_PinSource1) -#define DISP_PIN_SCS (GPIO_Pin_12) -#define DISP_PIN_SCLK (GPIO_Pin_13) -#define DISP_PIN_LCD (GPIO_Pin_14) -#define DISP_PIN_SI (GPIO_Pin_15) -#define DISP_MODE_STATIC (0x00) -#define DISP_MODE_WRITE (0x80) -#define DISP_MODE_CLEAR (0x20) - -// The bootloader leaves SYSCLK at defaults (connected to HSI at 16 Mhz), -// and there are no prescalers on any of the buses. Since the display -// can handle a max of 2 Mhz, we want to divide by 8 -#define DISPLAY_PERIPH_PRESCALER (SPI_BaudRatePrescaler_8) - -static void prv_enable_display_spi_clock() { - periph_config_enable(RCC_APB1PeriphClockCmd, DISPLAY_SPI_CLOCK); -} - -static void prv_disable_display_spi_clock() { - periph_config_disable(RCC_APB1PeriphClockCmd, DISPLAY_SPI_CLOCK); -} - -static void prv_enable_chip_select(void) { - gpio_use(DISP_GPIO); - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); - // required setup time > 3us - delay_us(7); - gpio_release(DISP_GPIO); -} - -static void prv_disable_chip_select(void) { - gpio_use(DISP_GPIO); - // delay while last byte is emitted by the SPI peripheral - delay_us(7); - - GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); - // hold time > 1us - // produce a delay 4ms - delay_us(4); - gpio_release(DISP_GPIO); -} - -//! These functions needed to be called around any commands that -//! are sent to the display. NOINLINE only for code size savings. -static NOINLINE void prv_enable_display_access(void) { - prv_enable_display_spi_clock(); - prv_enable_chip_select(); -} - -static NOINLINE void prv_disable_display_access(void) { - prv_disable_chip_select(); - prv_disable_display_spi_clock(); -} - -//! Write a single byte synchronously to the display. This is the only practical -//! way to write to the display in the bootloader since we don't have interrupts. -static void prv_display_write_byte(uint8_t d) { - // Block until the tx buffer is empty - SPI_I2S_SendData(DISP_SPI, d); - while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) {} -} - -// Since all these values are constant we can save code space -// by storing the initialized struct in memory rather than -// needing to copy in each value -static GPIO_InitTypeDef s_disp_gpio_init = { - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = GPIO_Speed_50MHz, - .GPIO_Pin = DISP_PIN_SCLK | DISP_PIN_SI -}; - -static SPI_InitTypeDef s_disp_spi_init = { - .SPI_Direction = SPI_Direction_1Line_Tx, // Write-only SPI - .SPI_Mode = SPI_Mode_Master, - .SPI_DataSize = SPI_DataSize_8b, - .SPI_CPOL = SPI_CPOL_Low, - .SPI_CPHA = SPI_CPHA_1Edge, - .SPI_NSS = SPI_NSS_Soft, - // We want the SPI clock to run at 2MHz - .SPI_BaudRatePrescaler = DISPLAY_PERIPH_PRESCALER, - // MSB order allows us to write pixels out without reversing bytes, but command bytes - // have to be reversed - .SPI_FirstBit = SPI_FirstBit_MSB, - .SPI_CRCPolynomial = 7 // default -}; - -//! Setup TIM3 to pulse VCOM every second to avoid damage to the display -void prv_setup_pulse_vcom(void) { - periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_TIM3); - - // Standard peripheral functions are too big so toggle the registers manually: - // Scale timer to roll over once per second - TIM3->CR1 = TIM_CounterMode_Up | TIM_CKD_DIV1 | TIM_CR1_ARPE; - - // Prescaler divides by PSC + 1, which gives us 64000 ticks per second - // in the timer. This prescaler is the smallest divisor that still allows the - // timer to roll over exactly once per second (since the timer is 16 bits) - TIM3->PSC = 249; - - // The timer reloads one cycle after ARR is reached, so ARR of - // 63999 means the timer will roll over once per second - TIM3->ARR = 63999; - - // Enable the preload register and put us in PWM mode 2 - TIM3->CCMR2 = TIM_CCMR2_OC4PE | (TIM_OCMode_PWM2 << 8); - - // The timer forces the pin high when the counter is greater than - // or equal to this value. Since ARR is 63999, this means the - // pin will be high for exactly one tick of the timer (~16 us) - TIM3->CCR4 = 63999; - - // Enable channel 4 - TIM3->CCER = TIM_CCER_CC4E; - - // Hook up the VCOM pin's alternate function to TIM3 - GPIO_PinAFConfig(DISP_GPIO, DISP_PINSOURCE_VCOM, GPIO_AF_TIM3); - - TIM3->EGR = TIM_PSCReloadMode_Immediate; // Reload shadow registers - TIM_Cmd(TIM3, ENABLE); -} - -static void prv_display_start(void) { - // Enable the GPIO{B,C} clocks; this is required before configuring the pins - gpio_use(DISP_GPIO); - gpio_use(PWR_CTL_GPIO); - - // Connect PB13 to SPI2_SCK - GPIO_PinAFConfig(DISP_GPIO, GPIO_PinSource13, GPIO_AF_SPI2); - - // Connect PB15 to SPI2_MOSI - GPIO_PinAFConfig(DISP_GPIO, GPIO_PinSource15, GPIO_AF_SPI2); - - GPIO_Init(DISP_GPIO, &s_disp_gpio_init); - - s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT; - - s_disp_gpio_init.GPIO_OType = GPIO_OType_OD; - - s_disp_gpio_init.GPIO_Pin = PWR_CTL_PIN; - GPIO_Init(PWR_CTL_GPIO, &s_disp_gpio_init); - - s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT; - s_disp_gpio_init.GPIO_OType = GPIO_OType_PP; - s_disp_gpio_init.GPIO_Pin = DISP_PIN_SCS; - GPIO_Init(DISP_GPIO, &s_disp_gpio_init); - - s_disp_gpio_init.GPIO_Mode = GPIO_Mode_AF; - s_disp_gpio_init.GPIO_Pin = DISP_PIN_VCOM; - GPIO_Init(DISP_GPIO, &s_disp_gpio_init); - - s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT; - s_disp_gpio_init.GPIO_OType = GPIO_OType_OD; - s_disp_gpio_init.GPIO_Pin = DISP_PIN_LCD; - GPIO_Init(DISP_GPIO, &s_disp_gpio_init); - - // Set up a SPI bus on SPI2 - SPI_I2S_DeInit(DISP_SPI); - SPI_Init(DISP_SPI, &s_disp_spi_init); - - SPI_Cmd(DISP_SPI, ENABLE); - - // +5V to 5V_EN pin - GPIO_WriteBit(PWR_CTL_GPIO, PWR_CTL_PIN, Bit_RESET); - - // +5V to LCD pin (Set this pin low to turn off the display) - GPIO_WriteBit(DISP_GPIO, DISP_PIN_LCD, Bit_SET); - - prv_setup_pulse_vcom(); - - // Don't need the GPIO peripheral clocks to be enabled anymore - gpio_release(PWR_CTL_GPIO); - gpio_release(DISP_GPIO); -} - -// Clear-all mode is entered by sending 0x04 to the panel -void display_clear(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_CLEAR); - prv_display_write_byte(0x00); - - prv_disable_display_access(); -} - -//! Static mode is entered by sending 0x00 to the panel -//! This stops any further updates being registered by -//! the display, preventing corruption on shutdown / boot -static void prv_display_enter_static(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_STATIC); - prv_display_write_byte(0x00); - prv_display_write_byte(0x00); - - prv_disable_display_access(); -} - -// Helper to reverse command bytes -static uint8_t prv_reverse_bits(uint8_t input) { - uint8_t result; - __asm__ ("rev %[result], %[input]\n\t" - "rbit %[result], %[result]" - : [result] "=r" (result) - : [input] "r" (input)); - return result; -} - -static void prv_display_start_write(void) { - prv_enable_display_access(); - - prv_display_write_byte(DISP_MODE_WRITE); -} - -static void prv_display_write_line(uint8_t line_addr, const uint8_t *line) { - // 1-indexed (ugh) 8bit line address (1-168) - prv_display_write_byte(prv_reverse_bits(168 - line_addr)); - - for (int i = DISP_LINE_BYTES - 1; i >= 0; --i) { - prv_display_write_byte(line[i]); - } - - prv_display_write_byte(0x00); -} - -static void prv_display_end_write(void) { - prv_display_write_byte(0x00); - - - prv_disable_display_access(); -} - -// Round a bit offset to a byte offset -static unsigned prv_round_to_byte(unsigned x) { - return (x + 7) >> 3; -} - -// Draw bitmap onto buffer. -static void prv_draw_bitmap(const uint8_t *bitmap, unsigned x_offset, unsigned y_offset, - unsigned width, unsigned height, - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - // Need to convert offsets to bytes for the horizontal dimensions - x_offset = prv_round_to_byte(x_offset); - width = prv_round_to_byte(width); - - for (unsigned i = 0; i < height; i++) { - memcpy(buffer[i + y_offset] + x_offset, bitmap + i * width, width); - } -} - -static void prv_display_buffer(uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - prv_display_start_write(); - for (int i = 0; i < DISP_ROWS; i++) { - prv_display_write_line(i, buffer[i]); - } - prv_display_end_write(); -} - -void display_boot_splash(void) { - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - // Draw black - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(pebble_logo_bits, 16, 64, pebble_logo_width, pebble_logo_height, buffer); - - prv_display_buffer(buffer); -} - -static void prv_set_bit(uint8_t x, uint8_t y, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - buffer[y][x / 8] |= (1 << (x % 8)); -} - -static void prv_render_char(unsigned digit, uint8_t x_offset_bits, uint8_t y_offset, - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - const unsigned char_rows = 18, char_cols = 9; - const uint8_t * char_data = hex_digits_bits[digit]; - - // Each character requires 2 bytes of storage - for (unsigned y = 0; y < char_rows; y++) { - unsigned cur_y = y_offset + y; - uint8_t first_byte = char_data[2 * y]; - - for (unsigned x = 0; x < char_cols; x++) { - bool pixel; - if (x < 8) { // Pixel is in first byte - pixel = first_byte & (1 << x); - } - else { // Last pixel is in second byte - pixel = char_data[2 * y + 1] & 1; - } - - // The buffer is already all black, so just set the white pixels - if (pixel) { - prv_set_bit(x_offset_bits + x, cur_y, buffer); - } - } - } -} - -static void prv_draw_code(uint32_t code, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) { - const unsigned y_offset = 116; // beneath sad face, above url - unsigned x_offset = 28; // Aligned with sad face - - // Extract and print digits - for (int i = 7; i >= 0; i--) { - // Mask off 4 bits at a time - uint32_t mask = (0xf << (i * 4)); - unsigned digit = ((code & mask) >> (i * 4)); - prv_render_char(digit, x_offset, y_offset, buffer); - - // Each character is 9px wide plus 2px of padding - x_offset += 11; - } -} - -void display_error_code(uint32_t code) { - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(dead_face_bits, 24, 32, dead_face_width, dead_face_height, buffer); - - prv_draw_code(code, buffer); - - prv_draw_bitmap(error_url_bits, 16, 144, error_url_width, error_url_height, buffer); - - prv_display_buffer(buffer); -} - -//! Do whatever is necessary to prevent visual artifacts when resetting -//! the watch. -void display_prepare_for_reset(void) { - prv_display_enter_static(); -} - -//! Display the progress of a firmware update. -//! -//! The progress is expressed as a rational number less than or equal to 1. -//! When numerator == denominator, the progress indicator shows that the update -//! is complete. -void display_firmware_update_progress(uint32_t numerator, uint32_t denominator) { - // Dimensions for progress bar - const unsigned x_offset = 24, y_offset = 106, - inner_bar_width = 94, inner_bar_height = 6; - - static unsigned s_prev_num_pixels = -1; - // Calculate number of pixels to fill in - unsigned num_pixels = inner_bar_width * numerator / denominator; - - if (num_pixels == s_prev_num_pixels) { - return; - } - s_prev_num_pixels = num_pixels; - - uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]; - memset(buffer, 0x00, sizeof(buffer)); - - prv_draw_bitmap(pebble_logo_bits, 16, 64, pebble_logo_width, pebble_logo_height, buffer); - - - prv_draw_bitmap(empty_bar_bits, x_offset, y_offset, empty_bar_width, empty_bar_height, buffer); - - for (unsigned y = 0; y < inner_bar_height; y++) { - for (unsigned x = 0; x < num_pixels; x++) { - // Add 1 to offsets so we don't write into outer box - prv_set_bit(x + x_offset + 1, y_offset + y + 1, buffer); - } - } - - prv_display_buffer(buffer); -} - -void display_init(void) { - prv_enable_display_spi_clock(); - prv_display_start(); - prv_disable_display_spi_clock(); -} diff --git a/platform/tintin/boot/src/drivers/flash.h b/platform/tintin/boot/src/drivers/flash.h deleted file mode 100644 index 13d2a16dc6..0000000000 --- a/platform/tintin/boot/src/drivers/flash.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Configure the micro's peripherals to communicate with the flash chip -void flash_init(void); - -//! Read 1 or more bytes starting at the specified 24bit address into -//! the provided buffer. This function does no range checking, so it is -//! currently possible to run off the end of the flash. -//! -//! @param buffer A byte-buffer that will be used to store the data -//! read from flash. -//! @param start_addr The address of the first byte to be read from flash. -//! @param buffer_size The total number of bytes to be read from flash. -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size); - -//! Check if we can talk to the flash. -//! @return true if the CFI table can be queried. -bool flash_sanity_check(void); diff --git a/platform/tintin/boot/src/drivers/flash/n25q.c b/platform/tintin/boot/src/drivers/flash/n25q.c deleted file mode 100644 index d286862dff..0000000000 --- a/platform/tintin/boot/src/drivers/flash/n25q.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "drivers/flash.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "stm32f2xx_gpio.h" -#include "util/delay.h" - -static const uint32_t EXPECTED_SPI_FLASH_ID_32MBIT = 0x20bb16; -static const uint32_t EXPECTED_SPI_FLASH_ID_64MBIT = 0x20bb17; - -// Serial-flash commands -static const uint8_t FLASH_CMD_WRITE_ENABLE = 0x06; -static const uint8_t FLASH_CMD_WRITE_DISABLE = 0x04; -static const uint8_t FLASH_CMD_READ_STATUS_REG = 0x05; -static const uint8_t FLASH_CMD_READ = 0x03; -static const uint8_t FLASH_CMD_READ_ID = 0x9F; -static const uint8_t FLASH_CMD_DEEP_SLEEP = 0xB9; -static const uint8_t FLASH_CMD_WAKE = 0xAB; -static const uint8_t FLASH_CMD_DUMMY = 0xA9; - -static const struct { - SPI_TypeDef *const spi; - GPIO_TypeDef *const spi_gpio; - uint8_t scs_pin, sclk_pin, miso_pin, mosi_pin; -} FLASH_CONFIG = { - .spi = SPI1, - .spi_gpio = GPIOA, - .scs_pin = 4, - .sclk_pin = 5, - .miso_pin = 6, - .mosi_pin = 7, -}; - -static void prv_enable_flash_spi_clock(void) { - RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); -} - -static void prv_disable_flash_spi_clock(void) { - RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE); -} - -static void prv_flash_start(void) { - gpio_use(FLASH_CONFIG.spi_gpio); - - // Enable the GPIOA clock - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - - uint8_t altfunc = GPIO_AF_SPI1; - - // Connect pins to their SPI functionality - GPIO_PinAFConfig(FLASH_CONFIG.spi_gpio, FLASH_CONFIG.sclk_pin, altfunc); - GPIO_PinAFConfig(FLASH_CONFIG.spi_gpio, FLASH_CONFIG.miso_pin, altfunc); - GPIO_PinAFConfig(FLASH_CONFIG.spi_gpio, FLASH_CONFIG.mosi_pin, altfunc); - - // Setup MISO/MOSI - GPIO_InitTypeDef gpio_cfg; - gpio_cfg.GPIO_OType = GPIO_OType_PP; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Mode = GPIO_Mode_AF; - gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz; - gpio_cfg.GPIO_Pin = (1 << FLASH_CONFIG.miso_pin) | (1 << FLASH_CONFIG.mosi_pin); - GPIO_Init(FLASH_CONFIG.spi_gpio, &gpio_cfg); - - // Configure the SCLK pin to have a weak pull-down to put it in a known state - // when SCS is toggled - gpio_cfg.GPIO_PuPd = GPIO_PuPd_DOWN; - gpio_cfg.GPIO_Pin = (1 << FLASH_CONFIG.sclk_pin); - GPIO_Init(FLASH_CONFIG.spi_gpio, &gpio_cfg); - - // Configure SCS to be controlled in software; pull up to high when inactive - gpio_cfg.GPIO_Mode = GPIO_Mode_OUT; - gpio_cfg.GPIO_Pin = (1 << FLASH_CONFIG.scs_pin); - gpio_cfg.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(FLASH_CONFIG.spi_gpio, &gpio_cfg); - - // Set up a SPI bus on SPI1 - SPI_InitTypeDef spi_cfg; - SPI_I2S_DeInit(FLASH_CONFIG.spi); - spi_cfg.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - spi_cfg.SPI_Mode = SPI_Mode_Master; - spi_cfg.SPI_DataSize = SPI_DataSize_8b; - spi_cfg.SPI_CPOL = SPI_CPOL_Low; - spi_cfg.SPI_CPHA = SPI_CPHA_1Edge; - spi_cfg.SPI_NSS = SPI_NSS_Soft; - // APB2 is at 16MHz, max is 54MHz, so we want the smallest prescaler - spi_cfg.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; - spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB; - spi_cfg.SPI_CRCPolynomial = 7; - SPI_Init(FLASH_CONFIG.spi, &spi_cfg); - - SPI_Cmd(FLASH_CONFIG.spi, ENABLE); - - gpio_release(FLASH_CONFIG.spi_gpio); -} - -static void prv_flash_start_cmd(void) { - gpio_use(FLASH_CONFIG.spi_gpio); - GPIO_ResetBits(FLASH_CONFIG.spi_gpio, 1 << FLASH_CONFIG.scs_pin); - gpio_release(FLASH_CONFIG.spi_gpio); -} - -static void prv_flash_end_cmd(void) { - gpio_use(FLASH_CONFIG.spi_gpio); - GPIO_SetBits(FLASH_CONFIG.spi_gpio, 1 << FLASH_CONFIG.scs_pin); - gpio_release(FLASH_CONFIG.spi_gpio); - - // 50ns required between SCS going high and low again, so just delay here to be safe - delay_us(1); -} - -static uint8_t prv_flash_send_and_receive_byte(uint8_t byte) { - // Ensure that there are no other write operations in progress - while (SPI_I2S_GetFlagStatus(FLASH_CONFIG.spi, SPI_I2S_FLAG_TXE) == RESET) {} - // Send the byte on the SPI bus - SPI_I2S_SendData(FLASH_CONFIG.spi, byte); - - // Wait for the response byte to be received - while (SPI_I2S_GetFlagStatus(FLASH_CONFIG.spi, SPI_I2S_FLAG_RXNE) == RESET) {} - // Return the byte - return SPI_I2S_ReceiveData(FLASH_CONFIG.spi); -} - -static void prv_flash_send_24b_address(uint32_t start_addr) { - // Ensure the high bits are not set. - prv_flash_send_and_receive_byte((start_addr & 0xFF0000) >> 16); - prv_flash_send_and_receive_byte((start_addr & 0x00FF00) >> 8); - prv_flash_send_and_receive_byte((start_addr & 0x0000FF)); -} - -static uint8_t prv_flash_read_next_byte(void) { - uint8_t result = prv_flash_send_and_receive_byte(FLASH_CMD_DUMMY); - return result; -} - -static void prv_flash_wait_for_write_bounded(volatile int cycles_to_wait) { - prv_flash_start_cmd(); - - prv_flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG); - - uint8_t status_register = 0; - do { - if (cycles_to_wait-- < 1) { - break; - } - status_register = prv_flash_read_next_byte(); - } while (status_register & 0x1); - - prv_flash_end_cmd(); -} - -static void prv_flash_wait_for_write(void) { - prv_flash_start_cmd(); - - prv_flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG); - - uint8_t status_register = 0; - do { - status_register = prv_flash_read_next_byte(); - } while (status_register & 0x1); - - prv_flash_end_cmd(); -} - -static void prv_flash_deep_sleep_exit(void) { - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_WAKE); - prv_flash_end_cmd(); - - // wait a sufficient amount of time to enter standby mode - // It appears violating these timing conditions can lead to - // random bit corruptions on flash writes! - delay_us(100); -} - -static uint32_t prv_flash_whoami(void) { - prv_enable_flash_spi_clock(); - - prv_flash_wait_for_write_bounded(64000000); - - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_READ_ID); - uint32_t manufacturer = prv_flash_read_next_byte(); - uint32_t type = prv_flash_read_next_byte(); - uint32_t capacity = prv_flash_read_next_byte(); - prv_flash_end_cmd(); - - prv_disable_flash_spi_clock(); - - return ((manufacturer << 16) | (type << 8) | capacity); -} - -static bool prv_check_whoami(uint32_t spi_flash_id) { - return spi_flash_id == EXPECTED_SPI_FLASH_ID_32MBIT || - spi_flash_id == EXPECTED_SPI_FLASH_ID_64MBIT; -} - -static bool prv_is_whoami_correct(void) { - uint32_t spi_flash_id = prv_flash_whoami(); - return prv_check_whoami(spi_flash_id); -} - -void flash_init(void) { - prv_enable_flash_spi_clock(); - - prv_flash_start(); - - // Assume that last time we shut down we were asleep. Come back out. - prv_flash_deep_sleep_exit(); - - prv_disable_flash_spi_clock(); - - prv_flash_whoami(); -} - -bool flash_sanity_check(void) { - return prv_is_whoami_correct(); -} - -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { - if (!buffer_size) { - return; - } - - prv_enable_flash_spi_clock(); - prv_flash_wait_for_write(); - - prv_flash_start_cmd(); - - prv_flash_send_and_receive_byte(FLASH_CMD_READ); - prv_flash_send_24b_address(start_addr); - - while (buffer_size--) { - *buffer = prv_flash_read_next_byte(); - buffer++; - } - - prv_flash_end_cmd(); - - prv_disable_flash_spi_clock(); -} diff --git a/platform/tintin/boot/src/drivers/gpio.h b/platform/tintin/boot/src/drivers/gpio.h deleted file mode 100644 index de839ba042..0000000000 --- a/platform/tintin/boot/src/drivers/gpio.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "stm32f2xx_gpio.h" - -#include "board/board.h" - -void gpio_use(GPIO_TypeDef* GPIOx); -void gpio_release(GPIO_TypeDef* GPIOx); diff --git a/platform/tintin/boot/src/drivers/otp.h b/platform/tintin/boot/src/drivers/otp.h deleted file mode 100644 index e760b7e0b6..0000000000 --- a/platform/tintin/boot/src/drivers/otp.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -enum { - // ML/FL / 1.0 and later: - OTP_SERIAL1 = 0, - OTP_HWVER = 1, - OTP_PCBA_SERIAL1 = 2, - // Quanta / HW 1.3 and later: - OTP_SERIAL2 = 3, - OTP_SERIAL3 = 4, - OTP_SERIAL4 = 5, - OTP_SERIAL5 = 6, - OTP_PCBA_SERIAL2 = 7, - OTP_PCBA_SERIAL3 = 8, - - NUM_OTP_SLOTS = 16, -}; - -uint8_t * otp_get_lock(const uint8_t index); -bool otp_is_locked(const uint8_t index); - -char * otp_get_slot(const uint8_t index); diff --git a/platform/tintin/boot/src/drivers/periph_config.h b/platform/tintin/boot/src/drivers/periph_config.h deleted file mode 100644 index 18f0732ea4..0000000000 --- a/platform/tintin/boot/src/drivers/periph_config.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "stm32f2xx.h" - -typedef void (*ClockCmd)(uint32_t periph, FunctionalState state); - -static inline void periph_config_enable(ClockCmd clock_cmd, uint32_t periph) { - clock_cmd(periph, ENABLE); -} - -static inline void periph_config_disable(ClockCmd clock_cmd, uint32_t periph) { - clock_cmd(periph, DISABLE); -} diff --git a/platform/tintin/boot/src/drivers/rtc.h b/platform/tintin/boot/src/drivers/rtc.h deleted file mode 100644 index dcdc331353..0000000000 --- a/platform/tintin/boot/src/drivers/rtc.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Initialize the RTC with LSE as the clocksource -//! @return false if LSE init failed -bool rtc_init(void); - -//! Set the RTC to run in fast mode -void rtc_initialize_fast_mode(void); - -//! Slow down the RTC so we can keep time in standby mode -void rtc_slow_down(void); - -//! Speed up the RTC for the firmware -void rtc_speed_up(void); diff --git a/platform/tintin/boot/src/drivers/stm32_common/button.c b/platform/tintin/boot/src/drivers/stm32_common/button.c deleted file mode 100644 index 7e32ca17ac..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/button.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/button.h" - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" - -static void initialize_button_common(void) { - if (!BOARD_CONFIG_BUTTON.button_com.gpio) { - // This board doesn't use a button common pin. - return; - } - - // Configure BUTTON_COM to drive low. When the button - // is pressed this pin will be connected to the pin for the - // button. - gpio_use(BOARD_CONFIG_BUTTON.button_com.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_BUTTON.button_com.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(BOARD_CONFIG_BUTTON.button_com.gpio, &GPIO_InitStructure); - GPIO_WriteBit(BOARD_CONFIG_BUTTON.button_com.gpio, BOARD_CONFIG_BUTTON.button_com.gpio_pin, 0); - - gpio_release(BOARD_CONFIG_BUTTON.button_com.gpio); -} - -static void initialize_button(const ButtonConfig* config) { - // Configure the pin itself - gpio_use(config->gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_InitStructure.GPIO_Pin = config->gpio_pin; - GPIO_Init(config->gpio, &GPIO_InitStructure); - - gpio_release(config->gpio); -} - -bool button_is_pressed(ButtonId id) { - const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id]; - gpio_use(button_config->gpio); - uint8_t bit = GPIO_ReadInputDataBit(button_config->gpio, button_config->gpio_pin); - gpio_release(button_config->gpio); - return !bit; -} - -uint8_t button_get_state_bits(void) { - uint8_t button_state = 0x00; - for (int i = 0; i < NUM_BUTTONS; ++i) { - button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i; - } - return button_state; -} - -void button_init(void) { - // Need to disable button wakeup functionality - // or the buttons don't register input - PWR_WakeUpPinCmd(DISABLE); - - periph_config_enable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG); - - initialize_button_common(); - for (int i = 0; i < NUM_BUTTONS; ++i) { - initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]); - } - - periph_config_disable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG); -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/crc.c b/platform/tintin/boot/src/drivers/stm32_common/crc.c deleted file mode 100644 index 2c449f770a..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/crc.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/crc.h" - -#include "drivers/flash.h" -#include "drivers/periph_config.h" - -#include "stm32f2xx_crc.h" -#include "stm32f2xx_rcc.h" - -#include - -static void prv_enable_crc_clock(void) { - periph_config_enable(RCC_AHB1PeriphClockCmd, RCC_AHB1Periph_CRC); -} - -static void prv_disable_crc_clock(void) { - periph_config_disable(RCC_AHB1PeriphClockCmd, RCC_AHB1Periph_CRC); -} - -static void prv_calculate_incremental_start(void) { - prv_enable_crc_clock(); - CRC_ResetDR(); -} - -static void prv_calculate_incremental_words(const uint32_t* data, unsigned int data_length) { - CRC_CalcBlockCRC((uint32_t*) data, data_length); -} - -static uint32_t prv_calculate_incremental_remaining_bytes(const uint8_t* data, - unsigned int data_length) { - uint32_t crc_value; - - if (data_length >= 4) { - const unsigned int num_words = data_length / 4; - prv_calculate_incremental_words((uint32_t*) data, num_words); - - data += num_words * 4; - data_length -= num_words * 4; - } - - if (data_length) { - uint32_t last_word = 0; - for (unsigned int i = 0; i < data_length; ++i) { - last_word = (last_word << 8) | data[i]; - } - crc_value = CRC_CalcCRC(last_word); - } else { - crc_value = CRC_GetCRC(); - } - - return crc_value; -} - -static void prv_calculate_incremental_stop(void) { - prv_disable_crc_clock(); -} - -uint32_t crc_calculate_bytes(const uint8_t* data, unsigned int data_length) { - prv_calculate_incremental_start(); - - // First calculate the CRC of the whole words, since the hardware works 4 - // bytes at a time. - uint32_t* data_words = (uint32_t*) data; - const unsigned int num_words = data_length / 4; - prv_calculate_incremental_words(data_words, num_words); - - const unsigned int num_remaining_bytes = data_length % 4; - const uint32_t res = - prv_calculate_incremental_remaining_bytes(data + (num_words * 4), num_remaining_bytes); - prv_calculate_incremental_stop(); - - return (res); -} - -uint32_t crc_calculate_flash(uint32_t address, unsigned int num_bytes) { - prv_calculate_incremental_start(); - const unsigned int chunk_size = 128; - - uint8_t buffer[chunk_size]; - while (num_bytes > chunk_size) { - flash_read_bytes(buffer, address, chunk_size); - prv_calculate_incremental_words((const uint32_t*) buffer, chunk_size / 4); - - num_bytes -= chunk_size; - address += chunk_size; - } - - flash_read_bytes(buffer, address, num_bytes); - const uint32_t res = prv_calculate_incremental_remaining_bytes(buffer, num_bytes); - prv_calculate_incremental_stop(); - - return (res); -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/dbgserial.c b/platform/tintin/boot/src/drivers/stm32_common/dbgserial.c deleted file mode 100644 index f83f46bc7f..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/dbgserial.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" - -#include "drivers/periph_config.h" - -#include "drivers/gpio.h" - -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_gpio.h" -#include "stm32f2xx_usart.h" -#include "misc.h" - -#include -#include -#include - -static const int SERIAL_BAUD_RATE = 230400; - -void dbgserial_init(void) { - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - - // Enable GPIO and UART3 peripheral clocks - gpio_use(GPIOC); - periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_USART3); - - // USART_OverSampling8Cmd(USART3, ENABLE); - - /* Connect PXx to USARTx_Tx*/ - GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3); - - /* Connect PXx to USARTx_Rx*/ - GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3); - - /* Configure USART Tx as alternate function */ - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; - GPIO_Init(GPIOC, &GPIO_InitStructure); - - /* Configure USART Rx as alternate function */ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; - GPIO_Init(GPIOC, &GPIO_InitStructure); - - /* USART configuration */ - USART_InitStructure.USART_BaudRate = SERIAL_BAUD_RATE; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(USART3, &USART_InitStructure); - - /* Enable USART */ - USART_Cmd(USART3, ENABLE); - - gpio_release(GPIOC); -} - -static void prv_putchar(uint8_t c) { - while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) continue; - USART_SendData(USART3, c); - while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) continue; -} - -void dbgserial_print(const char* str) { - while (*str) { - prv_putchar(*str); - ++str; - } -} - -void dbgserial_newline(void) { - prv_putchar('\r'); - prv_putchar('\n'); -} - -void dbgserial_putstr(const char* str) { - dbgserial_print(str); - - dbgserial_newline(); -} - -void dbgserial_print_hex(uint32_t value) { - char buf[12]; - itoa(value, buf, sizeof(buf)); - dbgserial_print(buf); -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/gpio.c b/platform/tintin/boot/src/drivers/stm32_common/gpio.c deleted file mode 100644 index 33b9067776..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/gpio.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" - -#include - -#define MAX_GPIO (9) - - -static uint32_t s_gpio_clock_count[MAX_GPIO]; - -void gpio_use(GPIO_TypeDef* GPIOx) { - uint32_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - if ((idx < MAX_GPIO) && !(s_gpio_clock_count[idx]++)) { - SET_BIT(RCC->AHB1ENR, (0x1 << idx)); - } -} - -void gpio_release(GPIO_TypeDef* GPIOx) { - uint32_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - if ((idx < MAX_GPIO) && s_gpio_clock_count[idx] && !(--s_gpio_clock_count[idx])) { - CLEAR_BIT(RCC->AHB1ENR, (0x1 << idx)); - } -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/otp.c b/platform/tintin/boot/src/drivers/stm32_common/otp.c deleted file mode 100644 index 50aa0dba33..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/otp.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/otp.h" - -#include "stm32f2xx_flash.h" - -#include -#include - -// See page 53 of STM Reference Manual RM0033: -#define OTP_SLOTS_BASE_ADDR (0x1FFF7800) -#define OTP_LOCKS_BASE_ADDR (0x1FFF7A00) - -//! Each OTP slot is 32 bytes. There are 16 slots: [0-15] -char * otp_get_slot(const uint8_t index) { - return (char * const) (OTP_SLOTS_BASE_ADDR + (32 * index)); -} - -uint8_t * otp_get_lock(const uint8_t index) { - return (uint8_t * const) (OTP_LOCKS_BASE_ADDR + index); -} - -bool otp_is_locked(const uint8_t index) { - return (*otp_get_lock(index) == 0); -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/rtc.c b/platform/tintin/boot/src/drivers/stm32_common/rtc.c deleted file mode 100644 index 954c48263a..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/rtc.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "drivers/dbgserial.h" -#include "drivers/periph_config.h" -#include "drivers/rtc.h" -#include "util/delay.h" -#include "system/rtc_registers.h" - -#include "stm32f2xx_rcc.h" -#include "stm32f2xx_rtc.h" - -//! LSE startup time, about 4 seconds empirically, -//! but we give it 30 seconds since it it fails we sadwatch -static const int LSE_READY_TIMEOUT_MS = 30000; -static const unsigned int LSE_FREQUENCY_HZ = 32768; -static const int RTC_ASYNC_PRESCALER = 7; -static const int RTC_SYNC_PRESCALER = 3; - -static const unsigned int RTC_TICKS_HZ = 1024; -static const unsigned int TICKS_IN_INTERVAL = 60 * 60 * 24; - -static uint32_t prv_get_asynchronous_prescaler(void) { - return (RTC->PRER >> 16) & 0x7f; -} - -static uint32_t prv_get_synchronous_prescaler(void) { - return RTC->PRER & 0x1fff; -} - -//! Are we in slow mode? -static bool prv_slow_mode() { - return prv_get_asynchronous_prescaler() == 0x7f && prv_get_synchronous_prescaler() == 0xff; -} - -static bool prv_clocksource_is_lse_started(void) { - return RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET; -} - -static bool prv_clocksource_lse_configure(void) { - if (prv_clocksource_is_lse_started()) { - // LSE remains on through standby and resets so often don't need to do anything - return true; - } - - dbgserial_putstr("Starting LSE oscillator"); - RCC_LSEConfig(RCC_LSE_ON); - for (int i = 0; i < LSE_READY_TIMEOUT_MS; i++) { - if (prv_clocksource_is_lse_started()) { - return true; - } - delay_us(1000); - } - - dbgserial_putstr("LSE oscillator did not start"); - return false; -} - -//! This routine relies on bootbits already having enabled -//! access to the PWR clock and backup domain. Re-enabling -//! it here breaks wakeup for some reason -//! Returns false if configuring LSE failed -bool rtc_init(void) { - if (!prv_clocksource_lse_configure()) { - return false; - } - - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); - RCC_RTCCLKCmd(ENABLE); - RTC_WaitForSynchro(); - return true; -} - -// Before entering standby we set the RTC to it's default time (Jan 1, 2000) -// here we calculate the seconds elapsed since then -static int32_t prv_seconds_since_standby(void) { - // This function assumes the RTC is running in slow mode - - RTC_TimeTypeDef rtc_time; - RTC_GetTime(RTC_Format_BIN, &rtc_time); - - RTC_DateTypeDef rtc_date; - RTC_GetDate(RTC_Format_BIN, &rtc_date); - - // Unlike mktime there's no error checking here since if something goes wrong - // it'll just give us the wrong time anyway - - unsigned days = rtc_date.RTC_Year * 365; // RTC_Year is 0-99 - days += (rtc_date.RTC_Year / 4); // Leap years - - // Cumulative days from previous months - const unsigned month_days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; - - days += month_days[rtc_date.RTC_Month - 1 ]; // RTC_Month is 1-12 - if ((rtc_date.RTC_Year + 1) % 4 == 0 && rtc_date.RTC_Month > 2) { - // On a leap year and past February so add a leap day. - days++; - } - - // Add in previous days of the current month - days += rtc_date.RTC_Date - 1; - - return rtc_time.RTC_Seconds + 60 * (rtc_time.RTC_Minutes + 60 * - (rtc_time.RTC_Hours + 24 * days)); -} - -void rtc_initialize_fast_mode(void) { - // We configure the RTC to run in "fast time". This means that the calendar will - // be completely wrong, as we're incrementing the second count many times for every - // real second. The firmware's driver will hide this fact from the rest of the - // system. The reason we're doing this is because the STM32F2 micro doesn't offer - // a subsecond field in their calendar, so we resort to crazy workarounds to get - // a higher resolution timer. - RTC_InitTypeDef rtc_init_struct; - RTC_StructInit(&rtc_init_struct); - - _Static_assert((LSE_FREQUENCY_HZ / ((RTC_ASYNC_PRESCALER + 1) * (RTC_SYNC_PRESCALER + 1))) == - RTC_TICKS_HZ, "Our prescalers won't create the clock we want"); - _Static_assert(RTC_ASYNC_PRESCALER >= 6, "PREDIV_A < 6 - Coarse calibration will not work."); - - rtc_init_struct.RTC_AsynchPrediv = RTC_ASYNC_PRESCALER; - rtc_init_struct.RTC_SynchPrediv = RTC_SYNC_PRESCALER; - - RTC_Init(&rtc_init_struct); - - // Reset RTC time to 0, fast mode doesn't use the date register so leave it alone - RTC_TimeTypeDef rtc_time; - RTC_TimeStructInit(&rtc_time); - RTC_SetTime(RTC_Format_BIN, &rtc_time); -} - -void rtc_speed_up(void) { - if (!prv_slow_mode()) { - // If we're not in slow mode there's nothing to do - return; - } - // On standby the RTC is reset to date 0, so the RTC's time is really - // the number of seconds we've been in standby - int32_t elapsed_since_standby = prv_seconds_since_standby(); - - int32_t saved_time = RTC_ReadBackupRegister(CURRENT_TIME_REGISTER); - // Correct the saved time with the number of seconds we've been in standby mode - saved_time += elapsed_since_standby; - - // Save time in the backup register so the firmware can read it once it boots - RTC_WriteBackupRegister(CURRENT_TIME_REGISTER, saved_time); - RTC_WriteBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER, 0); - - rtc_initialize_fast_mode(); -} - -static uint32_t prv_bcd_to_byte(uint32_t value) { - const uint32_t tmp = ((value & 0xF0) >> 0x4) * 10; - return (tmp + (value & 0x0F)); -} - -static uint32_t prv_cur_ticks(void) { - uint32_t time_register = RTC->TR; - - const uint32_t hours = prv_bcd_to_byte((time_register & (RTC_TR_HT | RTC_TR_HU)) >> 16); - const uint32_t minutes = prv_bcd_to_byte((time_register & (RTC_TR_MNT | RTC_TR_MNU)) >> 8); - const uint32_t seconds = prv_bcd_to_byte(time_register & (RTC_TR_ST | RTC_TR_SU)); - - return (((hours * 60) + minutes) * 60) + seconds; -} - -static uint32_t prv_elapsed_ticks(uint32_t before, uint32_t after) { - int32_t result = after - before; - if (result < 0) { - result = (TICKS_IN_INTERVAL - before) + after; - } - return result; -} - -void rtc_slow_down(void) { - if (prv_slow_mode()) { - // If we're already slowed down there is nothing to do - return; - } - - // Calculate the current time and then save it back into the backup register - int32_t last_save_time = RTC_ReadBackupRegister(CURRENT_TIME_REGISTER); - uint32_t last_save_ticks = RTC_ReadBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER); - uint32_t ticks_since_save = prv_elapsed_ticks(last_save_ticks, prv_cur_ticks()); - - int32_t cur_time = last_save_time + ticks_since_save / RTC_TICKS_HZ; - // Save the current time into the backup registers - RTC_WriteBackupRegister(CURRENT_TIME_REGISTER, cur_time); - - // Set the RTC back to defaults (normal prescalers) - RTC_InitTypeDef rtc_init_struct; - RTC_StructInit(&rtc_init_struct); - RTC_Init(&rtc_init_struct); - - // Set the RTC to default date and time. - // When we speed up the clock we'll add the elapsed seconds - // to the saved register to get the correct time - RTC_TimeTypeDef rtc_default_time; - RTC_TimeStructInit(&rtc_default_time); - RTC_SetTime(RTC_Format_BIN, &rtc_default_time); - RTC_DateTypeDef rtc_default_date; - RTC_DateStructInit(&rtc_default_date); - RTC_SetDate(RTC_Format_BIN, &rtc_default_date); -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/system_flash.c b/platform/tintin/boot/src/drivers/stm32_common/system_flash.c deleted file mode 100644 index 154a5896c4..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/system_flash.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/system_flash.h" - -#include "drivers/dbgserial.h" -#include "util/misc.h" - -#include "stm32f2xx_flash.h" - -static uint16_t s_sectors[] = { - FLASH_Sector_0, FLASH_Sector_1, FLASH_Sector_2, FLASH_Sector_3, - FLASH_Sector_4, FLASH_Sector_5, FLASH_Sector_6, FLASH_Sector_7 -}; -static uint32_t s_sector_addresses[] = { - ADDR_FLASH_SECTOR_0, ADDR_FLASH_SECTOR_1, ADDR_FLASH_SECTOR_2, ADDR_FLASH_SECTOR_3, - ADDR_FLASH_SECTOR_4, ADDR_FLASH_SECTOR_5, ADDR_FLASH_SECTOR_6, ADDR_FLASH_SECTOR_7 -}; - -int prv_get_sector_num_for_address(uint32_t address) { - if (address < s_sector_addresses[0]) { - dbgserial_print("address "); - dbgserial_print_hex(address); - dbgserial_putstr(" is outside system flash"); - return -1; - } - for (size_t i=0; i < ARRAY_LENGTH(s_sector_addresses)-1; ++i) { - if (s_sector_addresses[i] <= address - && address < s_sector_addresses[i+1]) { - return i; - } - } - return ARRAY_LENGTH(s_sector_addresses)-1; -} - -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - if (length == 0) { - // Nothing to do - return true; - } - - int first_sector = prv_get_sector_num_for_address(address); - int last_sector = prv_get_sector_num_for_address(address + length - 1); - if (first_sector < 0 || last_sector < 0) { - return false; - } - int count = last_sector - first_sector + 1; - if (progress_callback) { - progress_callback(0, count, progress_context); - } - - FLASH_Unlock(); - for (int sector = first_sector; sector <= last_sector; ++sector) { - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); - if (FLASH_EraseSector( - s_sectors[sector], VoltageRange_1) != FLASH_COMPLETE) { - dbgserial_print("failed to erase sector "); - dbgserial_print_hex(sector); - dbgserial_newline(); - FLASH_Lock(); - return false; - } - if (progress_callback) { - progress_callback(sector - first_sector + 1, count, progress_context); - } - } - FLASH_Lock(); - return true; -} - -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context) { - FLASH_Unlock(); - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); - - const uint8_t *data_array = data; - for (uint32_t i = 0; i < length; ++i) { - if (FLASH_ProgramByte(address + i, data_array[i]) != FLASH_COMPLETE) { - dbgserial_print("failed to write address "); - dbgserial_print_hex(address); - dbgserial_newline(); - FLASH_Lock(); - return false; - } - if (progress_callback && i % 128 == 0) { - progress_callback(i/128, length/128, progress_context); - } - } - FLASH_Lock(); - return true; -} diff --git a/platform/tintin/boot/src/drivers/stm32_common/watchdog.c b/platform/tintin/boot/src/drivers/stm32_common/watchdog.c deleted file mode 100644 index c2f74952c2..0000000000 --- a/platform/tintin/boot/src/drivers/stm32_common/watchdog.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/watchdog.h" - -#include "stm32f2xx_dbgmcu.h" -#include "stm32f2xx_iwdg.h" -#include "stm32f2xx_rcc.h" - -void watchdog_init(void) { - IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); - - IWDG_SetPrescaler(IWDG_Prescaler_64); // ~8 seconds - IWDG_SetReload(0xfff); - - IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); - - DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP, ENABLE); -} - -void watchdog_start(void) { - IWDG_Enable(); - IWDG_ReloadCounter(); -} - -bool watchdog_check_reset_flag(void) { - return RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET; -} diff --git a/platform/tintin/boot/src/drivers/system_flash.h b/platform/tintin/boot/src/drivers/system_flash.h deleted file mode 100644 index a32306a9d4..0000000000 --- a/platform/tintin/boot/src/drivers/system_flash.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "stm32f2xx_flash.h" - -#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */ -#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */ - -typedef void (*SystemFlashProgressCb)( - uint32_t progress, uint32_t total, void *context); - -// Erase the sectors of flash which lie within the given address range. -// -// If the address range overlaps even one single byte of a sector, the entire -// sector is erased. -// -// If progress_callback is not NULL, it is called at the beginning of the erase -// process and after each sector is erased. The rational number (progress/total) -// increases monotonically as the sector erasue procedure progresses. -// -// Returns true if successful, false if an error occurred. -bool system_flash_erase( - uint32_t address, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); - -// Write data into flash. The flash must already be erased. -// -// If progress_callback is not NULL, it is called at the beginning of the -// writing process and periodically thereafter. The rational number -// (progress/total) increases monotonically as the data is written. -// -// Returns true if successful, false if an error occurred. -bool system_flash_write( - uint32_t address, const void *data, size_t length, - SystemFlashProgressCb progress_callback, void *progress_context); diff --git a/platform/tintin/boot/src/drivers/watchdog.h b/platform/tintin/boot/src/drivers/watchdog.h deleted file mode 100644 index d42fec728d..0000000000 --- a/platform/tintin/boot/src/drivers/watchdog.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void watchdog_init(void); -void watchdog_start(void); - -bool watchdog_check_reset_flag(void); diff --git a/platform/tintin/boot/src/firmware.h b/platform/tintin/boot/src/firmware.h deleted file mode 100644 index 159a4f07ea..0000000000 --- a/platform/tintin/boot/src/firmware.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "stm32f2xx.h" - -#define FW_OLDWORLD_OFFSET (0x10000) -#define FW_NEWWORLD_OFFSET (0x04000) -#define FW_WORLD_DIFFERENCE (FW_OLDWORLD_OFFSET - FW_NEWWORLD_OFFSET) - -#define FIRMWARE_OLDWORLD_BASE (FLASH_BASE + FW_OLDWORLD_OFFSET) -#define FIRMWARE_NEWWORLD_BASE (FLASH_BASE + FW_NEWWORLD_OFFSET) - -// Byte offset of NeWo in firmware -#define FW_IDENTIFIER_OFFSET (28) - -bool firmware_is_new_world(void* base); diff --git a/platform/tintin/boot/src/flash_region.h b/platform/tintin/boot/src/flash_region.h deleted file mode 100644 index 29a7328c28..0000000000 --- a/platform/tintin/boot/src/flash_region.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Scratch space for firmware images (normal and recovery). -// We assume this is 64k aligned... -#define FLASH_REGION_FIRMWARE_SCRATCH_BEGIN 0x0 -#define FLASH_REGION_FIRMWARE_SCRATCH_END 0x80000 // 512k - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x200000 -#define FLASH_REGION_SAFE_FIRMWARE_END 0x280000 // 512k diff --git a/platform/tintin/boot/src/fw_copy.c b/platform/tintin/boot/src/fw_copy.c deleted file mode 100644 index fa26e3355f..0000000000 --- a/platform/tintin/boot/src/fw_copy.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fw_copy.h" - -#include "drivers/crc.h" -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/system_flash.h" -#include "firmware.h" -#include "flash_region.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/reset.h" -#include "util/misc.h" -#include "util/delay.h" - -#include -#include - -#define MAX_CHUNK_SIZE 65536 - -static bool prv_check_valid_firmware_crc(uint32_t flash_address, FirmwareDescription* desc) { - dbgserial_putstr("Checksumming firmware update"); - uint32_t crc = crc_calculate_flash(flash_address, desc->firmware_length); - return crc == desc->checksum; -} - -// Fills in the first 50% of the progress bar -static void prv_display_erase_progress( - uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress, total * 2); -} - -//! Returns true if we're going to install a new world firmware -static bool prv_check_firmware_world(uint32_t flash_new_fw_addr) { - // Read the beginning of the firmware off flash to see if it's new world or old world - const unsigned world_length = FW_IDENTIFIER_OFFSET + sizeof(uint32_t); // NeWo read as uint32_t - - uint8_t buffer[world_length]; - flash_read_bytes(buffer, flash_new_fw_addr, world_length); - - return firmware_is_new_world(buffer); -} - -static bool prv_erase_old_firmware(bool new_world, uint32_t firmware_length) { - dbgserial_putstr("erase_old_firmware"); - uint32_t system_flash_base = FIRMWARE_NEWWORLD_BASE; - uint32_t erase_length = firmware_length; - - if (!new_world) { - // If the incoming firmware is an old world firmware, we need to not only erase - // enough room for the old world firmware but we also need to erase the area - // between the new world and old world base addresses. If we don't erase this - // area and just load in the old world firmware at FIRMWARE_OLDWORLD_BASE, the - // bootloader will still see the start of the old new world firmware in this - // section and will attempt to boot that. - dbgserial_putstr("Old World firmware base"); - erase_length += FW_WORLD_DIFFERENCE; - } - - return system_flash_erase(system_flash_base, erase_length, prv_display_erase_progress, 0); -} - -// Fills in the last 50% of the progress bar -static void prv_display_write_progress( - uint32_t progress, uint32_t total, void *ctx) { - display_firmware_update_progress(progress/2 + total/2, total); -} - -static bool prv_write_new_firmware(bool new_world, uint32_t flash_new_fw_start, - uint32_t firmware_length) { - dbgserial_putstr("write_new_firmware"); - uint32_t system_flash_base = new_world ? FIRMWARE_NEWWORLD_BASE : FIRMWARE_OLDWORLD_BASE; - // We can't just read the flash like memory, so we gotta lift everything ourselves. - // buffer is static so it goes in BSS, since stack is only 8192 bytes - static uint8_t buffer[MAX_CHUNK_SIZE]; - uint32_t chunk_size; - for (uint32_t i = 0; i < firmware_length; i += chunk_size) { - chunk_size = MIN(MAX_CHUNK_SIZE, firmware_length - i); - flash_read_bytes(buffer, flash_new_fw_start + i, chunk_size); - if (!system_flash_write(system_flash_base + i, buffer, chunk_size, NULL, NULL)) { - dbgserial_putstr("We're dead"); - return false; - } - prv_display_write_progress(i, firmware_length, NULL); - } - return true; -} - -static bool prv_check_firmware_crc(FirmwareDescription* firmware_description) { - dbgserial_print("Checksumming "); - dbgserial_print_hex(firmware_description->firmware_length); - dbgserial_putstr(" bytes"); - - void *system_flash_base = (void*)FIRMWARE_OLDWORLD_BASE; - - if (firmware_is_new_world(NULL)) { - dbgserial_putstr("New World firmware system_flash_base"); - system_flash_base = (void*)FIRMWARE_NEWWORLD_BASE; - } else { - dbgserial_putstr("Old World firmware system_flash_base"); - } - uint32_t calculated_crc = crc_calculate_bytes((const uint8_t*)system_flash_base, - firmware_description->firmware_length); - - dbgserial_print("Checksum - wanted "); - dbgserial_print_hex(firmware_description->checksum); - dbgserial_print(" got "); - dbgserial_print_hex(calculated_crc); - dbgserial_newline(); - - return calculated_crc == firmware_description->checksum; -} - -typedef enum UpdateFirmwareResult { - UPDATE_FW_SUCCESS = 0, - UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED = 1, - UPDATE_FW_ERROR_MICRO_FLASH_MANGLED = 2 -} UpdateFirmwareResult; - -static UpdateFirmwareResult prv_update_fw(uint32_t flash_new_fw_addr) { - display_firmware_update_progress(0, 1); - boot_bit_set(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - - FirmwareDescription firmware_description = - firmware_storage_read_firmware_description(flash_new_fw_addr); - - if (!firmware_storage_check_valid_firmware_description(&firmware_description)) { - dbgserial_print("Desclen "); - dbgserial_print_hex(firmware_description.description_length); - dbgserial_print("\nFirmlen "); - dbgserial_print_hex(firmware_description.firmware_length); - dbgserial_print("\nXsum "); - dbgserial_print_hex(firmware_description.checksum); - dbgserial_putstr("\nInvalid firmware description!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - if (!prv_check_valid_firmware_crc( - flash_new_fw_addr + sizeof(FirmwareDescription), &firmware_description)) { - dbgserial_putstr("Invalid firmware CRC in SPI flash!"); - return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED; - } - - bool new_world = prv_check_firmware_world(flash_new_fw_addr + sizeof(FirmwareDescription)); - - prv_erase_old_firmware(new_world, firmware_description.firmware_length); - - prv_write_new_firmware(new_world, flash_new_fw_addr + sizeof(FirmwareDescription), - firmware_description.firmware_length); - - if (!prv_check_firmware_crc(&firmware_description)) { - dbgserial_putstr( - "Our internal flash contents are bad (checksum failed)! " - "This is really bad!"); - return UPDATE_FW_ERROR_MICRO_FLASH_MANGLED; - } - - return UPDATE_FW_SUCCESS; -} - -void check_update_fw(void) { - if (!boot_bit_test(BOOT_BIT_NEW_FW_AVAILABLE)) { - return; - } - - if (boot_bit_test(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS)) { - dbgserial_putstr("Our previous firmware update failed, aborting update."); - - // Pretend like the new firmware bit wasn't set afterall. We'll just run the - // previous code, whether that was normal firmware or the recovery firmware. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_clear(BOOT_BIT_NEW_FW_INSTALLED); - return; - } - - - dbgserial_putstr("New firmware is available!"); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - - UpdateFirmwareResult result = prv_update_fw(FLASH_REGION_FIRMWARE_SCRATCH_BEGIN); - switch (result) { - case UPDATE_FW_SUCCESS: - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - // Our firmware update failed in a way that didn't break our previous - // firmware. Just run the previous code, whether that was normal firmware - // or the recovery firmware. - break; - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // We've broken our internal flash when trying to update our normal - // firmware. Fall back immediately to the recovery firmare. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - system_reset(); - } - - // Done, we're ready to boot. - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE); - boot_bit_set(BOOT_BIT_NEW_FW_INSTALLED); -} - -bool switch_to_recovery_fw() { - dbgserial_putstr("Loading recovery firmware"); - - UpdateFirmwareResult result = prv_update_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - bool recovery_fw_ok = true; - switch (result) { - case UPDATE_FW_SUCCESS: - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - break; - case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED: - case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED: - // Keep us booting into recovery firmware. - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - - if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Failed to load recovery firmware, strike one. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO)) { - dbgserial_putstr("Failed to load recovery firmware, strike two. Try again."); - boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - system_reset(); - } else { - dbgserial_putstr("Failed to load recovery firmware, strike three. SAD WATCH"); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - recovery_fw_ok = false; - } - break; - } - - boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS); - return recovery_fw_ok; -} diff --git a/platform/tintin/boot/src/fw_copy.h b/platform/tintin/boot/src/fw_copy.h deleted file mode 100644 index aab6d1e7a7..0000000000 --- a/platform/tintin/boot/src/fw_copy.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void check_update_fw(void); - -//! @return false if we've failed to load recovery mode too many times and we should just sadwatch -bool switch_to_recovery_fw(void); diff --git a/platform/tintin/boot/src/git_version.auto.h.in b/platform/tintin/boot/src/git_version.auto.h.in deleted file mode 100644 index 92c500ceb9..0000000000 --- a/platform/tintin/boot/src/git_version.auto.h.in +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#define GIT_TIMESTAMP @TIMESTAMP@ -#define GIT_TAG "@TAG@" -#define GIT_MAJOR_VERSION @MAJOR_VERSION@ -#define GIT_MINOR_VERSION @MINOR_VERSION@ -#define GIT_PATCH_VERSION @PATCH_VERSION@ -#define GIT_REVISION "@COMMIT@" - diff --git a/platform/tintin/boot/src/hardfault_handler.c b/platform/tintin/boot/src/hardfault_handler.c deleted file mode 100644 index 5d0b18db7d..0000000000 --- a/platform/tintin/boot/src/hardfault_handler.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/reset.h" - -static void prv_hard_fault_handler_c(unsigned int *hardfault_args) { - dbgserial_putstr("HARD FAULT"); - -#ifdef NO_WATCHDOG - __asm("bkpt\n"); - while (1) continue; -#else - system_hard_reset(); -#endif -} - -void HardFault_Handler(void) { - // Grab the stack pointer, shove it into a register and call - // the c function above. - __asm("tst lr, #4\n" - "ite eq\n" - "mrseq r0, msp\n" - "mrsne r0, psp\n" - "b %0\n" :: "i" (prv_hard_fault_handler_c)); -} diff --git a/platform/tintin/boot/src/main.c b/platform/tintin/boot/src/main.c deleted file mode 100644 index b6fd2e61e0..0000000000 --- a/platform/tintin/boot/src/main.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "board/board.h" -#include "boot_tests.h" -#include "drivers/button.h" -#include "drivers/dbgserial.h" -#include "drivers/display.h" -#include "drivers/flash.h" -#include "drivers/periph_config.h" -#include "drivers/rtc.h" -#include "drivers/watchdog.h" -#include "firmware.h" -#include "fw_copy.h" -#include "pebble_errors.h" -#include "standby.h" -#include "system/bootbits.h" -#include "system/reset.h" -#include "util/delay.h" - -#include "stm32f2xx.h" - -static const uint8_t SELECT_BUTTON_MASK = 0x4; - -bool firmware_is_new_world(void* base) { - uint32_t* fw_base = base; - if (fw_base == NULL) { - fw_base = (uint32_t*)FIRMWARE_NEWWORLD_BASE; - } - if (fw_base[0] == 0xFFFFFFFF || - fw_base[1] == 0xFFFFFFFF) { // Erased flash - return false; - } - if (fw_base[FW_IDENTIFIER_OFFSET / sizeof(uint32_t)] != 0x4E65576F) { // NeWo - return false; - } - // TODO: Additional checks? - return true; -} - -static void prv_get_fw_reset_vector(void **reset_handler, - void **initial_stack_pointer) { - void** fw_vector_table; - if (firmware_is_new_world(NULL)) { - fw_vector_table = (void**) FIRMWARE_NEWWORLD_BASE; - } else { - fw_vector_table = (void**) FIRMWARE_OLDWORLD_BASE; - } - *initial_stack_pointer = fw_vector_table[0]; - *reset_handler = fw_vector_table[1]; -} - -static void prv_hw_reset(void) { - // Disable all interrupts, just in case. - for (int i = 0; i < 8; ++i) { - // Interrupt Clear-Enable Register - NVIC->ICER[i] = 0xFFFFFFFF; - // Interrupt Clear-Pending Register - NVIC->ICPR[i] = 0xFFFFFFFF; - } - - // Set the peripheral clock enable registers to their reset values as - // specified in the reference manual. - RCC->AHB1ENR = 0; - RCC->AHB2ENR = 0; - RCC->AHB3ENR = 0; - RCC->APB1ENR = 0; - RCC->APB2ENR = 0; - - // Reset most peripherals used by the bootloader. We want to minimize the - // chances that the firmware unintentionally relies on some state that the - // bootloader leaves behind. This includes disabling the PLL. - // GPIOs are not reset here: resetting them would change their output values, - // which could unintentionally modify peripherals (such as the display). - // The backup domain is not reset; that would be foolish. - RCC_DeInit(); - - // Reset flags for each bus taken from reset register lists in reference manual - // starting with 5.3.5 "RCC AHB1 peripheral reset register" - - const uint32_t ahb1_periphs = 0 - | RCC_AHB1Periph_CRC - | RCC_AHB1Periph_DMA1 - | RCC_AHB1Periph_DMA2 - | RCC_AHB1Periph_ETH_MAC - | RCC_AHB1Periph_OTG_HS; - - RCC_AHB1PeriphResetCmd(ahb1_periphs, ENABLE); - RCC_AHB1PeriphResetCmd(ahb1_periphs, DISABLE); - - const uint32_t ahb2_periphs = 0 - | RCC_AHB2Periph_DCMI - | RCC_AHB2Periph_CRYP - | RCC_AHB2Periph_HASH - | RCC_AHB2Periph_RNG - | RCC_AHB2Periph_OTG_FS; - - RCC_AHB2PeriphResetCmd(ahb2_periphs, ENABLE); - RCC_AHB2PeriphResetCmd(ahb2_periphs, DISABLE); - - const uint32_t ahb3_periphs = 0 - | RCC_AHB3Periph_FSMC; - - RCC_AHB3PeriphResetCmd(ahb3_periphs, ENABLE); - RCC_AHB3PeriphResetCmd(ahb3_periphs, DISABLE); - - const uint32_t apb1_periphs = 0 - | RCC_APB1Periph_TIM2 - | RCC_APB1Periph_TIM3 - | RCC_APB1Periph_TIM4 - | RCC_APB1Periph_TIM5 - | RCC_APB1Periph_TIM6 - | RCC_APB1Periph_TIM7 - | RCC_APB1Periph_TIM12 - | RCC_APB1Periph_TIM13 - | RCC_APB1Periph_TIM14 - | RCC_APB1Periph_WWDG - | RCC_APB1Periph_SPI2 - | RCC_APB1Periph_SPI3 - | RCC_APB1Periph_USART2 - | RCC_APB1Periph_USART3 - | RCC_APB1Periph_UART4 - | RCC_APB1Periph_UART5 - | RCC_APB1Periph_I2C1 - | RCC_APB1Periph_I2C2 - | RCC_APB1Periph_I2C3 - | RCC_APB1Periph_CAN1 - | RCC_APB1Periph_CAN2 - | RCC_APB1Periph_PWR - | RCC_APB1Periph_DAC; - - RCC_APB1PeriphResetCmd(apb1_periphs, ENABLE); - RCC_APB1PeriphResetCmd(apb1_periphs, DISABLE); - - const uint32_t apb2_periphs = 0 - | RCC_APB2Periph_TIM1 - | RCC_APB2Periph_TIM8 - | RCC_APB2Periph_USART1 - | RCC_APB2Periph_USART6 - | RCC_APB2Periph_ADC - | RCC_APB2Periph_SDIO - | RCC_APB2Periph_SPI1 - | RCC_APB2Periph_SYSCFG - | RCC_APB2Periph_TIM9 - | RCC_APB2Periph_TIM10 - | RCC_APB2Periph_TIM11; - - RCC_APB2PeriphResetCmd(apb2_periphs, ENABLE); - RCC_APB2PeriphResetCmd(apb2_periphs, DISABLE); -} - -static void __attribute__((noreturn)) prv_jump_to_fw(void) { - void *initial_stack_pointer, *reset_handler; - prv_get_fw_reset_vector(&reset_handler, &initial_stack_pointer); - - dbgserial_print("Booting firmware @ "); - dbgserial_print_hex((uintptr_t)reset_handler); - dbgserial_newline(); - dbgserial_newline(); - - prv_hw_reset(); - - // The Cortex-M user guide states that the reset values for the core registers - // are as follows: - // R0-R12 = Unknown - // MSP = VECTOR_TABLE[0] (main stack pointer) - // PSP = Unknown (process stack pointer) - // LR = 0xFFFFFFFF - // PC = VECTOR_TABLE[1] - // PRIMASK = 0x0 - // FAULTMASK = 0x0 - // BASEPRI = 0x0 - // CONTROL = 0x0 - // - // Attempt to put the processor into as close to the reset state as possible - // before passing control to the firmware. - // - // No attempt is made to set CONTROL to zero as it should already be set to - // the reset value when this code executes. - __asm volatile ( - "cpsie if\n" // Clear PRIMASK and FAULTMASK - "mov lr, 0xFFFFFFFF\n" - "mov sp, %[initial_sp]\n" - "bx %[reset_handler]\n" - : : [initial_sp] "r" (initial_stack_pointer), - [reset_handler] "r" (reset_handler) - ); - __builtin_unreachable(); -} - -static bool prv_check_and_increment_reset_loop_detection_bits(void) { - uint8_t counter = - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_THREE) << 2) | - (boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_TWO) << 1) | - boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_ONE); - - switch (++counter) { - case 1: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 2: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 3: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 4: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_THREE); - break; - case 5: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - case 6: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO); - break; - case 7: - boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE); - break; - default: - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO); - boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE); - return true; - } - return false; -} - -static bool prv_check_for_recovery_start_failure() { - return boot_bit_test(BOOT_BIT_RECOVERY_START_IN_PROGRESS); -} - -static bool prv_check_for_fw_start_failure() { - // Add more failure conditions here. - if (!watchdog_check_reset_flag() && !boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - // We're good, we're just starting normally. - dbgserial_putstr("Booting normally"); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return false; - } - - // We failed to start our firmware successfully! - if (watchdog_check_reset_flag()) { - dbgserial_putstr("Watchdog caused a reset"); - } - if (boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) { - dbgserial_putstr("Software failure caused a reset"); - } - - // Clean up after the last failure. - boot_bit_clear(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); - - // We have a "three strikes" algorithm: if the watch fails three times, return true - // to tell the parent we should load the recovery firmware. A reset for any other reason - // will reset this algorithm. - if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) { - // Yikes, our firmware is screwed. Boot into recovery mode. - dbgserial_putstr("Boot failed, strike 3"); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - return true; - } else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_ONE)) { - dbgserial_putstr("Boot failed, strike 2"); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - } else { - dbgserial_putstr("Boot failed, strike 1"); - boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - } - - return false; -} - -static bool prv_check_force_boot_recovery(void) { - if (boot_bit_test(BOOT_BIT_FORCE_PRF)) { - boot_bit_clear(BOOT_BIT_FORCE_PRF); - return true; - } - - if (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK)) { - dbgserial_putstr("Hold down UP + BACK for 5 secs. to force-boot PRF"); - for (int i = 0; i < 5000; ++i) { - if (!(button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK))) { - // stop waiting if not held down any longer - return false; - } - delay_ms(1); - } - - return true; - } - - void *reset_vector, *initial_sp; - prv_get_fw_reset_vector(&reset_vector, &initial_sp); - if ((uintptr_t)reset_vector == 0xffffffff || - (uintptr_t)initial_sp == 0xffffffff) { - dbgserial_putstr("Firmware is erased"); - return true; - } - return false; -} - -static void prv_sad_watch(uint32_t error_code) { - dbgserial_print("SAD WATCH: "); - dbgserial_print_hex(error_code); - dbgserial_newline(); - - display_error_code(error_code); - - static uint8_t prev_button_state = 0; - prev_button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - while (1) { - // See if we should restart - uint8_t button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK; - if (button_state != prev_button_state) { - system_reset(); - } - - delay_ms(10); - } -} - -static void prv_print_reset_reason(void) { - dbgserial_print("Reset Register "); - dbgserial_print_hex(RCC->CSR); - dbgserial_newline(); - if (RCC_GetFlagStatus(RCC_FLAG_BORRST) == SET) { - dbgserial_putstr("Brown out reset"); - } -} - -// SystemInit does this for the firmware, but since the bootloader isn't using -// the vendor SystemInit, initialize the flash cache here -static void prv_configure_system_flash(void) { - // Enable flash instruction and data caches - FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN; -} - -// RTC and bootbit assume access to the backup -// registers has been enabled -static void prv_enable_backup_access() { - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); - PWR_BackupAccessCmd(ENABLE); // Disable write-protect on RTC_BKP_x registers -} - -void boot_main(void) { - prv_configure_system_flash(); - prv_enable_backup_access(); - - dbgserial_init(); - prv_print_reset_reason(); - - boot_bit_init(); - - if (!rtc_init()) { - // Need to initialize the display in this - // case so we can see the sad watch - display_init(); - prv_sad_watch(ERROR_CANT_START_LSE); - } - - // Standby checks need to know the button pressed state - button_init(); - - // On tintin the bootloader handles entering and leaving standby manually - if (boot_bit_test(BOOT_BIT_STANDBY_MODE_REQUESTED)) { - boot_bit_clear(BOOT_BIT_STANDBY_MODE_REQUESTED); - enter_standby_mode(); - } else if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) { // Woke up from standby - // Clear the standby flag since only a power reset clears it - PWR_ClearFlag(PWR_FLAG_SB); - - // Before coming out of standby make sure we should be waking up - if (should_leave_standby_mode()) { - leave_standby_mode(); - } else { - dbgserial_putstr("returning to standby"); - enter_standby_mode(); - } - - dbgserial_putstr("leaving standby"); - } else { - // If not entering or leaving standby this is a cold boot. The firmware - // expects the clock to be running in fast mode - rtc_initialize_fast_mode(); - } - - // Print out our super cool bootloader logo: - // ______ __ - // /_ __/ __/ /_ - // / / /_ __/ - // /_/ /_/ - - dbgserial_putstr(" ______ __\r\n/_ __/ __/ /\r\n / / /_ __/\r\n/_/ /_/\r\n"); - - boot_version_write(); - - // Write the bootloader version to serial-out - dbgserial_print("Bootloader version: "); - dbgserial_print_hex(boot_version_read()); - dbgserial_newline(); - - if (boot_bit_test(BOOT_BIT_FW_STABLE)) { - dbgserial_putstr("Last firmware boot was stable; clear strikes"); - - boot_bit_clear(BOOT_BIT_FW_STABLE); - - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE); - boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO); - } - - display_init(); - display_boot_splash(); - -#ifdef DISPLAY_DEMO_LOOP - while (1) { - display_boot_splash(); - delay_us(1000000); - - for (int i=0; i <= 91; ++i) { - display_firmware_update_progress(i, 91); - delay_us(80000); - } - - for (uint32_t i=0; i <= 0xf; ++i) { - display_error_code(i * 0x11111111); - delay_us(200000); - } - for (uint32_t i=0; i < 8; ++i) { - for (uint32_t j=1; j <= 0xf; ++j) { - display_error_code(j << (i*4)); - delay_us(200000); - } - } - display_error_code(0x01234567); - delay_us(200000); - display_error_code(0x89abcdef); - delay_us(200000); - display_error_code(0xcafebabe); - delay_us(200000); - display_error_code(0xfeedface); - delay_us(200000); - display_error_code(0x8badf00d); - delay_us(200000); - display_error_code(0xbad1ce40); - delay_us(200000); - display_error_code(0xbeefcace); - delay_us(200000); - display_error_code(0x0defaced); - delay_us(200000); - display_error_code(0xd15ea5e5); - delay_us(200000); - display_error_code(0xdeadbeef); - delay_us(200000); - } -#endif - - flash_init(); - - if (is_button_stuck()) { - prv_sad_watch(ERROR_STUCK_BUTTON); - } - - if (is_flash_broken()) { - prv_sad_watch(ERROR_BAD_SPI_FLASH); - } - - boot_bit_dump(); - - // If the recovery firmware crashed at start-up, the watch is now a - // $150 brick. That's life! - if (prv_check_for_recovery_start_failure()) { - boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS); - prv_sad_watch(ERROR_CANT_LOAD_FW); - } - - bool force_boot_recovery_mode = prv_check_force_boot_recovery(); - if (force_boot_recovery_mode) { - dbgserial_putstr("Force-booting recovery mode..."); - } - - if (force_boot_recovery_mode || prv_check_for_fw_start_failure()) { - if (!switch_to_recovery_fw()) { - // We've failed to load recovery mode too many times. - prv_sad_watch(ERROR_CANT_LOAD_FW); - } - } else { - check_update_fw(); - } - - if (prv_check_and_increment_reset_loop_detection_bits()) { - prv_sad_watch(ERROR_RESET_LOOP); - } - - watchdog_init(); -#ifndef NO_WATCHDOG - watchdog_start(); -#endif - - prv_jump_to_fw(); -} diff --git a/platform/tintin/boot/src/pebble_errors.h b/platform/tintin/boot/src/pebble_errors.h deleted file mode 100644 index 1278bf459b..0000000000 --- a/platform/tintin/boot/src/pebble_errors.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -static const uint32_t ERROR_NO_ACTIVE_ERROR = 0; - -// FW Errors -static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501; -static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502; -static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503; -static const uint32_t ERROR_RESET_LOOP = 0xfe504504; - -// BT Errors -static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510; -static const uint32_t ERROR_CANT_START_LSE = 0xfe504511; -static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512; - -static const uint32_t ERROR_LOW_BATTERY = 0xfe504520; diff --git a/platform/tintin/boot/src/standby.c b/platform/tintin/boot/src/standby.c deleted file mode 100644 index 8101be35fa..0000000000 --- a/platform/tintin/boot/src/standby.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/button.h" -#include "drivers/otp.h" -#include "drivers/rtc.h" -#include "drivers/periph_config.h" -#include "drivers/dbgserial.h" - -#include "system/reset.h" - -static bool prv_is_wake_on_usb_supported(void) { - // we accidentally left off a pull-up on early BB2s and v1_5 boards - // with the upshot of not being able to support wake from standby on VUSB - if (BOARD_CONFIG_POWER.wake_on_usb_power) { - if (!otp_is_locked(OTP_HWVER)) { - dbgserial_putstr("No HW Version in OTP"); - // let's be optimistic - return true; - } - - const char* hw_ver = otp_get_slot(OTP_HWVER); -#if defined(BOARD_BB2) - // We fixed the issue for BB2.1 (900-0-22-02-R1) - const char* no_support_hw_ver = "BB2.0"; - return (memcmp(hw_ver, no_support_hw_ver, strlen(no_support_hw_ver)) != 0); -#elif defined(BOARD_V1_5) - // We fixed the issue for V3R2 (101-0-22-10-R3) - const char* no_support_hw_ver = "V3R1"; - return (memcmp(hw_ver, no_support_hw_ver, strlen(no_support_hw_ver)) != 0); -#else - return true; -#endif - } else { - return false; - } -} - -static void prv_wait_until_buttons_are_released(void) { - for (int bounce_count = 0; bounce_count < 10; ++bounce_count) { - uint8_t button_state; - - // First, see if the buttons are all released for a period of time. - for (int i = 0; i < 10000; ++i) { - button_state = button_get_state_bits(); - if (button_state != 0) { - // Someone pushed a button! - break; - } - } - - if (button_state == 0) { - // We made it through with all the buttons released. We're good. - return; - } - - // Alright, so either the button is held down or we hit a bounce. Wait - // for all the buttons to release again. - // 100000 is about a second in practice - for (int i = 0; i < 100000; ++i) { - if (button_get_state_bits() == 0) { - // All the buttons are released! - break; - } - } - } -} - -static void prv_clear_wakeup_flags(void) { - // This function follows the steps listed in Erratum 2.1.4 "Wakeup sequence from Standby mode..." - // to avoid a situation where the watch cannot wake up - // or immediately wakes up after going into standby. - - // The erratum says all used wakeup sources need to be disabled before - // reenabling the required ones, so to be safe we disable all wakeup sources - // to avoid dependence on knowing which wakeup sources the firmware left set - // Possible wakeup sources taken from reference manual 4.3.5 "Exiting Standby Mode" - - // Disable the Wakeup pin - PWR_WakeUpPinCmd(DISABLE); - - // Clear RTC interrupts, this ensures the flags won't be reset after we clear them - RTC_ITConfig(RTC_IT_TAMP - | RTC_IT_TS - | RTC_IT_WUT - | RTC_IT_ALRA - | RTC_IT_ALRB, DISABLE); - // Clear all RTC wakeup flags - RTC_ClearFlag(RTC_FLAG_TAMP1F - | RTC_FLAG_TSF - | RTC_FLAG_WUTF - | RTC_FLAG_ALRBF - | RTC_FLAG_ALRAF); - - // At this point we know the wakeup flags are cleared so we can clear the PWR wakeup flag - PWR->CR |= PWR_CR_CWUF; -} - -static void prv_enable_wake_on_usb(void) { - // Use the RTC timestamp alternate function to trigger a wakeup from the VUSB interrupt - // We don't clear all the wakeup flags here as said in - // 4.3.6 "Safe RTC alternate function wakeup flag clearing sequence", because - // prv_clear_wakeup_flags already cleared them for use by multiple wakeup sources - RTC_TimeStampPinSelection(RTC_TimeStampPin_PC13); - RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, ENABLE); - RTC_ITConfig(RTC_IT_TS, ENABLE); -} - -void enter_standby_mode(void) { - rtc_slow_down(); - - // Set wakeup events for the board - // If the WKUP pin is high when we enable wakeup, an additional - // wakeup event is registered (4.4.2 "PWR power control/status register"), - // which will cause the board to wake up immediately after entering standby. Therefore we - // wait until the button is released (or too much time has passed). - // It is possible to work around needing this by enabling the WKUP pin before - // clearing the PWR WUF flag, but that risks running afoul of errata 2.1.4 - prv_wait_until_buttons_are_released(); - - prv_clear_wakeup_flags(); - - PWR_WakeUpPinCmd(ENABLE); - - if (prv_is_wake_on_usb_supported()) { - dbgserial_putstr("usb wakeup supported"); - prv_enable_wake_on_usb(); - } - - // Put the board into standby mode. The standard peripheral library provides PWR_EnterSTANDBYMode - // to do this, but that function clears the WUF (wakeup) flag. According to errata 2.1.4 if the - // wakeup flag is cleared when any wakeup source is high, further wakeup events may be masked. - // This means if a button press or usb plugin was to occur in between enabling the wakeup events - // and clearing the flag, the watch wouldn't wake up. - dbgserial_putstr("Entering standby"); - - // Steps to enter standby follow 4.3.5 "Entering Standby mode" Table 11 - // (except where they conflict with errata 2.1.4) - - // Select STANDBY mode - PWR->CR |= PWR_CR_PDDS; - - // Set SLEEPDEEP bit on the cortex system control register - SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; - - // Wait for interrupt - __WFI(); -} - -bool should_leave_standby_mode(void) { - if (RTC_GetFlagStatus(RTC_FLAG_TSF)) { - // we were woken by the USB power being plugged in - dbgserial_putstr("USB wakeup"); - return true; - } - - // Make sure a button is held down before waking up - for (int i = 0; i < 100000; ++i) { - if (button_get_state_bits() == 0) { - // stop waiting if not held down any longer and go back to sleep - return false; - } - } - - return true; -} - -void leave_standby_mode(void) { - // Speed up the RTC so the firmware doesn't need to deal with it - rtc_speed_up(); -} diff --git a/platform/tintin/boot/src/standby.h b/platform/tintin/boot/src/standby.h deleted file mode 100644 index 5ce7cdf561..0000000000 --- a/platform/tintin/boot/src/standby.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Setup wakeup sources and put board into standby mode -void enter_standby_mode(void); - -//! Check to make sure we should be leaving standby mode -bool should_leave_standby_mode(void); - -//! Return functionality to normal after standby mode -void leave_standby_mode(void); diff --git a/platform/tintin/boot/src/startup_stm32.c b/platform/tintin/boot/src/startup_stm32.c deleted file mode 100644 index 5de96efaee..0000000000 --- a/platform/tintin/boot/src/startup_stm32.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! Initial firmware startup, contains the vector table that the bootloader loads. -//! Based on "https://github.com/pfalcon/cortex-uni-startup/blob/master/startup.c" -//! by Paul Sokolovsky (public domain) - -#include -#include -#include -#include "util/attributes.h" - -//! These symbols are defined in the linker script for use in initializing -//! the data sections. uint8_t since we do arithmetic with section lengths. -//! These are arrays to avoid the need for an & when dealing with linker symbols. -extern uint8_t __data_load_start[]; -extern uint8_t __data_start[]; -extern uint8_t __data_end[]; -extern uint8_t __bss_start[]; -extern uint8_t __bss_end[]; -extern uint8_t _estack[]; - -//! Bootloader entry point, ResetHandler calls this -extern int boot_main(void); - -//! STM32 system initialization function, defined in the standard peripheral library -extern void SystemInit(void); - -//! We don't use any interrupts in the bootloader so we map the core interrupts to -//! the HardFault_Handler to get useful debugging info if something goes wrong -extern void HardFault_Handler(void); - -//! This function is what gets called when the processor first -//! starts execution following a reset event. The data and bss -//! sections are initialized, then we call the firmware's main -//! function -void Reset_Handler(void) { - // Copy data section from flash to RAM - memcpy(__data_start, __data_load_start, __data_end - __data_start); - - // Clear the bss section, assumes .bss goes directly after .data - memset(__bss_start, 0, __bss_end - __bss_start); - - boot_main(); - - // Main shouldn't return - while (true) {} -} - -EXTERNALLY_VISIBLE SECTION(".isr_vector") void *vector_table[] = { - _estack, - Reset_Handler, - HardFault_Handler, - HardFault_Handler, - HardFault_Handler, - HardFault_Handler, - HardFault_Handler, - 0, - 0, - 0, - 0, - HardFault_Handler, - HardFault_Handler, - 0, - HardFault_Handler, - HardFault_Handler -}; diff --git a/platform/tintin/boot/src/stm32f_flash_boot.ld.in b/platform/tintin/boot/src/stm32f_flash_boot.ld.in deleted file mode 100644 index 8e89448dd8..0000000000 --- a/platform/tintin/boot/src/stm32f_flash_boot.ld.in +++ /dev/null @@ -1,236 +0,0 @@ -/* -Linker script for STM32F2xx_1024K_128K -*/ - -__Stack_Size = 8192; - -/* Memory Spaces Definitions */ - -MEMORY -{ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_LENGTH@ -} - -/* include the sections management sub-script for FLASH mode */ - -/* Sections Definitions */ - -SECTIONS -{ - /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */ - .isr_vector : - { - KEEP(*(.isr_vector)) /* Startup code */ - } >FLASH - - /* Exception handling sections. "contains index entries for section unwinding" - * We don't actually use this or know what it is, but it seems happy to go here. */ - .ARM.exidx : - { - *(.ARM.exidx) - } >FLASH - - /* Exported symbols: - * This list can be calculated by putting all the KEEP directives in the normal - * .text section, then using nm to find the addresses of each symbol. Then we - * put each exported symbol in its own section so we can control their addresses */ - - .text.memcpy 0x08000048 : { KEEP(*(.text.memcpy)) } > FLASH - .text.memset 0x0800005a : { KEEP(*(.text.memset)) } > FLASH - .text.memchr 0x0800006a : { KEEP(*(.text.memchr)) } > FLASH - .text.strcat 0x08000086 : { KEEP(*(.text.strcat)) } > FLASH - .text.strchr 0x0800009c : { KEEP(*(.text.strchr)) } > FLASH - .text.strcmp 0x080000b2 : { KEEP(*(.text.strcmp)) } > FLASH - .text.strcpy 0x080000c8 : { KEEP(*(.text.strcpy)) } > FLASH - .text.strlen 0x080000e0 : { KEEP(*(.text.strlen)) } > FLASH - .text.strnlen 0x080000ee : { KEEP(*(.text.strnlen)) } > FLASH - .text.strspn 0x08000100 : { KEEP(*(.text.strspn)) } > FLASH - .text.strtol 0x0800011c : { KEEP(*(.text.strtol)) } > FLASH - .text.atoi 0x08000140 : { KEEP(*(.text.atoi)) } > FLASH - .text.atol 0x08000168 : { KEEP(*(.text.atol)) } > FLASH - .text.puts 0x08000190 : { KEEP(*(.text.puts)) } > FLASH - .text.exit 0x08000194 : { KEEP(*(.text.exit)) } > FLASH - .text.strstr 0x08000196 : { KEEP(*(.text.strstr)) } > FLASH - .text.strcspn 0x080001c2 : { KEEP(*(.text.strcspn)) } > FLASH - .text.strncpy 0x080001e0 : { KEEP(*(.text.strncpy)) } > FLASH - .text.strncmp 0x08000214 : { KEEP(*(.text.strncmp)) } > FLASH - .text.strrchr 0x08000232 : { KEEP(*(.text.strrchr)) } > FLASH - .text.strncat 0x08000252 : { KEEP(*(.text.strncat)) } > FLASH - .text.memmove 0x0800027e : { KEEP(*(.text.memmove)) } > FLASH - .text.memcmp 0x080002a2 : { KEEP(*(.text.memcmp)) } > FLASH - .text.vsnprintf 0x080002bc : { KEEP(*(.text.vsnprintf)) } > FLASH - .text.snprintf 0x08000730 : { KEEP(*(.text.snprintf)) } > FLASH - .text.vsprintf 0x0800074a : { KEEP(*(.text.vsprintf)) } > FLASH - .text.sprintf 0x0800075c : { KEEP(*(.text.sprintf)) } > FLASH - .text.setlocale 0x08000776 : { KEEP(*(.text.setlocale)) } > FLASH - .text.RCC_ClearFlag 0x0800077c : { KEEP(*(.text.RCC_ClearFlag)) } > FLASH - .text.TIM_Cmd 0x0800078c : { KEEP(*(.text.TIM_Cmd)) } > FLASH - .text.CRC_ResetDR 0x08000810 : { KEEP(*(.text.CRC_ResetDR)) } > FLASH - .text.CRC_CalcCRC 0x0800081c : { KEEP(*(.text.CRC_CalcCRC)) } > FLASH - .text.CRC_CalcBlockCRC 0x08000828 : { KEEP(*(.text.CRC_CalcBlockCRC)) } > FLASH - .text.CRC_GetCRC 0x08000844 : { KEEP(*(.text.CRC_GetCRC)) } > FLASH - .text.DBGMCU_APB1PeriphConfig 0x08000850 : { KEEP(*(.text.DBGMCU_APB1PeriphConfig)) } > FLASH - .text.FLASH_Unlock 0x08000868 : { KEEP(*(.text.FLASH_Unlock)) } > FLASH - .text.FLASH_Lock 0x08000884 : { KEEP(*(.text.FLASH_Lock)) } > FLASH - .text.FLASH_ClearFlag 0x08000894 : { KEEP(*(.text.FLASH_ClearFlag)) } > FLASH - .text.FLASH_GetStatus 0x080008a0 : { KEEP(*(.text.FLASH_GetStatus)) } > FLASH - .text.FLASH_EraseSector 0x080008d4 : { KEEP(*(.text.FLASH_EraseSector)) } > FLASH - .text.FLASH_ProgramByte 0x08000944 : { KEEP(*(.text.FLASH_ProgramByte)) } > FLASH - .text.IWDG_WriteAccessCmd 0x0800097c : { KEEP(*(.text.IWDG_WriteAccessCmd)) } > FLASH - .text.IWDG_SetPrescaler 0x08000988 : { KEEP(*(.text.IWDG_SetPrescaler)) } > FLASH - .text.IWDG_SetReload 0x08000994 : { KEEP(*(.text.IWDG_SetReload)) } > FLASH - .text.IWDG_ReloadCounter 0x080009a0 : { KEEP(*(.text.IWDG_ReloadCounter)) } > FLASH - .text.IWDG_Enable 0x080009b0 : { KEEP(*(.text.IWDG_Enable)) } > FLASH - .text.PWR_BackupAccessCmd 0x080009c0 : { KEEP(*(.text.PWR_BackupAccessCmd)) } > FLASH - .text.PWR_WakeUpPinCmd 0x080009cc : { KEEP(*(.text.PWR_WakeUpPinCmd)) } > FLASH - .text.PWR_GetFlagStatus 0x080009d8 : { KEEP(*(.text.PWR_GetFlagStatus)) } > FLASH - .text.PWR_ClearFlag 0x080009ec : { KEEP(*(.text.PWR_ClearFlag)) } > FLASH - .text.RCC_DeInit 0x080009fc : { KEEP(*(.text.RCC_DeInit)) } > FLASH - .text.RCC_LSEConfig 0x08000a30 : { KEEP(*(.text.RCC_LSEConfig)) } > FLASH - .text.RCC_GetClocksFreq 0x08000a50 : { KEEP(*(.text.RCC_GetClocksFreq)) } > FLASH - .text.RCC_RTCCLKConfig 0x08000ad8 : { KEEP(*(.text.RCC_RTCCLKConfig)) } > FLASH - .text.RCC_RTCCLKCmd 0x08000b08 : { KEEP(*(.text.RCC_RTCCLKCmd)) } > FLASH - .text.RCC_AHB1PeriphClockCmd 0x08000b14 : { KEEP(*(.text.RCC_AHB1PeriphClockCmd)) } > FLASH - .text.RCC_APB1PeriphClockCmd 0x08000b2c : { KEEP(*(.text.RCC_APB1PeriphClockCmd)) } > FLASH - .text.RCC_APB2PeriphClockCmd 0x08000b44 : { KEEP(*(.text.RCC_APB2PeriphClockCmd)) } > FLASH - .text.RCC_AHB1PeriphResetCmd 0x08000b5c : { KEEP(*(.text.RCC_AHB1PeriphResetCmd)) } > FLASH - .text.RCC_AHB2PeriphResetCmd 0x08000b74 : { KEEP(*(.text.RCC_AHB2PeriphResetCmd)) } > FLASH - .text.RCC_AHB3PeriphResetCmd 0x08000b8c : { KEEP(*(.text.RCC_AHB3PeriphResetCmd)) } > FLASH - .text.RCC_APB1PeriphResetCmd 0x08000ba4 : { KEEP(*(.text.RCC_APB1PeriphResetCmd)) } > FLASH - .text.RCC_APB2PeriphResetCmd 0x08000bbc : { KEEP(*(.text.RCC_APB2PeriphResetCmd)) } > FLASH - .text.RCC_GetFlagStatus 0x08000bd4 : { KEEP(*(.text.RCC_GetFlagStatus)) } > FLASH - .text.RTC_EnterInitMode 0x08000bfc : { KEEP(*(.text.RTC_EnterInitMode)) } > FLASH - .text.RTC_ExitInitMode 0x08000c3c : { KEEP(*(.text.RTC_ExitInitMode)) } > FLASH - .text.RTC_Init 0x08000c4c : { KEEP(*(.text.RTC_Init)) } > FLASH - .text.RTC_WaitForSynchro 0x08000c90 : { KEEP(*(.text.RTC_WaitForSynchro)) } > FLASH - .text.RTC_SetTime 0x08000cd4 : { KEEP(*(.text.RTC_SetTime)) } > FLASH - .text.RTC_GetTime 0x08000d64 : { KEEP(*(.text.RTC_GetTime)) } > FLASH - .text.RTC_SetDate 0x08000da8 : { KEEP(*(.text.RTC_SetDate)) } > FLASH - .text.RTC_GetDate 0x08000e2c : { KEEP(*(.text.RTC_GetDate)) } > FLASH - .text.RTC_TimeStampCmd 0x08000e6c : { KEEP(*(.text.RTC_TimeStampCmd)) } > FLASH - .text.RTC_WriteBackupRegister 0x08000e94 : { KEEP(*(.text.RTC_WriteBackupRegister)) } > FLASH - .text.RTC_ReadBackupRegister 0x08000eb4 : { KEEP(*(.text.RTC_ReadBackupRegister)) } > FLASH - .text.RTC_TimeStampPinSelection 0x08000ed4 : { KEEP(*(.text.RTC_TimeStampPinSelection)) } > FLASH - .text.RTC_ITConfig 0x08000eec : { KEEP(*(.text.RTC_ITConfig)) } > FLASH - .text.RTC_GetFlagStatus 0x08000f28 : { KEEP(*(.text.RTC_GetFlagStatus)) } > FLASH - .text.RTC_ClearFlag 0x08000f44 : { KEEP(*(.text.RTC_ClearFlag)) } > FLASH - .text.SPI_I2S_DeInit 0x08000f60 : { KEEP(*(.text.SPI_I2S_DeInit)) } > FLASH - .text.USART_Init 0x08000fbc : { KEEP(*(.text.USART_Init)) } > FLASH - .text.FLASH_WaitForLastOperation 0x0800106c : { KEEP(*(.text.FLASH_WaitForLastOperation)) } > FLASH - .text.GPIO_Init 0x0800108e : { KEEP(*(.text.GPIO_Init)) } > FLASH - .text.GPIO_StructInit 0x0800110e : { KEEP(*(.text.GPIO_StructInit)) } > FLASH - .text.GPIO_ReadInputDataBit 0x08001120 : { KEEP(*(.text.GPIO_ReadInputDataBit)) } > FLASH - .text.GPIO_SetBits 0x0800112c : { KEEP(*(.text.GPIO_SetBits)) } > FLASH - .text.GPIO_ResetBits 0x08001130 : { KEEP(*(.text.GPIO_ResetBits)) } > FLASH - .text.GPIO_WriteBit 0x08001134 : { KEEP(*(.text.GPIO_WriteBit)) } > FLASH - .text.GPIO_PinAFConfig 0x0800113e : { KEEP(*(.text.GPIO_PinAFConfig)) } > FLASH - .text.RTC_ByteToBcd2 0x08001162 : { KEEP(*(.text.RTC_ByteToBcd2)) } > FLASH - .text.RTC_Bcd2ToByte 0x0800117c : { KEEP(*(.text.RTC_Bcd2ToByte)) } > FLASH - .text.RTC_StructInit 0x0800118e : { KEEP(*(.text.RTC_StructInit)) } > FLASH - .text.RTC_TimeStructInit 0x0800119a : { KEEP(*(.text.RTC_TimeStructInit)) } > FLASH - .text.RTC_DateStructInit 0x080011a6 : { KEEP(*(.text.RTC_DateStructInit)) } > FLASH - .text.SPI_Init 0x080011b4 : { KEEP(*(.text.SPI_Init)) } > FLASH - .text.SPI_Cmd 0x080011f2 : { KEEP(*(.text.SPI_Cmd)) } > FLASH - .text.SPI_I2S_ReceiveData 0x0800120a : { KEEP(*(.text.SPI_I2S_ReceiveData)) } > FLASH - .text.SPI_I2S_SendData 0x08001210 : { KEEP(*(.text.SPI_I2S_SendData)) } > FLASH - .text.SPI_I2S_GetFlagStatus 0x08001214 : { KEEP(*(.text.SPI_I2S_GetFlagStatus)) } > FLASH - .text.USART_Cmd 0x08001220 : { KEEP(*(.text.USART_Cmd)) } > FLASH - .text.USART_SendData 0x08001238 : { KEEP(*(.text.USART_SendData)) } > FLASH - .text.USART_GetFlagStatus 0x08001240 : { KEEP(*(.text.USART_GetFlagStatus)) } > FLASH - .text.ctype_data 0x0800124c : { KEEP(*(.rodata.__ctype_data)) } > FLASH - .text.bpapi 0x0800134c : { *(libgcc.a:bpapi.o*) } > FLASH - .text.uldivmod 0x08001378 : { *(libgcc.a:_aeabi_uldivmod.o*) } > FLASH - .text.dvmd 0x080013d8 : { *(libgcc.a:_dvmd_tls.o*) } > FLASH - .text.divdi3 0x080013dc : { *(libgcc.a:_divdi3.o*) } > FLASH - .text.udivdi3 0x0800167c : { *(libgcc.a:_udivdi3.o*) } > FLASH - - /* the program code is stored in the .text section, which goes to Flash */ - .text : { - *(.text) /* remaining code */ - *(.text.*) /* remaining code */ - *(.rodata) /* read-only data (constants) */ - *(.rodata*) - *(.constdata) /* read-only data (constants) */ - *(.constdata*) - . = ALIGN(4); - } >FLASH - - /* This is the initialized data section - The program executes knowing that the data is in the RAM - but the loader puts the initial values in the FLASH (inidata). - It is one task of the startup to copy the initial values from FLASH to RAM. */ - .data : { - . = ALIGN(4); - /* This is used by the startup in order to initialize the .data secion */ - __data_start = .; - - *(.data) - *(.data.*) - - . = ALIGN(4); - __data_end = .; /* This is used by the startup in order to initialize the .data secion */ - } >RAM AT>FLASH - __data_load_start = LOADADDR(.data); - - /* This is the uninitialized data section */ - .bss (NOLOAD) : - { - . = ALIGN(4); - __bss_start = .; /* This is used by the startup in order to initialize the .bss secion */ - - *(.bss) - *(.bss.*) - *(COMMON) - - . = ALIGN(4); - __bss_end = .; /* This is used by the startup in order to initialize the .bss secion */ - } >RAM - - .stack (NOLOAD) : - { - . = ALIGN(8); - . += __Stack_Size; - _estack = .; - } >RAM - - /* after that it's only debugging information. */ - /* remove the debugging information from the standard libraries */ - DISCARD : { - libgcc.a ( * ) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} diff --git a/platform/tintin/boot/src/system/bootbits.c b/platform/tintin/boot/src/system/bootbits.c deleted file mode 100644 index 19c508ff4b..0000000000 --- a/platform/tintin/boot/src/system/bootbits.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dbgserial.h" -#include "system/bootbits.h" -#include "system/rtc_registers.h" - -#include "git_version.auto.h" - -#include "stm32f2xx.h" - -#include -#include - -static const uint32_t s_bootloader_timestamp = GIT_TIMESTAMP; - -void boot_bit_init(void) { - if (!boot_bit_test(BOOT_BIT_INITIALIZED)) { - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED); - } -} - -void boot_bit_set(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value |= bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -void boot_bit_clear(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - current_value &= ~bit; - RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value); -} - -bool boot_bit_test(BootBitValue bit) { - uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR); - return (current_value & bit); -} - -void boot_bit_dump(void) { - dbgserial_print("Boot bits: "); - dbgserial_print_hex(RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR)); - dbgserial_newline(); -} - -void boot_version_write(void) { - if (boot_version_read() == s_bootloader_timestamp) { - return; - } - RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, s_bootloader_timestamp); -} - -uint32_t boot_version_read(void) { - return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER); -} diff --git a/platform/tintin/boot/src/system/bootbits.h b/platform/tintin/boot/src/system/bootbits.h deleted file mode 100644 index 7415662187..0000000000 --- a/platform/tintin/boot/src/system/bootbits.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum BootBitValue { - BOOT_BIT_INITIALIZED = 0x1 << 0, - BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1, - BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2, - BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3, - BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5, - BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6, - BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7, - BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, - BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9, - BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10, - BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11, - BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12, - BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13, - BOOT_BIT_FW_STABLE = 0x1 << 14, - BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15, - BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16, - BOOT_BIT_FORCE_PRF = 0x1 << 17, - BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18 -} BootBitValue; - -void boot_bit_init(); -void boot_bit_set(BootBitValue bit); -void boot_bit_clear(BootBitValue bit); -bool boot_bit_test(BootBitValue bit); - -// Dump the contents through dbgserial -void boot_bit_dump(void); - -void boot_version_write(void); -uint32_t boot_version_read(void); diff --git a/platform/tintin/boot/src/system/firmware_storage.c b/platform/tintin/boot/src/system/firmware_storage.c deleted file mode 100644 index 586ebf1649..0000000000 --- a/platform/tintin/boot/src/system/firmware_storage.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "firmware_storage.h" - -#include "drivers/flash.h" - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { - FirmwareDescription firmware_description; - flash_read_bytes((uint8_t*)&firmware_description, firmware_start_address, - sizeof(FirmwareDescription)); - return firmware_description; -} - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc) { - return desc->description_length == sizeof(FirmwareDescription); -} diff --git a/platform/tintin/boot/src/system/firmware_storage.h b/platform/tintin/boot/src/system/firmware_storage.h deleted file mode 100644 index e05c44b208..0000000000 --- a/platform/tintin/boot/src/system/firmware_storage.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file firmware_storage.h -//! Utilities for reading a firmware image stored in flash. - -#include -#include - -typedef struct __attribute__((__packed__)) FirmwareDescription { - uint32_t description_length; - uint32_t firmware_length; - uint32_t checksum; -} FirmwareDescription; - -FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address); - -bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc); diff --git a/platform/tintin/boot/src/system/reset.c b/platform/tintin/boot/src/system/reset.c deleted file mode 100644 index 39bee75073..0000000000 --- a/platform/tintin/boot/src/system/reset.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "system/reset.h" - -#include "drivers/display.h" - -#include "stm32f2xx.h" - -void system_reset(void) { - display_prepare_for_reset(); - - // Clear the reset reason since it will no longer - // apply after this bootloader reset - RCC_ClearFlag(); - - system_hard_reset(); -} - -void system_hard_reset(void) { - NVIC_SystemReset(); - __builtin_unreachable(); -} diff --git a/platform/tintin/boot/src/system/reset.h b/platform/tintin/boot/src/system/reset.h deleted file mode 100644 index 6b5ba28099..0000000000 --- a/platform/tintin/boot/src/system/reset.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Reset nicely after shutting down system services. Does not set the reboot_reason other than -//! calling reboot_reason_set_restarted_safely just before the reset occurs. -void system_reset(void)__attribute__((noreturn)); - -//! The final stage in the reset process. -void system_hard_reset(void) __attribute__((noreturn)); diff --git a/platform/tintin/boot/src/system/rtc_registers.h b/platform/tintin/boot/src/system/rtc_registers.h deleted file mode 100644 index d86c325dfd..0000000000 --- a/platform/tintin/boot/src/system/rtc_registers.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0 -#define STUCK_BUTTON_REGISTER RTC_BKP_DR1 -#define BOOTLOADER_VERSION_REGISTER RTC_BKP_DR2 -#define CURRENT_TIME_REGISTER RTC_BKP_DR3 -#define CURRENT_INTERVAL_TICKS_REGISTER RTC_BKP_DR4 -#define REBOOT_REASON_REGISTER_1 RTC_BKP_DR5 -#define REBOOT_REASON_REGISTER_2 RTC_BKP_DR6 -#define REBOOT_REASON_STUCK_TASK_PC RTC_BKP_DR7 -#define REBOOT_REASON_STUCK_TASK_LR RTC_BKP_DR8 -#define REBOOT_REASON_STUCK_TASK_CALLBACK RTC_BKP_DR9 -#define REBOOT_REASON_MUTEX_LR RTC_BKP_DR10 -#define REBOOT_REASON_MUTEX_PC RTC_BKP_DR11 // Deprecated -#define MAG_XY_CORRECTION_VALS RTC_BKP_DR17 -#define MAG_Z_CORRECTION_VAL RTC_BKP_DR18 -#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19 diff --git a/platform/tintin/boot/src/util/attributes.h b/platform/tintin/boot/src/util/attributes.h deleted file mode 100644 index 8cd71d62d5..0000000000 --- a/platform/tintin/boot/src/util/attributes.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if defined(__clang__) -#define GCC_ONLY(x) -#else -#define GCC_ONLY(x) x -#endif - -// Function attributes -#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST))) - -#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST) - -#define ALWAYS_INLINE __attribute__((__always_inline__)) inline -#define NOINLINE __attribute__((__noinline__)) -#define NORETURN __attribute__((__noreturn__)) void -#define NAKED_FUNC __attribute__((__naked__)) -#define OPTIMIZE_FUNC(LVL) __attribute__((__optimize__(LVL))) -#define CONST_FUNC __attribute__((__const__)) -#define PURE_FUNC __attribute__((__pure__)) - -// Variable attributes -#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC))) - -// Structure attributes -#define PACKED __attribute__((__packed__)) - -// General attributes -#define USED __attribute__((__used__)) -#define UNUSED __attribute__((__unused__)) -#define WEAK __attribute__((__weak__)) -#define ALIAS(sym) __attribute__((__weak__, __alias__(sym))) -#define SECTION(SEC) GCC_ONLY(__attribute__((__section__(SEC)))) -#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__))) diff --git a/platform/tintin/boot/src/util/delay.c b/platform/tintin/boot/src/util/delay.c deleted file mode 100644 index 8d659bb72c..0000000000 --- a/platform/tintin/boot/src/util/delay.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "delay.h" -#include "stm32f2xx.h" -#include "util/attributes.h" - -#include - -// Calculated using the formula from the firmware's delay_init: -// ceil( NS_PER_US / ( clock_period * INSTRUCTIONS_PER_LOOP ) -// where NS_PER_US = 1000 and INSTRUCTIONS_PER_LOOP = 3, -// and clock_period = 62.5 -#define LOOPS_PER_US (6) - -void NOINLINE delay_us(uint32_t us) { - uint32_t delay_loops = us * LOOPS_PER_US; - - __asm volatile ( - "spinloop: \n" - " subs %[delay_loops], #1 \n" - " bne spinloop \n" - : [delay_loops] "+r" (delay_loops) // read-write operand - : - : "cc" - ); -} - -void delay_ms(uint32_t millis) { - // delay_us(millis*1000) is not used because a long delay could easily - // overflow the veriable. Without the outer loop, a delay of even five - // seconds would overflow. - while (millis--) { - delay_us(1000); - } -} diff --git a/platform/tintin/boot/src/util/delay.h b/platform/tintin/boot/src/util/delay.h deleted file mode 100644 index 371ffd7cd7..0000000000 --- a/platform/tintin/boot/src/util/delay.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Carefully timed spinloop that allows one to delay at a microsecond -//! granularity. -void delay_us(uint32_t us); - -//! Waits for a certain amount of milliseconds by busy-waiting. -//! -//! @param millis The number of milliseconds to wait for -void delay_ms(uint32_t millis); diff --git a/platform/tintin/boot/src/util/misc.c b/platform/tintin/boot/src/util/misc.c deleted file mode 100644 index 1fba57b5d8..0000000000 --- a/platform/tintin/boot/src/util/misc.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "misc.h" - -#include "drivers/dbgserial.h" - -#include - -void itoa(uint32_t num, char *buffer, int buffer_length) { - if (buffer_length < 11) { - dbgserial_putstr("itoa buffer too small"); - return; - } - *buffer++ = '0'; - *buffer++ = 'x'; - - for (int i = 7; i >= 0; --i) { - uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4); - - char c; - if (digit < 0xa) { - c = '0' + digit; - } else if (digit < 0x10) { - c = 'a' + (digit - 0xa); - } else { - c = ' '; - } - - *buffer++ = c; - } - *buffer = '\0'; -} diff --git a/platform/tintin/boot/src/util/misc.h b/platform/tintin/boot/src/util/misc.h deleted file mode 100644 index 110846b04d..0000000000 --- a/platform/tintin/boot/src/util/misc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000) - -#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0])) - -//! Find the log base two of a number rounded up -int ceil_log_two(uint32_t n); - -//! Convert num to a hex string and put in buffer -void itoa(uint32_t num, char *buffer, int buffer_length); diff --git a/platform/tintin/boot/test/test_system_flash.c b/platform/tintin/boot/test/test_system_flash.c deleted file mode 100644 index caeaaa10a1..0000000000 --- a/platform/tintin/boot/test/test_system_flash.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "clar.h" - -#include "drivers/system_flash.h" - -#define KiB *1024 - -// Set bits n..0 in a bit-vector (zero-indexed) -#define BITS(n) ((((uint32_t)(1 << ((n) + 1)))) - 1) -// Set bits y..x in a bit-vector (x <= y; x, y >= 0) -#define BITS_BETWEEN(x, y) (BITS(y) & ~BITS(x-1)) - -// Yo dawg, I heard you like tests so I put tests in your tests so you can test -// your tests while you test! -void test_system_flash__bit_range_macros(void) { - cl_assert_equal_i(0b1, BITS(0)); - cl_assert_equal_i(0b00011111, BITS(4)); - cl_assert_equal_i(0b00111111, BITS_BETWEEN(0, 5)); - cl_assert_equal_i(0b00111000, BITS_BETWEEN(3, 5)); - cl_assert_equal_i(0b00010000, BITS_BETWEEN(4, 4)); -} - -// Flash memory is organized into twelve sectors of unequal sizes. -// Sectors 0-3 are 16 KiB. Sector 4 is 64 KiB. -// The remaining sectors are 128 KiB. - -// Bitset of sectors that have been "erased" -static uint32_t erased_sector; -static bool flash_locked, flash_flags_set; -static FLASH_Status return_status; -uint8_t *flash_written_data; -bool *flash_written_flag; -void *source_buffer; -uint32_t flash_data_start, flash_data_length; -bool callback_called; - -void test_system_flash__initialize(void) { - erased_sector = 0; - flash_locked = true; - flash_flags_set = false; - return_status = FLASH_COMPLETE; - flash_written_data = NULL; - flash_written_flag = NULL; - flash_data_start = 0; - flash_data_length = 0; - callback_called = false; -} - -void test_system_flash__cleanup(void) { - free(flash_written_data); - free(flash_written_flag); -} - -void test_system_flash__erase_zero_bytes(void) { - cl_assert(system_flash_erase(FLASH_BASE, 0, NULL, NULL)); - cl_assert_equal_i(0, erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_one_byte_in_middle_of_sector(void) { - cl_assert(system_flash_erase(FLASH_BASE + 12345, 1, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_some_sectors_from_beginning(void) { - cl_assert(system_flash_erase(FLASH_BASE, 128 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 4), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_full_flash(void) { - cl_assert(system_flash_erase(FLASH_BASE, 1024 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 7), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_sector_0(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 0), erased_sector); - cl_assert(flash_locked); -} - -void test_system_flash__erase_16KB_sectors(void) { - cl_assert(system_flash_erase(FLASH_BASE, 48 KiB, NULL, NULL)); - cl_assert_equal_i(BITS_BETWEEN(0, 2), erased_sector); - cl_assert(flash_locked); -} - -void callback_is_called_cb(uint32_t num, uint32_t den, void *context) { - callback_called = true; - cl_assert_equal_i(8675309, (uintptr_t)context); -} - -void test_system_flash__callback_is_called(void) { - cl_assert(system_flash_erase(FLASH_BASE, 16 KiB, callback_is_called_cb, - (void *)8675309)); - cl_assert(callback_called); - cl_assert(flash_locked); -} - -void test_system_flash__handle_erase_error(void) { - return_status = FLASH_ERROR_OPERATION; - cl_assert(!system_flash_erase(FLASH_BASE, 16 KiB, NULL, NULL)); - cl_assert(flash_locked); -} - -void error_in_middle_cb(uint32_t num, uint32_t den, void *context) { - int *countdown = context; - if ((*countdown)-- == 0) { - return_status = FLASH_ERROR_OPERATION; - } -} - -void test_system_flash__handle_erase_error_mid_operation(void) { - int countdown = 3; - cl_assert(!system_flash_erase(FLASH_BASE, 512 KiB, error_in_middle_cb, - &countdown)); - cl_assert(flash_locked); - cl_assert_(countdown <= 0, "Callback not called enough times"); -} - - -void malloc_flash_data(uint32_t size) { - flash_data_length = size; - flash_written_data = malloc(size * sizeof(uint8_t)); - cl_assert(flash_written_data); - flash_written_flag = malloc(size * sizeof(bool)); - cl_assert(flash_written_flag); -} - -void assert_flash_unwritten(uint32_t start, uint32_t length) { - for (uint32_t i = 0; i < length; ++i) { - cl_assert(flash_written_flag[start + i] == false); - } -} - -void test_system_flash__write_simple(void) { - const char testdata[] = "The quick brown fox jumps over the lazy dog."; - malloc_flash_data(100); - flash_data_start = FLASH_BASE; - cl_assert(system_flash_write( - FLASH_BASE + 10, testdata, sizeof(testdata), callback_is_called_cb, - (void *)8675309)); - cl_assert(flash_locked); - cl_assert_equal_s(testdata, (const char *)&flash_written_data[10]); - assert_flash_unwritten(0, 10); - assert_flash_unwritten(10 + sizeof(testdata), 90 - sizeof(testdata)); - cl_assert(callback_called); -} - -void test_system_flash__write_error(void) { - return_status = FLASH_ERROR_OPERATION; - malloc_flash_data(10); - flash_data_start = FLASH_BASE; - cl_assert(!system_flash_write(FLASH_BASE, "abc", 3, NULL, NULL)); - cl_assert(flash_locked); - assert_flash_unwritten(0, 10); -} - -extern void FLASH_Lock(void) { - flash_locked = true; -} - -extern void FLASH_Unlock(void) { - flash_locked = false; -} - -extern void FLASH_ClearFlag(uint32_t FLASH_FLAG) { - flash_flags_set = false; -} - -extern FLASH_Status FLASH_EraseSector(uint32_t sector, uint8_t voltage_range) { - // Pretty sure FLASH_Sector_N defines are simply 8*N, at least for the first - // twelve sectors. - cl_assert_(!flash_locked, "Attempted to erase a locked flash"); - cl_assert_(IS_FLASH_SECTOR(sector), "Sector number out of range"); - cl_assert(IS_VOLTAGERANGE(voltage_range)); - cl_check_(flash_flags_set == false, "Forgot to clear flags before erasing"); - cl_check_((erased_sector & (1 << sector/8)) == 0, - "Re-erasing an already erased sector"); - flash_flags_set = true; - if (return_status == FLASH_COMPLETE) { - erased_sector |= (1 << sector/8); - } - return return_status; -} - -extern FLASH_Status FLASH_ProgramByte(uint32_t address, uint8_t data) { - cl_assert_(!flash_locked, "Attempted to write to a locked flash"); - cl_assert_(address >= flash_data_start && - address < flash_data_start + flash_data_length, - "Address out of range"); - cl_assert_(flash_written_flag[address - flash_data_start] == false, - "Overwriting an already-written byte"); - if (return_status == FLASH_COMPLETE) { - flash_written_data[address - flash_data_start] = data; - } - return return_status; -} - -extern void dbgserial_print(char *str) { - fprintf(stderr, "%s", str); -} - -extern void dbgserial_print_hex(uint32_t num) { - fprintf(stderr, "0x%.08x", num); -} - -extern void dbgserial_putstr(char *str) { - fprintf(stderr, "%s\n", str); -} diff --git a/platform/tintin/boot/vendor/wscript b/platform/tintin/boot/vendor/wscript deleted file mode 100644 index a42116ec86..0000000000 --- a/platform/tintin/boot/vendor/wscript +++ /dev/null @@ -1,22 +0,0 @@ -def build(bld): - stm32_basedir = 'STM32F2xx_StdPeriph_Lib_V1.1.0/' - stm32_srcdirs = [stm32_basedir + subpath for subpath in ( - '', 'Libraries/STM32F2xx_StdPeriph_Driver/src', - 'Libraries/CMSIS/CM3/CoreSupport')] - stm32_sources = sum( - [bld.path.ant_glob('%s/*.c' % d) for d in stm32_srcdirs], []) - - stm32_incpath_base = stm32_basedir + 'Libraries/' - stm32_includes = [stm32_incpath_base + subpath for subpath in ( - 'CMSIS/Include', 'CMSIS/Device/ST/STM32F2xx/Include', - 'STM32F2xx_StdPeriph_Driver/inc')] - - stm32_includes += ['stm32_conf'] - - bld.stlib(source=stm32_sources, - cflags='-fno-lto', - target='stm32_stdlib', - includes=stm32_includes, - export_includes=stm32_includes) - -# vim:filetype=python diff --git a/platform/tintin/boot/waf b/platform/tintin/boot/waf deleted file mode 100755 index 4d6635a842..0000000000 --- a/platform/tintin/boot/waf +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# encoding: ISO8859-1 -# Thomas Nagy, 2005-2014 - -""" -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -""" - -import os, sys, inspect - -VERSION="1.8.5" -REVISION="69c046f2063dbb528ecf1ab20607ba25" -INSTALL='' -C1='#:' -C2='#5' -C3='#/' -cwd = os.getcwd() -join = os.path.join - - -WAF='waf' -def b(x): - return x -if sys.hexversion>0x300000f: - WAF='waf3' - def b(x): - return x.encode() - -def err(m): - print(('\033[91mError: %s\033[0m' % m)) - sys.exit(1) - -def unpack_wafdir(dir, src): - f = open(src,'rb') - c = 'corrupt archive (%d)' - while 1: - line = f.readline() - if not line: err('run waf-light from a folder containing waflib') - if line == b('#==>\n'): - txt = f.readline() - if not txt: err(c % 1) - if f.readline() != b('#<==\n'): err(c % 2) - break - if not txt: err(c % 3) - txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) - - import shutil, tarfile - try: shutil.rmtree(dir) - except OSError: pass - try: - for x in ('Tools', 'extras'): - os.makedirs(join(dir, 'waflib', x)) - except OSError: - err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) - - os.chdir(dir) - tmp = 't.bz2' - t = open(tmp,'wb') - try: t.write(txt) - finally: t.close() - - try: - t = tarfile.open(tmp) - except: - try: - os.system('bunzip2 t.bz2') - t = tarfile.open('t') - tmp = 't' - except: - os.chdir(cwd) - try: shutil.rmtree(dir) - except OSError: pass - err("Waf cannot be unpacked, check that bzip2 support is present") - - try: - for x in t: t.extract(x) - finally: - t.close() - - for x in ('Tools', 'extras'): - os.chmod(join('waflib',x), 493) - - if sys.hexversion<0x300000f: - sys.path = [join(dir, 'waflib')] + sys.path - import fixpy2 - fixpy2.fixdir(dir) - - os.remove(tmp) - os.chdir(cwd) - - try: dir = unicode(dir, 'mbcs') - except: pass - try: - from ctypes import windll - windll.kernel32.SetFileAttributesW(dir, 2) - except: - pass - -def test(dir): - try: - os.stat(join(dir, 'waflib')) - return os.path.abspath(dir) - except OSError: - pass - -def find_lib(): - src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) - base, name = os.path.split(src) - - #devs use $WAFDIR - w=test(os.environ.get('WAFDIR', '')) - if w: return w - - #waf-light - if name.endswith('waf-light'): - w = test(base) - if w: return w - err('waf-light requires waflib -> export WAFDIR=/folder') - - dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) - for i in (INSTALL,'/usr','/usr/local','/opt'): - w = test(i + '/lib/' + dirname) - if w: return w - - #waf-local - dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) - w = test(dir) - if w: return w - - #unpack - unpack_wafdir(dir, src) - return dir - -wafdir = find_lib() -sys.path.insert(0, wafdir) - -if __name__ == '__main__': - - from waflib import Scripting - Scripting.waf_entry_point(cwd, VERSION, wafdir) - -#==> -#BZh91AY&SY=y `"Ѐ#/!2Xa>w#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/Ir,iW5*C]sQvl7s_{E}x}ޥζ=╅m۶.l=ݻShz|.ۛxo9v>֝|4vZ[֌n=#/#/#/#/̀Km`{R=`(ݻ{מGCVnW:FREj#:1PIT=Rj*hZ(y{ϭqRwupl2鮻vn޹O- -}ݭ[a[˚-nvmG'Kt}ķ٣_c> #:#/]P(^u^{!#5W{ l]3s:#/UPKcJhdu{=xvlӮ<޼s{{TdUW>͝nup;Ƿ:ԶwzHh#5{(ހsZG[tw!#/Iq4y]Rov{gϝos[݌{fyUvw}\zewu86{_p-zׄxJqe9(h=P@m{}r[|uz۳܇^}z7=#/s6z>A@#:Gl*#5-UkjNjTr=tw1ɮovNE`pto[۰ҡ־>#/w#/#/o%|{^sݷ۽﮽_w;ڧ(NEu==ڀ{d7r$;z^;k#/;{Y˳(hy{wsZ\o6vc_ouuz޻on-`bPsGUiZ}{ ͮ%wק{RUkTS= `3{#:to7׸^sέ/A@ATk;}o_Z}Vnuw; 5y#5==ɛ/>.`:[;-};L#5mz9_wwV>Ӷƈ\\ݒa{]n&{+zeW֚y{Jfy0ѧ=n_}{]AOSﶫHn3싼]SH #/ #/h L#LM4E@z@ #5#54A#/@A0'##:(Sz<#5#/#/#/#/#/H SdhM&=?J5?&ʞ(O#:4#5h#/#/z$A&!~bSA<6#/#5#/#/#54b#/#/ 4#/14&4hLL##ž=M M44yCA& S1&F?ȆȧoTb(~d#/@_3~@G|֗mcA5j)",F #/,P"ߙe~Ŋ=be\L^'SuRĒ`Ejs1fO0ݘ`4 8$ Do%%ljaJH97. ӅqY3ZJucĵ1uJUcqjHY*_3,lB#:70f%]ֶUb#/VAQKED#/$1FlD(F"T(%DdTEADE\H ı &D4c6$RDJ)[IP%4mJADI&dJ% hI)R$%#:A0e$Hj2͊X#:IE4fţQm*RJbd2EE+X0͙jɔbT5I%HYI5̍HhԄlfJ4 E)T͌LQ3Jbi e6 #/CL"QadH)bY,"d!"RR&H6 j 1l,L(%iD,LTlMe2&F2JQLZBDbL!$#:lFlH6AHbL3b $Ԛ)#"&dFiL1)*1&i-$ɢS͢4(Њ!JHFh43T@)E%!6ih#5Kdlh3H $*$ʂfmP(̦R#5 BfƘHԙ)3#52RFʕL QdA%()@02CL"AaZhf̲,#:RMdFdPY(il b(llfj# JfiLBCaS&cjlReFLIB$,Jiɥ$6MFLC dXmd$"Id2M4#)-%*4TmȔƒf(i1 !eF,ME#V&ZLIdD RQc(!UK[h)-dZJ#5S%2LcY-h2%)EbQSf24VVK(MbEF̨(-j"JPM$k1PPȦd62J"б2f5-,HTXfe*F#:5"lK*2mդBek5)Jib2"ƴiQ%Q2X3F*-m %lmQ"E!PhՊa&BMbJ)FDԪ5$F5!cTɕZ֚aQșYffI-E-%3SJZU+%T ښmMi45YJQjiFF"FHFA!h)`Sj6Ee6ғM$i)R)bZYR hJdƲ41BPرfHcԖfY2Ȧ`RLX016"(ؒQjf6b0hɱbaj4P(B6XFb4)0Va$IM%#)d!IDI&J!5QS3)IYV4HfIDle#fiʚ6-e6hIL*ejFLd6D1Q&J!"Ѩm4dJ#:M"LlRD bIR#:d4!FFM,+AIب10h)DՍc)#:M%Z6CDYfڦ42j-bJhiJ4*Y%IJRj؃TE%QQX&fbF[!fA-3% уi0j%2čT4e-QƲB (R6%  QIIJ#:-)Si5mfbZ +EjJ,Sdэ$&6J`у6V6-bi(e#:&BL,ڍhkImJ3fiebUS,UMIeFQ4eMHTQY+&(&Rm)FhФj)(hf,"MEchP-R٫&21bEVlZ-dֱYTM#i&0l,H٥DPLđ$I2"B Hc+4K-%3Ka")5pAILTD4OEpwQT*r'tF|ힴtd7ɅhiY#5`:1vD_<#/vǟe㌺vk,DGXA"!v ݩL#=XB`V#:4/֘!ۭkdRTx}+qWd]> d!cA'$#5Hi- `CK.m" "nc #5ؓLQn#:ʺ--Jޥ%`aqR^6eDOE_lvv֌* R#:y:Pt+qj%YR9imDHM˿3*86e4t #58]%I3o7w+4&TX~\ $^7<DLPU QKڛG#5ﯷ^Ĺ{R*ckcm 1-5݊4dP|>w(h#qCM*Vߺt\w]uj. H(J#5@D٭ dXTZcʟ=0Zh摜N`6JFb?^1@«I(j}m|c[J~1 PYO:Oٜ3vvR]cÂ&B#5t%}oh%jv6 }91ΩBQX#5:t,XZ2kՍqe[IXR/=/G0IQ:kE5ƔE-bMvAzHiLgg¨Y3LsfYr#Q7Č'HqI22Q/^Io]v~.ܻX^9ɼW.V5_?JY0Q_Pd,^[uvcE ᤤ>uIcBEЀ^uE !BKIIe/y9s㌣~a"4"1S4J6nUb+Œ7/Jm} p|#50#@6scQ9daҶd``ADVÿK۩2F6v,&Au`@z!bZXi!B P)#5d@-Q4,JgoeYRLH]ͼHx%D 6ZqIv4hlENt+<{jL(@XYCC5;!{l-U 8{%pX.cX>>(vK#:(8B) l`gC'0m6aRC/ߕ|7Nl8]Y? $ӂEs¯Y| Xm @=}Tm؍\&rZf[LNꔂ=omBKTWf!Ώ'&gva-wRcJ4}GTW qk|zCi i=Bp#:P} Qk]xb:*xVMܼTՈȠ\]\;m3FSjBщrecs;0rx*KNWqPR0TPs[SLJGd(v!҆=aEqR JGcH#~DӿXi^Z#5ndz!& +M#5jdb:F[(~D4<XJ wPcwwg|Xl+"ϻ!("ͯe³1-eQC,xޝfenݱ>f zie9J@["Lį/<'W=9#:}ٔ=u,`: kvsM}zUƂl,y$()5ǎy`2LHJD]#:#:F=E#:N 'O:퉯mO1~+>;Ԣ =8\6irt{PAFh2T]^3f ti\.ie*uPk( pK(,z<ƣ'0)N1ouv$k{#5bb))Dގ-FBdá#I֣{ajC2!qOTe[$SK Ogmx*∬"; )Az*ۺ7ccLõ\.~GIσ~tl$[(-s;rBv·2w)L媷q,WB"t'Ňݩv#z1s!ǡ[DZ(sSQsƦͿ5(߻3]}1,#5_$쫺#5;#:%oӋxIN$>`MYTSt#4DFIm/Z?",ä-}_ғs9-8vwFǹҡ5ի#5^S5bzk^c`ήn~i(8*0Eb{L#oVb?[&5K42/ci҂7dBU"s}h)s!تq=b#5ndt!R6H&ޛ$'u0rU8˟U+ܱN.0}!3 InrMMݒ3a#:XDHn70wPC(Quf*aѣ#5wg^x¬(fU6tUx2%8u2|M(@V`Xd麩ĵVΨ6#5sxa?FE(Ϯ{,D|ud#5z(1|ܼSVԠ-en-4:jjM4tF"s$hA`c.tDz^%IJUNjÎλH I'x!3\=S؜]%_WϞvӾ$Scc$9dvY}[30W:ȗYo=ؼ.8C_%?vQsi]z\P ֖GR}&8)h(i3U<L1ά&}(#[Q.GK?}Mqڀ667u-ؾҺL;m6W-|(%魽{aoTG%,G妏4uʭxQRtL?>VvsΈ^tiЪct|p (E1gٗZͪ24k`8{W'R#:tJ*LKCA5vdw9KJ\byZQL֩rq&#/Z@]Z~o@%5*g22XP԰#:巫LH_ jTǮ QoCtޙZY{C G9D()$iwCL_'s%imLvdQpŨE_#:!AQd78-y+'׌}+|}Xg̽)2`Ǎ˹|΃Jo㮺LR/d:?'kiűtP~ys5x({X5' 0R4]m^F)<;GxkpmlUc5R߇Y;gX$Mbryc{1Eq.4RU'u:[+Xc&hQ~mʇ0o]oٲ40T*aF1k,:Q:wBQP~5Sk)M+0iu=UƋZ?yKeJn-a9aFؾym2k 4W+BKxEl\爙ʠK+i#}zNeR&#5!2ksz;9mAv^ʳb(/ޯ#/'s+W{|x5oUM/+|u})]R\zk0+//~#:<}sF۩67Wtě$adħ%NE:hZ@r(=e/ E/o#51M{k)xhi?u bOês/oyٖ @™rߟ5{_~rZI :jK'TZkoCGh>jϭ5vF||2 #/ܫ_fW1!-88g5|btx;9?a4$u^<#5-|b].ZCQh_~-L0.=GTvgQInl 4S3Lff|Hch2*yM1Ba\E2kIC'j$tO]9#?f-ŅYQ43iIav%.e+N#5$3t%[ 1E#:BZL5#Y٢hq53{X$AT! `q7oSCCӮu&$N < !x#5c8㚰B,+*EɰB#/R}%0JVy!^|qz< DrJ)63RJ6L?_\yRO$j0GX#:l縆#5PFbin!{>W> l`31n #:t/E ~+ijj˦oOb_#qyAyZl"m8&/5l |@y0\/h@i2sf^vofmpUrcXI&L&ۑ.r#:b=+J.#5Kdzr~czUwly F9pt~#5+a2:W܎a-<>+zcm2GAA$j3J'_9.<`7eM 9ij{pIY(5}**>ﷃ[E$ (bKJb^ "wMwUr4đݻ[*fX+|WpL1 "VYX!hz1!YKmQEĀ {*V\"iGNkĒ wH8!j(#:)9$`v< q7K8Xp#@Xf0frΜrxd88>8ه4#:Œ*#D$[0oZ d0&7( wyy%CSZ)yG_Nc \CL릉7]F|I(#:y4Q%fvTMhj}̑Ճ{M$t#:wfrv9h-i 󅦌DC,u51|T+fsBRąE#:qnF@%lPA C%@m=dQXP_4&nw]?'jXYîɀq tlk5*kc_&5kxQ,m* BI fHs$_cOQa;FX'o #5nB!t-z |d=ꩵkLw33qEI ク8 #/Ib".(lZpKlӃ!pJ&ߔP!@rK5۱zvbC4Ʋ$@-s#7\3"\ո6ܹ2$F((vHloUk#5 >BC?i`?x2)c.D0¦NH7pȖ>}o?YȠ@+jLׯ9P4(^%;unbe0Ok,Mz5j0[ͤZ-ktڙzۯ^H`]F+% 끢ZEI>ꜷ 8BttK$Xj{PLUC%*OQOy6Soin#"Eot1-E]C#2ꢦm(|?u'';77cѹ۩-M!Pj\ph;pIސ'WWr@wf9BE1.+Qcri_?E Wl}>LÎ>InG1)!0j 4N_/ՍQ$ 1ו_;n.WLrY^Pmݻ~Q?I=ҿӿl3|=mgb7DwrWA EPԴ{kG~lEBb3vL%+1"Kf&ңkMID#:#:J*PS%BЭwKg*-LzpM!` >sEeEWwm췍o^=!h#;i4?'+Xpp+?8!w{a߶濕qS`UX[CNv{˺0*8AB 0?Xu[:BER=|& :HdERs#:~VмkmUEIqAj"S aE벖d?C?oͻ6'wύ[ʨr(Bh#G~$ʟ%и2NZ#(R= 00_?ϛ˕drjptK%XGw(@Wsʩ9IUA֯yV>ٟ:M^O#:8Qˊ3;Us@ٖGÄuzN (wgQwS)=nhF1]U}VZcF|#/l8h7Kfy&rCCE͜*ce7x<%QpjsJovsDa)pܚ)xZ%Thd 3#p&WIFw#5lPogz4I*޾]Ϟ5귚PW\[;!YoSßӧYߧ,dNQn\*VSUO]u\wͱc5CTȔ׳_'.6ӾhZ4=E>]UExӿUk,<_*;mɣgNG˸EG7*춘z*OHe6SR_+h9S!FϬm'{9vW|bg Ph{A| MRJ>¨ugre*딡1ŮihxRH0vV~8Qն;=h__`.dʛC[#g(SjE3W|wiFNLix?eTo >Ͽ=t?=)Ϸ}Gv(la.I{+?6McdgݨeWWZq|M#5Öǧ룧^oOk1 )8IRq..8$҈1YWچծ `\"0'|XaL[@LF##5<?G{>/ѳYyHM݇u3Lv+Y2׫fOZPy˼nWe>ǿOgu{.X@:9?qߝk+g>v>ƍS-j߅'[%ig^;3owhbi2Mۺ#/>~~5m=p!?W}Cԍgm|J۷)pz/}6GUʒeUG~,GU^0FOjWw#5G 1ݦMZ#: ݋ L6θ5-gUzt2V:1)]RhVqȶ#5HmU+bz*x]'RǛTꮌnK߮_J%Nq؜M6$(A?l%CsB۔ _4ENB#5˪Z2[O#5>jral}Z z#5jMs;4TCmQ|Gl!!͇g>|'9])SheYr4gj&c)}.bݑޱ?qu}Ӫʆ\hJѢ%?6WkZ%O:>q?p߫t1/jq뢚kzл{klO!PM$J@-HUGң/kyz=}kY\Ξa+jNz0m!dbы/<), Z@]/whp'O_袸ʺ=}?ފn~:}Y#:O9 TZG#5O'KpE9IY#5?c]"VYaҷ%h]u(  @CZ SZS.S-ܦmMd&0v i#lKL攵EjYS5nxy-Ń"6##:eŃc²֔Ї+ړ{76`F`-cU5O2˷Ny^Hӣ0&8F'r(` D4lE4 D ,@8b[jARE)@lƖd>&:EJDQxS"2`qKa Bk( (EIui0l" T=TV҉ӕD;`X`- ejP~AB3;)nn&֤iխ,2#=y|̢PnOU0G{QƬ:3}|[xao?#576cꉫ}SUWKHjUI䱞i#5Aͳ퉻V,rCu#5Sz#5LmKzgN T=ǶcsdJ5xz+]fǏFNtlS&Wwwwwe\7UM}IAuY1ISTYW|gpTz:44( @aG&DN=Nr@GzY}UL+#-aJ7I(Aaa BM=|aWqX2[#GS.#5rxN G4a -a)VKp(Pb E#5h<ˁeF#Հ"D_^OLjٔZ²۟qZ7'HD:ks['??sy9R"a1D#/n#&yvPتq!#/aWw{,M:cN~ y|vkH[5=Fb8HV/y͚'sn;wI%u̫#5[Cc<4ԡ-oB ֶ?4fyf(p&BZcUߛ8Έ (YU#5[he†U3U Q/b%?ǚdzH³8!'L=߅o^z?_Lɶ0#:,FD4\(VA0z&uA/T#/Ut0fS_V-[)#Z5c Q$NViGXSp##3 9%w-QC(EUE7Nٴ\ qyU@Q۽`DF)e`hp_G 5EGK8< U ίfdD6KELAAeMDAbrQeh)P5ڔ4w*pEت%u±&vN.8iCFKm#-x󼕦bZȑ 72)(xZ}JvtlS0-BtSNQ,<'o:ElmIvi]{{vBA%}c1CzWtѧM74JKڍ]n.x<">BQa(2`\#:3Q 9m1g’ĚM|ˌ/G#5&Dpi2$HFOGtg\#:WEwƍNpyct sRưั -(Qcb Izv!ח J1iHp4G4DAFO}yh_i#O꾊ygӇk~Z"#a/IrҠm>&;L.Iq*)4Wr+BPP3JB2W_`:er9q UTМk]ћ306hWoH33L- ީ@Q.E4[Z|e .׷;9#:AhJDwt1;CGL$E:Z571xq2QfĢxF:2#:(EN,=J|޳kg9ѕ:lCbsΰĶP"SƮ,llff\M1Q F8l1%N}wuCbŻLwIgv_GBeG ,{aW'jfVq8X<#5b s4 %Ҹc#c#5cnFT{fV#526!U*!"Le ڂ;3S+)|qیb 7XҚG3n4"TeY4ui1L%iq}76!L8t2a ps.j+77ٓ"5J.0ͅ☒u@Y.:73b24&Bmw.Bf.d+l:f#N?N&~ Rފi#aZ{ 8Y}ezO- 8r?#5Z./,R_} ȼNWȮƪ}%g0QoS;䚜d(NL4b^13#3(tUb˿ $&f>iW^[o1#˿/fçz|<$[b<pg3/ZVSB2#9MZX3UDZĜ}ה-bxdK81ˇĺ!lO#5<͌aV7u)SWY>F4cw6*:$ᕶ;e3=}4&3RRn}юL3v.PEgvGߡȦY1,$24&/o#54HskW ͽFy@ݓ[jpxhl^X#mZΘHe+A ݻa(΅T`pǡ]EpǼsݞ]i4b##:u7G#WV~pP}o[A]jUeu#jbYtQQ#:#5(/60]ڶ}] g]$p/^ODyаHe#:5вkn7z#6U6x|";3,Q3M>=S@c(÷YebIwC2tJ?ZYz5hk8ㆢywDh#53Ǹ5-sòEGI:pX<'-NXj;/麔[hUv~ɖ&]n/92l娍%&:-1}t$NFQwȺe#:4V(H'Zen0-7QY-f|Hg]IC$qgj_S'ݨ^</]wJ5^%2VޙfeI^G`z{pW 8hv!Ƽ1׍dv.?dȽִ}ug2xir3Y12֚?=2R$m ;trYCOǎw (m9{ TG#;= NgpYQ!d^XA<(r1w>hףtd1k/M2C9&Meaϭ56L:8}ۮB[|mR2J6O+9ZWty -QO(r#5OuĦ/7mL-#:죵XۋȒǧ74mF"!!+ӉAJkd|OW}aY]-j[-r\Ԯ6ÞʕLf`ADŽTWˏU4#?5VL;q K#5Jwp[|7i0qRG<6PvX?e7gpM1=}'*L(8p9q#5/IB]wc ']N@Dȅoʍtȓe k0R PM3A|cj~gqwFGF"[1:ʥo}ސ[]u>3 mtW!N#5~#5Sa7Y0y^Q!ȢZG*'fsWBhɻk"˒tՌie/1*XpygGX],pdh7:zʣtRYN'򇟀,h6Xͱ3[,s.:eĢYm#ٝȢ^g0R%؎7$ĝtK]Ugzh;ISjFȾsYd'.Kڃ['/fܢN{8Hʈ#WXF0ꙖRhulJᮤLsrDZ4/5-v%nGuo09t0lZ?;4k>XF,r3N4Gtvtԉx)&lf#mݠ?qv|`EfšZբh#.u&:C륥_ra}nG?nV&Oo'nb ˑe#:R#:薫"PyB{v42|4Բİ):veEˤO#MoulÞ<,vvQ[t~P5_ZbVOB1OqSgO_#5bI*-vS-:c|ھ|yw {?,ro]5^o&K·t(=!#5W?䑆b[v*FH܏r3E[jjOZƱכO0"I Dz7J5?+-TOJ#:$!?mcUyR=#*goyizAF"oR˦ǧ_o]㤨žw+/0*U^x-ªW\.E#:ۚw *a(K?6P/`բUIJYDաe-0sY*QA]=*׋*'-9}-eι[YD3;a#:9]4Ndb7 뾫|g7> ry1?S82xvBcgdGmZk<y|g\vs[<볪Wx#?ug5L#5/;tQ|<ވ1Yn߯!߾<|qľKf켹A|MB4;*~>q>yywnj=N!&<*Un-UUXMvWN4Ӕ^{s#Aj2R֘jayDS>,-:x$B\ r!u0I;0Yg;:.W[_nV#5 wLxh"9G6*h_hhu*T}6{1a]*W9^xkzeM=o'8HmƘɩk(nSagr,4#:Rԑ٪Mz p}&MJE6 (^2X0IuOݷ<۩GsTS_k=#: n*H\)L$tczcDT*OxCL69%O]jc9oV1:=B5q- Ch^#:k,">noM`D}^cV"tB^1<%ul[_㬠O2Ol.|Ag"B1yGI&#:=[ޢXX]SDp@|}56)RFuIɔSN0֠o$\ {f;QݠǦaUp~[J8v7)V5 }c`}g_~ޙjw-txUQ?V:1[ڭLPmtaUBA`vUVN.Ztͷ7c®etrµL#5Q#54B+8+Ǘ|_%>ݞ/;Nж~VsY`Fyss< ^j5a#5iij ]/<#:O`z;$2*k7FY#ntkիVt(t\RB=;[F%~=7GI*yRx=I#[< :]9 RPiix#H SpVo⹤\XJ^\hǔ'4(ŽPmֺ)hK.#D[?O_3Ӈga 2KorBGN/Uwsk$cyc莊z}l`KQɼD?6oC~69R[`'r}(|;txI]M!hGYM,ՅMJڳD&2"n'+ɀu#]TLϪvK#5ؙ2eS)krD:W䎮Nw%#5'8ic KƝ4B(}>^[@)Ut^(ǣM%֙CWo571A h3*AG*wjO~W`58Y_zsmSןq>VqwnhI7oK[e]ح&޵[M6{|nWpVW޻w=Sߎ6{p9ˣYyLNCUu* {(q"3E {NFρ|N#s\i9O){E=?Xrd7%(gdD³7fBOw#:7[M(Y$EV羔4jd$;Ao)~#JAj.Fh>jȜL*ϢtBzslFtc]1z{jwl!N#ԡ/*",w{[qj-FuwH"ȗLy.zR*#5)E@vc,U\}[ShSlxY^\hR =s՝e\}]XX8ȡh1{Հ8{[YUiaX:0R E"_.ރBVP+nRKe.*sFfr1#50x]DrSvZмs̉ݙѳ2٧wB4Gߩ/޿G|~Ѷ;=>#cw?9^G1dg=rt#:Oy)tAWS6)??3S^)(% 4Q'IEEC8NV|a/VoI% ,:#5*pE1Bs~tt/%Ԛ@~}~l4Z%^i"&M!(.f#5[vgZ!odM_wǫtR#ako>[;`m9!})6Eܶ$kk&f#5AC٠y: Z@K(Z4rBhүUhC,i*L"S8 Քqw"˶5ڭZ?],6pi$C@(7^ Ux7˜۟{t6U#5P}tVpPHY#5yBP-xc3Y+`@Z}CݛKykzE2I=*#5;ZOUqxspth6؞v-MrJ"O7riS" \BmMT@+gA?WjCi &Γ|pl{DԠV4sm+sp@%a.C}'$(s\lo~gD;@↷=eA87J~$Q3DFۃJ'f&NAtd#5aڄDՁd'B!#5PN|,ֱ|H tCǪ3f(VnSDE ʂѕ9nE ( Ӥ#:W-QyP2[籆9pd}śu&n|*M#:WzO=%"kvo7] h2QQ_%X=h]{늌ݗ{ӷ9]f#/#5$PS}L.pX#_Y(=ʶpgyp H;#:+tV=j Ȃ6pA#:]D6!X:zDJgwG_bK!br߮B#:bzApwLo!7  )cPwpzC@jA}nI:>5?P}Pzh0!a$Y9&.ǎ9n/$uECNM$ PkM EBy[jI0mp\-4qĠjY-l|NM#HȰ5QC:6ȼ|.efCKVF4yi#51>=+aXXIQnrQzZKۻkȒ zlMlPՆ д0bK< ӌx\nךs 0< f^((M ~eWzH)߉6 b۳Ə;m iݍSr49j(VFDT,&m(ɱY-Ѻ4Zj;чqsӮqӶ JvkEmم$.繠d\;='0K#5D/4K>ܞtΫ#[{FB}Ud/XD#5РS9#:㑯.quk^gVt4ߺܱ2i>#51Ȅcc13&bHԙɝ<8ͦ7LP+ V K4I"Nԙp,FV ]`/:{( KȎaui2r`OSdM|PxD?妼Su"V1߂9 *..4Ĥ SwO|YCnrf @YVx3+Birk&$2#h*Ӝt^v;TZB)À##:)+Ade8l0h@9v%$S$I9XuIZq)3u;1L.>~,ԂS&zwزci*m5F5ΒMti!wSm ';u8X5q7P;٩Ɨ=?Chh]bJuӾoqًE3}[7M*]B6 Fh#5@Z.6qT8Y3tjuJO#5m){57MZQ@RSiϿ_R-MIA6Lm)u_kڲWcP`Ek(xQ_>J_=~L!<9k15@[LwFMw_s;u^^X\b Ӹ;jO#:~ZjqTd$ߧ$Ʉ&~]\2J\#53s_,N3Xfxq &ZS>^QXCDC a80`3|q )ogDݪi@Pt81d Z;PAg`rUį^獒Gլ8]>]XIT3csc7!?HaD3Cq\{Y`b pgqF{;Uf:W$$)vα}]7y,-==tļ:)DI'>I "j8/`sIIkQ}ɣ<ߕtTՠH6&Ggj`DP},8b3I&ѪukR!9 Esob Ɏ W 3A~?,a6ͽŎd&y>#:ɢ@0OZ!a<s r qIe$Pkk)Xn-R혫dd#5=RRQ ,tDAe4, -@nMl6 Aд.s0g٠ lH)t&SuS@C$@ J u>}0ev1xQFjhV*?v? []7_ޒ~=Q' &a͂=?){Y#/p2,8 GMh')jMSŧxTTxm V&фzcq]gQ#sXވ`jmWJ5NPb"ŔDu ږpCW/E8HfQƞϧ}Dt- *nyV#:%pBDwIJD]]#:ʘi.^.w Mk,)?nS<aMs8)ط7~yagf,  >~EuR-0)oHH68P)@4!aQY6٥4yᄏֈTTbUBͼs#5h)jRYȠ)0$jRH)HCe}#5qwǦq89NsC37ś`.%B)ᭈwi/V4χ=hG 詆BI <3cAFPr9PT5(=#5Z-Vhem,qUEti=F8 537|4MOJP#:C3vQw_esx 6$D|@t|H㍺(qݺˎ3$¨QFQ),Be%,GMq8FFJL#岒R4UݕvQZ(BZU|s|y-u؍=ɞ%/$B(%w+ثvQt;dYϡߓW so̐1o_kK;L{q7MD3'fr@πjȤD@Ҵ#5Hl_]=bMuR,P# s1FDhr_,\wwаx# XM~xjtLb#5 #5}@N"U4"NO{N]_.z 5 N(,Ύ1$usjl*S¸p#:iP.dc5)b--,hFݿr4wT琝ͤBO)4 +tj+]-#/v({,zD #5_g`4Dy؛vu[YqδM;Kw:LtS#A27J6 A5rF+:YWlƽ\˝RN}.! ۦpD8ؖK#:) /j]g[U1f*ϡߒmw=-c':9.UàUu9xp<#/U7U4w6V#/^}7Lq}ȲF%s!M8zM\"δƺg#(ĪnWo)cCI2C#5MxOOQ)$Dc$ ~Ǣ)<jcza?4?NH8:T?1KEqtڮ[5ԩzp) "U.>enoaJ44 r9`&&)Dnçy<;NyLz_&A$z%%?(Rc0E 1+9|>_ͥS^y0j9\^@s_蒪]$ g@Cxn =ս~O"}xx\ E>LK#']=}l샺[sӯs$|7Ӡ]outԟpY"yعl۲i{cqۮ1:#:nOʗк_ M>cԥfb xwˡmbPCdbL#:tSw†IF|EhɌǃd2NdJQ*Y7[x֕QT{㖂Q/v~:Q;9|" )h(ttD:QV_ #5{6-%}ZA:j30[_1=3N1SY~}aF6P2B 9P=֖WT9MbrUqJ'_w_Z:9:~ZVKڊ0.tҧ1&~Av0Ð>mVlG fn7U&f9W-uz1s6$F]?qvѮ;p1YV#5&mh wߔ/BeoBiL?cJ ~47$(dªyEjybQ]p'6 c#5җ~ʽX!I w껁}U/;SH:הa:G$do)Ҭ /M,L{q9x*vN`[._69Ciz#]7- lJ+ȪG '1k |+ O/2̝HGuyÿGዼYF2)qDŧ' M}ДzhD$W+OcJʩ+<29yj+@Ο)7Dc);#:1+Kc:HrްyIaygE9?oXlAiV\~:Vh)s.Gg)?di>mGK<5`O/D(-_ZA~#5;?7[}+4h_c:i^#:Di롣TgZx|3?V'֒0j/#:D4aN$WO6]4z 6vQ=oztST"pm3VDڻ4B#:OlDi]WXdvyCoMIGڻ̈́xQ`yöw`WfpksL'84G0@,޻IĝViL5}6FDEɷjEI>b7QӎI<}j 0I&8 #/΋eulcLg9xф4'CLDt2W9o*5V~#:i+hfVr9d855.%芌|j+߿KM0R{B6al?;?}&ݜQ[#:yGn>,Beb6̰18)L4{>%sD#:]d5({ܺY#:,")@,fE#:kI (s*7 X (``WҔg'q4yusVxxgrLWoę7*|H†ҔT"@/óowѺu\\fN\@TlT,EbK\kfdhDWA+Gr#5v9G:5ao~$\x;Cgtit$v+!6>>W_ 7WP*gc-b"qa⎕Uâ>*Jγ}a.N6&rGD#x}2sAwi6묍V2U ;ҨY$S+.HL!R2j0+m t "D'*T=ݐoi Iy|l/g*4:#:,}U9vUK4$%q ޹ޛbgMCƔAY]BrOVSJ>wst]liS fS|PL ?"I Ex["Sazuy!6irF?rWQ[_?b~6JS^8D9w&rĮ-HՍ+%^ڔxG}guWIҥRt&w)/im|SWD(L#+ Jl1hg-2iA{~FyL#:q&tgF:精c&CV<*gٯ^Ԡs˫R>L&*k|S؞ Y02UޱOGs;'%2T꾪+U0+4Qa*U/8Md|مR+xj6j7: [8BcuRk_izOC߅D@hE#:li$`fU :k>s]5M:s=ŵ >%%$uo9 ͊YN̲L5`d.#:S#/[r>ڀvwq5@sWF*z+џ_VEw"t[D$~c(9B@۟Ni8?[5)7mpD(h^}~<1vg%'@0|9܊3F_?2m! h󋱐]jM1]穸f5~Q%ٷ#:$E/MžMR_I2[*,3)1#: -&0پOW,JhRLh<}XUcY $w\LO^eVR"ĭ($aɀўIhmeCH.jvn{7/T;g~MPM5VxΫ486qlPC:+?@oX?G)iֻ6![roE3vu]#:m ĮyeM9=Sm]S!i}rg0 t]2wɵΤ!%0PbC9Aaas*d}a)Yڧˍ03dRc1W#5nT?]Z ::.%LdN0 aj0۩nL,Š9HŲ٪t8-!,eeڔ,՘'1]Kt#:-uB4TF?arC]˭Ӟa{֒#BBd%Pl ii#/+=Ñ|ywե1#6'#5qS/#:ZT i۔yg3P:i&CW،ov9+:ƗM映]~RB)rZ:[Jȸ*O.`ı{17 DAomzt7W[*u]Ц t;;2+^1&I-8Zq8Md#mڻ'eעonެ-`ox!{!ӪX݄#xd6m$49)%RFXQn9[3jy;7$>ЮZp¬Ko2hgf oc\^nv.ɇr 0z<WT#:I=ƺ1~eUgYt(%n\fSI~.AOcLMoTd )\G:KvǖDYr,v:&8i5RPwC3g$1Ajrޫ\{XV@w2,©g6|^[U1~D]7cql&h#Z@?[?pGKYq0vaI瞛>}D+];7MPlW082O3NfocP}cJ}nUڵA%6\X"ݯ#/Js;9Cҍ8+O?g~%~\WR[w5E#:iV71?bx]Me=˿~qjo׏8D~]3ʐw`X a)Rno%?GoVQ* #5}i aEUWˉeUM#/;)bQdn$R"eAlu{>.԰& bNC&7.a}^#/Q#5kp"#f^V4v@DӴ`UxBc#:(Хq[-BG_@Q;GORk/O;|=NOԾtQ#5a#5"逅<>DS@:0{=nj[}z>#5A!Ϲ0j`p S0ѥŏ&vOD[^Zz YBl'u˜#>bOtWFހ4k%$΁L:(Eh^d?Bߙ46Zy@<O6SA`#O"}1&9H$LfRQZzk4#/\P(cD&Ms_akZpD3RfOu&EOİ  NRMHu$d$[c˫)sA#:cR쩄>E>5#/ k ưcChL]{&Z~ p;C6$ M(@APajWc. qR#/ {clČ:nd $tDV-u$FGd}P>#/!#/|oW#5i."yLl'/qIxCu4C+!sH@g?7H7#/E!RK뼡4/+5AeV6 xt׀M ,Iבg(tdxdէ8gxc}F㳌 huq$7sxcQ'aٙL;;əWR>chő >pb*e?m=: (8~;ek=,Y(9X}ITHɠv r%E#/n:( T#/2ƞeUB4B3XGQg#52\c0G\q5).iOޒN6tEg7n$ww59MQ_q'Hozùזk0Ctn4óY#5dXqFO#5;K|Ob#5O䄵LNxR}}o#:]}iي@>#50[Wwt$Nҵy#/v#:ԑbdHW[HzT DM}vܝMdK b#:9JӥaQDmM;5NC~W䨰*JUEX"<CID3Dи?_J ~N^$B%RBS =o8'S`dsC8CdYu^APjѐ~)Op޲XOQU:Υ,߂AT #/8РU0N%ffjG3;gMG'ʠ1m};B!<C|^Ga,]L1aH 7^g&D ʔ;kdYb1§Ťj" nXG_Û溫]8sm$%5Ro^#yk"ueU1eX(K[kn 9!3\i#5zDrnS1>qr6Aor}E~,#5PoߦiH BS(97kx2k Ԓ*}.5jpyArd_`{@YXIR9p9}ZYd+ >CEN4#5.K6/#:ۖcXRd|#},>A7.7Jc#/y QLz"z"#(r],C `o#7Z4AX#/~}, C Cװ 'sB y8É~@E~yݭLǂvB<耆h@{UϮ y c:o1/*]Ynl]3 ;=rO#:#Pu}-*ځ5>V71/`0F=ȝA<#/$hopp#/,4j|x0b3q9lz#:`{Gh$!z~WW1EZ"%mV{ַ#/{̊7yC:_GajGuFsy?Dt=P> ! 2nfh:p|0mWyx{Jz08J_bn$5-SCNJ6F, >SC;񸳁=})K-[rNNdމ>+1#57ӚHo:0CI4yZGp[ЙP-t:aٜ#:usZNFxcʉqdPB:P>lQ,gz$Bw.ڎHJWHf7Dč#52bD㳩BC+>!g1bFhKJ/dvf}AL@p 'L".$BB $C"|5[TUYVQuǰ;}G@py@Yfi.f #/a;va3ŀ"4UJ,y{~mMW tV7'K{P"lR?궴UeƆr 1ӍfD;ȑzI8R8M Nږ={"dhT۽ _k@\Gn<@'#:>Ȇ'$tEDRT0#:|q>.}EɈe)}a=79FC Hxw;o]{I3` D5C?/Xz?#?N} 00{]"gC M1|.[v=]Fe31Ǽ뵝ڹkZјECI!FЛM`ti$l2BCZ]ku%p̻O h1#/_w$5MvS'}iWc{ZQY5]m|r׶f2#:B:#5%R#/nh$vAeR.3Dј{#50Rmg䢀[chL`hlUflLh&Y3C-dd0iH`3iA|pCtQ9\t[}=. Tu/2ldo`}_R4?$iB 9#51";2I(m_ >#5NP O0b!p@/E? Q@}ȜFuՒjJ L,!}Ɲ!`)ʄ)O#:٧ڏ*%Hy6>;YQ1d$XHQdǗm=#:?#,O~ ?:) ui1C"'~=D|Zn) p 0`DDDME(G(l6M`3tݫz+͞]-bDnR&&L+7"ӗJ`}]=oxw>ts3VJX,"b88:Ǩ}##:tvԯHuMd"nʘٰG^,#:W(*c\|)}U)_ 3@͢ݾS&UTj|-V{íB0#:cF#vm1htwD#-"Uh B]"i\&]Shs'u?(daݵXrm߱t<|4ËӇLi9_pw,u/aIwn\nN塤D) HppmX@'3VO_BƧJ!ma#AxAt^ܭ3G]B9~r(gz[+ G *+!=`yCd7+8EϛkL,ϙ''[碵{P{طA0c#/t``m盄C=`X-~?3]$1i#"L#:OYcftL"r7])9;xn[_u*oil1~>_ס~)Z4Izc^ #%lu`dAPHTR, nFO0s1S|J/OZ>!EK!By#/P~= 8Gw(~ML$>|x՛b!a6*F?uJ#;1vX(f#:ʫ3e!EZ\{bsz [ucU$d*Ƙ7R`X)s@:6;#/"v|q!#5&7g8HJT(5ԍĴ-/@?&owj P?r`RA?'!<;wϲd'6yGi'^%߶u˜t̶O)畺Њײ$E! iw4"d%$ F +M3K+Vh &R`Z,-XaX(D#/?mO|ᱛq[ONanAAP:Bkŋ.U&1r&0$݋*7A.L("#5(*̹72XN08@ށy|'Ma}Ąhv>XHDS9{L r(t #: JBT#eMI.ԡ5r=J5?x%TRZEC^ɨR;jNfC#/!#DBa0w1nRPg1 ΕJuY9&ExVQ82i >l?m/LP2BECP #:O UBHmXKUzl/nE4+yB i܂x͛={i #:1ũ3LR3ʩ#: 74N JIaD5_"Rpw)!Ur&&^#5N.#9jt͠ 2AHU$H ԅYHn9,)Gq(1PB=~#:(ALpt8^㿚OD+ND {$} M>82c(zo#: E*oڥ %պ-);Fn 89Yn^K6.2']0Y2D#!+YGԵ@XQA)3E'fb膟6O%FhIOҸ#:'J'.JI"fS")rn"+!#5ɇk!.~ͪ!b}،+ډ|pEu LSH"z A8Ӌt vw=圧#/N&U]LJyƣ3|b$*ےSY#/̫^-_D?F$#:q*?bhKM<__fTeqUzWu7cK$f(Dpĺ#cSeB.eE_[aʉT4#,sb4BU R)G559j#5#:VZ h.|-U#5*;ʚѝeE񋌄+\o0ɭ;- F%r)SFZh*Y^c xC:Zd0%K}(44#5q|1SSNyySR4 A5J-T#5DJg퉿wI xTBgS *:Cco`"6_6:"+pȒrg3vH4"R@j`G,RUO0Xa !W+PXR M6ߔo/,6iO#wz\N#Wj#NæQ#5aWnGȇ(#/.NW(Z7RBuPTscr=#:gvX#:'f*a !Hw;Nly s;x"X??}>D#/5eHwÖw[ѓ!_O"Qij:4&sC*.GmV&9L.؉M: 4Wx쏭^:)YEnp94Ղ*f~b/iyma`&i%ezRROv#/RLiiA@ɔ<5?sww|?}t`rc!l#:[Y UZ{aHY#/YhC`G]G#?b;v4S"c#5N]0`nh`nY^A}$<,=р2H#nܝl5bۭ `c﹜M0{j.0#/BdE"%hcGqhaUQUQF䍸U<6s 3JFlZ'h;}>`!#/HL=l< M j&#/@ qQxW**n +vs5{c5ٕbLgGM܅L@@a0ET0P2LvQL㭄EL8>.dQr3k\/zxRkp0Sk@+Vžv70ޅc'd)sf]mo)٢4N=O#5#:̏v.g`t̺$U&i\4CV1q1ZIfk ,4u/e[w-ĚWW/=[e,kŬU!%* IGҾ8AZPț!,_8$I?U0 m4BVTM@Z\#skaLm"`âH=I{qHY԰J,'nÉm')ZI,(Q܉_͌ 1U^L&ÀHeIsJ+#: 1EşY0P Ƿʪ#}K/`<l qM\DEgbf!'w-PJ3њͩ0SuOm&Is:?0.C#/lrz_%H[K.cʫU4*CtAן|/kݔ;8SgN+ΌɍݷՏ qxָR'Λ.&^4ڂ'kIhpݱ8ssⶳo75Sc.phb%:0b42fg4-!9f#:4Ti&#:,ǯnr̪%\YpR7#5#5 7xWSMNZw^.KlߒF KsZu6XE-vˉ #5$s<ò#/inܦ#NWW##:1hJs,~Bv3L>eCA!HB)B,AXwbrme -!67&6w)A -sbB M#5ٽ@_u51;DB[msSzMц2/2ysѬY͠#:IlLiT e|jT۔K'\z#|`x#:H;3Cyisi9NKѹH=˃#c 7-"ea2$tP:zÆs43Y[cfd˙cs3+s12KCm؃˜"XA`}RpΈ#5a6ǬՕwhC&w\c?^5\t뤱1Cnd%Qvvּym`?.+VL3#5֦V <1R #:g#;+=ڎPTOoqAS3ę1'=iHI>9y%@.'2c[EYce߼*`F/ #/pv"iuWWE2=OtՎD=!48(Ҋ"8؏@$k#5-p)3g@qTTy 6#:2!*gh`>_%LN7.Yjldzq BF P&|VZN6Ż!rޭG &6#5,f{{"С8B.#/ԚùWsfÊ)4cNygקs=1hp~09#5o 8+=c:0LrF ,D643Fd҂ Jj&qqyMx+#5S72)0E Ξմ#533bV;JžPԷN L,)I83.*1Y'43=Mtװs69#/{< QFcs2ƋA+rpo#:7ܱQsN=w٠#ƐtjJ|8K%VUUVqˀe#:tgʁtj(IsŶN(c9m1#:-;,i(k}+ H$fhJBzK"`LhY#5bBG]c8jvIB {7C8&&9Fvs4tӷNB[&+#:t(ѝȎE5Ye5c+מ؆uB-[ҊEœ&Mkqq$RctbDd0b1!HtOH#: }~6 UB#:0>՟w3Al9?S;*z~ꨈ+2BH@RBY$7~܀{NJtB (16YI^bt#/EpFjy2~k5[D*yH[\a qRROPGg'IQJBw ΁}@^^։\ >b#dAz2 VDD1R ('RႰ B bQ@2S2ZTP פy[1Z%Q }f4#0LWM ET9$K*P*#/_Y3i:2Qm,läYb2x[0Jn\8#/C\^'@A#/"P$A o8dU_(0*1D(0M>C_;`LDTFZ(@B՚Wue#5a)a@,#/\hvS!W,J@`E-AD`[]z\^\tu 5uw\csIY\ur{Ih9D_6Jf\4]3cIm}\Ѷl|pCX2A #/0WuOmm݋s8KHa'+שa=\'M|X7B#jy﷗x$>n(R@>""H_w~#5L Ial(("b}D(EBu=;Vw]]3v4#:T) -?,x'{mZb 4DU'gsȦ$ )Q@3| H߿%OI-HSׄPZmPȹKJ.Cx bcEcwͶ t6,hgb`٬MJ0`&̌Ő#5(^eӕ8kI$x:=~>rsP|n~Xye\T헝mWahV7? #:!?*"(:CKHxܡmFQ#/#:̽˦P)_QD3!ڐXM 2!f/ #55@cXj#5TT6-8ɭm` U"mƙs#:Rz 7LذL@;ijey%n2TQNJIa EK~#:]3A툻:k5_AE~=R#5( `ӻ1v\F"#5oNE"2)E$`B#/H0 H~M3 +\jCv&%/Q!!#/E@eᾛ\fS#/u1R4R:A3d@ӤRd;vzjG§Sv~LV!D嗽MQ], g}N /ylJ;9! l'9 緞I+.Gwѣ=1 ۭ'` U7-f0[WBk#:r7%S#5aMߋ=y㩗ػy6çj'NDAM};~:ѫCd:pG[3{ljz-ye38yt, 5;z_1XcMmҞ|\3ݺf=;Z1!LF!4M5>/F5Ye\DzΣ#:Xem#:+MRVեZz+A<736Ԙrfr:#/JQR#Q!.ZhÆ ,UMɉ(ro<0,#/`37gMH0L=i{p1&ewF k#:{-CU=u}ˋīwe5#:'4-#5PđnV#54 lID7*tNv~8w#YR.-ۖvKˋ;,M*VR `agk*‰#:ϨNg:mJnI #:"$]=1#/M1KB1zǕ-3UgOU{v=9^+D!6ySJ04vAk+$ QvD;+B2{#:-K\ڊ s:K"0@k6pk;R1b #phWQ ~$_+3>|SpCEj~`W$NtVFC*Q3Ʀ*\3ec<3pJ]eDq#5#:7STʄsh9M  @a9Ipowo(>g.c>Rh1;q d% >7UU'=;Y qF#/.VE+iw#5^(3ICau4 NtA&N@FA4} tEZRDJ(hib $^RTj#5YY D(!R(< fP+ d`Cbgfl #/~*j;#:ͅՊ[b 'fE#/hi5eum~VF*Ny/x,/.=F"d ]==`ce>PkȅEپX.L=r"q#Z+x5CS taƮ:GdNG#5C?48HP$.[ 2܏\/7<݂=J< 5 Utl~ixPI!AivIYNJ~^Qp^*YEPiid\LDuXý[bF10=unw~1BuaaLC!]E`⦹ ."R fŀ,Om&([#5"6Hqt50H ,rOlGQ?.sA{N}4V%A#/{:ʖ L#D#:b/( ׭qHt5c Pޟ,1#:{6SuRogO^-2̒D#5Dm24KTwi|OA&AMX~i͇tvKJB8ȣl5R0lV3N' bh0eV"Pi鞍tgvkXR >B5WP# 25ZaX" `X4YLcWn;޾t-6HܗNKQXp(1T^uA6ƗJ$I8WpHb;U[;73!zpO #:xF3NqYz 3!^R; :mz_96ݳ$Kcڣ8֒+L)$y=Թ0_hYVg5'c؂wd0 =%̄XDX2KxD֋[5mUk Z*6 "#5HPϯie 1`ҿ#VIen`>nYBH#5*wM;mRa LmHfI,&ش)2V"IMX$kۦ&Jh٥)VY&Q3"#/14|Ab#:DI),6$h%%)LQ1ՐeM#%Q#5 )A@Efr;5ۥNP)~B`#Ʒk_ƛ)dhCqgYE#P%ré!LVߖ`H-2h+.$Ms9oQ?<}/%8<(~ØO˅դphҟ-zYt z0$}g=?ʉShl1#:n#/\2#e+tc"E`)iZKfJLLH1QF(Fhd쭼en_Nᕭsd[fhyI,~_M{ޥv#//W$F0'?<(a<׸'x,GGqD!bY%'UQau`3.ˏcc\vGa{t6#:!;UM!B$!LX031`f/#/dEBYQ.;|=Fg~Pxq$8Z*V+φci~-Ci'hp-vG R jOϑ/oCfLX^f0qCǷxYtS#:Jy\icZRإuiS<%X*%OL)D)tLKix B܍a0fZaeDW‡K|\.jpG'2KSO{SCOkߙ1.Ltw;.#53#/K)L393NN{&ppe$y#ynH|hbuS5o% >7{PbTU#:ڍBA!&#5gS'lb:|]^oݖ982ULɾ!cX _;lT;.q#/X(c1ENb#5#:SHvhLQǼb Ps[qÃur"u;'q21I6$D#7= c-n ?#.KD, Ȅ#,<#5#:`Z"ZQ]"%BD lB!#5GNx q6Xp`ڏm6#51;ydyiiku$#4 s )hEKޖXrxF eu=(lb0U2Ih!9Bh1iЄ5Gb JG3o,x(w"y*&)R4IFAF٭f{Wma@QCtHWA D#5"JC+"[T vZ_/>]K4̢e0ϥNG:#pWȸi;Ce@UD剫wR͎Sn8sttUb`\0(q9×9סusB*ݯji[js89r"H$cGPVӬvf[_%II@"c5#//D/2!itkF`t=)Qj16L ޿ 7X#/T]H4h6,KLƒ-ꅐ%H Jp;QO xwl#5GF9s9@RAzK܋"?: XЄ5.o7EO_Wzj=yXB&}.\2 ,Jd+fDcbo!,xAn Ezml](F@`]ZiH9[R==?pgF2,#5bf|b%yc-OIA]6(RmX%E-35RcZ-H^#N

    A-ԧwWzKĺ=Oypv$#_phl"#:J ƪ#5@le'' @E8EO0 iAO/*HF^}loݐnag]T]Ed]tJ4&Ib b9hm#/MT cbL)gWIPh=A`0M1r(R&#ILgK1jM覆$5l {q y: qlYTήyd¤Rq,- JR7*dȖW=bnB#5d!1N6(Qd#/C(hp K^;2]! Bj l].z}B#5"OE2^(!" .!#_N.`rRv$xI#5K#/0Fv!)F#n49ƕjb9mk2cؘtg,hѥk(/#3t Xx93 VPIݮce3!CưӪ F]N`~4 q5H~}gh|9AprL!#0i`l32spmPn4碅h7;j5EQgrl @2ذ4)!E lb]ta:#:#/ Ld@)t]#: 3 #/BDVܡs0ӝ>@\P/!B'F!e봋|[_%ԋP0zəBXT\>p?8XM;6IG#/$\2 5=1j=  uܻm5!GC`&n%N!jyc:ubs7%-vCvZ8lUXʃ'YN " dڧJIaj#:MvaU0<Bn0k&*c*rT2 "3Jj|`9M:Bz9LSNyG#:7N4×cR#FrF,g[h5#NL1#|az(RpZr-`L{#5X>bƛ{bֵ̕+zk&gI#:& ʑ60%mSc+$Ȇ.)s ]3lHq̵ȸnS~/oEa !9< JDj LC$ф~ŭh#$s4,y&m:mu#:X|ky)!1gA{qg2P"!lDJ ssta1qyFjPWZIX7+Bם҉ח5!kv $f2R$p#:)lԅkkkSfI6kbRfZ$ 0ň?Gw0PH@hԆi*5 #/aI=ڑhM{*Z`2%HQ9a(|#5N0^ =d;޽–Cvo$@\Eq_!I 1ɜ²hlRlQTWcii65#5m& |渠v0~m(C1@F#G^'O!t4㭋}ٺ,omm PšeTHJKs\-i0Bv!!a0Tb]O5mӘbSXTtD:Ɔ':ڳmQ5J.Kܴ]J!#C/#5M֣ ଆG/nE] ,B"Ru^J~GEdMܼUDHD+N/8Gʽ. <4o?f~lI .jWӲ3Xڌi,%I뮪 $o(Zkkbim%R1)K)Mc(JeeRLQEm,^wTewtՑ>zkzv(Dc)*ĪYj5SIoZWLMZƤ5 MF&yזi2K(ɲVvֳmNjtZTήwFl+vTuI*Mqʵ)o+R3+IX],sxnݏFi;(,a޳0ra؋Є@AD2b Xf*CA:8bb" cn/3m1ݽYV }vvBF0)eTMuC |ҥ ҵ#5~sCQ(n#:wMN]~Hv##5P *=$7?=*1(QAdYER?c^"Ey:Z<-'JLCT}e"! 16,2%P&Xؠ"մl*UR4,4j&W+Dh5#tȗ#XpId#9iq#5`FY !mh #0\L\4֞JۿTLF#5u#/5:_jl{d#/~2R"sօ!^#/#C*ԋD1+beMTLYps"pE `:wԃwe$X1#5bd"r2wೠ6c eI #hĩm3C~+h>@-2`Nv(4{/5|-I!?t҆q,B o+oQ:.h\,DɭuCKFV.d"uv{[c nԥ'͎vEC\r:nJA}dDdNW `cZRNRJA7+8^+dj5!&KƬ[EוQo(v"=#:2idJf/y3rJ^\fSCh7dU X09ιG@QudinKިyuERR,7D+^u\)hvGn9`v)L#:} anbg@i1$pl`C&$ԳUKS&igSPEzןc IAɚGkpdHxp&bK+1NBFJiKm/wQ,!ymZ{ZXNwV6t(I #ϔƩۆgn!7TDtreʾ1KMZ#5yl<^$XU=xԞ3D0iGQ;: 2Pisr ȅ#:Ʀe=(9jUD}z#/!ɱoVyG X7/#yBH$(D9F'Ŋ"c8ػ=9* Hg4Uc0sI_D@+ݎJ`S82UHv]2/vF"GW&StTnʘR44(ܩJN4QJ ^0yO/V#:xl{׽N '>}}կۛ9!#/̶tfb*oi!zY7_x?oؘ46tQO@.I~#+쀕֓@ޢg&HCw`7P[(&s `1Y9`#:).ã;[%%A/%0j"sP!(WmiLH B4*b#:݄*)LBˮ@iF(j#:H](AMH=מۓf>nH7GS[xv<9GRMq;"i-#^ )M3|<{oJW5UbC,9\j&/hE[#,T,l,)닛D5#$̡F#:1%$CB1pI8eaFS|&zX#:2 Ӊ鶺E}d`@P\::uS|q 8!CiHC> }3@6!팰P6J*(8@(#:#5|hA#5v~ q$cxefCH n7~kߣcSMw]QI$BPҨo98Q10#Vw _Tg!{@U,:bvD-o~q Ѩ AI:X=~;&G&]Lm #:!=#kTPU#:?%hwUmoK#5!$@'@#/Lͤ7ס<}Vs/Dt?_U|^0f\ſzնmQ`!dDo]~A&w)#5mC#/(**# 67^Ɩb1#5j~L35!&:@oC>p?H<'==-#/ 0 mY\5V5UI]0F#:C1xb *" \VSn]`Ta qicHr'Tt0m41@mlo 9A Ot=OW'C{nhVCy)J vBw }XKaH!;>|~6YXejZ+{ C-ytx)꧜髏F#ӵ@!!1ўipS+R_Q}4Q1bUI仆Y)3R#:TB(TҿgQ K0#/݁JQ8¿T6&V)LR #:fV]fPr%. .R$Ɋ,H"b]22EYUr_^-c#544KTA #:VAe$Fd 9 eUYzԒ "5b c.I.+@M*oT;r#:4x#cAmnZL^ѓԺPk p#7 ٜ-3:īF%oui0k#/aʎUb6moR#:l V69) ) &]pz#/#5bxƒ2"Y~cu'UyуE8&,~G~+563-Pj3&؍㭵wk˳]ݖf%#5Lzo[χGFnjG*jA=ѽ>%AcX_H?"$F0AbMo6:6X±[u**m7(! hQ6B YT$#/\8E Nz\@`ru{4\zSטA (H?`" VP&/U[s{}8b W5)X*))ҥL0B2GcZ͙cbFybd1CLY4 $UTI#:I6*elKm v#/4ZPw0u5Z0F1#/PF1 i#/@1pAK0@EvKd{j*#55܉pk!%?lL``Dz@#57z7,8JCܾ"fq5<{fΉ>"a3ɨN#:()4zj;}þҫ|q v]ffA.\ةQ#/}18:qur!`@~h\+$-]Rb&֍YKZiYO%t,[!Eә!A$^r=~{w&}g0$,CX#S ¯]59voNv,lk̡.7MYNFևTf@Nܕqm~v``e4a ęw!Q~6ysYp0Hex|{`U'~ٚ8zX:=tL뤨Gʺq&DǢi=ZaddG1/ލ^}`IsMt屐'6CϚk@Tb-# BԚI#jijM֌fkMؓBTT6ٙTْ*(SRjcY[6kIb6l5hikHk-6k[A3SZ0i9nƉER*TQ)@Qjݛm\k-DX&='W #/0#:H#FP?3 ˔쯯}dd\ @!oKL#:)\#I┇Tl^z#5_cgpq$;BHlє:ѵUS#: nPߎw gݍ1#6BCe<9țj@L(aC>,(S0RP9HzJX,F$il]Y-%eq|o{1JFzSݶl|50~*=rJ3Am\glGb[4p.EX$(iRDxE(ƴ@}=Hv}w)ÕHJP4+[JHnYUIZdlʶ4n2Ƞ0T͌8|hi!u*L8pFYL>5Kd֋׃TQ.}=+{1iH>?NAq8i@*Եr\F߫+ACTV3 -#5Hbw0Y!܇L#:Oz.vM27_8'8)+%}٠y]"=Wld#5K:zXغHN#5W_Jk56 ]53F-K%29TO2bdēʱfd)D~[XVK/e"y$>:B|v#Cؘqp)#?#V-Y5+o`PgSP5S睙V;JD~6`{M1։l2%Cs ;9=JI7#::^%Юq ٲ1i#5p`?#0ܲ(E#5Tu؆Wh%0a3#5\U6'Da:R=$~r +[_4{Gqm[PC4X|B;QC!0|#5T>Yg:Y*9 O%YH:4q>fİrisY/DA2M#5-b'O"0'ƟwӁ찭42];Ctsӗq @=QE!azuU:NPjkֱj-lmTZZVjk2xӸcr#5(с\x Gѹ83DnY=E#5+j#/]#:$:P"7Vj=.YmUpA> *h}CǗa y$2٦4?|I#:f#/t΀qSP^!A1 I;2p0PdMWP@PAݕ3bוQ{7AzDE!%DmQ_Z%@5P܂RGХY)#:[mY?wU6" Ѷ[JX)e g/G\T6I4̸l48IF\4 ".T ##[6=C*ha"n1]0I@>4[%⛂\X61i¢4=Jjd.@X+/m6+,;W[a3XEE[IeXq9H0H"P%!2RP3d%ha*!hM5,YU B#i.#:h#54#/epLhc33~칱0LA Rea- ^Yj&J n4ϵ׷C&*ž"<d?df3emZ*`l FH"I=Jm*U0*%8v*|Zaޮ2J=1)rHOnG~׉|&5fK*f)_Ndѵ[3 f(D'ptW|,&h" "7rKcHsFpÝ'nVz?I{is{ wV#5#5!)`ϼgUNkٜz<#:I,K0^PM)0\U`SP!d*'BTM#BX3§CkUdō#/#5 z^Xwdj<Xz#/?S0>#:sP@o6U-mFgeѰ`2#/"|#:ta&8\iQ)'omxdemֹIj6K]WvfnQ]\4VE 6k*wUK`!PJ"BTRDVYE p(%xǷ:ZnLjR|p\!NRVR՗KZ.݈, R*W{ņ+5X*XT$JTnڣZ+xS6qڡV`̦ƱLmEmhhhI$-4mIQUBC#:#5ÍFI2#&0`VAQ Kq?^iv{2{{N'JV*iZlmo̭WV_nJ)IJVeFђVݦVlM5Wm6?;GdiROBRd9 1T 9d!!k#/ŵÁɶH⃁^USwt0d6[B>٩G*rl~RT"bH~v,at2ŋ![ ƫ1za PBի2=$ad^W#'KwՄf# oϿ9ca䕣e[᫅u25@6OBD:>gQ8PpM–s",.uNwn ښ hF4g[C([9}VlۊGNXdwSi,(Vn6'_Pe!<{Ii{MB5NCh}"h DaFE B[|~V~$Qo|%kWlߺjƪD#:`*gyA?9qE@" kp+Ń/d3mҲV^W5ڱUtc#:0@@kAAF  bc$AR!uMޭ} mIQy$d rl#:QI*쑋2ԊLIڅ{E+#:#Ii1&Z3VEm=X@c0c(#:*$DXPJiy#5r64BZ#:\#:/\шX1A"1J o. W\H&6L7^=LM4p!cZhMۿw#/CT{$"#5:ۊ&$B#//8,vM#߫lU *GCKAA?T># ʕ"{Q>c>i? Q3^+ijO7j#w}3.19hMՀ F cƊNwf-E{bHBixJ6{,}04~%A`QKSIly#/7$E[F1[M/b?D]bney;;}FIsj/2|d: CUroO#5@! ##5#: #5%\j%A<-9U}#5wGV vZ#\ .bY *miuyʅ99#5LMi ,h-சmί7uuͻtx9lK۶cVimEX٦kLQi]Kweugu殡͋EFݦ ql_b{ #5p1A{wGdt#:_uE = Au<tz~5:S!?;SnEA)D`ꦽ{Kzk#:~P^~HPA><wiS`EHc$jL/WoV|,0u9Fױ %lkl9G"'BBINu]ԳQFn\t劊1+7ΪU5I0v_mS E#5 ;r#PJT#5eF^Uv6򧴕V-eKރS43#:(o")1P L`RТ+#/j{+N#/#/*ekY-e/o6:+ԙ6ŶGy0Jqrp>}@ 4Lҙ6qE$#/ˇQ֦|5ȡ)X#:z]ܪ8`ɐIp@.-K3K e&\K#lRU#51H $Y#/ATT 5L N@&p\ ƈ'M+ds9-W(YTp@%((#: CX&;نL$c k)#Ӻ j#!#/숪|!E>xTDZ(T)H#5FvY5q3#/ħ6y&'رFҪJ&di5J}RFi4\g#r!c#I(#:6$cO#M3-<mS1$"wyh P[qarF܃UV9nٹL7@@#5gT@N#:#/X!IV{h*$QN#:luV@z?%68B ^(/n+l,d"'̡#" N(j:*Hzx]ow×Ff0Dj@CҦ76FKafRΛo#5ΒJT g,L5lj9|kVff?d]'#5^0 ЊЪyp Z|_4?/*'] f0niCB۶Dkqj<|UQr.1C*會u =1~jL['IL8]a #: F1t2MaDV$X5i*V1MDC>$FXyu2aI3.LU7x>Yt#:\#:!J; G=Zf1i;m8\)j$j>Ǐs~j)3L01u[4h!$4ݳ !C%9+!{ w騯OxfF6Ɗ#/BܩHd b=%Hq'rGԡL{&߯Ng SYW p@QVvq#5zSAžE HN5a&@SDI~F8NID iHHB#:n˳ h#:l`8Csfa$#5QHTA ^D=412{+Hʪ1m{=#5X*6``@+2jkn31 r<;']2(O/ kknS 䉿!i!lzԜb2NR_* 0ppkv(5Sl0JhZJ88#5#5Lb'6PAd+×"amAgme՗ C0vC7zVI$1Cpt&Xt r[ֆ4cesyOaEU[sA zL:gK"۸c+s Xamy Z]zBS4Y L(h#$ 0EF#5(#:H! #/@hw-WT7|}8ϴ)oC$btqA:.ż740NRmv7!Ja%O^RJT^XY:GimşG#xQgd8ɒOHbtz n% 3WKBnboOc^c@S#:" ogT#/@aD!Hv2BŌ59o# ;=U#*hIKB1Cz(GGL9cmnqX֐#:Q=Pji/#>;z T*#/т5NxC@` [8 $:_)AAS9:fvM>!ÐE6 ȡ]MЬBzQ/hHb@~з|/^qgl?)l~#/uNib#/(B~kd#5~8uD,/D*>T2JdGWXr}8ͮ#53BB΄n%j{vF[CܘPDh@G-FZhf@PHZ`$ &͔C`Âby6Ȟl,qPZ 馠XZ4#:~IFk6ҚZjlYEjld C>P!>}A~R1$ 4a#5Ɉf̒Xl5)SHRR hܢO!g_'>ߺRI폘UtT֍Q冉3JPH(xќy@ZplMCa* ]tIH=G6)M*E)jQ6A$+@DCtFX8@j5C6<#:!eChD 2eQ2UZtA+0nF HD,X'iȁUaXGUm*n5!D`СU|6[N͏Zo(j5!.ƃ(LMmLyMbRd|:۶(*J#5jXd#̅1X1b\/#AC:AHffA:Ε@qwT7HkS#%qd`2@4jκ՘;!D`27nѢ.r0$yJ`R3j4j#5 b#:f5(ȒiwL#5RTM|#/z] 1vIT7sA 1G,P#:-@4h YLiM {j'ݦ&?mh֤cg &P)@XRUx=tXh9ƚfMUTL3dXzjhaA&Tk`STط7'C0l=,z`h#:2_l =XP(*nLR#/l (G/:pPf[VۥMCQVD@ٓJ5Ĺ!`FԂ٤D#Ih7#/4Ѕ#:J#Hp`#5en`m h~Ei^*DjT^ěc 7IPcµF/WξDP[QVƅ,1d#)l{,5咭êO/BȈ)#/M#/.v?+}(\kDFBQ\xQVP$AR0RS`нq= <#:C 4PL H?uQ)qȜM ]ClVxg:؂WWKk{n:A=gMD`Aj$1Ql_Qs~yvжMLcix!IkEJ@m)0ִ|z,[E&-u9pSq-/gyXavk24G V4-x3W zn9X`gR1a\̒1>vw=B1 Wj xiS|?kՎKIkyi06A'3I! 0kp"HY~ @);#:0_5#/rt}SMפE^ ofaķ" >=ؑZF Do#6RŤ*$hZ6SNJչiU81CMA@-#:a )T6xo'&Λ)S)!9MTAt^bUx]ni=.PCU!Б**^9rQTm#: HiV$A(nɎQ«ih17S `x;_sB> ]>O4ѕ#/Y#/p<#5*^=r"H߿y(Wt{─|9U#/jDB|Dp"ȁ!LVwT#:F#:$YQhR^[xX E I QkQ5|"YP7lAE@dD#/ u?CԪ'WArH&=E@+\KUD"WC ̞A:)zf͋ !+ HF6R"4CfjeC-j6M4#:#/"PX0<Vjd#:#/ɌDlD aK`aQlt̀а5w %h ,nd(6#59,"#:bahhE$ *#:(b0#:/Os#5ZT,vڮ׮SAQA\#kCW M%*OM$_%aO_3Z\)^@{X$U|Co-#5! ]!(17f&&( 2V$Ҳ2 }#5[tDI o"70e@?lX}W۶%撝[K33z j5ȊAZ"%\r!2Y$ՓfJ(e՜5w&ͬRR({^m0c8$z~{PCˮoϧ4St#q; oTeY^gHI&z3K9#5:$>pGNG!`M8@Wm D3Lt"B<12v#:)m5BٸB99\Ct`!4/17 i2yYxmF]!%#/tFFf"i5'N2gg8ųtStff;)Td>\yxi"z~; :1#3Hm0L<{&Gnv@Sy>cch:=7Uܗѹj!1ruOB_,~]iC386)Hfvp@@mWP7hEiՁ/j<>y_4:VʧT['KxNH%*?;Zd&Hcڝ[Rϯԭ&yBɳJs_^m>'1N:-33U'wb`u֎&QoEEd//!Mt)!, 8C=*RN8M{1]{ή &9@4m#5Th2s6c^WFG >ZΧ|-|y[79)#:񓩫b#5ߧI% v5 &dTQm`ąi> mcM ֌ F#3Ljz=Ƥ^<$Big5:c ^!a*kk>#:h{ kku L>ٱ2--O#:u=vĎ/溹λ"PD/=Q$d?nd03V0x5P#544U1iN(z--X7\]p X ,#/ՓbT`ffK-CzI1e#V\楌Fy89vH3fEΰ֧C3xr&i6ډRmHXK.ԑNµ j8:5H##:AJtꛦ]PwE{wF0RFT. fpjYf8d5&]Jf6z|k;jVFڻm48&Ap0Lb֙MPš[8J6je*XaM0laXj,R7/<]-0Bb&!D9 hrH4v1kYx #5\C@(8#cTnL'TfA%n "A)FU-81A[#:+aXbX z6񬭃^Yái=r=.4:(e*#2 *-7b$`GګzpMW~p02p@}In9i?Kۭe)4l!|&0#:ՙR4'NM#M#, LJ^2zx֑|#:uի} i9Ìa%M}؈ ĚmQ#5Ǘd?UdF#:˺60`oyPQHIvV6mr۵ʩ$}CWx]TsA;*?Ku68xG?TЌ>i[DG4$U֘}EG> [Ed%vCF#epNE! M#㢁>@7O!aPKGX'z2YtRg N"ȡ#:kE-F"ovTKiYRY #/20yx$0_z\M MDȈL#zX59eg1Qte;Hٱq u`bбc@O2,`5k#5;{w{B|k,5#`9;YRgKbz-Xjӽ)DB$&FE`^ly|#8AI8-lHsŮ/_>󊢼kvc&bTb#e5ŕҋހ<2S#:Vѡ4̀ta E0^vX crjH6[%)s#/2"ޑ!I$=v҇5P@F$EgQ#/n(9=7;hoJߧ6EΨ;Hc#5AҰt2 5MF C+ DԒ,]bc6~Öfr0.xʯ6u(Eu㮺TRjeRy/:M&-z^4>l޵7*}Yr'YAPz'ا#5GU'M߸L!#/͞ΥClde[Q.!E~_sth*Ҍ*Q<:O#/"$$!!#: o?H/mRFOE#5*=8i!"&׺%ɧ P1~H3~0r+]~'8T<T=USOmb+[-,\LTTpd #:ÃCvt o|AYܥV1pUP*⣇c<8>mK#LC* Rq E*kj5KX#:O)VdAoNp=MYDk 0b͔7/5mmҾN((6֌ƗS)r#K88#5K_:#50 aX+N6t(ȇ:+M*#5GXsC͠vݠ%,*M9#56dM3R i rN=G"i246tQ`S3:ui4@䅞ZFD[4=se6 ݙ7#u>gV6f6" ZJLnF5j6X%]hŌ]K8|foz:¼P{5:r1y#5iIu5uT$ M-ⓛCA6,naOW.'.YNˮ "*>Ϯjq }.oFKÎ9ecd0c&*9l!6R#/arp?;o-͑C"[._q4rFVfa^eK#:p\ {YqU00}|\#5@m9ƒoqi5oO`@B*Y|/(Ȱ|(3ŭof^II 0Qi#5]٩ÛCcgMPٽ"``8Ct%J%#5T:fHH@_,8kG*ƺ'DsF3{i ot$ 5OuwqwcJ#A5<d  S&XVi~W87F"PyhEAFH"PԚmaך Gpyɠ2m3J meM%#5Br LbrlV}wė D0@̘"102KHnr f&"TK.#nגT$\mv5b"4kr6n&d2!R,"r))*MP5I#/`6]Q* H9@S@)eQ͌\M!ԧ8 P+tL  #:j} >xK`EDT4v!7,={F>Ȫ#5EF! ªT^+8guga/^u\îg\OЬbM|,n%jk) z'u4_#Ɠ9}xwCx#7~sn>)L.Q ʕij ~r2=E3=a:A$N<6Tá= 4mtR`MRpAUjIAgR"V(nވbm.N柩+1T-Z%k16_]Q'bL>DI3dEMa3N`4J}e@'I$L辻܇#:%tRaI |#5$v}ʼ2uEA ç hŘRȱX2EcxK݅J祹i]BXF2<(Q#/ TuA#:XS[#/h&b1= 6#5![r/"(X-AFFR[2Hc xC{"X!Dr4Tp RKs1U!eyؚHW2]Pr(OWXMu<88#5TU9΂1)1.`0ǐ#/Ms!6ND睳;`=Wc!qBqnf|HSu{YwIHt(%6!$J#:=u8^I|̷SwK_|(5BETiC](ɝ+'wD $RT~dI'ȂЊh` 䒘 Hb1 1PwAٖ!;d>HC~ߓy}4UJ4mK2h%k?t4Pgƙ3"b  " Ϗ\|:(2˃Z.u_X̦7f)H#5jq#:h"Ys(PF+xPAMZAy;Y㤳Ho4#y[(D@R1H1&SLf*meCFͩktJMZ:nͮ}Q_@z:d8AF9#/uZ#/G8M$0b$H h#d{!Ǡ@;"_L}\K>zŒZ~c,Q# -ٮQAMX΢(4W!߈-- xJmk#:PsΛ6S Nδt \!Ƴ8}̍4ۤNq3QYk# H -5,)u5,~Œg>X 5ԡljDᚥ0R޳Om4mUrhgG2#LQ~E(p<ʩ hRRv6I] R d(%#/H*}<{9&nhvL`G8A@ A)]dz>cƍSz^#`z@"W׳bttX0TНT^#5#lN`O?R4a/i÷˶ޜU*As̤"u NT\rhkYk&~gzs )"F,9xKF5+*#5灸 ;@B8H%.Y)"*1d2 ;aƪmX~ B4Ae Wv-QL1ʛiE?q#:@IQ1o|yz$#/8,3D`=FMb;YAd"A0BBgcI X5dL$}ߦ6/TQbQli*6Kw"ȼO*@M?$%-v{h7\IdhEY:j`E2"k6*EjKMoWB/iToFO}_a"I䯠 lpoٸt4$#5ݺ- 觉 !y#3c%$Obz%~X5dy4B½`φfؑ ePnFD7$;8`ABB'R{ֈ0T,I7RB&hMT Bͷ&QLJ=;`MPP!졠?9<#0azZ ix;JάܴlN~ -2$O"^Jߣm 2_Ǐ]58Ѷ6 ǫ#/v׫/Îf#5b,jJ#5XɵFol(h#:Te,HXO:d U,<覉?-BʡAp\pBwAHXQ (H[F6+KiHYiy$51A EGOQ #x#/ށG]ɪ=2Wn`\4̅0 kM҅ @7 6LJB$D#/27k\έo13KD>`V~"A-X\AόPG8r+#:mH#:D@W١a7R(,%LipCt셶+(mHUivjfeWs*5\YRo;Ϋ /nʋ*%0AEQm)[2MwV搥Br-#:εm!BIK=»)?ۅ\%ҥX$8#:Ԟ;M [*ĬX[|#5 c" &v(V55hc#:}j͟8qPU]zqfHNZFQh)6j"A I%d 0}]Roߒ-9@dЙs8~ܙtwK(Hb^ƕm؈ }vJ8b,~y N/Ϯucbkp|Xes|q`׈4f^_E<0W\6p{SMU\E Dܿ[l,ӵ?qB/}#5"Ș&DanM 0!{7ɉ/bTVq̏RQ#:]/lIʿmdR*L pl{ω`:f/jIb83G3zZYR4?[xn-; گZ΍,q'"Nng ^Pj#5nuQ2 tȶ,S#d5 xa!@3ad.<1\άhP4q#/pyLn!!rc `Z vDGM15@;`-?!YϼQl8E~ݠD?#5SHԞ@G,P;2dōb^*o-}4#: *ֱ1P4O}_g?~~ͧ?Oz?߫ߓ_{gW?_~.ߣs@hPChWgT Y?*@CH$#_i{;dL\0s5NQE MME8b.!pg5HJ]gFP{Zc3e< `Po?eGIZT^&?T)CB@g'M@Iqִ8M1(cQJ?ZaTg~*5hY.]$DC#5B;x?@`wpZeX h=0#5]f3MtjnJ82t#E/51ul$ 9 WqiWmģ/./L-.4R:c'?ٗf'e#:f,Y`/<Ո5޸+n0DcF+f]dWLpbuW{w{h1󍗳vѫ…IHB cx拔9YrZOYgpYQFp#:㾚|uܛ P#uR5/n[)ݿc .$dm p8劀>#:ȂAzd#:PbH#:#5C5YU̒ {}w!9=z;,ڑhdIUYAM PPUS%k%*4#:@9K~A?YEhs;Z$dCL9_@_Kz&OyC7yQp0#:!>a|$@`DS{l'mEVKjFmvWlkʭ~(-H@#566uwL`ƶ8oNvϑۭiNjtK4tbq(>bPȹ$Ե_%;~ ( %OL[m #:dP-zZ#5[.ikoO/(M:tZ(^ۊ+'k˪ _GMһxT>yrD;}tN]@&2S?xGu,4N*%bx:r:JƲ^(,+DCmԔ2'>B̚ta'3\߱OH#:/> -#<== -#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1\n\niQIcBAABCgAGBQJUja5YAAoJEGelZe39+Q5kZ0kP/RK2odJp1mbvHst3fceud4Dw\n69ymSDzBJKsLyl5oPl6RGbSXxGFnyd0eFuewUcJPVsNLObSfaXIA/AJdHAtZ8y9t\nbN51jD99jBGO+teMvjK93EFIb4P7cv8PA/LhQq3NUy2zrj2dN+TFjXx6+PU7BKrz\nTT0jTbXinNSF6Jr4SEqFrY/dpzs+IKSAEHP0BmPcMxmDU/uCCt27RhTgjQYaLLGi\nY7ZzhBxUk8PgIIpSucOUMQx/xbMvgbSRNAYVqqhBlnHjgPAbFY55d+k4uvlu2TJX\nmFXcOhB9dZbr9xuFyUD3fvfBQ6OZzrKfgNsiqqeTLVehDEaYexZ96eoDJXZawyjU\njer1p7qxKqscTKLsyJWbmjTAxElgmlLY2+LB9LRYqXT4tUbcPiahHo2djcdVprDI\nnEZlUx12uCHoLmKBAMtD2ha2uTXtkl8GU44zB2oOQU8hoXtRITWYyF6Q9qlLp9qw\nosuQoQ+6ovT83q8OLnBUmGbVYa4GFab2dq4rMXX91o3hMQ806xQTaijUlZCFGMD0\nxa10IC0udzGOMNPL749zlKOaK68LUbrGPiy65dBjN3i9zU0itYTSPLS+wu9GdY3m\ndR96fFsGRNcceHAXuqMl+nxtJVE8sMNO1VRle1sGUqfBw4jUnJ6CvShXsZQuAeZj\nSzBEpSFrNVqBi/BdbcZ7\n=HKUv\n-----END PGP SIGNATURE-----\n diff --git a/platform/tintin/boot/waftools/binary_header.py b/platform/tintin/boot/waftools/binary_header.py deleted file mode 100644 index 827c11e5ef..0000000000 --- a/platform/tintin/boot/waftools/binary_header.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import binascii - -from waflib import Task, TaskGen, Utils, Node, Errors - - -class binary_header(Task.Task): - """ - Create a header file containing an array with contents from a binary file. - """ - - def run(self): - if getattr(self.generator, 'hex', False): - # Input file is hexadecimal ASCII characters with whitespace - text = self.inputs[0].read( - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - # Strip all whitespace so that binascii is happy - text = ''.join(text.split()) - code = binascii.unhexlify(text) - else: - code = self.inputs[0].read('rb') - - array_name = getattr(self.generator, 'array_name', None) - if not array_name: - array_name = re.sub(r'[^A-Za-z0-9]', '_', self.inputs[0].name) - - output = ['#pragma once', '#include '] - output += ['static const uint8_t %s[] = {' % array_name] - line = [] - for n, b in enumerate(code): - line += ['0x%.2x,' % ord(b)] - if n % 16 == 15: - output += [''.join(line)] - line = [] - if line: - output += [''.join(line)] - output += ['};', ''] - - self.outputs[0].write( - '\n'.join(output), - encoding=getattr(self.generator, 'encoding', 'ISO8859-1')) - self.generator.bld.raw_deps[self.uid()] = self.dep_vars = 'array_name' - - if getattr(self.generator, 'chmod', None): - os.chmod(self.outputs[0].abspath(), self.generator.chmod) - - -@TaskGen.feature('binary_header') -@TaskGen.before_method('process_source', 'process_rule') -def process_binary_header(self): - """ - Define a transformation that substitutes the contents of *source* files to - *target* files:: - - def build(bld): - bld( - features='binary_header', - source='foo.bin', - target='foo.auto.h', - array_name='s_some_array' - ) - bld( - features='binary_header', - source='bar.hex', - target='bar.auto.h', - hex=True - ) - - If the *hex* parameter is True, the *source* files are read in an ASCII - hexadecimal format, where each byte is represented by a pair of hexadecimal - digits with optional whitespace. If *hex* is False or not specified, the - file is treated as a raw binary file. - - The name of the array variable defaults to the source file name with all - characters that are invaid C identifiers replaced with underscores. The name - can be explicitly specified by setting the *array_name* parameter. - - This method overrides the processing by - :py:meth:`waflib.TaskGen.process_source`. - """ - - src = Utils.to_list(getattr(self, 'source', [])) - if isinstance(src, Node.Node): - src = [src] - tgt = Utils.to_list(getattr(self, 'target', [])) - if isinstance(tgt, Node.Node): - tgt = [tgt] - if len(src) != len(tgt): - raise Errors.WafError('invalid number of source/target for %r' % self) - - for x, y in zip(src, tgt): - if not x or not y: - raise Errors.WafError('null source or target for %r' % self) - a, b = None, None - - if isinstance(x, str) and isinstance(y, str) and x == y: - a = self.path.find_node(x) - b = self.path.get_bld().make_node(y) - if not os.path.isfile(b.abspath()): - b.sig = None - b.parent.mkdir() - else: - if isinstance(x, str): - a = self.path.find_resource(x) - elif isinstance(x, Node.Node): - a = x - if isinstance(y, str): - b = self.path.find_or_declare(y) - elif isinstance(y, Node.Node): - b = y - - if not a: - raise Errors.WafError('could not find %r for %r' % (x, self)) - - has_constraints = False - tsk = self.create_task('binary_header', a, b) - for k in ('after', 'before', 'ext_in', 'ext_out'): - val = getattr(self, k, None) - if val: - has_constraints = True - setattr(tsk, k, val) - - tsk.before = [k for k in ('c', 'cxx') if k in Task.classes] - - self.source = [] diff --git a/platform/tintin/boot/waftools/file_name_c_define.py b/platform/tintin/boot/waftools/file_name_c_define.py deleted file mode 100644 index 3bdf4ee21b..0000000000 --- a/platform/tintin/boot/waftools/file_name_c_define.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Define a __FILE_NAME__ macro to expand to the filename of the C/C++ source, -stripping the other path components. -""" -from waflib.TaskGen import feature, after_method - - -@feature('c') -@after_method('create_compiled_task') -def file_name_c_define(self): - for task in self.tasks: - task.env.append_value( - 'DEFINES', '__FILE_NAME__="%s"' % task.inputs[0].name) diff --git a/platform/tintin/boot/waftools/gitinfo.py b/platform/tintin/boot/waftools/gitinfo.py deleted file mode 100644 index 8f8937aa17..0000000000 --- a/platform/tintin/boot/waftools/gitinfo.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re - -import waflib.Context -import waflib.Logs - - -def get_git_revision(ctx): - try: - tag = ctx.cmd_and_log(['git', 'describe'], quiet=waflib.Context.BOTH).strip() - commit = ctx.cmd_and_log(['git', 'rev-parse', '--short', 'HEAD'], - quiet=waflib.Context.BOTH).strip() - timestamp = ctx.cmd_and_log(['git', 'log', '-1', '--format=%ct', 'HEAD'], - quiet=waflib.Context.BOTH).strip() - except Exception: - waflib.Logs.warn('get_git_version: unable to determine git revision') - tag, commit, timestamp = ("?", "?", "1") - # Validate that git tag follows the required form: - # See https://github.com/pebble/tintin/wiki/Firmware,-PRF-&-Bootloader-Versions - # Note: version_regex.groups() returns sequence ('0', '0', '0', 'suffix'): - version_regex = re.search("^v(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:(?:-)(.+))?$", tag) - if version_regex: - # Get version numbers from version_regex.groups() sequence and replace None values with 0 - # e.g. v2-beta11 => ('2', None, None, 'beta11') => ('2', '0', '0') - version = [x if x else '0' for x in version_regex.groups()[:3]] - else: - waflib.Logs.warn('get_git_revision: Invalid git tag! ' - 'Must follow this form: `v0[.0[.0]][-suffix]`') - version = ['0', '0', '0', 'unknown'] - return {'TAG': tag, - 'COMMIT': commit, - 'TIMESTAMP': timestamp, - 'MAJOR_VERSION': version[0], - 'MINOR_VERSION': version[1], - 'PATCH_VERSION': version[2]} diff --git a/platform/tintin/boot/waftools/ldscript.py b/platform/tintin/boot/waftools/ldscript.py deleted file mode 100644 index 9fecd7f336..0000000000 --- a/platform/tintin/boot/waftools/ldscript.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Utils, Errors -from waflib.TaskGen import after, feature - - -@after('apply_link') -@feature('cprogram', 'cshlib') -def process_ldscript(self): - if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': - return - - node = self.path.find_resource(self.ldscript) - if not node: - raise Errors.WafError('could not find %r' % self.ldscript) - self.link_task.env.append_value('LINKFLAGS', '-T%s' % node.abspath()) - self.link_task.dep_nodes.append(node) diff --git a/platform/tintin/boot/waftools/objcopy.py b/platform/tintin/boot/waftools/objcopy.py deleted file mode 100644 index 97c4894dd7..0000000000 --- a/platform/tintin/boot/waftools/objcopy.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Grygoriy Fuchedzhy 2010 - -""" -Support for converting linked targets to ihex, srec or binary files using -objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' -feature. The 'objcopy' feature uses the following attributes: - -objcopy_bfdname Target object format name (eg. ihex, srec, binary). -Defaults to ihex. -objcopy_target File name used for objcopy output. This defaults to the -target name with objcopy_bfdname as extension. -objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. -objcopy_flags Additional flags passed to objcopy. -""" - -from waflib.Utils import def_attrs -from waflib import Task -from waflib.TaskGen import feature, after_method - - -class objcopy(Task.Task): - run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' - color = 'CYAN' - - -@feature('objcopy') -@after_method('apply_link') -def objcopy(self): - def_attrs(self, - objcopy_bfdname='ihex', - objcopy_target=None, - objcopy_install_path="${PREFIX}/firmware", - objcopy_flags='') - - link_output = self.link_task.outputs[0] - if not self.objcopy_target: - self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name - task = self.create_task('objcopy', - src=link_output, - tgt=self.path.find_or_declare(self.objcopy_target)) - - task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) - try: - task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) - except AttributeError: - pass - - if self.objcopy_install_path: - self.bld.install_files(self.objcopy_install_path, - task.outputs[0], - env=task.env.derive()) - - -def configure(ctx): - objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) - - -def objcopy_simple(task, mode): - return task.exec_command('arm-none-eabi-objcopy -S -R .stack -R .priv_bss' - ' -R .bss -O %s "%s" "%s"' % - (mode, task.inputs[0].abspath(), task.outputs[0].abspath())) - - -def objcopy_simple_bin(task): - return objcopy_simple(task, 'binary') diff --git a/platform/tintin/boot/wscript b/platform/tintin/boot/wscript deleted file mode 100644 index 257d9b5f50..0000000000 --- a/platform/tintin/boot/wscript +++ /dev/null @@ -1,102 +0,0 @@ -import os -import sys -import waflib.Logs - -sys.path.append(os.path.abspath('waftools')) -import objcopy -import gitinfo - - -def options(opt): - opt.load('compiler_c') - opt.add_option('--board', action='store', default='bb2', - help='Which board to build for ' - '(bb2|ev2_4|v1_5|v2_0)', - choices=['bb2', - 'ev2_4', - 'v1_5', - 'v2_0']) - opt.add_option('--nowatchdog', action='store_true', - help='Do not enable watchdog timer or reset upon failure') - opt.add_option('--loglevel', type='int', default=0, - help='Set the logging verbosity [default: %default]') - opt.add_option('--displaydemo', action='store_true', - help='Make the bootloader infinitely loop through boot splash and error codes') - - -def configure(conf): - CPU_FLAGS = ['-mcpu=cortex-m3', '-mthumb'] - OPT_FLAGS = ['-Os', '-g', '-flto', '-fno-fat-lto-objects', '-ffunction-sections', - '-fdata-sections', '-fno-if-conversion', '-fno-optimize-sibling-calls'] - C_FLAGS = [ - '-std=c11', '-ffreestanding', - '-Wall', '-Wextra', '-Werror', '-Wpointer-arith', - '-Wno-unused-parameter', '-Wno-missing-field-initializers', - '-Wno-error=unused-function', '-Wno-error=unused-variable', - '-Wno-error=unused-parameter', '-Wno-error=unused-but-set-variable', - '-Wno-packed-bitfield-compat' - ] - - conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True) - conf.env.AS = conf.env.CC - for tool in 'ar objcopy'.split(): - conf.find_program('arm-none-eabi-' + tool, var=tool.upper(), - mandatory=True) - conf.env.BOARD = conf.options.board - conf.env.append_value('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS) - conf.env.append_value('DEFINES', [ - '_REENT_SMALL=1', - 'USE_STDPERIPH_DRIVER=1', - 'BOARD_' + conf.options.board.upper() - ]) - conf.env.append_value('LINKFLAGS', ['-Wl,--cref', '-Wl,--gc-sections', - '-Wl,--sort-section=alignment', '-nostdlib'] + - CPU_FLAGS + OPT_FLAGS) - - if conf.options.nowatchdog: - conf.env.append_value('DEFINES', 'NO_WATCHDOG') - - if conf.options.displaydemo: - conf.env.append_value('DEFINES', 'DISPLAY_DEMO_LOOP') - - conf.env.BOOTLOADER_LENGTH = '16384' - conf.env.append_value( - 'DEFINES', 'BOOTLOADER_LENGTH=' + conf.env.BOOTLOADER_LENGTH) - - conf.load('gcc gas objcopy ldscript') - conf.load('binary_header') - conf.load('file_name_c_define') - - -def build(bld): - if bld.cmd == 'install': - raise Exception("install isn't a supported command. Did you mean flash?") - - linkflags = ['-Wl,-Map,tintin_boot.map'] - - sources = ( - bld.path.ant_glob('src/*.S') + - bld.path.ant_glob('src/**/*.c')) - - bld(features='subst', - source='src/git_version.auto.h.in', - target='src/git_version.auto.h', - **gitinfo.get_git_revision(bld)) - - bld.recurse('libc') - bld.recurse('vendor') - bld(features='subst', - source='src/stm32f_flash_boot.ld.in', - target='src/stm32f_flash_boot.ld') - bld(features='c asm cprogram objcopy', - source=sources, - includes='src', - target='tintin_boot.elf', - ldscript='src/stm32f_flash_boot.ld', - linkflags=linkflags, - objcopy_bfdname='ihex', - objcopy_target='tintin_boot.hex', - use=['stm32_stdlib', 'pblibc'], - lib=['gcc']) - bld(rule=objcopy.objcopy_simple_bin, source='tintin_boot.elf', target='tintin_boot.bin') -# vim:filetype=python diff --git a/platform/wscript b/platform/wscript deleted file mode 100644 index 9c488f77a7..0000000000 --- a/platform/wscript +++ /dev/null @@ -1,239 +0,0 @@ -from waflib.Configure import ConfigurationContext, conf -from platform_capabilities import get_capability_dict - -@conf -def get_board(ctx): - if isinstance(ctx, ConfigurationContext): - return ctx.options.board - else: - return ctx.env.BOARD - -@conf -def is_bigboard(ctx): - return '_bb' in ctx.get_board() - - -# Platform specific predicates - -@conf -def is_tintin(ctx): - return ctx.get_board() in ('bigboard', 'bb2', 'ev2_4', 'v1_5', 'v2_0') - -@conf -def is_snowy(ctx): - return ctx.get_board().startswith('snowy') - -@conf -def is_spalding(ctx): - return ctx.get_board().startswith('spalding') - -@conf -def is_silk(ctx): - return ctx.get_board().startswith('silk') - -@conf -def is_cutts(ctx): - return ctx.get_board().startswith('cutts') - -@conf -def is_robert(ctx): - return ctx.get_board().startswith('robert') - -@conf -def is_asterix(ctx): - return ctx.get_board().startswith('asterix') - -@conf -def is_obelix(ctx): - return ctx.get_board().startswith('obelix') - -@conf -def get_platform_name(ctx): - if is_tintin(ctx): - return "tintin" - elif is_snowy(ctx): - return "snowy" - elif is_spalding(ctx): - return "spalding" - elif is_silk(ctx): - return "silk" - elif is_cutts(ctx): - return "calculus" - elif is_robert(ctx): - return "robert" - elif is_asterix(ctx): - return "asterix" - elif is_obelix(ctx): - return "obelix" - else: - return "unknown" - -# Composite platform predicates - -@conf -def is_snowy_compatible(ctx): - return (ctx.is_snowy() or ctx.is_spalding()) - - -# Platform features - -@conf -def get_qemu_machine(ctx): - if is_tintin(ctx): - return 'pebble-bb2' - elif is_snowy(ctx): - return 'pebble-snowy-bb' - elif is_spalding(ctx): - return 'pebble-s4-bb' - elif is_silk(ctx): - return 'pebble-silk-bb' - elif is_cutts(ctx): - return 'pebble-cutts-bb' - elif is_robert(ctx): - return 'pebble-robert-bb' - else: - return 'unknown' - - -@conf -def get_qemu_cpu(ctx): - if is_tintin(ctx): - return 'cortex-m3' - elif is_snowy(ctx): - return 'cortex-m4' - elif is_spalding(ctx): - return 'cortex-m4' - elif is_silk(ctx): - return 'cortex-m4' - elif is_cutts(ctx): - return 'cortex-m4' - elif is_robert(ctx): - return 'cortex-m4' - elif is_asterix(ctx): - return 'cortex-m4' - elif is_obelix(ctx): - return 'cortex-star-mc1' - else: - return 'unknown' - - -@conf -def get_qemu_extflash_device_type(ctx): - if is_tintin(ctx): - return '-mtdblock' - elif is_snowy(ctx): - return '-pflash' - elif is_spalding(ctx): - return '-pflash' - elif is_silk(ctx): - return '-mtdblock' - elif is_cutts(ctx): - return '-pflash' - elif is_robert(ctx): - return '-pflash' - else: - return None - - -@conf -def has_touch(ctx): - if is_tintin(ctx): - return False - elif is_snowy(ctx): - return False - elif is_spalding(ctx): - return False - elif is_silk(ctx): - return False - elif is_cutts(ctx): - return True - elif is_robert(ctx): - return False - elif is_asterix(ctx): - return False - elif is_obelix(ctx): - # FIXME(OBELIX): needs to be updated - return False - else: - return None - - -@conf -def get_hrm(ctx): - if is_robert(ctx): - return "AS7000" - elif is_silk(ctx): - return "AS7000" - elif is_obelix(ctx): - return "STUB" - else: - return None - - -@conf -def capabilities_dict(ctx): - board = ctx.get_board() - # capabilities will never be None. An exception is raised if it fails. - capabilities = get_capability_dict(ctx, board) - return capabilities - - -@conf -def capability(ctx, capability_str): - capabilities = ctx.capabilities_dict() - return capabilities[capability_str] - - -@conf -def add_platform_defines(ctx, env): - if ctx.is_tintin(): - bit_depth = 1 - platform = 'TINTIN' - elif ctx.is_snowy(): - bit_depth = 8 - platform = 'SNOWY' - elif ctx.is_spalding(): - bit_depth = 8 - platform = 'SPALDING' - elif ctx.is_silk(): - bit_depth = 1 - platform = 'SILK' - elif ctx.is_cutts(): - bit_depth = 8 - platform = 'CALCULUS' - elif ctx.is_robert(): - bit_depth = 8 - platform = 'ROBERT' - elif ctx.is_asterix(): - bit_depth = 1 - platform = 'ASTERIX' - elif ctx.is_obelix(): - bit_depth = 8 - platform = 'OBELIX' - else: - ctx.fatal('No platform specified for {}!'.format(ctx.options.board)) - - env.append_value( - 'DEFINES', ['USE_STDPERIPH_DRIVER=1', - 'BOARD_{}=1'.format(ctx.options.board.upper()), - 'PLATFORM_{}=1'.format(platform), - 'MICRO_FAMILY_{}=1'.format(env.MICRO_FAMILY), - 'SCREEN_COLOR_DEPTH_BITS={}'.format(bit_depth), - 'MIN_SDK_VERSION={}'.format(env.MIN_SDK_VERSION)]) - if 'NRF5' in env.MICRO_FAMILY: - env.append_value('DEFINES', 'MICRO_FAMILY_NRF5=1') - - for cap, val in ctx.capabilities_dict().items(): - env.append_value('DEFINES', "CAPABILITY_%s=%s" % (cap, int(val))) - -# Build - -def configure(ctx): - pass - - -def build(ctx): - pass - - -# vim:filetype=python diff --git a/python_libs/pbl/pbl/__init__.py b/python_libs/pbl/pbl/__init__.py index 1a87b6b49a..aa93547fcf 100644 --- a/python_libs/pbl/pbl/__init__.py +++ b/python_libs/pbl/pbl/__init__.py @@ -6,32 +6,41 @@ from .commands import coredump from .commands import install_lang from .commands import test +from .commands import install_firmware +from .commands import flash_logs # TODO: unopened logging ports cause super noisy logs, fix this in the # pulse package then remove this -logging.getLogger('pebble.pulse2.transports').setLevel(logging.ERROR) +logging.getLogger("pebble.pulse2.transports").setLevel(logging.ERROR) + class PebbleTransportPULSE(pebble_tool.commands.base.PebbleTransportConfiguration): transport_class = PULSETransport - name = 'pulse' + name = "pulse" @classmethod def _connect_args(cls, args): try: from pebble import pulse2 except ImportError: - raise PebbleError('pulse2 package not installed: it is required for PULSE transport') + raise PebbleError( + "pulse2 package not installed: it is required for PULSE transport" + ) - url, = super(PebbleTransportPULSE, cls)._connect_args(args) + (url,) = super(PebbleTransportPULSE, cls)._connect_args(args) interface = pulse2.Interface.open_dbgserial(url=url) link = interface.get_link() return (link,) @classmethod def add_argument_handler(cls, parser): - parser.add_argument('--pulse', type=str, - help="Use this option to connect to your Pebble via" - " the PULSE transport. Equivalent to PEBBLE_PULSE.") + parser.add_argument( + "--pulse", + type=str, + help="Use this option to connect to your Pebble via" + " the PULSE transport. Equivalent to PEBBLE_PULSE.", + ) + def run_tool(args=None): pebble_tool.run_tool(args) diff --git a/python_libs/pbl/pbl/commands/coredump.py b/python_libs/pbl/pbl/commands/coredump.py index c240c031e5..d935a06b94 100644 --- a/python_libs/pbl/pbl/commands/coredump.py +++ b/python_libs/pbl/pbl/commands/coredump.py @@ -10,13 +10,23 @@ from pebble_tool.commands.base import PebbleCommand from pebble_tool.exceptions import ToolError + class CoredumpCommand(PebbleCommand): """Takes a screenshot from the watch.""" - command = 'coredump' + + command = "coredump" def __init__(self): - self.progress_bar = ProgressBar(widgets=[Percentage(), Bar(marker='=', left='[', right=']'), ' ', - FileTransferSpeed(), ' ', Timer(format='%s')]) + self.progress_bar = ProgressBar( + widgets=[ + Percentage(), + Bar(marker="=", left="[", right="]"), + " ", + FileTransferSpeed(), + " ", + Timer(format="%s"), + ] + ) self.started = False def __call__(self, args): @@ -29,7 +39,7 @@ def __call__(self, args): core_data = get_bytes.get_coredump(args.fresh) except GetBytesError as ex: if ex.code == GetBytesInfoResponse.ErrorCode.DoesNotExist: - raise ToolError('No coredump on device') + raise ToolError("No coredump on device") else: raise @@ -48,11 +58,17 @@ def _handle_progress(self, progress, total): @classmethod def _generate_filename(cls): - return datetime.datetime.now().strftime("pebble_coredump_%Y-%m-%d_%H-%M-%S.core") + return datetime.datetime.now().strftime( + "pebble_coredump_%Y-%m-%d_%H-%M-%S.core" + ) @classmethod def add_parser(cls, parser): parser = super(CoredumpCommand, cls).add_parser(parser) - parser.add_argument('filename', nargs='?', type=str, help="Filename of coredump") - parser.add_argument('--fresh', action="store_true", help="Require a fresh coredump") + parser.add_argument( + "filename", nargs="?", type=str, help="Filename of coredump" + ) + parser.add_argument( + "--fresh", action="store_true", help="Require a fresh coredump" + ) return parser diff --git a/python_libs/pbl/pbl/commands/flash_logs.py b/python_libs/pbl/pbl/commands/flash_logs.py new file mode 100644 index 0000000000..88d8312d52 --- /dev/null +++ b/python_libs/pbl/pbl/commands/flash_logs.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2025 Federico Bechini +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import absolute_import, print_function + +from libpebble2.services.getbytes import GetBytesService +from libpebble2.exceptions import GetBytesError +from libpebble2.protocol.transfers import GetBytesInfoResponse + +from pebble_tool.commands.base import PebbleCommand +from pebble_tool.exceptions import ToolError + +import os + + +class FlashLogsCommand(PebbleCommand): + """Dump flash logs (PBL_LOG) from the watch.""" + + command = "flash_logs" + + @classmethod + def add_parser(cls, parser): + parser = super(FlashLogsCommand, cls).add_parser(parser) + parser.add_argument( + "--board", + required=True, + type=str.lower, + help="Board name (e.g., aplite, basalt, asterix)", + ) + return parser + + def __call__(self, args): + super(FlashLogsCommand, self).__call__(args) + get_bytes = GetBytesService(self.pebble) + + # Map board names to (start_address, size) + # Sizes are mostly 128KB (0x20000) + FLASH_LOG_REGIONS = { + # Legacy Platforms + "aplite": (0x3E0000, 0x20000), + "tintin": (0x3E0000, 0x20000), + # Silk / Diorite + "diorite": (0x280000, 0x20000), + "silk": (0x280000, 0x20000), + # Asterix + "asterix": (0x1FD0000, 0x20000), + # Obelix / Getafix + "obelix": (0x1FCF000, 0x20000), + "getafix": (0x1FCF000, 0x20000), + } + + # Normalize board name + board = args.board + + region = FLASH_LOG_REGIONS.get(board) + if not region: + # Try simple aliasing or partial matching if needed, but for now strict map + print("Error: Unknown board '{}'.".format(board)) + print( + "Supported boards: {}".format( + ", ".join(sorted(FLASH_LOG_REGIONS.keys())) + ) + ) + return + + flash_log_start, flash_log_size = region + + print("Board: {}".format(board)) + print( + "Reading flash log region: 0x{:X} - 0x{:X} ({} KB)".format( + flash_log_start, + flash_log_start + flash_log_size, + flash_log_size // 1024, + ) + ) + + try: + flash_data = get_bytes.get_flash_region(flash_log_start, flash_log_size) + print("Read {} bytes from flash".format(len(flash_data))) + + # Save to file + import datetime + + filename = datetime.datetime.now().strftime( + "flash_logs_{}_%Y-%m-%d_%H-%M-%S.bin".format(board) + ) + filepath = os.path.abspath(filename) + with open(filename, "wb") as log_file: + log_file.write(flash_data) + print("Saved flash logs to {}".format(filepath)) + + print("\nTo parse and dehash the logs:") + print(" tools/dehash_flash_logs.py {}".format(filename)) + + except GetBytesError as ex: + if ex.code == GetBytesInfoResponse.ErrorCode.DoesNotExist: + raise ToolError( + "Could not read flash region. This may require non-release firmware." + ) + else: + raise diff --git a/python_libs/pbl/pbl/commands/install_firmware.py b/python_libs/pbl/pbl/commands/install_firmware.py new file mode 100644 index 0000000000..20807a9888 --- /dev/null +++ b/python_libs/pbl/pbl/commands/install_firmware.py @@ -0,0 +1,82 @@ +# Copyright (c) 2025 Joshua Wise +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import absolute_import, print_function + +from progressbar import ( + ProgressBar, + Bar, + FileTransferSpeed, + Timer, + Percentage, + FormatLabel, +) + +from libpebble2.protocol.system import SystemMessage +from libpebble2.services.putbytes import PutBytes, PutBytesType +from libpebble2.util.bundle import PebbleBundle + +from pebble_tool.commands.base import PebbleCommand + + +class InstallFirmwareCommand(PebbleCommand): + """Install a .pbz onto the watch.""" + + command = "install-firmware" + + def __init__(self): + self.label = FormatLabel("{variables[task]}", new_style=True) + self.progress_bar = ProgressBar( + widgets=[ + self.label, + Percentage(), + Bar(marker="=", left="[", right="]"), + " ", + FileTransferSpeed(), + " ", + Timer(format="%s"), + ] + ) + self.started = False + + def __call__(self, args): + super(InstallFirmwareCommand, self).__call__(args) + + self.pebble.send_and_read( + SystemMessage(message_type=SystemMessage.Type.FirmwareUpdateStart), + SystemMessage, + ) + + bundle = PebbleBundle(args.filename) + + self.progress_bar.variables["task"] = "tintin_fw.bin " + firmware_bytes = bundle.zip.read(bundle.get_firmware_info()["name"]) + pb = PutBytes(self.pebble, PutBytesType.Firmware, firmware_bytes, bank=0) + pb.register_handler("progress", self._handle_progress) + pb.send() + self.progress_bar.finish() + self.started = False + + self.progress_bar.variables["task"] = "system_resources.pbpack " + resource_bytes = bundle.zip.read(bundle.get_resource_path()) + pb = PutBytes(self.pebble, PutBytesType.SystemResources, resource_bytes, bank=0) + pb.register_handler("progress", self._handle_progress) + pb.send() + self.progress_bar.finish() + + self.pebble.send_packet( + SystemMessage(message_type=SystemMessage.Type.FirmwareUpdateComplete) + ) + + def _handle_progress(self, this_interval, progress, total): + if not self.started: + self.progress_bar.max_value = total + self.progress_bar.start() + self.started = True + self.progress_bar.update(progress) + + @classmethod + def add_parser(cls, parser): + parser = super(InstallFirmwareCommand, cls).add_parser(parser) + parser.add_argument("filename", nargs="?", type=str, help="Filename of pbz") + return parser diff --git a/python_libs/pbl/pbl/commands/install_lang.py b/python_libs/pbl/pbl/commands/install_lang.py index b256ee7689..2ddcda093d 100644 --- a/python_libs/pbl/pbl/commands/install_lang.py +++ b/python_libs/pbl/pbl/commands/install_lang.py @@ -5,18 +5,27 @@ from libpebble2.services.putbytes import PutBytes, PutBytesType + class InstallLangCcommand(PebbleCommand): """Install a language pack on a watch""" - command = 'install-lang' + command = "install-lang" def __call__(self, args): super(InstallLangCcommand, self).__call__(args) - progress_bar = ProgressBar(widgets=[Percentage(), Bar(marker='=', left='[', right=']'), - ' ', FileTransferSpeed(), ' ', Timer(format='%s')]) - - with open(args.lang_file, 'rb') as f: + progress_bar = ProgressBar( + widgets=[ + Percentage(), + Bar(marker="=", left="[", right="]"), + " ", + FileTransferSpeed(), + " ", + Timer(format="%s"), + ] + ) + + with open(args.lang_file, "rb") as f: lang_pack = f.read() progress_bar.maxval = len(lang_pack) @@ -25,7 +34,9 @@ def __call__(self, args): def _handle_progress(sent, total_sent, total_length): progress_bar.update(total_sent) - pb = PutBytes(self.pebble, PutBytesType.File, lang_pack, bank=0, filename="lang") + pb = PutBytes( + self.pebble, PutBytesType.File, lang_pack, bank=0, filename="lang" + ) pb.register_handler("progress", _handle_progress) pb.send() @@ -34,5 +45,5 @@ def _handle_progress(sent, total_sent, total_length): @classmethod def add_parser(cls, parser): parser = super(InstallLangCcommand, cls).add_parser(parser) - parser.add_argument('lang_file', help="Language file to install") + parser.add_argument("lang_file", help="Language file to install") return parser diff --git a/python_libs/pbl/pbl/commands/test.py b/python_libs/pbl/pbl/commands/test.py index 86f3cbb63a..b882126cc1 100644 --- a/python_libs/pbl/pbl/commands/test.py +++ b/python_libs/pbl/pbl/commands/test.py @@ -4,7 +4,8 @@ class TestCommand(BaseCommand): """Testing!""" - command = 'test' + + command = "test" def __call__(self, *args): super(TestCommand, self).__call__(*args) diff --git a/python_libs/pbl/pyproject.toml b/python_libs/pbl/pyproject.toml index f0af5058be..84cc3c34fd 100644 --- a/python_libs/pbl/pyproject.toml +++ b/python_libs/pbl/pyproject.toml @@ -5,8 +5,7 @@ description = "Internal tool for interacting with pebbles." readme = "README.md" requires-python = ">=3.9" dependencies = [ - # This doesn't install in Python 3 - # "pebble_tool @ git+https://github.com/pebble/pebble-tool.git", + "pebble_tool>=5.0.18", "libpebble2[pulse]>=0.0.23", ] diff --git a/python_libs/pblconvert/pblconvert-runner.py b/python_libs/pblconvert/pblconvert-runner.py index 579892d0bd..3d873707a4 100755 --- a/python_libs/pblconvert/pblconvert-runner.py +++ b/python_libs/pblconvert/pblconvert-runner.py @@ -4,9 +4,8 @@ """Convenience wrapper for running directly from source tree.""" - from pblconvert.pblconvert import main -if __name__ == '__main__': - main() \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/python_libs/pblconvert/pblconvert/__main__.py b/python_libs/pblconvert/pblconvert/__main__.py index 70ec956ea6..32da9cf267 100644 --- a/python_libs/pblconvert/pblconvert/__main__.py +++ b/python_libs/pblconvert/pblconvert/__main__.py @@ -3,6 +3,6 @@ """executed when directory is called as script.""" - from .pblconvert import main + main() diff --git a/python_libs/pblconvert/pblconvert/bin/pbi2png.py b/python_libs/pblconvert/pblconvert/bin/pbi2png.py index 52435051a9..750e7b7308 100644 --- a/python_libs/pblconvert/pblconvert/bin/pbi2png.py +++ b/python_libs/pblconvert/pblconvert/bin/pbi2png.py @@ -5,36 +5,42 @@ from PIL import Image format_dict = { - 'GBitmapFormat1Bit': 0, - 'GBitmapFormat8Bit': 1, - 'GBitmapFormat1BitPalette': 2, - 'GBitmapFormat2BitPalette': 3, - 'GBitmapFormat4BitPalette': 4 + "GBitmapFormat1Bit": 0, + "GBitmapFormat8Bit": 1, + "GBitmapFormat1BitPalette": 2, + "GBitmapFormat2BitPalette": 3, + "GBitmapFormat4BitPalette": 4, } # NOTE: If this changes, please update the GBitmapDump gdb command. class pbi_struct(Structure): _fields_ = [ - ("stride", c_uint16), ("info", c_uint16), - ("bounds_x", c_uint16), ("bounds_y", c_uint16), - ("bounds_w", c_uint16), ("bounds_h", c_uint16), - ] + ("stride", c_uint16), + ("info", c_uint16), + ("bounds_x", c_uint16), + ("bounds_y", c_uint16), + ("bounds_w", c_uint16), + ("bounds_h", c_uint16), + ] + def flip_byte(abyte): - return int('{:08b}'.format(abyte)[::-1],2) + return int("{:08b}".format(abyte)[::-1], 2) + -#converts from argb8 (2-bits per color channel) to RGBA32 (byte per channel) +# converts from argb8 (2-bits per color channel) to RGBA32 (byte per channel) def argb8_to_rgba32(argb8): return ( - ((argb8 >> 4) & 0x3) * 85, #R - ((argb8 >> 2) & 0x3) * 85, #G - ((argb8 ) & 0x3) * 85, #B - ((argb8 >> 6) & 0x3) * 85) #A + ((argb8 >> 4) & 0x3) * 85, # R + ((argb8 >> 2) & 0x3) * 85, # G + ((argb8) & 0x3) * 85, # B + ((argb8 >> 6) & 0x3) * 85, + ) # A def pbi_format(info): - return (info & 0xe) >> 1 + return (info & 0xE) >> 1 def pbi_bitdepth(fmt): @@ -43,7 +49,7 @@ def pbi_bitdepth(fmt): def pbi_is_palettized(fmt): - return fmt >= format_dict['GBitmapFormat1BitPalette'] + return fmt >= format_dict["GBitmapFormat1BitPalette"] def palette_size(fmt): @@ -54,18 +60,25 @@ def pbi_to_png(pbi, pixel_bytearray): gbitmap_version = (pbi.info >> 12) & 0x0F gbitmap_format = pbi_format(pbi.info) # if version is 2 and format is 0x01 (GBitmapFormat8Bit) - if gbitmap_version == 1 and gbitmap_format == format_dict['GBitmapFormat8Bit']: + if gbitmap_version == 1 and gbitmap_format == format_dict["GBitmapFormat8Bit"]: print("8-bit ARGB color image") pixel_rgba_array = bytearray() - for (idx, abyte) in enumerate(pixel_bytearray): + for idx, abyte in enumerate(pixel_bytearray): argb8 = pixel_bytearray[idx] pixel_rgba_array.append(((argb8 >> 4) & 0x3) * 85) # r pixel_rgba_array.append(((argb8 >> 2) & 0x3) * 85) # g pixel_rgba_array.append(((argb8 >> 0) & 0x3) * 85) # b pixel_rgba_array.append(((argb8 >> 6) & 0x3) * 85) # a - png = Image.frombuffer('RGBA', (pbi.bounds_w, pbi.bounds_h), - buffer(pixel_rgba_array), 'raw', 'RGBA', pbi.stride * 4, 1) + png = Image.frombuffer( + "RGBA", + (pbi.bounds_w, pbi.bounds_h), + buffer(pixel_rgba_array), + "raw", + "RGBA", + pbi.stride * 4, + 1, + ) elif gbitmap_version == 1 and pbi_is_palettized(gbitmap_format): bitdepth = pbi_bitdepth(gbitmap_format) @@ -85,9 +98,13 @@ def pbi_to_png(pbi, pixel_bytearray): for i in xrange(0, 8 / bitdepth): # only append actual pixels, ignoring padding pixels # which is the difference between the width and the stride - if (idx % (pbi.stride * (8 / bitdepth)) < pbi.bounds_w): + if idx % (pbi.stride * (8 / bitdepth)) < pbi.bounds_w: pixels.append( - ((pxl8 >> (bitdepth * (8 / bitdepth - (i + 1)))) & ~(~0 << bitdepth))) + ( + (pxl8 >> (bitdepth * (8 / bitdepth - (i + 1)))) + & ~(~0 << bitdepth) + ) + ) idx = idx + 1 # Manually convert from paletted to RGBA @@ -96,21 +113,29 @@ def pbi_to_png(pbi, pixel_bytearray): for pal_pxl in pixels: rgba_pixels.append(palette[pal_pxl]) - png = Image.new('RGBA', (pbi.bounds_w, pbi.bounds_h)) + png = Image.new("RGBA", (pbi.bounds_w, pbi.bounds_h)) png.putdata(rgba_pixels) # legacy 1-bit format - elif gbitmap_version == 0 or \ - (gbitmap_version == 1 and gbitmap_format == format_dict['GBitmapFormat1Bit']): + elif gbitmap_version == 0 or ( + gbitmap_version == 1 and gbitmap_format == format_dict["GBitmapFormat1Bit"] + ): print("1-bit b&w image") # pbi has bits in bytes reversed, so flip here - for (idx, abyte) in enumerate(pixel_bytearray): + for idx, abyte in enumerate(pixel_bytearray): pixel_bytearray[idx] = flip_byte(pixel_bytearray[idx]) - png = Image.frombuffer('1', (pbi.bounds_w, pbi.bounds_h), - buffer(pixel_bytearray), 'raw', '1', pbi.stride, 1) + png = Image.frombuffer( + "1", + (pbi.bounds_w, pbi.bounds_h), + buffer(pixel_bytearray), + "raw", + "1", + pbi.stride, + 1, + ) else: - print "Bad PBI" + print("Bad PBI") png = None return png @@ -131,7 +156,7 @@ def main(): print("Converting PBI to PNG...") pbi = pbi_struct() pixel_bytearray = bytearray() - with open(input_filename, 'rb') as afile: + with open(input_filename, "rb") as afile: afile.readinto(pbi) print("x:%d y:%d" % (pbi.bounds_x, pbi.bounds_y)) print("Width:%d Height:%d" % (pbi.bounds_w, pbi.bounds_h)) @@ -141,5 +166,6 @@ def main(): png = pbi_to_png(pbi, pixel_bytearray) png.save(output_filename) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/python_libs/pblconvert/pblconvert/exceptions.py b/python_libs/pblconvert/pblconvert/exceptions.py index 48364578b8..89a039661c 100644 --- a/python_libs/pblconvert/pblconvert/exceptions.py +++ b/python_libs/pblconvert/pblconvert/exceptions.py @@ -1,6 +1,7 @@ class PblConvertError(Exception): def __str__(self): - return self.__class__.__name__ + ': ' + ' '.join(self.args) + return self.__class__.__name__ + ": " + " ".join(self.args) + class PblConvertFormatError(PblConvertError): pass diff --git a/python_libs/pblconvert/pblconvert/gif2apng/exceptions.py b/python_libs/pblconvert/pblconvert/gif2apng/exceptions.py index 170d00fbcf..9d0c8069de 100644 --- a/python_libs/pblconvert/pblconvert/gif2apng/exceptions.py +++ b/python_libs/pblconvert/pblconvert/gif2apng/exceptions.py @@ -1,6 +1,6 @@ class Gif2ApngError(Exception): def __str__(self): - return self.__class__.__name__ + ': ' + ' '.join(self.args) + return self.__class__.__name__ + ": " + " ".join(self.args) class Gif2ApngFormatError(Gif2ApngError): diff --git a/python_libs/pblconvert/pblconvert/gif2apng/gif.py b/python_libs/pblconvert/pblconvert/gif2apng/gif.py index d1c8cab739..873fc64556 100644 --- a/python_libs/pblconvert/pblconvert/gif2apng/gif.py +++ b/python_libs/pblconvert/pblconvert/gif2apng/gif.py @@ -1,6 +1,5 @@ from __future__ import print_function import imghdr -import io import os import subprocess import sys @@ -9,15 +8,15 @@ # gif2apng from exceptions import * -GIF2APNG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../bin/gif2apng') -COLORMAP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'colormap.txt') +GIF2APNG_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "../bin/gif2apng" +) +COLORMAP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "colormap.txt") def read_gif(obj): data = obj.read() - if imghdr.what(None, data) != 'gif': + if imghdr.what(None, data) != "gif": raise Gif2ApngFormatError("{} is not a valid GIF data".format(path)) return data @@ -32,13 +31,21 @@ def convert_to_apng(gif): # Map onto Pebble colors mod_file = tempfile.NamedTemporaryFile(delete=False) mod_file.close() - p = subprocess.Popen(['gifsicle', - '--colors', '64', - '--use-colormap', COLORMAP_PATH, - '-O1', - '-o', mod_file.name, - gif_file.name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + [ + "gifsicle", + "--colors", + "64", + "--use-colormap", + COLORMAP_PATH, + "-O1", + "-o", + mod_file.name, + gif_file.name, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = p.communicate() # Deal with https://github.com/kohler/gifsicle/issues/28 # Which still exists in some of the packages out there @@ -49,11 +56,11 @@ def convert_to_apng(gif): # Convert to APNG apng_file = tempfile.NamedTemporaryFile(delete=False) apng_file.close() - p = subprocess.Popen([GIF2APNG_PATH, - '-z0', - mod_file.name, - apng_file.name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + [GIF2APNG_PATH, "-z0", mod_file.name, apng_file.name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = p.communicate() if p.returncode != 0: print(err, file=sys.stderr) diff --git a/python_libs/pblconvert/pblconvert/handlers.py b/python_libs/pblconvert/pblconvert/handlers.py index 62789af983..cf3c320599 100644 --- a/python_libs/pblconvert/pblconvert/handlers.py +++ b/python_libs/pblconvert/pblconvert/handlers.py @@ -54,8 +54,9 @@ def format(cls): def read(self, in_obj): try: - surface = surface_from_svg(bytestring=in_obj.read(), - approximate_bezier=True) + surface = surface_from_svg( + bytestring=in_obj.read(), approximate_bezier=True + ) except Svg2PdcFormatError as e: raise PblConvertFormatError(e.args[0]) return surface diff --git a/python_libs/pblconvert/pblconvert/pblconvert.py b/python_libs/pblconvert/pblconvert/pblconvert.py index ae8a9a0efe..fc2e558ef0 100644 --- a/python_libs/pblconvert/pblconvert/pblconvert.py +++ b/python_libs/pblconvert/pblconvert/pblconvert.py @@ -14,11 +14,10 @@ "png": ".png", "svg": ".svg", "apng": ".apng", - } + }, } -FORMAT_TO_EXT = dict(SUPPORTED_FORMATS_MAP["in"], - **SUPPORTED_FORMATS_MAP["out"]) +FORMAT_TO_EXT = dict(SUPPORTED_FORMATS_MAP["in"], **SUPPORTED_FORMATS_MAP["out"]) EXT_TO_FORMAT = {v: k for k, v in FORMAT_TO_EXT.items()} OUT_FORMATS = SUPPORTED_FORMATS_MAP["out"].keys() @@ -29,14 +28,10 @@ def parse_args(args): parser = argparse.ArgumentParser() - parser.add_argument('-i', '--infile', - type=argparse.FileType('r'), required=True) - parser.add_argument('-if', '--informat', - type=str, choices=IN_FORMATS) - parser.add_argument('-o', '--outfile', - type=argparse.FileType('w')) - parser.add_argument('-of', '--outformat', - type=str, choices=OUT_FORMATS) + parser.add_argument("-i", "--infile", type=argparse.FileType("r"), required=True) + parser.add_argument("-if", "--informat", type=str, choices=IN_FORMATS) + parser.add_argument("-o", "--outfile", type=argparse.FileType("w")) + parser.add_argument("-of", "--outformat", type=str, choices=OUT_FORMATS) parsed = parser.parse_args(args) @@ -45,19 +40,25 @@ def parse_args(args): if parsed.informat is None: parsed.informat = EXT_TO_FORMAT.get( - os.path.splitext(parsed.infile.name)[1], "svg") + os.path.splitext(parsed.infile.name)[1], "svg" + ) if parsed.outformat is None: - parsed.outformat = "pdc" if parsed.outfile is None else \ - EXT_TO_FORMAT.get(os.path.splitext(parsed.outfile.name)[1], "pdc") + parsed.outformat = ( + "pdc" + if parsed.outfile is None + else EXT_TO_FORMAT.get(os.path.splitext(parsed.outfile.name)[1], "pdc") + ) if parsed.outfile is None: if parsed.infile == sys.stdin: parsed.outfile = sys.stdout else: # look at format - outfile_path = os.path.splitext(parsed.infile.name)[0] + \ - FORMAT_TO_EXT[parsed.outformat] + outfile_path = ( + os.path.splitext(parsed.infile.name)[0] + + FORMAT_TO_EXT[parsed.outformat] + ) if os.path.exists(outfile_path): avoiding_path = None # avoid accidental overrides @@ -69,9 +70,10 @@ def parse_args(args): break if outfile_path != avoiding_path: - raise IOError("File %s and (%d similar alternatives) " - "already exists" % - (outfile_path, LIMIT_WHEN_AVOIDING_OVERRIDE)) + raise IOError( + "File %s and (%d similar alternatives) " + "already exists" % (outfile_path, LIMIT_WHEN_AVOIDING_OVERRIDE) + ) parsed.outfile = open(outfile_path, "w") diff --git a/python_libs/pblconvert/pblconvert/svg2pdc/annotation.py b/python_libs/pblconvert/pblconvert/svg2pdc/annotation.py index cba32eba29..c2b05e2ae5 100644 --- a/python_libs/pblconvert/pblconvert/svg2pdc/annotation.py +++ b/python_libs/pblconvert/pblconvert/svg2pdc/annotation.py @@ -1,6 +1,6 @@ # coding=utf-8 from xml.etree import ElementTree -from pdc import extend_bounding_box, bounding_box_around_points +from pdc import bounding_box_around_points NS_ANNOTATION = "http://www.pebble.com/2015/pdc" PREFIX_ANNOTATION = "pdc" @@ -8,10 +8,12 @@ TAG_HIGHLIGHT = "{%s}highlight" % NS_ANNOTATION TAG_ANNOTATION = "{%s}annotation" % NS_ANNOTATION + def to_str(value): return "%.2f" % float(value) -class Annotation(): + +class Annotation: def __init__(self, node, text, transformed=False, link=None): self.node = node self.text = text @@ -27,7 +29,9 @@ def __init__(self, node, text, transformed=False, link=None): def add_highlight(self, x, y, width=None, height=None, details=None): highlight = ElementTree.SubElement(self.element, TAG_HIGHLIGHT) - self.set_highlight(highlight, x=x, y=y, width=width, height=height, details=details) + self.set_highlight( + highlight, x=x, y=y, width=width, height=height, details=details + ) self.highlights.append(highlight) return highlight @@ -59,7 +63,9 @@ def transform(self, transformer): bottom_right = transformer.transform_point(bottom_right) bottom_left = transformer.transform_point(bottom_left) - box = bounding_box_around_points([top_left, top_right, bottom_right, bottom_left]) - self.set_highlight(highlight, x=box[0], y=box[1], width=box[2], height=box[3]) - - + box = bounding_box_around_points( + [top_left, top_right, bottom_right, bottom_left] + ) + self.set_highlight( + highlight, x=box[0], y=box[1], width=box[2], height=box[3] + ) diff --git a/python_libs/pblconvert/pblconvert/svg2pdc/exceptions.py b/python_libs/pblconvert/pblconvert/svg2pdc/exceptions.py index 0a26ff5581..057f34b1c1 100644 --- a/python_libs/pblconvert/pblconvert/svg2pdc/exceptions.py +++ b/python_libs/pblconvert/pblconvert/svg2pdc/exceptions.py @@ -1,6 +1,7 @@ class Svg2PdcError(Exception): def __str__(self): - return self.__class__.__name__ + ': ' + ' '.join(self.args) + return self.__class__.__name__ + ": " + " ".join(self.args) + class Svg2PdcFormatError(Svg2PdcError): pass diff --git a/python_libs/pblconvert/pblconvert/svg2pdc/pdc.py b/python_libs/pblconvert/pblconvert/svg2pdc/pdc.py index 95eb72d39b..dd18e71773 100644 --- a/python_libs/pblconvert/pblconvert/svg2pdc/pdc.py +++ b/python_libs/pblconvert/pblconvert/svg2pdc/pdc.py @@ -5,8 +5,11 @@ from struct import pack import sys from subprocess import Popen, PIPE -from pebble_image_routines import truncate_color_to_pebble64_palette, nearest_color_to_pebble64_palette, \ - rgba32_triplet_to_argb8 +from pebble_image_routines import ( + truncate_color_to_pebble64_palette, + nearest_color_to_pebble64_palette, + rgba32_triplet_to_argb8, +) DRAW_COMMAND_VERSION = 1 DRAW_COMMAND_TYPE_PATH = 1 @@ -15,16 +18,25 @@ epsilon = sys.float_info.epsilon + def valid_color(r, g, b, a): - return (r <= 0xFF) and (g <= 0xFF) and (b <= 0xFF) and (a <= 0xFF) and \ - (r >= 0x00) and (g >= 0x00) and (b >= 0x00) and (a >= 0x00) + return ( + (r <= 0xFF) + and (g <= 0xFF) + and (b <= 0xFF) + and (a <= 0xFF) + and (r >= 0x00) + and (g >= 0x00) + and (b >= 0x00) + and (a >= 0x00) + ) def convert_color(r, g, b, a, truncate=True): valid = valid_color(r, g, b, a) if not valid: - print "Invalid color: ({}, {}, {}, {})".format(r, g, b, a) + print("Invalid color: ({}, {}, {}, {})".format(r, g, b, a)) return 0 if truncate: @@ -34,6 +46,7 @@ def convert_color(r, g, b, a, truncate=True): return rgba32_triplet_to_argb8(r, g, b, a) + def sum_points(p1, p2): return p1[0] + p2[0], p1[1] + p2[1] @@ -43,8 +56,10 @@ def subtract_points(p1, p2): def round_point(p): - return round(p[0] + epsilon), round(p[1] + epsilon) # hack to get around the fact that python rounds negative - # numbers downwards + return round(p[0] + epsilon), round( + p[1] + epsilon + ) # hack to get around the fact that python rounds negative + # numbers downwards def scale_point(p, factor): @@ -64,14 +79,21 @@ def convert_to_pebble_coordinates(point, precise=False): # both if not precise: - nearest = find_nearest_valid_point(point) # used to give feedback to user if the point shifts considerably + nearest = find_nearest_valid_point( + point + ) # used to give feedback to user if the point shifts considerably else: nearest = find_nearest_valid_precise_point(point) - problem = None if compare_points(point, nearest) else "Invalid point: ({:.2f}, {:.2f}). Used closest supported coordinate: ({}, {})".format( - point[0], point[1], nearest[0], nearest[1]) + problem = ( + None + if compare_points(point, nearest) + else "Invalid point: ({:.2f}, {:.2f}). Used closest supported coordinate: ({}, {})".format( + point[0], point[1], nearest[0], nearest[1] + ) + ) - translated = sum_points(point, (-0.5, -0.5)) # translate point by (-0.5, -0.5) + translated = sum_points(point, (-0.5, -0.5)) # translate point by (-0.5, -0.5) if precise: translated = scale_point(translated, 8) # scale point for precise coordinates rounded = round_point(translated) @@ -123,7 +145,7 @@ def convert_to_png(pdc_data): with open(pdc_path, "wb") as pdc_file: pdc_file.write(pdc_data) - cmd = '%s %s' % (PDC2PNG, pdc_path) + cmd = "%s %s" % (PDC2PNG, pdc_path) p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if p.returncode != 0: @@ -137,7 +159,7 @@ def convert_to_png(pdc_data): class Command: - ''' + """ Draw command serialized structure: | Bytes | Field | 1 | Draw command type @@ -156,10 +178,11 @@ class Command: Point: | 2 | x | 2 | y - ''' + """ - def __init__(self, points, stroke_width=0, stroke_color=0, fill_color=0, - raise_error=False): + def __init__( + self, points, stroke_width=0, stroke_color=0, fill_color=0, raise_error=False + ): # for i in range(len(points)): # points[i], valid = convert_to_pebble_coordinates(points[i], precise) # if not valid and raise_error: @@ -184,10 +207,12 @@ def finalize(self, annotator): if problem is not None: if grid_annotation is None: link = "https://pebbletechnology.atlassian.net/wiki/display/DEV/Pebble+Draw+Commands#PebbleDrawCommands-issue-pixelgrid" - grid_annotation = annotator.add_annotation("Element is expressed with unsupported coordinate(s).", link=link) + grid_annotation = annotator.add_annotation( + "Element is expressed with unsupported coordinate(s).", + link=link, + ) grid_annotation.add_highlight(p[0], p[1], details=problem) - pass def bounding_box(self): @@ -197,55 +222,77 @@ def bounding_box(self): return result def serialize_common(self): - return pack('> 4) & 0x3) * 85, # R - ((i >> 2) & 0x3) * 85, # G - ((i ) & 0x3) * 85)) # B + pebble_palette.append( + ( + ((i >> 4) & 0x3) * 85, # R + ((i >> 2) & 0x3) * 85, # G + ((i) & 0x3) * 85, + ) + ) # B return pebble_palette @@ -46,10 +49,10 @@ def nearest_color_to_pebble2_palette(r, g, b, a): """ # these constants come from ITU-R recommendation BT.709 - luma = (r * 0.2126 + g * 0.7152 + b * 0.11) + luma = r * 0.2126 + g * 0.7152 + b * 0.11 def round_to_1_bit(value): - """ Round a [0-255] value to either 0 or 255 """ + """Round a [0-255] value to either 0 or 255""" if value > (255 / 2): return 255 return 0 @@ -104,7 +107,7 @@ def rgba32_triplet_to_argb8(r, g, b, a): # convert 32-bit color (r, g, b, a) to 32-bit RGBA word def rgba32_triplet_to_rgba32(r, g, b, a): - return (((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF)) + return ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF) # takes number of colors and outputs PNG & PBI compatible bit depths for paletted images diff --git a/python_libs/pblconvert/pblconvert/svg2pdc/svg.py b/python_libs/pblconvert/pblconvert/svg2pdc/svg.py index 22e3b61515..9cb1289bb3 100644 --- a/python_libs/pblconvert/pblconvert/svg2pdc/svg.py +++ b/python_libs/pblconvert/pblconvert/svg2pdc/svg.py @@ -8,9 +8,17 @@ from cairosvg.surface import size, node_format, normalize, gradient_or_pattern, color from cairosvg.surface.helpers import point, paint import io -from pdc import PathCommand, CircleCommand, extend_bounding_box, bounding_box_around_points +from pdc import ( + PathCommand, + CircleCommand, + extend_bounding_box, + bounding_box_around_points, +) from annotation import Annotation, NS_ANNOTATION, PREFIX_ANNOTATION, TAG_HIGHLIGHT -from pebble_image_routines import truncate_color_to_pebble64_palette, rgba32_triplet_to_argb8 +from pebble_image_routines import ( + truncate_color_to_pebble64_palette, + rgba32_triplet_to_argb8, +) try: import cairocffi as cairo @@ -33,14 +41,14 @@ def line_to(self, x, y): # http://effbot.org/zone/element-lib.htm#prettyprint def indent(elem, level=0): - i = "\n" + level*" " + i = "\n" + level * " " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: - indent(elem, level+1) + indent(elem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: @@ -50,7 +58,9 @@ def indent(elem, level=0): class PDCSurface(cairosvg.surface.PNGSurface): # noinspection PyMissingConstructor - def __init__(self, tree, output, dpi, parent_surface=None, approximate_bezier=False): + def __init__( + self, tree, output, dpi, parent_surface=None, approximate_bezier=False + ): self.svg_tree = tree self.cairo = None self.cairosvg_tags = [] @@ -94,12 +104,14 @@ def __init__(self, tree, output, dpi, parent_surface=None, approximate_bezier=Fa # Actual surface dimensions: may be rounded on raster surfaces types self.cairo, self.width, self.height = self._create_surface( width * self.device_units_per_user_units, - height * self.device_units_per_user_units) + height * self.device_units_per_user_units, + ) self.page_sizes.append((self.width, self.height)) self.context = PDCContext(self.cairo) # We must scale the context as the surface size is using physical units self.context.scale( - self.device_units_per_user_units, self.device_units_per_user_units) + self.device_units_per_user_units, self.device_units_per_user_units + ) # SVG spec says "viewbox of size 0 means 'don't render'" if viewbox is not None and viewbox[2] <= 0 and viewbox[3] <= 0: @@ -117,7 +129,9 @@ def __init__(self, tree, output, dpi, parent_surface=None, approximate_bezier=Fa # remove all PDC elements (annotations in case we're processing an annotated SVG) def remove_pdc_elements(elem): for child in elem: - if isinstance(child.tag, str) and child.tag.startswith("{%s}" % NS_ANNOTATION): + if isinstance(child.tag, str) and child.tag.startswith( + "{%s}" % NS_ANNOTATION + ): elem.remove(child) else: remove_pdc_elements(child) @@ -145,16 +159,25 @@ def cairo_tag_func(self, tag): def cairo_tags_push_and_wrap(self): self.cairosvg_tags.append(cairosvg.surface.TAGS.copy()) - custom_impl = {"polyline": polyline, "polygon": polygon, "line": line, "rect": rect, "circle": circle, - "path": path, "svg": svg} - for k,v in custom_impl.iteritems(): + custom_impl = { + "polyline": polyline, + "polygon": polygon, + "line": line, + "rect": rect, + "circle": circle, + "path": path, + "svg": svg, + } + for k, v in custom_impl.iteritems(): original = self.cairo_tag_func(k) cairosvg.surface.TAGS[k] = partial(custom_impl[k], original=original) def draw_root(self, node): - if node.get("display", "").upper() == 'NONE': + if node.get("display", "").upper() == "NONE": node.annotations = [] - Annotation(node, 'Attribute display="none" for root element will be ignored.') + Annotation( + node, 'Attribute display="none" for root element will be ignored.' + ) node.pop("display") super(PDCSurface, self).draw_root(node) @@ -202,7 +225,9 @@ def svg_color(surface, node, opacity, attribute, default=None): if gradient_or_pattern(surface, node, paint_source): return 0 (r, g, b, a) = color(paint_color, opacity) - color256 = truncate_color_to_pebble64_palette(int(r*255), int(g*255), int(b*255), int(a*255)) + color256 = truncate_color_to_pebble64_palette( + int(r * 255), int(g * 255), int(b * 255), int(a * 255) + ) gcolor8 = rgba32_triplet_to_argb8(*color256) return gcolor8 if gcolor8_is_visible(gcolor8) else 0 @@ -210,9 +235,9 @@ def svg_color(surface, node, opacity, attribute, default=None): def svg(surface, node, original=None): original(surface, node) width, height, viewbox = node_format(surface, node) - if not "width" in node: + if "width" not in node: width = None if viewbox is None else viewbox[2] - if not "height" in node: + if "height" not in node: height = None if viewbox is None else viewbox[3] if (width is not None) and (height is not None): surface.stored_size = (width, height) @@ -221,13 +246,15 @@ def svg(surface, node, original=None): def line(surface, node, original=None): x1, y1, x2, y2 = tuple( size(surface, node.get(position), position[0]) - for position in ("x1", "y1", "x2", "y2")) + for position in ("x1", "y1", "x2", "y2") + ) points = [(x1, y1), (x2, y2)] command = PathCommand(points, path_open=True, precise=True) handle_command(surface, node, command) return original(surface, node) + def polygon(surface, node, original=None): return poly_element(surface, node, open=False, original=original) @@ -292,16 +319,18 @@ def circle(surface, node, original=None): def cubicbezier_mid(p0, p1, p2, p3, min_dist, l, r): t = (r[0] + l[0]) / 2 - a = (1. - t)**3 - b = 3. * t * (1. - t)**2 + a = (1.0 - t) ** 3 + b = 3.0 * t * (1.0 - t) ** 2 c = 3.0 * t**2 * (1.0 - t) d = t**3 - p = (a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0], - a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1]) + p = ( + a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0], + a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1], + ) def pt_dist(p1, p2): - return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)**0.5 + return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5 if pt_dist(l[1], p) <= min_dist or pt_dist(r[1], p) <= min_dist: return [] @@ -311,6 +340,7 @@ def pt_dist(p1, p2): return left + [p] + right + def cubicbezier(p0, p1, p2, p3, min_dist): return [p0] + cubicbezier_mid(p0, p1, p2, p3, min_dist, (0.0, p0), (1.0, p3)) + [p3] @@ -325,8 +355,10 @@ def __init__(self, target, node, approximate_bezier): self.grouped_annotations = {} def get_grouped_description(self, description, *args, **kwargs): - if not description in self.grouped_annotations: - self.grouped_annotations[description] = self.add_annotation(description, *args, **kwargs) + if description not in self.grouped_annotations: + self.grouped_annotations[description] = self.add_annotation( + description, *args, **kwargs + ) return self.grouped_annotations[description] @@ -368,8 +400,9 @@ def curve_to(self, x1, y1, x2, y2, x3, y3): self.add_current_point() link = "https://pebbletechnology.atlassian.net/wiki/display/DEV/Pebble+Draw+Commands#PebbleDrawCommands-issue-bezier" - self.get_grouped_description("Element contains unsupported curved command(s).", link=link).add_highlight(*box) - + self.get_grouped_description( + "Element contains unsupported curved command(s).", link=link + ).add_highlight(*box) def rel_curve_to(self, dx1, dy1, dx2, dy2, dx3, dy3): cur = self.get_current_point() @@ -396,13 +429,15 @@ def close_path(self): def add_annotation_arc_unsupported(self, xc, yc, radius): # caller uses context transforms to express arcs points = [ - (xc-radius, yc-radius), # top-left - (xc+radius, yc-radius), # top-right - (xc+radius, yc+radius), # bottom-right - (xc-radius, yc+radius), # bottom-left + (xc - radius, yc - radius), # top-left + (xc + radius, yc - radius), # top-right + (xc + radius, yc + radius), # bottom-right + (xc - radius, yc + radius), # bottom-left ] box = bounding_box_around_points([self.user_to_device(*p) for p in points]) - self.get_grouped_description("Element contains unsupported arc command(s).").add_highlight(*box) + self.get_grouped_description( + "Element contains unsupported arc command(s)." + ).add_highlight(*box) class PathSurface: @@ -439,6 +474,7 @@ def transform_distance(self, dx, dy): def add_annotation(self, *args, **kwargs): return Annotation(self.node, *args, **kwargs) + def handle_command(surface, node, command): opacity = float(node.get("opacity", 1)) # Get stroke and fill opacity @@ -460,7 +496,9 @@ def handle_command(surface, node, command): command.transform(transformer) if command.stroke_width and node.get("vector-effect") != "non-scaling-stroke": transformed_stroke = transformer.transform_distance(command.stroke_width, 0) - transformed_stroke_width = (transformed_stroke[0]**2 + transformed_stroke[1]**2)**0.5 + transformed_stroke_width = ( + transformed_stroke[0] ** 2 + transformed_stroke[1] ** 2 + ) ** 0.5 command.stroke_width = transformed_stroke_width for annotation in node.annotations: annotation.transform(transformer) @@ -469,8 +507,14 @@ def handle_command(surface, node, command): # Manage display and visibility display = node.get("display", "inline") != "none" - visible = display and (node.get("visibility", "visible") != "hidden") and \ - (gcolor8_is_visible(command.fill_color) or gcolor8_is_visible(command.stroke_color)) + visible = ( + display + and (node.get("visibility", "visible") != "hidden") + and ( + gcolor8_is_visible(command.fill_color) + or gcolor8_is_visible(command.stroke_color) + ) + ) if visible: surface.pdc_commands.append(command) diff --git a/python_libs/pblconvert/pblconvert/version.py b/python_libs/pblconvert/pblconvert/version.py index 0f9c9c26ae..895d1fc138 100644 --- a/python_libs/pblconvert/pblconvert/version.py +++ b/python_libs/pblconvert/pblconvert/version.py @@ -1,2 +1,2 @@ __version_info__ = (0, 0, 2) -__version__ = '.'.join(map(str, __version_info__)) +__version__ = ".".join(map(str, __version_info__)) diff --git a/python_libs/pblconvert/tests/svg2pdc/test_examples.py b/python_libs/pblconvert/tests/svg2pdc/test_examples.py index 3b1209a8ec..93cf234043 100644 --- a/python_libs/pblconvert/tests/svg2pdc/test_examples.py +++ b/python_libs/pblconvert/tests/svg2pdc/test_examples.py @@ -10,15 +10,20 @@ class RenderExamplesTest(unittest.TestCase): - def all_examples(self): result = [] - directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "examples") + directory = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "examples" + ) for root, dirs, files in os.walk(directory): for f in files: - if f.endswith(".svg") and not f.endswith(".annotated.svg") \ - and not root.endswith(".actual") and not root.endswith(".expected"): + if ( + f.endswith(".svg") + and not f.endswith(".annotated.svg") + and not root.endswith(".actual") + and not root.endswith(".expected") + ): svg_file = os.path.join(root, f) result.append(svg_file) @@ -53,7 +58,7 @@ def test_examples(self): et = surface.element_tree() et.write(f, pretty_print=True) - annotated_png_path = os.path.splitext(annotated_path)[0]+'.png' + annotated_png_path = os.path.splitext(annotated_path)[0] + ".png" with open(annotated_png_path, "wb") as f: annotated_png_data = surface.render_annoations_on_top(png_path) f.write(annotated_png_data) @@ -68,14 +73,25 @@ def test_examples(self): def assertContainsSameFiles(self, actual_dir, expected_dir): cmp = dircmp(actual_dir, expected_dir, ignore=[".DS_Store"]) - self.assertTrue(len(cmp.left_only) == 0, - "'%s' missing in %s" % (",".join(cmp.left_only), cmp.right)) - self.assertTrue(len(cmp.right_only) == 0, - "'%s' unexpected in %s" % (",".join(cmp.right_only), cmp.left)) - self.assertTrue(len(cmp.diff_files) == 0, - "'%s' different between %s and %s" % (",".join(cmp.diff_files), cmp.left, cmp.right)) - self.assertTrue(len(cmp.funny_files) == 0, - "'%s' funny between %s and %s" % (",".join(cmp.funny_files), cmp.left, cmp.right)) - -if __name__ == '__main__': + self.assertTrue( + len(cmp.left_only) == 0, + "'%s' missing in %s" % (",".join(cmp.left_only), cmp.right), + ) + self.assertTrue( + len(cmp.right_only) == 0, + "'%s' unexpected in %s" % (",".join(cmp.right_only), cmp.left), + ) + self.assertTrue( + len(cmp.diff_files) == 0, + "'%s' different between %s and %s" + % (",".join(cmp.diff_files), cmp.left, cmp.right), + ) + self.assertTrue( + len(cmp.funny_files) == 0, + "'%s' funny between %s and %s" + % (",".join(cmp.funny_files), cmp.left, cmp.right), + ) + + +if __name__ == "__main__": unittest.main() diff --git a/python_libs/pblconvert/tests/svg2pdc/test_logic.py b/python_libs/pblconvert/tests/svg2pdc/test_logic.py index 974e24d2eb..c9162ae5e6 100644 --- a/python_libs/pblconvert/tests/svg2pdc/test_logic.py +++ b/python_libs/pblconvert/tests/svg2pdc/test_logic.py @@ -7,7 +7,6 @@ class LogicTests(unittest.TestCase): - def setUp(self): super(LogicTests, self).setUp() @@ -51,5 +50,6 @@ def test_annotated_png(self): handler.read.assert_called_once_with(parsed.infile) handler.write_annotated_png.assert_called_once_with(parsed.outfile, "surface") -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/python_libs/pblconvert/tests/svg2pdc/test_parse_args.py b/python_libs/pblconvert/tests/svg2pdc/test_parse_args.py index 5a9264fb35..d10be5cd51 100644 --- a/python_libs/pblconvert/tests/svg2pdc/test_parse_args.py +++ b/python_libs/pblconvert/tests/svg2pdc/test_parse_args.py @@ -1,5 +1,4 @@ import os -import tempfile import unittest import sys import argparse @@ -7,21 +6,23 @@ from pblconvert.pblconvert import parse_args from pblconvert import pblconvert -class FakeFile(): + +class FakeFile: def __init__(self, name): self.name = name + class FakeFileType(object): - def __init__(self, mode='r', bufsize=-1): + def __init__(self, mode="r", bufsize=-1): self._mode = mode self._bufsize = bufsize def __call__(self, string): # the special argument "-" means sys.std{in,out} - if string == '-': - if 'r' in self._mode: + if string == "-": + if "r" in self._mode: return sys.stdin - elif 'w' in self._mode: + elif "w" in self._mode: return sys.stdout else: msg = _('argument "-" with mode %r') % self._mode @@ -29,6 +30,7 @@ def __call__(self, string): return FakeFile(string) + class ParseArgsTests(unittest.TestCase): def fake_path_exists(self, path): if path in self.files: @@ -37,7 +39,7 @@ def fake_path_exists(self, path): return False def setUp(self): - self.files = [] + self.files = [] argparse.FileType = FakeFileType os.path.exists = self.fake_path_exists self.files.append("temp.svg") @@ -92,5 +94,6 @@ def test_error_if_out_of_alternatives_for_implicit_name(self): finally: pblconvert.LIMIT_WHEN_AVOIDING_OVERRIDE = old_value -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/python_libs/pblconvert/tests/svg2pdc/test_svg2commands.py b/python_libs/pblconvert/tests/svg2pdc/test_svg2commands.py index 55689b266b..07819d2e4d 100644 --- a/python_libs/pblconvert/tests/svg2pdc/test_svg2commands.py +++ b/python_libs/pblconvert/tests/svg2pdc/test_svg2commands.py @@ -4,21 +4,24 @@ import xml.etree.ElementTree as ET -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("..")) from pblconvert.svg2pdc import pdc -from pblconvert.svg2pdc.svg import PDCSurface, surface_from_svg +from pblconvert.svg2pdc.svg import surface_from_svg root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) sys.path.insert(0, root_dir) -svg_header = '' -svg_footer = '' +svg_header = ( + '' +) +svg_footer = "" + def create_root(s): - return ET.fromstring(svg_header + s + '') + return ET.fromstring(svg_header + s + "") def create_element(s): @@ -36,15 +39,18 @@ def parse_svg_elements(svg_text, translate=None): class MyTestCase(unittest.TestCase): - def test_parse_path(self): # test basic vertical line path path_element = '' command = parse_svg_element(path_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 2) - self.assertTrue(pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (-1.5, 4.5)), str(command.points[1])) + self.assertTrue( + pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (-1.5, 4.5)), str(command.points[1]) + ) self.assertTrue(command.open) # test basic multi-line open path described as a sequence of points @@ -52,9 +58,15 @@ def test_parse_path(self): command = parse_svg_element(path_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 3) - self.assertTrue(pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (-3.5, 6.5)), str(command.points[1])) - self.assertTrue(pdc.compare_points(command.points[2], (4.5, 6.5)), str(command.points[2])) + self.assertTrue( + pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (-3.5, 6.5)), str(command.points[1]) + ) + self.assertTrue( + pdc.compare_points(command.points[2], (4.5, 6.5)), str(command.points[2]) + ) self.assertTrue(command.open) # test basic multi-line closed path described as a sequence of points @@ -62,9 +74,15 @@ def test_parse_path(self): command = parse_svg_element(path_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 3) - self.assertTrue(pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (-3.5, 6.5)), str(command.points[1])) - self.assertTrue(pdc.compare_points(command.points[2], (4.5, 6.5)), str(command.points[2])) + self.assertTrue( + pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (-3.5, 6.5)), str(command.points[1]) + ) + self.assertTrue( + pdc.compare_points(command.points[2], (4.5, 6.5)), str(command.points[2]) + ) self.assertFalse(command.open) def test_parse_circle(self): @@ -72,7 +90,9 @@ def test_parse_circle(self): command = parse_svg_element(circle_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.CircleCommand) self.assertEqual(len(command.points), 1) - self.assertTrue(pdc.compare_points(command.points[0], (72.5, 84.5)), str(command.points[0])) + self.assertTrue( + pdc.compare_points(command.points[0], (72.5, 84.5)), str(command.points[0]) + ) self.assertEqual(command.radius, 12.0) def test_parse_polyline(self): @@ -81,32 +101,57 @@ def test_parse_polyline(self): command = parse_svg_element(polyline_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 4) - self.assertTrue(pdc.compare_points(command.points[0], (34.5, 23.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (26.5, 6.5)), str(command.points[1])) - self.assertTrue(pdc.compare_points(command.points[2], (110.5, 6.5)), str(command.points[2])) - self.assertTrue(pdc.compare_points(command.points[3], (118.5, 23.5)), str(command.points[3])) + self.assertTrue( + pdc.compare_points(command.points[0], (34.5, 23.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (26.5, 6.5)), str(command.points[1]) + ) + self.assertTrue( + pdc.compare_points(command.points[2], (110.5, 6.5)), str(command.points[2]) + ) + self.assertTrue( + pdc.compare_points(command.points[3], (118.5, 23.5)), str(command.points[3]) + ) self.assertTrue(command.open) def test_parse_polygon(self): # test polygon (closed path) element parsing - polygon_element = '' + polygon_element = ( + '' + ) command = parse_svg_element(polygon_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 4) - self.assertTrue(pdc.compare_points(command.points[0], (34.5, 23.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (26.5, 6.5)), str(command.points[1])) - self.assertTrue(pdc.compare_points(command.points[2], (110.5, 6.5)), str(command.points[2])) - self.assertTrue(pdc.compare_points(command.points[3], (118.5, 23.5)), str(command.points[3])) + self.assertTrue( + pdc.compare_points(command.points[0], (34.5, 23.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (26.5, 6.5)), str(command.points[1]) + ) + self.assertTrue( + pdc.compare_points(command.points[2], (110.5, 6.5)), str(command.points[2]) + ) + self.assertTrue( + pdc.compare_points(command.points[3], (118.5, 23.5)), str(command.points[3]) + ) self.assertFalse(command.open) def test_parse_line(self): # test line element parsing - line_element = '' + line_element = ( + '' + ) command = parse_svg_element(line_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 2) - self.assertTrue(pdc.compare_points(command.points[0], (26.5, 139.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (118.5, 139.5)), str(command.points[1])) + self.assertTrue( + pdc.compare_points(command.points[0], (26.5, 139.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (118.5, 139.5)), + str(command.points[1]), + ) self.assertTrue(command.open) def test_parse_rect(self): @@ -115,20 +160,36 @@ def test_parse_rect(self): command = parse_svg_element(rect_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 4) - self.assertTrue(pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (2.5, 2.5)), str(command.points[1])) - self.assertTrue(pdc.compare_points(command.points[2], (2.5, 7.5)), str(command.points[2])) - self.assertTrue(pdc.compare_points(command.points[3], (-1.5, 7.5)), str(command.points[3])) + self.assertTrue( + pdc.compare_points(command.points[0], (-1.5, 2.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (2.5, 2.5)), str(command.points[1]) + ) + self.assertTrue( + pdc.compare_points(command.points[2], (2.5, 7.5)), str(command.points[2]) + ) + self.assertTrue( + pdc.compare_points(command.points[3], (-1.5, 7.5)), str(command.points[3]) + ) self.assertFalse(command.open) def test_ignore_display_none(self): - commands = parse_svg_elements('') + commands = parse_svg_elements( + '' + ) self.assertEqual(len(commands), 1) - commands = parse_svg_elements('') + commands = parse_svg_elements( + '' + ) self.assertEqual(len(commands), 0) - commands = parse_svg_elements('') + commands = parse_svg_elements( + '' + ) self.assertEqual(len(commands), 0) - commands = parse_svg_elements('') + commands = parse_svg_elements( + '' + ) self.assertEqual(len(commands), 0) def test_create_command(self): @@ -138,11 +199,19 @@ def test_create_command(self): self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(len(command.points), 3) self.assertEqual(command.fill_color, pdc.convert_color(0xAA, 0xFF, 0x00, 0xFF)) - self.assertEqual(command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0xFF)) + self.assertEqual( + command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0xFF) + ) self.assertEqual(command.stroke_width, 3) - self.assertTrue(pdc.compare_points(command.points[0], (34.5, 23.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (26.5, 6.5)), str(command.points[1])) - self.assertTrue(pdc.compare_points(command.points[2], (110.5, 6.5)), str(command.points[2])) + self.assertTrue( + pdc.compare_points(command.points[0], (34.5, 23.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (26.5, 6.5)), str(command.points[1]) + ) + self.assertTrue( + pdc.compare_points(command.points[2], (110.5, 6.5)), str(command.points[2]) + ) self.assertTrue(command.open) # element should not be created if no color is specified (everything is transparent) @@ -160,31 +229,43 @@ def test_create_command(self): # element with no fill is created element = '' command = parse_svg_element(element) - self.assertEqual(command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0xFF)) + self.assertEqual( + command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0xFF) + ) self.assertEqual(command.stroke_width, 3) self.assertEqual(command.fill_color, 0) # test that opacity is assigned correctly (use opacity = 0.34 to ensure truncation to (255 / 3)) - element = '' + element = ( + '' + ) command = parse_svg_element(element) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(command.fill_color, pdc.convert_color(0xAA, 0xFF, 0x00, 0x55)) - self.assertEqual(command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0x55)) + self.assertEqual( + command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0x55) + ) self.assertEqual(command.stroke_width, 3) - # test that opacity is compounded when 'opacity' tag is included - element = '' + # test that opacity is compounded when 'opacity' tag is included + element = ( + '' + ) command = parse_svg_element(element) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(command.fill_color, 0) - self.assertEqual(command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0x55)) + self.assertEqual( + command.stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0x55) + ) self.assertEqual(command.stroke_width, 3) # stroke color should be set to clear when width is 0 - element = '' + element = ( + '' + ) command = parse_svg_element(element) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(command.fill_color, pdc.convert_color(0xAA, 0xFF, 0x00, 0xFF)) @@ -196,11 +277,18 @@ def test_create_command(self): command = parse_svg_element(element) self.assertIsInstance(command, pdc.PathCommand) self.assertEqual(command.fill_color, 0) - self.assertEqual(command.stroke_color, pdc.convert_color(0x00, 0x00, 0xFF, 0xFF)) + self.assertEqual( + command.stroke_color, pdc.convert_color(0x00, 0x00, 0xFF, 0xFF) + ) self.assertEqual(command.stroke_width, 5) self.assertEqual(len(command.points), 2) - self.assertTrue(pdc.compare_points(command.points[0], (26.0, 139.5)), str(command.points[0])) - self.assertTrue(pdc.compare_points(command.points[1], (118.5, 139.0)), str(command.points[1])) + self.assertTrue( + pdc.compare_points(command.points[0], (26.0, 139.5)), str(command.points[0]) + ) + self.assertTrue( + pdc.compare_points(command.points[1], (118.5, 139.0)), + str(command.points[1]), + ) # test elements that do not describe a path or circle are handled element = '' @@ -208,23 +296,46 @@ def test_create_command(self): self.assertTrue(command is None) def test_parse_svg(self): - root = '' \ - '' + root = ( + '' + '' + ) commands = parse_svg_elements(root) self.assertEqual(len(commands), 2) self.assertEqual(len(commands[0].points), 2) - self.assertTrue(pdc.compare_points(commands[0].points[0], (1.5, 5.5)), str(commands[0].points[0])) - self.assertTrue(pdc.compare_points(commands[0].points[1], (-6.5, -1.5)), str(commands[0].points[1])) + self.assertTrue( + pdc.compare_points(commands[0].points[0], (1.5, 5.5)), + str(commands[0].points[0]), + ) + self.assertTrue( + pdc.compare_points(commands[0].points[1], (-6.5, -1.5)), + str(commands[0].points[1]), + ) self.assertEqual(commands[0].fill_color, 0) - self.assertEqual(commands[0].stroke_color, pdc.convert_color(0x00, 0x00, 0xFF, 0xFF)) + self.assertEqual( + commands[0].stroke_color, pdc.convert_color(0x00, 0x00, 0xFF, 0xFF) + ) self.assertEqual(commands[0].stroke_width, 5) self.assertEqual(len(commands[1].points), 3) - self.assertTrue(pdc.compare_points(commands[1].points[0], (-3.5, 4.5)), str(commands[1].points[0])) - self.assertTrue(pdc.compare_points(commands[1].points[1], (9.5, 6.5)), str(commands[1].points[1])) - self.assertTrue(pdc.compare_points(commands[1].points[2], (7.5, 6.5)), str(commands[1].points[2])) - self.assertEqual(commands[1].fill_color, pdc.convert_color(0xAA, 0xFF, 0x00, 0xFF)) - self.assertEqual(commands[1].stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0xFF)) + self.assertTrue( + pdc.compare_points(commands[1].points[0], (-3.5, 4.5)), + str(commands[1].points[0]), + ) + self.assertTrue( + pdc.compare_points(commands[1].points[1], (9.5, 6.5)), + str(commands[1].points[1]), + ) + self.assertTrue( + pdc.compare_points(commands[1].points[2], (7.5, 6.5)), + str(commands[1].points[2]), + ) + self.assertEqual( + commands[1].fill_color, pdc.convert_color(0xAA, 0xFF, 0x00, 0xFF) + ) + self.assertEqual( + commands[1].stroke_color, pdc.convert_color(0x00, 0x55, 0xFF, 0xFF) + ) self.assertEqual(commands[1].stroke_width, 3) def test_transformed_circle(self): @@ -232,94 +343,114 @@ def test_transformed_circle(self): command = parse_svg_element(circle_element, precise=False, raise_error=False) self.assertIsInstance(command, pdc.CircleCommand) self.assertEqual(len(command.points), 1) - self.assertTrue(pdc.compare_points(command.points[0], (12+72.5, 34+84.5)), str(command.points[0])) + self.assertTrue( + pdc.compare_points(command.points[0], (12 + 72.5, 34 + 84.5)), + str(command.points[0]), + ) self.assertEqual(command.radius, 12.0) def test_scaled_stroke(self): - strokes = '' \ - '' \ - '' \ - '' \ - '' + strokes = ( + '' + '' + '' + '' + "" + ) commands = parse_svg_elements(strokes) self.assertEqual(3, len(commands)) self.assertEqual(9 * 5, commands[0].stroke_width) self.assertEqual(5, commands[1].stroke_width) self.assertEqual(9 * 5, commands[2].stroke_width) - def test_empty_width_height(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (40, 50)) self.assertEqual(surface.pdc_commands[0].bounding_box(), (-19, -29, 5, 5)) def test_only_width_height(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (60, 70)) self.assertEqual(surface.pdc_commands[0].bounding_box(), (1, 1, 5, 5)) def test_width_height_and_box(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (60, 70)) expected = (-22.73333333333333, -40.6, 7.0, 7.0) self.assertEqual(surface.pdc_commands[0].bounding_box(), expected) def test_neither_size_nor_viewbox(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '" + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (6, 6)) self.assertEqual(surface.pdc_commands[0].bounding_box(), (1, 1, 5, 5)) def test_zero_viewbox(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (0, 0)) self.assertEqual(0, len(surface.pdc_commands)) def test_ignore_non_px_size(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (1200, 400)) def test_adds_viewbox_if_missing(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (6, 6)) et = surface.element_tree() self.assertEqual(et.getroot().get("viewBox"), "0 0 6 6") def test_preserves_viewbox_if_exists(self): - svg = '' \ - '' \ - '' + svg = ( + '' + '' + '' + ) surface = surface_from_svg(bytestring=svg) self.assertEqual(surface.size(), (3, 4)) et = surface.element_tree() self.assertEqual(et.getroot().get("viewBox"), "1 2 3 4") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/python_libs/pblprog/.gitignore b/python_libs/pblprog/.gitignore deleted file mode 100644 index 7e3f79c3bb..0000000000 --- a/python_libs/pblprog/.gitignore +++ /dev/null @@ -1,93 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject - -# Rope project settings -.ropeproject - -.waf* -.lock* -*.swp diff --git a/python_libs/pblprog/README.md b/python_libs/pblprog/README.md deleted file mode 100644 index 482e4e81fb..0000000000 --- a/python_libs/pblprog/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# pblprog -Utility for flashing Pebble BigBoards over SWD - -Installation ------------- - -Install from PyPi (https://pebbletechnology.atlassian.net/wiki/display/DEV/pypi) under the package name `pebble.programmer`. - - -Supported devices ------------------ - -It is relatively easy to add support for any STM32/SWD based Pebble BigBoard. Currently supported are: - -- silk_bb -- robert_bb \ No newline at end of file diff --git a/python_libs/pblprog/loader/src/irq_stm32f2_f4.def b/python_libs/pblprog/loader/src/irq_stm32f2_f4.def deleted file mode 100644 index d7232304fd..0000000000 --- a/python_libs/pblprog/loader/src/irq_stm32f2_f4.def +++ /dev/null @@ -1,116 +0,0 @@ -IRQ_DEF(0, WWDG) // Window WatchDog -IRQ_DEF(1, PVD) // PVD through EXTI Line detection -IRQ_DEF(2, TAMP_STAMP) // Tamper and TimeStamps through the EXTI line -IRQ_DEF(3, RTC_WKUP) // RTC Wakeup through the EXTI line -IRQ_DEF(4, FLASH) // FLASH -IRQ_DEF(5, RCC) // RCC -IRQ_DEF(6, EXTI0) // EXTI Line0 -IRQ_DEF(7, EXTI1) // EXTI Line1 -IRQ_DEF(8, EXTI2) // EXTI Line2 -IRQ_DEF(9, EXTI3) // EXTI Line3 -IRQ_DEF(10, EXTI4) // EXTI Line4 -IRQ_DEF(11, DMA1_Stream0) // DMA1 Stream 0 -IRQ_DEF(12, DMA1_Stream1) // DMA1 Stream 1 -IRQ_DEF(13, DMA1_Stream2) // DMA1 Stream 2 -IRQ_DEF(14, DMA1_Stream3) // DMA1 Stream 3 -IRQ_DEF(15, DMA1_Stream4) // DMA1 Stream 4 -IRQ_DEF(16, DMA1_Stream5) // DMA1 Stream 5 -IRQ_DEF(17, DMA1_Stream6) // DMA1 Stream 6 -IRQ_DEF(18, ADC) // ADC1, ADC2 and ADC3s -IRQ_DEF(19, CAN1_TX) // CAN1 TX -IRQ_DEF(20, CAN1_RX0) // CAN1 RX0 -IRQ_DEF(21, CAN1_RX1) // CAN1 RX1 -IRQ_DEF(22, CAN1_SCE) // CAN1 SCE -IRQ_DEF(23, EXTI9_5) // External Line[9:5]s -IRQ_DEF(24, TIM1_BRK_TIM9) // TIM1 Break and TIM9 -IRQ_DEF(25, TIM1_UP_TIM10) // TIM1 Update and TIM10 -IRQ_DEF(26, TIM1_TRG_COM_TIM11) // TIM1 Trigger and Commutation and TIM11 -IRQ_DEF(27, TIM1_CC) // TIM1 Capture Compare -IRQ_DEF(28, TIM2) // TIM2 -IRQ_DEF(29, TIM3) // TIM3 -IRQ_DEF(30, TIM4) // TIM4 -IRQ_DEF(31, I2C1_EV) // I2C1 Event -IRQ_DEF(32, I2C1_ER) // I2C1 Error -IRQ_DEF(33, I2C2_EV) // I2C2 Event -IRQ_DEF(34, I2C2_ER) // I2C2 Error -IRQ_DEF(35, SPI1) // SPI1 -IRQ_DEF(36, SPI2) // SPI2 -IRQ_DEF(37, USART1) // USART1 -IRQ_DEF(38, USART2) // USART2 -IRQ_DEF(39, USART3) // USART3 -IRQ_DEF(40, EXTI15_10) // External Line[15:10]s -IRQ_DEF(41, RTC_Alarm) // RTC Alarm (A and B) through EXTI Line -IRQ_DEF(42, OTG_FS_WKUP) // USB OTG FS Wakeup through EXTI line -IRQ_DEF(43, TIM8_BRK_TIM12) // TIM8 Break and TIM12 -IRQ_DEF(44, TIM8_UP_TIM13) // TIM8 Update and TIM13 -IRQ_DEF(45, TIM8_TRG_COM_TIM14) // TIM8 Trigger and Commutation and TIM14 -IRQ_DEF(46, TIM8_CC) // TIM8 Capture Compare -IRQ_DEF(47, DMA1_Stream7) // DMA1 Stream7 -IRQ_DEF(48, FSMC) // FSMC -IRQ_DEF(49, SDIO) // SDIO -IRQ_DEF(50, TIM5) // TIM5 -IRQ_DEF(51, SPI3) // SPI3 -#if !defined(STM32F412xG) -IRQ_DEF(52, UART4) // UART4 -IRQ_DEF(53, UART5) // UART5 -IRQ_DEF(54, TIM6_DAC) // TIM6 and DAC1&2 underrun errors -#else -IRQ_DEF(54, TIM6) // TIM6 -#endif -IRQ_DEF(55, TIM7) // TIM7 -IRQ_DEF(56, DMA2_Stream0) // DMA2 Stream 0 -IRQ_DEF(57, DMA2_Stream1) // DMA2 Stream 1 -IRQ_DEF(58, DMA2_Stream2) // DMA2 Stream 2 -IRQ_DEF(59, DMA2_Stream3) // DMA2 Stream 3 -IRQ_DEF(60, DMA2_Stream4) // DMA2 Stream 4 -#if !defined(STM32F412xG) -IRQ_DEF(61, ETH) // Ethernet -IRQ_DEF(62, ETH_WKUP) // Ethernet Wakeup through EXTI line -#else -IRQ_DEF(61, DFSDM1) // DFSDM1 -IRQ_DEF(62, DFSDM2) // DFSDM2 -#endif -IRQ_DEF(63, CAN2_TX) // CAN2 TX -IRQ_DEF(64, CAN2_RX0) // CAN2 RX0 -IRQ_DEF(65, CAN2_RX1) // CAN2 RX1 -IRQ_DEF(66, CAN2_SCE) // CAN2 SCE -IRQ_DEF(67, OTG_FS) // USB OTG FS -IRQ_DEF(68, DMA2_Stream5) // DMA2 Stream 5 -IRQ_DEF(69, DMA2_Stream6) // DMA2 Stream 6 -IRQ_DEF(70, DMA2_Stream7) // DMA2 Stream 7 -IRQ_DEF(71, USART6) // USART6 -IRQ_DEF(72, I2C3_EV) // I2C3 event -IRQ_DEF(73, I2C3_ER) // I2C3 error -#if !defined(STM32F412xG) -IRQ_DEF(74, OTG_HS_EP1_OUT) // USB OTG HS End Point 1 Out -IRQ_DEF(75, OTG_HS_EP1_IN) // USB OTG HS End Point 1 In -IRQ_DEF(76, OTG_HS_WKUP) // USB OTG HS Wakeup through EXTI -IRQ_DEF(77, OTG_HS) // USB OTG HS -IRQ_DEF(78, DCMI) // DCMI -IRQ_DEF(79, CRYP) // CRYP crypto -#endif -#if !defined(STM32F412xG) -IRQ_DEF(80, HASH_RNG) // Hash and Rng -#else -IRQ_DEF(80, RNG) // Rng -#endif -#if !defined(STM32F2XX) // STM32F2 IRQs end here -IRQ_DEF(81, FPU) // FPU -#if !defined(STM32F412xG) -IRQ_DEF(82, UART7) // UART7 -IRQ_DEF(83, UART8) // UART8 -#endif -IRQ_DEF(84, SPI4) // SPI4 -IRQ_DEF(85, SPI5) // SPI5 -#if !defined(STM32F412xG) -IRQ_DEF(86, SPI6) // SPI6 -IRQ_DEF(87, SAI1) // SAI1 -IRQ_DEF(88, LTDC) // LTDC -IRQ_DEF(89, LTDC_ER) // LTDC_ER -IRQ_DEF(90, DMA2D) // DMA2D -#else -IRQ_DEF(92, QUADSPI) // QUADSPI -IRQ_DEF(95, FMPI2C1_EV) // FMPI2C1 Event -IRQ_DEF(96, FMPI2C1_ER) // FMPI2C1 Error -#endif -#endif // !defined(STM32F2XX) diff --git a/python_libs/pblprog/loader/src/main.c b/python_libs/pblprog/loader/src/main.c deleted file mode 100644 index 33f5ffec53..0000000000 --- a/python_libs/pblprog/loader/src/main.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#define HEADER_ADDR (0x20000400) -#define DATA_ADDR (0x20000800) -#define FLASH_SR_ADDR (0x40023C0C) - -#define STATE_WAITING (0) -#define STATE_WRITE (1) -#define STATE_CRC (2) - -// typedef to make it easy to change the program size -typedef uint8_t p_size_t; - -typedef struct __attribute__((__packed__)) { - uint32_t state; - volatile p_size_t *addr; - uint32_t length; -} Header; - -static uint8_t prv_crc8(const uint8_t *data, uint32_t data_len) { - uint8_t crc = 0; - - // nibble lookup table for (x^8 + x^5 + x^3 + x^2 + x + 1) - static const uint8_t lookup_table[] = - { 0, 47, 94, 113, 188, 147, 226, 205, 87, 120, 9, 38, 235, 196, 181, 154 }; - - for (uint32_t i = 0; i < data_len * 2; i++) { - uint8_t nibble = data[i / 2]; - - if (i % 2 == 0) { - nibble >>= 4; - } - - uint8_t index = nibble ^ (crc >> 4); - crc = lookup_table[index & 0xf] ^ ((crc << 4) & 0xf0); - } - - return crc; -} - -static void prv_wait_for_flash_not_busy(void) { - while ((*(volatile uint32_t *)FLASH_SR_ADDR) & (1 << 16)); // BSY flag in FLASH_SR -} - -__attribute__((__noreturn__)) void Reset_Handler(void) { - // Disable all interrupts - __asm__("cpsid i" : : : "memory"); - - volatile uint32_t *flash_sr = (volatile uint32_t *)FLASH_SR_ADDR; - volatile p_size_t *data = (volatile p_size_t *)DATA_ADDR; - volatile Header *header = (volatile Header *)HEADER_ADDR; - header->state = STATE_WAITING; - - while(1) { - switch (header->state) { - case STATE_WRITE: - prv_wait_for_flash_not_busy(); - for (uint32_t i = 0; i < header->length / sizeof(p_size_t); i++) { - header->addr[i] = data[i]; - __asm__("isb 0xF":::"memory"); - __asm__("dsb 0xF":::"memory"); - /// Wait until flash isn't busy - prv_wait_for_flash_not_busy(); - if (*flash_sr & (0x1f << 4)) { - // error raised, set bad state - header->state = *flash_sr; - } - if (header->addr[i] != data[i]) { - header->state = 0xbd; - } - } - header->addr += header->length / sizeof(p_size_t); - header->state = STATE_WAITING; - break; - case STATE_CRC: - *data = prv_crc8((uint8_t *)header->addr, header->length); - header->state = STATE_WAITING; - break; - default: - break; - } - } - - __builtin_unreachable(); -} - -//! These symbols are defined in the linker script for use in initializing -//! the data sections. uint8_t since we do arithmetic with section lengths. -//! These are arrays to avoid the need for an & when dealing with linker symbols. -extern uint8_t _estack[]; - - -__attribute__((__section__(".isr_vector"))) const void * const vector_table[] = { - _estack, - Reset_Handler -}; diff --git a/python_libs/pblprog/loader/src/stm32f4_loader.ld b/python_libs/pblprog/loader/src/stm32f4_loader.ld deleted file mode 100644 index ba02ce402b..0000000000 --- a/python_libs/pblprog/loader/src/stm32f4_loader.ld +++ /dev/null @@ -1,136 +0,0 @@ -__Stack_Size = 128; -PROVIDE ( _Stack_Size = __Stack_Size ) ; - -__Stack_Init = _estack - __Stack_Size ; -PROVIDE ( _Stack_Init = __Stack_Init ) ; - -MEMORY -{ - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 1K -} - -SECTIONS -{ - /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */ - .isr_vector : - { - . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - . = ALIGN(4); - } >RAM - - /* for some STRx devices, the beginning of the startup code is stored in the .flashtext section, which goes to FLASH */ - .flashtext : - { - . = ALIGN(4); - *(.flashtext) /* Startup code */ - . = ALIGN(4); - } >RAM - - /* Exception handling sections. "contains index entries for section unwinding" */ - .ARM.exidx : - { - . = ALIGN(4); - *(.ARM.exidx) - . = ALIGN(4); - } >RAM - - /* the program code is stored in the .text section, which goes to Flash */ - .text : - { - . = ALIGN(4); - - *(.text) /* remaining code */ - *(.text.*) /* remaining code */ - *(.rodata) /* read-only data (constants) */ - *(.rodata*) - *(.constdata) /* read-only data (constants) */ - *(.constdata*) - *(.glue_7) - *(.glue_7t) - *(i.*) - - . = ALIGN(4); - } >RAM - - /* This is the initialized data section - The program executes knowing that the data is in the RAM - but the loader puts the initial values in the FLASH (inidata). - It is one task of the startup to copy the initial values from FLASH to RAM. */ - .data : { - . = ALIGN(4); - /* This is used by the startup in order to initialize the .data secion */ - __data_start = .; - - *(.data) - *(.data.*) - - . = ALIGN(4); - __data_end = .; /* This is used by the startup in order to initialize the .data secion */ - } >RAM - __data_load_start = LOADADDR(.data); - - /* This is the uninitialized data section */ - .bss (NOLOAD) : { - . = ALIGN(4); - __bss_start = .; /* This is used by the startup in order to initialize the .bss secion */ - - *(.bss) - *(.bss.*) - *(COMMON) - - . = ALIGN(4); - __bss_end = .; /* This is used by the startup in order to initialize the .bss secion */ - } >RAM - - .stack (NOLOAD) : { - . = ALIGN(8); - _sstack = .; - . = . + __Stack_Size; - . = ALIGN(8); - _estack = .; - } >RAM - - /* after that it's only debugging information. */ - - /* remove the debugging information from the standard libraries */ - DISCARD : { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } -} diff --git a/python_libs/pblprog/loader/waf b/python_libs/pblprog/loader/waf deleted file mode 100755 index 4b322f1a7e..0000000000 --- a/python_libs/pblprog/loader/waf +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python -# encoding: ISO8859-1 -# Thomas Nagy, 2005-2016 - -""" -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -""" - -import os, sys, inspect - -VERSION="1.8.19" -REVISION="b1fc8f7baef51bd2db4c2971909a568d" -GIT="22213cd8abbd141bda40667f7ca2a48f2d6ad785" -INSTALL='' -C1='#5' -C2='#/' -C3='#,' -cwd = os.getcwd() -join = os.path.join - - -WAF='waf' -def b(x): - return x -if sys.hexversion>0x300000f: - WAF='waf3' - def b(x): - return x.encode() - -def err(m): - print(('\033[91mError: %s\033[0m' % m)) - sys.exit(1) - -def unpack_wafdir(dir, src): - f = open(src,'rb') - c = 'corrupt archive (%d)' - while 1: - line = f.readline() - if not line: err('run waf-light from a folder containing waflib') - if line == b('#==>\n'): - txt = f.readline() - if not txt: err(c % 1) - if f.readline() != b('#<==\n'): err(c % 2) - break - if not txt: err(c % 3) - txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) - - import shutil, tarfile - try: shutil.rmtree(dir) - except OSError: pass - try: - for x in ('Tools', 'extras'): - os.makedirs(join(dir, 'waflib', x)) - except OSError: - err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) - - os.chdir(dir) - tmp = 't.bz2' - t = open(tmp,'wb') - try: t.write(txt) - finally: t.close() - - try: - t = tarfile.open(tmp) - except: - try: - os.system('bunzip2 t.bz2') - t = tarfile.open('t') - tmp = 't' - except: - os.chdir(cwd) - try: shutil.rmtree(dir) - except OSError: pass - err("Waf cannot be unpacked, check that bzip2 support is present") - - try: - for x in t: t.extract(x) - finally: - t.close() - - for x in ('Tools', 'extras'): - os.chmod(join('waflib',x), 493) - - if sys.hexversion<0x300000f: - sys.path = [join(dir, 'waflib')] + sys.path - import fixpy2 - fixpy2.fixdir(dir) - - os.remove(tmp) - os.chdir(cwd) - - try: dir = unicode(dir, 'mbcs') - except: pass - try: - from ctypes import windll - windll.kernel32.SetFileAttributesW(dir, 2) - except: - pass - -def test(dir): - try: - os.stat(join(dir, 'waflib')) - return os.path.abspath(dir) - except OSError: - pass - -def find_lib(): - src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) - base, name = os.path.split(src) - - #devs use $WAFDIR - w=test(os.environ.get('WAFDIR', '')) - if w: return w - - #waf-light - if name.endswith('waf-light'): - w = test(base) - if w: return w - err('waf-light requires waflib -> export WAFDIR=/folder') - - dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) - for i in (INSTALL,'/usr','/usr/local','/opt'): - w = test(i + '/lib/' + dirname) - if w: return w - - #waf-local - dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) - w = test(dir) - if w: return w - - #unpack - unpack_wafdir(dir, src) - return dir - -wafdir = find_lib() -sys.path.insert(0, wafdir) - -if __name__ == '__main__': - - from waflib import Scripting - Scripting.waf_entry_point(cwd, VERSION, wafdir) - -#==> -#BZh91AY&SYmEKQ#/#,Y #%H4#,`(br}#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,ӭul]Z}i{l4ﱮ_plkLϱdzvݺ{]xԴRq楯y\e hgEm.ǻuw׺]2]yk={[yw@=ggkWe}}}owNz#,#,#,#,y#,x->#,<*{WLaOl5nۧF#,m6 PPu(QB#5JJeGc@#,P6bAnׯZ>H'{p#/KvPZ6tj>ޏ{zl_7Z}ۡMZlζˮ83oM<k=bWGӪUrz5O{y뽇z#/o3PKhF;l5#5v꽍٦f{%ՎڻsgR˒}}ށ#/#//Z"i@[sǭ^s +ހ>Bq*KxG5_/=='}>9믽e6mvun{E{6u.#/N»^FM>O}ӈXL|RZu.9.;/nۯ7rj;3ek|Pܧ;{U)+Kw^|#,=u #,=^|vާvqw{]8tz#Hۘ@^vlλzދg|6%WWݶx};e>&{o=}{ok:{}kL}>>ya۽v֊ﰣqM_^/:5x_-IڝynO@}o+LUS4Z:=ua+W{Sofw[{\7v_|\V:/nR+wц#/_wہ@mg#,#,:;5kGvsɮW"}t@x6wuNVvFH.SyUr^N/FϧOЦq^;N!yNēN{ovkwV]vzskNۮo6So#|w M @#,ɠ#/iOD=CjAxJhD@i2hj6SD4'#,#,#,4#,#,A!! Abh'Sh 6D='67#,#,#,#,#,'QI4jrVL*iH ?#/7R Ye˩jҵZ*1m#,-BIX(Ȃ![*E#,|R‚  FVmkjL@ ı &D4c6QSR%%(ҊmELXXMSQTDJ 4M-J̠I,hM0F)6"JmIJZS"@ %#5`2)lQmT#/A&()%҉YFiUI6d̙4lUZff6mKM5-M2jadS$djFB*M&LZH-h!`3M1lb`4HA31#/I((lR@Kɡ#2f)2Ac%4TZ#/5*ECI4l(Q&ȥ1PlMe2&FR)6T2(EBH6"X5 "J*5D3b $Ԗ)1Hd3Fa-S J!*M M%`Ĕ,0l#M#FJ!e$ ),0ɥjih#/K264ed&bIZHXCeA̠ƣfSKS[M!3HDjL@IK-,QQD‚#/&$Ȉ4 #$4P!3-EaJ!&B6Qli&I% EȶF̆L&ʓL1*1 PDTSb(2bJE*I!e2,Jiɲf&ɣ&B`B5$)YIY6J@!%(E3A2H0M*6BeȔbLE#/&60FTi#/*%k,Qe"f&DJ,5JJ256h(0IS@Hb)cIlFe3K*Mjdcal#5(Fhemv4%J2SXEbQITD%KAQV"Ɩ!e) RF*&,k&Cc)Ebd5-*c$&,32Ye#k*ERX$)dԈɦVD5 [%YYJSL+f(i5KMD%ƱX3hH3`FѶѱQ3L5E!4jQHmbY05Rhe1%h5(ږm$5!bVA,#ei6F&l3)#5I6ԲZRɶmZ4fk32IKYRSJ( C5J4Qb- 3ѵ$JME2iIFL#/%0ŠE,AieHi)e6hbĄTZ, bcl3K1`2FLlBK!IlI&h42S2T4Rma3`#5TX1S6RBlE543 3hFhɩ"aK-$L؅3R2I҈Jԙ6lei+#dfMBT)Fj[,شIlbTQfJfdѩH$FY1Қ#@Y6Jhbb&I F5BTmm1I2 &h K#5e$*50ie24´mIkhЙfHA ,#/RJQliI&k2*HI"*ƆMEc&&VJQ%4ET*Je)F`$Qdd06KdԛjIEJj&CI3dRTXiH- h#53BFV&#JKEhƲEd6%`1#hTh1E& VZSTj-V1Y-f(#(1B$4lVmhҚHe#5&ѐ66hTZI5f͐Ī1h,UMIH()DF`ƒ-dԖIeM‘QIFkTeCI$Z66Z[$Il%E$ѭFhڡK&ZeJ2bFL`lMlTȨ1$I2$jMfV?5K& |RZdI-I/E򻴖^|ưTƪ?,S^‰bObM4d7%č3m5T;%v>zyg̳',uc/eLY]Z1, tu-'e)QjoQmnfN1jqq=c,’6r6=)`S5KJ|9WY[#nei-M(@ _Q eRFƶh&O9Q 6Y6?݀45ȤU5vZQX?haR^ԛ2*2dXaZ3#,#57AOS(C (`)UAҼurKe f>Dӧ&9%CE%OUm@QE{zY]J4}]0n(iJn@rpe˒nN銃}K[lU ,D(FR-"MTFzUiz``ΙO-Li6J@`@hԨOV<ՀژD{q`CU}]l]~>ٛVp򅧆o;J0)jeN8TLhj0CfB(t\÷%v3ISʉf0 09!Κچ{z XZ%l(Oa"O– :>0@fPeJ#5WɗRcݿZ6G"_7v$#}_zF: e.W7"^*P$csWLViУ?.]u61^9d5˖ƫ)84mw {_K幣U\XQd$a됻 (hdSC"ʌ&/_W xoW{J_7*#5yWMu4"1SJBVbcXjOW#k"ȆkP?V"0KElz'< "1Ҷd< !w뼮m9u#EsHsAuBzZŎPwQ&қPTD׾XIraivdF90 L"Kv6m-82Q`"#5(@%.}UbdtA30"+it!cE0ꆀ6v,MP$Xb T}uV HCZ*SHuqf?Nyaڻ:uf/ji80jN Ho/9`>/,)B)A:+v#W*|u &7DjBQN84K&d#5*mg|͟QNo (|(s_|V'rI3mc+-IEMT}[@0Ž$‰hJZX|*&dFڐmĎȎ0Ggk;e x ǜnoqO.w􇞘7p3=FP'HOhhq{2oҿѯ%B҄:?_ͺCGʊo.WwD+:y/"]O{{X*Rpkq*(V1)"Mݴf솘PcM1AīpҤV=A=Fn.=oeH!a$hN|dm]-єL@:;֔?݌v3ܥD#PbӮZ:4@I*h=(<+4|#5B_x >4]GH |]#5b".x#/.Sθtaaؔ7lDݶ1m,8Hѓr.>y#/A; o͘HjEAhlWâ(v\v01:e9J0GIܕ[O6{0)JjU׍.agmAg@I#5Bމ`Ga-{_#/[%sO>ѻ5F31 8 971Fmz(-|4bdj~.QʤX=_oxpU5W,{#ֳ'Ti<P8):YmMbw@ @a=xեd}y+Mub#5n3M6~R-١\x@p8\6irt{PAFhSC1bR`^;4l)WZvEeCL;&#/Fҧt^yyxq.K3ᘝQI:d$Zg7B=0JBJq}>4iD%RRǒ"[CoqE1{~Y>kZ;xZoLhVR9H`Mux7cOrJD2Ǎ: uvOreDe$:3w׈X *B,J⡜^0swÓXF;n^߇\T#5pLt"3t8ǧњMH(1BP0Ajfӻ}s[{C}>7M/e-oe#5#5ּ dSR{^{}i#,$ NjGG5IO?˖WgLg]E*; z/o/bwA^?%g%zn|ö.?#}$q=^En Fcvf,#/,gg!#5~Pi::К:䲦QiW9kdUWkB#/IMvg(:0"p8H/F@:JgêUבe;tʙ3۹C~ۏzM,Tw6j)!kv|CP.QGB|r=*D2,Ѯw3J?JwHMS֥XTj,)<'i~Gu>w9w5Lf8$ZLf4.ҜLVsR`Y ALjg)œBj~oJ4}vZZU):g{u8Px󘳜le:o]<{ǽY/?35_1)4:m˿Cu (b1#/4c-;2ǣ1T]cFzfY#5FmUSZ6I+dU^S#/4O D=[q1UOֱZ~3"Yc|4oZ0]yQQF#/6#/AAz`eTT,UQPzkLCh%jVMP-R.dNdMdJ;i]\Q?8Uؖ#/ yJRN5Z+H5Z:||jnṽhq֎٘atI#&Zċ# Cm).#/6Git/iP-~Z0:~J+JmM}/sڻ^i@%CAThݰ|Ƌ[4wpUϧ#稺f\bLcBV8q^ &[>ܵk6l<9^K#/Sm/;)tVE,iw-#54&7ߺutdwi#/>Z~ә]wD|}>#uɝMgYYRTYqfб*qojf+?gmK&LC}W$3ӫ4e)ޫbՓ|Ʃڲ>u?#/$i[S߹ԉ̓|Ӡ#/Sw?{F._Y$Fl;timvCO+)~:*xd!4W'߶Q1o؄IfOz6k.&܂ws$D9812ͻs#On6؛*3ל[LhW"#1|#,TMEMqovs6]/q\<$AC}%LSM$V9l{zk;O~)tW\kרUoL|^ӑssٍeݛanʑ j@zA63W{xa$++o}Z@e]ҡ3T?مSU5an~bPI /;#5 ުR9K8ǣ9ѽ(Kh-Gs(k8Aqj wǔUsǓόUy_'~+~Xg]I#/LxBw3>gGBBq]!w]Ϝ]vgNM4aB9s5xsG YY$z@3^Gt+%y ݽ,|RT$ICY;gX$MC٢vgdK9oIV]JvsȵPRѮa3Q٬C >B0TUBEe([(hG\IIުKD+QYf_m#/t};x#Zַ>c8K|MPP和Dd!,IK惐eNMbd];>U!yzwgЪP[@?@L$NLjUPX[7ev4PiϛiyЏI'iq vy#/z8Zj wz{&|IZXDF ޏyT,c@#,`>nfkX~8~%ec< Ui۠Go _;#/[Daw "ŋ>gΤٵ]'nefqU őz='U.a6|vOsӤQɚo$!U,U}Z#W`u80HE[^ۏ`//]#/tP^(wQnV%tb(mݽpL'o 5's/@ VM^%-baݗb~c -tP#51UO,P&w̯m?buY뵮9T?M,xg ڡF΃ӮݷXGU^eku'TIAHk+~F!Ivs^Qs@M=#/fQL"SL+ܡKSiԣsOǃ:NVʹSsAPT%B'? u'.B驱Ph`1վ@vh7)wy*ޑI$5!e̍r&mCC(W&HLRiBY{Nl͡3)G54n@;7覧͑)uVfe5IDj߄!@hG4hf6DX9FF)ĻnZCDKmIyնa4A?g X< Z3޴Ic#/Hc?XXv[٠*뉣QְƟ{wN4FLdIAۃ3[BwE f찲8pVC #5L5i8.^Tiݬ40\s\ll=lpvM#/>W{l3㢏̶WTxTs+•S{EP~n^&¶P.XZ9IѮ=SF9g!8yW4`yt17,/+i'}nߔl#:>s򚧘l;B`idVF8qtyG65#/+oE{fw pp(&dqӍ|fI˿1 Ucv餑&`q|kL 5XQpj23(!;A|Y#5mJ:!ӷkMV1Ǔ&a/'kòԉ*NuDL3J?g<@dCD#5j3xXTin:2.\ iQccyH,la%Bt0?q.C`B=u"1v b*pe6o!!&[3F a6?>6L\Auͷ.ӪR|n [#5 h;2JASģ6.fJfZ%+|bcNG.g]w$93?5J7*Wkjr {o;XZwwٷ n3@\Hfŏ]Zdj!v5n>Os5_FjE[7sn(D㿁㩁۲f}o(}˥/o3M#/*\Sc"]teL(Dv5yCeKyl!8UdI 06`BB[Ce؁x\NX?~ZWxV8gE/ptv61+- Ku!+5D7KaA٦qd=3`QSObYuvL 7` _3ldZ+C|9ϻ{}2%\zmoM[ j=4q0HYa8B󺻼4;,iJH,XLֿo?Gߜ=r8Le%0(5aHRP+>{jEDEAa zV %YbT}~Z 9; < 9F*PO~+fcW%0HXf0fd<wTuH6\E0i]*ꂝD\Ll`M6WŶu=&0t"|"nLMov=E'8HSϷ!#N^ Zi[<.6P*#57ePS7bPG戎R֟2T4`@8jLLFK"f 噛#5`g-TbB®]Mt:ˮ^6-Jh?1%0")" GJ.ϯwySICQ(`a k5U4[5-Qaɋ#/XEV\ !]V"/z(~r#5+jO AK]c٫jv={[vdIj=qFRUxoͥpUswU+nn:.zӖe~<P11gM7*_'0J7(#5H0Q車W2rՓuܹh) OntI [_ٿ]A8VTP[ԓNq|tK[yVW6NᡗMHƑ @ dJ/5뱱^j-/Fd" REH4I#/ I.aϳ=u:jc5VRk/Iv޼scˁ#sDOGMF)Hcp"MUoWuuG#){Sd>?rdr6&foٲj[!J!bSB%Mk6{fp#/2ѲgZwʏ/80n\9P$=Xu,[&i /RdOʪ)(d.Z(QET(0QϵL:+wڌgw9!JiKۥsfN~\n6$<>).X! &}b 2 zl vLTd$yD}^gNьRuƖ8Tݮq/}/)$^sfl*rʴ̋U,^;\Mt0se#5k,6EUPq'=q#^D>lF)NBxSqF* Y$/#/2& ܲda=ݭ,\&f*Zj!;19TuCBs U.$ڌr\H͹-inDSU<*.;`X&cf֡ ndLNk8HQr>zehS30gc`L#~m!##ǁ/FTo]ltQ~Pbi걭 #/QmaՁ-;ݾҌ[C%߁Ijr#o"'R PxW+ۓi"#]1BF]ȌĽ4=9xOsz_;DU}>*6mc>N+~>ų3f_:ʹؽgDK;#5 EQx4{hyOfEʼnb|_'AC9<9ztFBӖP!׭Lk;w鷕#5iYŽ5VGW3a14 QcziIP%4Qhը%BF*hҪB-IWNX0; ]sW{ws"[A!!Bb3vL%LHfʖ%If&ңkvlQY\vq0Ι &1 =Ű^[-?MnzMve4.Z)M!t}^Mzߣϻ1DF"wh#52ObS&H'{_LxAN+Q dD>{]Lv0Q0$3~rBEXڌxgbdHudY#5gEkK^]JIm\%%Q.?SV%l"eHh#5[*]11CHOџ0xjg/$(o%_#5U0}q_^?3: [46?SL[13N?$y%1@zq m"z_ ׸n"=xM *3 IU+N_F,#I@*".N#5èPCdQ.q.CBk݅gbkdE=l|DU)I0 J#·MjjfuW/$㇮C?>Tb-߻񧮿ڸsǞ>8[%hy*M"MyJNm>ֿ*Ei|Xgav1t=H:oR<5{-G"j Q&j(*#,Jo|ezmf~?þ)頬M''V7q,RveSw28/Vd$/@T\cL3cv-EأL>4#,%3#5O=Q[MW9et=c_[*[شE͜ϥzd aB#5L445>KW*Yz=RL?ܲ\*wy~|ά܏N& ^ ?.T8#/Z5{ąco0Ns :9t "uO|U&;l}I~ޚG-Dǯ, ooc|HxGDy!&B1d8W_?uٓf!*O@|+N/?ugYny^9̜S:gwiDIM~4 LFڟ{!V4 A2IbBpVl#Y.tcHLJ~ z0:* #5|KTR#, TDDEW LoϗMea%'_\Zn2Sh2P, A1 Ҍ]oFi31-t1zԿͳff{{>th|tܻXgKzuGqs#/xT{s 3Gƚ/P?^tiʼrgQuBU~TrO~Cȷx8OM wkrWD_)u^xͧwD͟<ӻzs˟=m5;z4\5^-m7~}7VGifƜףɻ#/+%B{bq9N+Ž:ނ,9?fg=|Ż#/JGFju~~=vl?0zJOtVyl4S\,ZIp=%>^.S]ZUU:k;9]1Ǝ|vnjgmJyݗIͮ\2+ׯ#5U<8Ig朗ѽSIR$Mx~O6{!׿?y- +d_v:'=ʢAPe ʝ^luo>änJbۣ̚ڏ՟g՛B\z*zƿW51/U[dXhݹgW=9µYg^Y}O]/n{kv+Mrd܄ZK\68j˝qpGtKװ3fٶaȗ|~lmٺOywXo?[|MB-^]>&>D(hwsZIP؝FFU!d9SqCH"L$⟐_ *l>@EZ25fGi ߻_~z#9spXo뭼;ϏofolWECT|_~˳s+\~_x~;t~Ia&^7ͫ/=f8|EW.vծ|'ӏi̝S|Rzۻlرu+ZtPgJP9[V1qB T/;WR؞/ݩtT벙^ǮeJKNWfTSsv'k)s Q)cy#EI'!i!Z9/?#/^k#5 >_;*kM;:'Sf<)>8r߄1dsh!r˦~rl}5սwTb(c9HJYW=K`Բy5|Vr]:vwG/#t5/#/pp#IȃU^^J.Ew˻/$Wa\qZ̹%`Ϟ*;GA֏utaV$CcE(8#5*"jDF 6Ξ[k[ `Ҏ LXVaHhYSIU X0#@Ӷk,06Y8h $mE=MX޳!uX~1&~Zj]'hkQXغ913S%R(ȝ t{t/9h#08$øJ:QFk1}KIPc 0i`QV M@4 D A<wofIxS-gol,)~tgҷ1Q/4Wh?9YlZ%C̷a_#/z~ӮDՋu~-2]%<{nw;mx'=i1i5B}#5>fŮ>_^ZiBB D8%kw?Afowqע}Y?iI)B1x^@?O%>?㪥4N=/gu~>w祑kݸ5z(}spG|?~Ao{8VHm0acb!h22!In0KJإ%IH@H2)#,iuF$ZLڛ&ɮ^ĎS)jr7h(UX`ȣH*V&efeX6<+-iNF<+ړ{̈́f#/IEANivyN=QR29DInB pcK J )"%桶2)@lƃ2#/e&:E'(<)LRۊX-7օ̤J#5%F#50 (lR>7MiƗs6B⢷f /ZUV jPX`- ed?@Q'Ѹ.j.DGqrhs#/1>=j0}'tP0sM8#/$Ҿ0,.e/~hs7Q8m5_:{dltTmg"vSRi vIn7]#/CThb0IDZ^1fe!i?#8^[50!5;<(.#v;)|o2{\nXƖTM ڭEU6?SQױfhTU[wBۤ/BBW&/^a6yMRFjnH$ohq#/<ڔ%赵&r;_56UȇV1'}}CnL頀 Dڤldq!b?ӗM608;"·L!O7ztwO_DɋcꀠTH(Qk(VML ]Ƃ^1a[Tu] r2Ifq|(x8V3]*0be#5MxX0g⭖g1@aٲ=h;_FC4Fݣ*%i'eCK9ntlV Ƒ:iC9*i3b4PK5j&JQ@z+F ,#Q6k`bJ.^ )ivr6Qk0wc+z;EAhk Z4!5M)4ܦ &?}ʪ;CS^_kw3^cr*>V)mItҁHlu~&|f7TΘ77lӀDc#5DxS &21 1l'bF֓qB2,Ter9qUTМjdą9SL̅A Za|C;Ү-jތe]J1@l*Cr[9P@ tДC0]#áfӳAqCI*V&sb-^,\y`tZiAȤ GbvHƈ鐔ORkYkӿwTGgǫ{1J6O)Wj63srZ0ɩ4NX#I(9NlGqVdcLprvaGQ#5&Ӌ0ԄL#f#/j̐ ;d3,X nbIQp.6Aal#f*U2es]כW]9wP$$`ݚbVMw+^" 7+]](҆R mdGդtD(4x8)#5 Ȓ45lˌH#/ao|!BiNb!8#/oDJ[ˀ%U;3a/8k fA#/;~doa]78:ɰ`BLvP|lkx ГZͩu4f}]6ɲ,&M+wn#/ 8,F r7QJ--vmktQ6v5#5',jv+z΃+GcvsҴXqW/!X:X1qfn\Hk ƻ\ٜLOpsn\..LҪZ%'frfODHa;#/bL^v\#/7->6WoS-~oFqR2>De^F/K }`sD!!&|=mw%O~k esQ?"0J+Uny\(qUF̳Sls]$:dsu%̧_#5sL3ϋ{7f|2SD錙49i[>&02@rCwf˙YmsDz;A:vY1b]2CDv!Roqb|)ipg'ZlGL#/{Oqg=oX[å@cCY2Kdu}ވk;95[\A|+/wb$-2 ex2f*)ї-q2\3bBb`Eof/\d#/$] PqA;H;3i3ϵ@)DۜsLTHJ&=S^;y3ԞK#/a#J('Zen Zoܲ[^o(5do]h=OhG&{#5dZgHz뺁CZ$VfS%{i n˧(0G˫)'|Yxhv!Ƽ1׌sF[Huh劥xZ_)DijL< yc̯EOON4Z&NFCe5$?S?D3ʎN6v.:!H<]27|j$Ӂ0T$=̓,agG>Dމ@'.'_%暷 Ugn=-Mro^bZkI&t:p2-{n}n10cb(_Fձ+V-[}ʲP̍ Gv3^1'b/Mфnev;J)lʗqµwv]έjf;ÿQ_'.=Ӌ+~ݑjaߋgXXl#5ya(\Cvw3qf2N"^JH!և˂S'* {\q#/3ӆ_gP]<_篵Y:p"&-P&5*ZLʙᐙET4 ˩:GX_sB;2וZ~gqwFGF"[1jjuI&VڛzClg* ] ]jPP-~#/Sa[f<ҋr9bvzg\|Q h!^Mmc۸.Hn%V3ONc3i{=լ{|6RZoc.i.(aU%E;·aQ&-FL4aW*$MV !ɏI9GתbDd:ޤ-WjjW^6U;KWUvQ.*J#,ܑ$s\8' {ǧ̿=s./8<W=Z8l`;1)qZzΨ{Q‘N'{#,߮Fěnh;m1M#ˎq(VU qC%I<͙4sH51$1Ȑ'lLq-wqVa4&n2/Go\\t%*2L$m,<#5S2[s} AR&9rD,(K賨!LFFvP!fyni&aF,=x)Re޹=5"^#?#eO-eQ~,x-L lgWHח̣}܈Inh{aЌؚTyV驵0avD^f~MQNAWG~ݎQ3])#/l\|H.f|`khd7}ggXVX%AEV } /AFsY~aG6q;J*89Bʏ7oz<}*ɴQmqI88ZTD[CNVFܳk%E^V4[w \3:jRk)rP௼m9e7ƫKpWW>]4v2YsuJ*rO$pzoƼv'fʔ#HUi_}HDƚc1I=RYSeY8=XlY$]|txJeĴN 6h_Tvt޿/>J1"U5(n2*KCz׍_[bQA]#fqiׇUyNOS瞝q 7u'#5y$ rd53v{-q14Ltpo6Yu"2WΪ9VBc[#5U;elaW[Sk\Vd.an5ĝ$5jwǺ&6Kxkk7qӶd"`|HE3=k}ZǾ3NX^X\$ΞHi%F裣|3bg\#uNI{w>Y7Nnu6L55ʅvY nGF9Ն).<9ZYyByjrOpe{#鰞b\A<`STOJYL.gBVAeUbU2no]`D{TwjuD4<梺c.甴}O>kJb~n9FI럿O#YB*4/92W^my$L{7D.ɢ98}Ǘ#:HJka#//w]3mWQUpͦdE^sz0E-+aMk!J7YO+O++^A & S؞Q#/ԝ2G#5BAhwl*Nn-z!>+5S]S5Ot#5Tt>ϺSPׅz>74"x0ݍ zЍXn9~]Vro.iG\\\$yȼAw2 T܋ NW4fFٿWJﺙafZF z$?CYײew$ |<._a¹XGFdx!+x#5B:&adΓ Rª hR#5DMhDc7,O=P1e0|eɇUlޞ"^UPԛzn=c&˗M%;>^X4}sIڹG{"Bo.ZcJš&Bd[ }ά!#5%PVoWA-\c#,1D^뢊nyz_Nn/vtw~rk$cyco`]]~.Ŗ2Ooģ529#/K%yܟZ<ⒾeB;sni[%uՑuDz D]S1 +Fa  #1騃4u2#/w@kS.sϞwWCtp/Yk͗xGb;;.;EǴwΡ;a'/[ʩ ܷBvǑ(c|7㟨]b։CdYTc<7WӋUw=Z0knDqqZ7PlRjsTտI,e 51fnM<YdIu[\gWnV$Q>եTbL/b0׈֟O]MznKҿӪܼ yWAF.#5{/~ mqoG:7R<:ߣ2cAu^N!Cڊ\Ā C E|HY9(Kʿ%#/'F/;̝j>`_dG2/^3=͎ڮtìl5JP?HK3O2Y*ݻLl72R o(Lb5~CNw{E.H^:;tBz<&TcK#/vGPfef|zm$爄r{pr>ISyYCd5)cы{R\٭3Dɾd7F?H\Z H\ u?5Ud!z!ҘIhN#/$ۘtaql1Vd>C0JDm"+&-X(f#%!D 1&H+PiQc7`; sJmb U֭U"@1,>*qp۹'jWZR4?XlFHN/FT ylW#/2_Db0=KE*on~3}S*Br{o=tͶa#/J*#4W0SFq߇J6HGmօǕ=#ŀV{y`w_(tbGrX|Ulo,;gxaD,#5Jύ>GwG#$Ѓ-#,x1w@P[wpIM=SRMݡA~q18D7CVKmռN̓a*RhlWfQF=ڦ9.w:3t)IgR FݍpцVLMva5# ?3TւqBR7t)mG(eA0 ygDߔ_Y*k~+Q_*KNNJ=L 4qRhKnf=`{NV5tr~]O#@tFCP 4 &"Ӣ{tHwaP7v[J/y]TM8L -1>hfkbRMdRsӚmex:#/^,󄑢A{%dyЪ] WY0kNRF9WdoaK|X" 2CɔPQM ~㙴a#5bI#5Wcj9e[ncVtSr72A!t~T%IiLY-ٳttiq\;oNN/+#/zTR8 )#/#/_#/#/z1/ggsԃ( l%supIY GA6x=W#" ;MN]ߑ;|K4!/@CCWW|3bі뭈71|16c.<5o OCIf;#)a,~~X3qvXgj3+՜;s1sOu'GNBˬG?šQ,]XLI,uH|i8J(M}wA/Wl#/s3GJ+M %L\h婍LU2klg&jiXLPiVq!o8s +#/ZלLVN|fñ}/7MA)(c5iEIc#5m99ʉxm4l4jA8MA#5hӷҷp&V &E&u<"Jl =>M|4"RszH?WXR.O.Z MP0Ja"6._o/<1*I aʪTz36;MX}c! D&akV~tӬ 0|e\gn$#5q:3c|I&D?frd9 BMtܿu-}8A^ZO `xCQV-@F); Hb{cو_c9E#,ZI @EL0*"ï.%%R zG1iEJID*Ζͭ3<6f",`R`Su&gX@'q(&52I|]c/c3|!ٹ<"fD]q?l6%[#/^!j 9qL=M(apRjMTZXxe1cHI~({^z-#5?GJ(9ވ?rgCi>.s`?Q;#λcrwyo iV,;mGJl)],x3A##/%i!aHlu#58 sV;8P7(hWPa)/zU#/;he#5O3š 5!#FoտeC[:12 p:D>F͇(wQga(0š{z+BC0QEjun#/,YѠ$d^O39:iHPYET, q<35wmG&#/3pV` I$#5**P!*Y_AD}#/B<XԔ "B(T{#/y#/Pyg0[F p8mymp΢H]! ?9r':"MP:1H70Nl`;(Άq̣PKD}|U&>ul*4T۬%qk^80 <'OH<,u#)+#ԁH \^*pEN>>#L(1#(.UԸO{ɹ5DV% R] (׬^YO sУS7ś`CwȢ*ᩈwńNqV4Ϗ=9P a=JJC#,AH=a ȼHН}=QՂ)tEkag=L+ M)DN$[$!!&A #xK",Mσ3rr4&z8J VɛXX#[`Z;#5dtSO+ks!u~HWZu' e*:*#5Cr 2'oh(:ϲ`Lw@,7kW*/-SKUBZ#5SB6$W(M%C8/:좐fgR<#/1ThzʮelUdߜBDH$ mTK=hA0i*zM[ufr7 63PA9 N-X󹌊t;TP1m2RH(XxM(!2J97>SXc$T#r8"pyK}hG4!'))dt-vE؅[rCq[aҸ2.ˁ8#/(6 mJUKfQL:kȳ>~N\&Ϳ2BV~c7n#/ɌyC4 FDA'iT68|#52"),(5#)Pp 9QHmt,\tvvG.K} vCEΎ1d4>?P=M!5HS|z~x!ˣ47F'q0t,#[A1,Xwl#5{؋KK9lwܷ#,!I'D:u!zs,ykl;@#5LqZE ̬#/u;)Cnuf5W~"&9)#A-3aF@J2}yB;Zv`Yԃmţֺ,%)1D5$:0lTLؚ57q=%$7#,zۘ4zmSS߆t;J,uBuR!"TIȃ5~#y5$!t$cClzEu]u&fr^j`KJ E#,6z~ M: A2ANvl1vkB>܀سp?tL“712>9X+s:ŀx~ϘdcD>Xv~Beo{lP3\yEzC~UdM"aiK(S&Rif챇mOt<#5VZ:\XJ%o|~&b A+: @#50Y3zta/da1A_?!!a/ t]a&ex@=IXtQBׂeuүʥ>bmEoOwYmuw'.pQ̆!3ss#u1ygW[#saI $(HB?%6 LOya1ZDoHj/a|bB/P2w3ur æ;qHqKD[8VޱY~G#52y_~eu]fWx /}?30_}R6-bjy;#/`o~Vև(uDY_s j&8c_yfeG4ʊ$zj1rVaDkZWG-g2mjg}ag+%޳6aw1rNNs5311P`Kty!woő`N1/(Kk*>$>@~1[+9RqX̰r#,&sCR"&H<Lj㷯{(ػ.qAc?.hD d(|m7#/@OmLoO`^.޲]p0ttWG)xty(kawu+u-j߮(:l ^uyɢ`Y-Amz5yT禇˫ѥ"ߗ '9i#5|f"Ώ޸] 1z1[ĺ|ZӒ"kmYlk=x|DYe-bBfGC\".}yfn޽5Ϝvc1#, Eb"P>!#޳9NgLOp޶f` 'e3h 愀)9⬣u49s"➺_Mُ75L."H{upUg:z8]}ߚ2۝!@3'#5|#/y;bs*/ΠCxo.^Nʔ:2t7OĆ!8Kےri(ۈMgUb6vLtOt*ddtjC~yRX~Lw$j«JϜf.(|v5|cw1Y%;;v{]S0{#5O~aSFҺ}#5 SSa:; b4͑M0v8Lu_zvZ9J =T*;Rk ᖦR^4p#5~zHڞ$Op|-a{#aPt΢j+V3ZmY·EېHD۴BdBi]k%K~fQx & ۴P18G $jŋ%{jYBD^i/80(OD,|-$ʽ.kstӑM@/( 4(@vχ$葠9#ƍ_#5tM4aG.q"I";Z~˰#5]5/CKKSdixj!q mAV-#b09PUphnkYB.'Aly! wmA/ҳ4޿Yw611>o7?\/M<#5g"ѠMSfZtY>"Y:u7R#9D/;+:L20;.RU]>McNnI#/O峿,M61}^'[X* J|wsiH޴V<>O+7NEȄ*jI@JH8;׬su 0b.TStǪJ+3Ndl%fg kDI!{H_wۆ϶>e(nv"Vm@{y OAׄy \ޏv,TN-e{ed:*gsC;'J~lHg ͮEW7u+luSfMYL`fe#/k8 uq凛9,x$YGCMp@pkI_luPaw=Hw>ɩ;qx,CʍXL!T2jve.l: esj52"X~O{{ay}ۣѷnVKJ3lʺ5NҖ1,i{lӅڳ ucNi0tl_6Κ#{Ag<{]^sS)Zijތ@]ۻ}ٿ<0U&7ߵ7Ȅu0$/ *gsᚾ}+ 97uE4J ﭱeO"&ܵ*NaĚcU:bxx*Gsf&#/f2J&fEVfE*8R|ZV2r8MͫKp`}7'˵UQy9ͦQ#/(8zXiKe(UW0~y^(:zKkǒ dVjNJ~nNE^P4Ǝ#5BGeS i)lO>hfjjf )t~3ߘ BS/jn+o%CK,kUE.ЇHVD/$ۻD|v>} 1(ǝqEٰe% u2i,Ѹ.'gb9YV#5k6#5*t0#/2RJ~G`jM]beqKAJ`bi8!#/w=+-W8#,pCۈ\w_c]gѿ_tyC9YHfQ5(s68e6;iDv7:Lӊ:CÎ}y!!T}E#/nE Q&Ga #/=4!Kki7s;cQ(>=9\eV,,Y9i@{}6_w0RDXy0h~=O6{k1|=9Xƶd{<ȽRi##5;7PR*&vlVO^55q&Y_f_#5mڕ+ FoӜpيvcgUrR~y"ij!4f䋝dKm@(wa=Ǔ&Q\'9݂-ߏGlM,]_y?dCW6#/nP<W6쭲)9.9>sl:颅\`gvՇ9>Y0k|`6U%b[ya>,q+Ci1by3?T..>)xquzIo`5qY"c0c5-]lj1{bٚccGN=wFz^`#59HmjTDN#~<&}n. Eg: %{½(a#/8knXŖS4AT(qݞ2^M}Pm͵LtU#3mk$䂄ƿǡvȺ_=77n A FQWbr%)pr17 X۟~:S~:kQ˴88̄q0k13%u0#Pè!(##nj럁f](h0rt^DYU{U jz e~0^+cxXuFOUYbpw *wIsϠZ.7A&ATi$tny[_qsKk*9&`e>U\7&GI8~_MY+YcQTkC+g.U՘.45םgS{w3߶ nԷU@\l#5Z,,ndqrK[߈j[%&LPI~ba-#׶,Ps0e^8;='=a ӣM9YlV= ;J͸jV}g~f;P>w맶ZGVOA@%[?GxQD>FL+ЙDBq8;:#/y>1}~G6SA`''jYA"aKQ=eks=Z>'2ٟY*v}7W=;cU%b.qJX\@"j.6IN$6$RgRDuOA<ޤ?GbALx*UVa|OR<G7kn#/$5FSq#P-"#/ )"bNc{\`pRK$a-":\Nk"G:,dLXTJ#/p/hQh:H+P#/ #5ݙyտȓD@ޒx͉.tJQBa2Ddݠx#5V}xT1+\kgDƋFhU<C{JXqտ: (8<&'[_}fD8RH?]; F#y4IO}r$Ւ9J>pm9P{T)LHqDBD"S.ŹSHB3B= 8?#/IJ7ysRT4S4RƳrlm4UQ֫lm ϟgzag?q88?qGzNs,Xiu9r4 w`],͈C-~F*^=C$%Rff{6nkKIk}/eʆ(&?)#,Z`TS3|sVL[Wwt$N ҵ~aA|!K4i#,,*!5@Dهgp]8ԂXd#eJӥaE"E|4:#/_Qqi,BT<@#,6?i`>|v#aGdC1 >J 46}"*֦rNP Kb]; #,GD6d6Qbf(h=4xė9F)R 02!IY"{Owy_#,Ț># Fm~n5UB"{i{Ty7, ?_9Xf&f4eSu0b. Xr!_7G=/#,tal `6#$3<mFX =.Y%z#5nU[u<hqkrI F5+||ǷHIT|U3z5#zZ&`\SXD"x NģuEiVeCMfD!cGX@8l|߯,2"H~xِ,"s0F3ͮX>r6WUtvμwCŢ$G$I#`~ xa>Dyq#,u(KC!%px_GK7?h(w|4fȂdBދX}AɃ8)ID8nHtHU]O<'Wp))BCx6,M^#yk#OF#/Sf%;S-QSנg#,qOHPF!d㲀~0#//\n566ןf@x$X^X)gH#,QTCP>C?3OZ}@pH9#+9 ~~pm> ^F4w_lr  $({];7paެ{E;#Dc;x$dl6Aç]qwAр}g"D<#,;4| p-9сP$!!|4ȳ֧Kpx('B&Ex'7AjXGt" :dCp#5 )t_dc+ }eV=(~t3yxh *(k-A[06bz$y\U=@f{8-@qͲ4dGH^}3DIVWǻ?> ۦrܫk|[nz$F$A‰`(20*u4Qf#5B߅44ʽ~TN;:)1Jf &aQ$&$0buq#&nO5NǨǠm}i-#,ďOg{C1 xX#/_Ϭ<*B2 v;DjvKg''ÝX0>+ۿAS#,6nrt`9+R>SAv%V^}ML7 i3ҷ?)*绶'mYC^cCh:iH#/3D"G[#/B31tF®(wfCOj6ryf-`"÷خ[#,9R|;,p\q6{˼ *{cx۰}F`0")*F >#,d1EɊne)}a2}̄9hhX\Wl1CWlhYy홤wNA$D"DQ D]~b!.̅Qg}Mw u5@bIrI2qDf̦BLHzѽ`.~P+gP}BQp="#5 P~ X2b`hXPR@D0!#,bE&Idzj4jZ`RoaC5A#U+)'Qҡ@ m&S3oy[;skZفB#bA#eSmw^xˮ[Q{{>!9諁$:`xZ]kupA>@%|( B#,U4ð)^ 8lBB)#56 %1:hfȁ|⥅o =ve^EyѢA 7'^Sq2m80-lBɣ4{^w[F#,fm= zwwXʕ!^$;g"ظn骔UQ|7+?nC .Ij^THn}`cboZߜ16}ثKL #,$ &Oq0Y J<$ 2d#T@s}h"r#GSBPJfXu2rrٱH&*7 "?P>'п#;OZp!b÷㛁$! #Ux37^7Sɰ%by bȱ;PFKdZ*v$exzOgG#5M .JGD|̢RN%ي+.ĩ%#/ `i1;&(rE2# q8D\k\qxe~`9hTȉ\#y0nxhoHAL#,T7#/ l-#,@c?R(C.L-zt fǨu׾M" 겦#5.DՏ X9r:P׈{;JEX`&@Yo񳛹4^m@1K I2mN/2Eh0(^I},#/P $4) zj&D);&$M/9ѣD&$Zf12:6A{Wn88JnG`Tv)$+W <L!{+>U#WtŊ2-#5KW iLY_ٲ#l #ݟŵqnsTD?aȞԭ[i#/ڡ+Хff,x s&ЂAxno++'corX9oڗO>_õD~G!W*z&*|O[|vKyyZo.q26 j A ,L~AF hinݴKbDɌJ1V4J#4(P+mu6IX^F *4S g#/܄+xpKf=8 $Fh B(f~}BcY$&1t4AebʍZpqy :0G o~HwtGR*ts k*VLv ;/7Ly{ȃHDS.W?=;~c~/V@;Z߃lFQkgxګy/3bl~Ve!/f= rj-"N Wڐ2t3%'3@@D' Et@?c.ӿuQ챗#/7dr*!9Nc͏qF;O7BbqaH̀>>tɧK71n젤WP0-PBB$R$R#5"Fd(;wI2dxDrdgq!w 3g׶[)6hJYDlDt#>j)P =n7*ہA'PI%l$d 4h@+!vksZ~S!MONZ3h>"#,R]"AHZig#/y!-p=3UE#qVyO;#,&U^#,V#/ A/Ei$ж4H,-+[ VFl/F%D @jT#_US$hd8eb퍯\%:LPKw~=oRJ]fY0nEl{8lZ:{V*_8T50O~>_.ќx`KtymG"k#/&j-ҥYa]XR*,$3QGeR;tḍ]>mtNxPI_:wMg9y9+7=i<ь1!υ#51E㓶NZwmAߝar;<邺2 !(O~gƓX;0!sG;)`ۧw\#/s%yOnpȇmdE,0;% c-{U3G꫃~s|m΋fO5qow\;>Ð1 kc}_XL:nWWwgL:Vϛk{ީq:n={r7 \;=T ̈P1󰡼؈Q#,("0cՒ^7R@̄J PwASkw^ElUN٠BxFsd4(V*r.g]zBeY.Oqzydh'̬>Si(ab`&@@ rQyW:Ф5ܒe2W&.֊cfJ3,x~? <4#YXaET0PJA 1QL{8#,p}郛!^^~c^t'E{(,$!amaTbj;[!tvK.}ڿ[w}ƭΓjCY]4߼]MC7m;Sxl³d Fn`((YM" ]VӌA&LJ/ޕu[5Re,oo-]cU!%%TN!!Qn" z`m̦6~I| 0‰! +(yr.zy KKcDw}Q&;,CbٺE \(B,B&ðvȆIg1G&6)QI'72YĎ{o믉|]T-4d_. 01˳vyhzt,@zl qLB3v۠ztjZEYߕ/5Nzᎍ.ԣ)mk{Ui;QiL<4h6qcWPr̜"#Ӑm!wh^Ǝ\a[YNZ)lx4S ^ds>!4b]n&CF2g47(e׼4,J'j=f)c M8M|9ewCF $dIk6l~zɫR{!6R`jӼ'D!22$.3HQ^5#yq3ө)kyz,|cFwu  ;#/|4 >8Hkݡ9\x:GՔj+ܗ7G"&(pzc&ǡ q9#s os2,m#/haX}ԃ9oVqwx4z/s9L6mt^J$9^s b;xb\eRwX-H/Sd @3çif׃>Ooqc-,#/M,~QÉ 6ԐiN<Ҧ2 1ٴ׽d$ nyJD{Z$` L}\]/b`ȉ(ܤ&QD^"@g0Ghj{0e! \(Bg@= IMmu±)HHv@xd>dj;ɣ`DCɡK-!fBAG1iLi:6/b-j~`33)JcXCt3(Hb0#,] ;x8!=f#5E!SNٍ9}^1#5 !`v#/s 3n%%={#:1@9s( SCgU@,lKd\SU3X712Vpne.S`a2=͂!{dk2Fz9.ХaO%PȷL#5d@gHzNcpd#:7p)%l9{^Ž23^2l#/Fq[*.IeRPY.s>Şcm]ꮪ]yv$:W.DES1;|8)5sZNcʹ>۠Cndm#5U$53:glf-r%a=MXȵF%F#, q.&gE;-(j;2a!ljofrBgH"-3$(z}&[iu&yŋ]D >͌:?A!=0|xJ""VE ږ,KINw]#/f^z(>%.X`**,F"Y*( BQ^(h\!:.m6#9 ãǾ0?Ū? I5}* }zvgk)4ڷճdz=JHl=-)#,iϠ&jRQd) M"HAHNQlƀ9J\iFFMlZ)r3O:9q۹x .^<,~.T4=('k{r5{Ue\saR| " % A& s=$O}4#,TfD@7) @;S(hBDwsҥIäyk1EAPpV Eʀ?A&F71FkpLhc M`|{LȪM~eh:BH #,=fG@ 2cSuQhbXXP32[ jFO#,kV[tGɀ+@`@ $E.(A0 $'!:aDD$E"DW.)$[u[O.';tUթIlNXk#5iB LBɑ6SnW,jw^K{"oK)%4[ݼz\175K˻5k.Ά74b>tk&OR*x hD޿32FRbkͫm`\ q3K`Z6YEEb?)'Ov+EcBu‰QCț0JEj+jNIA,У3Z$`8r$RU"qQv Dd"F,Ft#۳UHA1&OӃ_J 'e#T0ɮ\;ҒhEd$rHJntBt)%A~,D Ǯiu.0Z]]mr852/6݃ȓ8A4Ra#5\K!1#/#/8 U^!%W/N/us@T{0徨(oɻnj좍AѳL˗e!ə}g(!EBRuvqܜNrA5OM&t#,(3}D~@ BT"j̢,'IחQm 94RsfnKc@wxum>ga,eTJ$hdTwi #/mFEJ$*Z̅*-Նʊ͚*؂N ' mʓ8l|a2 Ȩ(V#,@[YLopBT`3#/ U$̌i(-;#,% X8e:b=sz#/|U_ GFLM67L(sCIFEh 6ZEWZ92銯ڶD_n@V@۴Sc :hg=9ҭAm*]%]%#,ܿ;sD*;6Oۼ<6ޯd͚̪*JekrY+JfœEƵt-q+*+8(n#, @A]}z>v[it .7|^ z*,Ă$N"}v>:ٻW6ځ9!u.$@ 2[p,)Й@e*?K#5)Q`+T:n;N9Xh)Q\X/$&IeTi'T ?:x1#/o / (gT{hbn#/bDgdAcDHӹ2l`zݞaW\0ڂDvvudC!q Y:@iŢ6AU#/o{d䀶x$ U 6$cLǬzsE#5PǛ36pBAtʼnaDp@oO " $(⒢T;5<Ҟ1gN\ހ܄#ŏ~RBֈ)eb]VAHz$ #5DHx2#,B1X#,d==vߟsMjX3 vN#5QHFO~ms`j#/6#}@d#,  `|dSdo(ߞ$P8"[PO|RMamC. sgBL̒eZmr#bҤGHJ8,Gt>4g#xVcmֿ[DDL.z `tx~{Ss{.7;g֜<ԲZF[Q0I~1^zT<-`OdΙX۲X-Sdc(: MrwGuVlZn uc:K=VuAڨFjJ5C!+B1O;#5<>?f×˻щ\^6&3Xi[}^i8 66 `ڎ̄G;8{-75 hZ]zэ修Z[[',͵&ff@ J0bc詪&h 66mM0xC"o)b/rޙ/f_#,`#3Fk k FfiɓX,K/@lgp½v79isj.uCqѤFa" L(8A(2p5S(SMUJ,b1d;v%D%&~ԄȢ)q=DZ CDAF`I=h}j#/PwL!!sDNj1;-9#,rFT͜ CG糐xM}`z| jFB*)ZP|*o/FGHqB:lN0$=HDg3vkRwL qd[ 2!}c+v@VMAF6(,O#/ũ)dR%EԳDuKk^/xLj9bԲ! M;K SIyy %l+[ھhjFRȫQ5S b4Z9^PmU{.\^oߛt2D.E#U #E0AuQ- YJCGB!#/>#/'Qtp#,#,r#ɍC C7;i=h%[,9pQ9lGJejĐ3&޵@i߱M +e~+I%KL*dž|v[H'$'ӁzSbI"jn]h;ݭo#CT7!Q4#,#/$&HYJnBGA8Xn%(.db$P(a }h62`4LT5Bކ٣I <}O*-2<[Yi"mmĄV#,^a_ #/\vHPK#,I:RD#C.#X7`q/Ȣj֙" wbwXD#v;ᶖCdXS 449DGC4zwo5mx30ᤆZt6]Od34Q/|r2oD,Hu#/}g#cZ1C%ґ5)$6߀n!4ZʻI e@̝\\6NF¶n$)pjJ~ճr6~~3С׎hPsÅ jʌ4G9(!EUC377nA /N6A Q]\| B)L\aa{ylj -" ꄄTe,9[3CH&%V YAU*#,EV$:ĒA̻F$*$,#2\c 3֓=<ͨl3PA50t,-(jىI0̋#,o)MD2 lF#55`RWG'V/yeoˋ$;T2.; P O,HȅEDk-6PPrQ'HŴ^S܂}̶IMKue%#/OwQ9o̒$J@9:qi#,,F($/d~F6#poK%EKF T+ntN"s-qE(Y? B+!)Dl>W}&ϙk2/;I3z߅jwHeSRp0{&q"TI[\Q bD BNKQPa0NmjpvTs#n#/~s_{`sp#,*/Z2)Mzw0doO*TW48:%(Xr5gY3Bnc1o|p#/7[sQ!A9.%Zo-wJ,i#5#,U@W@t`?)@M5B2K '2/ZK@K:E ȍ #54 ITpǝh8ٜO&WM8Z\a,a#/ROKl=a<VN-ſI+p5AI$ Fh>k;5y2z/m5Qvbʢ'G0 c#, ϗ;4 A6e0!4ǥ.ƃ&Ǝ%"n3MI#5iI#/ #5 E4c=[&h&AYb  @#5 Z}d @쑿ih`^6wnɧU>0G{`H@ GYn#,5ӫMc=&1ƀwNGG"a樌^oq)8?L`OAM gdw!B*B"svYƉl6-oȹunIKmj6Ԅ`EUE$A #5i#,zȑ|:iC$[X7zЀm TuVzqHD!2@H%2RkHS$̖blZL2d6T!#0)6IJ̑ۦ&Mi4hRYS)#0(#/ID*hR$IIbb QPZ4M(EiKQ( Fe1!QMgz;8,09.ۜX&A~u=Jُf̈x=;p%S*F9.e :y~)W/te#/D!$t2X-q1zMF߅7y9,碓칷[BJG6k˥gd$|>/}!a h\#y,P!p ^F#, @9/rcP,SiZMa4L$$,qpx}miTx0QʙL1?bχ F=D؄}DM ! Zť=c;Ե}"DcqC‘[oZ}>rd/F t* J^faH1ՋZ 1d~杁{?Wݭ۵-ѵcH%#V i8$i m M(#%j7N.""3]{DưAarPOezAiB|J{L fev^`G$YK#5<@؏#@e?o-t;͔@kb<!C $ba#/}CogZIvƤP`M2!F Ӆ-̭;Z#Xe$|2}?VlfIԆ)ZQWo a31Mf.2^By51-,.KWZ1fޞ1`9۩LKКB$mJ 6tAIΉ>wwws!YL\.i T0R0JtɞOK7?z~#˭;"F`dά#5C0jv cTX~ܷ1Rogr3% LB 2>t#/<;;, 7xdC@p5Uƈڜy(a\rmzW2ZBA Wo0q52vس/uyh ;v[$33u,fwcbRd,vrh L_;l\;Nq#,X(c(He5-1”qDW;4Xa&(~]OuێLsx)ns/x.>Y㦫kL:Cؑ26I7= c-lvۅ%ޓ*oNdΦD@ߙ9;6.n%G=a읿n ˇbs'\R]j)XqMC#弗q.gx70ln6i¸q-n#ll#/j&(]&M7K}KJRrAa`"m86vcpLc+э\Zsl\#/eS!(BfkKZエk**)jݔJfGVXnC#B(Bۂ i#5'Ks \ ϕ1GQmPrIYM1C8CG2>TP.e[FPaN+img`r5U/V@Cd&^,I53S Qv9 Ta%3 8][PJc$d"mKJPTÔ3 31zCmΕ`LowB\G,A(Y+;tcI7rX<5iaNF:tt4 6XKe:G #5&$Y< kCEe9ziDSQHn!s(aMd&U2LTB@O6fRTQv(bȰ2P40dzݘphU-`J8T-!jYlY)7P10`aVe6IO$a`J^AX6ҌM +dLRٲ[,Ա2evgrxVE0`QhcgYfΔ:]֩ZRz LiM+3w- /с!+)e#, 8:Ha <>V-gnK`lErץ}Ď`G+PP}R9AsI`TiBf9h'Vm#,@ ΆEܝ3#/L3dJ׼сLMmH) #/#H. UV8(|WA]GDXj(L :#,߱ou)&Z7-m^%-OnL6CCۅo4Xn74"/|#-J:?ike$#3 93 }hELȆ ǠpqD` #/AĶ@ǝh۲MQf&GcrV HurU1.I8ɽ0(v)7Fk6&¥$,3HB*و@c8pvxI,afл/Y%z`XFuCTk@ ,׫z6/#5&i`pW5M4f3cm,Oq5#;pd0r\A᭍ʡJ,&0 "B @BEM )h RJU#5#/Wx;wV춌M!>cNTzqB#A!!ApV^;U KEj431[>q9"_ADpX[d.Ћih#5tݺR͎Aid-+X#dk`(o [FFK #5Mݧ`b̸;bmMv*|.㩴8KdB$1СU{n:kemi-m&@U P8C#/yPJ,Ъ1:z E[g?P;q: E]vM+okl%ɦcI]wfsuQmy#mg}Ƨpg,rHȳ"Q#/F2>Uq B0#/7>Gއa$RQ+F%E-35PhE#/ I#,U&aY_hS߷qLC]N*~Mž˦|}95v Kى#M h$FYDҠro!{M@H q:-'e #fu/V*(~RH)#,RZ ;#,v6hSXd&AUh*1"F#tJ4&i#,\% īmXԸD1!pHO3pL\e&EK1dfwGbv#/]! Bj l].t$ q.[YD*"Ix,؊(FXB`>d0A |A$& ?mFƆ~#o] 35o]Ȋæ!Ͻ k@nmpF_ol==1ɶW3yQKc+Ch laoZ4cXEz:ҙǥ VP[M3.)dCa074#53#3fi&;dѱ!6=.٢@.I9`5#/#`keL;CڠҖS3:g"K` A=:j4Y%v~g3j qv6K:AF%X)r)\d #,T0L1  C#/ 9Z$#5YE03#/nh/[棈 ? RD^NFe ejpH] 'fn ?Q?TpΠH5$L]YG=#,Z@P)^ d#,NqNU4QUUU#54FH[hbդU~+Ȓ8}ϸdhs#5}#5PO`8:ubrakwG#/dA, q2enjMtuiub;EF1p1\PitR 1;K4yYZ:ݭt &)U pQPYbȅ13ˏ5B"Q(D еfK-g)\:;X3BQ2qo#"찘J|!ֵ̕+;vՙ{o(Q4,mM/tQ6e)gL}`dCR MN2Jc`Vwvf˗\ ,,C "( Ő9xokeAE #,PJ` F2<݈ Jte.%qZȹ4R&nĉ@KB҅8Ѕ٦#5:zzٟ55]ݒm[}F.KawsK\fηs]Nh6Wi+rݤů;5;Ow^o5jB-TjCh*i#/#/ii"V#,n^62I I6V6ԢX1b"C0 fl#/+[IQY$AG?I{%"Й1}`NM.Ģ-"THlob ;;L'^aKZYL?yb<@#/NtFLJmz׫=TKy#,UEz,qP H|{LK8~L/B`BjԂ~#5lBfhZ4XR#/BDd7Qfv[M)a3ߥ}9-X6kP%,jT5\n 3v'ݬJs\mje2+@̐3p zϱk#l r*+xg"DevJ9g 끑t/Ѕ(7$txkӡ{ƤkTcEnLRY&mfvc-LԄr(|[[孠޺fҢYiElJK?__&m#|)DgvJJƶrQmo#/ԈY5PH%A@$E 5-%&i0jIk)6ͦV̵SE*Kb#EL-0Pl2%I)B2)(5&T5)YJjE(%i6jɵ4,TIYZPlH$e)3jdK61&V"FԔԦ*֥L4MJUK**|ݭMi)*M6mTi*͖cZlZj5jUPCIF9/HpnNoMYHe{20cB'~へ hY$=!^#/)Du/1_n'=Pk4#5.,=,kg]b٠3mezˣ>2A1`ydE,u}8MCFlt+YE Q(fIQ֚M$DDjXU#/ #,;{dkX',5vK60&Ӓv~#, 1"|uE.y!Jӯ2Cst!\\:q1"v_ B*750"t^+A#/tySyjzMI!4#o{a^E\^iYOW<|:xPӷrNA4btG R]Sc#Ng#>n,DB!L8r$JI֥ [JYkmGmȶ+jY>!S@`FeAB*\B#0}ז׃d>ɷ#1@ð3;x]ܩq@"Z#4ŖW_x ,Bkd+!ՁE:A#/b`5|ܤ(B1UmrͲ%6W]6M5jX,IkQlh?#,a#,AP"P7!Ba1Na3BUk0+"A0f6Ŀ&/``i4` `3c0-*m#,AV4Jg?+U4;㺣,7"I ψ>;vI#,%Ch#/;ǑOYyvǺ.uQ!«6FnCw7iD=wyG"T"q#pvv- @Y^F#Kj(h bXTe[1nXo:2 B?PCq#/e؃=_0[=CmVKbRj,muUKji+Z5FŘTD$A% !&#,3=wpAWVJPe$" %#59#:BaXLhcAُw53a Osarz( 8%cQ:GU,!Hw< 1HR**+vQhc10*5ÅhmXp[,&#/ǟqM+1y~N,HVRhx;;R# 8Jf՚0n(c<1Ыr#,*MTI%F8Y0z$it6uuOb{_zi|N&M~hֈb-"jM$%"m%21֛2hJK?qeR%XQM)i6LKfJƛYM-d։Pj"ZQU-*kU)h5z#/iO?6d"f#cD5$AJ Zj5kZn[2@$dHbuC~H_ro[W(MԿꄉ ˔uV,#/K>av\#,2~:(۰`ܧ.Hw`w0WAhDMzӊ A2=PU*PQ/j[(qXO"< ̕mB$!=L=|⪐h"jD98'ݕ2337}&$ziDk*4 @<{G#/gA'1#,bjIķXjP3v<䱬N Hw0lḆ\\(Hġk<!pI0Z~g!G i|@4/Myl&"Nk[FYD0uű[dBdbe^^07 sC+_} S .6i@11f&K>^&m5F~RE AN䥂(#bKw>d6kG:htt#,=_݌p2ɼnHcT=1,2&,KQCOun4T$Y`UD)-SƓ~˼S.(۠XcQq-' ˆAydfe@v`bqLq m]*HJJdݝ-tvw"\++6-Ky7͕E\P3HoVLƢ"3wkMm*o#/'Eq8A-H0z ] ,ӌQU̜<e͟B'0rwdj*yK5^P @3)c޷ԺWRa0;aDȢ߅o}g%.̡T BIn[FRP#[$Y Ti$kҢf/vAW<rG$bBLCh~Ņ'6A5F#,7Oߝ(F[$&ZJcnB?ja䥅Jk.91-ax}LQ#/fξ@kjGRРٱ]`B@F(iR@HE(f#5w:S tEvoοP1u5鬙kbD(uv9t` xD$6!UdE:`VAA%ZK\Gep^j<}]j~hu]vxPD^QZJW 6B #,Ƅ6% 1(2*TUF (PH}WCȢP;{kT `SU(^W{1\*&GaW0 n(w##57xnmĬc[p(#5a;@#:/2(_R(Ǧ9Z6IN0JlC*Dh) 6L"lsEg6f%YSd WL ƞB]0ȉۭDHٓ/mZhl0#/s`UD7wD01l2{TEO8 pB@u"T#,/-֗77KcI괌#d=^ix=KZ#/'6᭕FsLu8&:h)#,y, *]ZRjZ6eL֨RU["W]ڹ!(`)(Oݸc!#/ܑcVN}M<DחnOd`:g3!#T%T@DgD47:͟v ^}r G1+>irC'*YC(9$ 2idHA-"g7{=ـ/B6.W_E+ Q!yV`dvʗRڝ՞vӽl[zt֯ћSM_uҕĝVxțFFRs$h1j$_b7蒤=? Ǖhr[F*&k㱐xjV#~HReulLеd#/04孭Io#|Nz>P 4ǖ" R7%C2m﹄G#`#5H_8FHTը,\J v,nVi6ilUu[7e4\Jm}Rb`@E9q`@:DoU<Գ5Dz"*45!>MI3lAlcZ<#/rģI-{l>@ kQ LUMFa8֬bE,4#/SչW#/6MJ16߸7pl#/_p6h^on,ES`[`]Զ#5hXUջDA RF~VZ*֏3%Hk)JFl(3TqMP90yEAug#/TC~z,^G"`=T#/*UJo6M]ҫ!~ԡ{aM dpAlMH?.xS>Cŧ1M|,d50n>03hkľEFo5qIE.8<<4fji 4cCU5ٔ.!CF^2nNmEv>a0cc󱱗mi`Q00!OeSiAL]-#%/F 8]T(I8.0ayExѭퟧfpuꛧ<#5Ԥ%q y+ҜcQMBBniIłd8Γ#5<0H:@i}᤿5K`S#/$S}aS.=qP@o "ˆZKd8("|hUlCHMq2J .Vɼm*6mIZ*K][r׍hifِ§v֒Ml#, BAB2l@PKG ;Ȩ)Jkauv`Jl5YpMZҔջ-Zl8P76XjbH2@'%|my3D[kF.>/=e5)ʄ!EV񫩕I?P%X2ض-1YƣTmTͣEE3FVe$RŴ%kfv#5$9 Aﰡ:,֭{{#/&Մky_W|vm#/O:Oͷr]VÁP"#,H@rM*+~&\J[[v"V)O7]&ki)&m&VқVTW[KIQM!6^ A~jIJU G&0A F;JCay8 XEV)!P\7JW#5#/xaN"W]0mhP{5>o>O~#5?^H)AE~֕84}&mf,FE[TlcUcJY 2$hNJnLc,zdǿaCsb-PZR#5(P !gꮤM;:>$Q8du<^wᲩ[5]mY`[hƂQg{42-?⠈#;fu`vHO*Qt?0T!L3KܺnE2-!`yf@<`MmcTm-_}-HGLJ&12g#,KEdχQlՍZ5\յuX1nǝ>pD  <0?a?C_XZQA&_yqV0%h BKF@Xͅ؁Pl4$ڶvվ5IQUMoڼ݉ƮJ(DeR{%!a#ZawiZ{ҥcl‚L#5X*id-Y*  f c Š(T(`4t6L磻\EC2q4b( 1T _IP#/JKpc4a`sqcfL&CsWРqc0Ԗnݚ3@9@zDTQ1#,2vIihqX=nGo7ggHUy78~XC8GG!HC{z:\9pXɟW𶖬1] q{8띩q8 (΀TǍPUA v&ɷ䘎YJ蒤HB e#Ixm 2#5?Uy,)ji-: [G0Kl@(`-YEl8<WH#^y>;u茒i~6a!gtDž+Qˮ645A@>AEU&+t\զ[MJ!VwPDw&V3u6\JհfӱF{ͅwٸI -U#4櫆I!g#55jLRg AέR&HzM!;H@01B"fou֭mikʟnջl&4:d'@k:dG)R0 L0)A(#5l`|SCX @#/YfU4֦i/#,1۳0VQ#,umlʢԙ` *`T#0FJRRj0'P"$P prꃎs[S>5\,AL~>47H΄x²gWLlf-mEwc~#,b BO!{?=^"E#,e08g+`_}CyfHC I4>7z8blfh J 兡 #>,-j#6 `j#/$Ւ1W&h8$B(rA#/ڲ0@Ed&1<#5b#NMX; AMX`]Lw淈-GXjW/*ⶫW5Rk;fN={nj0#h~bH$G("c\<. Hk_*-jFi,%!Q*6TbIE#,5AE@w@ALZ`m6]CAX46Y7OmwMf#/bubF(yC?4Y53AgO6zƖlvA[>pZJ*m5FvC$MUtHF 0cKOxn]!p#/2Z}G%R,N 2MOBPZ Lڽϰtvc#5vbz"'/#/gyA 1>FTWlAv FE"HRD'#,#,C"XS#5`y`~#ި$ BﺌP>du`B#4趿nU 'oBx|WӗJ."5'ᱽ}P9JH( o#M-6h%w̰:wf79 Ӥ*cL@gТ\i`z\^!![$f4XBKLȭ*`&Y4n"Z}_$?/x*ۖ&o#/hHba !d;_/Q}h7"/:x/mۙUT`s6]Y3P{4֚;VBEe0:vr`4#/'8eFjqEb",q#/#+L m-0fQ2grI !.L; HNUkbf CǯFsSطR' Kv+`ǔ7={9 lGf-2>#ݺxZœxnǟSeI|>:lP#5.^)Iz npLMf f揇l5^.Jud!3@8)PhȘz#/QO@'1k:#/s>;YZ*ovfaUAݞ@J8EuBscŚ.oAMı @ipn"RVeA`T#p@~K?|v=0 iL$DXݖY` (4m#5h(>1m#/?9MI#5BHj}_5}J#/~ʭѭnDFFrٚ@7<*Veevvxw$NIY|h"'d_UwH0KU#/+g~Z}/0#/M:14=ه=.<4-Pr<ΜD1mz-jq admC\j0լ-KT\c# ^%#5I#5jwx<C3hl&W-7Ao@4PCrA)FP%DE!] O*܆~ҿ׽m OTۍ@>]`*t[6pbZ`RڡoB7#/]}ٜf hq0X>i Nm(v$El2[Bf,Di4ND$119#,(`P腤䡬GV:XiǓF}#,cm6&0hT"m#/,)^FJ&{Xg&AkcG6L~cA*u6(&B-#/4cl[n1gM#m>Jash(˰֚b…tDTѤpEНUHOID6<yp4t=(YeU$9H|m'#,:hF [s(ؘ;0fȈi-*@4!BQ bJ"4l`^)"4fjܑi%0**Mw-ݛ^M]WbkF Qd`!GQ#5?w#,YCBd1#,.&OϺP6$-ِ#,.v~O2E@_3#5#/((B2 nH B"ȢDFh, B'A9^H;y{m# BR!KFyhˆ7wRNևuדxŜcn*sjG%⋱tRSRHC)gvRKʉ#/0-2coPDS{n)ZƸK޸k!`6w/ȟG]!I.6Aȿx_N!q; HHDF*;2 yg!"^gUݾt48Y/#/H"L(pDE2%t`Y{OGwR*mTXt\T*#,;5k79O.|`Jٕ[Yì"hFb"TY@hZўS~'c5nZUI'(i(0%$٫o}6rQBT@C!U5AtKQV[OW4m淍_nkžWbW[k5shx;Zɵ!nk`[%B KV#/p^ud5\W’,b#HaN@{4T8"|@\Gؙb4c@I#,GĊ #,z@?ˀEKomW$4P!S)J#?q @>%X)ԉn"ȁUEl﵍%٥mw#5tX.BT%pZj-j&ںY$#5jK6V#5vؑDNDQ$VHAT} #,#,tP*'UOFAGB&q #5!zMX=`YH'J^`d:>BbEcdR@l14щ-3DJI2VŶVJU*ũcXf^hik2@ 91ȑ>)pM#/1<1HEd%T5Ah GGX"W#5)`#/eldE1T:UF44D"22*T #,fiL"'k&%Y;7 O®׽^PD AF B c#5Fևh.*W N0^6shhyKk#/EU)^}#,1 6۱!77q)2um:;9tM`,#5Y!VF@A1ީ-$<8kbHe.#,m“$Fb{]5LrەFU\\ֻ+kA.qY DbVU6dX|?fm8A凔}crB'Ä͸=PnO12R_'v۸ϯo. eu={pliܓ;:7OKt k/)΅-ۏ[/}E/,L'rB9]tMߊkGWtYA3[5lJڇEyF]!%#,WN-b2 ttUuĶCLK7ZXn?v#,:ÙN-Q67ʲ*pD5]NuDTSLEHEmy,[xvvb)WbOdbL(D2Swȴ;KOy7cқcP4с*mWϒq[kwy@co{T<#1܇Q'wb@뾽ʎu~HR*WOv5>#F#5wT 'RN rld5T=< Ax@2#5Շ&Gc'!c#/Xej#/*ԕ 9A%A\=Z?|4G(="LȻC=G/Kߞ<>7p%RhCِLs;IH)Y.$gF\m>Ӝ>OzһP{M9jj?/#/ۿk7UwFe1w16 X'#, JķukL9w*Gn|Pz il0;8~kFds$.jS$2*!Sږ3o`b#4@R 8D悚yvvb;8=qIzӾ.ThnAҦq<3G:M^OKp| 9#/5U #5h*N5Qu}X5EGmae"5#/PA8N) FToA\p Sh%V*RXs)fVjrʌ̕|Lvl*C_mPX =;ֈ缹e!M,-7R3u3ey@((̈gPy {ʤjPׂ%:f,ҭ&CcѮd҆#/&B\H֖N,a$߾Mn!: ، ;$7j0~jxdjl#/~T~?"%>=Lo25!*}D*sfҳ1fsWD<=^HQU:@ZXCVFѱDw*I0 65'm_okV-C0x5EƔtZJF08 D@fޭQAksmͨviGyYUSq"T'+AB`daLbBQVd{&0dUCw6=>rFaFvO9"}(Z]`@U#Dj#2Yj2ZL3^vEޓ')c !b3+nxK#c7l܁AAlȱ65љIs{)FmbmjZ#/Hj0$_u*qԊw#5Fu(3ƈ)a)pM,(PcfHSK21w̢xpk3 iY.lo! RƱ@+\ФIR7vmoĨ#/)l41lBM[:J6jje*XaLV1.ۗKG]Ȍ3Ek:@awm hqi2iq<˵aQٔ~nUJ5bࣄBc9GpF2 QU#cDF2#/BRhl;1F%c4-]Fipy+`zWdpTa#jP˲ԣr۰ꠃ? GҦfn*jR-yZzpmW\8@se-:1s[u,-J`qu6i.aZ JF$go$i-2FHFK3*ӌ!^5EIPQo#/:p0G 0%2Ȑ۾lDqJs;*ǎ}+?_X~;h4n>ξwz_3+#,B6iJ>X"B*Q)#5UHk2I=(e!eSgy[i T2۳ $UUFJ2*{џ/PE+ICcj=¾C^A"qm!JP@AJ&a28b̈́[Մzl uqFN!ݽx5i]tjaQp%-T3=gu2^? ;QppjUZ#/5G4AoD.F#5v~YzOgM#55n 5/ )"kI}ծ[1)4H)-A_r]jB#5PFR>eπ`'qx!B*Oۘ\3W@d}^j@j" QjxoC]+L"kVRQE4\#/4weٓ$X#,4w$l&1HmM$P* Xj%%ݛ&czzmF:*KHƑ#55Q#5E#,8'5 6FK^P{7g_y-Fޭ"bTiYDqbفl.g.jR4/ d$1A`Q<,1:5$m|ڬ 8tDn`*LCq- "I$PٷdÊgb(HѨ7$ǡi;M#/\W_78ڕ(,/d"B_60 h*sa-n]MA}RЗ0 T%|#, p{ W(4n%Ԣ[xc%~~>OjI:?SQ*):#se#8ba~X2aWJӭzTZ64.#51B}%s]k^v-ai=v䜭r4i>4"< f׸"5,:㻁;˶Lg&؛>f;}J`8$DW5Oا#/˝E`V@)2g 611}Xwh7!#QT7vuI3>_b7Olbwr>^BIDI(%B(jt큃 }'‘n,,).D~G~ǭn~MLFB P,+rc`6'^]hl0UqQ$*}_m[x`fo\e#/1IqokB%W*Y5X6Gg|]821w:*DyG7pl 9R%T:+\~q5gFԲ40bj"Bl3 2&#,LfڟZV) l!4vW"ihbE`BFȄTIx6;8Gxk9ɢK7Pe=#,˳RƘȭhRĹ6sLvA8M'&y DZ(`nQ?i``P̃"7VhͤB[BңS4,UMFY-ms=rkܱQ dg[YE`ٙNcQW gC #SAc՛f&3F|N.c(Fu0\;95`.iа;eC&:V3+:)t[C;Β[.59'LÙSs\Iu`B2 #/H%@ڙju >r+RfCe2g=mtyBCi fE2#,7 @杯̇jXH8΍.Q0tWF;btcir8M(e#?h0._҃ ̖57Ha< $t3 Slti#,sUP& mN5:[V-Pd+к0psW).(Ki 5fP`dYc0;1 \Zg;0SC#,ʩ&;X){(zphKbK&2_6S9`ʌwLx51X0͓~G36 x4~F(ky%\}GGѼ_kp9kIXOb\8WL3f G xıuړ #5tښbo["l]D!*~ ƹȴ7NP٨]4[ u,w1`GBmXcyӌ0fE4QHPz!hEAFH"PMO6;S]-2m)\g`unsEv#D" 3Q4"6#/K2DbHbj0Iz#/UO/wHRYu,DJb(Ů#,1a"/eHڂhC3biRCiQ p abb_B֊*EsI`dl!LZ6؈X&݄z}'яuF TTEAGnP]f^=`*;EHH) 5#,E~[ײUAs}* &ɸU$_.h"%Q v@PO.M31<+"ƒbP28dTLc`4kxKbT[A! d A5%TABJJ :#, >l|si(A:nKwLCdd&`R, N{ӳ|B|&>~Fp)~嚈Ά3FPp RKs1VDD 9Y~И]caS&J%u'CAMM iLx)A!H"Cɩ#,0BX ؂lHa>6LNpN=AU7#5h|>#,t"qvl"HjTv#5d:cDA\r,#/r}MB~e0a##K5 N"i1XY:Ed<饤CM"!,Fهa4š}C;PC-`k9NP+Z-sN ; ÷gl۔I6j*,YphYh[QU#,E% 6آj21pA-a]Z06pM NNJJRS,s#,&àN|P#,:v`R)eZҠA! .d|yl C7Y0ӮepLC8P$ E`AAU=|M#Ɖa-@!R.B6#,Rj#5~'$ceY>QŜ+4Stx_Rk ߅?ɨ|>>o*̌>k,"{M 3Ah'ĖoN憨m4kʫ@(#,$HE*oh侒 ZDbl *|d,)_vX.bv-$HL\VaI)"th5x^5^o+|MET"6uۨ &KQD Q底66@J *p$lO_wy$@! 9@eDM"!5H#,ok",H1FItI܂HKJ$*ehYfKfm_c}W)TCQ4e,ҐIOEP|f7@,)I#,Oc_T6bldMڊ*eF{j@B {(#,D"D-A5uwI⯐A5Cxt{Ġ"WDLp1 $NJ3)S T½`Ϟf)B%A{iq2 F1MCXSyL@A|Am6$ɭR)6t )sV7T"J=Z`JH(HeXOnKݨZ} @xRW69>Zؖ0l(Cmw_~C+CaXe-#"T44x d`;:`&lkrܒ}}*+|˱\ڍZ[[ƣErG6#Qd8$-#,,&OQϢIu#r"ĺ33AZMM ( phX}tf]JC`aP%LipCq5u]a%AB0Xskn^o*IbݚUҦԡ{V+*TQvך+A#,Ac)H&5h`[R4fZnNERDBNεm!XK=ۗૂYgUf48/1aU3qyjI벩"/PXL*&J8z0lcM=#/fB("y%!^K BA"5#/:8$Q; 6_c MZ׎_k|iinۦJ -~?Prgr8_`L9`~٤qʿDB6 ʇryc;ݦ"jIb8h#ߎ'\:-i#,e#CaU݁hz:9(dj2*&5趙m߶F [v3äB4Db'RmI*B#BC_`N"Rº#,m2ha/b"fg;Q۬;h-O5zwhi∤A c+qz)\ X7 ut %)`a_χ~O787v]b"jy65M\?9J<BU!pE!#ZWnwi|y$1y2IRCl`ݐG(Ay4!z#,63]ꥂ?ߞi\pxqF_TK,5()A@|n&8t00QaS,7tQy>*m4ؚ=({.eoD'4TCVzID%%-Wnmk^}IQTZDh#/1(мfӬ"5a+O(~^ K?ď Ki @r19Nac#56K*%*I}0$&Pk##=#5'e".hpH!!7E qFO$v' zj*bECkf˛%jFjro%Wؽh53_Zȣh#,wւw!=~({f80m֟x;.x#/gȤ?EBZv#/l'{pWi.#/ZXw솯S#l1zev񉒩~yAS>ӶVJszKpYk#DZ! ^G,$ }YOu68'L*ϫϣo3͏~WՑRs;jb/5Y_V7|{~.p ڋ -#<== -#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2\n\niQIcBAABCgAGBQJWpU3oAAoJEGelZe39+Q5kv04P/iyvALGAg2Z8oICEDjFkEXWW\nh2CMGLItAhqb3xNeV8WUMMpY4MbRRpN6cU/SPmt+as4oVn2pozca93eWD7yOxukK\n10seOyLTBamS0Wf+BNr6jYXZRQ2N7inc2p6AD75pMOFSg2HeIeQJ0aUIAxNeeojZ\nmUiLYMdtcrF1Kh7KWZAkYSbIAEjJeobLqk2oY7UyqKcODc4RtZJV1InnO4DItEWD\nnd3F5kkVMw4pwYAXaikmCXYBKHXdF5w82KxqEjrAWSoULipX0BVCsSbQ2L0UOs5h\nKXUS4M7AaYKyCcO17E7CnVXaW+vOVyGEECxtSaExWgK5MvYHIGE1OFvb12PkUvUY\nc7CiBxk6X5eZkPyxgxDj20r8zNQVGZ8jDI8Wg08yTAl8+09qCtkE8gGMdNeHYwX8\n2xDH+A3+19022ZZdyO2t5+2AzU6Kkl1qTPKaXJWFRtr8ApD45Y4D3/GAsTNqdOMi\nWeh1XvqQdHjm9rEoJX8aBXShzCMCNhmZalbUhrdzQY6/hnl0PqnlPtyvtkjCvWoF\nXLF6q8YV/ZtqCc36vePZ6lpUQB6FG3g6fhMGraT2VOmT3TROcG17pqIz5y9+85xy\nVSaDc82uHlyzIsZ7vuhV6d9x4yXnFkjMAogCJv6mitFbQsd+LtXYkU+2Zq6wOoEp\ndLLfK0Km4Vs9FYAUbuUi\n=7D7V\n-----END PGP SIGNATURE-----\n diff --git a/python_libs/pblprog/loader/waftools/ldscript.py b/python_libs/pblprog/loader/waftools/ldscript.py deleted file mode 100644 index 9fecd7f336..0000000000 --- a/python_libs/pblprog/loader/waftools/ldscript.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from waflib import Utils, Errors -from waflib.TaskGen import after, feature - - -@after('apply_link') -@feature('cprogram', 'cshlib') -def process_ldscript(self): - if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': - return - - node = self.path.find_resource(self.ldscript) - if not node: - raise Errors.WafError('could not find %r' % self.ldscript) - self.link_task.env.append_value('LINKFLAGS', '-T%s' % node.abspath()) - self.link_task.dep_nodes.append(node) diff --git a/python_libs/pblprog/loader/waftools/objcopy.py b/python_libs/pblprog/loader/waftools/objcopy.py deleted file mode 100644 index 4385f2aa88..0000000000 --- a/python_libs/pblprog/loader/waftools/objcopy.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Grygoriy Fuchedzhy 2010 - -""" -Support for converting linked targets to ihex, srec or binary files using -objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' -feature. The 'objcopy' feature uses the following attributes: - -objcopy_bfdname Target object format name (eg. ihex, srec, binary). -Defaults to ihex. -objcopy_target File name used for objcopy output. This defaults to the -target name with objcopy_bfdname as extension. -objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. -objcopy_flags Additional flags passed to objcopy. -""" - -from waflib.Utils import def_attrs -from waflib import Task -from waflib.TaskGen import feature, after_method - - -class objcopy(Task.Task): - run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' - color = 'CYAN' - - -@feature('objcopy') -@after_method('apply_link') -def objcopy(self): - def_attrs(self, - objcopy_bfdname='ihex', - objcopy_target=None, - objcopy_install_path="${PREFIX}/firmware", - objcopy_flags='') - - link_output = self.link_task.outputs[0] - - if not self.objcopy_target: - self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name - elif isinstance(self.objcopy_target, str): - self.objcopy_target = self.path.find_or_declare(self.objcopy_target) - - task = self.create_task('objcopy', - src=link_output, - tgt=self.objcopy_target) - - task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) - try: - task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) - except AttributeError: - pass - - if self.objcopy_install_path: - self.bld.install_files(self.objcopy_install_path, - task.outputs[0], - env=task.env.derive()) - - -def configure(ctx): - objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) - - -def objcopy_simple(task, mode): - return task.exec_command('arm-none-eabi-objcopy -S -R .stack -R .priv_bss' - ' -R .bss -O %s "%s" "%s"' % - (mode, task.inputs[0].abspath(), task.outputs[0].abspath())) - - -def objcopy_simple_bin(task): - return objcopy_simple(task, 'binary') diff --git a/python_libs/pblprog/loader/wscript b/python_libs/pblprog/loader/wscript deleted file mode 100644 index b2cb599f0c..0000000000 --- a/python_libs/pblprog/loader/wscript +++ /dev/null @@ -1,63 +0,0 @@ -# Build script for the silk loader - -import sys -import os -from waflib import Logs - -def options(opt): - opt.load('gcc') - - -def configure(conf): - # Find our binary tools - conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True) - conf.env.AS = conf.env.CC - conf.find_program('arm-none-eabi-gcc-ar', var='AR', mandatory=True) - - conf.load('gcc') - - for tool in 'ar objcopy'.split(): - conf.find_program('arm-none-eabi-' + tool, var=tool.upper(), mandatory=True) - - # Set up our compiler configuration - CPU_FLAGS = ['-mcpu=cortex-m3', '-mthumb'] - OPT_FLAGS = ['-Os', '-g'] - C_FLAGS = [ - '-std=c11', '-ffunction-sections', - '-Wall', '-Wextra', '-Werror', '-Wpointer-arith', - '-Wno-unused-parameter', '-Wno-missing-field-initializers', - '-Wno-error=unused-function', '-Wno-error=unused-variable', - '-Wno-error=unused-parameter', '-Wno-error=unused-but-set-variable', - '-Wno-packed-bitfield-compat' - ] - LINK_FLAGS = ['-Wl,--gc-sections', '-specs=nano.specs'] - - conf.env.append_unique('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS) - conf.env.append_unique('LINKFLAGS', LINK_FLAGS + CPU_FLAGS + OPT_FLAGS) - - conf.env.append_unique('DEFINES', ['_REENT_SMALL=1']) - - # Load up other waftools that we need - conf.load('objcopy ldscript', tooldir='waftools') - -def build(bld): - elf_node = bld.path.get_bld().make_node('loader.elf') - - linkflags = ['-Wl,-Map,loader.map'] - - sources = ['src/**/*.c'] - - includes = ['src'] - - bld.program(features="objcopy", - source=bld.path.ant_glob(sources), - includes=includes, - target=elf_node, - ldscript='src/stm32f4_loader.ld', - linkflags=linkflags, - objcopy_bfdname='ihex', - objcopy_target=elf_node.change_ext('.hex')) - import objcopy - bld(rule=objcopy.objcopy_simple_bin, source='loader.elf', target='loader.bin') - -# vim:filetype=python diff --git a/python_libs/pblprog/pebble/__init__.py b/python_libs/pblprog/pebble/__init__.py deleted file mode 100644 index 0dbb5a8558..0000000000 --- a/python_libs/pblprog/pebble/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__import__('pkg_resources').declare_namespace(__name__) diff --git a/python_libs/pblprog/pebble/programmer/__init__.py b/python_libs/pblprog/pebble/programmer/__init__.py deleted file mode 100644 index 68f1c8cb68..0000000000 --- a/python_libs/pblprog/pebble/programmer/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .targets import STM32F4FlashProgrammer, STM32F7FlashProgrammer -from .swd_port import SerialWireDebugPort -from .ftdi_swd import FTDISerialWireDebug - -def get_device(board, reset=True, frequency=None): - boards = { - 'silk_bb': (0x7893, 10E6, STM32F4FlashProgrammer), - 'robert_bb2': (0x7894, 3E6, STM32F7FlashProgrammer) - } - - if board not in boards: - raise Exception('Invalid board: {}'.format(board)) - - usb_pid, default_frequency, board_ctor = boards[board] - if not frequency: - frequency = default_frequency - - ftdi = FTDISerialWireDebug(vid=0x0403, pid=usb_pid, interface=0, direction=0x1b, - output_mask=0x02, reset_mask=0x40, frequency=frequency) - swd_port = SerialWireDebugPort(ftdi, reset) - return board_ctor(swd_port) - -def flash(board, hex_files): - with get_device(board) as programmer: - programmer.execute_loader() - for hex_file in hex_files: - programmer.load_hex(hex_file) - - programmer.reset_core() - diff --git a/python_libs/pblprog/pebble/programmer/__main__.py b/python_libs/pblprog/pebble/programmer/__main__.py deleted file mode 100644 index 3ad1e6e51d..0000000000 --- a/python_libs/pblprog/pebble/programmer/__main__.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import logging - -from . import flash - -def main(args=None): - if args is None: - parser = argparse.ArgumentParser(description='A tool for flashing a bigboard via FTDI+SWD') - parser.add_argument('hex_files', type=str, nargs='+', - help='Path to one or more hex files to flash') - parser.add_argument('--board', action='store', choices=['robert_bb2', 'silk_bb'], required=True, - help='Which board is being programmed') - parser.add_argument('--verbose', action='store_true', - help='Output lots of debugging info to the console.') - - args = parser.parse_args() - - logging.basicConfig(level=(logging.DEBUG if args.verbose else logging.INFO)) - - flash(args.board, args.hex_files) - -if __name__ == '__main__': - main() - diff --git a/python_libs/pblprog/pebble/programmer/ftdi_swd.py b/python_libs/pblprog/pebble/programmer/ftdi_swd.py deleted file mode 100644 index 52b228481b..0000000000 --- a/python_libs/pblprog/pebble/programmer/ftdi_swd.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from array import array - -from pyftdi.ftdi import Ftdi -import usb.util - -class FTDISerialWireDebug(object): - def __init__(self, vid, pid, interface, direction, output_mask, reset_mask, frequency): - self._direction = direction - self._output_mask = output_mask - self._reset_mask = reset_mask - self._ftdi = Ftdi() - try: - self._ftdi.open_mpsse(vid, pid, interface, direction=direction, frequency=frequency, - latency=1) - except: - self._ftdi = None - raise - - # get the FTDI FIFO size and increase the chuncksize to match - self._ftdi_fifo_size = min(self._ftdi.fifo_sizes) - self._ftdi.write_data_set_chunksize(self._ftdi_fifo_size) - - self._cmd_buffer = array('B') - self._output_enabled = False - self._pending_acks = 0 - self._sequence_cmd_buffer = None - - def close(self): - if not self._ftdi: - return - self.send_cmds() - self._ftdi.close() - # PyFTDI doesn't do a good job of cleaning up - make sure we release the usb device - usb.util.dispose_resources(self._ftdi.usb_dev) - self._ftdi = None - - def _fatal(self, message): - raise Exception('FATAL ERROR: {}'.format(message)) - - def _queue_cmd(self, write_data): - if len(write_data) > self._ftdi_fifo_size: - raise Exception('Data too big!') - if self._sequence_cmd_buffer is not None: - self._sequence_cmd_buffer.extend(write_data) - else: - if len(self._cmd_buffer) + len(write_data) > self._ftdi_fifo_size: - self.send_cmds() - self._cmd_buffer.extend(write_data) - - def _set_output_enabled(self, enabled): - if enabled == self._output_enabled: - return - self._output_enabled = enabled - direction = self._direction & ~(0x00 if enabled else self._output_mask) - self._queue_cmd([Ftdi.SET_BITS_LOW, 0, direction]) - - def reset(self): - # toggle the reset line - self.reset_lo() - self.reset_hi() - - def reset_lo(self): - direction = self._direction & ~(0x00 if self._output_enabled else self._output_mask) - self._queue_cmd([Ftdi.SET_BITS_LOW, 0, direction | self._reset_mask]) - self.send_cmds() - - def reset_hi(self): - direction = self._direction & ~(0x00 if self._output_enabled else self._output_mask) - self._queue_cmd([Ftdi.SET_BITS_LOW, 0, direction & ~self._reset_mask]) - self.send_cmds() - - def send_cmds(self): - if self._sequence_cmd_buffer is not None: - self._ftdi.write_data(self._sequence_cmd_buffer) - elif len(self._cmd_buffer) > 0: - self._ftdi.write_data(self._cmd_buffer) - self._cmd_buffer = array('B') - - def write_bits_cmd(self, data, num_bits): - if num_bits < 0 or num_bits > 8: - self._fatal('Invalid num_bits') - elif (data & ((1 << num_bits) - 1)) != data: - self._fatal('Invalid data!') - self._set_output_enabled(True) - self._queue_cmd([Ftdi.WRITE_BITS_NVE_LSB, num_bits - 1, data]) - - def write_bytes_cmd(self, data): - length = len(data) - 1 - if length < 0 or length > 0xffff: - self._fatal('Invalid length') - self._set_output_enabled(True) - self._queue_cmd([Ftdi.WRITE_BYTES_NVE_LSB, length & 0xff, length >> 8] + data) - - def read_bits_cmd(self, num_bits): - if num_bits < 0 or num_bits > 8: - self._fatal('Invalid num_bits') - self._set_output_enabled(False) - self._queue_cmd([Ftdi.READ_BITS_PVE_LSB, num_bits - 1]) - - def read_bytes_cmd(self, length): - length -= 1 - if length < 0 or length > 0xffff: - self._fatal('Invalid length') - self._set_output_enabled(False) - self._queue_cmd([Ftdi.READ_BYTES_PVE_LSB, length & 0xff, length >> 8]) - - def get_read_bytes(self, length): - return self._ftdi.read_data_bytes(length) - - def get_read_fifo_size(self): - return self._ftdi_fifo_size - - def start_sequence(self): - if self._sequence_cmd_buffer is not None: - self._fatal('Attempted to start a sequence while one is in progress') - self.send_cmds() - self._sequence_cmd_buffer = array('B') - - def end_sequence(self): - if self._sequence_cmd_buffer is None: - self._fatal('No sequence started') - self._sequence_cmd_buffer = None - diff --git a/python_libs/pblprog/pebble/programmer/swd_port.py b/python_libs/pblprog/pebble/programmer/swd_port.py deleted file mode 100644 index 8c68cb5ef2..0000000000 --- a/python_libs/pblprog/pebble/programmer/swd_port.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import struct -import time - -class SerialWireDebugPort(object): - # debug port registers - DP_IDCODE_ADDR = 0x00 - DP_ABORT_ADDR = 0x00 - DP_CTRLSTAT_ADDR = 0x04 - DP_SELECT_ADDR = 0x08 - DP_RDBUFF_ADDR = 0x0c - - # MEM-AP register - MEM_AP_CSW_ADDR = 0x0 - MEM_AP_CSW_MASTER_DEBUG = (1 << 29) - MEM_AP_CSW_PRIVILEGED_MODE = (1 << 25) - MEM_AP_CSW_ADDRINCWORD = (1 << 4) - MEM_AP_CSW_SIZE8BITS = (0 << 1) - MEM_AP_CSW_SIZE32BITS = (1 << 1) - - MEM_AP_TAR_ADDR = 0x4 - MEM_AP_DRW_ADDR = 0xc - MEM_AP_IDR_VALUES = [0x24770011, 0x74770001] - - def __init__(self, driver, reset=True): - self._driver = driver - self._swd_connected = False - self._reset = reset - self._pending_acks = 0 - - def close(self): - if self._swd_connected: - # power down the system and debug domains - self._write(self.DP_CTRLSTAT_ADDR, 0x00000000, is_access_port=False) - # send 1 byte worth of trailing bits since we're done communicating - self._driver.write_bits_cmd(0x00, 8) - # this makes the Saleae's SWD analyzer happy, and it's otherwise harmless - self._driver.write_bits_cmd(0x3, 2) - self._swd_connected = False - self._driver.close() - - @staticmethod - def _fatal(message): - raise Exception('FATAL ERROR: {}'.format(message)) - - def _get_request_header(self, addr, is_read, is_access_port): - # the header consists of the following fields - # bit 0: start (always 1) - # bit 1: DebugPort (0) or AccessPort (1) - # bit 2: write (0) or read (1) - # bits 3-4: bits 2 and 3 of the address - # bit 5: parity bit such that bits 1-5 contain an even number of 1's - # bit 6: stop (always 0) - # bit 7: park (always 1) - header = 0x1 - header |= (1 << 1) if is_access_port else 0 - header |= (1 << 2) if is_read else 0 - header |= ((addr & 0xf) >> 2) << 3 - parity = 0 - for i in range(1, 5): - parity += (header >> i) & 0x1 - header |= (parity & 0x1) << 5 - header |= 1 << 7 - return header - - def _send_request_header(self, addr, is_read, is_access_port): - self._driver.write_bytes_cmd([self._get_request_header(addr, is_read, is_access_port)]) - - def _check_write_acks(self): - if not self._pending_acks: - return - self._driver.send_cmds() - # the ACK is in the top 3 bits that we get from the FTDI read, so shift right by 5 - for ack in [x >> 5 for x in self._driver.get_read_bytes(self._pending_acks)]: - if ack != 0x1: - self._fatal('ACK=0x{:02x}'.format(ack)) - self._pending_acks = 0 - - def _read(self, addr, is_access_port): - # check any pending write ACKs before doing a read - self._check_write_acks() - - # send the read request - self._send_request_header(addr, is_read=True, is_access_port=is_access_port) - - # do all the reads at the same time as an optimization (and hope we get an ACK) - self._driver.read_bits_cmd(4) # 4 bits for ACK + turnaround - self._driver.read_bytes_cmd(4) # 4 data bytes - self._driver.read_bits_cmd(2) # 2 bits for parity + turnaround - self._driver.send_cmds() - result = self._driver.get_read_bytes(6) - - # check the ACK - ack = result[0] >> 5 - if ack != 0x1: - self._fatal('ACK=0x{:02x}'.format(ack)) - - # grab the response - response = struct.unpack('> 6) & 0x1 - if parity != sum((response >> i) & 0x1 for i in range(32)) & 0x1: - self._fatal('Bad parity') - - return response - - def _write(self, addr, data, is_access_port, no_ack=False): - if data > 0xffffffff: - self._fatal('Bad data') - - # send the write request - self._send_request_header(addr, is_read=False, is_access_port=is_access_port) - - # OPTIMIZATION: queue the ACK read now and keep going (hope we get an ACK) - self._driver.read_bits_cmd(4) - - # calculate the parity and send the data - parity = sum((data >> i) & 0x1 for i in range(32)) & 0x1 - # OPTIMIZATION: We need to send 1 turnaround bit, 4 data bytes, and 1 parity bit. - # We can combine this into a single FTDI write by sending it as 5 bytes, so - # let's shift everything such that the extra 6 bits are at the end where they - # will be properly ignored as trailing bits. - temp = ((data << 1) & 0xfffffffe) - data_bytes = [(temp >> (i * 8)) & 0xff for i in range(4)] - data_bytes += [(data >> 31) | (parity << 1)] - self._driver.write_bytes_cmd(data_bytes) - - # check the ACK(s) if necessary - self._pending_acks += 1 - if not no_ack or self._pending_acks >= self._driver.get_read_fifo_size(): - self._check_write_acks() - - def connect(self): - if self._reset: - # reset the target - self._driver.reset_lo() - - # switch from JTAG to SWD mode (based on what openocd does) - # - line reset - # - magic number of 0xE79E - # - line reset - # - 2 low bits for unknown reasons (maybe padding to nibbles?) - def line_reset(): - # a line reset is 50 high bits (6 bytes + 2 bits) - self._driver.write_bytes_cmd([0xff] * 6) - self._driver.write_bits_cmd(0x3, 2) - line_reset() - self._driver.write_bytes_cmd([0x9e, 0xe7]) - line_reset() - self._driver.write_bits_cmd(0x0, 2) - - idcode = self._read(self.DP_IDCODE_ADDR, is_access_port=False) - - # clear the error flags - self._write(self.DP_ABORT_ADDR, 0x0000001E, is_access_port=False) - - # power up the system and debug domains - self._write(self.DP_CTRLSTAT_ADDR, 0xF0000001, is_access_port=False) - - # check the MEM-AP IDR - # the IDR register is has the same address as the DRW register but on the 0xf bank - self._write(self.DP_SELECT_ADDR, 0xf0, is_access_port=False) # select the 0xf bank - self._read(self.MEM_AP_DRW_ADDR, is_access_port=True) # read the value register (twice) - if self._read(self.DP_RDBUFF_ADDR, is_access_port=False) not in self.MEM_AP_IDR_VALUES: - self._fatal('Invalid MEM-AP IDR') - self._write(self.DP_SELECT_ADDR, 0x0, is_access_port=False) # return to the 0x0 bank - - # enable privileged access to the MEM-AP with 32 bit data accesses and auto-incrementing - csw_value = self.MEM_AP_CSW_PRIVILEGED_MODE - csw_value |= self.MEM_AP_CSW_MASTER_DEBUG - csw_value |= self.MEM_AP_CSW_ADDRINCWORD - csw_value |= self.MEM_AP_CSW_SIZE32BITS - self._write(self.MEM_AP_CSW_ADDR, csw_value, is_access_port=True) - - self._swd_connected = True - if self._reset: - self._driver.reset_hi() - return idcode - - def read_memory_address(self, addr): - self._write(self.MEM_AP_TAR_ADDR, addr, is_access_port=True) - self._read(self.MEM_AP_DRW_ADDR, is_access_port=True) - return self._read(self.DP_RDBUFF_ADDR, is_access_port=False) - - def write_memory_address(self, addr, value): - self._write(self.MEM_AP_TAR_ADDR, addr, is_access_port=True) - self._write(self.MEM_AP_DRW_ADDR, value, is_access_port=True) - - def write_memory_bulk(self, base_addr, data): - # TAR is configured as auto-incrementing, but it wraps every 4096 bytes, so that's how much - # we can write before we need to explicitly set it again. - WORD_SIZE = 4 - BURST_LENGTH = 4096 / WORD_SIZE - assert(base_addr % BURST_LENGTH == 0 and len(data) % WORD_SIZE == 0) - for i in range(0, len(data), WORD_SIZE): - if i % BURST_LENGTH == 0: - # set the target address - self._write(self.MEM_AP_TAR_ADDR, base_addr + i, is_access_port=True, no_ack=True) - # write the word - word = sum(data[i+j] << (j * 8) for j in range(WORD_SIZE)) - self._write(self.MEM_AP_DRW_ADDR, word, is_access_port=True, no_ack=True) - - def continuous_read(self, addr, duration): - # This is a highly-optimized function which is samples the specified memory address for the - # specified duration. This is generally used for profiling by reading the PC sampling - # register. - - NUM_READS = 510 # a magic number which gives us the best sample rate on Silk/Robert - - # don't auto-increment the address - csw_value = self.MEM_AP_CSW_PRIVILEGED_MODE - csw_value |= self.MEM_AP_CSW_SIZE32BITS - self._write(self.MEM_AP_CSW_ADDR, csw_value, is_access_port=True) - - # set the address - self._write(self.MEM_AP_TAR_ADDR, addr, is_access_port=True) - - # discard the previous value - self._read(self.MEM_AP_DRW_ADDR, is_access_port=True) - - # flush everything - self._check_write_acks() - - header = self._get_request_header(self.MEM_AP_DRW_ADDR, is_read=True, is_access_port=True) - self._driver.start_sequence() - for i in range(NUM_READS): - self._driver.write_bits_cmd(header, 8) - self._driver.read_bits_cmd(6) - self._driver.read_bytes_cmd(4) - - raw_data = [] - end_time = time.time() + duration - while time.time() < end_time: - # send the read requests - self._driver.send_cmds() - # do all the reads at the same time as an optimization (and hope we get an ACK) - raw_data.extend(self._driver.get_read_bytes(5 * NUM_READS)) - self._driver.end_sequence() - - def get_value_from_bits(b): - result = 0 - for o in range(len(b)): - result |= b[o] << o - return result - values = [] - for raw_result in [raw_data[i:i+5] for i in range(0, len(raw_data), 5)]: - result = raw_result - # The result is read as 5 bytes, with the first one containing 6 bits (shifted in from - # the left as they are read). Let's convert this into an array of bits, and then - # reconstruct the values we care about. - bits = [] - bits.extend((result[0] >> (2 + j)) & 1 for j in range(6)) - for i in range(4): - bits.extend((result[i + 1] >> j) & 1 for j in range(8)) - ack = get_value_from_bits(bits[1:4]) - response = get_value_from_bits(bits[4:36]) - parity = bits[36] - - # check the ACK - if ack != 0x1: - self._fatal('ACK=0x{:02x}'.format(ack)) - - # read two more bits: the parity and another for some reason I don't understand - # check that the parity is correct - if parity != sum((response >> i) & 0x1 for i in range(32)) & 0x1: - self._fatal('Bad parity') - - # store the response - values.append(response) - - return values - diff --git a/python_libs/pblprog/pebble/programmer/targets/__init__.py b/python_libs/pblprog/pebble/programmer/targets/__init__.py deleted file mode 100644 index 4f59c4c40f..0000000000 --- a/python_libs/pblprog/pebble/programmer/targets/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .stm32f4 import STM32F4FlashProgrammer -from .stm32f7 import STM32F7FlashProgrammer diff --git a/python_libs/pblprog/pebble/programmer/targets/stm32.py b/python_libs/pblprog/pebble/programmer/targets/stm32.py deleted file mode 100644 index 68823aef23..0000000000 --- a/python_libs/pblprog/pebble/programmer/targets/stm32.py +++ /dev/null @@ -1,352 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import array -import logging -import os -import struct -import time -from intelhex import IntelHex - -LOG = logging.getLogger(__name__) - -class STM32FlashProgrammer(object): - # CPUID register - CPUID_ADDR = 0xE000ED00 - - # Flash constants - FLASH_BASE_ADDR = 0x08000000 - - # Flash key register (FLASH_KEYR) - FLASH_KEYR_ADDR = 0x40023C04 - FLASH_KEYR_VAL1 = 0x45670123 - FLASH_KEYR_VAL2 = 0xCDEF89AB - - # Flash status register (FLASH_SR) - FLASH_SR_ADDR = 0x40023C0C - FLASH_SR_BSY = (1 << 16) - - # Flash control register (FLASH_CR) - FLASH_CR_ADDR = 0x40023C10 - FLASH_CR_PG = (1 << 0) - FLASH_CR_SER = (1 << 1) - FLASH_CR_SNB_OFFSET = 3 - FLASH_CR_PSIZE_8BIT = (0x0 << 8) - FLASH_CR_PSIZE_16BIT = (0x1 << 8) - FLASH_CR_PSIZE_32BIT = (0x2 << 8) - FLASH_CR_STRT = (1 << 16) - - # Debug halting control and status register (DHCSR) - DHCSR_ADDR = 0xE000EDF0 - DHCSR_DBGKEY_VALUE = 0xA05F0000 - DHCSR_HALT = (1 << 0) - DHCSR_DEBUGEN = (1 << 1) - DHCSR_S_REGRDY = (1 << 16) - DHCSR_S_LOCKUP = (1 << 19) - - # Application interrupt and reset control register (AIRCR) - AIRCR_ADDR = 0xE000ED0C - AIRCR_VECTKEY_VALUE = 0x05FA0000 - AIRCR_SYSRESETREQ = (1 << 2) - - # Debug Core Register Selector Register (DCRSR) - DCRSR_ADDR = 0xE000EDF4 - DCRSR_WRITE = (1 << 16) - - # Debug Core Register Data register (DCRDR) - DCRDR_ADDR = 0xE000EDF8 - - # Debug Exception and Monitor Control register (DEMCR) - DEMCR_ADDR = 0xE000EDFC - DEMCR_RESET_CATCH = (1 << 0) - DEMCR_TRCENA = (1 << 24) - - # Program Counter Sample Register (PCSR) - PCSR_ADDR = 0xE000101C - - # Loader addresses - PBLLDR_HEADER_ADDR = 0x20000400 - PBLLDR_HEADER_OFFSET = PBLLDR_HEADER_ADDR + 0x4 - PBLLDR_HEADER_LENGTH = PBLLDR_HEADER_ADDR + 0x8 - PBLLDR_DATA_ADDR = 0x20000800 - PBLLDR_DATA_MAX_LENGTH = 0x20000 - PBLLDR_STATE_WAIT = 0 - PBLLDR_STATE_WRITE = 1 - PBLLDR_STATE_CRC = 2 - - # SRAM base addr - SRAM_BASE_ADDR = 0x20000000 - - def __init__(self, driver): - self._driver = driver - self._step_start_time = 0 - self.FLASH_SECTOR_SIZES = [x*1024 for x in self.FLASH_SECTOR_SIZES] - - def __enter__(self): - try: - self.connect() - return self - except: - self.close() - raise - - def __exit__(self, exc, value, trace): - self.close() - - def _fatal(self, message): - raise Exception('FATAL ERROR: {}'.format(message)) - - def _start_step(self, msg): - LOG.info(msg) - self._step_start_time = time.time() - - def _end_step(self, msg, no_time=False, num_bytes=None): - total_time = round(time.time() - self._step_start_time, 2) - if not no_time: - msg += ' in {}s'.format(total_time) - if num_bytes: - kibps = round(num_bytes / 1024.0 / total_time, 2) - msg += ' ({} KiB/s)'.format(kibps) - LOG.info(msg) - - def connect(self): - self._start_step('Connecting...') - - # connect and check the IDCODE - if self._driver.connect() != self.IDCODE: - self._fatal('Invalid IDCODE') - - # check the CPUID register - if self._driver.read_memory_address(self.CPUID_ADDR) != self.CPUID_VALUE: - self._fatal('Invalid CPU ID') - - self._end_step('Connected', no_time=True) - - def halt_core(self): - # halt the core immediately - dhcsr_value = self.DHCSR_DBGKEY_VALUE | self.DHCSR_DEBUGEN | self.DHCSR_HALT - self._driver.write_memory_address(self.DHCSR_ADDR, dhcsr_value) - - def resume_core(self): - # resume the core - dhcsr_value = self.DHCSR_DBGKEY_VALUE - self._driver.write_memory_address(self.DHCSR_ADDR, dhcsr_value) - - def reset_core(self, halt=False): - if self._driver.read_memory_address(self.DHCSR_ADDR) & self.DHCSR_S_LOCKUP: - # halt the core first to clear the lockup - LOG.info('Clearing lockup condition') - self.halt_core() - - # enable reset vector catch - demcr_value = 0 - if halt: - demcr_value |= self.DEMCR_RESET_CATCH - - self._driver.write_memory_address(self.DEMCR_ADDR, demcr_value) - self._driver.read_memory_address(self.DHCSR_ADDR) - - # reset the core - aircr_value = self.AIRCR_VECTKEY_VALUE | self.AIRCR_SYSRESETREQ - self._driver.write_memory_address(self.AIRCR_ADDR, aircr_value) - - if halt: - self.halt_core() - - def unlock_flash(self): - # unlock the flash - self._driver.write_memory_address(self.FLASH_KEYR_ADDR, self.FLASH_KEYR_VAL1) - self._driver.write_memory_address(self.FLASH_KEYR_ADDR, self.FLASH_KEYR_VAL2) - - def _poll_register(self, timeout=0.5): - end_time = time.time() + timeout - while end_time > time.time(): - val = self._driver.read_memory_address(self.DHCSR_ADDR) - if val & self.DHCSR_S_REGRDY: - break - else: - raise Exception('Register operation was not confirmed') - - def write_register(self, reg, val): - self._driver.write_memory_address(self.DCRDR_ADDR, val) - reg |= self.DCRSR_WRITE - self._driver.write_memory_address(self.DCRSR_ADDR, reg) - self._poll_register() - - def read_register(self, reg): - self._driver.write_memory_address(self.DCRSR_ADDR, reg) - self._poll_register() - return self._driver.read_memory_address(self.DCRDR_ADDR) - - def erase_flash(self, flash_offset, length): - self._start_step('Erasing...') - - def overlap(a1, a2, b1, b2): - return max(a1, b1) < min(a2, b2) - - # find all the sectors which we need to erase - erase_sectors = [] - for i, size in enumerate(self.FLASH_SECTOR_SIZES): - addr = self.FLASH_BASE_ADDR + sum(self.FLASH_SECTOR_SIZES[:i]) - if overlap(flash_offset, flash_offset+length, addr, addr+size): - erase_sectors += [i] - if not erase_sectors: - self._fatal('Could not find sectors to erase!') - - # erase the sectors - for sector in erase_sectors: - # start the erase - reg_value = (sector << self.FLASH_CR_SNB_OFFSET) - reg_value |= self.FLASH_CR_PSIZE_8BIT - reg_value |= self.FLASH_CR_STRT - reg_value |= self.FLASH_CR_SER - self._driver.write_memory_address(self.FLASH_CR_ADDR, reg_value) - # wait for the erase to finish - while self._driver.read_memory_address(self.FLASH_SR_ADDR) & self.FLASH_SR_BSY: - time.sleep(0) - - self._end_step('Erased') - - def close(self): - self._driver.close() - - def _write_loader_state(self, state): - self._driver.write_memory_address(self.PBLLDR_HEADER_ADDR, state) - - def _wait_loader_state(self, wanted_state, timeout=3): - end_time = time.time() + timeout - state = -1 - while time.time() < end_time: - time.sleep(0) - state = self._driver.read_memory_address(self.PBLLDR_HEADER_ADDR) - if state == wanted_state: - break - else: - raise Exception("Timed out waiting for loader state %d, got %d" % (wanted_state, state)) - - @staticmethod - def _chunks(l, n): - for i in xrange(0, len(l), n): - yield l[i:i+n], len(l[i:i+n]), i - - def execute_loader(self): - # reset and halt the core - self.reset_core(halt=True) - - with open(os.path.join(os.path.dirname(__file__), "loader.bin")) as f: - loader_bin = f.read() - - # load loader binary into SRAM - self._driver.write_memory_bulk(self.SRAM_BASE_ADDR, array.array('B', loader_bin)) - - # set PC based on value in loader - reg_sp, = struct.unpack(">= 4 - - index = nibble ^ (crc >> 4) - crc = lookup_table[index & 0xf] ^ ((crc << 4) & 0xf0) - - return crc - - def read_crc(self, addr, length): - self._driver.write_memory_address(self.PBLLDR_HEADER_OFFSET, addr) - self._driver.write_memory_address(self.PBLLDR_HEADER_LENGTH, length) - - self._write_loader_state(self.PBLLDR_STATE_CRC) - self._wait_loader_state(self.PBLLDR_STATE_WAIT) - return self._driver.read_memory_address(self.PBLLDR_DATA_ADDR) & 0xFF - - def load_hex(self, hex_path): - self._start_step("Loading binary: %s" % hex_path) - - ih = IntelHex(hex_path) - offset = ih.minaddr() - data = ih.tobinarray() - - self.load_bin(offset, data) - self._end_step("Loaded binary", num_bytes=len(data)) - - def load_bin(self, offset, data): - while len(data) % 4 != 0: - data.append(0xFF) - - length = len(data) - - # prepare the flash for programming - self.erase_flash(offset, length) - cr_value = self.FLASH_CR_PSIZE_8BIT | self.FLASH_CR_PG - self._driver.write_memory_address(self.FLASH_CR_ADDR, cr_value) - - # set the base address - self._wait_loader_state(self.PBLLDR_STATE_WAIT) - self._driver.write_memory_address(self.PBLLDR_HEADER_OFFSET, offset) - - for chunk, chunk_length, pos in self._chunks(data, self.PBLLDR_DATA_MAX_LENGTH): - LOG.info("Written %d/%d", pos, length) - - self._driver.write_memory_address(self.PBLLDR_HEADER_LENGTH, chunk_length) - self._driver.write_memory_bulk(self.PBLLDR_DATA_ADDR, chunk) - - self._write_loader_state(self.PBLLDR_STATE_WRITE) - self._wait_loader_state(self.PBLLDR_STATE_WAIT) - - expected_crc = self.generate_crc(data) - actual_crc = self.read_crc(offset, length) - - if actual_crc != expected_crc: - raise Exception("Bad CRC, expected %d, found %d" % (expected_crc, actual_crc)) - LOG.info("CRC-8 matched: %d", actual_crc) - - def profile(self, duration): - LOG.info('Collecting %f second(s) worth of samples...', duration) - - # ensure DWT is enabled so we can get PC samples from PCSR - demcr_value = self._driver.read_memory_address(self.DEMCR_ADDR) - self._driver.write_memory_address(self.DEMCR_ADDR, demcr_value | self.DEMCR_TRCENA) - - # take the samples - samples = self._driver.continuous_read(self.PCSR_ADDR, duration) - - # restore the original DEMCR value - self._driver.write_memory_address(self.DEMCR_ADDR, demcr_value) - - # process the samples - pcs = dict() - for sample in samples: - sample = '0x%08x' % sample - pcs[sample] = pcs.get(sample, 0) + 1 - - LOG.info('Collected %d samples!', len(samples)) - return pcs - diff --git a/python_libs/pblprog/pebble/programmer/targets/stm32f4.py b/python_libs/pblprog/pebble/programmer/targets/stm32f4.py deleted file mode 100644 index 35b9fab434..0000000000 --- a/python_libs/pblprog/pebble/programmer/targets/stm32f4.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .stm32 import STM32FlashProgrammer - -class STM32F4FlashProgrammer(STM32FlashProgrammer): - IDCODE = 0x2BA01477 - CPUID_VALUE = 0x410FC241 - - FLASH_SECTOR_SIZES = [16, 16, 16, 16, 64, 128, 128, 128, 128, 128, 128, 128] diff --git a/python_libs/pblprog/pebble/programmer/targets/stm32f7.py b/python_libs/pblprog/pebble/programmer/targets/stm32f7.py deleted file mode 100644 index 4e50321025..0000000000 --- a/python_libs/pblprog/pebble/programmer/targets/stm32f7.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .stm32 import STM32FlashProgrammer - -class STM32F7FlashProgrammer(STM32FlashProgrammer): - IDCODE = 0x5BA02477 - CPUID_VALUE = 0x411FC270 - - FLASH_SECTOR_SIZES = [32, 32, 32, 32, 128, 256, 256, 256, 256, 256, 256, 256] diff --git a/python_libs/pblprog/pyproject.toml b/python_libs/pblprog/pyproject.toml deleted file mode 100644 index cbefcbc13a..0000000000 --- a/python_libs/pblprog/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[project] -name = "pebble.programmer" -version = "0.0.3" -description = "Pebble Programmer" -readme = "README.md" -requires-python = ">=3.9" -dependencies = [ - "intelhex>=2.1,<3", - "pyftdi", -] - -[project.entry-points."pebble.programmer"] -targets = "loader.bin" - -[project.scripts] -pblprog = "pebble.programmer.__main__:main" - -[tool.setuptools] -packages = ["pebble"] diff --git a/python_libs/pebble-commander/pebble/__init__.py b/python_libs/pebble-commander/pebble/__init__.py index 0dbb5a8558..6a8939f209 100644 --- a/python_libs/pebble-commander/pebble/__init__.py +++ b/python_libs/pebble-commander/pebble/__init__.py @@ -1,15 +1,3 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 -__import__('pkg_resources').declare_namespace(__name__) diff --git a/python_libs/pebble-commander/pebble/commander/__init__.py b/python_libs/pebble-commander/pebble/commander/__init__.py index 88faea2130..68e1ce08c3 100644 --- a/python_libs/pebble-commander/pebble/commander/__init__.py +++ b/python_libs/pebble-commander/pebble/commander/__init__.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from .commander import PebbleCommander from . import _commands diff --git a/python_libs/pebble-commander/pebble/commander/__main__.py b/python_libs/pebble-commander/pebble/commander/__main__.py index 2d1ab900de..c052c98cf5 100644 --- a/python_libs/pebble-commander/pebble/commander/__main__.py +++ b/python_libs/pebble-commander/pebble/commander/__main__.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import absolute_import @@ -20,6 +9,7 @@ from . import interactive + def main(args=None): def reattach_handler(logger, formatter, handler): if handler is not None: @@ -30,38 +20,59 @@ def reattach_handler(logger, formatter, handler): return handler if args is None: - parser = argparse.ArgumentParser(description='Pebble Commander.') - parser.add_argument('-v', '--verbose', help='verbose logging', action='count', - default=0) - parser.add_argument('-t', '--tty', help='serial port (defaults to auto-detect)', metavar='TTY', - default=None) - parser.add_argument('-c', '--pcap', metavar='FILE', default=None, - help='write packet capture to pcap file') - parser.add_argument('dict', help='log-hashing dictionary file', metavar='loghash_dict.json', - nargs='?', default=None) + parser = argparse.ArgumentParser(description="Pebble Commander.") + parser.add_argument( + "-v", "--verbose", help="verbose logging", action="count", default=0 + ) + parser.add_argument( + "-t", + "--tty", + help="serial port (defaults to auto-detect)", + metavar="TTY", + default=None, + ) + parser.add_argument( + "-c", + "--pcap", + metavar="FILE", + default=None, + help="write packet capture to pcap file", + ) + parser.add_argument( + "dict", + help="log-hashing dictionary file", + metavar="loghash_dict.json", + nargs="?", + default=None, + ) args = parser.parse_args() - log_level = (logging.DEBUG if args.verbose >= 2 - else logging.INFO if args.verbose >= 1 - else logging.WARNING) + log_level = ( + logging.DEBUG + if args.verbose >= 2 + else logging.INFO + if args.verbose >= 1 + else logging.WARNING + ) use_colors = True - formatter_string = '%(name)-12s: %(levelname)-8s %(message)s' + formatter_string = "%(name)-12s: %(levelname)-8s %(message)s" if use_colors: - formatter_string = '\x1b[33m%s\x1b[m' % formatter_string + formatter_string = "\x1b[33m%s\x1b[m" % formatter_string formatter = logging.Formatter(formatter_string) handler = reattach_handler(logging.getLogger(), formatter, None) logging.getLogger().setLevel(log_level) with interactive.InteractivePebbleCommander( - loghash_path=args.dict, tty=args.tty, capfile=args.pcap) as cmdr: + loghash_path=args.dict, tty=args.tty, capfile=args.pcap + ) as cmdr: cmdr.attach_prompt_toolkit() # Re-create the logging handler to use the patched stdout handler = reattach_handler(logging.getLogger(), formatter, handler) cmdr.command_loop() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/python_libs/pebble-commander/pebble/commander/_commands/__init__.py b/python_libs/pebble-commander/pebble/commander/_commands/__init__.py index fb79f94591..1a5c52cf9e 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/__init__.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/__init__.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from . import app from . import battery diff --git a/python_libs/pebble-commander/pebble/commander/_commands/app.py b/python_libs/pebble-commander/pebble/commander/_commands/app.py index ea77866207..f6cca1bd0c 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/app.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/app.py @@ -1,31 +1,18 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 -from .. import PebbleCommander, exceptions, parsers +from .. import PebbleCommander, exceptions @PebbleCommander.command() def app_list(cmdr): - """ List applications. - """ + """List applications.""" return cmdr.send_prompt_command("app list") @PebbleCommander.command() def app_load_metadata(cmdr): - """ Ghetto metadata loading for pbw_image.py - """ + """Ghetto metadata loading for pbw_image.py""" ret = cmdr.send_prompt_command("app load metadata") if not ret[0].startswith("OK"): raise exceptions.PromptResponseError(ret) @@ -33,11 +20,10 @@ def app_load_metadata(cmdr): @PebbleCommander.command() def app_launch(cmdr, idnum): - """ Launch an application. - """ + """Launch an application.""" idnum = int(str(idnum), 0) if idnum == 0: - raise exceptions.ParameterError('idnum out of range: %d' % idnum) + raise exceptions.ParameterError("idnum out of range: %d" % idnum) ret = cmdr.send_prompt_command("app launch %d" % idnum) if not ret[0].startswith("OK"): raise exceptions.PromptResponseError(ret) @@ -45,11 +31,10 @@ def app_launch(cmdr, idnum): @PebbleCommander.command() def app_remove(cmdr, idnum): - """ Remove an application. - """ + """Remove an application.""" idnum = int(str(idnum), 0) if idnum == 0: - raise exceptions.ParameterError('idnum out of range: %d' % idnum) + raise exceptions.ParameterError("idnum out of range: %d" % idnum) ret = cmdr.send_prompt_command("app remove %d" % idnum) if not ret[0].startswith("OK"): raise exceptions.PromptResponseError(ret) @@ -57,11 +42,10 @@ def app_remove(cmdr, idnum): @PebbleCommander.command() def app_resource_bank(cmdr, idnum=0): - """ Get resource bank info for an application. - """ + """Get resource bank info for an application.""" idnum = int(str(idnum), 0) if idnum < 0: - raise exceptions.ParameterError('idnum out of range: %d' % idnum) + raise exceptions.ParameterError("idnum out of range: %d" % idnum) ret = cmdr.send_prompt_command("resource bank info %d" % idnum) if not ret[0].startswith("OK "): raise exceptions.PromptResponseError(ret) @@ -70,16 +54,14 @@ def app_resource_bank(cmdr, idnum=0): @PebbleCommander.command() def app_next_id(cmdr): - """ Get next free application ID. - """ + """Get next free application ID.""" return cmdr.send_prompt_command("app next id") @PebbleCommander.command() def app_available(cmdr, idnum): - """ Check if an application is available. - """ + """Check if an application is available.""" idnum = int(str(idnum), 0) if idnum == 0: - raise exceptions.ParameterError('idnum out of range: %d' % idnum) + raise exceptions.ParameterError("idnum out of range: %d" % idnum) return cmdr.send_prompt_command("app available %d" % idnum) diff --git a/python_libs/pebble-commander/pebble/commander/_commands/battery.py b/python_libs/pebble-commander/pebble/commander/_commands/battery.py index 3f0cfa35f6..cf52784193 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/battery.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/battery.py @@ -1,24 +1,12 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from .. import PebbleCommander, exceptions, parsers @PebbleCommander.command() def battery_force_charge(cmdr, charging=True): - """ Force the device to believe it is or isn't charging. - """ + """Force the device to believe it is or isn't charging.""" if parsers.str2bool(charging): charging = "enable" else: @@ -30,6 +18,5 @@ def battery_force_charge(cmdr, charging=True): @PebbleCommander.command() def battery_status(cmdr): - """ Get current battery status. - """ + """Get current battery status.""" return cmdr.send_prompt_command("battery status") diff --git a/python_libs/pebble-commander/pebble/commander/_commands/bluetooth.py b/python_libs/pebble-commander/pebble/commander/_commands/bluetooth.py index 7c3838e354..1f90c3ed44 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/bluetooth.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/bluetooth.py @@ -1,23 +1,12 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from .. import PebbleCommander, exceptions, parsers @PebbleCommander.command() def bt_airplane_mode(cmdr, enter=True): - """ Enter or exit airplane mode. + """Enter or exit airplane mode. `enter` should either be a boolean, "enter", or "exit". """ @@ -33,8 +22,7 @@ def bt_airplane_mode(cmdr, enter=True): @PebbleCommander.command() def bt_prefs_wipe(cmdr): - """ Wipe bluetooth preferences. - """ + """Wipe bluetooth preferences.""" ret = cmdr.send_prompt_command("bt prefs wipe") if ret: raise exceptions.PromptResponseError(ret) @@ -42,18 +30,17 @@ def bt_prefs_wipe(cmdr): @PebbleCommander.command() def bt_mac(cmdr): - """ Get the bluetooth MAC address. - """ + """Get the bluetooth MAC address.""" ret = cmdr.send_prompt_command("bt mac") if not ret[0].startswith("0x"): raise exceptions.PromptResponseError(ret) retstr = ret[0][2:] - return [':'.join(retstr[i:i+2] for i in range(0, len(retstr), 2))] + return [":".join(retstr[i : i + 2] for i in range(0, len(retstr), 2))] @PebbleCommander.command() def bt_set_addr(cmdr, new_mac=None): - """ Set the bluetooth MAC address. + """Set the bluetooth MAC address. Don't specify `new_mac` to revert to default. `new_mac` should be of the normal 6 hex octets split with colons. @@ -61,7 +48,7 @@ def bt_set_addr(cmdr, new_mac=None): if not new_mac: new_mac = "00:00:00:00:00:00" mac = parsers.str2mac(new_mac) - macstr = ''.join(["%02X" % byte for byte in mac]) + macstr = "".join(["%02X" % byte for byte in mac]) ret = cmdr.send_prompt_command("bt set addr %s" % macstr) if ret[0] != new_mac: raise exceptions.PromptResponseError(ret) @@ -69,13 +56,12 @@ def bt_set_addr(cmdr, new_mac=None): @PebbleCommander.command() def bt_set_name(cmdr, new_name=None): - """ Set the bluetooth name. - """ + """Set the bluetooth name.""" if not new_name: new_name = "" # Note: the only reason for this is because prompt sucks # This can probably be removed when prompt goes away - if ' ' in new_name: + if " " in new_name: raise exceptions.ParameterError("bluetooth name must not have spaces") ret = cmdr.send_prompt_command("bt set name %s" % new_name) if ret: diff --git a/python_libs/pebble-commander/pebble/commander/_commands/clicks.py b/python_libs/pebble-commander/pebble/commander/_commands/clicks.py index 758ef5d76c..45e4ee2ec0 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/clicks.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/clicks.py @@ -1,27 +1,15 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .. import PebbleCommander, exceptions, parsers +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +from .. import PebbleCommander, exceptions @PebbleCommander.command() def click_short(cmdr, button): - """ Click a button. - """ + """Click a button.""" button = int(str(button), 0) if not 0 <= button <= 3: - raise exceptions.ParameterError('button out of range: %d' % button) + raise exceptions.ParameterError("button out of range: %d" % button) ret = cmdr.send_prompt_command("click short %d" % button) if not ret[0].startswith("OK"): raise exceptions.PromptResponseError(ret) @@ -29,7 +17,7 @@ def click_short(cmdr, button): @PebbleCommander.command() def click_long(cmdr, button, hold_ms=20): - """ Hold a button. + """Hold a button. `hold_ms` is how many ms to hold the button down before releasing. """ @@ -38,21 +26,23 @@ def click_long(cmdr, button, hold_ms=20): @PebbleCommander.command() def click_multiple(cmdr, button, count=1, hold_ms=20, delay_ms=0): - """ Rhythmically click a button. - """ + """Rhythmically click a button.""" button = int(str(button), 0) count = int(str(count), 0) hold_ms = int(str(hold_ms), 0) delay_ms = int(str(delay_ms), 0) if not 0 <= button <= 3: - raise exceptions.ParameterError('button out of range: %d' % button) + raise exceptions.ParameterError("button out of range: %d" % button) if not count > 0: - raise exceptions.ParameterError('count out of range: %d' % count) + raise exceptions.ParameterError("count out of range: %d" % count) if hold_ms < 0: - raise exceptions.ParameterError('hold_ms out of range: %d' % hold_ms) + raise exceptions.ParameterError("hold_ms out of range: %d" % hold_ms) if delay_ms < 0: - raise exceptions.ParameterError('delay_ms out of range: %d' % delay_ms) + raise exceptions.ParameterError("delay_ms out of range: %d" % delay_ms) ret = cmdr.send_prompt_command( - "click multiple {button:d} {count:d} {hold_ms:d} {delay_ms:d}".format(**locals())) + "click multiple {button:d} {count:d} {hold_ms:d} {delay_ms:d}".format( + **locals() + ) + ) if not ret[0].startswith("OK"): raise exceptions.PromptResponseError(ret) diff --git a/python_libs/pebble-commander/pebble/commander/_commands/flash.py b/python_libs/pebble-commander/pebble/commander/_commands/flash.py index cb6e836550..b227174f93 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/flash.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/flash.py @@ -1,33 +1,22 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .. import PebbleCommander, exceptions, parsers +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +from .. import PebbleCommander, exceptions # TODO: flash-write # Can't do it with pulse prompt :( + @PebbleCommander.command() def flash_erase(cmdr, address, length): - """ Erase flash area. - """ + """Erase flash area.""" address = int(str(address), 0) length = int(str(length), 0) if address < 0: - raise exceptions.ParameterError('address out of range: %d' % address) + raise exceptions.ParameterError("address out of range: %d" % address) if length <= 0: - raise exceptions.ParameterError('length out of range: %d' % length) + raise exceptions.ParameterError("length out of range: %d" % length) # TODO: I guess catch errors ret = cmdr.send_prompt_command("erase flash 0x%X %d" % (address, length)) if not ret[1].startswith("OK"): @@ -36,14 +25,13 @@ def flash_erase(cmdr, address, length): @PebbleCommander.command() def flash_crc(cmdr, address, length): - """ Calculate CRC of flash area. - """ + """Calculate CRC of flash area.""" address = int(str(address), 0) length = int(str(length), 0) if address < 0: - raise exceptions.ParameterError('address out of range: %d' % address) + raise exceptions.ParameterError("address out of range: %d" % address) if length <= 0: - raise exceptions.ParameterError('length out of range: %d' % length) + raise exceptions.ParameterError("length out of range: %d" % length) # TODO: I guess catch errors ret = cmdr.send_prompt_command("crc flash 0x%X %d" % (address, length)) if not ret[0].startswith("CRC: "): @@ -53,8 +41,7 @@ def flash_crc(cmdr, address, length): @PebbleCommander.command() def prf_address(cmdr): - """ Get address of PRF. - """ + """Get address of PRF.""" ret = cmdr.send_prompt_command("prf image address") if not ret[0].startswith("OK "): raise exceptions.PromptResponseError(ret) diff --git a/python_libs/pebble-commander/pebble/commander/_commands/help.py b/python_libs/pebble-commander/pebble/commander/_commands/help.py index 90ccdb82c4..bb154f34c8 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/help.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/help.py @@ -1,25 +1,13 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 import inspect -import sys -from .. import PebbleCommander, exceptions, parsers +from .. import PebbleCommander, exceptions def trim_docstring(var): - return inspect.getdoc(var) or '' + return inspect.getdoc(var) or "" def get_help_short(cmdr, cmd_name, help_output=None): @@ -47,7 +35,7 @@ def get_help_short(cmdr, cmd_name, help_output=None): else: output = cmdstr else: # Prompt command - if cmd_name[0] == '!': # Strip the bang if it's there + if cmd_name[0] == "!": # Strip the bang if it's there cmd_name = cmd_name[1:] # Get the output if it wasn't provided @@ -56,9 +44,9 @@ def get_help_short(cmdr, cmd_name, help_output=None): for prompt_cmd in help_output[1:]: # Match, even with argument count provided - if prompt_cmd == cmd_name or prompt_cmd.startswith(cmd_name+" "): + if prompt_cmd == cmd_name or prompt_cmd.startswith(cmd_name + " "): # Output should be the full argument string with the bang - output = '!' + prompt_cmd + output = "!" + prompt_cmd break return output @@ -84,13 +72,15 @@ def get_help_long(cmdr, cmd_name): if spec.defaults is not None: minargs = len(spec.args[1:]) - len(spec.defaults) if i >= minargs: - specstr.append(help_arginfo_default(arg, spec.defaults[i - minargs])) + specstr.append( + help_arginfo_default(arg, spec.defaults[i - minargs]) + ) else: specstr.append(help_arginfo_nodefault(arg)) else: specstr.append(help_arginfo_nodefault(arg)) - specstr = ' '.join(specstr) + specstr = " ".join(specstr) cmdstr = func.name + " " + specstr if func.__doc__ is None: output = "%s\n\nNo help available." % cmdstr @@ -107,7 +97,7 @@ def get_help_long(cmdr, cmd_name): @PebbleCommander.command() def help(cmdr, cmd=None): - """ Show help. + """Show help. You're lookin' at it, dummy! """ @@ -126,7 +116,7 @@ def help(cmdr, cmd=None): out.append("\n===Prompt commands===") ret = cmdr.send_prompt_command("help") - if ret[0] != 'Available Commands:': + if ret[0] != "Available Commands:": raise exceptions.PromptResponseError("'help' prompt command output invalid") for cmd_name in ret[1:]: out.append(get_help_short(cmdr, "!" + cmd_name, ret)) diff --git a/python_libs/pebble-commander/pebble/commander/_commands/imaging.py b/python_libs/pebble-commander/pebble/commander/_commands/imaging.py index f58a5fc680..2d8a3fdde1 100644 --- a/python_libs/pebble-commander/pebble/commander/_commands/imaging.py +++ b/python_libs/pebble-commander/pebble/commander/_commands/imaging.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import print_function @@ -23,20 +12,20 @@ import pebble.pulse2.exceptions -from .. import PebbleCommander, exceptions, parsers +from .. import PebbleCommander from ..util import stm32_crc class PebbleFirmwareBinaryInfo(object): V1_STRUCT_VERSION = 1 V1_STRUCT_DEFINTION = [ - ('20s', 'build_id'), - ('L', 'version_timestamp'), - ('32s', 'version_tag'), - ('8s', 'version_short'), - ('?', 'is_recovery_firmware'), - ('B', 'hw_platform'), - ('B', 'metadata_version') + ("20s", "build_id"), + ("L", "version_timestamp"), + ("32s", "version_tag"), + ("8s", "version_short"), + ("?", "is_recovery_firmware"), + ("B", "hw_platform"), + ("B", "metadata_version"), ] # The platforms which use a legacy defective crc32 LEGACY_CRC_PLATFORMS = [ @@ -52,17 +41,17 @@ class PebbleFirmwareBinaryInfo(object): 9, # SpaldingEVT 10, # BobbyDVT 11, # Spalding - 0xff, # OneBigboard - 0xfe, # OneBigboard2 - 0xfd, # SnowyBigboard - 0xfc, # SnowyBigboard2 - 0xfb, # SpaldingBigboard + 0xFF, # OneBigboard + 0xFE, # OneBigboard2 + 0xFD, # SnowyBigboard + 0xFC, # SnowyBigboard2 + 0xFB, # SpaldingBigboard ] def get_crc(self): _, ext = os.path.splitext(self.path) - assert ext == '.bin', 'Can only calculate crc for .bin files' - with open(self.path, 'rb') as f: + assert ext == ".bin", "Can only calculate crc for .bin files" + with open(self.path, "rb") as f: image = f.read() if self.hw_platform in self.LEGACY_CRC_PLATFORMS: # use the legacy defective crc @@ -72,27 +61,30 @@ def get_crc(self): return crc32(image) & 0xFFFFFFFF def _get_footer_struct(self): - fmt = '<' + reduce(lambda s, t: s + t[0], - PebbleFirmwareBinaryInfo.V1_STRUCT_DEFINTION, '') + fmt = "<" + reduce( + lambda s, t: s + t[0], PebbleFirmwareBinaryInfo.V1_STRUCT_DEFINTION, "" + ) return struct.Struct(fmt) def _get_footer_data_from_bin(self, path): - with open(path, 'rb') as f: + with open(path, "rb") as f: struct_size = self.struct.size f.seek(-struct_size, 2) footer_data = f.read() return footer_data def _parse_footer_data(self, footer_data): - z = zip(PebbleFirmwareBinaryInfo.V1_STRUCT_DEFINTION, - self.struct.unpack(footer_data)) + z = zip( + PebbleFirmwareBinaryInfo.V1_STRUCT_DEFINTION, + self.struct.unpack(footer_data), + ) return {entry[1]: data for entry, data in z} def __init__(self, bin_path): self.path = bin_path self.struct = self._get_footer_struct() _, ext = os.path.splitext(bin_path) - if ext != '.bin': + if ext != ".bin": raise ValueError('Unexpected extension. Must be ".bin"') footer_data = self._get_footer_data_from_bin(bin_path) self.info = self._parse_footer_data(footer_data) @@ -118,7 +110,7 @@ def __getattr__(self, name): # uint32_t firmware_length; # uint32_t checksum; # } FirmwareDescription; -FW_DESCR_FORMAT = ' 0) + assert mtu > 0 unsent = collections.deque() for offset in range(0, len(data), mtu): - segment = data[offset:offset+mtu] - assert(len(segment)) + segment = data[offset : offset + mtu] + assert len(segment) seg_address = address + offset - unsent.appendleft( - (seg_address, WriteCommand(seg_address, segment), 0)) + unsent.appendleft((seg_address, WriteCommand(seg_address, segment), 0)) in_flight = collections.OrderedDict() retries = 0 @@ -229,8 +199,7 @@ def write(self, address, data, max_retries=5, max_in_flight=5, try: while True: # Process ACKs (if any) - ack = WriteResponse.parse( - self.socket.receive(block=False)) + ack = WriteResponse.parse(self.socket.receive(block=False)) try: cmd, _, _ = in_flight[ack.address] del in_flight[ack.address] @@ -240,23 +209,25 @@ def write(self, address, data, max_retries=5, max_in_flight=5, if retry_count == 0: # ACK for a segment we never sent?! raise exceptions.WriteError( - 'Received ACK for an unsent segment: ' - '%#.08x' % ack.address) + "Received ACK for an unsent segment: " + "%#.08x" % ack.address + ) # Got an ACK for a sent (but timed out) segment unsent.remove((seg_address, cmd, retry_count)) break else: raise exceptions.WriteError( - 'Received ACK for an unknown segment: ' - '%#.08x' % ack.address) - + "Received ACK for an unknown segment: " + "%#.08x" % ack.address + ) if len(cmd.data) != ack.length: raise exceptions.WriteError( - 'ACK length %d != data length %d' % ( - ack.length, len(cmd.data))) - assert(ack.complete) + "ACK length %d != data length %d" + % (ack.length, len(cmd.data)) + ) + assert ack.complete if progress_cb: progress_cb(True) except pebble.pulse2.exceptions.ReceiveQueueEmpty: @@ -265,19 +236,19 @@ def write(self, address, data, max_retries=5, max_in_flight=5, # Retry any in_flight writes where the ACK has timed out to_retry = [] timeout_time = time.time() - 0.5 - for (seg_address, - (cmd, send_time, retry_count)) in in_flight.copy().items(): + for seg_address, (cmd, send_time, retry_count) in in_flight.copy().items(): if send_time > timeout_time: # in_flight is an OrderedDict so iteration is in # chronological order. break if retry_count >= max_retries: raise exceptions.WriteError( - 'Segment %#.08x exceeded the max retry count (%d)' % ( - seg_address, max_retries)) + "Segment %#.08x exceeded the max retry count (%d)" + % (seg_address, max_retries) + ) # Enqueue the packet again to resend later. del in_flight[seg_address] - unsent.appendleft((seg_address, cmd, retry_count+1)) + unsent.appendleft((seg_address, cmd, retry_count + 1)) retries += 1 if progress_cb: progress_cb(False) diff --git a/python_libs/pebble-commander/pebble/commander/apps/prompt.py b/python_libs/pebble-commander/pebble/commander/apps/prompt.py index 02227fde3e..7f460c65a2 100644 --- a/python_libs/pebble-commander/pebble/commander/apps/prompt.py +++ b/python_libs/pebble-commander/pebble/commander/apps/prompt.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import absolute_import @@ -24,11 +13,10 @@ class Prompt(object): - - PORT_NUMBER = 0x3e20 + PORT_NUMBER = 0x3E20 def __init__(self, link): - self.socket = link.open_socket('reliable', self.PORT_NUMBER) + self.socket = link.open_socket("reliable", self.PORT_NUMBER) def command_and_response(self, command_string, timeout=20): log = [] @@ -37,8 +25,7 @@ def command_and_response(self, command_string, timeout=20): is_done = False while not is_done: try: - response = PromptResponse.parse( - self.socket.receive(timeout=timeout)) + response = PromptResponse.parse(self.socket.receive(timeout=timeout)) if response.is_done_response: is_done = True elif response.is_message_response: @@ -51,13 +38,13 @@ def close(self): self.socket.close() -class PromptResponse(collections.namedtuple('PromptResponse', - 'response_type timestamp message')): - +class PromptResponse( + collections.namedtuple("PromptResponse", "response_type timestamp message") +): DONE_RESPONSE = 101 MESSAGE_RESPONSE = 102 - response_struct = struct.Struct(' {self.message}') + template = ( + "{self.log_level} {self.task} {msec_timestamp} " + "{self.file_name}:{self.line_number}> {self.message}" + ) return template.format(self=self, msec_timestamp=msec_timestamp) @classmethod def parse(cls, packet): - result = cls.response_struct.unpack(packet[:cls.response_struct.size]) - msg = packet[cls.response_struct.size:].decode("utf8", errors="ignore") + result = cls.response_struct.unpack(packet[: cls.response_struct.size]) + msg = packet[cls.response_struct.size :].decode("utf8", errors="ignore") log_level = result[2].decode("utf8") task = result[3].decode("utf8") timestamp = datetime.fromtimestamp(result[4] / 1000.0) - file_name = result[1].split(b'\x00', 1)[0].decode("utf8") # NUL terminated + file_name = result[1].split(b"\x00", 1)[0].decode("utf8") # NUL terminated line_number = result[5] return cls(log_level, task, timestamp, file_name, line_number, msg) class StreamingLogs(object): - '''App for receiving log messages streamed by the firmware. - ''' + """App for receiving log messages streamed by the firmware.""" PORT_NUMBER = 0x0003 def __init__(self, interface): try: - self.socket = interface.simplex_transport.open_socket( - self.PORT_NUMBER) + self.socket = interface.simplex_transport.open_socket(self.PORT_NUMBER) except AttributeError: - raise TypeError('LoggingApp must be bound directly ' - 'to an Interface, not a Link') + raise TypeError( + "LoggingApp must be bound directly to an Interface, not a Link" + ) def receive(self, block=True, timeout=None): return LogMessage.parse(self.socket.receive(block, timeout)) diff --git a/python_libs/pebble-commander/pebble/commander/commander.py b/python_libs/pebble-commander/pebble/commander/commander.py index 2613de86fe..57887cac4f 100644 --- a/python_libs/pebble-commander/pebble/commander/commander.py +++ b/python_libs/pebble-commander/pebble/commander/commander.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import absolute_import @@ -25,13 +14,13 @@ class Pulse2ConnectionAdapter(object): - '''An adapter for the pulse2 API to look enough like pulse.Connection + """An adapter for the pulse2 API to look enough like pulse.Connection to make PebbleCommander work...ish. Prompt will break spectacularly if the firmware reboots or the link state otherwise changes. Commander itself needs to be modified to be link-state aware. - ''' + """ def __init__(self, interface): self.interface = interface @@ -45,14 +34,15 @@ def close(self): class PebbleCommander(object): - """ Pebble Commander. - Implements everything for interfacing with PULSE things. + """Pebble Commander. + Implements everything for interfacing with PULSE things. """ def __init__(self, tty=None, interactive=False, capfile=None): if capfile is not None: interface = pulse2.Interface.open_dbgserial( - url=tty, capture_stream=open(capfile, 'wb')) + url=tty, capture_stream=open(capfile, "wb") + ) else: interface = pulse2.Interface.open_dbgserial(url=tty) @@ -80,20 +70,21 @@ def __exit__(self, exc_type, exc_value, traceback): @classmethod def command(cls, name=None): - """ Registers a command. - `name` is the command name. If `name` is unspecified, name will be the function name - with underscores converted to hyphens. + """Registers a command. + `name` is the command name. If `name` is unspecified, name will be the function name + with underscores converted to hyphens. - The convention for `name` is to separate words with a hyphen. The function name - will be the same as `name` with hyphens replaced with underscores. - Example: `click-short` will result in a PebbleCommander.click_short function existing. + The convention for `name` is to separate words with a hyphen. The function name + will be the same as `name` with hyphens replaced with underscores. + Example: `click-short` will result in a PebbleCommander.click_short function existing. - `fn` should return an array of strings (or None), and take the current - `PebbleCommander` as the first argument, and the rest of the argument strings - as subsequent arguments. For errors, `fn` should throw an exception. + `fn` should return an array of strings (or None), and take the current + `PebbleCommander` as the first argument, and the rest of the argument strings + as subsequent arguments. For errors, `fn` should throw an exception. - # TODO: Probably make the return something structured instead of stringly typed. + # TODO: Probably make the return something structured instead of stringly typed. """ + def decorator(fn): # Story time: # Things are fine as long as you only read from `name`, but assigning to `name` @@ -103,26 +94,28 @@ def decorator(fn): # `name_` in the `decorator` scope cmdname = name if not cmdname: - cmdname = fn.__name__.replace('_', '-') - funcname = cmdname.replace('-', '_') - if not re.match(tokenize.Name + '$', funcname): + cmdname = fn.__name__.replace("_", "-") + funcname = cmdname.replace("-", "_") + if not re.match(tokenize.Name + "$", funcname): raise ValueError("command name %s isn't a valid name" % funcname) if hasattr(cls, funcname): - raise ValueError('function name %s clashes with existing attribute' % funcname) + raise ValueError( + "function name %s clashes with existing attribute" % funcname + ) fn.is_command = True fn.name = cmdname method = types.MethodType(fn, cls) setattr(cls, funcname, method) return fn + return decorator def close(self): self.connection.close() def _start_logging(self): - """ Thread to handle logging messages. - """ + """Thread to handle logging messages.""" while True: try: msg = self.connection.logging.receive() @@ -137,27 +130,26 @@ def _start_logging(self): pass def attach_log_listener(self, listener): - """ Attaches a listener for log messages. - Function takes message and returns are ignored. + """Attaches a listener for log messages. + Function takes message and returns are ignored. """ with self.log_listeners_lock: self.log_listeners.append(listener) def detach_log_listener(self, listener): - """ Removes a listener that was added with `attach_log_listener` - """ + """Removes a listener that was added with `attach_log_listener`""" with self.log_listeners_lock: self.log_listeners.remove(listener) def send_prompt_command(self, cmd): - """ Send a prompt command string. - Unfortunately this is indeed stringly typed, a better solution is necessary. + """Send a prompt command string. + Unfortunately this is indeed stringly typed, a better solution is necessary. """ return self.connection.prompt.command_and_response(cmd) def get_command(self, command): try: - fn = getattr(self, command.replace('-', '_')) + fn = getattr(self, command.replace("-", "_")) if fn.is_command: return fn except AttributeError: diff --git a/python_libs/pebble-commander/pebble/commander/exceptions.py b/python_libs/pebble-commander/pebble/commander/exceptions.py index 095732c2ce..56ce7a6b0c 100644 --- a/python_libs/pebble-commander/pebble/commander/exceptions.py +++ b/python_libs/pebble-commander/pebble/commander/exceptions.py @@ -1,16 +1,6 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + class PebbleCommanderError(Exception): pass diff --git a/python_libs/pebble-commander/pebble/commander/interactive.py b/python_libs/pebble-commander/pebble/commander/interactive.py index bcfa435f2c..82f83dbc7c 100644 --- a/python_libs/pebble-commander/pebble/commander/interactive.py +++ b/python_libs/pebble-commander/pebble/commander/interactive.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import absolute_import @@ -24,9 +13,10 @@ class InteractivePebbleCommander(object): - """ Interactive Pebble Commander. - Most/all UI implementations should either use this directly or sub-class it. + """Interactive Pebble Commander. + Most/all UI implementations should either use this directly or sub-class it. """ + def __init__(self, loghash_path=None, tty=None, capfile=None): self.cmdr = PebbleCommander(tty=tty, interactive=True, capfile=capfile) if loghash_path is None: @@ -50,30 +40,31 @@ def close(self): pass def attach_prompt_toolkit(self): - """ Attaches prompt_toolkit things - """ + """Attaches prompt_toolkit things""" self.history = prompt_toolkit.history.InMemoryHistory() self.cli = prompt_toolkit.CommandLineInterface( - application=prompt_toolkit.shortcuts.create_prompt_application(u"> ", - history=self.history), - eventloop=prompt_toolkit.shortcuts.create_eventloop()) + application=prompt_toolkit.shortcuts.create_prompt_application( + "> ", history=self.history + ), + eventloop=prompt_toolkit.shortcuts.create_eventloop(), + ) self.patch_context = self.cli.patch_stdout_context(raw=True) self.patch_context.__enter__() def log_listener(self, msg): - """ This is called on every incoming log message. - `msg` is the raw log message class, without any dehashing. + """This is called on every incoming log message. + `msg` is the raw log message class, without any dehashing. - Subclasses should override this probably. + Subclasses should override this probably. """ line_dict = self.dehasher.dehash(msg) line = self.dehasher.commander_format_line(line_dict) - print line + print(line) def dispatch_command(self, string): - """ Dispatches a command string. + """Dispatches a command string. - Subclasses should not override this. + Subclasses should not override this. """ args = shlex.split(string) # Starting with '!' passes the rest of the line directly to prompt. @@ -88,11 +79,11 @@ def dispatch_command(self, string): return self.cmdr.send_prompt_command(string) def input_handle(self, string): - """ Handles an input line. - Generally the flow is to handle any UI-specific commands, then pass on to - dispatch_command. + """Handles an input line. + Generally the flow is to handle any UI-specific commands, then pass on to + dispatch_command. - Subclasses should override this probably. + Subclasses should override this probably. """ # Handle "quit" strings if string in ["exit", "q", "quit"]: @@ -101,19 +92,19 @@ def input_handle(self, string): try: resp = self.dispatch_command(string) if resp is not None: - print "\x1b[1m" + '\n'.join(resp) + "\x1b[m" + print("\x1b[1m" + "\n".join(resp) + "\x1b[m") except: - print "An error occurred!" + print("An error occurred!") traceback.print_exc() return True def get_command(self): - """ Get a command input line. - If there is no line, return an empty string or None. - This may block. + """Get a command input line. + If there is no line, return an empty string or None. + This may block. - Subclasses should override this probably. + Subclasses should override this probably. """ if self.cli is None: self.attach_prompt_toolkit() @@ -124,9 +115,9 @@ def get_command(self): return None def command_loop(self): - """ The main command loop. + """The main command loop. - Subclasses could override this, but it's probably not useful to do. + Subclasses could override this, but it's probably not useful to do. """ while True: try: diff --git a/python_libs/pebble-commander/pebble/commander/parsers.py b/python_libs/pebble-commander/pebble/commander/parsers.py index c4b02cb32a..dd77a090bf 100644 --- a/python_libs/pebble-commander/pebble/commander/parsers.py +++ b/python_libs/pebble-commander/pebble/commander/parsers.py @@ -1,16 +1,5 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import absolute_import @@ -30,9 +19,9 @@ def str2bool(s, also_true=[], also_false=[]): def str2mac(s): s = str(s) - if not re.match(r'[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}', s): - raise exceptions.ParameterError('%s is not a valid MAC address' % s) + if not re.match(r"[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}", s): + raise exceptions.ParameterError("%s is not a valid MAC address" % s) mac = [] - for byte in str(s).split(':'): + for byte in str(s).split(":"): mac.append(int(byte, 16)) return tuple(mac) diff --git a/python_libs/pebble-commander/pebble/commander/util/__init__.py b/python_libs/pebble-commander/pebble/commander/util/__init__.py index c38dc3b14a..6a8939f209 100644 --- a/python_libs/pebble-commander/pebble/commander/util/__init__.py +++ b/python_libs/pebble-commander/pebble/commander/util/__init__.py @@ -1,14 +1,3 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 diff --git a/python_libs/pebble-commander/pebble/commander/util/stm32_crc.py b/python_libs/pebble-commander/pebble/commander/util/stm32_crc.py index e3d15902f4..6dab74ffd8 100644 --- a/python_libs/pebble-commander/pebble/commander/util/stm32_crc.py +++ b/python_libs/pebble-commander/pebble/commander/util/stm32_crc.py @@ -1,19 +1,9 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 CRC_POLY = 0x04C11DB7 + def precompute_table(bits): lookup_table = [] for i in range(2**bits): @@ -23,22 +13,25 @@ def precompute_table(bits): rr = (rr << 1) ^ CRC_POLY else: rr <<= 1 - lookup_table.append(rr & 0xffffffff) + lookup_table.append(rr & 0xFFFFFFFF) return lookup_table + lookup_table = precompute_table(8) -def process_word(data, crc=0xffffffff): - if (len(data) < 4): + +def process_word(data, crc=0xFFFFFFFF): + if len(data) < 4: # The CRC data is "padded" in a very unique and confusing fashion. - data = data[::-1] + b'\0' * (4 - len(data)) + data = data[::-1] + b"\0" * (4 - len(data)) for char in reversed(data): b = char - crc = ((crc << 8) ^ lookup_table[(crc >> 24) ^ b]) & 0xffffffff + crc = ((crc << 8) ^ lookup_table[(crc >> 24) ^ b]) & 0xFFFFFFFF return crc -def process_buffer(buf, c=0xffffffff): + +def process_buffer(buf, c=0xFFFFFFFF): word_count = (len(buf) + 3) // 4 crc = c @@ -46,16 +39,18 @@ def process_buffer(buf, c=0xffffffff): crc = process_word(buf[i * 4 : (i + 1) * 4], crc) return crc + def crc32(data): return process_buffer(data) -if __name__ == '__main__': + +if __name__ == "__main__": import sys - assert(0x89f3bab2 == process_buffer("123 567 901 34")) - assert(0xaff19057 == process_buffer("123456789")) - assert(0x519b130 == process_buffer("\xfe\xff\xfe\xff")) - assert(0x495e02ca == process_buffer("\xfe\xff\xfe\xff\x88")) + assert 0x89F3BAB2 == process_buffer("123 567 901 34") + assert 0xAFF19057 == process_buffer("123456789") + assert 0x519B130 == process_buffer("\xfe\xff\xfe\xff") + assert 0x495E02CA == process_buffer("\xfe\xff\xfe\xff\x88") print("All tests passed!") diff --git a/python_libs/pebble-loghash/pebble/__init__.py b/python_libs/pebble-loghash/pebble/__init__.py index b9b3cbb3cd..afd37d7094 100644 --- a/python_libs/pebble-loghash/pebble/__init__.py +++ b/python_libs/pebble-loghash/pebble/__init__.py @@ -1,18 +1,3 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 -""" -Namespace module -""" -__import__('pkg_resources').declare_namespace(__name__) diff --git a/python_libs/pebble-loghash/pebble/loghashing/__init__.py b/python_libs/pebble-loghash/pebble/loghashing/__init__.py index 36a1e8d75f..afd37d7094 100644 --- a/python_libs/pebble-loghash/pebble/loghashing/__init__.py +++ b/python_libs/pebble-loghash/pebble/loghashing/__init__.py @@ -1,14 +1,3 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 diff --git a/python_libs/pebble-loghash/pebble/loghashing/constants.py b/python_libs/pebble-loghash/pebble/loghashing/constants.py index 3b7eb0f8e6..765b6045b1 100644 --- a/python_libs/pebble-loghash/pebble/loghashing/constants.py +++ b/python_libs/pebble-loghash/pebble/loghashing/constants.py @@ -1,18 +1,7 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 -#/usr/bin/env python +# /usr/bin/env python """ Constants used in this module """ @@ -23,20 +12,28 @@ HASH_MASK = 0x00FFFFFF # Regular Expressions -LOG_LINE_CONSOLE_REGEX = r"^(?P.)\s+(?P.)\s+(?P

    Geometry

    -//! * The action bar is 30 pixels wide. Use the \ref ACTION_BAR_WIDTH define. +//! * The action bar's width varies per platform. 30px on most displays, 34px on Emery and +//! Gabbro, and 40px on Chalk. Use the \ref ACTION_BAR_WIDTH define. //! * Icons should not be wider than 28 pixels, or taller than 18 pixels. //! It is recommended to use a size of around 15 x 15 pixels for the "visual core" of the icon, //! and extending or contracting where needed. @@ -103,7 +91,9 @@ /*basalt*/ 30, \ /*chalk*/ 40, \ /*diorite*/ 30, \ - /*emery*/ 34) + /*emery*/ 34, \ + /*flint*/ 30, \ + /*gabbro*/ 34) //! The width of the action bar in pixels. #define ACTION_BAR_WIDTH _ACTION_BAR_WIDTH(PBL_PLATFORM_TYPE_CURRENT) diff --git a/src/fw/applib/ui/action_button.c b/src/fw/applib/ui/action_button.c index 515db55531..468a641893 100644 --- a/src/fw/applib/ui/action_button.c +++ b/src/fw/applib/ui/action_button.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "action_button.h" diff --git a/src/fw/applib/ui/action_button.h b/src/fw/applib/ui/action_button.h index 877a7b6a07..fb15c4ba64 100644 --- a/src/fw/applib/ui/action_button.h +++ b/src/fw/applib/ui/action_button.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/action_menu_hierarchy.c b/src/fw/applib/ui/action_menu_hierarchy.c index b1420f65b9..cc83f67705 100644 --- a/src/fw/applib/ui/action_menu_hierarchy.c +++ b/src/fw/applib/ui/action_menu_hierarchy.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "action_menu_hierarchy.h" #include "action_menu_window_private.h" diff --git a/src/fw/applib/ui/action_menu_hierarchy.h b/src/fw/applib/ui/action_menu_hierarchy.h index 0adea0d851..953459550a 100644 --- a/src/fw/applib/ui/action_menu_hierarchy.h +++ b/src/fw/applib/ui/action_menu_hierarchy.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/action_menu_layer.c b/src/fw/applib/ui/action_menu_layer.c index 9b8efeb171..9d9eeba621 100644 --- a/src/fw/applib/ui/action_menu_layer.c +++ b/src/fw/applib/ui/action_menu_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "action_menu_layer.h" #include "action_menu_window_private.h" @@ -36,8 +23,12 @@ #define INDICATOR "»" +#if !PBL_ROUND || (!defined(CONFIG_RECOVERY_FW) && CONFIG_SCREEN_COLOR_DEPTH_BITS == 8) static const int VERTICAL_PADDING = PBL_IF_COLOR_ELSE(2, 4); +#endif +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 static const int EXTRA_PADDING_1_BIT = 2; +#endif static const int SHORT_COL_COUNT = 3; static const int MAX_NUM_VISIBLE_LINES = 2; #if PBL_ROUND @@ -87,7 +78,7 @@ static void prv_cell_column_draw(GContext *ctx, struct Layer const *cell_layer, if (sel_idx == i) { graphics_context_set_text_color(ctx, PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)); -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 // We only want to have a background on non-color platforms, while leaving this in with // a PBL_IF_COLOR_ELSE makes this a no-op, we'll save some cycles and code space just // skipping it. @@ -140,14 +131,11 @@ static int16_t prv_get_item_line_height(ActionMenuLayer *aml, int idx) { // Tintin has a rounded rectangle highlight box = grect_inset_internal(box, PBL_IF_COLOR_ELSE(inset, 2 * inset), 0); - // We offset the text 5 pixels from the left of the cell. If the indicator is - // present, the indicator also will be offset, so we add 5 pixels more spacing - // between the text and the indicator. This extra padding isn't needed for round. - const int nudge = PBL_IF_ROUND_ELSE(0, menu_cell_basic_horizontal_inset()); GContext *ctx = graphics_context_get_current_context(); // On rectangular displays, if the indicator is present, the indicator also will be offset, // so we add another nudge between the text and the indicator. #if PBL_RECT + const int nudge = menu_cell_basic_horizontal_inset(); if (!item->is_leaf) { const GSize indicator_size = graphics_text_layout_get_max_used_size(ctx, INDICATOR, font, box, @@ -203,7 +191,7 @@ static void prv_animate_cell(ActionMenuLayer *aml, GRect *label_text_frame, bool const int16_t item_height = aml->layout_cache.item_heights[aml->selected_index]; const int16_t line_height = fonts_get_font_height(aml->layout_cache.font); -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 // We need to force it to scroll a little extra for 1 bit label_text_frame->origin.y -= EXTRA_PADDING_1_BIT; #endif @@ -282,6 +270,7 @@ static bool prv_should_center(ActionMenuLayer *aml) { return false; } +#if PBL_RECT static void prv_cell_item_content_draw_rect(GContext *ctx, const Layer *cell_layer, const ActionMenuLayer *aml, const ActionMenuItem *item, bool selected, GRect *content_box) { @@ -303,7 +292,7 @@ static void prv_cell_item_content_draw_rect(GContext *ctx, const Layer *cell_lay content_box->size.w -= horizontal_padding; } -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 // Fill in the background layer. This effectively does nothing on watches where we have the // ability to draw with color, but on others, it will render a background behind the selected // cell. @@ -334,6 +323,7 @@ static void prv_cell_item_content_draw_rect(GContext *ctx, const Layer *cell_lay // Restore the cell layer's bounds mutable_cell_layer->bounds = saved_bounds; } +#endif #if PBL_ROUND static void prv_cell_item_content_draw_round(GContext *ctx, const Layer *cell_layer, @@ -413,7 +403,7 @@ static void prv_cell_item_draw(GContext *ctx, const Layer *cell_layer, // layer box. if (selected) { prv_animate_cell(aml, &label_text_frame, &draw_top_shading, &draw_bottom_shading); -#if !defined(RECOVERY_FW) && SCREEN_COLOR_DEPTH_BITS == 8 +#if !defined(CONFIG_RECOVERY_FW) && CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // Replace the clip box with a clip box that will render the item in the right place with the // right size, without menu layer's selection clipping. Menu layer will responsible for cleaning // up the changes made to this clip box. @@ -439,7 +429,7 @@ static void prv_cell_item_draw(GContext *ctx, const Layer *cell_layer, prv_cell_item_content_draw_round)(ctx, cell_layer, aml, item, selected, &label_text_frame); -#if !defined(RECOVERY_FW) && SCREEN_COLOR_DEPTH_BITS == 8 +#if !defined(CONFIG_RECOVERY_FW) && CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 const int16_t fade_height = 10; graphics_context_set_compositing_mode(ctx, GCompOpSet); if (draw_top_shading) { @@ -529,7 +519,7 @@ static int16_t prv_get_cell_padding(ActionMenuLayer *aml) { #if PBL_ROUND // when showing columns, set cells further apart return prv_aml_is_short(aml) ? default_sep_height : 1; -#elif SCREEN_COLOR_DEPTH_BITS == 1 +#elif CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 return default_sep_height; #else const int16_t line_height = fonts_get_font_height(aml->layout_cache.font); @@ -598,11 +588,10 @@ static void prv_draw_separator_cb(GContext *ctx, const Layer *cell_layer, // If this index is the seperator index, we want to draw the separator line // in the vertical center of the separator const int16_t nudge_down = PBL_IF_RECT_ELSE(3, 0); - const int16_t nudge_right = menu_cell_basic_horizontal_inset() + 1; const int16_t separator_width = config->separator.w; const GRect *cell_layer_bounds = &cell_layer->bounds; - const int16_t offset_x = PBL_IF_RECT_ELSE(nudge_right, + const int16_t offset_x = PBL_IF_RECT_ELSE(menu_cell_basic_horizontal_inset() + 1, (cell_layer->bounds.size.w - separator_width) / 2); const int16_t offset_y = (cell_layer_bounds->size.h / 2) + nudge_down; GPoint separator_start_point = gpoint_add(cell_layer_bounds->origin, @@ -783,7 +772,7 @@ void action_menu_layer_init(ActionMenuLayer *aml, const GRect *frame) { .selection_changed = prv_selection_changed_cb }); -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) gbitmap_init_with_resource_system(&aml->item_animation.fade_top, SYSTEM_APP, RESOURCE_ID_ACTION_MENU_FADE_TOP); gbitmap_init_with_resource_system(&aml->item_animation.fade_bottom, SYSTEM_APP, @@ -802,7 +791,7 @@ void action_menu_layer_deinit(ActionMenuLayer *aml) { prv_unschedule_item_animation(aml); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW gbitmap_deinit(&aml->item_animation.fade_top); gbitmap_deinit(&aml->item_animation.fade_bottom); #endif diff --git a/src/fw/applib/ui/action_menu_layer.h b/src/fw/applib/ui/action_menu_layer.h index 85f4a2eefb..b0db64b0bf 100644 --- a/src/fw/applib/ui/action_menu_layer.h +++ b/src/fw/applib/ui/action_menu_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/action_menu_window.c b/src/fw/applib/ui/action_menu_window.c index 9675f3986f..f514f8350e 100644 --- a/src/fw/applib/ui/action_menu_window.c +++ b/src/fw/applib/ui/action_menu_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "action_menu_window.h" #include "action_menu_window_private.h" @@ -22,8 +9,8 @@ #include "applib/ui/window.h" #include "applib/ui/window_stack.h" #include "process_state/app_state/app_state.h" -#include "services/normal/timeline/timeline.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/i18n/i18n.h" #define ACTION_MENU_DEFAULT_BACKGROUND_COLOR GColorWhite @@ -131,16 +118,12 @@ static Animation* prv_create_content_in_animation(ActionMenuData *data, Animation *content_in = property_animation_get_animation(prop_anim); animation_set_duration(content_in, IN_OUT_ANIMATION_DURATION); -#if !defined(PLATFORM_TINTIN) // animate the dots Animation *crumbs_anim = crumbs_layer_get_animation(&data->crumbs_layer); animation_set_duration(crumbs_anim, IN_OUT_ANIMATION_DURATION); // combine the two Animation *spawn_anim = animation_spawn_create(content_in, crumbs_anim, NULL); return spawn_anim; -#else - return content_in; -#endif } static Animation* prv_create_content_out_animation(ActionMenuData *data, @@ -326,7 +309,7 @@ void action_menu_set_align(ActionMenuConfig *config, ActionMenuAlign align) { ActionMenu *action_menu_open(WindowStack *window_stack, ActionMenuConfig *config) { ActionMenuData *data = applib_type_zalloc(ActionMenuData); data->config = *config; -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // Apply defaults if client didn't assign foreground/background colors if (gcolor_is_invisible(data->config.colors.background)) { data->config.colors.background = ACTION_MENU_DEFAULT_BACKGROUND_COLOR; diff --git a/src/fw/applib/ui/action_menu_window.h b/src/fw/applib/ui/action_menu_window.h index 60f233076e..8d14c0aef7 100644 --- a/src/fw/applib/ui/action_menu_window.h +++ b/src/fw/applib/ui/action_menu_window.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/ui/window.h" #include "applib/ui/window_stack.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" //! @file action_menu_window.h //! @addtogroup UI diff --git a/src/fw/applib/ui/action_menu_window_private.h b/src/fw/applib/ui/action_menu_window_private.h index 43c0265b62..47f5eea749 100644 --- a/src/fw/applib/ui/action_menu_window_private.h +++ b/src/fw/applib/ui/action_menu_window_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/action_toggle.c b/src/fw/applib/ui/action_toggle.c index 8edb61df72..633740f272 100644 --- a/src/fw/applib/ui/action_toggle.c +++ b/src/fw/applib/ui/action_toggle.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "action_toggle.h" @@ -27,7 +14,7 @@ #include "kernel/ui/modals/modal_manager.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "system/passert.h" typedef struct ActionToggleDialogConfig { @@ -171,13 +158,7 @@ static void prv_push_prompt_dialog(ActionToggleContext *ctx) { static bool prv_should_prompt(const ActionToggleConfig *config) { switch (config->prompt) { case ActionTogglePrompt_Auto: -#if PLATFORM_SPALDING - return ((pebble_task_get_current() == PebbleTask_App) && - (app_launch_reason() == APP_LAUNCH_QUICK_LAUNCH) && - (app_launch_button() == BUTTON_ID_BACK)); -#else return false; -#endif case ActionTogglePrompt_NoPrompt: return false; case ActionTogglePrompt_Prompt: diff --git a/src/fw/applib/ui/action_toggle.h b/src/fw/applib/ui/action_toggle.h index 36bb7c4298..db08b0181a 100644 --- a/src/fw/applib/ui/action_toggle.h +++ b/src/fw/applib/ui/action_toggle.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/animation.c b/src/fw/applib/ui/animation.c index ca79263bc2..1fbd25066a 100644 --- a/src/fw/applib/ui/animation.c +++ b/src/fw/applib/ui/animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "animation_private.h" @@ -30,7 +17,7 @@ #include "kernel/kernel_applib_state.h" #include "kernel/memory_layout.h" -#include "services/common/animation_service.h" +#include "pbl/services/animation_service.h" #include "system/passert.h" #include "util/math.h" @@ -1064,7 +1051,7 @@ void animation_private_state_init(AnimationState *state) { #endif // If this a legacy 2.0 application, instantiate the 2.0 legacy animation support -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW if (process_manager_compiled_with_legacy2_sdk()) { animation_legacy2_private_init_scheduler((AnimationLegacy2Scheduler *)state); return; diff --git a/src/fw/applib/ui/animation.h b/src/fw/applib/ui/animation.h index f2bbf8224b..5696a19997 100644 --- a/src/fw/applib/ui/animation.h +++ b/src/fw/applib/ui/animation.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/src/fw/applib/ui/animation_interpolate.c b/src/fw/applib/ui/animation_interpolate.c index 8fcacdae35..47fca18eda 100644 --- a/src/fw/applib/ui/animation_interpolate.c +++ b/src/fw/applib/ui/animation_interpolate.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "animation.h" #include "animation_interpolate.h" diff --git a/src/fw/applib/ui/animation_interpolate.h b/src/fw/applib/ui/animation_interpolate.h index c007566801..90135a8f3d 100644 --- a/src/fw/applib/ui/animation_interpolate.h +++ b/src/fw/applib/ui/animation_interpolate.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/animation_private.h b/src/fw/applib/ui/animation_private.h index dfdfaef2e5..76dc6a608d 100644 --- a/src/fw/applib/ui/animation_private.h +++ b/src/fw/applib/ui/animation_private.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "animation.h" #define ANIMATION_LOG_DEBUG(fmt, args...) \ - PBL_LOG_D(LOG_DOMAIN_ANIMATION, LOG_LEVEL_DEBUG, fmt, ## args) + PBL_LOG_D_DBG(LOG_DOMAIN_ANIMATION, fmt, ## args) #define ANIMATION_MAX_CHILDREN 256 #define ANIMATION_PLAY_COUNT_INFINITE_STORED ((uint16_t)~0) diff --git a/src/fw/applib/ui/animation_timing.c b/src/fw/applib/ui/animation_timing.c index 7011efef23..b31d5967f6 100644 --- a/src/fw/applib/ui/animation_timing.c +++ b/src/fw/applib/ui/animation_timing.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "animation_timing.h" #include "animation_interpolate.h" diff --git a/src/fw/applib/ui/animation_timing.h b/src/fw/applib/ui/animation_timing.h index 7bb11f719a..f0a9c41c36 100644 --- a/src/fw/applib/ui/animation_timing.h +++ b/src/fw/applib/ui/animation_timing.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/app_window_click_glue.c b/src/fw/applib/ui/app_window_click_glue.c index 84669eb6d1..20f5f0072e 100644 --- a/src/fw/applib/ui/app_window_click_glue.c +++ b/src/fw/applib/ui/app_window_click_glue.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_window_click_glue.h" diff --git a/src/fw/applib/ui/app_window_click_glue.h b/src/fw/applib/ui/app_window_click_glue.h index 3448fc5041..a980a44d38 100644 --- a/src/fw/applib/ui/app_window_click_glue.h +++ b/src/fw/applib/ui/app_window_click_glue.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/app_window_stack.c b/src/fw/applib/ui/app_window_stack.c index 5999c38adc..5a5a0152e5 100644 --- a/src/fw/applib/ui/app_window_stack.c +++ b/src/fw/applib/ui/app_window_stack.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_window_stack.h" @@ -32,7 +19,7 @@ #include "semphr.h" void app_window_stack_push(Window *window, bool animated) { - PBL_LOG(LOG_LEVEL_DEBUG, "Pushing window %p onto app window stack %p", + PBL_LOG_DBG("Pushing window %p onto app window stack %p", window, app_state_get_window_stack()); window_stack_push(app_state_get_window_stack(), window, animated); } diff --git a/src/fw/applib/ui/app_window_stack.h b/src/fw/applib/ui/app_window_stack.h index 65724cfc4c..3ed4e06211 100644 --- a/src/fw/applib/ui/app_window_stack.h +++ b/src/fw/applib/ui/app_window_stack.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/bitmap_layer.c b/src/fw/applib/ui/bitmap_layer.c index 5c86db7201..28720d76b2 100644 --- a/src/fw/applib/ui/bitmap_layer.c +++ b/src/fw/applib/ui/bitmap_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bitmap_layer.h" diff --git a/src/fw/applib/ui/bitmap_layer.h b/src/fw/applib/ui/bitmap_layer.h index b50f2f9eeb..98df69e0b3 100644 --- a/src/fw/applib/ui/bitmap_layer.h +++ b/src/fw/applib/ui/bitmap_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file ui/bitmap_layer.h //! @@ -136,6 +123,7 @@ void bitmap_layer_set_alignment(BitmapLayer *bitmap_layer, GAlign alignment); //! The bitmap layer is automatically marked dirty after this operation. //! @param bitmap_layer The BitmapLayer for which to set the background color //! @param color The new \ref GColor to set the background to +//! @see \ref bitmap_layer_set_compositing_mode for enabling transparency void bitmap_layer_set_background_color(BitmapLayer *bitmap_layer, GColor color); void bitmap_layer_set_background_color_2bit(BitmapLayer *bitmap_layer, GColor2 color); @@ -146,11 +134,11 @@ void bitmap_layer_set_background_color_2bit(BitmapLayer *bitmap_layer, GColor2 c //! The compositing mode only affects the drawing of the bitmap and not the //! drawing of the background color. //! -//! For Aplite, there is no notion of "transparency" in the graphics system. However, the effect of -//! transparency can be created by masking and using compositing modes. +//! For black&white platforms, there is no notion of "transparency" in the graphics system. +//! However, the effect of transparency can be created by masking and using compositing modes. //! -//! For Basalt, when drawing \ref GBitmap images, \ref GCompOpSet will be required to apply any -//! transparency. +//! For color platforms, when drawing \ref GBitmap images, \ref GCompOpSet is +//! required to apply any transparency. //! //! The bitmap layer is automatically marked dirty after this operation. //! @param bitmap_layer The BitmapLayer for which to set the compositing mode diff --git a/src/fw/applib/ui/click.c b/src/fw/applib/ui/click.c index 0392f594b6..e84d97a6a2 100644 --- a/src/fw/applib/ui/click.c +++ b/src/fw/applib/ui/click.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "click.h" #include "click_internal.h" @@ -378,8 +365,22 @@ void click_manager_clear(ClickManager* click_manager) { } } +static void prv_fire_long_release_if_held(ClickRecognizer *recognizer) { + if (recognizer->is_button_down && + recognizer->hold_timer == NULL && + recognizer->config.long_click.release_handler != NULL) { + prv_dispatch_event(recognizer, ClickHandlerOffsetLongRelease, false /* needs_reset */); + } +} + +void click_recognizer_reset(ClickRecognizer *recognizer) { + prv_fire_long_release_if_held(recognizer); + prv_click_reset(recognizer); +} + void click_manager_reset(ClickManager* click_manager) { for (unsigned int button_id = 0; button_id < NUM_BUTTONS; button_id++) { + prv_fire_long_release_if_held(&click_manager->recognizers[button_id]); prv_click_reset(&click_manager->recognizers[button_id]); } } diff --git a/src/fw/applib/ui/click.h b/src/fw/applib/ui/click.h index 56627749a9..889a1f797f 100644 --- a/src/fw/applib/ui/click.h +++ b/src/fw/applib/ui/click.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file click.h //! diff --git a/src/fw/applib/ui/click_internal.h b/src/fw/applib/ui/click_internal.h index 9bd79f54eb..48521b5887 100644 --- a/src/fw/applib/ui/click_internal.h +++ b/src/fw/applib/ui/click_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file click_internal.h //! @@ -76,6 +63,9 @@ void click_recognizer_handle_button_up(ClickRecognizer *recognizer); //! Tell the particular recognizer that the associated button has been pressed. void click_recognizer_handle_button_down(ClickRecognizer *recognizer); +//! Reset the state of a single recognizer, including timers. +void click_recognizer_reset(ClickRecognizer *recognizer); + //! Initialize a click manager for use. This only needs to be called once to initialize the structure, and then the //! same struct can be reconfigured multiple times by using click_manager_clear. void click_manager_init(ClickManager* click_manager); diff --git a/src/fw/applib/ui/content_indicator.c b/src/fw/applib/ui/content_indicator.c index efa5654256..ddadc1390c 100644 --- a/src/fw/applib/ui/content_indicator.c +++ b/src/fw/applib/ui/content_indicator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "content_indicator.h" #include "content_indicator_private.h" diff --git a/src/fw/applib/ui/content_indicator.h b/src/fw/applib/ui/content_indicator.h index 03261361d2..43243692d9 100644 --- a/src/fw/applib/ui/content_indicator.h +++ b/src/fw/applib/ui/content_indicator.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/content_indicator_private.h b/src/fw/applib/ui/content_indicator_private.h index 43f8dff900..563c180184 100644 --- a/src/fw/applib/ui/content_indicator_private.h +++ b/src/fw/applib/ui/content_indicator_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/crumbs_layer.c b/src/fw/applib/ui/crumbs_layer.c index cb794d4098..e8f9c071c3 100644 --- a/src/fw/applib/ui/crumbs_layer.c +++ b/src/fw/applib/ui/crumbs_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "crumbs_layer.h" @@ -69,14 +56,15 @@ int crumbs_layer_width(void) { return prv_crumb_config()->layer_width; } -static int prv_crumb_x_position(void) { - return prv_crumb_config()->layer_width / 2; -} - static int prv_crumb_radius(void) { return prv_crumb_config()->crumb_radius; } +#if PBL_RECT +static int prv_crumb_x_position(void) { + return prv_crumb_config()->layer_width / 2; +} + static int prv_crumb_spacing(void) { return prv_crumb_config()->crumb_spacing; } @@ -84,6 +72,7 @@ static int prv_crumb_spacing(void) { static int prv_crumb_space_from_top(void) { return prv_crumb_config()->crumb_space_from_top; } +#endif static int prv_crumb_maximum_count(void) { // NOTE: Was originally: @@ -95,6 +84,7 @@ static int prv_crumb_maximum_count(void) { return 16; } +#if PBL_RECT static void prv_crumbs_layer_update_proc_rect(Layer *layer, GContext *ctx) { const int crumb_radius = prv_crumb_radius(); const int crumb_spacing = prv_crumb_spacing(); @@ -112,6 +102,7 @@ static void prv_crumbs_layer_update_proc_rect(Layer *layer, GContext *ctx) { p.y += crumb_spacing; } } +#endif #if PBL_ROUND static void prv_crumbs_layer_update_proc_round(Layer *layer, GContext *ctx) { @@ -133,7 +124,7 @@ static void prv_crumbs_layer_update_proc_round(Layer *layer, GContext *ctx) { void crumbs_layer_set_level(CrumbsLayer *crumbs_layer, int level) { const int max_crumbs = prv_crumb_maximum_count(); if (level > max_crumbs) { - PBL_LOG(LOG_LEVEL_WARNING, "exceeded max number of crumbs"); + PBL_LOG_WRN("exceeded max number of crumbs"); level = max_crumbs; } crumbs_layer->level = level; diff --git a/src/fw/applib/ui/crumbs_layer.h b/src/fw/applib/ui/crumbs_layer.h index c533e1bafd..6d13007baf 100644 --- a/src/fw/applib/ui/crumbs_layer.h +++ b/src/fw/applib/ui/crumbs_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/date_selection_window.c b/src/fw/applib/ui/date_selection_window.c new file mode 100644 index 0000000000..109b0be0a5 --- /dev/null +++ b/src/fw/applib/ui/date_selection_window.c @@ -0,0 +1,237 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "date_selection_window.h" + +#include "applib/ui/option_menu_window.h" +#include "pbl/services/clock.h" +#include "shell/system_theme.h" +#include "util/date.h" + +#include + +// --------------------------------------------------------------------------- +// Layout configuration +// --------------------------------------------------------------------------- + +typedef struct { + int16_t year_cell_width; + int16_t month_cell_width; + int16_t day_cell_width; + int16_t cell_padding; + int16_t top_offset; + int16_t label_origin_y; +} DateSelectionSizeConfig; + +static const DateSelectionSizeConfig s_date_selection_config_medium = { + .year_cell_width = 54, + .month_cell_width = 32, + .day_cell_width = 32, + .cell_padding = 4, + .top_offset = 75, + .label_origin_y = PBL_IF_RECT_ELSE(33, 38), +}; + +static const DateSelectionSizeConfig s_date_selection_config_large = { + .year_cell_width = 70, + .month_cell_width = 44, + .day_cell_width = 44, + .cell_padding = 6, + .top_offset = 87, + .label_origin_y = 33, +}; + +static const DateSelectionSizeConfig *s_date_selection_configs[NumPreferredContentSizes] = { + [PreferredContentSizeSmall] = &s_date_selection_config_medium, + [PreferredContentSizeMedium] = &s_date_selection_config_medium, + [PreferredContentSizeLarge] = &s_date_selection_config_large, + [PreferredContentSizeExtraLarge] = &s_date_selection_config_large, +}; + +static const DateSelectionSizeConfig *prv_config(void) { + const PreferredContentSize default_size = + system_theme_get_default_content_size_for_runtime_platform(); + return s_date_selection_configs[default_size]; +} + +// --------------------------------------------------------------------------- +// Cell text rendering +// --------------------------------------------------------------------------- + +static char *prv_get_cell_text(unsigned index, void *context) { + DateSelectionWindowData *data = context; + switch ((DateInputIndex)index) { + case DateInputIndexYear: + snprintf(data->cell_buf, sizeof(data->cell_buf), "%04d", + (int)(data->date.year + STDTIME_YEAR_OFFSET)); + return data->cell_buf; + case DateInputIndexMonth: + snprintf(data->cell_buf, sizeof(data->cell_buf), "%02d", + (int)(data->date.month + 1)); + return data->cell_buf; + case DateInputIndexDay: + snprintf(data->cell_buf, sizeof(data->cell_buf), "%02d", + (int)data->date.day); + return data->cell_buf; + default: + return ""; + } +} + +// --------------------------------------------------------------------------- +// Selection layer callbacks +// --------------------------------------------------------------------------- + +static void prv_handle_complete(void *context) { + DateSelectionWindowData *data = context; + if (data->complete_callback) { + data->complete_callback(data, data->callback_context); + } +} + +static void prv_handle_inc(unsigned index, void *context) { + DateSelectionWindowData *data = context; + switch ((DateInputIndex)index) { + case DateInputIndexYear: + data->date.year = date_time_selection_step_year(data->date.year, 1); + data->date.day = date_time_selection_truncate_date( + data->date.year, data->date.month, data->date.day); + break; + case DateInputIndexMonth: + data->date.month = date_time_selection_step_month(data->date.month, 1); + data->date.day = date_time_selection_truncate_date( + data->date.year, data->date.month, data->date.day); + break; + case DateInputIndexDay: + data->date.day = date_time_selection_step_day( + data->date.year, data->date.month, data->date.day, 1); + break; + default: + break; + } +} + +static void prv_handle_dec(unsigned index, void *context) { + DateSelectionWindowData *data = context; + switch ((DateInputIndex)index) { + case DateInputIndexYear: + data->date.year = date_time_selection_step_year(data->date.year, -1); + data->date.day = date_time_selection_truncate_date( + data->date.year, data->date.month, data->date.day); + break; + case DateInputIndexMonth: + data->date.month = date_time_selection_step_month(data->date.month, -1); + data->date.day = date_time_selection_truncate_date( + data->date.year, data->date.month, data->date.day); + break; + case DateInputIndexDay: + data->date.day = date_time_selection_step_day( + data->date.year, data->date.month, data->date.day, -1); + break; + default: + break; + } +} + +// --------------------------------------------------------------------------- +// Helper – text layer setup +// --------------------------------------------------------------------------- + +static void prv_text_layer_init(Layer *window_layer, TextLayer *text_layer, const GFont font) { + text_layer_init_with_parameters(text_layer, &GRectZero, NULL, font, GColorBlack, GColorClear, + GTextAlignmentCenter, GTextOverflowModeTrailingEllipsis); + layer_add_child(window_layer, &text_layer->layer); + layer_set_hidden(&text_layer->layer, true); +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +void date_selection_window_set_to_current_date(DateSelectionWindowData *window) { + struct tm now; + clock_get_time_tm(&now); + // Clamp the year to the selectable range so that dates outside the valid + // window (e.g. year 2000 after an RTC reset) are snapped to the nearest + // valid value instead of displaying an out-of-range year that jumps on + // the first button press. + window->date.year = date_time_selection_step_year(now.tm_year, 0); + window->date.month = now.tm_mon; + window->date.day = date_time_selection_truncate_date( + window->date.year, window->date.month, now.tm_mday); +} + +void date_selection_window_init(DateSelectionWindowData *window, const char *label, + GColor color, DateSelectionCompleteCallback complete, + void *context) { + *window = (DateSelectionWindowData){}; + + window->complete_callback = complete; + window->callback_context = context; + + // General window setup + Window *w = &window->window; + window_init(w, WINDOW_NAME("Date Selection Window")); + window_set_user_data(w, window); + + // Selection layer setup + const DateSelectionSizeConfig * const cfg = prv_config(); + const int num_cells = 3; // Year, Month, Day + SelectionLayer *sel = &window->selection_layer; + selection_layer_init(sel, &GRectZero, num_cells); + selection_layer_set_cell_width(sel, DateInputIndexYear, cfg->year_cell_width); + selection_layer_set_cell_width(sel, DateInputIndexMonth, cfg->month_cell_width); + selection_layer_set_cell_width(sel, DateInputIndexDay, cfg->day_cell_width); + selection_layer_set_cell_padding(sel, cfg->cell_padding); + selection_layer_set_inactive_bg_color(sel, GColorDarkGray); + if (color.a) { + selection_layer_set_active_bg_color(sel, color); + } + + layer_set_frame(&sel->layer, + &GRect(0, cfg->top_offset, w->layer.bounds.size.w, + selection_layer_default_cell_height())); + + selection_layer_set_click_config_onto_window(sel, w); + selection_layer_set_callbacks(sel, window, (SelectionLayerCallbacks){ + .get_cell_text = prv_get_cell_text, + .complete = prv_handle_complete, + .increment = prv_handle_inc, + .decrement = prv_handle_dec, + }); + layer_add_child(&w->layer, &sel->layer); + + // Label setup + const GFont header_font = system_theme_get_font_for_default_size(TextStyleFont_Header); + TextLayer *label_layer = &window->label_text_layer; + prv_text_layer_init(&w->layer, label_layer, header_font); + if (label) { + const int lines = 1; + const int line_height = fonts_get_font_height(header_font); + layer_set_frame(&label_layer->layer, + &GRect(0, cfg->label_origin_y, w->layer.bounds.size.w, + (lines + 1) * line_height + line_height / 2)); +#if PBL_ROUND + text_layer_enable_screen_text_flow_and_paging(label_layer, 4 /* inset */); +#endif + text_layer_set_text(label_layer, label); + layer_set_hidden(&label_layer->layer, false); + } + + // Status bar setup + status_bar_layer_init(&window->status_layer); + status_bar_layer_set_colors(&window->status_layer, + PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack), + PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); + status_bar_layer_set_separator_mode(&window->status_layer, + PBL_IF_COLOR_ELSE(OPTION_MENU_STATUS_SEPARATOR_MODE, + StatusBarLayerSeparatorModeNone)); + layer_add_child(&w->layer, &window->status_layer.layer); +} + +void date_selection_window_deinit(DateSelectionWindowData *window) { + if (window) { + status_bar_layer_deinit(&window->status_layer); + selection_layer_deinit(&window->selection_layer); + } +} diff --git a/src/fw/applib/ui/date_selection_window.h b/src/fw/applib/ui/date_selection_window.h new file mode 100644 index 0000000000..82134c0b4d --- /dev/null +++ b/src/fw/applib/ui/date_selection_window.h @@ -0,0 +1,55 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/date_time_selection_window_private.h" +#include "applib/ui/selection_layer.h" +#include "applib/ui/status_bar_layer.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" + +#include +#include + +struct DateSelectionWindowData; + +typedef void (*DateSelectionCompleteCallback)(struct DateSelectionWindowData *window, void *ctx); + +//! Date data stored as fields mirroring struct tm +typedef struct { + int16_t year; //!< years since 1900 (tm_year) + int8_t month; //!< 0-11 (tm_mon) + int8_t day; //!< 1-31 (tm_mday) +} DateData; + +typedef struct DateSelectionWindowData { + Window window; + SelectionLayer selection_layer; + TextLayer label_text_layer; + StatusBarLayer status_layer; + + DateData date; + + DateSelectionCompleteCallback complete_callback; + void *callback_context; + + //! Scratch buffer for cell text rendering (max 6 chars + NUL, e.g. "-30868") + char cell_buf[7]; +} DateSelectionWindowData; + +//! Populate the date fields with the current local date. +void date_selection_window_set_to_current_date(DateSelectionWindowData *window); + +//! Initialise the date selection window. +//! @param window The window data to initialise. +//! @param label Optional title string (may be NULL). +//! @param color Highlight colour for the active selection cell. +//! @param complete Callback invoked when the user confirms their selection. +//! @param context Caller-provided pointer passed to the callback. +void date_selection_window_init(DateSelectionWindowData *window, const char *label, + GColor color, DateSelectionCompleteCallback complete, + void *context); + +//! Deinitialise the date selection window. +void date_selection_window_deinit(DateSelectionWindowData *window); diff --git a/src/fw/applib/ui/date_time_selection_window_private.c b/src/fw/applib/ui/date_time_selection_window_private.c index 2e12e72ced..b2c82ed55f 100644 --- a/src/fw/applib/ui/date_time_selection_window_private.c +++ b/src/fw/applib/ui/date_time_selection_window_private.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "date_time_selection_window_private.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" #include "util/date.h" #include "util/math.h" diff --git a/src/fw/applib/ui/date_time_selection_window_private.h b/src/fw/applib/ui/date_time_selection_window_private.h index 87889c75d7..89cd272835 100644 --- a/src/fw/applib/ui/date_time_selection_window_private.h +++ b/src/fw/applib/ui/date_time_selection_window_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/dialogs/actionable_dialog.c b/src/fw/applib/ui/dialogs/actionable_dialog.c index 9de7692aa1..21a0bba3d1 100644 --- a/src/fw/applib/ui/dialogs/actionable_dialog.c +++ b/src/fw/applib/ui/dialogs/actionable_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "actionable_dialog.h" @@ -42,19 +29,14 @@ static void prv_actionable_dialog_load(Window *window) { const GSize icon_size = icon ? kino_reel_get_size(icon) : GSizeZero; const GRect *bounds = &window_root_layer->bounds; - const uint16_t icon_single_line_text_offset_px = 13; const uint16_t left_margin_px = PBL_IF_RECT_ELSE(5, 0); const uint16_t content_and_action_bar_horizontal_spacing = PBL_IF_RECT_ELSE(5, 7); const uint16_t right_margin_px = ACTION_BAR_WIDTH + content_and_action_bar_horizontal_spacing; - const uint16_t text_single_line_text_offset_px = icon_single_line_text_offset_px - 1; const GFont dialog_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - const int single_line_text_height_px = fonts_get_font_height(dialog_text_font); - const int max_text_line_height_px = 2 * single_line_text_height_px + 8; + const int max_text_line_height_px = 2 * fonts_get_font_height(dialog_text_font) + 8; + const uint16_t icon_text_spacing_px = 4; - const uint16_t status_layer_offset = dialog->show_status_layer ? 6 : 0; - uint16_t text_top_margin_px = icon ? icon_size.h + 22 : 6; - uint16_t icon_top_margin_px = 18; uint16_t x = 0; uint16_t y = 0; uint16_t w = PBL_IF_RECT_ELSE(bounds->size.w - ACTION_BAR_WIDTH, bounds->size.w); @@ -75,30 +57,26 @@ static void prv_actionable_dialog_load(Window *window) { text_attributes = graphics_text_attributes_create(); graphics_text_attributes_enable_screen_text_flow(text_attributes, 8); #endif - // Check if the text takes up more than one line. If the dialog has a single line of text, - // the icon and line of text are positioned lower so as to be more vertically centered. + + // Probe text height to vertically center icon + text in the available space GContext *ctx = graphics_context_get_current_context(); const GTextAlignment text_alignment = PBL_IF_RECT_ELSE(GTextAlignmentCenter, GTextAlignmentRight); - { - // do all this in a block so we enforce that nobody uses these variables outside of the block - // when dealing with round displays, sizes change depending on location. - const GRect probe_rect = GRect(x, y + text_single_line_text_offset_px, - w, max_text_line_height_px); - const uint16_t text_height = graphics_text_layout_get_max_used_size(ctx, - dialog->buffer, - dialog_text_font, - probe_rect, - GTextOverflowModeWordWrap, - text_alignment, - text_attributes).h; - if (text_height <= single_line_text_height_px) { - text_top_margin_px += text_single_line_text_offset_px; - icon_top_margin_px += icon_single_line_text_offset_px; - } else { - text_top_margin_px += status_layer_offset; - icon_top_margin_px += status_layer_offset; - } - } + const GRect probe_rect = GRect(x, 0, w, max_text_line_height_px); + const uint16_t text_height = graphics_text_layout_get_max_used_size(ctx, + dialog->buffer, + dialog_text_font, + probe_rect, + GTextOverflowModeWordWrap, + text_alignment, + text_attributes).h; + + const uint16_t status_bar_height = dialog->show_status_layer ? STATUS_BAR_LAYER_HEIGHT : 0; + const uint16_t available_height = bounds->size.h - status_bar_height; + const uint16_t spacing = icon ? icon_text_spacing_px : 0; + const uint16_t content_height = icon_size.h + spacing + text_height; + uint16_t icon_top_margin_px = status_bar_height + + (available_height > content_height ? (available_height - content_height) / 2 : 0); + uint16_t text_top_margin_px = icon_top_margin_px + icon_size.h + spacing; y = text_top_margin_px; h = bounds->size.h - y; @@ -125,20 +103,20 @@ static void prv_actionable_dialog_load(Window *window) { } ActionBarLayer *action_bar = actionable_dialog->action_bar; if (actionable_dialog->action_bar_type == DialogActionBarConfirm) { -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) actionable_dialog->select_icon = gbitmap_create_with_resource( RESOURCE_ID_ACTION_BAR_ICON_CHECK); #endif action_bar_layer_set_context(action_bar, window); action_bar_layer_set_icon(action_bar, BUTTON_ID_SELECT, actionable_dialog->select_icon); } else if (actionable_dialog->action_bar_type == DialogActionBarDecline) { -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) actionable_dialog->select_icon = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_X); #endif action_bar_layer_set_context(action_bar, window); action_bar_layer_set_icon(action_bar, BUTTON_ID_SELECT, actionable_dialog->select_icon); } else if (actionable_dialog->action_bar_type == DialogActionBarConfirmDecline) { -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) actionable_dialog->up_icon = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_CHECK); actionable_dialog->down_icon = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_X); #endif diff --git a/src/fw/applib/ui/dialogs/actionable_dialog.h b/src/fw/applib/ui/dialogs/actionable_dialog.h index 6e5b59cc4a..7931785317 100644 --- a/src/fw/applib/ui/dialogs/actionable_dialog.h +++ b/src/fw/applib/ui/dialogs/actionable_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/dialogs/actionable_dialog_private.h b/src/fw/applib/ui/dialogs/actionable_dialog_private.h index 3fab598fb2..a7ce174f35 100644 --- a/src/fw/applib/ui/dialogs/actionable_dialog_private.h +++ b/src/fw/applib/ui/dialogs/actionable_dialog_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! An ActionableDialog is a dialog that has an action bar on the right hand side //! of the window. The user can specify there own custom \ref ActionBarLayer to diff --git a/src/fw/applib/ui/dialogs/bt_conn_dialog.c b/src/fw/applib/ui/dialogs/bt_conn_dialog.c index 41e5693738..289ea78fbe 100644 --- a/src/fw/applib/ui/dialogs/bt_conn_dialog.c +++ b/src/fw/applib/ui/dialogs/bt_conn_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bt_conn_dialog.h" @@ -25,7 +12,7 @@ #include "kernel/ui/modals/modal_manager.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "syscall/syscall.h" #include "system/passert.h" diff --git a/src/fw/applib/ui/dialogs/bt_conn_dialog.h b/src/fw/applib/ui/dialogs/bt_conn_dialog.h index c12a5cab55..447a7d8423 100644 --- a/src/fw/applib/ui/dialogs/bt_conn_dialog.h +++ b/src/fw/applib/ui/dialogs/bt_conn_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/dialogs/confirmation_dialog.c b/src/fw/applib/ui/dialogs/confirmation_dialog.c index 7b8200c1b9..db40a3d5e9 100644 --- a/src/fw/applib/ui/dialogs/confirmation_dialog.c +++ b/src/fw/applib/ui/dialogs/confirmation_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "confirmation_dialog.h" diff --git a/src/fw/applib/ui/dialogs/confirmation_dialog.h b/src/fw/applib/ui/dialogs/confirmation_dialog.h index 18fc057f2f..d15a39f108 100644 --- a/src/fw/applib/ui/dialogs/confirmation_dialog.h +++ b/src/fw/applib/ui/dialogs/confirmation_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! A ConfirmationDialog is a wrapper around an ActionableDialog implementing //! the common features provided by a confirmation window. The user specifies diff --git a/src/fw/applib/ui/dialogs/dialog.c b/src/fw/applib/ui/dialogs/dialog.c index 5902f650e3..7f30039e3e 100644 --- a/src/fw/applib/ui/dialogs/dialog.c +++ b/src/fw/applib/ui/dialogs/dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dialog.h" diff --git a/src/fw/applib/ui/dialogs/dialog.h b/src/fw/applib/ui/dialogs/dialog.h index 5ab4de9f51..f365944bab 100644 --- a/src/fw/applib/ui/dialogs/dialog.h +++ b/src/fw/applib/ui/dialogs/dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -114,11 +101,11 @@ void dialog_set_text(Dialog *dialog, const char *text); void dialog_set_text_buffer(Dialog *dialog, char *buffer, bool take_ownership); // Sets the color of the dialog's text. -// if SCREEN_COLOR_DEPTH_BITS == 1 then the color will always be set to black +// if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 then the color will always be set to black void dialog_set_text_color(Dialog *dialog, GColor text_color); // Sets the background color of the dialog window. -// if SCREEN_COLOR_DEPTH_BITS == 1 then the color will always be set to white +// if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 then the color will always be set to white void dialog_set_background_color(Dialog *dialog, GColor background_color); // Sets the icon displayed by the dialog. diff --git a/src/fw/applib/ui/dialogs/dialog_private.c b/src/fw/applib/ui/dialogs/dialog_private.c index 02908765d6..af49e3a3e9 100644 --- a/src/fw/applib/ui/dialogs/dialog_private.c +++ b/src/fw/applib/ui/dialogs/dialog_private.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dialog_private.h" @@ -127,12 +114,14 @@ bool dialog_init_icon_layer(Dialog *dialog, KinoReel *image, layer_set_clips(&icon_layer->layer, false); GRect from = icon_rect; - // Animate from off screen. We need to be at least -80, since that is our largest icon size. - const int16_t DISP_OFFSET = 80; + // Animate from off screen + GRect bounds; + Layer *window_layer = window_get_root_layer(&dialog->window); + layer_get_bounds(window_layer, &bounds); if (dialog->icon_anim_direction == DialogIconAnimationFromLeft) { - from.origin.x = -DISP_OFFSET; + from.origin.x = bounds.origin.x - icon_rect.size.w; } else if (dialog->icon_anim_direction == DialogIconAnimationFromRight) { - from.origin.x = DISP_OFFSET; + from.origin.x = bounds.origin.x + bounds.size.w; } const int16_t ICON_TARGET_PT_X = icon_rect.size.w; diff --git a/src/fw/applib/ui/dialogs/dialog_private.h b/src/fw/applib/ui/dialogs/dialog_private.h index 48d321cb10..78e26867ce 100644 --- a/src/fw/applib/ui/dialogs/dialog_private.h +++ b/src/fw/applib/ui/dialogs/dialog_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/dialogs/expandable_dialog.c b/src/fw/applib/ui/dialogs/expandable_dialog.c index e4d8c88b65..79fd698b69 100644 --- a/src/fw/applib/ui/dialogs/expandable_dialog.c +++ b/src/fw/applib/ui/dialogs/expandable_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "expandable_dialog.h" @@ -156,9 +143,6 @@ static void prv_expandable_dialog_load(Window *window) { scroll_layer_init(scroll_layer, &GRect(x, y, w, h)); layer_add_child(&window->layer, &scroll_layer->layer); -#if PBL_ROUND - uint16_t page_height = scroll_layer->layer.bounds.size.h; -#endif // Set up the header if this dialog is set to have one. GTextAlignment alignment = PBL_IF_RECT_ELSE(GTextAlignmentLeft, @@ -204,7 +188,7 @@ static void prv_expandable_dialog_load(Window *window) { w = frame.size.w - right_margin_px - left_margin_px - action_bar_offset - right_aligned_box_reduction; h = INT16_MAX; // height is clamped to content size - GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + GFont font = expandable_dialog->body_font; TextLayer *text_layer = &dialog->text_layer; text_layer_init_with_parameters(text_layer, &GRect(x, y, w, h), dialog->buffer, font, @@ -238,7 +222,7 @@ static void prv_expandable_dialog_load(Window *window) { if (show_action_bar) { // Icons for up and down on the action bar. -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW expandable_dialog->up_icon = gbitmap_create_with_resource_system(SYSTEM_APP, RESOURCE_ID_ACTION_BAR_ICON_UP); expandable_dialog->down_icon = gbitmap_create_with_resource_system(SYSTEM_APP, @@ -342,6 +326,7 @@ void expandable_dialog_init(ExpandableDialog *expandable_dialog, const char *dia PBL_ASSERTN(expandable_dialog); *expandable_dialog = (ExpandableDialog) { .header_font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), + .body_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), }; dialog_init(&expandable_dialog->dialog, dialog_name); @@ -416,6 +401,10 @@ void expandable_dialog_set_header_font(ExpandableDialog *expandable_dialog, GFon expandable_dialog->header_font = header_font; } +void expandable_dialog_set_body_font(ExpandableDialog *expandable_dialog, GFont body_font) { + expandable_dialog->body_font = body_font; +} + void expandable_dialog_set_select_action(ExpandableDialog *expandable_dialog, uint32_t resource_id, ClickHandler select_click_handler) { diff --git a/src/fw/applib/ui/dialogs/expandable_dialog.h b/src/fw/applib/ui/dialogs/expandable_dialog.h index af4ec336db..580b12c0d6 100644 --- a/src/fw/applib/ui/dialogs/expandable_dialog.h +++ b/src/fw/applib/ui/dialogs/expandable_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -46,6 +33,7 @@ typedef struct ExpandableDialog { GBitmap *down_icon; GFont header_font; + GFont body_font; char header[DIALOG_MAX_HEADER_LEN + 1]; TextLayer header_layer; @@ -121,6 +109,11 @@ void expandable_dialog_set_header(ExpandableDialog *expandable_dialog, const cha //! @param header_font The font to use for the header text void expandable_dialog_set_header_font(ExpandableDialog *expandable_dialog, GFont header_font); +//! Sets the body text font +//! @param expandable_dialog Pointer to the \ref ExpandableDialog on which to set the body font +//! @param body_font The font to use for the body text +void expandable_dialog_set_body_font(ExpandableDialog *expandable_dialog, GFont body_font); + //! Sets the icon and ClickHandler of the SELECT button on the action bar. //! @param expandable_dialog Pointer to the \ref ExpandableDialog for which to set //! @param resource_id The resource id of the resource to be used to create the select bitmap diff --git a/src/fw/applib/ui/dialogs/simple_dialog.c b/src/fw/applib/ui/dialogs/simple_dialog.c index aca4c3748e..661e0979de 100644 --- a/src/fw/applib/ui/dialogs/simple_dialog.c +++ b/src/fw/applib/ui/dialogs/simple_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "simple_dialog.h" @@ -30,7 +17,7 @@ #include #include -#if (RECOVERY_FW || UNITTEST) +#if defined(CONFIG_RECOVERY_FW) || defined(UNITTEST) #define SIMPLE_DIALOG_ANIMATED false #else #define SIMPLE_DIALOG_ANIMATED true @@ -39,7 +26,11 @@ // Layout Defines #define TEXT_ALIGNMENT (GTextAlignmentCenter) #define TEXT_OVERFLOW (GTextOverflowModeWordWrap) +#if PBL_DISPLAY_HEIGHT >= 200 +#define TEXT_FONT (fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)) +#else #define TEXT_FONT (fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)) +#endif #define TEXT_LEFT_MARGIN_PX (PBL_IF_RECT_ELSE(6, 0)) #define TEXT_RIGHT_MARGIN_PX (PBL_IF_RECT_ELSE(6, 0)) @@ -64,21 +55,32 @@ static int prv_get_rendered_text_height(const char *text, const GRect *text_box) static int prv_get_icon_top_margin(bool has_status_bar, int icon_height, int window_height) { const uint16_t status_layer_offset = has_status_bar ? 6 : 0; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 const uint16_t icon_top_default_margin_px = 42 + status_layer_offset; #else const uint16_t icon_top_default_margin_px = 18 + status_layer_offset; #endif const uint16_t frame_height_claimed = icon_height + TEXT_MAX_HEIGHT_PX + status_layer_offset; const uint16_t icon_top_adjusted_margin_px = MAX(window_height - frame_height_claimed, 0); - // Try and use the default value if possible. - return (icon_top_adjusted_margin_px < icon_top_default_margin_px) ? icon_top_adjusted_margin_px : - icon_top_default_margin_px; + if (icon_top_adjusted_margin_px < icon_top_default_margin_px) { + return icon_top_adjusted_margin_px; + } + // For icon+text dialogs, vertically center the icon+text block when the window is taller + // than the default layout assumes, by splitting the leftover space evenly above and below. + // Text-only dialogs keep the default top margin so layouts tuned for them aren't disturbed. + if (icon_height > 0) { + return MAX(icon_top_default_margin_px, icon_top_adjusted_margin_px / 2); + } + return icon_top_default_margin_px; } static void prv_get_text_box(GSize frame_size, GSize icon_size, int icon_top_margin_px, GRect *text_box_out) { +#if PBL_DISPLAY_HEIGHT >= 200 + const uint16_t icon_text_spacing_px = PBL_IF_ROUND_ELSE(8, 16); +#else const uint16_t icon_text_spacing_px = PBL_IF_ROUND_ELSE(2, 4); +#endif const uint16_t text_x = TEXT_LEFT_MARGIN_PX; const uint16_t text_y = icon_top_margin_px + MAX(icon_size.h, 6) + icon_text_spacing_px; diff --git a/src/fw/applib/ui/dialogs/simple_dialog.h b/src/fw/applib/ui/dialogs/simple_dialog.h index 569d23d654..4e03ddbde5 100644 --- a/src/fw/applib/ui/dialogs/simple_dialog.h +++ b/src/fw/applib/ui/dialogs/simple_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/inverter_layer.c b/src/fw/applib/ui/inverter_layer.c index 8601edd22c..fe62429c44 100644 --- a/src/fw/applib/ui/inverter_layer.c +++ b/src/fw/applib/ui/inverter_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "inverter_layer.h" @@ -61,7 +48,7 @@ inline static void prv_inverter_layer_update_proc_bw(GContext *ctx) { } void inverter_layer_update_proc(InverterLayer *inverter, GContext* ctx) { -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 prv_inverter_layer_update_proc_bw(ctx); #else prv_inverter_layer_update_proc_color(ctx); diff --git a/src/fw/applib/ui/inverter_layer.h b/src/fw/applib/ui/inverter_layer.h index 5b76fb5071..ecee14047b 100644 --- a/src/fw/applib/ui/inverter_layer.h +++ b/src/fw/applib/ui/inverter_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "layer.h" diff --git a/src/fw/applib/ui/kino/kino_layer.c b/src/fw/applib/ui/kino/kino_layer.c index 7608d92022..1ed5c14dba 100644 --- a/src/fw/applib/ui/kino/kino_layer.c +++ b/src/fw/applib/ui/kino/kino_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_layer.h" diff --git a/src/fw/applib/ui/kino/kino_layer.h b/src/fw/applib/ui/kino/kino_layer.h index 5558c2c498..509fadcb7d 100644 --- a/src/fw/applib/ui/kino/kino_layer.h +++ b/src/fw/applib/ui/kino/kino_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_player.c b/src/fw/applib/ui/kino/kino_player.c index f04ea5c808..7ab307ac6b 100644 --- a/src/fw/applib/ui/kino/kino_player.c +++ b/src/fw/applib/ui/kino/kino_player.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_player.h" diff --git a/src/fw/applib/ui/kino/kino_player.h b/src/fw/applib/ui/kino/kino_player.h index 4f19de0e24..345e9fe837 100644 --- a/src/fw/applib/ui/kino/kino_player.h +++ b/src/fw/applib/ui/kino/kino_player.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel.c b/src/fw/applib/ui/kino/kino_reel.c index f6db9a62f0..723908e2cb 100644 --- a/src/fw/applib/ui/kino/kino_reel.c +++ b/src/fw/applib/ui/kino/kino_reel.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_reel.h" #include "kino_reel_custom.h" diff --git a/src/fw/applib/ui/kino/kino_reel.h b/src/fw/applib/ui/kino/kino_reel.h index f59811dc19..e55497d9e2 100644 --- a/src/fw/applib/ui/kino/kino_reel.h +++ b/src/fw/applib/ui/kino/kino_reel.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel/morph_square.c b/src/fw/applib/ui/kino/kino_reel/morph_square.c index 8c97d5991e..0cfaa1744b 100644 --- a/src/fw/applib/ui/kino/kino_reel/morph_square.c +++ b/src/fw/applib/ui/kino/kino_reel/morph_square.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "morph_square.h" #include "transform.h" diff --git a/src/fw/applib/ui/kino/kino_reel/morph_square.h b/src/fw/applib/ui/kino/kino_reel/morph_square.h index 4ac0017fad..31433a7d93 100644 --- a/src/fw/applib/ui/kino/kino_reel/morph_square.h +++ b/src/fw/applib/ui/kino/kino_reel/morph_square.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel/scale_segmented.c b/src/fw/applib/ui/kino/kino_reel/scale_segmented.c index 8cad39fdb3..05dd42267f 100644 --- a/src/fw/applib/ui/kino/kino_reel/scale_segmented.c +++ b/src/fw/applib/ui/kino/kino_reel/scale_segmented.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "transform.h" #include "scale_segmented.h" diff --git a/src/fw/applib/ui/kino/kino_reel/scale_segmented.h b/src/fw/applib/ui/kino/kino_reel/scale_segmented.h index 24d62078ee..529d0a45a6 100644 --- a/src/fw/applib/ui/kino/kino_reel/scale_segmented.h +++ b/src/fw/applib/ui/kino/kino_reel/scale_segmented.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel/transform.c b/src/fw/applib/ui/kino/kino_reel/transform.c index 7d7a94efe4..176f1c2d42 100644 --- a/src/fw/applib/ui/kino/kino_reel/transform.c +++ b/src/fw/applib/ui/kino/kino_reel/transform.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "transform.h" diff --git a/src/fw/applib/ui/kino/kino_reel/transform.h b/src/fw/applib/ui/kino/kino_reel/transform.h index 4c555c0f0d..478db6c4ec 100644 --- a/src/fw/applib/ui/kino/kino_reel/transform.h +++ b/src/fw/applib/ui/kino/kino_reel/transform.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel/unfold.c b/src/fw/applib/ui/kino/kino_reel/unfold.c index 38793bde1c..60d232f9f7 100644 --- a/src/fw/applib/ui/kino/kino_reel/unfold.c +++ b/src/fw/applib/ui/kino/kino_reel/unfold.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "transform.h" #include "scale_segmented.h" diff --git a/src/fw/applib/ui/kino/kino_reel/unfold.h b/src/fw/applib/ui/kino/kino_reel/unfold.h index 2e046d9c7a..3049d7b78e 100644 --- a/src/fw/applib/ui/kino/kino_reel/unfold.h +++ b/src/fw/applib/ui/kino/kino_reel/unfold.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel_custom.c b/src/fw/applib/ui/kino/kino_reel_custom.c index f5cd372924..1f0af2f9c3 100644 --- a/src/fw/applib/ui/kino/kino_reel_custom.c +++ b/src/fw/applib/ui/kino/kino_reel_custom.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_reel_custom.h" diff --git a/src/fw/applib/ui/kino/kino_reel_custom.h b/src/fw/applib/ui/kino/kino_reel_custom.h index 97cd5d209c..d3f939d29d 100644 --- a/src/fw/applib/ui/kino/kino_reel_custom.h +++ b/src/fw/applib/ui/kino/kino_reel_custom.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel_gbitmap.c b/src/fw/applib/ui/kino/kino_reel_gbitmap.c index 3519312723..012ce9fbb9 100644 --- a/src/fw/applib/ui/kino/kino_reel_gbitmap.c +++ b/src/fw/applib/ui/kino/kino_reel_gbitmap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_reel_gbitmap.h" #include "kino_reel_gbitmap_private.h" diff --git a/src/fw/applib/ui/kino/kino_reel_gbitmap.h b/src/fw/applib/ui/kino/kino_reel_gbitmap.h index c63936f560..4fbdc0b2c7 100644 --- a/src/fw/applib/ui/kino/kino_reel_gbitmap.h +++ b/src/fw/applib/ui/kino/kino_reel_gbitmap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel_gbitmap_private.h b/src/fw/applib/ui/kino/kino_reel_gbitmap_private.h index 2cb75dc853..f50437d7d5 100644 --- a/src/fw/applib/ui/kino/kino_reel_gbitmap_private.h +++ b/src/fw/applib/ui/kino/kino_reel_gbitmap_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c b/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c index b6e8507cd6..30a24df089 100644 --- a/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c +++ b/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_reel_gbitmap_sequence.h" diff --git a/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.h b/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.h index 22f679691c..af0bb506d1 100644 --- a/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.h +++ b/src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel_pdci.c b/src/fw/applib/ui/kino/kino_reel_pdci.c index 7f27b041f7..fbfd0f9d93 100644 --- a/src/fw/applib/ui/kino/kino_reel_pdci.c +++ b/src/fw/applib/ui/kino/kino_reel_pdci.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_reel_pdci.h" diff --git a/src/fw/applib/ui/kino/kino_reel_pdci.h b/src/fw/applib/ui/kino/kino_reel_pdci.h index 9a4b67937d..d172aed06f 100644 --- a/src/fw/applib/ui/kino/kino_reel_pdci.h +++ b/src/fw/applib/ui/kino/kino_reel_pdci.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/kino/kino_reel_pdcs.c b/src/fw/applib/ui/kino/kino_reel_pdcs.c index c6f40a1f68..8544fd450b 100644 --- a/src/fw/applib/ui/kino/kino_reel_pdcs.c +++ b/src/fw/applib/ui/kino/kino_reel_pdcs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_reel_pdcs.h" diff --git a/src/fw/applib/ui/kino/kino_reel_pdcs.h b/src/fw/applib/ui/kino/kino_reel_pdcs.h index b47bd89a56..a99d3ce9ac 100644 --- a/src/fw/applib/ui/kino/kino_reel_pdcs.h +++ b/src/fw/applib/ui/kino/kino_reel_pdcs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/layer.c b/src/fw/applib/ui/layer.c index bc689a325b..d7feeea75d 100644 --- a/src/fw/applib/ui/layer.c +++ b/src/fw/applib/ui/layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "layer.h" #include "layer_private.h" @@ -61,7 +48,7 @@ Layer* layer_create_with_data(GRect frame, size_t data_size) { return layer; } -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH static bool prv_destroy_recognizer(Recognizer *recognizer, void *context) { Layer *layer = context; layer_detach_recognizer(layer, recognizer); @@ -76,7 +63,7 @@ void layer_deinit(Layer *layer) { } layer_remove_from_parent(layer); -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH // Destroy all attached recognizers recognizer_list_iterate(&layer->recognizer_list, prv_destroy_recognizer, layer); #endif @@ -138,7 +125,7 @@ inline static Layer __attribute__((always_inline)) *prv_layer_tree_traverse_next if (*current_depth < stack_size-1) { return stack[++(*current_depth)] = top_of_stack->first_child; } else { - PBL_LOG(LOG_LEVEL_WARNING, "layer stack exceeded (%d). Will skip rendering.", stack_size); + PBL_LOG_WRN("layer stack exceeded (%d). Will skip rendering.", stack_size); } } @@ -397,7 +384,7 @@ void layer_add_child(Layer *parent, Layer *child) { // Prevent setting the child to point to itself, causing infinite loop the next time this is // called if (sibling == child) { - PBL_LOG(LOG_LEVEL_DEBUG, "Layer has already been added to this parent!"); + PBL_LOG_DBG("Layer has already been added to this parent!"); return; } @@ -484,7 +471,7 @@ bool layer_get_clips(const Layer *layer) { void* layer_get_data(const Layer *layer) { if (!layer->has_data) { - PBL_LOG(LOG_LEVEL_ERROR, "Layer was not allocated with a data region."); + PBL_LOG_ERR("Layer was not allocated with a data region."); return NULL; } return ((DataLayer *)layer)->data; @@ -528,7 +515,7 @@ bool layer_contains_point(const Layer *layer, const GPoint *point) { if (!layer || !point) { return false; } -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH if (layer->contains_point_override) { return layer->contains_point_override(layer, point); } @@ -540,7 +527,7 @@ void layer_set_contains_point_override(Layer *layer, LayerContainsPointOverride if (!layer) { return; } -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH layer->contains_point_override = override; #endif } @@ -589,7 +576,7 @@ MOCKABLE Layer *layer_find_layer_containing_point(const Layer *node, const GPoin } void layer_attach_recognizer(Layer *layer, Recognizer *recognizer) { -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH if (!layer || !recognizer) { return; } @@ -600,7 +587,7 @@ void layer_attach_recognizer(Layer *layer, Recognizer *recognizer) { } void layer_detach_recognizer(Layer *layer, Recognizer *recognizer) { -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH if (!layer || !recognizer) { return; } @@ -611,7 +598,7 @@ void layer_detach_recognizer(Layer *layer, Recognizer *recognizer) { } RecognizerList *layer_get_recognizer_list(const Layer *layer) { -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH if (!layer) { return NULL; } diff --git a/src/fw/applib/ui/layer.h b/src/fw/applib/ui/layer.h index 08eb0c54e2..9efdd0cf59 100644 --- a/src/fw/applib/ui/layer.h +++ b/src/fw/applib/ui/layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/applib/ui/layer_private.h b/src/fw/applib/ui/layer_private.h index 17ee3d03f3..10dd3935b1 100644 --- a/src/fw/applib/ui/layer_private.h +++ b/src/fw/applib/ui/layer_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "layer.h" diff --git a/src/fw/applib/ui/menu_cell_layer.h b/src/fw/applib/ui/menu_cell_layer.h index 1b53962816..582baea06e 100644 --- a/src/fw/applib/ui/menu_cell_layer.h +++ b/src/fw/applib/ui/menu_cell_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/menu_layer.c b/src/fw/applib/ui/menu_layer.c index de5b8ebc1f..f7a3114801 100644 --- a/src/fw/applib/ui/menu_layer.c +++ b/src/fw/applib/ui/menu_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "menu_layer.h" #include "menu_layer_private.h" @@ -34,6 +21,8 @@ #include "system/logging.h" #include "system/passert.h" #include "util/math.h" +#include "util/size.h" +#include "vibes.h" #include @@ -89,16 +78,103 @@ static void prv_menu_select_long_click_handler(ClickRecognizerRef recognizer, } } +static inline uint16_t prv_menu_layer_get_num_sections(MenuLayer *menu_layer); +static inline uint16_t prv_menu_layer_get_num_rows(MenuLayer *menu_layer, uint16_t section_index); + +static bool prv_menu_index_is_first_index(MenuLayer *menu_layer, const MenuIndex *index) { + (void)menu_layer; + + MenuIndex first_index = MenuIndex(0, 0); + return menu_index_compare(index, &first_index) == 0; +} + +static bool prv_menu_index_is_last_index(MenuLayer *menu_layer, const MenuIndex *index) { + int last_index_section = prv_menu_layer_get_num_sections(menu_layer) - 1; + int last_index_row = prv_menu_layer_get_num_rows(menu_layer, last_index_section) - 1; + MenuIndex last_index = MenuIndex(last_index_section, last_index_row); + return menu_index_compare(index, &last_index) == 0; +} + +static void prv_vibe_pulse(void) { + uint32_t const segments[] = { 50 }; + VibePattern pat = { + .durations = segments, + .num_segments = ARRAY_LENGTH(segments), + }; + vibes_enqueue_custom_pattern(pat); +} + +//! Handle the menu scroll wrap around +//! @param menu_layer reference to the current MenuLayer +//! @param recognizer reference to the ClickRecognizer struct +//! @param scrolling_up `true` if scrolling up, `false` if scrolling down +//! @return `true` if a wrap around has been applied +static bool prv_menu_scroll_handle_wrap_around(MenuLayer *menu_layer, ClickRecognizerRef recognizer, bool scrolling_up) { + const uint8_t current_scroll_action = scrolling_up ? MenuLayerRepeatScrollingUp : MenuLayerRepeatScrollingDown; + const bool is_repeating = click_recognizer_is_repeating(recognizer); + + if (is_repeating) { + menu_layer->cache.button_repeat_scrolling = current_scroll_action; + if (!menu_layer->scroll_force_wrap_on_repeat) { + return false; + } + } + menu_layer->cache.button_repeat_scrolling = MenuLayerNoRepeatScrolling; + + MenuIndex current_index = menu_layer->selection.index; + int last_index_section = prv_menu_layer_get_num_sections(menu_layer) - 1; + int last_index_row = prv_menu_layer_get_num_rows(menu_layer, last_index_section) - 1; + MenuIndex first_index = MenuIndex(0, 0); + MenuIndex last_index = MenuIndex(last_index_section, last_index_row); + MenuIndex *wraparound_dest_index; + if ((menu_index_compare(¤t_index, &first_index) == 0) && scrolling_up) { + wraparound_dest_index = &last_index; + } else if ((menu_index_compare(¤t_index, &last_index) == 0) && !scrolling_up) { + wraparound_dest_index = &first_index; + } else { + return false; + } + + const bool animated = true; + menu_layer_set_selected_index(menu_layer, *wraparound_dest_index, MenuRowAlignCenter, animated); + if (menu_layer->scroll_vibe_on_wrap_around) { + prv_vibe_pulse(); + } + return true; +} + void menu_up_click_handler(ClickRecognizerRef recognizer, MenuLayer *menu_layer) { const bool up = true; + if (menu_layer->scroll_wrap_around && prv_menu_scroll_handle_wrap_around(menu_layer, recognizer, up)) { + return; + } + + MenuIndex prev_index = menu_layer->selection.index; const bool animated = true; menu_layer_set_selected_next(menu_layer, up, MenuRowAlignCenter, animated); + MenuIndex current_index = menu_layer->selection.index; + if ((menu_layer->scroll_vibe_on_blocked) && + (menu_index_compare(¤t_index, &prev_index) == 0) && + (prv_menu_index_is_first_index(menu_layer, ¤t_index))) { + prv_vibe_pulse(); + } } void menu_down_click_handler(ClickRecognizerRef recognizer, MenuLayer *menu_layer) { const bool up = false; + if (menu_layer->scroll_wrap_around && prv_menu_scroll_handle_wrap_around(menu_layer, recognizer, up)) { + return; + } + + MenuIndex prev_index = menu_layer->selection.index; const bool animated = true; menu_layer_set_selected_next(menu_layer, up, MenuRowAlignCenter, animated); + MenuIndex current_index = menu_layer->selection.index; + if ((menu_layer->scroll_vibe_on_blocked) && + (menu_index_compare(¤t_index, &prev_index) == 0) && + (prv_menu_index_is_last_index(menu_layer, ¤t_index))) { + prv_vibe_pulse(); + } } static void prv_menu_click_config_provider(MenuLayer *menu_layer) { @@ -1169,7 +1245,7 @@ void menu_layer_set_selected_index(MenuLayer *menu_layer, MenuIndex index, MenuR } // check to make sure this callback has been set, return early if not if (menu_layer->callbacks.get_num_rows == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Please set menu layer callbacks before running menu_layer_set_selected_index."); + PBL_LOG_ERR("Please set menu layer callbacks before running menu_layer_set_selected_index."); return; } @@ -1306,3 +1382,46 @@ void menu_layer_set_center_focused(MenuLayer *menu_layer, bool center_focused) { prv_set_center_focused(menu_layer, center_focused); menu_layer_update_caches(menu_layer); } + +bool menu_layer_get_scroll_wrap_around(MenuLayer *menu_layer) { + return menu_layer->scroll_wrap_around; +} + +void menu_layer_set_scroll_wrap_around(MenuLayer *menu_layer, bool scroll_wrap_around) { + if (!menu_layer) { + return; + } + menu_layer->scroll_wrap_around = scroll_wrap_around; +} + +uint8_t menu_layer_get_scroll_vibe_behavior(MenuLayer *menu_layer) { + if (menu_layer->scroll_vibe_on_blocked) { + return 2; + } else if (menu_layer->scroll_vibe_on_wrap_around) { + return 1; + } else { + return 0; + } +} + +void menu_layer_set_scroll_vibe_on_wrap(MenuLayer *menu_layer, bool scroll_vibe_on_wrap) { + if (!menu_layer) { + return; + } + + if (scroll_vibe_on_wrap) { + menu_layer->scroll_vibe_on_blocked = false; + } + menu_layer->scroll_vibe_on_wrap_around = scroll_vibe_on_wrap; +} + +void menu_layer_set_scroll_vibe_on_blocked(MenuLayer *menu_layer, bool scroll_vibe_on_blocked) { + if (!menu_layer) { + return; + } + + if (scroll_vibe_on_blocked) { + menu_layer->scroll_vibe_on_wrap_around = false; + } + menu_layer->scroll_vibe_on_blocked = scroll_vibe_on_blocked; +} \ No newline at end of file diff --git a/src/fw/applib/ui/menu_layer.h b/src/fw/applib/ui/menu_layer.h index 289bffa262..88faf9bed5 100644 --- a/src/fw/applib/ui/menu_layer.h +++ b/src/fw/applib/ui/menu_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -351,6 +338,12 @@ enum { _Static_assert(MenuLayerColor_Count == 2, "Bad enum MenuLayerColor"); #endif +enum { + MenuLayerNoRepeatScrolling = 0, + MenuLayerRepeatScrollingUp = 1, + MenuLayerRepeatScrollingDown = 2, +}; + //! Data structure of a MenuLayer. //! @note a `MenuLayer *` can safely be casted to a `Layer *` and //! `ScrollLayer *` and can thus be used with all other functions that take a @@ -372,6 +365,8 @@ typedef struct MenuLayer { //! @internal //! Cell index + geometry cache of a cell that was in frame during the last redraw MenuCellSpan cursor; + + uint8_t button_repeat_scrolling:2; } cache; //! @internal //! Selected cell index + geometery cache of the selected cell @@ -414,10 +409,24 @@ typedef struct MenuLayer { //! independent of the scrolling animation. bool selection_animation_disabled:1; + //! If true, the MenuLayer will be able to wrap around the first element and the last element + //! when scrolling. + bool scroll_wrap_around:1; + + //! If this is true alongside \ref scroll_wrap_around, the MenuLayer will be able to wrap around + //! even when the 'up' or 'down' button is held down. + bool scroll_force_wrap_on_repeat:1; + + //! If True, a vibration will occur when wrapping around. + bool scroll_vibe_on_wrap_around:1; + + //! If True, a vibration will occur when cursor is getting blocked at the top or bottom + bool scroll_vibe_on_blocked:1; + //! Add some padding to keep track of the \ref MenuLayer size budget. //! As long as the size stays within this budget, 2.x apps can safely use the 3.x MenuLayer type. //! When padding is removed, the assertion below should also be removed. - uint8_t padding[44]; + uint8_t padding[40]; } MenuLayer; //! Padding used below the last item in pixels @@ -625,6 +634,42 @@ bool menu_layer_get_center_focused(MenuLayer *menu_layer); //! @see \ref menu_layer_get_center_focused void menu_layer_set_center_focused(MenuLayer *menu_layer, bool center_focused); +//! True, if the \ref MenuLayer can wrap around the first and last element. +//! @see \ref menu_layer_set_scroll_wrap_around +bool menu_layer_get_scroll_wrap_around(MenuLayer *menu_layer); + +//! Controls if the \ref MenuLayer can wrap around from the first element to the last when going +//! up and from the last element to the first when going down. +//! Even enabled, wrap around will stay disabled when holding down the navigation buttons (up or down). +//! Defaults to false for every platform +//! @param menu_layer The menu layer for which to enable or disable the behavior. +//! @param scroll_wrap_around true = enable the wrap around, false = disable it. +//! @see \ref menu_layer_get_scroll_wrap_around +void menu_layer_set_scroll_wrap_around(MenuLayer *menu_layer, bool scroll_wrap_around); + +//! Return a number depending on scroll vibe behavior : +//! - 0 : no vibe will occur +//! - 1 : vibe will occur on wrap around +//! - 2 : vibe will occur when stuck at the top or bottom of the menu +//! @see \ref menu_layer_set_scroll_vibe_on_wrap +//! @see \ref menu_layer_set_scroll_vibe_on_blocked +uint8_t menu_layer_get_scroll_vibe_behavior(MenuLayer *menu_layer); + +//! Controls if the \ref MenuLayer wil generate a vibe when wrapping around. +//! Defaults to false for every platform +//! @param menu_layer The menu layer for which to enable or disable the behavior. +//! @param scroll_vibe_on_wrap true = enable the vibe on wrap around, false = disable it. +//! @see \ref menu_layer_get_scroll_vibe_behavior +//! @see \ref menu_layer_set_scroll_vibe_on_blocked +void menu_layer_set_scroll_vibe_on_wrap(MenuLayer *menu_layer, bool scroll_vibe_on_wrap); + +//! Controls if the \ref MenuLayer wil generate a vibe when blocked at the top or bottom. +//! Defaults to false for every platform +//! @param menu_layer The menu layer for which to enable or disable the behavior. +//! @param scroll_vibe_on_wrap true = enable the vibe on cursor block, false = disable it. +//! @see \ref menu_layer_get_scroll_vibe_behavior +//! @see \ref menu_layer_set_scroll_vibe_on_wrap +void menu_layer_set_scroll_vibe_on_blocked(MenuLayer *menu_layer, bool scroll_vibe_on_blocked); //! @} // end addtogroup MenuLayer //! @} // end addtogroup Layer diff --git a/src/fw/applib/ui/menu_layer_private.h b/src/fw/applib/ui/menu_layer_private.h index ca6b66fd2b..5138c4459e 100644 --- a/src/fw/applib/ui/menu_layer_private.h +++ b/src/fw/applib/ui/menu_layer_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/menu_layer_system_cells.c b/src/fw/applib/ui/menu_layer_system_cells.c index 3a2a82a966..43d2c67ede 100644 --- a/src/fw/applib/ui/menu_layer_system_cells.c +++ b/src/fw/applib/ui/menu_layer_system_cells.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "menu_layer.h" @@ -87,9 +74,11 @@ int16_t menu_cell_basic_horizontal_inset(void) { return prv_get_dimensions_for_runtime_platform_default_size()->horizontal_inset; } +#if PBL_RECT static int16_t prv_title_subtitle_left_margin(void) { return prv_get_dimensions_for_runtime_platform_default_size()->title_subtitle_left_margin; } +#endif static ALWAYS_INLINE GFont prv_get_cell_title_font(const MenuCellLayerConfig *config) { return config->title_font ?: system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); @@ -118,6 +107,7 @@ static ALWAYS_INLINE void prv_draw_icon(GContext *ctx, GBitmap *icon, const GRec graphics_draw_bitmap_in_rect(ctx, icon, icon_frame); } +#if PBL_RECT static void prv_menu_cell_basic_draw_custom_rect( GContext *ctx, const Layer *cell_layer, const MenuCellLayerConfig *config) { const GRect *bounds = &cell_layer->bounds; @@ -221,6 +211,7 @@ static void prv_menu_cell_basic_draw_custom_rect( config->overflow_mode, GTextAlignmentLeft, NULL); } } +#endif // PBL_RECT // This function duplicates `grect_inset()` but helps us save some stack space by using pointer // arguments and always inlining the function diff --git a/src/fw/applib/ui/number_window.c b/src/fw/applib/ui/number_window.c index bfb2887025..7a33cb0292 100644 --- a/src/fw/applib/ui/number_window.c +++ b/src/fw/applib/ui/number_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "number_window.h" @@ -26,7 +13,7 @@ #include #include -#if RECOVERY_FW || MANUFACTURING_FW +#if defined(CONFIG_RECOVERY_FW) || defined(CONFIG_MFG) #define NUMBER_FONT_KEY FONT_KEY_GOTHIC_24_BOLD #else #define NUMBER_FONT_KEY FONT_KEY_BITHAM_34_MEDIUM_NUMBERS diff --git a/src/fw/applib/ui/number_window.h b/src/fw/applib/ui/number_window.h index 1c1333d5cc..e8f30420a0 100644 --- a/src/fw/applib/ui/number_window.h +++ b/src/fw/applib/ui/number_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/option_menu_window.c b/src/fw/applib/ui/option_menu_window.c index bf4d67dbb1..b1d51a32a5 100644 --- a/src/fw/applib/ui/option_menu_window.c +++ b/src/fw/applib/ui/option_menu_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "option_menu_window.h" @@ -159,6 +146,15 @@ static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, vo } } +static void prv_selection_will_change_callback(MenuLayer *menu_layer, MenuIndex *new_index, + MenuIndex old_index, void *context) { + OptionMenu *option_menu = context; + if (option_menu->callbacks.selection_will_change) { + option_menu->callbacks.selection_will_change( + option_menu, new_index->row, old_index.row, option_menu->context); + } +} + static void prv_window_load(Window *window) { OptionMenu *option_menu = window_get_user_data(window); @@ -166,6 +162,7 @@ static void prv_window_load(Window *window) { .get_cell_height = prv_get_cell_height_callback, .get_num_rows = prv_get_num_rows_callback, .draw_row = prv_draw_row_callback, + .selection_will_change = prv_selection_will_change_callback, .select_click = prv_select_callback }); menu_layer_set_click_config_onto_window(&option_menu->menu_layer, window); diff --git a/src/fw/applib/ui/option_menu_window.h b/src/fw/applib/ui/option_menu_window.h index 63ba47f8e9..1c15375303 100644 --- a/src/fw/applib/ui/option_menu_window.h +++ b/src/fw/applib/ui/option_menu_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -34,8 +21,18 @@ typedef void (*OptionMenuUnloadCallback)(OptionMenu *option_menu, void *context) typedef uint16_t (*OptionMenuGetCellHeightCallback)(OptionMenu *option_menu, uint16_t row, bool selected, void *context); +typedef uint16_t (*OptionMenuSelectionChangedCallback)(OptionMenu *option_menu, uint16_t row, + bool selected, void *context); + +typedef void (*OptionMenuSelectionWillChangeCallback)(OptionMenu *option_menu, + uint16_t new_row, + uint16_t old_row, + void *context); + + typedef struct OptionMenuCallbacks { OptionMenuSelectCallback select; + OptionMenuSelectionWillChangeCallback selection_will_change; OptionMenuGetNumRowsCallback get_num_rows; OptionMenuDrawRowCallback draw_row; OptionMenuUnloadCallback unload; diff --git a/src/fw/applib/ui/path_layer.c b/src/fw/applib/ui/path_layer.c index d50b2bee73..11823328e9 100644 --- a/src/fw/applib/ui/path_layer.c +++ b/src/fw/applib/ui/path_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "path_layer.h" #include "applib/graphics/graphics.h" diff --git a/src/fw/applib/ui/path_layer.h b/src/fw/applib/ui/path_layer.h index 3d5882db3a..5269b0c789 100644 --- a/src/fw/applib/ui/path_layer.h +++ b/src/fw/applib/ui/path_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/applib/ui/preferred_durations.c b/src/fw/applib/ui/preferred_durations.c index c30619afaa..ff0e3c5802 100644 --- a/src/fw/applib/ui/preferred_durations.c +++ b/src/fw/applib/ui/preferred_durations.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "preferred_durations.h" diff --git a/src/fw/applib/ui/preferred_durations.h b/src/fw/applib/ui/preferred_durations.h index 4abb1dfad9..4f20cb45fb 100644 --- a/src/fw/applib/ui/preferred_durations.h +++ b/src/fw/applib/ui/preferred_durations.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/progress_layer.c b/src/fw/applib/ui/progress_layer.c index 38765a3349..2fd03dc3b6 100644 --- a/src/fw/applib/ui/progress_layer.c +++ b/src/fw/applib/ui/progress_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "progress_layer.h" @@ -51,7 +38,7 @@ void progress_layer_update_proc(ProgressLayer* progress_layer, GContext* ctx) { graphics_context_set_fill_color(ctx, progress_layer->foreground_color); graphics_fill_round_rect(ctx, &progress_bar, corner_radius, GCornersAll); -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 graphics_context_set_stroke_color(ctx, progress_layer->foreground_color); graphics_draw_round_rect(ctx, bounds, corner_radius); #endif diff --git a/src/fw/applib/ui/progress_layer.h b/src/fw/applib/ui/progress_layer.h index f43247d92b..aa0d1499b3 100644 --- a/src/fw/applib/ui/progress_layer.h +++ b/src/fw/applib/ui/progress_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/applib/ui/progress_window.c b/src/fw/applib/ui/progress_window.c index 0e12e5b640..709ef1abde 100644 --- a/src/fw/applib/ui/progress_window.c +++ b/src/fw/applib/ui/progress_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "progress_window.h" @@ -22,8 +9,8 @@ #include "applib/ui/window_private.h" #include "applib/ui/window_stack.h" #include "kernel/pbl_malloc.h" -#include "services/common/compositor/compositor_transitions.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/compositor/compositor_transitions.h" +#include "pbl/services/timeline/timeline_resources.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" @@ -85,7 +72,6 @@ static void prv_animation_stopped_failure(Animation *animation, bool finished, v } static void prv_schedule_progress_success_animation(ProgressWindow *data) { -#if !PLATFORM_TINTIN GRect beg = data->progress_layer.layer.bounds; GRect mid = beg; GRect end = beg; @@ -125,11 +111,6 @@ static void prv_schedule_progress_success_animation(ProgressWindow *data) { data->result_animation = animation; animation_schedule(animation); -#else - // Don't animate to a dot on old platforms, just finish immediately. - static const bool success = true; - prv_finished(data, success); -#endif } static void prv_schedule_progress_failure_animation(ProgressWindow *data, uint32_t timeline_res_id, @@ -150,7 +131,6 @@ static void prv_schedule_progress_failure_animation(ProgressWindow *data, uint32 data->is_peek_layer_used = true; } -#if !PLATFORM_TINTIN // Animate the progress bar out, by shrinking it's width from it's current size down to 0. // When this completes, prv_animation_stopped_failure will show the peek layer. GRect *start = &data->progress_layer.layer.frame; @@ -171,11 +151,6 @@ static void prv_schedule_progress_failure_animation(ProgressWindow *data, uint32 data->result_animation = animation; animation_schedule(animation); - -#else - // Don't animate, just show the peek layer immediately. - prv_show_peek_layer(data); -#endif } //////////////////////////// diff --git a/src/fw/applib/ui/progress_window.h b/src/fw/applib/ui/progress_window.h index f8cc46f8eb..236e237ffc 100644 --- a/src/fw/applib/ui/progress_window.h +++ b/src/fw/applib/ui/progress_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,8 +7,8 @@ #include "applib/ui/progress_layer.h" #include "applib/ui/window.h" #include "applib/ui/window_stack.h" -#include "apps/system_apps/timeline/peek_layer.h" -#include "services/common/evented_timer.h" +#include "apps/system/timeline/peek_layer.h" +#include "pbl/services/evented_timer.h" //! @file progress_window.h //! diff --git a/src/fw/applib/ui/property_animation.c b/src/fw/applib/ui/property_animation.c index bc0df72dbd..44e7c19b4d 100644 --- a/src/fw/applib/ui/property_animation.c +++ b/src/fw/applib/ui/property_animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "property_animation_private.h" #include "animation_interpolate.h" @@ -170,7 +157,6 @@ void property_animation_update_grect(PropertyAnimation *property_animation_h, // ----------------------------------------------------------------------------------------- -#if !defined(PLATFORM_TINTIN) void property_animation_update_gtransform(PropertyAnimation *property_animation_h, const uint32_t distance_normalized) { PBL_ASSERTN(!animation_private_using_legacy_2(NULL)); @@ -214,7 +200,6 @@ void property_animation_update_gtransform(PropertyAnimation *property_animation_ setter(property_animation->subject, result); } -#endif // ----------------------------------------------------------------------------------------- @@ -312,7 +297,6 @@ static void prv_init(PropertyAnimationPrivate *property_animation, property_animation->values.from.grect = from_value ? *((GRect*)from_value) : implementation->accessors.getter.grect(subject); -#if !PLATFORM_TINTIN } else if (property_animation->animation.implementation->update == (AnimationUpdateImplementation)property_animation_update_gtransform) { // NOTE: We are not exposing the GTransform in the public SDK, so the setter and getter @@ -324,7 +308,6 @@ static void prv_init(PropertyAnimationPrivate *property_animation, : getter(subject); property_animation->values.from.gtransform = from_value ? *((GTransform*)from_value) : getter(subject); -#endif } else if (property_animation->animation.implementation->update == (AnimationUpdateImplementation)property_animation_update_gcolor8) { diff --git a/src/fw/applib/ui/property_animation.h b/src/fw/applib/ui/property_animation.h index 71d63f566a..9b4e1a50a8 100644 --- a/src/fw/applib/ui/property_animation.h +++ b/src/fw/applib/ui/property_animation.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "animation.h" @@ -631,10 +618,6 @@ void property_animation_update_gpoint(PropertyAnimation *property_animation, void property_animation_update_grect(PropertyAnimation *property_animation, const uint32_t distance_normalized); -//! @internal -//! GTransform is not exported, so don't include it in tintin. When GTransform will become exported, -//! remove this from here, property_animation_update_gtransform and property_animation_create. -#if !defined(PLATFORM_TINTIN) //! Default update callback for a property animations to update a property of type GTransform. //! Assign this function to the `.base.update` callback field of your //! PropertyAnimationImplementation, in combination with a `.getter` and `.setter` accessors of @@ -648,7 +631,6 @@ void property_animation_update_grect(PropertyAnimation *property_animation, //! when the animation is being run. void property_animation_update_gtransform(PropertyAnimation *property_animation, const uint32_t distance_normalized); -#endif //! Default update callback for a property animations to update a property of type GColor8. //! Assign this function to the `.base.update` callback field of your diff --git a/src/fw/applib/ui/property_animation_private.h b/src/fw/applib/ui/property_animation_private.h index e9d5bc0457..bed84be64a 100644 --- a/src/fw/applib/ui/property_animation_private.h +++ b/src/fw/applib/ui/property_animation_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/qr_code.c b/src/fw/applib/ui/qr_code.c index 72d4ef39ad..17c58650b7 100644 --- a/src/fw/applib/ui/qr_code.c +++ b/src/fw/applib/ui/qr_code.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "qr_code.h" diff --git a/src/fw/applib/ui/qr_code.h b/src/fw/applib/ui/qr_code.h index 2b7f71fed7..351f4078f0 100644 --- a/src/fw/applib/ui/qr_code.h +++ b/src/fw/applib/ui/qr_code.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/recognizer/recognizer.c b/src/fw/applib/ui/recognizer/recognizer.c index 5a078f9200..abe44fa17b 100644 --- a/src/fw/applib/ui/recognizer/recognizer.c +++ b/src/fw/applib/ui/recognizer/recognizer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "recognizer.h" #include "recognizer_impl.h" diff --git a/src/fw/applib/ui/recognizer/recognizer.h b/src/fw/applib/ui/recognizer/recognizer.h index 2b74fd21af..26f26abc91 100644 --- a/src/fw/applib/ui/recognizer/recognizer.h +++ b/src/fw/applib/ui/recognizer/recognizer.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "recognizer_list.h" -#include "services/common/touch/touch_event.h" +#include "pbl/services/touch/touch_event.h" #include #include diff --git a/src/fw/applib/ui/recognizer/recognizer_impl.h b/src/fw/applib/ui/recognizer/recognizer_impl.h index b60078c96a..8da75ac056 100644 --- a/src/fw/applib/ui/recognizer/recognizer_impl.h +++ b/src/fw/applib/ui/recognizer/recognizer_impl.h @@ -1,23 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "recognizer.h" +#include #include #include diff --git a/src/fw/applib/ui/recognizer/recognizer_list.h b/src/fw/applib/ui/recognizer/recognizer_list.h index b624382a62..365f020a1a 100644 --- a/src/fw/applib/ui/recognizer/recognizer_list.h +++ b/src/fw/applib/ui/recognizer/recognizer_list.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/recognizer/recognizer_manager.c b/src/fw/applib/ui/recognizer/recognizer_manager.c index ec910da58b..fda7282a12 100644 --- a/src/fw/applib/ui/recognizer/recognizer_manager.c +++ b/src/fw/applib/ui/recognizer/recognizer_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "recognizer.h" #include "recognizer_list.h" @@ -22,7 +9,7 @@ #include "applib/applib_malloc.auto.h" #include "applib/ui/layer.h" #include "process_state/app_state/app_state.h" -#include "services/common/touch/touch_event.h" +#include "pbl/services/touch/touch_event.h" #include "system/passert.h" #include "util/list.h" @@ -288,8 +275,9 @@ void recognizer_manager_handle_touch_event(const TouchEvent *touch_event, void * if (touch_event->type == TouchEvent_Touchdown) { Layer *root = window_get_root_layer(manager->window); + GPoint touch_pos = GPoint(touch_event->x, touch_event->y); Layer *new_active_layer = manager->window ? layer_find_layer_containing_point(root, - &touch_event->start_pos) : NULL; + &touch_pos) : NULL; if (new_active_layer == root) { new_active_layer = NULL; } diff --git a/src/fw/applib/ui/recognizer/recognizer_manager.h b/src/fw/applib/ui/recognizer/recognizer_manager.h index 78b1ac625f..6c107f95ad 100644 --- a/src/fw/applib/ui/recognizer/recognizer_manager.h +++ b/src/fw/applib/ui/recognizer/recognizer_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/recognizer/recognizer_private.h b/src/fw/applib/ui/recognizer/recognizer_private.h index bb74264223..74cfe3b953 100644 --- a/src/fw/applib/ui/recognizer/recognizer_private.h +++ b/src/fw/applib/ui/recognizer/recognizer_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "recognizer_impl.h" #include "recognizer_manager.h" -#include "services/common/touch/touch_event.h" +#include "pbl/services/touch/touch_event.h" #include "util/list.h" #include diff --git a/src/fw/applib/ui/recognizer/tap.c b/src/fw/applib/ui/recognizer/tap.c index 6c7a975bbd..381e1e565d 100644 --- a/src/fw/applib/ui/recognizer/tap.c +++ b/src/fw/applib/ui/recognizer/tap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "tap.h" diff --git a/src/fw/applib/ui/recognizer/tap.h b/src/fw/applib/ui/recognizer/tap.h index 443d856db2..1aa67f89c4 100644 --- a/src/fw/applib/ui/recognizer/tap.h +++ b/src/fw/applib/ui/recognizer/tap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/rotate_bitmap_layer.c b/src/fw/applib/ui/rotate_bitmap_layer.c index 3910fa4c46..75921efef1 100644 --- a/src/fw/applib/ui/rotate_bitmap_layer.c +++ b/src/fw/applib/ui/rotate_bitmap_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "rotate_bitmap_layer.h" @@ -77,6 +64,10 @@ void rot_bitmap_layer_destroy(RotBitmapLayer *rot_bitmap_layer) { applib_free(rot_bitmap_layer); } +Layer* rot_bitmap_layer_get_layer(const RotBitmapLayer *rot_bitmap_layer) { + return &((RotBitmapLayer *)rot_bitmap_layer)->layer; +} + void rot_bitmap_layer_set_corner_clip_color(RotBitmapLayer *image, GColor color) { if (gcolor_equal(color, image->corner_clip_color)) { return; diff --git a/src/fw/applib/ui/rotate_bitmap_layer.h b/src/fw/applib/ui/rotate_bitmap_layer.h index bb7fdb7090..0dbe2e03f3 100644 --- a/src/fw/applib/ui/rotate_bitmap_layer.h +++ b/src/fw/applib/ui/rotate_bitmap_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -95,6 +82,14 @@ void rot_bitmap_layer_deinit(RotBitmapLayer *bitmap); //! @param bitmap The RotBitmapLayer to destroy. void rot_bitmap_layer_destroy(RotBitmapLayer *bitmap); +//! Gets the "root" Layer of the RotBitmapLayer, which is the parent for the sub- +//! layers used for its implementation. +//! @param rot_bitmap_layer Pointer to the RotBitmapLayer for which to get the "root" Layer +//! @return The "root" Layer of the RotBitmapLayer. +//! @internal +//! @note The result is always equal to `(Layer *) rot_bitmap_layer`. +Layer* rot_bitmap_layer_get_layer(const RotBitmapLayer *rot_bitmap_layer); + //! Defines what color to use in areas that are not covered by the source bitmap. //! By default this is \ref GColorClear. //! @param bitmap The RotBitmapLayer on which to change the corner clip color @@ -126,7 +121,7 @@ void rot_bitmap_set_src_ic(RotBitmapLayer *bitmap, GPoint ic); //! Sets the compositing mode of how the bitmap image is composited onto what has been drawn beneath the //! RotBitmapLayer. -//! By default this is \ref GCompOpAssign. +//! By default this is \ref GCompOpAssign, i.e. transparency disabled. //! The RotBitmapLayer is automatically marked dirty after this operation. //! @param bitmap The RotBitmapLayer on which to change the rotation //! @param mode The compositing mode to set diff --git a/src/fw/applib/ui/rotbmp_pair_layer.c b/src/fw/applib/ui/rotbmp_pair_layer.c index a64627d07f..789c222240 100644 --- a/src/fw/applib/ui/rotbmp_pair_layer.c +++ b/src/fw/applib/ui/rotbmp_pair_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "rotbmp_pair_layer.h" #include "system/logging.h" @@ -26,7 +13,7 @@ static void set_compositing(RotBmpPairLayer *pair) { void rotbmp_pair_layer_init(RotBmpPairLayer *pair, GBitmap *white, GBitmap *black) { if (white->bounds.size.w != black->bounds.size.w && white->bounds.size.h != black->bounds.size.h) { - PBL_LOG(LOG_LEVEL_ERROR, "rotbmp_pair inited with unmatching bitmaps"); + PBL_LOG_ERR("rotbmp_pair inited with unmatching bitmaps"); return; } diff --git a/src/fw/applib/ui/rotbmp_pair_layer.h b/src/fw/applib/ui/rotbmp_pair_layer.h index 31791a7ac3..b88b5c3ac9 100644 --- a/src/fw/applib/ui/rotbmp_pair_layer.h +++ b/src/fw/applib/ui/rotbmp_pair_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/scroll_layer.c b/src/fw/applib/ui/scroll_layer.c index a7294502c4..bb31bf9baf 100644 --- a/src/fw/applib/ui/scroll_layer.c +++ b/src/fw/applib/ui/scroll_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "scroll_layer.h" diff --git a/src/fw/applib/ui/scroll_layer.h b/src/fw/applib/ui/scroll_layer.h index 686de2de23..9a2208f433 100644 --- a/src/fw/applib/ui/scroll_layer.h +++ b/src/fw/applib/ui/scroll_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/selection_layer.c b/src/fw/applib/ui/selection_layer.c index 8d8644e646..3c56e5226a 100644 --- a/src/fw/applib/ui/selection_layer.c +++ b/src/fw/applib/ui/selection_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "selection_layer.h" @@ -430,14 +417,10 @@ static Animation* prv_create_bump_settle_animation(SelectionLayer *selection_lay } static void prv_run_value_change_animation(SelectionLayer *selection_layer) { -#if !PLATFORM_TINTIN Animation *bump_text = prv_create_bump_text_animation(selection_layer); Animation *bump_settle = prv_create_bump_settle_animation(selection_layer); selection_layer->value_change_animation = animation_sequence_create(bump_text, bump_settle, NULL); animation_schedule(selection_layer->value_change_animation); -#else - prv_update_cell_value(selection_layer, selection_layer->bump_is_upwards); -#endif } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -521,17 +504,12 @@ static Animation* prv_create_slide_settle_animation(SelectionLayer *selection_la } static void prv_run_slide_animation(SelectionLayer *selection_layer) { -#if !PLATFORM_TINTIN Animation *over_animation = prv_create_slide_animation(selection_layer); Animation *settle_animation = prv_create_slide_settle_animation(selection_layer); selection_layer->next_cell_animation = animation_sequence_create(over_animation, settle_animation, NULL); animation_schedule(selection_layer->next_cell_animation); -#else - selection_layer->selected_cell_idx++; - layer_mark_dirty(&selection_layer->layer); -#endif } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/fw/applib/ui/selection_layer.h b/src/fw/applib/ui/selection_layer.h index 90e4a77ea3..2c7df591a9 100644 --- a/src/fw/applib/ui/selection_layer.h +++ b/src/fw/applib/ui/selection_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/shadows.c b/src/fw/applib/ui/shadows.c index 187179a8db..2bfdf5bb0a 100644 --- a/src/fw/applib/ui/shadows.c +++ b/src/fw/applib/ui/shadows.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shadows.h" diff --git a/src/fw/applib/ui/shadows.h b/src/fw/applib/ui/shadows.h index 2b84c1958b..16f3e15a12 100644 --- a/src/fw/applib/ui/shadows.h +++ b/src/fw/applib/ui/shadows.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/simple_menu_layer.c b/src/fw/applib/ui/simple_menu_layer.c index 836f293518..33039e6961 100644 --- a/src/fw/applib/ui/simple_menu_layer.c +++ b/src/fw/applib/ui/simple_menu_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "simple_menu_layer.h" diff --git a/src/fw/applib/ui/simple_menu_layer.h b/src/fw/applib/ui/simple_menu_layer.h index a591fd9edb..cab0c911a3 100644 --- a/src/fw/applib/ui/simple_menu_layer.h +++ b/src/fw/applib/ui/simple_menu_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/speaker.c b/src/fw/applib/ui/speaker.c new file mode 100644 index 0000000000..20c3d32834 --- /dev/null +++ b/src/fw/applib/ui/speaker.c @@ -0,0 +1,103 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/ui/speaker.h" + +#include "applib/event_service_client.h" +#include "kernel/events.h" +#include "process_state/app_state/app_state.h" +#include "syscall/syscall.h" +#include "system/logging.h" + +bool speaker_play_notes(const SpeakerNote *notes, uint32_t num_notes, uint8_t volume) { + if (!notes || num_notes == 0) { + PBL_LOG_ERR("tried to play null or empty note sequence"); + return false; + } + + return sys_speaker_play_note_seq(notes, num_notes, 0 /* SpeakerPriorityApp */, volume); +} + +bool speaker_play_tone(uint16_t frequency_hz, uint32_t duration_ms, + uint8_t volume, SpeakerWaveform waveform) { + if (duration_ms > 10000) { + duration_ms = 10000; + } + + return sys_speaker_play_tone(frequency_hz, (uint16_t)duration_ms, + (uint8_t)waveform, 0 /* use global volume */, + 0 /* SpeakerPriorityApp */, volume); +} + +bool speaker_stream_open(SpeakerPcmFormat format, uint8_t volume) { + return sys_speaker_stream_open(0 /* SpeakerPriorityApp */, volume, (uint8_t)format); +} + +uint32_t speaker_stream_write(const void *data, uint32_t num_bytes) { + if (!data || num_bytes == 0) { + return 0; + } + + return sys_speaker_stream_write(data, num_bytes); +} + +void speaker_stream_close(void) { + sys_speaker_stream_close(); +} + +void speaker_stop(void) { + sys_speaker_stop(); +} + +void speaker_set_volume(uint8_t volume) { + sys_speaker_set_volume(volume); +} + +SpeakerStatus speaker_get_status(void) { + return (SpeakerStatus)sys_speaker_get_state(); +} + +bool speaker_is_muted(void) { + return sys_speaker_is_muted(); +} + +bool speaker_play_tracks(const SpeakerTrack *tracks, uint32_t num_tracks, uint8_t volume) { + if (!tracks || num_tracks == 0) { + PBL_LOG_ERR("tried to play null or empty track list"); + return false; + } + + return sys_speaker_play_tracks(tracks, num_tracks, 0 /* SpeakerPriorityApp */, volume); +} + +static void prv_finish_event_handler(PebbleEvent *e, void *context) { + SpeakerFinishedCallback cb = app_state_get_speaker_finish_handler(); + if (cb == NULL) { + return; + } + cb((SpeakerFinishReason)e->speaker.finish_reason, + app_state_get_speaker_finish_ctx()); +} + +void speaker_set_finish_callback(SpeakerFinishedCallback cb, void *ctx) { + EventServiceInfo *info = app_state_get_speaker_finish_event_info(); + SpeakerFinishedCallback prev = app_state_get_speaker_finish_handler(); + + if (cb) { + app_state_set_speaker_finish_handler(cb); + app_state_set_speaker_finish_ctx(ctx); + if (prev == NULL) { + // First registration — subscribe and tell the service to post events. + info->type = PEBBLE_SPEAKER_EVENT; + info->handler = prv_finish_event_handler; + event_service_client_subscribe(info); + sys_speaker_register_finish(); + } + } else { + if (prev != NULL) { + event_service_client_unsubscribe(info); + } + app_state_set_speaker_finish_handler(NULL); + app_state_set_speaker_finish_ctx(NULL); + } +} diff --git a/src/fw/applib/ui/speaker.h b/src/fw/applib/ui/speaker.h new file mode 100644 index 0000000000..d33a9f4ff7 --- /dev/null +++ b/src/fw/applib/ui/speaker.h @@ -0,0 +1,105 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/speaker/speaker_finish_reason.h" +#include "pbl/services/speaker/speaker_pcm_format.h" +#include "pbl/services/speaker/track.h" + +#include +#include + +//! @file speaker.h +//! @addtogroup UI +//! @{ +//! @addtogroup Speaker +//! \brief Controlling the speaker +//! +//! The Speaker API provides calls that let you play sounds through the watch's speaker. +//! You can play simple note sequences (melodies), single tones, or stream raw PCM audio. +//! +//! Note sequences are compact representations of melodies using MIDI-like note definitions, +//! supporting 4 basic waveforms: sine, square, triangle, and sawtooth. +//! +//! Raw PCM streaming allows apps to generate arbitrary audio in configurable formats. +//! @{ + +//! Speaker status +typedef enum { + SpeakerStatusIdle = 0, + SpeakerStatusPlaying, + SpeakerStatusDraining, +} SpeakerStatus; + +//! Callback invoked when playback finishes. +//! @param reason Why playback ended +//! @param ctx User context +typedef void (*SpeakerFinishedCallback)(SpeakerFinishReason reason, void *ctx); + +//! Play a sequence of notes on the speaker. +//! @param notes Array of SpeakerNote structs defining the melody +//! @param num_notes Number of notes in the array +//! @param volume Playback volume (0-100) +//! @return true if playback started successfully +bool speaker_play_notes(const SpeakerNote *notes, uint32_t num_notes, uint8_t volume); + +//! Play N monophonic tracks in parallel, mixed (polyphony). +//! @param tracks Array of track descriptors (notes + optional sample). +//! @param num_tracks Number of tracks. Must be >= 1 and <= 4. +//! @param volume Playback volume (0-100). +//! @return true if playback started successfully. +bool speaker_play_tracks(const SpeakerTrack *tracks, uint32_t num_tracks, uint8_t volume); + +//! Play a single tone on the speaker (convenience wrapper). +//! @param frequency_hz Tone frequency in Hz +//! @param duration_ms Tone duration in milliseconds (max 10000) +//! @param volume Playback volume (0-100) +//! @param waveform Waveform to use +//! @return true if playback started successfully +bool speaker_play_tone(uint16_t frequency_hz, uint32_t duration_ms, + uint8_t volume, SpeakerWaveform waveform); + +//! Open a raw PCM stream for app-generated audio. +//! @param format PCM format specifying sample rate and bit depth +//! @param volume Playback volume (0-100) +//! @return true if stream opened successfully +bool speaker_stream_open(SpeakerPcmFormat format, uint8_t volume); + +//! Write PCM data to the open stream. +//! @param data Buffer of PCM data in the format specified at open +//! @param num_bytes Number of bytes to write +//! @return Number of bytes actually written (may be less if buffer is full) +uint32_t speaker_stream_write(const void *data, uint32_t num_bytes); + +//! Close the PCM stream. Buffered data will be played before stopping. +void speaker_stream_close(void); + +//! Stop any active speaker playback immediately. +void speaker_stop(void); + +//! Set the speaker volume. +//! @param volume Volume level (0-100) +void speaker_set_volume(uint8_t volume); + +//! Get the current speaker status. +//! @return Current SpeakerStatus +SpeakerStatus speaker_get_status(void); + +//! Check whether the speaker is currently muted system-wide. The watch can +//! mute the speaker either always-on (Settings → Sounds & Haptics) or for +//! the duration of Quiet Time (Settings → Quiet Time). Apps cannot +//! override the mute, but they can use this to adapt UI or skip long +//! sounds the user won't hear. +//! @return true if the speaker is currently muted +bool speaker_is_muted(void); + +//! Register a callback invoked when speaker playback ends. +//! The callback runs on the app task. +//! @param cb Callback to invoke, or NULL to unregister. +//! @param ctx User context passed back to cb. +void speaker_set_finish_callback(SpeakerFinishedCallback cb, void *ctx); + +//! @} // end addtogroup Speaker +//! @} // end addtogroup UI diff --git a/src/fw/applib/ui/status_bar_layer.c b/src/fw/applib/ui/status_bar_layer.c index d10545373e..4e67eed65c 100644 --- a/src/fw/applib/ui/status_bar_layer.c +++ b/src/fw/applib/ui/status_bar_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "status_bar_layer.h" @@ -27,7 +14,7 @@ #include "applib/ui/window_stack.h" #include "kernel/ui/kernel_ui.h" #include "process_state/app_state/app_state.h" -#include "services/common/clock.h" +#include "pbl/services/clock.h" #include "syscall/syscall.h" #include "syscall/syscall_internal.h" #include "system/passert.h" @@ -40,14 +27,37 @@ typedef struct StatusBarTextFormat { GFont font; } StatusBarTextFormat; -static ALWAYS_INLINE StatusBarTextFormat prv_get_text_format(void) { +static ALWAYS_INLINE bool prv_mode_is_clock(StatusBarLayerMode mode) { + return mode == StatusBarLayerModeClock || + mode == StatusBarLayerModeClockBold || + mode == StatusBarLayerModeClockLargeBold; +} + +static ALWAYS_INLINE StatusBarTextFormat prv_get_text_format( + const StatusBarLayerConfig *config) { const PlatformType platform = process_manager_current_platform(); - const char *font_key = PBL_PLATFORM_SWITCH(platform, - /*aplite*/ FONT_KEY_GOTHIC_14, - /*basalt*/ FONT_KEY_GOTHIC_14, - /*chalk*/ FONT_KEY_GOTHIC_14, - /*diorite*/ FONT_KEY_GOTHIC_14, - /*emery*/ FONT_KEY_GOTHIC_18); + const StatusBarLayerMode mode = config ? config->mode : StatusBarLayerModeClock; + const char *font_key; + if (mode == StatusBarLayerModeClockLargeBold) { + font_key = PBL_PLATFORM_SWITCH(platform, + /*aplite*/ FONT_KEY_GOTHIC_18_BOLD, + /*basalt*/ FONT_KEY_GOTHIC_18_BOLD, + /*chalk*/ FONT_KEY_GOTHIC_18_BOLD, + /*diorite*/ FONT_KEY_GOTHIC_18_BOLD, + /*emery*/ FONT_KEY_GOTHIC_24_BOLD, + /*flint*/ FONT_KEY_GOTHIC_18_BOLD, + /*gabbro*/ FONT_KEY_GOTHIC_24_BOLD); + } else { + const bool bold = (mode == StatusBarLayerModeClockBold); + font_key = PBL_PLATFORM_SWITCH(platform, + /*aplite*/ bold ? FONT_KEY_GOTHIC_14_BOLD : FONT_KEY_GOTHIC_14, + /*basalt*/ bold ? FONT_KEY_GOTHIC_14_BOLD : FONT_KEY_GOTHIC_14, + /*chalk*/ bold ? FONT_KEY_GOTHIC_14_BOLD : FONT_KEY_GOTHIC_14, + /*diorite*/ bold ? FONT_KEY_GOTHIC_14_BOLD : FONT_KEY_GOTHIC_14, + /*emery*/ bold ? FONT_KEY_GOTHIC_18_BOLD : FONT_KEY_GOTHIC_18, + /*flint*/ bold ? FONT_KEY_GOTHIC_14_BOLD : FONT_KEY_GOTHIC_14, + /*gabbro*/ bold ? FONT_KEY_GOTHIC_18_BOLD : FONT_KEY_GOTHIC_18); + } return (StatusBarTextFormat) { .overflow_mode = GTextOverflowModeTrailingEllipsis, .text_alignment = GTextAlignmentCenter, @@ -55,8 +65,11 @@ static ALWAYS_INLINE StatusBarTextFormat prv_get_text_format(void) { }; } -static int prv_height(void) { +static int prv_height(const StatusBarLayerConfig *config) { const PlatformType platform = process_manager_current_platform(); + if (config && config->mode == StatusBarLayerModeClockLargeBold) { + return _STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT(platform); + } return _STATUS_BAR_LAYER_HEIGHT(platform); } @@ -65,7 +78,8 @@ static void prv_status_bar_layer_update_clock(StatusBarLayer *status_bar_layer); static void prv_tick_timer_handler_cb(PebbleEvent *e, void *cb_data); static void prv_status_bar_property_changed(struct Layer *layer) { - const int16_t height = prv_height(); + StatusBarLayer *status_bar_layer = (StatusBarLayer *)layer; + const int16_t height = prv_height(&status_bar_layer->config); if (layer->frame.size.h != height) { layer->frame.size.h = height; } @@ -103,7 +117,8 @@ void status_bar_layer_init(StatusBarLayer *status_bar_layer) { GContext *ctx = graphics_context_get_current_context(); const GSize current_framebuffer_size = graphics_context_get_framebuffer_size(ctx); - layer_init(&status_bar_layer->layer, &GRect(0, 0, current_framebuffer_size.w, prv_height())); + layer_init(&status_bar_layer->layer, + &GRect(0, 0, current_framebuffer_size.w, prv_height(NULL))); status_bar_layer->layer.update_proc = prv_status_bar_layer_render; status_bar_layer->layer.property_changed_proc = prv_status_bar_property_changed; @@ -256,6 +271,15 @@ void status_bar_layer_set_separator_mode(StatusBarLayer *status_bar_layer, layer_mark_dirty(&(status_bar_layer->layer)); } +void status_bar_layer_set_mode(StatusBarLayer *status_bar_layer, StatusBarLayerMode mode) { + PBL_ASSERTN(status_bar_layer); + status_bar_layer->config.mode = mode; + GRect frame = status_bar_layer->layer.frame; + frame.size.h = prv_height(&status_bar_layer->config); + layer_set_frame(&status_bar_layer->layer, &frame); + layer_mark_dirty(&status_bar_layer->layer); +} + void status_bar_layer_set_separator_load_percentage(StatusBarLayer *status_bar_layer, int16_t percentage) { PBL_ASSERTN(status_bar_layer); @@ -283,7 +307,7 @@ static void prv_status_bar_layer_update_clock(StatusBarLayer *status_bar_layer) // Callback for updating title text clock's time static void prv_tick_timer_handler_cb(PebbleEvent *e, void *cb_data) { StatusBarLayer *status_bar_layer = (StatusBarLayer *)cb_data; - if (status_bar_layer->config.mode != StatusBarLayerModeClock) { + if (!prv_mode_is_clock(status_bar_layer->config.mode)) { return; } struct tm currtime; @@ -297,12 +321,13 @@ static void prv_tick_timer_handler_cb(PebbleEvent *e, void *cb_data) { // Calculate position and renders text static void prv_status_bar_layer_render_text(GContext *ctx, + const StatusBarLayerConfig *config, int16_t min_x, int16_t max_x, int16_t min_y, int16_t max_y, char *data) { - const StatusBarTextFormat text_format = prv_get_text_format(); + const StatusBarTextFormat text_format = prv_get_text_format(config); const GFont font = text_format.font; const uint8_t font_height = fonts_get_font_height(font); const int16_t center = (max_x + min_x) / 2; @@ -312,8 +337,27 @@ static void prv_status_bar_layer_render_text(GContext *ctx, const int16_t width = 2 * MAX(left_width, right_width); // starting point of text needs to be half width left of the center. const int16_t x_start = center - width / 2; - // Position the text vertically offset from the bottom of the status bar - const int16_t y = min_y + max_y - (2 * STATUS_BAR_LAYER_SEPARATOR_Y_OFFSET) - font_height; + int16_t y; + if (config && config->mode == StatusBarLayerModeClockLargeBold) { + // Center vertically in the taller bar. (font_height - 4) / 4 compensates + // Gothic's top leading so the glyph looks optically centered; this can + // produce a slightly negative y, which the draw context clips (intentional + // — tune against QEMU screenshots if it reads high/low). + // gabbro's round display needs a few extra px so the time clears the curve. + const PlatformType platform = process_manager_current_platform(); + const int16_t large_bold_y_nudge = PBL_PLATFORM_SWITCH(platform, + /*aplite*/ 0, + /*basalt*/ 0, + /*chalk*/ 0, + /*diorite*/ 0, + /*emery*/ 0, + /*flint*/ 0, + /*gabbro*/ 3); + y = min_y + (max_y - min_y - font_height) / 2 - (font_height - 4) / 4 + large_bold_y_nudge; + } else { + // Default: bottom-aligned with separator-area padding. + y = min_y + max_y - (2 * STATUS_BAR_LAYER_SEPARATOR_Y_OFFSET) - font_height; + } graphics_draw_text(ctx, data, font, @@ -345,25 +389,25 @@ void status_bar_layer_render(GContext *ctx, const GRect *bounds, StatusBarLayerC graphics_context_set_text_color(ctx, config->foreground_color); graphics_context_set_compositing_mode(ctx, GCompOpSet); - // update title buffer with time if in StatusBarLayerModeClock - if (config->mode == StatusBarLayerModeClock) { + // update title buffer with time if in a clock mode + if (prv_mode_is_clock(config->mode)) { clock_copy_time_string(config->title_text_buffer, sizeof(config->title_text_buffer)); } if (config->mode != StatusBarLayerModeCustomText) { // draw center text graphics_context_set_compositing_mode(ctx, GCompOpAssign); - prv_status_bar_layer_render_text(ctx, x_offset_l, x_offset_r, y_offset_top, y_offset_bottom, - config->title_text_buffer); + prv_status_bar_layer_render_text(ctx, config, x_offset_l, x_offset_r, y_offset_top, + y_offset_bottom, config->title_text_buffer); } else { // TODO: here goes center text animations } // render info text - GFont info_font = prv_get_text_format().font; + GFont info_font = prv_get_text_format(config).font; // find width of info text GSize max_used_size = graphics_text_layout_get_max_used_size(ctx, config->info_text_buffer, info_font, - GRect(0, 0, 100, prv_height()), + GRect(0, 0, 100, prv_height(config)), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); @@ -372,6 +416,7 @@ void status_bar_layer_render(GContext *ctx, const GRect *bounds, StatusBarLayerC STATUS_BAR_LAYER_INFO_PADDING); int16_t info_text_right_offset = (int16_t) (x_offset_r - STATUS_BAR_LAYER_INFO_PADDING); prv_status_bar_layer_render_text(ctx, + config, info_text_left_offset, info_text_right_offset, y_offset_top, @@ -392,9 +437,9 @@ bool layer_is_status_bar_layer(Layer *layer) { int16_t status_layer_get_title_text_width(StatusBarLayer *status_bar_layer) { // other modes not supported - PBL_ASSERTN(status_bar_layer->config.mode == StatusBarLayerModeClock); + PBL_ASSERTN(prv_mode_is_clock(status_bar_layer->config.mode)); - const StatusBarTextFormat text_format = prv_get_text_format(); + const StatusBarTextFormat text_format = prv_get_text_format(&status_bar_layer->config); char time_text_buffer[TITLE_TEXT_BUFFER_SIZE]; clock_copy_time_string(time_text_buffer, sizeof(time_text_buffer)); GContext *ctx = graphics_context_get_current_context(); diff --git a/src/fw/applib/ui/status_bar_layer.h b/src/fw/applib/ui/status_bar_layer.h index 41c1db9f71..ac4628fccb 100644 --- a/src/fw/applib/ui/status_bar_layer.h +++ b/src/fw/applib/ui/status_bar_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/app_timer.h" @@ -36,11 +23,28 @@ /*basalt*/ 16, \ /*chalk*/ 24, \ /*diorite*/ 16, \ - /*emery*/ 20) + /*emery*/ 20, \ + /*flint*/ 16, \ + /*gabbro*/ 20) //! The fixed height of the status bar, including separator height #define STATUS_BAR_LAYER_HEIGHT _STATUS_BAR_LAYER_HEIGHT(PBL_PLATFORM_TYPE_CURRENT) +//! The height of the status bar when rendering the "Big & Bold" clock, including +//! separator height, for all platforms. +#define _STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT(plat) PBL_PLATFORM_SWITCH(plat, \ + /*aplite*/ 20, \ + /*basalt*/ 20, \ + /*chalk*/ 24, /* already tall enough; only the font grows */ \ + /*diorite*/ 20, \ + /*emery*/ 26, \ + /*flint*/ 20, \ + /*gabbro*/ 26) + +//! The "Big & Bold" status bar height for the current platform. +#define STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT \ + _STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT(PBL_PLATFORM_TYPE_CURRENT) + //! The min width of the status bar #define STATUS_BAR_LAYER_MIN_WIDTH 35 //! The distance from info_text to right edge of the status bar @@ -66,7 +70,12 @@ typedef enum { //! Indicates to the user that something is loading. May or may not manually revert when complete. StatusBarLayerModeLoading = 1, //! Custom text with an optional auto-revert to the default mode. - StatusBarLayerModeCustomText = 2 + StatusBarLayerModeCustomText = 2, + //! Same as StatusBarLayerModeClock but renders the time using a bold font. + StatusBarLayerModeClockBold = 3, + //! Same as StatusBarLayerModeClock but renders the time using a larger bold + //! font in a taller bar (the notification "Big & Bold" style). + StatusBarLayerModeClockLargeBold = 4, } StatusBarLayerMode; //! Values that are used to indicate the different status bar separator modes. @@ -222,6 +231,11 @@ void status_bar_layer_reset_info(StatusBarLayer *status_bar_layer); void status_bar_layer_set_separator_mode(StatusBarLayer *status_bar_layer, StatusBarLayerSeparatorMode mode); +//! Sets the mode, resizing the layer frame/bounds when the new mode has a different bar height. +//! @param status_bar_layer The StatusBarLayer to configure +//! @param mode The StatusBarLayerMode to set +void status_bar_layer_set_mode(StatusBarLayer *status_bar_layer, StatusBarLayerMode mode); + //! Gets the mode of the StatusBarLayer separator //! @param status_bar_layer The StatusBarLayer of which to get the separator mode //! @return The current mode of the separator diff --git a/src/fw/applib/ui/text_layer.c b/src/fw/applib/ui/text_layer.c index 7f884687d6..00002ce638 100644 --- a/src/fw/applib/ui/text_layer.c +++ b/src/fw/applib/ui/text_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "text_layer.h" #include "text_layer_flow.h" @@ -195,7 +182,7 @@ void text_layer_set_should_cache_layout(TextLayer *text_layer, bool should_cache text_layer->should_cache_layout = should_cache_layout; if (text_layer->should_cache_layout) { - PBL_LOG(LOG_LEVEL_DEBUG, "Init layout"); + PBL_LOG_DBG("Init layout"); graphics_text_layout_cache_init(&text_layer->layout_cache); } else { graphics_text_layout_cache_deinit(&text_layer->layout_cache); diff --git a/src/fw/applib/ui/text_layer.h b/src/fw/applib/ui/text_layer.h index 8a1550b1bb..cd14392c60 100644 --- a/src/fw/applib/ui/text_layer.h +++ b/src/fw/applib/ui/text_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/text_layer_flow.c b/src/fw/applib/ui/text_layer_flow.c index 611326f0f1..2334f4789f 100644 --- a/src/fw/applib/ui/text_layer_flow.c +++ b/src/fw/applib/ui/text_layer_flow.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "text_layer_flow.h" #include "scroll_layer.h" diff --git a/src/fw/applib/ui/text_layer_flow.h b/src/fw/applib/ui/text_layer_flow.h index 0b97216d91..f373eed92e 100644 --- a/src/fw/applib/ui/text_layer_flow.h +++ b/src/fw/applib/ui/text_layer_flow.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/time_range_selection_window.c b/src/fw/applib/ui/time_range_selection_window.c index d1121c48b3..85008cda8d 100644 --- a/src/fw/applib/ui/time_range_selection_window.c +++ b/src/fw/applib/ui/time_range_selection_window.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "time_range_selection_window.h" #include "date_time_selection_window_private.h" #include "process_management/process_manager.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" #include "shell/system_theme.h" #include diff --git a/src/fw/applib/ui/time_range_selection_window.h b/src/fw/applib/ui/time_range_selection_window.h index 4c2f7e23a0..137baa3d61 100644 --- a/src/fw/applib/ui/time_range_selection_window.h +++ b/src/fw/applib/ui/time_range_selection_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/time_selection_window.c b/src/fw/applib/ui/time_selection_window.c index e609bfed08..6d9b6ab8db 100644 --- a/src/fw/applib/ui/time_selection_window.c +++ b/src/fw/applib/ui/time_selection_window.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "time_selection_window.h" #include "applib/ui/option_menu_window.h" -#include "services/common/clock.h" +#include "pbl/services/clock.h" #include "shell/system_theme.h" #include diff --git a/src/fw/applib/ui/time_selection_window.h b/src/fw/applib/ui/time_selection_window.h index 2c31e8ade1..af5a4a51a6 100644 --- a/src/fw/applib/ui/time_selection_window.h +++ b/src/fw/applib/ui/time_selection_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -22,7 +9,7 @@ #include "applib/ui/status_bar_layer.h" #include "applib/ui/text_layer.h" #include "applib/ui/window.h" -#include "services/common/clock.h" +#include "pbl/services/clock.h" #include diff --git a/src/fw/applib/ui/ui.h b/src/fw/applib/ui/ui.h index 5867baa06e..f188d22dd7 100644 --- a/src/fw/applib/ui/ui.h +++ b/src/fw/applib/ui/ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window.h" #include "app_window_stack.h" diff --git a/src/fw/applib/ui/ui_debug.c b/src/fw/applib/ui/ui_debug.c index 3120ff892d..060efb1b16 100644 --- a/src/fw/applib/ui/ui_debug.c +++ b/src/fw/applib/ui/ui_debug.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#ifdef UI_DEBUG +#ifdef CONFIG_UI_DEBUG #include "ui.h" #include "applib/ui/app_window_stack.h" @@ -106,4 +93,4 @@ void command_dump_window(void) { } layer_dump_tree(window_get_root_layer(window)); } -#endif /* UI_DEBUG */ +#endif /* CONFIG_UI_DEBUG */ diff --git a/src/fw/applib/ui/ui_debug.h b/src/fw/applib/ui/ui_debug.h index b9b30d4a8f..fc03ffc9f8 100644 --- a/src/fw/applib/ui/ui_debug.h +++ b/src/fw/applib/ui/ui_debug.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/vibes.c b/src/fw/applib/ui/vibes.c index eab6f3f457..b2481b2a36 100644 --- a/src/fw/applib/ui/vibes.c +++ b/src/fw/applib/ui/vibes.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/vibes.h" @@ -50,7 +37,7 @@ void vibes_cancel(void) { void vibes_enqueue_custom_pattern(VibePattern pattern) { if (pattern.durations == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "tried to enqueue a null pattern"); + PBL_LOG_ERR("tried to enqueue a null pattern"); return; } @@ -63,3 +50,18 @@ void vibes_enqueue_custom_pattern(VibePattern pattern) { sys_vibe_pattern_trigger_start(); } +void vibes_enqueue_custom_pattern_with_amplitudes(VibePatternWithAmplitudes pattern) { + if (pattern.durations == NULL || pattern.amplitudes == NULL) { + PBL_LOG_ERR("tried to enqueue a null pattern"); + return; + } + + for (uint32_t i = 0; i < pattern.num_segments; ++i) { + uint32_t amp = pattern.amplitudes[i]; + int32_t strength = (int32_t)(amp > 100 ? 100 : amp); + sys_vibe_pattern_enqueue_step_raw(pattern.durations[i], strength); + } + + sys_vibe_pattern_trigger_start(); +} + diff --git a/src/fw/applib/ui/vibes.h b/src/fw/applib/ui/vibes.h index a2c62b9ef4..c255e9d312 100644 --- a/src/fw/applib/ui/vibes.h +++ b/src/fw/applib/ui/vibes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -41,7 +28,7 @@ Example code: \code{.c} // Vibe pattern: ON for 200ms, OFF for 100ms, ON for 400ms: -static const uint32_t const segments[] = { 200, 100, 400 }; +static const uint32_t segments[] = { 200, 100, 400 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), @@ -62,6 +49,42 @@ typedef struct { uint32_t num_segments; } VibePattern; +/** Data structure describing a vibration pattern with per-segment amplitude control. + Each segment has a duration and an amplitude. Amplitude 0 means no vibration + (off), and 100 means maximum strength. Values above 100 are clamped. + + Example code: + \code{.c} + // Ramp-down pattern: 100% for 200ms, 50% for 200ms, 25% for 200ms: + static const uint32_t segments[] = { 200, 200, 200 }; + static const uint32_t amplitudes[] = { 100, 50, 25 }; + VibePatternWithAmplitudes pat = { + .durations = segments, + .amplitudes = amplitudes, + .num_segments = ARRAY_LENGTH(segments), + }; + vibes_enqueue_custom_pattern_with_amplitudes(pat); + \endcode + @see vibes_enqueue_custom_pattern_with_amplitudes + */ +typedef struct { + /** + Pointer to an array of segment durations, measured in milli-seconds. + The maximum allowed duration is 10000ms. + */ + const uint32_t *durations; + /** + Pointer to an array of per-segment amplitudes (0-100). + Must have the same length as the durations array. + 0 means no vibration; 100 means maximum strength. + */ + const uint32_t *amplitudes; + /** + The length of the durations and amplitudes arrays. + */ + uint32_t num_segments; +} VibePatternWithAmplitudes; + //! Cancel any in-flight vibe patterns; this is a no-op if there is no //! on-going vibe. void vibes_cancel(void); @@ -76,10 +99,17 @@ void vibes_long_pulse(void); //! void vibes_double_pulse(void); -//! Makes the watch emit a ‘custom’ vibration pattern. +//! Makes the watch emit a 'custom' vibration pattern. //! @param pattern An arbitrary vibration pattern //! @see VibePattern void vibes_enqueue_custom_pattern(VibePattern pattern); +//! Makes the watch emit a 'custom' vibration pattern with per-segment amplitude control. +//! Unlike vibes_enqueue_custom_pattern, this function bypasses the system vibration +//! intensity preference and uses the provided amplitude values directly. +//! @param pattern An arbitrary vibration pattern with amplitudes +//! @see VibePatternWithAmplitudes +void vibes_enqueue_custom_pattern_with_amplitudes(VibePatternWithAmplitudes pattern); + //! @} // end addtogroup Vibes //! @} // end addtogroup UI diff --git a/src/fw/applib/ui/window.c b/src/fw/applib/ui/window.c index cfc404c31b..8672c99963 100644 --- a/src/fw/applib/ui/window.c +++ b/src/fw/applib/ui/window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window_private.h" @@ -182,11 +169,11 @@ GRect window_calc_frame(bool fullscreen) { // an alternate function for initializing the window that takes a frame dimension too. void window_init(Window *window, const char* debug_name) { if (window == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Tried to init a NULL window"); + PBL_LOG_ERR("Tried to init a NULL window"); return; } *window = (Window){}; -#ifndef RELEASE +#ifndef CONFIG_RELEASE window->debug_name = debug_name; #else (void)debug_name; @@ -300,6 +287,7 @@ void window_single_click_subscribe(ButtonId button_id, ClickHandler handler) { void window_single_repeating_click_subscribe(ButtonId button_id, uint16_t repeat_interval_ms, ClickHandler handler) { prv_check_is_in_click_config_provider(window_manager_get_top_window(), "subscribe"); if (button_id == BUTTON_ID_BACK) { + PBL_LOG_ERR("Cannot register BUTTON_ID_BACK repeating click handler"); return; } ClickManager *mgr = prv_get_current_click_manager(); @@ -335,6 +323,7 @@ void window_long_click_subscribe(ButtonId button_id, uint16_t delay_ms, ClickHan // long-pressing the back button a normal interaction method, and users may // unintentionally hold the button too long and force-quit the app. if (app_install_id_from_app_db(sys_process_manager_get_current_process_id())) { + PBL_LOG_ERR("Cannot register BUTTON_ID_BACK long click handler"); return; } else { Window *window = window_manager_get_top_window(); @@ -352,7 +341,7 @@ void window_long_click_subscribe(ButtonId button_id, uint16_t delay_ms, ClickHan void window_raw_click_subscribe(ButtonId button_id, ClickHandler down_handler, ClickHandler up_handler, void *context) { prv_check_is_in_click_config_provider(window_manager_get_top_window(), "subscribe"); if (button_id == BUTTON_ID_BACK) { - PBL_LOG(LOG_LEVEL_DEBUG, "Cannot register BUTTON_ID_BACK raw handler"); + PBL_LOG_ERR("Cannot register BUTTON_ID_BACK raw handler"); return; } ClickManager *mgr = prv_get_current_click_manager(); @@ -513,7 +502,7 @@ bool window_is_focusable(Window *window) { } const char* window_get_debug_name(Window *window) { -#ifndef RELEASE +#ifndef CONFIG_RELEASE return window->debug_name; #else return "?"; diff --git a/src/fw/applib/ui/window.h b/src/fw/applib/ui/window.h index 850fcbb700..7dac4d10f3 100644 --- a/src/fw/applib/ui/window.h +++ b/src/fw/applib/ui/window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -162,7 +149,7 @@ typedef struct Window { const char* debug_name; } Window; -#ifdef RELEASE +#ifdef CONFIG_RELEASE #define WINDOW_NAME(x) "" #else #define WINDOW_NAME(x) x @@ -394,7 +381,7 @@ void window_set_focusable(Window *window, bool focusable); bool window_is_focusable(Window *window); //! @internal -//! @return A name used to identify the window. If RELEASE is defined will just return "?" +//! @return A name used to identify the window. If CONFIG_RELEASE is defined will just return "?" //! @param window The window for which to get the debug name const char* window_get_debug_name(Window *window); diff --git a/src/fw/applib/ui/window_manager.c b/src/fw/applib/ui/window_manager.c index f6922cb8ee..cf98fe9985 100644 --- a/src/fw/applib/ui/window_manager.c +++ b/src/fw/applib/ui/window_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window_manager.h" diff --git a/src/fw/applib/ui/window_manager.h b/src/fw/applib/ui/window_manager.h index d062b9432b..9e517cdf4b 100644 --- a/src/fw/applib/ui/window_manager.h +++ b/src/fw/applib/ui/window_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/window_private.h b/src/fw/applib/ui/window_private.h index bb613110e8..3d4760e527 100644 --- a/src/fw/applib/ui/window_private.h +++ b/src/fw/applib/ui/window_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/window_stack.c b/src/fw/applib/ui/window_stack.c index 8eec64b45a..62d3640bad 100644 --- a/src/fw/applib/ui/window_stack.c +++ b/src/fw/applib/ui/window_stack.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window_stack.h" @@ -30,7 +17,7 @@ #include "kernel/ui/kernel_ui.h" #include "process_management/app_manager.h" #include "process_state/app_state/app_state.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" #include "syscall/syscall.h" #include "system/logging.h" #include "system/passert.h" @@ -236,7 +223,7 @@ static void prv_insert_with_function(WindowStack *window_stack_to, Window *windo prv_transition_to(window_from, window, transition_insert); } - PBL_LOG(LOG_LEVEL_DEBUG, "(+) %s=%p <%s>", is_app_window ? "window" : "modal window", + PBL_LOG_DBG("(+) %s=%p <%s>", is_app_window ? "window" : "modal window", window, window_get_debug_name(window)); } @@ -279,7 +266,7 @@ static Window *prv_remove_item(WindowStackItem *pop_item, // Store the window here, as we're potentially free'ing the item later on. Window *pop_item_window = pop_item->window; bool is_app_window = window_manager_is_app_window(pop_item_window); - PBL_LOG(LOG_LEVEL_DEBUG, "(-) %s=%p <%s>", is_app_window ? "window" : "modal window", + PBL_LOG_DBG("(-) %s=%p <%s>", is_app_window ? "window" : "modal window", pop_item_window, window_get_debug_name(pop_item_window)); // Only animate if the window was previously at the top of the stack and there's a @@ -352,7 +339,7 @@ Window *window_stack_pop(WindowStack *window_stack, bool animated) { Window *window_stack_pop_with_transition(WindowStack *window_stack, const WindowTransitionImplementation *transition) { if (window_stack->list_head == NULL) { - PBL_LOG(LOG_LEVEL_DEBUG, "Nothing to pop."); + PBL_LOG_DBG("Nothing to pop."); return NULL; } @@ -468,7 +455,7 @@ bool window_transition_context_has_legacy_window_to(WindowStack *stack, Window * void window_transition_context_disappear(WindowTransitioningContext *context) { Window *window_from = context->window_from; if (!window_from || window_manager_is_window_visible(window_from)) { - PBL_LOG(LOG_LEVEL_DEBUG, "No windows to unload from stack."); + PBL_LOG_DBG("No windows to unload from stack."); context->window_from = NULL; return; } diff --git a/src/fw/applib/ui/window_stack.h b/src/fw/applib/ui/window_stack.h index 98b58d7aa3..940c40d6aa 100644 --- a/src/fw/applib/ui/window_stack.h +++ b/src/fw/applib/ui/window_stack.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/window_stack_animation.c b/src/fw/applib/ui/window_stack_animation.c index c19e888313..427cf4b3e4 100644 --- a/src/fw/applib/ui/window_stack_animation.c +++ b/src/fw/applib/ui/window_stack_animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window_stack_animation.h" diff --git a/src/fw/applib/ui/window_stack_animation.h b/src/fw/applib/ui/window_stack_animation.h index 40023bd907..e39c24f129 100644 --- a/src/fw/applib/ui/window_stack_animation.h +++ b/src/fw/applib/ui/window_stack_animation.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/window_stack_animation_rect.c b/src/fw/applib/ui/window_stack_animation_rect.c index 41f46f47dc..7095b22bd0 100644 --- a/src/fw/applib/ui/window_stack_animation_rect.c +++ b/src/fw/applib/ui/window_stack_animation_rect.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window_stack_animation_rect.h" diff --git a/src/fw/applib/ui/window_stack_animation_rect.h b/src/fw/applib/ui/window_stack_animation_rect.h index a49f5bdec4..c4a13e1ffc 100644 --- a/src/fw/applib/ui/window_stack_animation_rect.h +++ b/src/fw/applib/ui/window_stack_animation_rect.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/ui/window_stack_animation_round.c b/src/fw/applib/ui/window_stack_animation_round.c index 134a0a5cb4..0c85d8aefe 100644 --- a/src/fw/applib/ui/window_stack_animation_round.c +++ b/src/fw/applib/ui/window_stack_animation_round.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "window_stack_animation_round.h" @@ -27,7 +14,7 @@ #include "applib/graphics/gtypes.h" #include "board/display.h" #include "kernel/ui/kernel_ui.h" -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_transitions.h" #include "system/passert.h" #include "util/attributes.h" #include "util/math.h" @@ -43,8 +30,6 @@ static uint16_t prv_window_distance_from_screen_bounds(const Window *window) { } static void prv_window_transition_render(WindowTransitioningContext *context, GContext *ctx) { - const WindowTransitionRoundImplementation *implementation = - (WindowTransitionRoundImplementation *)context->implementation; Window *window_to = context->window_to; if (window_to) { // move framebuffer by amount of pixels the window_to moves diff --git a/src/fw/applib/ui/window_stack_animation_round.h b/src/fw/applib/ui/window_stack_animation_round.h index cb445ad76a..6ba8d5c2b5 100644 --- a/src/fw/applib/ui/window_stack_animation_round.h +++ b/src/fw/applib/ui/window_stack_animation_round.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "window_stack_animation.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" typedef struct { WindowTransitionImplementation implementation; diff --git a/src/fw/applib/ui/window_stack_private.h b/src/fw/applib/ui/window_stack_private.h index 56a08ed707..25cd16b297 100644 --- a/src/fw/applib/ui/window_stack_private.h +++ b/src/fw/applib/ui/window_stack_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Private API for manipulating the Window Stack #pragma once diff --git a/src/fw/applib/unobstructed_area_service.c b/src/fw/applib/unobstructed_area_service.c index ef4a40f8a7..20bfe6c7f7 100644 --- a/src/fw/applib/unobstructed_area_service.c +++ b/src/fw/applib/unobstructed_area_service.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "unobstructed_area_service.h" #include "unobstructed_area_service_private.h" @@ -161,19 +148,31 @@ void unobstructed_area_service_subscribe(UnobstructedAreaState *state, PBL_ASSERTN(state && handlers); state->handlers = *handlers; state->context = context; + state->is_subscribed = true; } void unobstructed_area_service_unsubscribe(UnobstructedAreaState *state) { PBL_ASSERTN(state); state->handlers = (UnobstructedAreaHandlers) {}; + state->context = NULL; + state->is_subscribed = false; } void unobstructed_area_service_get_area(UnobstructedAreaState *state, GRect *area_out) { if (state && area_out) { + state->has_requested_area = true; *area_out = state->area; } } +bool unobstructed_area_service_is_subscribed(UnobstructedAreaState *state) { + return state ? state->is_subscribed : false; +} + +bool unobstructed_area_service_has_requested_area(UnobstructedAreaState *state) { + return state ? state->has_requested_area : false; +} + void app_unobstructed_area_service_subscribe(UnobstructedAreaHandlers handlers, void *context) { unobstructed_area_service_subscribe(app_state_get_unobstructed_area_state(), &handlers, context); } diff --git a/src/fw/applib/unobstructed_area_service.h b/src/fw/applib/unobstructed_area_service.h index af11a42664..1ab6d5030a 100644 --- a/src/fw/applib/unobstructed_area_service.h +++ b/src/fw/applib/unobstructed_area_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/unobstructed_area_service_private.h b/src/fw/applib/unobstructed_area_service_private.h index fadbc26bdc..e24de4c56c 100644 --- a/src/fw/applib/unobstructed_area_service_private.h +++ b/src/fw/applib/unobstructed_area_service_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -27,6 +14,7 @@ typedef struct UnobstructedAreaState { GRect area; void *context; bool is_subscribed; + bool has_requested_area; bool is_changing; } UnobstructedAreaState; @@ -47,6 +35,16 @@ void unobstructed_area_service_deinit(UnobstructedAreaState *state); //! @param area The GRect to write the unobstructed area to void unobstructed_area_service_get_area(UnobstructedAreaState *state, GRect *area); +//! @internal +//! Returns whether the app subscribed to unobstructed area changes. +//! @param state Unobstructed area state belonging to the consuming task +bool unobstructed_area_service_is_subscribed(UnobstructedAreaState *state); + +//! @internal +//! Returns whether the app has queried the unobstructed area. +//! @param state Unobstructed area state belonging to the consuming task +bool unobstructed_area_service_has_requested_area(UnobstructedAreaState *state); + //! @internal //! Subscribe to be notified when the app's unobstructed area changes. //! @param state Unobstructed area state belonging to the consuming task diff --git a/src/fw/applib/vendor/tinflate/wscript b/src/fw/applib/vendor/tinflate/wscript deleted file mode 100644 index 70daf8faec..0000000000 --- a/src/fw/applib/vendor/tinflate/wscript +++ /dev/null @@ -1,10 +0,0 @@ -def configure(conf): - pass - -def build(bld): - bld.stlib(source=['tinflate.c'], - export_includes='.', - target='tinflate', - use='pblibc fw_includes') - -# vim:filetype=python diff --git a/src/fw/applib/vendor/tinflate/wscript_build b/src/fw/applib/vendor/tinflate/wscript_build new file mode 100644 index 0000000000..26d3c21cf4 --- /dev/null +++ b/src/fw/applib/vendor/tinflate/wscript_build @@ -0,0 +1,6 @@ +bld.stlib(source=['tinflate.c'], + export_includes='.', + target='tinflate', + use='pblibc fw_includes') + +# vim:filetype=python diff --git a/src/fw/applib/vendor/uPNG/wscript b/src/fw/applib/vendor/uPNG/wscript deleted file mode 100644 index bda1434a38..0000000000 --- a/src/fw/applib/vendor/uPNG/wscript +++ /dev/null @@ -1,11 +0,0 @@ -def configure(conf): - pass - -def build(bld): - bld.stlib(source=['upng.c'], - includes='.', - target='upng', - export_includes='.', - use='pblibc libutil_includes fw_includes tinflate') - -# vim:filetype=python diff --git a/src/fw/applib/vendor/uPNG/wscript_build b/src/fw/applib/vendor/uPNG/wscript_build new file mode 100644 index 0000000000..198a6ec267 --- /dev/null +++ b/src/fw/applib/vendor/uPNG/wscript_build @@ -0,0 +1,7 @@ +bld.stlib(source=['upng.c'], + includes='.', + target='upng', + export_includes='.', + use='pblibc libutil_includes fw_includes tinflate') + +# vim:filetype=python diff --git a/src/fw/applib/voice/dictation_session.c b/src/fw/applib/voice/dictation_session.c index 2e2bcf7d3f..35c1515a60 100644 --- a/src/fw/applib/voice/dictation_session.c +++ b/src/fw/applib/voice/dictation_session.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dictation_session.h" #include "dictation_session_private.h" @@ -27,12 +14,12 @@ #include -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC static void prv_handle_transcription_result(PebbleEvent *e, void *context) { PBL_ASSERTN(context); - PBL_LOG(LOG_LEVEL_DEBUG, "Exiting with status code: %"PRId8, e->dictation.result); + PBL_LOG_DBG("Exiting with status code: %"PRId8, e->dictation.result); DictationSession *session = context; session->callback(session, e->dictation.result, e->dictation.text, session->context); @@ -67,7 +54,7 @@ static void prv_stop_session(DictationSession *session) { DictationSession *dictation_session_create(uint32_t buffer_size, DictationSessionStatusCallback callback, void *context) { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC if (!callback) { return NULL; } @@ -79,7 +66,7 @@ DictationSession *dictation_session_create(uint32_t buffer_size, bool from_app = (pebble_task_get_current() == PebbleTask_App) && !app_install_id_from_system(sys_process_manager_get_current_process_id()); if (from_app && !sys_system_pp_has_capability(CommSessionVoiceApiSupport)) { - PBL_LOG(LOG_LEVEL_INFO, "No phone connected or phone app does not support app-initiated " + PBL_LOG_INFO("No phone connected or phone app does not support app-initiated " "dictation sessions"); return NULL; } @@ -132,7 +119,7 @@ DictationSession *dictation_session_create(uint32_t buffer_size, } void dictation_session_destroy(DictationSession *session) { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC if (!session) { return; } @@ -151,7 +138,7 @@ void dictation_session_destroy(DictationSession *session) { } void dictation_session_enable_confirmation(DictationSession *session, bool is_enabled) { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC if (!session || session->in_progress) { return; } @@ -160,7 +147,7 @@ void dictation_session_enable_confirmation(DictationSession *session, bool is_en } void dictation_session_enable_error_dialogs(DictationSession *session, bool is_enabled) { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC if (!session || session->in_progress) { return; } @@ -169,7 +156,7 @@ void dictation_session_enable_error_dialogs(DictationSession *session, bool is_e } DictationSessionStatus dictation_session_start(DictationSession *session) { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC if (!session || session->in_progress) { return DictationSessionStatusFailureInternalError; } @@ -191,7 +178,7 @@ DictationSessionStatus dictation_session_start(DictationSession *session) { } DictationSessionStatus dictation_session_stop(DictationSession *session) { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC if (!session || !session->in_progress) { return DictationSessionStatusFailureInternalError; } diff --git a/src/fw/applib/voice/dictation_session.h b/src/fw/applib/voice/dictation_session.h index fa75ad2d33..eda9456968 100644 --- a/src/fw/applib/voice/dictation_session.h +++ b/src/fw/applib/voice/dictation_session.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -99,7 +86,7 @@ typedef void (*DictationSessionStatusCallback)(DictationSession *session, //! Create a dictation session. The session object can be used more than once to get a //! transcription. When a transcription is received a buffer will be allocated to store the text in -//! with a maximum size specified by \ref buffer_size. When a transcription and accepted by the user +//! with a maximum size specified by \ref buffer_size. When a transcription is accepted by the user //! or a failure of some sort occurs, the callback specified will be called with the status and the //! transcription if one was accepted. //! @param buffer_size size of buffer to allocate for the transcription text; text will be diff --git a/src/fw/applib/voice/dictation_session_private.h b/src/fw/applib/voice/dictation_session_private.h index 24948a8bdc..4e9abaf458 100644 --- a/src/fw/applib/voice/dictation_session_private.h +++ b/src/fw/applib/voice/dictation_session_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/voice/loading_layer.c b/src/fw/applib/voice/loading_layer.c index f9854f80be..593737a435 100644 --- a/src/fw/applib/voice/loading_layer.c +++ b/src/fw/applib/voice/loading_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "loading_layer.h" diff --git a/src/fw/applib/voice/loading_layer.h b/src/fw/applib/voice/loading_layer.h index 8a8be5a1fd..0cce9c580e 100644 --- a/src/fw/applib/voice/loading_layer.h +++ b/src/fw/applib/voice/loading_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/voice/transcription_dialog.c b/src/fw/applib/voice/transcription_dialog.c index 09c45b00bb..9f05b3a71b 100644 --- a/src/fw/applib/voice/transcription_dialog.c +++ b/src/fw/applib/voice/transcription_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "transcription_dialog.h" @@ -27,6 +14,7 @@ #include "applib/ui/scroll_layer.h" #include "kernel/ui/kernel_ui.h" #include "resource/resource_ids.auto.h" +#include "shell/system_theme.h" #include "system/passert.h" #include @@ -262,6 +250,8 @@ void transcription_dialog_init(TranscriptionDialog *transcription_dialog) { *transcription_dialog = (TranscriptionDialog){}; expandable_dialog_init((ExpandableDialog *)transcription_dialog, "Transcription Dialog"); + expandable_dialog_set_body_font((ExpandableDialog *)transcription_dialog, + system_theme_get_font(TextStyleFont_Body)); expandable_dialog_set_select_action((ExpandableDialog *)transcription_dialog, RESOURCE_ID_ACTION_BAR_ICON_CHECK, prv_transcription_dialog_select_handler); diff --git a/src/fw/applib/voice/transcription_dialog.h b/src/fw/applib/voice/transcription_dialog.h index be3af447b0..4c06e75e4e 100644 --- a/src/fw/applib/voice/transcription_dialog.h +++ b/src/fw/applib/voice/transcription_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/voice/voice_window.c b/src/fw/applib/voice/voice_window.c index 74b6673dbd..bbd58b7482 100644 --- a/src/fw/applib/voice/voice_window.c +++ b/src/fw/applib/voice/voice_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "voice_window.h" #include "voice_window_private.h" @@ -54,9 +41,9 @@ #include "process_state/app_state/app_state.h" #include "process_management/app_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/comm_session/session.h" -#include "services/normal/voice/voice.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/event_service.h" +#include "pbl/services/voice/voice.h" #include "syscall/syscall.h" #include "system/logging.h" #include "system/passert.h" @@ -93,8 +80,6 @@ static const uint32_t UNFOLD_DURATION = 500; -#define VOICE_LOG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_VOICE, LOG_LEVEL_DEBUG, fmt, ## args) - static void prv_start_dictation(VoiceUiData *data); static void prv_stop_dictation(VoiceUiData *data); static void prv_cancel_dictation(VoiceUiData *data); @@ -107,9 +92,6 @@ static void prv_handle_stop_transition(VoiceUiData *data); static void prv_voice_window_push(VoiceUiData *data); char *sys_voice_get_transcription_from_event(PebbleVoiceServiceEvent *e, char *buffer, size_t buffer_size, size_t *sentence_len); -void sys_voice_analytics_log_event(AnalyticsEvent event_type, uint16_t response_size, - uint16_t response_len_chars, uint32_t response_len_ms, - uint8_t error_count, uint8_t num_sessions); @@ -120,31 +102,6 @@ static WindowStack *prv_get_window_stack(void) { return modal_manager_get_window_stack(ModalPriorityVoice); } -static void prv_put_analytics_event(VoiceUiData *data, bool success) { - size_t response_size = 0; - uint16_t response_len_chars = 0; - if (data->message) { - // If an error occurred and the owner of the voice window did not specify a buffer, - // data->message might be NULL - response_size = strlen(data->message); - utf8_t *cursor = (utf8_t *) data->message; - while ((cursor = utf8_get_next(cursor))) { - response_len_chars++; - } - } - - AnalyticsEvent event_type; - if (!data->show_confirmation_dialog) { - event_type = AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted; - } else if (success) { - event_type = AnalyticsEvent_VoiceTranscriptionAccepted; - } else { - event_type = AnalyticsEvent_VoiceTranscriptionRejected; - } - - sys_voice_analytics_log_event(event_type, response_size, response_len_chars, data->elapsed_ms, - data->error_count, data->num_sessions); -} static void prv_window_push(Window *window) { window_stack_push(prv_get_window_stack(), window, true /* animated */); @@ -170,7 +127,7 @@ static void prv_teardown(VoiceUiData *data) { } static void prv_exit_and_send_result_event(VoiceUiData *data, DictationSessionStatus result) { - VOICE_LOG("Send result"); + PBL_LOG_DBG("Send result"); PebbleEvent event = { .type = PEBBLE_DICTATION_EVENT, @@ -182,10 +139,6 @@ static void prv_exit_and_send_result_event(VoiceUiData *data, DictationSessionSt }; sys_send_pebble_event_to_kernel(&event); - if (data->num_sessions > 0) { - prv_put_analytics_event(data, (result == DictationSessionStatusSuccess)); - } - sys_light_reset_to_timed_mode(); prv_teardown(data); @@ -193,10 +146,10 @@ static void prv_exit_and_send_result_event(VoiceUiData *data, DictationSessionSt static void prv_handle_error_retries(VoiceUiData *data) { if (data->error_count < MAX_ERROR_COUNT) { - VOICE_LOG("Restarting dictation after error"); + PBL_LOG_DBG("Restarting dictation after error"); prv_start_dictation(data); } else { - VOICE_LOG("Too many errors! Exiting..."); + PBL_LOG_DBG("Too many errors! Exiting..."); prv_exit_and_send_result_event(data, data->error_exit_status); } } @@ -289,7 +242,7 @@ static void prv_handle_bt_conn_result(bool connected, void *context) { prv_voice_window_push(data); } } else { - VOICE_LOG("Bluetooth not restored! Exiting..."); + PBL_LOG_DBG("Bluetooth not restored! Exiting..."); prv_exit_and_send_result_event(data, DictationSessionStatusFailureConnectivityError); } } @@ -319,7 +272,7 @@ static void prv_update_analytics_metrics(VoiceUiData *data) { static void prv_dictation_timeout_cb(void *context) { VoiceUiData *data = context; - VOICE_LOG("Single session timeout"); + PBL_LOG_DBG("Single session timeout"); prv_stop_dictation(data); } @@ -339,11 +292,11 @@ static void prv_handle_ready_error(VoiceUiData *data) { } static void prv_handle_ready_event(VoiceUiData *data, PebbleVoiceServiceEvent *event) { - VOICE_LOG("Handling ready event"); + PBL_LOG_DBG("Handling ready event"); switch (event->status) { case VoiceStatusSuccess: - VOICE_LOG("Session setup successfully"); + PBL_LOG_DBG("Session setup successfully"); data->start_ms = prv_get_time_ms(); data->speech_detected = false; data->dictation_timeout = app_timer_register(DICTATION_TIMEOUT, prv_dictation_timeout_cb, @@ -374,7 +327,7 @@ static void prv_handle_ready_event(VoiceUiData *data, PebbleVoiceServiceEvent *e case VoiceStatusErrorGeneric: case VoiceStatusTimeout: - VOICE_LOG("Session setup error %d", event->status); + PBL_LOG_DBG("Session setup error %d", event->status); prv_handle_ready_error(data); break; default: @@ -398,11 +351,11 @@ static bool prv_handle_dictation_success(VoiceUiData *data, PebbleVoiceServiceEv } if (!data->message) { - VOICE_LOG("Empty sentence received"); + PBL_LOG_DBG("Empty sentence received"); return false; } - VOICE_LOG("New sentence: %s", data->message); + PBL_LOG_DBG("New sentence: %s", data->message); return true; } @@ -420,7 +373,7 @@ static void prv_handle_dictation_error(VoiceUiData *data, VoiceStatus error_stat } } else { if (!speech_detected) { - VOICE_LOG("No speech detected! Exiting..."); + PBL_LOG_DBG("No speech detected! Exiting..."); prv_exit_and_send_result_event(data, DictationSessionStatusFailureNoSpeechDetected); } else { prv_exit_and_send_result_event(data, DictationSessionStatusFailureRecognizerError); @@ -429,7 +382,7 @@ static void prv_handle_dictation_error(VoiceUiData *data, VoiceStatus error_stat } static void prv_handle_dictation_result(VoiceUiData *data, PebbleVoiceServiceEvent *event) { - VOICE_LOG("Handling result event"); + PBL_LOG_DBG("Handling result event"); data->session_id = VOICE_SESSION_ID_INVALID; bool success = false; switch (event->status) { @@ -453,17 +406,17 @@ static void prv_handle_dictation_result(VoiceUiData *data, PebbleVoiceServiceEve break; case VoiceStatusErrorGeneric: - VOICE_LOG("Result: error %"PRId8, event->status); + PBL_LOG_DBG("Result: error %"PRId8, event->status); prv_handle_dictation_error(data, event->status); break; case VoiceStatusRecognizerResponseError: - VOICE_LOG("Result: speech not recognized"); + PBL_LOG_DBG("Result: speech not recognized"); prv_handle_dictation_error(data, event->status); break; case VoiceStatusTimeout: - VOICE_LOG("Result: timeout"); + PBL_LOG_DBG("Result: timeout"); if (!connection_service_peek_pebble_app_connection()) { data->error_count++; if (data->error_count < MAX_ERROR_COUNT) { @@ -490,7 +443,6 @@ static VoiceUiState prv_get_simple_state(VoiceUiState state) { StateStart, StateWaitForReady, // StateStartWaitForReady StateWaitForReady, - StateRecording, // StateStopWaitForReady StateRecording, StateWaitForResponse, // StateStopRecording StateWaitForResponse, @@ -512,7 +464,7 @@ static void prv_voice_event_handler(PebbleEvent *e, void *context) { VoiceUiData *data = context; VoiceUiState simple_state = prv_get_simple_state(data->state); - VOICE_LOG("Event received: %"PRIu8"; state:%"PRIu8, event->type, simple_state); + PBL_LOG_DBG("Event received: %"PRIu8"; state:%"PRIu8, event->type, simple_state); switch (simple_state) { case StateWaitForReady: if (event->type == VoiceEventTypeSessionSetup) { @@ -528,11 +480,11 @@ static void prv_voice_event_handler(PebbleEvent *e, void *context) { case StateRecording: if (event->type == VoiceEventTypeSilenceDetected) { - VOICE_LOG("Silence detected"); + PBL_LOG_DBG("Silence detected"); prv_stop_dictation(data); } if (event->type == VoiceEventTypeSpeechDetected) { - VOICE_LOG("Speech detected"); + PBL_LOG_DBG("Speech detected"); data->speech_detected = true; } if (event->type == VoiceEventTypeSessionResult) { @@ -549,7 +501,7 @@ static void prv_voice_event_handler(PebbleEvent *e, void *context) { case StateFinished: case StateExiting: // Discard event - VOICE_LOG("Ignoring event"); + PBL_LOG_DBG("Ignoring event"); break; default: WTF; @@ -557,11 +509,11 @@ static void prv_voice_event_handler(PebbleEvent *e, void *context) { } static void prv_start_dictation(VoiceUiData *data) { - VOICE_LOG("Start dictation session"); + PBL_LOG_DBG("Start dictation session"); PBL_ASSERTN(data->session_id == VOICE_SESSION_ID_INVALID); data->session_id = sys_voice_start_dictation(data->session_type); if (data->session_id == VOICE_SESSION_ID_INVALID) { - PBL_LOG(LOG_LEVEL_ERROR, "Dictation session failed to start"); + PBL_LOG_ERR("Dictation session failed to start"); prv_exit_and_send_result_event(data, DictationSessionStatusFailureInternalError); return; } @@ -573,7 +525,7 @@ static void prv_start_dictation(VoiceUiData *data) { } static void prv_stop_dictation(VoiceUiData *data) { - VOICE_LOG("Stop dictation and wait for result"); + PBL_LOG_DBG("Stop dictation and wait for result"); sys_voice_stop_dictation(data->session_id); prv_set_mic_window_state(data, StateWaitForResponse); prv_update_analytics_metrics(data); @@ -583,7 +535,7 @@ static void prv_stop_dictation(VoiceUiData *data) { static void prv_cancel_dictation(VoiceUiData *data) { if ((data->state != StateStart) && (data->state != StateFinished) && (data->state != StateExiting) && (data->state != StateError)) { - VOICE_LOG("Cancel dictation session"); + PBL_LOG_DBG("Cancel dictation session"); sys_voice_cancel_dictation(data->session_id); data->session_id = VOICE_SESSION_ID_INVALID; app_timer_cancel(data->dictation_timeout); @@ -922,7 +874,7 @@ static void prv_mic_click_handler(ClickRecognizerRef recognizer, void *context) VoiceUiData *data = context; ButtonId button_id = click_recognizer_get_button_id(recognizer); if (button_id == BUTTON_ID_BACK) { - VOICE_LOG("Exit UI"); + PBL_LOG_DBG("Exit UI"); prv_cancel_dictation(data); DictationSessionStatus status = DictationSessionStatusFailureTranscriptionRejected; if ((data->error_count > 0) && !data->last_session_successful) { @@ -975,10 +927,6 @@ static void prv_handle_stop_transition(VoiceUiData *data) { prv_set_mic_window_state(data, StateWaitForReady); break; - case StateStopWaitForReady: - prv_set_mic_window_state(data, StateRecording); - break; - case StateRecording: // do nothing break; @@ -1032,7 +980,7 @@ static VoiceUiState prv_get_next_state(VoiceUiState current_state, VoiceUiState PBL_ASSERTN(next_state != StateStart); // Cannot transition to start state - VOICE_LOG("Transition: Current state: %d; new state: %d", current_state, next_state); + PBL_LOG_DBG("Transition: Current state: %d; new state: %d", current_state, next_state); // Transition will be handled by the animation stopped handler if defer_transition is set to true *defer_transition = false; @@ -1056,24 +1004,13 @@ static VoiceUiState prv_get_next_state(VoiceUiState current_state, VoiceUiState break; case StateStartWaitForReady: - if (next_state == StateRecording) { - *defer_transition = true; - // Spoof the state to StateStopForReady so the next transition takes us to - // StateRecording - return StateStopWaitForReady; - } else if (next_state == StateWaitForReady) { + if (next_state == StateWaitForReady || next_state == StateRecording) { return next_state; } break; case StateWaitForReady: if (next_state == StateRecording) { - return StateStopWaitForReady; - } - break; - - case StateStopWaitForReady: - if ((next_state == StateRecording) || (next_state == StateWaitForResponse)) { return next_state; } break; @@ -1138,7 +1075,7 @@ static VoiceUiState prv_get_next_state(VoiceUiState current_state, VoiceUiState // This handles all the microphone UI transitions static void prv_do_transition(VoiceUiData *data, VoiceUiState state) { - VOICE_LOG("Transition: %d -> %d", data->state, state); + PBL_LOG_DBG("Transition: %d -> %d", data->state, state); switch (state) { case StateStartWaitForReady: // Fly in dot @@ -1146,15 +1083,9 @@ static void prv_do_transition(VoiceUiData *data, VoiceUiState state) { break; case StateWaitForReady: - // Stop fly in animation. Start pulsing dot animation. Start progress bar animation + // Stop fly in animation. Start pulsing dot animation. prv_stop_fly_dot(data); prv_show_mic_dot_pulse(data); - prv_show_progress_bar(data, true /* animated */); - break; - - case StateStopWaitForReady: - // Shrink progress bar - prv_shrink_progress_bar(data); break; // TODO: Create an intermediate state where the microphone unfolds and the vibe plays before @@ -1227,7 +1158,7 @@ static void prv_set_mic_window_state(VoiceUiData *data, VoiceUiState state) { } data->state = state; - VOICE_LOG("State: %d", data->state); + PBL_LOG_DBG("State: %d", data->state); } static void prv_mic_window_load(Window *window) { @@ -1350,6 +1281,10 @@ static void prv_mic_window_unload(Window *window) { static void prv_mic_window_disappear(Window *window) { VoiceUiData *data = window_get_user_data(window); + // prv_mic_window_appear pinned the backlight via sys_light_enable_respect_settings(true). + // Release it here so the appear/disappear pair is symmetric, even if a later + // exit path (prv_exit_and_send_result_event, voice_window_pop) never runs. + sys_light_reset_to_timed_mode(); if (data->state != StateError) { // Do not indicate that an error occurred when a session is interrupted by a window transition if (data->state != StateFinished) { @@ -1500,6 +1435,15 @@ DEFINE_SYSCALL(char *, sys_voice_get_transcription_from_event, PebbleVoiceServic syscall_assert_userspace_buffer(buffer, buffer_size); } syscall_assert_userspace_buffer(sentence_len, sizeof(*sentence_len)); + // `e->data` is a kernel pointer the event service handed to the app along + // with the event. The app can rewrite the field in its copy before calling + // us, so confirm it still references a buffer the kernel actually allocated + // — otherwise an app could aim it at kernel memory and turn the strlen/memcpy + // below into an arbitrary kernel read primitive. + if (!event_service_is_known_buffer(e->data)) { + PBL_LOG_ERR("Rejecting voice event with unknown data pointer %p", e->data); + syscall_failed(); + } } size_t len; @@ -1532,22 +1476,3 @@ DEFINE_SYSCALL(char *, sys_voice_get_transcription_from_event, PebbleVoiceServic return sentence; } -DEFINE_SYSCALL(void, sys_voice_analytics_log_event, AnalyticsEvent event_type, - uint16_t response_size, uint16_t response_len_chars, uint32_t response_len_ms, - uint8_t error_count, uint8_t num_sessions) { - - if ((event_type < AnalyticsEvent_VoiceTranscriptionAccepted) && - (event_type > AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted)) { - return; - } - - Uuid uuid; - if (pebble_task_get_current() == PebbleTask_App) { - uuid = app_manager_get_current_app_md()->uuid; - } else { - uuid = (Uuid)UUID_SYSTEM; - } - - analytics_event_voice_response(event_type, response_size, response_len_chars, response_len_ms, - error_count, num_sessions, &uuid); -} diff --git a/src/fw/applib/voice/voice_window.h b/src/fw/applib/voice/voice_window.h index 250781bf99..dea6a3cd26 100644 --- a/src/fw/applib/voice/voice_window.h +++ b/src/fw/applib/voice/voice_window.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "util/uuid.h" #include "applib/voice/dictation_session.h" -#include "services/normal/voice_endpoint.h" +#include "pbl/services/voice_endpoint.h" #include #include diff --git a/src/fw/applib/voice/voice_window_private.h b/src/fw/applib/voice/voice_window_private.h index f7bf9b52fe..6d075c0f1e 100644 --- a/src/fw/applib/voice/voice_window_private.h +++ b/src/fw/applib/voice/voice_window_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -40,7 +27,6 @@ typedef enum { StateStart, // Start state. Nothing happens StateStartWaitForReady, // Dot flies in StateWaitForReady, // Progress bar shows and animates, dot pulses - StateStopWaitForReady, // Progress bar shrinks, dot continues to animate StateRecording, // Microphone unfolds and text appears StateStopRecording, // Microphone folds up again and text disappears StateWaitForResponse, // Dot pulses, progress bar shown diff --git a/src/fw/applib/worker.c b/src/fw/applib/worker.c index 7d1cba238d..059af5475d 100644 --- a/src/fw/applib/worker.c +++ b/src/fw/applib/worker.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "worker.h" @@ -20,7 +7,7 @@ #include "process_state/worker_state/worker_state.h" #include "applib/event_service_client.h" #include "syscall/syscall.h" -#include "services/common/event_service.h" +#include "pbl/services/event_service.h" #include "system/logging.h" diff --git a/src/fw/applib/worker.h b/src/fw/applib/worker.h index 20a0239921..f9e44f7b19 100644 --- a/src/fw/applib/worker.h +++ b/src/fw/applib/worker.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/applib/wscript b/src/fw/applib/wscript deleted file mode 100644 index 61d25bfa2e..0000000000 --- a/src/fw/applib/wscript +++ /dev/null @@ -1,103 +0,0 @@ -from tools.pebble_sdk_platform import pebble_platforms, maybe_import_internal - -def configure(conf): - maybe_import_internal(conf.env) - - platform = pebble_platforms[conf.env.PLATFORM_NAME] - define = 'MAX_FONT_GLYPH_SIZE={}'.format(platform['MAX_FONT_GLYPH_SIZE']) - conf.env.append_value('DEFINES', [define]) - - -def _autogen_applib_sources(bld): - def applib_autogen(task): - import applib_malloc - # disable size checks for cutts - if bld.is_cutts(): - disable_size_checks = True - else: - disable_size_checks = False - applib_malloc.generate_files(task.inputs[0].abspath(), task.outputs[0].abspath(), - task.outputs[1].abspath(), bld.env.MIN_SDK_VERSION, - disable_size_checks) - - sources = ['applib_malloc.json'] - sources.extend(bld.path.parent.parent.parent.ant_glob('tools/applib_malloc*')) - applib_malloc_h = bld.path.get_bld().make_node('applib_malloc.auto.h') - applib_malloc_c = bld.path.get_bld().make_node('applib_malloc.auto.c') - bld(rule=applib_autogen, source=sources, target=[applib_malloc_h, applib_malloc_c]) - - return [applib_malloc_c] - - -def _get_bit_depth_excludes(bld): - bit_depths = [1, 8] - if bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert() or bld.is_obelix(): - depth = 8 - else: - depth = 1 - return ['**/{}_bit/**'.format(x) for x in bit_depths if x != depth] - - -def build(bld): - excludes = _get_bit_depth_excludes(bld) - excludes += ['vendor'] - - bld.recurse('vendor/tinflate') - bld.recurse('vendor/uPNG') - - if bld.variant == 'prf': - excludes += ['ui/dialogs/actionable_dialog.c', - 'ui/dialogs/bt_conn_dialog.c', - 'ui/dialogs/confirmation_dialog.c', - 'ui/dialogs/expandable_dialog.c', - 'ui/option_menu_window.c', - 'ui/progress_window.c', - 'ui/selection_layer.c', - 'ui/time_selection_window.c', - 'ui/time_range_selection_window.c', - 'voice/*'] - - if not bld.capability('HAS_JAVASCRIPT') or bld.variant == 'prf': - excludes += ['rockyjs/*'] - - if not bld.is_spalding(): - excludes.append('ui/window_stack_animation_round*') - - if not bld.capability('HAS_MICROPHONE'): - excludes.append('voice/voice_window.c') - - if bld.capability('HAS_MAGNETOMETER'): - excludes.append('compass_service_stub.c') - else: - excludes.append('compass_service.c') - - if bld.variant in ('applib', 'test_rocky_emx'): - sources = bld.path.ant_glob('graphics/**/*.c', excl=excludes) - sources.extend(bld.path.ant_glob('fonts/*.c', excl=excludes)) - sources.extend(bld.path.ant_glob('rockyjs/**/*.c', excl=excludes)) - sources.append('ui/layer.c') - sources.append('ui/window.c') - else: - sources = bld.path.ant_glob('**/*.c', excl=excludes) - sources.extend(_autogen_applib_sources(bld)) - # extract strings for i18n - bld.gettext(source=sources, target='applib.pot') - - bld(export_includes=['.'], name='applib_includes') - - # Build the stlib - bld.stlib(source=sources, - target='applib', - use=['target_includes', - 'libbtutil_includes', - 'fw_includes', - 'upng', - 'qr_code_generator', - 'jerry_port_includes', - 'jerry_runtime_config', - 'jerry_common_config', - 'jerry_core', - 'freertos_includes', - 'root_includes']) - -# vim:filetype=python diff --git a/src/fw/applib/wscript_build b/src/fw/applib/wscript_build new file mode 100644 index 0000000000..aee8b35e75 --- /dev/null +++ b/src/fw/applib/wscript_build @@ -0,0 +1,75 @@ +def _autogen_applib_sources(bld): + def applib_autogen(task): + import applib_malloc + disable_size_checks = False + applib_malloc.generate_files(task.inputs[0].abspath(), task.outputs[0].abspath(), + task.outputs[1].abspath(), bld.env.MIN_SDK_VERSION, + disable_size_checks) + + sources = ['applib_malloc.json'] + sources.extend(bld.path.parent.parent.parent.ant_glob('tools/applib_malloc*')) + applib_malloc_h = bld.path.get_bld().make_node('applib_malloc.auto.h') + applib_malloc_c = bld.path.get_bld().make_node('applib_malloc.auto.c') + bld(rule=applib_autogen, source=sources, target=[applib_malloc_h, applib_malloc_c]) + + return [applib_malloc_c] + + +def _get_bit_depth_excludes(bld): + bit_depths = [1, 8] + depth = bld.env.CONFIG_SCREEN_COLOR_DEPTH_BITS + return ['**/{}_bit/**'.format(x) for x in bit_depths if x != depth] + + +excludes = _get_bit_depth_excludes(bld) +excludes += ['vendor'] + +bld.recurse('vendor/tinflate') +bld.recurse('vendor/uPNG') + +if bld.env.VARIANT == 'prf': + excludes += ['ui/dialogs/bt_conn_dialog.c', + 'ui/dialogs/expandable_dialog.c', + 'ui/option_menu_window.c', + 'ui/progress_window.c', + 'ui/selection_layer.c', + 'ui/date_selection_window.c', + 'ui/time_selection_window.c', + 'ui/time_range_selection_window.c', + 'voice/*'] + +if not bld.env.CONFIG_BOARD_FAMILY_GETAFIX and not bld.env.CONFIG_BOARD_QEMU_GABBRO: + excludes.append('ui/window_stack_animation_round*') + +if not bld.env.CONFIG_MIC: + excludes.append('voice/voice_window.c') + +if bld.env.CONFIG_MAG: + excludes.append('compass_service_stub.c') +else: + excludes.append('compass_service.c') + +if bld.env.CONFIG_TOUCH: + excludes.append('touch_service_stub.c') +else: + excludes.append('touch_service.c') + +sources = bld.path.ant_glob('**/*.c', excl=excludes) +sources.extend(_autogen_applib_sources(bld)) +# extract strings for i18n +bld.gettext(source=sources, target='applib.pot') + +bld(export_includes=['.'], name='applib_includes') + +# Build the stlib +bld.stlib(source=sources, + target='applib', + use=['libbtutil_includes', + 'fw_includes', + 'upng', + 'qr_code_generator', + 'libxs', + 'freertos_includes', + 'pbl_includes']) + +# vim:filetype=python diff --git a/src/fw/apps/Kconfig b/src/fw/apps/Kconfig new file mode 100644 index 0000000000..8b84e5975b --- /dev/null +++ b/src/fw/apps/Kconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menu "Applications" + +rsource "demo/Kconfig" + +endmenu \ No newline at end of file diff --git a/src/fw/apps/core/panic_window.c b/src/fw/apps/core/panic_window.c new file mode 100644 index 0000000000..389454154f --- /dev/null +++ b/src/fw/apps/core/panic_window.c @@ -0,0 +1,118 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/ui/window_private.h" +#include "applib/ui/app_window_stack.h" +#include "board/board.h" +#include "kernel/event_loop.h" +#include "kernel/panic.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/runlevel.h" +#include "system/passert.h" +#include "system/reset.h" + +#include + +static const uint8_t sad_watch[] = { + 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 0 - 16 */ + 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x57, 0xf5, 0xff, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0x57, 0xf5, 0xff, /* bytes 16 - 32 */ + 0xff, 0xa9, 0xca, 0xff, 0xff, 0x06, 0xb0, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0x7f, 0x06, 0x30, 0xff, /* bytes 32 - 48 */ + 0x7f, 0xfa, 0x2f, 0xff, 0x7f, 0xfa, 0x2f, 0xff, 0x7f, 0xaa, 0x2a, 0xff, 0xff, 0xda, 0xad, 0xff, /* bytes 48 - 64 */ + 0xff, 0xaa, 0x2a, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0x1a, 0x2c, 0xff, /* bytes 64 - 80 */ + 0xff, 0xea, 0xab, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0xfa, 0x2f, 0xff, /* bytes 80 - 96 */ + 0xff, 0x06, 0x20, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xff, 0x06, 0xb0, 0xff, /* bytes 96 - 112 */ + 0xff, 0xa9, 0xca, 0xff, 0xff, 0x57, 0xf5, 0xff, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0x57, 0xf5, 0xff, /* bytes 112 - 128 */ + 0xff, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +typedef struct PanicWindowAppData { + Window window; + Layer layer; +} PanicWindowAppData; + +static void prv_update_proc(Layer* layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpAssignInverted); + + GBitmap sad_watch_bitmap; + gbitmap_init_with_data(&sad_watch_bitmap, sad_watch); + + const GRect bitmap_dest_rect = GRect(56, 68, 32, 32); + graphics_draw_bitmap_in_rect(ctx, &sad_watch_bitmap, &bitmap_dest_rect); + + GFont error_code_face = fonts_get_system_font(FONT_KEY_GOTHIC_14); + const GRect text_dest_rect = GRect(38, 108, 70, 30); + + char text_buffer[11]; + snprintf(text_buffer, sizeof(text_buffer), "0x%"PRIx32, launcher_panic_get_current_error()); + + graphics_draw_text(ctx, text_buffer, error_code_face, + text_dest_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); +} + +static void prv_panic_reset_callback(void* data) { + RebootReason reason = { + .code = RebootReasonCode_LauncherPanic, + .extra = { .value = launcher_panic_get_current_error() } + }; + reboot_reason_set(&reason); + + system_reset(); +} + +static void prv_panic_button_click_handler(ClickRecognizerRef recognizer, void *context) { + launcher_task_add_callback(prv_panic_reset_callback, NULL); +} + +static void prv_panic_click_config_provider(void* context) { + for (ButtonId button_id = BUTTON_ID_BACK; button_id < NUM_BUTTONS; ++button_id) { + window_single_click_subscribe(button_id, prv_panic_button_click_handler); + } +} + +static void prv_handle_init(void) { + PanicWindowAppData* data = app_malloc_check(sizeof(PanicWindowAppData)); + + app_state_set_user_data(data); + services_set_runlevel(RunLevel_BareMinimum); + + Window *window = &data->window; + + window_init(window, WINDOW_NAME("Panic")); + window_set_overrides_back_button(window, true); + window_set_background_color(window, GColorBlack); + window_set_click_config_provider(window, prv_panic_click_config_provider); + + layer_init(&data->layer, &window_get_root_layer(&data->window)->frame); + layer_set_update_proc(&data->layer, prv_update_proc); + layer_add_child(window_get_root_layer(&data->window), &data->layer); + + const bool animated = false; + app_window_stack_push(window, animated); +} + +static void s_main(void) { + prv_handle_init(); + app_event_loop(); +} + +const PebbleProcessMd* panic_app_get_app_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = s_main, + .visibility = ProcessVisibilityHidden, + // UUID: 130fb6d7-da9e-485a-87ca-a5ca4bf21912 + .uuid = {0x13, 0x0f, 0xb6, 0xd7, 0xda, 0x9e, 0x48, 0x5a, 0x87, 0xca, 0xa5, 0xca, 0x4b, 0xf2, 0x19, 0x12}, + }, + .name = "Panic App", + .run_level = ProcessAppRunLevelCritical, + }; + return (const PebbleProcessMd*) &s_app_md; +} + diff --git a/src/fw/apps/core/panic_window.h b/src/fw/apps/core/panic_window.h new file mode 100644 index 0000000000..b8edde859e --- /dev/null +++ b/src/fw/apps/core/panic_window.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* panic_app_get_app_info(); + diff --git a/src/fw/apps/core/progress_ui.c b/src/fw/apps/core/progress_ui.c new file mode 100644 index 0000000000..7efcb13053 --- /dev/null +++ b/src/fw/apps/core/progress_ui.c @@ -0,0 +1,278 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "progress_ui.h" + +#include "applib/app.h" +#include "applib/app_timer.h" +#include "applib/graphics/gpath_builder.h" +#include "applib/graphics/graphics.h" +#include "util/trig.h" +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "applib/ui/layer.h" +#include "applib/ui/progress_layer.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window_private.h" +#include "applib/ui/app_window_stack.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/i18n/i18n.h" +#include "system/logging.h" +#include "system/passert.h" + +#include +#include + + +#define UPDATE_FREQ_MS 1000 +#define FAIL_SCREEN_VISIBLE_DURATION_MS 10000 +#define COMPLETE_SCREEN_VISIBLE_DURATION_MS 5000 +#define PROG_LAYER_START_VAL 6 +// Used to force the progress bar to start at PROG_LAYER_START_VAL and scale +// the reset of the progress between that value and MAX_PROGRESS_PERCENT +#define PROG_LAYER_TRANSFORM(real_prog) \ + (PROG_LAYER_START_VAL + (real_prog * \ + (MAX_PROGRESS_PERCENT - PROG_LAYER_START_VAL) / MAX_PROGRESS_PERCENT)) + +//////////////////////////////////////////////////////////// +// Data structures + +typedef struct { + Window window; + TextLayer percent_done_text_layer; + char percent_done_text_buffer[5]; //progress_source == PROGRESS_UI_SOURCE_FW_UPDATE) { + return success ? i18n_get("Update Complete", data) : i18n_get("Update Failed", data); + } else { + return ""; + } +} + +static void prv_handle_finished(ProgressUIData *data, bool success) { + if (data->is_finished) { + return; + } + data->is_finished = true; + layer_set_hidden(&data->percent_done_text_layer.layer, true); + layer_set_hidden(&data->progress_layer.layer, true); + + uint32_t res_id; + uint32_t end_screen_timeout; + if (success) { + res_id = RESOURCE_ID_GENERIC_CONFIRMATION_LARGE; + end_screen_timeout = COMPLETE_SCREEN_VISIBLE_DURATION_MS; + dialog_set_background_color(simple_dialog_get_dialog(&data->finished_dialog), GColorGreen); + simple_dialog_set_buttons_enabled(&data->finished_dialog, false); + } else { + res_id = RESOURCE_ID_GENERIC_WARNING_LARGE; + end_screen_timeout = FAIL_SCREEN_VISIBLE_DURATION_MS; + } + + dialog_set_icon(&data->finished_dialog.dialog, res_id); + dialog_set_text(&data->finished_dialog.dialog, prv_get_dialog_text(data, success)); + // Show the status screen for a bit before closing the app + dialog_set_timeout(&data->finished_dialog.dialog, end_screen_timeout); + + simple_dialog_push(&data->finished_dialog, app_state_get_window_stack()); + + app_timer_cancel(data->timer); +} + +static void prv_update_progress_text(ProgressUIData *data) { + sniprintf(data->percent_done_text_buffer, + sizeof(data->percent_done_text_buffer), "%u%%", data->percent_complete); + layer_mark_dirty(&data->percent_done_text_layer.layer); +} + +static void prv_update_progress(ProgressUIData *data) { + switch (data->progress_source) { + case PROGRESS_UI_SOURCE_COREDUMP: { + break; + } + case PROGRESS_UI_SOURCE_LOGS: { + break; + } + case PROGRESS_UI_SOURCE_FACTORY_RESET: { + return; + } + case PROGRESS_UI_SOURCE_FW_UPDATE: { + if (firmware_update_current_status() == FirmwareUpdateFailed) { + prv_handle_finished(data, false /* success */); + return; + } else if (firmware_update_current_status() == FirmwareUpdateStopped) { + prv_handle_finished(data, true /* success */); + return; + } + + data->percent_complete = firmware_update_get_percent_progress(); + break; + } + } + + prv_update_progress_text(data); + progress_layer_set_progress(&data->progress_layer, + PROG_LAYER_TRANSFORM(data->percent_complete)); + + if ((data->progress_source != PROGRESS_UI_SOURCE_FW_UPDATE) && + (data->percent_complete >= 100)) { + prv_handle_finished(data, true /* success */); + } +} + +static void prv_refresh_progress(void *data_in) { + ProgressUIData *data = (ProgressUIData*) data_in; + if (!data) { + // Sanity check + return; + } + + // Overwrite the old timer handle, it's no longer valid + data->timer = app_timer_register(UPDATE_FREQ_MS, prv_refresh_progress, data); + + prv_update_progress(data); +} + +//////////////////////////////////////////////////////////// +// Window loading, unloading, initializing + +static void prv_dialog_unloaded(void *context) { + ProgressUIData *data = context; + // Schedule a super quick timer to pop all windows. Can't call it here directly + // since we would actually try popping the dialog window too, causing a fault. + data->timer = app_timer_register(10, prv_quit, data); +} + +static void prv_window_unload_handler(Window* window) { + ProgressUIData *data = window_get_user_data(window); + if (data) { + i18n_free_all(data); + if (data->timer) { + app_timer_cancel(data->timer); + } + app_free(data); + } +} + +static void prv_push_factory_reset_dialog(ProgressUIData *data) { + simple_dialog_init(&data->finished_dialog, "Factory Reset"); + Dialog *dialog = simple_dialog_get_dialog(&data->finished_dialog); + dialog_set_text(dialog, i18n_get("Resetting...", data)); + dialog_set_icon(dialog, RESOURCE_ID_GENERIC_WARNING_LARGE); + dialog_set_timeout(dialog, DIALOG_TIMEOUT_INFINITE); + dialog_set_destroy_on_pop(dialog, false); + simple_dialog_set_buttons_enabled(&data->finished_dialog, false); + simple_dialog_set_icon_animated(&data->finished_dialog, false); + + simple_dialog_push(&data->finished_dialog, app_state_get_window_stack()); +} + +static void prv_window_load_handler(Window* window) { + ProgressUIData *data = window_get_user_data(window); + + const ProgressUIAppArgs* app_args = app_manager_get_task_context()->args; + data->progress_source = app_args->progress_source; + + if (data->progress_source == PROGRESS_UI_SOURCE_FACTORY_RESET) { + prv_push_factory_reset_dialog(data); + return; + } + + simple_dialog_init(&data->finished_dialog, "Update Completed Dialog"); + dialog_set_callbacks(&data->finished_dialog.dialog, &(DialogCallbacks) { + .unload = prv_dialog_unloaded, + }, data); + dialog_set_destroy_on_pop(&data->finished_dialog.dialog, false); + + const int16_t load_bar_length = 108; + const int16_t load_bar_height = 8; + const int16_t x_offset = (window->layer.bounds.size.w - load_bar_length) / 2; + const int16_t y_offset_progress = (window->layer.bounds.size.h - load_bar_height) / 2; + const int16_t y_offset_text = y_offset_progress - 38; + const GRect progress_bounds = GRect(x_offset, y_offset_progress, load_bar_length, load_bar_height); + ProgressLayer *progress_layer = &data->progress_layer; + progress_layer_init(progress_layer, &progress_bounds); + progress_layer_set_corner_radius(progress_layer, 3); + layer_add_child(&window->layer, &progress_layer->layer); + + TextLayer *percent_done_text_layer = &data->percent_done_text_layer; + text_layer_init_with_parameters(percent_done_text_layer, + &GRect(0, y_offset_text, window->layer.bounds.size.w, 30), + data->percent_done_text_buffer, + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), + GColorBlack, GColorClear, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->layer, &percent_done_text_layer->layer); + + data->timer = app_timer_register(UPDATE_FREQ_MS, prv_refresh_progress, data); + prv_update_progress(data); +} + +static void prv_progress_ui_window_push(void) { + ProgressUIData *data = app_zalloc_check(sizeof(ProgressUIData)); + + Window* window = &data->window; + window_init(window, WINDOW_NAME("Progress UI App")); + window_set_user_data(window, data); + window_set_overrides_back_button(window, true); + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + window_set_window_handlers(window, &(WindowHandlers){ + .load = prv_window_load_handler, + .unload = prv_window_unload_handler, + }); + app_window_stack_push(window, false); +} + +static void prv_main(void) { + if (!app_manager_get_task_context()->args) { + PBL_LOG_WRN("Progress UI App must be launched with args"); + return; + } + + launcher_block_popups(true); + + prv_progress_ui_window_push(); + + app_event_loop(); + + launcher_block_popups(false); +} + +//////////////////////////////////////////////////////////// +// Public functions + +const PebbleProcessMd* progress_ui_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &prv_main, + .visibility = ProcessVisibilityHidden, + // UUID: f29f18ac-bbec-452b-9262-49c4f6e5c920 + .uuid = {0xf2, 0x9f, 0x18, 0xac, 0xbb, 0xec, 0x45, 0x2b, + 0x92, 0x62, 0x49, 0xc4, 0xf6, 0xe5, 0xc9, 0x20}, + }, + .name = "Progress UI", + .run_level = ProcessAppRunLevelSystem, + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/core/progress_ui.h b/src/fw/apps/core/progress_ui.h new file mode 100644 index 0000000000..15ba2de045 --- /dev/null +++ b/src/fw/apps/core/progress_ui.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +typedef enum { + PROGRESS_UI_SOURCE_COREDUMP, + PROGRESS_UI_SOURCE_LOGS, + PROGRESS_UI_SOURCE_FW_UPDATE, + PROGRESS_UI_SOURCE_FACTORY_RESET, +} ProgressUISource; + +typedef struct { + ProgressUISource progress_source; +} ProgressUIAppArgs; + +const PebbleProcessMd* progress_ui_app_get_info(); diff --git a/src/fw/apps/core_apps/spinner_ui_window.c b/src/fw/apps/core/spinner_ui_window.c similarity index 76% rename from src/fw/apps/core_apps/spinner_ui_window.c rename to src/fw/apps/core/spinner_ui_window.c index e3958b690e..5ac788c494 100644 --- a/src/fw/apps/core_apps/spinner_ui_window.c +++ b/src/fw/apps/core/spinner_ui_window.c @@ -1,30 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "spinner_ui_window.h" #include "applib/graphics/gpath_builder.h" #include "applib/graphics/graphics.h" +#include "applib/graphics/gtypes.h" #include "util/trig.h" #include "applib/ui/animation.h" #include "applib/ui/layer.h" #include "applib/ui/property_animation.h" -#include "applib/ui/bitmap_layer.h" #include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" #include "system/passert.h" #include "string.h" @@ -34,9 +20,7 @@ typedef struct { Window window; - GBitmap *bitmap; - bool bitmap_inited; - BitmapLayer bitmap_layer; + Layer background_layer; Layer anim_layer; PropertyAnimation *spinner_animation; AnimationImplementation spinner_anim_impl; @@ -54,6 +38,23 @@ typedef struct { // This means that the the lag occurs once less frequently and is less noticable #define LOOPS_PER_ANIMATION 10 #define LOOP_DURATION_MS 1500 +#define SPINNER_CIRCLE_RADIUS 9 + +static int prv_get_background_circle_radius(const GRect *bounds) { + const int min_dimension = MIN(bounds->size.w, bounds->size.h); + return (min_dimension * 3) / (4 * 2); // 3/4 of min dimension is diameter +} + +static void prv_draw_background_circle(Layer *layer, GContext* ctx) { + graphics_context_set_antialiased(ctx, false); + const GPoint center = grect_center_point(&layer->bounds); + const int radius = prv_get_background_circle_radius(&layer->bounds); + + graphics_context_set_fill_color(ctx, GColorWhite); + graphics_fill_circle(ctx, center, radius); + graphics_context_set_stroke_color(ctx, GColorBlack); + graphics_draw_circle(ctx, center, radius); +} static void prv_draw_spinner_circles(Layer *layer, GContext* ctx) { // Drawing the circles with aa is just too slow and we end up backing up the rest of the system. @@ -62,15 +63,10 @@ static void prv_draw_spinner_circles(Layer *layer, GContext* ctx) { SpinnerUIData *data = window_get_user_data(layer_get_window(layer)); - // This is the background image's circle. -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX - const unsigned int center_of_circle_y_val = 103; -#else - const unsigned int center_of_circle_y_val = PBL_IF_RECT_ELSE(72, layer->bounds.size.h / 2); -#endif - const unsigned int radius_of_path = 37; - const unsigned int radius_of_spinner_circles = 9; - const GPoint circle_center_point = GPoint(layer->bounds.size.w / 2, center_of_circle_y_val); + const int bg_radius = prv_get_background_circle_radius(&layer->bounds); + const unsigned int radius_of_path = bg_radius - SPINNER_CIRCLE_RADIUS; + const unsigned int radius_of_spinner_circles = SPINNER_CIRCLE_RADIUS; + const GPoint circle_center_point = grect_center_point(&layer->bounds); const unsigned int angle = (TRIG_MAX_ANGLE * data->cur_distance_normalized * LOOPS_PER_ANIMATION) / ANIMATION_NORMALIZED_MAX; @@ -121,7 +117,6 @@ static void prv_anim_stopped(Animation *animation, bool finished, void *context) static void prv_window_unload_handler(Window* window) { SpinnerUIData *data = window_get_user_data(window); if (data) { - gbitmap_destroy(data->bitmap); data->should_cancel_animation = true; property_animation_destroy(data->spinner_animation); kernel_free(data); @@ -130,20 +125,16 @@ static void prv_window_unload_handler(Window* window) { static void prv_window_load_handler(Window* window) { SpinnerUIData *data = window_get_user_data(window); - GRect spinner_bounds = window->layer.bounds; - spinner_bounds.origin.y += PBL_IF_RECT_ELSE(0, 10); window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - BitmapLayer *bitmap_layer = &data->bitmap_layer; - bitmap_layer_init(bitmap_layer, &window->layer.bounds); - bitmap_layer_set_alignment(bitmap_layer, PBL_IF_RECT_ELSE(GAlignTopLeft, GAlignCenter)); - layer_set_frame(&bitmap_layer->layer, &spinner_bounds); - data->bitmap = gbitmap_create_with_resource(RESOURCE_ID_SPINNER_BACKGROUND); - bitmap_layer_set_bitmap(bitmap_layer, data->bitmap); - layer_add_child(&window->layer, &bitmap_layer->layer); + // Background circle layer + Layer *background_layer = &data->background_layer; + layer_set_bounds(background_layer, &window->layer.bounds); + layer_set_update_proc(background_layer, prv_draw_background_circle); + layer_add_child(&window->layer, background_layer); - // Animation setup + // Animation layer Layer *anim_layer = &data->anim_layer; layer_set_bounds(anim_layer, &window->layer.bounds); layer_set_update_proc(anim_layer, prv_draw_spinner_circles); diff --git a/src/fw/apps/core/spinner_ui_window.h b/src/fw/apps/core/spinner_ui_window.h new file mode 100644 index 0000000000..6025403be7 --- /dev/null +++ b/src/fw/apps/core/spinner_ui_window.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gtypes.h" +#include "applib/graphics/gcolor_definitions.h" +#include "applib/ui/window_private.h" +#include "applib/ui/window_stack.h" + +// The function creates a Spinner UI window on the heap with the specified color. +// The window is cleaned up when it is popped. +// Returns a pointer to the created window +Window* spinner_ui_window_get(GColor spinner_color); diff --git a/src/fw/apps/core_apps/panic_window_app.c b/src/fw/apps/core_apps/panic_window_app.c deleted file mode 100644 index 5eab6adec0..0000000000 --- a/src/fw/apps/core_apps/panic_window_app.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/ui/window_private.h" -#include "applib/ui/app_window_stack.h" -#include "board/board.h" -#include "kernel/event_loop.h" -#include "kernel/panic.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/i18n/i18n.h" -#include "services/runlevel.h" -#include "system/passert.h" -#include "system/reset.h" - -#include - -static const uint8_t sad_watch[] = { - 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 0 - 16 */ - 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x57, 0xf5, 0xff, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0x57, 0xf5, 0xff, /* bytes 16 - 32 */ - 0xff, 0xa9, 0xca, 0xff, 0xff, 0x06, 0xb0, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0x7f, 0x06, 0x30, 0xff, /* bytes 32 - 48 */ - 0x7f, 0xfa, 0x2f, 0xff, 0x7f, 0xfa, 0x2f, 0xff, 0x7f, 0xaa, 0x2a, 0xff, 0xff, 0xda, 0xad, 0xff, /* bytes 48 - 64 */ - 0xff, 0xaa, 0x2a, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0x1a, 0x2c, 0xff, /* bytes 64 - 80 */ - 0xff, 0xea, 0xab, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0xfa, 0x2f, 0xff, 0xff, 0xfa, 0x2f, 0xff, /* bytes 80 - 96 */ - 0xff, 0x06, 0x20, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xff, 0xfe, 0xbf, 0xff, 0xff, 0x06, 0xb0, 0xff, /* bytes 96 - 112 */ - 0xff, 0xa9, 0xca, 0xff, 0xff, 0x57, 0xf5, 0xff, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0x57, 0xf5, 0xff, /* bytes 112 - 128 */ - 0xff, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -typedef struct PanicWindowAppData { - Window window; - Layer layer; -} PanicWindowAppData; - -static void prv_update_proc(Layer* layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpAssignInverted); - - GBitmap sad_watch_bitmap; - gbitmap_init_with_data(&sad_watch_bitmap, sad_watch); - - const GRect bitmap_dest_rect = GRect(56, 68, 32, 32); - graphics_draw_bitmap_in_rect(ctx, &sad_watch_bitmap, &bitmap_dest_rect); - - GFont error_code_face = fonts_get_system_font(FONT_KEY_GOTHIC_14); - const GRect text_dest_rect = GRect(38, 108, 70, 30); - - char text_buffer[11]; - snprintf(text_buffer, sizeof(text_buffer), "0x%"PRIx32, launcher_panic_get_current_error()); - - graphics_draw_text(ctx, text_buffer, error_code_face, - text_dest_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static void prv_panic_reset_callback(void* data) { - RebootReason reason = { - .code = RebootReasonCode_LauncherPanic, - .extra = { .value = launcher_panic_get_current_error() } - }; - reboot_reason_set(&reason); - - system_reset(); -} - -static void prv_panic_button_click_handler(ClickRecognizerRef recognizer, void *context) { - launcher_task_add_callback(prv_panic_reset_callback, NULL); -} - -static void prv_panic_click_config_provider(void* context) { - for (ButtonId button_id = BUTTON_ID_BACK; button_id < NUM_BUTTONS; ++button_id) { - window_single_click_subscribe(button_id, prv_panic_button_click_handler); - } -} - -static void prv_handle_init(void) { - PanicWindowAppData* data = app_malloc_check(sizeof(PanicWindowAppData)); - - app_state_set_user_data(data); - services_set_runlevel(RunLevel_BareMinimum); - - Window *window = &data->window; - - window_init(window, WINDOW_NAME("Panic")); - window_set_overrides_back_button(window, true); - window_set_background_color(window, GColorBlack); - window_set_click_config_provider(window, prv_panic_click_config_provider); - -#if CAPABILITY_HAS_HARDWARE_PANIC_SCREEN - display_show_panic_screen(launcher_panic_get_current_error()); -#else - layer_init(&data->layer, &window_get_root_layer(&data->window)->frame); - layer_set_update_proc(&data->layer, prv_update_proc); - layer_add_child(window_get_root_layer(&data->window), &data->layer); -#endif - - const bool animated = false; - app_window_stack_push(window, animated); -} - -static void s_main(void) { - prv_handle_init(); - app_event_loop(); -} - -const PebbleProcessMd* panic_app_get_app_info() { - static const PebbleProcessMdSystem s_app_md = { - .common = { - .main_func = s_main, - .visibility = ProcessVisibilityHidden, - // UUID: 130fb6d7-da9e-485a-87ca-a5ca4bf21912 - .uuid = {0x13, 0x0f, 0xb6, 0xd7, 0xda, 0x9e, 0x48, 0x5a, 0x87, 0xca, 0xa5, 0xca, 0x4b, 0xf2, 0x19, 0x12}, - }, - .name = "Panic App", - .run_level = ProcessAppRunLevelCritical, - }; - return (const PebbleProcessMd*) &s_app_md; -} - diff --git a/src/fw/apps/core_apps/panic_window_app.h b/src/fw/apps/core_apps/panic_window_app.h deleted file mode 100644 index e109d82459..0000000000 --- a/src/fw/apps/core_apps/panic_window_app.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* panic_app_get_app_info(); - diff --git a/src/fw/apps/core_apps/progress_ui_app.c b/src/fw/apps/core_apps/progress_ui_app.c deleted file mode 100644 index 1bc160692e..0000000000 --- a/src/fw/apps/core_apps/progress_ui_app.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "progress_ui_app.h" - -#include "applib/app.h" -#include "applib/app_timer.h" -#include "applib/graphics/gpath_builder.h" -#include "applib/graphics/graphics.h" -#include "util/trig.h" -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "applib/ui/layer.h" -#include "applib/ui/progress_layer.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window_private.h" -#include "applib/ui/app_window_stack.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/firmware_update.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" -#include "system/passert.h" - -#include -#include - - -#define UPDATE_FREQ_MS 1000 -#define FAIL_SCREEN_VISIBLE_DURATION_MS 10000 -#define COMPLETE_SCREEN_VISIBLE_DURATION_MS 5000 -#define PROG_LAYER_START_VAL 6 -// Used to force the progress bar to start at PROG_LAYER_START_VAL and scale -// the reset of the progress between that value and MAX_PROGRESS_PERCENT -#define PROG_LAYER_TRANSFORM(real_prog) \ - (PROG_LAYER_START_VAL + (real_prog * \ - (MAX_PROGRESS_PERCENT - PROG_LAYER_START_VAL) / MAX_PROGRESS_PERCENT)) - -//////////////////////////////////////////////////////////// -// Data structures - -typedef struct { - Window window; - TextLayer percent_done_text_layer; - char percent_done_text_buffer[5]; //progress_source == PROGRESS_UI_SOURCE_FW_UPDATE) { - return success ? i18n_get("Update Complete", data) : i18n_get("Update Failed", data); - } else { - return ""; - } -} - -static void prv_handle_finished(ProgressUIData *data, bool success) { - if (data->is_finished) { - return; - } - data->is_finished = true; - layer_set_hidden(&data->percent_done_text_layer.layer, true); - layer_set_hidden(&data->progress_layer.layer, true); - - uint32_t res_id; - uint32_t end_screen_timeout; - if (success) { - res_id = RESOURCE_ID_GENERIC_CONFIRMATION_LARGE; - end_screen_timeout = COMPLETE_SCREEN_VISIBLE_DURATION_MS; - dialog_set_background_color(simple_dialog_get_dialog(&data->finished_dialog), GColorGreen); - simple_dialog_set_buttons_enabled(&data->finished_dialog, false); - } else { - res_id = RESOURCE_ID_GENERIC_WARNING_LARGE; - end_screen_timeout = FAIL_SCREEN_VISIBLE_DURATION_MS; - } - - dialog_set_icon(&data->finished_dialog.dialog, res_id); -#if !PLATFORM_ROBERT && !PLATFORM_CALCULUS && !PLATFORM_OBELIX - dialog_set_text(&data->finished_dialog.dialog, prv_get_dialog_text(data, success)); -#endif - // Show the status screen for a bit before closing the app - dialog_set_timeout(&data->finished_dialog.dialog, end_screen_timeout); - - simple_dialog_push(&data->finished_dialog, app_state_get_window_stack()); - - app_timer_cancel(data->timer); -} - -static void prv_update_progress_text(ProgressUIData *data) { - sniprintf(data->percent_done_text_buffer, - sizeof(data->percent_done_text_buffer), "%u%%", data->percent_complete); - layer_mark_dirty(&data->percent_done_text_layer.layer); -} - -static void prv_update_progress(ProgressUIData *data) { - switch (data->progress_source) { - case PROGRESS_UI_SOURCE_COREDUMP: { - break; - } - case PROGRESS_UI_SOURCE_LOGS: { - break; - } - case PROGRESS_UI_SOURCE_FW_UPDATE: { - if (firmware_update_current_status() == FirmwareUpdateFailed) { - prv_handle_finished(data, false /* success */); - return; - } else if (firmware_update_current_status() == FirmwareUpdateStopped) { - prv_handle_finished(data, true /* success */); - return; - } - - data->percent_complete = firmware_update_get_percent_progress(); - break; - } - } - - prv_update_progress_text(data); - progress_layer_set_progress(&data->progress_layer, - PROG_LAYER_TRANSFORM(data->percent_complete)); - - if ((data->progress_source != PROGRESS_UI_SOURCE_FW_UPDATE) && - (data->percent_complete >= 100)) { - prv_handle_finished(data, true /* success */); - } -} - -static void prv_refresh_progress(void *data_in) { - ProgressUIData *data = (ProgressUIData*) data_in; - if (!data) { - // Sanity check - return; - } - - // Overwrite the old timer handle, it's no longer valid - data->timer = app_timer_register(UPDATE_FREQ_MS, prv_refresh_progress, data); - - prv_update_progress(data); -} - -//////////////////////////////////////////////////////////// -// Window loading, unloading, initializing - -static void prv_dialog_unloaded(void *context) { - ProgressUIData *data = context; - // Schedule a super quick timer to pop all windows. Can't call it here directly - // since we would actually try popping the dialog window too, causing a fault. - data->timer = app_timer_register(10, prv_quit, data); -} - -static void prv_window_unload_handler(Window* window) { - ProgressUIData *data = window_get_user_data(window); - if (data) { - i18n_free_all(data); - app_timer_cancel(data->timer); - app_free(data); - } -} - -static void prv_window_load_handler(Window* window) { - ProgressUIData *data = window_get_user_data(window); - - const ProgressUIAppArgs* app_args = app_manager_get_task_context()->args; - data->progress_source = app_args->progress_source; - - simple_dialog_init(&data->finished_dialog, "Update Completed Dialog"); - dialog_set_callbacks(&data->finished_dialog.dialog, &(DialogCallbacks) { - .unload = prv_dialog_unloaded, - }, data); - dialog_set_destroy_on_pop(&data->finished_dialog.dialog, false); - - const int16_t load_bar_length = 108; - const int16_t x_offset = (window->layer.bounds.size.w - load_bar_length) / 2; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS - const int16_t y_offset_progress = 123; - const int16_t y_offset_text = 85; -#else - const int16_t y_offset_progress = PBL_IF_RECT_ELSE(93, 99); - const int16_t y_offset_text = PBL_IF_RECT_ELSE(55, 62); -#endif - const GRect progress_bounds = GRect(x_offset, y_offset_progress, load_bar_length, 8); - ProgressLayer *progress_layer = &data->progress_layer; - progress_layer_init(progress_layer, &progress_bounds); - progress_layer_set_corner_radius(progress_layer, 3); - layer_add_child(&window->layer, &progress_layer->layer); - - TextLayer *percent_done_text_layer = &data->percent_done_text_layer; - text_layer_init_with_parameters(percent_done_text_layer, - &GRect(0, y_offset_text, window->layer.bounds.size.w, 30), - data->percent_done_text_buffer, - fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), - GColorBlack, GColorClear, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&window->layer, &percent_done_text_layer->layer); - - data->timer = app_timer_register(UPDATE_FREQ_MS, prv_refresh_progress, data); - prv_update_progress(data); -} - -static void prv_progress_ui_window_push(void) { - ProgressUIData *data = app_zalloc_check(sizeof(ProgressUIData)); - - Window* window = &data->window; - window_init(window, WINDOW_NAME("Progress UI App")); - window_set_user_data(window, data); - window_set_overrides_back_button(window, true); - window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - window_set_window_handlers(window, &(WindowHandlers){ - .load = prv_window_load_handler, - .unload = prv_window_unload_handler, - }); - app_window_stack_push(window, false); -} - -static void prv_main(void) { - if (!app_manager_get_task_context()->args) { - PBL_LOG(LOG_LEVEL_WARNING, "Progress UI App must be launched with args"); - return; - } - - launcher_block_popups(true); - - prv_progress_ui_window_push(); - - app_event_loop(); - - launcher_block_popups(false); -} - -//////////////////////////////////////////////////////////// -// Public functions - -const PebbleProcessMd* progress_ui_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common = { - .main_func = &prv_main, - .visibility = ProcessVisibilityHidden, - // UUID: f29f18ac-bbec-452b-9262-49c4f6e5c920 - .uuid = {0xf2, 0x9f, 0x18, 0xac, 0xbb, 0xec, 0x45, 0x2b, - 0x92, 0x62, 0x49, 0xc4, 0xf6, 0xe5, 0xc9, 0x20}, - }, - .name = "Progress UI", - .run_level = ProcessAppRunLevelSystem, - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/core_apps/progress_ui_app.h b/src/fw/apps/core_apps/progress_ui_app.h deleted file mode 100644 index 8a5d3b123a..0000000000 --- a/src/fw/apps/core_apps/progress_ui_app.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -typedef enum { - PROGRESS_UI_SOURCE_COREDUMP, - PROGRESS_UI_SOURCE_LOGS, - PROGRESS_UI_SOURCE_FW_UPDATE, -} ProgressUISource; - -typedef struct { - ProgressUISource progress_source; -} ProgressUIAppArgs; - -const PebbleProcessMd* progress_ui_app_get_info(); diff --git a/src/fw/apps/core_apps/spinner_ui_window.h b/src/fw/apps/core_apps/spinner_ui_window.h deleted file mode 100644 index c9c3e990c4..0000000000 --- a/src/fw/apps/core_apps/spinner_ui_window.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" -#include "applib/graphics/gcolor_definitions.h" -#include "applib/ui/window_private.h" -#include "applib/ui/window_stack.h" - -// The function creates a Spinner UI window on the heap with the specified color. -// The window is cleaned up when it is popped. -// Returns a pointer to the created window -Window* spinner_ui_window_get(GColor spinner_color); diff --git a/src/fw/apps/demo/Kconfig b/src/fw/apps/demo/Kconfig new file mode 100644 index 0000000000..e9276eb33d --- /dev/null +++ b/src/fw/apps/demo/Kconfig @@ -0,0 +1,69 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menu "Demo" + +rsource "action_menu/Kconfig" +rsource "activity_demo/Kconfig" +rsource "activity_test/Kconfig" +rsource "amb_light_read/Kconfig" +rsource "animated_demo/Kconfig" +rsource "app_heap_demo/Kconfig" +rsource "bouncing_box_demo/Kconfig" +rsource "click/Kconfig" +rsource "data_logging_test/Kconfig" +rsource "deadlock/Kconfig" +rsource "dialogs/Kconfig" +rsource "double_tap_test/Kconfig" +rsource "event_service/Kconfig" +rsource "exit/Kconfig" +rsource "flash_diagnostic/Kconfig" +rsource "flash_prof/Kconfig" +rsource "font_test/Kconfig" +rsource "fs_resources/Kconfig" +rsource "fw_update_progress_sim/Kconfig" +rsource "gdrawmask_demo/Kconfig" +rsource "gfx_tests/Kconfig" +rsource "hrm_demo/Kconfig" +rsource "idl/Kconfig" +rsource "kill_bt/Kconfig" +rsource "kino_layer/Kconfig" +rsource "light_config/Kconfig" +rsource "menu/Kconfig" +rsource "menu_overflow/Kconfig" +rsource "menu_right_icon/Kconfig" +rsource "menu_round/Kconfig" +rsource "morph_square/Kconfig" +rsource "movable_line/Kconfig" +rsource "mpu_test/Kconfig" +rsource "mpu_violation_test/Kconfig" +rsource "number_field/Kconfig" +rsource "option_menu/Kconfig" +rsource "pebble_colors/Kconfig" +rsource "pebble_shapes/Kconfig" +rsource "persist/Kconfig" +rsource "profile_mutexes/Kconfig" +rsource "progress/Kconfig" +rsource "scroll/Kconfig" +rsource "simple_menu/Kconfig" +rsource "statusbar/Kconfig" +rsource "stroke_width/Kconfig" +rsource "swap_layer/Kconfig" +rsource "temperature_demo/Kconfig" +rsource "test_args_receiver/Kconfig" +rsource "test_args_sender/Kconfig" +rsource "test_bluetooth/Kconfig" +rsource "test_core_dump/Kconfig" +rsource "test_sys_timer/Kconfig" +rsource "text_clipping/Kconfig" +rsource "text_flow/Kconfig" +rsource "text_layout/Kconfig" +rsource "text_spacing/Kconfig" +rsource "timeline_pins/Kconfig" +rsource "timer/Kconfig" +rsource "trigger_alarm/Kconfig" +rsource "vibe_and_logs/Kconfig" +rsource "vibe_score/Kconfig" +rsource "vibe_strength/Kconfig" + +endmenu diff --git a/src/fw/apps/demo/action_menu/Kconfig b/src/fw/apps/demo/action_menu/Kconfig new file mode 100644 index 0000000000..bf831ecaab --- /dev/null +++ b/src/fw/apps/demo/action_menu/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_ACTION_MENU + bool "Action Menu" diff --git a/src/fw/apps/demo_apps/action_menu_demo.c b/src/fw/apps/demo/action_menu/action_menu_demo.c similarity index 92% rename from src/fw/apps/demo_apps/action_menu_demo.c rename to src/fw/apps/demo/action_menu/action_menu_demo.c index 1b3fd1279f..d9735df9e4 100644 --- a/src/fw/apps/demo_apps/action_menu_demo.c +++ b/src/fw/apps/demo/action_menu/action_menu_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "action_menu_demo.h" diff --git a/src/fw/apps/demo/action_menu/action_menu_demo.h b/src/fw/apps/demo/action_menu/action_menu_demo.h new file mode 100644 index 0000000000..e7c267115e --- /dev/null +++ b/src/fw/apps/demo/action_menu/action_menu_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* action_menu_demo_get_app_info(void); diff --git a/src/fw/apps/demo/action_menu/wscript_build b/src/fw/apps/demo/action_menu/wscript_build new file mode 100644 index 0000000000..d7841305c8 --- /dev/null +++ b/src/fw/apps/demo/action_menu/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['action_menu_demo.c'], + target='demo_app_action_menu', +) +bld.env.FW_APPS.append('demo_app_action_menu') + +# vim:filetype=python diff --git a/src/fw/apps/demo/activity_demo/Kconfig b/src/fw/apps/demo/activity_demo/Kconfig new file mode 100644 index 0000000000..b8847b0c5f --- /dev/null +++ b/src/fw/apps/demo/activity_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_ACTIVITY_DEMO + bool "Activity Demo" diff --git a/src/fw/apps/demo/activity_demo/activity_demo.c b/src/fw/apps/demo/activity_demo/activity_demo.c new file mode 100644 index 0000000000..f58544db49 --- /dev/null +++ b/src/fw/apps/demo/activity_demo/activity_demo.c @@ -0,0 +1,785 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/health_service.h" +#include "applib/app.h" +#include "applib/app_logging.h" +#include "applib/fonts/fonts.h" +#include "applib/persist.h" +#include "applib/ui/ui.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "apps/system_app_ids.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "shell/prefs.h" +#include "system/logging.h" +#include "util/size.h" +#include "util/string.h" +#include "util/trig.h" + +#include "activity_demo.h" + +#include + +#define CURRENT_STEP_AVG 500 +#define DAILY_STEP_AVG 1000 + +// Persist keys +typedef enum { + AppPersistKeyLapSteps = 0, +} AppPersistKey; + + + +// ------------------------------------------------------------------------------- +// Structures + +typedef struct { + char dialog_text[256]; + SimpleMenuItem *menu_items; + SimpleMenuLayer *menu_layer; +} DebugCard; + +// App globals +typedef struct { + Window *debug_window; + DebugCard debug_card; + uint32_t steps_offset; + uint32_t cur_steps; +} ActivityDemoAppData; + +static ActivityDemoAppData *s_data; + +// ------------------------------------------------------------------------------- +static void prv_convert_seconds_to_time(uint32_t secs_after_midnight, char *text, + int text_len) { + uint32_t minutes_after_midnight = secs_after_midnight / SECONDS_PER_MINUTE; + uint32_t hour = minutes_after_midnight / MINUTES_PER_HOUR; + uint32_t minute = minutes_after_midnight % MINUTES_PER_HOUR; +#pragma GCC diagnostic ignored "-Wformat-truncation" + snprintf(text, text_len, "%d:%02d", (int)hour, (int)minute); +} + +// ----------------------------------------------------------------------------------------- +static void prv_display_alert(const char *text) { + ExpandableDialog *expandable_dialog = expandable_dialog_create("Alert"); + + dialog_set_text(expandable_dialog_get_dialog(expandable_dialog), text); + expandable_dialog_show_action_bar(expandable_dialog, false); + app_expandable_dialog_push(expandable_dialog); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_display_scalar_history_alert(ActivityDemoAppData *data, const char *title, + ActivityMetric metric) { + strcpy(data->debug_card.dialog_text, title); + + // Get History + int32_t values[7]; + activity_get_metric(metric, ARRAY_LENGTH(values), values); + for (int i = 0; i < 7; i++) { + char temp[32]; + snprintf(temp, sizeof(temp), "\n%d: %d", i, (int)values[i]); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + } + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_display_averages_alert(ActivityDemoAppData *data, DayInWeek day) { + ActivityMetricAverages *averages = app_malloc_check(sizeof(ActivityMetricAverages)); + strcpy(data->debug_card.dialog_text, "Hourly avgs:"); + activity_get_step_averages(day, averages); + + // Sum into hours + const int k_avgs_per_hour = ACTIVITY_NUM_METRIC_AVERAGES / HOURS_PER_DAY; + for (int i = 0; i < HOURS_PER_DAY; i++) { + int value = 0; + for (int j = i * k_avgs_per_hour; j < (i + 1) * k_avgs_per_hour; j++) { + if (averages->average[j] == ACTIVITY_METRIC_AVERAGES_UNKNOWN) { + averages->average[j] = 0; + } + value += averages->average[j]; + } + char temp[32]; + snprintf(temp, sizeof(temp), "\n%02d: %d", i, value); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + } + prv_display_alert(data->debug_card.dialog_text); + app_free(averages); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_display_seconds_history_alert(ActivityDemoAppData *data, const char *title, + ActivityMetric metric) { + strcpy(data->debug_card.dialog_text, title); + + // Get History + int32_t values[7]; + activity_get_metric(metric, ARRAY_LENGTH(values), values); + for (int i = 0; i < 7; i++) { + char elapsed[8]; + prv_convert_seconds_to_time(values[i], elapsed, sizeof(elapsed)); + char temp[32]; + snprintf(temp, sizeof(temp), "\n%d: %s", i, elapsed); + strcat(data->debug_card.dialog_text, temp); + } + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_set_steps(int32_t steps, ActivityDemoAppData *data) { + activity_test_set_steps_and_avg(steps, CURRENT_STEP_AVG, DAILY_STEP_AVG); + + int32_t peek_steps = health_service_sum_today(HealthMetricStepCount); + data->cur_steps = peek_steps + data->steps_offset; + + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Current steps changed to: %"PRIu32"\n", data->cur_steps); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_tracking(int index, void *context) { + ActivityDemoAppData *data = context; + + bool enabled = activity_tracking_on(); + enabled = !enabled; + + if (enabled) { + activity_start_tracking(false /*test_mode*/); + } else { + activity_stop_tracking(); + } + activity_prefs_tracking_set_enabled(enabled); + + data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; + layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_activity_insights(int index, void *context) { + ActivityDemoAppData *data = context; + + bool enabled = activity_prefs_activity_insights_are_enabled(); + enabled = !enabled; + activity_prefs_activity_insights_set_enabled(enabled); + + data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; + layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_sleep_insights(int index, void *context) { + ActivityDemoAppData *data = context; + + bool enabled = activity_prefs_sleep_insights_are_enabled(); + enabled = !enabled; + activity_prefs_sleep_insights_set_enabled(enabled); + + data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; + layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_dls_sends(int index, void *context) { + ActivityDemoAppData *data = context; + + bool enabled = dls_get_send_enable(); + enabled = !enabled; + dls_set_send_enable_pp(enabled); + + data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; + layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_set_steps_below_avg(int index, void *context) { + ActivityDemoAppData *data = context; + prv_set_steps(CURRENT_STEP_AVG - 250, data); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_set_steps_at_avg(int index, void *context) { + ActivityDemoAppData *data = context; + prv_set_steps(CURRENT_STEP_AVG, data); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_set_steps_above_avg(int index, void *context) { + ActivityDemoAppData *data = context; + prv_set_steps(CURRENT_STEP_AVG + 250, data); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_set_steps_history(int index, void *context) { + ActivityDemoAppData *data = context; + activity_test_set_steps_history(); + + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Step history changed"); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_set_sleep_history(int index, void *context) { + ActivityDemoAppData *data = context; + activity_test_set_sleep_history(); + + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Sleep history changed"); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_sleep_file_info(int index, void *context) { + ActivityDemoAppData *data = context; + + // Get sleep file info + uint32_t num_records; + uint32_t data_bytes; + uint32_t minutes; + activity_test_minute_file_info(false /*compact_first*/, &num_records, &data_bytes, &minutes); + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Records: %d\nData bytes: %d\nMinutes: %d", (int)num_records, (int)data_bytes, + (int)minutes); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_sleep_file_compact(int index, void *context) { + ActivityDemoAppData *data = context; + + // Get sleep file info + uint32_t num_records; + uint32_t data_bytes; + uint32_t minutes; + activity_test_minute_file_info(true /*compact_first*/, &num_records, &data_bytes, &minutes); + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "After compaction\nRecords: %d\nData bytes: %d\nMinutes: %d", (int)num_records, + (int)data_bytes, (int)minutes); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_resting_calorie_history(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_scalar_history_alert(data, "Resting Calories", ActivityMetricRestingKCalories); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_active_calorie_history(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_scalar_history_alert(data, "Active Calories", ActivityMetricActiveKCalories); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_step_history(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_scalar_history_alert(data, "Steps", ActivityMetricStepCount); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_sleep_history(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_seconds_history_alert(data, "Sleep total", ActivityMetricSleepTotalSeconds); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_active_time_history(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_seconds_history_alert(data, "Active Time", ActivityMetricActiveSeconds); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_distance_history(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_scalar_history_alert(data, "Distance(m)", ActivityMetricDistanceMeters); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_sleep_sessions(int index, void *context) { + ActivityDemoAppData *data = context; + + // Allocate space for the sessions. Usually, there will only be about 4 or 5 sessions + // (1 container and a handful of restful periods). Allocating space for 32 (an arbitrary + // number) should be more than enough. + uint32_t num_sessions = 32; + ActivitySession *sessions = app_malloc(num_sessions * sizeof(ActivitySession)); + if (!sessions) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Not enough memory"); + return; + } + + // Get sessions + bool success = activity_get_sessions(&num_sessions, sessions); + if (!success) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Error getting sleep sessions"); + goto exit; + } + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Sleep sessions\n"); + + // Print info on each one + ActivitySession *session = sessions; + for (uint32_t i = 0; i < num_sessions; i++, session++) { + char *prefix = ""; + bool deep_sleep = false; + switch (session->type) { + case ActivitySessionType_Sleep: + prefix = "s"; + break; + case ActivitySessionType_Nap: + prefix = "n"; + break; + case ActivitySessionType_RestfulSleep: + case ActivitySessionType_RestfulNap: + prefix = "*"; + deep_sleep = true; + break; + default: + continue; + } + safe_strcat(data->debug_card.dialog_text, prefix, sizeof(data->debug_card.dialog_text)); + + // Write start time + struct tm local_tm; + char temp[32]; + localtime_r(&session->start_utc, &local_tm); + strftime(temp, sizeof(temp), "%H:%M", &local_tm); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + + // Write end time/elapsed + if (deep_sleep) { + snprintf(temp, sizeof(temp), " %dm\n", (int)(session->length_min)); + } else { + time_t end_time = session->start_utc + (session->length_min * SECONDS_PER_MINUTE); + localtime_r(&end_time, &local_tm); + strftime(temp, sizeof(temp), "-%H:%M\n", &local_tm); + } + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + } + +exit: + // Free session info memory + app_free(sessions); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_step_sessions(int index, void *context) { + ActivityDemoAppData *data = context; + + // Allocate space for the sessions. Usually, there will only be about 4 or 5 sessions + // (1 container and a handful of restful periods). Allocating space for 32 (an arbitrary + // number) should be more than enough. + uint32_t num_sessions = 32; + ActivitySession *sessions = app_malloc(num_sessions * sizeof(ActivitySession)); + if (!sessions) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Not enough memory"); + return; + } + + // Get sessions + bool success = activity_get_sessions(&num_sessions, sessions); + if (!success) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Error getting activity sessions"); + goto exit; + } + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Step activities\n"); + + // Print info on each one + ActivitySession *session = sessions; + for (uint32_t i = 0; i < num_sessions; i++, session++) { + char *prefix = ""; + switch (session->type) { + case ActivitySessionType_Sleep: + case ActivitySessionType_Nap: + case ActivitySessionType_RestfulSleep: + case ActivitySessionType_RestfulNap: + continue; + case ActivitySessionType_Walk: + prefix = "W"; + break; + case ActivitySessionType_Run: + prefix = "R"; + break; + default: + continue; + } + safe_strcat(data->debug_card.dialog_text, prefix, sizeof(data->debug_card.dialog_text)); + + // Write start time + struct tm local_tm; + char temp[64]; + localtime_r(&session->start_utc, &local_tm); + strftime(temp, sizeof(temp), "%H:%M", &local_tm); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + + // Write length + snprintf(temp, sizeof(temp), " %dm\n", (int)(session->length_min)); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + + // Write steps, calories, distance + snprintf(temp, sizeof(temp), " %"PRIu16", %"PRIu16"C, %"PRIu16"m\n", session->step_data.steps, + session->step_data.active_kcalories + session->step_data.resting_kcalories, + session->step_data.distance_meters); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + } + + exit: + // Free session info memory + app_free(sessions); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_weekday_averages(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_averages_alert(data, Monday); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_weekend_averages(int index, void *context) { + ActivityDemoAppData *data = context; + prv_display_averages_alert(data, Saturday); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_activity_prefs(int index, void *context) { + ActivityDemoAppData *data = context; + bool tracking_enabled = activity_prefs_tracking_is_enabled(); + bool activity_insights_enabled = activity_prefs_activity_insights_are_enabled(); + bool sleep_insights_enabled = activity_prefs_sleep_insights_are_enabled(); + ActivityGender gender = activity_prefs_get_gender(); + uint16_t height_mm = activity_prefs_get_height_mm(); + uint16_t weight_dag = activity_prefs_get_weight_dag(); + uint8_t age_years = activity_prefs_get_age_years(); + + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "activity tracking: %"PRIu8"\n" + "activity_insights: %"PRIu8"\n" + "sleep_insights: %"PRIu8"\n" + "gender: %"PRIu8"\n" + "height: %"PRIu16"\n" + "weight: %"PRIu16"\n" + "age: %"PRIu8"", tracking_enabled, activity_insights_enabled, sleep_insights_enabled, + gender, height_mm, weight_dag, age_years); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_minute_data(int index, void *context) { + ActivityDemoAppData *data = context; + bool success; + + const uint32_t k_size = 1000; + HealthMinuteData *minute_data = app_malloc(k_size * sizeof(HealthMinuteData)); + if (!minute_data) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Out of memory"); + prv_display_alert(data->debug_card.dialog_text); + goto exit; + } + + + // Start as far back as 30 days ago + time_t utc_start = rtc_get_time() - 30 * SECONDS_PER_DAY; + uint32_t num_records = 0; + while (true) { + uint32_t chunk = k_size; + time_t prior_start = utc_start; + success = activity_get_minute_history(minute_data, &chunk, &utc_start); + if (!success) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Failed"); + prv_display_alert(data->debug_card.dialog_text); + goto exit; + } + PBL_LOG_DBG("Got %d minutes with UTC of %d (delta of %d min)", (int)chunk, + (int)utc_start, (int)(utc_start - prior_start) / SECONDS_PER_MINUTE); + num_records += chunk; + utc_start += chunk * SECONDS_PER_MINUTE; + if (chunk == 0) { + break; + } + } + + // Print summary + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Retrieved %d minute data records", (int)num_records); + + + // Print detail on the last few minutes + const int k_print_batch_size = k_size; + PBL_LOG_DBG("Fetching last %d minutes", k_print_batch_size); + utc_start = rtc_get_time() - (k_print_batch_size * SECONDS_PER_MINUTE); + time_t prior_start = utc_start; + uint32_t chunk = k_print_batch_size; + success = activity_get_minute_history(minute_data, &chunk, &utc_start); + if (!success) { + snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), + "Failed"); + prv_display_alert(data->debug_card.dialog_text); + goto exit; + } + + PBL_LOG_DBG("Got last %d minutes with UTC of %d (delta of %d min)", (int)chunk, + (int)utc_start, (int)(utc_start - prior_start) / SECONDS_PER_MINUTE); + + const unsigned int k_num_last_minutes = 6; + if (chunk >= k_num_last_minutes) { + for (int i = (int)chunk - k_num_last_minutes; i < (int)chunk; i++) { + HealthMinuteData *m_data = &minute_data[i]; + char temp[32]; + snprintf(temp, sizeof(temp), "\n%"PRId8", 0x%"PRIx8", %"PRIu16", %"PRId8" ", + m_data->steps, m_data->orientation, m_data->vmc, m_data->light); + safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); + } + } + + prv_display_alert(data->debug_card.dialog_text); + +exit: + app_free(minute_data); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_send_fake_logging_record(int index, void *context) { + activity_test_send_fake_dls_records(); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_push_summary_pins(int index, void *context) { + prv_debug_cmd_set_steps_at_avg(index, context); + + activity_insights_test_push_summary_pins(); + + ActivityDemoAppData *data = context; + strncpy(data->debug_card.dialog_text, "Summary pins pushed", + sizeof(data->debug_card.dialog_text)); + prv_display_alert(data->debug_card.dialog_text); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_push_rewards(int index, void *context) { + activity_insights_test_push_rewards(); +} + + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_push_walk_run(int index, void *context) { + activity_insights_test_push_walk_run_sessions(); +} + +// ----------------------------------------------------------------------------------------- +static void prv_debug_cmd_push_nap_session(int index, void *context) { + activity_insights_test_push_nap_session(); +} + +// ----------------------------------------------------------------------------------------- +static void debug_window_load(Window *window) { + ActivityDemoAppData *data = window_get_user_data(window); + Layer *window_layer = window_get_root_layer(window); + const GRect *root_bounds = &window_layer->bounds; + + static SimpleMenuItem menu_items[] = { + { + .title = "Tracking", + .callback = prv_debug_cmd_tracking, + }, { + .title = "Activity Insights", + .callback = prv_debug_cmd_activity_insights, + }, { + .title = "Sleep Insights", + .callback = prv_debug_cmd_sleep_insights, + }, { + .title = "DLS sends", + .callback = prv_debug_cmd_dls_sends, + }, { + .title = "Step History", + .callback = prv_debug_cmd_step_history, + }, { + .title = "Distance(m) History", + .callback = prv_debug_cmd_distance_history, + }, { + .title = "Resting Calorie History", + .callback = prv_debug_cmd_resting_calorie_history, + }, { + .title = "Active Calorie History", + .callback = prv_debug_cmd_active_calorie_history, + }, { + .title = "Active Minutes History", + .callback = prv_debug_cmd_active_time_history, + }, { + .title = "Sleep History", + .callback = prv_debug_cmd_sleep_history, + }, { + .title = "Sleep Sessions", + .callback = prv_debug_cmd_sleep_sessions, + }, { + .title = "Step activities", + .callback = prv_debug_cmd_step_sessions, + }, { + .title = "Weekday averages", + .callback = prv_debug_cmd_weekday_averages, + }, { + .title = "Weekend averages", + .callback = prv_debug_cmd_weekend_averages, + }, { + .title = "Activity Prefs", + .callback = prv_debug_cmd_activity_prefs, + }, { + .title = "Steps below avg", + .callback = prv_debug_cmd_set_steps_below_avg, + }, { + .title = "Steps at avg", + .callback = prv_debug_cmd_set_steps_at_avg, + }, { + .title = "Steps above avg", + .callback = prv_debug_cmd_set_steps_above_avg, + }, { + .title = "Set step history", + .callback = prv_debug_cmd_set_steps_history, + }, { + .title = "Set sleep history", + .callback = prv_debug_cmd_set_sleep_history, + }, { + .title = "Sleep File Info", + .callback = prv_debug_cmd_sleep_file_info, + }, { + .title = "Sleep File Compact", + .callback = prv_debug_cmd_sleep_file_compact, + }, { + .title = "Read Minute data", + .callback = prv_debug_cmd_minute_data, + }, { + .title = "Send fake DL record", + .callback = prv_debug_cmd_send_fake_logging_record, + }, { + .title = "Push Summary Pins", + .callback = prv_debug_cmd_push_summary_pins, + }, { + .title = "Push Rewards", + .callback = prv_debug_cmd_push_rewards, + }, { + .title = "Walk/Run Notif", + .callback = prv_debug_cmd_push_walk_run, + }, { + .title = "Push Nap Session", + .callback = prv_debug_cmd_push_nap_session, + } + }; + static const SimpleMenuSection sections[] = { + { + .items = menu_items, + .num_items = ARRAY_LENGTH(menu_items) + } + }; + + data->debug_card.menu_items = menu_items; + data->debug_card.menu_layer = simple_menu_layer_create(*root_bounds, window, sections, + ARRAY_LENGTH(sections), data); + layer_add_child(window_layer, simple_menu_layer_get_layer(data->debug_card.menu_layer)); + + // Init status + data->debug_card.menu_items[0].subtitle = activity_tracking_on() ? "Enabled" : "Disabled"; + data->debug_card.menu_items[1].subtitle = + activity_prefs_activity_insights_are_enabled() ? "Enabled" : "Disabled"; + data->debug_card.menu_items[2].subtitle = + activity_prefs_sleep_insights_are_enabled() ? "Enabled" : "Disabled"; + data->debug_card.menu_items[3].subtitle = dls_get_send_enable() ? "Enabled" : "Disabled"; +} + + +// ------------------------------------------------------------------------------- +static void debug_window_unload(Window *window) { + ActivityDemoAppData *data = window_get_user_data(window); + simple_menu_layer_destroy(data->debug_card.menu_layer); +} + + +// ------------------------------------------------------------------------------- +static void deinit(void) { + ActivityDemoAppData *data = app_state_get_user_data(); + window_destroy(data->debug_window); + app_free(data); +} + + +// ------------------------------------------------------------------------------- +static void init(void) { + ActivityDemoAppData *data = app_malloc_check(sizeof(ActivityDemoAppData)); + s_data = data; + memset(data, 0, sizeof(ActivityDemoAppData)); + app_state_set_user_data(data); + + // Debug window + data->debug_window = window_create(); + window_set_user_data(data->debug_window, data); + window_set_window_handlers(data->debug_window, &(WindowHandlers) { + .load = debug_window_load, + .unload = debug_window_unload, + }); + + app_window_stack_push(data->debug_window, true /* Animated */); +} + + +// ------------------------------------------------------------------------------- +static void s_main(void) { + init(); + app_event_loop(); + deinit(); +} + + +// ------------------------------------------------------------------------------- +const PebbleProcessMd* activity_demo_get_app_info(void) { + static const PebbleProcessMdSystem s_activity_demo_app_info = { + .common.main_func = &s_main, + // UUID: 60206d97-818b-4f42-87ae-48fde623608d + .common.uuid = {0x60, 0x20, 0x6d, 0x97, 0x81, 0x8b, 0x4f, 0x42, 0x87, 0xae, 0x48, 0xfd, 0xe6, + 0x23, 0x60, 0x8d}, + .name = "ActivityDemo" + }; + return (const PebbleProcessMd*) &s_activity_demo_app_info; +} diff --git a/src/fw/apps/demo/activity_demo/activity_demo.h b/src/fw/apps/demo/activity_demo/activity_demo.h new file mode 100644 index 0000000000..7c5a6a4503 --- /dev/null +++ b/src/fw/apps/demo/activity_demo/activity_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* activity_demo_get_app_info(void); diff --git a/src/fw/apps/demo/activity_demo/wscript_build b/src/fw/apps/demo/activity_demo/wscript_build new file mode 100644 index 0000000000..2efa39f600 --- /dev/null +++ b/src/fw/apps/demo/activity_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['activity_demo.c'], + target='demo_app_activity_demo', +) +bld.env.FW_APPS.append('demo_app_activity_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/activity_test/Kconfig b/src/fw/apps/demo/activity_test/Kconfig new file mode 100644 index 0000000000..4043c82ce7 --- /dev/null +++ b/src/fw/apps/demo/activity_test/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_ACTIVITY_TEST + bool "Activity Test" diff --git a/src/fw/apps/demo_apps/activity_test.c b/src/fw/apps/demo/activity_test/activity_test.c similarity index 92% rename from src/fw/apps/demo_apps/activity_test.c rename to src/fw/apps/demo/activity_test/activity_test.c index 0bbf5f4918..52ce4fc52b 100644 --- a/src/fw/apps/demo_apps/activity_test.c +++ b/src/fw/apps/demo/activity_test/activity_test.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app.h" #include "applib/app_logging.h" @@ -22,12 +9,12 @@ #include "applib/ui/ui.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/kraepelin/activity_algorithm_kraepelin.h" -#include "services/normal/activity/insights_settings.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h" +#include "pbl/services/activity/insights_settings.h" #include "system/logging.h" #include "util/math.h" #include "util/size.h" @@ -35,7 +22,7 @@ #include "activity_test.h" #include -#include +#include // Test the activity API typedef struct { @@ -698,7 +685,7 @@ static void prv_test_steps(void *context) { steps = health_service_sum_today(HealthMetricStepCount); steps -= before; - PBL_LOG(LOG_LEVEL_DEBUG, "steps: %"PRId32, steps); + PBL_LOG_DBG("steps: %"PRId32, steps); if (steps >= 27 && steps <= 33) { passed = true; } @@ -732,7 +719,7 @@ static void prv_test_30_min_walk(void *context) { int32_t steps; steps = health_service_sum_today(HealthMetricStepCount); steps -= before; - PBL_LOG(LOG_LEVEL_DEBUG, "steps: %"PRId32, steps); + PBL_LOG_DBG("steps: %"PRId32, steps); if (steps >= ((8 * k_steps_per_minute * k_num_minutes) / 10)) { passed = true; } @@ -758,7 +745,7 @@ static void prv_test_sleep(void *context) { int32_t before_total, before_deep; activity_get_metric(ActivityMetricSleepTotalSeconds, 1, &before_total); activity_get_metric(ActivityMetricSleepRestfulSeconds, 1, &before_deep); - PBL_LOG(LOG_LEVEL_DEBUG, "start total: %d, start deep: %d", (int)before_total, (int)before_deep); + PBL_LOG_DBG("start total: %d, start deep: %d", (int)before_total, (int)before_deep); // Capture steps before sleep int32_t steps_before; @@ -771,7 +758,7 @@ static void prv_test_sleep(void *context) { // Capture steps before sleep activity_get_metric(ActivityMetricSleepState, 1, &value); - PBL_LOG(LOG_LEVEL_DEBUG, "sleep state: %d", (int)value); + PBL_LOG_DBG("sleep state: %d", (int)value); // See how many steps we took during sleep. The light sleep simulator ends up providing about // 12 steps every 10 minutes, so without the "no steps during sleep" logic in the activity @@ -779,9 +766,9 @@ static void prv_test_sleep(void *context) { // "no steps during sleep" logic in place, we should get close to 0 steps int32_t steps_after; activity_get_metric(ActivityMetricStepCount, 1, &steps_after); - PBL_LOG(LOG_LEVEL_DEBUG, "steps taken during sleep:: %d", (int)(steps_after - steps_before)); + PBL_LOG_DBG("steps taken during sleep:: %d", (int)(steps_after - steps_before)); if (steps_after - steps_before > 16) { - PBL_LOG(LOG_LEVEL_ERROR, "too many steps during sleep: test FAILED"); + PBL_LOG_ERR("too many steps during sleep: test FAILED"); passed = false; } @@ -795,7 +782,7 @@ static void prv_test_sleep(void *context) { activity_get_metric(ActivityMetricSleepRestfulSeconds, 1, &deep_sleep); deep_sleep -= before_deep; - PBL_LOG(LOG_LEVEL_DEBUG, "total: %d, deep: %d", (int)total_sleep / SECONDS_PER_MINUTE, + PBL_LOG_DBG("total: %d, deep: %d", (int)total_sleep / SECONDS_PER_MINUTE, (int)deep_sleep / SECONDS_PER_MINUTE); const int k_min_total_sleep_min = 240; const int k_max_total_sleep_min = 280; @@ -812,16 +799,16 @@ static void prv_test_sleep(void *context) { // Check other sleep metrics activity_get_metric(ActivityMetricSleepEnterAtSeconds, 1, &value); - PBL_LOG(LOG_LEVEL_DEBUG, "entry minute: %d", (int)(value / SECONDS_PER_MINUTE)); + PBL_LOG_DBG("entry minute: %d", (int)(value / SECONDS_PER_MINUTE)); activity_get_metric(ActivityMetricSleepExitAtSeconds, 1, &value); - PBL_LOG(LOG_LEVEL_DEBUG, "exit minute: %d", (int)(value / SECONDS_PER_MINUTE)); + PBL_LOG_DBG("exit minute: %d", (int)(value / SECONDS_PER_MINUTE)); activity_get_metric(ActivityMetricSleepState, 1, &value); - PBL_LOG(LOG_LEVEL_DEBUG, "sleep state: %d", (int)value); + PBL_LOG_DBG("sleep state: %d", (int)value); activity_get_metric(ActivityMetricSleepStateSeconds, 1, &value); - PBL_LOG(LOG_LEVEL_DEBUG, "sleep state minutes: %d", (int)(value / SECONDS_PER_MINUTE)); + PBL_LOG_DBG("sleep state minutes: %d", (int)(value / SECONDS_PER_MINUTE)); prv_test_end(context, passed); } @@ -841,7 +828,7 @@ static void prv_test_sleep_time_change(void *context) { // Get us into sleep mode time_t start_sleep_time = rtc_get_time(); - PBL_LOG(LOG_LEVEL_DEBUG, "Start sleep: %d", (int)start_sleep_time); + PBL_LOG_DBG("Start sleep: %d", (int)start_sleep_time); prv_feed_light_sleep_min(80); prv_feed_steps_min(2); prv_feed_light_sleep_min(1); @@ -862,7 +849,7 @@ static void prv_test_sleep_time_change(void *context) { // Make sure we registered sleep int32_t value; activity_get_metric(ActivityMetricSleepTotalSeconds, 1, &value); - PBL_LOG(LOG_LEVEL_DEBUG, "sleep total: %"PRIi32" ", value); + PBL_LOG_DBG("sleep total: %"PRIi32" ", value); if (value < (60 * SECONDS_PER_MINUTE) || value > (100 * SECONDS_PER_MINUTE)) { passed = false; } @@ -883,14 +870,14 @@ static void prv_count_sleep_sessions(time_t after_time, int *num_sleep, int *num return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Looking for sessions..."); + PBL_LOG_DBG("Looking for sessions..."); for (uint32_t i = 0; i < session_entries; i++) { ActivitySession *session = &sessions[i]; - PBL_LOG(LOG_LEVEL_DEBUG, " Found session type: %d, start_min: %d, length_min: %"PRIu16" ", + PBL_LOG_DBG(" Found session type: %d, start_min: %d, length_min: %"PRIu16" ", (int)session->type, time_util_get_minute_of_day(session->start_utc), session->length_min); if (session->start_utc < after_time) { - PBL_LOG(LOG_LEVEL_DEBUG, " Ignoring because too old"); + PBL_LOG_DBG(" Ignoring because too old"); continue; } if ((session->type == ActivitySessionType_Sleep) @@ -902,7 +889,7 @@ static void prv_count_sleep_sessions(time_t after_time, int *num_sleep, int *num (*num_nap)++; } } - PBL_LOG(LOG_LEVEL_DEBUG, "Done looking for sessions"); + PBL_LOG_DBG("Done looking for sessions"); } @@ -913,7 +900,7 @@ static void prv_test_nap(void *context) { activity_prefs_sleep_insights_set_enabled(true); const time_t now_utc = rtc_get_time(); - PBL_LOG(LOG_LEVEL_DEBUG, "test start time: %d", (int)now_utc); + PBL_LOG_DBG("test start time: %d", (int)now_utc); const time_t midnight_utc = time_util_get_midnight_of(now_utc); const time_t nap_time_start = midnight_utc + (ALG_PRIMARY_MORNING_MINUTE * SECONDS_PER_MINUTE); @@ -927,7 +914,7 @@ static void prv_test_nap(void *context) { } time_t test_start_utc = rtc_get_time(); - PBL_LOG(LOG_LEVEL_DEBUG, "test start time changed to: %d", (int)test_start_utc); + PBL_LOG_DBG("test start time changed to: %d", (int)test_start_utc); // Reset all stored data activity_test_reset(false /* reset_settings */, true /*tracking_on*/, NULL, NULL); @@ -946,9 +933,9 @@ static void prv_test_nap(void *context) { int nap_count = 0; int sleep_count = 0; prv_count_sleep_sessions(test_start_utc, &sleep_count, &nap_count); - PBL_LOG(LOG_LEVEL_DEBUG, "Found %d sleep sessions and %d nap sessions", sleep_count, nap_count); + PBL_LOG_DBG("Found %d sleep sessions and %d nap sessions", sleep_count, nap_count); if (nap_count > 0 || sleep_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "FAILED: expected only sleep but got %d naps and %d sleep", nap_count, + PBL_LOG_ERR("FAILED: expected only sleep but got %d naps and %d sleep", nap_count, sleep_count); prv_test_end(context, false); return; @@ -959,9 +946,9 @@ static void prv_test_nap(void *context) { // We should have only nap sessions now prv_count_sleep_sessions(test_start_utc, &sleep_count, &nap_count); - PBL_LOG(LOG_LEVEL_DEBUG, "Found %d sleep sessions and %d nap sessions", sleep_count, nap_count); + PBL_LOG_DBG("Found %d sleep sessions and %d nap sessions", sleep_count, nap_count); if (nap_count == 0 || sleep_count > 0) { - PBL_LOG(LOG_LEVEL_ERROR, "FAILED: expected only naps but got %d nap and %d sleep", nap_count, + PBL_LOG_ERR("FAILED: expected only naps but got %d nap and %d sleep", nap_count, sleep_count); prv_test_end(context, false); return; @@ -1242,7 +1229,7 @@ static TestEntry s_test_entries[] = { static void prv_test_begin(int index, void *context) { ActivityTestAppData *data = (ActivityTestAppData *)context; - PBL_LOG(LOG_LEVEL_DEBUG, "Running test: '%s'...", data->menu_items[index].title); + PBL_LOG_DBG("Running test: '%s'...", data->menu_items[index].title); data->menu_items[index].subtitle = "Running..."; layer_mark_dirty(simple_menu_layer_get_layer(data->menu_layer)); @@ -1257,7 +1244,7 @@ static void prv_test_end(void *context, bool passed) { ActivityTestAppData *data = (ActivityTestAppData *)context; const char *result_str = passed ? "PASS" : "FAIL"; - PBL_LOG(LOG_LEVEL_DEBUG, "Test result: %s", result_str); + PBL_LOG_DBG("Test result: %s", result_str); data->menu_items[data->test_index].subtitle = result_str; layer_mark_dirty(simple_menu_layer_get_layer(data->menu_layer)); diff --git a/src/fw/apps/demo/activity_test/activity_test.h b/src/fw/apps/demo/activity_test/activity_test.h new file mode 100644 index 0000000000..5c7ba998d1 --- /dev/null +++ b/src/fw/apps/demo/activity_test/activity_test.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* activity_test_get_app_info(void); diff --git a/src/fw/apps/demo/activity_test/wscript_build b/src/fw/apps/demo/activity_test/wscript_build new file mode 100644 index 0000000000..c4067d0dab --- /dev/null +++ b/src/fw/apps/demo/activity_test/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['activity_test.c'], + target='demo_app_activity_test', +) +bld.env.FW_APPS.append('demo_app_activity_test') + +# vim:filetype=python diff --git a/src/fw/apps/demo/amb_light_read/Kconfig b/src/fw/apps/demo/amb_light_read/Kconfig new file mode 100644 index 0000000000..5e2c0cdfac --- /dev/null +++ b/src/fw/apps/demo/amb_light_read/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_AMB_LIGHT_READ + bool "Amb Light Read" diff --git a/src/fw/apps/demo/amb_light_read/ambient_light_reading.c b/src/fw/apps/demo/amb_light_read/ambient_light_reading.c new file mode 100644 index 0000000000..19272de149 --- /dev/null +++ b/src/fw/apps/demo/amb_light_read/ambient_light_reading.c @@ -0,0 +1,79 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "drivers/ambient_light.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" + +#include +#include + +#define AMBIENT_READING_STR_LEN 32 +typedef struct { + Window *window; + TextLayer *text_layer; + char ambient_reading[AMBIENT_READING_STR_LEN]; +} AmbientLightAppData; + +static void prv_populate_amb_read_str(char *str) { + int level = ambient_light_get_light_level(); + snprintf(str, AMBIENT_READING_STR_LEN, "Amb Level:\n %d", level); +} + +static void timer_callback(void *cb_data) { + AmbientLightAppData *data = app_state_get_user_data(); + + prv_populate_amb_read_str(&data->ambient_reading[0]); + layer_mark_dirty(window_get_root_layer(data->window)); + + app_timer_register(500, timer_callback, NULL); +} + +static void handle_init(void) { + AmbientLightAppData *data = task_malloc_check(sizeof(AmbientLightAppData)); + + data->window = window_create(); + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + data->text_layer = text_layer_create((GRect) + { .origin = { 0, 40 }, .size = { bounds.size.w, 100 } }); + + prv_populate_amb_read_str(&data->ambient_reading[0]); + + text_layer_set_font(data->text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + text_layer_set_text(data->text_layer, data->ambient_reading); + text_layer_set_text_alignment(data->text_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(data->text_layer)); + + app_state_set_user_data(data); + app_window_stack_push(data->window, true); + + app_timer_register(10, timer_callback, NULL); +} + +static void handle_deinit(void) { + AmbientLightAppData *data = app_state_get_user_data(); + text_layer_destroy(data->text_layer); + window_destroy(data->window); + task_free(data); +} + +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +const PebbleProcessMd* ambient_light_reading_get_info() { + static const PebbleProcessMdSystem s_ambient_light_info = { + .common.main_func = s_main, + .name = "Amb Reading" + }; + return (const PebbleProcessMd*) &s_ambient_light_info; +} diff --git a/src/fw/apps/demo/amb_light_read/wscript_build b/src/fw/apps/demo/amb_light_read/wscript_build new file mode 100644 index 0000000000..78e7580c67 --- /dev/null +++ b/src/fw/apps/demo/amb_light_read/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['ambient_light_reading.c'], + target='demo_app_amb_light_read', +) +bld.env.FW_APPS.append('demo_app_amb_light_read') + +# vim:filetype=python diff --git a/src/fw/apps/demo/animated_demo/Kconfig b/src/fw/apps/demo/animated_demo/Kconfig new file mode 100644 index 0000000000..7c10e88159 --- /dev/null +++ b/src/fw/apps/demo/animated_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_ANIMATED_DEMO + bool "Animated Demo" diff --git a/src/fw/apps/demo_apps/animated_demo.c b/src/fw/apps/demo/animated_demo/animated_demo.c similarity index 89% rename from src/fw/apps/demo_apps/animated_demo.c rename to src/fw/apps/demo/animated_demo/animated_demo.c index aa4c5f099b..f6ac5c3f40 100644 --- a/src/fw/apps/demo_apps/animated_demo.c +++ b/src/fw/apps/demo/animated_demo/animated_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "animated_demo.h" diff --git a/src/fw/apps/demo/animated_demo/animated_demo.h b/src/fw/apps/demo/animated_demo/animated_demo.h new file mode 100644 index 0000000000..bb96dbfe96 --- /dev/null +++ b/src/fw/apps/demo/animated_demo/animated_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* animated_demo_get_app_info(); diff --git a/src/fw/apps/demo/animated_demo/wscript_build b/src/fw/apps/demo/animated_demo/wscript_build new file mode 100644 index 0000000000..f38e043cc3 --- /dev/null +++ b/src/fw/apps/demo/animated_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['animated_demo.c'], + target='demo_app_animated_demo', +) +bld.env.FW_APPS.append('demo_app_animated_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/app_heap_demo/Kconfig b/src/fw/apps/demo/app_heap_demo/Kconfig new file mode 100644 index 0000000000..b1f29f19fc --- /dev/null +++ b/src/fw/apps/demo/app_heap_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_APP_HEAP_DEMO + bool "App Heap Demo" diff --git a/src/fw/apps/demo/app_heap_demo/heap_demo.c b/src/fw/apps/demo/app_heap_demo/heap_demo.c new file mode 100644 index 0000000000..5d629df328 --- /dev/null +++ b/src/fw/apps/demo/app_heap_demo/heap_demo.c @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "heap_demo.h" +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" + +#include + +// This app allocated approximately 75% of memory available to it. +// The idea is to run it multiple times to show that all data is being freed on +// exit, and is available for the next app to use. + +static TextLayer *text_heap_info; +static Window *window; + +static void init(void) { + char *start = app_malloc_check(1); // Get a pointer close to where the heap starts + + window = window_create(); + app_window_stack_push(window, true /* Animated */); + Layer *window_layer = window_get_root_layer(window); + + text_heap_info = text_layer_create(window_layer->frame); + text_layer_set_text_color(text_heap_info, GColorWhite); + text_layer_set_background_color(text_heap_info, GColorBlack); + text_layer_set_font(text_heap_info, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + + // Get the size of the heap from the beginning of the first thing allocated + unsigned long heap_size = 0x20020000 - (unsigned long)start; + unsigned long alloc_size = 0.75*heap_size; + + char *buf = app_malloc_check(alloc_size); + snprintf(buf, 80, "%luB/%luB\n\nJust allocated %lu%% of the app heap.", alloc_size, heap_size, 100*alloc_size/heap_size); + text_layer_set_text(text_heap_info, buf); + layer_add_child(window_layer, text_layer_get_layer(text_heap_info)); +} + +static void deinit(void) { + // Don't free anything +} + +static void s_main(void) { + init(); + app_event_loop(); + deinit(); +} + +const PebbleProcessMd* app_heap_demo_app_get_info(void) { + static const PebbleProcessMdSystem s_app_heap_demo_app_info = { + .common.main_func = &s_main, + .name = "AppHeap" + }; + return (const PebbleProcessMd*) &s_app_heap_demo_app_info; +} diff --git a/src/fw/apps/demo/app_heap_demo/heap_demo.h b/src/fw/apps/demo/app_heap_demo/heap_demo.h new file mode 100644 index 0000000000..738aee5028 --- /dev/null +++ b/src/fw/apps/demo/app_heap_demo/heap_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* app_heap_demo_app_get_info(void); diff --git a/src/fw/apps/demo/app_heap_demo/wscript_build b/src/fw/apps/demo/app_heap_demo/wscript_build new file mode 100644 index 0000000000..ff8eccfae2 --- /dev/null +++ b/src/fw/apps/demo/app_heap_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['heap_demo.c'], + target='demo_app_app_heap_demo', +) +bld.env.FW_APPS.append('demo_app_app_heap_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/bouncing_box_demo/Kconfig b/src/fw/apps/demo/bouncing_box_demo/Kconfig new file mode 100644 index 0000000000..f88de39914 --- /dev/null +++ b/src/fw/apps/demo/bouncing_box_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_BOUNCING_BOX_DEMO + bool "Bouncing Box Demo" diff --git a/src/fw/apps/demo_apps/bouncing_box.c b/src/fw/apps/demo/bouncing_box_demo/bouncing_box.c similarity index 82% rename from src/fw/apps/demo_apps/bouncing_box.c rename to src/fw/apps/demo/bouncing_box_demo/bouncing_box.c index 16560f0e81..7dd0be75d2 100644 --- a/src/fw/apps/demo_apps/bouncing_box.c +++ b/src/fw/apps/demo/bouncing_box_demo/bouncing_box.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bouncing_box.h" diff --git a/src/fw/apps/demo/bouncing_box_demo/bouncing_box.h b/src/fw/apps/demo/bouncing_box_demo/bouncing_box.h new file mode 100644 index 0000000000..a1ecd3d78e --- /dev/null +++ b/src/fw/apps/demo/bouncing_box_demo/bouncing_box.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* bouncing_box_demo_get_app_info(void); + diff --git a/src/fw/apps/demo/bouncing_box_demo/wscript_build b/src/fw/apps/demo/bouncing_box_demo/wscript_build new file mode 100644 index 0000000000..09ad0d9939 --- /dev/null +++ b/src/fw/apps/demo/bouncing_box_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['bouncing_box.c'], + target='demo_app_bouncing_box_demo', +) +bld.env.FW_APPS.append('demo_app_bouncing_box_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/click/Kconfig b/src/fw/apps/demo/click/Kconfig new file mode 100644 index 0000000000..9f2006ad07 --- /dev/null +++ b/src/fw/apps/demo/click/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_CLICK + bool "Click" diff --git a/src/fw/apps/demo/click/click.c b/src/fw/apps/demo/click/click.c new file mode 100644 index 0000000000..19bb7a861f --- /dev/null +++ b/src/fw/apps/demo/click/click.c @@ -0,0 +1,178 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "click.h" + +#include "applib/app.h" +#include "process_state/app_state/app_state.h" +#include "applib/ui/ui.h" +#include "applib/ui/window.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/math.h" + +#include + +#define TEXT_BUFFER_SIZE 64 + +typedef struct { + Window window; + TextLayer text; + char text_buffer[TEXT_BUFFER_SIZE]; +} ClickAppData; + +//////////////////////////// +// Click app's main window + +//! Toggle the colors of the label, so we can see change even if the text stayed the same: +static void toggle_color(Window *window) { + ClickAppData *data = window_get_user_data(window); + TextLayer *text = &data->text; + const GColor bg_color = text->background_color; + if (gcolor_equal(bg_color, GColorBlack)) { + text_layer_set_background_color(text, GColorWhite); + text_layer_set_text_color(text, GColorBlack); + } else { + text_layer_set_background_color(text, GColorBlack); + text_layer_set_text_color(text, GColorWhite); + } +} + +static void raw_click_handler(ClickRecognizerRef recognizer, Window *window, const bool up) { + ClickAppData *data = window_get_user_data(window); + sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, up ? "Raw UP" : "Raw DOWN"); + if (up) { // PBL_LOG requires a fixed const string, so can't use ternary + PBL_LOG_DBG("Raw UP"); + } else { + PBL_LOG_DBG("Raw DOWN"); + } + text_layer_set_text(&data->text, data->text_buffer); + toggle_color(window); + (void)recognizer; +} + +static void raw_up_click_handler(ClickRecognizerRef recognizer, Window *window) { + raw_click_handler(recognizer, window, true); +} + +static void raw_down_click_handler(ClickRecognizerRef recognizer, Window *window) { + raw_click_handler(recognizer, window, false); +} + +static void select_multi_click_handler(ClickRecognizerRef recognizer, Window *window) { + ClickAppData *data = window_get_user_data(window); + const uint16_t count = click_number_of_clicks_counted(recognizer); + sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Multi Click! (%u)\nMin: 2, Max: 10", count); + PBL_LOG_DBG("Multi Click! (%u)", click_number_of_clicks_counted(recognizer)); + text_layer_set_text(&data->text, data->text_buffer); + toggle_color(window); +} + +static void select_single_click_handler(ClickRecognizerRef recognizer, Window *window) { + ClickAppData *data = window_get_user_data(window); + const uint16_t count = click_number_of_clicks_counted(recognizer); + sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Single Click! (%u)", count); + PBL_LOG_DBG("Single Click! (%u)", click_number_of_clicks_counted(recognizer)); + text_layer_set_text(&data->text, data->text_buffer); + toggle_color(window); + + // Let's try shortening the repeat interval as we go: + ClickConfig *config = click_recognizer_get_config(recognizer); + config->click.repeat_interval_ms = MAX((config->click.repeat_interval_ms / 2), 100); +} + +static void select_long_click_handler(ClickRecognizerRef recognizer, Window *window) { + ClickAppData *data = window_get_user_data(window); + sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Long Click!"); + PBL_LOG_DBG("Long Click!"); + text_layer_set_text(&data->text, data->text_buffer); + toggle_color(window); + (void)recognizer; +} + +static void select_long_click_release_handler(ClickRecognizerRef recognizer, Window *window) { + ClickAppData *data = window_get_user_data(window); + sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Long Click Released!"); + PBL_LOG_DBG("Long Click Released!"); + text_layer_set_text(&data->text, data->text_buffer); + toggle_color(window); + (void)recognizer; +} + +static void config_provider(Window *window) { + // See ui/click.h for more information and default values. + + // single click / repeat-on-hold config: + window_single_repeating_click_subscribe(BUTTON_ID_SELECT, 1000, (ClickHandler)select_single_click_handler); // "hold-to-repeat" gets overriden if there's a long click handler configured! + + // multi click config: + window_multi_click_subscribe(BUTTON_ID_SELECT, 2, 10, 0, false, (ClickHandler) select_multi_click_handler); + + // long click config: + window_long_click_subscribe(BUTTON_ID_SELECT, 700, (ClickHandler) select_long_click_handler, (ClickHandler) select_long_click_release_handler); + + // single click / repeat-on-hold config: + window_single_repeating_click_subscribe(BUTTON_ID_UP, 1000, (ClickHandler) select_single_click_handler); // "hold-to-repeat" gets overriden if there's a long click handler configured! + + // multi click config: + window_multi_click_subscribe(BUTTON_ID_UP, 2, 10, 0, true, (ClickHandler) select_multi_click_handler); + + // raw: + window_raw_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) raw_down_click_handler, (ClickHandler) raw_up_click_handler, NULL); +} + +static void prv_window_load(Window *window) { + ClickAppData *data = window_get_user_data(window); + TextLayer *text = &data->text; + text_layer_init(text, &window->layer.bounds); + text_layer_set_text(text, "Use select button and try different clicks: single, hold-to-repeat, multiple, long press, etc.\n\nNOTE: a long click config will override hold-to-repeat config. Comment out the long_click section of the config to enable hold-to-repeat."); + layer_add_child(&window->layer, &text->layer); +} + +static void push_window(ClickAppData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Click Demo")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + window_set_click_config_provider(window, (ClickConfigProvider) config_provider); + const bool animated = true; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate + +static void handle_init(void) { + ClickAppData *data = (ClickAppData*) app_malloc_check(sizeof(ClickAppData)); + if (data == NULL) { + PBL_CROAK("Out of memory"); + } + app_state_set_user_data(data); + push_window(data); +} + +static void handle_deinit(void) { + ClickAppData *data = app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* click_app_get_info() { + static const PebbleProcessMdSystem s_click_app_info = { + .common.main_func = s_main, + .name = "Clicks" + }; + return (const PebbleProcessMd*) &s_click_app_info; +} + +#undef TEXT_BUFFER_SIZE diff --git a/src/fw/apps/demo/click/click.h b/src/fw/apps/demo/click/click.h new file mode 100644 index 0000000000..e3d03701ef --- /dev/null +++ b/src/fw/apps/demo/click/click.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* click_app_get_info(); diff --git a/src/fw/apps/demo/click/wscript_build b/src/fw/apps/demo/click/wscript_build new file mode 100644 index 0000000000..72c0b5f73c --- /dev/null +++ b/src/fw/apps/demo/click/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['click.c'], + target='demo_app_click', +) +bld.env.FW_APPS.append('demo_app_click') + +# vim:filetype=python diff --git a/src/fw/apps/demo/data_logging_test/Kconfig b/src/fw/apps/demo/data_logging_test/Kconfig new file mode 100644 index 0000000000..6bf060e2f3 --- /dev/null +++ b/src/fw/apps/demo/data_logging_test/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_DATA_LOGGING_TEST + bool "Data Logging Test" diff --git a/src/fw/apps/demo_apps/data_logging_test.c b/src/fw/apps/demo/data_logging_test/data_logging_test.c similarity index 87% rename from src/fw/apps/demo_apps/data_logging_test.c rename to src/fw/apps/demo/data_logging_test/data_logging_test.c index 4e871dfed7..c22c6b8fa9 100644 --- a/src/fw/apps/demo_apps/data_logging_test.c +++ b/src/fw/apps/demo/data_logging_test/data_logging_test.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "data_logging_test.h" #include "system/logging.h" -#include "services/common/comm_session/session.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/data_logging/dls_private.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/dls_private.h" #include "applib/app.h" #include "applib/data_logging.h" #include "applib/ui/app_window_stack.h" diff --git a/src/fw/apps/demo/data_logging_test/data_logging_test.h b/src/fw/apps/demo/data_logging_test/data_logging_test.h new file mode 100644 index 0000000000..7f9cb0f99a --- /dev/null +++ b/src/fw/apps/demo/data_logging_test/data_logging_test.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* data_logging_test_get_info(); diff --git a/src/fw/apps/demo/data_logging_test/wscript_build b/src/fw/apps/demo/data_logging_test/wscript_build new file mode 100644 index 0000000000..0f8706b0ec --- /dev/null +++ b/src/fw/apps/demo/data_logging_test/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['data_logging_test.c'], + target='demo_app_data_logging_test', +) +bld.env.FW_APPS.append('demo_app_data_logging_test') + +# vim:filetype=python diff --git a/src/fw/apps/demo/deadlock/Kconfig b/src/fw/apps/demo/deadlock/Kconfig new file mode 100644 index 0000000000..ea20e6c4b7 --- /dev/null +++ b/src/fw/apps/demo/deadlock/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_DEADLOCK + bool "Deadlock" diff --git a/src/fw/apps/demo/deadlock/deadlock.c b/src/fw/apps/demo/deadlock/deadlock.c new file mode 100644 index 0000000000..60d302c50c --- /dev/null +++ b/src/fw/apps/demo/deadlock/deadlock.c @@ -0,0 +1,56 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "profile_mutexes.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" + +#include "system/logging.h" +#include "os/mutex.h" +#include "system/profiler.h" + +#include "kernel/util/sleep.h" +#include "pbl/services/new_timer/new_timer.h" + +static Window *window; +static PebbleMutex *s_mutex; +static PebbleMutex *s_mutex2; + +static void callback(void *data) { + PBL_LOG_DBG("Locking mutex 2 (new timer)"); + mutex_lock(s_mutex2); + PBL_LOG_DBG("Locking mutex 1 (new timer)"); + mutex_lock(s_mutex); +} + +static void deadlock(void) { + s_mutex = mutex_create(); + s_mutex2 = mutex_create(); + TimerID timer = new_timer_create(); + new_timer_start(timer, 10, callback, NULL, 0); + + PBL_LOG_DBG("Locking mutex 1"); + mutex_lock(s_mutex); + psleep(20); + PBL_LOG_DBG("Locking mutex 2"); + mutex_lock(s_mutex2); +} + +static void s_main(void) { + window = window_create(); + app_window_stack_push(window, true /* Animated */); + + deadlock(); + + app_event_loop(); +} + +const PebbleProcessMd* deadlock_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Deadlock" + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/deadlock/deadlock.h b/src/fw/apps/demo/deadlock/deadlock.h new file mode 100644 index 0000000000..89a84b01fa --- /dev/null +++ b/src/fw/apps/demo/deadlock/deadlock.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* deadlock_get_app_info(void); diff --git a/src/fw/apps/demo/deadlock/wscript_build b/src/fw/apps/demo/deadlock/wscript_build new file mode 100644 index 0000000000..39b97b18c0 --- /dev/null +++ b/src/fw/apps/demo/deadlock/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=[ +'.', +'../shared', +], + source=['deadlock.c'], + target='demo_app_deadlock', +) +bld.env.FW_APPS.append('demo_app_deadlock') + +# vim:filetype=python diff --git a/src/fw/apps/demo_apps/demo_images/Drake_after_01.png.h b/src/fw/apps/demo/demo_images/Drake_after_01.png.h similarity index 99% rename from src/fw/apps/demo_apps/demo_images/Drake_after_01.png.h rename to src/fw/apps/demo/demo_images/Drake_after_01.png.h index 67cef950f3..d1a8911d07 100644 --- a/src/fw/apps/demo_apps/demo_images/Drake_after_01.png.h +++ b/src/fw/apps/demo/demo_images/Drake_after_01.png.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/apps/demo_apps/demo_images/Jane_after_01.png.h b/src/fw/apps/demo/demo_images/Jane_after_01.png.h similarity index 99% rename from src/fw/apps/demo_apps/demo_images/Jane_after_01.png.h rename to src/fw/apps/demo/demo_images/Jane_after_01.png.h index afab532336..e7fec90d30 100644 --- a/src/fw/apps/demo_apps/demo_images/Jane_after_01.png.h +++ b/src/fw/apps/demo/demo_images/Jane_after_01.png.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/apps/demo_apps/demo_images/Morgan_after_03.png.h b/src/fw/apps/demo/demo_images/Morgan_after_03.png.h similarity index 99% rename from src/fw/apps/demo_apps/demo_images/Morgan_after_03.png.h rename to src/fw/apps/demo/demo_images/Morgan_after_03.png.h index 080f410cb8..0d03bcf220 100644 --- a/src/fw/apps/demo_apps/demo_images/Morgan_after_03.png.h +++ b/src/fw/apps/demo/demo_images/Morgan_after_03.png.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/apps/demo_apps/demo_images/flower_after_01.png.h b/src/fw/apps/demo/demo_images/flower_after_01.png.h similarity index 99% rename from src/fw/apps/demo_apps/demo_images/flower_after_01.png.h rename to src/fw/apps/demo/demo_images/flower_after_01.png.h index b20c4f7751..34b7befb24 100644 --- a/src/fw/apps/demo_apps/demo_images/flower_after_01.png.h +++ b/src/fw/apps/demo/demo_images/flower_after_01.png.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" diff --git a/src/fw/apps/demo/dialogs/Kconfig b/src/fw/apps/demo/dialogs/Kconfig new file mode 100644 index 0000000000..9d6086f0b2 --- /dev/null +++ b/src/fw/apps/demo/dialogs/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_DIALOGS + bool "Dialogs" diff --git a/src/fw/apps/demo_apps/dialogs_demo.c b/src/fw/apps/demo/dialogs/dialogs_demo.c similarity index 96% rename from src/fw/apps/demo_apps/dialogs_demo.c rename to src/fw/apps/demo/dialogs/dialogs_demo.c index c5a47be503..2ffc24aefd 100644 --- a/src/fw/apps/demo_apps/dialogs_demo.c +++ b/src/fw/apps/demo/dialogs/dialogs_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dialogs_demo.h" @@ -31,7 +18,7 @@ #include "shell/normal/watchface.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "system/passert.h" #include "util/size.h" diff --git a/src/fw/apps/demo/dialogs/dialogs_demo.h b/src/fw/apps/demo/dialogs/dialogs_demo.h new file mode 100644 index 0000000000..2b7331e6db --- /dev/null +++ b/src/fw/apps/demo/dialogs/dialogs_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* dialogs_demo_get_app_info(); diff --git a/src/fw/apps/demo/dialogs/wscript_build b/src/fw/apps/demo/dialogs/wscript_build new file mode 100644 index 0000000000..b8d690ce04 --- /dev/null +++ b/src/fw/apps/demo/dialogs/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['dialogs_demo.c'], + target='demo_app_dialogs', +) +bld.env.FW_APPS.append('demo_app_dialogs') + +# vim:filetype=python diff --git a/src/fw/apps/demo/double_tap_test/Kconfig b/src/fw/apps/demo/double_tap_test/Kconfig new file mode 100644 index 0000000000..1e96204856 --- /dev/null +++ b/src/fw/apps/demo/double_tap_test/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_DOUBLE_TAP_TEST + bool "Double Tap Test" diff --git a/src/fw/apps/demo_apps/double_tap_test.c b/src/fw/apps/demo/double_tap_test/double_tap_test.c similarity index 83% rename from src/fw/apps/demo_apps/double_tap_test.c rename to src/fw/apps/demo/double_tap_test/double_tap_test.c index c44d8fb0d6..6f2d640893 100644 --- a/src/fw/apps/demo_apps/double_tap_test.c +++ b/src/fw/apps/demo/double_tap_test/double_tap_test.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "double_tap_test.h" diff --git a/src/fw/apps/demo/double_tap_test/double_tap_test.h b/src/fw/apps/demo/double_tap_test/double_tap_test.h new file mode 100644 index 0000000000..69b677c159 --- /dev/null +++ b/src/fw/apps/demo/double_tap_test/double_tap_test.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* double_tap_test_get_info(); diff --git a/src/fw/apps/demo/double_tap_test/wscript_build b/src/fw/apps/demo/double_tap_test/wscript_build new file mode 100644 index 0000000000..8329b2d76b --- /dev/null +++ b/src/fw/apps/demo/double_tap_test/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['double_tap_test.c'], + target='demo_app_double_tap_test', +) +bld.env.FW_APPS.append('demo_app_double_tap_test') + +# vim:filetype=python diff --git a/src/fw/apps/demo/event_service/Kconfig b/src/fw/apps/demo/event_service/Kconfig new file mode 100644 index 0000000000..e18c4a53c1 --- /dev/null +++ b/src/fw/apps/demo/event_service/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_EVENT_SERVICE + bool "Event Service" diff --git a/src/fw/apps/demo/event_service/event_service.c b/src/fw/apps/demo/event_service/event_service.c new file mode 100644 index 0000000000..d6ca6f60a1 --- /dev/null +++ b/src/fw/apps/demo/event_service/event_service.c @@ -0,0 +1,90 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "text_layout.h" + +#include "applib/accel_service.h" +#include "applib/app.h" +#include "applib/connection_service.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" + +#include +#include + +typedef struct { + Window window; + TextLayer count_layer; + TextLayer connected_layer; + char count_str[12]; + int count; +} EventServiceAppData; + +static void handle_tap(AccelAxisType axis, int32_t sign) { + EventServiceAppData *data = app_state_get_user_data(); + ++data->count; + snprintf(data->count_str, sizeof(data->count_str), "%d", data->count); + text_layer_set_text(&data->count_layer, data->count_str); +} + +static void handle_bt_connection(bool connected) { + EventServiceAppData *data = app_state_get_user_data(); + text_layer_set_text(&data->connected_layer, connected ? "connected" : "disconnected"); +} + +static void handle_deinit(void) { + EventServiceAppData *data = app_state_get_user_data(); + app_free(data); + + accel_tap_service_unsubscribe(); + connection_service_unsubscribe(); +} + +static void handle_init(void) { + EventServiceAppData *data = app_malloc_check(sizeof(EventServiceAppData)); + memset(data, 0, sizeof(EventServiceAppData)); + app_state_set_user_data(data); + + // Init window + window_init(&data->window, "Event Service Demo"); + app_window_stack_push(&data->window, true /* Animated */); + + // Init text layer + Layer *windowlayer = &data->window.layer; + const int16_t width = windowlayer->bounds.size.w - ACTION_BAR_WIDTH - 6; + text_layer_init(&data->count_layer, &GRect(0, 0, width, 20)); + layer_add_child(&data->window.layer, &data->count_layer.layer); + text_layer_init(&data->connected_layer, &GRect(0, 20, width, 20)); + layer_add_child(&data->window.layer, &data->connected_layer.layer); + + text_layer_set_text(&data->count_layer, "No Presses"); + text_layer_set_text(&data->connected_layer, "No connection event"); + + // subscribe to the accelerometer event stream + accel_tap_service_subscribe(&handle_tap); + ConnectionHandlers conn_handlers = { + .pebble_app_connection_handler = handle_bt_connection + }; + + connection_service_subscribe(conn_handlers); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* event_service_app_get_info() { + static const PebbleProcessMdSystem event_service_app_info = { + .common.main_func = &s_main, + .name = "Event Service App", + }; + return (const PebbleProcessMd*) &event_service_app_info; +} diff --git a/src/fw/apps/demo/event_service/event_service.h b/src/fw/apps/demo/event_service/event_service.h new file mode 100644 index 0000000000..eaf7e3af72 --- /dev/null +++ b/src/fw/apps/demo/event_service/event_service.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* event_service_app_get_info(); diff --git a/src/fw/apps/demo/event_service/wscript_build b/src/fw/apps/demo/event_service/wscript_build new file mode 100644 index 0000000000..f0c04734bf --- /dev/null +++ b/src/fw/apps/demo/event_service/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=[ +'.', +'../shared', +], + source=['event_service.c'], + target='demo_app_event_service', +) +bld.env.FW_APPS.append('demo_app_event_service') + +# vim:filetype=python diff --git a/src/fw/apps/demo/exit/Kconfig b/src/fw/apps/demo/exit/Kconfig new file mode 100644 index 0000000000..0698ff257c --- /dev/null +++ b/src/fw/apps/demo/exit/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_EXIT + bool "Exit" diff --git a/src/fw/apps/demo/exit/exit.c b/src/fw/apps/demo/exit/exit.c new file mode 100644 index 0000000000..3abfcea9e6 --- /dev/null +++ b/src/fw/apps/demo/exit/exit.c @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "exit.h" +#include "applib/app_logging.h" + +// Verify that applications can actually call stdlib's exit(). +static void s_exit_app_main(void) { + + // Make visible in the debugger if we are running privileged. + uint32_t value; + __asm volatile("mrs %0, control" : "=r" (value)); + volatile bool is_privileged = !(value & 0x1); + APP_LOG(LOG_LEVEL_DEBUG, "Exit app is %sprivileged; now exiting", is_privileged ? "" : "not "); +} + +const PebbleProcessMd *exit_app_get_app_info(void) { + static const PebbleProcessMdSystem s_exit_app_info = { + .common.main_func = s_exit_app_main, + .common.is_unprivileged = true, + .name = "Exit Test" + }; + return (const PebbleProcessMd*) &s_exit_app_info; +} + diff --git a/src/fw/apps/demo/exit/exit.h b/src/fw/apps/demo/exit/exit.h new file mode 100644 index 0000000000..1aa0ba6a41 --- /dev/null +++ b/src/fw/apps/demo/exit/exit.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *exit_app_get_app_info(); diff --git a/src/fw/apps/demo/exit/wscript_build b/src/fw/apps/demo/exit/wscript_build new file mode 100644 index 0000000000..82b7827684 --- /dev/null +++ b/src/fw/apps/demo/exit/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['exit.c'], + target='demo_app_exit', +) +bld.env.FW_APPS.append('demo_app_exit') + +# vim:filetype=python diff --git a/src/fw/apps/demo/flash_demo.c b/src/fw/apps/demo/flash_demo.c new file mode 100644 index 0000000000..7390473010 --- /dev/null +++ b/src/fw/apps/demo/flash_demo.c @@ -0,0 +1,98 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "flash_demo.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" + +#include "drivers/flash.h" +#include "flash_region/flash_region.h" +#include "system/logging.h" + +static Window *window; + +#define BASE_ADDRESS 0x380000 + +static void test_write_short(void) { + uint16_t buffer; + flash_read_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); + PBL_LOG_DBG(">> Addr 0x%x is 0x%"PRIx16, BASE_ADDRESS, buffer); + + buffer = 0x0505; + flash_write_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); + PBL_LOG_DBG(">> Addr 0x%x Written to 0x%x", BASE_ADDRESS, buffer); + + uint8_t read_buffer = 0x0; + flash_read_bytes((uint8_t*) &read_buffer, BASE_ADDRESS, sizeof(read_buffer)); + PBL_LOG_DBG(">> Addr 0x%x is (8) 0x%"PRIx8, BASE_ADDRESS, read_buffer); + + buffer = 0x0; + flash_read_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); + PBL_LOG_DBG(">> Addr 0x%x is (16) 0x%"PRIx16, BASE_ADDRESS, buffer); +} + +static void test_write_bytes(void) { + for (int i = 1; i < 127; ++i) { + uint8_t data = i; + flash_write_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); + PBL_LOG_DBG(">> Wrote Addr 0x%x is 0x%"PRIx8, i, data); + } + + for (int i = 0; i < 128; ++i) { + uint8_t data = 0; + flash_read_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); + PBL_LOG_DBG(">> Read Addr 0x%x is (8) 0x%"PRIx8, i, data); + } +} + +static void test_write_block(void) { + uint8_t data[64]; + + for (unsigned int i = 0; i < sizeof(data); ++i) { + data[i] = i; + } + + flash_write_bytes(data, BASE_ADDRESS + 31, sizeof(data)); + + for (int i = 0; i < 128; ++i) { + uint8_t data = 0; + flash_read_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); + PBL_LOG_DBG(">> Read Addr 0x%x is (8) 0x%"PRIx8, i, data); + } +} + +static void do_flash_operation(void) { + PBL_LOG_DBG(">> Flash operation time!"); + PBL_LOG_DBG(">> Flash operation time!"); + PBL_LOG_DBG(">> Flash operation time!"); + PBL_LOG_DBG(">> Flash operation time!"); + PBL_LOG_DBG(">> Flash operation time!"); + PBL_LOG_DBG(">> Flash operation time!"); + + PBL_LOG_DBG(">> Erasing 0x%x", BASE_ADDRESS); + flash_erase_sector_blocking(BASE_ADDRESS); + PBL_LOG_DBG(">> Erasing 0x%x Done", BASE_ADDRESS); + + test_write_short(); +} + +static void s_main(void) { + window = window_create(); + app_window_stack_push(window, true /* Animated */); + + do_flash_operation(); + + app_event_loop(); +} + +const PebbleProcessMd* flash_demo_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Flash Demo" + }; + + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/demo/flash_demo.h b/src/fw/apps/demo/flash_demo.h new file mode 100644 index 0000000000..9635c880f1 --- /dev/null +++ b/src/fw/apps/demo/flash_demo.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* flash_demo_get_app_info(void); + diff --git a/src/fw/apps/demo/flash_diagnostic/Kconfig b/src/fw/apps/demo/flash_diagnostic/Kconfig new file mode 100644 index 0000000000..90069d578f --- /dev/null +++ b/src/fw/apps/demo/flash_diagnostic/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_FLASH_DIAGNOSTIC + bool "Flash Diagnostic" diff --git a/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c b/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c new file mode 100644 index 0000000000..96e5529e52 --- /dev/null +++ b/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.c @@ -0,0 +1,322 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "flash_diagnostic.h" + +#include + +#include "applib/app.h" +#include "applib/ui/simple_menu_layer.h" +#include "applib/ui/ui.h" +#include "applib/ui/window.h" +#include "drivers/flash.h" +#include "drivers/task_watchdog.h" +#include "flash_region/flash_region.h" +#include "kernel/pbl_malloc.h" +#include "kernel/pebble_tasks.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "resource/resource_storage_flash.h" +#include "system/logging.h" +#include "system/passert.h" +#include "kernel/util/sleep.h" +#include "util/size.h" + +#define NUM_REGIONS ARRAY_LENGTH(s_flash_regions) + +#define FILE_WRITE_STRESS (NUM_REGIONS) +#define FILE_SUBSECTOR_STRESS (FILE_WRITE_STRESS + 1) +#define NUM_STRESS_TESTS (FILE_SUBSECTOR_STRESS - NUM_REGIONS + 1) + +#define NUM_MENU_ITEMS (NUM_REGIONS + NUM_STRESS_TESTS) + +struct Region { + char *name; + uint32_t begin; + uint32_t end; +}; + +static struct Region s_flash_regions[] = { + { + "System Resources", + FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN, + FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END + }, + { + "System Resources", + FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN, + FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END + }, + { + "File System", + FLASH_REGION_FILESYSTEM_BEGIN, + FLASH_REGION_FILESYSTEM_END + }, +}; + +typedef struct { + Window window; + SimpleMenuLayer menu_layer; + SimpleMenuSection menu_section; + SimpleMenuItem menu_items[NUM_MENU_ITEMS]; +} FlashDiagAppData; + +typedef struct { + Window window; + TextLayer *text_layer; + int stress_iteration; + int stress_index; +} FlashStressWindow; + + +static bool check_region_erased(struct Region region) { + PBL_LOG_SYNC_INFO("Checking Erase ..."); + bool success = true; + for (uint32_t i = region.begin; i < region.end; i += sizeof(uint32_t)) { + uint32_t read = 0; + flash_read_bytes((uint8_t *)&read, i, sizeof(read)); + if (read != 0xffffffff) { + PBL_LOG_SYNC_INFO(">>>> Address 0x%lx failed to erase: 0x%lx", i, read); + success = false; + } + } + + return success; +} + +// region: region to check (and possibly write) +// use_rand: write random values +// perform_writes: perform writes if true, else see if the region reads as 0 +static bool check_region_write(struct Region region, bool use_rand, + bool perform_writes) { + bool success = true; + uint32_t write_rand = (use_rand && perform_writes) ? rand() : 0; + + PBL_LOG_SYNC_INFO("%sChecking 0x%lx over 0x%lx 0x%lx", + (perform_writes) ? "Writing and " : "", write_rand, region.begin, + region.end); + + for (uint32_t i = region.begin; i < region.end; i += sizeof(uint32_t)) { + uint32_t write = write_rand; + uint32_t read = 0xffff; + if (perform_writes) { + flash_write_bytes((uint8_t *)&write, i, sizeof(write)); + } + flash_read_bytes((uint8_t *)&read, i, sizeof(read)); + if (read != write) { + PBL_LOG_SYNC_INFO(">>>> Address 0x%lx failed to write: 0x%lx 0x%lx", + i, read, write); + success = false; + } + } + + return success; +} + +// Writes 0's to the first half of a flash sector and confirms that everything +// reads as 0. Then uses 8 subsector erases to erase the second half of the +// sector. Then re-reads the first half to see if any bits have flipped +static bool check_subsector_bitflip(struct Region region) { + bool success = true; + + if (((region.end - region.begin) % (64 * 1024)) != 0) { + PBL_LOG_WRN("Test only works on 64k aligned regions"); + return (false); + } + + const uint32_t block_size = 64 * 1024; + const uint32_t write_size = 32 * 1024; + const uint32_t subsector_size = 4 * 1024; + + for (uint32_t i = region.begin; i < region.end; i += block_size) { + + struct Region write_region; + write_region.begin = i; + write_region.end = i + write_size; + + if (!check_region_write(write_region, false, true)) { + success = false; + break; + } + + uint32_t subsec_begin = block_size - write_size; + PBL_ASSERTN((subsec_begin % (32*1024)) == 0); + + for (uint32_t subsec = subsec_begin; subsec < block_size; + subsec += subsector_size) { + uint32_t erase = subsec + i; + PBL_ASSERTN((erase % (4 * 1024)) == 0); + PBL_LOG_SYNC_INFO("Subsector Erase of 0x%lx", erase); + flash_erase_subsector_blocking(erase); + } + + if (!check_region_write(write_region, false, false)) { + success = false; + break; + } + psleep(5); + } + + return (success); +} + +static void menu_select_callback(int index, void *data) { + struct Region region = s_flash_regions[index]; + PBL_LOG_INFO(">>>> Erase %s", region.name); + flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); + PBL_LOG_INFO(">>>> Checking '%s' is erased", region.name); + check_region_erased(region); + PBL_LOG_INFO(">>>> Checking '%s' can write", region.name); + check_region_write(region, false, true); + PBL_LOG_INFO(">>>> Done!"); +} + +FlashStressWindow stress_data; +static bool abort_stress_test; +static void update_text(int iter, int tot, bool failed) { + static char status[50]; + + snprintf(status, sizeof(status), "%d / %d %s", iter, tot, + failed ? "Failed Out" : "Complete"); + text_layer_set_text(stress_data.text_layer, (char *)status); +} + +static void app_timer_cb(void *data) { + static const int num_stress_iters = 1000; + struct Region region = s_flash_regions[2]; + PBL_LOG_INFO(">>>> %s %d", "Test Loop", stress_data.stress_iteration); + + PBL_LOG_INFO("Erasing 0x%lx to 0x%lx", region.begin, region.end); + flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); + + bool failed = true; + if (stress_data.stress_index == FILE_WRITE_STRESS) { + failed = (!check_region_erased(region) || !check_region_write(region, true, true)); + } else if (stress_data.stress_index == FILE_SUBSECTOR_STRESS) { + failed = (!check_region_erased(region) || !check_subsector_bitflip(region)); + } else { + PBL_LOG_WRN("Unknown stress test %d!", stress_data.stress_index); + } + + if (!abort_stress_test) { + update_text(++stress_data.stress_iteration, num_stress_iters, failed); + + if (!failed && (stress_data.stress_iteration < num_stress_iters)) { + app_timer_register(1000, app_timer_cb, NULL); // allow for animation to complete + } else { // clean up state + flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); + } + } +} + +static void stress_window_load(Window *data) { + Layer *layer = window_get_root_layer(data); + const int16_t width = layer->frame.size.w - ACTION_BAR_WIDTH - 3; + + stress_data.text_layer = text_layer_create(GRect(4, 44, width, 60)); + TextLayer *text_layer = stress_data.text_layer; + text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + text_layer_set_background_color(text_layer, GColorClear); + layer_add_child(layer, text_layer_get_layer(text_layer)); + + text_layer_set_text(stress_data.text_layer, "Starting Stress Test"); + abort_stress_test = false; + app_timer_register(500, app_timer_cb, NULL); +}; + +static void stress_window_unload(Window *data) { + abort_stress_test = true; +} + +static void file_system_stress_callback(int index, void *data) { + stress_data.stress_iteration = 0; + stress_data.stress_index = index; + window_init(&stress_data.window, WINDOW_NAME("Stress Test")); + window_set_user_data(&stress_data.window, &stress_data); + window_set_window_handlers(&stress_data.window, &(WindowHandlers) { + .load = stress_window_load, + .unload = stress_window_unload + }); + app_window_stack_push(&stress_data.window, true); +} + +static void populate_menu(SimpleMenuSection *menu_section, SimpleMenuItem *menu_items) { + for (unsigned int i = 0; i < NUM_REGIONS; ++i) { + menu_items[i] = (SimpleMenuItem) { + .title = s_flash_regions[i].name, + .callback = menu_select_callback, + }; + } + menu_items[FILE_WRITE_STRESS] = (SimpleMenuItem) { + .title = "File Stress", + .callback = file_system_stress_callback, + }; + menu_items[FILE_SUBSECTOR_STRESS] = (SimpleMenuItem) { + .title = "Subsector Stress", + .callback = file_system_stress_callback, + }; + + menu_section->num_items = NUM_MENU_ITEMS; + menu_section->items = menu_items; + menu_section->title = "Flash Regions"; +} + +static void prv_window_load(Window *window) { + FlashDiagAppData *data = window_get_user_data(window); + populate_menu(&data->menu_section, data->menu_items); + Layer *root_layer = window_get_root_layer(window); + const GRect *bounds = &root_layer->bounds; + simple_menu_layer_init(&data->menu_layer, bounds, window, &data->menu_section, 1, NULL); + layer_add_child(root_layer, simple_menu_layer_get_layer(&data->menu_layer)); +} + +static void push_window(FlashDiagAppData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Flash Diagnostic")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate +static void handle_init(void) { + FlashDiagAppData *data = (FlashDiagAppData*) app_malloc_check(sizeof(FlashDiagAppData)); + if (data == NULL) { + PBL_CROAK("Out of memory"); + } + app_state_set_user_data(data); + push_window(data); +} + +static void handle_deinit(void) { + FlashDiagAppData *data = app_state_get_user_data(); + simple_menu_layer_deinit(&data->menu_layer); + app_free(data); +} + +static void s_main(void) { + if (resource_storage_flash_get_unused_bank()->begin == + s_flash_regions[0].begin) { + s_flash_regions[0].name = "Unused Resources"; + } else { + s_flash_regions[1].name = "Unused Resources"; + } + + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* flash_diagnostic_app_get_info() { + static const PebbleProcessMdSystem s_flash_diagnostic_app_info = { + .common.main_func = s_main, + .name = "Flash Diagnostic" + }; + return (const PebbleProcessMd*) &s_flash_diagnostic_app_info; +} diff --git a/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.h b/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.h new file mode 100644 index 0000000000..9f2661db88 --- /dev/null +++ b/src/fw/apps/demo/flash_diagnostic/flash_diagnostic.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* flash_diagnostic_app_get_info(); diff --git a/src/fw/apps/demo/flash_diagnostic/wscript_build b/src/fw/apps/demo/flash_diagnostic/wscript_build new file mode 100644 index 0000000000..49c8351c4e --- /dev/null +++ b/src/fw/apps/demo/flash_diagnostic/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['flash_diagnostic.c'], + target='demo_app_flash_diagnostic', +) +bld.env.FW_APPS.append('demo_app_flash_diagnostic') + +# vim:filetype=python diff --git a/src/fw/apps/demo/flash_prof/Kconfig b/src/fw/apps/demo/flash_prof/Kconfig new file mode 100644 index 0000000000..a954485e41 --- /dev/null +++ b/src/fw/apps/demo/flash_prof/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_FLASH_PROF + bool "Flash Prof" diff --git a/src/fw/apps/demo/flash_prof/flash_prof.c b/src/fw/apps/demo/flash_prof/flash_prof.c new file mode 100644 index 0000000000..b0b33332e4 --- /dev/null +++ b/src/fw/apps/demo/flash_prof/flash_prof.c @@ -0,0 +1,71 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "process_management/pebble_process_md.h" +#include "applib/app.h" +#include "system/logging.h" +#include "drivers/flash.h" +#include "drivers/rtc.h" +#include "flash_region/flash_region.h" +#include "system/passert.h" +#include "kernel/pbl_malloc.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/number_window.h" +#include "applib/ui/window_stack.h" + +#include "FreeRTOS.h" + +static NumberWindow number_window; + +static uint32_t timed_read_bytes(uint32_t num_bytes) { + uint8_t *buffer = kernel_malloc_check(num_bytes); + time_t start_time_s; + uint16_t start_time_ms; + rtc_get_time_ms(&start_time_s, &start_time_ms); + flash_read_bytes(buffer, FLASH_REGION_FILESYSTEM_BEGIN, num_bytes); + time_t stop_time_s; + uint16_t stop_time_ms; + rtc_get_time_ms(&stop_time_s, &stop_time_ms); + kernel_free(buffer); + return ((stop_time_s * 1000 + stop_time_ms) - (start_time_s * 1000 + start_time_ms)); +} + +static void do_timed_read(NumberWindow *nw, void *data) { + uint32_t num_bytes = nw->value; + uint32_t predicted_time = num_bytes * 8 / 16000; + uint32_t time = timed_read_bytes(num_bytes); + PBL_LOG_DBG("time to read %lu bytes: predicted %lu, actual %lu", num_bytes, predicted_time, time); + window_stack_remove(&number_window.window, false); + app_window_stack_push(&number_window.window, true); +} + +#define NUM_BYTES 1000 + +static void handle_init(void) { + number_window_init(&number_window, "Num Writes", (NumberWindowCallbacks) { + .selected = (NumberWindowCallback) do_timed_read, + }, NULL); + number_window_set_min(&number_window, 1000); + number_window_set_max(&number_window, 1000000); + number_window_set_step_size(&number_window, 1000); + app_window_stack_push((Window *)&number_window, true); +} + +static void handle_deinit(void) { + +} + +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +const PebbleProcessMd* flash_prof_get_app_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Flash Prof" + }; + + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/flash_prof/flash_prof.h b/src/fw/apps/demo/flash_prof/flash_prof.h new file mode 100644 index 0000000000..891cbf8f4b --- /dev/null +++ b/src/fw/apps/demo/flash_prof/flash_prof.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* flash_prof_get_app_info(); diff --git a/src/fw/apps/demo/flash_prof/wscript_build b/src/fw/apps/demo/flash_prof/wscript_build new file mode 100644 index 0000000000..e110a7a6ba --- /dev/null +++ b/src/fw/apps/demo/flash_prof/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['flash_prof.c'], + target='demo_app_flash_prof', +) +bld.env.FW_APPS.append('demo_app_flash_prof') + +# vim:filetype=python diff --git a/src/fw/apps/demo/font_test/Kconfig b/src/fw/apps/demo/font_test/Kconfig new file mode 100644 index 0000000000..b3a35c8e05 --- /dev/null +++ b/src/fw/apps/demo/font_test/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_FONT_TEST + bool "Font Test" diff --git a/src/fw/apps/demo/font_test/font_test.c b/src/fw/apps/demo/font_test/font_test.c new file mode 100644 index 0000000000..5a1371a58f --- /dev/null +++ b/src/fw/apps/demo/font_test/font_test.c @@ -0,0 +1,160 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "font_test.h" + +#include + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "util/size.h" + +#define FIRST_GLYPH 0x20 // space +#define LAST_GLYPH 0x7E // tilde + +typedef struct { + const char *key; + const char *label; +} FontEntry; + +static const FontEntry s_fonts[] = { + { FONT_KEY_LECO_20_BOLD_NUMBERS, "LECO_20_BOLD" }, + { FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM, "LECO_26_BOLD" }, + { FONT_KEY_LECO_28_LIGHT_NUMBERS, "LECO_28_LIGHT" }, + { FONT_KEY_LECO_32_BOLD_NUMBERS, "LECO_32_BOLD" }, + { FONT_KEY_LECO_36_BOLD_NUMBERS, "LECO_36_BOLD" }, + { FONT_KEY_LECO_38_BOLD_NUMBERS, "LECO_38_BOLD" }, + { FONT_KEY_LECO_42_NUMBERS, "LECO_42" }, + { FONT_KEY_LECO_60_NUMBERS_AM_PM, "LECO_60" }, + { FONT_KEY_LECO_60_BOLD_NUMBERS_AM_PM, "LECO_60_BOLD" }, +}; + +typedef struct { + Window window; + TextLayer header_layer; + TextLayer info_layer; + TextLayer glyph_layer; + GFont label_font; + unsigned int font_index; + uint8_t codepoint; + char header_buf[32]; + char info_buf[16]; + char glyph_buf[2]; +} AppState; + +static void prv_refresh(AppState *data) { + sniprintf(data->header_buf, sizeof(data->header_buf), "%u/%u %s", + data->font_index + 1, (unsigned int)ARRAY_LENGTH(s_fonts), + s_fonts[data->font_index].label); + sniprintf(data->info_buf, sizeof(data->info_buf), "U+%04X '%c'", + data->codepoint, data->codepoint); + data->glyph_buf[0] = (char)data->codepoint; + data->glyph_buf[1] = '\0'; + + text_layer_set_font(&data->glyph_layer, + fonts_get_system_font(s_fonts[data->font_index].key)); + + layer_mark_dirty(&data->header_layer.layer); + layer_mark_dirty(&data->info_layer.layer); + layer_mark_dirty(&data->glyph_layer.layer); +} + +static void prv_click_handler(ClickRecognizerRef recognizer, void *context) { + AppState *data = context; + ButtonId button = click_recognizer_get_button_id(recognizer); + if (button == BUTTON_ID_UP) { + data->codepoint = (data->codepoint == FIRST_GLYPH) + ? LAST_GLYPH + : (uint8_t)(data->codepoint - 1); + } else if (button == BUTTON_ID_DOWN) { + data->codepoint = (data->codepoint == LAST_GLYPH) + ? FIRST_GLYPH + : (uint8_t)(data->codepoint + 1); + } else if (button == BUTTON_ID_SELECT) { + data->font_index = (data->font_index + 1) % ARRAY_LENGTH(s_fonts); + } + prv_refresh(data); +} + +static void prv_config_provider(void *context) { + window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, prv_click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, prv_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_click_handler); +} + +static void prv_window_load(Window *window) { + AppState *data = window_get_user_data(window); + const GRect bounds = window->layer.bounds; + + data->label_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); + + text_layer_init(&data->header_layer, &GRect(0, 2, bounds.size.w, 18)); + text_layer_set_background_color(&data->header_layer, GColorWhite); + text_layer_set_text_color(&data->header_layer, GColorBlack); + text_layer_set_text_alignment(&data->header_layer, GTextAlignmentCenter); + text_layer_set_font(&data->header_layer, data->label_font); + text_layer_set_text(&data->header_layer, data->header_buf); + layer_add_child(&window->layer, &data->header_layer.layer); + + text_layer_init(&data->info_layer, &GRect(0, 22, bounds.size.w, 18)); + text_layer_set_background_color(&data->info_layer, GColorWhite); + text_layer_set_text_color(&data->info_layer, GColorBlack); + text_layer_set_text_alignment(&data->info_layer, GTextAlignmentCenter); + text_layer_set_font(&data->info_layer, data->label_font); + text_layer_set_text(&data->info_layer, data->info_buf); + layer_add_child(&window->layer, &data->info_layer.layer); + + // Big glyph fills the rest of the screen. + const int16_t glyph_y = 44; + text_layer_init(&data->glyph_layer, + &GRect(0, glyph_y, bounds.size.w, bounds.size.h - glyph_y)); + text_layer_set_background_color(&data->glyph_layer, GColorWhite); + text_layer_set_text_color(&data->glyph_layer, GColorBlack); + text_layer_set_text_alignment(&data->glyph_layer, GTextAlignmentCenter); + text_layer_set_text(&data->glyph_layer, data->glyph_buf); + layer_add_child(&window->layer, &data->glyph_layer.layer); + + prv_refresh(data); +} + +static void prv_push_window(AppState *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Font Test")); + window_set_user_data(window, data); + window_set_background_color(window, GColorWhite); + window_set_click_config_provider_with_context(window, prv_config_provider, data); + window_set_window_handlers(window, &(WindowHandlers){ + .load = prv_window_load, + }); + app_window_stack_push(window, true); +} + +static void prv_handle_init(void) { + AppState *data = app_zalloc_check(sizeof(AppState)); + data->font_index = 0; + data->codepoint = '0'; + app_state_set_user_data(data); + prv_push_window(data); +} + +static void prv_handle_deinit(void) { + AppState *data = app_state_get_user_data(); + app_free(data); +} + +static void prv_main(void) { + prv_handle_init(); + app_event_loop(); + prv_handle_deinit(); +} + +const PebbleProcessMd *font_test_app_get_info(void) { + static const PebbleProcessMdSystem s_info = { + .common.main_func = &prv_main, + .name = "Font Test", + }; + return (const PebbleProcessMd *)&s_info; +} diff --git a/src/fw/apps/demo/font_test/font_test.h b/src/fw/apps/demo/font_test/font_test.h new file mode 100644 index 0000000000..11a01cd332 --- /dev/null +++ b/src/fw/apps/demo/font_test/font_test.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *font_test_app_get_info(void); diff --git a/src/fw/apps/demo/font_test/wscript_build b/src/fw/apps/demo/font_test/wscript_build new file mode 100644 index 0000000000..73ec223995 --- /dev/null +++ b/src/fw/apps/demo/font_test/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['font_test.c'], + target='demo_app_font_test', +) +bld.env.FW_APPS.append('demo_app_font_test') + +# vim:filetype=python diff --git a/src/fw/apps/demo/fps_test.c b/src/fw/apps/demo/fps_test.c new file mode 100644 index 0000000000..5ca6193cc4 --- /dev/null +++ b/src/fw/apps/demo/fps_test.c @@ -0,0 +1,262 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "fps_test.h" +#include "fps_test_bitmaps.h" +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" +#include "applib/ui/bitmap_layer.h" +#include "applib/ui/menu_layer.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" +#include "system/profiler.h" +#include "util/size.h" + +typedef struct AppData { + Window window; + BitmapLayer background_layer; + BitmapLayer topleft_layer; + MenuLayer action_list1; + MenuLayer action_list2; + ScrollLayerCallback prv_orig_content_offset_changed; + + int64_t time_started; + uint32_t rendered_frames; +} AppData; + +static int64_t prv_time_64(void) { + time_t s; + uint16_t ms; + rtc_get_time_ms(&s, &ms); + return (int64_t)s * 1000 + ms; +} + +static void prv_redraw_timer_cb(void *cb_data) { + AppData *data = app_state_get_user_data(); + + layer_mark_dirty(&data->window.layer); + app_timer_register(0, prv_redraw_timer_cb, NULL); +} + +/***************************************************************************************** + Stop our timer and display results. + + A frame update consists of the following operations: + op_1) App renders to its own frame buffer + op_2) System copies the app frame buffer to the system frame buffer + op_3) System sends the system frame buffer to the display hardware (using DMA). + + op_3 can happen in parallel with op_1, so the effective frame period is: + frame_period = MAX(op_1_time + op_2_time, op2_time + op_3_time) + + This app measures op_1_time + op_2_time and does so by counting the number of times + the app window's update callback got called within a set amount of time. The window update + callback only does op1, but the app_render_handler() method in app.c insures that a window + update is not called again until op_2 has completed for the previous update. This throttling + if the app's window update also insures that: + (op_1_time + op_2_time) is always >= (op_2_time + op_3_time) + + To measure op_1, we use a profiler timer node called "render". This timer + measures the amount of time we spend in the window_render() method. + + To measure op_2, we use a profiler timer node called "framebuffer_prepare". This timer + measures the amount of time we spend copying the app's frame buffer to the system framebuffer + + To measure op_3, we use a profiler timer node called "framebuffer_send". This profiler timer + measures the amount of time we spend waiting for a display DMA to complete. + + op_1 can be computed from the app's update period - op_2_time +*/ +static void prv_pop_all_windows_cb(void *cb_data) { + // Print profiler stats which include the time spent copying the app frame buffer to the + // system frame buffer and the time spent sending the system frame buffer to the display. + PROFILER_STOP; + PROFILER_PRINT_STATS; + + AppData *data = app_state_get_user_data(); + int64_t time_rendered = prv_time_64() - data->time_started; + + PBL_LOG_INFO("## %d frames rendered", (int)data->rendered_frames); + if (time_rendered) { + int frame_period = time_rendered/(int64_t)data->rendered_frames; + int fps = (int64_t)data->rendered_frames*1000/time_rendered; + PBL_LOG_INFO("## at %d FPS (%d ms/frame)", fps, frame_period); + } + + app_window_stack_pop_all(false); +} + +static const char *prv_row_texts[] = { + "Row 1", + "Row 2", + "Row 3", + "Row 4", + "Row 5", + "Row 6", +}; + +static uint16_t prv_get_num_rows(struct MenuLayer *menu_layer, uint16_t section_index, + void *callback_context) { + return ARRAY_LENGTH(prv_row_texts); +} + +static void prv_draw_row(GContext* ctx, const Layer *cell_layer, char const *title, + int16_t offset) { + // mostly copied from menu_cell_basic_draw_with_value + // (that unfortunately doesn't respect bounds.origin.x) + const int16_t title_height = 24; + GRect box = cell_layer->bounds; + box.origin.x += offset; + box.origin.y = (box.size.h - title_height) / 2; + box.size.w -= offset; + box.size.h = title_height + 4; + + const GFont title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + if (title) { + graphics_context_set_text_color_2bit(ctx, GColor2White); + graphics_draw_text(ctx, title, title_font, box, + GTextOverflowModeFill, GTextAlignmentLeft, NULL); + } +} + +void prv_draw_row_1(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, + void *callback_context) { + const char *title = prv_row_texts[cell_index->row]; + prv_draw_row(ctx, cell_layer, title, -cell_layer->frame.origin.y/4); +} + +static void prv_draw_row_2(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, + void *callback_context) { + const char *title = prv_row_texts[cell_index->row]; + prv_draw_row(ctx, cell_layer, title, -cell_layer->frame.origin.y/4 + cell_layer->bounds.size.w); +} + +static int16_t prv_get_separator_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, + void *callback_context) { + return 0; +} + +static void prv_window_update_proc(struct Layer *layer, GContext *ctx) { + AppData *data = app_state_get_user_data(); + if (data->rendered_frames == 0) { + data->time_started = prv_time_64(); + PROFILER_INIT; + PROFILER_START; + } + data->rendered_frames++; +} + +static void prv_window_disapper(Window *window) { +} + +void prv_syncing_content_offset_changed(struct ScrollLayer *scroll_layer, void *context) { + AppData *data = app_state_get_user_data(); + data->prv_orig_content_offset_changed(scroll_layer, context); + + GPoint offset = scroll_layer_get_content_offset(scroll_layer); + scroll_layer_set_content_offset(&data->action_list1.scroll_layer, offset, false); +} + +static void prv_window_load(Window *window) { + // creates a structure as outlined at + // https://pebbletechnology.atlassian.net/wiki/display/DEV/3.0+Notifications+UI+MVP + + // it's one full screen background image .background_layer, + // one image at the top left .topleft_layer, + // and two menu layers .action_list1 and .action_list2 that overlay each other + + // some hackery with the two menu layers goes on to keep their scroll offest in sync + // and to have the inverter layer rendered only once + + const int16_t navbar_width = s_fps_topleft_bitmap.bounds.size.w; + + AppData *data = window_get_user_data(window); + const GRect *full_rect = &window->layer.bounds; + + bitmap_layer_init(&data->background_layer, full_rect); + bitmap_layer_set_background_color_2bit(&data->background_layer, GColor2Black); + bitmap_layer_set_bitmap(&data->background_layer, &s_fps_background_bitmap); + layer_add_child(&window->layer, &data->background_layer.layer); + + bitmap_layer_init(&data->topleft_layer, &GRect(0, 0, navbar_width, navbar_width)); + bitmap_layer_set_background_color_2bit(&data->topleft_layer, GColor2White); + bitmap_layer_set_bitmap(&data->topleft_layer, &s_fps_topleft_bitmap); + // PBL_LOG_DBG("heap_allocated: %d", s_fps_topleft_bitmap.is_heap_allocated); + // PBL_LOG_DBG("bitmapformat: %d", s_fps_topleft_bitmap.format); + + layer_add_child(&window->layer, &data->topleft_layer.layer); + + const GRect menu_layer_rect = + GRect(navbar_width, 0, full_rect->size.w - navbar_width, full_rect->size.h); + menu_layer_init(&data->action_list1, &menu_layer_rect); + menu_layer_set_callbacks(&data->action_list1, NULL, &(MenuLayerCallbacks){ + .get_num_rows = prv_get_num_rows, + .draw_row = prv_draw_row_1, + .get_separator_height = prv_get_separator_height, + }); + layer_set_hidden(&data->action_list1.inverter.layer, true); + + scroll_layer_set_shadow_hidden(&data->action_list1.scroll_layer, true); + layer_add_child(&window->layer, menu_layer_get_layer(&data->action_list1)); + + menu_layer_init(&data->action_list2, &menu_layer_rect); + menu_layer_set_callbacks(&data->action_list2, NULL, &(MenuLayerCallbacks){ + .get_num_rows = prv_get_num_rows, + .draw_row = prv_draw_row_2, + .get_separator_height = prv_get_separator_height, + }); + scroll_layer_set_shadow_hidden(&data->action_list2.scroll_layer, true); + data->prv_orig_content_offset_changed = + data->action_list2.scroll_layer.callbacks.content_offset_changed_handler; + data->action_list2.scroll_layer.callbacks.content_offset_changed_handler = + prv_syncing_content_offset_changed; + menu_layer_set_click_config_onto_window(&data->action_list2, window); + layer_add_child(&window->layer, menu_layer_get_layer(&data->action_list2)); + + // start infinite update loop + prv_redraw_timer_cb(NULL); + // run application for a given time, than terminate + app_timer_register(5000, prv_pop_all_windows_cb, NULL); +} + +static void prv_window_unload(Window *window) { + AppData *data = window_get_user_data(window); + menu_layer_deinit(&data->action_list1); + menu_layer_deinit(&data->action_list2); +} + +static void s_main(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + memset(data, 0, sizeof(AppData)); + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("FPS test")); + window_set_user_data(window, data); + window_set_fullscreen(window, true); + layer_set_update_proc(&window->layer, prv_window_update_proc); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + .disappear = prv_window_disapper, + }); + + app_window_stack_push(window, true); + + PROFILER_INIT; + PROFILER_START; + app_event_loop(); +} + +const PebbleProcessMd* fps_test_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = s_main, + .name = "FPS Test" + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/fps_test.h b/src/fw/apps/demo/fps_test.h new file mode 100644 index 0000000000..6ecff5e82a --- /dev/null +++ b/src/fw/apps/demo/fps_test.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* fps_test_get_app_info(void); diff --git a/src/fw/apps/demo/fps_test_bitmaps.h b/src/fw/apps/demo/fps_test_bitmaps.h new file mode 100644 index 0000000000..cdf5d35f3c --- /dev/null +++ b/src/fw/apps/demo/fps_test_bitmaps.h @@ -0,0 +1,1891 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +// @nolint + +#pragma once +#include "applib/graphics/gtypes.h" + +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 + +// GBitmap + pixel data generated by bitmapgen.py: + +static const uint8_t s_fps_background_pixels[] = { + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 0 - 16 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16 - 32 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 32 - 48 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 48 - 64 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 64 - 80 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 80 - 96 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 96 - 112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 112 - 128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 128 - 144 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 144 - 160 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 160 - 176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 176 - 192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 192 - 208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 208 - 224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 224 - 240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 240 - 256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 256 - 272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 272 - 288 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 288 - 304 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 304 - 320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 320 - 336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 336 - 352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 352 - 368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 368 - 384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 384 - 400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 400 - 416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 416 - 432 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 432 - 448 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 448 - 464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 464 - 480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 480 - 496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 496 - 512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 512 - 528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 528 - 544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 544 - 560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 560 - 576 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 576 - 592 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 592 - 608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 608 - 624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 624 - 640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 640 - 656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 656 - 672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 672 - 688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 688 - 704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 704 - 720 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 720 - 736 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 736 - 752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 752 - 768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 768 - 784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 784 - 800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 800 - 816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 816 - 832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 832 - 848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 848 - 864 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 864 - 880 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 880 - 896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 896 - 912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 912 - 928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 928 - 944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 944 - 960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 960 - 976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 976 - 992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 992 - 1008 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1008 - 1024 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1024 - 1040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1040 - 1056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1056 - 1072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1072 - 1088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1088 - 1104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1104 - 1120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1120 - 1136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1136 - 1152 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1152 - 1168 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1168 - 1184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1184 - 1200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1200 - 1216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1216 - 1232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1232 - 1248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1248 - 1264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1264 - 1280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1280 - 1296 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1296 - 1312 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1312 - 1328 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1328 - 1344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1344 - 1360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1360 - 1376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1376 - 1392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1392 - 1408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1408 - 1424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1424 - 1440 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1440 - 1456 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1456 - 1472 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1472 - 1488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1488 - 1504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1504 - 1520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1520 - 1536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1536 - 1552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1552 - 1568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1568 - 1584 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1584 - 1600 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1600 - 1616 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1616 - 1632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1632 - 1648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1648 - 1664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1664 - 1680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1680 - 1696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1696 - 1712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1712 - 1728 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1728 - 1744 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1744 - 1760 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1760 - 1776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1776 - 1792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1792 - 1808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1808 - 1824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1824 - 1840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1840 - 1856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1856 - 1872 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1872 - 1888 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1888 - 1904 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1904 - 1920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1920 - 1936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1936 - 1952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1952 - 1968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1968 - 1984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1984 - 2000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2000 - 2016 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2016 - 2032 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2032 - 2048 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2048 - 2064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2064 - 2080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2080 - 2096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2096 - 2112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2112 - 2128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2128 - 2144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2144 - 2160 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2160 - 2176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2176 - 2192 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2192 - 2208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2208 - 2224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2224 - 2240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2240 - 2256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2256 - 2272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2272 - 2288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2288 - 2304 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2304 - 2320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2320 - 2336 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2336 - 2352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2352 - 2368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2368 - 2384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2384 - 2400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2400 - 2416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2416 - 2432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2432 - 2448 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2448 - 2464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2464 - 2480 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2480 - 2496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2496 - 2512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2512 - 2528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2528 - 2544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2544 - 2560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2560 - 2576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2576 - 2592 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2592 - 2608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2608 - 2624 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2624 - 2640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2640 - 2656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2656 - 2672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2672 - 2688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2688 - 2704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2704 - 2720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2720 - 2736 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2736 - 2752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2752 - 2768 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2768 - 2784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2784 - 2800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2800 - 2816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2816 - 2832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2832 - 2848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2848 - 2864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2864 - 2880 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2880 - 2896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2896 - 2912 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2912 - 2928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2928 - 2944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2944 - 2960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2960 - 2976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2976 - 2992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2992 - 3008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3008 - 3024 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3024 - 3040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3040 - 3056 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3056 - 3072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3072 - 3088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3088 - 3104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3104 - 3120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3120 - 3136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3136 - 3152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3152 - 3168 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3168 - 3184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3184 - 3200 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3200 - 3216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3216 - 3232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3232 - 3248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3248 - 3264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3264 - 3280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3280 - 3296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3296 - 3312 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3312 - 3328 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3328 - 3344 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3344 - 3360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3360 - 3376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3376 - 3392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3392 - 3408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3408 - 3424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3424 - 3440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3440 - 3456 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3456 - 3472 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3472 - 3488 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3488 - 3504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3504 - 3520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3520 - 3536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3536 - 3552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3552 - 3568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3568 - 3584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3584 - 3600 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3600 - 3616 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3616 - 3632 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3632 - 3648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3648 - 3664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3664 - 3680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3680 - 3696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3696 - 3712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3712 - 3728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3728 - 3744 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3744 - 3760 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3760 - 3776 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3776 - 3792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3792 - 3808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3808 - 3824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3824 - 3840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3840 - 3856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3856 - 3872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3872 - 3888 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3888 - 3904 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3904 - 3920 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3920 - 3936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3936 - 3952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3952 - 3968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3968 - 3984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3984 - 4000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4000 - 4016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4016 - 4032 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4032 - 4048 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4048 - 4064 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4064 - 4080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4080 - 4096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4096 - 4112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4112 - 4128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4128 - 4144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4144 - 4160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4160 - 4176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4176 - 4192 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4192 - 4208 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4208 - 4224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4224 - 4240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4240 - 4256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4256 - 4272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4272 - 4288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4288 - 4304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4304 - 4320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4320 - 4336 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4336 - 4352 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4352 - 4368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4368 - 4384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4384 - 4400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4400 - 4416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4416 - 4432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4432 - 4448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4448 - 4464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4464 - 4480 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4480 - 4496 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4496 - 4512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4512 - 4528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4528 - 4544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4544 - 4560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4560 - 4576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4576 - 4592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4592 - 4608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4608 - 4624 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4624 - 4640 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4640 - 4656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4656 - 4672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4672 - 4688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4688 - 4704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4704 - 4720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4720 - 4736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4736 - 4752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4752 - 4768 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4768 - 4784 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4784 - 4800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4800 - 4816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4816 - 4832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4832 - 4848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4848 - 4864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4864 - 4880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4880 - 4896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4896 - 4912 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4912 - 4928 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4928 - 4944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4944 - 4960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4960 - 4976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4976 - 4992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4992 - 5008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5008 - 5024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5024 - 5040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5040 - 5056 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5056 - 5072 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5072 - 5088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5088 - 5104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5104 - 5120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5120 - 5136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5136 - 5152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5152 - 5168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5168 - 5184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5184 - 5200 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5200 - 5216 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5216 - 5232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5232 - 5248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5248 - 5264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5264 - 5280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5280 - 5296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5296 - 5312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5312 - 5328 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5328 - 5344 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5344 - 5360 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5360 - 5376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5376 - 5392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5392 - 5408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5408 - 5424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5424 - 5440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5440 - 5456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5456 - 5472 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5472 - 5488 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5488 - 5504 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5504 - 5520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5520 - 5536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5536 - 5552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5552 - 5568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5568 - 5584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5584 - 5600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5600 - 5616 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5616 - 5632 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5632 - 5648 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5648 - 5664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5664 - 5680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5680 - 5696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5696 - 5712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5712 - 5728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5728 - 5744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5744 - 5760 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5760 - 5776 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5776 - 5792 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5792 - 5808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5808 - 5824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5824 - 5840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5840 - 5856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5856 - 5872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5872 - 5888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5888 - 5904 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5904 - 5920 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5920 - 5936 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5936 - 5952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5952 - 5968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5968 - 5984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5984 - 6000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6000 - 6016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6016 - 6032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6032 - 6048 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6048 - 6064 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6064 - 6080 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6080 - 6096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6096 - 6112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6112 - 6128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6128 - 6144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6144 - 6160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6160 - 6176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6176 - 6192 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6192 - 6208 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6208 - 6224 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6224 - 6240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6240 - 6256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6256 - 6272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6272 - 6288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6288 - 6304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6304 - 6320 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6320 - 6336 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6336 - 6352 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6352 - 6368 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6368 - 6384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6384 - 6400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6400 - 6416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6416 - 6432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6432 - 6448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6448 - 6464 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6464 - 6480 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6480 - 6496 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6496 - 6512 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6512 - 6528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6528 - 6544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6544 - 6560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6560 - 6576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6576 - 6592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6592 - 6608 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6608 - 6624 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6624 - 6640 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6640 - 6656 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6656 - 6672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6672 - 6688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6688 - 6704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6704 - 6720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6720 - 6736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6736 - 6752 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6752 - 6768 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6768 - 6784 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6784 - 6800 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6800 - 6816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6816 - 6832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6832 - 6848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6848 - 6864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6864 - 6880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6880 - 6896 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6896 - 6912 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6912 - 6928 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6928 - 6944 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6944 - 6960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6960 - 6976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6976 - 6992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6992 - 7008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7008 - 7024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7024 - 7040 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7040 - 7056 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7056 - 7072 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7072 - 7088 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7088 - 7104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7104 - 7120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7120 - 7136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7136 - 7152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7152 - 7168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7168 - 7184 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7184 - 7200 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7200 - 7216 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7216 - 7232 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7232 - 7248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7248 - 7264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7264 - 7280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7280 - 7296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7296 - 7312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7312 - 7328 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7328 - 7344 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7344 - 7360 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7360 - 7376 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7376 - 7392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7392 - 7408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7408 - 7424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7424 - 7440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7440 - 7456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7456 - 7472 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7472 - 7488 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7488 - 7504 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7504 - 7520 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7520 - 7536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7536 - 7552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7552 - 7568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7568 - 7584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7584 - 7600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7600 - 7616 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7616 - 7632 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7632 - 7648 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7648 - 7664 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7664 - 7680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7680 - 7696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7696 - 7712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7712 - 7728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7728 - 7744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7744 - 7760 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7760 - 7776 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7776 - 7792 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7792 - 7808 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7808 - 7824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7824 - 7840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7840 - 7856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7856 - 7872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7872 - 7888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7888 - 7904 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7904 - 7920 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7920 - 7936 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7936 - 7952 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7952 - 7968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7968 - 7984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7984 - 8000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8000 - 8016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8016 - 8032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8032 - 8048 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8048 - 8064 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8064 - 8080 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8080 - 8096 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8096 - 8112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8112 - 8128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8128 - 8144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8144 - 8160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8160 - 8176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8176 - 8192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8192 - 8208 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8208 - 8224 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8224 - 8240 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8240 - 8256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8256 - 8272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8272 - 8288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8288 - 8304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8304 - 8320 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8320 - 8336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8336 - 8352 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8352 - 8368 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8368 - 8384 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8384 - 8400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8400 - 8416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8416 - 8432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8432 - 8448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8448 - 8464 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8464 - 8480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8480 - 8496 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8496 - 8512 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8512 - 8528 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8528 - 8544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8544 - 8560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8560 - 8576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8576 - 8592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8592 - 8608 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8608 - 8624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8624 - 8640 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8640 - 8656 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8656 - 8672 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8672 - 8688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8688 - 8704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8704 - 8720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8720 - 8736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8736 - 8752 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8752 - 8768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8768 - 8784 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8784 - 8800 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8800 - 8816 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8816 - 8832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8832 - 8848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8848 - 8864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8864 - 8880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8880 - 8896 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8896 - 8912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8912 - 8928 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8928 - 8944 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8944 - 8960 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8960 - 8976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8976 - 8992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8992 - 9008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9008 - 9024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9024 - 9040 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9040 - 9056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9056 - 9072 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9072 - 9088 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9088 - 9104 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9104 - 9120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9120 - 9136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9136 - 9152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9152 - 9168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9168 - 9184 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9184 - 9200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9200 - 9216 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9216 - 9232 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9232 - 9248 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9248 - 9264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9264 - 9280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9280 - 9296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9296 - 9312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9312 - 9328 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9328 - 9344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9344 - 9360 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9360 - 9376 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9376 - 9392 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9392 - 9408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9408 - 9424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9424 - 9440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9440 - 9456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9456 - 9472 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9472 - 9488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9488 - 9504 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9504 - 9520 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9520 - 9536 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9536 - 9552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9552 - 9568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9568 - 9584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9584 - 9600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9600 - 9616 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9616 - 9632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9632 - 9648 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9648 - 9664 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9664 - 9680 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9680 - 9696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9696 - 9712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9712 - 9728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9728 - 9744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9744 - 9760 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9760 - 9776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9776 - 9792 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9792 - 9808 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9808 - 9824 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9824 - 9840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9840 - 9856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9856 - 9872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9872 - 9888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9888 - 9904 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9904 - 9920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9920 - 9936 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9936 - 9952 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9952 - 9968 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9968 - 9984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9984 - 10000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10000 - 10016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10016 - 10032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10032 - 10048 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10048 - 10064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10064 - 10080 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10080 - 10096 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10096 - 10112 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10112 - 10128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10128 - 10144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10144 - 10160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10160 - 10176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10176 - 10192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10192 - 10208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10208 - 10224 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10224 - 10240 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10240 - 10256 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10256 - 10272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10272 - 10288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10288 - 10304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10304 - 10320 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10320 - 10336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10336 - 10352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10352 - 10368 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10368 - 10384 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10384 - 10400 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10400 - 10416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10416 - 10432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10432 - 10448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10448 - 10464 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10464 - 10480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10480 - 10496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10496 - 10512 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10512 - 10528 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10528 - 10544 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10544 - 10560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10560 - 10576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10576 - 10592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10592 - 10608 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10608 - 10624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10624 - 10640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10640 - 10656 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10656 - 10672 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10672 - 10688 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10688 - 10704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10704 - 10720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10720 - 10736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10736 - 10752 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10752 - 10768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10768 - 10784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10784 - 10800 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10800 - 10816 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10816 - 10832 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10832 - 10848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10848 - 10864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10864 - 10880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10880 - 10896 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10896 - 10912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10912 - 10928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10928 - 10944 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10944 - 10960 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10960 - 10976 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10976 - 10992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10992 - 11008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11008 - 11024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11024 - 11040 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11040 - 11056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11056 - 11072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11072 - 11088 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11088 - 11104 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11104 - 11120 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11120 - 11136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11136 - 11152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11152 - 11168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11168 - 11184 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11184 - 11200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11200 - 11216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11216 - 11232 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11232 - 11248 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11248 - 11264 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11264 - 11280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11280 - 11296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11296 - 11312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11312 - 11328 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11328 - 11344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11344 - 11360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11360 - 11376 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11376 - 11392 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11392 - 11408 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11408 - 11424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11424 - 11440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11440 - 11456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11456 - 11472 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11472 - 11488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11488 - 11504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11504 - 11520 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11520 - 11536 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11536 - 11552 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11552 - 11568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11568 - 11584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11584 - 11600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11600 - 11616 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11616 - 11632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11632 - 11648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11648 - 11664 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11664 - 11680 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11680 - 11696 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11696 - 11712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11712 - 11728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11728 - 11744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11744 - 11760 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11760 - 11776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11776 - 11792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11792 - 11808 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11808 - 11824 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11824 - 11840 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11840 - 11856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11856 - 11872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11872 - 11888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11888 - 11904 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11904 - 11920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11920 - 11936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11936 - 11952 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11952 - 11968 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11968 - 11984 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11984 - 12000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12000 - 12016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12016 - 12032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12032 - 12048 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12048 - 12064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12064 - 12080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12080 - 12096 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12096 - 12112 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12112 - 12128 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12128 - 12144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12144 - 12160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12160 - 12176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12176 - 12192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12192 - 12208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12208 - 12224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12224 - 12240 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12240 - 12256 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12256 - 12272 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12272 - 12288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12288 - 12304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12304 - 12320 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12320 - 12336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12336 - 12352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12352 - 12368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12368 - 12384 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12384 - 12400 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12400 - 12416 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12416 - 12432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12432 - 12448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12448 - 12464 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12464 - 12480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12480 - 12496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12496 - 12512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12512 - 12528 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12528 - 12544 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12544 - 12560 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12560 - 12576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12576 - 12592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12592 - 12608 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12608 - 12624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12624 - 12640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12640 - 12656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12656 - 12672 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12672 - 12688 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12688 - 12704 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12704 - 12720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12720 - 12736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12736 - 12752 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12752 - 12768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12768 - 12784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12784 - 12800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12800 - 12816 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12816 - 12832 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12832 - 12848 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12848 - 12864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12864 - 12880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12880 - 12896 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12896 - 12912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12912 - 12928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12928 - 12944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12944 - 12960 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12960 - 12976 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12976 - 12992 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12992 - 13008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13008 - 13024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13024 - 13040 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13040 - 13056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13056 - 13072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13072 - 13088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13088 - 13104 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13104 - 13120 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13120 - 13136 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13136 - 13152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13152 - 13168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13168 - 13184 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13184 - 13200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13200 - 13216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13216 - 13232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13232 - 13248 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13248 - 13264 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13264 - 13280 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13280 - 13296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13296 - 13312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13312 - 13328 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13328 - 13344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13344 - 13360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13360 - 13376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13376 - 13392 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13392 - 13408 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13408 - 13424 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13424 - 13440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13440 - 13456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13456 - 13472 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13472 - 13488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13488 - 13504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13504 - 13520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13520 - 13536 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13536 - 13552 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13552 - 13568 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13568 - 13584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13584 - 13600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13600 - 13616 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13616 - 13632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13632 - 13648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13648 - 13664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13664 - 13680 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13680 - 13696 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13696 - 13712 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13712 - 13728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13728 - 13744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13744 - 13760 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13760 - 13776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13776 - 13792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13792 - 13808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13808 - 13824 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13824 - 13840 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13840 - 13856 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13856 - 13872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13872 - 13888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13888 - 13904 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13904 - 13920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13920 - 13936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13936 - 13952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13952 - 13968 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13968 - 13984 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13984 - 14000 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14000 - 14016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14016 - 14032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14032 - 14048 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14048 - 14064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14064 - 14080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14080 - 14096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14096 - 14112 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14112 - 14128 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14128 - 14144 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14144 - 14160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14160 - 14176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14176 - 14192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14192 - 14208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14208 - 14224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14224 - 14240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14240 - 14256 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14256 - 14272 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14272 - 14288 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14288 - 14304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14304 - 14320 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14320 - 14336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14336 - 14352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14352 - 14368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14368 - 14384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14384 - 14400 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14400 - 14416 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14416 - 14432 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14432 - 14448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14448 - 14464 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14464 - 14480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14480 - 14496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14496 - 14512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14512 - 14528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14528 - 14544 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14544 - 14560 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14560 - 14576 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14576 - 14592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14592 - 14608 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14608 - 14624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14624 - 14640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14640 - 14656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14656 - 14672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14672 - 14688 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14688 - 14704 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14704 - 14720 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14720 - 14736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14736 - 14752 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14752 - 14768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14768 - 14784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14784 - 14800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14800 - 14816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14816 - 14832 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14832 - 14848 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14848 - 14864 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14864 - 14880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14880 - 14896 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14896 - 14912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14912 - 14928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14928 - 14944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14944 - 14960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14960 - 14976 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14976 - 14992 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14992 - 15008 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15008 - 15024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15024 - 15040 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15040 - 15056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15056 - 15072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15072 - 15088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15088 - 15104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15104 - 15120 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15120 - 15136 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15136 - 15152 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15152 - 15168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15168 - 15184 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15184 - 15200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15200 - 15216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15216 - 15232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15232 - 15248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15248 - 15264 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15264 - 15280 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15280 - 15296 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15296 - 15312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15312 - 15328 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15328 - 15344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15344 - 15360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15360 - 15376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15376 - 15392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15392 - 15408 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15408 - 15424 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15424 - 15440 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15440 - 15456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15456 - 15472 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15472 - 15488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15488 - 15504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15504 - 15520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15520 - 15536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15536 - 15552 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15552 - 15568 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15568 - 15584 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15584 - 15600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15600 - 15616 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15616 - 15632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15632 - 15648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15648 - 15664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15664 - 15680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15680 - 15696 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15696 - 15712 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15712 - 15728 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15728 - 15744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15744 - 15760 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15760 - 15776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15776 - 15792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15792 - 15808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15808 - 15824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15824 - 15840 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15840 - 15856 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15856 - 15872 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15872 - 15888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15888 - 15904 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15904 - 15920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15920 - 15936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15936 - 15952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15952 - 15968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15968 - 15984 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15984 - 16000 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16000 - 16016 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16016 - 16032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16032 - 16048 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16048 - 16064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16064 - 16080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16080 - 16096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16096 - 16112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16112 - 16128 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16128 - 16144 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16144 - 16160 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16160 - 16176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16176 - 16192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16192 - 16208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16208 - 16224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16224 - 16240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16240 - 16256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16256 - 16272 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16272 - 16288 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16288 - 16304 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16304 - 16320 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16320 - 16336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16336 - 16352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16352 - 16368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16368 - 16384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16384 - 16400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16400 - 16416 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16416 - 16432 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16432 - 16448 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16448 - 16464 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16464 - 16480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16480 - 16496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16496 - 16512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16512 - 16528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16528 - 16544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16544 - 16560 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16560 - 16576 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16576 - 16592 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16592 - 16608 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16608 - 16624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16624 - 16640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16640 - 16656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16656 - 16672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16672 - 16688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16688 - 16704 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16704 - 16720 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16720 - 16736 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16736 - 16752 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16752 - 16768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16768 - 16784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16784 - 16800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16800 - 16816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16816 - 16832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16832 - 16848 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16848 - 16864 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16864 - 16880 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16880 - 16896 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16896 - 16912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16912 - 16928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16928 - 16944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16944 - 16960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16960 - 16976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16976 - 16992 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16992 - 17008 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17008 - 17024 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17024 - 17040 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17040 - 17056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17056 - 17072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17072 - 17088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17088 - 17104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17104 - 17120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17120 - 17136 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17136 - 17152 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17152 - 17168 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17168 - 17184 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17184 - 17200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17200 - 17216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17216 - 17232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17232 - 17248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17248 - 17264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17264 - 17280 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17280 - 17296 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17296 - 17312 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17312 - 17328 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17328 - 17344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17344 - 17360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17360 - 17376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17376 - 17392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17392 - 17408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17408 - 17424 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17424 - 17440 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17440 - 17456 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17456 - 17472 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17472 - 17488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17488 - 17504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17504 - 17520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17520 - 17536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17536 - 17552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17552 - 17568 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17568 - 17584 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17584 - 17600 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17600 - 17616 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17616 - 17632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17632 - 17648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17648 - 17664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17664 - 17680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17680 - 17696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17696 - 17712 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17712 - 17728 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17728 - 17744 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17744 - 17760 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17760 - 17776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17776 - 17792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17792 - 17808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17808 - 17824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17824 - 17840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17840 - 17856 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17856 - 17872 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17872 - 17888 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17888 - 17904 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17904 - 17920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17920 - 17936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17936 - 17952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17952 - 17968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17968 - 17984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17984 - 18000 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18000 - 18016 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18016 - 18032 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18032 - 18048 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18048 - 18064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18064 - 18080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18080 - 18096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18096 - 18112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18112 - 18128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18128 - 18144 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18144 - 18160 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18160 - 18176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18176 - 18192 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18192 - 18208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18208 - 18224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18224 - 18240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18240 - 18256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18256 - 18272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18272 - 18288 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18288 - 18304 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18304 - 18320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18320 - 18336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18336 - 18352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18352 - 18368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18368 - 18384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18384 - 18400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18400 - 18416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18416 - 18432 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18432 - 18448 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18448 - 18464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18464 - 18480 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18480 - 18496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18496 - 18512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18512 - 18528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18528 - 18544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18544 - 18560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18560 - 18576 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18576 - 18592 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18592 - 18608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18608 - 18624 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18624 - 18640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18640 - 18656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18656 - 18672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18672 - 18688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18688 - 18704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18704 - 18720 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18720 - 18736 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18736 - 18752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18752 - 18768 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18768 - 18784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18784 - 18800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18800 - 18816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18816 - 18832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18832 - 18848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18848 - 18864 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18864 - 18880 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18880 - 18896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18896 - 18912 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18912 - 18928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18928 - 18944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18944 - 18960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18960 - 18976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18976 - 18992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18992 - 19008 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19008 - 19024 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19024 - 19040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19040 - 19056 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19056 - 19072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19072 - 19088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19088 - 19104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19104 - 19120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19120 - 19136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19136 - 19152 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19152 - 19168 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19168 - 19184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19184 - 19200 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19200 - 19216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19216 - 19232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19232 - 19248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19248 - 19264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19264 - 19280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19280 - 19296 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19296 - 19312 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19312 - 19328 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19328 - 19344 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19344 - 19360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19360 - 19376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19376 - 19392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19392 - 19408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19408 - 19424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19424 - 19440 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19440 - 19456 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19456 - 19472 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19472 - 19488 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19488 - 19504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19504 - 19520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19520 - 19536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19536 - 19552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19552 - 19568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19568 - 19584 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19584 - 19600 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19600 - 19616 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19616 - 19632 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19632 - 19648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19648 - 19664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19664 - 19680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19680 - 19696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19696 - 19712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19712 - 19728 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19728 - 19744 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19744 - 19760 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19760 - 19776 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19776 - 19792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19792 - 19808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19808 - 19824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19824 - 19840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19840 - 19856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19856 - 19872 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19872 - 19888 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19888 - 19904 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19904 - 19920 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19920 - 19936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19936 - 19952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19952 - 19968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19968 - 19984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19984 - 20000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20000 - 20016 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20016 - 20032 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20032 - 20048 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20048 - 20064 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20064 - 20080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20080 - 20096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20096 - 20112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20112 - 20128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20128 - 20144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20144 - 20160 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20160 - 20176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20176 - 20192 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20192 - 20208 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20208 - 20224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20224 - 20240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20240 - 20256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20256 - 20272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20272 - 20288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20288 - 20304 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20304 - 20320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20320 - 20336 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20336 - 20352 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20352 - 20368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20368 - 20384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20384 - 20400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20400 - 20416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20416 - 20432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20432 - 20448 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20448 - 20464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20464 - 20480 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20480 - 20496 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20496 - 20512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20512 - 20528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20528 - 20544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20544 - 20560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20560 - 20576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20576 - 20592 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20592 - 20608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20608 - 20624 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20624 - 20640 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20640 - 20656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20656 - 20672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20672 - 20688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20688 - 20704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20704 - 20720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20720 - 20736 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20736 - 20752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20752 - 20768 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20768 - 20784 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20784 - 20800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20800 - 20816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20816 - 20832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20832 - 20848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20848 - 20864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20864 - 20880 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20880 - 20896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20896 - 20912 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20912 - 20928 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20928 - 20944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20944 - 20960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20960 - 20976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20976 - 20992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20992 - 21008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21008 - 21024 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21024 - 21040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21040 - 21056 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21056 - 21072 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21072 - 21088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21088 - 21104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21104 - 21120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21120 - 21136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21136 - 21152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21152 - 21168 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21168 - 21184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21184 - 21200 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21200 - 21216 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21216 - 21232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21232 - 21248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21248 - 21264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21264 - 21280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21280 - 21296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21296 - 21312 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21312 - 21328 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21328 - 21344 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21344 - 21360 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21360 - 21376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21376 - 21392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21392 - 21408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21408 - 21424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21424 - 21440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21440 - 21456 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21456 - 21472 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21472 - 21488 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21488 - 21504 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21504 - 21520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21520 - 21536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21536 - 21552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21552 - 21568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21568 - 21584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21584 - 21600 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21600 - 21616 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21616 - 21632 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21632 - 21648 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21648 - 21664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21664 - 21680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21680 - 21696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21696 - 21712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21712 - 21728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21728 - 21744 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21744 - 21760 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21760 - 21776 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21776 - 21792 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21792 - 21808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21808 - 21824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21824 - 21840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21840 - 21856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21856 - 21872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21872 - 21888 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21888 - 21904 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21904 - 21920 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21920 - 21936 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21936 - 21952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21952 - 21968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21968 - 21984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21984 - 22000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22000 - 22016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22016 - 22032 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22032 - 22048 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22048 - 22064 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22064 - 22080 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22080 - 22096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22096 - 22112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22112 - 22128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22128 - 22144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22144 - 22160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22160 - 22176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22176 - 22192 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22192 - 22208 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22208 - 22224 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22224 - 22240 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22240 - 22256 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22256 - 22272 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22272 - 22288 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22288 - 22304 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22304 - 22320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22320 - 22336 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22336 - 22352 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22352 - 22368 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22368 - 22384 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22384 - 22400 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22400 - 22416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22416 - 22432 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22432 - 22448 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22448 - 22464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22464 - 22480 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22480 - 22496 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22496 - 22512 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22512 - 22528 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22528 - 22544 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22544 - 22560 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22560 - 22576 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22576 - 22592 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22592 - 22608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22608 - 22624 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22624 - 22640 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22640 - 22656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22656 - 22672 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22672 - 22688 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22688 - 22704 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22704 - 22720 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22720 - 22736 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22736 - 22752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22752 - 22768 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22768 - 22784 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22784 - 22800 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22800 - 22816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22816 - 22832 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22832 - 22848 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22848 - 22864 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22864 - 22880 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22880 - 22896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22896 - 22912 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22912 - 22928 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22928 - 22944 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22944 - 22960 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22960 - 22976 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22976 - 22992 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22992 - 23008 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23008 - 23024 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23024 - 23040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23040 - 23056 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23056 - 23072 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23072 - 23088 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23088 - 23104 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23104 - 23120 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23120 - 23136 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23136 - 23152 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23152 - 23168 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23168 - 23184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23184 - 23200 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23200 - 23216 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23216 - 23232 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23232 - 23248 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23248 - 23264 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23264 - 23280 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23280 - 23296 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23296 - 23312 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23312 - 23328 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23328 - 23344 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23344 - 23360 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23360 - 23376 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23376 - 23392 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23392 - 23408 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23408 - 23424 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23424 - 23440 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23440 - 23456 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23456 - 23472 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23472 - 23488 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23488 - 23504 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23504 - 23520 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23520 - 23536 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23536 - 23552 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23552 - 23568 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23568 - 23584 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23584 - 23600 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23600 - 23616 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23616 - 23632 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23632 - 23648 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23648 - 23664 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23664 - 23680 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23680 - 23696 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23696 - 23712 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23712 - 23728 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23728 - 23744 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23744 - 23760 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23760 - 23776 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23776 - 23792 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23792 - 23808 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23808 - 23824 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23824 - 23840 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23840 - 23856 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23856 - 23872 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23872 - 23888 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23888 - 23904 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23904 - 23920 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23920 - 23936 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23936 - 23952 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23952 - 23968 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23968 - 23984 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23984 - 24000 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24000 - 24016 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24016 - 24032 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24032 - 24048 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 24048 - 24064 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 24064 - 24080 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24080 - 24096 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24096 - 24112 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24112 - 24128 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24128 - 24144 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24144 - 24160 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24160 - 24176 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 +}; +static const GBitmap s_fps_background_bitmap = { + .addr = (void*) &s_fps_background_pixels, + .row_size_bytes = 144, + .info_flags = 0x1002, + .bounds = { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 144, .h = 168 }, + }, +}; + +// GBitmap + pixel data generated by bitmapgen.py: + +static const uint8_t s_fps_topleft_pixels[] = { + 0xa9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 0 - 16 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16 - 32 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 32 - 48 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 48 - 64 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 64 - 80 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 80 - 96 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 96 - 112 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 112 - 128 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 128 - 144 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 144 - 160 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 160 - 176 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 176 - 192 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 192 - 208 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 208 - 224 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 224 - 240 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 240 - 256 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 256 - 272 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 272 - 288 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xd4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 288 - 304 */ + 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 304 - 320 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 320 - 336 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 336 - 352 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 352 - 368 */ + 0xc0, 0xc0, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, /* bytes 368 - 384 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 384 - 400 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, /* bytes 400 - 416 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 416 - 432 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, /* bytes 432 - 448 */ + 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, /* bytes 448 - 464 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, /* bytes 464 - 480 */ + 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, /* bytes 480 - 496 */ + 0xd4, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 496 - 512 */ + 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, /* bytes 512 - 528 */ + 0xda, 0xc0, 0xc0, 0xd0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 528 - 544 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, /* bytes 544 - 560 */ + 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 560 - 576 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, /* bytes 576 - 592 */ + 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 592 - 608 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, /* bytes 608 - 624 */ + 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, /* bytes 624 - 640 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 640 - 656 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, /* bytes 656 - 672 */ + 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 672 - 688 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 688 - 704 */ + 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 704 - 720 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xe4, 0xc0, /* bytes 720 - 736 */ + 0xc0, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, /* bytes 736 - 752 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, /* bytes 752 - 768 */ + 0xf9, 0xf9, 0xd4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 768 - 784 */ + 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 784 - 800 */ + 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 800 - 816 */ + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 816 - 832 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 832 - 848 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 848 - 864 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 864 - 880 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 880 - 896 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, /* bytes 896 - 912 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 912 - 928 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 928 - 944 */ + 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 944 - 960 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 960 - 976 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 976 - 992 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 992 - 1008 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1008 - 1024 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1024 - 1040 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1040 - 1056 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1056 - 1072 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1072 - 1088 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1088 - 1104 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1104 - 1120 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1120 - 1136 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1136 - 1152 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1152 - 1168 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1168 - 1184 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1184 - 1200 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1200 - 1216 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1216 - 1232 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1232 - 1248 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1248 - 1264 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1264 - 1280 */ + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9 +}; +static const GBitmap s_fps_topleft_bitmap = { + .addr = (void*) &s_fps_topleft_pixels, + .row_size_bytes = 36, + .info_flags = 0x1002, + .bounds = { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 36, .h = 36 }, + }, +}; + +#else //CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 + +// GBitmap + pixel data generated by bitmapgen.py: + +static const uint8_t s_fps_background_pixels[] = { + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 0 - 16 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 16 - 32 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 32 - 48 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 48 - 64 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 64 - 80 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 80 - 96 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 96 - 112 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 112 - 128 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 128 - 144 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 144 - 160 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 160 - 176 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 176 - 192 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 192 - 208 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 208 - 224 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 224 - 240 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 240 - 256 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 256 - 272 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 272 - 288 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 288 - 304 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 304 - 320 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 320 - 336 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 336 - 352 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 352 - 368 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 368 - 384 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 384 - 400 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 400 - 416 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 416 - 432 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 432 - 448 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 448 - 464 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 464 - 480 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 480 - 496 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 496 - 512 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 512 - 528 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 528 - 544 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 544 - 560 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 560 - 576 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 576 - 592 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 592 - 608 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 608 - 624 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 624 - 640 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 640 - 656 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 656 - 672 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 672 - 688 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 688 - 704 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 704 - 720 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 720 - 736 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 736 - 752 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 752 - 768 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 768 - 784 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 784 - 800 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 800 - 816 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 816 - 832 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 832 - 848 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 848 - 864 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 864 - 880 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 880 - 896 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 896 - 912 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 912 - 928 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 928 - 944 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 944 - 960 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 960 - 976 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 976 - 992 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 992 - 1008 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1008 - 1024 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1024 - 1040 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1040 - 1056 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1056 - 1072 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1072 - 1088 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1088 - 1104 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1104 - 1120 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1120 - 1136 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1136 - 1152 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1152 - 1168 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1168 - 1184 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1184 - 1200 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1200 - 1216 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1216 - 1232 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1232 - 1248 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1248 - 1264 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1264 - 1280 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1280 - 1296 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1296 - 1312 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1312 - 1328 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1328 - 1344 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1344 - 1360 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1360 - 1376 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1376 - 1392 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1392 - 1408 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1408 - 1424 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1424 - 1440 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1440 - 1456 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1456 - 1472 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1472 - 1488 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1488 - 1504 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1504 - 1520 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1520 - 1536 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1536 - 1552 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1552 - 1568 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1568 - 1584 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1584 - 1600 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1600 - 1616 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1616 - 1632 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1632 - 1648 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1648 - 1664 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1664 - 1680 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1680 - 1696 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1696 - 1712 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1712 - 1728 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1728 - 1744 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1744 - 1760 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1760 - 1776 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1776 - 1792 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1792 - 1808 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1808 - 1824 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1824 - 1840 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1840 - 1856 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1856 - 1872 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1872 - 1888 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1888 - 1904 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1904 - 1920 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1920 - 1936 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1936 - 1952 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1952 - 1968 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1968 - 1984 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1984 - 2000 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2000 - 2016 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2016 - 2032 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2032 - 2048 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2048 - 2064 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2064 - 2080 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2080 - 2096 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2096 - 2112 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2112 - 2128 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2128 - 2144 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2144 - 2160 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2160 - 2176 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2176 - 2192 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2192 - 2208 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2208 - 2224 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2224 - 2240 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2240 - 2256 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2256 - 2272 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2272 - 2288 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2288 - 2304 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2304 - 2320 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2320 - 2336 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2336 - 2352 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2352 - 2368 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2368 - 2384 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2384 - 2400 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2400 - 2416 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2416 - 2432 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2432 - 2448 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2448 - 2464 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2464 - 2480 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2480 - 2496 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2496 - 2512 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2512 - 2528 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2528 - 2544 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2544 - 2560 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2560 - 2576 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2576 - 2592 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2592 - 2608 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2608 - 2624 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2624 - 2640 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2640 - 2656 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2656 - 2672 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2672 - 2688 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2688 - 2704 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2704 - 2720 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2720 - 2736 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2736 - 2752 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2752 - 2768 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2768 - 2784 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2784 - 2800 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2800 - 2816 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2816 - 2832 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2832 - 2848 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2848 - 2864 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2864 - 2880 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2880 - 2896 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2896 - 2912 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2912 - 2928 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2928 - 2944 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2944 - 2960 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2960 - 2976 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2976 - 2992 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2992 - 3008 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3008 - 3024 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3024 - 3040 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3040 - 3056 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3056 - 3072 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3072 - 3088 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3088 - 3104 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3104 - 3120 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3120 - 3136 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3136 - 3152 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3152 - 3168 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3168 - 3184 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3184 - 3200 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3200 - 3216 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3216 - 3232 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3232 - 3248 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3248 - 3264 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3264 - 3280 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3280 - 3296 */ + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3296 - 3312 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3312 - 3328 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3328 - 3344 */ + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const GBitmap s_fps_background_bitmap = { + .addr = (void*) &s_fps_background_pixels, + .row_size_bytes = 20, + .info_flags = 0x1000, + .bounds = { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 144, .h = 168 }, + }, +}; + + +// GBitmap + pixel data generated by bitmapgen.py: + +static const uint8_t s_fps_topleft_pixels[] = { + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 0 - 16 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 16 - 32 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 32 - 48 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 48 - 64 */ + 0xff, 0x00, 0xf8, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 64 - 80 */ + 0xff, 0xfc, 0xf3, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0x00, /* bytes 80 - 96 */ + 0xff, 0xfc, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xf3, 0xe3, 0x0f, 0x00, 0x00, 0x00, /* bytes 96 - 112 */ + 0xff, 0xfc, 0xf3, 0xf1, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xf3, 0xf8, 0x0f, 0x00, 0x00, 0x00, /* bytes 112 - 128 */ + 0xff, 0xfc, 0x73, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x73, 0xfc, 0x0f, 0x00, 0x00, 0x00, /* bytes 128 - 144 */ + 0xff, 0x00, 0xf0, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf0, 0xf1, 0x0f, 0x00, 0x00, 0x00, /* bytes 144 - 160 */ + 0xff, 0x7c, 0xf8, 0xe3, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x3c, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, /* bytes 160 - 176 */ + 0xff, 0x7c, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 176 - 192 */ + 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 192 - 208 */ + 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 208 - 224 */ + 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 224 - 240 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 240 - 256 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 256 - 272 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, +}; +static const GBitmap s_fps_topleft_bitmap = { + .addr = (void*) &s_fps_topleft_pixels, + .row_size_bytes = 8, + .info_flags = 0x1000, + .bounds = { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 36, .h = 36 }, + }, +}; + +#endif // else of CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 diff --git a/src/fw/apps/demo/fs_resources/Kconfig b/src/fw/apps/demo/fs_resources/Kconfig new file mode 100644 index 0000000000..dacfbfe8df --- /dev/null +++ b/src/fw/apps/demo/fs_resources/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_FS_RESOURCES + bool "Fs Resources" diff --git a/src/fw/apps/demo_apps/test_fs_resources.c b/src/fw/apps/demo/fs_resources/test_fs_resources.c similarity index 75% rename from src/fw/apps/demo_apps/test_fs_resources.c rename to src/fw/apps/demo/fs_resources/test_fs_resources.c index 2048b4d314..c49be0d5ab 100644 --- a/src/fw/apps/demo_apps/test_fs_resources.c +++ b/src/fw/apps/demo/fs_resources/test_fs_resources.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "test_fs_resources.h" diff --git a/src/fw/apps/demo/fs_resources/test_fs_resources.h b/src/fw/apps/demo/fs_resources/test_fs_resources.h new file mode 100644 index 0000000000..cc557e9b1b --- /dev/null +++ b/src/fw/apps/demo/fs_resources/test_fs_resources.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* fs_resources_app_get_info(); diff --git a/src/fw/apps/demo/fs_resources/wscript_build b/src/fw/apps/demo/fs_resources/wscript_build new file mode 100644 index 0000000000..853267df8a --- /dev/null +++ b/src/fw/apps/demo/fs_resources/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_fs_resources.c'], + target='demo_app_fs_resources', +) +bld.env.FW_APPS.append('demo_app_fs_resources') + +# vim:filetype=python diff --git a/src/fw/apps/demo/fw_update_progress_sim/Kconfig b/src/fw/apps/demo/fw_update_progress_sim/Kconfig new file mode 100644 index 0000000000..268b2a90e6 --- /dev/null +++ b/src/fw/apps/demo/fw_update_progress_sim/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_FW_UPDATE_PROGRESS_SIM + bool "Fw Update Progress Sim" diff --git a/src/fw/apps/demo/fw_update_progress_sim/fw_update_progress_sim.c b/src/fw/apps/demo/fw_update_progress_sim/fw_update_progress_sim.c new file mode 100644 index 0000000000..990e143327 --- /dev/null +++ b/src/fw/apps/demo/fw_update_progress_sim/fw_update_progress_sim.c @@ -0,0 +1,135 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "fw_update_progress_sim.h" + +#include "applib/app.h" +#include "applib/app_timer.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/progress_layer.h" +#include "applib/ui/text_layer.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" + +#include +#include + +#define UPDATE_FREQ_MS 75 +#define COMPLETE_PAUSE_MS 1000 +#define PROG_LAYER_START_VAL 6 +// Used to force the progress bar to start at PROG_LAYER_START_VAL and scale +// the rest of the progress between that value and MAX_PROGRESS_PERCENT +#define PROG_LAYER_TRANSFORM(real_prog) \ + (PROG_LAYER_START_VAL + (real_prog * \ + (MAX_PROGRESS_PERCENT - PROG_LAYER_START_VAL) / MAX_PROGRESS_PERCENT)) + +typedef struct { + Window window; + TextLayer percent_done_text_layer; + char percent_done_text_buffer[5]; + ProgressLayer progress_layer; + AppTimer *timer; + unsigned int percent_complete; +} FwUpdateProgressSimData; + +static void prv_update_progress_text(FwUpdateProgressSimData *data) { + sniprintf(data->percent_done_text_buffer, + sizeof(data->percent_done_text_buffer), "%u%%", data->percent_complete); + layer_mark_dirty(&data->percent_done_text_layer.layer); +} + +static void prv_refresh_progress(void *data_in) { + FwUpdateProgressSimData *data = data_in; + if (!data) { + return; + } + + uint32_t delay = UPDATE_FREQ_MS; + if (data->percent_complete >= MAX_PROGRESS_PERCENT) { + data->percent_complete = 0; + delay = COMPLETE_PAUSE_MS; + } else { + data->percent_complete++; + } + + prv_update_progress_text(data); + progress_layer_set_progress(&data->progress_layer, + PROG_LAYER_TRANSFORM(data->percent_complete)); + + data->timer = app_timer_register(delay, prv_refresh_progress, data); +} + +static void prv_window_load_handler(Window *window) { + FwUpdateProgressSimData *data = window_get_user_data(window); + + const int16_t load_bar_length = 108; + const int16_t load_bar_height = 8; + const int16_t x_offset = (window->layer.bounds.size.w - load_bar_length) / 2; + const int16_t y_offset_progress = (window->layer.bounds.size.h - load_bar_height) / 2; + const int16_t y_offset_text = y_offset_progress - 38; + const GRect progress_bounds = GRect(x_offset, y_offset_progress, load_bar_length, load_bar_height); + ProgressLayer *progress_layer = &data->progress_layer; + progress_layer_init(progress_layer, &progress_bounds); + progress_layer_set_corner_radius(progress_layer, 3); + layer_add_child(&window->layer, &progress_layer->layer); + + TextLayer *percent_done_text_layer = &data->percent_done_text_layer; + text_layer_init_with_parameters(percent_done_text_layer, + &GRect(0, y_offset_text, window->layer.bounds.size.w, 30), + data->percent_done_text_buffer, + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), + GColorBlack, GColorClear, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->layer, &percent_done_text_layer->layer); + + data->percent_complete = 0; + prv_update_progress_text(data); + progress_layer_set_progress(progress_layer, PROG_LAYER_TRANSFORM(data->percent_complete)); + data->timer = app_timer_register(UPDATE_FREQ_MS, prv_refresh_progress, data); +} + +static void prv_window_unload_handler(Window *window) { + FwUpdateProgressSimData *data = window_get_user_data(window); + if (data && data->timer) { + app_timer_cancel(data->timer); + data->timer = NULL; + } +} + +static void prv_handle_init(void) { + FwUpdateProgressSimData *data = app_zalloc_check(sizeof(FwUpdateProgressSimData)); + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("FW Update Progress Sim")); + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load_handler, + .unload = prv_window_unload_handler, + }); + app_window_stack_push(window, true); +} + +static void prv_handle_deinit(void) { + FwUpdateProgressSimData *data = app_state_get_user_data(); + app_free(data); +} + +static void prv_main(void) { + prv_handle_init(); + + app_event_loop(); + + prv_handle_deinit(); +} + +const PebbleProcessMd *fw_update_progress_sim_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &prv_main, + .name = "FW Update Progress Sim", + }; + return (const PebbleProcessMd *) &s_app_info; +} diff --git a/src/fw/apps/demo/fw_update_progress_sim/fw_update_progress_sim.h b/src/fw/apps/demo/fw_update_progress_sim/fw_update_progress_sim.h new file mode 100644 index 0000000000..dfcaf54896 --- /dev/null +++ b/src/fw/apps/demo/fw_update_progress_sim/fw_update_progress_sim.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *fw_update_progress_sim_app_get_info(void); diff --git a/src/fw/apps/demo/fw_update_progress_sim/wscript_build b/src/fw/apps/demo/fw_update_progress_sim/wscript_build new file mode 100644 index 0000000000..247a628bbd --- /dev/null +++ b/src/fw/apps/demo/fw_update_progress_sim/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['fw_update_progress_sim.c'], + target='demo_app_fw_update_progress_sim', +) +bld.env.FW_APPS.append('demo_app_fw_update_progress_sim') + +# vim:filetype=python diff --git a/src/fw/apps/demo/gdrawmask_demo/Kconfig b/src/fw/apps/demo/gdrawmask_demo/Kconfig new file mode 100644 index 0000000000..94f1ec8259 --- /dev/null +++ b/src/fw/apps/demo/gdrawmask_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_GDRAWMASK_DEMO + bool "Gdrawmask Demo" diff --git a/src/fw/apps/demo_apps/gdrawmask_demo.c b/src/fw/apps/demo/gdrawmask_demo/gdrawmask_demo.c similarity index 90% rename from src/fw/apps/demo_apps/gdrawmask_demo.c rename to src/fw/apps/demo/gdrawmask_demo/gdrawmask_demo.c index 55d3200e7e..43ad76f254 100644 --- a/src/fw/apps/demo_apps/gdrawmask_demo.c +++ b/src/fw/apps/demo/gdrawmask_demo/gdrawmask_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gdrawmask_demo.h" diff --git a/src/fw/apps/demo/gdrawmask_demo/gdrawmask_demo.h b/src/fw/apps/demo/gdrawmask_demo/gdrawmask_demo.h new file mode 100644 index 0000000000..297b01d54a --- /dev/null +++ b/src/fw/apps/demo/gdrawmask_demo/gdrawmask_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *gdrawmask_demo_get_app_info(); diff --git a/src/fw/apps/demo/gdrawmask_demo/wscript_build b/src/fw/apps/demo/gdrawmask_demo/wscript_build new file mode 100644 index 0000000000..1ce6b7e85f --- /dev/null +++ b/src/fw/apps/demo/gdrawmask_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['gdrawmask_demo.c'], + target='demo_app_gdrawmask_demo', +) +bld.env.FW_APPS.append('demo_app_gdrawmask_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/gfx_tests/Kconfig b/src/fw/apps/demo/gfx_tests/Kconfig new file mode 100644 index 0000000000..cec3223954 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_GFX_TESTS + bool "Gfx Tests" diff --git a/src/fw/apps/demo/gfx_tests/circles.c b/src/fw/apps/demo/gfx_tests/circles.c new file mode 100644 index 0000000000..fa3075d642 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/circles.c @@ -0,0 +1,218 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" + +static GRect rect; +static GPoint center; +static GOvalScaleMode scale_mode; +static int inset; +static int outer_size; +static int inner_size; +static int32_t angle_start; +static int32_t angle_end; + +static void prv_setup_data(GRect bounds) { + center = GPoint(bounds.origin.x + (bounds.size.w / 2), + bounds.origin.y + (bounds.size.h / 2)); + rect = GRect(center.x - (outer_size / 2), center.y - (outer_size / 2), + outer_size, outer_size); + inset = outer_size - inner_size; + scale_mode = GOvalScaleModeFitCircle; +} + +static void prv_setup_even_angles_inner(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 50; + inner_size = 35; + angle_start = (TRIG_MAX_ANGLE / 8) * 3; + angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; + + prv_setup_data(bounds); +} + +static void prv_setup_odd_angles_inner(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 49; + inner_size = 34; + angle_start = (TRIG_MAX_ANGLE / 8) * 3; + angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; + + prv_setup_data(bounds); +} + +static void prv_setup_even_inner(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 50; + inner_size = 35; + angle_start = TRIG_MAX_ANGLE / 4; + angle_end = TRIG_MAX_ANGLE; + + prv_setup_data(bounds); +} + +static void prv_setup_odd_inner(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 49; + inner_size = 34; + angle_start = TRIG_MAX_ANGLE / 4; + angle_end = TRIG_MAX_ANGLE; + + prv_setup_data(bounds); +} + +static void prv_setup_even_angles_full(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 50; + inner_size = 0; + angle_start = (TRIG_MAX_ANGLE / 8) * 3; + angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; + + prv_setup_data(bounds); +} + +static void prv_setup_odd_angles_full(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 49; + inner_size = 0; + angle_start = (TRIG_MAX_ANGLE / 8) * 3; + angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; + + prv_setup_data(bounds); +} + +static void prv_setup_even_full(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 50; + inner_size = 0; + angle_start = TRIG_MAX_ANGLE / 4; + angle_end = TRIG_MAX_ANGLE; + + prv_setup_data(bounds); +} + +static void prv_setup_odd_full(Window *window) { + Layer *window_layer = window_get_root_layer(window); + GRect bounds = window_layer->bounds; + + // Parameters + outer_size = 49; + inner_size = 0; + angle_start = TRIG_MAX_ANGLE / 4; + angle_end = TRIG_MAX_ANGLE; + + prv_setup_data(bounds); +} + +static void prv_test_radial(Layer *layer, GContext* ctx) { + GColor color = { .argb = (uint8_t) rand() }; + graphics_context_set_fill_color(ctx, color); + graphics_fill_radial(ctx, rect, GOvalScaleModeFillCircle, inset, angle_start, angle_end); +} + +static void prv_test_circle(Layer *layer, GContext* ctx) { + GColor color = { .argb = (uint8_t) rand() }; + graphics_context_set_fill_color(ctx, color); + graphics_fill_circle(ctx, center, (outer_size / 2)); +} + +GfxTest g_gfx_test_annulus_even_fill_angles = { + .name = "Annulus Even Angles", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_even_angles_inner, +}; + +GfxTest g_gfx_test_annulus_odd_fill_angles = { + .name = "Annulus Odd Angles", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_odd_angles_inner, +}; + +GfxTest g_gfx_test_annulus_even_fill = { + .name = "Annulus Even", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_even_inner, +}; + +GfxTest g_gfx_test_annulus_odd_fill = { + .name = "Annulus Odd", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_odd_inner, +}; + +GfxTest g_gfx_test_radial_even_fill_angles = { + .name = "Radial Even Angles", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_even_angles_full, +}; + +GfxTest g_gfx_test_radial_odd_fill_angles = { + .name = "Radial Odd Angles", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_odd_angles_full, +}; + +GfxTest g_gfx_test_radial_even_fill = { + .name = "Radial Even", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_even_full, +}; + +GfxTest g_gfx_test_radial_odd_fill = { + .name = "Radial Odd", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_radial, + .setup = prv_setup_odd_full, +}; + +GfxTest g_gfx_test_circle_even = { + .name = "Circle Even", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_circle, + .setup = prv_setup_even_full, +}; + +GfxTest g_gfx_test_circle_odd = { + .name = "Circle Odd", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test_circle, + .setup = prv_setup_odd_full, +}; diff --git a/src/fw/apps/demo/gfx_tests/gpath_masking.c b/src/fw/apps/demo/gfx_tests/gpath_masking.c new file mode 100644 index 0000000000..29ed79c80a --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/gpath_masking.c @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" + +static void prv_test(Layer *layer, GContext* ctx); + +GfxTest g_gfx_test_gpath_masking = { + .name = "GPath masking", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test, +}; + +static const GPathInfo s_triangle_mask = { + .num_points = 3, + .points = (GPoint[]) { + {0, 0}, + {50, 50}, + {50, -50} + } +}; + +static void prv_test(Layer *layer, GContext* ctx) { + GRect bounds = layer->bounds; + GPoint center = GPoint(bounds.size.w/2, bounds.size.h/2); + int outer_radius = 50; + int inner_radius = 35; + + GPath *mask = gpath_create(&s_triangle_mask); + gpath_move_to(mask, center); + + GColor color = { .argb = (uint8_t) rand() }; + GColor bg_color = { .argb = (uint8_t) rand() }; + graphics_context_set_fill_color(ctx, color); + graphics_fill_circle(ctx, center, outer_radius); + + graphics_context_set_fill_color(ctx, bg_color); + graphics_fill_circle(ctx, center, inner_radius); + gpath_draw_filled(ctx, mask); +} diff --git a/src/fw/apps/demo/gfx_tests/list.h b/src/fw/apps/demo/gfx_tests/list.h new file mode 100644 index 0000000000..ceb508e1e3 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/list.h @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +// List of tests. The name given should match the name of the test definition struct in the form +// g_gfx_test_##name + +GFX_TEST(single_line) +GFX_TEST(text) +GFX_TEST(text_clipping) +GFX_TEST(annulus_even_fill_angles) +GFX_TEST(annulus_odd_fill_angles) +GFX_TEST(annulus_even_fill) +GFX_TEST(annulus_odd_fill) +GFX_TEST(radial_even_fill_angles) +GFX_TEST(radial_odd_fill_angles) +GFX_TEST(radial_even_fill) +GFX_TEST(radial_odd_fill) +GFX_TEST(circle_odd) +GFX_TEST(circle_even) +GFX_TEST(gpath_masking) +GFX_TEST(rotated_bitmap_0_assign) +GFX_TEST(rotated_bitmap_0_set) +GFX_TEST(rotated_bitmap_45_assign) +GFX_TEST(rotated_bitmap_45_set) +GFX_TEST(rotated_bitmap_0_assign_64px) +GFX_TEST(rotated_bitmap_0_set_64px) +GFX_TEST(rotated_bitmap_45_assign_64px) +GFX_TEST(rotated_bitmap_45_set_64px) diff --git a/src/fw/apps/demo/gfx_tests/rotated_bitmap.c b/src/fw/apps/demo/gfx_tests/rotated_bitmap.c new file mode 100644 index 0000000000..bf98804794 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/rotated_bitmap.c @@ -0,0 +1,165 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" +#include "util/trig.h" + +static GBitmap *s_gfx_rotated_bitmap_bitmap; +static GBitmap *s_gfx_rotated_bitmap_64_bitmap; + +static GPoint bitmap_center = {DISP_COLS / 2, DISP_ROWS / 2}; +static GPoint bitmap_64_center = {64 / 2, 64 / 2}; + +static void prv_setup(Window *window) { +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 + s_gfx_rotated_bitmap_bitmap = gbitmap_create_blank(GSize(DISP_COLS, DISP_ROWS), + GBitmapFormat1Bit); + s_gfx_rotated_bitmap_64_bitmap = gbitmap_create_blank(GSize(64, 64), GBitmapFormat1Bit); + uint32_t size_full = (DISP_COLS*DISP_ROWS)/8; +#else + s_gfx_rotated_bitmap_bitmap = gbitmap_create_blank(GSize(DISP_COLS, DISP_ROWS), + GBitmapFormat8Bit); + s_gfx_rotated_bitmap_64_bitmap = gbitmap_create_blank(GSize(64, 64), GBitmapFormat8Bit); + uint32_t size_full = (DISP_COLS*DISP_ROWS); +#endif + uint8_t *s_gfx_rotated_bitmap_pixels = s_gfx_rotated_bitmap_bitmap->addr; + // Init images + for (uint32_t index = 0; index < size_full; index++) { + s_gfx_rotated_bitmap_pixels[index] = (index % 2) ? 0xf0 : 0xcc; + } + +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 + uint32_t size_64 = (64*64)/8; +#else + uint32_t size_64 = (64*64); +#endif + uint8_t *s_gfx_rotated_bitmap_64_pixels = s_gfx_rotated_bitmap_64_bitmap->addr; + for (uint32_t index = 0; index < size_64; index++) { + s_gfx_rotated_bitmap_64_pixels[index] = (index % 2) ? 0xf0 : 0xcc; + } +} + +static void prv_test_0_assign(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpAssign); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, + GPointZero, 0, GPointZero); +} + +static void prv_test_0_set(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpSet); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, + GPointZero, 0, GPointZero); +} + +static void prv_test_45_assign(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpAssign); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, + bitmap_center, DEG_TO_TRIGANGLE(45), bitmap_center); +} + +static void prv_test_45_set(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpSet); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, + bitmap_center, DEG_TO_TRIGANGLE(45), bitmap_center); +} + +static void prv_test_0_assign_64px(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpAssign); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, + GPointZero, 0, GPointZero); +} + +static void prv_test_0_set_64px(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpSet); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, + GPointZero, 0, GPointZero); +} + +static void prv_test_45_assign_64px(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpAssign); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, + bitmap_64_center, DEG_TO_TRIGANGLE(45), bitmap_64_center); +} + +static void prv_test_45_set_64px(Layer *layer, GContext* ctx) { + graphics_context_set_compositing_mode(ctx, GCompOpSet); + graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, + bitmap_64_center, DEG_TO_TRIGANGLE(45), bitmap_64_center); +} + +static void prv_teardown(Window *window) { + gbitmap_destroy(s_gfx_rotated_bitmap_bitmap); + gbitmap_destroy(s_gfx_rotated_bitmap_64_bitmap); +} + +GfxTest g_gfx_test_rotated_bitmap_0_assign = { + .name = "RotBit 0-A-full", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_0_assign, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_0_set = { + .name = "RotBit 0-S-full", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_0_set, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_45_assign = { + .name = "RotBit-45-A-full", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_45_assign, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_45_set = { + .name = "RotBit-45-S-full", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_45_set, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_0_assign_64px = { + .name = "RotBit-0-A-64px", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_0_assign_64px, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_0_set_64px = { + .name = "RotBit-0-S-64px", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_0_set_64px, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_45_assign_64px = { + .name = "RotBit-45-A-64px", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_45_assign_64px, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +GfxTest g_gfx_test_rotated_bitmap_45_set_64px = { + .name = "RotBit-45-S-64px", + .duration = 5, + .unit_multiple = 1, + .test_proc = prv_test_45_set_64px, + .setup = prv_setup, + .teardown = prv_teardown, +}; diff --git a/src/fw/apps/demo/gfx_tests/single_line.c b/src/fw/apps/demo/gfx_tests/single_line.c new file mode 100644 index 0000000000..11bd62a0db --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/single_line.c @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" + +static void prv_test(Layer *layer, GContext* ctx); + +GfxTest g_gfx_test_single_line = { + .name = "Single line", + .duration = 1, + .unit_multiple = 1, + .test_proc = prv_test, +}; + +static void prv_test(Layer *layer, GContext* ctx) { + GRect bounds = layer->bounds; + int16_t x1 = (rand() % bounds.size.w) + bounds.origin.x; + int16_t x2 = (rand() % bounds.size.w) + bounds.origin.x; + int16_t y1 = (rand() % bounds.size.h) + bounds.origin.y; + int16_t y2 = (rand() % bounds.size.h) + bounds.origin.y; + + GColor color = { .argb = (uint8_t) rand() }; + graphics_context_set_stroke_color(ctx, color); + graphics_draw_line(ctx, (GPoint){.x = x1, .y = y1}, (GPoint) {.x = x2, .y = y2}); +} diff --git a/src/fw/apps/demo/gfx_tests/tests.c b/src/fw/apps/demo/gfx_tests/tests.c new file mode 100644 index 0000000000..f5204f2411 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/tests.c @@ -0,0 +1,194 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" + +#include "applib/ui/app_window_stack.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" +#include "system/profiler.h" + +#include +#include + +typedef struct { + Window menu_window; + Window test_window; + Window results_window; + MenuLayer test_menu; + TextLayer results_text; + char results_str[100]; + GfxTest *current_test; +} AppData; + +#define RAND_SEED (775762732) // Randomly selected +#define US_PER_MS (1000) +#define US_PER_S (1000 * 1000) +#define TARGET_FPS (30) +#define US_PER_FRAME (20 * US_PER_MS) // Upper bound on amount of time available to the rest of + // the system while a frame is being pushed out to the + // display with the cpu clock at 64MHz + +#define GFX_TEST(name) extern GfxTest g_gfx_test_##name; +#include "list.h" +#undef GFX_TEST +#define GFX_TEST(name) &g_gfx_test_##name, +static GfxTest *s_tests[] = { +#include "list.h" +}; + +static void prv_pop_test_window(void *data) { + AppData *app_data = data; + app_window_stack_pop(false); + app_window_stack_push(&app_data->results_window, false); +} + +static void prv_test_update_proc(Layer *layer, GContext* ctx) { + AppData *app_data = window_get_user_data(layer->window); + GfxTest *test = app_data->current_test; + + srand(RAND_SEED); // Setup rand for routines that need it + + if (test->setup) { + test->setup(layer->window); + } + + PROFILER_INIT; + PROFILER_START; + while (PROFILER_NODE_GET_TOTAL_US(gfx_test_update_proc) < (test->duration * US_PER_S)) { + PROFILER_NODE_START(gfx_test_update_proc); + test->test_proc(layer, ctx); + PROFILER_NODE_STOP(gfx_test_update_proc); + } + PROFILER_STOP; + PROFILER_PRINT_STATS; + + if (test->teardown) { + test->teardown(layer->window); + } + + app_timer_register(0, prv_pop_test_window, app_data); +} + +static void prv_start_test(GfxTest *test, AppData *app_data) { + app_data->current_test = test; + Window *window = &app_data->test_window; + window_init(window, WINDOW_NAME(test->name)); + window_set_user_data(window, app_data); + window_set_fullscreen(window, true); + layer_set_update_proc((Layer *) window, prv_test_update_proc); + app_window_stack_push(window, false); +} + +static uint16_t prv_get_num_rows(struct MenuLayer *menu_layer, uint16_t section, + void *callback_context) { + return (uint16_t) ARRAY_LENGTH(s_tests); +} + +static void prv_draw_row(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, + void *callback_context) { + PBL_ASSERTN(cell_index->row < ARRAY_LENGTH(s_tests)); + menu_cell_title_draw(ctx, cell_layer, s_tests[cell_index->row]->name); +} + +static void prv_click_handler(struct MenuLayer *menu_layer, MenuIndex *cell_index, + void *callback_context) { + PBL_ASSERTN(cell_index->row < ARRAY_LENGTH(s_tests)); + prv_start_test(s_tests[cell_index->row], (AppData *)callback_context); +} + +static void prv_handle_results_click(ClickRecognizerRef recognizer, void *context) { + app_window_stack_pop(false); +} + +static void prv_results_window_load(Window *window) { + AppData *app_data = window_get_user_data(window); + + uint32_t total_us = PROFILER_NODE_GET_TOTAL_US(gfx_test_update_proc); + uint32_t count = PROFILER_NODE_GET_COUNT(gfx_test_update_proc); + + GfxTest *test = app_data->current_test; + + uint32_t avg_us = (10 * total_us) / count; // multiply by 10 to get decimals + uint32_t per_frame = (100 * US_PER_FRAME) / avg_us; // multiply by 100 to get decimals and account + // for x10 in avg_us calc + uint32_t fps = (TARGET_FPS * 100 * US_PER_FRAME) / avg_us; + uint32_t unit_per_frame = ((uint64_t)test->unit_multiple * US_PER_FRAME * 100) / avg_us; + + snprintf(app_data->results_str, sizeof(app_data->results_str), + "%10s\n" + "Avg (µs):\n%"PRIu32".%"PRIu32"\n" + "FPS:\n%"PRIu32".%"PRIu32"\n" + "Per frame @ 30fps:\n%"PRIu32".%"PRIu32"\n" + "Units per frame @ 30fps:\n%"PRIu32".%"PRIu32, + test->name, avg_us / 10, avg_us % 10, fps / 10, fps % 10, per_frame / 10, per_frame % 10, + unit_per_frame / 10, unit_per_frame % 10); + PBL_LOG_DBG("results: %s", app_data->results_str); + text_layer_set_text(&app_data->results_text, app_data->results_str); +} + +static void prv_results_window_unload(Window *window) { + AppData *app_data = window_get_user_data(window); + menu_layer_deinit(&app_data->test_menu); +} + +static void prv_results_window_click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_handle_results_click); +} + +static void handle_init(void) { + AppData *app_data = app_malloc_check(sizeof(AppData)); + + // Menu window + Window *window = &app_data->menu_window; + window_init(window, WINDOW_NAME("GFX Test Framework")); + window_set_user_data(window, app_data); + window_set_fullscreen(window, false); + + MenuLayer *menu = &app_data->test_menu; + menu_layer_init(menu, &window->layer.bounds); + menu_layer_set_callbacks(menu, app_data, &(MenuLayerCallbacks){ + .get_num_rows = prv_get_num_rows, + .draw_row = prv_draw_row, + .select_click = prv_click_handler, + }); + menu_layer_set_click_config_onto_window(menu, window); + layer_add_child((Layer *) window, (Layer *) menu); + app_window_stack_push(window, true); + + // Results window + window = &app_data->results_window; + window_init(window, WINDOW_NAME("Test Results")); + window_set_user_data(window, app_data); + window_set_fullscreen(window, false); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_results_window_load, + .unload = prv_results_window_unload, + }); + window_set_click_config_provider(window, prv_results_window_click_config_provider); + + TextLayer *text = &app_data->results_text; + text_layer_init(text, &window->layer.bounds); + text_layer_set_text(text, ""); + layer_add_child((Layer *) window, (Layer *) text); +} + +static void s_main(void) { + handle_init(); + app_event_loop(); +} + +const PebbleProcessMd* gfx_tests_get_app_info() { + static const PebbleProcessMdSystem gfx_tests_app_info = { + .name = "GFX Tests", + .common = { + .main_func = &s_main, + // UUID: 06a8126b-d805-4197-af6d-8df3c1efb8e4 + .uuid = { 0x06, 0xa8, 0x12, 0x6b, 0xd8, 0x05, 0x41, 0x97, 0xaf, 0x6d, 0x8d, 0xf3, + 0xc1, 0xef, 0xb8, 0xe4}, + } + }; + return (const PebbleProcessMd*) &gfx_tests_app_info; +} diff --git a/src/fw/apps/demo/gfx_tests/tests.h b/src/fw/apps/demo/gfx_tests/tests.h new file mode 100644 index 0000000000..6e6ff45e3d --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/tests.h @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// All graphics/UI includes needed for tests. Add here if more are needed. +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/layer.h" +#include "applib/ui/window.h" +#include "applib/ui/window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/menu_layer.h" +#include "applib/fonts/fonts.h" +#include "util/trig.h" +#include "applib/graphics/gpath.h" + +#include + +//! GFX test struct +typedef struct GfxTest { + char *name; //!< Name string + uint32_t duration; //!< Number of seconds to run the test for + uint32_t unit_multiple; //!< Number of actions per test iteration + LayerUpdateProc test_proc; //!< Test procedure + void (*setup)(Window *window); //!< Test setup function + void (*teardown)( Window *window); //!< Test teardown function +} GfxTest; + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* gfx_tests_get_app_info(void); diff --git a/src/fw/apps/demo/gfx_tests/text.c b/src/fw/apps/demo/gfx_tests/text.c new file mode 100644 index 0000000000..fc36549b7c --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/text.c @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" + +static void prv_setup(Window *window); +static void prv_test(Layer *layer, GContext* ctx); + +GfxTest g_gfx_test_text = { + .name = "Text", + .duration = 5, + .unit_multiple = 0, // Number of characters in test string - set later + .test_proc = prv_test, + .setup = prv_setup, +}; + +static GFont s_font; + +static void prv_setup(Window *window) { + s_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); +} + +static void prv_test(Layer *layer, GContext* ctx) { + const char *text_test_str = "Lorem ipsum dolor sit amet, ne choro argumentum est, quando latine " + "copiosae est ea, usu nonumes accusam te."; + g_gfx_test_text.unit_multiple = strlen(text_test_str); + GColor color = { .argb = (uint8_t) rand() }; + graphics_context_set_text_color(ctx, color); + graphics_draw_text(ctx, text_test_str, s_font, layer->bounds, + GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); +} diff --git a/src/fw/apps/demo/gfx_tests/text_clipping.c b/src/fw/apps/demo/gfx_tests/text_clipping.c new file mode 100644 index 0000000000..1044eb3430 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/text_clipping.c @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "tests.h" + +static void prv_setup(Window *window); +static void prv_test(Layer *layer, GContext* ctx); +static void prv_teardown(Window *window); + +GfxTest g_gfx_test_text_clipping = { + .name = "Text Clipping", + .duration = 5, + .unit_multiple = 0, // Number of characters in test string - set later + .test_proc = prv_test, + .setup = prv_setup, + .teardown = prv_teardown, +}; + +static GFont s_font; +static Layer s_canvas; + +static void prv_setup(Window *window) { + s_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + layer_init(&s_canvas, &GRect(40, 40, 80, 40)); + layer_add_child(&window->layer, &s_canvas); +} + +static void prv_test(Layer *layer, GContext* ctx) { + const char *text_test_str = "This is a test message that is really long!\"#$%&'()*+,-./01234" + "56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrs" + "tuvwxyz{|}"; + g_gfx_test_text_clipping.unit_multiple = strlen(text_test_str); + GColor color = { .argb = (uint8_t) rand() }; + graphics_context_set_text_color(ctx, color); + GRect bounds = layer->bounds; + bounds.origin.y -= 150; // Drop y by 150 pixels so some data gets clipped + graphics_draw_text(ctx, text_test_str, s_font, bounds, + GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); +} + +static void prv_teardown(Window *window) { + layer_remove_from_parent(&s_canvas); +} diff --git a/src/fw/apps/demo/gfx_tests/wscript_build b/src/fw/apps/demo/gfx_tests/wscript_build new file mode 100644 index 0000000000..18cf8bc039 --- /dev/null +++ b/src/fw/apps/demo/gfx_tests/wscript_build @@ -0,0 +1,20 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=[ +'circles.c', +'gpath_masking.c', +'rotated_bitmap.c', +'single_line.c', +'tests.c', +'text.c', +'text_clipping.c', +], + target='demo_app_gfx_tests', +) +bld.env.FW_APPS.append('demo_app_gfx_tests') + +# vim:filetype=python diff --git a/src/fw/apps/demo/hrm_demo/Kconfig b/src/fw/apps/demo/hrm_demo/Kconfig new file mode 100644 index 0000000000..af3901f755 --- /dev/null +++ b/src/fw/apps/demo/hrm_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_HRM_DEMO + bool "Hrm Demo" diff --git a/src/fw/apps/demo/hrm_demo/hrm_demo.c b/src/fw/apps/demo/hrm_demo/hrm_demo.c new file mode 100644 index 0000000000..00feeb2761 --- /dev/null +++ b/src/fw/apps/demo/hrm_demo/hrm_demo.c @@ -0,0 +1,296 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "applib/app.h" +#include "applib/app_message/app_message.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "apps/system_app_ids.h" +#include "kernel/pbl_malloc.h" +#include "mfg/mfg_info.h" +#include "mfg/mfg_serials.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "system/passert.h" + +#define BPM_STRING_LEN 10 + +typedef enum { + AppMessageKey_Status = 1, + + AppMessageKey_HeartRate = 10, + AppMessageKey_Confidence = 11, + AppMessageKey_Current = 12, + AppMessageKey_TIA = 13, + AppMessageKey_PPG = 14, + AppMessageKey_AccelData = 15, + AppMessageKey_SerialNumber = 16, + AppMessageKey_Model = 17, + AppMessageKey_HRMProtocolVersionMajor = 18, + AppMessageKey_HRMProtocolVersionMinor = 19, + AppMessageKey_HRMSoftwareVersionMajor = 20, + AppMessageKey_HRMSoftwareVersionMinor = 21, + AppMessageKey_HRMApplicationID = 22, + AppMessageKey_HRMHardwareRevision = 23, +} AppMessageKey; + +typedef enum { + AppStatus_Stopped = 0, + AppStatus_Enabled_1HZ = 1, +} AppStatus; + +typedef struct { + HRMSessionRef session; + EventServiceInfo hrm_event_info; + + Window window; + TextLayer bpm_text_layer; + TextLayer quality_text_layer; + + char bpm_string[BPM_STRING_LEN]; + + bool ready_to_send; + DictionaryIterator *out_iter; +} AppData; + +static char *prv_get_quality_string(HRMQuality quality) { + switch (quality) { + case HRMQuality_OffWrist: + return "Off Wrist"; + case HRMQuality_Worst: + return "Worst"; + case HRMQuality_Poor: + return "Poor"; + case HRMQuality_Acceptable: + return "Acceptable"; + case HRMQuality_Good: + return "Good"; + case HRMQuality_Excellent: + return "Excellent"; + } + WTF; +} + +static char *prv_translate_error(AppMessageResult result) { + switch (result) { + case APP_MSG_OK: return "APP_MSG_OK"; + case APP_MSG_SEND_TIMEOUT: return "APP_MSG_SEND_TIMEOUT"; + case APP_MSG_SEND_REJECTED: return "APP_MSG_SEND_REJECTED"; + case APP_MSG_NOT_CONNECTED: return "APP_MSG_NOT_CONNECTED"; + case APP_MSG_APP_NOT_RUNNING: return "APP_MSG_APP_NOT_RUNNING"; + case APP_MSG_INVALID_ARGS: return "APP_MSG_INVALID_ARGS"; + case APP_MSG_BUSY: return "APP_MSG_BUSY"; + case APP_MSG_BUFFER_OVERFLOW: return "APP_MSG_BUFFER_OVERFLOW"; + case APP_MSG_ALREADY_RELEASED: return "APP_MSG_ALREADY_RELEASED"; + case APP_MSG_CALLBACK_ALREADY_REGISTERED: return "APP_MSG_CALLBACK_ALREADY_REGISTERED"; + case APP_MSG_CALLBACK_NOT_REGISTERED: return "APP_MSG_CALLBACK_NOT_REGISTERED"; + case APP_MSG_OUT_OF_MEMORY: return "APP_MSG_OUT_OF_MEMORY"; + case APP_MSG_CLOSED: return "APP_MSG_CLOSED"; + case APP_MSG_INTERNAL_ERROR: return "APP_MSG_INTERNAL_ERROR"; + default: return "UNKNOWN ERROR"; + } +} + +static void prv_send_msg(void) { + AppData *app_data = app_state_get_user_data(); + + AppMessageResult result = app_message_outbox_send(); + if (result == APP_MSG_OK) { + app_data->ready_to_send = false; + } else { + PBL_LOG_DBG("Error sending message: %s", prv_translate_error(result)); + } +} + +static void prv_send_status_and_version(void) { + AppData *app_data = app_state_get_user_data(); + PBL_LOG_DBG("Sending status and version to mobile app"); + + AppMessageResult result = app_message_outbox_begin(&app_data->out_iter); + if (result != APP_MSG_OK) { + PBL_LOG_DBG("Failed to begin outbox - reason %i %s", + result, prv_translate_error(result)); + return; + } + + dict_write_uint8(app_data->out_iter, AppMessageKey_Status, AppStatus_Enabled_1HZ); + + char serial_number_buffer[MFG_SERIAL_NUMBER_SIZE + 1]; + mfg_info_get_serialnumber(serial_number_buffer, sizeof(serial_number_buffer)); + dict_write_data(app_data->out_iter, AppMessageKey_SerialNumber, + (uint8_t*) serial_number_buffer, sizeof(serial_number_buffer)); + +#ifdef CONFIG_IS_BIGBOARD + WatchInfoColor watch_color = WATCH_INFO_COLOR_UNKNOWN; +#else + WatchInfoColor watch_color = mfg_info_get_watch_color(); +#endif // CONFIG_IS_BIGBOARD + dict_write_uint32(app_data->out_iter, AppMessageKey_Model, watch_color); + + prv_send_msg(); +} + +static void prv_handle_hrm_data(PebbleEvent *e, void *context) { + AppData *app_data = app_state_get_user_data(); + + if (e->type == PEBBLE_HRM_EVENT) { + PebbleHRMEvent *hrm = &e->hrm; + + if (hrm->event_type == HRMEvent_BPM) { + snprintf(app_data->bpm_string, sizeof(app_data->bpm_string), "%"PRIu8" BPM", hrm->bpm.bpm); + text_layer_set_text(&app_data->quality_text_layer, prv_get_quality_string(hrm->bpm.quality)); + layer_mark_dirty(&app_data->window.layer); + } else if (hrm->event_type == HRMEvent_SubscriptionExpiring) { + PBL_LOG_INFO("Got subscription expiring event"); + // Subscribe again if our subscription is expiring + const uint32_t update_time_s = 1; + app_data->session = sys_hrm_manager_app_subscribe(APP_ID_HRM_DEMO, update_time_s, + SECONDS_PER_HOUR, HRMFeature_BPM); + } + } +} + +static void prv_enable_hrm(void) { + AppData *app_data = app_state_get_user_data(); + + app_data->hrm_event_info = (EventServiceInfo) { + .type = PEBBLE_HRM_EVENT, + .handler = prv_handle_hrm_data, + }; + event_service_client_subscribe(&app_data->hrm_event_info); + + // TODO: Let the mobile app control this? + const uint32_t update_time_s = 1; + app_data->session = sys_hrm_manager_app_subscribe( + APP_ID_HRM_DEMO, update_time_s, SECONDS_PER_HOUR, + HRMFeature_BPM); +} + +static void prv_disable_hrm(void) { + AppData *app_data = app_state_get_user_data(); + + event_service_client_unsubscribe(&app_data->hrm_event_info); + sys_hrm_manager_unsubscribe(app_data->session); +} + +static void prv_handle_mobile_status_request(AppStatus status) { + AppData *app_data = app_state_get_user_data(); + + if (status == AppStatus_Stopped) { + text_layer_set_text(&app_data->bpm_text_layer, "Paused"); + text_layer_set_text(&app_data->quality_text_layer, "Paused by mobile"); + prv_disable_hrm(); + } else { + app_data->bpm_string[0] = '\0'; + text_layer_set_text(&app_data->bpm_text_layer, app_data->bpm_string); + text_layer_set_text(&app_data->quality_text_layer, "Loading..."); + prv_enable_hrm(); + } +} + +static void prv_message_received_cb(DictionaryIterator *iterator, void *context) { + Tuple *status_tuple = dict_find(iterator, AppMessageKey_Status); + + if (status_tuple) { + prv_handle_mobile_status_request(status_tuple->value->uint8); + } +} + +static void prv_message_sent_cb(DictionaryIterator *iterator, void *context) { + AppData *app_data = app_state_get_user_data(); + + app_data->ready_to_send = true; +} + +static void prv_message_failed_cb(DictionaryIterator *iterator, + AppMessageResult reason, void *context) { + PBL_LOG_DBG("Out message send failed - reason %i %s", + reason, prv_translate_error(reason)); + AppData *app_data = app_state_get_user_data(); + app_data->ready_to_send = true; +} + +static void prv_remote_notify_timer_cb(void *data) { + prv_send_status_and_version(); +} + +static void prv_init(void) { + AppData *app_data = app_malloc_check(sizeof(*app_data)); + *app_data = (AppData) { + .session = (HRMSessionRef)app_data, // Use app data as session ref + .ready_to_send = false, + }; + app_state_set_user_data(app_data); + + Window *window = &app_data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + GRect bounds = window->layer.bounds; + + bounds.origin.y += 40; + TextLayer *bpm_tl = &app_data->bpm_text_layer; + text_layer_init(bpm_tl, &bounds); + text_layer_set_font(bpm_tl, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + text_layer_set_text_alignment(bpm_tl, GTextAlignmentCenter); + text_layer_set_text(bpm_tl, app_data->bpm_string); + layer_add_child(&window->layer, &bpm_tl->layer); + + bounds.origin.y += 35; + TextLayer *quality_tl = &app_data->quality_text_layer; + text_layer_init(quality_tl, &bounds); + text_layer_set_font(quality_tl, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_text_alignment(quality_tl, GTextAlignmentCenter); + text_layer_set_text(quality_tl, "Loading..."); + layer_add_child(&window->layer, &quality_tl->layer); + + const uint32_t inbox_size = 64; + const uint32_t outbox_size = 256; + AppMessageResult result = app_message_open(inbox_size, outbox_size); + if (result != APP_MSG_OK) { + PBL_LOG_ERR("Unable to open app message! %i %s", + result, prv_translate_error(result)); + } else { + PBL_LOG_DBG("Successfully opened app message"); + } + + if (!sys_hrm_manager_is_hrm_present()) { + text_layer_set_text(quality_tl, "No HRM Present"); + } else { + text_layer_set_text(quality_tl, "Loading..."); + prv_enable_hrm(); + } + + app_message_register_inbox_received(prv_message_received_cb); + app_message_register_outbox_sent(prv_message_sent_cb); + app_message_register_outbox_failed(prv_message_failed_cb); + + app_timer_register(1000, prv_remote_notify_timer_cb, NULL); + + app_window_stack_push(window, true); +} + +static void prv_deinit(void) { + AppData *app_data = app_state_get_user_data(); + sys_hrm_manager_unsubscribe(app_data->session); +} + +static void prv_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} + +const PebbleProcessMd* hrm_demo_get_app_info(void) { + static const PebbleProcessMdSystem s_hrm_demo_app_info = { + .name = "HRM Demo", + .common.uuid = { 0xf8, 0x1b, 0x2a, 0xf8, 0x13, 0x0a, 0x11, 0xe6, + 0x86, 0x9f, 0xa4, 0x5e, 0x60, 0xb9, 0x77, 0x3d }, + .common.main_func = &prv_main, + }; + // Only show in launcher if HRM is present + return (sys_hrm_manager_is_hrm_present()) ? (const PebbleProcessMd*)&s_hrm_demo_app_info : NULL; +} diff --git a/src/fw/apps/demo/hrm_demo/wscript_build b/src/fw/apps/demo/hrm_demo/wscript_build new file mode 100644 index 0000000000..9b8a94ec1e --- /dev/null +++ b/src/fw/apps/demo/hrm_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['hrm_demo.c'], + target='demo_app_hrm_demo', +) +bld.env.FW_APPS.append('demo_app_hrm_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/idl/Kconfig b/src/fw/apps/demo/idl/Kconfig new file mode 100644 index 0000000000..f7c9ad0966 --- /dev/null +++ b/src/fw/apps/demo/idl/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_IDL + bool "Idl" diff --git a/src/fw/apps/demo/idl/idl_demo.c b/src/fw/apps/demo/idl/idl_demo.c new file mode 100644 index 0000000000..66cb40010e --- /dev/null +++ b/src/fw/apps/demo/idl/idl_demo.c @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "idl_demo.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" +#include "system/hexdump.h" + +#include "pb_decode.h" +#include "pb_encode.h" +#include "nanopb/simple.pb.h" +#include "nanopb/measurements.pb.h" + +static void prv_init(void) { + SimpleMessage msg = { + .lucky_number = 42, + }; + uint8_t *buffer = app_malloc(30); + pb_ostream_t s = pb_ostream_from_buffer(buffer, 30); + pb_encode(&s, SimpleMessage_fields, &msg); + PBL_LOG_DBG("Encoded message, size: %u bytes", s.bytes_written); + PBL_HEXDUMP(LOG_LEVEL_DEBUG, buffer, s.bytes_written); + app_state_set_user_data(buffer); +} + +static void prv_deinit(void) { + SimpleMessage msg = {0}; + uint8_t *buffer = app_state_get_user_data(); + pb_istream_t s = pb_istream_from_buffer(buffer, 30); + pb_decode(&s, SimpleMessage_fields, &msg); + PBL_LOG_DBG("The lucky number is %"PRId32, msg.lucky_number); +} + +static void prv_app_main(void) { + prv_init(); + Window *window = window_create(); + app_window_stack_push(window, false /*animated*/); + app_event_loop(); + prv_deinit(); +} + +const PebbleProcessMd *idl_demo_get_app_info() { + static const PebbleProcessMdSystem s_app_data = { + .common = { + .main_func = prv_app_main, + // UUID: 101a32d95-1234-46d4-1234-854cc62f97f9 + .uuid = {0x99, 0xa3, 0x2d, 0x95, 0x12, 0x34, 0x46, 0xd4, + 0x12, 0x34, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9}, + }, + .name = "IDL Demo", + }; + return (const PebbleProcessMd *)&s_app_data; +} diff --git a/src/fw/apps/demo/idl/idl_demo.h b/src/fw/apps/demo/idl/idl_demo.h new file mode 100644 index 0000000000..256fe1ec83 --- /dev/null +++ b/src/fw/apps/demo/idl/idl_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* idl_demo_get_app_info(void); diff --git a/src/fw/apps/demo/idl/wscript_build b/src/fw/apps/demo/idl/wscript_build new file mode 100644 index 0000000000..e1c8776191 --- /dev/null +++ b/src/fw/apps/demo/idl/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['idl_demo.c'], + target='demo_app_idl', +) +bld.env.FW_APPS.append('demo_app_idl') + +# vim:filetype=python diff --git a/src/fw/apps/demo/kill_bt/Kconfig b/src/fw/apps/demo/kill_bt/Kconfig new file mode 100644 index 0000000000..4429aaa681 --- /dev/null +++ b/src/fw/apps/demo/kill_bt/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_KILL_BT + bool "Kill Bt" diff --git a/src/fw/apps/demo/kill_bt/kill_bt.c b/src/fw/apps/demo/kill_bt/kill_bt.c new file mode 100644 index 0000000000..dbb622093b --- /dev/null +++ b/src/fw/apps/demo/kill_bt/kill_bt.c @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "progress.h" + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" + +static unsigned int s_progress_count = 0; + +struct AppState { + Window window; +}; + +static void prv_window_load(Window *window) { + struct AppState* data = window_get_user_data(window); + (void)data; +} + +static void push_window(struct AppState *data) { + Window* window = &data->window; + window_init(window, WINDOW_NAME("Kill BT Demo")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + + +//////////////////// +// App boilerplate + +static void handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + PBL_LOG_DBG("Try to kill the BT:%d", s_progress_count); + s_progress_count += 1; + + bt_ctl_reset_bluetooth(); +} + +static void handle_init(void) { + struct AppState* data = app_malloc_check(sizeof(struct AppState)); + + app_state_set_user_data(data); + tick_timer_service_subscribe(SECOND_UNIT, handle_second_tick); + push_window(data); +} + +static void handle_deinit(void) { + struct AppState* data = app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* kill_bt_app_get_info() { + static const PebbleProcessMdSystem kill_bt_app_info = { + .name = "Kill BT Test", + .common.main_func = s_main, + }; + return (const PebbleProcessMd*) &kill_bt_app_info; +} + diff --git a/src/fw/apps/demo/kill_bt/kill_bt.h b/src/fw/apps/demo/kill_bt/kill_bt.h new file mode 100644 index 0000000000..2b75e0e188 --- /dev/null +++ b/src/fw/apps/demo/kill_bt/kill_bt.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* kill_bt_app_get_info(); diff --git a/src/fw/apps/demo/kill_bt/wscript_build b/src/fw/apps/demo/kill_bt/wscript_build new file mode 100644 index 0000000000..2d631f147f --- /dev/null +++ b/src/fw/apps/demo/kill_bt/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=[ +'.', +'../shared', +], + source=['kill_bt.c'], + target='demo_app_kill_bt', +) +bld.env.FW_APPS.append('demo_app_kill_bt') + +# vim:filetype=python diff --git a/src/fw/apps/demo/kino_layer/Kconfig b/src/fw/apps/demo/kino_layer/Kconfig new file mode 100644 index 0000000000..017b4321ac --- /dev/null +++ b/src/fw/apps/demo/kino_layer/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_KINO_LAYER + bool "Kino Layer" diff --git a/src/fw/apps/demo_apps/kino_layer_demo.c b/src/fw/apps/demo/kino_layer/kino_layer_demo.c similarity index 83% rename from src/fw/apps/demo_apps/kino_layer_demo.c rename to src/fw/apps/demo/kino_layer/kino_layer_demo.c index 38dc62753e..b631159835 100644 --- a/src/fw/apps/demo_apps/kino_layer_demo.c +++ b/src/fw/apps/demo/kino_layer/kino_layer_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kino_layer_demo.h" diff --git a/src/fw/apps/demo/kino_layer/kino_layer_demo.h b/src/fw/apps/demo/kino_layer/kino_layer_demo.h new file mode 100644 index 0000000000..2e9bc8545d --- /dev/null +++ b/src/fw/apps/demo/kino_layer/kino_layer_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *kino_layer_demo_get_info(void); diff --git a/src/fw/apps/demo/kino_layer/wscript_build b/src/fw/apps/demo/kino_layer/wscript_build new file mode 100644 index 0000000000..5866e220a9 --- /dev/null +++ b/src/fw/apps/demo/kino_layer/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['kino_layer_demo.c'], + target='demo_app_kino_layer', +) +bld.env.FW_APPS.append('demo_app_kino_layer') + +# vim:filetype=python diff --git a/src/fw/apps/demo/light_config/Kconfig b/src/fw/apps/demo/light_config/Kconfig new file mode 100644 index 0000000000..b933a7a26e --- /dev/null +++ b/src/fw/apps/demo/light_config/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_LIGHT_CONFIG + bool "Light Config" diff --git a/src/fw/apps/demo/light_config/light_config.c b/src/fw/apps/demo/light_config/light_config.c new file mode 100644 index 0000000000..4412ee43b9 --- /dev/null +++ b/src/fw/apps/demo/light_config/light_config.c @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/number_window.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/light.h" + +#include + +static void selected_pwm_percentage(NumberWindow *nw, void *ctx) { + uint8_t val = number_window_get_value(nw); + backlight_set_intensity(val); +} + +static void handle_init(void) { + NumberWindow *light_num_window = number_window_create("Light Config", + (NumberWindowCallbacks) { .selected = selected_pwm_percentage }, + NULL); + app_state_set_user_data(light_num_window); + + uint8_t scale_granularity = 5; // 5 percent at a time + uint8_t curr_percent = scale_granularity * ((backlight_get_intensity() + + scale_granularity - 1) / scale_granularity); + + number_window_set_value(light_num_window, curr_percent); + number_window_set_max(light_num_window, 100); + number_window_set_min(light_num_window, 0); + number_window_set_step_size(light_num_window, scale_granularity); + + app_window_stack_push(number_window_get_window(light_num_window), true); +} + +static void handle_deinit(void) { + NumberWindow *data = app_state_get_user_data(); + number_window_destroy(data); +} + +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +const PebbleProcessMd* light_config_get_info() { + static const PebbleProcessMdSystem s_accel_config_info = { + .common.main_func = s_main, + .name = "Light Config" + }; + return (const PebbleProcessMd*) &s_accel_config_info; +} diff --git a/src/fw/apps/demo/light_config/wscript_build b/src/fw/apps/demo/light_config/wscript_build new file mode 100644 index 0000000000..356033a292 --- /dev/null +++ b/src/fw/apps/demo/light_config/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['light_config.c'], + target='demo_app_light_config', +) +bld.env.FW_APPS.append('demo_app_light_config') + +# vim:filetype=python diff --git a/src/fw/apps/demo/menu/Kconfig b/src/fw/apps/demo/menu/Kconfig new file mode 100644 index 0000000000..38bda5371c --- /dev/null +++ b/src/fw/apps/demo/menu/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MENU + bool "Menu" diff --git a/src/fw/apps/demo/menu/menu.c b/src/fw/apps/demo/menu/menu.c new file mode 100644 index 0000000000..f62f78c95f --- /dev/null +++ b/src/fw/apps/demo/menu/menu.c @@ -0,0 +1,211 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "menu.h" + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" +#include "system/passert.h" + +#include + +#define BUFFER_SIZE 25 + +static const uint8_t s_music_launcher_icon_pixels[] = { + 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, /* bytes 0 - 16 */ + 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x18, 0x00, 0x7f, 0x00, 0x1f, 0x00, /* bytes 16 - 32 */ + 0x7f, 0xf0, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 32 - 48 */ + 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 48 - 64 */ + 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x7f, 0x7c, 0x00, 0x00, /* bytes 64 - 80 */ + 0x03, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x80, 0x00, 0x00, 0x3c, 0xc0, 0x00, /* bytes 80 - 96 */ + 0x00, 0x7e, 0xe0, 0x00, 0x00, 0xff, 0xff, 0x00, 0x81, 0xff, 0xff, 0x00, +}; + +static const GBitmap s_music_launcher_icon_bitmap = { + .addr = (void*) &s_music_launcher_icon_pixels, + .row_size_bytes = 4, + .info_flags = 0x1000, + .bounds = { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 24, .h = 27 }, + }, +}; + +typedef struct { + Window window; + MenuLayer menu_layer; + GBitmap icon; + + Window detail_window; + TextLayer detail_text; + char detail_text_buffer[64]; +} AppData; + +static uint16_t get_num_sections_callback(struct MenuLayer *menu_layer, AppData *data) { + (void)data; + (void)menu_layer; + return 4; +} + +static uint16_t get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { + (void)data; + (void)menu_layer; + switch (section_index) { + default: + case 0: return 2; + case 1: return 3; + case 2: return 4; + case 3: return 5; + } +} + +static int16_t get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { + (void)data; + (void)menu_layer; + (void)cell_index; + + // Variable row heights demo: + switch (cell_index->row % 3) { + default: + case 0: + return 44; + + case 1: + return 64; + + case 2: + return 84; + } +} + +static int16_t get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { + (void)data; + (void)menu_layer; + (void)section_index; + return MENU_CELL_BASIC_HEADER_HEIGHT; +} + +static void draw_row_callback(GContext* ctx, Layer *cell_layer, MenuIndex *cell_index, AppData *data) { + char title[BUFFER_SIZE]; + char subtitle[BUFFER_SIZE]; + + switch (cell_index->row % 2) { + case 0: + sniprintf(title, BUFFER_SIZE, "Title %i/%i ", cell_index->section, cell_index->row); + sniprintf(subtitle, BUFFER_SIZE, "Subtitle %i/%i", cell_index->section, cell_index->row); + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, (GBitmap*)&s_music_launcher_icon_bitmap); + break; + + default: + case 1: + sniprintf(title, BUFFER_SIZE, "Only Title %i/%i", cell_index->section, cell_index->row); + menu_cell_title_draw(ctx, cell_layer, title); + break; + } + (void)data; +} + +static void draw_header_callback(GContext* ctx, Layer *cell_layer, uint16_t section_index, AppData *data) { + char title[BUFFER_SIZE]; + sniprintf(title, BUFFER_SIZE, "Section Header (%i)", section_index); + menu_cell_basic_header_draw(ctx, cell_layer, title); + (void)data; +} + +static void detail_window_load(Window *window) { + AppData *data = window_get_user_data(window); + TextLayer *text_layer = &data->detail_text; + text_layer_init(text_layer, &window->layer.bounds); + text_layer_set_text(text_layer, data->detail_text_buffer); + layer_add_child(&window->layer, &text_layer->layer); +} + +static void push_detail_window(AppData *data, MenuIndex *index, bool is_long_click) { + sniprintf(data->detail_text_buffer, sizeof(data->detail_text_buffer), "SELECTION:\n\nSection %i, Row %i\nLong click: %c", index->section, index->row, is_long_click ? 'Y' : 'N'); + + Window *detail_window = &data->detail_window; + window_init(detail_window, WINDOW_NAME("Demo Menu Detail")); + window_set_user_data(detail_window, data); + window_set_window_handlers(detail_window, &(WindowHandlers) { + .load = detail_window_load, + }); + const bool animated = true; + app_window_stack_push(detail_window, animated); +} + +static void select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { + push_detail_window(data, cell_index, false); + (void)menu_layer; +} + +static void select_long_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { + push_detail_window(data, cell_index, true); + (void)menu_layer; +} + +static void prv_window_load(Window *window) { + AppData *data = window_get_user_data(window); + + MenuLayer *menu_layer = &data->menu_layer; + menu_layer_init(menu_layer, &window->layer.bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_sections = (MenuLayerGetNumberOfSectionsCallback) get_num_sections_callback, + .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) get_num_rows_callback, + .get_cell_height = (MenuLayerGetCellHeightCallback) get_cell_height_callback, + .get_header_height = (MenuLayerGetHeaderHeightCallback) get_header_height_callback, + .draw_row = (MenuLayerDrawRowCallback) draw_row_callback, + .draw_header = (MenuLayerDrawHeaderCallback) draw_header_callback, + .select_click = (MenuLayerSelectCallback) select_callback, + .select_long_click = (MenuLayerSelectCallback) select_long_callback, + }); + menu_layer_set_click_config_onto_window(menu_layer, window); + layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); +} + +static void push_window(AppData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Demo Menu")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate + +static void handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + push_window(data); +} + +static void handle_deinit(void) { + AppData *data = app_state_get_user_data(); + menu_layer_deinit(&data->menu_layer); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* menu_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "MenuLayer Demo" + }; + return (const PebbleProcessMd*) &s_app_info; +} + +#undef BUFFER_SIZE diff --git a/src/fw/apps/demo/menu/menu.h b/src/fw/apps/demo/menu/menu.h new file mode 100644 index 0000000000..1609c18075 --- /dev/null +++ b/src/fw/apps/demo/menu/menu.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* menu_app_get_info(); diff --git a/src/fw/apps/demo/menu/wscript_build b/src/fw/apps/demo/menu/wscript_build new file mode 100644 index 0000000000..1f78730810 --- /dev/null +++ b/src/fw/apps/demo/menu/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['menu.c'], + target='demo_app_menu', +) +bld.env.FW_APPS.append('demo_app_menu') + +# vim:filetype=python diff --git a/src/fw/apps/demo/menu_overflow/Kconfig b/src/fw/apps/demo/menu_overflow/Kconfig new file mode 100644 index 0000000000..a4d799c347 --- /dev/null +++ b/src/fw/apps/demo/menu_overflow/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MENU_OVERFLOW + bool "Menu Overflow" diff --git a/src/fw/apps/demo/menu_overflow/menu_overflow.c b/src/fw/apps/demo/menu_overflow/menu_overflow.c new file mode 100644 index 0000000000..69865d368c --- /dev/null +++ b/src/fw/apps/demo/menu_overflow/menu_overflow.c @@ -0,0 +1,106 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "menu_overflow.h" + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "applib/ui/menu_layer.h" + + +static Window *window; +static MenuLayer *menu_layer; +static char *section_names[] = { "Movies", "Books", "Video Games", "Television", "Alcohol" }; +static char *row_names[5][2] = { + { "Avengers", "Eden of the East" }, + { "A Song of Ice and Fire", "Lord of the Rings" }, + { "Team Fortress 2", "Super Meat Boy" }, + { "Sunny in Philadelphia", "Gotham" }, + { "Beer", "Vodka" } +}; + + +//////////////////// +// MenuLayer construction and callback + +static uint16_t prv_menu_get_num_sections_callback(struct MenuLayer *menu_layer, + void *callback_context) { + return 5; +} + +static uint16_t prv_menu_get_num_rows_callback(struct MenuLayer *menu_layer, + uint16_t section_index, void *callback_context) { + return 2; +} + +static int16_t prv_menu_get_header_height_callback(struct MenuLayer *menu_layer, + uint16_t section_index, void *callback_context) { + return 15; +} + +static int16_t prv_menu_get_cell_height_callback(struct MenuLayer *menu_layer, + MenuIndex *cell_index, void *callback_context) { + return 20; +} + +static int16_t prv_menu_get_separator_height_callback(struct MenuLayer *menu_layer, + MenuIndex *cell_index, void *callback_context) { + return 10; +} + +static void prv_menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, + uint16_t section_index, void *callback_context) { + menu_cell_basic_header_draw(ctx, cell_layer, section_names[section_index]); +} + +static void prv_menu_draw_row_callback(GContext *ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *callback_context) { + graphics_context_set_text_color(ctx, GColorBlack); + graphics_draw_text(ctx, row_names[cell_index->section][cell_index->row], + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), GRect(4, 2, 136, 22), + GTextOverflowModeFill, GTextAlignmentLeft, NULL); +} + +//////////////////// +// App boilerplate + +static void init(void) { + window = window_create(); + + menu_layer = menu_layer_create(window_get_root_layer(window)->bounds); + menu_layer_set_callbacks(menu_layer, NULL, &(MenuLayerCallbacks) { + .get_num_sections = prv_menu_get_num_sections_callback, + .get_num_rows = prv_menu_get_num_rows_callback, + .get_header_height = prv_menu_get_header_height_callback, + .get_cell_height = prv_menu_get_cell_height_callback, + .get_separator_height = prv_menu_get_separator_height_callback, + .draw_header = prv_menu_draw_header_callback, + .draw_row = prv_menu_draw_row_callback, + }); + + menu_layer_set_click_config_onto_window(menu_layer, window); + layer_add_child(window_get_root_layer(window), menu_layer_get_layer(menu_layer)); + + app_window_stack_push(window, true); +} + +static void deinit(void) { + menu_layer_destroy(menu_layer); + window_destroy(window); +} + +static void s_main(void) { + init(); + + app_event_loop(); + + deinit(); +} + +const PebbleProcessMd* menu_overflow_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Menu Overflow" + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/menu_overflow/menu_overflow.h b/src/fw/apps/demo/menu_overflow/menu_overflow.h new file mode 100644 index 0000000000..4542f0a62b --- /dev/null +++ b/src/fw/apps/demo/menu_overflow/menu_overflow.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* menu_overflow_app_get_info(); diff --git a/src/fw/apps/demo/menu_overflow/wscript_build b/src/fw/apps/demo/menu_overflow/wscript_build new file mode 100644 index 0000000000..8ff1e4f0dc --- /dev/null +++ b/src/fw/apps/demo/menu_overflow/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['menu_overflow.c'], + target='demo_app_menu_overflow', +) +bld.env.FW_APPS.append('demo_app_menu_overflow') + +# vim:filetype=python diff --git a/src/fw/apps/demo/menu_right_icon/Kconfig b/src/fw/apps/demo/menu_right_icon/Kconfig new file mode 100644 index 0000000000..eda1befe90 --- /dev/null +++ b/src/fw/apps/demo/menu_right_icon/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MENU_RIGHT_ICON + bool "Menu Right Icon" diff --git a/src/fw/apps/demo_apps/menu_layer_right_icon.c b/src/fw/apps/demo/menu_right_icon/menu_layer_right_icon.c similarity index 86% rename from src/fw/apps/demo_apps/menu_layer_right_icon.c rename to src/fw/apps/demo/menu_right_icon/menu_layer_right_icon.c index 7bf9c16522..7acf18bc9c 100644 --- a/src/fw/apps/demo_apps/menu_layer_right_icon.c +++ b/src/fw/apps/demo/menu_right_icon/menu_layer_right_icon.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "menu_layer_right_icon.h" @@ -90,7 +77,7 @@ static int16_t menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuI } static void prv_window_load(Window *window) { - PBL_LOG(LOG_LEVEL_INFO, "WINDOW LOADING"); + PBL_LOG_INFO("WINDOW LOADING"); AppData *data = window_get_user_data(window); gbitmap_init_with_resource(&data->checked_icon, RESOURCE_ID_CHECKBOX_ICON_CHECKED); @@ -109,7 +96,7 @@ static void prv_window_load(Window *window) { } static void push_window(AppData *data) { - PBL_LOG(LOG_LEVEL_INFO, "PUSHING WINDOW"); + PBL_LOG_INFO("PUSHING WINDOW"); Window *window = &data->window; window_init(window, WINDOW_NAME("Demo Menu")); window_set_user_data(window, data); diff --git a/src/fw/apps/demo/menu_right_icon/menu_layer_right_icon.h b/src/fw/apps/demo/menu_right_icon/menu_layer_right_icon.h new file mode 100644 index 0000000000..d9adbf4d04 --- /dev/null +++ b/src/fw/apps/demo/menu_right_icon/menu_layer_right_icon.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* menu_layer_right_icon_app_get_info(); diff --git a/src/fw/apps/demo/menu_right_icon/wscript_build b/src/fw/apps/demo/menu_right_icon/wscript_build new file mode 100644 index 0000000000..bf55f53bff --- /dev/null +++ b/src/fw/apps/demo/menu_right_icon/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['menu_layer_right_icon.c'], + target='demo_app_menu_right_icon', +) +bld.env.FW_APPS.append('demo_app_menu_right_icon') + +# vim:filetype=python diff --git a/src/fw/apps/demo/menu_round/Kconfig b/src/fw/apps/demo/menu_round/Kconfig new file mode 100644 index 0000000000..d487d9dc73 --- /dev/null +++ b/src/fw/apps/demo/menu_round/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MENU_ROUND + bool "Menu Round" diff --git a/src/fw/apps/demo/menu_round/menu_round.c b/src/fw/apps/demo/menu_round/menu_round.c new file mode 100644 index 0000000000..f97f7ff4de --- /dev/null +++ b/src/fw/apps/demo/menu_round/menu_round.c @@ -0,0 +1,348 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "menu_round.h" + +#include "applib/app.h" +#include "applib/graphics/gdraw_command_image.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" + +#include + +// Menu Detail +////////////////// + +typedef enum { + MenuLayerStyleTitle = 0, + MenuLayerStyleTitleAndSubtitle, + MenuLayerStyleTitleAndIconOnRight, + MenuLayerStyleTitleAndSubtitleAndValue, + MenuLayerStyleTitleAndSubtitleAndIcon, +} MenuLayerStyle; + +typedef struct { + char *title; + char *subtitle; + char *value; +} MenuDetailRowData; + +typedef struct { + Window window; + MenuLayer menu_layer; + StatusBarLayer status_bar_layer; + MenuLayerStyle style; +} MenuDetailWindowData; + + +static const MenuDetailRowData menu_detail_row_data_notifications[] = { + {"Liron Damir", "Late again. Sorry, I'll be on time in the future.", NULL}, + {"Angela Tam", "Late again? Can you be on time for once?", NULL}, + {"Eric Migicovsky", "Friday meeting will be held in the big room.", NULL}, + {"Intagram", "Keep scrolling down.", NULL}, + {"Liron Levak", "That's not my name.", NULL}, + {"Kimberly North West Kardashian", "I broke the Internet again.", NULL}, + {"Henry Damir", "That's not my name.", NULL}, + {"Kevin Conley", "Wubalubadubdub!", NULL}, +}; + +static const MenuDetailRowData menu_detail_row_data_days[] = { + {"Monday", NULL, NULL}, + {"Tuesday", NULL, NULL}, + {"Wednesday", NULL, NULL}, + {"Thursday", NULL, NULL}, + {"Friday", NULL, NULL}, + {"Saturday", NULL, NULL}, + {"Sunday", NULL, NULL}, +}; + +static const MenuDetailRowData menu_detail_row_data_alarms[] = { + {"8:00 AM", "Workdays", "ON"}, + {"10:00 AM", "Sat, Sun, Mon", "OFF"}, + {"11:30 AM", "Weekends", "ON"}, + {"5:00 PM", "Weekdays", "ON"}, +}; + +typedef struct { + const MenuDetailRowData *rows; + uint16_t num_rows; + int16_t selected_cell_height; + int16_t unselected_cell_height; + GColor highlight_background_color; +} MenuDetailInfo; + +static MenuDetailInfo prv_get_row_details_for_style(MenuLayerStyle style) { + switch (style) { + case MenuLayerStyleTitle: + return (MenuDetailInfo) { + .rows = menu_detail_row_data_notifications, + .num_rows = ARRAY_LENGTH(menu_detail_row_data_notifications), + .selected_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT, + .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, + .highlight_background_color = GColorFolly, + }; + case MenuLayerStyleTitleAndSubtitle: + return (MenuDetailInfo) { + .rows = menu_detail_row_data_notifications, + .num_rows = ARRAY_LENGTH(menu_detail_row_data_notifications), + .selected_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT, + .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, + .highlight_background_color = GColorIslamicGreen, + }; + case MenuLayerStyleTitleAndSubtitleAndIcon: + return (MenuDetailInfo) { + .rows = menu_detail_row_data_notifications, + .num_rows = ARRAY_LENGTH(menu_detail_row_data_notifications), + .selected_cell_height = MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT, + .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT, + .highlight_background_color = GColorFolly, + }; + case MenuLayerStyleTitleAndIconOnRight: + return (MenuDetailInfo) { + .rows = menu_detail_row_data_days, + .num_rows = ARRAY_LENGTH(menu_detail_row_data_days), + .selected_cell_height = menu_cell_basic_cell_height(), + .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, + .highlight_background_color = GColorIslamicGreen, + }; + case MenuLayerStyleTitleAndSubtitleAndValue: + return (MenuDetailInfo) { + .rows = menu_detail_row_data_alarms, + .num_rows = ARRAY_LENGTH(menu_detail_row_data_alarms), + .selected_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT, + .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, + .highlight_background_color = GColorIslamicGreen, + }; + default: + WTF; + } +} + +static int16_t prv_get_cell_height_for_menu_layer(MenuLayer *menu_layer, MenuIndex *cell_index, + MenuLayerStyle style) { + const MenuDetailInfo row_details = prv_get_row_details_for_style(style); + return menu_layer_is_index_selected(menu_layer, cell_index) ? + row_details.selected_cell_height : + row_details.unselected_cell_height; +} + +static int16_t prv_menu_detail_get_cell_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, + void *context) { + MenuDetailWindowData *data = context; + return prv_get_cell_height_for_menu_layer(menu_layer, cell_index, data->style); +} +static uint16_t prv_menu_detail_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, + void *context) { + MenuDetailWindowData *data = context; + return prv_get_row_details_for_style(data->style).num_rows; +} + +static void prv_menu_detail_draw_row(GContext *ctx, const Layer *cell_layer, + MenuDetailRowData *row_data, MenuLayerStyle style) { + const GFont title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + switch (style) { + case MenuLayerStyleTitle: { + menu_cell_basic_draw_custom(ctx, cell_layer, title_font, row_data->title, title_font, NULL, + NULL, NULL, NULL, false /* icon_on_right */, + GTextOverflowModeWordWrap); + break; + } + case MenuLayerStyleTitleAndSubtitle: { + char *subtitle = menu_cell_layer_is_highlighted(cell_layer) ? row_data->subtitle : NULL; + menu_cell_basic_draw(ctx, cell_layer, row_data->title, subtitle, NULL); + break; + } + case MenuLayerStyleTitleAndIconOnRight: { + GBitmap *radio_button = gbitmap_create_with_resource(RESOURCE_ID_CHECKED_RADIO_BUTTON); + menu_cell_basic_draw_icon_right(ctx, cell_layer, row_data->title, row_data->subtitle, + radio_button); + gbitmap_destroy(radio_button); + break; + } + case MenuLayerStyleTitleAndSubtitleAndValue: { + const GFont subtitle_font = fonts_get_system_font(FONT_KEY_GOTHIC_14); + menu_cell_basic_draw_custom(ctx, cell_layer, title_font, row_data->title, title_font, + row_data->value, subtitle_font, row_data->subtitle, NULL, false, + GTextOverflowModeFill); + break; + } + case MenuLayerStyleTitleAndSubtitleAndIcon: { + GBitmap *icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_MENU_ICON_TICTOC_WATCH); + menu_cell_basic_draw(ctx, cell_layer, row_data->title, row_data->subtitle, icon_bitmap); + gbitmap_destroy(icon_bitmap); + break; + } + default: + WTF; + } +} + +static void prv_menu_detail_draw_row_callback(GContext* ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + MenuDetailWindowData *data = context; + const MenuDetailInfo menu_info = prv_get_row_details_for_style(data->style); + MenuDetailRowData row_data = menu_info.rows[cell_index->row]; + prv_menu_detail_draw_row(ctx, cell_layer, &row_data, data->style); +} + +static void prv_detail_window_load(Window *window) { + MenuDetailWindowData *data = window_get_user_data(window); + + MenuLayer *menu_layer = &data->menu_layer; + const GRect menu_layer_frame = grect_inset_internal(window->layer.bounds, 0, + STATUS_BAR_LAYER_HEIGHT); + menu_layer_init(menu_layer, &menu_layer_frame); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_cell_height = prv_menu_detail_get_cell_height, + .get_num_rows = prv_menu_detail_get_num_rows_callback, + .draw_row = prv_menu_detail_draw_row_callback, + }); + menu_layer_set_click_config_onto_window(menu_layer, window); + menu_layer_set_selected_index(menu_layer, MenuIndex(0, 1), MenuRowAlignCenter, false); + const MenuDetailInfo menu_info = prv_get_row_details_for_style(data->style); + menu_layer_set_highlight_colors(menu_layer, menu_info.highlight_background_color, GColorWhite); + layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); + + StatusBarLayer *status_bar = &data->status_bar_layer; + status_bar_layer_init(status_bar); + status_bar_layer_set_colors(status_bar, GColorClear, GColorBlack); + layer_add_child(&window->layer, &status_bar->layer); +} + +static void prv_detail_window_unload(Window *window) { + MenuDetailWindowData *data = window_get_user_data(window); + menu_layer_deinit(&data->menu_layer); + app_free(data); +} + +static void prv_push_detail_window(MenuLayerStyle menu_layer_style) { + MenuDetailWindowData *data = app_zalloc_check(sizeof(MenuDetailWindowData)); + data->style = menu_layer_style; + + Window *window = &data->window; + window_init(window, WINDOW_NAME("MenuLayer Round Demo Detail Menu")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_detail_window_load, + .unload = prv_detail_window_unload, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +// Menu Chooser +////////////////// + +typedef struct { + Window window; + MenuLayer menu_layer; + StatusBarLayer status_bar_layer; +} MenuChooserData; + +typedef struct { + char *title; + MenuLayerStyle style; +} MenuChooserRowData; + +static const MenuChooserRowData menu_chooser_row_data[] = { + {"Title Only", MenuLayerStyleTitle}, + {"Title & Subtitle", MenuLayerStyleTitleAndSubtitle}, + {"Title & Right Icon", MenuLayerStyleTitleAndIconOnRight}, + {"Title, Sub, Value", MenuLayerStyleTitleAndSubtitleAndValue}, + {"Title, Sub, Icon", MenuLayerStyleTitleAndSubtitleAndIcon}, +}; + +static int16_t prv_menu_chooser_get_cell_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, + void *context) { + return prv_get_cell_height_for_menu_layer(menu_layer, cell_index, MenuLayerStyleTitle); +} + +static uint16_t prv_menu_chooser_get_num_rows_callback(struct MenuLayer *menu_layer, + uint16_t section_index, + void *context) { + return ARRAY_LENGTH(menu_chooser_row_data); +} + +static void prv_menu_chooser_draw_row_callback(GContext* ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + MenuChooserRowData row_data = menu_chooser_row_data[cell_index->row]; + menu_cell_basic_draw(ctx, cell_layer, row_data.title, NULL, NULL); +} + +static void prv_menu_chooser_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, + void *context) { + prv_push_detail_window(menu_chooser_row_data[cell_index->row].style); +} + +static void prv_window_load(Window *window) { + MenuChooserData *data = window_get_user_data(window); + + MenuLayer *menu_layer = &data->menu_layer; + const GRect menu_layer_frame = grect_inset_internal(window->layer.bounds, 0, + STATUS_BAR_LAYER_HEIGHT); + menu_layer_init(menu_layer, &menu_layer_frame); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_cell_height = prv_menu_chooser_get_cell_height, + .get_num_rows = prv_menu_chooser_get_num_rows_callback, + .draw_row = prv_menu_chooser_draw_row_callback, + .select_click = prv_menu_chooser_select_callback, + }); + menu_layer_set_click_config_onto_window(menu_layer, window); + menu_layer_set_selected_index(menu_layer, MenuIndex(0, 1), MenuRowAlignCenter, false); + menu_layer_set_highlight_colors(menu_layer, GColorPictonBlue, GColorWhite); + layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); + + StatusBarLayer *status_bar = &data->status_bar_layer; + status_bar_layer_init(status_bar); + status_bar_layer_set_colors(status_bar, GColorClear, GColorBlack); + layer_add_child(&window->layer, &status_bar->layer); +} + +static void prv_window_unload(Window *window) { + MenuChooserData *data = window_get_user_data(window); + menu_layer_deinit(&data->menu_layer); +} + +// App boilerplate +//////////////////// + +static void prv_init(void) { + MenuChooserData *data = app_zalloc_check(sizeof(MenuChooserData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("MenuLayer Round Demo Chooser Menu")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +static void prv_deinit(void) { + MenuChooserData *data = app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} + +const PebbleProcessMd* menu_round_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "MenuLayer Round Demo" + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/menu_round/menu_round.h b/src/fw/apps/demo/menu_round/menu_round.h new file mode 100644 index 0000000000..1175d73c54 --- /dev/null +++ b/src/fw/apps/demo/menu_round/menu_round.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* menu_round_app_get_info(); diff --git a/src/fw/apps/demo/menu_round/wscript_build b/src/fw/apps/demo/menu_round/wscript_build new file mode 100644 index 0000000000..9aa3bb115f --- /dev/null +++ b/src/fw/apps/demo/menu_round/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['menu_round.c'], + target='demo_app_menu_round', +) +bld.env.FW_APPS.append('demo_app_menu_round') + +# vim:filetype=python diff --git a/src/fw/apps/demo/morph_square/Kconfig b/src/fw/apps/demo/morph_square/Kconfig new file mode 100644 index 0000000000..4b0cfaadbb --- /dev/null +++ b/src/fw/apps/demo/morph_square/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MORPH_SQUARE + bool "Morph Square" diff --git a/src/fw/apps/demo_apps/morph_square_demo.c b/src/fw/apps/demo/morph_square/morph_square_demo.c similarity index 84% rename from src/fw/apps/demo_apps/morph_square_demo.c rename to src/fw/apps/demo/morph_square/morph_square_demo.c index bafffa386d..19aa3a1c52 100644 --- a/src/fw/apps/demo_apps/morph_square_demo.c +++ b/src/fw/apps/demo/morph_square/morph_square_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "morph_square_demo.h" diff --git a/src/fw/apps/demo/morph_square/morph_square_demo.h b/src/fw/apps/demo/morph_square/morph_square_demo.h new file mode 100644 index 0000000000..86c3845d6f --- /dev/null +++ b/src/fw/apps/demo/morph_square/morph_square_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *morph_square_demo_get_info(void); diff --git a/src/fw/apps/demo/morph_square/wscript_build b/src/fw/apps/demo/morph_square/wscript_build new file mode 100644 index 0000000000..5881369fb5 --- /dev/null +++ b/src/fw/apps/demo/morph_square/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['morph_square_demo.c'], + target='demo_app_morph_square', +) +bld.env.FW_APPS.append('demo_app_morph_square') + +# vim:filetype=python diff --git a/src/fw/apps/demo/movable_line/Kconfig b/src/fw/apps/demo/movable_line/Kconfig new file mode 100644 index 0000000000..b4d0480b4c --- /dev/null +++ b/src/fw/apps/demo/movable_line/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MOVABLE_LINE + bool "Movable Line" diff --git a/src/fw/apps/demo_apps/movable_line.c b/src/fw/apps/demo/movable_line/movable_line.c similarity index 93% rename from src/fw/apps/demo_apps/movable_line.c rename to src/fw/apps/demo/movable_line/movable_line.c index 20c20d02a7..b29026b0a8 100644 --- a/src/fw/apps/demo_apps/movable_line.c +++ b/src/fw/apps/demo/movable_line/movable_line.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "movable_line.h" @@ -198,7 +185,7 @@ static void canvas_update_proc(Layer *layer, GContext* ctx) { data->pixel_bit == PIXEL_BIT_LSB, data->selection == ATTRIBUTE_PIXEL_BIT); - char text[6]; + char text[10]; snprintf(text, sizeof(text), "x=%"PRId16, data->intersection.x); draw_ui_element(ctx, GRect(30, 120, 40, 20), text, data->selection == ATTRIBUTE_X, diff --git a/src/fw/apps/demo/movable_line/movable_line.h b/src/fw/apps/demo/movable_line/movable_line.h new file mode 100644 index 0000000000..6a883789cd --- /dev/null +++ b/src/fw/apps/demo/movable_line/movable_line.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* movable_line_get_app_info(); diff --git a/src/fw/apps/demo/movable_line/wscript_build b/src/fw/apps/demo/movable_line/wscript_build new file mode 100644 index 0000000000..450d4524fe --- /dev/null +++ b/src/fw/apps/demo/movable_line/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['movable_line.c'], + target='demo_app_movable_line', +) +bld.env.FW_APPS.append('demo_app_movable_line') + +# vim:filetype=python diff --git a/src/fw/apps/demo/mpu_test/Kconfig b/src/fw/apps/demo/mpu_test/Kconfig new file mode 100644 index 0000000000..c27fbc204b --- /dev/null +++ b/src/fw/apps/demo/mpu_test/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MPU_TEST + bool "Mpu Test" diff --git a/src/fw/apps/demo_apps/test_mpu_cache.c b/src/fw/apps/demo/mpu_test/test_mpu_cache.c similarity index 82% rename from src/fw/apps/demo_apps/test_mpu_cache.c rename to src/fw/apps/demo/mpu_test/test_mpu_cache.c index 8ccbfa639b..897f83ef14 100644 --- a/src/fw/apps/demo_apps/test_mpu_cache.c +++ b/src/fw/apps/demo/mpu_test/test_mpu_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "test_mpu_cache.h" diff --git a/src/fw/apps/demo/mpu_test/test_mpu_cache.h b/src/fw/apps/demo/mpu_test/test_mpu_cache.h new file mode 100644 index 0000000000..b85f18fc66 --- /dev/null +++ b/src/fw/apps/demo/mpu_test/test_mpu_cache.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +#include +#include + +const PebbleProcessMd* test_mpu_cache_get_info(); diff --git a/src/fw/apps/demo/mpu_test/wscript_build b/src/fw/apps/demo/mpu_test/wscript_build new file mode 100644 index 0000000000..b76a102097 --- /dev/null +++ b/src/fw/apps/demo/mpu_test/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_mpu_cache.c'], + target='demo_app_mpu_test', +) +bld.env.FW_APPS.append('demo_app_mpu_test') + +# vim:filetype=python diff --git a/src/fw/apps/demo/mpu_violation_test/Kconfig b/src/fw/apps/demo/mpu_violation_test/Kconfig new file mode 100644 index 0000000000..64ab097c92 --- /dev/null +++ b/src/fw/apps/demo/mpu_violation_test/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_MPU_VIOLATION_TEST + bool "Mpu Violation Test" diff --git a/src/fw/apps/demo/mpu_violation_test/test_mpu_violation.c b/src/fw/apps/demo/mpu_violation_test/test_mpu_violation.c new file mode 100644 index 0000000000..8e2c96664e --- /dev/null +++ b/src/fw/apps/demo/mpu_violation_test/test_mpu_violation.c @@ -0,0 +1,315 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "test_mpu_violation.h" + +#include "applib/app.h" +#include "applib/app_timer.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/click.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "font_resource_keys.auto.h" +#include "kernel/pbl_malloc.h" +#include "kernel/memory_layout.h" +#include "process_state/app_state/app_state.h" + +#include + +// Demo app that deliberately runs a series of memory accesses that the +// MPU is supposed to deny for the unprivileged App task. Use up/down to +// cycle through tests; press select to run the highlighted test. The +// expected outcome for every test is a MemManage fault: the kernel +// kills the App task and the launcher reclaims the screen. If the app +// stays alive long enough to render "SURVIVED!" the MPU let the access +// through -- that's the regression signal. +// +// SimpleMenuLayer would have been a nicer UI but it touches kernel data +// not accessible to an unprivileged App task, so we use a plain Window +// + TextLayers and raw click handlers. +// +// Linker symbols used for region-specific probes. +extern const uint32_t __WORKER_RAM__[]; +extern const uint32_t __FLASH_start__[]; +extern const uint32_t __APP_RAM__[]; +extern const uint32_t __kernel_main_stack_start__[]; +#ifdef CONFIG_SOC_SF32LB52 +extern const uint32_t __ramfunc_start[]; +#endif + +#ifndef CONFIG_MPU_TYPE_ARMV8M +KERNEL_READONLY_DATA static uint32_t s_ro_bss_probe; +#endif + +typedef enum { + TestKind_WorkerRamWrite, + TestKind_WorkerRamRead, + TestKind_KernelRamWrite, + TestKind_KernelRamRead, +#ifdef CONFIG_SOC_SF32LB52 + TestKind_RamfuncWrite, + TestKind_RamfuncRead, +#endif +#ifndef CONFIG_MPU_TYPE_ARMV8M + TestKind_RoBssWrite, +#endif + TestKind_FlashWrite, +#ifndef CONFIG_MPU_TYPE_ARMV8M + TestKind_StackGuardWrite, +#endif + TestKind_StackOverflow, + TestKindCount, +} TestKind; + +static const char *const s_test_titles[TestKindCount] = { + [TestKind_WorkerRamWrite] = "Worker RAM W", + [TestKind_WorkerRamRead] = "Worker RAM R", + [TestKind_KernelRamWrite] = "Kernel RAM W", + [TestKind_KernelRamRead] = "Kernel RAM R", +#ifdef CONFIG_SOC_SF32LB52 + [TestKind_RamfuncWrite] = "Ramfunc W", + [TestKind_RamfuncRead] = "Ramfunc R", +#endif +#ifndef CONFIG_MPU_TYPE_ARMV8M + [TestKind_RoBssWrite] = "RO BSS W", +#endif + [TestKind_FlashWrite] = "Flash W", +#ifndef CONFIG_MPU_TYPE_ARMV8M + [TestKind_StackGuardWrite] = "Stack guard W", +#endif + [TestKind_StackOverflow] = "Stack overflow", +}; + +typedef struct { + Window window; + TextLayer header_text; + TextLayer selection_text; + TextLayer hint_text; + + char selection_buffer[40]; + + int selected_index; + bool test_running; +} AppData; + +// Deeply-recursing helper for the stack-overflow test. The non-trivial +// return value plus the volatile local prevents the compiler from +// turning this into a tail call (which would not consume stack). +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winfinite-recursion" +static uint32_t __attribute__((noinline)) prv_overflow_recurse(uint32_t depth) { + volatile uint8_t big_local[128]; + big_local[0] = (uint8_t)depth; + return prv_overflow_recurse(depth + 1) + big_local[0]; +} +#pragma GCC diagnostic pop + +static void prv_run_test(TestKind kind) { + switch (kind) { + case TestKind_WorkerRamWrite: { + volatile uint32_t *p = (volatile uint32_t *)__WORKER_RAM__; + *p = 0xDEADBEEF; + break; + } + case TestKind_WorkerRamRead: { + volatile uint32_t *p = (volatile uint32_t *)__WORKER_RAM__; + volatile uint32_t v = *p; + (void)v; + break; + } + case TestKind_KernelRamWrite: { + // Bottom of the kernel main stack -- well inside kernel RAM and + // not covered by any region the App task can see. + volatile uint32_t *p = (volatile uint32_t *)__kernel_main_stack_start__; + *p = 0xDEADBEEF; + break; + } + case TestKind_KernelRamRead: { + volatile uint32_t *p = (volatile uint32_t *)__kernel_main_stack_start__; + volatile uint32_t v = *p; + (void)v; + break; + } +#ifdef CONFIG_SOC_SF32LB52 + case TestKind_RamfuncWrite: { + volatile uint32_t *p = (volatile uint32_t *)__ramfunc_start; + *p = 0xDEADBEEF; + break; + } + case TestKind_RamfuncRead: { + volatile uint32_t *p = (volatile uint32_t *)__ramfunc_start; + volatile uint32_t v = *p; + (void)v; + break; + } +#endif +#ifndef CONFIG_MPU_TYPE_ARMV8M + case TestKind_RoBssWrite: { + // ARMv7-M maps this as priv RW + user RO, so unprivileged writes fault. + volatile uint32_t *p = &s_ro_bss_probe; + *p = 0xDEADBEEF; + break; + } +#endif + case TestKind_FlashWrite: { + volatile uint32_t *p = (volatile uint32_t *)__FLASH_start__; + *p = 0xDEADBEEF; + break; + } +#ifndef CONFIG_MPU_TYPE_ARMV8M + case TestKind_StackGuardWrite: { + // Direct store to the bottom 32 B of App RAM. On ARMv7-M the per-task + // stack-guard MPU region blocks this; ARMv8-M uses PSPLIM instead, so + // this scenario is omitted there. + volatile uint32_t *p = (volatile uint32_t *)__APP_RAM__; + *p = 0xDEADBEEF; + break; + } +#endif + case TestKind_StackOverflow: + // Eventually trips PSPLIM (ARMv8-M) or the MPU stack-guard region + // (ARMv7-M), since each call frame consumes ~128 B. + (void)prv_overflow_recurse(0); + break; + case TestKindCount: + break; + } +} + +static void prv_redraw_selection(AppData *data) { + snprintf(data->selection_buffer, sizeof(data->selection_buffer), + "[%d/%d]\n%s", data->selected_index + 1, TestKindCount, + s_test_titles[data->selected_index]); + text_layer_set_text(&data->selection_text, data->selection_buffer); + layer_mark_dirty(text_layer_get_layer(&data->selection_text)); +} + +static void prv_attempt(void *cb_data) { + AppData *data = cb_data; + prv_run_test((TestKind)data->selected_index); + + // Reaching this point means no fault. Surface that prominently -- + // the previous "TESTING..." text gets replaced so the survival is + // obvious in a screenshot. + text_layer_set_text(&data->selection_text, "SURVIVED!\n(MPU MISS)"); + layer_mark_dirty(text_layer_get_layer(&data->selection_text)); + data->test_running = false; +} + +static void prv_up_click(ClickRecognizerRef rec, void *ctx) { + (void)rec; + AppData *data = ctx; + if (data->test_running) { + return; + } + data->selected_index = + (data->selected_index + TestKindCount - 1) % TestKindCount; + prv_redraw_selection(data); +} + +static void prv_down_click(ClickRecognizerRef rec, void *ctx) { + (void)rec; + AppData *data = ctx; + if (data->test_running) { + return; + } + data->selected_index = (data->selected_index + 1) % TestKindCount; + prv_redraw_selection(data); +} + +static void prv_select_click(ClickRecognizerRef rec, void *ctx) { + (void)rec; + AppData *data = ctx; + if (data->test_running) { + return; + } + data->test_running = true; + text_layer_set_text(&data->selection_text, "TESTING..."); + layer_mark_dirty(text_layer_get_layer(&data->selection_text)); + // Wait a tick so the "TESTING..." frame is visible before the access. + app_timer_register(300, prv_attempt, data); +} + +static void prv_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_up_click); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click); +} + +static void prv_window_load(Window *window) { + AppData *data = window_get_user_data(window); + + GRect bounds; + layer_get_bounds(window_get_root_layer(window), &bounds); + + // Header at the top: "MPU violation". + GRect header_frame = bounds; + header_frame.size.h = 24; + text_layer_init(&data->header_text, &header_frame); + text_layer_set_font(&data->header_text, + fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_text_alignment(&data->header_text, GTextAlignmentCenter); + text_layer_set_text(&data->header_text, "MPU violation"); + layer_add_child(window_get_root_layer(window), + text_layer_get_layer(&data->header_text)); + + // Selection display in the middle. + GRect selection_frame = bounds; + selection_frame.origin.y = 32; + selection_frame.size.h = bounds.size.h - 64; + text_layer_init(&data->selection_text, &selection_frame); + text_layer_set_font(&data->selection_text, + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(&data->selection_text, GTextAlignmentCenter); + layer_add_child(window_get_root_layer(window), + text_layer_get_layer(&data->selection_text)); + + // Hint at the bottom: button mapping. + GRect hint_frame = bounds; + hint_frame.origin.y = bounds.size.h - 24; + hint_frame.size.h = 24; + text_layer_init(&data->hint_text, &hint_frame); + text_layer_set_font(&data->hint_text, + fonts_get_system_font(FONT_KEY_GOTHIC_14)); + text_layer_set_text_alignment(&data->hint_text, GTextAlignmentCenter); + text_layer_set_text(&data->hint_text, "Up/Dn cycle, Sel run"); + layer_add_child(window_get_root_layer(window), + text_layer_get_layer(&data->hint_text)); + + prv_redraw_selection(data); +} + +static void prv_handle_init(void) { + AppData *app_data = app_malloc_check(sizeof(AppData)); + app_data->selected_index = 0; + app_data->test_running = false; + app_state_set_user_data(app_data); + + window_init(&app_data->window, WINDOW_NAME("test_mpu_violation")); + window_set_user_data(&app_data->window, app_data); + window_set_window_handlers(&app_data->window, &(WindowHandlers) { + .load = prv_window_load, + }); + // Pass app_data as the click context so handlers don't have to reach + // for a static (which would live in kernel BSS and fault on write). + window_set_click_config_provider_with_context(&app_data->window, + prv_click_config, app_data); + + app_window_stack_push(&app_data->window, true /* animated */); +} + +static void prv_main(void) { + prv_handle_init(); + app_event_loop(); +} + +const PebbleProcessMd* test_mpu_violation_get_info(void) { + static const PebbleProcessMdSystem s_info = { + .common.main_func = prv_main, + // System apps default to privileged; this one must run unprivileged + // for the MPU to actually evaluate access against the App task regions. + .common.is_unprivileged = true, + .name = "MPU violation", + }; + return (const PebbleProcessMd *)&s_info; +} diff --git a/src/fw/apps/demo/mpu_violation_test/test_mpu_violation.h b/src/fw/apps/demo/mpu_violation_test/test_mpu_violation.h new file mode 100644 index 0000000000..0abafc64b1 --- /dev/null +++ b/src/fw/apps/demo/mpu_violation_test/test_mpu_violation.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* test_mpu_violation_get_info(void); diff --git a/src/fw/apps/demo/mpu_violation_test/wscript_build b/src/fw/apps/demo/mpu_violation_test/wscript_build new file mode 100644 index 0000000000..b283d573ec --- /dev/null +++ b/src/fw/apps/demo/mpu_violation_test/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_mpu_violation.c'], + target='demo_app_mpu_violation_test', +) +bld.env.FW_APPS.append('demo_app_mpu_violation_test') + +# vim:filetype=python diff --git a/src/fw/apps/demo/number_field/Kconfig b/src/fw/apps/demo/number_field/Kconfig new file mode 100644 index 0000000000..4dfc848088 --- /dev/null +++ b/src/fw/apps/demo/number_field/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_NUMBER_FIELD + bool "Number Field" diff --git a/src/fw/apps/demo/number_field/number_field.c b/src/fw/apps/demo/number_field/number_field.c new file mode 100644 index 0000000000..fa5ad63cff --- /dev/null +++ b/src/fw/apps/demo/number_field/number_field.c @@ -0,0 +1,63 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "number_field.h" + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" +#include "system/passert.h" + +typedef struct { + NumberWindow num; +} AppData; + +static void selected(NumberWindow *nw, void *ctx) { + PBL_LOG_DBG("selected: %"PRId32, number_window_get_value(nw)); + + const bool animated = true; + app_window_stack_pop(animated); + + (void)ctx; +} + +static void handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + number_window_init(&data->num, "Some Number", + (NumberWindowCallbacks) { .selected = selected }, + data); + + number_window_set_min(&data->num, 10); + number_window_set_max(&data->num, 100); + number_window_set_step_size(&data->num, 5); + + const bool animated = true; + app_window_stack_push(&data->num.window, animated); +} + +static void handle_deinit(void) { + AppData *data = app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* number_field_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "NumberField Demo" + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/demo/number_field/number_field.h b/src/fw/apps/demo/number_field/number_field.h new file mode 100644 index 0000000000..0dc50edcf3 --- /dev/null +++ b/src/fw/apps/demo/number_field/number_field.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* number_field_app_get_info(); + diff --git a/src/fw/apps/demo/number_field/wscript_build b/src/fw/apps/demo/number_field/wscript_build new file mode 100644 index 0000000000..11d4ce838a --- /dev/null +++ b/src/fw/apps/demo/number_field/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['number_field.c'], + target='demo_app_number_field', +) +bld.env.FW_APPS.append('demo_app_number_field') + +# vim:filetype=python diff --git a/src/fw/apps/demo/option_menu/Kconfig b/src/fw/apps/demo/option_menu/Kconfig new file mode 100644 index 0000000000..e529d2a654 --- /dev/null +++ b/src/fw/apps/demo/option_menu/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_OPTION_MENU + bool "Option Menu" diff --git a/src/fw/apps/demo_apps/option_menu_demo.c b/src/fw/apps/demo/option_menu/option_menu_demo.c similarity index 78% rename from src/fw/apps/demo_apps/option_menu_demo.c rename to src/fw/apps/demo/option_menu/option_menu_demo.c index 838c32ebd9..33c29253b4 100644 --- a/src/fw/apps/demo_apps/option_menu_demo.c +++ b/src/fw/apps/demo/option_menu/option_menu_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "option_menu_demo.h" @@ -34,7 +21,7 @@ const char *s_strings[] = { }; static void prv_menu_select(OptionMenu *option_menu, int selection, void *context) { - PBL_LOG(LOG_LEVEL_DEBUG, "Option Menu Demo: selected %d", selection); + PBL_LOG_DBG("Option Menu Demo: selected %d", selection); } static uint16_t prv_menu_get_num_rows(OptionMenu *option_menu, void *context) { diff --git a/src/fw/apps/demo/option_menu/option_menu_demo.h b/src/fw/apps/demo/option_menu/option_menu_demo.h new file mode 100644 index 0000000000..b08fb4926d --- /dev/null +++ b/src/fw/apps/demo/option_menu/option_menu_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *option_menu_demo_get_app_info(void); diff --git a/src/fw/apps/demo/option_menu/wscript_build b/src/fw/apps/demo/option_menu/wscript_build new file mode 100644 index 0000000000..20dd1bf1bc --- /dev/null +++ b/src/fw/apps/demo/option_menu/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['option_menu_demo.c'], + target='demo_app_option_menu', +) +bld.env.FW_APPS.append('demo_app_option_menu') + +# vim:filetype=python diff --git a/src/fw/apps/demo/pebble_colors/Kconfig b/src/fw/apps/demo/pebble_colors/Kconfig new file mode 100644 index 0000000000..1c7a5545d1 --- /dev/null +++ b/src/fw/apps/demo/pebble_colors/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_PEBBLE_COLORS + bool "Pebble Colors" diff --git a/src/fw/apps/demo_apps/pebble_colors.c b/src/fw/apps/demo/pebble_colors/pebble_colors.c similarity index 96% rename from src/fw/apps/demo_apps/pebble_colors.c rename to src/fw/apps/demo/pebble_colors/pebble_colors.c index f818e2daec..ae2caf08b1 100644 --- a/src/fw/apps/demo_apps/pebble_colors.c +++ b/src/fw/apps/demo/pebble_colors/pebble_colors.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pebble_colors.h" @@ -34,7 +21,6 @@ #define ALPHA_66 0x80 #define ALPHA_100 0xC0 -static const int TARGET_FPS = 30; static Window *s_window; static Layer *s_canvas_layer; diff --git a/src/fw/apps/demo/pebble_colors/pebble_colors.h b/src/fw/apps/demo/pebble_colors/pebble_colors.h new file mode 100644 index 0000000000..58f48a7ab8 --- /dev/null +++ b/src/fw/apps/demo/pebble_colors/pebble_colors.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* pebble_colors_get_app_info(); diff --git a/src/fw/apps/demo/pebble_colors/wscript_build b/src/fw/apps/demo/pebble_colors/wscript_build new file mode 100644 index 0000000000..09b545a802 --- /dev/null +++ b/src/fw/apps/demo/pebble_colors/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['pebble_colors.c'], + target='demo_app_pebble_colors', +) +bld.env.FW_APPS.append('demo_app_pebble_colors') + +# vim:filetype=python diff --git a/src/fw/apps/demo/pebble_shapes/Kconfig b/src/fw/apps/demo/pebble_shapes/Kconfig new file mode 100644 index 0000000000..5c1e7d20ba --- /dev/null +++ b/src/fw/apps/demo/pebble_shapes/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_PEBBLE_SHAPES + bool "Pebble Shapes" diff --git a/src/fw/apps/demo_apps/pebble_shapes.c b/src/fw/apps/demo/pebble_shapes/pebble_shapes.c similarity index 92% rename from src/fw/apps/demo_apps/pebble_shapes.c rename to src/fw/apps/demo/pebble_shapes/pebble_shapes.c index 7596a0f295..58626b3c1d 100644 --- a/src/fw/apps/demo_apps/pebble_shapes.c +++ b/src/fw/apps/demo/pebble_shapes/pebble_shapes.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pebble_shapes.h" @@ -151,29 +138,29 @@ typedef struct AppData { static void log_state(AppData *data) { switch (data->state_index) { case APP_STATE_FILL_NON_AA: - PBL_LOG(LOG_LEVEL_DEBUG, "State: Fill Non-Antialiased; SW: N/A (but currently: %d)", + PBL_LOG_DBG("State: Fill Non-Antialiased; SW: N/A (but currently: %d)", data->stroke_width); break; case APP_STATE_FILL_AA: - PBL_LOG(LOG_LEVEL_DEBUG, "State: Fill Antialiased; SW: N/A (but currently: %d)", + PBL_LOG_DBG("State: Fill Antialiased; SW: N/A (but currently: %d)", data->stroke_width); break; case APP_STATE_DRAW_NON_AA_NO_SW: - PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Non-Antialiased; SW: N/A (but currently: %d)", + PBL_LOG_DBG("State: Draw Non-Antialiased; SW: N/A (but currently: %d)", data->stroke_width); break; case APP_STATE_DRAW_AA_NO_SW: - PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Antialiased; SW: N/A (but currently: %d)", + PBL_LOG_DBG("State: Draw Antialiased; SW: N/A (but currently: %d)", data->stroke_width); break; case APP_STATE_DRAW_NON_AA_SW: - PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Non-Antialiased; SW: %d", data->stroke_width); + PBL_LOG_DBG("State: Draw Non-Antialiased; SW: %d", data->stroke_width); break; case APP_STATE_DRAW_AA_SW: - PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Antialiased; SW: %d", data->stroke_width); + PBL_LOG_DBG("State: Draw Antialiased; SW: %d", data->stroke_width); break; default: - PBL_LOG(LOG_LEVEL_DEBUG, "Unknown State"); + PBL_LOG_DBG("Unknown State"); break; } } @@ -423,8 +410,8 @@ static void layer_update_proc(Layer *layer, GContext* ctx) { } else { int64_t time_rendered = prv_time_64() - data->time_started; if ((data->rendered_frames % 64) == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "## %d frames rendered", (int)data->rendered_frames); - PBL_LOG(LOG_LEVEL_DEBUG, "## at %"PRIu32" FPS", + PBL_LOG_DBG("## %d frames rendered", (int)data->rendered_frames); + PBL_LOG_DBG("## at %"PRIu32" FPS", (uint32_t)((uint64_t)data->rendered_frames*1000/time_rendered)); } } diff --git a/src/fw/apps/demo/pebble_shapes/pebble_shapes.h b/src/fw/apps/demo/pebble_shapes/pebble_shapes.h new file mode 100644 index 0000000000..28a8b48edb --- /dev/null +++ b/src/fw/apps/demo/pebble_shapes/pebble_shapes.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* pebble_shapes_get_app_info(); diff --git a/src/fw/apps/demo/pebble_shapes/wscript_build b/src/fw/apps/demo/pebble_shapes/wscript_build new file mode 100644 index 0000000000..77ce489fa9 --- /dev/null +++ b/src/fw/apps/demo/pebble_shapes/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['pebble_shapes.c'], + target='demo_app_pebble_shapes', +) +bld.env.FW_APPS.append('demo_app_pebble_shapes') + +# vim:filetype=python diff --git a/src/fw/apps/demo/persist/Kconfig b/src/fw/apps/demo/persist/Kconfig new file mode 100644 index 0000000000..4fb32ccc25 --- /dev/null +++ b/src/fw/apps/demo/persist/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_PERSIST + bool "Persist" diff --git a/src/fw/apps/demo/persist/persist.c b/src/fw/apps/demo/persist/persist.c new file mode 100644 index 0000000000..64b536772c --- /dev/null +++ b/src/fw/apps/demo/persist/persist.c @@ -0,0 +1,202 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "persist.h" + +#include "applib/app.h" +#include "process_state/app_state/app_state.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" + +#include "applib/persist.h" + +#include + +#define BUFFER_SIZE 25 + +static const uint8_t s_music_launcher_icon_pixels[] = { + 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, /* bytes 0 - 16 */ + 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x18, 0x00, 0x7f, 0x00, 0x1f, 0x00, /* bytes 16 - 32 */ + 0x7f, 0xf0, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 32 - 48 */ + 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 48 - 64 */ + 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x7f, 0x7c, 0x00, 0x00, /* bytes 64 - 80 */ + 0x03, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x80, 0x00, 0x00, 0x3c, 0xc0, 0x00, /* bytes 80 - 96 */ + 0x00, 0x7e, 0xe0, 0x00, 0x00, 0xff, 0xff, 0x00, 0x81, 0xff, 0xff, 0x00, +}; + +static const GBitmap s_music_launcher_icon_bitmap = { + .addr = (void*) &s_music_launcher_icon_pixels, + .row_size_bytes = 4, + .info_flags = 0x1000, + .bounds = { + .origin = { .x = 0, .y = 0 }, + .size = { .w = 24, .h = 27 }, + }, +}; + +typedef struct { + Window window; + MenuLayer menu_layer; + GBitmap icon; + + Window detail_window; + TextLayer detail_text; + char detail_text_buffer[50]; +} AppData; + +static const uint32_t COUNT_PKEY = 1; + +static uint16_t get_num_sections_callback(struct MenuLayer *menu_layer, AppData *data) { + (void)data; + (void)menu_layer; + return 1; +} + +static uint16_t get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { + (void)data; + (void)menu_layer; + switch (section_index) { + default: + case 0: return 3; + } +} + +static int16_t get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { + (void)data; + (void)menu_layer; + (void)section_index; + return MENU_CELL_BASIC_HEADER_HEIGHT; +} + +static void draw_row_callback(GContext* ctx, Layer *cell_layer, MenuIndex *cell_index, AppData *data) { + (void)data; + switch (cell_index->row) { + case 0: { + int num_beers = persist_read_int(COUNT_PKEY); + char title[50]; + snprintf(title, sizeof(title), "%d Bottles", num_beers); + menu_cell_basic_draw(ctx, cell_layer, title, "of beer on the wall", (GBitmap*)&s_music_launcher_icon_bitmap); + break; + } + case 1: + menu_cell_title_draw(ctx, cell_layer, "Order More"); + break; + case 2: + menu_cell_title_draw(ctx, cell_layer, "Drink!"); + break; + } +} + +static void draw_header_callback(GContext* ctx, Layer *cell_layer, uint16_t section_index, AppData *data) { + (void)section_index; + (void)data; + menu_cell_basic_header_draw(ctx, cell_layer, "Beer Counter"); +} + +static void select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { + (void)menu_layer; + (void)data; + switch (cell_index->row) { + case 1: { + int num_beers = persist_read_int(COUNT_PKEY); + int status = persist_write_int(COUNT_PKEY, num_beers+1); + PBL_LOG_DBG("argh %d %d", num_beers, status); + menu_layer_reload_data(menu_layer); + break; + } + case 2: { + int num_beers = persist_read_int(COUNT_PKEY); + persist_write_int(COUNT_PKEY, num_beers-1); + menu_layer_reload_data(menu_layer); + break; + } + } +} + +static void select_long_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { + (void)menu_layer; + (void)data; + switch (cell_index->row) { + case 1: { + int num_beers = persist_read_int(COUNT_PKEY); + persist_write_int(COUNT_PKEY, num_beers + (500 + rand() % 500)); + menu_layer_reload_data(menu_layer); + break; + } + case 2: { + int num_beers = persist_read_int(COUNT_PKEY); + persist_write_int(COUNT_PKEY, num_beers - (500 + rand() % 500)); + menu_layer_reload_data(menu_layer); + } + } +} + +static void prv_window_load(Window *window) { + AppData *data = window_get_user_data(window); + + MenuLayer *menu_layer = &data->menu_layer; + menu_layer_init(menu_layer, &window->layer.bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_sections = (MenuLayerGetNumberOfSectionsCallback) get_num_sections_callback, + .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) get_num_rows_callback, + .get_header_height = (MenuLayerGetHeaderHeightCallback) get_header_height_callback, + .draw_row = (MenuLayerDrawRowCallback) draw_row_callback, + .draw_header = (MenuLayerDrawHeaderCallback) draw_header_callback, + .select_click = (MenuLayerSelectCallback) select_callback, + .select_long_click = (MenuLayerSelectCallback) select_long_callback, + }); + menu_layer_set_click_config_onto_window(menu_layer, window); + layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); +} + +static void push_window(AppData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Demo Menu")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate + +static void handle_init() { + AppData *data = (AppData *)app_zalloc_check(sizeof(AppData)); + app_state_set_user_data(data); + push_window(data); + + const int exist_result = persist_exists(COUNT_PKEY); + PBL_LOG_DBG("- exist_result %d", exist_result); + if (exist_result == false) { + PBL_LOG_DBG("- writing..."); + persist_write_int(COUNT_PKEY, 10); + } +} + +static void handle_deinit() { + AppData *data = app_state_get_user_data(); + menu_layer_deinit(&data->menu_layer); + app_free(data); +} + +static void s_main() { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* persist_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Persist Demo" + }; + return (const PebbleProcessMd*) &s_app_info; +} + +#undef BUFFER_SIZE diff --git a/src/fw/apps/demo/persist/persist.h b/src/fw/apps/demo/persist/persist.h new file mode 100644 index 0000000000..1c99324f12 --- /dev/null +++ b/src/fw/apps/demo/persist/persist.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* persist_app_get_info(); diff --git a/src/fw/apps/demo/persist/wscript_build b/src/fw/apps/demo/persist/wscript_build new file mode 100644 index 0000000000..6e7cb7ca50 --- /dev/null +++ b/src/fw/apps/demo/persist/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['persist.c'], + target='demo_app_persist', +) +bld.env.FW_APPS.append('demo_app_persist') + +# vim:filetype=python diff --git a/src/fw/apps/demo/profile_mutexes/Kconfig b/src/fw/apps/demo/profile_mutexes/Kconfig new file mode 100644 index 0000000000..7d9bb3d9e0 --- /dev/null +++ b/src/fw/apps/demo/profile_mutexes/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_PROFILE_MUTEXES + bool "Profile Mutexes" diff --git a/src/fw/apps/demo/profile_mutexes/profile_mutexes.c b/src/fw/apps/demo/profile_mutexes/profile_mutexes.c new file mode 100644 index 0000000000..ccb22471b2 --- /dev/null +++ b/src/fw/apps/demo/profile_mutexes/profile_mutexes.c @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "profile_mutexes.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" +#include "applib/ui/window_stack.h" + +#include "system/logging.h" +#include "os/mutex.h" +#include "system/profiler.h" + +static Window *window; +static PebbleMutex *s_mutex; +static PebbleRecursiveMutex *s_rmutex; + +static void profile_mutexes(void) { + PBL_LOG_DBG("INITIALIZING PROFILER FOR MUTEXES!"); + PROFILER_INIT; + PROFILER_START; + + s_mutex = mutex_create(); + for (int i=0; i < 10000; i++) { + mutex_lock(s_mutex); + mutex_unlock(s_mutex); + } + mutex_destroy(s_mutex); + s_mutex = NULL; + + s_rmutex = mutex_create_recursive(); + for (int i=0; i < 10000; i++) { + mutex_lock_recursive(s_rmutex); + } + for (int i=0; i < 10000; i++) { + mutex_unlock_recursive(s_rmutex); + } + mutex_destroy((PebbleMutex *)s_rmutex); + s_rmutex = NULL; + + PROFILER_STOP; + PROFILER_PRINT_STATS; +} + +static void s_main(void) { + window = window_create(); + app_window_stack_push(window, true /* Animated */); + + profile_mutexes(); + + app_event_loop(); +} + +const PebbleProcessMd* profile_mutexes_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Profile Mutexes" + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/profile_mutexes/wscript_build b/src/fw/apps/demo/profile_mutexes/wscript_build new file mode 100644 index 0000000000..4dae909207 --- /dev/null +++ b/src/fw/apps/demo/profile_mutexes/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.', '../shared'], + source=['profile_mutexes.c'], + target='demo_app_profile_mutexes', +) +bld.env.FW_APPS.append('demo_app_profile_mutexes') + +# vim:filetype=python diff --git a/src/fw/apps/demo/progress/Kconfig b/src/fw/apps/demo/progress/Kconfig new file mode 100644 index 0000000000..9eaaf65ff2 --- /dev/null +++ b/src/fw/apps/demo/progress/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_PROGRESS + bool "Progress" diff --git a/src/fw/apps/demo/progress/progress.c b/src/fw/apps/demo/progress/progress.c new file mode 100644 index 0000000000..5c5b4921bc --- /dev/null +++ b/src/fw/apps/demo/progress/progress.c @@ -0,0 +1,105 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "progress.h" + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" +#include "util/math.h" + +#define PROGRESS_STEP 2 + +typedef struct { + Window window; + ProgressLayer progress_layer; + int progress; +} ProgressAppData; + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + // Reset + ProgressAppData *data = context; + data->progress = MIN_PROGRESS_PERCENT; + progress_layer_set_progress(&data->progress_layer, data->progress); +} + +static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) { + // Increment + ProgressAppData *data = context; + data->progress = MIN(MAX_PROGRESS_PERCENT, data->progress + PROGRESS_STEP); + progress_layer_set_progress(&data->progress_layer, data->progress); +} + +static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) { + // Decrement + ProgressAppData *data = context; + data->progress = MAX(MIN_PROGRESS_PERCENT, data->progress - PROGRESS_STEP); + progress_layer_set_progress(&data->progress_layer, data->progress); +} + +static void prv_click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_UP, 200, prv_up_click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 200, prv_down_click_handler); +} + +static void prv_window_load(Window *window) { + ProgressAppData *data = window_get_user_data(window); + data->progress = (MIN_PROGRESS_PERCENT + MAX_PROGRESS_PERCENT) / 2; + + ProgressLayer* progress_layer = &data->progress_layer; + + const GRect *frame = &window_get_root_layer(window)->frame; + + static const uint32_t MARGIN = 20; + static const uint32_t HEIGHT = PBL_IF_COLOR_ELSE(6, 7); + const GRect progress_bounds = GRect(MARGIN, (frame->size.h - HEIGHT) / 2, + frame->size.w - (2 * MARGIN), HEIGHT); + progress_layer_init(progress_layer, &progress_bounds); + progress_layer_set_progress(progress_layer, data->progress); + layer_add_child(&window->layer, &progress_layer->layer); + + progress_layer_set_corner_radius(progress_layer, PBL_IF_COLOR_ELSE(2, 3)); +} + +static void handle_init(void) { + ProgressAppData *data = app_zalloc_check(sizeof(ProgressAppData)); + app_state_set_user_data(data); + + Window* window = &data->window; + window_init(window, WINDOW_NAME("Progress Demo")); + window_set_user_data(window, data); + window_set_click_config_provider_with_context(window, prv_click_config_provider, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +static void handle_deinit(void) { + struct AppState* data = app_state_get_user_data(); + tick_timer_service_unsubscribe(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* progress_app_get_info() { + static const PebbleProcessMdSystem progress_app_info = { + .common.main_func = &s_main, + .name = "Progress Bar Test" + }; + return (const PebbleProcessMd*) &progress_app_info; +} + diff --git a/src/fw/apps/demo/progress/wscript_build b/src/fw/apps/demo/progress/wscript_build new file mode 100644 index 0000000000..51e01434c7 --- /dev/null +++ b/src/fw/apps/demo/progress/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.', '../shared'], + source=['progress.c'], + target='demo_app_progress', +) +bld.env.FW_APPS.append('demo_app_progress') + +# vim:filetype=python diff --git a/src/fw/apps/demo/scroll/Kconfig b/src/fw/apps/demo/scroll/Kconfig new file mode 100644 index 0000000000..0983fcdb13 --- /dev/null +++ b/src/fw/apps/demo/scroll/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_SCROLL + bool "Scroll" diff --git a/src/fw/apps/demo/scroll/scroll.c b/src/fw/apps/demo/scroll/scroll.c new file mode 100644 index 0000000000..4e482f1f4f --- /dev/null +++ b/src/fw/apps/demo/scroll/scroll.c @@ -0,0 +1,117 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "scroll.h" + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" + +typedef struct { + Window window; + ScrollLayer scroll_layer; + TextLayer text; + InverterLayer inverter; +} ScrollAppData; + +static void select_click_handler(ClickRecognizerRef recognizer, ScrollAppData *data) { + PBL_LOG_DBG("SELECT clicked!"); + (void)data; + (void)recognizer; +} + +#if 0 +static void select_long_click_handler(ClickRecognizerRef recognizer, ScrollAppData *data) { + PBL_LOG_DBG("SELECT long clicked!"); + (void)data; + (void)recognizer; +} +#endif + +static void click_config_provider(ScrollAppData *data) { + // The config that gets passed in, has already the UP and DOWN buttons configured + // to scroll up and down. It's possible to override that here, if needed. + + // Configure how the SELECT button should behave: + window_single_click_subscribe(BUTTON_ID_SELECT, (ClickHandler) select_click_handler); + window_long_click_subscribe(BUTTON_ID_SELECT, 0, (ClickHandler) select_click_handler, NULL); +} + +static void prv_window_load(Window *window) { + ScrollAppData *data = window_get_user_data(window); + + ScrollLayer *scroll_layer = &data->scroll_layer; + scroll_layer_init(scroll_layer, &window->layer.bounds); + scroll_layer_set_click_config_onto_window(scroll_layer, window); + scroll_layer_set_callbacks(scroll_layer, (ScrollLayerCallbacks) { + .click_config_provider = (ClickConfigProvider) click_config_provider, + }); + scroll_layer_set_context(scroll_layer, data); + scroll_layer_set_content_size(scroll_layer, GSize(window->layer.bounds.size.w, 500)); + + const GRect max_text_bounds = GRect(0, 0, window->layer.bounds.size.w, 500); + TextLayer *text = &data->text; + text_layer_init(text, &max_text_bounds); + text_layer_set_text(text, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam quam tellus, fermentum quis vulputate quis, vestibulum interdum sapien. Vestibulum lobortis pellentesque pretium. Quisque ultricies purus eu orci convallis lacinia. Cras a urna mi. Donec convallis ante id dui dapibus nec ullamcorper erat egestas. Aenean a mauris a sapien commodo lacinia. Sed posuere mi vel risus congue ornare. Curabitur leo nisi, euismod ut pellentesque sed, suscipit sit amet lorem. Aliquam eget sem vitae sem aliquam ornare. In sem sapien, imperdiet eget pharetra a, lacinia ac justo. Suspendisse at ante nec felis facilisis eleifend."); + + // Trim text layer and scroll content to fit text box + GSize max_size = text_layer_get_content_size(app_state_get_graphics_context(), text); + text_layer_set_size(text, max_size); + static const int vert_scroll_padding = 4; + scroll_layer_set_content_size(scroll_layer, GSize(window->layer.bounds.size.w, + max_size.h + vert_scroll_padding)); + + scroll_layer_add_child(scroll_layer, &text->layer); + + InverterLayer *inverter = &data->inverter; + inverter_layer_init(inverter, &GRect(15, 15, 30, 30)); + scroll_layer_add_child(scroll_layer, &inverter->layer); + + layer_add_child(&window->layer, &scroll_layer->layer); +} + +static void push_window(ScrollAppData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Scroll Demo")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate + +static void handle_init(void) { + ScrollAppData *data = app_malloc_check(sizeof(ScrollAppData)); + + app_state_set_user_data(data); + push_window(data); +} + +static void handle_deinit(void) { + ScrollAppData *data = app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* scroll_app_get_info() { + static const PebbleProcessMdSystem s_scroll_app_info = { + .common.main_func = &s_main, + .name = "Scroller" + }; + return (const PebbleProcessMd*) &s_scroll_app_info; +} + diff --git a/src/fw/apps/demo/scroll/scroll.h b/src/fw/apps/demo/scroll/scroll.h new file mode 100644 index 0000000000..9424831267 --- /dev/null +++ b/src/fw/apps/demo/scroll/scroll.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* scroll_app_get_info(); diff --git a/src/fw/apps/demo/scroll/wscript_build b/src/fw/apps/demo/scroll/wscript_build new file mode 100644 index 0000000000..6a15a4db66 --- /dev/null +++ b/src/fw/apps/demo/scroll/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['scroll.c'], + target='demo_app_scroll', +) +bld.env.FW_APPS.append('demo_app_scroll') + +# vim:filetype=python diff --git a/src/fw/apps/demo/shared/profile_mutexes.h b/src/fw/apps/demo/shared/profile_mutexes.h new file mode 100644 index 0000000000..2fecbdc87f --- /dev/null +++ b/src/fw/apps/demo/shared/profile_mutexes.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* profile_mutexes_get_app_info(void); diff --git a/src/fw/apps/demo/shared/progress.h b/src/fw/apps/demo/shared/progress.h new file mode 100644 index 0000000000..23b4f90d97 --- /dev/null +++ b/src/fw/apps/demo/shared/progress.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* progress_app_get_info(); diff --git a/src/fw/apps/demo/shared/test_args_rx.c b/src/fw/apps/demo/shared/test_args_rx.c new file mode 100644 index 0000000000..9d12b54e80 --- /dev/null +++ b/src/fw/apps/demo/shared/test_args_rx.c @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "test_args_rx.h" +#include "test_args_tx.h" + +#include "applib/app.h" +#include "kernel/event_loop.h" +#include "process_management/app_manager.h" +#include "process_management/process_manager.h" +#include "system/logging.h" + +static void s_main(void) { + const TestArgsData *args = process_manager_get_current_process_args(); + if (args == NULL) { + PBL_LOG_DBG("Got no args."); + } else { + PBL_LOG_DBG("Got argument 0x%x", args->data); + } +} + +const PebbleProcessMd* test_args_receiver_get_app_info() { + static const PebbleProcessMdSystem test_args_receiver_demo_app_info = { + .common.uuid = {0x48, 0xBB, 0xB5, 0x04, 0x5A, 0x56, 0x40, 0x73, 0xAF, + 0xEA, 0x5D, 0x83, 0x8D, 0x43, 0x01, 0xA4}, + .common.main_func = s_main, + .name = "Args Receiver Demo" + }; + return (const PebbleProcessMd*) &test_args_receiver_demo_app_info; +} diff --git a/src/fw/apps/demo/shared/test_args_rx.h b/src/fw/apps/demo/shared/test_args_rx.h new file mode 100644 index 0000000000..ebe082a90d --- /dev/null +++ b/src/fw/apps/demo/shared/test_args_rx.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* test_args_receiver_get_app_info(); diff --git a/src/fw/apps/demo/shared/test_args_tx.h b/src/fw/apps/demo/shared/test_args_tx.h new file mode 100644 index 0000000000..7877116661 --- /dev/null +++ b/src/fw/apps/demo/shared/test_args_tx.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +typedef struct { + uint8_t data; +} TestArgsData; + +const PebbleProcessMd* test_args_sender_get_app_info(); diff --git a/src/fw/apps/demo/shared/text_layout.h b/src/fw/apps/demo/shared/text_layout.h new file mode 100644 index 0000000000..440134b86b --- /dev/null +++ b/src/fw/apps/demo/shared/text_layout.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* text_layout_get_info(); diff --git a/src/fw/apps/demo/shared/trigger_alarm.h b/src/fw/apps/demo/shared/trigger_alarm.h new file mode 100644 index 0000000000..645cbd6e62 --- /dev/null +++ b/src/fw/apps/demo/shared/trigger_alarm.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* trigger_alarm_get_app_info(); diff --git a/src/fw/apps/demo/shared/wscript_build b/src/fw/apps/demo/shared/wscript_build new file mode 100644 index 0000000000..3373eda6d6 --- /dev/null +++ b/src/fw/apps/demo/shared/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_args_rx.c'], + target='demo_app_shared', +) +bld.env.FW_APPS.append('demo_app_shared') + +# vim:filetype=python diff --git a/src/fw/apps/demo/simple_menu/Kconfig b/src/fw/apps/demo/simple_menu/Kconfig new file mode 100644 index 0000000000..a71d74eb2f --- /dev/null +++ b/src/fw/apps/demo/simple_menu/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_SIMPLE_MENU + bool "Simple Menu" diff --git a/src/fw/apps/demo/simple_menu/simple_menu.c b/src/fw/apps/demo/simple_menu/simple_menu.c new file mode 100644 index 0000000000..446f955890 --- /dev/null +++ b/src/fw/apps/demo/simple_menu/simple_menu.c @@ -0,0 +1,90 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "simple_menu.h" + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/poll_remote.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" + +typedef struct { + Window window; + SimpleMenuLayer menu; +} AppData; + +static void callback_a(int index, void *ctx) { + (void)index; + (void)ctx; + PBL_LOG_DBG("A called back"); +} + +static void other_callback(int index, void *ctx) { + (void)ctx; + PBL_LOG_DBG("other callback: %d", index); +} + +static void poll_callback(int index, void *ctx) { + poll_remote_send_request(POLL_REMOTE_SERVICE_MAIL); +} + +static const SimpleMenuItem s_menu_items[] = { + { "Poll Mail", "", NULL, poll_callback }, + { "Title A", "Callback A", NULL, callback_a }, + { "Another Title", NULL, NULL, other_callback }, + { "Last Title", "Last subtitle", NULL, other_callback } +}; + +static const SimpleMenuSection s_menu_sections[] = {{ .title = NULL, .items = s_menu_items, .num_items = ARRAY_LENGTH(s_menu_items) }}; + +static void prv_window_load(Window *window) { + AppData *data = window_get_user_data(window); + + SimpleMenuLayer *menu = &data->menu; + simple_menu_layer_init(menu, &(GRect){{0, 0}, window->layer.frame.size}, window, s_menu_sections, + ARRAY_LENGTH(s_menu_sections), data); + layer_add_child(&window->layer, simple_menu_layer_get_layer(menu)); +} + +static void handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("Simple Menu Demo")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load + }); + + const bool animated = true; + app_window_stack_push(window, animated); +} + +static void handle_deinit(void) { + AppData *data = app_state_get_user_data(); + simple_menu_layer_deinit(&data->menu); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* simple_menu_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "SimpleMenuLayer Demo" + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/demo/simple_menu/simple_menu.h b/src/fw/apps/demo/simple_menu/simple_menu.h new file mode 100644 index 0000000000..c024acf0c6 --- /dev/null +++ b/src/fw/apps/demo/simple_menu/simple_menu.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* simple_menu_app_get_info(); diff --git a/src/fw/apps/demo/simple_menu/wscript_build b/src/fw/apps/demo/simple_menu/wscript_build new file mode 100644 index 0000000000..5b5183af8d --- /dev/null +++ b/src/fw/apps/demo/simple_menu/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['simple_menu.c'], + target='demo_app_simple_menu', +) +bld.env.FW_APPS.append('demo_app_simple_menu') + +# vim:filetype=python diff --git a/src/fw/apps/demo/statusbar/Kconfig b/src/fw/apps/demo/statusbar/Kconfig new file mode 100644 index 0000000000..8762b58833 --- /dev/null +++ b/src/fw/apps/demo/statusbar/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_STATUSBAR + bool "Statusbar" diff --git a/src/fw/apps/demo_apps/statusbar_demo.c b/src/fw/apps/demo/statusbar/statusbar_demo.c similarity index 85% rename from src/fw/apps/demo_apps/statusbar_demo.c rename to src/fw/apps/demo/statusbar/statusbar_demo.c index e31406354b..795a2aa944 100644 --- a/src/fw/apps/demo_apps/statusbar_demo.c +++ b/src/fw/apps/demo/statusbar/statusbar_demo.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "statusbar_demo.h" #include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "applib/app.h" #include "applib/ui/ui.h" #include "util/size.h" diff --git a/src/fw/apps/demo/statusbar/statusbar_demo.h b/src/fw/apps/demo/statusbar/statusbar_demo.h new file mode 100644 index 0000000000..25f6fe2b91 --- /dev/null +++ b/src/fw/apps/demo/statusbar/statusbar_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* statusbar_demo_get_app_info(void); diff --git a/src/fw/apps/demo/statusbar/wscript_build b/src/fw/apps/demo/statusbar/wscript_build new file mode 100644 index 0000000000..2b76fd8ee5 --- /dev/null +++ b/src/fw/apps/demo/statusbar/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['statusbar_demo.c'], + target='demo_app_statusbar', +) +bld.env.FW_APPS.append('demo_app_statusbar') + +# vim:filetype=python diff --git a/src/fw/apps/demo/stroke_width/Kconfig b/src/fw/apps/demo/stroke_width/Kconfig new file mode 100644 index 0000000000..ea3fabb378 --- /dev/null +++ b/src/fw/apps/demo/stroke_width/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_STROKE_WIDTH + bool "Stroke Width" diff --git a/src/fw/apps/demo_apps/stroke_width.c b/src/fw/apps/demo/stroke_width/stroke_width.c similarity index 92% rename from src/fw/apps/demo_apps/stroke_width.c rename to src/fw/apps/demo/stroke_width/stroke_width.c index a9f28404d5..18ee1d7455 100644 --- a/src/fw/apps/demo_apps/stroke_width.c +++ b/src/fw/apps/demo/stroke_width/stroke_width.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stroke_width.h" @@ -102,13 +89,13 @@ static void up_handler(ClickRecognizerRef recognizer, void *context) { data->p2.y++; break; default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid operation type: %d", data->operation); + PBL_LOG_ERR("Invalid operation type: %d", data->operation); break; } layer_mark_dirty(data->canvas_layer); - PBL_LOG(LOG_LEVEL_DEBUG, "line(p1(%d, %d), p2(%d, %d), width=%d), angle=%d)", + PBL_LOG_DBG("line(p1(%d, %d), p2(%d, %d), width=%d), angle=%d)", data->p1.x, data->p1.y, data->p2.x, data->p2.y, data->stroke_width, (int)(data->rotation_angle * 360 / TRIG_MAX_ANGLE)); } @@ -145,13 +132,13 @@ static void down_handler(ClickRecognizerRef recognizer, void *context) { data->p2.y--; break; default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid operation type: %d", data->operation); + PBL_LOG_ERR("Invalid operation type: %d", data->operation); break; } layer_mark_dirty(data->canvas_layer); - PBL_LOG(LOG_LEVEL_DEBUG, "line(p1(%d, %d), p2(%d, %d), width=%d), angle=%d)", + PBL_LOG_DBG("line(p1(%d, %d), p2(%d, %d), width=%d), angle=%d)", data->p1.x, data->p1.y, data->p2.x, data->p2.y, data->stroke_width, (int)(data->rotation_angle * 360 / TRIG_MAX_ANGLE)); } @@ -165,7 +152,7 @@ static void select_handler(ClickRecognizerRef recognizer, void *context) { layer_mark_dirty(data->canvas_layer); - PBL_LOG(LOG_LEVEL_DEBUG, "current operation type: %d", data->operation); + PBL_LOG_DBG("current operation type: %d", data->operation); } static void click_config_provider(void *context) { diff --git a/src/fw/apps/demo/stroke_width/stroke_width.h b/src/fw/apps/demo/stroke_width/stroke_width.h new file mode 100644 index 0000000000..2aac7f8961 --- /dev/null +++ b/src/fw/apps/demo/stroke_width/stroke_width.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* stroke_width_get_app_info(); diff --git a/src/fw/apps/demo/stroke_width/wscript_build b/src/fw/apps/demo/stroke_width/wscript_build new file mode 100644 index 0000000000..07bf6f9452 --- /dev/null +++ b/src/fw/apps/demo/stroke_width/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['stroke_width.c'], + target='demo_app_stroke_width', +) +bld.env.FW_APPS.append('demo_app_stroke_width') + +# vim:filetype=python diff --git a/src/fw/apps/demo/swap_layer/Kconfig b/src/fw/apps/demo/swap_layer/Kconfig new file mode 100644 index 0000000000..9b9d99ce8e --- /dev/null +++ b/src/fw/apps/demo/swap_layer/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_SWAP_LAYER + bool "Swap Layer" diff --git a/src/fw/apps/demo_apps/swap_layer_demo.c b/src/fw/apps/demo/swap_layer/swap_layer_demo.c similarity index 90% rename from src/fw/apps/demo_apps/swap_layer_demo.c rename to src/fw/apps/demo/swap_layer/swap_layer_demo.c index a764ac2746..5dae2613f4 100644 --- a/src/fw/apps/demo_apps/swap_layer_demo.c +++ b/src/fw/apps/demo/swap_layer/swap_layer_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "swap_layer_demo.h" @@ -29,10 +16,10 @@ #include "popups/phone_ui.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/notification_layout.h" -#include "services/normal/timeline/swap_layer.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/notification_layout.h" +#include "pbl/services/timeline/swap_layer.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/normal/watchface.h" #include "system/logging.h" #include "system/passert.h" @@ -104,7 +91,7 @@ typedef struct SwapLayerDemoData { static LayoutLayer *prv_get_layout_handler(SwapLayer *swap_layer, int8_t rel_position, void *context) { - PBL_LOG(LOG_LEVEL_DEBUG, "getting layer %d", rel_position); + PBL_LOG_DBG("getting layer %d", rel_position); SwapLayerDemoData *data = context; @@ -263,9 +250,7 @@ static void handle_init(void) { .app_id = ¬ification->header.parent_id, .context = &(NotificationLayoutInfo) { .item = notification, -#if !PLATFORM_TINTIN .show_notification_timestamp = true, -#endif }, }; LayoutLayer *layout = layout_create(notification->header.layout, &config); diff --git a/src/fw/apps/demo/swap_layer/swap_layer_demo.h b/src/fw/apps/demo/swap_layer/swap_layer_demo.h new file mode 100644 index 0000000000..9f46f7e812 --- /dev/null +++ b/src/fw/apps/demo/swap_layer/swap_layer_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* swap_layer_demo_get_app_info(void); diff --git a/src/fw/apps/demo/swap_layer/wscript_build b/src/fw/apps/demo/swap_layer/wscript_build new file mode 100644 index 0000000000..1ba2fcfe9a --- /dev/null +++ b/src/fw/apps/demo/swap_layer/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['swap_layer_demo.c'], + target='demo_app_swap_layer', +) +bld.env.FW_APPS.append('demo_app_swap_layer') + +# vim:filetype=python diff --git a/src/fw/apps/demo/temperature_demo/Kconfig b/src/fw/apps/demo/temperature_demo/Kconfig new file mode 100644 index 0000000000..54ab6d985f --- /dev/null +++ b/src/fw/apps/demo/temperature_demo/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEMPERATURE_DEMO + bool "Temperature Demo" diff --git a/src/fw/apps/demo_apps/temperature_demo.c b/src/fw/apps/demo/temperature_demo/temperature_demo.c similarity index 91% rename from src/fw/apps/demo_apps/temperature_demo.c rename to src/fw/apps/demo/temperature_demo/temperature_demo.c index 112ba97904..bf21360f31 100644 --- a/src/fw/apps/demo_apps/temperature_demo.c +++ b/src/fw/apps/demo/temperature_demo/temperature_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app.h" #include "applib/app_logging.h" @@ -100,7 +87,7 @@ static void cur_temp_update_text(TemperatureDemoAppData *data) { // ------------------------------------------------------------------------------- static void handle_second_tick(struct tm* tick_time, TimeUnits units_changed) { int32_t reading = temperature_read(); - memcpy(s_temp_readings, s_temp_readings + 1, (READ_HISTORY_ENTRIES - 1) * sizeof(int32_t)); + memmove(s_temp_readings, s_temp_readings + 1, (READ_HISTORY_ENTRIES - 1) * sizeof(int32_t)); s_temp_readings[READ_HISTORY_ENTRIES - 1] = reading; cur_temp_update_text(s_data); } @@ -153,7 +140,7 @@ static void layer_update_proc(Layer *layer, GContext* ctx) { if (first_non_zero_idx < 0) { return; } - PBL_LOG(LOG_LEVEL_DEBUG, "min temp: %d, max temp: %d", data->min_temp, data->max_temp); + PBL_LOG_DBG("min temp: %d, max temp: %d", data->min_temp, data->max_temp); int temp_range = data->max_temp - data->min_temp; temp_range = MAX(10, temp_range); diff --git a/src/fw/apps/demo/temperature_demo/temperature_demo.h b/src/fw/apps/demo/temperature_demo/temperature_demo.h new file mode 100644 index 0000000000..ca3c52820f --- /dev/null +++ b/src/fw/apps/demo/temperature_demo/temperature_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* temperature_demo_get_app_info(void); diff --git a/src/fw/apps/demo/temperature_demo/wscript_build b/src/fw/apps/demo/temperature_demo/wscript_build new file mode 100644 index 0000000000..3d3614b543 --- /dev/null +++ b/src/fw/apps/demo/temperature_demo/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['temperature_demo.c'], + target='demo_app_temperature_demo', +) +bld.env.FW_APPS.append('demo_app_temperature_demo') + +# vim:filetype=python diff --git a/src/fw/apps/demo/test_args_receiver/Kconfig b/src/fw/apps/demo/test_args_receiver/Kconfig new file mode 100644 index 0000000000..72fdb4ba54 --- /dev/null +++ b/src/fw/apps/demo/test_args_receiver/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEST_ARGS_RECEIVER + bool "Test Args Receiver" diff --git a/src/fw/apps/demo/test_args_receiver/wscript_build b/src/fw/apps/demo/test_args_receiver/wscript_build new file mode 100644 index 0000000000..883b25c280 --- /dev/null +++ b/src/fw/apps/demo/test_args_receiver/wscript_build @@ -0,0 +1,7 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +# test_args_receiver_get_app_info() lives in the always-built shared demo library. +pass + +# vim:filetype=python diff --git a/src/fw/apps/demo/test_args_sender/Kconfig b/src/fw/apps/demo/test_args_sender/Kconfig new file mode 100644 index 0000000000..ccbddf102f --- /dev/null +++ b/src/fw/apps/demo/test_args_sender/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEST_ARGS_SENDER + bool "Test Args Sender" diff --git a/src/fw/apps/demo/test_args_sender/test_args_tx.c b/src/fw/apps/demo/test_args_sender/test_args_tx.c new file mode 100644 index 0000000000..c8997c6c06 --- /dev/null +++ b/src/fw/apps/demo/test_args_sender/test_args_tx.c @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "test_args_tx.h" +#include "test_args_rx.h" + +#include "applib/app.h" +#include "kernel/event_loop.h" +#include "process_management/app_manager.h" +#include "process_management/app_install_manager.h" +#include "process_management/process_manager.h" +#include "system/logging.h" + +static TestArgsData s_data; + +static void prv_launch_receiver_callback(void *data) { + AppInstallId id = app_install_get_id_for_uuid(&(test_args_receiver_get_app_info()->uuid)); + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = id, + .common.args = data, + }); +} + +static void s_main(void) { + s_data.data = 0x43; + PBL_LOG_DBG("Launching again with argument: 0x%x", s_data.data); + launcher_task_add_callback(prv_launch_receiver_callback, &s_data); +} + +const PebbleProcessMd* test_args_sender_get_app_info() { + static const PebbleProcessMdSystem test_args_sender_demo_app_info = { + .common.uuid = {0xD1, 0x7E, 0x41, 0xAF, 0x40, 0x5E, 0x40, 0x76, + 0x82, 0xB5, 0x97, 0x71, 0x70, 0x52, 0x66, 0xBA}, + .common.main_func = s_main, + .name = "Args Sender Demo" + }; + return (const PebbleProcessMd*) &test_args_sender_demo_app_info; +} diff --git a/src/fw/apps/demo/test_args_sender/wscript_build b/src/fw/apps/demo/test_args_sender/wscript_build new file mode 100644 index 0000000000..4b16ce97db --- /dev/null +++ b/src/fw/apps/demo/test_args_sender/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=[ +'.', +'../shared', +], + source=['test_args_tx.c'], + target='demo_app_test_args_sender', +) +bld.env.FW_APPS.append('demo_app_test_args_sender') + +# vim:filetype=python diff --git a/src/fw/apps/demo/test_bluetooth/Kconfig b/src/fw/apps/demo/test_bluetooth/Kconfig new file mode 100644 index 0000000000..6f483aa912 --- /dev/null +++ b/src/fw/apps/demo/test_bluetooth/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEST_BLUETOOTH + bool "Test Bluetooth" diff --git a/src/fw/apps/demo/test_bluetooth/test_bluetooth.c b/src/fw/apps/demo/test_bluetooth/test_bluetooth.c new file mode 100644 index 0000000000..8cfb8f85ff --- /dev/null +++ b/src/fw/apps/demo/test_bluetooth/test_bluetooth.c @@ -0,0 +1,163 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "process_management/pebble_process_md.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "system/passert.h" + +#define NUM_MENU_ITEMS 2 + +// ================================================================================= +// Application Data +typedef struct { + Window *window; + SimpleMenuLayer *menu_layer; + SimpleMenuSection menu_section; + SimpleMenuItem menu_items[NUM_MENU_ITEMS]; + +} TestBTAppData; + +static TestBTAppData *s_app_data = 0; + + +static volatile int s_pending_count = 0; +static volatile bool s_connected = false; + +// ================================================================================= +static void send_bluetooth(void* data) { + //uint8_t *buffer = (uint8_t *)"hello world"; + + CommSession *session = comm_session_get_system_session(); + if (!session) { + s_pending_count--; + s_connected = false; + return; + } + + PBL_LOG_INFO("sending data"); + comm_session_send_data(session, 2000, (uint8_t *)0x08000000, + comm_session_send_buffer_get_max_payload_length(session), + COMM_SESSION_DEFAULT_TIMEOUT); + s_pending_count--; +} + + +// ================================================================================= +// You can capture when the user selects a menu icon with a menu item select callback +static void menu_select_callback(int index, void *ctx) { + PBL_LOG_DBG("Hit menu item %d", index); + + // Here we just change the subtitle to a literal string + s_app_data->menu_items[index].subtitle = "You've hit select here!"; + + // Mark the layer to be updated + layer_mark_dirty(simple_menu_layer_get_layer(s_app_data->menu_layer)); + + // --------------------------------------------------------------------------- + // Run the appropriate test + if (index == 0) { + s_connected = true; + + // Flood bluetooth + while (s_connected) { + while (s_pending_count > 6 && s_connected) { + psleep(100); + } + s_pending_count++; + system_task_add_callback(send_bluetooth, NULL); + } + PBL_LOG_INFO("Bluetooth disconnected"); + + } else if (index == 1) { + PBL_LOG_DBG("Not implemented"); + + } +} + + +// ================================================================================= +static void prv_window_load(Window *window) { + + TestBTAppData *data = s_app_data; + + int i = 0; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "flood BT", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "Ad space available", + .callback = menu_select_callback, + }; + PBL_ASSERTN(i == NUM_MENU_ITEMS); + + // The menu sections + data->menu_section = (SimpleMenuSection) { + .num_items = NUM_MENU_ITEMS, + .items = data->menu_items, + }; + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, + NULL); + layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); +} + + +// ================================================================================= +// Deinitialize resources on window unload that were initialized on window load +static void prv_window_unload(Window *window) { + simple_menu_layer_destroy(s_app_data->menu_layer); +} + + +// ================================================================================= +static void handle_init(void) { + TestBTAppData *data = app_malloc_check(sizeof(TestBTAppData)); + memset(data, 0, sizeof(TestBTAppData)); + s_app_data = data; + + data->window = window_create(); + if (data->window == NULL) { + return; + } + window_init(data->window, ""); + window_set_window_handlers(data->window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + app_window_stack_push(data->window, true /*animated*/); +} + +static void handle_deinit(void) { + // Don't bother freeing anything, the OS should be re-initing the heap. +} + + +// ================================================================================= +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +// ================================================================================= +const PebbleProcessMd* test_bluetooth_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Bluetooth Test" + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/demo/test_bluetooth/test_bluetooth.h b/src/fw/apps/demo/test_bluetooth/test_bluetooth.h new file mode 100644 index 0000000000..d535bdb2dc --- /dev/null +++ b/src/fw/apps/demo/test_bluetooth/test_bluetooth.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* test_bluetooth_app_get_info(); diff --git a/src/fw/apps/demo/test_bluetooth/wscript_build b/src/fw/apps/demo/test_bluetooth/wscript_build new file mode 100644 index 0000000000..cefe57a180 --- /dev/null +++ b/src/fw/apps/demo/test_bluetooth/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_bluetooth.c'], + target='demo_app_test_bluetooth', +) +bld.env.FW_APPS.append('demo_app_test_bluetooth') + +# vim:filetype=python diff --git a/src/fw/apps/demo/test_core_dump/Kconfig b/src/fw/apps/demo/test_core_dump/Kconfig new file mode 100644 index 0000000000..a93edaa00a --- /dev/null +++ b/src/fw/apps/demo/test_core_dump/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEST_CORE_DUMP + bool "Test Core Dump" diff --git a/src/fw/apps/demo/test_core_dump/test_core_dump.c b/src/fw/apps/demo/test_core_dump/test_core_dump.c new file mode 100644 index 0000000000..47c34d5404 --- /dev/null +++ b/src/fw/apps/demo/test_core_dump/test_core_dump.c @@ -0,0 +1,315 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/core_dump.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "process_management/pebble_process_md.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "system/passert.h" + +#include "FreeRTOSConfig.h" + + +#define NUM_MENU_ITEMS 13 + +static bool s_call_core_dump_from_isr = false; + +// ================================================================================= +// Application Data +typedef struct { + Window *window; + SimpleMenuLayer *menu_layer; + SimpleMenuSection menu_section; + SimpleMenuItem menu_items[NUM_MENU_ITEMS]; +} TestTimersAppData; + +static TestTimersAppData *s_app_data = 0; + + +// ================================================================================= +static void stuck_timer_callback(void* data) +{ + PBL_LOG_DBG("STT: Entering infinite loop in timer callback"); + while (true) { + psleep(100); + } +} + +static void stuck_system_task_callback(void *data) { + PBL_LOG_DBG("Entering infinite loop in system task callback"); + while (true) { + psleep(100); + } +} + +// ================================================================================ +// We install this ISR which does an infinite loop to test that the core dump captures the right +// task information if we get it while stuck in an ISR +void OTG_FS_WKUP_IRQHandler(void) { + if (s_call_core_dump_from_isr) { + core_dump_reset(false /* don't force overwrite */); + } else { + dbgserial_putstr("Entering infinite loop in ISR"); + while (true) ; + } +} + +// ================================================================================= +// You can capture when the user selects a menu icon with a menu item select callback +static void menu_select_callback(int index, void *ctx) { + PBL_LOG_DBG("Selected menu item %d", index); + + // Here we just change the subtitle to a literal string + s_app_data->menu_items[index].subtitle = "You've hit select here!"; + + // Mark the layer to be updated + layer_mark_dirty(simple_menu_layer_get_layer(s_app_data->menu_layer)); + + + // --------------------------------------------------------------------------- + // Run the appropriate test + if (index == 0) { + + // CROAK + PBL_CROAK("CROAK"); + + } else if (index == 1) { + + // stuck timer callback + TimerID timer = new_timer_create(); + PBL_LOG_INFO("Entering infinite loop in Timer callback"); + bool success = new_timer_start(timer, 100, stuck_timer_callback, NULL, 0 /*flags*/); + PBL_ASSERTN(success); + + } else if (index == 2) { + + // call directly + core_dump_reset(false /* don't force overwrite */); + + } else if (index == 3) { + + // stuck app + PBL_LOG_INFO("Entering infinite loop in App Task"); + while (true) ; + + } else if (index == 4) { + + PBL_LOG_INFO("Entering infinite loop in FreeRTOS ISR"); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn; + // Lower values are higher priority - make this same or lower priority than a FreeRTOS ISR + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4); + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); + + // Trigger it. This transfers control to our ISR handler OTG_FS_WKUP_IRQHandler + NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); + + } else if (index == 5) { + + PBL_LOG_INFO("Entering infinite loop in non-FreeRTOS ISR."); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn; + // Lower values are higher priority - make this higher priority than a FreeRTOS ISR + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4) - 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); + + // Trigger it. This transfers control to our ISR handler OTG_FS_WKUP_IRQHandler + NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); + + } else if (index == 6) { + + PBL_LOG_INFO("Forcing bus fault during core dump"); + core_dump_test_force_bus_fault(); + core_dump_reset(false /* don't force overwrite */); + + } else if (index == 7) { + + PBL_LOG_INFO("Forcing inf loop during core dump"); + core_dump_test_force_inf_loop(); + core_dump_reset(false /* don't force overwrite */); + + } else if (index == 8) { + + PBL_LOG_INFO("Forcing assert loop during core dump"); + core_dump_test_force_assert(); + core_dump_reset(false /* don't force overwrite */); + + } else if (index == 9) { + + PBL_LOG_INFO("Calling core_dump FreeRTOS ISR"); + s_call_core_dump_from_isr = true; + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn; + // Lower values are higher priority - make this same or lower priority than a FreeRTOS ISR + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4); + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); + + // Trigger it. This transfers control to our ISR handler OTG_FS_WKUP_IRQHandler + NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); + + } else if (index == 10) { + + PBL_LOG_INFO("Causing bus fault in app"); + typedef void (*KaboomCallback)(void); + KaboomCallback kaboom = 0; + kaboom(); + + } else if (index == 11) { + PBL_LOG_INFO("Infinite Loop on system task"); + system_task_add_callback(stuck_system_task_callback, NULL); + + } else if (index == 12) { + PBL_LOG_INFO("Generate hard-fault"); + + // Modify behavior of the ARM so that bus faults generate a hard fault + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; + + // Write to protected memory + extern uint32_t __isr_stack_start__[]; + __isr_stack_start__[0] = 0x55; + } + +} + + + +// ================================================================================= +static void prv_window_load(Window *window) { + + TestTimersAppData *data = s_app_data; + + int i = 0; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "croak", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "stuck timer", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "call core_dump_reset", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "stuck app", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "stuck RTOS ISR", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "stuck non-RTOS ISR", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "BusFault in CD", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "stuck in CD", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "assert in CD", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "call from ISR", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "BusFault in app", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "stuck system task", + .callback = menu_select_callback, + }; + data->menu_items[i++] = (SimpleMenuItem) { + .title = "hard fault", + .callback = menu_select_callback, + }; + PBL_ASSERTN(i == NUM_MENU_ITEMS); + + // The menu sections + data->menu_section = (SimpleMenuSection) { + .num_items = NUM_MENU_ITEMS, + .items = data->menu_items, + }; + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, + NULL); + layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); +} + + +// ================================================================================= +// Deinitialize resources on window unload that were initialized on window load +static void prv_window_unload(Window *window) { + simple_menu_layer_destroy(s_app_data->menu_layer); +} + + +// ================================================================================= +static void handle_init(void) { + TestTimersAppData *data = app_malloc_check(sizeof(TestTimersAppData)); + memset(data, 0, sizeof(TestTimersAppData)); + s_app_data = data; + + data->window = window_create(); + if (data->window == NULL) { + return; + } + window_init(data->window, ""); + window_set_window_handlers(data->window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + app_window_stack_push(data->window, true /*animated*/); +} + +static void handle_deinit(void) { + // Don't bother freeing anything, the OS should be re-initing the heap. +} + + +// ================================================================================= +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +// ================================================================================= +const PebbleProcessMd* test_core_dump_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Core Dump Test" + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/demo/test_core_dump/test_core_dump.h b/src/fw/apps/demo/test_core_dump/test_core_dump.h new file mode 100644 index 0000000000..62c009047c --- /dev/null +++ b/src/fw/apps/demo/test_core_dump/test_core_dump.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* test_core_dump_app_get_info(); diff --git a/src/fw/apps/demo/test_core_dump/wscript_build b/src/fw/apps/demo/test_core_dump/wscript_build new file mode 100644 index 0000000000..42cd49992b --- /dev/null +++ b/src/fw/apps/demo/test_core_dump/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_core_dump.c'], + target='demo_app_test_core_dump', +) +bld.env.FW_APPS.append('demo_app_test_core_dump') + +# vim:filetype=python diff --git a/src/fw/apps/demo/test_sys_timer/Kconfig b/src/fw/apps/demo/test_sys_timer/Kconfig new file mode 100644 index 0000000000..0f8add499d --- /dev/null +++ b/src/fw/apps/demo/test_sys_timer/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEST_SYS_TIMER + bool "Test Sys Timer" diff --git a/src/fw/apps/demo/test_sys_timer/test_sys_timer.c b/src/fw/apps/demo/test_sys_timer/test_sys_timer.c new file mode 100644 index 0000000000..c32e8daca7 --- /dev/null +++ b/src/fw/apps/demo/test_sys_timer/test_sys_timer.c @@ -0,0 +1,473 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#undef CONFIG_LOG_HASHED + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "process_management/pebble_process_md.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/regular_timer.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" + +#define NUM_MAX_TIMERS 10 + + +// ================================================================================= +// Application Data +typedef struct { + Window *window; + SimpleMenuLayer *menu_layer; + + TimerID timer[NUM_MAX_TIMERS]; + RtcTicks fired_time[NUM_MAX_TIMERS]; + + RegularTimerInfo reg_timers[NUM_MAX_TIMERS]; + + AppTimer *app_timer; + +} TestTimersAppData; + +static TestTimersAppData *s_app_data = 0; + + +// ================================================================================= +static void timer_callback(void* data) +{ + int idx = (int)data; + PBL_ASSERTN(idx >= 0 && idx < NUM_MAX_TIMERS); + PBL_LOG_DBG("STT normal callback %d executed", idx); + s_app_data->fired_time[idx] = rtc_get_ticks(); + return; +} + + +// ================================================================================= +static void stuck_timer_callback(void* data) +{ + PBL_LOG_DBG("STT entering infinite loop in callback"); + while (true) { + psleep(100); + } +} + + +// ================================================================================= +static void long_timer_callback(void* data) +{ + int idx = (int)data; + PBL_ASSERTN(idx >= 0 && idx < NUM_MAX_TIMERS); + PBL_LOG_DBG("STT long running callback %d executed", idx); + s_app_data->fired_time[idx] = rtc_get_ticks(); + + psleep(250); + return; +} + + +// ================================================================================= +// Try and reschedule a regular timer from it's callback +static void reg_timer_1_callback(void* data) { + PBL_LOG_DBG("STT running reg_timer_1_callback"); + if (s_app_data->reg_timers[0].cb != 0) { + PBL_LOG_DBG("STT reg_timer_1_callback rescheduling from callback for every 2 secs. "); + regular_timer_add_multisecond_callback(&s_app_data->reg_timers[0], 2); + } +} + + +// ================================================================================= +// Try and delete a regular timer from it's callback +static void reg_timer_2_callback(void* data) { + PBL_LOG_DBG("STT running reg_timer_2_callback"); + if (s_app_data->reg_timers[0].cb != 0) { + PBL_LOG_DBG("STT reg_timer_2_callback deleting from callback"); + regular_timer_remove_callback(&s_app_data->reg_timers[0]); + } +} + + +// ================================================================================= +// Try and delete, then re-add a regular timer from its callback +static int s_reg_timer_3_callback_count = 0; +static void reg_timer_3_callback(void* data) { + s_reg_timer_3_callback_count++; + PBL_LOG_DBG("STT running reg_timer_3_callback"); + if (s_app_data->reg_timers[0].cb != 0) { + PBL_LOG_DBG("STT reg_timer_3_callback deleting then adding from callback"); + regular_timer_remove_callback(&s_app_data->reg_timers[0]); + regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); + } +} + + +// ================================================================================= +static void menu_callback_prefix(int index, void *ctx) { + PBL_LOG_DBG("Hit menu item %d", index); + + // Mark the layer to be updated + layer_mark_dirty(simple_menu_layer_get_layer(s_app_data->menu_layer)); + + // Cancel and delete old timers if present + for (int i=0; ifired_time[i] = 0; + if (s_app_data->timer[i] != TIMER_INVALID_ID) { + PBL_LOG_DBG("STT stopping and deleting previous timer %d", i); + new_timer_stop(s_app_data->timer[i]); + new_timer_delete(s_app_data->timer[i]); + s_app_data->timer[i] = TIMER_INVALID_ID; + } + } + + if (s_app_data->app_timer != NULL) { + app_timer_cancel(s_app_data->app_timer); + s_app_data->app_timer = 0; + } + + // Cancel and delete old regular timers if present + for (int i=0; ireg_timers[i].cb != NULL) { + PBL_LOG_DBG("STT deleting previous regular timer %d", i); + regular_timer_remove_callback(&s_app_data->reg_timers[i]); + s_app_data->reg_timers[i].cb = NULL; + } + } + +} + + +// ================================================================================= +void single_shot_timer_menu_cb(int index, void *ctx) { + uint32_t zero_flags = 0; + uint32_t expire_ms; + int timer_idx_0 = 0; + bool scheduled = false; + + menu_callback_prefix(index, ctx); + + // Single shot timer + s_app_data->timer[0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[timer_idx_0], 100, timer_callback, (void*)timer_idx_0, zero_flags); + PBL_ASSERTN(success); + + // Make sure it's marked as scheduled + scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); + PBL_ASSERTN(scheduled && expire_ms <= 100); + PBL_LOG_DBG("STT firing in %d ms", (int)expire_ms); + + // Wait for it to fire + psleep(300); + PBL_ASSERTN(s_app_data->fired_time[timer_idx_0] != 0); + scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); + PBL_ASSERTN(!scheduled); +} + + +// ================================================================================= +void repeating_timer_menu_cb(int index, void *ctx) { + uint32_t expire_ms; + int timer_idx_0 = 0; + bool scheduled = false; + + menu_callback_prefix(index, ctx); + + // Repeating timer + s_app_data->timer[timer_idx_0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[timer_idx_0], 500, timer_callback, (void*)timer_idx_0, + TIMER_START_FLAG_REPEATING); + PBL_ASSERTN(success); + + scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); + PBL_ASSERTN(scheduled && expire_ms <= 500); + PBL_LOG_DBG("STT firing in %d ms", (int)expire_ms); +} + +// ================================================================================= +void two_timers_menu_cb(int index, void *ctx) { + uint32_t zero_flags = 0; + uint32_t expire_ms; + + menu_callback_prefix(index, ctx); + + // Multiple timers + int timer_idx_0 = 0; + s_app_data->timer[timer_idx_0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[timer_idx_0], 300, timer_callback, (void*)timer_idx_0, zero_flags); + PBL_ASSERTN(success); + + int timer_idx_1 = 1; + s_app_data->timer[timer_idx_1] = new_timer_create(); + success = new_timer_start(s_app_data->timer[timer_idx_1], 100, timer_callback, (void*)timer_idx_1, zero_flags); + PBL_ASSERTN(success); + + + // Wait for them to fire + psleep(500); + PBL_ASSERTN(s_app_data->fired_time[timer_idx_0] != 0); + PBL_ASSERTN(s_app_data->fired_time[timer_idx_1] != 0); + PBL_ASSERTN(s_app_data->fired_time[timer_idx_1] < s_app_data->fired_time[timer_idx_0]); + bool scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); + PBL_ASSERTN(!scheduled); + scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_1], &expire_ms); + PBL_ASSERTN(!scheduled); +} + +// ================================================================================= +void deferred_delete_menu_cb(int index, void *ctx) { + void *cb_data = 0; + uint32_t zero_flags = 0; + + menu_callback_prefix(index, ctx); + + // Deferred delete + s_app_data->timer[0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[0], 1, long_timer_callback, cb_data, zero_flags); + PBL_ASSERTN(success); + psleep(50); + + // Stop and then delete it + success = new_timer_stop(s_app_data->timer[0]); + PBL_ASSERTN(!success); /* stop returns false if callback is running */ + new_timer_delete(s_app_data->timer[0]); + s_app_data->timer[0] = TIMER_INVALID_ID; +} + +// ================================================================================= +void fail_if_executing_menu_cb(int index, void *ctx) { + void *cb_data = 0; + uint32_t zero_flags = 0; + + menu_callback_prefix(index, ctx); + + // fail if executing + s_app_data->timer[0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[0], 1, long_timer_callback, cb_data, zero_flags); + PBL_ASSERTN(success); + psleep(50); + + // try and reschedule while it's executing + success = new_timer_start(s_app_data->timer[0], 1, long_timer_callback, cb_data, + TIMER_START_FLAG_FAIL_IF_EXECUTING); + PBL_ASSERTN(!success); +} + +// ================================================================================= +void fail_if_scheduled_menu_cb(int index, void *ctx) { + void *cb_data = 0; + uint32_t zero_flags = 0; + + menu_callback_prefix(index, ctx); + + // fail if scheduled + s_app_data->timer[0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[0], 100, timer_callback, cb_data, + zero_flags); + PBL_ASSERTN(success); + + // try and reschedule while it's already scheduled + success = new_timer_start(s_app_data->timer[0], 1, timer_callback, cb_data, + TIMER_START_FLAG_FAIL_IF_SCHEDULED); + PBL_ASSERTN(!success); + +} + +// ================================================================================= +void evented_timer_menu_cb(int index, void *ctx) { + void *cb_data = 0; + + menu_callback_prefix(index, ctx); + + // Test evented_timer + s_app_data->app_timer = app_timer_register(100 /*ms*/, timer_callback, cb_data); + PBL_ASSERTN(s_app_data->app_timer); +} + +// ================================================================================= +void stuck_callback_menu_cb(int index, void *ctx) { + void *cb_data = 0; + uint32_t zero_flags = 0; + + menu_callback_prefix(index, ctx); + + // stuck callback + s_app_data->timer[0] = new_timer_create(); + bool success = new_timer_start(s_app_data->timer[0], 100, stuck_timer_callback, cb_data, zero_flags); + PBL_ASSERTN(success); +} + +// ================================================================================= +void invaid_timer_id_menu_cb(int index, void *ctx) { + void *cb_data = 0; + uint32_t zero_flags = 0; + + menu_callback_prefix(index, ctx); + + // invalid timer id + new_timer_start(0x12345678, 100, timer_callback, cb_data, zero_flags); +} + + +// ================================================================================= +void reg_timer_schedule_1sec_from_cb_menu_cb(int index, void *ctx) { + menu_callback_prefix(index, ctx); + + s_app_data->reg_timers[0].cb = reg_timer_1_callback; + regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); +} + +// ================================================================================= +void reg_timer_schedule_1min_from_cb_menu_cb(int index, void *ctx) { + menu_callback_prefix(index, ctx); + + s_app_data->reg_timers[0].cb = reg_timer_1_callback; + regular_timer_add_minutes_callback(&s_app_data->reg_timers[0]); + + // This should assert when the callback runs because it tries to reschedule as a seconds callback +} + +// ================================================================================= +void reg_timer_delete_from_cb_menu_cb(int index, void *ctx) { + menu_callback_prefix(index, ctx); + + s_app_data->reg_timers[0].cb = reg_timer_2_callback; + regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); +} + +// ================================================================================= +void reg_timer_delete_then_add_from_cb_menu_cb(int index, void *ctx) { + menu_callback_prefix(index, ctx); + + s_reg_timer_3_callback_count = 0; + s_app_data->reg_timers[0].cb = reg_timer_3_callback; + regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); + + // Wait for timer to run at least twice + PBL_LOG_DBG("waiting for callback to run 2 times"); + psleep(2200); + + PBL_ASSERT(s_reg_timer_3_callback_count >= 2, "Callback didn't run at least twice"); +} + +// ================================================================================= +void croak_menu_cb(int index, void *ctx) { + + menu_callback_prefix(index, ctx); + PBL_CROAK("DIE!"); +} + +// ================================================================================= +static void prv_window_load(Window *window) { + TestTimersAppData *data = s_app_data; + + static const SimpleMenuItem menu_items[] = { + { + .title = "single-shot timer", + .callback = single_shot_timer_menu_cb + }, { + .title = "repeating timer", + .callback = repeating_timer_menu_cb + }, { + .title = "two timers", + .callback = two_timers_menu_cb + }, { + .title = "deferred delete", + .callback = deferred_delete_menu_cb + }, { + .title = "fail if executing", + .callback = fail_if_executing_menu_cb + }, { + .title = "fail if scheduled", + .callback = fail_if_scheduled_menu_cb + }, { + .title = "evented_timer", + .callback = evented_timer_menu_cb + }, { + .title = "stuck callback", + .callback = stuck_callback_menu_cb + }, { + .title = "invalid timer ID", + .callback = invaid_timer_id_menu_cb + }, { + .title = "RT: sch 1 sec from cb", + .callback = reg_timer_schedule_1sec_from_cb_menu_cb + }, { + .title = "RT: sch 1 min from cb", + .callback = reg_timer_schedule_1min_from_cb_menu_cb + }, { + .title = "RT: delete from cb", + .callback = reg_timer_delete_from_cb_menu_cb + }, { + .title = "RT: delete+add from cb", + .callback = reg_timer_delete_then_add_from_cb_menu_cb + }, { + .title = "croak", + .callback = croak_menu_cb + } + }; + static const SimpleMenuSection sections[] = { + { + .items = menu_items, + .num_items = ARRAY_LENGTH(menu_items) + } + }; + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + data->menu_layer = simple_menu_layer_create(bounds, data->window, sections, ARRAY_LENGTH(sections), NULL); + layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); +} + + +// ================================================================================= +// Deinitialize resources on window unload that were initialized on window load +static void prv_window_unload(Window *window) { + simple_menu_layer_destroy(s_app_data->menu_layer); +} + + +// ================================================================================= +static void handle_init(void) { + TestTimersAppData *data = app_malloc_check(sizeof(TestTimersAppData)); + memset(data, 0, sizeof(TestTimersAppData)); + s_app_data = data; + + data->window = window_create(); + if (data->window == NULL) { + return; + } + window_init(data->window, ""); + window_set_window_handlers(data->window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + app_window_stack_push(data->window, true /*animated*/); +} + +static void handle_deinit(void) { + // Don't bother freeing anything, the OS should be re-initing the heap. +} + + +// ================================================================================= +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +// ================================================================================= +const PebbleProcessMd* test_sys_timer_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "System Timer Test" + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/demo/test_sys_timer/test_sys_timer.h b/src/fw/apps/demo/test_sys_timer/test_sys_timer.h new file mode 100644 index 0000000000..bc0a81dad9 --- /dev/null +++ b/src/fw/apps/demo/test_sys_timer/test_sys_timer.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* test_sys_timer_app_get_info(); diff --git a/src/fw/apps/demo/test_sys_timer/wscript_build b/src/fw/apps/demo/test_sys_timer/wscript_build new file mode 100644 index 0000000000..57f1b092f6 --- /dev/null +++ b/src/fw/apps/demo/test_sys_timer/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['test_sys_timer.c'], + target='demo_app_test_sys_timer', +) +bld.env.FW_APPS.append('demo_app_test_sys_timer') + +# vim:filetype=python diff --git a/src/fw/apps/demo/text_clipping/Kconfig b/src/fw/apps/demo/text_clipping/Kconfig new file mode 100644 index 0000000000..1d9376c436 --- /dev/null +++ b/src/fw/apps/demo/text_clipping/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEXT_CLIPPING + bool "Text Clipping" diff --git a/src/fw/apps/demo/text_clipping/text_clipping.c b/src/fw/apps/demo/text_clipping/text_clipping.c new file mode 100644 index 0000000000..dd36525d68 --- /dev/null +++ b/src/fw/apps/demo/text_clipping/text_clipping.c @@ -0,0 +1,225 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "text_clipping.h" + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "system/logging.h" + +typedef enum { + SelectIndexPixels, + SelectIndexDirection, + SelectIndexOverflow, + // Add new selection criteria above + SelectIndexMax +} SelectIndex; + +typedef struct AppState { + Window window; + Layer canvas; + GSize canvas_size; + TextLayer text_layer; + TextLayer direction_layer; + TextLayer word_wrap_layer; + SelectIndex select_index; + bool up_down_direction; // True = move up or down; False = move left or right + bool word_wrap; // True = word wrap; False = don't word wrap +} AppState; + +static const char* text_buffer = "Text Clipping"; + +static void init_text_layer(AppState *data, GRect frame) { + text_layer_init(&data->text_layer, &GRect(frame.origin.x, frame.origin.y, + frame.size.w, frame.size.h)); + text_layer_set_background_color(&data->text_layer, GColorWhite); + text_layer_set_text_color(&data->text_layer, GColorBlack); + text_layer_set_text(&data->text_layer, text_buffer); + GFont gothic_24_bold = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + text_layer_set_font(&data->text_layer, gothic_24_bold); + text_layer_set_text_alignment(&data->text_layer, GTextAlignmentCenter); + text_layer_set_overflow_mode(&data->text_layer, GTextOverflowModeTrailingEllipsis); +} + +static void click_handler(ClickRecognizerRef recognizer, Window *window) { + AppState *data = window_get_user_data(window); + ButtonId button = click_recognizer_get_button_id(recognizer); + GRect frame = data->text_layer.layer.frame; + + if (button == BUTTON_ID_UP) { + if (data->select_index == SelectIndexPixels) { + // Do inverse + if (data->up_down_direction) { + frame.origin.y--; + } else { + frame.origin.x--; + } + } else if (data->select_index == SelectIndexDirection) { + data->up_down_direction = !(data->up_down_direction); + } else if (data->select_index == SelectIndexOverflow) { + data->word_wrap = !(data->word_wrap); + } + } else if (button == BUTTON_ID_SELECT) { + data->select_index = (data->select_index + 1) % SelectIndexMax; + } else if (button == BUTTON_ID_DOWN) { + if (data->select_index == SelectIndexPixels) { + // Do inverse + if (data->up_down_direction) { + frame.origin.y++; + } else { + frame.origin.x++; + } + } else if (data->select_index == SelectIndexDirection) { + data->up_down_direction = !(data->up_down_direction); + } else if (data->select_index == SelectIndexOverflow) { + data->word_wrap = !(data->word_wrap); + } + } + + text_layer_set_text_color(&data->direction_layer, GColorBlack); + text_layer_set_background_color(&data->direction_layer, GColorWhite); + text_layer_set_text_color(&data->word_wrap_layer, GColorBlack); + text_layer_set_background_color(&data->word_wrap_layer, GColorWhite); + + if (data->select_index == SelectIndexDirection) { + text_layer_set_text_color(&data->direction_layer, GColorWhite); + text_layer_set_background_color(&data->direction_layer, GColorBlack); + } else if (data->select_index == SelectIndexOverflow) { + text_layer_set_text_color(&data->word_wrap_layer, GColorWhite); + text_layer_set_background_color(&data->word_wrap_layer, GColorBlack); + } + + if (data->up_down_direction) { + text_layer_set_text(&data->direction_layer, "Direction: Up/Down"); + } else { + text_layer_set_text(&data->direction_layer, "Direction: Left/Right"); + } + + if (data->word_wrap) { + frame.size.w = 72; + frame.size.h = 60; + } else { + frame.size.w = 72; + frame.size.h = 32; + } + + init_text_layer(data, frame); + + if (data->word_wrap) { + text_layer_set_text(&data->word_wrap_layer, "Overflow: Word Wrap"); + text_layer_set_overflow_mode(&data->text_layer, GTextOverflowModeWordWrap); + } else { + text_layer_set_text(&data->word_wrap_layer, "Overflow: Ellipsis"); + text_layer_set_overflow_mode(&data->text_layer, GTextOverflowModeTrailingEllipsis); + } +} + +static void config_provider(Window *window) { + window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, (ClickHandler)click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_SELECT, 100, (ClickHandler)click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, (ClickHandler)click_handler); + (void)window; +} + +static void update_window(Layer *layer, GContext *ctx) { + // Clear parent layer first + graphics_context_set_fill_color(ctx, GColorWhite); + graphics_fill_rect(ctx, &GRect(39, 39, 82, 42)); + + // Draw box just outside the clipping area + graphics_draw_rect(ctx, &GRect(39, 39, 82, 42)); +} + +static void prv_window_load(Window *window) { + AppState *data = window_get_user_data(window); + + data->select_index = SelectIndexPixels; + data->up_down_direction = true; + data->word_wrap = false; + + // Init canvas (i.e. clipping box) + data->canvas_size = GSize(80, 40); + + layer_init(&data->canvas, &GRect(40, 40, data->canvas_size.w, data->canvas_size.h)); + layer_add_child(&data->window.layer, &data->canvas); + + // Init text layer + init_text_layer(data, GRect(4, 4, 72, 32)); + + // Init direction layer + text_layer_init(&data->direction_layer, &GRect(5, 100, 135, 20)); + text_layer_set_background_color(&data->direction_layer, GColorWhite); + text_layer_set_text_color(&data->direction_layer, GColorBlack); + text_layer_set_text(&data->direction_layer, "Direction: Up/Down"); + GFont gothic_14_bold = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); + text_layer_set_font(&data->direction_layer, gothic_14_bold); + text_layer_set_text_alignment(&data->direction_layer, GTextAlignmentCenter); + text_layer_set_overflow_mode(&data->direction_layer, GTextOverflowModeTrailingEllipsis); + + // Init word wrap layer + text_layer_init(&data->word_wrap_layer, &GRect(5, 130, 135, 20)); + text_layer_set_background_color(&data->word_wrap_layer, GColorWhite); + text_layer_set_text_color(&data->word_wrap_layer, GColorBlack); + text_layer_set_text(&data->word_wrap_layer, "Overflow: Ellipsis"); + text_layer_set_font(&data->word_wrap_layer, gothic_14_bold); + text_layer_set_text_alignment(&data->word_wrap_layer, GTextAlignmentCenter); + text_layer_set_overflow_mode(&data->word_wrap_layer, GTextOverflowModeTrailingEllipsis); + + // Setup children for clipping canvas + layer_add_child(&data->canvas, &data->text_layer.layer); + + // Setup children for main window canvas + layer_add_child(&window->layer, &data->direction_layer.layer); + layer_add_child(&window->layer, &data->word_wrap_layer.layer); + + // Setup update proc to draw clipping box + layer_set_update_proc(&window->layer, update_window); +} + +static void push_window(struct AppState *data) { + Window* window = &data->window; + window_init(window, WINDOW_NAME("Text Clipping")); + window_set_user_data(window, data); + window_set_click_config_provider(window, (ClickConfigProvider) config_provider); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + }); + const bool animated = true; + app_window_stack_push(window, animated); +} + + +//////////////////// +// App boilerplate + +static void handle_init(void) { + struct AppState* data = app_malloc_check(sizeof(struct AppState)); + + app_state_set_user_data(data); + push_window(data); +} + +static void handle_deinit(void) { + struct AppState* data = app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* text_clipping_app_get_info() { + static const PebbleProcessMdSystem text_spacing_info = { + .common.main_func = &s_main, + .name = "Text Clipping" + }; + return (const PebbleProcessMd*) &text_spacing_info; +} diff --git a/src/fw/apps/demo/text_clipping/text_clipping.h b/src/fw/apps/demo/text_clipping/text_clipping.h new file mode 100644 index 0000000000..27e1329c7a --- /dev/null +++ b/src/fw/apps/demo/text_clipping/text_clipping.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* text_clipping_app_get_info(); diff --git a/src/fw/apps/demo/text_clipping/wscript_build b/src/fw/apps/demo/text_clipping/wscript_build new file mode 100644 index 0000000000..f4ce4f6d99 --- /dev/null +++ b/src/fw/apps/demo/text_clipping/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['text_clipping.c'], + target='demo_app_text_clipping', +) +bld.env.FW_APPS.append('demo_app_text_clipping') + +# vim:filetype=python diff --git a/src/fw/apps/demo/text_flow/Kconfig b/src/fw/apps/demo/text_flow/Kconfig new file mode 100644 index 0000000000..0a4501c9b7 --- /dev/null +++ b/src/fw/apps/demo/text_flow/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEXT_FLOW + bool "Text Flow" diff --git a/src/fw/apps/demo_apps/text_flow.c b/src/fw/apps/demo/text_flow/text_flow.c similarity index 89% rename from src/fw/apps/demo_apps/text_flow.c rename to src/fw/apps/demo/text_flow/text_flow.c index 121232a834..9d2aae87d0 100644 --- a/src/fw/apps/demo_apps/text_flow.c +++ b/src/fw/apps/demo/text_flow/text_flow.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "text_flow.h" diff --git a/src/fw/apps/demo/text_flow/text_flow.h b/src/fw/apps/demo/text_flow/text_flow.h new file mode 100644 index 0000000000..dbb55598f4 --- /dev/null +++ b/src/fw/apps/demo/text_flow/text_flow.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* text_flow_app_get_info(); diff --git a/src/fw/apps/demo/text_flow/wscript_build b/src/fw/apps/demo/text_flow/wscript_build new file mode 100644 index 0000000000..0046ddef79 --- /dev/null +++ b/src/fw/apps/demo/text_flow/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['text_flow.c'], + target='demo_app_text_flow', +) +bld.env.FW_APPS.append('demo_app_text_flow') + +# vim:filetype=python diff --git a/src/fw/apps/demo/text_layout/Kconfig b/src/fw/apps/demo/text_layout/Kconfig new file mode 100644 index 0000000000..d68c35b2b2 --- /dev/null +++ b/src/fw/apps/demo/text_layout/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEXT_LAYOUT + bool "Text Layout" diff --git a/src/fw/apps/demo_apps/text_layout.c b/src/fw/apps/demo/text_layout/text_layout.c similarity index 87% rename from src/fw/apps/demo_apps/text_layout.c rename to src/fw/apps/demo/text_layout/text_layout.c index e9c23b2fd8..75dfca4ebe 100644 --- a/src/fw/apps/demo_apps/text_layout.c +++ b/src/fw/apps/demo/text_layout/text_layout.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "text_layout.h" @@ -42,7 +29,7 @@ static void select_click_handler(ClickRecognizerRef recognizer, void* callback_p (void) recognizer; struct AppState* data = (struct AppState*) callback_param; - PBL_LOG(LOG_LEVEL_DEBUG, "I should be changing the font!"); + PBL_LOG_DBG("I should be changing the font!"); if (++s_font_selection > 7) { s_font_selection = 0; } @@ -65,7 +52,7 @@ static void select_long_click_handler(ClickRecognizerRef recognizer, struct AppS (void) data; (void) recognizer; - PBL_LOG(LOG_LEVEL_DEBUG, "SELECT loooong clicked!"); + PBL_LOG_DBG("SELECT loooong clicked!"); } #endif diff --git a/src/fw/apps/demo/text_layout/wscript_build b/src/fw/apps/demo/text_layout/wscript_build new file mode 100644 index 0000000000..f4d771d0cc --- /dev/null +++ b/src/fw/apps/demo/text_layout/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.', '../shared'], + source=['text_layout.c'], + target='demo_app_text_layout', +) +bld.env.FW_APPS.append('demo_app_text_layout') + +# vim:filetype=python diff --git a/src/fw/apps/demo/text_spacing/Kconfig b/src/fw/apps/demo/text_spacing/Kconfig new file mode 100644 index 0000000000..e6ea083d66 --- /dev/null +++ b/src/fw/apps/demo/text_spacing/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TEXT_SPACING + bool "Text Spacing" diff --git a/src/fw/apps/demo_apps/text_spacing.c b/src/fw/apps/demo/text_spacing/text_spacing.c similarity index 84% rename from src/fw/apps/demo_apps/text_spacing.c rename to src/fw/apps/demo/text_spacing/text_spacing.c index 17b6b6a92b..0e9fbcd9a8 100644 --- a/src/fw/apps/demo_apps/text_spacing.c +++ b/src/fw/apps/demo/text_spacing/text_spacing.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "text_spacing.h" @@ -60,7 +47,7 @@ static void click_handler(ClickRecognizerRef recognizer, Window *window) { GSize size_used = text_layer_get_content_size(app_get_current_graphics_context(), &data->text_layer); - PBL_LOG(LOG_LEVEL_DEBUG, "Line Delta: %d, Size %d x %d, Overflow: %d", data->line_spacing_delta, + PBL_LOG_DBG("Line Delta: %d, Size %d x %d, Overflow: %d", data->line_spacing_delta, size_used.w, size_used.h, data->overflow_mode); } @@ -93,7 +80,7 @@ static void prv_window_load(Window *window) { GSize size_used = text_layer_get_content_size(app_get_current_graphics_context(), &data->text_layer); - PBL_LOG(LOG_LEVEL_DEBUG, "Max size used %d %d", size_used.w, size_used.h); + PBL_LOG_DBG("Max size used %d %d", size_used.w, size_used.h); } static void push_window(struct AppState *data) { diff --git a/src/fw/apps/demo/text_spacing/text_spacing.h b/src/fw/apps/demo/text_spacing/text_spacing.h new file mode 100644 index 0000000000..77c7fdc296 --- /dev/null +++ b/src/fw/apps/demo/text_spacing/text_spacing.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* text_spacing_app_get_info(); diff --git a/src/fw/apps/demo/text_spacing/wscript_build b/src/fw/apps/demo/text_spacing/wscript_build new file mode 100644 index 0000000000..9c1acc6bde --- /dev/null +++ b/src/fw/apps/demo/text_spacing/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['text_spacing.c'], + target='demo_app_text_spacing', +) +bld.env.FW_APPS.append('demo_app_text_spacing') + +# vim:filetype=python diff --git a/src/fw/apps/demo/timeline_pins/Kconfig b/src/fw/apps/demo/timeline_pins/Kconfig new file mode 100644 index 0000000000..ef8fc98781 --- /dev/null +++ b/src/fw/apps/demo/timeline_pins/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TIMELINE_PINS + bool "Timeline Pins" diff --git a/src/fw/apps/demo_apps/timeline_pins_demo.c b/src/fw/apps/demo/timeline_pins/timeline_pins_demo.c similarity index 94% rename from src/fw/apps/demo_apps/timeline_pins_demo.c rename to src/fw/apps/demo/timeline_pins/timeline_pins_demo.c index 539400ca01..6fbe473cc5 100644 --- a/src/fw/apps/demo_apps/timeline_pins_demo.c +++ b/src/fw/apps/demo/timeline_pins/timeline_pins_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "timeline_pins_demo.h" @@ -20,16 +7,16 @@ #include "applib/ui/app_window_stack.h" #include "applib/ui/option_menu_window.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/timeline/timeline.h" +#include "apps/system/timeline/timeline.h" #include "process_management/app_manager.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/timeline/calendar_layout.h" -#include "services/normal/timeline/event.h" -#include "services/normal/timeline/health_layout.h" -#include "services/normal/timeline/weather_layout.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/timeline/calendar_layout.h" +#include "pbl/services/timeline/event.h" +#include "pbl/services/timeline/health_layout.h" +#include "pbl/services/timeline/weather_layout.h" #include "util/size.h" #include @@ -308,8 +295,8 @@ void timeline_pins_demo_add_pins(TimelinePinsDemoSet pin_set) { prv_add_weather_pin(1 * 24 * 60 * 60); prv_add_weather_pin(2 * 24 * 60 * 60); prv_add_weather_pin(3 * 24 * 60 * 60); - // Fallthrough } + // fallthrough case TimelinePinsDemo_OneDayAway: prv_add_weather_pin(-2 * 24 * 60 * 60); prv_add_weather_pin(2 * 24 * 60 * 60); diff --git a/src/fw/apps/demo/timeline_pins/timeline_pins_demo.h b/src/fw/apps/demo/timeline_pins/timeline_pins_demo.h new file mode 100644 index 0000000000..822b8e5936 --- /dev/null +++ b/src/fw/apps/demo/timeline_pins/timeline_pins_demo.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +typedef enum { + TimelinePinsDemo_Default = 0, + TimelinePinsDemo_Notifications, + TimelinePinsDemo_OneDayAway, + TimelinePinsDemo_OngoingEvent, + TimelinePinsDemo_TodayAndTomorrow, + TimelinePinsDemoCount, +} TimelinePinsDemoSet; + +void timeline_pins_demo_add_pins(TimelinePinsDemoSet pin_set); + +const PebbleProcessMd *timeline_pins_get_app_info(void); diff --git a/src/fw/apps/demo/timeline_pins/wscript_build b/src/fw/apps/demo/timeline_pins/wscript_build new file mode 100644 index 0000000000..3a16b38236 --- /dev/null +++ b/src/fw/apps/demo/timeline_pins/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['timeline_pins_demo.c'], + target='demo_app_timeline_pins', +) +bld.env.FW_APPS.append('demo_app_timeline_pins') + +# vim:filetype=python diff --git a/src/fw/apps/demo/timer/Kconfig b/src/fw/apps/demo/timer/Kconfig new file mode 100644 index 0000000000..362658fc7e --- /dev/null +++ b/src/fw/apps/demo/timer/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TIMER + bool "Timer" diff --git a/src/fw/apps/demo/timer/timer.c b/src/fw/apps/demo/timer/timer.c new file mode 100644 index 0000000000..9592b14f3c --- /dev/null +++ b/src/fw/apps/demo/timer/timer.c @@ -0,0 +1,72 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "process_management/pebble_process_md.h" +#include "applib/app.h" +#include "applib/app_logging.h" +#include "process_state/app_state/app_state.h" +#include "applib/ui/ui.h" +#include "process_management/pebble_process_md.h" +#include "system/logging.h" +#include "system/passert.h" + +static AppTimer *s_timer = NULL; + +static void shouldnt_happen(void *context) { + WTF; +} + +static void stupid_cancel(void *context) { + app_timer_cancel(s_timer); + + APP_LOG(LOG_LEVEL_INFO, "success"); +} + +static void prv_window_load(Window *window) { + int dummy_data = 0; + // Wait much longer than it should take to cancel the timer. + AppTimer *timer = app_timer_register(1000 /*ms*/, shouldnt_happen, &dummy_data); + PBL_ASSERTN(timer != NULL); + // Try to cancel it twice. This used to crash, but should not crash anymore. + // In particular, we're looking to see that at least if we don't do more app_heap + // allocations, we will be able to detect that we're effectively trying to + // double-release this timer. + app_timer_cancel(timer); + app_timer_cancel(timer); + + timer = app_timer_register(1 /*ms*/, stupid_cancel, &s_timer); + s_timer = timer; + PBL_ASSERTN(timer != NULL); +} + +static const WindowHandlers s_main_menu_handlers = { + .load = prv_window_load, +}; + +static void handle_init(void) { + Window *window = window_create(); + if (window == NULL) { + return; + } + window_init(window, ""); + window_set_window_handlers(window, &s_main_menu_handlers); + app_window_stack_push(window, true /*animated*/); +} + +static void handle_deinit(void) { + // Don't bother freeing anything, the OS should be re-initing the heap. +} + +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +const PebbleProcessMd* timer_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + .name = "Timer Cancel Test" + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/demo/timer/timer.h b/src/fw/apps/demo/timer/timer.h new file mode 100644 index 0000000000..6f33055b13 --- /dev/null +++ b/src/fw/apps/demo/timer/timer.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* timer_app_get_info(); diff --git a/src/fw/apps/demo/timer/wscript_build b/src/fw/apps/demo/timer/wscript_build new file mode 100644 index 0000000000..6ce189b911 --- /dev/null +++ b/src/fw/apps/demo/timer/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['timer.c'], + target='demo_app_timer', +) +bld.env.FW_APPS.append('demo_app_timer') + +# vim:filetype=python diff --git a/src/fw/apps/demo/trigger_alarm/Kconfig b/src/fw/apps/demo/trigger_alarm/Kconfig new file mode 100644 index 0000000000..3ae1e39b9f --- /dev/null +++ b/src/fw/apps/demo/trigger_alarm/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_TRIGGER_ALARM + bool "Trigger Alarm" diff --git a/src/fw/apps/demo/trigger_alarm/trigger_alarm.c b/src/fw/apps/demo/trigger_alarm/trigger_alarm.c new file mode 100644 index 0000000000..a555f9dc21 --- /dev/null +++ b/src/fw/apps/demo/trigger_alarm/trigger_alarm.c @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "trigger_alarm.h" + +#include "applib/app.h" +#include "process_state/app_state/app_state.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window_stack.h" +#include "kernel/pbl_malloc.h" +#include "kernel/events.h" +#include "drivers/rtc.h" + +typedef struct { + Window window; +} TriggerAlarmData; + +static void handle_init(void) { + TriggerAlarmData *data = app_malloc_check(sizeof(TriggerAlarmData)); + + app_state_set_user_data(data); + window_init(&data->window, WINDOW_NAME("Trigger Alarm Demo")); + const bool animated = true; + app_window_stack_push(&data->window, animated); + + PebbleEvent e = (PebbleEvent) { + .type = PEBBLE_ALARM_CLOCK_EVENT, + .alarm_clock = { + .alarm_time = rtc_get_time(), + .alarm_label = "Wake Up" + } + }; + + event_put(&e); +} + +static void handle_deinit(void) { + TriggerAlarmData *data = (TriggerAlarmData*)app_state_get_user_data(); + app_free(data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* trigger_alarm_get_app_info() { + static const PebbleProcessMdSystem s_trigger_alarm = { + .common.main_func = s_main, + .name = "Trigger Alarm" + }; + + return (const PebbleProcessMd*) &s_trigger_alarm; +} + diff --git a/src/fw/apps/demo/trigger_alarm/wscript_build b/src/fw/apps/demo/trigger_alarm/wscript_build new file mode 100644 index 0000000000..398cadbffb --- /dev/null +++ b/src/fw/apps/demo/trigger_alarm/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.', '../shared'], + source=['trigger_alarm.c'], + target='demo_app_trigger_alarm', +) +bld.env.FW_APPS.append('demo_app_trigger_alarm') + +# vim:filetype=python diff --git a/src/fw/apps/demo/vibe_and_logs/Kconfig b/src/fw/apps/demo/vibe_and_logs/Kconfig new file mode 100644 index 0000000000..2dd1bcb951 --- /dev/null +++ b/src/fw/apps/demo/vibe_and_logs/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_VIBE_AND_LOGS + bool "Vibe And Logs" diff --git a/src/fw/apps/demo/vibe_and_logs/vibe_and_logs.c b/src/fw/apps/demo/vibe_and_logs/vibe_and_logs.c new file mode 100644 index 0000000000..97a4cebd55 --- /dev/null +++ b/src/fw/apps/demo/vibe_and_logs/vibe_and_logs.c @@ -0,0 +1,68 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "trigger_alarm.h" +#include "applib/ui/vibes.h" + +#include "applib/app.h" +#include "process_state/app_state/app_state.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window_stack.h" +#include "kernel/events.h" +#include "drivers/rtc.h" +#include "drivers/vibe.h" +#include "system/logging.h" + +Window s_window; +static AppTimer *s_app_timer; +TimerID s_sys_timer = TIMER_INVALID_ID; + +static void app_timer_callback(void *data) { + for (int i=0; i<40; i++) { + PBL_LOG_INFO("%d Running app timer callback", i); + vibes_short_pulse(); + } + s_app_timer = app_timer_register(100 /* milliseconds */, app_timer_callback, NULL); +} + +#if 0 +static void sys_timer_callback(void* data) { + for (int i=0; i<1; i++) { + PBL_LOG_INFO("%d Running sys timer callback", i); + vibes_short_pulse(); + } + new_timer_start(s_sys_timer, 20, sys_timer_callback, NULL, 0); +} +#endif + +static void handle_init(void) { + window_init(&s_window, WINDOW_NAME("VibeAndLogs Demo")); + const bool animated = true; + app_window_stack_push(&s_window, animated); + + s_app_timer = app_timer_register(100 /* milliseconds */, app_timer_callback, NULL); + + s_sys_timer = new_timer_create(); + //new_timer_start(s_sys_timer, 10, sys_timer_callback, NULL, 0); +} + +static void handle_deinit(void) { +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* vibe_and_logs_get_app_info() { + static const PebbleProcessMdSystem s_trigger_alarm = { + .common.main_func = s_main, + .name = "VibeAndLogs" + }; + + return (const PebbleProcessMd*) &s_trigger_alarm; +} + diff --git a/src/fw/apps/demo/vibe_and_logs/vibe_and_logs.h b/src/fw/apps/demo/vibe_and_logs/vibe_and_logs.h new file mode 100644 index 0000000000..afec447d87 --- /dev/null +++ b/src/fw/apps/demo/vibe_and_logs/vibe_and_logs.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* vibe_and_logs_get_app_info(); diff --git a/src/fw/apps/demo/vibe_and_logs/wscript_build b/src/fw/apps/demo/vibe_and_logs/wscript_build new file mode 100644 index 0000000000..8fa9e6ed66 --- /dev/null +++ b/src/fw/apps/demo/vibe_and_logs/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=[ +'.', +'../shared', +], + source=['vibe_and_logs.c'], + target='demo_app_vibe_and_logs', +) +bld.env.FW_APPS.append('demo_app_vibe_and_logs') + +# vim:filetype=python diff --git a/src/fw/apps/demo/vibe_score/Kconfig b/src/fw/apps/demo/vibe_score/Kconfig new file mode 100644 index 0000000000..017935257b --- /dev/null +++ b/src/fw/apps/demo/vibe_score/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_VIBE_SCORE + bool "Vibe Score" diff --git a/src/fw/apps/demo_apps/vibe_score_demo.c b/src/fw/apps/demo/vibe_score/vibe_score_demo.c similarity index 76% rename from src/fw/apps/demo_apps/vibe_score_demo.c rename to src/fw/apps/demo/vibe_score/vibe_score_demo.c index cb3e458e03..82e1f10c2f 100644 --- a/src/fw/apps/demo_apps/vibe_score_demo.c +++ b/src/fw/apps/demo/vibe_score/vibe_score_demo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app.h" #include "applib/ui/app_window_stack.h" @@ -20,7 +7,7 @@ #include "process_management/pebble_process_md.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/normal/vibes/vibe_score.h" +#include "pbl/services/vibes/vibe_score.h" #include "util/size.h" #include diff --git a/src/fw/apps/demo/vibe_score/vibe_score_demo.h b/src/fw/apps/demo/vibe_score/vibe_score_demo.h new file mode 100644 index 0000000000..de7d52311f --- /dev/null +++ b/src/fw/apps/demo/vibe_score/vibe_score_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *vibe_score_demo_get_info(void); diff --git a/src/fw/apps/demo/vibe_score/wscript_build b/src/fw/apps/demo/vibe_score/wscript_build new file mode 100644 index 0000000000..8365c6e4fb --- /dev/null +++ b/src/fw/apps/demo/vibe_score/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['vibe_score_demo.c'], + target='demo_app_vibe_score', +) +bld.env.FW_APPS.append('demo_app_vibe_score') + +# vim:filetype=python diff --git a/src/fw/apps/demo/vibe_strength/Kconfig b/src/fw/apps/demo/vibe_strength/Kconfig new file mode 100644 index 0000000000..08ae2fc83c --- /dev/null +++ b/src/fw/apps/demo/vibe_strength/Kconfig @@ -0,0 +1,5 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config DEMO_APP_VIBE_STRENGTH + bool "Vibe Strength" diff --git a/src/fw/apps/demo/vibe_strength/vibe_strength_demo.c b/src/fw/apps/demo/vibe_strength/vibe_strength_demo.c new file mode 100644 index 0000000000..dc49f0eb10 --- /dev/null +++ b/src/fw/apps/demo/vibe_strength/vibe_strength_demo.c @@ -0,0 +1,55 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/number_window.h" +#include "drivers/vibe.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" + +#include + +static void selected_pwm_percentage(NumberWindow *nw, void *ctx) { + static bool on = true; + int8_t val = number_window_get_value(nw); + vibe_set_strength(val); + vibe_ctl(on); + on = !on; +} + +static void handle_init(void) { + NumberWindow *vibe_num_window = number_window_create("Vibe Strength", + (NumberWindowCallbacks) { .selected = selected_pwm_percentage }, + NULL); + app_state_set_user_data(vibe_num_window); + + uint8_t scale_granularity = 5; // 5 percent at a time + uint8_t curr_percent = VIBE_STRENGTH_MAX; + + number_window_set_value(vibe_num_window, curr_percent); + number_window_set_max(vibe_num_window, VIBE_STRENGTH_MAX); + number_window_set_min(vibe_num_window, VIBE_STRENGTH_MIN); + number_window_set_step_size(vibe_num_window, scale_granularity); + + app_window_stack_push(number_window_get_window(vibe_num_window), true); +} + +static void handle_deinit(void) { + NumberWindow *data = app_state_get_user_data(); + number_window_destroy(data); +} + +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +const PebbleProcessMd* vibe_strength_demo_get_info() { + static const PebbleProcessMdSystem s_vibe_strength_info = { + .common.main_func = s_main, + .name = "Vibe Strength" + }; + return (const PebbleProcessMd*) &s_vibe_strength_info; +} diff --git a/src/fw/apps/demo/vibe_strength/vibe_strength_demo.h b/src/fw/apps/demo/vibe_strength/vibe_strength_demo.h new file mode 100644 index 0000000000..90e339fc24 --- /dev/null +++ b/src/fw/apps/demo/vibe_strength/vibe_strength_demo.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *vibe_strength_demo_get_info(void); diff --git a/src/fw/apps/demo/vibe_strength/wscript_build b/src/fw/apps/demo/vibe_strength/wscript_build new file mode 100644 index 0000000000..781c518d9f --- /dev/null +++ b/src/fw/apps/demo/vibe_strength/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + includes=['.'], + source=['vibe_strength_demo.c'], + target='demo_app_vibe_strength', +) +bld.env.FW_APPS.append('demo_app_vibe_strength') + +# vim:filetype=python diff --git a/src/fw/apps/demo/wscript_build b/src/fw/apps/demo/wscript_build new file mode 100644 index 0000000000..12eefde511 --- /dev/null +++ b/src/fw/apps/demo/wscript_build @@ -0,0 +1,130 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.recurse('shared') +if bld.env.CONFIG_DEMO_APP_ACTION_MENU: + bld.recurse('action_menu') +if bld.env.CONFIG_DEMO_APP_ACTIVITY_DEMO: + bld.recurse('activity_demo') +if bld.env.CONFIG_DEMO_APP_ACTIVITY_TEST: + bld.recurse('activity_test') +if bld.env.CONFIG_DEMO_APP_AMB_LIGHT_READ: + bld.recurse('amb_light_read') +if bld.env.CONFIG_DEMO_APP_ANIMATED_DEMO: + bld.recurse('animated_demo') +if bld.env.CONFIG_DEMO_APP_APP_HEAP_DEMO: + bld.recurse('app_heap_demo') +if bld.env.CONFIG_DEMO_APP_BOUNCING_BOX_DEMO: + bld.recurse('bouncing_box_demo') +if bld.env.CONFIG_DEMO_APP_CLICK: + bld.recurse('click') +if bld.env.CONFIG_DEMO_APP_DATA_LOGGING_TEST: + bld.recurse('data_logging_test') +if bld.env.CONFIG_DEMO_APP_DEADLOCK: + bld.recurse('deadlock') +if bld.env.CONFIG_DEMO_APP_DIALOGS: + bld.recurse('dialogs') +if bld.env.CONFIG_DEMO_APP_DOUBLE_TAP_TEST: + bld.recurse('double_tap_test') +if bld.env.CONFIG_DEMO_APP_EVENT_SERVICE: + bld.recurse('event_service') +if bld.env.CONFIG_DEMO_APP_EXIT: + bld.recurse('exit') +if bld.env.CONFIG_DEMO_APP_FLASH_DIAGNOSTIC: + bld.recurse('flash_diagnostic') +if bld.env.CONFIG_DEMO_APP_FLASH_PROF: + bld.recurse('flash_prof') +if bld.env.CONFIG_DEMO_APP_FONT_TEST: + bld.recurse('font_test') +if bld.env.CONFIG_DEMO_APP_FS_RESOURCES: + bld.recurse('fs_resources') +if bld.env.CONFIG_DEMO_APP_FW_UPDATE_PROGRESS_SIM: + bld.recurse('fw_update_progress_sim') +if bld.env.CONFIG_DEMO_APP_GDRAWMASK_DEMO: + bld.recurse('gdrawmask_demo') +if bld.env.CONFIG_DEMO_APP_GFX_TESTS: + bld.recurse('gfx_tests') +if bld.env.CONFIG_DEMO_APP_HRM_DEMO: + bld.recurse('hrm_demo') +if bld.env.CONFIG_DEMO_APP_IDL: + bld.recurse('idl') +if bld.env.CONFIG_DEMO_APP_KILL_BT: + bld.recurse('kill_bt') +if bld.env.CONFIG_DEMO_APP_KINO_LAYER: + bld.recurse('kino_layer') +if bld.env.CONFIG_DEMO_APP_LIGHT_CONFIG: + bld.recurse('light_config') +if bld.env.CONFIG_DEMO_APP_MENU: + bld.recurse('menu') +if bld.env.CONFIG_DEMO_APP_MENU_OVERFLOW: + bld.recurse('menu_overflow') +if bld.env.CONFIG_DEMO_APP_MENU_RIGHT_ICON: + bld.recurse('menu_right_icon') +if bld.env.CONFIG_DEMO_APP_MENU_ROUND: + bld.recurse('menu_round') +if bld.env.CONFIG_DEMO_APP_MORPH_SQUARE: + bld.recurse('morph_square') +if bld.env.CONFIG_DEMO_APP_MOVABLE_LINE: + bld.recurse('movable_line') +if bld.env.CONFIG_DEMO_APP_MPU_TEST: + bld.recurse('mpu_test') +if bld.env.CONFIG_DEMO_APP_MPU_VIOLATION_TEST: + bld.recurse('mpu_violation_test') +if bld.env.CONFIG_DEMO_APP_NUMBER_FIELD: + bld.recurse('number_field') +if bld.env.CONFIG_DEMO_APP_OPTION_MENU: + bld.recurse('option_menu') +if bld.env.CONFIG_DEMO_APP_PEBBLE_COLORS: + bld.recurse('pebble_colors') +if bld.env.CONFIG_DEMO_APP_PEBBLE_SHAPES: + bld.recurse('pebble_shapes') +if bld.env.CONFIG_DEMO_APP_PERSIST: + bld.recurse('persist') +if bld.env.CONFIG_DEMO_APP_PROFILE_MUTEXES: + bld.recurse('profile_mutexes') +if bld.env.CONFIG_DEMO_APP_PROGRESS: + bld.recurse('progress') +if bld.env.CONFIG_DEMO_APP_SCROLL: + bld.recurse('scroll') +if bld.env.CONFIG_DEMO_APP_SIMPLE_MENU: + bld.recurse('simple_menu') +if bld.env.CONFIG_DEMO_APP_STATUSBAR: + bld.recurse('statusbar') +if bld.env.CONFIG_DEMO_APP_STROKE_WIDTH: + bld.recurse('stroke_width') +if bld.env.CONFIG_DEMO_APP_SWAP_LAYER: + bld.recurse('swap_layer') +if bld.env.CONFIG_DEMO_APP_TEMPERATURE_DEMO: + bld.recurse('temperature_demo') +if bld.env.CONFIG_DEMO_APP_TEST_ARGS_RECEIVER: + bld.recurse('test_args_receiver') +if bld.env.CONFIG_DEMO_APP_TEST_ARGS_SENDER: + bld.recurse('test_args_sender') +if bld.env.CONFIG_DEMO_APP_TEST_BLUETOOTH: + bld.recurse('test_bluetooth') +if bld.env.CONFIG_DEMO_APP_TEST_CORE_DUMP: + bld.recurse('test_core_dump') +if bld.env.CONFIG_DEMO_APP_TEST_SYS_TIMER: + bld.recurse('test_sys_timer') +if bld.env.CONFIG_DEMO_APP_TEXT_CLIPPING: + bld.recurse('text_clipping') +if bld.env.CONFIG_DEMO_APP_TEXT_FLOW: + bld.recurse('text_flow') +if bld.env.CONFIG_DEMO_APP_TEXT_LAYOUT: + bld.recurse('text_layout') +if bld.env.CONFIG_DEMO_APP_TEXT_SPACING: + bld.recurse('text_spacing') +if bld.env.CONFIG_DEMO_APP_TIMELINE_PINS: + bld.recurse('timeline_pins') +if bld.env.CONFIG_DEMO_APP_TIMER: + bld.recurse('timer') +if bld.env.CONFIG_DEMO_APP_TRIGGER_ALARM: + bld.recurse('trigger_alarm') +if bld.env.CONFIG_DEMO_APP_VIBE_AND_LOGS: + bld.recurse('vibe_and_logs') +if bld.env.CONFIG_DEMO_APP_VIBE_SCORE: + bld.recurse('vibe_score') +if bld.env.CONFIG_DEMO_APP_VIBE_STRENGTH: + bld.recurse('vibe_strength') + +# vim:filetype=python diff --git a/src/fw/apps/demo_apps/action_menu_demo.h b/src/fw/apps/demo_apps/action_menu_demo.h deleted file mode 100644 index 2b12a8216a..0000000000 --- a/src/fw/apps/demo_apps/action_menu_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* action_menu_demo_get_app_info(void); diff --git a/src/fw/apps/demo_apps/activity_test.h b/src/fw/apps/demo_apps/activity_test.h deleted file mode 100644 index 6bb844bb73..0000000000 --- a/src/fw/apps/demo_apps/activity_test.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* activity_test_get_app_info(void); diff --git a/src/fw/apps/demo_apps/ambient_light_reading_app.c b/src/fw/apps/demo_apps/ambient_light_reading_app.c deleted file mode 100644 index f6d257d2a4..0000000000 --- a/src/fw/apps/demo_apps/ambient_light_reading_app.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "drivers/ambient_light.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" - -#include -#include - -#define AMBIENT_READING_STR_LEN 32 -typedef struct { - Window *window; - TextLayer *text_layer; - char ambient_reading[AMBIENT_READING_STR_LEN]; -} AmbientLightAppData; - -static void prv_populate_amb_read_str(char *str) { - int level = ambient_light_get_light_level(); - snprintf(str, AMBIENT_READING_STR_LEN, "Amb Level:\n %d", level); -} - -static void timer_callback(void *cb_data) { - AmbientLightAppData *data = app_state_get_user_data(); - - prv_populate_amb_read_str(&data->ambient_reading[0]); - layer_mark_dirty(window_get_root_layer(data->window)); - - app_timer_register(500, timer_callback, NULL); -} - -static void handle_init(void) { - AmbientLightAppData *data = task_malloc_check(sizeof(AmbientLightAppData)); - - data->window = window_create(); - - Layer *window_layer = window_get_root_layer(data->window); - GRect bounds = window_layer->bounds; - - data->text_layer = text_layer_create((GRect) - { .origin = { 0, 40 }, .size = { bounds.size.w, 100 } }); - - prv_populate_amb_read_str(&data->ambient_reading[0]); - - text_layer_set_font(data->text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - text_layer_set_text(data->text_layer, data->ambient_reading); - text_layer_set_text_alignment(data->text_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(data->text_layer)); - - app_state_set_user_data(data); - app_window_stack_push(data->window, true); - - app_timer_register(10, timer_callback, NULL); -} - -static void handle_deinit(void) { - AmbientLightAppData *data = app_state_get_user_data(); - text_layer_destroy(data->text_layer); - window_destroy(data->window); - task_free(data); -} - -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -const PebbleProcessMd* ambient_light_reading_get_info() { - static const PebbleProcessMdSystem s_ambient_light_info = { - .common.main_func = s_main, - .name = "Amb Reading" - }; - return (const PebbleProcessMd*) &s_ambient_light_info; -} diff --git a/src/fw/apps/demo_apps/animated_demo.h b/src/fw/apps/demo_apps/animated_demo.h deleted file mode 100644 index 152b9159c9..0000000000 --- a/src/fw/apps/demo_apps/animated_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* animated_demo_get_app_info(); diff --git a/src/fw/apps/demo_apps/app_heap_demo.c b/src/fw/apps/demo_apps/app_heap_demo.c deleted file mode 100644 index 4ad7476d3a..0000000000 --- a/src/fw/apps/demo_apps/app_heap_demo.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_heap_demo.h" -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" - -#include - -// This app allocated approximately 75% of memory available to it. -// The idea is to run it multiple times to show that all data is being freed on -// exit, and is available for the next app to use. - -static TextLayer *text_heap_info; -static Window *window; - -static void init(void) { - char *start = app_malloc_check(1); // Get a pointer close to where the heap starts - - window = window_create(); - app_window_stack_push(window, true /* Animated */); - Layer *window_layer = window_get_root_layer(window); - - text_heap_info = text_layer_create(window_layer->frame); - text_layer_set_text_color(text_heap_info, GColorWhite); - text_layer_set_background_color(text_heap_info, GColorBlack); - text_layer_set_font(text_heap_info, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - - // Get the size of the heap from the beginning of the first thing allocated - unsigned long heap_size = 0x20020000 - (unsigned long)start; - unsigned long alloc_size = 0.75*heap_size; - - char *buf = app_malloc_check(alloc_size); - snprintf(buf, 80, "%luB/%luB\n\nJust allocated %lu%% of the app heap.", alloc_size, heap_size, 100*alloc_size/heap_size); - text_layer_set_text(text_heap_info, buf); - layer_add_child(window_layer, text_layer_get_layer(text_heap_info)); -} - -static void deinit(void) { - // Don't free anything -} - -static void s_main(void) { - init(); - app_event_loop(); - deinit(); -} - -const PebbleProcessMd* app_heap_demo_app_get_info(void) { - static const PebbleProcessMdSystem s_app_heap_demo_app_info = { - .common.main_func = &s_main, - .name = "AppHeap" - }; - return (const PebbleProcessMd*) &s_app_heap_demo_app_info; -} diff --git a/src/fw/apps/demo_apps/app_heap_demo.h b/src/fw/apps/demo_apps/app_heap_demo.h deleted file mode 100644 index e5dadd16fa..0000000000 --- a/src/fw/apps/demo_apps/app_heap_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* app_heap_demo_app_get_info(void); diff --git a/src/fw/apps/demo_apps/bouncing_box.h b/src/fw/apps/demo_apps/bouncing_box.h deleted file mode 100644 index 6cac0593d2..0000000000 --- a/src/fw/apps/demo_apps/bouncing_box.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* bouncing_box_demo_get_app_info(void); - diff --git a/src/fw/apps/demo_apps/click_app.c b/src/fw/apps/demo_apps/click_app.c deleted file mode 100644 index b2cea37908..0000000000 --- a/src/fw/apps/demo_apps/click_app.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "click_app.h" - -#include "applib/app.h" -#include "process_state/app_state/app_state.h" -#include "applib/ui/ui.h" -#include "applib/ui/window.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#include - -#define TEXT_BUFFER_SIZE 64 - -typedef struct { - Window window; - TextLayer text; - char text_buffer[TEXT_BUFFER_SIZE]; -} ClickAppData; - -//////////////////////////// -// Click app's main window - -//! Toggle the colors of the label, so we can see change even if the text stayed the same: -static void toggle_color(Window *window) { - ClickAppData *data = window_get_user_data(window); - TextLayer *text = &data->text; - const GColor bg_color = text->background_color; - if (gcolor_equal(bg_color, GColorBlack)) { - text_layer_set_background_color(text, GColorWhite); - text_layer_set_text_color(text, GColorBlack); - } else { - text_layer_set_background_color(text, GColorBlack); - text_layer_set_text_color(text, GColorWhite); - } -} - -static void raw_click_handler(ClickRecognizerRef recognizer, Window *window, const bool up) { - ClickAppData *data = window_get_user_data(window); - sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, up ? "Raw UP" : "Raw DOWN"); - if (up) { // PBL_LOG requires a fixed const string, so can't use ternary - PBL_LOG(LOG_LEVEL_DEBUG, "Raw UP"); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Raw DOWN"); - } - text_layer_set_text(&data->text, data->text_buffer); - toggle_color(window); - (void)recognizer; -} - -static void raw_up_click_handler(ClickRecognizerRef recognizer, Window *window) { - raw_click_handler(recognizer, window, true); -} - -static void raw_down_click_handler(ClickRecognizerRef recognizer, Window *window) { - raw_click_handler(recognizer, window, false); -} - -static void select_multi_click_handler(ClickRecognizerRef recognizer, Window *window) { - ClickAppData *data = window_get_user_data(window); - const uint16_t count = click_number_of_clicks_counted(recognizer); - sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Multi Click! (%u)\nMin: 2, Max: 10", count); - PBL_LOG(LOG_LEVEL_DEBUG, "Multi Click! (%u)", click_number_of_clicks_counted(recognizer)); - text_layer_set_text(&data->text, data->text_buffer); - toggle_color(window); -} - -static void select_single_click_handler(ClickRecognizerRef recognizer, Window *window) { - ClickAppData *data = window_get_user_data(window); - const uint16_t count = click_number_of_clicks_counted(recognizer); - sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Single Click! (%u)", count); - PBL_LOG(LOG_LEVEL_DEBUG, "Single Click! (%u)", click_number_of_clicks_counted(recognizer)); - text_layer_set_text(&data->text, data->text_buffer); - toggle_color(window); - - // Let's try shortening the repeat interval as we go: - ClickConfig *config = click_recognizer_get_config(recognizer); - config->click.repeat_interval_ms = MAX((config->click.repeat_interval_ms / 2), 100); -} - -static void select_long_click_handler(ClickRecognizerRef recognizer, Window *window) { - ClickAppData *data = window_get_user_data(window); - sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Long Click!"); - PBL_LOG(LOG_LEVEL_DEBUG, "Long Click!"); - text_layer_set_text(&data->text, data->text_buffer); - toggle_color(window); - (void)recognizer; -} - -static void select_long_click_release_handler(ClickRecognizerRef recognizer, Window *window) { - ClickAppData *data = window_get_user_data(window); - sniprintf(data->text_buffer, TEXT_BUFFER_SIZE, "Long Click Released!"); - PBL_LOG(LOG_LEVEL_DEBUG, "Long Click Released!"); - text_layer_set_text(&data->text, data->text_buffer); - toggle_color(window); - (void)recognizer; -} - -static void config_provider(Window *window) { - // See ui/click.h for more information and default values. - - // single click / repeat-on-hold config: - window_single_repeating_click_subscribe(BUTTON_ID_SELECT, 1000, (ClickHandler)select_single_click_handler); // "hold-to-repeat" gets overriden if there's a long click handler configured! - - // multi click config: - window_multi_click_subscribe(BUTTON_ID_SELECT, 2, 10, 0, false, (ClickHandler) select_multi_click_handler); - - // long click config: - window_long_click_subscribe(BUTTON_ID_SELECT, 700, (ClickHandler) select_long_click_handler, (ClickHandler) select_long_click_release_handler); - - // single click / repeat-on-hold config: - window_single_repeating_click_subscribe(BUTTON_ID_UP, 1000, (ClickHandler) select_single_click_handler); // "hold-to-repeat" gets overriden if there's a long click handler configured! - - // multi click config: - window_multi_click_subscribe(BUTTON_ID_UP, 2, 10, 0, true, (ClickHandler) select_multi_click_handler); - - // raw: - window_raw_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) raw_down_click_handler, (ClickHandler) raw_up_click_handler, NULL); -} - -static void prv_window_load(Window *window) { - ClickAppData *data = window_get_user_data(window); - TextLayer *text = &data->text; - text_layer_init(text, &window->layer.bounds); - text_layer_set_text(text, "Use select button and try different clicks: single, hold-to-repeat, multiple, long press, etc.\n\nNOTE: a long click config will override hold-to-repeat config. Comment out the long_click section of the config to enable hold-to-repeat."); - layer_add_child(&window->layer, &text->layer); -} - -static void push_window(ClickAppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Click Demo")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - window_set_click_config_provider(window, (ClickConfigProvider) config_provider); - const bool animated = true; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void handle_init(void) { - ClickAppData *data = (ClickAppData*) app_malloc_check(sizeof(ClickAppData)); - if (data == NULL) { - PBL_CROAK("Out of memory"); - } - app_state_set_user_data(data); - push_window(data); -} - -static void handle_deinit(void) { - ClickAppData *data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* click_app_get_info() { - static const PebbleProcessMdSystem s_click_app_info = { - .common.main_func = s_main, - .name = "Clicks" - }; - return (const PebbleProcessMd*) &s_click_app_info; -} - -#undef TEXT_BUFFER_SIZE diff --git a/src/fw/apps/demo_apps/click_app.h b/src/fw/apps/demo_apps/click_app.h deleted file mode 100644 index 4d0b126543..0000000000 --- a/src/fw/apps/demo_apps/click_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* click_app_get_info(); diff --git a/src/fw/apps/demo_apps/data_logging_test.h b/src/fw/apps/demo_apps/data_logging_test.h deleted file mode 100644 index 0a030718cd..0000000000 --- a/src/fw/apps/demo_apps/data_logging_test.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* data_logging_test_get_info(); diff --git a/src/fw/apps/demo_apps/deadlock_app.c b/src/fw/apps/demo_apps/deadlock_app.c deleted file mode 100644 index 2d21779895..0000000000 --- a/src/fw/apps/demo_apps/deadlock_app.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "profile_mutexes_app.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" - -#include "system/logging.h" -#include "os/mutex.h" -#include "system/profiler.h" - -#include "kernel/util/sleep.h" -#include "services/common/new_timer/new_timer.h" - -static Window *window; -static PebbleMutex *s_mutex; -static PebbleMutex *s_mutex2; - -static void callback(void *data) { - PBL_LOG(LOG_LEVEL_DEBUG, "Locking mutex 2 (new timer)"); - mutex_lock(s_mutex2); - PBL_LOG(LOG_LEVEL_DEBUG, "Locking mutex 1 (new timer)"); - mutex_lock(s_mutex); -} - -static void deadlock(void) { - s_mutex = mutex_create(); - s_mutex2 = mutex_create(); - TimerID timer = new_timer_create(); - new_timer_start(timer, 10, callback, NULL, 0); - - PBL_LOG(LOG_LEVEL_DEBUG, "Locking mutex 1"); - mutex_lock(s_mutex); - psleep(20); - PBL_LOG(LOG_LEVEL_DEBUG, "Locking mutex 2"); - mutex_lock(s_mutex2); -} - -static void s_main(void) { - window = window_create(); - app_window_stack_push(window, true /* Animated */); - - deadlock(); - - app_event_loop(); -} - -const PebbleProcessMd* deadlock_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Deadlock" - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/deadlock_app.h b/src/fw/apps/demo_apps/deadlock_app.h deleted file mode 100644 index 8a50d9c4f2..0000000000 --- a/src/fw/apps/demo_apps/deadlock_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* deadlock_get_app_info(void); diff --git a/src/fw/apps/demo_apps/dialogs_demo.h b/src/fw/apps/demo_apps/dialogs_demo.h deleted file mode 100644 index b17b69e2aa..0000000000 --- a/src/fw/apps/demo_apps/dialogs_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* dialogs_demo_get_app_info(); diff --git a/src/fw/apps/demo_apps/double_tap_test.h b/src/fw/apps/demo_apps/double_tap_test.h deleted file mode 100644 index 53ca2feee6..0000000000 --- a/src/fw/apps/demo_apps/double_tap_test.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* double_tap_test_get_info(); diff --git a/src/fw/apps/demo_apps/event_service_app.c b/src/fw/apps/demo_apps/event_service_app.c deleted file mode 100644 index 7ea03c4298..0000000000 --- a/src/fw/apps/demo_apps/event_service_app.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "text_layout.h" - -#include "applib/accel_service.h" -#include "applib/app.h" -#include "applib/connection_service.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" - -#include -#include - -typedef struct { - Window window; - TextLayer count_layer; - TextLayer connected_layer; - char count_str[10]; - int count; -} EventServiceAppData; - -static void handle_tap(AccelAxisType axis, int32_t sign) { - EventServiceAppData *data = app_state_get_user_data(); - ++data->count; - snprintf(data->count_str, 10, "%d", data->count); - text_layer_set_text(&data->count_layer, data->count_str); -} - -static void handle_bt_connection(bool connected) { - EventServiceAppData *data = app_state_get_user_data(); - text_layer_set_text(&data->connected_layer, connected ? "connected" : "disconnected"); -} - -static void handle_deinit(void) { - EventServiceAppData *data = app_state_get_user_data(); - app_free(data); - - accel_tap_service_unsubscribe(); - connection_service_unsubscribe(); -} - -static void handle_init(void) { - EventServiceAppData *data = app_malloc_check(sizeof(EventServiceAppData)); - memset(data, 0, sizeof(EventServiceAppData)); - app_state_set_user_data(data); - - // Init window - window_init(&data->window, "Event Service Demo"); - app_window_stack_push(&data->window, true /* Animated */); - - // Init text layer - Layer *windowlayer = &data->window.layer; - const int16_t width = windowlayer->bounds.size.w - ACTION_BAR_WIDTH - 6; - text_layer_init(&data->count_layer, &GRect(0, 0, width, 20)); - layer_add_child(&data->window.layer, &data->count_layer.layer); - text_layer_init(&data->connected_layer, &GRect(0, 20, width, 20)); - layer_add_child(&data->window.layer, &data->connected_layer.layer); - - text_layer_set_text(&data->count_layer, "No Presses"); - text_layer_set_text(&data->connected_layer, "No connection event"); - - // subscribe to the accelerometer event stream - accel_tap_service_subscribe(&handle_tap); - ConnectionHandlers conn_handlers = { - .pebble_app_connection_handler = handle_bt_connection - }; - - connection_service_subscribe(conn_handlers); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* event_service_app_get_info() { - static const PebbleProcessMdSystem event_service_app_info = { - .common.main_func = &s_main, - .name = "Event Service App", - }; - return (const PebbleProcessMd*) &event_service_app_info; -} diff --git a/src/fw/apps/demo_apps/event_service_app.h b/src/fw/apps/demo_apps/event_service_app.h deleted file mode 100644 index 83a1970093..0000000000 --- a/src/fw/apps/demo_apps/event_service_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* event_service_app_get_info(); diff --git a/src/fw/apps/demo_apps/exit_app.c b/src/fw/apps/demo_apps/exit_app.c deleted file mode 100644 index 861a45bafa..0000000000 --- a/src/fw/apps/demo_apps/exit_app.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "exit_app.h" -#include "applib/app_logging.h" - -// Verify that applications can actually call stdlib's exit(). -static void s_exit_app_main(void) { - - // Make visible in the debugger if we are running privileged. - uint32_t value; - __asm volatile("mrs %0, control" : "=r" (value)); - volatile bool is_privileged = !(value & 0x1); - APP_LOG(LOG_LEVEL_DEBUG, "Exit app is %sprivileged; now exiting", is_privileged ? "" : "not "); -} - -const PebbleProcessMd *exit_app_get_app_info(void) { - static const PebbleProcessMdSystem s_exit_app_info = { - .common.main_func = s_exit_app_main, - .common.is_unprivileged = true, - .name = "Exit Test" - }; - return (const PebbleProcessMd*) &s_exit_app_info; -} - diff --git a/src/fw/apps/demo_apps/exit_app.h b/src/fw/apps/demo_apps/exit_app.h deleted file mode 100644 index 01f3d5795a..0000000000 --- a/src/fw/apps/demo_apps/exit_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *exit_app_get_app_info(); diff --git a/src/fw/apps/demo_apps/flash_demo.c b/src/fw/apps/demo_apps/flash_demo.c deleted file mode 100644 index 90805af6fe..0000000000 --- a/src/fw/apps/demo_apps/flash_demo.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "flash_demo.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "system/logging.h" - -static Window *window; - -#define BASE_ADDRESS 0x380000 - -static void test_write_short(void) { - uint16_t buffer; - flash_read_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Addr 0x%x is 0x%"PRIx16, BASE_ADDRESS, buffer); - - buffer = 0x0505; - flash_write_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Addr 0x%x Written to 0x%x", BASE_ADDRESS, buffer); - - uint8_t read_buffer = 0x0; - flash_read_bytes((uint8_t*) &read_buffer, BASE_ADDRESS, sizeof(read_buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Addr 0x%x is (8) 0x%"PRIx8, BASE_ADDRESS, read_buffer); - - buffer = 0x0; - flash_read_bytes((uint8_t*) &buffer, BASE_ADDRESS, sizeof(buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Addr 0x%x is (16) 0x%"PRIx16, BASE_ADDRESS, buffer); -} - -static void test_write_bytes(void) { - for (int i = 1; i < 127; ++i) { - uint8_t data = i; - flash_write_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Wrote Addr 0x%x is 0x%"PRIx8, i, data); - } - - for (int i = 0; i < 128; ++i) { - uint8_t data = 0; - flash_read_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Read Addr 0x%x is (8) 0x%"PRIx8, i, data); - } -} - -static void test_write_block(void) { - uint8_t data[64]; - - for (unsigned int i = 0; i < sizeof(data); ++i) { - data[i] = i; - } - - flash_write_bytes(data, BASE_ADDRESS + 31, sizeof(data)); - - for (int i = 0; i < 128; ++i) { - uint8_t data = 0; - flash_read_bytes((uint8_t*) &data, BASE_ADDRESS + i, sizeof(data)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Read Addr 0x%x is (8) 0x%"PRIx8, i, data); - } -} - -static void do_flash_operation(void) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Flash operation time!"); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Flash operation time!"); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Flash operation time!"); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Flash operation time!"); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Flash operation time!"); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Flash operation time!"); - - PBL_LOG(LOG_LEVEL_DEBUG, ">> Erasing 0x%x", BASE_ADDRESS); - flash_erase_sector_blocking(BASE_ADDRESS); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Erasing 0x%x Done", BASE_ADDRESS); - - test_write_short(); -} - -static void s_main(void) { - window = window_create(); - app_window_stack_push(window, true /* Animated */); - - do_flash_operation(); - - app_event_loop(); -} - -const PebbleProcessMd* flash_demo_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Flash Demo" - }; - - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/flash_demo.h b/src/fw/apps/demo_apps/flash_demo.h deleted file mode 100644 index bf264af75a..0000000000 --- a/src/fw/apps/demo_apps/flash_demo.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* flash_demo_get_app_info(void); - diff --git a/src/fw/apps/demo_apps/flash_diagnostic_app.c b/src/fw/apps/demo_apps/flash_diagnostic_app.c deleted file mode 100644 index 23383f7e4d..0000000000 --- a/src/fw/apps/demo_apps/flash_diagnostic_app.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "flash_diagnostic_app.h" - -#include - -#include "applib/app.h" -#include "applib/ui/simple_menu_layer.h" -#include "applib/ui/ui.h" -#include "applib/ui/window.h" -#include "drivers/flash.h" -#include "drivers/task_watchdog.h" -#include "flash_region/flash_region.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "resource/resource_storage_flash.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/sleep.h" -#include "util/size.h" - -#define NUM_REGIONS ARRAY_LENGTH(s_flash_regions) - -#define FILE_WRITE_STRESS (NUM_REGIONS) -#define FILE_SUBSECTOR_STRESS (FILE_WRITE_STRESS + 1) -#define NUM_STRESS_TESTS (FILE_SUBSECTOR_STRESS - NUM_REGIONS + 1) - -#define NUM_MENU_ITEMS (NUM_REGIONS + NUM_STRESS_TESTS) - -struct Region { - char *name; - uint32_t begin; - uint32_t end; -}; - -static struct Region s_flash_regions[] = { - { - "System Resources", - FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN, - FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END - }, - { - "System Resources", - FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN, - FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END - }, - { - "File System", - FLASH_REGION_FILESYSTEM_BEGIN, - FLASH_REGION_FILESYSTEM_END - }, -}; - -typedef struct { - Window window; - SimpleMenuLayer menu_layer; - SimpleMenuSection menu_section; - SimpleMenuItem menu_items[NUM_MENU_ITEMS]; -} FlashDiagAppData; - -typedef struct { - Window window; - TextLayer *text_layer; - int stress_iteration; - int stress_index; -} FlashStressWindow; - - -static bool check_region_erased(struct Region region) { - PBL_LOG_SYNC(LOG_LEVEL_INFO, "Checking Erase ..."); - bool success = true; - for (uint32_t i = region.begin; i < region.end; i += sizeof(uint32_t)) { - uint32_t read = 0; - flash_read_bytes((uint8_t *)&read, i, sizeof(read)); - if (read != 0xffffffff) { - PBL_LOG_SYNC(LOG_LEVEL_INFO, ">>>> Address 0x%lx failed to erase: 0x%lx", i, read); - success = false; - } - } - - return success; -} - -// region: region to check (and possibly write) -// use_rand: write random values -// perform_writes: perform writes if true, else see if the region reads as 0 -static bool check_region_write(struct Region region, bool use_rand, - bool perform_writes) { - bool success = true; - uint32_t write_rand = (use_rand && perform_writes) ? rand() : 0; - - PBL_LOG_SYNC(LOG_LEVEL_INFO, "%sChecking 0x%lx over 0x%lx 0x%lx", - (perform_writes) ? "Writing and " : "", write_rand, region.begin, - region.end); - - for (uint32_t i = region.begin; i < region.end; i += sizeof(uint32_t)) { - uint32_t write = write_rand; - uint32_t read = 0xffff; - if (perform_writes) { - flash_write_bytes((uint8_t *)&write, i, sizeof(write)); - } - flash_read_bytes((uint8_t *)&read, i, sizeof(read)); - if (read != write) { - PBL_LOG_SYNC(LOG_LEVEL_INFO, ">>>> Address 0x%lx failed to write: 0x%lx 0x%lx", - i, read, write); - success = false; - } - } - - return success; -} - -// Writes 0's to the first half of a flash sector and confirms that everything -// reads as 0. Then uses 8 subsector erases to erase the second half of the -// sector. Then re-reads the first half to see if any bits have flipped -static bool check_subsector_bitflip(struct Region region) { -#if !CAPABILITY_USE_PARALLEL_FLASH - bool success = true; - - if (((region.end - region.begin) % (64 * 1024)) != 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Test only works on 64k aligned regions"); - return (false); - } - - const uint32_t block_size = 64 * 1024; - const uint32_t write_size = 32 * 1024; - const uint32_t subsector_size = 4 * 1024; - - for (uint32_t i = region.begin; i < region.end; i += block_size) { - - struct Region write_region; - write_region.begin = i; - write_region.end = i + write_size; - - if (!check_region_write(write_region, false, true)) { - success = false; - break; - } - - uint32_t subsec_begin = block_size - write_size; - PBL_ASSERTN((subsec_begin % (32*1024)) == 0); - - for (uint32_t subsec = subsec_begin; subsec < block_size; - subsec += subsector_size) { - uint32_t erase = subsec + i; - PBL_ASSERTN((erase % (4 * 1024)) == 0); - PBL_LOG_SYNC(LOG_LEVEL_INFO, "Subsector Erase of 0x%lx", erase); - flash_erase_subsector_blocking(erase); - } - - if (!check_region_write(write_region, false, false)) { - success = false; - break; - } - psleep(5); - } - - return (success); -#else - PBL_LOG_SYNC(LOG_LEVEL_INFO, "Test not supported for parallel flash"); - return (false); -#endif -} - -static void menu_select_callback(int index, void *data) { - struct Region region = s_flash_regions[index]; - PBL_LOG(LOG_LEVEL_INFO, ">>>> Erase %s", region.name); - flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); - PBL_LOG(LOG_LEVEL_INFO, ">>>> Checking '%s' is erased", region.name); - check_region_erased(region); - PBL_LOG(LOG_LEVEL_INFO, ">>>> Checking '%s' can write", region.name); - check_region_write(region, false, true); - PBL_LOG(LOG_LEVEL_INFO, ">>>> Done!"); -} - -FlashStressWindow stress_data; -static bool abort_stress_test; -static void update_text(int iter, int tot, bool failed) { - static char status[50]; - - snprintf(status, sizeof(status), "%d / %d %s", iter, tot, - failed ? "Failed Out" : "Complete"); - text_layer_set_text(stress_data.text_layer, (char *)status); -} - -static void app_timer_cb(void *data) { - static const int num_stress_iters = 1000; - struct Region region = s_flash_regions[2]; - PBL_LOG(LOG_LEVEL_INFO, ">>>> %s %d", "Test Loop", stress_data.stress_iteration); - - PBL_LOG(LOG_LEVEL_INFO, "Erasing 0x%lx to 0x%lx", region.begin, region.end); - flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); - - bool failed = true; - if (stress_data.stress_index == FILE_WRITE_STRESS) { - failed = (!check_region_erased(region) || !check_region_write(region, true, true)); - } else if (stress_data.stress_index == FILE_SUBSECTOR_STRESS) { - failed = (!check_region_erased(region) || !check_subsector_bitflip(region)); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Unknown stress test %d!", stress_data.stress_index); - } - - if (!abort_stress_test) { - update_text(++stress_data.stress_iteration, num_stress_iters, failed); - - if (!failed && (stress_data.stress_iteration < num_stress_iters)) { - app_timer_register(1000, app_timer_cb, NULL); // allow for animation to complete - } else { // clean up state - flash_region_erase_optimal_range(region.begin, region.begin, region.end, region.end); - } - } -} - -static void stress_window_load(Window *data) { - Layer *layer = window_get_root_layer(data); - const int16_t width = layer->frame.size.w - ACTION_BAR_WIDTH - 3; - - stress_data.text_layer = text_layer_create(GRect(4, 44, width, 60)); - TextLayer *text_layer = stress_data.text_layer; - text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - text_layer_set_background_color(text_layer, GColorClear); - layer_add_child(layer, text_layer_get_layer(text_layer)); - - text_layer_set_text(stress_data.text_layer, "Starting Stress Test"); - abort_stress_test = false; - app_timer_register(500, app_timer_cb, NULL); -}; - -static void stress_window_unload(Window *data) { - abort_stress_test = true; -} - -static void file_system_stress_callback(int index, void *data) { - stress_data.stress_iteration = 0; - stress_data.stress_index = index; - window_init(&stress_data.window, WINDOW_NAME("Stress Test")); - window_set_user_data(&stress_data.window, &stress_data); - window_set_window_handlers(&stress_data.window, &(WindowHandlers) { - .load = stress_window_load, - .unload = stress_window_unload - }); - app_window_stack_push(&stress_data.window, true); -} - -static void populate_menu(SimpleMenuSection *menu_section, SimpleMenuItem *menu_items) { - for (unsigned int i = 0; i < NUM_REGIONS; ++i) { - menu_items[i] = (SimpleMenuItem) { - .title = s_flash_regions[i].name, - .callback = menu_select_callback, - }; - } - menu_items[FILE_WRITE_STRESS] = (SimpleMenuItem) { - .title = "File Stress", - .callback = file_system_stress_callback, - }; - menu_items[FILE_SUBSECTOR_STRESS] = (SimpleMenuItem) { - .title = "Subsector Stress", - .callback = file_system_stress_callback, - }; - - menu_section->num_items = NUM_MENU_ITEMS; - menu_section->items = menu_items; - menu_section->title = "Flash Regions"; -} - -static void prv_window_load(Window *window) { - FlashDiagAppData *data = window_get_user_data(window); - populate_menu(&data->menu_section, data->menu_items); - Layer *root_layer = window_get_root_layer(window); - const GRect *bounds = &root_layer->bounds; - simple_menu_layer_init(&data->menu_layer, bounds, window, &data->menu_section, 1, NULL); - layer_add_child(root_layer, simple_menu_layer_get_layer(&data->menu_layer)); -} - -static void push_window(FlashDiagAppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Flash Diagnostic")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate -static void handle_init(void) { - FlashDiagAppData *data = (FlashDiagAppData*) app_malloc_check(sizeof(FlashDiagAppData)); - if (data == NULL) { - PBL_CROAK("Out of memory"); - } - app_state_set_user_data(data); - push_window(data); -} - -static void handle_deinit(void) { - FlashDiagAppData *data = app_state_get_user_data(); - simple_menu_layer_deinit(&data->menu_layer); - app_free(data); -} - -static void s_main(void) { - if (resource_storage_flash_get_unused_bank()->begin == - s_flash_regions[0].begin) { - s_flash_regions[0].name = "Unused Resources"; - } else { - s_flash_regions[1].name = "Unused Resources"; - } - - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* flash_diagnostic_app_get_info() { - static const PebbleProcessMdSystem s_flash_diagnostic_app_info = { - .common.main_func = s_main, - .name = "Flash Diagnostic" - }; - return (const PebbleProcessMd*) &s_flash_diagnostic_app_info; -} diff --git a/src/fw/apps/demo_apps/flash_diagnostic_app.h b/src/fw/apps/demo_apps/flash_diagnostic_app.h deleted file mode 100644 index 42bff5c09a..0000000000 --- a/src/fw/apps/demo_apps/flash_diagnostic_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* flash_diagnostic_app_get_info(); diff --git a/src/fw/apps/demo_apps/flash_prof.c b/src/fw/apps/demo_apps/flash_prof.c deleted file mode 100644 index 105aad4369..0000000000 --- a/src/fw/apps/demo_apps/flash_prof.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "process_management/pebble_process_md.h" -#include "applib/app.h" -#include "system/logging.h" -#include "drivers/flash.h" -#include "drivers/rtc.h" -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "kernel/pbl_malloc.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/number_window.h" -#include "applib/ui/window_stack.h" - -#include "FreeRTOS.h" - -static NumberWindow number_window; - -static uint32_t timed_read_bytes(uint32_t num_bytes) { - uint8_t *buffer = kernel_malloc_check(num_bytes); - time_t start_time_s; - uint16_t start_time_ms; - rtc_get_time_ms(&start_time_s, &start_time_ms); - flash_read_bytes(buffer, FLASH_REGION_FILESYSTEM_BEGIN, num_bytes); - time_t stop_time_s; - uint16_t stop_time_ms; - rtc_get_time_ms(&stop_time_s, &stop_time_ms); - kernel_free(buffer); - return ((stop_time_s * 1000 + stop_time_ms) - (start_time_s * 1000 + start_time_ms)); -} - -static void do_timed_read(NumberWindow *nw, void *data) { - uint32_t num_bytes = nw->value; - uint32_t predicted_time = num_bytes * 8 / 16000; - uint32_t time = timed_read_bytes(num_bytes); - PBL_LOG(LOG_LEVEL_DEBUG, "time to read %lu bytes: predicted %lu, actual %lu", num_bytes, predicted_time, time); - window_stack_remove(&number_window.window, false); - app_window_stack_push(&number_window.window, true); -} - -#define NUM_BYTES 1000 - -static void handle_init(void) { - number_window_init(&number_window, "Num Writes", (NumberWindowCallbacks) { - .selected = (NumberWindowCallback) do_timed_read, - }, NULL); - number_window_set_min(&number_window, 1000); - number_window_set_max(&number_window, 1000000); - number_window_set_step_size(&number_window, 1000); - app_window_stack_push((Window *)&number_window, true); -} - -static void handle_deinit(void) { - -} - -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -const PebbleProcessMd* flash_prof_get_app_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Flash Prof" - }; - - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/flash_prof.h b/src/fw/apps/demo_apps/flash_prof.h deleted file mode 100644 index c969de5baa..0000000000 --- a/src/fw/apps/demo_apps/flash_prof.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* flash_prof_get_app_info(); diff --git a/src/fw/apps/demo_apps/flash_test.c b/src/fw/apps/demo_apps/flash_test.c deleted file mode 100644 index bcf0bc8d48..0000000000 --- a/src/fw/apps/demo_apps/flash_test.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -// This app only makes sense on Snowy, as it uses addresses and sector sizes that only make sense -// on our parallel flash hardware -#if CAPABILITY_USE_PARALLEL_FLASH - -#include "flash_test.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/simple_menu_layer.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "applib/ui/window_stack.h" - - -#include "mfg/mfg_apps/mfg_flash_test.h" -#include "process_state/app_state/app_state.h" -#include "services/common/system_task.h" -#include "system/logging.h" - -#include "kernel/pbl_malloc.h" - -#include - -enum FlashTestCaseStatus { - FLASH_TEST_STATUS_INIT = 0, - FLASH_TEST_STATUS_RUNNING = 1, - FLASH_TEST_STATUS_STOPPED = 2, - FLASH_TEST_STATUS_PASSED = 3, - FLASH_TEST_STATUS_FAILED = 4, -}; - -#define STATUS_TEXT_SIZE 18 -struct FlashTestData { - Window window; - SimpleMenuLayer simple_menu_layer; - SimpleMenuSection menu_sections[1]; - SimpleMenuItem menu_items[FLASH_TEST_CASE_NUM_MENU_ITEMS]; - Window test_window; - TextLayer msg_text_layer[3]; - TextLayer status_text_layer; - char status_text[STATUS_TEXT_SIZE]; - FlashTestCaseType test_case; - uint8_t test_case_status; -}; - -/***********************************************************/ -/******************* Window Related Functions **************/ -/***********************************************************/ -static void handle_timer(struct tm *tick_time, TimeUnits units_changed) { - struct FlashTestData *data = app_state_get_user_data(); - // simply marking the window dirty will make everything update - if (data && (data->test_case_status != FLASH_TEST_STATUS_INIT)) - { - layer_mark_dirty(&data->test_window.layer); - } -} - -static void update_test_case_status(struct FlashTestData *data) { - switch (data->test_case_status) { - case FLASH_TEST_STATUS_INIT: - snprintf(data->status_text, STATUS_TEXT_SIZE, "Test Initialized"); - break; - case FLASH_TEST_STATUS_RUNNING: - snprintf(data->status_text, STATUS_TEXT_SIZE, "Test Running"); - break; - case FLASH_TEST_STATUS_STOPPED: - snprintf(data->status_text, STATUS_TEXT_SIZE, "Test Stopped"); - break; - case FLASH_TEST_STATUS_PASSED: - snprintf(data->status_text, STATUS_TEXT_SIZE, "Test Passed"); - break; - case FLASH_TEST_STATUS_FAILED: - snprintf(data->status_text, STATUS_TEXT_SIZE, "Test Failed"); - break; - default: - snprintf(data->status_text, STATUS_TEXT_SIZE, "Unknown Status"); - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Unknown Test Case Selected"); - break; - } - - text_layer_set_text(&data->status_text_layer, data->status_text); -} - -static void test_window_load(Window *window) { - struct FlashTestData *data = app_state_get_user_data(); - - window_set_background_color(window, GColorBlack); - - TextLayer *msg_text_layer = &data->msg_text_layer[0]; - text_layer_init(msg_text_layer, &GRect(0, 12, window->layer.bounds.size.w, 18)); - switch (data->test_case) { - case FLASH_TEST_CASE_RUN_DATA_TEST: - text_layer_set_text(msg_text_layer, "Data Bus Test"); - break; - case FLASH_TEST_CASE_RUN_ADDR_TEST: - text_layer_set_text(msg_text_layer, "Addr Bus Test"); - break; - case FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST: - text_layer_set_text(msg_text_layer, "Stress Addr Test"); - break; - case FLASH_TEST_CASE_RUN_PERF_DATA_TEST: - text_layer_set_text(msg_text_layer, "Perf Data Test"); - break; - case FLASH_TEST_CASE_RUN_SWITCH_MODE_ASYNC: - case FLASH_TEST_CASE_RUN_SWITCH_MODE_SYNC_BURST: - text_layer_set_text(msg_text_layer, "Switch Mode"); - break; - default: - text_layer_set_text(msg_text_layer, "Unknown Test"); - break; - } - - text_layer_set_text_alignment(msg_text_layer, GTextAlignmentCenter); - text_layer_set_background_color(msg_text_layer, GColorBlack); - text_layer_set_text_color(msg_text_layer, GColorWhite); - text_layer_set_font(msg_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); - layer_add_child(&window->layer, &msg_text_layer->layer); - - msg_text_layer = &data->msg_text_layer[1]; - text_layer_init(msg_text_layer, &GRect(0, 32, window->layer.bounds.size.w, 18)); - if (data->test_case >= FLASH_TEST_CASE_RUN_SWITCH_MODE_ASYNC) { - text_layer_set_text(msg_text_layer, "Select To Confirm Switch"); - } - else { - text_layer_set_text(msg_text_layer, "Press Select To Start"); - } - text_layer_set_text_alignment(msg_text_layer, GTextAlignmentCenter); - text_layer_set_background_color(msg_text_layer, GColorBlack); - text_layer_set_text_color(msg_text_layer, GColorWhite); - text_layer_set_font(msg_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); - layer_add_child(&window->layer, &msg_text_layer->layer); - - msg_text_layer = &data->msg_text_layer[2]; - text_layer_init(msg_text_layer, &GRect(0, 64, window->layer.bounds.size.w, 40)); - if (data->test_case >= FLASH_TEST_CASE_RUN_SWITCH_MODE_ASYNC) { - text_layer_set_text(msg_text_layer, "Press Back To Exit"); - } - else { - text_layer_set_text(msg_text_layer, "Press Up to Exit, Down to Stop Test"); - } - text_layer_set_text_alignment(msg_text_layer, GTextAlignmentCenter); - text_layer_set_background_color(msg_text_layer, GColorBlack); - text_layer_set_text_color(msg_text_layer, GColorWhite); - text_layer_set_font(msg_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); - layer_add_child(&window->layer, &msg_text_layer->layer); - - TextLayer *status_text_layer = &data->status_text_layer; - text_layer_init(status_text_layer, &GRect(0, 106, window->layer.bounds.size.w, 40)); - text_layer_set_text_alignment(status_text_layer, GTextAlignmentCenter); - text_layer_set_background_color(status_text_layer, GColorBlack); - text_layer_set_text_color(status_text_layer, GColorWhite); - text_layer_set_font(status_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); - layer_add_child(&window->layer, &status_text_layer->layer); - - update_test_case_status(data); -} - -void flash_test_dismiss_window(void) { - struct FlashTestData *data = app_state_get_user_data(); - - const bool animated = true; - window_stack_remove(&data->test_window, animated); -} - -static void up_click_handler(ClickRecognizerRef recognizer, void *unusued) { - struct FlashTestData *data = app_state_get_user_data(); - - if (data->test_case_status != FLASH_TEST_STATUS_RUNNING) { - flash_test_dismiss_window(); - } -} - -static void down_click_handler(ClickRecognizerRef recognizer, void *unusued) { - struct FlashTestData *data = app_state_get_user_data(); - - // Only stop stress test - if ((data->test_case == FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST) && (data->test_case_status == FLASH_TEST_STATUS_RUNNING)) { - data->test_case_status = FLASH_TEST_STATUS_STOPPED; - stop_flash_test_case(); - update_test_case_status(data); - } -} - -static void run_test(void* context) { - struct FlashTestData *data = app_state_get_user_data(); - FlashTestErrorType status = FLASH_TEST_SUCCESS; - - // Execute test - pass in 0 by default for iterations - status = run_flash_test_case(data->test_case, 0); - - if (status == FLASH_TEST_SUCCESS) { - data->test_case_status = FLASH_TEST_STATUS_PASSED; - } - else { - PBL_LOG(LOG_LEVEL_DEBUG, ">>>>>FAILED TEST CASE<<<<<"); - data->test_case_status = FLASH_TEST_STATUS_FAILED; - } - - update_test_case_status(data); -} - -static void select_click_handler(ClickRecognizerRef recognizer, void *unusued) { - struct FlashTestData *data = app_state_get_user_data(); - - if ((data->test_case == FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST) && (data->test_case_status == FLASH_TEST_STATUS_RUNNING)) { - data->test_case_status = FLASH_TEST_STATUS_STOPPED; - stop_flash_test_case(); - update_test_case_status(data); - } - else if (data->test_case_status == FLASH_TEST_STATUS_INIT) { - data->test_case_status = FLASH_TEST_STATUS_RUNNING; - update_test_case_status(data); - system_task_add_callback(run_test, NULL); - } -} - -static void click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler)up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler)down_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, (ClickHandler)select_click_handler); -} - -static void flash_test_select_callback(int index, void *context) { - struct FlashTestData *data = (struct FlashTestData *) context; - - data->test_case = index; - data->test_case_status = FLASH_TEST_STATUS_INIT; - - // Display window for running test case - // Init the window - Window *test_window = &data->test_window; - window_init(test_window, WINDOW_NAME("Test Case")); - window_set_window_handlers(test_window, &(WindowHandlers) { - .load = test_window_load, - }); - window_set_user_data(test_window, data); - window_set_click_config_provider(test_window, click_config_provider); - - const bool animated = true; - app_window_stack_push(test_window, animated); -} - -static void flash_test_window_load(Window *window) { - struct FlashTestData *data = (struct FlashTestData*) window_get_user_data(window); - - // Configure menu items: - uint8_t num_items = 0; - data->menu_items[num_items++] = (SimpleMenuItem) { .title = "Run Data Test", - .callback = flash_test_select_callback}; - data->menu_items[num_items++] = (SimpleMenuItem) { .title = "Run Address Test", - .callback = flash_test_select_callback}; - data->menu_items[num_items++] = (SimpleMenuItem) { .title = "Run Stress Test", - .callback = flash_test_select_callback}; - data->menu_items[num_items++] = (SimpleMenuItem) { .title = "Run Perf Data Test", - .callback = flash_test_select_callback}; - data->menu_items[num_items++] = (SimpleMenuItem) { .title = "-->Async Mode", - .callback = flash_test_select_callback}; - data->menu_items[num_items++] = (SimpleMenuItem) { .title = "-->Sync Burst Mode", - .callback = flash_test_select_callback}; - - data->menu_sections[0].num_items = num_items; - data->menu_sections[0].items = data->menu_items; - - // Configure simple menu: - const GRect *bounds = &data->window.layer.bounds; - simple_menu_layer_init(&data->simple_menu_layer, bounds, window, data->menu_sections, 1, data); - layer_add_child(&data->window.layer, simple_menu_layer_get_layer(&data->simple_menu_layer)); -} - -static void handle_init(void) { - struct FlashTestData* data_ptr = app_malloc_check(sizeof(struct FlashTestData)); - - memset(data_ptr, 0, sizeof(struct FlashTestData)); - - app_state_set_user_data(data_ptr); - - window_init(&data_ptr->window, WINDOW_NAME("Flash Test")); - window_set_user_data(&data_ptr->window, data_ptr); - window_set_window_handlers(&data_ptr->window, &(WindowHandlers){ - .load = flash_test_window_load, - }); - - const bool animated = true; - app_window_stack_push(&data_ptr->window, animated); - - tick_timer_service_subscribe(SECOND_UNIT, handle_timer); -} - -static void handle_deinit(void) { - struct FlashTestData *data = app_state_get_user_data(); - simple_menu_layer_deinit(&data->simple_menu_layer); - app_free(data); - - stop_flash_test_case(); -} - - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* flash_test_demo_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = s_main, - .name = "Flash Test" - }; - return (const PebbleProcessMd*) &s_app_info; -} - -#endif // CAPABILITY_USE_PARALLEL_FLASH diff --git a/src/fw/apps/demo_apps/flash_test.h b/src/fw/apps/demo_apps/flash_test.h deleted file mode 100644 index cd8208de73..0000000000 --- a/src/fw/apps/demo_apps/flash_test.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* flash_test_demo_get_app_info(void); - diff --git a/src/fw/apps/demo_apps/fps_test_app.c b/src/fw/apps/demo_apps/fps_test_app.c deleted file mode 100644 index 4d1f4a2630..0000000000 --- a/src/fw/apps/demo_apps/fps_test_app.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fps_test_app.h" -#include "fps_test_app_bitmaps.h" -#include "applib/app.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "applib/ui/bitmap_layer.h" -#include "applib/ui/menu_layer.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" -#include "system/profiler.h" -#include "util/size.h" - -typedef struct AppData { - Window window; - BitmapLayer background_layer; - BitmapLayer topleft_layer; - MenuLayer action_list1; - MenuLayer action_list2; - ScrollLayerCallback prv_orig_content_offset_changed; - - int64_t time_started; - uint32_t rendered_frames; -} AppData; - -static int64_t prv_time_64(void) { - time_t s; - uint16_t ms; - rtc_get_time_ms(&s, &ms); - return (int64_t)s * 1000 + ms; -} - -static void prv_redraw_timer_cb(void *cb_data) { - AppData *data = app_state_get_user_data(); - - layer_mark_dirty(&data->window.layer); - app_timer_register(0, prv_redraw_timer_cb, NULL); -} - -/***************************************************************************************** - Stop our timer and display results. - - A frame update consists of the following operations: - op_1) App renders to its own frame buffer - op_2) System copies the app frame buffer to the system frame buffer - op_3) System sends the system frame buffer to the display hardware (using DMA). - - op_3 can happen in parallel with op_1, so the effective frame period is: - frame_period = MAX(op_1_time + op_2_time, op2_time + op_3_time) - - This app measures op_1_time + op_2_time and does so by counting the number of times - the app window's update callback got called within a set amount of time. The window update - callback only does op1, but the app_render_handler() method in app.c insures that a window - update is not called again until op_2 has completed for the previous update. This throttling - if the app's window update also insures that: - (op_1_time + op_2_time) is always >= (op_2_time + op_3_time) - - To measure op_1, we use a profiler timer node called "render". This timer - measures the amount of time we spend in the window_render() method. - - To measure op_2, we use a profiler timer node called "framebuffer_prepare". This timer - measures the amount of time we spend copying the app's frame buffer to the system framebuffer - - To measure op_3, we use a profiler timer node called "framebuffer_send". This profiler timer - measures the amount of time we spend waiting for a display DMA to complete. - - op_1 can be computed from the app's update period - op_2_time -*/ -static void prv_pop_all_windows_cb(void *cb_data) { - // Print profiler stats which include the time spent copying the app frame buffer to the - // system frame buffer and the time spent sending the system frame buffer to the display. - PROFILER_STOP; - PROFILER_PRINT_STATS; - - AppData *data = app_state_get_user_data(); - int64_t time_rendered = prv_time_64() - data->time_started; - - PBL_LOG(LOG_LEVEL_INFO, "## %d frames rendered", (int)data->rendered_frames); - if (time_rendered) { - int frame_period = time_rendered/(int64_t)data->rendered_frames; - int fps = (int64_t)data->rendered_frames*1000/time_rendered; - PBL_LOG(LOG_LEVEL_INFO, "## at %d FPS (%d ms/frame)", fps, frame_period); - } - - app_window_stack_pop_all(false); -} - -static const char *prv_row_texts[] = { - "Row 1", - "Row 2", - "Row 3", - "Row 4", - "Row 5", - "Row 6", -}; - -static uint16_t prv_get_num_rows(struct MenuLayer *menu_layer, uint16_t section_index, - void *callback_context) { - return ARRAY_LENGTH(prv_row_texts); -} - -static void prv_draw_row(GContext* ctx, const Layer *cell_layer, char const *title, - int16_t offset) { - // mostly copied from menu_cell_basic_draw_with_value - // (that unfortunately doesn't respect bounds.origin.x) - const int16_t title_height = 24; - GRect box = cell_layer->bounds; - box.origin.x += offset; - box.origin.y = (box.size.h - title_height) / 2; - box.size.w -= offset; - box.size.h = title_height + 4; - - const GFont title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - if (title) { - graphics_context_set_text_color_2bit(ctx, GColor2White); - graphics_draw_text(ctx, title, title_font, box, - GTextOverflowModeFill, GTextAlignmentLeft, NULL); - } -} - -void prv_draw_row_1(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, - void *callback_context) { - const char *title = prv_row_texts[cell_index->row]; - prv_draw_row(ctx, cell_layer, title, -cell_layer->frame.origin.y/4); -} - -static void prv_draw_row_2(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, - void *callback_context) { - const char *title = prv_row_texts[cell_index->row]; - prv_draw_row(ctx, cell_layer, title, -cell_layer->frame.origin.y/4 + cell_layer->bounds.size.w); -} - -static int16_t prv_get_separator_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, - void *callback_context) { - return 0; -} - -static void prv_window_update_proc(struct Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - if (data->rendered_frames == 0) { - data->time_started = prv_time_64(); - PROFILER_INIT; - PROFILER_START; - } - data->rendered_frames++; -} - -static void prv_window_disapper(Window *window) { -} - -void prv_syncing_content_offset_changed(struct ScrollLayer *scroll_layer, void *context) { - AppData *data = app_state_get_user_data(); - data->prv_orig_content_offset_changed(scroll_layer, context); - - GPoint offset = scroll_layer_get_content_offset(scroll_layer); - scroll_layer_set_content_offset(&data->action_list1.scroll_layer, offset, false); -} - -static void prv_window_load(Window *window) { - // creates a structure as outlined at - // https://pebbletechnology.atlassian.net/wiki/display/DEV/3.0+Notifications+UI+MVP - - // it's one full screen background image .background_layer, - // one image at the top left .topleft_layer, - // and two menu layers .action_list1 and .action_list2 that overlay each other - - // some hackery with the two menu layers goes on to keep their scroll offest in sync - // and to have the inverter layer rendered only once - - const int16_t navbar_width = s_fps_topleft_bitmap.bounds.size.w; - - AppData *data = window_get_user_data(window); - const GRect *full_rect = &window->layer.bounds; - - bitmap_layer_init(&data->background_layer, full_rect); - bitmap_layer_set_background_color_2bit(&data->background_layer, GColor2Black); - bitmap_layer_set_bitmap(&data->background_layer, &s_fps_background_bitmap); - layer_add_child(&window->layer, &data->background_layer.layer); - - bitmap_layer_init(&data->topleft_layer, &GRect(0, 0, navbar_width, navbar_width)); - bitmap_layer_set_background_color_2bit(&data->topleft_layer, GColor2White); - bitmap_layer_set_bitmap(&data->topleft_layer, &s_fps_topleft_bitmap); - // PBL_LOG(LOG_LEVEL_DEBUG, "heap_allocated: %d", s_fps_topleft_bitmap.is_heap_allocated); - // PBL_LOG(LOG_LEVEL_DEBUG, "bitmapformat: %d", s_fps_topleft_bitmap.format); - - layer_add_child(&window->layer, &data->topleft_layer.layer); - - const GRect menu_layer_rect = - GRect(navbar_width, 0, full_rect->size.w - navbar_width, full_rect->size.h); - menu_layer_init(&data->action_list1, &menu_layer_rect); - menu_layer_set_callbacks(&data->action_list1, NULL, &(MenuLayerCallbacks){ - .get_num_rows = prv_get_num_rows, - .draw_row = prv_draw_row_1, - .get_separator_height = prv_get_separator_height, - }); - layer_set_hidden(&data->action_list1.inverter.layer, true); - - scroll_layer_set_shadow_hidden(&data->action_list1.scroll_layer, true); - layer_add_child(&window->layer, menu_layer_get_layer(&data->action_list1)); - - menu_layer_init(&data->action_list2, &menu_layer_rect); - menu_layer_set_callbacks(&data->action_list2, NULL, &(MenuLayerCallbacks){ - .get_num_rows = prv_get_num_rows, - .draw_row = prv_draw_row_2, - .get_separator_height = prv_get_separator_height, - }); - scroll_layer_set_shadow_hidden(&data->action_list2.scroll_layer, true); - data->prv_orig_content_offset_changed = - data->action_list2.scroll_layer.callbacks.content_offset_changed_handler; - data->action_list2.scroll_layer.callbacks.content_offset_changed_handler = - prv_syncing_content_offset_changed; - menu_layer_set_click_config_onto_window(&data->action_list2, window); - layer_add_child(&window->layer, menu_layer_get_layer(&data->action_list2)); - - // start infinite update loop - prv_redraw_timer_cb(NULL); - // run application for a given time, than terminate - app_timer_register(5000, prv_pop_all_windows_cb, NULL); -} - -static void prv_window_unload(Window *window) { - AppData *data = window_get_user_data(window); - menu_layer_deinit(&data->action_list1); - menu_layer_deinit(&data->action_list2); -} - -static void s_main(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - memset(data, 0, sizeof(AppData)); - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("FPS test")); - window_set_user_data(window, data); - window_set_fullscreen(window, true); - layer_set_update_proc(&window->layer, prv_window_update_proc); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - .disappear = prv_window_disapper, - }); - - app_window_stack_push(window, true); - - PROFILER_INIT; - PROFILER_START; - app_event_loop(); -} - -const PebbleProcessMd* fps_test_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = s_main, - .name = "FPS Test" - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/fps_test_app.h b/src/fw/apps/demo_apps/fps_test_app.h deleted file mode 100644 index 3b725996af..0000000000 --- a/src/fw/apps/demo_apps/fps_test_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* fps_test_get_app_info(void); diff --git a/src/fw/apps/demo_apps/fps_test_app_bitmaps.h b/src/fw/apps/demo_apps/fps_test_app_bitmaps.h deleted file mode 100644 index c23aa669a8..0000000000 --- a/src/fw/apps/demo_apps/fps_test_app_bitmaps.h +++ /dev/null @@ -1,1904 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// @nolint - -#pragma once -#include "applib/graphics/gtypes.h" - -#if SCREEN_COLOR_DEPTH_BITS == 8 - -// GBitmap + pixel data generated by bitmapgen.py: - -static const uint8_t s_fps_background_pixels[] = { - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 0 - 16 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16 - 32 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 32 - 48 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 48 - 64 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 64 - 80 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 80 - 96 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 96 - 112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 112 - 128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 128 - 144 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 144 - 160 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 160 - 176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 176 - 192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 192 - 208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 208 - 224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 224 - 240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 240 - 256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 256 - 272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 272 - 288 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 288 - 304 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 304 - 320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 320 - 336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 336 - 352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 352 - 368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 368 - 384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 384 - 400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 400 - 416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 416 - 432 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 432 - 448 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 448 - 464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 464 - 480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 480 - 496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 496 - 512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 512 - 528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 528 - 544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 544 - 560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 560 - 576 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 576 - 592 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 592 - 608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 608 - 624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 624 - 640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 640 - 656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 656 - 672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 672 - 688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 688 - 704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 704 - 720 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 720 - 736 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 736 - 752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 752 - 768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 768 - 784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 784 - 800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 800 - 816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 816 - 832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 832 - 848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 848 - 864 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 864 - 880 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 880 - 896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 896 - 912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 912 - 928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 928 - 944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 944 - 960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 960 - 976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 976 - 992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 992 - 1008 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1008 - 1024 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1024 - 1040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1040 - 1056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1056 - 1072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1072 - 1088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1088 - 1104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1104 - 1120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1120 - 1136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1136 - 1152 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1152 - 1168 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1168 - 1184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1184 - 1200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1200 - 1216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1216 - 1232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1232 - 1248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1248 - 1264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1264 - 1280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1280 - 1296 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1296 - 1312 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1312 - 1328 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1328 - 1344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1344 - 1360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1360 - 1376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1376 - 1392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1392 - 1408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1408 - 1424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1424 - 1440 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1440 - 1456 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1456 - 1472 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1472 - 1488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1488 - 1504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1504 - 1520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1520 - 1536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1536 - 1552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1552 - 1568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1568 - 1584 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1584 - 1600 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1600 - 1616 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1616 - 1632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1632 - 1648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1648 - 1664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1664 - 1680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1680 - 1696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1696 - 1712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1712 - 1728 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1728 - 1744 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1744 - 1760 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1760 - 1776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1776 - 1792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1792 - 1808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1808 - 1824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1824 - 1840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1840 - 1856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1856 - 1872 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1872 - 1888 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1888 - 1904 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1904 - 1920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1920 - 1936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1936 - 1952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1952 - 1968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1968 - 1984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 1984 - 2000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2000 - 2016 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2016 - 2032 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2032 - 2048 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2048 - 2064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2064 - 2080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2080 - 2096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2096 - 2112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2112 - 2128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2128 - 2144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2144 - 2160 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2160 - 2176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2176 - 2192 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2192 - 2208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2208 - 2224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2224 - 2240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2240 - 2256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2256 - 2272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2272 - 2288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2288 - 2304 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2304 - 2320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2320 - 2336 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2336 - 2352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2352 - 2368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2368 - 2384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2384 - 2400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2400 - 2416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2416 - 2432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2432 - 2448 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2448 - 2464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2464 - 2480 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2480 - 2496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2496 - 2512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2512 - 2528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2528 - 2544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2544 - 2560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2560 - 2576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2576 - 2592 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2592 - 2608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2608 - 2624 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2624 - 2640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2640 - 2656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2656 - 2672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2672 - 2688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2688 - 2704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2704 - 2720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2720 - 2736 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2736 - 2752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2752 - 2768 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2768 - 2784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2784 - 2800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2800 - 2816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2816 - 2832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2832 - 2848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2848 - 2864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2864 - 2880 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2880 - 2896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 2896 - 2912 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2912 - 2928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2928 - 2944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2944 - 2960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2960 - 2976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2976 - 2992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 2992 - 3008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3008 - 3024 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3024 - 3040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3040 - 3056 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3056 - 3072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3072 - 3088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3088 - 3104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3104 - 3120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3120 - 3136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3136 - 3152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3152 - 3168 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3168 - 3184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3184 - 3200 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3200 - 3216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3216 - 3232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3232 - 3248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3248 - 3264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3264 - 3280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3280 - 3296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3296 - 3312 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3312 - 3328 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3328 - 3344 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3344 - 3360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3360 - 3376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3376 - 3392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3392 - 3408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3408 - 3424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3424 - 3440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3440 - 3456 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3456 - 3472 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3472 - 3488 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3488 - 3504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3504 - 3520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3520 - 3536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3536 - 3552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3552 - 3568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3568 - 3584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3584 - 3600 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3600 - 3616 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3616 - 3632 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3632 - 3648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3648 - 3664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3664 - 3680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3680 - 3696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3696 - 3712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3712 - 3728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3728 - 3744 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3744 - 3760 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3760 - 3776 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3776 - 3792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3792 - 3808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3808 - 3824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3824 - 3840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3840 - 3856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3856 - 3872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3872 - 3888 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3888 - 3904 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 3904 - 3920 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3920 - 3936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3936 - 3952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3952 - 3968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3968 - 3984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 3984 - 4000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4000 - 4016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4016 - 4032 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4032 - 4048 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4048 - 4064 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4064 - 4080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4080 - 4096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4096 - 4112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4112 - 4128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4128 - 4144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4144 - 4160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4160 - 4176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4176 - 4192 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4192 - 4208 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4208 - 4224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4224 - 4240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4240 - 4256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4256 - 4272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4272 - 4288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4288 - 4304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4304 - 4320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4320 - 4336 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4336 - 4352 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4352 - 4368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4368 - 4384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4384 - 4400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4400 - 4416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4416 - 4432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4432 - 4448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4448 - 4464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4464 - 4480 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4480 - 4496 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4496 - 4512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4512 - 4528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4528 - 4544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4544 - 4560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4560 - 4576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4576 - 4592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4592 - 4608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4608 - 4624 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4624 - 4640 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4640 - 4656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4656 - 4672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4672 - 4688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4688 - 4704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4704 - 4720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4720 - 4736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4736 - 4752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4752 - 4768 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4768 - 4784 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4784 - 4800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4800 - 4816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4816 - 4832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4832 - 4848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4848 - 4864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4864 - 4880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4880 - 4896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4896 - 4912 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 4912 - 4928 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4928 - 4944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4944 - 4960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4960 - 4976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4976 - 4992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 4992 - 5008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5008 - 5024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5024 - 5040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5040 - 5056 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5056 - 5072 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5072 - 5088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5088 - 5104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5104 - 5120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5120 - 5136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5136 - 5152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5152 - 5168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5168 - 5184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5184 - 5200 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5200 - 5216 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5216 - 5232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5232 - 5248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5248 - 5264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5264 - 5280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5280 - 5296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5296 - 5312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5312 - 5328 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5328 - 5344 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5344 - 5360 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5360 - 5376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5376 - 5392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5392 - 5408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5408 - 5424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5424 - 5440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5440 - 5456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5456 - 5472 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5472 - 5488 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5488 - 5504 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5504 - 5520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5520 - 5536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5536 - 5552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5552 - 5568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5568 - 5584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5584 - 5600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5600 - 5616 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5616 - 5632 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5632 - 5648 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5648 - 5664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5664 - 5680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5680 - 5696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5696 - 5712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5712 - 5728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5728 - 5744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5744 - 5760 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5760 - 5776 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5776 - 5792 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5792 - 5808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5808 - 5824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5824 - 5840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5840 - 5856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5856 - 5872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5872 - 5888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5888 - 5904 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5904 - 5920 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 5920 - 5936 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5936 - 5952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5952 - 5968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5968 - 5984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 5984 - 6000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6000 - 6016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6016 - 6032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6032 - 6048 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6048 - 6064 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6064 - 6080 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6080 - 6096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6096 - 6112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6112 - 6128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6128 - 6144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6144 - 6160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6160 - 6176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6176 - 6192 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6192 - 6208 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6208 - 6224 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6224 - 6240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6240 - 6256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6256 - 6272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6272 - 6288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6288 - 6304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6304 - 6320 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6320 - 6336 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6336 - 6352 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6352 - 6368 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6368 - 6384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6384 - 6400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6400 - 6416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6416 - 6432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6432 - 6448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6448 - 6464 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6464 - 6480 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6480 - 6496 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6496 - 6512 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6512 - 6528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6528 - 6544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6544 - 6560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6560 - 6576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6576 - 6592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6592 - 6608 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6608 - 6624 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6624 - 6640 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6640 - 6656 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6656 - 6672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6672 - 6688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6688 - 6704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6704 - 6720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6720 - 6736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6736 - 6752 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6752 - 6768 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6768 - 6784 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6784 - 6800 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6800 - 6816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6816 - 6832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6832 - 6848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6848 - 6864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6864 - 6880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6880 - 6896 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6896 - 6912 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6912 - 6928 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 6928 - 6944 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6944 - 6960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6960 - 6976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6976 - 6992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 6992 - 7008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7008 - 7024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7024 - 7040 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7040 - 7056 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7056 - 7072 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7072 - 7088 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7088 - 7104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7104 - 7120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7120 - 7136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7136 - 7152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7152 - 7168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7168 - 7184 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7184 - 7200 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7200 - 7216 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7216 - 7232 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7232 - 7248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7248 - 7264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7264 - 7280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7280 - 7296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7296 - 7312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7312 - 7328 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7328 - 7344 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7344 - 7360 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7360 - 7376 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7376 - 7392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7392 - 7408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7408 - 7424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7424 - 7440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7440 - 7456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7456 - 7472 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7472 - 7488 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7488 - 7504 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7504 - 7520 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7520 - 7536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7536 - 7552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7552 - 7568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7568 - 7584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7584 - 7600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7600 - 7616 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7616 - 7632 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7632 - 7648 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7648 - 7664 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7664 - 7680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7680 - 7696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7696 - 7712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7712 - 7728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7728 - 7744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7744 - 7760 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7760 - 7776 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7776 - 7792 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7792 - 7808 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7808 - 7824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7824 - 7840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7840 - 7856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7856 - 7872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7872 - 7888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7888 - 7904 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7904 - 7920 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7920 - 7936 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 7936 - 7952 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7952 - 7968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7968 - 7984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 7984 - 8000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8000 - 8016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8016 - 8032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8032 - 8048 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8048 - 8064 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8064 - 8080 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8080 - 8096 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8096 - 8112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8112 - 8128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8128 - 8144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8144 - 8160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8160 - 8176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8176 - 8192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8192 - 8208 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8208 - 8224 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8224 - 8240 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8240 - 8256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8256 - 8272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8272 - 8288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8288 - 8304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8304 - 8320 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8320 - 8336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8336 - 8352 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8352 - 8368 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8368 - 8384 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8384 - 8400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8400 - 8416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8416 - 8432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8432 - 8448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8448 - 8464 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8464 - 8480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8480 - 8496 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8496 - 8512 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8512 - 8528 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8528 - 8544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8544 - 8560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8560 - 8576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8576 - 8592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8592 - 8608 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8608 - 8624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8624 - 8640 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8640 - 8656 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8656 - 8672 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8672 - 8688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8688 - 8704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8704 - 8720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8720 - 8736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8736 - 8752 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8752 - 8768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8768 - 8784 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8784 - 8800 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8800 - 8816 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8816 - 8832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8832 - 8848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8848 - 8864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8864 - 8880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8880 - 8896 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8896 - 8912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8912 - 8928 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8928 - 8944 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 8944 - 8960 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8960 - 8976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8976 - 8992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 8992 - 9008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9008 - 9024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9024 - 9040 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9040 - 9056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9056 - 9072 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9072 - 9088 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9088 - 9104 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9104 - 9120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9120 - 9136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9136 - 9152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9152 - 9168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9168 - 9184 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9184 - 9200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9200 - 9216 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9216 - 9232 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9232 - 9248 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9248 - 9264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9264 - 9280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9280 - 9296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9296 - 9312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9312 - 9328 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9328 - 9344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9344 - 9360 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9360 - 9376 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9376 - 9392 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9392 - 9408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9408 - 9424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9424 - 9440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9440 - 9456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9456 - 9472 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9472 - 9488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9488 - 9504 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9504 - 9520 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9520 - 9536 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9536 - 9552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9552 - 9568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9568 - 9584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9584 - 9600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9600 - 9616 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9616 - 9632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9632 - 9648 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9648 - 9664 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9664 - 9680 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9680 - 9696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9696 - 9712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9712 - 9728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9728 - 9744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9744 - 9760 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9760 - 9776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9776 - 9792 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9792 - 9808 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9808 - 9824 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9824 - 9840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9840 - 9856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9856 - 9872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9872 - 9888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9888 - 9904 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9904 - 9920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9920 - 9936 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9936 - 9952 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 9952 - 9968 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9968 - 9984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 9984 - 10000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10000 - 10016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10016 - 10032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10032 - 10048 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10048 - 10064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10064 - 10080 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10080 - 10096 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10096 - 10112 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10112 - 10128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10128 - 10144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10144 - 10160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10160 - 10176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10176 - 10192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10192 - 10208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10208 - 10224 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10224 - 10240 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10240 - 10256 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10256 - 10272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10272 - 10288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10288 - 10304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10304 - 10320 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10320 - 10336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10336 - 10352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10352 - 10368 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10368 - 10384 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10384 - 10400 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10400 - 10416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10416 - 10432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10432 - 10448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10448 - 10464 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10464 - 10480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10480 - 10496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10496 - 10512 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10512 - 10528 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10528 - 10544 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10544 - 10560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10560 - 10576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10576 - 10592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10592 - 10608 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10608 - 10624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10624 - 10640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10640 - 10656 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10656 - 10672 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10672 - 10688 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10688 - 10704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10704 - 10720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10720 - 10736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10736 - 10752 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10752 - 10768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10768 - 10784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10784 - 10800 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10800 - 10816 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10816 - 10832 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10832 - 10848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10848 - 10864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10864 - 10880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10880 - 10896 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10896 - 10912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10912 - 10928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10928 - 10944 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10944 - 10960 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 10960 - 10976 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10976 - 10992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 10992 - 11008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11008 - 11024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11024 - 11040 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11040 - 11056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11056 - 11072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11072 - 11088 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11088 - 11104 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11104 - 11120 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11120 - 11136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11136 - 11152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11152 - 11168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11168 - 11184 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11184 - 11200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11200 - 11216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11216 - 11232 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11232 - 11248 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11248 - 11264 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11264 - 11280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11280 - 11296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11296 - 11312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11312 - 11328 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11328 - 11344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11344 - 11360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11360 - 11376 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11376 - 11392 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11392 - 11408 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11408 - 11424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11424 - 11440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11440 - 11456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11456 - 11472 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11472 - 11488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11488 - 11504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11504 - 11520 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11520 - 11536 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11536 - 11552 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11552 - 11568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11568 - 11584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11584 - 11600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11600 - 11616 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11616 - 11632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11632 - 11648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11648 - 11664 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11664 - 11680 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11680 - 11696 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11696 - 11712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11712 - 11728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11728 - 11744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11744 - 11760 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11760 - 11776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11776 - 11792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11792 - 11808 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11808 - 11824 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11824 - 11840 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11840 - 11856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11856 - 11872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11872 - 11888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11888 - 11904 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11904 - 11920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11920 - 11936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11936 - 11952 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11952 - 11968 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 11968 - 11984 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 11984 - 12000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12000 - 12016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12016 - 12032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12032 - 12048 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12048 - 12064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12064 - 12080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12080 - 12096 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12096 - 12112 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12112 - 12128 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12128 - 12144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12144 - 12160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12160 - 12176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12176 - 12192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12192 - 12208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12208 - 12224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12224 - 12240 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12240 - 12256 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12256 - 12272 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12272 - 12288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12288 - 12304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12304 - 12320 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12320 - 12336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12336 - 12352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12352 - 12368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12368 - 12384 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12384 - 12400 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12400 - 12416 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12416 - 12432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12432 - 12448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12448 - 12464 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12464 - 12480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12480 - 12496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12496 - 12512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12512 - 12528 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12528 - 12544 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12544 - 12560 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12560 - 12576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12576 - 12592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12592 - 12608 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12608 - 12624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12624 - 12640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12640 - 12656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12656 - 12672 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12672 - 12688 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12688 - 12704 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12704 - 12720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12720 - 12736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12736 - 12752 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12752 - 12768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12768 - 12784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12784 - 12800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12800 - 12816 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12816 - 12832 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12832 - 12848 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12848 - 12864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12864 - 12880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12880 - 12896 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12896 - 12912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12912 - 12928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12928 - 12944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12944 - 12960 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12960 - 12976 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 12976 - 12992 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 12992 - 13008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13008 - 13024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13024 - 13040 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13040 - 13056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13056 - 13072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13072 - 13088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13088 - 13104 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13104 - 13120 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13120 - 13136 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13136 - 13152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13152 - 13168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13168 - 13184 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13184 - 13200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13200 - 13216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13216 - 13232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13232 - 13248 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13248 - 13264 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13264 - 13280 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13280 - 13296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13296 - 13312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13312 - 13328 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13328 - 13344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13344 - 13360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13360 - 13376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13376 - 13392 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13392 - 13408 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13408 - 13424 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13424 - 13440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13440 - 13456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13456 - 13472 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13472 - 13488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13488 - 13504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13504 - 13520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13520 - 13536 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13536 - 13552 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13552 - 13568 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13568 - 13584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13584 - 13600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13600 - 13616 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13616 - 13632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13632 - 13648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13648 - 13664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13664 - 13680 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13680 - 13696 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13696 - 13712 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13712 - 13728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13728 - 13744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13744 - 13760 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13760 - 13776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13776 - 13792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13792 - 13808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13808 - 13824 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13824 - 13840 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13840 - 13856 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13856 - 13872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13872 - 13888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13888 - 13904 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13904 - 13920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13920 - 13936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13936 - 13952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 13952 - 13968 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13968 - 13984 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 13984 - 14000 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14000 - 14016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14016 - 14032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14032 - 14048 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14048 - 14064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14064 - 14080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14080 - 14096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14096 - 14112 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14112 - 14128 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14128 - 14144 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14144 - 14160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14160 - 14176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14176 - 14192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14192 - 14208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14208 - 14224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14224 - 14240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14240 - 14256 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14256 - 14272 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14272 - 14288 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14288 - 14304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14304 - 14320 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14320 - 14336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14336 - 14352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14352 - 14368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14368 - 14384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14384 - 14400 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14400 - 14416 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14416 - 14432 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14432 - 14448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14448 - 14464 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14464 - 14480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14480 - 14496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14496 - 14512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14512 - 14528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14528 - 14544 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14544 - 14560 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14560 - 14576 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14576 - 14592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14592 - 14608 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14608 - 14624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14624 - 14640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14640 - 14656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14656 - 14672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14672 - 14688 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14688 - 14704 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14704 - 14720 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14720 - 14736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14736 - 14752 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14752 - 14768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14768 - 14784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14784 - 14800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14800 - 14816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14816 - 14832 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14832 - 14848 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14848 - 14864 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14864 - 14880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14880 - 14896 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14896 - 14912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14912 - 14928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14928 - 14944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14944 - 14960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 14960 - 14976 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14976 - 14992 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 14992 - 15008 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15008 - 15024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15024 - 15040 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15040 - 15056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15056 - 15072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15072 - 15088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15088 - 15104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15104 - 15120 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15120 - 15136 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15136 - 15152 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15152 - 15168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15168 - 15184 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15184 - 15200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15200 - 15216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15216 - 15232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15232 - 15248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15248 - 15264 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15264 - 15280 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15280 - 15296 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15296 - 15312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15312 - 15328 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15328 - 15344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15344 - 15360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15360 - 15376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15376 - 15392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15392 - 15408 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15408 - 15424 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15424 - 15440 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15440 - 15456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15456 - 15472 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15472 - 15488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15488 - 15504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15504 - 15520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15520 - 15536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15536 - 15552 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15552 - 15568 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15568 - 15584 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15584 - 15600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15600 - 15616 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15616 - 15632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15632 - 15648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15648 - 15664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15664 - 15680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15680 - 15696 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15696 - 15712 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15712 - 15728 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15728 - 15744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15744 - 15760 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15760 - 15776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15776 - 15792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15792 - 15808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15808 - 15824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15824 - 15840 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15840 - 15856 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15856 - 15872 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15872 - 15888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15888 - 15904 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15904 - 15920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15920 - 15936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15936 - 15952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15952 - 15968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 15968 - 15984 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 15984 - 16000 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16000 - 16016 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16016 - 16032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16032 - 16048 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16048 - 16064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16064 - 16080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16080 - 16096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16096 - 16112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16112 - 16128 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16128 - 16144 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16144 - 16160 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16160 - 16176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16176 - 16192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16192 - 16208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16208 - 16224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16224 - 16240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16240 - 16256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16256 - 16272 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16272 - 16288 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16288 - 16304 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16304 - 16320 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16320 - 16336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16336 - 16352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16352 - 16368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16368 - 16384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16384 - 16400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16400 - 16416 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16416 - 16432 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16432 - 16448 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16448 - 16464 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16464 - 16480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16480 - 16496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16496 - 16512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16512 - 16528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16528 - 16544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16544 - 16560 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16560 - 16576 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16576 - 16592 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16592 - 16608 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16608 - 16624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16624 - 16640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16640 - 16656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16656 - 16672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16672 - 16688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16688 - 16704 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16704 - 16720 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16720 - 16736 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16736 - 16752 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16752 - 16768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16768 - 16784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16784 - 16800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16800 - 16816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16816 - 16832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16832 - 16848 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16848 - 16864 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16864 - 16880 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16880 - 16896 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16896 - 16912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16912 - 16928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16928 - 16944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16944 - 16960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16960 - 16976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 16976 - 16992 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16992 - 17008 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17008 - 17024 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17024 - 17040 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17040 - 17056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17056 - 17072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17072 - 17088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17088 - 17104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17104 - 17120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17120 - 17136 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17136 - 17152 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17152 - 17168 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17168 - 17184 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17184 - 17200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17200 - 17216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17216 - 17232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17232 - 17248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17248 - 17264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17264 - 17280 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17280 - 17296 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17296 - 17312 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17312 - 17328 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17328 - 17344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17344 - 17360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17360 - 17376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17376 - 17392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17392 - 17408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17408 - 17424 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17424 - 17440 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17440 - 17456 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17456 - 17472 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17472 - 17488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17488 - 17504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17504 - 17520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17520 - 17536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17536 - 17552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17552 - 17568 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17568 - 17584 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17584 - 17600 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17600 - 17616 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17616 - 17632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17632 - 17648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17648 - 17664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17664 - 17680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17680 - 17696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17696 - 17712 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17712 - 17728 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17728 - 17744 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17744 - 17760 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17760 - 17776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17776 - 17792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17792 - 17808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17808 - 17824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17824 - 17840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17840 - 17856 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17856 - 17872 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 17872 - 17888 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17888 - 17904 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17904 - 17920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17920 - 17936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17936 - 17952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17952 - 17968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17968 - 17984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 17984 - 18000 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18000 - 18016 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18016 - 18032 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18032 - 18048 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18048 - 18064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18064 - 18080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18080 - 18096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18096 - 18112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18112 - 18128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18128 - 18144 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18144 - 18160 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18160 - 18176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18176 - 18192 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18192 - 18208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18208 - 18224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18224 - 18240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18240 - 18256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18256 - 18272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18272 - 18288 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18288 - 18304 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18304 - 18320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18320 - 18336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18336 - 18352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18352 - 18368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18368 - 18384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18384 - 18400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18400 - 18416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18416 - 18432 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18432 - 18448 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18448 - 18464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18464 - 18480 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18480 - 18496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18496 - 18512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18512 - 18528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18528 - 18544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18544 - 18560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18560 - 18576 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18576 - 18592 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18592 - 18608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18608 - 18624 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18624 - 18640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18640 - 18656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18656 - 18672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18672 - 18688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18688 - 18704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18704 - 18720 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18720 - 18736 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18736 - 18752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18752 - 18768 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18768 - 18784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18784 - 18800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18800 - 18816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18816 - 18832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18832 - 18848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18848 - 18864 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18864 - 18880 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 18880 - 18896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18896 - 18912 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18912 - 18928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18928 - 18944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18944 - 18960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18960 - 18976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18976 - 18992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 18992 - 19008 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19008 - 19024 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19024 - 19040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19040 - 19056 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19056 - 19072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19072 - 19088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19088 - 19104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19104 - 19120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19120 - 19136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19136 - 19152 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19152 - 19168 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19168 - 19184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19184 - 19200 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19200 - 19216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19216 - 19232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19232 - 19248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19248 - 19264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19264 - 19280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19280 - 19296 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19296 - 19312 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19312 - 19328 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19328 - 19344 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19344 - 19360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19360 - 19376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19376 - 19392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19392 - 19408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19408 - 19424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19424 - 19440 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19440 - 19456 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19456 - 19472 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19472 - 19488 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19488 - 19504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19504 - 19520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19520 - 19536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19536 - 19552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19552 - 19568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19568 - 19584 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19584 - 19600 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19600 - 19616 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19616 - 19632 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19632 - 19648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19648 - 19664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19664 - 19680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19680 - 19696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19696 - 19712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19712 - 19728 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19728 - 19744 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19744 - 19760 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19760 - 19776 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19776 - 19792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19792 - 19808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19808 - 19824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19824 - 19840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19840 - 19856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19856 - 19872 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19872 - 19888 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 19888 - 19904 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19904 - 19920 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19920 - 19936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19936 - 19952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19952 - 19968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19968 - 19984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 19984 - 20000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20000 - 20016 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20016 - 20032 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20032 - 20048 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20048 - 20064 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20064 - 20080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20080 - 20096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20096 - 20112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20112 - 20128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20128 - 20144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20144 - 20160 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20160 - 20176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20176 - 20192 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20192 - 20208 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20208 - 20224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20224 - 20240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20240 - 20256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20256 - 20272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20272 - 20288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20288 - 20304 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20304 - 20320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20320 - 20336 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20336 - 20352 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20352 - 20368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20368 - 20384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20384 - 20400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20400 - 20416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20416 - 20432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20432 - 20448 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20448 - 20464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20464 - 20480 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20480 - 20496 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20496 - 20512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20512 - 20528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20528 - 20544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20544 - 20560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20560 - 20576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20576 - 20592 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20592 - 20608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20608 - 20624 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20624 - 20640 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20640 - 20656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20656 - 20672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20672 - 20688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20688 - 20704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20704 - 20720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20720 - 20736 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20736 - 20752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20752 - 20768 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20768 - 20784 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20784 - 20800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20800 - 20816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20816 - 20832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20832 - 20848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20848 - 20864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20864 - 20880 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20880 - 20896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 20896 - 20912 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20912 - 20928 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20928 - 20944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20944 - 20960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20960 - 20976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20976 - 20992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 20992 - 21008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21008 - 21024 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21024 - 21040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21040 - 21056 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21056 - 21072 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21072 - 21088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21088 - 21104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21104 - 21120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21120 - 21136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21136 - 21152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21152 - 21168 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21168 - 21184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21184 - 21200 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21200 - 21216 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21216 - 21232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21232 - 21248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21248 - 21264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21264 - 21280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21280 - 21296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21296 - 21312 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21312 - 21328 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21328 - 21344 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21344 - 21360 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21360 - 21376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21376 - 21392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21392 - 21408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21408 - 21424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21424 - 21440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21440 - 21456 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21456 - 21472 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21472 - 21488 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21488 - 21504 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21504 - 21520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21520 - 21536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21536 - 21552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21552 - 21568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21568 - 21584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21584 - 21600 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21600 - 21616 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21616 - 21632 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21632 - 21648 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21648 - 21664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21664 - 21680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21680 - 21696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21696 - 21712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21712 - 21728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21728 - 21744 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21744 - 21760 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21760 - 21776 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21776 - 21792 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21792 - 21808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21808 - 21824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21824 - 21840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21840 - 21856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21856 - 21872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21872 - 21888 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21888 - 21904 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 21904 - 21920 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21920 - 21936 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21936 - 21952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21952 - 21968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21968 - 21984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 21984 - 22000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22000 - 22016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22016 - 22032 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22032 - 22048 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22048 - 22064 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22064 - 22080 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22080 - 22096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22096 - 22112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22112 - 22128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22128 - 22144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22144 - 22160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22160 - 22176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22176 - 22192 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22192 - 22208 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22208 - 22224 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22224 - 22240 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22240 - 22256 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22256 - 22272 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22272 - 22288 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22288 - 22304 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22304 - 22320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22320 - 22336 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22336 - 22352 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22352 - 22368 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22368 - 22384 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22384 - 22400 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22400 - 22416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22416 - 22432 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22432 - 22448 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22448 - 22464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22464 - 22480 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22480 - 22496 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22496 - 22512 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22512 - 22528 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22528 - 22544 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22544 - 22560 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22560 - 22576 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22576 - 22592 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22592 - 22608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22608 - 22624 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22624 - 22640 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22640 - 22656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22656 - 22672 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22672 - 22688 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22688 - 22704 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22704 - 22720 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22720 - 22736 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22736 - 22752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22752 - 22768 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22768 - 22784 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22784 - 22800 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22800 - 22816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22816 - 22832 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22832 - 22848 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22848 - 22864 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22864 - 22880 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22880 - 22896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22896 - 22912 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 22912 - 22928 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22928 - 22944 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22944 - 22960 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22960 - 22976 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22976 - 22992 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 22992 - 23008 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23008 - 23024 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23024 - 23040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23040 - 23056 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23056 - 23072 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23072 - 23088 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23088 - 23104 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23104 - 23120 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23120 - 23136 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23136 - 23152 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23152 - 23168 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23168 - 23184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23184 - 23200 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23200 - 23216 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23216 - 23232 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23232 - 23248 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23248 - 23264 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23264 - 23280 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23280 - 23296 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23296 - 23312 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23312 - 23328 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23328 - 23344 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23344 - 23360 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23360 - 23376 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23376 - 23392 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23392 - 23408 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23408 - 23424 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23424 - 23440 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23440 - 23456 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23456 - 23472 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23472 - 23488 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23488 - 23504 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23504 - 23520 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23520 - 23536 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23536 - 23552 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23552 - 23568 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23568 - 23584 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23584 - 23600 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23600 - 23616 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23616 - 23632 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23632 - 23648 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23648 - 23664 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23664 - 23680 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23680 - 23696 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23696 - 23712 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23712 - 23728 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23728 - 23744 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23744 - 23760 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23760 - 23776 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23776 - 23792 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23792 - 23808 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23808 - 23824 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23824 - 23840 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23840 - 23856 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23856 - 23872 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23872 - 23888 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23888 - 23904 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23904 - 23920 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 23920 - 23936 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23936 - 23952 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23952 - 23968 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23968 - 23984 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 23984 - 24000 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24000 - 24016 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24016 - 24032 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24032 - 24048 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 24048 - 24064 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 24064 - 24080 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24080 - 24096 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24096 - 24112 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24112 - 24128 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24128 - 24144 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24144 - 24160 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 24160 - 24176 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 -}; -static const GBitmap s_fps_background_bitmap = { - .addr = (void*) &s_fps_background_pixels, - .row_size_bytes = 144, - .info_flags = 0x1002, - .bounds = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 144, .h = 168 }, - }, -}; - -// GBitmap + pixel data generated by bitmapgen.py: - -static const uint8_t s_fps_topleft_pixels[] = { - 0xa9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 0 - 16 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 16 - 32 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 32 - 48 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 48 - 64 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 64 - 80 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 80 - 96 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 96 - 112 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 112 - 128 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 128 - 144 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 144 - 160 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 160 - 176 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 176 - 192 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 192 - 208 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 208 - 224 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 224 - 240 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 240 - 256 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 256 - 272 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 272 - 288 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xd4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 288 - 304 */ - 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 304 - 320 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 320 - 336 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 336 - 352 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 352 - 368 */ - 0xc0, 0xc0, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, /* bytes 368 - 384 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 384 - 400 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, /* bytes 400 - 416 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 416 - 432 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, /* bytes 432 - 448 */ - 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, /* bytes 448 - 464 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, /* bytes 464 - 480 */ - 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, /* bytes 480 - 496 */ - 0xd4, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 496 - 512 */ - 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, /* bytes 512 - 528 */ - 0xda, 0xc0, 0xc0, 0xd0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 528 - 544 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, /* bytes 544 - 560 */ - 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 560 - 576 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, /* bytes 576 - 592 */ - 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 592 - 608 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xda, 0xda, /* bytes 608 - 624 */ - 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, /* bytes 624 - 640 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 640 - 656 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, /* bytes 656 - 672 */ - 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 672 - 688 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 688 - 704 */ - 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 704 - 720 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xe4, 0xc0, /* bytes 720 - 736 */ - 0xc0, 0xc0, 0xc0, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, /* bytes 736 - 752 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, /* bytes 752 - 768 */ - 0xf9, 0xf9, 0xd4, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 768 - 784 */ - 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 784 - 800 */ - 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /* bytes 800 - 816 */ - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 816 - 832 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 832 - 848 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 848 - 864 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 864 - 880 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 880 - 896 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, /* bytes 896 - 912 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 912 - 928 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 928 - 944 */ - 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 944 - 960 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 960 - 976 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xe9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 976 - 992 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 992 - 1008 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc0, 0xc0, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1008 - 1024 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1024 - 1040 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1040 - 1056 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1056 - 1072 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1072 - 1088 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1088 - 1104 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1104 - 1120 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1120 - 1136 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1136 - 1152 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1152 - 1168 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1168 - 1184 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1184 - 1200 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1200 - 1216 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1216 - 1232 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1232 - 1248 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1248 - 1264 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, /* bytes 1264 - 1280 */ - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9 -}; -static const GBitmap s_fps_topleft_bitmap = { - .addr = (void*) &s_fps_topleft_pixels, - .row_size_bytes = 36, - .info_flags = 0x1002, - .bounds = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 36, .h = 36 }, - }, -}; - -#else //SCREEN_COLOR_DEPTH_BITS == 8 - -// GBitmap + pixel data generated by bitmapgen.py: - -static const uint8_t s_fps_background_pixels[] = { - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 0 - 16 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 16 - 32 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 32 - 48 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 48 - 64 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 64 - 80 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 80 - 96 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 96 - 112 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 112 - 128 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 128 - 144 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 144 - 160 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 160 - 176 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 176 - 192 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 192 - 208 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 208 - 224 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 224 - 240 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 240 - 256 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 256 - 272 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 272 - 288 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 288 - 304 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 304 - 320 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 320 - 336 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 336 - 352 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 352 - 368 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 368 - 384 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 384 - 400 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 400 - 416 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 416 - 432 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 432 - 448 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 448 - 464 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 464 - 480 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 480 - 496 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 496 - 512 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 512 - 528 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 528 - 544 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 544 - 560 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 560 - 576 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 576 - 592 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 592 - 608 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 608 - 624 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 624 - 640 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 640 - 656 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 656 - 672 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 672 - 688 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 688 - 704 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 704 - 720 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 720 - 736 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 736 - 752 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 752 - 768 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 768 - 784 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 784 - 800 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 800 - 816 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 816 - 832 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 832 - 848 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 848 - 864 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 864 - 880 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 880 - 896 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 896 - 912 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 912 - 928 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 928 - 944 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 944 - 960 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 960 - 976 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 976 - 992 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 992 - 1008 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1008 - 1024 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1024 - 1040 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1040 - 1056 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1056 - 1072 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1072 - 1088 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1088 - 1104 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1104 - 1120 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1120 - 1136 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1136 - 1152 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1152 - 1168 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1168 - 1184 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1184 - 1200 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1200 - 1216 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1216 - 1232 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1232 - 1248 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1248 - 1264 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1264 - 1280 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1280 - 1296 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1296 - 1312 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1312 - 1328 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1328 - 1344 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1344 - 1360 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1360 - 1376 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1376 - 1392 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1392 - 1408 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1408 - 1424 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1424 - 1440 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1440 - 1456 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1456 - 1472 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1472 - 1488 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1488 - 1504 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1504 - 1520 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1520 - 1536 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1536 - 1552 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1552 - 1568 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1568 - 1584 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1584 - 1600 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1600 - 1616 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1616 - 1632 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1632 - 1648 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1648 - 1664 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1664 - 1680 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1680 - 1696 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1696 - 1712 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1712 - 1728 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1728 - 1744 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1744 - 1760 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1760 - 1776 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1776 - 1792 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1792 - 1808 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1808 - 1824 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1824 - 1840 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1840 - 1856 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1856 - 1872 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1872 - 1888 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1888 - 1904 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1904 - 1920 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1920 - 1936 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1936 - 1952 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 1952 - 1968 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 1968 - 1984 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 1984 - 2000 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2000 - 2016 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2016 - 2032 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2032 - 2048 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2048 - 2064 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2064 - 2080 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2080 - 2096 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2096 - 2112 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2112 - 2128 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2128 - 2144 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2144 - 2160 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2160 - 2176 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2176 - 2192 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2192 - 2208 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2208 - 2224 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2224 - 2240 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2240 - 2256 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2256 - 2272 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2272 - 2288 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2288 - 2304 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2304 - 2320 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2320 - 2336 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2336 - 2352 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2352 - 2368 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2368 - 2384 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2384 - 2400 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2400 - 2416 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2416 - 2432 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2432 - 2448 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2448 - 2464 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2464 - 2480 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2480 - 2496 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2496 - 2512 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2512 - 2528 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2528 - 2544 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2544 - 2560 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2560 - 2576 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2576 - 2592 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2592 - 2608 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2608 - 2624 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2624 - 2640 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2640 - 2656 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2656 - 2672 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2672 - 2688 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2688 - 2704 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2704 - 2720 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2720 - 2736 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2736 - 2752 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2752 - 2768 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2768 - 2784 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2784 - 2800 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2800 - 2816 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2816 - 2832 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2832 - 2848 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2848 - 2864 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2864 - 2880 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2880 - 2896 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2896 - 2912 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2912 - 2928 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 2928 - 2944 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2944 - 2960 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2960 - 2976 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 2976 - 2992 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 2992 - 3008 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3008 - 3024 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3024 - 3040 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3040 - 3056 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3056 - 3072 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3072 - 3088 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3088 - 3104 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3104 - 3120 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3120 - 3136 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3136 - 3152 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3152 - 3168 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3168 - 3184 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3184 - 3200 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3200 - 3216 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3216 - 3232 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3232 - 3248 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3248 - 3264 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3264 - 3280 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3280 - 3296 */ - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 3296 - 3312 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 3312 - 3328 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* bytes 3328 - 3344 */ - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static const GBitmap s_fps_background_bitmap = { - .addr = (void*) &s_fps_background_pixels, - .row_size_bytes = 20, - .info_flags = 0x1000, - .bounds = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 144, .h = 168 }, - }, -}; - - -// GBitmap + pixel data generated by bitmapgen.py: - -static const uint8_t s_fps_topleft_pixels[] = { - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 0 - 16 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 16 - 32 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 32 - 48 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 48 - 64 */ - 0xff, 0x00, 0xf8, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 64 - 80 */ - 0xff, 0xfc, 0xf3, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0x00, /* bytes 80 - 96 */ - 0xff, 0xfc, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xf3, 0xe3, 0x0f, 0x00, 0x00, 0x00, /* bytes 96 - 112 */ - 0xff, 0xfc, 0xf3, 0xf1, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xf3, 0xf8, 0x0f, 0x00, 0x00, 0x00, /* bytes 112 - 128 */ - 0xff, 0xfc, 0x73, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x73, 0xfc, 0x0f, 0x00, 0x00, 0x00, /* bytes 128 - 144 */ - 0xff, 0x00, 0xf0, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf0, 0xf1, 0x0f, 0x00, 0x00, 0x00, /* bytes 144 - 160 */ - 0xff, 0x7c, 0xf8, 0xe3, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x3c, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, /* bytes 160 - 176 */ - 0xff, 0x7c, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 176 - 192 */ - 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 192 - 208 */ - 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 208 - 224 */ - 0xff, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 224 - 240 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 240 - 256 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, /* bytes 256 - 272 */ - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, -}; -static const GBitmap s_fps_topleft_bitmap = { - .addr = (void*) &s_fps_topleft_pixels, - .row_size_bytes = 8, - .info_flags = 0x1000, - .bounds = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 36, .h = 36 }, - }, -}; - -#endif // else of SCREEN_COLOR_DEPTH_BITS == 8 diff --git a/src/fw/apps/demo_apps/gdrawmask_demo.h b/src/fw/apps/demo_apps/gdrawmask_demo.h deleted file mode 100644 index e2abd346ce..0000000000 --- a/src/fw/apps/demo_apps/gdrawmask_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *gdrawmask_demo_get_app_info(); diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_circles.c b/src/fw/apps/demo_apps/gfx_tests/gfx_test_circles.c deleted file mode 100644 index 17422bc0bf..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_circles.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" - -static GRect rect; -static GPoint center; -static GOvalScaleMode scale_mode; -static int inset; -static int outer_size; -static int inner_size; -static int32_t angle_start; -static int32_t angle_end; - -static void prv_setup_data(GRect bounds) { - center = GPoint(bounds.origin.x + (bounds.size.w / 2), - bounds.origin.y + (bounds.size.h / 2)); - rect = GRect(center.x - (outer_size / 2), center.y - (outer_size / 2), - outer_size, outer_size); - inset = outer_size - inner_size; - scale_mode = GOvalScaleModeFitCircle; -} - -static void prv_setup_even_angles_inner(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 50; - inner_size = 35; - angle_start = (TRIG_MAX_ANGLE / 8) * 3; - angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; - - prv_setup_data(bounds); -} - -static void prv_setup_odd_angles_inner(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 49; - inner_size = 34; - angle_start = (TRIG_MAX_ANGLE / 8) * 3; - angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; - - prv_setup_data(bounds); -} - -static void prv_setup_even_inner(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 50; - inner_size = 35; - angle_start = TRIG_MAX_ANGLE / 4; - angle_end = TRIG_MAX_ANGLE; - - prv_setup_data(bounds); -} - -static void prv_setup_odd_inner(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 49; - inner_size = 34; - angle_start = TRIG_MAX_ANGLE / 4; - angle_end = TRIG_MAX_ANGLE; - - prv_setup_data(bounds); -} - -static void prv_setup_even_angles_full(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 50; - inner_size = 0; - angle_start = (TRIG_MAX_ANGLE / 8) * 3; - angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; - - prv_setup_data(bounds); -} - -static void prv_setup_odd_angles_full(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 49; - inner_size = 0; - angle_start = (TRIG_MAX_ANGLE / 8) * 3; - angle_end = TRIG_MAX_ANGLE + TRIG_MAX_ANGLE / 8; - - prv_setup_data(bounds); -} - -static void prv_setup_even_full(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 50; - inner_size = 0; - angle_start = TRIG_MAX_ANGLE / 4; - angle_end = TRIG_MAX_ANGLE; - - prv_setup_data(bounds); -} - -static void prv_setup_odd_full(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = window_layer->bounds; - - // Parameters - outer_size = 49; - inner_size = 0; - angle_start = TRIG_MAX_ANGLE / 4; - angle_end = TRIG_MAX_ANGLE; - - prv_setup_data(bounds); -} - -static void prv_test_radial(Layer *layer, GContext* ctx) { - GColor color = { .argb = (uint8_t) rand() }; - graphics_context_set_fill_color(ctx, color); - graphics_fill_radial(ctx, rect, GOvalScaleModeFillCircle, inset, angle_start, angle_end); -} - -static void prv_test_circle(Layer *layer, GContext* ctx) { - GColor color = { .argb = (uint8_t) rand() }; - graphics_context_set_fill_color(ctx, color); - graphics_fill_circle(ctx, center, (outer_size / 2)); -} - -GfxTest g_gfx_test_annulus_even_fill_angles = { - .name = "Annulus Even Angles", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_even_angles_inner, -}; - -GfxTest g_gfx_test_annulus_odd_fill_angles = { - .name = "Annulus Odd Angles", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_odd_angles_inner, -}; - -GfxTest g_gfx_test_annulus_even_fill = { - .name = "Annulus Even", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_even_inner, -}; - -GfxTest g_gfx_test_annulus_odd_fill = { - .name = "Annulus Odd", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_odd_inner, -}; - -GfxTest g_gfx_test_radial_even_fill_angles = { - .name = "Radial Even Angles", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_even_angles_full, -}; - -GfxTest g_gfx_test_radial_odd_fill_angles = { - .name = "Radial Odd Angles", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_odd_angles_full, -}; - -GfxTest g_gfx_test_radial_even_fill = { - .name = "Radial Even", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_even_full, -}; - -GfxTest g_gfx_test_radial_odd_fill = { - .name = "Radial Odd", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_radial, - .setup = prv_setup_odd_full, -}; - -GfxTest g_gfx_test_circle_even = { - .name = "Circle Even", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_circle, - .setup = prv_setup_even_full, -}; - -GfxTest g_gfx_test_circle_odd = { - .name = "Circle Odd", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test_circle, - .setup = prv_setup_odd_full, -}; diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_gpath_masking.c b/src/fw/apps/demo_apps/gfx_tests/gfx_test_gpath_masking.c deleted file mode 100644 index 55ecc8a896..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_gpath_masking.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" - -static void prv_test(Layer *layer, GContext* ctx); - -GfxTest g_gfx_test_gpath_masking = { - .name = "GPath masking", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test, -}; - -static const GPathInfo s_triangle_mask = { - .num_points = 3, - .points = (GPoint[]) { - {0, 0}, - {50, 50}, - {50, -50} - } -}; - -static void prv_test(Layer *layer, GContext* ctx) { - GRect bounds = layer->bounds; - GPoint center = GPoint(bounds.size.w/2, bounds.size.h/2); - int outer_radius = 50; - int inner_radius = 35; - - GPath *mask = gpath_create(&s_triangle_mask); - gpath_move_to(mask, center); - - GColor color = { .argb = (uint8_t) rand() }; - GColor bg_color = { .argb = (uint8_t) rand() }; - graphics_context_set_fill_color(ctx, color); - graphics_fill_circle(ctx, center, outer_radius); - - graphics_context_set_fill_color(ctx, bg_color); - graphics_fill_circle(ctx, center, inner_radius); - gpath_draw_filled(ctx, mask); -} diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_list.h b/src/fw/apps/demo_apps/gfx_tests/gfx_test_list.h deleted file mode 100644 index 21f0d3244a..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_list.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// List of tests. The name given should match the name of the test definition struct in the form -// g_gfx_test_##name - -GFX_TEST(single_line) -GFX_TEST(text) -GFX_TEST(text_clipping) -GFX_TEST(annulus_even_fill_angles) -GFX_TEST(annulus_odd_fill_angles) -GFX_TEST(annulus_even_fill) -GFX_TEST(annulus_odd_fill) -GFX_TEST(radial_even_fill_angles) -GFX_TEST(radial_odd_fill_angles) -GFX_TEST(radial_even_fill) -GFX_TEST(radial_odd_fill) -GFX_TEST(circle_odd) -GFX_TEST(circle_even) -GFX_TEST(gpath_masking) -GFX_TEST(rotated_bitmap_0_assign) -GFX_TEST(rotated_bitmap_0_set) -GFX_TEST(rotated_bitmap_45_assign) -GFX_TEST(rotated_bitmap_45_set) -GFX_TEST(rotated_bitmap_0_assign_64px) -GFX_TEST(rotated_bitmap_0_set_64px) -GFX_TEST(rotated_bitmap_45_assign_64px) -GFX_TEST(rotated_bitmap_45_set_64px) diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_rotated_bitmap.c b/src/fw/apps/demo_apps/gfx_tests/gfx_test_rotated_bitmap.c deleted file mode 100644 index 11b9e37a85..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_rotated_bitmap.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" -#include "util/trig.h" - -static GBitmap *s_gfx_rotated_bitmap_bitmap; -static GBitmap *s_gfx_rotated_bitmap_64_bitmap; - -static GPoint bitmap_center = {DISP_COLS / 2, DISP_ROWS / 2}; -static GPoint bitmap_64_center = {64 / 2, 64 / 2}; - -static void prv_setup(Window *window) { -#if SCREEN_COLOR_DEPTH_BITS == 1 - s_gfx_rotated_bitmap_bitmap = gbitmap_create_blank(GSize(DISP_COLS, DISP_ROWS), - GBitmapFormat1Bit); - s_gfx_rotated_bitmap_64_bitmap = gbitmap_create_blank(GSize(64, 64), GBitmapFormat1Bit); - uint32_t size_full = (DISP_COLS*DISP_ROWS)/8; -#else - s_gfx_rotated_bitmap_bitmap = gbitmap_create_blank(GSize(DISP_COLS, DISP_ROWS), - GBitmapFormat8Bit); - s_gfx_rotated_bitmap_64_bitmap = gbitmap_create_blank(GSize(64, 64), GBitmapFormat8Bit); - uint32_t size_full = (DISP_COLS*DISP_ROWS); -#endif - uint8_t *s_gfx_rotated_bitmap_pixels = s_gfx_rotated_bitmap_bitmap->addr; - // Init images - for (uint32_t index = 0; index < size_full; index++) { - s_gfx_rotated_bitmap_pixels[index] = (index % 2) ? 0xf0 : 0xcc; - } - -#if SCREEN_COLOR_DEPTH_BITS == 1 - uint32_t size_64 = (64*64)/8; -#else - uint32_t size_64 = (64*64); -#endif - uint8_t *s_gfx_rotated_bitmap_64_pixels = s_gfx_rotated_bitmap_64_bitmap->addr; - for (uint32_t index = 0; index < size_64; index++) { - s_gfx_rotated_bitmap_64_pixels[index] = (index % 2) ? 0xf0 : 0xcc; - } -} - -static void prv_test_0_assign(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpAssign); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, - GPointZero, 0, GPointZero); -} - -static void prv_test_0_set(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, - GPointZero, 0, GPointZero); -} - -static void prv_test_45_assign(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpAssign); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, - bitmap_center, DEG_TO_TRIGANGLE(45), bitmap_center); -} - -static void prv_test_45_set(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_bitmap, - bitmap_center, DEG_TO_TRIGANGLE(45), bitmap_center); -} - -static void prv_test_0_assign_64px(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpAssign); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, - GPointZero, 0, GPointZero); -} - -static void prv_test_0_set_64px(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, - GPointZero, 0, GPointZero); -} - -static void prv_test_45_assign_64px(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpAssign); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, - bitmap_64_center, DEG_TO_TRIGANGLE(45), bitmap_64_center); -} - -static void prv_test_45_set_64px(Layer *layer, GContext* ctx) { - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_rotated_bitmap(ctx, (GBitmap*)&s_gfx_rotated_bitmap_64_bitmap, - bitmap_64_center, DEG_TO_TRIGANGLE(45), bitmap_64_center); -} - -static void prv_teardown(Window *window) { - gbitmap_destroy(s_gfx_rotated_bitmap_bitmap); - gbitmap_destroy(s_gfx_rotated_bitmap_64_bitmap); -} - -GfxTest g_gfx_test_rotated_bitmap_0_assign = { - .name = "RotBit 0-A-full", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_0_assign, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_0_set = { - .name = "RotBit 0-S-full", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_0_set, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_45_assign = { - .name = "RotBit-45-A-full", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_45_assign, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_45_set = { - .name = "RotBit-45-S-full", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_45_set, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_0_assign_64px = { - .name = "RotBit-0-A-64px", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_0_assign_64px, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_0_set_64px = { - .name = "RotBit-0-S-64px", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_0_set_64px, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_45_assign_64px = { - .name = "RotBit-45-A-64px", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_45_assign_64px, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -GfxTest g_gfx_test_rotated_bitmap_45_set_64px = { - .name = "RotBit-45-S-64px", - .duration = 5, - .unit_multiple = 1, - .test_proc = prv_test_45_set_64px, - .setup = prv_setup, - .teardown = prv_teardown, -}; diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_single_line.c b/src/fw/apps/demo_apps/gfx_tests/gfx_test_single_line.c deleted file mode 100644 index 287c188ed3..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_single_line.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" - -static void prv_test(Layer *layer, GContext* ctx); - -GfxTest g_gfx_test_single_line = { - .name = "Single line", - .duration = 1, - .unit_multiple = 1, - .test_proc = prv_test, -}; - -static void prv_test(Layer *layer, GContext* ctx) { - GRect bounds = layer->bounds; - int16_t x1 = (rand() % bounds.size.w) + bounds.origin.x; - int16_t x2 = (rand() % bounds.size.w) + bounds.origin.x; - int16_t y1 = (rand() % bounds.size.h) + bounds.origin.y; - int16_t y2 = (rand() % bounds.size.h) + bounds.origin.y; - - GColor color = { .argb = (uint8_t) rand() }; - graphics_context_set_stroke_color(ctx, color); - graphics_draw_line(ctx, (GPoint){.x = x1, .y = y1}, (GPoint) {.x = x2, .y = y2}); -} diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_text.c b/src/fw/apps/demo_apps/gfx_tests/gfx_test_text.c deleted file mode 100644 index 325c404c3d..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_text.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" - -static void prv_setup(Window *window); -static void prv_test(Layer *layer, GContext* ctx); - -GfxTest g_gfx_test_text = { - .name = "Text", - .duration = 5, - .unit_multiple = 0, // Number of characters in test string - set later - .test_proc = prv_test, - .setup = prv_setup, -}; - -static GFont s_font; - -static void prv_setup(Window *window) { - s_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); -} - -static void prv_test(Layer *layer, GContext* ctx) { - const char *text_test_str = "Lorem ipsum dolor sit amet, ne choro argumentum est, quando latine " - "copiosae est ea, usu nonumes accusam te."; - g_gfx_test_text.unit_multiple = strlen(text_test_str); - GColor color = { .argb = (uint8_t) rand() }; - graphics_context_set_text_color(ctx, color); - graphics_draw_text(ctx, text_test_str, s_font, layer->bounds, - GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); -} diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_test_text_clipping.c b/src/fw/apps/demo_apps/gfx_tests/gfx_test_text_clipping.c deleted file mode 100644 index fd63f47e35..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_test_text_clipping.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" - -static void prv_setup(Window *window); -static void prv_test(Layer *layer, GContext* ctx); -static void prv_teardown(Window *window); - -GfxTest g_gfx_test_text_clipping = { - .name = "Text Clipping", - .duration = 5, - .unit_multiple = 0, // Number of characters in test string - set later - .test_proc = prv_test, - .setup = prv_setup, - .teardown = prv_teardown, -}; - -static GFont s_font; -static Layer s_canvas; - -static void prv_setup(Window *window) { - s_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - layer_init(&s_canvas, &GRect(40, 40, 80, 40)); - layer_add_child(&window->layer, &s_canvas); -} - -static void prv_test(Layer *layer, GContext* ctx) { - const char *text_test_str = "This is a test message that is really long!\"#$%&'()*+,-./01234" - "56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrs" - "tuvwxyz{|}"; - g_gfx_test_text_clipping.unit_multiple = strlen(text_test_str); - GColor color = { .argb = (uint8_t) rand() }; - graphics_context_set_text_color(ctx, color); - GRect bounds = layer->bounds; - bounds.origin.y -= 150; // Drop y by 150 pixels so some data gets clipped - graphics_draw_text(ctx, text_test_str, s_font, bounds, - GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); -} - -static void prv_teardown(Window *window) { - layer_remove_from_parent(&s_canvas); -} diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_tests.c b/src/fw/apps/demo_apps/gfx_tests/gfx_tests.c deleted file mode 100644 index 05a1eb0714..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_tests.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gfx_tests.h" - -#include "applib/ui/app_window_stack.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" -#include "system/profiler.h" - -#include -#include - -typedef struct { - Window menu_window; - Window test_window; - Window results_window; - MenuLayer test_menu; - TextLayer results_text; - char results_str[100]; - GfxTest *current_test; -} AppData; - -#define RAND_SEED (775762732) // Randomly selected -#define US_PER_MS (1000) -#define US_PER_S (1000 * 1000) -#define TARGET_FPS (30) -#define US_PER_FRAME (20 * US_PER_MS) // Upper bound on amount of time available to the rest of - // the system while a frame is being pushed out to the - // display with the cpu clock at 64MHz - -#define GFX_TEST(name) extern GfxTest g_gfx_test_##name; -#include "gfx_test_list.h" -#undef GFX_TEST -#define GFX_TEST(name) &g_gfx_test_##name, -static GfxTest *s_tests[] = { -#include "gfx_test_list.h" -}; - -static void prv_pop_test_window(void *data) { - AppData *app_data = data; - app_window_stack_pop(false); - app_window_stack_push(&app_data->results_window, false); -} - -static void prv_test_update_proc(Layer *layer, GContext* ctx) { - AppData *app_data = window_get_user_data(layer->window); - GfxTest *test = app_data->current_test; - - srand(RAND_SEED); // Setup rand for routines that need it - - if (test->setup) { - test->setup(layer->window); - } - - PROFILER_INIT; - PROFILER_START; - while (PROFILER_NODE_GET_TOTAL_US(gfx_test_update_proc) < (test->duration * US_PER_S)) { - PROFILER_NODE_START(gfx_test_update_proc); - test->test_proc(layer, ctx); - PROFILER_NODE_STOP(gfx_test_update_proc); - } - PROFILER_STOP; - PROFILER_PRINT_STATS; - - if (test->teardown) { - test->teardown(layer->window); - } - - app_timer_register(0, prv_pop_test_window, app_data); -} - -static void prv_start_test(GfxTest *test, AppData *app_data) { - app_data->current_test = test; - Window *window = &app_data->test_window; - window_init(window, WINDOW_NAME(test->name)); - window_set_user_data(window, app_data); - window_set_fullscreen(window, true); - layer_set_update_proc((Layer *) window, prv_test_update_proc); - app_window_stack_push(window, false); -} - -static uint16_t prv_get_num_rows(struct MenuLayer *menu_layer, uint16_t section, - void *callback_context) { - return (uint16_t) ARRAY_LENGTH(s_tests); -} - -static void prv_draw_row(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, - void *callback_context) { - PBL_ASSERTN(cell_index->row < ARRAY_LENGTH(s_tests)); - menu_cell_title_draw(ctx, cell_layer, s_tests[cell_index->row]->name); -} - -static void prv_click_handler(struct MenuLayer *menu_layer, MenuIndex *cell_index, - void *callback_context) { - PBL_ASSERTN(cell_index->row < ARRAY_LENGTH(s_tests)); - prv_start_test(s_tests[cell_index->row], (AppData *)callback_context); -} - -static void prv_handle_results_click(ClickRecognizerRef recognizer, void *context) { - app_window_stack_pop(false); -} - -static void prv_results_window_load(Window *window) { - AppData *app_data = window_get_user_data(window); - - uint32_t total_us = PROFILER_NODE_GET_TOTAL_US(gfx_test_update_proc); - uint32_t count = PROFILER_NODE_GET_COUNT(gfx_test_update_proc); - - GfxTest *test = app_data->current_test; - - uint32_t avg_us = (10 * total_us) / count; // multiply by 10 to get decimals - uint32_t per_frame = (100 * US_PER_FRAME) / avg_us; // multiply by 100 to get decimals and account - // for x10 in avg_us calc - uint32_t fps = (TARGET_FPS * 100 * US_PER_FRAME) / avg_us; - uint32_t unit_per_frame = ((uint64_t)test->unit_multiple * US_PER_FRAME * 100) / avg_us; - - snprintf(app_data->results_str, sizeof(app_data->results_str), - "%10s\n" - "Avg (µs):\n%"PRIu32".%"PRIu32"\n" - "FPS:\n%"PRIu32".%"PRIu32"\n" - "Per frame @ 30fps:\n%"PRIu32".%"PRIu32"\n" - "Units per frame @ 30fps:\n%"PRIu32".%"PRIu32, - test->name, avg_us / 10, avg_us % 10, fps / 10, fps % 10, per_frame / 10, per_frame % 10, - unit_per_frame / 10, unit_per_frame % 10); - PBL_LOG(LOG_LEVEL_DEBUG, "results: %s", app_data->results_str); - text_layer_set_text(&app_data->results_text, app_data->results_str); -} - -static void prv_results_window_unload(Window *window) { - AppData *app_data = window_get_user_data(window); - menu_layer_deinit(&app_data->test_menu); -} - -static void prv_results_window_click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_handle_results_click); -} - -static void handle_init(void) { - AppData *app_data = app_malloc_check(sizeof(AppData)); - - // Menu window - Window *window = &app_data->menu_window; - window_init(window, WINDOW_NAME("GFX Test Framework")); - window_set_user_data(window, app_data); - window_set_fullscreen(window, false); - - MenuLayer *menu = &app_data->test_menu; - menu_layer_init(menu, &window->layer.bounds); - menu_layer_set_callbacks(menu, app_data, &(MenuLayerCallbacks){ - .get_num_rows = prv_get_num_rows, - .draw_row = prv_draw_row, - .select_click = prv_click_handler, - }); - menu_layer_set_click_config_onto_window(menu, window); - layer_add_child((Layer *) window, (Layer *) menu); - app_window_stack_push(window, true); - - // Results window - window = &app_data->results_window; - window_init(window, WINDOW_NAME("Test Results")); - window_set_user_data(window, app_data); - window_set_fullscreen(window, false); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_results_window_load, - .unload = prv_results_window_unload, - }); - window_set_click_config_provider(window, prv_results_window_click_config_provider); - - TextLayer *text = &app_data->results_text; - text_layer_init(text, &window->layer.bounds); - text_layer_set_text(text, ""); - layer_add_child((Layer *) window, (Layer *) text); -} - -static void s_main(void) { - handle_init(); - app_event_loop(); -} - -const PebbleProcessMd* gfx_tests_get_app_info() { - static const PebbleProcessMdSystem gfx_tests_app_info = { - .name = "GFX Tests", - .common = { - .main_func = &s_main, - // UUID: 06a8126b-d805-4197-af6d-8df3c1efb8e4 - .uuid = { 0x06, 0xa8, 0x12, 0x6b, 0xd8, 0x05, 0x41, 0x97, 0xaf, 0x6d, 0x8d, 0xf3, - 0xc1, 0xef, 0xb8, 0xe4}, - } - }; - return (const PebbleProcessMd*) &gfx_tests_app_info; -} diff --git a/src/fw/apps/demo_apps/gfx_tests/gfx_tests.h b/src/fw/apps/demo_apps/gfx_tests/gfx_tests.h deleted file mode 100644 index a35cce0643..0000000000 --- a/src/fw/apps/demo_apps/gfx_tests/gfx_tests.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// All graphics/UI includes needed for tests. Add here if more are needed. -#include "applib/app.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/layer.h" -#include "applib/ui/window.h" -#include "applib/ui/window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/menu_layer.h" -#include "applib/fonts/fonts.h" -#include "util/trig.h" -#include "applib/graphics/gpath.h" - -#include - -//! GFX test struct -typedef struct GfxTest { - char *name; //!< Name string - uint32_t duration; //!< Number of seconds to run the test for - uint32_t unit_multiple; //!< Number of actions per test iteration - LayerUpdateProc test_proc; //!< Test procedure - void (*setup)(Window *window); //!< Test setup function - void (*teardown)( Window *window); //!< Test teardown function -} GfxTest; - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* gfx_tests_get_app_info(void); diff --git a/src/fw/apps/demo_apps/grenade_launcher.c b/src/fw/apps/demo_apps/grenade_launcher.c deleted file mode 100644 index 652e5916ef..0000000000 --- a/src/fw/apps/demo_apps/grenade_launcher.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "grenade_launcher.h" - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "drivers/flash.h" -#include "drivers/system_flash.h" -#include "flash_region/flash_region.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" - -#include - -/////////// -// Helpers - -static void fw_update_reboot(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "Rebooting to apply new firmware!"); - - boot_bit_set(BOOT_BIT_NEW_FW_AVAILABLE); - - services_set_runlevel(RunLevel_BareMinimum); - system_reset(); -} - -static void erase_fw(const uint32_t start_address) { - // Erase flash - flash_erase_sector_blocking(start_address); // Set everything high - - // Set up the firmware description - FirmwareDescription desc; - desc.description_length = sizeof(FirmwareDescription); - desc.firmware_length = (64 * 1024) - sizeof(FirmwareDescription); - desc.checksum = 0xdeadbeef; - - // Write the firmware description - flash_write_bytes((uint8_t*)&desc, start_address, desc.description_length); -} - -enum { - ERASE_NORMAL_FW = 1 << 0, - ERASE_RECOVERY_FW = 1 << 1, - ERASE_ALL = ERASE_NORMAL_FW | ERASE_RECOVERY_FW, -}; - -static void erase_callback(void *data) { - uintptr_t arg = (uintptr_t)data; - - if (arg & ERASE_RECOVERY_FW) { - erase_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN); - } - - if (arg & ERASE_NORMAL_FW) { - erase_fw(FLASH_REGION_FIRMWARE_DEST_BEGIN); - - system_flash_erase(FLASH_Sector_4); - system_flash_erase(FLASH_Sector_5); - system_flash_erase(FLASH_Sector_6); - system_flash_erase(FLASH_Sector_7); - } - - fw_update_reboot(); -} - -static void crash(void *data) { - ((void(*)(void))data)(); -} - -//////////////////// -// UI Code - -typedef struct { - Window window; - TextLayer text; -} AppData; - -static void set_text(Window *window, char *message) { - AppData *data = window_get_user_data(window); - TextLayer *text = &data->text; - text_layer_set_text(text, message); - PBL_LOG(LOG_LEVEL_DEBUG, "%s", message); -} - -static void up_click_handler(ClickRecognizerRef recognizer, Window *window) { - (void) recognizer; - set_text(window, "Erasing Normal+Sys firmware..."); - system_task_add_callback(erase_callback, (void *)ERASE_NORMAL_FW); -} - -#if 0 -static void up_long_click_handler(ClickRecognizerRef recognizer, Window *window) { - (void) recognizer; - set_text(window, "Erasing All..."); - system_task_add_callback(erase_callback, (void *)ERASE_ALL); -} -#endif - -static void select_click_handler(ClickRecognizerRef recognizer, Window *window) { - (void) recognizer; - set_text(window, "Going down for reboot..."); - system_task_add_callback(system_reset_callback, NULL); -} - -static void down_click_handler(ClickRecognizerRef recognizer, Window *window) { - (void) recognizer; - set_text(window, "Erasing recovery firmware"); - system_task_add_callback(erase_callback, (void *)ERASE_RECOVERY_FW); -} - -static void back_click_handler(ClickRecognizerRef recognizer, Window *window) { - (void) recognizer; - set_text(window, "Boom!"); - system_task_add_callback(crash, NULL); -} - -static void config_provider(Window *window) { - window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler) up_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, (ClickHandler) select_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) down_click_handler); - window_single_click_subscribe(BUTTON_ID_BACK, (ClickHandler) back_click_handler); -} - -static void prv_window_load(Window *window) { - AppData *data = window_get_user_data(window); - TextLayer *text = &data->text; - text_layer_init(text, &window->layer.bounds); - text_layer_set_text(text, "UP: Erase Normal+Sys FW\nUP LONG:Erase Normal+Recov+Sys\nSEL: Reboot FW\nDOWN: Erase Recovery\nBACK: Crash"); - text_layer_set_font(text, fonts_get_system_font(FONT_KEY_GOTHIC_14)); - layer_add_child(&window->layer, &text->layer); -} - -static void push_window(AppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Grenade Launcher")); - window_set_user_data(window, data); - window_set_overrides_back_button(window, true); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - window_set_click_config_provider(window, (ClickConfigProvider) config_provider); - window_set_fullscreen(window, true); - const bool animated = false; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - push_window(data); -} - -static void handle_deinit(void) { - AppData *data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* grenade_launcher_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = s_main, - .name = "Grenade Launcher" - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/grenade_launcher.h b/src/fw/apps/demo_apps/grenade_launcher.h deleted file mode 100644 index df3102377d..0000000000 --- a/src/fw/apps/demo_apps/grenade_launcher.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* grenade_launcher_app_get_info(); diff --git a/src/fw/apps/demo_apps/idl_demo.c b/src/fw/apps/demo_apps/idl_demo.c deleted file mode 100644 index abdf3c84cb..0000000000 --- a/src/fw/apps/demo_apps/idl_demo.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "idl_demo.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" -#include "system/hexdump.h" - -#include "pb_decode.h" -#include "pb_encode.h" -#include "nanopb/simple.pb.h" -#include "nanopb/measurements.pb.h" - -static void prv_init(void) { - SimpleMessage msg = { - .lucky_number = 42, - }; - uint8_t *buffer = app_malloc(30); - pb_ostream_t s = pb_ostream_from_buffer(buffer, 30); - pb_encode(&s, SimpleMessage_fields, &msg); - PBL_LOG(LOG_LEVEL_DEBUG, "Encoded message, size: %u bytes", s.bytes_written); - PBL_HEXDUMP(LOG_LEVEL_DEBUG, buffer, s.bytes_written); - app_state_set_user_data(buffer); -} - -static void prv_deinit(void) { - SimpleMessage msg = {0}; - uint8_t *buffer = app_state_get_user_data(); - pb_istream_t s = pb_istream_from_buffer(buffer, 30); - pb_decode(&s, SimpleMessage_fields, &msg); - PBL_LOG(LOG_LEVEL_DEBUG, "The lucky number is %"PRId32, msg.lucky_number); -} - -static void prv_app_main(void) { - prv_init(); - Window *window = window_create(); - app_window_stack_push(window, false /*animated*/); - app_event_loop(); - prv_deinit(); -} - -const PebbleProcessMd *idl_demo_get_app_info() { - static const PebbleProcessMdSystem s_app_data = { - .common = { - .main_func = prv_app_main, - // UUID: 101a32d95-1234-46d4-1234-854cc62f97f9 - .uuid = {0x99, 0xa3, 0x2d, 0x95, 0x12, 0x34, 0x46, 0xd4, - 0x12, 0x34, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9}, - }, - .name = "IDL Demo", - }; - return (const PebbleProcessMd *)&s_app_data; -} diff --git a/src/fw/apps/demo_apps/idl_demo.h b/src/fw/apps/demo_apps/idl_demo.h deleted file mode 100644 index 3f5a51db41..0000000000 --- a/src/fw/apps/demo_apps/idl_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* idl_demo_get_app_info(void); diff --git a/src/fw/apps/demo_apps/kill_bt_app.c b/src/fw/apps/demo_apps/kill_bt_app.c deleted file mode 100644 index 4801418b4a..0000000000 --- a/src/fw/apps/demo_apps/kill_bt_app.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "progress_app.h" - -#include - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" - -static unsigned int s_progress_count = 0; - -struct AppState { - Window window; -}; - -static void prv_window_load(Window *window) { - struct AppState* data = window_get_user_data(window); - (void)data; -} - -static void push_window(struct AppState *data) { - Window* window = &data->window; - window_init(window, WINDOW_NAME("Kill BT Demo")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - - -//////////////////// -// App boilerplate - -static void handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { - PBL_LOG(LOG_LEVEL_DEBUG, "Try to kill the BT:%d", s_progress_count); - s_progress_count += 1; - - bt_ctl_reset_bluetooth(); -} - -static void handle_init(void) { - struct AppState* data = app_malloc_check(sizeof(struct AppState)); - - app_state_set_user_data(data); - tick_timer_service_subscribe(SECOND_UNIT, handle_second_tick); - push_window(data); -} - -static void handle_deinit(void) { - struct AppState* data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* kill_bt_app_get_info() { - static const PebbleProcessMdSystem kill_bt_app_info = { - .name = "Kill BT Test", - .common.main_func = s_main, - }; - return (const PebbleProcessMd*) &kill_bt_app_info; -} - diff --git a/src/fw/apps/demo_apps/kill_bt_app.h b/src/fw/apps/demo_apps/kill_bt_app.h deleted file mode 100644 index 9284daaa03..0000000000 --- a/src/fw/apps/demo_apps/kill_bt_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* kill_bt_app_get_info(); diff --git a/src/fw/apps/demo_apps/kino_layer_demo.h b/src/fw/apps/demo_apps/kino_layer_demo.h deleted file mode 100644 index 560ede6d2c..0000000000 --- a/src/fw/apps/demo_apps/kino_layer_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *kino_layer_demo_get_info(void); diff --git a/src/fw/apps/demo_apps/light_config_app.c b/src/fw/apps/demo_apps/light_config_app.c deleted file mode 100644 index 2e38197e91..0000000000 --- a/src/fw/apps/demo_apps/light_config_app.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/number_window.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/light.h" - -#include - -static void selected_pwm_percentage(NumberWindow *nw, void *ctx) { - uint8_t val = number_window_get_value(nw); - backlight_set_intensity_percent(val); -} - -static void handle_init(void) { - NumberWindow *light_num_window = number_window_create("Light Config", - (NumberWindowCallbacks) { .selected = selected_pwm_percentage }, - NULL); - app_state_set_user_data(light_num_window); - - uint8_t scale_granularity = 5; // 5 percent at a time - uint8_t curr_percent = scale_granularity * ((backlight_get_intensity_percent() + - scale_granularity - 1) / scale_granularity); - - number_window_set_value(light_num_window, curr_percent); - number_window_set_max(light_num_window, 100); - number_window_set_min(light_num_window, 0); - number_window_set_step_size(light_num_window, scale_granularity); - - app_window_stack_push(number_window_get_window(light_num_window), true); -} - -static void handle_deinit(void) { - NumberWindow *data = app_state_get_user_data(); - number_window_destroy(data); -} - -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -const PebbleProcessMd* light_config_get_info() { - static const PebbleProcessMdSystem s_accel_config_info = { - .common.main_func = s_main, - .name = "Light Config" - }; - return (const PebbleProcessMd*) &s_accel_config_info; -} diff --git a/src/fw/apps/demo_apps/menu_app.c b/src/fw/apps/demo_apps/menu_app.c deleted file mode 100644 index 442df61989..0000000000 --- a/src/fw/apps/demo_apps/menu_app.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "menu_app.h" - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" -#include "system/passert.h" - -#include - -#define BUFFER_SIZE 25 - -static const uint8_t s_music_launcher_icon_pixels[] = { - 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, /* bytes 0 - 16 */ - 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x18, 0x00, 0x7f, 0x00, 0x1f, 0x00, /* bytes 16 - 32 */ - 0x7f, 0xf0, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 32 - 48 */ - 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 48 - 64 */ - 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x7f, 0x7c, 0x00, 0x00, /* bytes 64 - 80 */ - 0x03, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x80, 0x00, 0x00, 0x3c, 0xc0, 0x00, /* bytes 80 - 96 */ - 0x00, 0x7e, 0xe0, 0x00, 0x00, 0xff, 0xff, 0x00, 0x81, 0xff, 0xff, 0x00, -}; - -static const GBitmap s_music_launcher_icon_bitmap = { - .addr = (void*) &s_music_launcher_icon_pixels, - .row_size_bytes = 4, - .info_flags = 0x1000, - .bounds = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 24, .h = 27 }, - }, -}; - -typedef struct { - Window window; - MenuLayer menu_layer; - GBitmap icon; - - Window detail_window; - TextLayer detail_text; - char detail_text_buffer[50]; -} AppData; - -static uint16_t get_num_sections_callback(struct MenuLayer *menu_layer, AppData *data) { - (void)data; - (void)menu_layer; - return 4; -} - -static uint16_t get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { - (void)data; - (void)menu_layer; - switch (section_index) { - default: - case 0: return 2; - case 1: return 3; - case 2: return 4; - case 3: return 5; - } -} - -static int16_t get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { - (void)data; - (void)menu_layer; - (void)cell_index; - - // Variable row heights demo: - switch (cell_index->row % 3) { - default: - case 0: - return 44; - - case 1: - return 64; - - case 2: - return 84; - } -} - -static int16_t get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { - (void)data; - (void)menu_layer; - (void)section_index; - return MENU_CELL_BASIC_HEADER_HEIGHT; -} - -static void draw_row_callback(GContext* ctx, Layer *cell_layer, MenuIndex *cell_index, AppData *data) { - char title[BUFFER_SIZE]; - char subtitle[BUFFER_SIZE]; - - switch (cell_index->row % 2) { - case 0: - sniprintf(title, BUFFER_SIZE, "Title %i/%i ", cell_index->section, cell_index->row); - sniprintf(subtitle, BUFFER_SIZE, "Subtitle %i/%i", cell_index->section, cell_index->row); - menu_cell_basic_draw(ctx, cell_layer, title, subtitle, (GBitmap*)&s_music_launcher_icon_bitmap); - break; - - default: - case 1: - sniprintf(title, BUFFER_SIZE, "Only Title %i/%i", cell_index->section, cell_index->row); - menu_cell_title_draw(ctx, cell_layer, title); - break; - } - (void)data; -} - -static void draw_header_callback(GContext* ctx, Layer *cell_layer, uint16_t section_index, AppData *data) { - char title[BUFFER_SIZE]; - sniprintf(title, BUFFER_SIZE, "Section Header (%i)", section_index); - menu_cell_basic_header_draw(ctx, cell_layer, title); - (void)data; -} - -static void detail_window_load(Window *window) { - AppData *data = window_get_user_data(window); - TextLayer *text_layer = &data->detail_text; - text_layer_init(text_layer, &window->layer.bounds); - text_layer_set_text(text_layer, data->detail_text_buffer); - layer_add_child(&window->layer, &text_layer->layer); -} - -static void push_detail_window(AppData *data, MenuIndex *index, bool is_long_click) { - sniprintf(data->detail_text_buffer, 50, "SELECTION:\n\nSection %i, Row %i\nLong click: %c", index->section, index->row, is_long_click ? 'Y' : 'N'); - - Window *detail_window = &data->detail_window; - window_init(detail_window, WINDOW_NAME("Demo Menu Detail")); - window_set_user_data(detail_window, data); - window_set_window_handlers(detail_window, &(WindowHandlers) { - .load = detail_window_load, - }); - const bool animated = true; - app_window_stack_push(detail_window, animated); -} - -static void select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { - push_detail_window(data, cell_index, false); - (void)menu_layer; -} - -static void select_long_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { - push_detail_window(data, cell_index, true); - (void)menu_layer; -} - -static void prv_window_load(Window *window) { - AppData *data = window_get_user_data(window); - - MenuLayer *menu_layer = &data->menu_layer; - menu_layer_init(menu_layer, &window->layer.bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_sections = (MenuLayerGetNumberOfSectionsCallback) get_num_sections_callback, - .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) get_num_rows_callback, - .get_cell_height = (MenuLayerGetCellHeightCallback) get_cell_height_callback, - .get_header_height = (MenuLayerGetHeaderHeightCallback) get_header_height_callback, - .draw_row = (MenuLayerDrawRowCallback) draw_row_callback, - .draw_header = (MenuLayerDrawHeaderCallback) draw_header_callback, - .select_click = (MenuLayerSelectCallback) select_callback, - .select_long_click = (MenuLayerSelectCallback) select_long_callback, - }); - menu_layer_set_click_config_onto_window(menu_layer, window); - layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); -} - -static void push_window(AppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Demo Menu")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - push_window(data); -} - -static void handle_deinit(void) { - AppData *data = app_state_get_user_data(); - menu_layer_deinit(&data->menu_layer); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* menu_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "MenuLayer Demo" - }; - return (const PebbleProcessMd*) &s_app_info; -} - -#undef BUFFER_SIZE diff --git a/src/fw/apps/demo_apps/menu_app.h b/src/fw/apps/demo_apps/menu_app.h deleted file mode 100644 index 1ceba7307d..0000000000 --- a/src/fw/apps/demo_apps/menu_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* menu_app_get_info(); diff --git a/src/fw/apps/demo_apps/menu_layer_right_icon.h b/src/fw/apps/demo_apps/menu_layer_right_icon.h deleted file mode 100644 index 8a32fda175..0000000000 --- a/src/fw/apps/demo_apps/menu_layer_right_icon.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* menu_layer_right_icon_app_get_info(); diff --git a/src/fw/apps/demo_apps/menu_overflow_app.c b/src/fw/apps/demo_apps/menu_overflow_app.c deleted file mode 100644 index 2d63938eba..0000000000 --- a/src/fw/apps/demo_apps/menu_overflow_app.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "menu_overflow_app.h" - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "applib/ui/menu_layer.h" - - -static Window *window; -static MenuLayer *menu_layer; -static char *section_names[] = { "Movies", "Books", "Video Games", "Television", "Alcohol" }; -static char *row_names[5][2] = { - { "Avengers", "Eden of the East" }, - { "A Song of Ice and Fire", "Lord of the Rings" }, - { "Team Fortress 2", "Super Meat Boy" }, - { "Sunny in Philadelphia", "Gotham" }, - { "Beer", "Vodka" } -}; - - -//////////////////// -// MenuLayer construction and callback - -static uint16_t prv_menu_get_num_sections_callback(struct MenuLayer *menu_layer, - void *callback_context) { - return 5; -} - -static uint16_t prv_menu_get_num_rows_callback(struct MenuLayer *menu_layer, - uint16_t section_index, void *callback_context) { - return 2; -} - -static int16_t prv_menu_get_header_height_callback(struct MenuLayer *menu_layer, - uint16_t section_index, void *callback_context) { - return 15; -} - -static int16_t prv_menu_get_cell_height_callback(struct MenuLayer *menu_layer, - MenuIndex *cell_index, void *callback_context) { - return 20; -} - -static int16_t prv_menu_get_separator_height_callback(struct MenuLayer *menu_layer, - MenuIndex *cell_index, void *callback_context) { - return 10; -} - -static void prv_menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, - uint16_t section_index, void *callback_context) { - menu_cell_basic_header_draw(ctx, cell_layer, section_names[section_index]); -} - -static void prv_menu_draw_row_callback(GContext *ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *callback_context) { - graphics_context_set_text_color(ctx, GColorBlack); - graphics_draw_text(ctx, row_names[cell_index->section][cell_index->row], - fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), GRect(4, 2, 136, 22), - GTextOverflowModeFill, GTextAlignmentLeft, NULL); -} - -//////////////////// -// App boilerplate - -static void init(void) { - window = window_create(); - - menu_layer = menu_layer_create(window_get_root_layer(window)->bounds); - menu_layer_set_callbacks(menu_layer, NULL, &(MenuLayerCallbacks) { - .get_num_sections = prv_menu_get_num_sections_callback, - .get_num_rows = prv_menu_get_num_rows_callback, - .get_header_height = prv_menu_get_header_height_callback, - .get_cell_height = prv_menu_get_cell_height_callback, - .get_separator_height = prv_menu_get_separator_height_callback, - .draw_header = prv_menu_draw_header_callback, - .draw_row = prv_menu_draw_row_callback, - }); - - menu_layer_set_click_config_onto_window(menu_layer, window); - layer_add_child(window_get_root_layer(window), menu_layer_get_layer(menu_layer)); - - app_window_stack_push(window, true); -} - -static void deinit(void) { - menu_layer_destroy(menu_layer); - window_destroy(window); -} - -static void s_main(void) { - init(); - - app_event_loop(); - - deinit(); -} - -const PebbleProcessMd* menu_overflow_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Menu Overflow" - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/menu_overflow_app.h b/src/fw/apps/demo_apps/menu_overflow_app.h deleted file mode 100644 index e5a74c9c61..0000000000 --- a/src/fw/apps/demo_apps/menu_overflow_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* menu_overflow_app_get_info(); diff --git a/src/fw/apps/demo_apps/menu_round_app.c b/src/fw/apps/demo_apps/menu_round_app.c deleted file mode 100644 index 487b152e01..0000000000 --- a/src/fw/apps/demo_apps/menu_round_app.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "menu_round_app.h" - -#include "applib/app.h" -#include "applib/graphics/gdraw_command_image.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#include - -// Menu Detail -////////////////// - -typedef enum { - MenuLayerStyleTitle = 0, - MenuLayerStyleTitleAndSubtitle, - MenuLayerStyleTitleAndIconOnRight, - MenuLayerStyleTitleAndSubtitleAndValue, - MenuLayerStyleTitleAndSubtitleAndIcon, -} MenuLayerStyle; - -typedef struct { - char *title; - char *subtitle; - char *value; -} MenuDetailRowData; - -typedef struct { - Window window; - MenuLayer menu_layer; - StatusBarLayer status_bar_layer; - MenuLayerStyle style; -} MenuDetailWindowData; - - -static const MenuDetailRowData menu_detail_row_data_notifications[] = { - {"Liron Damir", "Late again. Sorry, I'll be on time in the future.", NULL}, - {"Angela Tam", "Late again? Can you be on time for once?", NULL}, - {"Eric Migicovsky", "Friday meeting will be held in the big room.", NULL}, - {"Intagram", "Keep scrolling down.", NULL}, - {"Liron Levak", "That's not my name.", NULL}, - {"Kimberly North West Kardashian", "I broke the Internet again.", NULL}, - {"Henry Damir", "That's not my name.", NULL}, - {"Kevin Conley", "Wubalubadubdub!", NULL}, -}; - -static const MenuDetailRowData menu_detail_row_data_days[] = { - {"Monday", NULL, NULL}, - {"Tuesday", NULL, NULL}, - {"Wednesday", NULL, NULL}, - {"Thursday", NULL, NULL}, - {"Friday", NULL, NULL}, - {"Saturday", NULL, NULL}, - {"Sunday", NULL, NULL}, -}; - -static const MenuDetailRowData menu_detail_row_data_alarms[] = { - {"8:00 AM", "Workdays", "ON"}, - {"10:00 AM", "Sat, Sun, Mon", "OFF"}, - {"11:30 AM", "Weekends", "ON"}, - {"5:00 PM", "Weekdays", "ON"}, -}; - -typedef struct { - const MenuDetailRowData *rows; - uint16_t num_rows; - int16_t selected_cell_height; - int16_t unselected_cell_height; - GColor highlight_background_color; -} MenuDetailInfo; - -static MenuDetailInfo prv_get_row_details_for_style(MenuLayerStyle style) { - switch (style) { - case MenuLayerStyleTitle: - return (MenuDetailInfo) { - .rows = menu_detail_row_data_notifications, - .num_rows = ARRAY_LENGTH(menu_detail_row_data_notifications), - .selected_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT, - .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, - .highlight_background_color = GColorFolly, - }; - case MenuLayerStyleTitleAndSubtitle: - return (MenuDetailInfo) { - .rows = menu_detail_row_data_notifications, - .num_rows = ARRAY_LENGTH(menu_detail_row_data_notifications), - .selected_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT, - .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, - .highlight_background_color = GColorIslamicGreen, - }; - case MenuLayerStyleTitleAndSubtitleAndIcon: - return (MenuDetailInfo) { - .rows = menu_detail_row_data_notifications, - .num_rows = ARRAY_LENGTH(menu_detail_row_data_notifications), - .selected_cell_height = MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT, - .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT, - .highlight_background_color = GColorFolly, - }; - case MenuLayerStyleTitleAndIconOnRight: - return (MenuDetailInfo) { - .rows = menu_detail_row_data_days, - .num_rows = ARRAY_LENGTH(menu_detail_row_data_days), - .selected_cell_height = menu_cell_basic_cell_height(), - .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, - .highlight_background_color = GColorIslamicGreen, - }; - case MenuLayerStyleTitleAndSubtitleAndValue: - return (MenuDetailInfo) { - .rows = menu_detail_row_data_alarms, - .num_rows = ARRAY_LENGTH(menu_detail_row_data_alarms), - .selected_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT, - .unselected_cell_height = MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT, - .highlight_background_color = GColorIslamicGreen, - }; - default: - WTF; - } -} - -static int16_t prv_get_cell_height_for_menu_layer(MenuLayer *menu_layer, MenuIndex *cell_index, - MenuLayerStyle style) { - const MenuDetailInfo row_details = prv_get_row_details_for_style(style); - return menu_layer_is_index_selected(menu_layer, cell_index) ? - row_details.selected_cell_height : - row_details.unselected_cell_height; -} - -static int16_t prv_menu_detail_get_cell_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, - void *context) { - MenuDetailWindowData *data = context; - return prv_get_cell_height_for_menu_layer(menu_layer, cell_index, data->style); -} -static uint16_t prv_menu_detail_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, - void *context) { - MenuDetailWindowData *data = context; - return prv_get_row_details_for_style(data->style).num_rows; -} - -static void prv_menu_detail_draw_row(GContext *ctx, const Layer *cell_layer, - MenuDetailRowData *row_data, MenuLayerStyle style) { - const GFont title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - switch (style) { - case MenuLayerStyleTitle: { - menu_cell_basic_draw_custom(ctx, cell_layer, title_font, row_data->title, title_font, NULL, - NULL, NULL, NULL, false /* icon_on_right */, - GTextOverflowModeWordWrap); - break; - } - case MenuLayerStyleTitleAndSubtitle: { - char *subtitle = menu_cell_layer_is_highlighted(cell_layer) ? row_data->subtitle : NULL; - menu_cell_basic_draw(ctx, cell_layer, row_data->title, subtitle, NULL); - break; - } - case MenuLayerStyleTitleAndIconOnRight: { - GBitmap *radio_button = gbitmap_create_with_resource(RESOURCE_ID_CHECKED_RADIO_BUTTON); - menu_cell_basic_draw_icon_right(ctx, cell_layer, row_data->title, row_data->subtitle, - radio_button); - gbitmap_destroy(radio_button); - break; - } - case MenuLayerStyleTitleAndSubtitleAndValue: { - const GFont subtitle_font = fonts_get_system_font(FONT_KEY_GOTHIC_14); - menu_cell_basic_draw_custom(ctx, cell_layer, title_font, row_data->title, title_font, - row_data->value, subtitle_font, row_data->subtitle, NULL, false, - GTextOverflowModeFill); - break; - } - case MenuLayerStyleTitleAndSubtitleAndIcon: { - GBitmap *icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_MENU_ICON_TICTOC_WATCH); - menu_cell_basic_draw(ctx, cell_layer, row_data->title, row_data->subtitle, icon_bitmap); - gbitmap_destroy(icon_bitmap); - break; - } - default: - WTF; - } -} - -static void prv_menu_detail_draw_row_callback(GContext* ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - MenuDetailWindowData *data = context; - const MenuDetailInfo menu_info = prv_get_row_details_for_style(data->style); - MenuDetailRowData row_data = menu_info.rows[cell_index->row]; - prv_menu_detail_draw_row(ctx, cell_layer, &row_data, data->style); -} - -static void prv_detail_window_load(Window *window) { - MenuDetailWindowData *data = window_get_user_data(window); - - MenuLayer *menu_layer = &data->menu_layer; - const GRect menu_layer_frame = grect_inset_internal(window->layer.bounds, 0, - STATUS_BAR_LAYER_HEIGHT); - menu_layer_init(menu_layer, &menu_layer_frame); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_cell_height = prv_menu_detail_get_cell_height, - .get_num_rows = prv_menu_detail_get_num_rows_callback, - .draw_row = prv_menu_detail_draw_row_callback, - }); - menu_layer_set_click_config_onto_window(menu_layer, window); - menu_layer_set_selected_index(menu_layer, MenuIndex(0, 1), MenuRowAlignCenter, false); - const MenuDetailInfo menu_info = prv_get_row_details_for_style(data->style); - menu_layer_set_highlight_colors(menu_layer, menu_info.highlight_background_color, GColorWhite); - layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); - - StatusBarLayer *status_bar = &data->status_bar_layer; - status_bar_layer_init(status_bar); - status_bar_layer_set_colors(status_bar, GColorClear, GColorBlack); - layer_add_child(&window->layer, &status_bar->layer); -} - -static void prv_detail_window_unload(Window *window) { - MenuDetailWindowData *data = window_get_user_data(window); - menu_layer_deinit(&data->menu_layer); - app_free(data); -} - -static void prv_push_detail_window(MenuLayerStyle menu_layer_style) { - MenuDetailWindowData *data = app_zalloc_check(sizeof(MenuDetailWindowData)); - data->style = menu_layer_style; - - Window *window = &data->window; - window_init(window, WINDOW_NAME("MenuLayer Round Demo Detail Menu")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_detail_window_load, - .unload = prv_detail_window_unload, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -// Menu Chooser -////////////////// - -typedef struct { - Window window; - MenuLayer menu_layer; - StatusBarLayer status_bar_layer; -} MenuChooserData; - -typedef struct { - char *title; - MenuLayerStyle style; -} MenuChooserRowData; - -static const MenuChooserRowData menu_chooser_row_data[] = { - {"Title Only", MenuLayerStyleTitle}, - {"Title & Subtitle", MenuLayerStyleTitleAndSubtitle}, - {"Title & Right Icon", MenuLayerStyleTitleAndIconOnRight}, - {"Title, Sub, Value", MenuLayerStyleTitleAndSubtitleAndValue}, - {"Title, Sub, Icon", MenuLayerStyleTitleAndSubtitleAndIcon}, -}; - -static int16_t prv_menu_chooser_get_cell_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, - void *context) { - return prv_get_cell_height_for_menu_layer(menu_layer, cell_index, MenuLayerStyleTitle); -} - -static uint16_t prv_menu_chooser_get_num_rows_callback(struct MenuLayer *menu_layer, - uint16_t section_index, - void *context) { - return ARRAY_LENGTH(menu_chooser_row_data); -} - -static void prv_menu_chooser_draw_row_callback(GContext* ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - MenuChooserRowData row_data = menu_chooser_row_data[cell_index->row]; - menu_cell_basic_draw(ctx, cell_layer, row_data.title, NULL, NULL); -} - -static void prv_menu_chooser_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, - void *context) { - prv_push_detail_window(menu_chooser_row_data[cell_index->row].style); -} - -static void prv_window_load(Window *window) { - MenuChooserData *data = window_get_user_data(window); - - MenuLayer *menu_layer = &data->menu_layer; - const GRect menu_layer_frame = grect_inset_internal(window->layer.bounds, 0, - STATUS_BAR_LAYER_HEIGHT); - menu_layer_init(menu_layer, &menu_layer_frame); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_cell_height = prv_menu_chooser_get_cell_height, - .get_num_rows = prv_menu_chooser_get_num_rows_callback, - .draw_row = prv_menu_chooser_draw_row_callback, - .select_click = prv_menu_chooser_select_callback, - }); - menu_layer_set_click_config_onto_window(menu_layer, window); - menu_layer_set_selected_index(menu_layer, MenuIndex(0, 1), MenuRowAlignCenter, false); - menu_layer_set_highlight_colors(menu_layer, GColorPictonBlue, GColorWhite); - layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); - - StatusBarLayer *status_bar = &data->status_bar_layer; - status_bar_layer_init(status_bar); - status_bar_layer_set_colors(status_bar, GColorClear, GColorBlack); - layer_add_child(&window->layer, &status_bar->layer); -} - -static void prv_window_unload(Window *window) { - MenuChooserData *data = window_get_user_data(window); - menu_layer_deinit(&data->menu_layer); -} - -// App boilerplate -//////////////////// - -static void prv_init(void) { - MenuChooserData *data = app_zalloc_check(sizeof(MenuChooserData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("MenuLayer Round Demo Chooser Menu")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -static void prv_deinit(void) { - MenuChooserData *data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - prv_init(); - app_event_loop(); - prv_deinit(); -} - -const PebbleProcessMd* menu_round_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "MenuLayer Round Demo" - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/menu_round_app.h b/src/fw/apps/demo_apps/menu_round_app.h deleted file mode 100644 index 0413c0afc5..0000000000 --- a/src/fw/apps/demo_apps/menu_round_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* menu_round_app_get_info(); diff --git a/src/fw/apps/demo_apps/morph_square_demo.h b/src/fw/apps/demo_apps/morph_square_demo.h deleted file mode 100644 index 3e323568cf..0000000000 --- a/src/fw/apps/demo_apps/morph_square_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *morph_square_demo_get_info(void); diff --git a/src/fw/apps/demo_apps/movable_line.h b/src/fw/apps/demo_apps/movable_line.h deleted file mode 100644 index 7837c84f68..0000000000 --- a/src/fw/apps/demo_apps/movable_line.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* movable_line_get_app_info(); diff --git a/src/fw/apps/demo_apps/number_field_app.c b/src/fw/apps/demo_apps/number_field_app.c deleted file mode 100644 index a3b9d575dc..0000000000 --- a/src/fw/apps/demo_apps/number_field_app.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "number_field_app.h" - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" -#include "system/passert.h" - -typedef struct { - NumberWindow num; -} AppData; - -static void selected(NumberWindow *nw, void *ctx) { - PBL_LOG(LOG_LEVEL_DEBUG, "selected: %"PRId32, number_window_get_value(nw)); - - const bool animated = true; - app_window_stack_pop(animated); - - (void)ctx; -} - -static void handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - number_window_init(&data->num, "Some Number", - (NumberWindowCallbacks) { .selected = selected }, - data); - - number_window_set_min(&data->num, 10); - number_window_set_max(&data->num, 100); - number_window_set_step_size(&data->num, 5); - - const bool animated = true; - app_window_stack_push(&data->num.window, animated); -} - -static void handle_deinit(void) { - AppData *data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* number_field_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "NumberField Demo" - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/number_field_app.h b/src/fw/apps/demo_apps/number_field_app.h deleted file mode 100644 index 80169f0f6f..0000000000 --- a/src/fw/apps/demo_apps/number_field_app.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* number_field_app_get_info(); - diff --git a/src/fw/apps/demo_apps/option_menu_demo.h b/src/fw/apps/demo_apps/option_menu_demo.h deleted file mode 100644 index fa9ff6e9a6..0000000000 --- a/src/fw/apps/demo_apps/option_menu_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *option_menu_demo_get_app_info(void); diff --git a/src/fw/apps/demo_apps/pebble_colors.h b/src/fw/apps/demo_apps/pebble_colors.h deleted file mode 100644 index 09b217f623..0000000000 --- a/src/fw/apps/demo_apps/pebble_colors.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* pebble_colors_get_app_info(); diff --git a/src/fw/apps/demo_apps/pebble_shapes.h b/src/fw/apps/demo_apps/pebble_shapes.h deleted file mode 100644 index 71a907e22d..0000000000 --- a/src/fw/apps/demo_apps/pebble_shapes.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* pebble_shapes_get_app_info(); diff --git a/src/fw/apps/demo_apps/persist_app.c b/src/fw/apps/demo_apps/persist_app.c deleted file mode 100644 index 50fe0cdfc0..0000000000 --- a/src/fw/apps/demo_apps/persist_app.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "persist_app.h" - -#include "applib/app.h" -#include "process_state/app_state/app_state.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" - -#include "applib/persist.h" - -#include - -#define BUFFER_SIZE 25 - -static const uint8_t s_music_launcher_icon_pixels[] = { - 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, /* bytes 0 - 16 */ - 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x18, 0x00, 0x7f, 0x00, 0x1f, 0x00, /* bytes 16 - 32 */ - 0x7f, 0xf0, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 32 - 48 */ - 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, /* bytes 48 - 64 */ - 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x1f, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x7f, 0x7c, 0x00, 0x00, /* bytes 64 - 80 */ - 0x03, 0x3c, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x80, 0x00, 0x00, 0x3c, 0xc0, 0x00, /* bytes 80 - 96 */ - 0x00, 0x7e, 0xe0, 0x00, 0x00, 0xff, 0xff, 0x00, 0x81, 0xff, 0xff, 0x00, -}; - -static const GBitmap s_music_launcher_icon_bitmap = { - .addr = (void*) &s_music_launcher_icon_pixels, - .row_size_bytes = 4, - .info_flags = 0x1000, - .bounds = { - .origin = { .x = 0, .y = 0 }, - .size = { .w = 24, .h = 27 }, - }, -}; - -typedef struct { - Window window; - MenuLayer menu_layer; - GBitmap icon; - - Window detail_window; - TextLayer detail_text; - char detail_text_buffer[50]; -} AppData; - -static const uint32_t COUNT_PKEY = 1; - -static uint16_t get_num_sections_callback(struct MenuLayer *menu_layer, AppData *data) { - (void)data; - (void)menu_layer; - return 1; -} - -static uint16_t get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { - (void)data; - (void)menu_layer; - switch (section_index) { - default: - case 0: return 3; - } -} - -static int16_t get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, AppData *data) { - (void)data; - (void)menu_layer; - (void)section_index; - return MENU_CELL_BASIC_HEADER_HEIGHT; -} - -static void draw_row_callback(GContext* ctx, Layer *cell_layer, MenuIndex *cell_index, AppData *data) { - (void)data; - switch (cell_index->row) { - case 0: { - int num_beers = persist_read_int(COUNT_PKEY); - char title[50]; - snprintf(title, sizeof(title), "%d Bottles", num_beers); - menu_cell_basic_draw(ctx, cell_layer, title, "of beer on the wall", (GBitmap*)&s_music_launcher_icon_bitmap); - break; - } - case 1: - menu_cell_title_draw(ctx, cell_layer, "Order More"); - break; - case 2: - menu_cell_title_draw(ctx, cell_layer, "Drink!"); - break; - } -} - -static void draw_header_callback(GContext* ctx, Layer *cell_layer, uint16_t section_index, AppData *data) { - (void)section_index; - (void)data; - menu_cell_basic_header_draw(ctx, cell_layer, "Beer Counter"); -} - -static void select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { - (void)menu_layer; - (void)data; - switch (cell_index->row) { - case 1: { - int num_beers = persist_read_int(COUNT_PKEY); - int status = persist_write_int(COUNT_PKEY, num_beers+1); - PBL_LOG(LOG_LEVEL_DEBUG, "argh %d %d", num_beers, status); - menu_layer_reload_data(menu_layer); - break; - } - case 2: { - int num_beers = persist_read_int(COUNT_PKEY); - persist_write_int(COUNT_PKEY, num_beers-1); - menu_layer_reload_data(menu_layer); - break; - } - } -} - -static void select_long_callback(MenuLayer *menu_layer, MenuIndex *cell_index, AppData *data) { - (void)menu_layer; - (void)data; - switch (cell_index->row) { - case 1: { - int num_beers = persist_read_int(COUNT_PKEY); - persist_write_int(COUNT_PKEY, num_beers + (500 + rand() % 500)); - menu_layer_reload_data(menu_layer); - break; - } - case 2: { - int num_beers = persist_read_int(COUNT_PKEY); - persist_write_int(COUNT_PKEY, num_beers - (500 + rand() % 500)); - menu_layer_reload_data(menu_layer); - } - } -} - -static void prv_window_load(Window *window) { - AppData *data = window_get_user_data(window); - - MenuLayer *menu_layer = &data->menu_layer; - menu_layer_init(menu_layer, &window->layer.bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_sections = (MenuLayerGetNumberOfSectionsCallback) get_num_sections_callback, - .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) get_num_rows_callback, - .get_header_height = (MenuLayerGetHeaderHeightCallback) get_header_height_callback, - .draw_row = (MenuLayerDrawRowCallback) draw_row_callback, - .draw_header = (MenuLayerDrawHeaderCallback) draw_header_callback, - .select_click = (MenuLayerSelectCallback) select_callback, - .select_long_click = (MenuLayerSelectCallback) select_long_callback, - }); - menu_layer_set_click_config_onto_window(menu_layer, window); - layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); -} - -static void push_window(AppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Demo Menu")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void handle_init() { - AppData *data = (AppData *)app_zalloc_check(sizeof(AppData)); - app_state_set_user_data(data); - push_window(data); - - const int exist_result = persist_exists(COUNT_PKEY); - PBL_LOG(LOG_LEVEL_DEBUG, "- exist_result %d", exist_result); - if (exist_result == false) { - PBL_LOG(LOG_LEVEL_DEBUG, "- writing..."); - persist_write_int(COUNT_PKEY, 10); - } -} - -static void handle_deinit() { - AppData *data = app_state_get_user_data(); - menu_layer_deinit(&data->menu_layer); - app_free(data); -} - -static void s_main() { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* persist_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Persist Demo" - }; - return (const PebbleProcessMd*) &s_app_info; -} - -#undef BUFFER_SIZE diff --git a/src/fw/apps/demo_apps/persist_app.h b/src/fw/apps/demo_apps/persist_app.h deleted file mode 100644 index 28db454bc8..0000000000 --- a/src/fw/apps/demo_apps/persist_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* persist_app_get_info(); diff --git a/src/fw/apps/demo_apps/profile_mutexes_app.c b/src/fw/apps/demo_apps/profile_mutexes_app.c deleted file mode 100644 index 5ca82d2b95..0000000000 --- a/src/fw/apps/demo_apps/profile_mutexes_app.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "profile_mutexes_app.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "applib/ui/window_stack.h" - -#include "system/logging.h" -#include "os/mutex.h" -#include "system/profiler.h" - -static Window *window; -static PebbleMutex *s_mutex; -static PebbleRecursiveMutex *s_rmutex; - -static void profile_mutexes(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "INITIALIZING PROFILER FOR MUTEXES!"); - PROFILER_INIT; - PROFILER_START; - - s_mutex = mutex_create(); - for (int i=0; i < 10000; i++) { - mutex_lock(s_mutex); - mutex_unlock(s_mutex); - } - mutex_destroy(s_mutex); - s_mutex = NULL; - - s_rmutex = mutex_create_recursive(); - for (int i=0; i < 10000; i++) { - mutex_lock_recursive(s_rmutex); - } - for (int i=0; i < 10000; i++) { - mutex_unlock_recursive(s_rmutex); - } - mutex_destroy((PebbleMutex *)s_rmutex); - s_rmutex = NULL; - - PROFILER_STOP; - PROFILER_PRINT_STATS; -} - -static void s_main(void) { - window = window_create(); - app_window_stack_push(window, true /* Animated */); - - profile_mutexes(); - - app_event_loop(); -} - -const PebbleProcessMd* profile_mutexes_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Profile Mutexes" - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/profile_mutexes_app.h b/src/fw/apps/demo_apps/profile_mutexes_app.h deleted file mode 100644 index fba5f8a06e..0000000000 --- a/src/fw/apps/demo_apps/profile_mutexes_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* profile_mutexes_get_app_info(void); diff --git a/src/fw/apps/demo_apps/progress_app.c b/src/fw/apps/demo_apps/progress_app.c deleted file mode 100644 index b4a50b4c06..0000000000 --- a/src/fw/apps/demo_apps/progress_app.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "progress_app.h" - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" -#include "util/math.h" - -#define PROGRESS_STEP 2 - -typedef struct { - Window window; - ProgressLayer progress_layer; - int progress; -} ProgressAppData; - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { - // Reset - ProgressAppData *data = context; - data->progress = MIN_PROGRESS_PERCENT; - progress_layer_set_progress(&data->progress_layer, data->progress); -} - -static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) { - // Increment - ProgressAppData *data = context; - data->progress = MIN(MAX_PROGRESS_PERCENT, data->progress + PROGRESS_STEP); - progress_layer_set_progress(&data->progress_layer, data->progress); -} - -static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) { - // Decrement - ProgressAppData *data = context; - data->progress = MAX(MIN_PROGRESS_PERCENT, data->progress - PROGRESS_STEP); - progress_layer_set_progress(&data->progress_layer, data->progress); -} - -static void prv_click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_UP, 200, prv_up_click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 200, prv_down_click_handler); -} - -static void prv_window_load(Window *window) { - ProgressAppData *data = window_get_user_data(window); - data->progress = (MIN_PROGRESS_PERCENT + MAX_PROGRESS_PERCENT) / 2; - - ProgressLayer* progress_layer = &data->progress_layer; - - const GRect *frame = &window_get_root_layer(window)->frame; - - static const uint32_t MARGIN = 20; - static const uint32_t HEIGHT = PBL_IF_COLOR_ELSE(6, 7); - const GRect progress_bounds = GRect(MARGIN, (frame->size.h - HEIGHT) / 2, - frame->size.w - (2 * MARGIN), HEIGHT); - progress_layer_init(progress_layer, &progress_bounds); - progress_layer_set_progress(progress_layer, data->progress); - layer_add_child(&window->layer, &progress_layer->layer); - - progress_layer_set_corner_radius(progress_layer, PBL_IF_COLOR_ELSE(2, 3)); -} - -static void handle_init(void) { - ProgressAppData *data = app_zalloc_check(sizeof(ProgressAppData)); - app_state_set_user_data(data); - - Window* window = &data->window; - window_init(window, WINDOW_NAME("Progress Demo")); - window_set_user_data(window, data); - window_set_click_config_provider_with_context(window, prv_click_config_provider, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -static void handle_deinit(void) { - struct AppState* data = app_state_get_user_data(); - tick_timer_service_unsubscribe(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* progress_app_get_info() { - static const PebbleProcessMdSystem progress_app_info = { - .common.main_func = &s_main, - .name = "Progress Bar Test" - }; - return (const PebbleProcessMd*) &progress_app_info; -} - diff --git a/src/fw/apps/demo_apps/progress_app.h b/src/fw/apps/demo_apps/progress_app.h deleted file mode 100644 index 23b1da8df1..0000000000 --- a/src/fw/apps/demo_apps/progress_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* progress_app_get_info(); diff --git a/src/fw/apps/demo_apps/scroll_app.c b/src/fw/apps/demo_apps/scroll_app.c deleted file mode 100644 index 2586a9a2c5..0000000000 --- a/src/fw/apps/demo_apps/scroll_app.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "scroll_app.h" - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" - -typedef struct { - Window window; - ScrollLayer scroll_layer; - TextLayer text; - InverterLayer inverter; -} ScrollAppData; - -static void select_click_handler(ClickRecognizerRef recognizer, ScrollAppData *data) { - PBL_LOG(LOG_LEVEL_DEBUG, "SELECT clicked!"); - (void)data; - (void)recognizer; -} - -#if 0 -static void select_long_click_handler(ClickRecognizerRef recognizer, ScrollAppData *data) { - PBL_LOG(LOG_LEVEL_DEBUG, "SELECT long clicked!"); - (void)data; - (void)recognizer; -} -#endif - -static void click_config_provider(ScrollAppData *data) { - // The config that gets passed in, has already the UP and DOWN buttons configured - // to scroll up and down. It's possible to override that here, if needed. - - // Configure how the SELECT button should behave: - window_single_click_subscribe(BUTTON_ID_SELECT, (ClickHandler) select_click_handler); - window_long_click_subscribe(BUTTON_ID_SELECT, 0, (ClickHandler) select_click_handler, NULL); -} - -static void prv_window_load(Window *window) { - ScrollAppData *data = window_get_user_data(window); - - ScrollLayer *scroll_layer = &data->scroll_layer; - scroll_layer_init(scroll_layer, &window->layer.bounds); - scroll_layer_set_click_config_onto_window(scroll_layer, window); - scroll_layer_set_callbacks(scroll_layer, (ScrollLayerCallbacks) { - .click_config_provider = (ClickConfigProvider) click_config_provider, - }); - scroll_layer_set_context(scroll_layer, data); - scroll_layer_set_content_size(scroll_layer, GSize(window->layer.bounds.size.w, 500)); - - const GRect max_text_bounds = GRect(0, 0, window->layer.bounds.size.w, 500); - TextLayer *text = &data->text; - text_layer_init(text, &max_text_bounds); - text_layer_set_text(text, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam quam tellus, fermentum quis vulputate quis, vestibulum interdum sapien. Vestibulum lobortis pellentesque pretium. Quisque ultricies purus eu orci convallis lacinia. Cras a urna mi. Donec convallis ante id dui dapibus nec ullamcorper erat egestas. Aenean a mauris a sapien commodo lacinia. Sed posuere mi vel risus congue ornare. Curabitur leo nisi, euismod ut pellentesque sed, suscipit sit amet lorem. Aliquam eget sem vitae sem aliquam ornare. In sem sapien, imperdiet eget pharetra a, lacinia ac justo. Suspendisse at ante nec felis facilisis eleifend."); - - // Trim text layer and scroll content to fit text box - GSize max_size = text_layer_get_content_size(app_state_get_graphics_context(), text); - text_layer_set_size(text, max_size); - static const int vert_scroll_padding = 4; - scroll_layer_set_content_size(scroll_layer, GSize(window->layer.bounds.size.w, - max_size.h + vert_scroll_padding)); - - scroll_layer_add_child(scroll_layer, &text->layer); - - InverterLayer *inverter = &data->inverter; - inverter_layer_init(inverter, &GRect(15, 15, 30, 30)); - scroll_layer_add_child(scroll_layer, &inverter->layer); - - layer_add_child(&window->layer, &scroll_layer->layer); -} - -static void push_window(ScrollAppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Scroll Demo")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void handle_init(void) { - ScrollAppData *data = app_malloc_check(sizeof(ScrollAppData)); - - app_state_set_user_data(data); - push_window(data); -} - -static void handle_deinit(void) { - ScrollAppData *data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* scroll_app_get_info() { - static const PebbleProcessMdSystem s_scroll_app_info = { - .common.main_func = &s_main, - .name = "Scroller" - }; - return (const PebbleProcessMd*) &s_scroll_app_info; -} - diff --git a/src/fw/apps/demo_apps/scroll_app.h b/src/fw/apps/demo_apps/scroll_app.h deleted file mode 100644 index b0463acbfd..0000000000 --- a/src/fw/apps/demo_apps/scroll_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* scroll_app_get_info(); diff --git a/src/fw/apps/demo_apps/simple_menu_app.c b/src/fw/apps/demo_apps/simple_menu_app.c deleted file mode 100644 index ef8682078d..0000000000 --- a/src/fw/apps/demo_apps/simple_menu_app.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "simple_menu_app.h" - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "services/common/poll_remote.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -typedef struct { - Window window; - SimpleMenuLayer menu; -} AppData; - -static void callback_a(int index, void *ctx) { - (void)index; - (void)ctx; - PBL_LOG(LOG_LEVEL_DEBUG, "A called back"); -} - -static void other_callback(int index, void *ctx) { - (void)ctx; - PBL_LOG(LOG_LEVEL_DEBUG, "other callback: %d", index); -} - -static void poll_callback(int index, void *ctx) { - poll_remote_send_request(POLL_REMOTE_SERVICE_MAIL); -} - -static const SimpleMenuItem s_menu_items[] = { - { "Poll Mail", "", NULL, poll_callback }, - { "Title A", "Callback A", NULL, callback_a }, - { "Another Title", NULL, NULL, other_callback }, - { "Last Title", "Last subtitle", NULL, other_callback } -}; - -static const SimpleMenuSection s_menu_sections[] = {{ .title = NULL, .items = s_menu_items, .num_items = ARRAY_LENGTH(s_menu_items) }}; - -static void prv_window_load(Window *window) { - AppData *data = window_get_user_data(window); - - SimpleMenuLayer *menu = &data->menu; - simple_menu_layer_init(menu, &(GRect){{0, 0}, window->layer.frame.size}, window, s_menu_sections, - ARRAY_LENGTH(s_menu_sections), data); - layer_add_child(&window->layer, simple_menu_layer_get_layer(menu)); -} - -static void handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("Simple Menu Demo")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load - }); - - const bool animated = true; - app_window_stack_push(window, animated); -} - -static void handle_deinit(void) { - AppData *data = app_state_get_user_data(); - simple_menu_layer_deinit(&data->menu); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* simple_menu_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "SimpleMenuLayer Demo" - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/simple_menu_app.h b/src/fw/apps/demo_apps/simple_menu_app.h deleted file mode 100644 index 4992c14188..0000000000 --- a/src/fw/apps/demo_apps/simple_menu_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* simple_menu_app_get_info(); diff --git a/src/fw/apps/demo_apps/statusbar_demo.h b/src/fw/apps/demo_apps/statusbar_demo.h deleted file mode 100644 index 35de60e2bd..0000000000 --- a/src/fw/apps/demo_apps/statusbar_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* statusbar_demo_get_app_info(void); diff --git a/src/fw/apps/demo_apps/stroke_width.h b/src/fw/apps/demo_apps/stroke_width.h deleted file mode 100644 index fd4c465bd9..0000000000 --- a/src/fw/apps/demo_apps/stroke_width.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* stroke_width_get_app_info(); diff --git a/src/fw/apps/demo_apps/swap_layer_demo.h b/src/fw/apps/demo_apps/swap_layer_demo.h deleted file mode 100644 index 0b5b001740..0000000000 --- a/src/fw/apps/demo_apps/swap_layer_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* swap_layer_demo_get_app_info(void); diff --git a/src/fw/apps/demo_apps/temperature_demo.h b/src/fw/apps/demo_apps/temperature_demo.h deleted file mode 100644 index 70bf862e26..0000000000 --- a/src/fw/apps/demo_apps/temperature_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* temperature_demo_get_app_info(void); diff --git a/src/fw/apps/demo_apps/test_args_rx.c b/src/fw/apps/demo_apps/test_args_rx.c deleted file mode 100644 index ade1c31c87..0000000000 --- a/src/fw/apps/demo_apps/test_args_rx.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "test_args_rx.h" -#include "test_args_tx.h" - -#include "applib/app.h" -#include "kernel/event_loop.h" -#include "process_management/app_manager.h" -#include "process_management/process_manager.h" -#include "system/logging.h" - -static void s_main(void) { - const TestArgsData *args = process_manager_get_current_process_args(); - if (args == NULL) { - PBL_LOG(LOG_LEVEL_DEBUG, "Got no args."); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Got argument 0x%x", args->data); - } -} - -const PebbleProcessMd* test_args_receiver_get_app_info() { - static const PebbleProcessMdSystem test_args_receiver_demo_app_info = { - .common.uuid = {0x48, 0xBB, 0xB5, 0x04, 0x5A, 0x56, 0x40, 0x73, 0xAF, - 0xEA, 0x5D, 0x83, 0x8D, 0x43, 0x01, 0xA4}, - .common.main_func = s_main, - .name = "Args Receiver Demo" - }; - return (const PebbleProcessMd*) &test_args_receiver_demo_app_info; -} diff --git a/src/fw/apps/demo_apps/test_args_rx.h b/src/fw/apps/demo_apps/test_args_rx.h deleted file mode 100644 index 62a2e6fccc..0000000000 --- a/src/fw/apps/demo_apps/test_args_rx.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* test_args_receiver_get_app_info(); diff --git a/src/fw/apps/demo_apps/test_args_tx.c b/src/fw/apps/demo_apps/test_args_tx.c deleted file mode 100644 index 2c4d49813c..0000000000 --- a/src/fw/apps/demo_apps/test_args_tx.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "test_args_tx.h" -#include "test_args_rx.h" - -#include "applib/app.h" -#include "kernel/event_loop.h" -#include "process_management/app_manager.h" -#include "process_management/app_install_manager.h" -#include "process_management/process_manager.h" -#include "system/logging.h" - -static TestArgsData s_data; - -static void prv_launch_receiver_callback(void *data) { - AppInstallId id = app_install_get_id_for_uuid(&(test_args_receiver_get_app_info()->uuid)); - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = id, - .common.args = data, - }); -} - -static void s_main(void) { - s_data.data = 0x43; - PBL_LOG(LOG_LEVEL_DEBUG, "Launching again with argument: 0x%x", s_data.data); - launcher_task_add_callback(prv_launch_receiver_callback, &s_data); -} - -const PebbleProcessMd* test_args_sender_get_app_info() { - static const PebbleProcessMdSystem test_args_sender_demo_app_info = { - .common.uuid = {0xD1, 0x7E, 0x41, 0xAF, 0x40, 0x5E, 0x40, 0x76, - 0x82, 0xB5, 0x97, 0x71, 0x70, 0x52, 0x66, 0xBA}, - .common.main_func = s_main, - .name = "Args Sender Demo" - }; - return (const PebbleProcessMd*) &test_args_sender_demo_app_info; -} diff --git a/src/fw/apps/demo_apps/test_args_tx.h b/src/fw/apps/demo_apps/test_args_tx.h deleted file mode 100644 index 808402a22a..0000000000 --- a/src/fw/apps/demo_apps/test_args_tx.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -typedef struct { - uint8_t data; -} TestArgsData; - -const PebbleProcessMd* test_args_sender_get_app_info(); diff --git a/src/fw/apps/demo_apps/test_bluetooth_app.c b/src/fw/apps/demo_apps/test_bluetooth_app.c deleted file mode 100644 index 9081131f43..0000000000 --- a/src/fw/apps/demo_apps/test_bluetooth_app.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "process_management/pebble_process_md.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" - -#define NUM_MENU_ITEMS 2 - -// ================================================================================= -// Application Data -typedef struct { - Window *window; - SimpleMenuLayer *menu_layer; - SimpleMenuSection menu_section; - SimpleMenuItem menu_items[NUM_MENU_ITEMS]; - -} TestBTAppData; - -static TestBTAppData *s_app_data = 0; - - -static volatile int s_pending_count = 0; -static volatile bool s_connected = false; - -// ================================================================================= -static void send_bluetooth(void* data) { - //uint8_t *buffer = (uint8_t *)"hello world"; - - CommSession *session = comm_session_get_system_session(); - if (!session) { - s_pending_count--; - s_connected = false; - return; - } - - PBL_LOG(LOG_LEVEL_INFO, "sending data"); - comm_session_send_data(session, 2000, (uint8_t *)0x08000000, - comm_session_send_buffer_get_max_payload_length(session), - COMM_SESSION_DEFAULT_TIMEOUT); - s_pending_count--; -} - - -// ================================================================================= -// You can capture when the user selects a menu icon with a menu item select callback -static void menu_select_callback(int index, void *ctx) { - PBL_LOG(LOG_LEVEL_DEBUG, "Hit menu item %d", index); - - // Here we just change the subtitle to a literal string - s_app_data->menu_items[index].subtitle = "You've hit select here!"; - - // Mark the layer to be updated - layer_mark_dirty(simple_menu_layer_get_layer(s_app_data->menu_layer)); - - // --------------------------------------------------------------------------- - // Run the appropriate test - if (index == 0) { - s_connected = true; - - // Flood bluetooth - while (s_connected) { - while (s_pending_count > 6 && s_connected) { - psleep(100); - } - s_pending_count++; - system_task_add_callback(send_bluetooth, NULL); - } - PBL_LOG(LOG_LEVEL_INFO, "Bluetooth disconnected"); - - } else if (index == 1) { - PBL_LOG(LOG_LEVEL_DEBUG, "Not implemented"); - - } -} - - -// ================================================================================= -static void prv_window_load(Window *window) { - - TestBTAppData *data = s_app_data; - - int i = 0; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "flood BT", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "Ad space available", - .callback = menu_select_callback, - }; - PBL_ASSERTN(i == NUM_MENU_ITEMS); - - // The menu sections - data->menu_section = (SimpleMenuSection) { - .num_items = NUM_MENU_ITEMS, - .items = data->menu_items, - }; - - Layer *window_layer = window_get_root_layer(data->window); - GRect bounds = window_layer->bounds; - - data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, - NULL); - layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); -} - - -// ================================================================================= -// Deinitialize resources on window unload that were initialized on window load -static void prv_window_unload(Window *window) { - simple_menu_layer_destroy(s_app_data->menu_layer); -} - - -// ================================================================================= -static void handle_init(void) { - TestBTAppData *data = app_malloc_check(sizeof(TestBTAppData)); - memset(data, 0, sizeof(TestBTAppData)); - s_app_data = data; - - data->window = window_create(); - if (data->window == NULL) { - return; - } - window_init(data->window, ""); - window_set_window_handlers(data->window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - app_window_stack_push(data->window, true /*animated*/); -} - -static void handle_deinit(void) { - // Don't bother freeing anything, the OS should be re-initing the heap. -} - - -// ================================================================================= -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -// ================================================================================= -const PebbleProcessMd* test_bluetooth_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Bluetooth Test" - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/test_bluetooth_app.h b/src/fw/apps/demo_apps/test_bluetooth_app.h deleted file mode 100644 index 4225a68fdb..0000000000 --- a/src/fw/apps/demo_apps/test_bluetooth_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* test_bluetooth_app_get_info(); diff --git a/src/fw/apps/demo_apps/test_core_dump_app.c b/src/fw/apps/demo_apps/test_core_dump_app.c deleted file mode 100644 index 5afb4af3af..0000000000 --- a/src/fw/apps/demo_apps/test_core_dump_app.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/core_dump.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "process_management/pebble_process_md.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "FreeRTOSConfig.h" - - -#define NUM_MENU_ITEMS 13 - -static bool s_call_core_dump_from_isr = false; - -// ================================================================================= -// Application Data -typedef struct { - Window *window; - SimpleMenuLayer *menu_layer; - SimpleMenuSection menu_section; - SimpleMenuItem menu_items[NUM_MENU_ITEMS]; -} TestTimersAppData; - -static TestTimersAppData *s_app_data = 0; - - -// ================================================================================= -static void stuck_timer_callback(void* data) -{ - PBL_LOG(LOG_LEVEL_DEBUG, "STT: Entering infinite loop in timer callback"); - while (true) { - psleep(100); - } -} - -static void stuck_system_task_callback(void *data) { - PBL_LOG(LOG_LEVEL_DEBUG, "Entering infinite loop in system task callback"); - while (true) { - psleep(100); - } -} - -// ================================================================================ -// We install this ISR which does an infinite loop to test that the core dump captures the right -// task information if we get it while stuck in an ISR -void OTG_FS_WKUP_IRQHandler(void) { - if (s_call_core_dump_from_isr) { - core_dump_reset(false /* don't force overwrite */); - } else { - dbgserial_putstr("Entering infinite loop in ISR"); - while (true) ; - } -} - -// ================================================================================= -// You can capture when the user selects a menu icon with a menu item select callback -static void menu_select_callback(int index, void *ctx) { - PBL_LOG(LOG_LEVEL_DEBUG, "Selected menu item %d", index); - - // Here we just change the subtitle to a literal string - s_app_data->menu_items[index].subtitle = "You've hit select here!"; - - // Mark the layer to be updated - layer_mark_dirty(simple_menu_layer_get_layer(s_app_data->menu_layer)); - - - // --------------------------------------------------------------------------- - // Run the appropriate test - if (index == 0) { - - // CROAK - PBL_CROAK("CROAK"); - - } else if (index == 1) { - - // stuck timer callback - TimerID timer = new_timer_create(); - PBL_LOG(LOG_LEVEL_INFO, "Entering infinite loop in Timer callback"); - bool success = new_timer_start(timer, 100, stuck_timer_callback, NULL, 0 /*flags*/); - PBL_ASSERTN(success); - - } else if (index == 2) { - - // call directly - core_dump_reset(false /* don't force overwrite */); - - } else if (index == 3) { - - // stuck app - PBL_LOG(LOG_LEVEL_INFO, "Entering infinite loop in App Task"); - while (true) ; - - } else if (index == 4) { - - PBL_LOG(LOG_LEVEL_INFO, "Entering infinite loop in FreeRTOS ISR"); - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn; - // Lower values are higher priority - make this same or lower priority than a FreeRTOS ISR - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4); - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); - - // Trigger it. This transfers control to our ISR handler OTG_FS_WKUP_IRQHandler - NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); - - } else if (index == 5) { - - PBL_LOG(LOG_LEVEL_INFO, "Entering infinite loop in non-FreeRTOS ISR."); - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn; - // Lower values are higher priority - make this higher priority than a FreeRTOS ISR - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4) - 1; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); - - // Trigger it. This transfers control to our ISR handler OTG_FS_WKUP_IRQHandler - NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); - - } else if (index == 6) { - - PBL_LOG(LOG_LEVEL_INFO, "Forcing bus fault during core dump"); - core_dump_test_force_bus_fault(); - core_dump_reset(false /* don't force overwrite */); - - } else if (index == 7) { - - PBL_LOG(LOG_LEVEL_INFO, "Forcing inf loop during core dump"); - core_dump_test_force_inf_loop(); - core_dump_reset(false /* don't force overwrite */); - - } else if (index == 8) { - - PBL_LOG(LOG_LEVEL_INFO, "Forcing assert loop during core dump"); - core_dump_test_force_assert(); - core_dump_reset(false /* don't force overwrite */); - - } else if (index == 9) { - - PBL_LOG(LOG_LEVEL_INFO, "Calling core_dump FreeRTOS ISR"); - s_call_core_dump_from_isr = true; - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_WKUP_IRQn; - // Lower values are higher priority - make this same or lower priority than a FreeRTOS ISR - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = (configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4); - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); - - // Trigger it. This transfers control to our ISR handler OTG_FS_WKUP_IRQHandler - NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); - - } else if (index == 10) { - - PBL_LOG(LOG_LEVEL_INFO, "Causing bus fault in app"); - typedef void (*KaboomCallback)(void); - KaboomCallback kaboom = 0; - kaboom(); - - } else if (index == 11) { - PBL_LOG(LOG_LEVEL_INFO, "Infinite Loop on system task"); - system_task_add_callback(stuck_system_task_callback, NULL); - - } else if (index == 12) { - PBL_LOG(LOG_LEVEL_INFO, "Generate hard-fault"); - - // Modify behavior of the ARM so that bus faults generate a hard fault - SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; - - // Write to protected memory - extern uint32_t __isr_stack_start__[]; - __isr_stack_start__[0] = 0x55; - } - -} - - - -// ================================================================================= -static void prv_window_load(Window *window) { - - TestTimersAppData *data = s_app_data; - - int i = 0; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "croak", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "stuck timer", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "call core_dump_reset", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "stuck app", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "stuck RTOS ISR", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "stuck non-RTOS ISR", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "BusFault in CD", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "stuck in CD", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "assert in CD", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "call from ISR", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "BusFault in app", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "stuck system task", - .callback = menu_select_callback, - }; - data->menu_items[i++] = (SimpleMenuItem) { - .title = "hard fault", - .callback = menu_select_callback, - }; - PBL_ASSERTN(i == NUM_MENU_ITEMS); - - // The menu sections - data->menu_section = (SimpleMenuSection) { - .num_items = NUM_MENU_ITEMS, - .items = data->menu_items, - }; - - Layer *window_layer = window_get_root_layer(data->window); - GRect bounds = window_layer->bounds; - - data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, - NULL); - layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); -} - - -// ================================================================================= -// Deinitialize resources on window unload that were initialized on window load -static void prv_window_unload(Window *window) { - simple_menu_layer_destroy(s_app_data->menu_layer); -} - - -// ================================================================================= -static void handle_init(void) { - TestTimersAppData *data = app_malloc_check(sizeof(TestTimersAppData)); - memset(data, 0, sizeof(TestTimersAppData)); - s_app_data = data; - - data->window = window_create(); - if (data->window == NULL) { - return; - } - window_init(data->window, ""); - window_set_window_handlers(data->window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - app_window_stack_push(data->window, true /*animated*/); -} - -static void handle_deinit(void) { - // Don't bother freeing anything, the OS should be re-initing the heap. -} - - -// ================================================================================= -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -// ================================================================================= -const PebbleProcessMd* test_core_dump_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Core Dump Test" - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/test_core_dump_app.h b/src/fw/apps/demo_apps/test_core_dump_app.h deleted file mode 100644 index f7f3a81e42..0000000000 --- a/src/fw/apps/demo_apps/test_core_dump_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* test_core_dump_app_get_info(); diff --git a/src/fw/apps/demo_apps/test_fs_resources.h b/src/fw/apps/demo_apps/test_fs_resources.h deleted file mode 100644 index d9480e3057..0000000000 --- a/src/fw/apps/demo_apps/test_fs_resources.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* fs_resources_app_get_info(); diff --git a/src/fw/apps/demo_apps/test_mpu_cache.h b/src/fw/apps/demo_apps/test_mpu_cache.h deleted file mode 100644 index 18603171bf..0000000000 --- a/src/fw/apps/demo_apps/test_mpu_cache.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -#include -#include - -const PebbleProcessMd* test_mpu_cache_get_info(); diff --git a/src/fw/apps/demo_apps/test_sys_timer_app.c b/src/fw/apps/demo_apps/test_sys_timer_app.c deleted file mode 100644 index 6d7564ec14..0000000000 --- a/src/fw/apps/demo_apps/test_sys_timer_app.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef PBL_LOGS_HASHED - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "process_management/pebble_process_md.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/regular_timer.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#define NUM_MAX_TIMERS 10 - - -// ================================================================================= -// Application Data -typedef struct { - Window *window; - SimpleMenuLayer *menu_layer; - - TimerID timer[NUM_MAX_TIMERS]; - RtcTicks fired_time[NUM_MAX_TIMERS]; - - RegularTimerInfo reg_timers[NUM_MAX_TIMERS]; - - AppTimer *app_timer; - -} TestTimersAppData; - -static TestTimersAppData *s_app_data = 0; - - -// ================================================================================= -static void timer_callback(void* data) -{ - int idx = (int)data; - PBL_ASSERTN(idx >= 0 && idx < NUM_MAX_TIMERS); - PBL_LOG(LOG_LEVEL_DEBUG, "STT normal callback %d executed", idx); - s_app_data->fired_time[idx] = rtc_get_ticks(); - return; -} - - -// ================================================================================= -static void stuck_timer_callback(void* data) -{ - PBL_LOG(LOG_LEVEL_DEBUG, "STT entering infinite loop in callback"); - while (true) { - psleep(100); - } -} - - -// ================================================================================= -static void long_timer_callback(void* data) -{ - int idx = (int)data; - PBL_ASSERTN(idx >= 0 && idx < NUM_MAX_TIMERS); - PBL_LOG(LOG_LEVEL_DEBUG, "STT long running callback %d executed", idx); - s_app_data->fired_time[idx] = rtc_get_ticks(); - - psleep(250); - return; -} - - -// ================================================================================= -// Try and reschedule a regular timer from it's callback -static void reg_timer_1_callback(void* data) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT running reg_timer_1_callback"); - if (s_app_data->reg_timers[0].cb != 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT reg_timer_1_callback rescheduling from callback for every 2 secs. "); - regular_timer_add_multisecond_callback(&s_app_data->reg_timers[0], 2); - } -} - - -// ================================================================================= -// Try and delete a regular timer from it's callback -static void reg_timer_2_callback(void* data) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT running reg_timer_2_callback"); - if (s_app_data->reg_timers[0].cb != 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT reg_timer_2_callback deleting from callback"); - regular_timer_remove_callback(&s_app_data->reg_timers[0]); - } -} - - -// ================================================================================= -// Try and delete, then re-add a regular timer from its callback -static int s_reg_timer_3_callback_count = 0; -static void reg_timer_3_callback(void* data) { - s_reg_timer_3_callback_count++; - PBL_LOG(LOG_LEVEL_DEBUG, "STT running reg_timer_3_callback"); - if (s_app_data->reg_timers[0].cb != 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT reg_timer_3_callback deleting then adding from callback"); - regular_timer_remove_callback(&s_app_data->reg_timers[0]); - regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); - } -} - - -// ================================================================================= -static void menu_callback_prefix(int index, void *ctx) { - PBL_LOG(LOG_LEVEL_DEBUG, "Hit menu item %d", index); - - // Mark the layer to be updated - layer_mark_dirty(simple_menu_layer_get_layer(s_app_data->menu_layer)); - - // Cancel and delete old timers if present - for (int i=0; ifired_time[i] = 0; - if (s_app_data->timer[i] != TIMER_INVALID_ID) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT stopping and deleting previous timer %d", i); - new_timer_stop(s_app_data->timer[i]); - new_timer_delete(s_app_data->timer[i]); - s_app_data->timer[i] = TIMER_INVALID_ID; - } - } - - if (s_app_data->app_timer != NULL) { - app_timer_cancel(s_app_data->app_timer); - s_app_data->app_timer = 0; - } - - // Cancel and delete old regular timers if present - for (int i=0; ireg_timers[i].cb != NULL) { - PBL_LOG(LOG_LEVEL_DEBUG, "STT deleting previous regular timer %d", i); - regular_timer_remove_callback(&s_app_data->reg_timers[i]); - s_app_data->reg_timers[i].cb = NULL; - } - } - -} - - -// ================================================================================= -void single_shot_timer_menu_cb(int index, void *ctx) { - uint32_t zero_flags = 0; - uint32_t expire_ms; - int timer_idx_0 = 0; - bool scheduled = false; - - menu_callback_prefix(index, ctx); - - // Single shot timer - s_app_data->timer[0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[timer_idx_0], 100, timer_callback, (void*)timer_idx_0, zero_flags); - PBL_ASSERTN(success); - - // Make sure it's marked as scheduled - scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); - PBL_ASSERTN(scheduled && expire_ms <= 100); - PBL_LOG(LOG_LEVEL_DEBUG, "STT firing in %d ms", (int)expire_ms); - - // Wait for it to fire - psleep(300); - PBL_ASSERTN(s_app_data->fired_time[timer_idx_0] != 0); - scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); - PBL_ASSERTN(!scheduled); -} - - -// ================================================================================= -void repeating_timer_menu_cb(int index, void *ctx) { - uint32_t expire_ms; - int timer_idx_0 = 0; - bool scheduled = false; - - menu_callback_prefix(index, ctx); - - // Repeating timer - s_app_data->timer[timer_idx_0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[timer_idx_0], 500, timer_callback, (void*)timer_idx_0, - TIMER_START_FLAG_REPEATING); - PBL_ASSERTN(success); - - scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); - PBL_ASSERTN(scheduled && expire_ms <= 500); - PBL_LOG(LOG_LEVEL_DEBUG, "STT firing in %d ms", (int)expire_ms); -} - -// ================================================================================= -void two_timers_menu_cb(int index, void *ctx) { - uint32_t zero_flags = 0; - uint32_t expire_ms; - - menu_callback_prefix(index, ctx); - - // Multiple timers - int timer_idx_0 = 0; - s_app_data->timer[timer_idx_0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[timer_idx_0], 300, timer_callback, (void*)timer_idx_0, zero_flags); - PBL_ASSERTN(success); - - int timer_idx_1 = 1; - s_app_data->timer[timer_idx_1] = new_timer_create(); - success = new_timer_start(s_app_data->timer[timer_idx_1], 100, timer_callback, (void*)timer_idx_1, zero_flags); - PBL_ASSERTN(success); - - - // Wait for them to fire - psleep(500); - PBL_ASSERTN(s_app_data->fired_time[timer_idx_0] != 0); - PBL_ASSERTN(s_app_data->fired_time[timer_idx_1] != 0); - PBL_ASSERTN(s_app_data->fired_time[timer_idx_1] < s_app_data->fired_time[timer_idx_0]); - bool scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_0], &expire_ms); - PBL_ASSERTN(!scheduled); - scheduled = new_timer_scheduled(s_app_data->timer[timer_idx_1], &expire_ms); - PBL_ASSERTN(!scheduled); -} - -// ================================================================================= -void deferred_delete_menu_cb(int index, void *ctx) { - void *cb_data = 0; - uint32_t zero_flags = 0; - - menu_callback_prefix(index, ctx); - - // Deferred delete - s_app_data->timer[0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[0], 1, long_timer_callback, cb_data, zero_flags); - PBL_ASSERTN(success); - psleep(50); - - // Stop and then delete it - success = new_timer_stop(s_app_data->timer[0]); - PBL_ASSERTN(!success); /* stop returns false if callback is running */ - new_timer_delete(s_app_data->timer[0]); - s_app_data->timer[0] = TIMER_INVALID_ID; -} - -// ================================================================================= -void fail_if_executing_menu_cb(int index, void *ctx) { - void *cb_data = 0; - uint32_t zero_flags = 0; - - menu_callback_prefix(index, ctx); - - // fail if executing - s_app_data->timer[0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[0], 1, long_timer_callback, cb_data, zero_flags); - PBL_ASSERTN(success); - psleep(50); - - // try and reschedule while it's executing - success = new_timer_start(s_app_data->timer[0], 1, long_timer_callback, cb_data, - TIMER_START_FLAG_FAIL_IF_EXECUTING); - PBL_ASSERTN(!success); -} - -// ================================================================================= -void fail_if_scheduled_menu_cb(int index, void *ctx) { - void *cb_data = 0; - uint32_t zero_flags = 0; - - menu_callback_prefix(index, ctx); - - // fail if scheduled - s_app_data->timer[0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[0], 100, timer_callback, cb_data, - zero_flags); - PBL_ASSERTN(success); - - // try and reschedule while it's already scheduled - success = new_timer_start(s_app_data->timer[0], 1, timer_callback, cb_data, - TIMER_START_FLAG_FAIL_IF_SCHEDULED); - PBL_ASSERTN(!success); - -} - -// ================================================================================= -void evented_timer_menu_cb(int index, void *ctx) { - void *cb_data = 0; - - menu_callback_prefix(index, ctx); - - // Test evented_timer - s_app_data->app_timer = app_timer_register(100 /*ms*/, timer_callback, cb_data); - PBL_ASSERTN(s_app_data->app_timer); -} - -// ================================================================================= -void stuck_callback_menu_cb(int index, void *ctx) { - void *cb_data = 0; - uint32_t zero_flags = 0; - - menu_callback_prefix(index, ctx); - - // stuck callback - s_app_data->timer[0] = new_timer_create(); - bool success = new_timer_start(s_app_data->timer[0], 100, stuck_timer_callback, cb_data, zero_flags); - PBL_ASSERTN(success); -} - -// ================================================================================= -void invaid_timer_id_menu_cb(int index, void *ctx) { - void *cb_data = 0; - uint32_t zero_flags = 0; - - menu_callback_prefix(index, ctx); - - // invalid timer id - new_timer_start(0x12345678, 100, timer_callback, cb_data, zero_flags); -} - - -// ================================================================================= -void reg_timer_schedule_1sec_from_cb_menu_cb(int index, void *ctx) { - menu_callback_prefix(index, ctx); - - s_app_data->reg_timers[0].cb = reg_timer_1_callback; - regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); -} - -// ================================================================================= -void reg_timer_schedule_1min_from_cb_menu_cb(int index, void *ctx) { - menu_callback_prefix(index, ctx); - - s_app_data->reg_timers[0].cb = reg_timer_1_callback; - regular_timer_add_minutes_callback(&s_app_data->reg_timers[0]); - - // This should assert when the callback runs because it tries to reschedule as a seconds callback -} - -// ================================================================================= -void reg_timer_delete_from_cb_menu_cb(int index, void *ctx) { - menu_callback_prefix(index, ctx); - - s_app_data->reg_timers[0].cb = reg_timer_2_callback; - regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); -} - -// ================================================================================= -void reg_timer_delete_then_add_from_cb_menu_cb(int index, void *ctx) { - menu_callback_prefix(index, ctx); - - s_reg_timer_3_callback_count = 0; - s_app_data->reg_timers[0].cb = reg_timer_3_callback; - regular_timer_add_seconds_callback(&s_app_data->reg_timers[0]); - - // Wait for timer to run at least twice - PBL_LOG(LOG_LEVEL_DEBUG, "waiting for callback to run 2 times"); - psleep(2200); - - PBL_ASSERT(s_reg_timer_3_callback_count >= 2, "Callback didn't run at least twice"); -} - -// ================================================================================= -void croak_menu_cb(int index, void *ctx) { - - menu_callback_prefix(index, ctx); - PBL_CROAK("DIE!"); -} - -// ================================================================================= -static void prv_window_load(Window *window) { - TestTimersAppData *data = s_app_data; - - static const SimpleMenuItem menu_items[] = { - { - .title = "single-shot timer", - .callback = single_shot_timer_menu_cb - }, { - .title = "repeating timer", - .callback = repeating_timer_menu_cb - }, { - .title = "two timers", - .callback = two_timers_menu_cb - }, { - .title = "deferred delete", - .callback = deferred_delete_menu_cb - }, { - .title = "fail if executing", - .callback = fail_if_executing_menu_cb - }, { - .title = "fail if scheduled", - .callback = fail_if_scheduled_menu_cb - }, { - .title = "evented_timer", - .callback = evented_timer_menu_cb - }, { - .title = "stuck callback", - .callback = stuck_callback_menu_cb - }, { - .title = "invalid timer ID", - .callback = invaid_timer_id_menu_cb - }, { - .title = "RT: sch 1 sec from cb", - .callback = reg_timer_schedule_1sec_from_cb_menu_cb - }, { - .title = "RT: sch 1 min from cb", - .callback = reg_timer_schedule_1min_from_cb_menu_cb - }, { - .title = "RT: delete from cb", - .callback = reg_timer_delete_from_cb_menu_cb - }, { - .title = "RT: delete+add from cb", - .callback = reg_timer_delete_then_add_from_cb_menu_cb - }, { - .title = "croak", - .callback = croak_menu_cb - } - }; - static const SimpleMenuSection sections[] = { - { - .items = menu_items, - .num_items = ARRAY_LENGTH(menu_items) - } - }; - - Layer *window_layer = window_get_root_layer(data->window); - GRect bounds = window_layer->bounds; - - data->menu_layer = simple_menu_layer_create(bounds, data->window, sections, ARRAY_LENGTH(sections), NULL); - layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); -} - - -// ================================================================================= -// Deinitialize resources on window unload that were initialized on window load -static void prv_window_unload(Window *window) { - simple_menu_layer_destroy(s_app_data->menu_layer); -} - - -// ================================================================================= -static void handle_init(void) { - TestTimersAppData *data = app_malloc_check(sizeof(TestTimersAppData)); - memset(data, 0, sizeof(TestTimersAppData)); - s_app_data = data; - - data->window = window_create(); - if (data->window == NULL) { - return; - } - window_init(data->window, ""); - window_set_window_handlers(data->window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - app_window_stack_push(data->window, true /*animated*/); -} - -static void handle_deinit(void) { - // Don't bother freeing anything, the OS should be re-initing the heap. -} - - -// ================================================================================= -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -// ================================================================================= -const PebbleProcessMd* test_sys_timer_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "System Timer Test" - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/demo_apps/test_sys_timer_app.h b/src/fw/apps/demo_apps/test_sys_timer_app.h deleted file mode 100644 index ec0bf2746d..0000000000 --- a/src/fw/apps/demo_apps/test_sys_timer_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* test_sys_timer_app_get_info(); diff --git a/src/fw/apps/demo_apps/text_clipping.c b/src/fw/apps/demo_apps/text_clipping.c deleted file mode 100644 index bee69c12d5..0000000000 --- a/src/fw/apps/demo_apps/text_clipping.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "text_clipping.h" - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" - -typedef enum { - SelectIndexPixels, - SelectIndexDirection, - SelectIndexOverflow, - // Add new selection criteria above - SelectIndexMax -} SelectIndex; - -typedef struct AppState { - Window window; - Layer canvas; - GSize canvas_size; - TextLayer text_layer; - TextLayer direction_layer; - TextLayer word_wrap_layer; - SelectIndex select_index; - bool up_down_direction; // True = move up or down; False = move left or right - bool word_wrap; // True = word wrap; False = don't word wrap -} AppState; - -static const char* text_buffer = "Text Clipping"; - -static void init_text_layer(AppState *data, GRect frame) { - text_layer_init(&data->text_layer, &GRect(frame.origin.x, frame.origin.y, - frame.size.w, frame.size.h)); - text_layer_set_background_color(&data->text_layer, GColorWhite); - text_layer_set_text_color(&data->text_layer, GColorBlack); - text_layer_set_text(&data->text_layer, text_buffer); - GFont gothic_24_bold = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - text_layer_set_font(&data->text_layer, gothic_24_bold); - text_layer_set_text_alignment(&data->text_layer, GTextAlignmentCenter); - text_layer_set_overflow_mode(&data->text_layer, GTextOverflowModeTrailingEllipsis); -} - -static void click_handler(ClickRecognizerRef recognizer, Window *window) { - AppState *data = window_get_user_data(window); - ButtonId button = click_recognizer_get_button_id(recognizer); - GRect frame = data->text_layer.layer.frame; - - if (button == BUTTON_ID_UP) { - if (data->select_index == SelectIndexPixels) { - // Do inverse - if (data->up_down_direction) { - frame.origin.y--; - } else { - frame.origin.x--; - } - } else if (data->select_index == SelectIndexDirection) { - data->up_down_direction = !(data->up_down_direction); - } else if (data->select_index == SelectIndexOverflow) { - data->word_wrap = !(data->word_wrap); - } - } else if (button == BUTTON_ID_SELECT) { - data->select_index = (data->select_index + 1) % SelectIndexMax; - } else if (button == BUTTON_ID_DOWN) { - if (data->select_index == SelectIndexPixels) { - // Do inverse - if (data->up_down_direction) { - frame.origin.y++; - } else { - frame.origin.x++; - } - } else if (data->select_index == SelectIndexDirection) { - data->up_down_direction = !(data->up_down_direction); - } else if (data->select_index == SelectIndexOverflow) { - data->word_wrap = !(data->word_wrap); - } - } - - text_layer_set_text_color(&data->direction_layer, GColorBlack); - text_layer_set_background_color(&data->direction_layer, GColorWhite); - text_layer_set_text_color(&data->word_wrap_layer, GColorBlack); - text_layer_set_background_color(&data->word_wrap_layer, GColorWhite); - - if (data->select_index == SelectIndexDirection) { - text_layer_set_text_color(&data->direction_layer, GColorWhite); - text_layer_set_background_color(&data->direction_layer, GColorBlack); - } else if (data->select_index == SelectIndexOverflow) { - text_layer_set_text_color(&data->word_wrap_layer, GColorWhite); - text_layer_set_background_color(&data->word_wrap_layer, GColorBlack); - } - - if (data->up_down_direction) { - text_layer_set_text(&data->direction_layer, "Direction: Up/Down"); - } else { - text_layer_set_text(&data->direction_layer, "Direction: Left/Right"); - } - - if (data->word_wrap) { - frame.size.w = 72; - frame.size.h = 60; - } else { - frame.size.w = 72; - frame.size.h = 32; - } - - init_text_layer(data, frame); - - if (data->word_wrap) { - text_layer_set_text(&data->word_wrap_layer, "Overflow: Word Wrap"); - text_layer_set_overflow_mode(&data->text_layer, GTextOverflowModeWordWrap); - } else { - text_layer_set_text(&data->word_wrap_layer, "Overflow: Ellipsis"); - text_layer_set_overflow_mode(&data->text_layer, GTextOverflowModeTrailingEllipsis); - } -} - -static void config_provider(Window *window) { - window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, (ClickHandler)click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_SELECT, 100, (ClickHandler)click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, (ClickHandler)click_handler); - (void)window; -} - -static void update_window(Layer *layer, GContext *ctx) { - // Clear parent layer first - graphics_context_set_fill_color(ctx, GColorWhite); - graphics_fill_rect(ctx, &GRect(39, 39, 82, 42)); - - // Draw box just outside the clipping area - graphics_draw_rect(ctx, &GRect(39, 39, 82, 42)); -} - -static void prv_window_load(Window *window) { - AppState *data = window_get_user_data(window); - - data->select_index = SelectIndexPixels; - data->up_down_direction = true; - data->word_wrap = false; - - // Init canvas (i.e. clipping box) - data->canvas_size = GSize(80, 40); - - layer_init(&data->canvas, &GRect(40, 40, data->canvas_size.w, data->canvas_size.h)); - layer_add_child(&data->window.layer, &data->canvas); - - // Init text layer - init_text_layer(data, GRect(4, 4, 72, 32)); - - // Init direction layer - text_layer_init(&data->direction_layer, &GRect(5, 100, 135, 20)); - text_layer_set_background_color(&data->direction_layer, GColorWhite); - text_layer_set_text_color(&data->direction_layer, GColorBlack); - text_layer_set_text(&data->direction_layer, "Direction: Up/Down"); - GFont gothic_14_bold = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); - text_layer_set_font(&data->direction_layer, gothic_14_bold); - text_layer_set_text_alignment(&data->direction_layer, GTextAlignmentCenter); - text_layer_set_overflow_mode(&data->direction_layer, GTextOverflowModeTrailingEllipsis); - - // Init word wrap layer - text_layer_init(&data->word_wrap_layer, &GRect(5, 130, 135, 20)); - text_layer_set_background_color(&data->word_wrap_layer, GColorWhite); - text_layer_set_text_color(&data->word_wrap_layer, GColorBlack); - text_layer_set_text(&data->word_wrap_layer, "Overflow: Ellipsis"); - text_layer_set_font(&data->word_wrap_layer, gothic_14_bold); - text_layer_set_text_alignment(&data->word_wrap_layer, GTextAlignmentCenter); - text_layer_set_overflow_mode(&data->word_wrap_layer, GTextOverflowModeTrailingEllipsis); - - // Setup children for clipping canvas - layer_add_child(&data->canvas, &data->text_layer.layer); - - // Setup children for main window canvas - layer_add_child(&window->layer, &data->direction_layer.layer); - layer_add_child(&window->layer, &data->word_wrap_layer.layer); - - // Setup update proc to draw clipping box - layer_set_update_proc(&window->layer, update_window); -} - -static void push_window(struct AppState *data) { - Window* window = &data->window; - window_init(window, WINDOW_NAME("Text Clipping")); - window_set_user_data(window, data); - window_set_click_config_provider(window, (ClickConfigProvider) config_provider); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - }); - const bool animated = true; - app_window_stack_push(window, animated); -} - - -//////////////////// -// App boilerplate - -static void handle_init(void) { - struct AppState* data = app_malloc_check(sizeof(struct AppState)); - - app_state_set_user_data(data); - push_window(data); -} - -static void handle_deinit(void) { - struct AppState* data = app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* text_clipping_app_get_info() { - static const PebbleProcessMdSystem text_spacing_info = { - .common.main_func = &s_main, - .name = "Text Clipping" - }; - return (const PebbleProcessMd*) &text_spacing_info; -} diff --git a/src/fw/apps/demo_apps/text_clipping.h b/src/fw/apps/demo_apps/text_clipping.h deleted file mode 100644 index 7b7f00e6eb..0000000000 --- a/src/fw/apps/demo_apps/text_clipping.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* text_clipping_app_get_info(); diff --git a/src/fw/apps/demo_apps/text_flow.h b/src/fw/apps/demo_apps/text_flow.h deleted file mode 100644 index 9f7dfd373e..0000000000 --- a/src/fw/apps/demo_apps/text_flow.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* text_flow_app_get_info(); diff --git a/src/fw/apps/demo_apps/text_layout.h b/src/fw/apps/demo_apps/text_layout.h deleted file mode 100644 index 909236962d..0000000000 --- a/src/fw/apps/demo_apps/text_layout.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* text_layout_get_info(); diff --git a/src/fw/apps/demo_apps/text_spacing.h b/src/fw/apps/demo_apps/text_spacing.h deleted file mode 100644 index f7f5bbc58f..0000000000 --- a/src/fw/apps/demo_apps/text_spacing.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* text_spacing_app_get_info(); diff --git a/src/fw/apps/demo_apps/timeline_pins_demo.h b/src/fw/apps/demo_apps/timeline_pins_demo.h deleted file mode 100644 index ffb681a39b..0000000000 --- a/src/fw/apps/demo_apps/timeline_pins_demo.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -typedef enum { - TimelinePinsDemo_Default = 0, - TimelinePinsDemo_Notifications, - TimelinePinsDemo_OneDayAway, - TimelinePinsDemo_OngoingEvent, - TimelinePinsDemo_TodayAndTomorrow, - TimelinePinsDemoCount, -} TimelinePinsDemoSet; - -void timeline_pins_demo_add_pins(TimelinePinsDemoSet pin_set); - -const PebbleProcessMd *timeline_pins_get_app_info(void); diff --git a/src/fw/apps/demo_apps/timer_app.c b/src/fw/apps/demo_apps/timer_app.c deleted file mode 100644 index aabf3b9e34..0000000000 --- a/src/fw/apps/demo_apps/timer_app.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "process_management/pebble_process_md.h" -#include "applib/app.h" -#include "applib/app_logging.h" -#include "process_state/app_state/app_state.h" -#include "applib/ui/ui.h" -#include "process_management/pebble_process_md.h" -#include "system/logging.h" -#include "system/passert.h" - -static AppTimer *s_timer = NULL; - -static void shouldnt_happen(void *context) { - WTF; -} - -static void stupid_cancel(void *context) { - app_timer_cancel(s_timer); - - APP_LOG(LOG_LEVEL_INFO, "success"); -} - -static void prv_window_load(Window *window) { - int dummy_data = 0; - // Wait much longer than it should take to cancel the timer. - AppTimer *timer = app_timer_register(1000 /*ms*/, shouldnt_happen, &dummy_data); - PBL_ASSERTN(timer != NULL); - // Try to cancel it twice. This used to crash, but should not crash anymore. - // In particular, we're looking to see that at least if we don't do more app_heap - // allocations, we will be able to detect that we're effectively trying to - // double-release this timer. - app_timer_cancel(timer); - app_timer_cancel(timer); - - timer = app_timer_register(1 /*ms*/, stupid_cancel, &s_timer); - s_timer = timer; - PBL_ASSERTN(timer != NULL); -} - -static const WindowHandlers s_main_menu_handlers = { - .load = prv_window_load, -}; - -static void handle_init(void) { - Window *window = window_create(); - if (window == NULL) { - return; - } - window_init(window, ""); - window_set_window_handlers(window, &s_main_menu_handlers); - app_window_stack_push(window, true /*animated*/); -} - -static void handle_deinit(void) { - // Don't bother freeing anything, the OS should be re-initing the heap. -} - -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -const PebbleProcessMd* timer_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - .name = "Timer Cancel Test" - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/demo_apps/timer_app.h b/src/fw/apps/demo_apps/timer_app.h deleted file mode 100644 index 8891bd3d49..0000000000 --- a/src/fw/apps/demo_apps/timer_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* timer_app_get_info(); diff --git a/src/fw/apps/demo_apps/trigger_alarm.c b/src/fw/apps/demo_apps/trigger_alarm.c deleted file mode 100644 index db2267c641..0000000000 --- a/src/fw/apps/demo_apps/trigger_alarm.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "trigger_alarm.h" - -#include "applib/app.h" -#include "process_state/app_state/app_state.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window_stack.h" -#include "kernel/pbl_malloc.h" -#include "kernel/events.h" -#include "drivers/rtc.h" - -typedef struct { - Window window; -} TriggerAlarmData; - -static void handle_init(void) { - TriggerAlarmData *data = app_malloc_check(sizeof(TriggerAlarmData)); - - app_state_set_user_data(data); - window_init(&data->window, WINDOW_NAME("Trigger Alarm Demo")); - const bool animated = true; - app_window_stack_push(&data->window, animated); - - PebbleEvent e = (PebbleEvent) { - .type = PEBBLE_ALARM_CLOCK_EVENT, - .alarm_clock = { - .alarm_time = rtc_get_time(), - .alarm_label = "Wake Up" - } - }; - - event_put(&e); -} - -static void handle_deinit(void) { - TriggerAlarmData *data = (TriggerAlarmData*)app_state_get_user_data(); - app_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* trigger_alarm_get_app_info() { - static const PebbleProcessMdSystem s_trigger_alarm = { - .common.main_func = s_main, - .name = "Trigger Alarm" - }; - - return (const PebbleProcessMd*) &s_trigger_alarm; -} - diff --git a/src/fw/apps/demo_apps/trigger_alarm.h b/src/fw/apps/demo_apps/trigger_alarm.h deleted file mode 100644 index 428faf4d45..0000000000 --- a/src/fw/apps/demo_apps/trigger_alarm.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* trigger_alarm_get_app_info(); diff --git a/src/fw/apps/demo_apps/vibe_and_logs.c b/src/fw/apps/demo_apps/vibe_and_logs.c deleted file mode 100644 index 504c2ae3a0..0000000000 --- a/src/fw/apps/demo_apps/vibe_and_logs.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "trigger_alarm.h" -#include "applib/ui/vibes.h" - -#include "applib/app.h" -#include "process_state/app_state/app_state.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window_stack.h" -#include "kernel/events.h" -#include "drivers/rtc.h" -#include "drivers/vibe.h" -#include "system/logging.h" - -Window s_window; -static AppTimer *s_app_timer; -TimerID s_sys_timer = TIMER_INVALID_ID; - -static void app_timer_callback(void *data) { - for (int i=0; i<40; i++) { - PBL_LOG(LOG_LEVEL_INFO, "%d Running app timer callback", i); - vibes_short_pulse(); - } - s_app_timer = app_timer_register(100 /* milliseconds */, app_timer_callback, NULL); -} - -#if 0 -static void sys_timer_callback(void* data) { - for (int i=0; i<1; i++) { - PBL_LOG(LOG_LEVEL_INFO, "%d Running sys timer callback", i); - vibes_short_pulse(); - } - new_timer_start(s_sys_timer, 20, sys_timer_callback, NULL, 0); -} -#endif - -static void handle_init(void) { - window_init(&s_window, WINDOW_NAME("VibeAndLogs Demo")); - const bool animated = true; - app_window_stack_push(&s_window, animated); - - s_app_timer = app_timer_register(100 /* milliseconds */, app_timer_callback, NULL); - - s_sys_timer = new_timer_create(); - //new_timer_start(s_sys_timer, 10, sys_timer_callback, NULL, 0); -} - -static void handle_deinit(void) { -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* vibe_and_logs_get_app_info() { - static const PebbleProcessMdSystem s_trigger_alarm = { - .common.main_func = s_main, - .name = "VibeAndLogs" - }; - - return (const PebbleProcessMd*) &s_trigger_alarm; -} - diff --git a/src/fw/apps/demo_apps/vibe_and_logs.h b/src/fw/apps/demo_apps/vibe_and_logs.h deleted file mode 100644 index 015ceda3d2..0000000000 --- a/src/fw/apps/demo_apps/vibe_and_logs.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* vibe_and_logs_get_app_info(); diff --git a/src/fw/apps/demo_apps/vibe_score_demo.h b/src/fw/apps/demo_apps/vibe_score_demo.h deleted file mode 100644 index 75adc1f43a..0000000000 --- a/src/fw/apps/demo_apps/vibe_score_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *vibe_score_demo_get_info(void); diff --git a/src/fw/apps/demo_apps/vibe_strength_demo.c b/src/fw/apps/demo_apps/vibe_strength_demo.c deleted file mode 100644 index 6894d1479c..0000000000 --- a/src/fw/apps/demo_apps/vibe_strength_demo.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/number_window.h" -#include "drivers/vibe.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" - -#include - -static void selected_pwm_percentage(NumberWindow *nw, void *ctx) { - static bool on = true; - int8_t val = number_window_get_value(nw); - vibe_set_strength(val); - vibe_ctl(on); - on = !on; -} - -static void handle_init(void) { - NumberWindow *vibe_num_window = number_window_create("Vibe Strength", - (NumberWindowCallbacks) { .selected = selected_pwm_percentage }, - NULL); - app_state_set_user_data(vibe_num_window); - - uint8_t scale_granularity = 5; // 5 percent at a time - uint8_t curr_percent = VIBE_STRENGTH_MAX; - - number_window_set_value(vibe_num_window, curr_percent); - number_window_set_max(vibe_num_window, VIBE_STRENGTH_MAX); - number_window_set_min(vibe_num_window, VIBE_STRENGTH_MIN); - number_window_set_step_size(vibe_num_window, scale_granularity); - - app_window_stack_push(number_window_get_window(vibe_num_window), true); -} - -static void handle_deinit(void) { - NumberWindow *data = app_state_get_user_data(); - number_window_destroy(data); -} - -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -const PebbleProcessMd* vibe_strength_demo_get_info() { - static const PebbleProcessMdSystem s_vibe_strength_info = { - .common.main_func = s_main, - .name = "Vibe Strength" - }; - return (const PebbleProcessMd*) &s_vibe_strength_info; -} diff --git a/src/fw/apps/demo_apps/vibe_strength_demo.h b/src/fw/apps/demo_apps/vibe_strength_demo.h deleted file mode 100644 index 2d5cfeb6a1..0000000000 --- a/src/fw/apps/demo_apps/vibe_strength_demo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *vibe_strength_demo_get_info(void); diff --git a/src/fw/apps/prf/low_power.c b/src/fw/apps/prf/low_power.c new file mode 100644 index 0000000000..0256b8d59d --- /dev/null +++ b/src/fw/apps/prf/low_power.c @@ -0,0 +1,136 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/ui/ui.h" +#include "applib/app.h" +#include "applib/app_timer.h" +#include "applib/graphics/graphics.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/kino/kino_reel.h" +#include "applib/ui/layer.h" +#include "applib/ui/window_private.h" +#include "applib/ui/window_stack.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" + + +#define LOW_POWER_APP_STATE_UPDATE_TIME_MS 2000 + +typedef struct { + Window window; + KinoLayer kino_layer; + GRect charging_kino_area; + GRect discharging_kino_area; + BatteryChargeState saved_state; + AppTimer *timer; +} LowPowerAppData; + +//////////////////////////////////////////////////////////// +// Update Logic + +static void prv_refresh_state(void *data_in) { + LowPowerAppData *data = (LowPowerAppData*) data_in; + BatteryChargeState current_state = battery_get_charge_state(); + uint32_t res_id; + GRect kino_area; + + if (current_state.is_charging && !data->saved_state.is_charging) { + kino_area = data->charging_kino_area; + res_id = RESOURCE_ID_RECOVERY_LOW_POWER_CHARGING; + } else if (!current_state.is_charging && data->saved_state.is_charging) { + kino_area = data->discharging_kino_area; + res_id = RESOURCE_ID_RECOVERY_LOW_POWER_DISCHARGING; + } else { + goto reschedule; + } + + layer_set_frame((Layer *) &data->kino_layer, &kino_area); + kino_layer_set_reel_with_resource(&data->kino_layer, res_id); + layer_mark_dirty(&data->kino_layer.layer); + + reschedule: + data->saved_state = current_state; + data->timer = app_timer_register(LOW_POWER_APP_STATE_UPDATE_TIME_MS, prv_refresh_state, data); +} + +//////////////////////////////////////////////////////////// +// Window loading, unloading, initializing + +static void prv_window_unload_handler(Window* window) { + LowPowerAppData *data = window_get_user_data(window); + if (!data) { + // Sanity check + return; + } + + kino_layer_deinit(&data->kino_layer); + app_timer_cancel(data->timer); + app_free(data); +} + +static void prv_window_load_handler(Window* window) { + LowPowerAppData *data = window_get_user_data(window); + + data->discharging_kino_area = GRect(PBL_IF_RECT_ELSE(4, 5), + PBL_IF_RECT_ELSE(2, 4), + data->window.layer.bounds.size.w, + data->window.layer.bounds.size.h); + data->charging_kino_area = GRect(0, 0, data->window.layer.bounds.size.w, + data->window.layer.bounds.size.h); + + kino_layer_init(&data->kino_layer, &data->discharging_kino_area); + kino_layer_set_reel_with_resource(&data->kino_layer, RESOURCE_ID_RECOVERY_LOW_POWER_DISCHARGING); + layer_add_child(&window->layer, &data->kino_layer.layer); + + data->timer = app_timer_register(LOW_POWER_APP_STATE_UPDATE_TIME_MS, prv_refresh_state, data); +} + +static void prv_prf_low_power_app_window_push(void) { + LowPowerAppData *data = app_malloc_check(sizeof(LowPowerAppData)); + + *data = (LowPowerAppData){}; + + Window* window = &data->window; + window_init(window, WINDOW_NAME("Low Power App")); + window_set_user_data(window, data); + window_set_overrides_back_button(window, true); + window_set_fullscreen(window, true); + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + window_set_window_handlers(window, &(WindowHandlers){ + .load = prv_window_load_handler, + .unload = prv_window_unload_handler, + }); + app_window_stack_push(window, false); +} + +static void s_main(void) { + launcher_block_popups(true); + + prv_prf_low_power_app_window_push(); + + app_event_loop(); + + launcher_block_popups(false); +} + +//////////////////////////////////////////////////////////// +// Public functions + +const PebbleProcessMd* prf_low_power_app_get_info() { + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &s_main, + .visibility = ProcessVisibilityHidden, + // UUID: f29f18ac-bbec-452b-9262-49b5f6e5c920 + .uuid = {0xf2, 0x9f, 0x18, 0xac, 0xbb, 0xec, 0x45, 0x2b, + 0x92, 0x62, 0x49, 0xb5, 0xf6, 0xe5, 0xc9, 0x20}, + }, + .name = "Low Power App", + .run_level = ProcessAppRunLevelSystem, + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/low_power.h b/src/fw/apps/prf/low_power.h new file mode 100644 index 0000000000..ba4d869ea5 --- /dev/null +++ b/src/fw/apps/prf/low_power.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* prf_low_power_app_get_info(); diff --git a/src/fw/apps/prf/mfg_accel.c b/src/fw/apps/prf/mfg_accel.c new file mode 100644 index 0000000000..6f7a9edf75 --- /dev/null +++ b/src/fw/apps/prf/mfg_accel.c @@ -0,0 +1,228 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "applib/ui/text_layer.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/pbl_malloc.h" +#include "drivers/accel.h" +#include "drivers/rtc.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "pbl/services/evented_timer.h" +#include "system/logging.h" + +#include +#include +#include + +#define STATUS_STRING_LEN 200 + +// Minimum variation required on each axis (in mG) +#define MIN_VARIATION_MG 500 + +#define TEST_DURATION_MS 5000 +#define RESULT_DISPLAY_MS 1000 +#define SAMPLE_INTERVAL_MS 100 + +typedef enum { + STATE_IDLE, + STATE_TESTING, + STATE_RESULT, +} TestState; + +static EventedTimerID s_timer; + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; + char status_string[STATUS_STRING_LEN]; + + TestState state; + RtcTicks state_start_time; + + // Track min/max per axis + int16_t min_x, max_x; + int16_t min_y, max_y; + int16_t min_z, max_z; + + uint32_t sample_count; + + // Variation per axis + int16_t variation_x; + int16_t variation_y; + int16_t variation_z; + + bool test_passed; +} AppData; + +static void prv_update_display(void *context) { + AppData *data = context; + + AccelDriverSample sample; + int ret = accel_peek(&sample); + + if (ret != 0) { + sniprintf(data->status_string, sizeof(data->status_string), + "ACCEL ERROR:\n%d", ret); + text_layer_set_text(&data->status, data->status_string); + return; + } + + PBL_LOG_DBG("Accel (mG): X:%" PRIi16 " Y:%" PRIi16 " Z:%" PRIi16, + sample.x, sample.y, sample.z); + + uint32_t elapsed = (uint32_t)(rtc_get_ticks() - data->state_start_time); + + switch (data->state) { + case STATE_IDLE: { + sniprintf(data->status_string, sizeof(data->status_string), + "X: %" PRIi16 " mG\nY: %" PRIi16 " mG\nZ: %" PRIi16 " mG\n\nPress SEL", + sample.x, sample.y, sample.z); + break; + } + + case STATE_TESTING: { + // Track min/max for each axis + if (data->sample_count == 0) { + data->min_x = data->max_x = sample.x; + data->min_y = data->max_y = sample.y; + data->min_z = data->max_z = sample.z; + } else { + if (sample.x < data->min_x) data->min_x = sample.x; + if (sample.x > data->max_x) data->max_x = sample.x; + if (sample.y < data->min_y) data->min_y = sample.y; + if (sample.y > data->max_y) data->max_y = sample.y; + if (sample.z < data->min_z) data->min_z = sample.z; + if (sample.z > data->max_z) data->max_z = sample.z; + } + data->sample_count++; + + // Calculate current variations + int16_t curr_var_x = data->max_x - data->min_x; + int16_t curr_var_y = data->max_y - data->min_y; + int16_t curr_var_z = data->max_z - data->min_z; + + sniprintf(data->status_string, sizeof(data->status_string), + "Testing...\nRotate device\n\nX: %" PRId16 " mG\nY: %" PRId16 + " mG\nZ: %" PRId16 " mG\n\n%" PRIu32 " sec remaining", + curr_var_x, curr_var_y, curr_var_z, + (TEST_DURATION_MS - elapsed) / 1000 + 1); + + if (elapsed >= TEST_DURATION_MS) { + // Store final variations + data->variation_x = curr_var_x; + data->variation_y = curr_var_y; + data->variation_z = curr_var_z; + + // Test passes if all axes vary by at least the minimum threshold + data->test_passed = + (data->variation_x >= MIN_VARIATION_MG && + data->variation_y >= MIN_VARIATION_MG && + data->variation_z >= MIN_VARIATION_MG); + + mfg_test_result_report(MfgTestId_Accel, data->test_passed, 0); + + data->state = STATE_RESULT; + data->state_start_time = rtc_get_ticks(); + } + break; + } + + case STATE_RESULT: { + if (elapsed >= RESULT_DISPLAY_MS) { + app_window_stack_pop(false); + return; + } + sniprintf(data->status_string, sizeof(data->status_string), + "ACCEL: %s\n\nX: %" PRId16 " mG\nY: %" PRId16 + " mG\nZ: %" PRId16 " mG", + data->test_passed ? "PASS" : "FAIL", + data->variation_x, data->variation_y, data->variation_z); + break; + } + } + + text_layer_set_text(&data->status, data->status_string); +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + AppData *data = context; + + switch (data->state) { + case STATE_IDLE: + // Start test + data->state = STATE_TESTING; + data->state_start_time = rtc_get_ticks(); + data->sample_count = 0; + break; + + default: + break; + } +} + +static void prv_click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) {}; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "ACCEL TEST"); + layer_add_child(&window->layer, &title->layer); + + TextLayer *status = &data->status; + text_layer_init(status, + &GRect(5, 40, + window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 40)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + layer_add_child(&window->layer, &status->layer); + + window_set_click_config_provider_with_context(window, prv_click_config_provider, data); + + data->state = STATE_IDLE; + data->state_start_time = rtc_get_ticks(); + + app_window_stack_push(window, true /* Animated */); + + s_timer = evented_timer_register(SAMPLE_INTERVAL_MS, true /* repeating */, prv_update_display, data); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); + + evented_timer_cancel(s_timer); +} + +const PebbleProcessMd* mfg_accel_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: ED2E214A-D4B5-4360-B5EC-612B9E49FB95 + .common.uuid = { 0xED, 0x2E, 0x21, 0x4A, 0xD4, 0xB5, 0x43, 0x60, + 0xB5, 0xEC, 0x61, 0x2B, 0x9E, 0x49, 0xFB, 0x95, + }, + .name = "MfgAccel", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_accel.h b/src/fw/apps/prf/mfg_accel.h new file mode 100644 index 0000000000..dcda60fc06 --- /dev/null +++ b/src/fw/apps/prf/mfg_accel.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_accel_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_adv.c b/src/fw/apps/prf/mfg_adv.c new file mode 100644 index 0000000000..d8a01fa921 --- /dev/null +++ b/src/fw/apps/prf/mfg_adv.c @@ -0,0 +1,82 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/qr_code.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "services/bluetooth/bluetooth_ctl.h" +#include "services/bluetooth/local_id.h" + +#include + +typedef struct { + Window window; + QRCode qr_code; + TextLayer mac_label; + char mac_buffer[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]; +} AppData; + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, "BLE Adv"); + window_set_fullscreen(window, true); + + bt_local_id_copy_address_mac_string(data->mac_buffer); + + QRCode *qr_code = &data->qr_code; + qr_code_init_with_parameters(qr_code, +#if PBL_ROUND +#define QR_CODE_SIZE ((window->layer.bounds.size.w * 10) / 14) + &GRect((window->layer.bounds.size.w - QR_CODE_SIZE) / 2, + (window->layer.bounds.size.h - QR_CODE_SIZE) / 2, + QR_CODE_SIZE, QR_CODE_SIZE), +#else + &GRect(10, 10, window->layer.bounds.size.w - 20, + window->layer.bounds.size.h - 30), +#endif + data->mac_buffer, strlen(data->mac_buffer), QRCodeECCMedium, + GColorBlack, GColorWhite); + layer_add_child(&window->layer, &qr_code->layer); + + TextLayer *mac_label = &data->mac_label; + text_layer_init_with_parameters(mac_label, + &GRect(0, window->layer.bounds.size.h - PBL_IF_RECT_ELSE(20, 40), + window->layer.bounds.size.w, 20), + data->mac_buffer, fonts_get_system_font(FONT_KEY_GOTHIC_14), + GColorBlack, GColorWhite, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->layer, &mac_label->layer); + + app_window_stack_push(window, true); +} + +static void s_main(void) { + // Restart BLE so it begins advertising + bt_ctl_set_enabled(false); + bt_ctl_set_enabled(true); + + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd *mfg_adv_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 4c8e2a1f-7d3b-4f9e-b5a2-6e1c8d3f7a9b + .common.uuid = {0x4c, 0x8e, 0x2a, 0x1f, 0x7d, 0x3b, 0x4f, 0x9e, + 0xb5, 0xa2, 0x6e, 0x1c, 0x8d, 0x3f, 0x7a, 0x9b}, + .name = "MfgAdv", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_adv.h b/src/fw/apps/prf/mfg_adv.h new file mode 100644 index 0000000000..a66e0172cd --- /dev/null +++ b/src/fw/apps/prf/mfg_adv.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *mfg_adv_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_als.c b/src/fw/apps/prf/mfg_als.c new file mode 100644 index 0000000000..e023e23219 --- /dev/null +++ b/src/fw/apps/prf/mfg_als.c @@ -0,0 +1,239 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_als.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window_private.h" +#include "apps/prf/mfg_test_result.h" +#include "drivers/rtc.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/light.h" +#include "system/logging.h" + +#include +#include + +// ALS pass/fail range (adjust these values based on your test requirements) +#ifdef CONFIG_BOARD_FAMILY_OBELIX +#define ALS_MIN_VALUE 100 +#define ALS_MAX_VALUE 250 +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) +#define ALS_MIN_VALUE 1000 +#define ALS_MAX_VALUE 3000 +#else +#define ALS_MIN_VALUE 0 +#define ALS_MAX_VALUE 65535 +#endif + +// Test parameters +#define COUNTDOWN_MS 2000 +#define SAMPLE_DURATION_MS 3000 +#define RESULT_DISPLAY_MS 1000 +#define SAMPLE_INTERVAL_MS 100 + +typedef enum { + ALSStateWaitForStart = 0, + ALSStateCountdown, + ALSStateSampling, + ALSStatePass, + ALSStateFail, +} ALSTestState; + +#define AMBIENT_READING_STR_LEN 64 + +static EventedTimerID s_timer; + +typedef struct { + Window *window; + TextLayer *status_text_layer; + TextLayer *reading_text_layer; + char status_text[AMBIENT_READING_STR_LEN]; + char ambient_reading[AMBIENT_READING_STR_LEN]; + + ALSTestState test_state; + RtcTicks state_start_time; + uint64_t als_sum; + uint32_t als_sample_count; + uint32_t als_average; +} AmbientLightAppData; + +static void prv_update_display(void *context) { + AmbientLightAppData *data = context; + uint32_t elapsed = (uint32_t)(rtc_get_ticks() - data->state_start_time); + + switch (data->test_state) { + case ALSStateWaitForStart: + snprintf(data->status_text, AMBIENT_READING_STR_LEN, "ALS Test\nPress CENTER\nto start"); + snprintf(data->ambient_reading, AMBIENT_READING_STR_LEN, " "); + break; + + case ALSStateCountdown: + snprintf(data->status_text, AMBIENT_READING_STR_LEN, "Place in\nlight box"); + snprintf(data->ambient_reading, AMBIENT_READING_STR_LEN, "Starting in: %"PRIu32"s", + (COUNTDOWN_MS - elapsed) / 1000 + 1); + if (elapsed >= COUNTDOWN_MS) { + // Start sampling + data->test_state = ALSStateSampling; + data->state_start_time = rtc_get_ticks(); + data->als_sum = 0; + data->als_sample_count = 0; + PBL_LOG_INFO("ALS sampling started"); + } + break; + + case ALSStateSampling: { + // Take a sample + uint32_t level = ambient_light_get_light_level(); + data->als_sum += level; + data->als_sample_count++; + + snprintf(data->status_text, AMBIENT_READING_STR_LEN, "Sampling..."); + snprintf(data->ambient_reading, AMBIENT_READING_STR_LEN, + "Time: %"PRIu32"s\nCurrent: %"PRIu32"\nSamples: %"PRIu32, + (SAMPLE_DURATION_MS - elapsed) / 1000 + 1, level, data->als_sample_count); + + if (elapsed >= SAMPLE_DURATION_MS) { + // Calculate average and determine pass/fail + data->als_average = (uint32_t)(data->als_sum / data->als_sample_count); + + PBL_LOG_INFO("ALS test complete - Average: %"PRIu32" (samples: %"PRIu32")", + data->als_average, data->als_sample_count); + + bool passed = ( +#if ALS_MIN_VALUE > 0 + data->als_average >= ALS_MIN_VALUE && +#endif + data->als_average <= ALS_MAX_VALUE); + mfg_test_result_report(MfgTestId_ALS, passed, data->als_average); + + if (passed) { + data->test_state = ALSStatePass; + PBL_LOG_INFO("ALS test PASSED"); + } else { + data->test_state = ALSStateFail; + PBL_LOG_ERR("ALS test FAILED - Average %"PRIu32" outside range %d-%d", + data->als_average, ALS_MIN_VALUE, ALS_MAX_VALUE); + } + data->state_start_time = rtc_get_ticks(); + } + break; + } + + case ALSStatePass: + case ALSStateFail: + if (elapsed >= RESULT_DISPLAY_MS) { + app_window_stack_pop(false); + return; + } + snprintf(data->status_text, AMBIENT_READING_STR_LEN, + data->test_state == ALSStatePass ? "PASS" : "FAIL"); + snprintf(data->ambient_reading, AMBIENT_READING_STR_LEN, + "Average: %"PRIu32"\nRange: %d-%d", + data->als_average, ALS_MIN_VALUE, ALS_MAX_VALUE); + break; + } + + text_layer_set_text(data->status_text_layer, data->status_text); + text_layer_set_text(data->reading_text_layer, data->ambient_reading); +} + + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + AmbientLightAppData *data = app_state_get_user_data(); + + if (data->test_state == ALSStateWaitForStart) { + // Start countdown + data->test_state = ALSStateCountdown; + data->state_start_time = rtc_get_ticks(); + + PBL_LOG_INFO("ALS test started - countdown %d ms", COUNTDOWN_MS); + } +} + +static void prv_back_click_handler(ClickRecognizerRef recognizer, void *context) { + app_window_stack_pop(true); +} + +static void prv_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); + window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); +} + +static void prv_handle_init(void) { + AmbientLightAppData *data = task_zalloc_check(sizeof(AmbientLightAppData)); + + // Force backlight off for the duration of the test to avoid interfering + // with ALS readings (e.g. when pressing CENTER to start sampling). + light_allow(false); + + data->window = window_create(); + window_set_fullscreen(data->window, true); + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + // Status text layer (top) + data->status_text_layer = text_layer_create(GRect(0, 30, bounds.size.w, 80)); + text_layer_set_font(data->status_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(data->status_text_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(data->status_text_layer)); + + // Reading text layer (bottom) + data->reading_text_layer = text_layer_create(GRect(0, 110, bounds.size.w, 80)); + text_layer_set_font(data->reading_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_text_alignment(data->reading_text_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(data->reading_text_layer)); + + // Initialize state + data->test_state = ALSStateWaitForStart; + data->state_start_time = rtc_get_ticks(); + data->als_sum = 0; + data->als_sample_count = 0; + data->als_average = 0; + + // Set up click handlers + window_set_click_config_provider(data->window, prv_config_provider); + + app_state_set_user_data(data); + app_window_stack_push(data->window, true); + + // Register evented timer for 100ms updates + s_timer = evented_timer_register(SAMPLE_INTERVAL_MS, true /* repeating */, prv_update_display, data); + + PBL_LOG_INFO("ALS test initialized - range: %d-%d", ALS_MIN_VALUE, ALS_MAX_VALUE); +} + +static void prv_handle_deinit(void) { + AmbientLightAppData *data = app_state_get_user_data(); + + // Cancel evented timer + evented_timer_cancel(s_timer); + + text_layer_destroy(data->status_text_layer); + text_layer_destroy(data->reading_text_layer); + window_destroy(data->window); + task_free(data); + + light_allow(true); +} + +static void prv_main(void) { + prv_handle_init(); + app_event_loop(); + prv_handle_deinit(); +} + +const PebbleProcessMd* mfg_als_app_get_info(void) { + static const PebbleProcessMdSystem s_ambient_light_info = { + .common.main_func = prv_main, + .name = "MfgALS" + }; + return (const PebbleProcessMd*) &s_ambient_light_info; +} diff --git a/src/fw/apps/prf/mfg_als.h b/src/fw/apps/prf/mfg_als.h new file mode 100644 index 0000000000..02cb7a252a --- /dev/null +++ b/src/fw/apps/prf/mfg_als.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_als_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_backlight.c b/src/fw/apps/prf/mfg_backlight.c new file mode 100644 index 0000000000..a0d2fc9c08 --- /dev/null +++ b/src/fw/apps/prf/mfg_backlight.c @@ -0,0 +1,152 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "drivers/backlight.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/light.h" + +typedef enum { + TestPattern_White, +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + TestPattern_Red, + TestPattern_Green, + TestPattern_Blue, + TestPattern_Black, +#endif + NumTestPatterns +} TestPattern; + +typedef struct { + Window window; + TestPattern test_pattern; +} AppData; + + +static void prv_update_proc(struct Layer *layer, GContext* ctx) { + graphics_context_set_fill_color(ctx, GColorWhite); + graphics_fill_rect(ctx, &layer->bounds); + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + AppData *app_data = app_state_get_user_data(); + + PBL_LOG_INFO("backlight id:%d", app_data->test_pattern); + switch (app_data->test_pattern) { + case TestPattern_White: + backlight_set_color(BACKLIGHT_COLOR_WHITE); + break; + case TestPattern_Red: + backlight_set_color(BACKLIGHT_COLOR_RED); + break; + case TestPattern_Green: + backlight_set_color(BACKLIGHT_COLOR_GREEN); + break; + case TestPattern_Blue: + backlight_set_color(BACKLIGHT_COLOR_BLUE); + break; + case TestPattern_Black: + backlight_set_color(BACKLIGHT_COLOR_BLACK); + break; + default: + break; + } +#endif +} + +static void prv_result_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool passed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + mfg_test_result_report(MfgTestId_Backlight, passed, 0); + app_window_stack_pop(false); +} + +static void prv_result_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_result_confirmed); +} + +static void prv_show_result_dialog(void) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Backlight Result"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Backlight OK?"); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_result_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_button_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + + app_data->test_pattern++; + + if (app_data->test_pattern >= NumTestPatterns) { + prv_show_result_dialog(); + return; + } + + layer_mark_dirty(&app_data->window.layer); +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_button_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) { + .test_pattern = (TestPattern) app_manager_get_task_context()->args + }; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + Layer *layer = window_get_root_layer(window); + layer_set_update_proc(layer, prv_update_proc); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + light_enable(true); + + prv_handle_init(); + + app_event_loop(); + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + backlight_set_color(BACKLIGHT_COLOR_WARM_WHITE); +#endif + + light_enable(false); +} + +const PebbleProcessMd* mfg_backlight_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 2d825a20-2fa3-4b26-be6f-ab41d1280c73 + .common.uuid = { 0x2d, 0x82, 0x5a, 0x20, 0x2f, 0xa3, 0x4b, 0x26, + 0xbe, 0x6f, 0xab, 0x41, 0xd1, 0x28, 0x0c, 0x73 }, + .name = "MfgBacklight", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_backlight.h b/src/fw/apps/prf/mfg_backlight.h new file mode 100644 index 0000000000..018db25fd6 --- /dev/null +++ b/src/fw/apps/prf/mfg_backlight.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! @file mfg_display.h +//! +//! Test app that shows various display pattern + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_backlight_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_button.c b/src/fw/apps/prf/mfg_button.c new file mode 100644 index 0000000000..203c02ef86 --- /dev/null +++ b/src/fw/apps/prf/mfg_button.c @@ -0,0 +1,244 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "board/display.h" +#include "util/trig.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "applib/ui/path_layer.h" +#include "applib/ui/text_layer.h" +#include "kernel/pbl_malloc.h" +#include "apps/prf/mfg_test_result.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "util/bitset.h" +#include "util/size.h" + +#include +#include +#include + +// How long after test pass / fail to wait before popping the window +#define WINDOW_POP_TIME_S (1) + +typedef struct { + Window window; + + PathLayer arrows[NUM_BUTTONS]; + + //! bitset of buttons pressed so far + uint32_t buttons_pressed; + + TextLayer title; + TextLayer status; + char status_string[35]; + + uint32_t seconds_remaining; + bool test_complete; +} AppData; + +static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + AppData *data = app_state_get_user_data(); + + if (data->test_complete) { + if (--data->seconds_remaining == 0) { + app_window_stack_pop(true); + } + return; + } + + const bool test_passed = (data->buttons_pressed == 0x0f); + if (data->seconds_remaining == 0 || test_passed) { + data->test_complete = true; + + mfg_test_result_report(MfgTestId_Buttons, test_passed, 0); + + if (test_passed) { + sniprintf(data->status_string, sizeof(data->status_string), "PASS!"); + } else { + sniprintf(data->status_string, sizeof(data->status_string), "FAIL!"); + } + data->seconds_remaining = WINDOW_POP_TIME_S; + } else { + sniprintf(data->status_string, sizeof(data->status_string), + "TIME REMAINING: %"PRIu32"s", data->seconds_remaining); + data->seconds_remaining--; + } + + text_layer_set_text(&data->status, data->status_string); +} + +static void prv_button_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + + ButtonId button_id_pressed = click_recognizer_get_button_id(recognizer); + bitset32_set(&app_data->buttons_pressed, button_id_pressed); + layer_set_hidden((struct Layer*)&app_data->arrows[button_id_pressed], true); + + if (app_data->test_complete) { + app_window_stack_remove(&app_data->window, false /* Animated */); + } +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_BACK, prv_button_click_handler); + window_single_click_subscribe(BUTTON_ID_UP, prv_button_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_button_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_button_click_handler); +} + +static void init_arrow_layer_for_button(AppData *data, ButtonId id) { + static GPoint ARROW_PATH_POINTS[] = + {{0, 7}, {14, 7}, {14, 0}, {26, 12}, {14, 24}, {14, 17}, {0, 17}}; + + static const GPathInfo ARROW_PATH_INFO = { + .num_points = ARRAY_LENGTH(ARROW_PATH_POINTS), + .points = ARROW_PATH_POINTS + }; + +// Original arrow dimensions +#define ARROW_W 26 +#define ARROW_H 24 +// The arrow path center (used as rotation pivot) +#define ARROW_CENTER_X 13 +#define ARROW_CENTER_Y 12 +// Layer size must be large enough to contain rotated arrow (diagonal of arrow) +#define ARROW_LAYER_SIZE 36 +#define ARROW_SIZE {ARROW_W, ARROW_H} + + PathLayer *arrow = &data->arrows[id]; + path_layer_init(arrow, &ARROW_PATH_INFO); + path_layer_set_fill_color(arrow, GColorBlack); + path_layer_set_stroke_color(arrow, GColorBlack); + +#if PBL_ROUND + // Position arrows along the display circle + // Angles are in Pebble trig format (0 at 3 o'clock, counter-clockwise) + // Up: -30 deg from top, Down: +30 deg from top, Back: +30 deg from left +#define ARROW_MARGIN 5 + const int16_t center_x = DISP_COLS / 2; + const int16_t center_y = DISP_ROWS / 2; + const int16_t radius = center_x - ARROW_MARGIN - ARROW_LAYER_SIZE / 2; + + // Angles: 0 = 3 o'clock, 90 = 12 o'clock (TRIG_MAX_ANGLE/4), etc. + // Back button: left side, arrow pointing left = 180 deg + // Up button: top-right, 30 deg from horizontal = 30 deg + // Select button: center right = 0 deg + // Down button: bottom-right, 30 deg from horizontal = -30 deg + const int32_t BUTTON_ANGLES[] = { + // BACK: left side, pointing left + DEG_TO_TRIGANGLE(180), + // UP: top-right, 30 deg + DEG_TO_TRIGANGLE(30), + // SELECT: center right, 0 deg + DEG_TO_TRIGANGLE(0), + // DOWN: bottom-right, -30 deg + DEG_TO_TRIGANGLE(-30), + }; + + int32_t angle = BUTTON_ANGLES[id]; + // Position the layer so its center is at the desired edge position + int16_t x = center_x + (cos_lookup(angle) * radius / TRIG_MAX_RATIO) - ARROW_LAYER_SIZE / 2; + int16_t y = center_y - (sin_lookup(angle) * radius / TRIG_MAX_RATIO) - ARROW_LAYER_SIZE / 2; + + // Use larger layer to accommodate rotated arrow without clipping + layer_set_frame(&arrow->layer, &GRect(x, y, ARROW_LAYER_SIZE, ARROW_LAYER_SIZE)); + + // Rotate arrow to point outward (away from center) + // Arrow graphic points right (0 deg), so rotate by -angle to point outward + int32_t rotation = -angle; + gpath_rotate_to(&arrow->path, rotation); + // Move the path so its center aligns with the layer center + // The path rotates around origin, so we need to offset by the layer center + // minus the rotated position of the original arrow center + int32_t cos_rot = cos_lookup(rotation); + int32_t sin_rot = sin_lookup(rotation); + int16_t rotated_center_x = (ARROW_CENTER_X * cos_rot - ARROW_CENTER_Y * sin_rot) / TRIG_MAX_RATIO; + int16_t rotated_center_y = (ARROW_CENTER_Y * cos_rot + ARROW_CENTER_X * sin_rot) / TRIG_MAX_RATIO; + gpath_move_to(&arrow->path, GPoint(ARROW_LAYER_SIZE / 2 - rotated_center_x, + ARROW_LAYER_SIZE / 2 - rotated_center_y)); +#else +#define ARROW_LR_MARGIN 5 +#define ARROW_TB_MARGIN 30 + const GRect ARROW_RECTS[] = { + // BACK + {{ARROW_LR_MARGIN, ARROW_TB_MARGIN}, ARROW_SIZE}, + // UP + {{DISP_COLS - ARROW_LR_MARGIN - ARROW_W, ARROW_TB_MARGIN}, ARROW_SIZE}, + // SELECT + {{DISP_COLS - ARROW_LR_MARGIN - ARROW_W, (DISP_ROWS - ARROW_H) / 2}, ARROW_SIZE}, + // DOWN + {{DISP_COLS - ARROW_LR_MARGIN - ARROW_W, DISP_ROWS - ARROW_TB_MARGIN - ARROW_H }, ARROW_SIZE}, + }; + + layer_set_frame(&arrow->layer, &ARROW_RECTS[id]); + + if (id == BUTTON_ID_BACK) { + gpath_rotate_to(&arrow->path, (TRIG_MAX_ANGLE / 2)); + gpath_move_to(&arrow->path, GPoint(26, 24)); + } +#endif + + layer_add_child(&data->window.layer, &arrow->layer); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) { + .seconds_remaining = 10, + .buttons_pressed = 0, + .test_complete = false, + }; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_overrides_back_button(window, true); + window_set_click_config_provider(window, prv_config_provider); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "BUTTON TEST"); + layer_add_child(&window->layer, &title->layer); + + TextLayer *status = &data->status; + text_layer_init(status, + &GRect(5, 110, + window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 110)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + layer_add_child(&window->layer, &status->layer); + + for (ButtonId id = 0; id < NUM_BUTTONS; ++id) { + init_arrow_layer_for_button(data, id); + } + + app_window_stack_push(window, true /* Animated */); + + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd* mfg_button_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: eed03647-fa9e-4bae-9254-608aa297e4e4 + .common.uuid = { 0xee, 0xd0, 0x36, 0x47, 0xfa, 0x9e, 0x4b, 0xae, + 0x92, 0x54, 0x60, 0x8a, 0xa2, 0x97, 0xe4, 0xe4}, + .name = "MfgButton", + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/prf/mfg_button.h b/src/fw/apps/prf/mfg_button.h new file mode 100644 index 0000000000..8e09280bce --- /dev/null +++ b/src/fw/apps/prf/mfg_button.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_button_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_charge.c b/src/fw/apps/prf/mfg_charge.c new file mode 100644 index 0000000000..6e4057c469 --- /dev/null +++ b/src/fw/apps/prf/mfg_charge.c @@ -0,0 +1,180 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_charge.h" + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/ui.h" +#include "applib/ui/window_private.h" +#include "apps/prf/mfg_test_result.h" +#include "drivers/battery.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/battery/battery_curve.h" +#include "pbl/services/idle_watchdog.h" +#include "system/logging.h" + +#include + +typedef enum { + ChargeStateWaitPlug = 0, + ChargeStateWaitCharge, + ChargeStatePass, + ChargeStateFail, +} ChargeTestState; + +static const char* status_text[] = { + [ChargeStateWaitPlug] = "Plug Charger", + [ChargeStateWaitCharge] = "Wait Charge...", + [ChargeStatePass] = "PASS - Unplug", + [ChargeStateFail] = "FAIL - Unplug", +}; + +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) +static const int TEMP_MIN_MC = 15000; // 15.0C +static const int TEMP_MAX_MC = 35000; // 35.0C +#else +static const int TEMP_MIN_MC = 0; +static const int TEMP_MAX_MC = 0; +#endif + +static const int WAIT_CHARGE_TIMEOUT_S = 5; + +typedef struct { + Window window; + + TextLayer status; + char status_string[20]; + + TextLayer details; + char details_string[64]; + + ChargeTestState test_state; + uint32_t seconds_remaining; +} AppData; + +static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + AppData *data = app_state_get_user_data(); + + ChargeTestState next_state = data->test_state; + + BatteryConstants battery_const; + int ret = battery_get_constants(&battery_const); + if (ret < 0) { + PBL_LOG_ERR("Skipping bad constants reading"); + return; + } + + const int charge_mv = battery_const.v_mv; + const int32_t temp_mc = battery_const.t_mc; + const BatteryChargeState charge_state = battery_get_charge_state(); + + switch (data->test_state) { + case ChargeStateWaitPlug: + if (charge_state.is_plugged) { + next_state = ChargeStateWaitCharge; + data->seconds_remaining = WAIT_CHARGE_TIMEOUT_S; + } + break; + case ChargeStateWaitCharge: + if (!charge_state.is_plugged) { + next_state = ChargeStateWaitPlug; + break; + } + if (charge_state.is_charging) { + if (temp_mc >= TEMP_MIN_MC && temp_mc <= TEMP_MAX_MC) { + next_state = ChargeStatePass; + } else { + PBL_LOG_ERR("Temperature out of range: %" PRId32 "mC", temp_mc); + next_state = ChargeStateFail; + } + } else { + --data->seconds_remaining; + if (data->seconds_remaining == 0) { + PBL_LOG_ERR("Timed out waiting for charge to start"); + next_state = ChargeStateFail; + } + } + break; + case ChargeStatePass: + case ChargeStateFail: + if (!charge_state.is_plugged) { + bool passed = (data->test_state == ChargeStatePass); + mfg_test_result_report(MfgTestId_Charge, passed, 0); + app_window_stack_pop(true); + return; + } + break; + default: + break; + } + + data->test_state = next_state; + + sniprintf(data->status_string, sizeof(data->status_string), + "CHARGE\n%s", status_text[data->test_state]); + text_layer_set_text(&data->status, data->status_string); + + int8_t temp_c = (int8_t)(temp_mc / 1000); + uint8_t temp_c_frac = ((temp_mc > 0 ? temp_mc : -temp_mc) % 1000) / 10; + sniprintf(data->details_string, sizeof(data->details_string), + "%umV %" PRId8 ".%02" PRIu8 "C\r\nUSB: %s\r\nCharging: %s", + charge_mv, + temp_c, temp_c_frac, + charge_state.is_plugged ? "yes" : "no", + charge_state.is_charging ? "yes" : "no"); + text_layer_set_text(&data->details, data->details_string); +} + +static void app_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + *data = (AppData) { + .test_state = ChargeStateWaitPlug, + .seconds_remaining = WAIT_CHARGE_TIMEOUT_S, + }; + + Window *window = &data->window; + window_init(window, "Charge Test"); + + TextLayer *status = &data->status; + text_layer_init(status, &window->layer.bounds); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + text_layer_set_text(status, status_text[data->test_state]); + layer_add_child(&window->layer, &status->layer); + + TextLayer *details = &data->details; + text_layer_init(details, + &GRect(0, 65, window->layer.bounds.size.w, window->layer.bounds.size.h - 65)); + text_layer_set_font(details, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(details, GTextAlignmentCenter); + layer_add_child(&window->layer, &details->layer); + + window_set_fullscreen(window, true); + + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + app_init(); + + app_event_loop(); +} + +const PebbleProcessMd* mfg_charge_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: fbb6d0e6-2d7d-40bc-8b01-f2f8beb9c394 + .common.uuid = { 0xfb, 0xb6, 0xd0, 0xe6, 0x2d, 0x7d, 0x40, 0xbc, + 0x8b, 0x01, 0xf2, 0xf8, 0xbe, 0xb9, 0xc3, 0x94 }, + .name = "Charge App", + }; + + return (PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_charge.h b/src/fw/apps/prf/mfg_charge.h new file mode 100644 index 0000000000..b1f2ee8cd4 --- /dev/null +++ b/src/fw/apps/prf/mfg_charge.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_charge_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_display.c b/src/fw/apps/prf/mfg_display.c new file mode 100644 index 0000000000..e1d7e423d2 --- /dev/null +++ b/src/fw/apps/prf/mfg_display.c @@ -0,0 +1,287 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/vibes.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "console/prompt.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "mfg/mfg_mode/mfg_factory_mode.h" +#include "process_management/app_manager.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "resource/system_resource.h" +#include "pbl/services/light.h" +#include "util/size.h" + +typedef enum { + TestPattern_Crosshair, + TestPattern_Black, + TestPattern_White, +#if PBL_COLOR + TestPattern_Red, + TestPattern_Green, + TestPattern_Blue, +#else + TestPattern_Gray, +#endif + + NumTestPatterns +} TestPattern; + +typedef struct { + Window window; + + TestPattern test_pattern; +} AppData; + +static void prv_draw_solid(Layer *layer, GContext *ctx, GColor color) { + graphics_context_set_fill_color(ctx, color); + graphics_fill_rect(ctx, &layer->bounds); +} + +#if PBL_ROUND + +static void prv_draw_round_border(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { + for (int i = 0; i < layer->bounds.size.h / 2 - radial_padding_size; ++i) { + const GBitmapDataRowInfoInternal *data_row_infos = g_gbitmap_data_row_infos; + const uint8_t mask = data_row_infos[i].min_x + radial_padding_size; + const int offset = i + radial_padding_size; + // Draw both row-wise and column-wise to fill in any discontinuities + // in the border circle. + + // Top-left quadrant + graphics_draw_pixel(ctx, GPoint(mask, offset)); + graphics_draw_pixel(ctx, GPoint(offset, mask)); + // Top-right quadrant + graphics_draw_pixel(ctx, GPoint(mask, layer->bounds.size.h - offset - 1)); + graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - offset - 1, mask)); + // Bottom-left quadrant + graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - mask - 1, offset)); + graphics_draw_pixel(ctx, GPoint(offset, layer->bounds.size.h - mask - 1)); + // Bottom-right quadrant + graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - mask - 1, + layer->bounds.size.h - offset - 1)); + graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - offset - 1, + layer->bounds.size.h - mask - 1)); + } +} + +static void prv_draw_border(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { + if (radial_padding_size != 0) { + prv_draw_round_border(layer, ctx, 0); + } + prv_draw_round_border(layer, ctx, radial_padding_size); + + // Draw letter to identify screen + if (radial_padding_size >= 2) { + GRect identifier_area = GRect(40, 40, 20, 20); + char identifier[] = {'A' + radial_padding_size - 2, 0}; + graphics_context_set_text_color(ctx, GColorWhite); + graphics_draw_text(ctx, identifier, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), + identifier_area, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + } +} + +#else + +static void prv_draw_border(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { + graphics_draw_rect(ctx, &layer->bounds); +} + +#endif + +static void prv_draw_crosshair_screen(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { + graphics_context_set_fill_color(ctx, GColorBlack); + graphics_fill_rect(ctx, &layer->bounds); + + // Draw crosshair + graphics_context_set_stroke_color(ctx, GColorWhite); + graphics_draw_line( + ctx, GPoint(layer->bounds.size.w / 2, radial_padding_size), + GPoint(layer->bounds.size.w / 2, layer->bounds.size.h - radial_padding_size - 1)); + graphics_draw_line( + ctx, GPoint(radial_padding_size, layer->bounds.size.h / 2), + GPoint(layer->bounds.size.w - radial_padding_size - 1, layer->bounds.size.h / 2)); + + prv_draw_border(layer, ctx, radial_padding_size); +} + +static void prv_update_proc(struct Layer *layer, GContext* ctx) { + AppData *app_data = app_state_get_user_data(); + + switch (app_data->test_pattern) { + case TestPattern_Crosshair: + prv_draw_crosshair_screen(layer, ctx, 0); + break; + case TestPattern_Black: + prv_draw_solid(layer, ctx, GColorBlack); + break; + case TestPattern_White: + prv_draw_solid(layer, ctx, GColorWhite); + break; +#if PBL_COLOR + case TestPattern_Red: + prv_draw_solid(layer, ctx, GColorRed); + break; + case TestPattern_Green: + prv_draw_solid(layer, ctx, GColorGreen); + break; + case TestPattern_Blue: + prv_draw_solid(layer, ctx, GColorBlue); + break; +#else + case TestPattern_Gray: + prv_draw_solid(layer, ctx, GColorDarkGray); + break; +#endif + default: + break; + } +} + +static void prv_result_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool passed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + mfg_test_result_report(MfgTestId_Display, passed, 0); + app_window_stack_pop(false); +} + +static void prv_result_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_result_confirmed); +} + +static void prv_show_result_dialog(void) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Display Result"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Display OK?"); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_result_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_button_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + + app_data->test_pattern++; + + if (app_data->test_pattern >= NumTestPatterns) { + prv_show_result_dialog(); + return; + } + + layer_mark_dirty(&app_data->window.layer); +} + +static void prv_change_pattern(void *data) { + AppData *app_data = app_state_get_user_data(); + + app_data->test_pattern = (TestPattern) data; + + layer_mark_dirty(&app_data->window.layer); +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_button_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) { + .test_pattern = (TestPattern) app_manager_get_task_context()->args + }; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + Layer *layer = window_get_root_layer(window); + layer_set_update_proc(layer, prv_update_proc); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd* mfg_display_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: df582042-5beb-410f-9fed-76eccd31821e + .common.uuid = { 0xdf, 0x58, 0x20, 0x42, 0x5b, 0xeb, 0x41, 0x0f, + 0x9f, 0xed, 0x76, 0xec, 0xcd, 0x31, 0x82, 0x1e }, + .name = "MfgDisplay", + }; + return (const PebbleProcessMd*) &s_app_info; +} + + +// Prompt Commands +/////////////////////////////////////////////////////////////////////////////// + +static void prv_launch_app_cb(void *data) { + if (app_manager_get_current_app_md() == mfg_display_app_get_info()) { + process_manager_send_callback_event_to_process(PebbleTask_App, prv_change_pattern, data); + } else { + app_manager_launch_new_app(&(AppLaunchConfig) { + .md = mfg_display_app_get_info(), + .common.args = data, + }); + } +} + +void command_display_set(const char *color) { + const char * const ARGS[] = { + [TestPattern_Crosshair] = "crosshair", + [TestPattern_Black] = "black", + [TestPattern_White] = "white", +#if PBL_COLOR + [TestPattern_Red] = "red", + [TestPattern_Green] = "green", + [TestPattern_Blue] = "blue", +#else + [TestPattern_Gray] = "gray", +#endif + }; + + for (unsigned int i = 0; i < ARRAY_LENGTH(ARGS); ++i) { + if (!strcmp(color, ARGS[i])) { + // Do this first because it launches the mfg menu using a callback, as if we did this in the + // callback we send below to launch the display app it would end up launching the menu on + // top of the display app. + if (!mfg_is_mfg_mode()) { + mfg_enter_mfg_mode_and_launch_app(); + } + + launcher_task_add_callback(prv_launch_app_cb, (void*) i); + prompt_send_response("OK"); + return; + } + } + + prompt_send_response("Invalid command"); +} diff --git a/src/fw/apps/prf/mfg_display.h b/src/fw/apps/prf/mfg_display.h new file mode 100644 index 0000000000..d9246e3ff1 --- /dev/null +++ b/src/fw/apps/prf/mfg_display.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! @file mfg_display.h +//! +//! Test app that shows various display pattern + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_display_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_hrm.c b/src/fw/apps/prf/mfg_hrm.c new file mode 100644 index 0000000000..8954448dbe --- /dev/null +++ b/src/fw/apps/prf/mfg_hrm.c @@ -0,0 +1,151 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_hrm.h" + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "drivers/accel.h" +#include "drivers/hrm.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "mfg/mfg_info.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "process_management/process_manager.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "util/bitset.h" +#include "util/size.h" +#include "util/trig.h" + +#include +#include +#include + +#ifdef CONFIG_HRM + +#define STATUS_STRING_LEN 32 +#define BPM_STRING_LEN 32 +#define SPO2_STRING_LEN 32 + +typedef struct { + Window window; + EventServiceInfo hrm_event_info; + + TextLayer title_text_layer; + TextLayer status_text_layer; + TextLayer bpm_text_layer; + TextLayer spo2_text_layer; + char status_string[STATUS_STRING_LEN]; + char bpm_string[BPM_STRING_LEN]; + char spo2_string[SPO2_STRING_LEN]; + HRMSessionRef hrm_session; +} AppData; + +static void prv_handle_hrm_data(PebbleEvent *e, void *context) { + AppData *app_data = app_state_get_user_data(); + + if (e->type == PEBBLE_HRM_EVENT) { + snprintf(app_data->status_string, STATUS_STRING_LEN, "Sampling..."); + + if (e->hrm.event_type == HRMEvent_BPM) { + snprintf(app_data->bpm_string, BPM_STRING_LEN, + "HR:%d (quality:%d)", e->hrm.bpm.bpm, e->hrm.bpm.quality); + } else if (e->hrm.event_type == HRMEvent_SpO2) { + snprintf(app_data->spo2_string, SPO2_STRING_LEN, + "SpO2:%d (quality:%d)", e->hrm.spo2.percent, e->hrm.spo2.quality); + } + + layer_mark_dirty(&app_data->window.layer); + } +} + +static void prv_handle_init(void) { + AppData *data = task_zalloc(sizeof(*data)); + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + TextLayer *title = &data->title_text_layer; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "HRM TEST"); + layer_add_child(&window->layer, &title->layer); + + sniprintf(data->status_string, STATUS_STRING_LEN, "Starting..."); + + snprintf(data->bpm_string, BPM_STRING_LEN, "HR:--"); + snprintf(data->spo2_string, SPO2_STRING_LEN, "SpO2:--"); + + TextLayer *status = &data->status_text_layer; + text_layer_init(status, + &GRect(5, 40, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 40)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + text_layer_set_text(status, data->status_string); + layer_add_child(&window->layer, &status->layer); + + TextLayer *bpm = &data->bpm_text_layer; + text_layer_init(bpm, + &GRect(5, 80, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 80)); + text_layer_set_font(bpm, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(bpm, GTextAlignmentCenter); + text_layer_set_text(bpm, data->bpm_string); + layer_add_child(&window->layer, &bpm->layer); + + TextLayer *spo2 = &data->spo2_text_layer; + text_layer_init(spo2, + &GRect(5, 120, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 120)); + text_layer_set_font(spo2, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(spo2, GTextAlignmentCenter); + text_layer_set_text(spo2, data->spo2_string); + layer_add_child(&window->layer, &spo2->layer); + + data->hrm_event_info = (EventServiceInfo){ + .type = PEBBLE_HRM_EVENT, + .handler = prv_handle_hrm_data, + }; + event_service_client_subscribe(&data->hrm_event_info); + + // Use app data as session ref + AppInstallId app_id = 1; + data->hrm_session = sys_hrm_manager_app_subscribe(app_id, 1, SECONDS_PER_HOUR, + HRMFeature_BPM | HRMFeature_SpO2); + + app_window_stack_push(window, true); +} + +static void prv_handle_deinit(void) { + AppData *data = app_state_get_user_data(); + event_service_client_unsubscribe(&data->hrm_event_info); + sys_hrm_manager_unsubscribe(data->hrm_session); + + text_layer_deinit(&data->title_text_layer); + text_layer_deinit(&data->status_text_layer); + window_deinit(&data->window); + app_free(data); +} + +static void prv_main(void) { + prv_handle_init(); + app_event_loop(); + prv_handle_deinit(); +} + +const PebbleProcessMd* mfg_hrm_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &prv_main, + .name = "MfgHRM", + }; + return (const PebbleProcessMd*) &s_app_info; +} + +#endif // CONFIG_HRM diff --git a/src/fw/apps/prf/mfg_hrm.h b/src/fw/apps/prf/mfg_hrm.h new file mode 100644 index 0000000000..fe2285aa82 --- /dev/null +++ b/src/fw/apps/prf/mfg_hrm.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_hrm_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_hrm_ctr_leakage_obelix.c b/src/fw/apps/prf/mfg_hrm_ctr_leakage_obelix.c new file mode 100644 index 0000000000..3c33557cdd --- /dev/null +++ b/src/fw/apps/prf/mfg_hrm_ctr_leakage_obelix.c @@ -0,0 +1,301 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_hrm_ctr_leakage_obelix.h" + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "apps/prf/mfg_test_result.h" +#include "drivers/accel.h" +#include "drivers/hrm.h" +#include "drivers/rtc.h" +#include "drivers/hrm/gh3x2x.h" +#include "gh_demo.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "mfg/mfg_info.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "process_management/process_manager.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "util/bitset.h" +#include "util/size.h" +#include "util/trig.h" + +#include +#include +#include + +#define STATUS_STRING_LEN 32 +#define CTR_STRING_LEN 128 +#define LEAKAGE_STRING_LEN 128 +#define RESULT_DISPLAY_MS 1000 + +#define PPG_GR_CTR_THS0 (423.0f) +#define PPG_GR_CTR_THS1 (441.0f) +#define PPG_IR_CTR_THS0 (339.0f) +#define PPG_IR_CTR_THS1 (336.0f) +#define PPG_RED_CTR_THS0 (564.0f) +#define PPG_RED_CTR_THS1 (597.0f) +#define PPG_GR_LEAK_THS0 (1.87f) +#define PPG_GR_LEAK_THS1 (2.4f) +#define PPG_IR_LEAK_THS0 (10.2f) +#define PPG_IR_LEAK_THS1 (9.8f) +#define PPG_RED_LEAK_THS0 (7.0f) +#define PPG_RED_LEAK_THS1 (9.6f) + +typedef enum { + TestMode_NULL, + TestMode_CTR, + TestMode_Leakage, + TestMode_Algo_HR, + TestMode_Algo_SPO2, +}HRMTestMode; + +typedef struct { + Window window; + EventServiceInfo hrm_event_info; + + TextLayer title_text_layer; + TextLayer status_text_layer; + TextLayer ctr_text_layer; + TextLayer leak_text_layer; + char status_string[STATUS_STRING_LEN]; + char ctr_string[CTR_STRING_LEN]; + char leak_string[LEAKAGE_STRING_LEN]; + HRMSessionRef hrm_session; + HRMTestMode test_mode; + + bool ctr_received; + bool leak_received; + bool ctr_passed; + bool leak_passed; + RtcTicks result_start_time; +} AppData; + +static void prv_handle_hrm_data(PebbleEvent *e, void *context) { + AppData *app_data = app_state_get_user_data(); + + if (e->type == PEBBLE_HRM_EVENT) { + if (app_data->test_mode >= TestMode_Algo_HR) { + if (e->hrm.event_type == HRMEvent_BPM) { + snprintf(app_data->status_string, STATUS_STRING_LEN, "HR Sampling... %d", HRM->state->is_wear); + memset(app_data->ctr_string, 0, CTR_STRING_LEN); + snprintf(app_data->ctr_string, CTR_STRING_LEN, "HR:%d Q:%d", e->hrm.bpm.bpm, e->hrm.bpm.quality); + PBL_LOG_DBG("%s", app_data->ctr_string); + } else if (e->hrm.event_type == HRMEvent_SpO2) { + snprintf(app_data->status_string, STATUS_STRING_LEN, "SPO2 Sampling... %d", HRM->state->is_wear); + memset(app_data->leak_string, 0, LEAKAGE_STRING_LEN); + snprintf(app_data->leak_string, CTR_STRING_LEN, "SPO2:%d Q:%d", e->hrm.spo2.percent, e->hrm.spo2.quality); + PBL_LOG_DBG("%s", app_data->leak_string); + } + } + else { + if (e->hrm.event_type == HRMEvent_CTR) { + bool rst = (e->hrm.ctr->ctr[0] >= PPG_GR_CTR_THS0) && (e->hrm.ctr->ctr[1] >= PPG_GR_CTR_THS1) + && (e->hrm.ctr->ctr[2] >= PPG_IR_CTR_THS0) && (e->hrm.ctr->ctr[3] >= PPG_IR_CTR_THS1) + && (e->hrm.ctr->ctr[4] >= PPG_RED_CTR_THS0) && (e->hrm.ctr->ctr[5] >= PPG_RED_CTR_THS1); + app_data->ctr_received = true; + app_data->ctr_passed = rst; + memset(app_data->ctr_string, 0, CTR_STRING_LEN); + snprintf(app_data->ctr_string, CTR_STRING_LEN, + "CTR:(%s)\n%4d.%02d %4d.%02d %4d.%02d\n%4d.%02d %4d.%02d %4d.%02d", + rst?"PASS":"FAILED", + (int)e->hrm.ctr->ctr[0], (int)(e->hrm.ctr->ctr[0]*100)%100, + (int)e->hrm.ctr->ctr[2], (int)(e->hrm.ctr->ctr[2]*100)%100, + (int)e->hrm.ctr->ctr[4], (int)(e->hrm.ctr->ctr[4]*100)%100, + (int)e->hrm.ctr->ctr[1], (int)(e->hrm.ctr->ctr[1]*100)%100, + (int)e->hrm.ctr->ctr[3], (int)(e->hrm.ctr->ctr[3]*100)%100, + (int)e->hrm.ctr->ctr[5], (int)(e->hrm.ctr->ctr[5]*100)%100); + PBL_LOG_DBG("%s", app_data->ctr_string); + } else if (e->hrm.event_type == HRMEvent_Leakage) { + bool rst = (e->hrm.leakage->leakage[0] <= PPG_GR_LEAK_THS0) && (e->hrm.leakage->leakage[1] <= PPG_GR_LEAK_THS1) + && (e->hrm.leakage->leakage[2] <= PPG_IR_LEAK_THS0) && (e->hrm.leakage->leakage[3] <= PPG_IR_LEAK_THS1) + && (e->hrm.leakage->leakage[4] <= PPG_RED_LEAK_THS0) && (e->hrm.leakage->leakage[5] <= PPG_RED_LEAK_THS1); + app_data->leak_received = true; + app_data->leak_passed = rst; + memset(app_data->leak_string, 0, LEAKAGE_STRING_LEN); + snprintf(app_data->leak_string, LEAKAGE_STRING_LEN, + "Leak:(%s)\n%4d.%02d %4d.%02d %4d.%02d\n%4d.%02d %4d.%02d %4d.%02d", + rst?"PASS":"FAILED", + (int)e->hrm.leakage->leakage[0], (int)(e->hrm.leakage->leakage[0]*100)%100, + (int)e->hrm.leakage->leakage[2], (int)(e->hrm.leakage->leakage[2]*100)%100, + (int)e->hrm.leakage->leakage[4], (int)(e->hrm.leakage->leakage[4]*100)%100, + (int)e->hrm.leakage->leakage[1], (int)(e->hrm.leakage->leakage[1]*100)%100, + (int)e->hrm.leakage->leakage[3], (int)(e->hrm.leakage->leakage[3]*100)%100, + (int)e->hrm.leakage->leakage[5], (int)(e->hrm.leakage->leakage[5]*100)%100); + PBL_LOG_DBG("%s", app_data->leak_string); + } + + // When both CTR and leakage results are in, report and start close timer + if (app_data->ctr_received && app_data->leak_received && + app_data->result_start_time == 0) { + bool passed = app_data->ctr_passed && app_data->leak_passed; + mfg_test_result_report(MfgTestId_HrmCtrLeakage, passed, 0); + snprintf(app_data->status_string, STATUS_STRING_LEN, + passed ? "PASS" : "FAIL"); + app_data->result_start_time = rtc_get_ticks(); + } + } + layer_mark_dirty(&app_data->window.layer); + } +} + +static void prv_result_timer_callback(void *cb_data) { + AppData *data = app_state_get_user_data(); + + if (data->result_start_time != 0) { + uint32_t elapsed = (uint32_t)(rtc_get_ticks() - data->result_start_time); + if (elapsed >= RESULT_DISPLAY_MS) { + app_window_stack_pop(false); + return; + } + } + + layer_mark_dirty(&data->window.layer); + app_timer_register(100, prv_result_timer_callback, NULL); +} + +static void prv_update_status(void* param) { + layer_mark_dirty((Layer *)param); +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + sys_hrm_manager_set_features(app_data->hrm_session, HRMFeature_CTR | HRMFeature_Leakage); + if (app_data->test_mode != TestMode_CTR) { + gh3x2x_start_ft_ctr(); + app_data->test_mode = TestMode_CTR; + snprintf(app_data->status_string, STATUS_STRING_LEN, "CTR Sampling..."); + // Start polling timer for result display timeout + app_timer_register(100, prv_result_timer_callback, NULL); + } else if(app_data->test_mode != TestMode_Leakage){ + gh3x2x_start_ft_leakage(); + app_data->test_mode = TestMode_Leakage; + snprintf(app_data->status_string, STATUS_STRING_LEN, "Leak Sampling..."); + } + + app_timer_register(10, prv_update_status, &app_data->window.layer); +} + +static void prv_down_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + + if (app_data->test_mode != TestMode_Algo_HR) { + snprintf(app_data->status_string, STATUS_STRING_LEN, "HR Sampling..."); + gh3x2x_set_work_mode(GH3X2X_FUNCTION_HR); + app_data->test_mode = TestMode_Algo_HR; + } else { + snprintf(app_data->status_string, STATUS_STRING_LEN, "SPO2 Sampling..."); + gh3x2x_set_work_mode(GH3X2X_FUNCTION_SPO2); + app_data->test_mode = TestMode_Algo_SPO2; + } + + snprintf(app_data->ctr_string, CTR_STRING_LEN, "HR: Q:"); + snprintf(app_data->leak_string, LEAKAGE_STRING_LEN, "SPO2: Q:"); + event_service_client_unsubscribe(&app_data->hrm_event_info); + sys_hrm_manager_unsubscribe(app_data->hrm_session); + //let sensor sleep by waiting 50ms + psleep(50); + event_service_client_subscribe(&app_data->hrm_event_info); + // Use app data as session ref + AppInstallId app_id = 1; + app_data->hrm_session = sys_hrm_manager_app_subscribe(app_id, 1, SECONDS_PER_HOUR, + HRMFeature_BPM | HRMFeature_SpO2); + + app_timer_register(10, prv_update_status, &app_data->window.layer); +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = task_zalloc(sizeof(*data)); + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + TextLayer *title = &data->title_text_layer; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "HRM TEST"); + layer_add_child(&window->layer, &title->layer); + + sniprintf(data->status_string, STATUS_STRING_LEN, "Press Sel to Start"); + + snprintf(data->ctr_string, CTR_STRING_LEN, "CTR:--"); + snprintf(data->leak_string, LEAKAGE_STRING_LEN, "Leak:--"); + + TextLayer *status = &data->status_text_layer; + text_layer_init(status, + &GRect(5, 30, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 30)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + text_layer_set_text(status, data->status_string); + layer_add_child(&window->layer, &status->layer); + + TextLayer *leak = &data->leak_text_layer; + text_layer_init(leak, + &GRect(5, 60, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 140)); + text_layer_set_font(leak, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(leak, GTextAlignmentCenter); + text_layer_set_text(leak, data->leak_string); + layer_add_child(&window->layer, &leak->layer); + + TextLayer *ctr = &data->ctr_text_layer; + text_layer_init(ctr, + &GRect(5, 140, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 60)); + text_layer_set_font(ctr, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(ctr, GTextAlignmentCenter); + text_layer_set_text(ctr, data->ctr_string); + layer_add_child(&window->layer, &ctr->layer); + + data->hrm_event_info = (EventServiceInfo){ + .type = PEBBLE_HRM_EVENT, + .handler = prv_handle_hrm_data, + }; + event_service_client_subscribe(&data->hrm_event_info); + + // Use app data as session ref + AppInstallId app_id = 1; + data->hrm_session = sys_hrm_manager_app_subscribe(app_id, 1, SECONDS_PER_HOUR, + HRMFeature_CTR | HRMFeature_Leakage); + + app_window_stack_push(window, true); +} + +static void prv_handle_deinit(void) { + AppData *data = app_state_get_user_data(); + event_service_client_unsubscribe(&data->hrm_event_info); + sys_hrm_manager_unsubscribe(data->hrm_session); + + text_layer_deinit(&data->title_text_layer); + text_layer_deinit(&data->status_text_layer); + window_deinit(&data->window); + app_free(data); +} + +static void prv_main(void) { + prv_handle_init(); + app_event_loop(); + prv_handle_deinit(); +} + +const PebbleProcessMd* mfg_hrm_ctr_leakage_obelix_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &prv_main, + .name = "MfgHRMCTRLeakageObelix", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_hrm_ctr_leakage_obelix.h b/src/fw/apps/prf/mfg_hrm_ctr_leakage_obelix.h new file mode 100644 index 0000000000..599a1f5c26 --- /dev/null +++ b/src/fw/apps/prf/mfg_hrm_ctr_leakage_obelix.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_hrm_ctr_leakage_obelix_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_info_qr.c b/src/fw/apps/prf/mfg_info_qr.c new file mode 100644 index 0000000000..e46bff6cab --- /dev/null +++ b/src/fw/apps/prf/mfg_info_qr.c @@ -0,0 +1,140 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/app_watch_info.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/qr_code.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "applib/ui/text_layer.h" +#include "kernel/pbl_malloc.h" +#include "mfg/mfg_info.h" +#include "mfg/mfg_serials.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/bluetooth/local_id.h" +#include "util/bitset.h" +#include "util/size.h" + +#include "git_version.auto.h" + +#include +#include +#include +#include + +typedef struct { + WatchInfoColor color; + const char *short_name; +} ColorTable; + +static const ColorTable s_color_table[] = { +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + { .color = WATCH_INFO_COLOR_COREDEVICES_P2D_BLACK, .short_name = "BK" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_P2D_WHITE, .short_name = "WH" }, +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + { .color = WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_GREY, .short_name = "BG" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_RED, .short_name = "BR" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_BLUE, .short_name = "SB" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_GREY, .short_name = "SG" }, +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) + { .color = WATCH_INFO_COLOR_COREDEVICES_PR2_BLACK_20, .short_name = "BK20" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_14, .short_name = "SV14" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_20, .short_name = "SV20" }, + { .color = WATCH_INFO_COLOR_COREDEVICES_PR2_GOLD_14, .short_name = "GD14" }, +#endif +}; + +static const char* prv_get_color_short_name(WatchInfoColor color) { + for (size_t i = 0; i < ARRAY_LENGTH(s_color_table); i++) { + if (s_color_table[i].color == color) { + return s_color_table[i].short_name; + } + } + return "??"; +} + +typedef struct { + Window window; + + QRCode qr_code; + TextLayer serial; + + char serial_buffer[MFG_SERIAL_NUMBER_SIZE + 1]; + char bt_mac_buffer[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]; + char qr_buffer[128]; +} AppData; + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + // Get all the required information + const char *serial_number = mfg_get_serial_number(); + strncpy(data->serial_buffer, serial_number, MFG_SERIAL_NUMBER_SIZE); + data->serial_buffer[MFG_SERIAL_NUMBER_SIZE] = '\0'; + + bt_local_id_copy_address_mac_string(data->bt_mac_buffer); + + BatteryChargeState battery_state = battery_get_charge_state(); + uint16_t battery_mv = battery_state_get_voltage(); + uint8_t battery_pct = battery_state.charge_percent; + + WatchInfoColor watch_color = mfg_info_get_watch_color(); + const char *color_short_name = prv_get_color_short_name(watch_color); + + // Build QR code string + snprintf(data->qr_buffer, sizeof(data->qr_buffer), + "%s;%s;%s;%u;%u;%s", + serial_number, data->bt_mac_buffer, GIT_TAG, battery_mv, battery_pct, color_short_name); + + QRCode* qr_code = &data->qr_code; + qr_code_init_with_parameters(qr_code, +#if PBL_ROUND +#define QR_CODE_SIZE ((window->layer.bounds.size.w * 10) / 14) + &GRect((window->layer.bounds.size.w - QR_CODE_SIZE) / 2, + (window->layer.bounds.size.h - QR_CODE_SIZE) / 2, + QR_CODE_SIZE, QR_CODE_SIZE), +#else + &GRect(10, 10, window->layer.bounds.size.w - 20, + window->layer.bounds.size.h - 30), +#endif + data->qr_buffer, strlen(data->qr_buffer), QRCodeECCMedium, + GColorBlack, GColorWhite); + layer_add_child(&window->layer, &qr_code->layer); + + TextLayer* serial = &data->serial; + text_layer_init_with_parameters(serial, + &GRect(0, window->layer.bounds.size.h - PBL_IF_RECT_ELSE(20, 40), + window->layer.bounds.size.w, 20), + data->serial_buffer, fonts_get_system_font(FONT_KEY_GOTHIC_14), + GColorBlack, GColorWhite, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->layer, &serial->layer); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd* mfg_info_qr_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 4f8a2d3e-1c5b-4a9f-8e7d-6c3b2a1f0e9d + .common.uuid = { 0x4f, 0x8a, 0x2d, 0x3e, 0x1c, 0x5b, 0x4a, 0x9f, + 0x8e, 0x7d, 0x6c, 0x3b, 0x2a, 0x1f, 0x0e, 0x9d }, + .name = "MfgInfoQR", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_info_qr.h b/src/fw/apps/prf/mfg_info_qr.h new file mode 100644 index 0000000000..e19df97210 --- /dev/null +++ b/src/fw/apps/prf/mfg_info_qr.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_info_qr_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_mag.c b/src/fw/apps/prf/mfg_mag.c new file mode 100644 index 0000000000..1abdbb897a --- /dev/null +++ b/src/fw/apps/prf/mfg_mag.c @@ -0,0 +1,249 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "applib/ui/text_layer.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/pbl_malloc.h" +#include "drivers/mag.h" +#include "drivers/rtc.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "pbl/services/evented_timer.h" +#include "system/logging.h" + +#include +#include +#include + +#define STATUS_STRING_LEN 200 + +// Minimum variation required on each axis (in mG) +#define MIN_VARIATION_MG 100 // Each axis must vary by at least this much + +#define TEST_DURATION_MS 5000 +#define RESULT_DISPLAY_MS 1000 +#define SAMPLE_INTERVAL_MS 100 + +typedef enum { + STATE_IDLE, + STATE_TESTING, + STATE_RESULT, +} TestState; + +static EventedTimerID s_timer; + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; + char status_string[STATUS_STRING_LEN]; + + TestState state; + RtcTicks state_start_time; + + // Track min/max per axis + int16_t min_x, max_x; + int16_t min_y, max_y; + int16_t min_z, max_z; + + uint32_t sample_count; + + // Variation per axis + int16_t variation_x; + int16_t variation_y; + int16_t variation_z; + + bool test_passed; +} AppData; + +static void prv_update_display(void *context) { + AppData *data = context; + + MagData sample; + MagReadStatus ret = mag_read_data(&sample); + + if (ret != MagReadSuccess) { + sniprintf(data->status_string, sizeof(data->status_string), "MAG ERROR:\n%d", ret); + text_layer_set_text(&data->status, data->status_string); + return; + } + + // Log raw magnetometer samples at debug level + PBL_LOG_DBG("Mag (mG): X:%" PRIi16 " Y:%" PRIi16 " Z:%" PRIi16, sample.x, sample.y, sample.z); + + uint32_t elapsed = (uint32_t)(rtc_get_ticks() - data->state_start_time); + + switch (data->state) { + case STATE_IDLE: { + sniprintf(data->status_string, sizeof(data->status_string), + "X: %" PRIi16 " mG\nY: %" PRIi16 " mG\nZ: %" PRIi16 " mG\n\nPress SEL", sample.x, + sample.y, sample.z); + break; + } + + case STATE_TESTING: { + // Track min/max for each axis + if (data->sample_count == 0) { + data->min_x = data->max_x = sample.x; + data->min_y = data->max_y = sample.y; + data->min_z = data->max_z = sample.z; + } else { + if (sample.x < data->min_x) data->min_x = sample.x; + if (sample.x > data->max_x) data->max_x = sample.x; + if (sample.y < data->min_y) data->min_y = sample.y; + if (sample.y > data->max_y) data->max_y = sample.y; + if (sample.z < data->min_z) data->min_z = sample.z; + if (sample.z > data->max_z) data->max_z = sample.z; + } + data->sample_count++; + + // Calculate current variations + int16_t curr_var_x = data->max_x - data->min_x; + int16_t curr_var_y = data->max_y - data->min_y; + int16_t curr_var_z = data->max_z - data->min_z; + + sniprintf(data->status_string, sizeof(data->status_string), + "Testing...\nRotate device\n\nX: %" PRId16 " mG\nY: %" PRId16 " mG\nZ: %" PRId16 + " mG\n\n%" PRIu32 " sec remaining", + curr_var_x, curr_var_y, curr_var_z, (TEST_DURATION_MS - elapsed) / 1000 + 1); + + if (elapsed >= TEST_DURATION_MS) { + // Store final variations + data->variation_x = curr_var_x; + data->variation_y = curr_var_y; + data->variation_z = curr_var_z; + + // Test passes if all axes vary by at least the minimum threshold + data->test_passed = + (data->variation_x >= MIN_VARIATION_MG && data->variation_y >= MIN_VARIATION_MG && + data->variation_z >= MIN_VARIATION_MG); + + mfg_test_result_report(MfgTestId_Mag, data->test_passed, 0); + + data->state = STATE_RESULT; + data->state_start_time = rtc_get_ticks(); + } + break; + } + + case STATE_RESULT: { + if (elapsed >= RESULT_DISPLAY_MS) { + app_window_stack_pop(false); + return; + } + sniprintf(data->status_string, sizeof(data->status_string), + "MAG: %s\n\nX: %" PRId16 " mG\nY: %" PRId16 " mG\nZ: %" PRId16 " mG", + data->test_passed ? "PASS" : "FAIL", data->variation_x, data->variation_y, + data->variation_z); + break; + } + } + + text_layer_set_text(&data->status, data->status_string); +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + AppData *data = context; + + switch (data->state) { + case STATE_IDLE: + // Start test + data->state = STATE_TESTING; + data->state_start_time = rtc_get_ticks(); + data->sample_count = 0; + break; + + default: + break; + } +} + +static void prv_click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData){}; + + app_state_set_user_data(data); + + // Initialize magnetometer + mag_start_sampling(); + mag_change_sample_rate(MagSampleRate20Hz); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "MAG TEST"); + layer_add_child(&window->layer, &title->layer); + + TextLayer *status = &data->status; + text_layer_init(status, + &GRect(5, 40, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 40)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + layer_add_child(&window->layer, &status->layer); + + window_set_click_config_provider_with_context(window, prv_click_config_provider, data); + + data->state = STATE_IDLE; + data->state_start_time = rtc_get_ticks(); + + app_window_stack_push(window, true /* Animated */); + + s_timer = + evented_timer_register(SAMPLE_INTERVAL_MS, true /* repeating */, prv_update_display, data); +} + +static void prv_handle_deinit(void) { + evented_timer_cancel(s_timer); + mag_release(); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); + + prv_handle_deinit(); +} + +const PebbleProcessMd *mfg_mag_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 3F4C8A2E-1B6D-4F9E-A3C5-7D8E9F0A1B2C + .common.uuid = + { + 0x3F, + 0x4C, + 0x8A, + 0x2E, + 0x1B, + 0x6D, + 0x4F, + 0x9E, + 0xA3, + 0xC5, + 0x7D, + 0x8E, + 0x9F, + 0x0A, + 0x1B, + 0x2C, + }, + .name = "MfgMag", + }; + return (const PebbleProcessMd *)&s_app_info; +} \ No newline at end of file diff --git a/src/fw/apps/prf/mfg_mag.h b/src/fw/apps/prf/mfg_mag.h new file mode 100644 index 0000000000..a6c8cf192a --- /dev/null +++ b/src/fw/apps/prf/mfg_mag.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_mag_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_menu.c b/src/fw/apps/prf/mfg_menu.c new file mode 100644 index 0000000000..6bf9d4aa3a --- /dev/null +++ b/src/fw/apps/prf/mfg_menu.c @@ -0,0 +1,253 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "applib/app.h" +#include "applib/battery_state_service.h" +#include "applib/ui/ui.h" +#include "applib/ui/window_private.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "apps/prf/mfg_adv.h" +#include "apps/prf/mfg_test_aging.h" +#include "apps/prf/mfg_info_qr.h" +#include "apps/prf/mfg_test_menu.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/standby.h" +#include "mfg/mfg_info.h" +#include "mfg/mfg_serials.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/bluetooth/pairability.h" +#include "system/bootbits.h" +#include "system/reset.h" +#include "util/size.h" + +#include + +// "XXXXXX - 100%" +#define DEVICE_INFO_SUBTITLE_SIZE (MFG_SERIAL_NUMBER_SIZE + 8) + +typedef struct { + Window *window; + SimpleMenuLayer *menu_layer; + SimpleMenuSection menu_section; + char device_info_subtitle[DEVICE_INFO_SUBTITLE_SIZE]; +} MfgMenuAppData; + +static uint16_t s_menu_position = 0; +static const PebbleProcessMd *s_last_test_menu = NULL; + +//! Callback to run from the kernel main task +static void prv_launch_app_cb(void *data) { + app_manager_launch_new_app(&(AppLaunchConfig) { .md = data }); +} + +static void prv_select_info_qr(int index, void *context) { + launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_info_qr_app_get_info()); +} + +static void prv_select_tests_sf(int index, void *context) { + s_last_test_menu = mfg_test_menu_semi_finished_app_get_info(); + launcher_task_add_callback(prv_launch_app_cb, (void*) s_last_test_menu); +} + +static void prv_select_tests_fi(int index, void *context) { + s_last_test_menu = mfg_test_menu_finished_app_get_info(); + launcher_task_add_callback(prv_launch_app_cb, (void*) s_last_test_menu); +} + +static void prv_select_aging(int index, void *context) { + launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_test_aging_app_get_info()); +} + +static void prv_select_ble_adv(int index, void *context) { + launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_adv_app_get_info()); +} + +static void prv_select_reset(int index, void *context) { + system_reset(); +} + +static void prv_select_shutdown(int index, void *context) { + enter_standby(RebootReasonCode_ShutdownMenuItem); +} + +#ifdef CONFIG_MFG +static void prv_load_prf_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool confirmed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + if (confirmed) { + boot_bit_set(BOOT_BIT_FORCE_PRF); + system_reset(); + } +} + +static void prv_load_prf_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_load_prf_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_load_prf_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_load_prf_confirmed); +} + +static void prv_select_load_prf(int index, void *context) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Load PRF"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Load PRF?\n\nThis action cannot be undone!"); + dialog_set_background_color(dialog, GColorOrange); + dialog_set_text_color(dialog, GColorWhite); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_load_prf_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_reset_results_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool confirmed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + if (confirmed) { + mfg_test_result_reset(); + } +} + +static void prv_reset_results_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_reset_results_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_reset_results_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_reset_results_confirmed); +} + +static void prv_select_reset_results(int index, void *context) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Reset Results"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Reset MFG results?\n\nThis action cannot be undone!"); + dialog_set_background_color(dialog, GColorOrange); + dialog_set_text_color(dialog, GColorWhite); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, + prv_reset_results_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} +#endif + +static void prv_update_device_info_subtitle(MfgMenuAppData *data, uint8_t charge_percent) { + char serial[MFG_SERIAL_NUMBER_SIZE + 1]; + mfg_info_get_serialnumber(serial, sizeof(serial)); + sniprintf(data->device_info_subtitle, sizeof(data->device_info_subtitle), + "%s - %"PRIu8"%%", serial, charge_percent); +} + +static void prv_battery_state_handler(BatteryChargeState charge) { + MfgMenuAppData *data = app_state_get_user_data(); + prv_update_device_info_subtitle(data, charge.charge_percent); + layer_mark_dirty(simple_menu_layer_get_layer(data->menu_layer)); +} + +//! @param[out] out_items +static size_t prv_create_menu_items(SimpleMenuItem** out_menu_items) { + + // Define a const blueprint on the stack. + const SimpleMenuItem s_menu_items[] = { + { .title = "Device Info", .callback = prv_select_info_qr }, + { .title = "Semi-finished Tests", .callback = prv_select_tests_sf }, + { .title = "Finished Tests", .callback = prv_select_tests_fi }, + { .title = "Aging Test", .callback = prv_select_aging }, + { .title = "BLE Advertising", .callback = prv_select_ble_adv }, + { .title = "Shutdown", .callback = prv_select_shutdown }, + { .title = "Reset", .callback = prv_select_reset }, +#ifdef CONFIG_MFG + { .title = "Reset Results", .callback = prv_select_reset_results }, + { .title = "Load PRF", .callback = prv_select_load_prf }, +#endif + }; + + // Copy it into the heap so we can modify it. + *out_menu_items = app_malloc(sizeof(s_menu_items)); + memcpy(*out_menu_items, s_menu_items, sizeof(s_menu_items)); + + size_t num_items = ARRAY_LENGTH(s_menu_items); + + return num_items; +} + +static void prv_window_load(Window *window) { + MfgMenuAppData *data = app_state_get_user_data(); + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + SimpleMenuItem* menu_items; + size_t num_items = prv_create_menu_items(&menu_items); + + // Set initial subtitle with serial and battery % + BatteryChargeState charge = battery_state_service_peek(); + prv_update_device_info_subtitle(data, charge.charge_percent); + menu_items[0].subtitle = data->device_info_subtitle; + + data->menu_section = (SimpleMenuSection) { + .num_items = num_items, + .items = menu_items + }; + + data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, NULL); + layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); + + // Set the menu layer back to it's previous highlight position + simple_menu_layer_set_selected_index(data->menu_layer, s_menu_position, false); + + battery_state_service_subscribe(prv_battery_state_handler); +} + +static void s_main(void) { + // If returning from a submenu item, relaunch the appropriate submenu + if (mfg_test_menu_should_relaunch() && s_last_test_menu) { + launcher_task_add_callback(prv_launch_app_cb, (void*) s_last_test_menu); + } + + bt_pairability_use(); + + MfgMenuAppData *data = app_malloc_check(sizeof(MfgMenuAppData)); + *data = (MfgMenuAppData){}; + + app_state_set_user_data(data); + + data->window = window_create(); + window_init(data->window, ""); + window_set_window_handlers(data->window, &(WindowHandlers) { + .load = prv_window_load, + }); + window_set_overrides_back_button(data->window, true); + window_set_fullscreen(data->window, true); + app_window_stack_push(data->window, true /*animated*/); + + app_event_loop(); + + bt_pairability_release(); + + s_menu_position = simple_menu_layer_get_selected_index(data->menu_layer); +} + +const PebbleProcessMd* mfg_menu_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: ddfdf403-664e-47dd-a620-b1a14ce2b59b + .common.uuid = { 0xdd, 0xfd, 0xf4, 0x03, 0x66, 0x4e, 0x47, 0xdd, + 0xa6, 0x20, 0xb1, 0xa1, 0x4c, 0xe2, 0xb5, 0x9b }, + .name = "MfgMenu", + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/prf/mfg_menu.h b/src/fw/apps/prf/mfg_menu.h new file mode 100644 index 0000000000..7d2b7d03e0 --- /dev/null +++ b/src/fw/apps/prf/mfg_menu.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_menu_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_mic_asterix.c b/src/fw/apps/prf/mfg_mic_asterix.c new file mode 100644 index 0000000000..55ab0bb6c0 --- /dev/null +++ b/src/fw/apps/prf/mfg_mic_asterix.c @@ -0,0 +1,415 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_sine_wave.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "board/board.h" +#include "drivers/i2c.h" +#include "drivers/flash.h" +#include "drivers/clocksource.h" +#include "flash_region/flash_region.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "util/size.h" + +#include +#include + +#include "hal/nrf_clock.h" +#include "nrfx_i2s.h" +#include "nrfx_pdm.h" + +#include "console/dbgserial.h" + +#define DUMP_RECORDING_DBGSERIAL 0 +#define PLAY_SINEWAVE 0 + +#define DA7212_PLL_STATUS 0x03 +#define DA7212_CIF_CTRL 0x1D +#define DA7212_DIG_ROUTING_DAI 0x21 +#define DA7212_SR 0x22 +#define DA7212_REFERENCES 0x23 +#define DA7212_PLL_FRAC_TOP 0x24 +#define DA7212_PLL_FRAC_BOT 0x25 +#define DA7212_PLL_INTEGER 0x26 +#define DA7212_PLL_CTRL 0x27 +#define DA7212_DAI_CLK_MODE 0x28 +#define DA7212_DAI_CTRL 0x29 +#define DA7212_DIG_ROUTING_DAC 0x2A +#define DA7212_DAC_FILTERS5 0x40 +#define DA7212_DAC_R_GAIN 0x46 +#define DA7212_LINE_GAIN 0x4A +#define DA7212_MIXOUT_R_SELECT 0x4C +#define DA7212_SYSTEM_MODES_OUTPUT 0x51 +#define DA7212_DAC_R_CTRL 0x6A +#define DA7212_LINE_CTRL 0x6D +#define DA7212_MIXOUT_R_CTRL 0x6F +#define DA7212_LDO_CTRL 0x90 +#define DA7212_GAIN_RAMP_CTRL 0x92 +#define DA7212_TONE_GEN_CFG1 0xB4 +#define DA7212_TONE_GEN_CYCLES 0xB6 +#define DA7212_TONE_GEN_ON_PER 0xBB +#define DA7212_SYSTEM_ACTIVE 0xFD + +#define RECORDING_MS 3000 +#define SAMPLE_RATE_HZ 16000 +#define SAMPLE_BITS 16 +#define CAPTURE_MS 100 +#define N_CHANNELS 2 +#define N_SAMPLES (N_CHANNELS * ((SAMPLE_RATE_HZ * CAPTURE_MS) / 1000)) +#define SAMPLE_SIZE_BYTES (SAMPLE_BITS / 8) +#define BLOCK_SIZE (N_SAMPLES * SAMPLE_SIZE_BYTES) + +#define FLASH_START FLASH_REGION_FIRMWARE_DEST_BEGIN +#define FLASH_END FLASH_REGION_FIRMWARE_DEST_END + +#if !PLAY_SINEWAVE +static int16_t s_buf[2][N_SAMPLES]; +static int16_t *s_buf_rd; +static int16_t *s_buf_wr; +static uint8_t s_buf_idx; +static SemaphoreHandle_t s_data_ready; +static SemaphoreHandle_t s_need_data; +#endif +static nrfx_i2s_buffers_t s_i2s_bufs; + +#if !PLAY_SINEWAVE +static const nrfx_pdm_t s_pdm = NRFX_PDM_INSTANCE(0); +static nrfx_pdm_config_t s_pdm_cfg = + NRFX_PDM_DEFAULT_CONFIG(NRF_GPIO_PIN_MAP(1, 0), NRF_GPIO_PIN_MAP(0, 24)); +#endif + +static const nrfx_i2s_t s_i2s = NRFX_I2S_INSTANCE(0); +static nrfx_i2s_config_t s_i2s_cfg = + NRFX_I2S_DEFAULT_CONFIG(NRF_GPIO_PIN_MAP(0, 12), NRF_GPIO_PIN_MAP(0, 7), NRF_GPIO_PIN_MAP(1, 9), + NRF_GPIO_PIN_MAP(0, 13), NRF_I2S_PIN_NOT_CONNECTED); + +typedef struct { + Window window; + + TextLayer title; +} AppData; + +static void da7212_register_write(uint8_t reg, uint8_t value) { + uint8_t data[2] = {reg, value}; + bool ret; + + i2c_use(I2C_DA7212); + PBL_LOG_DBG("Writing DA7212 register 0x%02x with value 0x%02x", reg, value); + + ret = i2c_write_block(I2C_DA7212, 2, data); + PBL_ASSERTN(ret); + + i2c_release(I2C_DA7212); +} + +static uint8_t da7212_register_read(uint8_t reg) { + uint8_t data; + bool ret; + + i2c_use(I2C_DA7212); + + ret = i2c_read_register(I2C_DA7212, reg, &data); + PBL_ASSERTN(ret); + + i2c_release(I2C_DA7212); + + return data; +} + +static void prv_codec_setup(void) { + // CIF_CTRL: soft reset + da7212_register_write(DA7212_CIF_CTRL, 0x80); + + psleep(10); + + // SYSTEM_ACTIVE: wake-up + da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x01); + + // REFERENCES: enable master bias + da7212_register_write(DA7212_REFERENCES, 0x08); + + psleep(30); + + // LDO_CTRL: enable LDO, 1.05V + da7212_register_write(DA7212_LDO_CTRL, 0x80); + + // PLL: MCLK=4MHz (so input divider=2), we need 12.288MHz System Clock for SR=16KHz (see table 34) + // VCO = System Clock * 8 = 98.304MHz + // Feedback divider = VCO * Input Divider / MCLK + // = 98.304MHz * 2 / 4MHz = 49.152 + // PLL_INTEGER = 49 (0x31) + // PLL_FRAC = 0.152 * 2^13 = 1245 (0x4dd) + // PLL_FRAC_TOP = 0x04 + // PLL_FRAC_BOT = 0xdd + da7212_register_write(DA7212_PLL_FRAC_TOP, 0x04); + da7212_register_write(DA7212_PLL_FRAC_BOT, 0xdd); + da7212_register_write(DA7212_PLL_INTEGER, 0x31); + + // PLL_CTRL: enable + SRM, input clock range 2-10MHz + da7212_register_write(DA7212_PLL_CTRL, 0xC0); + + // PLL: operate with a 2-5MHz MCLK (ref. DA7212 rev 3.6, 13.29) + da7212_register_write(0xF0, 0x8B); + da7212_register_write(0xF2, 0x03); + da7212_register_write(0xF0, 0x00); + + psleep(40); + + PBL_ASSERT(da7212_register_read(DA7212_PLL_STATUS) == 0x07, "DA7212 PLL not locked"); + + // GAIN_RAMP_CTRL: 1s + da7212_register_write(DA7212_GAIN_RAMP_CTRL, 0x02); + // SR: 16KHz + da7212_register_write(DA7212_SR, 0x05); + // DAI_CLK_MODE: slave + da7212_register_write(DA7212_DAI_CLK_MODE, 0x00); + // DAI_CTRL: enable, 16-bit + da7212_register_write(DA7212_DAI_CTRL, 0x80); + // DIG_ROUTING_DAI: DAI_R/L_SRC to DAI_R/L + da7212_register_write(DA7212_DIG_ROUTING_DAI, 0x32); + // DIG_ROUTING_DAC: DAC_R/L mono mix of R/L + da7212_register_write(DA7212_DIG_ROUTING_DAC, 0xba); + // DAC_R_GAIN: 0dB + da7212_register_write(DA7212_DAC_R_GAIN, 0x6f); + // DAC_R_CTRL: enable + da7212_register_write(DA7212_DAC_R_CTRL, 0x80); + // MIXOUT_R_SELECT: DAC_R + da7212_register_write(DA7212_MIXOUT_R_SELECT, 0x08); + // MIXOUT_R_CTRL: enable, softmix enable, amp enable + da7212_register_write(DA7212_MIXOUT_R_CTRL, 0x98); + // LINE_GAIN: 10dB + da7212_register_write(DA7212_LINE_GAIN, 0x3a); + // LINE_CTRL: enable + da7212_register_write(DA7212_LINE_CTRL, 0x80); +} + +static void prv_codec_standby(void) { + da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x00); +} + +static void prv_data_handler(nrfx_i2s_buffers_t const *p_released, uint32_t status) { +#if !PLAY_SINEWAVE + PBL_ASSERT(!(status == NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED && p_released == NULL), "I2S buffers re-used"); + + if (status == NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED) { + s_i2s_bufs.p_tx_buffer = (uint32_t *)s_buf[s_buf_idx]; + s_buf_idx = (s_buf_idx + 1) % 2; + nrfx_i2s_next_buffers_set(&s_i2s, &s_i2s_bufs); + } + + if (p_released != NULL && p_released->p_tx_buffer != NULL) { + BaseType_t woken; + s_buf_wr = (int16_t *)p_released->p_tx_buffer; + xSemaphoreGiveFromISR(s_need_data, &woken); + portYIELD_FROM_ISR(woken); + } +#endif +} + +#if !PLAY_SINEWAVE +static void prv_pdm_evt_handler(nrfx_pdm_evt_t const * p_evt) { + PBL_ASSERT(p_evt->error == NRFX_PDM_NO_ERROR, "PDM overflow"); + + if (p_evt->buffer_requested) { + nrfx_pdm_buffer_set(&s_pdm, s_buf[s_buf_idx], N_SAMPLES); + s_buf_idx = (s_buf_idx + 1) % 2; + } + + if (p_evt->buffer_released) { + BaseType_t woken; + s_buf_rd = p_evt->buffer_released; + xSemaphoreGiveFromISR(s_data_ready, &woken); + portYIELD_FROM_ISR(woken); + } +} + +static void prv_mic_capture(void) { + uint32_t flash_addr; + nrfx_err_t err; + + s_data_ready = xSemaphoreCreateBinary(); + + clocksource_hfxo_request(); + + s_pdm_cfg.mode = NRF_PDM_MODE_STEREO; + // Sample rate of 16KHz (1280KHz / 80 = 16KHz) + s_pdm_cfg.clock_freq = NRF_PDM_FREQ_1280K; + s_pdm_cfg.ratio = NRF_PDM_RATIO_80X; + s_pdm_cfg.gain_l = NRF_PDM_GAIN_MAXIMUM; + s_pdm_cfg.gain_r = NRF_PDM_GAIN_MAXIMUM; + + err = nrfx_pdm_init(&s_pdm, &s_pdm_cfg, prv_pdm_evt_handler); + PBL_ASSERTN(err == NRFX_SUCCESS); + + flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + + err = nrfx_pdm_start(&s_pdm); + PBL_ASSERTN(err == NRFX_SUCCESS); + + flash_addr = FLASH_START; + for (unsigned int i = 0U; i < RECORDING_MS / CAPTURE_MS; i++) { + xSemaphoreTake(s_data_ready, portMAX_DELAY); + + flash_write_bytes((uint8_t *)s_buf_rd, flash_addr, BLOCK_SIZE); + flash_addr += BLOCK_SIZE; + } + + nrfx_pdm_stop(&s_pdm); + nrfx_pdm_uninit(&s_pdm); + + clocksource_hfxo_release(); + + vSemaphoreDelete(s_data_ready); + +#if DUMP_RECORDING_DBGSERIAL + flash_addr = FLASH_START; + dbgserial_putstr("S"); + for (unsigned int i = 0U; i < RECORDING_MS / CAPTURE_MS; i++) { + flash_read_bytes((uint8_t *)s_buf_rd, flash_addr, BLOCK_SIZE); + flash_addr += BLOCK_SIZE; + + for (unsigned int j = 0U; j < N_SAMPLES; j++) { + char buf[8]; + dbgserial_putstr_fmt(buf, sizeof(buf), "%d", s_buf_rd[j]); + } + } + dbgserial_putstr("E"); +#endif // DUMP_RECORDING_DBGSERIAL +} +#endif + +#if !PLAY_SINEWAVE +static void prv_playback(void) { + uint32_t flash_addr; + nrfx_err_t err; + + s_need_data = xSemaphoreCreateBinary(); + + clocksource_hfxo_request(); + + // MCLK: 4MHz, sample rate: ~16KHz (4Mhz / 256 = 15625Hz) + s_i2s_cfg.mck_setup = NRF_I2S_MCK_32MDIV8; + s_i2s_cfg.ratio = NRF_I2S_RATIO_256X; + s_i2s_cfg.channels = NRF_I2S_CHANNELS_STEREO; + + err = nrfx_i2s_init(&s_i2s, &s_i2s_cfg, prv_data_handler); + PBL_ASSERTN(err == NRFX_SUCCESS); + + flash_addr = FLASH_START; + flash_read_bytes((uint8_t *)s_buf[0], flash_addr, BLOCK_SIZE); + flash_addr += BLOCK_SIZE; + + s_buf_idx = 1U; + s_buf_wr = s_buf[s_buf_idx]; + + s_i2s_bufs.p_tx_buffer = (uint32_t *)s_buf[0]; + s_i2s_bufs.buffer_size = BLOCK_SIZE / 4; + err = nrfx_i2s_start(&s_i2s, &s_i2s_bufs, 0); + PBL_ASSERTN(err == NRFX_SUCCESS); + + prv_codec_setup(); + + for (unsigned int i = 0U; i < (RECORDING_MS / CAPTURE_MS) - 1U; i++) { + flash_read_bytes((uint8_t *)s_buf_wr, flash_addr, BLOCK_SIZE); + flash_addr += BLOCK_SIZE; + + xSemaphoreTake(s_need_data, portMAX_DELAY); + } + + prv_codec_standby(); + + nrfx_i2s_stop(&s_i2s); + nrfx_i2s_uninit(&s_i2s); + + clocksource_hfxo_release(); + + vSemaphoreDelete(s_need_data); + + flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); +} +#else +static void prv_playback(void) { + nrfx_err_t err; + + clocksource_hfxo_request(); + + // MCLK: 4MHz, sample rate: ~16KHz (4Mhz / 256 = 15625Hz) + s_i2s_cfg.mck_setup = NRF_I2S_MCK_32MDIV8; + s_i2s_cfg.ratio = NRF_I2S_RATIO_256X; + s_i2s_cfg.channels = NRF_I2S_CHANNELS_STEREO; + + err = nrfx_i2s_init(&s_i2s, &s_i2s_cfg, prv_data_handler); + PBL_ASSERTN(err == NRFX_SUCCESS); + + s_i2s_bufs.p_tx_buffer = (uint32_t *)sine_wave; + s_i2s_bufs.buffer_size = SINE_WAVE_TOTAL_SAMPLES / 2; + err = nrfx_i2s_start(&s_i2s, &s_i2s_bufs, 0); + PBL_ASSERTN(err == NRFX_SUCCESS); + + prv_codec_setup(); + psleep(3000); + prv_codec_standby(); + + nrfx_i2s_stop(&s_i2s); + nrfx_i2s_uninit(&s_i2s); + + clocksource_hfxo_release(); +} +#endif + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { +#if !PLAY_SINEWAVE + prv_mic_capture(); +#endif + prv_playback(); + + app_window_stack_pop(true); +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "MICROPHONE TEST"); + layer_add_child(&window->layer, &title->layer); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd *mfg_mic_asterix_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 95ada1ce-04b3-46b0-8519-0b42260b5c39 + .common.uuid = {0x95, 0xad, 0xa1, 0xce, 0x04, 0xb3, 0x46, 0xb0, + 0x85, 0x19, 0x0b, 0x42, 0x26, 0x0b, 0x5c, 0x39}, + .name = "MfgMicAsterix", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_mic_asterix.h b/src/fw/apps/prf/mfg_mic_asterix.h new file mode 100644 index 0000000000..cc39658f3f --- /dev/null +++ b/src/fw/apps/prf/mfg_mic_asterix.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_mic_asterix_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_mic_getafix.c b/src/fw/apps/prf/mfg_mic_getafix.c new file mode 100644 index 0000000000..de6da5ef8e --- /dev/null +++ b/src/fw/apps/prf/mfg_mic_getafix.c @@ -0,0 +1,408 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_mic_getafix.h" + +#include +#include +#include + +#include "util/math.h" +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "board/board.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "drivers/mic.h" +#include "drivers/rtc.h" +#include "mfg/mfg_info.h" +#include "flash_region/flash_region.h" +#include "drivers/flash.h" + +// Kiss FFT from Speex library - must include config and os_support before kiss_fft headers +// Save and undefine ABS to avoid redefinition warning with Speex's arch.h +#ifdef ABS +#undef ABS +#endif +#include "config.h" +#include "os_support_custom.h" +#include "kiss_fftr.h" + +#define SAMPLE_RATE_HZ 16000 +#define RECORDING_DURATION_MS 1000 +#define SAMPLE_BITS 16 +#define FFT_SIZE 1024 +#define MAX_CHANNELS 2 +#define PCM_BUFFER_SIZE 1024 + +// Calculate total samples and flash requirements +#define N_SAMPLES (MAX_CHANNELS * ((SAMPLE_RATE_HZ * RECORDING_DURATION_MS) / 1000)) +#define SAMPLE_SIZE_BYTES (SAMPLE_BITS / 8) +#define BLOCK_SIZE (N_SAMPLES * SAMPLE_SIZE_BYTES) + +#define FLASH_START FLASH_REGION_FIRMWARE_DEST_BEGIN +#define FLASH_END (FLASH_REGION_FIRMWARE_DEST_BEGIN + \ + ROUND_TO_MOD_CEIL(BLOCK_SIZE, SUBSECTOR_SIZE_BYTES)) + +// Target frequency: 1 kHz ± 100 Hz +#define TARGET_FREQ_HZ 1000 +#define FREQ_TOLERANCE_HZ 100 + +// Minimum peak magnitude threshold (to reject noise) +#define MIN_PEAK_MAGNITUDE 1000 + +#define RESULT_DISPLAY_MS 1000 + +#define STATUS_STR_LEN 128 + +typedef enum { + TestState_Init, + TestState_Recording, + TestState_Analyzing, + TestState_Complete, + TestState_Failed +} TestState; + +typedef struct { + Window window; + TextLayer title; + TextLayer status; + TextLayer mic1_result; + TextLayer mic2_result; + char status_text[STATUS_STR_LEN]; + char mic1_text[STATUS_STR_LEN]; + char mic2_text[STATUS_STR_LEN]; + + int16_t pcm[PCM_BUFFER_SIZE]; + uint32_t flash_addr; + + TestState state; + uint32_t result_display_start; + bool mic1_passed; + bool mic2_passed; + int mic1_peak_freq; + int mic2_peak_freq; +} AppData; + +// FFT analysis function +static int prv_find_peak_frequency(int16_t *samples, size_t sample_count) { + if (sample_count < FFT_SIZE) { + PBL_LOG_WRN("Not enough samples for FFT: %zu", sample_count); + return -1; + } + + // Allocate FFT configuration + kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(FFT_SIZE, 0, NULL, NULL); + if (!fft_cfg) { + PBL_LOG_ERR("Failed to allocate FFT configuration"); + return -1; + } + + // Allocate frequency domain buffer (FFT_SIZE/2 + 1 complex points) + kiss_fft_cpx *freq_data = kernel_malloc_check((FFT_SIZE / 2 + 1) * sizeof(kiss_fft_cpx)); + + // Perform real FFT + kiss_fftr(fft_cfg, samples, freq_data); + + // Find peak magnitude and its bin index + int peak_bin = 0; + int64_t max_magnitude = 0; + + // Search from bin 1 to FFT_SIZE/2 (skip DC component at bin 0) + for (int i = 1; i <= FFT_SIZE / 2; i++) { + // Calculate magnitude squared (r^2 + i^2) + int32_t real = freq_data[i].r; + int32_t imag = freq_data[i].i; + int64_t magnitude = (real * real) + (imag * imag); + + if (magnitude > max_magnitude) { + max_magnitude = magnitude; + peak_bin = i; + } + } + + // Convert bin to frequency + // Frequency = (bin * sample_rate) / FFT_SIZE + int peak_freq = (peak_bin * SAMPLE_RATE_HZ) / FFT_SIZE; + + PBL_LOG_INFO("Peak found at bin %d, frequency %d Hz, magnitude %ld", + peak_bin, peak_freq, (long)max_magnitude); + + // Clean up + kernel_free(freq_data); + kiss_fftr_free(fft_cfg); + + // Check if magnitude is above threshold + if (max_magnitude < MIN_PEAK_MAGNITUDE) { + PBL_LOG_WRN("Peak magnitude too low: %ld", (long)max_magnitude); + return -1; + } + + return peak_freq; +} + +// Convert interleaved stereo (L/R/L/R) to non-interleaved (all L, then all R) +static void prv_interleaved_to_non_interleaved(int16_t *audio_data, size_t frame_count) { + if (audio_data == NULL || frame_count == 0) { + return; + } + + for (size_t i = 1; i < frame_count; i++) { + int16_t temp = audio_data[2 * i]; + for (size_t j = 2 * i; j > i; j--) { + audio_data[j] = audio_data[j - 1]; + } + audio_data[i] = temp; + } +} + +// Separate channels for dual microphone analysis from flash +static void prv_analyze_dual_mic(AppData *data) { + uint32_t num_channels = mic_get_channels(MIC); + + // Allocate temporary buffer for FFT analysis + int16_t *fft_buffer = kernel_malloc_check(FFT_SIZE * 2 * sizeof(int16_t)); + + while (data->flash_addr - FLASH_START < BLOCK_SIZE) { + // Read first portion of data from flash for FFT analysis + flash_read_bytes((uint8_t *)fft_buffer, data->flash_addr, FFT_SIZE * 2 * sizeof(int16_t)); + data->flash_addr += FFT_SIZE * 2 * sizeof(int16_t); + if (num_channels == 2) { + // Convert interleaved stereo to non-interleaved format + // After conversion: first half = MIC1, second half = MIC2 + prv_interleaved_to_non_interleaved(fft_buffer, FFT_SIZE); + + // Analyze each microphone from sequential blocks + data->mic1_peak_freq = prv_find_peak_frequency(fft_buffer, FFT_SIZE); + data->mic2_peak_freq = prv_find_peak_frequency(&fft_buffer[FFT_SIZE], FFT_SIZE); + } else { + // Single microphone - analyze full buffer + data->mic1_peak_freq = prv_find_peak_frequency(fft_buffer, FFT_SIZE); + data->mic2_peak_freq = -1; // No second mic + } + + // Check if peaks are around target frequency + data->mic1_passed = data->mic1_passed || ((data->mic1_peak_freq > 0) && + (abs(data->mic1_peak_freq - TARGET_FREQ_HZ) <= FREQ_TOLERANCE_HZ)); + + if (num_channels == 2) { + data->mic2_passed = data->mic2_passed || ((data->mic2_peak_freq > 0) && + (abs(data->mic2_peak_freq - TARGET_FREQ_HZ) <= FREQ_TOLERANCE_HZ)); + } else { + data->mic2_passed = true; // N/A for single mic + } + + // Update status text + if (data->mic1_peak_freq > 0) { + snprintf(data->mic1_text, STATUS_STR_LEN, "Mic 1: %d Hz %s", + TARGET_FREQ_HZ, data->mic1_passed ? "PASS" : "FAIL"); + } else { + snprintf(data->mic1_text, STATUS_STR_LEN, "Mic 1: No signal"); + } + + if (num_channels == 2) { + if (data->mic2_peak_freq > 0) { + snprintf(data->mic2_text, STATUS_STR_LEN, "Mic 2: %d Hz %s", + TARGET_FREQ_HZ, data->mic2_passed ? "PASS" : "FAIL"); + } else { + snprintf(data->mic2_text, STATUS_STR_LEN, "Mic 2: No signal"); + } + } else { + snprintf(data->mic2_text, STATUS_STR_LEN, "Mic 2: N/A"); + } + } + kernel_free(fft_buffer); +} + +// Microphone data callback - writes to flash +static void prv_mic_data_handler(int16_t *samples, size_t sample_count, void *context) { + AppData *data = app_state_get_user_data(); + + if (data->state != TestState_Recording) { + return; + } + + // Write samples to flash + if (data->flash_addr - FLASH_START + (sample_count * sizeof(int16_t)) > BLOCK_SIZE) { + // Recording complete + mic_stop(MIC); + data->flash_addr = FLASH_START; + data->state = TestState_Analyzing; + snprintf(data->status_text, STATUS_STR_LEN, "Analyzing..."); + + // Perform FFT analysis from flash + prv_analyze_dual_mic(data); + + bool passed = data->mic1_passed && data->mic2_passed; + mfg_test_result_report(MfgTestId_Mic, passed, 0); + + data->state = TestState_Complete; + snprintf(data->status_text, STATUS_STR_LEN, passed ? "PASS" : "FAIL"); + data->result_display_start = rtc_get_ticks(); + + return; + } + + // Write to flash + flash_write_bytes((uint8_t *)samples, data->flash_addr, sample_count * sizeof(int16_t)); + data->flash_addr += sample_count * sizeof(int16_t); + + // Update progress + uint32_t bytes_written = data->flash_addr - FLASH_START; + uint32_t progress = (bytes_written * 100) / BLOCK_SIZE; + snprintf(data->status_text, STATUS_STR_LEN, "Recording... %lu%%", (unsigned long)progress); +} + +// Start the test +static void prv_start_test(void) { + AppData *data = app_state_get_user_data(); + + data->state = TestState_Recording; + data->flash_addr = FLASH_START; + + // Erase flash region for recording + flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + + uint32_t num_channels = mic_get_channels(MIC); + + snprintf(data->status_text, STATUS_STR_LEN, "Recording..."); + + PBL_LOG_INFO("Starting microphone test (channels=%lu, block_size=%lu)", + (unsigned long)num_channels, (unsigned long)BLOCK_SIZE); + + mic_init(MIC); + mic_set_volume(MIC, 100); // maximum volume + + if (!mic_start(MIC, prv_mic_data_handler, NULL, data->pcm, PCM_BUFFER_SIZE)) { + PBL_LOG_ERR("Failed to start microphone"); + mfg_test_result_report(MfgTestId_Mic, false, 0); + data->state = TestState_Failed; + snprintf(data->status_text, STATUS_STR_LEN, "Mic start failed"); + data->result_display_start = rtc_get_ticks(); + } +} + +// Button click handler to start the test +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + if (app_data->state == TestState_Init) { + prv_start_test(); + } +} + +// Configure button click providers +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); +} + +// Timer callback to update UI +static void prv_timer_callback(void *cb_data) { + AppData *data = app_state_get_user_data(); + + // Mark layer dirty to trigger redraw + layer_mark_dirty(window_get_root_layer(&data->window)); + + // Auto-close after result display timeout + if (data->state == TestState_Complete || data->state == TestState_Failed) { + uint32_t elapsed = (uint32_t)(rtc_get_ticks() - data->result_display_start); + if (elapsed >= RESULT_DISPLAY_MS) { + app_window_stack_pop(false); + return; + } + } + + app_timer_register(100, prv_timer_callback, NULL); +} + +// Initialize the app +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + memset(data, 0, sizeof(AppData)); + + app_state_set_user_data(data); + + data->state = TestState_Init; + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + GRect bounds = window->layer.bounds; + + // Title + TextLayer *title = &data->title; + GRect title_frame = bounds; + title_frame.size.h = 30; + text_layer_init(title, &title_frame); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "Mic Input Test"); + layer_add_child(&window->layer, &title->layer); + + // Status text + TextLayer *status = &data->status; + GRect status_frame = bounds; + status_frame.origin.y = 35; + status_frame.size.h = 25; + text_layer_init(status, &status_frame); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + snprintf(data->status_text, STATUS_STR_LEN, "Press SEL to start"); + text_layer_set_text(status, data->status_text); + layer_add_child(&window->layer, &status->layer); + + // Mic 1 result + TextLayer *mic1 = &data->mic1_result; + GRect mic1_frame = bounds; + mic1_frame.origin.y = 65; + mic1_frame.size.h = 25; + text_layer_init(mic1, &mic1_frame); + text_layer_set_font(mic1, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_text_alignment(mic1, GTextAlignmentCenter); + snprintf(data->mic1_text, STATUS_STR_LEN, "Mic 1: Waiting..."); + text_layer_set_text(mic1, data->mic1_text); + layer_add_child(&window->layer, &mic1->layer); + + // Mic 2 result + TextLayer *mic2 = &data->mic2_result; + GRect mic2_frame = bounds; + mic2_frame.origin.y = 95; + mic2_frame.size.h = 25; + text_layer_init(mic2, &mic2_frame); + text_layer_set_font(mic2, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_text_alignment(mic2, GTextAlignmentCenter); + snprintf(data->mic2_text, STATUS_STR_LEN, "Mic 2: Waiting..."); + text_layer_set_text(mic2, data->mic2_text); + layer_add_child(&window->layer, &mic2->layer); + + app_window_stack_push(window, true); + + // Start UI update timer + app_timer_register(100, prv_timer_callback, NULL); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); + + // Cleanup + mic_stop(MIC); +} + +const PebbleProcessMd *mfg_mic_getafix_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 3e8f9a2c-1b4d-4f5e-9c6a-7d8e0f1a2b3c + .common.uuid = {0x3e, 0x8f, 0x9a, 0x2c, 0x1b, 0x4d, 0x4f, 0x5e, + 0x9c, 0x6a, 0x7d, 0x8e, 0x0f, 0x1a, 0x2b, 0x3c}, + .name = "MfgMicGetafix", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_mic_getafix.h b/src/fw/apps/prf/mfg_mic_getafix.h new file mode 100644 index 0000000000..1983cc5787 --- /dev/null +++ b/src/fw/apps/prf/mfg_mic_getafix.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +//! Get the process metadata for the MFG Microphone test app +//! This test records from both microphones, performs FFT analysis, +//! detects peak frequency around 1kHz, and reports PASS/FAIL +const PebbleProcessMd *mfg_mic_getafix_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_mic_obelix.c b/src/fw/apps/prf/mfg_mic_obelix.c new file mode 100644 index 0000000000..44f083751b --- /dev/null +++ b/src/fw/apps/prf/mfg_mic_obelix.c @@ -0,0 +1,258 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "board/board.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "drivers/audio.h" +#include "drivers/pmic/npm1300.h" +#include "flash_region/flash_region.h" +#include "drivers/flash.h" +#include "applib/ui/window_private.h" + +#define PCM_BUFFER_SIZE 1024 + +#define RECORDING_MS 5000 +#define SAMPLE_RATE_HZ 16000 +#define SAMPLE_BITS 16 +#define CAPTURE_MS 100 +#define N_CHANNELS 2 +#define N_SAMPLES (N_CHANNELS * ((SAMPLE_RATE_HZ * RECORDING_MS) / 1000)) +#define SAMPLE_SIZE_BYTES (SAMPLE_BITS / 8) +#define BLOCK_SIZE (N_SAMPLES * SAMPLE_SIZE_BYTES) + +#define FLASH_START FLASH_REGION_FIRMWARE_DEST_BEGIN +#define FLASH_END (FLASH_REGION_FIRMWARE_DEST_BEGIN + \ + ROUND_TO_MOD_CEIL(BLOCK_SIZE, SUBSECTOR_SIZE_BYTES)) + +#define PROCESS_STATUS_STR_LEN 64 + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; + char status_text[PROCESS_STATUS_STR_LEN]; + uint32_t flash_addr; + int16_t pcm[PCM_BUFFER_SIZE]; + bool in_testing; + bool audio_playing; + bool mic_recording; + bool show_result_pending; +} AppData; + +static void prv_interleaved_to_non_interleaved(int16_t *audio_data, size_t frame_count) { + if (audio_data == NULL || frame_count == 0) { + return; + } + + for (size_t i = 1; i < frame_count; i++) { + int16_t temp = audio_data[2 * i]; + for (size_t j = 2 * i; j > i; j--) { + audio_data[j] = audio_data[j - 1]; + } + audio_data[i] = temp; + } +} + +static void prv_audio_trans_handler(uint32_t *free_size) { + uint32_t available_size = *free_size; + AppData *app_data = app_state_get_user_data(); + static enum { + MIC1, MIC2, MIC_MAX, + } mic_id = MIC1; + + while (available_size > PCM_BUFFER_SIZE*sizeof(int16_t)) { + flash_read_bytes((uint8_t *)app_data->pcm, app_data->flash_addr, PCM_BUFFER_SIZE*sizeof(int16_t)); + app_data->flash_addr += PCM_BUFFER_SIZE*sizeof(int16_t); + prv_interleaved_to_non_interleaved(app_data->pcm, PCM_BUFFER_SIZE/2); + if (mic_id == MIC1) { + available_size = audio_write(AUDIO, (void*)app_data->pcm, PCM_BUFFER_SIZE); + } else if (mic_id == MIC2) { + available_size = audio_write(AUDIO, (void*)&app_data->pcm[PCM_BUFFER_SIZE/2], PCM_BUFFER_SIZE); + } + } + + if (app_data->flash_addr >= FLASH_START + BLOCK_SIZE) { + app_data->flash_addr = FLASH_START; + if(++mic_id >= MIC_MAX) { + audio_stop(AUDIO); + app_data->audio_playing = false; + mic_id = MIC1; + app_data->show_result_pending = true; + return; + } + + snprintf(app_data->status_text, PROCESS_STATUS_STR_LEN, "Playing MIC2"); + } +} + +static void prv_start_playback() { + AppData *app_data = app_state_get_user_data(); + + audio_set_volume(AUDIO, 100); + audio_start(AUDIO, prv_audio_trans_handler); + app_data->audio_playing = true; +} + +static void prv_mic_data_handler(int16_t *samples, size_t sample_count, void *context) { + AppData *app_data = app_state_get_user_data(); + + if (app_data->flash_addr - FLASH_START > BLOCK_SIZE) { + mic_stop(MIC); + app_data->mic_recording = false; + app_data->flash_addr = FLASH_START; + snprintf(app_data->status_text, PROCESS_STATUS_STR_LEN, "Playing MIC1"); + prv_start_playback(); + return; + } + flash_write_bytes((uint8_t *)samples, app_data->flash_addr, sample_count*sizeof(int16_t)); + app_data->flash_addr += sample_count*sizeof(int16_t); +} + +static void prv_recording_start(void) { + AppData *app_data = app_state_get_user_data(); + app_data->flash_addr = FLASH_START; + flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); + + // Disable back button during recording/playback + window_set_overrides_back_button(&app_data->window, true); + + snprintf(app_data->status_text, PROCESS_STATUS_STR_LEN, "Pls Speak"); + mic_init(MIC); + //set to maximum make it's more audiable in testing + mic_set_volume(MIC, 100); + mic_start(MIC, prv_mic_data_handler, NULL, app_data->pcm, PCM_BUFFER_SIZE); + app_data->mic_recording = true; +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + if (!app_data->in_testing) { + app_data->in_testing = true; + prv_recording_start(); + } +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); +} + +static void prv_result_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool passed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + mfg_test_result_report(MfgTestId_Mic, passed, 0); + app_window_stack_pop(false); +} + +static void prv_result_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_result_confirmed); +} + +static void prv_show_result_dialog(void) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Microphone Result"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Microphone OK?"); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_result_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_timer_callback(void *cb_data) { + AppData *data = app_state_get_user_data(); + + layer_mark_dirty(window_get_root_layer(&data->window)); + + if (data->show_result_pending) { + data->show_result_pending = false; + prv_show_result_dialog(); + return; + } + + app_timer_register(500, prv_timer_callback, NULL); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + Layer *window_layer = window_get_root_layer(window); + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "MICROPHONE TEST"); + layer_add_child(&window->layer, &title->layer); + + TextLayer *status = &data->status; + GRect bounds = window_layer->bounds; + bounds.origin.y += 80; + text_layer_init(status, &window->layer.bounds); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + snprintf(data->status_text, PROCESS_STATUS_STR_LEN, "Press Sel to start"); + text_layer_set_text(status, data->status_text); + layer_set_frame((Layer*)status, &bounds); + layer_add_child(&window->layer, &status->layer); + + app_window_stack_push(window, true /* Animated */); + app_timer_register(500, prv_timer_callback, NULL); + + // HACK(OBELIX): we need proper regulator API (with consumer current, etc.) + (void)NPM1300_OPS.dischg_limit_ma_set(NPM1300_DISCHG_LIMIT_MA_MAX); +} + +static void prv_handler_deinit(void) { + AppData *data = app_state_get_user_data(); + if (data->mic_recording) { + mic_stop(MIC); + } + if (data->audio_playing) { + audio_stop(AUDIO); + } + + // HACK(OBELIX): we need proper regulator API (with consumer current, etc.) + (void)NPM1300_OPS.dischg_limit_ma_set(NPM1300_CONFIG.dischg_limit_ma); +} + +static void s_main(void) { + prv_handle_init(); + app_event_loop(); + prv_handler_deinit(); +} + +const PebbleProcessMd *mfg_mic_obelix_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 6b064482-dc20-45a9-804c-5002746b49f9 + .common.uuid = {0x6b, 0x06, 0x44, 0x82, 0xdc, 0x20, 0x45, 0xa9, 0x80, 0x4c, 0x50, 0x02, 0x74, + 0x6b, 0x49, 0xf9}, + .name = "MfgMicObelix", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_mic_obelix.h b/src/fw/apps/prf/mfg_mic_obelix.h new file mode 100644 index 0000000000..e459d649e5 --- /dev/null +++ b/src/fw/apps/prf/mfg_mic_obelix.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_mic_obelix_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_program_color.c b/src/fw/apps/prf/mfg_program_color.c new file mode 100644 index 0000000000..056cf8d621 --- /dev/null +++ b/src/fw/apps/prf/mfg_program_color.c @@ -0,0 +1,395 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "util/trig.h" +#include "applib/app_watch_info.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "applib/ui/path_layer.h" +#include "applib/ui/text_layer.h" +#include "applib/graphics/graphics.h" +#include "apps/prf/mfg_test_result.h" +#include "drivers/display/display.h" +#include "kernel/pbl_malloc.h" +#include "mfg/mfg_info.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "util/bitset.h" +#include "util/size.h" + +#include +#include +#include + +#ifdef CONFIG_BOARD_FAMILY_ASTERIX +const char *const s_model = "P2D"; +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) +const char *const s_model = "PT2"; +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) +const char *const s_model = "PR2"; +#else +const char *const s_model = "Unknown"; +#endif + +typedef struct { + WatchInfoColor color; + const char *name; + const char *short_name; +} ColorTable; + +const ColorTable s_color_table[] = { +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + { + .color = WATCH_INFO_COLOR_COREDEVICES_P2D_BLACK, + .name = "BLACK", + .short_name = "BK", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_P2D_WHITE, + .name = "WHITE", + .short_name = "WH", + } +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + { + .color = WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_GREY, + .name = "BLACK/GREY", + .short_name = "BG", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_RED, + .name = "BLACK/RED", + .short_name = "BR", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_BLUE, + .name = "SILVER/BLUE", + .short_name = "SB", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_GREY, + .name = "SILVER/GREY", + .short_name = "SG", + }, +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) + { + .color = WATCH_INFO_COLOR_COREDEVICES_PR2_BLACK_20, + .name = "BLACK-20MM", + .short_name = "BK20", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_14, + .name = "SILVER-14MM", + .short_name = "SV14", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_20, + .name = "SILVER-20MM", + .short_name = "SV20", + }, + { + .color = WATCH_INFO_COLOR_COREDEVICES_PR2_GOLD_14, + .name = "GOLD-14MM", + .short_name = "GD14", + }, +#endif +}; + +typedef struct { + Window window; + + TextLayer title; + TextLayer color; + TextLayer status; + + PathLayer up_arrow; + PathLayer down_arrow; + +#ifdef PBL_COLOR + Layer color_preview; +#endif + + int selected_color_index; + char color_text[32]; // Buffer for "NAME (SN)" format +} AppData; + +#ifdef PBL_COLOR +// Helper function to convert WatchInfoColor to GColor for display +static void prv_get_display_colors(WatchInfoColor watch_color, GColor *color1, GColor *color2) { + // Default to black for unknown colors + *color1 = GColorBlack; + *color2 = GColorBlack; + + switch (watch_color) { + // Single color options (C2D) + case WATCH_INFO_COLOR_COREDEVICES_P2D_BLACK: + *color1 = GColorBlack; + *color2 = GColorBlack; + break; + case WATCH_INFO_COLOR_COREDEVICES_P2D_WHITE: + *color1 = GColorWhite; + *color2 = GColorWhite; + break; + + // Two color options (CT2) + case WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_GREY: + *color1 = GColorBlack; + *color2 = GColorLightGray; + break; + case WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_RED: + *color1 = GColorBlack; + *color2 = GColorRed; + break; + case WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_BLUE: + *color1 = GColorLightGray; // Silver approximation + *color2 = GColorBlue; + break; + case WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_GREY: + *color1 = GColorLightGray; // Silver approximation + *color2 = GColorDarkGray; + break; + + // PR2 + case WATCH_INFO_COLOR_COREDEVICES_PR2_BLACK_20: + *color1 = GColorBlack; + *color2 = GColorBlack; + break; + case WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_14: + *color1 = GColorLightGray; // Silver approximation + *color2 = GColorLightGray; + break; + case WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_20: + *color1 = GColorLightGray; // Silver approximation + *color2 = GColorLightGray; + break; + case WATCH_INFO_COLOR_COREDEVICES_PR2_GOLD_14: + *color1 = GColorYellow; // Gold approximation + *color2 = GColorYellow; + break; + + default: + break; + } +} + +static void prv_color_preview_update_proc(Layer *layer, GContext *ctx) { + AppData *app_data = app_state_get_user_data(); + + if (app_data->selected_color_index < 0 || + app_data->selected_color_index >= (int)ARRAY_LENGTH(s_color_table)) { + return; + } + + GRect bounds; + layer_get_bounds(layer, &bounds); + WatchInfoColor watch_color = s_color_table[app_data->selected_color_index].color; + + GColor color1, color2; + prv_get_display_colors(watch_color, &color1, &color2); + + // Draw a square split diagonally if two different colors + if (gcolor_equal(color1, color2)) { + // Single color - fill entire square + graphics_context_set_fill_color(ctx, color1); + graphics_fill_rect(ctx, &bounds); + } else { + // Two colors - split diagonally + // Top triangle (color1) + graphics_context_set_fill_color(ctx, color1); + for (int y = 0; y < bounds.size.h; y++) { + GRect top_rect = GRect(bounds.origin.x, bounds.origin.y + y, + bounds.size.w - y, 1); + graphics_fill_rect(ctx, &top_rect); + } + + // Bottom triangle (color2) + graphics_context_set_fill_color(ctx, color2); + for (int y = 0; y < bounds.size.h; y++) { + GRect bottom_rect = GRect(bounds.origin.x + bounds.size.w - y, + bounds.origin.y + y, y, 1); + graphics_fill_rect(ctx, &bottom_rect); + } + } + + // Draw border + graphics_context_set_stroke_color(ctx, GColorBlack); + graphics_draw_rect(ctx, &bounds); +} +#endif + +static void prv_up_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + + if (ARRAY_LENGTH(s_color_table) == 0) { + return; + } + + if (app_data->selected_color_index == 0) { + app_data->selected_color_index = ARRAY_LENGTH(s_color_table) - 1; + } else { + app_data->selected_color_index--; + } + snprintf(app_data->color_text, sizeof(app_data->color_text), "%s (%s)", + s_color_table[app_data->selected_color_index].name, + s_color_table[app_data->selected_color_index].short_name); + text_layer_set_text(&app_data->color, app_data->color_text); +#ifdef PBL_COLOR + layer_mark_dirty(&app_data->color_preview); +#endif +} + +static void prv_down_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + + if (ARRAY_LENGTH(s_color_table) == 0) { + return; + } + + if (app_data->selected_color_index == (int)ARRAY_LENGTH(s_color_table) - 1) { + app_data->selected_color_index = 0; + } else { + app_data->selected_color_index++; + } + snprintf(app_data->color_text, sizeof(app_data->color_text), "%s (%s)", + s_color_table[app_data->selected_color_index].name, + s_color_table[app_data->selected_color_index].short_name); + text_layer_set_text(&app_data->color, app_data->color_text); +#ifdef PBL_COLOR + layer_mark_dirty(&app_data->color_preview); +#endif +} + +static void prv_close_timer_callback(void *cb_data) { + app_window_stack_pop(false); +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { + AppData *app_data = app_state_get_user_data(); + char model[MFG_INFO_MODEL_STRING_LENGTH]; + + if (ARRAY_LENGTH(s_color_table) == 0 || app_data->selected_color_index == -1) { + return; + } + + snprintf(model, sizeof(model), "%s-%s", + s_model, s_color_table[app_data->selected_color_index].short_name); + + mfg_info_set_model(model); + mfg_info_set_watch_color(s_color_table[app_data->selected_color_index].color); + + mfg_test_result_report(MfgTestId_ProgramColor, true, + s_color_table[app_data->selected_color_index].color); + text_layer_set_text(&app_data->status, "PROGRAMMED!"); + app_timer_register(3000, prv_close_timer_callback, NULL); +} + +static void prv_config_provider(void *data) { + window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) { + .selected_color_index = -1, + }; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "PROGRAM COLOR"); + layer_add_child(&window->layer, &title->layer); + + // Create up arrow (pointing up) + static GPoint UP_ARROW_POINTS[] = {{0, 10}, {7, 0}, {14, 10}}; + static const GPathInfo UP_ARROW_PATH_INFO = { + .num_points = ARRAY_LENGTH(UP_ARROW_POINTS), + .points = UP_ARROW_POINTS + }; + PathLayer *up_arrow = &data->up_arrow; + path_layer_init(up_arrow, &UP_ARROW_PATH_INFO); + path_layer_set_fill_color(up_arrow, GColorBlack); + path_layer_set_stroke_color(up_arrow, GColorBlack); + layer_set_frame(&up_arrow->layer, + &GRect((window->layer.bounds.size.w / 2) - 7, 40, 14, 10)); + layer_add_child(&window->layer, &up_arrow->layer); + +#ifdef PBL_COLOR + // Create color preview square above the color text + Layer *color_preview = &data->color_preview; + const int preview_size = 40; + layer_init(color_preview, + &GRect((window->layer.bounds.size.w / 2) - (preview_size / 2), 55, + preview_size, preview_size)); + layer_set_update_proc(color_preview, prv_color_preview_update_proc); + layer_add_child(&window->layer, color_preview); +#endif + + TextLayer *color = &data->color; + text_layer_init(color, + &GRect(5, 100, + window->layer.bounds.size.w - 10, 28)); + text_layer_set_font(color, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(color, GTextAlignmentCenter); + if (ARRAY_LENGTH(s_color_table) > 0) { + data->selected_color_index = 0; + snprintf(data->color_text, sizeof(data->color_text), "%s (%s)", + s_color_table[0].name, s_color_table[0].short_name); + text_layer_set_text(color, data->color_text); + } else { + text_layer_set_text(color, "NO COLORS AVAILABLE"); + } + layer_add_child(&window->layer, &color->layer); + + // Create down arrow (pointing down) + static GPoint DOWN_ARROW_POINTS[] = {{0, 0}, {7, 10}, {14, 0}}; + static const GPathInfo DOWN_ARROW_PATH_INFO = { + .num_points = ARRAY_LENGTH(DOWN_ARROW_POINTS), + .points = DOWN_ARROW_POINTS + }; + PathLayer *down_arrow = &data->down_arrow; + path_layer_init(down_arrow, &DOWN_ARROW_PATH_INFO); + path_layer_set_fill_color(down_arrow, GColorBlack); + path_layer_set_stroke_color(down_arrow, GColorBlack); + layer_set_frame(&down_arrow->layer, + &GRect((window->layer.bounds.size.w / 2) - 7, 133, 14, 10)); + layer_add_child(&window->layer, &down_arrow->layer); + + TextLayer *status = &data->status; + text_layer_init(status, + &GRect(5, 148, + window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 148)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + layer_add_child(&window->layer, &status->layer); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd* mfg_program_color_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: d5f0a47d-e570-499d-bcaa-fc6d56230038 + .common.uuid = { 0xd5, 0xf0, 0xa4, 0x7d, 0xe5, 0x70, 0x49, 0x9d, + 0xbc, 0xaa, 0xfc, 0x6d, 0x56, 0x23, 0x00, 0x38 }, + .name = "MfgProgramColor", + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/prf/mfg_program_color.h b/src/fw/apps/prf/mfg_program_color.h new file mode 100644 index 0000000000..7e2da6697e --- /dev/null +++ b/src/fw/apps/prf/mfg_program_color.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_program_color_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_qr_results.c b/src/fw/apps/prf/mfg_qr_results.c new file mode 100644 index 0000000000..e0f3f9a5d4 --- /dev/null +++ b/src/fw/apps/prf/mfg_qr_results.c @@ -0,0 +1,194 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/app_watch_info.h" +#include "applib/battery_state_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/qr_code.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_menu.h" +#include "apps/prf/mfg_test_result.h" +#include "bluetooth/bluetooth_types.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "process_management/pebble_process_md.h" +#include "mfg/mfg_serials.h" +#include "services/bluetooth/local_id.h" +#include "system/version.h" + +#include +#include + +static const char *prv_color_short_name(WatchInfoColor color) { + switch (color) { +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + case WATCH_INFO_COLOR_COREDEVICES_P2D_BLACK: return "BK"; + case WATCH_INFO_COLOR_COREDEVICES_P2D_WHITE: return "WH"; +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + case WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_GREY: return "BG"; + case WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_RED: return "BR"; + case WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_BLUE: return "SB"; + case WATCH_INFO_COLOR_COREDEVICES_PT2_SILVER_GREY: return "SG"; +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) + case WATCH_INFO_COLOR_COREDEVICES_PR2_BLACK_20: return "BK20"; + case WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_14: return "SV14"; + case WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_20: return "SV20"; + case WATCH_INFO_COLOR_COREDEVICES_PR2_GOLD_14: return "GD14"; +#endif + default: return "??"; + } +} + +typedef struct { + Window window; + QRCode qr_code; + char qr_buffer[256]; +} AppData; + +static void prv_append_result(char *buf, size_t bufsz, MfgTestId test) { + // Aging is launched from the top-level mfg menu, not from the per-mode + // test menu, so it isn't registered in the menu's availability table. + if (test != MfgTestId_Aging && !mfg_test_menu_is_test_available(test)) { + return; + } + + const MfgTestResult *r = mfg_test_result_get(test); + if (!r) { + return; + } + + size_t pos = strlen(buf); + if (pos > 0 && pos < bufsz - 1) { + buf[pos++] = ';'; + buf[pos] = '\0'; + } + + char entry[16]; + char rc; + if (r->ran) { + rc = r->passed ? 'P' : 'F'; + } else { + rc = 'N'; + } + + switch (test) { + case MfgTestId_Buttons: + snprintf(entry, sizeof(entry), "BTN:%c", rc); + break; + case MfgTestId_Display: + snprintf(entry, sizeof(entry), "DSP:%c", rc); + break; +#ifdef CONFIG_TOUCH + case MfgTestId_Touch: + snprintf(entry, sizeof(entry), "TCH:%c", rc); + break; +#endif + case MfgTestId_Backlight: + snprintf(entry, sizeof(entry), "BKL:%c", rc); + break; + case MfgTestId_Accel: + snprintf(entry, sizeof(entry), "ACC:%c", rc); + break; +#ifdef CONFIG_MAG + case MfgTestId_Mag: + snprintf(entry, sizeof(entry), "MAG:%c", rc); + break; +#endif +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) + case MfgTestId_Speaker: + snprintf(entry, sizeof(entry), "SPK:%c", rc); + break; +#endif +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + case MfgTestId_Mic: + snprintf(entry, sizeof(entry), "MIC:%c", rc); + break; +#endif + case MfgTestId_ALS: + snprintf(entry, sizeof(entry), "ALS:%c,%lu", rc, (unsigned long)r->value); + break; + case MfgTestId_Vibration: + snprintf(entry, sizeof(entry), "VIB:%c", rc); + break; +#if defined(CONFIG_BOARD_FAMILY_OBELIX) && defined(CONFIG_MFG) + case MfgTestId_HrmCtrLeakage: + snprintf(entry, sizeof(entry), "HRM:%c", rc); + break; +#endif + case MfgTestId_Charge: + snprintf(entry, sizeof(entry), "CHG:%c", rc); + break; + case MfgTestId_ProgramColor: + snprintf(entry, sizeof(entry), "CLR:%c,%s", rc, + prv_color_short_name((WatchInfoColor)r->value)); + break; + case MfgTestId_Aging: + snprintf(entry, sizeof(entry), "AGE:%c", rc); + break; + default: + return; + } + + strncat(buf, entry, bufsz - strlen(buf) - 1); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + // Start with serial number, Bluetooth MAC address, firmware version, + // and current battery percentage. + char mac[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]; + bt_local_id_copy_address_mac_string(mac); + BatteryChargeState charge = battery_state_service_peek(); + snprintf(data->qr_buffer, sizeof(data->qr_buffer), "%s;%s;%s;%" PRIu8, + mfg_get_serial_number(), mac, TINTIN_METADATA.version_tag, + charge.charge_percent); + + for (MfgTestId id = 0; id < MfgTestIdCount; id++) { + prv_append_result(data->qr_buffer, sizeof(data->qr_buffer), id); + } + + QRCode *qr_code = &data->qr_code; +#if PBL_ROUND +#define QR_CODE_SIZE ((window->layer.bounds.size.w * 10) / 14) + qr_code_init_with_parameters(qr_code, + &GRect((window->layer.bounds.size.w - QR_CODE_SIZE) / 2, + (window->layer.bounds.size.h - QR_CODE_SIZE) / 2, + QR_CODE_SIZE, QR_CODE_SIZE), + data->qr_buffer, strlen(data->qr_buffer), QRCodeECCMedium, + GColorBlack, GColorWhite); +#else + qr_code_init_with_parameters(qr_code, + &GRect(10, 10, window->layer.bounds.size.w - 20, + window->layer.bounds.size.h - 20), + data->qr_buffer, strlen(data->qr_buffer), QRCodeECCMedium, + GColorBlack, GColorWhite); +#endif + layer_add_child(&window->layer, &qr_code->layer); + + app_window_stack_push(window, true); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd* mfg_qr_results_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 7b3e9f2a-5d1c-4e8b-a6f0-3c9d8e7a1b5f + .common.uuid = { 0x7b, 0x3e, 0x9f, 0x2a, 0x5d, 0x1c, 0x4e, 0x8b, + 0xa6, 0xf0, 0x3c, 0x9d, 0x8e, 0x7a, 0x1b, 0x5f }, + .name = "MfgQRResults", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_qr_results.h b/src/fw/apps/prf/mfg_qr_results.h new file mode 100644 index 0000000000..1dd7bc3159 --- /dev/null +++ b/src/fw/apps/prf/mfg_qr_results.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_qr_results_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_sine_wave.c b/src/fw/apps/prf/mfg_sine_wave.c new file mode 100644 index 0000000000..a20f61dd64 --- /dev/null +++ b/src/fw/apps/prf/mfg_sine_wave.c @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_sine_wave.h" + +/* Stereo sine wave data (L, R, L, R, ...) */ +int16_t sine_wave[SINE_WAVE_TOTAL_SAMPLES] = { + 0, 0, 3134, 3134, 5791, 5791, 7567, 7567, 8191, 8191, 7567, 7567, 5791, 5791, 3134, 3134, + 0, 0, -3134, -3134, -5791, -5791, -7567, -7567, -8191, -8191, -7567, -7567, -5791, -5791, -3134, -3134 +}; diff --git a/src/fw/apps/prf/mfg_sine_wave.h b/src/fw/apps/prf/mfg_sine_wave.h new file mode 100644 index 0000000000..1f091d8cb6 --- /dev/null +++ b/src/fw/apps/prf/mfg_sine_wave.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#define SINE_WAVE_SAMPLE_RATE 16000 +#define SINE_WAVE_FREQUENCY 1000 +#define SINE_WAVE_SAMPLES_PER_PERIOD 16 +#define SINE_WAVE_TOTAL_SAMPLES 32 + +/* Stereo sine wave data (L, R, L, R, ...) */ +extern int16_t sine_wave[SINE_WAVE_TOTAL_SAMPLES]; diff --git a/src/fw/apps/prf/mfg_speaker_asterix.c b/src/fw/apps/prf/mfg_speaker_asterix.c new file mode 100644 index 0000000000..5af9365054 --- /dev/null +++ b/src/fw/apps/prf/mfg_speaker_asterix.c @@ -0,0 +1,180 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "board/board.h" +#include "drivers/i2c.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "util/size.h" + +#define DA7212_CIF_CTRL 0x1D +#define DA7212_DAI_CLK_MODE 0x28 +#define DA7212_DAI_CTRL 0x29 +#define DA7212_DIG_ROUTING_DAC 0x2A +#define DA7212_DAC_FILTERS5 0x40 +#define DA7212_DAC_R_GAIN 0x46 +#define DA7212_LINE_GAIN 0x4A +#define DA7212_MIXOUT_R_SELECT 0x4C +#define DA7212_SYSTEM_MODES_OUTPUT 0x51 +#define DA7212_DAC_R_CTRL 0x6A +#define DA7212_LINE_CTRL 0x6D +#define DA7212_MIXOUT_R_CTRL 0x6F +#define DA7212_TONE_GEN_CFG1 0xB4 +#define DA7212_TONE_GEN_CYCLES 0xB6 +#define DA7212_TONE_GEN_ON_PER 0xBB +#define DA7212_SYSTEM_ACTIVE 0xFD + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; +} AppData; + +static void da7212_register_write(uint8_t reg, uint8_t value) { + uint8_t data[2] = {reg, value}; + bool ret; + + i2c_use(I2C_DA7212); + + ret = i2c_write_block(I2C_DA7212, 2, data); + PBL_ASSERTN(ret); + + i2c_release(I2C_DA7212); +} + +static void prv_da7212_play_tone(void) { + // CIF_CTRL: soft reset + da7212_register_write(DA7212_CIF_CTRL, 0x80); + + psleep(10); + + // SYSTEM_ACTIVE: wake-up + da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x01); + + // Soft mute + da7212_register_write(DA7212_DAC_FILTERS5, 0x80); + + // DAI: enable (16-bit) + da7212_register_write(DA7212_DAI_CTRL, 0x80); + // DAI: enable master mode, BCLK=64 + da7212_register_write(DA7212_DAI_CLK_MODE, 0x81); + + // DAC_R/L source: DAI_R/L + da7212_register_write(DA7212_DIG_ROUTING_DAC, 0x32); + + // DAC_R: 0dB gain + da7212_register_write(DA7212_DAC_R_GAIN, 0x6f); + // DAC_R: enable + da7212_register_write(DA7212_DAC_R_CTRL, 0x80); + + // MIXOUT_R: input from DAC_R + da7212_register_write(DA7212_MIXOUT_R_SELECT, 0x08); + // MIXOUT_R: enable + amplifier + da7212_register_write(DA7212_MIXOUT_R_CTRL, 0x88); + + // Line: 0dB gain + da7212_register_write(DA7212_LINE_GAIN, 0x30); + // Line: enable amplifier, gain ramping, drive output + da7212_register_write(DA7212_LINE_CTRL, 0xa8); + + // Tone generator: infinite cycles + da7212_register_write(DA7212_TONE_GEN_CYCLES, 0x07); + // Tone generator: 200ms pulse + da7212_register_write(DA7212_TONE_GEN_ON_PER, 0x14); + // Tone generator: start + da7212_register_write(DA7212_TONE_GEN_CFG1, 0x80); + + // System modes output: enable DAC_R, Line + da7212_register_write(DA7212_SYSTEM_MODES_OUTPUT, 0x89); + + // Soft mute off + da7212_register_write(DA7212_DAC_FILTERS5, 0x00); +} + +static void prv_da7212_idle(void) { + da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x00); +} + +static void prv_result_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool passed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + mfg_test_result_report(MfgTestId_Speaker, passed, 0); + app_window_stack_pop(false); +} + +static void prv_result_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_result_confirmed); +} + +static void prv_show_result_dialog(void) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Speaker Result"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Speaker OK?"); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_result_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_timer_callback(void *cb_data) { + prv_da7212_idle(); + prv_show_result_dialog(); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "SPEAKER TEST"); + layer_add_child(&window->layer, &title->layer); + + app_window_stack_push(window, true /* Animated */); + + prv_da7212_play_tone(); + + app_timer_register(5000, prv_timer_callback, NULL); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd *mfg_speaker_asterix_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 27047635-68f1-4ece-9ca7-52dd8e22d1dd + .common.uuid = {0x27, 0x04, 0x76, 0x35, 0x68, 0xf1, 0x4e, 0xce, 0x9c, 0xa7, 0x52, 0xdd, 0x8e, + 0x22, 0xd1, 0xdd}, + .name = "MfgSpeakerAsterix", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_speaker_asterix.h b/src/fw/apps/prf/mfg_speaker_asterix.h new file mode 100644 index 0000000000..9770b00f29 --- /dev/null +++ b/src/fw/apps/prf/mfg_speaker_asterix.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_speaker_asterix_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_speaker_obelix.c b/src/fw/apps/prf/mfg_speaker_obelix.c new file mode 100644 index 0000000000..2a6a0b98c4 --- /dev/null +++ b/src/fw/apps/prf/mfg_speaker_obelix.c @@ -0,0 +1,115 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/pbl_malloc.h" +#include "board/board.h" +#include "drivers/pmic/npm1300.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "drivers/audio.h" + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; +} AppData; + +static const int16_t sine_wave_4k[] = { + 0, 32767, 0, -32768, 0, 32767, 0, -32768, + 0, 32767, 0, -32768, 0, 32767, 0, -32768, +}; + +static void prv_audio_trans_handler(uint32_t *free_size) { + uint32_t available_size = *free_size; + while (available_size > sizeof(sine_wave_4k)) { + available_size = audio_write(AUDIO, (void*)&sine_wave_4k[0], sizeof(sine_wave_4k)); + } +} + +static void prv_play_audio(void) { + audio_start(AUDIO, prv_audio_trans_handler); + audio_set_volume(AUDIO, 100); +} + +static void prv_result_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool passed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + mfg_test_result_report(MfgTestId_Speaker, passed, 0); + app_window_stack_pop(false); +} + +static void prv_result_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_result_confirmed); +} + +static void prv_show_result_dialog(void) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Speaker Result"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Speaker OK?"); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_result_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_timer_callback(void *cb_data) { + audio_stop(AUDIO); + prv_show_result_dialog(); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "SPEAKER TEST"); + layer_add_child(&window->layer, &title->layer); + + app_window_stack_push(window, true /* Animated */); +} + +static void s_main(void) { + // HACK(OBELIX): we need proper regulator API (with consumer current, etc.) + (void)NPM1300_OPS.dischg_limit_ma_set(NPM1300_DISCHG_LIMIT_MA_MAX); + prv_handle_init(); + prv_play_audio(); + app_timer_register(5000, prv_timer_callback, NULL); + app_event_loop(); + audio_stop(AUDIO); + // HACK(OBELIX): we need proper regulator API (with consumer current, etc.) + (void)NPM1300_OPS.dischg_limit_ma_set(NPM1300_CONFIG.dischg_limit_ma); +} + +const PebbleProcessMd *mfg_speaker_obelix_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: c1479d03-5550-4444-b1e7-e2cbad0e5678 + .common.uuid = {0xc1, 0x47, 0x9d, 0x03, 0x55, 0x50, 0x44, 0x44, 0xb1, 0xe7, 0xe2, 0xcb, 0xad, + 0x0e, 0x56, 0x78}, + .name = "MfgSpeakerObelix", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_speaker_obelix.h b/src/fw/apps/prf/mfg_speaker_obelix.h new file mode 100644 index 0000000000..bc31f73d74 --- /dev/null +++ b/src/fw/apps/prf/mfg_speaker_obelix.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_speaker_obelix_app_get_info(void); + diff --git a/src/fw/apps/prf/mfg_test_aging.c b/src/fw/apps/prf/mfg_test_aging.c new file mode 100644 index 0000000000..2aaf345156 --- /dev/null +++ b/src/fw/apps/prf/mfg_test_aging.c @@ -0,0 +1,672 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg_test_aging.h" + +#include "apps/prf/mfg_test_result.h" +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/vibes.h" +#include "applib/ui/window.h" +#include "board/board.h" +#include "console/console_internal.h" +#include "drivers/accel.h" +#include "drivers/ambient_light.h" +#include "drivers/audio.h" +#include "drivers/battery.h" +#include "drivers/backlight.h" +#include "drivers/mag.h" +#include "drivers/vibe.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/bluetooth/bluetooth_ctl.h" +#include "pbl/services/light.h" +#include "pbl/services/idle_watchdog.h" +#include "system/logging.h" + +#include + +#define STATUS_STRING_LEN 200 +#define COMPONENT_TEST_DURATION_SEC 10 +#define COMPONENT_BACKLIGHT_DURATION_SEC 2 +#define COMPONENT_VIBE_DURATION_SEC 1 + +// Charge + cycling phase parameters +#define CHARGE_AND_CYCLE_DURATION_SEC (4 * 3600) // 4 hours total +#define CHARGE_TIMEOUT_SEC (90 * 60) // Charge must reach target within 90min +#define CHARGE_TARGET_PERCENT 100 +#define CHARGE_HOLD_MIN_PERCENT 99 // Tolerance for ADC noise after 100% reached +#define TEMP_MIN_MC 15000 // 15.0C +#define TEMP_MAX_MC 35000 // 35.0C + +// Idle phase parameters +#define IDLE_DURATION_SEC (10 * 3600) // 10 hours +#define IDLE_MAX_DROP_PERCENT 6 // Fail if battery drops more than this during idle + +// Discharge phase parameters — bring battery down to a safe shipping level. +// Adjust here if the target ever changes. +#define DISCHARGE_TARGET_PERCENT 65 + +#ifdef CONFIG_SPEAKER +static const int16_t sine_wave_4k[] = { + 0, 32767, 0, -32768, 0, 32767, 0, -32768, + 0, 32767, 0, -32768, 0, 32767, 0, -32768, +}; +#endif + +typedef enum { + AgingStateWaitPlug = 0, + AgingStateChargingAndCycling, + AgingStateWaitUnplug, + AgingStateIdle, + AgingStateDischarging, + AgingStatePass, + AgingStateFail, +} AgingState; + +// Component sub-states for cycling +typedef enum { + ComponentAccel = 0, +#ifdef CONFIG_MAG + ComponentMag, +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + ComponentBacklightWhite, + ComponentBacklightRed, + ComponentBacklightGreen, + ComponentBacklightBlue, +#else + ComponentBacklight, +#endif +#ifdef CONFIG_SPEAKER + ComponentAudio, +#endif + ComponentALS, + ComponentVibe, + NumComponents, +} ComponentState; + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; + char status_string[STATUS_STRING_LEN]; + + AgingState state; + ComponentState component; + uint32_t component_elapsed_sec; + + uint32_t phase_elapsed_sec; + uint32_t cycle_count; + + // Set once battery first reaches CHARGE_TARGET_PERCENT during charge+cycle phase + bool charge_complete; + + // Battery state at start of idle phase, used for the 6% drop check + uint8_t initial_percent; + + char fail_reason[64]; + +#ifdef CONFIG_SPEAKER + bool audio_playing; +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + uint32_t saved_backlight_color; +#endif +} AppData; + +#ifdef CONFIG_SPEAKER +static void prv_audio_trans_handler(uint32_t *free_size) { + uint32_t available_size = *free_size; + while (available_size > sizeof(sine_wave_4k)) { + available_size = audio_write(AUDIO, (void *)&sine_wave_4k[0], sizeof(sine_wave_4k)); + } +} +#endif + +static void prv_format_time(char *buffer, size_t size, uint32_t seconds) { + uint32_t hours = seconds / 3600; + uint32_t minutes = (seconds % 3600) / 60; + uint32_t secs = seconds % 60; + sniprintf(buffer, size, "%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, hours, minutes, secs); +} + +static void prv_cleanup_component(AppData *data) { +#ifdef CONFIG_MAG + if (data->component == ComponentMag) { + mag_release(); + } +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + if (data->component >= ComponentBacklightWhite && + data->component <= ComponentBacklightBlue) { + backlight_set_color(data->saved_backlight_color); + light_enable(false); + } +#else + if (data->component == ComponentBacklight) { + light_enable(false); + } +#endif +#ifdef CONFIG_SPEAKER + if (data->audio_playing) { + audio_stop(AUDIO); + data->audio_playing = false; + } +#endif +} + +static void prv_start_component(AppData *data) { +#ifdef CONFIG_MAG + if (data->component == ComponentMag) { + mag_start_sampling(); + } +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + if (data->component >= ComponentBacklightWhite && + data->component <= ComponentBacklightBlue) { + light_enable(true); + } +#else + if (data->component == ComponentBacklight) { + light_enable(true); + } +#endif +} + +static uint32_t prv_component_duration(ComponentState comp) { + switch (comp) { +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + case ComponentBacklightWhite: + case ComponentBacklightRed: + case ComponentBacklightGreen: + case ComponentBacklightBlue: +#else + case ComponentBacklight: +#endif + return COMPONENT_BACKLIGHT_DURATION_SEC; + case ComponentVibe: + return COMPONENT_VIBE_DURATION_SEC; + default: + return COMPONENT_TEST_DURATION_SEC; + } +} + +static void prv_advance_component(AppData *data) { + prv_cleanup_component(data); + + data->component_elapsed_sec = 0; + data->component++; + + if (data->component >= NumComponents) { + data->component = ComponentAccel; + data->cycle_count++; + } + + prv_start_component(data); +} + +static void prv_report_result(bool passed) { + // Always report into the FINISHED bucket: aging is launched outside the + // per-mode test menu, so we can't rely on whatever mode happened to be + // active last. + mfg_test_result_set_mode(MFG_TEST_MODE_FINISHED); + mfg_test_result_report(MfgTestId_Aging, passed, 0); +} + +static void prv_enter_fail(AppData *data, const char *reason) { + prv_cleanup_component(data); + // Restore charging in case we disabled it during the charge+cycle phase, + // so the device is left in a normal state on exit. + battery_set_charge_enable(true); + data->state = AgingStateFail; + sniprintf(data->fail_reason, sizeof(data->fail_reason), "%s", reason); + PBL_LOG_ERR("Aging test FAIL: %s", reason); + prv_report_result(false); +} + +static void prv_run_component_display(AppData *data) { + char time_str[16]; + prv_format_time(time_str, sizeof(time_str), data->phase_elapsed_sec); + + uint32_t remaining = CHARGE_AND_CYCLE_DURATION_SEC - data->phase_elapsed_sec; + char rem_str[16]; + prv_format_time(rem_str, sizeof(rem_str), remaining); + + const char *comp_name = "?"; + char comp_detail[48] = ""; + switch (data->component) { + case ComponentAccel: { + AccelDriverSample sample; + accel_peek(&sample); + comp_name = "Accel"; + sniprintf(comp_detail, sizeof(comp_detail), + "X:%" PRIi16 " Y:%" PRIi16 " Z:%" PRIi16, + sample.x, sample.y, sample.z); + break; + } +#ifdef CONFIG_MAG + case ComponentMag: { + MagData mag_sample; + mag_read_data(&mag_sample); + comp_name = "Mag"; + sniprintf(comp_detail, sizeof(comp_detail), + "X:%" PRIi16 " Y:%" PRIi16 " Z:%" PRIi16, + mag_sample.x, mag_sample.y, mag_sample.z); + break; + } +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + case ComponentBacklightWhite: + backlight_set_color(0xD0D0D0); + comp_name = "BL White"; + break; + case ComponentBacklightRed: + backlight_set_color(0xFF0000); + comp_name = "BL Red"; + break; + case ComponentBacklightGreen: + backlight_set_color(0x00FF00); + comp_name = "BL Green"; + break; + case ComponentBacklightBlue: + backlight_set_color(0x0000FF); + comp_name = "BL Blue"; + break; +#else + case ComponentBacklight: + comp_name = "Backlight"; + break; +#endif +#ifdef CONFIG_SPEAKER + case ComponentAudio: + comp_name = "Audio"; + if (!data->audio_playing) { + audio_start(AUDIO, prv_audio_trans_handler); + audio_set_volume(AUDIO, 100); + data->audio_playing = true; + } + break; +#endif + case ComponentALS: { + uint32_t level = ambient_light_get_light_level(); + comp_name = "ALS"; + sniprintf(comp_detail, sizeof(comp_detail), "Level: %" PRIu32, level); + break; + } + case ComponentVibe: { + // Only pulse once at the start of the vibe phase + if (data->component_elapsed_sec <= 1) { + static const uint32_t vibe_durations[] = { 250 }; + static const uint32_t vibe_amplitudes[] = { 50 }; + VibePatternWithAmplitudes pat = { + .durations = vibe_durations, + .amplitudes = vibe_amplitudes, + .num_segments = 1, + }; + vibes_enqueue_custom_pattern_with_amplitudes(pat); + } + comp_name = "Vibe"; + break; + } + default: + break; + } + + BatteryConstants bc; + battery_get_constants(&bc); + BatteryChargeState cs = battery_get_charge_state(); + + int8_t temp_c = (int8_t)(bc.t_mc / 1000); + uint8_t temp_c_frac = ((bc.t_mc > 0 ? bc.t_mc : -bc.t_mc) % 1000) / 10; + + BatteryChargeStatus charge_status; + battery_charge_status_get(&charge_status); + const char *chg_str; + if (!cs.is_charging) { + chg_str = "Idle"; + } else { + switch (charge_status) { + case BatteryChargeStatusComplete: chg_str = "Cmpl"; break; + case BatteryChargeStatusTrickle: chg_str = "Trk"; break; + case BatteryChargeStatusCC: chg_str = "CC"; break; + case BatteryChargeStatusCV: chg_str = "CV"; break; + default: chg_str = "?"; break; + } + } + + sniprintf(data->status_string, sizeof(data->status_string), + "CHG+CYC [%s]\n" + "Cycle: %" PRIu32 "\n" + "Elapsed: %s\n" + "Remain: %s\n" + "%" PRId32 "mV %" PRIu8 "%% %s\n" + "%" PRId8 ".%02" PRIu8 "C\n" + "%s", + comp_name, data->cycle_count, + time_str, rem_str, + bc.v_mv, cs.charge_percent, chg_str, + temp_c, temp_c_frac, + comp_detail); +} + +static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + AppData *data = app_state_get_user_data(); + + switch (data->state) { + case AgingStateWaitPlug: { + BatteryChargeState cs = battery_get_charge_state(); + if (cs.is_plugged) { + data->state = AgingStateChargingAndCycling; + data->phase_elapsed_sec = 0; + data->charge_complete = (cs.charge_percent >= CHARGE_TARGET_PERCENT); + if (data->charge_complete) { + // Battery was already full at plug-in: stop active charging now + // so the cell isn't held at 100% by continuous BMS top-off. + battery_set_charge_enable(false); + } + data->component = ComponentAccel; + data->component_elapsed_sec = 0; + data->cycle_count = 1; + prv_start_component(data); + sniprintf(data->status_string, sizeof(data->status_string), + "CHG+CYC\nStarting..."); + } else { + sniprintf(data->status_string, sizeof(data->status_string), + "PLUG CHARGER\n\nPlug the watch\nto start"); + } + break; + } + + case AgingStateChargingAndCycling: { + data->phase_elapsed_sec++; + data->component_elapsed_sec++; + + BatteryConstants bc; + battery_get_constants(&bc); + BatteryChargeState cs = battery_get_charge_state(); + + if (!cs.is_plugged) { + prv_enter_fail(data, "Unplugged during\ncharge+cycle"); + break; + } + + if (bc.t_mc < TEMP_MIN_MC || bc.t_mc > TEMP_MAX_MC) { + prv_enter_fail(data, "Temperature out\nof range"); + break; + } + + // Charge progress / completion tracking + if (!data->charge_complete) { + if (cs.charge_percent >= CHARGE_TARGET_PERCENT) { + data->charge_complete = true; + // Stop active charging now that we're full. The system keeps + // running off USB power, so the battery just holds at 100% + // for the rest of the cycling phase instead of being held + // there by continuous top-off, which is gentler on the cell. + battery_set_charge_enable(false); + } else if (data->phase_elapsed_sec >= CHARGE_TIMEOUT_SEC) { + prv_enter_fail(data, "Charge timeout\n(90min)"); + break; + } + } else if (cs.charge_percent < CHARGE_HOLD_MIN_PERCENT) { + prv_enter_fail(data, "Battery dropped\nafter charge"); + break; + } + + // Phase done? + if (data->phase_elapsed_sec >= CHARGE_AND_CYCLE_DURATION_SEC) { + if (!data->charge_complete) { + prv_enter_fail(data, "Charge incomplete\nat phase end"); + break; + } + prv_cleanup_component(data); + battery_set_charge_enable(true); + data->state = AgingStateWaitUnplug; + break; + } + + // Advance component if needed + if (data->component_elapsed_sec >= prv_component_duration(data->component)) { + prv_advance_component(data); + } + + prv_run_component_display(data); + break; + } + + case AgingStateWaitUnplug: { + BatteryChargeState cs = battery_get_charge_state(); + if (!cs.is_plugged) { + data->state = AgingStateIdle; + data->phase_elapsed_sec = 0; + data->initial_percent = cs.charge_percent; + sniprintf(data->status_string, sizeof(data->status_string), "IDLE\nStarting..."); + } else { + sniprintf(data->status_string, sizeof(data->status_string), + "UNPLUG WATCH\n\nCharged to %" PRIu8 "%%\n" + "Unplug to begin\nidle test", + cs.charge_percent); + } + break; + } + + case AgingStateIdle: { + data->phase_elapsed_sec++; + + BatteryConstants bc; + battery_get_constants(&bc); + BatteryChargeState cs = battery_get_charge_state(); + + if (cs.is_plugged) { + prv_enter_fail(data, "Plugged during\nidle test"); + break; + } + + int8_t drop = (int8_t)data->initial_percent - (int8_t)cs.charge_percent; + if (drop < 0) { + drop = 0; + } + + // Phase done? + if (data->phase_elapsed_sec >= IDLE_DURATION_SEC) { + if (drop > IDLE_MAX_DROP_PERCENT) { + char reason[64]; + sniprintf(reason, sizeof(reason), + "Idle drop %" PRId8 "%%\n> %d%%", + drop, IDLE_MAX_DROP_PERCENT); + prv_enter_fail(data, reason); + break; + } + // Idle passed — discharge to safe shipping level with backlight on + data->state = AgingStateDischarging; + data->phase_elapsed_sec = 0; +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + data->saved_backlight_color = backlight_get_color(); + backlight_set_color(0xFFFFFF); +#endif + light_enable(true); + break; + } + + char time_str[16], rem_str[16]; + prv_format_time(time_str, sizeof(time_str), data->phase_elapsed_sec); + prv_format_time(rem_str, sizeof(rem_str), + IDLE_DURATION_SEC - data->phase_elapsed_sec); + + int8_t temp_c = (int8_t)(bc.t_mc / 1000); + uint8_t temp_c_frac = ((bc.t_mc > 0 ? bc.t_mc : -bc.t_mc) % 1000) / 10; + + sniprintf(data->status_string, sizeof(data->status_string), + "IDLE\n" + "Elapsed: %s\n" + "Remain: %s\n" + "%" PRId32 "mV %" PRIu8 "%%\n" + "%" PRId8 ".%02" PRIu8 "C\n" + "Start:%" PRIu8 "%% Drop:%" PRId8 "/%d%%", + time_str, rem_str, + bc.v_mv, cs.charge_percent, + temp_c, temp_c_frac, + data->initial_percent, drop, IDLE_MAX_DROP_PERCENT); + break; + } + + case AgingStateDischarging: { + data->phase_elapsed_sec++; + + BatteryConstants bc; + battery_get_constants(&bc); + BatteryChargeState cs = battery_get_charge_state(); + + if (cs.is_plugged) { + light_enable(false); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + backlight_set_color(data->saved_backlight_color); +#endif + prv_enter_fail(data, "Plugged while\ndischarging"); + break; + } + + if (cs.charge_percent <= DISCHARGE_TARGET_PERCENT) { + light_enable(false); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + backlight_set_color(data->saved_backlight_color); +#endif + data->state = AgingStatePass; + sniprintf(data->status_string, sizeof(data->status_string), + "PASS\n\nDischarged to\n%" PRIu8 "%%", + cs.charge_percent); + prv_report_result(true); + tick_timer_service_unsubscribe(); + break; + } + + char time_str[16]; + prv_format_time(time_str, sizeof(time_str), data->phase_elapsed_sec); + + int8_t temp_c = (int8_t)(bc.t_mc / 1000); + uint8_t temp_c_frac = ((bc.t_mc > 0 ? bc.t_mc : -bc.t_mc) % 1000) / 10; + + sniprintf(data->status_string, sizeof(data->status_string), + "DISCHARGING\nTime: %s\n\n" + "%" PRId32 "mV %" PRIu8 "%%\n" + "%" PRId8 ".%02" PRIu8 "C\n\n" + "Target: %d%%", + time_str, bc.v_mv, cs.charge_percent, + temp_c, temp_c_frac, + DISCHARGE_TARGET_PERCENT); + break; + } + + case AgingStatePass: + case AgingStateFail: + return; + } + + if (data->state == AgingStateFail) { + sniprintf(data->status_string, sizeof(data->status_string), + "FAIL\n\n%s", data->fail_reason); + } + + text_layer_set_text(&data->status, data->status_string); +} + +static void prv_back_click_handler(ClickRecognizerRef recognizer, void *context) { + AppData *data = app_state_get_user_data(); + + if (data->state == AgingStateDischarging) { + light_enable(false); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + backlight_set_color(data->saved_backlight_color); +#endif + } else if (data->state == AgingStateChargingAndCycling) { + prv_cleanup_component(data); + battery_set_charge_enable(true); + } + + tick_timer_service_unsubscribe(); + + serial_console_set_rx_enabled(true); + bt_ctl_set_enabled(true); + prf_idle_watchdog_start(); + + app_window_stack_pop(true); +} + +static void prv_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData){ + .state = AgingStateWaitPlug, +#ifdef CONFIG_SPEAKER + .audio_playing = false, +#endif + }; + + app_state_set_user_data(data); + + // Disable power consumers and idle watchdog for the entire test + serial_console_set_rx_enabled(false); + bt_ctl_set_enabled(false); + prf_idle_watchdog_stop(); + + // Make sure charging is enabled at start of test in case a previous run + // left it disabled. + battery_set_charge_enable(true); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + window_set_click_config_provider(window, prv_config_provider); + + Layer *window_layer = &window->layer; + GRect bounds = window_layer->bounds; + + TextLayer *title = &data->title; + const int16_t title_y = PBL_IF_ROUND_ELSE(10, 0); + text_layer_init(title, &GRect(0, title_y, bounds.size.w, 24)); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "AGING TEST"); + layer_add_child(window_layer, &title->layer); + + TextLayer *status = &data->status; + const int16_t status_y = PBL_IF_ROUND_ELSE(40, 25); + const int16_t status_x = PBL_IF_ROUND_ELSE(15, 5); + text_layer_init(status, &GRect(status_x, status_y, bounds.size.w - (status_x * 2), + bounds.size.h - status_y)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_18)); + text_layer_set_text_alignment(status, PBL_IF_ROUND_ELSE(GTextAlignmentCenter, + GTextAlignmentLeft)); + layer_add_child(window_layer, &status->layer); + + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); + + app_window_stack_push(window, true); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); +} + +const PebbleProcessMd *mfg_test_aging_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: 12345678-ABCD-EF01-2345-6789ABCDEF01 + .common.uuid = { 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01, + 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01 }, + .name = "MfgTestAging", + }; + return (const PebbleProcessMd *)&s_app_info; +} diff --git a/src/fw/apps/prf/mfg_test_aging.h b/src/fw/apps/prf/mfg_test_aging.h new file mode 100644 index 0000000000..d19a290362 --- /dev/null +++ b/src/fw/apps/prf/mfg_test_aging.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_test_aging_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_test_menu.c b/src/fw/apps/prf/mfg_test_menu.c new file mode 100644 index 0000000000..13c1e86127 --- /dev/null +++ b/src/fw/apps/prf/mfg_test_menu.c @@ -0,0 +1,268 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "apps/prf/mfg_accel.h" +#ifdef CONFIG_MAG +#include "apps/prf/mfg_mag.h" +#endif +#include "apps/prf/mfg_als.h" +#include "apps/prf/mfg_backlight.h" +#include "apps/prf/mfg_button.h" +#include "apps/prf/mfg_charge.h" +#include "apps/prf/mfg_display.h" +#include "apps/prf/mfg_hrm_ctr_leakage_obelix.h" +#include "apps/prf/mfg_mic_asterix.h" +#include "apps/prf/mfg_mic_getafix.h" +#include "apps/prf/mfg_mic_obelix.h" +#include "apps/prf/mfg_program_color.h" +#include "apps/prf/mfg_qr_results.h" +#include "apps/prf/mfg_speaker_asterix.h" +#include "apps/prf/mfg_speaker_obelix.h" +#include "apps/prf/mfg_touch.h" +#include "apps/prf/mfg_test_menu.h" +#include "apps/prf/mfg_test_result.h" +#include "apps/prf/mfg_vibration.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "util/size.h" + +typedef const PebbleProcessMd *(*MfgTestGetInfoFn)(void); + +typedef struct { + const char *title; + MfgTestId test_id; + MfgTestGetInfoFn get_info; + uint8_t modes; +} MfgTestMenuEntry; + +#define SF MFG_TEST_MODE_SEMI_FINISHED +#define FI MFG_TEST_MODE_FINISHED + +static const MfgTestMenuEntry s_all_entries[] = { + { "Buttons", MfgTestId_Buttons, mfg_button_app_get_info, SF | FI }, + { "Display", MfgTestId_Display, mfg_display_app_get_info, SF | FI }, +#ifdef CONFIG_TOUCH + { "Touch", MfgTestId_Touch, mfg_touch_app_get_info, SF | FI }, +#endif + { "Backlight", MfgTestId_Backlight, mfg_backlight_app_get_info, SF | FI }, + { "Accelerometer", MfgTestId_Accel, mfg_accel_app_get_info, SF | FI }, +#ifdef CONFIG_MAG + { "Magnetometer", MfgTestId_Mag, mfg_mag_app_get_info, SF | FI }, +#endif +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + { "Speaker", MfgTestId_Speaker, mfg_speaker_asterix_app_get_info, SF | FI }, +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + { "Speaker", MfgTestId_Speaker, mfg_speaker_obelix_app_get_info, SF | FI }, +#endif +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + { "Microphone", MfgTestId_Mic, mfg_mic_asterix_app_get_info, SF | FI }, +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + { "Microphone", MfgTestId_Mic, mfg_mic_obelix_app_get_info, SF | FI }, +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) + { "Microphone", MfgTestId_Mic, mfg_mic_getafix_app_get_info, SF | FI }, +#endif + { "ALS", MfgTestId_ALS, mfg_als_app_get_info, SF | FI }, + { "Vibration", MfgTestId_Vibration, mfg_vibration_app_get_info, SF | FI }, +#if defined(CONFIG_BOARD_FAMILY_OBELIX) && defined(CONFIG_MFG) + { "HRM CTR/L", MfgTestId_HrmCtrLeakage, mfg_hrm_ctr_leakage_obelix_app_get_info, SF | FI }, +#endif + { "Charge", MfgTestId_Charge, mfg_charge_app_get_info, SF | FI }, + { "Program Color", MfgTestId_ProgramColor, mfg_program_color_app_get_info, FI }, +}; + +#undef SF +#undef FI + +// Filtered entries for the active mode (built at startup) +static const MfgTestMenuEntry *s_entries[ARRAY_LENGTH(s_all_entries)]; +static size_t s_num_entries; + +static uint8_t s_active_mode; + +typedef struct { + Window *window; + SimpleMenuLayer *menu_layer; + SimpleMenuSection menu_section; +} MfgTestMenuAppData; + +static bool s_relaunch_menu = false; +static int16_t s_last_selected = -1; + +//! Callback to run from the kernel main task +static void prv_launch_app_cb(void *data) { + app_manager_launch_new_app(&(AppLaunchConfig) { .md = data }); +} + +static void prv_launch_test(int index, const PebbleProcessMd *md) { + s_relaunch_menu = true; + s_last_selected = index; + launcher_task_add_callback(prv_launch_app_cb, (void*) md); +} + +static void prv_select_test(int index, void *context) { + if ((size_t)index < s_num_entries) { + prv_launch_test(index, s_entries[index]->get_info()); + } +} + +static void prv_select_results(int index, void *context) { + prv_launch_test(index, mfg_qr_results_app_get_info()); +} + +static const char * prv_get_status_prefix(MfgTestId test) { + const MfgTestResult *result = mfg_test_result_get(test); + if (!result || !result->ran) { + return "[ ]"; + } + return result->passed ? "[P]" : "[F]"; +} + +static void prv_build_filtered_entries(uint8_t mode) { + s_num_entries = 0; + for (size_t i = 0; i < ARRAY_LENGTH(s_all_entries); i++) { + if (s_all_entries[i].modes & mode) { + s_entries[s_num_entries++] = &s_all_entries[i]; + } + } +} + +static void prv_window_load(Window *window) { + MfgTestMenuAppData *data = app_state_get_user_data(); + + Layer *window_layer = window_get_root_layer(data->window); + GRect bounds = window_layer->bounds; + + size_t num_items = s_num_entries + 1; // +1 for RESULTS + SimpleMenuItem *items = app_malloc(num_items * sizeof(SimpleMenuItem)); + + for (size_t i = 0; i < s_num_entries; i++) { + const char *prefix = prv_get_status_prefix(s_entries[i]->test_id); + size_t len = snprintf(NULL, 0, "%zu. %s %s", i + 1, prefix, s_entries[i]->title) + 1; + char *title = app_malloc(len); + snprintf(title, len, "%zu. %s %s", i + 1, prefix, s_entries[i]->title); + + items[i] = (SimpleMenuItem) { + .title = title, + .callback = prv_select_test, + }; + } + + items[s_num_entries] = (SimpleMenuItem) { + .title = "RESULTS", + .callback = prv_select_results, + }; + + data->menu_section = (SimpleMenuSection) { + .num_items = num_items, + .items = items + }; + + data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, NULL); + + if (s_last_selected >= 0) { + if (mfg_test_result_was_reported()) { + const MfgTestResult *result = ((size_t)s_last_selected < s_num_entries) + ? mfg_test_result_get(s_entries[s_last_selected]->test_id) + : NULL; + + if (result && result->passed) { + // Test passed: auto-advance to next test + int16_t next = s_last_selected + 1; + if ((size_t)next < s_num_entries) { + simple_menu_layer_set_selected_index(data->menu_layer, next, false); + prv_launch_test(next, s_entries[next]->get_info()); + } else { + // Reached end of test list, launch RESULTS + simple_menu_layer_set_selected_index(data->menu_layer, s_num_entries, false); + prv_launch_test(s_num_entries, mfg_qr_results_app_get_info()); + } + } else { + // Test failed: stay on same test so operator can retry + simple_menu_layer_set_selected_index(data->menu_layer, s_last_selected, false); + } + } else { + // User backed out: select the test they came from + simple_menu_layer_set_selected_index(data->menu_layer, s_last_selected, false); + } + } + + layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); +} + +bool mfg_test_menu_should_relaunch(void) { + if (s_relaunch_menu) { + s_relaunch_menu = false; + return true; + } + return false; +} + +bool mfg_test_menu_is_test_available(MfgTestId test_id) { + for (size_t i = 0; i < ARRAY_LENGTH(s_all_entries); i++) { + if (s_all_entries[i].test_id == test_id) { + return (s_all_entries[i].modes & s_active_mode) != 0; + } + } + return false; +} + +static void prv_run_menu(uint8_t mode) { + if (s_active_mode != mode) { + s_last_selected = -1; + } + s_active_mode = mode; + mfg_test_result_set_mode(mode); + prv_build_filtered_entries(mode); + + MfgTestMenuAppData *data = app_malloc_check(sizeof(MfgTestMenuAppData)); + *data = (MfgTestMenuAppData){}; + + app_state_set_user_data(data); + + data->window = window_create(); + window_init(data->window, "Tests"); + window_set_window_handlers(data->window, &(WindowHandlers) { + .load = prv_window_load, + }); + window_set_fullscreen(data->window, true); + app_window_stack_push(data->window, true); + + app_event_loop(); +} + +static void s_main_semi_finished(void) { + prv_run_menu(MFG_TEST_MODE_SEMI_FINISHED); +} + +static void s_main_finished(void) { + prv_run_menu(MFG_TEST_MODE_FINISHED); +} + +const PebbleProcessMd* mfg_test_menu_semi_finished_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main_semi_finished, + // UUID: 8a3f6c1e-9b2d-4f5a-a7c3-1d4e6b8f9a2c + .common.uuid = { 0x8a, 0x3f, 0x6c, 0x1e, 0x9b, 0x2d, 0x4f, 0x5a, + 0xa7, 0xc3, 0x1d, 0x4e, 0x6b, 0x8f, 0x9a, 0x2c }, + .name = "MfgTestMenuSF", + }; + return (const PebbleProcessMd*) &s_app_info; +} + +const PebbleProcessMd* mfg_test_menu_finished_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main_finished, + // UUID: 8a3f6c1e-9b2d-4f5a-a7c3-1d4e6b8f9a2d + .common.uuid = { 0x8a, 0x3f, 0x6c, 0x1e, 0x9b, 0x2d, 0x4f, 0x5a, + 0xa7, 0xc3, 0x1d, 0x4e, 0x6b, 0x8f, 0x9a, 0x2d }, + .name = "MfgTestMenuFI", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_test_menu.h b/src/fw/apps/prf/mfg_test_menu.h new file mode 100644 index 0000000000..dcb149343e --- /dev/null +++ b/src/fw/apps/prf/mfg_test_menu.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "apps/prf/mfg_test_result.h" +#include "process_management/pebble_process_md.h" + +#include +#include + +const PebbleProcessMd* mfg_test_menu_semi_finished_app_get_info(void); +const PebbleProcessMd* mfg_test_menu_finished_app_get_info(void); + +//! Check and clear the relaunch flag (set when returning from a test) +bool mfg_test_menu_should_relaunch(void); + +//! Check if a test is available in the current mode +bool mfg_test_menu_is_test_available(MfgTestId test_id); diff --git a/src/fw/apps/prf/mfg_test_result.c b/src/fw/apps/prf/mfg_test_result.c new file mode 100644 index 0000000000..d83e9e9680 --- /dev/null +++ b/src/fw/apps/prf/mfg_test_result.c @@ -0,0 +1,149 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/prf/mfg_test_result.h" + +#include + +#ifdef CONFIG_MFG +#include "drivers/flash.h" +#include "flash_region/flash_region.h" +#include "util/attributes.h" +#endif + +#define NUM_MODES 2 + +static MfgTestResult s_results[NUM_MODES][MfgTestIdCount]; +static bool s_result_reported; +static uint8_t s_mode_index; + +#ifdef CONFIG_MFG +// Append-only log of results in the MFG_RESULTS subsector. Each report writes +// one record; the subsector is only erased once the log fills up (compaction). +typedef struct PACKED { + uint8_t test_id; // MFG_RESULT_EMPTY marks a free slot + uint8_t mode_index; + uint8_t passed; + uint8_t rsvd; + uint32_t value; +} MfgResultRecord; + +#define MFG_RESULT_EMPTY 0xFF +#define MFG_RESULTS_SIZE (FLASH_REGION_MFG_RESULTS_END - FLASH_REGION_MFG_RESULTS_BEGIN) +#define MFG_RESULTS_MAX_RECORDS (MFG_RESULTS_SIZE / sizeof(MfgResultRecord)) + +static bool s_loaded; +static uint32_t s_record_count; + +static void prv_write_record(uint32_t index, MfgTestId test, uint8_t mode_index, bool passed, + uint32_t value) { + MfgResultRecord rec = { + .test_id = test, + .mode_index = mode_index, + .passed = passed, + .rsvd = 0, + .value = value, + }; + flash_write_bytes((const uint8_t *)&rec, + FLASH_REGION_MFG_RESULTS_BEGIN + index * sizeof(rec), sizeof(rec)); +} + +static void prv_load(void) { + for (uint32_t i = 0; i < MFG_RESULTS_MAX_RECORDS; i++) { + MfgResultRecord rec; + flash_read_bytes((uint8_t *)&rec, FLASH_REGION_MFG_RESULTS_BEGIN + i * sizeof(rec), + sizeof(rec)); + if (rec.test_id == MFG_RESULT_EMPTY) { + break; + } + s_record_count = i + 1; + if (rec.test_id < MfgTestIdCount && rec.mode_index < NUM_MODES) { + s_results[rec.mode_index][rec.test_id] = (MfgTestResult) { + .ran = true, + .passed = (rec.passed != 0), + .value = rec.value, + }; + } + } +} + +static void prv_ensure_loaded(void) { + if (s_loaded) { + return; + } + s_loaded = true; + prv_load(); +} + +static void prv_append(MfgTestId test, uint8_t mode_index, bool passed, uint32_t value) { + if (s_record_count >= MFG_RESULTS_MAX_RECORDS) { + // Log full: erase and rewrite the current state as a compacted log. + flash_erase_subsector_blocking(FLASH_REGION_MFG_RESULTS_BEGIN); + s_record_count = 0; + for (uint8_t m = 0; m < NUM_MODES; m++) { + for (uint8_t t = 0; t < MfgTestIdCount; t++) { + if (s_results[m][t].ran) { + prv_write_record(s_record_count++, t, m, s_results[m][t].passed, + s_results[m][t].value); + } + } + } + } + + prv_write_record(s_record_count++, test, mode_index, passed, value); +} +#endif // CONFIG_MFG + +void mfg_test_result_set_mode(uint8_t mode) { + // Map mode bitmask to array index: semi-finished=0, finished=1 + s_mode_index = (mode == MFG_TEST_MODE_FINISHED) ? 1 : 0; +} + +void mfg_test_result_report(MfgTestId test, bool passed, uint32_t value) { + if (test >= MfgTestIdCount) { + return; + } + +#ifdef CONFIG_MFG + prv_ensure_loaded(); + prv_append(test, s_mode_index, passed, value); +#endif + + s_results[s_mode_index][test] = (MfgTestResult) { + .ran = true, + .passed = passed, + .value = value, + }; + s_result_reported = true; +} + +const MfgTestResult *mfg_test_result_get(MfgTestId test) { + if (test >= MfgTestIdCount) { + return NULL; + } + +#ifdef CONFIG_MFG + prv_ensure_loaded(); +#endif + + return &s_results[s_mode_index][test]; +} + +bool mfg_test_result_was_reported(void) { + bool reported = s_result_reported; + s_result_reported = false; + return reported; +} + +void mfg_test_result_reset(void) { + memset(s_results, 0, sizeof(s_results)); + s_result_reported = false; + +#ifdef CONFIG_MFG + if (!flash_subsector_is_erased(FLASH_REGION_MFG_RESULTS_BEGIN)) { + flash_erase_subsector_blocking(FLASH_REGION_MFG_RESULTS_BEGIN); + } + s_record_count = 0; + s_loaded = true; +#endif +} diff --git a/src/fw/apps/prf/mfg_test_result.h b/src/fw/apps/prf/mfg_test_result.h new file mode 100644 index 0000000000..487ab8c113 --- /dev/null +++ b/src/fw/apps/prf/mfg_test_result.h @@ -0,0 +1,67 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include + +// Test mode bitmask +#define MFG_TEST_MODE_SEMI_FINISHED (1 << 0) +#define MFG_TEST_MODE_FINISHED (1 << 1) +#define MFG_TEST_MODE_ALL (MFG_TEST_MODE_SEMI_FINISHED | MFG_TEST_MODE_FINISHED) + +typedef enum { + MfgTestId_Buttons, + MfgTestId_Display, +#ifdef CONFIG_TOUCH + MfgTestId_Touch, +#endif + MfgTestId_Backlight, + MfgTestId_Accel, +#ifdef CONFIG_MAG + MfgTestId_Mag, +#endif +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) + MfgTestId_Speaker, +#endif +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + MfgTestId_Mic, +#endif + MfgTestId_ALS, + MfgTestId_Vibration, +#if defined(CONFIG_BOARD_FAMILY_OBELIX) && defined(CONFIG_MFG) + MfgTestId_HrmCtrLeakage, +#endif + MfgTestId_ProgramColor, + MfgTestId_Charge, + MfgTestId_Aging, + + MfgTestIdCount +} MfgTestId; + +typedef struct { + bool ran; + bool passed; + uint32_t value; +} MfgTestResult; + +//! Set the active test mode (MFG_TEST_MODE_SEMI_FINISHED or +//! MFG_TEST_MODE_FINISHED). Results are stored separately per mode. +void mfg_test_result_set_mode(uint8_t mode); + +//! Report the result of a test. Can be called from any test app. +void mfg_test_result_report(MfgTestId test, bool passed, uint32_t value); + +//! Get the result of a test. Returns NULL if the test has not been run. +const MfgTestResult *mfg_test_result_get(MfgTestId test); + +//! Check if a result was reported since the last call to this function. +//! Returns true once, then resets. Used by the test menu to detect test +//! completion vs user backing out. +bool mfg_test_result_was_reported(void); + +//! Clear all stored results. In manufacturing firmware this also erases the +//! MFG_RESULTS flash region. +void mfg_test_result_reset(void); diff --git a/src/fw/apps/prf/mfg_touch.c b/src/fw/apps/prf/mfg_touch.c new file mode 100644 index 0000000000..6d9c4ad0d2 --- /dev/null +++ b/src/fw/apps/prf/mfg_touch.c @@ -0,0 +1,270 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "applib/touch_service.h" +#include "pbl/services/light.h" +#include "pbl/services/touch/touch.h" +#include "util/trig.h" + +#include + +#if PBL_ROUND +#define CIRCLE_ROWS (5) +#define CIRCLE_COLS (5) +#else +#define RECTR_ROW (5) +#define RECTR_COL (4) +#endif + +#define TOUCH_SUPPORT_DEBUG 0 + +#define TEST_TIMEOUT_S (10) +#define WINDOW_POP_TIME_S (3) + +typedef struct { + Window window; + uint32_t touch_mark; +#if PBL_ROUND + GPoint circle_centers[CIRCLE_ROWS * CIRCLE_COLS]; + uint16_t circle_radius; + uint8_t num_circles; +#else + GRect rectr[RECTR_ROW * RECTR_COL]; + uint16_t rectr_radius; + uint16_t rectr_corners_index; +#endif + TextLayer status; + char status_string[35]; + uint32_t seconds_remaining; + bool test_complete; +} AppData; + +static uint32_t prv_all_touched_mask(const AppData *data) { +#if PBL_ROUND + return (1u << data->num_circles) - 1; +#else + return (1u << (RECTR_ROW * RECTR_COL)) - 1; +#endif +} + +static void prv_complete_test(AppData *data, bool passed) { + data->test_complete = true; + data->seconds_remaining = WINDOW_POP_TIME_S; + + mfg_test_result_report(MfgTestId_Touch, passed, data->touch_mark); + + sniprintf(data->status_string, sizeof(data->status_string), "%s", passed ? "PASS!" : "FAIL!"); + text_layer_set_text(&data->status, data->status_string); +} + +static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + AppData *data = app_state_get_user_data(); + + if (data->test_complete) { + if (--data->seconds_remaining == 0) { + app_window_stack_pop(true); + } + return; + } + + if (data->seconds_remaining == 0) { + prv_complete_test(data, false); + } else { + sniprintf(data->status_string, sizeof(data->status_string), + "TIME: %" PRIu32 "s", data->seconds_remaining); + text_layer_set_text(&data->status, data->status_string); + data->seconds_remaining--; + } +} + +static void prv_update_proc(struct Layer *layer, GContext* ctx) { + AppData *data = app_state_get_user_data(); + +#if PBL_ROUND + for (uint8_t i=0; i < data->num_circles; i++) { + if (data->touch_mark & (1 << i)) { + graphics_context_set_fill_color(ctx, GColorGreen); + graphics_fill_circle(ctx, data->circle_centers[i], data->circle_radius); + } + } +#else + for (uint8_t i=0; itouch_mark & (1 << i)) { + graphics_context_set_fill_color(ctx, GColorGreen); + graphics_fill_round_rect(ctx, &data->rectr[i], data->rectr_radius, GCornersAll); + } + } +#endif +} + +static void prv_touch_event_handler(const TouchEvent *event, void *context) { + AppData *data = app_state_get_user_data(); + int16_t touch_x = event->x; + int16_t touch_y = event->y; + + uint8_t touch_id; + +#if PBL_ROUND + uint32_t min_dist_sq = 0xFFFFFFFF; + touch_id = 0; + + for (uint8_t i = 0; i < data->num_circles; i++) { + int16_t dx = touch_x - data->circle_centers[i].x; + int16_t dy = touch_y - data->circle_centers[i].y; + uint32_t dist_sq = dx * dx + dy * dy; + if (dist_sq < min_dist_sq) { + min_dist_sq = dist_sq; + touch_id = i; + } + } +#else + uint8_t ind_x = touch_x / data->rectr[0].size.w; + if (ind_x > RECTR_COL - 1) ind_x = RECTR_COL - 1; + uint8_t ind_y = touch_y / data->rectr[0].size.h; + if (ind_y > RECTR_ROW - 1) ind_y = RECTR_ROW - 1; + touch_id = ind_x * RECTR_ROW + ind_y; +#endif + + data->touch_mark |= 1 << touch_id; +#if TOUCH_SUPPORT_DEBUG + PBL_LOG_INFO("x:%d y:%d id:%d", touch_x, touch_y, touch_id); +#endif + layer_mark_dirty(&data->window.layer); + + if (!data->test_complete && data->touch_mark == prv_all_touched_mask(data)) { + prv_complete_test(data, true); + } +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) { + .seconds_remaining = TEST_TIMEOUT_S, + }; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + Layer *layer = window_get_root_layer(window); + layer_set_update_proc(layer, prv_update_proc); + app_window_stack_push(window, true /* Animated */); + GContext* context = app_state_get_graphics_context(); + +#if PBL_ROUND + // Create honeycomb-like pattern with circles that touch but don't overlap + uint16_t display_radius = PBL_DISPLAY_WIDTH / 2; + + // Calculate circle radius to fit 5 circles across the diameter + // Spacing: circles just touch, so horizontal spacing = 2 * radius + // For 5 circles across: 5 * (2 * radius) should fit in display width + data->circle_radius = PBL_DISPLAY_WIDTH / 10; + + // For honeycomb packing, vertical spacing is 2 * radius * sqrt(3)/2 ≈ 1.732 * radius + // We'll approximate sqrt(3)/2 ≈ 0.866 using integer math: 866/1000 + int16_t vertical_spacing = (data->circle_radius * 1732) / 1000; + int16_t horizontal_spacing = 2 * data->circle_radius; + + // Calculate grid offset to center the pattern + int16_t grid_width = (CIRCLE_COLS - 1) * horizontal_spacing; + int16_t grid_height = (CIRCLE_ROWS - 1) * vertical_spacing; + int16_t start_x = (PBL_DISPLAY_WIDTH - grid_width) / 2; + int16_t start_y = (PBL_DISPLAY_HEIGHT - grid_height) / 2; + + data->num_circles = 0; + + // Create a honeycomb pattern: odd rows are offset by radius + for (uint8_t row = 0; row < CIRCLE_ROWS; row++) { + for (uint8_t col = 0; col < CIRCLE_COLS; col++) { + int16_t x = start_x + col * horizontal_spacing; + int16_t y = start_y + row * vertical_spacing; + + // Offset odd rows to create honeycomb pattern + if (row % 2 == 1) { + x += data->circle_radius; + } + + // Only add circles that are within the display radius + int16_t dx = x - PBL_DISPLAY_WIDTH / 2; + int16_t dy = y - PBL_DISPLAY_HEIGHT / 2; + uint32_t dist_sq = dx * dx + dy * dy; + uint32_t max_dist_sq = (display_radius - data->circle_radius) * (display_radius - data->circle_radius); + + if (dist_sq <= max_dist_sq && data->num_circles < CIRCLE_ROWS * CIRCLE_COLS) { + data->circle_centers[data->num_circles].x = x; + data->circle_centers[data->num_circles].y = y; + data->num_circles++; + } + } + } + + // Draw the circle outlines + graphics_context_set_stroke_color(context, GColorBlack); + for (uint8_t i = 0; i < data->num_circles; i++) { + graphics_draw_circle(context, data->circle_centers[i], data->circle_radius); + } +#else + //draw rectrs + data->rectr_radius = 5; + data->rectr_corners_index = GCornersAll; + for (uint8_t x=0; xrectr[RECTR_ROW * x + y].origin.x = (PBL_DISPLAY_WIDTH/RECTR_COL) * x; + data->rectr[RECTR_ROW * x + y].origin.y = (PBL_DISPLAY_HEIGHT/RECTR_ROW) * y; + data->rectr[RECTR_ROW * x + y].size.w = PBL_DISPLAY_WIDTH/RECTR_COL; + data->rectr[RECTR_ROW * x + y].size.h = PBL_DISPLAY_HEIGHT/RECTR_ROW; + graphics_context_set_stroke_color(context, GColorBlack); + graphics_draw_round_rect(context, &data->rectr[RECTR_ROW * x + y], data->rectr_radius); + } + } +#endif + layer_mark_dirty(&data->window.layer); + + TextLayer *status = &data->status; + text_layer_init(status, + &GRect(5, PBL_DISPLAY_HEIGHT - 40, PBL_DISPLAY_WIDTH - 10, 40)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + text_layer_set_background_color(status, GColorWhite); + layer_add_child(&window->layer, &status->layer); + + touch_service_subscribe(prv_touch_event_handler, NULL); + + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); +} + +static void s_main(void) { + light_enable(true); + + prv_handle_init(); + + app_event_loop(); + + touch_service_unsubscribe(); + light_enable(false); +} + +const PebbleProcessMd* mfg_touch_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: a53e7d1c-d2ee-4592-96b9-5d33a46237db + .common.uuid = { 0xa5, 0x3e, 0x7d, 0x1c, 0xd2, 0xee, 0x45, 0x92, + 0x96, 0xb9, 0x5d, 0x33, 0xa4, 0x62, 0x37, 0xdb }, + .name = "MfgTouch", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_touch.h b/src/fw/apps/prf/mfg_touch.h new file mode 100644 index 0000000000..706af6dcba --- /dev/null +++ b/src/fw/apps/prf/mfg_touch.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +//! @file mfg_display.h +//! +//! Test app that shows various display pattern + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_touch_app_get_info(void); diff --git a/src/fw/apps/prf/mfg_vibration.c b/src/fw/apps/prf/mfg_vibration.c new file mode 100644 index 0000000000..7cfd384e7b --- /dev/null +++ b/src/fw/apps/prf/mfg_vibration.c @@ -0,0 +1,167 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/vibes.h" +#include "applib/ui/window.h" +#include "apps/prf/mfg_test_result.h" +#include "drivers/vibe.h" +#include "kernel/pbl_malloc.h" +#include "mfg/mfg_info.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" + +#define VIBE_COUNT 5 +#define FAIL_DISPLAY_S 3 +#define MAX_CALIBRATION_ATTEMPTS 3 + +typedef enum { + STATE_CALIBRATE, + STATE_WAITING, + STATE_VIBING, + STATE_ERROR, +} VibeTestState; + +typedef struct { + Window window; + + TextLayer title; + TextLayer status; + + int wait; + int vibe_count; + int cali_attempts; + VibeTestState state; +} AppData; + +static void prv_result_confirmed(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *confirmation_dialog = (ConfirmationDialog *)context; + confirmation_dialog_pop(confirmation_dialog); + + bool passed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + mfg_test_result_report(MfgTestId_Vibration, passed, 0); + app_window_stack_pop(false); +} + +static void prv_result_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_result_confirmed); + window_single_click_subscribe(BUTTON_ID_BACK, prv_result_confirmed); +} + +static void prv_show_result_dialog(void) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Vibration Result"); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, "Vibration OK?"); + + confirmation_dialog_set_click_config_provider(confirmation_dialog, prv_result_click_config); + + ActionBarLayer *action_bar = confirmation_dialog_get_action_bar(confirmation_dialog); + action_bar_layer_set_context(action_bar, confirmation_dialog); + + app_confirmation_dialog_push(confirmation_dialog); +} + +static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + AppData *data = app_state_get_user_data(); + + const int WAIT_AFTER_CALIBRATION_S = 3; + + if (data->state == STATE_CALIBRATE) { + status_t status = vibe_calibrate(); + if (status == S_SUCCESS) { + mfg_info_set_vibe_cali(vibe_get_calibration()); + text_layer_set_text(&data->status, "CALIBRATED"); + data->state = STATE_WAITING; + } else if (status == E_INVALID_OPERATION) { + text_layer_set_text(&data->status, "CALIBRATION SKIPPED"); + data->state = STATE_WAITING; + } else if (++data->cali_attempts < MAX_CALIBRATION_ATTEMPTS) { + text_layer_set_text(&data->status, "CALIBRATION RETRY"); + // Stay in STATE_CALIBRATE to retry on the next tick. + } else { + text_layer_set_text(&data->status, "CALIBRATION FAILED"); + mfg_test_result_report(MfgTestId_Vibration, false, 0); + data->state = STATE_ERROR; + data->wait = 0; + } + } else if (data->state == STATE_WAITING) { + data->wait++; + if (data->wait >= WAIT_AFTER_CALIBRATION_S) { + text_layer_set_text(&data->status, "VIBING..."); + data->state = STATE_VIBING; + data->vibe_count = 0; + } + } else if (data->state == STATE_VIBING) { + vibes_long_pulse(); + data->vibe_count++; + if (data->vibe_count >= VIBE_COUNT) { + tick_timer_service_unsubscribe(); + prv_show_result_dialog(); + } + } else if (data->state == STATE_ERROR) { + data->wait++; + if (data->wait >= FAIL_DISPLAY_S) { + tick_timer_service_unsubscribe(); + app_window_stack_pop(false); + } + } +} + +static void prv_handle_init(void) { + AppData *data = app_malloc_check(sizeof(AppData)); + *data = (AppData) { + .wait = 0, + .state = STATE_CALIBRATE, + }; + + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, ""); + window_set_fullscreen(window, true); + + TextLayer *title = &data->title; + text_layer_init(title, &window->layer.bounds); + text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(title, GTextAlignmentCenter); + text_layer_set_text(title, "VIBRATION TEST"); + layer_add_child(&window->layer, &title->layer); + + TextLayer *status = &data->status; + text_layer_init(status, + &GRect(5, 70, + window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 70)); + text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); + text_layer_set_text_alignment(status, GTextAlignmentCenter); + layer_add_child(&window->layer, &status->layer); + + app_window_stack_push(window, true /* Animated */); + + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); +} + +static void s_main(void) { + prv_handle_init(); + + app_event_loop(); + + tick_timer_service_unsubscribe(); + vibes_cancel(); +} + +const PebbleProcessMd* mfg_vibration_app_get_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common.main_func = &s_main, + // UUID: f676085a-b130-4492-b6a1-85492602ba00 + .common.uuid = { 0xf6, 0x76, 0x08, 0x5a, 0xb1, 0x30, 0x44, 0x92, + 0xb6, 0xa1, 0x85, 0x49, 0x26, 0x02, 0xba, 0x00 }, + .name = "MfgVibration", + }; + return (const PebbleProcessMd*) &s_app_info; +} diff --git a/src/fw/apps/prf/mfg_vibration.h b/src/fw/apps/prf/mfg_vibration.h new file mode 100644 index 0000000000..fd61b25aa3 --- /dev/null +++ b/src/fw/apps/prf/mfg_vibration.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! @file mfg_vibe.h +//! +//! Boring test app that vibes 5 times and quits + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* mfg_vibration_app_get_info(void); + diff --git a/src/fw/apps/prf/recovery_first_use/getting_started_button_combo.c b/src/fw/apps/prf/recovery_first_use/getting_started_button_combo.c new file mode 100644 index 0000000000..f23939c1e4 --- /dev/null +++ b/src/fw/apps/prf/recovery_first_use/getting_started_button_combo.c @@ -0,0 +1,97 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "getting_started_button_combo.h" + +#include "applib/ui/app_window_stack.h" +#include "applib/graphics/gtypes.h" + +#include "apps/core/spinner_ui_window.h" +#include "kernel/util/factory_reset.h" +#include "mfg/mfg_mode/mfg_factory_mode.h" +#include "process_management/process_manager.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "util/size.h" + +void getting_started_button_combo_init(GettingStartedButtonComboState *state, + GettingStartedButtonComboCallback select_callback) { + *state = (GettingStartedButtonComboState) { + .buttons_held_bitset = 0, + .combo_timer = new_timer_create(), + .select_callback = select_callback + }; +} + +void getting_started_button_combo_deinit(GettingStartedButtonComboState *state) { + new_timer_delete(state->combo_timer); +} + +static void prv_factory_reset(void *not_used) { + factory_reset(false /* should_shutdown */); +} + +static void prv_down_cb(void *data) { + Window *spinner_window = spinner_ui_window_get(PBL_IF_COLOR_ELSE(GColorBlue, GColorDarkGray)); + app_window_stack_push(spinner_window, false /* animated */); + + // Factory reset on KernelBG so the animation gets priority + system_task_add_callback(prv_factory_reset, NULL); +} + +#ifdef CONFIG_RECOVERY_FW +static void prv_mfg_mode_cb(void *data) { + mfg_enter_mfg_mode_and_launch_app(); +} +#endif + +static void prv_timeout_expired(void *data) { + PBL_LOG_INFO("Button combo timeout expired!"); + + // Timeout expired, jump over the app thread to do the thing. + void (*real_callback)(void*) = data; + process_manager_send_callback_event_to_process(PebbleTask_App, real_callback, NULL); +} + +static void prv_update_state(GettingStartedButtonComboState *state) { + const uint32_t COMBO_HOLD_MS = 5 * 1000; // Wait for 5 seconds + + // Map of button combos -> callback to call if we hit it. + const struct { + uint8_t desired_bitset; + void (*callback)(void*); + } BUTTON_COMBOS[] = { + { (1 << BUTTON_ID_SELECT), state->select_callback }, + { (1 << BUTTON_ID_DOWN), prv_down_cb }, +#ifdef CONFIG_RECOVERY_FW + { (1 << BUTTON_ID_UP) | (1 << BUTTON_ID_SELECT), prv_mfg_mode_cb }, +#endif + }; + + for (unsigned int i = 0; i < ARRAY_LENGTH(BUTTON_COMBOS); ++i) { + if (state->buttons_held_bitset == BUTTON_COMBOS[i].desired_bitset) { + PBL_LOG_DBG("Starting timer for combo #%d", i); + + new_timer_start(state->combo_timer, COMBO_HOLD_MS, prv_timeout_expired, + BUTTON_COMBOS[i].callback, 0); + return; + } + } + + PBL_LOG_DBG("Stopping combo timer"); + + // No combo found, cancel the timer. It's harmless to call this if the timer isn't running. + new_timer_stop(state->combo_timer); +} + +void getting_started_button_combo_button_pressed(GettingStartedButtonComboState *state, + ButtonId button_id) { + bitset8_set(&state->buttons_held_bitset, button_id); + prv_update_state(state); +} + +void getting_started_button_combo_button_released(GettingStartedButtonComboState *state, + ButtonId button_id) { + bitset8_clear(&state->buttons_held_bitset, button_id); + prv_update_state(state); +} diff --git a/src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.h b/src/fw/apps/prf/recovery_first_use/getting_started_button_combo.h similarity index 79% rename from src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.h rename to src/fw/apps/prf/recovery_first_use/getting_started_button_combo.h index 24bda27d66..6a0f1bfa38 100644 --- a/src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.h +++ b/src/fw/apps/prf/recovery_first_use/getting_started_button_combo.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "util/bitset.h" //! @file prf_button_combo.h diff --git a/src/fw/apps/prf/recovery_first_use/recovery_first_use.c b/src/fw/apps/prf/recovery_first_use/recovery_first_use.c new file mode 100644 index 0000000000..53b921557c --- /dev/null +++ b/src/fw/apps/prf/recovery_first_use/recovery_first_use.c @@ -0,0 +1,384 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "recovery_first_use.h" + +#include "getting_started_button_combo.h" + +#include "apps/core/spinner_ui_window.h" +#include "applib/fonts/fonts.h" +#include "comm/ble/gap_le_connection.h" +#include "comm/ble/gap_le_device_name.h" +#include "comm/ble/gap_le_connect.h" +#include "drivers/backlight.h" +#include "mfg/mfg_info.h" +#include "mfg/mfg_serials.h" +#include "process_management/app_install_manager.h" +#include "process_management/app_manager.h" + +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" + +#include "system/logging.h" +#include "system/passert.h" + +#include "applib/app.h" +#include "applib/event_service_client.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/layer.h" +#include "applib/ui/qr_code.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window.h" +#include "applib/ui/window_private.h" +#include "applib/ui/window_stack.h" +#include "process_state/app_state/app_state.h" + +#include "apps/system/settings/time.h" + +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/comm_session/session.h" + +#include "git_version.auto.h" + +#include +#include +#include + +#define QR_URL_BUFFER_SIZE 72 +#define NAME_BUFFER_SIZE (BT_DEVICE_NAME_BUFFER_SIZE + 2) + +typedef struct RecoveryFUAppData { + Window launch_app_window; + + QRCode qr_code; + char qr_url_buffer[QR_URL_BUFFER_SIZE]; + bool is_showing_version; + TextLayer name_text_layer; + char name_text_buffer[NAME_BUFFER_SIZE]; + + AppTimer *spinner_close_timer; + + //! Is the mobile app currently connected (comm session is up?) + bool is_pebble_mobile_app_connected; + //! Has the mobile app ever connected during this boot? Used to avoid flickering the layout + //! for brief disconnects. + bool has_pebble_mobile_app_connected; + bool is_pairing_allowed; + bool spinner_is_visible; + bool spinner_should_close; + + EventServiceInfo pebble_mobile_app_event_info; + EventServiceInfo bt_connection_event_info; + EventServiceInfo pebble_gather_logs_event_info; + EventServiceInfo ble_device_name_updated_event_info; + + GettingStartedButtonComboState button_combo_state; +} RecoveryFUAppData; + +static const char *s_qr_url_fmt = "https://qr.repebble.com/?sn=%s&model=%s"; + +// Unfortunately, the event_service_client_subscribe doesn't take a void *context... +static RecoveryFUAppData *s_fu_app_data; + +static void prv_update_name_text(RecoveryFUAppData *data); + +//////////////////////////////////////////////////////////// +// Spinner Logic + +static void prv_pop_spinner(void *not_used) { + if (s_fu_app_data && s_fu_app_data->spinner_should_close) { + app_window_stack_pop(false /* animated */); + s_fu_app_data->spinner_is_visible = false; + s_fu_app_data->spinner_should_close = false; + } +} + +static void prv_show_spinner(RecoveryFUAppData *data) { + if (!data->spinner_is_visible) { + Window *spinner_window = spinner_ui_window_get(PBL_IF_COLOR_ELSE(GColorRed, GColorDarkGray)); + app_window_stack_push(spinner_window, false /* animated */); + } + data->spinner_is_visible = true; + data->spinner_should_close = false; +} + +static void prv_hide_spinner(RecoveryFUAppData *data) { + data->spinner_should_close = true; + data->spinner_close_timer = app_timer_register(3000, prv_pop_spinner, data); +} + +//////////////////////////////////////////////////////////// +// Button Handlers +static void prv_select_combo_callback(void *cb_data) { + // When the user holds select for a long period of time, toggle between showing the help URL + // and the version of the firmware. + + RecoveryFUAppData *data = app_state_get_user_data(); + data->is_showing_version = !data->is_showing_version; + + prv_update_name_text(data); +} + +static void prv_raw_down_handler(ClickRecognizerRef recognizer, void *context) { + RecoveryFUAppData *data = app_state_get_user_data(); + + getting_started_button_combo_button_pressed(&data->button_combo_state, + click_recognizer_get_button_id(recognizer)); +} + +static void prv_raw_up_handler(ClickRecognizerRef recognizer, void *context) { + RecoveryFUAppData *data = app_state_get_user_data(); + + getting_started_button_combo_button_released(&data->button_combo_state, + click_recognizer_get_button_id(recognizer)); +} + +static void prv_click_configure(void* context) { + window_raw_click_subscribe(BUTTON_ID_UP, prv_raw_down_handler, prv_raw_up_handler, NULL); + window_raw_click_subscribe(BUTTON_ID_SELECT, prv_raw_down_handler, prv_raw_up_handler, NULL); + window_raw_click_subscribe(BUTTON_ID_DOWN, prv_raw_down_handler, prv_raw_up_handler, NULL); +} + +//////////////////////////////////////////////////////////// +// Windows + +static void prv_update_name_text(RecoveryFUAppData *data) { + const GAPLEConnection *gap_conn = gap_le_connection_any(); + + // Set the name text + if (data->is_showing_version) { + size_t len = MIN(strlen(GIT_TAG), sizeof(data->name_text_buffer) - 1); + memcpy(data->name_text_buffer, GIT_TAG, len); + data->name_text_buffer[len] = '\0'; + } else if ((comm_session_get_system_session() != NULL) && (gap_conn != NULL)) { + // If we have connected to a device and we have a connection to the mobile app, show the device + // name (we are required to have a connection to mobile app to get the name). + gap_le_connection_copy_device_name(gap_conn, data->name_text_buffer, + BT_DEVICE_NAME_BUFFER_SIZE); + } else { + // If we aren't connected and/or don't have a session, display the name of the device + // so it's easier for a user to figure out what they should be trying to connect to + bt_local_id_copy_device_name(data->name_text_buffer, false); + + // For debugging purposes, we are going to add -'s to the beginning and end of the name + // if we are connected to a BLE device but don't have a session + if (gap_le_connect_is_connected_as_slave()) { + memmove(&data->name_text_buffer[1], &data->name_text_buffer[0], BT_DEVICE_NAME_BUFFER_SIZE); + data->name_text_buffer[0] = '-'; + strcat(data->name_text_buffer, "-"); + } + } + text_layer_set_text(&data->name_text_layer, data->name_text_buffer); +} + +static void prv_window_load(Window* window) { + struct RecoveryFUAppData *data = (struct RecoveryFUAppData*) window_get_user_data(window); + + char serial_number[MFG_SERIAL_NUMBER_SIZE + 1]; + char model_name[MFG_INFO_MODEL_STRING_LENGTH]; + + mfg_info_get_serialnumber(serial_number, sizeof(serial_number)); + mfg_info_get_model(model_name); + + snprintf(data->qr_url_buffer, QR_URL_BUFFER_SIZE, s_qr_url_fmt, serial_number, model_name); + + QRCode* qr_code = &data->qr_code; + qr_code_init_with_parameters(qr_code, +#if PBL_ROUND +#define QR_CODE_SIZE ((window->layer.bounds.size.w * 10) / 14) + &GRect((window->layer.bounds.size.w - QR_CODE_SIZE) / 2, + (window->layer.bounds.size.h - QR_CODE_SIZE) / 2, + QR_CODE_SIZE, QR_CODE_SIZE), +#else + &GRect(10, 10, window->layer.bounds.size.w - 20, + window->layer.bounds.size.h - 30), +#endif + data->qr_url_buffer, strlen(data->qr_url_buffer), QRCodeECCMedium, + GColorBlack, GColorWhite); + layer_add_child(&window->layer, &qr_code->layer); + +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + const uint16_t name_height = 30; +#else + const uint16_t name_height = 20; +#endif + + TextLayer* name_text_layer = &data->name_text_layer; + text_layer_init_with_parameters(name_text_layer, + &GRect(0, window->layer.bounds.size.h - + PBL_IF_RECT_ELSE(name_height, name_height + 10), + window->layer.bounds.size.w, name_height), + NULL, +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + fonts_get_system_font(FONT_KEY_GOTHIC_24), +#else + fonts_get_system_font(FONT_KEY_GOTHIC_14), +#endif + GColorBlack, GColorWhite, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->layer, &name_text_layer->layer); + data->is_showing_version = false; + + prv_update_name_text(data); +} + +static void prv_push_window(struct RecoveryFUAppData* data) { + Window* window = &data->launch_app_window; + + window_init(window, WINDOW_NAME("First Use / Recovery")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers){ + .load = prv_window_load, + }); + window_set_click_config_provider_with_context(window, prv_click_configure, window); + + window_set_fullscreen(window, true); + window_set_overrides_back_button(window, true); + + const bool animated = false; + app_window_stack_push(window, animated); +} + +//////////////////// +// App Event Handler + Loop + +static void prv_allow_pairing(RecoveryFUAppData* data, bool allow) { + if (data->is_pairing_allowed == allow) { + return; + } + data->is_pairing_allowed = allow; + if (allow) { + bt_pairability_use(); + } else { + bt_pairability_release(); + } +} + +static void prv_pebble_mobile_app_event_handler(PebbleEvent *event, void *context) { + if (!s_fu_app_data) { + return; + } + + if (!event->bluetooth.comm_session_event.is_system) { + return; + } + + const bool is_connected = event->bluetooth.comm_session_event.is_open; + + s_fu_app_data->is_pebble_mobile_app_connected = event->bluetooth.comm_session_event.is_open; + if (is_connected) { + s_fu_app_data->has_pebble_mobile_app_connected = true; + gap_le_device_name_request_all(); + } + prv_update_name_text(s_fu_app_data); +} + +static void prv_bt_event_handler(PebbleEvent *event, void *context) { + if (!s_fu_app_data) { + return; + } + prv_update_name_text(s_fu_app_data); +} + +static void prv_gather_debug_info_event_handler(PebbleEvent *event, void *context) { + if (!s_fu_app_data) { + return; + } + if (event->debug_info.state == DebugInfoStateStarted) { + prv_show_spinner(s_fu_app_data); + } else { + prv_hide_spinner(s_fu_app_data); + } +} + +//////////////////// +// App boilerplate + +static void handle_init(void) { + launcher_block_popups(true); + + RecoveryFUAppData *data = app_malloc_check(sizeof(RecoveryFUAppData)); + s_fu_app_data = data; + + *data = (RecoveryFUAppData){}; + app_state_set_user_data(data); + + const bool is_connected = (comm_session_get_system_session() != NULL); + data->is_pebble_mobile_app_connected = is_connected; + prv_allow_pairing(data, !is_connected); + + data->pebble_mobile_app_event_info = (EventServiceInfo) { + .type = PEBBLE_COMM_SESSION_EVENT, + .handler = prv_pebble_mobile_app_event_handler, + }; + event_service_client_subscribe(&data->pebble_mobile_app_event_info); + + data->pebble_gather_logs_event_info = (EventServiceInfo) { + .type = PEBBLE_GATHER_DEBUG_INFO_EVENT, + .handler = prv_gather_debug_info_event_handler, + }; + event_service_client_subscribe(&data->pebble_gather_logs_event_info); + + data->bt_connection_event_info = (EventServiceInfo) { + .type = PEBBLE_BT_CONNECTION_EVENT, + .handler = prv_bt_event_handler, + }; + event_service_client_subscribe(&data->bt_connection_event_info); + + data->ble_device_name_updated_event_info = (EventServiceInfo) { + .type = PEBBLE_BLE_DEVICE_NAME_UPDATED_EVENT, + .handler = prv_bt_event_handler, + }; + event_service_client_subscribe(&data->ble_device_name_updated_event_info); + + getting_started_button_combo_init(&data->button_combo_state, prv_select_combo_callback); + + prv_push_window(data); +} + +static void handle_deinit(void) { + RecoveryFUAppData *data = app_state_get_user_data(); + + getting_started_button_combo_deinit(&data->button_combo_state); + + event_service_client_unsubscribe(&data->pebble_mobile_app_event_info); + event_service_client_unsubscribe(&data->bt_connection_event_info); + event_service_client_unsubscribe(&data->pebble_gather_logs_event_info); + event_service_client_unsubscribe(&data->ble_device_name_updated_event_info); + + app_window_stack_pop_all(false); + + prv_allow_pairing(data, false); + + app_free(data); + s_fu_app_data = NULL; + + launcher_block_popups(false); +} + +static void prv_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* recovery_first_use_app_get_app_info(void) { + static const PebbleProcessMdSystem s_recovery_first_use_app = { + .common = { + .main_func = prv_main, + .visibility = ProcessVisibilityHidden, + // UUID: 85b80081-d78f-41aa-96fa-a821c79f3f0f + .uuid = { + 0x85, 0xb8, 0x00, 0x81, 0xd7, 0x8f, 0x41, 0xaa, + 0x96, 0xfa, 0xa8, 0x21, 0xc7, 0x9f, 0x3f, 0x0f + }, + }, + .name = "Getting Started", + .run_level = ProcessAppRunLevelSystem, + }; + return (const PebbleProcessMd*) &s_recovery_first_use_app; +} diff --git a/src/fw/apps/prf/recovery_first_use/recovery_first_use.h b/src/fw/apps/prf/recovery_first_use/recovery_first_use.h new file mode 100644 index 0000000000..96d5da5873 --- /dev/null +++ b/src/fw/apps/prf/recovery_first_use/recovery_first_use.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +typedef struct PebbleProcessMd PebbleProcessMd; + +const PebbleProcessMd* recovery_first_use_app_get_app_info(void); diff --git a/src/fw/apps/prf_apps/mfg_accel_app.c b/src/fw/apps/prf_apps/mfg_accel_app.c deleted file mode 100644 index 1ff00d7a4f..0000000000 --- a/src/fw/apps/prf_apps/mfg_accel_app.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/tick_timer_service.h" -#include "util/trig.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "applib/ui/path_layer.h" -#include "applib/ui/text_layer.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "drivers/accel.h" -#include "process_state/app_state/app_state.h" -#include "process_management/pebble_process_md.h" -#include "services/common/evented_timer.h" -#include "util/bitset.h" -#include "util/size.h" - -#include -#include -#include - -#define STATUS_STRING_LEN 50 - -static EventedTimerID s_timer; - -typedef struct { - Window window; - - TextLayer title; - TextLayer status; - char status_string[STATUS_STRING_LEN]; -} AppData; - -static void prv_update_display(void *context) { - AppData *data = context; - - AccelDriverSample sample; - - int ret = accel_peek(&sample); - - if (ret == 0) { - sniprintf(data->status_string, sizeof(data->status_string), - "X: %"PRIi16"\nY: %"PRIi16"\nZ:%"PRIi16"", sample.x, sample.y, sample.z); - } else { - sniprintf(data->status_string, sizeof(data->status_string), - "ACCEL ERROR:\n%d", ret); - } - - text_layer_set_text(&data->status, data->status_string); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) {}; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "ACCEL TEST"); - layer_add_child(&window->layer, &title->layer); - - TextLayer *status = &data->status; - text_layer_init(status, - &GRect(5, 40, - window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 40)); - text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(status, GTextAlignmentCenter); - layer_add_child(&window->layer, &status->layer); - - app_window_stack_push(window, true /* Animated */); - - s_timer = evented_timer_register(100, true /* repeating */, prv_update_display, data); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); - - evented_timer_cancel(s_timer); -} - -const PebbleProcessMd* mfg_accel_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: ED2E214A-D4B5-4360-B5EC-612B9E49FB95 - .common.uuid = { 0xED, 0x2E, 0x21, 0x4A, 0xD4, 0xB5, 0x43, 0x60, - 0xB5, 0xEC, 0x61, 0x2B, 0x9E, 0x49, 0xFB, 0x95, - }, - .name = "MfgAccel", - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/prf_apps/mfg_accel_app.h b/src/fw/apps/prf_apps/mfg_accel_app.h deleted file mode 100644 index 2dffacfda9..0000000000 --- a/src/fw/apps/prf_apps/mfg_accel_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_accel_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_als_app.c b/src/fw/apps/prf_apps/mfg_als_app.c deleted file mode 100644 index fbcab2604e..0000000000 --- a/src/fw/apps/prf_apps/mfg_als_app.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_als_app.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "drivers/ambient_light.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_info.h" -#include "mfg/results_ui.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" - -#include -#include - -#define AMBIENT_READING_STR_LEN 32 -typedef struct { - Window *window; - TextLayer *title_text_layer; - TextLayer *reading_text_layer; - char ambient_reading[AMBIENT_READING_STR_LEN]; - uint32_t latest_als_value; -#if MFG_INFO_RECORDS_TEST_RESULTS - MfgResultsUI results_ui; -#endif -} AmbientLightAppData; - -static void prv_update_reading(AmbientLightAppData *data) { - uint32_t level = ambient_light_get_light_level(); - snprintf(data->ambient_reading, AMBIENT_READING_STR_LEN, "%"PRIu32, level); - data->latest_als_value = level; -} - -static void prv_timer_callback(void *cb_data) { - AmbientLightAppData *data = app_state_get_user_data(); - - prv_update_reading(data); - layer_mark_dirty(window_get_root_layer(data->window)); - - app_timer_register(500, prv_timer_callback, NULL); -} - -#if MFG_INFO_RECORDS_TEST_RESULTS -static void prv_record_als_reading(void) { - AmbientLightAppData *data = app_state_get_user_data(); - mfg_info_write_als_result(data->latest_als_value); -} -#endif - -static void prv_handle_init(void) { - AmbientLightAppData *data = task_zalloc_check(sizeof(AmbientLightAppData)); - - data->window = window_create(); - - Layer *window_layer = window_get_root_layer(data->window); - GRect bounds = window_layer->bounds; - bounds.origin.y += 40; - - data->title_text_layer = text_layer_create(bounds); - text_layer_set_font(data->title_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text(data->title_text_layer, "ALS"); - text_layer_set_text_alignment(data->title_text_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(data->title_text_layer)); - - bounds.origin.y += 30; - data->reading_text_layer = text_layer_create(bounds); - - prv_update_reading(data); - - text_layer_set_font(data->reading_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text(data->reading_text_layer, data->ambient_reading); - text_layer_set_text_alignment(data->reading_text_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(data->reading_text_layer)); - -#if MFG_INFO_RECORDS_TEST_RESULTS - mfg_results_ui_init(&data->results_ui, MfgTest_ALS, data->window); - mfg_results_ui_set_callback(&data->results_ui, prv_record_als_reading); -#endif - - app_state_set_user_data(data); - app_window_stack_push(data->window, true); - - app_timer_register(10, prv_timer_callback, NULL); -} - -static void prv_handle_deinit(void) { - AmbientLightAppData *data = app_state_get_user_data(); - text_layer_destroy(data->title_text_layer); - text_layer_destroy(data->reading_text_layer); - window_destroy(data->window); - task_free(data); -} - -static void prv_main(void) { - prv_handle_init(); - app_event_loop(); - prv_handle_deinit(); -} - -const PebbleProcessMd* mfg_als_app_get_info(void) { - static const PebbleProcessMdSystem s_ambient_light_info = { - .common.main_func = prv_main, - .name = "MfgALS" - }; - return (const PebbleProcessMd*) &s_ambient_light_info; -} diff --git a/src/fw/apps/prf_apps/mfg_als_app.h b/src/fw/apps/prf_apps/mfg_als_app.h deleted file mode 100644 index 316fb9864d..0000000000 --- a/src/fw/apps/prf_apps/mfg_als_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_als_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_backlight_app.c b/src/fw/apps/prf_apps/mfg_backlight_app.c deleted file mode 100644 index 85e468fc71..0000000000 --- a/src/fw/apps/prf_apps/mfg_backlight_app.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/graphics/graphics.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "services/common/light.h" -#include "drivers/led_controller.h" - -#define BACKLIGHT_COLOR_WHITE 0xD0D0D0 -#define BACKLIGHT_COLOR_RED 0xFF0000 -#define BACKLIGHT_COLOR_GREEN 0x00FF00 -#define BACKLIGHT_COLOR_BLUE 0x0000FF -#define BACKLIGHT_COLOR_BLACK 0x000000 - -typedef enum { - TestPattern_White, - TestPattern_Red, - TestPattern_Green, - TestPattern_Blue, - TestPattern_Black, - NumTestPatterns -} TestPattern; - -typedef struct { - Window window; - TestPattern test_pattern; -} AppData; - - -static void prv_update_proc(struct Layer *layer, GContext* ctx) { - AppData *app_data = app_state_get_user_data(); - graphics_context_set_fill_color(ctx, GColorWhite); - graphics_fill_rect(ctx, &layer->bounds); - - PBL_LOG(LOG_LEVEL_INFO, "backlight id:%d", app_data->test_pattern); - switch (app_data->test_pattern) { - case TestPattern_White: - led_controller_rgb_set_color(BACKLIGHT_COLOR_WHITE); - break; - case TestPattern_Red: - led_controller_rgb_set_color(BACKLIGHT_COLOR_RED); - break; - case TestPattern_Green: - led_controller_rgb_set_color(BACKLIGHT_COLOR_GREEN); - break; - case TestPattern_Blue: - led_controller_rgb_set_color(BACKLIGHT_COLOR_BLUE); - break; - case TestPattern_Black: - led_controller_rgb_set_color(BACKLIGHT_COLOR_BLACK); - break; - default: - break; - } -} - -static void prv_button_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - app_data->test_pattern = (app_data->test_pattern + 1) % NumTestPatterns; - - layer_mark_dirty(&app_data->window.layer); -} - -static void prv_change_pattern(void *data) { - AppData *app_data = app_state_get_user_data(); - - app_data->test_pattern = (TestPattern) data; - - layer_mark_dirty(&app_data->window.layer); -} - -static void prv_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_button_click_handler); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .test_pattern = (TestPattern) app_manager_get_task_context()->args - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - window_set_click_config_provider(window, prv_config_provider); - - Layer *layer = window_get_root_layer(window); - layer_set_update_proc(layer, prv_update_proc); - - app_window_stack_push(window, true /* Animated */); -} - -static void s_main(void) { - light_enable(true); - - prv_handle_init(); - - app_event_loop(); - - led_controller_rgb_set_color(0xd0d0d0); - light_enable(false); -} - -const PebbleProcessMd* mfg_backlight_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: 2d825a20-2fa3-4b26-be6f-ab41d1280c73 - .common.uuid = { 0x2d, 0x82, 0x5a, 0x20, 0x2f, 0xa3, 0x4b, 0x26, - 0xbe, 0x6f, 0xab, 0x41, 0xd1, 0x28, 0x0c, 0x73 }, - .name = "MfgBacklight", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_backlight_app.h b/src/fw/apps/prf_apps/mfg_backlight_app.h deleted file mode 100644 index cf69834bce..0000000000 --- a/src/fw/apps/prf_apps/mfg_backlight_app.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file mfg_display_app.h -//! -//! Test app that shows various display pattern - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_backlight_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_bt_device_name_app.c b/src/fw/apps/prf_apps/mfg_bt_device_name_app.c deleted file mode 100644 index 2ff717e5cd..0000000000 --- a/src/fw/apps/prf_apps/mfg_bt_device_name_app.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/qr_code.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "applib/ui/text_layer.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "process_management/pebble_process_md.h" -#include "services/common/bluetooth/local_id.h" -#include "util/bitset.h" -#include "util/size.h" - -#include -#include -#include - -typedef struct { - Window window; - - QRCode qr_code; - TextLayer name; - - char name_buffer[BT_DEVICE_NAME_BUFFER_SIZE]; -} AppData; - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - bt_local_id_copy_device_name(data->name_buffer, false); - - QRCode* qr_code = &data->qr_code; - qr_code_init_with_parameters(qr_code, - &GRect(10, 10, window->layer.bounds.size.w - 20, - window->layer.bounds.size.h - 30), - data->name_buffer, strlen(data->name_buffer), QRCodeECCMedium, - GColorBlack, GColorWhite); - layer_add_child(&window->layer, &qr_code->layer); - - TextLayer* name = &data->name; - text_layer_init_with_parameters(name, - &GRect(0, window->layer.bounds.size.h - 20, - window->layer.bounds.size.w, 20), - data->name_buffer, fonts_get_system_font(FONT_KEY_GOTHIC_14), - GColorBlack, GColorWhite, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&window->layer, &name->layer); - - app_window_stack_push(window, true /* Animated */); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd* mfg_bt_device_name_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: 31b5a232-d638-4ccb-b89a-910202d85a1f - .common.uuid = { 0x31, 0xb5, 0xa2, 0x32, 0xd6, 0x38, 0x4c, 0xcb, - 0xb8, 0x9a, 0x91, 0x02, 0x02, 0xd8, 0x5a, 0x1f }, - .name = "MfgBTDeviceName", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_bt_device_name_app.h b/src/fw/apps/prf_apps/mfg_bt_device_name_app.h deleted file mode 100644 index e95759c4a8..0000000000 --- a/src/fw/apps/prf_apps/mfg_bt_device_name_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_bt_device_name_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_bt_sig_rf_app.c b/src/fw/apps/prf_apps/mfg_bt_sig_rf_app.c deleted file mode 100644 index 8f42dfb8f9..0000000000 --- a/src/fw/apps/prf_apps/mfg_bt_sig_rf_app.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/bluetooth/bt_compliance_tests.h" -#include "system/logging.h" - -typedef struct { - Window window; - - TextLayer title; - - //! How many times we've vibrated - int vibe_count; -} AppData; - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .vibe_count = 0 - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "BT_SIG_RF\nTEST"); - layer_add_child(&window->layer, &title->layer); - - app_window_stack_push(window, true /* Animated */); - - // Enter the bluetooth test mode - if (!bt_test_bt_sig_rf_test_mode()) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to enter bt_sig_rf!"); - } -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); - - // Bring us out of test mode. Do this on the kernel main thread as this app is currently - // closing and if we take too long we'll get force-killed. - bt_ctl_reset_bluetooth(); -} - -const PebbleProcessMd* mfg_bt_sig_rf_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: 278f66e0-11a1-4139-a5f4-fceb64efcf55 - .common.uuid = { 0x27, 0x8f, 0x66, 0xe0, 0x11, 0xa1, 0x41, 0x39, - 0xa5, 0xf4, 0xfc, 0xeb, 0x64, 0xef, 0xcf, 0x55 }, - .name = "MfgBtSigRf", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_bt_sig_rf_app.h b/src/fw/apps/prf_apps/mfg_bt_sig_rf_app.h deleted file mode 100644 index 54b3924aa0..0000000000 --- a/src/fw/apps/prf_apps/mfg_bt_sig_rf_app.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file mfg_bt_sig_rf_app.h -//! -//! Boring test app that puts us into bt_sig_rf mode for testing. - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_bt_sig_rf_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_button_app.c b/src/fw/apps/prf_apps/mfg_button_app.c deleted file mode 100644 index bb74eececa..0000000000 --- a/src/fw/apps/prf_apps/mfg_button_app.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/tick_timer_service.h" -#include "util/trig.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "applib/ui/path_layer.h" -#include "applib/ui/text_layer.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_info.h" -#include "process_state/app_state/app_state.h" -#include "process_management/pebble_process_md.h" -#include "util/bitset.h" -#include "util/size.h" - -#include -#include -#include - -// How long after test pass / fail to wait before popping the window -#define WINDOW_POP_TIME_S (3) - -typedef struct { - Window window; - - PathLayer arrows[NUM_BUTTONS]; - - //! bitset of buttons pressed so far - uint32_t buttons_pressed; - - TextLayer title; - TextLayer status; - char status_string[35]; - - uint32_t seconds_remaining; - bool test_complete; -} AppData; - -static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { - AppData *data = app_state_get_user_data(); - - if (data->test_complete) { - if (--data->seconds_remaining == 0) { - app_window_stack_pop(true); - } - return; - } - - const bool test_passed = (data->buttons_pressed == 0x0f); - if (data->seconds_remaining == 0 || test_passed) { - data->test_complete = true; - -#if MFG_INFO_RECORDS_TEST_RESULTS - mfg_info_write_test_result(MfgTest_Buttons, test_passed); -#endif - - if (test_passed) { - // pass - sniprintf(data->status_string, sizeof(data->status_string), "PASS!"); - } else { - // fail - sniprintf(data->status_string, sizeof(data->status_string), "FAIL!"); - } - data->seconds_remaining = WINDOW_POP_TIME_S; - } else { - sniprintf(data->status_string, sizeof(data->status_string), - "TIME REMAINING: %"PRIu32"s", data->seconds_remaining); - data->seconds_remaining--; - } - - text_layer_set_text(&data->status, data->status_string); -} - -static void prv_button_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - ButtonId button_id_pressed = click_recognizer_get_button_id(recognizer); - bitset32_set(&app_data->buttons_pressed, button_id_pressed); - layer_set_hidden((struct Layer*)&app_data->arrows[button_id_pressed], true); - - if (app_data->test_complete) { - app_window_stack_remove(&app_data->window, false /* Animated */); - } -} - -static void prv_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_BACK, prv_button_click_handler); - window_single_click_subscribe(BUTTON_ID_UP, prv_button_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_button_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_button_click_handler); -} - -static void init_arrow_layer_for_button(AppData *data, ButtonId id) { - static GPoint ARROW_PATH_POINTS[] = - {{0, 7}, {14, 7}, {14, 0}, {26, 12}, {14, 24}, {14, 17}, {0, 17}}; - - static const GPathInfo ARROW_PATH_INFO = { - .num_points = ARRAY_LENGTH(ARROW_PATH_POINTS), - .points = ARROW_PATH_POINTS - }; - -#define ARROW_SIZE {26, 24} - static const GRect ARROW_RECTS[] = { - {{ 5, 55}, ARROW_SIZE}, // BACK - {{112, 30}, ARROW_SIZE}, // UP - {{112, 90}, ARROW_SIZE}, // SELECT - {{112, 140}, ARROW_SIZE}, // DOWN - }; - - PathLayer *arrow = &data->arrows[id]; - path_layer_init(arrow, &ARROW_PATH_INFO); - path_layer_set_fill_color(arrow, GColorBlack); - path_layer_set_stroke_color(arrow, GColorBlack); - - layer_set_frame(&arrow->layer, &ARROW_RECTS[id]); - - if (id == BUTTON_ID_BACK) { - gpath_rotate_to(&arrow->path, (TRIG_MAX_ANGLE / 2)); - gpath_move_to(&arrow->path, GPoint(26, 24)); - } - - layer_add_child(&data->window.layer, &arrow->layer); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .seconds_remaining = 10, - .buttons_pressed = 0, - .test_complete = false, - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - window_set_overrides_back_button(window, true); - window_set_click_config_provider(window, prv_config_provider); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "BUTTON TEST"); - layer_add_child(&window->layer, &title->layer); - - TextLayer *status = &data->status; - text_layer_init(status, - &GRect(5, 110, - window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 110)); - text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); - text_layer_set_text_alignment(status, GTextAlignmentCenter); - layer_add_child(&window->layer, &status->layer); - - for (ButtonId id = 0; id < NUM_BUTTONS; ++id) { - init_arrow_layer_for_button(data, id); - } - - app_window_stack_push(window, true /* Animated */); - - tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd* mfg_button_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: eed03647-fa9e-4bae-9254-608aa297e4e4 - .common.uuid = { 0xee, 0xd0, 0x36, 0x47, 0xfa, 0x9e, 0x4b, 0xae, - 0x92, 0x54, 0x60, 0x8a, 0xa2, 0x97, 0xe4, 0xe4}, - .name = "MfgButton", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_button_app.h b/src/fw/apps/prf_apps/mfg_button_app.h deleted file mode 100644 index 0e76f95881..0000000000 --- a/src/fw/apps/prf_apps/mfg_button_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_button_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_certification_app.c b/src/fw/apps/prf_apps/mfg_certification_app.c deleted file mode 100644 index 3f2a855c9f..0000000000 --- a/src/fw/apps/prf_apps/mfg_certification_app.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/app_light.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/inverter_layer.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/vibes.h" -#include "applib/ui/window.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "applib/tick_timer_service.h" -#include "util/size.h" - -//! How long to wait between vibes -static int INTER_VIBE_PERIOD_MS = 5000; - -typedef struct { - Window window; - - TextLayer title; - - InverterLayer inverter; -} AppData; - -static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { - AppData *data = app_state_get_user_data(); - - Layer *inverter = &data->inverter.layer; - layer_set_hidden(inverter, !layer_get_hidden(inverter)); -} - -static void prv_vibe_timer_callback(void *data) { - static const uint32_t SECOND_PULSE_DURATIONS[] = { 1000 }; - VibePattern pat = { - .durations = SECOND_PULSE_DURATIONS, - .num_segments = ARRAY_LENGTH(SECOND_PULSE_DURATIONS) - }; - vibes_enqueue_custom_pattern(pat); - - app_timer_register(INTER_VIBE_PERIOD_MS, prv_vibe_timer_callback, NULL); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.frame); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "Certification"); - layer_add_child(&window->layer, &title->layer); - - InverterLayer *inverter = &data->inverter; - inverter_layer_init(inverter, &window->layer.frame); - layer_add_child(&window->layer, &inverter->layer); - - app_window_stack_push(window, true /* Animated */); - - prv_vibe_timer_callback(NULL); - app_light_enable(true); - tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd* mfg_certification_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: 266135d1-827f-4f64-9752-fffe604e1dbe - .common.uuid = { 0x26, 0x61, 0x35, 0xd1, 0x82, 0x7f, 0x4f, 0x64, - 0x97, 0x52, 0xff, 0xfe, 0x60, 0x4e, 0x1d, 0xbe }, - .name = "MfgCertification", - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/prf_apps/mfg_certification_app.h b/src/fw/apps/prf_apps/mfg_certification_app.h deleted file mode 100644 index c7d97523f7..0000000000 --- a/src/fw/apps/prf_apps/mfg_certification_app.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file mfg_certification_app.h -//! -//! Boring test app that tweaks various hardware levers to create a worst case scenario for -//! certification testing. - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_certification_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_display_app.c b/src/fw/apps/prf_apps/mfg_display_app.c deleted file mode 100644 index 87d5711599..0000000000 --- a/src/fw/apps/prf_apps/mfg_display_app.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/vibes.h" -#include "applib/ui/window.h" -#include "console/prompt.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "mfg/results_ui.h" -#include "process_management/app_manager.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "resource/system_resource.h" -#include "services/common/light.h" -#include "util/size.h" - -typedef enum { - TestPattern_Black, - TestPattern_Gray, - TestPattern_White, - TestPattern_Crosshair, -#if PBL_COLOR - TestPattern_Red, - TestPattern_Green, - TestPattern_Blue, -#endif -#if PBL_ROUND - TestPattern_Border1, - TestPattern_Border2, - TestPattern_Border3, -#endif -#if PBL_COLOR - TestPattern_Pinwheel, - TestPattern_Veggies, -#endif - - NumTestPatterns -} TestPattern; - -typedef struct { - Window window; - - TestPattern test_pattern; - -#if MFG_INFO_RECORDS_TEST_RESULTS - Window results_window; - MfgResultsUI results_ui; -#endif -} AppData; - -static void prv_draw_solid(Layer *layer, GContext *ctx, GColor color) { - graphics_context_set_fill_color(ctx, color); - graphics_fill_rect(ctx, &layer->bounds); -} - -#if PBL_ROUND - -static void prv_draw_round_border(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { - for (int i = 0; i < layer->bounds.size.h / 2 - radial_padding_size; ++i) { - const GBitmapDataRowInfoInternal *data_row_infos = g_gbitmap_spalding_data_row_infos; - const uint8_t mask = data_row_infos[i].min_x + radial_padding_size; - const int offset = i + radial_padding_size; - // Draw both row-wise and column-wise to fill in any discontinuities - // in the border circle. - - // Top-left quadrant - graphics_draw_pixel(ctx, GPoint(mask, offset)); - graphics_draw_pixel(ctx, GPoint(offset, mask)); - // Top-right quadrant - graphics_draw_pixel(ctx, GPoint(mask, layer->bounds.size.h - offset - 1)); - graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - offset - 1, mask)); - // Bottom-left quadrant - graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - mask - 1, offset)); - graphics_draw_pixel(ctx, GPoint(offset, layer->bounds.size.h - mask - 1)); - // Bottom-right quadrant - graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - mask - 1, - layer->bounds.size.h - offset - 1)); - graphics_draw_pixel(ctx, GPoint(layer->bounds.size.w - offset - 1, - layer->bounds.size.h - mask - 1)); - } -} - -static void prv_draw_border(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { - if (radial_padding_size != 0) { - prv_draw_round_border(layer, ctx, 0); - } - prv_draw_round_border(layer, ctx, radial_padding_size); - - // Draw letter to identify screen - if (radial_padding_size >= 2) { - GRect identifier_area = GRect(40, 40, 20, 20); - char identifier[] = {'A' + radial_padding_size - 2, 0}; - graphics_context_set_text_color(ctx, GColorWhite); - graphics_draw_text(ctx, identifier, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), - identifier_area, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - } -} - -#else - -static void prv_draw_border(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { - graphics_draw_rect(ctx, &layer->bounds); -} - -#endif - -static void prv_draw_crosshair_screen(Layer *layer, GContext *ctx, uint8_t radial_padding_size) { - graphics_context_set_fill_color(ctx, GColorBlack); - graphics_fill_rect(ctx, &layer->bounds); - - // Draw crosshair - graphics_context_set_stroke_color(ctx, GColorWhite); - graphics_draw_line( - ctx, GPoint(layer->bounds.size.w / 2, radial_padding_size), - GPoint(layer->bounds.size.w / 2, layer->bounds.size.h - radial_padding_size - 1)); - graphics_draw_line( - ctx, GPoint(radial_padding_size, layer->bounds.size.h / 2), - GPoint(layer->bounds.size.w - radial_padding_size - 1, layer->bounds.size.h / 2)); - - prv_draw_border(layer, ctx, radial_padding_size); -} - -#if PBL_COLOR -static void prv_draw_bitmap(struct Layer *layer, GContext *ctx, uint32_t res) { - GBitmap *bitmap = gbitmap_create_with_resource(res); - graphics_draw_bitmap_in_rect(ctx, bitmap, &layer->bounds); - gbitmap_destroy(bitmap); -} -#endif - -static void prv_update_proc(struct Layer *layer, GContext* ctx) { - AppData *app_data = app_state_get_user_data(); - - switch (app_data->test_pattern) { - case TestPattern_Black: - prv_draw_solid(layer, ctx, GColorBlack); - break; - case TestPattern_Gray: - prv_draw_solid(layer, ctx, GColorDarkGray); - break; - case TestPattern_White: - prv_draw_solid(layer, ctx, GColorWhite); - break; - case TestPattern_Crosshair: - prv_draw_crosshair_screen(layer, ctx, 0); - break; -#if PBL_COLOR - case TestPattern_Red: - prv_draw_solid(layer, ctx, GColorRed); - break; - case TestPattern_Green: - prv_draw_solid(layer, ctx, GColorGreen); - break; - case TestPattern_Blue: - prv_draw_solid(layer, ctx, GColorBlue); - break; -#endif -#if PBL_ROUND - case TestPattern_Border1: - prv_draw_crosshair_screen(layer, ctx, 2); - break; - case TestPattern_Border2: - prv_draw_crosshair_screen(layer, ctx, 3); - break; - case TestPattern_Border3: - prv_draw_crosshair_screen(layer, ctx, 4); - break; -#endif -#if PBL_COLOR - case TestPattern_Pinwheel: - prv_draw_bitmap(layer, ctx, RESOURCE_ID_TEST_IMAGE_PINWHEEL); - break; - case TestPattern_Veggies: - prv_draw_bitmap(layer, ctx, RESOURCE_ID_TEST_IMAGE_VEGGIES); - break; -#endif - default: - break; - } -} - -static void prv_button_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - app_data->test_pattern = (app_data->test_pattern + 1) % NumTestPatterns; - - layer_mark_dirty(&app_data->window.layer); - -#if MFG_INFO_RECORDS_TEST_RESULTS - if (app_data->test_pattern == 0) { - app_window_stack_pop(false); - app_window_stack_push(&app_data->results_window, false); - } -#endif -} - -static void prv_change_pattern(void *data) { - AppData *app_data = app_state_get_user_data(); - - app_data->test_pattern = (TestPattern) data; - - layer_mark_dirty(&app_data->window.layer); -} - -static void prv_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_button_click_handler); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .test_pattern = (TestPattern) app_manager_get_task_context()->args - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - window_set_click_config_provider(window, prv_config_provider); - - Layer *layer = window_get_root_layer(window); - layer_set_update_proc(layer, prv_update_proc); - - app_window_stack_push(window, true /* Animated */); - -#if MFG_INFO_RECORDS_TEST_RESULTS - window_init(&data->results_window, ""); - window_set_fullscreen(&data->results_window, true); - mfg_results_ui_init(&data->results_ui, MfgTest_Display, &data->results_window); -#endif -} - -static void s_main(void) { - light_enable(true); - - prv_handle_init(); - - app_event_loop(); - - light_enable(false); -} - -const PebbleProcessMd* mfg_display_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: df582042-5beb-410f-9fed-76eccd31821e - .common.uuid = { 0xdf, 0x58, 0x20, 0x42, 0x5b, 0xeb, 0x41, 0x0f, - 0x9f, 0xed, 0x76, 0xec, 0xcd, 0x31, 0x82, 0x1e }, - .name = "MfgDisplay", - }; - return (const PebbleProcessMd*) &s_app_info; -} - - -// Prompt Commands -/////////////////////////////////////////////////////////////////////////////// - -static void prv_launch_app_cb(void *data) { - if (app_manager_get_current_app_md() == mfg_display_app_get_info()) { - process_manager_send_callback_event_to_process(PebbleTask_App, prv_change_pattern, data); - } else { - app_manager_launch_new_app(&(AppLaunchConfig) { - .md = mfg_display_app_get_info(), - .common.args = data, - }); - } -} - -void command_display_set(const char *color) { - const char * const ARGS[] = { - [TestPattern_Black] = "black", - [TestPattern_Gray] = "gray", - [TestPattern_White] = "white", - [TestPattern_Crosshair] = "crosshair", -#if PBL_COLOR - [TestPattern_Veggies] = "veggies", - [TestPattern_Pinwheel] = "pinwheel", - [TestPattern_Red] = "red", - [TestPattern_Green] = "green", - [TestPattern_Blue] = "blue", -#endif - }; - - for (unsigned int i = 0; i < ARRAY_LENGTH(ARGS); ++i) { - if (!strcmp(color, ARGS[i])) { - // Do this first because it launches the mfg menu using a callback, as if we did this in the - // callback we send below to launch the display app it would end up launching the menu on - // top of the display app. - if (!mfg_is_mfg_mode()) { - mfg_enter_mfg_mode_and_launch_app(); - } - - launcher_task_add_callback(prv_launch_app_cb, (void*) i); - prompt_send_response("OK"); - return; - } - } - - prompt_send_response("Invalid command"); -} diff --git a/src/fw/apps/prf_apps/mfg_display_app.h b/src/fw/apps/prf_apps/mfg_display_app.h deleted file mode 100644 index 864df82a6a..0000000000 --- a/src/fw/apps/prf_apps/mfg_display_app.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file mfg_display_app.h -//! -//! Test app that shows various display pattern - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_display_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_display_calibration_app.c b/src/fw/apps/prf_apps/mfg_display_calibration_app.c deleted file mode 100644 index 19d34577f4..0000000000 --- a/src/fw/apps/prf_apps/mfg_display_calibration_app.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/app_timer.h" -#include "applib/fonts/fonts.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/ui/window.h" -#include "applib/ui/app_window_stack.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_serials.h" -#include "mfg/mfg_info.h" -#include "services/common/light.h" -#include "process_management/app_manager.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "resource/system_resource.h" - -#include - -#define TICK_LENGTH 20 -#define LINE_HEIGHT 20 - -typedef enum { - X_AdjustState, - Y_AdjustState, - - NumAdjustStates -} AppState; - -typedef struct { - Window window; - - AppTimer *exit_timer; - AppState app_state; - int8_t axis_offsets[NumAdjustStates]; - - char text_buffer[16]; - char device_serial[MFG_SERIAL_NUMBER_SIZE + 1]; - bool is_saving; -} AppData; - -static void prv_draw_solid(Layer *layer, GContext *ctx, GColor color) { - graphics_context_set_fill_color(ctx, color); - graphics_fill_rect(ctx, &layer->bounds); -} - -static void prv_display_offsets(Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - graphics_context_set_text_color(ctx, GColorWhite); - - const GRect *bounds = &data->window.layer.bounds; - int pixel_max = bounds->origin.x + bounds->size.w - 1; - - GFont font; - for (int i = 0; i < NumAdjustStates; i++) { - font = fonts_get_system_font((i == data->app_state) ? FONT_KEY_GOTHIC_24_BOLD - : FONT_KEY_GOTHIC_24); - - snprintf(data->text_buffer, sizeof(data->text_buffer), "%c: %"PRIi8, 'X' + i, - data->axis_offsets[X_AdjustState + i]); - graphics_draw_text(ctx, data->text_buffer, font, - GRect(bounds->origin.x, bounds->origin.y + 35 + LINE_HEIGHT * i, - pixel_max, pixel_max), - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - } -} - -static void prv_display_serial_number(Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - - const GRect *bounds = &data->window.layer.bounds; - graphics_context_set_text_color(ctx, GColorWhite); - graphics_draw_text(ctx, data->device_serial, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), - GRect(bounds->origin.x, (bounds->origin.x + bounds->size.w) / 2, - bounds->size.w, bounds->size.h), - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static void prv_display_saving_message(Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - - const GRect *bounds = &data->window.layer.bounds; - graphics_context_set_text_color(ctx, GColorWhite); - graphics_draw_text(ctx, "Saving...", fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), - GRect(bounds->origin.x, (bounds->origin.y + bounds->size.h) / 2 + LINE_HEIGHT, - bounds->size.w, bounds->size.h), - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static void prv_draw_crosshair(Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - - const GRect *bounds = &data->window.layer.bounds; - const int mid_pixel_minus_one = (bounds->origin.x + bounds->size.w - 1) / 2; - const int pixel_min = bounds->origin.x; - const int pixel_max = bounds->origin.x + bounds->size.w - 1; - - for (int i = 0; i < 2; i++) { - graphics_context_set_stroke_color(ctx, i ? GColorWhite : GColorWhite); - - graphics_draw_line(ctx, (GPoint) {.x = mid_pixel_minus_one + i, .y = pixel_min}, - (GPoint) {.x = mid_pixel_minus_one + i, .y = pixel_min + TICK_LENGTH}); - graphics_draw_line(ctx, (GPoint) {.x = mid_pixel_minus_one + i, .y = pixel_max - TICK_LENGTH}, - (GPoint) {.x = mid_pixel_minus_one + i, .y = pixel_max}); - graphics_draw_line(ctx, (GPoint) {.x = pixel_min, .y = mid_pixel_minus_one + i}, - (GPoint) {.x = pixel_min + TICK_LENGTH, .y = mid_pixel_minus_one + i}); - graphics_draw_line(ctx, (GPoint) {.x = pixel_max - TICK_LENGTH, .y = mid_pixel_minus_one + i}, - (GPoint) {.x = pixel_max, .y = mid_pixel_minus_one + i}); - } -} - -static void prv_draw_border_stripes(Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - - const GRect *bounds = &data->window.layer.bounds; - const int pixel_min = bounds->origin.x; - const int pixel_max = bounds->origin.x + bounds->size.w - 1; - - for (int j = 0; j < 2; j++) { - graphics_context_set_stroke_color(ctx, j ? GColorGreen : GColorRed); - for (int i = 0 + j; i < 5; i += 2) { - graphics_draw_line(ctx, (GPoint) {.x = i, .y = pixel_min}, - (GPoint) {.x = i, .y = pixel_max}); - graphics_draw_line(ctx, (GPoint) {.x = pixel_max - i, .y = pixel_min}, - (GPoint) {.x = pixel_max - i, .y = pixel_max}); - graphics_draw_line(ctx, (GPoint) {.x = pixel_min, .y = i}, - (GPoint) {.x = pixel_max, .y = i}); - graphics_draw_line(ctx, (GPoint) {.x = pixel_min, .y = pixel_max - i}, - (GPoint) {.x = pixel_max, .y = pixel_max - i}); - } - } -} - -static void prv_layer_update_proc(Layer *layer, GContext *ctx) { - AppData *data = app_state_get_user_data(); - - prv_draw_solid(layer, ctx, GColorBlack); - - ctx->draw_state.drawing_box.origin.x += data->axis_offsets[X_AdjustState]; - ctx->draw_state.drawing_box.origin.y += data->axis_offsets[Y_AdjustState]; - - prv_draw_border_stripes(layer, ctx); - prv_draw_crosshair(layer, ctx); - - prv_display_offsets(layer, ctx); - prv_display_serial_number(layer, ctx); - if (data->is_saving) { - prv_display_saving_message(layer, ctx); - } -} - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - app_data->app_state = (app_data->app_state + 1) % (NumAdjustStates); - - layer_mark_dirty(&app_data->window.layer); -} - -static void prv_up_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - --(app_data->axis_offsets[app_data->app_state]); - - layer_mark_dirty(&app_data->window.layer); -} - -static void prv_down_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - ++(app_data->axis_offsets[app_data->app_state]); - - layer_mark_dirty(&app_data->window.layer); -} - -static void prv_save_offsets_callback(void *data) { - AppData *app_data = app_state_get_user_data(); - - mfg_info_set_disp_offsets((GPoint) { - .x = app_data->axis_offsets[X_AdjustState], - .y = app_data->axis_offsets[Y_AdjustState] - }); - - app_window_stack_pop_all(false); -} - -static void prv_back_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - if (app_data->axis_offsets[X_AdjustState] == mfg_info_get_disp_offsets().x && - app_data->axis_offsets[Y_AdjustState] == mfg_info_get_disp_offsets().y) { - app_window_stack_pop_all(true); - return; - } - - app_data->is_saving = true; - layer_mark_dirty(&app_data->window.layer); - - app_data->exit_timer = app_timer_register(200, prv_save_offsets_callback, NULL); -} - -static void prv_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); - window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); - window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .app_state = (AppState) app_manager_get_task_context()->args, - .axis_offsets[X_AdjustState] = mfg_info_get_disp_offsets().x, - .axis_offsets[Y_AdjustState] = mfg_info_get_disp_offsets().y - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, "MfgDisplayCalibration"); - window_set_fullscreen(window, true); - window_set_click_config_provider(window, prv_config_provider); - - Layer *layer = window_get_root_layer(window); - layer_set_update_proc(layer, prv_layer_update_proc); - - mfg_info_get_serialnumber(data->device_serial, sizeof(data->device_serial)); - - light_enable(true); - - app_window_stack_push(window, true /* Animated */); -} - -static void prv_handle_deinit(void) { - light_enable(false); - - AppData *data = app_state_get_user_data(); - app_timer_cancel(data->exit_timer); - app_free(data); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); - - prv_handle_deinit(); -} - -const PebbleProcessMd* mfg_display_calibration_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: d0582042-5beb-410f-9fed-76eccd31821e - .common.uuid = { 0xd0, 0x58, 0x20, 0x42, 0x5b, 0xeb, 0x41, 0x0f, - 0x9f, 0xed, 0x76, 0xec, 0xcd, 0x31, 0x82, 0x1e }, - .name = "MfgDisplayCalibration", - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/prf_apps/mfg_display_calibration_app.h b/src/fw/apps/prf_apps/mfg_display_calibration_app.h deleted file mode 100644 index 9643f346df..0000000000 --- a/src/fw/apps/prf_apps/mfg_display_calibration_app.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file mfg_display_calibration_app.h -//! -//! Test app for display calibration - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_display_calibration_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_hrm_app.c b/src/fw/apps/prf_apps/mfg_hrm_app.c deleted file mode 100644 index ccd8f9c689..0000000000 --- a/src/fw/apps/prf_apps/mfg_hrm_app.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_hrm_app.h" - -#include "applib/app.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "drivers/accel.h" -#include "drivers/hrm.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "mfg/mfg_info.h" -#include "process_state/app_state/app_state.h" -#include "process_management/pebble_process_md.h" -#include "process_management/process_manager.h" -#include "services/common/evented_timer.h" -#include "services/common/hrm/hrm_manager.h" -#include "util/bitset.h" -#include "util/size.h" -#include "util/trig.h" - -#include -#include -#include - -#if CAPABILITY_HAS_BUILTIN_HRM - -#define STATUS_STRING_LEN 32 - -typedef struct { - Window window; - EventServiceInfo hrm_event_info; - - TextLayer title_text_layer; - TextLayer status_text_layer; - char status_string[STATUS_STRING_LEN]; - HRMSessionRef hrm_session; -} AppData; - -static void prv_handle_hrm_data(PebbleEvent *e, void *context) { - AppData *app_data = app_state_get_user_data(); - - if (e->type == PEBBLE_HRM_EVENT) { - snprintf(app_data->status_string, STATUS_STRING_LEN, - "TIA: %"PRIu16"\nLED: %"PRIu16" mA", e->hrm.led.tia, e->hrm.led.current_ua); - layer_mark_dirty(&app_data->window.layer); - } -} - -static void prv_handle_init(void) { - const bool has_hrm = mfg_info_is_hrm_present(); - - AppData *data = task_zalloc(sizeof(*data)); - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - TextLayer *title = &data->title_text_layer; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "HRM TEST"); - layer_add_child(&window->layer, &title->layer); - - if (has_hrm) { - sniprintf(data->status_string, STATUS_STRING_LEN, "Starting..."); - } else { - sniprintf(data->status_string, STATUS_STRING_LEN, "Not an HRM device"); - } - TextLayer *status = &data->status_text_layer; - text_layer_init(status, - &GRect(5, 40, window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 40)); - text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(status, GTextAlignmentCenter); - text_layer_set_text(status, data->status_string); - layer_add_child(&window->layer, &status->layer); - - - if (has_hrm) { - data->hrm_event_info = (EventServiceInfo){ - .type = PEBBLE_HRM_EVENT, - .handler = prv_handle_hrm_data, - }; - event_service_client_subscribe(&data->hrm_event_info); - - // Use app data as session ref - AppInstallId app_id = 1; - data->hrm_session = sys_hrm_manager_app_subscribe(app_id, 1, SECONDS_PER_HOUR, - HRMFeature_LEDCurrent); - } - - app_window_stack_push(window, true); -} - -static void prv_handle_deinit(void) { - AppData *data = app_state_get_user_data(); - event_service_client_unsubscribe(&data->hrm_event_info); - if (mfg_info_is_hrm_present()) { - sys_hrm_manager_unsubscribe(data->hrm_session); - } - - text_layer_deinit(&data->title_text_layer); - text_layer_deinit(&data->status_text_layer); - window_deinit(&data->window); - app_free(data); -} - -static void prv_main(void) { - prv_handle_init(); - app_event_loop(); - prv_handle_deinit(); -} - -const PebbleProcessMd* mfg_hrm_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &prv_main, - .name = "MfgHRM", - }; - return (const PebbleProcessMd*) &s_app_info; -} - -#endif // CAPABILITY_HAS_BUILTIN_HRM diff --git a/src/fw/apps/prf_apps/mfg_hrm_app.h b/src/fw/apps/prf_apps/mfg_hrm_app.h deleted file mode 100644 index 8f5642eaa7..0000000000 --- a/src/fw/apps/prf_apps/mfg_hrm_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_hrm_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_menu_app.c b/src/fw/apps/prf_apps/mfg_menu_app.c deleted file mode 100644 index 5a5e11c6d9..0000000000 --- a/src/fw/apps/prf_apps/mfg_menu_app.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/graphics/bitblt.h" -#include "applib/ui/ui.h" -#include "applib/ui/window_private.h" -#include "apps/prf_apps/mfg_accel_app.h" -#include "apps/prf_apps/mfg_als_app.h" -#include "apps/prf_apps/mfg_bt_device_name_app.h" -#include "apps/prf_apps/mfg_bt_sig_rf_app.h" -#include "apps/prf_apps/mfg_button_app.h" -#include "apps/prf_apps/mfg_certification_app.h" -#include "apps/prf_apps/mfg_display_app.h" -#include "apps/prf_apps/mfg_hrm_app.h" -#include "apps/prf_apps/mfg_mic_app.h" -#include "apps/prf_apps/mfg_program_color_app.h" -#include "apps/prf_apps/mfg_runin_app.h" -#include "apps/prf_apps/mfg_speaker_app.h" -#include "apps/prf_apps/mfg_vibe_app.h" -#include "apps/prf_apps/mfg_touch_app.h" -#include "apps/prf_apps/mfg_backlight_app.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/standby.h" -#include "mfg/mfg_info.h" -#include "mfg/mfg_serials.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/bluetooth/local_id.h" -#include "services/common/bluetooth/pairability.h" -#include "system/bootbits.h" -#include "system/reset.h" -#include "util/size.h" - -#if PBL_ROUND -#include "apps/prf_apps/mfg_display_calibration_app.h" -#endif - -#include - -typedef struct { - Window *window; - SimpleMenuLayer *menu_layer; - SimpleMenuSection menu_section; -} MfgMenuAppData; - -static uint16_t s_menu_position = 0; - -#if MFG_INFO_RECORDS_TEST_RESULTS -static GBitmap *s_menu_icons[2]; -#define ICON_IDX_CHECK 0 -#define ICON_IDX_X 1 -#endif - -//! Callback to run from the kernel main task -static void prv_launch_app_cb(void *data) { - app_manager_launch_new_app(&(AppLaunchConfig) { .md = data }); -} - -static void prv_select_bt_device_name(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_bt_device_name_app_get_info()); -} - -#if PBL_ROUND -static void prv_select_calibrate_display(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_display_calibration_app_get_info()); -} -#endif - -static void prv_select_accel(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_accel_app_get_info()); -} - -static void prv_select_button(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_button_app_get_info()); -} - -static void prv_select_display(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_display_app_get_info()); -} - -#if PLATFORM_OBELIX -static void prv_select_backlight(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_backlight_app_get_info()); -} -#endif - -static void prv_select_runin(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_runin_app_get_info()); -} - -static void prv_select_vibe(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_vibe_app_get_info()); -} - -static void prv_select_als(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_als_app_get_info()); -} - -#if PLATFORM_ASTERIX -static void prv_select_speaker(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_speaker_app_get_info()); -} - -static void prv_select_mic(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_mic_app_get_info()); -} -#endif - -#if !PLATFORM_SILK && !PLATFORM_ASTERIX && !PLATFORM_OBELIX -static void prv_select_bt_sig_rf(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_bt_sig_rf_app_get_info()); -} -#endif - -#if CAPABILITY_HAS_BUILTIN_HRM -static void prv_select_hrm(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_hrm_app_get_info()); -} -#endif - -#if CAPABILITY_HAS_TOUCHSCREEN -static void prv_select_touch(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_touch_app_get_info()); -} -#endif - -static void prv_select_certification(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_certification_app_get_info()); -} - -static void prv_select_program_color(int index, void *context) { - launcher_task_add_callback(prv_launch_app_cb, (void*) mfg_program_color_app_get_info()); -} - -static void prv_select_load_prf(int index, void *context) { - boot_bit_set(BOOT_BIT_FORCE_PRF); - system_reset(); -} - -static void prv_select_reset(int index, void *context) { - system_reset(); -} - -static void prv_select_shutdown(int index, void *context) { - enter_standby(RebootReasonCode_ShutdownMenuItem); -} - -static GBitmap * prv_get_icon_for_test(MfgTest test) { -#if MFG_INFO_RECORDS_TEST_RESULTS - const bool passed = mfg_info_get_test_result(test); - if (passed) { - return s_menu_icons[ICON_IDX_CHECK]; - } - return s_menu_icons[ICON_IDX_X]; -#else - return NULL; -#endif -} - -static void prv_load_icons(void) { -#if MFG_INFO_RECORDS_TEST_RESULTS - // The icons in resources are black boxes with either a white checkmark or X. - // In order to make them look correct in the way we are using them, we want to - // invert the icons so that they are black icon on a white background. - // - // To do this, load each resource temporarily and then create two new bitmaps. - // Then bitblt the original resource into the new bitmap using GCompOpAssignInverted. - - const uint32_t icon_id[] = { RESOURCE_ID_ACTION_BAR_ICON_CHECK, RESOURCE_ID_ACTION_BAR_ICON_X }; - - for (unsigned i = 0; i < ARRAY_LENGTH(icon_id); ++i) { - GBitmap tmp; - gbitmap_init_with_resource(&tmp, icon_id[i]); - - GBitmap *icon = gbitmap_create_blank(tmp.bounds.size, tmp.info.format); - bitblt_bitmap_into_bitmap(icon, &tmp, GPointZero, GCompOpAssignInverted, GColorBlack); - - s_menu_icons[i] = icon; - gbitmap_deinit(&tmp); - } -#endif -} - -//! @param[out] out_items -static size_t prv_create_menu_items(SimpleMenuItem** out_menu_items) { - prv_load_icons(); - - // Define a const blueprint on the stack. - const SimpleMenuItem s_menu_items[] = { - { .title = "BT Device Name", .callback = prv_select_bt_device_name }, - { .title = "Device Serial" }, -#if PBL_ROUND - { .title = "Calibrate Display", .callback = prv_select_calibrate_display }, -#endif - { .title = "Test Accel", .callback = prv_select_accel }, - { .icon = prv_get_icon_for_test(MfgTest_Buttons), - .title = "Test Buttons", .callback = prv_select_button }, - { .icon = prv_get_icon_for_test(MfgTest_Display), - .title = "Test Display", .callback = prv_select_display }, -#if CAPABILITY_HAS_TOUCHSCREEN - { .title = "Test Touch", .callback = prv_select_touch }, -#endif -#if PLATFORM_OBELIX - { .title = "Test Backlight", .callback = prv_select_backlight }, -#endif - { .icon = prv_get_icon_for_test(MfgTest_ALS), - .title = "Test ALS", .callback = prv_select_als }, - { .title = "Test Runin", .callback = prv_select_runin }, - { .icon = prv_get_icon_for_test(MfgTest_Vibe), - .title = "Test Vibe", .callback = prv_select_vibe }, -#if !PLATFORM_SILK && !PLATFORM_ASTERIX && !PLATFORM_OBELIX - { .title = "Test bt_sig_rf", .callback = prv_select_bt_sig_rf }, -#endif -#if CAPABILITY_HAS_BUILTIN_HRM - { .title = "Test HRM", .callback = prv_select_hrm }, -#endif -#if PLATFORM_ASTERIX - { .title = "Test Speaker", .callback = prv_select_speaker }, - { .title = "Test Microphone", .callback = prv_select_mic }, -#endif - { .title = "Certification", .callback = prv_select_certification }, - { .title = "Program Color", .callback = prv_select_program_color }, - { .title = "Load PRF", .callback = prv_select_load_prf }, - { .title = "Reset", .callback = prv_select_reset }, - { .title = "Shutdown", .callback = prv_select_shutdown } - }; - - // Copy it into the heap so we can modify it. - *out_menu_items = app_malloc(sizeof(s_menu_items)); - memcpy(*out_menu_items, s_menu_items, sizeof(s_menu_items)); - - // Now we're going to modify the first two elements in the menu to include data available only - // at runtime. If it was available at compile time we could have just shoved it in the - // s_menu_items array but it's not. Note that we allocate a few buffers here that we never - // bother freeing for simplicity. It's all on the app heap so it will automatically get cleaned - // up on app exit. - - // Poke in the bluetooth name - int buffer_size = BT_DEVICE_NAME_BUFFER_SIZE; - char *bt_dev_name = app_malloc(buffer_size); - bt_local_id_copy_device_name(bt_dev_name, false); - - (*out_menu_items)[0].subtitle = bt_dev_name; - - // Poke in the serial number - buffer_size = MFG_SERIAL_NUMBER_SIZE + 1; - char *device_serial = app_malloc(buffer_size); - mfg_info_get_serialnumber(device_serial, buffer_size); - - (*out_menu_items)[1].subtitle = device_serial; - - // We've now populated out_menu_items with the correct data. Return the number of items by - // looking at the original list of menu items. - return ARRAY_LENGTH(s_menu_items); -} - -static void prv_window_load(Window *window) { - MfgMenuAppData *data = app_state_get_user_data(); - - Layer *window_layer = window_get_root_layer(data->window); - GRect bounds = window_layer->bounds; -#ifdef PLATFORM_SPALDING - bounds.origin.x += 25; - bounds.origin.y += 25; - bounds.size.w -= 50; - bounds.size.h -= 25; -#endif - - SimpleMenuItem* menu_items; - size_t num_items = prv_create_menu_items(&menu_items); - - data->menu_section = (SimpleMenuSection) { - .num_items = num_items, - .items = menu_items - }; - - data->menu_layer = simple_menu_layer_create(bounds, data->window, &data->menu_section, 1, NULL); - layer_add_child(window_layer, simple_menu_layer_get_layer(data->menu_layer)); - - // Set the menu layer back to it's previous highlight position - simple_menu_layer_set_selected_index(data->menu_layer, s_menu_position, false); -} - -static void s_main(void) { - bt_pairability_use(); - - MfgMenuAppData *data = app_malloc_check(sizeof(MfgMenuAppData)); - *data = (MfgMenuAppData){}; - - app_state_set_user_data(data); - - data->window = window_create(); - window_init(data->window, ""); - window_set_window_handlers(data->window, &(WindowHandlers) { - .load = prv_window_load, - }); - window_set_overrides_back_button(data->window, true); - window_set_fullscreen(data->window, true); - app_window_stack_push(data->window, true /*animated*/); - - app_event_loop(); - - bt_pairability_release(); - - s_menu_position = simple_menu_layer_get_selected_index(data->menu_layer); -} - -const PebbleProcessMd* mfg_menu_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: ddfdf403-664e-47dd-a620-b1a14ce2b59b - .common.uuid = { 0xdd, 0xfd, 0xf4, 0x03, 0x66, 0x4e, 0x47, 0xdd, - 0xa6, 0x20, 0xb1, 0xa1, 0x4c, 0xe2, 0xb5, 0x9b }, - .name = "MfgMenu", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_menu_app.h b/src/fw/apps/prf_apps/mfg_menu_app.h deleted file mode 100644 index 840f144ade..0000000000 --- a/src/fw/apps/prf_apps/mfg_menu_app.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_menu_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_mic_app.c b/src/fw/apps/prf_apps/mfg_mic_app.c deleted file mode 100644 index 64afe36b24..0000000000 --- a/src/fw/apps/prf_apps/mfg_mic_app.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_sine_wave.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "board/board.h" -#include "drivers/i2c.h" -#include "drivers/i2c_definitions.h" -#include "drivers/i2c_hal.h" -#include "drivers/flash.h" -#include "drivers/clocksource.h" -#include "flash_region/flash_region.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "util/size.h" - -#include -#include - -#include "hal/nrf_clock.h" -#include "nrfx_i2s.h" -#include "nrfx_pdm.h" - -#include "console/dbgserial.h" - -#define DUMP_RECORDING_DBGSERIAL 0 -#define PLAY_SINEWAVE 0 - -#define DA7212_PLL_STATUS 0x03 -#define DA7212_CIF_CTRL 0x1D -#define DA7212_DIG_ROUTING_DAI 0x21 -#define DA7212_SR 0x22 -#define DA7212_REFERENCES 0x23 -#define DA7212_PLL_FRAC_TOP 0x24 -#define DA7212_PLL_FRAC_BOT 0x25 -#define DA7212_PLL_INTEGER 0x26 -#define DA7212_PLL_CTRL 0x27 -#define DA7212_DAI_CLK_MODE 0x28 -#define DA7212_DAI_CTRL 0x29 -#define DA7212_DIG_ROUTING_DAC 0x2A -#define DA7212_DAC_FILTERS5 0x40 -#define DA7212_DAC_R_GAIN 0x46 -#define DA7212_LINE_GAIN 0x4A -#define DA7212_MIXOUT_R_SELECT 0x4C -#define DA7212_SYSTEM_MODES_OUTPUT 0x51 -#define DA7212_DAC_R_CTRL 0x6A -#define DA7212_LINE_CTRL 0x6D -#define DA7212_MIXOUT_R_CTRL 0x6F -#define DA7212_LDO_CTRL 0x90 -#define DA7212_GAIN_RAMP_CTRL 0x92 -#define DA7212_TONE_GEN_CFG1 0xB4 -#define DA7212_TONE_GEN_CYCLES 0xB6 -#define DA7212_TONE_GEN_ON_PER 0xBB -#define DA7212_SYSTEM_ACTIVE 0xFD - -#define RECORDING_MS 3000 -#define SAMPLE_RATE_HZ 16000 -#define SAMPLE_BITS 16 -#define CAPTURE_MS 100 -#define N_CHANNELS 2 -#define N_SAMPLES (N_CHANNELS * ((SAMPLE_RATE_HZ * CAPTURE_MS) / 1000)) -#define SAMPLE_SIZE_BYTES (SAMPLE_BITS / 8) -#define BLOCK_SIZE (N_SAMPLES * SAMPLE_SIZE_BYTES) - -#define FLASH_START FLASH_REGION_FIRMWARE_DEST_BEGIN -#define FLASH_END FLASH_REGION_FIRMWARE_DEST_END - -#if !PLAY_SINEWAVE -static int16_t s_buf[2][N_SAMPLES]; -static int16_t *s_buf_rd; -static int16_t *s_buf_wr; -static uint8_t s_buf_idx; -static SemaphoreHandle_t s_data_ready; -static SemaphoreHandle_t s_need_data; -#endif -static nrfx_i2s_buffers_t s_i2s_bufs; - -#if !PLAY_SINEWAVE -static const nrfx_pdm_t s_pdm = NRFX_PDM_INSTANCE(0); -static nrfx_pdm_config_t s_pdm_cfg = - NRFX_PDM_DEFAULT_CONFIG(NRF_GPIO_PIN_MAP(1, 0), NRF_GPIO_PIN_MAP(0, 24)); -#endif - -static const nrfx_i2s_t s_i2s = NRFX_I2S_INSTANCE(0); -static nrfx_i2s_config_t s_i2s_cfg = - NRFX_I2S_DEFAULT_CONFIG(NRF_GPIO_PIN_MAP(0, 12), NRF_GPIO_PIN_MAP(0, 7), NRF_GPIO_PIN_MAP(1, 9), - NRF_GPIO_PIN_MAP(0, 13), NRF_I2S_PIN_NOT_CONNECTED); - -typedef struct { - Window window; - - TextLayer title; -} AppData; - -static void da7212_register_write(uint8_t reg, uint8_t value) { - uint8_t data[2] = {reg, value}; - bool ret; - - i2c_use(I2C_DA7212); - PBL_LOG(LOG_LEVEL_DEBUG, "Writing DA7212 register 0x%02x with value 0x%02x", reg, value); - - ret = i2c_write_block(I2C_DA7212, 2, data); - PBL_ASSERTN(ret); - - i2c_release(I2C_DA7212); -} - -static uint8_t da7212_register_read(uint8_t reg) { - uint8_t data; - bool ret; - - i2c_use(I2C_DA7212); - - ret = i2c_read_register(I2C_DA7212, reg, &data); - PBL_ASSERTN(ret); - - i2c_release(I2C_DA7212); - - return data; -} - -static void prv_codec_setup(void) { - // CIF_CTRL: soft reset - da7212_register_write(DA7212_CIF_CTRL, 0x80); - - psleep(10); - - // SYSTEM_ACTIVE: wake-up - da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x01); - - // REFERENCES: enable master bias - da7212_register_write(DA7212_REFERENCES, 0x08); - - psleep(30); - - // LDO_CTRL: enable LDO, 1.05V - da7212_register_write(DA7212_LDO_CTRL, 0x80); - - // PLL: MCLK=4MHz (so input divider=2), we need 12.288MHz System Clock for SR=16KHz (see table 34) - // VCO = System Clock * 8 = 98.304MHz - // Feedback divider = VCO * Input Divider / MCLK - // = 98.304MHz * 2 / 4MHz = 49.152 - // PLL_INTEGER = 49 (0x31) - // PLL_FRAC = 0.152 * 2^13 = 1245 (0x4dd) - // PLL_FRAC_TOP = 0x04 - // PLL_FRAC_BOT = 0xdd - da7212_register_write(DA7212_PLL_FRAC_TOP, 0x04); - da7212_register_write(DA7212_PLL_FRAC_BOT, 0xdd); - da7212_register_write(DA7212_PLL_INTEGER, 0x31); - - // PLL_CTRL: enable + SRM, input clock range 2-10MHz - da7212_register_write(DA7212_PLL_CTRL, 0xC0); - - // PLL: operate with a 2-5MHz MCLK (ref. DA7212 rev 3.6, 13.29) - da7212_register_write(0xF0, 0x8B); - da7212_register_write(0xF2, 0x03); - da7212_register_write(0xF0, 0x00); - - psleep(40); - - PBL_ASSERT(da7212_register_read(DA7212_PLL_STATUS) == 0x07, "DA7212 PLL not locked"); - - // GAIN_RAMP_CTRL: 1s - da7212_register_write(DA7212_GAIN_RAMP_CTRL, 0x02); - // SR: 16KHz - da7212_register_write(DA7212_SR, 0x05); - // DAI_CLK_MODE: slave - da7212_register_write(DA7212_DAI_CLK_MODE, 0x00); - // DAI_CTRL: enable, 16-bit - da7212_register_write(DA7212_DAI_CTRL, 0x80); - // DIG_ROUTING_DAI: DAI_R/L_SRC to DAI_R/L - da7212_register_write(DA7212_DIG_ROUTING_DAI, 0x32); - // DIG_ROUTING_DAC: DAC_R/L mono mix of R/L - da7212_register_write(DA7212_DIG_ROUTING_DAC, 0xba); - // DAC_R_GAIN: 0dB - da7212_register_write(DA7212_DAC_R_GAIN, 0x6f); - // DAC_R_CTRL: enable - da7212_register_write(DA7212_DAC_R_CTRL, 0x80); - // MIXOUT_R_SELECT: DAC_R - da7212_register_write(DA7212_MIXOUT_R_SELECT, 0x08); - // MIXOUT_R_CTRL: enable, softmix enable, amp enable - da7212_register_write(DA7212_MIXOUT_R_CTRL, 0x98); - // LINE_GAIN: 10dB - da7212_register_write(DA7212_LINE_GAIN, 0x3a); - // LINE_CTRL: enable - da7212_register_write(DA7212_LINE_CTRL, 0x80); -} - -static void prv_codec_standby(void) { - da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x00); -} - -static void prv_data_handler(nrfx_i2s_buffers_t const *p_released, uint32_t status) { -#if !PLAY_SINEWAVE - PBL_ASSERT(!(status == NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED && p_released == NULL), "I2S buffers re-used"); - - if (status == NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED) { - s_i2s_bufs.p_tx_buffer = (uint32_t *)s_buf[s_buf_idx]; - s_buf_idx = (s_buf_idx + 1) % 2; - nrfx_i2s_next_buffers_set(&s_i2s, &s_i2s_bufs); - } - - if (p_released != NULL && p_released->p_tx_buffer != NULL) { - BaseType_t woken; - s_buf_wr = (int16_t *)p_released->p_tx_buffer; - xSemaphoreGiveFromISR(s_need_data, &woken); - portYIELD_FROM_ISR(woken); - } -#endif -} - -#if !PLAY_SINEWAVE -static void prv_pdm_evt_handler(nrfx_pdm_evt_t const * p_evt) { - PBL_ASSERT(p_evt->error == NRFX_PDM_NO_ERROR, "PDM overflow"); - - if (p_evt->buffer_requested) { - nrfx_pdm_buffer_set(&s_pdm, s_buf[s_buf_idx], N_SAMPLES); - s_buf_idx = (s_buf_idx + 1) % 2; - } - - if (p_evt->buffer_released) { - BaseType_t woken; - s_buf_rd = p_evt->buffer_released; - xSemaphoreGiveFromISR(s_data_ready, &woken); - portYIELD_FROM_ISR(woken); - } -} - -static void prv_mic_capture(void) { - uint32_t flash_addr; - nrfx_err_t err; - - s_data_ready = xSemaphoreCreateBinary(); - - clocksource_hfxo_request(); - - s_pdm_cfg.mode = NRF_PDM_MODE_STEREO; - // Sample rate of 16KHz (1280KHz / 80 = 16KHz) - s_pdm_cfg.clock_freq = NRF_PDM_FREQ_1280K; - s_pdm_cfg.ratio = NRF_PDM_RATIO_80X; - s_pdm_cfg.gain_l = NRF_PDM_GAIN_MAXIMUM; - s_pdm_cfg.gain_r = NRF_PDM_GAIN_MAXIMUM; - - err = nrfx_pdm_init(&s_pdm, &s_pdm_cfg, prv_pdm_evt_handler); - PBL_ASSERTN(err == NRFX_SUCCESS); - - flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); - - err = nrfx_pdm_start(&s_pdm); - PBL_ASSERTN(err == NRFX_SUCCESS); - - flash_addr = FLASH_START; - for (unsigned int i = 0U; i < RECORDING_MS / CAPTURE_MS; i++) { - xSemaphoreTake(s_data_ready, portMAX_DELAY); - - flash_write_bytes((uint8_t *)s_buf_rd, flash_addr, BLOCK_SIZE); - flash_addr += BLOCK_SIZE; - } - - nrfx_pdm_stop(&s_pdm); - nrfx_pdm_uninit(&s_pdm); - - clocksource_hfxo_release(); - - vSemaphoreDelete(s_data_ready); - -#if DUMP_RECORDING_DBGSERIAL - flash_addr = FLASH_START; - dbgserial_putstr("S"); - for (unsigned int i = 0U; i < RECORDING_MS / CAPTURE_MS; i++) { - flash_read_bytes((uint8_t *)s_buf_rd, flash_addr, BLOCK_SIZE); - flash_addr += BLOCK_SIZE; - - for (unsigned int j = 0U; j < N_SAMPLES; j++) { - char buf[8]; - dbgserial_putstr_fmt(buf, sizeof(buf), "%d", s_buf_rd[j]); - } - } - dbgserial_putstr("E"); -#endif // DUMP_RECORDING_DBGSERIAL -} -#endif - -#if !PLAY_SINEWAVE -static void prv_playback(void) { - uint32_t flash_addr; - nrfx_err_t err; - - s_need_data = xSemaphoreCreateBinary(); - - clocksource_hfxo_request(); - - // MCLK: 4MHz, sample rate: ~16KHz (4Mhz / 256 = 15625Hz) - s_i2s_cfg.mck_setup = NRF_I2S_MCK_32MDIV8; - s_i2s_cfg.ratio = NRF_I2S_RATIO_256X; - s_i2s_cfg.channels = NRF_I2S_CHANNELS_STEREO; - - err = nrfx_i2s_init(&s_i2s, &s_i2s_cfg, prv_data_handler); - PBL_ASSERTN(err == NRFX_SUCCESS); - - flash_addr = FLASH_START; - flash_read_bytes((uint8_t *)s_buf[0], flash_addr, BLOCK_SIZE); - flash_addr += BLOCK_SIZE; - - s_buf_idx = 1U; - s_buf_wr = s_buf[s_buf_idx]; - - s_i2s_bufs.p_tx_buffer = (uint32_t *)s_buf[0]; - s_i2s_bufs.buffer_size = BLOCK_SIZE / 4; - err = nrfx_i2s_start(&s_i2s, &s_i2s_bufs, 0); - PBL_ASSERTN(err == NRFX_SUCCESS); - - prv_codec_setup(); - - for (unsigned int i = 0U; i < (RECORDING_MS / CAPTURE_MS) - 1U; i++) { - flash_read_bytes((uint8_t *)s_buf_wr, flash_addr, BLOCK_SIZE); - flash_addr += BLOCK_SIZE; - - xSemaphoreTake(s_need_data, portMAX_DELAY); - } - - prv_codec_standby(); - - nrfx_i2s_stop(&s_i2s); - nrfx_i2s_uninit(&s_i2s); - - clocksource_hfxo_release(); - - vSemaphoreDelete(s_need_data); - - flash_region_erase_optimal_range(FLASH_START, FLASH_START, FLASH_END, FLASH_END); -} -#else -static void prv_playback(void) { - nrfx_err_t err; - - clocksource_hfxo_request(); - - // MCLK: 4MHz, sample rate: ~16KHz (4Mhz / 256 = 15625Hz) - s_i2s_cfg.mck_setup = NRF_I2S_MCK_32MDIV8; - s_i2s_cfg.ratio = NRF_I2S_RATIO_256X; - s_i2s_cfg.channels = NRF_I2S_CHANNELS_STEREO; - - err = nrfx_i2s_init(&s_i2s, &s_i2s_cfg, prv_data_handler); - PBL_ASSERTN(err == NRFX_SUCCESS); - - s_i2s_bufs.p_tx_buffer = (uint32_t *)sine_wave; - s_i2s_bufs.buffer_size = SINE_WAVE_TOTAL_SAMPLES / 2; - err = nrfx_i2s_start(&s_i2s, &s_i2s_bufs, 0); - PBL_ASSERTN(err == NRFX_SUCCESS); - - prv_codec_setup(); - psleep(3000); - prv_codec_standby(); - - nrfx_i2s_stop(&s_i2s); - nrfx_i2s_uninit(&s_i2s); - - clocksource_hfxo_release(); -} -#endif - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { -#if !PLAY_SINEWAVE - prv_mic_capture(); -#endif - prv_playback(); - - app_window_stack_pop(true); -} - -static void prv_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - window_set_click_config_provider(window, prv_config_provider); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "MICROPHONE TEST"); - layer_add_child(&window->layer, &title->layer); - - app_window_stack_push(window, true /* Animated */); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd *mfg_mic_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: 95ada1ce-04b3-46b0-8519-0b42260b5c39 - .common.uuid = {0x95, 0xad, 0xa1, 0xce, 0x04, 0xb3, 0x46, 0xb0, - 0x85, 0x19, 0x0b, 0x42, 0x26, 0x0b, 0x5c, 0x39}, - .name = "MfgMic", - }; - return (const PebbleProcessMd *)&s_app_info; -} diff --git a/src/fw/apps/prf_apps/mfg_mic_app.h b/src/fw/apps/prf_apps/mfg_mic_app.h deleted file mode 100644 index 3771ffef1e..0000000000 --- a/src/fw/apps/prf_apps/mfg_mic_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_mic_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_program_color_app.c b/src/fw/apps/prf_apps/mfg_program_color_app.c deleted file mode 100644 index b1a0ccc090..0000000000 --- a/src/fw/apps/prf_apps/mfg_program_color_app.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "util/trig.h" -#include "applib/app_watch_info.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "applib/ui/text_layer.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_info.h" -#include "process_state/app_state/app_state.h" -#include "process_management/pebble_process_md.h" -#include "util/bitset.h" -#include "util/size.h" - -#include -#include -#include - -#if PLATFORM_ASTERIX -const char *const s_model = "C2D"; -#elif PLATFORM_OBELIX -const char *const s_model = "CT2"; -#else -const char *const s_model = "Unknown"; -#endif - -typedef struct { - WatchInfoColor color; - const char *name; - const char *short_name; -} ColorTable; - -const ColorTable s_color_table[] = { -#if PLATFORM_ASTERIX - { - .color = WATCH_INFO_COLOR_COREDEVICES_C2D_BLACK, - .name = "BLACK", - .short_name = "BK", - }, - { - .color = WATCH_INFO_COLOR_COREDEVICES_C2D_WHITE, - .name = "WHITE", - .short_name = "WH", - } -#elif PLATFORM_OBELIX - { - .color = WATCH_INFO_COLOR_COREDEVICES_CT2_BLACK, - .name = "BLACK", - .short_name = "BK", - }, -#endif -}; - -typedef struct { - Window window; - - TextLayer title; - TextLayer color; - TextLayer status; - - int selected_color_index; -} AppData; - -static void prv_up_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - if (ARRAY_LENGTH(s_color_table) == 0 || app_data->selected_color_index == 0) { - return; - } - - app_data->selected_color_index--; - text_layer_set_text(&app_data->color, - s_color_table[app_data->selected_color_index].name); -} - -static void prv_down_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - if (ARRAY_LENGTH(s_color_table) == 0 || - app_data->selected_color_index == (int)ARRAY_LENGTH(s_color_table) - 1) { - return; - } - - app_data->selected_color_index++; - text_layer_set_text(&app_data->color, - s_color_table[app_data->selected_color_index].name); -} - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - char model[MFG_INFO_MODEL_STRING_LENGTH]; - - if (ARRAY_LENGTH(s_color_table) == 0 || app_data->selected_color_index == -1) { - return; - } - - snprintf(model, sizeof(model), "%s-%s", - s_model, s_color_table[app_data->selected_color_index].short_name); - - mfg_info_set_model(model); - mfg_info_set_watch_color(s_color_table[app_data->selected_color_index].color); - - text_layer_set_text(&app_data->status, "PROGRAMMED!"); -} - -static void prv_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .selected_color_index = -1, - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - window_set_click_config_provider(window, prv_config_provider); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "PROGRAM COLOR"); - layer_add_child(&window->layer, &title->layer); - - TextLayer *color = &data->color; - text_layer_init(color, - &GRect(5, 70, - window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 70)); - text_layer_set_font(color, fonts_get_system_font(FONT_KEY_GOTHIC_24)); - text_layer_set_text_alignment(color, GTextAlignmentCenter); - if (ARRAY_LENGTH(s_color_table) > 0) { - data->selected_color_index = 0; - text_layer_set_text(color, s_color_table[0].name); - } else { - text_layer_set_text(color, "NO COLORS AVAILABLE"); - } - layer_add_child(&window->layer, &color->layer); - - TextLayer *status = &data->status; - text_layer_init(status, - &GRect(5, 110, - window->layer.bounds.size.w - 5, window->layer.bounds.size.h - 110)); - text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); - text_layer_set_text_alignment(status, GTextAlignmentCenter); - layer_add_child(&window->layer, &status->layer); - - app_window_stack_push(window, true /* Animated */); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd* mfg_program_color_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: d5f0a47d-e570-499d-bcaa-fc6d56230038 - .common.uuid = { 0xd5, 0xf0, 0xa4, 0x7d, 0xe5, 0x70, 0x49, 0x9d, - 0xbc, 0xaa, 0xfc, 0x6d, 0x56, 0x23, 0x00, 0x38 }, - .name = "MfgProgramColor", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_program_color_app.h b/src/fw/apps/prf_apps/mfg_program_color_app.h deleted file mode 100644 index 08cfa5f8fd..0000000000 --- a/src/fw/apps/prf_apps/mfg_program_color_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_program_color_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_runin_app.c b/src/fw/apps/prf_apps/mfg_runin_app.c deleted file mode 100644 index 8dc11b0ae2..0000000000 --- a/src/fw/apps/prf_apps/mfg_runin_app.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_runin_app.h" - -#include "applib/app.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/ui.h" -#include "applib/ui/window_private.h" -#include "drivers/battery.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "services/common/battery/battery_curve.h" -#include "system/logging.h" - -#include - -typedef enum { - RuninStateStart = 0, - RuninStatePlugCharger, - RuninStateRunning, - RuninStatePass, - RuninStateFail, -} RuninTestState; - -static const char* status_text[] = { - [RuninStateStart] = "Start", - [RuninStatePlugCharger] = "Plug Charger", - [RuninStateRunning] = "Running...", - [RuninStatePass] = "Pass", - [RuninStateFail] = "Fail", -}; - -#ifdef PLATFORM_TINTIN -static const int SLOW_THRESHOLD_PERCENTAGE = 42; // ~3850mv -static const int PASS_BATTERY_PERCENTAGE = 84; // ~4050mv -#elif defined(PLATFORM_ASTERIX) || defined(PLATFORM_OBELIX) -static const int SLOW_THRESHOLD_PERCENTAGE = 0; -static const int PASS_BATTERY_PERCENTAGE = 70; -#else -static const int SLOW_THRESHOLD_PERCENTAGE = 0; // Always go "slow" on snowy -static const int PASS_BATTERY_PERCENTAGE = 60; // ~4190mv -#endif - -typedef struct { - Window window; - - TextLayer status; - char status_string[20]; - - TextLayer details; - char details_string[64]; - - RuninTestState test_state; - uint32_t seconds_remaining; - bool countdown_running; - bool fastcharge_enabled; - - int pass_count; -} AppData; - -static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { - AppData *data = app_state_get_user_data(); - - RuninTestState next_state = data->test_state; - - const int charge_mv = battery_get_millivolts(); - const BatteryChargeState charge_state = battery_get_charge_state(); - - switch (data->test_state) { - case RuninStateStart: - if (charge_state.is_plugged && charge_state.is_charging) { - next_state = RuninStateRunning; - } else { - next_state = RuninStatePlugCharger; - } - break; - case RuninStatePlugCharger: - if (charge_state.is_plugged && charge_state.is_charging) { - next_state = RuninStateRunning; - } - break; - case RuninStateRunning: - if (!data->countdown_running) { - data->countdown_running = true; - } - if (!charge_state.is_plugged || !charge_state.is_charging) { - data->pass_count = 0; - next_state = RuninStatePlugCharger; - break; - } - if (charge_state.charge_percent > SLOW_THRESHOLD_PERCENTAGE && data->fastcharge_enabled) { - // go slow for a bit - battery_set_fast_charge(false); - data->fastcharge_enabled = false; - } else if (charge_state.charge_percent > PASS_BATTERY_PERCENTAGE) { - // The reading can be a bit shaky in the short term (i.e. a flaky USB connection), or we - // just started charging. Make sure we have settled before transitioning into the - // RuninStatePass state - if (data->pass_count > 5) { - next_state = RuninStatePass; - - data->countdown_running = false; - // disable the charger so that we don't overcharge the battery - battery_set_charge_enable(false); - } - data->pass_count++; - } else { - data->pass_count = 0; - } - break; - case RuninStatePass: - case RuninStateFail: - default: - break; - } - - if (data->countdown_running) { - --data->seconds_remaining; - if (data->seconds_remaining == 0) { - // Time's up! - next_state = RuninStateFail; - data->countdown_running = false; - PBL_LOG(LOG_LEVEL_ERROR, "Failed runin testing"); - } - } - - data->test_state = next_state; - - sniprintf(data->status_string, sizeof(data->status_string), - "RUNIN\n%s", status_text[data->test_state]); - text_layer_set_text(&data->status, data->status_string); - - int mins_remaining = data->seconds_remaining / 60; - int secs_remaining = data->seconds_remaining % 60; - sniprintf(data->details_string, sizeof(data->details_string), - "Time:%02u:%02u\r\n%umV (%"PRIu8"%%)\r\nUSB: %s\r\nCharging: %s", - mins_remaining, secs_remaining, charge_mv, - charge_state.charge_percent, - charge_state.is_plugged ? "yes" : "no", - charge_state.is_charging ? "yes" : "no"); - text_layer_set_text(&data->details, data->details_string); -} - -static void prv_back_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - if (!app_data->countdown_running && - (app_data->test_state == RuninStateStart || - app_data->test_state == RuninStatePlugCharger)) { - - // if the test has not yet started, it is ok to push the back button to leave. - app_window_stack_pop(true); - } -} - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *data) { - AppData *app_data = app_state_get_user_data(); - - if (app_data->test_state == RuninStateFail || app_data->test_state == RuninStatePass) { - // we've finished the runin test - long-press to close the app - app_window_stack_pop(true); - } -} - -static void prv_config_provider(void *data) { - window_long_click_subscribe(BUTTON_ID_SELECT, 3000, NULL, prv_select_click_handler); - window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); -} - -static void app_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - *data = (AppData) { - .test_state = RuninStateStart, - .countdown_running = false, - .seconds_remaining = 5400, //1.5h - .fastcharge_enabled = true, - .pass_count = 0, - }; - - battery_set_fast_charge(true); - battery_set_charge_enable(true); - - Window *window = &data->window; - window_init(window, "Runin Test"); - - TextLayer *status = &data->status; - text_layer_init(status, &window->layer.bounds); - text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24)); - text_layer_set_text_alignment(status, GTextAlignmentCenter); - text_layer_set_text(status, status_text[data->test_state]); - layer_add_child(&window->layer, &status->layer); - - TextLayer *details = &data->details; - text_layer_init(details, - &GRect(0, 65, window->layer.bounds.size.w, window->layer.bounds.size.h - 65)); - text_layer_set_font(details, fonts_get_system_font(FONT_KEY_GOTHIC_24)); - text_layer_set_text_alignment(details, GTextAlignmentCenter); - layer_add_child(&window->layer, &details->layer); - - window_set_click_config_provider(window, prv_config_provider); - window_set_fullscreen(window, true); - - tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); - - app_window_stack_push(window, true /* Animated */); -} - -static void s_main(void) { - app_init(); - - app_event_loop(); -} - -const PebbleProcessMd* mfg_runin_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: fbb6d0e6-2d7d-40bc-8b01-f2f8beb9c394 - .common.uuid = { 0xfb, 0xb6, 0xd0, 0xe6, 0x2d, 0x7d, 0x40, 0xbc, - 0x8b, 0x01, 0xf2, 0xf8, 0xbe, 0xb9, 0xc3, 0x94 }, - .name = "Runin App", - }; - - return (PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_runin_app.h b/src/fw/apps/prf_apps/mfg_runin_app.h deleted file mode 100644 index 5049e9eb02..0000000000 --- a/src/fw/apps/prf_apps/mfg_runin_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_runin_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_sine_wave.c b/src/fw/apps/prf_apps/mfg_sine_wave.c deleted file mode 100644 index 92bbcf0743..0000000000 --- a/src/fw/apps/prf_apps/mfg_sine_wave.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_sine_wave.h" - -/* Stereo sine wave data (L, R, L, R, ...) */ -int16_t sine_wave[SINE_WAVE_TOTAL_SAMPLES] = { - 0, 0, 3134, 3134, 5791, 5791, 7567, 7567, 8191, 8191, 7567, 7567, 5791, 5791, 3134, 3134, - 0, 0, -3134, -3134, -5791, -5791, -7567, -7567, -8191, -8191, -7567, -7567, -5791, -5791, -3134, -3134 -}; diff --git a/src/fw/apps/prf_apps/mfg_sine_wave.h b/src/fw/apps/prf_apps/mfg_sine_wave.h deleted file mode 100644 index 29cd3eda7e..0000000000 --- a/src/fw/apps/prf_apps/mfg_sine_wave.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#define SINE_WAVE_SAMPLE_RATE 16000 -#define SINE_WAVE_FREQUENCY 1000 -#define SINE_WAVE_SAMPLES_PER_PERIOD 16 -#define SINE_WAVE_TOTAL_SAMPLES 32 - -/* Stereo sine wave data (L, R, L, R, ...) */ -extern int16_t sine_wave[SINE_WAVE_TOTAL_SAMPLES]; diff --git a/src/fw/apps/prf_apps/mfg_speaker_app.c b/src/fw/apps/prf_apps/mfg_speaker_app.c deleted file mode 100644 index 4012b34cfb..0000000000 --- a/src/fw/apps/prf_apps/mfg_speaker_app.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "board/board.h" -#include "drivers/i2c.h" -#include "drivers/i2c_definitions.h" -#include "drivers/i2c_hal.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "util/size.h" - -#define DA7212_CIF_CTRL 0x1D -#define DA7212_DAI_CLK_MODE 0x28 -#define DA7212_DAI_CTRL 0x29 -#define DA7212_DIG_ROUTING_DAC 0x2A -#define DA7212_DAC_FILTERS5 0x40 -#define DA7212_DAC_R_GAIN 0x46 -#define DA7212_LINE_GAIN 0x4A -#define DA7212_MIXOUT_R_SELECT 0x4C -#define DA7212_SYSTEM_MODES_OUTPUT 0x51 -#define DA7212_DAC_R_CTRL 0x6A -#define DA7212_LINE_CTRL 0x6D -#define DA7212_MIXOUT_R_CTRL 0x6F -#define DA7212_TONE_GEN_CFG1 0xB4 -#define DA7212_TONE_GEN_CYCLES 0xB6 -#define DA7212_TONE_GEN_ON_PER 0xBB -#define DA7212_SYSTEM_ACTIVE 0xFD - -typedef struct { - Window window; - - TextLayer title; - TextLayer status; -} AppData; - -static void da7212_register_write(uint8_t reg, uint8_t value) { - uint8_t data[2] = {reg, value}; - bool ret; - - i2c_use(I2C_DA7212); - - ret = i2c_write_block(I2C_DA7212, 2, data); - PBL_ASSERTN(ret); - - i2c_release(I2C_DA7212); -} - -static void prv_da7212_play_tone(void) { - // CIF_CTRL: soft reset - da7212_register_write(DA7212_CIF_CTRL, 0x80); - - psleep(10); - - // SYSTEM_ACTIVE: wake-up - da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x01); - - // Soft mute - da7212_register_write(DA7212_DAC_FILTERS5, 0x80); - - // DAI: enable (16-bit) - da7212_register_write(DA7212_DAI_CTRL, 0x80); - // DAI: enable master mode, BCLK=64 - da7212_register_write(DA7212_DAI_CLK_MODE, 0x81); - - // DAC_R/L source: DAI_R/L - da7212_register_write(DA7212_DIG_ROUTING_DAC, 0x32); - - // DAC_R: 0dB gain - da7212_register_write(DA7212_DAC_R_GAIN, 0x6f); - // DAC_R: enable - da7212_register_write(DA7212_DAC_R_CTRL, 0x80); - - // MIXOUT_R: input from DAC_R - da7212_register_write(DA7212_MIXOUT_R_SELECT, 0x08); - // MIXOUT_R: enable + amplifier - da7212_register_write(DA7212_MIXOUT_R_CTRL, 0x88); - - // Line: 0dB gain - da7212_register_write(DA7212_LINE_GAIN, 0x30); - // Line: enable amplifier, gain ramping, drive output - da7212_register_write(DA7212_LINE_CTRL, 0xa8); - - // Tone generator: infinite cycles - da7212_register_write(DA7212_TONE_GEN_CYCLES, 0x07); - // Tone generator: 200ms pulse - da7212_register_write(DA7212_TONE_GEN_ON_PER, 0x14); - // Tone generator: start - da7212_register_write(DA7212_TONE_GEN_CFG1, 0x80); - - // System modes output: enable DAC_R, Line - da7212_register_write(DA7212_SYSTEM_MODES_OUTPUT, 0x89); - - // Soft mute off - da7212_register_write(DA7212_DAC_FILTERS5, 0x00); -} - -static void prv_da7212_idle(void) { - da7212_register_write(DA7212_SYSTEM_ACTIVE, 0x00); -} - -static void prv_timer_callback(void *cb_data) { - (void)prv_da7212_idle(); - app_window_stack_pop(true /* animated */); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - window_set_overrides_back_button(window, true); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "SPEAKER TEST"); - layer_add_child(&window->layer, &title->layer); - - app_window_stack_push(window, true /* Animated */); - - prv_da7212_play_tone(); - - app_timer_register(5000, prv_timer_callback, NULL); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd *mfg_speaker_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: 27047635-68f1-4ece-9ca7-52dd8e22d1dd - .common.uuid = {0x27, 0x04, 0x76, 0x35, 0x68, 0xf1, 0x4e, 0xce, 0x9c, 0xa7, 0x52, 0xdd, 0x8e, - 0x22, 0xd1, 0xdd}, - .name = "MfgSpeaker", - }; - return (const PebbleProcessMd *)&s_app_info; -} diff --git a/src/fw/apps/prf_apps/mfg_speaker_app.h b/src/fw/apps/prf_apps/mfg_speaker_app.h deleted file mode 100644 index 85c6780598..0000000000 --- a/src/fw/apps/prf_apps/mfg_speaker_app.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_speaker_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/mfg_touch_app.c b/src/fw/apps/prf_apps/mfg_touch_app.c deleted file mode 100644 index 76c9e5244b..0000000000 --- a/src/fw/apps/prf_apps/mfg_touch_app.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include "applib/app.h" -#include "applib/graphics/graphics.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "mfg/results_ui.h" -#include "process_management/app_manager.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/light.h" -#include "services/common/touch/touch.h" -#include "services/common/touch/touch_event.h" -#include "services/common/touch/touch_client.h" - -#define RECTR_ROW (5) -#define RECTR_COL (4) - -#define TOUCH_SUPPORT_DEBUG 0 - -typedef struct { - Window window; - GRect rectr[RECTR_ROW * RECTR_COL]; - uint16_t rectr_radius; - uint16_t rectr_corners_index; - uint32_t touch_mark; - EventServiceInfo event_info; -} AppData; - -static void prv_update_proc(struct Layer *layer, GContext* ctx) { - AppData *data = app_state_get_user_data(); - - for (uint8_t i=0; itouch_mark & (1 << i)) { - graphics_context_set_fill_color(ctx, GColorGreen); - graphics_fill_round_rect(ctx, &data->rectr[i], data->rectr_radius, GCornersAll); - } - } -} - -static void prv_touch_envent_handler(const TouchEvent *event, void *context) { - AppData *data = app_state_get_user_data(); - uint16_t x = event->start_pos.x; - uint16_t y = event->start_pos.y; - - int16_t offset_x = event->diff_pos.x; - int16_t offset_y = event->diff_pos.y; - - uint8_t ind_x = (x+offset_x)/data->rectr[0].size.w; - if (ind_x > RECTR_COL-1) ind_x = RECTR_COL-1; - uint8_t ind_y = (y+offset_y)/data->rectr[0].size.h; - if (ind_y > RECTR_ROW-1) ind_y = RECTR_ROW-1; - uint8_t touch_id = ind_x * RECTR_ROW + ind_y; - - data->touch_mark |= 1<window.layer); -} - -static void prv_handle_touch_event(PebbleEvent *e, void *context) { - AppData *data = app_state_get_user_data(); - - if (e->type == PEBBLE_TOUCH_EVENT) { - PebbleTouchEvent *touch = &e->touch; - touch_dispatch_touch_events(touch->touch_idx, prv_touch_envent_handler, context); - } -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - Layer *layer = window_get_root_layer(window); - layer_set_update_proc(layer, prv_update_proc); - app_window_stack_push(window, true /* Animated */); - GContext* context = app_state_get_graphics_context(); - - //draw rectrs - data->rectr_radius = 5; - data->rectr_corners_index = GCornersAll; - for (uint8_t x=0; xrectr[RECTR_ROW * x + y].origin.x = (PBL_DISPLAY_WIDTH/RECTR_COL) * x; - data->rectr[RECTR_ROW * x + y].origin.y = (PBL_DISPLAY_HEIGHT/RECTR_ROW) * y; - data->rectr[RECTR_ROW * x + y].size.w = PBL_DISPLAY_WIDTH/RECTR_COL; - data->rectr[RECTR_ROW * x + y].size.h = PBL_DISPLAY_HEIGHT/RECTR_ROW; - graphics_context_set_stroke_color(context, GColorBlack); - graphics_draw_round_rect(context, &data->rectr[RECTR_ROW * x + y], data->rectr_radius); - } - } - layer_mark_dirty(&data->window.layer); - - data->event_info = (EventServiceInfo) { - .type = PEBBLE_TOUCH_EVENT, - .handler = prv_handle_touch_event, - }; - event_service_client_subscribe(&data->event_info); -} - -static void s_main(void) { - light_enable(true); - - prv_handle_init(); - - app_event_loop(); - - light_enable(false); -} - -const PebbleProcessMd* mfg_touch_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: a53e7d1c-d2ee-4592-96b9-5d33a46237db - .common.uuid = { 0xa5, 0x3e, 0x7d, 0x1c, 0xd2, 0xee, 0x45, 0x92, - 0x96, 0xb9, 0x5d, 0x33, 0xa4, 0x62, 0x37, 0xdb }, - .name = "MfgTouch", - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/prf_apps/mfg_touch_app.h b/src/fw/apps/prf_apps/mfg_touch_app.h deleted file mode 100644 index 3aecdd5601..0000000000 --- a/src/fw/apps/prf_apps/mfg_touch_app.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -//! @file mfg_display_app.h -//! -//! Test app that shows various display pattern - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_touch_app_get_info(void); diff --git a/src/fw/apps/prf_apps/mfg_vibe_app.c b/src/fw/apps/prf_apps/mfg_vibe_app.c deleted file mode 100644 index 45466f31e1..0000000000 --- a/src/fw/apps/prf_apps/mfg_vibe_app.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/vibes.h" -#include "applib/ui/window.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_info.h" -#include "mfg/results_ui.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" - -typedef struct { - Window window; - - TextLayer title; - - //! How many times we've vibrated - int vibe_count; - -#if MFG_INFO_RECORDS_TEST_RESULTS - MfgResultsUI results_ui; -#endif -} AppData; - -static void prv_handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { -#if !MFG_INFO_RECORDS_TEST_RESULTS - AppData *data = app_state_get_user_data(); - - const int MAX_VIBE_COUNT = 5; - if (data->vibe_count >= MAX_VIBE_COUNT) { - // We've vibed the number of times we wanted to, time to leave! - app_window_stack_pop(true /* animated */); - return; - } - - ++data->vibe_count; -#endif - - vibes_short_pulse(); -} - -static void prv_handle_init(void) { - AppData *data = app_malloc_check(sizeof(AppData)); - *data = (AppData) { - .vibe_count = 0 - }; - - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(title, GTextAlignmentCenter); - text_layer_set_text(title, "VIBE TEST"); - layer_add_child(&window->layer, &title->layer); - -#if MFG_INFO_RECORDS_TEST_RESULTS - mfg_results_ui_init(&data->results_ui, MfgTest_Vibe, window); -#endif - - app_window_stack_push(window, true /* Animated */); - - tick_timer_service_subscribe(SECOND_UNIT, prv_handle_second_tick); -} - -static void s_main(void) { - prv_handle_init(); - - app_event_loop(); -} - -const PebbleProcessMd* mfg_vibe_app_get_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common.main_func = &s_main, - // UUID: f676085a-b130-4492-b6a1-85492602ba00 - .common.uuid = { 0xf6, 0x76, 0x08, 0x5a, 0xb1, 0x30, 0x44, 0x92, - 0xb6, 0xa1, 0x85, 0x49, 0x26, 0x02, 0xba, 0x00 }, - .name = "MfgVibe", - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/prf_apps/mfg_vibe_app.h b/src/fw/apps/prf_apps/mfg_vibe_app.h deleted file mode 100644 index dd86cbd87f..0000000000 --- a/src/fw/apps/prf_apps/mfg_vibe_app.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file mfg_vibe_app.h -//! -//! Boring test app that vibes 5 times and quits - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_vibe_app_get_info(void); - diff --git a/src/fw/apps/prf_apps/prf_low_power_app.c b/src/fw/apps/prf_apps/prf_low_power_app.c deleted file mode 100644 index f8316a9ee2..0000000000 --- a/src/fw/apps/prf_apps/prf_low_power_app.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/ui/ui.h" -#include "applib/app.h" -#include "applib/app_timer.h" -#include "applib/graphics/graphics.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/kino/kino_reel.h" -#include "applib/ui/layer.h" -#include "applib/ui/window_private.h" -#include "applib/ui/window_stack.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" - - -#define LOW_POWER_APP_STATE_UPDATE_TIME_MS 2000 - -typedef struct { - Window window; - KinoLayer kino_layer; - GRect charging_kino_area; - GRect discharging_kino_area; - BatteryChargeState saved_state; - AppTimer *timer; -} LowPowerAppData; - -//////////////////////////////////////////////////////////// -// Update Logic - -static void prv_refresh_state(void *data_in) { - LowPowerAppData *data = (LowPowerAppData*) data_in; - BatteryChargeState current_state = battery_get_charge_state(); - uint32_t res_id; - GRect kino_area; - - if (current_state.is_charging && !data->saved_state.is_charging) { - kino_area = data->charging_kino_area; - res_id = RESOURCE_ID_RECOVERY_LOW_POWER_CHARGING; - } else if (!current_state.is_charging && data->saved_state.is_charging) { - kino_area = data->discharging_kino_area; - res_id = RESOURCE_ID_RECOVERY_LOW_POWER_DISCHARGING; - } else { - goto reschedule; - } - - layer_set_frame((Layer *) &data->kino_layer, &kino_area); - kino_layer_set_reel_with_resource(&data->kino_layer, res_id); - layer_mark_dirty(&data->kino_layer.layer); - - reschedule: - data->saved_state = current_state; - data->timer = app_timer_register(LOW_POWER_APP_STATE_UPDATE_TIME_MS, prv_refresh_state, data); -} - -//////////////////////////////////////////////////////////// -// Window loading, unloading, initializing - -static void prv_window_unload_handler(Window* window) { - LowPowerAppData *data = window_get_user_data(window); - if (!data) { - // Sanity check - return; - } - - kino_layer_deinit(&data->kino_layer); - app_timer_cancel(data->timer); - app_free(data); -} - -static void prv_window_load_handler(Window* window) { - LowPowerAppData *data = window_get_user_data(window); - - data->discharging_kino_area = GRect(PBL_IF_RECT_ELSE(4, 5), - PBL_IF_RECT_ELSE(2, 4), - data->window.layer.bounds.size.w, - data->window.layer.bounds.size.h); - data->charging_kino_area = GRect(0, 0, data->window.layer.bounds.size.w, - data->window.layer.bounds.size.h); - - kino_layer_init(&data->kino_layer, &data->discharging_kino_area); - kino_layer_set_reel_with_resource(&data->kino_layer, RESOURCE_ID_RECOVERY_LOW_POWER_DISCHARGING); - layer_add_child(&window->layer, &data->kino_layer.layer); - - data->timer = app_timer_register(LOW_POWER_APP_STATE_UPDATE_TIME_MS, prv_refresh_state, data); -} - -static void prv_prf_low_power_app_window_push(void) { - LowPowerAppData *data = app_malloc_check(sizeof(LowPowerAppData)); - - *data = (LowPowerAppData){}; - - Window* window = &data->window; - window_init(window, WINDOW_NAME("Low Power App")); - window_set_user_data(window, data); - window_set_overrides_back_button(window, true); - window_set_fullscreen(window, true); - window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - window_set_window_handlers(window, &(WindowHandlers){ - .load = prv_window_load_handler, - .unload = prv_window_unload_handler, - }); - app_window_stack_push(window, false); -} - -static void s_main(void) { - launcher_block_popups(true); - - prv_prf_low_power_app_window_push(); - - app_event_loop(); - - launcher_block_popups(false); -} - -//////////////////////////////////////////////////////////// -// Public functions - -const PebbleProcessMd* prf_low_power_app_get_info() { - static const PebbleProcessMdSystem s_app_info = { - .common = { - .main_func = &s_main, - .visibility = ProcessVisibilityHidden, - // UUID: f29f18ac-bbec-452b-9262-49b5f6e5c920 - .uuid = {0xf2, 0x9f, 0x18, 0xac, 0xbb, 0xec, 0x45, 0x2b, - 0x92, 0x62, 0x49, 0xb5, 0xf6, 0xe5, 0xc9, 0x20}, - }, - .name = "Low Power App", - .run_level = ProcessAppRunLevelSystem, - }; - return (const PebbleProcessMd*) &s_app_info; -} diff --git a/src/fw/apps/prf_apps/prf_low_power_app.h b/src/fw/apps/prf_apps/prf_low_power_app.h deleted file mode 100644 index 63fc865be0..0000000000 --- a/src/fw/apps/prf_apps/prf_low_power_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* prf_low_power_app_get_info(); diff --git a/src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.c b/src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.c deleted file mode 100644 index 50fe863665..0000000000 --- a/src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "getting_started_button_combo.h" - -#include "applib/ui/app_window_stack.h" -#include "applib/graphics/gtypes.h" - -#include "apps/core_apps/spinner_ui_window.h" -#include "kernel/util/factory_reset.h" -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "process_management/process_manager.h" -#include "services/common/system_task.h" -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR -#include "services/prf/accessory/accessory_imaging.h" -#endif -#include "system/logging.h" -#include "util/size.h" - -void getting_started_button_combo_init(GettingStartedButtonComboState *state, - GettingStartedButtonComboCallback select_callback) { - *state = (GettingStartedButtonComboState) { - .buttons_held_bitset = 0, - .combo_timer = new_timer_create(), - .select_callback = select_callback - }; -} - -void getting_started_button_combo_deinit(GettingStartedButtonComboState *state) { - new_timer_delete(state->combo_timer); -} - -static void prv_factory_reset(void *not_used) { - factory_reset(false /* should_shutdown */); -} - -static void prv_down_cb(void *data) { - Window *spinner_window = spinner_ui_window_get(PBL_IF_COLOR_ELSE(GColorBlue, GColorDarkGray)); - app_window_stack_push(spinner_window, false /* animated */); - - // Factory reset on KernelBG so the animation gets priority - system_task_add_callback(prv_factory_reset, NULL); -} - -#ifdef RECOVERY_FW -static void prv_mfg_mode_cb(void *data) { -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - accessory_imaging_enable(); -#endif - mfg_enter_mfg_mode_and_launch_app(); -} -#endif - -static void prv_timeout_expired(void *data) { - PBL_LOG(LOG_LEVEL_INFO, "Button combo timeout expired!"); - - // Timeout expired, jump over the app thread to do the thing. - void (*real_callback)(void*) = data; - process_manager_send_callback_event_to_process(PebbleTask_App, real_callback, NULL); -} - -static void prv_update_state(GettingStartedButtonComboState *state) { - const uint32_t COMBO_HOLD_MS = 5 * 1000; // Wait for 5 seconds - - // Map of button combos -> callback to call if we hit it. - const struct { - uint8_t desired_bitset; - void (*callback)(void*); - } BUTTON_COMBOS[] = { - { (1 << BUTTON_ID_SELECT), state->select_callback }, - { (1 << BUTTON_ID_DOWN), prv_down_cb }, -#ifdef RECOVERY_FW - { (1 << BUTTON_ID_UP) | (1 << BUTTON_ID_SELECT), prv_mfg_mode_cb }, -#endif - }; - - for (unsigned int i = 0; i < ARRAY_LENGTH(BUTTON_COMBOS); ++i) { - if (state->buttons_held_bitset == BUTTON_COMBOS[i].desired_bitset) { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting timer for combo #%d", i); - - new_timer_start(state->combo_timer, COMBO_HOLD_MS, prv_timeout_expired, - BUTTON_COMBOS[i].callback, 0); - return; - } - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Stopping combo timer"); - - // No combo found, cancel the timer. It's harmless to call this if the timer isn't running. - new_timer_stop(state->combo_timer); -} - -void getting_started_button_combo_button_pressed(GettingStartedButtonComboState *state, - ButtonId button_id) { - bitset8_set(&state->buttons_held_bitset, button_id); - prv_update_state(state); -} - -void getting_started_button_combo_button_released(GettingStartedButtonComboState *state, - ButtonId button_id) { - bitset8_clear(&state->buttons_held_bitset, button_id); - prv_update_state(state); -} diff --git a/src/fw/apps/prf_apps/recovery_first_use_app/recovery_first_use_app.c b/src/fw/apps/prf_apps/recovery_first_use_app/recovery_first_use_app.c deleted file mode 100644 index 2691e312df..0000000000 --- a/src/fw/apps/prf_apps/recovery_first_use_app/recovery_first_use_app.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "recovery_first_use_app.h" - -#include "getting_started_button_combo.h" - -#include "apps/core_apps/spinner_ui_window.h" -#include "applib/fonts/fonts.h" -#include "comm/ble/gap_le_connection.h" -#include "comm/ble/gap_le_device_name.h" -#include "comm/ble/gap_le_connect.h" -#include "drivers/backlight.h" -#include "mfg/mfg_info.h" -#include "mfg/mfg_serials.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" - -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" - -#include "resource/resource_ids.auto.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "applib/app.h" -#include "applib/event_service_client.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/bitmap_layer.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/kino/kino_reel.h" -#include "applib/ui/layer.h" -#include "applib/ui/qr_code.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "applib/ui/window_private.h" -#include "applib/ui/window_stack.h" -#include "process_state/app_state/app_state.h" - -#include "apps/system_apps/settings/settings_time.h" - -#include "services/common/bluetooth/local_id.h" -#include "services/common/bluetooth/pairability.h" -#include "services/common/comm_session/session.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" - -#include "git_version.auto.h" - -#include - -#include -#include -#include - -#define QR_URL_BUFFER_SIZE 72 -#define URL_BUFFER_SIZE 32 -#define NAME_BUFFER_SIZE (BT_DEVICE_NAME_BUFFER_SIZE + 2) - -typedef struct RecoveryFUAppData { - Window launch_app_window; - -#if PLATFORM_ASTERIX || PLATFORM_OBELIX - QRCode qr_code; - char qr_url_buffer[QR_URL_BUFFER_SIZE]; -#else - KinoLayer kino_layer; - TextLayer url_text_layer; - char url_text_buffer[URL_BUFFER_SIZE]; -#endif - bool is_showing_version; - TextLayer name_text_layer; - char name_text_buffer[NAME_BUFFER_SIZE]; - - AppTimer *spinner_close_timer; - - //! Is the mobile app currently connected (comm session is up?) - bool is_pebble_mobile_app_connected; - //! Has the mobile app ever connected during this boot? Used to avoid flickering the layout - //! for brief disconnects. - bool has_pebble_mobile_app_connected; - bool is_pairing_allowed; - bool spinner_is_visible; - bool spinner_should_close; - - EventServiceInfo pebble_mobile_app_event_info; - EventServiceInfo bt_connection_event_info; - EventServiceInfo pebble_gather_logs_event_info; - EventServiceInfo ble_device_name_updated_event_info; - - GettingStartedButtonComboState button_combo_state; -} RecoveryFUAppData; - -#if PLATFORM_ASTERIX || PLATFORM_OBELIX -static const char *s_qr_url_fmt = "https://qr.repebble.com/?sn=%s&model=%s"; -#endif - -// Unfortunately, the event_service_client_subscribe doesn't take a void *context... -static RecoveryFUAppData *s_fu_app_data; - -static void prv_update_name_text(RecoveryFUAppData *data); - -//////////////////////////////////////////////////////////// -// Spinner Logic - -static void prv_pop_spinner(void *not_used) { - if (s_fu_app_data && s_fu_app_data->spinner_should_close) { - app_window_stack_pop(false /* animated */); - s_fu_app_data->spinner_is_visible = false; - s_fu_app_data->spinner_should_close = false; - } -} - -static void prv_show_spinner(RecoveryFUAppData *data) { - if (!data->spinner_is_visible) { - Window *spinner_window = spinner_ui_window_get(PBL_IF_COLOR_ELSE(GColorRed, GColorDarkGray)); - app_window_stack_push(spinner_window, false /* animated */); - } - data->spinner_is_visible = true; - data->spinner_should_close = false; -} - -static void prv_hide_spinner(RecoveryFUAppData *data) { - data->spinner_should_close = true; - data->spinner_close_timer = app_timer_register(3000, prv_pop_spinner, data); -} - -//////////////////////////////////////////////////////////// -// Button Handlers -static void prv_select_combo_callback(void *cb_data) { - // When the user holds select for a long period of time, toggle between showing the help URL - // and the version of the firmware. - - RecoveryFUAppData *data = app_state_get_user_data(); - data->is_showing_version = !data->is_showing_version; - - prv_update_name_text(data); -} - -static void prv_raw_down_handler(ClickRecognizerRef recognizer, void *context) { - RecoveryFUAppData *data = app_state_get_user_data(); - - getting_started_button_combo_button_pressed(&data->button_combo_state, - click_recognizer_get_button_id(recognizer)); -} - -static void prv_raw_up_handler(ClickRecognizerRef recognizer, void *context) { - RecoveryFUAppData *data = app_state_get_user_data(); - - getting_started_button_combo_button_released(&data->button_combo_state, - click_recognizer_get_button_id(recognizer)); -} - -static void prv_click_configure(void* context) { - window_raw_click_subscribe(BUTTON_ID_UP, prv_raw_down_handler, prv_raw_up_handler, NULL); - window_raw_click_subscribe(BUTTON_ID_SELECT, prv_raw_down_handler, prv_raw_up_handler, NULL); - window_raw_click_subscribe(BUTTON_ID_DOWN, prv_raw_down_handler, prv_raw_up_handler, NULL); -} - -//////////////////////////////////////////////////////////// -// Windows - -#if !PLATFORM_ASTERIX && !PLATFORM_OBELIX -static void prv_update_background_image_and_url_text(RecoveryFUAppData *data) { - uint32_t icon_res_id; - const char *url_string; - GColor background; - GRect kino_area; - int16_t icon_x_offset; - int16_t icon_y_offset; - int16_t text_y_offset; - - // Have we gone through first use before? If not, show first use UI. Otherwise show recovery UI. - const bool first_use_is_complete = shared_prf_storage_get_getting_started_complete(); - - // Pick the right layout for the screen - if (first_use_is_complete || data->has_pebble_mobile_app_connected) { - // If first use was completed, it means we're in recovery mode. - icon_res_id = RESOURCE_ID_LAUNCH_APP; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS - icon_x_offset = 41; - icon_y_offset = -21; - text_y_offset = 140; -#else - icon_x_offset = PBL_IF_RECT_ELSE(49, 67); - icon_y_offset = 28; - text_y_offset = 124; -#endif - } else { - icon_res_id = RESOURCE_ID_MOBILE_APP_ICON; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS - icon_x_offset = 74; - icon_y_offset = 56; - text_y_offset = 121; -#else - icon_x_offset = PBL_IF_RECT_ELSE(49, 67); - icon_y_offset = 38; - text_y_offset = 90; -#endif - } - - if (first_use_is_complete) { -#if PBL_BW && !PLATFORM_TINTIN - // Override the icon to use the fullscreen version - icon_res_id = RESOURCE_ID_LAUNCH_APP; - icon_x_offset = 0; - icon_y_offset = 0; - - url_string = ""; // URL is baked into the background image - background = GColorWhite; -#else - url_string = "pebble.com/sos"; - background = PBL_IF_COLOR_ELSE(GColorRed, GColorWhite);; -#endif - } else { - url_string = "pebble.com/app"; - background = PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite); - } - - // Create the icon - KinoReel *icon_reel = kino_reel_create_with_resource(icon_res_id); - if (!icon_reel) { - PBL_CROAK("Couldn't create kino reel"); - } - - // Position the icon - kino_area = GRect(icon_x_offset, icon_y_offset, data->launch_app_window.layer.bounds.size.w, - data->launch_app_window.layer.bounds.size.h); - layer_set_frame((Layer *) &data->kino_layer, &kino_area); - kino_layer_set_alignment(&data->kino_layer, GAlignTopLeft); - window_set_background_color(&data->launch_app_window, background); - - kino_layer_set_reel(&data->kino_layer, icon_reel, /* take_ownership */ true); - - // Configure the url text layer - data->url_text_layer.layer.frame.origin.y = text_y_offset; - text_layer_set_text(&data->url_text_layer, url_string); -} -#endif - -static void prv_update_name_text(RecoveryFUAppData *data) { - const GAPLEConnection *gap_conn = gap_le_connection_any(); - - // Set the name text - if (data->is_showing_version) { - size_t len = MIN(strlen(GIT_TAG), sizeof(data->name_text_buffer) - 1); - memcpy(data->name_text_buffer, GIT_TAG, len); - data->name_text_buffer[len] = '\0'; - } else if (bt_driver_classic_is_connected()) { - // If BT Classic connected, show the name of the connected device - bt_driver_classic_copy_connected_device_name(data->name_text_buffer); - } else if ((comm_session_get_system_session() != NULL) && (gap_conn != NULL)) { - // If we have connected to a device and we have a connection to the mobile app, show the device - // name (we are required to have a connection to mobile app to get the name). - gap_le_connection_copy_device_name(gap_conn, data->name_text_buffer, - BT_DEVICE_NAME_BUFFER_SIZE); - } else { - // If we aren't connected and/or don't have a session, display the name of the device - // so it's easier for a user to figure out what they should be trying to connect to - bt_local_id_copy_device_name(data->name_text_buffer, false); - - // For debugging purposes, we are going to add -'s to the beginning and end of the name - // if we are connected to a BLE device but don't have a session - if (gap_le_connect_is_connected_as_slave()) { - memmove(&data->name_text_buffer[1], &data->name_text_buffer[0], BT_DEVICE_NAME_BUFFER_SIZE); - data->name_text_buffer[0] = '-'; - strcat(data->name_text_buffer, "-"); - } - } - text_layer_set_text(&data->name_text_layer, data->name_text_buffer); - -#if !PLATFORM_ASTERIX && !PLATFORM_OBELIX - // Set the name font -#if !PLATFORM_ROBERT && !PLATFORM_CALCULUS - const bool first_use_is_complete = shared_prf_storage_get_getting_started_complete(); - const char *name_font_key; - if (first_use_is_complete || data->has_pebble_mobile_app_connected || data->is_showing_version) { - name_font_key = FONT_KEY_GOTHIC_14; - } else { - name_font_key = FONT_KEY_GOTHIC_24; - } - text_layer_set_font(&data->name_text_layer, fonts_get_system_font(name_font_key)); -#endif - - // Update the size of the name text layer based on the new content. - - // First set the text layer to be the width of the entire window and only a single line of text - // high. - layer_set_frame(&data->name_text_layer.layer, - &(GRect) { { 0, 0 }, { data->launch_app_window.layer.frame.size.w, 26 } }); - - // Ask the text layer for a content size based on the frame we just set. If there's no text, - // hide the layer by setting the size to zero. - GSize content_size = { 0, 0 }; - if (strlen(data->name_text_layer.text)) { - content_size = text_layer_get_content_size(app_state_get_graphics_context(), - &data->name_text_layer); - content_size.w += 4; - content_size.h += 4; - } - - // Actually set the frame centered on the screen and just below the url_text_layer. - const int16_t window_width = data->launch_app_window.layer.frame.size.w; - const int16_t text_x_offset = (window_width - content_size.w) / 2; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS - const int16_t text_y_offset = 33; -#else - const int16_t text_y_offset = 22; -#endif - const GRect frame = { { text_x_offset, text_y_offset }, content_size }; - layer_set_frame(&data->name_text_layer.layer, &frame); -#endif -} - -static void prv_window_load(Window* window) { - struct RecoveryFUAppData *data = (struct RecoveryFUAppData*) window_get_user_data(window); - -#if PLATFORM_ASTERIX || PLATFORM_OBELIX - char serial_number[MFG_SERIAL_NUMBER_SIZE + 1]; - char model_name[MFG_INFO_MODEL_STRING_LENGTH]; - - mfg_info_get_serialnumber(serial_number, sizeof(serial_number)); - mfg_info_get_model(model_name); - - snprintf(data->qr_url_buffer, QR_URL_BUFFER_SIZE, s_qr_url_fmt, serial_number, model_name); - - QRCode* qr_code = &data->qr_code; - qr_code_init_with_parameters(qr_code, - &GRect(10, 10, window->layer.bounds.size.w - 20, - window->layer.bounds.size.h - 30), - data->qr_url_buffer, strlen(data->qr_url_buffer), QRCodeECCMedium, - GColorBlack, GColorWhite); - layer_add_child(&window->layer, &qr_code->layer); - - TextLayer* name_text_layer = &data->name_text_layer; - text_layer_init_with_parameters(name_text_layer, - &GRect(0, window->layer.bounds.size.h - 20, - window->layer.bounds.size.w, 20), - NULL, fonts_get_system_font(FONT_KEY_GOTHIC_14), - GColorBlack, GColorWhite, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&window->layer, &name_text_layer->layer); - data->is_showing_version = false; - - prv_update_name_text(data); -#else - KinoLayer *kino_layer = &data->kino_layer; - kino_layer_init(kino_layer, &window->layer.bounds); - layer_add_child(&window->layer, &kino_layer->layer); - -#if PLATFORM_ROBERT || PLATFORM_CALCULUS - const char *url_font_key = FONT_KEY_GOTHIC_28_BOLD; - const GColor name_bg_color = GColorClear; - const char *name_font_key = FONT_KEY_GOTHIC_24; -#else - const char *url_font_key = FONT_KEY_GOTHIC_18_BOLD; - const GColor name_bg_color = GColorWhite; - const char *name_font_key = FONT_KEY_GOTHIC_14; -#endif - - TextLayer* url_text_layer = &data->url_text_layer; - text_layer_init_with_parameters(url_text_layer, - &GRect(0, 124, window->layer.bounds.size.w, 64), - NULL, fonts_get_system_font(url_font_key), - GColorBlack, GColorClear, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&window->layer, &url_text_layer->layer); - - TextLayer* name_text_layer = &data->name_text_layer; - text_layer_init_with_parameters(name_text_layer, - &data->launch_app_window.layer.frame, - NULL, fonts_get_system_font(name_font_key), - GColorBlack, name_bg_color, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&url_text_layer->layer, &name_text_layer->layer); - data->is_showing_version = false; - - prv_update_background_image_and_url_text(data); - prv_update_name_text(data); -#endif -} - -static void prv_push_window(struct RecoveryFUAppData* data) { - Window* window = &data->launch_app_window; - - window_init(window, WINDOW_NAME("First Use / Recovery")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers){ - .load = prv_window_load, - }); - window_set_click_config_provider_with_context(window, prv_click_configure, window); - - window_set_fullscreen(window, true); - window_set_overrides_back_button(window, true); - - const bool animated = false; - app_window_stack_push(window, animated); -} - -//////////////////// -// App Event Handler + Loop - -static void prv_allow_pairing(RecoveryFUAppData* data, bool allow) { - if (data->is_pairing_allowed == allow) { - return; - } - data->is_pairing_allowed = allow; - if (allow) { - bt_pairability_use(); - } else { - bt_pairability_release(); - } -} - -static void prv_pebble_mobile_app_event_handler(PebbleEvent *event, void *context) { - if (!s_fu_app_data) { - return; - } - - if (!event->bluetooth.comm_session_event.is_system) { - return; - } - - const bool is_connected = event->bluetooth.comm_session_event.is_open; - - s_fu_app_data->is_pebble_mobile_app_connected = event->bluetooth.comm_session_event.is_open; - if (is_connected) { - s_fu_app_data->has_pebble_mobile_app_connected = true; - gap_le_device_name_request_all(); - } -#if !PLATFORM_ASTERIX && !PLATFORM_OBELIX - prv_update_background_image_and_url_text(s_fu_app_data); -#endif - prv_update_name_text(s_fu_app_data); -} - -static void prv_bt_event_handler(PebbleEvent *event, void *context) { - if (!s_fu_app_data) { - return; - } - prv_update_name_text(s_fu_app_data); -} - -static void prv_gather_debug_info_event_handler(PebbleEvent *event, void *context) { - if (!s_fu_app_data) { - return; - } - if (event->debug_info.state == DebugInfoStateStarted) { - prv_show_spinner(s_fu_app_data); - } else { - prv_hide_spinner(s_fu_app_data); - } -} - -//////////////////// -// App boilerplate - -static void handle_init(void) { - launcher_block_popups(true); - - RecoveryFUAppData *data = app_malloc_check(sizeof(RecoveryFUAppData)); - s_fu_app_data = data; - - *data = (RecoveryFUAppData){}; - app_state_set_user_data(data); - - const bool is_connected = (comm_session_get_system_session() != NULL); - data->is_pebble_mobile_app_connected = is_connected; - prv_allow_pairing(data, !is_connected); - - data->pebble_mobile_app_event_info = (EventServiceInfo) { - .type = PEBBLE_COMM_SESSION_EVENT, - .handler = prv_pebble_mobile_app_event_handler, - }; - event_service_client_subscribe(&data->pebble_mobile_app_event_info); - - data->pebble_gather_logs_event_info = (EventServiceInfo) { - .type = PEBBLE_GATHER_DEBUG_INFO_EVENT, - .handler = prv_gather_debug_info_event_handler, - }; - event_service_client_subscribe(&data->pebble_gather_logs_event_info); - - data->bt_connection_event_info = (EventServiceInfo) { - .type = PEBBLE_BT_CONNECTION_EVENT, - .handler = prv_bt_event_handler, - }; - event_service_client_subscribe(&data->bt_connection_event_info); - - data->ble_device_name_updated_event_info = (EventServiceInfo) { - .type = PEBBLE_BLE_DEVICE_NAME_UPDATED_EVENT, - .handler = prv_bt_event_handler, - }; - event_service_client_subscribe(&data->ble_device_name_updated_event_info); - - getting_started_button_combo_init(&data->button_combo_state, prv_select_combo_callback); - - prv_push_window(data); -} - -static void handle_deinit(void) { - RecoveryFUAppData *data = app_state_get_user_data(); - - getting_started_button_combo_deinit(&data->button_combo_state); - -#if !PLATFORM_ASTERIX && !PLATFORM_OBELIX - kino_layer_deinit(&data->kino_layer); -#endif - - event_service_client_unsubscribe(&data->pebble_mobile_app_event_info); - event_service_client_unsubscribe(&data->bt_connection_event_info); - event_service_client_unsubscribe(&data->pebble_gather_logs_event_info); - event_service_client_unsubscribe(&data->ble_device_name_updated_event_info); - - app_window_stack_pop_all(false); - - prv_allow_pairing(data, false); - - app_free(data); - s_fu_app_data = NULL; - - launcher_block_popups(false); -} - -static void prv_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* recovery_first_use_app_get_app_info(void) { - static const PebbleProcessMdSystem s_recovery_first_use_app = { - .common = { - .main_func = prv_main, - .visibility = ProcessVisibilityHidden, - // UUID: 85b80081-d78f-41aa-96fa-a821c79f3f0f - .uuid = { - 0x85, 0xb8, 0x00, 0x81, 0xd7, 0x8f, 0x41, 0xaa, - 0x96, 0xfa, 0xa8, 0x21, 0xc7, 0x9f, 0x3f, 0x0f - }, - }, - .name = "Getting Started", - .run_level = ProcessAppRunLevelSystem, - }; - return (const PebbleProcessMd*) &s_recovery_first_use_app; -} diff --git a/src/fw/apps/prf_apps/recovery_first_use_app/recovery_first_use_app.h b/src/fw/apps/prf_apps/recovery_first_use_app/recovery_first_use_app.h deleted file mode 100644 index 5e390b0abf..0000000000 --- a/src/fw/apps/prf_apps/recovery_first_use_app/recovery_first_use_app.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -typedef struct PebbleProcessMd PebbleProcessMd; - -const PebbleProcessMd* recovery_first_use_app_get_app_info(void); diff --git a/src/fw/apps/sdk/app.c b/src/fw/apps/sdk/app.c new file mode 100644 index 0000000000..8410f6d82d --- /dev/null +++ b/src/fw/apps/sdk/app.c @@ -0,0 +1,143 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app.h" + +#include "applib/app.h" +#include "applib/graphics/perimeter.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window_private.h" +#include "apps/system_app_ids.h" +#include "apps/system/launcher/launcher.h" +#include "apps/system/timeline/timeline.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/clock.h" +#include "shell/sdk/shell_sdk.h" +#include "shell/sdk/watchface.h" +#include "system/logging.h" + +typedef struct SdkAppData { + Window window; + TextLayer info_text_layer; + TextLayer time_text_layer; + char time_buffer[TIME_STRING_TIME_LENGTH]; +} SdkAppData; + +static void prv_sdk_home_update_proc(Layer *layer, GContext *ctx) { + window_do_layer_update_proc(layer, ctx); + + SdkAppData *data = app_state_get_user_data(); + const int time_max_y = grect_get_max_y(&data->time_text_layer.layer.frame); + const int line_top_margin = 14; + const int line_stroke_width = 2; + graphics_context_set_fill_color(ctx, GColorBlack); + graphics_fill_rect(ctx, &GRect(0, time_max_y + line_top_margin, DISP_COLS, line_stroke_width)); +} + +static void prv_update_time(SdkAppData *data) { + clock_copy_time_string(data->time_buffer, sizeof(data->time_buffer)); + text_layer_set_text(&data->time_text_layer, data->time_buffer); +} + +static void prv_update_info(SdkAppData *data) { + const AppInstallId app_id = shell_sdk_get_last_installed_app(); + const char *text; + const unsigned int tip_delay_s = 5; + if ((app_id == INSTALL_ID_INVALID) || shell_sdk_last_installed_app_is_watchface()) { + text = "Install an app to continue"; + } else if ((rtc_get_time() / tip_delay_s) & 1) { + // Show the launcher help on every odd set of five seconds since the epoch + text = "Press Select to access Launcher"; + } else { + text = "Press Down to access Timeline"; + } + text_layer_set_text(&data->info_text_layer, text); +} + +static void prv_update_ui(SdkAppData *data) { + prv_update_time(data); + prv_update_info(data); +} + +static void prv_handle_tick_timer(struct tm *tick_time, TimeUnits units_changed) { + prv_update_ui(app_state_get_user_data()); +} + +static void prv_config_provider(void *data) { +} + +static void prv_init(void) { + SdkAppData *data = app_malloc_check(sizeof(SdkAppData)); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("SDK Home")); + window_set_click_config_provider(window, prv_config_provider); + window_set_overrides_back_button(window, true); + window_set_fullscreen(window, true); + layer_set_update_proc(&window->layer, prv_sdk_home_update_proc); + + GRect frame = data->window.layer.frame; + + const int top_margin = PBL_IF_RECT_ELSE(19, 25); + const int time_height = 46; + frame.origin.y = top_margin; + frame.size.h = time_height; + + TextLayer *time_text_layer = &data->time_text_layer; + text_layer_init(time_text_layer, &frame); + text_layer_set_font(time_text_layer, fonts_get_system_font(FONT_KEY_LECO_42_NUMBERS)); + text_layer_set_text_alignment(time_text_layer, GTextAlignmentCenter); + text_layer_set_overflow_mode(time_text_layer, GTextOverflowModeWordWrap); + text_layer_set_background_color(&data->time_text_layer, GColorClear); + layer_add_child(&window->layer, &time_text_layer->layer); + + const int time_padding = 22; + frame.origin.y += time_height + time_padding; + frame.size.h = DISP_ROWS - frame.origin.y; + + TextLayer *info_text_layer = &data->info_text_layer; + text_layer_init(info_text_layer, &frame); + text_layer_set_font(info_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment(info_text_layer, GTextAlignmentCenter); + text_layer_set_background_color(&data->info_text_layer, GColorClear); + layer_add_child(&window->layer, &info_text_layer->layer); + +#if PBL_ROUND + const uint8_t inset = 8; + text_layer_enable_screen_text_flow_and_paging(info_text_layer, inset); +#endif + + app_state_set_user_data(data); + + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_tick_timer); + + prv_update_ui(data); + + app_window_stack_push(window, true /* is_animated */); +} + +static void s_main(void) { + prv_init(); + + app_event_loop(); +} + +const PebbleProcessMd* sdk_app_get_info(void) { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = s_main, + // UUID: 1197fc39-47e7-439b-82be-f56d9ba1dbd8 + .uuid = { 0x11, 0x97, 0xfc, 0x39, 0x47, 0xe7, 0x43, 0x9b, + 0x82, 0xbe, 0xf5, 0x6d, 0x9b, 0xa1, 0xdb, 0xd8 }, + .process_type = ProcessTypeWatchface + }, + .icon_resource_id = RESOURCE_ID_MENU_ICON_TICTOC_WATCH, + .name = "TicToc" + }; + return (const PebbleProcessMd*) &s_app_md; +} diff --git a/src/fw/apps/sdk/app.h b/src/fw/apps/sdk/app.h new file mode 100644 index 0000000000..ff55cd471a --- /dev/null +++ b/src/fw/apps/sdk/app.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* sdk_app_get_info(void); diff --git a/src/fw/apps/sdk/sdk_app.c b/src/fw/apps/sdk/sdk_app.c deleted file mode 100644 index ee9f4ef7ac..0000000000 --- a/src/fw/apps/sdk/sdk_app.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sdk_app.h" - -#include "applib/app.h" -#include "applib/graphics/perimeter.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window_private.h" -#include "apps/system_app_ids.h" -#include "apps/system_apps/launcher/launcher_app.h" -#include "apps/system_apps/timeline/timeline.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "services/common/clock.h" -#include "shell/sdk/shell_sdk.h" -#include "shell/sdk/watchface.h" -#include "system/logging.h" - -typedef struct SdkAppData { - Window window; - TextLayer info_text_layer; -#if CAPABILITY_HAS_SDK_SHELL4 - TextLayer time_text_layer; - char time_buffer[TIME_STRING_TIME_LENGTH]; -#endif -} SdkAppData; - -static void prv_sdk_home_update_proc(Layer *layer, GContext *ctx) { - window_do_layer_update_proc(layer, ctx); - -#if CAPABILITY_HAS_SDK_SHELL4 - SdkAppData *data = app_state_get_user_data(); - const int time_max_y = grect_get_max_y(&data->time_text_layer.layer.frame); - const int line_top_margin = 14; - const int line_stroke_width = 2; - graphics_context_set_fill_color(ctx, GColorBlack); - graphics_fill_rect(ctx, &GRect(0, time_max_y + line_top_margin, DISP_COLS, line_stroke_width)); -#endif -} - -static void prv_update_time(SdkAppData *data) { -#if CAPABILITY_HAS_SDK_SHELL4 - clock_copy_time_string(data->time_buffer, sizeof(data->time_buffer)); - text_layer_set_text(&data->time_text_layer, data->time_buffer); -#endif -} - -static void prv_update_info(SdkAppData *data) { - const AppInstallId app_id = shell_sdk_get_last_installed_app(); - const char *text; -#if CAPABILITY_HAS_SDK_SHELL4 - const unsigned int tip_delay_s = 5; - if ((app_id == INSTALL_ID_INVALID) || shell_sdk_last_installed_app_is_watchface()) { - text = "Install an app to continue"; - } else if ((rtc_get_time() / tip_delay_s) & 1) { - // Show the launcher help on every odd set of five seconds since the epoch - text = "Press Select to access Launcher"; - } else { - text = "Press Down to access Timeline"; - } -#else - GColor color; - if (app_id == INSTALL_ID_INVALID) { - text = "Install an app to continue or press up / down to browse the timeline"; - color = GColorChromeYellow; - } else { - text = "Press select to launch your app or press up / down to browse the timeline"; - color = GColorPictonBlue; - } - window_set_background_color(&data->window, PBL_IF_COLOR_ELSE(color, GColorWhite)); -#endif - text_layer_set_text(&data->info_text_layer, text); -} - -static void prv_update_ui(SdkAppData *data) { - prv_update_time(data); - prv_update_info(data); -} - -static void prv_handle_tick_timer(struct tm *tick_time, TimeUnits units_changed) { - prv_update_ui(app_state_get_user_data()); -} - -#if !CAPABILITY_HAS_SDK_SHELL4 -static void prv_launch_last_installed_app(ClickRecognizerRef recognizer, void *data) { - const AppInstallId app_id = shell_sdk_get_last_installed_app(); - - PBL_LOG(LOG_LEVEL_DEBUG, "Last installed app is %"PRId32, app_id); - - if (app_id != INSTALL_ID_INVALID) { - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = app_id }); - } -} - -static void prv_launch_timeline(ClickRecognizerRef recognizer, void *data) { - static TimelineArgs s_timeline_args = { - .launch_into_pin = false, - .pin_id = UUID_INVALID_INIT, - }; - - const bool is_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); - if (is_up) { - PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in past mode."); - s_timeline_args.direction = TimelineIterDirectionPast; - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in future mode."); - s_timeline_args.direction = TimelineIterDirectionFuture; - } - - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = APP_ID_TIMELINE, - .common.args = &s_timeline_args, - }); -} -#endif - -static void prv_config_provider(void *data) { -#if !CAPABILITY_HAS_SDK_SHELL4 - window_single_click_subscribe(BUTTON_ID_UP, prv_launch_timeline); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_launch_last_installed_app); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_launch_timeline); -#endif -} - -static void prv_init(void) { - SdkAppData *data = app_malloc_check(sizeof(SdkAppData)); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("SDK Home")); - window_set_click_config_provider(window, prv_config_provider); - window_set_overrides_back_button(window, true); - window_set_fullscreen(window, true); - layer_set_update_proc(&window->layer, prv_sdk_home_update_proc); - - GRect frame = data->window.layer.frame; - -#if CAPABILITY_HAS_SDK_SHELL4 - const int top_margin = PBL_IF_RECT_ELSE(19, 25); - const int time_height = 46; - frame.origin.y = top_margin; - frame.size.h = time_height; - - TextLayer *time_text_layer = &data->time_text_layer; - text_layer_init(time_text_layer, &frame); - text_layer_set_font(time_text_layer, fonts_get_system_font(FONT_KEY_LECO_42_NUMBERS)); - text_layer_set_text_alignment(time_text_layer, GTextAlignmentCenter); - text_layer_set_overflow_mode(time_text_layer, GTextOverflowModeWordWrap); - text_layer_set_background_color(&data->time_text_layer, GColorClear); - layer_add_child(&window->layer, &time_text_layer->layer); - - const int time_padding = 22; - frame.origin.y += time_height + time_padding; - frame.size.h = DISP_ROWS - frame.origin.y; -#elif PBL_RECT - const int top_margin = 23; - frame.origin.y = top_margin; - frame.size.h -= top_margin; -#endif - - TextLayer *info_text_layer = &data->info_text_layer; - text_layer_init(info_text_layer, &frame); - text_layer_set_font(info_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(info_text_layer, GTextAlignmentCenter); - text_layer_set_background_color(&data->info_text_layer, GColorClear); - layer_add_child(&window->layer, &info_text_layer->layer); - -#if PBL_ROUND -#if CAPABILITY_HAS_SDK_SHELL4 - const uint8_t inset = 8; -#else - const uint8_t inset = 18; -#endif - text_layer_enable_screen_text_flow_and_paging(info_text_layer, inset); -#endif - - app_state_set_user_data(data); - -#if CAPABILITY_HAS_SDK_SHELL4 - window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - tick_timer_service_subscribe(SECOND_UNIT, prv_handle_tick_timer); -#endif - - prv_update_ui(data); - - app_window_stack_push(window, true /* is_animated */); -} - -static void s_main(void) { - prv_init(); - - app_event_loop(); -} - -const PebbleProcessMd* sdk_app_get_info(void) { - static const PebbleProcessMdSystem s_app_md = { - .common = { - .main_func = s_main, - // UUID: 1197fc39-47e7-439b-82be-f56d9ba1dbd8 - .uuid = { 0x11, 0x97, 0xfc, 0x39, 0x47, 0xe7, 0x43, 0x9b, - 0x82, 0xbe, 0xf5, 0x6d, 0x9b, 0xa1, 0xdb, 0xd8 }, -#if CAPABILITY_HAS_SDK_SHELL4 - .process_type = ProcessTypeWatchface -#endif - }, - .icon_resource_id = RESOURCE_ID_MENU_ICON_TICTOC_WATCH, - .name = "TicToc" - }; - return (const PebbleProcessMd*) &s_app_md; -} diff --git a/src/fw/apps/sdk/sdk_app.h b/src/fw/apps/sdk/sdk_app.h deleted file mode 100644 index 67f7802857..0000000000 --- a/src/fw/apps/sdk/sdk_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* sdk_app_get_info(void); diff --git a/src/fw/apps/system/alarms/alarm_detail.c b/src/fw/apps/system/alarms/alarm_detail.c new file mode 100644 index 0000000000..3719d32765 --- /dev/null +++ b/src/fw/apps/system/alarms/alarm_detail.c @@ -0,0 +1,335 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "alarm_detail.h" +#include "alarm_editor.h" + +#include "applib/ui/action_menu_window.h" +#include "applib/ui/action_menu_window_private.h" +#include "applib/ui/dialogs/actionable_dialog.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "kernel/pbl_malloc.h" +#include "popups/health_tracking_ui.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/alarms/alarm.h" +#include "system/logging.h" + +#ifdef CONFIG_SPEAKER +#include "services/alarms/alarm_tones.h" +#endif + +#include + +#define NUM_SNOOZE_MENU_ITEMS 5 + +#ifdef CONFIG_SPEAKER +// Sound submenu: "Off" + one entry per AlarmTone (Reveille, Beacon, Bell, Chime). +#define NUM_SOUND_MENU_ITEMS 5 +#endif + +typedef enum DetailMenuItemIndex { + DetailMenuItemIndexEnable = 0, + DetailMenuItemIndexDelete, + DetailMenuItemIndexChangeTime, + DetailMenuItemIndexChangeDays, + DetailMenuItemIndexConvertSmart, +#ifdef CONFIG_SPEAKER + DetailMenuItemIndexSound, +#endif + DetailMenuItemIndexVibration, + DetailMenuItemIndexSnooze, + DetailMenuItemIndexNum, +} DetailMenuItemIndex; + +typedef struct AlarmDetailData { + ActionMenuConfig menu_config; + + AlarmId alarm_id; + AlarmInfo alarm_info; + + AlarmEditorCompleteCallback alarm_editor_callback; + void *callback_context; +} AlarmDetailData; + + +static SimpleDialog *prv_snooze_set_confirm_dialog(void) { + SimpleDialog *simple_dialog = simple_dialog_create("AlarmSnoozeSet"); + Dialog *dialog = simple_dialog_get_dialog(simple_dialog); + const char *snooze_text = i18n_noop("Snooze delay set to %d minutes"); + char snooze_buf[64]; + snprintf(snooze_buf, sizeof(snooze_buf), i18n_get(snooze_text, dialog), alarm_get_snooze_delay()); + i18n_free(snooze_text, dialog); + dialog_set_text(dialog, snooze_buf); + dialog_set_icon(dialog, RESOURCE_ID_GENERIC_CONFIRMATION_LARGE); + dialog_set_background_color(dialog, GColorJaegerGreen); + dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT); + return simple_dialog; +} + +static void prv_edit_snooze_delay(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + alarm_set_snooze_delay((uintptr_t)item->action_data); + SimpleDialog *snooze_delay_dialog = prv_snooze_set_confirm_dialog(); + action_menu_set_result_window(action_menu, (Window *)snooze_delay_dialog); +} + +static void prv_toggle_enable_alarm_handler(ActionMenu *action_menu, const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + alarm_set_enabled(data->alarm_id, !data->alarm_info.enabled); + if (data->alarm_editor_callback) { + data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); + } +} + +static void prv_toggle_smart_alarm_handler(ActionMenu *action_menu, const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = context; + + if (!data->alarm_info.is_smart && !activity_prefs_tracking_is_enabled()) { + // Notify about Health and keep the menu open + health_tracking_ui_feature_show_disabled(); + return; + } + + alarm_set_smart(data->alarm_id, !data->alarm_info.is_smart); + if (data->alarm_editor_callback) { + data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); + } +} + +static void prv_edit_time_handler(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + alarm_editor_update_alarm_time(data->alarm_id, + data->alarm_info.is_smart ? AlarmType_Smart : AlarmType_Basic, + data->alarm_editor_callback, data->callback_context); +} + +static void prv_edit_day_handler(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + alarm_editor_update_alarm_days(data->alarm_id, data->alarm_editor_callback, + data->callback_context); +} + +static void prv_delete_alarm_handler(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + alarm_delete(data->alarm_id); + if (data->alarm_editor_callback) { + data->alarm_editor_callback(DELETED, data->alarm_id, data->callback_context); + } +} + +static void prv_toggle_vibrate_handler(ActionMenu *action_menu, const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + alarm_set_vibrate_enabled(data->alarm_id, !data->alarm_info.vibrate_enabled); + if (data->alarm_editor_callback) { + data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); + } +} + +#ifdef CONFIG_SPEAKER +static void prv_disable_sound_handler(ActionMenu *action_menu, const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + alarm_set_sound_enabled(data->alarm_id, false); + if (data->alarm_editor_callback) { + data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); + } +} + +static void prv_select_tone_handler(ActionMenu *action_menu, const ActionMenuItem *item, + void *context) { + AlarmDetailData *data = (AlarmDetailData *) context; + AlarmTone tone = (AlarmTone)(uintptr_t)item->action_data; + alarm_set_tone(data->alarm_id, tone); + alarm_set_sound_enabled(data->alarm_id, true); + if (data->alarm_editor_callback) { + data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); + } +} +#endif + +static ActionMenuLevel *prv_create_main_menu(void) { + ActionMenuLevel *level = task_malloc(sizeof(ActionMenuLevel) + + DetailMenuItemIndexNum * sizeof(ActionMenuItem)); + if (!level) return NULL; + *level = (ActionMenuLevel) { + .num_items = DetailMenuItemIndexNum, + .parent_level = NULL, + .display_mode = ActionMenuLevelDisplayModeWide, + }; + return level; +} + +static ActionMenuLevel *prv_create_snooze_menu(ActionMenuLevel *parent_level) { + ActionMenuLevel *level = task_malloc(sizeof(ActionMenuLevel) + + NUM_SNOOZE_MENU_ITEMS * sizeof(ActionMenuItem)); + if (!level) return NULL; + *level = (ActionMenuLevel) { + .num_items = NUM_SNOOZE_MENU_ITEMS, + .parent_level = parent_level, + .display_mode = ActionMenuLevelDisplayModeWide, + }; + return level; +} + +#ifdef CONFIG_SPEAKER +static ActionMenuLevel *prv_create_sound_menu(ActionMenuLevel *parent_level) { + ActionMenuLevel *level = task_malloc(sizeof(ActionMenuLevel) + + NUM_SOUND_MENU_ITEMS * sizeof(ActionMenuItem)); + if (!level) return NULL; + *level = (ActionMenuLevel) { + .num_items = NUM_SOUND_MENU_ITEMS, + .parent_level = parent_level, + .display_mode = ActionMenuLevelDisplayModeWide, + }; + return level; +} +#endif + +void prv_cleanup_alarm_detail_menu(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); + AlarmDetailData *data = (AlarmDetailData *) context; + i18n_free_all(data); + task_free((void *)root_level->items[DetailMenuItemIndexSnooze].next_level); +#ifdef CONFIG_SPEAKER + task_free((void *)root_level->items[DetailMenuItemIndexSound].next_level); +#endif + task_free((void *)root_level); + task_free(data); + data = NULL; +} + +void alarm_detail_window_push(AlarmId alarm_id, AlarmInfo *alarm_info, + AlarmEditorCompleteCallback alarm_editor_callback, + void *callback_context) { + AlarmDetailData* data = task_malloc_check(sizeof(AlarmDetailData)); + *data = (AlarmDetailData) { + .alarm_id = alarm_id, + .alarm_info = *alarm_info, + .alarm_editor_callback = alarm_editor_callback, + .callback_context = callback_context, + .menu_config = { + .context = data, + .colors.background = ALARMS_APP_HIGHLIGHT_COLOR, + .did_close = prv_cleanup_alarm_detail_menu, + }, + }; + + // Setup main menu items + ActionMenuLevel *main_menu = prv_create_main_menu(); + + main_menu->items[DetailMenuItemIndexDelete] = (ActionMenuItem) { + .label = i18n_get("Delete", data), + .perform_action = prv_delete_alarm_handler, + .action_data = data, + }; + main_menu->items[DetailMenuItemIndexEnable] = (ActionMenuItem) { + .label = data->alarm_info.enabled ? i18n_get("Disable", data) : i18n_get("Enable", data), + .perform_action = prv_toggle_enable_alarm_handler, + .action_data = data, + }; + main_menu->items[DetailMenuItemIndexChangeTime] = (ActionMenuItem) { + .label = i18n_get("Change Time", data), + .perform_action = prv_edit_time_handler, + .action_data = data, + }; + main_menu->items[DetailMenuItemIndexChangeDays] = (ActionMenuItem) { + .label = i18n_get("Change Days", data), + .perform_action = prv_edit_day_handler, + .action_data = data, + }; + main_menu->items[DetailMenuItemIndexConvertSmart] = (ActionMenuItem) { + .label = data->alarm_info.is_smart ? i18n_get("Convert to Basic Alarm", data) : + i18n_get("Convert to Smart Alarm", data), + .perform_action = prv_toggle_smart_alarm_handler, + .action_data = data, + }; +#ifdef CONFIG_SPEAKER + main_menu->items[DetailMenuItemIndexSound] = (ActionMenuItem) { + .label = i18n_get("Sound", data), + .is_leaf = 0, + .next_level = prv_create_sound_menu(main_menu), + }; +#endif + main_menu->items[DetailMenuItemIndexVibration] = (ActionMenuItem) { + .label = data->alarm_info.vibrate_enabled ? i18n_get("Vibration: On", data) + : i18n_get("Vibration: Off", data), + .perform_action = prv_toggle_vibrate_handler, + .action_data = data, + }; + main_menu->items[DetailMenuItemIndexSnooze] = (ActionMenuItem) { + .label = i18n_get("Snooze Delay", data), + .is_leaf = 0, + .next_level = prv_create_snooze_menu(main_menu), + }; + main_menu->separator_index = DetailMenuItemIndexSnooze; + data->menu_config.root_level = main_menu; + + // Setup snooze menu items + ActionMenuLevel *snooze_level = main_menu->items[DetailMenuItemIndexSnooze].next_level; + static const unsigned snooze_delays[NUM_SNOOZE_MENU_ITEMS] = {5, 10, 15, 30, 60}; + static const char *snooze_delay_strs[NUM_SNOOZE_MENU_ITEMS] = { + i18n_noop("5 minutes"), + i18n_noop("10 minutes"), + i18n_noop("15 minutes"), + i18n_noop("30 minutes"), + i18n_noop("1 hour") + }; + + unsigned current_snooze_delay = alarm_get_snooze_delay(); + for (int i = 0; i < NUM_SNOOZE_MENU_ITEMS; i++) { + snooze_level->items[i] = (ActionMenuItem) { + .label = i18n_get(snooze_delay_strs[i], data), + .perform_action = prv_edit_snooze_delay, + .action_data = (void *) (uintptr_t) snooze_delays[i], + }; + + if (current_snooze_delay == snooze_delays[i]) { + snooze_level->default_selected_item = i; + } + } + +#ifdef CONFIG_SPEAKER + // Setup sound menu items: Off + one entry per AlarmTone. + ActionMenuLevel *sound_level = main_menu->items[DetailMenuItemIndexSound].next_level; + static const AlarmTone tone_values[] = { + AlarmTone_Reveille, AlarmTone_Beacon, AlarmTone_Bell, AlarmTone_Chime, + }; + sound_level->items[0] = (ActionMenuItem) { + .label = i18n_get("Off", data), + .perform_action = prv_disable_sound_handler, + .action_data = data, + }; + const int num_tones = (int)(sizeof(tone_values) / sizeof(tone_values[0])); + for (int i = 0; i < num_tones; i++) { + sound_level->items[i + 1] = (ActionMenuItem) { + .label = i18n_get(alarm_tones_get_name(tone_values[i]), data), + .perform_action = prv_select_tone_handler, + .action_data = (void *)(uintptr_t)tone_values[i], + }; + } + // Bounds-check the stored tone before indexing the menu. The 3-bit storage + // field can hold 0..7 but the menu only has num_tones entries; defend + // against corrupted storage or future tone-table changes. + int selected = 0; + if (data->alarm_info.sound_enabled && (int)data->alarm_info.tone < num_tones) { + selected = (int)data->alarm_info.tone + 1; + } + sound_level->default_selected_item = selected; +#endif + + app_action_menu_open(&data->menu_config); +} diff --git a/src/fw/apps/system/alarms/alarm_detail.h b/src/fw/apps/system/alarms/alarm_detail.h new file mode 100644 index 0000000000..44ae551131 --- /dev/null +++ b/src/fw/apps/system/alarms/alarm_detail.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/alarms/alarm.h" +#include "alarm_editor.h" + +void alarm_detail_window_push(AlarmId alarm_id, AlarmInfo *alarm_info, + AlarmEditorCompleteCallback alarm_editor_callback, + void *callback_context); diff --git a/src/fw/apps/system_apps/alarms/alarm_editor.c b/src/fw/apps/system/alarms/alarm_editor.c similarity index 94% rename from src/fw/apps/system_apps/alarms/alarm_editor.c rename to src/fw/apps/system/alarms/alarm_editor.c index 4ff0be90c9..c67d63e09e 100644 --- a/src/fw/apps/system_apps/alarms/alarm_editor.c +++ b/src/fw/apps/system/alarms/alarm_editor.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "alarm_editor.h" @@ -22,13 +9,14 @@ #include "applib/ui/simple_menu_layer.h" #include "applib/ui/time_selection_window.h" #include "applib/ui/ui.h" -#include "apps/system_apps/settings/settings_option_menu.h" +#include "apps/system/settings/option_menu.h" #include "kernel/pbl_malloc.h" #include "popups/health_tracking_ui.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" -#include "services/normal/alarms/alarm.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/alarms/alarm.h" +#include "shell/prefs.h" #include "system/logging.h" #include "system/passert.h" #include "util/size.h" @@ -174,6 +162,11 @@ static void prv_handle_selection(int index, void *callback_context) { .minute = data->alarm_minute, .kind = data->alarm_kind, .is_smart = (data->alarm_type == AlarmType_Smart), + .vibrate_enabled = true, +#ifdef CONFIG_SPEAKER + .sound_enabled = false, + .tone = AlarmTone_Reveille, +#endif }; data->alarm_id = alarm_create(&info); data->complete_callback(CREATED, data->alarm_id, data->callback_context); @@ -272,6 +265,9 @@ static void prv_setup_day_picker_window(AlarmEditorData *data) { ALARMS_APP_HIGHLIGHT_COLOR, GColorWhite); menu_layer_set_click_config_onto_window(&data->day_picker_menu_layer, &data->day_picker_window); + menu_layer_set_scroll_wrap_around(&data->day_picker_menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(&data->day_picker_menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(&data->day_picker_menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); layer_add_child(&data->day_picker_window.layer, menu_layer_get_layer(&data->day_picker_menu_layer)); if (!alarm_get_kind(data->alarm_id, &data->alarm_kind)) { @@ -379,6 +375,11 @@ static void prv_custom_day_picker_handle_selection(MenuLayer *menu_layer, MenuIn .kind = ALARM_KIND_CUSTOM, .scheduled_days = &data->scheduled_days, .is_smart = (data->alarm_type == AlarmType_Smart), + .vibrate_enabled = true, +#ifdef CONFIG_SPEAKER + .sound_enabled = false, + .tone = AlarmTone_Reveille, +#endif }; data->alarm_id = alarm_create(&info); data->complete_callback(CREATED, data->alarm_id, data->callback_context); @@ -427,6 +428,9 @@ static void prv_setup_custom_day_picker_window(AlarmEditorData *data) { GColorWhite); menu_layer_set_click_config_onto_window(&data->custom_day_picker_menu_layer, &data->custom_day_picker_window); + menu_layer_set_scroll_wrap_around(&data->custom_day_picker_menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(&data->custom_day_picker_menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(&data->custom_day_picker_menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); layer_add_child(&data->custom_day_picker_window.layer, menu_layer_get_layer(&data->custom_day_picker_menu_layer)); gbitmap_init_with_resource(&data->selected_icon, RESOURCE_ID_CHECKBOX_ICON_CHECKED); @@ -442,9 +446,6 @@ static void prv_setup_custom_day_picker_window(AlarmEditorData *data) { static void prv_time_picker_window_unload(Window *window) { AlarmEditorData *data = (AlarmEditorData *)window_get_user_data(window); if (data->creating_alarm) { -#if !CAPABILITY_HAS_HEALTH_TRACKING - prv_call_complete_cancelled_if_no_alarm(data); -#endif return; } @@ -533,13 +534,11 @@ static void prv_type_menu_select(OptionMenu *option_menu, int selection, void *c AlarmEditorData *data = settings_option_menu_get_context(context); data->alarm_type = selection; -#if CAPABILITY_HAS_HEALTH_TRACKING if (selection == AlarmType_Smart && !activity_prefs_tracking_is_enabled()) { // Notify about Health and keep the menu open health_tracking_ui_feature_show_disabled(); return; } -#endif if (data->creating_alarm) { app_window_stack_push(&data->time_picker_window.window, true); @@ -583,12 +582,8 @@ Window* alarm_editor_create_new_alarm(AlarmEditorCompleteCallback complete_callb // Setup the windows prv_setup_time_picker_window(data); prv_setup_day_picker_window(data); -#if CAPABILITY_HAS_HEALTH_TRACKING prv_setup_type_menu_window(data); return &data->alarm_type_menu->window; -#else - return &data->time_picker_window.window; -#endif } void alarm_editor_update_alarm_time(AlarmId alarm_id, AlarmType alarm_type, diff --git a/src/fw/apps/system/alarms/alarm_editor.h b/src/fw/apps/system/alarms/alarm_editor.h new file mode 100644 index 0000000000..481fa05cc8 --- /dev/null +++ b/src/fw/apps/system/alarms/alarm_editor.h @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/alarms/alarm.h" +#include "applib/ui/window.h" + +typedef enum { + CREATED, + DELETED, + EDITED, + CANCELLED +} AlarmEditorResult; + +typedef void (*AlarmEditorCompleteCallback)(AlarmEditorResult result, AlarmId id, + void *callback_context); + +Window* alarm_editor_create_new_alarm(AlarmEditorCompleteCallback editor_complete_callback, + void *callback_context); + +void alarm_editor_update_alarm_time(AlarmId alarm_id, AlarmType alarm_type, + AlarmEditorCompleteCallback editor_complete_callback, + void *callback_context); + +void alarm_editor_update_alarm_days(AlarmId alarm_id, + AlarmEditorCompleteCallback editor_complete_callback, + void *callback_context); diff --git a/src/fw/apps/system_apps/alarms/alarms.c b/src/fw/apps/system/alarms/alarms.c similarity index 94% rename from src/fw/apps/system_apps/alarms/alarms.c rename to src/fw/apps/system/alarms/alarms.c index b6ad179f2f..feedf1790e 100644 --- a/src/fw/apps/system_apps/alarms/alarms.c +++ b/src/fw/apps/system/alarms/alarms.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "alarms.h" @@ -30,10 +17,11 @@ #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/timeline/timeline.h" +#include "shell/prefs.h" #include "shell/system_theme.h" #include "system/logging.h" #include "system/passert.h" @@ -62,9 +50,7 @@ typedef struct AlarmsAppData { StatusBarLayer status_layer; GBitmap plus_icon; -#if CAPABILITY_HAS_HEALTH_TRACKING GBitmap smart_alarm_icon; -#endif AlarmNode *alarm_list_head; MenuIndex selected_index; @@ -303,13 +289,11 @@ static void prv_alarm_list_draw_row_callback(GContext *ctx, const Layer *cell_la MenuCellLayerConfig config = { .title = alarm_time_text, .value = enabled, -#if CAPABILITY_HAS_HEALTH_TRACKING .icon = &data->smart_alarm_icon, .icon_align = MenuCellLayerIconAlign_TopLeft, .icon_box_model = &(GBoxModel) { .offset = { 0, 5 }, .margin = { 6, 0 } }, .icon_form_fit = true, .horizontal_inset = PBL_IF_ROUND_ELSE(-6, 0), -#endif .overflow_mode = GTextOverflowModeTrailingEllipsis, }; if (node->info.kind != ALARM_KIND_CUSTOM) { @@ -349,7 +333,6 @@ static void prv_alarm_list_selection_changed_callback(MenuLayer *menu_layer, Men } } -#if CAPABILITY_HAS_HEALTH_TRACKING /////////////////////////////////////////////////////////////////////////////////////////////////// //! Smart Alarm first use dialog @@ -383,7 +366,6 @@ static void prv_push_alarms_app_opened_dialog(AlarmsAppData *data) { // Show immediately since this is the first window and there is already a compositor animation app_window_stack_push(&expandable_dialog->dialog.window, false /* animated */); } -#endif /////////////////////////////////////////////////////////////////////////////////////////////////// //! App boilerplate @@ -415,6 +397,9 @@ static void prv_handle_init(void) { menu_layer_set_highlight_colors(&data->menu_layer, ALARMS_APP_HIGHLIGHT_COLOR, GColorWhite); menu_layer_set_click_config_onto_window(&data->menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(&data->menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(&data->menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(&data->menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); layer_add_child(&data->window.layer, menu_layer_get_layer(&data->menu_layer)); status_bar_layer_init(&data->status_layer); @@ -423,9 +408,7 @@ static void prv_handle_init(void) { status_bar_layer_set_separator_mode(&data->status_layer, StatusBarLayerSeparatorModeNone); layer_add_child(&data->window.layer, status_bar_layer_get_layer(&data->status_layer)); -#if CAPABILITY_HAS_HEALTH_TRACKING gbitmap_init_with_resource(&data->smart_alarm_icon, RESOURCE_ID_SMART_ALARM_ICON_BLACK); -#endif gbitmap_init_with_resource(&data->plus_icon, RESOURCE_ID_PLUS_ICON_BLACK); data->current_plus_icon_resource_id = RESOURCE_ID_PLUS_ICON_BLACK; @@ -457,13 +440,11 @@ static void prv_handle_init(void) { app_window_stack_insert_next(&data->window); } -#if CAPABILITY_HAS_HEALTH_TRACKING uint32_t version = alarm_prefs_get_alarms_app_opened(); if (version == 0) { prv_push_alarms_app_opened_dialog(data); } alarm_prefs_set_alarms_app_opened(CURRENT_ALARMS_APP_VERSION); -#endif } static void prv_handle_deinit(void) { @@ -491,11 +472,7 @@ const PebbleProcessMd* alarms_app_get_info() { .uuid = UUID_ALARMS_DATA_SOURCE, }, .name = i18n_noop("Alarms"), -#if CAPABILITY_HAS_APP_GLANCES .icon_resource_id = RESOURCE_ID_ALARM_CLOCK_TINY, -#elif PLATFORM_TINTIN - .icon_resource_id = RESOURCE_ID_MENU_LAYER_ALARMS_APP_ICON, -#endif }; return (const PebbleProcessMd*) &s_alarms_app_info; } diff --git a/src/fw/apps/system/alarms/alarms.h b/src/fw/apps/system/alarms/alarms.h new file mode 100644 index 0000000000..833e761918 --- /dev/null +++ b/src/fw/apps/system/alarms/alarms.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* alarms_app_get_info(); diff --git a/src/fw/apps/system_apps/app_fetch_ui.c b/src/fw/apps/system/app_fetch_ui.c similarity index 86% rename from src/fw/apps/system_apps/app_fetch_ui.c rename to src/fw/apps/system/app_fetch_ui.c index 282ae0714b..5b07a74faa 100644 --- a/src/fw/apps/system_apps/app_fetch_ui.c +++ b/src/fw/apps/system/app_fetch_ui.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_fetch_ui.h" @@ -31,17 +18,17 @@ #include "process_management/app_manager.h" #include "process_management/worker_manager.h" #include "process_state/app_state/app_state.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/app_fetch_endpoint.h" -#include "services/normal/timeline/timeline_resources.h" -#include "apps/system_apps/timeline/peek_layer.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/app_fetch_endpoint.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "apps/system/timeline/peek_layer.h" #include "shell/normal/watchface.h" #include "shell/shell.h" #include "shell/system_app_state_machine.h" -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_transitions.h" #include "system/logging.h" #include "system/passert.h" -#include "services/common/evented_timer.h" +#include "pbl/services/evented_timer.h" #define FAIL_PAUSE_MS 1000 #define SCROLL_OUT_MS 250 @@ -81,7 +68,7 @@ static void prv_set_progress(AppFetchUIData *data, int16_t progress) { // Launch the desired app static void prv_app_fetch_launch_app(AppFetchUIData *data) { // Let's launch the application we just fetched. - PBL_LOG(LOG_LEVEL_DEBUG, "App Fetch: Putting launch event"); + PBL_LOG_DBG("App Fetch: Putting launch event"); // if this was launched by the phone, it's probably a new install if ((data->next_app_args.common.reason == APP_LAUNCH_PHONE) && @@ -96,11 +83,7 @@ static void prv_app_fetch_launch_app(AppFetchUIData *data) { .common = data->next_app_args.common, .wakeup = data->next_app_args.wakeup_info }; -#if PLATFORM_TINTIN - ext->common.transition = compositor_app_slide_transition_get(true /* slide to right */); -#else ext->common.transition = compositor_dot_transition_app_fetch_get(); -#endif if ((data->next_app_args.common.reason == APP_LAUNCH_WAKEUP) && (data->next_app_args.common.args != NULL)) { ext->common.args = &data->next_app_args.wakeup_info; @@ -189,14 +172,14 @@ static void prv_progress_window_finished(ProgressWindow *window, bool success, v //! Used to clean up the application's data before exiting static void prv_app_fetch_cleanup(AppFetchUIData *data) { - PBL_LOG(LOG_LEVEL_DEBUG, "App Fetch: prv_app_fetch_cleanup"); + PBL_LOG_DBG("App Fetch: prv_app_fetch_cleanup"); event_service_client_unsubscribe(&data->fetch_event_info); event_service_client_unsubscribe(&data->connect_event_info); } //! Used when the app fetch process has failed static void prv_app_fetch_failure(AppFetchUIData *data, uint8_t error_code) { - PBL_LOG(LOG_LEVEL_WARNING, "App Fetch: prv_app_fetch_failure: %d", error_code); + PBL_LOG_WRN("App Fetch: prv_app_fetch_failure: %d", error_code); if (error_code == AppFetchResultUserCancelled) { app_window_stack_pop(true); @@ -207,13 +190,13 @@ static void prv_app_fetch_failure(AppFetchUIData *data, uint8_t error_code) { app_install_entry_is_watchface(&data->install_entry)) { // We failed to fetch a watchface and it was our default. // Invalidate it and it will be reassigned to one that exists next time around. - PBL_LOG(LOG_LEVEL_WARNING, "Default watchface fetch failed, setting INVALID as default"); + PBL_LOG_WRN("Default watchface fetch failed, setting INVALID as default"); watchface_set_default_install_id(INSTALL_ID_INVALID); } else if ((worker_manager_get_default_install_id() == data->install_entry.install_id) && app_install_entry_has_worker(&data->install_entry)) { // We failed to fetch a worker and it was our default. // Invalidate it and it will be reassigned to one that is launched next. - PBL_LOG(LOG_LEVEL_WARNING, "Default worker fetch failed, setting INVALID as default"); + PBL_LOG_WRN("Default worker fetch failed, setting INVALID as default"); worker_manager_set_default_install_id(INSTALL_ID_INVALID); } @@ -229,7 +212,7 @@ static void prv_app_fetch_event_handler(PebbleEvent *event, void *context) { // We have starting the App Fetch Process if (af_event->type == AppFetchEventTypeStart) { - PBL_LOG(LOG_LEVEL_DEBUG, "App Fetch: Got the start event"); + PBL_LOG_DBG("App Fetch: Got the start event"); // We have received a new progress event } else if (af_event->type == AppFetchEventTypeProgress) { @@ -282,7 +265,7 @@ static void handle_init(void) { // retrieve data about the AppInstallId given if (!app_install_get_entry_for_install_id(data->next_app_args.app_id, &data->install_entry)) { - PBL_LOG(LOG_LEVEL_ERROR, "App Fetch: Error getting entry for id: %"PRIu32"", + PBL_LOG_ERR("App Fetch: Error getting entry for id: %"PRIu32"", data->next_app_args.app_id); return; } diff --git a/src/fw/apps/system/app_fetch_ui.h b/src/fw/apps/system/app_fetch_ui.h new file mode 100644 index 0000000000..b5d30b1f22 --- /dev/null +++ b/src/fw/apps/system/app_fetch_ui.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_install_types.h" +#include "process_management/launch_config.h" +#include "process_management/pebble_process_md.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/wakeup.h" + +#include + +typedef struct AppFetchUIArgs { + LaunchConfigCommon common; + WakeupInfo wakeup_info; + AppInstallId app_id; + bool forcefully; //! whether to launch forcefully or not +} AppFetchUIArgs; + +//! Used to launch the app_fetch_ui application +const PebbleProcessMd *app_fetch_ui_get_app_info(void); diff --git a/src/fw/apps/system/battery_critical.c b/src/fw/apps/system/battery_critical.c new file mode 100644 index 0000000000..7a66c1f8b1 --- /dev/null +++ b/src/fw/apps/system/battery_critical.c @@ -0,0 +1,81 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/graphics/graphics.h" +#include "applib/ui/window_private.h" +#include "applib/ui/app_window_stack.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "system/passert.h" + +typedef struct BatteryCriticalAppData { + Window window; + Layer layer; + GBitmap bitmap; +} BatteryCriticalAppData; + +static void update_proc(Layer* layer, GContext* ctx) { + BatteryCriticalAppData *app_data = app_state_get_user_data(); + + GRect low_battery_bounds = { + .origin = { + .x = (DISP_COLS - app_data->bitmap.bounds.size.w) / 2, + .y = (DISP_ROWS - app_data->bitmap.bounds.size.h), + }, + .size = app_data->bitmap.bounds.size, + }; + + graphics_draw_bitmap_in_rect(ctx, &app_data->bitmap, &low_battery_bounds); +} + +static void handle_init(void) { + BatteryCriticalAppData* data = app_malloc_check(sizeof(BatteryCriticalAppData)); + + gbitmap_init_with_resource(&data->bitmap, RESOURCE_ID_BATTERY_ICON_CHARGE); + + app_state_set_user_data(data); + + Window *window = &data->window; + + window_init(window, WINDOW_NAME("Battery Critical")); + window_set_overrides_back_button(window, true); + + layer_init(&data->layer, &window_get_root_layer(&data->window)->frame); + layer_set_update_proc(&data->layer, update_proc); + layer_add_child(window_get_root_layer(&data->window), &data->layer); + + const bool animated = false; + app_window_stack_push(window, animated); +} + +static void handle_deinit(void) { + BatteryCriticalAppData* app_data = app_state_get_user_data(); + gbitmap_deinit(&app_data->bitmap); + app_free(app_data); +} + +static void s_main(void) { + handle_init(); + + app_event_loop(); + + handle_deinit(); +} + +const PebbleProcessMd* battery_critical_get_app_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = s_main, + .visibility = ProcessVisibilityHidden, + // UUID: 4a71eb65-238d-4faa-b2a0-112aa910d7b4 + .uuid = {0x4a, 0x71, 0xeb, 0x65, 0x23, 0x8d, 0x4f, 0xaa, 0xb2, 0xa0, 0x11, 0x2a, 0xa9, 0x10, 0xd7, 0xb4}, + }, + .name = "Battery Critical", + .run_level = ProcessAppRunLevelCritical, + }; + return (const PebbleProcessMd*) &s_app_md; +} + diff --git a/src/fw/apps/system/battery_critical.h b/src/fw/apps/system/battery_critical.h new file mode 100644 index 0000000000..b03648e117 --- /dev/null +++ b/src/fw/apps/system/battery_critical.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* battery_critical_get_app_info(); + diff --git a/src/fw/apps/system/health/activity_detail_card.c b/src/fw/apps/system/health/activity_detail_card.c new file mode 100644 index 0000000000..b9658dfd91 --- /dev/null +++ b/src/fw/apps/system/health/activity_detail_card.c @@ -0,0 +1,143 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "activity_detail_card.h" +#include "detail_card.h" +#include "pbl/services/activity/health_util.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" + +#include + +typedef struct HealthActivityDetailCard { + int32_t daily_avg; + int32_t weekly_max; + + int16_t num_headings; + HealthDetailHeading headings[MAX_NUM_HEADINGS]; + + int16_t num_subtitles; + HealthDetailSubtitle subtitles[MAX_NUM_SUBTITLES]; + + int16_t num_zones; + HealthDetailZone zones[MAX_NUM_ZONES]; +} HealthActivityDetailCardData; + +static void prv_set_calories(char *buffer, size_t buffer_size, int32_t current_calories) { + if (current_calories == 0) { + strncpy(buffer, EN_DASH, buffer_size); + return; + } + + snprintf(buffer, buffer_size, "%"PRId32, current_calories); +} + +static void prv_set_distance(char *buffer, size_t buffer_size, int32_t current_distance_meters) { + if (current_distance_meters == 0) { + strncpy(buffer, EN_DASH, buffer_size); + return; + } + + const int conversion_factor = health_util_get_distance_factor(); + const char *units_string = health_util_get_distance_string(i18n_noop("mi"), i18n_noop("km")); + + char distance_buffer[HEALTH_WHOLE_AND_DECIMAL_LENGTH]; + health_util_format_whole_and_decimal(distance_buffer, HEALTH_WHOLE_AND_DECIMAL_LENGTH, + current_distance_meters, conversion_factor); + + snprintf(buffer, buffer_size, "%s%s", distance_buffer, units_string); +} + +static void prv_set_avg(char *buffer, size_t buffer_size, int32_t daily_avg, void *i18n_owner) { + int pos = 0; + + pos += snprintf(buffer, buffer_size, + PBL_IF_ROUND_ELSE("%s\n", "%s"), i18n_get("30 DAY AVG", i18n_owner)); + + if (daily_avg > 0) { + snprintf(buffer + pos, buffer_size - pos, " %"PRId32, daily_avg); + } else { + snprintf(buffer + pos, buffer_size - pos, " "EN_DASH); + } +} + +Window *health_activity_detail_card_create(HealthData *health_data) { + HealthActivityDetailCardData *card_data = app_zalloc_check(sizeof(HealthActivityDetailCardData)); + + card_data->daily_avg = health_data_steps_get_monthly_average(health_data); + + const GColor fill_color = PBL_IF_COLOR_ELSE(GColorIslamicGreen, GColorDarkGray); + const GColor today_fill_color = PBL_IF_COLOR_ELSE(GColorScreaminGreen, GColorDarkGray); + + health_detail_card_set_render_day_zones(card_data->zones, + &card_data->num_zones, + &card_data->weekly_max, + false /* format hours and minutes */, + true /* show crown */, + fill_color, + today_fill_color, + health_data_steps_get(health_data), + card_data); + + const size_t buffer_len = 32; + + HealthDetailHeading *heading = &card_data->headings[card_data->num_headings++]; + + *heading = (HealthDetailHeading) { + .primary_label = (char *)i18n_get("CALORIES", card_data), + .primary_value = app_zalloc_check(buffer_len), + .secondary_label = (char *)i18n_get("DISTANCE", card_data), + .secondary_value = app_zalloc_check(buffer_len), + .fill_color = GColorWhite, + .outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack), + }; + + prv_set_calories(heading->primary_value, buffer_len, + health_data_current_calories_get(health_data)); + + prv_set_distance(heading->secondary_value, buffer_len, + health_data_current_distance_meters_get(health_data)); + + HealthDetailSubtitle *subtitle = &card_data->subtitles[card_data->num_subtitles++]; + + *subtitle = (HealthDetailSubtitle) { + .label = app_zalloc_check(buffer_len), + .fill_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack), + }; + + prv_set_avg(subtitle->label, buffer_len, card_data->daily_avg, card_data); + + const HealthDetailCardConfig config = { + .num_headings = card_data->num_headings, + .headings = card_data->headings, + .num_subtitles = card_data->num_subtitles, + .subtitles = card_data->subtitles, + .daily_avg = card_data->daily_avg, + .weekly_max = card_data->weekly_max, + .bg_color = PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite), + .num_zones = card_data->num_zones, + .zones = card_data->zones, + .data = card_data, + }; + + return (Window *)health_detail_card_create(&config); +} + +void health_activity_detail_card_destroy(Window *window) { + HealthDetailCard *card = (HealthDetailCard *)window; + HealthActivityDetailCardData *card_data = card->data; + for (int i = 0; i < card_data->num_headings; i++) { + app_free(card_data->headings[i].primary_value); + app_free(card_data->headings[i].secondary_value); + } + for (int i = 0; i < card_data->num_subtitles; i++) { + app_free(card_data->subtitles[i].label); + } + for (int i = 0; i < card_data->num_zones; i++) { + app_free(card_data->zones[i].label); + } + i18n_free_all(card_data); + app_free(card_data); + health_detail_card_destroy(card); +} diff --git a/src/fw/apps/system/health/activity_detail_card.h b/src/fw/apps/system/health/activity_detail_card.h new file mode 100644 index 0000000000..afa36c2475 --- /dev/null +++ b/src/fw/apps/system/health/activity_detail_card.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + +//! Creates a health activity detail window +//! @param HealthData pointer to the health data to be given to this card +//! @return A pointer to a newly allocated health activity detail window +Window *health_activity_detail_card_create(HealthData *health_data); + +//! Destroys a health activity detail window +//! @param window Window pointer to health activity detail window +void health_activity_detail_card_destroy(Window *window); diff --git a/src/fw/apps/system/health/activity_summary_card.c b/src/fw/apps/system/health/activity_summary_card.c new file mode 100644 index 0000000000..bcfe2cdec0 --- /dev/null +++ b/src/fw/apps/system/health/activity_summary_card.c @@ -0,0 +1,197 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "activity_summary_card.h" +#include "activity_summary_card_segments.h" +#include "activity_detail_card.h" +#include "progress.h" +#include "ui.h" +#include "pbl/services/activity/health_util.h" + +#include "applib/pbl_std/pbl_std.h" +#include "applib/ui/kino/kino_reel.h" +#include "applib/ui/text_layer.h" +#include "board/display.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "system/logging.h" +#include "util/size.h" +#include "util/string.h" + +// Compile-time display offset calculations +#define HEALTH_X_OFFSET ((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) +#define HEALTH_Y_OFFSET ((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + +// Use larger font for taller displays +#if DISP_ROWS > LEGACY_2X_DISP_ROWS +#define HEALTH_STEPS_FONT FONT_KEY_LECO_32_BOLD_NUMBERS +#else +#define HEALTH_STEPS_FONT FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM +#endif + +typedef struct HealthActivitySummaryCardData { + HealthData *health_data; + HealthProgressBar progress_bar; + KinoReel *icon; + int32_t current_steps; + int32_t typical_steps; + int32_t daily_average_steps; +} HealthActivitySummaryCardData; + + +#define PROGRESS_CURRENT_COLOR (PBL_IF_COLOR_ELSE(GColorIslamicGreen, GColorDarkGray)) +#define PROGRESS_TYPICAL_COLOR (PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack)) +#define PROGRESS_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorDarkGray, GColorClear)) +#define PROGRESS_OUTLINE_COLOR (PBL_IF_COLOR_ELSE(GColorClear, GColorBlack)) + +#define CURRENT_TEXT_COLOR PROGRESS_CURRENT_COLOR +#define CARD_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)) + + +static void prv_render_progress_bar(GContext *ctx, Layer *base_layer) { + HealthActivitySummaryCardData *data = layer_get_data(base_layer); + + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_BACKGROUND_COLOR, + 0, HEALTH_PROGRESS_BAR_MAX_VALUE); + + const int32_t progress_max = MAX(data->current_steps, data->daily_average_steps); + if (!progress_max) { + health_progress_bar_outline(ctx, &data->progress_bar, PROGRESS_OUTLINE_COLOR); + return; + } + + const int current_fill = data->current_steps * HEALTH_PROGRESS_BAR_MAX_VALUE / progress_max; + const int typical_fill = data->typical_steps * HEALTH_PROGRESS_BAR_MAX_VALUE / progress_max; + +#if PBL_COLOR + const bool behind_typical = (data->current_steps < data->typical_steps); + if (behind_typical) { + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, 0, typical_fill); + } +#endif + + if (data->current_steps) { + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_CURRENT_COLOR, 0, current_fill); + } + +#if PBL_COLOR + if (!behind_typical) { + health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_fill); + } +#else + health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_fill); +#endif + + // This needs to be done after drawing the progress bars or else the progress fill + // overlaps the outline and things look weird + health_progress_bar_outline(ctx, &data->progress_bar, PROGRESS_OUTLINE_COLOR); +} + +static void prv_render_icon(GContext *ctx, Layer *base_layer) { + HealthActivitySummaryCardData *data = layer_get_data(base_layer); + + const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(43, 38), 43) + HEALTH_Y_OFFSET; + const int x_center_offset = PBL_IF_BW_ELSE(19, 18); + kino_reel_draw(data->icon, ctx, GPoint(base_layer->bounds.size.w / 2 - x_center_offset, y)); +} + +static void prv_render_current_steps(GContext *ctx, Layer *base_layer) { + HealthActivitySummaryCardData *data = layer_get_data(base_layer); + + char buffer[8]; + GFont font; + if (data->current_steps) { + font = fonts_get_system_font(HEALTH_STEPS_FONT); + snprintf(buffer, sizeof(buffer), "%"PRIu32"", data->current_steps); + } else { + font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD); + snprintf(buffer, sizeof(buffer), EM_DASH); + } + + const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(85, 83), 88) + HEALTH_Y_OFFSET; + graphics_context_set_text_color(ctx, CURRENT_TEXT_COLOR); + graphics_draw_text(ctx, buffer, font, + GRect(0, y, base_layer->bounds.size.w, 40), + GTextOverflowModeFill, GTextAlignmentCenter, NULL); +} + +static void prv_render_typical_steps(GContext *ctx, Layer *base_layer) { + HealthActivitySummaryCardData *data = layer_get_data(base_layer); + + char steps_buffer[12]; + if (data->typical_steps) { + snprintf(steps_buffer, sizeof(steps_buffer), "%"PRId32, data->typical_steps); + } else { + snprintf(steps_buffer, sizeof(steps_buffer), EM_DASH); + } + + health_ui_render_typical_text_box(ctx, base_layer, steps_buffer); +} + +static void prv_base_layer_update_proc(Layer *base_layer, GContext *ctx) { + HealthActivitySummaryCardData *data = layer_get_data(base_layer); + + data->current_steps = health_data_current_steps_get(data->health_data); + data->typical_steps = health_data_steps_get_current_average(data->health_data); + data->daily_average_steps = health_data_steps_get_cur_wday_average(data->health_data); + + prv_render_icon(ctx, base_layer); + + prv_render_progress_bar(ctx, base_layer); + + prv_render_current_steps(ctx, base_layer); + + prv_render_typical_steps(ctx, base_layer); +} + +static void prv_activity_detail_card_unload_callback(Window *window) { + health_activity_detail_card_destroy(window); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +Layer *health_activity_summary_card_create(HealthData *health_data) { + // create base layer + Layer *base_layer = layer_create_with_data(GRectZero, sizeof(HealthActivitySummaryCardData)); + HealthActivitySummaryCardData *health_activity_summary_card_data = layer_get_data(base_layer); + layer_set_update_proc(base_layer, prv_base_layer_update_proc); + // set health data + *health_activity_summary_card_data = (HealthActivitySummaryCardData) { + .health_data = health_data, + .icon = kino_reel_create_with_resource(RESOURCE_ID_HEALTH_APP_ACTIVITY), + .progress_bar = { + .num_segments = ARRAY_LENGTH(s_activity_summary_progress_segments), + .segments = s_activity_summary_progress_segments, + }, + }; + + return base_layer; +} + +void health_activity_summary_card_select_click_handler(Layer *layer) { + HealthActivitySummaryCardData *health_activity_summary_card_data = layer_get_data(layer); + HealthData *health_data = health_activity_summary_card_data->health_data; + Window *window = health_activity_detail_card_create(health_data); + window_set_window_handlers(window, &(WindowHandlers) { + .unload = prv_activity_detail_card_unload_callback, + }); + app_window_stack_push(window, true); +} + +void health_activity_summary_card_destroy(Layer *base_layer) { + HealthActivitySummaryCardData *data = layer_get_data(base_layer); + i18n_free_all(base_layer); + kino_reel_destroy(data->icon); + layer_destroy(base_layer); +} + +GColor health_activity_summary_card_get_bg_color(Layer *layer) { + return CARD_BACKGROUND_COLOR; +} + +bool health_activity_summary_show_select_indicator(Layer *layer) { + return true; +} diff --git a/src/fw/apps/system/health/activity_summary_card.h b/src/fw/apps/system/health/activity_summary_card.h new file mode 100644 index 0000000000..537b196315 --- /dev/null +++ b/src/fw/apps/system/health/activity_summary_card.h @@ -0,0 +1,32 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +//! Creates a special layer with data +//! @param health_data A pointer to the health data being given this card +//! @return A pointer to a newly allocated layer, which contains its own data +Layer *health_activity_summary_card_create(HealthData *health_data); + +//! Health activity summary select click handler +//! @param layer A pointer to an existing layer containing its own data +void health_activity_summary_card_select_click_handler(Layer *layer); + +//! Destroy a special layer +//! @param base_layer A pointer to an existing layer containing its own data +void health_activity_summary_card_destroy(Layer *base_layer); + +//! Health activity summary layer background color getter +GColor health_activity_summary_card_get_bg_color(Layer *layer); + +//! Health activity summary layer select click is available +bool health_activity_summary_show_select_indicator(Layer *layer); diff --git a/src/fw/apps/system/health/activity_summary_card_segments.h b/src/fw/apps/system/health/activity_summary_card_segments.h new file mode 100644 index 0000000000..d531522f77 --- /dev/null +++ b/src/fw/apps/system/health/activity_summary_card_segments.h @@ -0,0 +1,147 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "progress.h" +#include "board/display.h" + +//! 5 main segments + 2 real corners + 2 endcaps implemented as corners (for bw) +//! Each of the 5 non-corener segments get 20% of the total +#define AMOUNT_PER_SEGMENT (HEALTH_PROGRESS_BAR_MAX_VALUE * 20 / 100) + +// Found through trial and error +#define DEFAULT_MARK_WIDTH 50 + +// Dynamically center based on display size vs legacy 144x168 base +// Round displays need additional adjustment for the bezel +#define X_ADJ (((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) + PBL_IF_ROUND_ELSE(18, 0)) +#define Y_ADJ (((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + PBL_IF_ROUND_ELSE(6, 0)) + +#if PBL_BW +// The shape of the hexagon is slightly different on BW than on Color +static HealthProgressSegment s_activity_summary_progress_segments[] = { + { + // This is an endcap for BW (is a no-op on color) + .type = HealthProgressSegmentType_Corner, + .points = {{42, 85}, {51, 85}, {42, 85}, {51, 85}}, + }, + { + // Left side bottom + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{42, 84}, {51, 84}, {38, 58}, {28, 58}}, + }, + { + // Left side top + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{38, 57}, {28, 57}, {46, 26}, {56, 26}}, + }, + { + // Top left corner + .type = HealthProgressSegmentType_Corner, + .points = {{56, 26}, {46, 26}, {50, 18}, {56, 18}}, + }, + { + // Center top + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH * 2, + .points = {{55, 26}, {88, 26}, {89, 18}, {54, 18}}, + }, + { + // Top right corner + .type = HealthProgressSegmentType_Corner, + .points = {{88, 26}, {88, 18}, {92, 18}, {96, 26}}, + }, + { + // Right side top + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{87, 26}, {96, 26}, {113, 57}, {104, 57}}, + }, + { + // Right side bottom + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{104, 58}, {113, 58}, {99, 84}, {90, 84}}, + }, + { + // This is an endcap for BW (is a no-op on color) + .type = HealthProgressSegmentType_Corner, + .points = {{99, 85}, {90, 85}, {99, 85}, {90, 85}}, + }, +}; +#else // Color +static HealthProgressSegment s_activity_summary_progress_segments[] = { + { + // This is an endcap for BW (is a no-op on color) + .type = HealthProgressSegmentType_Corner, + .points = {{46 + X_ADJ, 81 + Y_ADJ}, {58 + X_ADJ, 81 + Y_ADJ}, + {46 + X_ADJ, 81 + Y_ADJ}, {58 + X_ADJ, 81 + Y_ADJ}}, + }, + { + // Left side bottom + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{46 + X_ADJ, 81 + Y_ADJ}, {58 + X_ADJ, 81 + Y_ADJ}, + {41 + X_ADJ, 51 + Y_ADJ}, {29 + X_ADJ, 51 + Y_ADJ}}, + }, + { + // Left side top + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{29 + X_ADJ, 51 + Y_ADJ}, {41 + X_ADJ, 51 + Y_ADJ}, + {57 + X_ADJ, 24 + Y_ADJ}, {45 + X_ADJ, 24 + Y_ADJ}}, + }, + { + // Top left corner + .type = HealthProgressSegmentType_Corner, + .points = {{57 + X_ADJ, 24 + Y_ADJ}, {45 + X_ADJ, 24 + Y_ADJ}, + {51 + X_ADJ, 15 + Y_ADJ}, {57 + X_ADJ, 15 + Y_ADJ}}, + }, + { + // Center top + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH * 2, + .points = {{55 + X_ADJ, 24 + Y_ADJ}, {89 + X_ADJ, 24 + Y_ADJ}, + {89 + X_ADJ, 15 + Y_ADJ}, {55 + X_ADJ, 15 + Y_ADJ}}, + }, + { + // Top right corner + .type = HealthProgressSegmentType_Corner, + .points = {{87 + X_ADJ, 24 + Y_ADJ}, {87 + X_ADJ, 15 + Y_ADJ}, + {93 + X_ADJ, 15 + Y_ADJ}, {99 + X_ADJ, 24 + Y_ADJ}}, + }, + { + // Right side top + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{87 + X_ADJ, 24 + Y_ADJ}, {99 + X_ADJ, 24 + Y_ADJ}, + {115 + X_ADJ, 51 + Y_ADJ}, {103 + X_ADJ, 51 + Y_ADJ}}, + }, + { + // Right side bottom + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{103 + X_ADJ, 51 + Y_ADJ}, {115 + X_ADJ, 51 + Y_ADJ}, + {98 + X_ADJ, 81 + Y_ADJ}, {86 + X_ADJ, 81 + Y_ADJ}}, + }, + { + // This is an endcap for BW (is a no-op on color) + .type = HealthProgressSegmentType_Corner, + .points = {{98 + X_ADJ, 81 + Y_ADJ}, {86 + X_ADJ, 81 + Y_ADJ}, + {98 + X_ADJ, 81 + Y_ADJ}, {86 + X_ADJ, 81 + Y_ADJ}}, + }, +}; +#endif diff --git a/src/fw/apps/system/health/card_view.c b/src/fw/apps/system/health/card_view.c new file mode 100644 index 0000000000..02b9ebe4ac --- /dev/null +++ b/src/fw/apps/system/health/card_view.c @@ -0,0 +1,385 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "card_view.h" + +#include "health.h" +#include "activity_summary_card.h" +#include "sleep_summary_card.h" +#include "hr_summary_card.h" + +#include "applib/app_launch_reason.h" +#include "applib/ui/action_button.h" +#include "applib/ui/content_indicator.h" +#include "applib/ui/content_indicator_private.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/timeline/health_layout.h" +#include "system/logging.h" +#include "util/time/time.h" + +#define BACK_TO_WATCHFACE (-1) + +// Enum for different card types +typedef enum { + Card_ActivitySummary, +#ifdef CONFIG_HRM + Card_HrSummary, +#endif + Card_SleepSummary, + CardCount +} Card; + +// Main structure for card view +typedef struct HealthCardView { + Window window; + HealthData *health_data; + Card current_card_index; + Layer *card_layers[CardCount]; + Animation *slide_animation; + Layer select_indicator_layer; + Layer down_arrow_layer; + Layer up_arrow_layer; + ContentIndicator down_indicator; + ContentIndicator up_indicator; + GColor select_indicator_color; +} HealthCardView; + +static Layer* (*s_card_view_create[CardCount])(HealthData *health_data) = { + [Card_ActivitySummary] = health_activity_summary_card_create, +#ifdef CONFIG_HRM + [Card_HrSummary] = health_hr_summary_card_create, +#endif + [Card_SleepSummary] = health_sleep_summary_card_create, +}; + +static void (*s_card_view_select_click_handler[CardCount])(Layer *layer) = { + [Card_ActivitySummary] = health_activity_summary_card_select_click_handler, +#ifdef CONFIG_HRM + [Card_HrSummary] = health_hr_summary_card_select_click_handler, +#endif + [Card_SleepSummary] = health_sleep_summary_card_select_click_handler, +}; + +static GColor (*s_card_view_get_bg_color[CardCount])(Layer *layer) = { + [Card_ActivitySummary] = health_activity_summary_card_get_bg_color, +#ifdef CONFIG_HRM + [Card_HrSummary] = health_hr_summary_card_get_bg_color, +#endif + [Card_SleepSummary] = health_sleep_summary_card_get_bg_color, +}; + +static bool (*s_card_view_show_select_indicator[CardCount])(Layer *layer) = { + [Card_ActivitySummary] = health_activity_summary_show_select_indicator, +#ifdef CONFIG_HRM + [Card_HrSummary] = health_hr_summary_show_select_indicator, +#endif + [Card_SleepSummary] = health_sleep_summary_show_select_indicator, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Private Functions +// + +static int prv_get_next_card_idx(Card current, bool up) { + const int direction = up ? 1 : -1; + int next = current + direction; + +// Skip over the HR card if we don't support it +#ifdef CONFIG_HRM + if (next == Card_HrSummary && !activity_is_hrm_present()) { + next = next + direction; + } + // if heart rate is diabled, change the order of cards to Activiy <-> Sleep <-> HR + else if (activity_is_hrm_present() && !activity_prefs_heart_rate_is_enabled()) { + if (current == Card_ActivitySummary) { + next = up ? Card_SleepSummary : BACK_TO_WATCHFACE; + } else if (current == Card_SleepSummary) { + next = up ? Card_HrSummary : Card_ActivitySummary; + } else if (current == Card_HrSummary) { + next = up ? CardCount : Card_SleepSummary; + } + } +#endif + + return next; +} + +static void prv_select_indicator_layer_update_proc(Layer *layer, GContext *ctx) { + HealthCardView *health_card_view = window_get_user_data(layer_get_window(layer)); + action_button_draw(ctx, layer, health_card_view->select_indicator_color); +} + +static void prv_refresh_select_indicator(HealthCardView *health_card_view) { + Layer *card_layer = health_card_view->card_layers[health_card_view->current_card_index]; + + const bool is_hidden = !s_card_view_show_select_indicator[health_card_view->current_card_index]( + card_layer); + + GColor card_bg_color = s_card_view_get_bg_color[health_card_view->current_card_index](card_layer); + health_card_view->select_indicator_color = gcolor_legible_over(card_bg_color); + + layer_set_hidden(&health_card_view->select_indicator_layer, is_hidden); +} + +static void prv_content_indicator_setup_direction(HealthCardView *health_card_view, + ContentIndicator *content_indicator, + Layer *indicator_layer, + ContentIndicatorDirection direction) { + Layer *card_layer = health_card_view->card_layers[health_card_view->current_card_index]; + + GColor card_bg_color = s_card_view_get_bg_color[health_card_view->current_card_index](card_layer); + + content_indicator_configure_direction(content_indicator, direction, &(ContentIndicatorConfig) { + .layer = indicator_layer, + .colors.foreground = gcolor_legible_over(card_bg_color), + .colors.background = card_bg_color, + }); +} + +static void prv_refresh_content_indicators(HealthCardView *health_card_view) { + prv_content_indicator_setup_direction(health_card_view, + &health_card_view->up_indicator, + &health_card_view->up_arrow_layer, + ContentIndicatorDirectionUp); + prv_content_indicator_setup_direction(health_card_view, + &health_card_view->down_indicator, + &health_card_view->down_arrow_layer, + ContentIndicatorDirectionDown); + + bool is_up_visible = true; + if (prv_get_next_card_idx(health_card_view->current_card_index, true) >= CardCount) { + is_up_visible = false; + } + + content_indicator_set_content_available(&health_card_view->up_indicator, + ContentIndicatorDirectionUp, + is_up_visible); + + // Down is always visible (the watchface is always an option) + content_indicator_set_content_available(&health_card_view->down_indicator, + ContentIndicatorDirectionDown, + true); +} + +static void prv_hide_content_indicators(HealthCardView *health_card_view) { + content_indicator_set_content_available(&health_card_view->up_indicator, + ContentIndicatorDirectionUp, + false); + content_indicator_set_content_available(&health_card_view->down_indicator, + ContentIndicatorDirectionDown, + false); +} + +static void prv_set_window_background_color(HealthCardView *health_card_view) { + Layer *card_layer = health_card_view->card_layers[health_card_view->current_card_index]; + window_set_background_color(&health_card_view->window, + s_card_view_get_bg_color[health_card_view->current_card_index](card_layer)); +} + +#define NUM_MID_FRAMES 1 + +static void prv_bg_animation_update(Animation *animation, AnimationProgress normalized) { + HealthCardView *health_card_view = animation_get_context(animation); + const AnimationProgress bounce_back_length = + (interpolate_moook_out_duration() * ANIMATION_NORMALIZED_MAX) / + interpolate_moook_soft_duration(NUM_MID_FRAMES); + if (normalized >= ANIMATION_NORMALIZED_MAX - bounce_back_length) { + prv_set_window_background_color(health_card_view); + } +} + +static void prv_bg_animation_started_handler(Animation *animation, void *context) { + HealthCardView *health_card_view = context; + + layer_set_hidden(health_card_view->card_layers[health_card_view->current_card_index], false); + + layer_set_hidden(&health_card_view->select_indicator_layer, true); + + prv_hide_content_indicators(health_card_view); +} + +static void prv_bg_animation_stopped_handler(Animation *animation, bool finished, void *context) { + HealthCardView *health_card_view = context; + + for (int i = 0; i < CardCount; i++) { + if (i != health_card_view->current_card_index) { + layer_set_hidden(health_card_view->card_layers[i], true); + } + } + + if (!finished) { + prv_set_window_background_color(health_card_view); + } else { + prv_refresh_select_indicator(health_card_view); + prv_refresh_content_indicators(health_card_view); + } +} + +static const AnimationImplementation prv_bg_animation_implementation = { + .update = &prv_bg_animation_update +}; + +static int64_t prv_interpolate_moook_soft(int32_t normalized, int64_t from, int64_t to) { + return interpolate_moook_soft(normalized, from, to, NUM_MID_FRAMES); +} + +static Animation* prv_create_slide_animation(Layer *layer, GRect *from_frame, GRect *to_frame) { + Animation *anim = (Animation *)property_animation_create_layer_frame(layer, from_frame, to_frame); + animation_set_duration(anim, interpolate_moook_soft_duration(NUM_MID_FRAMES)); + animation_set_custom_interpolation(anim, prv_interpolate_moook_soft); + return anim; +} + +// Create animation +static void prv_schedule_slide_animation(HealthCardView *health_card_view, + Card next_card_index, bool slide_up) { + animation_unschedule(health_card_view->slide_animation); + health_card_view->slide_animation = NULL; + + GRect window_bounds = window_get_root_layer(&health_card_view->window)->bounds; + + Layer *current_card_layer = health_card_view->card_layers[health_card_view->current_card_index]; + Layer *next_card_layer = health_card_view->card_layers[next_card_index]; + + GRect curr_stop = window_bounds; + curr_stop.origin.y = slide_up ? window_bounds.size.h : -window_bounds.size.h; + GRect next_start = window_bounds; + next_start.origin.y = slide_up ? -window_bounds.size.h : window_bounds.size.h; + + Animation *curr_out = prv_create_slide_animation(current_card_layer, &window_bounds, &curr_stop); + Animation *next_in = prv_create_slide_animation(next_card_layer, &next_start, &window_bounds); + + Animation *bg_anim = animation_create(); + animation_set_duration(bg_anim, interpolate_moook_soft_duration(NUM_MID_FRAMES)); + animation_set_handlers(bg_anim, (AnimationHandlers){ + .started = prv_bg_animation_started_handler, + .stopped = prv_bg_animation_stopped_handler, + }, health_card_view); + animation_set_implementation(bg_anim, &prv_bg_animation_implementation); + + health_card_view->slide_animation = animation_spawn_create(curr_out, next_in, bg_anim, NULL); + animation_schedule(health_card_view->slide_animation); + + health_card_view->current_card_index = next_card_index; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Callback Functions +// + +// Up/Down click handler +static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { + HealthCardView *health_card_view = context; + const bool slide_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + const int next_card_index = prv_get_next_card_idx(health_card_view->current_card_index, slide_up); + + if (next_card_index < 0) { + // Exit + app_window_stack_pop_all(true); + } else if (next_card_index >= CardCount) { + // Top of the list (no-op) + // TODO: maybe we want an animation? + return; + } else { + // animate the cards' positions + prv_schedule_slide_animation(health_card_view, next_card_index, slide_up); + } +} + +// Select click handler +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + HealthCardView *health_card_view = context; + Layer *layer = health_card_view->card_layers[health_card_view->current_card_index]; + s_card_view_select_click_handler[health_card_view->current_card_index](layer); + health_card_view_mark_dirty(health_card_view); +} + +// Click config provider +static void prv_click_config_provider(void *context) { + window_set_click_context(BUTTON_ID_UP, context); + window_set_click_context(BUTTON_ID_SELECT, context); + window_set_click_context(BUTTON_ID_DOWN, context); + window_single_click_subscribe(BUTTON_ID_UP, prv_up_down_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_up_down_click_handler); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +HealthCardView *health_card_view_create(HealthData *health_data) { + HealthCardView *health_card_view = app_malloc_check(sizeof(HealthCardView)); + *health_card_view = (HealthCardView) { + .health_data = health_data, + }; + window_init(&health_card_view->window, "Health Card View"); + window_set_user_data(&health_card_view->window, health_card_view); + window_set_click_config_provider_with_context(&health_card_view->window, + prv_click_config_provider, health_card_view); + Layer *window_root = window_get_root_layer(&health_card_view->window); + + // create and add all card layers to window root layer + for (int i = 0; i < CardCount; i++) { + health_card_view->card_layers[i] = s_card_view_create[i](health_data); + layer_add_child(window_root, health_card_view->card_layers[i]); + } + + // set starting card based on launch args + HealthLaunchArgs launch_args = { .args = app_launch_get_args() }; + + health_card_view->current_card_index = + (launch_args.card_type == HealthCardType_Sleep) ? Card_SleepSummary : Card_ActivitySummary; + + // set window background + prv_set_window_background_color(health_card_view); + + // position current card + layer_set_frame(health_card_view->card_layers[health_card_view->current_card_index], + &window_root->frame); + + // setup select indicator + layer_init(&health_card_view->select_indicator_layer, &window_root->frame); + layer_add_child(window_root, &health_card_view->select_indicator_layer); + layer_set_update_proc(&health_card_view->select_indicator_layer, + prv_select_indicator_layer_update_proc); + + // setup content indicators + const int content_indicator_height = PBL_IF_ROUND_ELSE(18, 11); + const GRect down_arrow_layer_frame = grect_inset(window_root->frame, + GEdgeInsets(window_root->frame.size.h - content_indicator_height, 0, 0)); + layer_init(&health_card_view->down_arrow_layer, &down_arrow_layer_frame); + layer_add_child(window_root, &health_card_view->down_arrow_layer); + content_indicator_init(&health_card_view->down_indicator); + + const GRect up_arrow_layer_frame = grect_inset(window_root->frame, + GEdgeInsets(0, 0, window_root->frame.size.h - content_indicator_height)); + layer_init(&health_card_view->up_arrow_layer, &up_arrow_layer_frame); + layer_add_child(window_root, &health_card_view->up_arrow_layer); + content_indicator_init(&health_card_view->up_indicator); + + prv_refresh_select_indicator(health_card_view); + prv_refresh_content_indicators(health_card_view); + + return health_card_view; +} + +void health_card_view_destroy(HealthCardView *health_card_view) { + // destroy cards + health_activity_summary_card_destroy(health_card_view->card_layers[Card_ActivitySummary]); + health_sleep_summary_card_destroy(health_card_view->card_layers[Card_SleepSummary]); + // destroy self + window_deinit(&health_card_view->window); + app_free(health_card_view); +} + +void health_card_view_push(HealthCardView *health_card_view) { + app_window_stack_push(&health_card_view->window, true); +} + +void health_card_view_mark_dirty(HealthCardView *health_card_view) { + layer_mark_dirty(health_card_view->card_layers[health_card_view->current_card_index]); +} diff --git a/src/fw/apps/system/health/card_view.h b/src/fw/apps/system/health/card_view.h new file mode 100644 index 0000000000..26444d7e44 --- /dev/null +++ b/src/fw/apps/system/health/card_view.h @@ -0,0 +1,33 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + +//! Main structure for card view +typedef struct HealthCardView HealthCardView; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +//! Creates a HealthCardView +//! @param health_data A pointer to the health data being given this view +//! @return A pointer to the newly allocated HealthCardView +HealthCardView *health_card_view_create(HealthData *health_data); + +//! Destroy a HealthCardView +//! @param health_card_view A pointer to an existing HealthCardView +void health_card_view_destroy(HealthCardView *health_card_view); + +//! Push a HealthCardView to the window stack +//! @param health_card_view A pointer to an existing HealthCardView +void health_card_view_push(HealthCardView *health_card_view); + +//! Mark the card view as dirty so it is refreshed +//! @param health_card_view A pointer to an existing HealthCardView +void health_card_view_mark_dirty(HealthCardView *health_card_view); diff --git a/src/fw/apps/system/health/data.c b/src/fw/apps/system/health/data.c new file mode 100644 index 0000000000..eb6cd4bbfa --- /dev/null +++ b/src/fw/apps/system/health/data.c @@ -0,0 +1,314 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "data.h" +#include "data_private.h" + +#include "applib/app_logging.h" +#include "applib/health_service_private.h" +#include "drivers/rtc.h" +#include "kernel/pbl_malloc.h" +#include "syscall/syscall.h" +#include "system/logging.h" +#include "util/math.h" +#include "util/stats.h" +#include "util/time/time.h" + + +T_STATIC void prv_merge_adjacent_sessions(ActivitySession *current, + ActivitySession *previous) { + if (previous == NULL || current == NULL) { + return; + } + + if (current->type != previous->type || + (current->type != ActivitySessionType_RestfulNap && + current->type != ActivitySessionType_RestfulSleep)) { + // We only merge sessions if they are "deep" sleep/nap + return; + } + + // [FBO]: note that this only works because sleep sessions are + // all we care about and they are sorted. Don't try to extend this to walk + // or run sessions + + const uint16_t max_apart_merge_secs = 5 * SECONDS_PER_MINUTE; + time_t end_time = previous->start_utc + previous->length_min * SECONDS_PER_MINUTE; + if ((end_time + max_apart_merge_secs) > current->start_utc) { + current->length_min += previous->length_min + + (current->start_utc - end_time) / SECONDS_PER_MINUTE; + current->start_utc = previous->start_utc; + previous->length_min = 0; + previous->type = ActivitySessionType_None; + } +} + +// API Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +HealthData *health_data_create(void) { + return (HealthData *)app_zalloc_check(sizeof(HealthData)); +} + +void health_data_destroy(HealthData *health_data) { + app_free(health_data); +} + +void health_data_update_quick(HealthData *health_data) { + const time_t now = rtc_get_time(); + struct tm local_tm; + localtime_r(&now, &local_tm); + + // Get the current steps + health_service_private_get_metric_history(HealthMetricStepCount, 1, health_data->step_data); + + // Get the typical step averages for every 15 minutes + activity_get_step_averages(local_tm.tm_wday, &health_data->step_averages); + + health_data->current_hr_bpm = health_service_peek_current_value(HealthMetricHeartRateBPM); + // Get the most recent stable HR Reading timestamp. + activity_get_metric(ActivityMetricHeartRateFilteredUpdatedTimeUTC, 1, + (int32_t *)&health_data->hr_last_updated); +} + +void health_data_update(HealthData *health_data) { + const time_t now = rtc_get_time(); + struct tm local_tm; + localtime_r(&now, &local_tm); + + + //! Step / activity related data + // Get the step totals for today and the past 6 days + health_service_private_get_metric_history(HealthMetricStepCount, DAYS_PER_WEEK, + health_data->step_data); + // Update distance / calories now that we have our steps + health_data_update_step_derived_metrics(health_data); + + // Get the step averages for each 15 minute window. Used for typical steps + activity_get_step_averages(local_tm.tm_wday, &health_data->step_averages); + + // Get the average steps for the past month + activity_get_metric_monthly_avg(ActivityMetricStepCount, &health_data->monthly_step_average); + + + //! Sleep related data + health_service_private_get_metric_history(HealthMetricSleepSeconds, DAYS_PER_WEEK, + health_data->sleep_data); + // Check if we have sleep data for today. If not, we want to show the last sleep session + // (yesterday's data) + bool use_yesterday = (health_data->sleep_data[0] == 0 && health_data->sleep_data[1] > 0); + int day_offset = use_yesterday ? 1 : 0; + int wday = (local_tm.tm_wday - day_offset + 7) % 7; + + activity_get_metric_typical(ActivityMetricSleepTotalSeconds, wday, + &health_data->typical_sleep); + + int32_t deep_sleep_history[2]; + activity_get_metric(ActivityMetricSleepRestfulSeconds, 2, deep_sleep_history); + health_data->deep_sleep = deep_sleep_history[day_offset]; + + int32_t sleep_start_history[2]; + activity_get_metric(ActivityMetricSleepEnterAtSeconds, 2, sleep_start_history); + health_data->sleep_start = sleep_start_history[day_offset]; + + int32_t sleep_end_history[2]; + activity_get_metric(ActivityMetricSleepExitAtSeconds, 2, sleep_end_history); + health_data->sleep_end = sleep_end_history[day_offset]; + + activity_get_metric_typical(ActivityMetricSleepEnterAtSeconds, wday, + &health_data->typical_sleep_start); + activity_get_metric_typical(ActivityMetricSleepExitAtSeconds, wday, + &health_data->typical_sleep_end); + activity_get_metric_monthly_avg(ActivityMetricSleepTotalSeconds, + &health_data->monthly_sleep_average); + + + //! Activity sessions + health_data->num_activity_sessions = ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT; + if (!activity_get_sessions(&health_data->num_activity_sessions, + health_data->activity_sessions)) { + PBL_LOG_ERR("Fetching activity sessions failed"); + } else { + ActivitySession *previous_session = NULL; + for (unsigned int i = 0; i < health_data->num_activity_sessions; i++) { + ActivitySession *session = &health_data->activity_sessions[i]; + prv_merge_adjacent_sessions(session, previous_session); + previous_session = session; + } + } + + //! HR related data + health_data_update_current_bpm(health_data); + health_data_update_hr_zone_minutes(health_data); +} + +void health_data_update_step_derived_metrics(HealthData *health_data) { + // get distance in meters + health_data->current_distance_meters = health_service_sum_today(HealthMetricWalkedDistanceMeters); + + // get calories + health_data->current_calories = health_service_sum_today(HealthMetricActiveKCalories) + + health_service_sum_today(HealthMetricRestingKCalories); +} + +void health_data_update_steps(HealthData *health_data, uint32_t new_steps) { + health_data->step_data[0] = new_steps; + health_data_update_step_derived_metrics(health_data); +} + +void health_data_update_sleep(HealthData *health_data, uint32_t new_sleep, + uint32_t new_deep_sleep) { + health_data->sleep_data[0] = new_sleep; + health_data->deep_sleep = new_deep_sleep; +} + +void health_data_update_current_bpm(HealthData *health_data) { + health_data->resting_hr_bpm = activity_prefs_heart_get_resting_hr(); + + // Check the quality. If it doesn't meet our standards, bail + int32_t quality; + activity_get_metric(ActivityMetricHeartRateRawQuality, 1, &quality); + if (quality < HRMQuality_Acceptable) { + return; + } + + uint32_t current_hr_timestamp; + activity_get_metric(ActivityMetricHeartRateRawUpdatedTimeUTC, 1, + (int32_t *)¤t_hr_timestamp); + if (current_hr_timestamp > (uint32_t)health_data->hr_last_updated) { + health_data->current_hr_bpm = health_service_peek_current_value(HealthMetricHeartRateRawBPM); + health_data->hr_last_updated = current_hr_timestamp; + } +} + +void health_data_update_hr_zone_minutes(HealthData *health_data) { + activity_get_metric(ActivityMetricHeartRateZone1Minutes, 1, &health_data->hr_zone1_minutes); + activity_get_metric(ActivityMetricHeartRateZone2Minutes, 1, &health_data->hr_zone2_minutes); + activity_get_metric(ActivityMetricHeartRateZone3Minutes, 1, &health_data->hr_zone3_minutes); +} + +int32_t *health_data_steps_get(HealthData *health_data) { + return health_data->step_data; +} + +int32_t health_data_current_steps_get(HealthData *health_data) { + return health_data->step_data[0]; +} + +int32_t health_data_current_distance_meters_get(HealthData *health_data) { + return health_data->current_distance_meters; +} + +int32_t health_data_current_calories_get(HealthData *health_data) { + return health_data->current_calories; +} + +static int32_t prv_health_data_get_n_average_chunks(HealthData *health_data, int number_of_chunks) { + uint32_t total_steps_avg = 0; + for (int i = 0; (i < ACTIVITY_NUM_METRIC_AVERAGES) && (i < number_of_chunks); i++) { + if (health_data->step_averages.average[i] != ACTIVITY_METRIC_AVERAGES_UNKNOWN) { + total_steps_avg += health_data->step_averages.average[i]; + } + } + return total_steps_avg; +} + +int32_t health_data_steps_get_current_average(HealthData *health_data) { + // get the current minutes into today + time_t utc_sec = rtc_get_time(); + struct tm local_tm; + localtime_r(&utc_sec, &local_tm); + int32_t today_min = local_tm.tm_hour * MINUTES_PER_HOUR + local_tm.tm_min; + const int k_minutes_per_step_avg = MINUTES_PER_DAY / ACTIVITY_NUM_METRIC_AVERAGES; + + // each average chunk is 15 mins long + if (health_data->step_average_last_updated_time != + ((today_min / k_minutes_per_step_avg) * k_minutes_per_step_avg)) { + // current_step_average is stale + health_data->current_step_average = + prv_health_data_get_n_average_chunks(health_data, today_min / k_minutes_per_step_avg); + health_data->step_average_last_updated_time = + (today_min / k_minutes_per_step_avg) * k_minutes_per_step_avg; + } + return health_data->current_step_average; +} + +int32_t health_data_steps_get_cur_wday_average(HealthData *health_data) { + return prv_health_data_get_n_average_chunks(health_data, ACTIVITY_NUM_METRIC_AVERAGES); +} + +int32_t health_data_steps_get_monthly_average(HealthData *health_data) { + return health_data->monthly_step_average; +} + + +int32_t *health_data_sleep_get(HealthData *health_data) { + return health_data->sleep_data; +} + +int32_t health_data_current_sleep_get(HealthData *health_data) { + if (health_data->sleep_data[0] == 0 && health_data->sleep_data[1] > 0) { + return health_data->sleep_data[1]; + } + return health_data->sleep_data[0]; +} + +int32_t health_data_sleep_get_cur_wday_average(HealthData *health_data) { + return health_data->typical_sleep; +} + +int32_t health_data_current_deep_sleep_get(HealthData *health_data) { + return health_data->deep_sleep; +} + +int32_t health_data_sleep_get_monthly_average(HealthData *health_data) { + return health_data->monthly_sleep_average; +} + +int32_t health_data_sleep_get_start_time(HealthData *health_data) { + return health_data->sleep_start; +} + +int32_t health_data_sleep_get_end_time(HealthData *health_data) { + return health_data->sleep_end; +} + +int32_t health_data_sleep_get_typical_start_time(HealthData *health_data) { + return health_data->typical_sleep_start; +} + +int32_t health_data_sleep_get_typical_end_time(HealthData *health_data) { + return health_data->typical_sleep_end; +} + +int32_t health_data_sleep_get_num_sessions(HealthData *health_data) { + return health_data->num_activity_sessions; +} + +ActivitySession *health_data_sleep_get_sessions(HealthData *health_data) { + return health_data->activity_sessions; +} + +uint32_t health_data_hr_get_current_bpm(HealthData *health_data) { + return health_data->current_hr_bpm; +} + +uint32_t health_data_hr_get_resting_bpm(HealthData *health_data) { + return health_data->resting_hr_bpm; +} + +time_t health_data_hr_get_last_updated_timestamp(HealthData *health_data) { + return health_data->hr_last_updated; +} + +int32_t health_data_hr_get_zone1_minutes(HealthData *health_data) { + return health_data->hr_zone1_minutes; +} + +int32_t health_data_hr_get_zone2_minutes(HealthData *health_data) { + return health_data->hr_zone2_minutes; +} + +int32_t health_data_hr_get_zone3_minutes(HealthData *health_data) { + return health_data->hr_zone3_minutes; +} diff --git a/src/fw/apps/system/health/data.h b/src/fw/apps/system/health/data.h new file mode 100644 index 0000000000..3bbf91ed8a --- /dev/null +++ b/src/fw/apps/system/health/data.h @@ -0,0 +1,160 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/activity/activity.h" + +typedef struct { + int32_t sum; + int32_t avg; + int32_t max; +} BasicHealthStats; + +typedef struct { + BasicHealthStats weekday; + BasicHealthStats weekend; + BasicHealthStats daily; +} WeeklyStats; + +//! Health data model +typedef struct HealthData HealthData; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +//! Create a health data structure +//! @return A pointer to the new HealthData structure +HealthData *health_data_create(void); + +//! Destroy a health data structure +//! @param health_data A pointer to an existing health data structure +void health_data_destroy(HealthData *health_data); + +//! Fetch the current activity data from the system +//! @param health_data A pointer to the health data to use +void health_data_update(HealthData *health_data); + +//! Fetch only the data required to display the initial card. +//! This helps reduce lag when opening the app +//! @param health_data A pointer to the health data to use +void health_data_update_quick(HealthData *health_data); + +//! Fetch the current data for step derived metrics (distance, active time, calories) +//! @param health_data A pointer to the health data to use +void health_data_update_step_derived_metrics(HealthData *health_data); + +//! Update the number of steps the user has taken today +//! @param health_data A pointer to the health data to use +//! @param new_steps the new value of the steps for toaday +void health_data_update_steps(HealthData *health_data, uint32_t new_steps); + +//! Update the number of seconds the user has slept today +//! @param health_data A pointer to the health data to use +//! @param new_sleep the new value of the seconds of sleep today +//! @param new_deep_sleep the new value of the seconds of deep sleep today +void health_data_update_sleep(HealthData *health_data, uint32_t new_sleep, uint32_t new_deep_sleep); + +//! Update the current HR BPM +//! @param health_data A pointer to the health data to use +void health_data_update_current_bpm(HealthData *health_data); + +//! Update the time in HR zones +//! @param health_data A pointer to the health data to use +void health_data_update_hr_zone_minutes(HealthData *health_data); + + +//! Get the current step count +//! @param health_data A pointer to the health data to use +//! @return the current step count +int32_t health_data_current_steps_get(HealthData *health_data); + +//! Get the historical step data +//! @param health_data A pointer to the health data to use +//! @return A pointer to historical step data +int32_t *health_data_steps_get(HealthData *health_data); + +//! Get the current distance traveled in meters +//! @param health_data A pointer to the health data to use +//! @return the current distance travelled in meters +int32_t health_data_current_distance_meters_get(HealthData *health_data); + +//! Get the current calories +//! @param health_data A pointer to the health data to use +//! @return the current calories +int32_t health_data_current_calories_get(HealthData *health_data); + +//! Get current number of steps that should be taken by this time today +//! @param health_data A pointer to the health data to use +//! @return The integer number of steps that should be taken by this time today +int32_t health_data_steps_get_current_average(HealthData *health_data); + +//! Get the step average for the current day of the week +//! @param health_data A pointer to the health data to use +//! @return An integer value for the number of steps that are typically taken on this week day +int32_t health_data_steps_get_cur_wday_average(HealthData *health_data); + +//! Get the step average over the past month +//! @param health_data A pointer to the health data to use +//! @return An integer value for the average number of steps that we taken over the past month +int32_t health_data_steps_get_monthly_average(HealthData *health_data); + +//! Get the historical sleep data +//! @param health_data A pointer to the health data to use +//! @return A pointer to historical sleep data +int32_t *health_data_sleep_get(HealthData *health_data); + +//! Get the current sleep length +//! @param health_data A pointer to the health data to use +//! @return the current sleep length +int32_t health_data_current_sleep_get(HealthData *health_data); + +//! Gets the typical sleep duration for the current weekday +int32_t health_data_sleep_get_cur_wday_average(HealthData *health_data); + +//! Get the current deep sleep data +//! @param health_data A pointer to the health data to use +//! @return the current deep sleep length +int32_t health_data_current_deep_sleep_get(HealthData *health_data); + +//! Get the sleep average over the past month +//! @param health_data A pointer to the health data to use +//! @return The average daily sleep over the past month +int32_t health_data_sleep_get_monthly_average(HealthData *health_data); + +// Get the sleep start time +int32_t health_data_sleep_get_start_time(HealthData *health_data); + +// Get the sleep end time +int32_t health_data_sleep_get_end_time(HealthData *health_data); + +// Get the typical sleep start time +int32_t health_data_sleep_get_typical_start_time(HealthData *health_data); + +// Get the typical sleep end time +int32_t health_data_sleep_get_typical_end_time(HealthData *health_data); + +// Get the number of sleep sessions +int32_t health_data_sleep_get_num_sessions(HealthData *health_data); + +// Get today's sleep sessions +ActivitySession *health_data_sleep_get_sessions(HealthData *health_data); + +// Get current BPM +uint32_t health_data_hr_get_current_bpm(HealthData *health_data); + +// Get resting BPM +uint32_t health_data_hr_get_resting_bpm(HealthData *health_data); + +// Get HR last updated timestamp +time_t health_data_hr_get_last_updated_timestamp(HealthData *health_data); + +// Get number of minutes in Zone 1 +int32_t health_data_hr_get_zone1_minutes(HealthData *health_data); + +// Get number of minutes in Zone 2 +int32_t health_data_hr_get_zone2_minutes(HealthData *health_data); + +// Get number of minutes in Zone 3 +int32_t health_data_hr_get_zone3_minutes(HealthData *health_data); diff --git a/src/fw/apps/system/health/data_private.h b/src/fw/apps/system/health/data_private.h new file mode 100644 index 0000000000..ab79db50fa --- /dev/null +++ b/src/fw/apps/system/health/data_private.h @@ -0,0 +1,42 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +typedef struct HealthData { + //!< Current step / activity info + int32_t step_data[DAYS_PER_WEEK]; //!< Step histroy for today and the previous 6 days + int32_t current_distance_meters; + int32_t current_calories; + + //!< Typical step info + ActivityMetricAverages step_averages; //!< The step averages for the current day + int32_t current_step_average; //!< The current step average so far + int32_t step_average_last_updated_time; //!< The time at which current_step_average was updated + + int32_t monthly_step_average; + + int32_t sleep_data[DAYS_PER_WEEK]; //!< Sleep history for the past week + int32_t typical_sleep; //! Typical sleep for the current week day + int32_t deep_sleep; //!< Amount of deep sleep last night + + int32_t sleep_start; //!< When the user went to sleep (seconds after midnight) + int32_t sleep_end; //!< When the user woke up (seconds after midnight) + int32_t typical_sleep_start; //!< When the user typically goes to sleep + int32_t typical_sleep_end; //!< When the user typically wakes up + + int32_t monthly_sleep_average; + + uint32_t num_activity_sessions; //!< Number of activity sessions returned by the API + ActivitySession activity_sessions[ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT]; //!< Activity sessions + + int32_t current_hr_bpm; //!< Current BPM + int32_t resting_hr_bpm; //!< Resting BPM + time_t hr_last_updated; //!< Time at which HR data was last updated + + int32_t hr_zone1_minutes; + int32_t hr_zone2_minutes; + int32_t hr_zone3_minutes; +} HealthData; diff --git a/src/fw/apps/system/health/detail_card.c b/src/fw/apps/system/health/detail_card.c new file mode 100644 index 0000000000..d3667beba9 --- /dev/null +++ b/src/fw/apps/system/health/detail_card.c @@ -0,0 +1,542 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "detail_card.h" + +#include "applib/pbl_std/pbl_std.h" +#include "board/display.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/health_util.h" +#include "shell/prefs.h" +#include "util/size.h" + +#include "system/logging.h" + +// Compile-time display offset calculations +#define HEALTH_Y_OFFSET ((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) +#define HEALTH_PADDING_OFFSET (HEALTH_Y_OFFSET / 10) + +#define CORNER_RADIUS (3) + +static void prv_draw_headings(HealthDetailCard *detail_card, GContext *ctx, const Layer *layer) { + const int16_t rect_padding = PBL_IF_RECT_ELSE(5 + HEALTH_PADDING_OFFSET, 22); + const int16_t rect_height = 35; + + for (int i = 0; i < detail_card->num_headings; i++) { + HealthDetailHeading *heading = &detail_card->headings[i]; + + if (!heading->primary_label) { + continue; + } + +#if PBL_ROUND + int16_t header_y_origin = ((detail_card->num_headings > 1) ? 22 : 32) + (i * (rect_height + 5)); +#endif + + GRect header_rect = grect_inset(layer->bounds, GEdgeInsets(rect_padding)); + header_rect.origin.y += PBL_IF_RECT_ELSE(detail_card->y_origin, header_y_origin); + header_rect.size.h = rect_height; + + detail_card->y_origin += rect_height + rect_padding; + +#if PBL_BW + const GRect inner_rect = grect_inset(header_rect, GEdgeInsets(1)); + const uint16_t inner_corner_radius = CORNER_RADIUS - 1; + graphics_context_set_stroke_color(ctx, heading->outline_color); + graphics_draw_round_rect(ctx, &inner_rect, inner_corner_radius); + graphics_draw_round_rect(ctx, &header_rect, CORNER_RADIUS); +#else + graphics_context_set_fill_color(ctx, heading->fill_color); + graphics_fill_round_rect(ctx, &header_rect, CORNER_RADIUS, GCornersAll); +#endif + + const bool has_secondary_heading = heading->secondary_label; + + GRect label_rect = header_rect; + if (has_secondary_heading) { + label_rect.size.w /= 2; + } + label_rect.size.h = 12; // Restrict to a single line + + graphics_context_set_text_color(ctx, gcolor_legible_over(heading->fill_color)); + + graphics_draw_text(ctx, heading->primary_label, detail_card->heading_label_font, + label_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + + const int16_t value_rect_y_padding = 12; + + GRect value_rect = label_rect; + value_rect.origin.y += value_rect_y_padding; + + graphics_draw_text(ctx, heading->primary_value, detail_card->heading_value_font, + value_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + + if (!heading->secondary_label) { + continue; + } + + const int separator_padding = 5; + + GPoint separator_top_point = GPoint(header_rect.origin.x + (header_rect.size.w / 2) - 1, + header_rect.origin.y + separator_padding); + GPoint separator_bot_point = GPoint(separator_top_point.x, separator_top_point.y + + header_rect.size.h - (separator_padding * 2) - 1); + + graphics_draw_line(ctx, separator_top_point, separator_bot_point); + + // draw another line to make the width 2px + separator_top_point.x++; + separator_bot_point.x++; + graphics_draw_line(ctx, separator_top_point, separator_bot_point); + + label_rect.origin.x += label_rect.size.w; + value_rect.origin.x += value_rect.size.w; + + graphics_draw_text(ctx, heading->secondary_label, detail_card->heading_label_font, + label_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + + graphics_draw_text(ctx, heading->secondary_value, detail_card->heading_value_font, + value_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + } +} + +static void prv_draw_subtitles(HealthDetailCard *detail_card, GContext *ctx, const Layer *layer) { + const int16_t rect_padding = PBL_IF_RECT_ELSE(5 + HEALTH_PADDING_OFFSET, 0); + const int16_t rect_height = PBL_IF_RECT_ELSE(23, 36); + + for (int i = 0; i < detail_card->num_subtitles; i++) { + HealthDetailSubtitle *subtitle = &detail_card->subtitles[i]; + + if (!subtitle->label) { + continue; + } + + GRect subtitle_rect = grect_inset(layer->bounds, GEdgeInsets(rect_padding)); + subtitle_rect.origin.y += PBL_IF_RECT_ELSE(detail_card->y_origin, 125); + subtitle_rect.size.h = rect_height; + + detail_card->y_origin += rect_height + rect_padding; + + graphics_context_set_fill_color(ctx, subtitle->fill_color); + graphics_fill_round_rect(ctx, &subtitle_rect, CORNER_RADIUS, GCornersAll); + + if (!gcolor_equal(subtitle->outline_color, GColorClear)) { + graphics_context_set_stroke_color(ctx, subtitle->outline_color); + graphics_draw_round_rect(ctx, &subtitle_rect, CORNER_RADIUS); + } + + // font offset + subtitle_rect.origin.y -= PBL_IF_RECT_ELSE(1, 3); + + graphics_context_set_text_color(ctx, gcolor_legible_over(subtitle->fill_color)); + graphics_draw_text(ctx, subtitle->label, detail_card->subtitle_font, subtitle_rect, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + } +} + +static void prv_draw_progress_bar(GContext *ctx, HealthProgressBar *progress_bar, GColor bg_color, + GColor fill_color, int current_progress, int typical_progress, int max_progress, + bool hide_typical) { + const GColor typical_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack); + const GColor outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack); + + health_progress_bar_fill(ctx, progress_bar, bg_color, 0, HEALTH_PROGRESS_BAR_MAX_VALUE); + + if (max_progress > 0) { + const int current_fill = (current_progress * HEALTH_PROGRESS_BAR_MAX_VALUE) / max_progress; + + health_progress_bar_fill(ctx, progress_bar, fill_color, 0, current_fill); + + if (typical_progress > 0) { + const int typical_fill = (typical_progress * HEALTH_PROGRESS_BAR_MAX_VALUE) / max_progress; + health_progress_bar_mark(ctx, progress_bar, typical_color, typical_fill); + } + } + + health_progress_bar_outline(ctx, progress_bar, outline_color); +} + +#if PBL_RECT +static void prv_draw_progress_bar_in_zone(GContext *ctx, const GRect *zone_rect, GColor fill_color, + int current_progress, int typical_progress, int max_progress, bool hide_typical) { + const int16_t progress_bar_x = zone_rect->origin.x + PBL_IF_BW_ELSE(0, -1); + const int16_t progress_bar_y = zone_rect->origin.y + 22; + const int16_t progress_bar_width = zone_rect->size.w + PBL_IF_BW_ELSE(-2, 1); + const int16_t progress_bar_height = 10 + PBL_IF_BW_ELSE(-1, 0); + + HealthProgressSegment segments[] = { + { + // Left side vertical line (needed for the draw outline function to draw the verticle lines) + .type = HealthProgressSegmentType_Corner, + .points = { + {progress_bar_x, progress_bar_y}, + {progress_bar_x, progress_bar_y + progress_bar_height}, + {progress_bar_x, progress_bar_y + progress_bar_height}, + {progress_bar_x, progress_bar_y}, + }, + }, + { + // Right side vertical line (needed for the draw outline function to draw the verticle lines) + .type = HealthProgressSegmentType_Corner, + .points = { + {progress_bar_x + progress_bar_width, progress_bar_y}, + {progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height}, + {progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height}, + {progress_bar_x + progress_bar_width, progress_bar_y}, + }, + }, + { + // Horizontal bar from left line to right line + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = HEALTH_PROGRESS_BAR_MAX_VALUE, + .mark_width = 124, // Arbitrarily chosen through trial and error + .points = { + {progress_bar_x, progress_bar_y + progress_bar_height}, + {progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height}, + {progress_bar_x + progress_bar_width, progress_bar_y}, + {progress_bar_x, progress_bar_y}, + }, + }, + }; + + HealthProgressBar progress_bar = { + .num_segments = ARRAY_LENGTH(segments), + .segments = segments, + }; + + const GColor bg_color = PBL_IF_COLOR_ELSE(GColorDarkGray, GColorWhite); + + prv_draw_progress_bar(ctx, &progress_bar, bg_color, fill_color, current_progress, + typical_progress, max_progress, hide_typical); +} + +static void prv_draw_zones(HealthDetailCard *detail_card, GContext *ctx) { + if (detail_card->num_zones <= 0) { + return; + } + + const int16_t rect_padding = 5 + HEALTH_PADDING_OFFSET; + const int16_t rect_height = 33; + + GRect zone_rect = grect_inset(detail_card->window.layer.bounds, GEdgeInsets(rect_padding)); + zone_rect.origin.y += detail_card->y_origin; + zone_rect.size.h = rect_height; + + for (int i = 0; i < detail_card->num_zones; i++) { + HealthDetailZone *zone = &detail_card->zones[i]; + + graphics_context_set_text_color(ctx, gcolor_legible_over(detail_card->bg_color)); + graphics_draw_text(ctx, zone->label, detail_card->subtitle_font, zone_rect, + GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); + + if (zone->show_crown) { + const GSize label_size = app_graphics_text_layout_get_content_size(zone->label, + detail_card->subtitle_font, zone_rect, GTextOverflowModeWordWrap, GTextAlignmentLeft); + GPoint icon_offset = zone_rect.origin; + icon_offset.x += label_size.w + 4; +#if PBL_BW + icon_offset.y += 2; +#endif + gdraw_command_image_draw(ctx, detail_card->icon_crown, icon_offset); + } + + prv_draw_progress_bar_in_zone(ctx, &zone_rect, zone->fill_color, zone->progress, + detail_card->daily_avg, detail_card->max_progress, zone->hide_typical); + + zone_rect.origin.y += rect_height + rect_padding; + detail_card->y_origin += rect_height + rect_padding; + } + + detail_card->y_origin += rect_padding; +} +#endif // PBL_RECT + +#if PBL_ROUND +static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, void *context) { + HealthDetailCard *detail_card = (HealthDetailCard *)context; + return detail_card->num_zones + 1; +} + +static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + HealthDetailCard *detail_card = (HealthDetailCard *)context; + + MenuIndex selected_index = menu_layer_get_selected_index(&detail_card->menu_layer); + + if (cell_index->row == 0) { + graphics_context_set_fill_color(ctx, detail_card->bg_color); + graphics_fill_rect(ctx, &cell_layer->bounds); + + prv_draw_headings(detail_card, ctx, cell_layer); + prv_draw_subtitles(detail_card, ctx, cell_layer); + return; + } + + HealthDetailZone *zone = &detail_card->zones[cell_index->row - 1]; + + const int16_t rect_padding = 5; + + GRect label_rect = grect_inset(cell_layer->bounds, GEdgeInsets(rect_padding)); + + if (!menu_layer_is_index_selected(&detail_card->menu_layer, cell_index)) { + label_rect.origin.y = (cell_index->row < selected_index.row) ? 3 : 22; + + graphics_context_set_text_color(ctx, gcolor_legible_over(detail_card->bg_color)); + graphics_draw_text(ctx, zone->label, detail_card->subtitle_font, label_rect, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + } else { + const GRect cell_bounds = grect_inset(cell_layer->bounds, GEdgeInsets(0, -1)); + + HealthProgressSegment segments[] = { + { + // Horizontal bar from left line to right line + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = HEALTH_PROGRESS_BAR_MAX_VALUE, + .mark_width = 100, // Arbitrarily chosen through trial and error + .points = { + {cell_bounds.origin.x, cell_bounds.size.h}, + {cell_bounds.size.w, cell_bounds.size.h}, + {cell_bounds.size.w, cell_bounds.origin.y}, + {cell_bounds.origin.x, cell_bounds.origin.y}, + }, + }, + }; + + HealthProgressBar progress_bar = { + .num_segments = ARRAY_LENGTH(segments), + .segments = segments, + }; + + prv_draw_progress_bar(ctx, &progress_bar, GColorLightGray, zone->fill_color, zone->progress, + detail_card->daily_avg, detail_card->max_progress, zone->hide_typical); + + label_rect.origin.y += 3; + + if (zone->show_crown) { + const GSize icon_size = gdraw_command_image_get_bounds_size(detail_card->icon_crown); + GPoint icon_offset = GPoint((cell_layer->bounds.size.w / 2) - (icon_size.w / 2), 4); + gdraw_command_image_draw(ctx, detail_card->icon_crown, icon_offset); + + label_rect.origin.y += 8; + } + + graphics_context_set_text_color(ctx, GColorBlack); + graphics_draw_text(ctx, zone->label, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), label_rect, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + } +} + +static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, void *context) { + if (cell_index->row == 0) { + return menu_layer_is_index_selected(menu_layer, cell_index) ? DISP_ROWS : 0; + } + + return menu_layer_is_index_selected(menu_layer, cell_index) ? 50 : 54; +} + +static void prv_refresh_content_indicators(HealthDetailCard *detail_card) { + const bool is_up_visible = (menu_layer_get_selected_index(&detail_card->menu_layer).row > 0); + const bool is_down_visible = (menu_layer_get_selected_index(&detail_card->menu_layer).row < + prv_get_num_rows_callback(&detail_card->menu_layer, 0, detail_card) - 1); + + content_indicator_set_content_available(&detail_card->up_indicator, + ContentIndicatorDirectionUp, + is_up_visible); + + content_indicator_set_content_available(&detail_card->down_indicator, + ContentIndicatorDirectionDown, + is_down_visible); +} + +static void prv_selection_changed_callback(struct MenuLayer *menu_layer, MenuIndex new_index, + MenuIndex old_index, void *context) { + HealthDetailCard *detail_card = (HealthDetailCard *)context; + prv_refresh_content_indicators(detail_card); +} +#else // PBL_RECT +static void prv_health_detail_scroll_layer_update_proc(Layer *layer, GContext *ctx) { + ScrollLayer *scroll_layer = (ScrollLayer *)layer->parent; + HealthDetailCard *detail_card = (HealthDetailCard *)scroll_layer->context; + + detail_card->y_origin = 0; + + prv_draw_headings(detail_card, ctx, &detail_card->window.layer); + prv_draw_subtitles(detail_card, ctx, &detail_card->window.layer); + prv_draw_zones(detail_card, ctx); + + scroll_layer_set_content_size(&detail_card->scroll_layer, + GSize(layer->bounds.size.w, detail_card->y_origin)); +} +#endif // PBL_RECT + +HealthDetailCard *health_detail_card_create(const HealthDetailCardConfig *config) { + HealthDetailCard *detail_card = app_zalloc_check(sizeof(HealthDetailCard)); + window_init(&detail_card->window, WINDOW_NAME("Health Detail Card")); + health_detail_card_configure(detail_card, config); + GRect window_frame = detail_card->window.layer.frame; +#if PBL_ROUND + // setup menu layer + MenuLayer *menu_layer = &detail_card->menu_layer; + menu_layer_init(menu_layer, &window_frame); + menu_layer_set_callbacks(menu_layer, detail_card, &(MenuLayerCallbacks) { + .get_num_rows = prv_get_num_rows_callback, + .get_cell_height = prv_get_cell_height_callback, + .draw_row = prv_draw_row_callback, + .selection_changed = prv_selection_changed_callback, + }); + menu_layer_set_normal_colors(menu_layer, detail_card->bg_color, GColorWhite); + menu_layer_set_highlight_colors(menu_layer, detail_card->bg_color, GColorBlack); + menu_layer_set_click_config_onto_window(menu_layer, &detail_card->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + layer_add_child(&detail_card->window.layer, menu_layer_get_layer(menu_layer)); + + // setup content indicators + const int content_indicator_height = 15; + const GRect down_arrow_layer_frame = grect_inset(window_frame, + GEdgeInsets(window_frame.size.h - content_indicator_height, 0, 0)); + layer_init(&detail_card->down_arrow_layer, &down_arrow_layer_frame); + layer_add_child(&detail_card->window.layer, &detail_card->down_arrow_layer); + content_indicator_init(&detail_card->down_indicator); + + const GRect up_arrow_layer_frame = grect_inset(window_frame, + GEdgeInsets(0, 0, window_frame.size.h - content_indicator_height)); + layer_init(&detail_card->up_arrow_layer, &up_arrow_layer_frame); + layer_add_child(&detail_card->window.layer, &detail_card->up_arrow_layer); + content_indicator_init(&detail_card->up_indicator); + + ContentIndicatorConfig content_indicator_config = (ContentIndicatorConfig) { + .layer = &detail_card->up_arrow_layer, + .colors.foreground = gcolor_legible_over(detail_card->bg_color), + .colors.background = detail_card->bg_color, + }; + content_indicator_configure_direction(&detail_card->up_indicator, ContentIndicatorDirectionUp, + &content_indicator_config); + content_indicator_config.layer = &detail_card->down_arrow_layer; + content_indicator_configure_direction(&detail_card->down_indicator, ContentIndicatorDirectionDown, + &content_indicator_config); + prv_refresh_content_indicators(detail_card); +#else // PBL_RECT + // setup scroll layer + scroll_layer_init(&detail_card->scroll_layer, &window_frame); + scroll_layer_set_click_config_onto_window(&detail_card->scroll_layer, &detail_card->window); + scroll_layer_set_context(&detail_card->scroll_layer, detail_card); + scroll_layer_set_shadow_hidden(&detail_card->scroll_layer, true); + layer_add_child(&detail_card->window.layer, (Layer *)&detail_card->scroll_layer); + layer_set_update_proc(&detail_card->scroll_layer.content_sublayer, + prv_health_detail_scroll_layer_update_proc); +#endif // PBL_RECT + detail_card->icon_crown = gdraw_command_image_create_with_resource(RESOURCE_ID_HEALTH_APP_CROWN); + detail_card->heading_label_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); + detail_card->heading_value_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + detail_card->subtitle_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + return detail_card; +} + +void health_detail_card_destroy(HealthDetailCard *detail_card) { + if (!detail_card) { + return; + } + gdraw_command_image_destroy(detail_card->icon_crown); +#if PBL_ROUND + menu_layer_deinit(&detail_card->menu_layer); + content_indicator_deinit(&detail_card->down_indicator); + content_indicator_deinit(&detail_card->up_indicator); +#else + scroll_layer_deinit(&detail_card->scroll_layer); +#endif + i18n_free_all(detail_card); + app_free(detail_card); +} + +void health_detail_card_configure(HealthDetailCard *detail_card, + const HealthDetailCardConfig *config) { + if (!detail_card || !config) { + return; + } + detail_card->bg_color = config->bg_color; + window_set_background_color(&detail_card->window, detail_card->bg_color); + if (config->num_headings) { + detail_card->num_headings = config->num_headings; + detail_card->headings = config->headings; + } + if (config->num_subtitles) { + detail_card->num_subtitles = config->num_subtitles; + detail_card->subtitles = config->subtitles; + } + detail_card->daily_avg = config->daily_avg; + detail_card->max_progress = MAX(config->weekly_max, detail_card->daily_avg); + detail_card->max_progress = detail_card->max_progress * 11 / 10; // add 10%; + if (config->num_zones) { + detail_card->num_zones = config->num_zones; + detail_card->zones = config->zones; + } + if (config->data) { + detail_card->data = config->data; + } +} + +void health_detail_card_set_render_day_zones(HealthDetailZone *zones, int16_t *num_zones, + int32_t *weekly_max, bool format_hours_and_minutes, bool show_crown, GColor fill_color, + GColor today_fill_color, int32_t *day_data, void *i18n_owner) { + time_t time_utc = rtc_get_time(); + struct tm time_tm; + + int max_data = 0; + int crown_index = 0; + + *num_zones = DAYS_PER_WEEK; + + for (int i = 0; i < *num_zones; i++) { + localtime_r(&time_utc, &time_tm); + + const bool is_today = (i == 0); + + const size_t buffer_size = 32; + zones[i] = (HealthDetailZone) { + .label = app_zalloc_check(buffer_size), + .progress = day_data[i], + .fill_color = is_today ? PBL_IF_ROUND_ELSE(fill_color, today_fill_color) : fill_color, + .hide_typical = is_today, + }; + + char *label_ptr = zones[i].label; + + int pos = 0; + + if (i == 0) { + pos += snprintf(label_ptr, buffer_size, "%s ", i18n_get("Today", i18n_owner)); + } else { + pos += strftime(label_ptr, buffer_size, "%a ", &time_tm); + } + + if (day_data[i] > 0) { + if (format_hours_and_minutes) { + health_util_format_hours_and_minutes(label_ptr + pos, buffer_size - pos, day_data[i], + i18n_owner); + } else { + snprintf(label_ptr + pos, buffer_size - pos, "%"PRId32, day_data[i]); + } + } + + if (day_data[i] > *weekly_max) { + *weekly_max = day_data[i]; + } + + if (day_data[i] > max_data) { + max_data = day_data[i]; + crown_index = i; + } + + time_utc -= SECONDS_PER_DAY; + } + + if (crown_index && show_crown) { + zones[crown_index].show_crown = true; + } +} diff --git a/src/fw/apps/system/health/detail_card.h b/src/fw/apps/system/health/detail_card.h new file mode 100644 index 0000000000..85ab4d7017 --- /dev/null +++ b/src/fw/apps/system/health/detail_card.h @@ -0,0 +1,113 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" +#include "progress.h" + +#include "applib/fonts/fonts.h" +#include "applib/graphics/gdraw_command_image.h" +#include "applib/ui/content_indicator_private.h" +#include "applib/ui/ui.h" + +#define MAX_NUM_HEADINGS (2) +#define MAX_NUM_SUBTITLES (2) +#define MAX_NUM_ZONES (7) + +typedef struct HealthDetailHeading { + char *primary_label; + char *primary_value; + char *secondary_label; + char *secondary_value; + GColor fill_color; + GColor outline_color; +} HealthDetailHeading; + +typedef struct HealthDetailSubtitle { + char *label; + GColor fill_color; + GColor outline_color; +} HealthDetailSubtitle; + +typedef struct HealthDetailZone { + char *label; + bool show_crown; + bool hide_typical; + GColor fill_color; + HealthProgressBarValue progress; +} HealthDetailZone; + +typedef struct HealthDetailCardConfig { + GColor bg_color; + int16_t num_headings; + HealthDetailHeading *headings; + int16_t num_subtitles; + HealthDetailSubtitle *subtitles; + int32_t daily_avg; + int32_t weekly_max; + int16_t num_zones; + HealthDetailZone *zones; + void *data; +} HealthDetailCardConfig; + +typedef struct HealthDetailCard { + Window window; +#if PBL_ROUND + MenuLayer menu_layer; + Layer down_arrow_layer; + Layer up_arrow_layer; + ContentIndicator down_indicator; + ContentIndicator up_indicator; +#else + ScrollLayer scroll_layer; +#endif + + GColor bg_color; + + int16_t num_headings; + HealthDetailHeading *headings; + + int16_t num_subtitles; + HealthDetailSubtitle *subtitles; + + GFont heading_label_font; + GFont heading_value_font; + GFont subtitle_font; + + GDrawCommandImage *icon_crown; + + int32_t daily_avg; + int32_t max_progress; + + int16_t num_zones; + HealthDetailZone *zones; + + int16_t y_origin; + + void *data; +} HealthDetailCard; + +//! Creates a HealthDetailCard +HealthDetailCard *health_detail_card_create(const HealthDetailCardConfig *config); + +//! Destroys a HealthDetailCard +void health_detail_card_destroy(HealthDetailCard *detail_card); + +//! Configures a HealthDetailCard +void health_detail_card_configure(HealthDetailCard *detail_card, + const HealthDetailCardConfig *config); + +//! Sets the zones for any daily history (steps/sleep) +//! @param zones pointer to the HealthDetailZone to be set +//! @param num_zones number of zones in the pointer +//! @wparam weekly_max pointer to the weekly max to be set +//! @param format_hours_and_minutes whether to a format the values for hours and minutes in label +//! @param show_crown whether to set the `show_crown` in the zone with weekly max +//! @param fill_color color to fill all the progress bars with except today +//! @param today_fill_color color to fill the today progress bar with +//! @param day_data pointer to the daily history data +//! @param i18n_owner pointer to the i18n owner +void health_detail_card_set_render_day_zones(HealthDetailZone *zones, int16_t *num_zones, + int32_t *weekly_max, bool format_hours_and_minutes, bool show_crown, GColor fill_color, + GColor today_fill_color, int32_t *day_data, void *i18n_owner); diff --git a/src/fw/apps/system/health/graph_card.c b/src/fw/apps/system/health/graph_card.c new file mode 100644 index 0000000000..16df5d8fc6 --- /dev/null +++ b/src/fw/apps/system/health/graph_card.c @@ -0,0 +1,444 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "graph_card.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "board/display.h" +#include "drivers/rtc.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "util/math.h" +#include "util/size.h" +#include "util/string.h" +#include "util/time/time.h" + +// Compile-time display offset calculations +#define DISPLAY_Y_OFFSET ((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + +//! Marks where the graph begins - base values for 168px height +#define GRAPH_OFFSET_Y_BASE PBL_IF_RECT_ELSE(38, 48) + +//! Marks where the graph ends and where the labels begin - base values for 168px height +#define LABEL_OFFSET_Y_BASE PBL_IF_RECT_ELSE(118, 113) +#define LABEL_HEIGHT 27 +#define GRAPH_OFFSET_Y (GRAPH_OFFSET_Y_BASE + DISPLAY_Y_OFFSET) +#define LABEL_OFFSET_Y (LABEL_OFFSET_Y_BASE + DISPLAY_Y_OFFSET) + +#define GRAPH_HEIGHT (LABEL_OFFSET_Y - GRAPH_OFFSET_Y) + +// Compile-time bar width calculation +#define HEALTH_BAR_WIDTH (23 + (DISP_COLS - LEGACY_2X_DISP_COLS) / 19) +#define HEALTH_BAR_INSET 3 +#define HEALTH_TOTAL_BAR_WIDTHS PBL_IF_RECT_ELSE((HEALTH_BAR_WIDTH * 7 + 3) - (HEALTH_BAR_INSET * 6), 141) + +// Compile-time avg line width calculations +#define HEALTH_WEEKDAY_WIDTH_BASE PBL_IF_RECT_ELSE(103, 119) +#define HEALTH_WEEKEND_WIDTH_BASE PBL_IF_RECT_ELSE(38, 58) +#define HEALTH_WEEKDAY_WIDTH (HEALTH_WEEKDAY_WIDTH_BASE + (DISP_COLS - LEGACY_2X_DISP_COLS) * HEALTH_WEEKDAY_WIDTH_BASE / LEGACY_2X_DISP_COLS) +#define HEALTH_WEEKEND_WIDTH (HEALTH_WEEKEND_WIDTH_BASE + (DISP_COLS - LEGACY_2X_DISP_COLS) * HEALTH_WEEKEND_WIDTH_BASE / LEGACY_2X_DISP_COLS) + +#define AVG_LINE_HEIGHT 4 +#define AVG_LINE_LEGEND_WIDTH 10 +#define AVG_LINE_COLOR GColorYellow + +#define INFO_PADDING_BOTTOM 6 + +//! Get the current day in the standard tm format. Sunday is 0 +static uint8_t prv_get_weekday(time_t timestamp) { + return time_util_get_day_in_week(timestamp); +} + +static void prv_draw_title(HealthGraphCard *graph_card, GContext *ctx) { + const GRect *bounds = &graph_card->layer.bounds; + graphics_context_set_text_color(ctx, GColorBlack); + const int title_height = 60; + GRect drawing_box = GRect(0, 0, bounds->size.w, title_height); + +#if PBL_ROUND + // inset the drawing bounds if on round to account for the bezel + drawing_box = grect_inset(drawing_box, GEdgeInsets(8)); + + const GSize text_size = graphics_text_layout_get_max_used_size(ctx, graph_card->title, + graph_card->title_font, drawing_box, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + + // increase drawing box y offset if we're only drawing one line of text + if (text_size.h < 30) { + drawing_box.origin.y += 10; + } +#endif + + graphics_draw_text(ctx, graph_card->title, graph_card->title_font, drawing_box, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); +} + +static void prv_draw_day_labels_background(HealthGraphCard *graph_card, GContext *ctx) { + const GRect *bounds = &graph_card->layer.bounds; + GRect box = {{ .y = LABEL_OFFSET_Y }, { bounds->size.w, LABEL_HEIGHT }}; + graphics_context_set_fill_color(ctx, GColorBlack); + graphics_fill_rect(ctx, &box); + const int border_width = 3; + box = grect_inset(box, GEdgeInsets(border_width, 0)); + const GColor label_background_color = GColorWhite; + graphics_context_set_fill_color(ctx, label_background_color); + graphics_fill_rect(ctx, &box); +} + +//! Get the corresponding data point for a weekday. +//! Sunday is 0, and the day data begins with today and continues into the past. +static int32_t prv_get_day_point(HealthGraphCard *graph_card, int weekday) { + const int index = positive_modulo(graph_card->current_day - weekday, DAYS_PER_WEEK); + return graph_card->day_data[index]; +} + +static int32_t prv_convert_to_graph_height(HealthGraphCard *graph_card, int32_t point) { + // Round up in order to show the minimum stub bar once progress begins + int bar_height = (point * GRAPH_HEIGHT + graph_card->data_max - 1) / graph_card->data_max; + const int minimum_stub_height = 5; + if (bar_height > 0 && bar_height < minimum_stub_height) { + // Show the minimum stub bar if progress just began + bar_height = minimum_stub_height; + } + return bar_height; +} + +static void prv_setup_day_bar_box(int weekday, GRect *box, int16_t bar_height) { + const int w = HEALTH_BAR_WIDTH; +#if PBL_RECT + // The center bars are slightly wider than the other bars + // Note that Thursday is the center bar, not Wednesday since drawing begins with Monday + // S M T W T F S + const int bar_widths[DAYS_PER_WEEK] = { w, w, w, w + 1, w + 1, w + 1, w }; + const int bar_width = bar_widths[weekday]; +#else + const int bar_width = w; +#endif + box->origin.y = LABEL_OFFSET_Y - bar_height; + box->size = GSize(bar_width, bar_height); +} + +static void prv_draw_day_bar_wide(GContext *ctx, const GRect *box, const GRect *box_inset, + GColor bar_color) { + const GColor border_color = GColorBlack; + graphics_context_set_fill_color(ctx, border_color); + graphics_fill_rect(ctx, box); + graphics_context_set_fill_color(ctx, bar_color); + graphics_fill_rect(ctx, box_inset); +} + +static void prv_draw_day_bar_thin(GContext *ctx, const GRect *box, int weekday, + GColor bar_color) { + GRect thin_box = *box; + // Nudge the bars before Thursday (inclusive). Note that Sunday is on the right side, at the end + const int thin_offset_x = WITHIN(weekday, Monday, Thursday) ? 1 : 0; + const int thin_width = 5; + thin_box.origin.x += thin_offset_x + (box->size.w - thin_width) / 2; + thin_box.size.w = thin_width; + graphics_context_set_fill_color(ctx, bar_color); + graphics_fill_rect(ctx, &thin_box); +} + +static int16_t prv_draw_day_bar(GContext *ctx, int weekday, const GRect *box, + GColor bar_color, bool wide_bar) { + const int bar_inset = 3; + GRect box_inset = grect_inset(*box, GEdgeInsets(bar_inset, bar_inset, 0, bar_inset)); + if (wide_bar) { + prv_draw_day_bar_wide(ctx, box, &box_inset, bar_color); + } else { + prv_draw_day_bar_thin(ctx, box, weekday, bar_color); + } + // The borders of the boxes caused by the inset need to overlap each other + return box->origin.x + box->size.w - bar_inset; +} + +static bool prv_bar_should_be_wide(int draw_weekday, int current_weekday) { + // The graph begins on Monday, so all bars from Monday until current (inclusive) should be wide + return (positive_modulo(draw_weekday - Monday, DAYS_PER_WEEK) <= + positive_modulo(current_weekday - Monday, DAYS_PER_WEEK)); +} + +static GColor prv_get_bar_color(HealthGraphCard *graph_card, bool is_active, bool is_wide) { + const GColor active_color = GColorWhite; + const GColor inactive_wide_color = GColorDarkGray; + const GColor inactive_thin_color = graph_card->inactive_color; + return (is_active ? active_color : (is_wide ? inactive_wide_color : inactive_thin_color)); +} + +static void prv_draw_day_bars(HealthGraphCard *graph_card, GContext *ctx) { + // With values from prv_setup_day_bar_box and prv_draw_day_bar, + // total_bar_width is sum(bar_widths) - (bar_inset * (DAYS_PER_WEEK - 1)) + const int total_bar_widths = HEALTH_TOTAL_BAR_WIDTHS; + const int legend_line_height = fonts_get_font_height(graph_card->legend_font); + const GRect *bounds = &graph_card->layer.bounds; + GRect box = { .origin.x = (bounds->size.w - total_bar_widths) / 2, .origin.y = LABEL_OFFSET_Y }; + // The first day to draw is Monday, and draw a week's worth of bars + for (int i = Monday, draw_count = 0; + draw_count < DAYS_PER_WEEK; + draw_count++, i = (i + 1) % DAYS_PER_WEEK) { + // Setup the dimensions and color of the day bar + const int32_t day_point = prv_get_day_point(graph_card, i); + const int bar_height = prv_convert_to_graph_height(graph_card, day_point); + + const bool is_active = (graph_card->selection == i); + if (graph_card->current_day == i) { + // Draw last week's bar as a thin bar behind this bar + const int32_t last_bar_height = + prv_convert_to_graph_height(graph_card, graph_card->day_data[DAYS_PER_WEEK]); + prv_setup_day_bar_box(i, &box, last_bar_height); + const GColor bar_color = prv_get_bar_color(graph_card, is_active, false /* wide bar */); + prv_draw_day_bar(ctx, i, &box, bar_color, false /* wide bar */); + } + + // Draw the day bar + prv_setup_day_bar_box(i, &box, bar_height); + const bool is_wide = prv_bar_should_be_wide(i, graph_card->current_day); + const GColor bar_color = prv_get_bar_color(graph_card, is_active, is_wide); + const int16_t next_x = prv_draw_day_bar(ctx, i, &box, bar_color, is_wide); + + // Draw the day character legend + const int char_offset_y = 1; + box.origin.y = LABEL_OFFSET_Y + char_offset_y; + box.size.h = legend_line_height; + char char_buffer[] = { graph_card->day_chars[i], '\0' }; + const GColor active_legend_color = GColorRed; + const GColor inactive_legend_color = GColorBlack; + graphics_context_set_text_color(ctx, is_active ? active_legend_color : inactive_legend_color); + graphics_draw_text(ctx, char_buffer, graph_card->legend_font, box, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + + // Move the box cursor to the next bar + box.origin.x = next_x; + } +} + +static void prv_draw_avg_line(HealthGraphCard *graph_card, GContext *ctx, int32_t avg, + int16_t offset_x, int16_t width) { + if (avg == 0) { + return; + } + const int offset_y = LABEL_OFFSET_Y - MAX(prv_convert_to_graph_height(graph_card, avg), + AVG_LINE_HEIGHT / 2); + graphics_context_set_fill_color(ctx, AVG_LINE_COLOR); + graphics_fill_rect(ctx, &(GRect) {{ offset_x, offset_y - AVG_LINE_HEIGHT / 2 }, + { width, AVG_LINE_HEIGHT }}); +} + +static void prv_draw_avg_lines(HealthGraphCard *graph_card, GContext *ctx) { + const GRect *bounds = &graph_card->layer.bounds; + prv_draw_avg_line(graph_card, ctx, graph_card->stats.weekday.avg, 0, HEALTH_WEEKDAY_WIDTH); + prv_draw_avg_line(graph_card, ctx, graph_card->stats.weekend.avg, bounds->size.w - HEALTH_WEEKEND_WIDTH, + HEALTH_WEEKEND_WIDTH); +} + +static int32_t prv_get_info_data_point(HealthGraphCard *graph_card) { + // Show today's data point if the selection is a day of the week, otherwise show the weekday + // average if the current day is a weekday or weekend average if the current day is on the weekend + if (graph_card->selection == HealthGraphIndex_Average) { + return IS_WEEKDAY(graph_card->current_day) ? graph_card->stats.weekday.avg : + graph_card->stats.weekend.avg; + } + int day_point = prv_get_day_point(graph_card, graph_card->selection); + if (graph_card->selection == graph_card->current_day && day_point == 0) { + // If today has no progress, use the info from last week + day_point = graph_card->day_data[DAYS_PER_WEEK]; + } + return day_point; +} + +static void prv_draw_avg_line_legend(HealthGraphCard *graph_card, GContext *ctx, int offset_x, + int info_offset_y, GSize custom_text_size) { + const int info_line_height = fonts_get_font_height(graph_card->legend_font); + const int avg_line_offset_y = -1; + const GRect avg_line_box = { + .origin.x = offset_x, + // Position vertically centered with the text + .origin.y = info_offset_y + (info_line_height + INFO_PADDING_BOTTOM) / 2 + avg_line_offset_y, + .size = { AVG_LINE_LEGEND_WIDTH, AVG_LINE_HEIGHT }, + }; + graphics_context_set_fill_color(ctx, AVG_LINE_COLOR); + graphics_fill_rect(ctx, &avg_line_box); +} + +static void prv_draw_avg_info_text(HealthGraphCard *graph_card, GContext *ctx, int offset_x, + int offset_y, int height) { + const GRect *bounds = &graph_card->layer.bounds; + const GRect avg_text_box = {{ offset_x, offset_y }, { bounds->size.w, height }}; + graphics_draw_text(ctx, graph_card->info_avg, graph_card->legend_font, avg_text_box, + GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); +} + +static void prv_draw_custom_info_text(HealthGraphCard *graph_card, GContext *ctx, char *text, + int offset_x, int info_offset_y, int info_height) { + const GRect *bounds = &graph_card->layer.bounds; + const GRect info_text_box = {{ offset_x, info_offset_y }, { bounds->size.w, info_height }}; + graphics_context_set_text_color(ctx, GColorBlack); + graphics_draw_text(ctx, text, graph_card->legend_font, info_text_box, + GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); +} + +static bool prv_is_selection_last_weekday(HealthGraphCard *graph_card) { + // If the selection is today, the selection is last week's only if today has no progress + // Else if today is Sunday, the entire graph represents the current week + // Otherwise the selection is last week if either the selection is Sunday + // or if the selection is greater than the current day + return (((int)graph_card->current_day == graph_card->selection && graph_card->day_data[0] == 0) || + ((graph_card->current_day == Sunday) ? false : + ((int)graph_card->selection == Sunday || + (int)graph_card->selection > graph_card->current_day))); +} + +size_t health_graph_format_weekday_prefix(HealthGraphCard *graph_card, char *buffer, + size_t buffer_size) { + if (prv_is_selection_last_weekday(graph_card)) { + // The graph starts on Monday, so wrap around the selection and current_day for Sunday + const time_t selection_time = + ((positive_modulo(graph_card->selection - Monday, DAYS_PER_WEEK) - + positive_modulo(graph_card->current_day - Monday, DAYS_PER_WEEK) - DAYS_PER_WEEK) * + SECONDS_PER_DAY) + graph_card->data_timestamp; + const int pos = clock_get_month_named_abbrev_date(buffer, buffer_size, selection_time); + strncat(buffer, i18n_get(": ", graph_card), buffer_size - pos - 1); + return strlen(buffer); + } else { + struct tm local_tm = (struct tm) { + .tm_wday = positive_modulo(graph_card->selection, DAYS_PER_WEEK), + }; + return strftime(buffer, buffer_size, i18n_get("%a: ", graph_card), &local_tm); + } +} + +static void prv_draw_info_with_text(HealthGraphCard *graph_card, GContext *ctx, char *text) { + const GRect *bounds = &graph_card->layer.bounds; + // Calculate the custom info text size + GSize custom_text_size; + const TextLayoutExtended text_layout = {}; + graphics_text_layout_get_max_used_size(ctx, text, graph_card->legend_font, *bounds, + GTextOverflowModeWordWrap, GTextAlignmentLeft, + (GTextAttributes *)&text_layout); + custom_text_size = text_layout.max_used_size; + GSize avg_text_size = GSizeZero; + int total_width = custom_text_size.w; + + const int info_padding_top = PBL_IF_RECT_ELSE(-1, 1); + const int info_offset_y = LABEL_OFFSET_Y + LABEL_HEIGHT + info_padding_top; + const int info_line_height = fonts_get_font_height(graph_card->legend_font); + const int info_height = PBL_IF_ROUND_ELSE(2, 1) * info_line_height + INFO_PADDING_BOTTOM; + + int cursor_x = 0; + if (graph_card->selection == HealthGraphIndex_Average) { + graphics_text_layout_get_max_used_size(ctx, graph_card->info_avg, graph_card->legend_font, + *bounds, GTextOverflowModeWordWrap, + GTextAlignmentLeft, (GTextAttributes *)&text_layout); + avg_text_size = text_layout.max_used_size; + total_width += avg_text_size.w + AVG_LINE_LEGEND_WIDTH; + + // Draw the avg line legend + cursor_x = (bounds->size.w - total_width) / 2; + prv_draw_avg_line_legend(graph_card, ctx, cursor_x, info_offset_y, custom_text_size); + cursor_x += AVG_LINE_LEGEND_WIDTH; + + // Draw the avg info text + prv_draw_avg_info_text(graph_card, ctx, cursor_x, info_offset_y, info_height); + cursor_x += avg_text_size.w; + } else { + // Center the custom text + cursor_x = (bounds->size.w - total_width) / 2; + } + + // Draw the custom info text + prv_draw_custom_info_text(graph_card, ctx, text, cursor_x, info_offset_y, info_height); +} + +static void prv_draw_info(HealthGraphCard *graph_card, GContext *ctx) { + if (!graph_card->info_buffer_size) { + return; + } + char buffer[graph_card->info_buffer_size]; + memset(buffer, 0, sizeof(buffer)); + if (graph_card->info_update) { + const int32_t day_point = prv_get_info_data_point(graph_card); + graph_card->info_update(graph_card, day_point, buffer, sizeof(buffer)); + } + if (IS_EMPTY_STRING(buffer)) { + return; + } + prv_draw_info_with_text(graph_card, ctx, buffer); +} + +static void prv_health_graph_layer_update_proc(Layer *layer, GContext *ctx) { + HealthGraphCard *graph_card = (HealthGraphCard *)layer; + + prv_draw_title(graph_card, ctx); + prv_draw_day_labels_background(graph_card, ctx); + prv_draw_day_bars(graph_card, ctx); + prv_draw_avg_lines(graph_card, ctx); + prv_draw_info(graph_card, ctx); +} + +HealthGraphCard *health_graph_card_create(const HealthGraphCardConfig *config) { + HealthGraphCard *graph_card = app_zalloc_check(sizeof(HealthGraphCard)); + if (graph_card) { + layer_init(&graph_card->layer, &GRectZero); + layer_set_update_proc(&graph_card->layer, prv_health_graph_layer_update_proc); + health_graph_card_configure(graph_card, config); + graph_card->title_font = fonts_get_system_font(PBL_IF_RECT_ELSE(FONT_KEY_GOTHIC_24_BOLD, + FONT_KEY_GOTHIC_18_BOLD)); + graph_card->legend_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + graph_card->current_day = prv_get_weekday(graph_card->data_timestamp); + // The day characters in standard tm weekday order + graph_card->day_chars = i18n_get("SMTWTFS", graph_card); + graph_card->selection = HealthGraphIndex_Average; + } + return graph_card; +} + +void health_graph_card_destroy(HealthGraphCard *graph_card) { + if (!graph_card) { + return; + } + layer_deinit(&graph_card->layer); + i18n_free_all(graph_card); + app_free(graph_card); +} + +void health_graph_card_configure(HealthGraphCard *graph_card, const HealthGraphCardConfig *config) { + if (!graph_card || !config) { + return; + } + if (config->title) { + graph_card->title = i18n_get(config->title, graph_card); + } + if (config->info_avg) { + graph_card->info_avg = i18n_get(config->info_avg, graph_card); + } + if (config->graph_data) { + graph_card->stats = config->graph_data->stats; + memcpy(graph_card->day_data, config->graph_data->day_data, sizeof(graph_card->day_data)); + graph_card->data_timestamp = config->graph_data->timestamp; + graph_card->data_max = MAX(config->graph_data->default_max, + config->graph_data->stats.daily.max); + } + if (config->info_update) { + graph_card->info_update = config->info_update; + } + if (config->info_buffer_size) { + graph_card->info_buffer_size = config->info_buffer_size; + } + if (!gcolor_equal(config->inactive_color, GColorClear)) { + graph_card->inactive_color = config->inactive_color; + } +} + +void health_graph_card_cycle_selected(HealthGraphCard *graph_card) { + if (graph_card->selection == HealthGraphIndex_Sunday) { + // Sunday is the last day in the graph, show the average next + graph_card->selection = HealthGraphIndex_Average; + } else if (graph_card->selection == HealthGraphIndex_Average) { + // Monday is the first day in the graph, show Monday after showing the average + graph_card->selection = HealthGraphIndex_Monday; + } else { + // Otherwise progress through the weekdays normally + graph_card->selection = (graph_card->selection + 1) % DAYS_PER_WEEK; + } +} diff --git a/src/fw/apps/system/health/graph_card.h b/src/fw/apps/system/health/graph_card.h new file mode 100644 index 0000000000..a65e0338a0 --- /dev/null +++ b/src/fw/apps/system/health/graph_card.h @@ -0,0 +1,78 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/fonts/fonts.h" +#include "applib/ui/layer.h" +#include "util/time/time.h" + +typedef enum { + HealthGraphIndex_Sunday = Sunday, + HealthGraphIndex_Monday = Monday, + HealthGraphIndex_Saturday = Saturday, + HealthGraphIndex_Average = HealthGraphIndex_Sunday + DAYS_PER_WEEK, + HealthGraphIndexCount, +} HealthGraphIndex; + +typedef struct HealthGraphCard HealthGraphCard; + +typedef void (*HealthGraphCardInfoUpdate)(HealthGraphCard *graph_card, int32_t day_point, + char *buffer, size_t buffer_size); + +typedef struct { + WeeklyStats stats; + time_t timestamp; + int32_t *day_data; + int32_t default_max; +} HealthGraphCardData; + +typedef struct { + const char *title; + const char *info_avg; + const HealthGraphCardData *graph_data; + HealthGraphCardInfoUpdate info_update; + size_t info_buffer_size; + const GColor inactive_color; +} HealthGraphCardConfig; + +struct HealthGraphCard { + Layer layer; + + WeeklyStats stats; + //! Today is 0. Save up to and including last week's day of the same week day + int32_t day_data[DAYS_PER_WEEK + 1]; + time_t data_timestamp; //!< Time at which the data applies in UTC seconds + int32_t data_max; + + GFont title_font; + GFont legend_font; + const char *day_chars; + const char *title; + const char *info_avg; + GColor inactive_color; + + HealthGraphCardInfoUpdate info_update; + size_t info_buffer_size; + + uint8_t current_day; //!< Current weekday (weekend inclusive) where Sunday is first at 0 + HealthGraphIndex selection; +}; + +//! Creates a HealthGraphCard +HealthGraphCard *health_graph_card_create(const HealthGraphCardConfig *config); + +//! Destroys a HealthGraphCard +void health_graph_card_destroy(HealthGraphCard *graph_card); + +//! Configures a HealthGraphCard +void health_graph_card_configure(HealthGraphCard *graph_card, const HealthGraphCardConfig *config); + +//! Cycles the HealthGraphCard selection +void health_graph_card_cycle_selected(HealthGraphCard *graph_card); + +//! Formats a string with a prefix of the current weekday selection +size_t health_graph_format_weekday_prefix(HealthGraphCard *graph_card, char *buffer, + size_t buffer_size); diff --git a/src/fw/apps/system/health/health.c b/src/fw/apps/system/health/health.c new file mode 100644 index 0000000000..2007489785 --- /dev/null +++ b/src/fw/apps/system/health/health.c @@ -0,0 +1,193 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "health.h" +#include "card_view.h" +#include "data.h" + +#include "applib/app.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/modals/modal_manager.h" +#include "popups/health_tracking_ui.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/timeline/timeline.h" +#include "resource/resource_ids.auto.h" +#include "system/logging.h" + +// Health app versions +// 0: Invalid (app was never opened) +// 1: Initial version +// 2: Graphs moved to mobile apps +// 3: 4.0 app redesign +// 4: Added insights onboarding prompt +#define CURRENT_HEALTH_APP_VERSION 4 + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Main Structures +// + +//! Main structure for application +typedef struct HealthAppData { + HealthCardView *health_card_view; + HealthData *health_data; +} HealthAppData; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Callbacks +// + +//! Tick timer service callback +//! @param tick_time Pointer to time structure +//! @param units_changed The time units changed +static void prv_tick_timer_handler(struct tm *tick_time, TimeUnits units_changed) { + HealthAppData *health_app_data = app_state_get_user_data(); + health_data_update_step_derived_metrics(health_app_data->health_data); + health_card_view_mark_dirty(health_app_data->health_card_view); +} + +// Activity change callback +static void prv_health_service_event_handler(HealthEventType event, void *context) { + HealthAppData *health_app_data = context; + if (event == HealthEventMovementUpdate) { + const uint32_t steps_today = health_service_sum_today(HealthMetricStepCount); + health_data_update_steps(health_app_data->health_data, steps_today); + } else if (event == HealthEventSleepUpdate) { + const uint32_t seconds_sleep_today = health_service_sum_today(HealthMetricSleepSeconds); + const uint32_t seconds_restful_sleep_today = + health_service_sum_today(HealthMetricSleepRestfulSeconds); + health_data_update_sleep(health_app_data->health_data, seconds_sleep_today, + seconds_restful_sleep_today); + } else if (event == HealthEventHeartRateUpdate) { + health_data_update_current_bpm(health_app_data->health_data); + } else { + health_data_update(health_app_data->health_data); + } + health_card_view_mark_dirty(health_app_data->health_card_view); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Initialization and Termination +// + +//! Show insights onboarding dialog for first-time users +static void prv_show_insights_onboarding_dialog(void) { + /// Insights onboarding message + static const char *text = i18n_noop("Psst! Want smart tips about your activity and sleep? " + "You can enable Insights in the mobile app."); + ExpandableDialog *dialog = expandable_dialog_create_with_params( + "Insights Onboarding", + RESOURCE_ID_HEALTH_ICON_MOON, + text, + GColorBlack, + GColorWhite, + NULL, + RESOURCE_ID_ACTION_BAR_ICON_CHECK, + expandable_dialog_close_cb); + app_expandable_dialog_push(dialog); +} + +//! Initialize application +static void prv_finish_initilization_cb(bool in_focus) { + if (in_focus) { + HealthAppData *health_app_data = app_state_get_user_data(); + + tick_timer_service_subscribe(MINUTE_UNIT, prv_tick_timer_handler); + + health_service_set_heart_rate_sample_period(1 /* interval_s */); + + // Subscribing to health events causes a `HealthEventSignificantUpdate` which + // will trigger us to update our health data + health_service_events_subscribe(prv_health_service_event_handler, health_app_data); + + // Unsubscribe, we only want to do this on the initial appearance (opening the app) + app_focus_service_unsubscribe(); + } +} + +static void prv_initialize(void) { + if (!activity_prefs_tracking_is_enabled()) { + /// Health disabled text + static const char *msg = i18n_noop("Track your steps, sleep, and more!" + " Enable Pebble Health in the mobile app."); + health_tracking_ui_show_message(RESOURCE_ID_HEART_TINY, msg, true); + return; + } + + if (!activity_is_initialized()) { + /// Health waiting for time sync + static const char *msg = i18n_noop("Health requires the time to be synced." + " Please connect your phone."); + health_tracking_ui_show_message(RESOURCE_ID_ALARM_CLOCK_TINY, msg, true); + return; + } + + const uint8_t previous_version = activity_prefs_get_health_app_opened_version(); + activity_prefs_set_health_app_opened_version(CURRENT_HEALTH_APP_VERSION); + + HealthAppData *health_app_data = app_zalloc_check(sizeof(HealthAppData)); + + app_state_set_user_data(health_app_data); + + health_app_data->health_data = health_data_create(); + health_data_update_quick(health_app_data->health_data); + + health_app_data->health_card_view = health_card_view_create(health_app_data->health_data); + + health_card_view_push(health_app_data->health_card_view); + + // Show insights onboarding if user hasn't seen it yet and doesn't have insights enabled + const bool insights_enabled = activity_prefs_activity_insights_are_enabled() || + activity_prefs_sleep_insights_are_enabled(); + if (previous_version < CURRENT_HEALTH_APP_VERSION && !insights_enabled) { + prv_show_insights_onboarding_dialog(); + } + + // Finish up initializing the app a bit later. This helps reduce lag when opening the app + app_focus_service_subscribe_handlers((AppFocusHandlers){ + .did_focus = prv_finish_initilization_cb, + }); +} + +//! Terminate application +static void prv_terminate(void) { + HealthAppData *health_app_data = app_state_get_user_data(); + + // cancel explicit hr sample period + health_service_set_heart_rate_sample_period(0 /* interval_s */); + + if (health_app_data) { + health_card_view_destroy(health_app_data->health_card_view); + + health_data_destroy(health_app_data->health_data); + + app_free(health_app_data); + } +} + +//! Main entry point +static void prv_main(void) { + prv_initialize(); + app_event_loop(); + prv_terminate(); +} + +const PebbleProcessMd *health_app_get_info(void) { + static const PebbleProcessMdSystem s_health_app_info = { + .common = { + .main_func = &prv_main, + .uuid = UUID_HEALTH_DATA_SOURCE, +#if CAPABILITY_HAS_CORE_NAVIGATION4 + .visibility = ProcessVisibilityHidden, +#endif + }, + .icon_resource_id = RESOURCE_ID_MENU_ICON_HEALTH, + .name = i18n_noop("Health"), + }; + return (const PebbleProcessMd*) &s_health_app_info; +} diff --git a/src/fw/apps/system/health/health.h b/src/fw/apps/system/health/health.h new file mode 100644 index 0000000000..6997a2375d --- /dev/null +++ b/src/fw/apps/system/health/health.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +//! Call for system to obtain information about the application +//! @return System information about the app +const PebbleProcessMd *health_app_get_info(void); diff --git a/src/fw/apps/system/health/hr_detail_card.c b/src/fw/apps/system/health/hr_detail_card.c new file mode 100644 index 0000000000..6ef97deebb --- /dev/null +++ b/src/fw/apps/system/health/hr_detail_card.c @@ -0,0 +1,113 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "hr_detail_card.h" +#include "detail_card.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/health_util.h" + +#include + +typedef struct HealthHrDetailCard { + int16_t num_headings; + HealthDetailHeading headings[MAX_NUM_HEADINGS]; + + int16_t num_zones; + HealthDetailZone zones[MAX_NUM_ZONES]; +} HealthHrDetailCardData; + +#define DEFAULT_MAX_PROGRESS (10 * SECONDS_PER_MINUTE) + +static void prv_set_zone(HealthDetailZone *zone, int32_t minutes, int32_t *max_progress, + const size_t buffer_size, const char *zone_label, void *i18n_owner) { + *zone = (HealthDetailZone) { + .label = app_zalloc_check(buffer_size), + .progress = minutes * SECONDS_PER_MINUTE, + .fill_color = PBL_IF_COLOR_ELSE(GColorSunsetOrange, GColorDarkGray), + }; + + int pos = snprintf(zone->label, buffer_size, "%s ", i18n_get(zone_label, i18n_owner)); + if (zone->progress) { + health_util_format_hours_and_minutes(zone->label + pos, buffer_size - pos, + zone->progress, i18n_owner); + } + + if (zone->progress > *max_progress) { + *max_progress = zone->progress; + } +} + +static void prv_set_heading_value(char *buffer, const size_t buffer_size, const int32_t zone_time_s, + void *i18n_owner) { + if (zone_time_s == 0) { + strncpy(buffer, EN_DASH, buffer_size); + return; + } + + health_util_format_hours_and_minutes(buffer, buffer_size, zone_time_s, i18n_owner); +} + +Window *health_hr_detail_card_create(HealthData *health_data) { + HealthHrDetailCardData *card_data = app_zalloc_check(sizeof(HealthHrDetailCardData)); + + const int32_t zone1_minutes = health_data_hr_get_zone1_minutes(health_data); + const int32_t zone2_minutes = health_data_hr_get_zone2_minutes(health_data); + const int32_t zone3_minutes = health_data_hr_get_zone3_minutes(health_data); + const int32_t zone_time_minutes = zone1_minutes + zone2_minutes + zone3_minutes; + + int32_t max_progress = DEFAULT_MAX_PROGRESS; + + const size_t buffer_size = 32; + + prv_set_zone(&card_data->zones[card_data->num_zones++], zone1_minutes, &max_progress, buffer_size, + i18n_noop("Fat Burn"), card_data); + + prv_set_zone(&card_data->zones[card_data->num_zones++], zone2_minutes, &max_progress, buffer_size, + i18n_noop("Endurance"), card_data); + + prv_set_zone(&card_data->zones[card_data->num_zones++], zone3_minutes, &max_progress, buffer_size, + i18n_noop("Performance"), card_data); + + HealthDetailHeading *heading = &card_data->headings[card_data->num_headings++]; + + *heading = (HealthDetailHeading) { + /// Resting HR + .primary_label = (char *)i18n_get("TIME IN ZONES", card_data), + .primary_value = app_zalloc_check(buffer_size), + .fill_color = PBL_IF_COLOR_ELSE(GColorDarkCandyAppleRed, GColorWhite), + .outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack), + }; + + prv_set_heading_value(heading->primary_value, buffer_size, + (zone_time_minutes * SECONDS_PER_MINUTE), card_data); + + const HealthDetailCardConfig config = { + .num_headings = card_data->num_headings, + .headings = card_data->headings, + .weekly_max = max_progress, + .bg_color = GColorWhite, + .num_zones = card_data->num_zones, + .zones = card_data->zones, + .data = card_data, + }; + + return (Window *)health_detail_card_create(&config); +} + +void health_hr_detail_card_destroy(Window *window) { + HealthDetailCard *card = (HealthDetailCard *)window; + HealthHrDetailCardData *card_data = card->data; + for (int i = 0; i < card_data->num_headings; i++) { + app_free(card_data->headings[i].primary_value); + } + for (int i = 0; i < card_data->num_zones; i++) { + app_free(card_data->zones[i].label); + } + i18n_free_all(card_data); + app_free(card_data); + health_detail_card_destroy(card); +} diff --git a/src/fw/apps/system/health/hr_detail_card.h b/src/fw/apps/system/health/hr_detail_card.h new file mode 100644 index 0000000000..aaa4e3c347 --- /dev/null +++ b/src/fw/apps/system/health/hr_detail_card.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + +//! Creates a health hr detail window +//! @param HealthData pointer to the health data to be given to this card +//! @return A pointer to a newly allocated health hr detail window +Window *health_hr_detail_card_create(HealthData *health_data); + +//! Destroys a health hr detail window +//! @param window Window pointer to health hr detail window +void health_hr_detail_card_destroy(Window *window); diff --git a/src/fw/apps/system/health/hr_summary_card.c b/src/fw/apps/system/health/hr_summary_card.c new file mode 100644 index 0000000000..9b9338ff91 --- /dev/null +++ b/src/fw/apps/system/health/hr_summary_card.c @@ -0,0 +1,251 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "hr_summary_card.h" +#include "hr_summary_card_segments.h" +#include "hr_detail_card.h" +#include "progress.h" +#include "pbl/services/activity/health_util.h" + +#include "applib/pbl_std/pbl_std.h" +#include "applib/ui/kino/kino_reel.h" +#include "applib/ui/text_layer.h" +#include "apps/system/timeline/text_node.h" +#include "board/display.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "system/logging.h" +#include "util/size.h" +#include "util/string.h" + +// Compile-time display offset calculations +#define HEALTH_X_OFFSET ((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) +#define HEALTH_Y_OFFSET ((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + +typedef struct HealthHrSummaryCardData { + HealthData *health_data; + HealthProgressBar progress_bar; + GDrawCommandSequence *pulsing_heart; + uint32_t pulsing_heart_frame_index; + AppTimer *pulsing_heart_timer; + uint32_t num_heart_beats; + uint32_t now_bpm; + uint32_t resting_bpm; + time_t last_updated; + GFont bpm_font; + GFont timestamp_font; + GFont units_font; +} HealthHrSummaryCardData; + +#define PULSING_HEART_TIMEOUT (30 * MS_PER_SECOND) + +#define PROGRESS_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorDarkCandyAppleRed, GColorBlack)) +#define PROGRESS_OUTLINE_COLOR (PBL_IF_COLOR_ELSE(GColorClear, GColorBlack)) + +#define TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorBulgarianRose, GColorBlack)) +#define CARD_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorWhite, GColorWhite)) + + +static void prv_pulsing_heart_timer_cb(void *context) { + Layer *base_layer = context; + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + const uint32_t duration = gdraw_command_sequence_get_total_duration(data->pulsing_heart); + const uint32_t num_frames = gdraw_command_sequence_get_num_frames(data->pulsing_heart); + const uint32_t timer_duration = duration / num_frames; + const uint32_t max_heart_beats = PULSING_HEART_TIMEOUT / duration; + + data->pulsing_heart_frame_index++; + + if (data->pulsing_heart_frame_index >= num_frames) { + data->pulsing_heart_frame_index = 0; + data->num_heart_beats++; + } + + if (data->num_heart_beats < max_heart_beats) { + data->pulsing_heart_timer = app_timer_register(timer_duration, + prv_pulsing_heart_timer_cb, + base_layer); + } + + layer_mark_dirty(base_layer); +} + +static void prv_render_progress_bar(GContext *ctx, Layer *base_layer) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_BACKGROUND_COLOR, + 0, HEALTH_PROGRESS_BAR_MAX_VALUE); +} + +static void prv_render_icon(GContext *ctx, Layer *base_layer) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + GDrawCommandFrame *frame = gdraw_command_sequence_get_frame_by_index( + data->pulsing_heart, data->pulsing_heart_frame_index); + if (frame) { + const GPoint offset = GPoint(-1 + HEALTH_X_OFFSET, -23 + HEALTH_Y_OFFSET); + gdraw_command_frame_draw(ctx, data->pulsing_heart, frame, offset); + return; + } +} + +static void prv_render_bpm(GContext *ctx, Layer *base_layer) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + const int units_offset_y = + fonts_get_font_height(data->bpm_font) - fonts_get_font_height(data->units_font); + + GTextNodeHorizontal *horiz_container = graphics_text_node_create_horizontal(MAX_TEXT_NODES); + GTextNodeContainer *container = &horiz_container->container; + horiz_container->horizontal_alignment = GTextAlignmentCenter; + + if (data->now_bpm == 0) { + health_util_create_text_node_with_text( + EM_DASH, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), TEXT_COLOR, container); + } else { + const size_t bpm_size = sizeof("000"); + + GTextNodeText *number_text_node = + health_util_create_text_node(bpm_size, data->bpm_font, TEXT_COLOR, container); + snprintf((char *)number_text_node->text, bpm_size, "%"PRIu32, data->now_bpm); + + GTextNodeText *units_text_node = health_util_create_text_node_with_text( + i18n_get("BPM", base_layer), data->units_font, TEXT_COLOR, container); + units_text_node->node.offset.x += 2; + units_text_node->node.offset.y = units_offset_y; + } + + const int offset_y = PBL_IF_RECT_ELSE(101, 109) + HEALTH_Y_OFFSET; + + graphics_text_node_draw(&container->node, ctx, + &GRect(0, offset_y, base_layer->bounds.size.w, + fonts_get_font_height(data->bpm_font)), NULL, NULL); + graphics_text_node_destroy(&container->node); +} + +static void prv_render_timstamp(GContext *ctx, Layer *base_layer) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + if (data->last_updated <= 0 || data->now_bpm == 0) { + return; + } + + const size_t buffer_size = 32; + char buffer[buffer_size]; + + clock_get_until_time_without_fulltime(buffer, buffer_size, data->last_updated, HOURS_PER_DAY); + + const int y = PBL_IF_RECT_ELSE(130, 136) + HEALTH_Y_OFFSET; + GRect rect = GRect(0, y, base_layer->bounds.size.w, 35); +#if PBL_RECT + rect = grect_inset(rect, GEdgeInsets(0, 18)); +#endif + + graphics_context_set_text_color(ctx, TEXT_COLOR); + graphics_draw_text(ctx, buffer, data->timestamp_font, + rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); +} + +static void prv_render_hrm_disabled(GContext *ctx, Layer *base_layer) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + const int y = PBL_IF_RECT_ELSE(100, 109) + HEALTH_Y_OFFSET; + + GRect rect = GRect(0, y, base_layer->bounds.size.w, 52); + + /// HRM disabled + const char *text = i18n_get("Enable heart rate monitoring in the mobile app", base_layer); + + graphics_context_set_text_color(ctx, TEXT_COLOR); + graphics_draw_text(ctx, text, data->timestamp_font, + rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); +} + +static void prv_base_layer_update_proc(Layer *base_layer, GContext *ctx) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + + data->now_bpm = health_data_hr_get_current_bpm(data->health_data); + data->last_updated = health_data_hr_get_last_updated_timestamp(data->health_data); + + prv_render_icon(ctx, base_layer); + + prv_render_progress_bar(ctx, base_layer); + + if (!activity_prefs_heart_rate_is_enabled()) { + prv_render_hrm_disabled(ctx, base_layer); + return; + } + + prv_render_bpm(ctx, base_layer); + + prv_render_timstamp(ctx, base_layer); +} + +static void prv_hr_detail_card_unload_callback(Window *window) { + health_hr_detail_card_destroy(window); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +Layer *health_hr_summary_card_create(HealthData *health_data) { + // create base layer + Layer *base_layer = layer_create_with_data(GRectZero, sizeof(HealthHrSummaryCardData)); + HealthHrSummaryCardData *data = layer_get_data(base_layer); + layer_set_update_proc(base_layer, prv_base_layer_update_proc); + // set health data + *data = (HealthHrSummaryCardData) { + .health_data = health_data, + .pulsing_heart = + gdraw_command_sequence_create_with_resource(RESOURCE_ID_HEALTH_APP_PULSING_HEART), + .progress_bar = { + .num_segments = ARRAY_LENGTH(s_hr_summary_progress_segments), + .segments = s_hr_summary_progress_segments, + }, + .now_bpm = health_data_hr_get_current_bpm(health_data), + .resting_bpm = health_data_hr_get_resting_bpm(health_data), + .last_updated = health_data_hr_get_last_updated_timestamp(health_data), +#if DISP_ROWS > LEGACY_2X_DISP_ROWS + .bpm_font = fonts_get_system_font(FONT_KEY_LECO_32_BOLD_NUMBERS), + .units_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM), +#else + .bpm_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM), + .units_font = fonts_get_system_font(FONT_KEY_LECO_20_BOLD_NUMBERS), +#endif + .timestamp_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), + }; + + data->pulsing_heart_timer = app_timer_register(0, prv_pulsing_heart_timer_cb, base_layer); + + return base_layer; +} + +void health_hr_summary_card_select_click_handler(Layer *layer) { + HealthHrSummaryCardData *data = layer_get_data(layer); + HealthData *health_data = data->health_data; + Window *window = health_hr_detail_card_create(health_data); + window_set_window_handlers(window, &(WindowHandlers) { + .unload = prv_hr_detail_card_unload_callback, + }); + app_window_stack_push(window, true); +} + +void health_hr_summary_card_destroy(Layer *base_layer) { + HealthHrSummaryCardData *data = layer_get_data(base_layer); + app_timer_cancel(data->pulsing_heart_timer); + gdraw_command_sequence_destroy(data->pulsing_heart); + i18n_free_all(base_layer); + layer_destroy(base_layer); +} + +GColor health_hr_summary_card_get_bg_color(Layer *layer) { + return CARD_BACKGROUND_COLOR; +} + +bool health_hr_summary_show_select_indicator(Layer *layer) { + return true; +} diff --git a/src/fw/apps/system/health/hr_summary_card.h b/src/fw/apps/system/health/hr_summary_card.h new file mode 100644 index 0000000000..df58d9d328 --- /dev/null +++ b/src/fw/apps/system/health/hr_summary_card.h @@ -0,0 +1,32 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +//! Creates hr summary card with data +//! @param health_data A pointer to the health data being given this card +//! @return A pointer to a newly allocated layer, which contains its own data +Layer *health_hr_summary_card_create(HealthData *health_data); + +//! Health hr summary select click handler +//! @param layer A pointer to an existing layer containing its own data +void health_hr_summary_card_select_click_handler(Layer *layer); + +//! Destroy hr summary card +//! @param base_layer A pointer to an existing layer containing its own data +void health_hr_summary_card_destroy(Layer *base_layer); + +//! Health hr summary layer background color getter +GColor health_hr_summary_card_get_bg_color(Layer *layer); + +//! Health hr summary layer should show select click indicator +bool health_hr_summary_show_select_indicator(Layer *layer); diff --git a/src/fw/apps/system/health/hr_summary_card_segments.h b/src/fw/apps/system/health/hr_summary_card_segments.h new file mode 100644 index 0000000000..7c116b2516 --- /dev/null +++ b/src/fw/apps/system/health/hr_summary_card_segments.h @@ -0,0 +1,72 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "board/display.h" +#include "progress.h" + +//! 4 main segments + 4 real corners +//! Each of the 4 non-corener segments get 25% of the total +#define AMOUNT_PER_SEGMENT (HEALTH_PROGRESS_BAR_MAX_VALUE * 25 / 100) + +// The shape is the same, but the offsets are different +// Dynamically center based on display size vs legacy 144x168 base +// Round displays need additional adjustment for the bezel +#define X_SHIFT (((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) + PBL_IF_ROUND_ELSE(18, 0)) +#define Y_SHIFT (((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + PBL_IF_ROUND_ELSE(6, 0)) + +static HealthProgressSegment s_hr_summary_progress_segments[] = { + { + // Bottom corner + .type = HealthProgressSegmentType_Corner, + .points = {{72 + X_SHIFT, 87 + Y_SHIFT}, {65 + X_SHIFT, 94 + Y_SHIFT}, + {72 + X_SHIFT, 101 + Y_SHIFT}, {79 + X_SHIFT, 94 + Y_SHIFT}}, + }, + { + // Left side bottom + .amount_of_total = AMOUNT_PER_SEGMENT, + .type = HealthProgressSegmentType_Vertical, + .points = {{66 + X_SHIFT, 95 + Y_SHIFT}, {73 + X_SHIFT, 88 + Y_SHIFT}, + {42 + X_SHIFT, 57 + Y_SHIFT}, {35 + X_SHIFT, 64 + Y_SHIFT}}, + }, + { + // Left corner + .type = HealthProgressSegmentType_Corner, + .points = {{43 + X_SHIFT, 58 + Y_SHIFT}, {36 + X_SHIFT, 51 + Y_SHIFT}, + {29 + X_SHIFT, 58 + Y_SHIFT}, {36 + X_SHIFT, 65 + Y_SHIFT}}, + }, + { + // Left side top + .amount_of_total = AMOUNT_PER_SEGMENT, + .type = HealthProgressSegmentType_Vertical, + .points = {{35 + X_SHIFT, 52 + Y_SHIFT}, {42 + X_SHIFT, 59 + Y_SHIFT}, + {73 + X_SHIFT, 28 + Y_SHIFT}, {66 + X_SHIFT, 21 + Y_SHIFT}}, + }, + { + // Top corner + .type = HealthProgressSegmentType_Corner, + .points = {{72 + X_SHIFT, 29 + Y_SHIFT}, {79 + X_SHIFT, 22 + Y_SHIFT}, + {72 + X_SHIFT, 15 + Y_SHIFT}, {65 + X_SHIFT, 22 + Y_SHIFT}}, + }, + { + // Right side top + .amount_of_total = AMOUNT_PER_SEGMENT, + .type = HealthProgressSegmentType_Vertical, + .points = {{78 + X_SHIFT, 21 + Y_SHIFT}, {71 + X_SHIFT, 28 + Y_SHIFT}, + {102 + X_SHIFT, 59 + Y_SHIFT}, {109 + X_SHIFT, 52 + Y_SHIFT}}, + }, + { + // Right corner + .type = HealthProgressSegmentType_Corner, + .points = {{101 + X_SHIFT, 58 + Y_SHIFT}, {108 + X_SHIFT, 65 + Y_SHIFT}, + {115 + X_SHIFT, 58 + Y_SHIFT}, {108 + X_SHIFT, 51 + Y_SHIFT}}, + }, + { + // Right side bottom + .amount_of_total = AMOUNT_PER_SEGMENT, + .type = HealthProgressSegmentType_Vertical, + .points = {{102 + X_SHIFT, 57 + Y_SHIFT}, {109 + X_SHIFT, 64 + Y_SHIFT}, + {78 + X_SHIFT, 95 + Y_SHIFT}, {71 + X_SHIFT, 88 + Y_SHIFT}}, + }, +}; diff --git a/src/fw/apps/system/health/progress.c b/src/fw/apps/system/health/progress.c new file mode 100644 index 0000000000..840c4df840 --- /dev/null +++ b/src/fw/apps/system/health/progress.c @@ -0,0 +1,170 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "progress.h" + +#include "applib/graphics/gpath_builder.h" + +#include "system/logging.h" + +// Scales a total shape offset to an individual segment offset. +// @param total_offset should not be larger than the segment's percent of total +static int prv_total_offset_to_segment_offset(HealthProgressSegment *segment, + int total_offset) { + return total_offset * HEALTH_PROGRESS_BAR_MAX_VALUE / segment->amount_of_total; +} + +static bool prv_is_segment_corner(HealthProgressSegment *segment) { + return (segment->type == HealthProgressSegmentType_Corner); +} + +static GPointPrecise prv_get_adjusted_gpoint_precise_from_gpoint(GPoint point) { + GPointPrecise pointP = GPointPreciseFromGPoint(point); + // Hack to make it draw 2px lines on b/w + // Note that this shifts it down and to the right, but it works for us. + pointP.x.fraction += FIXED_S16_3_HALF.raw_value; + pointP.y.fraction += FIXED_S16_3_HALF.raw_value; + return pointP; +} + +static GPoint prv_get_point_between_points(GPoint p1, GPoint p2, HealthProgressBarValue val) { + int x = p1.x + ((p2.x - p1.x) * val / HEALTH_PROGRESS_BAR_MAX_VALUE); + int y = p1.y + ((p2.y - p1.y) * val / HEALTH_PROGRESS_BAR_MAX_VALUE); + + return GPoint(x, y); +} + +static void prv_fill_segment(GContext *ctx, HealthProgressSegment *segment, GColor color, + HealthProgressBarValue start, HealthProgressBarValue end) { + GPoint p1, p2, p3, p4; + if (segment->type == HealthProgressSegmentType_Vertical) { + p1 = prv_get_point_between_points(segment->points[0], segment->points[3], start); + p2 = prv_get_point_between_points(segment->points[1], segment->points[2], start); + p3 = prv_get_point_between_points(segment->points[1], segment->points[2], end); + p4 = prv_get_point_between_points(segment->points[0], segment->points[3], end); + } else if (segment->type == HealthProgressSegmentType_Horizontal) { + p1 = prv_get_point_between_points(segment->points[0], segment->points[1], start); + p2 = prv_get_point_between_points(segment->points[3], segment->points[2], start); + p3 = prv_get_point_between_points(segment->points[3], segment->points[2], end); + p4 = prv_get_point_between_points(segment->points[0], segment->points[1], end); + } else { + p1 = segment->points[0]; + p2 = segment->points[1]; + p3 = segment->points[2]; + p4 = segment->points[3]; + } + + GPathBuilder *builder = gpath_builder_create(5); + gpath_builder_move_to_point(builder, p1); + gpath_builder_line_to_point(builder, p2); + gpath_builder_line_to_point(builder, p3); + gpath_builder_line_to_point(builder, p4); + GPath *path = gpath_builder_create_path(builder); + gpath_builder_destroy(builder); + + graphics_context_set_fill_color(ctx, color); + gpath_draw_filled(ctx, path); + gpath_destroy(path); +} + +void health_progress_bar_fill(GContext *ctx, HealthProgressBar *progress_bar, GColor color, + HealthProgressBarValue start, HealthProgressBarValue end) { + if (start < 0) { + // This ensures we don't deal with negative values + start += HEALTH_PROGRESS_BAR_MAX_VALUE; + } + if (start > end) { + // This ensures the end is always after the start + end += HEALTH_PROGRESS_BAR_MAX_VALUE; + } + + int amount_traversed = 0; + HealthProgressSegment *segment = progress_bar->segments; + while (start >= amount_traversed + segment->amount_of_total) { + // Skip until the segment which includes the start + amount_traversed += segment->amount_of_total; + segment++; + } + + if (prv_is_segment_corner(segment)) { + segment++; + } + + while (amount_traversed < end) { + if (prv_is_segment_corner(segment)) { + // Fully fill corner segments for now + prv_fill_segment(ctx, segment, color, 0, HEALTH_PROGRESS_BAR_MAX_VALUE); + segment++; + continue; + } + + const int from_total = MAX(start, amount_traversed) - amount_traversed; + const int to_total = MIN(end, amount_traversed + segment->amount_of_total) - amount_traversed; + + const int from = prv_total_offset_to_segment_offset(segment, from_total); + const int to = prv_total_offset_to_segment_offset(segment, to_total); + + prv_fill_segment(ctx, segment, color, from, to); + + amount_traversed += segment->amount_of_total; + + if (segment == &progress_bar->segments[progress_bar->num_segments - 1]) { + // We are on the last segment, wrap back to the first + segment = &progress_bar->segments[0]; + } else { + segment++; + } + } +} + +void health_progress_bar_mark(GContext *ctx, HealthProgressBar *progress_bar, GColor color, + HealthProgressBarValue value_to_mark) { + if (value_to_mark < 0) { + // This ensures we don't deal with negative values + value_to_mark += HEALTH_PROGRESS_BAR_MAX_VALUE; + } + + HealthProgressSegment *segment = progress_bar->segments; + while (value_to_mark > segment->amount_of_total) { + value_to_mark -= segment->amount_of_total; + segment++; + } + + if (prv_is_segment_corner(segment)) { + segment++; + } + + const int from = prv_total_offset_to_segment_offset(segment, value_to_mark); + + // Fill backwards if we can, otherwise forwards + const int dir = value_to_mark - segment->mark_width < 0 ? 1 : -1; + const int to_total = value_to_mark + (dir * segment->mark_width); + const int to = prv_total_offset_to_segment_offset(segment, to_total); + + prv_fill_segment(ctx, segment, color, from, to); +} + +void health_progress_bar_outline(GContext *ctx, HealthProgressBar *progress_bar, GColor color) { + graphics_context_set_stroke_color(ctx, color); + graphics_context_set_stroke_width(ctx, 2); + + for (int i = 0; i < progress_bar->num_segments; i++) { + HealthProgressSegment *segment = &progress_bar->segments[i]; + + GPointPrecise p0 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[0]); + GPointPrecise p1 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[1]); + GPointPrecise p2 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[2]); + GPointPrecise p3 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[3]); + + if (segment->type == HealthProgressSegmentType_Vertical) { + graphics_line_draw_precise_stroked(ctx, p0, p3); + graphics_line_draw_precise_stroked(ctx, p1, p2); + } else if (segment->type == HealthProgressSegmentType_Horizontal) { + graphics_line_draw_precise_stroked(ctx, p0, p1); + graphics_line_draw_precise_stroked(ctx, p2, p3); + } else { + graphics_line_draw_precise_stroked(ctx, p1, p2); + graphics_line_draw_precise_stroked(ctx, p2, p3); + } + } +} diff --git a/src/fw/apps/system/health/progress.h b/src/fw/apps/system/health/progress.h new file mode 100644 index 0000000000..4070b68909 --- /dev/null +++ b/src/fw/apps/system/health/progress.h @@ -0,0 +1,40 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/ui.h" + +#define HEALTH_PROGRESS_BAR_MAX_VALUE 0xfff + +typedef int32_t HealthProgressBarValue; + +typedef enum HealthProgressSegmentType { + HealthProgressSegmentType_Horizontal, + HealthProgressSegmentType_Vertical, + HealthProgressSegmentType_Corner, + HealthProgressSegmentTypeCount, +} HealthProgressSegmentType; + +typedef struct HealthProgressSegment { + HealthProgressSegmentType type; + // The amount of the total progress bar that this segment occupies. + // Summing this value over all segments should total HEALTH_PROGRESS_BAR_MAX_VALUE + int amount_of_total; + int mark_width; + GPoint points[4]; +} HealthProgressSegment; + +typedef struct HealthProgressBar { + int num_segments; + HealthProgressSegment *segments; +} HealthProgressBar; + + +void health_progress_bar_fill(GContext *ctx, HealthProgressBar *progress_bar, GColor color, + HealthProgressBarValue start, HealthProgressBarValue end); + +void health_progress_bar_mark(GContext *ctx, HealthProgressBar *progress_bar, GColor color, + HealthProgressBarValue value_to_mark); + +void health_progress_bar_outline(GContext *ctx, HealthProgressBar *progress_bar, GColor color); diff --git a/src/fw/apps/system/health/sleep_detail_card.c b/src/fw/apps/system/health/sleep_detail_card.c new file mode 100644 index 0000000000..dcd42f61c3 --- /dev/null +++ b/src/fw/apps/system/health/sleep_detail_card.c @@ -0,0 +1,168 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "sleep_detail_card.h" +#include "detail_card.h" +#include "pbl/services/activity/health_util.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" + +#include + +typedef struct HealthSleepDetailCard { + int32_t daily_avg; + int32_t weekly_max; + + int16_t num_headings; + HealthDetailHeading headings[MAX_NUM_HEADINGS]; + + int16_t num_subtitles; + HealthDetailSubtitle subtitles[MAX_NUM_SUBTITLES]; + + int16_t num_zones; + HealthDetailZone zones[MAX_NUM_ZONES]; +} HealthSleepDetailCardData; + +static void prv_set_sleep_session(char *buffer, size_t buffer_size, int32_t sleep_start, + int32_t sleep_end) { + // We don't have a sleep session if either start or end time is not greater than 0. + // Sometimes if there's no sleep, sleep session rendered as "16:00 - 16:00" so for a quick + // fix, we assume there's no sleep if the start and end times are the same. + // https://pebbletechnology.atlassian.net/browse/PBL-40031 + if (sleep_start <= 0 || sleep_end <= 0 || sleep_start == sleep_end) { + strncpy(buffer, EN_DASH, buffer_size); + return; + } + + const int start_hours = sleep_start / SECONDS_PER_HOUR; + const int start_minutes = (sleep_start % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE; + const int end_hours = sleep_end / SECONDS_PER_HOUR; + const int end_minutes = (sleep_end % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE; + + int pos = 0; + + pos += clock_format_time(buffer + pos, buffer_size - pos, + start_hours, start_minutes, false); + + pos += snprintf(buffer + pos, buffer_size - pos, " %s ", "-"); + + pos += clock_format_time(buffer + pos, buffer_size - pos, + end_hours, end_minutes, false); +} + +static void prv_set_deep_sleep(char *buffer, size_t buffer_size, int32_t sleep_duration, + void *i18n_owner) { + if (sleep_duration <= 0) { + strncpy(buffer, EN_DASH, buffer_size); + return; + } + + health_util_format_hours_and_minutes(buffer, buffer_size, sleep_duration, i18n_owner); +} + +static void prv_set_avg(char *buffer, size_t buffer_size, int32_t daily_avg, void *i18n_owner) { +#if PBL_ROUND + int avg_len = snprintf(buffer, buffer_size, "%s\n", i18n_get("30 DAY AVG", i18n_owner)); +#else + int avg_len = snprintf(buffer, buffer_size, "%s ", i18n_get("30 DAY", i18n_owner)); +#endif + + if (daily_avg <= 0) { + strncpy(buffer + avg_len, EN_DASH, buffer_size - avg_len); + } else { + health_util_format_hours_and_minutes(buffer + avg_len, buffer_size - avg_len, + daily_avg, i18n_owner); + } +} + +Window *health_sleep_detail_card_create(HealthData *health_data) { + HealthSleepDetailCardData *card_data = app_zalloc_check(sizeof(HealthSleepDetailCardData)); + + card_data->daily_avg = health_data_sleep_get_monthly_average(health_data); + + const GColor fill_color = PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorDarkGray); + const GColor today_fill_color = PBL_IF_COLOR_ELSE(GColorElectricBlue, GColorDarkGray); + + health_detail_card_set_render_day_zones(card_data->zones, + &card_data->num_zones, + &card_data->weekly_max, + true /* format hours and minutes */, + false /* show crown */, + fill_color, + today_fill_color, + health_data_sleep_get(health_data), + card_data); + + const size_t buffer_len = 32; + + HealthDetailHeading *heading = &card_data->headings[card_data->num_headings++]; + + *heading = (HealthDetailHeading) { + .primary_label = (char *)i18n_get("SLEEP SESSION", card_data), + .primary_value = app_zalloc_check(buffer_len), + .fill_color = GColorWhite, + .outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack), + }; + + prv_set_sleep_session(heading->primary_value, buffer_len, + health_data_sleep_get_start_time(health_data), + health_data_sleep_get_end_time(health_data)); + + heading = &card_data->headings[card_data->num_headings++]; + + *heading = (HealthDetailHeading) { + .primary_label = (char *)i18n_get("DEEP SLEEP", card_data), + .primary_value = app_zalloc_check(buffer_len), + .fill_color = PBL_IF_COLOR_ELSE(GColorCobaltBlue, GColorWhite), +#if PBL_BW + .outline_color = GColorBlack, +#endif + }; + + prv_set_deep_sleep(heading->primary_value, buffer_len, + health_data_current_deep_sleep_get(health_data), card_data); + + HealthDetailSubtitle *subtitle = &card_data->subtitles[card_data->num_subtitles++]; + + *subtitle = (HealthDetailSubtitle) { + .label = app_zalloc_check(buffer_len), + .fill_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack), + }; + + prv_set_avg(subtitle->label, buffer_len, card_data->daily_avg, card_data); + + const HealthDetailCardConfig config = { + .num_headings = card_data->num_headings, + .headings = card_data->headings, + .num_subtitles = card_data->num_subtitles, + .subtitles = card_data->subtitles, + .daily_avg = card_data->daily_avg, + .weekly_max = card_data->weekly_max, + .bg_color = PBL_IF_COLOR_ELSE(GColorOxfordBlue, GColorWhite), + .num_zones = card_data->num_zones, + .zones = card_data->zones, + .data = card_data, + }; + + return (Window *)health_detail_card_create(&config); +} + +void health_sleep_detail_card_destroy(Window *window) { + HealthDetailCard *card = (HealthDetailCard *)window; + HealthSleepDetailCardData *card_data = card->data; + for (int i = 0; i < card_data->num_headings; i++) { + app_free(card_data->headings[i].primary_value); + app_free(card_data->headings[i].secondary_value); + } + for (int i = 0; i < card_data->num_subtitles; i++) { + app_free(card_data->subtitles[i].label); + } + for (int i = 0; i < card_data->num_zones; i++) { + app_free(card_data->zones[i].label); + } + i18n_free_all(card_data); + app_free(card_data); + health_detail_card_destroy(card); +} diff --git a/src/fw/apps/system/health/sleep_detail_card.h b/src/fw/apps/system/health/sleep_detail_card.h new file mode 100644 index 0000000000..33088982eb --- /dev/null +++ b/src/fw/apps/system/health/sleep_detail_card.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + +//! Creates a health sleep detail window +//! @param HealthData pointer to the health data to be given to this card +//! @return A pointer to a newly allocated health sleep detail window +Window *health_sleep_detail_card_create(HealthData *health_data); + +//! Destroys a health sleep detail window +//! @param window Window pointer to health sleep detail window +void health_sleep_detail_card_destroy(Window *window); diff --git a/src/fw/apps/system/health/sleep_summary_card.c b/src/fw/apps/system/health/sleep_summary_card.c new file mode 100644 index 0000000000..7b420d53a6 --- /dev/null +++ b/src/fw/apps/system/health/sleep_summary_card.c @@ -0,0 +1,303 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "sleep_summary_card.h" +#include "sleep_summary_card_segments.h" +#include "sleep_detail_card.h" +#include "progress.h" +#include "ui.h" +#include "pbl/services/activity/health_util.h" + +#include "applib/pbl_std/pbl_std.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/text_layer.h" +#include "board/display.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "system/logging.h" +#include "util/size.h" +#include "util/string.h" + +// Compile-time display offset calculations +#define HEALTH_X_OFFSET ((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) +#define HEALTH_Y_OFFSET ((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + +typedef struct HealthSleepSummaryCardData { + HealthData *health_data; + HealthProgressBar progress_bar; + KinoReel *icon; + + GFont number_font; + GFont unit_font; + GFont typical_font; + GFont em_dash_font; +} HealthSleepSummaryCardData; + +#define PROGRESS_CURRENT_COLOR (PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorDarkGray)) +#define PROGRESS_SECONDARY_COLOR (PBL_IF_COLOR_ELSE(GColorVeryLightBlue, GColorBlack)) +#define PROGRESS_TYPICAL_COLOR (PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack)) +#define PROGRESS_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorDarkGray, GColorClear)) +#define PROGRESS_OUTLINE_COLOR (PBL_IF_COLOR_ELSE(GColorClear, GColorBlack)) + +#define CURRENT_TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorBlack)) +#define TYPICAL_TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)) +#define NO_DATA_TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)) +#define CARD_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorOxfordBlue, GColorWhite)) + +#define TWELVE_HOURS (SECONDS_PER_HOUR * 12) + + +static void prv_render_sleep_sessions(GContext *ctx, HealthSleepSummaryCardData *data) { + const int num_sessions = health_data_sleep_get_num_sessions(data->health_data); + ActivitySession *sessions = health_data_sleep_get_sessions(data->health_data); + for (int i = 0; i < num_sessions; i++) { + ActivitySession *session = &sessions[i]; + GColor fill_color = GColorClear; + + if (session->type == ActivitySessionType_Sleep) { + fill_color = PROGRESS_CURRENT_COLOR; + } else if (session->type == ActivitySessionType_RestfulSleep) { + fill_color = PROGRESS_SECONDARY_COLOR; + } + + if (gcolor_equal(fill_color, GColorClear)) { + continue; + } + + struct tm local_tm; + localtime_r(&session->start_utc, &local_tm); + + const int session_start_24h = (local_tm.tm_sec + + local_tm.tm_min * SECONDS_PER_MINUTE + + local_tm.tm_hour * SECONDS_PER_HOUR); + const int session_end_24h = session_start_24h + (session->length_min * SECONDS_PER_MINUTE); + + const int session_start_12h = session_start_24h % TWELVE_HOURS; + const int session_end_12h = session_end_24h % TWELVE_HOURS; + + const int start = (session_start_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); + const int end = (session_end_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); + + health_progress_bar_fill(ctx, &data->progress_bar, fill_color, start, end); + } +} + +static void prv_render_typical_markers(GContext *ctx, HealthSleepSummaryCardData *data) { + // Some time fuzz is applied to a couple values to ensure that typical fill touches the sleep + // sessions (needed because of how our fill algorithms work) + const int sleep_start_24h = health_data_sleep_get_start_time(data->health_data); + + const int sleep_end_24h = health_data_sleep_get_end_time(data->health_data); + + if (sleep_start_24h || sleep_end_24h) { +#if PBL_COLOR + const int time_fuzz = (2 * SECONDS_PER_MINUTE); + const int sleep_start_12h = (sleep_start_24h) % TWELVE_HOURS; + const int sleep_end_12h = (sleep_end_24h - time_fuzz) % TWELVE_HOURS; + const int sleep_start = (sleep_start_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); + const int sleep_end = (sleep_end_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); +#endif + + const int typical_sleep_start_24h = health_data_sleep_get_typical_start_time(data->health_data); + const int typical_sleep_start_12h = typical_sleep_start_24h % TWELVE_HOURS; + const int typical_sleep_end_24h = health_data_sleep_get_typical_end_time(data->health_data); + const int typical_sleep_end_12h = typical_sleep_end_24h % TWELVE_HOURS; + + const int typical_start = + (typical_sleep_start_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); + const int typical_end = + (typical_sleep_end_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); + +#if PBL_COLOR + const bool fell_asleep_late = (typical_sleep_start_24h < sleep_start_24h); + if (fell_asleep_late) { + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, + typical_start, sleep_start); + } else { + health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_start); + } + + const bool woke_up_early = (typical_sleep_end_24h > sleep_end_24h); + if (woke_up_early) { + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, + sleep_end, typical_end); + } else { + health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_end); + } +#else + health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_start); + health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_end); +#endif + } +} + +static void prv_render_progress_bar(GContext *ctx, Layer *base_layer) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + + // Renders the background + health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_BACKGROUND_COLOR, + 0, HEALTH_PROGRESS_BAR_MAX_VALUE); + + prv_render_sleep_sessions(ctx, data); + + prv_render_typical_markers(ctx, data); + + // This is required to get the rounded corners on the outside of the rectangle + graphics_context_set_stroke_width(ctx, 2); + graphics_context_set_stroke_color(ctx, CARD_BACKGROUND_COLOR); + graphics_draw_round_rect(ctx, &s_sleep_summary_masking_rect, 5); + + // This needs to be done after drawing the progress bars or else the progress fill + // overlaps the outline and things look weird + health_progress_bar_outline(ctx, &data->progress_bar, PROGRESS_OUTLINE_COLOR); +} + +static void prv_render_icon(GContext *ctx, Layer *base_layer) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + + const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(37, 32), 39) + HEALTH_Y_OFFSET; + const int x_center_offset = 17; + kino_reel_draw(data->icon, ctx, GPoint(base_layer->bounds.size.w / 2 - x_center_offset, y)); +} + +static void prv_render_current_sleep_text(GContext *ctx, Layer *base_layer) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + + const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(85, 83), 88) + HEALTH_Y_OFFSET; + const GRect rect = GRect(0, y, base_layer->bounds.size.w, 40); + + const int current_sleep = health_data_current_sleep_get(data->health_data); + if (current_sleep) { + // Draw the hours slept + GTextNodeHorizontal *horiz_container = graphics_text_node_create_horizontal(MAX_TEXT_NODES); + GTextNodeContainer *container = &horiz_container->container; + horiz_container->horizontal_alignment = GTextAlignmentCenter; + health_util_duration_to_hours_and_minutes_text_node(current_sleep, base_layer, + data->number_font, + data->unit_font, + CURRENT_TEXT_COLOR, container); + graphics_text_node_draw(&container->node, ctx, &rect, NULL, NULL); + graphics_text_node_destroy(&container->node); + } else { + char buffer[16]; + const GFont font = data->em_dash_font; + snprintf(buffer, sizeof(buffer), EM_DASH); + graphics_context_set_text_color(ctx, CURRENT_TEXT_COLOR); + graphics_draw_text(ctx, buffer, font, rect, GTextOverflowModeFill, GTextAlignmentCenter, NULL); + } +} + +static void prv_render_typical_sleep_text(GContext *ctx, Layer *base_layer) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + + const int typical_sleep = health_data_sleep_get_cur_wday_average(data->health_data); + + char sleep_text[32]; + + if (typical_sleep) { + health_util_format_hours_and_minutes(sleep_text, sizeof(sleep_text), typical_sleep, base_layer); + } else { + snprintf(sleep_text, sizeof(sleep_text), EM_DASH); + } + + health_ui_render_typical_text_box(ctx, base_layer, sleep_text); +} + +static void prv_render_no_sleep_data_text(GContext *ctx, Layer *base_layer) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + + const int y = PBL_IF_RECT_ELSE(91, 100) + HEALTH_Y_OFFSET; + const GRect rect = GRect(0, y, base_layer->bounds.size.w, 60); + + const char *text = i18n_get("No sleep data,\nwear your watch\nto sleep", base_layer); + + graphics_context_set_text_color(ctx, NO_DATA_TEXT_COLOR); + graphics_draw_text(ctx, text, data->typical_font, + rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); +} + +static bool prv_has_sleep_data(HealthData *health_data) { + // daily weekly stats doesn't include the first index so we check that separately + return health_data_current_sleep_get(health_data) || + health_data_sleep_get_monthly_average(health_data) > 0; +} + +static void prv_base_layer_update_proc(Layer *base_layer, GContext *ctx) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + + prv_render_icon(ctx, base_layer); + + prv_render_progress_bar(ctx, base_layer); + + if (!prv_has_sleep_data(data->health_data)) { + prv_render_no_sleep_data_text(ctx, base_layer); + return; + } + + prv_render_current_sleep_text(ctx, base_layer); + + prv_render_typical_sleep_text(ctx, base_layer); +} + +static void prv_sleep_detail_card_unload_callback(Window *window) { + health_sleep_detail_card_destroy(window); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +Layer *health_sleep_summary_card_create(HealthData *health_data) { + // create base layer + Layer *base_layer = layer_create_with_data(GRectZero, sizeof(HealthSleepSummaryCardData)); + HealthSleepSummaryCardData *health_sleep_summary_card_data = layer_get_data(base_layer); + layer_set_update_proc(base_layer, prv_base_layer_update_proc); + // set health data + *health_sleep_summary_card_data = (HealthSleepSummaryCardData) { + .icon = kino_reel_create_with_resource(RESOURCE_ID_HEALTH_APP_SLEEP), + .progress_bar = { + .num_segments = ARRAY_LENGTH(s_sleep_summary_progress_segments), + .segments = s_sleep_summary_progress_segments, + }, + .health_data = health_data, +#if DISP_ROWS > LEGACY_2X_DISP_ROWS + .number_font = fonts_get_system_font(FONT_KEY_LECO_32_BOLD_NUMBERS), + .unit_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM), +#else + .number_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM), + .unit_font = fonts_get_system_font(FONT_KEY_LECO_20_BOLD_NUMBERS), +#endif + .typical_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), + .em_dash_font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), + }; + return base_layer; +} + +void health_sleep_summary_card_select_click_handler(Layer *layer) { + HealthSleepSummaryCardData *health_sleep_summary_card_data = layer_get_data(layer); + HealthData *health_data = health_sleep_summary_card_data->health_data; + if (prv_has_sleep_data(health_data)) { + Window *window = health_sleep_detail_card_create(health_data); + window_set_window_handlers(window, &(WindowHandlers) { + .unload = prv_sleep_detail_card_unload_callback, + }); + app_window_stack_push(window, true); + } +} + +void health_sleep_summary_card_destroy(Layer *base_layer) { + HealthSleepSummaryCardData *data = layer_get_data(base_layer); + i18n_free_all(base_layer); + kino_reel_destroy(data->icon); + layer_destroy(base_layer); +} + +GColor health_sleep_summary_card_get_bg_color(Layer *layer) { + return CARD_BACKGROUND_COLOR; +} + +bool health_sleep_summary_show_select_indicator(Layer *layer) { + HealthSleepSummaryCardData *health_sleep_summary_card_data = layer_get_data(layer); + return prv_has_sleep_data(health_sleep_summary_card_data->health_data); +} diff --git a/src/fw/apps/system/health/sleep_summary_card.h b/src/fw/apps/system/health/sleep_summary_card.h new file mode 100644 index 0000000000..5ba99ef892 --- /dev/null +++ b/src/fw/apps/system/health/sleep_summary_card.h @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "data.h" + +#include "applib/ui/ui.h" + +typedef enum { + SleepSummaryView_Sleep, + SleepSummaryView_DeepSleep, + SleepSummaryView_EndAndWake, + SleepSummaryView_Nap, + SleepSummaryViewCount +} SleepSummaryView; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// API Functions +// + +//! Creates a layer with extra data +//! @param health_data A pointer to the health data buffer +//! @return A pointer to the newly allocated layer +Layer *health_sleep_summary_card_create(HealthData *health_data); + +//! Health activity summary select click handler +//! @param layer A pointer to an existing layer with extra data +void health_sleep_summary_card_select_click_handler(Layer *layer); + +//! Set the card to a given view +//! @param view the view type to show +void health_sleep_summary_card_set_view(Layer *layer, SleepSummaryView view); + +//! Destroy a layer with extra data +//! @param base_layer A pointer to an existing layer with extra data +void health_sleep_summary_card_destroy(Layer *base_layer); + +//! Health sleep summary layer background color getter +GColor health_sleep_summary_card_get_bg_color(Layer *layer); + +//! Health sleep summary layer should show select click indicator +bool health_sleep_summary_show_select_indicator(Layer *layer); diff --git a/src/fw/apps/system/health/sleep_summary_card_segments.h b/src/fw/apps/system/health/sleep_summary_card_segments.h new file mode 100644 index 0000000000..ce371e6ec5 --- /dev/null +++ b/src/fw/apps/system/health/sleep_summary_card_segments.h @@ -0,0 +1,125 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "board/display.h" +#include "progress.h" + +//! 5 main segments + 4 real corners +//! The top bar is split up into 2 segments (12am is the middle of the top bar) +//! Each of line gets 25% of the total (top line split into 2 segments which are 12.5% each) +#define AMOUNT_PER_SEGMENT (HEALTH_PROGRESS_BAR_MAX_VALUE * 25 / 100) + +// Found through trial and error +#define DEFAULT_MARK_WIDTH 40 + +// Dynamically center based on display size vs legacy 144x168 base +// Round displays need additional adjustment for the bezel +#define X_SHIFT (((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) + PBL_IF_ROUND_ELSE(23, PBL_IF_BW_ELSE(1, 0))) +#define Y_SHIFT (((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) + PBL_IF_ROUND_ELSE(8, PBL_IF_BW_ELSE(3, 0))) + +// Used to shrink the thinkness of the bars +#define X_SHRINK (PBL_IF_BW_ELSE(2, 0)) + +// These are used to shrink the shape for round +#define X_ADJ (PBL_IF_ROUND_ELSE(-12, PBL_IF_BW_ELSE(-3, 0))) +#define Y_ADJ (PBL_IF_ROUND_ELSE(-3, PBL_IF_BW_ELSE(1, 0))) + + +static HealthProgressSegment s_sleep_summary_progress_segments[] = { + { + // Top right + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = AMOUNT_PER_SEGMENT / 2, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{71 + X_SHIFT, 22 + Y_SHIFT}, + {116 + X_SHRINK + X_SHIFT + X_ADJ, 22 + Y_SHIFT}, + {116 + X_SHRINK + X_SHIFT + X_ADJ, 13 + Y_SHIFT}, + {71 + X_SHIFT, 13 + Y_SHIFT}}, + }, + { + // Top right corner + .type = HealthProgressSegmentType_Corner, + .points = {{115 + X_SHRINK + X_SHIFT + X_ADJ, 22 + Y_SHIFT}, + {115 + X_SHRINK + X_SHIFT + X_ADJ, 13 + Y_SHIFT}, + {127 + X_SHIFT + X_ADJ, 13 + Y_SHIFT}, + {127 + X_SHIFT + X_ADJ, 22 + Y_SHIFT}}, + }, + { + // Right + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH + 10, + .points = {{116 + X_SHRINK + X_SHIFT + X_ADJ, 23 + Y_SHIFT}, + {127 + X_SHIFT + X_ADJ, 23 + Y_SHIFT}, + {127 + X_SHIFT + X_ADJ, 73 + Y_SHIFT + Y_ADJ}, + {116 + X_SHRINK + X_SHIFT + X_ADJ, 73 + Y_SHIFT + Y_ADJ}}, + }, + { + // Bottom right corner + .type = HealthProgressSegmentType_Corner, + .points = {{115 + X_SHRINK + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, + {127 + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, + {127 + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}, + {115 + X_SHRINK + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}}, + }, + { + // Bottom + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{116 + X_SHRINK + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, + {27 + X_SHRINK + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, + {27 + X_SHRINK + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}, + {116 + X_SHRINK + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}}, + }, + { + // Bottom left corner + .type = HealthProgressSegmentType_Corner, + .points = {{29 + -X_SHRINK + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, + {17 + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, + {17 + X_SHIFT, 83 + Y_SHIFT + Y_ADJ}, + {29 + -X_SHRINK + X_SHIFT, 83 + Y_SHIFT + Y_ADJ}}, + }, + { + // Left + .type = HealthProgressSegmentType_Vertical, + .amount_of_total = AMOUNT_PER_SEGMENT, + .mark_width = DEFAULT_MARK_WIDTH, + .points = {{28 + -X_SHRINK + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, + {17 + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, + {17 + X_SHIFT, 23 + Y_SHIFT}, + {28 + -X_SHRINK + X_SHIFT, 23 + Y_SHIFT}}, + }, + { + // Top left corner + .type = HealthProgressSegmentType_Corner, + .points = {{29 + X_SHIFT, 22 + Y_SHIFT}, + {17 + X_SHIFT, 22 + Y_SHIFT}, + {17 + X_SHIFT, 13 + Y_SHIFT}, + {29 + X_SHIFT, 13 + Y_SHIFT}}, + }, + { + // Top left + .type = HealthProgressSegmentType_Horizontal, + .amount_of_total = AMOUNT_PER_SEGMENT / 2, + .mark_width = DEFAULT_MARK_WIDTH + 10, + .points = {{28 + -X_SHRINK + X_SHIFT, 22 + Y_SHIFT}, + {72 + X_SHIFT, 22 + Y_SHIFT}, + {72 + X_SHIFT, 13 + Y_SHIFT}, + {28 + -X_SHRINK + X_SHIFT, 13 + Y_SHIFT}}, + }, +}; + +#define MASKING_RECT_X_SHIFT (X_SHIFT + PBL_IF_BW_ELSE(1, 0)) +#define MASKING_RECT_Y_SHIFT (Y_SHIFT + PBL_IF_BW_ELSE(1, 0)) +#define MASKING_RECT_X_ADJ (X_ADJ + PBL_IF_BW_ELSE(-1, 0)) +#define MASKING_RECT_Y_ADJ (Y_ADJ + PBL_IF_BW_ELSE(-1, 0)) + +static const GRect s_sleep_summary_masking_rect = { + .origin.x = 16 + MASKING_RECT_X_SHIFT, + .origin.y = 11 + MASKING_RECT_Y_SHIFT, + .size.w = 113 + MASKING_RECT_X_ADJ, + .size.h = 75 + MASKING_RECT_Y_ADJ, +}; diff --git a/src/fw/apps/system/health/ui.c b/src/fw/apps/system/health/ui.c new file mode 100644 index 0000000000..cf36b0c919 --- /dev/null +++ b/src/fw/apps/system/health/ui.c @@ -0,0 +1,87 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "ui.h" + +#include "applib/pbl_std/pbl_std.h" +#include "board/display.h" +#include "pbl/services/i18n/i18n.h" +#include "util/string.h" + +// Compile-time display offset calculations +#define HEALTH_X_OFFSET ((DISP_COLS - LEGACY_2X_DISP_COLS) / 2) +#define HEALTH_Y_OFFSET ((DISP_ROWS - LEGACY_2X_DISP_ROWS) / 2) +#define HEALTH_INSET (18 + HEALTH_X_OFFSET / 5) + +void health_ui_draw_text_in_box(GContext *ctx, const char *text, const GRect drawing_bounds, + const int16_t y_offset, const GFont small_font, GColor box_color, + GColor text_color) { + const uint8_t text_height = fonts_get_font_height(small_font); + const GTextOverflowMode overflow_mode = GTextOverflowModeFill; + const GTextAlignment alignment = GTextAlignmentCenter; + + const GRect text_box = GRect(drawing_bounds.origin.x, y_offset, + drawing_bounds.size.w, text_height); + + GRect text_fill_box = text_box; + text_fill_box.size = app_graphics_text_layout_get_content_size( + text, small_font, text_box, overflow_mode, alignment); + text_fill_box.origin.x += ((drawing_bounds.size.w - text_fill_box.size.w) / 2); + + // add a 3 px border (get content size already adds 1 px) + text_fill_box = grect_inset(text_fill_box, GEdgeInsets(-2)); + + // get content size adds 5 to the height, and the y offset is too high by a px (+ the 5px) + const int height_correction = 5; + text_fill_box.size.h -= height_correction; + text_fill_box.origin.y += height_correction + 1; + + if (!gcolor_equal(box_color, GColorClear)) { + graphics_context_set_fill_color(ctx, box_color); + graphics_fill_rect(ctx, &text_fill_box); + } + + if (!gcolor_equal(text_color, GColorClear)) { + graphics_context_set_text_color(ctx, text_color); + graphics_draw_text(ctx, text, small_font, text_box, overflow_mode, alignment, NULL); + } +} + +void health_ui_render_typical_text_box(GContext *ctx, Layer *layer, const char *value_text) { + time_t now = rtc_get_time(); + struct tm time_tm; + localtime_r(&now, &time_tm); + char weekday[8]; + strftime(weekday, sizeof(weekday), "%a", &time_tm); + toupper_str(weekday); + + char typical_text[32]; + snprintf(typical_text, sizeof(typical_text), i18n_get("TYPICAL %s", layer), weekday); + + const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(122, 120), 125) + HEALTH_Y_OFFSET; + GRect rect = GRect(0, y, layer->bounds.size.w, PBL_IF_RECT_ELSE(35, 36)); +#if PBL_RECT + rect = grect_inset(rect, GEdgeInsets(0, HEALTH_INSET)); +#endif + + const GColor bg_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack); + const GColor text_color = PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite); + const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + + graphics_context_set_fill_color(ctx, bg_color); + graphics_fill_round_rect(ctx, &rect, 3, GCornersAll); + + rect.origin.y -= PBL_IF_RECT_ELSE(3, 2); + // Restrict the rect to draw one line at a time to prevent them from wrapping into each other + rect.size.h = 16; + + graphics_context_set_text_color(ctx, text_color); + + graphics_draw_text(ctx, typical_text, font, rect, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); + + rect.origin.y += 16; + + graphics_draw_text(ctx, value_text, font, rect, + GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); +} diff --git a/src/fw/apps/system/health/ui.h b/src/fw/apps/system/health/ui.h new file mode 100644 index 0000000000..21fd4a4b81 --- /dev/null +++ b/src/fw/apps/system/health/ui.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/ui.h" + +void health_ui_draw_text_in_box(GContext *ctx, const char *text, const GRect drawing_bounds, + const int16_t y_offset, const GFont small_font, GColor box_color, + GColor text_color); + +void health_ui_render_typical_text_box(GContext *ctx, Layer *layer, const char *value_text); diff --git a/src/fw/apps/system/launcher/default/app_glance.c b/src/fw/apps/system/launcher/default/app_glance.c new file mode 100644 index 0000000000..1e4716751c --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance.c @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance.h" + +#include "menu_layer.h" + +#include "applib/app_glance.h" +#include "applib/ui/kino/kino_reel_custom.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "system/passert.h" +#include "util/string.h" + +void launcher_app_glance_init(LauncherAppGlance *glance, const Uuid *uuid, KinoReel *impl, + bool should_consider_slices, + const LauncherAppGlanceHandlers *handlers) { + if (!glance || !uuid) { + return; + } + + *glance = (LauncherAppGlance) { + .uuid = *uuid, + .reel = impl, + .should_consider_slices = should_consider_slices, + }; + + if (handlers) { + glance->handlers = *handlers; + } + + launcher_app_glance_update_current_slice(glance); +} + +void launcher_app_glance_update_current_slice(LauncherAppGlance *glance) { + if (!glance || !glance->should_consider_slices) { + return; + } + + const Uuid *uuid = &glance->uuid; + + AppGlanceSliceInternal current_slice = {}; + // If there's no current slice, this function won't modify the zeroed-out current_slice, so we + // can safely set the glance's current slice to current_slice either way + app_glance_service_get_current_slice(uuid, ¤t_slice); + glance->current_slice = current_slice; + + if (glance->handlers.current_slice_updated) { + glance->handlers.current_slice_updated(glance); + } + + launcher_app_glance_service_notify_glance_changed(glance->service); +} + +void launcher_app_glance_draw(GContext *ctx, const GRect *frame, LauncherAppGlance *glance, + bool is_highlighted) { + if (!glance || !frame || !ctx) { + return; + } + + glance->size = frame->size; + glance->is_highlighted = is_highlighted; + kino_reel_draw(glance->reel, ctx, frame->origin); +} + +void launcher_app_glance_notify_service_glance_changed(LauncherAppGlance *glance) { + if (!glance) { + return; + } + launcher_app_glance_service_notify_glance_changed(glance->service); +} + +void launcher_app_glance_destroy(LauncherAppGlance *glance) { + if (!glance) { + return; + } + + kino_reel_destroy(glance->reel); + app_free(glance); +} diff --git a/src/fw/apps/system/launcher/default/app_glance.h b/src/fw/apps/system/launcher/default/app_glance.h new file mode 100644 index 0000000000..4f912e70df --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance.h @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance_service.h" + +#include "applib/ui/kino/kino_reel.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "util/uuid.h" + +//! Forward declaration +typedef struct LauncherAppGlance LauncherAppGlance; + +//! Called when a launcher app glance's current slice has been updated. The glance will +//! automatically be redrawn after this function is called. +//! @param The glance whose current slice has been updated +typedef void (*LauncherAppGlanceCurrentSliceUpdated)(LauncherAppGlance *glance); + +typedef struct LauncherAppGlanceHandlers { + LauncherAppGlanceCurrentSliceUpdated current_slice_updated; +} LauncherAppGlanceHandlers; + +struct LauncherAppGlance { + //! The UUID of the app the launcher app glance represents + Uuid uuid; + //! The reel that implements how the launcher app glance should be drawn + KinoReel *reel; + //! Size of the area in which the launcher app glance expects to draw itself + GSize size; + //! Whether or not the launcher app glance is currently highlighted + bool is_highlighted; + //! Screen Y center position for arc effect calculation + int16_t screen_center_y; + //! Whether or not the launcher app glance should consider slices + bool should_consider_slices; + //! The current slice that should be drawn in the launcher app glance + AppGlanceSliceInternal current_slice; + //! The launcher app glance service that created the glance; used by the glance to notify the + //! service that the glance needs to be redrawn + LauncherAppGlanceService *service; + //! Callback handlers for the launcher app glance + LauncherAppGlanceHandlers handlers; +}; + +//! Initialize a launcher app glance. +//! @param glance The glance to initialize +//! @param uuid The UUID of the app +//! @param impl The KinoReel implementation for the glance +//! @param should_consider_slices Whether or not the glance should consider slices +//! @param handlers Optional handlers to use with the glance +void launcher_app_glance_init(LauncherAppGlance *glance, const Uuid *uuid, KinoReel *impl, + bool should_consider_slices, + const LauncherAppGlanceHandlers *handlers); + +//! Update the current slice of the launcher app glance as well as the icon if the slice needs to +//! change it. +//! @param glance The glance for which to update the current slice +void launcher_app_glance_update_current_slice(LauncherAppGlance *glance); + +//! Draw the provided launcher app glance. +//! @param ctx The graphics context to use when drawing the glance +//! @param frame The frame in which to draw the glance +//! @param glance The glance to draw +//! @param is_highlighted Whether or not the glance should be drawn highlighted +void launcher_app_glance_draw(GContext *ctx, const GRect *frame, LauncherAppGlance *glance, + bool is_highlighted); + +//! Notify the launcher app glance's service that its content has changed. +//! @param glance The glance that has changed +void launcher_app_glance_notify_service_glance_changed(LauncherAppGlance *glance); + +//! Destroy the provided launcher app glance. +//! @param glance The glance to destroy +void launcher_app_glance_destroy(LauncherAppGlance *glance); diff --git a/src/fw/apps/system/launcher/default/app_glance_alarms.c b/src/fw/apps/system/launcher/default/app_glance_alarms.c new file mode 100644 index 0000000000..da8abe92c4 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_alarms.c @@ -0,0 +1,184 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_alarms.h" + +#include "app_glance_structured.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/timeline/attribute.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +typedef struct LauncherAppGlanceAlarms { + char title[APP_NAME_SIZE_BYTES]; + char subtitle[ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN]; + KinoReel *icon; + uint32_t icon_resource_id; + uint32_t default_icon_resource_id; + EventServiceInfo alarm_clock_event_info; +} LauncherAppGlanceAlarms; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceAlarms *alarms_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(alarms_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceAlarms *alarms_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(alarms_glance, title, NULL); +} + +static void prv_alarms_glance_subtitle_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceAlarms *alarms_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (alarms_glance) { + strncpy(buffer, alarms_glance->subtitle, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_alarms_glance_subtitle_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceAlarms *alarms_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (alarms_glance) { + event_service_client_unsubscribe(&alarms_glance->alarm_clock_event_info); + kino_reel_destroy(alarms_glance->icon); + } + app_free(alarms_glance); +} + +static void prv_set_glance_icon(LauncherAppGlanceAlarms *alarms_glance, + uint32_t new_icon_resource_id) { + if (alarms_glance->icon_resource_id == new_icon_resource_id) { + // Nothing to do, bail out + return; + } + + // Destroy the existing icon + kino_reel_destroy(alarms_glance->icon); + + // Set the new icon and record its resource ID + alarms_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); + PBL_ASSERTN(alarms_glance->icon); + alarms_glance->icon_resource_id = new_icon_resource_id; +} + +//! If alarm is for today, alarm text should look like "5:43 PM" (12 hr) or "17:43" (24 hr) +//! If alarm is not for today, text should look like "Fri, 11:30 PM" (12 hr) or "Fri, 23:30" (24 hr) +//! If no alarms are set, the alarm text should be the empty string "" +static void prv_update_glance_for_next_alarm(LauncherAppGlanceAlarms *alarms_glance) { + // Start by assuming we'll set the default icon + uint32_t new_icon_resource_id = alarms_glance->default_icon_resource_id; + + time_t alarm_time_epoch; + if (!alarm_get_next_enabled_alarm(&alarm_time_epoch)) { + // Clear the alarm text if there are no alarms set + alarms_glance->subtitle[0] = '\0'; + } else { + // If the next alarm is smart, use the smart alarm icon + if (alarm_is_next_enabled_alarm_smart()) { + // TODO PBL-39113: Replace this placeholder with a better smart alarm icon for the glance + new_icon_resource_id = RESOURCE_ID_SMART_ALARM_TINY; + } + + char time_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; + clock_copy_time_string_timestamp(time_buffer, sizeof(time_buffer), alarm_time_epoch); + + // Determine if the alarm is for today + const time_t current_time = rtc_get_time(); + const time_t today_midnight = time_util_get_midnight_of(current_time); + const time_t alarm_midnight = time_util_get_midnight_of(alarm_time_epoch); + const bool is_alarm_for_today = (alarm_midnight == today_midnight); + + const size_t alarm_subtitle_size = sizeof(alarms_glance->subtitle); + + // Only show the day of the week if the alarm is not for today + if (!is_alarm_for_today) { + // Get a string for the abbreviated day of the week in the user's locale + char day_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; + struct tm alarm_time; + localtime_r(&alarm_time_epoch, &alarm_time); + strftime(day_buffer, sizeof(day_buffer), "%a", &alarm_time); + + snprintf(alarms_glance->subtitle, alarm_subtitle_size, "%s, %s", day_buffer, time_buffer); + } else { + strncpy(alarms_glance->subtitle, time_buffer, alarm_subtitle_size); + alarms_glance->subtitle[alarm_subtitle_size - 1] = '\0'; + } + } + + // Update the icon + prv_set_glance_icon(alarms_glance, new_icon_resource_id); +} + +static void prv_alarm_clock_event_handler(PBL_UNUSED PebbleEvent *event, void *context) { + LauncherAppGlanceStructured *structured_glance = context; + LauncherAppGlanceAlarms *alarms_glance = + launcher_app_glance_structured_get_data(structured_glance); + + prv_update_glance_for_next_alarm(alarms_glance); + + // Broadcast to the service that we changed the glance + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); +} + +static const LauncherAppGlanceStructuredImpl s_alarms_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_alarms_create(const AppMenuNode *node) { + PBL_ASSERTN(node); + + LauncherAppGlanceAlarms *alarms_glance = app_zalloc_check(sizeof(*alarms_glance)); + + // Copy the name of the Alarms app as the title + const size_t title_size = sizeof(alarms_glance->title); + strncpy(alarms_glance->title, node->name, title_size); + alarms_glance->title[title_size - 1] = '\0'; + + // Save the default app icon resource ID + alarms_glance->default_icon_resource_id = node->icon_resource_id; + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_alarms_structured_glance_impl, + should_consider_slices, alarms_glance); + PBL_ASSERTN(structured_glance); + + // Get the first state of the glance + prv_update_glance_for_next_alarm(alarms_glance); + + // Subscribe to alarm clock events for updating the glance + alarms_glance->alarm_clock_event_info = (EventServiceInfo) { + .type = PEBBLE_ALARM_CLOCK_EVENT, + .handler = prv_alarm_clock_event_handler, + .context = structured_glance, + }; + event_service_client_subscribe(&alarms_glance->alarm_clock_event_info); + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_alarms.h b/src/fw/apps/system/launcher/default/app_glance_alarms.h new file mode 100644 index 0000000000..31a981ec4f --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_alarms.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_alarms_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/app_glance_generic.c b/src/fw/apps/system/launcher/default/app_glance_generic.c new file mode 100644 index 0000000000..cd59c7cb17 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_generic.c @@ -0,0 +1,382 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_generic.h" + +#include "app_glance_structured.h" + +#include "applib/app_glance.h" +#include "applib/app_timer.h" +#include "applib/template_string.h" +#include "applib/ui/kino/kino_reel.h" +#include "apps/system/timeline/text_node.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "process_management/pebble_process_info.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "system/passert.h" +#include "util/string.h" +#include "util/struct.h" + +#define APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR (PROCESS_INFO_FIRST_4X_SDK_VERSION_MAJOR) +#define APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MINOR (PROCESS_INFO_FIRST_4X_SDK_VERSION_MINOR) + +typedef struct LauncherAppGlanceGeneric { + //! The title that will be displayed + char title_buffer[APP_NAME_SIZE_BYTES]; + //! The icon that will be displayed + KinoReel *displayed_icon; + //! The resource info of the displayed icon + AppResourceInfo displayed_icon_resource_info; + //! The resource info of the default app icon + AppResourceInfo default_icon_resource_info; + //! Fallback icon to use if other icons aren't available; owned by client + const KinoReel *fallback_icon; + //! The resource ID of the fallback icon; used for comparisons + uint32_t fallback_icon_resource_id; + //! App timer used for re-evaluating the current slice's subtitle template string + AppTimer *slice_subtitle_template_string_reeval_timer; + //! UTC timestamp of when the current slice's subtitle template string must be re-evaluated + //! A zero value indicates that there is no need to re-evaluate the subtitle template string + time_t next_slice_subtitle_template_string_reeval_time; + //! Whether to use the legacy 28x28 icon size limit + bool use_legacy_28x28_icon_size_limit; +} LauncherAppGlanceGeneric; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceGeneric *generic_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(generic_glance, displayed_icon, NULL); +} + +static void prv_generic_glance_destroy_displayed_icon(LauncherAppGlanceGeneric *generic_glance) { + // Only delete the displayed icon if it doesn't match the fallback icon because we don't own it + if (generic_glance && (generic_glance->displayed_icon != generic_glance->fallback_icon)) { + kino_reel_destroy(generic_glance->displayed_icon); + } +} + +static KinoReel *prv_create_glance_icon(const AppResourceInfo *res_info, + bool legacy_icon_size_limit) { + if (!res_info) { + return NULL; + } + + KinoReel *icon = kino_reel_create_with_resource_system(res_info->res_app_num, res_info->res_id); + if (!icon) { + return NULL; + } + + const GSize size = kino_reel_get_size(icon); + // Not const to deal with horrifying GCC bug + GSize max_size = legacy_icon_size_limit ? LAUNCHER_APP_GLANCE_STRUCTURED_ICON_LEGACY_MAX_SIZE + : LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE; + if ((size.w > max_size.w) || + (size.h > max_size.h)) { + // The icon is too big + kino_reel_destroy(icon); + return NULL; + } + + return icon; +} + +static bool prv_app_resource_info_equal(const AppResourceInfo *a, const AppResourceInfo *b) { + PBL_ASSERTN(a && b); + return ((a == b) || ((a->res_id == b->res_id) && (a->res_app_num == b->res_app_num))); +} + +static void prv_generic_glance_set_icon(LauncherAppGlanceGeneric *generic_glance, + const AppResourceInfo *res_info) { + if (!generic_glance || !res_info) { + return; + } + + const bool is_requested_resource_the_default_icon = + ((res_info->res_app_num == generic_glance->default_icon_resource_info.res_app_num) && + ((res_info->res_id == APP_GLANCE_SLICE_DEFAULT_ICON) || + (res_info->res_id == generic_glance->default_icon_resource_info.res_id))); + const bool does_default_icon_need_to_be_loaded = + (is_requested_resource_the_default_icon && + !prv_app_resource_info_equal(&generic_glance->displayed_icon_resource_info, + &generic_glance->default_icon_resource_info)); + const bool is_icon_stale = + (!prv_app_resource_info_equal(&generic_glance->displayed_icon_resource_info, res_info)); + + if (generic_glance->displayed_icon && + !does_default_icon_need_to_be_loaded && + !is_icon_stale) { + // Nothing to do, bail out + return; + } + + // Destroy the currently displayed icon + prv_generic_glance_destroy_displayed_icon(generic_glance); + + AppResourceInfo res_info_to_load = *res_info; + + // Set the resource info to the real default icon resource info if the default icon was requested + if (is_requested_resource_the_default_icon) { + res_info_to_load = generic_glance->default_icon_resource_info; + } + + const bool legacy_icon_size_limit = generic_glance->use_legacy_28x28_icon_size_limit; + // Try loading the requested icon + generic_glance->displayed_icon = prv_create_glance_icon(&res_info_to_load, + legacy_icon_size_limit); + + if (!generic_glance->displayed_icon) { + // Try again with the app's default icon if we didn't just try it + if (!prv_app_resource_info_equal(&res_info_to_load, + &generic_glance->default_icon_resource_info)) { + res_info_to_load = generic_glance->default_icon_resource_info; + generic_glance->displayed_icon = prv_create_glance_icon(&res_info_to_load, + legacy_icon_size_limit); + } + + // If we don't have a valid icon at this point, use the fallback icon (casting to non-const so + // we can use it) + if (!generic_glance->displayed_icon && generic_glance->fallback_icon) { + // Note that this (reasonably) assumes that the system fallback icon is a system icon + res_info_to_load = (AppResourceInfo) { + .res_app_num = SYSTEM_APP, + .res_id = generic_glance->fallback_icon_resource_id, + }; + generic_glance->displayed_icon = (KinoReel *)generic_glance->fallback_icon; + } + } + + // We require that we have some sort of icon at this point + PBL_ASSERTN(generic_glance->displayed_icon); + + // Update our recording of the resource info of the displayed icon + generic_glance->displayed_icon_resource_info = res_info_to_load; +} + +static void prv_cancel_subtitle_reeval_timer(LauncherAppGlanceGeneric *generic_glance) { + if (!generic_glance) { + return; + } + + if (generic_glance->slice_subtitle_template_string_reeval_timer) { + app_timer_cancel(generic_glance->slice_subtitle_template_string_reeval_timer); + generic_glance->slice_subtitle_template_string_reeval_timer = NULL; + } + + // Set the next re-evaluation time to "never" + generic_glance->next_slice_subtitle_template_string_reeval_time = 0; +} + +static void prv_subtitle_reeval_timer_cb(void *data) { + LauncherAppGlanceStructured *structured_glance = data; + PBL_ASSERTN(structured_glance); + LauncherAppGlanceGeneric *generic_glance = + launcher_app_glance_structured_get_data(structured_glance); + + // Reset the timer + generic_glance->slice_subtitle_template_string_reeval_timer = NULL; + prv_cancel_subtitle_reeval_timer(generic_glance); + + // Notify the service that the glance changed + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); +} + + +static void prv_update_subtitle_template_string_reeval_timer_if_necessary( + LauncherAppGlanceStructured *structured_glance, time_t new_reeval_time) { + LauncherAppGlanceGeneric *generic_glance = + launcher_app_glance_structured_get_data(structured_glance); + const time_t existing_reeval_time = + generic_glance->next_slice_subtitle_template_string_reeval_time; + // Bail out if the new re-evaluation time is not earlier than the existing one we have + if ((new_reeval_time == 0) || + ((existing_reeval_time != 0) && (new_reeval_time >= existing_reeval_time))) { + return; + } + + const int time_until_next_reeval = new_reeval_time - rtc_get_time(); + // On the off chance that we missed the reeval, immediately call the timer callback + if (time_until_next_reeval <= 0) { + prv_subtitle_reeval_timer_cb(structured_glance); + return; + } + + const uint64_t time_until_next_reeval_ms = (uint64_t)time_until_next_reeval * MS_PER_SECOND; + if (time_until_next_reeval_ms > UINT32_MAX) { + // Next reeval time is so far in the future that its offset in milliseconds from the + // current time would overflow the argument to AppTimer, so just ignore this reeval because it's + // not worth setting a timer for it + return; + } + + prv_cancel_subtitle_reeval_timer(generic_glance); + + generic_glance->slice_subtitle_template_string_reeval_timer = + app_timer_register((uint32_t)time_until_next_reeval_ms, prv_subtitle_reeval_timer_cb, + structured_glance); + generic_glance->next_slice_subtitle_template_string_reeval_time = new_reeval_time; +} + +static void prv_current_slice_updated(LauncherAppGlance *glance) { + // Ignore slices that aren't of the IconAndSubtitle type beyond this point for now + if (glance->current_slice.type != AppGlanceSliceType_IconAndSubtitle) { + return; + } + + LauncherAppGlanceStructured *structured_glance = (LauncherAppGlanceStructured *)glance; + LauncherAppGlanceGeneric *generic_glance = + launcher_app_glance_structured_get_data(structured_glance); + + const TimelineResourceId timeline_res_id = + (TimelineResourceId)glance->current_slice.icon_and_subtitle.icon_resource_id; + + // Initialize the resource info to be the default icon + AppResourceInfo resource_info = generic_glance->default_icon_resource_info; + // Override it if we have a valid timeline resource ID from the new app glance slice + if (timeline_res_id != APP_GLANCE_SLICE_DEFAULT_ICON) { + // NOTE: This variant of the timeline_resources_get_id() function is safe to call here with + // respect to the app supporting published resources because we only consider slices if the + // glance is for a system app (where it doesn't matter) or for apps that were compiled with + // an SDK that supports app glances (which is newer than the first SDK that supported published + // resources as proved by the following asserts) + _Static_assert((APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR > + TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR) || + ((APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR == + TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR) && + (APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MINOR >= + TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MINOR)), + "App glance min supported SDK version must be equal to or newer than first " + "timeline/published resource PBW supported SDK version"); + timeline_resources_get_id_system(timeline_res_id, LAUNCHER_APP_GLANCE_GENERIC_ICON_SIZE_TYPE, + resource_info.res_app_num, &resource_info); + } + + prv_generic_glance_set_icon(generic_glance, &resource_info); + + prv_cancel_subtitle_reeval_timer(generic_glance); + + // The glance will automatically be redrawn after this function is called (which will also update + // the glance's state regarding its subtitle template string), so no need to mark it as dirty + // (see launcher_app_glance_update_current_slice()) +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceGeneric *generic_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(generic_glance, title_buffer, NULL); +} + +static void prv_generic_glance_dynamic_text_node_update(PBL_UNUSED GContext *ctx, + PBL_UNUSED GTextNode *node, + PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, + PBL_UNUSED bool render, char *buffer, + size_t buffer_size, void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + if (!structured_glance) { + return; + } else if (structured_glance->glance.current_slice.type != AppGlanceSliceType_IconAndSubtitle) { + PBL_LOG_WRN("Generic glance doesn't know how to handle slice type %d", + structured_glance->glance.current_slice.type); + return; + } + + // Evaluate the subtitle as a template string + const char *subtitle_template_string = + structured_glance->glance.current_slice.icon_and_subtitle.template_string; + TemplateStringEvalConditions template_string_reeval_conditions = {0}; + const TemplateStringVars template_string_vars = (TemplateStringVars) { + .current_time = rtc_get_time(), + }; + TemplateStringError template_string_error = {0}; + template_string_evaluate(subtitle_template_string, buffer, buffer_size, + &template_string_reeval_conditions, &template_string_vars, + &template_string_error); + + if (template_string_error.status != TemplateStringErrorStatus_Success) { + // Zero out the buffer and return + buffer[0] = '\0'; + PBL_LOG_WRN("Error at index %zu in evaluating template string: %s", + template_string_error.index_in_string, subtitle_template_string); + return; + } + + // Update the timer for re-evaluating the template string, if necessary + prv_update_subtitle_template_string_reeval_timer_if_necessary( + structured_glance, template_string_reeval_conditions.eval_time); +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_generic_glance_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceGeneric *generic_glance = + launcher_app_glance_structured_get_data(structured_glance); + prv_cancel_subtitle_reeval_timer(generic_glance); + prv_generic_glance_destroy_displayed_icon(generic_glance); + app_free(generic_glance); +} + +static const LauncherAppGlanceStructuredImpl s_generic_structured_glance_impl = { + .base_handlers.current_slice_updated = prv_current_slice_updated, + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_generic_create(const AppMenuNode *node, + const KinoReel *fallback_icon, + uint32_t fallback_icon_resource_id) { + if (!node) { + return NULL; + } + + LauncherAppGlanceGeneric *generic_glance = app_zalloc_check(sizeof(*generic_glance)); + const size_t title_buffer_size = sizeof(generic_glance->title_buffer); + strncpy(generic_glance->title_buffer, node->name, title_buffer_size); + generic_glance->title_buffer[title_buffer_size - 1] = '\0'; + generic_glance->default_icon_resource_info = (AppResourceInfo) { + .res_app_num = node->app_num, + .res_id = node->icon_resource_id, + }; + generic_glance->fallback_icon = fallback_icon; + generic_glance->fallback_icon_resource_id = fallback_icon_resource_id; + + const Version app_glance_min_supported_sdk_version = (Version) { + .major = APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR, + .minor = APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MINOR, + }; + + const bool app_glances_supported = + (version_compare(node->sdk_version, app_glance_min_supported_sdk_version) >= 0); + + generic_glance->use_legacy_28x28_icon_size_limit = !app_glances_supported; + + // Our unit tests rely on system app icons for testing generic glances which means + // the != SYSTEM_APP condition can't be satisfied easily, so just skip this part for unit tests +#if !UNITTEST + // Only consider slices for non-system apps that were compiled with an SDK that supports glances + const bool should_consider_slices = (node->app_num != SYSTEM_APP) && + app_glances_supported; +#else + const bool should_consider_slices = true; +#endif + + prv_generic_glance_set_icon(generic_glance, &generic_glance->default_icon_resource_info); + + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_generic_structured_glance_impl, + should_consider_slices, generic_glance); + if (structured_glance) { + if (generic_glance->use_legacy_28x28_icon_size_limit) { + launcher_app_glance_structured_set_icon_max_size(structured_glance, + LAUNCHER_APP_GLANCE_STRUCTURED_ICON_LEGACY_MAX_SIZE); + } + return &structured_glance->glance; + } else { + return NULL; + } +} diff --git a/src/fw/apps/system/launcher/default/app_glance_generic.h b/src/fw/apps/system/launcher/default/app_glance_generic.h new file mode 100644 index 0000000000..ef20e8cf87 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_generic.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "applib/ui/kino/kino_reel.h" +#include "process_management/app_menu_data_source.h" +#include "pbl/services/timeline/timeline_resources.h" + +#define LAUNCHER_APP_GLANCE_GENERIC_ICON_SIZE_TYPE (TimelineResourceSizeTiny) + +//! Create a generic launcher app glance for the provided app menu node. +//! @param node The node that the new generic glance should represent +//! @param fallback_icon A long-lived fallback icon to use if no other icons are available; will +//! not be destroyed when the generic glance is destroyed +//! @param fallback_icon_resource_id The resource ID of the fallback icon +LauncherAppGlance *launcher_app_glance_generic_create(const AppMenuNode *node, + const KinoReel *fallback_icon, + uint32_t fallback_icon_resource_id); diff --git a/src/fw/apps/system/launcher/default/app_glance_music.c b/src/fw/apps/system/launcher/default/app_glance_music.c new file mode 100644 index 0000000000..8016131017 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_music.c @@ -0,0 +1,209 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_music.h" + +#include "app_glance_structured.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/music.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +// We need enough space for the track artist and title (so 2 * MUSIC_BUFFER_LENGTH from music.h), +// the delimiter string " - " (3), and 1 for the null terminator +#define TRACK_TEXT_BUFFER_SIZE ((MUSIC_BUFFER_LENGTH * 2) + 3 + 1) + +typedef struct LauncherAppGlanceMusic { + char title[APP_NAME_SIZE_BYTES]; + char subtitle[TRACK_TEXT_BUFFER_SIZE]; + KinoReel *icon; + uint32_t icon_resource_id; + uint32_t default_icon_resource_id; + EventServiceInfo music_event_info; +} LauncherAppGlanceMusic; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceMusic *music_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(music_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceMusic *music_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(music_glance, title, NULL); +} + +static void prv_music_glance_subtitle_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceMusic *music_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (music_glance) { + strncpy(buffer, music_glance->subtitle, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_music_glance_subtitle_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceMusic *music_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (music_glance) { + event_service_client_unsubscribe(&music_glance->music_event_info); + kino_reel_destroy(music_glance->icon); + } + app_free(music_glance); +} + +static void prv_set_glance_icon(LauncherAppGlanceMusic *music_glance, + uint32_t new_icon_resource_id) { + if (music_glance->icon_resource_id == new_icon_resource_id) { + // Nothing to do, bail out + return; + } + + // Destroy the existing icon + kino_reel_destroy(music_glance->icon); + + // Set the new icon and record its resource ID + // TODO PBL-38539: Switch from using a regular resource ID to using a TimelineResourceId + music_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); + PBL_ASSERTN(music_glance->icon); + music_glance->icon_resource_id = new_icon_resource_id; +} + +static bool prv_should_display_music_state(MusicPlayState play_state, + uint32_t last_updated_time_elapsed_ms) { + const uint32_t music_last_updated_display_threshold_ms = 30 * MS_PER_SECOND * SECONDS_PER_MINUTE; + + switch (play_state) { + case MusicPlayStatePlaying: + case MusicPlayStateForwarding: + case MusicPlayStateRewinding: + return true; + case MusicPlayStatePaused: + // We won't display the music state if the music is paused and it hasn't changed in a while + return (last_updated_time_elapsed_ms < music_last_updated_display_threshold_ms); + case MusicPlayStateUnknown: + case MusicPlayStateInvalid: + return false; + } + WTF; +} + +static void prv_update_glance_for_music_state(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceMusic *music_glance = + launcher_app_glance_structured_get_data(structured_glance); + PBL_ASSERTN(music_glance); + + // Zero out the glance's subtitle buffer + const size_t music_glance_subtitle_size = sizeof(music_glance->subtitle); + memset(music_glance->subtitle, 0, music_glance_subtitle_size); + + // Default to showing the default icon + uint32_t new_icon_resource_id = music_glance->default_icon_resource_id; + + // Determine if we should display the current music state + const MusicPlayState play_state = music_get_playback_state(); + const uint32_t last_updated_time_elapsed_ms = music_get_ms_since_pos_last_updated(); + const bool should_display_music_state = + prv_should_display_music_state(play_state, last_updated_time_elapsed_ms); + + if (should_display_music_state) { + // Get the artist and title strings for the music playing or paused + char artist_buffer[MUSIC_BUFFER_LENGTH] = {}; + char title_buffer[MUSIC_BUFFER_LENGTH] = {}; + music_get_now_playing(title_buffer, artist_buffer, NULL /* album_buffer */); + + // Only populate the glance with music info if we have both an artist string and a title string + if (!IS_EMPTY_STRING(artist_buffer) && !IS_EMPTY_STRING(title_buffer)) { + // Use the strings to fill the subtitle buffer + snprintf(music_glance->subtitle, music_glance_subtitle_size, "%s - %s", artist_buffer, + title_buffer); + + // Choose the icon we should display; note that we'll use the default icon we set above if we + // don't have an icon for the current play state + if (play_state == MusicPlayStatePlaying) { + new_icon_resource_id = RESOURCE_ID_MUSIC_APP_GLANCE_PLAY; + } else if (play_state == MusicPlayStatePaused) { + new_icon_resource_id = RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE; + } + } + } + + // Update the glance icon + prv_set_glance_icon(music_glance, new_icon_resource_id); + + // Broadcast to the service that we changed the glance + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); +} + +static void prv_music_event_handler(PebbleEvent *event, void *context) { + switch (event->media.type) { + case PebbleMediaEventTypeNowPlayingChanged: + case PebbleMediaEventTypePlaybackStateChanged: + case PebbleMediaEventTypeServerConnected: + case PebbleMediaEventTypeServerDisconnected: + prv_update_glance_for_music_state(context); + return; + case PebbleMediaEventTypeVolumeChanged: + case PebbleMediaEventTypeTrackPosChanged: + return; + } + WTF; +} + +static const LauncherAppGlanceStructuredImpl s_music_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_music_create(const AppMenuNode *node) { + PBL_ASSERTN(node); + + LauncherAppGlanceMusic *music_glance = app_zalloc_check(sizeof(*music_glance)); + + // Copy the name of the Music app as the title + const size_t title_size = sizeof(music_glance->title); + strncpy(music_glance->title, node->name, title_size); + music_glance->title[title_size - 1] = '\0'; + + // Save the default icon resource ID for the Music app + music_glance->default_icon_resource_id = node->icon_resource_id; + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_music_structured_glance_impl, + should_consider_slices, music_glance); + PBL_ASSERTN(structured_glance); + + // Get the first state of the glance + prv_update_glance_for_music_state(structured_glance); + + // Subscribe to music events for updating the glance + music_glance->music_event_info = (EventServiceInfo) { + .type = PEBBLE_MEDIA_EVENT, + .handler = prv_music_event_handler, + .context = structured_glance, + }; + event_service_client_subscribe(&music_glance->music_event_info); + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_music.h b/src/fw/apps/system/launcher/default/app_glance_music.h new file mode 100644 index 0000000000..f2d8b0551b --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_music.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_music_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/app_glance_notifications.c b/src/fw/apps/system/launcher/default/app_glance_notifications.c new file mode 100644 index 0000000000..fdf8e2aae0 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_notifications.c @@ -0,0 +1,172 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_notifications.h" + +#include "app_glance_structured.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/timeline/attribute.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +typedef struct LauncherAppGlanceNotifications { + char title[APP_NAME_SIZE_BYTES]; + char subtitle[ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN]; + KinoReel *icon; + EventServiceInfo notification_event_info; +} LauncherAppGlanceNotifications; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceNotifications *notifications_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(notifications_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceNotifications *notifications_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(notifications_glance, title, NULL); +} + +static void prv_notifications_glance_subtitle_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceNotifications *notifications_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (notifications_glance) { + strncpy(buffer, notifications_glance->subtitle, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_notifications_glance_subtitle_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceNotifications *notifications_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (notifications_glance) { + event_service_client_unsubscribe(¬ifications_glance->notification_event_info); + kino_reel_destroy(notifications_glance->icon); + } + app_free(notifications_glance); +} + +static bool prv_notification_iterator_cb(void *data, SerializedTimelineItemHeader *header_id) { + Uuid *last_notification_received_id = data; + + // The iterator proceeds from the first notification received to the last notification received, + // so copy the ID of the current notification and then return true so we iterate until the end. + // Thus the last ID we save will be the last notification received. + *last_notification_received_id = header_id->common.id; + + return true; +} + +static void prv_update_glance_for_last_notification_received( + LauncherAppGlanceNotifications *notifications_glance) { + // Find the ID of the last notification received + Uuid last_notification_received_id; + notification_storage_iterate(prv_notification_iterator_cb, &last_notification_received_id); + + TimelineItem notification; + if (!notification_storage_get(&last_notification_received_id, ¬ification)) { + // We couldn't load the notification for some reason; just bail out with the subtitle cleared + notifications_glance->subtitle[0] = '\0'; + return; + } + + const char *title = attribute_get_string(¬ification.attr_list, AttributeIdTitle, ""); + const char *subtitle = attribute_get_string(¬ification.attr_list, AttributeIdSubtitle, ""); + const char *body = attribute_get_string(¬ification.attr_list, AttributeIdBody, ""); + + // Determine which string we should use in the glance subtitle + const char *string_to_use_in_glance_subtitle = ""; + if (!IS_EMPTY_STRING(title)) { + string_to_use_in_glance_subtitle = title; + } else if (!IS_EMPTY_STRING(subtitle)) { + // Fallback to the subtitle + string_to_use_in_glance_subtitle = subtitle; + } else { + // Fallback to the body + string_to_use_in_glance_subtitle = body; + } + + // Copy the string to the glance + const size_t glance_subtitle_size = sizeof(notifications_glance->subtitle); + strncpy(notifications_glance->subtitle, string_to_use_in_glance_subtitle, glance_subtitle_size); + notifications_glance->subtitle[glance_subtitle_size - 1] = '\0'; +} + +static void prv_notification_event_handler(PebbleEvent *event, void *context) { + LauncherAppGlanceStructured *structured_glance = context; + LauncherAppGlanceNotifications *notifications_glance = + launcher_app_glance_structured_get_data(structured_glance); + switch (event->sys_notification.type) { + case NotificationAdded: + case NotificationRemoved: + prv_update_glance_for_last_notification_received(notifications_glance); + // Broadcast to the service that we changed the glance + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); + return; + case NotificationActedUpon: + case NotificationActionResult: + return; + } + WTF; +} + +static const LauncherAppGlanceStructuredImpl s_notifications_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_notifications_create(const AppMenuNode *node) { + PBL_ASSERTN(node); + + LauncherAppGlanceNotifications *notifications_glance = + app_zalloc_check(sizeof(*notifications_glance)); + + // Copy the name of the Notifications app as the title + const size_t title_size = sizeof(notifications_glance->title); + strncpy(notifications_glance->title, node->name, title_size); + notifications_glance->title[title_size - 1] = '\0'; + + // Create the icon for the Notifications app + notifications_glance->icon = kino_reel_create_with_resource_system(node->app_num, + node->icon_resource_id); + PBL_ASSERTN(notifications_glance->icon); + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_notifications_structured_glance_impl, + should_consider_slices, notifications_glance); + PBL_ASSERTN(structured_glance); + + // Get the first state of the glance + prv_update_glance_for_last_notification_received(notifications_glance); + + // Subscribe to notification events for updating the glance + notifications_glance->notification_event_info = (EventServiceInfo) { + .type = PEBBLE_SYS_NOTIFICATION_EVENT, + .handler = prv_notification_event_handler, + .context = structured_glance, + }; + event_service_client_subscribe(¬ifications_glance->notification_event_info); + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_notifications.h b/src/fw/apps/system/launcher/default/app_glance_notifications.h new file mode 100644 index 0000000000..14332275ff --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_notifications.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_notifications_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/app_glance_private.c b/src/fw/apps/system/launcher/default/app_glance_private.c new file mode 100644 index 0000000000..5d4fac166b --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_private.c @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance.h" + +#include "applib/ui/kino/kino_reel_custom.h" +#include "system/passert.h" +#include "util/struct.h" + +GSize launcher_app_glance_get_size_for_reel(KinoReel *reel) { + PBL_ASSERTN(reel->impl && (reel->impl->reel_type == KinoReelTypeCustom)); + LauncherAppGlance *glance = kino_reel_custom_get_data(reel); + return NULL_SAFE_FIELD_ACCESS(glance, size, GSizeZero); +} diff --git a/src/fw/apps/system/launcher/default/app_glance_private.h b/src/fw/apps/system/launcher/default/app_glance_private.h new file mode 100644 index 0000000000..d325678ace --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_private.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/kino/kino_reel.h" + +//! Get the size of the provided reel that implements how a launcher app glance should be drawn. +//! @param reel The reel that implements how a glance should be drawn +//! @return The size of the reel +GSize launcher_app_glance_get_size_for_reel(KinoReel *reel); diff --git a/src/fw/apps/system/launcher/default/app_glance_service.c b/src/fw/apps/system/launcher/default/app_glance_service.c new file mode 100644 index 0000000000..c9319ce272 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_service.c @@ -0,0 +1,474 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_service.h" + +#include "app_glance.h" +#include "app_glance_alarms.h" +#include "app_glance_generic.h" +#include "app_glance_music.h" +#include "app_glance_notifications.h" +#include "app_glance_settings.h" +#include "app_glance_watchfaces.h" +#include "app_glance_weather.h" +#include "app_glance_workout.h" +#include "menu_layer.h" +#include "menu_layer_private.h" + +#include "applib/app_glance.h" +#include "applib/ui/kino/kino_reel.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/size.h" +#include "util/struct.h" +#include "util/uuid.h" + +//! Cache twice the number of glances we'll show simultaneously in the launcher +#define LAUNCHER_APP_GLANCE_SERVICE_CACHE_NUM_ENTRIES (2 * LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS) + +typedef struct LauncherAppGlanceCacheEntry { + ListNode node; + LauncherAppGlance *glance; +} LauncherAppGlanceCacheEntry; + +////////////////////////////////////// +// KinoPlayer callbacks + +static void prv_glance_reel_player_frame_did_change_cb(KinoPlayer *player, void *context) { + LauncherAppGlanceService *service = context; + launcher_app_glance_service_notify_glance_changed(service); +} + +/////////////////////////// +// Slice expiration timer + +static void prv_slice_expiration_timer_cb(void *data); + +static void prv_reset_slice_expiration_timer(LauncherAppGlanceService *service) { + if (!service) { + return; + } + + if (service->slice_expiration_timer) { + app_timer_cancel(service->slice_expiration_timer); + service->slice_expiration_timer = NULL; + } + + // Set the next slice expiration time to "never" + service->next_slice_expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION; +} + +static void prv_update_slice_expiration_timer_if_necessary(LauncherAppGlanceService *service, + time_t new_slice_expire_time) { + const time_t next_slice_expiration_time = service->next_slice_expiration_time; + const bool is_new_slice_expire_time_earlier_than_existing_earliest = + (new_slice_expire_time != APP_GLANCE_SLICE_NO_EXPIRATION) && + ((next_slice_expiration_time == APP_GLANCE_SLICE_NO_EXPIRATION) || + (new_slice_expire_time < next_slice_expiration_time)); + if (!is_new_slice_expire_time_earlier_than_existing_earliest) { + return; + } + + const int time_until_slice_expires = new_slice_expire_time - rtc_get_time(); + // On the off chance that this slice has already expired, immediately call the timer callback + if (time_until_slice_expires <= 0) { + prv_slice_expiration_timer_cb(service); + return; + } + + const uint64_t time_until_slice_expires_ms = (uint64_t)time_until_slice_expires * MS_PER_SECOND; + if (time_until_slice_expires_ms > UINT32_MAX) { + // Slice expiration time is so far in the future that its offset in milliseconds from the + // current time would overflow the argument to AppTimer, so just ignore this slice because it's + // not worth setting a timer for it + return; + } + + prv_reset_slice_expiration_timer(service); + + service->slice_expiration_timer = + app_timer_register((uint32_t)time_until_slice_expires_ms, prv_slice_expiration_timer_cb, + service); + service->next_slice_expiration_time = new_slice_expire_time; +} + +static bool prv_glance_cache_slice_expiration_foreach_cb(ListNode *node, void *context) { + LauncherAppGlanceService *service = context; + PBL_ASSERTN(service); + + const LauncherAppGlanceCacheEntry *entry = (LauncherAppGlanceCacheEntry *)node; + LauncherAppGlance *glance = entry->glance; + + // Update the glance's current slice + launcher_app_glance_update_current_slice(glance); + + // If necessary, update the slice expiration timer with the updated current slice + prv_update_slice_expiration_timer_if_necessary(service, glance->current_slice.expiration_time); + + // Continue iterating until we've looked at all of the glances in the cache + return true; +} + +static void prv_slice_expiration_timer_cb(void *data) { + LauncherAppGlanceService *service = data; + PBL_ASSERTN(service); + + // Reset the timer + prv_reset_slice_expiration_timer(service); + + // Iterate over the glances in the cache to find the next earliest expiring slice + list_foreach(service->glance_cache, prv_glance_cache_slice_expiration_foreach_cb, service); +} + +///////////////////// +// Glance cache + +_Static_assert((offsetof(LauncherAppGlanceCacheEntry, node) == 0), + "ListNode is not the first field of LauncherAppGlanceCacheEntry"); + +static void prv_glance_cache_destroy_entry(LauncherAppGlanceService *service, + LauncherAppGlanceCacheEntry *entry) { + if (!entry) { + return; + } + + if (service) { + PBL_ASSERTN(entry->glance); + KinoReel *glance_reel = entry->glance->reel; + + // Set the glance reel player's reel to NULL if it belongs to the glance we're going to destroy + KinoPlayer *glance_reel_player = &service->glance_reel_player; + if (glance_reel && (glance_reel == kino_player_get_reel(glance_reel_player))) { + kino_player_set_reel(glance_reel_player, NULL, false /* take_ownership */); + } + } + + launcher_app_glance_destroy(entry->glance); + app_free(entry); +} + +static bool prv_glance_cache_deinit_foreach_cb(ListNode *node, void *context) { + LauncherAppGlanceService *service = context; + LauncherAppGlanceCacheEntry *entry = (LauncherAppGlanceCacheEntry *)node; + + prv_glance_cache_destroy_entry(service, entry); + // Continue iterating to destroy all of the entries + return true; +} + +static void prv_glance_cache_deinit(LauncherAppGlanceService *service) { + if (service) { + list_foreach(service->glance_cache, prv_glance_cache_deinit_foreach_cb, service); + service->glance_cache = NULL; + } +} + +//! Don't call this directly; it's used by prv_get_glance_for_node() below +static void prv_glance_cache_put(LauncherAppGlanceService *service, const Uuid *uuid, + LauncherAppGlance *glance) { + if (!service || !uuid || !glance) { + return; + } + + // If necessary, evict the LRU cache entry + const uint32_t cache_entry_count = list_count(service->glance_cache); + PBL_ASSERTN(cache_entry_count <= LAUNCHER_APP_GLANCE_SERVICE_CACHE_NUM_ENTRIES); + if (cache_entry_count == LAUNCHER_APP_GLANCE_SERVICE_CACHE_NUM_ENTRIES) { + LauncherAppGlanceCacheEntry *cache_entry_to_destroy = + (LauncherAppGlanceCacheEntry *)list_get_tail(service->glance_cache); + list_remove(&cache_entry_to_destroy->node, &service->glance_cache, NULL); + prv_glance_cache_destroy_entry(service, cache_entry_to_destroy); + } + + // Initialize a new cache entry, add it to the head of the cache list, and return it + LauncherAppGlanceCacheEntry *new_cache_entry = app_zalloc_check(sizeof(*new_cache_entry)); + *new_cache_entry = (LauncherAppGlanceCacheEntry) { + .glance = glance, + }; + service->glance_cache = list_insert_before(service->glance_cache, &new_cache_entry->node); +} + +static bool prv_glance_cache_entry_find_cb(ListNode *current_node, void *context) { + LauncherAppGlanceCacheEntry *current_entry = (LauncherAppGlanceCacheEntry *)current_node; + Uuid *uuid_to_find = context; + return (current_entry && uuid_equal(¤t_entry->glance->uuid, uuid_to_find)); +} + +static LauncherAppGlance *prv_load_glance_for_node(const AppMenuNode *node, + LauncherAppGlanceService *service) { + typedef struct { + Uuid uuid; + LauncherAppGlance* (*constructor)(const AppMenuNode *); + } SystemAppGlanceFactory; + + static const SystemAppGlanceFactory s_system_glance_factories[] = { + { // Settings + .uuid = {0x07, 0xe0, 0xd9, 0xcb, 0x89, 0x57, 0x4b, 0xf7, + 0x9d, 0x42, 0x35, 0xbf, 0x47, 0xca, 0xad, 0xfe}, + .constructor = launcher_app_glance_settings_create, + }, + { // Music + .uuid = {0x1f, 0x03, 0x29, 0x3d, 0x47, 0xaf, 0x4f, 0x28, + 0xb9, 0x60, 0xf2, 0xb0, 0x2a, 0x6d, 0xd7, 0x57}, + .constructor = launcher_app_glance_music_create, + }, + { // Weather + .uuid = {0x61, 0xb2, 0x2b, 0xc8, 0x1e, 0x29, 0x46, 0xd, + 0xa2, 0x36, 0x3f, 0xe4, 0x9, 0xa4, 0x39, 0xff}, + .constructor = launcher_app_glance_weather_create, + }, + { // Notifications + .uuid = {0xb2, 0xca, 0xe8, 0x18, 0x10, 0xf8, 0x46, 0xdf, + 0xad, 0x2b, 0x98, 0xad, 0x22, 0x54, 0xa3, 0xc1}, + .constructor = launcher_app_glance_notifications_create, + }, + { // Alarms + .uuid = {0x67, 0xa3, 0x2d, 0x95, 0xef, 0x69, 0x46, 0xd4, + 0xa0, 0xb9, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9}, + .constructor = launcher_app_glance_alarms_create, + }, + { // Watchfaces + .uuid = {0x18, 0xe4, 0x43, 0xce, 0x38, 0xfd, 0x47, 0xc8, + 0x84, 0xd5, 0x6d, 0x0c, 0x77, 0x5f, 0xbe, 0x55}, + .constructor = launcher_app_glance_watchfaces_create, + }, + { + // Workout + .uuid = {0xfe, 0xf8, 0x2c, 0x82, 0x71, 0x76, 0x4e, 0x22, + 0x88, 0xde, 0x35, 0xa3, 0xfc, 0x18, 0xd4, 0x3f}, + .constructor = launcher_app_glance_workout_create, + }, + }; + + LauncherAppGlance *glance = NULL; + + // Check if the UUID matches a known system glance + for (unsigned int i = 0; i < ARRAY_LENGTH(s_system_glance_factories); i++) { + const SystemAppGlanceFactory *factory = &s_system_glance_factories[i]; + if (uuid_equal(&factory->uuid, &node->uuid)) { + glance = factory->constructor(node); + break; + } + } + + // If we haven't loaded a glance yet, try loading a generic glance for the node + if (!glance) { + glance = launcher_app_glance_generic_create(node, service->generic_glance_icon, + service->generic_glance_icon_resource_id); + } + + // If we successfully loaded a glance, set its service field + if (glance) { + glance->service = service; + } + + return glance; +} + +static LauncherAppGlanceCacheEntry *prv_find_glance_entry_in_cache( + LauncherAppGlanceService *service, Uuid *uuid) { + return (LauncherAppGlanceCacheEntry *)list_find(service->glance_cache, + prv_glance_cache_entry_find_cb, uuid); +} + +static LauncherAppGlance *prv_find_glance_in_cache(LauncherAppGlanceService *service, Uuid *uuid) { + const LauncherAppGlanceCacheEntry *entry = prv_find_glance_entry_in_cache(service, uuid); + return NULL_SAFE_FIELD_ACCESS(entry, glance, NULL); +} + +//! Request a glance for an icon ID from an "MRU linked list" (list sorted by accesses so that +//! most recent accesses are at the head of the list) +static LauncherAppGlance *prv_fetch_from_cache_or_load_glance_for_node(AppMenuNode *node, + LauncherAppGlanceService *service) { + if (!service || !node) { + return NULL; + } + + Uuid *uuid = &node->uuid; + + LauncherAppGlance *glance = NULL; + + // Try to find the requested glance in the cache + LauncherAppGlanceCacheEntry *cache_entry = prv_find_glance_entry_in_cache(service, uuid); + if (cache_entry) { + // Move the found cache entry to the front of the cache list (to mark it as "MRU") + // This makes it easy to remove the "LRU" entry later by simply removing the tail + list_remove(&cache_entry->node, &service->glance_cache, NULL); + service->glance_cache = list_insert_before(service->glance_cache, &cache_entry->node); + glance = cache_entry->glance; + } + + // Try to load the glance requested if we didn't find it in the cache + if (!glance) { + glance = prv_load_glance_for_node(node, service); + if (!glance) { + // Just bail out and don't modify the cache if we fail + return NULL; + } + + // Add the new glance to the cache + prv_glance_cache_put(service, uuid, glance); + } + + // Update the slice expiration timer if the glance's current slice expires soon + prv_update_slice_expiration_timer_if_necessary(service, glance->current_slice.expiration_time); + + return glance; +} + +static bool prv_should_use_glance_cache_for_app_with_uuid(const Uuid *uuid) { + // Use the glance cache only if the app does not have the system UUID (all zeros) + return !uuid_is_system(uuid); +} + +///////////////////// +// Glance events + +static void prv_handle_glance_event(PebbleEvent *event, void *context) { + LauncherAppGlanceService *service = context; + PBL_ASSERTN(service); + + // Update the current slice of the glance that was changed if the glance is in the cache + LauncherAppGlance *glance_in_cache = prv_find_glance_in_cache(service, + event->app_glance.app_uuid); + if (glance_in_cache) { + launcher_app_glance_update_current_slice(glance_in_cache); + + // If necessary, update the slice expiration timer with the updated current slice + prv_update_slice_expiration_timer_if_necessary(service, + glance_in_cache->current_slice.expiration_time); + } +} + +///////////////////// +// Public API + +void launcher_app_glance_service_draw_glance_for_app_node(LauncherAppGlanceService *service, + GContext *ctx, const GRect *frame, + bool is_highlighted, + int16_t screen_center_y, + AppMenuNode *node) { + const bool use_glance_cache = prv_should_use_glance_cache_for_app_with_uuid(&node->uuid); + + LauncherAppGlance *glance = + use_glance_cache ? prv_fetch_from_cache_or_load_glance_for_node(node, service) : + prv_load_glance_for_node(node, service); + + // Set the screen Y position for arc effect on round displays + if (glance) { + glance->screen_center_y = screen_center_y; + } + + // Draw the glance in the provided frame + launcher_app_glance_draw(ctx, frame, glance, is_highlighted); + + // If we didn't use the glance cache, destroy the glance now + if (!use_glance_cache) { + launcher_app_glance_destroy(glance); + } +} + +void launcher_app_glance_service_rewind_current_glance(LauncherAppGlanceService *service) { + if (!service) { + return; + } + kino_player_rewind(&service->glance_reel_player); +} + +void launcher_app_glance_service_pause_current_glance(LauncherAppGlanceService *service) { + if (!service) { + return; + } + kino_player_pause(&service->glance_reel_player); +} + +void launcher_app_glance_service_play_current_glance(LauncherAppGlanceService *service) { + if (!service) { + return; + } + kino_player_play(&service->glance_reel_player); +} + +void launcher_app_glance_service_play_glance_for_app_node(LauncherAppGlanceService *service, + AppMenuNode *node) { + if (!service || !node) { + return; + } + + KinoPlayer *glance_reel_player = &service->glance_reel_player; + + // Rewind the player for any previously played glance + kino_player_rewind(glance_reel_player); + + const bool use_glance_cache = prv_should_use_glance_cache_for_app_with_uuid(&node->uuid); + if (!use_glance_cache) { + // Don't play glances that we don't store in the cache since they don't live long enough + // to advance frames + return; + } + + LauncherAppGlance *glance = prv_fetch_from_cache_or_load_glance_for_node(node, service); + PBL_ASSERTN(glance); + kino_player_set_reel(glance_reel_player, glance->reel, false /* take_ownership */); + kino_player_play(glance_reel_player); +} + +void launcher_app_glance_service_notify_glance_changed(LauncherAppGlanceService *service) { + if (service && service->handlers.glance_changed) { + service->handlers.glance_changed(service->handlers_context); + } +} + +void launcher_app_glance_service_init(LauncherAppGlanceService *service, + uint32_t generic_glance_icon_resource_id) { + if (!service) { + return; + } + + *service = (LauncherAppGlanceService) {}; + + prv_reset_slice_expiration_timer(service); + + service->glance_event_info = (EventServiceInfo) { + .type = PEBBLE_APP_GLANCE_EVENT, + .handler = prv_handle_glance_event, + .context = service, + }; + event_service_client_subscribe(&service->glance_event_info); + + service->generic_glance_icon = kino_reel_create_with_resource(generic_glance_icon_resource_id); + PBL_ASSERTN(service->generic_glance_icon); + service->generic_glance_icon_resource_id = generic_glance_icon_resource_id; + + const KinoPlayerCallbacks glance_reel_player_callbacks = (KinoPlayerCallbacks) { + .frame_did_change = prv_glance_reel_player_frame_did_change_cb, + }; + kino_player_set_callbacks(&service->glance_reel_player, glance_reel_player_callbacks, service); +} + +void launcher_app_glance_service_set_handlers(LauncherAppGlanceService *service, + const LauncherAppGlanceServiceHandlers *handlers, + void *context) { + if (!service) { + return; + } else if (!handlers) { + service->handlers = (LauncherAppGlanceServiceHandlers) {}; + } else { + service->handlers = *handlers; + } + service->handlers_context = context; +} + +void launcher_app_glance_service_deinit(LauncherAppGlanceService *service) { + if (!service) { + return; + } + + kino_player_deinit(&service->glance_reel_player); + event_service_client_unsubscribe(&service->glance_event_info); + prv_glance_cache_deinit(service); + prv_reset_slice_expiration_timer(service); + kino_reel_destroy(service->generic_glance_icon); +} diff --git a/src/fw/apps/system/launcher/default/app_glance_service.h b/src/fw/apps/system/launcher/default/app_glance_service.h new file mode 100644 index 0000000000..129eda0fb2 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_service.h @@ -0,0 +1,90 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/app_timer.h" +#include "applib/event_service_client.h" +#include "applib/ui/kino/kino_player.h" +#include "process_management/app_menu_data_source.h" +#include "util/list.h" + +//! Handler called when a glance in the service's cache changes, either because a glance's slice +//! expired or a glance was reloaded. +//! @param context Context provided when calling launcher_app_glance_service_set_handlers() +typedef void (*LauncherAppGlanceServiceGlanceChangedHandler)(void *context); + +typedef struct LauncherAppGlanceServiceHandlers { + LauncherAppGlanceServiceGlanceChangedHandler glance_changed; +} LauncherAppGlanceServiceHandlers; + +typedef struct LauncherAppGlanceService { + //! Cache of launcher app glances + ListNode *glance_cache; + //! Event service info used to subscribe to glance reload events + EventServiceInfo glance_event_info; + //! Client handlers set via launcher_app_glance_service_set_handlers() + LauncherAppGlanceServiceHandlers handlers; + //! Context for the handlers set via launcher_app_glance_service_set_handlers() + void *handlers_context; + //! The Unix epoch UTC timestamp of the next expiring slice of any of the glances in the cache + time_t next_slice_expiration_time; + //! App timer used for updating glances when a slice of a glance in the cache expires + AppTimer *slice_expiration_timer; + //! A generic icon to use for generic glances that can't otherwise load an icon + KinoReel *generic_glance_icon; + //! The resource ID of the generic glance icon + uint32_t generic_glance_icon_resource_id; + //! A \ref KinoReelPlayer for the currently selected glance + KinoPlayer glance_reel_player; +} LauncherAppGlanceService; + +//! Initialize the provided launcher app glance service. +//! @param service The launcher app glance service to initialize +//! @param generic_glance_icon_resource_id A resource ID to use if a generic launcher app glance +//! does not otherwise have an icon to draw +void launcher_app_glance_service_init(LauncherAppGlanceService *service, + uint32_t generic_glance_icon_resource_id); + +void launcher_app_glance_service_set_handlers(LauncherAppGlanceService *service, + const LauncherAppGlanceServiceHandlers *handlers, + void *context); + +//! Deinitialize the provided launcher app glance service. +//! @param service The launcher app glance service to deinitialize +void launcher_app_glance_service_deinit(LauncherAppGlanceService *service); + +//! Draw the launcher app glance for the provided app node. +//! @param service The service to use to draw the launcher app glance +//! @param ctx The graphics context to use to draw the launcher app glance +//! @param frame The frame in which to draw the launcher app glance +//! @param is_highlighted Whether or not the launcher app glance should be drawn highlighted +//! @param screen_center_y Screen Y center position for arc effect +//! @param node The \ref AppMenuNode of the app whose glance we should draw +void launcher_app_glance_service_draw_glance_for_app_node(LauncherAppGlanceService *service, + GContext *ctx, const GRect *frame, + bool is_highlighted, + int16_t screen_center_y, + AppMenuNode *node); + +//! Rewind any glance being played by the provided launcher app glance service. +//! @param service The service for which to rewind any playing glance +void launcher_app_glance_service_rewind_current_glance(LauncherAppGlanceService *service); + +//! Pause any glance being played by the provided launcher app glance service. +//! @param service The service for which to pause any playing glance +void launcher_app_glance_service_pause_current_glance(LauncherAppGlanceService *service); + +//! Start playing the current glance for the provided launcher app glance service. +//! @param service The service for which to play the current glance +void launcher_app_glance_service_play_current_glance(LauncherAppGlanceService *service); + +//! Play the launcher app glance for the provided app node. +//! @param service The service to use to play the launcher app glance +//! @param node The \ref AppMenuNode for the glance to play +void launcher_app_glance_service_play_glance_for_app_node(LauncherAppGlanceService *service, + AppMenuNode *node); + +//! Notify the service that a launcher app glance in its cache changed. +//! @param service The service to notify +void launcher_app_glance_service_notify_glance_changed(LauncherAppGlanceService *service); diff --git a/src/fw/apps/system/launcher/default/app_glance_settings.c b/src/fw/apps/system/launcher/default/app_glance_settings.c new file mode 100644 index 0000000000..9a1d239fb9 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_settings.c @@ -0,0 +1,438 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_settings.h" + +#include "app_glance_structured.h" +#include "menu_layer.h" + +#include "applib/battery_state_service.h" +#include "applib/graphics/gpath.h" +#include "apps/system/timeline/text_node.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/bluetooth/ble_hrm.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/size.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +// These dimensions are separate defines so we can use them to statically define the battery points +#define BATTERY_SILHOUETTE_ICON_WIDTH (16) +#define BATTERY_SILHOUETTE_ICON_HEIGHT (9) + +typedef struct LauncherAppGlanceSettingsState { + BatteryChargeState battery_charge_state; + bool is_pebble_app_connected; + bool is_airplane_mode_enabled; + bool is_quiet_time_enabled; +#ifdef CONFIG_HRM + bool is_sharing_hrm; +#endif +} LauncherAppGlanceSettingsState; + +typedef struct LauncherAppGlanceSettings { + char title[APP_NAME_SIZE_BYTES]; + char battery_percent_text[5]; //!< longest string is "100%" (4 characters + 1 for NULL terminator) + KinoReel *icon; + uint32_t icon_resource_id; + KinoReel *charging_indicator_icon; + uint8_t subtitle_font_height; + LauncherAppGlanceSettingsState glance_state; + EventServiceInfo battery_state_event_info; + EventServiceInfo pebble_app_event_info; + EventServiceInfo airplane_mode_event_info; + EventServiceInfo quiet_time_event_info; +#ifdef CONFIG_HRM + EventServiceInfo hrm_sharing_event_info; +#endif +} LauncherAppGlanceSettings; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(settings_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(settings_glance, title, NULL); +} + +static void prv_charging_icon_node_draw_cb(GContext *ctx, const GRect *rect, + PBL_UNUSED const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + + KinoReel *charging_indicator_icon = NULL_SAFE_FIELD_ACCESS(settings_glance, + charging_indicator_icon, NULL); + PBL_ASSERTN(charging_indicator_icon); + + + if (render && charging_indicator_icon) { + launcher_app_glance_structured_draw_icon(structured_glance, ctx, charging_indicator_icon, + rect->origin); + } + + if (size_out) { + *size_out = GSize(kino_reel_get_size(charging_indicator_icon).w, + settings_glance->subtitle_font_height); + } +} + +static void prv_battery_icon_node_draw_cb(GContext *ctx, const GRect *rect, + PBL_UNUSED const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + + const GSize battery_silhouette_icon_size = GSize(BATTERY_SILHOUETTE_ICON_WIDTH, + BATTERY_SILHOUETTE_ICON_HEIGHT); + + if (render) { + // This points array is static to help conserve stack usage + static const GPoint s_battery_silhouette_path_points[] = { + {0, 0}, + {BATTERY_SILHOUETTE_ICON_WIDTH - 1, 0}, + {BATTERY_SILHOUETTE_ICON_WIDTH - 1, 1}, + {BATTERY_SILHOUETTE_ICON_WIDTH + 1, 2}, + {BATTERY_SILHOUETTE_ICON_WIDTH + 1, BATTERY_SILHOUETTE_ICON_HEIGHT - 3}, + {BATTERY_SILHOUETTE_ICON_WIDTH - 1, BATTERY_SILHOUETTE_ICON_HEIGHT - 3}, + {BATTERY_SILHOUETTE_ICON_WIDTH - 1, BATTERY_SILHOUETTE_ICON_HEIGHT - 1}, + {0, BATTERY_SILHOUETTE_ICON_HEIGHT - 1}, + }; + GPath battery_silhouette_path = (GPath) { + .num_points = ARRAY_LENGTH(s_battery_silhouette_path_points), + .points = (GPoint *)s_battery_silhouette_path_points, + .offset = rect->origin, + }; + + const GColor battery_silhouette_color = + launcher_app_glance_structured_get_highlight_color(structured_glance); + const GColor battery_fill_color = + PBL_IF_COLOR_ELSE(gcolor_legible_over(battery_silhouette_color), GColorWhite); + + graphics_context_set_fill_color(ctx, battery_silhouette_color); + + // Draw the battery silhouette + const GRect battery_silhouette_frame = (GRect) { + .origin = rect->origin, + .size = battery_silhouette_icon_size, + }; + gpath_draw_filled(ctx, &battery_silhouette_path); + + // Inset the filled area + GRect battery_fill_rect = grect_inset_internal(battery_silhouette_frame, 3, 2); +#if !PBL_COLOR + // Fill the battery silhouette all the way for B&W, in order to make the BG black always. + graphics_context_set_fill_color(ctx, GColorBlack); + graphics_fill_rect(ctx, &battery_fill_rect); +#endif + + // Adjust fill width for charge percentage, never filling below 10% + uint8_t clipped_charge_percent = + settings_glance->glance_state.battery_charge_state.charge_percent; + clipped_charge_percent = CLIP(clipped_charge_percent, (uint8_t)10, (uint8_t)100); + battery_fill_rect.size.w = battery_fill_rect.size.w * clipped_charge_percent / (int16_t)100; + // Fill the battery silhouette based on the charge percent + graphics_context_set_fill_color(ctx, battery_fill_color); + graphics_fill_rect(ctx, &battery_fill_rect); + } + + if (size_out) { + *size_out = GSize(battery_silhouette_icon_size.w, settings_glance->subtitle_font_height); + } +} + +static void prv_battery_percent_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (settings_glance) { + buffer_size = MIN(sizeof(settings_glance->battery_percent_text), buffer_size); + strncpy(buffer, settings_glance->battery_percent_text, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_wrap_text_node_in_vertically_centered_container(GTextNode *node) { + const size_t max_vertical_container_nodes = 1; + GTextNodeVertical *vertical_container_node = + graphics_text_node_create_vertical(max_vertical_container_nodes); + vertical_container_node->vertical_alignment = GVerticalAlignmentCenter; + + graphics_text_node_container_add_child(&vertical_container_node->container, node); + + return &vertical_container_node->container.node; +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + PBL_ASSERTN(structured_glance); + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + PBL_ASSERTN(settings_glance); + + // Battery text (if not plugged in), battery icon, and (if plugged in) a lightning bolt icon + const size_t max_horizontal_nodes = 3; + GTextNodeHorizontal *horizontal_container_node = + graphics_text_node_create_horizontal(max_horizontal_nodes); + horizontal_container_node->horizontal_alignment = GTextAlignmentLeft; + + if (!settings_glance->glance_state.battery_charge_state.is_plugged) { + GTextNode *battery_percent_text_node = + launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_battery_percent_dynamic_text_node_update); + // Achieves the design spec'd 6 px horizontal spacing b/w the percent text and battery icon + battery_percent_text_node->margin.w = 4; + GTextNode *vertically_centered_battery_percent_text_node = + prv_wrap_text_node_in_vertically_centered_container(battery_percent_text_node); + graphics_text_node_container_add_child(&horizontal_container_node->container, + vertically_centered_battery_percent_text_node); + } + +#if PBL_DISPLAY_HEIGHT >= 200 + const int16_t subtitle_icon_offset_y = 5; +#else + const int16_t subtitle_icon_offset_y = 2; +#endif + + GTextNodeCustom *battery_icon_node = + graphics_text_node_create_custom(prv_battery_icon_node_draw_cb, structured_glance); + // Push the battery icon down to center it properly + battery_icon_node->node.offset.y += subtitle_icon_offset_y; + + // Achieves the design spec'd 6 px horizontal spacing b/w the battery icon and charging icon + battery_icon_node->node.margin.w = 7; + GTextNode *vertically_centered_battery_icon_node = + prv_wrap_text_node_in_vertically_centered_container(&battery_icon_node->node); + graphics_text_node_container_add_child(&horizontal_container_node->container, + vertically_centered_battery_icon_node); + + if (settings_glance->glance_state.battery_charge_state.is_plugged) { + GTextNodeCustom *charging_icon_node = + graphics_text_node_create_custom(prv_charging_icon_node_draw_cb, structured_glance); + // Push the charging icon down to center it properly + charging_icon_node->node.offset.y += subtitle_icon_offset_y; + GTextNode *vertically_centered_charging_icon_node = + prv_wrap_text_node_in_vertically_centered_container(&charging_icon_node->node); + graphics_text_node_container_add_child(&horizontal_container_node->container, + vertically_centered_charging_icon_node); + } + + return &horizontal_container_node->container.node; +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (settings_glance) { + event_service_client_unsubscribe(&settings_glance->battery_state_event_info); + event_service_client_unsubscribe(&settings_glance->pebble_app_event_info); + event_service_client_unsubscribe(&settings_glance->airplane_mode_event_info); + event_service_client_unsubscribe(&settings_glance->quiet_time_event_info); +#ifdef CONFIG_HRM + event_service_client_unsubscribe(&settings_glance->hrm_sharing_event_info); +#endif + kino_reel_destroy(settings_glance->icon); + kino_reel_destroy(settings_glance->charging_indicator_icon); + } + app_free(settings_glance); +} + +static void prv_set_glance_icon(LauncherAppGlanceSettings *settings_glance, + uint32_t new_icon_resource_id) { + if (settings_glance->icon_resource_id == new_icon_resource_id) { + // Nothing to do, bail out + return; + } + + // Destroy the existing icon + kino_reel_destroy(settings_glance->icon); + + // Set the new icon and record its resource ID + settings_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); + PBL_ASSERTN(settings_glance->icon); + settings_glance->icon_resource_id = new_icon_resource_id; +} + +static bool prv_mute_notifications_allow_calls_only(void) { + return (alerts_get_mask() == AlertMaskPhoneCalls); +} + +static uint32_t prv_get_resource_id_for_connectivity_status( + LauncherAppGlanceSettings *settings_glance) { +#ifdef CONFIG_HRM + if (settings_glance->glance_state.is_sharing_hrm) { + return RESOURCE_ID_CONNECTIVITY_SHARING_HRM; + } +#endif + if (settings_glance->glance_state.is_airplane_mode_enabled) { + return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE; + } else if (!settings_glance->glance_state.is_pebble_app_connected) { + return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED; + } else if (settings_glance->glance_state.is_quiet_time_enabled) { + return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND; + } else if (prv_mute_notifications_allow_calls_only()) { + return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY; + } else if (settings_glance->glance_state.is_pebble_app_connected) { + return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED; + } else { + WTF; + } +} + +static void prv_refresh_glance_content(LauncherAppGlanceSettings *settings_glance) { + // Update the battery percent text in the glance + const size_t battery_percent_text_size = sizeof(settings_glance->battery_percent_text); + snprintf(settings_glance->battery_percent_text, battery_percent_text_size, "%"PRIu8"%%", + settings_glance->glance_state.battery_charge_state.charge_percent); + + // Update the icon + const uint32_t new_icon_resource_id = + prv_get_resource_id_for_connectivity_status(settings_glance); + prv_set_glance_icon(settings_glance, new_icon_resource_id); +} + +static bool prv_is_pebble_app_connected(void) { + return (comm_session_get_system_session() != NULL); +} + +static void prv_event_handler(PebbleEvent *event, void *context) { + LauncherAppGlanceStructured *structured_glance = context; + PBL_ASSERTN(structured_glance); + + LauncherAppGlanceSettings *settings_glance = + launcher_app_glance_structured_get_data(structured_glance); + PBL_ASSERTN(settings_glance); + + switch (event->type) { + case PEBBLE_BATTERY_STATE_CHANGE_EVENT: + settings_glance->glance_state.battery_charge_state = battery_state_service_peek(); + break; + case PEBBLE_COMM_SESSION_EVENT: + if (event->bluetooth.comm_session_event.is_system) { + settings_glance->glance_state.is_pebble_app_connected = + event->bluetooth.comm_session_event.is_open; + } + break; + case PEBBLE_BT_STATE_EVENT: + settings_glance->glance_state.is_airplane_mode_enabled = bt_ctl_is_airplane_mode_on(); + break; + case PEBBLE_DO_NOT_DISTURB_EVENT: + settings_glance->glance_state.is_quiet_time_enabled = do_not_disturb_is_active(); + break; +#ifdef CONFIG_HRM + case PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT: { + const bool prev_is_sharing = settings_glance->glance_state.is_sharing_hrm; + const bool is_sharing = (event->bluetooth.le.hrm_sharing_state.subscription_count > 0); + if (prev_is_sharing == is_sharing) { + return; + } + settings_glance->glance_state.is_sharing_hrm = is_sharing; + break; + } +#endif + default: + WTF; + } + + // Refresh the content in the glance + prv_refresh_glance_content(settings_glance); + + // Broadcast to the service that we changed the glance + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); +} + +static void prv_subscribe_to_event(EventServiceInfo *event_service_info, PebbleEventType type, + LauncherAppGlanceStructured *structured_glance) { + PBL_ASSERTN(event_service_info); + + *event_service_info = (EventServiceInfo) { + .type = type, + .handler = prv_event_handler, + .context = structured_glance, + }; + + event_service_client_subscribe(event_service_info); +} + +static const LauncherAppGlanceStructuredImpl s_settings_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_settings_create(const AppMenuNode *node) { + PBL_ASSERTN(node); + + LauncherAppGlanceSettings *settings_glance = app_zalloc_check(sizeof(*settings_glance)); + + // Copy the name of the Settings app as the title + const size_t title_size = sizeof(settings_glance->title); + strncpy(settings_glance->title, node->name, title_size); + settings_glance->title[title_size - 1] = '\0'; + + // Load the charging indicator icon + settings_glance->charging_indicator_icon = + kino_reel_create_with_resource(RESOURCE_ID_BATTERY_CHARGING_ICON); + + // Cache the subtitle font height for simplifying layout calculations + settings_glance->subtitle_font_height = + fonts_get_font_height(fonts_get_system_font(LAUNCHER_MENU_LAYER_SUBTITLE_FONT)); + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_settings_structured_glance_impl, + should_consider_slices, settings_glance); + PBL_ASSERTN(structured_glance); + // Disable selection animations for the settings glance + structured_glance->selection_animation_disabled = true; + + // Set the first state of the glance + settings_glance->glance_state = (LauncherAppGlanceSettingsState) { + .battery_charge_state = battery_state_service_peek(), + .is_pebble_app_connected = prv_is_pebble_app_connected(), + .is_airplane_mode_enabled = bt_ctl_is_airplane_mode_on(), + .is_quiet_time_enabled = do_not_disturb_is_active(), +#ifdef CONFIG_HRM + .is_sharing_hrm = ble_hrm_is_sharing(), +#endif + }; + + // Refresh the glance now that we have set the first state of the glance + prv_refresh_glance_content(settings_glance); + + // Subscribe to the various events we care about + prv_subscribe_to_event(&settings_glance->battery_state_event_info, + PEBBLE_BATTERY_STATE_CHANGE_EVENT, structured_glance); + prv_subscribe_to_event(&settings_glance->pebble_app_event_info, PEBBLE_COMM_SESSION_EVENT, + structured_glance); + prv_subscribe_to_event(&settings_glance->airplane_mode_event_info, PEBBLE_BT_STATE_EVENT, + structured_glance); + prv_subscribe_to_event(&settings_glance->quiet_time_event_info, PEBBLE_DO_NOT_DISTURB_EVENT, + structured_glance); +#ifdef CONFIG_HRM + prv_subscribe_to_event(&settings_glance->hrm_sharing_event_info, + PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT, + structured_glance); +#endif + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_settings.h b/src/fw/apps/system/launcher/default/app_glance_settings.h new file mode 100644 index 0000000000..2a1480199b --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_settings.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_settings_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/app_glance_structured.c b/src/fw/apps/system/launcher/default/app_glance_structured.c new file mode 100644 index 0000000000..29b5aaf165 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_structured.c @@ -0,0 +1,569 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_structured.h" + +#include "app_glance_private.h" +#include "menu_layer.h" + +#include "applib/graphics/gdraw_command_transforms.h" +#include "applib/ui/kino/kino_reel_custom.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/timeline/attribute.h" +#include "shell/prefs.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" +#include "board/display.h" + +// Use display height to determine icon margins: larger displays use more margin +#if PBL_DISPLAY_HEIGHT >= 200 +#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN (9) +#else +#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN (5) +#endif + +typedef struct GenericGlanceIconDrawCommandProcessor { + GDrawCommandProcessor draw_command_processor; + GColor8 *luminance_tint_lookup_table; +} GenericGlanceIconDrawCommandProcessor; + +static void prv_structured_glance_icon_draw_command_processor_process_command( + GDrawCommandProcessor *processor, GDrawCommand *processed_command, + PBL_UNUSED size_t processed_command_max_size, PBL_UNUSED const GDrawCommandList *list, + PBL_UNUSED const GDrawCommand *command) { + GenericGlanceIconDrawCommandProcessor *processor_with_data = + (GenericGlanceIconDrawCommandProcessor *)processor; + const GColor8 *luminance_tint_lookup_table = processor_with_data->luminance_tint_lookup_table; + + // Luminance tint the fill color + const GColor fill_color = gdraw_command_get_fill_color(processed_command); + const GColor tinted_fill_color = + gcolor_perform_lookup_using_color_luminance_and_multiply_alpha(fill_color, + luminance_tint_lookup_table); + gdraw_command_replace_color(processed_command, fill_color, tinted_fill_color); + gdraw_command_set_fill_color(processed_command, tinted_fill_color); + + // Luminance tint the stroke color + const GColor stroke_color = gdraw_command_get_stroke_color(processed_command); + const GColor tinted_stroke_color = + gcolor_perform_lookup_using_color_luminance_and_multiply_alpha(stroke_color, + luminance_tint_lookup_table); + gdraw_command_set_stroke_color(processed_command, tinted_stroke_color); +} + +typedef struct GenericGlanceIconBitmapProcessor { + GBitmapProcessor bitmap_processor; + GCompOp saved_compositing_mode; + GColor saved_tint_color; + GColor desired_tint_color; +} GenericGlanceIconBitmapProcessor; + +static void prv_strucutred_glance_icon_bitmap_processor_pre_func( + GBitmapProcessor *processor, GContext *ctx, PBL_UNUSED const GBitmap **bitmap_to_use, + PBL_UNUSED GRect *global_grect_to_use) { + GenericGlanceIconBitmapProcessor *processor_with_data = + (GenericGlanceIconBitmapProcessor *)processor; + // Save the current compositing mode and tint color + processor_with_data->saved_compositing_mode = ctx->draw_state.compositing_mode; + processor_with_data->saved_tint_color = ctx->draw_state.tint_color; + // Set the compositing mode so that we luminance tint the icon to the specified color + ctx->draw_state.compositing_mode = GCompOpTintLuminance; + ctx->draw_state.tint_color = processor_with_data->desired_tint_color; +} + +static void prv_structured_glance_icon_bitmap_processor_post_func( + GBitmapProcessor *processor, GContext *ctx, PBL_UNUSED const GBitmap *bitmap_used, + PBL_UNUSED const GRect *global_clipped_grect_used) { + GenericGlanceIconBitmapProcessor *processor_with_data = + (GenericGlanceIconBitmapProcessor *)processor; + // Restore the saved compositing mode and tint color + ctx->draw_state.compositing_mode = processor_with_data->saved_compositing_mode; + ctx->draw_state.tint_color = processor_with_data->saved_tint_color; +} + +GColor launcher_app_glance_structured_get_highlight_color( + LauncherAppGlanceStructured *structured_glance) { +#if PBL_COLOR + if (structured_glance->glance.is_highlighted) { + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + return gcolor_legible_over(highlight_bg); + } else { + return GColorBlack; + } +#else + return structured_glance->glance.is_highlighted ? GColorWhite : GColorBlack; +#endif +} + +static GColor prv_get_icon_tint_color(LauncherAppGlanceStructured *structured_glance) { + // Icons should always be tinted black on color displays, white on B&W when highlighted + return PBL_IF_COLOR_ELSE(GColorBlack, + structured_glance->glance.is_highlighted ? GColorWhite : GColorBlack); +} + +void launcher_app_glance_structured_draw_icon(LauncherAppGlanceStructured *structured_glance, + GContext *ctx, KinoReel *icon, GPoint origin) { + const GColor desired_tint_color = prv_get_icon_tint_color(structured_glance); + + GenericGlanceIconBitmapProcessor structured_glance_icon_bitmap_processor = { + .bitmap_processor = { + .pre = prv_strucutred_glance_icon_bitmap_processor_pre_func, + .post = prv_structured_glance_icon_bitmap_processor_post_func, + }, + .desired_tint_color = desired_tint_color, + }; + + GColor8 luminance_tint_lookup_table[GCOLOR8_COMPONENT_NUM_VALUES] = {}; + gcolor_tint_luminance_lookup_table_init(desired_tint_color, luminance_tint_lookup_table); + + GenericGlanceIconDrawCommandProcessor strucutred_glance_icon_draw_command_processor = { + .draw_command_processor.command = + prv_structured_glance_icon_draw_command_processor_process_command, + .luminance_tint_lookup_table = luminance_tint_lookup_table, + }; + + KinoReelProcessor structured_glance_icon_processor = { + .bitmap_processor = &structured_glance_icon_bitmap_processor.bitmap_processor, + .draw_command_processor = + &strucutred_glance_icon_draw_command_processor.draw_command_processor, + }; + + // Draw the glance's icon, luminance tinting its colors according to the glance's highlight + kino_reel_draw_processed(icon, ctx, origin, &structured_glance_icon_processor); +} + +static void prv_structured_glance_icon_node_draw_cb(GContext *ctx, const GRect *rect, + PBL_UNUSED const GTextNodeDrawConfig *config, + bool render, GSize *size_out, void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + KinoReel *icon = NULL; + if (structured_glance && structured_glance->impl && structured_glance->impl->get_icon) { + icon = structured_glance->impl->get_icon(structured_glance); + } + + if (render && icon) { + // Center the frame in which we'll draw the icon + GRect icon_frame = (GRect) { .size = kino_reel_get_size(icon) }; + grect_align(&icon_frame, rect, GAlignCenter, false /* clip */); + + // Save the GContext's clip box and override it so we clip the icon to the max icon size + const GRect saved_clip_box = ctx->draw_state.clip_box; + ctx->draw_state.clip_box.origin = gpoint_add(ctx->draw_state.drawing_box.origin, + rect->origin); + ctx->draw_state.clip_box.size = rect->size; + + // Prevent drawing outside of the existing clip box + grect_clip(&ctx->draw_state.clip_box, &saved_clip_box); + + // Draw the icon! + launcher_app_glance_structured_draw_icon(structured_glance, ctx, icon, icon_frame.origin); + + // Restore the saved clip box + ctx->draw_state.clip_box = saved_clip_box; + } + + if (size_out) { + *size_out = structured_glance->icon_max_size; + } +} + +static GTextNode *prv_structured_glance_create_text_node( + LauncherAppGlanceStructured *structured_glance, GFont font, size_t buffer_size, + GTextNodeTextDynamicUpdate update) { + if (!structured_glance) { + return NULL; + } + GTextNodeTextDynamic *dynamic_text_node = + graphics_text_node_create_text_dynamic(buffer_size, update, structured_glance); + GTextNodeText *underlying_text_node_text = &dynamic_text_node->text; + underlying_text_node_text->font = font; + underlying_text_node_text->color = + launcher_app_glance_structured_get_highlight_color(structured_glance); + underlying_text_node_text->overflow = GTextOverflowModeTrailingEllipsis; + underlying_text_node_text->node.offset = GPoint(0, -fonts_get_font_cap_offset(font)); + underlying_text_node_text->max_size.h = fonts_get_font_height(font); + return &underlying_text_node_text->node; +} + +static void prv_structured_glance_title_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + const char *title = NULL; + if (structured_glance && structured_glance->impl && structured_glance->impl->get_title) { + title = structured_glance->impl->get_title(structured_glance); + } + if (title) { + strncpy(buffer, title, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_structured_glance_create_title_text_node( + LauncherAppGlanceStructured *structured_glance) { + return prv_structured_glance_create_text_node( + structured_glance, structured_glance->title_font, APP_NAME_SIZE_BYTES, + prv_structured_glance_title_dynamic_text_node_update); +} + +typedef struct ScrollAnimationVars { + int16_t total_px_to_scroll; + int16_t current_offset; + uint32_t duration_ms; +} ScrollAnimationVars; + +//! Calculates the variables of a text scrolling animation that proceeds as follows: +//! - Pauses a bit at the start +//! - Scrolls the provided text at a moderate pace up to 3x the width of the provided draw_box +//! - Pauses a bit when the end of the scrollable text is reached +//! - Rewinds the text back to a zero offset at a rapid pace +//! Returns true if conditions are right for scrolling and output arguments should be used, +//! false otherwise. +static bool prv_get_text_scroll_vars(GContext *ctx, uint32_t cumulative_elapsed_ms, + const char *text, const GRect *draw_box, GFont font, + GTextAlignment text_alignment, GTextOverflowMode overflow_mode, + TextLayoutExtended *layout, ScrollAnimationVars *vars_out) { + if (!vars_out) { + return false; + } + + // Allow for showing up to 3x the width of the draw_box for the text + const int16_t max_text_width = draw_box->size.w * (int16_t)3; + const GRect max_text_box = (GRect) { .size = GSize(max_text_width, draw_box->size.h) }; + const int16_t scroll_visible_text_width = + graphics_text_layout_get_max_used_size(ctx, text, font, max_text_box, + overflow_mode, text_alignment, + (GTextLayoutCacheRef)layout).w; + + if (scroll_visible_text_width <= draw_box->size.w) { + // No need to scroll because text fits completely in the provided draw_box + return false; + } + + // This is the amount we'll scroll the text from start to end to show all of the + // scroll_visible_text_width in the provided draw_box + const int16_t total_px_to_scroll = scroll_visible_text_width - draw_box->size.w; + vars_out->total_px_to_scroll = total_px_to_scroll; + + // These values were tuned with feedback from Design + const uint32_t normal_scroll_speed_ms_per_px = 20; + const uint32_t normal_scroll_duration_ms = total_px_to_scroll * normal_scroll_speed_ms_per_px; + const uint32_t rewind_scroll_speed_ms_per_px = 2; + const uint32_t rewind_scroll_duration_ms = total_px_to_scroll * rewind_scroll_speed_ms_per_px; + const uint32_t pause_at_start_ms = 600; + const uint32_t pause_at_end_ms = 750; + + const uint32_t scroll_duration_ms = + pause_at_start_ms + normal_scroll_duration_ms + pause_at_end_ms + rewind_scroll_duration_ms; + vars_out->duration_ms = scroll_duration_ms; + + // Technically mod isn't necessary right now, but it's needed for looping eventually (PBL-40544) + int64_t elapsed_ms = cumulative_elapsed_ms % scroll_duration_ms; + const uint32_t end_of_normal_scroll_duration_ms = pause_at_start_ms + normal_scroll_duration_ms; + bool rewind = false; + if (WITHIN(elapsed_ms, 0, end_of_normal_scroll_duration_ms)) { + elapsed_ms = MAX(elapsed_ms - pause_at_start_ms, 0); + } else if (elapsed_ms < end_of_normal_scroll_duration_ms + pause_at_end_ms) { + elapsed_ms = normal_scroll_duration_ms; + } else { + elapsed_ms = scroll_duration_ms - elapsed_ms; + rewind = true; + } + + const uint32_t elapsed_normalized = + ((uint32_t)elapsed_ms * ANIMATION_NORMALIZED_MAX) / + (rewind ? rewind_scroll_duration_ms : normal_scroll_duration_ms); + vars_out->current_offset = interpolate_int16(elapsed_normalized, 0, total_px_to_scroll); + + return true; +} + +//! Currently the subtitle scrolling drives the duration of the overall glance selection animation +//! because we only scroll once, and since we don't know what we're scrolling until this function +//! is called, we need to record the duration of the scrolling animation in this function so the +//! glance's KinoReel reports the correct duration for the overall selection animation. +static void prv_adjust_subtitle_node_for_scrolling_animation( + LauncherAppGlanceStructured *structured_glance, GContext *ctx, GTextNodeText *node_text, + const char *text, const GRect *draw_box) { + const uint32_t cumulative_elapsed_ms = structured_glance->selection_animation_elapsed_ms; + + ScrollAnimationVars vars; + if (!prv_get_text_scroll_vars(ctx, + cumulative_elapsed_ms, + text, draw_box, node_text->font, + node_text->alignment, node_text->overflow, + &structured_glance->subtitle_scroll_calc_text_layout, &vars)) { + // No need to scroll because text fits completely on-screen, set the selection animation + // duration to 0 and bail out + structured_glance->selection_animation_duration_ms = 0; + return; + } + + // Assumes that the default offset.x for the subtitle node is 0, which is true for generic glances + node_text->node.offset.x = -vars.current_offset; + // Assumes that the default margin.w for the subtitle node is 0, which is true for generic glances + node_text->node.margin.w = (vars.current_offset != 0) ? -vars.total_px_to_scroll : (int16_t)0; + + // Record any change in the selection animation's duration + LauncherAppGlanceService *service = structured_glance->glance.service; + if (vars.duration_ms != structured_glance->selection_animation_duration_ms) { + const uint32_t previous_selection_animation_duration_ms = + structured_glance->selection_animation_duration_ms; + structured_glance->selection_animation_duration_ms = vars.duration_ms; + // If we're starting a new scroll or a scroll is currently in-progress, pause and then + // play the animation so it is updated with the new duration (e.g. so we don't stop in a weird + // place because the previous duration is shorter than the new one) + if ((previous_selection_animation_duration_ms == 0) || (cumulative_elapsed_ms != 0)) { + launcher_app_glance_service_pause_current_glance(service); + launcher_app_glance_service_play_current_glance(service); + } + } +} + +static void prv_structured_glance_subtitle_dynamic_text_node_update( + GContext *ctx, GTextNode *node, const GRect *box, const GTextNodeDrawConfig *config, + bool render, char *buffer, size_t buffer_size, void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + if (structured_glance->subtitle_update) { + structured_glance->subtitle_update(ctx, node, box, config, render, buffer, buffer_size, + user_data); + } + GTextNodeText *node_text = (GTextNodeText *)node; + if (!render) { + prv_adjust_subtitle_node_for_scrolling_animation(structured_glance, ctx, node_text, buffer, + box); + } +} + +GTextNode *launcher_app_glance_structured_create_subtitle_text_node( + LauncherAppGlanceStructured *structured_glance, GTextNodeTextDynamicUpdate update) { + const size_t subtitle_buffer_size = ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1; + structured_glance->subtitle_update = update; + GTextNode *node = prv_structured_glance_create_text_node( + structured_glance, structured_glance->subtitle_font, subtitle_buffer_size, + prv_structured_glance_subtitle_dynamic_text_node_update); + // Clip subtitle text nodes to their draw box since we scroll them if they're too long + node->clip = true; + return node; +} + +static GTextNode *prv_create_structured_glance_title_subtitle_node( + LauncherAppGlanceStructured *structured_glance, const GRect *glance_frame) { + // Title node and subtitle node + const size_t max_vertical_nodes = 2; + GTextNodeVertical *vertical_node = graphics_text_node_create_vertical(max_vertical_nodes); + vertical_node->vertical_alignment = GVerticalAlignmentCenter; + + GTextNode *title_node = prv_structured_glance_create_title_text_node(structured_glance); + // We require a valid title node + PBL_ASSERTN(title_node); + + // Push the margin a bit closer +#if PBL_DISPLAY_HEIGHT >= 200 && PBL_RECT + title_node->margin.h = -3; +#endif + + title_node->offset.y -= 1; + graphics_text_node_container_add_child(&vertical_node->container, title_node); + + GTextNode *subtitle_node = NULL; + if (structured_glance->impl && structured_glance->impl->create_subtitle_node) { + subtitle_node = structured_glance->impl->create_subtitle_node(structured_glance); + } + // The subtitle node is optional + if (subtitle_node) { + graphics_text_node_container_add_child(&vertical_node->container, subtitle_node); + } + + // Set the vertical container's width to exactly what it should be so it doesn't resize based + // on its changing content (e.g. scrolling subtitle) + vertical_node->container.size.w = + glance_frame->size.w - structured_glance->icon_horizontal_margin - + structured_glance->icon_max_size.w; + + return &vertical_node->container.node; +} + +// NOINLINE to save stack; on Spalding this can be enough to push us over the edge. +static NOINLINE GTextNode *prv_create_structured_glance_node( + LauncherAppGlanceStructured *structured_glance, const GRect *glance_frame) { + // Icon node and title/subtitle nodes + const size_t max_horizontal_nodes = 2; + GTextNodeHorizontal *horizontal_node = graphics_text_node_create_horizontal(max_horizontal_nodes); + horizontal_node->horizontal_alignment = GTextAlignmentLeft; + + // This vertical node is just a container to vertically center the icon node + const size_t max_vertical_icon_container_nodes = 1; + GTextNodeVertical *vertical_icon_container_node = + graphics_text_node_create_vertical(max_vertical_icon_container_nodes); + vertical_icon_container_node->vertical_alignment = GVerticalAlignmentCenter; + + // This horizontal node is just a container to horizontally center the icon node + const size_t max_horizontal_icon_container_nodes = 1; + GTextNodeHorizontal *horizontal_icon_container_node = + graphics_text_node_create_horizontal(max_horizontal_icon_container_nodes); + horizontal_icon_container_node->horizontal_alignment = GTextAlignmentCenter; + graphics_text_node_container_add_child(&vertical_icon_container_node->container, + &horizontal_icon_container_node->container.node); + + GTextNodeCustom *icon_node = + graphics_text_node_create_custom(prv_structured_glance_icon_node_draw_cb, structured_glance); + icon_node->node.margin.w = structured_glance->icon_horizontal_margin; + // The +1 is to force a rounding up. This way, 3 pixels extra will move closer to the screen + // edge, instead of closer to the text. + icon_node->node.offset.x -= (LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN - + structured_glance->icon_horizontal_margin + 1) / 2; + graphics_text_node_container_add_child(&horizontal_icon_container_node->container, + &icon_node->node); + + graphics_text_node_container_add_child(&horizontal_node->container, + &vertical_icon_container_node->container.node); + + GTextNode *title_subtitle_node = + prv_create_structured_glance_title_subtitle_node(structured_glance, glance_frame); + graphics_text_node_container_add_child(&horizontal_node->container, + title_subtitle_node); + + return &horizontal_node->container.node; +} + +static void prv_draw_processed(KinoReel *reel, GContext *ctx, GPoint offset, + PBL_UNUSED KinoReelProcessor *processor) { + LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); + if (!structured_glance) { + return; + } + + GRect glance_frame = (GRect) { .origin = offset, .size = structured_glance->glance.size }; +#if PBL_ROUND && PBL_DISPLAY_HEIGHT >= 200 + // For a circle: x = R - sqrt(R^2 - (y - R)^2), where R = display_size / 2 + const int16_t radius = PBL_DISPLAY_HEIGHT / 2; + + // Use actual screen Y position for smooth arc during scroll animations + const int16_t y_offset_from_center = structured_glance->glance.screen_center_y - radius; + + // Calculate how far the circle edge is from the display edge at this Y + // circle_x = R - sqrt(R^2 - y_offset^2) + const int32_t y_offset_sq = (int32_t)y_offset_from_center * y_offset_from_center; + const int32_t radius_sq = (int32_t)radius * radius; + // Clamp to avoid negative sqrt when row is outside the circle (during scroll animations) + const int32_t sqrt_arg = MAX(0, radius_sq - y_offset_sq); + const int16_t circle_inset = radius - integer_sqrt(sqrt_arg); + // Add base inset for padding from the actual circle edge + // Extra 2px shift for non-center rows to push content slightly more inward + const int16_t base_inset = 10; + const int16_t horizontal_inset = base_inset + circle_inset; +#elif PBL_DISPLAY_HEIGHT >= 200 && PBL_RECT + const int16_t horizontal_inset = 10; +#else + const int16_t horizontal_inset = PBL_IF_RECT_ELSE(6, 23); +#endif + glance_frame = grect_inset_internal(glance_frame, horizontal_inset, 0); + + GTextNode *structured_glance_node = prv_create_structured_glance_node(structured_glance, + &glance_frame); + if (structured_glance_node) { + graphics_text_node_draw(structured_glance_node, ctx, &glance_frame, NULL, NULL); + } + graphics_text_node_destroy(structured_glance_node); +} + +static uint32_t prv_get_elapsed(KinoReel *reel) { + LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); + return NULL_SAFE_FIELD_ACCESS(structured_glance, selection_animation_elapsed_ms, 0); +} + +static bool prv_set_elapsed(KinoReel *reel, uint32_t elapsed_ms) { + LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); + if (!structured_glance) { + return false; + } + if (!structured_glance->selection_animation_disabled) { + structured_glance->selection_animation_elapsed_ms = elapsed_ms; + } + // We assume the selection animation loops so that it's last frame is the same as its first frame, + // so let's enforce that here so the animation update code above works properly + if (structured_glance->selection_animation_elapsed_ms == kino_reel_get_duration(reel)) { + structured_glance->selection_animation_elapsed_ms = 0; + } + return !structured_glance->selection_animation_disabled; +} + +static uint32_t prv_get_duration(KinoReel *reel) { + // TODO PBL-40544: Loop the selection animation + LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); + return NULL_SAFE_FIELD_ACCESS(structured_glance, selection_animation_duration_ms, 0); +} + +static void prv_destructor(KinoReel *reel) { + LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); + if (structured_glance && structured_glance->impl && structured_glance->impl->destructor) { + structured_glance->impl->destructor(structured_glance); + } +} + +static const KinoReelImpl s_launcher_app_glance_structured_reel_impl = { + .reel_type = KinoReelTypeCustom, + .get_size = launcher_app_glance_get_size_for_reel, + .draw_processed = prv_draw_processed, + .destructor = prv_destructor, + .get_duration = prv_get_duration, + .get_elapsed = prv_get_elapsed, + .set_elapsed = prv_set_elapsed, +}; + +LauncherAppGlanceStructured *launcher_app_glance_structured_create( + const Uuid *uuid, const LauncherAppGlanceStructuredImpl *impl, bool should_consider_slices, + void *data) { + PBL_ASSERTN(uuid); + LauncherAppGlanceStructured *structured_glance = app_zalloc_check(sizeof(*structured_glance)); + const LauncherAppGlanceHandlers *base_handlers = impl ? &impl->base_handlers : NULL; + structured_glance->impl = impl; + structured_glance->data = data; + structured_glance->icon_max_size = LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE; + structured_glance->icon_horizontal_margin = + LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN; + structured_glance->title_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_TITLE_FONT); + structured_glance->subtitle_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_SUBTITLE_FONT); + KinoReel *glance_impl = kino_reel_custom_create(&s_launcher_app_glance_structured_reel_impl, + structured_glance); + // Now that we've setup the structured glance's fields, initialize the LauncherAppGlance + launcher_app_glance_init(&structured_glance->glance, uuid, glance_impl, should_consider_slices, + base_handlers); + return structured_glance; +} + +void *launcher_app_glance_structured_get_data(LauncherAppGlanceStructured *structured_glance) { + return NULL_SAFE_FIELD_ACCESS(structured_glance, data, NULL); +} + +void launcher_app_glance_structured_notify_service_glance_changed( + LauncherAppGlanceStructured *structured_glance) { + if (!structured_glance) { + return; + } + launcher_app_glance_notify_service_glance_changed(&structured_glance->glance); +} + +void launcher_app_glance_structured_set_icon_max_size( + LauncherAppGlanceStructured *structured_glance, GSize new_size) { + if (!structured_glance) { + return; + } + + structured_glance->icon_max_size = new_size; + + const int width_diff = structured_glance->icon_max_size.w - + LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE.w; + structured_glance->icon_horizontal_margin = + LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN - width_diff; + + if (structured_glance->icon_horizontal_margin < 0) { + structured_glance->icon_horizontal_margin = 0; + } +} diff --git a/src/fw/apps/system/launcher/default/app_glance_structured.h b/src/fw/apps/system/launcher/default/app_glance_structured.h new file mode 100644 index 0000000000..0b010c15c1 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_structured.h @@ -0,0 +1,135 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "applib/ui/kino/kino_reel.h" +#include "apps/system/timeline/text_node.h" +#include "util/uuid.h" + +#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE \ + (GSize(ATTRIBUTE_ICON_TINY_SIZE_PX, ATTRIBUTE_ICON_TINY_SIZE_PX)) +#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_LEGACY_MAX_SIZE \ + (GSize(28, 28)) + +//! Forward declaration +typedef struct LauncherAppGlanceStructured LauncherAppGlanceStructured; + +//! Function used to get the title to display in the structured launcher app glance. +//! @param structured_glance The structured glance for which to get the title +//! @return The title to display in the structured glance; will be copied so can be short-lived +typedef const char *(*LauncherAppGlanceStructuredTitleGetter) + (LauncherAppGlanceStructured *structured_glance); + +//! Function used to create subtitle text nodes for the structured launcher app glance. +//! @param structured_glance The structured glance for which to create a text node +//! @return The text node the structured glance should use +typedef GTextNode *(*LauncherAppGlanceStructuredTextNodeConstructor) + (LauncherAppGlanceStructured *structured_glance); + +//! Function called when the structured launcher app glance is being destroyed. +//! @param structured_glance The structured glance that is being destroyed +//! @note This function should NOT free the structured glance; only deinit impl-specific things +typedef void (*LauncherAppGlanceStructuredDestructor) + (LauncherAppGlanceStructured *structured_glance); + +//! Function called to request the icon that should be drawn in the structured glance. +//! @param structured_glance The structured glance requesting the icon to draw +//! @return The icon to draw in the structured glance +typedef KinoReel *(*LauncherAppGlanceStructuredIconGetter) + (LauncherAppGlanceStructured *structured_glance); + +typedef struct LauncherAppGlanceStructuredImpl { + //! Base handlers for the underlying LauncherAppGlance of the structured glance + LauncherAppGlanceHandlers base_handlers; + //! Called to get the icon to draw in the structured glance + LauncherAppGlanceStructuredIconGetter get_icon; + //! Called to create the title text node for the structured glance; must return a valid text node + LauncherAppGlanceStructuredTitleGetter get_title; + //! Called to create the subtitle text node for the structured glance + LauncherAppGlanceStructuredTextNodeConstructor create_subtitle_node; + //! Called when the structured glance is being destroyed; should NOT free the structured glance + LauncherAppGlanceStructuredDestructor destructor; +} LauncherAppGlanceStructuredImpl; + +struct LauncherAppGlanceStructured { + //! The underlying launcher app glance + LauncherAppGlance glance; + //! The implementation of the structured app glance + const LauncherAppGlanceStructuredImpl *impl; + //! The user-provided data for the structured app glance's implementation + void *data; + //! Cached title font that will be used when drawing the structured app glance + GFont title_font; + //! Cached subtitle font that will be used when drawing the structured app glance + GFont subtitle_font; + // Cached text layout used when calculating the width of the subtitle during scrolling + TextLayoutExtended subtitle_scroll_calc_text_layout; + //! Optional implementation-provided dynamic text node update callback for the subtitle + GTextNodeTextDynamicUpdate subtitle_update; + //! Whether or not selection animations should be disabled for this structured app glance + bool selection_animation_disabled; + //! Current cumulative elapsed time (in milliseconds) of the glance's selection animation + uint32_t selection_animation_elapsed_ms; + //! Duration (in milliseconds) of the glance's selection animation + uint32_t selection_animation_duration_ms; + //! Maximum size an icon may have + GSize icon_max_size; + //! Horizontal margin for the icon + int32_t icon_horizontal_margin; +}; + +_Static_assert((offsetof(LauncherAppGlanceStructured, glance) == 0), + "LauncherAppGlance is not the first field of LauncherAppGlanceStructured"); + +//! Create a structured launcher app glance for the provided app menu node. +//! @param uuid The UUID of the app for which to initialize this structured glance +//! @param impl The implementation of the structured glance +//! @param should_consider_slices Whether or not the structured glance should consider slices +//! @param data Custom data to use in the implementation of the structured glance +LauncherAppGlanceStructured *launcher_app_glance_structured_create( + const Uuid *uuid, const LauncherAppGlanceStructuredImpl *impl, bool should_consider_slices, + void *data); + +//! Get the user-provided data for the implementation of a structured launcher app glance. +//! @param structured_glance The structured glance for which to get the user-provided data +//! @return The user-provided data +void *launcher_app_glance_structured_get_data(LauncherAppGlanceStructured *structured_glance); + +//! Get the highlight color that should be used for the provided structured launcher app glance. +//! @param structured_glance The structured glance for which to get the highlight color +//! @return The highlight color to use when drawing the structured glance +GColor launcher_app_glance_structured_get_highlight_color( + LauncherAppGlanceStructured *structured_glance); + +//! Draw an icon in the structured launcher app glance. +//! @param structured_glance The structured glance in which to draw an icon +//! @param ctx The graphics context to use when drawing the icon +//! @param icon The icon to draw +//! @param origin The origin at which to draw the icon +void launcher_app_glance_structured_draw_icon(LauncherAppGlanceStructured *structured_glance, + GContext *ctx, KinoReel *icon, GPoint origin); + +//! Create a subtitle text node for a structured launcher app glance. It is expected that subclasses +//! of \ref LauncherAppGlanceStructured will use this function in their own custom subtitle node +//! creation functions they specify in their \ref LauncherAppGlanceStructuredImpl. Calling this +//! function saves the provided callback to the \ref LauncherAppGlanceStructured struct, thus you +//! should only call this once per structured glance implementation. +//! @param structured_glance The structured glance for which to create a subtitle text node +//! @param update Callback for updating the text buffer of the text node +//! @return The resulting subtitle text node, or NULL upon failure +GTextNode *launcher_app_glance_structured_create_subtitle_text_node( + LauncherAppGlanceStructured *structured_glance, GTextNodeTextDynamicUpdate update); + +//! Notify the structured launcher app glance's service that its content has changed. +//! @param structured_glance The structured glance that has changed +void launcher_app_glance_structured_notify_service_glance_changed( + LauncherAppGlanceStructured *structured_glance); + +//! Change the icon max size, and adjust related settings. +//! @param structured_glance The structured glance for which to change the icon size +//! @param new_size The new maximum size allowed for the icon +void launcher_app_glance_structured_set_icon_max_size( + LauncherAppGlanceStructured *structured_glance, GSize new_size); diff --git a/src/fw/apps/system/launcher/default/app_glance_watchfaces.c b/src/fw/apps/system/launcher/default/app_glance_watchfaces.c new file mode 100644 index 0000000000..6adb1a4f3b --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_watchfaces.c @@ -0,0 +1,107 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_watchfaces.h" + +#include "app_glance_structured.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "shell/normal/watchface.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +typedef struct LauncherAppGlanceWatchfaces { + char title[APP_NAME_SIZE_BYTES]; + char subtitle[APP_NAME_SIZE_BYTES]; + KinoReel *icon; +} LauncherAppGlanceWatchfaces; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWatchfaces *watchfaces_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(watchfaces_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWatchfaces *watchfaces_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(watchfaces_glance, title, NULL); +} + +static void prv_watchfaces_glance_subtitle_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceWatchfaces *watchfaces_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (watchfaces_glance) { + strncpy(buffer, watchfaces_glance->subtitle, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_watchfaces_glance_subtitle_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWatchfaces *watchfaces_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (watchfaces_glance) { + kino_reel_destroy(watchfaces_glance->icon); + } + app_free(watchfaces_glance); +} + +static void prv_update_active_watchface_title(LauncherAppGlanceWatchfaces *watchfaces_glance) { + const AppInstallId selected_watch_id = watchface_get_default_install_id(); + + AppInstallEntry entry; + if (app_install_get_entry_for_install_id(selected_watch_id, &entry)) { + const size_t watchfaces_subtitle_size = sizeof(watchfaces_glance->subtitle); + strncpy(watchfaces_glance->subtitle, entry.name, watchfaces_subtitle_size); + watchfaces_glance->subtitle[watchfaces_subtitle_size - 1] = '\0'; + } +} + +static const LauncherAppGlanceStructuredImpl s_watchfaces_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_watchfaces_create(const AppMenuNode *node) { + PBL_ASSERTN(node); + + LauncherAppGlanceWatchfaces *watchfaces_glance = app_zalloc_check(sizeof(*watchfaces_glance)); + + // Copy the name of the Watchfaces app as the title + const size_t title_size = sizeof(watchfaces_glance->title); + strncpy(watchfaces_glance->title, node->name, title_size); + watchfaces_glance->title[title_size - 1] = '\0'; + + // Create the icon for the Watchfaces app + watchfaces_glance->icon = kino_reel_create_with_resource_system(node->app_num, + node->icon_resource_id); + PBL_ASSERTN(watchfaces_glance->icon); + + // Update the active watchface title in the glance's subtitle + prv_update_active_watchface_title(watchfaces_glance); + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_watchfaces_structured_glance_impl, + should_consider_slices, watchfaces_glance); + PBL_ASSERTN(structured_glance); + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_watchfaces.h b/src/fw/apps/system/launcher/default/app_glance_watchfaces.h new file mode 100644 index 0000000000..2967998db5 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_watchfaces.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_watchfaces_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/app_glance_weather.c b/src/fw/apps/system/launcher/default/app_glance_weather.c new file mode 100644 index 0000000000..3ce71521d2 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_weather.c @@ -0,0 +1,179 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_weather.h" + +#include "app_glance_structured.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_types.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +// Max size of the temperature and phrase displayed together +#define WEATHER_APP_GLANCE_MAX_STRING_BUFFER_SIZE (WEATHER_SERVICE_MAX_SHORT_PHRASE_BUFFER_SIZE + 5) + +typedef struct LauncherAppGlanceWeather { + char title[APP_NAME_SIZE_BYTES]; + char fallback_title[APP_NAME_SIZE_BYTES]; + char subtitle[WEATHER_APP_GLANCE_MAX_STRING_BUFFER_SIZE]; + KinoReel *icon; + uint32_t icon_resource_id; + EventServiceInfo weather_event_info; +} LauncherAppGlanceWeather; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWeather *weather_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(weather_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWeather *weather_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(weather_glance, title, NULL); +} + +static void prv_weather_glance_subtitle_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceWeather *weather_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (weather_glance) { + strncpy(buffer, weather_glance->subtitle, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_weather_glance_subtitle_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWeather *weather_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (weather_glance) { + event_service_client_unsubscribe(&weather_glance->weather_event_info); + kino_reel_destroy(weather_glance->icon); + } + app_free(weather_glance); +} + +static uint32_t prv_get_weather_icon_resource_id_for_type(WeatherType type) { + AppResourceInfo res_info; + const bool lookup_success = + timeline_resources_get_id_system(weather_type_get_timeline_resource_id(type), + TimelineResourceSizeTiny, SYSTEM_APP, &res_info); + return lookup_success ? res_info.res_id : (uint32_t)RESOURCE_ID_INVALID; +} + +static void prv_weather_event_handler(PBL_UNUSED PebbleEvent *event, void *context) { + LauncherAppGlanceStructured *structured_glance = context; + LauncherAppGlanceWeather *weather_glance = + launcher_app_glance_structured_get_data(structured_glance); + PBL_ASSERTN(weather_glance); + + WeatherLocationForecast *forecast = weather_service_create_default_forecast(); + + // Update the icon for the forecast's weather type + const WeatherType weather_type = NULL_SAFE_FIELD_ACCESS(forecast, current_weather_type, + WeatherType_Unknown); + const uint32_t new_weather_icon_resource_id = + prv_get_weather_icon_resource_id_for_type(weather_type); + if (weather_glance->icon_resource_id != new_weather_icon_resource_id) { + kino_reel_destroy(weather_glance->icon); + weather_glance->icon = kino_reel_create_with_resource(new_weather_icon_resource_id); + weather_glance->icon_resource_id = new_weather_icon_resource_id; + } + + // Zero out the glance's title buffer + const size_t weather_glance_title_size = sizeof(weather_glance->title); + memset(weather_glance->title, 0, weather_glance_title_size); + // Choose the title we should display based on whether or not we have a forecast + const char *title = NULL_SAFE_FIELD_ACCESS(forecast, location_name, + weather_glance->fallback_title); + // Subtract 1 from the size as a shortcut for null terminating the title since we zero it out + // above + strncpy(weather_glance->title, title, weather_glance_title_size - 1); + + // Zero out the glance's subtitle buffer + const size_t weather_glance_subtitle_size = sizeof(weather_glance->subtitle); + memset(weather_glance->subtitle, 0, weather_glance_subtitle_size); + // We'll only set the subtitle if we have a default forecast + if (forecast) { + if (forecast->current_temp == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { + /// Shown when the current temperature is unknown + const char *no_temperature_string = i18n_get("--°", weather_glance); + // Subtract 1 from the size as a shortcut for null terminating the subtitle since we zero it + // out above + strncpy(weather_glance->subtitle, no_temperature_string, weather_glance_subtitle_size - 1); + } else { + /// Shown when today's temperature and conditions phrase is known (e.g. "52° - Fair") + const char *temp_and_phrase_formatter = i18n_get("%i° - %s", weather_glance); + /// Today's current temperature (e.g. "68°") + const char *temp_only_formatter = i18n_get("%i°", weather_glance); + const char *localized_phrase = i18n_get(forecast->current_weather_phrase, weather_glance); + const char *formatter_string = strlen(localized_phrase) ? temp_and_phrase_formatter : + temp_only_formatter; + // It's safe to pass more arguments to snprintf() than might be used by formatter_string + snprintf(weather_glance->subtitle, weather_glance_subtitle_size, formatter_string, + forecast->current_temp, localized_phrase); + } + } + + i18n_free_all(weather_glance); + + weather_service_destroy_default_forecast(forecast); + + // Broadcast to the service that we changed the glance + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); +} + +static const LauncherAppGlanceStructuredImpl s_weather_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_weather_create(const AppMenuNode *node) { + if (!node) { + return NULL; + } + + LauncherAppGlanceWeather *weather_glance = app_zalloc_check(sizeof(*weather_glance)); + // Copy the name of the Weather app as a fallback title + const size_t fallback_title_size = sizeof(weather_glance->fallback_title); + strncpy(weather_glance->fallback_title, node->name, fallback_title_size); + weather_glance->fallback_title[fallback_title_size - 1] = '\0'; + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_weather_structured_glance_impl, + should_consider_slices, weather_glance); + PBL_ASSERTN(structured_glance); + + prv_weather_event_handler(NULL, structured_glance); + + weather_glance->weather_event_info = (EventServiceInfo) { + .type = PEBBLE_WEATHER_EVENT, + .handler = prv_weather_event_handler, + .context = structured_glance, + }; + event_service_client_subscribe(&weather_glance->weather_event_info); + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_weather.h b/src/fw/apps/system/launcher/default/app_glance_weather.h new file mode 100644 index 0000000000..fcbc6a6963 --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_weather.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_weather_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/app_glance_workout.c b/src/fw/apps/system/launcher/default/app_glance_workout.c new file mode 100644 index 0000000000..f1f0bb080c --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_workout.c @@ -0,0 +1,205 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "app_glance_workout.h" + +#include "app_glance_structured.h" + +#include "applib/template_string.h" +#include "apps/system/workout/utils.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_install_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/health_util.h" +#include "pbl/services/activity/workout_service.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/string.h" +#include "util/struct.h" + +#include + +#define MAX_SUBTITLE_BUFFER_SIZE (16) + +typedef struct LauncherAppGlanceWorkout { + char title[APP_NAME_SIZE_BYTES]; + char subtitle[MAX_SUBTITLE_BUFFER_SIZE]; + KinoReel *icon; + uint32_t icon_resource_id; + AppTimer *timer; +} LauncherAppGlanceWorkout; + +static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWorkout *workout_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(workout_glance, icon, NULL); +} + +static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWorkout *workout_glance = + launcher_app_glance_structured_get_data(structured_glance); + return NULL_SAFE_FIELD_ACCESS(workout_glance, title, NULL); +} + +static void prv_workout_glance_subtitle_dynamic_text_node_update( + PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, + PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, + void *user_data) { + LauncherAppGlanceStructured *structured_glance = user_data; + LauncherAppGlanceWorkout *workout_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (workout_glance) { + strncpy(buffer, workout_glance->subtitle, buffer_size); + buffer[buffer_size - 1] = '\0'; + } +} + +static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { + return launcher_app_glance_structured_create_subtitle_text_node( + structured_glance, prv_workout_glance_subtitle_dynamic_text_node_update); +} + +static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { + LauncherAppGlanceWorkout *workout_glance = + launcher_app_glance_structured_get_data(structured_glance); + if (workout_glance) { + kino_reel_destroy(workout_glance->icon); + app_timer_cancel(workout_glance->timer); + } + app_free(workout_glance); +} + +static bool prv_set_glance_icon(LauncherAppGlanceWorkout *workout_glance, + uint32_t new_icon_resource_id) { + if (workout_glance->icon_resource_id == new_icon_resource_id) { + // Nothing to do, bail out + return false; + } + + // Destroy the existing icon + kino_reel_destroy(workout_glance->icon); + + // Set the new icon and record its resource ID + workout_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); + PBL_ASSERTN(workout_glance->icon); + workout_glance->icon_resource_id = new_icon_resource_id; + + return true; +} + +static uint32_t prv_get_workout_icon_resource_id_for_type(ActivitySessionType type) { + switch (type) { + case ActivitySessionType_Open: + return RESOURCE_ID_WORKOUT_APP_HEART; + case ActivitySessionType_Walk: + return RESOURCE_ID_WORKOUT_APP_WALK_TINY; + case ActivitySessionType_Run: + return RESOURCE_ID_WORKOUT_APP_RUN_TINY; + case ActivitySessionType_Sleep: + case ActivitySessionType_RestfulSleep: + case ActivitySessionType_Nap: + case ActivitySessionType_RestfulNap: + case ActivitySessionTypeCount: + case ActivitySessionType_None: + break; + } + + WTF; +} + +static void prv_timer_callback(void *data) { + LauncherAppGlanceStructured *structured_glance = data; + LauncherAppGlanceWorkout *workout_glance = + launcher_app_glance_structured_get_data(structured_glance); + PBL_ASSERTN(workout_glance); + + ActivitySession automatic_session = {}; + const bool has_automatic_session = + workout_utils_find_ongoing_activity_session(&automatic_session); + + ActivitySessionType workout_type; + int32_t workout_duration_s = 0; + + if (workout_service_is_workout_ongoing()) { + // Manual workout is going on - get the type and duration + workout_service_get_current_workout_type(&workout_type); + workout_service_get_current_workout_info(NULL, &workout_duration_s, NULL, NULL, NULL); + } else if (has_automatic_session) { + // Automatic workout is going on - get the type and duration + workout_type = automatic_session.type; + workout_duration_s = rtc_get_time() - automatic_session.start_utc; + } else { + // No workout is going on + bool glance_changed = false; + + // Set the icon back to default if it isn't already + if (prv_set_glance_icon(workout_glance, RESOURCE_ID_ACTIVITY_TINY)) { + glance_changed = true; + } + + // Clear subtitle if it isn't already + if (!IS_EMPTY_STRING(workout_glance->subtitle)) { + memset(workout_glance->subtitle, 0, sizeof(workout_glance->subtitle)); + glance_changed = true; + } + + // Broadcast to the service that we changed the glance if it was changed + if (glance_changed) { + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); + } + + // Bail since no workout is going on + return; + } + + // Set icon for the ongoing workout type + prv_set_glance_icon(workout_glance, prv_get_workout_icon_resource_id_for_type(workout_type)); + + // Zero out the glance's subtitle buffer + memset(workout_glance->subtitle, 0, sizeof(workout_glance->subtitle)); + + // Set subtitle + health_util_format_hours_minutes_seconds(workout_glance->subtitle, + sizeof(workout_glance->subtitle), workout_duration_s, true, workout_glance); + + i18n_free_all(workout_glance); + + // Broadcast to the service that we changed the glance + launcher_app_glance_structured_notify_service_glance_changed(structured_glance); +} + +static const LauncherAppGlanceStructuredImpl s_workout_structured_glance_impl = { + .get_icon = prv_get_icon, + .get_title = prv_get_title, + .create_subtitle_node = prv_create_subtitle_node, + .destructor = prv_destructor, +}; + +LauncherAppGlance *launcher_app_glance_workout_create(const AppMenuNode *node) { + PBL_ASSERTN(node); + + LauncherAppGlanceWorkout *workout_glance = app_zalloc_check(sizeof(*workout_glance)); + + // Copy the name of the Workout app as the title + const size_t title_size = sizeof(workout_glance->title); + strncpy(workout_glance->title, node->name, title_size); + workout_glance->title[title_size - 1] = '\0'; + + const bool should_consider_slices = false; + LauncherAppGlanceStructured *structured_glance = + launcher_app_glance_structured_create(&node->uuid, &s_workout_structured_glance_impl, + should_consider_slices, workout_glance); + PBL_ASSERTN(structured_glance); + + // Call timer callback and register it to repeat + prv_timer_callback(structured_glance); + + const uint32_t timer_interval_ms = 1000; + workout_glance->timer = app_timer_register_repeatable(timer_interval_ms, prv_timer_callback, + structured_glance, true /* repeating */); + + return &structured_glance->glance; +} diff --git a/src/fw/apps/system/launcher/default/app_glance_workout.h b/src/fw/apps/system/launcher/default/app_glance_workout.h new file mode 100644 index 0000000000..a9cb360c2c --- /dev/null +++ b/src/fw/apps/system/launcher/default/app_glance_workout.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance.h" + +#include "process_management/app_menu_data_source.h" + +LauncherAppGlance *launcher_app_glance_workout_create(const AppMenuNode *node); diff --git a/src/fw/apps/system/launcher/default/launcher.c b/src/fw/apps/system/launcher/default/launcher.c new file mode 100644 index 0000000000..29bd51101b --- /dev/null +++ b/src/fw/apps/system/launcher/default/launcher.c @@ -0,0 +1,183 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "launcher.h" + +#include "menu_layer.h" + +#include "applib/app.h" +#include "applib/app_focus_service.h" +#include "applib/ui/app_window_stack.h" +#include "kernel/pbl_malloc.h" +#include "shell/normal/app_idle_timeout.h" +#include "system/passert.h" +#include "shell/prefs.h" +#include "process_state/app_state/app_state.h" +#include "util/attributes.h" + +typedef struct LauncherAppWindowData { + Window window; + LauncherMenuLayer launcher_menu_layer; + AppMenuDataSource app_menu_data_source; +} LauncherAppWindowData; + +typedef struct LauncherAppPersistedData { + bool valid; + RtcTicks leave_time; + LauncherMenuLayerSelectionState selection_state; + LauncherDrawState draw_state; +} LauncherAppPersistedData; + +static LauncherAppPersistedData s_launcher_app_persisted_data; + +///////////////////////////// +// AppFocusService handlers + +static void prv_did_focus(bool in_focus) { + LauncherAppWindowData *data = app_state_get_user_data(); + if (in_focus) { + launcher_menu_layer_set_selection_animations_enabled(&data->launcher_menu_layer, true); + } +} + +static void prv_will_focus(bool in_focus) { + LauncherAppWindowData *data = app_state_get_user_data(); + if (!in_focus) { + launcher_menu_layer_set_selection_animations_enabled(&data->launcher_menu_layer, false); + } +} + +//////////////////////////////// +// AppMenuDataSource callbacks + +static bool prv_app_filter_callback(PBL_UNUSED AppMenuDataSource *source, AppInstallEntry *entry) { + // Skip watchfaces and hidden apps + return (!app_install_entry_is_watchface(entry) && !app_install_entry_is_hidden((entry))); +} + +static void prv_data_changed(void *context) { + LauncherAppWindowData *data = context; + launcher_menu_layer_reload_data(&data->launcher_menu_layer); +} + +//! We're not 100% sure of the order of the launcher list yet, so use this function to transform +//! the row index to achieve the desired list ordering +static uint16_t prv_transform_index(AppMenuDataSource *data_source, uint16_t original_index, + void *context) { +#ifdef CONFIG_SHELL_SDK + // We want the newest installed developer app to appear at the top + // This works at the moment because there is only one system app, Watchfaces + return app_menu_data_source_get_count(data_source) - 1 - original_index; +#else + return original_index; +#endif +} + +///////////////////// +// Window callbacks + +static void prv_window_load(Window *window) { + LauncherAppWindowData *data = window_get_user_data(window); + + Layer *window_root_layer = window_get_root_layer(window); + + AppMenuDataSource *data_source = &data->app_menu_data_source; + app_menu_data_source_init(data_source, &(AppMenuDataSourceCallbacks) { + .changed = prv_data_changed, + .filter = prv_app_filter_callback, + .transform_index = prv_transform_index, + }, data); + + LauncherMenuLayer *launcher_menu_layer = &data->launcher_menu_layer; + launcher_menu_layer_init(launcher_menu_layer, data_source); + launcher_menu_layer_set_click_config_onto_window(launcher_menu_layer, window); + layer_add_child(window_root_layer, launcher_menu_layer_get_layer(launcher_menu_layer)); + + // If we have a saved launcher selection state, restore it + if (s_launcher_app_persisted_data.valid) { + launcher_menu_layer_set_selection_state(launcher_menu_layer, + &s_launcher_app_persisted_data.selection_state); + } + + app_focus_service_subscribe_handlers((AppFocusHandlers) { + .did_focus = prv_did_focus, + .will_focus = prv_will_focus, + }); +} + +static void prv_window_unload(Window *window) { + LauncherAppWindowData *data = window_get_user_data(window); + + // Capture the vertical range of the selection rectangle for compositor transition animations + GRangeVertical launcher_selection_vertical_range; + launcher_menu_layer_get_selection_vertical_range(&data->launcher_menu_layer, + &launcher_selection_vertical_range); + + // Save the current state of the launcher so we can know its draw state and restore it later + s_launcher_app_persisted_data = (LauncherAppPersistedData) { + .valid = true, + .leave_time = rtc_get_ticks(), + .draw_state.selection_vertical_range = launcher_selection_vertical_range, + .draw_state.selection_background_color = shell_prefs_get_theme_highlight_color(), + }; + launcher_menu_layer_get_selection_state(&data->launcher_menu_layer, + &s_launcher_app_persisted_data.selection_state); + + app_focus_service_unsubscribe(); + launcher_menu_layer_deinit(&data->launcher_menu_layer); + app_menu_data_source_deinit(&data->app_menu_data_source); +} + +//////////////////// +// App boilerplate + +static void prv_launcher_menu_window_push(void) { + LauncherAppWindowData *data = app_zalloc_check(sizeof(*data)); + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("Launcher Menu")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + + const bool animated = false; + app_window_stack_push(window, animated); +} + +static void prv_main(void) { + const LauncherMenuArgs *args = (const LauncherMenuArgs *)app_manager_get_task_context()->args; + // Reset the selection state of the launcher if we're visiting it for the first time or if + // it has been more than RETURN_TIMEOUT_TICKS since we were last in the launcher + if (args && args->reset_scroll) { + if ((s_launcher_app_persisted_data.leave_time + RETURN_TIMEOUT_TICKS) <= rtc_get_ticks()) { + s_launcher_app_persisted_data.valid = false; + } + } + + prv_launcher_menu_window_push(); + + app_idle_timeout_start(); + + app_event_loop(); +} + +const PebbleProcessMd *launcher_menu_app_get_app_info(void) { + static const PebbleProcessMdSystem s_launcher_menu_app_info = { + .common = { + .main_func = prv_main, + // UUID: dec0424c-0625-4878-b1f2-147e57e83688 + .uuid = {0xde, 0xc0, 0x42, 0x4c, 0x06, 0x25, 0x48, 0x78, + 0xb1, 0xf2, 0x14, 0x7e, 0x57, 0xe8, 0x36, 0x88}, + .visibility = ProcessVisibilityHidden + }, + .name = "Launcher", + }; + return (const PebbleProcessMd *)&s_launcher_menu_app_info; +} + +const LauncherDrawState *launcher_app_get_draw_state(void) { + return &s_launcher_app_persisted_data.draw_state; +} diff --git a/src/fw/apps/system/launcher/default/launcher.h b/src/fw/apps/system/launcher/default/launcher.h new file mode 100644 index 0000000000..4e11956c1d --- /dev/null +++ b/src/fw/apps/system/launcher/default/launcher.h @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "../launcher.h" + +#include "menu_layer.h" + +#include "applib/graphics/gtypes.h" + +#include + +typedef struct LauncherMenuArgs { + bool reset_scroll; +} LauncherMenuArgs; + +typedef struct LauncherDrawState { + GRangeVertical selection_vertical_range; + GColor selection_background_color; +} LauncherDrawState; + +const LauncherDrawState *launcher_app_get_draw_state(void); diff --git a/src/fw/apps/system/launcher/default/menu_layer.c b/src/fw/apps/system/launcher/default/menu_layer.c new file mode 100644 index 0000000000..0ca74dd164 --- /dev/null +++ b/src/fw/apps/system/launcher/default/menu_layer.c @@ -0,0 +1,373 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "menu_layer.h" + +#include "app_glance_service.h" +#include "menu_layer_private.h" + +#include "applib/graphics/gtypes.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/content_indicator.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "system/passert.h" +#include "shell/prefs.h" +#include "util/attributes.h" +#include "util/struct.h" + +#define LAUNCHER_MENU_LAYER_CONTENT_INDICATOR_LAYER_HEIGHT (32) +#define LAUNCHER_MENU_LAYER_GENERIC_APP_ICON (RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON) + +//////////////////////////// +// Misc. callbacks/helpers + +static void prv_launch_app_cb(void *data) { + const AppInstallId app_install_id_to_launch = (AppInstallId)data; + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = app_install_id_to_launch, + .common.reason = APP_LAUNCH_USER, + .common.button = BUTTON_ID_SELECT, + }); +} + +static void prv_launcher_menu_layer_mark_dirty(LauncherMenuLayer *launcher_menu_layer) { + if (launcher_menu_layer) { + layer_mark_dirty(menu_layer_get_layer(&launcher_menu_layer->menu_layer)); + } +} + +////////////////////////////////////// +// LauncherAppGlanceService handlers + +static void prv_glance_changed(void *context) { + LauncherMenuLayer *launcher_menu_layer = context; + prv_launcher_menu_layer_mark_dirty(launcher_menu_layer); +} + +//////////////////////// +// MenuLayer callbacks + +static void prv_menu_layer_select(PBL_UNUSED MenuLayer *menu_layer, MenuIndex *cell_index, + void *context) { + LauncherMenuLayer *launcher_menu_layer = context; + AppMenuDataSource *data_source = launcher_menu_layer->data_source; + if (!data_source) { + return; + } + + Window *window = layer_get_window(launcher_menu_layer_get_layer(launcher_menu_layer)); + if (!window) { + return; + } + // Disable all clicking on the window so the user can't scroll anymore + window_set_click_config_provider(window, NULL); + + // Capture what app we should launch - we'll actually launch it as part of an app task callback + // we register in our .draw_row callback so that we don't launch the app until after we finish + // rendering the last frame of the menu layer; we need to do this because some clients (like the + // normal firmware app launcher) rely on the display reflecting the final state of the launcher + // when we launch an app (e.g. for compositor transition animations) + AppMenuNode *node = app_menu_data_source_get_node_at_index(data_source, cell_index->row); + PBL_ASSERTN(node); + launcher_menu_layer->app_to_launch_after_next_render = node->install_id; + + // Now kick off a render of the last frame of the menu layer; note that any menu layer scroll or + // selection animation has already been advanced to completion by the menu layer before it called + // this select click handler + prv_launcher_menu_layer_mark_dirty(launcher_menu_layer); +} + +static uint16_t prv_menu_layer_get_num_rows(PBL_UNUSED MenuLayer *menu_layer, + PBL_UNUSED uint16_t section_index, void *context) { + LauncherMenuLayer *launcher_menu_layer = context; + AppMenuDataSource *data_source = launcher_menu_layer->data_source; + return data_source ? app_menu_data_source_get_count(data_source) : (uint16_t)0; +} + +static void prv_menu_layer_draw_row(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, + void *context) { + LauncherMenuLayer *launcher_menu_layer = context; + AppMenuDataSource *data_source = launcher_menu_layer->data_source; + if (!data_source) { + return; + } + + AppMenuNode *node = app_menu_data_source_get_node_at_index(data_source, cell_index->row); + + const GRect *cell_layer_bounds = &cell_layer->bounds; + const bool is_highlighted = menu_cell_layer_is_highlighted(cell_layer); + + // Get global frame for continuous arc effect during scroll animations + // Include the animation offset for smooth animation during center-focused scroll + GRect global_frame; + layer_get_global_frame((Layer *)cell_layer, &global_frame); + const int16_t animation_offset = launcher_menu_layer->menu_layer.animation.cell_content_origin_offset_y; + const int16_t screen_center_y = global_frame.origin.y + animation_offset + (global_frame.size.h / 2); + + launcher_app_glance_service_draw_glance_for_app_node(&launcher_menu_layer->glance_service, + ctx, cell_layer_bounds, is_highlighted, + screen_center_y, node); + + // If we should launch an app after this render, push a callback to do that on the app task + if (launcher_menu_layer->app_to_launch_after_next_render != INSTALL_ID_INVALID) { + const AppInstallId app_to_launch_install_id = + launcher_menu_layer->app_to_launch_after_next_render; + // Resetting this here in combination with disabling user input in the select click handler + // (the only place that sets this field) ensures we only do this once + launcher_menu_layer->app_to_launch_after_next_render = INSTALL_ID_INVALID; + process_manager_send_callback_event_to_process(PebbleTask_App, prv_launch_app_cb, + (void *)(uintptr_t)app_to_launch_install_id); + } +} + +static int16_t prv_menu_layer_get_cell_height(PBL_UNUSED MenuLayer *menu_layer, + PBL_UNUSED MenuIndex *cell_index, PBL_UNUSED void *context) { +#if PBL_RECT + return LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT; +#elif PBL_ROUND + return menu_layer_is_index_selected(menu_layer, cell_index) ? + LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT : + LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT; +#else +#error "Unknown display shape type" +#endif +} + +static void prv_play_glance_for_row(LauncherMenuLayer *launcher_menu_layer, uint16_t row) { + if (!launcher_menu_layer || !launcher_menu_layer->selection_animations_enabled) { + return; + } + + // Get the app menu node for the glance that is about to be selected + AppMenuDataSource *data_source = launcher_menu_layer->data_source; + AppMenuNode *node = app_menu_data_source_get_node_at_index(data_source, row); + + // Instruct the launcher app glance service to play the glance for the node + launcher_app_glance_service_play_glance_for_app_node(&launcher_menu_layer->glance_service, node); +} + +static void prv_menu_layer_selection_will_change(MenuLayer *PBL_UNUSED menu_layer, MenuIndex *new_index, + MenuIndex PBL_UNUSED old_index, void *context) { + LauncherMenuLayer *launcher_menu_layer = context; + prv_play_glance_for_row(launcher_menu_layer, new_index->row); +} + +T_STATIC void prv_launcher_menu_layer_set_selection_index(LauncherMenuLayer *launcher_menu_layer, + uint16_t index, MenuRowAlign row_align, + bool animated) { + if (!launcher_menu_layer || !launcher_menu_layer->data_source) { + return; + } + + const MenuIndex new_selected_menu_index = MenuIndex(0, index); + menu_layer_set_selected_index(&launcher_menu_layer->menu_layer, new_selected_menu_index, + row_align, animated); + prv_play_glance_for_row(launcher_menu_layer, index); +} + +//////////////////////// +// Public API + +void launcher_menu_layer_init(LauncherMenuLayer *launcher_menu_layer, + AppMenuDataSource *data_source) { + if (!launcher_menu_layer) { + return; + } + + // We force the launcher menu layer to be the size of the display so that the calculation of + // LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS in launcher_menu_layer_private.h is valid + const GRect frame = DISP_FRAME; + + launcher_menu_layer->title_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_TITLE_FONT); + launcher_menu_layer->subtitle_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_SUBTITLE_FONT); + + Layer *container_layer = &launcher_menu_layer->container_layer; + layer_init(container_layer, &frame); + + launcher_menu_layer->data_source = data_source; + + GRect menu_layer_frame = frame; +#if PBL_ROUND + const int top_bottom_inset = + (frame.size.h - LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT - + (2 * LAUNCHER_MENU_LAYER_NUM_UNFOCUSED_ROWS_PER_SIDE * + LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT)) / 2; + const GEdgeInsets menu_layer_frame_insets = GEdgeInsets(top_bottom_inset, 0); + menu_layer_frame = grect_inset(menu_layer_frame, menu_layer_frame_insets); +#endif + + MenuLayer *menu_layer = &launcher_menu_layer->menu_layer; + menu_layer_init(menu_layer, &menu_layer_frame); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_highlight_colors(menu_layer, + highlight_bg, + gcolor_legible_over(highlight_bg)); + menu_layer_pad_bottom_enable(menu_layer, false); + menu_layer_set_callbacks(menu_layer, launcher_menu_layer, &(MenuLayerCallbacks) { + .get_num_rows = prv_menu_layer_get_num_rows, + .draw_row = prv_menu_layer_draw_row, + .select_click = prv_menu_layer_select, + .get_cell_height = prv_menu_layer_get_cell_height, + .selection_will_change = prv_menu_layer_selection_will_change, + }); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + + // Only setup the content indicator on round +#if PBL_ROUND + const GSize arrow_layer_frame_size = GSize(frame.size.w, + LAUNCHER_MENU_LAYER_CONTENT_INDICATOR_LAYER_HEIGHT); + const GRect up_arrow_layer_frame = (GRect) { + .size = arrow_layer_frame_size, + }; + Layer *up_arrow_layer = &launcher_menu_layer->up_arrow_layer; + layer_init(up_arrow_layer, &up_arrow_layer_frame); + layer_add_child(container_layer, up_arrow_layer); + + const int16_t down_arrow_layer_frame_origin_y = + (int16_t)(frame.size.h - LAUNCHER_MENU_LAYER_CONTENT_INDICATOR_LAYER_HEIGHT); + + const GRect down_arrow_layer_frame = + grect_inset(frame, GEdgeInsets(down_arrow_layer_frame_origin_y, 0, 0, 0)); + Layer *down_arrow_layer = &launcher_menu_layer->down_arrow_layer; + layer_init(down_arrow_layer, &down_arrow_layer_frame); + layer_add_child(container_layer, down_arrow_layer); + + ContentIndicator *content_indicator = + scroll_layer_get_content_indicator(&menu_layer->scroll_layer); + ContentIndicatorConfig content_indicator_config = (ContentIndicatorConfig) { + .layer = up_arrow_layer, + .colors.background = GColorWhite, + .colors.foreground = GColorDarkGray, + }; + content_indicator_configure_direction(content_indicator, ContentIndicatorDirectionUp, + &content_indicator_config); + content_indicator_config.layer = down_arrow_layer; + content_indicator_configure_direction(content_indicator, ContentIndicatorDirectionDown, + &content_indicator_config); +#endif + + // Wait to add the menu layer until after we might have added the content indicators because + // the indicator arrows only get positioned properly if their layers overlap with the menu layer's + // edges + layer_add_child(container_layer, menu_layer_get_layer(menu_layer)); + + launcher_app_glance_service_init(&launcher_menu_layer->glance_service, + LAUNCHER_MENU_LAYER_GENERIC_APP_ICON); + const LauncherAppGlanceServiceHandlers glance_handlers = (LauncherAppGlanceServiceHandlers) { + .glance_changed = prv_glance_changed, + }; + launcher_app_glance_service_set_handlers(&launcher_menu_layer->glance_service, + &glance_handlers, launcher_menu_layer); + + // Select the visually first item from the top + const uint16_t first_index = 0; + const bool animated = false; + prv_launcher_menu_layer_set_selection_index(launcher_menu_layer, first_index, MenuRowAlignBottom, + animated); +} + +Layer *launcher_menu_layer_get_layer(LauncherMenuLayer *launcher_menu_layer) { + if (!launcher_menu_layer) { + return NULL; + } + + return &launcher_menu_layer->container_layer; +} + +void launcher_menu_layer_set_click_config_onto_window(LauncherMenuLayer *launcher_menu_layer, + Window *window) { + if (!launcher_menu_layer || !window) { + return; + } + + menu_layer_set_click_config_onto_window(&launcher_menu_layer->menu_layer, window); +} + +void launcher_menu_layer_reload_data(LauncherMenuLayer *launcher_menu_layer) { + if (!launcher_menu_layer) { + return; + } + + menu_layer_reload_data(&launcher_menu_layer->menu_layer); +} + +void launcher_menu_layer_set_selection_state(LauncherMenuLayer *launcher_menu_layer, + const LauncherMenuLayerSelectionState *new_state) { + if (!launcher_menu_layer || !launcher_menu_layer->data_source || !new_state) { + return; + } + + const bool animated = false; + + prv_launcher_menu_layer_set_selection_index(launcher_menu_layer, new_state->row_index, + MenuRowAlignNone, animated); + + const GPoint new_scroll_offset = GPoint(0, new_state->scroll_offset_y); + scroll_layer_set_content_offset(&launcher_menu_layer->menu_layer.scroll_layer, new_scroll_offset, + animated); +} + +void launcher_menu_layer_get_selection_vertical_range(const LauncherMenuLayer *launcher_menu_layer, + GRangeVertical *vertical_range_out) { + if (!launcher_menu_layer || !vertical_range_out) { + return; + } + + GRect selection_global_rect; + layer_get_global_frame(&launcher_menu_layer->menu_layer.inverter.layer, &selection_global_rect); + + *vertical_range_out = (GRangeVertical) { + .origin_y = selection_global_rect.origin.y, + .size_h = selection_global_rect.size.h, + }; +} + +void launcher_menu_layer_get_selection_state(const LauncherMenuLayer *launcher_menu_layer, + LauncherMenuLayerSelectionState *state_out) { + if (!launcher_menu_layer || !launcher_menu_layer->data_source || !state_out) { + return; + } + + const MenuLayer *menu_layer = &launcher_menu_layer->menu_layer; + const ScrollLayer *scroll_layer = &menu_layer->scroll_layer; + + *state_out = (LauncherMenuLayerSelectionState) { + .row_index = menu_layer_get_selected_index(menu_layer).row, + // This cast is required because this ScrollLayer function's argument isn't const + .scroll_offset_y = scroll_layer_get_content_offset((ScrollLayer *)scroll_layer).y, + }; +} + +void launcher_menu_layer_set_selection_animations_enabled(LauncherMenuLayer *launcher_menu_layer, + bool enabled) { + if (!launcher_menu_layer) { + return; + } + launcher_menu_layer->selection_animations_enabled = enabled; + if (enabled) { + const MenuIndex selected_index = + menu_layer_get_selected_index(&launcher_menu_layer->menu_layer); + prv_play_glance_for_row(launcher_menu_layer, selected_index.row); + } else { + launcher_app_glance_service_rewind_current_glance(&launcher_menu_layer->glance_service); + } +} + +void launcher_menu_layer_deinit(LauncherMenuLayer *launcher_menu_layer) { + if (!launcher_menu_layer) { + return; + } + + launcher_app_glance_service_deinit(&launcher_menu_layer->glance_service); + menu_layer_deinit(&launcher_menu_layer->menu_layer); + +#if PBL_ROUND + layer_deinit(&launcher_menu_layer->up_arrow_layer); + layer_deinit(&launcher_menu_layer->down_arrow_layer); +#endif + layer_deinit(&launcher_menu_layer->container_layer); +} diff --git a/src/fw/apps/system/launcher/default/menu_layer.h b/src/fw/apps/system/launcher/default/menu_layer.h new file mode 100644 index 0000000000..a8241a45ff --- /dev/null +++ b/src/fw/apps/system/launcher/default/menu_layer.h @@ -0,0 +1,63 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "app_glance_service.h" + +#include "process_management/app_menu_data_source.h" +#include "board/display.h" + +// Use display height to determine launcher fonts: larger displays use larger fonts +#if PBL_DISPLAY_HEIGHT >= 200 +#define LAUNCHER_MENU_LAYER_TITLE_FONT (FONT_KEY_GOTHIC_24_BOLD) +#define LAUNCHER_MENU_LAYER_SUBTITLE_FONT (FONT_KEY_GOTHIC_18) +#else +#define LAUNCHER_MENU_LAYER_TITLE_FONT (FONT_KEY_GOTHIC_18_BOLD) +#define LAUNCHER_MENU_LAYER_SUBTITLE_FONT (FONT_KEY_GOTHIC_14) +#endif + + +typedef struct LauncherMenuLayer { + Layer container_layer; + MenuLayer menu_layer; +#if PBL_ROUND + Layer up_arrow_layer; + Layer down_arrow_layer; +#endif + GFont title_font; + GFont subtitle_font; + AppMenuDataSource *data_source; + LauncherAppGlanceService glance_service; + bool selection_animations_enabled; + AppInstallId app_to_launch_after_next_render; +} LauncherMenuLayer; + +typedef struct LauncherMenuLayerSelectionState { + int16_t scroll_offset_y; + uint16_t row_index; +} LauncherMenuLayerSelectionState; + +void launcher_menu_layer_init(LauncherMenuLayer *launcher_menu_layer, + AppMenuDataSource *data_source); + +Layer *launcher_menu_layer_get_layer(LauncherMenuLayer *launcher_menu_layer); + +void launcher_menu_layer_set_click_config_onto_window(LauncherMenuLayer *launcher_menu_layer, + Window *window); + +void launcher_menu_layer_reload_data(LauncherMenuLayer *launcher_menu_layer); + +void launcher_menu_layer_set_selection_state(LauncherMenuLayer *launcher_menu_layer, + const LauncherMenuLayerSelectionState *new_state); + +void launcher_menu_layer_get_selection_state(const LauncherMenuLayer *launcher_menu_layer, + LauncherMenuLayerSelectionState *state_out); + +void launcher_menu_layer_get_selection_vertical_range(const LauncherMenuLayer *launcher_menu_layer, + GRangeVertical *vertical_range_out); + +void launcher_menu_layer_set_selection_animations_enabled(LauncherMenuLayer *launcher_menu_layer, + bool enabled); + +void launcher_menu_layer_deinit(LauncherMenuLayer *launcher_menu_layer); diff --git a/src/fw/apps/system/launcher/default/menu_layer_private.h b/src/fw/apps/system/launcher/default/menu_layer_private.h new file mode 100644 index 0000000000..dfab6f559b --- /dev/null +++ b/src/fw/apps/system/launcher/default/menu_layer_private.h @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/math.h" +#include "board/display.h" + +// Use display height to determine launcher cell height: larger displays use taller cells +#if PBL_DISPLAY_HEIGHT >= 200 +#define LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT (50) +#else +#define LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT (42) +#endif + +#if PBL_ROUND && PBL_DISPLAY_HEIGHT >= 200 +#define LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT (55) +#define LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT (45) +#else +#define LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT (52) +#define LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT (38) +#endif + +#if PBL_ROUND && PBL_DISPLAY_HEIGHT >= 200 +//! Two "unfocused" cells above and below one centered "focused" cell (5 total for larger display) +#define LAUNCHER_MENU_LAYER_NUM_UNFOCUSED_ROWS_PER_SIDE (2) +#define LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS (5) +#elif PBL_ROUND +//! One "unfocused" cell above and below one centered "focused" cell +#define LAUNCHER_MENU_LAYER_NUM_UNFOCUSED_ROWS_PER_SIDE (1) +#define LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS (3) +#else +#define LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS \ + (DIVIDE_CEIL(DISP_ROWS, LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT)) +#endif diff --git a/src/fw/apps/system/launcher/launcher.h b/src/fw/apps/system/launcher/launcher.h new file mode 100644 index 0000000000..11804c6274 --- /dev/null +++ b/src/fw/apps/system/launcher/launcher.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "default/launcher.h" + +#include "process_management/pebble_process_md.h" + +#define RETURN_TIMEOUT_TICKS (5 * RTC_TICKS_HZ) + +const PebbleProcessMd* launcher_menu_app_get_app_info(void); diff --git a/src/fw/apps/system/launcher/legacy/launcher.c b/src/fw/apps/system/launcher/legacy/launcher.c new file mode 100644 index 0000000000..b994524798 --- /dev/null +++ b/src/fw/apps/system/launcher/legacy/launcher.c @@ -0,0 +1,300 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "launcher.h" + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "applib/legacy2/ui/menu_layer_legacy2.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "process_management/app_menu_data_source.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource.h" +#include "resource/resource_ids.auto.h" +#include "shell/normal/app_idle_timeout.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/alerts_private.h" +#include "applib/ui/kino/kino_layer.h" +#include "system/passert.h" + +#include +#include +#include +#include + +typedef struct LauncherMenuData { + Window window; + StatusBarLayer status_bar; + Layer status_bar_icons_layer; + MenuLayer menu_layer; + AppMenuDataSource data_source; + + EventServiceInfo battery_state_event_info; + EventServiceInfo do_not_disturb_event_info; + EventServiceInfo pebble_app_event_info; + + KinoLayer connectivity_icon; + uint32_t connectivity_icon_id; + KinoLayer battery_icon; + uint32_t battery_icon_id; +} LauncherMenuData; + +typedef struct LauncherMenuPersistedData { + int scroll_offset_y; + int menu_index_row; + bool valid; + RtcTicks leave_time; +} LauncherMenuPersistedData; + +static LauncherMenuPersistedData s_launcher_menu_persisted_data; + +/////////////////// +// Status Bar + +static bool prv_is_pebble_app_connected(void) { + return (comm_session_get_system_session() != NULL); +} + +static uint32_t prv_get_resource_id_for_battery_charge_state(BatteryChargeState charge_state) { + const uint32_t battery_base_resource_id = (charge_state.is_charging || charge_state.is_plugged) + ? RESOURCE_ID_TINTIN_LAUNCHER_CHARGING_5_PERCENT + : RESOURCE_ID_TINTIN_LAUNCHER_BATTERY_5_PERCENT; + if (charge_state.charge_percent <= 100) { + return battery_base_resource_id + (charge_state.charge_percent / 10); + } else { + WTF; + } +} + +static void prv_reload_status_bar_icons(LauncherMenuData *data) { + // Draw airplane mode, do not disturb, or silent status icon. + AlertMask alert_mask = alerts_get_mask(); + + // Get the connectivity ResourceId + uint32_t new_connectivity_icon_id = RESOURCE_ID_INVALID; + if (bt_ctl_is_airplane_mode_on()) { + new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE; + } else if (do_not_disturb_is_active()) { + new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND; + } else if (!prv_is_pebble_app_connected()) { + new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED; + } else if (alert_mask != AlertMaskAllOn) { + if (alert_mask == AlertMaskPhoneCalls) { + new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY; + } + } else if (prv_is_pebble_app_connected()) { + new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED; + // probably need an All Muted icon here + } + + // replace the image if the connectivity ResourceId has changed + if (data->connectivity_icon_id != new_connectivity_icon_id) { + data->connectivity_icon_id = new_connectivity_icon_id; + kino_layer_set_reel_with_resource(&data->connectivity_icon, data->connectivity_icon_id); + } + + // Get the connectivity ResourceId + const uint32_t new_battery_icon_id = + prv_get_resource_id_for_battery_charge_state(battery_get_charge_state()); + + // replace the image if the battery ResourceId has changed + if (data->battery_icon_id != new_battery_icon_id) { + data->battery_icon_id = new_battery_icon_id; + kino_layer_set_reel_with_resource(&data->battery_icon, data->battery_icon_id); + } +} + +/////////////////// +// Events + +static void prv_event_handler(PebbleEvent *e, void *context) { + LauncherMenuData *data = (LauncherMenuData *) context; + prv_reload_status_bar_icons(data); +} + +static void prv_subscribe_to_event(EventServiceInfo *result, + PebbleEventType type, + void *callback_context) { + *result = (EventServiceInfo) { + .type = type, + .handler = prv_event_handler, + .context = callback_context, + }; + event_service_client_subscribe(result); +} + +/////////////////// +// AppMenuDataSource callbacks + +static bool prv_app_filter_callback(struct AppMenuDataSource * const source, + AppInstallEntry *entry) { + if (app_install_entry_is_watchface(entry) + || app_install_entry_is_hidden((entry))) { + return false; // Skip watchfaces and hidden apps + } + return true; +} + +static void prv_data_changed(void *context) { + LauncherMenuData *data = context; + menu_layer_reload_data(&data->menu_layer); +} + +////////////// +// MenuLayer callbacks + +static void select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, + LauncherMenuData *data) { + AppMenuNode *node = app_menu_data_source_get_node_at_index(&data->data_source, cell_index->row); + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = node->install_id, + .common.reason = APP_LAUNCH_USER, + .common.button = BUTTON_ID_SELECT, + }); +} + +static uint16_t get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, + LauncherMenuData *data) { + return app_menu_data_source_get_count(&data->data_source); +} + +static void draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *cell_index, + LauncherMenuData *data) { + app_menu_data_source_draw_row(&data->data_source, ctx, cell_layer, cell_index); +} + +/////////////////// +// Window callbacks + +static void prv_window_load(Window *window) { + LauncherMenuData *data = window_get_user_data(window); + GRect bounds = window->layer.bounds; + + status_bar_layer_init(&data->status_bar); + status_bar_layer_set_colors(&data->status_bar, GColorBlack, GColorWhite); + layer_add_child(&window->layer, status_bar_layer_get_layer(&data->status_bar)); + + static const int kino_width = 20; + static const int kino_padding = 6; + kino_layer_init(&data->connectivity_icon, &GRect(kino_padding, 0, + kino_width, STATUS_BAR_LAYER_HEIGHT)); + kino_layer_set_alignment(&data->connectivity_icon, GAlignLeft); + layer_add_child(&window->layer, kino_layer_get_layer(&data->connectivity_icon)); + + kino_layer_init(&data->battery_icon, &GRect(DISP_COLS - kino_width - kino_padding, 0, + kino_width, STATUS_BAR_LAYER_HEIGHT)); + kino_layer_set_alignment(&data->battery_icon, GAlignRight); + layer_add_child(&window->layer, kino_layer_get_layer(&data->battery_icon)); + + prv_reload_status_bar_icons(data); + + bounds = grect_inset(bounds, GEdgeInsets(STATUS_BAR_LAYER_HEIGHT, 0, 0, 0)); + + MenuLayer *menu_layer = &data->menu_layer; + menu_layer_init(menu_layer, &bounds); + app_menu_data_source_init(&data->data_source, &(AppMenuDataSourceCallbacks) { + .changed = prv_data_changed, + .filter = prv_app_filter_callback, + }, data); + + app_menu_data_source_enable_icons(&data->data_source, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON); + + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) get_num_rows_callback, + .draw_row = (MenuLayerDrawRowCallback) draw_row_callback, + .select_click = (MenuLayerSelectCallback) select_callback, + }); + menu_layer_set_click_config_onto_window(menu_layer, window); + layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); + + scroll_layer_set_shadow_hidden(&data->menu_layer.scroll_layer, true); + + if (s_launcher_menu_persisted_data.valid) { + // If we have a saved state, reload it. + menu_layer_set_selected_index(&data->menu_layer, + MenuIndex(0, s_launcher_menu_persisted_data.menu_index_row), + MenuRowAlignNone, + false); + scroll_layer_set_content_offset(&data->menu_layer.scroll_layer, + GPoint(0, s_launcher_menu_persisted_data.scroll_offset_y), + false); + } else { + // If we are resetting the launcher, select the second entry (Settings is at the top) + menu_layer_set_selected_index(&data->menu_layer, MenuIndex(0, 1), MenuRowAlignNone, false); + } + + prv_subscribe_to_event(&data->battery_state_event_info, PEBBLE_BATTERY_STATE_CHANGE_EVENT, data); + prv_subscribe_to_event(&data->do_not_disturb_event_info, PEBBLE_DO_NOT_DISTURB_EVENT, data); + prv_subscribe_to_event(&data->pebble_app_event_info, PEBBLE_COMM_SESSION_EVENT, data); +} + +static void prv_window_unload(Window *window) { + LauncherMenuData *data = window_get_user_data(window); + + kino_layer_deinit(&data->connectivity_icon); + kino_layer_deinit(&data->battery_icon); + + // Save the current state of the menu so we can restore it later. + s_launcher_menu_persisted_data = (LauncherMenuPersistedData) { + .valid = true, + .scroll_offset_y = scroll_layer_get_content_offset(&data->menu_layer.scroll_layer).y, + .menu_index_row = menu_layer_get_selected_index(&data->menu_layer).row, + .leave_time = rtc_get_ticks(), + }; + + menu_layer_deinit(&data->menu_layer); + app_menu_data_source_deinit(&data->data_source); +} + + +static void launcher_menu_push_window(void) { + LauncherMenuData *data = app_zalloc(sizeof(LauncherMenuData)); + app_state_set_user_data(data); + + // Push launcher menu window: + Window *window = &data->window; + window_init(window, WINDOW_NAME("Launcher Menu")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + + const bool animated = false; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate + +static void s_main(void) { + const LauncherMenuArgs *args = (const LauncherMenuArgs *) app_manager_get_task_context()->args; + if (args && args->reset_scroll) { + if ((s_launcher_menu_persisted_data.leave_time + RETURN_TIMEOUT_TICKS) <= rtc_get_ticks()) { + s_launcher_menu_persisted_data.valid = false; + } + } + + launcher_menu_push_window(); + + app_idle_timeout_start(); + + app_event_loop(); +} + +const PebbleProcessMd* launcher_menu_app_get_app_info() { + static const PebbleProcessMdSystem s_launcher_menu_app_info = { + .common = { + .main_func = s_main, + // UUID: dec0424c-0625-4878-b1f2-147e57e83688 + .uuid = {0xde, 0xc0, 0x42, 0x4c, 0x06, 0x25, 0x48, 0x78, + 0xb1, 0xf2, 0x14, 0x7e, 0x57, 0xe8, 0x36, 0x88}, + .visibility = ProcessVisibilityHidden + }, + .name = "Launcher", + }; + return (const PebbleProcessMd*) &s_launcher_menu_app_info; +} diff --git a/src/fw/apps/system/launcher/legacy/launcher.h b/src/fw/apps/system/launcher/legacy/launcher.h new file mode 100644 index 0000000000..67b4bf9cee --- /dev/null +++ b/src/fw/apps/system/launcher/legacy/launcher.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "../launcher.h" + +#include + +typedef struct LauncherMenuArgs { + bool reset_scroll; +} LauncherMenuArgs; diff --git a/src/fw/apps/system/music.c b/src/fw/apps/system/music.c new file mode 100644 index 0000000000..b6a2d8c04e --- /dev/null +++ b/src/fw/apps/system/music.c @@ -0,0 +1,1037 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "music.h" + +#include "applib/app.h" +#include "applib/event_service_client.h" +#include "applib/app_timer.h" +#include "applib/fonts/fonts.h" +#include "applib/preferred_content_size.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/property_animation.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/system_icons.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/music.h" +#include "pbl/services/vibes/vibe_score.h" +#include "shell/prefs.h" +#include "shell/system_theme.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/math.h" + +#include +#include +#include + +enum ActionBarState { + ActionBarStateSkip, + ActionBarStateVolume, + ActionBarStateLongPress, +}; + +typedef struct MusicAppSizeConfig { + const char *music_time_font_key; + const char *no_music_font_key; + int16_t horizontal_margin; + + GRangeVertical artist_field; + GRangeVertical title_field; + GRangeVertical time_field; + + GRect cassette_rect; + int16_t cassette_animation_x; + int32_t cassette_animation_time; + + GRangeVertical track_field; + int16_t track_corner_radius; + + GPoint no_music_img_pos; + GRangeVertical no_music_text_field; +} MusicAppSizeConfig; + +// The reference animations ran at 28fps. +#define ANIMATION_FRAME_MS (1000 / 28) + +#define CONTENT_VERTICAL_OFFSET PBL_IF_RECT_ELSE(0, 5) +static const MusicAppSizeConfig s_music_size_config_medium = { + .music_time_font_key = FONT_KEY_GOTHIC_14, + .no_music_font_key = FONT_KEY_GOTHIC_18_BOLD, + .horizontal_margin = PBL_IF_RECT_ELSE(12, 25), + + .artist_field = { + .origin_y = 31 + CONTENT_VERTICAL_OFFSET, + .size_h = 21, + }, + .title_field = { + .origin_y = 53 + CONTENT_VERTICAL_OFFSET, + .size_h = 44, + }, + .time_field = { + .origin_y = 106 + CONTENT_VERTICAL_OFFSET, + .size_h = 14, + }, + + .cassette_rect = {{0, -1 + CONTENT_VERTICAL_OFFSET}, {43, 28}}, + .cassette_animation_x = 60, + .cassette_animation_time = 1 * ANIMATION_FRAME_MS, + + .track_field = { + .origin_y = 120 + CONTENT_VERTICAL_OFFSET, + .size_h = 4, + }, + .track_corner_radius = 1, + + .no_music_img_pos = {PBL_IF_RECT_ELSE(29, 53), PBL_IF_RECT_ELSE(25, 26)}, + .no_music_text_field = { + .origin_y = PBL_IF_RECT_ELSE(107, 104), + .size_h = 58, + }, +}; + +static const MusicAppSizeConfig s_music_size_config_large = { + .music_time_font_key = FONT_KEY_GOTHIC_18_BOLD, + .no_music_font_key = FONT_KEY_GOTHIC_28, + .horizontal_margin = 10, + + .artist_field = { + .origin_y = 30, + .size_h = 21, + }, + .title_field = { + .origin_y = 60, + .size_h = 80, + }, + .time_field = { + .origin_y = 146, + .size_h = 20, + }, + + .cassette_rect = {{0, -8}, {43, 28}}, + .cassette_animation_x = 140, + .cassette_animation_time = 3 * ANIMATION_FRAME_MS, + + .track_field = { + .origin_y = 168, + .size_h = 10, + }, + .track_corner_radius = 4, + + .no_music_img_pos = {57, 46}, + .no_music_text_field = { + .origin_y = 131, + .size_h = 58, + }, +}; + +static const MusicAppSizeConfig * const s_music_size_configs[NumPreferredContentSizes] = { + [PreferredContentSizeSmall] = &s_music_size_config_medium, + [PreferredContentSizeMedium] = &s_music_size_config_medium, + [PreferredContentSizeLarge] = &s_music_size_config_large, + [PreferredContentSizeExtraLarge] = &s_music_size_config_large, +}; + +static const MusicAppSizeConfig *prv_config(void) { + return s_music_size_configs[PreferredContentSizeDefault]; +} + +static int prv_content_width(void) { + return DISP_COLS - ACTION_BAR_WIDTH - (prv_config()->horizontal_margin * 2); +} + +static int prv_text_layer_width(void) { + return prv_content_width() + PBL_IF_RECT_ELSE(prv_config()->horizontal_margin / 2, 0); +} + +static GRect prv_artist_rect(void) { + const MusicAppSizeConfig *config = prv_config(); + return GRect(config->horizontal_margin, config->artist_field.origin_y, + prv_text_layer_width(), config->artist_field.size_h); +} + +static GRect prv_title_rect(void) { + const MusicAppSizeConfig *config = prv_config(); + return GRect(config->horizontal_margin, config->title_field.origin_y, + prv_text_layer_width(), config->title_field.size_h); +} + +static GRect prv_time_rect(void) { + const MusicAppSizeConfig *config = prv_config(); + return GRect(config->horizontal_margin, config->time_field.origin_y, + prv_content_width(), config->time_field.size_h); +} + +static GRect prv_cassette_rect(void) { + const MusicAppSizeConfig *config = prv_config(); + const int16_t cassette_x = config->horizontal_margin + + PBL_IF_RECT_ELSE(0, prv_content_width() - config->cassette_rect.size.w); + return GRect(cassette_x, config->cassette_rect.origin.y, + config->cassette_rect.size.w, config->cassette_rect.size.h); +} + +static GRect prv_track_rect(void) { + const MusicAppSizeConfig *config = prv_config(); + return GRect(config->horizontal_margin, config->track_field.origin_y, + prv_content_width(), config->track_field.size_h); +} + +static const ButtonId BUTTON_FORWARD = BUTTON_ID_DOWN; +static const ButtonId BUTTON_BACKWARD = BUTTON_ID_UP; + +static const int16_t BOUNCEBACK_OFFSET = 6; +// These offsets get rid of the empty space above the first line of text, +// enabling neater animations by clipping immediately at the top of the text. +static const int16_t TITLE_BOUNDS_OFFSET = 5; +static const int16_t ARTIST_BOUNDS_OFFSET = 3; +static const int16_t TIME_BOUNDS_OFFSET = 2; + +static const uint32_t VOLUME_REPEAT_INTERVAL_MS = 400; +static const uint32_t ACTION_BAR_TIMEOUT_MS = 2000; +static const uint32_t VOLUME_ICON_TIMEOUT_MS = 2000; + +typedef struct { + Window window; + BitmapLayer bitmap_layer; + GBitmap bitmap; + TextLayer text_layer; +} MusicNoMusicWindow; + +typedef struct { + Window window; + ActionBarLayer action_bar; + + TextLayer artist_text_layer; + char artist_buffer[MUSIC_BUFFER_LENGTH]; + + TextLayer title_text_layer; + char title_buffer[MUSIC_BUFFER_LENGTH]; + + StatusBarLayer status_layer; + + TextLayer position_text_layer; + char position_buffer[9]; // 9 will fit "00:00:00" + + TextLayer length_text_layer; + char length_buffer[9]; + + Animation *transition; + AppTimer *volume_icon_timer; + + MusicPlayState current_play_state; + + GBitmap icon_skip_forward; + GBitmap icon_skip_backward; + GBitmap icon_ellipsis; + GBitmap icon_pause; + GBitmap icon_play; + GBitmap icon_play_pause; + GBitmap icon_volume_up; + GBitmap icon_volume_down; + GBitmap image_cassette; + GBitmap image_pause; + GBitmap image_volume_up; + GBitmap image_volume_down; + + Layer cassette_container; + BitmapLayer cassette_layer; + GBitmap *cassette_current_icon; + + EventServiceInfo event_info; + + ProgressLayer track_pos_bar; + uint32_t track_length; + uint32_t track_pos; + bool pause_track_pos_updates; + + enum ActionBarState action_bar_state; + AppTimer *action_bar_revert_timer; + AppTimer *volume_repeat_timer; + bool volume_is_up; + + MusicNoMusicWindow *no_music_window; + + VibeScore *score; +} MusicAppData; + +static void prv_set_action_bar_state(MusicAppData *data, enum ActionBarState state); + +static void prv_trigger_cassette_icon_switch(GBitmap *bitmap, bool animated); +static void prv_update_cassette_icon(MusicAppData *data, bool animated); + +static void prv_do_haptic_feedback_vibe(MusicAppData *data) { + vibe_score_do_vibe(data->score); +} + +static void prv_handle_volume_icon_timer(void *context) { + MusicAppData *data = context; + data->volume_icon_timer = NULL; + prv_update_cassette_icon(data, true); +} + +static void prv_show_volume_image(GBitmap *bitmap) { + MusicAppData *data = app_state_get_user_data(); + if (data->volume_icon_timer) { + app_timer_reschedule(data->volume_icon_timer, VOLUME_ICON_TIMEOUT_MS); + } else { + data->volume_icon_timer = app_timer_register(VOLUME_ICON_TIMEOUT_MS, + prv_handle_volume_icon_timer, data); + } + prv_trigger_cassette_icon_switch(bitmap, true); +} + +static void prv_change_volume(bool volume_is_up) { + if (!music_is_command_supported(MusicCommandVolumeUp) || + !music_is_command_supported(MusicCommandVolumeDown)) { + return; + } + + MusicAppData *data = app_state_get_user_data(); + prv_show_volume_image(volume_is_up ? &data->image_volume_up : &data->image_volume_down); + music_command_send(volume_is_up ? MusicCommandVolumeUp : MusicCommandVolumeDown); +} + +static Animation* prv_create_layer_upwards_animation(Layer *layer, int16_t offset) { + GPoint target = GPoint(0, -layer->bounds.size.h - offset); + GPoint origin = GPoint(0, -offset); + Animation *animation = property_animation_get_animation( + property_animation_create_bounds_origin(layer, &origin, &target)); + animation_set_duration(animation, 4 * ANIMATION_FRAME_MS); + animation_set_curve(animation, AnimationCurveEaseIn); + return animation; +} + +static Animation* prv_create_upwards_animation(MusicAppData *data) { + return animation_spawn_create( + prv_create_layer_upwards_animation(&data->artist_text_layer.layer, ARTIST_BOUNDS_OFFSET), + prv_create_layer_upwards_animation(&data->title_text_layer.layer, TITLE_BOUNDS_OFFSET), + prv_create_layer_upwards_animation(&data->position_text_layer.layer, TIME_BOUNDS_OFFSET), + prv_create_layer_upwards_animation(&data->length_text_layer.layer, TIME_BOUNDS_OFFSET), + NULL); +} + +static const PropertyAnimationImplementation s_frame_layer_implementation = { + .base = { + .update = (AnimationUpdateImplementation) property_animation_update_grect, + }, + .accessors = { + .setter = { .grect = (const GRectSetter) layer_set_frame_by_value, }, + .getter = { .grect = (const GRectGetter) layer_get_frame_by_value, }, + }, +}; + +static Animation *prv_create_layer_bounceback_animation(Layer *layer, GRect origin) { + GRect target = origin; + origin.origin.y -= BOUNCEBACK_OFFSET; + Animation *animation = property_animation_get_animation( + property_animation_create(&s_frame_layer_implementation, layer, &origin, &target)); + animation_set_duration(animation, 4 * ANIMATION_FRAME_MS); + animation_set_curve(animation, AnimationCurveEaseOut); + return animation; +} + +static Animation *prv_create_bounceback_animation(MusicAppData *data) { + const GRect time_rect = prv_time_rect(); + return animation_spawn_create( + prv_create_layer_bounceback_animation(&data->artist_text_layer.layer, prv_artist_rect()), + prv_create_layer_bounceback_animation(&data->title_text_layer.layer, prv_title_rect()), + prv_create_layer_bounceback_animation(&data->position_text_layer.layer, time_rect), + prv_create_layer_bounceback_animation(&data->length_text_layer.layer, time_rect), + NULL); +} + +static void prv_update_track_progress(MusicAppData *data); + +static void prv_flip_animated_text(Animation *animation, bool finished, void *context) { + MusicAppData *data = context; + data->pause_track_pos_updates = false; + prv_update_track_progress(data); + music_get_now_playing(data->title_buffer, data->artist_buffer, NULL); + // Restore the layers to their original bounds for the next part of the animation. + data->title_text_layer.layer.bounds.origin.y = -TITLE_BOUNDS_OFFSET; + data->artist_text_layer.layer.bounds.origin.y = -ARTIST_BOUNDS_OFFSET; + data->position_text_layer.layer.bounds.origin.y = -TIME_BOUNDS_OFFSET; + data->length_text_layer.layer.bounds.origin.y = -TIME_BOUNDS_OFFSET; +} + +static inline bool prv_should_animate_casssette(void) { + return music_get_playback_state() != MusicPlayStatePaused; +} + +static Animation *prv_create_cassette_animation(MusicAppData *data) { + const MusicAppSizeConfig *config = prv_config(); + const GRect cassette_rect = prv_cassette_rect(); + GPoint left_target = GPoint(-cassette_rect.size.w - cassette_rect.origin.x, 0); + Animation *cassette_left = property_animation_get_animation( + property_animation_create_bounds_origin(&data->cassette_container, &GPointZero, + &left_target)); + Animation *cassette_right = property_animation_get_animation( + property_animation_create_bounds_origin(&data->cassette_container, + &GPoint(config->cassette_animation_x, 0), &GPoint(-4, 0))); + Animation *cassette_bounceback = property_animation_get_animation( + property_animation_create_bounds_origin(&data->cassette_container, &GPoint(-4, 0), + &GPointZero)); + animation_set_duration(cassette_left, 4 * ANIMATION_FRAME_MS); + animation_set_curve(cassette_left, AnimationCurveEaseIn); + animation_set_duration(cassette_right, config->cassette_animation_time); + animation_set_curve(cassette_left, AnimationCurveLinear); + animation_set_duration(cassette_bounceback, 4 * ANIMATION_FRAME_MS); + animation_set_curve(cassette_bounceback, AnimationCurveEaseOut); + Animation *sequence = animation_sequence_create(cassette_left, cassette_right, + cassette_bounceback, NULL); + if (!prv_should_animate_casssette()) { + animation_set_play_count(sequence, 0); + } + return sequence; +} + +static void prv_trigger_track_change_animation(MusicAppData *data) { + // Animation structure: + // - Master animation + // - Cassette animation + // - Move to left + // - Move in from right + // - Bounceback + // - Upwards animation + // - Per-layer animations + // - (flip text) + // - Bounceback animation + // - Per-layer animations + + if (animation_is_scheduled(data->transition)) { + return; + } + data->pause_track_pos_updates = true; + Animation *scroll_up = prv_create_upwards_animation(data); + Animation *bounceback = prv_create_bounceback_animation(data); + animation_set_handlers(scroll_up, (AnimationHandlers) { + .stopped = prv_flip_animated_text, + }, data); + + Animation *complete; + complete = animation_sequence_create(scroll_up, bounceback, NULL); + data->transition = complete; + animation_schedule(complete); +} + +static void prv_update_icon(Animation *animation, bool finished, void *context) { + MusicAppData *data = app_state_get_user_data(); + GBitmap *bitmap = context; + bitmap_layer_set_bitmap(&data->cassette_layer, bitmap); + data->cassette_layer.layer.bounds.origin.y = 0; +} + +static void prv_trigger_cassette_icon_switch(GBitmap *new_bitmap, bool animated) { + MusicAppData *data = app_state_get_user_data(); + + if (!animated) { + bitmap_layer_set_bitmap(&data->cassette_layer, new_bitmap); + data->cassette_current_icon = new_bitmap; + return; + } + // Never animate an icon to itself. We can't use the current value of the bitmap layer itself, + // because that will cause false positives if an icon change is triggered, but a revert + // is triggered before the first half of the icon animation completes (currently 107 ms). + if (new_bitmap == data->cassette_current_icon) { + return; + } + + GRect cassette_rect = prv_cassette_rect(); + Animation *disappear_animation = property_animation_get_animation( + property_animation_create_bounds_origin(&data->cassette_layer.layer, &GPointZero, + &GPoint(0, -cassette_rect.size.h))); + animation_set_duration(disappear_animation, 3 * ANIMATION_FRAME_MS); + animation_set_curve(disappear_animation, AnimationCurveEaseIn); + + GRect origin = cassette_rect; + origin.origin.y -= BOUNCEBACK_OFFSET; + + Animation *bounceback_animation = property_animation_get_animation( + property_animation_create(&s_frame_layer_implementation, &data->cassette_layer.layer, + &origin, &cassette_rect)); + animation_set_duration(bounceback_animation, 4 * ANIMATION_FRAME_MS); + animation_set_curve(bounceback_animation, AnimationCurveEaseOut); + + animation_set_handlers(disappear_animation, (AnimationHandlers) { + .stopped = prv_update_icon, + }, new_bitmap); + + Animation *sequence = animation_sequence_create(disappear_animation, bounceback_animation, NULL); + + data->cassette_current_icon = new_bitmap; + animation_schedule(sequence); +} + +static void prv_skipping_click_config_provider(void *data); +static void prv_volume_click_config_provider(void *data); + +static void prv_update_cassette_icon(MusicAppData *data, bool animated) { + if (music_get_playback_state() == MusicPlayStatePaused) { + prv_trigger_cassette_icon_switch(&data->image_pause, animated); + } else { + prv_trigger_cassette_icon_switch(&data->image_cassette, animated); + } +} + +static void prv_update_ui_state_skipping(MusicAppData *data, bool animated) { + action_bar_layer_set_click_config_provider(&data->action_bar, + prv_skipping_click_config_provider); + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_FORWARD, + &data->icon_skip_forward, animated); + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_BACKWARD, + &data->icon_skip_backward, animated); + const bool show_volume_controls = shell_prefs_get_music_show_volume_controls(); + if (music_get_playback_state() == MusicPlayStatePaused) { + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, &data->icon_play, + animated); + } else if (show_volume_controls) { + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, &data->icon_ellipsis, + animated); + } else { + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, &data->icon_pause, + animated); + } +} + +static void prv_update_ui_state_volume(MusicAppData *data, bool animated) { + if (data->action_bar_state == ActionBarStateVolume) { + action_bar_layer_set_click_config_provider(&data->action_bar, + prv_volume_click_config_provider); + } + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_UP, &data->icon_volume_up, + animated); + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_DOWN, &data->icon_volume_down, + animated); + GBitmap const *select_bitmap; + switch (music_get_playback_state()) { + case MusicPlayStatePlaying: select_bitmap = &data->icon_pause; break; + case MusicPlayStatePaused: select_bitmap = &data->icon_play; break; + default: select_bitmap = &data->icon_play_pause; break; + } + action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, select_bitmap, animated); +} + +static void prv_update_ui_state(MusicAppData *data, bool animated) { + if (data->action_bar_state == ActionBarStateSkip) { + prv_update_ui_state_skipping(data, animated); + } else { + prv_update_ui_state_volume(data, animated); + } + + if (music_get_playback_state() != data->current_play_state) { + data->current_play_state = music_get_playback_state(); + prv_update_cassette_icon(data, animated); + } +} + +static void prv_set_action_bar_state(MusicAppData *data, enum ActionBarState state) { + data->action_bar_state = state; + prv_update_ui_state(data, true); +} + +static void prv_action_bar_revert(void *context) { + MusicAppData *data = context; + data->action_bar_revert_timer = NULL; + prv_set_action_bar_state(data, ActionBarStateSkip); +} + +static void reset_action_bar_revert_timer(MusicAppData *data) { + if (data->action_bar_revert_timer) { + app_timer_reschedule(data->action_bar_revert_timer, ACTION_BAR_TIMEOUT_MS); + } +} + +static void prv_skip_click_handler(ClickRecognizerRef recognizer, void *context) { + // no animations on tintin + Animation *animation = prv_create_cassette_animation(context); + animation_schedule(animation); + if (click_recognizer_get_button_id(recognizer) == BUTTON_BACKWARD) { + music_command_send(MusicCommandPreviousTrack); + } else { + music_command_send(MusicCommandNextTrack); + } +} + +static void prv_volume_click_handler(ClickRecognizerRef recognizer, void *context) { + reset_action_bar_revert_timer(context); + // TODO: absolute volume + volume indicator, when that information is available. + bool is_volume_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + prv_change_volume(is_volume_up); + + // Trigger haptic feedback only on repeat + if (click_number_of_clicks_counted(recognizer) >= 2) { + prv_do_haptic_feedback_vibe(context); + } +} + +static void prv_ellipsis_click_handler(ClickRecognizerRef recognizer, void *context) { + if (!shell_prefs_get_music_show_volume_controls()) { + music_command_send(MusicCommandTogglePlayPause); + return; + } + + MusicAppData *data = context; + data->action_bar_revert_timer = app_timer_register(ACTION_BAR_TIMEOUT_MS, prv_action_bar_revert, + data); + prv_set_action_bar_state(data, ActionBarStateVolume); +} + +static void prv_toggle_playing(void) { + music_command_send(MusicCommandTogglePlayPause); +} + +static void prv_play_pause_click_handler(ClickRecognizerRef recognizer, void *context) { + reset_action_bar_revert_timer(context); + prv_toggle_playing(); +} + +static void prv_handle_volume_repeat(void *context) { + MusicAppData *data = context; + if (!data->volume_repeat_timer) { + return; + } + data->volume_repeat_timer = app_timer_register(VOLUME_REPEAT_INTERVAL_MS, + prv_handle_volume_repeat, data); + prv_change_volume(data->volume_is_up); + prv_do_haptic_feedback_vibe(context); +} + +static void prv_volume_long_click_start_handler(ClickRecognizerRef recognizer, void *context) { + const bool volume_is_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); + prv_change_volume(volume_is_up); + prv_set_action_bar_state(context, ActionBarStateLongPress); + MusicAppData *data = context; + data->volume_is_up = volume_is_up; + data->volume_repeat_timer = app_timer_register(VOLUME_REPEAT_INTERVAL_MS, + prv_handle_volume_repeat, data); + prv_do_haptic_feedback_vibe(data); +} + +static void prv_volume_long_click_end_handler(ClickRecognizerRef recognizer, void *context) { + MusicAppData *data = context; + prv_set_action_bar_state(data, ActionBarStateSkip); + app_timer_cancel(data->volume_repeat_timer); + data->volume_repeat_timer = NULL; +} + +static void prv_play_pause_long_click_start_handler(ClickRecognizerRef recognizer, void *context) { + if (!shell_prefs_get_music_show_volume_controls()) { + return; + } + + prv_toggle_playing(); + prv_set_action_bar_state(context, ActionBarStateLongPress); + prv_do_haptic_feedback_vibe(context); +} + +static void prv_play_pause_long_click_end_handler(ClickRecognizerRef recognizer, void *context) { + prv_set_action_bar_state(context, ActionBarStateSkip); +} + +static void prv_skipping_click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_skip_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_skip_click_handler); + const bool show_volume_controls = shell_prefs_get_music_show_volume_controls(); + if (music_get_playback_state() == MusicPlayStatePaused) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_play_pause_click_handler); + } else if (show_volume_controls) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_ellipsis_click_handler); + } else { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_play_pause_click_handler); + } + if (show_volume_controls) { + window_long_click_subscribe(BUTTON_ID_UP, 0, prv_volume_long_click_start_handler, + prv_volume_long_click_end_handler); + window_long_click_subscribe(BUTTON_ID_DOWN, 0, prv_volume_long_click_start_handler, + prv_volume_long_click_end_handler); + window_long_click_subscribe(BUTTON_ID_SELECT, 0, prv_play_pause_long_click_start_handler, + prv_play_pause_long_click_end_handler); + } +} + +static void prv_volume_click_config_provider(void *context) { + window_single_repeating_click_subscribe(BUTTON_ID_UP, VOLUME_REPEAT_INTERVAL_MS, + prv_volume_click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_DOWN, VOLUME_REPEAT_INTERVAL_MS, + prv_volume_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_play_pause_click_handler); +} + +static void prv_update_layout(MusicAppData *data) { + const bool show_progress_bar = shell_prefs_get_music_show_progress_bar(); + bool hide_layer = !show_progress_bar || !music_is_progress_reporting_supported(); + layer_set_hidden(&data->track_pos_bar.layer, hide_layer); + layer_set_hidden(&data->position_text_layer.layer, hide_layer); + layer_set_hidden(&data->length_text_layer.layer, hide_layer); +} + +static void prv_unload_no_music_window(Window *window) { + MusicNoMusicWindow *music_window = (MusicNoMusicWindow *)window; + gbitmap_deinit(&music_window->bitmap); + bitmap_layer_deinit(&music_window->bitmap_layer); + text_layer_deinit(&music_window->text_layer); + i18n_free_all(music_window); +} + +static void prv_handle_no_music_back(ClickRecognizerRef recognizer, void *context) { + app_window_stack_pop_all(true); +} + +static void prv_no_music_window_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_BACK, prv_handle_no_music_back); +} + +static MusicNoMusicWindow *prv_create_no_music_window(void) { + MusicNoMusicWindow *window = app_malloc_check(sizeof(MusicNoMusicWindow)); + window_init(&window->window, WINDOW_NAME("NoMusicWindow")); + window_set_background_color(&window->window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + window_set_window_handlers(&window->window, &(WindowHandlers) { + .unload = prv_unload_no_music_window + }); + + const MusicAppSizeConfig *config = prv_config(); + + gbitmap_init_with_resource(&window->bitmap, RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC); + const GSize NO_MUSIC_IMAGE_SIZE = window->bitmap.bounds.size; + const GRect NO_MUSIC_IMAGE_RECT = GRect(config->no_music_img_pos.x, + config->no_music_img_pos.y, + NO_MUSIC_IMAGE_SIZE.w, NO_MUSIC_IMAGE_SIZE.h); + bitmap_layer_init(&window->bitmap_layer, &NO_MUSIC_IMAGE_RECT); + bitmap_layer_set_bitmap(&window->bitmap_layer, &window->bitmap); + bitmap_layer_set_compositing_mode(&window->bitmap_layer, GCompOpSet); + + const GRect NO_MUSIC_TEXT_RECT = GRect(0, config->no_music_text_field.origin_y, + DISP_COLS, config->no_music_text_field.size_h); + + text_layer_init_with_parameters(&window->text_layer, + &NO_MUSIC_TEXT_RECT, + i18n_get("START PLAYBACK\nON YOUR PHONE", window), + fonts_get_system_font(config->no_music_font_key), + GColorBlack, GColorClear, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->window.layer, &window->bitmap_layer.layer); + layer_add_child(&window->window.layer, &window->text_layer.layer); + window_set_click_config_provider(&window->window, prv_no_music_window_click_config); + return window; +} + +static void prv_push_no_music_window(MusicAppData *data) { + if (data->no_music_window) { + return; + } + data->no_music_window = prv_create_no_music_window(); + app_window_stack_push(&data->no_music_window->window, false); +} + +static void prv_pop_no_music_window(MusicAppData *data) { + if (data->no_music_window) { + app_window_stack_remove(&data->no_music_window->window, true); + app_free(data->no_music_window); + data->no_music_window = NULL; + } +} + +static void prv_update_now_playing(MusicAppData *data) { + const bool show_progress_bar = shell_prefs_get_music_show_progress_bar(); + layer_set_hidden((Layer *)&data->track_pos_bar, + !show_progress_bar || !music_is_progress_reporting_supported()); + + char artist_buffer[MUSIC_BUFFER_LENGTH]; + char title_buffer[MUSIC_BUFFER_LENGTH]; + music_get_now_playing(title_buffer, artist_buffer, NULL); + + if (music_needs_user_to_start_playback_on_phone()) { + prv_push_no_music_window(data); + } else { + prv_pop_no_music_window(data); + } + + bool title_changed = strncmp(data->title_buffer, title_buffer, MUSIC_BUFFER_LENGTH) != 0; + bool artist_changed = strncmp(data->artist_buffer, artist_buffer, MUSIC_BUFFER_LENGTH) != 0; + if (title_changed || artist_changed) { + // Animating nothing looks weird, so don't do that. + if (data->artist_buffer[0] == 0 && data->title_buffer[0] == 0) { + strncpy(data->artist_buffer, artist_buffer, MUSIC_BUFFER_LENGTH); + strncpy(data->title_buffer, title_buffer, MUSIC_BUFFER_LENGTH); + // It is sufficient to mark one layer as dirty. + layer_mark_dirty(&data->title_text_layer.layer); + } else if (title_changed) { + prv_trigger_track_change_animation(data); + } + } + prv_update_layout(data); +} + +static void prv_copy_time_period(char *buffer, size_t n, uint32_t period_s) { + uint32_t hours = period_s / SECONDS_PER_HOUR; + uint32_t minutes = (period_s % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE; + uint32_t seconds = period_s % SECONDS_PER_MINUTE; +#pragma GCC diagnostic ignored "-Wformat-truncation" + if (hours > 0) { + snprintf(buffer, n, "%"PRIu32":%02"PRIu32":%02"PRIu32, hours, minutes, seconds); + } else { + snprintf(buffer, n, "%"PRIu32":%02"PRIu32, minutes, seconds); + } +} + +static void prv_update_track_progress(MusicAppData *data) { + if (data->pause_track_pos_updates) { + return; + } + if (!shell_prefs_get_music_show_progress_bar()) { + return; + } + if (!music_is_progress_reporting_supported()) { + progress_layer_set_progress(&data->track_pos_bar, 0); + } else { + unsigned int percent = 0; + if (data->track_length > 0) { + percent = MIN((data->track_pos * 100) / data->track_length, 100); + } + progress_layer_set_progress(&data->track_pos_bar, percent); + prv_copy_time_period(data->position_buffer, sizeof(data->position_buffer), + data->track_pos / 1000); + prv_copy_time_period(data->length_buffer, sizeof(data->length_buffer), + data->track_length / 1000); + } +} + +static void prv_update_pos(void) { + MusicAppData *data = app_state_get_user_data(); + music_get_pos(&data->track_pos, &data->track_length); + prv_update_track_progress(data); +} + +static void prv_handle_tick_time(struct tm *time, TimeUnits units_changed) { + if (music_get_playback_state() == MusicPlayStatePlaying) { + prv_update_pos(); + } +} + +static void prv_set_pos_update_timer(MusicAppData* data, MusicPlayState playstate) { + if (!music_is_progress_reporting_supported() || !shell_prefs_get_music_show_progress_bar()) { + tick_timer_service_unsubscribe(); + return; + } + switch (playstate) { + case MusicPlayStatePlaying: + // We need to update the progress bar every second. + tick_timer_service_subscribe(SECOND_UNIT, prv_handle_tick_time); + break; + default: + // We're no longer updating the progress bar; unsubscribe. + tick_timer_service_unsubscribe(); + } +} + +static void prv_configure_music_text_layer( + TextLayer *text_layer, char* text_buffer, const GRect *rect, int16_t y_offset, + GTextAlignment align, GFont font) { + text_layer_init_with_parameters(text_layer, rect, text_buffer, font, + GColorBlack, GColorClear, align, GTextOverflowModeFill); + layer_set_bounds(&text_layer->layer, &GRect(0, -y_offset, + rect->size.w, rect->size.h + y_offset)); +} + +static void prv_init_ui(Window *window) { + MusicAppData *data = window_get_user_data(window); + + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + + const GSize WINDOW_SIZE = window->layer.bounds.size; + + const GTextAlignment ARTIST_TITLE_TEXT_ALIGNMENT = PBL_IF_RECT_ELSE(GTextAlignmentLeft, + GTextAlignmentRight); + + const MusicAppSizeConfig *config = prv_config(); + + const GRect artist_rect = prv_artist_rect(); + const GRect title_rect = prv_title_rect(); + const GRect time_rect = prv_time_rect(); + const GRect cassette_rect = prv_cassette_rect(); + const GRect track_rect = prv_track_rect(); + + prv_configure_music_text_layer(&data->artist_text_layer, data->artist_buffer, + &artist_rect, ARTIST_BOUNDS_OFFSET, + ARTIST_TITLE_TEXT_ALIGNMENT, + system_theme_get_font_for_default_size(TextStyleFont_Header)); + layer_add_child(&data->window.layer, &data->artist_text_layer.layer); + + prv_configure_music_text_layer(&data->position_text_layer, data->position_buffer, &time_rect, + TIME_BOUNDS_OFFSET, GTextAlignmentLeft, + fonts_get_system_font(config->music_time_font_key)); + layer_add_child(&data->window.layer, &data->position_text_layer.layer); + + prv_configure_music_text_layer(&data->length_text_layer, data->length_buffer, &time_rect, + TIME_BOUNDS_OFFSET, GTextAlignmentRight, + fonts_get_system_font(config->music_time_font_key)); + layer_add_child(&data->window.layer, &data->length_text_layer.layer); + + prv_configure_music_text_layer(&data->title_text_layer, data->title_buffer, &title_rect, + TITLE_BOUNDS_OFFSET, ARTIST_TITLE_TEXT_ALIGNMENT, + system_theme_get_font_for_default_size(TextStyleFont_Subtitle)); + text_layer_set_line_spacing_delta(&data->title_text_layer, -2); + + layer_add_child(&data->window.layer, &data->title_text_layer.layer); + + const int16_t horizontal_margin = config->horizontal_margin; + layer_init(&data->cassette_container, &GRect(0, WINDOW_SIZE.h - horizontal_margin - 24, + WINDOW_SIZE.w - ACTION_BAR_WIDTH, 24)); + layer_add_child(&data->window.layer, &data->cassette_container); + layer_set_clips(&data->cassette_container, false); + + bitmap_layer_init(&data->cassette_layer, &cassette_rect); + bitmap_layer_set_bitmap(&data->cassette_layer, &data->image_cassette); + data->cassette_current_icon = &data->image_cassette; + const GAlign CASSETTE_LAYER_ALIGNMENT = PBL_IF_RECT_ELSE(GAlignTopLeft, GAlignTopRight); + bitmap_layer_set_alignment(&data->cassette_layer, CASSETTE_LAYER_ALIGNMENT); + bitmap_layer_set_compositing_mode(&data->cassette_layer, GCompOpSet); + layer_add_child(&data->cassette_container, &data->cassette_layer.layer); + + progress_layer_init(&data->track_pos_bar, &track_rect); + progress_layer_set_background_color(&data->track_pos_bar, + PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); + progress_layer_set_foreground_color(&data->track_pos_bar, + PBL_IF_COLOR_ELSE(GColorRed, GColorBlack)); + progress_layer_set_corner_radius(&data->track_pos_bar, config->track_corner_radius); + layer_add_child(&window->layer, (Layer *)&data->track_pos_bar); + + ActionBarLayer *action_bar = &data->action_bar; + data->action_bar_state = ActionBarStateSkip; + action_bar_layer_init(action_bar); + action_bar_layer_set_context(action_bar, data); + action_bar_layer_add_to_window(action_bar, window); + + StatusBarLayer *status_layer = &data->status_layer; + status_bar_layer_init(status_layer); + GRect status_layer_frame = status_layer->layer.frame; + const int16_t STATUS_BAR_LAYER_WIDTH = PBL_IF_RECT_ELSE(WINDOW_SIZE.w - ACTION_BAR_WIDTH, + WINDOW_SIZE.w); + status_layer_frame.size.w = STATUS_BAR_LAYER_WIDTH; + layer_set_frame(&status_layer->layer, &status_layer_frame); + status_bar_layer_set_colors(&data->status_layer, GColorClear, GColorBlack); + layer_add_child(&data->window.layer, &status_layer->layer); + + music_get_pos(&data->track_pos, &data->track_length); + + data->score = vibe_score_create_with_resource(RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK); + + prv_update_now_playing(data); + prv_update_layout(data); + prv_update_track_progress(data); + prv_update_ui_state(data, false); +} + +static void prv_push_window(MusicAppData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Music")); + window_set_user_data(window, data); + window_set_status_bar_icon(window, (GBitmap*)&s_status_icon_music_bitmap); + + const bool animated = true; + app_window_stack_push(window, animated); + prv_init_ui(window); +} + +static void prv_music_event_handler(PebbleEvent *event, void *context) { + MusicAppData *data = app_state_get_user_data(); + switch (event->media.type) { + case PebbleMediaEventTypeNowPlayingChanged: + prv_update_now_playing(data); + return; + case PebbleMediaEventTypePlaybackStateChanged: { + prv_set_pos_update_timer(data, event->media.playback_state); + prv_update_ui_state(data, true); + return; + } + case PebbleMediaEventTypeVolumeChanged: + case PebbleMediaEventTypeServerConnected: + case PebbleMediaEventTypeServerDisconnected: + case PebbleMediaEventTypeTrackPosChanged: + music_get_pos(&data->track_pos, &data->track_length); + prv_update_track_progress(data); + prv_update_layout(data); + return; + default: return; + } +} + +//////////////////// +// App boilerplate + +static void prv_handle_init(void) { + MusicAppData *data = app_malloc_check(sizeof(MusicAppData)); + *data = (MusicAppData){}; + app_state_set_user_data(data); + + data->event_info = (EventServiceInfo){ + .type = PEBBLE_MEDIA_EVENT, + .handler = prv_music_event_handler, + }; + + // TODO: Once we have some sort of system-wide "needs bluetooth" assertion, invoke that here. + + data->current_play_state = MusicPlayStateInvalid; + + gbitmap_init_with_resource(&data->icon_skip_backward, RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD); + gbitmap_init_with_resource(&data->icon_skip_forward, RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD); + gbitmap_init_with_resource(&data->icon_ellipsis, RESOURCE_ID_MUSIC_ICON_ELLIPSIS); + gbitmap_init_with_resource(&data->icon_play, RESOURCE_ID_MUSIC_ICON_PLAY); + gbitmap_init_with_resource(&data->icon_pause, RESOURCE_ID_MUSIC_ICON_PAUSE); + gbitmap_init_with_resource(&data->icon_play_pause, RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE); + gbitmap_init_with_resource(&data->icon_volume_up, RESOURCE_ID_MUSIC_ICON_VOLUME_UP); + gbitmap_init_with_resource(&data->icon_volume_down, RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN); + gbitmap_init_with_resource(&data->image_cassette, RESOURCE_ID_MUSIC_LARGE_CASSETTE); + gbitmap_init_with_resource(&data->image_pause, RESOURCE_ID_MUSIC_LARGE_PAUSED); + gbitmap_init_with_resource(&data->image_volume_up, RESOURCE_ID_MUSIC_LARGE_VOLUME_UP); + gbitmap_init_with_resource(&data->image_volume_down, RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN); + + event_service_client_subscribe(&data->event_info); + prv_push_window(data); + + // Overall reduce the latency at the expense of some power... + music_request_reduced_latency(true); + + // Give us a super responsive initial period: + music_request_low_latency_for_period(5000); + + prv_set_pos_update_timer(data, music_get_playback_state()); +} + +static void prv_handle_deinit(void) { + tick_timer_service_unsubscribe(); + music_request_reduced_latency(false); + + MusicAppData *data = app_state_get_user_data(); + i18n_free_all(data); +} + +static void prv_main(void) { + prv_handle_init(); + + app_event_loop(); + + prv_handle_deinit(); +} + +const PebbleProcessMd* music_app_get_info(void) { + // [INTL] The app name should come from a standard app resource, so it's localizable. + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &prv_main, + // UUID: 1f03293d-47af-4f28-b960-f2b02a6dd757 + .uuid = {0x1f, 0x03, 0x29, 0x3d, 0x47, 0xaf, 0x4f, 0x28, + 0xb9, 0x60, 0xf2, 0xb0, 0x2a, 0x6d, 0xd7, 0x57}, + }, + .name = i18n_noop("Music"), + .icon_resource_id = RESOURCE_ID_AUDIO_CASSETTE_TINY, + }; + return (const PebbleProcessMd*) &s_app_info; +} + diff --git a/src/fw/apps/system/music.h b/src/fw/apps/system/music.h new file mode 100644 index 0000000000..fdf54f17e6 --- /dev/null +++ b/src/fw/apps/system/music.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* music_app_get_info(); + diff --git a/src/fw/apps/system/notifications.c b/src/fw/apps/system/notifications.c new file mode 100644 index 0000000000..81977f42e3 --- /dev/null +++ b/src/fw/apps/system/notifications.c @@ -0,0 +1,819 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "notifications.h" + +#include +#include + +#include "applib/app.h" +#include "applib/app_exit_reason.h" +#include "applib/preferred_content_size.h" +#include "applib/fonts/fonts.h" +#include "applib/graphics/gdraw_command_image.h" +#include "applib/graphics/gdraw_command_list.h" +#include "applib/graphics/graphics.h" +#include "applib/ui/dialogs/actionable_dialog.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/menu_cell_layer.h" +#include "applib/ui/ui.h" +#include "applib/ui/window_stack_private.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/system_icons.h" +#include "popups/notifications/notification_window.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/timeline/notification_layout.h" +#include "shell/prefs.h" +#include "shell/system_theme.h" +#include "system/passert.h" +#include "util/date.h" +#include "util/list.h" +#include "util/string.h" + +typedef struct LoadedNotificationNode { + ListNode node; + TimelineItem notification; + GDrawCommandImage *icon; + bool icon_is_default; +} LoadedNotificationNode; + +typedef struct NotificationNode { + ListNode node; + Uuid id; +} NotificationNode; + +typedef struct NotificationsData { + Window window; + MenuLayer menu_layer; + TextLayer text_layer; + NotificationNode *notification_list; + LoadedNotificationNode *loaded_notification_list; + EventServiceInfo notification_event_info; + ActionableDialog *actionable_dialog; +#if PBL_ROUND + StatusBarLayer status_bar_layer; +#endif +} NotificationsData; + +static NotificationsData *s_data = NULL; + +static const unsigned int MAX_ACTIVE_NOTIFICATIONS = 6; + +static bool prv_loaded_notification_list_filter_cb(ListNode *node, void *data) { + LoadedNotificationNode *loaded_notification = (LoadedNotificationNode *)node; + Uuid *id = data; + return uuid_equal(&loaded_notification->notification.header.id, id); +} + +static bool prv_notification_list_filter_cb(ListNode *node, void *data) { + NotificationNode *notification = (NotificationNode *)node; + Uuid *id = data; + return uuid_equal(¬ification->id, id); +} + +static NotificationNode *prv_find_notification(NotificationNode *list, Uuid *id) { + return (NotificationNode *)list_find((ListNode *)list, + prv_notification_list_filter_cb, + id); +} + +static LoadedNotificationNode *prv_find_loaded_notification(LoadedNotificationNode *list, + Uuid *id) { + return (LoadedNotificationNode *)list_find((ListNode *)list, + prv_loaded_notification_list_filter_cb, + id); +} + +static NotificationNode *prv_notification_list_add_notification_by_id( + NotificationNode **notification_list, Uuid *id) { + NotificationNode *new_node = app_malloc_check(sizeof(NotificationNode)); + + list_init((ListNode*) new_node); + new_node->id = *id; + + *notification_list = (NotificationNode*) list_prepend((ListNode*) *notification_list, + (ListNode*) new_node); + + return new_node; +} + +static void prv_notification_list_remove_notification_by_id( + NotificationNode **notification_list, Uuid *id) { + + NotificationNode *node = prv_find_notification(*notification_list, id); + list_remove((ListNode *)node, (ListNode **)notification_list, NULL); +} + +static NotificationNode *prv_add_notification(NotificationsData *data, Uuid *id) { + NotificationNode *node = prv_notification_list_add_notification_by_id(&data->notification_list, + id); + return node; +} + +static void prv_remove_notification(NotificationsData *data, Uuid *id) { + prv_notification_list_remove_notification_by_id(&data->notification_list, id); +} + +static bool prv_notif_iterator_callback(void *data, SerializedTimelineItemHeader *header) { + return (prv_add_notification(data, &header->common.id) != NULL); +} + +static void prv_load_notification_storage(NotificationsData *data) { + notification_storage_iterate(&prv_notif_iterator_callback, data); +} + +static void prv_notification_list_deinit(NotificationNode *notification_list) { + while (notification_list) { + NotificationNode *node = notification_list; + notification_list = (NotificationNode*) list_pop_head((ListNode*) notification_list); + app_free(node); + } +} + +static void prv_unload_loaded_notification(LoadedNotificationNode *loaded_notif) { + timeline_item_free_allocated_buffer(&loaded_notif->notification); + gdraw_command_image_destroy(loaded_notif->icon); + app_free(loaded_notif); +} + +static NOINLINE LoadedNotificationNode *prv_loaded_notification_list_load_item( + LoadedNotificationNode **loaded_list, NotificationNode *node) { + if (node == NULL) { + return NULL; + } + + LoadedNotificationNode *loaded_node = prv_find_loaded_notification(*loaded_list, &node->id); + if (loaded_node) { + return loaded_node; + } + + // unload old notifications + if (list_count((ListNode*) *loaded_list) > MAX_ACTIVE_NOTIFICATIONS) { + LoadedNotificationNode *old_node = (LoadedNotificationNode*) list_get_tail( + (ListNode*) *loaded_list); + list_remove((ListNode*) old_node, (ListNode**) loaded_list, NULL); + prv_unload_loaded_notification(old_node); + } + + // load the notification + TimelineItem notification; + if (!notification_storage_get(&node->id, ¬ification)) { + return NULL; + } + + // track the loaded notification + loaded_node = app_malloc_check(sizeof(LoadedNotificationNode)); + + list_init((ListNode*) loaded_node); + loaded_node->notification = notification; + + TimelineResourceId timeline_res_id = attribute_get_uint32(¬ification.attr_list, + AttributeIdIconTiny, + NOTIF_FALLBACK_ICON); + + // Read the associated pin's app id + TimelineItem pin; + if (timeline_resources_is_system(timeline_res_id) || + pin_db_read_item_header(&pin, ¬ification.header.parent_id) != S_SUCCESS) { + pin.header.parent_id = (Uuid)UUID_INVALID; + } + + TimelineResourceInfo timeline_res = { + .res_id = timeline_res_id, + .app_id = &pin.header.parent_id, + .fallback_id = NOTIF_FALLBACK_ICON + }; + AppResourceInfo icon_res_info; + timeline_resources_get_id(&timeline_res, TimelineResourceSizeTiny, &icon_res_info); + loaded_node->icon = gdraw_command_image_create_with_resource_system(icon_res_info.res_app_num, + icon_res_info.res_id); + loaded_node->icon_is_default = (timeline_res_id == NOTIF_FALLBACK_ICON) || + (timeline_res_id == TIMELINE_RESOURCE_NOTIFICATION_GENERIC); + + *loaded_list = (LoadedNotificationNode*) list_prepend((ListNode*) *loaded_list, + (ListNode*)loaded_node); + + return loaded_node; +} + +static void prv_loaded_notification_list_deinit(LoadedNotificationNode *loaded_list) { + while (loaded_list) { + LoadedNotificationNode *node = loaded_list; + loaded_list = (LoadedNotificationNode*) list_pop_head((ListNode*) loaded_list); + prv_unload_loaded_notification(node); + } +} + +// Return true if successful +static bool prv_push_notification_window(NotificationsData *data) { + notification_window_init(false /*is_modal*/); + + // Bail if a notification came in ahead of us and created a modal window + // before we had a chance to react to the select button event. + if (notification_window_is_modal()) { + return false; + } + + // iterate over visible items as visible (including the groups) in reverse order + // since notification_window shows each newly added notification first + NotificationNode *node = (NotificationNode*)list_get_tail(&data->notification_list->node); + while (node) { + notification_window_add_notification_by_id(&node->id); + node = (NotificationNode*)list_get_prev(&node->node); + } + + notification_window_show(); + return true; +} + +/////////////////// +// Confirm Dialog + +static void prv_dialog_unloaded(void *context) { + NotificationsData *data = context; + data->actionable_dialog = NULL; +} + +static void prv_confirmed_handler(ClickRecognizerRef recognizer, void *context) { + NotificationsData *data = context; + notification_storage_reset_and_init(); + prv_loaded_notification_list_deinit(data->loaded_notification_list); + data->loaded_notification_list = NULL; + prv_notification_list_deinit(data->notification_list); + data->notification_list = NULL; + prv_load_notification_storage(data); + actionable_dialog_pop(data->actionable_dialog); + + // Create and display DONE dialog + SimpleDialog *confirmation_dialog = simple_dialog_create("Notifications Cleared"); + Dialog *dialog = simple_dialog_get_dialog(confirmation_dialog); + dialog_set_text(dialog, i18n_get("Done", data)); + dialog_set_icon(dialog, RESOURCE_ID_RESULT_SHREDDED_LARGE); + static const uint32_t DIALOG_TIMEOUT = 2000; + dialog_set_timeout(dialog, DIALOG_TIMEOUT); + + // Set the app exit reason so we will go to the watchface upon exit + app_exit_reason_set(APP_EXIT_ACTION_PERFORMED_SUCCESSFULLY); + + // Pop all windows so we'll soon exit the app + app_window_stack_pop_all(true /* animated */); + + // Immediately push this result dialog so it's the last thing we see before exiting + app_simple_dialog_push(confirmation_dialog); +} + + +static void prv_dialog_click_config(void *context) { + NotificationsData *data = app_state_get_user_data(); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_confirmed_handler); + window_set_click_context(BUTTON_ID_SELECT, data); +} + +static void prv_settings_clear_history_window_push(NotificationsData *data) { + ActionableDialog *actionable_dialog = actionable_dialog_create("Clear Notifications"); + actionable_dialog_set_click_config_provider(actionable_dialog, prv_dialog_click_config); + actionable_dialog_set_action_bar_type(actionable_dialog, DialogActionBarConfirm, NULL); + Dialog *dialog = actionable_dialog_get_dialog(actionable_dialog); + dialog_set_text(dialog, i18n_get("Clear history?", data)); + TimelineResourceInfo timeline_res = { + .res_id = TIMELINE_RESOURCE_GENERIC_QUESTION, + }; + AppResourceInfo icon_res_info; + timeline_resources_get_id(&timeline_res, TimelineResourceSizeLarge, &icon_res_info); + dialog_set_icon(dialog, icon_res_info.res_id); + dialog_set_icon_animate_direction(dialog, DialogIconAnimationFromRight); + dialog_set_callbacks(dialog, &(DialogCallbacks) { + .unload = prv_dialog_unloaded, + }, data); + app_actionable_dialog_push(actionable_dialog); + data->actionable_dialog = actionable_dialog; +} + +#if PBL_BW +static GColor prv_invert_bw_color(GColor color) { + if (gcolor_equal(color, GColorBlack)) { + return GColorWhite; + } else if (gcolor_equal(color, GColorWhite)) { + return GColorBlack; + } + return color; +} + +static void prv_invert_pdc_colors(GDrawCommandProcessor *processor, + GDrawCommand *processed_command, + size_t processed_command_max_size, + const GDrawCommandList* list, + const GDrawCommand *command) { + gdraw_command_set_stroke_color(processed_command, + prv_invert_bw_color(gdraw_command_get_stroke_color((GDrawCommand *)command))); + gdraw_command_set_fill_color(processed_command, + prv_invert_bw_color(gdraw_command_get_fill_color((GDrawCommand *)command))); +} + +static void prv_draw_pdc_bw_inverted(GContext *ctx, GDrawCommandImage *image, GPoint offset) { + GDrawCommandProcessor processor = { + .command = prv_invert_pdc_colors, + }; + gdraw_command_image_draw_processed(ctx, image, offset, &processor); +} +#endif // PBL_BW + +////////////// +// MenuLayer callbacks + +#if PBL_RECT +static void prv_draw_notification_cell_rect(GContext *ctx, const Layer *cell_layer, + const char *title, const char *subtitle, + GDrawCommandImage *icon) { + const GRect cell_layer_bounds = cell_layer->bounds; + const GSize icon_size = gdraw_command_image_get_bounds_size(icon); + const int16_t icon_left_margin = menu_cell_basic_horizontal_inset(); + if (icon) { + void (*draw_func)(GContext *, GDrawCommandImage *, GPoint) = gdraw_command_image_draw; +#if PBL_BW + if (menu_cell_layer_is_highlighted(cell_layer)) { + draw_func = prv_draw_pdc_bw_inverted; + } +#endif + + // Inset the draw box from the left to leave some margin on the icon's left side + GRect box = cell_layer_bounds; + box.origin.x += icon_left_margin; + + // Align the icon to the left of the draw box, centered vertically + GRect icon_rect = (GRect) { .size = gdraw_command_image_get_bounds_size(icon) }; + grect_align(&icon_rect, &box, GAlignLeft, false /* clip */); + + draw_func(ctx, icon, icon_rect.origin); + } + + // Temporarily inset the cell layer's bounds from the left so the text doesn't draw over any + // icon on the left + Layer *mutable_cell_layer = (Layer *)cell_layer; + const int text_left_margin = + icon_left_margin + MAX(icon_size.w, ATTRIBUTE_ICON_TINY_SIZE_PX); + mutable_cell_layer->bounds = grect_inset(cell_layer_bounds, + GEdgeInsets(0, 5, 0, text_left_margin)); + + const GFont title_font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); + const GFont subtitle_font = system_theme_get_font_for_default_size(TextStyleFont_Caption); + menu_cell_basic_draw_custom(ctx, cell_layer, title_font, title, NULL /* value_font */, + NULL /* value */, subtitle_font, subtitle, NULL /* icon */, + false /* icon_on_right */, GTextOverflowModeTrailingEllipsis); + + // Restore the cell layer's bounds + mutable_cell_layer->bounds = cell_layer_bounds; +} +#endif + +//! outer_box is passed as a pointer to save stack space +static int16_t prv_draw_centered_text_line_in(GContext *ctx, GFont font, const GRect *outer_box, + const char *text, GAlign align) { + if (!text) { + return 0; + } + + GRect text_box = *outer_box; + text_box.size.h = fonts_get_font_height(font); + grect_align(&text_box, outer_box, align, true); + + graphics_draw_text(ctx, text, font, text_box, GTextOverflowModeTrailingEllipsis, + GTextAlignmentCenter, NULL); + + return text_box.size.h; +} + +//! box is passed as a pointer to save stack space +//! after this call, box will point to the GRect where +//! the notification title was drawn +void prv_draw_notification_cell_round(GContext *ctx, const Layer *cell_layer, GRect *box, + GFont const title_font, const char *title, + GFont const subtitle_font, const char *subtitle, + GDrawCommandImage *icon) { + + if (icon) { + GRect icon_rect = (GRect){.size = gdraw_command_image_get_bounds_size(icon)}; + + grect_align(&icon_rect, box, GAlignTop, true); + icon_rect.origin.y += 4; + + gdraw_command_image_draw(ctx, icon, icon_rect.origin); + + // more box by icon + some margin + const int16_t icon_space = icon_rect.origin.y + icon_rect.size.h - 12; + + // manually inset to save stack space, instead of using grect_inset + box->origin.y += icon_space; + box->size.h -= icon_space; + } + + // hack: compensate for text placement inside a rect + box->origin.y -= 4; + + if (subtitle) { + box->size.h -= prv_draw_centered_text_line_in(ctx, subtitle_font, box, subtitle, + GAlignBottom); + } + + if (title) { + prv_draw_centered_text_line_in(ctx, title_font, box, title, GAlignCenter); + } +} + +#if PBL_ROUND +static void prv_draw_notification_cell_round_selected(GContext *ctx, const Layer *cell_layer, + const char *title, const char *subtitle, + GDrawCommandImage *icon) { + // as measured from the design specs + const int inset = 8; + GRect frame = cell_layer->bounds; + // manually inset the frame to save stack space, instead of using grect_inset + frame.origin.x += inset; + frame.origin.y += inset; + frame.size.h -= inset * 2; + frame.size.w -= inset * 2; + const GFont title_font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); + const GFont subtitle_font = + system_theme_get_font_for_default_size(TextStyleFont_MenuCellSubtitle); + prv_draw_notification_cell_round(ctx, cell_layer, &frame, title_font, title, subtitle_font, + subtitle, icon); +} + +static void prv_draw_notification_cell_round_unselected(GContext *ctx, const Layer *cell_layer, + const char *title, const char *subtitle, + GDrawCommandImage *icon) { + // as measured from the design specs + const int horizontal_inset = MENU_CELL_ROUND_UNFOCUSED_HORIZONTAL_INSET; + const int top_inset = 2; + GRect frame = cell_layer->bounds; + // manually inset the frame to save stack space, instead of using grect_inset + frame.origin.x += horizontal_inset; + frame.size.w -= horizontal_inset * 2; + frame.origin.y += top_inset; + frame.size.h -= top_inset; + // Using TextStyleFont_Header here is a little bit of a hack to achieve Gothic 18 Bold on + // Spalding's default content size (medium) while still being a little robust for any future round + // watches that have a default content size larger than medium + const GFont font = system_theme_get_font_for_default_size(TextStyleFont_Header); + prv_draw_notification_cell_round(ctx, cell_layer, &frame, font, title, NULL, NULL, NULL); +} +#endif + +static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, + void *data) { + NotificationsData *notifications_data = data; + + if ((notifications_data->notification_list) && (cell_index->row == 0)) { + // Clear All button selected + prv_settings_clear_history_window_push(notifications_data); + return; + } + + // shift index since the first one is hard coded to Clear + int16_t notif_idx = cell_index->row - 1; + + NotificationNode *node = (NotificationNode*) list_get_at( + (ListNode*) notifications_data->notification_list, notif_idx); + if (!node) { + return; + } + + bool success = prv_push_notification_window(notifications_data); + if (!success) { + // Bail if a notification came in ahead of us and created a modal window + // before we had a chance to react to the select button event. + return; + } + const bool animated = false; + notification_window_focus_notification(&node->id, animated); +} + +static uint16_t prv_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, + void *data) { + NotificationsData *notifications_data = data; + NotificationNode *node = notifications_data->notification_list; + // There's no notifications, don't draw anything + if (!node) { + return 0; + } + + // add one for the CLEAR ALL at the top + return list_count((ListNode *)notifications_data->notification_list) + 1; +} + +static int16_t prv_get_cell_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, + void *data) { +#if PBL_ROUND + MenuIndex selected_index = menu_layer_get_selected_index(menu_layer); + bool is_selected = menu_index_compare(cell_index, &selected_index) == 0; + if (is_selected) { + return MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT; + } +#endif + const PreferredContentSize runtime_platform_content_size = + system_theme_get_default_content_size_for_runtime_platform(); + return ((int16_t[NumPreferredContentSizes]) { + //! @note this is the same as Medium until Small is designed + [PreferredContentSizeSmall] = PBL_IF_RECT_ELSE(46, MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT), + [PreferredContentSizeMedium] = PBL_IF_RECT_ELSE(46, + MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT), + [PreferredContentSizeLarge] = menu_cell_basic_cell_height(), + //! @note this is the same as Large until ExtraLarge is designed + [PreferredContentSizeExtraLarge] = menu_cell_basic_cell_height(), + })[runtime_platform_content_size]; +} + +static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, + void *data) { + NotificationsData *notifications_data = data; + + void (*draw_cell)(GContext *, const Layer *, const char *, const char *, GDrawCommandImage *) = + PBL_IF_RECT_ELSE(prv_draw_notification_cell_rect, prv_draw_notification_cell_round_selected); +#if PBL_ROUND + // on round: just draw the title for anything but the focused row + if (!menu_layer_is_index_selected(&s_data->menu_layer, cell_index)) { + draw_cell = prv_draw_notification_cell_round_unselected; + } +#endif + + bool first_row = (cell_index->row == 0); + // Test if there are any notifications in the list. + if (first_row) { + // Draw "Clear all" box and exit +#if PBL_ROUND + draw_cell(ctx, cell_layer, i18n_get("Clear All", data), NULL, NULL); +#else + const GFont font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); + GRect box = cell_layer->bounds; + box.origin.y += (box.size.h - fonts_get_font_height(font)) / 2 - fonts_get_font_cap_offset(font); + + graphics_draw_text(ctx, i18n_get("Clear All", data), font, box, + GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); +#endif + return; + } + + // shift index since the first one is hard coded to Clear + const int16_t notif_idx = cell_index->row - 1; + + NotificationNode *node = (NotificationNode*) list_get_at( + (ListNode*) notifications_data->notification_list, notif_idx); + if (!node) { + return; + } + + LoadedNotificationNode *loaded_node = prv_loaded_notification_list_load_item( + ¬ifications_data->loaded_notification_list, node); + if (!loaded_node) { + return; + } + + TimelineItem *notification = &loaded_node->notification; + const char *title = attribute_get_string(¬ification->attr_list, AttributeIdTitle, ""); + const char *subtitle = attribute_get_string(¬ification->attr_list, AttributeIdSubtitle, ""); + const char *app_name = attribute_get_string(¬ification->attr_list, AttributeIdAppName, ""); + const char *body = attribute_get_string(¬ification->attr_list, AttributeIdBody, ""); + + // We show the app name if we don't have a custom icon, otherwise we use the title + if (!IS_EMPTY_STRING(app_name) && loaded_node->icon_is_default) { + title = app_name; + } + + if (!IS_EMPTY_STRING(title) && !IS_EMPTY_STRING(subtitle)) { + // we got a title & subtitle, we're done + } else if (IS_EMPTY_STRING(title) && IS_EMPTY_STRING(subtitle)) { + // we got neither, use the body + if (IS_EMPTY_STRING(body)) { + // we're screwed... empty message + title = "[Empty]"; + } else { + // try to show as much content as possible in title + subtitle + title = body; + subtitle = strchr(body, '\n'); // NULL handled gracefully downstream + } + } else if (IS_EMPTY_STRING(title)) { + // no title, but yes subtitle. + title = subtitle; + subtitle = body; + } else if (IS_EMPTY_STRING(subtitle)) { + // no subtitle, but yes title + subtitle = body; + } else { + WTF; + } + + draw_cell(ctx, cell_layer, title, subtitle, loaded_node->icon); +} + +// Display the appropriate layer +static void prv_update_text_layer_visibility(NotificationsData *data) { + NotificationNode *node = data->notification_list; + + // Toggle which layer is visible + if (node == NULL) { + layer_set_hidden((Layer *) &data->menu_layer, true); + layer_set_hidden((Layer *) &data->text_layer, false); + } else { + layer_set_hidden((Layer *) &data->menu_layer, false); + layer_set_hidden((Layer *) &data->text_layer, true); + } +} + +static void prv_handle_notification_removed(Uuid *id) { + prv_remove_notification(s_data, id); + app_notification_window_remove_notification_by_id(id); +} + +static void prv_handle_notification_acted_upon(Uuid *id) { + prv_remove_notification(s_data, id); + app_notification_window_remove_notification_by_id(id); +} + +static void prv_handle_notification_added(Uuid *id) { + TimelineItem notification; + if (!notification_storage_get(id, ¬ification)) { + return; + } + + prv_add_notification(s_data, id); + + // NOTE: To avoid having two flash reads, we only read and validate the notification once. + // We do it here, instead of in the function call below. If the above + // notification_storage validation above is removed, then we should at least validate + // it in the function call below. + app_notification_window_add_new_notification_by_id(id); +} + +static void prv_handle_notification(PebbleEvent *e, void *context) { + if (e->type == PEBBLE_SYS_NOTIFICATION_EVENT) { + Uuid *id = e->sys_notification.notification_id; + switch(e->sys_notification.type) { + case NotificationAdded: + prv_handle_notification_added(id); + break; + case NotificationRemoved: + prv_handle_notification_removed(id); + break; + case NotificationActedUpon: + prv_handle_notification_acted_upon(id); + break; + case NotificationActionResult: { + PebbleSysNotificationActionResult *action_result = e->sys_notification.action_result; + if (action_result && + (action_result->type == ActionResultTypeSuccess || + action_result->type == ActionResultTypeSuccessANCSDismiss)) { + prv_remove_notification(s_data, &action_result->id); + app_notification_window_remove_notification_by_id(&action_result->id); + } + break; + } + default: + break; + // Not implemented + } + menu_layer_reload_data(&s_data->menu_layer); + prv_update_text_layer_visibility(s_data); + } + // we don't handle reminders within the notifications app +} + +/////////////////// +// Window callbacks + +static void prv_window_appear(Window *window) { + NotificationsData *data = window_get_user_data(window); + + prv_update_text_layer_visibility(data); +} + +static void prv_window_disappear(Window *window) { + NotificationsData *data = window_get_user_data(window); + prv_loaded_notification_list_deinit(data->loaded_notification_list); + data->loaded_notification_list = NULL; +} + +static void prv_window_load(Window *window) { + NotificationsData *data = window_get_user_data(window); + MenuLayer *menu_layer = &data->menu_layer; + const GRect menu_layer_frame = PBL_IF_RECT_ELSE( + window->layer.bounds, grect_inset_internal(window->layer.bounds, 0, STATUS_BAR_LAYER_HEIGHT)); + menu_layer_init(menu_layer, &menu_layer_frame); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = prv_get_num_rows_callback, + .draw_row = prv_draw_row_callback, + .get_cell_height = prv_get_cell_height, + .select_click = prv_select_callback, + }); + + menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack); + menu_layer_set_highlight_colors(menu_layer, + PBL_IF_COLOR_ELSE(DEFAULT_NOTIFICATION_COLOR, GColorBlack), + GColorWhite); + + menu_layer_set_click_config_onto_window(menu_layer, window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); + + TextLayer *text_layer = &data->text_layer; + const int16_t horizontal_margin = 5; + const GFont font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); + // configure text layer to be vertically aligned (15 is hacking around our poor fonts) + text_layer_init_with_parameters(text_layer, + &GRect(horizontal_margin, window->layer.bounds.size.h / 2 - 15, + window->layer.bounds.size.w - horizontal_margin, + window->layer.bounds.size.h / 2), + i18n_get("No notifications", data), font, GColorBlack, + GColorWhite, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(&window->layer, text_layer_get_layer(text_layer)); + +#if PBL_ROUND + GColor bg_color = GColorClear; + GColor fg_color = GColorBlack; + + StatusBarLayer *status_bar = &data->status_bar_layer; + status_bar_layer_init(status_bar); + status_bar_layer_set_colors(status_bar, bg_color, fg_color); + layer_add_child(&window->layer, &status_bar->layer); +#endif + + menu_layer_set_selected_index(menu_layer, MenuIndex(0, 1), + PBL_IF_RECT_ELSE(MenuRowAlignNone, MenuRowAlignCenter), false); +} + +static void prv_push_window(NotificationsData *data) { + Window *window = &data->window; + window_init(window, WINDOW_NAME("Notifications")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + .appear = prv_window_appear, + .disappear = prv_window_disappear, + }); + + const bool animated = true; + app_window_stack_push(window, animated); +} + +//////////////////// +// App boilerplate + +static void prv_handle_init(void) { + NotificationsData *data = s_data = app_zalloc_check(sizeof(NotificationsData)); + + app_state_set_user_data(data); + + data->notification_event_info = (EventServiceInfo) { + .type = PEBBLE_SYS_NOTIFICATION_EVENT, + .handler = prv_handle_notification, + }; + event_service_client_subscribe(&data->notification_event_info); + prv_load_notification_storage(data); + + prv_push_window(data); +} + +static void prv_handle_deinit(void) { + NotificationsData *data = app_state_get_user_data(); +#if PBL_ROUND + status_bar_layer_deinit(&data->status_bar_layer); +#endif + menu_layer_deinit(&data->menu_layer); + event_service_client_unsubscribe(&data->notification_event_info); + prv_loaded_notification_list_deinit(data->loaded_notification_list); + prv_notification_list_deinit(data->notification_list); + + i18n_free_all(data); + app_free(data); + s_data = NULL; +} + +static void prv_s_main(void) { + prv_handle_init(); + + app_event_loop(); + + prv_handle_deinit(); +} + + +const PebbleProcessMd* notifications_app_get_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = prv_s_main, + // UUID: b2cae818-10f8-46df-ad2b-98ad2254a3c1 + .uuid = {0xb2, 0xca, 0xe8, 0x18, 0x10, 0xf8, 0x46, 0xdf, + 0xad, 0x2b, 0x98, 0xad, 0x22, 0x54, 0xa3, 0xc1}, + }, + .name = i18n_noop("Notifications"), + .icon_resource_id = RESOURCE_ID_NOTIFICATIONS_APP_GLANCE, + }; + return (const PebbleProcessMd*) &s_app_md; +} diff --git a/src/fw/apps/system/notifications.h b/src/fw/apps/system/notifications.h new file mode 100644 index 0000000000..f59c7ad4e4 --- /dev/null +++ b/src/fw/apps/system/notifications.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* notifications_app_get_info(); diff --git a/src/fw/apps/system/reminders/reminder.c b/src/fw/apps/system/reminders/reminder.c new file mode 100644 index 0000000000..247eb79a6f --- /dev/null +++ b/src/fw/apps/system/reminders/reminder.c @@ -0,0 +1,301 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "reminder.h" +#include "reminder_prefs.h" + +#include "applib/app.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "applib/ui/ui.h" +#include "applib/voice/transcription_dialog.h" +#include "applib/voice/voice_window.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "resource/timeline_resource_ids.auto.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/clock.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" +#include "util/time/time.h" + +#include "system/logging.h" + +typedef enum ReminderAppUIState { + ReminderAppUIState_Start, + ReminderAppUIState_WaitForDictationEvent, + ReminderAppUIState_Exit, +} ReminderAppUIState; + +typedef struct ReminderAppData { + Window window; + VoiceWindow *voice_window; + EventServiceInfo event_service_info; + TranscriptionDialog transcription_dialog; + char *dialog_text; + char *reminder_str; + time_t timestamp; + ReminderAppUIState ui_state; +} ReminderAppData; + +static void prv_create_reminder(ReminderAppData *data) { + AttributeList pin_attr_list = {0}; + attribute_list_add_uint32(&pin_attr_list, AttributeIdIconTiny, + TIMELINE_RESOURCE_NOTIFICATION_REMINDER); + attribute_list_add_cstring(&pin_attr_list, AttributeIdTitle, data->reminder_str); + attribute_list_add_uint8(&pin_attr_list, AttributeIdBgColor, GColorChromeYellowARGB8); + + AttributeList completed_attr_list = {0}; + attribute_list_add_cstring(&completed_attr_list, AttributeIdTitle, + i18n_get("Completed", &pin_attr_list)); + + AttributeList postpone_attr_list = {0}; + attribute_list_add_cstring(&postpone_attr_list, AttributeIdTitle, + i18n_get("Postpone", &pin_attr_list)); + + AttributeList remove_attr_list = {0}; + attribute_list_add_cstring(&remove_attr_list, AttributeIdTitle, + i18n_get("Remove", &pin_attr_list)); + + const int num_actions = 3; + TimelineItemActionGroup action_group = { + .num_actions = num_actions, + .actions = (TimelineItemAction[]) { + { + .id = 0, + .type = TimelineItemActionTypeComplete, + .attr_list = completed_attr_list, + }, + { + .id = 1, + .type = TimelineItemActionTypePostpone, + .attr_list = postpone_attr_list, + }, + { + .id = 2, + .type = TimelineItemActionTypeRemoteRemove, + .attr_list = remove_attr_list, + } + }, + }; + + TimelineItem *item = timeline_item_create_with_attributes(data->timestamp, + 0, // duration + TimelineItemTypePin, + LayoutIdGeneric, + &pin_attr_list, + &action_group); + item->header.from_watch = true; + item->header.parent_id = (Uuid)UUID_REMINDERS_DATA_SOURCE; + timeline_add(item); + + // Tweak the item before adding the reminder + item->header.parent_id = item->header.id; + uuid_generate(&item->header.id); + item->header.type = TimelineItemTypeReminder; + item->header.layout = LayoutIdReminder; + reminders_insert(item); + + i18n_free_all(&pin_attr_list); + attribute_list_destroy_list(&pin_attr_list); + attribute_list_destroy_list(&completed_attr_list); + attribute_list_destroy_list(&postpone_attr_list); + attribute_list_destroy_list(&remove_attr_list); + timeline_item_destroy(item); +} + +static void prv_push_success_dialog(void) { + SimpleDialog *simple_dialog = simple_dialog_create("Reminder Added"); + Dialog *dialog = simple_dialog_get_dialog(simple_dialog); + dialog_set_text(dialog, i18n_get("Added", dialog)); + dialog_set_icon(dialog, RESOURCE_ID_GENERIC_REMINDER_LARGE); + dialog_set_background_color(dialog, GColorChromeYellow); + dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT); + app_simple_dialog_push(simple_dialog); + i18n_free_all(dialog); +} + +static void prv_confirm_cb(void *context) { + ReminderAppData *data = context; + data->ui_state = ReminderAppUIState_Exit; + prv_create_reminder(data); + prv_push_success_dialog(); +} + +static void prv_push_transcription_dialog(ReminderAppData *data) { + TranscriptionDialog *transcription_dialog = &data->transcription_dialog; + transcription_dialog_init(transcription_dialog); + transcription_dialog_update_text(transcription_dialog, + data->dialog_text, + strlen(data->dialog_text)); + transcription_dialog_set_callback(transcription_dialog, prv_confirm_cb, data); + Dialog *dialog = expandable_dialog_get_dialog((ExpandableDialog *)transcription_dialog); + dialog_set_destroy_on_pop(dialog, false /* free_on_pop */); + app_transcription_dialog_push(transcription_dialog); +} + +static void prv_build_transcription_dialog_text(ReminderAppData *data) { + if (data->dialog_text) { + app_free(data->dialog_text); + } + const size_t sentence_len = strlen(data->reminder_str); + const size_t date_time_len = 32; // "September 19th 9:05pm", "Yesterday 12:33pm" + const size_t required_buf_size = sentence_len + date_time_len + 2 /* \n\n */ + 1 /* \0 */; + data->dialog_text = app_zalloc_check(required_buf_size); + + // The string that is being built below looks something like: + // "Take out the trash" + // + // "Tomorrow 7:00AM" + + int buf_space_remaining = required_buf_size - 1 /*for the final \0 */; + strncpy(data->dialog_text, data->reminder_str, sentence_len); + // Having to call MAX everytime is a bit silly, but the strn function expect a size_t (unsigned). + // Calling MAX ensures that a negative value isn't passed in which gets cast to something positive + buf_space_remaining = MAX(buf_space_remaining - sentence_len, 0); + + strncat(data->dialog_text, "\n\n", buf_space_remaining); + buf_space_remaining = MAX(buf_space_remaining - 2, 0); + + + char tmp[date_time_len]; + clock_get_friendly_date(tmp, date_time_len, data->timestamp); + strncat(data->dialog_text, tmp, buf_space_remaining); + buf_space_remaining = MAX(buf_space_remaining - strlen(tmp), 0); + strncat(data->dialog_text, " ", buf_space_remaining); + buf_space_remaining = MAX(buf_space_remaining - 1, 0);; + + clock_get_time_number(tmp, date_time_len, data->timestamp); + strncat(data->dialog_text, tmp, buf_space_remaining); + buf_space_remaining = MAX(buf_space_remaining - strlen(tmp), 0); + + clock_get_time_word(tmp, date_time_len, data->timestamp); + strncat(data->dialog_text, tmp, buf_space_remaining); +} + +static void prv_handle_dictation_event(PebbleEvent *e, void *context) { + ReminderAppData *data = context; + const DictationSessionStatus status = e->dictation.result; + + if (status == DictationSessionStatusSuccess) { + if (data->reminder_str) { + app_free(data->reminder_str); + } + const size_t reminder_str_len = strlen(e->dictation.text); + data->reminder_str = app_zalloc_check(reminder_str_len + 1 /* \0 */); + strcpy(data->reminder_str, e->dictation.text); + data->reminder_str[reminder_str_len] = '\0'; + + data->timestamp = e->dictation.timestamp; + if (data->timestamp == 0) { + // If the user didn't specify a time set it to be 1 hour from the current time, + // rounded up to the nearest 15 min. + // Ex: a reminder created at 10:08 AM with no specified time is due at 11:15 AM + time_t utc_sec = rtc_get_time() + SECONDS_PER_HOUR + (15 * SECONDS_PER_MINUTE); + struct tm local_tm; + localtime_r(&utc_sec, &local_tm); + local_tm.tm_min -= (local_tm.tm_min % 15); + local_tm.tm_sec = 0; + data->timestamp = mktime(&local_tm); + } + + // If the user doesn't accept the transcription, try again. + data->ui_state = ReminderAppUIState_Start; + + prv_build_transcription_dialog_text(data); + + prv_push_transcription_dialog(data); + } else { + // Exit immediately because this event may or may not be handled before the main window appears. + data->ui_state = ReminderAppUIState_Exit; + app_window_stack_pop_all(false); + } +} + +static void prv_appear(struct Window *window) { + ReminderAppData *data = app_state_get_user_data(); + switch (data->ui_state) { + case ReminderAppUIState_Start: + // Start a transcription + data->ui_state = ReminderAppUIState_WaitForDictationEvent; + voice_window_reset(data->voice_window); + voice_window_push(data->voice_window); + break; + case ReminderAppUIState_WaitForDictationEvent: + break; + case ReminderAppUIState_Exit: + app_window_stack_pop_all(false); + break; + default: + WTF; + } +} + +static NOINLINE void prv_init(void) { + ReminderAppData *data = app_zalloc_check(sizeof(ReminderAppData)); + app_state_set_user_data(data); + + data->ui_state = ReminderAppUIState_Start; + + // This "background" window is needed because without voice confirmation enabled, + // the voice window pops before we get the event and can push the transcription dialog. + // This means we have no windows for a moment and thus the app deinits. + // This window is now also used to catch a 'back' at the confirmation dialog. + Window *window = &data->window; + window_init(window, WINDOW_NAME("Reminders")); + + WindowHandlers handlers = { .appear = prv_appear, }; + window_set_window_handlers(window, &handlers); + + data->event_service_info = (EventServiceInfo) { + .type = PEBBLE_DICTATION_EVENT, + .handler = prv_handle_dictation_event, + .context = data, + }; + event_service_client_subscribe(&data->event_service_info); + + data->voice_window = voice_window_create(NULL, 0, VoiceEndpointSessionTypeNLP); + voice_window_set_confirmation_enabled(data->voice_window, false); + + // Let the main window manage the voice window + app_window_stack_push(window, false); +} + +static void prv_deinit(void) { + ReminderAppData *data = app_state_get_user_data(); + voice_window_destroy(data->voice_window); + event_service_client_unsubscribe(&data->event_service_info); + app_free(data->dialog_text); + app_free(data); +} + +static void prv_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} + +const PebbleProcessMd* reminder_app_get_info(void) { + PebbleProtocolCapabilities capabilities; + bt_persistent_storage_get_cached_system_capabilities(&capabilities); + SerializedReminderAppPrefs *prefs = watch_app_prefs_get_reminder(); + + const bool is_visible_in_launcher = capabilities.reminders_app_support && + (prefs ? (prefs->appState == ReminderAppState_Enabled) : false); + + task_free(prefs); + + static const PebbleProcessMdSystem s_reminder_app_info = { + .common = { + .main_func = prv_main, + .uuid = UUID_REMINDERS_DATA_SOURCE, + }, + .name = i18n_noop("Reminder"), + .icon_resource_id = RESOURCE_ID_GENERIC_REMINDER_TINY, + }; + + return is_visible_in_launcher ? (const PebbleProcessMd *)&s_reminder_app_info : NULL; +} diff --git a/src/fw/apps/system/reminders/reminder.h b/src/fw/apps/system/reminders/reminder.h new file mode 100644 index 0000000000..4a425335d9 --- /dev/null +++ b/src/fw/apps/system/reminders/reminder.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* reminder_app_get_info(void); diff --git a/src/fw/apps/system/reminders/reminder_prefs.h b/src/fw/apps/system/reminders/reminder_prefs.h new file mode 100644 index 0000000000..0ac80602f3 --- /dev/null +++ b/src/fw/apps/system/reminders/reminder_prefs.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" + +#define PREF_KEY_REMINDER_APP "remindersApp" + +typedef enum ReminderAppState { + ReminderAppState_NotEnabled = 0, + ReminderAppState_NotConfigured = 1, + ReminderAppState_Enabled = 2, + ReminderAppStateCount +} ReminderAppState; + +typedef struct PACKED SerializedReminderAppPrefs { + uint8_t appState; // actually enum ReminderAppState +} SerializedReminderAppPrefs; diff --git a/src/fw/apps/system/send_text/prefs.h b/src/fw/apps/system/send_text/prefs.h new file mode 100644 index 0000000000..09e9692671 --- /dev/null +++ b/src/fw/apps/system/send_text/prefs.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "util/attributes.h" +#include "util/uuid.h" + +typedef struct PACKED { + Uuid contact_uuid; + Uuid address_uuid; + bool is_fav; +} SerializedSendTextContact; + +typedef struct PACKED { + uint8_t num_contacts; + SerializedSendTextContact contacts[]; +} SerializedSendTextPrefs; diff --git a/src/fw/apps/system_apps/send_text/send_text.c b/src/fw/apps/system/send_text/send_text.c similarity index 91% rename from src/fw/apps/system_apps/send_text/send_text.c rename to src/fw/apps/system/send_text/send_text.c index 9622fb3792..c1b7516973 100644 --- a/src/fw/apps/system_apps/send_text/send_text.c +++ b/src/fw/apps/system/send_text/send_text.c @@ -1,39 +1,26 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "send_text.h" -#include "send_text_app_prefs.h" +#include "prefs.h" #include "applib/app.h" #include "applib/app_exit_reason.h" #include "applib/ui/app_window_stack.h" #include "applib/ui/ui.h" -#include "apps/system_apps/timeline/peek_layer.h" +#include "apps/system/timeline/peek_layer.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/contacts_db.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/blob_db/watch_app_prefs_db.h" -#include "services/normal/contacts/contacts.h" -#include "services/normal/notifications/notification_constants.h" -#include "services/normal/send_text_service.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/contacts_db.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" +#include "pbl/services/contacts/contacts.h" +#include "pbl/services/notifications/notification_constants.h" +#include "pbl/services/send_text_service.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_actions.h" #include "shell/prefs.h" #include "system/logging.h" #include "system/passert.h" @@ -134,7 +121,7 @@ static void prv_open_action_menu(SendTextAppData *data, const char *number) { item, TimelineItemActionTypeResponse); if (!reply_action) { - PBL_LOG(LOG_LEVEL_ERROR, "Not opening response menu - unable to load reply action"); + PBL_LOG_ERR("Not opening response menu - unable to load reply action"); timeline_item_destroy(item); return; } @@ -338,6 +325,9 @@ static void prv_init(void) { menu_layer_set_highlight_colors(&data->menu_layer, SEND_TEXT_APP_HIGHLIGHT_COLOR, GColorWhite); menu_layer_set_click_config_onto_window(&data->menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(&data->menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(&data->menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(&data->menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); layer_add_child(&data->window.layer, menu_layer_get_layer(&data->menu_layer)); StatusBarLayer *status_layer = &data->status_layer; diff --git a/src/fw/apps/system/send_text/send_text.h b/src/fw/apps/system/send_text/send_text.h new file mode 100644 index 0000000000..86554a322c --- /dev/null +++ b/src/fw/apps/system/send_text/send_text.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* send_text_app_get_info(); diff --git a/src/fw/apps/system/settings/activity_tracker.c b/src/fw/apps/system/settings/activity_tracker.c new file mode 100644 index 0000000000..7b71ffa1ce --- /dev/null +++ b/src/fw/apps/system/settings/activity_tracker.c @@ -0,0 +1,268 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "activity_tracker.h" +#include "menu.h" +#include "window.h" + +#include "applib/app.h" +#include "applib/app_timer.h" +#include "applib/ui/kino/kino_reel.h" +#include "applib/ui/option_menu_window.h" +#include "applib/ui/ui.h" +#include "applib/ui/window.h" +#include "applib/ui/window_stack.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "popups/switch_worker_ui.h" +#include "process_management/app_menu_data_source.h" +#include "process_management/worker_manager.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/normal/watchface.h" +#include "system/passert.h" + +#include + +typedef struct SettingsActivityTrackerData { + OptionMenu option_menu; + MenuLayer menu_layer; + AppMenuDataSource *data_source; + TextLayer *text_layer; + EventServiceInfo worker_launch_info; +} SettingsActivityTrackerData; + +//////////////////// +// AppMenuDataSource callbacks + +static bool prv_app_filter_callback(struct AppMenuDataSource *const source, + AppInstallEntry *entry) { + if (!app_install_entry_is_hidden(entry) && + app_install_entry_has_worker(entry)) { + return true; + } + return false; +} + +static int16_t prv_get_chosen_row_index_for_id(SettingsActivityTrackerData *data, + AppInstallId worker_id) { + if (worker_id == INSTALL_ID_INVALID) { + return 0; + } + + const uint16_t current_worker_app_index = + app_menu_data_source_get_index_of_app_with_install_id(data->data_source, worker_id); + + if (current_worker_app_index == MENU_INDEX_NOT_FOUND) { + return 0; + } else { + return current_worker_app_index + 1; + } +} + +// Gets the current chosen row index; i.e., the row which was most recently chosen by the user. +static int16_t prv_get_chosen_row_index(SettingsActivityTrackerData *data) { + const AppInstallId worker_id = worker_manager_get_current_worker_id(); + return prv_get_chosen_row_index_for_id(data, worker_id); +} + +static int prv_num_rows(SettingsActivityTrackerData *data) { + if (data->data_source) { + return app_menu_data_source_get_count(data->data_source); + } else { + return 0; + } +} + +static void prv_reload_menu_data(void *context) { + SettingsActivityTrackerData *data = context; + const uint16_t count = prv_num_rows(data); + const bool use_icons = (count != 0); + option_menu_set_icons_enabled(&data->option_menu, use_icons /* icons_enabled */); + + option_menu_set_choice(&data->option_menu, prv_get_chosen_row_index(data)); + option_menu_reload_data(&data->option_menu); +} + +// Settings Menu callbacks +/////////////////////////// + +static void prv_select_cb(OptionMenu *option_menu, int row, void *context) { + SettingsActivityTrackerData *data = context; + if (app_menu_data_source_get_count(data->data_source) == 0) { + return; + } + if (row == 0) { + // Killing current worker + process_manager_put_kill_process_event(PebbleTask_Worker, true /* graceful */); + worker_manager_set_default_install_id(INSTALL_ID_INVALID); + } else { + const uint16_t app_index = row - 1; // offset because of the "None" selection + const AppMenuNode *app_node = + app_menu_data_source_get_node_at_index(data->data_source, app_index); + if (worker_manager_get_task_context()->install_id == INSTALL_ID_INVALID) { + // No worker currently running, launch this one and make it the default + worker_manager_put_launch_worker_event(app_node->install_id); + worker_manager_set_default_install_id(app_node->install_id); + } else if (worker_manager_get_task_context()->install_id != app_node->install_id) { + // Undo the choice change that the OptionMenu does before we call select. We may decline + // the change and therefore we don't want it to visually update yet. prv_worker_launch_handler + // will update the choice if it fires. + option_menu_set_choice(&data->option_menu, prv_get_chosen_row_index(data)); + + // Switching to a different worker, display confirmation dialog + switch_worker_confirm(app_node->install_id, true /* set as default */, + app_state_get_window_stack()); + } else { + // User selected the option they already had, do nothing + } + } +} + +#if PBL_RECT +static void prv_draw_no_activities_cell_rect(GContext *ctx, const Layer *cell_layer, + const char *no_activities_string) { + const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + GRect box = cell_layer->bounds; + + const GTextOverflowMode overflow = GTextOverflowModeTrailingEllipsis; + const GTextAlignment alignment = GTextAlignmentCenter; + + const GSize text_size = graphics_text_layout_get_max_used_size(ctx, no_activities_string, font, + box, overflow, alignment, NULL); + + // We want to position the text in the center of the cell veritically, + // we divide the height of the cell by two and subtract half of the text size. + // However, that just puts the TOP of a line vertically aligned. + // So we also have to subtract half of a single line's width. + box.origin.y = (box.size.h - text_size.h - fonts_get_font_height(font)/2) / 2; + + graphics_draw_text(ctx, no_activities_string, font, box, overflow, alignment, NULL); +} +#endif + +#if PBL_ROUND +static void prv_draw_no_activities_cell_round(GContext *ctx, const Layer *cell_layer, + const char *no_activities_string) { + menu_cell_basic_draw(ctx, cell_layer, no_activities_string, NULL, NULL); +} +#endif + +static uint16_t prv_get_num_rows_cb(OptionMenu *option_menu, void *context) { + SettingsActivityTrackerData *data = context; + const uint16_t count = prv_num_rows(data); + return count + 1; +} + +static void prv_draw_row_cb(OptionMenu *option_menu, GContext *ctx, const Layer *cell_layer, + const GRect *text_frame, uint32_t row, bool selected, void *context) { + SettingsActivityTrackerData *data = context; + + if (prv_num_rows(data) == 0) { + // Draw "No background apps" box and exit + const char *no_background_apps_string = i18n_get("No background apps", data); + PBL_IF_RECT_ELSE(prv_draw_no_activities_cell_rect, + prv_draw_no_activities_cell_round) + (ctx, cell_layer, no_background_apps_string); + return; + } + + const char *title = NULL; + if (row == 0) { + title = i18n_get("None", data); + } else { + AppMenuNode *node = app_menu_data_source_get_node_at_index(data->data_source, row - 1); + title = node->name; + } + + option_menu_system_draw_row(option_menu, ctx, cell_layer, text_frame, title, false, NULL); +} + +static uint16_t prv_row_height_cb(OptionMenu *option_menu, uint16_t row, bool is_selected, + void *context) { + const int16_t cell_height = + option_menu_default_cell_height(option_menu->content_type, is_selected); +#if PBL_RECT + if (prv_num_rows(context) == 0) { + // When we have no background apps, we want a double height row to display the + // 'No background apps' line, so that translations can fit and we stop wasting so much screen + // space. + return 2 * cell_height; + } +#endif + return cell_height; +} + +static void prv_worker_launch_handler(PebbleEvent *event, void *context) { + // Our worker changed while we were visible, update the selected choice + SettingsActivityTrackerData *data = context; + + const AppInstallId worker_id = event->launch_app.id; + const int16_t chosen_row = prv_get_chosen_row_index_for_id(data, worker_id); + + option_menu_set_choice(&data->option_menu, chosen_row); +} + +static void prv_unload_cb(OptionMenu *option_menu, void *context) { + SettingsActivityTrackerData *data = context; + + event_service_client_unsubscribe(&data->worker_launch_info); + + app_menu_data_source_deinit(data->data_source); + + app_free(data->data_source); + data->data_source = NULL; + + option_menu_deinit(&data->option_menu); + + i18n_free_all(data); + app_free(data); +} + +static Window *prv_init(void) { + SettingsActivityTrackerData *data = app_zalloc_check(sizeof(SettingsActivityTrackerData)); + + const OptionMenuCallbacks option_menu_callbacks = { + .unload = prv_unload_cb, + .draw_row = prv_draw_row_cb, + .select = prv_select_cb, + .get_num_rows = prv_get_num_rows_cb, + .get_cell_height = prv_row_height_cb, + }; + + data->data_source = app_zalloc_check(sizeof(AppMenuDataSource)); + app_menu_data_source_init(data->data_source, &(AppMenuDataSourceCallbacks) { + .changed = prv_reload_menu_data, + .filter = prv_app_filter_callback, + }, data); + + option_menu_init(&data->option_menu); + // Not using option_menu_configure because prv_reload_menu_data already sets + // icons_enabled and chosen row index + option_menu_set_status_colors(&data->option_menu, GColorWhite, GColorBlack); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + option_menu_set_highlight_colors(&data->option_menu, highlight_bg, gcolor_legible_over(highlight_bg)); + option_menu_set_title(&data->option_menu, i18n_get("Background App", data)); + option_menu_set_content_type(&data->option_menu, OptionMenuContentType_SingleLine); + option_menu_set_callbacks(&data->option_menu, &option_menu_callbacks, data); + prv_reload_menu_data(data); + + data->worker_launch_info = (EventServiceInfo) { + .type = PEBBLE_WORKER_LAUNCH_EVENT, + .handler = prv_worker_launch_handler, + .context = data + }; + event_service_client_subscribe(&data->worker_launch_info); + + return &data->option_menu.window; +} + +const SettingsModuleMetadata *settings_activity_tracker_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Background App"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/activity_tracker.h b/src/fw/apps/system/settings/activity_tracker.h new file mode 100644 index 0000000000..356c5119f9 --- /dev/null +++ b/src/fw/apps/system/settings/activity_tracker.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_activity_tracker_get_info(void); diff --git a/src/fw/apps/system/settings/bluetooth.c b/src/fw/apps/system/settings/bluetooth.c new file mode 100644 index 0000000000..f5d8bf8bba --- /dev/null +++ b/src/fw/apps/system/settings/bluetooth.c @@ -0,0 +1,603 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "bluetooth.h" +#include "menu.h" +#include "remote.h" +#include "window.h" + +#include "applib/app.h" +#include "applib/app_focus_service.h" +#include "applib/event_service_client.h" +#include "applib/fonts/fonts.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/ui.h" +#include "comm/bt_lock.h" +#include "comm/ble/gap_le_connection.h" +#include "comm/ble/gap_le_device_name.h" +#include "drivers/rtc.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/system_icons.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/system_task.h" +#include "pbl/services/bluetooth/ble_hrm.h" +#include "shell/system_theme.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/string.h" + +#include +#include +#include + +#include +#include + +#define HEADER_BUFFER_SIZE 22 + +#define SHARING_HEART_RATE_EXTRA_HEIGHT_PX (18) + +typedef enum SettingsBluetooth { + SettingsBluetoothAirplaneMode, + SettingsBluetoothTotal, +} SettingsBluetooth; + +enum { + BluetoothIconIdx, + BluetoothAltIconIdx, + AirplaneIconIdx, + NumIcons, +}; + +static const uint32_t ICON_RESOURCE_ID[NumIcons] = { + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT, + RESOURCE_ID_SETTINGS_ICON_AIRPLANE, +}; + +typedef enum { + ToggleStateIdle, + ToggleStateEnablingBluetooth, + ToggleStateDisablingBluetooth, +} ToggleState; + +typedef struct SettingsBluetoothData { + SettingsCallbacks callbacks; + + GBitmap icon_heap_bitmap[NumIcons]; + + ListNode* remote_list_head; + + char header_buffer[HEADER_BUFFER_SIZE]; + ToggleState toggle_state; + bool did_enable_pairability; + + EventServiceInfo bt_airplane_event_info; + EventServiceInfo bt_connection_event_info; + EventServiceInfo bt_pairing_event_info; + EventServiceInfo ble_device_name_updated_event_info; +#ifdef CONFIG_HRM + EventServiceInfo ble_hrm_sharing_event_info; +#endif +} SettingsBluetoothData; + +// BT stack interaction stuff +/////////////////////////// + +static void settings_bluetooth_toggle_airplane_mode(SettingsBluetoothData* data) { + const bool airplane_mode = bt_ctl_is_airplane_mode_on(); + bt_ctl_set_airplane_mode_async(!airplane_mode); + data->toggle_state = + airplane_mode ? ToggleStateEnablingBluetooth : ToggleStateDisablingBluetooth; + settings_menu_mark_dirty(SettingsMenuItemBluetooth); +} + +bool is_remote_connected(StoredRemote* remote) { + return (remote->ble.connection != NULL); +} + +static int remote_comparator(StoredRemote* remote, StoredRemote* other) { + if (is_remote_connected(remote) != is_remote_connected(other)) { + return is_remote_connected(remote) ? -1 : 1; + } else { + return strncmp(remote->name, other->name, sizeof(remote->name)); + } +} + +static void add_remote(SettingsBluetoothData* data, StoredRemote* remote) { + const bool ascending = false; + data->remote_list_head = list_sorted_add(data->remote_list_head, &remote->list_node, + (Comparator) remote_comparator, ascending); +} + +static StoredRemote* stored_remote_create(void) { + StoredRemote* remote = task_malloc_check(sizeof(*remote)); + *remote = (StoredRemote){}; + return remote; +} + +static void prv_copy_device_name_with_fallback(StoredRemote *remote, const char *name) { + if (!name || strlen(name) == 0) { + i18n_get_with_buffer("", remote->name, sizeof(remote->name)); + } else { + strncpy(remote->name, name, sizeof(remote->name)); + } +} + +static void prv_add_ble_remote(BTDeviceInternal *device, SMIdentityResolvingKey *irk, + const char *name, BTBondingID *id, void *context) { + SettingsBluetoothData *data = (SettingsBluetoothData*) context; + if (!data) { + return; + } + + StoredRemote* remote = stored_remote_create(); + remote->ble.bonding = *id; + prv_copy_device_name_with_fallback(remote, name); + add_remote(data, remote); +} + +static void prv_add_ble_remotes(SettingsBluetoothData *data) { + bt_persistent_storage_for_each_ble_pairing(prv_add_ble_remote, data); + + StoredRemote *remote = (StoredRemote *)data->remote_list_head; + while (remote) { + SMIdentityResolvingKey irk; + BTDeviceInternal device; + + if (bt_persistent_storage_get_ble_pairing_by_id(remote->ble.bonding, &irk, &device, NULL)) { + bt_lock(); + GAPLEConnection *connection = gap_le_connection_find_by_irk(&irk); + if (!connection) { + connection = gap_le_connection_by_device(&device); + } + remote->ble.connection = connection; +#ifdef CONFIG_HRM + remote->ble.is_sharing_heart_rate = ble_hrm_is_sharing_to_connection(connection); +#endif + bt_unlock(); + } + remote = (StoredRemote *)remote->list_node.next; + } +} + +static void prv_clear_remote_list(SettingsBluetoothData* data) { + while (data->remote_list_head) { + StoredRemote* remote = (StoredRemote*) data->remote_list_head; + data->remote_list_head = list_pop_head(&remote->list_node); + task_free(remote); + } +} + +static void prv_reload_remote_list(SettingsBluetoothData* data) { + prv_clear_remote_list(data); + prv_add_ble_remotes(data); +} + +static void settings_bluetooth_update_remotes_private(SettingsBluetoothData* data) { + prv_reload_remote_list(data); + + if (!data->remote_list_head) { + strncpy(data->header_buffer, i18n_get("Pairing Instructions", data), HEADER_BUFFER_SIZE); + } else { + const unsigned int num_remotes = list_count(data->remote_list_head); + sniprintf(data->header_buffer, HEADER_BUFFER_SIZE, + (num_remotes != 1) ? i18n_get("%u Paired Phones", data) : + i18n_get("%u Paired Phone", data), + num_remotes); + } +} + +void settings_bluetooth_update_remotes(SettingsBluetoothData *data) { + settings_bluetooth_update_remotes_private(data); + settings_menu_reload_data(SettingsMenuItemBluetooth); +} + +////////// + +static void prv_settings_bluetooth_event_handler(PebbleEvent *event, void *context) { + SettingsBluetoothData* settings_data = (SettingsBluetoothData *) context; + PBL_LOG_DBG("BT EVENT"); + switch (event->type) { + case PEBBLE_BT_CONNECTION_EVENT: + // If BT Settings is open, update BLE device name upon connecting device: + if (event->bluetooth.connection.is_ble && + event->bluetooth.connection.state == PebbleBluetoothConnectionEventStateConnected) { + // https://pebbletechnology.atlassian.net/browse/PBL-22176 + // iOS seems to respond with 0x0E (Unlikely Error) when performing this request while + // the encryption set up is going on. For non-bonded devices it will work fine though. + gap_le_device_name_request(&event->bluetooth.connection.device); + } + // fall-through! + case PEBBLE_BT_PAIRING_EVENT: +#ifdef CONFIG_HRM + case PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT: +#endif + case PEBBLE_BLE_DEVICE_NAME_UPDATED_EVENT: { + bool had_remotes = (settings_data->remote_list_head != NULL); + settings_bluetooth_update_remotes_private(settings_data); + bool has_remotes = (settings_data->remote_list_head != NULL); + + // Handle single phone pairing policy: enable/disable advertising based on pairing state + if (had_remotes && !has_remotes) { + // Device was removed, enable advertising + if (!settings_data->did_enable_pairability) { + bt_pairability_use(); + settings_data->did_enable_pairability = true; + PBL_LOG_INFO("Enabled advertising - no paired devices"); + } + } else if (!had_remotes && has_remotes) { + // Device was added, disable advertising + if (settings_data->did_enable_pairability) { + bt_pairability_release(); + settings_data->did_enable_pairability = false; + PBL_LOG_INFO("Disabled advertising - device paired"); + } + } + + settings_menu_mark_dirty(SettingsMenuItemBluetooth); + break; + } + + case PEBBLE_BT_STATE_EVENT: { + settings_data->toggle_state = ToggleStateIdle; + settings_menu_mark_dirty(SettingsMenuItemBluetooth); + break; + } + + default: + break; + } +} + +// UI Stuff +///////////////////////////// +// Menu Layer Callbacks +///////////////////////////// +//-- Address +// ... +//| Airplane Mode: Off +//-- Paired Devices +//| Device Name +// Connected +//| Device Name +// + +#if PBL_RECT +static void prv_draw_stored_remote_item_rect(GContext *ctx, const Layer *cell_layer, + const char *remote_name, const char *connected_string, + const char *le_string, + const char *is_sharing_heart_rate_string) { + const GFont font = ((le_string || is_sharing_heart_rate_string) ? + fonts_get_system_font(FONT_KEY_GOTHIC_18) : NULL); + + if (le_string) { + GRect box = cell_layer->bounds; + box.size.w -= 5; + box.origin.y += 20; + box.size.h = 24; + + graphics_draw_text(ctx, le_string, font, box, GTextOverflowModeFill, GTextAlignmentRight, NULL); + } + + if (is_sharing_heart_rate_string) { + const int horizontal_margin = menu_cell_basic_horizontal_inset(); + GRect box = grect_inset(cell_layer->bounds, GEdgeInsets(0, horizontal_margin)); + box.origin.y += 38; + box.size.h = 24; + + graphics_draw_text(ctx, is_sharing_heart_rate_string, font, box, + GTextOverflowModeFill, GTextAlignmentLeft, NULL); + + // Gross hack to avoid centering the title / subtitle labels in the entire cell: + ((Layer *)cell_layer)->bounds.size.h -= SHARING_HEART_RATE_EXTRA_HEIGHT_PX; + } + + menu_cell_basic_draw(ctx, cell_layer, remote_name, connected_string, NULL); + + if (is_sharing_heart_rate_string) { + // Restore original height: + ((Layer *)cell_layer)->bounds.size.h += SHARING_HEART_RATE_EXTRA_HEIGHT_PX; + } +} +#endif + +bool settings_bluetooth_is_sharing_heart_rate_for_stored_remote(StoredRemote* remote) { +#ifdef CONFIG_HRM + return remote->ble.is_sharing_heart_rate; +#else + return false; +#endif // CONFIG_HRM +} + +#if PBL_ROUND +static void prv_draw_stored_remote_item_round(GContext *ctx, const Layer *cell_layer, + const char *remote_name, const char *connected_string, + const char *le_string, + const char *is_sharing_heart_rate_string) { +# ifdef CONFIG_HRM + _Static_assert(false, "FIXME: Implement round drawing code to show heart rate sharing status!"); +# endif // CONFIG_HRM + menu_cell_basic_draw(ctx, cell_layer, remote_name, connected_string, NULL); +} +#endif // PBL_ROUND + +static void draw_stored_remote_item(GContext *ctx, const Layer *cell_layer, + uint16_t device_index, SettingsBluetoothData *data) { + const uint32_t num_remotes = list_count(data->remote_list_head); + PBL_ASSERT(device_index < num_remotes, "Got index %" PRId16 " only have %" PRId32, + device_index, num_remotes); + StoredRemote* remote = (StoredRemote*) list_get_at(data->remote_list_head, device_index); + bool connected = is_remote_connected(remote); + + const char *le_string = NULL; + + const char *connected_string = connected ? i18n_get("Connected", data) : + PBL_IF_RECT_ELSE("", NULL); + + // Add ellipsis if the name might have been cut off by the mobile + const char ellipsis[] = UTF8_ELLIPSIS_STRING; + const size_t max_name_size = BT_DEVICE_NAME_BUFFER_SIZE - 2; + const size_t name_size = strnlen(remote->name, BT_DEVICE_NAME_BUFFER_SIZE); + char *remote_name = task_zalloc_check(max_name_size + sizeof(ellipsis)); + strncpy(remote_name, remote->name, name_size); + if (name_size > max_name_size) { + const size_t ellipsis_start_offset = utf8_get_size_truncate(remote_name, name_size); + strncpy(&remote_name[ellipsis_start_offset], ellipsis, sizeof(ellipsis)); + } + + const char *is_sharing_heart_rate = + (settings_bluetooth_is_sharing_heart_rate_for_stored_remote(remote) ? + i18n_get("Sharing Heart Rate ❤", data) : NULL); + + PBL_IF_RECT_ELSE(prv_draw_stored_remote_item_rect, + prv_draw_stored_remote_item_round)(ctx, cell_layer, remote_name, + connected_string, le_string, + is_sharing_heart_rate); + + task_free(remote_name); +} + + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + return list_count(data->remote_list_head) + 1; +} + +static int16_t prv_row_height_cb(SettingsCallbacks *context, uint16_t row, bool is_selected) { +#if PBL_RECT +# ifdef CONFIG_HRM + int heart_rate_sharing_text_height = 0; + if (row > 0) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + const uint16_t device_index = row - 1; + StoredRemote* remote = (StoredRemote*) list_get_at(data->remote_list_head, device_index); + if (settings_bluetooth_is_sharing_heart_rate_for_stored_remote(remote)) { + heart_rate_sharing_text_height = SHARING_HEART_RATE_EXTRA_HEIGHT_PX; + } + } +# else + const int heart_rate_sharing_text_height = 0; +# endif // CONFIG_HRM + return menu_cell_basic_cell_height() + heart_rate_sharing_text_height; +#elif PBL_ROUND + return (is_selected ? MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT : + MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT); +#else +#endif +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + if (row == 0) { + char device_name_buffer[BT_DEVICE_NAME_BUFFER_SIZE]; + const char *subtitle = NULL; + const char *title = i18n_get("Connection", data); + GBitmap *icon = NULL; + if (data->toggle_state == ToggleStateIdle) { + if (bt_ctl_is_airplane_mode_on()) { + subtitle = i18n_get("Airplane Mode", data); + icon = &data->icon_heap_bitmap[AirplaneIconIdx]; + } else { + // Always show device name as subtitle + bt_local_id_copy_device_name(device_name_buffer, false); + subtitle = device_name_buffer; + icon = &data->icon_heap_bitmap[BluetoothIconIdx]; + } + } else { + subtitle = (data->toggle_state == ToggleStateDisablingBluetooth) + ? i18n_get("Disabling...", data) : i18n_get("Enabling...", data); + icon = &data->icon_heap_bitmap[BluetoothAltIconIdx]; + } + + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, icon); + + // TODO PBL-23111: Decide how we should show these strings on round displays +#if PBL_RECT + // Hack: the pairing instruction is drawn in the cell callback, but outside of the cell... + const GDrawState draw_state = ctx->draw_state; + // Enable drawing outside of the cell: + ctx->draw_state.clip_box = ctx->dest_bitmap.bounds; + + graphics_context_set_text_color(ctx, GColorBlack); + GFont font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellSubtitle); + const int16_t horizontal_inset = menu_cell_basic_horizontal_inset() * 3; + GRect box = cell_layer->bounds; + box.origin.x = horizontal_inset; + box.origin.y = menu_cell_basic_cell_height() + (int16_t)9; + box.size.w -= horizontal_inset * 2; + box.size.h = 83; + + if (!data->remote_list_head) { + if (bt_ctl_is_airplane_mode_on()) { + graphics_draw_text(ctx, i18n_get("Disable Airplane Mode to connect.", data), font, + box, GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); + } else { + graphics_draw_text(ctx, i18n_get("Open the Pebble app on your phone to connect.", data), + font, box, GTextOverflowModeTrailingEllipsis, + GTextAlignmentCenter, NULL); + } + } else { + // Show message when any phone is paired (even if disconnected) + // Position the message lower to appear below the paired phone row + GRect msg_box = box; + msg_box.origin.y += menu_cell_basic_cell_height() - 10; + graphics_draw_text(ctx, i18n_get("Forget this device to pair a new device.", data), + font, msg_box, GTextOverflowModeTrailingEllipsis, + GTextAlignmentCenter, NULL); + } + + ctx->draw_state = draw_state; +#endif + } else { + const uint16_t device_index = row - 1; + draw_stored_remote_item(ctx, cell_layer, device_index, data); + } +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + if (row == 0) { + settings_bluetooth_toggle_airplane_mode(data); + return; + } + if (!data->remote_list_head) { + return; + } + prv_reload_remote_list(data); + StoredRemote* remote = (StoredRemote*) list_get_at(data->remote_list_head, row - 1); + settings_remote_menu_push(data, remote); +} + +static void prv_focus_handler(bool in_focus) { + if (!in_focus) { + return; + } + settings_menu_reload_data(SettingsMenuItemBluetooth); +} + +static void prv_expand_cb(SettingsCallbacks *context) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + + settings_bluetooth_update_remotes_private(data); + + // When entering the BT Settings, update device names of all connected devices: + if (!bt_ctl_is_airplane_mode_on()) { + gap_le_device_name_request_all(); + } + + data->bt_airplane_event_info = (EventServiceInfo) { + .type = PEBBLE_BT_STATE_EVENT, + .handler = prv_settings_bluetooth_event_handler, + .context = data, + }; + data->bt_connection_event_info = (EventServiceInfo) { + .type = PEBBLE_BT_CONNECTION_EVENT, + .handler = prv_settings_bluetooth_event_handler, + .context = data, + }; + data->bt_pairing_event_info = (EventServiceInfo) { + .type = PEBBLE_BT_PAIRING_EVENT, + .handler = prv_settings_bluetooth_event_handler, + .context = data, + }; + data->ble_device_name_updated_event_info = (EventServiceInfo) { + .type = PEBBLE_BLE_DEVICE_NAME_UPDATED_EVENT, + .handler = prv_settings_bluetooth_event_handler, + .context = data, + }; +#ifdef CONFIG_HRM + data->ble_hrm_sharing_event_info = (EventServiceInfo) { + .type = PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT, + .handler = prv_settings_bluetooth_event_handler, + .context = data, + }; + event_service_client_subscribe(&data->ble_hrm_sharing_event_info); +#endif + event_service_client_subscribe(&data->bt_airplane_event_info); + event_service_client_subscribe(&data->bt_connection_event_info); + event_service_client_subscribe(&data->bt_pairing_event_info); + event_service_client_subscribe(&data->ble_device_name_updated_event_info); + + // Only enable pairing/advertising if there are no paired devices (single phone policy) + data->did_enable_pairability = false; + if (!data->remote_list_head) { + bt_pairability_use(); + data->did_enable_pairability = true; + } + + // Reload & redraw after pairing popup + app_focus_service_subscribe_handlers((AppFocusHandlers) { .did_focus = prv_focus_handler }); +} + +// Turns off services that are part of the bluetooth settings menu such as enabling +// discovery. We don't want to keep these services running longer than necessary because +// they consume a fair amount of power +static void prv_hide_cb(SettingsCallbacks *context) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + + // Only release pairability if we enabled it + if (data->did_enable_pairability) { + bt_pairability_release(); + } + +#ifdef CONFIG_HRM + event_service_client_unsubscribe(&data->ble_hrm_sharing_event_info); +#endif + event_service_client_unsubscribe(&data->bt_airplane_event_info); + event_service_client_unsubscribe(&data->bt_connection_event_info); + event_service_client_unsubscribe(&data->bt_pairing_event_info); + event_service_client_unsubscribe(&data->ble_device_name_updated_event_info); + app_focus_service_unsubscribe(); +} + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsBluetoothData *data = (SettingsBluetoothData *) context; + + i18n_free_all(data); + + prv_clear_remote_list(data); + for (unsigned int idx = 0; idx < NumIcons; ++idx) { + gbitmap_deinit(&data->icon_heap_bitmap[idx]); + } + app_free(data); +} + +static Window *prv_init(void) { + SettingsBluetoothData *data = app_malloc_check(sizeof(SettingsBluetoothData)); + *data = (SettingsBluetoothData){}; + + for (unsigned int idx = 0; idx < NumIcons; ++idx) { + gbitmap_init_with_resource(&data->icon_heap_bitmap[idx], ICON_RESOURCE_ID[idx]); + } + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + .row_height = prv_row_height_cb, + .expand = prv_expand_cb, + .hide = prv_hide_cb, + }; + + return settings_window_create(SettingsMenuItemBluetooth, &data->callbacks); +} + +const SettingsModuleMetadata *settings_bluetooth_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Bluetooth"), + .init = prv_init, + }; + + return &s_module_info; +} + +#undef HEADER_BUFFER_SIZE diff --git a/src/fw/apps/system/settings/bluetooth.h b/src/fw/apps/system/settings/bluetooth.h new file mode 100644 index 0000000000..3189035c76 --- /dev/null +++ b/src/fw/apps/system/settings/bluetooth.h @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "kernel/events.h" +#include "menu.h" +#include "util/list.h" + +typedef struct GAPLEConnection GAPLEConnection; + +typedef struct StoredRemoteBLE { + BTBondingID bonding; + GAPLEConnection *connection; +#ifdef CONFIG_HRM + bool is_sharing_heart_rate; +#endif +} StoredRemoteBLE; + +typedef struct StoredRemote { + ListNode list_node; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + StoredRemoteBLE ble; +} StoredRemote; + +struct SettingsBluetoothData; + +void settings_bluetooth_update_remotes(struct SettingsBluetoothData *data); + +const SettingsModuleMetadata *settings_bluetooth_get_info(void); + +bool settings_bluetooth_is_sharing_heart_rate_for_stored_remote(StoredRemote* remote); + +#define BT_FORGET_PAIRING_STR \ + i18n_noop("Remember to also forget your Pebble's Bluetooth connection from your phone.") diff --git a/src/fw/apps/system/settings/certifications.h b/src/fw/apps/system/settings/certifications.h new file mode 100644 index 0000000000..c5630d88f9 --- /dev/null +++ b/src/fw/apps/system/settings/certifications.h @@ -0,0 +1,159 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "mfg/mfg_info.h" + +#include + + +//! Which regulatory marks and/or IDs a given product should display. +typedef struct RegulatoryFlags { +//! Australia Regulatory Compliance Mark + bool has_australia_rcm:1; +//! Canada IC ID + bool has_canada_ic:1; +//! Canada ISED ID + bool has_canada_ised:1; +//! China CMIIT ID + bool has_china_cmiit:1; +//! EU CE Mark + bool has_eu_ce:1; +//! EU WEEE Mark (wastebin with X) + bool has_eu_weee:1; +//! UKCA Mark + bool has_ukca:1; +//! Japan TELEC (Telecom Engineering Center) [R] mark and ID +//! (Radio equipment conformity) + bool has_japan_telec_r:1; +//! TELEC mark [T] mark and ID (Terminal equipment conformity) + bool has_japan_telec_t:1; +//! Korea +//! - KCC mark +//! - Details window with KCC mark and KCC ID + bool has_korea_kcc:1; +//! Mexico NOM NYCE mark + bool has_mexico_nom_nyce:1; +//! USA FCC Mark and FCC ID + bool has_usa_fcc:1; +} RegulatoryFlags; + +typedef struct CertificationIds { + const char *company_name; + const char *product_type; + const char *trademark; + const char *place_of_origin; + const char *dc_input; + const char *rated_voltage; + const char *milliampere_hour; + const char *watt_hour; + const char *canada_ic_id; + const char *canada_ised_id; + const char *china_cmiit_id; + const char *japan_telec_r_id; + const char *japan_telec_t_id; + const char *korea_kcc_id; + const char *mexico_ifetel_id; + const char *usa_fcc_id; +} CertificationIds; + + +static const RegulatoryFlags s_regulatory_flags_fallback = { +}; + +// Certifiation ID strings used for bigboards and such. +static const CertificationIds s_certification_ids_fallback = { + .company_name = "ACME Inc.", + .product_type = "Product Type", + .trademark = "Product Trademark", + .place_of_origin = "Country of Origin", + .dc_input = "XV/YYYmA", + .rated_voltage = "X.XV", + .milliampere_hour = "XXXmAh", + .watt_hour = "X.XXWh", + .canada_ic_id = "XXXXXX-YYY", + .canada_ised_id = "XXXXXX-YYYYYYYYYYY", + .china_cmiit_id = "ABCDEFGHIJ", + .japan_telec_r_id = "XXX-YYYYYY", + .japan_telec_t_id = "D XX YYYY ZZZ", + .korea_kcc_id = "WWWW-XXX-YYY-ZZZ", + .mexico_ifetel_id = "RCPPEXXXX-YYYY", + .usa_fcc_id = "XXX-YYY", +}; + + +static const RegulatoryFlags s_regulatory_flags_obelix = { + .has_canada_ised = true, + .has_eu_ce = true, + .has_eu_weee = true, + .has_ukca = true, + .has_usa_fcc = true, +}; + +static const CertificationIds s_certification_ids_obelix = { + .company_name = "Core Devices LLC", + .product_type = "Smart watch", + .trademark = "Pebble", + .place_of_origin = "Made in China", + .dc_input = "5 V / 300 mA", + .rated_voltage = "3.8 V", + .milliampere_hour = "185 mAh", + .watt_hour = "0.71 Wh", + .canada_ised_id = "34223-PEBBLETIME2", + .usa_fcc_id = "2BQB2-PEBBLETIME2", +}; + +static const RegulatoryFlags * prv_get_regulatory_flags(void) { +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + // TODO: add applicable flags + return &s_regulatory_flags_fallback; +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + return &s_regulatory_flags_obelix; +#else + return &s_regulatory_flags_fallback; +#endif +} + +//! Don't call this function directly. Use the prv_get_*_id functions instead. +static const CertificationIds * prv_get_certification_ids(void) { +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + // TODO: add real certification ids + return &s_certification_ids_fallback; +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + return &s_certification_ids_obelix; +#else + return &s_certification_ids_fallback; +#endif +} + +#define ID_GETTER(ID_KIND) \ + static const char * prv_get_##ID_KIND(void) { \ + return prv_get_certification_ids()->ID_KIND ?: \ + s_certification_ids_fallback.ID_KIND; \ + } + +ID_GETTER(company_name) +ID_GETTER(product_type) +ID_GETTER(trademark) +ID_GETTER(place_of_origin) +ID_GETTER(dc_input) +ID_GETTER(rated_voltage) +ID_GETTER(milliampere_hour) +ID_GETTER(watt_hour) +ID_GETTER(canada_ic_id) +ID_GETTER(china_cmiit_id) +ID_GETTER(japan_telec_r_id) +ID_GETTER(japan_telec_t_id) +ID_GETTER(korea_kcc_id) +ID_GETTER(mexico_ifetel_id) +ID_GETTER(usa_fcc_id) +ID_GETTER(canada_ised_id) + +#undef ID_GETTER + +//! Get the model string from MFG storage +//! @param buffer a character array that's at least MFG_INFO_MODEL_STRING_LENGTH in size +static void prv_get_model(char *buffer) { + mfg_info_get_model(buffer); +} diff --git a/src/fw/apps/system/settings/display.c b/src/fw/apps/system/settings/display.c new file mode 100644 index 0000000000..5c9a759493 --- /dev/null +++ b/src/fw/apps/system/settings/display.c @@ -0,0 +1,583 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "display.h" +#include "menu.h" +#include "option_menu.h" +#include "window.h" + +#include "applib/fonts/fonts.h" +#include "applib/ui/ui.h" +#include "drivers/ambient_light.h" +#include "drivers/battery.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "popups/notifications/notification_window.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "shell/prefs.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" + +#include +#include +#include +#include +#include + +// Forward decl so the parent menu can push the Backlight submenu. +static void prv_backlight_submenu_push(void); + +typedef struct SettingsDisplayData { + SettingsCallbacks callbacks; +} SettingsDisplayData; + +typedef struct SettingsBacklightData { + SettingsCallbacks callbacks; + char als_value_buffer[16]; // Buffer for ALS value display + char backlight_percent_buffer[16]; // Buffer for backlight percentage display + AppTimer *update_timer; // Timer for live updating debug values +} SettingsBacklightData; + +static const char *s_language_labels[] = { + [ShellLanguageInstalledPack] = i18n_noop("Custom"), + [ShellLanguageEnglish] = "English", + [ShellLanguageCatalan] = "Català", + [ShellLanguageGerman] = "Deutsch", + [ShellLanguageSpanish] = "Español", + [ShellLanguageFrench] = "Français", + [ShellLanguageItalian] = "Italiano", + [ShellLanguageDutch] = "Nederlands", + [ShellLanguagePortuguese] = "Português", +}; + +static void prv_language_menu_select(OptionMenu *option_menu, int selection, void *context) { + shell_prefs_set_language((ShellLanguage)selection); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_language_menu_push(SettingsDisplayData *data) { + const OptionMenuCallbacks callbacks = { + .select = prv_language_menu_select, + }; + settings_option_menu_push(i18n_noop("Language"), OptionMenuContentType_SingleLine, + shell_prefs_get_language(), &callbacks, + ARRAY_LENGTH(s_language_labels), false /* icons_enabled */, + s_language_labels, data); +} + +// Intensity Settings +///////////////////////////// + +static const uint32_t s_intensity_values[] = { 10, 25, 50, 100 }; + +static const char *s_intensity_labels[] = { + i18n_noop("Low"), + i18n_noop("Medium"), + i18n_noop("High"), + i18n_noop("Blinding") +}; + +#define BACKLIGHT_SCALE_GRANULARITY 5 +// Normalize the result from light get brightness as it sometimes +// will round down/up by a % +static uint8_t prv_get_scaled_brightness(void) { + return BACKLIGHT_SCALE_GRANULARITY + * ((backlight_get_intensity() + BACKLIGHT_SCALE_GRANULARITY - 1) + / BACKLIGHT_SCALE_GRANULARITY); +} + +static int prv_intensity_get_selection_index() { + const uint8_t intensity = prv_get_scaled_brightness(); + + // FIXME: PBL-22272 ... We will return idx 0 if someone has an old value for + // one of the intensity options + for (int i = 0; i < (int)ARRAY_LENGTH(s_intensity_values); i++) { + if (s_intensity_values[i] == intensity) { + return i; + } + } + return 0; +} + +static void prv_intensity_menu_select(OptionMenu *option_menu, int selection, void *context) { + backlight_set_intensity(s_intensity_values[selection]); + app_window_stack_remove(&option_menu->window, true /*animated*/); +} + +static void prv_intensity_menu_push(SettingsBacklightData *data) { + const int index = prv_intensity_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_intensity_menu_select, + }; + const char *title = PBL_IF_RECT_ELSE(i18n_noop("INTENSITY"), i18n_noop("Intensity")); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, ARRAY_LENGTH(s_intensity_labels), + true /* icons_enabled */, s_intensity_labels, data); +} + +#ifdef CONFIG_ORIENTATION_MANAGER +// Orientation Settings +///////////////////////////// +static const char *s_display_orientation_labels[] = { + i18n_noop("Default"), + i18n_noop("Left-Handed"), +}; + +static int prv_display_orientation_get_selection_index() { + return display_orientation_is_left() ? 1 : 0; +} + +static void prv_display_orientation_menu_select(OptionMenu *option_menu, int selection, + void *context) { + if (prv_display_orientation_get_selection_index() == selection) { + // No change + app_window_stack_remove(&option_menu->window, true /* animated */); + return; + } + + display_orientation_set_left(!display_orientation_is_left()); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_display_orientation_menu_push(SettingsDisplayData *data) { + const int index = prv_display_orientation_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_display_orientation_menu_select, + }; + + const char *title = i18n_noop("Orientation"); + settings_option_menu_push(title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_display_orientation_labels), true, + s_display_orientation_labels, data); +} +#endif + +// Timeout Settings +///////////////////////////// + +static const uint32_t s_timeout_values[] = { 3000, 5000, 8000 }; + +static const char *s_timeout_labels[] = { + i18n_noop("3 Seconds"), + i18n_noop("5 Seconds"), + i18n_noop("8 Seconds") +}; + +static int prv_timeout_get_selection_index() { + uint32_t timeout_ms = backlight_get_timeout_ms(); + for (size_t i = 0; i < ARRAY_LENGTH(s_timeout_values); i++) { + if (s_timeout_values[i] == timeout_ms) { + return i; + } + } + return 0; +} + +static void prv_timeout_menu_select(OptionMenu *option_menu, int selection, void *context) { + backlight_set_timeout_ms(s_timeout_values[selection]); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_timeout_menu_push(SettingsBacklightData *data) { + int index = prv_timeout_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_timeout_menu_select, + }; + const char *title = PBL_IF_RECT_ELSE(i18n_noop("TIMEOUT"), i18n_noop("Timeout")); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, ARRAY_LENGTH(s_timeout_labels), + true /* icons_enabled */, s_timeout_labels, data); +} + +// Touch Wake Settings +///////////////////////////// +#ifdef CONFIG_TOUCH +static const char *s_touch_wake_labels[] = { + [BacklightTouchWake_DoubleTap] = i18n_noop("Double Tap"), + [BacklightTouchWake_Tap] = i18n_noop("Tap"), + [BacklightTouchWake_Off] = i18n_noop("Off"), +}; + +static void prv_touch_wake_menu_select(OptionMenu *option_menu, int selection, void *context) { + backlight_set_touch_wake((BacklightTouchWake)selection); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_touch_wake_menu_push(SettingsBacklightData *data) { + const int index = (int)backlight_get_touch_wake(); + const OptionMenuCallbacks callbacks = { + .select = prv_touch_wake_menu_select, + }; + const char *title = PBL_IF_RECT_ELSE(i18n_noop("WAKE ON TOUCH"), i18n_noop("Wake on touch")); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_touch_wake_labels), true /* icons_enabled */, s_touch_wake_labels, data); +} +#endif + +// Legacy App Mode Settings (Obelix only) +///////////////////////////// +#ifdef CONFIG_APP_SCALING +static const char *s_legacy_app_mode_labels[] = { + i18n_noop("Centered"), + i18n_noop("Scaled (Nearest)"), + i18n_noop("Scaled (Bilinear)") +}; + +static void prv_legacy_app_mode_menu_select(OptionMenu *option_menu, int selection, void *context) { + shell_prefs_set_legacy_app_render_mode((LegacyAppRenderMode)selection); + app_window_stack_remove(&option_menu->window, true /*animated*/); +} + +static void prv_legacy_app_mode_menu_push(SettingsDisplayData *data) { + const int index = (int)shell_prefs_get_legacy_app_render_mode(); + const OptionMenuCallbacks callbacks = { + .select = prv_legacy_app_mode_menu_select, + }; + const char *title = i18n_noop("Legacy App Display"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_legacy_app_mode_labels), + false /* icons_enabled */, s_legacy_app_mode_labels, data); +} +#endif + +// Backlight submenu +////////////////////////////////////////////////////////////////////////////// + +enum SettingsBacklightItem { + SettingsBacklightMode, + SettingsBacklightMotionWake, +#ifdef CONFIG_TOUCH + SettingsBacklightTouchWake, +#endif + SettingsBacklightAmbientSensor, +#ifdef CONFIG_DYNAMIC_BACKLIGHT + SettingsBacklightDynamicIntensity, +#endif + SettingsBacklightIntensity, + SettingsBacklightTimeout, + NumSettingsBacklightItems +}; + +// Number of items after the Mode row that are hidden when the backlight is off. +static const int NUM_BACKLIGHT_SUB_ITEMS = CLIP(SettingsBacklightTimeout - + SettingsBacklightMode - 1, 0, NumSettingsBacklightItems); + +static bool prv_should_show_backlight_sub_items(void) { + return backlight_is_enabled(); +} + +#ifdef CONFIG_TOUCH +// The wake-on-touch row is only relevant when global touch is enabled. +// It gets hidden dynamically (not just gated at compile time) so users don't +// see a dangling backlight option that can't do anything. +static bool prv_should_show_touch_wake(void) { + return touch_is_globally_enabled(); +} +#endif + +static uint16_t prv_backlight_item_from_row(uint16_t row) { + if (!prv_should_show_backlight_sub_items() && (row > SettingsBacklightMode)) { + row += NUM_BACKLIGHT_SUB_ITEMS; +#ifdef CONFIG_TOUCH + } else if (!prv_should_show_touch_wake() && (row >= SettingsBacklightTouchWake)) { + row += 1; +#endif + } + return row; +} + +static void prv_backlight_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsBacklightData *data = (SettingsBacklightData*)context; + switch (prv_backlight_item_from_row(row)) { + case SettingsBacklightMode: + light_toggle_enabled(); + break; + case SettingsBacklightMotionWake: + backlight_set_motion_enabled(!backlight_is_motion_enabled()); + break; +#ifdef CONFIG_TOUCH + case SettingsBacklightTouchWake: + prv_touch_wake_menu_push(data); + break; +#endif + case SettingsBacklightAmbientSensor: + light_toggle_ambient_sensor_enabled(); + break; +#ifdef CONFIG_DYNAMIC_BACKLIGHT + case SettingsBacklightDynamicIntensity: + light_toggle_dynamic_intensity_enabled(); + break; +#endif + case SettingsBacklightIntensity: + prv_intensity_menu_push(data); + break; + case SettingsBacklightTimeout: + prv_timeout_menu_push(data); + break; + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemDisplay); + settings_menu_mark_dirty(SettingsMenuItemDisplay); +} + +static void prv_backlight_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsBacklightData *data = (SettingsBacklightData*) context; + const char *title = NULL; + const char *subtitle = NULL; + switch (prv_backlight_item_from_row(row)) { + case SettingsBacklightMode: + title = i18n_noop("Backlight"); + if (backlight_is_enabled()) { +#ifdef CONFIG_DYNAMIC_BACKLIGHT + if (backlight_is_dynamic_intensity_enabled()) { + uint8_t current_percent = light_get_current_brightness_percent(); + snprintf(data->backlight_percent_buffer, sizeof(data->backlight_percent_buffer), + "On - %"PRIu8"%%", current_percent); + subtitle = data->backlight_percent_buffer; + } else { + subtitle = i18n_noop("On"); + } +#else + subtitle = i18n_noop("On"); +#endif + } else { + subtitle = i18n_noop("Off"); + } + break; + case SettingsBacklightMotionWake: + title = i18n_noop("Wake on motion"); + subtitle = backlight_is_motion_enabled() ? i18n_noop("On") : i18n_noop("Off"); + break; +#ifdef CONFIG_TOUCH + case SettingsBacklightTouchWake: + title = i18n_noop("Wake on touch"); + subtitle = s_touch_wake_labels[backlight_get_touch_wake()]; + break; +#endif + case SettingsBacklightAmbientSensor: + title = i18n_noop("Ambient Sensor"); + if (backlight_is_ambient_sensor_enabled()) { + uint32_t als_value = ambient_light_get_light_level(); + snprintf(data->als_value_buffer, sizeof(data->als_value_buffer), + "On (%"PRIu32")", als_value); + subtitle = data->als_value_buffer; + } else { + subtitle = i18n_noop("Off"); + } + break; +#ifdef CONFIG_DYNAMIC_BACKLIGHT + case SettingsBacklightDynamicIntensity: + title = i18n_noop("Dynamic Backlight"); + subtitle = backlight_is_dynamic_intensity_enabled() ? i18n_noop("On") : i18n_noop("Off"); + break; +#endif + case SettingsBacklightIntensity: +#ifdef CONFIG_DYNAMIC_BACKLIGHT + title = backlight_is_dynamic_intensity_enabled() ? i18n_noop("Max Intensity") + : i18n_noop("Intensity"); +#else + title = i18n_noop("Intensity"); +#endif + subtitle = s_intensity_labels[prv_intensity_get_selection_index()]; + break; + case SettingsBacklightTimeout: + title = i18n_noop("Timeout"); + subtitle = s_timeout_labels[prv_timeout_get_selection_index()]; + break; + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static uint16_t prv_backlight_num_rows_cb(SettingsCallbacks *context) { + uint16_t rows = NumSettingsBacklightItems; + if (!prv_should_show_backlight_sub_items()) { + rows -= NUM_BACKLIGHT_SUB_ITEMS; +#ifdef CONFIG_TOUCH + } else if (!prv_should_show_touch_wake()) { + // Only deduct when the sub-items are otherwise visible; when the backlight + // itself is off the touch-wake row is already covered by the bulk collapse. + rows -= 1; +#endif + } + return rows; +} + +// Timer refresh while the Backlight submenu is visible — lets us live-update +// the ALS reading and the dynamic-backlight percentage subtitle. +#define UPDATE_INTERVAL_MS 500 +static void prv_backlight_update_timer_cb(void *context) { + SettingsBacklightData *data = (SettingsBacklightData*)context; + settings_menu_mark_dirty(SettingsMenuItemDisplay); + data->update_timer = app_timer_register(UPDATE_INTERVAL_MS, prv_backlight_update_timer_cb, data); +} + +static void prv_backlight_appear_cb(SettingsCallbacks *context) { + SettingsBacklightData *data = (SettingsBacklightData*)context; + if (!data->update_timer) { + data->update_timer = app_timer_register(UPDATE_INTERVAL_MS, + prv_backlight_update_timer_cb, data); + } +} + +static void prv_backlight_hide_cb(SettingsCallbacks *context) { + SettingsBacklightData *data = (SettingsBacklightData*)context; + if (data->update_timer) { + app_timer_cancel(data->update_timer); + data->update_timer = NULL; + } +} + +static void prv_backlight_deinit_cb(SettingsCallbacks *context) { + SettingsBacklightData *data = (SettingsBacklightData*) context; + if (data->update_timer) { + app_timer_cancel(data->update_timer); + data->update_timer = NULL; + } + i18n_free_all(data); + app_free(data); +} + +static void prv_backlight_submenu_push(void) { + SettingsBacklightData *data = app_malloc_check(sizeof(*data)); + *data = (SettingsBacklightData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_backlight_deinit_cb, + .draw_row = prv_backlight_draw_row_cb, + .select_click = prv_backlight_select_click_cb, + .num_rows = prv_backlight_num_rows_cb, + .appear = prv_backlight_appear_cb, + .hide = prv_backlight_hide_cb, + }; + + Window *window = settings_window_create_with_title(SettingsMenuItemDisplay, + i18n_noop("Backlight"), &data->callbacks); + app_window_stack_push(window, true /* animated */); +} + +// Display top-level menu +////////////////////////////////////////////////////////////////////////////// + +enum SettingsDisplayItem { + SettingsDisplayLanguage, +#ifdef CONFIG_ORIENTATION_MANAGER + SettingsDisplayOrientation, +#endif +#ifdef CONFIG_TOUCH + SettingsDisplayTouch, +#endif + SettingsDisplayBacklight, +#ifdef CONFIG_APP_SCALING + SettingsDisplayLegacyAppMode, +#endif + NumSettingsDisplayItems +}; + +static void prv_display_select_click_cb(SettingsCallbacks *context, uint16_t row) { + switch (row) { + case SettingsDisplayLanguage: + prv_language_menu_push((SettingsDisplayData *)context); + break; +#ifdef CONFIG_ORIENTATION_MANAGER + case SettingsDisplayOrientation: + prv_display_orientation_menu_push((SettingsDisplayData*)context); + break; +#endif +#ifdef CONFIG_TOUCH + case SettingsDisplayTouch: + touch_set_globally_enabled(!touch_is_globally_enabled()); + break; +#endif + case SettingsDisplayBacklight: + prv_backlight_submenu_push(); + break; +#ifdef CONFIG_APP_SCALING + case SettingsDisplayLegacyAppMode: + prv_legacy_app_mode_menu_push((SettingsDisplayData*)context); + break; +#endif + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemDisplay); + settings_menu_mark_dirty(SettingsMenuItemDisplay); +} + +static void prv_display_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsDisplayData *data = (SettingsDisplayData*) context; + const char *title = NULL; + const char *subtitle = NULL; + switch (row) { + case SettingsDisplayLanguage: + title = i18n_noop("Language"); + subtitle = i18n_get_lang_name(); + break; +#ifdef CONFIG_ORIENTATION_MANAGER + case SettingsDisplayOrientation: + title = i18n_noop("Orientation"); + subtitle = s_display_orientation_labels[prv_display_orientation_get_selection_index()]; + break; +#endif +#ifdef CONFIG_TOUCH + case SettingsDisplayTouch: + title = i18n_noop("Touch"); + subtitle = touch_is_globally_enabled() ? i18n_noop("On") : i18n_noop("Off"); + break; +#endif + case SettingsDisplayBacklight: + title = i18n_noop("Backlight"); + break; +#ifdef CONFIG_APP_SCALING + case SettingsDisplayLegacyAppMode: + title = i18n_noop("Legacy Apps"); + subtitle = s_legacy_app_mode_labels[shell_prefs_get_legacy_app_render_mode()]; + break; +#endif + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static uint16_t prv_display_num_rows_cb(SettingsCallbacks *context) { + return NumSettingsDisplayItems; +} + +static void prv_display_deinit_cb(SettingsCallbacks *context) { + SettingsDisplayData *data = (SettingsDisplayData*) context; + i18n_free_all(data); + app_free(data); +} + +static Window *prv_init(void) { + SettingsDisplayData *data = app_malloc_check(sizeof(*data)); + *data = (SettingsDisplayData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_display_deinit_cb, + .draw_row = prv_display_draw_row_cb, + .select_click = prv_display_select_click_cb, + .num_rows = prv_display_num_rows_cb, + }; + + return settings_window_create(SettingsMenuItemDisplay, &data->callbacks); +} + +const SettingsModuleMetadata *settings_display_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Display"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/display.h b/src/fw/apps/system/settings/display.h new file mode 100644 index 0000000000..faf05cf8a3 --- /dev/null +++ b/src/fw/apps/system/settings/display.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_display_get_info(void); diff --git a/src/fw/apps/system/settings/factory_reset.c b/src/fw/apps/system/settings/factory_reset.c new file mode 100644 index 0000000000..3e3c1bd105 --- /dev/null +++ b/src/fw/apps/system/settings/factory_reset.c @@ -0,0 +1,178 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "factory_reset.h" + +#include "applib/app_timer.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/action_bar_layer.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/window_stack_private.h" +#include "applib/ui/ui.h" +#include "apps/system/timeline/peek_layer.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/kernel_ui.h" +#include "kernel/ui/system_icons.h" +#include "kernel/util/factory_reset.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/system_task.h" +#include "bluetooth.h" +#include "system/logging.h" + +#define MESSAGE_BUF_SIZE 96 + +typedef struct ConfirmUIData { + Window window; + ActionBarLayer action_bar; + TextLayer msg_text_layer; + TextLayer forget_text_layer; + PeekLayer resetting_layer; + char msg_text_layer_buffer[MESSAGE_BUF_SIZE]; + GBitmap *action_bar_icon_check; + GBitmap *action_bar_icon_x; +} ConfirmUIData; + +//! Wipe registry + Reboot +static void start_factory_reset(void *data) { + factory_reset(false /* should_shutdown */); +} + +static void prv_lockout_back_button(Window *window) { + window_set_overrides_back_button(window, true); + window_set_click_config_provider(window, NULL); +} + +static void confirm_click_handler(ClickRecognizerRef recognizer, Window *window) { + ConfirmUIData *data = window_get_user_data(window); + + // Need to lock-out inputs after starting the factory reset. + prv_lockout_back_button(window); + + PeekLayer *peek_layer = &data->resetting_layer; + peek_layer_init(peek_layer, &window->layer.bounds); + peek_layer_set_title_font(peek_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + TimelineResourceInfo timeline_res = { + .res_id = TIMELINE_RESOURCE_GENERIC_WARNING, + }; + peek_layer_set_icon(peek_layer, &timeline_res); + peek_layer_set_title(peek_layer, i18n_get("Resetting...", data)); + peek_layer_set_background_color(peek_layer, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); + peek_layer_play(peek_layer); + layer_add_child(&window->layer, &peek_layer->layer); + + // give it a chance to animate + const uint32_t factory_reset_start_delay = 100; + app_timer_register(PEEK_LAYER_UNFOLD_DURATION + factory_reset_start_delay, + start_factory_reset, NULL); +} + +//! Wipe registry + Enter Standby (for factory) +static void confirm_long_click_handler(ClickRecognizerRef recognizer, Window *window) { + // Need to lock-out inputs after starting the factory reset. + prv_lockout_back_button(window); + + factory_reset(true /* should_shutdown */); +} + +static void decline_click_handler(ClickRecognizerRef recognizer, Window *window) { + const bool animated = true; + app_window_stack_pop(animated); + (void)recognizer; + (void)window; +} + +static void config_provider(Window *window) { + window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler) confirm_click_handler); + window_long_click_subscribe(BUTTON_ID_UP, 1200, (ClickHandler) confirm_long_click_handler, NULL); + window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) decline_click_handler); + (void)window; +} + +static void prv_window_load(Window *window) { + ConfirmUIData *data = window_get_user_data(window); + const GRect *root_layer_bounds = &window_get_root_layer(window)->bounds; + const int16_t width = root_layer_bounds->size.w - ACTION_BAR_WIDTH; + + const uint16_t x_margin_px = PBL_IF_ROUND_ELSE(6, 3); + const uint16_t msg_text_y_offset_px = PBL_IF_ROUND_ELSE(15, 0); + const uint16_t msg_text_max_height_px = root_layer_bounds->size.h - msg_text_y_offset_px; + + const GTextAlignment alignment = PBL_IF_ROUND_ELSE(GTextAlignmentRight, GTextAlignmentLeft); + const GTextOverflowMode overflow_mode = GTextOverflowModeTrailingEllipsis; + const GColor text_color = PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack); + + TextLayer *msg_text_layer = &data->msg_text_layer; + GRect msg_text_frame = (GRect) { + .origin = GPoint(x_margin_px, msg_text_y_offset_px), + .size = GSize(width - (2 * x_margin_px), msg_text_max_height_px) + }; + text_layer_init_with_parameters(msg_text_layer, &msg_text_frame, + i18n_get("Perform factory reset?", data), + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), text_color, + GColorClear, alignment, overflow_mode); + layer_add_child(&window->layer, &msg_text_layer->layer); +#if PBL_ROUND + const uint8_t text_flow_inset = 8; + text_layer_enable_screen_text_flow_and_paging(msg_text_layer, text_flow_inset); +#endif + + // handle different title heights gracefully + GContext *ctx = graphics_context_get_current_context(); + const uint16_t msg_text_height_px = text_layer_get_content_size(ctx, msg_text_layer).h; + const int text_spacing = 7; + const uint16_t forget_text_y_offset_px = msg_text_y_offset_px + msg_text_height_px + text_spacing; + + TextLayer *forget_text_layer = &data->forget_text_layer; + const GRect forget_text_frame = (GRect) { + .origin = GPoint(x_margin_px, forget_text_y_offset_px), + .size = GSize(width - (2 * x_margin_px), root_layer_bounds->size.h - forget_text_y_offset_px) + }; + text_layer_init_with_parameters(forget_text_layer, &forget_text_frame, + i18n_get(BT_FORGET_PAIRING_STR, data), + fonts_get_system_font(FONT_KEY_GOTHIC_18), text_color, + GColorClear, alignment, overflow_mode); + layer_add_child(&window->layer, &forget_text_layer->layer); +#if PBL_ROUND + text_layer_enable_screen_text_flow_and_paging(forget_text_layer, text_flow_inset); +#endif + + // Action bar: + ActionBarLayer *action_bar = &data->action_bar; + action_bar_layer_init(action_bar); + action_bar_layer_set_context(action_bar, window); + action_bar_layer_add_to_window(action_bar, window); + action_bar_layer_set_click_config_provider(action_bar, (ClickConfigProvider) config_provider); + action_bar_layer_set_icon(action_bar, BUTTON_ID_UP, data->action_bar_icon_check); + action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, data->action_bar_icon_x); +} + +static void prv_window_unload(Window *window) { + ConfirmUIData *data = window_get_user_data(window); + gbitmap_destroy(data->action_bar_icon_check); + gbitmap_destroy(data->action_bar_icon_x); + i18n_free_all(data); + app_free(data); +} + +void settings_factory_reset_window_push(void) { + ConfirmUIData *data = (ConfirmUIData*)app_malloc_check(sizeof(ConfirmUIData)); + *data = (ConfirmUIData){}; + + Window *window = &data->window; + window_init(window, "Settings Factory Reset"); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); +#if PBL_COLOR + window_set_background_color(window, GColorCobaltBlue); +#endif + window_set_user_data(window, data); + data->action_bar_icon_check = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_CHECK); + data->action_bar_icon_x = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_X); + const bool animated = true; + app_window_stack_push(window, animated); +} + +#undef MESSAGE_BUF_SIZE diff --git a/src/fw/apps/system/settings/factory_reset.h b/src/fw/apps/system/settings/factory_reset.h new file mode 100644 index 0000000000..685be1ec93 --- /dev/null +++ b/src/fw/apps/system/settings/factory_reset.h @@ -0,0 +1,7 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include "kernel/events.h" + +void settings_factory_reset_window_push(); diff --git a/src/fw/apps/system/settings/health.c b/src/fw/apps/system/settings/health.c new file mode 100644 index 0000000000..79047cd7bf --- /dev/null +++ b/src/fw/apps/system/settings/health.c @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "health.h" +#include "menu.h" +#include "option_menu.h" +#include "window.h" + +#include "applib/ui/option_menu_window.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "shell/prefs.h" +#include "system/passert.h" +#include "util/size.h" + +#include + +typedef struct SettingsHealthData { + SettingsCallbacks callbacks; +} SettingsHealthData; + +static const char *s_units_distance_labels[] = { + i18n_noop("Kilometers"), + i18n_noop("Miles"), +}; + +#ifdef CONFIG_HRM +static const char *s_hrm_interval_labels[] = { + i18n_noop("10 Minutes"), + i18n_noop("30 Minutes"), + i18n_noop("1 Hour"), + i18n_noop("Disabled"), +}; +#endif + +enum SettingsHealthItem { + SettingsHealthTrackingEnabled, + SettingsHealthUnitDistance, +#ifdef CONFIG_HRM + SettingsHealthHRMonitoringInterval, + SettingsHealthHRActivityTracking, +#endif + NumSettingsHealthItems +}; + +#ifdef CONFIG_HRM +// HRM Interval option menu +///////////////////////////// + +static void prv_hrm_interval_menu_select(OptionMenu *option_menu, int selection, void *context) { + activity_prefs_set_hrm_measurement_interval((HRMonitoringInterval)selection); + app_window_stack_remove(&option_menu->window, true /*animated*/); +} + +static void prv_hrm_interval_menu_push(SettingsHealthData *data) { + const int index = (int)activity_prefs_get_hrm_measurement_interval(); + const OptionMenuCallbacks callbacks = { + .select = prv_hrm_interval_menu_select, + }; + const char *title = i18n_noop("HR Monitoring"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_hrm_interval_labels), true /* icons_enabled */, + s_hrm_interval_labels, data); +} +#endif + +// Menu Callbacks +///////////////////////////// + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsHealthData *data = (SettingsHealthData*)context; + + i18n_free_all(data); + app_free(data); +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsHealthData *data = (SettingsHealthData*) context; + + const char *title = NULL; + const char *subtitle = NULL; + + switch (row) { + case SettingsHealthTrackingEnabled: { + title = i18n_noop("Health Tracking"); + subtitle = activity_prefs_tracking_is_enabled() + ? i18n_noop("On") : i18n_noop("Off"); + break; + } + case SettingsHealthUnitDistance: { + title = i18n_noop("Distance Unit"); + UnitsDistance unit = shell_prefs_get_units_distance(); + if (unit >= UnitsDistanceCount) { + subtitle = i18n_noop("Unknown"); + } else { + subtitle = s_units_distance_labels[unit]; + } + break; + } +#ifdef CONFIG_HRM + case SettingsHealthHRMonitoringInterval: { + title = i18n_noop("HR Monitoring"); + HRMonitoringInterval interval = activity_prefs_get_hrm_measurement_interval(); + if (interval >= HRMonitoringIntervalCount) { + subtitle = i18n_noop("Unknown"); + } else { + subtitle = s_hrm_interval_labels[interval]; + } + break; + } + case SettingsHealthHRActivityTracking: { + title = i18n_noop("HR During Activity"); + subtitle = activity_prefs_hrm_activity_tracking_is_enabled() + ? i18n_noop("On") : i18n_noop("Off"); + break; + } +#endif + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + switch (row) { + case SettingsHealthTrackingEnabled: { + bool new_value = !activity_prefs_tracking_is_enabled(); + activity_prefs_tracking_set_enabled(new_value); + if (new_value) { + activity_start_tracking(false); + } else { + activity_stop_tracking(); + } + break; + } + case SettingsHealthUnitDistance: { + UnitsDistance unit = shell_prefs_get_units_distance(); + unit = (unit + 1) % UnitsDistanceCount; + shell_prefs_set_units_distance(unit); + break; + } +#ifdef CONFIG_HRM + case SettingsHealthHRMonitoringInterval: + prv_hrm_interval_menu_push((SettingsHealthData*)context); + break; + case SettingsHealthHRActivityTracking: + activity_prefs_set_hrm_activity_tracking_enabled( + !activity_prefs_hrm_activity_tracking_is_enabled()); + break; +#endif + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemHealth); + settings_menu_mark_dirty(SettingsMenuItemHealth); +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + if (!activity_prefs_tracking_is_enabled()) { + return 1; // Only show the Health Tracking toggle + } + return NumSettingsHealthItems; +} + +static void prv_appear_cb(SettingsCallbacks *context) { +} + +static void prv_hide_cb(SettingsCallbacks *context) { +} + +static Window *prv_init(void) { + SettingsHealthData *data = app_malloc_check(sizeof(*data)); + *data = (SettingsHealthData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + .appear = prv_appear_cb, + .hide = prv_hide_cb, + }; + + return settings_window_create(SettingsMenuItemHealth, &data->callbacks); +} + +const SettingsModuleMetadata *settings_health_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Health"), + .init = prv_init, + }; + + return &s_module_info; +} \ No newline at end of file diff --git a/src/fw/apps/system/settings/health.h b/src/fw/apps/system/settings/health.h new file mode 100644 index 0000000000..57f939294b --- /dev/null +++ b/src/fw/apps/system/settings/health.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_health_get_info(void); diff --git a/src/fw/apps/system/settings/menu.c b/src/fw/apps/system/settings/menu.c new file mode 100644 index 0000000000..9b6e8d490e --- /dev/null +++ b/src/fw/apps/system/settings/menu.c @@ -0,0 +1,56 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "activity_tracker.h" +#include "bluetooth.h" +#include "display.h" +#include "menu.h" +#include "notifications.h" +#include "quick_launch.h" +#include "quiet_time.h" +#include "remote.h" +#include "system.h" +#include "time.h" +#include "timeline.h" +#ifdef CONFIG_THEMING +#include "themes.h" +#endif +#include "health.h" +#include "vibe_patterns.h" + +#include "applib/ui/app_window_stack.h" +#include "pbl/services/i18n/i18n.h" +#include "system/passert.h" +#include "shell/prefs.h" + +static const SettingsModuleGetMetadata s_submodule_registry[] = { + [SettingsMenuItemBluetooth] = settings_bluetooth_get_info, + [SettingsMenuItemNotifications] = settings_notifications_get_info, + [SettingsMenuItemVibrations] = settings_vibe_patterns_get_info, + [SettingsMenuItemQuietTime] = settings_quiet_time_get_info, + [SettingsMenuItemTimeline] = settings_timeline_get_info, + [SettingsMenuItemHealth] = settings_health_get_info, + [SettingsMenuItemActivity] = settings_activity_tracker_get_info, + [SettingsMenuItemQuickLaunch] = settings_quick_launch_get_info, + [SettingsMenuItemDateTime] = settings_time_get_info, + [SettingsMenuItemDisplay] = settings_display_get_info, +#ifdef CONFIG_THEMING + [SettingsMenuItemThemes] = settings_themes_get_info, +#endif + [SettingsMenuItemSystem] = settings_system_get_info, +}; + +const SettingsModuleMetadata *settings_menu_get_submodule_info(SettingsMenuItem category) { + PBL_ASSERTN(category < SettingsMenuItem_Count); + return s_submodule_registry[category](); +} + +const char *settings_menu_get_status_name(SettingsMenuItem category) { + const SettingsModuleMetadata *info = settings_menu_get_submodule_info(category); + return info->name; +} + +void settings_menu_push(SettingsMenuItem category) { + Window *window = settings_menu_get_submodule_info(category)->init(); + app_window_stack_push(window, true /* animated */); +} diff --git a/src/fw/apps/system/settings/menu.h b/src/fw/apps/system/settings/menu.h new file mode 100644 index 0000000000..c7a7eadc8e --- /dev/null +++ b/src/fw/apps/system/settings/menu.h @@ -0,0 +1,81 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gtypes.h" +#include "applib/ui/layer.h" +#include "applib/ui/window.h" +#include "shell/prefs.h" + +#include + + +typedef enum { + SettingsMenuItemBluetooth = 0, + SettingsMenuItemNotifications, + SettingsMenuItemVibrations, + SettingsMenuItemQuietTime, + SettingsMenuItemTimeline, + SettingsMenuItemQuickLaunch, + SettingsMenuItemDateTime, + SettingsMenuItemDisplay, + SettingsMenuItemHealth, +#ifdef CONFIG_THEMING + SettingsMenuItemThemes, +#endif + SettingsMenuItemActivity, + SettingsMenuItemSystem, + SettingsMenuItem_Count, + SettingsMenuItem_Invalid +} SettingsMenuItem; + +struct SettingsCallbacks; +typedef struct SettingsCallbacks SettingsCallbacks; + +typedef void (*SettingsDeinit)(SettingsCallbacks *context); +typedef uint16_t (*SettingsGetInitialSelection)(SettingsCallbacks *context); +typedef void (*SettingsSelectionChangedCallback)(SettingsCallbacks *context, uint16_t new_row, + uint16_t old_row); +typedef void (*SettingsSelectionWillChangeCallback)(SettingsCallbacks *context, uint16_t *new_row, + uint16_t old_row); +typedef void (*SettingsSelectClickCallback)(SettingsCallbacks *context, uint16_t row); +typedef void (*SettingsDrawRowCallback)(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected); +typedef uint16_t (*SettingsNumRowsCallback)(SettingsCallbacks *context); +typedef int16_t (*SettingsRowHeightCallback)(SettingsCallbacks *context, uint16_t row, + bool is_selected); +typedef void (*SettingsExpandCallback)(SettingsCallbacks *context); +typedef void (*SettingsAppearCallback)(SettingsCallbacks *context); +typedef void (*SettingsHideCallback)(SettingsCallbacks *context); + +struct SettingsCallbacks { + SettingsDeinit deinit; + SettingsDrawRowCallback draw_row; + SettingsGetInitialSelection get_initial_selection; + SettingsSelectionChangedCallback selection_changed; + SettingsSelectionWillChangeCallback selection_will_change; + SettingsSelectClickCallback select_click; + SettingsNumRowsCallback num_rows; + SettingsRowHeightCallback row_height; + SettingsExpandCallback expand; + SettingsAppearCallback appear; + SettingsHideCallback hide; +}; + +typedef Window *(*SettingsInitFunction)(void); + +typedef struct { + const char *name; + SettingsInitFunction init; +} SettingsModuleMetadata; + +typedef const SettingsModuleMetadata *(*SettingsModuleGetMetadata)(void); + +void settings_menu_mark_dirty(SettingsMenuItem category); +void settings_menu_reload_data(SettingsMenuItem category); +int16_t settings_menu_get_selected_row(SettingsMenuItem category); + +const SettingsModuleMetadata *settings_menu_get_submodule_info(SettingsMenuItem category); +const char *settings_menu_get_status_name(SettingsMenuItem category); +void settings_menu_push(SettingsMenuItem category); diff --git a/src/fw/apps/system/settings/notifications.c b/src/fw/apps/system/settings/notifications.c new file mode 100644 index 0000000000..73408acdbc --- /dev/null +++ b/src/fw/apps/system/settings/notifications.c @@ -0,0 +1,459 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "notifications_private.h" +#include "menu.h" +#include "option_menu.h" +#include "window.h" + +#include "applib/event_service_client.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/action_menu_window_private.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/option_menu_window.h" +#include "applib/ui/ui.h" +#include "drivers/battery.h" +#include "kernel/pbl_malloc.h" +#include "popups/notifications/notification_window.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/vibes/vibe_intensity.h" +#include "shell/prefs.h" +#include "shell/system_theme.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" +#include "util/time/time.h" + +#include + +// Offset between vibe intensity menu item index and vibe intensity enum values +#define INTENSITY_ROW_OFFSET 1 + +typedef struct { + SettingsCallbacks callbacks; + EventServiceInfo battery_connection_event_info; +} SettingsNotificationsData; + +enum NotificationsItem { + NotificationsItemFilter, + NotificationsItemTextSize, + NotificationsItemWindowTimeout, +#if PBL_BW + NotificationsItemDesignStyle, +#endif + NotificationsItemVibeDelay, + NotificationsItemBacklight, + NotificationsItemStatusBarStyle, + NotificationsItem_Count, +}; + +// Filter Alerts +////////////////////////// + +#define NUM_ALERT_MODES_IN_LIST 3 + +// These aren't all of the values of AlertMask, so to add extra ones you have to update both of +// these arrays + +static const AlertMask s_alert_mode_values[NUM_ALERT_MODES_IN_LIST] = { + AlertMaskAllOn, + AlertMaskPhoneCalls, + AlertMaskAllOff, +}; + +static const char *s_alert_mode_labels[NUM_ALERT_MODES_IN_LIST] = { + i18n_noop("Allow All Notifications"), + i18n_noop("Allow Phone Calls Only"), + i18n_noop("Mute All Notifications"), +}; + +static const char *prv_alert_mask_to_label(AlertMask mask) { + for (uint32_t i = 0; i < NUM_ALERT_MODES_IN_LIST; i++) { + if (s_alert_mode_values[i] == mask) { + return s_alert_mode_labels[i]; + } + } + return "???"; +} + +static void prv_filter_menu_select(OptionMenu *option_menu, int selection, void *context) { + alerts_set_mask(s_alert_mode_values[selection]); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_filter_menu_push(SettingsNotificationsData *data) { + AlertMask mask = alerts_get_mask(); + size_t cycle_len = ARRAY_LENGTH(s_alert_mode_values); + size_t index = 0; + // TODO PBL-24306: update once AlertMask logic is made safer + for (size_t i = 0; i < cycle_len; i++) { + if (s_alert_mode_values[i] == mask) { + index = i; + break; + } + } + const OptionMenuCallbacks callbacks = { + .select = prv_filter_menu_select, + }; + /// The option in the Settings app for filtering notifications by type. + const char *title = i18n_noop("Filter"); + settings_option_menu_push( + title, OptionMenuContentType_DoubleLine, index, &callbacks, cycle_len, + true /* icons_enabled */, s_alert_mode_labels, data); +} + +// Text Size +//////////////////////// + +static const char *s_text_size_names[] = { + [SettingsContentSize_Small] = i18n_noop("Smaller"), + [SettingsContentSize_Default] = i18n_noop("Default"), + [SettingsContentSize_Large] = i18n_noop("Larger"), +}; + +static void prv_text_size_menu_select(OptionMenu *option_menu, int selection, void *context) { + system_theme_set_content_size(settings_content_size_to_preferred_size(selection)); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_text_size_menu_push(SettingsNotificationsData *data) { + const OptionMenuCallbacks callbacks = { + .select = prv_text_size_menu_select, + }; + /// The option in the Settings app for choosing the text size of notifications. + const char *title = i18n_noop("Text Size"); + const SettingsContentSize index = + settings_content_size_from_preferred_size(system_theme_get_content_size()); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, SettingsContentSizeCount, + true /* icons_enabled */, s_text_size_names, data); +} + +// Text Size +//////////////////////// + +// NOTE: Keep the following two arrays in sync and with the same size. +static const uint32_t s_window_timeouts_ms[] = { + 15 * MS_PER_SECOND, + 30 * MS_PER_SECOND, + 1 * MS_PER_MINUTE, + NOTIF_WINDOW_TIMEOUT_DEFAULT, + 10 * MS_PER_MINUTE, + NOTIF_WINDOW_TIMEOUT_INFINITE +}; + +static const char *s_window_timeouts_labels[] = { + /// 15 Second Notification Window Timeout + i18n_noop("15 Seconds"), + /// 30 Second Notification Window Timeout + i18n_noop("30 Seconds"), + /// 1 Minute Notification Window Timeout + i18n_noop("1 Minute"), + /// 3 Minute Notification Window Timeout + i18n_noop("3 Minutes"), + /// 10 Minute Notification Window Timeout + i18n_noop("10 Minutes"), + /// No Notification Window Timeout + i18n_noop("None"), +}; + +_Static_assert(ARRAY_LENGTH(s_window_timeouts_ms) == ARRAY_LENGTH(s_window_timeouts_labels), ""); + +static int prv_window_timeout_get_selection_index(void) { + const int DEFAULT_IDX = 3; + // Double check no one has fudged with the order and the fallback/default + PBL_ASSERTN(s_window_timeouts_ms[DEFAULT_IDX] == NOTIF_WINDOW_TIMEOUT_DEFAULT); + + const uint32_t timeout_ms = alerts_preferences_get_notification_window_timeout_ms(); + for (size_t i = 0; i < ARRAY_LENGTH(s_window_timeouts_ms); i++) { + if (s_window_timeouts_ms[i] == timeout_ms) { + return i; + } + } + // Should never happen (only should happen if we remove a timeout and don't migrate the user + // to a new setting automatically + return DEFAULT_IDX; +} + +static void prv_window_timeout_menu_select(OptionMenu *option_menu, int selection, void *context) { + alerts_preferences_set_notification_window_timeout_ms(s_window_timeouts_ms[selection]); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_window_timeout_menu_push(SettingsNotificationsData *data) { + const int index = prv_window_timeout_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_window_timeout_menu_select, + }; + /// Status bar title for the Notification Window Timeout settings screen + const char *title = i18n_noop("Timeout"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_window_timeouts_labels), true /* icons_enabled */, s_window_timeouts_labels, + data); +} + +// Design Style +//////////////////////// + +#if PBL_BW +static const char *s_design_style_labels[] = { + /// Standard notification design option (default) + i18n_noop("Classic"), + /// Alternative notification design option + i18n_noop("Flat Black"), +}; + +static int prv_design_style_get_selection_index(void) { + return alerts_preferences_get_notification_alternative_design() ? 1 : 0; +} + +static void prv_design_style_menu_select(OptionMenu *option_menu, int selection, void *context) { + alerts_preferences_set_notification_alternative_design(selection == 1); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_design_style_menu_push(SettingsNotificationsData *data) { + const int index = prv_design_style_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_design_style_menu_select, + }; + /// Status bar title for the Notification Design Style settings screen + const char *title = i18n_noop("Banner Style"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_design_style_labels), true /* icons_enabled */, s_design_style_labels, + data); +} +#endif /* PBL_BW */ + +// Vibe Delay +//////////////////////// + +static const char *s_vibe_delay_labels[] = { + /// Vibrate at the beginning of notification animation (immediate) + i18n_noop("Beginning"), + /// Vibrate at the end of notification animation (delayed) + i18n_noop("End"), +}; + +static int prv_vibe_delay_get_selection_index(void) { + return alerts_preferences_get_notification_vibe_delay() ? 1 : 0; +} + +static void prv_vibe_delay_menu_select(OptionMenu *option_menu, int selection, void *context) { + alerts_preferences_set_notification_vibe_delay(selection == 1); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_vibe_delay_menu_push(SettingsNotificationsData *data) { + const int index = prv_vibe_delay_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_vibe_delay_menu_select, + }; + /// Status bar title for the Notification Vibe Timing settings screen + const char *title = i18n_noop("Vibe Timing"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_vibe_delay_labels), true /* icons_enabled */, s_vibe_delay_labels, + data); +} + +// Status Bar Style +//////////////////////// + +static const char *s_status_bar_style_labels[] = { + [NotificationStatusBarStyle_Default] = i18n_noop("Default"), + [NotificationStatusBarStyle_Bold] = i18n_noop("Bold"), + [NotificationStatusBarStyle_LargeBold] = i18n_noop("Big & Bold"), +}; + +_Static_assert(ARRAY_LENGTH(s_status_bar_style_labels) == NotificationStatusBarStyleCount, ""); + +static int prv_status_bar_style_get_selection_index(void) { + const NotificationStatusBarStyle style = + alerts_preferences_get_notification_status_bar_style(); + return (style < NotificationStatusBarStyleCount) ? (int)style : 0; +} + +static void prv_status_bar_style_menu_select(OptionMenu *option_menu, int selection, + void *context) { + alerts_preferences_set_notification_status_bar_style((NotificationStatusBarStyle)selection); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_status_bar_style_menu_push(SettingsNotificationsData *data) { + const int index = prv_status_bar_style_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_status_bar_style_menu_select, + }; + /// Status bar title for the Notification Status Bar Style settings screen + const char *title = i18n_noop("Clock Style"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_status_bar_style_labels), true /* icons_enabled */, + s_status_bar_style_labels, data); +} + +// Menu Layer Callbacks +//////////////////////// + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return NotificationsItem_Count; +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsNotificationsData *data = ((SettingsOptionMenuData *)context)->context; + const char *subtitle = NULL; + const char *title = NULL; + + switch (row) { + case NotificationsItemFilter: + title = i18n_noop("Filter"); + subtitle = prv_alert_mask_to_label(alerts_get_mask()); + break; + case NotificationsItemTextSize: { + /// String within Settings->Notifications that describes the text font size + title = i18n_noop("Text Size"); + const SettingsContentSize index = + settings_content_size_from_preferred_size(system_theme_get_content_size()); + subtitle = (index < SettingsContentSizeCount) ? s_text_size_names[index] : ""; + break; + } + case NotificationsItemWindowTimeout: { + /// String within Settings->Notifications that describes the window timeout setting + title = i18n_noop("Timeout"); + subtitle = s_window_timeouts_labels[prv_window_timeout_get_selection_index()]; + break; + } + #if PBL_BW + case NotificationsItemDesignStyle: { + /// String within Settings->Notifications that describes the notification design style + title = i18n_noop("Banner Style"); + subtitle = s_design_style_labels[prv_design_style_get_selection_index()]; + break; + } + #endif /* PBL_BW */ + case NotificationsItemVibeDelay: { + /// String within Settings->Notifications that describes when vibration happens + title = i18n_noop("Vibe Timing"); + subtitle = s_vibe_delay_labels[prv_vibe_delay_get_selection_index()]; + break; + } + case NotificationsItemBacklight: { + /// String within Settings->Notifications that describes backlight setting + title = i18n_noop("Backlight"); + subtitle = alerts_preferences_get_notification_backlight() ? + i18n_noop("On") : i18n_noop("Off"); + break; + } + case NotificationsItemStatusBarStyle: { + /// String within Settings->Notifications that selects the notification status bar style + title = i18n_noop("Status Bar"); + subtitle = s_status_bar_style_labels[prv_status_bar_style_get_selection_index()]; + break; + } + default: + WTF; + } + + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsNotificationsData *data = (SettingsNotificationsData *)context; + i18n_free_all(data); + app_free(data); +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsNotificationsData *data = (SettingsNotificationsData *) context; + + switch (row) { + case NotificationsItemFilter: + prv_filter_menu_push(data); + break; + case NotificationsItemTextSize: + prv_text_size_menu_push(data); + break; + case NotificationsItemWindowTimeout: + prv_window_timeout_menu_push(data); + break; +#if PBL_BW + case NotificationsItemDesignStyle: + prv_design_style_menu_push(data); + break; +#endif /* PBL_BW */ + case NotificationsItemVibeDelay: + prv_vibe_delay_menu_push(data); + break; + case NotificationsItemBacklight: + // Toggle backlight directly without submenu + alerts_preferences_set_notification_backlight( + !alerts_preferences_get_notification_backlight()); + break; + case NotificationsItemStatusBarStyle: + prv_status_bar_style_menu_push(data); + break; + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemNotifications); +} + +static void prv_settings_notifications_event_handler(PebbleEvent *event, void *context) { + switch (event->type) { + case PEBBLE_BATTERY_CONNECTION_EVENT: + // Redraw the menu so that the Vibration status will be re-rendered. + settings_menu_mark_dirty(SettingsMenuItemNotifications); + break; + default: + break; + } +} + +static void prv_expand_cb(SettingsCallbacks *context) { + SettingsNotificationsData *data = (SettingsNotificationsData *) context; + + data->battery_connection_event_info = (EventServiceInfo) { + .type = PEBBLE_BATTERY_CONNECTION_EVENT, + .handler = prv_settings_notifications_event_handler, + }; + event_service_client_subscribe(&data->battery_connection_event_info); + +} + +static void prv_hide_cb(SettingsCallbacks *context) { + SettingsNotificationsData *data = (SettingsNotificationsData *) context; + + event_service_client_unsubscribe(&data->battery_connection_event_info); +} + +static Window *prv_init(void) { + SettingsNotificationsData* data = app_malloc_check(sizeof(*data)); + *data = (SettingsNotificationsData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + .expand = prv_expand_cb, + .hide = prv_hide_cb, + }; + + return settings_window_create(SettingsMenuItemNotifications, &data->callbacks); +} + +const SettingsModuleMetadata *settings_notifications_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Notifications"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/notifications.h b/src/fw/apps/system/settings/notifications.h new file mode 100644 index 0000000000..ff4caefbd4 --- /dev/null +++ b/src/fw/apps/system/settings/notifications.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_notifications_get_info(void); diff --git a/src/fw/apps/system/settings/notifications_private.h b/src/fw/apps/system/settings/notifications_private.h new file mode 100644 index 0000000000..2298fd5aff --- /dev/null +++ b/src/fw/apps/system/settings/notifications_private.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "notifications.h" + +#include "applib/preferred_content_size.h" + +//! TODO PBL-41920: This mapping should be an opt in set in a platform specific location +typedef enum SettingsContentSize { + SettingsContentSize_Small, + SettingsContentSize_Default, + SettingsContentSize_Large, + SettingsContentSizeCount, +} SettingsContentSize; + +static inline SettingsContentSize settings_content_size_from_preferred_size( + PreferredContentSize preferred_size) { + return preferred_size + (SettingsContentSize_Default - PreferredContentSizeDefault); +} + +static inline PreferredContentSize settings_content_size_to_preferred_size( + SettingsContentSize settings_size) { + return settings_size + (PreferredContentSizeDefault - SettingsContentSize_Default); +} diff --git a/src/fw/apps/system/settings/option_menu.c b/src/fw/apps/system/settings/option_menu.c new file mode 100644 index 0000000000..c41d872aec --- /dev/null +++ b/src/fw/apps/system/settings/option_menu.c @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "option_menu.h" + +#include "menu.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" + +static void prv_menu_unload(OptionMenu *option_menu, void *context) { + SettingsOptionMenuData *data = context; + if (data->callbacks.unload) { + data->callbacks.unload(option_menu, data); + } + option_menu_destroy(option_menu); + i18n_free_all(option_menu); + task_free(context); +} + +static uint16_t prv_menu_get_num_rows(OptionMenu *option_menu, void *context) { + return ((SettingsOptionMenuData *)context)->num_rows; +} + +static void prv_menu_draw_row(OptionMenu *option_menu, GContext *ctx, const Layer *cell_layer, + const GRect *cell_frame, uint32_t row, bool selected, void *context) { + SettingsOptionMenuData *data = context; + const char *title = i18n_get(data->rows[row], option_menu); + option_menu_system_draw_row(option_menu, ctx, cell_layer, cell_frame, title, selected, context); +} + +OptionMenu *settings_option_menu_create( + const char *i18n_title_key, OptionMenuContentType content_type, int choice, + const OptionMenuCallbacks *callbacks_ref, uint16_t num_rows, bool icons_enabled, + const char **rows, void *context) { + OptionMenu *option_menu = option_menu_create(); + if (!option_menu) { + return NULL; + } + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + const OptionMenuConfig config = { + .title = i18n_get(i18n_title_key, option_menu), + .content_type = content_type, + .choice = choice, + .status_colors = { GColorWhite, GColorBlack }, + .highlight_colors = { highlight_bg, gcolor_legible_over(highlight_bg) }, + .icons_enabled = icons_enabled, + }; + option_menu_configure(option_menu, &config); + SettingsOptionMenuData *data = task_malloc_check(sizeof(SettingsOptionMenuData)); + OptionMenuCallbacks callbacks = *callbacks_ref; + *data = (SettingsOptionMenuData) { + .callbacks = callbacks, + .context = context, + .num_rows = num_rows, + .rows = rows, + }; + callbacks.draw_row = prv_menu_draw_row; + callbacks.get_num_rows = prv_menu_get_num_rows; + callbacks.unload = prv_menu_unload; + option_menu_set_callbacks(option_menu, &callbacks, data); + return option_menu; +} + +OptionMenu *settings_option_menu_push( + const char *i18n_title_key, OptionMenuContentType content_type, int choice, + const OptionMenuCallbacks *callbacks_ref, uint16_t num_rows, bool icons_enabled, + const char **rows, void *context) { + OptionMenu * const option_menu = settings_option_menu_create( + i18n_title_key, content_type, choice, callbacks_ref, num_rows, icons_enabled, rows, context); + if (option_menu) { + const bool animated = true; + app_window_stack_push(&option_menu->window, animated); + } + return option_menu; +} + +void *settings_option_menu_get_context(SettingsOptionMenuData *data) { + return data->context; +} diff --git a/src/fw/apps/system/settings/option_menu.h b/src/fw/apps/system/settings/option_menu.h new file mode 100644 index 0000000000..fa2635d3ee --- /dev/null +++ b/src/fw/apps/system/settings/option_menu.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/ui/option_menu_window.h" + +#include "applib/ui/ui.h" + +typedef struct { + OptionMenuCallbacks callbacks; + void *context; + const char **rows; + uint16_t num_rows; +} SettingsOptionMenuData; + +OptionMenu *settings_option_menu_create( + const char *i18n_title_key, OptionMenuContentType content_type, int choice, + const OptionMenuCallbacks *callbacks, uint16_t num_rows, bool icons_enabled, const char **rows, + void *context); + +OptionMenu *settings_option_menu_push( + const char *i18n_title_key, OptionMenuContentType content_type, int choice, + const OptionMenuCallbacks *callbacks, uint16_t num_rows, bool icons_enabled, const char **rows, + void *context); + +void *settings_option_menu_get_context(SettingsOptionMenuData *data); diff --git a/src/fw/apps/system/settings/quick_launch.c b/src/fw/apps/system/settings/quick_launch.c new file mode 100644 index 0000000000..aa1d1f5d33 --- /dev/null +++ b/src/fw/apps/system/settings/quick_launch.c @@ -0,0 +1,199 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! This file displays the main Quick Launch menu that is found in our settings menu +//! It allows the feature to be enabled or for an app to be set +//! The list of apps that the user can choose from is found in settings_quick_launch_app_menu.c +//! This file is also responsible for saving / storing the uuid of each quichlaunch app as well as +//! whether or not the quicklaunch app is enabled. + +#include "menu.h" +#include "quick_launch.h" +#include "quick_launch_app_menu.h" +#include "quick_launch_setup_menu.h" +#include "window.h" + +#include "applib/app.h" +#include "applib/app_launch_button.h" +#include "applib/app_launch_reason.h" +#include "applib/ui/window_stack.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_menu_data_source.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/normal/quick_launch.h" +#include "system/passert.h" +#include "system/status_codes.h" + +#define NUM_ROWS (NUM_BUTTONS + 2) // 4 hold buttons + 2 tap buttons (up and down) + +typedef enum { + ROW_TAP_UP = 0, + ROW_TAP_DOWN, + ROW_HOLD_UP, + ROW_HOLD_SELECT, + ROW_HOLD_DOWN, + ROW_HOLD_BACK, +} QuickLaunchRow; + +typedef struct QuickLaunchData { + SettingsCallbacks callbacks; + char app_names[NUM_ROWS][APP_NAME_SIZE_BYTES]; +} QuickLaunchData; + +static const char *s_row_titles[NUM_ROWS] = { + /// Shown in Quick Launch Settings as the title of the tap up button option. + [ROW_TAP_UP] = i18n_noop("Tap Up"), + /// Shown in Quick Launch Settings as the title of the tap down button option. + [ROW_TAP_DOWN] = i18n_noop("Tap Down"), + /// Shown in Quick Launch Settings as the title of the hold up button quick launch option. + [ROW_HOLD_UP] = i18n_noop("Hold Up"), + /// Shown in Quick Launch Settings as the title of the hold center button quick launch option. + [ROW_HOLD_SELECT] = i18n_noop("Hold Center"), + /// Shown in Quick Launch Settings as the title of the hold down button quick launch option. + [ROW_HOLD_DOWN] = i18n_noop("Hold Down"), + /// Shown in Quick Launch Settings as the title of the hold back button quick launch option. + [ROW_HOLD_BACK] = i18n_noop("Hold Back"), +}; + +static void prv_get_subtitle_string(AppInstallId app_id, QuickLaunchData *data, + char *buffer, uint8_t buf_len) { + if (app_id == INSTALL_ID_INVALID) { + /// Shown in Quick Launch Settings when the button is disabled. + i18n_get_with_buffer("Disabled", buffer, buf_len); + return; + } else { + AppInstallEntry entry; + if (app_install_get_entry_for_install_id(app_id, &entry)) { + strncpy(buffer, entry.name, buf_len); + buffer[buf_len - 1] = '\0'; + return; + } + } + // if failed both, set as empty string + buffer[0] = '\0'; +} + +// Filter List Callbacks +//////////////////////// +static void prv_deinit_cb(SettingsCallbacks *context) { + QuickLaunchData *data = (QuickLaunchData *) context; + i18n_free_all(data); + app_free(data); +} + +static void prv_update_app_names(QuickLaunchData *data) { + // Tap buttons + prv_get_subtitle_string(quick_launch_single_click_get_app(BUTTON_ID_UP), data, + data->app_names[ROW_TAP_UP], APP_NAME_SIZE_BYTES); + prv_get_subtitle_string(quick_launch_single_click_get_app(BUTTON_ID_DOWN), data, + data->app_names[ROW_TAP_DOWN], APP_NAME_SIZE_BYTES); + + // Hold buttons + prv_get_subtitle_string(quick_launch_get_app(BUTTON_ID_UP), data, + data->app_names[ROW_HOLD_UP], APP_NAME_SIZE_BYTES); + prv_get_subtitle_string(quick_launch_get_app(BUTTON_ID_SELECT), data, + data->app_names[ROW_HOLD_SELECT], APP_NAME_SIZE_BYTES); + prv_get_subtitle_string(quick_launch_get_app(BUTTON_ID_DOWN), data, + data->app_names[ROW_HOLD_DOWN], APP_NAME_SIZE_BYTES); + prv_get_subtitle_string(quick_launch_get_app(BUTTON_ID_BACK), data, + data->app_names[ROW_HOLD_BACK], APP_NAME_SIZE_BYTES); +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + QuickLaunchData *data = (QuickLaunchData *)context; + PBL_ASSERTN(row < NUM_ROWS); + const char *title = i18n_get(s_row_titles[row], data); + char *subtitle_buf = data->app_names[row]; + menu_cell_basic_draw(ctx, cell_layer, title, subtitle_buf, NULL); +} + +static uint16_t prv_get_initial_selection_cb(SettingsCallbacks *context) { + // If launched by quick launch, select the row of the button pressed, otherwise default to 0 + if (app_launch_reason() == APP_LAUNCH_QUICK_LAUNCH) { + ButtonId button = app_launch_button(); + // Map button to hold row (quick launch is always hold/long press) + switch (button) { + case BUTTON_ID_UP: return ROW_HOLD_UP; + case BUTTON_ID_SELECT: return ROW_HOLD_SELECT; + case BUTTON_ID_DOWN: return ROW_HOLD_DOWN; + case BUTTON_ID_BACK: return ROW_HOLD_BACK; + default: break; + } + } + return 0; +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + PBL_ASSERTN(row < NUM_ROWS); + + ButtonId button; + bool is_tap; + + switch (row) { + case ROW_TAP_UP: + button = BUTTON_ID_UP; + is_tap = true; + break; + case ROW_TAP_DOWN: + button = BUTTON_ID_DOWN; + is_tap = true; + break; + case ROW_HOLD_UP: + button = BUTTON_ID_UP; + is_tap = false; + break; + case ROW_HOLD_SELECT: + button = BUTTON_ID_SELECT; + is_tap = false; + break; + case ROW_HOLD_DOWN: + button = BUTTON_ID_DOWN; + is_tap = false; + break; + case ROW_HOLD_BACK: + button = BUTTON_ID_BACK; + is_tap = false; + break; + default: + return; + } + + quick_launch_app_menu_window_push(button, is_tap); +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return NUM_ROWS; +} + +static void prv_appear(SettingsCallbacks *context) { + QuickLaunchData *data = (QuickLaunchData *)context; + prv_update_app_names(data); +} + +static Window *prv_init(void) { + QuickLaunchData *data = app_malloc_check(sizeof(*data)); + *data = (QuickLaunchData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .get_initial_selection = prv_get_initial_selection_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + .appear = prv_appear, + }; + + return settings_window_create(SettingsMenuItemQuickLaunch, &data->callbacks); +} + +const SettingsModuleMetadata *settings_quick_launch_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + /// Title of the Quick Launch Settings submenu in Settings + .name = i18n_noop("Quick Launch"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/quick_launch.h b/src/fw/apps/system/settings/quick_launch.h new file mode 100644 index 0000000000..a0f01a2c99 --- /dev/null +++ b/src/fw/apps/system/settings/quick_launch.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_quick_launch_get_info(void); diff --git a/src/fw/apps/system/settings/quick_launch_app_menu.c b/src/fw/apps/system/settings/quick_launch_app_menu.c new file mode 100644 index 0000000000..750f76881b --- /dev/null +++ b/src/fw/apps/system/settings/quick_launch_app_menu.c @@ -0,0 +1,175 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! This file generates a menu that lets the user select a quicklaunch app +//! The menu that is generated is the same as the "main menu" but with a +//! title + +#include "quick_launch_app_menu.h" +#include "quick_launch_setup_menu.h" +#include "quick_launch.h" +#include "menu.h" +#include "option_menu.h" + +#include "applib/graphics/graphics.h" +#include "applib/graphics/text.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/option_menu_window.h" +#include "applib/ui/window_stack.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" +#include "process_management/app_install_manager.h" +#include "apps/system/timeline/timeline.h" +#include "process_management/app_menu_data_source.h" +#include "resource/resource_ids.auto.h" +#include "shell/prefs.h" + +typedef struct { + AppMenuDataSource data_source; + ButtonId button; + bool is_tap; + int16_t selected; + OptionMenu *option_menu; +} QuickLaunchAppMenuData; + +#define NUM_CUSTOM_CELLS 1 + + +/* Callback Functions */ + +static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstallEntry *entry) { + QuickLaunchAppMenuData *data = (QuickLaunchAppMenuData *)source->callback_context; + const Uuid timeline_uuid = TIMELINE_UUID_INIT; + const Uuid timeline_past_uuid = TIMELINE_PAST_UUID_INIT; + const Uuid health_uuid = UUID_HEALTH_DATA_SOURCE; + + if (app_install_entry_is_watchface(entry)) { + return false; // Skip watchfaces + } + if (app_install_entry_is_hidden(entry) && + !app_install_entry_is_quick_launch_visible_only(entry)) { + return false; // Skip hidden apps unless they are quick launch visible + } + + // For tap buttons, filter Timeline apps based on button + if (data->is_tap) { + if (data->button == BUTTON_ID_UP) { + // Tap Up: Only show Timeline Past, hide Timeline Future + if (uuid_equal(&entry->uuid, &timeline_uuid)) { + return false; + } + } else if (data->button == BUTTON_ID_DOWN) { + // Tap Down: Only show Timeline Future, hide Timeline Past + if (uuid_equal(&entry->uuid, &timeline_past_uuid)) { + return false; + } + // We also only want the Health app for Tap Up + if (uuid_equal(&entry->uuid, &health_uuid)) { + return false; + } + } else { + if (uuid_equal(&entry->uuid, &health_uuid)) { + return false; + } + } + } + + return true; +} + +static uint16_t prv_menu_get_num_rows(OptionMenu *option_menu, void *context) { + QuickLaunchAppMenuData *data = context; + return app_menu_data_source_get_count(&data->data_source) + NUM_CUSTOM_CELLS; +} + +static void prv_menu_draw_row(OptionMenu *option_menu, GContext* ctx, const Layer *cell_layer, + const GRect *text_frame, uint32_t row, bool selected, void *context) { + + QuickLaunchAppMenuData *data = context; + const char *text = NULL; + if (row == 0) { + text = i18n_get("Disable", data); + } else { + AppMenuNode *node = app_menu_data_source_get_node_at_index(&data->data_source, + row - NUM_CUSTOM_CELLS); + text = node->name; + } + option_menu_system_draw_row(option_menu, ctx, cell_layer, text_frame, text, selected, context); +} + +static void prv_menu_select(OptionMenu *option_menu, int selection, void *context) { + window_set_click_config_provider(&option_menu->window, NULL); + + QuickLaunchAppMenuData *data = context; + if (selection == 0) { + if (data->is_tap) { + quick_launch_single_click_set_app(data->button, INSTALL_ID_INVALID); + quick_launch_single_click_set_enabled(data->button, false); + } else { + quick_launch_set_app(data->button, INSTALL_ID_INVALID); + quick_launch_set_enabled(data->button, false); + } + app_window_stack_pop(true); + } else { + AppMenuNode* app_menu_node = + app_menu_data_source_get_node_at_index(&data->data_source, selection - NUM_CUSTOM_CELLS); + if (data->is_tap) { + quick_launch_single_click_set_app(data->button, app_menu_node->install_id); + } else { + quick_launch_set_app(data->button, app_menu_node->install_id); + } + app_window_stack_pop(true); + } +} + +static void prv_menu_reload_data(void *context) { + QuickLaunchAppMenuData *data = context; + option_menu_reload_data(data->option_menu); +} + +static void prv_menu_unload(OptionMenu *option_menu, void *context) { + QuickLaunchAppMenuData *data = context; + + option_menu_destroy(option_menu); + app_menu_data_source_deinit(&data->data_source); + i18n_free_all(data); + app_free(data); +} + +void quick_launch_app_menu_window_push(ButtonId button, bool is_tap) { + QuickLaunchAppMenuData *data = app_zalloc_check(sizeof(*data)); + data->button = button; + data->is_tap = is_tap; + + OptionMenu *option_menu = option_menu_create(); + data->option_menu = option_menu; + + app_menu_data_source_init(&data->data_source, &(AppMenuDataSourceCallbacks) { + .changed = prv_menu_reload_data, + .filter = prv_app_filter_callback, + }, data); + + const AppInstallId install_id = is_tap ? quick_launch_single_click_get_app(button) + : quick_launch_get_app(button); + const int app_index = app_menu_data_source_get_index_of_app_with_install_id(&data->data_source, + install_id); + + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + const OptionMenuConfig config = { + .title = i18n_get(i18n_noop("Quick Launch"), data), + .choice = (install_id == INSTALL_ID_INVALID) ? 0 : (app_index + NUM_CUSTOM_CELLS), + .status_colors = { GColorWhite, GColorBlack, }, + .highlight_colors = { highlight_bg, gcolor_legible_over(highlight_bg) }, + .icons_enabled = true, + }; + option_menu_configure(option_menu, &config); + option_menu_set_callbacks(option_menu, &(OptionMenuCallbacks) { + .select = prv_menu_select, + .get_num_rows = prv_menu_get_num_rows, + .draw_row = prv_menu_draw_row, + .unload = prv_menu_unload, + }, data); + + const bool animated = true; + app_window_stack_push(&option_menu->window, animated); +} diff --git a/src/fw/apps/system/settings/quick_launch_app_menu.h b/src/fw/apps/system/settings/quick_launch_app_menu.h new file mode 100644 index 0000000000..090c872a6c --- /dev/null +++ b/src/fw/apps/system/settings/quick_launch_app_menu.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "shell/normal/quick_launch.h" +#include + +void quick_launch_app_menu_window_push(ButtonId button, bool is_tap); diff --git a/src/fw/apps/system/settings/quick_launch_setup_menu.c b/src/fw/apps/system/settings/quick_launch_setup_menu.c new file mode 100644 index 0000000000..f1b9e04ebe --- /dev/null +++ b/src/fw/apps/system/settings/quick_launch_setup_menu.c @@ -0,0 +1,97 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! This file is responsible for displaying the initial Quick Launch setup menu. +//! If a user long presses up or down from a watchface and has previously not +//! set up an application to launch for that long press direction or has not disabled +//! the Quick Launch feature, then this will act as a mini-setup guide for the feature. +//! Once an application is set up to launch for that menu press direction, this should +//! never appear again. + +#include "quick_launch_setup_menu.h" +#include "quick_launch_app_menu.h" +#include "quick_launch.h" + +#include "applib/app.h" +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/window_stack.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_menu_data_source.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "system/logging.h" +#include "system/passert.h" + +#include + +typedef enum QuickLaunchSetupVersion { + //! Initial version or never opened + QuickLaunchSetupVersion_InitialVersion = 0, + //! 4.0 UX with Toggle Apps + QuickLaunchSetupVersion_UX4WithToggleApps = 1, + + QuickLaunchSetupVersionCount, + //! QuickLaunchSetupVersion is an increasing version number. QuickLaunchSetupVersionCurrent must + //! not decrement. This should ensure that the current version is always the latest. + QuickLaunchSetupVersionCurrent = QuickLaunchSetupVersionCount - 1, +} QuickLaunchSetupVersion; + +static void prv_push_settings_menu(void) { + settings_menu_push(SettingsMenuItemQuickLaunch); +} + +static void prv_handle_quick_launch_confirm(ClickRecognizerRef recognizer, void *context) { + PBL_ASSERTN(context); + expandable_dialog_pop(context); + prv_push_settings_menu(); +} + +static void prv_push_first_use_dialog(void) { + const void *i18n_owner = prv_push_first_use_dialog; // Use this function as the i18n owner + /// Title for the Quick Launch first use dialog. + const char *header = i18n_get("Quick Launch", i18n_owner); + /// Help text for the Quick Launch first use dialog. + const char *text = i18n_get("Open favorite apps quickly with a long button press from your " + "watchface.", i18n_owner); + ExpandableDialog *expandable_dialog = expandable_dialog_create_with_params( + WINDOW_NAME("Quick Launch First Use"), RESOURCE_ID_SUNNY_DAY_TINY, text, GColorBlack, + GColorWhite, NULL, RESOURCE_ID_ACTION_BAR_ICON_CHECK, prv_handle_quick_launch_confirm); + expandable_dialog_set_header(expandable_dialog, header); +#if PBL_ROUND + expandable_dialog_set_header_font(expandable_dialog, + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); +#endif + i18n_free_all(i18n_owner); + app_expandable_dialog_push(expandable_dialog); +} + +static void prv_init(void) { + const uint32_t version = quick_launch_get_quick_launch_setup_opened(); + quick_launch_set_quick_launch_setup_opened(QuickLaunchSetupVersionCurrent); + if (version == QuickLaunchSetupVersion_InitialVersion) { + prv_push_first_use_dialog(); + } else { + prv_push_settings_menu(); + } +} + +static void prv_main(void) { + prv_init(); + app_event_loop(); +} + +const PebbleProcessMd* quick_launch_setup_get_app_info(void) { + static const PebbleProcessMdSystem s_quick_launch_setup_app = { + .common = { + .visibility = ProcessVisibilityHidden, + .main_func = prv_main, + // UUID: 07e0d9cb-8957-4bf7-9d42-aaaaaaaaaaaa + .uuid = {0x07, 0xe0, 0xd9, 0xcb, 0x89, 0x57, 0x4b, 0xf7, + 0x9d, 0x42, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}, + }, + .name = "Quick Launch", + }; + return (const PebbleProcessMd*) &s_quick_launch_setup_app; +} diff --git a/src/fw/apps/system/settings/quick_launch_setup_menu.h b/src/fw/apps/system/settings/quick_launch_setup_menu.h new file mode 100644 index 0000000000..f3da2fe792 --- /dev/null +++ b/src/fw/apps/system/settings/quick_launch_setup_menu.h @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "quick_launch.h" + +#include "process_management/pebble_process_md.h" +#include "shell/normal/quick_launch.h" + +#include + +const PebbleProcessMd* quick_launch_setup_get_app_info(void); diff --git a/src/fw/apps/system/settings/quiet_time.c b/src/fw/apps/system/settings/quiet_time.c new file mode 100644 index 0000000000..52a3b270e3 --- /dev/null +++ b/src/fw/apps/system/settings/quiet_time.c @@ -0,0 +1,540 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "quiet_time.h" +#include "menu.h" +#include "window.h" + +#include "applib/ui/action_menu_window_private.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/menu_layer.h" +#include "applib/ui/time_range_selection_window.h" +#include "kernel/pbl_malloc.h" +#include "popups/health_tracking_ui.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" +#include "util/string.h" +#include "shell/prefs.h" + +#include + +typedef struct { + SettingsCallbacks callbacks; +} SettingsQuietTimeData; + +typedef struct { + SettingsCallbacks callbacks; + char *action_menu_text; + TimeRangeSelectionWindowData schedule_window; + ActionMenuConfig action_menu; +} SettingsQuietTimeScheduleData; + +#ifdef CONFIG_TOUCH +typedef struct { + SettingsCallbacks callbacks; +} SettingsQuietTimeBacklightData; +#endif + +enum QuietTimeItem { + QuietTimeItemManual, + QuietTimeItemSchedule, + QuietTimeItemInterruptions, + QuietTimeItemNotifications, +#ifdef CONFIG_TOUCH + QuietTimeItemBacklight, +#else + QuietTimeItemMotionBacklight, +#endif +#ifdef CONFIG_SPEAKER + QuietTimeItemMuteSpeaker, +#endif + QuietTimeItem_Count, +}; + +enum QuietTimeScheduleItem { + QuietTimeScheduleItemCalendarAware, + QuietTimeScheduleItemWeekday, + QuietTimeScheduleItemWeekend, + QuietTimeScheduleItem_Count, +}; + +#ifdef CONFIG_TOUCH +enum QuietTimeBacklightItem { + QuietTimeBacklightItemMotion, + QuietTimeBacklightItemTouch, + QuietTimeBacklightItem_Count, +}; +#endif + +static const AlertMask s_dnd_mask_cycle[] = { + AlertMaskAllOff, + AlertMaskPhoneCalls, +}; + +static AlertMask prv_cycle_dnd_mask(void) { + AlertMask mask = alerts_get_dnd_mask(); + int index = 0; + for (size_t i = 0; i < ARRAY_LENGTH(s_dnd_mask_cycle); i++) { + if (s_dnd_mask_cycle[i] == mask) { + index = i; + break; + } + } + mask = s_dnd_mask_cycle[(index + 1) % ARRAY_LENGTH(s_dnd_mask_cycle)]; + alerts_set_dnd_mask(mask); + return mask; +} + +static const char *prv_get_dnd_mask_subtitle(void *i18n_key) { + const char *title = NULL; + switch (alerts_get_dnd_mask()) { + case AlertMaskAllOff: + title = i18n_get("Quiet All Notifications", i18n_key); + break; + case AlertMaskPhoneCalls: + title = i18n_get("Allow Phone Calls", i18n_key); + break; + default: + title = "???"; + break; + } + return title; +} + +static const DndNotificationMode s_dnd_notification_mode_cycle[] = { + DndNotificationModeShow, + DndNotificationModeHide, +}; + +static DndNotificationMode prv_cycle_dnd_notification_mode(void) { + DndNotificationMode mode = alerts_preferences_dnd_get_show_notifications(); + int index = 0; + for (size_t i = 0; i < ARRAY_LENGTH(s_dnd_notification_mode_cycle); i++) { + if (s_dnd_notification_mode_cycle[i] == mode) { + index = i; + break; + } + } + mode = s_dnd_notification_mode_cycle[(index + 1) % ARRAY_LENGTH(s_dnd_notification_mode_cycle)]; + alerts_preferences_dnd_set_show_notifications(mode); + return mode; +} + +static const char *prv_get_dnd_notifications_enable(void *i18n_key) { + switch (alerts_preferences_dnd_get_show_notifications()) { + case DndNotificationModeShow: + return i18n_get("Show", i18n_key); + case DndNotificationModeHide: + return i18n_get("Hide", i18n_key); + default: + return "???"; + } +} + +static void prv_get_dnd_time(DoNotDisturbScheduleType type, char *time_string, const uint8_t len) { + DoNotDisturbSchedule schedule; + do_not_disturb_get_schedule(type, &schedule); + + clock_format_time(time_string, len, schedule.from_hour, schedule.from_minute, true); + strcat(time_string, " - "); + uint8_t current_length = strnlen(time_string, len); + char *buffer = time_string + current_length; + clock_format_time(buffer, len - current_length, schedule.to_hour, schedule.to_minute, true); +} + +/////////////////////////////// +// DND Action Menu Window +/////////////////////////////// + +enum { + DNDMenuItemDisable = 0, + DNDMenuItemChangeSchedule, + DNDMenuItem_Count +}; + +static void prv_toggle_scheduled_dnd(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + do_not_disturb_toggle_scheduled((DoNotDisturbScheduleType) item->action_data); +} + +static void prv_complete_schedule(TimeRangeSelectionWindowData *schedule_window, void *data) { + DoNotDisturbScheduleType type = (DoNotDisturbScheduleType) data; + DoNotDisturbSchedule schedule = { + .from_hour = schedule_window->from.hour, + .from_minute = schedule_window->from.minute, + .to_hour = schedule_window->to.hour, + .to_minute = schedule_window->to.minute, + }; + + if (schedule.from_hour == schedule.to_hour && schedule.from_minute == schedule.to_minute) { + if ((schedule.to_minute = (schedule.to_minute + 1) % 60) == 0) { + schedule.to_hour = (schedule.to_hour + 1) % 24; + } + } + + do_not_disturb_set_schedule(type, &schedule); + + const bool animated = true; + window_stack_remove(&schedule_window->window, animated); +} + +static void prv_time_range_select_window_push(DoNotDisturbScheduleType type, + SettingsQuietTimeScheduleData *data) { + DoNotDisturbSchedule schedule; + do_not_disturb_get_schedule(type, &schedule); + TimeRangeSelectionWindowData *schedule_window = &data->schedule_window; + time_range_selection_window_init(schedule_window, GColorCobaltBlue, + prv_complete_schedule, (void*)(uintptr_t) type); + + schedule_window->from.hour = schedule.from_hour; + schedule_window->from.minute = schedule.from_minute; + schedule_window->to.hour = schedule.to_hour; + schedule_window->to.minute = schedule.to_minute; + app_window_stack_push(&schedule_window->window, true); +} + +static void prv_dnd_set_schedule(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + DoNotDisturbScheduleType type = (DoNotDisturbScheduleType) item->action_data; + do_not_disturb_set_schedule_enabled(type, true); + prv_time_range_select_window_push(type, context); +} + +static void prv_scheduled_dnd_menu_cleanup(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); + SettingsQuietTimeScheduleData *data = context; + time_range_selection_window_deinit(&data->schedule_window); + app_free(data->action_menu_text); + i18n_free_all(&data->action_menu); + task_free(root_level); +} + +static void prv_scheduled_dnd_menu_push(DoNotDisturbScheduleType type, + SettingsQuietTimeScheduleData *data) { + data->action_menu = (ActionMenuConfig) { + .context = data, + .colors.background = shell_prefs_get_theme_highlight_color(), + .did_close = prv_scheduled_dnd_menu_cleanup, + }; + + ActionMenuLevel *level = + task_malloc_check(sizeof(ActionMenuLevel) + DNDMenuItem_Count * sizeof(ActionMenuItem)); + *level = (ActionMenuLevel) { + .num_items = DNDMenuItem_Count, + .display_mode = ActionMenuLevelDisplayModeWide, + }; + const uint8_t text_max_size = 30; + const uint8_t buffer_size = text_max_size + 22; + data->action_menu_text = app_malloc_check(buffer_size); + if (do_not_disturb_is_schedule_enabled(type)) { + strncpy(data->action_menu_text, i18n_get("Disable", &data->action_menu), buffer_size); + } else { + strncpy(data->action_menu_text, i18n_get("Enable", &data->action_menu), text_max_size); + strcat(data->action_menu_text, " ("); + uint8_t current_length = strnlen(data->action_menu_text, buffer_size); + char *buffer = data->action_menu_text + current_length; + prv_get_dnd_time(type, buffer, buffer_size - current_length); + strcat(data->action_menu_text, ")"); + } + + level->items[DNDMenuItemDisable] = (ActionMenuItem) { + .label = data->action_menu_text, + .perform_action = prv_toggle_scheduled_dnd, + }; + + level->items[DNDMenuItemChangeSchedule] = (ActionMenuItem) { + .label = i18n_get("Change Schedule", &data->action_menu), + .perform_action = prv_dnd_set_schedule, + }; + + if (type == WeekdaySchedule) { + level->items[DNDMenuItemDisable].action_data = (void*)(uintptr_t) WeekdaySchedule; + level->items[DNDMenuItemChangeSchedule].action_data = (void*)(uintptr_t) WeekdaySchedule; + } else { + level->items[DNDMenuItemDisable].action_data = (void*)(uintptr_t) WeekendSchedule; + level->items[DNDMenuItemChangeSchedule].action_data = (void*)(uintptr_t) WeekendSchedule; + } + + data->action_menu.root_level = level; + app_action_menu_open(&data->action_menu); +} + +/////////////////////////////// +// Schedule sub-menu +/////////////////////////////// + +static void prv_schedule_deinit_cb(SettingsCallbacks *context) { + SettingsQuietTimeScheduleData *data = (SettingsQuietTimeScheduleData *) context; + i18n_free_all(data); + app_free(data); +} + +static void prv_schedule_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsQuietTimeScheduleData *data = (SettingsQuietTimeScheduleData *) context; + const char *title = NULL; + char *subtitle = NULL; + const uint8_t buffer_length = 80; + subtitle = app_malloc_check(buffer_length); + + switch (row) { + case QuietTimeScheduleItemCalendarAware: + title = i18n_get("Calendar Aware", data); + strncpy(subtitle, do_not_disturb_is_smart_dnd_enabled() ? + i18n_ctx_get("QuietTime", "Enabled", data) : + i18n_ctx_get("QuietTime", "Disabled", data), buffer_length); + break; + case QuietTimeScheduleItemWeekday: + title = i18n_get("Weekdays", data); + if (do_not_disturb_is_schedule_enabled(WeekdaySchedule)) { + prv_get_dnd_time(WeekdaySchedule, subtitle, buffer_length); + } else { + strncpy(subtitle, i18n_ctx_get("QuietTime", "Disabled", data), buffer_length); + } + break; + case QuietTimeScheduleItemWeekend: + title = i18n_get("Weekends", data); + if (do_not_disturb_is_schedule_enabled(WeekendSchedule)) { + prv_get_dnd_time(WeekendSchedule, subtitle, buffer_length); + } else { + strncpy(subtitle, i18n_ctx_get("QuietTime", "Disabled", data), buffer_length); + } + break; + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); + app_free(subtitle); +} + +static void prv_schedule_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsQuietTimeScheduleData *data = (SettingsQuietTimeScheduleData *) context; + + switch (row) { + case QuietTimeScheduleItemCalendarAware: + do_not_disturb_toggle_smart_dnd(); + break; + case QuietTimeScheduleItemWeekday: + prv_scheduled_dnd_menu_push(WeekdaySchedule, data); + break; + case QuietTimeScheduleItemWeekend: + prv_scheduled_dnd_menu_push(WeekendSchedule, data); + break; + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemQuietTime); +} + +static uint16_t prv_schedule_num_rows_cb(SettingsCallbacks *context) { + return QuietTimeScheduleItem_Count; +} + +static void prv_schedule_submenu_push(void) { + SettingsQuietTimeScheduleData *data = app_zalloc_check(sizeof(*data)); + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_schedule_deinit_cb, + .draw_row = prv_schedule_draw_row_cb, + .select_click = prv_schedule_select_click_cb, + .num_rows = prv_schedule_num_rows_cb, + }; + + Window *window = settings_window_create_with_title(SettingsMenuItemQuietTime, + i18n_noop("Schedule"), &data->callbacks); + app_window_stack_push(window, true /* animated */); +} + +/////////////////////////////// +// Backlight sub-menu (touch boards) +/////////////////////////////// + +#ifdef CONFIG_TOUCH +static void prv_backlight_deinit_cb(SettingsCallbacks *context) { + SettingsQuietTimeBacklightData *data = (SettingsQuietTimeBacklightData *) context; + i18n_free_all(data); + app_free(data); +} + +static void prv_backlight_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsQuietTimeBacklightData *data = (SettingsQuietTimeBacklightData *) context; + const char *title = NULL; + const char *subtitle = NULL; + switch (row) { + case QuietTimeBacklightItemMotion: + title = i18n_noop("Motion"); + subtitle = alerts_preferences_dnd_get_motion_backlight() ? i18n_noop("On") : i18n_noop("Off"); + break; + case QuietTimeBacklightItemTouch: + title = i18n_noop("Touch"); + subtitle = alerts_preferences_dnd_get_touch_backlight() ? i18n_noop("On") : i18n_noop("Off"); + break; + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static void prv_backlight_select_click_cb(SettingsCallbacks *context, uint16_t row) { + switch (row) { + case QuietTimeBacklightItemMotion: + alerts_preferences_dnd_set_motion_backlight(!alerts_preferences_dnd_get_motion_backlight()); + break; + case QuietTimeBacklightItemTouch: + alerts_preferences_dnd_set_touch_backlight(!alerts_preferences_dnd_get_touch_backlight()); + break; + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemQuietTime); +} + +static uint16_t prv_backlight_num_rows_cb(SettingsCallbacks *context) { + return QuietTimeBacklightItem_Count; +} + +static void prv_backlight_submenu_push(void) { + SettingsQuietTimeBacklightData *data = app_zalloc_check(sizeof(*data)); + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_backlight_deinit_cb, + .draw_row = prv_backlight_draw_row_cb, + .select_click = prv_backlight_select_click_cb, + .num_rows = prv_backlight_num_rows_cb, + }; + + Window *window = settings_window_create_with_title(SettingsMenuItemQuietTime, + i18n_noop("Backlight"), &data->callbacks); + app_window_stack_push(window, true /* animated */); +} +#endif // CONFIG_TOUCH + +/////////////////////////////// +// Top-level Quiet Time menu +/////////////////////////////// + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsQuietTimeData *data = (SettingsQuietTimeData *) context; + i18n_free_all(data); + app_free(data); +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsQuietTimeData *data = (SettingsQuietTimeData *) context; + const char *title = NULL; + const char *subtitle = NULL; + + switch (row) { + case QuietTimeItemManual: + title = i18n_get("Manual", data); + subtitle = do_not_disturb_is_manually_enabled() ? + i18n_get("On", data) : i18n_get("Off", data); + break; + case QuietTimeItemSchedule: + title = i18n_get("Schedule", data); + break; + case QuietTimeItemInterruptions: + title = i18n_get("Interruptions", data); + subtitle = prv_get_dnd_mask_subtitle(data); + break; + case QuietTimeItemNotifications: + title = i18n_get("Notifications", data); + subtitle = prv_get_dnd_notifications_enable(data); + break; +#ifdef CONFIG_TOUCH + case QuietTimeItemBacklight: + title = i18n_get("Backlight", data); + break; +#else + case QuietTimeItemMotionBacklight: + title = i18n_get("Motion Backlight", data); + subtitle = alerts_preferences_dnd_get_motion_backlight() ? + i18n_get("On", data) : i18n_get("Off", data); + break; +#endif +#ifdef CONFIG_SPEAKER + case QuietTimeItemMuteSpeaker: + title = i18n_get("Mute Speaker", data); + subtitle = alerts_preferences_dnd_get_mute_speaker() ? + i18n_get("On", data) : i18n_get("Off", data); + break; +#endif + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + switch (row) { + case QuietTimeItemManual: + do_not_disturb_toggle_manually_enabled(ManualDNDFirstUseSourceSettingsMenu); + break; + case QuietTimeItemSchedule: + prv_schedule_submenu_push(); + break; + case QuietTimeItemInterruptions: + prv_cycle_dnd_mask(); + break; + case QuietTimeItemNotifications: + prv_cycle_dnd_notification_mode(); + break; +#ifdef CONFIG_TOUCH + case QuietTimeItemBacklight: + prv_backlight_submenu_push(); + break; +#else + case QuietTimeItemMotionBacklight: + alerts_preferences_dnd_set_motion_backlight(!alerts_preferences_dnd_get_motion_backlight()); + break; +#endif +#ifdef CONFIG_SPEAKER + case QuietTimeItemMuteSpeaker: + alerts_preferences_dnd_set_mute_speaker(!alerts_preferences_dnd_get_mute_speaker()); + break; +#endif + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemQuietTime); +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return QuietTimeItem_Count; +} + +static Window *prv_init(void) { + SettingsQuietTimeData* data = app_zalloc_check(sizeof(*data)); + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + }; + + return settings_window_create(SettingsMenuItemQuietTime, &data->callbacks); +} + +const SettingsModuleMetadata *settings_quiet_time_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Quiet Time"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/quiet_time.h b/src/fw/apps/system/settings/quiet_time.h new file mode 100644 index 0000000000..3714a25b20 --- /dev/null +++ b/src/fw/apps/system/settings/quiet_time.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_quiet_time_get_info(void); diff --git a/src/fw/apps/system/settings/remote.c b/src/fw/apps/system/settings/remote.c new file mode 100644 index 0000000000..eaaf62ae60 --- /dev/null +++ b/src/fw/apps/system/settings/remote.c @@ -0,0 +1,150 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "remote.h" +#include "bluetooth.h" + +#include "applib/fonts/fonts.h" +#include "applib/ui/action_menu_window_private.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/menu_layer.h" +#include "applib/ui/text_layer.h" +#include "applib/ui/ui.h" +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/system_icons.h" +#include "popups/ble_hrm/ble_hrm_stop_sharing_popup.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/bluetooth/ble_hrm.h" +#include "system/logging.h" +#include "system/passert.h" + +#include +#include + +enum { + RemoteMenuForget = 0, +#ifdef CONFIG_HRM + RemoteMenuStopSharingHeartRate, +#endif + RemoteMenu_Count +}; + +typedef struct { + StoredRemote remote; + + ActionMenuConfig action_menu; + struct SettingsBluetoothData *bt_data; +} SettingsRemoteData; + +static void prv_dialog_unload(void *context) { + PBL_ASSERTN(context); + i18n_free_all(context); +} + +static void prv_show_dialog(void *i18n_owner) { + DialogCallbacks callback = { .unload = prv_dialog_unload }; + const char *text = BT_FORGET_PAIRING_STR; + ExpandableDialog *e_dialog = expandable_dialog_create_with_params( + "Forget Remote", RESOURCE_ID_GENERIC_CONFIRMATION_TINY, i18n_get(text, i18n_owner), + GColorWhite, GColorCobaltBlue, &callback, RESOURCE_ID_ACTION_BAR_ICON_CHECK, + expandable_dialog_close_cb); + i18n_free(text, i18n_owner); + + expandable_dialog_show_action_bar(e_dialog, true); + expandable_dialog_set_header(e_dialog, i18n_get("You're all set", e_dialog)); + + app_expandable_dialog_push(e_dialog); +} + +static void prv_forget_ble_remote(int id) { + bt_persistent_storage_delete_ble_pairing_by_id(id); +} + +static void prv_remote_menu_cleanup(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); + SettingsRemoteData *data = (SettingsRemoteData *) context; + i18n_free_all(data); + task_free((void *) root_level); + task_free(data); +} + +static void prv_forget_item(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + SettingsRemoteData* remote_data = (SettingsRemoteData*) context; + StoredRemote* remote = &remote_data->remote; + prv_forget_ble_remote(remote->ble.bonding); + PBL_LOG_INFO("User Forgot BT Pairing"); + PBL_LOG_DBG("Name: %s", remote->name); + settings_bluetooth_update_remotes(remote_data->bt_data); + prv_show_dialog(context); +} + +#ifdef CONFIG_HRM +static void prv_stop_sharing_heart_rate(ActionMenu *action_menu, + const ActionMenuItem *item, + void *context) { + SettingsRemoteData *remote_data = (SettingsRemoteData*) context; + StoredRemote *remote = &remote_data->remote; + + ble_hrm_revoke_sharing_permission_for_connection(remote->ble.connection); + + app_simple_dialog_push(ble_hrm_stop_sharing_popup_create()); +} +#endif // CONFIG_HRM + +void settings_remote_menu_push(struct SettingsBluetoothData *bt_data, StoredRemote *stored_remote) { + SettingsRemoteData *data = app_malloc_check(sizeof(SettingsRemoteData)); + + PBL_LOG_DBG("NAME: %s", stored_remote->name); + *data = (SettingsRemoteData){}; + data->remote = *stored_remote; + data->bt_data = bt_data; + + data->action_menu = (ActionMenuConfig) { + .context = data, + .colors.background = shell_prefs_get_theme_highlight_color(), + .did_close = prv_remote_menu_cleanup, + }; + +#ifdef CONFIG_HRM + const bool is_sharing_hr = + settings_bluetooth_is_sharing_heart_rate_for_stored_remote(stored_remote); + const size_t num_items = RemoteMenu_Count - (is_sharing_hr ? 0 : 1); +#else + const size_t num_items = RemoteMenu_Count; +#endif + ActionMenuLevel *level = + task_zalloc_check(sizeof(ActionMenuLevel) + num_items * sizeof(ActionMenuItem)); + *level = (ActionMenuLevel) { + .num_items = num_items, + .display_mode = ActionMenuLevelDisplayModeWide, + }; + + level->items[RemoteMenuForget] = (ActionMenuItem) { + .label = i18n_get("Forget", data), + .perform_action = prv_forget_item, + .action_data = data, + }; + +#ifdef CONFIG_HRM + if (is_sharing_hr) { + level->items[RemoteMenuStopSharingHeartRate] = (ActionMenuItem) { + .label = i18n_get("Stop Sharing Heart Rate", data), + .perform_action = prv_stop_sharing_heart_rate, + .action_data = data, + }; + } +#endif + + data->action_menu.root_level = level; + app_action_menu_open(&data->action_menu); +} diff --git a/src/fw/apps/system/settings/remote.h b/src/fw/apps/system/settings/remote.h new file mode 100644 index 0000000000..40888d58b2 --- /dev/null +++ b/src/fw/apps/system/settings/remote.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include "kernel/events.h" +#include "bluetooth.h" + +void settings_remote_menu_push(struct SettingsBluetoothData *bt_data, StoredRemote* stored_remote); diff --git a/src/fw/apps/system/settings/settings.c b/src/fw/apps/system/settings/settings.c new file mode 100644 index 0000000000..ceb6218169 --- /dev/null +++ b/src/fw/apps/system/settings/settings.c @@ -0,0 +1,203 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "settings.h" +#include "menu.h" +#include "window.h" + +#include "applib/app.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "system/passert.h" +#include "shell/prefs.h" +#include "util/size.h" + +#define SETTINGS_CATEGORY_MENU_CELL_UNFOCUSED_ROUND_VERTICAL_PADDING 14 + +#ifdef CONFIG_SETTINGS_ICONS +// Icon resource IDs for each settings menu item (RESOURCE_ID_INVALID means no icon) +static const uint32_t SETTINGS_MENU_ICON_RESOURCES[SettingsMenuItem_Count] = { + [SettingsMenuItemBluetooth] = RESOURCE_ID_SETTINGS_MENU_ICON_BLUETOOTH, + [SettingsMenuItemNotifications] = RESOURCE_ID_SETTINGS_MENU_ICON_NOTIFICATIONS, + [SettingsMenuItemVibrations] = RESOURCE_ID_SETTINGS_MENU_ICON_VIBRATIONS, + [SettingsMenuItemQuietTime] = RESOURCE_ID_SETTINGS_MENU_ICON_QUIET_TIME, + [SettingsMenuItemTimeline] = RESOURCE_ID_SETTINGS_MENU_ICON_TIMELINE, + [SettingsMenuItemQuickLaunch] = RESOURCE_ID_SETTINGS_MENU_ICON_QUICK_LAUNCH, + [SettingsMenuItemDateTime] = RESOURCE_ID_SETTINGS_MENU_ICON_DATE_TIME, + [SettingsMenuItemDisplay] = RESOURCE_ID_SETTINGS_MENU_ICON_DISPLAY, + [SettingsMenuItemHealth] = RESOURCE_ID_SETTINGS_MENU_ICON_HEALTH, +#ifdef CONFIG_THEMING + [SettingsMenuItemThemes] = RESOURCE_ID_SETTINGS_MENU_ICON_THEMES, +#endif + [SettingsMenuItemActivity] = RESOURCE_ID_SETTINGS_MENU_ICON_BACKGROUND_APP, + [SettingsMenuItemSystem] = RESOURCE_ID_SETTINGS_MENU_ICON_SYSTEM, +}; +#endif + +typedef struct { + Window window; + MenuLayer menu_layer; +#ifdef CONFIG_SETTINGS_ICONS + GBitmap *icons[SettingsMenuItem_Count]; +#endif +} SettingsAppData; + +static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, void *context) { + return SettingsMenuItem_Count; +} + +static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + SettingsAppData *data = context; + + PBL_ASSERTN(cell_index->row < SettingsMenuItem_Count); + + const char *category_title = settings_menu_get_submodule_info(cell_index->row)->name; + const char *title = i18n_get(category_title, data); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_highlight_colors(&(data->menu_layer), + highlight_bg, + gcolor_legible_over(highlight_bg)); + menu_layer_set_scroll_wrap_around(&(data->menu_layer), + shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(&(data->menu_layer), + shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(&(data->menu_layer), + shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + +#ifdef CONFIG_SETTINGS_ICONS + GBitmap *icon = data->icons[cell_index->row]; +#else + GBitmap *icon = NULL; +#endif + menu_cell_basic_draw(ctx, cell_layer, title, NULL, icon); +} + +static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { + settings_menu_push(cell_index->row); +} + +static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, void *context) { +#if PBL_ROUND + PBL_ASSERTN(cell_index->row < SettingsMenuItem_Count); + + const int16_t focused_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT; + const int16_t unfocused_cell_height = + ((DISP_ROWS - focused_cell_height) / 2) - + SETTINGS_CATEGORY_MENU_CELL_UNFOCUSED_ROUND_VERTICAL_PADDING; + return menu_layer_is_index_selected(menu_layer, cell_index) ? focused_cell_height : + unfocused_cell_height; +#else + // FIXME: hardcoding as settings menu is "special" + return 37; +#endif +} + +static int16_t prv_get_separator_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, + void *context) { + return 0; +} + +static void prv_window_load(Window *window) { + SettingsAppData *data = window_get_user_data(window); + +#ifdef CONFIG_SETTINGS_ICONS + // Load icons + for (size_t i = 0; i < ARRAY_LENGTH(data->icons); i++) { + if (SETTINGS_MENU_ICON_RESOURCES[i] != RESOURCE_ID_INVALID) { + data->icons[i] = gbitmap_create_with_resource(SETTINGS_MENU_ICON_RESOURCES[i]); + } else { + data->icons[i] = NULL; + } + } +#endif + + // Create the menu + GRect bounds = data->window.layer.bounds; +#if PBL_ROUND + bounds = grect_inset_internal(bounds, 0, + SETTINGS_CATEGORY_MENU_CELL_UNFOCUSED_ROUND_VERTICAL_PADDING); +#endif + MenuLayer *menu_layer = &data->menu_layer; + menu_layer_init(menu_layer, &bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = prv_get_num_rows_callback, + .get_cell_height = prv_get_cell_height_callback, + .draw_row = prv_draw_row_callback, + .select_click = prv_select_callback, + .get_separator_height = prv_get_separator_height_callback + }); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_normal_colors(menu_layer, + PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite), + PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)); + menu_layer_set_highlight_colors(menu_layer, + highlight_bg, + gcolor_legible_over(highlight_bg)); + menu_layer_set_click_config_onto_window(menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + + layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); +} + +static void prv_window_unload(Window *window) { + SettingsAppData *data = window_get_user_data(window); + +#ifdef CONFIG_SETTINGS_ICONS + // Free icons + for (size_t i = 0; i < ARRAY_LENGTH(data->icons); i++) { + if (data->icons[i]) { + gbitmap_destroy(data->icons[i]); + } + } +#endif + + menu_layer_deinit(&data->menu_layer); + app_free(data); +} + +static void handle_init(void) { + SettingsAppData *data = app_zalloc_check(sizeof(SettingsAppData)); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("Settings")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers){ + .load = prv_window_load, + .unload = prv_window_unload, + }); + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); + app_window_stack_push(window, true); +} + +static void handle_deinit(void) { + // Window unload deinits everything +} + +static void s_main(void) { + handle_init(); + app_event_loop(); + handle_deinit(); +} + +const PebbleProcessMd *settings_get_app_info() { + static const PebbleProcessMdSystem s_settings_app = { + .common = { + .main_func = s_main, + // UUID: 07e0d9cb-8957-4bf7-9d42-35bf47caadfe + .uuid = {0x07, 0xe0, 0xd9, 0xcb, 0x89, 0x57, 0x4b, 0xf7, + 0x9d, 0x42, 0x35, 0xbf, 0x47, 0xca, 0xad, 0xfe}, + }, + .name = i18n_noop("Settings"), + .icon_resource_id = RESOURCE_ID_SETTINGS_TINY, + }; + return (const PebbleProcessMd*) &s_settings_app; +} diff --git a/src/fw/apps/system/settings/settings.h b/src/fw/apps/system/settings/settings.h new file mode 100644 index 0000000000..1b2fafe4c3 --- /dev/null +++ b/src/fw/apps/system/settings/settings.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* settings_get_app_info(); diff --git a/src/fw/apps/system/settings/system.c b/src/fw/apps/system/settings/system.c new file mode 100644 index 0000000000..a7320f8674 --- /dev/null +++ b/src/fw/apps/system/settings/system.c @@ -0,0 +1,1464 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "factory_reset.h" +#include "menu.h" +#include "option_menu.h" +#include "system.h" +#include "window.h" + +#include "applib/fonts/fonts.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/actionable_dialog.h" +#include "applib/ui/dialogs/confirmation_dialog.h" +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "applib/ui/option_menu_window.h" +#include "applib/ui/ui.h" +#include "drivers/ambient_light.h" +#include "kernel/core_dump.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/modals/modal_manager.h" +#include "kernel/util/sleep.h" +#include "mfg/mfg_info.h" +#include "mfg/mfg_serials.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "pbl/services/system_task.h" +#include "pbl/services/stationary.h" +#include "shell/normal/battery_ui.h" +#include "shell/prefs.h" +#include "system/bootbits.h" +#include "system/passert.h" +#include "util/math.h" +#include "util/size.h" +#include "util/time/time.h" +#include "system/version.h" + +#include "pbl/services/activity/activity.h" +#include "pbl/services/blob_db/api.h" +#include "system/logging.h" + +#include +#include + +#include "certifications.h" + +enum { + SystemInformationItemBtAddress = 0, + SystemInformationItemFirmware, + SystemInformationItemLanguage, + SystemInformationItemRecovery, + SystemInformationItemBootloader, + SystemInformationItemHardware, + SystemInformationItemSerial, + SystemInformationItemUptime, + SystemInformationItemLegal, + SystemInformationItem_Count, +}; + +enum { + DebuggingItemCoreDumpNow = 0, + DebuggingItemCoreDumpShortcut, + DebuggingItemPowerMode, + DebuggingItemALSThreshold, +#ifdef CONFIG_ACCEL_SENSITIVITY + DebuggingItemMotionSensitivity, +#endif +#ifdef CONFIG_DYNAMIC_BACKLIGHT + DebuggingItemDynamicBacklightMinThreshold, +#endif + DebuggingItemAccelShakeLogInfo, + DebuggingItemVibeLogInfo, + DebuggingItemCompactSettingsDbs, + DebuggingItem_Count, +}; + +typedef struct SystemCertificationData SystemCertificationData; + +typedef struct SystemCertificationMenuItem { + void (*draw_cell_fn)( + GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, + bool is_selected, const void *arg1, const void *arg2); + const void *arg1; + const void *arg2; + void (*select_cb)(SystemCertificationData *cd); +} SystemCertificationMenuItem; + +typedef struct SystemCertificationData { + GBitmap fcc_mark; + GBitmap kcc_mark; + GBitmap ce_mark; + GBitmap weee_mark; + GBitmap ukca_mark; + GBitmap r_mark; + GBitmap t_mark; + GBitmap aus_rcm_mark; + GBitmap nom_nyce_mark; + + GBitmap **regulatory_marks; + uint8_t regulatory_marks_count; + // For buiding up regulatory marks cells when constructing the menu + uint8_t current_regulatory_marks_cell_start_idx; + uint8_t num_regulatory_marks_in_current_cell; + uint16_t current_regulatory_marks_cell_width; + + SystemCertificationMenuItem *menu_items; + uint16_t menu_count; + + Window kcc_window; + BitmapLayer bmp_layer; + TextLayer title_text; + TextLayer info_text; + StatusBarLayer status_layer; + + char model_buffer[MFG_INFO_MODEL_STRING_LENGTH]; +} SystemCertificationData; + +typedef struct SystemInformationData { + FirmwareMetadata recovery_fw_metadata; + char bt_mac_addr[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]; + char boot_version_string[(sizeof(uint32_t) * 2) + 3]; + char recovery_version_string[sizeof(TINTIN_METADATA.version_tag)]; + // Ensure that OTP values are null-terminated + char serial_string[MFG_SERIAL_NUMBER_SIZE + 1]; + char hw_version_string[MFG_HW_VERSION_SIZE + 1]; + char uptime_string[16]; // "xxd xxh xxm xxs" + char const * subtitle_text[SystemInformationItem_Count]; + char language_string[16]; +} SystemInformationData; + +typedef struct SettingsSystemData { + SettingsCallbacks callbacks; + + SystemInformationData information_data; + SystemCertificationData certification_data; + + // The following components are shared by information, and certification. + Window window; + MenuLayer menu_layer; + StatusBarLayer status_layer; + + // ALS threshold data + char als_threshold_buffer[16]; // Buffer for formatted ALS threshold + char als_status_buffer[64]; // Buffer for NumberWindow label with status + bool als_adjustment_active; // Track if ALS adjustment is active + +#ifdef CONFIG_DYNAMIC_BACKLIGHT + // Dynamic backlight threshold data + char dyn_bl_min_threshold_buffer[16]; // Buffer for formatted min threshold +#endif +} SettingsSystemData; + +typedef enum { + SystemMenuItemInformation, + SystemMenuItemCertification, + SystemMenuItemStationaryToggle, + SystemMenuItemDebugging, + SystemMenuItemShutDown, + SystemMenuItemFactoryReset, + SystemMenuItem_Count, +} SystemMenuItem; + +static const char *s_item_titles[SystemMenuItem_Count] = { + [SystemMenuItemInformation] = i18n_noop("Information"), + [SystemMenuItemCertification] = i18n_noop("Certification"), + [SystemMenuItemStationaryToggle] = i18n_noop("Stand-By Mode"), + [SystemMenuItemDebugging] = i18n_noop("Debugging"), + [SystemMenuItemShutDown] = i18n_noop("Shut Down"), + [SystemMenuItemFactoryReset] = i18n_noop("Factory Reset"), +}; + +// Common status bar component is used across all windows that need them. +// This will init it and set the correct style to be used within the settings +// app. +static void prv_init_status_bar(StatusBarLayer *status_layer, Window *window, const char *text) { + status_bar_layer_init(status_layer); + status_bar_layer_set_title(status_layer, text, false, false); + status_bar_layer_set_separator_mode(status_layer, OPTION_MENU_STATUS_SEPARATOR_MODE); + status_bar_layer_set_colors(status_layer, GColorWhite, GColorBlack); + layer_add_child(&window->layer, status_bar_layer_get_layer(status_layer)); +} + +// Deinit the common status bar component. +static void prv_deinit_status_bar(StatusBarLayer *status_layer) { + layer_remove_from_parent(status_bar_layer_get_layer(status_layer)); + status_bar_layer_deinit(status_layer); +} + +// Dialog callbacks for confirmation. +//////////////////////////////////////////////////// +static ConfirmationDialog *prv_settings_confirm(const char *title, const char *text, + uint32_t resource_id) { + ConfirmationDialog *confirmation_dialog = confirmation_dialog_create(title); + Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); + + dialog_set_text(dialog, i18n_get(text, confirmation_dialog)); + dialog_set_background_color(dialog, GColorRed); + dialog_set_text_color(dialog, GColorWhite); + dialog_set_icon(dialog, resource_id); + + i18n_free_all(confirmation_dialog); + + return confirmation_dialog; +} + +// Information Window +////////////////////// + +static const char* s_information_titles[SystemInformationItem_Count] = { + [SystemInformationItemBtAddress] = i18n_noop("BT Address"), + [SystemInformationItemFirmware] = i18n_noop("Firmware"), + [SystemInformationItemLanguage] = i18n_noop("Language"), + [SystemInformationItemRecovery] = i18n_noop("Recovery"), + [SystemInformationItemBootloader] = i18n_noop("Bootloader"), + [SystemInformationItemHardware] = i18n_noop("Hardware"), + [SystemInformationItemSerial] = i18n_noop("Serial"), + [SystemInformationItemUptime] = i18n_noop("Uptime"), + [SystemInformationItemLegal] = i18n_noop("Legal") +}; + +static void prv_populate_uptime_string(SystemInformationData* data) { + uint32_t seconds_since_reboot = time_get_uptime_seconds(); + + uint32_t days, hours, minutes, seconds; + time_util_split_seconds_into_parts(seconds_since_reboot, &days, &hours, &minutes, &seconds); + + sniprintf(data->uptime_string, sizeof(data->uptime_string), + "%"PRIu32"d %"PRIu32"h %"PRIu32"m %"PRIu32"s", days, hours, minutes, seconds); +} + +static void prv_information_draw_row_callback(GContext* ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + PBL_ASSERTN(cell_index->section == 0); + PBL_ASSERTN(cell_index->row < SystemInformationItem_Count); + + SettingsSystemData *data = (SettingsSystemData *) context; + SystemInformationData *info = &data->information_data; + + const char *title = i18n_get(s_information_titles[cell_index->row], data); + menu_cell_basic_draw(ctx, cell_layer, title, info->subtitle_text[cell_index->row], NULL); +} + +int16_t prv_information_get_cell_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, void *context) { + return PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), + (menu_layer_is_index_selected(menu_layer, cell_index) ? + MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : + MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); +} + +static uint16_t prv_information_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, void *context) { + return SystemInformationItem_Count; +} + +#include "system/rtc_registers.h" + +static void prv_information_window_load(Window *window) { + SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); + + prv_init_status_bar(&data->status_layer, &data->window, i18n_get("Information", data)); + + // Create the menu + MenuLayer *menu_layer = &data->menu_layer; + GRect bounds = data->window.layer.bounds; + const GEdgeInsets menu_layer_insets = (GEdgeInsets) { + .top = STATUS_BAR_LAYER_HEIGHT, + .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT) + }; + bounds = grect_inset(bounds, menu_layer_insets); + menu_layer_init(menu_layer, &bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = prv_information_get_num_rows_callback, + .get_cell_height = prv_information_get_cell_height_callback, + .draw_row = prv_information_draw_row_callback, + }); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_highlight_colors(menu_layer, highlight_bg, gcolor_legible_over(highlight_bg)); + menu_layer_set_click_config_onto_window(menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + + layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); +} + +static void prv_information_window_unload(Window *window) { + SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); + menu_layer_deinit(&data->menu_layer); + prv_deinit_status_bar(&data->status_layer); +} + +static void prv_information_window_push(SettingsSystemData *data) { + SystemInformationData *info = &data->information_data; + *info = (SystemInformationData){}; + + bool success = version_copy_recovery_fw_version(info->recovery_version_string, + ARRAY_LENGTH(info->recovery_version_string)); + if (success == false) { + info->recovery_version_string[0] = '\0'; + } + + sniprintf(info->boot_version_string, sizeof(info->boot_version_string), + "0x%08" PRIx32, boot_version_read()); + bt_local_id_copy_address_mac_string(info->bt_mac_addr); + + // Ensure OTP strings are null-terminated + mfg_info_get_serialnumber(info->serial_string, MFG_SERIAL_NUMBER_SIZE + 1); + mfg_info_get_hw_version(info->hw_version_string, MFG_HW_VERSION_SIZE + 1); + prv_populate_uptime_string(info); + + sniprintf(info->language_string, sizeof(info->language_string), + "%s, v%u", i18n_get_locale(), i18n_get_version()); + + info->subtitle_text[SystemInformationItemBtAddress] = info->bt_mac_addr; + info->subtitle_text[SystemInformationItemFirmware] = + (char*) (strlen(TINTIN_METADATA.version_tag) >= 2 + ? TINTIN_METADATA.version_tag : TINTIN_METADATA.version_short); + info->subtitle_text[SystemInformationItemLanguage] = info->language_string; + info->subtitle_text[SystemInformationItemRecovery] = info->recovery_version_string; + info->subtitle_text[SystemInformationItemBootloader] = info->boot_version_string; + info->subtitle_text[SystemInformationItemHardware] = info->hw_version_string; + info->subtitle_text[SystemInformationItemSerial] = info->serial_string; + info->subtitle_text[SystemInformationItemUptime] = info->uptime_string; + info->subtitle_text[SystemInformationItemLegal] = "repebble.com/terms"; + + window_init(&data->window, WINDOW_NAME("System Information")); + window_set_user_data(&data->window, data); + window_set_window_handlers(&data->window, &(WindowHandlers) { + .load = prv_information_window_load, + .unload = prv_information_window_unload, + }); + + app_window_stack_push(&data->window, true); +} + +// Coredump trigger sequence +//////////////////////////// + +static void prv_coredump_confirm_cb(ClickRecognizerRef recognizer, void *context) { + core_dump_reset(true /* force_overwrite */); +} + +static void prv_confirm_pop(ClickRecognizerRef recognizer, void *context) { + confirmation_dialog_pop((ConfirmationDialog *)context); +} + +static void prv_coredump_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_coredump_confirm_cb); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_confirm_pop); + window_single_click_subscribe(BUTTON_ID_BACK, prv_confirm_pop); +} + +static void prv_maybe_trigger_core_dump() { + ConfirmationDialog *confirmation_dialog = prv_settings_confirm("Core Dump", + i18n_noop("Core dump and reboot?"), RESOURCE_ID_RESULT_FAILED_LARGE); + confirmation_dialog_set_click_config_provider(confirmation_dialog, + prv_coredump_click_config); + app_confirmation_dialog_push(confirmation_dialog); +} + +// ALS Threshold Settings +///////////////////////////// + +static void prv_update_als_threshold_label(NumberWindow *number_window, SettingsSystemData *data) { + uint32_t current_reading = ambient_light_get_light_level(); + uint32_t current_threshold = (uint32_t)number_window_get_value(number_window); + bool would_backlight_be_on = current_reading <= current_threshold; + + snprintf(data->als_status_buffer, sizeof(data->als_status_buffer), + "Backlight: %s", + would_backlight_be_on ? "ON" : "OFF"); + + number_window_set_label(number_window, data->als_status_buffer); +} + +static void prv_als_threshold_incremented(NumberWindow *number_window, void *context) { + SettingsSystemData *data = (SettingsSystemData*)context; + prv_update_als_threshold_label(number_window, data); +} + +static void prv_als_threshold_decremented(NumberWindow *number_window, void *context) { + SettingsSystemData *data = (SettingsSystemData*)context; + prv_update_als_threshold_label(number_window, data); +} + +static void prv_als_threshold_selected(NumberWindow *number_window, void *context) { + SettingsSystemData *data = (SettingsSystemData*)context; + uint32_t new_threshold = (uint32_t)number_window_get_value(number_window); + backlight_set_ambient_threshold(new_threshold); + data->als_adjustment_active = false; + light_allow(true); + app_window_stack_remove(&number_window->window, true /* animated */); +} + +static void prv_als_threshold_menu_push(SettingsSystemData *data) { + // Disable backlight while adjusting ALS threshold to prevent skewing readings + light_allow(false); + data->als_adjustment_active = true; + + // Give time for backlight to turn off + // If we don't do this, the text may say the false result + // until the user changes the value + psleep(200); + + // Get current ambient light reading to show backlight status + uint32_t current_reading = ambient_light_get_light_level(); + uint32_t current_threshold = backlight_get_ambient_threshold(); + bool would_backlight_be_on = current_reading <= current_threshold; + + // Create descriptive label with current status + snprintf(data->als_status_buffer, sizeof(data->als_status_buffer), + "Backlight: %s", + would_backlight_be_on ? "ON" : "OFF"); + + NumberWindow *number_window = number_window_create( + data->als_status_buffer, + (NumberWindowCallbacks) { + .selected = prv_als_threshold_selected, + .incremented = prv_als_threshold_incremented, + .decremented = prv_als_threshold_decremented, + }, + data + ); + + if (!number_window) { + // Re-enable backlight if NumberWindow creation failed + data->als_adjustment_active = false; + light_allow(true); + return; + } + + + // Set reasonable min/max values for ALS threshold + number_window_set_min(number_window, 0); + number_window_set_max(number_window, AMBIENT_LIGHT_LEVEL_MAX); + number_window_set_step_size(number_window, 1); + number_window_set_value(number_window, (int32_t)current_threshold); + + const bool animated = true; + app_window_stack_push(&number_window->window, animated); +} + +// Dynamic Backlight Min Threshold Settings +///////////////////////////// +#ifdef CONFIG_DYNAMIC_BACKLIGHT +static void prv_dyn_bl_min_threshold_selected(NumberWindow *number_window, void *context) { + uint32_t new_threshold = (uint32_t)number_window_get_value(number_window); + backlight_set_dynamic_min_threshold(new_threshold); + app_window_stack_remove(&number_window->window, true /* animated */); +} + +static void prv_dyn_bl_min_threshold_menu_push(SettingsSystemData *data) { + NumberWindow *number_window = number_window_create( + "Min Light Threshold", + (NumberWindowCallbacks) { + .selected = prv_dyn_bl_min_threshold_selected, + }, + data + ); + + if (!number_window) { + return; + } + + // Set reasonable min/max values + number_window_set_min(number_window, 0); + number_window_set_max(number_window, AMBIENT_LIGHT_LEVEL_MAX); + number_window_set_step_size(number_window, 1); + number_window_set_value(number_window, (int32_t)backlight_get_dynamic_min_threshold()); + + const bool animated = true; + app_window_stack_push(&number_window->window, animated); +} + +#endif + +// Motion Sensitivity Settings (Asterix/Obelix only) +///////////////////////////// +#ifdef CONFIG_ACCEL_SENSITIVITY +static const uint8_t s_motion_sensitivity_values[] = { 10, 25, 40, 55, 70, 85, 100 }; + +static const char *s_motion_sensitivity_labels[] = { + i18n_noop("Very Low"), + i18n_noop("Low"), + i18n_noop("Medium-Low"), + i18n_noop("Medium"), + i18n_noop("Medium-High"), + i18n_noop("High"), + i18n_noop("Very High") +}; + +static int prv_motion_sensitivity_get_selection_index() { + const uint8_t sensitivity = shell_prefs_get_motion_sensitivity(); + + // Find closest match + for (int i = 0; i < (int)ARRAY_LENGTH(s_motion_sensitivity_values); i++) { + if (sensitivity <= s_motion_sensitivity_values[i]) { + return i; + } + } + return ARRAY_LENGTH(s_motion_sensitivity_values) - 1; +} + +static void prv_motion_sensitivity_menu_select(OptionMenu *option_menu, int selection, void *context) { + shell_prefs_set_motion_sensitivity(s_motion_sensitivity_values[selection]); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_motion_sensitivity_menu_push(SettingsSystemData *data) { + int index = prv_motion_sensitivity_get_selection_index(); + const OptionMenuCallbacks callbacks = { + .select = prv_motion_sensitivity_menu_select, + }; + const char *title = i18n_noop("Motion Sensitivity"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_motion_sensitivity_labels), + true /* icons_enabled */, s_motion_sensitivity_labels, data); +} +#endif + +// Power mode option menu +///////////////////////// + +static const char *s_power_mode_labels[] = { + i18n_noop("High performance"), + i18n_noop("Low power"), +}; + +static void prv_power_mode_menu_select(OptionMenu *option_menu, int selection, void *context) { + shell_prefs_set_power_mode((PowerMode)selection); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_power_mode_menu_push(SettingsSystemData *data) { + int index = (int)shell_prefs_get_power_mode(); + const OptionMenuCallbacks callbacks = { + .select = prv_power_mode_menu_select, + }; + const char *title = i18n_noop("Power Mode"); + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, index, &callbacks, + ARRAY_LENGTH(s_power_mode_labels), + true /* icons_enabled */, s_power_mode_labels, data); +} + +// Compact growable settings DBs +//////////////////////////////// + +static void prv_compact_settings_dbs_task_cb(void *data) { + blob_db_compact_growable_dbs(); +} + +static void prv_compact_settings_dbs(void) { + system_task_add_callback(prv_compact_settings_dbs_task_cb, NULL); +} + +// Debug options window +/////////////////////// + +static const char* s_debugging_titles[DebuggingItem_Count] = { + [DebuggingItemCoreDumpNow] = i18n_noop("CoreDump now"), + [DebuggingItemCoreDumpShortcut] = i18n_noop("CoreDump shortcut"), + [DebuggingItemPowerMode] = i18n_noop("Power Mode"), + [DebuggingItemALSThreshold] = i18n_noop("ALS Threshold"), +#ifdef CONFIG_ACCEL_SENSITIVITY + [DebuggingItemMotionSensitivity] = i18n_noop("Motion Sensitivity"), +#endif +#ifdef CONFIG_DYNAMIC_BACKLIGHT + [DebuggingItemDynamicBacklightMinThreshold] = i18n_noop("Dyn BL Min Threshold"), +#endif + [DebuggingItemAccelShakeLogInfo] = i18n_noop("Shake Log Info"), + [DebuggingItemVibeLogInfo] = i18n_noop("Vibe Log Info"), + [DebuggingItemCompactSettingsDbs] = i18n_noop("Compact Settings DBs"), +}; + +static void prv_debugging_draw_row_callback(GContext* ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + PBL_ASSERTN(cell_index->section == 0); + PBL_ASSERTN(cell_index->row < DebuggingItem_Count); + + SettingsSystemData *data = (SettingsSystemData *) context; + + // Check if user canceled out of ALS adjustment (this gets called when we return to settings menu) + if (data->als_adjustment_active) { + data->als_adjustment_active = false; + light_allow(true); + } + + const char *title = i18n_get(s_debugging_titles[cell_index->row], data); + const char *subtitle_text = NULL; + if (cell_index->row == DebuggingItemCoreDumpShortcut) { + subtitle_text = shell_prefs_can_coredump_on_request() ? i18n_get("10 back-button presses", data) : i18n_get("Disabled", data); + } else if (cell_index->row == DebuggingItemPowerMode) { + subtitle_text = i18n_get(s_power_mode_labels[shell_prefs_get_power_mode()], data); + } else if (cell_index->row == DebuggingItemALSThreshold) { + // Show current threshold value + uint32_t current_threshold = backlight_get_ambient_threshold(); + snprintf(data->als_threshold_buffer, sizeof(data->als_threshold_buffer), + "%"PRIu32, current_threshold); + subtitle_text = data->als_threshold_buffer; + } +#ifdef CONFIG_ACCEL_SENSITIVITY + else if (cell_index->row == DebuggingItemMotionSensitivity) { + subtitle_text = i18n_get(s_motion_sensitivity_labels[prv_motion_sensitivity_get_selection_index()], data); + } +#endif +#ifdef CONFIG_DYNAMIC_BACKLIGHT + else if (cell_index->row == DebuggingItemDynamicBacklightMinThreshold) { + uint32_t min_threshold = backlight_get_dynamic_min_threshold(); + snprintf(data->dyn_bl_min_threshold_buffer, sizeof(data->dyn_bl_min_threshold_buffer), + "%"PRIu32, min_threshold); + subtitle_text = data->dyn_bl_min_threshold_buffer; + } +#endif + else if (cell_index->row == DebuggingItemAccelShakeLogInfo) { + subtitle_text = shell_prefs_get_accel_shake_log_info_enabled() ? + i18n_get("Enabled", data) : i18n_get("Disabled", data); + } + else if (cell_index->row == DebuggingItemVibeLogInfo) { + subtitle_text = shell_prefs_get_vibe_log_info_enabled() ? + i18n_get("Enabled", data) : i18n_get("Disabled", data); + } + menu_cell_basic_draw(ctx, cell_layer, title, subtitle_text, NULL); +} + +int16_t prv_debugging_get_cell_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, void *context) { + return PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), + (menu_layer_is_index_selected(menu_layer, cell_index) ? + MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : + MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); +} + +static uint16_t prv_debugging_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, void *context) { + return DebuggingItem_Count; +} + +static void prv_debugging_select_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, + void *context) { + SettingsSystemData *data = (SettingsSystemData *) context; + + switch (cell_index->row) { + case DebuggingItemCoreDumpNow: + prv_maybe_trigger_core_dump(); + break; + case DebuggingItemCoreDumpShortcut: + shell_prefs_set_coredump_on_request(!shell_prefs_can_coredump_on_request()); + break; + case DebuggingItemPowerMode: + prv_power_mode_menu_push(data); + break; + case DebuggingItemALSThreshold: + prv_als_threshold_menu_push(data); + break; +#ifdef CONFIG_ACCEL_SENSITIVITY + case DebuggingItemMotionSensitivity: + prv_motion_sensitivity_menu_push(data); + break; +#endif +#ifdef CONFIG_DYNAMIC_BACKLIGHT + case DebuggingItemDynamicBacklightMinThreshold: + prv_dyn_bl_min_threshold_menu_push(data); + break; +#endif + case DebuggingItemAccelShakeLogInfo: + shell_prefs_set_accel_shake_log_info_enabled( + !shell_prefs_get_accel_shake_log_info_enabled()); + break; + case DebuggingItemVibeLogInfo: + shell_prefs_set_vibe_log_info_enabled( + !shell_prefs_get_vibe_log_info_enabled()); + break; + case DebuggingItemCompactSettingsDbs: + prv_compact_settings_dbs(); + break; + default: + WTF; + } + menu_layer_reload_data(menu_layer); +} + +static void prv_debugging_window_load(Window *window) { + SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); + + prv_init_status_bar(&data->status_layer, &data->window, i18n_get("Debugging", data)); + + // Create the menu + MenuLayer *menu_layer = &data->menu_layer; + GRect bounds = data->window.layer.bounds; + const GEdgeInsets menu_layer_insets = (GEdgeInsets) { + .top = STATUS_BAR_LAYER_HEIGHT, + .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT) + }; + bounds = grect_inset(bounds, menu_layer_insets); + menu_layer_init(menu_layer, &bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = prv_debugging_get_num_rows_callback, + .get_cell_height = prv_debugging_get_cell_height_callback, + .draw_row = prv_debugging_draw_row_callback, + .select_click = prv_debugging_select_callback, + }); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_highlight_colors(menu_layer, highlight_bg, gcolor_legible_over(highlight_bg)); + menu_layer_set_click_config_onto_window(menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + + layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); +} + +static void prv_debugging_window_unload(Window *window) { + SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); + menu_layer_deinit(&data->menu_layer); + prv_deinit_status_bar(&data->status_layer); +} + +static void prv_debugging_window_push(SettingsSystemData *data) { + window_init(&data->window, WINDOW_NAME("Debugging")); + window_set_user_data(&data->window, data); + window_set_window_handlers(&data->window, &(WindowHandlers) { + .load = prv_debugging_window_load, + .unload = prv_debugging_window_unload, + }); + + app_window_stack_push(&data->window, true); +} + +// Debugging interstitial +///////////////////////// + +// Only show this once per boot. +static bool s_debugging_interstitial_confirmed = false; + +static void prv_debugging_confirm_cb(ClickRecognizerRef recognizer, void *context) { + ConfirmationDialog *dialog = (ConfirmationDialog *)context; + SettingsSystemData *data = (SettingsSystemData *)actionable_dialog_get_user_data((ActionableDialog *) dialog); + + confirmation_dialog_pop(dialog); + + s_debugging_interstitial_confirmed = true; + prv_debugging_window_push(data); +} + +static void prv_debugging_interstitial_click_config(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_debugging_confirm_cb); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_confirm_pop); + window_single_click_subscribe(BUTTON_ID_BACK, prv_confirm_pop); +} + +static void prv_debugging_interstitial_trigger(SettingsSystemData *context) { + if (s_debugging_interstitial_confirmed) { + prv_debugging_window_push(context); + return; + } + + ConfirmationDialog *confirmation_dialog = prv_settings_confirm("Debugging confirmation", + i18n_noop("PebbleOS developers only!"), RESOURCE_ID_GENERIC_WARNING_SMALL); + actionable_dialog_set_user_data((ActionableDialog *)confirmation_dialog, (void *)context); + confirmation_dialog_set_click_config_provider(confirmation_dialog, + prv_debugging_interstitial_click_config); + app_confirmation_dialog_push(confirmation_dialog); +} + +// Certification Window +/////////////////////// + +int16_t prv_certification_get_cell_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, + void *context) { + return PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), + (menu_layer_is_index_selected(menu_layer, cell_index) ? + MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : + MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); +} + +static void prv_draw_mark_with_inversion(GContext *ctx, GBitmap *mark, const GRect *box, + bool inverted) { + graphics_context_set_compositing_mode(ctx, GCompOpTint); + graphics_draw_bitmap_in_rect(ctx, mark, box); +} + +static int16_t prv_draw_generic_mark(GContext *ctx, GBitmap *mark, GPoint origin, bool highlight) { + GRect box = (GRect) { + .origin = origin, + .size = mark->bounds.size + }; + prv_draw_mark_with_inversion(ctx, mark, &box, highlight); + return origin.x + box.size.w; +} + +#define MARK_PADDING 10 + +#if PBL_RECT +static void prv_draw_rt_cell_rect(GContext *ctx, const Layer *cell_layer, GBitmap *mark, + const char *text, PBL_UNUSED bool is_selected) { + int16_t x = (MARK_PADDING / 2); + GRect box = cell_layer->bounds; + const bool highlight = menu_cell_layer_is_highlighted(cell_layer); + const int16_t vertical_padding = 6; + const GPoint mark_origin = GPoint(x, vertical_padding); + x = prv_draw_generic_mark(ctx, mark, mark_origin, highlight) + (MARK_PADDING / 2); + box.origin.x = x; + box.origin.y += 8; + box.size.w -= x; + box.size.h -= 8; + const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18); + graphics_draw_text(ctx, text, font, box, GTextOverflowModeFill, GTextAlignmentLeft, NULL); +} +#endif + +#if PBL_ROUND +static void prv_draw_rt_cell_round(GContext *ctx, const Layer *cell_layer, GBitmap *mark, + const char *text, bool is_selected) { + GRect rt_rect = cell_layer->bounds; + const int16_t horizontal_padding = 10; + const int16_t vertical_padding = is_selected ? 6 : 0; + rt_rect = grect_inset_internal(rt_rect, horizontal_padding, vertical_padding); + + // Calculate where the mark should be drawn + const GSize mark_size = mark->bounds.size; + GRect mark_rect = (GRect) { .size = mark_size }; + // If the cell is selected, align the mark at the top center so we can draw the text below it + const GAlign alignment = is_selected ? GAlignTop : GAlignCenter; + grect_align(&mark_rect, &rt_rect, alignment, true /* clip */); + + // Draw the mark + const bool highlight = menu_cell_layer_is_highlighted(cell_layer); + prv_draw_generic_mark(ctx, mark, mark_rect.origin, highlight); + + // Only draw the text if the cell is selected + if (is_selected) { + const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18); + GRect text_rect = rt_rect; + text_rect.size.h = fonts_get_font_height(font); + grect_align(&text_rect, &rt_rect, GAlignBottom, true /* clip */); + graphics_draw_text(ctx, text, font, text_rect, GTextOverflowModeFill, GTextAlignmentCenter, + NULL); + } +} +#endif + +static void prv_draw_rt_cell( + GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, + bool is_selected, const void *arg1, const void *arg2) { + GBitmap *mark = (GBitmap *)arg1; + const char *text = arg2; + PBL_IF_RECT_ELSE(prv_draw_rt_cell_rect, + prv_draw_rt_cell_round)(ctx, cell_layer, mark, text, is_selected); +} + +#if PBL_ROUND +static void prv_draw_fcc_cell_round( + GContext *ctx, const GRect *cell_layer_bounds, const char *fcc_title, + const char *fcc_number_subtitle, GBitmap *fcc_mark_icon, + bool cell_is_selected, bool cell_is_highlighted) { + const GFont fcc_title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + const int16_t fcc_title_font_cap_padding = 10; + const GFont fcc_number_subtitle_font = fonts_get_system_font(FONT_KEY_GOTHIC_18); + const uint8_t fcc_title_height = fonts_get_font_height(fcc_title_font); + const uint8_t fcc_number_subtitle_height = fonts_get_font_height(fcc_number_subtitle_font); + const GTextOverflowMode text_overflow_mode = GTextOverflowModeFill; + + graphics_context_set_text_color(ctx, cell_is_highlighted ? GColorWhite : GColorBlack); + + // Calculate the container of the FCC cell content and center it within the cell + const int16_t title_and_icon_width = 50; + GRect container_rect = (GRect) { + .size = GSize(title_and_icon_width, fcc_title_height - fcc_title_font_cap_padding) + }; + if (cell_is_selected) { + // Note that we don't subtract the subtitle font's cap padding from the container height + // because it exactly matches the vertical spacing we want between the title and subtitle + container_rect.size.h += fcc_number_subtitle_height; + } + grect_align(&container_rect, cell_layer_bounds, GAlignCenter, true /* clip */); + + // Draw the FCC title in the top left of the container + // We'll reuse this box for the title, subtitle, and icon frames + GRect box = (GRect) { .size = GSize(container_rect.size.w, fcc_title_height) }; + grect_align(&box, &container_rect, GAlignTopLeft, true /* clip */); + box.origin.y -= fcc_title_font_cap_padding; + graphics_draw_text(ctx, fcc_title, fcc_title_font, box, text_overflow_mode, GTextAlignmentLeft, + NULL); + + // If the cell is selected, draw the FCC # subtitle centered at the bottom of the container + if (cell_is_selected) { + const int16_t fcc_number_subtitle_width = 60; + box.size = GSize(fcc_number_subtitle_width, fcc_number_subtitle_height); + // Note that we don't clip when we align the subtitle frame because it is wider than the + // combined width of the title and icon + grect_align(&box, &container_rect, GAlignBottom, false /* clip */); + graphics_draw_text(ctx, fcc_number_subtitle, fcc_number_subtitle_font, box, text_overflow_mode, + GTextAlignmentCenter, NULL); + } + + // Align the FCC mark icon to be drawn in the top right of the container + box.size = fcc_mark_icon->bounds.size; + grect_align(&box, &container_rect, GAlignTopRight, true /* clip */); + prv_draw_mark_with_inversion(ctx, fcc_mark_icon, &box, cell_is_highlighted); +} +#endif + +static void prv_draw_fcc_cell( + GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, + bool is_selected, const void *arg1, const void *arg2) { + const char *title = arg1; + const char *subtitle = arg2; + const bool highlight = menu_cell_layer_is_highlighted(cell_layer); + GBitmap *mark = &cd->fcc_mark; +#if PBL_RECT + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); + // FCC has a mark in the top right of its cell + const GPoint mark_origin = GPoint(119, 7); + const GRect box = (GRect){.origin = mark_origin, .size = mark->bounds.size}; + prv_draw_mark_with_inversion(ctx, mark, &box, highlight); +#else + prv_draw_fcc_cell_round(ctx, &cell_layer->bounds, title, subtitle, mark, + is_selected, highlight); +#endif +} + +static void prv_draw_regulatory_marks_cell( + GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, + bool is_selected, const void *arg1, const void *arg2) { + const GRect *cell_layer_bounds = &cell_layer->bounds; + uint32_t start_idx = (uintptr_t)arg1; + uint32_t num_marks = (uintptr_t)arg2; + // Calculate rect containing generic marks + GSize overall_size = GSize(MARK_PADDING * (num_marks + 1), 0); + for (uint32_t i = 0; i < num_marks; i++) { + const GSize mark_size = cd->regulatory_marks[start_idx + i]->bounds.size; + overall_size.h = MAX(overall_size.h, mark_size.h); + overall_size.w += mark_size.w; + } + GRect regulatory_marks_rect = (GRect) { .size = overall_size }; + // Align the rect based on the display shape + const GAlign alignment = PBL_IF_RECT_ELSE(GAlignLeft, GAlignCenter); + grect_align(®ulatory_marks_rect, cell_layer_bounds, alignment, + /* clip */ true); + // Draw the regulatory marks + GPoint mark_origin = regulatory_marks_rect.origin; + mark_origin.x += MARK_PADDING; + const bool highlight = menu_cell_layer_is_highlighted(cell_layer); + for (uint32_t i = 0; i < num_marks; i++) { + GBitmap *mark = cd->regulatory_marks[start_idx + i]; + // Vertically center the icon in the cell + mark_origin.y = (cell_layer_bounds->size.h - mark->bounds.size.h) / 2; + // Draw the icon and advance the x coordinate for drawing the next icon + mark_origin.x = prv_draw_generic_mark(ctx, mark, mark_origin, + highlight) + MARK_PADDING; + } +} + +static void prv_append_certification_menu(SystemCertificationData *cd, + SystemCertificationMenuItem *item) { + PBL_ASSERTN(item->draw_cell_fn); + cd->menu_items = app_realloc(cd->menu_items, + sizeof(*cd->menu_items) * ++cd->menu_count); + PBL_ASSERTN(cd->menu_items); + cd->menu_items[cd->menu_count - 1] = *item; +} + +static void prv_append_regulatory_compliance_mark(SystemCertificationData *cd, + GBitmap *mark) { + // Determine whether adding this mark overflows the cell, necessitating + // another cell for this mark. + uint16_t mark_width = mark->bounds.size.w; + if (cd->current_regulatory_marks_cell_width + mark_width >= DISP_COLS) { + // Flush the current marks to a cell and start a new one. + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_marks_cell, + .arg1 = (void *)(uintptr_t)cd->current_regulatory_marks_cell_start_idx, + .arg2 = (void *)(uintptr_t)cd->num_regulatory_marks_in_current_cell, + }); + cd->current_regulatory_marks_cell_start_idx += + cd->num_regulatory_marks_in_current_cell; + cd->num_regulatory_marks_in_current_cell = 0; + cd->current_regulatory_marks_cell_width = 0; + } + + cd->regulatory_marks = app_realloc( + cd->regulatory_marks, sizeof(GBitmap *) * ++cd->regulatory_marks_count); + PBL_ASSERTN(cd->regulatory_marks); + cd->regulatory_marks[cd->regulatory_marks_count - 1] = mark; + cd->num_regulatory_marks_in_current_cell++; + cd->current_regulatory_marks_cell_width += mark_width + MARK_PADDING; +} + +static void prv_finished_appending_regulatory_compliance_marks( + SystemCertificationData *cd) { + if (cd->num_regulatory_marks_in_current_cell) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_marks_cell, + .arg1 = (void *)(uintptr_t)cd->current_regulatory_marks_cell_start_idx, + .arg2 = (void *)(uintptr_t)cd->num_regulatory_marks_in_current_cell, + }); + } +} + +static void prv_draw_regulatory_id_cell( + GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, + bool is_selected, const void *arg1, const void *arg2) { + const char *title = arg1; + const char *subtitle = arg2; + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); +} + +static void prv_draw_korea_regulatory_cell( + GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, + bool is_selected, const void *arg1, const void *arg2) { + const char *title = arg1; + const char *subtitle = i18n_get("See details...", title); + menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); + i18n_free(subtitle, title); +} + +static void prv_certification_draw_row_callback(GContext* ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + SettingsSystemData *data = (SettingsSystemData *) context; + PBL_ASSERTN(cell_index->section == 0); + + SystemCertificationData *cd = &data->certification_data; + const bool is_selected = menu_layer_is_index_selected(&data->menu_layer, cell_index); + SystemCertificationMenuItem * const item = &cd->menu_items[cell_index->row]; + item->draw_cell_fn(ctx, cell_layer, cd, is_selected, item->arg1, item->arg2); +} + +static uint16_t prv_certification_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, + void *context) { + SettingsSystemData *data = (SettingsSystemData *) context; + return data->certification_data.menu_count; +} + +static void prv_push_kcc_window(SystemCertificationData *data); + +static void prv_certification_select_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, + void *context) { + SettingsSystemData *data = (SettingsSystemData *) context; + SystemCertificationData *cd = &data->certification_data; + if (cell_index->row < cd->menu_count && + cd->menu_items[cell_index->row].select_cb) { + cd->menu_items[cell_index->row].select_cb(cd); + } +} + +static void prv_certification_window_load(Window *window) { + SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); + + prv_init_status_bar(&data->status_layer, &data->window, i18n_get("Certification", data)); + + SystemCertificationData *cd = &data->certification_data; + *cd = (SystemCertificationData) {}; + + // Load up the assets + gbitmap_init_with_resource(&cd->fcc_mark, RESOURCE_ID_SYSTEM_FCC_MARK); + gbitmap_init_with_resource(&cd->kcc_mark, RESOURCE_ID_SYSTEM_KCC_MARK); + gbitmap_init_with_resource(&cd->ce_mark, RESOURCE_ID_SYSTEM_CE_MARK); + gbitmap_init_with_resource(&cd->weee_mark, RESOURCE_ID_SYSTEM_WEEE_MARK); + gbitmap_init_with_resource(&cd->ukca_mark, RESOURCE_ID_SYSTEM_UKCA_MARK); + gbitmap_init_with_resource(&cd->r_mark, RESOURCE_ID_SYSTEM_R_MARK); + gbitmap_init_with_resource(&cd->t_mark, RESOURCE_ID_SYSTEM_T_MARK); + gbitmap_init_with_resource( + &cd->aus_rcm_mark, RESOURCE_ID_SYSTEM_AUS_RCM_MARK); + gbitmap_init_with_resource( + &cd->nom_nyce_mark, RESOURCE_ID_SYSTEM_NOM_NYCE_MARK); + + // Construct the certification menu + const RegulatoryFlags *flags = prv_get_regulatory_flags(); + + // Add company name + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Company", + .arg2 = prv_get_company_name(), + }); + + // Add model from MFG storage + prv_get_model(cd->model_buffer); + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Product Model", + .arg2 = cd->model_buffer, + }); + + // Add product type + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Product Type", + .arg2 = prv_get_product_type(), + }); + + // Add trademark + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Trademark", + .arg2 = prv_get_trademark(), + }); + + // Add place of origin + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Place of Origin", + .arg2 = prv_get_place_of_origin(), + }); + + // Add DC input + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "DC Input", + .arg2 = prv_get_dc_input(), + }); + + // Add rated voltage + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Rated Voltage", + .arg2 = prv_get_rated_voltage(), + }); + + // Add milliampere-hour + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Milliampere-hour", + .arg2 = prv_get_milliampere_hour(), + }); + + // Add watt-hour + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Watt-hour", + .arg2 = prv_get_watt_hour(), + }); + + if (flags->has_usa_fcc) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_fcc_cell, + .arg1 = "FCC", + .arg2 = prv_get_usa_fcc_id(), + }); + } + if (flags->has_canada_ic) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Canada IC", + .arg2 = prv_get_canada_ic_id(), + }); + } + if (flags->has_canada_ised) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "Canada ISED", + .arg2 = prv_get_canada_ised_id(), + }); + } + if (flags->has_china_cmiit) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "CMIIT ID", + .arg2 = prv_get_china_cmiit_id(), + }); + } + if (flags->has_korea_kcc) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_korea_regulatory_cell, + .arg1 = "South Korea KCC", + .select_cb = prv_push_kcc_window, + }); + } + if (flags->has_mexico_nom_nyce) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_regulatory_id_cell, + .arg1 = "IFETEL", + .arg2 = prv_get_mexico_ifetel_id(), + }); + } + + if (flags->has_korea_kcc) { + prv_append_regulatory_compliance_mark(cd, &cd->kcc_mark); + } + if (flags->has_eu_ce) { + prv_append_regulatory_compliance_mark(cd, &cd->ce_mark); + } + if (flags->has_eu_weee) { + prv_append_regulatory_compliance_mark(cd, &cd->weee_mark); + } + if (flags->has_ukca) { + prv_append_regulatory_compliance_mark(cd, &cd->ukca_mark); + } + if (flags->has_australia_rcm) { + prv_append_regulatory_compliance_mark(cd, &cd->aus_rcm_mark); + } + if (flags->has_mexico_nom_nyce) { + prv_append_regulatory_compliance_mark(cd, &cd->nom_nyce_mark); + } + prv_finished_appending_regulatory_compliance_marks(cd); + + if (flags->has_japan_telec_r) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_rt_cell, + .arg1 = &cd->r_mark, + .arg2 = prv_get_japan_telec_r_id() + }); + } + if (flags->has_japan_telec_t) { + prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { + .draw_cell_fn = prv_draw_rt_cell, + .arg1 = &cd->t_mark, + .arg2 = prv_get_japan_telec_t_id() + }); + } + + // Create the menu + MenuLayer *menu_layer = &data->menu_layer; + GRect bounds = data->window.layer.bounds; + const GEdgeInsets menu_layer_insets = (GEdgeInsets) { + .top = STATUS_BAR_LAYER_HEIGHT, + .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT) + }; + bounds = grect_inset(bounds, menu_layer_insets); + menu_layer_init(menu_layer, &bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = prv_certification_get_num_rows_callback, + .get_cell_height = prv_certification_get_cell_height_callback, + .draw_row = prv_certification_draw_row_callback, + .select_click = prv_certification_select_callback, + }); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_highlight_colors(menu_layer, highlight_bg, gcolor_legible_over(highlight_bg)); + menu_layer_set_click_config_onto_window(menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + + layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); +} + +static void prv_certification_window_unload(Window *window) { + SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); + + menu_layer_deinit(&data->menu_layer); + + gbitmap_deinit(&data->certification_data.fcc_mark); + gbitmap_deinit(&data->certification_data.kcc_mark); + gbitmap_deinit(&data->certification_data.ce_mark); + gbitmap_deinit(&data->certification_data.weee_mark); + gbitmap_deinit(&data->certification_data.ukca_mark); + gbitmap_deinit(&data->certification_data.r_mark); + gbitmap_deinit(&data->certification_data.t_mark); + gbitmap_deinit(&data->certification_data.aus_rcm_mark); + gbitmap_deinit(&data->certification_data.nom_nyce_mark); + + app_free(data->certification_data.regulatory_marks); + app_free(data->certification_data.menu_items); + + prv_deinit_status_bar(&data->status_layer); +} + +static void prv_certification_window_push(SettingsSystemData *data) { + window_init(&data->window, WINDOW_NAME("System Certification")); + window_set_user_data(&data->window, data); + window_set_window_handlers(&data->window, &(WindowHandlers) { + .load = prv_certification_window_load, + .unload = prv_certification_window_unload, + }); + app_window_stack_push(&data->window, true); +} + +static void prv_kcc_window_load(Window *window) { + SystemCertificationData *data = (SystemCertificationData *) window_get_user_data(window); + Layer *window_layer = window_get_root_layer(window); + + const char *title = "South Korea KCC"; + prv_init_status_bar(&data->status_layer, &data->kcc_window, title); + + GRect window_bounds = window_layer->bounds; + + // Calculate the bounding rect for the certification content and center it in the window + GBitmap *bmp = &data->kcc_mark; + const GSize bmp_size = bmp->bounds.size; + const GFont title_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + const GSize title_text_size = GSize(window_bounds.size.w, fonts_get_font_height(title_text_font)); + const GFont info_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_14); + const GSize info_text_size = GSize(window_bounds.size.w, fonts_get_font_height(info_text_font)); + const int16_t vertical_spacing = 3; + GRect certification_rect = (GRect) { + .size = GSize(window_bounds.size.w, + bmp_size.h + title_text_size.h + info_text_size.h + vertical_spacing) + }; + grect_align(&certification_rect, &window_bounds, GAlignCenter, true /* clip */); + + GRect bmp_frame = (GRect) { .size = bmp_size }; + grect_align(&bmp_frame, &certification_rect, GAlignTop, true /* clip */); + bitmap_layer_init(&data->bmp_layer, &bmp_frame); + bitmap_layer_set_bitmap(&data->bmp_layer, bmp); + bitmap_layer_set_compositing_mode(&data->bmp_layer, GCompOpAssign); + layer_add_child(window_layer, bitmap_layer_get_layer(&data->bmp_layer)); + + GRect title_text_frame = (GRect) { .size = title_text_size }; + const int16_t title_text_internal_padding = 5; + title_text_frame.origin.y = bmp_frame.origin.y + bmp_size.h + vertical_spacing + - title_text_internal_padding; + text_layer_init_with_parameters(&data->title_text, &title_text_frame, + title, title_text_font, + GColorBlack, GColorClear, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(window_layer, text_layer_get_layer(&data->title_text)); + + GRect info_text_frame = (GRect) { .size = info_text_size }; + info_text_frame.origin.y = title_text_frame.origin.y + title_text_size.h + vertical_spacing; + text_layer_init_with_parameters(&data->info_text, &info_text_frame, + prv_get_korea_kcc_id(), info_text_font, + GColorBlack, GColorClear, GTextAlignmentCenter, + GTextOverflowModeTrailingEllipsis); + layer_add_child(window_layer, text_layer_get_layer(&data->info_text)); +} + +static void prv_kcc_window_unload(Window *window) { + SystemCertificationData *data = (SystemCertificationData *) window_get_user_data(window); + prv_deinit_status_bar(&data->status_layer); + bitmap_layer_deinit(&data->bmp_layer); + text_layer_deinit(&data->title_text); + text_layer_deinit(&data->info_text); + i18n_free_all(data); +} + +static void prv_push_kcc_window(SystemCertificationData *data) { + window_init(&data->kcc_window, WINDOW_NAME("System KCC")); + window_set_user_data(&data->kcc_window, data); + window_set_window_handlers(&data->kcc_window, &(WindowHandlers) { + .load = prv_kcc_window_load, + .unload = prv_kcc_window_unload, + }); + app_window_stack_push(&data->kcc_window, true); +} + +// Callbacks for the main settings filter list menu. +//////////////////////////////////////////////////// + +static void prv_shutdown_confirm_cb(ClickRecognizerRef recognizer, void *context) { + actionable_dialog_pop((ActionableDialog *) context); + battery_ui_handle_shut_down(); +} + +static void prv_shutdown_back_cb(ClickRecognizerRef recognizer, void *context) { + actionable_dialog_pop((ActionableDialog *) context); +} + +static void prv_shutdown_click_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_SELECT, prv_shutdown_confirm_cb); + window_single_click_subscribe(BUTTON_ID_BACK, prv_shutdown_back_cb); +} + +static void prv_shutdown_cb(void* data) { + ActionableDialog *a_dialog = actionable_dialog_create("Shutdown"); + Dialog *dialog = actionable_dialog_get_dialog(a_dialog); + + actionable_dialog_set_action_bar_type(a_dialog, DialogActionBarConfirm, NULL); + actionable_dialog_set_click_config_provider(a_dialog, prv_shutdown_click_provider); + + dialog_set_text_color(dialog, GColorWhite); + dialog_set_background_color(dialog, GColorCobaltBlue); + dialog_set_text(dialog, i18n_get("Do you want to shut down?", a_dialog)); + dialog_set_icon(dialog, RESOURCE_ID_GENERIC_QUESTION_LARGE); + + i18n_free_all(a_dialog); + + actionable_dialog_push(a_dialog, modal_manager_get_window_stack(ModalPriorityGeneric)); +} + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsSystemData *data = (SettingsSystemData *) context; + i18n_free_all(data); +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsSystemData *data = (SettingsSystemData *) context; + const char *subtitle = NULL; + PBL_ASSERTN(row < SystemMenuItem_Count); + switch (row) { + case SystemMenuItemStationaryToggle: + subtitle = stationary_get_enabled() ? i18n_get("On", data) : i18n_get("Off", data); + break; + case SystemMenuItemShutDown: + case SystemMenuItemInformation: + case SystemMenuItemCertification: + case SystemMenuItemDebugging: + case SystemMenuItemFactoryReset: + case SystemMenuItem_Count: + break; + default: + WTF; + } + menu_cell_basic_draw(ctx, cell_layer, i18n_get(s_item_titles[row], data), subtitle, NULL); +} + +void factory_reset_select_callback(int index, void *context) { + settings_factory_reset_window_push(); +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsSystemData *data = (SettingsSystemData *) context; + + switch (row) { + case SystemMenuItemInformation: + prv_information_window_push(data); + break; + case SystemMenuItemCertification: + prv_certification_window_push(data); + break; + case SystemMenuItemStationaryToggle: + stationary_set_enabled(!stationary_get_enabled()); + break; + case SystemMenuItemShutDown: + launcher_task_add_callback(prv_shutdown_cb, 0); + break; + case SystemMenuItemDebugging: + prv_debugging_interstitial_trigger(data); + break; + case SystemMenuItemFactoryReset: + settings_factory_reset_window_push(); + break; + default: + WTF; + } + settings_menu_reload_data(SettingsMenuItemSystem); +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return SystemMenuItem_Count; +} + +static Window *prv_init(void) { + SettingsSystemData *data = app_malloc_check(sizeof(SettingsSystemData)); + *data = (SettingsSystemData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + }; + + return settings_window_create(SettingsMenuItemSystem, &data->callbacks); +} + +const SettingsModuleMetadata *settings_system_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("System"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/system.h b/src/fw/apps/system/settings/system.h new file mode 100644 index 0000000000..c94db5eafb --- /dev/null +++ b/src/fw/apps/system/settings/system.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_system_get_info(void); diff --git a/src/fw/apps/system/settings/themes.c b/src/fw/apps/system/settings/themes.c new file mode 100644 index 0000000000..889ad685b3 --- /dev/null +++ b/src/fw/apps/system/settings/themes.c @@ -0,0 +1,161 @@ +/* SPDX-FileCopyrightText: 2025 Elad Dvash */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "themes.h" +#include "menu.h" +#include "option_menu.h" +#include "window.h" + +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "applib/graphics/gtypes.h" +#include "applib/graphics/graphics.h" +#include "applib/ui/menu_layer.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/prefs.h" +#include "system/passert.h" +#include "util/size.h" + +#ifdef CONFIG_THEMING + +#define DEFAULT_THEME_HIGHLIGHT_COLOR GColorVividCerulean + +typedef struct ColorDefinition { + const char *name; + const GColor color; +} ColorDefinition; + +static const ColorDefinition s_color_definitions[11] = { + {"Default", GColorClear}, + {"Red", GColorSunsetOrange}, + {"Orange", GColorChromeYellow}, + {"Yellow", GColorYellow}, + {"Green", GColorGreen}, + {"Cyan", GColorCyan}, + {"Light Blue", GColorVividCerulean}, + {"Royal Blue", GColorVeryLightBlue}, + {"Purple", GColorLavenderIndigo}, + {"Magenta", GColorMagenta}, + {"Pink", GColorBrilliantRose}, +}; +static const char* color_names[ARRAY_LENGTH(s_color_definitions)]; +static bool color_names_initialized = false; + +static const char** prv_get_color_names(bool short_list) { + if (!color_names_initialized) { + for (size_t i = 0; i < ARRAY_LENGTH(s_color_definitions); i++) { + color_names[i] = (char*)s_color_definitions[i].name; + } + color_names_initialized = true; + } + return color_names; +} + + + + +static int prv_color_to_index(GColor color, GColor default_color) { + if (color.argb == GColorClear.argb || color.argb == default_color.argb) { + return 0; + } + for (size_t i = 0; i < ARRAY_LENGTH(s_color_definitions); i++) { + GColor selected_color = s_color_definitions[i].color; + if ((uint8_t)(color.argb) == (uint8_t)(selected_color.argb)) { + return i; + } + } + return -1; +} + + +///////////////////////////// +// Unified Accent Color Settings +///////////////////////////// + +static void prv_color_menu_select(OptionMenu *option_menu, int selection, void *context) { + GColor color; + if (selection == 0) { + /* Default option selected -> restore default color. */ + color = DEFAULT_THEME_HIGHLIGHT_COLOR; + } else { + color = s_color_definitions[selection].color; + } + + /* Set the theme highlight color */ + shell_prefs_set_theme_highlight_color(color); + + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_option_menu_selection_will_change(OptionMenu *option_menu, + uint16_t new_row, + uint16_t old_row, + void *context) { + if (new_row == old_row) { + return; + } + GColor color = s_color_definitions[new_row].color; + if (color.argb != GColorClear.argb) { + option_menu_set_highlight_colors(option_menu, color, gcolor_legible_over(color)); + } else { + option_menu_set_highlight_colors(option_menu, DEFAULT_THEME_HIGHLIGHT_COLOR, gcolor_legible_over(DEFAULT_THEME_HIGHLIGHT_COLOR)); + } +} + +static OptionMenu *prv_push_color_menu(void) { + const char *title = i18n_noop("Accent Color"); + int selected = prv_color_to_index(shell_prefs_get_theme_highlight_color(), DEFAULT_THEME_HIGHLIGHT_COLOR); + const char** color_names = prv_get_color_names(false); + const OptionMenuCallbacks callbacks = { + .select = prv_color_menu_select, + .selection_will_change = prv_option_menu_selection_will_change, + }; + if (selected < 0) { + // Invalid color stored - fall back to default instead of crashing + // This can happen if an invalid color was synced from the phone + PBL_LOG_WRN("Invalid menu color, using default"); + selected = 0; + } + OptionMenu * const option_menu = settings_option_menu_create( + title, OptionMenuContentType_SingleLine, selected, &callbacks, + ARRAY_LENGTH(s_color_definitions), true /* icons_enabled */, color_names, NULL); + + if (option_menu) { + if (selected == 0) { + option_menu_set_highlight_colors(option_menu, DEFAULT_THEME_HIGHLIGHT_COLOR, + gcolor_legible_over(DEFAULT_THEME_HIGHLIGHT_COLOR)); + } else { + option_menu_set_highlight_colors(option_menu, s_color_definitions[selected].color, + gcolor_legible_over(s_color_definitions[selected].color)); + } + } + + return option_menu; +} +#endif // CONFIG_THEMING + +static Window *prv_create_color_menu(void) { +#ifdef CONFIG_THEMING + OptionMenu *option_menu = prv_push_color_menu(); + return option_menu ? &option_menu->window : NULL; +#else + WTF; + return NULL; +#endif +} + +static Window *prv_init(void) { + return prv_create_color_menu(); +} + + +const SettingsModuleMetadata *settings_themes_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + /// Title of the Themes Settings submenu in Settings + .name = i18n_noop("Themes"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/themes.h b/src/fw/apps/system/settings/themes.h new file mode 100644 index 0000000000..6d5c90812f --- /dev/null +++ b/src/fw/apps/system/settings/themes.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Elad Dvash */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_themes_get_info(void); diff --git a/src/fw/apps/system/settings/time.c b/src/fw/apps/system/settings/time.c new file mode 100644 index 0000000000..1b4f605778 --- /dev/null +++ b/src/fw/apps/system/settings/time.c @@ -0,0 +1,450 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "menu.h" +#include "option_menu.h" +#include "time.h" +#include "window.h" + +#include "applib/app.h" +#include "applib/applib_resource.h" +#include "applib/fonts/fonts.h" +#include "applib/graphics/text.h" +#include "applib/ui/action_menu_window_private.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/date_selection_window.h" +#include "applib/ui/option_menu_window.h" +#include "applib/ui/time_selection_window.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "util/date.h" +#include "util/size.h" +#include "util/time/time.h" +#include "util/string.h" + +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timezone_database.h" +#include "shell/prefs.h" + +#include + +// 9 (TZ) continents: Africa, America, Antarctica, Asia, Atlantic, Australia, +// Europe, Indian, Pacific +#define NUM_CONTINENTS 9 + +typedef struct { + SettingsCallbacks callbacks; + + MenuLayer menu_layer; + + int hour; + bool is_morning; + + // Timezone data + uint16_t region_count; + uint16_t continent_selected; + uint16_t continent_start[NUM_CONTINENTS + 1]; //!< First region id for the continent + uint16_t continent_end[NUM_CONTINENTS]; //!< Last+1 region id for the continent + + const char **continent_names; + const char **region_names; + char *region_names_buffer; + + ActionMenuConfig action_menu; + + Window *continent_window; + + // Manual time / date picker windows + TimeSelectionWindowData time_picker; + DateSelectionWindowData date_picker; +} SettingsTimeData; + +typedef enum { + TimeRow_TimeSource, + TimeRow_SetTime, + TimeRow_SetDate, + TimeRow_Format, + TimeRow_TimezoneSource, + TimeRow_Timezone, + TimeRowNum, +} TimeRow; + +//! Rows that remain visible when time source is automatic (Set Time and Set Date are hidden). +static const TimeRow s_auto_visible_rows[] = { + TimeRow_TimeSource, + TimeRow_Format, + TimeRow_TimezoneSource, + TimeRow_Timezone, +}; + +//! Map a visible row index to its TimeRow enum value. +//! When time source is automatic, Set Time and Set Date rows are hidden. +static TimeRow prv_row_for_index(uint16_t index) { + if (clock_time_source_is_manual()) { + // All rows visible + return (TimeRow)index; + } + // Automatic mode: map through the reduced row list + if (index < ARRAY_LENGTH(s_auto_visible_rows)) { + return s_auto_visible_rows[index]; + } + return TimeRowNum; +} + +static uint16_t prv_visible_row_count(void) { + if (clock_time_source_is_manual()) { + return TimeRowNum; + } + return ARRAY_LENGTH(s_auto_visible_rows); +} + + +// Timezone Window Setup +//////////////////////////// + +static void prv_format_region_name(char *region_name) { + const size_t string_length = strnlen(region_name, TIMEZONE_NAME_LENGTH); + for (size_t i = 0; i < string_length; i++) { + if (region_name[i] == '_') { + region_name[i] = ' '; + } + } +} + +//! Initialize the continent and region names for the timezone windows +static void prv_init_continent_and_region_names(SettingsTimeData *data) { + const uint16_t region_count = data->region_count = timezone_database_get_region_count(); + char * const region_names_buffer = app_zalloc_check(region_count * TIMEZONE_NAME_LENGTH); + char *cursor = region_names_buffer; + char *last_cursor = cursor; + const char **continent_names = app_zalloc_check(NUM_CONTINENTS * sizeof(char *)); + const char **region_names = app_zalloc_check(region_count * sizeof(char *)); + uint16_t continent_index = 0; + // Iterate through the region IDs to sort out the region IDs into continents. + // We have sorted the region IDs by name, so each continent _will_ be separate from each other. + data->continent_start[continent_index] = 0; + for (uint16_t i = 0; i < region_count; i++, cursor += TIMEZONE_NAME_LENGTH) { + timezone_database_load_region_name(i, cursor); + prv_format_region_name(cursor); + // Split 'Continent/City' into the two parts + const int sep_pos = strcspn(cursor, "/"); + cursor[sep_pos] = '\0'; + // Store pointer to region name + region_names[i] = cursor + sep_pos + 1; + // If the continent is the same as the last entry, keep going as-is + if (strcmp(cursor, last_cursor) == 0) { + data->continent_end[continent_index] = i + 1; + continue; + } + // If the new continent is filtered out, don't create a new continent or update the last + // continent pointer. + if (strcmp(cursor, "Etc") == 0) { // Filter out 'Etc' fake-continent + continue; + } + // Set pointer for the continent name + continent_names[continent_index] = last_cursor; + // Set the new continent name + last_cursor = cursor; + // Set the starting region ID for the new continent. + data->continent_start[++continent_index] = i; + } + // Save the last continent name + continent_names[continent_index] = last_cursor; + + data->region_names_buffer = region_names_buffer; + data->region_names = region_names; + data->continent_names = continent_names; +} + +static char *prv_get_timezone_title(void) { + /// Title of the menu for changing the watch's timezone. + return i18n_noop("Timezone"); +} + +// Timezone Region Menu +///////////////////////// + +static void prv_region_menu_select(OptionMenu *option_menu, int selection, void *context) { + SettingsTimeData *data = ((SettingsOptionMenuData *)context)->context; + const uint16_t region_id = data->continent_start[data->continent_selected] + selection; + clock_set_timezone_by_region_id(region_id); + + const bool continent_animated = false; + app_window_stack_remove(data->continent_window, continent_animated); + const bool region_animated = true; + app_window_stack_remove(&option_menu->window, region_animated); +} + +static void prv_region_menu_push(SettingsTimeData *data) { + const char *title = prv_get_timezone_title(); + const OptionMenuCallbacks callbacks = { + .select = prv_region_menu_select, + }; + const int start_index = data->continent_start[data->continent_selected]; + const int end_index = data->continent_end[data->continent_selected]; + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, OPTION_MENU_CHOICE_NONE, &callbacks, + end_index - start_index, true /* icons_enabled */, &data->region_names[start_index], data); +} + +// Timezone Continent Menu +///////////////////////// + +static void prv_continent_menu_select(OptionMenu *option_menu, int selection, void *context) { + SettingsTimeData *data = ((SettingsOptionMenuData *)context)->context; + data->continent_selected = selection; + prv_region_menu_push(data); +} + +static void prv_continent_menu_push(SettingsTimeData *data) { + const char *title = prv_get_timezone_title(); + const OptionMenuCallbacks callbacks = { + .select = prv_continent_menu_select, + }; + OptionMenu * const continent_menu = settings_option_menu_push( + title, OptionMenuContentType_SingleLine, OPTION_MENU_CHOICE_NONE, &callbacks, NUM_CONTINENTS, + false /* icons_enabled */, data->continent_names, data); + data->continent_window = &continent_menu->window; +} + +// 24h Switch +///////////////////////// + +static void prv_cycle_clock_style(void) { + clock_set_24h_style(!clock_is_24h_style()); +} + +static void prv_cycle_clock_time_source(void) { + const bool was_manual = clock_time_source_is_manual(); + clock_set_manual_time_source(!was_manual); + + if (was_manual) { + // Switching from manual to automatic: ask the phone to send its current time + clock_request_time_from_phone(); + } +} + +static void prv_cycle_clock_timezone_source(void) { + clock_set_manual_timezone_source(!clock_timezone_source_is_manual()); + + if (!clock_timezone_source_is_manual()) { + clock_set_timezone_by_region_id(shell_prefs_get_automatic_timezone_id()); + } +} + +// Set Time +///////////////////////// + +static void prv_time_picker_complete(TimeSelectionWindowData *picker, void *context) { + // Build a new UTC timestamp: keep today's local date, update hours/minutes. + // Seconds are reset to zero when manually setting the time. + struct tm now; + clock_get_time_tm(&now); + now.tm_hour = picker->time_data.hour; + now.tm_min = picker->time_data.minute; + now.tm_sec = 0; + // mktime interprets struct tm as local time and returns a UTC epoch + time_t new_utc = mktime(&now); + clock_set_time(new_utc); + + const bool animated = true; + app_window_stack_remove(&picker->window, animated); +} + +static void prv_time_picker_push(SettingsTimeData *data) { + TimeSelectionWindowData *picker = &data->time_picker; + + // Deinit any previous state before re-initializing (safe on zeroed struct) + time_selection_window_deinit(picker); + + const TimeSelectionWindowConfig config = { + .label = i18n_noop("Set Time"), + .color = PBL_IF_COLOR_ELSE(GColorJaegerGreen, GColorBlack), + .range = { .update = true, .enabled = false }, + .callback = { + .update = true, + .complete = prv_time_picker_complete, + .context = data, + }, + }; + time_selection_window_init(picker, &config); + time_selection_window_set_to_current_time(picker); + + app_window_stack_push(&picker->window, true /* animated */); +} + +// Set Date +///////////////////////// + +static void prv_date_picker_complete(DateSelectionWindowData *picker, void *context) { + // Build a new UTC timestamp: keep today's local time-of-day (hour/min/sec), update year/month/day + struct tm now; + clock_get_time_tm(&now); + now.tm_year = picker->date.year; + now.tm_mon = picker->date.month; + now.tm_mday = picker->date.day; + // mktime normalises any out-of-range fields and converts local → UTC + time_t new_utc = mktime(&now); + clock_set_time(new_utc); + + const bool animated = true; + app_window_stack_remove(&picker->window, animated); +} + +static void prv_date_picker_push(SettingsTimeData *data) { + DateSelectionWindowData *picker = &data->date_picker; + + // Deinit any previous state before re-initializing (safe on zeroed struct) + date_selection_window_deinit(picker); + + date_selection_window_init(picker, i18n_noop("Set Date"), + PBL_IF_COLOR_ELSE(GColorJaegerGreen, GColorBlack), + prv_date_picker_complete, data); + date_selection_window_set_to_current_date(picker); + + app_window_stack_push(&picker->window, true /* animated */); +} + +// Date & Time Menu +//////////////////////////// +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsTimeData *data = (SettingsTimeData*) context; + switch (prv_row_for_index(row)) { + case TimeRow_TimeSource: + // Toggle automatic / manual time + prv_cycle_clock_time_source(); + settings_menu_reload_data(SettingsMenuItemDateTime); + return; + case TimeRow_SetTime: + prv_time_picker_push(data); + return; // mark dirty happens via window unload callback + case TimeRow_SetDate: + prv_date_picker_push(data); + return; // mark dirty happens via window unload callback + case TimeRow_Format: + // Set Time Display + prv_cycle_clock_style(); + break; + case TimeRow_TimezoneSource: + // Time settings (automatic / manual) + prv_cycle_clock_timezone_source(); + break; + case TimeRow_Timezone: + // Set Timezone Region + PBL_ASSERTN(clock_timezone_source_is_manual()); + prv_continent_menu_push(data); + break; + default: + break; + } + settings_menu_mark_dirty(SettingsMenuItemDateTime); +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsTimeData *data = (SettingsTimeData*) context; + + const char *title = NULL; + const char *subtitle = NULL; + char current_timezone_region[TIMEZONE_NAME_LENGTH]; + char time_buf[TIME_STRING_TIME_LENGTH]; + char date_buf[16]; + + switch (prv_row_for_index(row)) { + case TimeRow_TimeSource: { + title = i18n_noop("Time Source"); + subtitle = clock_time_source_is_manual() ? i18n_noop("Manual") : + i18n_noop("Automatic"); + break; + } + case TimeRow_SetTime: { + title = i18n_noop("Set Time"); + struct tm local_now; + clock_get_time_tm(&local_now); + clock_format_time(time_buf, sizeof(time_buf), + local_now.tm_hour, local_now.tm_min, true); + subtitle = time_buf; + break; + } + case TimeRow_SetDate: { + title = i18n_noop("Set Date"); + struct tm local_now; + clock_get_time_tm(&local_now); + strftime(date_buf, sizeof(date_buf), "%Y-%m-%d", &local_now); + subtitle = date_buf; + break; + } + case TimeRow_Format: { + title = i18n_noop("Time Format"); + subtitle = clock_is_24h_style() ? i18n_noop("24h") : i18n_noop("12h"); + break; + } + case TimeRow_TimezoneSource: { + title = i18n_noop("Timezone Source"); + subtitle = clock_timezone_source_is_manual() ? i18n_noop("Manual") : + i18n_noop("Automatic"); + break; + } + case TimeRow_Timezone: { + title = i18n_noop("Timezone"); + clock_get_timezone_region(current_timezone_region, TIMEZONE_NAME_LENGTH); + subtitle = current_timezone_region; + break; + } + default: + break; + } + + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static void prv_selection_will_change_cb(SettingsCallbacks *context, uint16_t *new_row, + uint16_t old_row) { + if (!clock_timezone_source_is_manual() && + prv_row_for_index(*new_row) == TimeRow_Timezone) { + *new_row = old_row; + } +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return prv_visible_row_count(); +} + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsTimeData *data = (SettingsTimeData*) context; + i18n_free_all(data); + app_free(data->continent_names); + app_free(data->region_names); + app_free(data->region_names_buffer); + time_selection_window_deinit(&data->time_picker); + date_selection_window_deinit(&data->date_picker); + app_free(data); +} + +static Window *prv_init(void) { + SettingsTimeData *data = app_malloc_check(sizeof(*data)); + *data = (SettingsTimeData){}; + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + .selection_will_change = prv_selection_will_change_cb, + }; + + prv_init_continent_and_region_names(data); + + return settings_window_create(SettingsMenuItemDateTime, &data->callbacks); +} + +const SettingsModuleMetadata *settings_time_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Date & Time"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/time.h b/src/fw/apps/system/settings/time.h new file mode 100644 index 0000000000..9f81e1ce3f --- /dev/null +++ b/src/fw/apps/system/settings/time.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_time_get_info(void); diff --git a/src/fw/apps/system/settings/timeline.c b/src/fw/apps/system/settings/timeline.c new file mode 100644 index 0000000000..21a83e75e3 --- /dev/null +++ b/src/fw/apps/system/settings/timeline.c @@ -0,0 +1,297 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "timeline.h" +#include "option_menu.h" +#include "window.h" + +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "applib/graphics/graphics.h" +#include "applib/ui/menu_layer.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/prefs.h" +#include "system/passert.h" +#include "util/size.h" + +typedef enum TimelineSettingsVersion { + //! Initial version or never opened + TimelineSettingsVersion_InitialVersion = 0, + //! 4.0 UX with Timeline Quick View (code named Peek) + TimelineSettingsVersion_UX4WithQuickView = 1, + + TimelineSettingsVersionCount, + //! TimelineSettingsVersion is an increasing version number. TimelineSettingsVersionCurrent must + //! not decrement. This should ensure that the current version is always the latest. + TimelineSettingsVersionCurrent = TimelineSettingsVersionCount - 1, +} TimelineSettingsVersion; + +typedef struct SettingsTimelinePeekData { + SettingsCallbacks callbacks; + GFont info_font; +} SettingsTimelinePeekData; + +typedef enum TimelinePeekMenuIndex { + TimelinePeekMenuIndex_Toggle, +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED + TimelinePeekMenuIndex_UnsupportedFaces, +#endif + TimelinePeekMenuIndex_Timing, + + TimelinePeekMenuIndexCount, + TimelinePeekMenuIndexEnabledCount = TimelinePeekMenuIndexCount, + TimelinePeekMenuIndexDisabledCount = (TimelinePeekMenuIndex_Toggle + 1), +} TimelinePeekMenuIndex; + +typedef enum PeekBeforeTimingMenuIndex { + PeekBeforeTimingMenuIndex_StartTime, + PeekBeforeTimingMenuIndex_5Min, + PeekBeforeTimingMenuIndex_10Min, + PeekBeforeTimingMenuIndex_15Min, + PeekBeforeTimingMenuIndex_30Min, + + PeekBeforeTimingMenuIndexCount, + PeekBeforeTimingMenuIndexDefault = PeekBeforeTimingMenuIndex_10Min, +} PeekBeforeTimingMenuIndex; + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +typedef enum UnsupportedFacesMenuIndex { + UnsupportedFacesMenuIndex_Nothing, + UnsupportedFacesMenuIndex_ShiftUp, + UnsupportedFacesMenuIndex_SquishUp, + + UnsupportedFacesMenuIndexCount, +} UnsupportedFacesMenuIndex; +#endif + +static const char *s_before_time_strings[PeekBeforeTimingMenuIndexCount] = { + /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. + i18n_noop("Start Time"), + /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. + i18n_noop("5 Min Before"), + /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. + i18n_noop("10 Min Before"), + /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. + i18n_noop("15 Min Before"), + /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. + i18n_noop("30 Min Before"), +}; + +static uint16_t s_before_time_values[PeekBeforeTimingMenuIndexCount] = { + 0, 5, 10, 15, 30, +}; + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +static const char *s_unsupported_faces_strings[UnsupportedFacesMenuIndexCount] = { + /// Shows up in the Timeline settings as an "Unsupported Faces" submenu option. + i18n_noop("Overlay"), + /// Shows up in the Timeline settings as an "Unsupported Faces" submenu option. + i18n_noop("Shift up"), + /// Shows up in the Timeline settings as an "Unsupported Faces" submenu option. + i18n_noop("Squish up"), +}; + +static const TimelinePeekUnsupportedFaceMode + s_unsupported_faces_values[UnsupportedFacesMenuIndexCount] = { + TimelinePeekUnsupportedFaceMode_None, + TimelinePeekUnsupportedFaceMode_ShiftUp, + TimelinePeekUnsupportedFaceMode_SquishUp, +}; +#endif + +static PeekBeforeTimingMenuIndex prv_before_time_min_to_index(unsigned int before_time_m) { + if (before_time_m == 0) { + return PeekBeforeTimingMenuIndex_StartTime; + } else if (before_time_m <= 5) { + return PeekBeforeTimingMenuIndex_5Min; + } else if (before_time_m <= 10) { + return PeekBeforeTimingMenuIndex_10Min; + } else if (before_time_m <= 15) { + return PeekBeforeTimingMenuIndex_15Min; + } else if (before_time_m <= 30) { + return PeekBeforeTimingMenuIndex_30Min; + } + return PeekBeforeTimingMenuIndexDefault; +} + +static void prv_before_time_menu_select(OptionMenu *option_menu, int selection, void *context) { + timeline_peek_prefs_set_before_time(s_before_time_values[selection]); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_push_before_time_menu(SettingsTimelinePeekData *data) { + /// Shows up in the Timeline settings as the title for the "Timing" submenu window. + const char *title = i18n_noop("Timing"); + const int selected = prv_before_time_min_to_index(timeline_peek_prefs_get_before_time()); + const OptionMenuCallbacks callbacks = { + .select = prv_before_time_menu_select, + }; + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, selected, &callbacks, + ARRAY_LENGTH(s_before_time_strings), true /* icons_enabled */, s_before_time_strings, data); +} + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +static UnsupportedFacesMenuIndex prv_unsupported_face_mode_to_index( + TimelinePeekUnsupportedFaceMode mode) { + for (unsigned int i = 0; i < ARRAY_LENGTH(s_unsupported_faces_values); i++) { + if (s_unsupported_faces_values[i] == mode) { + return i; + } + } + return UnsupportedFacesMenuIndex_Nothing; +} + +static void prv_unsupported_faces_menu_select(OptionMenu *option_menu, int selection, + void *context) { + timeline_peek_prefs_set_unsupported_face_mode(s_unsupported_faces_values[selection]); + app_window_stack_remove(&option_menu->window, true /* animated */); +} + +static void prv_push_unsupported_faces_menu(SettingsTimelinePeekData *data) { + /// Shows up in the Timeline settings as the title for unsupported watchface behavior. + const char *title = i18n_noop("Unsupported Faces"); + const int selected = prv_unsupported_face_mode_to_index( + timeline_peek_prefs_get_unsupported_face_mode()); + const OptionMenuCallbacks callbacks = { + .select = prv_unsupported_faces_menu_select, + }; + settings_option_menu_push( + title, OptionMenuContentType_SingleLine, selected, &callbacks, + ARRAY_LENGTH(s_unsupported_faces_strings), true /* icons_enabled */, + s_unsupported_faces_strings, data); +} +#endif + +static void prv_deinit_cb(SettingsCallbacks *context) { + i18n_free_all(context); + app_free(context); +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return timeline_peek_prefs_get_enabled() ? TimelinePeekMenuIndexEnabledCount : + TimelinePeekMenuIndexDisabledCount; +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsTimelinePeekData *data = (SettingsTimelinePeekData *)context; + const char *title = NULL; + const char *subtitle = NULL; + + switch ((TimelinePeekMenuIndex)row) { + case TimelinePeekMenuIndex_Toggle: + /// Shows up in the Timeline settings as a toggle-able "Quick View" item. + title = i18n_noop("Quick View"); + /// Shows up in the Timeline settings as the status under the "Quick View" toggle. + subtitle = timeline_peek_prefs_get_enabled() ? i18n_noop("On") : + /// Shows up in the Timeline settings as the status under the "Quick View" toggle. + i18n_noop("Off"); + break; + case TimelinePeekMenuIndex_Timing: + /// Shows up in the Timeline settings as the title for the menu item that controls the + /// timing for when to begin showing the peek for an event. + title = i18n_noop("Timing"); + subtitle = s_before_time_strings[ + prv_before_time_min_to_index(timeline_peek_prefs_get_before_time())]; + break; +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED + case TimelinePeekMenuIndex_UnsupportedFaces: + /// Shows up in the Timeline settings for watchfaces that do not support Quick View. + title = i18n_noop("Unsupported Faces"); + subtitle = s_unsupported_faces_strings[prv_unsupported_face_mode_to_index( + timeline_peek_prefs_get_unsupported_face_mode())]; + break; +#endif + case TimelinePeekMenuIndexCount: + break; + } + + PBL_ASSERTN(title); + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + SettingsTimelinePeekData *data = (SettingsTimelinePeekData *)context; + switch ((TimelinePeekMenuIndex)row) { + case TimelinePeekMenuIndex_Toggle: + timeline_peek_prefs_set_enabled(!timeline_peek_prefs_get_enabled()); + goto done; + case TimelinePeekMenuIndex_Timing: + prv_push_before_time_menu(data); + goto done; +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED + case TimelinePeekMenuIndex_UnsupportedFaces: + prv_push_unsupported_faces_menu(data); + goto done; +#endif + case TimelinePeekMenuIndexCount: + break; + } + WTF; +done: + settings_menu_reload_data(SettingsMenuItemTimeline); +} + +static Window *prv_create_settings_window(void) { + SettingsTimelinePeekData *data = app_malloc_check(sizeof(*data)); + + *data = (SettingsTimelinePeekData) { + .callbacks = { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + }, + .info_font = fonts_get_system_font(FONT_KEY_GOTHIC_18), + }; + + return settings_window_create(SettingsMenuItemTimeline, &data->callbacks); +} + +static void prv_push_settings_window(ClickRecognizerRef recognizer, void *context) { + PBL_ASSERTN(context); + expandable_dialog_pop(context); + Window *window = prv_create_settings_window(); + app_window_stack_push(window, true /* animated */); +} + +static Window *prv_create_first_use_dialog(void) { + const void *i18n_owner = prv_create_first_use_dialog; // Use this function as the i18n owner + /// Title for the Timeline Quick View first use dialog. + const char *header = i18n_get("Quick View", i18n_owner); + /// Help text for the Timeline Quick View first use dialog. + const char *text = i18n_get("Appears on your watchface when an event is about to start.", + i18n_owner); + ExpandableDialog *expandable_dialog = expandable_dialog_create_with_params( + WINDOW_NAME("Timeline Quick View First Use"), RESOURCE_ID_SUNNY_DAY_TINY, text, GColorBlack, + PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite), NULL, RESOURCE_ID_ACTION_BAR_ICON_CHECK, + prv_push_settings_window); + expandable_dialog_set_header(expandable_dialog, header); +#if PBL_ROUND + expandable_dialog_set_header_font(expandable_dialog, + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); +#endif + i18n_free_all(i18n_owner); + return &expandable_dialog->dialog.window; +} + +static Window *prv_init(void) { + const uint32_t version = timeline_prefs_get_settings_opened(); + timeline_prefs_set_settings_opened(TimelineSettingsVersionCurrent); + if (version == TimelineSettingsVersion_InitialVersion) { + return prv_create_first_use_dialog(); + } else { + return prv_create_settings_window(); + } +} + +const SettingsModuleMetadata *settings_timeline_get_info(void) { + static const SettingsModuleMetadata s_module_info = { + .name = i18n_noop("Timeline"), + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/timeline.h b/src/fw/apps/system/settings/timeline.h new file mode 100644 index 0000000000..5f10c84004 --- /dev/null +++ b/src/fw/apps/system/settings/timeline.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_timeline_get_info(void); diff --git a/src/fw/apps/system/settings/vibe_patterns.c b/src/fw/apps/system/settings/vibe_patterns.c new file mode 100644 index 0000000000..835853116d --- /dev/null +++ b/src/fw/apps/system/settings/vibe_patterns.c @@ -0,0 +1,320 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "vibe_patterns.h" +#include "window.h" + +#include "applib/ui/number_window.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/speaker/speaker_service.h" +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_intensity.h" +#include "pbl/services/vibes/vibe_score.h" +#include "pbl/services/vibes/vibe_score_info.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/string.h" + +#include +#include + +typedef enum VibeSettingsRow { +#ifdef CONFIG_SPEAKER + VibeSettingsRow_MuteSpeaker = 0, + VibeSettingsRow_SpeakerVolume, + VibeSettingsRow_Notifications, +#else + VibeSettingsRow_Notifications = 0, +#endif + VibeSettingsRow_PhoneCalls, + VibeSettingsRow_Alarms, + VibeSettingsRow_Hourly, + VibeSettingsRow_OnDisconnect, + VibeSettingsRow_System, + VibeSettingsRow_Count, +} VibeSettingsRow; + +typedef struct SettingsVibePatternsData { + SettingsCallbacks callbacks; + unsigned int toggled_vibes_mask; +#ifdef CONFIG_SPEAKER + char volume_subtitle[8]; // "100%" + NUL +#endif +} SettingsVibePatternsData; + +static void prv_deinit_cb(SettingsCallbacks *context) { + SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; + i18n_free_all(data); + app_free(data); +} + +static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, + const Layer *cell_layer, uint16_t row, bool selected) { + SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; + + const char *title = NULL; + const char *subtitle = NULL; + + VibeClient client = VibeClient_Notifications; + switch (row) { +#ifdef CONFIG_SPEAKER + case VibeSettingsRow_MuteSpeaker: { + title = i18n_noop("Mute Speaker"); + subtitle = alerts_preferences_get_speaker_muted() ? i18n_noop("On") : i18n_noop("Off"); + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), + i18n_get(subtitle, data), NULL); + return; + } + case VibeSettingsRow_SpeakerVolume: { + title = i18n_noop("Volume"); + snprintf(data->volume_subtitle, sizeof(data->volume_subtitle), "%u%%", + alerts_preferences_get_speaker_volume()); + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), + data->volume_subtitle, NULL); + return; + } +#endif + case VibeSettingsRow_Notifications: { + title = i18n_noop("Notifications"); + client = VibeClient_Notifications; + break; + } + case VibeSettingsRow_PhoneCalls: { + title = i18n_noop("Incoming Calls"); + client = VibeClient_PhoneCalls; + break; + } + case VibeSettingsRow_Alarms: { + title = i18n_noop("Alarms"); + client = VibeClient_Alarms; + break; + } + case VibeSettingsRow_Hourly: { + title = i18n_noop("Hourly Notice"); + client = VibeClient_Hourly; + break; + } + case VibeSettingsRow_OnDisconnect: { + title = i18n_noop("On Disconnect"); + client = VibeClient_OnDisconnect; + break; + } + case VibeSettingsRow_System: { + /// Refers to the class of all non-score vibes, e.g. 3rd party app vibes + title = i18n_noop("System"); + + const VibeIntensity current_system_default_vibe_intensity = vibe_intensity_get(); + subtitle = vibe_intensity_get_string_for_intensity(current_system_default_vibe_intensity); + break; + } + default: { + WTF; + } + } + + // We need to set the subtitle to the name of a vibe score if it's NULL at this point + if (!subtitle) { + subtitle = vibe_score_info_get_name(alerts_preferences_get_vibe_score_for_client(client)); + if (subtitle && IS_EMPTY_STRING(subtitle)) { + subtitle = NULL; + } + } + menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); +} + +static void prv_selection_changed_cb(SettingsCallbacks *context, uint16_t new_row, + uint16_t old_row) { + vibes_cancel(); + VibeScore *score; + switch (new_row) { +#ifdef CONFIG_SPEAKER + case VibeSettingsRow_MuteSpeaker: + case VibeSettingsRow_SpeakerVolume: { + // No vibe preview — this row controls a non-vibe setting. + return; + } +#endif + case VibeSettingsRow_Notifications: { + score = vibe_client_get_score(VibeClient_Notifications); + break; + } + case VibeSettingsRow_PhoneCalls: { + score = vibe_client_get_score(VibeClient_PhoneCalls); + break; + } + case VibeSettingsRow_Alarms: { + score = vibe_client_get_score(VibeClient_Alarms); + break; + } + case VibeSettingsRow_Hourly: { + score = vibe_client_get_score(VibeClient_Hourly); + break; + } + case VibeSettingsRow_OnDisconnect: { + score = vibe_client_get_score(VibeClient_OnDisconnect); + break; + } + case VibeSettingsRow_System: { + // Vibe a short pulse so the user can feel the current system default vibe intensity + vibes_short_pulse(); + // Just return because the remainder of this function only applies to vibe scores + return; + } + default: + WTF; + } + if (!score) { + PBL_LOG_ERR("Null VibeScore!"); + return; + } + vibe_score_do_vibe(score); + vibe_score_destroy(score); +} + +#ifdef CONFIG_SPEAKER +static void prv_volume_window_selected(NumberWindow *number_window, void *context) { + const int32_t value = number_window_get_value(number_window); + alerts_preferences_set_speaker_volume((uint8_t)value); + speaker_service_handle_audio_prefs_changed(); + settings_menu_mark_dirty(SettingsMenuItemVibrations); + app_window_stack_remove(&number_window->window, true /* animated */); +} + +static void prv_push_volume_window(SettingsVibePatternsData *data) { + NumberWindow *number_window = number_window_create( + i18n_get("Volume", data), + (NumberWindowCallbacks) { + .selected = prv_volume_window_selected, + }, + data); + if (!number_window) { + return; + } + number_window_set_min(number_window, 0); + number_window_set_max(number_window, 100); + number_window_set_step_size(number_window, 5); + number_window_set_value(number_window, + (int32_t)alerts_preferences_get_speaker_volume()); + app_window_stack_push(&number_window->window, true /* animated */); +} +#endif + +static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { + vibes_cancel(); + + VibeClient client; + switch (row) { +#ifdef CONFIG_SPEAKER + case VibeSettingsRow_MuteSpeaker: { + const bool new_muted = !alerts_preferences_get_speaker_muted(); + alerts_preferences_set_speaker_muted(new_muted); + speaker_service_handle_audio_prefs_changed(); + settings_menu_mark_dirty(SettingsMenuItemVibrations); + return; + } + case VibeSettingsRow_SpeakerVolume: { + prv_push_volume_window((SettingsVibePatternsData *)context); + return; + } +#endif + case VibeSettingsRow_Notifications: { + client = VibeClient_Notifications; + break; + } + case VibeSettingsRow_PhoneCalls: { + client = VibeClient_PhoneCalls; + break; + } + case VibeSettingsRow_Alarms: { + client = VibeClient_Alarms; + break; + } + case VibeSettingsRow_Hourly: { + client = VibeClient_Hourly; + break; + } + case VibeSettingsRow_OnDisconnect: { + client = VibeClient_OnDisconnect; + break; + } + case VibeSettingsRow_System: { + const VibeIntensity current_system_default_vibe_intensity = vibe_intensity_get(); + const VibeIntensity next_system_default_vibe_intensity = + vibe_intensity_cycle_next(current_system_default_vibe_intensity); + + // Set the next system default vibe intensity and vibe a short pulse so the user can feel it + vibe_intensity_set(next_system_default_vibe_intensity); + alerts_preferences_set_vibe_intensity(next_system_default_vibe_intensity); + vibes_short_pulse(); + + settings_menu_mark_dirty(SettingsMenuItemVibrations); + + // Just return because the remainder of this function only applies to vibe scores + return; + } + default: + WTF; + } + + VibeScoreId current_vibe_score = alerts_preferences_get_vibe_score_for_client(client); + VibeScoreId new_vibe_score = vibe_score_info_cycle_next(client, current_vibe_score); + alerts_preferences_set_vibe_score_for_client(client, new_vibe_score); + settings_menu_mark_dirty(SettingsMenuItemVibrations); + VibeScore *score = vibe_client_get_score(client); + if (!score) { + return; + } + vibe_score_do_vibe(score); + vibe_score_destroy(score); +} + +static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { + return VibeSettingsRow_Count; +} + +static void prv_expand_cb(SettingsCallbacks *context) { + SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; + + // window is visible again, remind user which vibe pattern they're on + int16_t current_row = settings_menu_get_selected_row(SettingsMenuItemVibrations); + prv_selection_changed_cb(&data->callbacks, current_row, 0); + + settings_menu_mark_dirty(SettingsMenuItemVibrations); +} + +static void prv_hide_cb(SettingsCallbacks *context) { + vibes_cancel(); +} + +static Window *prv_init(void) { + SettingsVibePatternsData *data = app_zalloc_check(sizeof(SettingsVibePatternsData)); + + data->callbacks = (SettingsCallbacks) { + .deinit = prv_deinit_cb, + .draw_row = prv_draw_row_cb, + .selection_changed = prv_selection_changed_cb, + .select_click = prv_select_click_cb, + .num_rows = prv_num_rows_cb, + .expand = prv_expand_cb, + .hide = prv_hide_cb, + }; + + return settings_window_create(SettingsMenuItemVibrations, &data->callbacks); +} + +const SettingsModuleMetadata *settings_vibe_patterns_get_info(void) { + static const SettingsModuleMetadata s_module_info = { +#ifdef CONFIG_SPEAKER + .name = i18n_noop("Sounds & Haptics"), +#else + .name = i18n_noop("Vibrations"), +#endif + .init = prv_init, + }; + + return &s_module_info; +} diff --git a/src/fw/apps/system/settings/vibe_patterns.h b/src/fw/apps/system/settings/vibe_patterns.h new file mode 100644 index 0000000000..653519f124 --- /dev/null +++ b/src/fw/apps/system/settings/vibe_patterns.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +const SettingsModuleMetadata *settings_vibe_patterns_get_info(void); diff --git a/src/fw/apps/system/settings/window.c b/src/fw/apps/system/settings/window.c new file mode 100644 index 0000000000..70bf7dc978 --- /dev/null +++ b/src/fw/apps/system/settings/window.c @@ -0,0 +1,332 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "settings.h" +#include "activity_tracker.h" +#include "system.h" +#include "bluetooth.h" +#include "display.h" +#include "menu.h" +#include "notifications.h" +#include "quick_launch.h" +#include "remote.h" +#include "time.h" +#include "window.h" + +#include "applib/app.h" +#include "applib/battery_state_service.h" +#include "applib/event_service_client.h" +#include "applib/fonts/fonts.h" +#include "applib/ui/menu_layer.h" +#include "applib/ui/option_menu_window.h" +#include "applib/ui/ui.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/fw_reset.h" +#include "process_management/app_manager.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/system_task.h" +#include "system/bootbits.h" +#include "system/passert.h" +#include "shell/prefs.h" + +#include +#include + +typedef struct SettingsData { + Window window; + StatusBarLayer status_layer; + MenuLayer menu_layer; + + SettingsMenuItem current_category; //!< SettingsMenuItem_Invalid if not currently in a category. + + //! Optional explicit title (used by nested submenus). When NULL, the title + //! falls back to the category's status name. + const char *title_override; + + //! Previous app-state user_data value stashed at window creation; restored + //! on unload so nested settings windows don't leak pointers to freed data. + void *prev_app_user_data; + + const char *title; + SettingsCallbacks *callbacks; + + ClickConfigProvider menu_layer_click_config; //! HACK: Used to register a back click. + + EventServiceInfo pref_change_event_info; //!< Subscription for pref change notifications +} SettingsData; + + +// Pref change handler +/////////////////////// + +static void prv_pref_change_handler(PebbleEvent *event, void *context) { + SettingsData *data = context; + // Refresh the menu when any pref changes + layer_mark_dirty(menu_layer_get_layer(&data->menu_layer)); +} + +// Filter category helpers +////////////////////////// + +static SettingsCallbacks *prv_get_current_callbacks(SettingsData *data) { + PBL_ASSERTN(data && data->callbacks); + return data->callbacks; +} + +// Menu appearance helpers +////////////////////////// + +static void prv_set_sub_menu_colors(GContext *ctx, const Layer *cell_layer, bool highlight) { + if (highlight) { + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + graphics_context_set_fill_color(ctx, highlight_bg); + graphics_context_set_text_color(ctx, gcolor_legible_over(highlight_bg)); + } else { + graphics_context_set_fill_color(ctx, GColorWhite); + graphics_context_set_text_color(ctx, GColorBlack); + } + graphics_fill_rect(ctx, &cell_layer->bounds); +} + +// Menu Layer Handling +////////////////////// + +static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { + SettingsData *data = context; + + const uint16_t row = cell_index->row; + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->select_click) { + callbacks->select_click(callbacks, row); + } +} + +static void prv_selection_changed_callback(MenuLayer *menu_layer, MenuIndex new_index, + MenuIndex old_index, void *context) { + SettingsData *data = context; + + const uint16_t new_row = new_index.row; + const uint16_t old_row = old_index.row; + + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->selection_changed) { + callbacks->selection_changed(callbacks, new_row, old_row); + } +} + +static void prv_selection_will_change_callback(MenuLayer *menu_layer, MenuIndex *new_index, + MenuIndex old_index, void *context) { + SettingsData *data = context; + + const uint16_t old_row = old_index.row; + + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->selection_will_change) { + callbacks->selection_will_change(callbacks, &new_index->row, old_row); + } +} + +static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, + MenuIndex *cell_index, void *context) { + SettingsData *data = context; + + uint16_t row = cell_index->row; + const uint16_t section = cell_index->section; + PBL_ASSERTN(section < SettingsMenuItem_Count); + + bool highlight = menu_cell_layer_is_highlighted(cell_layer); + + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + prv_set_sub_menu_colors(ctx, cell_layer, highlight); + if (callbacks->draw_row) { + const bool is_selected = menu_cell_layer_is_highlighted(cell_layer); + callbacks->draw_row(callbacks, ctx, cell_layer, row, is_selected); + } +} + +static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, + uint16_t section_index, void *context) { + PBL_ASSERTN(section_index < SettingsMenuItem_Count); + SettingsData *data = context; + + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + return callbacks->num_rows ? callbacks->num_rows(callbacks) : (uint16_t)0; +} + +static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, + MenuIndex *cell_index, void *context) { + PBL_ASSERTN(cell_index->section < SettingsMenuItem_Count); + SettingsData *data = context; + + const uint16_t row = cell_index->row; + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + const bool is_selected = menu_layer_is_index_selected(menu_layer, cell_index); + return (callbacks->row_height) ? + callbacks->row_height(callbacks, row, is_selected) : + PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), + (is_selected ? MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : + MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); +} + +// Settings Window: +//////////////////////// + +static void prv_settings_window_load(Window *window) { + SettingsData *data = window_get_user_data(window); + + StatusBarLayer *status_layer = &data->status_layer; + status_bar_layer_init(status_layer); + const char *title = data->title_override + ? data->title_override + : settings_menu_get_status_name(data->current_category); + status_bar_layer_set_title(status_layer, i18n_get(title, data), false, false); + status_bar_layer_set_colors(status_layer, GColorWhite, GColorBlack); + status_bar_layer_set_separator_mode(status_layer, OPTION_MENU_STATUS_SEPARATOR_MODE); + layer_add_child(&data->window.layer, status_bar_layer_get_layer(status_layer)); + + GRect bounds = grect_inset(data->window.layer.bounds, (GEdgeInsets) { + .top = STATUS_BAR_LAYER_HEIGHT, + .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT), + }); + + // Create the menu + MenuLayer *menu_layer = &data->menu_layer; + menu_layer_init(menu_layer, &bounds); + menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { + .get_num_rows = prv_get_num_rows_callback, + .get_cell_height = prv_get_cell_height_callback, + .draw_row = prv_draw_row_callback, + .select_click = prv_select_callback, + .selection_changed = prv_selection_changed_callback, + .selection_will_change = prv_selection_will_change_callback, + }); + menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack); + GColor highlight_bg = shell_prefs_get_theme_highlight_color(); + menu_layer_set_highlight_colors(menu_layer, highlight_bg, gcolor_legible_over(highlight_bg)); + menu_layer_set_click_config_onto_window(menu_layer, &data->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); + + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->get_initial_selection) { + const uint16_t selected_row = callbacks->get_initial_selection(data->callbacks); + menu_layer_set_selected_index(menu_layer, MenuIndex(0, selected_row), MenuRowAlignCenter, + false /* animated */); + } + + if (callbacks->expand) { + callbacks->expand(data->callbacks); + } + + // Subscribe to pref change events to auto-refresh when settings change remotely + data->pref_change_event_info = (EventServiceInfo) { + .type = PEBBLE_PREF_CHANGE_EVENT, + .handler = prv_pref_change_handler, + .context = data, + }; + event_service_client_subscribe(&data->pref_change_event_info); +} + +static void prv_settings_window_appear(Window *window) { + SettingsData *data = window_get_user_data(window); + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->appear) { + callbacks->appear(data->callbacks); + } +} + +static void prv_settings_window_unload(Window *window) { + SettingsData *data = window_get_user_data(window); + + // Unsubscribe from pref change events + event_service_client_unsubscribe(&data->pref_change_event_info); + + // Call the hide callback for the currently open category. + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->hide) { + callbacks->hide(callbacks); + } + i18n_free_all(data); + menu_layer_deinit(&data->menu_layer); + status_bar_layer_deinit(&data->status_layer); + settings_window_destroy(window); +} + +static Window *prv_create(SettingsMenuItem category, const char *title_override, + SettingsCallbacks *callbacks) { + PBL_ASSERTN(callbacks && (category < SettingsMenuItem_Count)); + + SettingsData *data = app_zalloc_check(sizeof(*data)); + + data->current_category = category; + data->title = settings_menu_get_submodule_info(category)->name; + data->title_override = title_override; + data->callbacks = callbacks; + + // Stash the previously-active settings data so we can restore it on unload + // and not leave app-state user_data pointing at freed memory after a nested + // submenu is popped. + data->prev_app_user_data = app_state_get_user_data(); + app_state_set_user_data(data); + + window_init(&data->window, WINDOW_NAME("Settings Window")); + window_set_user_data(&data->window, data); + window_set_window_handlers(&data->window, &(WindowHandlers){ + .load = prv_settings_window_load, + .appear = prv_settings_window_appear, + .unload = prv_settings_window_unload, + }); + + return &data->window; +} + +Window *settings_window_create(SettingsMenuItem category, SettingsCallbacks *callbacks) { + return prv_create(category, NULL, callbacks); +} + +Window *settings_window_create_with_title(SettingsMenuItem category, const char *title, + SettingsCallbacks *callbacks) { + return prv_create(category, title, callbacks); +} + +void settings_window_destroy(Window *window) { + SettingsData *data = window_get_user_data(window); + + SettingsCallbacks *callbacks = prv_get_current_callbacks(data); + if (callbacks->deinit) { + callbacks->deinit(data->callbacks); + } + + // Restore whatever was in app-state user_data before we took over. + app_state_set_user_data(data->prev_app_user_data); + + i18n_free_all(data); + app_free(data); +} + +void settings_menu_mark_dirty(SettingsMenuItem category) { + SettingsData *data = app_state_get_user_data(); + if (data->current_category == category) { + layer_mark_dirty(menu_layer_get_layer(&data->menu_layer)); + } +} + +void settings_menu_reload_data(SettingsMenuItem category) { + SettingsData *data = app_state_get_user_data(); + if (data->current_category == category) { + menu_layer_reload_data(&data->menu_layer); + } +} + +int16_t settings_menu_get_selected_row(SettingsMenuItem category) { + SettingsData *data = app_state_get_user_data(); + if (data->current_category == category) { + return menu_layer_get_selected_index(&data->menu_layer).row; + } + return 0; // say first row is selected if all else fails +} diff --git a/src/fw/apps/system/settings/window.h b/src/fw/apps/system/settings/window.h new file mode 100644 index 0000000000..3fc460882f --- /dev/null +++ b/src/fw/apps/system/settings/window.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "menu.h" + +#include "applib/ui/window.h" + +Window *settings_window_create(SettingsMenuItem category, SettingsCallbacks *callbacks); + +//! Create a settings window that reuses a parent category's identity (for +//! mark-dirty / reload routing) but shows a distinct title. Use this for +//! nested submenus under an existing top-level category — e.g. a "Backlight" +//! submenu pushed from within the "Display" settings. +//! @param category The owning top-level SettingsMenuItem for dirty routing. +//! @param title Status-bar title for this window. Must outlive the window. +//! @param callbacks Row-handling callbacks for this submenu. +Window *settings_window_create_with_title(SettingsMenuItem category, const char *title, + SettingsCallbacks *callbacks); + +void settings_window_destroy(Window *window); diff --git a/src/fw/apps/system/timeline/animations.c b/src/fw/apps/system/timeline/animations.c new file mode 100644 index 0000000000..d90790c3ff --- /dev/null +++ b/src/fw/apps/system/timeline/animations.c @@ -0,0 +1,36 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "animations.h" + +#include "applib/ui/animation_timing.h" +#include "applib/ui/property_animation.h" +#include "applib/ui/ui.h" + +#include + +int64_t timeline_animation_interpolate_moook_soft(int32_t normalized, + int64_t from, int64_t to) { + return interpolate_moook_soft(normalized, from, to, TIMELINE_NUM_MOOOK_FRAMES_MID); +} + +int64_t timeline_animation_interpolate_moook_second_half(int32_t normalized, + int64_t from, int64_t to) { + const int32_t cut = (normalized + ANIMATION_NORMALIZED_MAX) / 2; + return timeline_animation_interpolate_moook_soft(cut, from, to); +} + +void timeline_animation_layer_stopped_cut_to_end(Animation *animation, bool finished, + void *context) { + PropertyAnimation *property_animation = context; + if (finished) { + return; + } + + GRect to; + Layer *layer; + if (property_animation_get_to_grect(property_animation, &to) && + property_animation_get_subject(property_animation, (void **)&layer)) { + layer_set_frame(layer, &to); + } +} diff --git a/src/fw/apps/system/timeline/animations.h b/src/fw/apps/system/timeline/animations.h new file mode 100644 index 0000000000..b90c06a29e --- /dev/null +++ b/src/fw/apps/system/timeline/animations.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/animation.h" + +#define TIMELINE_NUM_MOOOK_FRAMES_MID 3 +#define TIMELINE_UP_DOWN_ANIMATION_DURATION_MS \ + (interpolate_moook_soft_duration(TIMELINE_NUM_MOOOK_FRAMES_MID)) + +int64_t timeline_animation_interpolate_moook_soft(int32_t normalized, + int64_t from, int64_t to); + +int64_t timeline_animation_interpolate_moook_second_half(int32_t normalized, + int64_t from, int64_t to); + +void timeline_animation_layer_stopped_cut_to_end(Animation *animation, bool finished, + void *context); diff --git a/src/fw/apps/system/timeline/common.h b/src/fw/apps/system/timeline/common.h new file mode 100644 index 0000000000..8acaa5ca5b --- /dev/null +++ b/src/fw/apps/system/timeline/common.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/timeline/timeline.h" + +#define TIMELINE_NUM_VISIBLE_ITEMS (2) + +#define TIMELINE_PAST_COLOR PBL_IF_COLOR_ELSE(GColorChromeYellow, GColorLightGray) +#define TIMELINE_FUTURE_COLOR GColorVividCerulean +#define TIMELINE_DOT_COLOR GColorBlack + +typedef TimelineIterDirection TimelineDirection; diff --git a/src/fw/apps/system/timeline/layer.c b/src/fw/apps/system/timeline/layer.c new file mode 100644 index 0000000000..1dfbe758ca --- /dev/null +++ b/src/fw/apps/system/timeline/layer.c @@ -0,0 +1,973 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "animations.h" +#include "layer.h" +#include "model.h" +#include "relbar.h" + +#include "applib/fonts/fonts.h" +#include "applib/graphics/gpath.h" +#include "applib/graphics/graphics.h" +#include "applib/preferred_content_size.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/kino/kino_reel/scale_segmented.h" +#include "applib/ui/property_animation.h" +#include "applib/ui/window.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/kernel_ui.h" +#include "kernel/ui/system_icons.h" +#include "popups/timeline/peek_animations.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/layout_layer.h" +#include "pbl/services/timeline/timeline_layout.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/math.h" +#include "util/size.h" +#include "util/string.h" +#include "util/struct.h" +#include "util/trig.h" + +#include +#include + +#define PAST_TOP_MARGIN_EXTRA PBL_IF_RECT_ELSE(10, 38) +#define FUTURE_TOP_MARGIN_EXTRA PBL_IF_RECT_ELSE(10, 18) + +typedef struct TimelineLayerStyle { + GSize sidebar_arrow_size; + GPoint day_sep_offset; + uint16_t sidebar_width; + int16_t fin_offset_x; + int16_t past_fin_offset_y; + int16_t future_fin_offset_y; + uint16_t past_top_margin; + uint16_t past_thin_pin_margin; + uint16_t future_top_margin; + uint16_t left_margin; + uint16_t right_margin; + int16_t icon_offset_y; + uint16_t icon_right_margin; + uint16_t fat_pin_height; + uint16_t thin_pin_height; + uint16_t day_sep_dot_diameter; + uint16_t day_sep_subtitle_margin; + int16_t past_day_sep_dot_offset_y; + int16_t future_day_sep_dot_offset_y; +} TimelineLayerStyle; + +#define MARGIN_MEDIUM PBL_IF_RECT_ELSE(4, 13) + +static const TimelineLayerStyle s_style_medium = { + .sidebar_arrow_size.w = PBL_IF_RECT_ELSE(10, 7), + .sidebar_arrow_size.h = PBL_IF_RECT_ELSE(20, 28), + .sidebar_width = PBL_IF_RECT_ELSE(30, 48), + .past_fin_offset_y = PBL_IF_ROUND_ELSE(-12, 0), + .future_fin_offset_y = PBL_IF_ROUND_ELSE(-20, 0), + .past_top_margin = PBL_IF_RECT_ELSE(10, 18), + .future_top_margin = PBL_IF_RECT_ELSE(10, 39), + .left_margin = MARGIN_MEDIUM, + .right_margin = MARGIN_MEDIUM, + .icon_right_margin = MARGIN_MEDIUM, + .fat_pin_height = 110, + // PBL-42540: This property is dependent on the screen size. The thin pin height is the + // remainder of the screen space after the fat pin. + .thin_pin_height = PBL_IF_RECT_ELSE(66, 43), + .day_sep_dot_diameter = 9, + .day_sep_offset.x = PBL_IF_ROUND_ELSE(12, 0), + .day_sep_offset.y = -12, + .day_sep_subtitle_margin = PEEK_LAYER_SUBTITLE_MARGIN, + .past_day_sep_dot_offset_y = PBL_IF_ROUND_ELSE(-17, 0), + .future_day_sep_dot_offset_y = PBL_IF_ROUND_ELSE(-13, 0), +}; + +static const TimelineLayerStyle s_style_large = { + .sidebar_arrow_size.w = PBL_IF_RECT_ELSE(14, 10), + .sidebar_arrow_size.h = PBL_IF_RECT_ELSE(28, 40), + .sidebar_width = PBL_IF_RECT_ELSE(34, 51), + .fin_offset_x = 4, + .future_fin_offset_y = 37, + .past_top_margin = PBL_IF_RECT_ELSE(7, 18), + .past_thin_pin_margin = 11, + .future_top_margin = PBL_IF_RECT_ELSE(7, 39), + .left_margin = 9, + .right_margin = 14, + .icon_offset_y = 3, + .icon_right_margin = PBL_IF_RECT_ELSE(6, 12), + .fat_pin_height = 131, + // PBL-42540: This property is dependent on the screen size. + .thin_pin_height = 88, + .day_sep_dot_diameter = 12, + .day_sep_offset.y = -21, + .past_day_sep_dot_offset_y = PBL_IF_RECT_ELSE(-16, -32), + .future_day_sep_dot_offset_y = 16, +}; + +static const TimelineLayerStyle * const s_styles[NumPreferredContentSizes] = { + [PreferredContentSizeSmall] = &s_style_medium, + [PreferredContentSizeMedium] = &s_style_medium, + [PreferredContentSizeLarge] = &s_style_large, + [PreferredContentSizeExtraLarge] = &s_style_large, +}; + +static int16_t s_height_offsets[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER]; +static const int s_visible_items[] = {1, 2}; +static const int s_nonvisible_items[] = {0, TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1}; + +static const TimelineLayerStyle *prv_get_style(void) { + return s_styles[PreferredContentSizeDefault]; +} + +uint16_t timeline_layer_get_fat_pin_height(void) { + return prv_get_style()->fat_pin_height; +} + +uint16_t timeline_layer_get_ideal_sidebar_width(void) { + return prv_get_style()->sidebar_width; +} + +/////////////////////////////////////////////////////////// +// Drawing functions +/////////////////////////////////////////////////////////// + +static int prv_get_scroll_delta(TimelineLayer *timeline_layer) { + return (timeline_layer->scroll_direction == TimelineScrollDirectionUp ? -1 : 1); +} + +static int prv_get_index_delta(TimelineLayer *timeline_layer) { + return timeline_layer->move_delta * prv_get_scroll_delta(timeline_layer); +} + +static void prv_set_layout_hidden(TimelineLayout *layout, bool hidden) { + layer_set_hidden((Layer *)layout, hidden); + layer_set_hidden((Layer *)&layout->icon_layer, hidden); +} + +static LayoutLayerMode prv_get_mode(int index) { + switch (index) { + default: + case 1: + return LayoutLayerModePinnedFat; + case 2: + return LayoutLayerModePinnedThin; + } +} + +static void prv_get_size_for(LayoutLayerMode layout_mode, const GRect *bounds, GSize *size) { + const TimelineLayerStyle *style = prv_get_style(); + const int16_t width = bounds->size.w - (style->left_margin + style->right_margin); + switch (layout_mode) { + default: + case LayoutLayerModePinnedFat: + *size = GSize(width, style->fat_pin_height); + break; + case LayoutLayerModePinnedThin: + *size = GSize(width, style->thin_pin_height); + break; + } +} + +static void prv_get_frame(TimelineLayer *layer, int index, GRect *frame) { + const TimelineLayerStyle *style = prv_get_style(); + index = CLIP(index, 0, (int)ARRAY_LENGTH(s_height_offsets) - 1); + const GRect *bounds = &layer->layer.bounds; + frame->origin = GPoint(style->left_margin, s_height_offsets[index]); + prv_get_size_for(prv_get_mode(index), bounds, &frame->size); +} + +void timeline_layer_get_layout_frame(TimelineLayer *layer, int index, GRect *frame_out) { + prv_get_frame(layer, index, frame_out); +} + +static void prv_get_icon_frame_exact(TimelineLayer *layer, int index, GRect *icon_frame) { + const TimelineLayerStyle *style = prv_get_style(); + GRect frame; + prv_get_frame(layer, index, &frame); + frame.origin.y += style->icon_offset_y; + // Remove sidebar and apply icon margin + frame.size.w += style->right_margin - style->icon_right_margin; + timeline_layout_get_icon_frame(&frame, layer->scroll_direction, icon_frame); +} + +#if PBL_ROUND +static void prv_get_icon_frame_centered(TimelineLayer *layer, int index, GRect *icon_frame) { + const int center_index = 1; + const GRect *bounds = &((Layer *)layer)->bounds; + prv_get_icon_frame_exact(layer, center_index, icon_frame); + icon_frame->origin.y += prv_get_scroll_delta(layer) * (index - center_index) * bounds->size.h / 2; +} +#endif + +void timeline_layer_get_icon_frame(TimelineLayer *layer, int index, GRect *icon_frame) { + return PBL_IF_RECT_ELSE(prv_get_icon_frame_exact, + prv_get_icon_frame_centered)(layer, index, icon_frame); +} + +static void prv_get_end_of_timeline_frame(TimelineLayer *layer, int index, GRect *frame) { + prv_get_frame(layer, index, frame); + const bool is_future = (layer->scroll_direction == TimelineScrollDirectionDown); + const TimelineLayerStyle *style = prv_get_style(); + gpoint_add_eq(&frame->origin, + GPoint(style->fin_offset_x, + is_future ? style->future_fin_offset_y : style->past_fin_offset_y)); + frame->size.w -= PBL_IF_RECT_ELSE(style->sidebar_width, 0); +} + +static void prv_get_day_sep_frame(TimelineLayer *layer, int index, GRect *frame) { + prv_get_frame(layer, index, frame); + const bool is_future = (layer->scroll_direction == TimelineScrollDirectionDown); + const TimelineLayerStyle *style = prv_get_style(); + frame->origin.y += is_future ? style->future_day_sep_dot_offset_y : + style->past_day_sep_dot_offset_y; + // Remove the built-in margins and subtract the sidebar + frame->origin.x -= style->left_margin; + frame->size.w += ((style->left_margin + style->right_margin) - + PBL_IF_RECT_ELSE(style->sidebar_width, 0)); +} + +static void prv_get_day_sep_show_frame(TimelineLayer *layer, GRect *frame) { + const GRect *bounds = &((Layer *)layer)->bounds; + const TimelineLayerStyle *style = prv_get_style(); + *frame = (GRect) { + .origin = gpoint_add(bounds->origin, style->day_sep_offset), + .size.w = bounds->size.w - style->sidebar_width, + .size.h = bounds->size.h, + }; +} + +static void prv_create_layout(TimelineLayer *layer, TimelineIterState *state, int index) { + TimelineItem *item = &state->pin; + TimelineLayoutInfo *info = app_malloc_check(sizeof(TimelineLayoutInfo)); + timeline_layout_init_info(info, item, state->current_day); + info->scroll_direction = layer->scroll_direction; + info->app_id = item->header.parent_id; + + GRect rect; + prv_get_frame(layer, index, &rect); + const LayoutLayerConfig config = { + .frame = &rect, + .attributes = &item->attr_list, + .mode = prv_get_mode(index), + .app_id = &item->header.parent_id, + .context = info, + }; + TimelineLayout *layout = (TimelineLayout *)layout_create(item->header.layout, &config); + layer_add_child(&layer->layouts_layer, (Layer *)layout); + GRect icon_rect; + timeline_layer_get_icon_frame(layer, index, &icon_rect); + layer_set_frame((Layer *)&layout->icon_layer, &icon_rect); + layer_add_child(&layer->layouts_layer, (Layer *)&layout->icon_layer); + layer->layouts[index] = layout; + layer->layouts_info[index] = info; +} + +static void prv_destroy_layout(TimelineLayer *layer, int index) { + TimelineLayout *timeline_layout = layer->layouts[index]; + timeline_layout->is_being_destroyed = true; + layer_remove_from_parent((Layer *)timeline_layout); + layout_destroy((LayoutLayer *)timeline_layout); + layer->layouts[index] = NULL; + + app_free(layer->layouts_info[index]); + layer->layouts_info[index] = NULL; +} + +static void prv_destroy_nonvisible_items(TimelineLayer *layer) { + for (int i = 0; i < (int)ARRAY_LENGTH(s_nonvisible_items); i++) { + TimelineLayout *timeline_layout = layer->layouts[s_nonvisible_items[i]]; + if (timeline_layout) { + prv_destroy_layout(layer, s_nonvisible_items[i]); + layer->layouts[s_nonvisible_items[i]] = NULL; + } + } +} + +static void prv_set_layouts_to_final_position(TimelineLayer *layer) { + for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + TimelineLayout *layout = layer->layouts[s_visible_items[i]]; + if (layout) { + GRect frame; + prv_get_frame(layer, i + 1, &frame); + layer_set_frame((Layer *)layout, &frame); + } + } +} + +static void prv_hide_non_current_day_items(TimelineLayer *layer) { + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { + TimelineLayout *layout = layer->layouts[i]; + TimelineIterState *state = timeline_model_get_iter_state(i - 1); + if (layout && state->current_day != layer->current_day) { + prv_set_layout_hidden(layout, true); + } + } +} + +static void prv_reset_layouts(TimelineLayer *layer) { + int num_items = timeline_model_get_num_items(); + for (int i = 0; i < (int)ARRAY_LENGTH(s_visible_items); i++) { + if (layer->layouts[s_visible_items[i]]) { + prv_destroy_layout(layer, s_visible_items[i]); + } + TimelineIterState *state = timeline_model_get_iter_state(i); + if (!state) { + continue; + } + TimelineNode *node = state->node; + if (i < num_items && node) { + prv_create_layout(layer, state, s_visible_items[i]); + } + } +} + +static void prv_update_pins_mode(TimelineLayer *layer) { + for (int i = 0; i < (int)ARRAY_LENGTH(s_visible_items); i++) { + TimelineLayout *timeline_layout = layer->layouts[s_visible_items[i]]; + if (timeline_layout) { + LayoutLayerMode mode = prv_get_mode(i + 1); + layout_set_mode((LayoutLayer *)timeline_layout, mode); + } + } +} + +/////////////////////////////////////////////////////////// +// Drawing functions +/////////////////////////////////////////////////////////// + +// An animation that moves a layer from an initial position to its final position +static Animation *prv_create_layout_up_down_animation( + TimelineLayout *timeline_layout, int to_index, TimelineLayer *timeline_layer, + uint32_t duration, InterpolateInt64Function interpolate) { + const int from_index = to_index + prv_get_index_delta(timeline_layer); + GRect from, to, icon_from, icon_to; + prv_get_frame(timeline_layer, from_index, &from); + prv_get_frame(timeline_layer, to_index, &to); + timeline_layer_get_icon_frame(timeline_layer, from_index, &icon_from); + timeline_layer_get_icon_frame(timeline_layer, to_index, &icon_to); + layer_set_frame((Layer *)timeline_layout, &from); + return timeline_layout_create_up_down_animation(timeline_layout, &from, &to, &icon_from, &icon_to, + duration, interpolate); +} + +static Animation *prv_create_end_of_timeline_animation( + TimelineLayer *layer, int to_index, uint32_t duration, InterpolateInt64Function interpolate) { + const int from_index = to_index + prv_get_index_delta(layer); + GRect from_frame, to_frame; + prv_get_end_of_timeline_frame(layer, from_index, &from_frame); + prv_get_end_of_timeline_frame(layer, to_index, &to_frame); + PropertyAnimation *prop_animation = property_animation_create_layer_frame( + (Layer *)&layer->end_of_timeline, &from_frame, &to_frame); + Animation *animation = property_animation_get_animation(prop_animation); + animation_set_duration(animation, duration); + animation_set_custom_interpolation(animation, interpolate); + animation_set_handlers(animation, (AnimationHandlers) { + .stopped = timeline_animation_layer_stopped_cut_to_end, + }, prop_animation); + return animation; +} + +Animation *timeline_layer_create_day_sep_hide(TimelineLayer *timeline_layer) { + GRect frame; + layer_get_global_frame((Layer *)&timeline_layer->day_separator, &frame); + const TimelineLayerStyle *style = prv_get_style(); + const int16_t expanded_layer_height = + frame.size.h + 2 * (style->left_margin + style->right_margin); + // move way off screen: 2x height * (1/2 - move_delta) in integer logic + const int16_t TARGET_Y = (2 * expanded_layer_height * (1 - 2 * timeline_layer->move_delta)) / 2; + const GRect scale_to = GRect(frame.origin.x + frame.size.w / 2, // go to the center + TARGET_Y, 0, 0); + peek_layer_set_scale_to(&timeline_layer->day_separator, scale_to); + + // out anim + GRect to = timeline_layer->day_separator.layer.frame; + to.origin = GPoint(0, frame.size.h * timeline_layer->move_delta); // all the way off screen + PropertyAnimation *prop_anim = + property_animation_create_layer_frame((Layer *)&timeline_layer->day_separator, NULL, &to); + Animation *anim = property_animation_get_animation(prop_anim); + animation_set_duration(anim, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS); + animation_set_custom_interpolation(anim, timeline_animation_interpolate_moook_soft); + + peek_layer_clear_fields(&timeline_layer->day_separator); + peek_layer_play(&timeline_layer->day_separator); + + return anim; +} + +void timeline_layer_set_day_sep_frame(TimelineLayer *timeline_layer, const GRect *frame) { + layer_set_hidden((Layer *)&timeline_layer->day_separator, false); + peek_layer_set_frame(&timeline_layer->day_separator, frame); +} + +static void prv_show_day_sep(TimelineLayer *timeline_layer, bool slide) { + // update the day to show the right date + timeline_layer->current_day = timeline_model_get_current_state()->current_day; + char friendly_date[TIME_STRING_REQUIRED_LENGTH]; + char month_and_day[TIME_STRING_REQUIRED_LENGTH]; + clock_get_friendly_date(friendly_date, TIME_STRING_REQUIRED_LENGTH, timeline_layer->current_day); + clock_get_month_named_date(month_and_day, TIME_STRING_REQUIRED_LENGTH, + timeline_layer->current_day); + + GRect frame; + layer_get_global_frame((Layer *)&timeline_layer->day_separator, &frame); + const GRect icon_from = { grect_center_point(&frame), GSizeZero }; + prv_get_day_sep_show_frame(timeline_layer, &frame); + peek_layer_set_frame(&timeline_layer->day_separator, &frame); + TimelineResourceInfo timeline_res = { + .res_id = TIMELINE_RESOURCE_DAY_SEPARATOR, + }; + peek_layer_set_icon_with_size(&timeline_layer->day_separator, &timeline_res, + TimelineResourceSizeLarge, icon_from); + + if (slide) { + const bool align_in_frame = true; + frame.origin.y += PEEK_LAYER_ICON_OFFSET_Y; + peek_layer_set_scale_to_image(&timeline_layer->day_separator, &timeline_res, + TimelineResourceSizeLarge, frame, align_in_frame); + peek_layer_set_fields_hidden(&timeline_layer->day_separator, true); + } + + peek_layer_set_fields(&timeline_layer->day_separator, "", friendly_date, month_and_day); + peek_layer_play(&timeline_layer->day_separator); +} + +void timeline_layer_unfold_day_sep(TimelineLayer *timeline_layer) { + const bool slide = false; + prv_show_day_sep(timeline_layer, slide); +} + +void timeline_layer_slide_day_sep(TimelineLayer *timeline_layer) { + const bool slide = true; + prv_show_day_sep(timeline_layer, slide); +} + +static void prv_day_sep_anim_stopped(Animation *anim, bool finished, void *context) { + TimelineLayer *timeline_layer = context; + if (finished) { + timeline_layer_unfold_day_sep(timeline_layer); + } +} + +// TODO: PBL-21717 Day separator on Spalding +Animation *timeline_layer_create_day_sep_show(TimelineLayer *timeline_layer) { + GRect *from = &((Layer *)&timeline_layer->day_separator)->frame; + GRect to; + prv_get_day_sep_show_frame(timeline_layer, &to); + // Keep the x-axis values until the actual unfold to maintain alignment + to.origin.x = from->origin.x; + to.size.w = from->size.w; + + PropertyAnimation *prop_anim = + property_animation_create_layer_frame((Layer *)&timeline_layer->day_separator, from, &to); + Animation *anim = property_animation_get_animation(prop_anim); + animation_set_handlers(anim, (AnimationHandlers) { + .stopped = prv_day_sep_anim_stopped, + }, timeline_layer); + animation_set_duration(anim, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS); + animation_set_custom_interpolation(anim, timeline_animation_interpolate_moook_soft); + return anim; +} + +#define MAX_UP_DOWN_ANIMATIONS (2 * TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER + 1) + +static Animation *prv_create_up_down_animation(TimelineLayer *layer, uint32_t duration, + InterpolateInt64Function interpolate) { + Animation *animations[MAX_UP_DOWN_ANIMATIONS] = {}; + int num_animations = 0; + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { + if (layer->layouts[i]) { + animations[num_animations++] = + prv_create_layout_up_down_animation(layer->layouts[i], i, layer, duration, interpolate); + animations[num_animations++] = + (Animation *)kino_layer_create_play_section_animation( + &layer->layouts[i]->icon_layer, 0, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS); + } else if (i == 2 || i == 3) { + animations[num_animations++] = + prv_create_end_of_timeline_animation(layer, i, duration, interpolate); + break; + } + } + +// TODO: PBL-21982: Only support rectangular screen for now +#if PBL_RECT + Animation *relbar_animation = + timeline_relbar_layer_create_animation(layer, duration, interpolate); + if (relbar_animation) { + animations[num_animations++] = relbar_animation; + } +#endif + + Animation *animation = animation_spawn_create_from_array(animations, num_animations); + return animation; +} + +static void prv_place_day_separator(TimelineLayer *layer) { + if (!layer_get_hidden((Layer *)&layer->day_separator)) { + // already on screen + return; + } + + GRect day_sep_frame = ((Layer *)&layer->day_separator)->frame; + // substitute the day separator for the hidden pin + TimelineLayout *prev = layer->layouts[s_nonvisible_items[0]]; + TimelineLayout *next = layer->layouts[s_visible_items[1]]; + if (prev && layer_get_hidden(&prev->layout_layer.layer)) { + prv_get_day_sep_frame(layer, 0, &day_sep_frame); + } else if (next && layer_get_hidden(&next->layout_layer.layer)) { + prv_get_day_sep_frame(layer, 2, &day_sep_frame); + } else { + // don't show the day separator + return; + } + + layer_set_frame((Layer *)&layer->day_separator, &day_sep_frame); + layer_set_hidden((Layer *)&layer->day_separator, false); +} + +static void prv_place_end_of_timeline(TimelineLayer *timeline_layer) { + const bool was_hidden = layer_get_hidden((Layer *)&timeline_layer->end_of_timeline); + const bool is_hidden = (timeline_layer_should_animate_day_separator(timeline_layer) || + timeline_layer->layouts[2]); + layer_set_hidden((Layer *)&timeline_layer->end_of_timeline, is_hidden); + GRect frame; + prv_get_end_of_timeline_frame(timeline_layer, is_hidden ? 3 : 2, &frame); + layer_set_frame((Layer *)&timeline_layer->end_of_timeline, &frame); + if (was_hidden) { + kino_layer_rewind(&timeline_layer->end_of_timeline); + } + kino_layer_play(&timeline_layer->end_of_timeline); +}; + +static void prv_up_down_stopped(Animation *animation, bool is_finished, void *context) { + TimelineLayer *layer = context; + prv_update_pins_mode(layer); + prv_set_layouts_to_final_position(layer); + prv_destroy_nonvisible_items(layer); + prv_place_day_separator(layer); + prv_place_end_of_timeline(layer); +} + +static void prv_mode_change_update(Animation *animation, AnimationProgress normalized) { + TimelineLayer *timeline_layer = animation_get_context(animation); + const AnimationProgress bounce_back_length = + (interpolate_moook_out_duration() * ANIMATION_NORMALIZED_MAX) / interpolate_moook_duration(); + if (normalized >= ANIMATION_NORMALIZED_MAX - bounce_back_length) { + prv_update_pins_mode(timeline_layer); + animation_unschedule(animation); + } +} + +Animation *timeline_layer_create_up_down_animation(TimelineLayer *layer, uint32_t duration, + InterpolateInt64Function interpolate) { + Animation *animation = prv_create_up_down_animation(layer, duration, interpolate); + + animation_set_handlers(animation, (AnimationHandlers) { + .stopped = prv_up_down_stopped, + }, layer); + + static const AnimationImplementation s_mode_change_impl = { + .update = prv_mode_change_update, + }; + + Animation *mode_change = animation_create(); + animation_set_implementation(mode_change, &s_mode_change_impl); + animation_set_handlers(mode_change, (AnimationHandlers){ 0 }, layer); + + return animation_spawn_create(animation, mode_change, NULL); +} + +#if PBL_ROUND +static void prv_draw_round_flip(GContext *ctx, const GRect *layer_bounds, const int sidebar_x) { + // Use a radius larger than the screen's radius so we don't see the top/bottom of the circle + int16_t circle_radius = DISP_COLS * 3 / 4; + const int16_t flip_overlap_region_width = layer_bounds->size.w / 5; + const GPoint bounds_center = grect_center_point(layer_bounds); + const int16_t flip_point_x = bounds_center.x - (flip_overlap_region_width / 2); + // If the origin x value is not past the flip point, draw a colored circle starting at the x pos + if (sidebar_x <= flip_point_x) { + // Don't draw the circle in the flip region overlap; we want an instantaneous jump past this + // region during the flip + const int16_t circle_left_edge_x = MIN(sidebar_x, flip_point_x); + const GPoint circle_center = GPoint(circle_left_edge_x + circle_radius, bounds_center.y); + graphics_fill_circle(ctx, circle_center, circle_radius); + } else { + // Otherwise, use fill_radial to fill the sidebar as a radial on the right side of the screen + const GPoint circle_center = GPoint(sidebar_x - circle_radius, bounds_center.y); + // Add half the final sidebar width to the radius so we see a bounce-back effect at the end + const TimelineLayerStyle *style = prv_get_style(); + circle_radius += style->sidebar_width / 2; + graphics_fill_radial_internal(ctx, circle_center, circle_radius, + layer_bounds->size.w - circle_center.x, + 0, TRIG_MAX_ANGLE); + } +} +#endif + +static void prv_update_proc(struct Layer *layer, GContext* ctx) { + TimelineLayer *timeline_layer = (TimelineLayer *)layer; + const GRect *bounds = &layer->bounds; + + graphics_context_set_fill_color(ctx, GColorWhite); + graphics_fill_rect(ctx, &(GRect) { .size = bounds->size }); + + AnimationProgress progress; + if (timeline_layer->animating_intro_or_exit && + animation_get_progress(timeline_layer->animation, &progress)) { + const GPoint offset = { PEEK_ANIMATIONS_SPEED_LINES_OFFSET_X, + interpolate_int64_linear(progress, 0, -DISP_ROWS) }; + graphics_context_set_fill_color(ctx, GColorBlack); + peek_animations_draw_timeline_speed_lines(ctx, offset); + } + + const int16_t sidebar_width = timeline_layer->sidebar_width; + const GRect sidebar_rect = GRect(bounds->size.w - sidebar_width, 0, sidebar_width, + bounds->size.h); + graphics_context_set_fill_color(ctx, timeline_layer->sidebar_color); + + // On round displays, draw the round flip effect if we're animating the intro or exit and then + // return early so we don't draw the arrow notch +#if PBL_ROUND + if (timeline_layer->animating_intro_or_exit) { + prv_draw_round_flip(ctx, bounds, sidebar_rect.origin.x); + return; + } +#endif + + graphics_fill_rect(ctx, &sidebar_rect); + int16_t arrow_base_x = bounds->size.w - sidebar_width; +#if PBL_ROUND + // Nudge the arrow's base left on round displays by one pixel + arrow_base_x -= 1; +#endif + const TimelineLayerStyle *style = prv_get_style(); + const GSize arrow_size = style->sidebar_arrow_size; + const int16_t arrow_base_center_y = PBL_IF_RECT_ELSE(16, bounds->size.h / 2); + const int16_t arrow_point_x_offset = PBL_IF_RECT_ELSE(-arrow_size.w, arrow_size.w); + GPath arrow_path = { + .num_points = 3, + .points = (GPoint[]) { { arrow_base_x, arrow_base_center_y - (arrow_size.h / 2) }, + { arrow_base_x + arrow_point_x_offset, arrow_base_center_y }, + { arrow_base_x, arrow_base_center_y + (arrow_size.h / 2) } } + }; + + if (timeline_layer->scroll_direction == TimelineScrollDirectionUp) { + // arrow is in a different position for past & future, but only on rectangular displays + gpath_move_to(&arrow_path, + PBL_IF_RECT_ELSE(GPoint(0, (style->thin_pin_height + + style->past_thin_pin_margin)), GPointZero)); + } + + graphics_context_set_antialiased(ctx, true); + const GColor arrow_fill_color = PBL_IF_RECT_ELSE(timeline_layer->sidebar_color, GColorWhite); + graphics_context_set_fill_color(ctx, arrow_fill_color); + gpath_draw_filled(ctx, &arrow_path); + graphics_context_set_antialiased(ctx, false); +} + +///////////////////////////////////////// +// Public functions +///////////////////////////////////////// + +// when we create a new next or previous item, we want it out of view so we can animate it in +void timeline_layer_set_next_item(TimelineLayer *layer, int index) { + PBL_LOG_DBG("Setting next item with index %d", index); + TimelineIterState *iter_state = timeline_model_get_iter_state_with_timeline_idx(index); + if (!iter_state) { + return; + } + if (layer->layouts[s_nonvisible_items[1]]) { + prv_destroy_layout(layer, s_nonvisible_items[1]); + } + prv_create_layout(layer, iter_state, s_nonvisible_items[1]); +} + +void timeline_layer_set_prev_item(TimelineLayer *layer, int index) { + PBL_LOG_DBG("Setting prev item with index %d", index); + TimelineIterState *iter_state = timeline_model_get_iter_state_with_timeline_idx(index); + if (!iter_state) { + return; + } + if (layer->layouts[s_nonvisible_items[0]]) { + prv_destroy_layout(layer, s_nonvisible_items[0]); + } + prv_create_layout(layer, iter_state, s_nonvisible_items[0]); + + if (iter_state->current_day != layer->current_day) { + // we moved back to an item from the previous day, display the day separator + // by first hiding the pin of the "last" day that is about to come in and placing it there + if (layer->layouts[0]) { + prv_set_layout_hidden(layer->layouts[0], true); + } + prv_place_day_separator(layer); + } else { + // continue to hide the day separator + layer_set_hidden((Layer *)&layer->day_separator, true); + } +} + +bool timeline_layer_should_animate_day_separator(TimelineLayer *layer) { + return !layer_get_hidden((Layer *)&layer->day_separator); +} + +void timeline_layer_move_data(TimelineLayer *layer, int delta) { + PBL_ASSERTN(delta == 1 || delta == -1); + if (delta == 1) { + if (layer->layouts[s_nonvisible_items[0]]) { + prv_destroy_layout(layer, s_nonvisible_items[0]); + } + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1; i++) { + layer->layouts[i] = layer->layouts[i + 1]; + layer->layouts_info[i] = layer->layouts_info[i + 1]; + } + layer->layouts[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1] = NULL; + layer->layouts_info[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1] = NULL; + } else if (delta == -1) { + if (layer->layouts[s_nonvisible_items[1]]) { + prv_destroy_layout(layer, s_nonvisible_items[1]); + } + for (int i = TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1; i > 0; i--) { + layer->layouts[i] = layer->layouts[i - 1]; + layer->layouts_info[i] = layer->layouts_info[i - 1]; + } + layer->layouts[0] = NULL; + layer->layouts_info[0] = NULL; + } + + layer->move_delta = delta * prv_get_scroll_delta(layer); + + // hide other day's pins before the animation shows them + prv_hide_non_current_day_items(layer); +} + +TimelineLayout *timeline_layer_get_current_layout(TimelineLayer *timeline_layer) { + return timeline_layer->layouts[TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT]; +} + +void timeline_layer_reset(TimelineLayer *layer) { + // reset the animation + animation_unschedule(layer->animation); + layer->animation = NULL; + + // reset the day separator + layer_set_hidden((Layer *)&layer->day_separator, true); + + TimelineResourceInfo timeline_res = { + .res_id = TIMELINE_RESOURCE_DAY_SEPARATOR, + }; + peek_layer_set_icon(&layer->day_separator, &timeline_res); + + // reset the layouts + prv_reset_layouts(layer); + prv_destroy_nonvisible_items(layer); + timeline_layer_set_layouts_hidden(layer, false); + + // PBL-18815: Reset the current day in case the pin was deleted. This should later be animated. + const int index = TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT; + if (layer->layouts[index]) { + layer->current_day = layer->layouts[index]->info->current_day; + } + + prv_hide_non_current_day_items(layer); + prv_place_day_separator(layer); + prv_place_end_of_timeline(layer); + +// TODO: PBL-21982: Only support rectangular screen for now +#if PBL_RECT + timeline_relbar_layer_reset(layer); +#endif +} + +void timeline_layer_set_sidebar_color(TimelineLayer *timeline_layer, GColor color) { + timeline_layer->sidebar_color = color; +} + +void timeline_layer_set_sidebar_width(TimelineLayer *timeline_layer, int16_t width) { + timeline_layer->sidebar_width = width; +} + +static void prv_sidebar_setter(void *context, int16_t value) { + TimelineLayer *timeline_layer = context; + timeline_layer->sidebar_width = value; + layer_mark_dirty((Layer *)timeline_layer); +} + +static int16_t prv_sidebar_getter(void *context) { + TimelineLayer *timeline_layer = context; + return timeline_layer->sidebar_width; +} + +Animation *timeline_layer_create_sidebar_animation(TimelineLayer *timeline_layer, + int16_t to_sidebar_width) { + static const PropertyAnimationImplementation s_implementation = { + .base.update = (AnimationUpdateImplementation) property_animation_update_int16, + .accessors.setter.int16 = prv_sidebar_setter, + .accessors.getter.int16 = prv_sidebar_getter, + }; + Animation *animation = (Animation *)property_animation_create( + &s_implementation, timeline_layer, &timeline_layer->sidebar_width, &to_sidebar_width); + animation_set_duration(animation, interpolate_moook_in_duration()); + animation_set_custom_interpolation(animation, interpolate_moook_in_only); + return animation; +} + +static void prv_speed_lines_update(Animation *animation, AnimationProgress progress) {} + +Animation *timeline_layer_create_speed_lines_animation(TimelineLayer *timeline_layer) { + static const AnimationImplementation s_speed_lines_impl = { + .update = prv_speed_lines_update, + }; + Animation *animation = animation_create(); + animation_set_implementation(animation, &s_speed_lines_impl); + const unsigned int num_jump_frames = 3; + animation_set_duration(animation, num_jump_frames * ANIMATION_TARGET_FRAME_INTERVAL_MS); + timeline_layer->animation = animation; + return animation; +} + +static Animation *prv_create_bounce_back_animation(Layer *layer, const GRect *to_orig, + GPoint direction) { + GRect from = *to_orig; + GRect to = *to_orig; + gpoint_add_eq(&from.origin, GPoint(direction.x * INTERPOLATE_MOOOK_BOUNCE_BACK, + direction.y * INTERPOLATE_MOOOK_BOUNCE_BACK)); + layer_set_frame(layer, &from); + Animation *animation = (Animation *)property_animation_create_layer_frame(layer, &from, &to); + animation_set_curve(animation, AnimationCurveEaseOut); + animation_set_duration(animation, TIMELINE_LAYER_SLIDE_MS); + animation_set_handlers(animation, (AnimationHandlers) { + .stopped = timeline_animation_layer_stopped_cut_to_end, + }, animation); + return animation; +} + +Animation *timeline_layer_create_bounce_back_animation(TimelineLayer *layer, GPoint direction) { + Animation *animations[TIMELINE_NUM_VISIBLE_ITEMS + 2] = {}; + int num_animations = 0; + + for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + TimelineLayout *layout = layer->layouts[s_visible_items[i]]; + if (layout) { + GRect frame; + prv_get_frame(layer, i + 1, &frame); + animations[num_animations++] = prv_create_bounce_back_animation((Layer *)layout, &frame, + direction); + } + } + + const GRect *day_sep_from = &((Layer *)&layer->day_separator)->frame; + animations[num_animations++] = prv_create_bounce_back_animation((Layer *)&layer->day_separator, + day_sep_from, direction); + + const GRect *fin_from = &layer->end_of_timeline.layer.frame; + animations[num_animations++] = prv_create_bounce_back_animation( + &layer->end_of_timeline.layer, fin_from, direction); + + Animation *animation = animation_spawn_create_from_array(animations, num_animations); + return animation; +} + +void timeline_layer_set_layouts_hidden(TimelineLayer *layer, bool hidden) { + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { + TimelineLayout *layout = layer->layouts[i]; + if (layout) { + prv_set_layout_hidden(layout, hidden); + } + } + + layer_set_hidden((Layer *)&layer->end_of_timeline, hidden); +} + +void timeline_layer_init(TimelineLayer *layer, const GRect *frame_ref, + TimelineScrollDirection scroll_direction) { + *layer = (TimelineLayer) {}; + // timeline layer + layer_init(&layer->layer, frame_ref); + layer_set_clips(&layer->layer, false); + layer_set_update_proc(&layer->layer, prv_update_proc); + TimelineIterState *state = timeline_model_get_current_state(); + layer->current_day = NULL_SAFE_FIELD_ACCESS(state, current_day, 0); + const TimelineLayerStyle *style = prv_get_style(); + // The arrow is inverted on round, so hide it by extending the width of the sidebar + layer->sidebar_width = frame_ref->size.w + PBL_IF_ROUND_ELSE(style->sidebar_arrow_size.w, 0); + // layouts + layer->scroll_direction = scroll_direction; + layer->move_delta = prv_get_scroll_delta(layer); + if (scroll_direction == TimelineScrollDirectionUp) { + s_height_offsets[0] = (PAST_TOP_MARGIN_EXTRA + style->thin_pin_height + + (2 * style->fat_pin_height)); + s_height_offsets[1] = (style->past_top_margin + style->thin_pin_height + + style->past_thin_pin_margin); + s_height_offsets[2] = style->past_top_margin; + s_height_offsets[3] = style->past_top_margin - 2 * style->fat_pin_height; + } else { + s_height_offsets[0] = FUTURE_TOP_MARGIN_EXTRA - 2 * style->fat_pin_height; + s_height_offsets[1] = style->future_top_margin; + s_height_offsets[2] = style->future_top_margin + style->fat_pin_height; + s_height_offsets[3] = (style->future_top_margin + style->fat_pin_height + + (2 * style->fat_pin_height)); + } + // layouts layer - contains all the pin + layer_init(&layer->layouts_layer, &(GRect) { .size = frame_ref->size }); + layer_set_clips(&layer->layouts_layer, false); + layer_add_child((Layer *)layer, (Layer *)&layer->layouts_layer); + + // day separator + GRect frame; + prv_get_day_sep_show_frame(layer, &frame); + peek_layer_init(&layer->day_separator, &frame); + const GFont title_font = + system_theme_get_font_for_size(PreferredContentSizeDefault, TextStyleFont_Title); + peek_layer_set_title_font(&layer->day_separator, title_font); + const GFont subtitle_font = + system_theme_get_font_for_size(PreferredContentSizeDefault, TextStyleFont_PinSubtitle); + peek_layer_set_subtitle_font(&layer->day_separator, subtitle_font, + style->day_sep_subtitle_margin); + + TimelineResourceInfo timeline_res = { + .res_id = TIMELINE_RESOURCE_DAY_SEPARATOR, + }; + peek_layer_set_icon(&layer->day_separator, &timeline_res); + peek_layer_set_background_color(&layer->day_separator, GColorClear); + peek_layer_set_dot_diameter(&layer->day_separator, style->day_sep_dot_diameter); + layer_set_hidden((Layer *)&layer->day_separator, true); + layer_add_child((Layer *)layer, (Layer *)&layer->day_separator); + + // end-of-timeline indicator + // TODO: PBL-21716 Fin icon layout on Spalding + prv_get_end_of_timeline_frame(layer, 3, &frame); + kino_layer_init(&layer->end_of_timeline, &frame); + kino_layer_set_reel_with_resource(&layer->end_of_timeline, RESOURCE_ID_END_OF_TIMELINE); + kino_layer_set_alignment(&layer->end_of_timeline, GAlignTop); + layer_add_child((Layer *)layer, (Layer *)&layer->end_of_timeline); + + // populate the timeline with items + timeline_layer_reset(layer); + +// TODO: PBL-21982: Only support rectangular screen for now +#if PBL_RECT + // Initialize Relationship bar + timeline_relbar_layer_init(layer); +#endif +} + +void timeline_layer_deinit(TimelineLayer *layer) { + animation_unschedule_all(); + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { + if (layer->layouts[i]) { + prv_destroy_layout(layer, i); + } + } + peek_layer_deinit(&layer->day_separator); + kino_layer_deinit(&layer->end_of_timeline); + +// TODO: PBL-21982: Only support rectangular screen for now +#if PBL_RECT + timeline_relbar_layer_deinit(layer); +#endif +} diff --git a/src/fw/apps/system/timeline/layer.h b/src/fw/apps/system/timeline/layer.h new file mode 100644 index 0000000000..5daf9ddebf --- /dev/null +++ b/src/fw/apps/system/timeline/layer.h @@ -0,0 +1,149 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "common.h" +#include "peek_layer.h" + +#include "applib/graphics/text.h" +#include "applib/ui/animation.h" +#include "applib/ui/layer.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/item.h" +#include "pbl/services/timeline/timeline_layout.h" +#include "pbl/services/timeline/timeline_layout_animations.h" + +#include + +#define TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER (TIMELINE_NUM_VISIBLE_ITEMS + 2) + +#define TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT 1 + +#define TIMELINE_LAYER_TEXT_ALIGNMENT \ + PBL_IF_RECT_ELSE(GTextAlignmentLeft, GTextAlignmentRight) +#define TIMELINE_LAYER_TEXT_VERTICAL_ALIGNMENT GVerticalAlignmentTop + +#define TIMELINE_LAYER_SLIDE_MS (150) + +//! Relationship bars describe the relationship between two Timeline events as a visual. +typedef enum { + //! The two timeline events are not in the same day. + RelationshipBarTypeNone = 0, + //! There is time between the end of the first event and the start of the second event. + RelationshipBarTypeFreeTime, + //! As soon as the first event starts, the second event ends. + RelationshipBarTypeBackToBack, + //! The first event is still in progress when the second event starts. + RelationshipBarTypeOverlap, +} RelationshipBarType; + +typedef struct { + RelationshipBarType rel_bar_type; + int16_t anim_offset; +} RelationshipBar; + +typedef struct { + Layer layer; + RelationshipBar prev_rel_bar; // Used for previous relationship bar animation exit + RelationshipBar curr_rel_bar; // Used for current on-screen relationship bar animation + EventedTimerID rel_bar_timer; // Used to show bars after user stops fast scrolling + void *timeline_layer; // Necessary for the layer update proc to access the TimelineLayer +} RelationshipBarLayer; + +// The timeline layer is the view(controller, sort of) for the timeline -- it uses +// TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER layout layers, timeline layouts and view slots +// layouts[1] is the first item shown, layouts[0] and layouts[TIMELINE_NUM_VISIBLE_ITEMS + 1] +// should be NULL most of the time and are used to animate out layers + +typedef struct { + Layer layer; + Layer layouts_layer; + TimelineLayout *layouts[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER]; + TimelineLayoutInfo *layouts_info[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER]; + TimelineScrollDirection scroll_direction; + int16_t sidebar_width; + GColor sidebar_color; + KinoLayer end_of_timeline; + PeekLayer day_separator; + time_t current_day; + // TODO: PBL-22076 Remove Timeline Layer move_delta + // It is not good to keep too much questionably long lived state in views + int move_delta; + Animation *animation; + RelationshipBarLayer relbar_layer; + bool animating_intro_or_exit; +} TimelineLayer; + +void timeline_layer_reset(TimelineLayer *layer); + +void timeline_layer_set_next_item(TimelineLayer *layer, int index); + +void timeline_layer_set_prev_item(TimelineLayer *layer, int index); + +void timeline_layer_move_data(TimelineLayer *layer, int delta); + +uint16_t timeline_layer_get_fat_pin_height(void); + +uint16_t timeline_layer_get_ideal_sidebar_width(void); + +void timeline_layer_get_layout_frame(TimelineLayer *layer, int index, GRect *frame_out); + +void timeline_layer_get_icon_frame(TimelineLayer *layer, int index, GRect *frame_out); + +Animation *timeline_layer_create_up_down_animation(TimelineLayer *layer, uint32_t duration, + InterpolateInt64Function interpolate); + +// TODO: move animation logic to timeline_animations.c +// Returns whether the move animation should animate the day separator +bool timeline_layer_should_animate_day_separator(TimelineLayer *layer); + +void timeline_layer_set_day_sep_frame(TimelineLayer *timeline_layer, const GRect *frame); + +void timeline_layer_unfold_day_sep(TimelineLayer *timeline_layer); + +void timeline_layer_slide_day_sep(TimelineLayer *timeline_layer); + +Animation *timeline_layer_create_day_sep_show(TimelineLayer *timeline_layer); + +Animation *timeline_layer_create_day_sep_hide(TimelineLayer *timeline_layer); + +//! Initialize a timeline layer +//! @param layer a pointer to the TimelineLayer to initialize +//! @param frame the frame with which to initialize the layer +//! @param scroll_direction the direction to scroll for the next item +void timeline_layer_init(TimelineLayer *layer, const GRect *frame, + TimelineScrollDirection scroll_direction); + +//! Sets the sidebar color +void timeline_layer_set_sidebar_color(TimelineLayer *timeline_layer, GColor color); + +//! Sets the sidebar width +//! @param layer Pointer to the TimelineLayer. +//! @param width Width to set the TimelineLayer sidebar to. +void timeline_layer_set_sidebar_width(TimelineLayer *timeline_layer, int16_t width); + +//! Create a sidebar animation that changes the width +//! @param timeline_layer Pointer to the TimelineLayer. +//! @param to_sidebar_width Width to animate the TimelineLayer sidebar to. +Animation *timeline_layer_create_sidebar_animation(TimelineLayer *timeline_layer, + int16_t to_sidebar_width); + +//! Creates a speed lines animation that simulates scrolling through Timeline +//! @param timeline_layer Pointer to the TimelineLayer. +Animation *timeline_layer_create_speed_lines_animation(TimelineLayer *timeline_layer); + +//! Create a bounce back animation for all layouts +//! @param layer Pointer to the TimelineLayer. +//! @param direction The direction of the bounce back as a unit vector. +Animation *timeline_layer_create_bounce_back_animation(TimelineLayer *layer, GPoint direction); + +//! Sets whether the layout layers are hidden or not +void timeline_layer_set_layouts_hidden(TimelineLayer *layer, bool hidden); + +//! Get the current timeline layout +TimelineLayout *timeline_layer_get_current_layout(TimelineLayer *timeline_layer); + +//! Deinitialize a timeline item layer. +void timeline_layer_deinit(TimelineLayer *layer); diff --git a/src/fw/apps/system/timeline/model.c b/src/fw/apps/system/timeline/model.c new file mode 100644 index 0000000000..0ab85d56e3 --- /dev/null +++ b/src/fw/apps/system/timeline/model.c @@ -0,0 +1,278 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "model.h" + +#include "system/logging.h" +#include "system/passert.h" + +//////////////////////////////////////////////// +// Timeline model circular array of iters logic +//////////////////////////////////////////////// + +static TimelineModel *s_model_data; + +static int prv_get_idx_for_timeline_idx(int timeline_idx) { + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_MODEL; i++) { + if (s_model_data->states[i].index == timeline_idx) { + return i; + } + } + return -1; +} + +static int prv_raw_to_adj_idx(int raw_idx) { + return positive_modulo(raw_idx - s_model_data->first_index, TIMELINE_NUM_ITEMS_IN_MODEL); +} + +static int prv_adj_to_raw_idx(int adj_idx) { + return positive_modulo(adj_idx + s_model_data->first_index, TIMELINE_NUM_ITEMS_IN_MODEL); +} + +TimelineIterState *timeline_model_get_iter_state_with_timeline_idx(int index) { + int raw_idx = prv_get_idx_for_timeline_idx(index); + if (raw_idx == -1) { + return NULL; + } else { + return &s_model_data->states[raw_idx]; + } +} + +int timeline_model_get_idx_for_timeline_idx(int index) { + int raw_idx = prv_get_idx_for_timeline_idx(index); + if (raw_idx == -1) { + return -1; + } else { + return prv_raw_to_adj_idx(prv_get_idx_for_timeline_idx(index)); + } +} + +TimelineIterState *timeline_model_get_iter_state(int index) { + return &s_model_data->states[prv_adj_to_raw_idx(index)]; +} + +bool timeline_model_is_empty(void) { + return (timeline_model_get_num_items() == 0); +} + +int timeline_model_get_num_items(void) { + if (timeline_model_get_current_state() == NULL) { + return 0; + } + int num = positive_modulo(s_model_data->last_index - + s_model_data->first_index, TIMELINE_NUM_ITEMS_IN_MODEL) + 1; + // we always keep one slot marked empty so we can tell if we have zero items + if (num == TIMELINE_NUM_ITEMS_IN_MODEL) { + return 0; + } else { + return num; + } +} + +static int prv_get_next_item_idx(void) { + return positive_modulo(s_model_data->last_index + 1, + TIMELINE_NUM_ITEMS_IN_MODEL); +} + +static int prv_get_prev_item_idx(void) { + return positive_modulo(s_model_data->first_index - 1, + TIMELINE_NUM_ITEMS_IN_MODEL); +} + +static Iterator *prv_get_iter(int index) { + return &s_model_data->iters[prv_adj_to_raw_idx(index)]; +} + +TimelineIterState *timeline_model_get_current_state(void) { + if (timeline_model_get_iter_state(0)->node == NULL) { + return NULL; + } + return timeline_model_get_iter_state(0); +} + +static int prv_find_item_by_uuid(Uuid *id) { + for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + if (timeline_model_get_iter_state(i)->node && + uuid_equal(&timeline_model_get_iter_state(i)->pin.header.id, id)) { + return i; + } + } + return -1; +} + +#ifdef TIMELINE_DEBUG +static void prv_log_all_items(void) { + PBL_LOG_DBG("First item: %d, last item: %d", s_model_data->first_index, + s_model_data->last_index); + for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + TimelineItem *item = &timeline_model_get_iter_state(i)->pin; + PBL_LOG_DBG("ID first byte: 0x%x", item->header.id.byte0); + PBL_LOG_DBG("Address of node: %p", timeline_model_get_iter_state(i)->node); + PBL_LOG_DBG("Timestamp: %ld", item->header.timestamp); + } +} +#endif + +static void prv_move_first_index(int delta) { + s_model_data->first_index = positive_modulo( + s_model_data->first_index + delta, TIMELINE_NUM_ITEMS_IN_MODEL); + PBL_LOG_DBG("Set origin, initial item: %d, final item: %d", + s_model_data->first_index, s_model_data->last_index); +} + +bool timeline_model_iter_next(int *new_idx, bool *has_next) { + int next_idx = prv_get_next_item_idx(); + int last_idx = s_model_data->last_index; + timeline_iter_copy_state( + &s_model_data->states[next_idx], + &s_model_data->states[last_idx], + &s_model_data->iters[next_idx], + &s_model_data->iters[last_idx]); + bool rv = iter_next(&s_model_data->iters[next_idx]); + if (rv) { + if (has_next) { + *has_next = true; + } + s_model_data->last_index = next_idx; + prv_move_first_index(1); + if (new_idx) { + *new_idx = s_model_data->states[next_idx].index; + } + } else { + if (has_next) { + *has_next = false; + } + rv = timeline_model_get_num_items() > 1; + if (rv) { + prv_move_first_index(1); + } + } +#ifdef TIMELINE_DEBUG + prv_log_all_items(); +#endif + return rv; +} + +bool timeline_model_iter_prev(int *new_idx, bool *has_prev) { + int prev_idx = prv_get_prev_item_idx(); + int first_idx = s_model_data->first_index; + timeline_iter_copy_state( + &s_model_data->states[prev_idx], + &s_model_data->states[first_idx], + &s_model_data->iters[prev_idx], + &s_model_data->iters[first_idx]); + bool rv = iter_prev(&s_model_data->iters[prev_idx]); + if (rv) { + if (has_prev) { + *has_prev = true; + } + // bring the last_index back if we've succeeded iterating prev-wards and there are at least + // TIMELINE_NUM_VISIBLE_ITEMS items in the model. If there are fewer, we keep the last_index + // where it is so the model can "grow" to contain TIMELINE_NUM_VISIBLE_ITEMS + if (timeline_model_get_num_items() >= TIMELINE_NUM_VISIBLE_ITEMS) { + s_model_data->last_index = positive_modulo( + s_model_data->last_index - 1, TIMELINE_NUM_ITEMS_IN_MODEL); + } + if (new_idx) { + *new_idx = s_model_data->states[prev_idx].index; + } + prv_move_first_index(-1); + } else { + if (has_prev) { + *has_prev = false; + } + } +#ifdef TIMELINE_DEBUG + prv_log_all_items(); +#endif + return rv; +} + +// Initialize the TIMELINE_NUM_VISIBLE_ITEMS iterators and states +// Try to move the iterators except iters[0] next-wards the appropriate number of times +void timeline_model_init(time_t timestamp, TimelineModel *model) { + PBL_ASSERTN(model); + s_model_data = model; + + // build the timeline + status_t rv = timeline_init(&s_model_data->timeline); + PBL_ASSERTN(PASSED(rv)); + + s_model_data->first_index = 0; + s_model_data->last_index = TIMELINE_NUM_VISIBLE_ITEMS; + for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + rv = timeline_iter_init(prv_get_iter(i), timeline_model_get_iter_state(i), + &s_model_data->timeline, s_model_data->direction, timestamp); + if (FAILED(rv)) { + PBL_LOG_ERR("Timeline iterator failed to init!"); + } + if (FAILED(rv) || rv == S_NO_MORE_ITEMS) { + timeline_model_get_iter_state(i)->node = NULL; + } + bool iter_at_final_position = true; + for (int num_to_iter = 0; num_to_iter < i; num_to_iter++) { + iter_at_final_position = iter_at_final_position && iter_next(prv_get_iter(i)); + } + if (iter_at_final_position) { + s_model_data->last_index = prv_get_next_item_idx(); + } + } +#ifdef TIMELINE_DEBUG + prv_log_all_items(); +#endif +} + +void timeline_model_deinit(void) { + for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_MODEL; i++) { + timeline_iter_deinit(&s_model_data->iters[i], + &s_model_data->states[i], &s_model_data->timeline); + s_model_data->states[i].node = NULL; + } + s_model_data->first_index = 0; + s_model_data->last_index = TIMELINE_NUM_VISIBLE_ITEMS; +} + +static void prv_remove_index_gracefully(int idx) { + PBL_ASSERTN(idx >= 0); + TimelineNode *node = timeline_model_get_iter_state(idx)->node; + if (iter_next(prv_get_iter(idx))) { + for (int i = idx + 1; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + // it's possible for an iter to be on a node that is no longer valid, which could leave + // multiple iterators starting off at different nodes but ending up on the same one + // after one iter_next, so try to separate them + do { + if (!iter_next(prv_get_iter(i))) { + break; + } + } while (timeline_nodes_equal(timeline_model_get_iter_state(i)->node, + timeline_model_get_iter_state(i - 1)->node)); + } + timeline_iter_remove_node(&s_model_data->timeline, node); + PBL_LOG_DBG("Item to delete in view, iterating next"); + } else if (iter_prev(prv_get_iter(idx))) { + // prv_get_iter(idx) is at the end, so we have to move prev-wards + // if prv_get_iter(idx) is at the end, all iters > idx must also be at the end + // so iterate those prev-wards + for (int i = idx + 1; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + iter_prev(prv_get_iter(i)); + } + timeline_iter_remove_node(&s_model_data->timeline, node); + PBL_LOG_DBG("Item to delete in view, iterating prev"); + } else { + // if we can't iterate next or prev, we've deleted the only item + timeline_model_deinit(); + PBL_LOG_DBG("Item to delete in view, deiniting "); + } +} + +void timeline_model_remove(Uuid *id) { + int item_idx; + // more than one item with the same ID is possible due to multi-day events + // remove them from our list first + while ((item_idx = prv_find_item_by_uuid(id)) != -1) { + prv_remove_index_gracefully(item_idx); + } + + // remove the rest from the iterator list + while (timeline_iter_remove_node_with_id(&s_model_data->timeline, id)) {} +} diff --git a/src/fw/apps/system/timeline/model.h b/src/fw/apps/system/timeline/model.h new file mode 100644 index 0000000000..56b80d9f7b --- /dev/null +++ b/src/fw/apps/system/timeline/model.h @@ -0,0 +1,63 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "common.h" + +#include "kernel/events.h" + +#define TIMELINE_NUM_ITEMS_IN_MODEL (TIMELINE_NUM_VISIBLE_ITEMS + 1) + +// Timeline is a circular array of TIMELINE_NUM_ITEMS_IN_MODEL iter states + +typedef struct { + TimelineNode *timeline; + TimelineDirection direction; + Iterator iters[TIMELINE_NUM_ITEMS_IN_MODEL]; + TimelineIterState states[TIMELINE_NUM_ITEMS_IN_MODEL]; + int first_index; + int last_index; +} TimelineModel; + +//! Get the iterator state with the timeline location index, i.e. the iterator with the given index +//! if it's within the model +//! These indices do not change when an iteration has occurred, i.e. +//! timeline_model_get_iter_state_timeline_idx(2) is the same before and after an iter_next or +//! iter_prev +//! @return NULL if the item with that index is not contained in the model's window of items +TimelineIterState *timeline_model_get_iter_state_with_timeline_idx(int index); + +//! Get the index with respect to the model of the timeline item with the given timeline index +//! @return -1 if the item with that index is not contained in the model's window of items +int timeline_model_get_idx_for_timeline_idx(int index); + +//! Get the iterator state with the index with respect to the model, i.e. the index that represents +//! which item in the model it is +//! These indices do change when an iteration has occurred, i.e. +//! timeline_model_get_iter_state_idx(0) changes after an iter_next or iter_prev +TimelineIterState *timeline_model_get_iter_state(int index); + +TimelineIterState *timeline_model_get_current_state(void); + +bool timeline_model_is_empty(void); + +int timeline_model_get_num_items(void); + +//! Iterate the model towards the "next" direction +//! @new_idx Set the raw index of the new iterator if new_idx is not NULL +//! @has_next, set to whether or not there is a new third item in the list, i.e. false = iterate but +//! show no more new items +//! @return whether or not the current item is at the end of the list, i.e. false = stop iterating +bool timeline_model_iter_next(int *new_idx, bool *has_next); + +//! Iterate the model towards the "prev" direction +//! @new_idx Set the raw index of the new iterator if new_idx is not NULL +//! @return whether or not the current item is at the beginning of the list, i.e. stop iterating +bool timeline_model_iter_prev(int *new_idx, bool *has_prev); + +void timeline_model_init(time_t timestamp, TimelineModel *model_data); + +void timeline_model_deinit(void); + +void timeline_model_remove(Uuid *id); diff --git a/src/fw/apps/system_apps/timeline/peek_layer.c b/src/fw/apps/system/timeline/peek_layer.c similarity index 95% rename from src/fw/apps/system_apps/timeline/peek_layer.c rename to src/fw/apps/system/timeline/peek_layer.c index 25d95ecf2e..acc9da3298 100644 --- a/src/fw/apps/system_apps/timeline/peek_layer.c +++ b/src/fw/apps/system/timeline/peek_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "peek_layer.h" @@ -22,9 +9,9 @@ #include "applib/ui/kino/kino_reel/unfold.h" #include "kernel/pbl_malloc.h" #include "kernel/ui/kernel_ui.h" -#include "services/common/evented_timer.h" -#include "services/normal/timeline/notification_layout.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/timeline/notification_layout.h" +#include "pbl/services/timeline/timeline_resources.h" #include "system/logging.h" #include "util/math.h" diff --git a/src/fw/apps/system_apps/timeline/peek_layer.h b/src/fw/apps/system/timeline/peek_layer.h similarity index 89% rename from src/fw/apps/system_apps/timeline/peek_layer.h rename to src/fw/apps/system/timeline/peek_layer.h index b6988bbbba..af042038ae 100644 --- a/src/fw/apps/system_apps/timeline/peek_layer.h +++ b/src/fw/apps/system/timeline/peek_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,9 +7,9 @@ #include "applib/ui/layer.h" #include "applib/ui/kino/kino_layer.h" #include "applib/ui/text_layer.h" -#include "services/common/evented_timer.h" -#include "services/normal/timeline/timeline_resources.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "pbl/services/timeline/item.h" #define PEEK_LAYER_UNFOLD_DURATION 500 #define PEEK_LAYER_SCALE_DURATION 300 diff --git a/src/fw/apps/system_apps/timeline/pin_window.c b/src/fw/apps/system/timeline/pin_window.c similarity index 90% rename from src/fw/apps/system_apps/timeline/pin_window.c rename to src/fw/apps/system/timeline/pin_window.c index 55e9c69776..3f84fcff5f 100644 --- a/src/fw/apps/system_apps/timeline/pin_window.c +++ b/src/fw/apps/system/timeline/pin_window.c @@ -1,28 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pin_window.h" -#include "timeline_model.h" +#include "model.h" #include "applib/ui/action_button.h" #include "applib/ui/app_window_stack.h" #include "applib/ui/ui.h" #include "kernel/pbl_malloc.h" #include "kernel/ui/modals/modal_manager.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/timeline/timeline.h" #include diff --git a/src/fw/apps/system/timeline/pin_window.h b/src/fw/apps/system/timeline/pin_window.h new file mode 100644 index 0000000000..7a23be9c65 --- /dev/null +++ b/src/fw/apps/system/timeline/pin_window.h @@ -0,0 +1,33 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "popups/timeline/timeline_item_layer.h" +#include "pbl/services/timeline/timeline.h" + +#include "applib/ui/ui.h" + +typedef struct TimelinePinWindow TimelinePinWindow; + +struct TimelinePinWindow { + Window window; + Layer layer; //!< Used to perform a bounds animation of the window + Layer action_button_layer; + StatusBarLayer status_layer; + TimelineItemLayer item_detail_layer; + TimelineLayoutInfo info; + Animation *pop_animation; + EventServiceInfo blobdb_event_info; //!< Used for pin events when in modal window +}; + +void timeline_pin_window_set_item(TimelinePinWindow *pin_window, TimelineItem *item, + time_t current_day); + +//! Pop the timeline pin window +void timeline_pin_window_pop(TimelinePinWindow *pin_window); + +void timeline_pin_window_init(TimelinePinWindow *pin_window, TimelineItem *item, + time_t current_day); + +void timeline_pin_window_push_modal(TimelineItem *item); diff --git a/src/fw/apps/system/timeline/private.h b/src/fw/apps/system/timeline/private.h new file mode 100644 index 0000000000..048fbf9513 --- /dev/null +++ b/src/fw/apps/system/timeline/private.h @@ -0,0 +1,3 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + diff --git a/src/fw/apps/system/timeline/relbar.c b/src/fw/apps/system/timeline/relbar.c new file mode 100644 index 0000000000..45f0e29498 --- /dev/null +++ b/src/fw/apps/system/timeline/relbar.c @@ -0,0 +1,593 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "layer.h" +#include "animations.h" +#include "relbar.h" + +#include "applib/graphics/graphics.h" +#include "pbl/services/timeline/timeline_layout.h" +#include "system/logging.h" + +#include + +#define TIMELINE_FAT_PIN_SIZE (timeline_layer_get_fat_pin_height()) +#define SIDEBAR_WIDTH (timeline_layer_get_ideal_sidebar_width()) + +/////////////////////////////////////////////////////////// +// Private functions +/////////////////////////////////////////////////////////// +static void prv_rel_bar_reset_offsets(RelationshipBarLayer *relbar_layer, + RelationshipBarOffsetType rel_bar_type) { + switch (rel_bar_type) { + case RelationshipBarOffsetTypeCurr: + relbar_layer->curr_rel_bar.anim_offset = 0; + break; + case RelationshipBarOffsetTypePrev: + relbar_layer->prev_rel_bar.anim_offset = 0; + break; + case RelationshipBarOffsetTypeBoth: + relbar_layer->curr_rel_bar.anim_offset = 0; + relbar_layer->prev_rel_bar.anim_offset = 0; + break; + } +} + +static void prv_prev_rel_bar_setter(void *context, int16_t value) { + TimelineLayer *timeline_layer = context; + timeline_layer->relbar_layer.prev_rel_bar.anim_offset = value; + layer_mark_dirty(&timeline_layer->layer); +} + +static int16_t prv_prev_rel_bar_getter(void *context) { + TimelineLayer *timeline_layer = context; + return timeline_layer->relbar_layer.prev_rel_bar.anim_offset; +} + +static void prv_curr_rel_bar_setter(void *context, int16_t value) { + TimelineLayer *timeline_layer = context; + timeline_layer->relbar_layer.curr_rel_bar.anim_offset = value; + layer_mark_dirty(&timeline_layer->layer); +} + +static int16_t prv_curr_rel_bar_getter(void *context) { + TimelineLayer *timeline_layer = context; + return timeline_layer->relbar_layer.curr_rel_bar.anim_offset; +} + +static RelationshipBarType prv_get_pin_relationship(TimelineLayoutInfo *current, + TimelineLayoutInfo *next) { + if ((!current) || (current->duration_s == 0) || (current->all_day) || (!next)) { + // No relationship bar shown + return RelationshipBarTypeNone; + } + + const time_t current_end = current->end_time; + if ((next->timestamp > current_end) && (current->current_day == next->current_day)) { + // Next pin starts after the end of the current pin + return RelationshipBarTypeFreeTime; + } else if ((next->timestamp == current_end) && (current->current_day == next->current_day)) { + // Next pin starts exactly at the end of the current pin + return RelationshipBarTypeBackToBack; + } else if (current->current_day != next->current_day) { + // Don't show relationship bar when event is across days + return RelationshipBarTypeNone; + } else { + // All other cases considered as overlapped + return RelationshipBarTypeOverlap; + } +} + +static void prv_rel_bar_stopped(Animation *animation, bool is_finished, void *context) { + // Don't show the rel bar if the animation was interrupted + if (!is_finished) { + TimelineLayer *layer = (TimelineLayer *)context; + prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeBoth); + } +} + +#define REL_BAR_VERT_MARGIN 14 +static int prv_get_line_length(TimelineLayer *timeline_layer, GRect *first_icon_frame_out, + GRect *second_icon_frame_out) { + GRect first_icon_frame; + GRect second_icon_frame; + timeline_layer_get_icon_frame(timeline_layer, TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT, + &first_icon_frame); + timeline_layer_get_icon_frame(timeline_layer, TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT + 1, + &second_icon_frame); + if (first_icon_frame_out) { + *first_icon_frame_out = first_icon_frame; + } + if (second_icon_frame_out) { + *second_icon_frame_out = second_icon_frame; + } + + const int total_space = second_icon_frame.origin.y - grect_get_max_y(&first_icon_frame); + return total_space - (REL_BAR_VERT_MARGIN * 2); +} + +static int prv_get_overlap_line_length(TimelineLayer *layer, GRect *first_icon_frame_out, + GRect *second_icon_frame_out) { + const int full_line_length = + prv_get_line_length(layer, first_icon_frame_out, second_icon_frame_out); + return (3 * full_line_length) / 5; +} + +#define REL_BAR_BACK_TO_BACK_OFFSET 10 +#define REL_BAR_PREV_ANIM_OFFSET 10 + +// Create previous rel bar animation +static Animation *prv_create_prev_rel_bar_animation(TimelineLayer *layer, uint32_t duration, + InterpolateInt64Function interpolate) { + Animation *prev_rel_bar_anim = NULL; + prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypePrev); + int16_t prev_from_rel_bar_value = 0; + int16_t prev_to_rel_bar_value = 0; + static const PropertyAnimationImplementation prev_implementation = { + .base.update = (AnimationUpdateImplementation) property_animation_update_int16, + .accessors.setter.int16 = prv_prev_rel_bar_setter, + .accessors.getter.int16 = prv_prev_rel_bar_getter, + }; + + if (layer->relbar_layer.prev_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { + const int overlap_line_length = prv_get_overlap_line_length(layer, NULL, NULL); + layer->relbar_layer.prev_rel_bar.anim_offset = overlap_line_length; + prev_from_rel_bar_value = overlap_line_length; + prev_to_rel_bar_value = 0; + prev_rel_bar_anim = (Animation *)property_animation_create( + &prev_implementation, layer, &prev_from_rel_bar_value, &prev_to_rel_bar_value); + } else if ((layer->relbar_layer.prev_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || + (layer->relbar_layer.prev_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { + layer->relbar_layer.prev_rel_bar.anim_offset = REL_BAR_PREV_ANIM_OFFSET; + prev_from_rel_bar_value = REL_BAR_PREV_ANIM_OFFSET; + prev_to_rel_bar_value = 0; + prev_rel_bar_anim = (Animation *)property_animation_create( + &prev_implementation, layer, &prev_from_rel_bar_value, &prev_to_rel_bar_value); + } + if (prev_rel_bar_anim) { + // Delay to avoid overlapping moving icon with bars + animation_set_delay(prev_rel_bar_anim, 0); + animation_set_duration(prev_rel_bar_anim, duration / 3); + } + + return prev_rel_bar_anim; +} + +#define REL_BAR_CURR_OVERLAP_START_OFFSET 10 +#define REL_BAR_CURR_ANIM_DELAY(delay) ((2 * delay) / 3) +#define REL_BAR_CURR_ANIM_DURATION(duration) ((2 * duration) / 3) +// Create current rel bar animation +static Animation *prv_create_curr_rel_bar_animation(TimelineLayer *layer, uint32_t duration, + InterpolateInt64Function interpolate) { + Animation *curr_rel_bar_anim = NULL; + prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeCurr); + int16_t curr_from_rel_bar_value = 0; + int16_t curr_to_rel_bar_value = 0; + + static const PropertyAnimationImplementation curr_implementation = { + .base.update = (AnimationUpdateImplementation) property_animation_update_int16, + .accessors.setter.int16 = prv_curr_rel_bar_setter, + .accessors.getter.int16 = prv_curr_rel_bar_getter, + }; + + if (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { + curr_from_rel_bar_value = REL_BAR_CURR_OVERLAP_START_OFFSET; + curr_to_rel_bar_value = prv_get_overlap_line_length(layer, NULL, NULL); + curr_rel_bar_anim = (Animation *)property_animation_create( + &curr_implementation, layer, &curr_from_rel_bar_value, &curr_to_rel_bar_value); + } else if ((layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || + (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { + curr_from_rel_bar_value = 0; + curr_to_rel_bar_value = REL_BAR_BACK_TO_BACK_OFFSET; + curr_rel_bar_anim = (Animation *)property_animation_create( + &curr_implementation, layer, &curr_from_rel_bar_value, &curr_to_rel_bar_value); + } + + if (curr_rel_bar_anim) { + // Delay to avoid overlapping moving icon with bars + animation_set_delay(curr_rel_bar_anim, REL_BAR_CURR_ANIM_DELAY(duration)); + animation_set_duration(curr_rel_bar_anim, REL_BAR_CURR_ANIM_DURATION(duration)); + animation_set_custom_interpolation(curr_rel_bar_anim, interpolate); + } + + return curr_rel_bar_anim; +} + +#define REL_BAR_LINE_CHECK_LENGTH 6 +#define REL_BAR_LINE_WIDTH 2 +#define REL_BAR_LINE_HORIZ_OFFSET ((SIDEBAR_WIDTH / 2) + (REL_BAR_LINE_WIDTH / 2)) +#define REL_BAR_LINE_NOTCH_HORIZ_OFFSET ((REL_BAR_LINE_CHECK_LENGTH / 2) + (REL_BAR_LINE_WIDTH / 2)) +static void prv_draw_rel_bar_line(TimelineLayer *timeline_layer, GContext* ctx, + bool current, int16_t anim_offset) { + int16_t prev_offset = 0; // Used to animate the previous animation offset + if (!current) { + prev_offset = REL_BAR_PREV_ANIM_OFFSET - anim_offset; + prev_offset = (timeline_layer->move_delta > 0) ? prev_offset : -prev_offset; + } + + int16_t curr_offset = anim_offset; // Used to animate the previous animation offset + if ((current && (curr_offset <= 0)) || + ((!current) && ((prev_offset >= REL_BAR_PREV_ANIM_OFFSET) || + (prev_offset <= -REL_BAR_PREV_ANIM_OFFSET)))) { + return; + } + + // Choose which offset to use based on current or previous animation + if (!current) { + curr_offset = 0; + } else { + curr_offset = (-timeline_layer->move_delta) * (REL_BAR_BACK_TO_BACK_OFFSET - curr_offset); + prev_offset = 0; + } + + GRect first_icon_frame; + GRect second_icon_frame; + const int line_length = (prv_get_line_length(timeline_layer, &first_icon_frame, + &second_icon_frame) - REL_BAR_LINE_WIDTH) / 2; + + // Draw two lines that are centered in the side bar + // Bar 1 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + graphics_context_set_fill_color(ctx, GColorWhite); + GRect layer_bounds = timeline_layer->layer.bounds; + GRect line; + line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; + // Account for the size of the icon when positioning vertically + line.origin.y = + grect_get_max_y(&first_icon_frame) + REL_BAR_VERT_MARGIN - curr_offset - prev_offset; + line.size.w = REL_BAR_LINE_WIDTH; + line.size.h = line_length + curr_offset; + graphics_fill_rect(ctx, &line); + + // Bottom Notch for Bar 1 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + GRect line2; + line2.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; + line2.size.h = REL_BAR_LINE_WIDTH; + line2.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; + line2.origin.y = grect_get_max_y(&line) - line2.size.h; + graphics_fill_rect(ctx, &line2); + + // Bar 2 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; + line.origin.y = second_icon_frame.origin.y - (REL_BAR_VERT_MARGIN + line_length) - prev_offset; + line.size.w = REL_BAR_LINE_WIDTH; + line.size.h = line_length - curr_offset; + graphics_fill_rect(ctx, &line); + + // Top Notch for Bar 2 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + line2.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; + line2.origin.y = line.origin.y; + line2.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; + line2.size.h = REL_BAR_LINE_WIDTH; + graphics_fill_rect(ctx, &line2); +} + +#define REL_BAR_DOT_SIZE 2 +static void prv_draw_rel_bar_dotted(TimelineLayer *timeline_layer, GContext* ctx, + bool current, int16_t rel_bar_value) { + int16_t prev_offset = 0; // Used to animate the previous animation offset + if (!current) { + prev_offset = REL_BAR_PREV_ANIM_OFFSET - rel_bar_value; + prev_offset = (timeline_layer->move_delta > 0) ? prev_offset : -prev_offset; + } + + int16_t curr_offset = rel_bar_value; // Used to animate the current animation offset + if ((current && (curr_offset <= 0)) || + ((!current) && ((prev_offset >= REL_BAR_PREV_ANIM_OFFSET) || + (prev_offset <= -REL_BAR_PREV_ANIM_OFFSET)))) { + return; + } + + + // Choose which offset to use based on current or previous animation + if (!current) { + curr_offset = 0; + } else { + curr_offset = (-timeline_layer->move_delta) * (REL_BAR_BACK_TO_BACK_OFFSET - curr_offset); + prev_offset = 0; + } + + GRect first_icon_frame; + GRect second_icon_frame; + const int line_length = + prv_get_line_length(timeline_layer, &first_icon_frame, &second_icon_frame) / 3; + const int dot_padding = 1; + const int solid_line_length = line_length - dot_padding; + const int dot_line_length = line_length + dot_padding; + + // Bar 1 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + graphics_context_set_fill_color(ctx, GColorWhite); + GRect layer_bounds = timeline_layer->layer.bounds; + GRect line; + line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; + // Account for the size of the icon when positioning vertically + line.origin.y = grect_get_max_y(&first_icon_frame) + + REL_BAR_VERT_MARGIN - curr_offset - prev_offset; + line.size.w = REL_BAR_LINE_WIDTH; + line.size.h = solid_line_length + curr_offset; + graphics_fill_rect(ctx, &line); + + // Bottom Notch for Bar 1 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + GRect notch; + notch.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; + notch.origin.y = grect_get_max_y(&line) - REL_BAR_LINE_WIDTH; + notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; + notch.size.h = REL_BAR_LINE_WIDTH; + graphics_fill_rect(ctx, ¬ch); + + const int dot_origin_y_max = + grect_get_max_y(&line) + dot_line_length + curr_offset - prev_offset; + // Dots in between two bars + GRect dot = GRect(line.origin.x, grect_get_max_y(&line) + REL_BAR_LINE_WIDTH + dot_padding, + REL_BAR_DOT_SIZE, REL_BAR_DOT_SIZE); + const int dot_advance = 2 * REL_BAR_DOT_SIZE; + for (; dot.origin.y + dot_advance <= dot_origin_y_max; dot.origin.y += dot_advance) { + graphics_fill_rect(ctx, &dot); + } + + // Bar 2 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + layer_bounds = timeline_layer->layer.bounds; + line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; + line.origin.y = dot.origin.y + dot_padding; + line.size.w = REL_BAR_LINE_WIDTH; + line.size.h = solid_line_length - curr_offset; + graphics_fill_rect(ctx, &line); + + // Top Notch for Bar 2 + // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width + notch.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; + notch.origin.y = line.origin.y; + notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; + notch.size.h = REL_BAR_LINE_WIDTH; + graphics_fill_rect(ctx, ¬ch); +} + +#define REL_BAR_OVERLAP_STROKE_WIDTH 2 +#define REL_BAR_OVERLAP_SIDE_MARGIN 2 +#define REL_BAR_OVERLAP_NUDGE_X 1 +#define REL_BAR_OVERLAP_LINE2_HORIZ_OFFSET ((2 * REL_BAR_OVERLAP_SIDE_MARGIN) + 1) +static void prv_draw_rel_bar_overlap(TimelineLayer *timeline_layer, GContext* ctx, + bool current, int16_t rel_bar_value) { + GRect first_icon_frame; + GRect second_icon_frame; + const int full_line_length = + prv_get_overlap_line_length(timeline_layer, &first_icon_frame, &second_icon_frame); + + int16_t line_length = rel_bar_value; + int16_t y_offset = 0; + if (!current) { + line_length = full_line_length; + y_offset = (((full_line_length - rel_bar_value) * REL_BAR_PREV_ANIM_OFFSET) / + full_line_length) * timeline_layer->move_delta; + } + + if (((line_length <= 0) && current) || + ((!current) && ((y_offset >= REL_BAR_PREV_ANIM_OFFSET) || + (y_offset <= -REL_BAR_PREV_ANIM_OFFSET)))) { + return; + } + + graphics_context_set_fill_color(ctx, GColorWhite); + graphics_context_set_antialiased(ctx, false); + GRect layer_bounds = timeline_layer->layer.bounds; + GPoint line1_start; + GPoint line2_start; + + // Draw down + line1_start.x = layer_bounds.origin.x + layer_bounds.size.w - (SIDEBAR_WIDTH / 2) - + REL_BAR_OVERLAP_NUDGE_X - REL_BAR_OVERLAP_SIDE_MARGIN; + line1_start.y = grect_get_max_y(&first_icon_frame) + REL_BAR_VERT_MARGIN - + y_offset + REL_BAR_LINE_WIDTH; + + graphics_fill_rect(ctx, &GRect(line1_start.x, line1_start.y, + REL_BAR_OVERLAP_STROKE_WIDTH, line_length)); + GRect notch = GRectZero; + notch.origin.x = line1_start.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; + notch.origin.y = line1_start.y - REL_BAR_LINE_WIDTH; + notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; + notch.size.h = REL_BAR_LINE_WIDTH; + graphics_fill_rect(ctx, ¬ch); + + // Draw up + line2_start.x = line1_start.x + REL_BAR_OVERLAP_LINE2_HORIZ_OFFSET; + line2_start.y = second_icon_frame.origin.y - REL_BAR_VERT_MARGIN - y_offset - + REL_BAR_LINE_WIDTH; + graphics_fill_rect(ctx, &GRect(line2_start.x, line2_start.y, + REL_BAR_OVERLAP_STROKE_WIDTH, -line_length)); + notch.origin.x = line2_start.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; + notch.origin.y = line2_start.y; + notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; + notch.size.h = REL_BAR_LINE_WIDTH; + graphics_fill_rect(ctx, ¬ch); +} + +#define NUM_REL_BAR_ANIMATIONS 2 +#define CURRENT_REL_BAR_PIN_INDEX 1 +static void prv_update_proc(Layer *layer, GContext *ctx) { + RelationshipBarLayer *relbar_layer = (RelationshipBarLayer *)layer; + TimelineLayer *timeline_layer = (TimelineLayer *)relbar_layer->timeline_layer; + // Don't draw the relationship bars if they are meant to be hidden + // Currently drawing relationship bar for future only - no plan for past + if (layer_get_hidden(&relbar_layer->layer) || + (timeline_layer->scroll_direction == TimelineScrollDirectionUp)) { + return; + } + + for (int index = 0; index < NUM_REL_BAR_ANIMATIONS; index++) { + bool current = (index == CURRENT_REL_BAR_PIN_INDEX); + RelationshipBarType rel_bar = current ? + timeline_layer->relbar_layer.curr_rel_bar.rel_bar_type : + timeline_layer->relbar_layer.prev_rel_bar.rel_bar_type; + int16_t rel_bar_value = current ? timeline_layer->relbar_layer.curr_rel_bar.anim_offset : + timeline_layer->relbar_layer.prev_rel_bar.anim_offset; + switch (rel_bar) { + case RelationshipBarTypeFreeTime: + // Draw dotted line + prv_draw_rel_bar_dotted(timeline_layer, ctx, current, rel_bar_value); + break; + case RelationshipBarTypeOverlap: + // Draw overlapping lines + prv_draw_rel_bar_overlap(timeline_layer, ctx, current, rel_bar_value); + break; + case RelationshipBarTypeBackToBack: + prv_draw_rel_bar_line(timeline_layer, ctx, current, rel_bar_value); + break; + case RelationshipBarTypeNone: + // Draw nothing + break; + } + } +} + +// Timer callback that displays the current relationship bar if user has stopped fast clicking +void prv_rel_bar_show(void *context) { + TimelineLayer *layer = (TimelineLayer *)context; + if (timeline_layer_should_animate_day_separator(layer)) { + prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeCurr); + layer_set_hidden(&layer->relbar_layer.layer, true); + return; + } + + layer_set_hidden(&layer->relbar_layer.layer, false); + if (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { + layer->relbar_layer.curr_rel_bar.anim_offset = prv_get_overlap_line_length(layer, NULL, NULL); + } else if ((layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || + (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { + layer->relbar_layer.curr_rel_bar.anim_offset = REL_BAR_BACK_TO_BACK_OFFSET; + } + + layer_mark_dirty((Layer*) layer); +} + + +#define TIMELINE_NUM_REL_BARS (TIMELINE_NUM_VISIBLE_ITEMS + 1) +static void prv_update_rel_bars(TimelineLayer *layer) { + // Store prev, current, and next item relationship bar types + RelationshipBarType rel_bar_types[TIMELINE_NUM_REL_BARS]; + + layer->relbar_layer.prev_rel_bar.rel_bar_type = layer->relbar_layer.curr_rel_bar.rel_bar_type; + + for (int index = 0; index < TIMELINE_NUM_REL_BARS; index++) { + if (layer->layouts_info[index]) { + rel_bar_types[index] = prv_get_pin_relationship(layer->layouts_info[index], + layer->layouts_info[index + 1]); + } + } + layer->relbar_layer.curr_rel_bar.rel_bar_type = rel_bar_types[1]; + if (layer->layouts_info[1]) { + PBL_LOG_DBG("Current rel bar %d, duration %"PRIu32, rel_bar_types[1], + layer->layouts_info[1]->duration_s); + } +} + +///////////////////////////////////////// +// Public functions +///////////////////////////////////////// +#define REL_BAR_TIMER_DELAY(delay) (delay / 3) // delay of timer is based on the duration +Animation *timeline_relbar_layer_create_animation(TimelineLayer *layer, uint32_t duration, + InterpolateInt64Function interpolate) { + bool force_rel_bar_anim = false; + // Cancel previous fast scroll timer since user has clicked again + evented_timer_cancel(layer->relbar_layer.rel_bar_timer); + if (timeline_animation_interpolate_moook_second_half == interpolate) { + force_rel_bar_anim = true; + if (duration == TIMELINE_UP_DOWN_ANIMATION_DURATION_MS) { + // Update current state of relationship bars + prv_update_rel_bars(layer); + + // Hide bars + layer_set_hidden(&layer->relbar_layer.layer, true); + + // Setup a timer to display bars after a fraction of the input duration (i.e. if the user + // has stopped scrolling fast) + layer->relbar_layer.rel_bar_timer = + evented_timer_register(REL_BAR_TIMER_DELAY(duration), false, prv_rel_bar_show, layer); + + // Don't schedule animation + return NULL; + } + } + + prv_update_rel_bars(layer); + + bool curr_anim_needed = true; + bool prev_anim_needed = true; + if (timeline_layer_should_animate_day_separator(layer)) { + curr_anim_needed = false; + prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeCurr); + layer_set_hidden(&layer->relbar_layer.layer, true); + } else { + layer_set_hidden(&layer->relbar_layer.layer, false); + } + + // This check is to ensure rel bar does not show up while day separator is on screen. + // The force animation is used to ensure the rel bar shows up after the day separator is hidden - + // timeline_create_up_down_animation is called both when sliding between timeline items as well as + // during the day separator animation. + if (force_rel_bar_anim) { + prev_anim_needed = false; + prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypePrev); + curr_anim_needed = true; + } + + // Create previous rel bar animation + Animation *prev_rel_bar_anim = NULL; + + if (prev_anim_needed) { + prev_rel_bar_anim = prv_create_prev_rel_bar_animation(layer, duration, interpolate); + } + + Animation *curr_rel_bar_anim = NULL; + + if (curr_anim_needed) { + curr_rel_bar_anim = prv_create_curr_rel_bar_animation(layer, duration, interpolate); + } + + Animation *rel_bar_anim = NULL; + if (prev_rel_bar_anim && curr_rel_bar_anim) { + rel_bar_anim = animation_spawn_create(prev_rel_bar_anim, curr_rel_bar_anim, NULL); + } else if (prev_rel_bar_anim) { + rel_bar_anim = prev_rel_bar_anim; + } else { + rel_bar_anim = curr_rel_bar_anim; + } + + if (rel_bar_anim) { + animation_set_handlers(rel_bar_anim, (AnimationHandlers) { + .stopped = prv_rel_bar_stopped, + }, layer); + } + + return rel_bar_anim; +} + +void timeline_relbar_layer_reset(TimelineLayer *layer) { + prv_update_rel_bars(layer); + if (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { + layer->relbar_layer.curr_rel_bar.anim_offset = prv_get_overlap_line_length(layer, NULL, NULL); + } else if ((layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || + (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { + layer->relbar_layer.curr_rel_bar.anim_offset = REL_BAR_BACK_TO_BACK_OFFSET; + } +} + +void timeline_relbar_layer_init(TimelineLayer *timeline_layer) { + RelationshipBarLayer *relbar_layer = &timeline_layer->relbar_layer; + *relbar_layer = (RelationshipBarLayer){}; + // init layer + layer_init(&relbar_layer->layer, &timeline_layer->layer.frame); + layer_set_update_proc(&relbar_layer->layer, prv_update_proc); + layer_add_child((Layer*)timeline_layer, (Layer *)&timeline_layer->relbar_layer); + relbar_layer->timeline_layer = timeline_layer; +} + +//! Deinitialize the timeline relationship bar layer within the \ref TimelineLayer +void timeline_relbar_layer_deinit(TimelineLayer *timeline_layer) { + layer_deinit(&timeline_layer->relbar_layer.layer); +} diff --git a/src/fw/apps/system/timeline/relbar.h b/src/fw/apps/system/timeline/relbar.h new file mode 100644 index 0000000000..83f8cf0133 --- /dev/null +++ b/src/fw/apps/system/timeline/relbar.h @@ -0,0 +1,29 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "layer.h" +#include "applib/ui/animation.h" + +typedef enum { + RelationshipBarOffsetTypePrev, + RelationshipBarOffsetTypeCurr, + RelationshipBarOffsetTypeBoth +} RelationshipBarOffsetType; + +//! Create the animations for the relationship bars +//! @param layer Pointer to the \ref TimelineLayer to create the relationship bar animation for +//! @param duration Duration in ms of the animation to create +//! @param interpolate Custom interpolation function to use for the animation +Animation *timeline_relbar_layer_create_animation(TimelineLayer *timeline_layer, uint32_t duration, + InterpolateInt64Function interpolate); + +//! Reset the relationship bar state +void timeline_relbar_layer_reset(TimelineLayer *timeline_layer); + +//! Initialize the timeline relationship bar layer within the \ref TimelineLayer +void timeline_relbar_layer_init(TimelineLayer *timeline_layer); + +//! Deinitialize the timeline relationship bar layer within the \ref TimelineLayer +void timeline_relbar_layer_deinit(TimelineLayer *timeline_layer); diff --git a/src/fw/apps/system_apps/timeline/text_node.c b/src/fw/apps/system/timeline/text_node.c similarity index 97% rename from src/fw/apps/system_apps/timeline/text_node.c rename to src/fw/apps/system/timeline/text_node.c index 07bd9694ff..26a890c93f 100644 --- a/src/fw/apps/system_apps/timeline/text_node.c +++ b/src/fw/apps/system/timeline/text_node.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "text_node.h" diff --git a/src/fw/apps/system_apps/timeline/text_node.h b/src/fw/apps/system/timeline/text_node.h similarity index 95% rename from src/fw/apps/system_apps/timeline/text_node.h rename to src/fw/apps/system/timeline/text_node.h index 67d9588af1..edf25d7301 100644 --- a/src/fw/apps/system_apps/timeline/text_node.h +++ b/src/fw/apps/system/timeline/text_node.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/apps/system/timeline/timeline.c b/src/fw/apps/system/timeline/timeline.c new file mode 100644 index 0000000000..62ae25bd8f --- /dev/null +++ b/src/fw/apps/system/timeline/timeline.c @@ -0,0 +1,1318 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pin_window.h" +#include "timeline.h" +#include "animations.h" +#include "model.h" + +#include "applib/app.h" +#include "applib/ui/animation_interpolate.h" +#include "applib/ui/animation_timing.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/kino/kino_reel/scale_segmented.h" +#include "applib/ui/kino/kino_reel/unfold.h" +#include "applib/ui/ui.h" +#include "drivers/rtc.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "process_management/app_manager.h" +#include "resource/resource_ids.auto.h" +#include "resource/timeline_resource_ids.auto.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/compositor/compositor_transitions.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/actions_endpoint.h" +#include "pbl/services/timeline/attribute.h" +#include "shell/normal/watchface.h" +#include "syscall/syscall.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/array.h" +#include "util/attributes.h" +#include "util/size.h" +#include "util/uuid.h" + +// This is used to determine whether this app was launched as Timeline or Timeline Past. +// See timeline_get_app_info, timeline_past_get_app_info, and the usage of sys_get_app_uuid. + +#if PBL_ROUND +#define ANIMATION_DOT 1 +#define ANIMATION_SLIDE 0 +#else +#define ANIMATION_DOT 0 +#define ANIMATION_SLIDE 1 +#endif + +typedef struct TimelineAppStyle { + int16_t peek_offset_y; + int16_t peek_icon_offset_y; +} TimelineAppStyle; + +static const TimelineAppStyle s_style_medium = { + .peek_icon_offset_y = PEEK_LAYER_ICON_OFFSET_Y, +}; + +static const TimelineAppStyle s_style_large = { + .peek_offset_y = -7, + .peek_icon_offset_y = -16, +}; + +static const TimelineAppStyle * const s_styles[NumPreferredContentSizes] = { + [PreferredContentSizeSmall] = &s_style_medium, + [PreferredContentSizeMedium] = &s_style_medium, + [PreferredContentSizeLarge] = &s_style_large, + [PreferredContentSizeExtraLarge] = &s_style_large, +}; + +static TimelineAppData *s_app_data; + +static const uint32_t TIMELINE_SLIDE_ANIMATION_MS = 150; +static const uint32_t PEEK_SHOW_TIME_MS = 660; + + +static const TimelineAppStyle *prv_get_style(void) { + return s_styles[PreferredContentSizeDefault]; +} + +///////////////////////////////////// +// State Machine +///////////////////////////////////// + +static bool prv_can_transition_state(TimelineAppData *data, TimelineAppState next_state) { + // all non-exit states can transition to exit + if (data->state != TimelineAppStateExit && + next_state == TimelineAppStateExit) { + return true; + } + switch (data->state) { + case TimelineAppStateNone: + return (next_state == TimelineAppStatePeek || + next_state == TimelineAppStateHidePeek || + next_state == TimelineAppStateFarDayHidePeek || + next_state == TimelineAppStateNoEvents); + case TimelineAppStatePeek: + return (next_state == TimelineAppStateHidePeek); + case TimelineAppStateHidePeek: + return (next_state == TimelineAppStateStationary); + case TimelineAppStateFarDayHidePeek: + return (next_state == TimelineAppStateDaySeparator); + case TimelineAppStateStationary: + return (next_state == TimelineAppStateUpDown || + next_state == TimelineAppStatePushCard || + next_state == TimelineAppStateNoEvents || + next_state == TimelineAppStateInactive); + case TimelineAppStateUpDown: + return (next_state == TimelineAppStateUpDown || + next_state == TimelineAppStateShowDaySeparator || + next_state == TimelineAppStateStationary); + case TimelineAppStateShowDaySeparator: + return (next_state == TimelineAppStateDaySeparator); + case TimelineAppStateDaySeparator: + return (next_state == TimelineAppStateHideDaySeparator); + case TimelineAppStateHideDaySeparator: + return (next_state == TimelineAppStateStationary); + case TimelineAppStatePushCard: + return (next_state == TimelineAppStateCard || + next_state == TimelineAppStatePopCard); + case TimelineAppStateCard: + return (next_state == TimelineAppStatePopCard || + next_state == TimelineAppStateStationary); + case TimelineAppStatePopCard: + return (next_state == TimelineAppStateStationary || + next_state == TimelineAppStatePushCard); + case TimelineAppStateNoEvents: + return (next_state == TimelineAppStateInactive); + case TimelineAppStateInactive: + case TimelineAppStateExit: + return false; + default: + WTF; + } +} + +static bool prv_set_state(TimelineAppData *data, TimelineAppState next_state) { + const bool can_transition = prv_can_transition_state(data, next_state); + PBL_LOG_DBG("state transition %d->%d valid:%d", + data->state, next_state, can_transition); + if (can_transition) { + data->state = next_state; + } + return can_transition; +} + +///////////////////////////////////// +// Exit Animation & Inactivity Timer +///////////////////////////////////// + +T_STATIC void prv_init_peek_layer(TimelineAppData *data); + +static void prv_launch_watchface(void *data) { +#ifdef CONFIG_SHELL_SDK + // FIXME: We don't want to show off our unfinished animations in the sdkshell + watchface_launch_default(NULL); +#else + + const bool is_future = (s_app_data->timeline_model.direction == TimelineIterDirectionFuture); + const bool to_timeline = false; + const CompositorTransition *transition = PBL_IF_RECT_ELSE( + compositor_slide_transition_timeline_get(is_future, to_timeline, timeline_model_is_empty()), + compositor_dot_transition_timeline_get(is_future, to_timeline)); + + watchface_launch_default(transition); +#endif +} + +// This function swaps from Timeline Future to Timeline Past when launching Timeline Full +// To be precise: Timeline Full behaves like Timeline Future, but instead of going back to the +// watchface when scrolling up, it will launch Timeline Past with a "force_full" argument. +// By doing so, Timeline Past will launch the Timeline Full (being Timeline Future) when scrolling +// down. +static void prv_swap_timeline(void *data) { + const bool is_future = (s_app_data->timeline_model.direction == TimelineIterDirectionFuture); + const bool to_timeline = true; + const CompositorTransition *transition = PBL_IF_RECT_ELSE( + compositor_slide_transition_timeline_get(!is_future, to_timeline, timeline_model_is_empty()), + compositor_dot_transition_timeline_get(!is_future, to_timeline)); + static TimelineArgs args = (TimelineArgs) { + .force_full = true + }; + args.force_display_day_sep_on_start = s_app_data->day_sep_displayed_on_start; + if (is_future) { + Uuid past_uuid = TIMELINE_PAST_UUID_INIT; + args.direction = TimelineIterDirectionPast; + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = app_install_get_id_for_uuid((const Uuid *)(&past_uuid)), + .common.args = (const void *)&args, + .common.transition = transition, + }); + } else { + Uuid app_uuid; + sys_get_app_uuid(&app_uuid); + const Uuid target_uuid = uuid_equal(&app_uuid, &(Uuid)TIMELINE_FULL_UUID_INIT) ? + (Uuid)TIMELINE_UUID_INIT : (Uuid)TIMELINE_FULL_UUID_INIT; + args.direction = TimelineIterDirectionFuture; + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = app_install_get_id_for_uuid((const Uuid *)&target_uuid), + .common.args = (const void *)&args, + .common.transition = transition, + }); + } +} + +static void prv_cleanup_timer(EventedTimerID *timer) { + if (evented_timer_exists(*timer)) { + evented_timer_cancel(*timer); + *timer = EVENTED_TIMER_INVALID_ID; + } +} + +#if ANIMATION_DOT +static void prv_exit_timer_callback(void *context) { + TimelineAppData *data = context; + data->timeline_layer.animating_intro_or_exit = false; + launcher_task_add_callback(prv_launch_watchface, data); +} +#endif + +static void prv_intro_or_exit_anim_started(Animation *anim, void *context) { + TimelineAppData *data = context; + data->timeline_layer.animating_intro_or_exit = true; +} + +#if ANIMATION_DOT +static void prv_exit_anim_stopped(Animation *animation, bool finished, void *context) { + // we must use a timer to allow the last frame to render + const int exit_timeout_ms = 2 * ANIMATION_TARGET_FRAME_INTERVAL_MS; + evented_timer_register(exit_timeout_ms, false, prv_exit_timer_callback, context); +} +#endif + +//! Used for setting the animation frame source and/or destination of the peek layer. +//! If use_pin is true, the animation frame size and position will be that of the first pin icon. +//! If shift_offscreen is true, the frame will be shifted by the screen row amount in a direction +//! depending on the scroll direction. +static void prv_get_icon_animation_frame(TimelineAppData *data, GRect *icon_frame_out, + bool use_pin, bool shift_offscreen) { +#if ANIMATION_DOT + const GRect *layer_frame = &data->timeline_window.layer.frame; + *icon_frame_out = (GRect) { + .origin.x = layer_frame->origin.x + (layer_frame->size.w - UNFOLD_DOT_SIZE_PX) / 2, + .origin.y = layer_frame->origin.y + (layer_frame->size.h - UNFOLD_DOT_SIZE_PX) / 2, + .size = UNFOLD_DOT_SIZE, + }; +#elif ANIMATION_SLIDE + GRect icon_frame; + TimelineLayout *first_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); + if (first_timeline_layout && use_pin) { + GRect frame; + timeline_layer_get_layout_frame(&data->timeline_layer, TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT, + &frame); + timeline_layout_get_icon_frame(&frame, data->timeline_layer.scroll_direction, &icon_frame); + } else { + // Since there is no pin, we need the peek size, which is the large size + icon_frame = (GRect) { .size = TIMELINE_LARGE_RESOURCE_SIZE }; + grect_align(&icon_frame, &data->peek_layer.layer.frame, GAlignCenter, false); + const TimelineAppStyle *style = prv_get_style(); + icon_frame.origin.y += style->peek_icon_offset_y; + } + if (shift_offscreen) { + if (data->timeline_model.direction == TimelineIterDirectionPast) { + icon_frame.origin.y -= DISP_ROWS; + } else { + icon_frame.origin.y += DISP_ROWS; + } + } + *icon_frame_out = icon_frame; +#endif +} + +#if ANIMATION_DOT +static Animation *prv_create_peek_exit_anim(TimelineAppData *data, TimelineAppState prev_state, + uint32_t duration) { + PeekLayer *peek_layer = &data->peek_layer; + if (prev_state == TimelineAppStateNoEvents || + prev_state == TimelineAppStatePeek || + prev_state == TimelineAppStateHidePeek) { + prv_cleanup_timer(&data->intro_timer_id); + } else if (prev_state == TimelineAppStateStationary || + prev_state == TimelineAppStateUpDown) { + TimelineLayout *first_timeline_layout = + timeline_layer_get_current_layout(&data->timeline_layer); + if (!first_timeline_layout) { + return NULL; + } + + prv_init_peek_layer(data); + + GRect icon_from; + layer_get_global_frame((Layer *)&first_timeline_layout->icon_layer, &icon_from); + + peek_layer_set_icon_with_size(peek_layer, &first_timeline_layout->icon_info, + TimelineResourceSizeTiny, icon_from); + } else { + return NULL; + } + + GRect icon_to; + const bool use_pin = true; + const bool shift_offscreen = true; + prv_get_icon_animation_frame(data, &icon_to, use_pin, shift_offscreen); + + peek_layer_clear_fields(peek_layer); + peek_layer_set_scale_to(peek_layer, icon_to); + peek_layer_set_duration(peek_layer, duration); + + // Play only a section to reduce the duration to the scaling, ignoring the PDCS duration + return (Animation *)peek_layer_create_play_section_animation(&data->peek_layer, 0, duration); +} +#endif + +static Animation *prv_create_sidebar_animation(TimelineAppData *data, bool open) { + int16_t to_sidebar_width; + if (open) { + to_sidebar_width = timeline_layer_get_ideal_sidebar_width(); + } else { + const GRect *layer_frame = &data->timeline_window.layer.frame; + to_sidebar_width = layer_frame->size.w; +#if PBL_ROUND + // Use a larger width to ensure we fill the entire screen since we use a circular background + to_sidebar_width += 25; +#endif + } + return timeline_layer_create_sidebar_animation(&data->timeline_layer, to_sidebar_width); +} + +static void prv_exit(TimelineAppData *data) { + PBL_UNUSED const TimelineAppState prev_state = data->state; + if (!prv_set_state(data, TimelineAppStateExit)) { + return; + } + +#if ANIMATION_SLIDE + prv_launch_watchface(data); +#elif ANIMATION_DOT + const uint32_t duration = interpolate_moook_in_duration(); + + animation_unschedule(data->current_animation); + layer_remove_child_layers((Layer *)&data->timeline_layer); + + Animation *sidebar_slide = prv_create_sidebar_animation(data, false /* open */); + animation_set_duration(sidebar_slide, duration); + animation_set_handlers(sidebar_slide, (AnimationHandlers) { + .started = prv_intro_or_exit_anim_started, + .stopped = prv_exit_anim_stopped, + }, data); + + Animation *peek_anim = prv_create_peek_exit_anim(data, prev_state, duration); + + // Just play them at the same time + animation_schedule(sidebar_slide); + if (peek_anim) { + animation_schedule(peek_anim); + } +#endif +} + +static void prv_inactive_timer_callack(void *data) { + prv_set_state(data, TimelineAppStateInactive); + prv_exit(data); +} + +static void prv_inactive_timer_refresh(TimelineAppData *data) { + static const uint32_t INACTIVITY_TIMEOUT_MS = 30 * 1000; + s_app_data->inactive_timer_id = evented_timer_register_or_reschedule( + s_app_data->inactive_timer_id, INACTIVITY_TIMEOUT_MS, prv_inactive_timer_callack, data); +} + +///////////////////////////////////// +// Pin View +///////////////////////////////////// + +static void prv_move_timeline_layer_stopped(Animation *animation, bool finished, void *context) { + TimelineAppData *data = context; + + // reset the timeline layer + data->timeline_layer.layer.bounds.origin.x = 0; + window_set_background_color(&data->timeline_window, GColorWhite); + + if (!finished) { + return; + } + + TimelineLayout *timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); + if (!timeline_layout) { + return; + } + + // cut to the card window + app_window_stack_push(&data->pin_window.window, false); + prv_set_state(data, TimelineAppStateCard); + + TimelineIterState *state = timeline_model_get_current_state(); + Uuid app_uuid; + timeline_get_originator_id(&state->pin, &app_uuid); +} + +static Animation *prv_animate_to_pin_window(TimelineAppData *data) { + Layer *layer = &data->timeline_layer.layer; + GPoint to_origin = GPoint(-layer->bounds.size.w, 0); + Animation *animation = (Animation *)property_animation_create_bounds_origin(layer, NULL, + &to_origin); + animation_set_handlers(animation, (AnimationHandlers) { + .stopped = prv_move_timeline_layer_stopped, + }, data); + animation_set_duration(animation, TIMELINE_CARD_TRANSITION_MS / 2); + animation_set_custom_interpolation(animation, interpolate_moook); + animation_schedule(animation); + return animation; +} + +static void prv_push_pin_window(TimelineAppData *data, TimelineIterState *state, bool animated) { + TimelineLayout *timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); + if (!timeline_layout) { + return; + } + + // Animation structure: + // - Scheduled simultaneously + // - Transition pin to card + // - Move timeline layer to the left + + animation_unschedule(data->current_animation); + + // initialize the pin window with the card layout + timeline_pin_window_init(&data->pin_window, &state->pin, state->current_day); + + // match the card background color + const LayoutColors *colors = layout_get_colors((LayoutLayer *)timeline_layout); + window_set_background_color(&data->timeline_window, colors->bg_color); + + // animate the card from the right + TimelineLayout *card_timeline_layout = data->pin_window.item_detail_layer.timeline_layout; + timeline_layout_transition_pin_to_card(timeline_layout, card_timeline_layout); + + // animate the timeline to the left + data->current_animation = prv_animate_to_pin_window(data); +} + +static bool prv_pin_in_card(TimelineAppData *data, Uuid *uuid) { + if (!app_window_stack_contains_window((Window *)&data->pin_window)) { + return false; + } + + TimelineIterState *current_state = timeline_model_get_current_state(); + if (current_state == NULL) { + return false; + } + + return uuid_equal(¤t_state->pin.header.id, uuid); +} + +static void prv_refresh_pin(TimelineAppData *data, int idx) { + PBL_ASSERTN(idx >= 0); + TimelineIterState *state = timeline_model_get_iter_state(idx); + timeline_iter_refresh_pin(state); + + if (idx == 0 && prv_pin_in_card(data, &state->pin.header.id)) { + timeline_pin_window_set_item(&data->pin_window, &state->pin, state->current_day); + } +} + +///////////////////////////////////// +// Timeline Controller +///////////////////////////////////// + +T_STATIC void prv_setup_no_events_peek(TimelineAppData *data); + +static void prv_update_timeline_layer(TimelineAppData *data) { + TimelineLayer *timeline_layer = &data->timeline_layer; + if (data->state != TimelineAppStateStationary && + data->state != TimelineAppStateUpDown && + data->state != TimelineAppStateCard) { + return; + } + animation_unschedule(data->current_animation); + data->current_animation = NULL; + timeline_layer_reset(timeline_layer); + + if (timeline_model_is_empty() && + prv_set_state(data, TimelineAppStateNoEvents)) { + // Hide layouts and animate to "No events" + timeline_layer_set_layouts_hidden(&data->timeline_layer, true); + + prv_init_peek_layer(data); + prv_setup_no_events_peek(data); + peek_layer_play(&data->peek_layer); + + Animation *sidebar_slide = prv_create_sidebar_animation(data, false /* open */); + data->current_animation = sidebar_slide; + animation_schedule(sidebar_slide); + } +} + +static void prv_back_click_handler(ClickRecognizerRef recognizer, void *context) { + TimelineAppData *data = context; + prv_exit(data); +} + +static void prv_up_down_stopped(Animation *animation, bool finished, void *context) { + TimelineAppData *data = context; + if (finished) { + prv_set_state(data, TimelineAppStateStationary); + } +} + +static void prv_hide_day_sep_stopped(Animation *animation, bool finished, void *context) { + TimelineAppData *data = context; + if (!finished || !prv_set_state(data, TimelineAppStateStationary)) { + return; + } + + data->current_animation = NULL; + prv_update_timeline_layer(data); + + Animation *move_animation = timeline_layer_create_up_down_animation( + &data->timeline_layer, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS / 2, + timeline_animation_interpolate_moook_second_half); + animation_set_handlers(move_animation, (AnimationHandlers) { + .stopped = prv_up_down_stopped, + }, data); + + data->current_animation = move_animation; + animation_schedule(move_animation); +} + +static void prv_hide_day_sep(void *context) { + TimelineAppData *data = context; + data->day_separator_timer_id = EVENTED_TIMER_INVALID_ID; + if (!prv_set_state(data, TimelineAppStateHideDaySeparator)) { + return; + } + + animation_unschedule(data->current_animation); + + Animation *day_sep_hide = timeline_layer_create_day_sep_hide(&data->timeline_layer); + animation_set_handlers(day_sep_hide, (AnimationHandlers){ + .stopped = prv_hide_day_sep_stopped, + }, data); + data->current_animation = day_sep_hide; + animation_schedule(day_sep_hide); +} + +static bool prv_attempt_hide_day_sep(TimelineAppData *data) { + if (data->state == TimelineAppStateDaySeparator) { + prv_cleanup_timer(&data->day_separator_timer_id); + prv_hide_day_sep(data); + return true; + } + return false; +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + TimelineAppData *data = context; + + prv_attempt_hide_day_sep(data); + + if (!prv_set_state(data, TimelineAppStatePushCard)) { + return; + } + + TimelineIterState *state = timeline_model_get_current_state(); + if (state) { + const bool animated = true; + prv_push_pin_window(data, state, animated); + } +} + +static void prv_set_day_sep_timer(TimelineAppData *data) { + const int DAY_SEP_TIMEOUT_MS = 1000; + data->day_separator_timer_id = evented_timer_register(DAY_SEP_TIMEOUT_MS, + false, + prv_hide_day_sep, + data); +} + +static void prv_day_sep_show_stopped(Animation *animation, bool finished, void *context) { + TimelineAppData *data = context; + if (!finished || !prv_set_state(data, TimelineAppStateDaySeparator)) { + return; + } + + // Pins will reappear after the day separator completes hiding in `prv_hide_day_sep_stopped` + timeline_layer_set_layouts_hidden(&data->timeline_layer, true); + + prv_set_day_sep_timer(data); +} + +static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { + TimelineAppData *data = context; + + prv_inactive_timer_refresh(data); + + ButtonId button = click_recognizer_get_button_id(recognizer); + const bool next = (button == BUTTON_ID_UP) ^ + (data->timeline_model.direction == TimelineIterDirectionFuture); + + // We want to know if it was stationary before transitioning + const bool was_stationary = (data->state == TimelineAppStateStationary); + + if (data->state == TimelineAppStateNoEvents) { + if (!next) { + if (data->full_app_view) { + // We don't want to exit by scroll backwards while in the full timeline + // We want to switch instead + prv_swap_timeline(data); + } else { + prv_exit(data); + } + } + return; // There are no events + } else if (prv_attempt_hide_day_sep(data)) { + return; // Successfully interrupted the day separator, let it hide + } else if (!prv_set_state(data, TimelineAppStateUpDown)) { + return; // Not in a state able to scroll at the moment + } + + animation_unschedule(data->current_animation); + + int new_idx; + bool has_new; + if (next) { + if (!timeline_model_iter_next(&new_idx, &has_new)) { + prv_set_state(data, TimelineAppStateStationary); + goto done; + } + if (has_new) { + timeline_layer_set_next_item(&data->timeline_layer, new_idx); + } + timeline_layer_move_data(&data->timeline_layer, 1); + } else { + if (!timeline_model_iter_prev(&new_idx, &has_new)) { + if (data->full_app_view) { + prv_swap_timeline(data); + } else { + prv_exit(data); + } + goto done; + } + if (has_new) { + timeline_layer_set_prev_item(&data->timeline_layer, new_idx); + } + timeline_layer_move_data(&data->timeline_layer, -1); + } + + // If we interrupted a previous scroll, hasten this scroll + const bool is_hasted = !was_stationary; + const uint32_t duration = TIMELINE_UP_DOWN_ANIMATION_DURATION_MS; + const InterpolateInt64Function interpolate = is_hasted ? + timeline_animation_interpolate_moook_second_half : + timeline_animation_interpolate_moook_soft; + Animation *move_animation = timeline_layer_create_up_down_animation( + &data->timeline_layer, duration, interpolate); + + if (timeline_layer_should_animate_day_separator(&data->timeline_layer) && + prv_set_state(data, TimelineAppStateShowDaySeparator)) { + Animation *day_sep_show = timeline_layer_create_day_sep_show(&data->timeline_layer); + move_animation = animation_spawn_create(move_animation, day_sep_show, NULL); + animation_set_handlers(move_animation, (AnimationHandlers) { + .stopped = prv_day_sep_show_stopped, + }, data); + } else { + animation_set_handlers(move_animation, (AnimationHandlers) { + .stopped = prv_up_down_stopped, + }, data); + } + + data->current_animation = move_animation; + animation_schedule(move_animation); + +done: + if (data->timeline_model.direction == TimelineIterDirectionPast) { + } else { + } +} + +static void prv_click_config_provider(void *context) { + TimelineAppData *data = (TimelineAppData *)context; + if (!data->full_app_view) { + // We don't want to be brought back to the watchface when using the full timeline + // i.e. if we started the app in the menu launcher + window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); + } + window_single_click_subscribe(BUTTON_ID_UP, prv_up_down_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_up_down_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); +} + +static void prv_blobdb_event_handler(PebbleEvent *event, void *context) { + TimelineAppData *data = context; + PebbleBlobDBEvent *blobdb_event = &event->blob_db; + if (blobdb_event->db_id != BlobDBIdPins) { + // we only care about pins + return; + } + + BlobDBEventType type = blobdb_event->type; + Uuid *id = (Uuid *)blobdb_event->key; + if (type == BlobDBEventTypeDelete) { + if (prv_pin_in_card(data, id)) { + // remove the pin window if we just removed the pin + app_window_stack_remove((Window *)&data->pin_window, false); + prv_set_state(data, TimelineAppStateStationary); + } + timeline_model_remove(id); + prv_update_timeline_layer(data); + } else if (type == BlobDBEventTypeInsert) { + for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { + if (timeline_model_get_iter_state(i)->node && + uuid_equal(&timeline_model_get_iter_state(i)->pin.header.id, id)) { + prv_refresh_pin(data, i); + } + } + prv_update_timeline_layer(data); + } +} + +///////////////////////////////////// +// Intro Animation +///////////////////////////////////// + +static void prv_intro_anim_stopped(Animation *anim, bool finished, void *context) { + TimelineAppData *data = context; + i18n_free_all(&data->peek_layer); + peek_layer_deinit(&data->peek_layer); + window_set_click_config_provider_with_context(&data->timeline_window, prv_click_config_provider, + data); + data->timeline_layer.animating_intro_or_exit = false; + + if (!finished || (!prv_set_state(s_app_data, TimelineAppStateStationary) && + !prv_set_state(s_app_data, TimelineAppStateDaySeparator))) { + return; + } + + data->current_animation = NULL; + prv_update_timeline_layer(data); + + if (data->state == TimelineAppStateDaySeparator) { + // Hidden until the day separator hide animation stops + timeline_layer_set_layouts_hidden(&data->timeline_layer, true); +#if ANIMATION_DOT + timeline_layer_unfold_day_sep(&data->timeline_layer); +#elif ANIMATION_SLIDE + timeline_layer_slide_day_sep(&data->timeline_layer); +#endif + prv_set_day_sep_timer(data); + } else { + GPoint direction = GPoint(0, -1); + Animation *layer_bounce = timeline_layer_create_bounce_back_animation(&data->timeline_layer, + direction); + data->current_animation = layer_bounce; + animation_schedule(layer_bounce); + } +} + +static Animation *prv_create_intro_animation(TimelineAppData *data, uint32_t duration, + bool was_mini_peek) { + // Animation structure: + // - Scheduled simultaneously + // - Spawn + // - Move peek layer to right (frame) + // - Resize sidebar from fullscreen to thin + // - After completion + // - Bounce back timeline pin layouts + // - Speed lines (if launching into a deep pin) + + // animate the peek layer to the right + GRect *start = &data->peek_layer.layer.frame; + GRect stop = { { was_mini_peek ? 0 : start->size.w , 0 }, start->size }; + Animation *peek_out = (Animation *)property_animation_create_layer_frame( + (Layer *)&data->peek_layer, start, &stop); + animation_set_duration(peek_out, duration); + animation_set_custom_interpolation(peek_out, interpolate_moook_in_only); + + // resize the sidebar from fullscreen to become thin on the right + Animation *sidebar_slide = prv_create_sidebar_animation(data, true /* open */); + animation_set_duration(sidebar_slide, duration); + + Animation *speed_lines = data->launch_into_deep_pin ? + timeline_layer_create_speed_lines_animation(&data->timeline_layer) : NULL; + + return animation_spawn_create(peek_out, sidebar_slide, speed_lines, NULL); +} + +#if ANIMATION_SLIDE +static void prv_play_peek_in(TimelineAppData *data) { + // Skip the first frame since the icon is offscreen + const int num_frames_skip = 1; + // The peek layer scale animation has a bounce back effect, so the icon reaches the destination + // if set to exactly the short moook in duration, so extend with more frames + const int num_frames_extend = 3; + const uint32_t duration = interpolate_moook_in_duration(); + peek_layer_set_duration(&data->peek_layer, duration + ((num_frames_skip + num_frames_extend) * + ANIMATION_TARGET_FRAME_INTERVAL_MS)); + Animation *animation = (Animation *)peek_layer_create_play_animation(&data->peek_layer); + animation_schedule(animation); + animation_set_elapsed(animation, num_frames_skip * ANIMATION_TARGET_FRAME_INTERVAL_MS); +} +#endif + +static void prv_scale_peek_to_first_pin_icon(TimelineAppData *data, uint32_t duration, + bool was_mini_peek) { + TimelineLayout *first_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); + if (!first_timeline_layout) { + return; + } + + // scale the peek layer icon to the pin position + GRect frame; + layer_get_global_frame((Layer *)first_timeline_layout, &frame); + GRect icon_to; + timeline_layout_get_icon_frame(&frame, data->timeline_layer.scroll_direction, &icon_to); + const bool align_in_frame = true; + PeekLayer *peek_layer = &data->peek_layer; + peek_layer_set_scale_to_image(peek_layer, &first_timeline_layout->icon_info, + TimelineResourceSizeTiny, icon_to, align_in_frame); + +#if ANIMATION_SLIDE + if (was_mini_peek) { + prv_play_peek_in(data); + return; + } +#endif + + peek_layer_set_duration(peek_layer, duration); + peek_layer_play(peek_layer); +} + +static void prv_intro_timer_callback(void *context) { + TimelineAppData *data = context; + data->intro_timer_id = EVENTED_TIMER_INVALID_ID; + + // if we are already hiding the peek, we were in a mini peek + const bool was_mini_peek = (data->state == TimelineAppStateHidePeek); + + prv_set_state(data, TimelineAppStateHidePeek); + + if (data->state != TimelineAppStateHidePeek && + data->state != TimelineAppStateFarDayHidePeek) { + return; + } + + // hide the peek text + PeekLayer *peek_layer = &data->peek_layer; + peek_layer_clear_fields(peek_layer); + + animation_unschedule(data->current_animation); + + const uint32_t duration = was_mini_peek ? interpolate_moook_in_duration() : + TIMELINE_SLIDE_ANIMATION_MS; + Animation *intro = prv_create_intro_animation(data, duration, was_mini_peek); + animation_set_handlers(intro, (AnimationHandlers) { + .started = prv_intro_or_exit_anim_started, + .stopped = prv_intro_anim_stopped, + }, data); + + data->current_animation = intro; + animation_schedule(intro); + + if (!layer_get_hidden((Layer *)&data->peek_layer)) { + prv_scale_peek_to_first_pin_icon(data, duration, was_mini_peek); + } +} + +static void prv_open_did_focus_handler(PebbleEvent *e, void *context) { + TimelineAppData *data = context; + event_service_client_unsubscribe(&data->focus_event_info); + prv_intro_timer_callback(data); +} + +static void prv_peek_did_focus_handler(PebbleEvent *e, void *context) { + TimelineAppData *data = context; + event_service_client_unsubscribe(&data->focus_event_info); + +#if ANIMATION_DOT + peek_layer_play(&data->peek_layer); +#elif ANIMATION_SLIDE + prv_play_peek_in(data); +#endif + + if (data->state == TimelineAppStateNoEvents) { + window_set_click_config_provider_with_context(&data->timeline_window, prv_click_config_provider, + data); + } else if (data->state == TimelineAppStatePeek && + data->intro_timer_id == EVENTED_TIMER_INVALID_ID) { + data->intro_timer_id = evented_timer_register(PEEK_SHOW_TIME_MS, + false, + prv_intro_timer_callback, + data); + } +} + +static void prv_setup_peek_animation(TimelineAppData *data, TimelineResourceInfo *timeline_res, + bool use_pin) { + PeekLayer *peek_layer = &data->peek_layer; +#if ANIMATION_DOT + peek_layer_set_icon(peek_layer, timeline_res); +#elif ANIMATION_SLIDE + GRect icon_from; + GRect icon_to; + const bool shift_offscreen_from = true; + const bool shift_offscreen_to = false; + prv_get_icon_animation_frame(data, &icon_from, use_pin, shift_offscreen_from); + prv_get_icon_animation_frame(data, &icon_to, use_pin, shift_offscreen_to); + peek_layer_set_icon_with_size(peek_layer, timeline_res, TimelineResourceSizeLarge, icon_from); + peek_layer_set_scale_to(peek_layer, icon_to); + peek_layer_set_fields_hidden(peek_layer, true); +#endif +} + +T_STATIC void prv_setup_no_events_peek(TimelineAppData *data) { + PeekLayer *peek_layer = &data->peek_layer; + // set the text + peek_layer_set_fields(peek_layer, "", i18n_get("No events", peek_layer), ""); + // set the icon resource + TimelineResourceInfo timeline_res = { + .res_id = TIMELINE_RESOURCE_NO_EVENTS, + }; + const bool use_pin = false; + prv_setup_peek_animation(data, &timeline_res, use_pin); +} + +static void prv_setup_first_pin_peek(TimelineAppData *data) { + TimelineIterState *state = timeline_model_get_current_state(); + // TODO: PBL-22075 Refactor Timeline Model + // timeline_model_get_current_state explicitly tries to return NULL when supposedly empty, + // but this does not seem to actually happen + if (!state) { + return; + } + + TimelineItem *first_pin = &state->pin; + if (!first_pin) { + return; + } + + TimelineLayout *first_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); + if (!first_timeline_layout) { + return; + } + + PeekLayer *peek_layer = &s_app_data->peek_layer; + + // if we are hiding the peek, we are in a mini peek + const bool is_mini_peek = (data->state == TimelineAppStateHidePeek); + + // set the text + char number_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; // "11" + char word_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; // "min to" + if (!is_mini_peek) { + clock_get_event_relative_time_string( + number_buffer, sizeof(number_buffer), word_buffer, sizeof(word_buffer), + first_pin->header.timestamp, first_pin->header.duration, state->current_day, + first_pin->header.all_day); + } + peek_layer_set_fields(peek_layer, number_buffer, word_buffer, ""); + + // set the icon + if (is_mini_peek) { + GRect icon_from; + const bool shift_offscreen = true; + const bool use_pin = true; + prv_get_icon_animation_frame(data, &icon_from, use_pin, shift_offscreen); + peek_layer_set_icon_with_size(peek_layer, &first_timeline_layout->icon_info, + TimelineResourceSizeTiny, icon_from); + } else { + const bool use_pin = false; + prv_setup_peek_animation(data, &first_timeline_layout->icon_info, use_pin); + } +} + +static void NOINLINE prv_setup_peek(TimelineAppData *data) { + TimelineIterState *state = timeline_model_get_current_state(); + TimelineItem *first_pin = state ? &state->pin : NULL; + EventServiceEventHandler focus_handler = prv_open_did_focus_handler; + + // we'll only show the first pin peek if timeline peek (aka quick view) isn't enabled + time_t now = rtc_get_time(); + if (!first_pin && prv_set_state(s_app_data, TimelineAppStateNoEvents)) { + layer_set_hidden((Layer *)&data->peek_layer, false); + prv_setup_no_events_peek(data); + focus_handler = prv_peek_did_focus_handler; + } else if (state && ((state->current_day != time_util_get_midnight_of(now)) || (data->force_display_day_sep == true)) && + prv_set_state(data, TimelineAppStateFarDayHidePeek)) { + // entering into a day that isn't today, setup the day separator + // If switching from Past to Future or Future to Past, + // this flag ensures that the next switch will display the day + // separator + data->day_sep_displayed_on_start = true; + + layer_set_hidden((Layer *)&data->peek_layer, true); +#if ANIMATION_DOT + timeline_layer_set_day_sep_frame(&data->timeline_layer, &data->timeline_layer.layer.frame); +#elif ANIMATION_SLIDE + GRect frame; + layer_get_frame(&data->timeline_layer.day_separator.layer, &frame); + const bool is_future = (s_app_data->timeline_model.direction == TimelineIterDirectionFuture); + frame.origin.y += is_future ? DISP_ROWS : -DISP_ROWS; + timeline_layer_set_day_sep_frame(&data->timeline_layer, &frame); +#endif + focus_handler = prv_open_did_focus_handler; + } else if (prv_set_state(data, TimelineAppStateHidePeek)) { + // setup mini-peek where the icon animates directly into the pin position + prv_setup_first_pin_peek(data); + focus_handler = prv_open_did_focus_handler; + } + + // set the did_focus handler + data->focus_event_info = (EventServiceInfo) { + .type = PEBBLE_APP_DID_CHANGE_FOCUS_EVENT, + .handler = focus_handler, + .context = s_app_data, + }; + event_service_client_subscribe(&s_app_data->focus_event_info); +} + +#if PBL_COLOR +static GColor prv_get_sidebar_color(TimelineAppData *data) { + if (s_app_data->timeline_model.direction == TimelineIterDirectionPast) { + return TIMELINE_PAST_COLOR; + } else { + return TIMELINE_FUTURE_COLOR; + } +} +#endif + +T_STATIC void prv_init_peek_layer(TimelineAppData *data) { + Window *window = &data->timeline_window; + PeekLayer *peek_layer = &data->peek_layer; + const TimelineAppStyle *style = prv_get_style(); + const GRect frame = { .origin.y = style->peek_offset_y, .size = window->layer.bounds.size }; + peek_layer_init(peek_layer, &frame); + peek_layer_set_icon_offset_y(peek_layer, style->peek_icon_offset_y); + peek_layer_set_frame(peek_layer, &frame); + peek_layer_set_background_color(peek_layer, GColorClear); + layer_add_child(&window->layer, &peek_layer->layer); +} + +static void prv_timeline_window_load(Window *window) { + TimelineLayer *timeline_layer = &s_app_data->timeline_layer; + TimelineScrollDirection scroll_direction; + if (s_app_data->timeline_model.direction == TimelineIterDirectionPast) { + scroll_direction = TimelineScrollDirectionUp; + } else { + scroll_direction = TimelineScrollDirectionDown; + } + + window_set_background_color(window, GColorWhite); + + // timeline layer + timeline_layer_init(timeline_layer, &window->layer.bounds, scroll_direction); + timeline_layer_set_sidebar_color(timeline_layer, + PBL_IF_COLOR_ELSE(prv_get_sidebar_color(s_app_data), + GColorLightGray)); + timeline_layer_set_layouts_hidden(timeline_layer, true); // hide until the peek is over + layer_set_hidden((Layer *)&timeline_layer->day_separator, true); + layer_add_child(&window->layer, (Layer *)timeline_layer); + + // peek layer + prv_init_peek_layer(s_app_data); + prv_setup_peek(s_app_data); +} + +static void prv_timeline_window_appear(Window *window) { + TimelineAppData *data = window_get_user_data(window); + + // re-enable the inactivity timer back in timeline view + prv_inactive_timer_refresh(data); +} + +static void prv_timeline_window_disappear(Window *window) { + TimelineAppData *data = window_get_user_data(window); + + // disable the inactivity timer when the user leaves + prv_cleanup_timer(&data->inactive_timer_id); +} + +static void prv_timeline_window_unload(Window *window) { + TimelineAppData *data = window_get_user_data(window); + + // clean up any running animations + animation_unschedule(data->current_animation); + prv_cleanup_timer(&data->day_separator_timer_id); +} + +static void prv_back_from_card_stopped(Animation *animation, bool finished, void *context) { + TimelineAppData *data = context; + if (!finished || !prv_set_state(data, TimelineAppStateStationary)) { + return; + } + + window_set_background_color(&data->timeline_window, GColorWhite); + + data->current_animation = NULL; + prv_update_timeline_layer(data); + + Animation *layer_bounce = timeline_layer_create_bounce_back_animation(&data->timeline_layer, + GPoint(1, 0)); + + data->current_animation = layer_bounce; + animation_schedule(layer_bounce); +} + +///////////////////////////////////// +// Public API +///////////////////////////////////// + +Animation *timeline_animate_back_from_card(void) { + TimelineAppData *data = s_app_data; + PBL_ASSERTN(data); + + if (!prv_set_state(data, TimelineAppStatePopCard)) { + return NULL; + } + + // Animation structure: + // - Scheduled simultaneously + // - Transition card to pin + // - Move timeline layer from the left + // - After completion + // - Bounce back the timeline layer + // - Move pin window to the right + + animation_unschedule(data->current_animation); + + timeline_layer_set_layouts_hidden(&data->timeline_layer, true); + window_set_background_color(&data->timeline_window, GColorWhite); + + + TimelineLayout *pin_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); + if (pin_timeline_layout) { + // animation the pin icon + TimelineItemLayer *item_layer = &data->pin_window.item_detail_layer; + timeline_layout_transition_card_to_pin(item_layer->timeline_layout, pin_timeline_layout); + } + + // animate the timeline layer from the left + Layer *layer = &data->timeline_layer.layer; + GPoint from_origin = { -layer->bounds.size.w, 0 }; + Animation *layer_in = (Animation *)property_animation_create_bounds_origin(layer, &from_origin, + &GPointZero); + animation_set_duration(layer_in, TIMELINE_CARD_TRANSITION_MS / 2); + animation_set_custom_interpolation(layer_in, interpolate_moook); + animation_set_handlers(layer_in, (AnimationHandlers) { + .stopped = prv_back_from_card_stopped, + }, data); + + data->current_animation = layer_in; + animation_schedule(layer_in); + + // animate the card layout + timeline_pin_window_pop(&data->pin_window); + + return layer_in; +} + +///////////////////////////////////// +// App boilerplate +///////////////////////////////////// + +static bool NOINLINE prv_setup_timeline_app(void) { + TimelineAppData *data = app_malloc_check(sizeof(TimelineAppData)); + s_app_data = data; + *data = (TimelineAppData){}; + + data->blobdb_event_info = (EventServiceInfo) { + .type = PEBBLE_BLOBDB_EVENT, + .handler = prv_blobdb_event_handler, + .context = data, + }; + event_service_client_subscribe(&data->blobdb_event_info); + + const TimelineArgs *args = process_manager_get_current_process_args(); + Uuid app_uuid; + sys_get_app_uuid(&app_uuid); + + if (uuid_equal(&app_uuid, &(Uuid)TIMELINE_FULL_UUID_INIT) || (args && args->force_full)) { + // Activating the full app flag if we entered this code using "timeline full" UUID, + // or if the argument force_full is set. + data->full_app_view = true; + if (args != NULL) { + data->force_display_day_sep = args->force_display_day_sep_on_start; + } else { + data->force_display_day_sep = false; + } + } + + if (uuid_equal(&app_uuid, &(Uuid)TIMELINE_PAST_UUID_INIT)) { + data->timeline_model.direction = TimelineIterDirectionPast; + } else if (args == NULL) { + data->timeline_model.direction = TimelineIterDirectionFuture; + } else { + data->timeline_model.direction = args->direction; + } + + // check if we were asked to launch into a specific item + time_t now = rtc_get_time(); + TimelineItem pin; + bool launch_into_pin = false; + if (args && args->launch_into_pin && + !uuid_is_invalid(&args->pin_id) && + pin_db_get(&args->pin_id, &pin) == S_SUCCESS) { + launch_into_pin = true; + if (!args->stay_in_list_view) { + // Launching directly into the pin, change the direction to match + data->timeline_model.direction = + timeline_direction_for_item(&pin, data->timeline_model.timeline, now); + } + timeline_item_free_allocated_buffer(&pin); + } + + timeline_model_init(now, &data->timeline_model); + + // if we're launching into a particular item, we iterate to it now + if (launch_into_pin) { + while (!uuid_equal(&timeline_model_get_current_state()->pin.header.id, &args->pin_id)) { + data->launch_into_deep_pin = true; + if (!timeline_model_iter_next(NULL, NULL)) { + // for some reason we can't find the pin we were asked to launch into + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&args->pin_id, uuid_buffer); + PBL_LOG_ERR("Asked to launch into pin but can't find it %s", uuid_buffer); + launch_into_pin = false; + data->launch_into_deep_pin = false; + // we couldn't find the launch pin, go back to the present + while (timeline_model_iter_prev(NULL, NULL)) {} + break; + } + } + } + + Window *window = &data->timeline_window; + window_init(window, WINDOW_NAME("Timeline")); + window_set_user_data(window, data); + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_timeline_window_load, + .appear = prv_timeline_window_appear, + .disappear = prv_timeline_window_disappear, + .unload = prv_timeline_window_unload + }); + + return (launch_into_pin && !(args && args->stay_in_list_view)); +} + +T_STATIC void NOINLINE prv_init(void) { + bool do_push_pin_window = prv_setup_timeline_app(); + + app_window_stack_push(&s_app_data->timeline_window, true /* animated */); + + if (do_push_pin_window) { + prv_push_pin_window(s_app_data, timeline_model_get_current_state(), false /* animated */); + } + + if (!timeline_model_is_empty()) { + timeline_layer_set_sidebar_width(&s_app_data->timeline_layer, + timeline_layer_get_ideal_sidebar_width()); + } +} + +static void NOINLINE prv_deinit(void) { + prv_cleanup_timer(&s_app_data->intro_timer_id); + prv_cleanup_timer(&s_app_data->inactive_timer_id); + + event_service_client_unsubscribe(&s_app_data->focus_event_info); + event_service_client_unsubscribe(&s_app_data->blobdb_event_info); + + timeline_layer_deinit(&s_app_data->timeline_layer); + timeline_model_deinit(); + app_free(s_app_data); +} + +static void prv_main(void) { + prv_init(); + + app_event_loop(); + + prv_deinit(); +} + +const PebbleProcessMd *timeline_get_app_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = prv_main, + // uuid: 79C76B48-6111-4E80-8DEB-3119EEBEF33E + .uuid = TIMELINE_UUID_INIT, + .visibility = ProcessVisibilityQuickLaunch, + }, + .name = i18n_noop("Timeline Future"), + }; + return &s_app_md.common; +} + +const PebbleProcessMd *timeline_past_get_app_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = prv_main, + .uuid = TIMELINE_PAST_UUID_INIT, + .visibility = ProcessVisibilityQuickLaunch, + }, + /// The title of Timeline Past in Quick Launch. If the translation is too long, cut out + /// Timeline and only translate "Past". + .name = i18n_noop("Timeline Past"), + }; + return &s_app_md.common; +} + +const PebbleProcessMd *timeline_full_get_app_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + .main_func = prv_main, + .uuid = TIMELINE_FULL_UUID_INIT, + .visibility = ProcessVisibilityShown, + }, + .name = i18n_noop("Timeline"), + .icon_resource_id = RESOURCE_ID_DAY_SEPARATOR_TINY, + }; + return &s_app_md.common; +} \ No newline at end of file diff --git a/src/fw/apps/system/timeline/timeline.h b/src/fw/apps/system/timeline/timeline.h new file mode 100644 index 0000000000..234312749c --- /dev/null +++ b/src/fw/apps/system/timeline/timeline.h @@ -0,0 +1,91 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "peek_layer.h" +#include "pin_window.h" +#include "model.h" +#include "layer.h" + +#include "applib/ui/action_menu_layer.h" +#include "applib/ui/ui.h" +#include "popups/timeline/timeline_item_layer.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/timeline/timeline.h" + +typedef enum { + TimelineAppStateNone = 0, + TimelineAppStatePeek, + TimelineAppStateHidePeek, + TimelineAppStateStationary, + TimelineAppStateUpDown, + TimelineAppStateFarDayHidePeek, + TimelineAppStateShowDaySeparator, + TimelineAppStateDaySeparator, + TimelineAppStateHideDaySeparator, + TimelineAppStatePushCard, + TimelineAppStateCard, + TimelineAppStatePopCard, + TimelineAppStateNoEvents, + TimelineAppStateInactive, + TimelineAppStateExit, +} TimelineAppState; + +typedef struct { + TimelineDirection direction; + bool launch_into_pin; //!< Launch to a pin specified by `pin_id`. + bool stay_in_list_view; //!< Whether to stay in list view or launch into the detail view. + Uuid pin_id; + bool force_full; + bool force_display_day_sep_on_start; +} TimelineArgs; + +typedef struct { + // Windows + Window timeline_window; + TimelinePinWindow pin_window; + + // Layers + TimelineLayer timeline_layer; + PeekLayer peek_layer; + + EventServiceInfo blobdb_event_info; + EventServiceInfo focus_event_info; + + EventedTimerID inactive_timer_id; //!< To go back to watchface after inactivity + EventedTimerID intro_timer_id; //!< To perform the intro animation after a peek + EventedTimerID day_separator_timer_id; //!< To hide the day separator after a moment + + TimelineModel timeline_model; + + Animation *current_animation; + + TimelineAppState state; + + bool launch_into_deep_pin; //!< Whether we launched directly into a pin that isn't the first + bool in_pin_view; //!< Whether we're in pin view + bool full_app_view; //!< Whether we launched as full app mode + bool day_sep_displayed_on_start; + bool force_display_day_sep; +} TimelineAppData; + +Animation *timeline_animate_back_from_card(void); + +// uuid: 79C76B48-6111-4E80-8DEB-3119EEBEF33E +#define TIMELINE_UUID_INIT {0x79, 0xC7, 0x6B, 0x48, 0x61, 0x11, 0x4E, 0x80, \ + 0x8D, 0xEB, 0x31, 0x19, 0xEE, 0xBE, 0xF3, 0x3E} + +// uuid: DAAE3686-BFF6-4BA5-921B-262F847BB6E8 +#define TIMELINE_PAST_UUID_INIT {0xDA, 0xAE, 0x36, 0x86, 0xBF, 0xF6, 0x4B, 0xA5, \ + 0x92, 0x1B, 0x26, 0x2F, 0x84, 0x7B, 0xB6, 0xE8} + +// uuid: 426ccd53-b380-4d83-8d06-9893de3477ce +#define TIMELINE_FULL_UUID_INIT {0x42, 0x6c, 0xcd, 0x53, 0xb3, 0x80, 0x4d, 0x83, \ + 0x8d, 0x06, 0x98, 0x93, 0xde, 0x34, 0x77, 0xce} + +const PebbleProcessMd *timeline_get_app_info(void); +const PebbleProcessMd *timeline_past_get_app_info(void); +const PebbleProcessMd *timeline_full_get_app_info(void); \ No newline at end of file diff --git a/src/fw/apps/system/toggle/airplane_mode.c b/src/fw/apps/system/toggle/airplane_mode.c new file mode 100644 index 0000000000..cf7abdc63f --- /dev/null +++ b/src/fw/apps/system/toggle/airplane_mode.c @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "airplane_mode.h" + +#include "applib/app.h" +#include "applib/ui/action_toggle.h" +#include "process_management/app_manager.h" +#include "pbl/services/bluetooth/bluetooth_ctl.h" +#include "pbl/services/i18n/i18n.h" + +static bool prv_get_state(void *context) { + return bt_ctl_is_airplane_mode_on(); +} + +static void prv_set_state(bool enabled, void *context) { + bt_ctl_set_airplane_mode_async(!bt_ctl_is_airplane_mode_on()); +} + +static const ActionToggleImpl s_airplane_mode_action_toggle_impl = { + .window_name = "Airplane Mode Toggle", + .prompt_icon = RESOURCE_ID_AIRPLANE, + .result_icon = RESOURCE_ID_AIRPLANE, + // Toggling airplane mode involves locks which can block animation, don't animate + .result_icon_static = true, + .prompt_enable_message = i18n_noop("Turn On Airplane Mode?"), + .prompt_disable_message = i18n_noop("Turn Off Airplane Mode?"), + .result_enable_message = i18n_noop("Airplane\nMode On"), + .result_disable_message = i18n_noop("Airplane\nMode Off"), + .callbacks = { + .get_state = prv_get_state, + .set_state = prv_set_state, + }, +}; + +static void prv_main(void) { + action_toggle_push(&(ActionToggleConfig) { + .impl = &s_airplane_mode_action_toggle_impl, + .set_exit_reason = true, + }); + app_event_loop(); +} + +const PebbleProcessMd *airplane_mode_toggle_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &prv_main, + .uuid = AIRPLANE_MODE_TOGGLE_UUID, + .visibility = ProcessVisibilityQuickLaunch, + }, + .name = i18n_noop("Airplane Mode"), + }; + return &s_app_info.common; +} diff --git a/src/fw/apps/system/toggle/airplane_mode.h b/src/fw/apps/system/toggle/airplane_mode.h new file mode 100644 index 0000000000..cf1efc8c7d --- /dev/null +++ b/src/fw/apps/system/toggle/airplane_mode.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_manager.h" + +#define AIRPLANE_MODE_TOGGLE_UUID {0x88, 0xc2, 0x8c, 0x12, 0x7f, 0x81, 0x42, 0xdb, \ + 0xaa, 0xa6, 0x14, 0xcc, 0xef, 0x6f, 0x27, 0xe5} + +const PebbleProcessMd *airplane_mode_toggle_get_app_info(void); diff --git a/src/fw/apps/system/toggle/backlight_state.c b/src/fw/apps/system/toggle/backlight_state.c new file mode 100644 index 0000000000..31ddcabd42 --- /dev/null +++ b/src/fw/apps/system/toggle/backlight_state.c @@ -0,0 +1,55 @@ +/* SPDX-FileCopyrightText: 2025 Elad Dvash */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "backlight_state.h" + +#include "applib/app.h" +#include "applib/ui/action_toggle.h" +#include "process_management/app_manager.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "shell/prefs.h" + +static bool prv_get_state(void *context) { + return backlight_is_enabled(); +} + +static void prv_set_state(bool enabled, void *context) { + if (enabled != backlight_is_enabled()) { + light_toggle_enabled(); + } +} + +static const ActionToggleImpl s_backlight_state_action_toggle_impl = { + .window_name = "Backlight Toggle", + .prompt_icon = RESOURCE_ID_BACKLIGHT, + .result_icon = RESOURCE_ID_BACKLIGHT, + .prompt_enable_message = i18n_noop("Turn On Backlight?"), + .prompt_disable_message = i18n_noop("Turn Off Backlight?"), + .result_enable_message = i18n_noop("Backlight On"), + .result_disable_message = i18n_noop("Backlight Off"), + .callbacks = { + .get_state = prv_get_state, + .set_state = prv_set_state, + }, +}; + +static void prv_main(void) { + action_toggle_push(&(ActionToggleConfig) { + .impl = &s_backlight_state_action_toggle_impl, + .set_exit_reason = true, + }); + app_event_loop(); +} + +const PebbleProcessMd *backlight_state_toggle_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &prv_main, + .uuid = BACKLIGHT_STATE_TOGGLE_UUID, + .visibility = ProcessVisibilityQuickLaunch, + }, + .name = i18n_noop("Backlight"), + }; + return &s_app_info.common; +} diff --git a/src/fw/apps/system/toggle/backlight_state.h b/src/fw/apps/system/toggle/backlight_state.h new file mode 100644 index 0000000000..83c71d69ee --- /dev/null +++ b/src/fw/apps/system/toggle/backlight_state.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2025 Elad Dvash */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_manager.h" + +#define BACKLIGHT_STATE_TOGGLE_UUID {0xd0, 0xf1, 0x2e, 0x6c, 0x97, 0xeb, 0x22, 0x87, \ + 0xa2, 0xf5, 0x11, 0x5d, 0xfa, 0xA1, 0xd1, 0x68} + +const PebbleProcessMd *backlight_state_toggle_get_app_info(void); diff --git a/src/fw/apps/system/toggle/motion_backlight.c b/src/fw/apps/system/toggle/motion_backlight.c new file mode 100644 index 0000000000..142cc60eaa --- /dev/null +++ b/src/fw/apps/system/toggle/motion_backlight.c @@ -0,0 +1,52 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "motion_backlight.h" + +#include "applib/app.h" +#include "applib/ui/action_toggle.h" +#include "process_management/app_manager.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/prefs.h" + +static bool prv_get_state(void *context) { + return backlight_is_motion_enabled(); +} + +static void prv_set_state(bool enabled, void *context) { + backlight_set_motion_enabled(enabled); +} + +static const ActionToggleImpl s_motion_backlight_action_toggle_impl = { + .window_name = "Motion Backlight Toggle", + .prompt_icon = RESOURCE_ID_BACKLIGHT, + .result_icon = RESOURCE_ID_BACKLIGHT, + .prompt_enable_message = i18n_noop("Turn On Motion Backlight?"), + .prompt_disable_message = i18n_noop("Turn Off Motion Backlight?"), + .result_enable_message = i18n_noop("Motion\nBacklight On"), + .result_disable_message = i18n_noop("Motion\nBacklight Off"), + .callbacks = { + .get_state = prv_get_state, + .set_state = prv_set_state, + }, +}; + +static void prv_main(void) { + action_toggle_push(&(ActionToggleConfig) { + .impl = &s_motion_backlight_action_toggle_impl, + .set_exit_reason = true, + }); + app_event_loop(); +} + +const PebbleProcessMd *motion_backlight_toggle_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &prv_main, + .uuid = MOTION_BACKLIGHT_TOGGLE_UUID, + .visibility = ProcessVisibilityQuickLaunch, + }, + .name = i18n_noop("Motion Backlight"), + }; + return &s_app_info.common; +} diff --git a/src/fw/apps/system/toggle/motion_backlight.h b/src/fw/apps/system/toggle/motion_backlight.h new file mode 100644 index 0000000000..80e3387624 --- /dev/null +++ b/src/fw/apps/system/toggle/motion_backlight.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_manager.h" + +#define MOTION_BACKLIGHT_TOGGLE_UUID {0xd4, 0xf7, 0xbe, 0x63, 0x97, 0xe6, 0x49, 0x52, \ + 0xb2, 0x65, 0xdd, 0x4b, 0xce, 0x11, 0xc1, 0x55} + +const PebbleProcessMd *motion_backlight_toggle_get_app_info(void); diff --git a/src/fw/apps/system/toggle/quiet_time.c b/src/fw/apps/system/toggle/quiet_time.c new file mode 100644 index 0000000000..9fc71561c2 --- /dev/null +++ b/src/fw/apps/system/toggle/quiet_time.c @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "quiet_time.h" + +#include "applib/app.h" +#include "applib/ui/action_toggle.h" +#include "process_management/app_manager.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/notifications/do_not_disturb_toggle.h" + +static void prv_main(void) { + do_not_disturb_toggle_push(ActionTogglePrompt_Auto, true /* set_exit_reason */); + app_event_loop(); +} + +const PebbleProcessMd *quiet_time_toggle_get_app_info(void) { + static const PebbleProcessMdSystem s_app_info = { + .common = { + .main_func = &prv_main, + .uuid = QUIET_TIME_TOGGLE_UUID, + .visibility = ProcessVisibilityQuickLaunch, + }, + .name = i18n_noop("Quiet Time"), + }; + return &s_app_info.common; +} diff --git a/src/fw/apps/system/toggle/quiet_time.h b/src/fw/apps/system/toggle/quiet_time.h new file mode 100644 index 0000000000..4bede675bf --- /dev/null +++ b/src/fw/apps/system/toggle/quiet_time.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/app_manager.h" + +#define QUIET_TIME_TOGGLE_UUID {0x22, 0x20, 0xd8, 0x05, 0xcf, 0x9a, 0x4e, 0x12, \ + 0x92, 0xb9, 0x5c, 0xa7, 0x78, 0xaf, 0xf6, 0xbb} + +const PebbleProcessMd *quiet_time_toggle_get_app_info(void); diff --git a/src/fw/apps/system_apps/watchfaces.c b/src/fw/apps/system/watchfaces.c similarity index 90% rename from src/fw/apps/system_apps/watchfaces.c rename to src/fw/apps/system/watchfaces.c index 2dbb63b961..1b9f8b05ba 100644 --- a/src/fw/apps/system_apps/watchfaces.c +++ b/src/fw/apps/system/watchfaces.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "watchfaces.h" @@ -30,14 +17,14 @@ #include "shell/normal/watchface.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/prefs.h" #include "system/passert.h" #include #include #include -#if !TINTIN_FORCE_FIT typedef struct SettingsWatchfacesData { Window window; MenuLayer menu_layer; @@ -63,7 +50,7 @@ static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstall static uint16_t prv_transform_index(AppMenuDataSource *data_source, uint16_t original_index, void *context) { -#if (SHELL_SDK && CAPABILITY_HAS_SDK_SHELL4) +#ifdef CONFIG_SHELL_SDK // We want the newest installed developer app to appear at the top // This works at the moment because there is only one system watchface, TicToc return app_menu_data_source_get_count(data_source) - 1 - original_index; @@ -167,6 +154,9 @@ static void prv_window_load(Window *window) { PBL_IF_COLOR_ELSE(GColorJazzberryJam, GColorBlack), GColorWhite); menu_layer_set_click_config_onto_window(menu_layer, window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); } @@ -204,9 +194,6 @@ static void s_main(void) { app_event_loop(); } -#else -static void s_main(void) {} -#endif // !TINTIN_FORCE_FIT const PebbleProcessMd* watchfaces_get_app_info() { static const PebbleProcessMdSystem s_app_md = { diff --git a/src/fw/apps/system/watchfaces.h b/src/fw/apps/system/watchfaces.h new file mode 100644 index 0000000000..64ba0b1f76 --- /dev/null +++ b/src/fw/apps/system/watchfaces.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +#define WATCHFACES_APP_COLOR_PRIMARY GColorJazzberryJam + +const PebbleProcessMd* watchfaces_get_app_info(); diff --git a/src/fw/apps/system/weather/layout.c b/src/fw/apps/system/weather/layout.c new file mode 100644 index 0000000000..4abe5bddc5 --- /dev/null +++ b/src/fw/apps/system/weather/layout.c @@ -0,0 +1,606 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "layout.h" + +#include "applib/fonts/fonts.h" +#include "applib/graphics/gcontext.h" +#include "applib/graphics/gdraw_command_image.h" +#include "applib/graphics/gpath.h" +#include "applib/graphics/graphics.h" +#include "applib/graphics/graphics_circle.h" +#include "applib/graphics/gtypes.h" +#include "applib/graphics/text.h" +#include "applib/ui/animation.h" +#include "applib/ui/animation_interpolate.h" +#include "applib/ui/animation_timing.h" +#include "applib/ui/content_indicator.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/kino/kino_reel/morph_square.h" +#include "applib/ui/kino/kino_reel/transform.h" +#include "applib/ui/window.h" +#include "apps/system/timeline/text_node.h" +#include "font_resource_keys.auto.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/timeline_resources.h" +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_types.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" +#include "util/trig.h" + +#include + +#define WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT (18) +#define WEATHER_APP_LAYOUT_TOP_PADDING PBL_IF_RECT_ELSE(0, 15) +#if PBL_DISPLAY_HEIGHT >= 200 +#define WEATHER_APP_LAYOUT_TODAY_ICON_SIZE (TimelineResourceSizeSmall) +#define WEATHER_APP_LAYOUT_TOMORROW_ICON_SIZE (TimelineResourceSizeTiny) +#else +#define WEATHER_APP_LAYOUT_TODAY_ICON_SIZE (TimelineResourceSizeTiny) +#define WEATHER_APP_LAYOUT_TOMORROW_ICON_SIZE (TimelineResourceSizeTiny) +#endif +#define WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET PBL_IF_RECT_ELSE(3, 23) + +static int prv_draw_text(GPoint offset, int max_width, GContext *context, + const char *text, const GFont font, + GColor font_color, GTextAlignment alignment) { + const int height = fonts_get_font_height(font); + const GRect box = (GRect) {offset, GSize(max_width, height)}; + + graphics_context_set_text_color(context, font_color); + graphics_draw_text(context, text, font, box, GTextOverflowModeFill, alignment, NULL); + + return height; +} + +static void prv_draw_weather_background(const GRect *circle_bounding_box, GContext *context, + GColor background_color) { + if (!gcolor_is_invisible(background_color)) { + graphics_context_set_fill_color(context, background_color); + graphics_fill_oval(context, *circle_bounding_box, GOvalScaleModeFitCircle); + } +} + +static void prv_fill_high_low_temp_buffer(const int high, const int low, char *buffer, + const size_t buffer_size, const void *i18n_owner) { + if ((high == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) && + (low == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP)) { + /// Shown when neither high nor low temperature is known + const char *both_temps_no_data = i18n_get("--° / --°", i18n_owner); + strncpy(buffer, both_temps_no_data, strlen(both_temps_no_data)); + buffer[buffer_size - 1] = '\0'; + } else if (low == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { + /// Shown when only the day's high temperature is known, (e.g. "68° / --°") + snprintf(buffer, buffer_size, i18n_get("%i° / --°", i18n_owner), high); + } else if (high == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { + /// Shown when only the day's low temperature is known (e.g. "--° / 52°") + snprintf(buffer, buffer_size, i18n_get("--° / %i°", i18n_owner), low); + } else { + /// A day's high and low temperature, separated by a foward slash (e.g. "68° / 52°") + snprintf(buffer, buffer_size, i18n_get("%i° / %i°", i18n_owner), high, low); + } +} + +#define GPS_ARROW_WIDTH (12) +#define GPS_ARROW_HEIGHT (14) + +static const GPoint s_gps_arrow_path_points[] = { + {0, GPS_ARROW_HEIGHT}, + {(GPS_ARROW_WIDTH / 2), 0}, + {GPS_ARROW_WIDTH, GPS_ARROW_HEIGHT}, + // This 6/7 height ratio for the arrow notch achieves the design spec + {(GPS_ARROW_WIDTH / 2), (GPS_ARROW_HEIGHT * 6 / 7)} +}; + +static void prv_draw_gps_arrow_node_callback(GContext *ctx, const GRect *rect, + PBL_UNUSED const GTextNodeDrawConfig *config, bool render, + GSize *size_out, PBL_UNUSED void *user_data) { + GPath gps_arrow_path = (GPath) { + .num_points = ARRAY_LENGTH(s_gps_arrow_path_points), + .points = (GPoint *)s_gps_arrow_path_points, + .offset = rect->origin, + // Ideal rotation would be 45 degrees, but the shape of the arrow matches the design best at + // 38 degrees + .rotation = DEG_TO_TRIGANGLE(38), + }; + + if (render) { + graphics_context_set_fill_color(ctx, GColorBlack); + gpath_draw_filled(ctx, &gps_arrow_path); + } + if (size_out) { + // Note that gpath_outer_rect() doesn't take into account the rotation; we'll add margin to the + // location text node to account for it + *size_out = gpath_outer_rect(&gps_arrow_path).size; + } +} + +static GTextNode *prv_create_location_name_area_node(const WeatherLocationForecast *forecast, + GFont location_font) { + const GTextAlignment location_name_alignment = PBL_IF_RECT_ELSE(GTextAlignmentLeft, + GTextAlignmentCenter); + + // One node for the location name text and one node for the possible GPS arrow + const size_t max_nodes = 2; + GTextNodeHorizontal *horizontal_node = graphics_text_node_create_horizontal(max_nodes); + horizontal_node->horizontal_alignment = location_name_alignment; + + GTextNodeText *location_text_node = graphics_text_node_create_text(0); + location_text_node->text = forecast->location_name; + location_text_node->font = location_font; + location_text_node->color = GColorBlack; + location_text_node->overflow = GTextOverflowModeTrailingEllipsis; + if (forecast->is_current_location) { + // Horizontal spacing between location name and GPS arrow is spec'd by design to be 11 pixels + location_text_node->node.margin = GSize(11, 0); + } + graphics_text_node_container_add_child(&horizontal_node->container, &location_text_node->node); + + if (forecast->is_current_location) { + GTextNodeCustom *arrow_node = graphics_text_node_create_custom(prv_draw_gps_arrow_node_callback, + NULL); + const int cap_offset = fonts_get_font_cap_offset(location_font); + const int visible_center = cap_offset + + (fonts_get_font_height(location_font) - cap_offset) / 2; + arrow_node->node.offset = + GPoint(0, (int16_t)(visible_center - GPS_ARROW_HEIGHT / 2)); + graphics_text_node_container_add_child(&horizontal_node->container, &arrow_node->node); + } + + return &horizontal_node->container.node; +} + +static GSize prv_draw_location_name_area(GPoint offset, int max_width, GContext *ctx, + GFont location_font, + const WeatherLocationForecast *forecast) { + GTextNode *location_name_area_node = prv_create_location_name_area_node(forecast, location_font); + + GRect location_name_area_rect = (GRect) { + .origin = offset, + .size = GSize(max_width, fonts_get_font_height(location_font)), + }; + +#if PBL_ROUND + // On round the location name text and arrow can be obscured by the edges of the bezel, so we + // horizontally inset the rectangle by a few pixels + const int16_t horizontal_inset = 5; + location_name_area_rect = grect_inset(location_name_area_rect, + GEdgeInsets(0, horizontal_inset, 0)); +#endif + + GSize location_name_area_size; + graphics_text_node_draw(location_name_area_node, ctx, &location_name_area_rect, NULL, + &location_name_area_size); + graphics_text_node_destroy(location_name_area_node); + return location_name_area_size; +} +// All text before the separator +static void prv_draw_top_half_text(const WeatherAppLayout *layout, GPoint *current_offset, + int content_width, + GContext *context) { + const WeatherLocationForecast *forecast = layout->forecast; + + current_offset->y += prv_draw_location_name_area(*current_offset, content_width, context, + layout->location_font, forecast).h; + +#if PBL_DISPLAY_HEIGHT >= 200 + const int location_and_today_temperature_vertical_spacing = 10; +#else + const int location_and_today_temperature_vertical_spacing = 7; +#endif + current_offset->y += location_and_today_temperature_vertical_spacing; + + char text_buffer[15] = {0}; + const size_t max_text_buff_size = ARRAY_LENGTH(text_buffer); + + if (forecast->current_temp == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { + /// Shown when today's current temperature is unknown + const char *unknown_temp_string = i18n_get("--°", layout); + strncpy(text_buffer, unknown_temp_string, strlen(unknown_temp_string)); + text_buffer[max_text_buff_size - 1] = '\0'; + } else { + /// Today's current temperature (e.g. "68°") + snprintf(text_buffer, max_text_buff_size, i18n_get("%i°", layout), forecast->current_temp); + } + current_offset->y += prv_draw_text(*current_offset, content_width, context, text_buffer, + layout->temperature_font, GColorBlack, GTextAlignmentLeft); + + prv_fill_high_low_temp_buffer(forecast->today_high, forecast->today_low, text_buffer, + max_text_buff_size, layout); + current_offset->y += prv_draw_text(*current_offset, content_width, context, text_buffer, + layout->high_low_phrase_font, GColorBlack, + GTextAlignmentLeft); + const int today_high_low_gap_vertical_spacing_reduction = 2; + current_offset->y -= today_high_low_gap_vertical_spacing_reduction; + + current_offset->y += prv_draw_text(*current_offset, content_width, context, + forecast->current_weather_phrase, layout->high_low_phrase_font, + GColorBlack, GTextAlignmentLeft); +} + +// All text after the separator +static void prv_draw_bottom_half_text(const WeatherAppLayout *layout, GPoint *current_offset, + int content_width, GContext *context) { + const WeatherLocationForecast *forecast = layout->forecast; + + const int separator_tomorrow_title_vertical_spacing = 6; + current_offset->y += separator_tomorrow_title_vertical_spacing; + current_offset->y += prv_draw_text(*current_offset, content_width, context, + /// Refers to the weather conditions for tomorrow + i18n_get("TOMORROW", layout), layout->tomorrow_font, + GColorBlack, GTextAlignmentLeft); + char text_buffer[15] = {0}; + const size_t max_text_buff_size = ARRAY_LENGTH(text_buffer); + prv_fill_high_low_temp_buffer(forecast->tomorrow_high, forecast->tomorrow_low, text_buffer, + max_text_buff_size, layout); + prv_draw_text(*current_offset, content_width, context, text_buffer, layout->high_low_phrase_font, + GColorBlack, GTextAlignmentLeft); +} + +static GRect prv_get_icon_bg_circle_rect(const KinoLayer *icon_layer) { + const GSize icon_size = icon_layer->layer.bounds.size; + const unsigned int circle_diam = integer_sqrt(2 * icon_size.w * icon_size.h); + const GRect icon_frame = icon_layer->layer.frame; + return (GRect) { + .origin = GPoint( + icon_frame.origin.x + (int)icon_size.w / 2 - (int)circle_diam / 2, + icon_frame.origin.y + (int)icon_size.h / 2 - (int)circle_diam / 2), + .size = GSize(circle_diam, circle_diam), + }; +} + +static void prv_draw_weather_icon_backgrounds(const WeatherAppLayout *layout, + const GRect *content_bounds, GContext *context) { + const WeatherLocationForecast *forecast = layout->forecast; + + GRect bg_circle = prv_get_icon_bg_circle_rect(&layout->current_weather_icon_layer); + prv_draw_weather_background(&bg_circle, context, + weather_type_get_bg_color(forecast->current_weather_type)); + + bg_circle = prv_get_icon_bg_circle_rect(&layout->tomorrow_weather_icon_layer); + prv_draw_weather_background(&bg_circle, context, + weather_type_get_bg_color(forecast->tomorrow_weather_type)); +} + + +static void prv_render_layout(Layer *layer, GContext *context) { + // "Content" refers to everything except the dot separator + const GRect content_bounds = + grect_inset(layer->bounds, GEdgeInsets(0, WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET, + 0)); + const int content_x_offset = content_bounds.origin.x; + const int content_width = content_bounds.size.w; + + const WeatherAppLayout *layout = window_get_user_data(layer_get_window(layer)); + const WeatherLocationForecast *forecast = layout->forecast; + + if (!forecast) { + // Nothing to draw. + return; + } + // start at 1 from the top to match design docs + GPoint current_offset = GPoint(content_x_offset, 1); + GPoint *offset = ¤t_offset; + + prv_draw_top_half_text(layout, offset, content_width, context); + + // dotted separator +#if PBL_DISPLAY_HEIGHT >= 200 + // Anchor the separator + tomorrow block to the bottom of the content area + const int separator_tomorrow_spacing = 6; + const int bottom_block_height = separator_tomorrow_spacing + + fonts_get_font_height(layout->tomorrow_font) + + fonts_get_font_height(layout->high_low_phrase_font) * 2; + current_offset.y = layer->bounds.size.h - bottom_block_height; +#else + const int phrase_separator_vertical_spacing = 10; + current_offset.y += phrase_separator_vertical_spacing; +#endif + + const GPoint separator_start = GPoint(0, current_offset.y); + graphics_context_set_stroke_width(context, 5); + graphics_context_set_stroke_color(context, PBL_IF_COLOR_ELSE(GColorLightGray, GColorBlack)); + graphics_draw_horizontal_line_dotted(context, separator_start, + layer->bounds.size.w); + + if (!layout->animation_state.hide_bottom_half_text) { + prv_draw_bottom_half_text(layout, offset, content_width, context); + } + prv_draw_weather_icon_backgrounds(layout, &content_bounds, context); +} + +static void prv_content_indicator_setup_direction(ContentIndicator *content_indicator, + Layer *indicator_layer, + ContentIndicatorDirection direction) { + content_indicator_configure_direction(content_indicator, direction, &(ContentIndicatorConfig) { + .layer = indicator_layer, + .colors.foreground = GColorBlack, + .colors.background = GColorWhite, + }); +} + +void weather_app_layout_init(WeatherAppLayout *layout, const GRect *frame) { +#if PBL_DISPLAY_HEIGHT >= 200 + layout->location_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + layout->temperature_font = fonts_get_system_font(FONT_KEY_LECO_36_BOLD_NUMBERS); + layout->high_low_phrase_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + layout->tomorrow_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); +#else + layout->location_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + layout->temperature_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM); + layout->high_low_phrase_font = fonts_get_system_font(FONT_KEY_GOTHIC_18); + layout->tomorrow_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); +#endif + + Layer *root_layer = &layout->root_layer; + layer_init(root_layer, frame); + + Layer *down_arrow_layer = &layout->down_arrow_layer; + const GRect down_arrow_layer_frame = grect_inset( + *frame, GEdgeInsets(frame->size.h - WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT, 0, 0)); + layer_init(down_arrow_layer, &down_arrow_layer_frame); + layer_add_child(root_layer, down_arrow_layer); + + const int content_layer_side_padding = PBL_IF_RECT_ELSE(5, 12); + const GRect content_layer_frame = grect_inset(*frame, + GEdgeInsets(WEATHER_APP_LAYOUT_TOP_PADDING, + content_layer_side_padding, + WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT)); + + Layer *content_layer = &layout->content_layer; + layer_init(content_layer, &content_layer_frame); + layer_set_update_proc(content_layer, prv_render_layout); + layer_add_child(root_layer, content_layer); + + ContentIndicator *content_indicator = &layout->content_indicator; + content_indicator_init(content_indicator); + + prv_content_indicator_setup_direction(content_indicator, down_arrow_layer, + ContentIndicatorDirectionDown); + + const GSize today_icon_size = + timeline_resources_get_gsize(WEATHER_APP_LAYOUT_TODAY_ICON_SIZE); + const GSize tomorrow_icon_size = + timeline_resources_get_gsize(WEATHER_APP_LAYOUT_TOMORROW_ICON_SIZE); + +#if PBL_DISPLAY_HEIGHT >= 200 + // Today icon: align top of background circle with temperature line cap + const unsigned int today_circle_diam = integer_sqrt(2 * today_icon_size.w * today_icon_size.h); + const int today_circle_inset = (int)(today_circle_diam - today_icon_size.w) / 2; + const int today_icon_x = content_layer_frame.size.w - today_icon_size.w - + WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET - today_circle_inset; + const int temp_line_y = 1 + fonts_get_font_height(layout->location_font) + 10; + const int top_icon_y = temp_line_y + fonts_get_font_cap_offset(layout->temperature_font) + + today_circle_inset; + + // Tomorrow icon: anchor to bottom of content area, aligned with "TOMORROW" text cap + const unsigned int tomorrow_circle_diam = + integer_sqrt(2 * tomorrow_icon_size.w * tomorrow_icon_size.h); + const int tomorrow_circle_inset = (int)(tomorrow_circle_diam - tomorrow_icon_size.w) / 2; + const int tomorrow_icon_x = content_layer_frame.size.w - tomorrow_icon_size.w - + WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET - + tomorrow_circle_inset; + const int separator_tomorrow_spacing = 6; + const int bottom_block_height = separator_tomorrow_spacing + + fonts_get_font_height(layout->tomorrow_font) + + fonts_get_font_height(layout->high_low_phrase_font) * 2; + const int tomorrow_line_y = content_layer_frame.size.h - bottom_block_height + + separator_tomorrow_spacing; + const int bottom_icon_y = tomorrow_line_y + fonts_get_font_cap_offset(layout->tomorrow_font) + + tomorrow_circle_inset; +#else + const unsigned int today_circle_diam = integer_sqrt(2 * today_icon_size.w * today_icon_size.h); + const int today_circle_inset = (int)(today_circle_diam - today_icon_size.w) / 2; + const int today_icon_x = content_layer_frame.size.w - today_icon_size.w - + WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET - today_circle_inset; + const int tomorrow_icon_x = today_icon_x; + const int top_icon_y = content_layer_frame.origin.y + PBL_IF_RECT_ELSE(33, 18); + const int bottom_icon_y = top_icon_y + today_icon_size.h + 50; +#endif + + GRect today_icon_frame = (GRect) { { today_icon_x, top_icon_y }, today_icon_size }; + KinoLayer *current_weather_icon_layer = &layout->current_weather_icon_layer; + kino_layer_init(current_weather_icon_layer, &today_icon_frame); + layer_add_child(content_layer, kino_layer_get_layer(current_weather_icon_layer)); + + GRect tomorrow_icon_frame = (GRect) { { tomorrow_icon_x, bottom_icon_y }, tomorrow_icon_size }; + KinoLayer *tomorrow_weather_icon_layer = &layout->tomorrow_weather_icon_layer; + kino_layer_init(tomorrow_weather_icon_layer, &tomorrow_icon_frame); + layer_add_child(content_layer, kino_layer_get_layer(tomorrow_weather_icon_layer)); +} + +static uint32_t prv_get_resource_id_for_weather_type(WeatherType type, + TimelineResourceSize size) { + const TimelineResourceInfo timeline_res = { + .res_id = weather_type_get_timeline_resource_id(type), + }; + AppResourceInfo icon_res_info; + timeline_resources_get_id(&timeline_res, size, &icon_res_info); + return icon_res_info.res_id; +} + +void weather_app_layout_set_data(WeatherAppLayout *layout, + const WeatherLocationForecast *forecast) { + layout->forecast = forecast; + + const uint32_t current_weather_res_id = forecast ? + prv_get_resource_id_for_weather_type(forecast->current_weather_type, + WEATHER_APP_LAYOUT_TODAY_ICON_SIZE) : + RESOURCE_ID_INVALID; + const uint32_t tomorrow_weather_res_id = forecast ? + prv_get_resource_id_for_weather_type(forecast->tomorrow_weather_type, + WEATHER_APP_LAYOUT_TOMORROW_ICON_SIZE) : + RESOURCE_ID_INVALID; + + kino_layer_set_reel_with_resource(&layout->current_weather_icon_layer, current_weather_res_id); + kino_layer_set_reel_with_resource(&layout->tomorrow_weather_icon_layer, tomorrow_weather_res_id); + + layer_mark_dirty(&layout->root_layer); +} + +void weather_app_layout_set_down_arrow_visible(WeatherAppLayout *layout, bool is_down_visible) { + content_indicator_set_content_available(&layout->content_indicator, ContentIndicatorDirectionDown, + is_down_visible); +} + +void weather_app_layout_deinit(WeatherAppLayout *layout) { + i18n_free_all(layout); + layer_deinit(&layout->root_layer); +} + +// Down arrow layer grows until a point, after which the entire content teleports to a height +// slightly higher than its resting position, then relaxes into place +static void prv_down_animation_update(Animation *animation, AnimationProgress normalized) { + WeatherAppLayout *layout = animation_get_context(animation); + // Progress at which to switch from the down arrow growing to entire content relaxing downwards + const AnimationProgress animation_cut_frame_progress = + (interpolate_moook_in_duration() * ANIMATION_NORMALIZED_MAX) / interpolate_moook_duration(); + // Progress at which to hide "TOMORROW" and tomorrow high / low temperature text + const AnimationProgress animation_hide_bottom_half_text_progress = + (animation_cut_frame_progress * 2) / 3; + + int down_arrow_layer_height = WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT; + layout->animation_state.hide_bottom_half_text = false; + + if (normalized <= animation_cut_frame_progress) { + if (normalized >= animation_hide_bottom_half_text_progress) { + layout->animation_state.hide_bottom_half_text = true; + } + // renormalize the progress so that interpolate_moook_in_only works as expected + int32_t new_normalized = animation_timing_scaled(normalized, ANIMATION_NORMALIZED_MIN, + animation_cut_frame_progress); + const int additional_down_arrow_height = 25; + // grow the down arrow layer + down_arrow_layer_height += interpolate_moook_in_only(new_normalized, 0, + additional_down_arrow_height); + } else { + // We've cut, so display the next forecast's data + if (layout->animation_state.next_forecast) { + weather_app_layout_set_data(layout, layout->animation_state.next_forecast); + layout->animation_state.next_forecast = NULL; + } + int32_t new_normalized = animation_timing_scaled(normalized, animation_cut_frame_progress, + ANIMATION_NORMALIZED_MAX); + + // Relax the content by changing its top margin + const int animation_margin_top_from = WEATHER_APP_LAYOUT_TOP_PADDING - PBL_IF_RECT_ELSE(10, 15); + const int animation_margin_top_to = WEATHER_APP_LAYOUT_TOP_PADDING; + const int num_frames_from = 1; + const bool bounce_back = false; + int animation_margin_top = interpolate_moook_out(new_normalized, animation_margin_top_from, + animation_margin_top_to, num_frames_from, + bounce_back); + layout->content_layer.frame.origin.y = animation_margin_top; + + // The down arrow's height follows the content margin. It starts off large, then goes back to + // its original size, as the content relaxes into place + down_arrow_layer_height += (-animation_margin_top + WEATHER_APP_LAYOUT_TOP_PADDING); + } + + const GRect down_arrow_layer_frame = grect_inset(layout->root_layer.frame, + GEdgeInsets(layout->root_layer.frame.size.h - down_arrow_layer_height, 0, 0)); + layer_set_frame(&layout->down_arrow_layer, &down_arrow_layer_frame); + + layer_mark_dirty(&layout->root_layer); +} + +// moves the entire root layer up back into place +static void prv_up_animation_update(Animation *animation, AnimationProgress normalized) { + const int root_layer_top_margin_from = (WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT * 2) / 3; + const int root_layer_top_margin_to = 0; + const int num_frames_from = 1; + const bool bounce_back = false; + int root_layer_top_margin = interpolate_moook_out(normalized, root_layer_top_margin_from, + root_layer_top_margin_to, num_frames_from, bounce_back); + + WeatherAppLayout *layout = animation_get_context(animation); + if (layout->animation_state.next_forecast) { + layout->forecast = layout->animation_state.next_forecast; + layout->animation_state.next_forecast = NULL; + } + + layout->root_layer.frame.origin.y = root_layer_top_margin; + layer_set_frame(&layout->root_layer, &layout->root_layer.frame); +} + +static void prv_animation_stopped(Animation *animation, bool finished, void *context) { + WeatherAppLayout *layout = context; + if (layout->animation_state.next_forecast) { + weather_app_layout_set_data(layout, layout->animation_state.next_forecast); + layout->animation_state.next_forecast = NULL; + } + layout->animation_state.hide_bottom_half_text = false; + + GRect *root_layer_frame = &layout->root_layer.frame; + root_layer_frame->origin.y = 0; + layer_set_frame(&layout->root_layer, root_layer_frame); + + GRect *content_layer_frame = &layout->content_layer.frame; + content_layer_frame->origin.y = WEATHER_APP_LAYOUT_TOP_PADDING; + layer_set_frame(&layout->content_layer, content_layer_frame); + + const GRect down_arrow_layer_frame = grect_inset(*root_layer_frame, + GEdgeInsets(root_layer_frame->size.h - WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT, 0, 0)); + layer_set_frame(&layout->down_arrow_layer, &down_arrow_layer_frame); +} + +static const AnimationImplementation s_down_animation_implementation = { + .update = &prv_down_animation_update, +}; + +static const AnimationImplementation s_up_animation_implementation = { + .update = &prv_up_animation_update, +}; + +static const AnimationHandlers s_animation_handlers = { + .stopped = &prv_animation_stopped, +}; + +static void prv_morph_weather_icons(KinoLayer *icon_layer, WeatherType from, WeatherType to, + TimelineResourceSize size, uint32_t duration) { + uint32_t from_image_res_id = + prv_get_resource_id_for_weather_type(from, size); + KinoReel *from_reel = kino_reel_create_with_resource(from_image_res_id); + KinoReel *to_reel = kino_reel_create_with_resource( + prv_get_resource_id_for_weather_type(to, size)); + + KinoReel *icon_reel = kino_reel_morph_square_create(from_reel, true); + kino_reel_transform_set_to_reel(icon_reel, to_reel, true); + kino_reel_transform_set_transform_duration(icon_reel, duration); + + kino_layer_set_reel(icon_layer, icon_reel, true); + kino_layer_play(icon_layer); +} + +void weather_app_layout_animate(WeatherAppLayout *layout, WeatherLocationForecast *new_forecast, + bool animate_down) { + animation_unschedule_all(); + + const uint32_t anim_duration = animate_down ? interpolate_moook_duration() : + interpolate_moook_out_duration(); + layout->animation_state.next_forecast = new_forecast; + Animation *animation = animation_create(); + animation_set_duration(animation, anim_duration); + InterpolateInt64Function interpolation = animate_down ? interpolate_moook : + interpolate_moook_in_only; + animation_set_custom_interpolation(animation, interpolation); + animation_set_handlers(animation, s_animation_handlers, layout); + const AnimationImplementation *implementation = animate_down ? &s_down_animation_implementation : + &s_up_animation_implementation; + animation_set_implementation(animation, implementation); + animation_schedule(animation); + + prv_morph_weather_icons(&layout->current_weather_icon_layer, + layout->forecast->current_weather_type, + new_forecast->current_weather_type, + WEATHER_APP_LAYOUT_TODAY_ICON_SIZE, anim_duration); + + prv_morph_weather_icons(&layout->tomorrow_weather_icon_layer, + layout->forecast->tomorrow_weather_type, + new_forecast->tomorrow_weather_type, + WEATHER_APP_LAYOUT_TOMORROW_ICON_SIZE, anim_duration); +} diff --git a/src/fw/apps/system/weather/layout.h b/src/fw/apps/system/weather/layout.h new file mode 100644 index 0000000000..c364014331 --- /dev/null +++ b/src/fw/apps/system/weather/layout.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gdraw_command_image.h" +#include "applib/ui/content_indicator_private.h" +#include "applib/ui/kino/kino_layer.h" +#include "pbl/services/weather/weather_service.h" + +typedef struct WeatherAppLayout { + Layer root_layer; + Layer content_layer; + KinoLayer current_weather_icon_layer; + KinoLayer tomorrow_weather_icon_layer; + const WeatherLocationForecast *forecast; + GFont location_font; + GFont temperature_font; + GFont high_low_phrase_font; + GFont tomorrow_font; + Layer down_arrow_layer; + ContentIndicator content_indicator; + struct { // used during animations + const WeatherLocationForecast *next_forecast; + bool hide_bottom_half_text; + } animation_state; +} WeatherAppLayout; + +void weather_app_layout_init(WeatherAppLayout *layout, const GRect *frame); + +void weather_app_layout_set_data(WeatherAppLayout *layout, + const WeatherLocationForecast *forecast); + +void weather_app_layout_set_down_arrow_visible(WeatherAppLayout *layout, bool is_down_visible); + +void weather_app_layout_deinit(WeatherAppLayout *layout); + +void weather_app_layout_animate(WeatherAppLayout *layout, WeatherLocationForecast *new_forecast, + bool animate_down); diff --git a/src/fw/apps/system/weather/warning_dialog.c b/src/fw/apps/system/weather/warning_dialog.c new file mode 100644 index 0000000000..433034762d --- /dev/null +++ b/src/fw/apps/system/weather/warning_dialog.c @@ -0,0 +1,50 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "warning_dialog.h" + +#include "applib/ui/app_window_stack.h" +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/expandable_dialog.h" +#include "kernel/pbl_malloc.h" + +typedef struct WeatherAppWarningDialogData { + WeatherAppWarningDialogDismissedCallback dismissed_cb; +} WeatherAppWarningDialogData; + +static void prv_warning_dialog_unload(void *context) { + WeatherAppWarningDialogData *data = context; + if (data->dismissed_cb) { + data->dismissed_cb(); + } + task_free(data); +} + +static void prv_warning_dialog_select_handler(ClickRecognizerRef recognizer, void *context) { + ExpandableDialog *expandable_dialog = context; + expandable_dialog_pop(expandable_dialog); +} + +WeatherAppWarningDialog *weather_app_warning_dialog_push(const char *localized_string, + WeatherAppWarningDialogDismissedCallback dismissed_cb) { + WeatherAppWarningDialogData *data = task_zalloc_check(sizeof(WeatherAppWarningDialogData)); + ExpandableDialog *expandable_dialog = expandable_dialog_create("Weather - warning dialog"); + + Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog); + dialog_set_destroy_on_pop(dialog, false); + dialog_set_icon(dialog, RESOURCE_ID_GENERIC_WARNING_TINY); + dialog_set_text(dialog, localized_string); + const DialogCallbacks callbacks = { + .unload = prv_warning_dialog_unload, + }; + dialog_set_callbacks(dialog, &callbacks, data); + + expandable_dialog_show_action_bar(expandable_dialog, true); + expandable_dialog_set_select_action(expandable_dialog, RESOURCE_ID_ACTION_BAR_ICON_CHECK, + prv_warning_dialog_select_handler); + + data->dismissed_cb = dismissed_cb; + app_expandable_dialog_push(expandable_dialog); + + return expandable_dialog; +} diff --git a/src/fw/apps/system/weather/warning_dialog.h b/src/fw/apps/system/weather/warning_dialog.h new file mode 100644 index 0000000000..5e8197aa90 --- /dev/null +++ b/src/fw/apps/system/weather/warning_dialog.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/dialogs/expandable_dialog.h" + +#include + +typedef ExpandableDialog WeatherAppWarningDialog; + +typedef void (*WeatherAppWarningDialogDismissedCallback)(void); + +WeatherAppWarningDialog *weather_app_warning_dialog_push(const char *localized_string, + WeatherAppWarningDialogDismissedCallback dismissed_cb); diff --git a/src/fw/apps/system/weather/weather.c b/src/fw/apps/system/weather/weather.c new file mode 100644 index 0000000000..55410e7e6a --- /dev/null +++ b/src/fw/apps/system/weather/weather.c @@ -0,0 +1,219 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "weather.h" +#include "layout.h" +#include "warning_dialog.h" + +#include "applib/app.h" +#include "applib/event_service_client.h" +#include "applib/ui/click.h" +#include "applib/ui/content_indicator.h" +#include "applib/ui/ui.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_md.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_types.h" +#include "util/array.h" +#include "util/attributes.h" +#include "util/list.h" +#include "util/math.h" + +typedef struct WeatherAppData { + Window window; + WeatherAppLayout layout; + WeatherDataListNode *forecasts_list_head; + size_t forecasts_count; + unsigned int current_forecast_index; + EventServiceInfo weather_event_info; + WeatherAppWarningDialog *warning_dialog; +} WeatherAppData; + +static bool prv_is_weather_forecast_recent(WeatherLocationForecast *forecast) { + if (!forecast) { + return false; + } + const time_t current_time_utc = rtc_get_time(); + const int recent_threshold_seconds = 5 * SECONDS_PER_HOUR / 2; // 2.5 hours + const int seconds_since_forecast_was_updated = current_time_utc - forecast->time_updated_utc; + return (seconds_since_forecast_was_updated < recent_threshold_seconds); +} + +static void prv_warning_dialog_dismiss_cb(void) { + WeatherAppData *data = app_state_get_user_data(); + data->warning_dialog = NULL; +} + +static void prv_show_warning_dialog(WeatherAppData *data, bool exit_on_pop, + const char *localized_text) { + if (data->warning_dialog) { + return; // only show one dialog at a time + } + if (exit_on_pop) { + bool animated = false; + app_window_stack_pop_all(animated); + } + data->warning_dialog = weather_app_warning_dialog_push(localized_text, + prv_warning_dialog_dismiss_cb); +} + +static void prv_handle_weather(PebbleEvent *unused_event, void *unused_context) { + // Unschedule any ongoing animations that would try to touch the weather data we're about to + // update + animation_unschedule_all(); + + size_t forecasts_count_out = 0; + WeatherDataListNode *forecasts_list_head = + weather_service_locations_list_create(&forecasts_count_out); + + WeatherAppData *data = app_state_get_user_data(); + weather_service_locations_list_destroy(data->forecasts_list_head); + WeatherAppLayout *layout = &data->layout; + if (forecasts_count_out > 0) { + weather_app_layout_set_data(layout, &forecasts_list_head->forecast); + const bool multiple_forecasts_exist = (forecasts_count_out > 1); + weather_app_layout_set_down_arrow_visible(layout, multiple_forecasts_exist); + + data->forecasts_list_head = forecasts_list_head; + // Only show the first forecast if the number of forecasts has differed between fetches. + // i.e. assume that the same number of forecasts means the locations have remained the same. + if (data->forecasts_count != forecasts_count_out) { + data->forecasts_count = forecasts_count_out; + data->current_forecast_index = 0; + } + } else { + /// Shown when there are no forecasts available to show the user + const char *warning_text = i18n_get("No location information available. To see weather, add "\ + "locations in your Pebble mobile app.", data); + const bool exit_on_pop = true; + prv_show_warning_dialog(data, exit_on_pop, warning_text); + weather_app_layout_set_down_arrow_visible(layout, false); + weather_app_layout_set_data(layout, NULL); + } +} + +static void prv_main_window_appear(Window *window) { + WeatherAppData *data = app_state_get_user_data(); + data->weather_event_info = (EventServiceInfo) { + .type = PEBBLE_WEATHER_EVENT, + .handler = prv_handle_weather, + }; + event_service_client_subscribe(&data->weather_event_info); +} + +static void prv_main_window_load(Window *window) { + WeatherAppData *data = app_state_get_user_data(); + layer_add_child(&window->layer, &data->layout.root_layer); +} + +static void prv_main_window_disappear(Window *window) { + WeatherAppData *data = app_state_get_user_data(); + event_service_client_unsubscribe(&data->weather_event_info); +} + +static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { + WeatherAppData *data = app_state_get_user_data(); + const bool not_enough_items_to_scroll = (data->forecasts_count <= 1); + if (not_enough_items_to_scroll) { + return; + } + + const bool is_down_pressed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_DOWN); + const int delta = is_down_pressed ? 1 : -1; + data->current_forecast_index = positive_modulo(data->current_forecast_index + delta, + data->forecasts_count); + + WeatherDataListNode *node = + weather_service_locations_list_get_location_at_index(data->forecasts_list_head, + data->current_forecast_index); + weather_app_layout_animate(&data->layout, &node->forecast, is_down_pressed); +} + +static void prv_main_window_click_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_up_down_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_up_down_click_handler); +} + +static void prv_main_window_unload(Window *window) { + WeatherAppData *data = app_state_get_user_data(); + weather_app_layout_deinit(&data->layout); +} + +static NOINLINE void prv_init(void) { + WeatherAppData *data = app_zalloc_check(sizeof(WeatherAppData)); + app_state_set_user_data(data); + + Window *window = &data->window; + window_init(window, WINDOW_NAME("Weather")); + const WindowHandlers window_handlers = { + .appear = prv_main_window_appear, + .load = prv_main_window_load, + .disappear = prv_main_window_disappear, + .unload = prv_main_window_unload, + }; + window_set_window_handlers(window, &window_handlers); + + window_set_click_config_provider(window, prv_main_window_click_provider); + window_set_user_data(window, data); + + const GRect *layout_frame = &window->layer.bounds; + WeatherAppLayout *layout = &data->layout; + weather_app_layout_init(layout, layout_frame); + window_set_user_data(window, layout); + + // Fetch initial data + prv_handle_weather(NULL, NULL); + + if (data->forecasts_count == 0) { + return; + } + + const bool animated = true; + app_window_stack_push(&data->window, animated); + + // Request the default forecast separately instead of using the forecast list in `data` to avoid + // any potential race conditions + WeatherLocationForecast *default_forecast = weather_service_create_default_forecast(); + const bool is_default_forecast_data_recent = prv_is_weather_forecast_recent(default_forecast); + weather_service_destroy_default_forecast(default_forecast); + + // TODO PBL-38484: Consider using a different dialog for when data is stale but phone is connected + if (!is_default_forecast_data_recent && !connection_service_peek_pebble_app_connection()) { + /// Shown when there is no connection to the phone and the data that we have is not recent + const char *warning_text = i18n_get("Unable to connect. Your weather data may be out of date; "\ + "try checking the connection on your phone.", data); + const bool exit_on_pop = false; + prv_show_warning_dialog(data, exit_on_pop, warning_text); + } +} + +static void prv_deinit(void) { + WeatherAppData *data = app_state_get_user_data(); + i18n_free_all(data); +} + +static void prv_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} + +const PebbleProcessMd* weather_app_get_info() { + const bool is_visible_in_launcher = weather_service_supported_by_phone(); + + static const PebbleProcessMdSystem s_weather_app_info = { + .common = { + .main_func = prv_main, + .uuid = UUID_WEATHER_DATA_SOURCE, + }, + .name = i18n_noop("Weather"), + .icon_resource_id = RESOURCE_ID_GENERIC_WEATHER_TINY, + }; + + return is_visible_in_launcher ? (const PebbleProcessMd *)&s_weather_app_info : NULL; +} diff --git a/src/fw/apps/system/weather/weather.h b/src/fw/apps/system/weather/weather.h new file mode 100644 index 0000000000..745d39efda --- /dev/null +++ b/src/fw/apps/system/weather/weather.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* weather_app_get_info(); diff --git a/src/fw/apps/system/workout/active.c b/src/fw/apps/system/workout/active.c new file mode 100644 index 0000000000..4029f8797f --- /dev/null +++ b/src/fw/apps/system/workout/active.c @@ -0,0 +1,1069 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "active.h" +#include "dialog.h" +#include "summary.h" +#include "workout.h" + +#include "applib/app.h" +#include "board/display.h" +#include "applib/ui/action_menu_hierarchy.h" +#include "applib/ui/action_menu_window.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/ui.h" +#include "applib/ui/window_manager.h" +#include "apps/system/timeline/text_node.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/health_util.h" +#include "pbl/services/activity/hr_util.h" +#include "pbl/services/activity/workout_service.h" +#include "system/logging.h" +#include "util/size.h" + +#include + +#define TEXT_COLOR (GColorBlack) +#define TEXT_ALIGNMENT (PBL_IF_RECT_ELSE(GTextAlignmentLeft, GTextAlignmentRight)) +#define BACKGROUND_COLOR PBL_IF_COLOR_ELSE(GColorYellow, GColorWhite) + +typedef enum WorkoutLayout { + WorkoutLayout_SingleMetric, + WorkoutLayout_StaticAndScrollable, + WorkoutLayout_TwoStaticAndScrollable, +} WorkoutLayout; + +typedef struct WorkoutActiveWindow { + Window window; + ActionBarLayer action_bar; + StatusBarLayer status_layer; + Layer base_layer; + Layer top_metric_layer; + Layer middle_metric_layer; + Layer scrollable_metric_layer; + WorkoutDialog end_workout_dialog; + + ButtonId pause_button; + + WorkoutController *workout_controller; + void *workout_data; + + WorkoutLayout layout; + + WorkoutMetricType top_metric; + WorkoutMetricType middle_metric; + + int num_scrollable_metrics; + int current_scrollable_metric; + WorkoutMetricType scrollable_metrics[WorkoutMetricTypeCount]; + + GBitmap *heart_icon; + GBitmap *hr_measuring_icon; + + GBitmap *action_bar_start; + GBitmap *action_bar_pause; + GBitmap *action_bar_stop; + GBitmap *action_bar_more; + GBitmap *action_bar_next; + + AppTimer *update_timer; + AppTimer *hr_measuring_timer; + + int cur_hr_measuring_width_idx; +} WorkoutActiveWindow; + +static const int s_hr_measuring_widths[] = {36, 0, 24, 28, 32}; + +static void prv_draw_heart_node_callback(GContext *ctx, const GRect *box, + const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data); + +static void prv_draw_hr_measuring_node_callback(GContext *ctx, const GRect *box, + const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Helpers + +static void prv_add_scrollable_metrics(WorkoutActiveWindow *active_window, + int num_scrollable_metrics, + WorkoutMetricType *metrics) { + for (int i = 0; i < num_scrollable_metrics; i++) { + active_window->scrollable_metrics[active_window->num_scrollable_metrics++] = metrics[i]; + } +} + +static const char* prv_get_label_for_hr_metric(int bpm) { + switch (hr_util_get_hr_zone(bpm)) { + case HRZone_Zone1: + /// Zone 1 HR Label + return i18n_noop("FAT BURN"); + case HRZone_Zone2: + /// Zone 2 HR Label + return i18n_noop("ENDURANCE"); + case HRZone_Zone3: + /// Zone 3 HR Label + return i18n_noop("PERFORMANCE"); + default: + /// Default/Zone 0 HR Label + return i18n_noop("HEART RATE"); + } +} + +static const char* prv_get_label_for_metric(WorkoutMetricType metric_type, + WorkoutActiveWindow *active_window) { + switch (metric_type) { + case WorkoutMetricType_Hr: + { + int bpm = active_window->workout_controller->get_metric_value(WorkoutMetricType_Hr, + active_window->workout_data); + return prv_get_label_for_hr_metric(bpm); + } + case WorkoutMetricType_Custom: + /// Custom Label from Sports App + return active_window->workout_controller->get_custom_metric_label_string(); + case WorkoutMetricType_Duration: + /// Duration Label + return i18n_noop("DURATION"); + case WorkoutMetricType_AvgPace: +#if PBL_RECT + /// Average Pace Label + return i18n_noop("AVG PACE"); +#else + /// Average Pace Label with units + return active_window->workout_controller->get_distance_string(i18n_noop("AVG PACE (/MI)"), + i18n_noop("AVG PACE (/KM)")); +#endif + case WorkoutMetricType_Pace: +#if PBL_RECT + /// Pace Label + return i18n_noop("PACE"); +#else + /// Pace Label with units + return active_window->workout_controller->get_distance_string(i18n_noop("PACE (/MI)"), + i18n_noop("PACE (/KM)")); +#endif + case WorkoutMetricType_Speed: +#if PBL_RECT + /// Speed Label + return i18n_noop("SPEED"); +#else + /// Speed Label with units + return active_window->workout_controller->get_distance_string(i18n_noop("SPEED (MPH)"), + i18n_noop("SPEED (KM/H)")); +#endif + case WorkoutMetricType_Distance: +#if PBL_RECT + /// Distance Label + return i18n_noop("DISTANCE"); +#else + /// Distance Label with units + return active_window->workout_controller->get_distance_string(i18n_noop("DISTANCE (MI)"), + i18n_noop("DISTANCE (KM)")); +#endif + case WorkoutMetricType_Steps: + /// Steps Label + return i18n_noop("STEPS"); + default: + return ""; + } +} + +static GColor prv_get_bg_color_for_metric(WorkoutMetricType metric_type, + WorkoutActiveWindow *active_window, + bool is_scrollable) { +#if PBL_BW + return GColorWhite; +#else + if (metric_type == WorkoutMetricType_Hr) { + switch (hr_util_get_hr_zone(active_window->workout_controller->get_metric_value( + metric_type, active_window->workout_data))) { + case HRZone_Zone0: + return GColorWhite; + case HRZone_Zone1: + return GColorMelon; + case HRZone_Zone2: + return GColorChromeYellow; + case HRZone_Zone3: + return GColorOrange; + default: + return BACKGROUND_COLOR; + } + } else { + return is_scrollable ? GColorPastelYellow : BACKGROUND_COLOR; + } +#endif +} + +static GFont prv_get_number_font(bool prefer_larger_font) { +#if PBL_DISPLAY_HEIGHT >= 200 + return prefer_larger_font ? fonts_get_system_font(FONT_KEY_LECO_60_NUMBERS_AM_PM) + : fonts_get_system_font(FONT_KEY_LECO_42_NUMBERS); +#else + return prefer_larger_font ? fonts_get_system_font(FONT_KEY_LECO_38_BOLD_NUMBERS) + : fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM); +#endif +} + +static GTextNode* prv_create_text_node(WorkoutActiveWindow *active_window, + WorkoutMetricType metric_type, + bool prefer_larger_font, + void *i18n_owner) { + GTextNodeHorizontal *horiz_container = graphics_text_node_create_horizontal(MAX_TEXT_NODES); + GTextNodeContainer *container = &horiz_container->container; + horiz_container->horizontal_alignment = TEXT_ALIGNMENT; + + const GFont number_font = prv_get_number_font(prefer_larger_font); + const GFont units_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + + const int units_offset_y = fonts_get_font_height(number_font) - fonts_get_font_height(units_font); + + switch (metric_type) { + case WorkoutMetricType_Hr: { + GPoint heart_node_offset = GPoint(2, prefer_larger_font ? 5 : 0); + GTextNodeCustom *heart_node; + if (active_window->workout_controller->get_metric_value( + metric_type, active_window->workout_data) > 0) { + const size_t buffer_size = sizeof("000"); + GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, + TEXT_COLOR, container); + active_window->workout_controller->metric_to_string(metric_type, + (char *) number_text_node->text, + buffer_size, i18n_owner, + active_window->workout_data); + heart_node_offset.y += fonts_get_font_cap_offset(number_font); + heart_node = graphics_text_node_create_custom(prv_draw_heart_node_callback, + active_window); + } else { + // if metric value is 0, we draw another icon that needs different offset + heart_node_offset.x += 2; + heart_node_offset.y += 7; + heart_node = graphics_text_node_create_custom(prv_draw_hr_measuring_node_callback, + active_window); + } + heart_node->node.offset = heart_node_offset; + graphics_text_node_container_add_child(container, &heart_node->node); + break; + } + case WorkoutMetricType_Steps: { + const size_t buffer_size = sizeof("000000"); + GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, + TEXT_COLOR, container); + active_window->workout_controller->metric_to_string(metric_type, + (char *) number_text_node->text, + buffer_size, i18n_owner, + active_window->workout_data); + break; + } + case WorkoutMetricType_Distance: { + GTextNodeText *number_text_node = health_util_create_text_node( + HEALTH_WHOLE_AND_DECIMAL_LENGTH, number_font, TEXT_COLOR, container); + active_window->workout_controller->metric_to_string(metric_type, + (char *) number_text_node->text, + HEALTH_WHOLE_AND_DECIMAL_LENGTH, + i18n_owner, + active_window->workout_data); + +#if PBL_RECT + /// MI/KM units string + const char *units_string = active_window->workout_controller->get_distance_string( + i18n_noop("MI"), i18n_noop("KM")); + GTextNodeText *units_text_node = health_util_create_text_node_with_text( + i18n_get(units_string, i18n_owner), units_font, TEXT_COLOR, container); + units_text_node->node.offset.y = units_offset_y; +#endif + break; + } + case WorkoutMetricType_Custom: + { + const size_t buffer_size = 20; + GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, + TEXT_COLOR, container); + number_text_node->overflow = GTextOverflowModeTrailingEllipsis; + active_window->workout_controller->metric_to_string(metric_type, + (char *) number_text_node->text, + buffer_size, i18n_owner, + active_window->workout_data); + if (strlen(number_text_node->text) > 5) { + number_text_node->font = prv_get_number_font(false); + } + break; + } + case WorkoutMetricType_Duration: + { + const size_t buffer_size = sizeof("00:00:00"); + GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, + TEXT_COLOR, container); + active_window->workout_controller->metric_to_string(metric_type, + (char *)number_text_node->text, buffer_size, i18n_owner, active_window->workout_data); + + if (strlen(number_text_node->text) > 5) { + // text is long so use smaller font + number_text_node->font = prv_get_number_font(false); + } + break; + } + case WorkoutMetricType_Pace: + case WorkoutMetricType_AvgPace: + { + if (active_window->workout_controller->get_metric_value( + metric_type, active_window->workout_data) >= SECONDS_PER_HOUR) { + GTextNodeText *text_node = + health_util_create_text_node_with_text(EM_DASH, units_font, TEXT_COLOR, container); + text_node->node.offset.x += 1; + text_node->node.offset.y = units_offset_y; + } else { + const size_t buffer_size = sizeof("00:00:00"); + GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, + TEXT_COLOR, container); + active_window->workout_controller->metric_to_string(metric_type, + (char *)number_text_node->text, buffer_size, i18n_owner, active_window->workout_data); + +#if PBL_RECT + GTextNodeText *divider_text_node = health_util_create_text_node_with_text( + "/", units_font, TEXT_COLOR, container); + divider_text_node->node.offset.y = units_offset_y; + + /// MI/KM units string + const char *units_string = + active_window->workout_controller->get_distance_string(i18n_noop("MI"), + i18n_noop("KM")); + GTextNodeText *units_text_node = health_util_create_text_node_with_text( + i18n_get(units_string, i18n_owner), units_font, TEXT_COLOR, container); + units_text_node->node.offset.y = units_offset_y; +#endif + } + break; + } + case WorkoutMetricType_Speed: + { + const size_t buffer_size = sizeof("00:00:00"); + GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, + TEXT_COLOR, container); + active_window->workout_controller->metric_to_string(metric_type, + (char *)number_text_node->text, buffer_size, i18n_owner, active_window->workout_data); + +#if PBL_RECT + /// MI/KM units string + const char *units_string = + active_window->workout_controller->get_distance_string(i18n_noop("MPH"), + i18n_noop("KM/H")); + GTextNodeText *units_text_node = health_util_create_text_node_with_text( + i18n_get(units_string, i18n_owner), units_font, TEXT_COLOR, container); + units_text_node->node.offset.y = units_offset_y; +#endif + break; + } + // don't have default here so when we have a new type, we don't forget to add it here + case WorkoutMetricType_None: + case WorkoutMetricTypeCount: + break; + } + + return &container->node; +} + +static void prv_set_action_bar_icons(WorkoutActiveWindow *active_window) { + ActionBarLayer *action_bar = &active_window->action_bar; + bool is_paused = false; + bool can_stop = false; + if (active_window->workout_controller) { + is_paused = active_window->workout_controller->is_paused(); + can_stop = active_window->workout_controller->stop != NULL; + } + + if (is_paused) { + action_bar_layer_set_icon(action_bar, active_window->pause_button, + active_window->action_bar_start); + if (can_stop) { + action_bar_layer_set_icon(action_bar, BUTTON_ID_SELECT, active_window->action_bar_stop); + } + } else { + action_bar_layer_clear_icon(action_bar, BUTTON_ID_SELECT); + action_bar_layer_set_icon(action_bar, active_window->pause_button, + active_window->action_bar_pause); + } + + if (active_window->num_scrollable_metrics > 1) { + action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, active_window->action_bar_next); + } +} + +static void prv_update_ui(WorkoutActiveWindow *active_window) { + if (window_manager_is_window_visible(&active_window->window)) { + layer_mark_dirty(&active_window->base_layer); + + // Update the action bar in case another client updated the workout's status + prv_set_action_bar_icons(active_window); + } +} + +static void prv_hr_measuring_timer_callback(void *data) { + WorkoutActiveWindow *active_window = data; + + active_window->cur_hr_measuring_width_idx = (active_window->cur_hr_measuring_width_idx + 1) + % ARRAY_LENGTH(s_hr_measuring_widths); + + prv_update_ui(active_window); + + if (active_window->workout_controller->get_metric_value( + WorkoutMetricType_Hr, active_window->workout_data) == 0) { + int timeout_ms = (active_window->cur_hr_measuring_width_idx == 0) ? 800 : 200; + active_window->hr_measuring_timer = + app_timer_register(timeout_ms, prv_hr_measuring_timer_callback, active_window); + } else { + active_window->hr_measuring_timer = NULL; + } +} + +static void prv_update_timer_callback(void *data) { + WorkoutActiveWindow *active_window = data; + + if (active_window->workout_controller) { + active_window->workout_controller->update_data(active_window->workout_data); + } + + prv_update_ui(active_window); + active_window->update_timer = app_timer_register(1000, prv_update_timer_callback, active_window); + + const int bpm = active_window->workout_controller->get_metric_value( + WorkoutMetricType_Hr, active_window->workout_data); + if (bpm == 0 && !active_window->hr_measuring_timer) { + active_window->cur_hr_measuring_width_idx = 0; + prv_hr_measuring_timer_callback(active_window); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Drawing + +static void prv_draw_heart_icon(GContext *ctx, GBitmap *icon, const GRect *rect, + bool render, GSize *size_out) { + if (render) { + graphics_context_set_compositing_mode(ctx, GCompOpSet); + graphics_draw_bitmap_in_rect(ctx, icon, rect); + } + if (size_out) { + *size_out = rect->size; + } +} + +static void prv_draw_heart_node_callback(GContext *ctx, const GRect *box, + const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data) { + WorkoutActiveWindow *active_window = user_data; + GRect bounds = gbitmap_get_bounds(active_window->heart_icon); + bounds.origin = box->origin; + prv_draw_heart_icon(ctx, active_window->heart_icon, &bounds, render, size_out); +} + +static void prv_draw_hr_measuring_node_callback(GContext *ctx, const GRect *box, + const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data) { + WorkoutActiveWindow *active_window = user_data; + GRect bounds = gbitmap_get_bounds(active_window->hr_measuring_icon); + bounds.origin = box->origin; + bounds.size.w = s_hr_measuring_widths[active_window->cur_hr_measuring_width_idx]; + prv_draw_heart_icon(ctx, active_window->hr_measuring_icon, &bounds, render, size_out); +} + +static void prv_render_separator(GContext *ctx, Layer *layer) { + graphics_context_set_stroke_color(ctx, GColorBlack); + graphics_draw_horizontal_line_dotted(ctx, GPoint(0, layer->bounds.size.h - 1), + layer->bounds.size.w); +} + +static void prv_render_bg_color(GContext *ctx, GRect *bounds, GColor color) { + graphics_context_set_fill_color(ctx, color); + graphics_fill_rect(ctx, bounds); +} + +static void prv_render_metric_label(GContext *ctx, GRect *box, WorkoutMetricType metric_type, + WorkoutActiveWindow *active_window, void *i18n_owner) { + GRect label_box = *box; + GTextOverflowMode overflow_mode = GTextOverflowModeWordWrap; + if (metric_type == WorkoutMetricType_Custom) { + // I seriously have no idea why the height is hardcoded to 40 and overflow is set to word + // wrap when there's a note that says the height is being set to 40 to avoid wrapping. Also, + // with a font size of 18, I don't know how it wouldn't wrap. This fixes the inconsistent + // magic number problem for the WorkoutMetricType_Custom only + label_box.size.h = 20; + overflow_mode = GTextOverflowModeTrailingEllipsis; + } + + + graphics_context_set_text_color(ctx, TEXT_COLOR); + graphics_draw_text(ctx, + i18n_get(prv_get_label_for_metric(metric_type, active_window), i18n_owner), + fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), + label_box, + overflow_mode, + TEXT_ALIGNMENT, + NULL); +} + +static void prv_render_hr_zones(GContext *ctx, GRect *box, WorkoutActiveWindow *active_window) { + graphics_context_set_stroke_color(ctx, GColorBlack); + graphics_context_set_fill_color(ctx, GColorBlack); + + GRect zone_rect = *box; + zone_rect.origin.x += PBL_IF_RECT_ELSE(1, 69); + // add some padding after the label + zone_rect.origin.y += 10; + // size of a zone rect + zone_rect.size = GSize(20, 8); + + const int zone_padding = 2; + + for (HRZone i = HRZone_Zone1; i < HRZoneCount; i++) { + if (i <= hr_util_get_hr_zone(active_window->workout_controller->get_metric_value( + WorkoutMetricType_Hr, active_window->workout_data))) { + graphics_fill_rect(ctx, &zone_rect); + } else { + // drawing it twice to draw a 2px border + GRect inner_rect = grect_inset(zone_rect, GEdgeInsets(1)); + graphics_draw_rect(ctx, &zone_rect); + graphics_draw_rect(ctx, &inner_rect); + } + // increment x to draw more zones + zone_rect.origin.x += zone_rect.size.w + zone_padding; + } +} + +static void prv_render_metric(GContext *ctx, WorkoutMetricType metric_type, Layer *layer, + GColor bg_color, bool draw_hr_zones, bool prefer_larger_font) { + WorkoutActiveWindow *active_window = window_get_user_data(layer_get_window(layer)); + + prv_render_bg_color(ctx, &layer->bounds, bg_color); + + const int16_t rl_margin = PBL_IF_RECT_ELSE(5, 23); + + GRect rect = grect_inset(layer->bounds, GEdgeInsets(0, rl_margin)); + + // set rect y depending on layout, primary metric and display shape + if (active_window->layout == WorkoutLayout_SingleMetric) { + rect.origin.y = PBL_IF_RECT_ELSE(35, 41); + } else if (active_window->layout == WorkoutLayout_StaticAndScrollable) { + rect.origin.y = prefer_larger_font ? PBL_IF_RECT_ELSE(2, 13) : PBL_IF_RECT_ELSE(5, 1); + } else if (active_window->layout == WorkoutLayout_TwoStaticAndScrollable) { + rect.origin.y = (&active_window->scrollable_metric_layer == layer) ? + PBL_IF_RECT_ELSE(-2, 0) : PBL_IF_RECT_ELSE(-4, -2); + } + + // set the rect height so we don't wrap text to the next line + rect.size.h = 40; + +#if PBL_ROUND + if (draw_hr_zones) { + // padding between text and zones is less on round + rect.origin.y -= 10; + } + rect.origin.x -= 24; +#endif + + prv_render_metric_label(ctx, &rect, metric_type, active_window, layer); + + // update rect y for the label height + if (active_window->layout == WorkoutLayout_TwoStaticAndScrollable) { + rect.origin.y += prefer_larger_font ? 11 : 15; + } else { + rect.origin.y += prefer_larger_font ? 12 : 15; + } + + if (draw_hr_zones) { + prv_render_hr_zones(ctx, &rect, active_window); + // update rect y for the zones height + rect.origin.y += PBL_IF_RECT_ELSE(18, 15); + } + + // adjust rect for drawing the text node + rect.origin.x -= PBL_IF_RECT_ELSE(1, 46); + rect.size.w += (rl_margin * 2); + + GTextNode *text_node = prv_create_text_node(active_window, metric_type, + prefer_larger_font, layer); + graphics_text_node_draw(text_node, ctx, &rect, NULL, NULL); + graphics_text_node_destroy(text_node); +} + +static void prv_static_layer_update_proc(struct Layer *layer, GContext *ctx) { + WorkoutActiveWindow *active_window = window_get_user_data(layer_get_window(layer)); + + WorkoutMetricType metric_type = WorkoutMetricType_None; + if (layer == &active_window->top_metric_layer) { + metric_type = active_window->top_metric; + } else if (layer == &active_window->middle_metric_layer) { + metric_type = active_window->middle_metric; + } + + GColor bg_color = prv_get_bg_color_for_metric(metric_type, active_window, false); + + HRZone hr_zone = hr_util_get_hr_zone(active_window->workout_controller->get_metric_value( + metric_type, active_window->workout_data)); + const bool draw_zones = (metric_type == WorkoutMetricType_Hr) && hr_zone > HRZone_Zone0; + const bool prefer_larger_font = active_window->layout == WorkoutLayout_SingleMetric || + active_window->layout == WorkoutLayout_StaticAndScrollable; + + prv_render_metric(ctx, metric_type, layer, bg_color, draw_zones, prefer_larger_font); + + if (layer == &active_window->top_metric_layer) { + status_bar_layer_set_colors(&active_window->status_layer, bg_color, GColorBlack); + } + + if (active_window->layout == WorkoutLayout_StaticAndScrollable || + (active_window->layout == WorkoutLayout_TwoStaticAndScrollable && + layer == &active_window->middle_metric_layer)) { + prv_render_separator(ctx, layer); + } +} + +static void prv_scrollable_layer_update_proc(struct Layer *layer, GContext *ctx) { + WorkoutActiveWindow *active_window = window_get_user_data(layer_get_window(layer)); + + if (!active_window->num_scrollable_metrics) { + return; + } + + WorkoutMetricType metric_type = + active_window->scrollable_metrics[active_window->current_scrollable_metric]; + + GColor bg_color = prv_get_bg_color_for_metric(metric_type, active_window, true); + + const bool draw_hr_zones = false; + const bool prefer_larger_font = false; + prv_render_metric(ctx, metric_type, layer, bg_color, draw_hr_zones, prefer_larger_font); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! End Workout + +static void prv_end_workout_up_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutActiveWindow *active_window = context; + + if (active_window->workout_controller) { + active_window->workout_controller->stop(); + } + + workout_push_summary_window(); + + workout_dialog_pop(&active_window->end_workout_dialog); + app_window_stack_remove(&active_window->window, false); +} + +static void prv_end_workout_down_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutActiveWindow *active_window = context; + + workout_dialog_pop(&active_window->end_workout_dialog); +} + +static void prv_end_workout_click_config_provider(void *context) { + window_single_click_subscribe(BUTTON_ID_UP, prv_end_workout_up_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_end_workout_down_click_handler); +} + +static void prv_end_workout(void *context) { + WorkoutActiveWindow *active_window = context; + + WorkoutDialog *workout_dialog = &active_window->end_workout_dialog; + + workout_dialog_init(workout_dialog, "Workout End"); + Dialog *dialog = workout_dialog_get_dialog(workout_dialog); + + dialog_show_status_bar_layer(dialog, true); + dialog_set_fullscreen(dialog, true); + dialog_set_text(dialog, i18n_get("End Workout?", workout_dialog)); + dialog_set_background_color(dialog, BACKGROUND_COLOR); + dialog_set_text_color(dialog, TEXT_COLOR); + dialog_set_icon(dialog, RESOURCE_ID_WORKOUT_APP_END); + dialog_set_icon_animate_direction(dialog, DialogIconAnimateNone); + dialog_set_destroy_on_pop(dialog, false); + + i18n_free_all(workout_dialog); + + workout_dialog_set_click_config_provider(workout_dialog, prv_end_workout_click_config_provider); + workout_dialog_set_click_config_context(workout_dialog, context); + + app_workout_dialog_push(workout_dialog); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Handlers + +static void prv_handle_pause_button(WorkoutActiveWindow *active_window) { + bool is_paused = false; + if (active_window->workout_controller) { + is_paused = active_window->workout_controller->is_paused(); + } + + if (active_window->workout_controller) { + active_window->workout_controller->pause(!is_paused); + } + + prv_update_ui(active_window); +} + +static void prv_handle_stop_button(WorkoutActiveWindow *active_window) { + bool is_paused = false; + bool can_stop = false; + if (active_window->workout_controller) { + is_paused = active_window->workout_controller->is_paused(); + can_stop = active_window->workout_controller->stop != NULL; + } + + if (!is_paused || !can_stop) { + return; + } + + prv_end_workout(active_window); +} + +static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutActiveWindow *active_window = context; + + if (active_window->pause_button == BUTTON_ID_UP) { + prv_handle_pause_button(active_window); + } +} + +static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutActiveWindow *active_window = context; + + if (active_window->pause_button == BUTTON_ID_SELECT) { + prv_handle_pause_button(active_window); + } else { + prv_handle_stop_button(active_window); + } +} + +static void prv_set_pause_button(WorkoutActiveWindow *active_window) { + bool can_stop = active_window->workout_controller->stop != NULL; + if (can_stop || active_window->num_scrollable_metrics > 1) { + active_window->pause_button = BUTTON_ID_UP; + } else { + active_window->pause_button = BUTTON_ID_SELECT; + } +} + +T_STATIC void prv_cycle_scrollable_metrics(WorkoutActiveWindow *active_window) { + active_window->current_scrollable_metric = + (active_window->current_scrollable_metric + 1) % active_window->num_scrollable_metrics; +} + +static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutActiveWindow *active_window = context; + prv_cycle_scrollable_metrics(active_window); +} + +static void prv_click_config_provider(void *context) { + window_set_click_context(BUTTON_ID_UP, context); + window_set_click_context(BUTTON_ID_SELECT, context); + window_set_click_context(BUTTON_ID_DOWN, context); + window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); +} + +static void prv_window_unload_handler(Window *window) { + WorkoutActiveWindow *active_window = window_get_user_data(window); + if (active_window) { + app_timer_cancel(active_window->update_timer); + app_timer_cancel(active_window->hr_measuring_timer); + gbitmap_destroy(active_window->action_bar_start); + gbitmap_destroy(active_window->action_bar_pause); + gbitmap_destroy(active_window->action_bar_stop); + gbitmap_destroy(active_window->action_bar_more); + gbitmap_destroy(active_window->action_bar_next); + gbitmap_destroy(active_window->heart_icon); + gbitmap_destroy(active_window->hr_measuring_icon); + action_bar_layer_deinit(&active_window->action_bar); + status_bar_layer_deinit(&active_window->status_layer); + layer_deinit(&active_window->top_metric_layer); + layer_deinit(&active_window->scrollable_metric_layer); + layer_deinit(&active_window->base_layer); + window_deinit(&active_window->window); + i18n_free_all(active_window); + app_free(active_window); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Common Setup +static void prv_create_window_common(WorkoutActiveWindow *active_window, + void *workout_data, + WorkoutController *workout_controller) { + active_window->workout_data = workout_data; + active_window->workout_controller = workout_controller; + + Window *window = &active_window->window; + window_init(window, WINDOW_NAME("Workout Active Info")); + window_set_user_data(window, active_window); + window_set_background_color(window, BACKGROUND_COLOR); + window_set_window_handlers(window, &(WindowHandlers){ + .unload = prv_window_unload_handler, + }); + + GRect base_layer_bounds = window->layer.bounds; +#if PBL_RECT + base_layer_bounds.size.w -= ACTION_BAR_WIDTH; +#endif + + base_layer_bounds.origin.y = STATUS_BAR_LAYER_HEIGHT; + layer_init(&active_window->base_layer, &base_layer_bounds); + layer_add_child(&window->layer, &active_window->base_layer); + base_layer_bounds.origin.y = 0; + + if (active_window->layout == WorkoutLayout_SingleMetric) { + // Only 1 metric to show. It can have the whole screen + GRect metric_bounds = base_layer_bounds; + layer_init(&active_window->top_metric_layer, &metric_bounds); + layer_set_update_proc(&active_window->top_metric_layer, prv_static_layer_update_proc); + layer_add_child(&active_window->base_layer, &active_window->top_metric_layer); + } else if (active_window->layout == WorkoutLayout_StaticAndScrollable) { + // Two metrics. 1 big static metric above a smaller scrollable metric + GRect top_metric_bounds = base_layer_bounds; +#if PBL_DISPLAY_HEIGHT >= 200 + top_metric_bounds.size.h = PBL_IF_RECT_ELSE(105, 115); +#else + top_metric_bounds.size.h = PBL_IF_RECT_ELSE(90, 77); +#endif + layer_init(&active_window->top_metric_layer, &top_metric_bounds); + layer_set_update_proc(&active_window->top_metric_layer, prv_static_layer_update_proc); + layer_add_child(&active_window->base_layer, &active_window->top_metric_layer); + + GRect scrollable_metric_bounds = top_metric_bounds; + scrollable_metric_bounds.origin.y = scrollable_metric_bounds.size.h; + scrollable_metric_bounds.size.h = window->layer.bounds.size.h - + scrollable_metric_bounds.origin.y; + layer_init(&active_window->scrollable_metric_layer, &scrollable_metric_bounds); + layer_set_update_proc(&active_window->scrollable_metric_layer, + prv_scrollable_layer_update_proc); + layer_add_child(&active_window->base_layer, &active_window->scrollable_metric_layer); + } else if (active_window->layout == WorkoutLayout_TwoStaticAndScrollable) { + // Three metrics. Two static metrics above a scrollable metric +#if PBL_DISPLAY_HEIGHT >= 200 + const int layer_height = 68; +#else + const int layer_height = 51; +#endif + GRect top_metric_bounds = base_layer_bounds; + top_metric_bounds.size.h = layer_height; + layer_init(&active_window->top_metric_layer, &top_metric_bounds); + layer_set_update_proc(&active_window->top_metric_layer, prv_static_layer_update_proc); + layer_add_child(&active_window->base_layer, &active_window->top_metric_layer); + + GRect middle_metric_bounds = top_metric_bounds; + middle_metric_bounds.origin.y = top_metric_bounds.size.h; + middle_metric_bounds.size.h = layer_height - 2; + layer_init(&active_window->middle_metric_layer, &middle_metric_bounds); + layer_set_update_proc(&active_window->middle_metric_layer, prv_static_layer_update_proc); + layer_add_child(&active_window->base_layer, &active_window->middle_metric_layer); + + GRect scrollable_metric_bounds = middle_metric_bounds; + scrollable_metric_bounds.origin.y = top_metric_bounds.size.h + middle_metric_bounds.size.h; + scrollable_metric_bounds.size.h = layer_height + 10; + layer_init(&active_window->scrollable_metric_layer, &scrollable_metric_bounds); + layer_set_update_proc(&active_window->scrollable_metric_layer, + prv_scrollable_layer_update_proc); + layer_add_child(&active_window->base_layer, &active_window->scrollable_metric_layer); + } + + StatusBarLayer *status_layer = &active_window->status_layer; + status_bar_layer_init(status_layer); + status_bar_layer_set_colors(status_layer, GColorClear, GColorBlack); + layer_add_child(&window->layer, status_bar_layer_get_layer(status_layer)); + +#if PBL_RECT + GRect status_layer_bounds = window->layer.bounds; + status_layer_bounds.size.w -= ACTION_BAR_WIDTH; + layer_set_frame(&status_layer->layer, &status_layer_bounds); +#endif + + ActionBarLayer *action_bar = &active_window->action_bar; + action_bar_layer_init(action_bar); + action_bar_layer_set_context(action_bar, active_window); + action_bar_layer_set_click_config_provider(action_bar, prv_click_config_provider); + action_bar_layer_add_to_window(action_bar, window); + + active_window->heart_icon = gbitmap_create_with_resource(RESOURCE_ID_WORKOUT_APP_HEART), + active_window->hr_measuring_icon = + gbitmap_create_with_resource(RESOURCE_ID_WORKOUT_APP_MEASURING_HR), + + active_window->action_bar_start = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_START); + active_window->action_bar_pause = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_PAUSE); + active_window->action_bar_stop = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_STOP); + active_window->action_bar_more = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_MORE); + active_window->action_bar_next = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_TOGGLE); + + prv_set_pause_button(active_window); + prv_set_action_bar_icons(active_window); + + active_window->update_timer = app_timer_register(1000, prv_update_timer_callback, active_window); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Public API + +WorkoutActiveWindow *workout_active_create_single_layout(WorkoutMetricType metric, + void *workout_data, + WorkoutController *workout_controller) { + if (metric == WorkoutMetricType_None) { + PBL_LOG_ERR("Invalid argument"); + return NULL; + } + + WorkoutActiveWindow *active_window = app_zalloc_check(sizeof(WorkoutActiveWindow)); + active_window->layout = WorkoutLayout_SingleMetric; + + active_window->top_metric = metric; + + prv_create_window_common(active_window, workout_data, workout_controller); + + return active_window; +} + +WorkoutActiveWindow *workout_active_create_double_layout(WorkoutMetricType top_metric, + int num_scrollable_metrics, + WorkoutMetricType *scrollable_metrics, + void *workout_data, + WorkoutController *workout_controller) { + if (top_metric == WorkoutMetricType_None || num_scrollable_metrics == 0 || !scrollable_metrics) { + PBL_LOG_ERR("Invalid argument(s)"); + return NULL; + } + + WorkoutActiveWindow *active_window = app_zalloc_check(sizeof(WorkoutActiveWindow)); + active_window->layout = WorkoutLayout_StaticAndScrollable; + + active_window->top_metric = top_metric; + prv_add_scrollable_metrics(active_window, num_scrollable_metrics, scrollable_metrics); + + prv_create_window_common(active_window, workout_data, workout_controller); + + return active_window; +} + +WorkoutActiveWindow *workout_active_create_tripple_layout(WorkoutMetricType top_metric, + WorkoutMetricType middle_metric, + int num_scrollable_metrics, + WorkoutMetricType *scrollable_metrics, + void *workout_data, + WorkoutController *workout_controller) { + if (top_metric == WorkoutMetricType_None || middle_metric == WorkoutMetricType_None || + (num_scrollable_metrics != 0 && !scrollable_metrics)) { + PBL_LOG_ERR("Invalid argument(s)"); + return NULL; + } + + WorkoutActiveWindow *active_window = app_zalloc_check(sizeof(WorkoutActiveWindow)); + active_window->layout = WorkoutLayout_TwoStaticAndScrollable; + + active_window->top_metric = top_metric; + active_window->middle_metric = middle_metric; + prv_add_scrollable_metrics(active_window, num_scrollable_metrics, scrollable_metrics); + + prv_create_window_common(active_window, workout_data, workout_controller); + + return active_window; +} + +WorkoutActiveWindow *workout_active_create_for_activity_type(ActivitySessionType type, + void *workout_data, WorkoutController *workout_controller) { + const bool hrm_is_available = activity_is_hrm_present() && activity_prefs_heart_rate_is_enabled(); + + switch (type) { + case ActivitySessionType_Open: + { + if (hrm_is_available) { + WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Duration}; + return workout_active_create_double_layout(WorkoutMetricType_Hr, + ARRAY_LENGTH(scrollable_metrics), + scrollable_metrics, + workout_data, + workout_controller); + } else { + return workout_active_create_single_layout(WorkoutMetricType_Duration, + workout_data, + workout_controller); + } + } + case ActivitySessionType_Walk: + { + if (hrm_is_available) { + WorkoutMetricType top_metric = WorkoutMetricType_Hr; + WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Duration, + WorkoutMetricType_Distance, + WorkoutMetricType_AvgPace, + WorkoutMetricType_Steps}; + return workout_active_create_double_layout(top_metric, + ARRAY_LENGTH(scrollable_metrics), + scrollable_metrics, + workout_data, + workout_controller); + } else { + WorkoutMetricType top_metric = WorkoutMetricType_Duration; + WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Distance, + WorkoutMetricType_AvgPace, + WorkoutMetricType_Steps}; + return workout_active_create_double_layout(top_metric, + ARRAY_LENGTH(scrollable_metrics), + scrollable_metrics, + workout_data, + workout_controller); + } + } + case ActivitySessionType_Run: + { + if (hrm_is_available) { + WorkoutMetricType top_metric = WorkoutMetricType_Hr; + WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Duration, + WorkoutMetricType_AvgPace, + WorkoutMetricType_Distance}; + return workout_active_create_double_layout(top_metric, + ARRAY_LENGTH(scrollable_metrics), + scrollable_metrics, + workout_data, + workout_controller); + } else { + WorkoutMetricType top_metric = WorkoutMetricType_Duration; + WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_AvgPace, + WorkoutMetricType_Distance}; + return workout_active_create_double_layout(top_metric, + ARRAY_LENGTH(scrollable_metrics), + scrollable_metrics, + workout_data, + workout_controller); + } + } + default: + return NULL; + } +} + +void workout_active_window_push(WorkoutActiveWindow *active_window) { + app_window_stack_push(&active_window->window, true); +} + +void workout_active_update_scrollable_metrics(WorkoutActiveWindow *active_window, + int num_scrollable_metrics, + WorkoutMetricType *scrollable_metrics) { + active_window->num_scrollable_metrics = 0; + prv_add_scrollable_metrics(active_window, num_scrollable_metrics, scrollable_metrics); + + prv_set_pause_button(active_window); + + if (active_window->current_scrollable_metric >= active_window->num_scrollable_metrics) { + active_window->current_scrollable_metric = 0; + } + + prv_update_ui(active_window); +} diff --git a/src/fw/apps/system/workout/active.h b/src/fw/apps/system/workout/active.h new file mode 100644 index 0000000000..503cf28d61 --- /dev/null +++ b/src/fw/apps/system/workout/active.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "controller.h" +#include "metrics.h" + +#include "pbl/services/activity/activity.h" + +typedef struct WorkoutActiveWindow WorkoutActiveWindow; + + +WorkoutActiveWindow *workout_active_create_single_layout(WorkoutMetricType metric, + void *workout_data, + WorkoutController *workout_controller); + +WorkoutActiveWindow *workout_active_create_double_layout(WorkoutMetricType top_metric, + int num_scrollable_metrics, + WorkoutMetricType *scrollable_metrics, + void *workout_data, + WorkoutController *workout_controller); + +WorkoutActiveWindow *workout_active_create_tripple_layout(WorkoutMetricType top_metric, + WorkoutMetricType middle_metric, + int num_scrollable_metrics, + WorkoutMetricType *scrollable_metrics, + void *workout_data, + WorkoutController *workout_controller); + +WorkoutActiveWindow *workout_active_create_for_activity_type(ActivitySessionType type, + void *workout_data, + WorkoutController *workout_controller); + +void workout_active_window_push(WorkoutActiveWindow *window); + +void workout_active_update_scrollable_metrics(WorkoutActiveWindow *active_window, + int num_scrollable_metrics, + WorkoutMetricType *scrollable_metrics); diff --git a/src/fw/apps/system/workout/controller.h b/src/fw/apps/system/workout/controller.h new file mode 100644 index 0000000000..ca4d7569bd --- /dev/null +++ b/src/fw/apps/system/workout/controller.h @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "metrics.h" + +#include +#include +#include + +typedef struct WorkoutController { + bool (*is_paused)(void); + bool (*pause)(bool should_be_paused); + bool (*stop)(void); + + void (*update_data)(void *data); + void (*metric_to_string)(WorkoutMetricType type, char *buffer, + size_t buffer_size, void *i18n_owner, void *workout_data); + int32_t (*get_metric_value)(WorkoutMetricType type, void *data); + const char* (*get_distance_string)(const char *miles_string, const char *km_string); + char* (*get_custom_metric_label_string)(void); +} WorkoutController; diff --git a/src/fw/apps/system/workout/countdown.c b/src/fw/apps/system/workout/countdown.c new file mode 100644 index 0000000000..ecc1bc29fe --- /dev/null +++ b/src/fw/apps/system/workout/countdown.c @@ -0,0 +1,97 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "countdown.h" + +#include "applib/ui/ui.h" +#include "applib/ui/kino/kino_layer.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" + +#define TIMER_DURATION (1000) +#define NUM_IMAGES (3) + +typedef struct WorkoutCountdownWindow { + Window window; + Layer base_layer; + + KinoReel *images[NUM_IMAGES]; + int current_image; + + StartWorkoutCallback start_workout_cb; + ActivitySessionType activity_type; + + AppTimer *timer; +} WorkoutCountdownWindow; + +static void prv_base_layer_update_proc(Layer *layer, GContext *ctx) { + WorkoutCountdownWindow *countdown_window = window_get_user_data(layer_get_window(layer)); + + KinoReel *image = countdown_window->images[countdown_window->current_image]; + + const GSize icon_size = kino_reel_get_size(image); + + GPoint offset; + offset.x = (layer->bounds.size.w / 2) - (icon_size.w / 2); + offset.y = (layer->bounds.size.h / 2) - (icon_size.h / 2); + kino_reel_draw(image, ctx, offset); +} + +static void prv_timer_callback(void *data) { + WorkoutCountdownWindow *countdown_window = data; + + if (countdown_window->current_image <= 0) { + countdown_window->start_workout_cb(countdown_window->activity_type); + window_stack_remove(&countdown_window->window, false); + vibes_long_pulse(); + return; + } + + countdown_window->current_image--; + + layer_mark_dirty(&countdown_window->base_layer); + + app_timer_register(TIMER_DURATION, prv_timer_callback, countdown_window); +} + +static void prv_window_unload_handler(Window *window) { + WorkoutCountdownWindow *countdown_window = window_get_user_data(window); + if (countdown_window) { + for (int i = 0; i < NUM_IMAGES; i++) { + kino_reel_destroy(countdown_window->images[i]); + } + app_timer_cancel(countdown_window->timer); + layer_deinit(&countdown_window->base_layer); + window_deinit(&countdown_window->window); + app_free(countdown_window); + } +} + +void workout_countdown_start(ActivitySessionType type, StartWorkoutCallback start_workout_cb) { + WorkoutCountdownWindow *countdown_window = app_zalloc_check(sizeof(WorkoutCountdownWindow)); + + countdown_window->start_workout_cb = start_workout_cb; + countdown_window->activity_type = type; + + Window *window = &countdown_window->window; + window_init(window, WINDOW_NAME("Workout Countdown")); + window_set_user_data(window, countdown_window); + window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorYellow, GColorDarkGray)); + window_set_window_handlers(window, &(WindowHandlers){ + .unload = prv_window_unload_handler, + }); + + layer_init(&countdown_window->base_layer, &window->layer.bounds); + layer_set_update_proc(&countdown_window->base_layer, prv_base_layer_update_proc); + layer_add_child(&window->layer, &countdown_window->base_layer); + + countdown_window->images[0] = kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_ONE); + countdown_window->images[1] = kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_TWO); + countdown_window->images[2] = kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_THREE); + + countdown_window->current_image = NUM_IMAGES - 1; + + app_timer_register(TIMER_DURATION, prv_timer_callback, countdown_window); + + app_window_stack_push(window, true); +} diff --git a/src/fw/apps/system/workout/countdown.h b/src/fw/apps/system/workout/countdown.h new file mode 100644 index 0000000000..b55513ea2c --- /dev/null +++ b/src/fw/apps/system/workout/countdown.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "summary.h" + +#include "pbl/services/activity/activity.h" + +void workout_countdown_start(ActivitySessionType type, StartWorkoutCallback start_workout_cb); diff --git a/src/fw/apps/system/workout/data.c b/src/fw/apps/system/workout/data.c new file mode 100644 index 0000000000..77ba3b75d8 --- /dev/null +++ b/src/fw/apps/system/workout/data.c @@ -0,0 +1,84 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "data.h" + +#include "pbl/services/activity/health_util.h" +#include "pbl/services/activity/workout_service.h" + +#include + +void workout_data_update(void *data) { + if (!data) { + return; + } + WorkoutData *workout_data = data; + + workout_service_get_current_workout_info(&workout_data->steps, &workout_data->duration_s, + &workout_data->distance_m, &workout_data->bpm, + &workout_data->hr_zone); + + if (workout_data->duration_s && workout_data->distance_m) { + workout_data->avg_pace = health_util_get_pace(workout_data->duration_s, + workout_data->distance_m); + } +} + +void workout_data_fill_metric_value(WorkoutMetricType type, char *buffer, size_t buffer_size, + void *i18n_owner, void *data) { + int32_t metric_value = workout_data_get_metric_value(type, data); + + switch (type) { + case WorkoutMetricType_Hr: + case WorkoutMetricType_Steps: + { + snprintf(buffer, buffer_size, "%"PRId32, metric_value); + break; + } + case WorkoutMetricType_Distance: + { + const int conversion_factor = health_util_get_distance_factor(); + health_util_format_whole_and_decimal(buffer, buffer_size, metric_value, conversion_factor); + break; + } + case WorkoutMetricType_Duration: + { + health_util_format_hours_minutes_seconds(buffer, buffer_size, metric_value, + true, i18n_owner); + break; + } + case WorkoutMetricType_Pace: + case WorkoutMetricType_AvgPace: + { + health_util_format_hours_minutes_seconds(buffer, buffer_size, metric_value, + false, i18n_owner); + break; + } + case WorkoutMetricType_Speed: + // Not part of the workout service yet + case WorkoutMetricType_Custom: + // Sports app only + case WorkoutMetricType_None: + case WorkoutMetricTypeCount: + break; + } +} + +int32_t workout_data_get_metric_value(WorkoutMetricType type, void *data) { + WorkoutData *workout_data = data; + + switch (type) { + case WorkoutMetricType_Hr: + return workout_data->bpm; + case WorkoutMetricType_Duration: + return workout_data->duration_s; + case WorkoutMetricType_AvgPace: + return workout_data->avg_pace; + case WorkoutMetricType_Distance: + return workout_data->distance_m; + case WorkoutMetricType_Steps: + return workout_data->steps; + default: + return 0; + } +} diff --git a/src/fw/apps/system/workout/data.h b/src/fw/apps/system/workout/data.h new file mode 100644 index 0000000000..79997bbb07 --- /dev/null +++ b/src/fw/apps/system/workout/data.h @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "metrics.h" + +#include "pbl/services/activity/hr_util.h" + +#include +#include +#include + +typedef struct WorkoutData { + int32_t steps; + int32_t duration_s; + int32_t distance_m; + int32_t avg_pace; + int32_t bpm; + HRZone hr_zone; +} WorkoutData; + +void workout_data_update(void *workout_data); + +void workout_data_fill_metric_value(WorkoutMetricType type, char *buffer, + size_t buffer_size, void *i18n_owner, void *workout_data); + +int32_t workout_data_get_metric_value(WorkoutMetricType type, void *workout_data); diff --git a/src/fw/apps/system/workout/dialog.c b/src/fw/apps/system/workout/dialog.c new file mode 100644 index 0000000000..e46e65fb1d --- /dev/null +++ b/src/fw/apps/system/workout/dialog.c @@ -0,0 +1,263 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "dialog.h" + +#include "applib/applib_malloc.auto.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/dialogs/dialog_private.h" +#include "kernel/ui/kernel_ui.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "system/passert.h" + +#define TEXT_FLOW_INSET_PX (PBL_IF_RECT_ELSE(0, 8)) + +static void prv_workout_dialog_load(Window *window) { + WorkoutDialog *workout_dialog = window_get_user_data(window); + Dialog *dialog = &workout_dialog->dialog; + Layer *window_root_layer = window_get_root_layer(window); + + // Ownership of icon is taken over by KinoLayer in dialog_init_icon_layer() call below + KinoReel *icon = dialog_create_icon(dialog); + const GSize icon_size = icon ? kino_reel_get_size(icon) : GSizeZero; + + const bool show_action_bar = !workout_dialog->hide_action_bar; + + const GRect *bounds = &window_root_layer->bounds; + const uint16_t icon_single_line_text_offset_px = 9; + const uint16_t small_icon_offset = (icon_size.h < 60) ? 7 : 0; + const uint16_t left_margin_px = PBL_IF_RECT_ELSE(5, 0); + const uint16_t action_bar_width = show_action_bar ? ACTION_BAR_WIDTH : 0; + const uint16_t content_and_action_bar_horizontal_spacing = + PBL_IF_RECT_ELSE(left_margin_px, show_action_bar ? 11 : left_margin_px); + const uint16_t right_margin_px = action_bar_width + content_and_action_bar_horizontal_spacing; + const uint16_t text_single_line_text_offset_px = 17; + const int16_t text_layer_line_spacing_delta = -4; + const GFont dialog_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + const GFont dialog_subtext_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); + const int single_line_text_height_px = fonts_get_font_height(dialog_text_font); + const int max_text_line_height_px = 2 * single_line_text_height_px + 8; + + const uint16_t status_layer_offset = dialog->show_status_layer ? 6 : 0; + uint16_t text_top_margin_px = icon ? icon_size.h + 22 : 6; + uint16_t subtext_top_margin_px = text_top_margin_px + single_line_text_height_px; + uint16_t icon_top_margin_px = PBL_IF_RECT_ELSE(18, 22); + uint16_t text_height; + uint16_t x = 0; + uint16_t y = 0; + uint16_t w = PBL_IF_RECT_ELSE(bounds->size.w - action_bar_width, bounds->size.w); + uint16_t h = STATUS_BAR_LAYER_HEIGHT; + + if (dialog->show_status_layer) { + dialog_add_status_bar_layer(dialog, &GRect(x, y, w, h)); + } + + x = left_margin_px; + w = bounds->size.w - left_margin_px - right_margin_px; + + GTextAttributes *text_attributes = NULL; +#if PBL_ROUND + // Create a GTextAttributes for the TextLayer. Note that the matching + // graphics_text_attributes_destroy() will not need to be called here, as the ownership + // of text_attributes will be transferred to the TextLayer we assign it to. + text_attributes = graphics_text_attributes_create(); + graphics_text_attributes_enable_screen_text_flow(text_attributes, TEXT_FLOW_INSET_PX); +#endif + // Check if the text takes up more than one line. If the dialog has a single line of text, + // the icon and line of text are positioned lower so as to be more vertically centered. + GContext *ctx = graphics_context_get_current_context(); + const GTextAlignment text_alignment = PBL_IF_RECT_ELSE(GTextAlignmentCenter, + show_action_bar ? GTextAlignmentRight : GTextAlignmentCenter); + { + // do all this in a block so we enforce that nobody uses these variables outside of the block + // when dealing with round displays, sizes change depending on location. + const GRect probe_rect = GRect(x, y + text_single_line_text_offset_px, + w, max_text_line_height_px); + text_height = graphics_text_layout_get_max_used_size(ctx, + dialog->buffer, + dialog_text_font, + probe_rect, + GTextOverflowModeWordWrap, + text_alignment, + text_attributes).h; + if (text_height <= single_line_text_height_px) { + text_top_margin_px += text_single_line_text_offset_px; + icon_top_margin_px += icon_single_line_text_offset_px; + } else { + text_top_margin_px += status_layer_offset + small_icon_offset + 2; + icon_top_margin_px += status_layer_offset + small_icon_offset; + } + subtext_top_margin_px = text_top_margin_px + text_height + text_layer_line_spacing_delta; + } + + y = text_top_margin_px; + h = text_height; + + // Set up the text. + TextLayer *text_layer = &dialog->text_layer; + text_layer_init_with_parameters(text_layer, &GRect(x, y, w, h), + dialog->buffer, dialog_text_font, + dialog->text_color, GColorClear, text_alignment, + GTextOverflowModeWordWrap); +#if PBL_ROUND + text_layer_enable_screen_text_flow_and_paging(text_layer, TEXT_FLOW_INSET_PX); +#endif + text_layer_set_line_spacing_delta(text_layer, text_layer_line_spacing_delta); + + layer_add_child(&window->layer, &text_layer->layer); + + if (workout_dialog->subtext_buffer) { + y = subtext_top_margin_px; + + TextLayer *subtext_layer = &workout_dialog->subtext_layer; + text_layer_init_with_parameters(subtext_layer, &GRect(x, y, w, h), + workout_dialog->subtext_buffer, dialog_subtext_font, + dialog->text_color, GColorClear, text_alignment, + GTextOverflowModeWordWrap); +#if PBL_ROUND + text_layer_enable_screen_text_flow_and_paging(subtext_layer, TEXT_FLOW_INSET_PX); +#endif + + layer_add_child(&window->layer, &subtext_layer->layer); + } + + if (show_action_bar) { + action_bar_layer_add_to_window(&workout_dialog->action_bar, window); + } + + // Icon + // On rectangular displays we just center it horizontally b/w the left edge of the display and + // the left edge of the action bar +#if PBL_RECT + x = (grect_get_max_x(bounds) - action_bar_width - icon_size.w) / 2; +#else + // On round displays we right align it with respect to the same imaginary vertical line that the + // text is right aligned to if action bar is present otherwise do what rect does + if (show_action_bar) { + x = grect_get_max_x(bounds) - action_bar_width - content_and_action_bar_horizontal_spacing - + icon_size.w; + } else { + x = (grect_get_max_x(bounds) - action_bar_width - icon_size.w) / 2; + } +#endif + + y = icon_top_margin_px; + + if (dialog_init_icon_layer(dialog, icon, GPoint(x, y), false)) { + layer_add_child(window_root_layer, &dialog->icon_layer.layer); + } + + dialog_load(dialog); +} + +static void prv_workout_dialog_appear(Window *window) { + WorkoutDialog *workout_dialog = window_get_user_data(window); + dialog_appear(&workout_dialog->dialog); +} + +static void prv_workout_dialog_unload(Window *window) { + WorkoutDialog *workout_dialog = window_get_user_data(window); + dialog_unload(&workout_dialog->dialog); + + action_bar_layer_remove_from_window(&workout_dialog->action_bar); + action_bar_layer_deinit(&workout_dialog->action_bar); + + gbitmap_deinit(&workout_dialog->confirm_icon); + gbitmap_deinit(&workout_dialog->decline_icon); + + if (workout_dialog->subtext_buffer) { + applib_free(workout_dialog->subtext_buffer); + } + + if (workout_dialog->dialog.destroy_on_pop) { + applib_free(workout_dialog); + } +} + +void workout_dialog_init(WorkoutDialog *workout_dialog, const char *dialog_name) { + PBL_ASSERTN(workout_dialog); + *workout_dialog = (WorkoutDialog){}; + + dialog_init(&workout_dialog->dialog, dialog_name); + Window *window = &workout_dialog->dialog.window; + window_set_window_handlers(window, &(WindowHandlers) { + .load = prv_workout_dialog_load, + .unload = prv_workout_dialog_unload, + .appear = prv_workout_dialog_appear, + }); + window_set_user_data(window, workout_dialog); + + gbitmap_init_with_resource(&workout_dialog->confirm_icon, RESOURCE_ID_ACTION_BAR_ICON_CHECK); + gbitmap_init_with_resource(&workout_dialog->decline_icon, RESOURCE_ID_ACTION_BAR_ICON_X); + + ActionBarLayer *action_bar = &workout_dialog->action_bar; + action_bar_layer_init(action_bar); + action_bar_layer_set_icon(action_bar, BUTTON_ID_UP, &workout_dialog->confirm_icon); + action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, &workout_dialog->decline_icon); + action_bar_layer_set_background_color(action_bar, GColorBlack); + action_bar_layer_set_context(action_bar, workout_dialog); +} + +WorkoutDialog *workout_dialog_create(const char *dialog_name) { + // Note: Not exported so no need for padding. + WorkoutDialog *workout_dialog = applib_malloc(sizeof(WorkoutDialog)); + if (workout_dialog) { + workout_dialog_init(workout_dialog, dialog_name); + } + return workout_dialog; +} + +Dialog *workout_dialog_get_dialog(WorkoutDialog *workout_dialog) { + return &workout_dialog->dialog; +} + +ActionBarLayer *workout_dialog_get_action_bar(WorkoutDialog *workout_dialog) { + return &workout_dialog->action_bar; +} + +void workout_dialog_set_click_config_provider(WorkoutDialog *workout_dialog, + ClickConfigProvider click_config_provider) { + if (workout_dialog == NULL) { + return; + } + ActionBarLayer *action_bar = &workout_dialog->action_bar; + action_bar_layer_set_click_config_provider(action_bar, click_config_provider); +} + +void workout_dialog_set_click_config_context(WorkoutDialog *workout_dialog, void *context) { + if (workout_dialog == NULL) { + return; + } + ActionBarLayer *action_bar = &workout_dialog->action_bar; + action_bar_layer_set_context(action_bar, context); +} + +void workout_dialog_push(WorkoutDialog *workout_dialog, WindowStack *window_stack) { + dialog_push(&workout_dialog->dialog, window_stack); +} + +void app_workout_dialog_push(WorkoutDialog *workout_dialog) { + app_dialog_push(&workout_dialog->dialog); +} + +void workout_dialog_pop(WorkoutDialog *workout_dialog) { + dialog_pop(&workout_dialog->dialog); +} + +void workout_dialog_set_text(WorkoutDialog *workout_dialog, const char *text) { + dialog_set_text(&workout_dialog->dialog, text); +} + +void workout_dialog_set_subtext(WorkoutDialog *workout_dialog, const char *text) { + if (workout_dialog->subtext_buffer) { + applib_free(workout_dialog->subtext_buffer); + } + uint16_t len = strlen(text); + workout_dialog->subtext_buffer = applib_malloc(len + 1); + strncpy(workout_dialog->subtext_buffer, text, len + 1); +} + +void workout_dialog_set_action_bar_hidden(WorkoutDialog *workout_dialog, bool should_hide) { + workout_dialog->hide_action_bar = should_hide; +} diff --git a/src/fw/apps/system/workout/dialog.h b/src/fw/apps/system/workout/dialog.h new file mode 100644 index 0000000000..555fc226ce --- /dev/null +++ b/src/fw/apps/system/workout/dialog.h @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/ui/action_bar_layer.h" +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/window_stack.h" + +typedef struct WorkoutDialog { + Dialog dialog; + ActionBarLayer action_bar; + GBitmap confirm_icon; + GBitmap decline_icon; + TextLayer subtext_layer; + char *subtext_buffer; + bool hide_action_bar; +} WorkoutDialog; + +void workout_dialog_init(WorkoutDialog *workout_dialog, const char *dialog_name); + +WorkoutDialog *workout_dialog_create(const char *dialog_name); + +Dialog *workout_dialog_get_dialog(WorkoutDialog *workout_dialog); + +ActionBarLayer *workout_dialog_get_action_bar(WorkoutDialog *workout_dialog); + +void workout_dialog_set_click_config_provider(WorkoutDialog *workout_dialog, + ClickConfigProvider click_config_provider); + +void workout_dialog_set_click_config_context(WorkoutDialog *workout_dialog, void *context); + +void workout_dialog_push(WorkoutDialog *workout_dialog, WindowStack *window_stack); + +void app_workout_dialog_push(WorkoutDialog *workout_dialog); + +void workout_dialog_pop(WorkoutDialog *workout_dialog); + +void workout_dialog_set_text(WorkoutDialog *workout_dialog, const char *text); + +void workout_dialog_set_subtext(WorkoutDialog *workout_dialog, const char *text); + +void workout_dialog_set_action_bar_hidden(WorkoutDialog *workout_dialog, bool should_hide); diff --git a/src/fw/apps/system/workout/metrics.h b/src/fw/apps/system/workout/metrics.h new file mode 100644 index 0000000000..1b1980cffd --- /dev/null +++ b/src/fw/apps/system/workout/metrics.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef enum { + WorkoutMetricType_None = 0, + WorkoutMetricType_Hr, + WorkoutMetricType_Duration, + WorkoutMetricType_Distance, + WorkoutMetricType_Steps, + WorkoutMetricType_Pace, + WorkoutMetricType_AvgPace, + WorkoutMetricType_Speed, + WorkoutMetricType_Custom, + WorkoutMetricTypeCount, +} WorkoutMetricType; diff --git a/src/fw/apps/system/workout/selection.c b/src/fw/apps/system/workout/selection.c new file mode 100644 index 0000000000..26978398a7 --- /dev/null +++ b/src/fw/apps/system/workout/selection.c @@ -0,0 +1,192 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "selection.h" +#include "utils.h" + +#include "applib/app.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "shell/prefs.h" +#include "system/logging.h" + +#include + +typedef enum { + WorkoutType_Run, + WorkoutType_Walk, + WorkoutType_OpenWorkout, + WorkoutTypeCount, +} WorkoutType; + +typedef struct WorkoutSelectionWindow { + Window window; + MenuLayer menu_layer; + GBitmap workout_icons[WorkoutTypeCount]; + SelectWorkoutCallback select_workout_cb; +} WorkoutSelectionWindow; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Helpers + +static uint32_t prv_get_icon_resource_id(WorkoutType workout_type) { + switch (workout_type) { + case WorkoutType_Run: + return RESOURCE_ID_WORKOUT_APP_RUN_SMALL; + case WorkoutType_Walk: + return RESOURCE_ID_WORKOUT_APP_WALK_SMALL; + case WorkoutType_OpenWorkout: + return RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL; + default: + return 0; + } +} + +static ActivitySessionType prv_get_activity_type(WorkoutType workout_type) { + switch (workout_type) { + case WorkoutType_Run: + return ActivitySessionType_Run; + case WorkoutType_Walk: + return ActivitySessionType_Walk; + case WorkoutType_OpenWorkout: + return ActivitySessionType_Open; + default: + return ActivitySessionType_Invalid; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Menu Layer Callbacks + +static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, + void *context) { + return WorkoutTypeCount; +} + +static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, MenuIndex *cell_index, + void *context) { +#if PBL_RECT + return 56; +#else + return menu_layer_is_index_selected(menu_layer, cell_index) ? 84 : 38; +#endif +} + +static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, + void *context) { + WorkoutSelectionWindow *selection_window = (WorkoutSelectionWindow *)context; + + ActivitySessionType activity_type = prv_get_activity_type(cell_index->row); + + const char *title = workout_utils_get_name_for_activity(activity_type); + const GBitmap *icon = &selection_window->workout_icons[cell_index->row]; + + const int icon_top_padding = 11; + const int title_top_padding = PBL_IF_RECT_ELSE(11, cell_layer->is_highlighted ? 40 : 2); + const int max_icon_w = PBL_IF_RECT_ELSE(55, cell_layer->bounds.size.w); + const int title_origin_x = PBL_IF_RECT_ELSE(max_icon_w, 0); + const GTextAlignment title_alignment = PBL_IF_RECT_ELSE(GTextAlignmentLeft, GTextAlignmentCenter); + const GFont title_font = PBL_IF_RECT_ELSE(fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), + cell_layer->is_highlighted ? fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD) + : fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + const int title_height = fonts_get_font_height(title_font); + + GRect image_bounds = gbitmap_get_bounds(icon); + image_bounds.origin.x = (max_icon_w / 2) - (image_bounds.size.w / 2); + image_bounds.origin.y = icon_top_padding; + +#if PBL_COLOR + GCompOp compositing_mode = GCompOpSet; +#else + GCompOp compositing_mode = cell_layer->is_highlighted ? GCompOpTintLuminance : GCompOpSet; + graphics_context_set_tint_color(ctx, (cell_layer->is_highlighted ? GColorWhite : GColorBlack)); +#endif + + graphics_context_set_compositing_mode(ctx, compositing_mode); + +#if PBL_ROUND + if (cell_layer->is_highlighted) { + graphics_draw_bitmap_in_rect(ctx, icon, &image_bounds); + } +#else + graphics_draw_bitmap_in_rect(ctx, icon, &image_bounds); +#endif + + GRect title_bounds = cell_layer->bounds; + title_bounds.origin.x = title_origin_x; + title_bounds.origin.y = title_top_padding; + title_bounds.size.w -= title_origin_x; + title_bounds.size.h = title_height; + + graphics_draw_text(ctx, i18n_get(title, selection_window), title_font, title_bounds, + GTextOverflowModeFill, title_alignment, NULL); +} + +static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { + WorkoutSelectionWindow *selection_window = (WorkoutSelectionWindow *)context; + + ActivitySessionType activity_type = prv_get_activity_type(cell_index->row); + selection_window->select_workout_cb(activity_type); + window_stack_remove(&selection_window->window, true); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Handlers + +static void prv_window_unload_handler(Window *window) { + WorkoutSelectionWindow *selection_window = window_get_user_data(window); + if (selection_window) { + for (int i = 0; i < WorkoutTypeCount; i++) { + gbitmap_deinit(&selection_window->workout_icons[i]); + } + menu_layer_deinit(&selection_window->menu_layer); + window_deinit(&selection_window->window); + i18n_free_all(selection_window); + app_free(selection_window); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Public API + +WorkoutSelectionWindow *workout_selection_push(SelectWorkoutCallback select_workout_cb) { + WorkoutSelectionWindow *selection_window = app_zalloc_check(sizeof(WorkoutSelectionWindow)); + + selection_window->select_workout_cb = select_workout_cb; + + Window *window = &selection_window->window; + window_init(window, WINDOW_NAME("Workout Selection")); + window_set_user_data(window, selection_window); + window_set_window_handlers(window, &(WindowHandlers){ + .unload = prv_window_unload_handler, + }); + + for (int i = 0; i < WorkoutTypeCount; i++) { + gbitmap_init_with_resource(&selection_window->workout_icons[i], prv_get_icon_resource_id(i)); + } + + MenuLayer *menu_layer = &selection_window->menu_layer; + menu_layer_init(menu_layer, &window->layer.bounds); + menu_layer_pad_bottom_enable(menu_layer, false); + menu_layer_set_callbacks(menu_layer, selection_window, &(MenuLayerCallbacks){ + .get_num_rows = prv_get_num_rows_callback, + .get_cell_height = prv_get_cell_height_callback, + .draw_row = prv_draw_row_callback, + .select_click = prv_select_callback, + }); + menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack); + menu_layer_set_highlight_colors(menu_layer, + PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack), + PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); + menu_layer_set_click_config_onto_window(menu_layer, &selection_window->window); + menu_layer_set_scroll_wrap_around(menu_layer, shell_prefs_get_menu_scroll_wrap_around_enable()); + menu_layer_set_scroll_vibe_on_wrap(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnWrapAround); + menu_layer_set_scroll_vibe_on_blocked(menu_layer, shell_prefs_get_menu_scroll_vibe_behavior() == MenuScrollVibeOnLocked); + layer_add_child(&selection_window->window.layer, menu_layer_get_layer(menu_layer)); + + app_window_stack_push(&selection_window->window, true); + + return selection_window; +} diff --git a/src/fw/apps/system/workout/selection.h b/src/fw/apps/system/workout/selection.h new file mode 100644 index 0000000000..a0fd7736c7 --- /dev/null +++ b/src/fw/apps/system/workout/selection.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/activity/activity.h" + +typedef void (*SelectWorkoutCallback)(ActivitySessionType type); + +typedef struct WorkoutSelectionWindow WorkoutSelectionWindow; + +WorkoutSelectionWindow *workout_selection_push(SelectWorkoutCallback select_workout_cb); diff --git a/src/fw/apps/system_apps/workout/sports.c b/src/fw/apps/system/workout/sports.c similarity index 92% rename from src/fw/apps/system_apps/workout/sports.c rename to src/fw/apps/system/workout/sports.c index 469d0ade17..fdda2b7551 100644 --- a/src/fw/apps/system_apps/workout/sports.c +++ b/src/fw/apps/system/workout/sports.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app.h" #include "applib/app_comm.h" @@ -20,15 +7,15 @@ #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/activity_private.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" #include "util/size.h" -#include "workout_active.h" -#include "workout_controller.h" -#include "workout_metrics.h" +#include "active.h" +#include "controller.h" +#include "metrics.h" #include #include @@ -89,7 +76,7 @@ static void prv_health_service_event_handler(HealthEventType event, void *contex static void prv_sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) { - PBL_LOG(LOG_LEVEL_DEBUG, "Sports error! dict: %u, app msg: %u", dict_error, + PBL_LOG_DBG("Sports error! dict: %u, app msg: %u", dict_error, app_message_error); } diff --git a/src/fw/apps/system/workout/sports.h b/src/fw/apps/system/workout/sports.h new file mode 100644 index 0000000000..864f915557 --- /dev/null +++ b/src/fw/apps/system/workout/sports.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd *sports_app_get_info(void); diff --git a/src/fw/apps/system/workout/summary.c b/src/fw/apps/system/workout/summary.c new file mode 100644 index 0000000000..4d261c0601 --- /dev/null +++ b/src/fw/apps/system/workout/summary.c @@ -0,0 +1,196 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "summary.h" +#include "countdown.h" +#include "selection.h" +#include "utils.h" + +#include "applib/app.h" +#include "applib/ui/action_menu_hierarchy.h" +#include "applib/ui/action_menu_window.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/health_util.h" +#include "pbl/services/activity/workout_service.h" +#include "system/logging.h" + +#include + +#define BACKGROUND_COLOR PBL_IF_COLOR_ELSE(GColorYellow, GColorWhite) +#define TEXT_COLOR (gcolor_legible_over(BACKGROUND_COLOR)) + +typedef struct WorkoutSummaryWindow { + Window window; + ActionBarLayer action_bar; + StatusBarLayer status_layer; + Layer base_layer; + + GBitmap *action_bar_start; + GBitmap *action_bar_more; + + ActivitySessionType activity_type; + + KinoReel *icon; + const char *name; + + StartWorkoutCallback start_workout_cb; + SelectWorkoutCallback select_workout_cb; +} WorkoutSummaryWindow; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Helpers + +static KinoReel* prv_get_icon_for_activity(ActivitySessionType type) { + switch (type) { + case ActivitySessionType_Open: + return kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_WORKOUT); + case ActivitySessionType_Walk: + return kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_WALK); + case ActivitySessionType_Run: + default: + return kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_RUN); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Drawing + +static void prv_render_activity_type(GContext *ctx, Layer *layer, KinoReel *icon, + const char *name) { + const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); + const GTextOverflowMode overflow_mode = GTextOverflowModeWordWrap; + const GTextAlignment alignment = GTextAlignmentCenter; + const int16_t rl_margin = PBL_IF_RECT_ELSE(4, 16); + + GRect drawing_rect = grect_inset(layer->bounds, GEdgeInsets(0, rl_margin)); + + const GSize icon_size = kino_reel_get_size(icon); + const int icon_x = drawing_rect.origin.x + PBL_IF_RECT_ELSE(0, (rl_margin / 2)) + + (drawing_rect.size.w / 2) - (icon_size.w / 2); + const int icon_y = PBL_IF_RECT_ELSE(45, 49); + kino_reel_draw(icon, ctx, GPoint(icon_x, icon_y)); + + const int name_x = drawing_rect.origin.x + PBL_IF_RECT_ELSE(0, (rl_margin / 2)); + const int name_y = PBL_IF_RECT_ELSE(107, 109); + GRect name_rect = GRect(name_x, name_y, drawing_rect.size.w, 32); + + graphics_context_set_text_color(ctx, TEXT_COLOR); + graphics_draw_text(ctx, name, font, name_rect, overflow_mode, alignment, NULL); +} + +static void prv_base_layer_update_proc(struct Layer *layer, GContext *ctx) { + WorkoutSummaryWindow *summary_window = window_get_user_data(layer_get_window(layer)); + + prv_render_activity_type(ctx, layer, summary_window->icon, summary_window->name); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Handlers + +static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutSummaryWindow *summary_window = context; + + workout_countdown_start(summary_window->activity_type, summary_window->start_workout_cb); + + window_stack_remove(&summary_window->window, false); +} + +static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) { + WorkoutSummaryWindow *summary_window = context; + + workout_selection_push(summary_window->select_workout_cb); +} + +static void prv_click_config_provider(void *context) { + window_set_click_context(BUTTON_ID_UP, context); + window_set_click_context(BUTTON_ID_DOWN, context); + window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); +} + +static void prv_window_unload_handler(Window *window) { + WorkoutSummaryWindow *summary_window = window_get_user_data(window); + if (summary_window) { + kino_reel_destroy(summary_window->icon); + gbitmap_destroy(summary_window->action_bar_more); + gbitmap_destroy(summary_window->action_bar_start); + action_bar_layer_deinit(&summary_window->action_bar); + status_bar_layer_deinit(&summary_window->status_layer); + layer_deinit(&summary_window->base_layer); + window_deinit(&summary_window->window); + i18n_free_all(summary_window); + app_free(summary_window); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Public API + +WorkoutSummaryWindow *workout_summary_window_create(ActivitySessionType activity_type, + StartWorkoutCallback start_workout_cb, + SelectWorkoutCallback select_workout_cb) { + WorkoutSummaryWindow *summary_window = app_zalloc_check(sizeof(WorkoutSummaryWindow)); + + summary_window->start_workout_cb = start_workout_cb; + summary_window->select_workout_cb = select_workout_cb; + + Window *window = &summary_window->window; + window_init(window, WINDOW_NAME("Workout Summary")); + window_set_user_data(window, summary_window); + window_set_background_color(window, BACKGROUND_COLOR); + window_set_window_handlers(window, &(WindowHandlers){ + .unload = prv_window_unload_handler, + }); + + GRect layer_bounds = window->layer.bounds; + layer_bounds.size.w -= ACTION_BAR_WIDTH; + + layer_init(&summary_window->base_layer, &layer_bounds); + layer_set_update_proc(&summary_window->base_layer, prv_base_layer_update_proc); + layer_add_child(&window->layer, &summary_window->base_layer); + + StatusBarLayer *status_layer = &summary_window->status_layer; + status_bar_layer_init(status_layer); + status_bar_layer_set_colors(status_layer, GColorClear, TEXT_COLOR); + layer_add_child(&window->layer, status_bar_layer_get_layer(status_layer)); + +#if PBL_RECT + GRect status_layer_bounds = window->layer.bounds; + status_layer_bounds.size.w -= ACTION_BAR_WIDTH; + layer_set_frame(&status_layer->layer, &status_layer_bounds); +#endif + + ActionBarLayer *action_bar = &summary_window->action_bar; + action_bar_layer_init(action_bar); + action_bar_layer_set_context(action_bar, summary_window); + action_bar_layer_set_click_config_provider(action_bar, prv_click_config_provider); + action_bar_layer_add_to_window(action_bar, window); + + summary_window->action_bar_start = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_START); + summary_window->action_bar_more = + gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_MORE); + + action_bar_layer_set_icon(action_bar, BUTTON_ID_UP, summary_window->action_bar_start); + action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, summary_window->action_bar_more); + + workout_summary_update_activity_type(summary_window, activity_type); + + return summary_window; +} + +void workout_summary_window_push(WorkoutSummaryWindow *summary_window) { + app_window_stack_push(&summary_window->window, true); +} + +void workout_summary_update_activity_type(WorkoutSummaryWindow *summary_window, + ActivitySessionType activity_type) { + summary_window->activity_type = activity_type; + summary_window->icon = prv_get_icon_for_activity(activity_type); + summary_window->name = workout_utils_get_name_for_activity(activity_type); +} diff --git a/src/fw/apps/system/workout/summary.h b/src/fw/apps/system/workout/summary.h new file mode 100644 index 0000000000..1e13b3378f --- /dev/null +++ b/src/fw/apps/system/workout/summary.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "selection.h" + +#include "pbl/services/activity/activity.h" + +typedef void (*StartWorkoutCallback)(ActivitySessionType type); + +typedef struct WorkoutSummaryWindow WorkoutSummaryWindow; + +WorkoutSummaryWindow *workout_summary_window_create(ActivitySessionType activity_type, + StartWorkoutCallback start_workout_cb, + SelectWorkoutCallback select_workout_cb); + +void workout_summary_window_push(WorkoutSummaryWindow *window); + +void workout_summary_update_activity_type(WorkoutSummaryWindow *summary_window, + ActivitySessionType activity_type); diff --git a/src/fw/apps/system/workout/utils.c b/src/fw/apps/system/workout/utils.c new file mode 100644 index 0000000000..67c0d68f7a --- /dev/null +++ b/src/fw/apps/system/workout/utils.c @@ -0,0 +1,157 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "utils.h" +#include "workout.h" + +#include "kernel/pbl_malloc.h" +#include "resource/timeline_resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/workout_service.h" +#include "pbl/services/timeline/timeline.h" +#include "system/passert.h" +#include "util/attributes.h" + +static TimelineItem *prv_create_abandoned_workout_notification(void) { + const char *msg = i18n_noop("Still sweating? Your workout is active and will be ended soon. " + "Open the workout to keep it going."); + + AttributeList notif_attr_list = {0}; + attribute_list_add_uint32(¬if_attr_list, AttributeIdIconTiny, TIMELINE_RESOURCE_ACTIVITY); + attribute_list_add_cstring(¬if_attr_list, AttributeIdBody, i18n_get(msg, ¬if_attr_list)); + attribute_list_add_uint8(¬if_attr_list, AttributeIdBgColor, + PBL_IF_COLOR_ELSE(GColorYellowARGB8, GColorDarkGrayARGB8)); + + AttributeList dismiss_attr_list = {0}; + attribute_list_add_cstring(&dismiss_attr_list, AttributeIdTitle, + i18n_get("Dismiss", ¬if_attr_list)); + + AttributeList end_workout_attr_list = {0}; + attribute_list_add_cstring(&end_workout_attr_list, AttributeIdTitle, + i18n_get("End Workout", ¬if_attr_list)); + attribute_list_add_uint32(&end_workout_attr_list, AttributeIdLaunchCode, + WorkoutLaunchArg_EndWorkout); + + AttributeList open_workout_attr_list = {0}; + attribute_list_add_cstring(&open_workout_attr_list, AttributeIdTitle, + i18n_get("Open Workout", ¬if_attr_list)); + + const int num_actions = 3; + TimelineItemActionGroup action_group = { + .num_actions = num_actions, + .actions = (TimelineItemAction[]) { + { + .id = 0, + .type = TimelineItemActionTypeDismiss, + .attr_list = dismiss_attr_list, + }, + { + .id = 1, + .type = TimelineItemActionTypeOpenWatchApp, + .attr_list = end_workout_attr_list, + }, + { + .id = 2, + .type = TimelineItemActionTypeOpenWatchApp, + .attr_list = open_workout_attr_list, + }, + }, + }; + + const time_t now_utc = rtc_get_time(); + + // Note: it's fine if this returns null, since the parent functions will check for a null pointer + TimelineItem *item = timeline_item_create_with_attributes(now_utc, 0, + TimelineItemTypeNotification, + LayoutIdNotification, ¬if_attr_list, + &action_group); + + i18n_free_all(¬if_attr_list); + attribute_list_destroy_list(¬if_attr_list); + attribute_list_destroy_list(&end_workout_attr_list); + attribute_list_destroy_list(&open_workout_attr_list); + + return item; +} + +void workout_utils_send_abandoned_workout_notification(void) { + TimelineItem *item = prv_create_abandoned_workout_notification(); + if (item) { + item->header.from_watch = true; + item->header.parent_id = (Uuid)UUID_WORKOUT_DATA_SOURCE; + notifications_add_notification(item); + timeline_item_destroy(item); + } +} + +const char* workout_utils_get_name_for_activity(ActivitySessionType type) { + switch (type) { + case ActivitySessionType_Open: + /// Workout Label + return i18n_noop("Workout"); + case ActivitySessionType_Walk: + /// Walk Label + return i18n_noop("Walk"); + case ActivitySessionType_Run: + /// Run Label + return i18n_noop("Run"); + case ActivitySessionType_Sleep: + case ActivitySessionType_RestfulSleep: + case ActivitySessionType_Nap: + case ActivitySessionType_RestfulNap: + case ActivitySessionType_None: + case ActivitySessionTypeCount: + // ActivitySessionType_Invalid should have the same value + break; + } + + WTF; +} + +const char* workout_utils_get_detection_text_for_activity(ActivitySessionType type) { + switch (type) { + case ActivitySessionType_Open: + /// Workout automatically detected dialog text + return i18n_noop("Workout\nDetected"); + case ActivitySessionType_Walk: + /// Walk automatically detected dialog text + return i18n_noop("Walk\nDetected"); + case ActivitySessionType_Run: + /// Run automatically detected dialog text + return i18n_noop("Run\nDetected"); + case ActivitySessionType_Sleep: + case ActivitySessionType_RestfulSleep: + case ActivitySessionType_Nap: + case ActivitySessionType_RestfulNap: + case ActivitySessionType_None: + case ActivitySessionTypeCount: + // ActivitySessionType_Invalid should have the same value + break; + } + + WTF; +} + +bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out) { + bool found_session = false; + + uint32_t num_sessions = ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT; + ActivitySession *sessions = app_zalloc_check(sizeof(ActivitySession) * + ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT); + activity_get_sessions(&num_sessions, sessions); + + for (int i = num_sessions; i >= 0; i--) { + if (workout_service_is_workout_type_supported(sessions[i].type) && sessions[i].ongoing) { + if (session_out) { + memcpy(session_out, &sessions[i], sizeof(ActivitySession)); + } + found_session = true; + break; + } + } + + app_free(sessions); + + return found_session; +} diff --git a/src/fw/apps/system/workout/utils.h b/src/fw/apps/system/workout/utils.h new file mode 100644 index 0000000000..ee6cead6ed --- /dev/null +++ b/src/fw/apps/system/workout/utils.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/activity/activity.h" + +void workout_utils_send_abandoned_workout_notification(void); + +const char* workout_utils_get_name_for_activity(ActivitySessionType type); + +const char* workout_utils_get_detection_text_for_activity(ActivitySessionType type); + +bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out); diff --git a/src/fw/apps/system_apps/workout/workout.c b/src/fw/apps/system/workout/workout.c similarity index 89% rename from src/fw/apps/system_apps/workout/workout.c rename to src/fw/apps/system/workout/workout.c index 4ab4977e96..50dfa0baad 100644 --- a/src/fw/apps/system_apps/workout/workout.c +++ b/src/fw/apps/system/workout/workout.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "workout.h" -#include "workout_active.h" -#include "workout_controller.h" -#include "workout_data.h" -#include "workout_dialog.h" -#include "workout_summary.h" -#include "workout_utils.h" +#include "active.h" +#include "controller.h" +#include "data.h" +#include "dialog.h" +#include "summary.h" +#include "utils.h" #include "applib/app.h" #include "applib/ui/dialogs/expandable_dialog.h" @@ -30,11 +17,11 @@ #include "popups/health_tracking_ui.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/health_util.h" -#include "services/normal/activity/workout_service.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/health_util.h" +#include "pbl/services/activity/workout_service.h" #include "system/logging.h" #include "resource/resource_ids.auto.h" #include "popups/health_tracking_ui.h" @@ -272,9 +259,7 @@ const PebbleProcessMd *workout_app_get_info(void) { 0x88, 0xde, 0x35, 0xa3, 0xfc, 0x18, 0xd4, 0x3f}, }, .name = i18n_noop("Workout"), -#if CAPABILITY_HAS_APP_GLANCES .icon_resource_id = RESOURCE_ID_ACTIVITY_TINY, -#endif }; return (const PebbleProcessMd*) &s_workout_app_info; } diff --git a/src/fw/apps/system/workout/workout.h b/src/fw/apps/system/workout/workout.h new file mode 100644 index 0000000000..ec5943c2c6 --- /dev/null +++ b/src/fw/apps/system/workout/workout.h @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +typedef enum { + WorkoutLaunchArg_EndWorkout = 1, +} WorkoutLaunchArg; + +void workout_push_summary_window(void); + +//! Call for system to obtain information about the application +//! @return System information about the app +const PebbleProcessMd *workout_app_get_info(void); diff --git a/src/fw/apps/system_app_ids.h b/src/fw/apps/system_app_ids.h index 011a76bc13..45cb49236d 100644 --- a/src/fw/apps/system_app_ids.h +++ b/src/fw/apps/system_app_ids.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/system_app_ids.auto.h" diff --git a/src/fw/apps/system_app_registry.h b/src/fw/apps/system_app_registry.h index e8ed381280..9aab709026 100644 --- a/src/fw/apps/system_app_registry.h +++ b/src/fw/apps/system_app_registry.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/system_app_registry_list.auto.h" diff --git a/src/fw/apps/system_apps/activity_demo_app.c b/src/fw/apps/system_apps/activity_demo_app.c deleted file mode 100644 index 9fea22394c..0000000000 --- a/src/fw/apps/system_apps/activity_demo_app.c +++ /dev/null @@ -1,806 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/health_service.h" -#include "applib/app.h" -#include "applib/app_logging.h" -#include "applib/fonts/fonts.h" -#include "applib/persist.h" -#include "applib/ui/ui.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "apps/system_app_ids.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "shell/prefs.h" -#include "system/logging.h" -#include "util/size.h" -#include "util/string.h" -#include "util/trig.h" - -#include "activity_demo_app.h" - -#include - -#define CURRENT_STEP_AVG 500 -#define DAILY_STEP_AVG 1000 - -// Persist keys -typedef enum { - AppPersistKeyLapSteps = 0, -} AppPersistKey; - - - -// ------------------------------------------------------------------------------- -// Structures - -typedef struct { - char dialog_text[256]; - SimpleMenuItem *menu_items; - SimpleMenuLayer *menu_layer; -} DebugCard; - -// App globals -typedef struct { - Window *debug_window; - DebugCard debug_card; - uint32_t steps_offset; - uint32_t cur_steps; -} ActivityDemoAppData; - -static ActivityDemoAppData *s_data; - -// ------------------------------------------------------------------------------- -static void prv_convert_seconds_to_time(uint32_t secs_after_midnight, char *text, - int text_len) { - uint32_t minutes_after_midnight = secs_after_midnight / SECONDS_PER_MINUTE; - uint32_t hour = minutes_after_midnight / MINUTES_PER_HOUR; - uint32_t minute = minutes_after_midnight % MINUTES_PER_HOUR; -#pragma GCC diagnostic ignored "-Wformat-truncation" - snprintf(text, text_len, "%d:%02d", (int)hour, (int)minute); -} - -// ----------------------------------------------------------------------------------------- -static void prv_display_alert(const char *text) { - ExpandableDialog *expandable_dialog = expandable_dialog_create("Alert"); - - dialog_set_text(expandable_dialog_get_dialog(expandable_dialog), text); - expandable_dialog_show_action_bar(expandable_dialog, false); - app_expandable_dialog_push(expandable_dialog); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_display_scalar_history_alert(ActivityDemoAppData *data, const char *title, - ActivityMetric metric) { - strcpy(data->debug_card.dialog_text, title); - - // Get History - int32_t values[7]; - activity_get_metric(metric, ARRAY_LENGTH(values), values); - for (int i = 0; i < 7; i++) { - char temp[32]; - snprintf(temp, sizeof(temp), "\n%d: %d", i, (int)values[i]); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - } - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_display_averages_alert(ActivityDemoAppData *data, DayInWeek day) { - ActivityMetricAverages *averages = app_malloc_check(sizeof(ActivityMetricAverages)); - strcpy(data->debug_card.dialog_text, "Hourly avgs:"); - activity_get_step_averages(day, averages); - - // Sum into hours - const int k_avgs_per_hour = ACTIVITY_NUM_METRIC_AVERAGES / HOURS_PER_DAY; - for (int i = 0; i < HOURS_PER_DAY; i++) { - int value = 0; - for (int j = i * k_avgs_per_hour; j < (i + 1) * k_avgs_per_hour; j++) { - if (averages->average[j] == ACTIVITY_METRIC_AVERAGES_UNKNOWN) { - averages->average[j] = 0; - } - value += averages->average[j]; - } - char temp[32]; - snprintf(temp, sizeof(temp), "\n%02d: %d", i, value); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - } - prv_display_alert(data->debug_card.dialog_text); - app_free(averages); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_display_seconds_history_alert(ActivityDemoAppData *data, const char *title, - ActivityMetric metric) { - strcpy(data->debug_card.dialog_text, title); - - // Get History - int32_t values[7]; - activity_get_metric(metric, ARRAY_LENGTH(values), values); - for (int i = 0; i < 7; i++) { - char elapsed[8]; - prv_convert_seconds_to_time(values[i], elapsed, sizeof(elapsed)); - char temp[32]; - snprintf(temp, sizeof(temp), "\n%d: %s", i, elapsed); - strcat(data->debug_card.dialog_text, temp); - } - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_set_steps(int32_t steps, ActivityDemoAppData *data) { - activity_test_set_steps_and_avg(steps, CURRENT_STEP_AVG, DAILY_STEP_AVG); - - int32_t peek_steps = health_service_sum_today(HealthMetricStepCount); - data->cur_steps = peek_steps + data->steps_offset; - - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Current steps changed to: %"PRIu32"\n", data->cur_steps); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_tracking(int index, void *context) { - ActivityDemoAppData *data = context; - - bool enabled = activity_tracking_on(); - enabled = !enabled; - - if (enabled) { - activity_start_tracking(false /*test_mode*/); - } else { - activity_stop_tracking(); - } - activity_prefs_tracking_set_enabled(enabled); - - data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; - layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_activity_insights(int index, void *context) { - ActivityDemoAppData *data = context; - - bool enabled = activity_prefs_activity_insights_are_enabled(); - enabled = !enabled; - activity_prefs_activity_insights_set_enabled(enabled); - - data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; - layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_sleep_insights(int index, void *context) { - ActivityDemoAppData *data = context; - - bool enabled = activity_prefs_sleep_insights_are_enabled(); - enabled = !enabled; - activity_prefs_sleep_insights_set_enabled(enabled); - - data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; - layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_dls_sends(int index, void *context) { - ActivityDemoAppData *data = context; - - bool enabled = dls_get_send_enable(); - enabled = !enabled; - dls_set_send_enable_pp(enabled); - - data->debug_card.menu_items[index].subtitle = enabled ? "Enabled" : "Disabled"; - layer_mark_dirty(simple_menu_layer_get_layer(data->debug_card.menu_layer)); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_set_steps_below_avg(int index, void *context) { - ActivityDemoAppData *data = context; - prv_set_steps(CURRENT_STEP_AVG - 250, data); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_set_steps_at_avg(int index, void *context) { - ActivityDemoAppData *data = context; - prv_set_steps(CURRENT_STEP_AVG, data); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_set_steps_above_avg(int index, void *context) { - ActivityDemoAppData *data = context; - prv_set_steps(CURRENT_STEP_AVG + 250, data); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_set_steps_history(int index, void *context) { - ActivityDemoAppData *data = context; - activity_test_set_steps_history(); - - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Step history changed"); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_set_sleep_history(int index, void *context) { - ActivityDemoAppData *data = context; - activity_test_set_sleep_history(); - - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Sleep history changed"); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_sleep_file_info(int index, void *context) { - ActivityDemoAppData *data = context; - - // Get sleep file info - uint32_t num_records; - uint32_t data_bytes; - uint32_t minutes; - activity_test_minute_file_info(false /*compact_first*/, &num_records, &data_bytes, &minutes); - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Records: %d\nData bytes: %d\nMinutes: %d", (int)num_records, (int)data_bytes, - (int)minutes); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_sleep_file_compact(int index, void *context) { - ActivityDemoAppData *data = context; - - // Get sleep file info - uint32_t num_records; - uint32_t data_bytes; - uint32_t minutes; - activity_test_minute_file_info(true /*compact_first*/, &num_records, &data_bytes, &minutes); - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "After compaction\nRecords: %d\nData bytes: %d\nMinutes: %d", (int)num_records, - (int)data_bytes, (int)minutes); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_resting_calorie_history(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_scalar_history_alert(data, "Resting Calories", ActivityMetricRestingKCalories); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_active_calorie_history(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_scalar_history_alert(data, "Active Calories", ActivityMetricActiveKCalories); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_step_history(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_scalar_history_alert(data, "Steps", ActivityMetricStepCount); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_sleep_history(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_seconds_history_alert(data, "Sleep total", ActivityMetricSleepTotalSeconds); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_active_time_history(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_seconds_history_alert(data, "Active Time", ActivityMetricActiveSeconds); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_distance_history(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_scalar_history_alert(data, "Distance(m)", ActivityMetricDistanceMeters); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_sleep_sessions(int index, void *context) { - ActivityDemoAppData *data = context; - - // Allocate space for the sessions. Usually, there will only be about 4 or 5 sessions - // (1 container and a handful of restful periods). Allocating space for 32 (an arbitrary - // number) should be more than enough. - uint32_t num_sessions = 32; - ActivitySession *sessions = app_malloc(num_sessions * sizeof(ActivitySession)); - if (!sessions) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Not enough memory"); - return; - } - - // Get sessions - bool success = activity_get_sessions(&num_sessions, sessions); - if (!success) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Error getting sleep sessions"); - goto exit; - } - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Sleep sessions\n"); - - // Print info on each one - ActivitySession *session = sessions; - for (uint32_t i = 0; i < num_sessions; i++, session++) { - char *prefix = ""; - bool deep_sleep = false; - switch (session->type) { - case ActivitySessionType_Sleep: - prefix = "s"; - break; - case ActivitySessionType_Nap: - prefix = "n"; - break; - case ActivitySessionType_RestfulSleep: - case ActivitySessionType_RestfulNap: - prefix = "*"; - deep_sleep = true; - break; - default: - continue; - } - safe_strcat(data->debug_card.dialog_text, prefix, sizeof(data->debug_card.dialog_text)); - - // Write start time - struct tm local_tm; - char temp[32]; - localtime_r(&session->start_utc, &local_tm); - strftime(temp, sizeof(temp), "%H:%M", &local_tm); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - - // Write end time/elapsed - if (deep_sleep) { - snprintf(temp, sizeof(temp), " %dm\n", (int)(session->length_min)); - } else { - time_t end_time = session->start_utc + (session->length_min * SECONDS_PER_MINUTE); - localtime_r(&end_time, &local_tm); - strftime(temp, sizeof(temp), "-%H:%M\n", &local_tm); - } - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - } - -exit: - // Free session info memory - app_free(sessions); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_step_sessions(int index, void *context) { - ActivityDemoAppData *data = context; - - // Allocate space for the sessions. Usually, there will only be about 4 or 5 sessions - // (1 container and a handful of restful periods). Allocating space for 32 (an arbitrary - // number) should be more than enough. - uint32_t num_sessions = 32; - ActivitySession *sessions = app_malloc(num_sessions * sizeof(ActivitySession)); - if (!sessions) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Not enough memory"); - return; - } - - // Get sessions - bool success = activity_get_sessions(&num_sessions, sessions); - if (!success) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Error getting activity sessions"); - goto exit; - } - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Step activities\n"); - - // Print info on each one - ActivitySession *session = sessions; - for (uint32_t i = 0; i < num_sessions; i++, session++) { - char *prefix = ""; - switch (session->type) { - case ActivitySessionType_Sleep: - case ActivitySessionType_Nap: - case ActivitySessionType_RestfulSleep: - case ActivitySessionType_RestfulNap: - continue; - case ActivitySessionType_Walk: - prefix = "W"; - break; - case ActivitySessionType_Run: - prefix = "R"; - break; - default: - continue; - } - safe_strcat(data->debug_card.dialog_text, prefix, sizeof(data->debug_card.dialog_text)); - - // Write start time - struct tm local_tm; - char temp[64]; - localtime_r(&session->start_utc, &local_tm); - strftime(temp, sizeof(temp), "%H:%M", &local_tm); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - - // Write length - snprintf(temp, sizeof(temp), " %dm\n", (int)(session->length_min)); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - - // Write steps, calories, distance - snprintf(temp, sizeof(temp), " %"PRIu16", %"PRIu16"C, %"PRIu16"m\n", session->step_data.steps, - session->step_data.active_kcalories + session->step_data.resting_kcalories, - session->step_data.distance_meters); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - } - - exit: - // Free session info memory - app_free(sessions); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_weekday_averages(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_averages_alert(data, Monday); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_weekend_averages(int index, void *context) { - ActivityDemoAppData *data = context; - prv_display_averages_alert(data, Saturday); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_activity_prefs(int index, void *context) { - ActivityDemoAppData *data = context; - bool tracking_enabled = activity_prefs_tracking_is_enabled(); - bool activity_insights_enabled = activity_prefs_activity_insights_are_enabled(); - bool sleep_insights_enabled = activity_prefs_sleep_insights_are_enabled(); - ActivityGender gender = activity_prefs_get_gender(); - uint16_t height_mm = activity_prefs_get_height_mm(); - uint16_t weight_dag = activity_prefs_get_weight_dag(); - uint8_t age_years = activity_prefs_get_age_years(); - - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "activity tracking: %"PRIu8"\n" - "activity_insights: %"PRIu8"\n" - "sleep_insights: %"PRIu8"\n" - "gender: %"PRIu8"\n" - "height: %"PRIu16"\n" - "weight: %"PRIu16"\n" - "age: %"PRIu8"", tracking_enabled, activity_insights_enabled, sleep_insights_enabled, - gender, height_mm, weight_dag, age_years); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_minute_data(int index, void *context) { - ActivityDemoAppData *data = context; - bool success; - - const uint32_t k_size = 1000; - HealthMinuteData *minute_data = app_malloc(k_size * sizeof(HealthMinuteData)); - if (!minute_data) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Out of memory"); - prv_display_alert(data->debug_card.dialog_text); - goto exit; - } - - - // Start as far back as 30 days ago - time_t utc_start = rtc_get_time() - 30 * SECONDS_PER_DAY; - uint32_t num_records = 0; - while (true) { - uint32_t chunk = k_size; - time_t prior_start = utc_start; - success = activity_get_minute_history(minute_data, &chunk, &utc_start); - if (!success) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Failed"); - prv_display_alert(data->debug_card.dialog_text); - goto exit; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Got %d minutes with UTC of %d (delta of %d min)", (int)chunk, - (int)utc_start, (int)(utc_start - prior_start) / SECONDS_PER_MINUTE); - num_records += chunk; - utc_start += chunk * SECONDS_PER_MINUTE; - if (chunk == 0) { - break; - } - } - - // Print summary - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Retrieved %d minute data records", (int)num_records); - - - // Print detail on the last few minutes - const int k_print_batch_size = k_size; - PBL_LOG(LOG_LEVEL_DEBUG, "Fetching last %d minutes", k_print_batch_size); - utc_start = rtc_get_time() - (k_print_batch_size * SECONDS_PER_MINUTE); - time_t prior_start = utc_start; - uint32_t chunk = k_print_batch_size; - success = activity_get_minute_history(minute_data, &chunk, &utc_start); - if (!success) { - snprintf(data->debug_card.dialog_text, sizeof(data->debug_card.dialog_text), - "Failed"); - prv_display_alert(data->debug_card.dialog_text); - goto exit; - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Got last %d minutes with UTC of %d (delta of %d min)", (int)chunk, - (int)utc_start, (int)(utc_start - prior_start) / SECONDS_PER_MINUTE); - - const unsigned int k_num_last_minutes = 6; - if (chunk >= k_num_last_minutes) { - for (int i = (int)chunk - k_num_last_minutes; i < (int)chunk; i++) { - HealthMinuteData *m_data = &minute_data[i]; - char temp[32]; - snprintf(temp, sizeof(temp), "\n%"PRId8", 0x%"PRIx8", %"PRIu16", %"PRId8" ", - m_data->steps, m_data->orientation, m_data->vmc, m_data->light); - safe_strcat(data->debug_card.dialog_text, temp, sizeof(data->debug_card.dialog_text)); - } - } - - prv_display_alert(data->debug_card.dialog_text); - -exit: - app_free(minute_data); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_send_fake_logging_record(int index, void *context) { - activity_test_send_fake_dls_records(); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_push_summary_pins(int index, void *context) { - prv_debug_cmd_set_steps_at_avg(index, context); - - activity_insights_test_push_summary_pins(); - - ActivityDemoAppData *data = context; - strncpy(data->debug_card.dialog_text, "Summary pins pushed", - sizeof(data->debug_card.dialog_text)); - prv_display_alert(data->debug_card.dialog_text); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_push_rewards(int index, void *context) { - activity_insights_test_push_rewards(); -} - - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_push_walk_run(int index, void *context) { - activity_insights_test_push_walk_run_sessions(); -} - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_push_day_insights(int index, void *context) { - activity_insights_test_push_day_insights(); -} - -// ----------------------------------------------------------------------------------------- -static void prv_debug_cmd_push_nap_session(int index, void *context) { - activity_insights_test_push_nap_session(); -} - -// ----------------------------------------------------------------------------------------- -static void debug_window_load(Window *window) { - ActivityDemoAppData *data = window_get_user_data(window); - Layer *window_layer = window_get_root_layer(window); - const GRect *root_bounds = &window_layer->bounds; - - static SimpleMenuItem menu_items[] = { - { - .title = "Tracking", - .callback = prv_debug_cmd_tracking, - }, { - .title = "Activity Insights", - .callback = prv_debug_cmd_activity_insights, - }, { - .title = "Sleep Insights", - .callback = prv_debug_cmd_sleep_insights, - }, { - .title = "DLS sends", - .callback = prv_debug_cmd_dls_sends, - }, { - .title = "Step History", - .callback = prv_debug_cmd_step_history, - }, { - .title = "Distance(m) History", - .callback = prv_debug_cmd_distance_history, - }, { - .title = "Resting Calorie History", - .callback = prv_debug_cmd_resting_calorie_history, - }, { - .title = "Active Calorie History", - .callback = prv_debug_cmd_active_calorie_history, - }, { - .title = "Active Minutes History", - .callback = prv_debug_cmd_active_time_history, - }, { - .title = "Sleep History", - .callback = prv_debug_cmd_sleep_history, - }, { - .title = "Sleep Sessions", - .callback = prv_debug_cmd_sleep_sessions, - }, { - .title = "Step activities", - .callback = prv_debug_cmd_step_sessions, - }, { - .title = "Weekday averages", - .callback = prv_debug_cmd_weekday_averages, - }, { - .title = "Weekend averages", - .callback = prv_debug_cmd_weekend_averages, - }, { - .title = "Activity Prefs", - .callback = prv_debug_cmd_activity_prefs, - }, { - .title = "Steps below avg", - .callback = prv_debug_cmd_set_steps_below_avg, - }, { - .title = "Steps at avg", - .callback = prv_debug_cmd_set_steps_at_avg, - }, { - .title = "Steps above avg", - .callback = prv_debug_cmd_set_steps_above_avg, - }, { - .title = "Set step history", - .callback = prv_debug_cmd_set_steps_history, - }, { - .title = "Set sleep history", - .callback = prv_debug_cmd_set_sleep_history, - }, { - .title = "Sleep File Info", - .callback = prv_debug_cmd_sleep_file_info, - }, { - .title = "Sleep File Compact", - .callback = prv_debug_cmd_sleep_file_compact, - }, { - .title = "Read Minute data", - .callback = prv_debug_cmd_minute_data, - }, { - .title = "Send fake DL record", - .callback = prv_debug_cmd_send_fake_logging_record, - }, { - .title = "Push Summary Pins", - .callback = prv_debug_cmd_push_summary_pins, - }, { - .title = "Push Rewards", - .callback = prv_debug_cmd_push_rewards, - }, { - .title = "Walk/Run Notif", - .callback = prv_debug_cmd_push_walk_run, - }, { - .title = "Push Day Insights", - .callback = prv_debug_cmd_push_day_insights, - }, { - .title = "Push Nap Session", - .callback = prv_debug_cmd_push_nap_session, - } - }; - static const SimpleMenuSection sections[] = { - { - .items = menu_items, - .num_items = ARRAY_LENGTH(menu_items) - } - }; - - data->debug_card.menu_items = menu_items; - data->debug_card.menu_layer = simple_menu_layer_create(*root_bounds, window, sections, - ARRAY_LENGTH(sections), data); - layer_add_child(window_layer, simple_menu_layer_get_layer(data->debug_card.menu_layer)); - - // Init status - data->debug_card.menu_items[0].subtitle = activity_tracking_on() ? "Enabled" : "Disabled"; - data->debug_card.menu_items[1].subtitle = - activity_prefs_activity_insights_are_enabled() ? "Enabled" : "Disabled"; - data->debug_card.menu_items[2].subtitle = - activity_prefs_sleep_insights_are_enabled() ? "Enabled" : "Disabled"; - data->debug_card.menu_items[3].subtitle = dls_get_send_enable() ? "Enabled" : "Disabled"; -} - - -// ------------------------------------------------------------------------------- -static void debug_window_unload(Window *window) { - ActivityDemoAppData *data = window_get_user_data(window); - simple_menu_layer_destroy(data->debug_card.menu_layer); -} - - -// ------------------------------------------------------------------------------- -static void deinit(void) { - ActivityDemoAppData *data = app_state_get_user_data(); - window_destroy(data->debug_window); - app_free(data); -} - - -// ------------------------------------------------------------------------------- -static void init(void) { - ActivityDemoAppData *data = app_malloc_check(sizeof(ActivityDemoAppData)); - s_data = data; - memset(data, 0, sizeof(ActivityDemoAppData)); - app_state_set_user_data(data); - - // Debug window - data->debug_window = window_create(); - window_set_user_data(data->debug_window, data); - window_set_window_handlers(data->debug_window, &(WindowHandlers) { - .load = debug_window_load, - .unload = debug_window_unload, - }); - - app_window_stack_push(data->debug_window, true /* Animated */); -} - - -// ------------------------------------------------------------------------------- -static void s_main(void) { - init(); - app_event_loop(); - deinit(); -} - - -// ------------------------------------------------------------------------------- -const PebbleProcessMd* activity_demo_get_app_info(void) { - static const PebbleProcessMdSystem s_activity_demo_app_info = { - .common.main_func = &s_main, - // UUID: 60206d97-818b-4f42-87ae-48fde623608d - .common.uuid = {0x60, 0x20, 0x6d, 0x97, 0x81, 0x8b, 0x4f, 0x42, 0x87, 0xae, 0x48, 0xfd, 0xe6, - 0x23, 0x60, 0x8d}, - .name = "ActivityDemo" - }; - return (const PebbleProcessMd*) &s_activity_demo_app_info; -} diff --git a/src/fw/apps/system_apps/activity_demo_app.h b/src/fw/apps/system_apps/activity_demo_app.h deleted file mode 100644 index ddc3520d17..0000000000 --- a/src/fw/apps/system_apps/activity_demo_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* activity_demo_get_app_info(void); diff --git a/src/fw/apps/system_apps/alarms/alarm_detail.c b/src/fw/apps/system_apps/alarms/alarm_detail.c deleted file mode 100644 index 7383284f8a..0000000000 --- a/src/fw/apps/system_apps/alarms/alarm_detail.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "alarm_detail.h" -#include "alarm_editor.h" - -#include "applib/ui/action_menu_window.h" -#include "applib/ui/action_menu_window_private.h" -#include "applib/ui/dialogs/actionable_dialog.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "kernel/pbl_malloc.h" -#include "popups/health_tracking_ui.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" -#include "services/normal/alarms/alarm.h" -#include "system/logging.h" - -#include - -#define NUM_SNOOZE_MENU_ITEMS 5 - -typedef enum DetailMenuItemIndex { - DetailMenuItemIndexEnable = 0, - DetailMenuItemIndexDelete, - DetailMenuItemIndexChangeTime, - DetailMenuItemIndexChangeDays, -#if CAPABILITY_HAS_HEALTH_TRACKING - DetailMenuItemIndexConvertSmart, -#endif - DetailMenuItemIndexSnooze, - DetailMenuItemIndexNum, -} DetailMenuItemIndex; - -typedef struct AlarmDetailData { - ActionMenuConfig menu_config; - - AlarmId alarm_id; - AlarmInfo alarm_info; - - AlarmEditorCompleteCallback alarm_editor_callback; - void *callback_context; -} AlarmDetailData; - - -static SimpleDialog *prv_snooze_set_confirm_dialog(void) { - SimpleDialog *simple_dialog = simple_dialog_create("AlarmSnoozeSet"); - Dialog *dialog = simple_dialog_get_dialog(simple_dialog); - const char *snooze_text = i18n_noop("Snooze delay set to %d minutes"); - char snooze_buf[64]; - snprintf(snooze_buf, sizeof(snooze_buf), i18n_get(snooze_text, dialog), alarm_get_snooze_delay()); - i18n_free(snooze_text, dialog); - dialog_set_text(dialog, snooze_buf); - dialog_set_icon(dialog, RESOURCE_ID_GENERIC_CONFIRMATION_LARGE); - dialog_set_background_color(dialog, GColorJaegerGreen); - dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT); - return simple_dialog; -} - -static void prv_edit_snooze_delay(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - alarm_set_snooze_delay((uintptr_t)item->action_data); - SimpleDialog *snooze_delay_dialog = prv_snooze_set_confirm_dialog(); - action_menu_set_result_window(action_menu, (Window *)snooze_delay_dialog); -} - -static void prv_toggle_enable_alarm_handler(ActionMenu *action_menu, const ActionMenuItem *item, - void *context) { - AlarmDetailData *data = (AlarmDetailData *) context; - alarm_set_enabled(data->alarm_id, !data->alarm_info.enabled); - if (data->alarm_editor_callback) { - data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); - } -} - -static void prv_toggle_smart_alarm_handler(ActionMenu *action_menu, const ActionMenuItem *item, - void *context) { - AlarmDetailData *data = context; - -#if CAPABILITY_HAS_HEALTH_TRACKING - if (!data->alarm_info.is_smart && !activity_prefs_tracking_is_enabled()) { - // Notify about Health and keep the menu open - health_tracking_ui_feature_show_disabled(); - return; - } -#endif - - alarm_set_smart(data->alarm_id, !data->alarm_info.is_smart); - if (data->alarm_editor_callback) { - data->alarm_editor_callback(EDITED, data->alarm_id, data->callback_context); - } -} - -static void prv_edit_time_handler(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - AlarmDetailData *data = (AlarmDetailData *) context; - alarm_editor_update_alarm_time(data->alarm_id, - data->alarm_info.is_smart ? AlarmType_Smart : AlarmType_Basic, - data->alarm_editor_callback, data->callback_context); -} - -static void prv_edit_day_handler(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - AlarmDetailData *data = (AlarmDetailData *) context; - alarm_editor_update_alarm_days(data->alarm_id, data->alarm_editor_callback, - data->callback_context); -} - -static void prv_delete_alarm_handler(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - AlarmDetailData *data = (AlarmDetailData *) context; - alarm_delete(data->alarm_id); - if (data->alarm_editor_callback) { - data->alarm_editor_callback(DELETED, data->alarm_id, data->callback_context); - } -} - -static ActionMenuLevel *prv_create_main_menu(void) { - ActionMenuLevel *level = task_malloc(sizeof(ActionMenuLevel) + - DetailMenuItemIndexNum * sizeof(ActionMenuItem)); - if (!level) return NULL; - *level = (ActionMenuLevel) { - .num_items = DetailMenuItemIndexNum, - .parent_level = NULL, - .display_mode = ActionMenuLevelDisplayModeWide, - }; - return level; -} - -static ActionMenuLevel *prv_create_snooze_menu(ActionMenuLevel *parent_level) { - ActionMenuLevel *level = task_malloc(sizeof(ActionMenuLevel) + - NUM_SNOOZE_MENU_ITEMS * sizeof(ActionMenuItem)); - if (!level) return NULL; - *level = (ActionMenuLevel) { - .num_items = NUM_SNOOZE_MENU_ITEMS, - .parent_level = parent_level, - .display_mode = ActionMenuLevelDisplayModeWide, - }; - return level; -} - -void prv_cleanup_alarm_detail_menu(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); - AlarmDetailData *data = (AlarmDetailData *) context; - i18n_free_all(data); - task_free((void *)root_level->items[DetailMenuItemIndexSnooze].next_level); - task_free((void *)root_level); - task_free(data); - data = NULL; -} - -void alarm_detail_window_push(AlarmId alarm_id, AlarmInfo *alarm_info, - AlarmEditorCompleteCallback alarm_editor_callback, - void *callback_context) { - AlarmDetailData* data = task_malloc_check(sizeof(AlarmDetailData)); - *data = (AlarmDetailData) { - .alarm_id = alarm_id, - .alarm_info = *alarm_info, - .alarm_editor_callback = alarm_editor_callback, - .callback_context = callback_context, - .menu_config = { - .context = data, - .colors.background = ALARMS_APP_HIGHLIGHT_COLOR, - .did_close = prv_cleanup_alarm_detail_menu, - }, - }; - - // Setup main menu items - ActionMenuLevel *main_menu = prv_create_main_menu(); - - main_menu->items[DetailMenuItemIndexDelete] = (ActionMenuItem) { - .label = i18n_get("Delete", data), - .perform_action = prv_delete_alarm_handler, - .action_data = data, - }; - main_menu->items[DetailMenuItemIndexEnable] = (ActionMenuItem) { - .label = data->alarm_info.enabled ? i18n_get("Disable", data) : i18n_get("Enable", data), - .perform_action = prv_toggle_enable_alarm_handler, - .action_data = data, - }; - main_menu->items[DetailMenuItemIndexChangeTime] = (ActionMenuItem) { - .label = i18n_get("Change Time", data), - .perform_action = prv_edit_time_handler, - .action_data = data, - }; - main_menu->items[DetailMenuItemIndexChangeDays] = (ActionMenuItem) { - .label = i18n_get("Change Days", data), - .perform_action = prv_edit_day_handler, - .action_data = data, - }; -#if CAPABILITY_HAS_HEALTH_TRACKING - main_menu->items[DetailMenuItemIndexConvertSmart] = (ActionMenuItem) { - .label = data->alarm_info.is_smart ? i18n_get("Convert to Basic Alarm", data) : - i18n_get("Convert to Smart Alarm", data), - .perform_action = prv_toggle_smart_alarm_handler, - .action_data = data, - }; -#endif - main_menu->items[DetailMenuItemIndexSnooze] = (ActionMenuItem) { - .label = i18n_get("Snooze Delay", data), - .is_leaf = 0, - .next_level = prv_create_snooze_menu(main_menu), - }; - main_menu->separator_index = DetailMenuItemIndexSnooze; - data->menu_config.root_level = main_menu; - - // Setup snooze menu items - ActionMenuLevel *snooze_level = main_menu->items[DetailMenuItemIndexSnooze].next_level; - static const unsigned snooze_delays[NUM_SNOOZE_MENU_ITEMS] = {5, 10, 15, 30, 60}; - static const char *snooze_delay_strs[NUM_SNOOZE_MENU_ITEMS] = { - i18n_noop("5 minutes"), - i18n_noop("10 minutes"), - i18n_noop("15 minutes"), - i18n_noop("30 minutes"), - i18n_noop("1 hour") - }; - - unsigned current_snooze_delay = alarm_get_snooze_delay(); - for (int i = 0; i < NUM_SNOOZE_MENU_ITEMS; i++) { - snooze_level->items[i] = (ActionMenuItem) { - .label = i18n_get(snooze_delay_strs[i], data), - .perform_action = prv_edit_snooze_delay, - .action_data = (void *) (uintptr_t) snooze_delays[i], - }; - - if (current_snooze_delay == snooze_delays[i]) { - snooze_level->default_selected_item = i; - } - } - - app_action_menu_open(&data->menu_config); -} diff --git a/src/fw/apps/system_apps/alarms/alarm_detail.h b/src/fw/apps/system_apps/alarms/alarm_detail.h deleted file mode 100644 index 8d1c0be680..0000000000 --- a/src/fw/apps/system_apps/alarms/alarm_detail.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/alarms/alarm.h" -#include "alarm_editor.h" - -void alarm_detail_window_push(AlarmId alarm_id, AlarmInfo *alarm_info, - AlarmEditorCompleteCallback alarm_editor_callback, - void *callback_context); diff --git a/src/fw/apps/system_apps/alarms/alarm_editor.h b/src/fw/apps/system_apps/alarms/alarm_editor.h deleted file mode 100644 index b1e9f28c37..0000000000 --- a/src/fw/apps/system_apps/alarms/alarm_editor.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/alarms/alarm.h" -#include "applib/ui/window.h" - -typedef enum { - CREATED, - DELETED, - EDITED, - CANCELLED -} AlarmEditorResult; - -typedef void (*AlarmEditorCompleteCallback)(AlarmEditorResult result, AlarmId id, - void *callback_context); - -Window* alarm_editor_create_new_alarm(AlarmEditorCompleteCallback editor_complete_callback, - void *callback_context); - -void alarm_editor_update_alarm_time(AlarmId alarm_id, AlarmType alarm_type, - AlarmEditorCompleteCallback editor_complete_callback, - void *callback_context); - -void alarm_editor_update_alarm_days(AlarmId alarm_id, - AlarmEditorCompleteCallback editor_complete_callback, - void *callback_context); diff --git a/src/fw/apps/system_apps/alarms/alarms.h b/src/fw/apps/system_apps/alarms/alarms.h deleted file mode 100644 index b7453ec07b..0000000000 --- a/src/fw/apps/system_apps/alarms/alarms.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* alarms_app_get_info(); diff --git a/src/fw/apps/system_apps/app_fetch_ui.h b/src/fw/apps/system_apps/app_fetch_ui.h deleted file mode 100644 index d93fe1b2ea..0000000000 --- a/src/fw/apps/system_apps/app_fetch_ui.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/app_install_types.h" -#include "process_management/launch_config.h" -#include "process_management/pebble_process_md.h" -#include "services/common/compositor/compositor.h" -#include "services/normal/wakeup.h" - -#include - -typedef struct AppFetchUIArgs { - LaunchConfigCommon common; - WakeupInfo wakeup_info; - AppInstallId app_id; - bool forcefully; //! whether to launch forcefully or not -} AppFetchUIArgs; - -//! Used to launch the app_fetch_ui application -const PebbleProcessMd *app_fetch_ui_get_app_info(void); diff --git a/src/fw/apps/system_apps/battery_critical_app.c b/src/fw/apps/system_apps/battery_critical_app.c deleted file mode 100644 index 3987fe8443..0000000000 --- a/src/fw/apps/system_apps/battery_critical_app.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app.h" -#include "applib/graphics/graphics.h" -#include "applib/ui/window_private.h" -#include "applib/ui/app_window_stack.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "system/passert.h" - -typedef struct BatteryCriticalAppData { - Window window; - Layer layer; - GBitmap bitmap; -} BatteryCriticalAppData; - -static void update_proc(Layer* layer, GContext* ctx) { - BatteryCriticalAppData *app_data = app_state_get_user_data(); - - GRect low_battery_bounds = { - .origin = { - .x = (DISP_COLS - app_data->bitmap.bounds.size.w) / 2, - .y = (DISP_ROWS - app_data->bitmap.bounds.size.h), - }, - .size = app_data->bitmap.bounds.size, - }; - - graphics_draw_bitmap_in_rect(ctx, &app_data->bitmap, &low_battery_bounds); -} - -static void handle_init(void) { - BatteryCriticalAppData* data = app_malloc_check(sizeof(BatteryCriticalAppData)); - - gbitmap_init_with_resource(&data->bitmap, RESOURCE_ID_BATTERY_ICON_CHARGE); - - app_state_set_user_data(data); - - Window *window = &data->window; - - window_init(window, WINDOW_NAME("Battery Critical")); - window_set_overrides_back_button(window, true); - - layer_init(&data->layer, &window_get_root_layer(&data->window)->frame); - layer_set_update_proc(&data->layer, update_proc); - layer_add_child(window_get_root_layer(&data->window), &data->layer); - - const bool animated = false; - app_window_stack_push(window, animated); -} - -static void handle_deinit(void) { - BatteryCriticalAppData* app_data = app_state_get_user_data(); - gbitmap_deinit(&app_data->bitmap); - app_free(app_data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -const PebbleProcessMd* battery_critical_get_app_info() { - static const PebbleProcessMdSystem s_app_md = { - .common = { - .main_func = s_main, - .visibility = ProcessVisibilityHidden, - // UUID: 4a71eb65-238d-4faa-b2a0-112aa910d7b4 - .uuid = {0x4a, 0x71, 0xeb, 0x65, 0x23, 0x8d, 0x4f, 0xaa, 0xb2, 0xa0, 0x11, 0x2a, 0xa9, 0x10, 0xd7, 0xb4}, - }, - .name = "Battery Critical", - .run_level = ProcessAppRunLevelCritical, - }; - return (const PebbleProcessMd*) &s_app_md; -} - diff --git a/src/fw/apps/system_apps/battery_critical_app.h b/src/fw/apps/system_apps/battery_critical_app.h deleted file mode 100644 index 7c1b7e9437..0000000000 --- a/src/fw/apps/system_apps/battery_critical_app.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* battery_critical_get_app_info(); - diff --git a/src/fw/apps/system_apps/health/health.c b/src/fw/apps/system_apps/health/health.c deleted file mode 100644 index a8a2aa7ebf..0000000000 --- a/src/fw/apps/system_apps/health/health.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health.h" -#include "health_card_view.h" -#include "health_data.h" - -#include "applib/app.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/modals/modal_manager.h" -#include "popups/health_tracking_ui.h" -#include "process_state/app_state/app_state.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/timeline/timeline.h" -#include "resource/resource_ids.auto.h" -#include "system/logging.h" - -// Health app versions -// 0: Invalid (app was never opened) -// 1: Initial version -// 2: Graphs moved to mobile apps -// 3: 4.0 app redesign -#define CURRENT_HEALTH_APP_VERSION 3 - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Main Structures -// - -//! Main structure for application -typedef struct HealthAppData { - HealthCardView *health_card_view; - HealthData *health_data; -} HealthAppData; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Callbacks -// - -//! Tick timer service callback -//! @param tick_time Pointer to time structure -//! @param units_changed The time units changed -static void prv_tick_timer_handler(struct tm *tick_time, TimeUnits units_changed) { - HealthAppData *health_app_data = app_state_get_user_data(); - health_data_update_step_derived_metrics(health_app_data->health_data); - health_card_view_mark_dirty(health_app_data->health_card_view); -} - -// Activity change callback -static void prv_health_service_event_handler(HealthEventType event, void *context) { - HealthAppData *health_app_data = context; - if (event == HealthEventMovementUpdate) { - const uint32_t steps_today = health_service_sum_today(HealthMetricStepCount); - health_data_update_steps(health_app_data->health_data, steps_today); - } else if (event == HealthEventSleepUpdate) { - const uint32_t seconds_sleep_today = health_service_sum_today(HealthMetricSleepSeconds); - const uint32_t seconds_restful_sleep_today = - health_service_sum_today(HealthMetricSleepRestfulSeconds); - health_data_update_sleep(health_app_data->health_data, seconds_sleep_today, - seconds_restful_sleep_today); - } else if (event == HealthEventHeartRateUpdate) { - health_data_update_current_bpm(health_app_data->health_data); - } else { - health_data_update(health_app_data->health_data); - } - health_card_view_mark_dirty(health_app_data->health_card_view); -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Initialization and Termination -// - -//! Initialize application -static void prv_finish_initilization_cb(bool in_focus) { - if (in_focus) { - HealthAppData *health_app_data = app_state_get_user_data(); - - tick_timer_service_subscribe(MINUTE_UNIT, prv_tick_timer_handler); - - health_service_set_heart_rate_sample_period(1 /* interval_s */); - - // Subscribing to health events causes a `HealthEventSignificantUpdate` which - // will trigger us to update our health data - health_service_events_subscribe(prv_health_service_event_handler, health_app_data); - - // Unsubscribe, we only want to do this on the initial appearance (opening the app) - app_focus_service_unsubscribe(); - } -} - -static void prv_initialize(void) { - if (!activity_prefs_tracking_is_enabled()) { - /// Health disabled text - static const char *msg = i18n_noop("Track your steps, sleep, and more!" - " Enable Pebble Health in the mobile app."); - health_tracking_ui_show_message(RESOURCE_ID_HEART_TINY, msg, true); - return; - } - - if (!activity_is_initialized()) { - /// Health waiting for time sync - static const char *msg = i18n_noop("Health requires the time to be synced." - " Please connect your phone."); - health_tracking_ui_show_message(RESOURCE_ID_ALARM_CLOCK_TINY, msg, true); - return; - } - - activity_prefs_set_health_app_opened_version(CURRENT_HEALTH_APP_VERSION); - - HealthAppData *health_app_data = app_zalloc_check(sizeof(HealthAppData)); - - app_state_set_user_data(health_app_data); - - health_app_data->health_data = health_data_create(); - health_data_update_quick(health_app_data->health_data); - - health_app_data->health_card_view = health_card_view_create(health_app_data->health_data); - - health_card_view_push(health_app_data->health_card_view); - - // Finish up initializing the app a bit later. This helps reduce lag when opening the app - app_focus_service_subscribe_handlers((AppFocusHandlers){ - .did_focus = prv_finish_initilization_cb, - }); -} - -//! Terminate application -static void prv_terminate(void) { - HealthAppData *health_app_data = app_state_get_user_data(); - - // cancel explicit hr sample period - health_service_set_heart_rate_sample_period(0 /* interval_s */); - - if (health_app_data) { - health_card_view_destroy(health_app_data->health_card_view); - - health_data_destroy(health_app_data->health_data); - - app_free(health_app_data); - } -} - -//! Main entry point -static void prv_main(void) { - prv_initialize(); - app_event_loop(); - prv_terminate(); -} - -const PebbleProcessMd *health_app_get_info(void) { - static const PebbleProcessMdSystem s_health_app_info = { - .common = { - .main_func = &prv_main, - .uuid = UUID_HEALTH_DATA_SOURCE, -#if CAPABILITY_HAS_CORE_NAVIGATION4 - .visibility = ProcessVisibilityHidden, -#endif - }, - .name = i18n_noop("Health"), - }; - return (const PebbleProcessMd*) &s_health_app_info; -} diff --git a/src/fw/apps/system_apps/health/health.h b/src/fw/apps/system_apps/health/health.h deleted file mode 100644 index a570df4018..0000000000 --- a/src/fw/apps/system_apps/health/health.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -//! Call for system to obtain information about the application -//! @return System information about the app -const PebbleProcessMd *health_app_get_info(void); diff --git a/src/fw/apps/system_apps/health/health_activity_detail_card.c b/src/fw/apps/system_apps/health/health_activity_detail_card.c deleted file mode 100644 index c7dfe14e9a..0000000000 --- a/src/fw/apps/system_apps/health/health_activity_detail_card.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_activity_detail_card.h" -#include "health_detail_card.h" -#include "services/normal/activity/health_util.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" - -#include - -typedef struct HealthActivityDetailCard { - int32_t daily_avg; - int32_t weekly_max; - - int16_t num_headings; - HealthDetailHeading headings[MAX_NUM_HEADINGS]; - - int16_t num_subtitles; - HealthDetailSubtitle subtitles[MAX_NUM_SUBTITLES]; - - int16_t num_zones; - HealthDetailZone zones[MAX_NUM_ZONES]; -} HealthActivityDetailCardData; - -static void prv_set_calories(char *buffer, size_t buffer_size, int32_t current_calories) { - if (current_calories == 0) { - strncpy(buffer, EN_DASH, buffer_size); - return; - } - - snprintf(buffer, buffer_size, "%"PRId32, current_calories); -} - -static void prv_set_distance(char *buffer, size_t buffer_size, int32_t current_distance_meters) { - if (current_distance_meters == 0) { - strncpy(buffer, EN_DASH, buffer_size); - return; - } - - const int conversion_factor = health_util_get_distance_factor(); - const char *units_string = health_util_get_distance_string(i18n_noop("MI"), i18n_noop("KM")); - - char distance_buffer[HEALTH_WHOLE_AND_DECIMAL_LENGTH]; - health_util_format_whole_and_decimal(distance_buffer, HEALTH_WHOLE_AND_DECIMAL_LENGTH, - current_distance_meters, conversion_factor); - - snprintf(buffer, buffer_size, "%s%s", distance_buffer, units_string); -} - -static void prv_set_avg(char *buffer, size_t buffer_size, int32_t daily_avg, void *i18n_owner) { - int pos = 0; - - pos += snprintf(buffer, buffer_size, - PBL_IF_ROUND_ELSE("%s\n", "%s"), i18n_get("30 DAY AVG", i18n_owner)); - - if (daily_avg > 0) { - snprintf(buffer + pos, buffer_size - pos, " %"PRId32, daily_avg); - } else { - snprintf(buffer + pos, buffer_size - pos, " "EN_DASH); - } -} - -Window *health_activity_detail_card_create(HealthData *health_data) { - HealthActivityDetailCardData *card_data = app_zalloc_check(sizeof(HealthActivityDetailCardData)); - - card_data->daily_avg = health_data_steps_get_monthly_average(health_data); - - const GColor fill_color = PBL_IF_COLOR_ELSE(GColorIslamicGreen, GColorDarkGray); - const GColor today_fill_color = PBL_IF_COLOR_ELSE(GColorScreaminGreen, GColorDarkGray); - - health_detail_card_set_render_day_zones(card_data->zones, - &card_data->num_zones, - &card_data->weekly_max, - false /* format hours and minutes */, - true /* show crown */, - fill_color, - today_fill_color, - health_data_steps_get(health_data), - card_data); - - const size_t buffer_len = 32; - - HealthDetailHeading *heading = &card_data->headings[card_data->num_headings++]; - - *heading = (HealthDetailHeading) { - .primary_label = (char *)i18n_get("CALORIES", card_data), - .primary_value = app_zalloc_check(buffer_len), - .secondary_label = (char *)i18n_get("DISTANCE", card_data), - .secondary_value = app_zalloc_check(buffer_len), - .fill_color = GColorWhite, - .outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack), - }; - - prv_set_calories(heading->primary_value, buffer_len, - health_data_current_calories_get(health_data)); - - prv_set_distance(heading->secondary_value, buffer_len, - health_data_current_distance_meters_get(health_data)); - - HealthDetailSubtitle *subtitle = &card_data->subtitles[card_data->num_subtitles++]; - - *subtitle = (HealthDetailSubtitle) { - .label = app_zalloc_check(buffer_len), - .fill_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack), - }; - - prv_set_avg(subtitle->label, buffer_len, card_data->daily_avg, card_data); - - const HealthDetailCardConfig config = { - .num_headings = card_data->num_headings, - .headings = card_data->headings, - .num_subtitles = card_data->num_subtitles, - .subtitles = card_data->subtitles, - .daily_avg = card_data->daily_avg, - .weekly_max = card_data->weekly_max, - .bg_color = PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite), - .num_zones = card_data->num_zones, - .zones = card_data->zones, - .data = card_data, - }; - - return (Window *)health_detail_card_create(&config); -} - -void health_activity_detail_card_destroy(Window *window) { - HealthDetailCard *card = (HealthDetailCard *)window; - HealthActivityDetailCardData *card_data = card->data; - for (int i = 0; i < card_data->num_headings; i++) { - app_free(card_data->headings[i].primary_value); - app_free(card_data->headings[i].secondary_value); - } - for (int i = 0; i < card_data->num_subtitles; i++) { - app_free(card_data->subtitles[i].label); - } - for (int i = 0; i < card_data->num_zones; i++) { - app_free(card_data->zones[i].label); - } - i18n_free_all(card_data); - app_free(card_data); - health_detail_card_destroy(card); -} diff --git a/src/fw/apps/system_apps/health/health_activity_detail_card.h b/src/fw/apps/system_apps/health/health_activity_detail_card.h deleted file mode 100644 index d465d88ea0..0000000000 --- a/src/fw/apps/system_apps/health/health_activity_detail_card.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - -//! Creates a health activity detail window -//! @param HealthData pointer to the health data to be given to this card -//! @return A pointer to a newly allocated health activity detail window -Window *health_activity_detail_card_create(HealthData *health_data); - -//! Destroys a health activity detail window -//! @param window Window pointer to health activity detail window -void health_activity_detail_card_destroy(Window *window); diff --git a/src/fw/apps/system_apps/health/health_activity_summary_card.c b/src/fw/apps/system_apps/health/health_activity_summary_card.c deleted file mode 100644 index 3139307d33..0000000000 --- a/src/fw/apps/system_apps/health/health_activity_summary_card.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_activity_summary_card.h" -#include "health_activity_summary_card_segments.h" -#include "health_activity_detail_card.h" -#include "health_progress.h" -#include "health_ui.h" -#include "services/normal/activity/health_util.h" - -#include "applib/pbl_std/pbl_std.h" -#include "applib/ui/kino/kino_reel.h" -#include "applib/ui/text_layer.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" -#include "util/size.h" -#include "util/string.h" - -typedef struct HealthActivitySummaryCardData { - HealthData *health_data; - HealthProgressBar progress_bar; - KinoReel *icon; - int32_t current_steps; - int32_t typical_steps; - int32_t daily_average_steps; -} HealthActivitySummaryCardData; - - -#define PROGRESS_CURRENT_COLOR (PBL_IF_COLOR_ELSE(GColorIslamicGreen, GColorDarkGray)) -#define PROGRESS_TYPICAL_COLOR (PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack)) -#define PROGRESS_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorDarkGray, GColorClear)) -#define PROGRESS_OUTLINE_COLOR (PBL_IF_COLOR_ELSE(GColorClear, GColorBlack)) - -#define CURRENT_TEXT_COLOR PROGRESS_CURRENT_COLOR -#define CARD_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)) - - -static void prv_render_progress_bar(GContext *ctx, Layer *base_layer) { - HealthActivitySummaryCardData *data = layer_get_data(base_layer); - - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_BACKGROUND_COLOR, - 0, HEALTH_PROGRESS_BAR_MAX_VALUE); - - const int32_t progress_max = MAX(data->current_steps, data->daily_average_steps); - if (!progress_max) { - health_progress_bar_outline(ctx, &data->progress_bar, PROGRESS_OUTLINE_COLOR); - return; - } - - const int current_fill = data->current_steps * HEALTH_PROGRESS_BAR_MAX_VALUE / progress_max; - const int typical_fill = data->typical_steps * HEALTH_PROGRESS_BAR_MAX_VALUE / progress_max; - -#if PBL_COLOR - const bool behind_typical = (data->current_steps < data->typical_steps); - if (behind_typical) { - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, 0, typical_fill); - } -#endif - - if (data->current_steps) { - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_CURRENT_COLOR, 0, current_fill); - } - -#if PBL_COLOR - if (!behind_typical) { - health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_fill); - } -#else - health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_fill); -#endif - - // This needs to be done after drawing the progress bars or else the progress fill - // overlaps the outline and things look weird - health_progress_bar_outline(ctx, &data->progress_bar, PROGRESS_OUTLINE_COLOR); -} - -static void prv_render_icon(GContext *ctx, Layer *base_layer) { - HealthActivitySummaryCardData *data = layer_get_data(base_layer); - - const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(43, 38), 43); - const int x_center_offset = PBL_IF_BW_ELSE(19, 18); - kino_reel_draw(data->icon, ctx, GPoint(base_layer->bounds.size.w / 2 - x_center_offset, y)); -} - -static void prv_render_current_steps(GContext *ctx, Layer *base_layer) { - HealthActivitySummaryCardData *data = layer_get_data(base_layer); - - char buffer[8]; - GFont font; - if (data->current_steps) { - font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM); - snprintf(buffer, sizeof(buffer), "%"PRIu32"", data->current_steps); - } else { - font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD); - snprintf(buffer, sizeof(buffer), EM_DASH); - } - - const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(85, 83), 88); - graphics_context_set_text_color(ctx, CURRENT_TEXT_COLOR); - graphics_draw_text(ctx, buffer, font, - GRect(0, y, base_layer->bounds.size.w, 35), - GTextOverflowModeFill, GTextAlignmentCenter, NULL); -} - -static void prv_render_typical_steps(GContext *ctx, Layer *base_layer) { - HealthActivitySummaryCardData *data = layer_get_data(base_layer); - - char steps_buffer[12]; - if (data->typical_steps) { - snprintf(steps_buffer, sizeof(steps_buffer), "%"PRId32, data->typical_steps); - } else { - snprintf(steps_buffer, sizeof(steps_buffer), EM_DASH); - } - - health_ui_render_typical_text_box(ctx, base_layer, steps_buffer); -} - -static void prv_base_layer_update_proc(Layer *base_layer, GContext *ctx) { - HealthActivitySummaryCardData *data = layer_get_data(base_layer); - - data->current_steps = health_data_current_steps_get(data->health_data); - data->typical_steps = health_data_steps_get_current_average(data->health_data); - data->daily_average_steps = health_data_steps_get_cur_wday_average(data->health_data); - - prv_render_icon(ctx, base_layer); - - prv_render_progress_bar(ctx, base_layer); - - prv_render_current_steps(ctx, base_layer); - - prv_render_typical_steps(ctx, base_layer); -} - -static void prv_activity_detail_card_unload_callback(Window *window) { - health_activity_detail_card_destroy(window); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -Layer *health_activity_summary_card_create(HealthData *health_data) { - // create base layer - Layer *base_layer = layer_create_with_data(GRectZero, sizeof(HealthActivitySummaryCardData)); - HealthActivitySummaryCardData *health_activity_summary_card_data = layer_get_data(base_layer); - layer_set_update_proc(base_layer, prv_base_layer_update_proc); - // set health data - *health_activity_summary_card_data = (HealthActivitySummaryCardData) { - .health_data = health_data, - .icon = kino_reel_create_with_resource(RESOURCE_ID_HEALTH_APP_ACTIVITY), - .progress_bar = { - .num_segments = ARRAY_LENGTH(s_activity_summary_progress_segments), - .segments = s_activity_summary_progress_segments, - }, - }; - - return base_layer; -} - -void health_activity_summary_card_select_click_handler(Layer *layer) { - HealthActivitySummaryCardData *health_activity_summary_card_data = layer_get_data(layer); - HealthData *health_data = health_activity_summary_card_data->health_data; - Window *window = health_activity_detail_card_create(health_data); - window_set_window_handlers(window, &(WindowHandlers) { - .unload = prv_activity_detail_card_unload_callback, - }); - app_window_stack_push(window, true); -} - -void health_activity_summary_card_destroy(Layer *base_layer) { - HealthActivitySummaryCardData *data = layer_get_data(base_layer); - i18n_free_all(base_layer); - kino_reel_destroy(data->icon); - layer_destroy(base_layer); -} - -GColor health_activity_summary_card_get_bg_color(Layer *layer) { - return CARD_BACKGROUND_COLOR; -} - -bool health_activity_summary_show_select_indicator(Layer *layer) { - return true; -} diff --git a/src/fw/apps/system_apps/health/health_activity_summary_card.h b/src/fw/apps/system_apps/health/health_activity_summary_card.h deleted file mode 100644 index 79a6d3ff98..0000000000 --- a/src/fw/apps/system_apps/health/health_activity_summary_card.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -//! Creates a special layer with data -//! @param health_data A pointer to the health data being given this card -//! @return A pointer to a newly allocated layer, which contains its own data -Layer *health_activity_summary_card_create(HealthData *health_data); - -//! Health activity summary select click handler -//! @param layer A pointer to an existing layer containing its own data -void health_activity_summary_card_select_click_handler(Layer *layer); - -//! Destroy a special layer -//! @param base_layer A pointer to an existing layer containing its own data -void health_activity_summary_card_destroy(Layer *base_layer); - -//! Health activity summary layer background color getter -GColor health_activity_summary_card_get_bg_color(Layer *layer); - -//! Health activity summary layer select click is available -bool health_activity_summary_show_select_indicator(Layer *layer); diff --git a/src/fw/apps/system_apps/health/health_activity_summary_card_segments.h b/src/fw/apps/system_apps/health/health_activity_summary_card_segments.h deleted file mode 100644 index 1a9b38c1e7..0000000000 --- a/src/fw/apps/system_apps/health/health_activity_summary_card_segments.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_progress.h" - -//! 5 main segments + 2 real corners + 2 endcaps implemented as corners (for bw) -//! Each of the 5 non-corener segments get 20% of the total -#define AMOUNT_PER_SEGMENT (HEALTH_PROGRESS_BAR_MAX_VALUE * 20 / 100) - -// Found through trial and error -#define DEFAULT_MARK_WIDTH 50 - -#if PBL_BW -// The shape of the hexagon is slightly different on BW than on Color -static HealthProgressSegment s_activity_summary_progress_segments[] = { - { - // This is an endcap for BW (is a no-op on color) - .type = HealthProgressSegmentType_Corner, - .points = {{42, 85}, {51, 85}, {42, 85}, {51, 85}}, - }, - { - // Left side bottom - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{42, 84}, {51, 84}, {38, 58}, {28, 58}}, - }, - { - // Left side top - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{38, 57}, {28, 57}, {46, 26}, {56, 26}}, - }, - { - // Top left corner - .type = HealthProgressSegmentType_Corner, - .points = {{56, 26}, {46, 26}, {50, 18}, {56, 18}}, - }, - { - // Center top - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH * 2, - .points = {{55, 26}, {88, 26}, {89, 18}, {54, 18}}, - }, - { - // Top right corner - .type = HealthProgressSegmentType_Corner, - .points = {{88, 26}, {88, 18}, {92, 18}, {96, 26}}, - }, - { - // Right side top - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{87, 26}, {96, 26}, {113, 57}, {104, 57}}, - }, - { - // Right side bottom - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{104, 58}, {113, 58}, {99, 84}, {90, 84}}, - }, - { - // This is an endcap for BW (is a no-op on color) - .type = HealthProgressSegmentType_Corner, - .points = {{99, 85}, {90, 85}, {99, 85}, {90, 85}}, - }, -}; -#else // Color -// The shape is the same, but the offsets are different -// Slightly adjust the points on Round -#define X_ADJ (PBL_IF_ROUND_ELSE(18, 0)) -#define Y_ADJ (PBL_IF_ROUND_ELSE(6, 0)) - -static HealthProgressSegment s_activity_summary_progress_segments[] = { - { - // This is an endcap for BW (is a no-op on color) - .type = HealthProgressSegmentType_Corner, - .points = {{46 + X_ADJ, 81 + Y_ADJ}, {58 + X_ADJ, 81 + Y_ADJ}, - {46 + X_ADJ, 81 + Y_ADJ}, {58 + X_ADJ, 81 + Y_ADJ}}, - }, - { - // Left side bottom - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{46 + X_ADJ, 81 + Y_ADJ}, {58 + X_ADJ, 81 + Y_ADJ}, - {41 + X_ADJ, 51 + Y_ADJ}, {29 + X_ADJ, 51 + Y_ADJ}}, - }, - { - // Left side top - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{29 + X_ADJ, 51 + Y_ADJ}, {41 + X_ADJ, 51 + Y_ADJ}, - {57 + X_ADJ, 24 + Y_ADJ}, {45 + X_ADJ, 24 + Y_ADJ}}, - }, - { - // Top left corner - .type = HealthProgressSegmentType_Corner, - .points = {{57 + X_ADJ, 24 + Y_ADJ}, {45 + X_ADJ, 24 + Y_ADJ}, - {51 + X_ADJ, 15 + Y_ADJ}, {57 + X_ADJ, 15 + Y_ADJ}}, - }, - { - // Center top - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH * 2, - .points = {{55 + X_ADJ, 24 + Y_ADJ}, {89 + X_ADJ, 24 + Y_ADJ}, - {89 + X_ADJ, 15 + Y_ADJ}, {55 + X_ADJ, 15 + Y_ADJ}}, - }, - { - // Top right corner - .type = HealthProgressSegmentType_Corner, - .points = {{87 + X_ADJ, 24 + Y_ADJ}, {87 + X_ADJ, 15 + Y_ADJ}, - {93 + X_ADJ, 15 + Y_ADJ}, {99 + X_ADJ, 24 + Y_ADJ}}, - }, - { - // Right side top - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{87 + X_ADJ, 24 + Y_ADJ}, {99 + X_ADJ, 24 + Y_ADJ}, - {115 + X_ADJ, 51 + Y_ADJ}, {103 + X_ADJ, 51 + Y_ADJ}}, - }, - { - // Right side bottom - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{103 + X_ADJ, 51 + Y_ADJ}, {115 + X_ADJ, 51 + Y_ADJ}, - {98 + X_ADJ, 81 + Y_ADJ}, {86 + X_ADJ, 81 + Y_ADJ}}, - }, - { - // This is an endcap for BW (is a no-op on color) - .type = HealthProgressSegmentType_Corner, - .points = {{98 + X_ADJ, 81 + Y_ADJ}, {86 + X_ADJ, 81 + Y_ADJ}, - {98 + X_ADJ, 81 + Y_ADJ}, {86 + X_ADJ, 81 + Y_ADJ}}, - }, -}; -#endif diff --git a/src/fw/apps/system_apps/health/health_card_view.c b/src/fw/apps/system_apps/health/health_card_view.c deleted file mode 100644 index 2d1f2cfbec..0000000000 --- a/src/fw/apps/system_apps/health/health_card_view.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_card_view.h" - -#include "health.h" -#include "health_activity_summary_card.h" -#include "health_sleep_summary_card.h" -#include "health_hr_summary_card.h" - -#include "applib/app_launch_reason.h" -#include "applib/ui/action_button.h" -#include "applib/ui/content_indicator.h" -#include "applib/ui/content_indicator_private.h" -#include "kernel/pbl_malloc.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/timeline/health_layout.h" -#include "system/logging.h" -#include "util/time/time.h" - -#define SELECT_INDICATOR_COLOR (PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)) -#define BACK_TO_WATCHFACE (-1) - -// Enum for different card types -typedef enum { - Card_ActivitySummary, -#if CAPABILITY_HAS_BUILTIN_HRM - Card_HrSummary, -#endif - Card_SleepSummary, - CardCount -} Card; - -// Main structure for card view -typedef struct HealthCardView { - Window window; - HealthData *health_data; - Card current_card_index; - Layer *card_layers[CardCount]; - Animation *slide_animation; - Layer select_indicator_layer; - Layer down_arrow_layer; - Layer up_arrow_layer; - ContentIndicator down_indicator; - ContentIndicator up_indicator; -} HealthCardView; - -static Layer* (*s_card_view_create[CardCount])(HealthData *health_data) = { - [Card_ActivitySummary] = health_activity_summary_card_create, -#if CAPABILITY_HAS_BUILTIN_HRM - [Card_HrSummary] = health_hr_summary_card_create, -#endif - [Card_SleepSummary] = health_sleep_summary_card_create, -}; - -static void (*s_card_view_select_click_handler[CardCount])(Layer *layer) = { - [Card_ActivitySummary] = health_activity_summary_card_select_click_handler, -#if CAPABILITY_HAS_BUILTIN_HRM - [Card_HrSummary] = health_hr_summary_card_select_click_handler, -#endif - [Card_SleepSummary] = health_sleep_summary_card_select_click_handler, -}; - -static GColor (*s_card_view_get_bg_color[CardCount])(Layer *layer) = { - [Card_ActivitySummary] = health_activity_summary_card_get_bg_color, -#if CAPABILITY_HAS_BUILTIN_HRM - [Card_HrSummary] = health_hr_summary_card_get_bg_color, -#endif - [Card_SleepSummary] = health_sleep_summary_card_get_bg_color, -}; - -static bool (*s_card_view_show_select_indicator[CardCount])(Layer *layer) = { - [Card_ActivitySummary] = health_activity_summary_show_select_indicator, -#if CAPABILITY_HAS_BUILTIN_HRM - [Card_HrSummary] = health_hr_summary_show_select_indicator, -#endif - [Card_SleepSummary] = health_sleep_summary_show_select_indicator, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Private Functions -// - -static int prv_get_next_card_idx(Card current, bool up) { - const int direction = up ? 1 : -1; - int next = current + direction; - -// Skip over the HR card if we don't support it -#if CAPABILITY_HAS_BUILTIN_HRM - if (next == Card_HrSummary && !activity_is_hrm_present()) { - next = next + direction; - } - // if heart rate is diabled, change the order of cards to Activiy <-> Sleep <-> HR - else if (activity_is_hrm_present() && !activity_prefs_heart_rate_is_enabled()) { - if (current == Card_ActivitySummary) { - next = up ? Card_SleepSummary : BACK_TO_WATCHFACE; - } else if (current == Card_SleepSummary) { - next = up ? Card_HrSummary : Card_ActivitySummary; - } else if (current == Card_HrSummary) { - next = up ? CardCount : Card_SleepSummary; - } - } -#endif - - return next; -} - -static void prv_select_indicator_layer_update_proc(Layer *layer, GContext *ctx) { - action_button_draw(ctx, layer, SELECT_INDICATOR_COLOR); -} - -static void prv_refresh_select_indicator(HealthCardView *health_card_view) { - Layer *card_layer = health_card_view->card_layers[health_card_view->current_card_index]; - - const bool is_hidden = !s_card_view_show_select_indicator[health_card_view->current_card_index]( - card_layer); - - layer_set_hidden(&health_card_view->select_indicator_layer, is_hidden); -} - -static void prv_content_indicator_setup_direction(HealthCardView *health_card_view, - ContentIndicator *content_indicator, - Layer *indicator_layer, - ContentIndicatorDirection direction) { - Layer *card_layer = health_card_view->card_layers[health_card_view->current_card_index]; - - GColor card_bg_color = s_card_view_get_bg_color[health_card_view->current_card_index](card_layer); - - content_indicator_configure_direction(content_indicator, direction, &(ContentIndicatorConfig) { - .layer = indicator_layer, - .colors.foreground = gcolor_legible_over(card_bg_color), - .colors.background = card_bg_color, - }); -} - -static void prv_refresh_content_indicators(HealthCardView *health_card_view) { - prv_content_indicator_setup_direction(health_card_view, - &health_card_view->up_indicator, - &health_card_view->up_arrow_layer, - ContentIndicatorDirectionUp); - prv_content_indicator_setup_direction(health_card_view, - &health_card_view->down_indicator, - &health_card_view->down_arrow_layer, - ContentIndicatorDirectionDown); - - bool is_up_visible = true; - if (prv_get_next_card_idx(health_card_view->current_card_index, true) >= CardCount) { - is_up_visible = false; - } - - content_indicator_set_content_available(&health_card_view->up_indicator, - ContentIndicatorDirectionUp, - is_up_visible); - - // Down is always visible (the watchface is always an option) - content_indicator_set_content_available(&health_card_view->down_indicator, - ContentIndicatorDirectionDown, - true); -} - -static void prv_hide_content_indicators(HealthCardView *health_card_view) { - content_indicator_set_content_available(&health_card_view->up_indicator, - ContentIndicatorDirectionUp, - false); - content_indicator_set_content_available(&health_card_view->down_indicator, - ContentIndicatorDirectionDown, - false); -} - -static void prv_set_window_background_color(HealthCardView *health_card_view) { - Layer *card_layer = health_card_view->card_layers[health_card_view->current_card_index]; - window_set_background_color(&health_card_view->window, - s_card_view_get_bg_color[health_card_view->current_card_index](card_layer)); -} - -#define NUM_MID_FRAMES 1 - -static void prv_bg_animation_update(Animation *animation, AnimationProgress normalized) { - HealthCardView *health_card_view = animation_get_context(animation); - const AnimationProgress bounce_back_length = - (interpolate_moook_out_duration() * ANIMATION_NORMALIZED_MAX) / - interpolate_moook_soft_duration(NUM_MID_FRAMES); - if (normalized >= ANIMATION_NORMALIZED_MAX - bounce_back_length) { - prv_set_window_background_color(health_card_view); - } -} - -static void prv_bg_animation_started_handler(Animation *animation, void *context) { - HealthCardView *health_card_view = context; - - layer_set_hidden(health_card_view->card_layers[health_card_view->current_card_index], false); - - layer_set_hidden(&health_card_view->select_indicator_layer, true); - - prv_hide_content_indicators(health_card_view); -} - -static void prv_bg_animation_stopped_handler(Animation *animation, bool finished, void *context) { - HealthCardView *health_card_view = context; - - for (int i = 0; i < CardCount; i++) { - if (i != health_card_view->current_card_index) { - layer_set_hidden(health_card_view->card_layers[i], true); - } - } - - if (!finished) { - prv_set_window_background_color(health_card_view); - } else { - prv_refresh_select_indicator(health_card_view); - prv_refresh_content_indicators(health_card_view); - } -} - -static const AnimationImplementation prv_bg_animation_implementation = { - .update = &prv_bg_animation_update -}; - -static int64_t prv_interpolate_moook_soft(int32_t normalized, int64_t from, int64_t to) { - return interpolate_moook_soft(normalized, from, to, NUM_MID_FRAMES); -} - -static Animation* prv_create_slide_animation(Layer *layer, GRect *from_frame, GRect *to_frame) { - Animation *anim = (Animation *)property_animation_create_layer_frame(layer, from_frame, to_frame); - animation_set_duration(anim, interpolate_moook_soft_duration(NUM_MID_FRAMES)); - animation_set_custom_interpolation(anim, prv_interpolate_moook_soft); - return anim; -} - -// Create animation -static void prv_schedule_slide_animation(HealthCardView *health_card_view, - Card next_card_index, bool slide_up) { - animation_unschedule(health_card_view->slide_animation); - health_card_view->slide_animation = NULL; - - GRect window_bounds = window_get_root_layer(&health_card_view->window)->bounds; - - Layer *current_card_layer = health_card_view->card_layers[health_card_view->current_card_index]; - Layer *next_card_layer = health_card_view->card_layers[next_card_index]; - - GRect curr_stop = window_bounds; - curr_stop.origin.y = slide_up ? window_bounds.size.h : -window_bounds.size.h; - GRect next_start = window_bounds; - next_start.origin.y = slide_up ? -window_bounds.size.h : window_bounds.size.h; - - Animation *curr_out = prv_create_slide_animation(current_card_layer, &window_bounds, &curr_stop); - Animation *next_in = prv_create_slide_animation(next_card_layer, &next_start, &window_bounds); - - Animation *bg_anim = animation_create(); - animation_set_duration(bg_anim, interpolate_moook_soft_duration(NUM_MID_FRAMES)); - animation_set_handlers(bg_anim, (AnimationHandlers){ - .started = prv_bg_animation_started_handler, - .stopped = prv_bg_animation_stopped_handler, - }, health_card_view); - animation_set_implementation(bg_anim, &prv_bg_animation_implementation); - - health_card_view->slide_animation = animation_spawn_create(curr_out, next_in, bg_anim, NULL); - animation_schedule(health_card_view->slide_animation); - - health_card_view->current_card_index = next_card_index; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Callback Functions -// - -// Up/Down click handler -static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { - HealthCardView *health_card_view = context; - const bool slide_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); - const int next_card_index = prv_get_next_card_idx(health_card_view->current_card_index, slide_up); - - if (next_card_index < 0) { - // Exit - app_window_stack_pop_all(true); - } else if (next_card_index >= CardCount) { - // Top of the list (no-op) - // TODO: maybe we want an animation? - return; - } else { - // animate the cards' positions - prv_schedule_slide_animation(health_card_view, next_card_index, slide_up); - } -} - -// Select click handler -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { - HealthCardView *health_card_view = context; - Layer *layer = health_card_view->card_layers[health_card_view->current_card_index]; - s_card_view_select_click_handler[health_card_view->current_card_index](layer); - health_card_view_mark_dirty(health_card_view); -} - -// Click config provider -static void prv_click_config_provider(void *context) { - window_set_click_context(BUTTON_ID_UP, context); - window_set_click_context(BUTTON_ID_SELECT, context); - window_set_click_context(BUTTON_ID_DOWN, context); - window_single_click_subscribe(BUTTON_ID_UP, prv_up_down_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_up_down_click_handler); -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -HealthCardView *health_card_view_create(HealthData *health_data) { - HealthCardView *health_card_view = app_malloc_check(sizeof(HealthCardView)); - *health_card_view = (HealthCardView) { - .health_data = health_data, - }; - window_init(&health_card_view->window, "Health Card View"); - window_set_click_config_provider_with_context(&health_card_view->window, - prv_click_config_provider, health_card_view); - Layer *window_root = window_get_root_layer(&health_card_view->window); - - // create and add all card layers to window root layer - for (int i = 0; i < CardCount; i++) { - health_card_view->card_layers[i] = s_card_view_create[i](health_data); - layer_add_child(window_root, health_card_view->card_layers[i]); - } - - // set starting card based on launch args - HealthLaunchArgs launch_args = { .args = app_launch_get_args() }; - - health_card_view->current_card_index = - (launch_args.card_type == HealthCardType_Sleep) ? Card_SleepSummary : Card_ActivitySummary; - - // set window background - prv_set_window_background_color(health_card_view); - - // position current card - layer_set_frame(health_card_view->card_layers[health_card_view->current_card_index], - &window_root->frame); - - // setup select indicator - layer_init(&health_card_view->select_indicator_layer, &window_root->frame); - layer_add_child(window_root, &health_card_view->select_indicator_layer); - layer_set_update_proc(&health_card_view->select_indicator_layer, - prv_select_indicator_layer_update_proc); - - // setup content indicators - const int content_indicator_height = PBL_IF_ROUND_ELSE(18, 11); - const GRect down_arrow_layer_frame = grect_inset(window_root->frame, - GEdgeInsets(window_root->frame.size.h - content_indicator_height, 0, 0)); - layer_init(&health_card_view->down_arrow_layer, &down_arrow_layer_frame); - layer_add_child(window_root, &health_card_view->down_arrow_layer); - content_indicator_init(&health_card_view->down_indicator); - - const GRect up_arrow_layer_frame = grect_inset(window_root->frame, - GEdgeInsets(0, 0, window_root->frame.size.h - content_indicator_height)); - layer_init(&health_card_view->up_arrow_layer, &up_arrow_layer_frame); - layer_add_child(window_root, &health_card_view->up_arrow_layer); - content_indicator_init(&health_card_view->up_indicator); - - prv_refresh_content_indicators(health_card_view); - - return health_card_view; -} - -void health_card_view_destroy(HealthCardView *health_card_view) { - // destroy cards - health_activity_summary_card_destroy(health_card_view->card_layers[Card_ActivitySummary]); - health_sleep_summary_card_destroy(health_card_view->card_layers[Card_SleepSummary]); - // destroy self - window_deinit(&health_card_view->window); - app_free(health_card_view); -} - -void health_card_view_push(HealthCardView *health_card_view) { - app_window_stack_push(&health_card_view->window, true); -} - -void health_card_view_mark_dirty(HealthCardView *health_card_view) { - layer_mark_dirty(health_card_view->card_layers[health_card_view->current_card_index]); -} diff --git a/src/fw/apps/system_apps/health/health_card_view.h b/src/fw/apps/system_apps/health/health_card_view.h deleted file mode 100644 index 6eaa1f89d8..0000000000 --- a/src/fw/apps/system_apps/health/health_card_view.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - -//! Main structure for card view -typedef struct HealthCardView HealthCardView; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -//! Creates a HealthCardView -//! @param health_data A pointer to the health data being given this view -//! @return A pointer to the newly allocated HealthCardView -HealthCardView *health_card_view_create(HealthData *health_data); - -//! Destroy a HealthCardView -//! @param health_card_view A pointer to an existing HealthCardView -void health_card_view_destroy(HealthCardView *health_card_view); - -//! Push a HealthCardView to the window stack -//! @param health_card_view A pointer to an existing HealthCardView -void health_card_view_push(HealthCardView *health_card_view); - -//! Mark the card view as dirty so it is refreshed -//! @param health_card_view A pointer to an existing HealthCardView -void health_card_view_mark_dirty(HealthCardView *health_card_view); diff --git a/src/fw/apps/system_apps/health/health_data.c b/src/fw/apps/system_apps/health/health_data.c deleted file mode 100644 index c59f88bad1..0000000000 --- a/src/fw/apps/system_apps/health/health_data.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_data.h" -#include "health_data_private.h" - -#include "applib/app_logging.h" -#include "applib/health_service_private.h" -#include "drivers/rtc.h" -#include "kernel/pbl_malloc.h" -#include "syscall/syscall.h" -#include "system/logging.h" -#include "util/math.h" -#include "util/stats.h" -#include "util/time/time.h" - - -T_STATIC void prv_merge_adjacent_sessions(ActivitySession *current, - ActivitySession *previous) { - if (previous == NULL || current == NULL) { - return; - } - - if (current->type != previous->type || - (current->type != ActivitySessionType_RestfulNap && - current->type != ActivitySessionType_RestfulSleep)) { - // We only merge sessions if they are "deep" sleep/nap - return; - } - - // [FBO]: note that this only works because sleep sessions are - // all we care about and they are sorted. Don't try to extend this to walk - // or run sessions - - const uint16_t max_apart_merge_secs = 5 * SECONDS_PER_MINUTE; - time_t end_time = previous->start_utc + previous->length_min * SECONDS_PER_MINUTE; - if ((end_time + max_apart_merge_secs) > current->start_utc) { - current->length_min += previous->length_min + - (current->start_utc - end_time) / SECONDS_PER_MINUTE; - current->start_utc = previous->start_utc; - previous->length_min = 0; - previous->type = ActivitySessionType_None; - } -} - -// API Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -HealthData *health_data_create(void) { - return (HealthData *)app_zalloc_check(sizeof(HealthData)); -} - -void health_data_destroy(HealthData *health_data) { - app_free(health_data); -} - -void health_data_update_quick(HealthData *health_data) { - const time_t now = rtc_get_time(); - struct tm local_tm; - localtime_r(&now, &local_tm); - - // Get the current steps - health_service_private_get_metric_history(HealthMetricStepCount, 1, health_data->step_data); - - // Get the typical step averages for every 15 minutes - activity_get_step_averages(local_tm.tm_wday, &health_data->step_averages); - - health_data->current_hr_bpm = health_service_peek_current_value(HealthMetricHeartRateBPM); - // Get the most recent stable HR Reading timestamp. - activity_get_metric(ActivityMetricHeartRateFilteredUpdatedTimeUTC, 1, - (int32_t *)&health_data->hr_last_updated); -} - -void health_data_update(HealthData *health_data) { - const time_t now = rtc_get_time(); - struct tm local_tm; - localtime_r(&now, &local_tm); - - - //! Step / activity related data - // Get the step totals for today and the past 6 days - health_service_private_get_metric_history(HealthMetricStepCount, DAYS_PER_WEEK, - health_data->step_data); - // Update distance / calories now that we have our steps - health_data_update_step_derived_metrics(health_data); - - // Get the step averages for each 15 minute window. Used for typical steps - activity_get_step_averages(local_tm.tm_wday, &health_data->step_averages); - - // Get the average steps for the past month - activity_get_metric_monthly_avg(ActivityMetricStepCount, &health_data->monthly_step_average); - - - //! Sleep related data - health_service_private_get_metric_history(HealthMetricSleepSeconds, DAYS_PER_WEEK, - health_data->sleep_data); - activity_get_metric_typical(ActivityMetricSleepTotalSeconds, local_tm.tm_wday, - &health_data->typical_sleep); - activity_get_metric(ActivityMetricSleepRestfulSeconds, 1, &health_data->deep_sleep); - - activity_get_metric(ActivityMetricSleepEnterAtSeconds, 1, &health_data->sleep_start); - activity_get_metric(ActivityMetricSleepExitAtSeconds, 1, &health_data->sleep_end); - activity_get_metric_typical(ActivityMetricSleepEnterAtSeconds, local_tm.tm_wday, - &health_data->typical_sleep_start); - activity_get_metric_typical(ActivityMetricSleepExitAtSeconds, local_tm.tm_wday, - &health_data->typical_sleep_end); - activity_get_metric_monthly_avg(ActivityMetricSleepTotalSeconds, - &health_data->monthly_sleep_average); - - - //! Activity sessions - health_data->num_activity_sessions = ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT; - if (!activity_get_sessions(&health_data->num_activity_sessions, - health_data->activity_sessions)) { - PBL_LOG(LOG_LEVEL_ERROR, "Fetching activity sessions failed"); - } else { - ActivitySession *previous_session = NULL; - for (unsigned int i = 0; i < health_data->num_activity_sessions; i++) { - ActivitySession *session = &health_data->activity_sessions[i]; - prv_merge_adjacent_sessions(session, previous_session); - previous_session = session; - } - } - - //! HR related data - health_data_update_current_bpm(health_data); - health_data_update_hr_zone_minutes(health_data); -} - -void health_data_update_step_derived_metrics(HealthData *health_data) { - // get distance in meters - health_data->current_distance_meters = health_service_sum_today(HealthMetricWalkedDistanceMeters); - - // get calories - health_data->current_calories = health_service_sum_today(HealthMetricActiveKCalories) - + health_service_sum_today(HealthMetricRestingKCalories); -} - -void health_data_update_steps(HealthData *health_data, uint32_t new_steps) { - health_data->step_data[0] = new_steps; - health_data_update_step_derived_metrics(health_data); -} - -void health_data_update_sleep(HealthData *health_data, uint32_t new_sleep, - uint32_t new_deep_sleep) { - health_data->sleep_data[0] = new_sleep; - health_data->deep_sleep = new_deep_sleep; -} - -void health_data_update_current_bpm(HealthData *health_data) { - health_data->resting_hr_bpm = activity_prefs_heart_get_resting_hr(); - - // Check the quality. If it doesn't meet our standards, bail - int32_t quality; - activity_get_metric(ActivityMetricHeartRateRawQuality, 1, &quality); - if (quality < HRMQuality_Acceptable) { - return; - } - - uint32_t current_hr_timestamp; - activity_get_metric(ActivityMetricHeartRateRawUpdatedTimeUTC, 1, - (int32_t *)¤t_hr_timestamp); - if (current_hr_timestamp > (uint32_t)health_data->hr_last_updated) { - health_data->current_hr_bpm = health_service_peek_current_value(HealthMetricHeartRateRawBPM); - health_data->hr_last_updated = current_hr_timestamp; - } -} - -void health_data_update_hr_zone_minutes(HealthData *health_data) { - activity_get_metric(ActivityMetricHeartRateZone1Minutes, 1, &health_data->hr_zone1_minutes); - activity_get_metric(ActivityMetricHeartRateZone2Minutes, 1, &health_data->hr_zone2_minutes); - activity_get_metric(ActivityMetricHeartRateZone3Minutes, 1, &health_data->hr_zone3_minutes); -} - -int32_t *health_data_steps_get(HealthData *health_data) { - return health_data->step_data; -} - -int32_t health_data_current_steps_get(HealthData *health_data) { - return health_data->step_data[0]; -} - -int32_t health_data_current_distance_meters_get(HealthData *health_data) { - return health_data->current_distance_meters; -} - -int32_t health_data_current_calories_get(HealthData *health_data) { - return health_data->current_calories; -} - -static int32_t prv_health_data_get_n_average_chunks(HealthData *health_data, int number_of_chunks) { - uint32_t total_steps_avg = 0; - for (int i = 0; (i < ACTIVITY_NUM_METRIC_AVERAGES) && (i < number_of_chunks); i++) { - if (health_data->step_averages.average[i] != ACTIVITY_METRIC_AVERAGES_UNKNOWN) { - total_steps_avg += health_data->step_averages.average[i]; - } - } - return total_steps_avg; -} - -int32_t health_data_steps_get_current_average(HealthData *health_data) { - // get the current minutes into today - time_t utc_sec = rtc_get_time(); - struct tm local_tm; - localtime_r(&utc_sec, &local_tm); - int32_t today_min = local_tm.tm_hour * MINUTES_PER_HOUR + local_tm.tm_min; - const int k_minutes_per_step_avg = MINUTES_PER_DAY / ACTIVITY_NUM_METRIC_AVERAGES; - - // each average chunk is 15 mins long - if (health_data->step_average_last_updated_time != - ((today_min / k_minutes_per_step_avg) * k_minutes_per_step_avg)) { - // current_step_average is stale - health_data->current_step_average = - prv_health_data_get_n_average_chunks(health_data, today_min / k_minutes_per_step_avg); - health_data->step_average_last_updated_time = - (today_min / k_minutes_per_step_avg) * k_minutes_per_step_avg; - } - return health_data->current_step_average; -} - -int32_t health_data_steps_get_cur_wday_average(HealthData *health_data) { - return prv_health_data_get_n_average_chunks(health_data, ACTIVITY_NUM_METRIC_AVERAGES); -} - -int32_t health_data_steps_get_monthly_average(HealthData *health_data) { - return health_data->monthly_step_average; -} - - -int32_t *health_data_sleep_get(HealthData *health_data) { - return health_data->sleep_data; -} - -int32_t health_data_current_sleep_get(HealthData *health_data) { - return health_data->sleep_data[0]; -} - -int32_t health_data_sleep_get_cur_wday_average(HealthData *health_data) { - return health_data->typical_sleep; -} - -int32_t health_data_current_deep_sleep_get(HealthData *health_data) { - return health_data->deep_sleep; -} - -int32_t health_data_sleep_get_monthly_average(HealthData *health_data) { - return health_data->monthly_sleep_average; -} - -int32_t health_data_sleep_get_start_time(HealthData *health_data) { - return health_data->sleep_start; -} - -int32_t health_data_sleep_get_end_time(HealthData *health_data) { - return health_data->sleep_end; -} - -int32_t health_data_sleep_get_typical_start_time(HealthData *health_data) { - return health_data->typical_sleep_start; -} - -int32_t health_data_sleep_get_typical_end_time(HealthData *health_data) { - return health_data->typical_sleep_end; -} - -int32_t health_data_sleep_get_num_sessions(HealthData *health_data) { - return health_data->num_activity_sessions; -} - -ActivitySession *health_data_sleep_get_sessions(HealthData *health_data) { - return health_data->activity_sessions; -} - -uint32_t health_data_hr_get_current_bpm(HealthData *health_data) { - return health_data->current_hr_bpm; -} - -uint32_t health_data_hr_get_resting_bpm(HealthData *health_data) { - return health_data->resting_hr_bpm; -} - -time_t health_data_hr_get_last_updated_timestamp(HealthData *health_data) { - return health_data->hr_last_updated; -} - -int32_t health_data_hr_get_zone1_minutes(HealthData *health_data) { - return health_data->hr_zone1_minutes; -} - -int32_t health_data_hr_get_zone2_minutes(HealthData *health_data) { - return health_data->hr_zone2_minutes; -} - -int32_t health_data_hr_get_zone3_minutes(HealthData *health_data) { - return health_data->hr_zone3_minutes; -} diff --git a/src/fw/apps/system_apps/health/health_data.h b/src/fw/apps/system_apps/health/health_data.h deleted file mode 100644 index 299cf3dd0d..0000000000 --- a/src/fw/apps/system_apps/health/health_data.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/activity/activity.h" - -typedef struct { - int32_t sum; - int32_t avg; - int32_t max; -} BasicHealthStats; - -typedef struct { - BasicHealthStats weekday; - BasicHealthStats weekend; - BasicHealthStats daily; -} WeeklyStats; - -//! Health data model -typedef struct HealthData HealthData; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -//! Create a health data structure -//! @return A pointer to the new HealthData structure -HealthData *health_data_create(void); - -//! Destroy a health data structure -//! @param health_data A pointer to an existing health data structure -void health_data_destroy(HealthData *health_data); - -//! Fetch the current activity data from the system -//! @param health_data A pointer to the health data to use -void health_data_update(HealthData *health_data); - -//! Fetch only the data required to display the initial card. -//! This helps reduce lag when opening the app -//! @param health_data A pointer to the health data to use -void health_data_update_quick(HealthData *health_data); - -//! Fetch the current data for step derived metrics (distance, active time, calories) -//! @param health_data A pointer to the health data to use -void health_data_update_step_derived_metrics(HealthData *health_data); - -//! Update the number of steps the user has taken today -//! @param health_data A pointer to the health data to use -//! @param new_steps the new value of the steps for toaday -void health_data_update_steps(HealthData *health_data, uint32_t new_steps); - -//! Update the number of seconds the user has slept today -//! @param health_data A pointer to the health data to use -//! @param new_sleep the new value of the seconds of sleep today -//! @param new_deep_sleep the new value of the seconds of deep sleep today -void health_data_update_sleep(HealthData *health_data, uint32_t new_sleep, uint32_t new_deep_sleep); - -//! Update the current HR BPM -//! @param health_data A pointer to the health data to use -void health_data_update_current_bpm(HealthData *health_data); - -//! Update the time in HR zones -//! @param health_data A pointer to the health data to use -void health_data_update_hr_zone_minutes(HealthData *health_data); - - -//! Get the current step count -//! @param health_data A pointer to the health data to use -//! @return the current step count -int32_t health_data_current_steps_get(HealthData *health_data); - -//! Get the historical step data -//! @param health_data A pointer to the health data to use -//! @return A pointer to historical step data -int32_t *health_data_steps_get(HealthData *health_data); - -//! Get the current distance traveled in meters -//! @param health_data A pointer to the health data to use -//! @return the current distance travelled in meters -int32_t health_data_current_distance_meters_get(HealthData *health_data); - -//! Get the current calories -//! @param health_data A pointer to the health data to use -//! @return the current calories -int32_t health_data_current_calories_get(HealthData *health_data); - -//! Get current number of steps that should be taken by this time today -//! @param health_data A pointer to the health data to use -//! @return The integer number of steps that should be taken by this time today -int32_t health_data_steps_get_current_average(HealthData *health_data); - -//! Get the step average for the current day of the week -//! @param health_data A pointer to the health data to use -//! @return An integer value for the number of steps that are typically taken on this week day -int32_t health_data_steps_get_cur_wday_average(HealthData *health_data); - -//! Get the step average over the past month -//! @param health_data A pointer to the health data to use -//! @return An integer value for the average number of steps that we taken over the past month -int32_t health_data_steps_get_monthly_average(HealthData *health_data); - -//! Get the historical sleep data -//! @param health_data A pointer to the health data to use -//! @return A pointer to historical sleep data -int32_t *health_data_sleep_get(HealthData *health_data); - -//! Get the current sleep length -//! @param health_data A pointer to the health data to use -//! @return the current sleep length -int32_t health_data_current_sleep_get(HealthData *health_data); - -//! Gets the typical sleep duration for the current weekday -int32_t health_data_sleep_get_cur_wday_average(HealthData *health_data); - -//! Get the current deep sleep data -//! @param health_data A pointer to the health data to use -//! @return the current deep sleep length -int32_t health_data_current_deep_sleep_get(HealthData *health_data); - -//! Get the sleep average over the past month -//! @param health_data A pointer to the health data to use -//! @return The average daily sleep over the past month -int32_t health_data_sleep_get_monthly_average(HealthData *health_data); - -// Get the sleep start time -int32_t health_data_sleep_get_start_time(HealthData *health_data); - -// Get the sleep end time -int32_t health_data_sleep_get_end_time(HealthData *health_data); - -// Get the typical sleep start time -int32_t health_data_sleep_get_typical_start_time(HealthData *health_data); - -// Get the typical sleep end time -int32_t health_data_sleep_get_typical_end_time(HealthData *health_data); - -// Get the number of sleep sessions -int32_t health_data_sleep_get_num_sessions(HealthData *health_data); - -// Get today's sleep sessions -ActivitySession *health_data_sleep_get_sessions(HealthData *health_data); - -// Get current BPM -uint32_t health_data_hr_get_current_bpm(HealthData *health_data); - -// Get resting BPM -uint32_t health_data_hr_get_resting_bpm(HealthData *health_data); - -// Get HR last updated timestamp -time_t health_data_hr_get_last_updated_timestamp(HealthData *health_data); - -// Get number of minutes in Zone 1 -int32_t health_data_hr_get_zone1_minutes(HealthData *health_data); - -// Get number of minutes in Zone 2 -int32_t health_data_hr_get_zone2_minutes(HealthData *health_data); - -// Get number of minutes in Zone 3 -int32_t health_data_hr_get_zone3_minutes(HealthData *health_data); diff --git a/src/fw/apps/system_apps/health/health_data_private.h b/src/fw/apps/system_apps/health/health_data_private.h deleted file mode 100644 index 863fe0b21d..0000000000 --- a/src/fw/apps/system_apps/health/health_data_private.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -typedef struct HealthData { - //!< Current step / activity info - int32_t step_data[DAYS_PER_WEEK]; //!< Step histroy for today and the previous 6 days - int32_t current_distance_meters; - int32_t current_calories; - - //!< Typical step info - ActivityMetricAverages step_averages; //!< The step averages for the current day - int32_t current_step_average; //!< The current step average so far - int32_t step_average_last_updated_time; //!< The time at which current_step_average was updated - - int32_t monthly_step_average; - - int32_t sleep_data[DAYS_PER_WEEK]; //!< Sleep history for the past week - int32_t typical_sleep; //! Typical sleep for the current week day - int32_t deep_sleep; //!< Amount of deep sleep last night - - int32_t sleep_start; //!< When the user went to sleep (seconds after midnight) - int32_t sleep_end; //!< When the user woke up (seconds after midnight) - int32_t typical_sleep_start; //!< When the user typically goes to sleep - int32_t typical_sleep_end; //!< When the user typically wakes up - - int32_t monthly_sleep_average; - - uint32_t num_activity_sessions; //!< Number of activity sessions returned by the API - ActivitySession activity_sessions[ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT]; //!< Activity sessions - - int32_t current_hr_bpm; //!< Current BPM - int32_t resting_hr_bpm; //!< Resting BPM - time_t hr_last_updated; //!< Time at which HR data was last updated - - int32_t hr_zone1_minutes; - int32_t hr_zone2_minutes; - int32_t hr_zone3_minutes; -} HealthData; diff --git a/src/fw/apps/system_apps/health/health_detail_card.c b/src/fw/apps/system_apps/health/health_detail_card.c deleted file mode 100644 index c525f3e465..0000000000 --- a/src/fw/apps/system_apps/health/health_detail_card.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_detail_card.h" - -#include "applib/pbl_std/pbl_std.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/health_util.h" -#include "util/size.h" - -#include "system/logging.h" - -#define CORNER_RADIUS (3) - -static void prv_draw_headings(HealthDetailCard *detail_card, GContext *ctx, const Layer *layer) { - const int16_t rect_padding = PBL_IF_RECT_ELSE(5, 22); - const int16_t rect_height = 35; - - for (int i = 0; i < detail_card->num_headings; i++) { - HealthDetailHeading *heading = &detail_card->headings[i]; - - if (!heading->primary_label) { - continue; - } - -#if PBL_ROUND - int16_t header_y_origin = ((detail_card->num_headings > 1) ? 22 : 32) + (i * (rect_height + 5)); -#endif - - GRect header_rect = grect_inset(layer->bounds, GEdgeInsets(rect_padding)); - header_rect.origin.y += PBL_IF_RECT_ELSE(detail_card->y_origin, header_y_origin); - header_rect.size.h = rect_height; - - detail_card->y_origin += rect_height + rect_padding; - -#if PBL_BW - const GRect inner_rect = grect_inset(header_rect, GEdgeInsets(1)); - const uint16_t inner_corner_radius = CORNER_RADIUS - 1; - graphics_context_set_stroke_color(ctx, heading->outline_color); - graphics_draw_round_rect(ctx, &inner_rect, inner_corner_radius); - graphics_draw_round_rect(ctx, &header_rect, CORNER_RADIUS); -#else - graphics_context_set_fill_color(ctx, heading->fill_color); - graphics_fill_round_rect(ctx, &header_rect, CORNER_RADIUS, GCornersAll); -#endif - - const bool has_secondary_heading = heading->secondary_label; - - GRect label_rect = header_rect; - if (has_secondary_heading) { - label_rect.size.w /= 2; - } - label_rect.size.h = 12; // Restrict to a single line - - graphics_context_set_text_color(ctx, gcolor_legible_over(heading->fill_color)); - - graphics_draw_text(ctx, heading->primary_label, detail_card->heading_label_font, - label_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - - const int16_t value_rect_y_padding = 12; - - GRect value_rect = label_rect; - value_rect.origin.y += value_rect_y_padding; - - graphics_draw_text(ctx, heading->primary_value, detail_card->heading_value_font, - value_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - - if (!heading->secondary_label) { - continue; - } - - const int separator_padding = 5; - - GPoint separator_top_point = GPoint(header_rect.origin.x + (header_rect.size.w / 2) - 1, - header_rect.origin.y + separator_padding); - GPoint separator_bot_point = GPoint(separator_top_point.x, separator_top_point.y + - header_rect.size.h - (separator_padding * 2) - 1); - - graphics_draw_line(ctx, separator_top_point, separator_bot_point); - - // draw another line to make the width 2px - separator_top_point.x++; - separator_bot_point.x++; - graphics_draw_line(ctx, separator_top_point, separator_bot_point); - - label_rect.origin.x += label_rect.size.w; - value_rect.origin.x += value_rect.size.w; - - graphics_draw_text(ctx, heading->secondary_label, detail_card->heading_label_font, - label_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - - graphics_draw_text(ctx, heading->secondary_value, detail_card->heading_value_font, - value_rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - } -} - -static void prv_draw_subtitles(HealthDetailCard *detail_card, GContext *ctx, const Layer *layer) { - const int16_t rect_padding = PBL_IF_RECT_ELSE(5, 0); - const int16_t rect_height = PBL_IF_RECT_ELSE(23, 36); - - for (int i = 0; i < detail_card->num_subtitles; i++) { - HealthDetailSubtitle *subtitle = &detail_card->subtitles[i]; - - if (!subtitle->label) { - continue; - } - - GRect subtitle_rect = grect_inset(layer->bounds, GEdgeInsets(rect_padding)); - subtitle_rect.origin.y += PBL_IF_RECT_ELSE(detail_card->y_origin, 125); - subtitle_rect.size.h = rect_height; - - detail_card->y_origin += rect_height + rect_padding; - - graphics_context_set_fill_color(ctx, subtitle->fill_color); - graphics_fill_round_rect(ctx, &subtitle_rect, CORNER_RADIUS, GCornersAll); - - if (!gcolor_equal(subtitle->outline_color, GColorClear)) { - graphics_context_set_stroke_color(ctx, subtitle->outline_color); - graphics_draw_round_rect(ctx, &subtitle_rect, CORNER_RADIUS); - } - - // font offset - subtitle_rect.origin.y -= PBL_IF_RECT_ELSE(1, 3); - - graphics_context_set_text_color(ctx, gcolor_legible_over(subtitle->fill_color)); - graphics_draw_text(ctx, subtitle->label, detail_card->subtitle_font, subtitle_rect, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - } -} - -static void prv_draw_progress_bar(GContext *ctx, HealthProgressBar *progress_bar, GColor bg_color, - GColor fill_color, int current_progress, int typical_progress, int max_progress, - bool hide_typical) { - const GColor typical_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack); - const GColor outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack); - - health_progress_bar_fill(ctx, progress_bar, bg_color, 0, HEALTH_PROGRESS_BAR_MAX_VALUE); - - if (max_progress > 0) { - const int current_fill = (current_progress * HEALTH_PROGRESS_BAR_MAX_VALUE) / max_progress; - - health_progress_bar_fill(ctx, progress_bar, fill_color, 0, current_fill); - - if (typical_progress > 0) { - const int typical_fill = (typical_progress * HEALTH_PROGRESS_BAR_MAX_VALUE) / max_progress; - health_progress_bar_mark(ctx, progress_bar, typical_color, typical_fill); - } - } - - health_progress_bar_outline(ctx, progress_bar, outline_color); -} - -#if PBL_RECT -static void prv_draw_progress_bar_in_zone(GContext *ctx, const GRect *zone_rect, GColor fill_color, - int current_progress, int typical_progress, int max_progress, bool hide_typical) { - const int16_t progress_bar_x = zone_rect->origin.x + PBL_IF_BW_ELSE(0, -1); - const int16_t progress_bar_y = zone_rect->origin.y + 22; - const int16_t progress_bar_width = zone_rect->size.w + PBL_IF_BW_ELSE(-2, 1); - const int16_t progress_bar_height = 10 + PBL_IF_BW_ELSE(-1, 0); - - HealthProgressSegment segments[] = { - { - // Left side vertical line (needed for the draw outline function to draw the verticle lines) - .type = HealthProgressSegmentType_Corner, - .points = { - {progress_bar_x, progress_bar_y}, - {progress_bar_x, progress_bar_y + progress_bar_height}, - {progress_bar_x, progress_bar_y + progress_bar_height}, - {progress_bar_x, progress_bar_y}, - }, - }, - { - // Right side vertical line (needed for the draw outline function to draw the verticle lines) - .type = HealthProgressSegmentType_Corner, - .points = { - {progress_bar_x + progress_bar_width, progress_bar_y}, - {progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height}, - {progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height}, - {progress_bar_x + progress_bar_width, progress_bar_y}, - }, - }, - { - // Horizontal bar from left line to right line - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = HEALTH_PROGRESS_BAR_MAX_VALUE, - .mark_width = 124, // Arbitrarily chosen through trial and error - .points = { - {progress_bar_x, progress_bar_y + progress_bar_height}, - {progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height}, - {progress_bar_x + progress_bar_width, progress_bar_y}, - {progress_bar_x, progress_bar_y}, - }, - }, - }; - - HealthProgressBar progress_bar = { - .num_segments = ARRAY_LENGTH(segments), - .segments = segments, - }; - - const GColor bg_color = PBL_IF_COLOR_ELSE(GColorDarkGray, GColorWhite); - - prv_draw_progress_bar(ctx, &progress_bar, bg_color, fill_color, current_progress, - typical_progress, max_progress, hide_typical); -} - -static void prv_draw_zones(HealthDetailCard *detail_card, GContext *ctx) { - if (detail_card->num_zones <= 0) { - return; - } - - const int16_t rect_padding = 5; - const int16_t rect_height = 33; - - GRect zone_rect = grect_inset(detail_card->window.layer.bounds, GEdgeInsets(rect_padding)); - zone_rect.origin.y += detail_card->y_origin; - zone_rect.size.h = rect_height; - - for (int i = 0; i < detail_card->num_zones; i++) { - HealthDetailZone *zone = &detail_card->zones[i]; - - graphics_context_set_text_color(ctx, PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)); - graphics_draw_text(ctx, zone->label, detail_card->subtitle_font, zone_rect, - GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); - - if (zone->show_crown) { - const GSize label_size = app_graphics_text_layout_get_content_size(zone->label, - detail_card->subtitle_font, zone_rect, GTextOverflowModeWordWrap, GTextAlignmentLeft); - GPoint icon_offset = zone_rect.origin; - icon_offset.x += label_size.w + 4; -#if PBL_BW - icon_offset.y += 2; -#endif - gdraw_command_image_draw(ctx, detail_card->icon_crown, icon_offset); - } - - prv_draw_progress_bar_in_zone(ctx, &zone_rect, zone->fill_color, zone->progress, - detail_card->daily_avg, detail_card->max_progress, zone->hide_typical); - - zone_rect.origin.y += rect_height + rect_padding; - detail_card->y_origin += rect_height + rect_padding; - } - - detail_card->y_origin += rect_padding; -} -#endif // PBL_RECT - -#if PBL_ROUND -static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, void *context) { - HealthDetailCard *detail_card = (HealthDetailCard *)context; - return detail_card->num_zones + 1; -} - -static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - HealthDetailCard *detail_card = (HealthDetailCard *)context; - - MenuIndex selected_index = menu_layer_get_selected_index(&detail_card->menu_layer); - - if (cell_index->row == 0) { - graphics_context_set_fill_color(ctx, detail_card->bg_color); - graphics_fill_rect(ctx, &cell_layer->bounds); - - prv_draw_headings(detail_card, ctx, cell_layer); - prv_draw_subtitles(detail_card, ctx, cell_layer); - return; - } - - HealthDetailZone *zone = &detail_card->zones[cell_index->row - 1]; - - const int16_t rect_padding = 5; - - GRect label_rect = grect_inset(cell_layer->bounds, GEdgeInsets(rect_padding)); - - if (!menu_layer_is_index_selected(&detail_card->menu_layer, cell_index)) { - label_rect.origin.y = (cell_index->row < selected_index.row) ? 3 : 22; - - graphics_context_set_text_color(ctx, GColorWhite); - graphics_draw_text(ctx, zone->label, detail_card->subtitle_font, label_rect, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - } else { - const GRect cell_bounds = grect_inset(cell_layer->bounds, GEdgeInsets(0, -1)); - - HealthProgressSegment segments[] = { - { - // Horizontal bar from left line to right line - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = HEALTH_PROGRESS_BAR_MAX_VALUE, - .mark_width = 100, // Arbitrarily chosen through trial and error - .points = { - {cell_bounds.origin.x, cell_bounds.size.h}, - {cell_bounds.size.w, cell_bounds.size.h}, - {cell_bounds.size.w, cell_bounds.origin.y}, - {cell_bounds.origin.x, cell_bounds.origin.y}, - }, - }, - }; - - HealthProgressBar progress_bar = { - .num_segments = ARRAY_LENGTH(segments), - .segments = segments, - }; - - prv_draw_progress_bar(ctx, &progress_bar, GColorLightGray, zone->fill_color, zone->progress, - detail_card->daily_avg, detail_card->max_progress, zone->hide_typical); - - label_rect.origin.y += 3; - - if (zone->show_crown) { - const GSize icon_size = gdraw_command_image_get_bounds_size(detail_card->icon_crown); - GPoint icon_offset = GPoint((cell_layer->bounds.size.w / 2) - (icon_size.w / 2), 4); - gdraw_command_image_draw(ctx, detail_card->icon_crown, icon_offset); - - label_rect.origin.y += 8; - } - - graphics_context_set_text_color(ctx, GColorBlack); - graphics_draw_text(ctx, zone->label, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), label_rect, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - } -} - -static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, void *context) { - if (cell_index->row == 0) { - return menu_layer_is_index_selected(menu_layer, cell_index) ? DISP_ROWS : 0; - } - - return menu_layer_is_index_selected(menu_layer, cell_index) ? 50 : 54; -} - -static void prv_refresh_content_indicators(HealthDetailCard *detail_card) { - const bool is_up_visible = (menu_layer_get_selected_index(&detail_card->menu_layer).row > 0); - const bool is_down_visible = (menu_layer_get_selected_index(&detail_card->menu_layer).row < - prv_get_num_rows_callback(&detail_card->menu_layer, 0, detail_card) - 1); - - content_indicator_set_content_available(&detail_card->up_indicator, - ContentIndicatorDirectionUp, - is_up_visible); - - content_indicator_set_content_available(&detail_card->down_indicator, - ContentIndicatorDirectionDown, - is_down_visible); -} - -static void prv_selection_changed_callback(struct MenuLayer *menu_layer, MenuIndex new_index, - MenuIndex old_index, void *context) { - HealthDetailCard *detail_card = (HealthDetailCard *)context; - prv_refresh_content_indicators(detail_card); -} -#else // PBL_RECT -static void prv_health_detail_scroll_layer_update_proc(Layer *layer, GContext *ctx) { - ScrollLayer *scroll_layer = (ScrollLayer *)layer->parent; - HealthDetailCard *detail_card = (HealthDetailCard *)scroll_layer->context; - - detail_card->y_origin = 0; - - prv_draw_headings(detail_card, ctx, &detail_card->window.layer); - prv_draw_subtitles(detail_card, ctx, &detail_card->window.layer); - prv_draw_zones(detail_card, ctx); - - scroll_layer_set_content_size(&detail_card->scroll_layer, - GSize(layer->bounds.size.w, detail_card->y_origin)); -} -#endif // PBL_RECT - -HealthDetailCard *health_detail_card_create(const HealthDetailCardConfig *config) { - HealthDetailCard *detail_card = app_zalloc_check(sizeof(HealthDetailCard)); - window_init(&detail_card->window, WINDOW_NAME("Health Detail Card")); - health_detail_card_configure(detail_card, config); - GRect window_frame = detail_card->window.layer.frame; -#if PBL_ROUND - // setup menu layer - MenuLayer *menu_layer = &detail_card->menu_layer; - menu_layer_init(menu_layer, &window_frame); - menu_layer_set_callbacks(menu_layer, detail_card, &(MenuLayerCallbacks) { - .get_num_rows = prv_get_num_rows_callback, - .get_cell_height = prv_get_cell_height_callback, - .draw_row = prv_draw_row_callback, - .selection_changed = prv_selection_changed_callback, - }); - menu_layer_set_normal_colors(menu_layer, detail_card->bg_color, GColorWhite); - menu_layer_set_highlight_colors(menu_layer, detail_card->bg_color, GColorBlack); - menu_layer_set_click_config_onto_window(menu_layer, &detail_card->window); - layer_add_child(&detail_card->window.layer, menu_layer_get_layer(menu_layer)); - - // setup content indicators - const int content_indicator_height = 15; - const GRect down_arrow_layer_frame = grect_inset(window_frame, - GEdgeInsets(window_frame.size.h - content_indicator_height, 0, 0)); - layer_init(&detail_card->down_arrow_layer, &down_arrow_layer_frame); - layer_add_child(&detail_card->window.layer, &detail_card->down_arrow_layer); - content_indicator_init(&detail_card->down_indicator); - - const GRect up_arrow_layer_frame = grect_inset(window_frame, - GEdgeInsets(0, 0, window_frame.size.h - content_indicator_height)); - layer_init(&detail_card->up_arrow_layer, &up_arrow_layer_frame); - layer_add_child(&detail_card->window.layer, &detail_card->up_arrow_layer); - content_indicator_init(&detail_card->up_indicator); - - ContentIndicatorConfig content_indicator_config = (ContentIndicatorConfig) { - .layer = &detail_card->up_arrow_layer, - .colors.foreground = gcolor_legible_over(detail_card->bg_color), - .colors.background = detail_card->bg_color, - }; - content_indicator_configure_direction(&detail_card->up_indicator, ContentIndicatorDirectionUp, - &content_indicator_config); - content_indicator_config.layer = &detail_card->down_arrow_layer; - content_indicator_configure_direction(&detail_card->down_indicator, ContentIndicatorDirectionDown, - &content_indicator_config); - prv_refresh_content_indicators(detail_card); -#else // PBL_RECT - // setup scroll layer - scroll_layer_init(&detail_card->scroll_layer, &window_frame); - scroll_layer_set_click_config_onto_window(&detail_card->scroll_layer, &detail_card->window); - scroll_layer_set_context(&detail_card->scroll_layer, detail_card); - scroll_layer_set_shadow_hidden(&detail_card->scroll_layer, true); - layer_add_child(&detail_card->window.layer, (Layer *)&detail_card->scroll_layer); - layer_set_update_proc(&detail_card->scroll_layer.content_sublayer, - prv_health_detail_scroll_layer_update_proc); -#endif // PBL_RECT - detail_card->icon_crown = gdraw_command_image_create_with_resource(RESOURCE_ID_HEALTH_APP_CROWN); - detail_card->heading_label_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); - detail_card->heading_value_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - detail_card->subtitle_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - return detail_card; -} - -void health_detail_card_destroy(HealthDetailCard *detail_card) { - if (!detail_card) { - return; - } - gdraw_command_image_destroy(detail_card->icon_crown); -#if PBL_ROUND - menu_layer_deinit(&detail_card->menu_layer); - content_indicator_deinit(&detail_card->down_indicator); - content_indicator_deinit(&detail_card->up_indicator); -#else - scroll_layer_deinit(&detail_card->scroll_layer); -#endif - i18n_free_all(detail_card); - app_free(detail_card); -} - -void health_detail_card_configure(HealthDetailCard *detail_card, - const HealthDetailCardConfig *config) { - if (!detail_card || !config) { - return; - } - detail_card->bg_color = config->bg_color; - window_set_background_color(&detail_card->window, detail_card->bg_color); - if (config->num_headings) { - detail_card->num_headings = config->num_headings; - detail_card->headings = config->headings; - } - if (config->num_subtitles) { - detail_card->num_subtitles = config->num_subtitles; - detail_card->subtitles = config->subtitles; - } - detail_card->daily_avg = config->daily_avg; - detail_card->max_progress = MAX(config->weekly_max, detail_card->daily_avg); - detail_card->max_progress = detail_card->max_progress * 11 / 10; // add 10%; - if (config->num_zones) { - detail_card->num_zones = config->num_zones; - detail_card->zones = config->zones; - } - if (config->data) { - detail_card->data = config->data; - } -} - -void health_detail_card_set_render_day_zones(HealthDetailZone *zones, int16_t *num_zones, - int32_t *weekly_max, bool format_hours_and_minutes, bool show_crown, GColor fill_color, - GColor today_fill_color, int32_t *day_data, void *i18n_owner) { - time_t time_utc = rtc_get_time(); - struct tm time_tm; - - int max_data = 0; - int crown_index = 0; - - *num_zones = DAYS_PER_WEEK; - - for (int i = 0; i < *num_zones; i++) { - localtime_r(&time_utc, &time_tm); - - const bool is_today = (i == 0); - - const size_t buffer_size = 32; - zones[i] = (HealthDetailZone) { - .label = app_zalloc_check(buffer_size), - .progress = day_data[i], - .fill_color = is_today ? PBL_IF_ROUND_ELSE(fill_color, today_fill_color) : fill_color, - .hide_typical = is_today, - }; - - char *label_ptr = zones[i].label; - - int pos = 0; - - if (i == 0) { - pos += snprintf(label_ptr, buffer_size, "%s ", i18n_get("Today", i18n_owner)); - } else { - pos += strftime(label_ptr, buffer_size, "%a ", &time_tm); - } - - if (day_data[i] > 0) { - if (format_hours_and_minutes) { - health_util_format_hours_and_minutes(label_ptr + pos, buffer_size - pos, day_data[i], - i18n_owner); - } else { - snprintf(label_ptr + pos, buffer_size - pos, "%"PRId32, day_data[i]); - } - } - - if (day_data[i] > *weekly_max) { - *weekly_max = day_data[i]; - } - - if (day_data[i] > max_data) { - max_data = day_data[i]; - crown_index = i; - } - - time_utc -= SECONDS_PER_DAY; - } - - if (crown_index && show_crown) { - zones[crown_index].show_crown = true; - } -} diff --git a/src/fw/apps/system_apps/health/health_detail_card.h b/src/fw/apps/system_apps/health/health_detail_card.h deleted file mode 100644 index 78d778d3c0..0000000000 --- a/src/fw/apps/system_apps/health/health_detail_card.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" -#include "health_progress.h" - -#include "applib/fonts/fonts.h" -#include "applib/graphics/gdraw_command_image.h" -#include "applib/ui/content_indicator_private.h" -#include "applib/ui/ui.h" - -#define MAX_NUM_HEADINGS (2) -#define MAX_NUM_SUBTITLES (2) -#define MAX_NUM_ZONES (7) - -typedef struct HealthDetailHeading { - char *primary_label; - char *primary_value; - char *secondary_label; - char *secondary_value; - GColor fill_color; - GColor outline_color; -} HealthDetailHeading; - -typedef struct HealthDetailSubtitle { - char *label; - GColor fill_color; - GColor outline_color; -} HealthDetailSubtitle; - -typedef struct HealthDetailZone { - char *label; - bool show_crown; - bool hide_typical; - GColor fill_color; - HealthProgressBarValue progress; -} HealthDetailZone; - -typedef struct HealthDetailCardConfig { - GColor bg_color; - int16_t num_headings; - HealthDetailHeading *headings; - int16_t num_subtitles; - HealthDetailSubtitle *subtitles; - int32_t daily_avg; - int32_t weekly_max; - int16_t num_zones; - HealthDetailZone *zones; - void *data; -} HealthDetailCardConfig; - -typedef struct HealthDetailCard { - Window window; -#if PBL_ROUND - MenuLayer menu_layer; - Layer down_arrow_layer; - Layer up_arrow_layer; - ContentIndicator down_indicator; - ContentIndicator up_indicator; -#else - ScrollLayer scroll_layer; -#endif - - GColor bg_color; - - int16_t num_headings; - HealthDetailHeading *headings; - - int16_t num_subtitles; - HealthDetailSubtitle *subtitles; - - GFont heading_label_font; - GFont heading_value_font; - GFont subtitle_font; - - GDrawCommandImage *icon_crown; - - int32_t daily_avg; - int32_t max_progress; - - int16_t num_zones; - HealthDetailZone *zones; - - int16_t y_origin; - - void *data; -} HealthDetailCard; - -//! Creates a HealthDetailCard -HealthDetailCard *health_detail_card_create(const HealthDetailCardConfig *config); - -//! Destroys a HealthDetailCard -void health_detail_card_destroy(HealthDetailCard *detail_card); - -//! Configures a HealthDetailCard -void health_detail_card_configure(HealthDetailCard *detail_card, - const HealthDetailCardConfig *config); - -//! Sets the zones for any daily history (steps/sleep) -//! @param zones pointer to the HealthDetailZone to be set -//! @param num_zones number of zones in the pointer -//! @wparam weekly_max pointer to the weekly max to be set -//! @param format_hours_and_minutes whether to a format the values for hours and minutes in label -//! @param show_crown whether to set the `show_crown` in the zone with weekly max -//! @param fill_color color to fill all the progress bars with except today -//! @param today_fill_color color to fill the today progress bar with -//! @param day_data pointer to the daily history data -//! @param i18n_owner pointer to the i18n owner -void health_detail_card_set_render_day_zones(HealthDetailZone *zones, int16_t *num_zones, - int32_t *weekly_max, bool format_hours_and_minutes, bool show_crown, GColor fill_color, - GColor today_fill_color, int32_t *day_data, void *i18n_owner); diff --git a/src/fw/apps/system_apps/health/health_graph_card.c b/src/fw/apps/system_apps/health/health_graph_card.c deleted file mode 100644 index adc1d51478..0000000000 --- a/src/fw/apps/system_apps/health/health_graph_card.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_graph_card.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "drivers/rtc.h" -#include "kernel/pbl_malloc.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "util/math.h" -#include "util/size.h" -#include "util/string.h" -#include "util/time/time.h" - -//! Marks where the graph begins -#define GRAPH_OFFSET_Y PBL_IF_RECT_ELSE(38, 48) - -//! Marks where the graph ends and where the labels begin -#define LABEL_OFFSET_Y PBL_IF_RECT_ELSE(118, 113) -#define LABEL_HEIGHT 27 - -#define GRAPH_HEIGHT (LABEL_OFFSET_Y - GRAPH_OFFSET_Y) - -#define AVG_LINE_HEIGHT 4 -#define AVG_LINE_LEGEND_WIDTH 10 -#define AVG_LINE_COLOR GColorYellow - -#define INFO_PADDING_BOTTOM 6 - -//! Get the current day in the standard tm format. Sunday is 0 -static uint8_t prv_get_weekday(time_t timestamp) { - return time_util_get_day_in_week(timestamp); -} - -static void prv_draw_title(HealthGraphCard *graph_card, GContext *ctx) { - const GRect *bounds = &graph_card->layer.bounds; - graphics_context_set_text_color(ctx, GColorBlack); - const int title_height = 60; - GRect drawing_box = GRect(0, 0, bounds->size.w, title_height); - -#if PBL_ROUND - // inset the drawing bounds if on round to account for the bezel - drawing_box = grect_inset(drawing_box, GEdgeInsets(8)); - - const GSize text_size = graphics_text_layout_get_max_used_size(ctx, graph_card->title, - graph_card->title_font, drawing_box, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - - // increase drawing box y offset if we're only drawing one line of text - if (text_size.h < 30) { - drawing_box.origin.y += 10; - } -#endif - - graphics_draw_text(ctx, graph_card->title, graph_card->title_font, drawing_box, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static void prv_draw_day_labels_background(HealthGraphCard *graph_card, GContext *ctx) { - const GRect *bounds = &graph_card->layer.bounds; - GRect box = {{ .y = LABEL_OFFSET_Y }, { bounds->size.w, LABEL_HEIGHT }}; - graphics_context_set_fill_color(ctx, GColorBlack); - graphics_fill_rect(ctx, &box); - const int border_width = 3; - box = grect_inset(box, GEdgeInsets(border_width, 0)); - const GColor label_background_color = GColorWhite; - graphics_context_set_fill_color(ctx, label_background_color); - graphics_fill_rect(ctx, &box); -} - -//! Get the corresponding data point for a weekday. -//! Sunday is 0, and the day data begins with today and continues into the past. -static int32_t prv_get_day_point(HealthGraphCard *graph_card, int weekday) { - const int index = positive_modulo(graph_card->current_day - weekday, DAYS_PER_WEEK); - return graph_card->day_data[index]; -} - -static int32_t prv_convert_to_graph_height(HealthGraphCard *graph_card, int32_t point) { - // Round up in order to show the minimum stub bar once progress begins - int bar_height = (point * GRAPH_HEIGHT + graph_card->data_max - 1) / graph_card->data_max; - const int minimum_stub_height = 5; - if (bar_height > 0 && bar_height < minimum_stub_height) { - // Show the minimum stub bar if progress just began - bar_height = minimum_stub_height; - } - return bar_height; -} - -static void prv_setup_day_bar_box(int weekday, GRect *box, int16_t bar_height) { - const int w = 23; // normal bar width; -#if PBL_RECT - // The center bars are slightly wider than the other bars - // Note that Thursday is the center bar, not Wednesday since drawing begins with Monday - // S M T W T F S - const int bar_widths[DAYS_PER_WEEK] = { w, w, w, w + 1, w + 1, w + 1, w }; - const int bar_width = bar_widths[weekday]; -#else - const int bar_width = w; -#endif - box->origin.y = LABEL_OFFSET_Y - bar_height; - box->size = GSize(bar_width, bar_height); -} - -static void prv_draw_day_bar_wide(GContext *ctx, const GRect *box, const GRect *box_inset, - GColor bar_color) { - const GColor border_color = GColorBlack; - graphics_context_set_fill_color(ctx, border_color); - graphics_fill_rect(ctx, box); - graphics_context_set_fill_color(ctx, bar_color); - graphics_fill_rect(ctx, box_inset); -} - -static void prv_draw_day_bar_thin(GContext *ctx, const GRect *box, int weekday, - GColor bar_color) { - GRect thin_box = *box; - // Nudge the bars before Thursday (inclusive). Note that Sunday is on the right side, at the end - const int thin_offset_x = WITHIN(weekday, Monday, Thursday) ? 1 : 0; - const int thin_width = 5; - thin_box.origin.x += thin_offset_x + (box->size.w - thin_width) / 2; - thin_box.size.w = thin_width; - graphics_context_set_fill_color(ctx, bar_color); - graphics_fill_rect(ctx, &thin_box); -} - -static int16_t prv_draw_day_bar(GContext *ctx, int weekday, const GRect *box, - GColor bar_color, bool wide_bar) { - const int bar_inset = 3; - GRect box_inset = grect_inset(*box, GEdgeInsets(bar_inset, bar_inset, 0, bar_inset)); - if (wide_bar) { - prv_draw_day_bar_wide(ctx, box, &box_inset, bar_color); - } else { - prv_draw_day_bar_thin(ctx, box, weekday, bar_color); - } - // The borders of the boxes caused by the inset need to overlap each other - return box->origin.x + box->size.w - bar_inset; -} - -static bool prv_bar_should_be_wide(int draw_weekday, int current_weekday) { - // The graph begins on Monday, so all bars from Monday until current (inclusive) should be wide - return (positive_modulo(draw_weekday - Monday, DAYS_PER_WEEK) <= - positive_modulo(current_weekday - Monday, DAYS_PER_WEEK)); -} - -static GColor prv_get_bar_color(HealthGraphCard *graph_card, bool is_active, bool is_wide) { - const GColor active_color = GColorWhite; - const GColor inactive_wide_color = GColorDarkGray; - const GColor inactive_thin_color = graph_card->inactive_color; - return (is_active ? active_color : (is_wide ? inactive_wide_color : inactive_thin_color)); -} - -static void prv_draw_day_bars(HealthGraphCard *graph_card, GContext *ctx) { - // With values from prv_setup_day_bar_box and prv_draw_day_bar, - // total_bar_width is sum(bar_widths) - (bar_inset * (DAYS_PER_WEEK - 1)) - const int total_bar_widths = PBL_IF_RECT_ELSE(144, 141); - const int legend_line_height = fonts_get_font_height(graph_card->legend_font); - const GRect *bounds = &graph_card->layer.bounds; - GRect box = { .origin.x = (bounds->size.w - total_bar_widths) / 2, .origin.y = LABEL_OFFSET_Y }; - // The first day to draw is Monday, and draw a week's worth of bars - for (int i = Monday, draw_count = 0; - draw_count < DAYS_PER_WEEK; - draw_count++, i = (i + 1) % DAYS_PER_WEEK) { - // Setup the dimensions and color of the day bar - const int32_t day_point = prv_get_day_point(graph_card, i); - const int bar_height = prv_convert_to_graph_height(graph_card, day_point); - - const bool is_active = (graph_card->selection == i); - if (graph_card->current_day == i) { - // Draw last week's bar as a thin bar behind this bar - const int32_t last_bar_height = - prv_convert_to_graph_height(graph_card, graph_card->day_data[DAYS_PER_WEEK]); - prv_setup_day_bar_box(i, &box, last_bar_height); - const GColor bar_color = prv_get_bar_color(graph_card, is_active, false /* wide bar */); - prv_draw_day_bar(ctx, i, &box, bar_color, false /* wide bar */); - } - - // Draw the day bar - prv_setup_day_bar_box(i, &box, bar_height); - const bool is_wide = prv_bar_should_be_wide(i, graph_card->current_day); - const GColor bar_color = prv_get_bar_color(graph_card, is_active, is_wide); - const int16_t next_x = prv_draw_day_bar(ctx, i, &box, bar_color, is_wide); - - // Draw the day character legend - const int char_offset_y = 1; - box.origin.y = LABEL_OFFSET_Y + char_offset_y; - box.size.h = legend_line_height; - char char_buffer[] = { graph_card->day_chars[i], '\0' }; - const GColor active_legend_color = GColorRed; - const GColor inactive_legend_color = GColorBlack; - graphics_context_set_text_color(ctx, is_active ? active_legend_color : inactive_legend_color); - graphics_draw_text(ctx, char_buffer, graph_card->legend_font, box, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - - // Move the box cursor to the next bar - box.origin.x = next_x; - } -} - -static void prv_draw_avg_line(HealthGraphCard *graph_card, GContext *ctx, int32_t avg, - int16_t offset_x, int16_t width) { - if (avg == 0) { - return; - } - const int offset_y = LABEL_OFFSET_Y - MAX(prv_convert_to_graph_height(graph_card, avg), - AVG_LINE_HEIGHT / 2); - graphics_context_set_fill_color(ctx, AVG_LINE_COLOR); - graphics_fill_rect(ctx, &(GRect) {{ offset_x, offset_y - AVG_LINE_HEIGHT / 2 }, - { width, AVG_LINE_HEIGHT }}); -} - -static void prv_draw_avg_lines(HealthGraphCard *graph_card, GContext *ctx) { - const GRect *bounds = &graph_card->layer.bounds; - const int weekday_width = PBL_IF_RECT_ELSE(103, 119); - prv_draw_avg_line(graph_card, ctx, graph_card->stats.weekday.avg, 0, weekday_width); - const int weekend_width = PBL_IF_RECT_ELSE(38, 58); - prv_draw_avg_line(graph_card, ctx, graph_card->stats.weekend.avg, bounds->size.w - weekend_width, - weekend_width); -} - -static int32_t prv_get_info_data_point(HealthGraphCard *graph_card) { - // Show today's data point if the selection is a day of the week, otherwise show the weekday - // average if the current day is a weekday or weekend average if the current day is on the weekend - if (graph_card->selection == HealthGraphIndex_Average) { - return IS_WEEKDAY(graph_card->current_day) ? graph_card->stats.weekday.avg : - graph_card->stats.weekend.avg; - } - int day_point = prv_get_day_point(graph_card, graph_card->selection); - if (graph_card->selection == graph_card->current_day && day_point == 0) { - // If today has no progress, use the info from last week - day_point = graph_card->day_data[DAYS_PER_WEEK]; - } - return day_point; -} - -static void prv_draw_avg_line_legend(HealthGraphCard *graph_card, GContext *ctx, int offset_x, - int info_offset_y, GSize custom_text_size) { - const int info_line_height = fonts_get_font_height(graph_card->legend_font); - const int avg_line_offset_y = -1; - const GRect avg_line_box = { - .origin.x = offset_x, - // Position vertically centered with the text - .origin.y = info_offset_y + (info_line_height + INFO_PADDING_BOTTOM) / 2 + avg_line_offset_y, - .size = { AVG_LINE_LEGEND_WIDTH, AVG_LINE_HEIGHT }, - }; - graphics_context_set_fill_color(ctx, AVG_LINE_COLOR); - graphics_fill_rect(ctx, &avg_line_box); -} - -static void prv_draw_avg_info_text(HealthGraphCard *graph_card, GContext *ctx, int offset_x, - int offset_y, int height) { - const GRect *bounds = &graph_card->layer.bounds; - const GRect avg_text_box = {{ offset_x, offset_y }, { bounds->size.w, height }}; - graphics_draw_text(ctx, graph_card->info_avg, graph_card->legend_font, avg_text_box, - GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); -} - -static void prv_draw_custom_info_text(HealthGraphCard *graph_card, GContext *ctx, char *text, - int offset_x, int info_offset_y, int info_height) { - const GRect *bounds = &graph_card->layer.bounds; - const GRect info_text_box = {{ offset_x, info_offset_y }, { bounds->size.w, info_height }}; - graphics_context_set_text_color(ctx, GColorBlack); - graphics_draw_text(ctx, text, graph_card->legend_font, info_text_box, - GTextOverflowModeWordWrap, GTextAlignmentLeft, NULL); -} - -static bool prv_is_selection_last_weekday(HealthGraphCard *graph_card) { - // If the selection is today, the selection is last week's only if today has no progress - // Else if today is Sunday, the entire graph represents the current week - // Otherwise the selection is last week if either the selection is Sunday - // or if the selection is greater than the current day - return (((int)graph_card->current_day == graph_card->selection && graph_card->day_data[0] == 0) || - ((graph_card->current_day == Sunday) ? false : - ((int)graph_card->selection == Sunday || - (int)graph_card->selection > graph_card->current_day))); -} - -size_t health_graph_format_weekday_prefix(HealthGraphCard *graph_card, char *buffer, - size_t buffer_size) { - if (prv_is_selection_last_weekday(graph_card)) { - // The graph starts on Monday, so wrap around the selection and current_day for Sunday - const time_t selection_time = - ((positive_modulo(graph_card->selection - Monday, DAYS_PER_WEEK) - - positive_modulo(graph_card->current_day - Monday, DAYS_PER_WEEK) - DAYS_PER_WEEK) * - SECONDS_PER_DAY) + graph_card->data_timestamp; - const int pos = clock_get_month_named_abbrev_date(buffer, buffer_size, selection_time); - strncat(buffer, i18n_get(": ", graph_card), buffer_size - pos - 1); - return strlen(buffer); - } else { - struct tm local_tm = (struct tm) { - .tm_wday = positive_modulo(graph_card->selection, DAYS_PER_WEEK), - }; - return strftime(buffer, buffer_size, i18n_get("%a: ", graph_card), &local_tm); - } -} - -static void prv_draw_info_with_text(HealthGraphCard *graph_card, GContext *ctx, char *text) { - const GRect *bounds = &graph_card->layer.bounds; - // Calculate the custom info text size - GSize custom_text_size; - const TextLayoutExtended text_layout = {}; - graphics_text_layout_get_max_used_size(ctx, text, graph_card->legend_font, *bounds, - GTextOverflowModeWordWrap, GTextAlignmentLeft, - (GTextAttributes *)&text_layout); - custom_text_size = text_layout.max_used_size; - GSize avg_text_size = GSizeZero; - int total_width = custom_text_size.w; - - const int info_padding_top = PBL_IF_RECT_ELSE(-1, 1); - const int info_offset_y = LABEL_OFFSET_Y + LABEL_HEIGHT + info_padding_top; - const int info_line_height = fonts_get_font_height(graph_card->legend_font); - const int info_height = PBL_IF_ROUND_ELSE(2, 1) * info_line_height + INFO_PADDING_BOTTOM; - - int cursor_x = 0; - if (graph_card->selection == HealthGraphIndex_Average) { - graphics_text_layout_get_max_used_size(ctx, graph_card->info_avg, graph_card->legend_font, - *bounds, GTextOverflowModeWordWrap, - GTextAlignmentLeft, (GTextAttributes *)&text_layout); - avg_text_size = text_layout.max_used_size; - total_width += avg_text_size.w + AVG_LINE_LEGEND_WIDTH; - - // Draw the avg line legend - cursor_x = (bounds->size.w - total_width) / 2; - prv_draw_avg_line_legend(graph_card, ctx, cursor_x, info_offset_y, custom_text_size); - cursor_x += AVG_LINE_LEGEND_WIDTH; - - // Draw the avg info text - prv_draw_avg_info_text(graph_card, ctx, cursor_x, info_offset_y, info_height); - cursor_x += avg_text_size.w; - } else { - // Center the custom text - cursor_x = (bounds->size.w - total_width) / 2; - } - - // Draw the custom info text - prv_draw_custom_info_text(graph_card, ctx, text, cursor_x, info_offset_y, info_height); -} - -static void prv_draw_info(HealthGraphCard *graph_card, GContext *ctx) { - if (!graph_card->info_buffer_size) { - return; - } - char buffer[graph_card->info_buffer_size]; - memset(buffer, 0, sizeof(buffer)); - if (graph_card->info_update) { - const int32_t day_point = prv_get_info_data_point(graph_card); - graph_card->info_update(graph_card, day_point, buffer, sizeof(buffer)); - } - if (IS_EMPTY_STRING(buffer)) { - return; - } - prv_draw_info_with_text(graph_card, ctx, buffer); -} - -static void prv_health_graph_layer_update_proc(Layer *layer, GContext *ctx) { - HealthGraphCard *graph_card = (HealthGraphCard *)layer; - - prv_draw_title(graph_card, ctx); - prv_draw_day_labels_background(graph_card, ctx); - prv_draw_day_bars(graph_card, ctx); - prv_draw_avg_lines(graph_card, ctx); - prv_draw_info(graph_card, ctx); -} - -HealthGraphCard *health_graph_card_create(const HealthGraphCardConfig *config) { - HealthGraphCard *graph_card = app_zalloc_check(sizeof(HealthGraphCard)); - if (graph_card) { - layer_init(&graph_card->layer, &GRectZero); - layer_set_update_proc(&graph_card->layer, prv_health_graph_layer_update_proc); - health_graph_card_configure(graph_card, config); - graph_card->title_font = fonts_get_system_font(PBL_IF_RECT_ELSE(FONT_KEY_GOTHIC_24_BOLD, - FONT_KEY_GOTHIC_18_BOLD)); - graph_card->legend_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - graph_card->current_day = prv_get_weekday(graph_card->data_timestamp); - // The day characters in standard tm weekday order - graph_card->day_chars = i18n_get("SMTWTFS", graph_card); - graph_card->selection = HealthGraphIndex_Average; - } - return graph_card; -} - -void health_graph_card_destroy(HealthGraphCard *graph_card) { - if (!graph_card) { - return; - } - layer_deinit(&graph_card->layer); - i18n_free_all(graph_card); - app_free(graph_card); -} - -void health_graph_card_configure(HealthGraphCard *graph_card, const HealthGraphCardConfig *config) { - if (!graph_card || !config) { - return; - } - if (config->title) { - graph_card->title = i18n_get(config->title, graph_card); - } - if (config->info_avg) { - graph_card->info_avg = i18n_get(config->info_avg, graph_card); - } - if (config->graph_data) { - graph_card->stats = config->graph_data->stats; - memcpy(graph_card->day_data, config->graph_data->day_data, sizeof(graph_card->day_data)); - graph_card->data_timestamp = config->graph_data->timestamp; - graph_card->data_max = MAX(config->graph_data->default_max, - config->graph_data->stats.daily.max); - } - if (config->info_update) { - graph_card->info_update = config->info_update; - } - if (config->info_buffer_size) { - graph_card->info_buffer_size = config->info_buffer_size; - } - if (!gcolor_equal(config->inactive_color, GColorClear)) { - graph_card->inactive_color = config->inactive_color; - } -} - -void health_graph_card_cycle_selected(HealthGraphCard *graph_card) { - if (graph_card->selection == HealthGraphIndex_Sunday) { - // Sunday is the last day in the graph, show the average next - graph_card->selection = HealthGraphIndex_Average; - } else if (graph_card->selection == HealthGraphIndex_Average) { - // Monday is the first day in the graph, show Monday after showing the average - graph_card->selection = HealthGraphIndex_Monday; - } else { - // Otherwise progress through the weekdays normally - graph_card->selection = (graph_card->selection + 1) % DAYS_PER_WEEK; - } -} diff --git a/src/fw/apps/system_apps/health/health_graph_card.h b/src/fw/apps/system_apps/health/health_graph_card.h deleted file mode 100644 index 37f2ee63ee..0000000000 --- a/src/fw/apps/system_apps/health/health_graph_card.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/fonts/fonts.h" -#include "applib/ui/layer.h" -#include "util/time/time.h" - -typedef enum { - HealthGraphIndex_Sunday = Sunday, - HealthGraphIndex_Monday = Monday, - HealthGraphIndex_Saturday = Saturday, - HealthGraphIndex_Average = HealthGraphIndex_Sunday + DAYS_PER_WEEK, - HealthGraphIndexCount, -} HealthGraphIndex; - -typedef struct HealthGraphCard HealthGraphCard; - -typedef void (*HealthGraphCardInfoUpdate)(HealthGraphCard *graph_card, int32_t day_point, - char *buffer, size_t buffer_size); - -typedef struct { - WeeklyStats stats; - time_t timestamp; - int32_t *day_data; - int32_t default_max; -} HealthGraphCardData; - -typedef struct { - const char *title; - const char *info_avg; - const HealthGraphCardData *graph_data; - HealthGraphCardInfoUpdate info_update; - size_t info_buffer_size; - const GColor inactive_color; -} HealthGraphCardConfig; - -struct HealthGraphCard { - Layer layer; - - WeeklyStats stats; - //! Today is 0. Save up to and including last week's day of the same week day - int32_t day_data[DAYS_PER_WEEK + 1]; - time_t data_timestamp; //!< Time at which the data applies in UTC seconds - int32_t data_max; - - GFont title_font; - GFont legend_font; - const char *day_chars; - const char *title; - const char *info_avg; - GColor inactive_color; - - HealthGraphCardInfoUpdate info_update; - size_t info_buffer_size; - - uint8_t current_day; //!< Current weekday (weekend inclusive) where Sunday is first at 0 - HealthGraphIndex selection; -}; - -//! Creates a HealthGraphCard -HealthGraphCard *health_graph_card_create(const HealthGraphCardConfig *config); - -//! Destroys a HealthGraphCard -void health_graph_card_destroy(HealthGraphCard *graph_card); - -//! Configures a HealthGraphCard -void health_graph_card_configure(HealthGraphCard *graph_card, const HealthGraphCardConfig *config); - -//! Cycles the HealthGraphCard selection -void health_graph_card_cycle_selected(HealthGraphCard *graph_card); - -//! Formats a string with a prefix of the current weekday selection -size_t health_graph_format_weekday_prefix(HealthGraphCard *graph_card, char *buffer, - size_t buffer_size); diff --git a/src/fw/apps/system_apps/health/health_hr_detail_card.c b/src/fw/apps/system_apps/health/health_hr_detail_card.c deleted file mode 100644 index ee39c98814..0000000000 --- a/src/fw/apps/system_apps/health/health_hr_detail_card.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_hr_detail_card.h" -#include "health_detail_card.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/health_util.h" - -#include - -typedef struct HealthHrDetailCard { - int16_t num_headings; - HealthDetailHeading headings[MAX_NUM_HEADINGS]; - - int16_t num_zones; - HealthDetailZone zones[MAX_NUM_ZONES]; -} HealthHrDetailCardData; - -#define DEFAULT_MAX_PROGRESS (10 * SECONDS_PER_MINUTE) - -static void prv_set_zone(HealthDetailZone *zone, int32_t minutes, int32_t *max_progress, - const size_t buffer_size, const char *zone_label, void *i18n_owner) { - *zone = (HealthDetailZone) { - .label = app_zalloc_check(buffer_size), - .progress = minutes * SECONDS_PER_MINUTE, - .fill_color = PBL_IF_COLOR_ELSE(GColorSunsetOrange, GColorDarkGray), - }; - - int pos = snprintf(zone->label, buffer_size, "%s ", i18n_get(zone_label, i18n_owner)); - if (zone->progress) { - health_util_format_hours_and_minutes(zone->label + pos, buffer_size - pos, - zone->progress, i18n_owner); - } - - if (zone->progress > *max_progress) { - *max_progress = zone->progress; - } -} - -static void prv_set_heading_value(char *buffer, const size_t buffer_size, const int32_t zone_time_s, - void *i18n_owner) { - if (zone_time_s == 0) { - strncpy(buffer, EN_DASH, buffer_size); - return; - } - - health_util_format_hours_and_minutes(buffer, buffer_size, zone_time_s, i18n_owner); -} - -Window *health_hr_detail_card_create(HealthData *health_data) { - HealthHrDetailCardData *card_data = app_zalloc_check(sizeof(HealthHrDetailCardData)); - - const int32_t zone1_minutes = health_data_hr_get_zone1_minutes(health_data); - const int32_t zone2_minutes = health_data_hr_get_zone2_minutes(health_data); - const int32_t zone3_minutes = health_data_hr_get_zone3_minutes(health_data); - const int32_t zone_time_minutes = zone1_minutes + zone2_minutes + zone3_minutes; - - int32_t max_progress = DEFAULT_MAX_PROGRESS; - - const size_t buffer_size = 32; - - prv_set_zone(&card_data->zones[card_data->num_zones++], zone1_minutes, &max_progress, buffer_size, - i18n_noop("Fat Burn"), card_data); - - prv_set_zone(&card_data->zones[card_data->num_zones++], zone2_minutes, &max_progress, buffer_size, - i18n_noop("Endurance"), card_data); - - prv_set_zone(&card_data->zones[card_data->num_zones++], zone3_minutes, &max_progress, buffer_size, - i18n_noop("Performance"), card_data); - - HealthDetailHeading *heading = &card_data->headings[card_data->num_headings++]; - - *heading = (HealthDetailHeading) { - /// Resting HR - .primary_label = (char *)i18n_get("TIME IN ZONES", card_data), - .primary_value = app_zalloc_check(buffer_size), - .fill_color = GColorWhite, - .outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack), - }; - - prv_set_heading_value(heading->primary_value, buffer_size, - (zone_time_minutes * SECONDS_PER_MINUTE), card_data); - - const HealthDetailCardConfig config = { - .num_headings = card_data->num_headings, - .headings = card_data->headings, - .weekly_max = max_progress, - .bg_color = PBL_IF_COLOR_ELSE(GColorBulgarianRose, GColorWhite), - .num_zones = card_data->num_zones, - .zones = card_data->zones, - .data = card_data, - }; - - return (Window *)health_detail_card_create(&config); -} - -void health_hr_detail_card_destroy(Window *window) { - HealthDetailCard *card = (HealthDetailCard *)window; - HealthHrDetailCardData *card_data = card->data; - for (int i = 0; i < card_data->num_headings; i++) { - app_free(card_data->headings[i].primary_value); - } - for (int i = 0; i < card_data->num_zones; i++) { - app_free(card_data->zones[i].label); - } - i18n_free_all(card_data); - app_free(card_data); - health_detail_card_destroy(card); -} diff --git a/src/fw/apps/system_apps/health/health_hr_detail_card.h b/src/fw/apps/system_apps/health/health_hr_detail_card.h deleted file mode 100644 index 9f7b0e203a..0000000000 --- a/src/fw/apps/system_apps/health/health_hr_detail_card.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - -//! Creates a health hr detail window -//! @param HealthData pointer to the health data to be given to this card -//! @return A pointer to a newly allocated health hr detail window -Window *health_hr_detail_card_create(HealthData *health_data); - -//! Destroys a health hr detail window -//! @param window Window pointer to health hr detail window -void health_hr_detail_card_destroy(Window *window); diff --git a/src/fw/apps/system_apps/health/health_hr_summary_card.c b/src/fw/apps/system_apps/health/health_hr_summary_card.c deleted file mode 100644 index cf44ec64dd..0000000000 --- a/src/fw/apps/system_apps/health/health_hr_summary_card.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_hr_summary_card.h" -#include "health_hr_summary_card_segments.h" -#include "health_hr_detail_card.h" -#include "health_progress.h" -#include "services/normal/activity/health_util.h" - -#include "applib/pbl_std/pbl_std.h" -#include "applib/ui/kino/kino_reel.h" -#include "applib/ui/text_layer.h" -#include "apps/system_apps/timeline/text_node.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" -#include "util/size.h" -#include "util/string.h" - -typedef struct HealthHrSummaryCardData { - HealthData *health_data; - HealthProgressBar progress_bar; - GDrawCommandSequence *pulsing_heart; - uint32_t pulsing_heart_frame_index; - AppTimer *pulsing_heart_timer; - uint32_t num_heart_beats; - uint32_t now_bpm; - uint32_t resting_bpm; - time_t last_updated; - GFont bpm_font; - GFont timestamp_font; - GFont units_font; -} HealthHrSummaryCardData; - -#define PULSING_HEART_TIMEOUT (30 * MS_PER_SECOND) - -#define PROGRESS_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorRoseVale, GColorBlack)) -#define PROGRESS_OUTLINE_COLOR (PBL_IF_COLOR_ELSE(GColorClear, GColorBlack)) - -#define TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorSunsetOrange, GColorBlack)) -#define CARD_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorBulgarianRose, GColorWhite)) - - -static void prv_pulsing_heart_timer_cb(void *context) { - Layer *base_layer = context; - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - const uint32_t duration = gdraw_command_sequence_get_total_duration(data->pulsing_heart); - const uint32_t num_frames = gdraw_command_sequence_get_num_frames(data->pulsing_heart); - const uint32_t timer_duration = duration / num_frames; - const uint32_t max_heart_beats = PULSING_HEART_TIMEOUT / duration; - - data->pulsing_heart_frame_index++; - - if (data->pulsing_heart_frame_index >= num_frames) { - data->pulsing_heart_frame_index = 0; - data->num_heart_beats++; - } - - if (data->num_heart_beats < max_heart_beats) { - data->pulsing_heart_timer = app_timer_register(timer_duration, - prv_pulsing_heart_timer_cb, - base_layer); - } - - layer_mark_dirty(base_layer); -} - -static void prv_render_progress_bar(GContext *ctx, Layer *base_layer) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_BACKGROUND_COLOR, - 0, HEALTH_PROGRESS_BAR_MAX_VALUE); -} - -static void prv_render_icon(GContext *ctx, Layer *base_layer) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - GDrawCommandFrame *frame = gdraw_command_sequence_get_frame_by_index( - data->pulsing_heart, data->pulsing_heart_frame_index); - if (frame) { - const GPoint offset = GPoint(-1, -23); - gdraw_command_frame_draw(ctx, data->pulsing_heart, frame, offset); - return; - } -} - -static void prv_render_bpm(GContext *ctx, Layer *base_layer) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - const int units_offset_y = - fonts_get_font_height(data->bpm_font) - fonts_get_font_height(data->units_font); - - GTextNodeHorizontal *horiz_container = graphics_text_node_create_horizontal(MAX_TEXT_NODES); - GTextNodeContainer *container = &horiz_container->container; - horiz_container->horizontal_alignment = GTextAlignmentCenter; - - if (data->now_bpm == 0) { - health_util_create_text_node_with_text( - EM_DASH, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), TEXT_COLOR, container); - } else { - const size_t bpm_size = sizeof("000"); - - GTextNodeText *number_text_node = - health_util_create_text_node(bpm_size, data->bpm_font, TEXT_COLOR, container); - snprintf((char *)number_text_node->text, bpm_size, "%"PRIu32, data->now_bpm); - - GTextNodeText *units_text_node = health_util_create_text_node_with_text( - i18n_get("BPM", base_layer), data->units_font, TEXT_COLOR, container); - units_text_node->node.offset.x += 2; - units_text_node->node.offset.y = units_offset_y; - } - - const int offset_y = PBL_IF_RECT_ELSE(101, 109); - - graphics_text_node_draw(&container->node, ctx, - &GRect(0, offset_y, base_layer->bounds.size.w, - fonts_get_font_height(data->bpm_font)), NULL, NULL); - graphics_text_node_destroy(&container->node); -} - -static void prv_render_timstamp(GContext *ctx, Layer *base_layer) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - if (data->last_updated <= 0 || data->now_bpm == 0) { - return; - } - - const size_t buffer_size = 32; - char buffer[buffer_size]; - - clock_get_until_time_without_fulltime(buffer, buffer_size, data->last_updated, HOURS_PER_DAY); - - const int y = PBL_IF_RECT_ELSE(130, 136); - GRect rect = GRect(0, y, base_layer->bounds.size.w, 35); -#if PBL_RECT - rect = grect_inset(rect, GEdgeInsets(0, 18)); -#endif - - graphics_context_set_text_color(ctx, TEXT_COLOR); - graphics_draw_text(ctx, buffer, data->timestamp_font, - rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static void prv_render_hrm_disabled(GContext *ctx, Layer *base_layer) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - const int y = PBL_IF_RECT_ELSE(100, 109); - - GRect rect = GRect(0, y, base_layer->bounds.size.w, 52); - - /// HRM disabled - const char *text = i18n_get("Enable heart rate monitoring in the mobile app", base_layer); - - graphics_context_set_text_color(ctx, TEXT_COLOR); - graphics_draw_text(ctx, text, data->timestamp_font, - rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static void prv_base_layer_update_proc(Layer *base_layer, GContext *ctx) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - - data->now_bpm = health_data_hr_get_current_bpm(data->health_data); - data->last_updated = health_data_hr_get_last_updated_timestamp(data->health_data); - - prv_render_icon(ctx, base_layer); - - prv_render_progress_bar(ctx, base_layer); - - if (!activity_prefs_heart_rate_is_enabled()) { - prv_render_hrm_disabled(ctx, base_layer); - return; - } - - prv_render_bpm(ctx, base_layer); - - prv_render_timstamp(ctx, base_layer); -} - -static void prv_hr_detail_card_unload_callback(Window *window) { - health_hr_detail_card_destroy(window); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -Layer *health_hr_summary_card_create(HealthData *health_data) { - // create base layer - Layer *base_layer = layer_create_with_data(GRectZero, sizeof(HealthHrSummaryCardData)); - HealthHrSummaryCardData *data = layer_get_data(base_layer); - layer_set_update_proc(base_layer, prv_base_layer_update_proc); - // set health data - *data = (HealthHrSummaryCardData) { - .health_data = health_data, - .pulsing_heart = - gdraw_command_sequence_create_with_resource(RESOURCE_ID_HEALTH_APP_PULSING_HEART), - .progress_bar = { - .num_segments = ARRAY_LENGTH(s_hr_summary_progress_segments), - .segments = s_hr_summary_progress_segments, - }, - .now_bpm = health_data_hr_get_current_bpm(health_data), - .resting_bpm = health_data_hr_get_resting_bpm(health_data), - .last_updated = health_data_hr_get_last_updated_timestamp(health_data), - .bpm_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM), - .timestamp_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), - .units_font = fonts_get_system_font(FONT_KEY_LECO_20_BOLD_NUMBERS), - }; - - data->pulsing_heart_timer = app_timer_register(0, prv_pulsing_heart_timer_cb, base_layer); - - return base_layer; -} - -void health_hr_summary_card_select_click_handler(Layer *layer) { - HealthHrSummaryCardData *data = layer_get_data(layer); - HealthData *health_data = data->health_data; - Window *window = health_hr_detail_card_create(health_data); - window_set_window_handlers(window, &(WindowHandlers) { - .unload = prv_hr_detail_card_unload_callback, - }); - app_window_stack_push(window, true); -} - -void health_hr_summary_card_destroy(Layer *base_layer) { - HealthHrSummaryCardData *data = layer_get_data(base_layer); - app_timer_cancel(data->pulsing_heart_timer); - gdraw_command_sequence_destroy(data->pulsing_heart); - i18n_free_all(base_layer); - layer_destroy(base_layer); -} - -GColor health_hr_summary_card_get_bg_color(Layer *layer) { - return CARD_BACKGROUND_COLOR; -} - -bool health_hr_summary_show_select_indicator(Layer *layer) { - return true; -} diff --git a/src/fw/apps/system_apps/health/health_hr_summary_card.h b/src/fw/apps/system_apps/health/health_hr_summary_card.h deleted file mode 100644 index d17b92b7ba..0000000000 --- a/src/fw/apps/system_apps/health/health_hr_summary_card.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -//! Creates hr summary card with data -//! @param health_data A pointer to the health data being given this card -//! @return A pointer to a newly allocated layer, which contains its own data -Layer *health_hr_summary_card_create(HealthData *health_data); - -//! Health hr summary select click handler -//! @param layer A pointer to an existing layer containing its own data -void health_hr_summary_card_select_click_handler(Layer *layer); - -//! Destroy hr summary card -//! @param base_layer A pointer to an existing layer containing its own data -void health_hr_summary_card_destroy(Layer *base_layer); - -//! Health hr summary layer background color getter -GColor health_hr_summary_card_get_bg_color(Layer *layer); - -//! Health hr summary layer should show select click indicator -bool health_hr_summary_show_select_indicator(Layer *layer); diff --git a/src/fw/apps/system_apps/health/health_hr_summary_card_segments.h b/src/fw/apps/system_apps/health/health_hr_summary_card_segments.h deleted file mode 100644 index 90dbd10c76..0000000000 --- a/src/fw/apps/system_apps/health/health_hr_summary_card_segments.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_progress.h" - -//! 4 main segments + 4 real corners -//! Each of the 4 non-corener segments get 25% of the total -#define AMOUNT_PER_SEGMENT (HEALTH_PROGRESS_BAR_MAX_VALUE * 25 / 100) - -// The shape is the same, but the offsets are different -// Slightly adjust the points on Round -#define X_SHIFT (PBL_IF_ROUND_ELSE(18, 0)) -#define Y_SHIFT (PBL_IF_ROUND_ELSE(6, 0)) - -static HealthProgressSegment s_hr_summary_progress_segments[] = { - { - // Bottom corner - .type = HealthProgressSegmentType_Corner, - .points = {{71 + X_SHIFT, 88 + Y_SHIFT}, {64 + X_SHIFT, 94 + Y_SHIFT}, - {72 + X_SHIFT, 101 + Y_SHIFT}, {80 + X_SHIFT, 93 + Y_SHIFT}}, - }, - { - // Left side bottom - .amount_of_total = AMOUNT_PER_SEGMENT, - .type = HealthProgressSegmentType_Vertical, - .points = {{65 + X_SHIFT, 95 + Y_SHIFT}, {72 + X_SHIFT, 89 + Y_SHIFT}, - {42 + X_SHIFT, 58 + Y_SHIFT}, {35 + X_SHIFT, 65 + Y_SHIFT}}, - }, - { - // Left corner - .type = HealthProgressSegmentType_Corner, - .points = {{43 + X_SHIFT, 58 + Y_SHIFT}, {36 + X_SHIFT, 50 + Y_SHIFT}, - {29 + X_SHIFT, 58 + Y_SHIFT}, {36 + X_SHIFT, 66 + Y_SHIFT}}, - }, - { - // Left side top - .amount_of_total = AMOUNT_PER_SEGMENT, - .type = HealthProgressSegmentType_Vertical, - .points = {{36 + X_SHIFT, 51 + Y_SHIFT}, {44 + X_SHIFT, 58 + Y_SHIFT}, - {72 + X_SHIFT, 29 + Y_SHIFT}, {65 + X_SHIFT, 22 + Y_SHIFT}}, - }, - { - // Top corner - .type = HealthProgressSegmentType_Corner, - .points = {{71 + X_SHIFT, 30 + Y_SHIFT}, {79 + X_SHIFT, 23 + Y_SHIFT}, - {71 + X_SHIFT, 16 + Y_SHIFT}, {65 + X_SHIFT, 22 + Y_SHIFT}}, - }, - { - // Right side top - .amount_of_total = AMOUNT_PER_SEGMENT, - .type = HealthProgressSegmentType_Vertical, - .points = {{78 + X_SHIFT, 22 + Y_SHIFT}, {71 + X_SHIFT, 28 + Y_SHIFT}, - {102 + X_SHIFT, 60 + Y_SHIFT}, {108 + X_SHIFT, 53 + Y_SHIFT}}, - }, - { - // Right corner - .type = HealthProgressSegmentType_Corner, - .points = {{100 + X_SHIFT, 56 + Y_SHIFT}, {108 + X_SHIFT, 66 + Y_SHIFT}, - {114 + X_SHIFT, 59 + Y_SHIFT}, {106 + X_SHIFT, 50 + Y_SHIFT}}, - }, - { - // Right side bottom - .amount_of_total = AMOUNT_PER_SEGMENT, - .type = HealthProgressSegmentType_Vertical, - .points = {{102 + X_SHIFT, 57 + Y_SHIFT}, {108 + X_SHIFT, 64 + Y_SHIFT}, - {78 + X_SHIFT, 95 + Y_SHIFT}, {71 + X_SHIFT, 89 + Y_SHIFT}}, - }, -}; diff --git a/src/fw/apps/system_apps/health/health_progress.c b/src/fw/apps/system_apps/health/health_progress.c deleted file mode 100644 index b48e8a565c..0000000000 --- a/src/fw/apps/system_apps/health/health_progress.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_progress.h" - -#include "applib/graphics/gpath_builder.h" - -#include "system/logging.h" - -// Scales a total shape offset to an individual segment offset. -// @param total_offset should not be larger than the segment's percent of total -static int prv_total_offset_to_segment_offset(HealthProgressSegment *segment, - int total_offset) { - return total_offset * HEALTH_PROGRESS_BAR_MAX_VALUE / segment->amount_of_total; -} - -static bool prv_is_segment_corner(HealthProgressSegment *segment) { - return (segment->type == HealthProgressSegmentType_Corner); -} - -static GPointPrecise prv_get_adjusted_gpoint_precise_from_gpoint(GPoint point) { - GPointPrecise pointP = GPointPreciseFromGPoint(point); - // Hack to make it draw 2px lines on b/w - // Note that this shifts it down and to the right, but it works for us. - pointP.x.fraction += FIXED_S16_3_HALF.raw_value; - pointP.y.fraction += FIXED_S16_3_HALF.raw_value; - return pointP; -} - -static GPoint prv_get_point_between_points(GPoint p1, GPoint p2, HealthProgressBarValue val) { - int x = p1.x + ((p2.x - p1.x) * val / HEALTH_PROGRESS_BAR_MAX_VALUE); - int y = p1.y + ((p2.y - p1.y) * val / HEALTH_PROGRESS_BAR_MAX_VALUE); - - return GPoint(x, y); -} - -static void prv_fill_segment(GContext *ctx, HealthProgressSegment *segment, GColor color, - HealthProgressBarValue start, HealthProgressBarValue end) { - GPoint p1, p2, p3, p4; - if (segment->type == HealthProgressSegmentType_Vertical) { - p1 = prv_get_point_between_points(segment->points[0], segment->points[3], start); - p2 = prv_get_point_between_points(segment->points[1], segment->points[2], start); - p3 = prv_get_point_between_points(segment->points[1], segment->points[2], end); - p4 = prv_get_point_between_points(segment->points[0], segment->points[3], end); - } else if (segment->type == HealthProgressSegmentType_Horizontal) { - p1 = prv_get_point_between_points(segment->points[0], segment->points[1], start); - p2 = prv_get_point_between_points(segment->points[3], segment->points[2], start); - p3 = prv_get_point_between_points(segment->points[3], segment->points[2], end); - p4 = prv_get_point_between_points(segment->points[0], segment->points[1], end); - } else { - p1 = segment->points[0]; - p2 = segment->points[1]; - p3 = segment->points[2]; - p4 = segment->points[3]; - } - - GPathBuilder *builder = gpath_builder_create(5); - gpath_builder_move_to_point(builder, p1); - gpath_builder_line_to_point(builder, p2); - gpath_builder_line_to_point(builder, p3); - gpath_builder_line_to_point(builder, p4); - GPath *path = gpath_builder_create_path(builder); - gpath_builder_destroy(builder); - - graphics_context_set_fill_color(ctx, color); - gpath_draw_filled(ctx, path); - gpath_destroy(path); -} - -void health_progress_bar_fill(GContext *ctx, HealthProgressBar *progress_bar, GColor color, - HealthProgressBarValue start, HealthProgressBarValue end) { - if (start < 0) { - // This ensures we don't deal with negative values - start += HEALTH_PROGRESS_BAR_MAX_VALUE; - } - if (start > end) { - // This ensures the end is always after the start - end += HEALTH_PROGRESS_BAR_MAX_VALUE; - } - - int amount_traversed = 0; - HealthProgressSegment *segment = progress_bar->segments; - while (start >= amount_traversed + segment->amount_of_total) { - // Skip until the segment which includes the start - amount_traversed += segment->amount_of_total; - segment++; - } - - if (prv_is_segment_corner(segment)) { - segment++; - } - - while (amount_traversed < end) { - if (prv_is_segment_corner(segment)) { - // Fully fill corner segments for now - prv_fill_segment(ctx, segment, color, 0, HEALTH_PROGRESS_BAR_MAX_VALUE); - segment++; - continue; - } - - const int from_total = MAX(start, amount_traversed) - amount_traversed; - const int to_total = MIN(end, amount_traversed + segment->amount_of_total) - amount_traversed; - - const int from = prv_total_offset_to_segment_offset(segment, from_total); - const int to = prv_total_offset_to_segment_offset(segment, to_total); - - prv_fill_segment(ctx, segment, color, from, to); - - amount_traversed += segment->amount_of_total; - - if (segment == &progress_bar->segments[progress_bar->num_segments - 1]) { - // We are on the last segment, wrap back to the first - segment = &progress_bar->segments[0]; - } else { - segment++; - } - } -} - -void health_progress_bar_mark(GContext *ctx, HealthProgressBar *progress_bar, GColor color, - HealthProgressBarValue value_to_mark) { - if (value_to_mark < 0) { - // This ensures we don't deal with negative values - value_to_mark += HEALTH_PROGRESS_BAR_MAX_VALUE; - } - - HealthProgressSegment *segment = progress_bar->segments; - while (value_to_mark > segment->amount_of_total) { - value_to_mark -= segment->amount_of_total; - segment++; - } - - if (prv_is_segment_corner(segment)) { - segment++; - } - - const int from = prv_total_offset_to_segment_offset(segment, value_to_mark); - - // Fill backwards if we can, otherwise forwards - const int dir = value_to_mark - segment->mark_width < 0 ? 1 : -1; - const int to_total = value_to_mark + (dir * segment->mark_width); - const int to = prv_total_offset_to_segment_offset(segment, to_total); - - prv_fill_segment(ctx, segment, color, from, to); -} - -void health_progress_bar_outline(GContext *ctx, HealthProgressBar *progress_bar, GColor color) { - graphics_context_set_stroke_color(ctx, color); - graphics_context_set_stroke_width(ctx, 2); - - for (int i = 0; i < progress_bar->num_segments; i++) { - HealthProgressSegment *segment = &progress_bar->segments[i]; - - GPointPrecise p0 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[0]); - GPointPrecise p1 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[1]); - GPointPrecise p2 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[2]); - GPointPrecise p3 = prv_get_adjusted_gpoint_precise_from_gpoint(segment->points[3]); - - if (segment->type == HealthProgressSegmentType_Vertical) { - graphics_line_draw_precise_stroked(ctx, p0, p3); - graphics_line_draw_precise_stroked(ctx, p1, p2); - } else if (segment->type == HealthProgressSegmentType_Horizontal) { - graphics_line_draw_precise_stroked(ctx, p0, p1); - graphics_line_draw_precise_stroked(ctx, p2, p3); - } else { - graphics_line_draw_precise_stroked(ctx, p1, p2); - graphics_line_draw_precise_stroked(ctx, p2, p3); - } - } -} diff --git a/src/fw/apps/system_apps/health/health_progress.h b/src/fw/apps/system_apps/health/health_progress.h deleted file mode 100644 index 0c6c7ec581..0000000000 --- a/src/fw/apps/system_apps/health/health_progress.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/ui.h" - -#define HEALTH_PROGRESS_BAR_MAX_VALUE 0xfff - -typedef int32_t HealthProgressBarValue; - -typedef enum HealthProgressSegmentType { - HealthProgressSegmentType_Horizontal, - HealthProgressSegmentType_Vertical, - HealthProgressSegmentType_Corner, - HealthProgressSegmentTypeCount, -} HealthProgressSegmentType; - -typedef struct HealthProgressSegment { - HealthProgressSegmentType type; - // The amount of the total progress bar that this segment occupies. - // Summing this value over all segments should total HEALTH_PROGRESS_BAR_MAX_VALUE - int amount_of_total; - int mark_width; - GPoint points[4]; -} HealthProgressSegment; - -typedef struct HealthProgressBar { - int num_segments; - HealthProgressSegment *segments; -} HealthProgressBar; - - -void health_progress_bar_fill(GContext *ctx, HealthProgressBar *progress_bar, GColor color, - HealthProgressBarValue start, HealthProgressBarValue end); - -void health_progress_bar_mark(GContext *ctx, HealthProgressBar *progress_bar, GColor color, - HealthProgressBarValue value_to_mark); - -void health_progress_bar_outline(GContext *ctx, HealthProgressBar *progress_bar, GColor color); diff --git a/src/fw/apps/system_apps/health/health_sleep_detail_card.c b/src/fw/apps/system_apps/health/health_sleep_detail_card.c deleted file mode 100644 index 393f38088e..0000000000 --- a/src/fw/apps/system_apps/health/health_sleep_detail_card.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_sleep_detail_card.h" -#include "health_detail_card.h" -#include "services/normal/activity/health_util.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" - -#include - -typedef struct HealthSleepDetailCard { - int32_t daily_avg; - int32_t weekly_max; - - int16_t num_headings; - HealthDetailHeading headings[MAX_NUM_HEADINGS]; - - int16_t num_subtitles; - HealthDetailSubtitle subtitles[MAX_NUM_SUBTITLES]; - - int16_t num_zones; - HealthDetailZone zones[MAX_NUM_ZONES]; -} HealthSleepDetailCardData; - -static void prv_set_sleep_session(char *buffer, size_t buffer_size, int32_t sleep_start, - int32_t sleep_end) { - // We don't have a sleep session if either start or end time is not greater than 0. - // Sometimes if there's no sleep, sleep session rendered as "16:00 - 16:00" so for a quick - // fix, we assume there's no sleep if the start and end times are the same. - // https://pebbletechnology.atlassian.net/browse/PBL-40031 - if (sleep_start <= 0 || sleep_end <= 0 || sleep_start == sleep_end) { - strncpy(buffer, EN_DASH, buffer_size); - return; - } - - const int start_hours = sleep_start / SECONDS_PER_HOUR; - const int start_minutes = (sleep_start % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE; - const int end_hours = sleep_end / SECONDS_PER_HOUR; - const int end_minutes = (sleep_end % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE; - - int pos = 0; - - pos += clock_format_time(buffer + pos, buffer_size - pos, - start_hours, start_minutes, false); - - pos += snprintf(buffer + pos, buffer_size - pos, " %s ", "-"); - - pos += clock_format_time(buffer + pos, buffer_size - pos, - end_hours, end_minutes, false); -} - -static void prv_set_deep_sleep(char *buffer, size_t buffer_size, int32_t sleep_duration, - void *i18n_owner) { - if (sleep_duration <= 0) { - strncpy(buffer, EN_DASH, buffer_size); - return; - } - - health_util_format_hours_and_minutes(buffer, buffer_size, sleep_duration, i18n_owner); -} - -static void prv_set_avg(char *buffer, size_t buffer_size, int32_t daily_avg, void *i18n_owner) { -#if PBL_ROUND - int avg_len = snprintf(buffer, buffer_size, "%s\n", i18n_get("30 DAY AVG", i18n_owner)); -#else - int avg_len = snprintf(buffer, buffer_size, "%s ", i18n_get("30 DAY", i18n_owner)); -#endif - - if (daily_avg <= 0) { - strncpy(buffer + avg_len, EN_DASH, buffer_size - avg_len); - } else { - health_util_format_hours_and_minutes(buffer + avg_len, buffer_size - avg_len, - daily_avg, i18n_owner); - } -} - -Window *health_sleep_detail_card_create(HealthData *health_data) { - HealthSleepDetailCardData *card_data = app_zalloc_check(sizeof(HealthSleepDetailCardData)); - - card_data->daily_avg = health_data_sleep_get_monthly_average(health_data); - - const GColor fill_color = PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorDarkGray); - const GColor today_fill_color = PBL_IF_COLOR_ELSE(GColorElectricBlue, GColorDarkGray); - - health_detail_card_set_render_day_zones(card_data->zones, - &card_data->num_zones, - &card_data->weekly_max, - true /* format hours and minutes */, - false /* show crown */, - fill_color, - today_fill_color, - health_data_sleep_get(health_data), - card_data); - - const size_t buffer_len = 32; - - HealthDetailHeading *heading = &card_data->headings[card_data->num_headings++]; - - *heading = (HealthDetailHeading) { - .primary_label = (char *)i18n_get("SLEEP SESSION", card_data), - .primary_value = app_zalloc_check(buffer_len), - .fill_color = GColorWhite, - .outline_color = PBL_IF_COLOR_ELSE(GColorClear, GColorBlack), - }; - - prv_set_sleep_session(heading->primary_value, buffer_len, - health_data_sleep_get_start_time(health_data), - health_data_sleep_get_end_time(health_data)); - - heading = &card_data->headings[card_data->num_headings++]; - - *heading = (HealthDetailHeading) { - .primary_label = (char *)i18n_get("DEEP SLEEP", card_data), - .primary_value = app_zalloc_check(buffer_len), - .fill_color = PBL_IF_COLOR_ELSE(GColorCobaltBlue, GColorWhite), -#if PBL_BW - .outline_color = GColorBlack, -#endif - }; - - prv_set_deep_sleep(heading->primary_value, buffer_len, - health_data_current_deep_sleep_get(health_data), card_data); - - HealthDetailSubtitle *subtitle = &card_data->subtitles[card_data->num_subtitles++]; - - *subtitle = (HealthDetailSubtitle) { - .label = app_zalloc_check(buffer_len), - .fill_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack), - }; - - prv_set_avg(subtitle->label, buffer_len, card_data->daily_avg, card_data); - - const HealthDetailCardConfig config = { - .num_headings = card_data->num_headings, - .headings = card_data->headings, - .num_subtitles = card_data->num_subtitles, - .subtitles = card_data->subtitles, - .daily_avg = card_data->daily_avg, - .weekly_max = card_data->weekly_max, - .bg_color = PBL_IF_COLOR_ELSE(GColorOxfordBlue, GColorWhite), - .num_zones = card_data->num_zones, - .zones = card_data->zones, - .data = card_data, - }; - - return (Window *)health_detail_card_create(&config); -} - -void health_sleep_detail_card_destroy(Window *window) { - HealthDetailCard *card = (HealthDetailCard *)window; - HealthSleepDetailCardData *card_data = card->data; - for (int i = 0; i < card_data->num_headings; i++) { - app_free(card_data->headings[i].primary_value); - app_free(card_data->headings[i].secondary_value); - } - for (int i = 0; i < card_data->num_subtitles; i++) { - app_free(card_data->subtitles[i].label); - } - for (int i = 0; i < card_data->num_zones; i++) { - app_free(card_data->zones[i].label); - } - i18n_free_all(card_data); - app_free(card_data); - health_detail_card_destroy(card); -} diff --git a/src/fw/apps/system_apps/health/health_sleep_detail_card.h b/src/fw/apps/system_apps/health/health_sleep_detail_card.h deleted file mode 100644 index a990b9b8fa..0000000000 --- a/src/fw/apps/system_apps/health/health_sleep_detail_card.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - -//! Creates a health sleep detail window -//! @param HealthData pointer to the health data to be given to this card -//! @return A pointer to a newly allocated health sleep detail window -Window *health_sleep_detail_card_create(HealthData *health_data); - -//! Destroys a health sleep detail window -//! @param window Window pointer to health sleep detail window -void health_sleep_detail_card_destroy(Window *window); diff --git a/src/fw/apps/system_apps/health/health_sleep_summary_card.c b/src/fw/apps/system_apps/health/health_sleep_summary_card.c deleted file mode 100644 index d7654c064f..0000000000 --- a/src/fw/apps/system_apps/health/health_sleep_summary_card.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_sleep_summary_card.h" -#include "health_sleep_summary_card_segments.h" -#include "health_sleep_detail_card.h" -#include "health_progress.h" -#include "health_ui.h" -#include "services/normal/activity/health_util.h" - -#include "applib/pbl_std/pbl_std.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/text_layer.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" -#include "util/size.h" -#include "util/string.h" - -typedef struct HealthSleepSummaryCardData { - HealthData *health_data; - HealthProgressBar progress_bar; - KinoReel *icon; - - GFont number_font; - GFont unit_font; - GFont typical_font; - GFont em_dash_font; -} HealthSleepSummaryCardData; - -#define PROGRESS_CURRENT_COLOR (PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorDarkGray)) -#define PROGRESS_SECONDARY_COLOR (PBL_IF_COLOR_ELSE(GColorVeryLightBlue, GColorClear)) -#define PROGRESS_TYPICAL_COLOR (PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack)) -#define PROGRESS_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorDarkGray, GColorClear)) -#define PROGRESS_OUTLINE_COLOR (PBL_IF_COLOR_ELSE(GColorClear, GColorBlack)) - -#define CURRENT_TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorBlack)) -#define TYPICAL_TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)) -#define NO_DATA_TEXT_COLOR (PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)) -#define CARD_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorOxfordBlue, GColorWhite)) - -#define TWELVE_HOURS (SECONDS_PER_HOUR * 12) - - -static void prv_render_sleep_sessions(GContext *ctx, HealthSleepSummaryCardData *data) { - const int num_sessions = health_data_sleep_get_num_sessions(data->health_data); - ActivitySession *sessions = health_data_sleep_get_sessions(data->health_data); - for (int i = 0; i < num_sessions; i++) { - ActivitySession *session = &sessions[i]; - GColor fill_color = GColorClear; - - if (session->type == ActivitySessionType_Sleep) { - fill_color = PROGRESS_CURRENT_COLOR; - } else if (session->type == ActivitySessionType_RestfulSleep) { - fill_color = PROGRESS_SECONDARY_COLOR; - } - - if (gcolor_equal(fill_color, GColorClear)) { - continue; - } - - struct tm local_tm; - localtime_r(&session->start_utc, &local_tm); - - const int session_start_24h = (local_tm.tm_sec + - local_tm.tm_min * SECONDS_PER_MINUTE + - local_tm.tm_hour * SECONDS_PER_HOUR); - const int session_end_24h = session_start_24h + (session->length_min * SECONDS_PER_MINUTE); - - const int session_start_12h = session_start_24h % TWELVE_HOURS; - const int session_end_12h = session_end_24h % TWELVE_HOURS; - - const int start = (session_start_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); - const int end = (session_end_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); - - health_progress_bar_fill(ctx, &data->progress_bar, fill_color, start, end); - } -} - -static void prv_render_typical_markers(GContext *ctx, HealthSleepSummaryCardData *data) { - // Some time fuzz is applied to a couple values to ensure that typical fill touches the sleep - // sessions (needed because of how our fill algorithms work) - const int sleep_start_24h = health_data_sleep_get_start_time(data->health_data); - - const int sleep_end_24h = health_data_sleep_get_end_time(data->health_data); - - if (sleep_start_24h || sleep_end_24h) { -#if PBL_COLOR - const int time_fuzz = (2 * SECONDS_PER_MINUTE); - const int sleep_start_12h = (sleep_start_24h) % TWELVE_HOURS; - const int sleep_end_12h = (sleep_end_24h - time_fuzz) % TWELVE_HOURS; - const int sleep_start = (sleep_start_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); - const int sleep_end = (sleep_end_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); -#endif - - const int typical_sleep_start_24h = health_data_sleep_get_typical_start_time(data->health_data); - const int typical_sleep_start_12h = typical_sleep_start_24h % TWELVE_HOURS; - const int typical_sleep_end_24h = health_data_sleep_get_typical_end_time(data->health_data); - const int typical_sleep_end_12h = typical_sleep_end_24h % TWELVE_HOURS; - - const int typical_start = - (typical_sleep_start_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); - const int typical_end = - (typical_sleep_end_12h * HEALTH_PROGRESS_BAR_MAX_VALUE / TWELVE_HOURS); - -#if PBL_COLOR - const bool fell_asleep_late = (typical_sleep_start_24h < sleep_start_24h); - if (fell_asleep_late) { - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, - typical_start, sleep_start); - } else { - health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_start); - } - - const bool woke_up_early = (typical_sleep_end_24h > sleep_end_24h); - if (woke_up_early) { - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, - sleep_end, typical_end); - } else { - health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_end); - } -#else - health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_start); - health_progress_bar_mark(ctx, &data->progress_bar, PROGRESS_TYPICAL_COLOR, typical_end); -#endif - } -} - -static void prv_render_progress_bar(GContext *ctx, Layer *base_layer) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - - // Renders the background - health_progress_bar_fill(ctx, &data->progress_bar, PROGRESS_BACKGROUND_COLOR, - 0, HEALTH_PROGRESS_BAR_MAX_VALUE); - - prv_render_sleep_sessions(ctx, data); - - prv_render_typical_markers(ctx, data); - - // This is required to get the rounded corners on the outside of the rectangle - graphics_context_set_stroke_width(ctx, 2); - graphics_context_set_stroke_color(ctx, CARD_BACKGROUND_COLOR); - graphics_draw_round_rect(ctx, &s_sleep_summary_masking_rect, 5); - - // This needs to be done after drawing the progress bars or else the progress fill - // overlaps the outline and things look weird - health_progress_bar_outline(ctx, &data->progress_bar, PROGRESS_OUTLINE_COLOR); -} - -static void prv_render_icon(GContext *ctx, Layer *base_layer) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - - const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(37, 32), 39); - const int x_center_offset = 17; - kino_reel_draw(data->icon, ctx, GPoint(base_layer->bounds.size.w / 2 - x_center_offset, y)); -} - -static void prv_render_current_sleep_text(GContext *ctx, Layer *base_layer) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - - const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(85, 83), 88); - const GRect rect = GRect(0, y, base_layer->bounds.size.w, 35); - - const int current_sleep = health_data_current_sleep_get(data->health_data); - if (current_sleep) { - // Draw the hours slept - GTextNodeHorizontal *horiz_container = graphics_text_node_create_horizontal(MAX_TEXT_NODES); - GTextNodeContainer *container = &horiz_container->container; - horiz_container->horizontal_alignment = GTextAlignmentCenter; - health_util_duration_to_hours_and_minutes_text_node(current_sleep, base_layer, - data->number_font, - data->unit_font, - CURRENT_TEXT_COLOR, container); - graphics_text_node_draw(&container->node, ctx, &rect, NULL, NULL); - graphics_text_node_destroy(&container->node); - } else { - char buffer[16]; - const GFont font = data->em_dash_font; - snprintf(buffer, sizeof(buffer), EM_DASH); - graphics_context_set_text_color(ctx, CURRENT_TEXT_COLOR); - graphics_draw_text(ctx, buffer, font, rect, GTextOverflowModeFill, GTextAlignmentCenter, NULL); - } -} - -static void prv_render_typical_sleep_text(GContext *ctx, Layer *base_layer) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - - const int typical_sleep = health_data_sleep_get_cur_wday_average(data->health_data); - - char sleep_text[32]; - - if (typical_sleep) { - health_util_format_hours_and_minutes(sleep_text, sizeof(sleep_text), typical_sleep, base_layer); - } else { - snprintf(sleep_text, sizeof(sleep_text), EM_DASH); - } - - health_ui_render_typical_text_box(ctx, base_layer, sleep_text); -} - -static void prv_render_no_sleep_data_text(GContext *ctx, Layer *base_layer) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - - const int y = PBL_IF_RECT_ELSE(91, 100); - const GRect rect = GRect(0, y, base_layer->bounds.size.w, 60); - - const char *text = i18n_get("No sleep data,\nwear your watch\nto sleep", base_layer); - - graphics_context_set_text_color(ctx, NO_DATA_TEXT_COLOR); - graphics_draw_text(ctx, text, data->typical_font, - rect, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} - -static bool prv_has_sleep_data(HealthData *health_data) { - // daily weekly stats doesn't include the first index so we check that separately - return health_data_current_sleep_get(health_data) || - health_data_sleep_get_monthly_average(health_data) > 0; -} - -static void prv_base_layer_update_proc(Layer *base_layer, GContext *ctx) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - - prv_render_icon(ctx, base_layer); - - prv_render_progress_bar(ctx, base_layer); - - if (!prv_has_sleep_data(data->health_data)) { - prv_render_no_sleep_data_text(ctx, base_layer); - return; - } - - prv_render_current_sleep_text(ctx, base_layer); - - prv_render_typical_sleep_text(ctx, base_layer); -} - -static void prv_sleep_detail_card_unload_callback(Window *window) { - health_sleep_detail_card_destroy(window); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -Layer *health_sleep_summary_card_create(HealthData *health_data) { - // create base layer - Layer *base_layer = layer_create_with_data(GRectZero, sizeof(HealthSleepSummaryCardData)); - HealthSleepSummaryCardData *health_sleep_summary_card_data = layer_get_data(base_layer); - layer_set_update_proc(base_layer, prv_base_layer_update_proc); - // set health data - *health_sleep_summary_card_data = (HealthSleepSummaryCardData) { - .icon = kino_reel_create_with_resource(RESOURCE_ID_HEALTH_APP_SLEEP), - .progress_bar = { - .num_segments = ARRAY_LENGTH(s_sleep_summary_progress_segments), - .segments = s_sleep_summary_progress_segments, - }, - .health_data = health_data, - .number_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM), - .unit_font = fonts_get_system_font(FONT_KEY_LECO_20_BOLD_NUMBERS), - .typical_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), - .em_dash_font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), - }; - return base_layer; -} - -void health_sleep_summary_card_select_click_handler(Layer *layer) { - HealthSleepSummaryCardData *health_sleep_summary_card_data = layer_get_data(layer); - HealthData *health_data = health_sleep_summary_card_data->health_data; - if (prv_has_sleep_data(health_data)) { - Window *window = health_sleep_detail_card_create(health_data); - window_set_window_handlers(window, &(WindowHandlers) { - .unload = prv_sleep_detail_card_unload_callback, - }); - app_window_stack_push(window, true); - } -} - -void health_sleep_summary_card_destroy(Layer *base_layer) { - HealthSleepSummaryCardData *data = layer_get_data(base_layer); - i18n_free_all(base_layer); - kino_reel_destroy(data->icon); - layer_destroy(base_layer); -} - -GColor health_sleep_summary_card_get_bg_color(Layer *layer) { - return CARD_BACKGROUND_COLOR; -} - -bool health_sleep_summary_show_select_indicator(Layer *layer) { - HealthSleepSummaryCardData *health_sleep_summary_card_data = layer_get_data(layer); - return prv_has_sleep_data(health_sleep_summary_card_data->health_data); -} diff --git a/src/fw/apps/system_apps/health/health_sleep_summary_card.h b/src/fw/apps/system_apps/health/health_sleep_summary_card.h deleted file mode 100644 index e5779fa4a0..0000000000 --- a/src/fw/apps/system_apps/health/health_sleep_summary_card.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_data.h" - -#include "applib/ui/ui.h" - -typedef enum { - SleepSummaryView_Sleep, - SleepSummaryView_DeepSleep, - SleepSummaryView_EndAndWake, - SleepSummaryView_Nap, - SleepSummaryViewCount -} SleepSummaryView; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// API Functions -// - -//! Creates a layer with extra data -//! @param health_data A pointer to the health data buffer -//! @return A pointer to the newly allocated layer -Layer *health_sleep_summary_card_create(HealthData *health_data); - -//! Health activity summary select click handler -//! @param layer A pointer to an existing layer with extra data -void health_sleep_summary_card_select_click_handler(Layer *layer); - -//! Set the card to a given view -//! @param view the view type to show -void health_sleep_summary_card_set_view(Layer *layer, SleepSummaryView view); - -//! Destroy a layer with extra data -//! @param base_layer A pointer to an existing layer with extra data -void health_sleep_summary_card_destroy(Layer *base_layer); - -//! Health sleep summary layer background color getter -GColor health_sleep_summary_card_get_bg_color(Layer *layer); - -//! Health sleep summary layer should show select click indicator -bool health_sleep_summary_show_select_indicator(Layer *layer); diff --git a/src/fw/apps/system_apps/health/health_sleep_summary_card_segments.h b/src/fw/apps/system_apps/health/health_sleep_summary_card_segments.h deleted file mode 100644 index e2f5c08758..0000000000 --- a/src/fw/apps/system_apps/health/health_sleep_summary_card_segments.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "health_progress.h" - -//! 5 main segments + 4 real corners -//! The top bar is split up into 2 segments (12am is the middle of the top bar) -//! Each of line gets 25% of the total (top line split into 2 segments which are 12.5% each) -#define AMOUNT_PER_SEGMENT (HEALTH_PROGRESS_BAR_MAX_VALUE * 25 / 100) - -// Found through trial and error -#define DEFAULT_MARK_WIDTH 40 - -#define X_SHIFT (PBL_IF_ROUND_ELSE(23, PBL_IF_BW_ELSE(1, 0))) -#define Y_SHIFT (PBL_IF_ROUND_ELSE(8, PBL_IF_BW_ELSE(3, 0))) - -// Used to shrink the thinkness of the bars -#define X_SHRINK (PBL_IF_BW_ELSE(2, 0)) - -// These are used to shrink the shape for round -#define X_ADJ (PBL_IF_ROUND_ELSE(-12, PBL_IF_BW_ELSE(-3, 0))) -#define Y_ADJ (PBL_IF_ROUND_ELSE(-3, PBL_IF_BW_ELSE(1, 0))) - - -static HealthProgressSegment s_sleep_summary_progress_segments[] = { - { - // Top right - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = AMOUNT_PER_SEGMENT / 2, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{71 + X_SHIFT, 22 + Y_SHIFT}, - {116 + X_SHRINK + X_SHIFT + X_ADJ, 22 + Y_SHIFT}, - {116 + X_SHRINK + X_SHIFT + X_ADJ, 13 + Y_SHIFT}, - {71 + X_SHIFT, 13 + Y_SHIFT}}, - }, - { - // Top right corner - .type = HealthProgressSegmentType_Corner, - .points = {{115 + X_SHRINK + X_SHIFT + X_ADJ, 22 + Y_SHIFT}, - {115 + X_SHRINK + X_SHIFT + X_ADJ, 13 + Y_SHIFT}, - {127 + X_SHIFT + X_ADJ, 13 + Y_SHIFT}, - {127 + X_SHIFT + X_ADJ, 22 + Y_SHIFT}}, - }, - { - // Right - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH + 10, - .points = {{116 + X_SHRINK + X_SHIFT + X_ADJ, 23 + Y_SHIFT}, - {127 + X_SHIFT + X_ADJ, 23 + Y_SHIFT}, - {127 + X_SHIFT + X_ADJ, 73 + Y_SHIFT + Y_ADJ}, - {116 + X_SHRINK + X_SHIFT + X_ADJ, 73 + Y_SHIFT + Y_ADJ}}, - }, - { - // Bottom right corner - .type = HealthProgressSegmentType_Corner, - .points = {{115 + X_SHRINK + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, - {127 + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, - {127 + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}, - {115 + X_SHRINK + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}}, - }, - { - // Bottom - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{116 + X_SHRINK + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, - {27 + X_SHRINK + X_SHIFT + X_ADJ, 74 + Y_SHIFT + Y_ADJ}, - {27 + X_SHRINK + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}, - {116 + X_SHRINK + X_SHIFT + X_ADJ, 83 + Y_SHIFT + Y_ADJ}}, - }, - { - // Bottom left corner - .type = HealthProgressSegmentType_Corner, - .points = {{29 + -X_SHRINK + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, - {17 + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, - {17 + X_SHIFT, 83 + Y_SHIFT + Y_ADJ}, - {29 + -X_SHRINK + X_SHIFT, 83 + Y_SHIFT + Y_ADJ}}, - }, - { - // Left - .type = HealthProgressSegmentType_Vertical, - .amount_of_total = AMOUNT_PER_SEGMENT, - .mark_width = DEFAULT_MARK_WIDTH, - .points = {{28 + -X_SHRINK + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, - {17 + X_SHIFT, 74 + Y_SHIFT + Y_ADJ}, - {17 + X_SHIFT, 23 + Y_SHIFT}, - {28 + -X_SHRINK + X_SHIFT, 23 + Y_SHIFT}}, - }, - { - // Top left corner - .type = HealthProgressSegmentType_Corner, - .points = {{29 + X_SHIFT, 22 + Y_SHIFT}, - {17 + X_SHIFT, 22 + Y_SHIFT}, - {17 + X_SHIFT, 13 + Y_SHIFT}, - {29 + X_SHIFT, 13 + Y_SHIFT}}, - }, - { - // Top left - .type = HealthProgressSegmentType_Horizontal, - .amount_of_total = AMOUNT_PER_SEGMENT / 2, - .mark_width = DEFAULT_MARK_WIDTH + 10, - .points = {{28 + -X_SHRINK + X_SHIFT, 22 + Y_SHIFT}, - {72 + X_SHIFT, 22 + Y_SHIFT}, - {72 + X_SHIFT, 13 + Y_SHIFT}, - {28 + -X_SHRINK + X_SHIFT, 13 + Y_SHIFT}}, - }, -}; - -#define MASKING_RECT_X_SHIFT (X_SHIFT + PBL_IF_BW_ELSE(1, 0)) -#define MASKING_RECT_Y_SHIFT (Y_SHIFT + PBL_IF_BW_ELSE(1, 0)) -#define MASKING_RECT_X_ADJ (X_ADJ + PBL_IF_BW_ELSE(-1, 0)) -#define MASKING_RECT_Y_ADJ (Y_ADJ + PBL_IF_BW_ELSE(-1, 0)) - -static const GRect s_sleep_summary_masking_rect = { - .origin.x = 16 + MASKING_RECT_X_SHIFT, - .origin.y = 11 + MASKING_RECT_Y_SHIFT, - .size.w = 113 + MASKING_RECT_X_ADJ, - .size.h = 75 + MASKING_RECT_Y_ADJ, -}; diff --git a/src/fw/apps/system_apps/health/health_ui.c b/src/fw/apps/system_apps/health/health_ui.c deleted file mode 100644 index cbdab62144..0000000000 --- a/src/fw/apps/system_apps/health/health_ui.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_ui.h" - -#include "applib/pbl_std/pbl_std.h" -#include "services/common/i18n/i18n.h" -#include "util/string.h" - -void health_ui_draw_text_in_box(GContext *ctx, const char *text, const GRect drawing_bounds, - const int16_t y_offset, const GFont small_font, GColor box_color, - GColor text_color) { - const uint8_t text_height = fonts_get_font_height(small_font); - const GTextOverflowMode overflow_mode = GTextOverflowModeFill; - const GTextAlignment alignment = GTextAlignmentCenter; - - const GRect text_box = GRect(drawing_bounds.origin.x, y_offset, - drawing_bounds.size.w, text_height); - - GRect text_fill_box = text_box; - text_fill_box.size = app_graphics_text_layout_get_content_size( - text, small_font, text_box, overflow_mode, alignment); - text_fill_box.origin.x += ((drawing_bounds.size.w - text_fill_box.size.w) / 2); - - // add a 3 px border (get content size already adds 1 px) - text_fill_box = grect_inset(text_fill_box, GEdgeInsets(-2)); - - // get content size adds 5 to the height, and the y offset is too high by a px (+ the 5px) - const int height_correction = 5; - text_fill_box.size.h -= height_correction; - text_fill_box.origin.y += height_correction + 1; - - if (!gcolor_equal(box_color, GColorClear)) { - graphics_context_set_fill_color(ctx, box_color); - graphics_fill_rect(ctx, &text_fill_box); - } - - if (!gcolor_equal(text_color, GColorClear)) { - graphics_context_set_text_color(ctx, text_color); - graphics_draw_text(ctx, text, small_font, text_box, overflow_mode, alignment, NULL); - } -} - -void health_ui_render_typical_text_box(GContext *ctx, Layer *layer, const char *value_text) { - time_t now = rtc_get_time(); - struct tm time_tm; - localtime_r(&now, &time_tm); - char weekday[8]; - strftime(weekday, sizeof(weekday), "%a", &time_tm); - toupper_str(weekday); - - char typical_text[32]; - snprintf(typical_text, sizeof(typical_text), i18n_get("TYPICAL %s", layer), weekday); - - const int y = PBL_IF_RECT_ELSE(PBL_IF_BW_ELSE(122, 120), 125); - GRect rect = GRect(0, y, layer->bounds.size.w, PBL_IF_RECT_ELSE(35, 36)); -#if PBL_RECT - rect = grect_inset(rect, GEdgeInsets(0, 18)); -#endif - - const GColor bg_color = PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack); - const GColor text_color = PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite); - const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - - graphics_context_set_fill_color(ctx, bg_color); - graphics_fill_round_rect(ctx, &rect, 3, GCornersAll); - - rect.origin.y -= PBL_IF_RECT_ELSE(3, 2); - // Restrict the rect to draw one line at a time to prevent them from wrapping into each other - rect.size.h = 16; - - graphics_context_set_text_color(ctx, text_color); - - graphics_draw_text(ctx, typical_text, font, rect, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); - - rect.origin.y += 16; - - graphics_draw_text(ctx, value_text, font, rect, - GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); -} diff --git a/src/fw/apps/system_apps/health/health_ui.h b/src/fw/apps/system_apps/health/health_ui.h deleted file mode 100644 index ef719ecbe3..0000000000 --- a/src/fw/apps/system_apps/health/health_ui.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/ui.h" - -void health_ui_draw_text_in_box(GContext *ctx, const char *text, const GRect drawing_bounds, - const int16_t y_offset, const GFont small_font, GColor box_color, - GColor text_color); - -void health_ui_render_typical_text_box(GContext *ctx, Layer *layer, const char *value_text); diff --git a/src/fw/apps/system_apps/hrm_demo.c b/src/fw/apps/system_apps/hrm_demo.c deleted file mode 100644 index 82d386937b..0000000000 --- a/src/fw/apps/system_apps/hrm_demo.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "applib/app.h" -#include "applib/app_message/app_message.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window.h" -#include "apps/system_app_ids.h" -#if PLATFORM_SILK || PLATFORM_ROBERT -#include "drivers/hrm/as7000/as7000.h" -#endif -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_info.h" -#include "mfg/mfg_serials.h" -#include "process_state/app_state/app_state.h" -#include "services/common/hrm/hrm_manager.h" -#include "system/passert.h" - -#define BPM_STRING_LEN 10 - -typedef enum { - AppMessageKey_Status = 1, - - AppMessageKey_HeartRate = 10, - AppMessageKey_Confidence = 11, - AppMessageKey_Current = 12, - AppMessageKey_TIA = 13, - AppMessageKey_PPG = 14, - AppMessageKey_AccelData = 15, - AppMessageKey_SerialNumber = 16, - AppMessageKey_Model = 17, - AppMessageKey_HRMProtocolVersionMajor = 18, - AppMessageKey_HRMProtocolVersionMinor = 19, - AppMessageKey_HRMSoftwareVersionMajor = 20, - AppMessageKey_HRMSoftwareVersionMinor = 21, - AppMessageKey_HRMApplicationID = 22, - AppMessageKey_HRMHardwareRevision = 23, -} AppMessageKey; - -typedef enum { - AppStatus_Stopped = 0, - AppStatus_Enabled_1HZ = 1, -} AppStatus; - -typedef struct { - HRMSessionRef session; - EventServiceInfo hrm_event_info; - - Window window; - TextLayer bpm_text_layer; - TextLayer quality_text_layer; - - char bpm_string[BPM_STRING_LEN]; - - bool ready_to_send; - DictionaryIterator *out_iter; -} AppData; - -static char *prv_get_quality_string(HRMQuality quality) { - switch (quality) { - case HRMQuality_NoAccel: - return "No Accel Data"; - case HRMQuality_OffWrist: - return "Off Wrist"; - case HRMQuality_NoSignal: - return "No Signal"; - case HRMQuality_Worst: - return "Worst"; - case HRMQuality_Poor: - return "Poor"; - case HRMQuality_Acceptable: - return "Acceptable"; - case HRMQuality_Good: - return "Good"; - case HRMQuality_Excellent: - return "Excellent"; - } - WTF; -} - -static char *prv_translate_error(AppMessageResult result) { - switch (result) { - case APP_MSG_OK: return "APP_MSG_OK"; - case APP_MSG_SEND_TIMEOUT: return "APP_MSG_SEND_TIMEOUT"; - case APP_MSG_SEND_REJECTED: return "APP_MSG_SEND_REJECTED"; - case APP_MSG_NOT_CONNECTED: return "APP_MSG_NOT_CONNECTED"; - case APP_MSG_APP_NOT_RUNNING: return "APP_MSG_APP_NOT_RUNNING"; - case APP_MSG_INVALID_ARGS: return "APP_MSG_INVALID_ARGS"; - case APP_MSG_BUSY: return "APP_MSG_BUSY"; - case APP_MSG_BUFFER_OVERFLOW: return "APP_MSG_BUFFER_OVERFLOW"; - case APP_MSG_ALREADY_RELEASED: return "APP_MSG_ALREADY_RELEASED"; - case APP_MSG_CALLBACK_ALREADY_REGISTERED: return "APP_MSG_CALLBACK_ALREADY_REGISTERED"; - case APP_MSG_CALLBACK_NOT_REGISTERED: return "APP_MSG_CALLBACK_NOT_REGISTERED"; - case APP_MSG_OUT_OF_MEMORY: return "APP_MSG_OUT_OF_MEMORY"; - case APP_MSG_CLOSED: return "APP_MSG_CLOSED"; - case APP_MSG_INTERNAL_ERROR: return "APP_MSG_INTERNAL_ERROR"; - default: return "UNKNOWN ERROR"; - } -} - -static void prv_send_msg(void) { - AppData *app_data = app_state_get_user_data(); - - AppMessageResult result = app_message_outbox_send(); - if (result == APP_MSG_OK) { - app_data->ready_to_send = false; - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Error sending message: %s", prv_translate_error(result)); - } -} - -static void prv_send_status_and_version(void) { - AppData *app_data = app_state_get_user_data(); - PBL_LOG(LOG_LEVEL_DEBUG, "Sending status and version to mobile app"); - - AppMessageResult result = app_message_outbox_begin(&app_data->out_iter); - if (result != APP_MSG_OK) { - PBL_LOG(LOG_LEVEL_DEBUG, "Failed to begin outbox - reason %i %s", - result, prv_translate_error(result)); - return; - } - - dict_write_uint8(app_data->out_iter, AppMessageKey_Status, AppStatus_Enabled_1HZ); - -#if CAPABILITY_HAS_BUILTIN_HRM && (PLATFORM_SILK || PLATFORM_ROBERT) - if (mfg_info_is_hrm_present()) { - AS7000InfoRecord hrm_info = {}; - as7000_get_version_info(HRM, &hrm_info); - dict_write_uint8(app_data->out_iter, AppMessageKey_HRMProtocolVersionMajor, - hrm_info.protocol_version_major); - dict_write_uint8(app_data->out_iter, AppMessageKey_HRMProtocolVersionMinor, - hrm_info.protocol_version_minor); - dict_write_uint8(app_data->out_iter, AppMessageKey_HRMSoftwareVersionMajor, - hrm_info.sw_version_major); - dict_write_uint8(app_data->out_iter, AppMessageKey_HRMSoftwareVersionMinor, - hrm_info.sw_version_minor); - dict_write_uint8(app_data->out_iter, AppMessageKey_HRMApplicationID, - hrm_info.application_id); - dict_write_uint8(app_data->out_iter, AppMessageKey_HRMHardwareRevision, - hrm_info.hw_revision); - } -#endif - - char serial_number_buffer[MFG_SERIAL_NUMBER_SIZE + 1]; - mfg_info_get_serialnumber(serial_number_buffer, sizeof(serial_number_buffer)); - dict_write_data(app_data->out_iter, AppMessageKey_SerialNumber, - (uint8_t*) serial_number_buffer, sizeof(serial_number_buffer)); - -#if IS_BIGBOARD - WatchInfoColor watch_color = WATCH_INFO_MODEL_UNKNOWN; -#else - WatchInfoColor watch_color = mfg_info_get_watch_color(); -#endif // IS_BIGBOARD - dict_write_uint32(app_data->out_iter, AppMessageKey_Model, watch_color); - - prv_send_msg(); -} - -static void prv_handle_hrm_data(PebbleEvent *e, void *context) { - AppData *app_data = app_state_get_user_data(); - - if (e->type == PEBBLE_HRM_EVENT) { - PebbleHRMEvent *hrm = &e->hrm; - - // Save HRMEventBPM data and send when we get the current into. - static uint8_t bpm = 0; - static uint8_t bpm_quality = 0; - static uint16_t led_current = 0; - - if (hrm->event_type == HRMEvent_BPM) { - snprintf(app_data->bpm_string, sizeof(app_data->bpm_string), "%"PRIu8" BPM", hrm->bpm.bpm); - text_layer_set_text(&app_data->quality_text_layer, prv_get_quality_string(hrm->bpm.quality)); - layer_mark_dirty(&app_data->window.layer); - - bpm = hrm->bpm.bpm; - bpm_quality = hrm->bpm.quality; - } else if (hrm->event_type == HRMEvent_LEDCurrent) { - led_current = hrm->led.current_ua; - } else if (hrm->event_type == HRMEvent_Diagnostics) { - if (!app_data->ready_to_send) { - return; - } - - AppMessageResult result = app_message_outbox_begin(&app_data->out_iter); - PBL_ASSERTN(result == APP_MSG_OK); - - if (bpm) { - dict_write_uint8(app_data->out_iter, AppMessageKey_HeartRate, bpm); - dict_write_uint8(app_data->out_iter, AppMessageKey_Confidence, bpm_quality); - } - - if (led_current) { - dict_write_uint16(app_data->out_iter, AppMessageKey_Current, led_current); - } - - if (hrm->debug->ppg_data.num_samples) { - HRMPPGData *d = &hrm->debug->ppg_data; - dict_write_data(app_data->out_iter, AppMessageKey_TIA, - (uint8_t *)d->tia, d->num_samples * sizeof(d->tia[0])); - dict_write_data(app_data->out_iter, AppMessageKey_PPG, - (uint8_t *)d->ppg, d->num_samples * sizeof(d->ppg[0])); - } - - if (hrm->debug->ppg_data.tia[hrm->debug->ppg_data.num_samples - 1] == 0) { - PBL_LOG_COLOR(LOG_LEVEL_DEBUG, LOG_COLOR_CYAN, "last PPG TIA sample is 0!"); - } - - if (hrm->debug->ppg_data.num_samples != 20) { - PBL_LOG_COLOR(LOG_LEVEL_DEBUG, LOG_COLOR_CYAN, "Only got %"PRIu16" samples!", - hrm->debug->ppg_data.num_samples); - } - - if (hrm->debug->accel_data.num_samples) { - HRMAccelData *d = &hrm->debug->accel_data; - dict_write_data(app_data->out_iter, AppMessageKey_AccelData, - (uint8_t *)d->data, d->num_samples * sizeof(d->data[0])); - } - - PBL_LOG(LOG_LEVEL_DEBUG, - "Sending message - bpm:%u quality:%u current:%u " - "ppg_readings:%u accel_readings %"PRIu32, - bpm, - bpm_quality, - led_current, - hrm->debug->ppg_data.num_samples, - hrm->debug->accel_data.num_samples); - - led_current = bpm = bpm_quality = 0; - - prv_send_msg(); - } else if (hrm->event_type == HRMEvent_SubscriptionExpiring) { - PBL_LOG(LOG_LEVEL_INFO, "Got subscription expiring event"); - // Subscribe again if our subscription is expiring - const uint32_t update_time_s = 1; - app_data->session = sys_hrm_manager_app_subscribe(APP_ID_HRM_DEMO, update_time_s, - SECONDS_PER_HOUR, HRMFeature_BPM); - } - } -} - -static void prv_enable_hrm(void) { - AppData *app_data = app_state_get_user_data(); - - app_data->hrm_event_info = (EventServiceInfo) { - .type = PEBBLE_HRM_EVENT, - .handler = prv_handle_hrm_data, - }; - event_service_client_subscribe(&app_data->hrm_event_info); - - // TODO: Let the mobile app control this? - const uint32_t update_time_s = 1; - app_data->session = sys_hrm_manager_app_subscribe( - APP_ID_HRM_DEMO, update_time_s, SECONDS_PER_HOUR, - HRMFeature_BPM | HRMFeature_LEDCurrent | HRMFeature_Diagnostics); -} - -static void prv_disable_hrm(void) { - AppData *app_data = app_state_get_user_data(); - - event_service_client_unsubscribe(&app_data->hrm_event_info); - sys_hrm_manager_unsubscribe(app_data->session); -} - -static void prv_handle_mobile_status_request(AppStatus status) { - AppData *app_data = app_state_get_user_data(); - - if (status == AppStatus_Stopped) { - text_layer_set_text(&app_data->bpm_text_layer, "Paused"); - text_layer_set_text(&app_data->quality_text_layer, "Paused by mobile"); - prv_disable_hrm(); - } else { - app_data->bpm_string[0] = '\0'; - text_layer_set_text(&app_data->bpm_text_layer, app_data->bpm_string); - text_layer_set_text(&app_data->quality_text_layer, "Loading..."); - prv_enable_hrm(); - } -} - -static void prv_message_received_cb(DictionaryIterator *iterator, void *context) { - Tuple *status_tuple = dict_find(iterator, AppMessageKey_Status); - - if (status_tuple) { - prv_handle_mobile_status_request(status_tuple->value->uint8); - } -} - -static void prv_message_sent_cb(DictionaryIterator *iterator, void *context) { - AppData *app_data = app_state_get_user_data(); - - app_data->ready_to_send = true; -} - -static void prv_message_failed_cb(DictionaryIterator *iterator, - AppMessageResult reason, void *context) { - PBL_LOG(LOG_LEVEL_DEBUG, "Out message send failed - reason %i %s", - reason, prv_translate_error(reason)); - AppData *app_data = app_state_get_user_data(); - app_data->ready_to_send = true; -} - -static void prv_remote_notify_timer_cb(void *data) { - prv_send_status_and_version(); -} - -static void prv_init(void) { - AppData *app_data = app_malloc_check(sizeof(*app_data)); - *app_data = (AppData) { - .session = (HRMSessionRef)app_data, // Use app data as session ref - .ready_to_send = false, - }; - app_state_set_user_data(app_data); - - Window *window = &app_data->window; - window_init(window, ""); - window_set_fullscreen(window, true); - - GRect bounds = window->layer.bounds; - - bounds.origin.y += 40; - TextLayer *bpm_tl = &app_data->bpm_text_layer; - text_layer_init(bpm_tl, &bounds); - text_layer_set_font(bpm_tl, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - text_layer_set_text_alignment(bpm_tl, GTextAlignmentCenter); - text_layer_set_text(bpm_tl, app_data->bpm_string); - layer_add_child(&window->layer, &bpm_tl->layer); - - bounds.origin.y += 35; - TextLayer *quality_tl = &app_data->quality_text_layer; - text_layer_init(quality_tl, &bounds); - text_layer_set_font(quality_tl, fonts_get_system_font(FONT_KEY_GOTHIC_18)); - text_layer_set_text_alignment(quality_tl, GTextAlignmentCenter); - text_layer_set_text(quality_tl, "Loading..."); - layer_add_child(&window->layer, &quality_tl->layer); - - const uint32_t inbox_size = 64; - const uint32_t outbox_size = 256; - AppMessageResult result = app_message_open(inbox_size, outbox_size); - if (result != APP_MSG_OK) { - PBL_LOG(LOG_LEVEL_ERROR, "Unable to open app message! %i %s", - result, prv_translate_error(result)); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Successfully opened app message"); - } - - if (!sys_hrm_manager_is_hrm_present()) { - text_layer_set_text(quality_tl, "No HRM Present"); - } else { - text_layer_set_text(quality_tl, "Loading..."); - prv_enable_hrm(); - } - - app_message_register_inbox_received(prv_message_received_cb); - app_message_register_outbox_sent(prv_message_sent_cb); - app_message_register_outbox_failed(prv_message_failed_cb); - - app_timer_register(1000, prv_remote_notify_timer_cb, NULL); - - app_window_stack_push(window, true); -} - -static void prv_deinit(void) { - AppData *app_data = app_state_get_user_data(); - sys_hrm_manager_unsubscribe(app_data->session); -} - -static void prv_main(void) { - prv_init(); - app_event_loop(); - prv_deinit(); -} - -const PebbleProcessMd* hrm_demo_get_app_info(void) { - static const PebbleProcessMdSystem s_hrm_demo_app_info = { - .name = "HRM Demo", - .common.uuid = { 0xf8, 0x1b, 0x2a, 0xf8, 0x13, 0x0a, 0x11, 0xe6, - 0x86, 0x9f, 0xa4, 0x5e, 0x60, 0xb9, 0x77, 0x3d }, - .common.main_func = &prv_main, - }; - // Only show in launcher if HRM is present - return (sys_hrm_manager_is_hrm_present()) ? (const PebbleProcessMd*)&s_hrm_demo_app_info : NULL; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app.c b/src/fw/apps/system_apps/launcher/default/launcher_app.c deleted file mode 100644 index 9c71441265..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app.h" - -#include "launcher_menu_layer.h" - -#include "applib/app.h" -#include "applib/app_focus_service.h" -#include "applib/ui/app_window_stack.h" -#include "kernel/pbl_malloc.h" -#include "shell/normal/app_idle_timeout.h" -#include "system/passert.h" -#include "process_state/app_state/app_state.h" -#include "util/attributes.h" - -typedef struct LauncherAppWindowData { - Window window; - LauncherMenuLayer launcher_menu_layer; - AppMenuDataSource app_menu_data_source; -} LauncherAppWindowData; - -typedef struct LauncherAppPersistedData { - bool valid; - RtcTicks leave_time; - LauncherMenuLayerSelectionState selection_state; - LauncherDrawState draw_state; -} LauncherAppPersistedData; - -static LauncherAppPersistedData s_launcher_app_persisted_data; - -///////////////////////////// -// AppFocusService handlers - -static void prv_did_focus(bool in_focus) { - LauncherAppWindowData *data = app_state_get_user_data(); - if (in_focus) { - launcher_menu_layer_set_selection_animations_enabled(&data->launcher_menu_layer, true); - } -} - -static void prv_will_focus(bool in_focus) { - LauncherAppWindowData *data = app_state_get_user_data(); - if (!in_focus) { - launcher_menu_layer_set_selection_animations_enabled(&data->launcher_menu_layer, false); - } -} - -//////////////////////////////// -// AppMenuDataSource callbacks - -static bool prv_app_filter_callback(PBL_UNUSED AppMenuDataSource *source, AppInstallEntry *entry) { - // Skip watchfaces and hidden apps - return (!app_install_entry_is_watchface(entry) && !app_install_entry_is_hidden((entry))); -} - -static void prv_data_changed(void *context) { - LauncherAppWindowData *data = context; - launcher_menu_layer_reload_data(&data->launcher_menu_layer); -} - -//! We're not 100% sure of the order of the launcher list yet, so use this function to transform -//! the row index to achieve the desired list ordering -static uint16_t prv_transform_index(AppMenuDataSource *data_source, uint16_t original_index, - void *context) { -#if (SHELL_SDK && CAPABILITY_HAS_SDK_SHELL4) - // We want the newest installed developer app to appear at the top - // This works at the moment because there is only one system app, Watchfaces - return app_menu_data_source_get_count(data_source) - 1 - original_index; -#else - return original_index; -#endif -} - -///////////////////// -// Window callbacks - -static void prv_window_load(Window *window) { - LauncherAppWindowData *data = window_get_user_data(window); - - Layer *window_root_layer = window_get_root_layer(window); - - AppMenuDataSource *data_source = &data->app_menu_data_source; - app_menu_data_source_init(data_source, &(AppMenuDataSourceCallbacks) { - .changed = prv_data_changed, - .filter = prv_app_filter_callback, - .transform_index = prv_transform_index, - }, data); - - LauncherMenuLayer *launcher_menu_layer = &data->launcher_menu_layer; - launcher_menu_layer_init(launcher_menu_layer, data_source); - launcher_menu_layer_set_click_config_onto_window(launcher_menu_layer, window); - layer_add_child(window_root_layer, launcher_menu_layer_get_layer(launcher_menu_layer)); - - // If we have a saved launcher selection state, restore it - if (s_launcher_app_persisted_data.valid) { - launcher_menu_layer_set_selection_state(launcher_menu_layer, - &s_launcher_app_persisted_data.selection_state); - } - - app_focus_service_subscribe_handlers((AppFocusHandlers) { - .did_focus = prv_did_focus, - .will_focus = prv_will_focus, - }); -} - -static void prv_window_unload(Window *window) { - LauncherAppWindowData *data = window_get_user_data(window); - - // Capture the vertical range of the selection rectangle for compositor transition animations - GRangeVertical launcher_selection_vertical_range; - launcher_menu_layer_get_selection_vertical_range(&data->launcher_menu_layer, - &launcher_selection_vertical_range); - - // Save the current state of the launcher so we can know its draw state and restore it later - s_launcher_app_persisted_data = (LauncherAppPersistedData) { - .valid = true, - .leave_time = rtc_get_ticks(), - .draw_state.selection_vertical_range = launcher_selection_vertical_range, - .draw_state.selection_background_color = LAUNCHER_MENU_LAYER_SELECTION_BACKGROUND_COLOR, - }; - launcher_menu_layer_get_selection_state(&data->launcher_menu_layer, - &s_launcher_app_persisted_data.selection_state); - - app_focus_service_unsubscribe(); - launcher_menu_layer_deinit(&data->launcher_menu_layer); - app_menu_data_source_deinit(&data->app_menu_data_source); -} - -//////////////////// -// App boilerplate - -static void prv_launcher_menu_window_push(void) { - LauncherAppWindowData *data = app_zalloc_check(sizeof(*data)); - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("Launcher Menu")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - - const bool animated = false; - app_window_stack_push(window, animated); -} - -static void prv_main(void) { - const LauncherMenuArgs *args = (const LauncherMenuArgs *)app_manager_get_task_context()->args; - // Reset the selection state of the launcher if we're visiting it for the first time or if - // it has been more than RETURN_TIMEOUT_TICKS since we were last in the launcher - if (args && args->reset_scroll) { - if ((s_launcher_app_persisted_data.leave_time + RETURN_TIMEOUT_TICKS) <= rtc_get_ticks()) { - s_launcher_app_persisted_data.valid = false; - } - } - - prv_launcher_menu_window_push(); - - app_idle_timeout_start(); - - app_event_loop(); -} - -const PebbleProcessMd *launcher_menu_app_get_app_info(void) { - static const PebbleProcessMdSystem s_launcher_menu_app_info = { - .common = { - .main_func = prv_main, - // UUID: dec0424c-0625-4878-b1f2-147e57e83688 - .uuid = {0xde, 0xc0, 0x42, 0x4c, 0x06, 0x25, 0x48, 0x78, - 0xb1, 0xf2, 0x14, 0x7e, 0x57, 0xe8, 0x36, 0x88}, - .visibility = ProcessVisibilityHidden - }, - .name = "Launcher", - }; - return (const PebbleProcessMd *)&s_launcher_menu_app_info; -} - -const LauncherDrawState *launcher_app_get_draw_state(void) { - return &s_launcher_app_persisted_data.draw_state; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app.h b/src/fw/apps/system_apps/launcher/default/launcher_app.h deleted file mode 100644 index 434d2426b6..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "../launcher_app.h" - -#include "launcher_menu_layer.h" - -#include "applib/graphics/gtypes.h" - -#include - -typedef struct LauncherMenuArgs { - bool reset_scroll; -} LauncherMenuArgs; - -typedef struct LauncherDrawState { - GRangeVertical selection_vertical_range; - GColor selection_background_color; -} LauncherDrawState; - -const LauncherDrawState *launcher_app_get_draw_state(void); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance.c deleted file mode 100644 index 3f9e0fb926..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance.h" - -#include "launcher_menu_layer.h" - -#include "applib/app_glance.h" -#include "applib/ui/kino/kino_reel_custom.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "system/passert.h" -#include "util/string.h" - -void launcher_app_glance_init(LauncherAppGlance *glance, const Uuid *uuid, KinoReel *impl, - bool should_consider_slices, - const LauncherAppGlanceHandlers *handlers) { - if (!glance || !uuid) { - return; - } - - *glance = (LauncherAppGlance) { - .uuid = *uuid, - .reel = impl, - .should_consider_slices = should_consider_slices, - }; - - if (handlers) { - glance->handlers = *handlers; - } - - launcher_app_glance_update_current_slice(glance); -} - -void launcher_app_glance_update_current_slice(LauncherAppGlance *glance) { - if (!glance || !glance->should_consider_slices) { - return; - } - - const Uuid *uuid = &glance->uuid; - - AppGlanceSliceInternal current_slice = {}; - // If there's no current slice, this function won't modify the zeroed-out current_slice, so we - // can safely set the glance's current slice to current_slice either way - app_glance_service_get_current_slice(uuid, ¤t_slice); - glance->current_slice = current_slice; - - if (glance->handlers.current_slice_updated) { - glance->handlers.current_slice_updated(glance); - } - - launcher_app_glance_service_notify_glance_changed(glance->service); -} - -void launcher_app_glance_draw(GContext *ctx, const GRect *frame, LauncherAppGlance *glance, - bool is_highlighted) { - if (!glance || !frame || !ctx) { - return; - } - - glance->size = frame->size; - glance->is_highlighted = is_highlighted; - kino_reel_draw(glance->reel, ctx, frame->origin); -} - -void launcher_app_glance_notify_service_glance_changed(LauncherAppGlance *glance) { - if (!glance) { - return; - } - launcher_app_glance_service_notify_glance_changed(glance->service); -} - -void launcher_app_glance_destroy(LauncherAppGlance *glance) { - if (!glance) { - return; - } - - kino_reel_destroy(glance->reel); - app_free(glance); -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance.h deleted file mode 100644 index 99c13dd325..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance_service.h" - -#include "applib/ui/kino/kino_reel.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "util/uuid.h" - -//! Forward declaration -typedef struct LauncherAppGlance LauncherAppGlance; - -//! Called when a launcher app glance's current slice has been updated. The glance will -//! automatically be redrawn after this function is called. -//! @param The glance whose current slice has been updated -typedef void (*LauncherAppGlanceCurrentSliceUpdated)(LauncherAppGlance *glance); - -typedef struct LauncherAppGlanceHandlers { - LauncherAppGlanceCurrentSliceUpdated current_slice_updated; -} LauncherAppGlanceHandlers; - -struct LauncherAppGlance { - //! The UUID of the app the launcher app glance represents - Uuid uuid; - //! The reel that implements how the launcher app glance should be drawn - KinoReel *reel; - //! Size of the area in which the launcher app glance expects to draw itself - GSize size; - //! Whether or not the launcher app glance is currently highlighted - bool is_highlighted; - //! Whether or not the launcher app glance should consider slices - bool should_consider_slices; - //! The current slice that should be drawn in the launcher app glance - AppGlanceSliceInternal current_slice; - //! The launcher app glance service that created the glance; used by the glance to notify the - //! service that the glance needs to be redrawn - LauncherAppGlanceService *service; - //! Callback handlers for the launcher app glance - LauncherAppGlanceHandlers handlers; -}; - -//! Initialize a launcher app glance. -//! @param glance The glance to initialize -//! @param uuid The UUID of the app -//! @param impl The KinoReel implementation for the glance -//! @param should_consider_slices Whether or not the glance should consider slices -//! @param handlers Optional handlers to use with the glance -void launcher_app_glance_init(LauncherAppGlance *glance, const Uuid *uuid, KinoReel *impl, - bool should_consider_slices, - const LauncherAppGlanceHandlers *handlers); - -//! Update the current slice of the launcher app glance as well as the icon if the slice needs to -//! change it. -//! @param glance The glance for which to update the current slice -void launcher_app_glance_update_current_slice(LauncherAppGlance *glance); - -//! Draw the provided launcher app glance. -//! @param ctx The graphics context to use when drawing the glance -//! @param frame The frame in which to draw the glance -//! @param glance The glance to draw -//! @param is_highlighted Whether or not the glance should be drawn highlighted -void launcher_app_glance_draw(GContext *ctx, const GRect *frame, LauncherAppGlance *glance, - bool is_highlighted); - -//! Notify the launcher app glance's service that its content has changed. -//! @param glance The glance that has changed -void launcher_app_glance_notify_service_glance_changed(LauncherAppGlance *glance); - -//! Destroy the provided launcher app glance. -//! @param glance The glance to destroy -void launcher_app_glance_destroy(LauncherAppGlance *glance); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.c deleted file mode 100644 index 70af8552af..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_alarms.h" - -#include "launcher_app_glance_structured.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/timeline/attribute.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -typedef struct LauncherAppGlanceAlarms { - char title[APP_NAME_SIZE_BYTES]; - char subtitle[ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN]; - KinoReel *icon; - uint32_t icon_resource_id; - uint32_t default_icon_resource_id; - EventServiceInfo alarm_clock_event_info; -} LauncherAppGlanceAlarms; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceAlarms *alarms_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(alarms_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceAlarms *alarms_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(alarms_glance, title, NULL); -} - -static void prv_alarms_glance_subtitle_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceAlarms *alarms_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (alarms_glance) { - strncpy(buffer, alarms_glance->subtitle, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_alarms_glance_subtitle_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceAlarms *alarms_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (alarms_glance) { - event_service_client_unsubscribe(&alarms_glance->alarm_clock_event_info); - kino_reel_destroy(alarms_glance->icon); - } - app_free(alarms_glance); -} - -static void prv_set_glance_icon(LauncherAppGlanceAlarms *alarms_glance, - uint32_t new_icon_resource_id) { - if (alarms_glance->icon_resource_id == new_icon_resource_id) { - // Nothing to do, bail out - return; - } - - // Destroy the existing icon - kino_reel_destroy(alarms_glance->icon); - - // Set the new icon and record its resource ID - alarms_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); - PBL_ASSERTN(alarms_glance->icon); - alarms_glance->icon_resource_id = new_icon_resource_id; -} - -//! If alarm is for today, alarm text should look like "5:43 PM" (12 hr) or "17:43" (24 hr) -//! If alarm is not for today, text should look like "Fri, 11:30 PM" (12 hr) or "Fri, 23:30" (24 hr) -//! If no alarms are set, the alarm text should be the empty string "" -static void prv_update_glance_for_next_alarm(LauncherAppGlanceAlarms *alarms_glance) { - // Start by assuming we'll set the default icon - uint32_t new_icon_resource_id = alarms_glance->default_icon_resource_id; - - time_t alarm_time_epoch; - if (!alarm_get_next_enabled_alarm(&alarm_time_epoch)) { - // Clear the alarm text if there are no alarms set - alarms_glance->subtitle[0] = '\0'; - } else { - // If the next alarm is smart, use the smart alarm icon - if (alarm_is_next_enabled_alarm_smart()) { - // TODO PBL-39113: Replace this placeholder with a better smart alarm icon for the glance - new_icon_resource_id = RESOURCE_ID_SMART_ALARM_TINY; - } - - char time_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; - clock_copy_time_string_timestamp(time_buffer, sizeof(time_buffer), alarm_time_epoch); - - // Determine if the alarm is for today - const time_t current_time = rtc_get_time(); - const time_t today_midnight = time_util_get_midnight_of(current_time); - const time_t alarm_midnight = time_util_get_midnight_of(alarm_time_epoch); - const bool is_alarm_for_today = (alarm_midnight == today_midnight); - - const size_t alarm_subtitle_size = sizeof(alarms_glance->subtitle); - - // Only show the day of the week if the alarm is not for today - if (!is_alarm_for_today) { - // Get a string for the abbreviated day of the week in the user's locale - char day_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; - struct tm alarm_time; - localtime_r(&alarm_time_epoch, &alarm_time); - strftime(day_buffer, sizeof(day_buffer), "%a", &alarm_time); - - snprintf(alarms_glance->subtitle, alarm_subtitle_size, "%s, %s", day_buffer, time_buffer); - } else { - strncpy(alarms_glance->subtitle, time_buffer, alarm_subtitle_size); - alarms_glance->subtitle[alarm_subtitle_size - 1] = '\0'; - } - } - - // Update the icon - prv_set_glance_icon(alarms_glance, new_icon_resource_id); -} - -static void prv_alarm_clock_event_handler(PBL_UNUSED PebbleEvent *event, void *context) { - LauncherAppGlanceStructured *structured_glance = context; - LauncherAppGlanceAlarms *alarms_glance = - launcher_app_glance_structured_get_data(structured_glance); - - prv_update_glance_for_next_alarm(alarms_glance); - - // Broadcast to the service that we changed the glance - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); -} - -static const LauncherAppGlanceStructuredImpl s_alarms_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_alarms_create(const AppMenuNode *node) { - PBL_ASSERTN(node); - - LauncherAppGlanceAlarms *alarms_glance = app_zalloc_check(sizeof(*alarms_glance)); - - // Copy the name of the Alarms app as the title - const size_t title_size = sizeof(alarms_glance->title); - strncpy(alarms_glance->title, node->name, title_size); - alarms_glance->title[title_size - 1] = '\0'; - - // Save the default app icon resource ID - alarms_glance->default_icon_resource_id = node->icon_resource_id; - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_alarms_structured_glance_impl, - should_consider_slices, alarms_glance); - PBL_ASSERTN(structured_glance); - - // Get the first state of the glance - prv_update_glance_for_next_alarm(alarms_glance); - - // Subscribe to alarm clock events for updating the glance - alarms_glance->alarm_clock_event_info = (EventServiceInfo) { - .type = PEBBLE_ALARM_CLOCK_EVENT, - .handler = prv_alarm_clock_event_handler, - .context = structured_glance, - }; - event_service_client_subscribe(&alarms_glance->alarm_clock_event_info); - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.h deleted file mode 100644 index 92fb93cb47..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_alarms_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.c deleted file mode 100644 index 143ad1a9cd..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_generic.h" - -#include "launcher_app_glance_structured.h" - -#include "applib/app_glance.h" -#include "applib/app_timer.h" -#include "applib/template_string.h" -#include "applib/ui/kino/kino_reel.h" -#include "apps/system_apps/timeline/text_node.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "process_management/pebble_process_info.h" -#include "services/normal/timeline/timeline_resources.h" -#include "system/passert.h" -#include "util/string.h" -#include "util/struct.h" - -#define APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR (PROCESS_INFO_FIRST_4X_SDK_VERSION_MAJOR) -#define APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MINOR (PROCESS_INFO_FIRST_4X_SDK_VERSION_MINOR) - -typedef struct LauncherAppGlanceGeneric { - //! The title that will be displayed - char title_buffer[APP_NAME_SIZE_BYTES]; - //! The icon that will be displayed - KinoReel *displayed_icon; - //! The resource info of the displayed icon - AppResourceInfo displayed_icon_resource_info; - //! The resource info of the default app icon - AppResourceInfo default_icon_resource_info; - //! Fallback icon to use if other icons aren't available; owned by client - const KinoReel *fallback_icon; - //! The resource ID of the fallback icon; used for comparisons - uint32_t fallback_icon_resource_id; - //! App timer used for re-evaluating the current slice's subtitle template string - AppTimer *slice_subtitle_template_string_reeval_timer; - //! UTC timestamp of when the current slice's subtitle template string must be re-evaluated - //! A zero value indicates that there is no need to re-evaluate the subtitle template string - time_t next_slice_subtitle_template_string_reeval_time; - //! Whether to use the legacy 28x28 icon size limit - bool use_legacy_28x28_icon_size_limit; -} LauncherAppGlanceGeneric; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceGeneric *generic_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(generic_glance, displayed_icon, NULL); -} - -static void prv_generic_glance_destroy_displayed_icon(LauncherAppGlanceGeneric *generic_glance) { - // Only delete the displayed icon if it doesn't match the fallback icon because we don't own it - if (generic_glance && (generic_glance->displayed_icon != generic_glance->fallback_icon)) { - kino_reel_destroy(generic_glance->displayed_icon); - } -} - -static KinoReel *prv_create_glance_icon(const AppResourceInfo *res_info, - bool legacy_icon_size_limit) { - if (!res_info) { - return NULL; - } - - KinoReel *icon = kino_reel_create_with_resource_system(res_info->res_app_num, res_info->res_id); - if (!icon) { - return NULL; - } - - const GSize size = kino_reel_get_size(icon); - // Not const to deal with horrifying GCC bug - GSize max_size = legacy_icon_size_limit ? LAUNCHER_APP_GLANCE_STRUCTURED_ICON_LEGACY_MAX_SIZE - : LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE; - if ((size.w > max_size.w) || - (size.h > max_size.h)) { - // The icon is too big - kino_reel_destroy(icon); - return NULL; - } - - return icon; -} - -static bool prv_app_resource_info_equal(const AppResourceInfo *a, const AppResourceInfo *b) { - PBL_ASSERTN(a && b); - return ((a == b) || ((a->res_id == b->res_id) && (a->res_app_num == b->res_app_num))); -} - -static void prv_generic_glance_set_icon(LauncherAppGlanceGeneric *generic_glance, - const AppResourceInfo *res_info) { - if (!generic_glance || !res_info) { - return; - } - - const bool is_requested_resource_the_default_icon = - ((res_info->res_app_num == generic_glance->default_icon_resource_info.res_app_num) && - ((res_info->res_id == APP_GLANCE_SLICE_DEFAULT_ICON) || - (res_info->res_id == generic_glance->default_icon_resource_info.res_id))); - const bool does_default_icon_need_to_be_loaded = - (is_requested_resource_the_default_icon && - !prv_app_resource_info_equal(&generic_glance->displayed_icon_resource_info, - &generic_glance->default_icon_resource_info)); - const bool is_icon_stale = - (!prv_app_resource_info_equal(&generic_glance->displayed_icon_resource_info, res_info)); - - if (generic_glance->displayed_icon && - !does_default_icon_need_to_be_loaded && - !is_icon_stale) { - // Nothing to do, bail out - return; - } - - // Destroy the currently displayed icon - prv_generic_glance_destroy_displayed_icon(generic_glance); - - AppResourceInfo res_info_to_load = *res_info; - - // Set the resource info to the real default icon resource info if the default icon was requested - if (is_requested_resource_the_default_icon) { - res_info_to_load = generic_glance->default_icon_resource_info; - } - - const bool legacy_icon_size_limit = generic_glance->use_legacy_28x28_icon_size_limit; - // Try loading the requested icon - generic_glance->displayed_icon = prv_create_glance_icon(&res_info_to_load, - legacy_icon_size_limit); - - if (!generic_glance->displayed_icon) { - // Try again with the app's default icon if we didn't just try it - if (!prv_app_resource_info_equal(&res_info_to_load, - &generic_glance->default_icon_resource_info)) { - res_info_to_load = generic_glance->default_icon_resource_info; - generic_glance->displayed_icon = prv_create_glance_icon(&res_info_to_load, - legacy_icon_size_limit); - } - - // If we don't have a valid icon at this point, use the fallback icon (casting to non-const so - // we can use it) - if (!generic_glance->displayed_icon && generic_glance->fallback_icon) { - // Note that this (reasonably) assumes that the system fallback icon is a system icon - res_info_to_load = (AppResourceInfo) { - .res_app_num = SYSTEM_APP, - .res_id = generic_glance->fallback_icon_resource_id, - }; - generic_glance->displayed_icon = (KinoReel *)generic_glance->fallback_icon; - } - } - - // We require that we have some sort of icon at this point - PBL_ASSERTN(generic_glance->displayed_icon); - - // Update our recording of the resource info of the displayed icon - generic_glance->displayed_icon_resource_info = res_info_to_load; -} - -static void prv_cancel_subtitle_reeval_timer(LauncherAppGlanceGeneric *generic_glance) { - if (!generic_glance) { - return; - } - - if (generic_glance->slice_subtitle_template_string_reeval_timer) { - app_timer_cancel(generic_glance->slice_subtitle_template_string_reeval_timer); - generic_glance->slice_subtitle_template_string_reeval_timer = NULL; - } - - // Set the next re-evaluation time to "never" - generic_glance->next_slice_subtitle_template_string_reeval_time = 0; -} - -static void prv_subtitle_reeval_timer_cb(void *data) { - LauncherAppGlanceStructured *structured_glance = data; - PBL_ASSERTN(structured_glance); - LauncherAppGlanceGeneric *generic_glance = - launcher_app_glance_structured_get_data(structured_glance); - - // Reset the timer - generic_glance->slice_subtitle_template_string_reeval_timer = NULL; - prv_cancel_subtitle_reeval_timer(generic_glance); - - // Notify the service that the glance changed - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); -} - - -static void prv_update_subtitle_template_string_reeval_timer_if_necessary( - LauncherAppGlanceStructured *structured_glance, time_t new_reeval_time) { - LauncherAppGlanceGeneric *generic_glance = - launcher_app_glance_structured_get_data(structured_glance); - const time_t existing_reeval_time = - generic_glance->next_slice_subtitle_template_string_reeval_time; - // Bail out if the new re-evaluation time is not earlier than the existing one we have - if ((new_reeval_time == 0) || - ((existing_reeval_time != 0) && (new_reeval_time >= existing_reeval_time))) { - return; - } - - const int time_until_next_reeval = new_reeval_time - rtc_get_time(); - // On the off chance that we missed the reeval, immediately call the timer callback - if (time_until_next_reeval <= 0) { - prv_subtitle_reeval_timer_cb(structured_glance); - return; - } - - const uint64_t time_until_next_reeval_ms = (uint64_t)time_until_next_reeval * MS_PER_SECOND; - if (time_until_next_reeval_ms > UINT32_MAX) { - // Next reeval time is so far in the future that its offset in milliseconds from the - // current time would overflow the argument to AppTimer, so just ignore this reeval because it's - // not worth setting a timer for it - return; - } - - prv_cancel_subtitle_reeval_timer(generic_glance); - - generic_glance->slice_subtitle_template_string_reeval_timer = - app_timer_register((uint32_t)time_until_next_reeval_ms, prv_subtitle_reeval_timer_cb, - structured_glance); - generic_glance->next_slice_subtitle_template_string_reeval_time = new_reeval_time; -} - -static void prv_current_slice_updated(LauncherAppGlance *glance) { - // Ignore slices that aren't of the IconAndSubtitle type beyond this point for now - if (glance->current_slice.type != AppGlanceSliceType_IconAndSubtitle) { - return; - } - - LauncherAppGlanceStructured *structured_glance = (LauncherAppGlanceStructured *)glance; - LauncherAppGlanceGeneric *generic_glance = - launcher_app_glance_structured_get_data(structured_glance); - - const TimelineResourceId timeline_res_id = - (TimelineResourceId)glance->current_slice.icon_and_subtitle.icon_resource_id; - - // Initialize the resource info to be the default icon - AppResourceInfo resource_info = generic_glance->default_icon_resource_info; - // Override it if we have a valid timeline resource ID from the new app glance slice - if (timeline_res_id != APP_GLANCE_SLICE_DEFAULT_ICON) { - // NOTE: This variant of the timeline_resources_get_id() function is safe to call here with - // respect to the app supporting published resources because we only consider slices if the - // glance is for a system app (where it doesn't matter) or for apps that were compiled with - // an SDK that supports app glances (which is newer than the first SDK that supported published - // resources as proved by the following asserts) - _Static_assert((APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR > - TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR) || - ((APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR == - TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MAJOR) && - (APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MINOR >= - TIMELINE_RESOURCE_PBW_SUPPORT_FIRST_SDK_VERSION_MINOR)), - "App glance min supported SDK version must be equal to or newer than first " - "timeline/published resource PBW supported SDK version"); - timeline_resources_get_id_system(timeline_res_id, LAUNCHER_APP_GLANCE_GENERIC_ICON_SIZE_TYPE, - resource_info.res_app_num, &resource_info); - } - - prv_generic_glance_set_icon(generic_glance, &resource_info); - - prv_cancel_subtitle_reeval_timer(generic_glance); - - // The glance will automatically be redrawn after this function is called (which will also update - // the glance's state regarding its subtitle template string), so no need to mark it as dirty - // (see launcher_app_glance_update_current_slice()) -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceGeneric *generic_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(generic_glance, title_buffer, NULL); -} - -static void prv_generic_glance_dynamic_text_node_update(PBL_UNUSED GContext *ctx, - PBL_UNUSED GTextNode *node, - PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, - PBL_UNUSED bool render, char *buffer, - size_t buffer_size, void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - if (!structured_glance) { - return; - } else if (structured_glance->glance.current_slice.type != AppGlanceSliceType_IconAndSubtitle) { - PBL_LOG(LOG_LEVEL_WARNING, "Generic glance doesn't know how to handle slice type %d", - structured_glance->glance.current_slice.type); - return; - } - - // Evaluate the subtitle as a template string - const char *subtitle_template_string = - structured_glance->glance.current_slice.icon_and_subtitle.template_string; - TemplateStringEvalConditions template_string_reeval_conditions = {0}; - const TemplateStringVars template_string_vars = (TemplateStringVars) { - .current_time = rtc_get_time(), - }; - TemplateStringError template_string_error = {0}; - template_string_evaluate(subtitle_template_string, buffer, buffer_size, - &template_string_reeval_conditions, &template_string_vars, - &template_string_error); - - if (template_string_error.status != TemplateStringErrorStatus_Success) { - // Zero out the buffer and return - buffer[0] = '\0'; - PBL_LOG(LOG_LEVEL_WARNING, "Error at index %zu in evaluating template string: %s", - template_string_error.index_in_string, subtitle_template_string); - return; - } - - // Update the timer for re-evaluating the template string, if necessary - prv_update_subtitle_template_string_reeval_timer_if_necessary( - structured_glance, template_string_reeval_conditions.eval_time); -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_generic_glance_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceGeneric *generic_glance = - launcher_app_glance_structured_get_data(structured_glance); - prv_cancel_subtitle_reeval_timer(generic_glance); - prv_generic_glance_destroy_displayed_icon(generic_glance); - app_free(generic_glance); -} - -static const LauncherAppGlanceStructuredImpl s_generic_structured_glance_impl = { - .base_handlers.current_slice_updated = prv_current_slice_updated, - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_generic_create(const AppMenuNode *node, - const KinoReel *fallback_icon, - uint32_t fallback_icon_resource_id) { - if (!node) { - return NULL; - } - - LauncherAppGlanceGeneric *generic_glance = app_zalloc_check(sizeof(*generic_glance)); - const size_t title_buffer_size = sizeof(generic_glance->title_buffer); - strncpy(generic_glance->title_buffer, node->name, title_buffer_size); - generic_glance->title_buffer[title_buffer_size - 1] = '\0'; - generic_glance->default_icon_resource_info = (AppResourceInfo) { - .res_app_num = node->app_num, - .res_id = node->icon_resource_id, - }; - generic_glance->fallback_icon = fallback_icon; - generic_glance->fallback_icon_resource_id = fallback_icon_resource_id; - - const Version app_glance_min_supported_sdk_version = (Version) { - .major = APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MAJOR, - .minor = APP_GLANCE_MIN_SUPPORTED_SDK_VERSION_MINOR, - }; - - const bool app_glances_supported = - (version_compare(node->sdk_version, app_glance_min_supported_sdk_version) >= 0); - - generic_glance->use_legacy_28x28_icon_size_limit = !app_glances_supported; - - // Our unit tests rely on system app icons for testing generic glances which means - // the != SYSTEM_APP condition can't be satisfied easily, so just skip this part for unit tests -#if !UNITTEST - // Only consider slices for non-system apps that were compiled with an SDK that supports glances - const bool should_consider_slices = (node->app_num != SYSTEM_APP) && - app_glances_supported; -#else - const bool should_consider_slices = true; -#endif - - prv_generic_glance_set_icon(generic_glance, &generic_glance->default_icon_resource_info); - - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_generic_structured_glance_impl, - should_consider_slices, generic_glance); - if (structured_glance) { - if (generic_glance->use_legacy_28x28_icon_size_limit) { - launcher_app_glance_structured_set_icon_max_size(structured_glance, - LAUNCHER_APP_GLANCE_STRUCTURED_ICON_LEGACY_MAX_SIZE); - } - return &structured_glance->glance; - } else { - return NULL; - } -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.h deleted file mode 100644 index ffa58beb17..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "applib/ui/kino/kino_reel.h" -#include "process_management/app_menu_data_source.h" -#include "services/normal/timeline/timeline_resources.h" - -#define LAUNCHER_APP_GLANCE_GENERIC_ICON_SIZE_TYPE (TimelineResourceSizeTiny) - -//! Create a generic launcher app glance for the provided app menu node. -//! @param node The node that the new generic glance should represent -//! @param fallback_icon A long-lived fallback icon to use if no other icons are available; will -//! not be destroyed when the generic glance is destroyed -//! @param fallback_icon_resource_id The resource ID of the fallback icon -LauncherAppGlance *launcher_app_glance_generic_create(const AppMenuNode *node, - const KinoReel *fallback_icon, - uint32_t fallback_icon_resource_id); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.c deleted file mode 100644 index 14965f0f3c..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_music.h" - -#include "launcher_app_glance_structured.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/normal/music.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -// We need enough space for the track artist and title (so 2 * MUSIC_BUFFER_LENGTH from music.h), -// the delimiter string " - " (3), and 1 for the null terminator -#define TRACK_TEXT_BUFFER_SIZE ((MUSIC_BUFFER_LENGTH * 2) + 3 + 1) - -typedef struct LauncherAppGlanceMusic { - char title[APP_NAME_SIZE_BYTES]; - char subtitle[TRACK_TEXT_BUFFER_SIZE]; - KinoReel *icon; - uint32_t icon_resource_id; - uint32_t default_icon_resource_id; - EventServiceInfo music_event_info; -} LauncherAppGlanceMusic; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceMusic *music_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(music_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceMusic *music_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(music_glance, title, NULL); -} - -static void prv_music_glance_subtitle_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceMusic *music_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (music_glance) { - strncpy(buffer, music_glance->subtitle, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_music_glance_subtitle_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceMusic *music_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (music_glance) { - event_service_client_unsubscribe(&music_glance->music_event_info); - kino_reel_destroy(music_glance->icon); - } - app_free(music_glance); -} - -static void prv_set_glance_icon(LauncherAppGlanceMusic *music_glance, - uint32_t new_icon_resource_id) { - if (music_glance->icon_resource_id == new_icon_resource_id) { - // Nothing to do, bail out - return; - } - - // Destroy the existing icon - kino_reel_destroy(music_glance->icon); - - // Set the new icon and record its resource ID - // TODO PBL-38539: Switch from using a regular resource ID to using a TimelineResourceId - music_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); - PBL_ASSERTN(music_glance->icon); - music_glance->icon_resource_id = new_icon_resource_id; -} - -static bool prv_should_display_music_state(MusicPlayState play_state, - uint32_t last_updated_time_elapsed_ms) { - const uint32_t music_last_updated_display_threshold_ms = 30 * MS_PER_SECOND * SECONDS_PER_MINUTE; - - switch (play_state) { - case MusicPlayStatePlaying: - case MusicPlayStateForwarding: - case MusicPlayStateRewinding: - return true; - case MusicPlayStatePaused: - // We won't display the music state if the music is paused and it hasn't changed in a while - return (last_updated_time_elapsed_ms < music_last_updated_display_threshold_ms); - case MusicPlayStateUnknown: - case MusicPlayStateInvalid: - return false; - } - WTF; -} - -static void prv_update_glance_for_music_state(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceMusic *music_glance = - launcher_app_glance_structured_get_data(structured_glance); - PBL_ASSERTN(music_glance); - - // Zero out the glance's subtitle buffer - const size_t music_glance_subtitle_size = sizeof(music_glance->subtitle); - memset(music_glance->subtitle, 0, music_glance_subtitle_size); - - // Default to showing the default icon - uint32_t new_icon_resource_id = music_glance->default_icon_resource_id; - - // Determine if we should display the current music state - const MusicPlayState play_state = music_get_playback_state(); - const uint32_t last_updated_time_elapsed_ms = music_get_ms_since_pos_last_updated(); - const bool should_display_music_state = - prv_should_display_music_state(play_state, last_updated_time_elapsed_ms); - - if (should_display_music_state) { - // Get the artist and title strings for the music playing or paused - char artist_buffer[MUSIC_BUFFER_LENGTH] = {}; - char title_buffer[MUSIC_BUFFER_LENGTH] = {}; - music_get_now_playing(title_buffer, artist_buffer, NULL /* album_buffer */); - - // Only populate the glance with music info if we have both an artist string and a title string - if (!IS_EMPTY_STRING(artist_buffer) && !IS_EMPTY_STRING(title_buffer)) { - // Use the strings to fill the subtitle buffer - snprintf(music_glance->subtitle, music_glance_subtitle_size, "%s - %s", artist_buffer, - title_buffer); - - // Choose the icon we should display; note that we'll use the default icon we set above if we - // don't have an icon for the current play state - if (play_state == MusicPlayStatePlaying) { - new_icon_resource_id = RESOURCE_ID_MUSIC_APP_GLANCE_PLAY; - } else if (play_state == MusicPlayStatePaused) { - new_icon_resource_id = RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE; - } - } - } - - // Update the glance icon - prv_set_glance_icon(music_glance, new_icon_resource_id); - - // Broadcast to the service that we changed the glance - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); -} - -static void prv_music_event_handler(PebbleEvent *event, void *context) { - switch (event->media.type) { - case PebbleMediaEventTypeNowPlayingChanged: - case PebbleMediaEventTypePlaybackStateChanged: - case PebbleMediaEventTypeServerConnected: - case PebbleMediaEventTypeServerDisconnected: - prv_update_glance_for_music_state(context); - return; - case PebbleMediaEventTypeVolumeChanged: - case PebbleMediaEventTypeTrackPosChanged: - return; - } - WTF; -} - -static const LauncherAppGlanceStructuredImpl s_music_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_music_create(const AppMenuNode *node) { - PBL_ASSERTN(node); - - LauncherAppGlanceMusic *music_glance = app_zalloc_check(sizeof(*music_glance)); - - // Copy the name of the Music app as the title - const size_t title_size = sizeof(music_glance->title); - strncpy(music_glance->title, node->name, title_size); - music_glance->title[title_size - 1] = '\0'; - - // Save the default icon resource ID for the Music app - music_glance->default_icon_resource_id = node->icon_resource_id; - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_music_structured_glance_impl, - should_consider_slices, music_glance); - PBL_ASSERTN(structured_glance); - - // Get the first state of the glance - prv_update_glance_for_music_state(structured_glance); - - // Subscribe to music events for updating the glance - music_glance->music_event_info = (EventServiceInfo) { - .type = PEBBLE_MEDIA_EVENT, - .handler = prv_music_event_handler, - .context = structured_glance, - }; - event_service_client_subscribe(&music_glance->music_event_info); - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.h deleted file mode 100644 index 889fe22dfe..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_music_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.c deleted file mode 100644 index 491ef8e8af..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_notifications.h" - -#include "launcher_app_glance_structured.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/timeline/attribute.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -typedef struct LauncherAppGlanceNotifications { - char title[APP_NAME_SIZE_BYTES]; - char subtitle[ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN]; - KinoReel *icon; - EventServiceInfo notification_event_info; -} LauncherAppGlanceNotifications; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceNotifications *notifications_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(notifications_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceNotifications *notifications_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(notifications_glance, title, NULL); -} - -static void prv_notifications_glance_subtitle_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceNotifications *notifications_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (notifications_glance) { - strncpy(buffer, notifications_glance->subtitle, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_notifications_glance_subtitle_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceNotifications *notifications_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (notifications_glance) { - event_service_client_unsubscribe(¬ifications_glance->notification_event_info); - kino_reel_destroy(notifications_glance->icon); - } - app_free(notifications_glance); -} - -static bool prv_notification_iterator_cb(void *data, SerializedTimelineItemHeader *header_id) { - Uuid *last_notification_received_id = data; - - // The iterator proceeds from the first notification received to the last notification received, - // so copy the ID of the current notification and then return true so we iterate until the end. - // Thus the last ID we save will be the last notification received. - *last_notification_received_id = header_id->common.id; - - return true; -} - -static void prv_update_glance_for_last_notification_received( - LauncherAppGlanceNotifications *notifications_glance) { - // Find the ID of the last notification received - Uuid last_notification_received_id; - notification_storage_iterate(prv_notification_iterator_cb, &last_notification_received_id); - - TimelineItem notification; - if (!notification_storage_get(&last_notification_received_id, ¬ification)) { - // We couldn't load the notification for some reason; just bail out with the subtitle cleared - notifications_glance->subtitle[0] = '\0'; - return; - } - - const char *title = attribute_get_string(¬ification.attr_list, AttributeIdTitle, ""); - const char *subtitle = attribute_get_string(¬ification.attr_list, AttributeIdSubtitle, ""); - const char *body = attribute_get_string(¬ification.attr_list, AttributeIdBody, ""); - - // Determine which string we should use in the glance subtitle - const char *string_to_use_in_glance_subtitle = ""; - if (!IS_EMPTY_STRING(title)) { - string_to_use_in_glance_subtitle = title; - } else if (!IS_EMPTY_STRING(subtitle)) { - // Fallback to the subtitle - string_to_use_in_glance_subtitle = subtitle; - } else { - // Fallback to the body - string_to_use_in_glance_subtitle = body; - } - - // Copy the string to the glance - const size_t glance_subtitle_size = sizeof(notifications_glance->subtitle); - strncpy(notifications_glance->subtitle, string_to_use_in_glance_subtitle, glance_subtitle_size); - notifications_glance->subtitle[glance_subtitle_size - 1] = '\0'; -} - -static void prv_notification_event_handler(PebbleEvent *event, void *context) { - LauncherAppGlanceStructured *structured_glance = context; - LauncherAppGlanceNotifications *notifications_glance = - launcher_app_glance_structured_get_data(structured_glance); - switch (event->sys_notification.type) { - case NotificationAdded: - case NotificationRemoved: - prv_update_glance_for_last_notification_received(notifications_glance); - // Broadcast to the service that we changed the glance - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); - return; - case NotificationActedUpon: - case NotificationActionResult: - return; - } - WTF; -} - -static const LauncherAppGlanceStructuredImpl s_notifications_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_notifications_create(const AppMenuNode *node) { - PBL_ASSERTN(node); - - LauncherAppGlanceNotifications *notifications_glance = - app_zalloc_check(sizeof(*notifications_glance)); - - // Copy the name of the Notifications app as the title - const size_t title_size = sizeof(notifications_glance->title); - strncpy(notifications_glance->title, node->name, title_size); - notifications_glance->title[title_size - 1] = '\0'; - - // Create the icon for the Notifications app - notifications_glance->icon = kino_reel_create_with_resource_system(node->app_num, - node->icon_resource_id); - PBL_ASSERTN(notifications_glance->icon); - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_notifications_structured_glance_impl, - should_consider_slices, notifications_glance); - PBL_ASSERTN(structured_glance); - - // Get the first state of the glance - prv_update_glance_for_last_notification_received(notifications_glance); - - // Subscribe to notification events for updating the glance - notifications_glance->notification_event_info = (EventServiceInfo) { - .type = PEBBLE_SYS_NOTIFICATION_EVENT, - .handler = prv_notification_event_handler, - .context = structured_glance, - }; - event_service_client_subscribe(¬ifications_glance->notification_event_info); - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.h deleted file mode 100644 index eb4fd3ccb9..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_notifications_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.c deleted file mode 100644 index b623cde3d7..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance.h" - -#include "applib/ui/kino/kino_reel_custom.h" -#include "system/passert.h" -#include "util/struct.h" - -GSize launcher_app_glance_get_size_for_reel(KinoReel *reel) { - PBL_ASSERTN(reel->impl && (reel->impl->reel_type == KinoReelTypeCustom)); - LauncherAppGlance *glance = kino_reel_custom_get_data(reel); - return NULL_SAFE_FIELD_ACCESS(glance, size, GSizeZero); -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.h deleted file mode 100644 index 2bb22c540a..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/kino/kino_reel.h" - -//! Get the size of the provided reel that implements how a launcher app glance should be drawn. -//! @param reel The reel that implements how a glance should be drawn -//! @return The size of the reel -GSize launcher_app_glance_get_size_for_reel(KinoReel *reel); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.c deleted file mode 100644 index d8c8bd2daa..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_service.h" - -#include "launcher_app_glance.h" -#include "launcher_app_glance_alarms.h" -#include "launcher_app_glance_generic.h" -#include "launcher_app_glance_music.h" -#include "launcher_app_glance_notifications.h" -#include "launcher_app_glance_settings.h" -#include "launcher_app_glance_watchfaces.h" -#include "launcher_app_glance_weather.h" -#include "launcher_app_glance_workout.h" -#include "launcher_menu_layer.h" -#include "launcher_menu_layer_private.h" - -#include "applib/app_glance.h" -#include "applib/ui/kino/kino_reel.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/size.h" -#include "util/struct.h" -#include "util/uuid.h" - -//! Cache twice the number of glances we'll show simultaneously in the launcher -#define LAUNCHER_APP_GLANCE_SERVICE_CACHE_NUM_ENTRIES (2 * LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS) - -typedef struct LauncherAppGlanceCacheEntry { - ListNode node; - LauncherAppGlance *glance; -} LauncherAppGlanceCacheEntry; - -////////////////////////////////////// -// KinoPlayer callbacks - -static void prv_glance_reel_player_frame_did_change_cb(KinoPlayer *player, void *context) { - LauncherAppGlanceService *service = context; - launcher_app_glance_service_notify_glance_changed(service); -} - -/////////////////////////// -// Slice expiration timer - -static void prv_slice_expiration_timer_cb(void *data); - -static void prv_reset_slice_expiration_timer(LauncherAppGlanceService *service) { - if (!service) { - return; - } - - if (service->slice_expiration_timer) { - app_timer_cancel(service->slice_expiration_timer); - service->slice_expiration_timer = NULL; - } - - // Set the next slice expiration time to "never" - service->next_slice_expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION; -} - -static void prv_update_slice_expiration_timer_if_necessary(LauncherAppGlanceService *service, - time_t new_slice_expire_time) { - const time_t next_slice_expiration_time = service->next_slice_expiration_time; - const bool is_new_slice_expire_time_earlier_than_existing_earliest = - (new_slice_expire_time != APP_GLANCE_SLICE_NO_EXPIRATION) && - ((next_slice_expiration_time == APP_GLANCE_SLICE_NO_EXPIRATION) || - (new_slice_expire_time < next_slice_expiration_time)); - if (!is_new_slice_expire_time_earlier_than_existing_earliest) { - return; - } - - const int time_until_slice_expires = new_slice_expire_time - rtc_get_time(); - // On the off chance that this slice has already expired, immediately call the timer callback - if (time_until_slice_expires <= 0) { - prv_slice_expiration_timer_cb(service); - return; - } - - const uint64_t time_until_slice_expires_ms = (uint64_t)time_until_slice_expires * MS_PER_SECOND; - if (time_until_slice_expires_ms > UINT32_MAX) { - // Slice expiration time is so far in the future that its offset in milliseconds from the - // current time would overflow the argument to AppTimer, so just ignore this slice because it's - // not worth setting a timer for it - return; - } - - prv_reset_slice_expiration_timer(service); - - service->slice_expiration_timer = - app_timer_register((uint32_t)time_until_slice_expires_ms, prv_slice_expiration_timer_cb, - service); - service->next_slice_expiration_time = new_slice_expire_time; -} - -static bool prv_glance_cache_slice_expiration_foreach_cb(ListNode *node, void *context) { - LauncherAppGlanceService *service = context; - PBL_ASSERTN(service); - - const LauncherAppGlanceCacheEntry *entry = (LauncherAppGlanceCacheEntry *)node; - LauncherAppGlance *glance = entry->glance; - - // Update the glance's current slice - launcher_app_glance_update_current_slice(glance); - - // If necessary, update the slice expiration timer with the updated current slice - prv_update_slice_expiration_timer_if_necessary(service, glance->current_slice.expiration_time); - - // Continue iterating until we've looked at all of the glances in the cache - return true; -} - -static void prv_slice_expiration_timer_cb(void *data) { - LauncherAppGlanceService *service = data; - PBL_ASSERTN(service); - - // Reset the timer - prv_reset_slice_expiration_timer(service); - - // Iterate over the glances in the cache to find the next earliest expiring slice - list_foreach(service->glance_cache, prv_glance_cache_slice_expiration_foreach_cb, service); -} - -///////////////////// -// Glance cache - -_Static_assert((offsetof(LauncherAppGlanceCacheEntry, node) == 0), - "ListNode is not the first field of LauncherAppGlanceCacheEntry"); - -static void prv_glance_cache_destroy_entry(LauncherAppGlanceService *service, - LauncherAppGlanceCacheEntry *entry) { - if (!entry) { - return; - } - - if (service) { - PBL_ASSERTN(entry->glance); - KinoReel *glance_reel = entry->glance->reel; - - // Set the glance reel player's reel to NULL if it belongs to the glance we're going to destroy - KinoPlayer *glance_reel_player = &service->glance_reel_player; - if (glance_reel && (glance_reel == kino_player_get_reel(glance_reel_player))) { - kino_player_set_reel(glance_reel_player, NULL, false /* take_ownership */); - } - } - - launcher_app_glance_destroy(entry->glance); - app_free(entry); -} - -static bool prv_glance_cache_deinit_foreach_cb(ListNode *node, void *context) { - LauncherAppGlanceService *service = context; - LauncherAppGlanceCacheEntry *entry = (LauncherAppGlanceCacheEntry *)node; - - prv_glance_cache_destroy_entry(service, entry); - // Continue iterating to destroy all of the entries - return true; -} - -static void prv_glance_cache_deinit(LauncherAppGlanceService *service) { - if (service) { - list_foreach(service->glance_cache, prv_glance_cache_deinit_foreach_cb, service); - service->glance_cache = NULL; - } -} - -//! Don't call this directly; it's used by prv_get_glance_for_node() below -static void prv_glance_cache_put(LauncherAppGlanceService *service, const Uuid *uuid, - LauncherAppGlance *glance) { - if (!service || !uuid || !glance) { - return; - } - - // If necessary, evict the LRU cache entry - const uint32_t cache_entry_count = list_count(service->glance_cache); - PBL_ASSERTN(cache_entry_count <= LAUNCHER_APP_GLANCE_SERVICE_CACHE_NUM_ENTRIES); - if (cache_entry_count == LAUNCHER_APP_GLANCE_SERVICE_CACHE_NUM_ENTRIES) { - LauncherAppGlanceCacheEntry *cache_entry_to_destroy = - (LauncherAppGlanceCacheEntry *)list_get_tail(service->glance_cache); - list_remove(&cache_entry_to_destroy->node, &service->glance_cache, NULL); - prv_glance_cache_destroy_entry(service, cache_entry_to_destroy); - } - - // Initialize a new cache entry, add it to the head of the cache list, and return it - LauncherAppGlanceCacheEntry *new_cache_entry = app_zalloc_check(sizeof(*new_cache_entry)); - *new_cache_entry = (LauncherAppGlanceCacheEntry) { - .glance = glance, - }; - service->glance_cache = list_insert_before(service->glance_cache, &new_cache_entry->node); -} - -static bool prv_glance_cache_entry_find_cb(ListNode *current_node, void *context) { - LauncherAppGlanceCacheEntry *current_entry = (LauncherAppGlanceCacheEntry *)current_node; - Uuid *uuid_to_find = context; - return (current_entry && uuid_equal(¤t_entry->glance->uuid, uuid_to_find)); -} - -static LauncherAppGlance *prv_load_glance_for_node(const AppMenuNode *node, - LauncherAppGlanceService *service) { - typedef struct { - Uuid uuid; - LauncherAppGlance* (*constructor)(const AppMenuNode *); - } SystemAppGlanceFactory; - - static const SystemAppGlanceFactory s_system_glance_factories[] = { - { // Settings - .uuid = {0x07, 0xe0, 0xd9, 0xcb, 0x89, 0x57, 0x4b, 0xf7, - 0x9d, 0x42, 0x35, 0xbf, 0x47, 0xca, 0xad, 0xfe}, - .constructor = launcher_app_glance_settings_create, - }, - { // Music - .uuid = {0x1f, 0x03, 0x29, 0x3d, 0x47, 0xaf, 0x4f, 0x28, - 0xb9, 0x60, 0xf2, 0xb0, 0x2a, 0x6d, 0xd7, 0x57}, - .constructor = launcher_app_glance_music_create, - }, - { // Weather - .uuid = {0x61, 0xb2, 0x2b, 0xc8, 0x1e, 0x29, 0x46, 0xd, - 0xa2, 0x36, 0x3f, 0xe4, 0x9, 0xa4, 0x39, 0xff}, - .constructor = launcher_app_glance_weather_create, - }, - { // Notifications - .uuid = {0xb2, 0xca, 0xe8, 0x18, 0x10, 0xf8, 0x46, 0xdf, - 0xad, 0x2b, 0x98, 0xad, 0x22, 0x54, 0xa3, 0xc1}, - .constructor = launcher_app_glance_notifications_create, - }, - { // Alarms - .uuid = {0x67, 0xa3, 0x2d, 0x95, 0xef, 0x69, 0x46, 0xd4, - 0xa0, 0xb9, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9}, - .constructor = launcher_app_glance_alarms_create, - }, - { // Watchfaces - .uuid = {0x18, 0xe4, 0x43, 0xce, 0x38, 0xfd, 0x47, 0xc8, - 0x84, 0xd5, 0x6d, 0x0c, 0x77, 0x5f, 0xbe, 0x55}, - .constructor = launcher_app_glance_watchfaces_create, - }, - { - // Workout - .uuid = {0xfe, 0xf8, 0x2c, 0x82, 0x71, 0x76, 0x4e, 0x22, - 0x88, 0xde, 0x35, 0xa3, 0xfc, 0x18, 0xd4, 0x3f}, - .constructor = launcher_app_glance_workout_create, - }, - }; - - LauncherAppGlance *glance = NULL; - - // Check if the UUID matches a known system glance - for (unsigned int i = 0; i < ARRAY_LENGTH(s_system_glance_factories); i++) { - const SystemAppGlanceFactory *factory = &s_system_glance_factories[i]; - if (uuid_equal(&factory->uuid, &node->uuid)) { - glance = factory->constructor(node); - break; - } - } - - // If we haven't loaded a glance yet, try loading a generic glance for the node - if (!glance) { - glance = launcher_app_glance_generic_create(node, service->generic_glance_icon, - service->generic_glance_icon_resource_id); - } - - // If we successfully loaded a glance, set its service field - if (glance) { - glance->service = service; - } - - return glance; -} - -static LauncherAppGlanceCacheEntry *prv_find_glance_entry_in_cache( - LauncherAppGlanceService *service, Uuid *uuid) { - return (LauncherAppGlanceCacheEntry *)list_find(service->glance_cache, - prv_glance_cache_entry_find_cb, uuid); -} - -static LauncherAppGlance *prv_find_glance_in_cache(LauncherAppGlanceService *service, Uuid *uuid) { - const LauncherAppGlanceCacheEntry *entry = prv_find_glance_entry_in_cache(service, uuid); - return NULL_SAFE_FIELD_ACCESS(entry, glance, NULL); -} - -//! Request a glance for an icon ID from an "MRU linked list" (list sorted by accesses so that -//! most recent accesses are at the head of the list) -static LauncherAppGlance *prv_fetch_from_cache_or_load_glance_for_node(AppMenuNode *node, - LauncherAppGlanceService *service) { - if (!service || !node) { - return NULL; - } - - Uuid *uuid = &node->uuid; - - LauncherAppGlance *glance = NULL; - - // Try to find the requested glance in the cache - LauncherAppGlanceCacheEntry *cache_entry = prv_find_glance_entry_in_cache(service, uuid); - if (cache_entry) { - // Move the found cache entry to the front of the cache list (to mark it as "MRU") - // This makes it easy to remove the "LRU" entry later by simply removing the tail - list_remove(&cache_entry->node, &service->glance_cache, NULL); - service->glance_cache = list_insert_before(service->glance_cache, &cache_entry->node); - glance = cache_entry->glance; - } - - // Try to load the glance requested if we didn't find it in the cache - if (!glance) { - glance = prv_load_glance_for_node(node, service); - if (!glance) { - // Just bail out and don't modify the cache if we fail - return NULL; - } - - // Add the new glance to the cache - prv_glance_cache_put(service, uuid, glance); - } - - // Update the slice expiration timer if the glance's current slice expires soon - prv_update_slice_expiration_timer_if_necessary(service, glance->current_slice.expiration_time); - - return glance; -} - -static bool prv_should_use_glance_cache_for_app_with_uuid(const Uuid *uuid) { - // Use the glance cache only if the app does not have the system UUID (all zeros) - return !uuid_is_system(uuid); -} - -///////////////////// -// Glance events - -static void prv_handle_glance_event(PebbleEvent *event, void *context) { - LauncherAppGlanceService *service = context; - PBL_ASSERTN(service); - - // Update the current slice of the glance that was changed if the glance is in the cache - LauncherAppGlance *glance_in_cache = prv_find_glance_in_cache(service, - event->app_glance.app_uuid); - if (glance_in_cache) { - launcher_app_glance_update_current_slice(glance_in_cache); - - // If necessary, update the slice expiration timer with the updated current slice - prv_update_slice_expiration_timer_if_necessary(service, - glance_in_cache->current_slice.expiration_time); - } -} - -///////////////////// -// Public API - -void launcher_app_glance_service_draw_glance_for_app_node(LauncherAppGlanceService *service, - GContext *ctx, const GRect *frame, - bool is_highlighted, AppMenuNode *node) { - const bool use_glance_cache = prv_should_use_glance_cache_for_app_with_uuid(&node->uuid); - - LauncherAppGlance *glance = - use_glance_cache ? prv_fetch_from_cache_or_load_glance_for_node(node, service) : - prv_load_glance_for_node(node, service); - - // Draw the glance in the provided frame - launcher_app_glance_draw(ctx, frame, glance, is_highlighted); - - // If we didn't use the glance cache, destroy the glance now - if (!use_glance_cache) { - launcher_app_glance_destroy(glance); - } -} - -void launcher_app_glance_service_rewind_current_glance(LauncherAppGlanceService *service) { - if (!service) { - return; - } - kino_player_rewind(&service->glance_reel_player); -} - -void launcher_app_glance_service_pause_current_glance(LauncherAppGlanceService *service) { - if (!service) { - return; - } - kino_player_pause(&service->glance_reel_player); -} - -void launcher_app_glance_service_play_current_glance(LauncherAppGlanceService *service) { - if (!service) { - return; - } - kino_player_play(&service->glance_reel_player); -} - -void launcher_app_glance_service_play_glance_for_app_node(LauncherAppGlanceService *service, - AppMenuNode *node) { - if (!service || !node) { - return; - } - - KinoPlayer *glance_reel_player = &service->glance_reel_player; - - // Rewind the player for any previously played glance - kino_player_rewind(glance_reel_player); - - const bool use_glance_cache = prv_should_use_glance_cache_for_app_with_uuid(&node->uuid); - if (!use_glance_cache) { - // Don't play glances that we don't store in the cache since they don't live long enough - // to advance frames - return; - } - - LauncherAppGlance *glance = prv_fetch_from_cache_or_load_glance_for_node(node, service); - PBL_ASSERTN(glance); - kino_player_set_reel(glance_reel_player, glance->reel, false /* take_ownership */); - kino_player_play(glance_reel_player); -} - -void launcher_app_glance_service_notify_glance_changed(LauncherAppGlanceService *service) { - if (service && service->handlers.glance_changed) { - service->handlers.glance_changed(service->handlers_context); - } -} - -void launcher_app_glance_service_init(LauncherAppGlanceService *service, - uint32_t generic_glance_icon_resource_id) { - if (!service) { - return; - } - - *service = (LauncherAppGlanceService) {}; - - prv_reset_slice_expiration_timer(service); - - service->glance_event_info = (EventServiceInfo) { - .type = PEBBLE_APP_GLANCE_EVENT, - .handler = prv_handle_glance_event, - .context = service, - }; - event_service_client_subscribe(&service->glance_event_info); - - service->generic_glance_icon = kino_reel_create_with_resource(generic_glance_icon_resource_id); - PBL_ASSERTN(service->generic_glance_icon); - service->generic_glance_icon_resource_id = generic_glance_icon_resource_id; - - const KinoPlayerCallbacks glance_reel_player_callbacks = (KinoPlayerCallbacks) { - .frame_did_change = prv_glance_reel_player_frame_did_change_cb, - }; - kino_player_set_callbacks(&service->glance_reel_player, glance_reel_player_callbacks, service); -} - -void launcher_app_glance_service_set_handlers(LauncherAppGlanceService *service, - const LauncherAppGlanceServiceHandlers *handlers, - void *context) { - if (!service) { - return; - } else if (!handlers) { - service->handlers = (LauncherAppGlanceServiceHandlers) {}; - } else { - service->handlers = *handlers; - } - service->handlers_context = context; -} - -void launcher_app_glance_service_deinit(LauncherAppGlanceService *service) { - if (!service) { - return; - } - - kino_player_deinit(&service->glance_reel_player); - event_service_client_unsubscribe(&service->glance_event_info); - prv_glance_cache_deinit(service); - prv_reset_slice_expiration_timer(service); - kino_reel_destroy(service->generic_glance_icon); -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.h deleted file mode 100644 index a5869b3113..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/app_timer.h" -#include "applib/event_service_client.h" -#include "applib/ui/kino/kino_player.h" -#include "process_management/app_menu_data_source.h" -#include "util/list.h" - -//! Handler called when a glance in the service's cache changes, either because a glance's slice -//! expired or a glance was reloaded. -//! @param context Context provided when calling launcher_app_glance_service_set_handlers() -typedef void (*LauncherAppGlanceServiceGlanceChangedHandler)(void *context); - -typedef struct LauncherAppGlanceServiceHandlers { - LauncherAppGlanceServiceGlanceChangedHandler glance_changed; -} LauncherAppGlanceServiceHandlers; - -typedef struct LauncherAppGlanceService { - //! Cache of launcher app glances - ListNode *glance_cache; - //! Event service info used to subscribe to glance reload events - EventServiceInfo glance_event_info; - //! Client handlers set via launcher_app_glance_service_set_handlers() - LauncherAppGlanceServiceHandlers handlers; - //! Context for the handlers set via launcher_app_glance_service_set_handlers() - void *handlers_context; - //! The Unix epoch UTC timestamp of the next expiring slice of any of the glances in the cache - time_t next_slice_expiration_time; - //! App timer used for updating glances when a slice of a glance in the cache expires - AppTimer *slice_expiration_timer; - //! A generic icon to use for generic glances that can't otherwise load an icon - KinoReel *generic_glance_icon; - //! The resource ID of the generic glance icon - uint32_t generic_glance_icon_resource_id; - //! A \ref KinoReelPlayer for the currently selected glance - KinoPlayer glance_reel_player; -} LauncherAppGlanceService; - -//! Initialize the provided launcher app glance service. -//! @param service The launcher app glance service to initialize -//! @param generic_glance_icon_resource_id A resource ID to use if a generic launcher app glance -//! does not otherwise have an icon to draw -void launcher_app_glance_service_init(LauncherAppGlanceService *service, - uint32_t generic_glance_icon_resource_id); - -void launcher_app_glance_service_set_handlers(LauncherAppGlanceService *service, - const LauncherAppGlanceServiceHandlers *handlers, - void *context); - -//! Deinitialize the provided launcher app glance service. -//! @param service The launcher app glance service to deinitialize -void launcher_app_glance_service_deinit(LauncherAppGlanceService *service); - -//! Draw the launcher app glance for the provided app node. -//! @param service The service to use to draw the launcher app glance -//! @param ctx The graphics context to use to draw the launcher app glance -//! @param frame The frame in which to draw the launcher app glance -//! @param is_highlighted Whether or not the launcher app glance should be drawn highlighted -//! @param node The \ref AppMenuNode of the app whose glance we should draw -void launcher_app_glance_service_draw_glance_for_app_node(LauncherAppGlanceService *service, - GContext *ctx, const GRect *frame, - bool is_highlighted, AppMenuNode *node); - -//! Rewind any glance being played by the provided launcher app glance service. -//! @param service The service for which to rewind any playing glance -void launcher_app_glance_service_rewind_current_glance(LauncherAppGlanceService *service); - -//! Pause any glance being played by the provided launcher app glance service. -//! @param service The service for which to pause any playing glance -void launcher_app_glance_service_pause_current_glance(LauncherAppGlanceService *service); - -//! Start playing the current glance for the provided launcher app glance service. -//! @param service The service for which to play the current glance -void launcher_app_glance_service_play_current_glance(LauncherAppGlanceService *service); - -//! Play the launcher app glance for the provided app node. -//! @param service The service to use to play the launcher app glance -//! @param node The \ref AppMenuNode for the glance to play -void launcher_app_glance_service_play_glance_for_app_node(LauncherAppGlanceService *service, - AppMenuNode *node); - -//! Notify the service that a launcher app glance in its cache changed. -//! @param service The service to notify -void launcher_app_glance_service_notify_glance_changed(LauncherAppGlanceService *service); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.c deleted file mode 100644 index 86e56f94fd..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_settings.h" - -#include "launcher_app_glance_structured.h" -#include "launcher_menu_layer.h" - -#include "applib/battery_state_service.h" -#include "applib/graphics/gpath.h" -#include "apps/system_apps/timeline/text_node.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/common/battery/battery_state.h" -#include "services/common/comm_session/session.h" -#include "services/normal/bluetooth/ble_hrm.h" -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/size.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -// These dimensions are separate defines so we can use them to statically define the battery points -#define BATTERY_SILHOUETTE_ICON_WIDTH (16) -#define BATTERY_SILHOUETTE_ICON_HEIGHT (9) - -typedef struct LauncherAppGlanceSettingsState { - BatteryChargeState battery_charge_state; - bool is_pebble_app_connected; - bool is_airplane_mode_enabled; - bool is_quiet_time_enabled; -#if CAPABILITY_HAS_BUILTIN_HRM - bool is_sharing_hrm; -#endif -} LauncherAppGlanceSettingsState; - -typedef struct LauncherAppGlanceSettings { - char title[APP_NAME_SIZE_BYTES]; - char battery_percent_text[5]; //!< longest string is "100%" (4 characters + 1 for NULL terminator) - KinoReel *icon; - uint32_t icon_resource_id; - KinoReel *charging_indicator_icon; - uint8_t subtitle_font_height; - LauncherAppGlanceSettingsState glance_state; - EventServiceInfo battery_state_event_info; - EventServiceInfo pebble_app_event_info; - EventServiceInfo airplane_mode_event_info; - EventServiceInfo quiet_time_event_info; -#if CAPABILITY_HAS_BUILTIN_HRM - EventServiceInfo hrm_sharing_event_info; -#endif -} LauncherAppGlanceSettings; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(settings_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(settings_glance, title, NULL); -} - -static void prv_charging_icon_node_draw_cb(GContext *ctx, const GRect *rect, - PBL_UNUSED const GTextNodeDrawConfig *config, bool render, - GSize *size_out, void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - - KinoReel *charging_indicator_icon = NULL_SAFE_FIELD_ACCESS(settings_glance, - charging_indicator_icon, NULL); - PBL_ASSERTN(charging_indicator_icon); - - - if (render && charging_indicator_icon) { - launcher_app_glance_structured_draw_icon(structured_glance, ctx, charging_indicator_icon, - rect->origin); - } - - if (size_out) { - *size_out = GSize(kino_reel_get_size(charging_indicator_icon).w, - settings_glance->subtitle_font_height); - } -} - -static void prv_battery_icon_node_draw_cb(GContext *ctx, const GRect *rect, - PBL_UNUSED const GTextNodeDrawConfig *config, bool render, - GSize *size_out, void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - - const GSize battery_silhouette_icon_size = GSize(BATTERY_SILHOUETTE_ICON_WIDTH, - BATTERY_SILHOUETTE_ICON_HEIGHT); - - if (render) { - // This points array is static to help conserve stack usage - static const GPoint s_battery_silhouette_path_points[] = { - {0, 0}, - {BATTERY_SILHOUETTE_ICON_WIDTH - 1, 0}, - {BATTERY_SILHOUETTE_ICON_WIDTH - 1, 1}, - {BATTERY_SILHOUETTE_ICON_WIDTH + 1, 2}, - {BATTERY_SILHOUETTE_ICON_WIDTH + 1, BATTERY_SILHOUETTE_ICON_HEIGHT - 3}, - {BATTERY_SILHOUETTE_ICON_WIDTH - 1, BATTERY_SILHOUETTE_ICON_HEIGHT - 3}, - {BATTERY_SILHOUETTE_ICON_WIDTH - 1, BATTERY_SILHOUETTE_ICON_HEIGHT - 1}, - {0, BATTERY_SILHOUETTE_ICON_HEIGHT - 1}, - }; - GPath battery_silhouette_path = (GPath) { - .num_points = ARRAY_LENGTH(s_battery_silhouette_path_points), - .points = (GPoint *)s_battery_silhouette_path_points, - .offset = rect->origin, - }; - - const GColor battery_silhouette_color = - launcher_app_glance_structured_get_highlight_color(structured_glance); - const GColor battery_fill_color = - PBL_IF_COLOR_ELSE(gcolor_legible_over(battery_silhouette_color), GColorWhite); - - graphics_context_set_fill_color(ctx, battery_silhouette_color); - - // Draw the battery silhouette - const GRect battery_silhouette_frame = (GRect) { - .origin = rect->origin, - .size = battery_silhouette_icon_size, - }; - gpath_draw_filled(ctx, &battery_silhouette_path); - - // Inset the filled area - GRect battery_fill_rect = grect_inset_internal(battery_silhouette_frame, 3, 2); -#if !PBL_COLOR - // Fill the battery silhouette all the way for B&W, in order to make the BG black always. - graphics_context_set_fill_color(ctx, GColorBlack); - graphics_fill_rect(ctx, &battery_fill_rect); -#endif - - // Adjust fill width for charge percentage, never filling below 10% - uint8_t clipped_charge_percent = - settings_glance->glance_state.battery_charge_state.charge_percent; - clipped_charge_percent = CLIP(clipped_charge_percent, (uint8_t)10, (uint8_t)100); - battery_fill_rect.size.w = battery_fill_rect.size.w * clipped_charge_percent / (int16_t)100; - // Fill the battery silhouette based on the charge percent - graphics_context_set_fill_color(ctx, battery_fill_color); - graphics_fill_rect(ctx, &battery_fill_rect); - } - - if (size_out) { - *size_out = GSize(battery_silhouette_icon_size.w, settings_glance->subtitle_font_height); - } -} - -static void prv_battery_percent_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (settings_glance) { - buffer_size = MIN(sizeof(settings_glance->battery_percent_text), buffer_size); - strncpy(buffer, settings_glance->battery_percent_text, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_wrap_text_node_in_vertically_centered_container(GTextNode *node) { - const size_t max_vertical_container_nodes = 1; - GTextNodeVertical *vertical_container_node = - graphics_text_node_create_vertical(max_vertical_container_nodes); - vertical_container_node->vertical_alignment = GVerticalAlignmentCenter; - - graphics_text_node_container_add_child(&vertical_container_node->container, node); - - return &vertical_container_node->container.node; -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - PBL_ASSERTN(structured_glance); - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - PBL_ASSERTN(settings_glance); - - // Battery text (if not plugged in), battery icon, and (if plugged in) a lightning bolt icon - const size_t max_horizontal_nodes = 3; - GTextNodeHorizontal *horizontal_container_node = - graphics_text_node_create_horizontal(max_horizontal_nodes); - horizontal_container_node->horizontal_alignment = GTextAlignmentLeft; - - if (!settings_glance->glance_state.battery_charge_state.is_plugged) { - GTextNode *battery_percent_text_node = - launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_battery_percent_dynamic_text_node_update); - // Achieves the design spec'd 6 px horizontal spacing b/w the percent text and battery icon - battery_percent_text_node->margin.w = 4; - GTextNode *vertically_centered_battery_percent_text_node = - prv_wrap_text_node_in_vertically_centered_container(battery_percent_text_node); - graphics_text_node_container_add_child(&horizontal_container_node->container, - vertically_centered_battery_percent_text_node); - } - -#if PLATFORM_ROBERT || PLATFORM_OBELIX - const int16_t subtitle_icon_offset_y = 5; -#else - const int16_t subtitle_icon_offset_y = 2; -#endif - - GTextNodeCustom *battery_icon_node = - graphics_text_node_create_custom(prv_battery_icon_node_draw_cb, structured_glance); - // Push the battery icon down to center it properly - battery_icon_node->node.offset.y += subtitle_icon_offset_y; - - // Achieves the design spec'd 6 px horizontal spacing b/w the battery icon and charging icon - battery_icon_node->node.margin.w = 7; - GTextNode *vertically_centered_battery_icon_node = - prv_wrap_text_node_in_vertically_centered_container(&battery_icon_node->node); - graphics_text_node_container_add_child(&horizontal_container_node->container, - vertically_centered_battery_icon_node); - - if (settings_glance->glance_state.battery_charge_state.is_plugged) { - GTextNodeCustom *charging_icon_node = - graphics_text_node_create_custom(prv_charging_icon_node_draw_cb, structured_glance); - // Push the charging icon down to center it properly - charging_icon_node->node.offset.y += subtitle_icon_offset_y; - GTextNode *vertically_centered_charging_icon_node = - prv_wrap_text_node_in_vertically_centered_container(&charging_icon_node->node); - graphics_text_node_container_add_child(&horizontal_container_node->container, - vertically_centered_charging_icon_node); - } - - return &horizontal_container_node->container.node; -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (settings_glance) { - event_service_client_unsubscribe(&settings_glance->battery_state_event_info); - event_service_client_unsubscribe(&settings_glance->pebble_app_event_info); - event_service_client_unsubscribe(&settings_glance->airplane_mode_event_info); - event_service_client_unsubscribe(&settings_glance->quiet_time_event_info); -#if CAPABILITY_HAS_BUILTIN_HRM - event_service_client_unsubscribe(&settings_glance->hrm_sharing_event_info); -#endif - kino_reel_destroy(settings_glance->icon); - kino_reel_destroy(settings_glance->charging_indicator_icon); - } - app_free(settings_glance); -} - -static void prv_set_glance_icon(LauncherAppGlanceSettings *settings_glance, - uint32_t new_icon_resource_id) { - if (settings_glance->icon_resource_id == new_icon_resource_id) { - // Nothing to do, bail out - return; - } - - // Destroy the existing icon - kino_reel_destroy(settings_glance->icon); - - // Set the new icon and record its resource ID - settings_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); - PBL_ASSERTN(settings_glance->icon); - settings_glance->icon_resource_id = new_icon_resource_id; -} - -static bool prv_mute_notifications_allow_calls_only(void) { - return (alerts_get_mask() == AlertMaskPhoneCalls); -} - -static uint32_t prv_get_resource_id_for_connectivity_status( - LauncherAppGlanceSettings *settings_glance) { -#if CAPABILITY_HAS_BUILTIN_HRM - if (settings_glance->glance_state.is_sharing_hrm) { - return RESOURCE_ID_CONNECTIVITY_SHARING_HRM; - } -#endif - if (settings_glance->glance_state.is_airplane_mode_enabled) { - return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE; - } else if (!settings_glance->glance_state.is_pebble_app_connected) { - return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED; - } else if (settings_glance->glance_state.is_quiet_time_enabled) { - return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND; - } else if (prv_mute_notifications_allow_calls_only()) { - return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY; - } else if (settings_glance->glance_state.is_pebble_app_connected) { - return RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED; - } else { - WTF; - } -} - -static void prv_refresh_glance_content(LauncherAppGlanceSettings *settings_glance) { - // Update the battery percent text in the glance - const size_t battery_percent_text_size = sizeof(settings_glance->battery_percent_text); - snprintf(settings_glance->battery_percent_text, battery_percent_text_size, "%"PRIu8"%%", - settings_glance->glance_state.battery_charge_state.charge_percent); - - // Update the icon - const uint32_t new_icon_resource_id = - prv_get_resource_id_for_connectivity_status(settings_glance); - prv_set_glance_icon(settings_glance, new_icon_resource_id); -} - -static bool prv_is_pebble_app_connected(void) { - return (comm_session_get_system_session() != NULL); -} - -static void prv_event_handler(PebbleEvent *event, void *context) { - LauncherAppGlanceStructured *structured_glance = context; - PBL_ASSERTN(structured_glance); - - LauncherAppGlanceSettings *settings_glance = - launcher_app_glance_structured_get_data(structured_glance); - PBL_ASSERTN(settings_glance); - - switch (event->type) { - case PEBBLE_BATTERY_STATE_CHANGE_EVENT: - settings_glance->glance_state.battery_charge_state = battery_state_service_peek(); - break; - case PEBBLE_COMM_SESSION_EVENT: - if (event->bluetooth.comm_session_event.is_system) { - settings_glance->glance_state.is_pebble_app_connected = - event->bluetooth.comm_session_event.is_open; - } - break; - case PEBBLE_BT_STATE_EVENT: - settings_glance->glance_state.is_airplane_mode_enabled = bt_ctl_is_airplane_mode_on(); - break; - case PEBBLE_DO_NOT_DISTURB_EVENT: - settings_glance->glance_state.is_quiet_time_enabled = do_not_disturb_is_active(); - break; -#if CAPABILITY_HAS_BUILTIN_HRM - case PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT: { - const bool prev_is_sharing = settings_glance->glance_state.is_sharing_hrm; - const bool is_sharing = (event->bluetooth.le.hrm_sharing_state.subscription_count > 0); - if (prev_is_sharing == is_sharing) { - return; - } - settings_glance->glance_state.is_sharing_hrm = is_sharing; - break; - } -#endif - default: - WTF; - } - - // Refresh the content in the glance - prv_refresh_glance_content(settings_glance); - - // Broadcast to the service that we changed the glance - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); -} - -static void prv_subscribe_to_event(EventServiceInfo *event_service_info, PebbleEventType type, - LauncherAppGlanceStructured *structured_glance) { - PBL_ASSERTN(event_service_info); - - *event_service_info = (EventServiceInfo) { - .type = type, - .handler = prv_event_handler, - .context = structured_glance, - }; - - event_service_client_subscribe(event_service_info); -} - -static const LauncherAppGlanceStructuredImpl s_settings_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_settings_create(const AppMenuNode *node) { - PBL_ASSERTN(node); - - LauncherAppGlanceSettings *settings_glance = app_zalloc_check(sizeof(*settings_glance)); - - // Copy the name of the Settings app as the title - const size_t title_size = sizeof(settings_glance->title); - strncpy(settings_glance->title, node->name, title_size); - settings_glance->title[title_size - 1] = '\0'; - - // Load the charging indicator icon - settings_glance->charging_indicator_icon = - kino_reel_create_with_resource(RESOURCE_ID_BATTERY_CHARGING_ICON); - - // Cache the subtitle font height for simplifying layout calculations - settings_glance->subtitle_font_height = - fonts_get_font_height(fonts_get_system_font(LAUNCHER_MENU_LAYER_SUBTITLE_FONT)); - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_settings_structured_glance_impl, - should_consider_slices, settings_glance); - PBL_ASSERTN(structured_glance); - // Disable selection animations for the settings glance - structured_glance->selection_animation_disabled = true; - - // Set the first state of the glance - settings_glance->glance_state = (LauncherAppGlanceSettingsState) { - .battery_charge_state = battery_state_service_peek(), - .is_pebble_app_connected = prv_is_pebble_app_connected(), - .is_airplane_mode_enabled = bt_ctl_is_airplane_mode_on(), - .is_quiet_time_enabled = do_not_disturb_is_active(), -#if CAPABILITY_HAS_BUILTIN_HRM - .is_sharing_hrm = ble_hrm_is_sharing(), -#endif - }; - - // Refresh the glance now that we have set the first state of the glance - prv_refresh_glance_content(settings_glance); - - // Subscribe to the various events we care about - prv_subscribe_to_event(&settings_glance->battery_state_event_info, - PEBBLE_BATTERY_STATE_CHANGE_EVENT, structured_glance); - prv_subscribe_to_event(&settings_glance->pebble_app_event_info, PEBBLE_COMM_SESSION_EVENT, - structured_glance); - prv_subscribe_to_event(&settings_glance->airplane_mode_event_info, PEBBLE_BT_STATE_EVENT, - structured_glance); - prv_subscribe_to_event(&settings_glance->quiet_time_event_info, PEBBLE_DO_NOT_DISTURB_EVENT, - structured_glance); -#if CAPABILITY_HAS_BUILTIN_HRM - prv_subscribe_to_event(&settings_glance->hrm_sharing_event_info, - PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT, - structured_glance); -#endif - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.h deleted file mode 100644 index 869891355f..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_settings_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.c deleted file mode 100644 index 3cb31f0ab9..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_structured.h" - -#include "launcher_app_glance_private.h" -#include "launcher_menu_layer.h" - -#include "applib/graphics/gdraw_command_transforms.h" -#include "applib/ui/kino/kino_reel_custom.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/normal/timeline/attribute.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#if PLATFORM_ROBERT || PLATFORM_OBELIX -#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN (9) -#else -#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN (5) -#endif - -typedef struct GenericGlanceIconDrawCommandProcessor { - GDrawCommandProcessor draw_command_processor; - GColor8 *luminance_tint_lookup_table; -} GenericGlanceIconDrawCommandProcessor; - -static void prv_structured_glance_icon_draw_command_processor_process_command( - GDrawCommandProcessor *processor, GDrawCommand *processed_command, - PBL_UNUSED size_t processed_command_max_size, PBL_UNUSED const GDrawCommandList *list, - PBL_UNUSED const GDrawCommand *command) { - GenericGlanceIconDrawCommandProcessor *processor_with_data = - (GenericGlanceIconDrawCommandProcessor *)processor; - const GColor8 *luminance_tint_lookup_table = processor_with_data->luminance_tint_lookup_table; - - // Luminance tint the fill color - const GColor fill_color = gdraw_command_get_fill_color(processed_command); - const GColor tinted_fill_color = - gcolor_perform_lookup_using_color_luminance_and_multiply_alpha(fill_color, - luminance_tint_lookup_table); - gdraw_command_replace_color(processed_command, fill_color, tinted_fill_color); - gdraw_command_set_fill_color(processed_command, tinted_fill_color); - - // Luminance tint the stroke color - const GColor stroke_color = gdraw_command_get_stroke_color(processed_command); - const GColor tinted_stroke_color = - gcolor_perform_lookup_using_color_luminance_and_multiply_alpha(stroke_color, - luminance_tint_lookup_table); - gdraw_command_set_stroke_color(processed_command, tinted_stroke_color); -} - -typedef struct GenericGlanceIconBitmapProcessor { - GBitmapProcessor bitmap_processor; - GCompOp saved_compositing_mode; - GColor saved_tint_color; - GColor desired_tint_color; -} GenericGlanceIconBitmapProcessor; - -static void prv_strucutred_glance_icon_bitmap_processor_pre_func( - GBitmapProcessor *processor, GContext *ctx, PBL_UNUSED const GBitmap **bitmap_to_use, - PBL_UNUSED GRect *global_grect_to_use) { - GenericGlanceIconBitmapProcessor *processor_with_data = - (GenericGlanceIconBitmapProcessor *)processor; - // Save the current compositing mode and tint color - processor_with_data->saved_compositing_mode = ctx->draw_state.compositing_mode; - processor_with_data->saved_tint_color = ctx->draw_state.tint_color; - // Set the compositing mode so that we luminance tint the icon to the specified color - ctx->draw_state.compositing_mode = GCompOpTintLuminance; - ctx->draw_state.tint_color = processor_with_data->desired_tint_color; -} - -static void prv_structured_glance_icon_bitmap_processor_post_func( - GBitmapProcessor *processor, GContext *ctx, PBL_UNUSED const GBitmap *bitmap_used, - PBL_UNUSED const GRect *global_clipped_grect_used) { - GenericGlanceIconBitmapProcessor *processor_with_data = - (GenericGlanceIconBitmapProcessor *)processor; - // Restore the saved compositing mode and tint color - ctx->draw_state.compositing_mode = processor_with_data->saved_compositing_mode; - ctx->draw_state.tint_color = processor_with_data->saved_tint_color; -} - -GColor launcher_app_glance_structured_get_highlight_color( - LauncherAppGlanceStructured *structured_glance) { - return PBL_IF_COLOR_ELSE(GColorBlack, - structured_glance->glance.is_highlighted ? GColorWhite : GColorBlack); -} - -void launcher_app_glance_structured_draw_icon(LauncherAppGlanceStructured *structured_glance, - GContext *ctx, KinoReel *icon, GPoint origin) { - const GColor desired_tint_color = - launcher_app_glance_structured_get_highlight_color(structured_glance); - - GenericGlanceIconBitmapProcessor structured_glance_icon_bitmap_processor = { - .bitmap_processor = { - .pre = prv_strucutred_glance_icon_bitmap_processor_pre_func, - .post = prv_structured_glance_icon_bitmap_processor_post_func, - }, - .desired_tint_color = desired_tint_color, - }; - - GColor8 luminance_tint_lookup_table[GCOLOR8_COMPONENT_NUM_VALUES] = {}; - gcolor_tint_luminance_lookup_table_init(desired_tint_color, luminance_tint_lookup_table); - - GenericGlanceIconDrawCommandProcessor strucutred_glance_icon_draw_command_processor = { - .draw_command_processor.command = - prv_structured_glance_icon_draw_command_processor_process_command, - .luminance_tint_lookup_table = luminance_tint_lookup_table, - }; - - KinoReelProcessor structured_glance_icon_processor = { - .bitmap_processor = &structured_glance_icon_bitmap_processor.bitmap_processor, - .draw_command_processor = - &strucutred_glance_icon_draw_command_processor.draw_command_processor, - }; - - // Draw the glance's icon, luminance tinting its colors according to the glance's highlight - kino_reel_draw_processed(icon, ctx, origin, &structured_glance_icon_processor); -} - -static void prv_structured_glance_icon_node_draw_cb(GContext *ctx, const GRect *rect, - PBL_UNUSED const GTextNodeDrawConfig *config, - bool render, GSize *size_out, void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - KinoReel *icon = NULL; - if (structured_glance && structured_glance->impl && structured_glance->impl->get_icon) { - icon = structured_glance->impl->get_icon(structured_glance); - } - - if (render && icon) { - // Center the frame in which we'll draw the icon - GRect icon_frame = (GRect) { .size = kino_reel_get_size(icon) }; - grect_align(&icon_frame, rect, GAlignCenter, false /* clip */); - - // Save the GContext's clip box and override it so we clip the icon to the max icon size - const GRect saved_clip_box = ctx->draw_state.clip_box; - ctx->draw_state.clip_box.origin = gpoint_add(ctx->draw_state.drawing_box.origin, - rect->origin); - ctx->draw_state.clip_box.size = rect->size; - - // Prevent drawing outside of the existing clip box - grect_clip(&ctx->draw_state.clip_box, &saved_clip_box); - - // Draw the icon! - launcher_app_glance_structured_draw_icon(structured_glance, ctx, icon, icon_frame.origin); - - // Restore the saved clip box - ctx->draw_state.clip_box = saved_clip_box; - } - - if (size_out) { - *size_out = structured_glance->icon_max_size; - } -} - -static GTextNode *prv_structured_glance_create_text_node( - LauncherAppGlanceStructured *structured_glance, GFont font, size_t buffer_size, - GTextNodeTextDynamicUpdate update) { - if (!structured_glance) { - return NULL; - } - GTextNodeTextDynamic *dynamic_text_node = - graphics_text_node_create_text_dynamic(buffer_size, update, structured_glance); - GTextNodeText *underlying_text_node_text = &dynamic_text_node->text; - underlying_text_node_text->font = font; - underlying_text_node_text->color = - launcher_app_glance_structured_get_highlight_color(structured_glance); - underlying_text_node_text->overflow = GTextOverflowModeTrailingEllipsis; - underlying_text_node_text->node.offset = GPoint(0, -fonts_get_font_cap_offset(font)); - underlying_text_node_text->max_size.h = fonts_get_font_height(font); - return &underlying_text_node_text->node; -} - -static void prv_structured_glance_title_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - const char *title = NULL; - if (structured_glance && structured_glance->impl && structured_glance->impl->get_title) { - title = structured_glance->impl->get_title(structured_glance); - } - if (title) { - strncpy(buffer, title, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_structured_glance_create_title_text_node( - LauncherAppGlanceStructured *structured_glance) { - return prv_structured_glance_create_text_node( - structured_glance, structured_glance->title_font, APP_NAME_SIZE_BYTES, - prv_structured_glance_title_dynamic_text_node_update); -} - -typedef struct ScrollAnimationVars { - int16_t total_px_to_scroll; - int16_t current_offset; - uint32_t duration_ms; -} ScrollAnimationVars; - -//! Calculates the variables of a text scrolling animation that proceeds as follows: -//! - Pauses a bit at the start -//! - Scrolls the provided text at a moderate pace up to 3x the width of the provided draw_box -//! - Pauses a bit when the end of the scrollable text is reached -//! - Rewinds the text back to a zero offset at a rapid pace -//! Returns true if conditions are right for scrolling and output arguments should be used, -//! false otherwise. -static bool prv_get_text_scroll_vars(GContext *ctx, uint32_t cumulative_elapsed_ms, - const char *text, const GRect *draw_box, GFont font, - GTextAlignment text_alignment, GTextOverflowMode overflow_mode, - TextLayoutExtended *layout, ScrollAnimationVars *vars_out) { - if (!vars_out) { - return false; - } - - // Allow for showing up to 3x the width of the draw_box for the text - const int16_t max_text_width = draw_box->size.w * (int16_t)3; - const GRect max_text_box = (GRect) { .size = GSize(max_text_width, draw_box->size.h) }; - const int16_t scroll_visible_text_width = - graphics_text_layout_get_max_used_size(ctx, text, font, max_text_box, - overflow_mode, text_alignment, - (GTextLayoutCacheRef)layout).w; - - if (scroll_visible_text_width <= draw_box->size.w) { - // No need to scroll because text fits completely in the provided draw_box - return false; - } - - // This is the amount we'll scroll the text from start to end to show all of the - // scroll_visible_text_width in the provided draw_box - const int16_t total_px_to_scroll = scroll_visible_text_width - draw_box->size.w; - vars_out->total_px_to_scroll = total_px_to_scroll; - - // These values were tuned with feedback from Design - const uint32_t normal_scroll_speed_ms_per_px = 20; - const uint32_t normal_scroll_duration_ms = total_px_to_scroll * normal_scroll_speed_ms_per_px; - const uint32_t rewind_scroll_speed_ms_per_px = 2; - const uint32_t rewind_scroll_duration_ms = total_px_to_scroll * rewind_scroll_speed_ms_per_px; - const uint32_t pause_at_start_ms = 600; - const uint32_t pause_at_end_ms = 750; - - const uint32_t scroll_duration_ms = - pause_at_start_ms + normal_scroll_duration_ms + pause_at_end_ms + rewind_scroll_duration_ms; - vars_out->duration_ms = scroll_duration_ms; - - // Technically mod isn't necessary right now, but it's needed for looping eventually (PBL-40544) - int64_t elapsed_ms = cumulative_elapsed_ms % scroll_duration_ms; - const uint32_t end_of_normal_scroll_duration_ms = pause_at_start_ms + normal_scroll_duration_ms; - bool rewind = false; - if (WITHIN(elapsed_ms, 0, end_of_normal_scroll_duration_ms)) { - elapsed_ms = MAX(elapsed_ms - pause_at_start_ms, 0); - } else if (elapsed_ms < end_of_normal_scroll_duration_ms + pause_at_end_ms) { - elapsed_ms = normal_scroll_duration_ms; - } else { - elapsed_ms = scroll_duration_ms - elapsed_ms; - rewind = true; - } - - const uint32_t elapsed_normalized = - ((uint32_t)elapsed_ms * ANIMATION_NORMALIZED_MAX) / - (rewind ? rewind_scroll_duration_ms : normal_scroll_duration_ms); - vars_out->current_offset = interpolate_int16(elapsed_normalized, 0, total_px_to_scroll); - - return true; -} - -//! Currently the subtitle scrolling drives the duration of the overall glance selection animation -//! because we only scroll once, and since we don't know what we're scrolling until this function -//! is called, we need to record the duration of the scrolling animation in this function so the -//! glance's KinoReel reports the correct duration for the overall selection animation. -static void prv_adjust_subtitle_node_for_scrolling_animation( - LauncherAppGlanceStructured *structured_glance, GContext *ctx, GTextNodeText *node_text, - const char *text, const GRect *draw_box) { - const uint32_t cumulative_elapsed_ms = structured_glance->selection_animation_elapsed_ms; - - ScrollAnimationVars vars; - if (!prv_get_text_scroll_vars(ctx, - cumulative_elapsed_ms, - text, draw_box, node_text->font, - node_text->alignment, node_text->overflow, - &structured_glance->subtitle_scroll_calc_text_layout, &vars)) { - // No need to scroll because text fits completely on-screen, set the selection animation - // duration to 0 and bail out - structured_glance->selection_animation_duration_ms = 0; - return; - } - - // Assumes that the default offset.x for the subtitle node is 0, which is true for generic glances - node_text->node.offset.x = -vars.current_offset; - // Assumes that the default margin.w for the subtitle node is 0, which is true for generic glances - node_text->node.margin.w = (vars.current_offset != 0) ? -vars.total_px_to_scroll : (int16_t)0; - - // Record any change in the selection animation's duration - LauncherAppGlanceService *service = structured_glance->glance.service; - if (vars.duration_ms != structured_glance->selection_animation_duration_ms) { - const uint32_t previous_selection_animation_duration_ms = - structured_glance->selection_animation_duration_ms; - structured_glance->selection_animation_duration_ms = vars.duration_ms; - // If we're starting a new scroll or a scroll is currently in-progress, pause and then - // play the animation so it is updated with the new duration (e.g. so we don't stop in a weird - // place because the previous duration is shorter than the new one) - if ((previous_selection_animation_duration_ms == 0) || (cumulative_elapsed_ms != 0)) { - launcher_app_glance_service_pause_current_glance(service); - launcher_app_glance_service_play_current_glance(service); - } - } -} - -static void prv_structured_glance_subtitle_dynamic_text_node_update( - GContext *ctx, GTextNode *node, const GRect *box, const GTextNodeDrawConfig *config, - bool render, char *buffer, size_t buffer_size, void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - if (structured_glance->subtitle_update) { - structured_glance->subtitle_update(ctx, node, box, config, render, buffer, buffer_size, - user_data); - } - GTextNodeText *node_text = (GTextNodeText *)node; - if (!render) { - prv_adjust_subtitle_node_for_scrolling_animation(structured_glance, ctx, node_text, buffer, - box); - } -} - -GTextNode *launcher_app_glance_structured_create_subtitle_text_node( - LauncherAppGlanceStructured *structured_glance, GTextNodeTextDynamicUpdate update) { - const size_t subtitle_buffer_size = ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1; - structured_glance->subtitle_update = update; - GTextNode *node = prv_structured_glance_create_text_node( - structured_glance, structured_glance->subtitle_font, subtitle_buffer_size, - prv_structured_glance_subtitle_dynamic_text_node_update); - // Clip subtitle text nodes to their draw box since we scroll them if they're too long - node->clip = true; - return node; -} - -static GTextNode *prv_create_structured_glance_title_subtitle_node( - LauncherAppGlanceStructured *structured_glance, const GRect *glance_frame) { - // Title node and subtitle node - const size_t max_vertical_nodes = 2; - GTextNodeVertical *vertical_node = graphics_text_node_create_vertical(max_vertical_nodes); - vertical_node->vertical_alignment = GVerticalAlignmentCenter; - - GTextNode *title_node = prv_structured_glance_create_title_text_node(structured_glance); - // We require a valid title node - PBL_ASSERTN(title_node); - // Push the title node a little up or down to match the relevant design spec -#if PLATFORM_ROBERT || PLATFORM_OBELIX - title_node->offset.y += 1; -#else - title_node->offset.y -= 1; -#endif - graphics_text_node_container_add_child(&vertical_node->container, title_node); - - GTextNode *subtitle_node = NULL; - if (structured_glance->impl && structured_glance->impl->create_subtitle_node) { - subtitle_node = structured_glance->impl->create_subtitle_node(structured_glance); - } - // The subtitle node is optional - if (subtitle_node) { - graphics_text_node_container_add_child(&vertical_node->container, subtitle_node); - } - - // Set the vertical container's width to exactly what it should be so it doesn't resize based - // on its changing content (e.g. scrolling subtitle) - vertical_node->container.size.w = - glance_frame->size.w - structured_glance->icon_horizontal_margin - - structured_glance->icon_max_size.w; - - return &vertical_node->container.node; -} - -// NOINLINE to save stack; on Spalding this can be enough to push us over the edge. -static NOINLINE GTextNode *prv_create_structured_glance_node( - LauncherAppGlanceStructured *structured_glance, const GRect *glance_frame) { - // Icon node and title/subtitle nodes - const size_t max_horizontal_nodes = 2; - GTextNodeHorizontal *horizontal_node = graphics_text_node_create_horizontal(max_horizontal_nodes); - horizontal_node->horizontal_alignment = GTextAlignmentLeft; - - // This vertical node is just a container to vertically center the icon node - const size_t max_vertical_icon_container_nodes = 1; - GTextNodeVertical *vertical_icon_container_node = - graphics_text_node_create_vertical(max_vertical_icon_container_nodes); - vertical_icon_container_node->vertical_alignment = GVerticalAlignmentCenter; - - // This horizontal node is just a container to horizontally center the icon node - const size_t max_horizontal_icon_container_nodes = 1; - GTextNodeHorizontal *horizontal_icon_container_node = - graphics_text_node_create_horizontal(max_horizontal_icon_container_nodes); - horizontal_icon_container_node->horizontal_alignment = GTextAlignmentCenter; - graphics_text_node_container_add_child(&vertical_icon_container_node->container, - &horizontal_icon_container_node->container.node); - - GTextNodeCustom *icon_node = - graphics_text_node_create_custom(prv_structured_glance_icon_node_draw_cb, structured_glance); - icon_node->node.margin.w = structured_glance->icon_horizontal_margin; - // The +1 is to force a rounding up. This way, 3 pixels extra will move closer to the screen - // edge, instead of closer to the text. - icon_node->node.offset.x -= (LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN - - structured_glance->icon_horizontal_margin + 1) / 2; - graphics_text_node_container_add_child(&horizontal_icon_container_node->container, - &icon_node->node); - - graphics_text_node_container_add_child(&horizontal_node->container, - &vertical_icon_container_node->container.node); - - GTextNode *title_subtitle_node = - prv_create_structured_glance_title_subtitle_node(structured_glance, glance_frame); - graphics_text_node_container_add_child(&horizontal_node->container, - title_subtitle_node); - - return &horizontal_node->container.node; -} - -static void prv_draw_processed(KinoReel *reel, GContext *ctx, GPoint offset, - PBL_UNUSED KinoReelProcessor *processor) { - LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); - if (!structured_glance) { - return; - } - - GRect glance_frame = (GRect) { .origin = offset, .size = structured_glance->glance.size }; -#if PLATFORM_ROBERT || PLATFORM_OBELIX - const int16_t horizontal_inset = 10; -#else - const int16_t horizontal_inset = PBL_IF_RECT_ELSE(6, 23); -#endif - glance_frame = grect_inset_internal(glance_frame, horizontal_inset, 0); - - GTextNode *structured_glance_node = prv_create_structured_glance_node(structured_glance, - &glance_frame); - if (structured_glance_node) { - graphics_text_node_draw(structured_glance_node, ctx, &glance_frame, NULL, NULL); - } - graphics_text_node_destroy(structured_glance_node); -} - -static uint32_t prv_get_elapsed(KinoReel *reel) { - LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); - return NULL_SAFE_FIELD_ACCESS(structured_glance, selection_animation_elapsed_ms, 0); -} - -static bool prv_set_elapsed(KinoReel *reel, uint32_t elapsed_ms) { - LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); - if (!structured_glance) { - return false; - } - if (!structured_glance->selection_animation_disabled) { - structured_glance->selection_animation_elapsed_ms = elapsed_ms; - } - // We assume the selection animation loops so that it's last frame is the same as its first frame, - // so let's enforce that here so the animation update code above works properly - if (structured_glance->selection_animation_elapsed_ms == kino_reel_get_duration(reel)) { - structured_glance->selection_animation_elapsed_ms = 0; - } - return !structured_glance->selection_animation_disabled; -} - -static uint32_t prv_get_duration(KinoReel *reel) { - // TODO PBL-40544: Loop the selection animation - LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); - return NULL_SAFE_FIELD_ACCESS(structured_glance, selection_animation_duration_ms, 0); -} - -static void prv_destructor(KinoReel *reel) { - LauncherAppGlanceStructured *structured_glance = kino_reel_custom_get_data(reel); - if (structured_glance && structured_glance->impl && structured_glance->impl->destructor) { - structured_glance->impl->destructor(structured_glance); - } -} - -static const KinoReelImpl s_launcher_app_glance_structured_reel_impl = { - .reel_type = KinoReelTypeCustom, - .get_size = launcher_app_glance_get_size_for_reel, - .draw_processed = prv_draw_processed, - .destructor = prv_destructor, - .get_duration = prv_get_duration, - .get_elapsed = prv_get_elapsed, - .set_elapsed = prv_set_elapsed, -}; - -LauncherAppGlanceStructured *launcher_app_glance_structured_create( - const Uuid *uuid, const LauncherAppGlanceStructuredImpl *impl, bool should_consider_slices, - void *data) { - PBL_ASSERTN(uuid); - LauncherAppGlanceStructured *structured_glance = app_zalloc_check(sizeof(*structured_glance)); - const LauncherAppGlanceHandlers *base_handlers = impl ? &impl->base_handlers : NULL; - structured_glance->impl = impl; - structured_glance->data = data; - structured_glance->icon_max_size = LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE; - structured_glance->icon_horizontal_margin = - LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN; - structured_glance->title_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_TITLE_FONT); - structured_glance->subtitle_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_SUBTITLE_FONT); - KinoReel *glance_impl = kino_reel_custom_create(&s_launcher_app_glance_structured_reel_impl, - structured_glance); - // Now that we've setup the structured glance's fields, initialize the LauncherAppGlance - launcher_app_glance_init(&structured_glance->glance, uuid, glance_impl, should_consider_slices, - base_handlers); - return structured_glance; -} - -void *launcher_app_glance_structured_get_data(LauncherAppGlanceStructured *structured_glance) { - return NULL_SAFE_FIELD_ACCESS(structured_glance, data, NULL); -} - -void launcher_app_glance_structured_notify_service_glance_changed( - LauncherAppGlanceStructured *structured_glance) { - if (!structured_glance) { - return; - } - launcher_app_glance_notify_service_glance_changed(&structured_glance->glance); -} - -void launcher_app_glance_structured_set_icon_max_size( - LauncherAppGlanceStructured *structured_glance, GSize new_size) { - if (!structured_glance) { - return; - } - - structured_glance->icon_max_size = new_size; - - const int width_diff = structured_glance->icon_max_size.w - - LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE.w; - structured_glance->icon_horizontal_margin = - LAUNCHER_APP_GLANCE_STRUCTURED_ICON_HORIZONTAL_MARGIN - width_diff; - - if (structured_glance->icon_horizontal_margin < 0) { - structured_glance->icon_horizontal_margin = 0; - } -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.h deleted file mode 100644 index edf257e743..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "applib/ui/kino/kino_reel.h" -#include "apps/system_apps/timeline/text_node.h" -#include "util/uuid.h" - -#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_MAX_SIZE \ - (GSize(ATTRIBUTE_ICON_TINY_SIZE_PX, ATTRIBUTE_ICON_TINY_SIZE_PX)) -#define LAUNCHER_APP_GLANCE_STRUCTURED_ICON_LEGACY_MAX_SIZE \ - (GSize(28, 28)) - -//! Forward declaration -typedef struct LauncherAppGlanceStructured LauncherAppGlanceStructured; - -//! Function used to get the title to display in the structured launcher app glance. -//! @param structured_glance The structured glance for which to get the title -//! @return The title to display in the structured glance; will be copied so can be short-lived -typedef const char *(*LauncherAppGlanceStructuredTitleGetter) - (LauncherAppGlanceStructured *structured_glance); - -//! Function used to create subtitle text nodes for the structured launcher app glance. -//! @param structured_glance The structured glance for which to create a text node -//! @return The text node the structured glance should use -typedef GTextNode *(*LauncherAppGlanceStructuredTextNodeConstructor) - (LauncherAppGlanceStructured *structured_glance); - -//! Function called when the structured launcher app glance is being destroyed. -//! @param structured_glance The structured glance that is being destroyed -//! @note This function should NOT free the structured glance; only deinit impl-specific things -typedef void (*LauncherAppGlanceStructuredDestructor) - (LauncherAppGlanceStructured *structured_glance); - -//! Function called to request the icon that should be drawn in the structured glance. -//! @param structured_glance The structured glance requesting the icon to draw -//! @return The icon to draw in the structured glance -typedef KinoReel *(*LauncherAppGlanceStructuredIconGetter) - (LauncherAppGlanceStructured *structured_glance); - -typedef struct LauncherAppGlanceStructuredImpl { - //! Base handlers for the underlying LauncherAppGlance of the structured glance - LauncherAppGlanceHandlers base_handlers; - //! Called to get the icon to draw in the structured glance - LauncherAppGlanceStructuredIconGetter get_icon; - //! Called to create the title text node for the structured glance; must return a valid text node - LauncherAppGlanceStructuredTitleGetter get_title; - //! Called to create the subtitle text node for the structured glance - LauncherAppGlanceStructuredTextNodeConstructor create_subtitle_node; - //! Called when the structured glance is being destroyed; should NOT free the structured glance - LauncherAppGlanceStructuredDestructor destructor; -} LauncherAppGlanceStructuredImpl; - -struct LauncherAppGlanceStructured { - //! The underlying launcher app glance - LauncherAppGlance glance; - //! The implementation of the structured app glance - const LauncherAppGlanceStructuredImpl *impl; - //! The user-provided data for the structured app glance's implementation - void *data; - //! Cached title font that will be used when drawing the structured app glance - GFont title_font; - //! Cached subtitle font that will be used when drawing the structured app glance - GFont subtitle_font; - // Cached text layout used when calculating the width of the subtitle during scrolling - TextLayoutExtended subtitle_scroll_calc_text_layout; - //! Optional implementation-provided dynamic text node update callback for the subtitle - GTextNodeTextDynamicUpdate subtitle_update; - //! Whether or not selection animations should be disabled for this structured app glance - bool selection_animation_disabled; - //! Current cumulative elapsed time (in milliseconds) of the glance's selection animation - uint32_t selection_animation_elapsed_ms; - //! Duration (in milliseconds) of the glance's selection animation - uint32_t selection_animation_duration_ms; - //! Maximum size an icon may have - GSize icon_max_size; - //! Horizontal margin for the icon - int32_t icon_horizontal_margin; -}; - -_Static_assert((offsetof(LauncherAppGlanceStructured, glance) == 0), - "LauncherAppGlance is not the first field of LauncherAppGlanceStructured"); - -//! Create a structured launcher app glance for the provided app menu node. -//! @param uuid The UUID of the app for which to initialize this structured glance -//! @param impl The implementation of the structured glance -//! @param should_consider_slices Whether or not the structured glance should consider slices -//! @param data Custom data to use in the implementation of the structured glance -LauncherAppGlanceStructured *launcher_app_glance_structured_create( - const Uuid *uuid, const LauncherAppGlanceStructuredImpl *impl, bool should_consider_slices, - void *data); - -//! Get the user-provided data for the implementation of a structured launcher app glance. -//! @param structured_glance The structured glance for which to get the user-provided data -//! @return The user-provided data -void *launcher_app_glance_structured_get_data(LauncherAppGlanceStructured *structured_glance); - -//! Get the highlight color that should be used for the provided structured launcher app glance. -//! @param structured_glance The structured glance for which to get the highlight color -//! @return The highlight color to use when drawing the structured glance -GColor launcher_app_glance_structured_get_highlight_color( - LauncherAppGlanceStructured *structured_glance); - -//! Draw an icon in the structured launcher app glance. -//! @param structured_glance The structured glance in which to draw an icon -//! @param ctx The graphics context to use when drawing the icon -//! @param icon The icon to draw -//! @param origin The origin at which to draw the icon -void launcher_app_glance_structured_draw_icon(LauncherAppGlanceStructured *structured_glance, - GContext *ctx, KinoReel *icon, GPoint origin); - -//! Create a subtitle text node for a structured launcher app glance. It is expected that subclasses -//! of \ref LauncherAppGlanceStructured will use this function in their own custom subtitle node -//! creation functions they specify in their \ref LauncherAppGlanceStructuredImpl. Calling this -//! function saves the provided callback to the \ref LauncherAppGlanceStructured struct, thus you -//! should only call this once per structured glance implementation. -//! @param structured_glance The structured glance for which to create a subtitle text node -//! @param update Callback for updating the text buffer of the text node -//! @return The resulting subtitle text node, or NULL upon failure -GTextNode *launcher_app_glance_structured_create_subtitle_text_node( - LauncherAppGlanceStructured *structured_glance, GTextNodeTextDynamicUpdate update); - -//! Notify the structured launcher app glance's service that its content has changed. -//! @param structured_glance The structured glance that has changed -void launcher_app_glance_structured_notify_service_glance_changed( - LauncherAppGlanceStructured *structured_glance); - -//! Change the icon max size, and adjust related settings. -//! @param structured_glance The structured glance for which to change the icon size -//! @param new_size The new maximum size allowed for the icon -void launcher_app_glance_structured_set_icon_max_size( - LauncherAppGlanceStructured *structured_glance, GSize new_size); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.c deleted file mode 100644 index 6f179cade0..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_watchfaces.h" - -#include "launcher_app_glance_structured.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "shell/normal/watchface.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -typedef struct LauncherAppGlanceWatchfaces { - char title[APP_NAME_SIZE_BYTES]; - char subtitle[APP_NAME_SIZE_BYTES]; - KinoReel *icon; -} LauncherAppGlanceWatchfaces; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWatchfaces *watchfaces_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(watchfaces_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWatchfaces *watchfaces_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(watchfaces_glance, title, NULL); -} - -static void prv_watchfaces_glance_subtitle_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceWatchfaces *watchfaces_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (watchfaces_glance) { - strncpy(buffer, watchfaces_glance->subtitle, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_watchfaces_glance_subtitle_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWatchfaces *watchfaces_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (watchfaces_glance) { - kino_reel_destroy(watchfaces_glance->icon); - } - app_free(watchfaces_glance); -} - -static void prv_update_active_watchface_title(LauncherAppGlanceWatchfaces *watchfaces_glance) { - const AppInstallId selected_watch_id = watchface_get_default_install_id(); - - AppInstallEntry entry; - if (app_install_get_entry_for_install_id(selected_watch_id, &entry)) { - const size_t watchfaces_subtitle_size = sizeof(watchfaces_glance->subtitle); - strncpy(watchfaces_glance->subtitle, entry.name, watchfaces_subtitle_size); - watchfaces_glance->subtitle[watchfaces_subtitle_size - 1] = '\0'; - } -} - -static const LauncherAppGlanceStructuredImpl s_watchfaces_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_watchfaces_create(const AppMenuNode *node) { - PBL_ASSERTN(node); - - LauncherAppGlanceWatchfaces *watchfaces_glance = app_zalloc_check(sizeof(*watchfaces_glance)); - - // Copy the name of the Watchfaces app as the title - const size_t title_size = sizeof(watchfaces_glance->title); - strncpy(watchfaces_glance->title, node->name, title_size); - watchfaces_glance->title[title_size - 1] = '\0'; - - // Create the icon for the Watchfaces app - watchfaces_glance->icon = kino_reel_create_with_resource_system(node->app_num, - node->icon_resource_id); - PBL_ASSERTN(watchfaces_glance->icon); - - // Update the active watchface title in the glance's subtitle - prv_update_active_watchface_title(watchfaces_glance); - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_watchfaces_structured_glance_impl, - should_consider_slices, watchfaces_glance); - PBL_ASSERTN(structured_glance); - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.h deleted file mode 100644 index cafc212ebe..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_watchfaces_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.c deleted file mode 100644 index b78e938d1e..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_weather.h" - -#include "launcher_app_glance_structured.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/timeline_resources.h" -#include "services/normal/weather/weather_service.h" -#include "services/normal/weather/weather_types.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -// Max size of the temperature and phrase displayed together -#define WEATHER_APP_GLANCE_MAX_STRING_BUFFER_SIZE (WEATHER_SERVICE_MAX_SHORT_PHRASE_BUFFER_SIZE + 5) - -typedef struct LauncherAppGlanceWeather { - char title[APP_NAME_SIZE_BYTES]; - char fallback_title[APP_NAME_SIZE_BYTES]; - char subtitle[WEATHER_APP_GLANCE_MAX_STRING_BUFFER_SIZE]; - KinoReel *icon; - uint32_t icon_resource_id; - EventServiceInfo weather_event_info; -} LauncherAppGlanceWeather; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWeather *weather_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(weather_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWeather *weather_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(weather_glance, title, NULL); -} - -static void prv_weather_glance_subtitle_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceWeather *weather_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (weather_glance) { - strncpy(buffer, weather_glance->subtitle, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_weather_glance_subtitle_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWeather *weather_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (weather_glance) { - event_service_client_unsubscribe(&weather_glance->weather_event_info); - kino_reel_destroy(weather_glance->icon); - } - app_free(weather_glance); -} - -static uint32_t prv_get_weather_icon_resource_id_for_type(WeatherType type) { - AppResourceInfo res_info; - const bool lookup_success = - timeline_resources_get_id_system(weather_type_get_timeline_resource_id(type), - TimelineResourceSizeTiny, SYSTEM_APP, &res_info); - return lookup_success ? res_info.res_id : (uint32_t)RESOURCE_ID_INVALID; -} - -static void prv_weather_event_handler(PBL_UNUSED PebbleEvent *event, void *context) { - LauncherAppGlanceStructured *structured_glance = context; - LauncherAppGlanceWeather *weather_glance = - launcher_app_glance_structured_get_data(structured_glance); - PBL_ASSERTN(weather_glance); - - WeatherLocationForecast *forecast = weather_service_create_default_forecast(); - - // Update the icon for the forecast's weather type - const WeatherType weather_type = NULL_SAFE_FIELD_ACCESS(forecast, current_weather_type, - WeatherType_Unknown); - const uint32_t new_weather_icon_resource_id = - prv_get_weather_icon_resource_id_for_type(weather_type); - if (weather_glance->icon_resource_id != new_weather_icon_resource_id) { - kino_reel_destroy(weather_glance->icon); - weather_glance->icon = kino_reel_create_with_resource(new_weather_icon_resource_id); - weather_glance->icon_resource_id = new_weather_icon_resource_id; - } - - // Zero out the glance's title buffer - const size_t weather_glance_title_size = sizeof(weather_glance->title); - memset(weather_glance->title, 0, weather_glance_title_size); - // Choose the title we should display based on whether or not we have a forecast - const char *title = NULL_SAFE_FIELD_ACCESS(forecast, location_name, - weather_glance->fallback_title); - // Subtract 1 from the size as a shortcut for null terminating the title since we zero it out - // above - strncpy(weather_glance->title, title, weather_glance_title_size - 1); - - // Zero out the glance's subtitle buffer - const size_t weather_glance_subtitle_size = sizeof(weather_glance->subtitle); - memset(weather_glance->subtitle, 0, weather_glance_subtitle_size); - // We'll only set the subtitle if we have a default forecast - if (forecast) { - if (forecast->current_temp == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { - /// Shown when the current temperature is unknown - const char *no_temperature_string = i18n_get("--°", weather_glance); - // Subtract 1 from the size as a shortcut for null terminating the subtitle since we zero it - // out above - strncpy(weather_glance->subtitle, no_temperature_string, weather_glance_subtitle_size - 1); - } else { - /// Shown when today's temperature and conditions phrase is known (e.g. "52° - Fair") - const char *temp_and_phrase_formatter = i18n_get("%i° - %s", weather_glance); - /// Today's current temperature (e.g. "68°") - const char *temp_only_formatter = i18n_get("%i°", weather_glance); - const char *localized_phrase = i18n_get(forecast->current_weather_phrase, weather_glance); - const char *formatter_string = strlen(localized_phrase) ? temp_and_phrase_formatter : - temp_only_formatter; - // It's safe to pass more arguments to snprintf() than might be used by formatter_string - snprintf(weather_glance->subtitle, weather_glance_subtitle_size, formatter_string, - forecast->current_temp, localized_phrase); - } - } - - i18n_free_all(weather_glance); - - weather_service_destroy_default_forecast(forecast); - - // Broadcast to the service that we changed the glance - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); -} - -static const LauncherAppGlanceStructuredImpl s_weather_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_weather_create(const AppMenuNode *node) { - if (!node) { - return NULL; - } - - LauncherAppGlanceWeather *weather_glance = app_zalloc_check(sizeof(*weather_glance)); - // Copy the name of the Weather app as a fallback title - const size_t fallback_title_size = sizeof(weather_glance->fallback_title); - strncpy(weather_glance->fallback_title, node->name, fallback_title_size); - weather_glance->fallback_title[fallback_title_size - 1] = '\0'; - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_weather_structured_glance_impl, - should_consider_slices, weather_glance); - PBL_ASSERTN(structured_glance); - - prv_weather_event_handler(NULL, structured_glance); - - weather_glance->weather_event_info = (EventServiceInfo) { - .type = PEBBLE_WEATHER_EVENT, - .handler = prv_weather_event_handler, - .context = structured_glance, - }; - event_service_client_subscribe(&weather_glance->weather_event_info); - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.h deleted file mode 100644 index c85e0e7134..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_weather_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.c b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.c deleted file mode 100644 index dfefdae79b..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app_glance_workout.h" - -#include "launcher_app_glance_structured.h" - -#include "applib/template_string.h" -#include "apps/system_apps/workout/workout_utils.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_install_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/health_util.h" -#include "services/normal/activity/workout_service.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/string.h" -#include "util/struct.h" - -#include - -#define MAX_SUBTITLE_BUFFER_SIZE (16) - -typedef struct LauncherAppGlanceWorkout { - char title[APP_NAME_SIZE_BYTES]; - char subtitle[MAX_SUBTITLE_BUFFER_SIZE]; - KinoReel *icon; - uint32_t icon_resource_id; - AppTimer *timer; -} LauncherAppGlanceWorkout; - -static KinoReel *prv_get_icon(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWorkout *workout_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(workout_glance, icon, NULL); -} - -static const char *prv_get_title(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWorkout *workout_glance = - launcher_app_glance_structured_get_data(structured_glance); - return NULL_SAFE_FIELD_ACCESS(workout_glance, title, NULL); -} - -static void prv_workout_glance_subtitle_dynamic_text_node_update( - PBL_UNUSED GContext *ctx, PBL_UNUSED GTextNode *node, PBL_UNUSED const GRect *box, - PBL_UNUSED const GTextNodeDrawConfig *config, PBL_UNUSED bool render, char *buffer, size_t buffer_size, - void *user_data) { - LauncherAppGlanceStructured *structured_glance = user_data; - LauncherAppGlanceWorkout *workout_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (workout_glance) { - strncpy(buffer, workout_glance->subtitle, buffer_size); - buffer[buffer_size - 1] = '\0'; - } -} - -static GTextNode *prv_create_subtitle_node(LauncherAppGlanceStructured *structured_glance) { - return launcher_app_glance_structured_create_subtitle_text_node( - structured_glance, prv_workout_glance_subtitle_dynamic_text_node_update); -} - -static void prv_destructor(LauncherAppGlanceStructured *structured_glance) { - LauncherAppGlanceWorkout *workout_glance = - launcher_app_glance_structured_get_data(structured_glance); - if (workout_glance) { - kino_reel_destroy(workout_glance->icon); - app_timer_cancel(workout_glance->timer); - } - app_free(workout_glance); -} - -static bool prv_set_glance_icon(LauncherAppGlanceWorkout *workout_glance, - uint32_t new_icon_resource_id) { - if (workout_glance->icon_resource_id == new_icon_resource_id) { - // Nothing to do, bail out - return false; - } - - // Destroy the existing icon - kino_reel_destroy(workout_glance->icon); - - // Set the new icon and record its resource ID - workout_glance->icon = kino_reel_create_with_resource(new_icon_resource_id); - PBL_ASSERTN(workout_glance->icon); - workout_glance->icon_resource_id = new_icon_resource_id; - - return true; -} - -static uint32_t prv_get_workout_icon_resource_id_for_type(ActivitySessionType type) { - switch (type) { - case ActivitySessionType_Open: - return RESOURCE_ID_WORKOUT_APP_HEART; - case ActivitySessionType_Walk: - return RESOURCE_ID_WORKOUT_APP_WALK_TINY; - case ActivitySessionType_Run: - return RESOURCE_ID_WORKOUT_APP_RUN_TINY; - case ActivitySessionType_Sleep: - case ActivitySessionType_RestfulSleep: - case ActivitySessionType_Nap: - case ActivitySessionType_RestfulNap: - case ActivitySessionTypeCount: - case ActivitySessionType_None: - break; - } - - WTF; -} - -static void prv_timer_callback(void *data) { - LauncherAppGlanceStructured *structured_glance = data; - LauncherAppGlanceWorkout *workout_glance = - launcher_app_glance_structured_get_data(structured_glance); - PBL_ASSERTN(workout_glance); - - ActivitySession automatic_session = {}; - const bool has_automatic_session = - workout_utils_find_ongoing_activity_session(&automatic_session); - - ActivitySessionType workout_type; - int32_t workout_duration_s = 0; - - if (workout_service_is_workout_ongoing()) { - // Manual workout is going on - get the type and duration - workout_service_get_current_workout_type(&workout_type); - workout_service_get_current_workout_info(NULL, &workout_duration_s, NULL, NULL, NULL); - } else if (has_automatic_session) { - // Automatic workout is going on - get the type and duration - workout_type = automatic_session.type; - workout_duration_s = rtc_get_time() - automatic_session.start_utc; - } else { - // No workout is going on - bool glance_changed = false; - - // Set the icon back to default if it isn't already - if (prv_set_glance_icon(workout_glance, RESOURCE_ID_ACTIVITY_TINY)) { - glance_changed = true; - } - - // Clear subtitle if it isn't already - if (!IS_EMPTY_STRING(workout_glance->subtitle)) { - memset(workout_glance->subtitle, 0, sizeof(workout_glance->subtitle)); - glance_changed = true; - } - - // Broadcast to the service that we changed the glance if it was changed - if (glance_changed) { - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); - } - - // Bail since no workout is going on - return; - } - - // Set icon for the ongoing workout type - prv_set_glance_icon(workout_glance, prv_get_workout_icon_resource_id_for_type(workout_type)); - - // Zero out the glance's subtitle buffer - memset(workout_glance->subtitle, 0, sizeof(workout_glance->subtitle)); - - // Set subtitle - health_util_format_hours_minutes_seconds(workout_glance->subtitle, - sizeof(workout_glance->subtitle), workout_duration_s, true, workout_glance); - - i18n_free_all(workout_glance); - - // Broadcast to the service that we changed the glance - launcher_app_glance_structured_notify_service_glance_changed(structured_glance); -} - -static const LauncherAppGlanceStructuredImpl s_workout_structured_glance_impl = { - .get_icon = prv_get_icon, - .get_title = prv_get_title, - .create_subtitle_node = prv_create_subtitle_node, - .destructor = prv_destructor, -}; - -LauncherAppGlance *launcher_app_glance_workout_create(const AppMenuNode *node) { - PBL_ASSERTN(node); - - LauncherAppGlanceWorkout *workout_glance = app_zalloc_check(sizeof(*workout_glance)); - - // Copy the name of the Workout app as the title - const size_t title_size = sizeof(workout_glance->title); - strncpy(workout_glance->title, node->name, title_size); - workout_glance->title[title_size - 1] = '\0'; - - const bool should_consider_slices = false; - LauncherAppGlanceStructured *structured_glance = - launcher_app_glance_structured_create(&node->uuid, &s_workout_structured_glance_impl, - should_consider_slices, workout_glance); - PBL_ASSERTN(structured_glance); - - // Call timer callback and register it to repeat - prv_timer_callback(structured_glance); - - const uint32_t timer_interval_ms = 1000; - workout_glance->timer = app_timer_register_repeatable(timer_interval_ms, prv_timer_callback, - structured_glance, true /* repeating */); - - return &structured_glance->glance; -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.h b/src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.h deleted file mode 100644 index cd621134a7..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance.h" - -#include "process_management/app_menu_data_source.h" - -LauncherAppGlance *launcher_app_glance_workout_create(const AppMenuNode *node); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_menu_layer.c b/src/fw/apps/system_apps/launcher/default/launcher_menu_layer.c deleted file mode 100644 index 38b7d78727..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_menu_layer.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_menu_layer.h" - -#include "launcher_app_glance_service.h" -#include "launcher_menu_layer_private.h" - -#include "applib/graphics/gtypes.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/content_indicator.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/struct.h" - -#define LAUNCHER_MENU_LAYER_CONTENT_INDICATOR_LAYER_HEIGHT (32) -#define LAUNCHER_MENU_LAYER_GENERIC_APP_ICON (RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON) - -//////////////////////////// -// Misc. callbacks/helpers - -static void prv_launch_app_cb(void *data) { - const AppInstallId app_install_id_to_launch = (AppInstallId)data; - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = app_install_id_to_launch, - .common.reason = APP_LAUNCH_USER, - .common.button = BUTTON_ID_SELECT, - }); -} - -static void prv_launcher_menu_layer_mark_dirty(LauncherMenuLayer *launcher_menu_layer) { - if (launcher_menu_layer) { - layer_mark_dirty(menu_layer_get_layer(&launcher_menu_layer->menu_layer)); - } -} - -////////////////////////////////////// -// LauncherAppGlanceService handlers - -static void prv_glance_changed(void *context) { - LauncherMenuLayer *launcher_menu_layer = context; - prv_launcher_menu_layer_mark_dirty(launcher_menu_layer); -} - -//////////////////////// -// MenuLayer callbacks - -static void prv_menu_layer_select(PBL_UNUSED MenuLayer *menu_layer, MenuIndex *cell_index, - void *context) { - LauncherMenuLayer *launcher_menu_layer = context; - AppMenuDataSource *data_source = launcher_menu_layer->data_source; - if (!data_source) { - return; - } - - Window *window = layer_get_window(launcher_menu_layer_get_layer(launcher_menu_layer)); - if (!window) { - return; - } - // Disable all clicking on the window so the user can't scroll anymore - window_set_click_config_provider(window, NULL); - - // Capture what app we should launch - we'll actually launch it as part of an app task callback - // we register in our .draw_row callback so that we don't launch the app until after we finish - // rendering the last frame of the menu layer; we need to do this because some clients (like the - // normal firmware app launcher) rely on the display reflecting the final state of the launcher - // when we launch an app (e.g. for compositor transition animations) - AppMenuNode *node = app_menu_data_source_get_node_at_index(data_source, cell_index->row); - PBL_ASSERTN(node); - launcher_menu_layer->app_to_launch_after_next_render = node->install_id; - - // Now kick off a render of the last frame of the menu layer; note that any menu layer scroll or - // selection animation has already been advanced to completion by the menu layer before it called - // this select click handler - prv_launcher_menu_layer_mark_dirty(launcher_menu_layer); -} - -static uint16_t prv_menu_layer_get_num_rows(PBL_UNUSED MenuLayer *menu_layer, - PBL_UNUSED uint16_t section_index, void *context) { - LauncherMenuLayer *launcher_menu_layer = context; - AppMenuDataSource *data_source = launcher_menu_layer->data_source; - return data_source ? app_menu_data_source_get_count(data_source) : (uint16_t)0; -} - -static void prv_menu_layer_draw_row(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, - void *context) { - LauncherMenuLayer *launcher_menu_layer = context; - AppMenuDataSource *data_source = launcher_menu_layer->data_source; - if (!data_source) { - return; - } - - AppMenuNode *node = app_menu_data_source_get_node_at_index(data_source, cell_index->row); - - const GRect *cell_layer_bounds = &cell_layer->bounds; - const bool is_highlighted = menu_cell_layer_is_highlighted(cell_layer); - launcher_app_glance_service_draw_glance_for_app_node(&launcher_menu_layer->glance_service, - ctx, cell_layer_bounds, is_highlighted, - node); - - // If we should launch an app after this render, push a callback to do that on the app task - if (launcher_menu_layer->app_to_launch_after_next_render != INSTALL_ID_INVALID) { - const AppInstallId app_to_launch_install_id = - launcher_menu_layer->app_to_launch_after_next_render; - // Resetting this here in combination with disabling user input in the select click handler - // (the only place that sets this field) ensures we only do this once - launcher_menu_layer->app_to_launch_after_next_render = INSTALL_ID_INVALID; - process_manager_send_callback_event_to_process(PebbleTask_App, prv_launch_app_cb, - (void *)(uintptr_t)app_to_launch_install_id); - } -} - -static int16_t prv_menu_layer_get_cell_height(PBL_UNUSED MenuLayer *menu_layer, - PBL_UNUSED MenuIndex *cell_index, PBL_UNUSED void *context) { -#if PBL_RECT - return LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT; -#elif PBL_ROUND - return menu_layer_is_index_selected(menu_layer, cell_index) ? - LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT : - LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT; -#else -#error "Unknown display shape type" -#endif -} - -static void prv_play_glance_for_row(LauncherMenuLayer *launcher_menu_layer, uint16_t row) { - if (!launcher_menu_layer || !launcher_menu_layer->selection_animations_enabled) { - return; - } - - // Get the app menu node for the glance that is about to be selected - AppMenuDataSource *data_source = launcher_menu_layer->data_source; - AppMenuNode *node = app_menu_data_source_get_node_at_index(data_source, row); - - // Instruct the launcher app glance service to play the glance for the node - launcher_app_glance_service_play_glance_for_app_node(&launcher_menu_layer->glance_service, node); -} - -static void prv_menu_layer_selection_will_change(MenuLayer *PBL_UNUSED menu_layer, MenuIndex *new_index, - MenuIndex PBL_UNUSED old_index, void *context) { - LauncherMenuLayer *launcher_menu_layer = context; - prv_play_glance_for_row(launcher_menu_layer, new_index->row); -} - -T_STATIC void prv_launcher_menu_layer_set_selection_index(LauncherMenuLayer *launcher_menu_layer, - uint16_t index, MenuRowAlign row_align, - bool animated) { - if (!launcher_menu_layer || !launcher_menu_layer->data_source) { - return; - } - - const MenuIndex new_selected_menu_index = MenuIndex(0, index); - menu_layer_set_selected_index(&launcher_menu_layer->menu_layer, new_selected_menu_index, - row_align, animated); - prv_play_glance_for_row(launcher_menu_layer, index); -} - -//////////////////////// -// Public API - -void launcher_menu_layer_init(LauncherMenuLayer *launcher_menu_layer, - AppMenuDataSource *data_source) { - if (!launcher_menu_layer) { - return; - } - - // We force the launcher menu layer to be the size of the display so that the calculation of - // LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS in launcher_menu_layer_private.h is valid - const GRect frame = DISP_FRAME; - - launcher_menu_layer->title_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_TITLE_FONT); - launcher_menu_layer->subtitle_font = fonts_get_system_font(LAUNCHER_MENU_LAYER_SUBTITLE_FONT); - - Layer *container_layer = &launcher_menu_layer->container_layer; - layer_init(container_layer, &frame); - - launcher_menu_layer->data_source = data_source; - - GRect menu_layer_frame = frame; -#if PBL_ROUND - const int top_bottom_inset = - (frame.size.h - LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT - - (2 * LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT)) / 2; - const GEdgeInsets menu_layer_frame_insets = GEdgeInsets(top_bottom_inset, 0); - menu_layer_frame = grect_inset(menu_layer_frame, menu_layer_frame_insets); -#endif - - MenuLayer *menu_layer = &launcher_menu_layer->menu_layer; - menu_layer_init(menu_layer, &menu_layer_frame); - menu_layer_set_highlight_colors(menu_layer, - LAUNCHER_MENU_LAYER_SELECTION_BACKGROUND_COLOR, - PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); - menu_layer_pad_bottom_enable(menu_layer, false); - menu_layer_set_callbacks(menu_layer, launcher_menu_layer, &(MenuLayerCallbacks) { - .get_num_rows = prv_menu_layer_get_num_rows, - .draw_row = prv_menu_layer_draw_row, - .select_click = prv_menu_layer_select, - .get_cell_height = prv_menu_layer_get_cell_height, - .selection_will_change = prv_menu_layer_selection_will_change, - }); - - // Only setup the content indicator on round -#if PBL_ROUND - const GSize arrow_layer_frame_size = GSize(frame.size.w, - LAUNCHER_MENU_LAYER_CONTENT_INDICATOR_LAYER_HEIGHT); - const GRect up_arrow_layer_frame = (GRect) { - .size = arrow_layer_frame_size, - }; - Layer *up_arrow_layer = &launcher_menu_layer->up_arrow_layer; - layer_init(up_arrow_layer, &up_arrow_layer_frame); - layer_add_child(container_layer, up_arrow_layer); - - const int16_t down_arrow_layer_frame_origin_y = - (int16_t)(frame.size.h - LAUNCHER_MENU_LAYER_CONTENT_INDICATOR_LAYER_HEIGHT); - - const GRect down_arrow_layer_frame = - grect_inset(frame, GEdgeInsets(down_arrow_layer_frame_origin_y, 0, 0, 0)); - Layer *down_arrow_layer = &launcher_menu_layer->down_arrow_layer; - layer_init(down_arrow_layer, &down_arrow_layer_frame); - layer_add_child(container_layer, down_arrow_layer); - - ContentIndicator *content_indicator = - scroll_layer_get_content_indicator(&menu_layer->scroll_layer); - ContentIndicatorConfig content_indicator_config = (ContentIndicatorConfig) { - .layer = up_arrow_layer, - .colors.background = GColorWhite, - .colors.foreground = GColorDarkGray, - }; - content_indicator_configure_direction(content_indicator, ContentIndicatorDirectionUp, - &content_indicator_config); - content_indicator_config.layer = down_arrow_layer; - content_indicator_configure_direction(content_indicator, ContentIndicatorDirectionDown, - &content_indicator_config); -#endif - - // Wait to add the menu layer until after we might have added the content indicators because - // the indicator arrows only get positioned properly if their layers overlap with the menu layer's - // edges - layer_add_child(container_layer, menu_layer_get_layer(menu_layer)); - - launcher_app_glance_service_init(&launcher_menu_layer->glance_service, - LAUNCHER_MENU_LAYER_GENERIC_APP_ICON); - const LauncherAppGlanceServiceHandlers glance_handlers = (LauncherAppGlanceServiceHandlers) { - .glance_changed = prv_glance_changed, - }; - launcher_app_glance_service_set_handlers(&launcher_menu_layer->glance_service, - &glance_handlers, launcher_menu_layer); - - // Select the visually first item from the top - const uint16_t first_index = 0; - const bool animated = false; - prv_launcher_menu_layer_set_selection_index(launcher_menu_layer, first_index, MenuRowAlignBottom, - animated); -} - -Layer *launcher_menu_layer_get_layer(LauncherMenuLayer *launcher_menu_layer) { - if (!launcher_menu_layer) { - return NULL; - } - - return &launcher_menu_layer->container_layer; -} - -void launcher_menu_layer_set_click_config_onto_window(LauncherMenuLayer *launcher_menu_layer, - Window *window) { - if (!launcher_menu_layer || !window) { - return; - } - - menu_layer_set_click_config_onto_window(&launcher_menu_layer->menu_layer, window); -} - -void launcher_menu_layer_reload_data(LauncherMenuLayer *launcher_menu_layer) { - if (!launcher_menu_layer) { - return; - } - - menu_layer_reload_data(&launcher_menu_layer->menu_layer); -} - -void launcher_menu_layer_set_selection_state(LauncherMenuLayer *launcher_menu_layer, - const LauncherMenuLayerSelectionState *new_state) { - if (!launcher_menu_layer || !launcher_menu_layer->data_source || !new_state) { - return; - } - - const bool animated = false; - - prv_launcher_menu_layer_set_selection_index(launcher_menu_layer, new_state->row_index, - MenuRowAlignNone, animated); - - const GPoint new_scroll_offset = GPoint(0, new_state->scroll_offset_y); - scroll_layer_set_content_offset(&launcher_menu_layer->menu_layer.scroll_layer, new_scroll_offset, - animated); -} - -void launcher_menu_layer_get_selection_vertical_range(const LauncherMenuLayer *launcher_menu_layer, - GRangeVertical *vertical_range_out) { - if (!launcher_menu_layer || !vertical_range_out) { - return; - } - - GRect selection_global_rect; - layer_get_global_frame(&launcher_menu_layer->menu_layer.inverter.layer, &selection_global_rect); - - *vertical_range_out = (GRangeVertical) { - .origin_y = selection_global_rect.origin.y, - .size_h = selection_global_rect.size.h, - }; -} - -void launcher_menu_layer_get_selection_state(const LauncherMenuLayer *launcher_menu_layer, - LauncherMenuLayerSelectionState *state_out) { - if (!launcher_menu_layer || !launcher_menu_layer->data_source || !state_out) { - return; - } - - const MenuLayer *menu_layer = &launcher_menu_layer->menu_layer; - const ScrollLayer *scroll_layer = &menu_layer->scroll_layer; - - *state_out = (LauncherMenuLayerSelectionState) { - .row_index = menu_layer_get_selected_index(menu_layer).row, - // This cast is required because this ScrollLayer function's argument isn't const - .scroll_offset_y = scroll_layer_get_content_offset((ScrollLayer *)scroll_layer).y, - }; -} - -void launcher_menu_layer_set_selection_animations_enabled(LauncherMenuLayer *launcher_menu_layer, - bool enabled) { - if (!launcher_menu_layer) { - return; - } - launcher_menu_layer->selection_animations_enabled = enabled; - if (enabled) { - const MenuIndex selected_index = - menu_layer_get_selected_index(&launcher_menu_layer->menu_layer); - prv_play_glance_for_row(launcher_menu_layer, selected_index.row); - } else { - launcher_app_glance_service_rewind_current_glance(&launcher_menu_layer->glance_service); - } -} - -void launcher_menu_layer_deinit(LauncherMenuLayer *launcher_menu_layer) { - if (!launcher_menu_layer) { - return; - } - - launcher_app_glance_service_deinit(&launcher_menu_layer->glance_service); - menu_layer_deinit(&launcher_menu_layer->menu_layer); - -#if PBL_ROUND - layer_deinit(&launcher_menu_layer->up_arrow_layer); - layer_deinit(&launcher_menu_layer->down_arrow_layer); -#endif - layer_deinit(&launcher_menu_layer->container_layer); -} diff --git a/src/fw/apps/system_apps/launcher/default/launcher_menu_layer.h b/src/fw/apps/system_apps/launcher/default/launcher_menu_layer.h deleted file mode 100644 index a8339822ac..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_menu_layer.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "launcher_app_glance_service.h" - -#include "process_management/app_menu_data_source.h" - -#if PLATFORM_ROBERT || PLATFORM_OBELIX -#define LAUNCHER_MENU_LAYER_TITLE_FONT (FONT_KEY_GOTHIC_24_BOLD) -#define LAUNCHER_MENU_LAYER_SUBTITLE_FONT (FONT_KEY_GOTHIC_18) -#else -#define LAUNCHER_MENU_LAYER_TITLE_FONT (FONT_KEY_GOTHIC_18_BOLD) -#define LAUNCHER_MENU_LAYER_SUBTITLE_FONT (FONT_KEY_GOTHIC_14) -#endif - -#define LAUNCHER_MENU_LAYER_SELECTION_BACKGROUND_COLOR (PBL_IF_COLOR_ELSE(GColorVividCerulean, \ - GColorBlack)) - -typedef struct LauncherMenuLayer { - Layer container_layer; - MenuLayer menu_layer; -#if PBL_ROUND - Layer up_arrow_layer; - Layer down_arrow_layer; -#endif - GFont title_font; - GFont subtitle_font; - AppMenuDataSource *data_source; - LauncherAppGlanceService glance_service; - bool selection_animations_enabled; - AppInstallId app_to_launch_after_next_render; -} LauncherMenuLayer; - -typedef struct LauncherMenuLayerSelectionState { - int16_t scroll_offset_y; - uint16_t row_index; -} LauncherMenuLayerSelectionState; - -void launcher_menu_layer_init(LauncherMenuLayer *launcher_menu_layer, - AppMenuDataSource *data_source); - -Layer *launcher_menu_layer_get_layer(LauncherMenuLayer *launcher_menu_layer); - -void launcher_menu_layer_set_click_config_onto_window(LauncherMenuLayer *launcher_menu_layer, - Window *window); - -void launcher_menu_layer_reload_data(LauncherMenuLayer *launcher_menu_layer); - -void launcher_menu_layer_set_selection_state(LauncherMenuLayer *launcher_menu_layer, - const LauncherMenuLayerSelectionState *new_state); - -void launcher_menu_layer_get_selection_state(const LauncherMenuLayer *launcher_menu_layer, - LauncherMenuLayerSelectionState *state_out); - -void launcher_menu_layer_get_selection_vertical_range(const LauncherMenuLayer *launcher_menu_layer, - GRangeVertical *vertical_range_out); - -void launcher_menu_layer_set_selection_animations_enabled(LauncherMenuLayer *launcher_menu_layer, - bool enabled); - -void launcher_menu_layer_deinit(LauncherMenuLayer *launcher_menu_layer); diff --git a/src/fw/apps/system_apps/launcher/default/launcher_menu_layer_private.h b/src/fw/apps/system_apps/launcher/default/launcher_menu_layer_private.h deleted file mode 100644 index a10c6b7f51..0000000000 --- a/src/fw/apps/system_apps/launcher/default/launcher_menu_layer_private.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/math.h" - -#if PLATFORM_ROBERT || PLATFORM_OBELIX -#define LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT (53) -#else -#define LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT (42) -#endif - -#define LAUNCHER_MENU_LAYER_CELL_ROUND_FOCUSED_CELL_HEIGHT (52) -#define LAUNCHER_MENU_LAYER_CELL_ROUND_UNFOCUSED_CELL_HEIGHT (38) - -#if PBL_ROUND -//! Two "unfocused" cells above and below one centered "focused" cell -#define LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS (3) -#else -#define LAUNCHER_MENU_LAYER_NUM_VISIBLE_ROWS \ - (DIVIDE_CEIL(DISP_ROWS, LAUNCHER_MENU_LAYER_CELL_RECT_CELL_HEIGHT)) -#endif diff --git a/src/fw/apps/system_apps/launcher/launcher_app.h b/src/fw/apps/system_apps/launcher/launcher_app.h deleted file mode 100644 index 1b43605f2c..0000000000 --- a/src/fw/apps/system_apps/launcher/launcher_app.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#if PLATFORM_TINTIN -#include "legacy/launcher_app.h" -#else -#include "default/launcher_app.h" -#endif - -#include "process_management/pebble_process_md.h" - -#define RETURN_TIMEOUT_TICKS (5 * RTC_TICKS_HZ) - -const PebbleProcessMd* launcher_menu_app_get_app_info(void); diff --git a/src/fw/apps/system_apps/launcher/legacy/launcher_app.c b/src/fw/apps/system_apps/launcher/legacy/launcher_app.c deleted file mode 100644 index c4e8a6b403..0000000000 --- a/src/fw/apps/system_apps/launcher/legacy/launcher_app.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "launcher_app.h" - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "applib/legacy2/ui/menu_layer_legacy2.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "process_management/app_menu_data_source.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource.h" -#include "resource/resource_ids.auto.h" -#include "shell/normal/app_idle_timeout.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/notifications/alerts_private.h" -#include "applib/ui/kino/kino_layer.h" -#include "system/passert.h" - -#include -#include -#include -#include - -typedef struct LauncherMenuData { - Window window; - StatusBarLayer status_bar; - Layer status_bar_icons_layer; - MenuLayer menu_layer; - AppMenuDataSource data_source; - - EventServiceInfo battery_state_event_info; - EventServiceInfo do_not_disturb_event_info; - EventServiceInfo pebble_app_event_info; - - KinoLayer connectivity_icon; - uint32_t connectivity_icon_id; - KinoLayer battery_icon; - uint32_t battery_icon_id; -} LauncherMenuData; - -typedef struct LauncherMenuPersistedData { - int scroll_offset_y; - int menu_index_row; - bool valid; - RtcTicks leave_time; -} LauncherMenuPersistedData; - -static LauncherMenuPersistedData s_launcher_menu_persisted_data; - -/////////////////// -// Status Bar - -static bool prv_is_pebble_app_connected(void) { - return (comm_session_get_system_session() != NULL); -} - -static uint32_t prv_get_resource_id_for_battery_charge_state(BatteryChargeState charge_state) { - const uint32_t battery_base_resource_id = (charge_state.is_charging || charge_state.is_plugged) - ? RESOURCE_ID_TINTIN_LAUNCHER_CHARGING_5_PERCENT - : RESOURCE_ID_TINTIN_LAUNCHER_BATTERY_5_PERCENT; - if (charge_state.charge_percent <= 100) { - return battery_base_resource_id + (charge_state.charge_percent / 10); - } else { - WTF; - } -} - -static void prv_reload_status_bar_icons(LauncherMenuData *data) { - // Draw airplane mode, do not disturb, or silent status icon. - AlertMask alert_mask = alerts_get_mask(); - - // Get the connectivity ResourceId - uint32_t new_connectivity_icon_id = RESOURCE_ID_INVALID; - if (bt_ctl_is_airplane_mode_on()) { - new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE; - } else if (do_not_disturb_is_active()) { - new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND; - } else if (!prv_is_pebble_app_connected()) { - new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED; - } else if (alert_mask != AlertMaskAllOn) { - if (alert_mask == AlertMaskPhoneCalls) { - new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY; - } - } else if (prv_is_pebble_app_connected()) { - new_connectivity_icon_id = RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED; - // probably need an All Muted icon here - } - - // replace the image if the connectivity ResourceId has changed - if (data->connectivity_icon_id != new_connectivity_icon_id) { - data->connectivity_icon_id = new_connectivity_icon_id; - kino_layer_set_reel_with_resource(&data->connectivity_icon, data->connectivity_icon_id); - } - - // Get the connectivity ResourceId - const uint32_t new_battery_icon_id = - prv_get_resource_id_for_battery_charge_state(battery_get_charge_state()); - - // replace the image if the battery ResourceId has changed - if (data->battery_icon_id != new_battery_icon_id) { - data->battery_icon_id = new_battery_icon_id; - kino_layer_set_reel_with_resource(&data->battery_icon, data->battery_icon_id); - } -} - -/////////////////// -// Events - -static void prv_event_handler(PebbleEvent *e, void *context) { - LauncherMenuData *data = (LauncherMenuData *) context; - prv_reload_status_bar_icons(data); -} - -static void prv_subscribe_to_event(EventServiceInfo *result, - PebbleEventType type, - void *callback_context) { - *result = (EventServiceInfo) { - .type = type, - .handler = prv_event_handler, - .context = callback_context, - }; - event_service_client_subscribe(result); -} - -/////////////////// -// AppMenuDataSource callbacks - -static bool prv_app_filter_callback(struct AppMenuDataSource * const source, - AppInstallEntry *entry) { - if (app_install_entry_is_watchface(entry) - || app_install_entry_is_hidden((entry))) { - return false; // Skip watchfaces and hidden apps - } - return true; -} - -static void prv_data_changed(void *context) { - LauncherMenuData *data = context; - menu_layer_reload_data(&data->menu_layer); -} - -////////////// -// MenuLayer callbacks - -static void select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, - LauncherMenuData *data) { - AppMenuNode *node = app_menu_data_source_get_node_at_index(&data->data_source, cell_index->row); - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = node->install_id, - .common.reason = APP_LAUNCH_USER, - .common.button = BUTTON_ID_SELECT, - }); -} - -static uint16_t get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, - LauncherMenuData *data) { - return app_menu_data_source_get_count(&data->data_source); -} - -static void draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *cell_index, - LauncherMenuData *data) { - app_menu_data_source_draw_row(&data->data_source, ctx, cell_layer, cell_index); -} - -/////////////////// -// Window callbacks - -static void prv_window_load(Window *window) { - LauncherMenuData *data = window_get_user_data(window); - GRect bounds = window->layer.bounds; - - status_bar_layer_init(&data->status_bar); - status_bar_layer_set_colors(&data->status_bar, GColorBlack, GColorWhite); - layer_add_child(&window->layer, status_bar_layer_get_layer(&data->status_bar)); - - static const int kino_width = 20; - static const int kino_padding = 6; - kino_layer_init(&data->connectivity_icon, &GRect(kino_padding, 0, - kino_width, STATUS_BAR_LAYER_HEIGHT)); - kino_layer_set_alignment(&data->connectivity_icon, GAlignLeft); - layer_add_child(&window->layer, kino_layer_get_layer(&data->connectivity_icon)); - - kino_layer_init(&data->battery_icon, &GRect(DISP_COLS - kino_width - kino_padding, 0, - kino_width, STATUS_BAR_LAYER_HEIGHT)); - kino_layer_set_alignment(&data->battery_icon, GAlignRight); - layer_add_child(&window->layer, kino_layer_get_layer(&data->battery_icon)); - - prv_reload_status_bar_icons(data); - - bounds = grect_inset(bounds, GEdgeInsets(STATUS_BAR_LAYER_HEIGHT, 0, 0, 0)); - - MenuLayer *menu_layer = &data->menu_layer; - menu_layer_init(menu_layer, &bounds); - app_menu_data_source_init(&data->data_source, &(AppMenuDataSourceCallbacks) { - .changed = prv_data_changed, - .filter = prv_app_filter_callback, - }, data); - - app_menu_data_source_enable_icons(&data->data_source, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON); - - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) get_num_rows_callback, - .draw_row = (MenuLayerDrawRowCallback) draw_row_callback, - .select_click = (MenuLayerSelectCallback) select_callback, - }); - menu_layer_set_click_config_onto_window(menu_layer, window); - layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); - - scroll_layer_set_shadow_hidden(&data->menu_layer.scroll_layer, true); - - if (s_launcher_menu_persisted_data.valid) { - // If we have a saved state, reload it. - menu_layer_set_selected_index(&data->menu_layer, - MenuIndex(0, s_launcher_menu_persisted_data.menu_index_row), - MenuRowAlignNone, - false); - scroll_layer_set_content_offset(&data->menu_layer.scroll_layer, - GPoint(0, s_launcher_menu_persisted_data.scroll_offset_y), - false); - } else { - // If we are resetting the launcher, select the second entry (Settings is at the top) - menu_layer_set_selected_index(&data->menu_layer, MenuIndex(0, 1), MenuRowAlignNone, false); - } - - prv_subscribe_to_event(&data->battery_state_event_info, PEBBLE_BATTERY_STATE_CHANGE_EVENT, data); - prv_subscribe_to_event(&data->do_not_disturb_event_info, PEBBLE_DO_NOT_DISTURB_EVENT, data); - prv_subscribe_to_event(&data->pebble_app_event_info, PEBBLE_COMM_SESSION_EVENT, data); -} - -static void prv_window_unload(Window *window) { - LauncherMenuData *data = window_get_user_data(window); - - kino_layer_deinit(&data->connectivity_icon); - kino_layer_deinit(&data->battery_icon); - - // Save the current state of the menu so we can restore it later. - s_launcher_menu_persisted_data = (LauncherMenuPersistedData) { - .valid = true, - .scroll_offset_y = scroll_layer_get_content_offset(&data->menu_layer.scroll_layer).y, - .menu_index_row = menu_layer_get_selected_index(&data->menu_layer).row, - .leave_time = rtc_get_ticks(), - }; - - menu_layer_deinit(&data->menu_layer); - app_menu_data_source_deinit(&data->data_source); -} - - -static void launcher_menu_push_window(void) { - LauncherMenuData *data = app_zalloc(sizeof(LauncherMenuData)); - app_state_set_user_data(data); - - // Push launcher menu window: - Window *window = &data->window; - window_init(window, WINDOW_NAME("Launcher Menu")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - - const bool animated = false; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void s_main(void) { - const LauncherMenuArgs *args = (const LauncherMenuArgs *) app_manager_get_task_context()->args; - if (args && args->reset_scroll) { - if ((s_launcher_menu_persisted_data.leave_time + RETURN_TIMEOUT_TICKS) <= rtc_get_ticks()) { - s_launcher_menu_persisted_data.valid = false; - } - } - - launcher_menu_push_window(); - - app_idle_timeout_start(); - - app_event_loop(); -} - -const PebbleProcessMd* launcher_menu_app_get_app_info() { - static const PebbleProcessMdSystem s_launcher_menu_app_info = { - .common = { - .main_func = s_main, - // UUID: dec0424c-0625-4878-b1f2-147e57e83688 - .uuid = {0xde, 0xc0, 0x42, 0x4c, 0x06, 0x25, 0x48, 0x78, - 0xb1, 0xf2, 0x14, 0x7e, 0x57, 0xe8, 0x36, 0x88}, - .visibility = ProcessVisibilityHidden - }, - .name = "Launcher", - }; - return (const PebbleProcessMd*) &s_launcher_menu_app_info; -} diff --git a/src/fw/apps/system_apps/launcher/legacy/launcher_app.h b/src/fw/apps/system_apps/launcher/legacy/launcher_app.h deleted file mode 100644 index c1ad9264b3..0000000000 --- a/src/fw/apps/system_apps/launcher/legacy/launcher_app.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "../launcher_app.h" - -#include - -typedef struct LauncherMenuArgs { - bool reset_scroll; -} LauncherMenuArgs; diff --git a/src/fw/apps/system_apps/music_app.c b/src/fw/apps/system_apps/music_app.c deleted file mode 100644 index 866142afc8..0000000000 --- a/src/fw/apps/system_apps/music_app.c +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "music_app.h" - -#include "applib/app.h" -#include "applib/event_service_client.h" -#include "applib/app_timer.h" -#include "applib/fonts/fonts.h" -#include "applib/preferred_content_size.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/property_animation.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/system_icons.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/music.h" -#include "services/normal/vibes/vibe_score.h" -#include "shell/system_theme.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#include -#include -#include - -enum ActionBarState { - ActionBarStateSkip, - ActionBarStateVolume, - ActionBarStateLongPress, -}; - -typedef struct MusicAppSizeConfig { - const char *music_time_font_key; - const char *no_music_font_key; - int16_t horizontal_margin; - - GRangeVertical artist_field; - GRangeVertical title_field; - GRangeVertical time_field; - - GRect cassette_rect; - int16_t cassette_animation_x; - int32_t cassette_animation_time; - - GRangeVertical track_field; - int16_t track_corner_radius; - - GPoint no_music_img_pos; - GRangeVertical no_music_text_field; -} MusicAppSizeConfig; - -// The reference animations ran at 28fps. -#define ANIMATION_FRAME_MS (1000 / 28) - -#define CONTENT_VERTICAL_OFFSET PBL_IF_RECT_ELSE(0, 5) -static const MusicAppSizeConfig s_music_size_config_medium = { - .music_time_font_key = FONT_KEY_GOTHIC_14, - .no_music_font_key = FONT_KEY_GOTHIC_18_BOLD, - .horizontal_margin = PBL_IF_RECT_ELSE(12, 25), - - .artist_field = { - .origin_y = 31 + CONTENT_VERTICAL_OFFSET, - .size_h = 21, - }, - .title_field = { - .origin_y = 53 + CONTENT_VERTICAL_OFFSET, - .size_h = 44, - }, - .time_field = { - .origin_y = 106 + CONTENT_VERTICAL_OFFSET, - .size_h = 14, - }, - - .cassette_rect = {{0, -1 + CONTENT_VERTICAL_OFFSET}, {43, 28}}, - .cassette_animation_x = 60, - .cassette_animation_time = 1 * ANIMATION_FRAME_MS, - - .track_field = { - .origin_y = 120 + CONTENT_VERTICAL_OFFSET, - .size_h = 4, - }, - .track_corner_radius = 1, - - .no_music_img_pos = {PBL_IF_RECT_ELSE(29, 53), PBL_IF_RECT_ELSE(25, 26)}, - .no_music_text_field = { - .origin_y = PBL_IF_RECT_ELSE(107, 104), - .size_h = 58, - }, -}; - -static const MusicAppSizeConfig s_music_size_config_large = { - .music_time_font_key = FONT_KEY_GOTHIC_18_BOLD, - .no_music_font_key = FONT_KEY_GOTHIC_28, - .horizontal_margin = 10, - - .artist_field = { - .origin_y = 30, - .size_h = 21, - }, - .title_field = { - .origin_y = 60, - .size_h = 80, - }, - .time_field = { - .origin_y = 146, - .size_h = 20, - }, - - .cassette_rect = {{0, -8}, {43, 28}}, - .cassette_animation_x = 140, - .cassette_animation_time = 3 * ANIMATION_FRAME_MS, - - .track_field = { - .origin_y = 168, - .size_h = 10, - }, - .track_corner_radius = 4, - - .no_music_img_pos = {57, 46}, - .no_music_text_field = { - .origin_y = 131, - .size_h = 58, - }, -}; - -static const MusicAppSizeConfig * const s_music_size_configs[NumPreferredContentSizes] = { - [PreferredContentSizeSmall] = &s_music_size_config_medium, - [PreferredContentSizeMedium] = &s_music_size_config_medium, - [PreferredContentSizeLarge] = &s_music_size_config_large, - [PreferredContentSizeExtraLarge] = &s_music_size_config_large, -}; - -static const MusicAppSizeConfig *prv_config(void) { - return s_music_size_configs[PreferredContentSizeDefault]; -} - -static int prv_content_width(void) { - return DISP_COLS - ACTION_BAR_WIDTH - (prv_config()->horizontal_margin * 2); -} - -static int prv_text_layer_width(void) { - return prv_content_width() + PBL_IF_RECT_ELSE(prv_config()->horizontal_margin / 2, 0); -} - -static GRect prv_artist_rect(void) { - const MusicAppSizeConfig *config = prv_config(); - return GRect(config->horizontal_margin, config->artist_field.origin_y, - prv_text_layer_width(), config->artist_field.size_h); -} - -static GRect prv_title_rect(void) { - const MusicAppSizeConfig *config = prv_config(); - return GRect(config->horizontal_margin, config->title_field.origin_y, - prv_text_layer_width(), config->title_field.size_h); -} - -static GRect prv_time_rect(void) { - const MusicAppSizeConfig *config = prv_config(); - return GRect(config->horizontal_margin, config->time_field.origin_y, - prv_content_width(), config->time_field.size_h); -} - -static GRect prv_cassette_rect(void) { - const MusicAppSizeConfig *config = prv_config(); - const int16_t cassette_x = config->horizontal_margin + - PBL_IF_RECT_ELSE(0, prv_content_width() - config->cassette_rect.size.w); - return GRect(cassette_x, config->cassette_rect.origin.y, - config->cassette_rect.size.w, config->cassette_rect.size.h); -} - -static GRect prv_track_rect(void) { - const MusicAppSizeConfig *config = prv_config(); - return GRect(config->horizontal_margin, config->track_field.origin_y, - prv_content_width(), config->track_field.size_h); -} - -static const ButtonId BUTTON_FORWARD = BUTTON_ID_DOWN; -static const ButtonId BUTTON_BACKWARD = BUTTON_ID_UP; - -static const int16_t BOUNCEBACK_OFFSET = 6; -// These offsets get rid of the empty space above the first line of text, -// enabling neater animations by clipping immediately at the top of the text. -static const int16_t TITLE_BOUNDS_OFFSET = 5; -static const int16_t ARTIST_BOUNDS_OFFSET = 3; -static const int16_t TIME_BOUNDS_OFFSET = 2; - -static const uint32_t VOLUME_REPEAT_INTERVAL_MS = 400; -static const uint32_t ACTION_BAR_TIMEOUT_MS = 2000; -static const uint32_t VOLUME_ICON_TIMEOUT_MS = 2000; - -typedef struct { - Window window; - BitmapLayer bitmap_layer; - GBitmap bitmap; - TextLayer text_layer; -} MusicNoMusicWindow; - -typedef struct { - Window window; - ActionBarLayer action_bar; - - TextLayer artist_text_layer; - char artist_buffer[MUSIC_BUFFER_LENGTH]; - - TextLayer title_text_layer; - char title_buffer[MUSIC_BUFFER_LENGTH]; - - StatusBarLayer status_layer; - - TextLayer position_text_layer; - char position_buffer[9]; // 9 will fit "00:00:00" - - TextLayer length_text_layer; - char length_buffer[9]; - - Animation *transition; - AppTimer *volume_icon_timer; - - MusicPlayState current_play_state; - - GBitmap icon_skip_forward; - GBitmap icon_skip_backward; - GBitmap icon_ellipsis; - GBitmap icon_pause; - GBitmap icon_play; - GBitmap icon_play_pause; - GBitmap icon_volume_up; - GBitmap icon_volume_down; - GBitmap image_cassette; - GBitmap image_pause; - GBitmap image_volume_up; - GBitmap image_volume_down; - - Layer cassette_container; - BitmapLayer cassette_layer; - GBitmap *cassette_current_icon; - - EventServiceInfo event_info; - - ProgressLayer track_pos_bar; - uint32_t track_length; - uint32_t track_pos; - bool pause_track_pos_updates; - - enum ActionBarState action_bar_state; - AppTimer *action_bar_revert_timer; - AppTimer *volume_repeat_timer; - bool volume_is_up; - - MusicNoMusicWindow *no_music_window; - -#if CAPABILITY_HAS_VIBE_SCORES - VibeScore *score; -#endif -} MusicAppData; - -static void prv_set_action_bar_state(MusicAppData *data, enum ActionBarState state); - -static void prv_trigger_cassette_icon_switch(GBitmap *bitmap, bool animated); -static void prv_update_cassette_icon(MusicAppData *data, bool animated); - -static void prv_do_haptic_feedback_vibe(MusicAppData *data) { -#if CAPABILITY_HAS_VIBE_SCORES - vibe_score_do_vibe(data->score); -#endif -} - -static void prv_handle_volume_icon_timer(void *context) { - MusicAppData *data = context; - data->volume_icon_timer = NULL; - prv_update_cassette_icon(data, true); -} - -static void prv_show_volume_image(GBitmap *bitmap) { - MusicAppData *data = app_state_get_user_data(); - if (data->volume_icon_timer) { - app_timer_reschedule(data->volume_icon_timer, VOLUME_ICON_TIMEOUT_MS); - } else { - data->volume_icon_timer = app_timer_register(VOLUME_ICON_TIMEOUT_MS, - prv_handle_volume_icon_timer, data); - } - prv_trigger_cassette_icon_switch(bitmap, true); -} - -static void prv_change_volume(bool volume_is_up) { - if (!music_is_command_supported(MusicCommandVolumeUp) || - !music_is_command_supported(MusicCommandVolumeDown)) { - return; - } - - MusicAppData *data = app_state_get_user_data(); - prv_show_volume_image(volume_is_up ? &data->image_volume_up : &data->image_volume_down); - music_command_send(volume_is_up ? MusicCommandVolumeUp : MusicCommandVolumeDown); -} - -static Animation* prv_create_layer_upwards_animation(Layer *layer, int16_t offset) { - GPoint target = GPoint(0, -layer->bounds.size.h - offset); - GPoint origin = GPoint(0, -offset); - Animation *animation = property_animation_get_animation( - property_animation_create_bounds_origin(layer, &origin, &target)); - animation_set_duration(animation, 4 * ANIMATION_FRAME_MS); - animation_set_curve(animation, AnimationCurveEaseIn); - return animation; -} - -static Animation* prv_create_upwards_animation(MusicAppData *data) { - return animation_spawn_create( - prv_create_layer_upwards_animation(&data->artist_text_layer.layer, ARTIST_BOUNDS_OFFSET), - prv_create_layer_upwards_animation(&data->title_text_layer.layer, TITLE_BOUNDS_OFFSET), - prv_create_layer_upwards_animation(&data->position_text_layer.layer, TIME_BOUNDS_OFFSET), - prv_create_layer_upwards_animation(&data->length_text_layer.layer, TIME_BOUNDS_OFFSET), - NULL); -} - -static const PropertyAnimationImplementation s_frame_layer_implementation = { - .base = { - .update = (AnimationUpdateImplementation) property_animation_update_grect, - }, - .accessors = { - .setter = { .grect = (const GRectSetter) layer_set_frame_by_value, }, - .getter = { .grect = (const GRectGetter) layer_get_frame_by_value, }, - }, -}; - -static Animation *prv_create_layer_bounceback_animation(Layer *layer, GRect origin) { - GRect target = origin; - origin.origin.y -= BOUNCEBACK_OFFSET; - Animation *animation = property_animation_get_animation( - property_animation_create(&s_frame_layer_implementation, layer, &origin, &target)); - animation_set_duration(animation, 4 * ANIMATION_FRAME_MS); - animation_set_curve(animation, AnimationCurveEaseOut); - return animation; -} - -static Animation *prv_create_bounceback_animation(MusicAppData *data) { - const GRect time_rect = prv_time_rect(); - return animation_spawn_create( - prv_create_layer_bounceback_animation(&data->artist_text_layer.layer, prv_artist_rect()), - prv_create_layer_bounceback_animation(&data->title_text_layer.layer, prv_title_rect()), - prv_create_layer_bounceback_animation(&data->position_text_layer.layer, time_rect), - prv_create_layer_bounceback_animation(&data->length_text_layer.layer, time_rect), - NULL); -} - -static void prv_update_track_progress(MusicAppData *data); - -static void prv_flip_animated_text(Animation *animation, bool finished, void *context) { - MusicAppData *data = context; - data->pause_track_pos_updates = false; - prv_update_track_progress(data); - music_get_now_playing(data->title_buffer, data->artist_buffer, NULL); - // Restore the layers to their original bounds for the next part of the animation. - data->title_text_layer.layer.bounds.origin.y = -TITLE_BOUNDS_OFFSET; - data->artist_text_layer.layer.bounds.origin.y = -ARTIST_BOUNDS_OFFSET; - data->position_text_layer.layer.bounds.origin.y = -TIME_BOUNDS_OFFSET; - data->length_text_layer.layer.bounds.origin.y = -TIME_BOUNDS_OFFSET; -} - -static inline bool prv_should_animate_casssette(void) { - return music_get_playback_state() != MusicPlayStatePaused; -} - -static Animation *prv_create_cassette_animation(MusicAppData *data) { - const MusicAppSizeConfig *config = prv_config(); - const GRect cassette_rect = prv_cassette_rect(); - GPoint left_target = GPoint(-cassette_rect.size.w - cassette_rect.origin.x, 0); - Animation *cassette_left = property_animation_get_animation( - property_animation_create_bounds_origin(&data->cassette_container, &GPointZero, - &left_target)); - Animation *cassette_right = property_animation_get_animation( - property_animation_create_bounds_origin(&data->cassette_container, - &GPoint(config->cassette_animation_x, 0), &GPoint(-4, 0))); - Animation *cassette_bounceback = property_animation_get_animation( - property_animation_create_bounds_origin(&data->cassette_container, &GPoint(-4, 0), - &GPointZero)); - animation_set_duration(cassette_left, 4 * ANIMATION_FRAME_MS); - animation_set_curve(cassette_left, AnimationCurveEaseIn); - animation_set_duration(cassette_right, config->cassette_animation_time); - animation_set_curve(cassette_left, AnimationCurveLinear); - animation_set_duration(cassette_bounceback, 4 * ANIMATION_FRAME_MS); - animation_set_curve(cassette_bounceback, AnimationCurveEaseOut); - Animation *sequence = animation_sequence_create(cassette_left, cassette_right, - cassette_bounceback, NULL); - if (!prv_should_animate_casssette()) { - animation_set_play_count(sequence, 0); - } - return sequence; -} - -static void prv_trigger_track_change_animation(MusicAppData *data) { - // Animation structure: - // - Master animation - // - Cassette animation - // - Move to left - // - Move in from right - // - Bounceback - // - Upwards animation - // - Per-layer animations - // - (flip text) - // - Bounceback animation - // - Per-layer animations - - if (animation_is_scheduled(data->transition)) { - return; - } - data->pause_track_pos_updates = true; - Animation *scroll_up = prv_create_upwards_animation(data); - Animation *bounceback = prv_create_bounceback_animation(data); - animation_set_handlers(scroll_up, (AnimationHandlers) { - .stopped = prv_flip_animated_text, - }, data); - - Animation *complete; - complete = animation_sequence_create(scroll_up, bounceback, NULL); - data->transition = complete; - animation_schedule(complete); -} - -static void prv_update_icon(Animation *animation, bool finished, void *context) { - MusicAppData *data = app_state_get_user_data(); - GBitmap *bitmap = context; - bitmap_layer_set_bitmap(&data->cassette_layer, bitmap); - data->cassette_layer.layer.bounds.origin.y = 0; -} - -static void prv_trigger_cassette_icon_switch(GBitmap *new_bitmap, bool animated) { - MusicAppData *data = app_state_get_user_data(); - - if (!animated) { - bitmap_layer_set_bitmap(&data->cassette_layer, new_bitmap); - data->cassette_current_icon = new_bitmap; - return; - } - // Never animate an icon to itself. We can't use the current value of the bitmap layer itself, - // because that will cause false positives if an icon change is triggered, but a revert - // is triggered before the first half of the icon animation completes (currently 107 ms). - if (new_bitmap == data->cassette_current_icon) { - return; - } - - GRect cassette_rect = prv_cassette_rect(); - Animation *disappear_animation = property_animation_get_animation( - property_animation_create_bounds_origin(&data->cassette_layer.layer, &GPointZero, - &GPoint(0, -cassette_rect.size.h))); - animation_set_duration(disappear_animation, 3 * ANIMATION_FRAME_MS); - animation_set_curve(disappear_animation, AnimationCurveEaseIn); - - GRect origin = cassette_rect; - origin.origin.y -= BOUNCEBACK_OFFSET; - - Animation *bounceback_animation = property_animation_get_animation( - property_animation_create(&s_frame_layer_implementation, &data->cassette_layer.layer, - &origin, &cassette_rect)); - animation_set_duration(bounceback_animation, 4 * ANIMATION_FRAME_MS); - animation_set_curve(bounceback_animation, AnimationCurveEaseOut); - - animation_set_handlers(disappear_animation, (AnimationHandlers) { - .stopped = prv_update_icon, - }, new_bitmap); - - Animation *sequence = animation_sequence_create(disappear_animation, bounceback_animation, NULL); - - data->cassette_current_icon = new_bitmap; - animation_schedule(sequence); -} - -static void prv_skipping_click_config_provider(void *data); -static void prv_volume_click_config_provider(void *data); - -static void prv_update_cassette_icon(MusicAppData *data, bool animated) { - if (music_get_playback_state() == MusicPlayStatePaused) { - prv_trigger_cassette_icon_switch(&data->image_pause, animated); - } else { - prv_trigger_cassette_icon_switch(&data->image_cassette, animated); - } -} - -static void prv_update_ui_state_skipping(MusicAppData *data, bool animated) { - action_bar_layer_set_click_config_provider(&data->action_bar, - prv_skipping_click_config_provider); - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_FORWARD, - &data->icon_skip_forward, animated); - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_BACKWARD, - &data->icon_skip_backward, animated); - if (music_get_playback_state() == MusicPlayStatePaused) { - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, &data->icon_play, - animated); - } else { - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, &data->icon_ellipsis, - animated); - } -} - -static void prv_update_ui_state_volume(MusicAppData *data, bool animated) { - if (data->action_bar_state == ActionBarStateVolume) { - action_bar_layer_set_click_config_provider(&data->action_bar, - prv_volume_click_config_provider); - } - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_UP, &data->icon_volume_up, - animated); - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_DOWN, &data->icon_volume_down, - animated); - GBitmap const *select_bitmap; - switch (music_get_playback_state()) { - case MusicPlayStatePlaying: select_bitmap = &data->icon_pause; break; - case MusicPlayStatePaused: select_bitmap = &data->icon_play; break; - default: select_bitmap = &data->icon_play_pause; break; - } - action_bar_layer_set_icon_animated(&data->action_bar, BUTTON_ID_SELECT, select_bitmap, animated); -} - -static void prv_update_ui_state(MusicAppData *data, bool animated) { - if (data->action_bar_state == ActionBarStateSkip) { - prv_update_ui_state_skipping(data, animated); - } else { - prv_update_ui_state_volume(data, animated); - } - - if (music_get_playback_state() != data->current_play_state) { - data->current_play_state = music_get_playback_state(); - prv_update_cassette_icon(data, animated); - } -} - -static void prv_set_action_bar_state(MusicAppData *data, enum ActionBarState state) { - data->action_bar_state = state; - prv_update_ui_state(data, true); -} - -static void prv_action_bar_revert(void *context) { - MusicAppData *data = context; - data->action_bar_revert_timer = NULL; - prv_set_action_bar_state(data, ActionBarStateSkip); -} - -static void reset_action_bar_revert_timer(MusicAppData *data) { - app_timer_reschedule(data->action_bar_revert_timer, ACTION_BAR_TIMEOUT_MS); -} - -static void prv_skip_click_handler(ClickRecognizerRef recognizer, void *context) { - // no animations on tintin - Animation *animation = prv_create_cassette_animation(context); - animation_schedule(animation); - if (click_recognizer_get_button_id(recognizer) == BUTTON_BACKWARD) { - music_command_send(MusicCommandPreviousTrack); - } else { - music_command_send(MusicCommandNextTrack); - } -} - -static void prv_volume_click_handler(ClickRecognizerRef recognizer, void *context) { - reset_action_bar_revert_timer(context); - // TODO: absolute volume + volume indicator, when that information is available. - bool is_volume_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); - prv_change_volume(is_volume_up); - - // Trigger haptic feedback only on repeat - if (click_number_of_clicks_counted(recognizer) >= 2) { - prv_do_haptic_feedback_vibe(context); - } -} - -static void prv_ellipsis_click_handler(ClickRecognizerRef recognizer, void *context) { - MusicAppData *data = context; - data->action_bar_revert_timer = app_timer_register(ACTION_BAR_TIMEOUT_MS, prv_action_bar_revert, - data); - prv_set_action_bar_state(data, ActionBarStateVolume); -} - -static void prv_toggle_playing(void) { - music_command_send(MusicCommandTogglePlayPause); -} - -static void prv_play_pause_click_handler(ClickRecognizerRef recognizer, void *context) { - reset_action_bar_revert_timer(context); - prv_toggle_playing(); -} - -static void prv_handle_volume_repeat(void *context) { - MusicAppData *data = context; - if (!data->volume_repeat_timer) { - return; - } - data->volume_repeat_timer = app_timer_register(VOLUME_REPEAT_INTERVAL_MS, - prv_handle_volume_repeat, data); - prv_change_volume(data->volume_is_up); - prv_do_haptic_feedback_vibe(context); -} - -static void prv_volume_long_click_start_handler(ClickRecognizerRef recognizer, void *context) { - const bool volume_is_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); - prv_change_volume(volume_is_up); - prv_set_action_bar_state(context, ActionBarStateLongPress); - MusicAppData *data = context; - data->volume_is_up = volume_is_up; - data->volume_repeat_timer = app_timer_register(VOLUME_REPEAT_INTERVAL_MS, - prv_handle_volume_repeat, data); - prv_do_haptic_feedback_vibe(data); -} - -static void prv_volume_long_click_end_handler(ClickRecognizerRef recognizer, void *context) { - MusicAppData *data = context; - prv_set_action_bar_state(data, ActionBarStateSkip); - app_timer_cancel(data->volume_repeat_timer); - data->volume_repeat_timer = NULL; -} - -static void prv_play_pause_long_click_start_handler(ClickRecognizerRef recognizer, void *context) { - prv_toggle_playing(); - prv_set_action_bar_state(context, ActionBarStateLongPress); - prv_do_haptic_feedback_vibe(context); -} - -static void prv_play_pause_long_click_end_handler(ClickRecognizerRef recognizer, void *context) { - prv_set_action_bar_state(context, ActionBarStateSkip); -} - -static void prv_skipping_click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, prv_skip_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_skip_click_handler); - if (music_get_playback_state() == MusicPlayStatePaused) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_play_pause_click_handler); - } else { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_ellipsis_click_handler); - } - window_long_click_subscribe(BUTTON_ID_UP, 0, prv_volume_long_click_start_handler, - prv_volume_long_click_end_handler); - window_long_click_subscribe(BUTTON_ID_DOWN, 0, prv_volume_long_click_start_handler, - prv_volume_long_click_end_handler); - window_long_click_subscribe(BUTTON_ID_SELECT, 0, prv_play_pause_long_click_start_handler, - prv_play_pause_long_click_end_handler); -} - -static void prv_volume_click_config_provider(void *context) { - window_single_repeating_click_subscribe(BUTTON_ID_UP, VOLUME_REPEAT_INTERVAL_MS, - prv_volume_click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_DOWN, VOLUME_REPEAT_INTERVAL_MS, - prv_volume_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_play_pause_click_handler); -} - -static void prv_update_layout(MusicAppData *data) { - // Hide track position bar if progress reporting not supported - bool hide_layer = !music_is_progress_reporting_supported(); - layer_set_hidden(&data->track_pos_bar.layer, hide_layer); - layer_set_hidden(&data->position_text_layer.layer, hide_layer); - layer_set_hidden(&data->length_text_layer.layer, hide_layer); -} - -static void prv_unload_no_music_window(Window *window) { - MusicNoMusicWindow *music_window = (MusicNoMusicWindow *)window; - gbitmap_deinit(&music_window->bitmap); - bitmap_layer_deinit(&music_window->bitmap_layer); - text_layer_deinit(&music_window->text_layer); - i18n_free_all(music_window); -} - -static void prv_handle_no_music_back(ClickRecognizerRef recognizer, void *context) { - app_window_stack_pop_all(true); -} - -static void prv_no_music_window_click_config(void *context) { - window_single_click_subscribe(BUTTON_ID_BACK, prv_handle_no_music_back); -} - -static MusicNoMusicWindow *prv_create_no_music_window(void) { - MusicNoMusicWindow *window = app_malloc_check(sizeof(MusicNoMusicWindow)); - window_init(&window->window, WINDOW_NAME("NoMusicWindow")); - window_set_background_color(&window->window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - window_set_window_handlers(&window->window, &(WindowHandlers) { - .unload = prv_unload_no_music_window - }); - - const MusicAppSizeConfig *config = prv_config(); - - gbitmap_init_with_resource(&window->bitmap, RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC); - const GSize NO_MUSIC_IMAGE_SIZE = window->bitmap.bounds.size; - const GRect NO_MUSIC_IMAGE_RECT = GRect(config->no_music_img_pos.x, - config->no_music_img_pos.y, - NO_MUSIC_IMAGE_SIZE.w, NO_MUSIC_IMAGE_SIZE.h); - bitmap_layer_init(&window->bitmap_layer, &NO_MUSIC_IMAGE_RECT); - bitmap_layer_set_bitmap(&window->bitmap_layer, &window->bitmap); - bitmap_layer_set_compositing_mode(&window->bitmap_layer, GCompOpSet); - - const GRect NO_MUSIC_TEXT_RECT = GRect(0, config->no_music_text_field.origin_y, - DISP_COLS, config->no_music_text_field.size_h); - - text_layer_init_with_parameters(&window->text_layer, - &NO_MUSIC_TEXT_RECT, - i18n_get("START PLAYBACK\nON YOUR PHONE", window), - fonts_get_system_font(config->no_music_font_key), - GColorBlack, GColorClear, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&window->window.layer, &window->bitmap_layer.layer); - layer_add_child(&window->window.layer, &window->text_layer.layer); - window_set_click_config_provider(&window->window, prv_no_music_window_click_config); - return window; -} - -static void prv_push_no_music_window(MusicAppData *data) { - if (data->no_music_window) { - return; - } - data->no_music_window = prv_create_no_music_window(); - app_window_stack_push(&data->no_music_window->window, false); -} - -static void prv_pop_no_music_window(MusicAppData *data) { - if (data->no_music_window) { - app_window_stack_remove(&data->no_music_window->window, true); - app_free(data->no_music_window); - data->no_music_window = NULL; - } -} - -static void prv_update_now_playing(MusicAppData *data) { - layer_set_hidden((Layer *)&data->track_pos_bar, !music_is_progress_reporting_supported()); - - char artist_buffer[MUSIC_BUFFER_LENGTH]; - char title_buffer[MUSIC_BUFFER_LENGTH]; - music_get_now_playing(title_buffer, artist_buffer, NULL); - - if (music_needs_user_to_start_playback_on_phone()) { - prv_push_no_music_window(data); - } else { - prv_pop_no_music_window(data); - } - - bool title_changed = strncmp(data->title_buffer, title_buffer, MUSIC_BUFFER_LENGTH) != 0; - bool artist_changed = strncmp(data->artist_buffer, artist_buffer, MUSIC_BUFFER_LENGTH) != 0; - if (title_changed || artist_changed) { - // Animating nothing looks weird, so don't do that. - if (data->artist_buffer[0] == 0 && data->title_buffer[0] == 0) { - strncpy(data->artist_buffer, artist_buffer, MUSIC_BUFFER_LENGTH); - strncpy(data->title_buffer, title_buffer, MUSIC_BUFFER_LENGTH); - // It is sufficient to mark one layer as dirty. - layer_mark_dirty(&data->title_text_layer.layer); - } else if (title_changed) { - prv_trigger_track_change_animation(data); - } - } - prv_update_layout(data); -} - -static void prv_copy_time_period(char *buffer, size_t n, uint32_t period_s) { - uint32_t hours = period_s / SECONDS_PER_HOUR; - uint32_t minutes = (period_s % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE; - uint32_t seconds = period_s % SECONDS_PER_MINUTE; -#pragma GCC diagnostic ignored "-Wformat-truncation" - if (hours > 0) { - snprintf(buffer, n, "%"PRIu32":%02"PRIu32":%02"PRIu32, hours, minutes, seconds); - } else { - snprintf(buffer, n, "%"PRIu32":%02"PRIu32, minutes, seconds); - } -} - -static void prv_update_track_progress(MusicAppData *data) { - if (data->pause_track_pos_updates) { - return; - } - if (!music_is_progress_reporting_supported()) { - progress_layer_set_progress(&data->track_pos_bar, 0); - } else { - unsigned int percent = MIN((data->track_pos * 100) / data->track_length, 100); - progress_layer_set_progress(&data->track_pos_bar, percent); - prv_copy_time_period(data->position_buffer, sizeof(data->position_buffer), - data->track_pos / 1000); - prv_copy_time_period(data->length_buffer, sizeof(data->length_buffer), - data->track_length / 1000); - } -} - -static void prv_update_pos(void) { - MusicAppData *data = app_state_get_user_data(); - music_get_pos(&data->track_pos, &data->track_length); - prv_update_track_progress(data); -} - -static void prv_handle_tick_time(struct tm *time, TimeUnits units_changed) { - if (music_get_playback_state() == MusicPlayStatePlaying) { - prv_update_pos(); - } -} - -static void prv_set_pos_update_timer(MusicAppData* data, MusicPlayState playstate) { - if (!music_is_progress_reporting_supported()) { - return; - } - switch (playstate) { - case MusicPlayStatePlaying: - // We need to update the progress bar every second. - tick_timer_service_subscribe(SECOND_UNIT, prv_handle_tick_time); - break; - default: - // We're no longer updating the progress bar; unsubscribe. - tick_timer_service_unsubscribe(); - } -} - -static void prv_configure_music_text_layer( - TextLayer *text_layer, char* text_buffer, const GRect *rect, int16_t y_offset, - GTextAlignment align, GFont font) { - text_layer_init_with_parameters(text_layer, rect, text_buffer, font, - GColorBlack, GColorClear, align, GTextOverflowModeFill); - layer_set_bounds(&text_layer->layer, &GRect(0, -y_offset, - rect->size.w, rect->size.h + y_offset)); -} - -static void prv_init_ui(Window *window) { - MusicAppData *data = window_get_user_data(window); - - window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - - const GSize WINDOW_SIZE = window->layer.bounds.size; - - const GTextAlignment ARTIST_TITLE_TEXT_ALIGNMENT = PBL_IF_RECT_ELSE(GTextAlignmentLeft, - GTextAlignmentRight); - - const MusicAppSizeConfig *config = prv_config(); - - const GRect artist_rect = prv_artist_rect(); - const GRect title_rect = prv_title_rect(); - const GRect time_rect = prv_time_rect(); - const GRect cassette_rect = prv_cassette_rect(); - const GRect track_rect = prv_track_rect(); - - prv_configure_music_text_layer(&data->artist_text_layer, data->artist_buffer, - &artist_rect, ARTIST_BOUNDS_OFFSET, - ARTIST_TITLE_TEXT_ALIGNMENT, - system_theme_get_font_for_default_size(TextStyleFont_Header)); - layer_add_child(&data->window.layer, &data->artist_text_layer.layer); - - prv_configure_music_text_layer(&data->position_text_layer, data->position_buffer, &time_rect, - TIME_BOUNDS_OFFSET, GTextAlignmentLeft, - fonts_get_system_font(config->music_time_font_key)); - layer_add_child(&data->window.layer, &data->position_text_layer.layer); - - prv_configure_music_text_layer(&data->length_text_layer, data->length_buffer, &time_rect, - TIME_BOUNDS_OFFSET, GTextAlignmentRight, - fonts_get_system_font(config->music_time_font_key)); - layer_add_child(&data->window.layer, &data->length_text_layer.layer); - - prv_configure_music_text_layer(&data->title_text_layer, data->title_buffer, &title_rect, - TITLE_BOUNDS_OFFSET, ARTIST_TITLE_TEXT_ALIGNMENT, - system_theme_get_font_for_default_size(TextStyleFont_Subtitle)); - text_layer_set_line_spacing_delta(&data->title_text_layer, -2); - - layer_add_child(&data->window.layer, &data->title_text_layer.layer); - - const int16_t horizontal_margin = config->horizontal_margin; - layer_init(&data->cassette_container, &GRect(0, WINDOW_SIZE.h - horizontal_margin - 24, - WINDOW_SIZE.w - ACTION_BAR_WIDTH, 24)); - layer_add_child(&data->window.layer, &data->cassette_container); - layer_set_clips(&data->cassette_container, false); - - bitmap_layer_init(&data->cassette_layer, &cassette_rect); - bitmap_layer_set_bitmap(&data->cassette_layer, &data->image_cassette); - data->cassette_current_icon = &data->image_cassette; - const GAlign CASSETTE_LAYER_ALIGNMENT = PBL_IF_RECT_ELSE(GAlignTopLeft, GAlignTopRight); - bitmap_layer_set_alignment(&data->cassette_layer, CASSETTE_LAYER_ALIGNMENT); - bitmap_layer_set_compositing_mode(&data->cassette_layer, GCompOpSet); - layer_add_child(&data->cassette_container, &data->cassette_layer.layer); - - progress_layer_init(&data->track_pos_bar, &track_rect); - progress_layer_set_background_color(&data->track_pos_bar, - PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); - progress_layer_set_foreground_color(&data->track_pos_bar, - PBL_IF_COLOR_ELSE(GColorRed, GColorBlack)); - progress_layer_set_corner_radius(&data->track_pos_bar, config->track_corner_radius); - layer_add_child(&window->layer, (Layer *)&data->track_pos_bar); - - ActionBarLayer *action_bar = &data->action_bar; - data->action_bar_state = ActionBarStateSkip; - action_bar_layer_init(action_bar); - action_bar_layer_set_context(action_bar, data); - action_bar_layer_add_to_window(action_bar, window); - - StatusBarLayer *status_layer = &data->status_layer; - status_bar_layer_init(status_layer); - GRect status_layer_frame = status_layer->layer.frame; - const int16_t STATUS_BAR_LAYER_WIDTH = PBL_IF_RECT_ELSE(WINDOW_SIZE.w - ACTION_BAR_WIDTH, - WINDOW_SIZE.w); - status_layer_frame.size.w = STATUS_BAR_LAYER_WIDTH; - layer_set_frame(&status_layer->layer, &status_layer_frame); - status_bar_layer_set_colors(&data->status_layer, GColorClear, GColorBlack); - layer_add_child(&data->window.layer, &status_layer->layer); - - music_get_pos(&data->track_pos, &data->track_length); - -#if CAPABILITY_HAS_VIBE_SCORES - data->score = vibe_score_create_with_resource(RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK); -#endif - - prv_update_now_playing(data); - prv_update_layout(data); - prv_update_track_progress(data); - prv_update_ui_state(data, false); -} - -static void prv_push_window(MusicAppData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Music")); - window_set_user_data(window, data); - window_set_status_bar_icon(window, (GBitmap*)&s_status_icon_music_bitmap); - - const bool animated = true; - app_window_stack_push(window, animated); - prv_init_ui(window); -} - -static void prv_music_event_handler(PebbleEvent *event, void *context) { - MusicAppData *data = app_state_get_user_data(); - switch (event->media.type) { - case PebbleMediaEventTypeNowPlayingChanged: - prv_update_now_playing(data); - return; - case PebbleMediaEventTypePlaybackStateChanged: { - prv_set_pos_update_timer(data, event->media.playback_state); - prv_update_ui_state(data, true); - return; - } - case PebbleMediaEventTypeVolumeChanged: - case PebbleMediaEventTypeServerConnected: - case PebbleMediaEventTypeServerDisconnected: - case PebbleMediaEventTypeTrackPosChanged: - music_get_pos(&data->track_pos, &data->track_length); - prv_update_track_progress(data); - prv_update_layout(data); - return; - default: return; - } -} - -//////////////////// -// App boilerplate - -static void prv_handle_init(void) { - MusicAppData *data = app_malloc_check(sizeof(MusicAppData)); - *data = (MusicAppData){}; - app_state_set_user_data(data); - - data->event_info = (EventServiceInfo){ - .type = PEBBLE_MEDIA_EVENT, - .handler = prv_music_event_handler, - }; - - // TODO: Once we have some sort of system-wide "needs bluetooth" assertion, invoke that here. - - data->current_play_state = MusicPlayStateInvalid; - - gbitmap_init_with_resource(&data->icon_skip_backward, RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD); - gbitmap_init_with_resource(&data->icon_skip_forward, RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD); - gbitmap_init_with_resource(&data->icon_ellipsis, RESOURCE_ID_MUSIC_ICON_ELLIPSIS); - gbitmap_init_with_resource(&data->icon_play, RESOURCE_ID_MUSIC_ICON_PLAY); - gbitmap_init_with_resource(&data->icon_pause, RESOURCE_ID_MUSIC_ICON_PAUSE); - gbitmap_init_with_resource(&data->icon_play_pause, RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE); - gbitmap_init_with_resource(&data->icon_volume_up, RESOURCE_ID_MUSIC_ICON_VOLUME_UP); - gbitmap_init_with_resource(&data->icon_volume_down, RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN); - gbitmap_init_with_resource(&data->image_cassette, RESOURCE_ID_MUSIC_LARGE_CASSETTE); - gbitmap_init_with_resource(&data->image_pause, RESOURCE_ID_MUSIC_LARGE_PAUSED); - gbitmap_init_with_resource(&data->image_volume_up, RESOURCE_ID_MUSIC_LARGE_VOLUME_UP); - gbitmap_init_with_resource(&data->image_volume_down, RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN); - - event_service_client_subscribe(&data->event_info); - prv_push_window(data); - - // Overall reduce the latency at the expense of some power... - music_request_reduced_latency(true); - - // Give us a super responsive initial period: - music_request_low_latency_for_period(5000); - - prv_set_pos_update_timer(data, music_get_playback_state()); -} - -static void prv_handle_deinit(void) { - music_request_reduced_latency(false); - - MusicAppData *data = app_state_get_user_data(); - i18n_free_all(data); - - // we'll be cleaned up properly by the system -} - -static void prv_main(void) { - prv_handle_init(); - - app_event_loop(); - - prv_handle_deinit(); -} - -const PebbleProcessMd* music_app_get_info(void) { - // [INTL] The app name should come from a standard app resource, so it's localizable. - static const PebbleProcessMdSystem s_app_info = { - .common = { - .main_func = &prv_main, - // UUID: 1f03293d-47af-4f28-b960-f2b02a6dd757 - .uuid = {0x1f, 0x03, 0x29, 0x3d, 0x47, 0xaf, 0x4f, 0x28, - 0xb9, 0x60, 0xf2, 0xb0, 0x2a, 0x6d, 0xd7, 0x57}, - }, - .name = i18n_noop("Music"), -#if CAPABILITY_HAS_APP_GLANCES - .icon_resource_id = RESOURCE_ID_AUDIO_CASSETTE_TINY, -#endif - }; - return (const PebbleProcessMd*) &s_app_info; -} - diff --git a/src/fw/apps/system_apps/music_app.h b/src/fw/apps/system_apps/music_app.h deleted file mode 100644 index db41bd2141..0000000000 --- a/src/fw/apps/system_apps/music_app.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* music_app_get_info(); - diff --git a/src/fw/apps/system_apps/notifications_app.c b/src/fw/apps/system_apps/notifications_app.c deleted file mode 100644 index 0abf859638..0000000000 --- a/src/fw/apps/system_apps/notifications_app.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "notifications_app.h" - -#include -#include - -#include "applib/app.h" -#include "applib/app_exit_reason.h" -#include "applib/preferred_content_size.h" -#include "applib/fonts/fonts.h" -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/gdraw_command_list.h" -#include "applib/graphics/graphics.h" -#include "applib/ui/dialogs/actionable_dialog.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/menu_cell_layer.h" -#include "applib/ui/ui.h" -#include "applib/ui/window_stack_private.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/system_icons.h" -#include "popups/notifications/notification_window.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/timeline/notification_layout.h" -#include "shell/system_theme.h" -#include "system/passert.h" -#include "util/date.h" -#include "util/list.h" -#include "util/string.h" - -#if !TINTIN_FORCE_FIT -typedef struct LoadedNotificationNode { - ListNode node; - TimelineItem notification; - GDrawCommandImage *icon; - bool icon_is_default; -} LoadedNotificationNode; - -typedef struct NotificationNode { - ListNode node; - Uuid id; -} NotificationNode; - -typedef struct NotificationsData { - Window window; - MenuLayer menu_layer; - TextLayer text_layer; - NotificationNode *notification_list; - LoadedNotificationNode *loaded_notification_list; - EventServiceInfo notification_event_info; - ActionableDialog *actionable_dialog; -#if PBL_ROUND - StatusBarLayer status_bar_layer; -#endif -} NotificationsData; - -static NotificationsData *s_data = NULL; - -static const unsigned int MAX_ACTIVE_NOTIFICATIONS = 6; - -static bool prv_loaded_notification_list_filter_cb(ListNode *node, void *data) { - LoadedNotificationNode *loaded_notification = (LoadedNotificationNode *)node; - Uuid *id = data; - return uuid_equal(&loaded_notification->notification.header.id, id); -} - -static bool prv_notification_list_filter_cb(ListNode *node, void *data) { - NotificationNode *notification = (NotificationNode *)node; - Uuid *id = data; - return uuid_equal(¬ification->id, id); -} - -static NotificationNode *prv_find_notification(NotificationNode *list, Uuid *id) { - return (NotificationNode *)list_find((ListNode *)list, - prv_notification_list_filter_cb, - id); -} - -static LoadedNotificationNode *prv_find_loaded_notification(LoadedNotificationNode *list, - Uuid *id) { - return (LoadedNotificationNode *)list_find((ListNode *)list, - prv_loaded_notification_list_filter_cb, - id); -} - -static NotificationNode *prv_notification_list_add_notification_by_id( - NotificationNode **notification_list, Uuid *id) { - NotificationNode *new_node = app_malloc_check(sizeof(NotificationNode)); - - list_init((ListNode*) new_node); - new_node->id = *id; - - *notification_list = (NotificationNode*) list_prepend((ListNode*) *notification_list, - (ListNode*) new_node); - - return new_node; -} - -static void prv_notification_list_remove_notification_by_id( - NotificationNode **notification_list, Uuid *id) { - - NotificationNode *node = prv_find_notification(*notification_list, id); - list_remove((ListNode *)node, (ListNode **)notification_list, NULL); -} - -static NotificationNode *prv_add_notification(NotificationsData *data, Uuid *id) { - NotificationNode *node = prv_notification_list_add_notification_by_id(&data->notification_list, - id); - return node; -} - -static void prv_remove_notification(NotificationsData *data, Uuid *id) { - prv_notification_list_remove_notification_by_id(&data->notification_list, id); -} - -static bool prv_notif_iterator_callback(void *data, SerializedTimelineItemHeader *header) { - return (prv_add_notification(data, &header->common.id) != NULL); -} - -static void prv_load_notification_storage(NotificationsData *data) { - notification_storage_iterate(&prv_notif_iterator_callback, data); -} - -static void prv_notification_list_deinit(NotificationNode *notification_list) { - while (notification_list) { - NotificationNode *node = notification_list; - notification_list = (NotificationNode*) list_pop_head((ListNode*) notification_list); - app_free(node); - } -} - -static void prv_unload_loaded_notification(LoadedNotificationNode *loaded_notif) { - timeline_item_free_allocated_buffer(&loaded_notif->notification); - gdraw_command_image_destroy(loaded_notif->icon); - app_free(loaded_notif); -} - -static NOINLINE LoadedNotificationNode *prv_loaded_notification_list_load_item( - LoadedNotificationNode **loaded_list, NotificationNode *node) { - if (node == NULL) { - return NULL; - } - - LoadedNotificationNode *loaded_node = prv_find_loaded_notification(*loaded_list, &node->id); - if (loaded_node) { - return loaded_node; - } - - // unload old notifications - if (list_count((ListNode*) *loaded_list) > MAX_ACTIVE_NOTIFICATIONS) { - LoadedNotificationNode *old_node = (LoadedNotificationNode*) list_get_tail( - (ListNode*) *loaded_list); - list_remove((ListNode*) old_node, (ListNode**) loaded_list, NULL); - prv_unload_loaded_notification(old_node); - } - - // load the notification - TimelineItem notification; - if (!notification_storage_get(&node->id, ¬ification)) { - return NULL; - } - - // track the loaded notification - loaded_node = app_malloc_check(sizeof(LoadedNotificationNode)); - - list_init((ListNode*) loaded_node); - loaded_node->notification = notification; - - TimelineResourceId timeline_res_id = attribute_get_uint32(¬ification.attr_list, - AttributeIdIconTiny, - NOTIF_FALLBACK_ICON); - - // Read the associated pin's app id - TimelineItem pin; - if (timeline_resources_is_system(timeline_res_id) || - pin_db_read_item_header(&pin, ¬ification.header.parent_id) != S_SUCCESS) { - pin.header.parent_id = (Uuid)UUID_INVALID; - } - - TimelineResourceInfo timeline_res = { - .res_id = timeline_res_id, - .app_id = &pin.header.parent_id, - .fallback_id = NOTIF_FALLBACK_ICON - }; - AppResourceInfo icon_res_info; - timeline_resources_get_id(&timeline_res, TimelineResourceSizeTiny, &icon_res_info); - loaded_node->icon = gdraw_command_image_create_with_resource_system(icon_res_info.res_app_num, - icon_res_info.res_id); - loaded_node->icon_is_default = (timeline_res_id == NOTIF_FALLBACK_ICON) || - (timeline_res_id == TIMELINE_RESOURCE_NOTIFICATION_GENERIC); - - *loaded_list = (LoadedNotificationNode*) list_prepend((ListNode*) *loaded_list, - (ListNode*)loaded_node); - - return loaded_node; -} - -static void prv_loaded_notification_list_deinit(LoadedNotificationNode *loaded_list) { - while (loaded_list) { - LoadedNotificationNode *node = loaded_list; - loaded_list = (LoadedNotificationNode*) list_pop_head((ListNode*) loaded_list); - prv_unload_loaded_notification(node); - } -} - -// Return true if successful -static bool prv_push_notification_window(NotificationsData *data) { - notification_window_init(false /*is_modal*/); - - // Bail if a notification came in ahead of us and created a modal window - // before we had a chance to react to the select button event. - if (notification_window_is_modal()) { - return false; - } - - // iterate over visible items as visible (including the groups) in reverse order - // since notification_window shows each newly added notification first - NotificationNode *node = (NotificationNode*)list_get_tail(&data->notification_list->node); - while (node) { - notification_window_add_notification_by_id(&node->id); - node = (NotificationNode*)list_get_prev(&node->node); - } - - notification_window_show(); - return true; -} - -/////////////////// -// Confirm Dialog - -static void prv_dialog_unloaded(void *context) { - NotificationsData *data = context; - data->actionable_dialog = NULL; -} - -static void prv_confirmed_handler(ClickRecognizerRef recognizer, void *context) { - NotificationsData *data = context; - notification_storage_reset_and_init(); - prv_loaded_notification_list_deinit(data->loaded_notification_list); - data->loaded_notification_list = NULL; - prv_notification_list_deinit(data->notification_list); - data->notification_list = NULL; - prv_load_notification_storage(data); - actionable_dialog_pop(data->actionable_dialog); - - // Create and display DONE dialog - SimpleDialog *confirmation_dialog = simple_dialog_create("Notifications Cleared"); - Dialog *dialog = simple_dialog_get_dialog(confirmation_dialog); - dialog_set_text(dialog, i18n_get("Done", data)); - dialog_set_icon(dialog, RESOURCE_ID_RESULT_SHREDDED_LARGE); - static const uint32_t DIALOG_TIMEOUT = 2000; - dialog_set_timeout(dialog, DIALOG_TIMEOUT); - - // Set the app exit reason so we will go to the watchface upon exit - app_exit_reason_set(APP_EXIT_ACTION_PERFORMED_SUCCESSFULLY); - - // Pop all windows so we'll soon exit the app - app_window_stack_pop_all(true /* animated */); - - // Immediately push this result dialog so it's the last thing we see before exiting - app_simple_dialog_push(confirmation_dialog); -} - - -static void prv_dialog_click_config(void *context) { - NotificationsData *data = app_state_get_user_data(); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_confirmed_handler); - window_set_click_context(BUTTON_ID_SELECT, data); -} - -static void prv_settings_clear_history_window_push(NotificationsData *data) { - ActionableDialog *actionable_dialog = actionable_dialog_create("Clear Notifications"); - actionable_dialog_set_click_config_provider(actionable_dialog, prv_dialog_click_config); - actionable_dialog_set_action_bar_type(actionable_dialog, DialogActionBarConfirm, NULL); - Dialog *dialog = actionable_dialog_get_dialog(actionable_dialog); - dialog_set_text(dialog, i18n_get("Clear history?", data)); - TimelineResourceInfo timeline_res = { - .res_id = TIMELINE_RESOURCE_GENERIC_QUESTION, - }; - AppResourceInfo icon_res_info; - timeline_resources_get_id(&timeline_res, TimelineResourceSizeLarge, &icon_res_info); - dialog_set_icon(dialog, icon_res_info.res_id); - dialog_set_icon_animate_direction(dialog, DialogIconAnimationFromRight); - dialog_set_callbacks(dialog, &(DialogCallbacks) { - .unload = prv_dialog_unloaded, - }, data); - app_actionable_dialog_push(actionable_dialog); - data->actionable_dialog = actionable_dialog; -} - -static GColor prv_invert_bw_color(GColor color) { - if (gcolor_equal(color, GColorBlack)) { - return GColorWhite; - } else if (gcolor_equal(color, GColorWhite)) { - return GColorBlack; - } - return color; -} - -static void prv_invert_pdc_colors(GDrawCommandProcessor *processor, - GDrawCommand *processed_command, - size_t processed_command_max_size, - const GDrawCommandList* list, - const GDrawCommand *command) { - gdraw_command_set_stroke_color(processed_command, - prv_invert_bw_color(gdraw_command_get_stroke_color((GDrawCommand *)command))); - gdraw_command_set_fill_color(processed_command, - prv_invert_bw_color(gdraw_command_get_fill_color((GDrawCommand *)command))); -} - -static void prv_draw_pdc_bw_inverted(GContext *ctx, GDrawCommandImage *image, GPoint offset) { - GDrawCommandProcessor processor = { - .command = prv_invert_pdc_colors, - }; - gdraw_command_image_draw_processed(ctx, image, offset, &processor); -} - -////////////// -// MenuLayer callbacks - -static void prv_draw_notification_cell_rect(GContext *ctx, const Layer *cell_layer, - const char *title, const char *subtitle, - GDrawCommandImage *icon) { - const GRect cell_layer_bounds = cell_layer->bounds; - const GSize icon_size = gdraw_command_image_get_bounds_size(icon); - const int16_t icon_left_margin = menu_cell_basic_horizontal_inset(); - if (icon) { - void (*draw_func)(GContext *, GDrawCommandImage *, GPoint) = gdraw_command_image_draw; -#if PBL_BW - if (menu_cell_layer_is_highlighted(cell_layer)) { - draw_func = prv_draw_pdc_bw_inverted; - } -#endif - - // Inset the draw box from the left to leave some margin on the icon's left side - GRect box = cell_layer_bounds; - box.origin.x += icon_left_margin; - - // Align the icon to the left of the draw box, centered vertically - GRect icon_rect = (GRect) { .size = gdraw_command_image_get_bounds_size(icon) }; - grect_align(&icon_rect, &box, GAlignLeft, false /* clip */); - - draw_func(ctx, icon, icon_rect.origin); - } - - // Temporarily inset the cell layer's bounds from the left so the text doesn't draw over any - // icon on the left - Layer *mutable_cell_layer = (Layer *)cell_layer; - const int text_left_margin = - icon_left_margin + MAX(icon_size.w, ATTRIBUTE_ICON_TINY_SIZE_PX); - mutable_cell_layer->bounds = grect_inset(cell_layer_bounds, - GEdgeInsets(0, 5, 0, text_left_margin)); - - const GFont title_font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); - const GFont subtitle_font = system_theme_get_font_for_default_size(TextStyleFont_Caption); - menu_cell_basic_draw_custom(ctx, cell_layer, title_font, title, NULL /* value_font */, - NULL /* value */, subtitle_font, subtitle, NULL /* icon */, - false /* icon_on_right */, GTextOverflowModeTrailingEllipsis); - - // Restore the cell layer's bounds - mutable_cell_layer->bounds = cell_layer_bounds; -} - -//! outer_box is passed as a pointer to save stack space -static int16_t prv_draw_centered_text_line_in(GContext *ctx, GFont font, const GRect *outer_box, - const char *text, GAlign align) { - if (!text) { - return 0; - } - - GRect text_box = *outer_box; - text_box.size.h = fonts_get_font_height(font); - grect_align(&text_box, outer_box, align, true); - - graphics_draw_text(ctx, text, font, text_box, GTextOverflowModeTrailingEllipsis, - GTextAlignmentCenter, NULL); - - return text_box.size.h; -} - -//! box is passed as a pointer to save stack space -//! after this call, box will point to the GRect where -//! the notification title was drawn -void prv_draw_notification_cell_round(GContext *ctx, const Layer *cell_layer, GRect *box, - GFont const title_font, const char *title, - GFont const subtitle_font, const char *subtitle, - GDrawCommandImage *icon) { - - if (icon) { - GRect icon_rect = (GRect){.size = gdraw_command_image_get_bounds_size(icon)}; - - grect_align(&icon_rect, box, GAlignTop, true); - icon_rect.origin.y += 4; - - gdraw_command_image_draw(ctx, icon, icon_rect.origin); - - // more box by icon + some margin - const int16_t icon_space = icon_rect.origin.y + icon_rect.size.h - 12; - - // manually inset to save stack space, instead of using grect_inset - box->origin.y += icon_space; - box->size.h -= icon_space; - } - - // hack: compensate for text placement inside a rect - box->origin.y -= 4; - - if (subtitle) { - box->size.h -= prv_draw_centered_text_line_in(ctx, subtitle_font, box, subtitle, - GAlignBottom); - } - - if (title) { - prv_draw_centered_text_line_in(ctx, title_font, box, title, GAlignCenter); - } -} - -#if PBL_ROUND -static void prv_draw_notification_cell_round_selected(GContext *ctx, const Layer *cell_layer, - const char *title, const char *subtitle, - GDrawCommandImage *icon) { - // as measured from the design specs - const int inset = 8; - GRect frame = cell_layer->bounds; - // manually inset the frame to save stack space, instead of using grect_inset - frame.origin.x += inset; - frame.origin.y += inset; - frame.size.h -= inset * 2; - frame.size.w -= inset * 2; - const GFont title_font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); - const GFont subtitle_font = - system_theme_get_font_for_default_size(TextStyleFont_MenuCellSubtitle); - prv_draw_notification_cell_round(ctx, cell_layer, &frame, title_font, title, subtitle_font, - subtitle, icon); -} - -static void prv_draw_notification_cell_round_unselected(GContext *ctx, const Layer *cell_layer, - const char *title, const char *subtitle, - GDrawCommandImage *icon) { - // as measured from the design specs - const int horizontal_inset = MENU_CELL_ROUND_UNFOCUSED_HORIZONTAL_INSET; - const int top_inset = 2; - GRect frame = cell_layer->bounds; - // manually inset the frame to save stack space, instead of using grect_inset - frame.origin.x += horizontal_inset; - frame.size.w -= horizontal_inset * 2; - frame.origin.y += top_inset; - frame.size.h -= top_inset; - // Using TextStyleFont_Header here is a little bit of a hack to achieve Gothic 18 Bold on - // Spalding's default content size (medium) while still being a little robust for any future round - // watches that have a default content size larger than medium - const GFont font = system_theme_get_font_for_default_size(TextStyleFont_Header); - prv_draw_notification_cell_round(ctx, cell_layer, &frame, font, title, NULL, NULL, NULL); -} -#endif - -static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, - void *data) { - NotificationsData *notifications_data = data; - - if ((notifications_data->notification_list) && (cell_index->row == 0)) { - // Clear All button selected - prv_settings_clear_history_window_push(notifications_data); - return; - } - - // shift index since the first one is hard coded to Clear - int16_t notif_idx = cell_index->row - 1; - - NotificationNode *node = (NotificationNode*) list_get_at( - (ListNode*) notifications_data->notification_list, notif_idx); - if (!node) { - return; - } - - bool success = prv_push_notification_window(notifications_data); - if (!success) { - // Bail if a notification came in ahead of us and created a modal window - // before we had a chance to react to the select button event. - return; - } - const bool animated = false; - notification_window_focus_notification(&node->id, animated); -} - -static uint16_t prv_get_num_rows_callback(struct MenuLayer *menu_layer, uint16_t section_index, - void *data) { - NotificationsData *notifications_data = data; - NotificationNode *node = notifications_data->notification_list; - // There's no notifications, don't draw anything - if (!node) { - return 0; - } - - // add one for the CLEAR ALL at the top - return list_count((ListNode *)notifications_data->notification_list) + 1; -} - -static int16_t prv_get_cell_height(struct MenuLayer *menu_layer, MenuIndex *cell_index, - void *data) { -#if PBL_ROUND - MenuIndex selected_index = menu_layer_get_selected_index(menu_layer); - bool is_selected = menu_index_compare(cell_index, &selected_index) == 0; - if (is_selected) { - return MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT; - } -#endif - const PreferredContentSize runtime_platform_content_size = - system_theme_get_default_content_size_for_runtime_platform(); - return ((int16_t[NumPreferredContentSizes]) { - //! @note this is the same as Medium until Small is designed - [PreferredContentSizeSmall] = PBL_IF_RECT_ELSE(46, MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT), - [PreferredContentSizeMedium] = PBL_IF_RECT_ELSE(46, - MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT), - [PreferredContentSizeLarge] = menu_cell_basic_cell_height(), - //! @note this is the same as Large until ExtraLarge is designed - [PreferredContentSizeExtraLarge] = menu_cell_basic_cell_height(), - })[runtime_platform_content_size]; -} - -static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, - void *data) { - NotificationsData *notifications_data = data; - - void (*draw_cell)(GContext *, const Layer *, const char *, const char *, GDrawCommandImage *) = - PBL_IF_RECT_ELSE(prv_draw_notification_cell_rect, prv_draw_notification_cell_round_selected); -#if PBL_ROUND - // on round: just draw the title for anything but the focused row - if (!menu_layer_is_index_selected(&s_data->menu_layer, cell_index)) { - draw_cell = prv_draw_notification_cell_round_unselected; - } -#endif - - bool first_row = (cell_index->row == 0); - // Test if there are any notifications in the list. - if (first_row) { - // Draw "Clear all" box and exit -#if PBL_ROUND - draw_cell(ctx, cell_layer, i18n_get("Clear All", data), NULL, NULL); -#else - const GFont font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); - GRect box = cell_layer->bounds; - box.origin.y += 6; - - graphics_draw_text(ctx, i18n_get("Clear All", data), font, box, - GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); -#endif - return; - } - - // shift index since the first one is hard coded to Clear - const int16_t notif_idx = cell_index->row - 1; - - NotificationNode *node = (NotificationNode*) list_get_at( - (ListNode*) notifications_data->notification_list, notif_idx); - if (!node) { - return; - } - - LoadedNotificationNode *loaded_node = prv_loaded_notification_list_load_item( - ¬ifications_data->loaded_notification_list, node); - if (!loaded_node) { - return; - } - - TimelineItem *notification = &loaded_node->notification; - const char *title = attribute_get_string(¬ification->attr_list, AttributeIdTitle, ""); - const char *subtitle = attribute_get_string(¬ification->attr_list, AttributeIdSubtitle, ""); - const char *app_name = attribute_get_string(¬ification->attr_list, AttributeIdAppName, ""); - const char *body = attribute_get_string(¬ification->attr_list, AttributeIdBody, ""); - - // We show the app name if we don't have a custom icon, otherwise we use the title - if (!IS_EMPTY_STRING(app_name) && loaded_node->icon_is_default) { - title = app_name; - } - - if (!IS_EMPTY_STRING(title) && !IS_EMPTY_STRING(subtitle)) { - // we got a title & subtitle, we're done - } else if (IS_EMPTY_STRING(title) && IS_EMPTY_STRING(subtitle)) { - // we got neither, use the body - if (IS_EMPTY_STRING(body)) { - // we're screwed... empty message - title = "[Empty]"; - } else { - // try to show as much content as possible in title + subtitle - title = body; - subtitle = strchr(body, '\n'); // NULL handled gracefully downstream - } - } else if (IS_EMPTY_STRING(title)) { - // no title, but yes subtitle. - title = subtitle; - subtitle = body; - } else if (IS_EMPTY_STRING(subtitle)) { - // no subtitle, but yes title - subtitle = body; - } else { - WTF; - } - - draw_cell(ctx, cell_layer, title, subtitle, loaded_node->icon); -} - -// Display the appropriate layer -static void prv_update_text_layer_visibility(NotificationsData *data) { - NotificationNode *node = data->notification_list; - - // Toggle which layer is visible - if (node == NULL) { - layer_set_hidden((Layer *) &data->menu_layer, true); - layer_set_hidden((Layer *) &data->text_layer, false); - } else { - layer_set_hidden((Layer *) &data->menu_layer, false); - layer_set_hidden((Layer *) &data->text_layer, true); - } -} - -static void prv_handle_notification_removed(Uuid *id) { - prv_remove_notification(s_data, id); - app_notification_window_remove_notification_by_id(id); -} - -static void prv_handle_notification_acted_upon(Uuid *id) { - app_notification_window_handle_notification_acted_upon_by_id(id); -} - -static void prv_handle_notification_added(Uuid *id) { - TimelineItem notification; - if (!notification_storage_get(id, ¬ification)) { - return; - } - - prv_add_notification(s_data, id); - - // NOTE: To avoid having two flash reads, we only read and validate the notification once. - // We do it here, instead of in the function call below. If the above - // notification_storage validation above is removed, then we should at least validate - // it in the function call below. - app_notification_window_add_new_notification_by_id(id); -} - -static void prv_handle_notification(PebbleEvent *e, void *context) { - if (e->type == PEBBLE_SYS_NOTIFICATION_EVENT) { - Uuid *id = e->sys_notification.notification_id; - switch(e->sys_notification.type) { - case NotificationAdded: - prv_handle_notification_added(id); - break; - case NotificationRemoved: - prv_handle_notification_removed(id); - break; - case NotificationActedUpon: - prv_handle_notification_acted_upon(id); - break; - default: - break; - // Not implemented - } - menu_layer_reload_data(&s_data->menu_layer); - prv_update_text_layer_visibility(s_data); - } - // we don't handle reminders within the notifications app -} - -/////////////////// -// Window callbacks - -static void prv_window_appear(Window *window) { - NotificationsData *data = window_get_user_data(window); - - prv_update_text_layer_visibility(data); -} - -static void prv_window_disappear(Window *window) { - NotificationsData *data = window_get_user_data(window); - prv_loaded_notification_list_deinit(data->loaded_notification_list); - data->loaded_notification_list = NULL; -} - -static void prv_window_load(Window *window) { - NotificationsData *data = window_get_user_data(window); - MenuLayer *menu_layer = &data->menu_layer; - const GRect menu_layer_frame = PBL_IF_RECT_ELSE( - window->layer.bounds, grect_inset_internal(window->layer.bounds, 0, STATUS_BAR_LAYER_HEIGHT)); - menu_layer_init(menu_layer, &menu_layer_frame); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = prv_get_num_rows_callback, - .draw_row = prv_draw_row_callback, - .get_cell_height = prv_get_cell_height, - .select_click = prv_select_callback, - }); - - menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack); - menu_layer_set_highlight_colors(menu_layer, - PBL_IF_COLOR_ELSE(DEFAULT_NOTIFICATION_COLOR, GColorBlack), - GColorWhite); - - menu_layer_set_click_config_onto_window(menu_layer, window); - layer_add_child(&window->layer, menu_layer_get_layer(menu_layer)); - - TextLayer *text_layer = &data->text_layer; - const int16_t horizontal_margin = 5; - const GFont font = system_theme_get_font_for_default_size(TextStyleFont_MenuCellTitle); - // configure text layer to be vertically aligned (15 is hacking around our poor fonts) - text_layer_init_with_parameters(text_layer, - &GRect(horizontal_margin, window->layer.bounds.size.h / 2 - 15, - window->layer.bounds.size.w - horizontal_margin, - window->layer.bounds.size.h / 2), - i18n_get("No notifications", data), font, GColorBlack, - GColorWhite, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(&window->layer, text_layer_get_layer(text_layer)); - -#if PBL_ROUND - GColor bg_color = GColorClear; - GColor fg_color = GColorBlack; - - StatusBarLayer *status_bar = &data->status_bar_layer; - status_bar_layer_init(status_bar); - status_bar_layer_set_colors(status_bar, bg_color, fg_color); - layer_add_child(&window->layer, &status_bar->layer); -#endif - - menu_layer_set_selected_index(menu_layer, MenuIndex(0, 1), - PBL_IF_RECT_ELSE(MenuRowAlignNone, MenuRowAlignCenter), false); -} - -static void prv_push_window(NotificationsData *data) { - Window *window = &data->window; - window_init(window, WINDOW_NAME("Notifications")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - .appear = prv_window_appear, - .disappear = prv_window_disappear, - }); - - const bool animated = true; - app_window_stack_push(window, animated); -} - -//////////////////// -// App boilerplate - -static void prv_handle_init(void) { - NotificationsData *data = s_data = app_zalloc_check(sizeof(NotificationsData)); - - app_state_set_user_data(data); - - data->notification_event_info = (EventServiceInfo) { - .type = PEBBLE_SYS_NOTIFICATION_EVENT, - .handler = prv_handle_notification, - }; - event_service_client_subscribe(&data->notification_event_info); - prv_load_notification_storage(data); - - prv_push_window(data); -} - -static void prv_handle_deinit(void) { - NotificationsData *data = app_state_get_user_data(); -#if PBL_ROUND - status_bar_layer_deinit(&data->status_bar_layer); -#endif - menu_layer_deinit(&data->menu_layer); - event_service_client_unsubscribe(&data->notification_event_info); - prv_loaded_notification_list_deinit(data->loaded_notification_list); - prv_notification_list_deinit(data->notification_list); - - i18n_free_all(data); - app_free(data); - s_data = NULL; -} - -static void prv_s_main(void) { - prv_handle_init(); - - app_event_loop(); - - prv_handle_deinit(); -} -#else -static void prv_s_main(void) {} -#endif - - -const PebbleProcessMd* notifications_app_get_info() { - static const PebbleProcessMdSystem s_app_md = { - .common = { - .main_func = prv_s_main, - // UUID: b2cae818-10f8-46df-ad2b-98ad2254a3c1 - .uuid = {0xb2, 0xca, 0xe8, 0x18, 0x10, 0xf8, 0x46, 0xdf, - 0xad, 0x2b, 0x98, 0xad, 0x22, 0x54, 0xa3, 0xc1}, - }, - .name = i18n_noop("Notifications"), - .icon_resource_id = RESOURCE_ID_NOTIFICATIONS_APP_GLANCE, - }; - return (const PebbleProcessMd*) &s_app_md; -} diff --git a/src/fw/apps/system_apps/notifications_app.h b/src/fw/apps/system_apps/notifications_app.h deleted file mode 100644 index bedd07bd4b..0000000000 --- a/src/fw/apps/system_apps/notifications_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* notifications_app_get_info(); diff --git a/src/fw/apps/system_apps/reminders/reminder_app.c b/src/fw/apps/system_apps/reminders/reminder_app.c deleted file mode 100644 index ca82696dfa..0000000000 --- a/src/fw/apps/system_apps/reminders/reminder_app.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "reminder_app.h" -#include "reminder_app_prefs.h" - -#include "applib/app.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "applib/ui/ui.h" -#include "applib/voice/transcription_dialog.h" -#include "applib/voice/voice_window.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "resource/timeline_resource_ids.auto.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/clock.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/blob_db/watch_app_prefs_db.h" -#include "util/time/time.h" - -#include "system/logging.h" - -typedef enum ReminderAppUIState { - ReminderAppUIState_Start, - ReminderAppUIState_WaitForDictationEvent, - ReminderAppUIState_Exit, -} ReminderAppUIState; - -typedef struct ReminderAppData { - Window window; - VoiceWindow *voice_window; - EventServiceInfo event_service_info; - TranscriptionDialog transcription_dialog; - char *dialog_text; - char *reminder_str; - time_t timestamp; - ReminderAppUIState ui_state; -} ReminderAppData; - -static void prv_create_reminder(ReminderAppData *data) { - AttributeList pin_attr_list = {0}; - attribute_list_add_uint32(&pin_attr_list, AttributeIdIconTiny, - TIMELINE_RESOURCE_NOTIFICATION_REMINDER); - attribute_list_add_cstring(&pin_attr_list, AttributeIdTitle, data->reminder_str); - attribute_list_add_uint8(&pin_attr_list, AttributeIdBgColor, GColorChromeYellowARGB8); - - AttributeList completed_attr_list = {0}; - attribute_list_add_cstring(&completed_attr_list, AttributeIdTitle, - i18n_get("Completed", &pin_attr_list)); - - AttributeList postpone_attr_list = {0}; - attribute_list_add_cstring(&postpone_attr_list, AttributeIdTitle, - i18n_get("Postpone", &pin_attr_list)); - - AttributeList remove_attr_list = {0}; - attribute_list_add_cstring(&remove_attr_list, AttributeIdTitle, - i18n_get("Remove", &pin_attr_list)); - - const int num_actions = 3; - TimelineItemActionGroup action_group = { - .num_actions = num_actions, - .actions = (TimelineItemAction[]) { - { - .id = 0, - .type = TimelineItemActionTypeComplete, - .attr_list = completed_attr_list, - }, - { - .id = 1, - .type = TimelineItemActionTypePostpone, - .attr_list = postpone_attr_list, - }, - { - .id = 2, - .type = TimelineItemActionTypeRemoteRemove, - .attr_list = remove_attr_list, - } - }, - }; - - TimelineItem *item = timeline_item_create_with_attributes(data->timestamp, - 0, // duration - TimelineItemTypePin, - LayoutIdGeneric, - &pin_attr_list, - &action_group); - item->header.from_watch = true; - item->header.parent_id = (Uuid)UUID_REMINDERS_DATA_SOURCE; - timeline_add(item); - - // Tweak the item before adding the reminder - item->header.parent_id = item->header.id; - uuid_generate(&item->header.id); - item->header.type = TimelineItemTypeReminder; - item->header.layout = LayoutIdReminder; - reminders_insert(item); - - i18n_free_all(&pin_attr_list); - attribute_list_destroy_list(&pin_attr_list); - attribute_list_destroy_list(&completed_attr_list); - attribute_list_destroy_list(&postpone_attr_list); - attribute_list_destroy_list(&remove_attr_list); - timeline_item_destroy(item); -} - -static void prv_push_success_dialog(void) { - SimpleDialog *simple_dialog = simple_dialog_create("Reminder Added"); - Dialog *dialog = simple_dialog_get_dialog(simple_dialog); - dialog_set_text(dialog, i18n_get("Added", dialog)); - dialog_set_icon(dialog, RESOURCE_ID_GENERIC_REMINDER_LARGE); - dialog_set_background_color(dialog, GColorChromeYellow); - dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT); - app_simple_dialog_push(simple_dialog); - i18n_free_all(dialog); -} - -static void prv_confirm_cb(void *context) { - ReminderAppData *data = context; - data->ui_state = ReminderAppUIState_Exit; - prv_create_reminder(data); - prv_push_success_dialog(); -} - -static void prv_push_transcription_dialog(ReminderAppData *data) { - TranscriptionDialog *transcription_dialog = &data->transcription_dialog; - transcription_dialog_init(transcription_dialog); - transcription_dialog_update_text(transcription_dialog, - data->dialog_text, - strlen(data->dialog_text)); - transcription_dialog_set_callback(transcription_dialog, prv_confirm_cb, data); - Dialog *dialog = expandable_dialog_get_dialog((ExpandableDialog *)transcription_dialog); - dialog_set_destroy_on_pop(dialog, false /* free_on_pop */); - app_transcription_dialog_push(transcription_dialog); -} - -static void prv_build_transcription_dialog_text(ReminderAppData *data) { - if (data->dialog_text) { - app_free(data->dialog_text); - } - const size_t sentence_len = strlen(data->reminder_str); - const size_t date_time_len = 32; // "September 19th 9:05pm", "Yesterday 12:33pm" - const size_t required_buf_size = sentence_len + date_time_len + 2 /* \n\n */ + 1 /* \0 */; - data->dialog_text = app_zalloc_check(required_buf_size); - - // The string that is being built below looks something like: - // "Take out the trash" - // - // "Tomorrow 7:00AM" - - int buf_space_remaining = required_buf_size - 1 /*for the final \0 */; - strncpy(data->dialog_text, data->reminder_str, sentence_len); - // Having to call MAX everytime is a bit silly, but the strn function expect a size_t (unsigned). - // Calling MAX ensures that a negative value isn't passed in which gets cast to something positive - buf_space_remaining = MAX(buf_space_remaining - sentence_len, 0); - - strncat(data->dialog_text, "\n\n", buf_space_remaining); - buf_space_remaining = MAX(buf_space_remaining - 2, 0); - - - char tmp[date_time_len]; - clock_get_friendly_date(tmp, date_time_len, data->timestamp); - strncat(data->dialog_text, tmp, buf_space_remaining); - buf_space_remaining = MAX(buf_space_remaining - strlen(tmp), 0); - strncat(data->dialog_text, " ", buf_space_remaining); - buf_space_remaining = MAX(buf_space_remaining - 1, 0);; - - clock_get_time_number(tmp, date_time_len, data->timestamp); - strncat(data->dialog_text, tmp, buf_space_remaining); - buf_space_remaining = MAX(buf_space_remaining - strlen(tmp), 0); - - clock_get_time_word(tmp, date_time_len, data->timestamp); - strncat(data->dialog_text, tmp, buf_space_remaining); -} - -static void prv_handle_dictation_event(PebbleEvent *e, void *context) { - ReminderAppData *data = context; - const DictationSessionStatus status = e->dictation.result; - - if (status == DictationSessionStatusSuccess) { - if (data->reminder_str) { - app_free(data->reminder_str); - } - const size_t reminder_str_len = strlen(e->dictation.text); - data->reminder_str = app_zalloc_check(reminder_str_len + 1 /* \0 */); - strcpy(data->reminder_str, e->dictation.text); - data->reminder_str[reminder_str_len] = '\0'; - - data->timestamp = e->dictation.timestamp; - if (data->timestamp == 0) { - // If the user didn't specify a time set it to be 1 hour from the current time, - // rounded up to the nearest 15 min. - // Ex: a reminder created at 10:08 AM with no specified time is due at 11:15 AM - time_t utc_sec = rtc_get_time() + SECONDS_PER_HOUR + (15 * SECONDS_PER_MINUTE); - struct tm local_tm; - localtime_r(&utc_sec, &local_tm); - local_tm.tm_min -= (local_tm.tm_min % 15); - local_tm.tm_sec = 0; - data->timestamp = mktime(&local_tm); - } - - // If the user doesn't accept the transcription, try again. - data->ui_state = ReminderAppUIState_Start; - - prv_build_transcription_dialog_text(data); - - prv_push_transcription_dialog(data); - } else { - // Exit immediately because this event may or may not be handled before the main window appears. - data->ui_state = ReminderAppUIState_Exit; - app_window_stack_pop_all(false); - } -} - -static void prv_appear(struct Window *window) { - ReminderAppData *data = app_state_get_user_data(); - switch (data->ui_state) { - case ReminderAppUIState_Start: - // Start a transcription - data->ui_state = ReminderAppUIState_WaitForDictationEvent; - voice_window_reset(data->voice_window); - voice_window_push(data->voice_window); - break; - case ReminderAppUIState_WaitForDictationEvent: - break; - case ReminderAppUIState_Exit: - app_window_stack_pop_all(false); - break; - default: - WTF; - } -} - -static NOINLINE void prv_init(void) { - ReminderAppData *data = app_zalloc_check(sizeof(ReminderAppData)); - app_state_set_user_data(data); - - data->ui_state = ReminderAppUIState_Start; - - // This "background" window is needed because without voice confirmation enabled, - // the voice window pops before we get the event and can push the transcription dialog. - // This means we have no windows for a moment and thus the app deinits. - // This window is now also used to catch a 'back' at the confirmation dialog. - Window *window = &data->window; - window_init(window, WINDOW_NAME("Reminders")); - - WindowHandlers handlers = { .appear = prv_appear, }; - window_set_window_handlers(window, &handlers); - - data->event_service_info = (EventServiceInfo) { - .type = PEBBLE_DICTATION_EVENT, - .handler = prv_handle_dictation_event, - .context = data, - }; - event_service_client_subscribe(&data->event_service_info); - - data->voice_window = voice_window_create(NULL, 0, VoiceEndpointSessionTypeNLP); - voice_window_set_confirmation_enabled(data->voice_window, false); - - // Let the main window manage the voice window - app_window_stack_push(window, false); -} - -static void prv_deinit(void) { - ReminderAppData *data = app_state_get_user_data(); - voice_window_destroy(data->voice_window); - event_service_client_unsubscribe(&data->event_service_info); - app_free(data->dialog_text); - app_free(data); -} - -static void prv_main(void) { - prv_init(); - app_event_loop(); - prv_deinit(); -} - -const PebbleProcessMd* reminder_app_get_info(void) { - PebbleProtocolCapabilities capabilities; - bt_persistent_storage_get_cached_system_capabilities(&capabilities); - SerializedReminderAppPrefs *prefs = watch_app_prefs_get_reminder(); - - const bool is_visible_in_launcher = capabilities.reminders_app_support && - (prefs ? (prefs->appState == ReminderAppState_Enabled) : false); - - task_free(prefs); - - static const PebbleProcessMdSystem s_reminder_app_info = { - .common = { - .main_func = prv_main, - .uuid = UUID_REMINDERS_DATA_SOURCE, - }, - .name = i18n_noop("Reminder"), -#if CAPABILITY_HAS_APP_GLANCES - .icon_resource_id = RESOURCE_ID_GENERIC_REMINDER_TINY, -#endif - }; - - return is_visible_in_launcher ? (const PebbleProcessMd *)&s_reminder_app_info : NULL; -} diff --git a/src/fw/apps/system_apps/reminders/reminder_app.h b/src/fw/apps/system_apps/reminders/reminder_app.h deleted file mode 100644 index 569312afd2..0000000000 --- a/src/fw/apps/system_apps/reminders/reminder_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* reminder_app_get_info(void); diff --git a/src/fw/apps/system_apps/reminders/reminder_app_prefs.h b/src/fw/apps/system_apps/reminders/reminder_app_prefs.h deleted file mode 100644 index a48f8b2b79..0000000000 --- a/src/fw/apps/system_apps/reminders/reminder_app_prefs.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" - -#define PREF_KEY_REMINDER_APP "remindersApp" - -typedef enum ReminderAppState { - ReminderAppState_NotEnabled = 0, - ReminderAppState_NotConfigured = 1, - ReminderAppState_Enabled = 2, - ReminderAppStateCount -} ReminderAppState; - -typedef struct PACKED SerializedReminderAppPrefs { - uint8_t appState; // actually enum ReminderAppState -} SerializedReminderAppPrefs; diff --git a/src/fw/apps/system_apps/send_text/send_text.h b/src/fw/apps/system_apps/send_text/send_text.h deleted file mode 100644 index 1b368bf188..0000000000 --- a/src/fw/apps/system_apps/send_text/send_text.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* send_text_app_get_info(); diff --git a/src/fw/apps/system_apps/send_text/send_text_app_prefs.h b/src/fw/apps/system_apps/send_text/send_text_app_prefs.h deleted file mode 100644 index aa91663b58..0000000000 --- a/src/fw/apps/system_apps/send_text/send_text_app_prefs.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" -#include "util/uuid.h" - -typedef struct PACKED { - Uuid contact_uuid; - Uuid address_uuid; - bool is_fav; -} SerializedSendTextContact; - -typedef struct PACKED { - uint8_t num_contacts; - SerializedSendTextContact contacts[]; -} SerializedSendTextPrefs; diff --git a/src/fw/apps/system_apps/settings/settings.c b/src/fw/apps/system_apps/settings/settings.c deleted file mode 100644 index 84034d768f..0000000000 --- a/src/fw/apps/system_apps/settings/settings.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings.h" -#include "settings_menu.h" -#include "settings_window.h" - -#include "applib/app.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "system/passert.h" - -#define SETTINGS_CATEGORY_MENU_CELL_UNFOCUSED_ROUND_VERTICAL_PADDING 14 - -typedef struct { - Window window; - MenuLayer menu_layer; -} SettingsAppData; - -static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, void *context) { - return SettingsMenuItem_Count; -} - -static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - SettingsAppData *data = context; - - PBL_ASSERTN(cell_index->row < SettingsMenuItem_Count); - - const char *category_title = settings_menu_get_submodule_info(cell_index->row)->name; - const char *title = i18n_get(category_title, data); - menu_cell_basic_draw(ctx, cell_layer, title, NULL, NULL); -} - -static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { - settings_menu_push(cell_index->row); -} - -static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, void *context) { - PBL_ASSERTN(cell_index->row < SettingsMenuItem_Count); - -#if PBL_RECT - const int16_t category_title_height = 37; - return category_title_height; -#else - const int16_t focused_cell_height = MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT; - const int16_t unfocused_cell_height = - ((DISP_ROWS - focused_cell_height) / 2) - - SETTINGS_CATEGORY_MENU_CELL_UNFOCUSED_ROUND_VERTICAL_PADDING; - return menu_layer_is_index_selected(menu_layer, cell_index) ? focused_cell_height : - unfocused_cell_height; -#endif -} - -static int16_t prv_get_separator_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, - void *context) { - return 0; -} - -static void prv_window_load(Window *window) { - SettingsAppData *data = window_get_user_data(window); - - // Create the menu - GRect bounds = data->window.layer.bounds; -#if PBL_ROUND - bounds = grect_inset_internal(bounds, 0, - SETTINGS_CATEGORY_MENU_CELL_UNFOCUSED_ROUND_VERTICAL_PADDING); -#endif - MenuLayer *menu_layer = &data->menu_layer; - menu_layer_init(menu_layer, &bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = prv_get_num_rows_callback, - .get_cell_height = prv_get_cell_height_callback, - .draw_row = prv_draw_row_callback, - .select_click = prv_select_callback, - .get_separator_height = prv_get_separator_height_callback - }); - menu_layer_set_normal_colors(menu_layer, - PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite), - PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack)); - menu_layer_set_highlight_colors(menu_layer, - PBL_IF_COLOR_ELSE(SETTINGS_MENU_HIGHLIGHT_COLOR, GColorBlack), - GColorWhite); - menu_layer_set_click_config_onto_window(menu_layer, &data->window); - - layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); -} - -static void prv_window_unload(Window *window) { - SettingsAppData *data = window_get_user_data(window); - menu_layer_deinit(&data->menu_layer); - app_free(data); -} - -static void handle_init(void) { - SettingsAppData *data = app_zalloc_check(sizeof(SettingsAppData)); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("Settings")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers){ - .load = prv_window_load, - .unload = prv_window_unload, - }); - window_set_background_color(window, GColorBlack); - app_window_stack_push(window, true); -} - -static void handle_deinit(void) { - // Window unload deinits everything -} - -static void s_main(void) { - handle_init(); - app_event_loop(); - handle_deinit(); -} - -const PebbleProcessMd *settings_get_app_info() { - static const PebbleProcessMdSystem s_settings_app = { - .common = { - .main_func = s_main, - // UUID: 07e0d9cb-8957-4bf7-9d42-35bf47caadfe - .uuid = {0x07, 0xe0, 0xd9, 0xcb, 0x89, 0x57, 0x4b, 0xf7, - 0x9d, 0x42, 0x35, 0xbf, 0x47, 0xca, 0xad, 0xfe}, - }, - .name = i18n_noop("Settings"), -#if CAPABILITY_HAS_APP_GLANCES - .icon_resource_id = RESOURCE_ID_SETTINGS_TINY, -#elif PLATFORM_TINTIN - .icon_resource_id = RESOURCE_ID_MENU_LAYER_SETTINGS_APP_ICON, -#endif - }; - return (const PebbleProcessMd*) &s_settings_app; -} diff --git a/src/fw/apps/system_apps/settings/settings.h b/src/fw/apps/system_apps/settings/settings.h deleted file mode 100644 index da1b9d4241..0000000000 --- a/src/fw/apps/system_apps/settings/settings.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* settings_get_app_info(); diff --git a/src/fw/apps/system_apps/settings/settings_activity_tracker.c b/src/fw/apps/system_apps/settings/settings_activity_tracker.c deleted file mode 100644 index 749d45f689..0000000000 --- a/src/fw/apps/system_apps/settings/settings_activity_tracker.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_activity_tracker.h" -#include "settings_menu.h" -#include "settings_window.h" - -#include "applib/app.h" -#include "applib/app_timer.h" -#include "applib/ui/kino/kino_reel.h" -#include "applib/ui/option_menu_window.h" -#include "applib/ui/ui.h" -#include "applib/ui/window.h" -#include "applib/ui/window_stack.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "popups/switch_worker_ui.h" -#include "process_management/app_menu_data_source.h" -#include "process_management/worker_manager.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "shell/normal/watchface.h" -#include "system/passert.h" - -#include - -typedef struct SettingsActivityTrackerData { - OptionMenu option_menu; - MenuLayer menu_layer; - AppMenuDataSource *data_source; - TextLayer *text_layer; - EventServiceInfo worker_launch_info; -} SettingsActivityTrackerData; - -//////////////////// -// AppMenuDataSource callbacks - -static bool prv_app_filter_callback(struct AppMenuDataSource *const source, - AppInstallEntry *entry) { - if (!app_install_entry_is_hidden(entry) && - app_install_entry_has_worker(entry)) { - return true; - } - return false; -} - -static int16_t prv_get_chosen_row_index_for_id(SettingsActivityTrackerData *data, - AppInstallId worker_id) { - if (worker_id == INSTALL_ID_INVALID) { - return 0; - } - - const uint16_t current_worker_app_index = - app_menu_data_source_get_index_of_app_with_install_id(data->data_source, worker_id); - - if (current_worker_app_index == MENU_INDEX_NOT_FOUND) { - return 0; - } else { - return current_worker_app_index + 1; - } -} - -// Gets the current chosen row index; i.e., the row which was most recently chosen by the user. -static int16_t prv_get_chosen_row_index(SettingsActivityTrackerData *data) { - const AppInstallId worker_id = worker_manager_get_current_worker_id(); - return prv_get_chosen_row_index_for_id(data, worker_id); -} - -static int prv_num_rows(SettingsActivityTrackerData *data) { - if (data->data_source) { - return app_menu_data_source_get_count(data->data_source); - } else { - return 0; - } -} - -static void prv_reload_menu_data(void *context) { - SettingsActivityTrackerData *data = context; - const uint16_t count = prv_num_rows(data); - const bool use_icons = (count != 0); - option_menu_set_icons_enabled(&data->option_menu, use_icons /* icons_enabled */); - - option_menu_set_choice(&data->option_menu, prv_get_chosen_row_index(data)); - option_menu_reload_data(&data->option_menu); -} - -// Settings Menu callbacks -/////////////////////////// - -static void prv_select_cb(OptionMenu *option_menu, int row, void *context) { - SettingsActivityTrackerData *data = context; - if (app_menu_data_source_get_count(data->data_source) == 0) { - return; - } - if (row == 0) { - // Killing current worker - process_manager_put_kill_process_event(PebbleTask_Worker, true /* graceful */); - worker_manager_set_default_install_id(INSTALL_ID_INVALID); - } else { - const uint16_t app_index = row - 1; // offset because of the "None" selection - const AppMenuNode *app_node = - app_menu_data_source_get_node_at_index(data->data_source, app_index); - if (worker_manager_get_task_context()->install_id == INSTALL_ID_INVALID) { - // No worker currently running, launch this one and make it the default - worker_manager_put_launch_worker_event(app_node->install_id); - worker_manager_set_default_install_id(app_node->install_id); - } else if (worker_manager_get_task_context()->install_id != app_node->install_id) { - // Undo the choice change that the OptionMenu does before we call select. We may decline - // the change and therefore we don't want it to visually update yet. prv_worker_launch_handler - // will update the choice if it fires. - option_menu_set_choice(&data->option_menu, prv_get_chosen_row_index(data)); - - // Switching to a different worker, display confirmation dialog - switch_worker_confirm(app_node->install_id, true /* set as default */, - app_state_get_window_stack()); - } else { - // User selected the option they already had, do nothing - } - } -} - -static void prv_draw_no_activities_cell_rect(GContext *ctx, const Layer *cell_layer, - const char *no_activities_string) { - const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - GRect box = cell_layer->bounds; - - const GTextOverflowMode overflow = GTextOverflowModeTrailingEllipsis; - const GTextAlignment alignment = GTextAlignmentCenter; - - const GSize text_size = graphics_text_layout_get_max_used_size(ctx, no_activities_string, font, - box, overflow, alignment, NULL); - - // We want to position the text in the center of the cell veritically, - // we divide the height of the cell by two and subtract half of the text size. - // However, that just puts the TOP of a line vertically aligned. - // So we also have to subtract half of a single line's width. - box.origin.y = (box.size.h - text_size.h - fonts_get_font_height(font)/2) / 2; - - graphics_draw_text(ctx, no_activities_string, font, box, overflow, alignment, NULL); -} - -#if PBL_ROUND -static void prv_draw_no_activities_cell_round(GContext *ctx, const Layer *cell_layer, - const char *no_activities_string) { - menu_cell_basic_draw(ctx, cell_layer, no_activities_string, NULL, NULL); -} -#endif - -static uint16_t prv_get_num_rows_cb(OptionMenu *option_menu, void *context) { - SettingsActivityTrackerData *data = context; - const uint16_t count = prv_num_rows(data); - return count + 1; -} - -static void prv_draw_row_cb(OptionMenu *option_menu, GContext *ctx, const Layer *cell_layer, - const GRect *text_frame, uint32_t row, bool selected, void *context) { - SettingsActivityTrackerData *data = context; - - if (prv_num_rows(data) == 0) { - // Draw "No background apps" box and exit - const char *no_background_apps_string = i18n_get("No background apps", data); - PBL_IF_RECT_ELSE(prv_draw_no_activities_cell_rect, - prv_draw_no_activities_cell_round) - (ctx, cell_layer, no_background_apps_string); - return; - } - - const char *title = NULL; - if (row == 0) { - title = i18n_get("None", data); - } else { - AppMenuNode *node = app_menu_data_source_get_node_at_index(data->data_source, row - 1); - title = node->name; - } - - option_menu_system_draw_row(option_menu, ctx, cell_layer, text_frame, title, false, NULL); -} - -static uint16_t prv_row_height_cb(OptionMenu *option_menu, uint16_t row, bool is_selected, - void *context) { - const int16_t cell_height = - option_menu_default_cell_height(option_menu->content_type, is_selected); -#if PBL_RECT - if (prv_num_rows(context) == 0) { - // When we have no background apps, we want a double height row to display the - // 'No background apps' line, so that translations can fit and we stop wasting so much screen - // space. - return 2 * cell_height; - } -#endif - return cell_height; -} - -static void prv_worker_launch_handler(PebbleEvent *event, void *context) { - // Our worker changed while we were visible, update the selected choice - SettingsActivityTrackerData *data = context; - - const AppInstallId worker_id = event->launch_app.id; - const int16_t chosen_row = prv_get_chosen_row_index_for_id(data, worker_id); - - option_menu_set_choice(&data->option_menu, chosen_row); -} - -static void prv_unload_cb(OptionMenu *option_menu, void *context) { - SettingsActivityTrackerData *data = context; - - event_service_client_unsubscribe(&data->worker_launch_info); - - app_menu_data_source_deinit(data->data_source); - - app_free(data->data_source); - data->data_source = NULL; - - option_menu_deinit(&data->option_menu); - - i18n_free_all(data); - app_free(data); -} - -static Window *prv_init(void) { - SettingsActivityTrackerData *data = app_zalloc_check(sizeof(SettingsActivityTrackerData)); - - const OptionMenuCallbacks option_menu_callbacks = { - .unload = prv_unload_cb, - .draw_row = prv_draw_row_cb, - .select = prv_select_cb, - .get_num_rows = prv_get_num_rows_cb, - .get_cell_height = prv_row_height_cb, - }; - - data->data_source = app_zalloc_check(sizeof(AppMenuDataSource)); - app_menu_data_source_init(data->data_source, &(AppMenuDataSourceCallbacks) { - .changed = prv_reload_menu_data, - .filter = prv_app_filter_callback, - }, data); - - option_menu_init(&data->option_menu); - // Not using option_menu_configure because prv_reload_menu_data already sets - // icons_enabled and chosen row index - option_menu_set_status_colors(&data->option_menu, GColorWhite, GColorBlack); - option_menu_set_highlight_colors(&data->option_menu, SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite); - option_menu_set_title(&data->option_menu, i18n_get("Background App", data)); - option_menu_set_content_type(&data->option_menu, OptionMenuContentType_SingleLine); - option_menu_set_callbacks(&data->option_menu, &option_menu_callbacks, data); - prv_reload_menu_data(data); - - data->worker_launch_info = (EventServiceInfo) { - .type = PEBBLE_WORKER_LAUNCH_EVENT, - .handler = prv_worker_launch_handler, - .context = data - }; - event_service_client_subscribe(&data->worker_launch_info); - - return &data->option_menu.window; -} - -const SettingsModuleMetadata *settings_activity_tracker_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Background App"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_activity_tracker.h b/src/fw/apps/system_apps/settings/settings_activity_tracker.h deleted file mode 100644 index e27907dcb9..0000000000 --- a/src/fw/apps/system_apps/settings/settings_activity_tracker.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_activity_tracker_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_bluetooth.c b/src/fw/apps/system_apps/settings/settings_bluetooth.c deleted file mode 100644 index e5e937a255..0000000000 --- a/src/fw/apps/system_apps/settings/settings_bluetooth.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE - -#include "settings_bluetooth.h" -#include "settings_menu.h" -#include "settings_remote.h" -#include "settings_window.h" - -#include "applib/app.h" -#include "applib/app_focus_service.h" -#include "applib/event_service_client.h" -#include "applib/fonts/fonts.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/ui.h" -#include "comm/bt_lock.h" -#include "comm/ble/gap_le_connection.h" -#include "comm/ble/gap_le_device_name.h" -#include "drivers/rtc.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/system_icons.h" -#include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/local_id.h" -#include "services/common/bluetooth/pairability.h" -#include "services/common/i18n/i18n.h" -#include "services/common/system_task.h" -#include "services/normal/bluetooth/ble_hrm.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/string.h" - -#include -#include -#include -#include -#include - -#include -#include - -#define HEADER_BUFFER_SIZE 22 - -#define SHARING_HEART_RATE_EXTRA_HEIGHT_PX (18) - -typedef enum SettingsBluetooth { - SettingsBluetoothAirplaneMode, - SettingsBluetoothTotal, -} SettingsBluetooth; - -enum { - BluetoothIconIdx, - BluetoothAltIconIdx, - AirplaneIconIdx, - NumIcons, -}; - -static const uint32_t ICON_RESOURCE_ID[NumIcons] = { - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT, - RESOURCE_ID_SETTINGS_ICON_AIRPLANE, -}; - -typedef enum { - ToggleStateIdle, - ToggleStateEnablingBluetooth, - ToggleStateDisablingBluetooth, -} ToggleState; - -typedef struct SettingsBluetoothData { - SettingsCallbacks callbacks; - - GBitmap icon_heap_bitmap[NumIcons]; - - ListNode* remote_list_head; - - char header_buffer[HEADER_BUFFER_SIZE]; - ToggleState toggle_state; - - EventServiceInfo bt_airplane_event_info; - EventServiceInfo bt_connection_event_info; - EventServiceInfo bt_pairing_event_info; - EventServiceInfo ble_device_name_updated_event_info; -#if CAPABILITY_HAS_BUILTIN_HRM - EventServiceInfo ble_hrm_sharing_event_info; -#endif -} SettingsBluetoothData; - -// BT stack interaction stuff -/////////////////////////// - -static void settings_bluetooth_reconnect_once(void) { - // After the user toggles BT back on, immediately attempt to reconnect once: - if (bt_ctl_is_airplane_mode_on() == false) { - bt_driver_reconnect_try_now(true /*ignore_paused*/); - } -} - -static void settings_bluetooth_toggle_airplane_mode(SettingsBluetoothData* data) { - const bool airplane_mode = bt_ctl_is_airplane_mode_on(); - bt_ctl_set_airplane_mode_async(!airplane_mode); - data->toggle_state = - airplane_mode ? ToggleStateEnablingBluetooth : ToggleStateDisablingBluetooth; - settings_menu_mark_dirty(SettingsMenuItemBluetooth); -} - -bool is_remote_connected(StoredRemote* remote) { - switch (remote->type) { - case StoredRemoteTypeBTClassic: - return remote->classic.connected; - case StoredRemoteTypeBLE: - return (remote->ble.connection != NULL); - case StoredRemoteTypeBTDual: - return remote->dual.classic.connected || (remote->dual.ble.connection != NULL); - default: - WTF; - } - return false; -} - -static int remote_comparator(StoredRemote* remote, StoredRemote* other) { - if (is_remote_connected(remote) != is_remote_connected(other)) { - return is_remote_connected(remote) ? -1 : 1; - } else { - return strncmp(remote->name, other->name, sizeof(remote->name)); - } -} - -static void add_remote(SettingsBluetoothData* data, StoredRemote* remote) { - const bool ascending = false; - data->remote_list_head = list_sorted_add(data->remote_list_head, &remote->list_node, - (Comparator) remote_comparator, ascending); -} - -static StoredRemote* stored_remote_create(void) { - StoredRemote* remote = task_malloc_check(sizeof(*remote)); - *remote = (StoredRemote){}; - return remote; -} - -static void prv_copy_device_name_with_fallback(StoredRemote *remote, const char *name) { - if (!name || strlen(name) == 0) { - i18n_get_with_buffer("", remote->name, sizeof(remote->name)); - } else { - strncpy(remote->name, name, sizeof(remote->name)); - } -} - -static void prv_add_bt_classic_remote(BTDeviceAddress *addr, SM128BitKey *link_key, - const char *name, uint8_t *platform_bits, void *context) { - SettingsBluetoothData *data = (SettingsBluetoothData*) context; - if (!data) { - return; - } - - // Determine the address of our active remote, if we have one. - BTDeviceAddress active_addr = {}; - const bool is_connected = bt_driver_classic_copy_connected_address(&active_addr); - - // Create the new remote - StoredRemote *remote = stored_remote_create(); - remote->classic.bd_addr = *addr; - prv_copy_device_name_with_fallback(remote, name); - - if (is_connected && (0 == memcmp(addr, &active_addr, sizeof(*addr)))) { - remote->classic.connected = true; - } else { - remote->classic.connected = false; - } - - add_remote(data, remote); -} - - -static void prv_add_bt_classic_remotes(SettingsBluetoothData *data) { - bt_persistent_storage_for_each_bt_classic_pairing(prv_add_bt_classic_remote, data); -} - -static bool dual_remote_filter(ListNode *node, void *data) { - StoredRemote *classic_remote = (StoredRemote *) node; - BTDeviceInternal *device = (BTDeviceInternal *) data; - BTDeviceInternal le_device_with_classic_address = (const BTDeviceInternal) { - .address = classic_remote->classic.bd_addr, - .is_random_address = false, - }; - return bt_device_equal(&le_device_with_classic_address.opaque, &device->opaque); -} - -static void prv_add_and_merge_ble_remote(BTDeviceInternal *device, SMIdentityResolvingKey *irk, - const char *name, BTBondingID *id, void *context) { - SettingsBluetoothData *data = (SettingsBluetoothData*) context; - if (!data) { - return; - } - - StoredRemote* remote = (StoredRemote*) list_find_next(data->remote_list_head, - dual_remote_filter, true, device); - if (remote) { - // The remote is also a ble device, promote to a dual remote - const bool classic_connected = remote->classic.connected; - remote->type = StoredRemoteTypeBTDual; - remote->dual.classic.connected = classic_connected; - // Note: We update remote->dual.ble.connected outside this cb - remote->dual.ble.bonding = *id; - } else { - // Remote for which we only have a BLE key, add it in the menu as well, so it is accessible - // and can be removed by the user: - StoredRemote* remote = stored_remote_create(); - remote->type = StoredRemoteTypeBLE; - // Note: We update remote->ble.connection outside this cb - remote->ble.bonding = *id; - prv_copy_device_name_with_fallback(remote, name); - add_remote(data, remote); - } -} - -//! This must be called after updating classic remotes for remote consolidation -static void prv_add_and_merge_ble_remotes(SettingsBluetoothData *data) { - bt_persistent_storage_for_each_ble_pairing(prv_add_and_merge_ble_remote, data); - - StoredRemote *remote = (StoredRemote *)data->remote_list_head; - while (remote) { - StoredRemoteBLE *ble_rem = NULL; - if (remote->type == StoredRemoteTypeBLE) { - ble_rem = &remote->ble; - } else if (remote->type == StoredRemoteTypeBTDual) { - ble_rem = &remote->dual.ble; - } - - if (ble_rem) { - SMIdentityResolvingKey irk; - BTDeviceInternal device; - - if (bt_persistent_storage_get_ble_pairing_by_id(ble_rem->bonding, &irk, &device, NULL)) { - bt_lock(); - GAPLEConnection *connection = gap_le_connection_find_by_irk(&irk); - if (!connection) { - connection = gap_le_connection_by_device(&device); - } - ble_rem->connection = connection; -#if CAPABILITY_HAS_BUILTIN_HRM - ble_rem->is_sharing_heart_rate = ble_hrm_is_sharing_to_connection(connection); -#endif - bt_unlock(); - } - } - remote = (StoredRemote *)remote->list_node.next; - } -} - -static void prv_clear_remote_list(SettingsBluetoothData* data) { - while (data->remote_list_head) { - StoredRemote* remote = (StoredRemote*) data->remote_list_head; - data->remote_list_head = list_pop_head(&remote->list_node); - task_free(remote); - } -} - -static void prv_reload_remote_list(SettingsBluetoothData* data) { - prv_clear_remote_list(data); - prv_add_bt_classic_remotes(data); - prv_add_and_merge_ble_remotes(data); -} - -static void settings_bluetooth_update_remotes_private(SettingsBluetoothData* data) { - prv_reload_remote_list(data); - - if (!data->remote_list_head) { - strncpy(data->header_buffer, i18n_get("Pairing Instructions", data), HEADER_BUFFER_SIZE); - } else { - const unsigned int num_remotes = list_count(data->remote_list_head); - sniprintf(data->header_buffer, HEADER_BUFFER_SIZE, - (num_remotes != 1) ? i18n_get("%u Paired Phones", data) : - i18n_get("%u Paired Phone", data), - num_remotes); - } -} - -void settings_bluetooth_update_remotes(SettingsBluetoothData *data) { - settings_bluetooth_update_remotes_private(data); - settings_menu_reload_data(SettingsMenuItemBluetooth); -} - -////////// - -static void prv_settings_bluetooth_event_handler(PebbleEvent *event, void *context) { - SettingsBluetoothData* settings_data = (SettingsBluetoothData *) context; - PBL_LOG_COLOR(LOG_LEVEL_DEBUG, LOG_COLOR_BLUE, "BT EVENT"); - switch (event->type) { - case PEBBLE_BT_CONNECTION_EVENT: - // If BT Settings is open, update BLE device name upon connecting device: - if (event->bluetooth.connection.is_ble && - event->bluetooth.connection.state == PebbleBluetoothConnectionEventStateConnected) { - // https://pebbletechnology.atlassian.net/browse/PBL-22176 - // iOS seems to respond with 0x0E (Unlikely Error) when performing this request while - // the encryption set up is going on. For non-bonded devices it will work fine though. - gap_le_device_name_request(&event->bluetooth.connection.device); - } - // fall-through! - case PEBBLE_BT_PAIRING_EVENT: -#if CAPABILITY_HAS_BUILTIN_HRM - case PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT: -#endif - case PEBBLE_BLE_DEVICE_NAME_UPDATED_EVENT: { - settings_bluetooth_update_remotes_private(settings_data); - settings_menu_mark_dirty(SettingsMenuItemBluetooth); - break; - } - - case PEBBLE_BT_STATE_EVENT: { - settings_bluetooth_reconnect_once(); - settings_data->toggle_state = ToggleStateIdle; - settings_menu_mark_dirty(SettingsMenuItemBluetooth); - break; - } - - default: - break; - } -} - -// UI Stuff -///////////////////////////// -// Menu Layer Callbacks -///////////////////////////// -//-- Address -// ... -//| Airplane Mode: Off -//-- Paired Devices -//| Device Name -// Connected -//| Device Name -// - -static void prv_draw_stored_remote_item_rect(GContext *ctx, const Layer *cell_layer, - const char *remote_name, const char *connected_string, - const char *le_string, - const char *is_sharing_heart_rate_string) { - const GFont font = ((le_string || is_sharing_heart_rate_string) ? - fonts_get_system_font(FONT_KEY_GOTHIC_18) : NULL); - - if (le_string) { - GRect box = cell_layer->bounds; - box.size.w -= 5; - box.origin.y += 20; - box.size.h = 24; - - graphics_draw_text(ctx, le_string, font, box, GTextOverflowModeFill, GTextAlignmentRight, NULL); - } - - if (is_sharing_heart_rate_string) { - const int horizontal_margin = menu_cell_basic_horizontal_inset(); - GRect box = grect_inset(cell_layer->bounds, GEdgeInsets(0, horizontal_margin)); - box.origin.y += 38; - box.size.h = 24; - - graphics_draw_text(ctx, is_sharing_heart_rate_string, font, box, - GTextOverflowModeFill, GTextAlignmentLeft, NULL); - - // Gross hack to avoid centering the title / subtitle labels in the entire cell: - ((Layer *)cell_layer)->bounds.size.h -= SHARING_HEART_RATE_EXTRA_HEIGHT_PX; - } - - menu_cell_basic_draw(ctx, cell_layer, remote_name, connected_string, NULL); - - if (is_sharing_heart_rate_string) { - // Restore original height: - ((Layer *)cell_layer)->bounds.size.h += SHARING_HEART_RATE_EXTRA_HEIGHT_PX; - } -} - -bool settings_bluetooth_is_sharing_heart_rate_for_stored_remote(StoredRemote* remote) { -#if CAPABILITY_HAS_BUILTIN_HRM - switch (remote->type) { - case StoredRemoteTypeBLE: return remote->ble.is_sharing_heart_rate; - case StoredRemoteTypeBTDual: return remote->dual.ble.is_sharing_heart_rate; - default: - return false; - } -#else - return false; -#endif // CAPABILITY_HAS_BUILTIN_HRM -} - -#if PBL_ROUND -static void prv_draw_stored_remote_item_round(GContext *ctx, const Layer *cell_layer, - const char *remote_name, const char *connected_string, - const char *le_string, - const char *is_sharing_heart_rate_string) { -# if CAPABILITY_HAS_BUILTIN_HRM - _Static_assert(false, "FIXME: Implement round drawing code to show heart rate sharing status!"); -# endif // CAPABILITY_HAS_BUILTIN_HRM - menu_cell_basic_draw(ctx, cell_layer, remote_name, connected_string, NULL); -} -#endif // PBL_ROUND - -static void draw_stored_remote_item(GContext *ctx, const Layer *cell_layer, - uint16_t device_index, SettingsBluetoothData *data) { - const uint32_t num_remotes = list_count(data->remote_list_head); - PBL_ASSERT(device_index < num_remotes, "Got index %" PRId16 " only have %" PRId32, - device_index, num_remotes); - StoredRemote* remote = (StoredRemote*) list_get_at(data->remote_list_head, device_index); - bool connected = is_remote_connected(remote); - - const char *le_string = NULL; - if (remote->type == StoredRemoteTypeBTDual - && remote->dual.classic.connected != (remote->dual.ble.connection != NULL)) { - le_string = remote->dual.classic.connected - ? i18n_get("No LE", data) : i18n_get("LE Only", data); - } - - const char *connected_string = connected ? i18n_get("Connected", data) : - PBL_IF_RECT_ELSE("", NULL); - - // Add ellipsis if the name might have been cut off by the mobile - const char ellipsis[] = UTF8_ELLIPSIS_STRING; - const size_t max_name_size = BT_DEVICE_NAME_BUFFER_SIZE - 2; - const size_t name_size = strnlen(remote->name, BT_DEVICE_NAME_BUFFER_SIZE); - char *remote_name = task_zalloc_check(max_name_size + sizeof(ellipsis)); - strncpy(remote_name, remote->name, name_size); - if (name_size > max_name_size) { - const size_t ellipsis_start_offset = utf8_get_size_truncate(remote_name, name_size); - strncpy(&remote_name[ellipsis_start_offset], ellipsis, sizeof(ellipsis)); - } - - const char *is_sharing_heart_rate = - (settings_bluetooth_is_sharing_heart_rate_for_stored_remote(remote) ? - i18n_get("Sharing Heart Rate ❤", data) : NULL); - - PBL_IF_RECT_ELSE(prv_draw_stored_remote_item_rect, - prv_draw_stored_remote_item_round)(ctx, cell_layer, remote_name, - connected_string, le_string, - is_sharing_heart_rate); - - task_free(remote_name); -} - - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - return list_count(data->remote_list_head) + 1; -} - -static int16_t prv_row_height_cb(SettingsCallbacks *context, uint16_t row, bool is_selected) { -#if PBL_RECT -# if CAPABILITY_HAS_BUILTIN_HRM - int heart_rate_sharing_text_height = 0; - if (row > 0) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - const uint16_t device_index = row - 1; - StoredRemote* remote = (StoredRemote*) list_get_at(data->remote_list_head, device_index); - if (settings_bluetooth_is_sharing_heart_rate_for_stored_remote(remote)) { - heart_rate_sharing_text_height = SHARING_HEART_RATE_EXTRA_HEIGHT_PX; - } - } -# else - const int heart_rate_sharing_text_height = 0; -# endif // CAPABILITY_HAS_BUILTIN_HRM - return menu_cell_basic_cell_height() + heart_rate_sharing_text_height; -#elif PBL_ROUND - return (is_selected ? MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT : - MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT); -#else -#endif -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - if (row == 0) { - char device_name_buffer[BT_DEVICE_NAME_BUFFER_SIZE]; - const char *subtitle = NULL; - const char *title = i18n_get("Connection", data); - GBitmap *icon = NULL; - if (data->toggle_state == ToggleStateIdle) { - if (bt_ctl_is_airplane_mode_on()) { - subtitle = i18n_get("Airplane Mode", data); - icon = &data->icon_heap_bitmap[AirplaneIconIdx]; - } else { - if (selected) { - bt_local_id_copy_device_name(device_name_buffer, false); - subtitle = device_name_buffer; - } else { - subtitle = i18n_get("Now Discoverable", data); - } - icon = &data->icon_heap_bitmap[BluetoothIconIdx]; - } - } else { - subtitle = (data->toggle_state == ToggleStateDisablingBluetooth) - ? i18n_get("Disabling...", data) : i18n_get("Enabling...", data); - icon = &data->icon_heap_bitmap[BluetoothAltIconIdx]; - } - - menu_cell_basic_draw(ctx, cell_layer, title, subtitle, icon); - - // TODO PBL-23111: Decide how we should show these strings on round displays -#if PBL_RECT - // Hack: the pairing instruction is drawn in the cell callback, but outside of the cell... - if (!data->remote_list_head) { - const GDrawState draw_state = ctx->draw_state; - // Enable drawing outside of the cell: - ctx->draw_state.clip_box = ctx->dest_bitmap.bounds; - - graphics_context_set_text_color(ctx, GColorBlack); - GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18); - GRect box = cell_layer->bounds; - box.origin.x = 15; - box.origin.y = menu_cell_basic_cell_height() + (int16_t)9; - box.size.w -= 30; - box.size.h = 83; - - if (bt_ctl_is_airplane_mode_on()) { - graphics_draw_text(ctx, i18n_get("Disable Airplane Mode to connect.", data), font, - box, GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); - } else { - graphics_draw_text(ctx, i18n_get("Open the Pebble app on your phone to connect.", data), - font, box, GTextOverflowModeTrailingEllipsis, - GTextAlignmentCenter, NULL); - } - - ctx->draw_state = draw_state; - } -#endif - } else { - const uint16_t device_index = row - 1; - draw_stored_remote_item(ctx, cell_layer, device_index, data); - } -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - if (row == 0) { - settings_bluetooth_toggle_airplane_mode(data); - return; - } - if (!data->remote_list_head) { - return; - } - prv_reload_remote_list(data); - StoredRemote* remote = (StoredRemote*) list_get_at(data->remote_list_head, row - 1); - settings_remote_menu_push(data, remote); -} - -static void prv_focus_handler(bool in_focus) { - if (!in_focus) { - return; - } - settings_menu_reload_data(SettingsMenuItemBluetooth); -} - -static void prv_expand_cb(SettingsCallbacks *context) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - - settings_bluetooth_update_remotes_private(data); - - // When entering the BT Settings, update device names of all connected devices: - if (!bt_ctl_is_airplane_mode_on()) { - gap_le_device_name_request_all(); - } - - data->bt_airplane_event_info = (EventServiceInfo) { - .type = PEBBLE_BT_STATE_EVENT, - .handler = prv_settings_bluetooth_event_handler, - .context = data, - }; - data->bt_connection_event_info = (EventServiceInfo) { - .type = PEBBLE_BT_CONNECTION_EVENT, - .handler = prv_settings_bluetooth_event_handler, - .context = data, - }; - data->bt_pairing_event_info = (EventServiceInfo) { - .type = PEBBLE_BT_PAIRING_EVENT, - .handler = prv_settings_bluetooth_event_handler, - .context = data, - }; - data->ble_device_name_updated_event_info = (EventServiceInfo) { - .type = PEBBLE_BLE_DEVICE_NAME_UPDATED_EVENT, - .handler = prv_settings_bluetooth_event_handler, - .context = data, - }; -#if CAPABILITY_HAS_BUILTIN_HRM - data->ble_hrm_sharing_event_info = (EventServiceInfo) { - .type = PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT, - .handler = prv_settings_bluetooth_event_handler, - .context = data, - }; - event_service_client_subscribe(&data->ble_hrm_sharing_event_info); -#endif - event_service_client_subscribe(&data->bt_airplane_event_info); - event_service_client_subscribe(&data->bt_connection_event_info); - event_service_client_subscribe(&data->bt_pairing_event_info); - event_service_client_subscribe(&data->ble_device_name_updated_event_info); - bt_pairability_use(); - bt_driver_reconnect_pause(); - // Reload & redraw after pairing popup - app_focus_service_subscribe_handlers((AppFocusHandlers) { .did_focus = prv_focus_handler }); -} - -// Turns off services that are part of the bluetooth settings menu such as enabling -// discovery. We don't want to keep these services running longer than necessary because -// they consume a fair amount of power -static void prv_hide_cb(SettingsCallbacks *context) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - bt_pairability_release(); - bt_driver_reconnect_resume(); - bt_driver_reconnect_reset_interval(); - bt_driver_reconnect_try_now(false /*ignore_paused*/); - -#if CAPABILITY_HAS_BUILTIN_HRM - event_service_client_unsubscribe(&data->ble_hrm_sharing_event_info); -#endif - event_service_client_unsubscribe(&data->bt_airplane_event_info); - event_service_client_unsubscribe(&data->bt_connection_event_info); - event_service_client_unsubscribe(&data->bt_pairing_event_info); - event_service_client_unsubscribe(&data->ble_device_name_updated_event_info); - app_focus_service_unsubscribe(); -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsBluetoothData *data = (SettingsBluetoothData *) context; - - i18n_free_all(data); - - prv_clear_remote_list(data); - for (unsigned int idx = 0; idx < NumIcons; ++idx) { - gbitmap_deinit(&data->icon_heap_bitmap[idx]); - } - app_free(data); -} - -static Window *prv_init(void) { - SettingsBluetoothData *data = app_malloc_check(sizeof(SettingsBluetoothData)); - *data = (SettingsBluetoothData){}; - - for (unsigned int idx = 0; idx < NumIcons; ++idx) { - gbitmap_init_with_resource(&data->icon_heap_bitmap[idx], ICON_RESOURCE_ID[idx]); - } - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - .row_height = prv_row_height_cb, - .expand = prv_expand_cb, - .hide = prv_hide_cb, - }; - - return settings_window_create(SettingsMenuItemBluetooth, &data->callbacks); -} - -const SettingsModuleMetadata *settings_bluetooth_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Bluetooth"), - .init = prv_init, - }; - - return &s_module_info; -} - -#undef HEADER_BUFFER_SIZE diff --git a/src/fw/apps/system_apps/settings/settings_bluetooth.h b/src/fw/apps/system_apps/settings/settings_bluetooth.h deleted file mode 100644 index 5418b9911e..0000000000 --- a/src/fw/apps/system_apps/settings/settings_bluetooth.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "kernel/events.h" -#include "settings_menu.h" -#include "util/list.h" - -typedef struct GAPLEConnection GAPLEConnection; - -typedef enum StoredRemoteType { - StoredRemoteTypeBTClassic, - StoredRemoteTypeBLE, - StoredRemoteTypeBTDual, -} StoredRemoteType; - -typedef struct StoredRemoteClassic { - bool connected; - BTDeviceAddress bd_addr; -} StoredRemoteClassic; - -typedef struct StoredRemoteBLE { - BTBondingID bonding; - GAPLEConnection *connection; -#if CAPABILITY_HAS_BUILTIN_HRM - bool is_sharing_heart_rate; -#endif -} StoredRemoteBLE; - -typedef struct StoredRemoteDual { - StoredRemoteClassic classic; - StoredRemoteBLE ble; -} StoredRemoteDual; - -typedef struct StoredRemote { - ListNode list_node; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - StoredRemoteType type; - union { - StoredRemoteClassic classic; - StoredRemoteBLE ble; - StoredRemoteDual dual; - }; -} StoredRemote; - -struct SettingsBluetoothData; - -void settings_bluetooth_update_remotes(struct SettingsBluetoothData *data); - -const SettingsModuleMetadata *settings_bluetooth_get_info(void); - -bool settings_bluetooth_is_sharing_heart_rate_for_stored_remote(StoredRemote* remote); - -#define BT_FORGET_PAIRING_STR \ - i18n_noop("Remember to also forget your Pebble's Bluetooth connection from your phone.") diff --git a/src/fw/apps/system_apps/settings/settings_certifications.h b/src/fw/apps/system_apps/settings/settings_certifications.h deleted file mode 100644 index e125ede74e..0000000000 --- a/src/fw/apps/system_apps/settings/settings_certifications.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "mfg/mfg_info.h" - -#include - - -//! Which regulatory marks and/or IDs a given product should display. -typedef struct RegulatoryFlags { -//! Australia Regulatory Compliance Mark - bool has_australia_rcm:1; -//! Canada IC ID - bool has_canada_ic:1; -//! China CMIIT ID - bool has_china_cmiit:1; -//! EU CE Mark - bool has_eu_ce:1; -//! EU WEEE Mark (wastebin with X) - bool has_eu_weee:1; -//! Japan TELEC (Telecom Engineering Center) [R] mark and ID -//! (Radio equipment conformity) - bool has_japan_telec_r:1; -//! TELEC mark [T] mark and ID (Terminal equipment conformity) - bool has_japan_telec_t:1; -//! Korea -//! - KCC mark -//! - Details window with KCC mark and KCC ID - bool has_korea_kcc:1; -//! Mexico NOM NYCE mark - bool has_mexico_nom_nyce:1; -//! USA FCC Mark and FCC ID - bool has_usa_fcc:1; -} RegulatoryFlags; - -typedef struct CertificationIds { - const char *canada_ic_id; - const char *china_cmiit_id; - const char *japan_telec_r_id; - const char *japan_telec_t_id; - const char *korea_kcc_id; - const char *mexico_ifetel_id; - const char *usa_fcc_id; -} CertificationIds; - - -static const RegulatoryFlags s_regulatory_flags_fallback = { -}; - -// Certifiation ID strings used for bigboards and such. -static const CertificationIds s_certification_ids_fallback = { - .canada_ic_id = "XXXXXX-YYY", - .china_cmiit_id = "ABCDEFGHIJ", - .japan_telec_r_id = "XXX-YYYYYY", - .japan_telec_t_id = "D XX YYYY ZZZ", - .korea_kcc_id = "WWWW-XXX-YYY-ZZZ", - .mexico_ifetel_id = "RCPPEXXXX-YYYY", - .usa_fcc_id = "XXX-YYY", -}; - - -static const RegulatoryFlags s_regulatory_flags_snowy = { - .has_canada_ic = true, - .has_china_cmiit = true, - .has_eu_ce = true, - .has_eu_weee = true, - .has_japan_telec_r = true, - .has_japan_telec_t = true, - .has_korea_kcc = true, - .has_usa_fcc = true, -}; - -static const CertificationIds s_certification_ids_snowy = { - .canada_ic_id = "10805A-501", - .china_cmiit_id = "2015DJ1504", - .japan_telec_r_id = "201-150104", - .japan_telec_t_id = "D 15 0015 201", - .korea_kcc_id = "MSIP-CRM-PEB-WQ3", - .usa_fcc_id = "RGQ-501", -}; - -static const CertificationIds s_certification_ids_bobby = { - .canada_ic_id = "10805A-511", - .china_cmiit_id = "2015DJ3458", - .japan_telec_r_id = "201-150257", - .japan_telec_t_id = "D 15 0065 201", - .korea_kcc_id = "MSIP-CRM-PEB-WQ3", - .usa_fcc_id = "RGQ-511", -}; - -static const RegulatoryFlags s_regulatory_flags_spalding = { - .has_canada_ic = true, - .has_eu_ce = true, - .has_eu_weee = true, - .has_usa_fcc = true, -}; - -static const CertificationIds s_certification_ids_spalding = { - .canada_ic_id = "10805A-601", - .usa_fcc_id = "RGQ-601", -}; - -static const RegulatoryFlags s_regulatory_flags_silk = { - .has_australia_rcm = true, - .has_canada_ic = true, - .has_china_cmiit = true, - .has_eu_ce = true, - .has_eu_weee = true, - .has_japan_telec_r = true, - .has_mexico_nom_nyce = true, - .has_usa_fcc = true, -}; - -static const CertificationIds s_certification_ids_silk = { - .canada_ic_id = "10805A-1001", - .china_cmiit_id = "2016DJ4469", - .usa_fcc_id = "RGQ-1001", - .japan_telec_r_id = "201-160535", - .mexico_ifetel_id = "RCPPE1016-1161" -}; - -static const CertificationIds s_certification_ids_silk_hr = { - .canada_ic_id = "10805A-1002", - .china_cmiit_id = "2016DJ4931", - .usa_fcc_id = "RGQ-1002", - .japan_telec_r_id = "201-160558", - .mexico_ifetel_id = "RCPPE1016-1238" -}; - - -static const RegulatoryFlags * prv_get_regulatory_flags(void) { -#if PLATFORM_SNOWY - return &s_regulatory_flags_snowy; -#elif PLATFORM_SPALDING - return &s_regulatory_flags_spalding; -#elif PLATFORM_SILK - return &s_regulatory_flags_silk; -#elif PLATFORM_ASTERIX - // TODO: add applicable flags - return &s_regulatory_flags_fallback; -#else - return &s_regulatory_flags_fallback; -#endif -} - -//! Don't call this function directly. Use the prv_get_*_id functions instead. -static const CertificationIds * prv_get_certification_ids(void) { -#if defined(BOARD_SNOWY_S3) - return &s_certification_ids_bobby; -#elif defined(BOARD_SNOWY_EVT) || defined(BOARD_SNOWY_EVT2) || \ - defined(BOARD_SNOWY_DVT) - return &s_certification_ids_snowy; -#elif defined(BOARD_SPALDING) || defined(BOARD_SPALDING_EVT) - return &s_certification_ids_spalding; -#elif PLATFORM_SILK - if (mfg_info_is_hrm_present()) { - return &s_certification_ids_silk_hr; - } else { - return &s_certification_ids_silk; - } -#elif PLATFORM_ASTERIX - // TODO: add real certification ids - return &s_certification_ids_fallback; -#else - return &s_certification_ids_fallback; -#endif -} - -#define ID_GETTER(ID_KIND) \ - static const char * prv_get_##ID_KIND(void) { \ - return prv_get_certification_ids()->ID_KIND ?: \ - s_certification_ids_fallback.ID_KIND; \ - } - -ID_GETTER(canada_ic_id) -ID_GETTER(china_cmiit_id) -ID_GETTER(japan_telec_r_id) -ID_GETTER(japan_telec_t_id) -ID_GETTER(korea_kcc_id) -ID_GETTER(mexico_ifetel_id) -ID_GETTER(usa_fcc_id) - -#undef ID_GETTER diff --git a/src/fw/apps/system_apps/settings/settings_display.c b/src/fw/apps/system_apps/settings/settings_display.c deleted file mode 100644 index 8a724eef52..0000000000 --- a/src/fw/apps/system_apps/settings/settings_display.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_display.h" -#include "settings_display_calibration.h" -#include "settings_menu.h" -#include "settings_option_menu.h" -#include "settings_window.h" - -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "drivers/battery.h" -#include "kernel/pbl_malloc.h" -#include "popups/notifications/notification_window.h" -#include "process_state/app_state/app_state.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "shell/prefs.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#include -#include -#include -#include - -typedef struct SettingsDisplayData { - SettingsCallbacks callbacks; -} SettingsDisplayData; - -// Intensity Settings -///////////////////////////// - -static const uint32_t s_intensity_values[] = { 5, 25, 45, 70 }; - -static const char *s_intensity_labels[] = { - i18n_noop("Low"), - i18n_noop("Medium"), - i18n_noop("High"), - i18n_noop("Blinding") -}; - -#define BACKLIGHT_SCALE_GRANULARITY 5 -// Normalize the result from light get brightness as it sometimes -// will round down/up by a % -static uint8_t prv_get_scaled_brightness(void) { - return BACKLIGHT_SCALE_GRANULARITY - * ((backlight_get_intensity_percent() + BACKLIGHT_SCALE_GRANULARITY - 1) - / BACKLIGHT_SCALE_GRANULARITY); -} - -static int prv_intensity_get_selection_index() { - const uint8_t intensity = prv_get_scaled_brightness(); - - // FIXME: PBL-22272 ... We will return idx 0 if someone has an old value for - // one of the intensity options - for (int i = 0; i < (int)ARRAY_LENGTH(s_intensity_values); i++) { - if (s_intensity_values[i] == intensity) { - return i; - } - } - return 0; -} - -static void prv_intensity_menu_select(OptionMenu *option_menu, int selection, void *context) { - backlight_set_intensity_percent(s_intensity_values[selection]); - app_window_stack_remove(&option_menu->window, true /*animated*/); -} - -static void prv_intensity_menu_push(SettingsDisplayData *data) { - const int index = prv_intensity_get_selection_index(); - const OptionMenuCallbacks callbacks = { - .select = prv_intensity_menu_select, - }; - const char *title = PBL_IF_RECT_ELSE(i18n_noop("INTENSITY"), i18n_noop("Intensity")); - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, index, &callbacks, ARRAY_LENGTH(s_intensity_labels), - true /* icons_enabled */, s_intensity_labels, data); -} - -// Timeout Settings -///////////////////////////// - -static const uint32_t s_timeout_values[] = { 3000, 5000, 8000 }; - -static const char *s_timeout_labels[] = { - i18n_noop("3 Seconds"), - i18n_noop("5 Seconds"), - i18n_noop("8 Seconds") -}; - -static int prv_timeout_get_selection_index() { - uint32_t timeout_ms = backlight_get_timeout_ms(); - for (size_t i = 0; i < ARRAY_LENGTH(s_timeout_values); i++) { - if (s_timeout_values[i] == timeout_ms) { - return i; - } - } - return 0; -} - -static void prv_timeout_menu_select(OptionMenu *option_menu, int selection, void *context) { - backlight_set_timeout_ms(s_timeout_values[selection]); - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_timeout_menu_push(SettingsDisplayData *data) { - int index = prv_timeout_get_selection_index(); - const OptionMenuCallbacks callbacks = { - .select = prv_timeout_menu_select, - }; - const char *title = PBL_IF_RECT_ELSE(i18n_noop("TIMEOUT"), i18n_noop("Timeout")); - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, index, &callbacks, ARRAY_LENGTH(s_timeout_labels), - true /* icons_enabled */, s_timeout_labels, data); -} - -// Menu Callbacks -///////////////////////////// - -enum SettingsDisplayItem { - SettingsDisplayLanguage, - SettingsDisplayBacklightMode, - SettingsDisplayMotionSensor, - SettingsDisplayAmbientSensor, - SettingsDisplayBacklightIntensity, - SettingsDisplayBacklightTimeout, -#if PLATFORM_SPALDING - SettingsDisplayAdjustAlignment, -#endif - NumSettingsDisplayItems -}; - -// number of items under SettingsDisplayBacklightMode which are hidden when backlight is disabled -static const int NUM_BACKLIGHT_SUB_ITEMS = SettingsDisplayBacklightTimeout - - SettingsDisplayBacklightMode; - -static bool prv_should_show_backlight_sub_items() { - return backlight_is_enabled(); -} - -uint16_t prv_get_item_from_row(uint16_t row) { - if (!prv_should_show_backlight_sub_items() && (row > SettingsDisplayBacklightMode)) { - return row + NUM_BACKLIGHT_SUB_ITEMS; - } - return row; -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsDisplayData *data = (SettingsDisplayData*)context; - switch (prv_get_item_from_row(row)) { - case SettingsDisplayLanguage: - shell_prefs_toggle_language_english(); - break; - case SettingsDisplayBacklightMode: - light_toggle_enabled(); - break; - case SettingsDisplayMotionSensor: - backlight_set_motion_enabled(!backlight_is_motion_enabled()); - break; - case SettingsDisplayAmbientSensor: - light_toggle_ambient_sensor_enabled(); - break; - case SettingsDisplayBacklightIntensity: - prv_intensity_menu_push(data); - break; - case SettingsDisplayBacklightTimeout: - prv_timeout_menu_push(data); - break; -#if PLATFORM_SPALDING - case SettingsDisplayAdjustAlignment: - settings_display_calibration_push(app_state_get_window_stack()); - break; -#endif - default: - WTF; - } - settings_menu_reload_data(SettingsMenuItemDisplay); - settings_menu_mark_dirty(SettingsMenuItemDisplay); -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsDisplayData *data = (SettingsDisplayData*) context; - const char *title = NULL; - const char *subtitle = NULL; - switch (prv_get_item_from_row(row)) { - case SettingsDisplayLanguage: - title = i18n_noop("Language"); - subtitle = i18n_get_lang_name(); - break; - case SettingsDisplayBacklightMode: - title = i18n_noop("Backlight"); - if (backlight_is_enabled()) { - subtitle = i18n_noop("On"); - } else { - subtitle = i18n_noop("Off"); - } - break; - case SettingsDisplayMotionSensor: - title = i18n_noop("Motion Enabled"); - if (backlight_is_motion_enabled()) { - subtitle = i18n_noop("On"); - } else { - subtitle = i18n_noop("Off"); - } - break; - case SettingsDisplayAmbientSensor: - title = i18n_noop("Ambient Sensor"); - if (backlight_is_ambient_sensor_enabled()) { - subtitle = i18n_noop("On"); - } else { - subtitle = i18n_noop("Off"); - } - break; - case SettingsDisplayBacklightIntensity: - title = i18n_noop("Intensity"); - subtitle = s_intensity_labels[prv_intensity_get_selection_index()]; - break; - case SettingsDisplayBacklightTimeout: - title = i18n_noop("Timeout"); - subtitle = s_timeout_labels[prv_timeout_get_selection_index()]; - break; -#if PLATFORM_SPALDING - case SettingsDisplayAdjustAlignment: - title = i18n_noop("Screen Alignment"); - break; -#endif - default: - WTF; - } - menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - if (!prv_should_show_backlight_sub_items()) { - return NumSettingsDisplayItems - NUM_BACKLIGHT_SUB_ITEMS; - } - return NumSettingsDisplayItems; -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsDisplayData *data = (SettingsDisplayData*) context; - i18n_free_all(data); - app_free(data); -} - -static Window *prv_init(void) { - SettingsDisplayData *data = app_malloc_check(sizeof(*data)); - *data = (SettingsDisplayData){}; - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - }; - - return settings_window_create(SettingsMenuItemDisplay, &data->callbacks); -} - -const SettingsModuleMetadata *settings_display_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Display"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_display.h b/src/fw/apps/system_apps/settings/settings_display.h deleted file mode 100644 index cf842e091e..0000000000 --- a/src/fw/apps/system_apps/settings/settings_display.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_display_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_display_calibration.c b/src/fw/apps/system_apps/settings/settings_display_calibration.c deleted file mode 100644 index 8ac3132132..0000000000 --- a/src/fw/apps/system_apps/settings/settings_display_calibration.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if PLATFORM_SPALDING - -#include "settings_display_calibration.h" - -#include "applib/fonts/fonts.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "util/trig.h" -#include "applib/ui/window.h" -#include "applib/ui/window_stack.h" - -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "shell/prefs.h" -#include "system/passert.h" -#include "util/math.h" - -static const int16_t MAX_OFFSET_MAGNITUDE = 10; - -typedef enum { - DisplayCalibrationState_X_Adjust, - DisplayCalibrationState_Y_Adjust, - DisplayCalibrationState_Confirm -} DisplayCalibrationState; - -static const DisplayCalibrationState INITIAL_STATE = DisplayCalibrationState_X_Adjust; - -typedef struct { - Window window; - Layer layer; - - DisplayCalibrationState state; - GPoint offset; - GBitmap arrow_down; - GBitmap arrow_left; - GBitmap arrow_up; - GBitmap arrow_right; -} DisplayCalibrationData; - -static void prv_draw_text(Layer *layer, GContext *ctx) { - DisplayCalibrationData *data = window_get_user_data(layer_get_window(layer)); - graphics_context_set_text_color(ctx, GColorWhite); - - const char *titles[] = { - [DisplayCalibrationState_X_Adjust] = i18n_noop("Horizontal Alignment"), - [DisplayCalibrationState_Y_Adjust] = i18n_noop("Vertical Alignment"), - [DisplayCalibrationState_Confirm] = i18n_noop("Confirm Alignment") - }; - const char *instructions[] = { - [DisplayCalibrationState_X_Adjust] = i18n_noop("Up/Down to adjust\nSelect to proceed"), - [DisplayCalibrationState_Y_Adjust] = i18n_noop("Up/Down to adjust\nSelect to proceed"), - [DisplayCalibrationState_Confirm] = i18n_noop("Select to confirm alignment changes") - }; - - const char *title_text = i18n_get(titles[data->state], data); - const char *instruction_text = i18n_get(instructions[data->state], data); - - const GTextOverflowMode overflow_mode = GTextOverflowModeTrailingEllipsis; - const GTextAlignment text_alignment = GTextAlignmentCenter; - const GFont title_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - const GFont instruction_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - - const int16_t title_line_height = fonts_get_font_height(title_font); - const int16_t text_margin_x = 16; - const int16_t text_margin_y = 32; - const GRect max_text_container_frame = grect_inset_internal(layer->bounds, - text_margin_x, text_margin_y); - - GRect title_frame = (GRect) { - .size = GSize(max_text_container_frame.size.w, title_line_height) - }; - GRect instruction_frame = (GRect) { - .size = GSize(max_text_container_frame.size.w, - max_text_container_frame.size.h - title_line_height) - }; - instruction_frame.size = graphics_text_layout_get_max_used_size(ctx, instruction_text, - instruction_font, - instruction_frame, - overflow_mode, text_alignment, - NULL); - GRect text_container_frame = (GRect) { - .size = GSize(max_text_container_frame.size.w, title_frame.size.h + instruction_frame.size.h) - }; - - const bool clips = true; - grect_align(&text_container_frame, &max_text_container_frame, GAlignCenter, clips); - grect_align(&title_frame, &text_container_frame, GAlignTop, clips); - grect_align(&instruction_frame, &text_container_frame, GAlignBottom, clips); - - const int16_t title_vertical_adjust_px = -fonts_get_font_cap_offset(title_font); - title_frame.origin.y += title_vertical_adjust_px; - - graphics_draw_text(ctx, title_text, title_font, title_frame, - overflow_mode, text_alignment, NULL); - graphics_draw_text(ctx, instruction_text, instruction_font, - instruction_frame, overflow_mode, text_alignment, NULL); -} - -void prv_draw_border_stripe(Layer *layer, GContext *ctx, GAlign alignment) { - const int16_t stripe_inset = 6; - const int16_t stripe_width = 2; - - const GRect *layer_bounds = &layer->bounds; - const bool is_horizontal = ((alignment == GAlignTop) || (alignment == GAlignBottom)); - GRect rect = (GRect) { - .size = (is_horizontal) ? GSize(layer_bounds->size.w, stripe_width) - : GSize(stripe_width, layer_bounds->size.h) - }; - - for (int i = stripe_inset - stripe_width; i >= -MAX_OFFSET_MAGNITUDE; i -= stripe_width) { - // alternate yellow and red stripes - graphics_context_set_stroke_color(ctx, (i % (2 * stripe_width)) ? GColorRed : GColorYellow); - const GRect outer_bounds = grect_inset_internal(*layer_bounds, i, i); - grect_align(&rect, &outer_bounds, alignment, false); - graphics_draw_rect(ctx, &rect); - } -} - -static void prv_draw_border_stripes(Layer *layer, GContext *ctx) { - DisplayCalibrationData *data = window_get_user_data(layer_get_window(layer)); - - if ((data->state == DisplayCalibrationState_X_Adjust) || - (data->state == DisplayCalibrationState_Confirm)) { - prv_draw_border_stripe(layer, ctx, GAlignLeft); - prv_draw_border_stripe(layer, ctx, GAlignRight); - } - if ((data->state == DisplayCalibrationState_Y_Adjust) || - (data->state == DisplayCalibrationState_Confirm)) { - prv_draw_border_stripe(layer, ctx, GAlignTop); - prv_draw_border_stripe(layer, ctx, GAlignBottom); - } -} - -static void prv_draw_arrow(Layer *layer, GContext *ctx, GBitmap *arrow_bitmap, GAlign alignment) { - graphics_context_set_compositing_mode(ctx, GCompOpSet); - const int16_t margin = 8; - const GRect bounds = grect_inset_internal(layer->bounds, margin, margin); - GRect box = arrow_bitmap->bounds; - grect_align(&box, &bounds, alignment, true); - graphics_draw_bitmap_in_rect(ctx, arrow_bitmap, &box); -} - -static void prv_draw_arrows(Layer *layer, GContext *ctx) { - DisplayCalibrationData *data = window_get_user_data(layer_get_window(layer)); - - switch (data->state) { - case DisplayCalibrationState_X_Adjust: - if (data->offset.x > -MAX_OFFSET_MAGNITUDE) { - prv_draw_arrow(layer, ctx, &data->arrow_left, GAlignLeft); - } - if (data->offset.x < MAX_OFFSET_MAGNITUDE) { - prv_draw_arrow(layer, ctx, &data->arrow_right, GAlignRight); - } - break; - case DisplayCalibrationState_Y_Adjust: - if (data->offset.y > -MAX_OFFSET_MAGNITUDE) { - prv_draw_arrow(layer, ctx, &data->arrow_up, GAlignTop); - } - if (data->offset.y < MAX_OFFSET_MAGNITUDE) { - prv_draw_arrow(layer, ctx, &data->arrow_down, GAlignBottom); - } - break; - case DisplayCalibrationState_Confirm: - break; - } -} - -static void prv_layer_update_proc(Layer *layer, GContext *ctx) { - DisplayCalibrationData *data = window_get_user_data(layer_get_window(layer)); - - ctx->draw_state.drawing_box.origin.x += data->offset.x; - ctx->draw_state.drawing_box.origin.y += data->offset.y; - - prv_draw_border_stripes(layer, ctx); - prv_draw_text(layer, ctx); - prv_draw_arrows(layer, ctx); -} - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { - DisplayCalibrationData *data = context; - if (data->state == DisplayCalibrationState_Confirm) { - // set a new user offset - shell_prefs_set_display_offset(data->offset); - analytics_inc(ANALYTICS_DEVICE_METRIC_DISPLAY_OFFSET_MODIFIED_COUNT, AnalyticsClient_System); - window_stack_remove(&data->window, true /* animated */); - return; - } - - data->state++; - layer_mark_dirty(&data->window.layer); -} - -static void prv_back_click_handler(ClickRecognizerRef recognizer, void *context) { - DisplayCalibrationData *data = context; - if (data->state == INITIAL_STATE) { - // exit the calibration window without changing the prefs - window_stack_remove(&data->window, true /* animated */); - return; - } - - data->state--; - layer_mark_dirty(&data->window.layer); -} - -static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { - DisplayCalibrationData *data = context; - - int to_add = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP) ? -1 : 1; - - switch (data->state) { - case DisplayCalibrationState_X_Adjust: - data->offset.x = CLIP(data->offset.x + to_add, -MAX_OFFSET_MAGNITUDE, MAX_OFFSET_MAGNITUDE); - break; - case DisplayCalibrationState_Y_Adjust: - data->offset.y = CLIP(data->offset.y + to_add, -MAX_OFFSET_MAGNITUDE, MAX_OFFSET_MAGNITUDE); - break; - case DisplayCalibrationState_Confirm: - break; - } - - layer_mark_dirty(&data->window.layer); -} - -static void prv_config_provider(void *data) { - const uint16_t interval_ms = 50; - window_single_repeating_click_subscribe(BUTTON_ID_UP, interval_ms, prv_up_down_click_handler); - window_single_repeating_click_subscribe(BUTTON_ID_DOWN, interval_ms, prv_up_down_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); - window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); -} - -void prv_window_unload(struct Window *window) { - DisplayCalibrationData *data = window_get_user_data(window); - - light_reset_user_controlled(); - - gbitmap_deinit(&data->arrow_down); - gbitmap_deinit(&data->arrow_left); - gbitmap_deinit(&data->arrow_up); - gbitmap_deinit(&data->arrow_right); - - // reinitialize display offset now that values may have changed - shell_prefs_display_offset_init(); - - layer_deinit(&data->layer); - i18n_free_all(data); - task_free(data); -} - -static void prv_init_arrow_bitmap(GBitmap *bitmap, uint32_t resource_id) { - gbitmap_init_with_resource(bitmap, resource_id); - - // tint cyan - PBL_ASSERTN(bitmap->info.format == GBitmapFormat2BitPalette); - unsigned int palette_size = gbitmap_get_palette_size(bitmap->info.format); - GColor *palette = task_zalloc_check(sizeof(GColor) * palette_size); - for (unsigned int i = 0; i < palette_size; i++) { - palette[i].argb = (bitmap->palette[i].argb & 0xc0) | (GColorCyan.argb & 0x3f); - } - gbitmap_set_palette(bitmap, palette, true /* free on destroy */); -} - -void settings_display_calibration_push(WindowStack *window_stack) { - DisplayCalibrationData *data = task_zalloc_check(sizeof(DisplayCalibrationData)); - *data = (DisplayCalibrationData) { - .offset = shell_prefs_get_display_offset() - }; - - shell_prefs_set_should_prompt_display_calibration(false); - display_set_offset(GPointZero); - light_enable(true); - - Window *window = &data->window; - window_init(window, "SettingsDisplayCalibration"); - window_set_click_config_provider_with_context(window, prv_config_provider, data); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { .unload = prv_window_unload }); - window_set_background_color(window, GColorBlack); - - Layer *root_layer = window_get_root_layer(window); - Layer *layer = &data->layer; - layer_init(layer, &root_layer->bounds); - layer_set_update_proc(layer, prv_layer_update_proc); - layer_add_child(root_layer, layer); - - prv_init_arrow_bitmap(&data->arrow_down, RESOURCE_ID_ARROW_DOWN); - prv_init_arrow_bitmap(&data->arrow_left, RESOURCE_ID_ARROW_LEFT); - prv_init_arrow_bitmap(&data->arrow_up, RESOURCE_ID_ARROW_UP); - prv_init_arrow_bitmap(&data->arrow_right, RESOURCE_ID_ARROW_RIGHT); - - window_stack_push(window_stack, window, true /* animated */); -} - -#endif diff --git a/src/fw/apps/system_apps/settings/settings_display_calibration.h b/src/fw/apps/system_apps/settings/settings_display_calibration.h deleted file mode 100644 index 6142088761..0000000000 --- a/src/fw/apps/system_apps/settings/settings_display_calibration.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// forward declaration -struct WindowStack; -typedef struct WindowStack WindowStack; - -void settings_display_calibration_push(WindowStack *window_stack); diff --git a/src/fw/apps/system_apps/settings/settings_factory_reset.c b/src/fw/apps/system_apps/settings/settings_factory_reset.c deleted file mode 100644 index 5fe4ba09e5..0000000000 --- a/src/fw/apps/system_apps/settings/settings_factory_reset.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_factory_reset.h" - -#include "applib/app_timer.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/action_bar_layer.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window_stack_private.h" -#include "applib/ui/ui.h" -#include "apps/system_apps/timeline/peek_layer.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/kernel_ui.h" -#include "kernel/ui/system_icons.h" -#include "kernel/util/factory_reset.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/common/system_task.h" -#include "settings_bluetooth.h" -#include "system/logging.h" - -#define MESSAGE_BUF_SIZE 96 - -typedef struct ConfirmUIData { - Window window; - ActionBarLayer action_bar; - TextLayer msg_text_layer; - TextLayer forget_text_layer; - PeekLayer resetting_layer; - char msg_text_layer_buffer[MESSAGE_BUF_SIZE]; - GBitmap *action_bar_icon_check; - GBitmap *action_bar_icon_x; -} ConfirmUIData; - -//! Wipe registry + Reboot -static void start_factory_reset(void *data) { - factory_reset(false /* should_shutdown */); -} - -static void prv_lockout_back_button(Window *window) { - window_set_overrides_back_button(window, true); - window_set_click_config_provider(window, NULL); -} - -static void confirm_click_handler(ClickRecognizerRef recognizer, Window *window) { - ConfirmUIData *data = window_get_user_data(window); - - // Need to lock-out inputs after starting the factory reset. - prv_lockout_back_button(window); - - PeekLayer *peek_layer = &data->resetting_layer; - peek_layer_init(peek_layer, &window->layer.bounds); - peek_layer_set_title_font(peek_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - TimelineResourceInfo timeline_res = { - .res_id = TIMELINE_RESOURCE_GENERIC_WARNING, - }; - peek_layer_set_icon(peek_layer, &timeline_res); - peek_layer_set_title(peek_layer, i18n_get("Resetting...", data)); - peek_layer_set_background_color(peek_layer, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)); - peek_layer_play(peek_layer); - layer_add_child(&window->layer, &peek_layer->layer); - - // give it a chance to animate - const uint32_t factory_reset_start_delay = 100; - app_timer_register(PEEK_LAYER_UNFOLD_DURATION + factory_reset_start_delay, - start_factory_reset, NULL); -} - -//! Wipe registry + Enter Standby (for factory) -static void confirm_long_click_handler(ClickRecognizerRef recognizer, Window *window) { - // Need to lock-out inputs after starting the factory reset. - prv_lockout_back_button(window); - - factory_reset(true /* should_shutdown */); -} - -static void decline_click_handler(ClickRecognizerRef recognizer, Window *window) { - const bool animated = true; - app_window_stack_pop(animated); - (void)recognizer; - (void)window; -} - -static void config_provider(Window *window) { - window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler) confirm_click_handler); - window_long_click_subscribe(BUTTON_ID_UP, 1200, (ClickHandler) confirm_long_click_handler, NULL); - window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) decline_click_handler); - (void)window; -} - -static void prv_window_load(Window *window) { - ConfirmUIData *data = window_get_user_data(window); - const GRect *root_layer_bounds = &window_get_root_layer(window)->bounds; - const int16_t width = root_layer_bounds->size.w - ACTION_BAR_WIDTH; - - const uint16_t x_margin_px = PBL_IF_ROUND_ELSE(6, 3); - const uint16_t msg_text_y_offset_px = PBL_IF_ROUND_ELSE(15, 0); - const uint16_t msg_text_max_height_px = root_layer_bounds->size.h - msg_text_y_offset_px; - - const GTextAlignment alignment = PBL_IF_ROUND_ELSE(GTextAlignmentRight, GTextAlignmentLeft); - const GTextOverflowMode overflow_mode = GTextOverflowModeTrailingEllipsis; - const GColor text_color = PBL_IF_COLOR_ELSE(GColorWhite, GColorBlack); - - TextLayer *msg_text_layer = &data->msg_text_layer; - GRect msg_text_frame = (GRect) { - .origin = GPoint(x_margin_px, msg_text_y_offset_px), - .size = GSize(width - (2 * x_margin_px), msg_text_max_height_px) - }; - text_layer_init_with_parameters(msg_text_layer, &msg_text_frame, - i18n_get("Perform factory reset?", data), - fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), text_color, - GColorClear, alignment, overflow_mode); - layer_add_child(&window->layer, &msg_text_layer->layer); -#if PBL_ROUND - const uint8_t text_flow_inset = 8; - text_layer_enable_screen_text_flow_and_paging(msg_text_layer, text_flow_inset); -#endif - - // handle different title heights gracefully - GContext *ctx = graphics_context_get_current_context(); - const uint16_t msg_text_height_px = text_layer_get_content_size(ctx, msg_text_layer).h; - const int text_spacing = 7; - const uint16_t forget_text_y_offset_px = msg_text_y_offset_px + msg_text_height_px + text_spacing; - - TextLayer *forget_text_layer = &data->forget_text_layer; - const GRect forget_text_frame = (GRect) { - .origin = GPoint(x_margin_px, forget_text_y_offset_px), - .size = GSize(width - (2 * x_margin_px), root_layer_bounds->size.h - forget_text_y_offset_px) - }; - text_layer_init_with_parameters(forget_text_layer, &forget_text_frame, - i18n_get(BT_FORGET_PAIRING_STR, data), - fonts_get_system_font(FONT_KEY_GOTHIC_18), text_color, - GColorClear, alignment, overflow_mode); - layer_add_child(&window->layer, &forget_text_layer->layer); -#if PBL_ROUND - text_layer_enable_screen_text_flow_and_paging(forget_text_layer, text_flow_inset); -#endif - - // Action bar: - ActionBarLayer *action_bar = &data->action_bar; - action_bar_layer_init(action_bar); - action_bar_layer_set_context(action_bar, window); - action_bar_layer_add_to_window(action_bar, window); - action_bar_layer_set_click_config_provider(action_bar, (ClickConfigProvider) config_provider); - action_bar_layer_set_icon(action_bar, BUTTON_ID_UP, data->action_bar_icon_check); - action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, data->action_bar_icon_x); -} - -static void prv_window_unload(Window *window) { - ConfirmUIData *data = window_get_user_data(window); - gbitmap_destroy(data->action_bar_icon_check); - gbitmap_destroy(data->action_bar_icon_x); - i18n_free_all(data); - app_free(data); -} - -void settings_factory_reset_window_push(void) { - ConfirmUIData *data = (ConfirmUIData*)app_malloc_check(sizeof(ConfirmUIData)); - *data = (ConfirmUIData){}; - - Window *window = &data->window; - window_init(window, "Settings Factory Reset"); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); -#if PBL_COLOR - window_set_background_color(window, GColorCobaltBlue); -#endif - window_set_user_data(window, data); - data->action_bar_icon_check = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_CHECK); - data->action_bar_icon_x = gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_X); - const bool animated = true; - app_window_stack_push(window, animated); -} - -#undef MESSAGE_BUF_SIZE diff --git a/src/fw/apps/system_apps/settings/settings_factory_reset.h b/src/fw/apps/system_apps/settings/settings_factory_reset.h deleted file mode 100644 index 7d78b053e3..0000000000 --- a/src/fw/apps/system_apps/settings/settings_factory_reset.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "kernel/events.h" - -void settings_factory_reset_window_push(); diff --git a/src/fw/apps/system_apps/settings/settings_menu.c b/src/fw/apps/system_apps/settings/settings_menu.c deleted file mode 100644 index 5e3e5fbc11..0000000000 --- a/src/fw/apps/system_apps/settings/settings_menu.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_activity_tracker.h" -#include "settings_bluetooth.h" -#include "settings_display.h" -#include "settings_menu.h" -#include "settings_notifications.h" -#include "settings_quick_launch.h" -#include "settings_quiet_time.h" -#include "settings_remote.h" -#include "settings_system.h" -#include "settings_time.h" -#include "settings_timeline.h" - -#if CAPABILITY_HAS_VIBE_SCORES -#include "settings_vibe_patterns.h" -#endif - -#include "applib/ui/app_window_stack.h" -#include "services/common/i18n/i18n.h" -#include "system/passert.h" - -static const SettingsModuleGetMetadata s_submodule_registry[] = { - [SettingsMenuItemBluetooth] = settings_bluetooth_get_info, - [SettingsMenuItemNotifications] = settings_notifications_get_info, -#if CAPABILITY_HAS_VIBE_SCORES - [SettingsMenuItemVibrations] = settings_vibe_patterns_get_info, -#endif - [SettingsMenuItemQuietTime] = settings_quiet_time_get_info, -#if CAPABILITY_HAS_TIMELINE_PEEK - [SettingsMenuItemTimeline] = settings_timeline_get_info, -#endif -#if !TINTIN_FORCE_FIT - [SettingsMenuItemActivity] = settings_activity_tracker_get_info, - [SettingsMenuItemQuickLaunch] = settings_quick_launch_get_info, - [SettingsMenuItemDateTime] = settings_time_get_info, -#else - [SettingsMenuItemActivity] = settings_system_get_info, - [SettingsMenuItemQuickLaunch] = settings_system_get_info, - [SettingsMenuItemDateTime] = settings_system_get_info, -#endif - [SettingsMenuItemDisplay] = settings_display_get_info, - [SettingsMenuItemSystem] = settings_system_get_info, -}; - -const SettingsModuleMetadata *settings_menu_get_submodule_info(SettingsMenuItem category) { - PBL_ASSERTN(category < SettingsMenuItem_Count); - return s_submodule_registry[category](); -} - -const char *settings_menu_get_status_name(SettingsMenuItem category) { - const SettingsModuleMetadata *info = settings_menu_get_submodule_info(category); - return info->name; -} - -void settings_menu_push(SettingsMenuItem category) { - Window *window = settings_menu_get_submodule_info(category)->init(); - app_window_stack_push(window, true /* animated */); -} diff --git a/src/fw/apps/system_apps/settings/settings_menu.h b/src/fw/apps/system_apps/settings/settings_menu.h deleted file mode 100644 index 41614c597e..0000000000 --- a/src/fw/apps/system_apps/settings/settings_menu.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" -#include "applib/ui/layer.h" -#include "applib/ui/window.h" - -#include - -#define SETTINGS_MENU_HIGHLIGHT_COLOR PBL_IF_COLOR_ELSE(GColorCobaltBlue, GColorBlack) -#define SETTINGS_MENU_TITLE_NORMAL_COLOR PBL_IF_COLOR_ELSE(GColorDarkGray, GColorBlack) - -typedef enum { - SettingsMenuItemBluetooth = 0, - SettingsMenuItemNotifications, -#if CAPABILITY_HAS_VIBE_SCORES - SettingsMenuItemVibrations, -#endif - SettingsMenuItemQuietTime, -#if CAPABILITY_HAS_TIMELINE_PEEK - SettingsMenuItemTimeline, -#endif - SettingsMenuItemQuickLaunch, - SettingsMenuItemDateTime, - SettingsMenuItemDisplay, - SettingsMenuItemActivity, - SettingsMenuItemSystem, - SettingsMenuItem_Count, - SettingsMenuItem_Invalid -} SettingsMenuItem; - -struct SettingsCallbacks; -typedef struct SettingsCallbacks SettingsCallbacks; - -typedef void (*SettingsDeinit)(SettingsCallbacks *context); -typedef uint16_t (*SettingsGetInitialSelection)(SettingsCallbacks *context); -typedef void (*SettingsSelectionChangedCallback)(SettingsCallbacks *context, uint16_t new_row, - uint16_t old_row); -typedef void (*SettingsSelectionWillChangeCallback)(SettingsCallbacks *context, uint16_t *new_row, - uint16_t old_row); -typedef void (*SettingsSelectClickCallback)(SettingsCallbacks *context, uint16_t row); -typedef void (*SettingsDrawRowCallback)(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected); -typedef uint16_t (*SettingsNumRowsCallback)(SettingsCallbacks *context); -typedef int16_t (*SettingsRowHeightCallback)(SettingsCallbacks *context, uint16_t row, - bool is_selected); -typedef void (*SettingsExpandCallback)(SettingsCallbacks *context); -typedef void (*SettingsAppearCallback)(SettingsCallbacks *context); -typedef void (*SettingsHideCallback)(SettingsCallbacks *context); - -struct SettingsCallbacks { - SettingsDeinit deinit; - SettingsDrawRowCallback draw_row; - SettingsGetInitialSelection get_initial_selection; - SettingsSelectionChangedCallback selection_changed; - SettingsSelectionWillChangeCallback selection_will_change; - SettingsSelectClickCallback select_click; - SettingsNumRowsCallback num_rows; - SettingsRowHeightCallback row_height; - SettingsExpandCallback expand; - SettingsAppearCallback appear; - SettingsHideCallback hide; -}; - -typedef Window *(*SettingsInitFunction)(void); - -typedef struct { - const char *name; - SettingsInitFunction init; -} SettingsModuleMetadata; - -typedef const SettingsModuleMetadata *(*SettingsModuleGetMetadata)(void); - -void settings_menu_mark_dirty(SettingsMenuItem category); -void settings_menu_reload_data(SettingsMenuItem category); -int16_t settings_menu_get_selected_row(SettingsMenuItem category); - -const SettingsModuleMetadata *settings_menu_get_submodule_info(SettingsMenuItem category); -const char *settings_menu_get_status_name(SettingsMenuItem category); -void settings_menu_push(SettingsMenuItem category); diff --git a/src/fw/apps/system_apps/settings/settings_notifications.c b/src/fw/apps/system_apps/settings/settings_notifications.c deleted file mode 100644 index bda47a219c..0000000000 --- a/src/fw/apps/system_apps/settings/settings_notifications.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_notifications_private.h" -#include "settings_menu.h" -#include "settings_option_menu.h" -#include "settings_window.h" - -#include "applib/event_service_client.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/action_menu_window_private.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/option_menu_window.h" -#include "applib/ui/ui.h" -#include "drivers/battery.h" -#include "kernel/pbl_malloc.h" -#include "popups/notifications/notification_window.h" -#include "services/common/analytics/analytics.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/vibes/vibe_intensity.h" -#include "shell/prefs.h" -#include "shell/system_theme.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" -#include "util/time/time.h" - -#include - -// Offset between vibe intensity menu item index and vibe intensity enum values -#define INTENSITY_ROW_OFFSET 1 - -typedef struct { - SettingsCallbacks callbacks; - EventServiceInfo battery_connection_event_info; -} SettingsNotificationsData; - -enum NotificationsItem { - NotificationsItemFilter, -#if !CAPABILITY_HAS_VIBE_SCORES - NotificationsItemVibration, -#endif - NotificationsItemTextSize, - NotificationsItemWindowTimeout, - NotificationsItemDesignStyle, - NotificationsItem_Count, -}; - -// Filter Alerts -////////////////////////// - -#define NUM_ALERT_MODES_IN_LIST 3 - -// These aren't all of the values of AlertMask, so to add extra ones you have to update both of -// these arrays - -static const AlertMask s_alert_mode_values[NUM_ALERT_MODES_IN_LIST] = { - AlertMaskAllOn, - AlertMaskPhoneCalls, - AlertMaskAllOff, -}; - -static const char *s_alert_mode_labels[NUM_ALERT_MODES_IN_LIST] = { - i18n_noop("Allow All Notifications"), - i18n_noop("Allow Phone Calls Only"), - i18n_noop("Mute All Notifications"), -}; - -static const char *prv_alert_mask_to_label(AlertMask mask) { - for (uint32_t i = 0; i < NUM_ALERT_MODES_IN_LIST; i++) { - if (s_alert_mode_values[i] == mask) { - return s_alert_mode_labels[i]; - } - } - return "???"; -} - -static void prv_filter_menu_select(OptionMenu *option_menu, int selection, void *context) { - alerts_set_mask(s_alert_mode_values[selection]); - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_filter_menu_push(SettingsNotificationsData *data) { - AlertMask mask = alerts_get_mask(); - size_t cycle_len = ARRAY_LENGTH(s_alert_mode_values); - size_t index = 0; - // TODO PBL-24306: update once AlertMask logic is made safer - for (size_t i = 0; i < cycle_len; i++) { - if (s_alert_mode_values[i] == mask) { - index = i; - break; - } - } - const OptionMenuCallbacks callbacks = { - .select = prv_filter_menu_select, - }; - /// The option in the Settings app for filtering notifications by type. - const char *title = i18n_noop("Filter"); - settings_option_menu_push( - title, OptionMenuContentType_DoubleLine, index, &callbacks, cycle_len, - true /* icons_enabled */, s_alert_mode_labels, data); -} - -// Vibe Settings (If vibes scores disabled for this model) -////////////////////////// -#if !CAPABILITY_HAS_VIBE_SCORES -static const char *strings_for_vibe_intensities[] = { - i18n_ctx_noop("NotifVibe", "Disabled"), - i18n_ctx_noop("NotifVibe", "Low"), - i18n_ctx_noop("NotifVibe", "Medium"), - i18n_ctx_noop("NotifVibe", "High") -}; - -static void prv_vibe_menu_select(OptionMenu *option_menu, int selection, void *context) { - const bool enable_vibration = (selection != 0); - const VibeIntensity new_vibe_intensity = enable_vibration ? (selection - INTENSITY_ROW_OFFSET) : - DEFAULT_VIBE_INTENSITY; - - alerts_set_vibrate(enable_vibration); - alerts_preferences_set_vibe_intensity(new_vibe_intensity); - vibe_intensity_set(new_vibe_intensity); - - if (enable_vibration) { - vibes_short_pulse(); - } - - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_vibe_menu_push(SettingsNotificationsData *data) { - const OptionMenuCallbacks callbacks = { - .select = prv_vibe_menu_select, - }; - /// The option in the Settings app for choosing a vibration intensity for notifications. - const char *title = i18n_noop("Vibration"); - uint32_t selected = vibe_intensity_get() + INTENSITY_ROW_OFFSET; - if (!alerts_get_vibrate()) { - selected = 0; - } - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, selected, &callbacks, - ARRAY_LENGTH(strings_for_vibe_intensities), true /* icons_enabled */, - strings_for_vibe_intensities, data); -} -#endif /* !CAPABILITY_HAS_VIBE_SCORES */ - -// Text Size -//////////////////////// - -static const char *s_text_size_names[] = { - [SettingsContentSize_Small] = i18n_noop("Smaller"), - [SettingsContentSize_Default] = i18n_noop("Default"), - [SettingsContentSize_Large] = i18n_noop("Larger"), -}; - -static void prv_text_size_menu_select(OptionMenu *option_menu, int selection, void *context) { - system_theme_set_content_size(settings_content_size_to_preferred_size(selection)); - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_text_size_menu_push(SettingsNotificationsData *data) { - const OptionMenuCallbacks callbacks = { - .select = prv_text_size_menu_select, - }; - /// The option in the Settings app for choosing the text size of notifications. - const char *title = i18n_noop("Text Size"); - const SettingsContentSize index = - settings_content_size_from_preferred_size(system_theme_get_content_size()); - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, index, &callbacks, SettingsContentSizeCount, - true /* icons_enabled */, s_text_size_names, data); -} - -// Text Size -//////////////////////// - -// NOTE: Keep the following two arrays in sync and with the same size. -static const uint32_t s_window_timeouts_ms[] = { - 15 * MS_PER_SECOND, - 1 * MS_PER_MINUTE, - NOTIF_WINDOW_TIMEOUT_DEFAULT, - 10 * MS_PER_MINUTE, - NOTIF_WINDOW_TIMEOUT_INFINITE -}; - -static const char *s_window_timeouts_labels[] = { - /// 15 Second Notification Window Timeout - i18n_noop("15 Seconds"), - /// 1 Minute Notification Window Timeout - i18n_noop("1 Minute"), - /// 3 Minute Notification Window Timeout - i18n_noop("3 Minutes"), - /// 10 Minute Notification Window Timeout - i18n_noop("10 Minutes"), - /// No Notification Window Timeout - i18n_noop("None"), -}; - -_Static_assert(ARRAY_LENGTH(s_window_timeouts_ms) == ARRAY_LENGTH(s_window_timeouts_labels), ""); - -static int prv_window_timeout_get_selection_index(void) { - const int DEFAULT_IDX = 2; - // Double check no one has fudged with the order and the fallback/default - PBL_ASSERTN(s_window_timeouts_ms[DEFAULT_IDX] == NOTIF_WINDOW_TIMEOUT_DEFAULT); - - const uint32_t timeout_ms = alerts_preferences_get_notification_window_timeout_ms(); - for (size_t i = 0; i < ARRAY_LENGTH(s_window_timeouts_ms); i++) { - if (s_window_timeouts_ms[i] == timeout_ms) { - return i; - } - } - // Should never happen (only should happen if we remove a timeout and don't migrate the user - // to a new setting automatically - return DEFAULT_IDX; -} - -static void prv_window_timeout_menu_select(OptionMenu *option_menu, int selection, void *context) { - alerts_preferences_set_notification_window_timeout_ms(s_window_timeouts_ms[selection]); - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_window_timeout_menu_push(SettingsNotificationsData *data) { - const int index = prv_window_timeout_get_selection_index(); - const OptionMenuCallbacks callbacks = { - .select = prv_window_timeout_menu_select, - }; - /// Status bar title for the Notification Window Timeout settings screen - const char *title = i18n_noop("Timeout"); - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, index, &callbacks, - ARRAY_LENGTH(s_window_timeouts_labels), true /* icons_enabled */, s_window_timeouts_labels, - data); -} - -// Design Style -//////////////////////// - -static const char *s_design_style_labels[] = { - /// Standard notification design option (default) - i18n_noop("Standard"), - /// Alternative notification design option - i18n_noop("Alternative"), -}; - -static int prv_design_style_get_selection_index(void) { - return alerts_preferences_get_notification_alternative_design() ? 1 : 0; -} - -static void prv_design_style_menu_select(OptionMenu *option_menu, int selection, void *context) { - alerts_preferences_set_notification_alternative_design(selection == 1); - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_design_style_menu_push(SettingsNotificationsData *data) { - const int index = prv_design_style_get_selection_index(); - const OptionMenuCallbacks callbacks = { - .select = prv_design_style_menu_select, - }; - /// Status bar title for the Notification Design Style settings screen - const char *title = i18n_noop("Style"); - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, index, &callbacks, - ARRAY_LENGTH(s_design_style_labels), true /* icons_enabled */, s_design_style_labels, - data); -} - -// Menu Layer Callbacks -//////////////////////// - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return NotificationsItem_Count; -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsNotificationsData *data = ((SettingsOptionMenuData *)context)->context; - const char *subtitle = NULL; - const char *title = NULL; - - switch (row) { - case NotificationsItemFilter: - title = i18n_noop("Filter"); - subtitle = prv_alert_mask_to_label(alerts_get_mask()); - break; -#if !CAPABILITY_HAS_VIBE_SCORES - case NotificationsItemVibration: - title = i18n_noop("Vibration"); - if (battery_is_usb_connected()) { - subtitle = i18n_noop("Disabled (Plugged In)"); - } else if (alerts_get_vibrate()) { - subtitle = strings_for_vibe_intensities[vibe_intensity_get() + INTENSITY_ROW_OFFSET]; - } else { - subtitle = strings_for_vibe_intensities[0]; - } - break; -#endif /* !CAPABILITY_HAS_VIBE_SCORES */ - case NotificationsItemTextSize: { - /// String within Settings->Notifications that describes the text font size - title = i18n_noop("Text Size"); - const SettingsContentSize index = - settings_content_size_from_preferred_size(system_theme_get_content_size()); - subtitle = (index < SettingsContentSizeCount) ? s_text_size_names[index] : ""; - break; - } - case NotificationsItemWindowTimeout: { - /// String within Settings->Notifications that describes the window timeout setting - title = i18n_noop("Timeout"); - subtitle = s_window_timeouts_labels[prv_window_timeout_get_selection_index()]; - break; - } - case NotificationsItemDesignStyle: { - /// String within Settings->Notifications that describes the notification design style - title = i18n_noop("Style"); - subtitle = s_design_style_labels[prv_design_style_get_selection_index()]; - break; - } - default: - WTF; - } - - menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsNotificationsData *data = (SettingsNotificationsData *)context; - i18n_free_all(data); - app_free(data); -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsNotificationsData *data = (SettingsNotificationsData *) context; - - switch (row) { - case NotificationsItemFilter: - prv_filter_menu_push(data); - break; -#if !CAPABILITY_HAS_VIBE_SCORES - case NotificationsItemVibration: - if (battery_is_usb_connected()) { - return; - } - prv_vibe_menu_push(data); - break; -#endif /* !CAPABILITY_HAS_VIBE_SCORES */ - case NotificationsItemTextSize: - prv_text_size_menu_push(data); - break; - case NotificationsItemWindowTimeout: - prv_window_timeout_menu_push(data); - break; - case NotificationsItemDesignStyle: - prv_design_style_menu_push(data); - break; - default: - WTF; - } - settings_menu_reload_data(SettingsMenuItemNotifications); -} - -static void prv_settings_notifications_event_handler(PebbleEvent *event, void *context) { - switch (event->type) { - case PEBBLE_BATTERY_CONNECTION_EVENT: - // Redraw the menu so that the Vibration status will be re-rendered. - settings_menu_mark_dirty(SettingsMenuItemNotifications); - break; - default: - break; - } -} - -static void prv_expand_cb(SettingsCallbacks *context) { - SettingsNotificationsData *data = (SettingsNotificationsData *) context; - - data->battery_connection_event_info = (EventServiceInfo) { - .type = PEBBLE_BATTERY_CONNECTION_EVENT, - .handler = prv_settings_notifications_event_handler, - }; - event_service_client_subscribe(&data->battery_connection_event_info); - -} - -static void prv_hide_cb(SettingsCallbacks *context) { - SettingsNotificationsData *data = (SettingsNotificationsData *) context; - - event_service_client_unsubscribe(&data->battery_connection_event_info); -} - -static Window *prv_init(void) { - SettingsNotificationsData* data = app_malloc_check(sizeof(*data)); - *data = (SettingsNotificationsData){}; - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - .expand = prv_expand_cb, - .hide = prv_hide_cb, - }; - - return settings_window_create(SettingsMenuItemNotifications, &data->callbacks); -} - -const SettingsModuleMetadata *settings_notifications_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Notifications"), - .init = prv_init, - }; - - return &s_module_info; -} - -void analytics_external_collect_notification_settings(void) { - const uint8_t strength = get_strength_for_intensity(vibe_intensity_get()); - analytics_set(ANALYTICS_DEVICE_METRIC_SETTING_VIBRATION_STRENGTH, - strength, AnalyticsClient_System); -} diff --git a/src/fw/apps/system_apps/settings/settings_notifications.h b/src/fw/apps/system_apps/settings/settings_notifications.h deleted file mode 100644 index 6efbb9edb7..0000000000 --- a/src/fw/apps/system_apps/settings/settings_notifications.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_notifications_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_notifications_private.h b/src/fw/apps/system_apps/settings/settings_notifications_private.h deleted file mode 100644 index 0144113345..0000000000 --- a/src/fw/apps/system_apps/settings/settings_notifications_private.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_notifications.h" - -#include "applib/preferred_content_size.h" - -//! TODO PBL-41920: This mapping should be an opt in set in a platform specific location -typedef enum SettingsContentSize { - SettingsContentSize_Small, - SettingsContentSize_Default, - SettingsContentSize_Large, - SettingsContentSizeCount, -} SettingsContentSize; - -static inline SettingsContentSize settings_content_size_from_preferred_size( - PreferredContentSize preferred_size) { - return preferred_size + (SettingsContentSize_Default - PreferredContentSizeDefault); -} - -static inline PreferredContentSize settings_content_size_to_preferred_size( - SettingsContentSize settings_size) { - return settings_size + (PreferredContentSizeDefault - SettingsContentSize_Default); -} diff --git a/src/fw/apps/system_apps/settings/settings_option_menu.c b/src/fw/apps/system_apps/settings/settings_option_menu.c deleted file mode 100644 index 32e4e4734e..0000000000 --- a/src/fw/apps/system_apps/settings/settings_option_menu.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_option_menu.h" - -#include "settings_menu.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" - -static void prv_menu_unload(OptionMenu *option_menu, void *context) { - SettingsOptionMenuData *data = context; - if (data->callbacks.unload) { - data->callbacks.unload(option_menu, data); - } - option_menu_destroy(option_menu); - i18n_free_all(option_menu); - task_free(context); -} - -static uint16_t prv_menu_get_num_rows(OptionMenu *option_menu, void *context) { - return ((SettingsOptionMenuData *)context)->num_rows; -} - -static void prv_menu_draw_row(OptionMenu *option_menu, GContext *ctx, const Layer *cell_layer, - const GRect *cell_frame, uint32_t row, bool selected, void *context) { - SettingsOptionMenuData *data = context; - const char *title = i18n_get(data->rows[row], option_menu); - option_menu_system_draw_row(option_menu, ctx, cell_layer, cell_frame, title, selected, context); -} - -OptionMenu *settings_option_menu_create( - const char *i18n_title_key, OptionMenuContentType content_type, int choice, - const OptionMenuCallbacks *callbacks_ref, uint16_t num_rows, bool icons_enabled, - const char **rows, void *context) { - OptionMenu *option_menu = option_menu_create(); - if (!option_menu) { - return NULL; - } - const OptionMenuConfig config = { - .title = i18n_get(i18n_title_key, option_menu), - .content_type = content_type, - .choice = choice, - .status_colors = { GColorWhite, GColorBlack }, - .highlight_colors = { SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite }, - .icons_enabled = icons_enabled, - }; - option_menu_configure(option_menu, &config); - SettingsOptionMenuData *data = task_malloc_check(sizeof(SettingsOptionMenuData)); - OptionMenuCallbacks callbacks = *callbacks_ref; - *data = (SettingsOptionMenuData) { - .callbacks = callbacks, - .context = context, - .num_rows = num_rows, - .rows = rows, - }; - callbacks.draw_row = prv_menu_draw_row; - callbacks.get_num_rows = prv_menu_get_num_rows; - callbacks.unload = prv_menu_unload; - option_menu_set_callbacks(option_menu, &callbacks, data); - return option_menu; -} - -OptionMenu *settings_option_menu_push( - const char *i18n_title_key, OptionMenuContentType content_type, int choice, - const OptionMenuCallbacks *callbacks_ref, uint16_t num_rows, bool icons_enabled, - const char **rows, void *context) { - OptionMenu * const option_menu = settings_option_menu_create( - i18n_title_key, content_type, choice, callbacks_ref, num_rows, icons_enabled, rows, context); - if (option_menu) { - const bool animated = true; - app_window_stack_push(&option_menu->window, animated); - } - return option_menu; -} - -void *settings_option_menu_get_context(SettingsOptionMenuData *data) { - return data->context; -} diff --git a/src/fw/apps/system_apps/settings/settings_option_menu.h b/src/fw/apps/system_apps/settings/settings_option_menu.h deleted file mode 100644 index 138f0f8a74..0000000000 --- a/src/fw/apps/system_apps/settings/settings_option_menu.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/ui/option_menu_window.h" - -#include "applib/ui/ui.h" - -typedef struct { - OptionMenuCallbacks callbacks; - void *context; - const char **rows; - uint16_t num_rows; -} SettingsOptionMenuData; - -OptionMenu *settings_option_menu_create( - const char *i18n_title_key, OptionMenuContentType content_type, int choice, - const OptionMenuCallbacks *callbacks, uint16_t num_rows, bool icons_enabled, const char **rows, - void *context); - -OptionMenu *settings_option_menu_push( - const char *i18n_title_key, OptionMenuContentType content_type, int choice, - const OptionMenuCallbacks *callbacks, uint16_t num_rows, bool icons_enabled, const char **rows, - void *context); - -void *settings_option_menu_get_context(SettingsOptionMenuData *data); diff --git a/src/fw/apps/system_apps/settings/settings_quick_launch.c b/src/fw/apps/system_apps/settings/settings_quick_launch.c deleted file mode 100644 index 9b757f208a..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quick_launch.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! This file displays the main Quick Launch menu that is found in our settings menu -//! It allows the feature to be enabled or for an app to be set -//! The list of apps that the user can choose from is found in settings_quick_launch_app_menu.c -//! This file is also responsible for saving / storing the uuid of each quichlaunch app as well as -//! whether or not the quicklaunch app is enabled. - -#include "settings_menu.h" -#include "settings_quick_launch.h" -#include "settings_quick_launch_app_menu.h" -#include "settings_quick_launch_setup_menu.h" -#include "settings_window.h" - -#include "applib/app.h" -#include "applib/app_launch_button.h" -#include "applib/app_launch_reason.h" -#include "applib/ui/window_stack.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_menu_data_source.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "shell/normal/quick_launch.h" -#include "system/passert.h" -#include "system/status_codes.h" - -typedef struct QuickLaunchData { - SettingsCallbacks callbacks; - char app_names[NUM_BUTTONS][APP_NAME_SIZE_BYTES]; -} QuickLaunchData; - -static const char *s_button_titles[NUM_BUTTONS] = { - /// Shown in Quick Launch Settings as the title of the up button quick launch option. - [BUTTON_ID_UP] = i18n_noop("Up Button"), - /// Shown in Quick Launch Settings as the title of the center button quick launch option. - [BUTTON_ID_SELECT] = i18n_noop("Center Button"), - /// Shown in Quick Launch Settings as the title of the down button quick launch option. - [BUTTON_ID_DOWN] = i18n_noop("Down Button"), - /// Shown in Quick Launch Settings as the title of the back button quick launch option. - [BUTTON_ID_BACK] = i18n_noop("Back Button"), -}; - -static ButtonId s_button_order[NUM_BUTTONS] = { - BUTTON_ID_UP, - BUTTON_ID_SELECT, - BUTTON_ID_DOWN, - BUTTON_ID_BACK, -}; - -static ButtonId s_button_order_map[NUM_BUTTONS] = { - [BUTTON_ID_UP] = 0, - [BUTTON_ID_SELECT] = 1, - [BUTTON_ID_DOWN] = 2, - [BUTTON_ID_BACK] = 3, -}; - -static void prv_get_subtitle_string(AppInstallId app_id, QuickLaunchData *data, - char *buffer, uint8_t buf_len) { - if (app_id == INSTALL_ID_INVALID) { - /// Shown in Quick Launch Settings when the button is disabled. - i18n_get_with_buffer("Disabled", buffer, buf_len); - return; - } else { - AppInstallEntry entry; - if (app_install_get_entry_for_install_id(app_id, &entry)) { - strncpy(buffer, entry.name, buf_len); - buffer[buf_len - 1] = '\0'; - return; - } - } - // if failed both, set as empty string - buffer[0] = '\0'; -} - -// Filter List Callbacks -//////////////////////// -static void prv_deinit_cb(SettingsCallbacks *context) { - QuickLaunchData *data = (QuickLaunchData *) context; - i18n_free_all(data); - app_free(data); -} - -static void prv_update_app_names(QuickLaunchData *data) { - for (ButtonId button = 0; button < NUM_BUTTONS; button++) { - char *subtitle_buf = data->app_names[button]; - prv_get_subtitle_string(quick_launch_get_app(button), data, subtitle_buf, APP_NAME_SIZE_BYTES); - } -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - QuickLaunchData *data = (QuickLaunchData *)context; - PBL_ASSERTN(row < NUM_BUTTONS); - const ButtonId button = s_button_order[row]; - const char *title = i18n_get(s_button_titles[button], data); - char *subtitle_buf = data->app_names[button]; - menu_cell_basic_draw(ctx, cell_layer, title, subtitle_buf, NULL); -} - -static uint16_t prv_get_initial_selection_cb(SettingsCallbacks *context) { - // If launched by quick launch, select the row of the button pressed, otherwise default to 0 - return (app_launch_reason() == APP_LAUNCH_QUICK_LAUNCH) ? - s_button_order_map[app_launch_button()] : 0; -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - PBL_ASSERTN(row < NUM_BUTTONS); - quick_launch_app_menu_window_push(s_button_order[row]); -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return NUM_BUTTONS; -} - -static void prv_appear(SettingsCallbacks *context) { - QuickLaunchData *data = (QuickLaunchData *)context; - prv_update_app_names(data); -} - -static Window *prv_init(void) { - QuickLaunchData *data = app_malloc_check(sizeof(*data)); - *data = (QuickLaunchData){}; - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .get_initial_selection = prv_get_initial_selection_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - .appear = prv_appear, - }; - - return settings_window_create(SettingsMenuItemQuickLaunch, &data->callbacks); -} - -const SettingsModuleMetadata *settings_quick_launch_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - /// Title of the Quick Launch Settings submenu in Settings - .name = i18n_noop("Quick Launch"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_quick_launch.h b/src/fw/apps/system_apps/settings/settings_quick_launch.h deleted file mode 100644 index 5719f086d2..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quick_launch.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_quick_launch_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_quick_launch_app_menu.c b/src/fw/apps/system_apps/settings/settings_quick_launch_app_menu.c deleted file mode 100644 index 399e6d4322..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quick_launch_app_menu.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! This file generates a menu that lets the user select a quicklaunch app -//! The menu that is generated is the same as the "main menu" but with a -//! title - -#include "settings_quick_launch_app_menu.h" -#include "settings_quick_launch_setup_menu.h" -#include "settings_quick_launch.h" -#include "settings_menu.h" -#include "settings_option_menu.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/option_menu_window.h" -#include "applib/ui/window_stack.h" -#include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_menu_data_source.h" -#include "resource/resource_ids.auto.h" - -typedef struct { - AppMenuDataSource data_source; - ButtonId button; - int16_t selected; -} QuickLaunchAppMenuData; - -#define NUM_CUSTOM_CELLS 1 - - -/* Callback Functions */ - -static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstallEntry *entry) { - if (app_install_entry_is_watchface(entry)) { - return false; // Skip watchfaces - } - if (app_install_entry_is_hidden(entry) && - !app_install_entry_is_quick_launch_visible_only(entry)) { - return false; // Skip hidden apps unless they are quick launch visible - } - return true; -} - -static uint16_t prv_menu_get_num_rows(OptionMenu *option_menu, void *context) { - QuickLaunchAppMenuData *data = context; - return app_menu_data_source_get_count(&data->data_source) + NUM_CUSTOM_CELLS; -} - -static void prv_menu_draw_row(OptionMenu *option_menu, GContext* ctx, const Layer *cell_layer, - const GRect *text_frame, uint32_t row, bool selected, void *context) { - - QuickLaunchAppMenuData *data = context; - const char *text = NULL; - if (row == 0) { - text = i18n_get("Disable", data); - } else { - AppMenuNode *node = app_menu_data_source_get_node_at_index(&data->data_source, - row - NUM_CUSTOM_CELLS); - text = node->name; - } - option_menu_system_draw_row(option_menu, ctx, cell_layer, text_frame, text, selected, context); -} - -static void prv_menu_select(OptionMenu *option_menu, int selection, void *context) { - window_set_click_config_provider(&option_menu->window, NULL); - - QuickLaunchAppMenuData *data = context; - if (selection == 0) { - quick_launch_set_app(data->button, INSTALL_ID_INVALID); - quick_launch_set_enabled(data->button, false); - app_window_stack_pop(true); - } else { - AppMenuNode* app_menu_node = - app_menu_data_source_get_node_at_index(&data->data_source, selection - NUM_CUSTOM_CELLS); - quick_launch_set_app(data->button, app_menu_node->install_id); - app_window_stack_pop(true); - } -} - -static void prv_menu_reload_data(void *context) { - OptionMenu *option_menu = context; - option_menu_reload_data(option_menu); -} - -static void prv_menu_unload(OptionMenu *option_menu, void *context) { - QuickLaunchAppMenuData *data = context; - - option_menu_destroy(option_menu); - app_menu_data_source_deinit(&data->data_source); - i18n_free_all(data); - app_free(data); -} - -void quick_launch_app_menu_window_push(ButtonId button) { - QuickLaunchAppMenuData *data = app_zalloc_check(sizeof(*data)); - data->button = button; - - OptionMenu *option_menu = option_menu_create(); - - app_menu_data_source_init(&data->data_source, &(AppMenuDataSourceCallbacks) { - .changed = prv_menu_reload_data, - .filter = prv_app_filter_callback, - }, option_menu); - - const AppInstallId install_id = quick_launch_get_app(button); - const int app_index = app_menu_data_source_get_index_of_app_with_install_id(&data->data_source, - install_id); - - const OptionMenuConfig config = { - .title = i18n_get(i18n_noop("Quick Launch"), data), - .choice = (install_id == INSTALL_ID_INVALID) ? 0 : (app_index + NUM_CUSTOM_CELLS), - .status_colors = { GColorWhite, GColorBlack, }, - .highlight_colors = { SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite }, - .icons_enabled = true, - }; - option_menu_configure(option_menu, &config); - option_menu_set_callbacks(option_menu, &(OptionMenuCallbacks) { - .select = prv_menu_select, - .get_num_rows = prv_menu_get_num_rows, - .draw_row = prv_menu_draw_row, - .unload = prv_menu_unload, - }, data); - - const bool animated = true; - app_window_stack_push(&option_menu->window, animated); -} diff --git a/src/fw/apps/system_apps/settings/settings_quick_launch_app_menu.h b/src/fw/apps/system_apps/settings/settings_quick_launch_app_menu.h deleted file mode 100644 index 3725a0f686..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quick_launch_app_menu.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "shell/normal/quick_launch.h" -#include - -void quick_launch_app_menu_window_push(ButtonId button); diff --git a/src/fw/apps/system_apps/settings/settings_quick_launch_setup_menu.c b/src/fw/apps/system_apps/settings/settings_quick_launch_setup_menu.c deleted file mode 100644 index d9cdf306af..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quick_launch_setup_menu.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! This file is responsible for displaying the initial Quick Launch setup menu. -//! If a user long presses up or down from a watchface and has previously not -//! set up an application to launch for that long press direction or has not disabled -//! the Quick Launch feature, then this will act as a mini-setup guide for the feature. -//! Once an application is set up to launch for that menu press direction, this should -//! never appear again. - -#include "settings_quick_launch_setup_menu.h" -#include "settings_quick_launch_app_menu.h" -#include "settings_quick_launch.h" - -#include "applib/app.h" -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/window_stack.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_menu_data_source.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" -#include "system/passert.h" - -#include - -typedef enum QuickLaunchSetupVersion { - //! Initial version or never opened - QuickLaunchSetupVersion_InitialVersion = 0, - //! 4.0 UX with Toggle Apps - QuickLaunchSetupVersion_UX4WithToggleApps = 1, - - QuickLaunchSetupVersionCount, - //! QuickLaunchSetupVersion is an increasing version number. QuickLaunchSetupVersionCurrent must - //! not decrement. This should ensure that the current version is always the latest. - QuickLaunchSetupVersionCurrent = QuickLaunchSetupVersionCount - 1, -} QuickLaunchSetupVersion; - -static void prv_push_settings_menu(void) { - settings_menu_push(SettingsMenuItemQuickLaunch); -} - -static void prv_handle_quick_launch_confirm(ClickRecognizerRef recognizer, void *context) { - PBL_ASSERTN(context); - expandable_dialog_pop(context); - prv_push_settings_menu(); -} - -static void prv_push_first_use_dialog(void) { - const void *i18n_owner = prv_push_first_use_dialog; // Use this function as the i18n owner - /// Title for the Quick Launch first use dialog. - const char *header = i18n_get("Quick Launch", i18n_owner); - /// Help text for the Quick Launch first use dialog. - const char *text = i18n_get("Open favorite apps quickly with a long button press from your " - "watchface.", i18n_owner); - ExpandableDialog *expandable_dialog = expandable_dialog_create_with_params( - WINDOW_NAME("Quick Launch First Use"), RESOURCE_ID_SUNNY_DAY_TINY, text, GColorBlack, - GColorWhite, NULL, RESOURCE_ID_ACTION_BAR_ICON_CHECK, prv_handle_quick_launch_confirm); - expandable_dialog_set_header(expandable_dialog, header); -#if PBL_ROUND - expandable_dialog_set_header_font(expandable_dialog, - fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); -#endif - i18n_free_all(i18n_owner); - app_expandable_dialog_push(expandable_dialog); -} - -static void prv_init(void) { - const uint32_t version = quick_launch_get_quick_launch_setup_opened(); - quick_launch_set_quick_launch_setup_opened(QuickLaunchSetupVersionCurrent); - if (version == QuickLaunchSetupVersion_InitialVersion) { - prv_push_first_use_dialog(); - } else { - prv_push_settings_menu(); - } -} - -static void prv_main(void) { - prv_init(); - app_event_loop(); -} - -const PebbleProcessMd* quick_launch_setup_get_app_info(void) { - static const PebbleProcessMdSystem s_quick_launch_setup_app = { - .common = { - .visibility = ProcessVisibilityHidden, - .main_func = prv_main, - // UUID: 07e0d9cb-8957-4bf7-9d42-aaaaaaaaaaaa - .uuid = {0x07, 0xe0, 0xd9, 0xcb, 0x89, 0x57, 0x4b, 0xf7, - 0x9d, 0x42, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}, - }, - .name = "Quick Launch", - }; - return (const PebbleProcessMd*) &s_quick_launch_setup_app; -} diff --git a/src/fw/apps/system_apps/settings/settings_quick_launch_setup_menu.h b/src/fw/apps/system_apps/settings/settings_quick_launch_setup_menu.h deleted file mode 100644 index 987b921344..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quick_launch_setup_menu.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_quick_launch.h" - -#include "process_management/pebble_process_md.h" -#include "shell/normal/quick_launch.h" - -#include - -const PebbleProcessMd* quick_launch_setup_get_app_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_quiet_time.c b/src/fw/apps/system_apps/settings/settings_quiet_time.c deleted file mode 100644 index a1d23e71c6..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quiet_time.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_quiet_time.h" -#include "settings_menu.h" -#include "settings_window.h" - -#include "applib/ui/action_menu_window_private.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/menu_layer.h" -#include "applib/ui/time_range_selection_window.h" -#include "kernel/pbl_malloc.h" -#include "popups/health_tracking_ui.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" -#include "util/string.h" - -#include - -typedef struct { - SettingsCallbacks callbacks; - - char *action_menu_text; - - TimeRangeSelectionWindowData schedule_window; - ActionMenuConfig action_menu; -} SettingsQuietTimeData; - -enum QuietTimeItem { - QuietTimeItemManual, - QuietTimeItemCalendarAware, - QuietTimeItemWeekdayScheduled, - QuietTimeItemWeekendScheduled, - QuietTimeItemInterruptions, - QuietTimeItem_Count, -}; - -static const AlertMask s_dnd_mask_cycle[] = { - AlertMaskAllOff, - AlertMaskPhoneCalls, -}; - -static AlertMask prv_cycle_dnd_mask(void) { - AlertMask mask = alerts_get_dnd_mask(); - int index = 0; - for (size_t i = 0; i < ARRAY_LENGTH(s_dnd_mask_cycle); i++) { - if (s_dnd_mask_cycle[i] == mask) { - index = i; - break; - } - } - mask = s_dnd_mask_cycle[(index + 1) % ARRAY_LENGTH(s_dnd_mask_cycle)]; - alerts_set_dnd_mask(mask); - return mask; -} - -static const char *prv_get_dnd_mask_subtitle(void *i18n_key) { - const char *title = NULL; - switch (alerts_get_dnd_mask()) { - case AlertMaskAllOff: - title = i18n_get("Quiet All Notifications", i18n_key); - break; - case AlertMaskPhoneCalls: - title = i18n_get("Allow Phone Calls", i18n_key); - break; - default: - title = "???"; - break; - } - return title; -} - -/////////////////////////////// -// DND Action Menu Window -/////////////////////////////// - -enum { - DNDMenuItemDisable = 0, - DNDMenuItemChangeSchedule, - DNDMenuItem_Count -}; - -static void prv_toggle_scheduled_dnd(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - do_not_disturb_toggle_scheduled((DoNotDisturbScheduleType) item->action_data); -} - -static void prv_complete_schedule(TimeRangeSelectionWindowData *schedule_window, void *data) { - DoNotDisturbScheduleType type = (DoNotDisturbScheduleType) data; - DoNotDisturbSchedule schedule = { - .from_hour = schedule_window->from.hour, - .from_minute = schedule_window->from.minute, - .to_hour = schedule_window->to.hour, - .to_minute = schedule_window->to.minute, - }; - - if (schedule.from_hour == schedule.to_hour && schedule.from_minute == schedule.to_minute) { - if ((schedule.to_minute = (schedule.to_minute + 1) % 60) == 0) { - schedule.to_hour = (schedule.to_hour + 1) % 24; - } - } - - do_not_disturb_set_schedule(type, &schedule); - - const bool animated = true; - window_stack_remove(&schedule_window->window, animated); -} - -static void prv_time_range_select_window_push(DoNotDisturbScheduleType type, - SettingsQuietTimeData *data) { - DoNotDisturbSchedule schedule; - do_not_disturb_get_schedule(type, &schedule); - TimeRangeSelectionWindowData *schedule_window = &data->schedule_window; - time_range_selection_window_init(schedule_window, GColorCobaltBlue, - prv_complete_schedule, (void*)(uintptr_t) type); - - schedule_window->from.hour = schedule.from_hour; - schedule_window->from.minute = schedule.from_minute; - schedule_window->to.hour = schedule.to_hour; - schedule_window->to.minute = schedule.to_minute; - app_window_stack_push(&schedule_window->window, true); -} - -static void prv_dnd_set_schedule(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - DoNotDisturbScheduleType type = (DoNotDisturbScheduleType) item->action_data; - do_not_disturb_set_schedule_enabled(type, true); - prv_time_range_select_window_push(type, context); -} - -static void prv_scheduled_dnd_menu_cleanup(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); - SettingsQuietTimeData *data = context; - time_range_selection_window_deinit(&data->schedule_window); - app_free(data->action_menu_text); - i18n_free_all(&data->action_menu); - task_free(root_level); -} - -static void prv_get_dnd_time(DoNotDisturbScheduleType type, char *time_string, const uint8_t len) { - DoNotDisturbSchedule schedule; - do_not_disturb_get_schedule(type, &schedule); - - clock_format_time(time_string, len, schedule.from_hour, schedule.from_minute, true); - strcat(time_string, " - "); - uint8_t current_length = strnlen(time_string, len); - char *buffer = time_string + current_length; - clock_format_time(buffer, len - current_length, schedule.to_hour, schedule.to_minute, true); -} - -static void prv_scheduled_dnd_menu_push(DoNotDisturbScheduleType type, - SettingsQuietTimeData *data) { - data->action_menu = (ActionMenuConfig) { - .context = data, - .colors.background = SETTINGS_MENU_HIGHLIGHT_COLOR, - .did_close = prv_scheduled_dnd_menu_cleanup, - }; - - ActionMenuLevel *level = - task_malloc_check(sizeof(ActionMenuLevel) + DNDMenuItem_Count * sizeof(ActionMenuItem)); - *level = (ActionMenuLevel) { - .num_items = DNDMenuItem_Count, - .display_mode = ActionMenuLevelDisplayModeWide, - }; - const uint8_t text_max_size = 30; - const uint8_t buffer_size = text_max_size + 22; - data->action_menu_text = app_malloc_check(buffer_size); - if (do_not_disturb_is_schedule_enabled(type)) { - strncpy(data->action_menu_text, i18n_get("Disable", &data->action_menu), buffer_size); - } else { - strncpy(data->action_menu_text, i18n_get("Enable", &data->action_menu), text_max_size); - strcat(data->action_menu_text, " ("); - uint8_t current_length = strnlen(data->action_menu_text, buffer_size); - char *buffer = data->action_menu_text + current_length; - prv_get_dnd_time(type, buffer, buffer_size - current_length); - strcat(data->action_menu_text, ")"); - } - - level->items[DNDMenuItemDisable] = (ActionMenuItem) { - .label = data->action_menu_text, - .perform_action = prv_toggle_scheduled_dnd, - }; - - level->items[DNDMenuItemChangeSchedule] = (ActionMenuItem) { - .label = i18n_get("Change Schedule", &data->action_menu), - .perform_action = prv_dnd_set_schedule, - }; - - if (type == WeekdaySchedule) { - level->items[DNDMenuItemDisable].action_data = (void*)(uintptr_t) WeekdaySchedule; - level->items[DNDMenuItemChangeSchedule].action_data = (void*)(uintptr_t) WeekdaySchedule; - } else { - level->items[DNDMenuItemDisable].action_data = (void*)(uintptr_t) WeekendSchedule; - level->items[DNDMenuItemChangeSchedule].action_data = (void*)(uintptr_t) WeekendSchedule; - } - - data->action_menu.root_level = level; - app_action_menu_open(&data->action_menu); -} - -/////////////////////////////// -// Menu Layer Callbacks -/////////////////////////////// - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsQuietTimeData *data = (SettingsQuietTimeData *) context; - i18n_free_all(data); - app_free(data); -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsQuietTimeData *data = (SettingsQuietTimeData *) context; - const char *title = NULL; - char *subtitle = NULL; - const uint8_t buffer_length = 80; - subtitle = app_malloc_check(buffer_length); - - switch (row) { - case QuietTimeItemManual: - title = i18n_get("Manual", data); - strncpy(subtitle, do_not_disturb_is_manually_enabled() ? - i18n_get("On", data) : i18n_get("Off", data), buffer_length); - break; - case QuietTimeItemCalendarAware: - title = i18n_get("Calendar Aware", data); - strncpy(subtitle, do_not_disturb_is_smart_dnd_enabled() ? - i18n_ctx_get("QuietTime", "Enabled", data) : - i18n_ctx_get("QuietTime", "Disabled", data), buffer_length); - break; - case QuietTimeItemWeekdayScheduled: - title = i18n_get("Weekdays", data); - if (do_not_disturb_is_schedule_enabled(WeekdaySchedule)) { - prv_get_dnd_time(WeekdaySchedule, subtitle, buffer_length); - } else { - strncpy(subtitle, i18n_ctx_get("QuietTime", "Disabled", data), buffer_length); - } - break; - case QuietTimeItemWeekendScheduled: - title = i18n_get("Weekends", data); - if (do_not_disturb_is_schedule_enabled(WeekendSchedule)) { - prv_get_dnd_time(WeekendSchedule, subtitle, buffer_length); - } else { - strncpy(subtitle, i18n_ctx_get("QuietTime", "Disabled", data), buffer_length); - } - break; - case QuietTimeItemInterruptions: - title = i18n_get("Interruptions", data); - strncpy(subtitle, prv_get_dnd_mask_subtitle(data), buffer_length); - break; - default: - WTF; - } - menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); - app_free(subtitle); -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsQuietTimeData *data = (SettingsQuietTimeData *) context; - - switch (row) { - case QuietTimeItemManual: - do_not_disturb_toggle_manually_enabled(ManualDNDFirstUseSourceSettingsMenu); - break; - case QuietTimeItemCalendarAware: - do_not_disturb_toggle_smart_dnd(); - break; - case QuietTimeItemWeekdayScheduled: - prv_scheduled_dnd_menu_push(WeekdaySchedule, data); - break; - case QuietTimeItemWeekendScheduled: - prv_scheduled_dnd_menu_push(WeekendSchedule, data); - break; - case QuietTimeItemInterruptions: - prv_cycle_dnd_mask(); - break; - default: - WTF; - } - settings_menu_reload_data(SettingsMenuItemQuietTime); -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return QuietTimeItem_Count; -} - -static Window *prv_init(void) { - SettingsQuietTimeData* data = app_zalloc_check(sizeof(*data)); - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - }; - - return settings_window_create(SettingsMenuItemQuietTime, &data->callbacks); -} - -const SettingsModuleMetadata *settings_quiet_time_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Quiet Time"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_quiet_time.h b/src/fw/apps/system_apps/settings/settings_quiet_time.h deleted file mode 100644 index c62c2a83d4..0000000000 --- a/src/fw/apps/system_apps/settings/settings_quiet_time.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_quiet_time_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_remote.c b/src/fw/apps/system_apps/settings/settings_remote.c deleted file mode 100644 index 22196940ea..0000000000 --- a/src/fw/apps/system_apps/settings/settings_remote.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_remote.h" -#include "settings_bluetooth.h" - -#include "applib/fonts/fonts.h" -#include "applib/ui/action_menu_window_private.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/menu_layer.h" -#include "applib/ui/text_layer.h" -#include "applib/ui/ui.h" -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/system_icons.h" -#include "popups/ble_hrm/ble_hrm_stop_sharing_popup.h" -#include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/bluetooth/ble_hrm.h" -#include "system/logging.h" -#include "system/passert.h" - -#include - -#include -#include - -enum { - RemoteMenuForget = 0, -#if CAPABILITY_HAS_BUILTIN_HRM - RemoteMenuStopSharingHeartRate, -#endif - RemoteMenu_Count -}; - -typedef struct { - StoredRemote remote; - - ActionMenuConfig action_menu; - struct SettingsBluetoothData *bt_data; -} SettingsRemoteData; - -static void prv_dialog_unload(void *context) { - PBL_ASSERTN(context); - i18n_free_all(context); -} - -static void prv_show_dialog(void *i18n_owner) { - DialogCallbacks callback = { .unload = prv_dialog_unload }; - const char *text = BT_FORGET_PAIRING_STR; - ExpandableDialog *e_dialog = expandable_dialog_create_with_params( - "Forget Remote", RESOURCE_ID_GENERIC_CONFIRMATION_TINY, i18n_get(text, i18n_owner), - GColorWhite, GColorCobaltBlue, &callback, RESOURCE_ID_ACTION_BAR_ICON_CHECK, - expandable_dialog_close_cb); - i18n_free(text, i18n_owner); - - expandable_dialog_show_action_bar(e_dialog, true); - expandable_dialog_set_header(e_dialog, i18n_get("You're all set", e_dialog)); - - app_expandable_dialog_push(e_dialog); -} - -static void prv_forget_bt_classic_remote(BTDeviceAddress* address) { - bt_persistent_storage_delete_bt_classic_pairing_by_addr(address); - bt_driver_classic_disconnect(address); - analytics_inc(ANALYTICS_DEVICE_METRIC_BT_PAIRING_FORGET_COUNT, AnalyticsClient_System); -} - -static void prv_forget_ble_remote(int id) { - bt_persistent_storage_delete_ble_pairing_by_id(id); - - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_FORGET_COUNT, AnalyticsClient_System); -} - -static void prv_remote_menu_cleanup(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - ActionMenuLevel *root_level = action_menu_get_root_level(action_menu); - SettingsRemoteData *data = (SettingsRemoteData *) context; - i18n_free_all(data); - task_free((void *) root_level); - task_free(data); -} - -static void prv_forget_item(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - SettingsRemoteData* remote_data = (SettingsRemoteData*) context; - StoredRemote* remote = &remote_data->remote; - switch (remote->type) { - case StoredRemoteTypeBTClassic: - prv_forget_bt_classic_remote(&remote->classic.bd_addr); - break; - case StoredRemoteTypeBLE: - prv_forget_ble_remote(remote->ble.bonding); - break; - case StoredRemoteTypeBTDual: - prv_forget_bt_classic_remote(&remote->dual.classic.bd_addr); - prv_forget_ble_remote(remote->dual.ble.bonding); - break; - default: - WTF; - } - PBL_LOG(LOG_LEVEL_INFO, "User Forgot BT Pairing (%u)", remote->type); - PBL_LOG(LOG_LEVEL_DEBUG, "Name: %s", remote->name); - settings_bluetooth_update_remotes(remote_data->bt_data); - prv_show_dialog(context); -} - -#if CAPABILITY_HAS_BUILTIN_HRM -static GAPLEConnection *prv_le_connection_for_stored_remote(const StoredRemote *const remote) { - switch (remote->type) { - case StoredRemoteTypeBLE: return remote->ble.connection; - case StoredRemoteTypeBTDual: return remote->dual.ble.connection; - default: - return NULL; - } -} - -static void prv_stop_sharing_heart_rate(ActionMenu *action_menu, - const ActionMenuItem *item, - void *context) { - SettingsRemoteData *remote_data = (SettingsRemoteData*) context; - StoredRemote *remote = &remote_data->remote; - - GAPLEConnection *const connection = prv_le_connection_for_stored_remote(remote); - ble_hrm_revoke_sharing_permission_for_connection(connection); - - app_simple_dialog_push(ble_hrm_stop_sharing_popup_create()); -} -#endif // CAPABILITY_HAS_BUILTIN_HRM - -void settings_remote_menu_push(struct SettingsBluetoothData *bt_data, StoredRemote *stored_remote) { - SettingsRemoteData *data = app_malloc_check(sizeof(SettingsRemoteData)); - - PBL_LOG(LOG_LEVEL_DEBUG, "NAME: %s", stored_remote->name); - *data = (SettingsRemoteData){}; - data->remote = *stored_remote; - data->bt_data = bt_data; - - data->action_menu = (ActionMenuConfig) { - .context = data, - .colors.background = SETTINGS_MENU_HIGHLIGHT_COLOR, - .did_close = prv_remote_menu_cleanup, - }; - -#if CAPABILITY_HAS_BUILTIN_HRM - const bool is_sharing_hr = - settings_bluetooth_is_sharing_heart_rate_for_stored_remote(stored_remote); - const size_t num_items = RemoteMenu_Count - (is_sharing_hr ? 0 : 1); -#else - const size_t num_items = RemoteMenu_Count; -#endif - ActionMenuLevel *level = - task_zalloc_check(sizeof(ActionMenuLevel) + num_items * sizeof(ActionMenuItem)); - *level = (ActionMenuLevel) { - .num_items = num_items, - .display_mode = ActionMenuLevelDisplayModeWide, - }; - - level->items[RemoteMenuForget] = (ActionMenuItem) { - .label = i18n_get("Forget", data), - .perform_action = prv_forget_item, - .action_data = data, - }; - -#if CAPABILITY_HAS_BUILTIN_HRM - if (is_sharing_hr) { - level->items[RemoteMenuStopSharingHeartRate] = (ActionMenuItem) { - .label = i18n_get("Stop Sharing Heart Rate", data), - .perform_action = prv_stop_sharing_heart_rate, - .action_data = data, - }; - } -#endif - - data->action_menu.root_level = level; - app_action_menu_open(&data->action_menu); -} diff --git a/src/fw/apps/system_apps/settings/settings_remote.h b/src/fw/apps/system_apps/settings/settings_remote.h deleted file mode 100644 index 007a26d885..0000000000 --- a/src/fw/apps/system_apps/settings/settings_remote.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "kernel/events.h" -#include "settings_bluetooth.h" - -void settings_remote_menu_push(struct SettingsBluetoothData *bt_data, StoredRemote* stored_remote); diff --git a/src/fw/apps/system_apps/settings/settings_system.c b/src/fw/apps/system_apps/settings/settings_system.c deleted file mode 100644 index b56c71afc1..0000000000 --- a/src/fw/apps/system_apps/settings/settings_system.c +++ /dev/null @@ -1,1129 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_factory_reset.h" -#include "settings_menu.h" -#include "settings_system.h" -#include "settings_window.h" - -#include "applib/fonts/fonts.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/dialogs/actionable_dialog.h" -#include "applib/ui/dialogs/confirmation_dialog.h" -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "applib/ui/option_menu_window.h" -#include "applib/ui/ui.h" -#include "kernel/core_dump.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/modals/modal_manager.h" -#include "mfg/mfg_info.h" -#include "mfg/mfg_serials.h" -#include "resource/resource_ids.auto.h" -#include "services/common/bluetooth/local_id.h" -#include "services/common/i18n/i18n.h" -#include "services/common/status_led.h" -#include "services/common/system_task.h" -#include "services/normal/stationary.h" -#include "shell/normal/battery_ui.h" -#include "shell/prefs.h" -#include "system/bootbits.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/size.h" -#include "util/time/time.h" -#include "system/version.h" - -#include "services/normal/activity/activity.h" - -#include -#include - -#include "settings_certifications.h" - -enum { - SystemInformationItemBtAddress = 0, - SystemInformationItemFirmware, - SystemInformationItemLanguage, - SystemInformationItemRecovery, - SystemInformationItemBootloader, - SystemInformationItemHardware, - SystemInformationItemSerial, - SystemInformationItemUptime, - SystemInformationItemLegal, - SystemInformationItem_Count, -}; - -enum { - DebuggingItemCoreDumpNow = 0, - DebuggingItemCoreDumpShortcut, - DebuggingItem_Count, -}; - -typedef struct SystemCertificationData SystemCertificationData; - -typedef struct SystemCertificationMenuItem { - void (*draw_cell_fn)( - GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, - bool is_selected, const void *arg1, const void *arg2); - const void *arg1; - const void *arg2; - void (*select_cb)(SystemCertificationData *cd); -} SystemCertificationMenuItem; - -typedef struct SystemCertificationData { - GBitmap fcc_mark; - GBitmap kcc_mark; - GBitmap ce_mark; - GBitmap weee_mark; - GBitmap r_mark; - GBitmap t_mark; - GBitmap aus_rcm_mark; - GBitmap nom_nyce_mark; - - GBitmap **regulatory_marks; - uint8_t regulatory_marks_count; - // For buiding up regulatory marks cells when constructing the menu - uint8_t current_regulatory_marks_cell_start_idx; - uint8_t num_regulatory_marks_in_current_cell; - uint16_t current_regulatory_marks_cell_width; - - SystemCertificationMenuItem *menu_items; - uint16_t menu_count; - - Window kcc_window; - BitmapLayer bmp_layer; - TextLayer title_text; - TextLayer info_text; - StatusBarLayer status_layer; -} SystemCertificationData; - -typedef struct SystemInformationData { - FirmwareMetadata recovery_fw_metadata; - char bt_mac_addr[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]; - char boot_version_string[(sizeof(uint32_t) * 2) + 3]; - char recovery_version_string[sizeof(TINTIN_METADATA.version_tag)]; - // Ensure that OTP values are null-terminated - char serial_string[MFG_SERIAL_NUMBER_SIZE + 1]; - char hw_version_string[MFG_HW_VERSION_SIZE + 1]; - char uptime_string[16]; // "xxd xxh xxm xxs" - char const * subtitle_text[SystemInformationItem_Count]; - char language_string[16]; -} SystemInformationData; - -typedef struct SettingsSystemData { - SettingsCallbacks callbacks; - - SystemInformationData information_data; - SystemCertificationData certification_data; - - // The following components are shared by information, and certification. - Window window; - MenuLayer menu_layer; - StatusBarLayer status_layer; -} SettingsSystemData; - -typedef enum { - SystemMenuItemInformation, - SystemMenuItemCertification, - SystemMenuItemStationaryToggle, - SystemMenuItemDebugging, - SystemMenuItemShutDown, - SystemMenuItemFactoryReset, - SystemMenuItem_Count, -} SystemMenuItem; - -static const char *s_item_titles[SystemMenuItem_Count] = { - [SystemMenuItemInformation] = i18n_noop("Information"), - [SystemMenuItemCertification] = i18n_noop("Certification"), - [SystemMenuItemStationaryToggle] = i18n_noop("Stand-By Mode"), - [SystemMenuItemDebugging] = i18n_noop("Debugging"), - [SystemMenuItemShutDown] = i18n_noop("Shut Down"), - [SystemMenuItemFactoryReset] = i18n_noop("Factory Reset"), -}; - -// Common status bar component is used across all windows that need them. -// This will init it and set the correct style to be used within the settings -// app. -static void prv_init_status_bar(StatusBarLayer *status_layer, Window *window, const char *text) { - status_bar_layer_init(status_layer); - status_bar_layer_set_title(status_layer, text, false, false); - status_bar_layer_set_separator_mode(status_layer, OPTION_MENU_STATUS_SEPARATOR_MODE); - status_bar_layer_set_colors(status_layer, GColorWhite, GColorBlack); - layer_add_child(&window->layer, status_bar_layer_get_layer(status_layer)); -} - -// Deinit the common status bar component. -static void prv_deinit_status_bar(StatusBarLayer *status_layer) { - layer_remove_from_parent(status_bar_layer_get_layer(status_layer)); - status_bar_layer_deinit(status_layer); -} - -// Dialog callbacks for confirmation. -//////////////////////////////////////////////////// -static ConfirmationDialog *prv_settings_confirm(const char *title, const char *text, - uint32_t resource_id) { - ConfirmationDialog *confirmation_dialog = confirmation_dialog_create(title); - Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); - - dialog_set_text(dialog, i18n_get(text, confirmation_dialog)); - dialog_set_background_color(dialog, GColorRed); - dialog_set_text_color(dialog, GColorWhite); - dialog_set_icon(dialog, resource_id); - - i18n_free_all(confirmation_dialog); - - return confirmation_dialog; -} - -// Information Window -////////////////////// - -static const char* s_information_titles[SystemInformationItem_Count] = { - [SystemInformationItemBtAddress] = i18n_noop("BT Address"), - [SystemInformationItemFirmware] = i18n_noop("Firmware"), - [SystemInformationItemLanguage] = i18n_noop("Language"), - [SystemInformationItemRecovery] = i18n_noop("Recovery"), - [SystemInformationItemBootloader] = i18n_noop("Bootloader"), - [SystemInformationItemHardware] = i18n_noop("Hardware"), - [SystemInformationItemSerial] = i18n_noop("Serial"), - [SystemInformationItemUptime] = i18n_noop("Uptime"), - [SystemInformationItemLegal] = i18n_noop("Legal") -}; - -static void prv_populate_uptime_string(SystemInformationData* data) { - uint32_t seconds_since_reboot = time_get_uptime_seconds(); - - uint32_t days, hours, minutes, seconds; - time_util_split_seconds_into_parts(seconds_since_reboot, &days, &hours, &minutes, &seconds); - - sniprintf(data->uptime_string, sizeof(data->uptime_string), - "%"PRIu32"d %"PRIu32"h %"PRIu32"m %"PRIu32"s", days, hours, minutes, seconds); -} - -static void prv_information_draw_row_callback(GContext* ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - PBL_ASSERTN(cell_index->section == 0); - PBL_ASSERTN(cell_index->row < SystemInformationItem_Count); - - SettingsSystemData *data = (SettingsSystemData *) context; - SystemInformationData *info = &data->information_data; - - const char *title = i18n_get(s_information_titles[cell_index->row], data); - menu_cell_basic_draw(ctx, cell_layer, title, info->subtitle_text[cell_index->row], NULL); -} - -int16_t prv_information_get_cell_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, void *context) { - return PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), - (menu_layer_is_index_selected(menu_layer, cell_index) ? - MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : - MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); -} - -static uint16_t prv_information_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, void *context) { - return SystemInformationItem_Count; -} - -#include "drivers/led_controller.h" -#include "system/rtc_registers.h" -static void prv_color_led_easter_egg(void) { -#if CAPABILITY_HAS_LED - static int i = 0; - - // Simple test code to exercise each of the LEDs in the RGB cluster. - // Start after 3 clicks - switch (i) { - case 3: - led_controller_rgb_set_color(LED_RED); - break; - case 4: led_controller_rgb_set_color(LED_GREEN); break; - case 5: led_controller_rgb_set_color(LED_BLUE); break; - case 6: led_controller_rgb_set_color(LED_BLACK); i = 2; break; - default: break; - } - - i = (i + 1)%7; -#endif -} - -static void prv_information_select_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, - void *context) { - prv_color_led_easter_egg(); -} - -static void prv_information_window_load(Window *window) { - SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); - - prv_init_status_bar(&data->status_layer, &data->window, i18n_get("Information", data)); - - // Create the menu - MenuLayer *menu_layer = &data->menu_layer; - GRect bounds = data->window.layer.bounds; - const GEdgeInsets menu_layer_insets = (GEdgeInsets) { - .top = STATUS_BAR_LAYER_HEIGHT, - .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT) - }; - bounds = grect_inset(bounds, menu_layer_insets); - menu_layer_init(menu_layer, &bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = prv_information_get_num_rows_callback, - .get_cell_height = prv_information_get_cell_height_callback, - .draw_row = prv_information_draw_row_callback, - .select_click = prv_information_select_callback, - }); - menu_layer_set_highlight_colors(menu_layer, SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite); - menu_layer_set_click_config_onto_window(menu_layer, &data->window); - - layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); -} - -static void prv_information_window_unload(Window *window) { - SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); - menu_layer_deinit(&data->menu_layer); - prv_deinit_status_bar(&data->status_layer); -} - -static void prv_information_window_push(SettingsSystemData *data) { - SystemInformationData *info = &data->information_data; - *info = (SystemInformationData){}; - - bool success = version_copy_recovery_fw_version(info->recovery_version_string, - ARRAY_LENGTH(info->recovery_version_string)); - if (success == false) { - info->recovery_version_string[0] = '\0'; - } - - sniprintf(info->boot_version_string, sizeof(info->boot_version_string), - "0x%" PRIx32, boot_version_read()); - bt_local_id_copy_address_mac_string(info->bt_mac_addr); - - // Ensure OTP strings are null-terminated - mfg_info_get_serialnumber(info->serial_string, MFG_SERIAL_NUMBER_SIZE + 1); - mfg_info_get_hw_version(info->hw_version_string, MFG_HW_VERSION_SIZE + 1); - prv_populate_uptime_string(info); - - sniprintf(info->language_string, sizeof(info->language_string), - "%s, v%u", i18n_get_locale(), i18n_get_version()); - - info->subtitle_text[SystemInformationItemBtAddress] = info->bt_mac_addr; - info->subtitle_text[SystemInformationItemFirmware] = - (char*) (strlen(TINTIN_METADATA.version_tag) >= 2 - ? TINTIN_METADATA.version_tag : TINTIN_METADATA.version_short); - info->subtitle_text[SystemInformationItemLanguage] = info->language_string; - info->subtitle_text[SystemInformationItemRecovery] = info->recovery_version_string; - info->subtitle_text[SystemInformationItemBootloader] = info->boot_version_string; - info->subtitle_text[SystemInformationItemHardware] = info->hw_version_string; - info->subtitle_text[SystemInformationItemSerial] = info->serial_string; - info->subtitle_text[SystemInformationItemUptime] = info->uptime_string; -#if PLATFORM_ASTERIX || PLATFORM_OBELIX - info->subtitle_text[SystemInformationItemLegal] = "repebble.com/terms"; -#else - info->subtitle_text[SystemInformationItemLegal] = "pebble.com/legal"; -#endif - - window_init(&data->window, WINDOW_NAME("System Information")); - window_set_user_data(&data->window, data); - window_set_window_handlers(&data->window, &(WindowHandlers) { - .load = prv_information_window_load, - .unload = prv_information_window_unload, - }); - - app_window_stack_push(&data->window, true); -} - -// Coredump trigger sequence -//////////////////////////// - -static void prv_coredump_confirm_cb(ClickRecognizerRef recognizer, void *context) { - core_dump_reset(true /* force_overwrite */); -} - -static void prv_confirm_pop(ClickRecognizerRef recognizer, void *context) { - confirmation_dialog_pop((ConfirmationDialog *)context); -} - -static void prv_coredump_click_config(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, prv_coredump_confirm_cb); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_confirm_pop); - window_single_click_subscribe(BUTTON_ID_BACK, prv_confirm_pop); -} - -static void prv_maybe_trigger_core_dump() { - ConfirmationDialog *confirmation_dialog = prv_settings_confirm("Core Dump", - i18n_noop("Core dump and reboot?"), RESOURCE_ID_RESULT_FAILED_LARGE); - confirmation_dialog_set_click_config_provider(confirmation_dialog, - prv_coredump_click_config); - app_confirmation_dialog_push(confirmation_dialog); -} - -// Debug options window -/////////////////////// - -static const char* s_debugging_titles[DebuggingItem_Count] = { - [DebuggingItemCoreDumpNow] = i18n_noop("Bug report now"), - [DebuggingItemCoreDumpShortcut] = i18n_noop("Bug shortcut"), -}; - -static void prv_debugging_draw_row_callback(GContext* ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - PBL_ASSERTN(cell_index->section == 0); - PBL_ASSERTN(cell_index->row < DebuggingItem_Count); - - SettingsSystemData *data = (SettingsSystemData *) context; - - const char *title = i18n_get(s_debugging_titles[cell_index->row], data); - const char *subtitle_text = NULL; - if (cell_index->row == DebuggingItemCoreDumpShortcut) { - subtitle_text = shell_prefs_can_coredump_on_request() ? i18n_get("10 back-button presses", data) : i18n_get("Disabled", data); - } - menu_cell_basic_draw(ctx, cell_layer, title, subtitle_text, NULL); -} - -int16_t prv_debugging_get_cell_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, void *context) { - return PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), - (menu_layer_is_index_selected(menu_layer, cell_index) ? - MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : - MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); -} - -static uint16_t prv_debugging_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, void *context) { - return DebuggingItem_Count; -} - -static void prv_debugging_select_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, - void *context) { - switch (cell_index->row) { - case DebuggingItemCoreDumpNow: - prv_maybe_trigger_core_dump(); - break; - case DebuggingItemCoreDumpShortcut: - shell_prefs_set_coredump_on_request(!shell_prefs_can_coredump_on_request()); - break; - default: - WTF; - } - menu_layer_reload_data(menu_layer); -} - -static void prv_debugging_window_load(Window *window) { - SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); - - prv_init_status_bar(&data->status_layer, &data->window, i18n_get("Debugging", data)); - - // Create the menu - MenuLayer *menu_layer = &data->menu_layer; - GRect bounds = data->window.layer.bounds; - const GEdgeInsets menu_layer_insets = (GEdgeInsets) { - .top = STATUS_BAR_LAYER_HEIGHT, - .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT) - }; - bounds = grect_inset(bounds, menu_layer_insets); - menu_layer_init(menu_layer, &bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = prv_debugging_get_num_rows_callback, - .get_cell_height = prv_debugging_get_cell_height_callback, - .draw_row = prv_debugging_draw_row_callback, - .select_click = prv_debugging_select_callback, - }); - menu_layer_set_highlight_colors(menu_layer, SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite); - menu_layer_set_click_config_onto_window(menu_layer, &data->window); - - layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); -} - -static void prv_debugging_window_unload(Window *window) { - SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); - menu_layer_deinit(&data->menu_layer); - prv_deinit_status_bar(&data->status_layer); -} - -static void prv_debugging_window_push(SettingsSystemData *data) { - window_init(&data->window, WINDOW_NAME("Debugging")); - window_set_user_data(&data->window, data); - window_set_window_handlers(&data->window, &(WindowHandlers) { - .load = prv_debugging_window_load, - .unload = prv_debugging_window_unload, - }); - - app_window_stack_push(&data->window, true); -} - -// Debugging interstitial -///////////////////////// - -// Only show this once per boot. -static bool s_debugging_interstitial_confirmed = false; - -static void prv_debugging_confirm_cb(ClickRecognizerRef recognizer, void *context) { - ConfirmationDialog *dialog = (ConfirmationDialog *)context; - SettingsSystemData *data = (SettingsSystemData *)actionable_dialog_get_user_data((ActionableDialog *) dialog); - - confirmation_dialog_pop(dialog); - - s_debugging_interstitial_confirmed = true; - prv_debugging_window_push(data); -} - -static void prv_debugging_interstitial_click_config(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, prv_debugging_confirm_cb); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_confirm_pop); - window_single_click_subscribe(BUTTON_ID_BACK, prv_confirm_pop); -} - -static void prv_debugging_interstitial_trigger(SettingsSystemData *context) { - if (s_debugging_interstitial_confirmed) { - prv_debugging_window_push(context); - return; - } - - ConfirmationDialog *confirmation_dialog = prv_settings_confirm("Debugging confirmation", - i18n_noop("PebbleOS developers only!"), RESOURCE_ID_GENERIC_WARNING_SMALL); - actionable_dialog_set_user_data((ActionableDialog *)confirmation_dialog, (void *)context); - confirmation_dialog_set_click_config_provider(confirmation_dialog, - prv_debugging_interstitial_click_config); - app_confirmation_dialog_push(confirmation_dialog); -} - -// Certification Window -/////////////////////// - -int16_t prv_certification_get_cell_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, - void *context) { - return PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), - (menu_layer_is_index_selected(menu_layer, cell_index) ? - MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : - MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); -} - -static void prv_draw_mark_with_inversion(GContext *ctx, GBitmap *mark, const GRect *box, - bool inverted) { - graphics_context_set_compositing_mode(ctx, GCompOpTint); - graphics_draw_bitmap_in_rect(ctx, mark, box); -} - -static int16_t prv_draw_generic_mark(GContext *ctx, GBitmap *mark, GPoint origin, bool highlight) { - GRect box = (GRect) { - .origin = origin, - .size = mark->bounds.size - }; - prv_draw_mark_with_inversion(ctx, mark, &box, highlight); - return origin.x + box.size.w; -} - -#define MARK_PADDING 10 - -static void prv_draw_rt_cell_rect(GContext *ctx, const Layer *cell_layer, GBitmap *mark, - const char *text, PBL_UNUSED bool is_selected) { - int16_t x = (MARK_PADDING / 2); - GRect box = cell_layer->bounds; - const bool highlight = menu_cell_layer_is_highlighted(cell_layer); - const int16_t vertical_padding = 6; - const GPoint mark_origin = GPoint(x, vertical_padding); - x = prv_draw_generic_mark(ctx, mark, mark_origin, highlight) + (MARK_PADDING / 2); - box.origin.x = x; - box.origin.y += 8; - box.size.w -= x; - box.size.h -= 8; - const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18); - graphics_draw_text(ctx, text, font, box, GTextOverflowModeFill, GTextAlignmentLeft, NULL); -} - -#if PBL_ROUND -static void prv_draw_rt_cell_round(GContext *ctx, const Layer *cell_layer, GBitmap *mark, - const char *text, bool is_selected) { - GRect rt_rect = cell_layer->bounds; - const int16_t horizontal_padding = 10; - const int16_t vertical_padding = is_selected ? 6 : 0; - rt_rect = grect_inset_internal(rt_rect, horizontal_padding, vertical_padding); - - // Calculate where the mark should be drawn - const GSize mark_size = mark->bounds.size; - GRect mark_rect = (GRect) { .size = mark_size }; - // If the cell is selected, align the mark at the top center so we can draw the text below it - const GAlign alignment = is_selected ? GAlignTop : GAlignCenter; - grect_align(&mark_rect, &rt_rect, alignment, true /* clip */); - - // Draw the mark - const bool highlight = menu_cell_layer_is_highlighted(cell_layer); - prv_draw_generic_mark(ctx, mark, mark_rect.origin, highlight); - - // Only draw the text if the cell is selected - if (is_selected) { - const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_18); - GRect text_rect = rt_rect; - text_rect.size.h = fonts_get_font_height(font); - grect_align(&text_rect, &rt_rect, GAlignBottom, true /* clip */); - graphics_draw_text(ctx, text, font, text_rect, GTextOverflowModeFill, GTextAlignmentCenter, - NULL); - } -} -#endif - -static void prv_draw_rt_cell( - GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, - bool is_selected, const void *arg1, const void *arg2) { - GBitmap *mark = (GBitmap *)arg1; - const char *text = arg2; - PBL_IF_RECT_ELSE(prv_draw_rt_cell_rect, - prv_draw_rt_cell_round)(ctx, cell_layer, mark, text, is_selected); -} - -#if PBL_ROUND -static void prv_draw_fcc_cell_round( - GContext *ctx, const GRect *cell_layer_bounds, const char *fcc_title, - const char *fcc_number_subtitle, GBitmap *fcc_mark_icon, - bool cell_is_selected, bool cell_is_highlighted) { - const GFont fcc_title_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - const int16_t fcc_title_font_cap_padding = 10; - const GFont fcc_number_subtitle_font = fonts_get_system_font(FONT_KEY_GOTHIC_18); - const uint8_t fcc_title_height = fonts_get_font_height(fcc_title_font); - const uint8_t fcc_number_subtitle_height = fonts_get_font_height(fcc_number_subtitle_font); - const GTextOverflowMode text_overflow_mode = GTextOverflowModeFill; - - graphics_context_set_text_color(ctx, cell_is_highlighted ? GColorWhite : GColorBlack); - - // Calculate the container of the FCC cell content and center it within the cell - const int16_t title_and_icon_width = 50; - GRect container_rect = (GRect) { - .size = GSize(title_and_icon_width, fcc_title_height - fcc_title_font_cap_padding) - }; - if (cell_is_selected) { - // Note that we don't subtract the subtitle font's cap padding from the container height - // because it exactly matches the vertical spacing we want between the title and subtitle - container_rect.size.h += fcc_number_subtitle_height; - } - grect_align(&container_rect, cell_layer_bounds, GAlignCenter, true /* clip */); - - // Draw the FCC title in the top left of the container - // We'll reuse this box for the title, subtitle, and icon frames - GRect box = (GRect) { .size = GSize(container_rect.size.w, fcc_title_height) }; - grect_align(&box, &container_rect, GAlignTopLeft, true /* clip */); - box.origin.y -= fcc_title_font_cap_padding; - graphics_draw_text(ctx, fcc_title, fcc_title_font, box, text_overflow_mode, GTextAlignmentLeft, - NULL); - - // If the cell is selected, draw the FCC # subtitle centered at the bottom of the container - if (cell_is_selected) { - const int16_t fcc_number_subtitle_width = 60; - box.size = GSize(fcc_number_subtitle_width, fcc_number_subtitle_height); - // Note that we don't clip when we align the subtitle frame because it is wider than the - // combined width of the title and icon - grect_align(&box, &container_rect, GAlignBottom, false /* clip */); - graphics_draw_text(ctx, fcc_number_subtitle, fcc_number_subtitle_font, box, text_overflow_mode, - GTextAlignmentCenter, NULL); - } - - // Align the FCC mark icon to be drawn in the top right of the container - box.size = fcc_mark_icon->bounds.size; - grect_align(&box, &container_rect, GAlignTopRight, true /* clip */); - prv_draw_mark_with_inversion(ctx, fcc_mark_icon, &box, cell_is_highlighted); -} -#endif - -static void prv_draw_fcc_cell( - GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, - bool is_selected, const void *arg1, const void *arg2) { - const char *title = arg1; - const char *subtitle = arg2; - const bool highlight = menu_cell_layer_is_highlighted(cell_layer); - GBitmap *mark = &cd->fcc_mark; -#if PBL_RECT - menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); - // FCC has a mark in the top right of its cell - const GPoint mark_origin = GPoint(119, 7); - const GRect box = (GRect){.origin = mark_origin, .size = mark->bounds.size}; - prv_draw_mark_with_inversion(ctx, mark, &box, highlight); -#else - prv_draw_fcc_cell_round(ctx, &cell_layer->bounds, title, subtitle, mark, - is_selected, highlight); -#endif -} - -static void prv_draw_regulatory_marks_cell( - GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, - bool is_selected, const void *arg1, const void *arg2) { - const GRect *cell_layer_bounds = &cell_layer->bounds; - uint32_t start_idx = (uintptr_t)arg1; - uint32_t num_marks = (uintptr_t)arg2; - // Calculate rect containing generic marks - GSize overall_size = GSize(MARK_PADDING * (num_marks + 1), 0); - for (uint32_t i = 0; i < num_marks; i++) { - const GSize mark_size = cd->regulatory_marks[start_idx + i]->bounds.size; - overall_size.h = MAX(overall_size.h, mark_size.h); - overall_size.w += mark_size.w; - } - GRect regulatory_marks_rect = (GRect) { .size = overall_size }; - // Align the rect based on the display shape - const GAlign alignment = PBL_IF_RECT_ELSE(GAlignLeft, GAlignCenter); - grect_align(®ulatory_marks_rect, cell_layer_bounds, alignment, - /* clip */ true); - // Draw the regulatory marks - GPoint mark_origin = regulatory_marks_rect.origin; - mark_origin.x += MARK_PADDING; - const bool highlight = menu_cell_layer_is_highlighted(cell_layer); - for (uint32_t i = 0; i < num_marks; i++) { - GBitmap *mark = cd->regulatory_marks[start_idx + i]; - // Vertically center the icon in the cell - mark_origin.y = (cell_layer_bounds->size.h - mark->bounds.size.h) / 2; - // Draw the icon and advance the x coordinate for drawing the next icon - mark_origin.x = prv_draw_generic_mark(ctx, mark, mark_origin, - highlight) + MARK_PADDING; - } -} - -static void prv_append_certification_menu(SystemCertificationData *cd, - SystemCertificationMenuItem *item) { - PBL_ASSERTN(item->draw_cell_fn); - cd->menu_items = app_realloc(cd->menu_items, - sizeof(*cd->menu_items) * ++cd->menu_count); - PBL_ASSERTN(cd->menu_items); - cd->menu_items[cd->menu_count - 1] = *item; -} - -static void prv_append_regulatory_compliance_mark(SystemCertificationData *cd, - GBitmap *mark) { - // Determine whether adding this mark overflows the cell, necessitating - // another cell for this mark. - uint16_t mark_width = mark->bounds.size.w; - if (cd->current_regulatory_marks_cell_width + mark_width >= DISP_COLS) { - // Flush the current marks to a cell and start a new one. - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_regulatory_marks_cell, - .arg1 = (void *)(uintptr_t)cd->current_regulatory_marks_cell_start_idx, - .arg2 = (void *)(uintptr_t)cd->num_regulatory_marks_in_current_cell, - }); - cd->current_regulatory_marks_cell_start_idx += - cd->num_regulatory_marks_in_current_cell; - cd->num_regulatory_marks_in_current_cell = 0; - cd->current_regulatory_marks_cell_width = 0; - } - - cd->regulatory_marks = app_realloc( - cd->regulatory_marks, sizeof(GBitmap *) * ++cd->regulatory_marks_count); - PBL_ASSERTN(cd->regulatory_marks); - cd->regulatory_marks[cd->regulatory_marks_count - 1] = mark; - cd->num_regulatory_marks_in_current_cell++; - cd->current_regulatory_marks_cell_width += mark_width + MARK_PADDING; -} - -static void prv_finished_appending_regulatory_compliance_marks( - SystemCertificationData *cd) { - if (cd->num_regulatory_marks_in_current_cell) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_regulatory_marks_cell, - .arg1 = (void *)(uintptr_t)cd->current_regulatory_marks_cell_start_idx, - .arg2 = (void *)(uintptr_t)cd->num_regulatory_marks_in_current_cell, - }); - } -} - -static void prv_draw_regulatory_id_cell( - GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, - bool is_selected, const void *arg1, const void *arg2) { - const char *title = arg1; - const char *subtitle = arg2; - menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); -} - -static void prv_draw_korea_regulatory_cell( - GContext *ctx, const Layer *cell_layer, SystemCertificationData *cd, - bool is_selected, const void *arg1, const void *arg2) { - const char *title = arg1; - const char *subtitle = i18n_get("See details...", title); - menu_cell_basic_draw(ctx, cell_layer, title, subtitle, NULL); - i18n_free(subtitle, title); -} - -static void prv_certification_draw_row_callback(GContext* ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - SettingsSystemData *data = (SettingsSystemData *) context; - PBL_ASSERTN(cell_index->section == 0); - - SystemCertificationData *cd = &data->certification_data; - const bool is_selected = menu_layer_is_index_selected(&data->menu_layer, cell_index); - SystemCertificationMenuItem * const item = &cd->menu_items[cell_index->row]; - item->draw_cell_fn(ctx, cell_layer, cd, is_selected, item->arg1, item->arg2); -} - -static uint16_t prv_certification_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, - void *context) { - SettingsSystemData *data = (SettingsSystemData *) context; - return data->certification_data.menu_count; -} - -static void prv_push_kcc_window(SystemCertificationData *data); - -static void prv_certification_select_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, - void *context) { - SettingsSystemData *data = (SettingsSystemData *) context; - SystemCertificationData *cd = &data->certification_data; - if (cell_index->row < cd->menu_count && - cd->menu_items[cell_index->row].select_cb) { - cd->menu_items[cell_index->row].select_cb(cd); - } -} - -static void prv_certification_window_load(Window *window) { - SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); - - prv_init_status_bar(&data->status_layer, &data->window, i18n_get("Certification", data)); - - SystemCertificationData *cd = &data->certification_data; - *cd = (SystemCertificationData) {}; - - // Load up the assets - gbitmap_init_with_resource(&cd->fcc_mark, RESOURCE_ID_SYSTEM_FCC_MARK); - gbitmap_init_with_resource(&cd->kcc_mark, RESOURCE_ID_SYSTEM_KCC_MARK); - gbitmap_init_with_resource(&cd->ce_mark, RESOURCE_ID_SYSTEM_CE_MARK); - gbitmap_init_with_resource(&cd->weee_mark, RESOURCE_ID_SYSTEM_WEEE_MARK); - gbitmap_init_with_resource(&cd->r_mark, RESOURCE_ID_SYSTEM_R_MARK); - gbitmap_init_with_resource(&cd->t_mark, RESOURCE_ID_SYSTEM_T_MARK); - gbitmap_init_with_resource( - &cd->aus_rcm_mark, RESOURCE_ID_SYSTEM_AUS_RCM_MARK); - gbitmap_init_with_resource( - &cd->nom_nyce_mark, RESOURCE_ID_SYSTEM_NOM_NYCE_MARK); - - // Construct the certification menu - const RegulatoryFlags *flags = prv_get_regulatory_flags(); - if (flags->has_usa_fcc) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_fcc_cell, - .arg1 = "FCC", - .arg2 = prv_get_usa_fcc_id(), - }); - } - if (flags->has_canada_ic) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_regulatory_id_cell, - .arg1 = "Canada IC", - .arg2 = prv_get_canada_ic_id(), - }); - } - if (flags->has_china_cmiit) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_regulatory_id_cell, - .arg1 = "CMIIT ID", - .arg2 = prv_get_china_cmiit_id(), - }); - } - if (flags->has_korea_kcc) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_korea_regulatory_cell, - .arg1 = "South Korea KCC", - .select_cb = prv_push_kcc_window, - }); - } - if (flags->has_mexico_nom_nyce) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_regulatory_id_cell, - .arg1 = "IFETEL", - .arg2 = prv_get_mexico_ifetel_id(), - }); - } - - if (flags->has_korea_kcc) { - prv_append_regulatory_compliance_mark(cd, &cd->kcc_mark); - } - if (flags->has_eu_ce) { - prv_append_regulatory_compliance_mark(cd, &cd->ce_mark); - } - if (flags->has_eu_weee) { - prv_append_regulatory_compliance_mark(cd, &cd->weee_mark); - } - if (flags->has_australia_rcm) { - prv_append_regulatory_compliance_mark(cd, &cd->aus_rcm_mark); - } - if (flags->has_mexico_nom_nyce) { - prv_append_regulatory_compliance_mark(cd, &cd->nom_nyce_mark); - } - prv_finished_appending_regulatory_compliance_marks(cd); - - if (flags->has_japan_telec_r) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_rt_cell, - .arg1 = &cd->r_mark, - .arg2 = prv_get_japan_telec_r_id() - }); - } - if (flags->has_japan_telec_t) { - prv_append_certification_menu(cd, &(SystemCertificationMenuItem) { - .draw_cell_fn = prv_draw_rt_cell, - .arg1 = &cd->t_mark, - .arg2 = prv_get_japan_telec_t_id() - }); - } - - // Create the menu - MenuLayer *menu_layer = &data->menu_layer; - GRect bounds = data->window.layer.bounds; - const GEdgeInsets menu_layer_insets = (GEdgeInsets) { - .top = STATUS_BAR_LAYER_HEIGHT, - .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT) - }; - bounds = grect_inset(bounds, menu_layer_insets); - menu_layer_init(menu_layer, &bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = prv_certification_get_num_rows_callback, - .get_cell_height = prv_certification_get_cell_height_callback, - .draw_row = prv_certification_draw_row_callback, - .select_click = prv_certification_select_callback, - }); - menu_layer_set_highlight_colors(menu_layer, SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite); - menu_layer_set_click_config_onto_window(menu_layer, &data->window); - - layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); -} - -static void prv_certification_window_unload(Window *window) { - SettingsSystemData *data = (SettingsSystemData*) window_get_user_data(window); - - menu_layer_deinit(&data->menu_layer); - - gbitmap_deinit(&data->certification_data.fcc_mark); - gbitmap_deinit(&data->certification_data.kcc_mark); - gbitmap_deinit(&data->certification_data.ce_mark); - gbitmap_deinit(&data->certification_data.weee_mark); - gbitmap_deinit(&data->certification_data.r_mark); - gbitmap_deinit(&data->certification_data.t_mark); - gbitmap_deinit(&data->certification_data.aus_rcm_mark); - gbitmap_deinit(&data->certification_data.nom_nyce_mark); - - app_free(data->certification_data.regulatory_marks); - app_free(data->certification_data.menu_items); - - prv_deinit_status_bar(&data->status_layer); -} - -static void prv_certification_window_push(SettingsSystemData *data) { - window_init(&data->window, WINDOW_NAME("System Certification")); - window_set_user_data(&data->window, data); - window_set_window_handlers(&data->window, &(WindowHandlers) { - .load = prv_certification_window_load, - .unload = prv_certification_window_unload, - }); - app_window_stack_push(&data->window, true); -} - -static void prv_kcc_window_load(Window *window) { - SystemCertificationData *data = (SystemCertificationData *) window_get_user_data(window); - Layer *window_layer = window_get_root_layer(window); - - const char *title = "South Korea KCC"; - prv_init_status_bar(&data->status_layer, &data->kcc_window, title); - - GRect window_bounds = window_layer->bounds; - - // Calculate the bounding rect for the certification content and center it in the window - GBitmap *bmp = &data->kcc_mark; - const GSize bmp_size = bmp->bounds.size; - const GFont title_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - const GSize title_text_size = GSize(window_bounds.size.w, fonts_get_font_height(title_text_font)); - const GFont info_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_14); - const GSize info_text_size = GSize(window_bounds.size.w, fonts_get_font_height(info_text_font)); - const int16_t vertical_spacing = 3; - GRect certification_rect = (GRect) { - .size = GSize(window_bounds.size.w, - bmp_size.h + title_text_size.h + info_text_size.h + vertical_spacing) - }; - grect_align(&certification_rect, &window_bounds, GAlignCenter, true /* clip */); - - GRect bmp_frame = (GRect) { .size = bmp_size }; - grect_align(&bmp_frame, &certification_rect, GAlignTop, true /* clip */); - bitmap_layer_init(&data->bmp_layer, &bmp_frame); - bitmap_layer_set_bitmap(&data->bmp_layer, bmp); - bitmap_layer_set_compositing_mode(&data->bmp_layer, GCompOpAssign); - layer_add_child(window_layer, bitmap_layer_get_layer(&data->bmp_layer)); - - GRect title_text_frame = (GRect) { .size = title_text_size }; - const int16_t title_text_internal_padding = 5; - title_text_frame.origin.y = bmp_frame.origin.y + bmp_size.h + vertical_spacing - - title_text_internal_padding; - text_layer_init_with_parameters(&data->title_text, &title_text_frame, - title, title_text_font, - GColorBlack, GColorClear, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(window_layer, text_layer_get_layer(&data->title_text)); - - GRect info_text_frame = (GRect) { .size = info_text_size }; - info_text_frame.origin.y = title_text_frame.origin.y + title_text_size.h + vertical_spacing; - text_layer_init_with_parameters(&data->info_text, &info_text_frame, - prv_get_korea_kcc_id(), info_text_font, - GColorBlack, GColorClear, GTextAlignmentCenter, - GTextOverflowModeTrailingEllipsis); - layer_add_child(window_layer, text_layer_get_layer(&data->info_text)); -} - -static void prv_kcc_window_unload(Window *window) { - SystemCertificationData *data = (SystemCertificationData *) window_get_user_data(window); - prv_deinit_status_bar(&data->status_layer); - bitmap_layer_deinit(&data->bmp_layer); - text_layer_deinit(&data->title_text); - text_layer_deinit(&data->info_text); - i18n_free_all(data); -} - -static void prv_push_kcc_window(SystemCertificationData *data) { - window_init(&data->kcc_window, WINDOW_NAME("System KCC")); - window_set_user_data(&data->kcc_window, data); - window_set_window_handlers(&data->kcc_window, &(WindowHandlers) { - .load = prv_kcc_window_load, - .unload = prv_kcc_window_unload, - }); - app_window_stack_push(&data->kcc_window, true); -} - -// Callbacks for the main settings filter list menu. -//////////////////////////////////////////////////// - -#define SHUTDOWN_MIN_BOOT_VERSION 1354647953 - -static bool prv_shutdown_enabled(void) { - return boot_version_read() >= SHUTDOWN_MIN_BOOT_VERSION; -} - -static void prv_shutdown_confirm_cb(ClickRecognizerRef recognizer, void *context) { - actionable_dialog_pop((ActionableDialog *) context); - battery_ui_handle_shut_down(); -} - -static void prv_shutdown_back_cb(ClickRecognizerRef recognizer, void *context) { - actionable_dialog_pop((ActionableDialog *) context); -} - -static void prv_shutdown_click_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_SELECT, prv_shutdown_confirm_cb); - window_single_click_subscribe(BUTTON_ID_BACK, prv_shutdown_back_cb); -} - -static void prv_shutdown_cb(void* data) { - ActionableDialog *a_dialog = actionable_dialog_create("Shutdown"); - Dialog *dialog = actionable_dialog_get_dialog(a_dialog); - - actionable_dialog_set_action_bar_type(a_dialog, DialogActionBarConfirm, NULL); - actionable_dialog_set_click_config_provider(a_dialog, prv_shutdown_click_provider); - - dialog_set_text_color(dialog, GColorWhite); - dialog_set_background_color(dialog, GColorCobaltBlue); - dialog_set_text(dialog, i18n_get("Do you want to shut down?", a_dialog)); - dialog_set_icon(dialog, RESOURCE_ID_GENERIC_QUESTION_LARGE); - - i18n_free_all(a_dialog); - - actionable_dialog_push(a_dialog, modal_manager_get_window_stack(ModalPriorityGeneric)); -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsSystemData *data = (SettingsSystemData *) context; - i18n_free_all(data); -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsSystemData *data = (SettingsSystemData *) context; - const char *subtitle = NULL; - PBL_ASSERTN(row < SystemMenuItem_Count); - switch (row) { - case SystemMenuItemStationaryToggle: - subtitle = stationary_get_enabled() ? i18n_get("On", data) : i18n_get("Off", data); - break; - case SystemMenuItemShutDown: - if (!prv_shutdown_enabled()) { - // XXX: For now, gray out the Shut Down item if unusable. - graphics_context_set_text_color(ctx, GColorDarkGray); - } - break; - case SystemMenuItemInformation: - case SystemMenuItemCertification: - case SystemMenuItemDebugging: - case SystemMenuItemFactoryReset: - case SystemMenuItem_Count: - break; - default: - WTF; - } - menu_cell_basic_draw(ctx, cell_layer, i18n_get(s_item_titles[row], data), subtitle, NULL); -} - -void factory_reset_select_callback(int index, void *context) { - settings_factory_reset_window_push(); -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsSystemData *data = (SettingsSystemData *) context; - - switch (row) { - case SystemMenuItemInformation: - prv_information_window_push(data); - break; - case SystemMenuItemCertification: - prv_certification_window_push(data); - break; - case SystemMenuItemStationaryToggle: - stationary_set_enabled(!stationary_get_enabled()); - break; - case SystemMenuItemShutDown: - if (prv_shutdown_enabled()) { - launcher_task_add_callback(prv_shutdown_cb, 0); - } - break; - case SystemMenuItemDebugging: - prv_debugging_interstitial_trigger(data); - break; - case SystemMenuItemFactoryReset: - settings_factory_reset_window_push(); - break; - default: - WTF; - } - settings_menu_reload_data(SettingsMenuItemSystem); -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return SystemMenuItem_Count; -} - -static Window *prv_init(void) { - SettingsSystemData *data = app_malloc_check(sizeof(SettingsSystemData)); - *data = (SettingsSystemData){}; - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - }; - - return settings_window_create(SettingsMenuItemSystem, &data->callbacks); -} - -const SettingsModuleMetadata *settings_system_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("System"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_system.h b/src/fw/apps/system_apps/settings/settings_system.h deleted file mode 100644 index a490c6130c..0000000000 --- a/src/fw/apps/system_apps/settings/settings_system.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_system_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_time.c b/src/fw/apps/system_apps/settings/settings_time.c deleted file mode 100644 index 074707bf25..0000000000 --- a/src/fw/apps/system_apps/settings/settings_time.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_menu.h" -#include "settings_option_menu.h" -#include "settings_time.h" -#include "settings_window.h" - -#include "applib/app.h" -#include "applib/applib_resource.h" -#include "applib/fonts/fonts.h" -#include "applib/graphics/text.h" -#include "applib/ui/action_menu_window_private.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/option_menu_window.h" -#include "applib/ui/time_selection_window.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "util/date.h" -#include "util/time/time.h" -#include "util/string.h" - -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timezone_database.h" -#include "shell/prefs.h" - -#include - -// 9 (TZ) continents: Africa, America, Antarctica, Asia, Atlantic, Australia, -// Europe, Indian, Pacific -#define NUM_CONTINENTS 9 - -typedef struct { - SettingsCallbacks callbacks; - - MenuLayer menu_layer; - - int hour; - bool is_morning; - - // Timezone data - uint16_t region_count; - uint16_t continent_selected; - uint16_t continent_start[NUM_CONTINENTS + 1]; //!< First region id for the continent - uint16_t continent_end[NUM_CONTINENTS]; //!< Last+1 region id for the continent - - const char **continent_names; - const char **region_names; - char *region_names_buffer; - - ActionMenuConfig action_menu; - - Window *continent_window; -} SettingsTimeData; - -typedef enum { - TimeRow_Format, - TimeRow_TimezoneSource, - TimeRow_Timezone, - TimeRowNum, -} TimeRow; - - -// Timezone Window Setup -//////////////////////////// - -static void prv_format_region_name(char *region_name) { - const size_t string_length = strnlen(region_name, TIMEZONE_NAME_LENGTH); - for (size_t i = 0; i < string_length; i++) { - if (region_name[i] == '_') { - region_name[i] = ' '; - } - } -} - -//! Initialize the continent and region names for the timezone windows -static void prv_init_continent_and_region_names(SettingsTimeData *data) { - const uint16_t region_count = data->region_count = timezone_database_get_region_count(); - char * const region_names_buffer = app_zalloc_check(region_count * TIMEZONE_NAME_LENGTH); - char *cursor = region_names_buffer; - char *last_cursor = cursor; - const char **continent_names = app_zalloc_check(NUM_CONTINENTS * sizeof(char *)); - const char **region_names = app_zalloc_check(region_count * sizeof(char *)); - uint16_t continent_index = 0; - // Iterate through the region IDs to sort out the region IDs into continents. - // We have sorted the region IDs by name, so each continent _will_ be separate from each other. - data->continent_start[continent_index] = 0; - for (uint16_t i = 0; i < region_count; i++, cursor += TIMEZONE_NAME_LENGTH) { - timezone_database_load_region_name(i, cursor); - prv_format_region_name(cursor); - // Split 'Continent/City' into the two parts - const int sep_pos = strcspn(cursor, "/"); - cursor[sep_pos] = '\0'; - // Store pointer to region name - region_names[i] = cursor + sep_pos + 1; - // If the continent is the same as the last entry, keep going as-is - if (strcmp(cursor, last_cursor) == 0) { - data->continent_end[continent_index] = i + 1; - continue; - } - // If the new continent is filtered out, don't create a new continent or update the last - // continent pointer. - if (strcmp(cursor, "Etc") == 0) { // Filter out 'Etc' fake-continent - continue; - } - // Set pointer for the continent name - continent_names[continent_index] = last_cursor; - // Set the new continent name - last_cursor = cursor; - // Set the starting region ID for the new continent. - data->continent_start[++continent_index] = i; - } - // Save the last continent name - continent_names[continent_index] = last_cursor; - - data->region_names_buffer = region_names_buffer; - data->region_names = region_names; - data->continent_names = continent_names; -} - -static char *prv_get_timezone_title(void) { - /// Title of the menu for changing the watch's timezone. - return i18n_noop("Timezone"); -} - -// Timezone Region Menu -///////////////////////// - -static void prv_region_menu_select(OptionMenu *option_menu, int selection, void *context) { - SettingsTimeData *data = ((SettingsOptionMenuData *)context)->context; - const uint16_t region_id = data->continent_start[data->continent_selected] + selection; - clock_set_timezone_by_region_id(region_id); - - const bool continent_animated = false; - app_window_stack_remove(data->continent_window, continent_animated); - const bool region_animated = true; - app_window_stack_remove(&option_menu->window, region_animated); -} - -static void prv_region_menu_push(SettingsTimeData *data) { - const char *title = prv_get_timezone_title(); - const OptionMenuCallbacks callbacks = { - .select = prv_region_menu_select, - }; - const int start_index = data->continent_start[data->continent_selected]; - const int end_index = data->continent_end[data->continent_selected]; - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, OPTION_MENU_CHOICE_NONE, &callbacks, - end_index - start_index, true /* icons_enabled */, &data->region_names[start_index], data); -} - -// Timezone Continent Menu -///////////////////////// - -static void prv_continent_menu_select(OptionMenu *option_menu, int selection, void *context) { - SettingsTimeData *data = ((SettingsOptionMenuData *)context)->context; - data->continent_selected = selection; - prv_region_menu_push(data); -} - -static void prv_continent_menu_push(SettingsTimeData *data) { - const char *title = prv_get_timezone_title(); - const OptionMenuCallbacks callbacks = { - .select = prv_continent_menu_select, - }; - OptionMenu * const continent_menu = settings_option_menu_push( - title, OptionMenuContentType_SingleLine, OPTION_MENU_CHOICE_NONE, &callbacks, NUM_CONTINENTS, - false /* icons_enabled */, data->continent_names, data); - data->continent_window = &continent_menu->window; -} - -// 24h Switch -///////////////////////// - -static void prv_cycle_clock_style(void) { - clock_set_24h_style(!clock_is_24h_style()); -} - -static void prv_cycle_clock_timezone_source(void) { - clock_set_manual_timezone_source(!clock_timezone_source_is_manual()); - - if (!clock_timezone_source_is_manual()) { - clock_set_timezone_by_region_id(shell_prefs_get_automatic_timezone_id()); - } -} - -// Date & Time Menu -//////////////////////////// -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsTimeData *data = (SettingsTimeData*) context; - switch (row) { - case TimeRow_Format: - // Set Time Display - prv_cycle_clock_style(); - break; - case TimeRow_TimezoneSource: - // Time settings (automatic / manual) - prv_cycle_clock_timezone_source(); - break; - case TimeRow_Timezone: - // Set Timezone Region - PBL_ASSERTN(clock_timezone_source_is_manual()); - prv_continent_menu_push(data); - break; - } - settings_menu_mark_dirty(SettingsMenuItemDateTime); -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsTimeData *data = (SettingsTimeData*) context; - - const char *title = NULL; - const char *subtitle = NULL; - char current_timezone_region[TIMEZONE_NAME_LENGTH]; - - switch (row) { - case TimeRow_Format: { - title = i18n_noop("Time Format"); - subtitle = clock_is_24h_style() ? i18n_noop("24h") : i18n_noop("12h"); - break; - } - case TimeRow_TimezoneSource: { - title = i18n_noop("Timezone Source"); - subtitle = clock_timezone_source_is_manual() ? i18n_noop("Manual") : - i18n_noop("Automatic"); - break; - } - case TimeRow_Timezone: { - title = i18n_noop("Timezone"); - clock_get_timezone_region(current_timezone_region, TIMEZONE_NAME_LENGTH); - subtitle = current_timezone_region; - break; - } - } - - menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); -} - -static void prv_selection_will_change_cb(SettingsCallbacks *context, uint16_t *new_row, - uint16_t old_row) { - if (!clock_timezone_source_is_manual() && *new_row == TimeRow_Timezone) { - *new_row = old_row; - } -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return TimeRowNum; -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsTimeData *data = (SettingsTimeData*) context; - i18n_free_all(data); - app_free(data->continent_names); - app_free(data->region_names); - app_free(data->region_names_buffer); - app_free(data); -} - -static Window *prv_init(void) { - SettingsTimeData *data = app_malloc_check(sizeof(*data)); - *data = (SettingsTimeData){}; - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - .selection_will_change = prv_selection_will_change_cb, - }; - - prv_init_continent_and_region_names(data); - - return settings_window_create(SettingsMenuItemDateTime, &data->callbacks); -} - -const SettingsModuleMetadata *settings_time_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Date & Time"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_time.h b/src/fw/apps/system_apps/settings/settings_time.h deleted file mode 100644 index 539e6a7ce9..0000000000 --- a/src/fw/apps/system_apps/settings/settings_time.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_time_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_timeline.c b/src/fw/apps/system_apps/settings/settings_timeline.c deleted file mode 100644 index 4fe0f175b5..0000000000 --- a/src/fw/apps/system_apps/settings/settings_timeline.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_timeline.h" -#include "settings_option_menu.h" -#include "settings_window.h" - -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "applib/graphics/graphics.h" -#include "applib/ui/menu_layer.h" -#include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" -#include "shell/prefs.h" -#include "system/passert.h" -#include "util/size.h" - -#if CAPABILITY_HAS_TIMELINE_PEEK -typedef enum TimelineSettingsVersion { - //! Initial version or never opened - TimelineSettingsVersion_InitialVersion = 0, - //! 4.0 UX with Timeline Quick View (code named Peek) - TimelineSettingsVersion_UX4WithQuickView = 1, - - TimelineSettingsVersionCount, - //! TimelineSettingsVersion is an increasing version number. TimelineSettingsVersionCurrent must - //! not decrement. This should ensure that the current version is always the latest. - TimelineSettingsVersionCurrent = TimelineSettingsVersionCount - 1, -} TimelineSettingsVersion; - -typedef struct SettingsTimelinePeekData { - SettingsCallbacks callbacks; - GFont info_font; -} SettingsTimelinePeekData; - -typedef enum TimelinePeekMenuIndex { - TimelinePeekMenuIndex_Toggle, - TimelinePeekMenuIndex_Timing, - - TimelinePeekMenuIndexCount, - TimelinePeekMenuIndexEnabledCount = TimelinePeekMenuIndexCount, - TimelinePeekMenuIndexDisabledCount = (TimelinePeekMenuIndex_Toggle + 1), -} TimelinePeekMenuIndex; - -typedef enum PeekBeforeTimingMenuIndex { - PeekBeforeTimingMenuIndex_StartTime, - PeekBeforeTimingMenuIndex_5Min, - PeekBeforeTimingMenuIndex_10Min, - PeekBeforeTimingMenuIndex_15Min, - PeekBeforeTimingMenuIndex_30Min, - - PeekBeforeTimingMenuIndexCount, - PeekBeforeTimingMenuIndexDefault = PeekBeforeTimingMenuIndex_10Min, -} PeekBeforeTimingMenuIndex; - -static const char *s_before_time_strings[PeekBeforeTimingMenuIndexCount] = { - /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. - i18n_noop("Start Time"), - /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. - i18n_noop("5 Min Before"), - /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. - i18n_noop("10 Min Before"), - /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. - i18n_noop("15 Min Before"), - /// Shows up in the Timeline settings as a "Timing" subtitle and submenu option. - i18n_noop("30 Min Before"), -}; - -static uint16_t s_before_time_values[PeekBeforeTimingMenuIndexCount] = { - 0, 5, 10, 15, 30, -}; - -static PeekBeforeTimingMenuIndex prv_before_time_min_to_index(unsigned int before_time_m) { - if (before_time_m == 0) { - return PeekBeforeTimingMenuIndex_StartTime; - } else if (before_time_m <= 5) { - return PeekBeforeTimingMenuIndex_5Min; - } else if (before_time_m <= 10) { - return PeekBeforeTimingMenuIndex_10Min; - } else if (before_time_m <= 15) { - return PeekBeforeTimingMenuIndex_15Min; - } else if (before_time_m <= 30) { - return PeekBeforeTimingMenuIndex_30Min; - } - return PeekBeforeTimingMenuIndexDefault; -} - -static void prv_before_time_menu_select(OptionMenu *option_menu, int selection, void *context) { - timeline_peek_prefs_set_before_time(s_before_time_values[selection]); - app_window_stack_remove(&option_menu->window, true /* animated */); -} - -static void prv_push_before_time_menu(SettingsTimelinePeekData *data) { - /// Shows up in the Timeline settings as the title for the "Timing" submenu window. - const char *title = i18n_noop("Timing"); - const int selected = prv_before_time_min_to_index(timeline_peek_prefs_get_before_time()); - const OptionMenuCallbacks callbacks = { - .select = prv_before_time_menu_select, - }; - settings_option_menu_push( - title, OptionMenuContentType_SingleLine, selected, &callbacks, - ARRAY_LENGTH(s_before_time_strings), true /* icons_enabled */, s_before_time_strings, data); -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - i18n_free_all(context); - app_free(context); -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return timeline_peek_prefs_get_enabled() ? TimelinePeekMenuIndexEnabledCount : - TimelinePeekMenuIndexDisabledCount; -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsTimelinePeekData *data = (SettingsTimelinePeekData *)context; - const char *title = NULL; - const char *subtitle = NULL; - - switch ((TimelinePeekMenuIndex)row) { - case TimelinePeekMenuIndex_Toggle: - /// Shows up in the Timeline settings as a toggle-able "Quick View" item. - title = i18n_noop("Quick View"); - /// Shows up in the Timeline settings as the status under the "Quick View" toggle. - subtitle = timeline_peek_prefs_get_enabled() ? i18n_noop("On") : - /// Shows up in the Timeline settings as the status under the "Quick View" toggle. - i18n_noop("Off"); - break; - case TimelinePeekMenuIndex_Timing: - /// Shows up in the Timeline settings as the title for the menu item that controls the - /// timing for when to begin showing the peek for an event. - title = i18n_noop("Timing"); - subtitle = s_before_time_strings[ - prv_before_time_min_to_index(timeline_peek_prefs_get_before_time())]; - break; - case TimelinePeekMenuIndexCount: - break; - } - - PBL_ASSERTN(title); - menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - SettingsTimelinePeekData *data = (SettingsTimelinePeekData *)context; - switch ((TimelinePeekMenuIndex)row) { - case TimelinePeekMenuIndex_Toggle: - timeline_peek_prefs_set_enabled(!timeline_peek_prefs_get_enabled()); - goto done; - case TimelinePeekMenuIndex_Timing: - prv_push_before_time_menu(data); - goto done; - case TimelinePeekMenuIndexCount: - break; - } - WTF; -done: - settings_menu_reload_data(SettingsMenuItemTimeline); -} - -static Window *prv_create_settings_window(void) { - SettingsTimelinePeekData *data = app_malloc_check(sizeof(*data)); - - *data = (SettingsTimelinePeekData) { - .callbacks = { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - }, - .info_font = fonts_get_system_font(FONT_KEY_GOTHIC_18), - }; - - return settings_window_create(SettingsMenuItemTimeline, &data->callbacks); -} - -static void prv_push_settings_window(ClickRecognizerRef recognizer, void *context) { - PBL_ASSERTN(context); - expandable_dialog_pop(context); - Window *window = prv_create_settings_window(); - app_window_stack_push(window, true /* animated */); -} - -static Window *prv_create_first_use_dialog(void) { - const void *i18n_owner = prv_create_first_use_dialog; // Use this function as the i18n owner - /// Title for the Timeline Quick View first use dialog. - const char *header = i18n_get("Quick View", i18n_owner); - /// Help text for the Timeline Quick View first use dialog. - const char *text = i18n_get("Appears on your watchface when an event is about to start.", - i18n_owner); - ExpandableDialog *expandable_dialog = expandable_dialog_create_with_params( - WINDOW_NAME("Timeline Quick View First Use"), RESOURCE_ID_SUNNY_DAY_TINY, text, GColorBlack, - PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite), NULL, RESOURCE_ID_ACTION_BAR_ICON_CHECK, - prv_push_settings_window); - expandable_dialog_set_header(expandable_dialog, header); -#if PBL_ROUND - expandable_dialog_set_header_font(expandable_dialog, - fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); -#endif - i18n_free_all(i18n_owner); - return &expandable_dialog->dialog.window; -} - -static Window *prv_init(void) { - const uint32_t version = timeline_prefs_get_settings_opened(); - timeline_prefs_set_settings_opened(TimelineSettingsVersionCurrent); - if (version == TimelineSettingsVersion_InitialVersion) { - return prv_create_first_use_dialog(); - } else { - return prv_create_settings_window(); - } -} - -const SettingsModuleMetadata *settings_timeline_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Timeline"), - .init = prv_init, - }; - - return &s_module_info; -} -#endif // CAPABILITY_HAS_TIMELINE_PEEK diff --git a/src/fw/apps/system_apps/settings/settings_timeline.h b/src/fw/apps/system_apps/settings/settings_timeline.h deleted file mode 100644 index 040d7cdac9..0000000000 --- a/src/fw/apps/system_apps/settings/settings_timeline.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_timeline_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_vibe_patterns.c b/src/fw/apps/system_apps/settings/settings_vibe_patterns.c deleted file mode 100644 index 2b9d1867e8..0000000000 --- a/src/fw/apps/system_apps/settings/settings_vibe_patterns.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_vibe_patterns.h" -#include "settings_window.h" - -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/vibes/vibe_client.h" -#include "services/normal/vibes/vibe_intensity.h" -#include "services/normal/vibes/vibe_score.h" -#include "services/normal/vibes/vibe_score_info.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/string.h" - -#include - -typedef enum VibeSettingsRow { - VibeSettingsRow_Notifications = 0, - VibeSettingsRow_PhoneCalls, - VibeSettingsRow_Alarms, - VibeSettingsRow_System, - VibeSettingsRow_Count, -} VibeSettingsRow; - -typedef struct SettingsVibePatternsData { - SettingsCallbacks callbacks; - unsigned int toggled_vibes_mask; -} SettingsVibePatternsData; - -static void prv_log_analytic_if_toggled(VibePatternFeature feature, VibeClient client, - SettingsVibePatternsData *data) { - if ((data->toggled_vibes_mask & feature) == feature) { - analytics_event_vibe_access(feature, alerts_preferences_get_vibe_score_for_client(client)); - } -} - -static void prv_deinit_cb(SettingsCallbacks *context) { - SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; - i18n_free_all(data); - prv_log_analytic_if_toggled(VibePatternFeature_Notifications, VibeClient_Notifications, data); - prv_log_analytic_if_toggled(VibePatternFeature_PhoneCalls, VibeClient_PhoneCalls, data); - prv_log_analytic_if_toggled(VibePatternFeature_Alarms, VibeClient_Alarms, data); - app_free(data); -} - -static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, - const Layer *cell_layer, uint16_t row, bool selected) { - SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; - - const char *title = NULL; - const char *subtitle = NULL; - - VibeClient client = VibeClient_Notifications; - switch (row) { - case VibeSettingsRow_Notifications: { - title = i18n_noop("Notifications"); - client = VibeClient_Notifications; - break; - } - case VibeSettingsRow_PhoneCalls: { - title = i18n_noop("Incoming Calls"); - client = VibeClient_PhoneCalls; - break; - } - case VibeSettingsRow_Alarms: { - title = i18n_noop("Alarms"); - client = VibeClient_Alarms; - break; - } - case VibeSettingsRow_System: { - /// Refers to the class of all non-score vibes, e.g. 3rd party app vibes - title = i18n_noop("System"); - - const VibeIntensity current_system_default_vibe_intensity = vibe_intensity_get(); - subtitle = vibe_intensity_get_string_for_intensity(current_system_default_vibe_intensity); - break; - } - default: { - WTF; - } - } - - // We need to set the subtitle to the name of a vibe score if it's NULL at this point - if (!subtitle) { - subtitle = vibe_score_info_get_name(alerts_preferences_get_vibe_score_for_client(client)); - if (subtitle && IS_EMPTY_STRING(subtitle)) { - subtitle = NULL; - } - } - menu_cell_basic_draw(ctx, cell_layer, i18n_get(title, data), i18n_get(subtitle, data), NULL); -} - -static void prv_selection_changed_cb(SettingsCallbacks *context, uint16_t new_row, - uint16_t old_row) { - vibes_cancel(); - VibeScore *score; - switch (new_row) { - case VibeSettingsRow_Notifications: { - score = vibe_client_get_score(VibeClient_Notifications); - break; - } - case VibeSettingsRow_PhoneCalls: { - score = vibe_client_get_score(VibeClient_PhoneCalls); - break; - } - case VibeSettingsRow_Alarms: { - score = vibe_client_get_score(VibeClient_Alarms); - break; - } - case VibeSettingsRow_System: { - // Vibe a short pulse so the user can feel the current system default vibe intensity - vibes_short_pulse(); - // Just return because the remainder of this function only applies to vibe scores - return; - } - default: - WTF; - } - if (!score) { - PBL_LOG(LOG_LEVEL_ERROR, "Null VibeScore!"); - return; - } - vibe_score_do_vibe(score); - vibe_score_destroy(score); -} - -static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { - vibes_cancel(); - - SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; - VibeClient client; - switch (row) { - case VibeSettingsRow_Notifications: { - data->toggled_vibes_mask |= VibePatternFeature_Notifications; - client = VibeClient_Notifications; - break; - } - case VibeSettingsRow_PhoneCalls: { - data->toggled_vibes_mask |= VibePatternFeature_PhoneCalls; - client = VibeClient_PhoneCalls; - break; - } - case VibeSettingsRow_Alarms: { - data->toggled_vibes_mask |= VibePatternFeature_Alarms; - client = VibeClient_Alarms; - break; - } - case VibeSettingsRow_System: { - const VibeIntensity current_system_default_vibe_intensity = vibe_intensity_get(); - const VibeIntensity next_system_default_vibe_intensity = - vibe_intensity_cycle_next(current_system_default_vibe_intensity); - - // Set the next system default vibe intensity and vibe a short pulse so the user can feel it - vibe_intensity_set(next_system_default_vibe_intensity); - alerts_preferences_set_vibe_intensity(next_system_default_vibe_intensity); - vibes_short_pulse(); - - settings_menu_mark_dirty(SettingsMenuItemVibrations); - - // Just return because the remainder of this function only applies to vibe scores - return; - } - default: - WTF; - } - - VibeScoreId current_vibe_score = alerts_preferences_get_vibe_score_for_client(client); - VibeScoreId new_vibe_score = vibe_score_info_cycle_next(client, current_vibe_score); - alerts_preferences_set_vibe_score_for_client(client, new_vibe_score); - settings_menu_mark_dirty(SettingsMenuItemVibrations); - VibeScore *score = vibe_client_get_score(client); - if (!score) { - return; - } - vibe_score_do_vibe(score); - vibe_score_destroy(score); -} - -static uint16_t prv_num_rows_cb(SettingsCallbacks *context) { - return VibeSettingsRow_Count; -} - -static void prv_expand_cb(SettingsCallbacks *context) { - SettingsVibePatternsData *data = (SettingsVibePatternsData *)context; - - // window is visible again, remind user which vibe pattern they're on - int16_t current_row = settings_menu_get_selected_row(SettingsMenuItemVibrations); - prv_selection_changed_cb(&data->callbacks, current_row, 0); - - settings_menu_mark_dirty(SettingsMenuItemVibrations); -} - -static void prv_hide_cb(SettingsCallbacks *context) { - vibes_cancel(); -} - -static Window *prv_init(void) { - SettingsVibePatternsData *data = app_zalloc_check(sizeof(SettingsVibePatternsData)); - - data->callbacks = (SettingsCallbacks) { - .deinit = prv_deinit_cb, - .draw_row = prv_draw_row_cb, - .selection_changed = prv_selection_changed_cb, - .select_click = prv_select_click_cb, - .num_rows = prv_num_rows_cb, - .expand = prv_expand_cb, - .hide = prv_hide_cb, - }; - - return settings_window_create(SettingsMenuItemVibrations, &data->callbacks); -} - -const SettingsModuleMetadata *settings_vibe_patterns_get_info(void) { - static const SettingsModuleMetadata s_module_info = { - .name = i18n_noop("Vibrations"), - .init = prv_init, - }; - - return &s_module_info; -} diff --git a/src/fw/apps/system_apps/settings/settings_vibe_patterns.h b/src/fw/apps/system_apps/settings/settings_vibe_patterns.h deleted file mode 100644 index dfa2ed94e0..0000000000 --- a/src/fw/apps/system_apps/settings/settings_vibe_patterns.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -const SettingsModuleMetadata *settings_vibe_patterns_get_info(void); diff --git a/src/fw/apps/system_apps/settings/settings_window.c b/src/fw/apps/system_apps/settings/settings_window.c deleted file mode 100644 index 3fba48bedb..0000000000 --- a/src/fw/apps/system_apps/settings/settings_window.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings.h" -#include "settings_activity_tracker.h" -#include "settings_system.h" -#include "settings_bluetooth.h" -#include "settings_display.h" -#include "settings_menu.h" -#include "settings_notifications.h" -#include "settings_quick_launch.h" -#include "settings_remote.h" -#include "settings_time.h" -#include "settings_window.h" - -#include "applib/app.h" -#include "applib/battery_state_service.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/menu_layer.h" -#include "applib/ui/option_menu_window.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/fw_reset.h" -#include "process_management/app_manager.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/common/system_task.h" -#include "system/bootbits.h" -#include "system/passert.h" - -#include -#include - -typedef struct SettingsData { - Window window; - StatusBarLayer status_layer; - MenuLayer menu_layer; - - SettingsMenuItem current_category; //!< SettingsMenuItem_Invalid if not currently in a category. - - const char *title; - SettingsCallbacks *callbacks; - - ClickConfigProvider menu_layer_click_config; //! HACK: Used to register a back click. -} SettingsData; - - -// Filter category helpers -////////////////////////// - -static SettingsCallbacks *prv_get_current_callbacks(SettingsData *data) { - PBL_ASSERTN(data && data->callbacks); - return data->callbacks; -} - -// Menu appearance helpers -////////////////////////// - -static void prv_set_sub_menu_colors(GContext *ctx, const Layer *cell_layer, bool highlight) { - if (highlight) { - graphics_context_set_fill_color(ctx, SETTINGS_MENU_HIGHLIGHT_COLOR); - graphics_context_set_text_color(ctx, GColorWhite); - } else { - graphics_context_set_fill_color(ctx, GColorWhite); - graphics_context_set_text_color(ctx, GColorBlack); - } - graphics_fill_rect(ctx, &cell_layer->bounds); -} - -// Menu Layer Handling -////////////////////// - -static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { - SettingsData *data = context; - - const uint16_t row = cell_index->row; - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->select_click) { - callbacks->select_click(callbacks, row); - } -} - -static void prv_selection_changed_callback(MenuLayer *menu_layer, MenuIndex new_index, - MenuIndex old_index, void *context) { - SettingsData *data = context; - - const uint16_t new_row = new_index.row; - const uint16_t old_row = old_index.row; - - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->selection_changed) { - callbacks->selection_changed(callbacks, new_row, old_row); - } -} - -static void prv_selection_will_change_callback(MenuLayer *menu_layer, MenuIndex *new_index, - MenuIndex old_index, void *context) { - SettingsData *data = context; - - const uint16_t old_row = old_index.row; - - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->selection_will_change) { - callbacks->selection_will_change(callbacks, &new_index->row, old_row); - } -} - -static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, - MenuIndex *cell_index, void *context) { - SettingsData *data = context; - - uint16_t row = cell_index->row; - const uint16_t section = cell_index->section; - PBL_ASSERTN(section < SettingsMenuItem_Count); - - bool highlight = menu_cell_layer_is_highlighted(cell_layer); - - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - prv_set_sub_menu_colors(ctx, cell_layer, highlight); - if (callbacks->draw_row) { - const bool is_selected = menu_cell_layer_is_highlighted(cell_layer); - callbacks->draw_row(callbacks, ctx, cell_layer, row, is_selected); - } -} - -static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, - uint16_t section_index, void *context) { - PBL_ASSERTN(section_index < SettingsMenuItem_Count); - SettingsData *data = context; - - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - return callbacks->num_rows ? callbacks->num_rows(callbacks) : (uint16_t)0; -} - -static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, - MenuIndex *cell_index, void *context) { - PBL_ASSERTN(cell_index->section < SettingsMenuItem_Count); - SettingsData *data = context; - - const uint16_t row = cell_index->row; - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - const bool is_selected = menu_layer_is_index_selected(menu_layer, cell_index); - return (callbacks->row_height) ? - callbacks->row_height(callbacks, row, is_selected) : - PBL_IF_RECT_ELSE(menu_cell_basic_cell_height(), - (is_selected ? MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT : - MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT)); -} - -// Settings Window: -//////////////////////// - -static void prv_settings_window_load(Window *window) { - SettingsData *data = window_get_user_data(window); - - StatusBarLayer *status_layer = &data->status_layer; - status_bar_layer_init(status_layer); - const char *title = settings_menu_get_status_name(data->current_category); - status_bar_layer_set_title(status_layer, i18n_get(title, data), false, false); - status_bar_layer_set_colors(status_layer, GColorWhite, GColorBlack); - status_bar_layer_set_separator_mode(status_layer, OPTION_MENU_STATUS_SEPARATOR_MODE); - layer_add_child(&data->window.layer, status_bar_layer_get_layer(status_layer)); - - GRect bounds = grect_inset(data->window.layer.bounds, (GEdgeInsets) { - .top = STATUS_BAR_LAYER_HEIGHT, - .bottom = PBL_IF_RECT_ELSE(0, STATUS_BAR_LAYER_HEIGHT), - }); - - // Create the menu - MenuLayer *menu_layer = &data->menu_layer; - menu_layer_init(menu_layer, &bounds); - menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) { - .get_num_rows = prv_get_num_rows_callback, - .get_cell_height = prv_get_cell_height_callback, - .draw_row = prv_draw_row_callback, - .select_click = prv_select_callback, - .selection_changed = prv_selection_changed_callback, - .selection_will_change = prv_selection_will_change_callback, - }); - menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack); - menu_layer_set_highlight_colors(menu_layer, SETTINGS_MENU_HIGHLIGHT_COLOR, GColorWhite); - menu_layer_set_click_config_onto_window(menu_layer, &data->window); - layer_add_child(&data->window.layer, menu_layer_get_layer(menu_layer)); - - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->get_initial_selection) { - const uint16_t selected_row = callbacks->get_initial_selection(data->callbacks); - menu_layer_set_selected_index(menu_layer, MenuIndex(0, selected_row), MenuRowAlignCenter, - false /* animated */); - } - - if (callbacks->expand) { - callbacks->expand(data->callbacks); - } -} - -static void prv_settings_window_appear(Window *window) { - SettingsData *data = window_get_user_data(window); - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->appear) { - callbacks->appear(data->callbacks); - } -} - -static void prv_settings_window_unload(Window *window) { - SettingsData *data = window_get_user_data(window); - // Call the hide callback for the currently open category. - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->hide) { - callbacks->hide(callbacks); - } - i18n_free_all(data); - menu_layer_deinit(&data->menu_layer); - status_bar_layer_deinit(&data->status_layer); - settings_window_destroy(window); -} - -Window *settings_window_create(SettingsMenuItem category, SettingsCallbacks *callbacks) { - PBL_ASSERTN(callbacks && (category < SettingsMenuItem_Count)); - - SettingsData *data = app_zalloc_check(sizeof(*data)); - - data->current_category = category; - data->title = settings_menu_get_submodule_info(category)->name; - data->callbacks = callbacks; - - app_state_set_user_data(data); - - window_init(&data->window, WINDOW_NAME("Settings Window")); - window_set_user_data(&data->window, data); - window_set_window_handlers(&data->window, &(WindowHandlers){ - .load = prv_settings_window_load, - .appear = prv_settings_window_appear, - .unload = prv_settings_window_unload, - }); - - return &data->window; -} - -void settings_window_destroy(Window *window) { - SettingsData *data = window_get_user_data(window); - - SettingsCallbacks *callbacks = prv_get_current_callbacks(data); - if (callbacks->deinit) { - callbacks->deinit(data->callbacks); - } - - i18n_free_all(data); - app_free(data); -} - -void settings_menu_mark_dirty(SettingsMenuItem category) { - SettingsData *data = app_state_get_user_data(); - if (data->current_category == category) { - layer_mark_dirty(menu_layer_get_layer(&data->menu_layer)); - } -} - -void settings_menu_reload_data(SettingsMenuItem category) { - SettingsData *data = app_state_get_user_data(); - if (data->current_category == category) { - menu_layer_reload_data(&data->menu_layer); - } -} - -int16_t settings_menu_get_selected_row(SettingsMenuItem category) { - SettingsData *data = app_state_get_user_data(); - if (data->current_category == category) { - return menu_layer_get_selected_index(&data->menu_layer).row; - } - return 0; // say first row is selected if all else fails -} diff --git a/src/fw/apps/system_apps/settings/settings_window.h b/src/fw/apps/system_apps/settings/settings_window.h deleted file mode 100644 index c5ab1961fb..0000000000 --- a/src/fw/apps/system_apps/settings/settings_window.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_menu.h" - -#include "applib/ui/window.h" - -Window *settings_window_create(SettingsMenuItem category, SettingsCallbacks *callbacks); - -void settings_window_destroy(Window *window); diff --git a/src/fw/apps/system_apps/timeline/pin_window.h b/src/fw/apps/system_apps/timeline/pin_window.h deleted file mode 100644 index 70e7d499bb..0000000000 --- a/src/fw/apps/system_apps/timeline/pin_window.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "popups/timeline/timeline_item_layer.h" -#include "services/normal/timeline/timeline.h" - -#include "applib/ui/ui.h" - -typedef struct TimelinePinWindow TimelinePinWindow; - -struct TimelinePinWindow { - Window window; - Layer layer; //!< Used to perform a bounds animation of the window - Layer action_button_layer; - StatusBarLayer status_layer; - TimelineItemLayer item_detail_layer; - TimelineLayoutInfo info; - Animation *pop_animation; - EventServiceInfo blobdb_event_info; //!< Used for pin events when in modal window -}; - -void timeline_pin_window_set_item(TimelinePinWindow *pin_window, TimelineItem *item, - time_t current_day); - -//! Pop the timeline pin window -void timeline_pin_window_pop(TimelinePinWindow *pin_window); - -void timeline_pin_window_init(TimelinePinWindow *pin_window, TimelineItem *item, - time_t current_day); - -void timeline_pin_window_push_modal(TimelineItem *item); diff --git a/src/fw/apps/system_apps/timeline/timeline.c b/src/fw/apps/system_apps/timeline/timeline.c deleted file mode 100644 index c633c74692..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline.c +++ /dev/null @@ -1,1286 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pin_window.h" -#include "timeline.h" -#include "timeline_animations.h" -#include "timeline_model.h" - -#include "applib/app.h" -#include "applib/ui/animation_interpolate.h" -#include "applib/ui/animation_timing.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/kino/kino_reel/scale_segmented.h" -#include "applib/ui/kino/kino_reel/unfold.h" -#include "applib/ui/ui.h" -#include "drivers/rtc.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "resource/resource_ids.auto.h" -#include "resource/timeline_resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/compositor/compositor_transitions.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/actions_endpoint.h" -#include "services/normal/timeline/attribute.h" -#include "shell/normal/watchface.h" -#include "syscall/syscall.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/array.h" -#include "util/attributes.h" -#include "util/size.h" -#include "util/uuid.h" - -// This is used to determine whether this app was launched as Timeline or Timeline Past. -// See timeline_get_app_info, timeline_past_get_app_info, and the usage of sys_get_app_uuid. -// uuid: DAAE3686-BFF6-4BA5-921B-262F847BB6E8 -#define TIMELINE_PAST_UUID_INIT {0xDA, 0xAE, 0x36, 0x86, 0xBF, 0xF6, 0x4B, 0xA5, \ - 0x92, 0x1B, 0x26, 0x2F, 0x84, 0x7B, 0xB6, 0xE8} - -#if PBL_ROUND || PLATFORM_TINTIN -// Tintin looks funny with the dot animation, but it results in less code space usage -#define ANIMATION_DOT 1 -#define ANIMATION_SLIDE 0 -#else -#define ANIMATION_DOT 0 -#define ANIMATION_SLIDE 1 -#endif - -typedef struct TimelineAppStyle { - int16_t peek_offset_y; - int16_t peek_icon_offset_y; -} TimelineAppStyle; - -static const TimelineAppStyle s_style_medium = { - .peek_icon_offset_y = PEEK_LAYER_ICON_OFFSET_Y, -}; - -static const TimelineAppStyle s_style_large = { - .peek_offset_y = -7, - .peek_icon_offset_y = -16, -}; - -static const TimelineAppStyle * const s_styles[NumPreferredContentSizes] = { - [PreferredContentSizeSmall] = &s_style_medium, - [PreferredContentSizeMedium] = &s_style_medium, - [PreferredContentSizeLarge] = &s_style_large, - [PreferredContentSizeExtraLarge] = &s_style_large, -}; - -static TimelineAppData *s_app_data; - -static const uint32_t TIMELINE_SLIDE_ANIMATION_MS = 150; -static const uint32_t PEEK_SHOW_TIME_MS = 660; - - -static const TimelineAppStyle *prv_get_style(void) { - return s_styles[PreferredContentSizeDefault]; -} - -///////////////////////////////////// -// State Machine -///////////////////////////////////// - -static bool prv_can_transition_state(TimelineAppData *data, TimelineAppState next_state) { - // all non-exit states can transition to exit - if (data->state != TimelineAppStateExit && - next_state == TimelineAppStateExit) { - return true; - } - switch (data->state) { - case TimelineAppStateNone: - return (next_state == TimelineAppStatePeek || - next_state == TimelineAppStateHidePeek || - next_state == TimelineAppStateFarDayHidePeek || - next_state == TimelineAppStateNoEvents); - case TimelineAppStatePeek: - return (next_state == TimelineAppStateHidePeek); - case TimelineAppStateHidePeek: - return (next_state == TimelineAppStateStationary); - case TimelineAppStateFarDayHidePeek: - return (next_state == TimelineAppStateDaySeparator); - case TimelineAppStateStationary: - return (next_state == TimelineAppStateUpDown || - next_state == TimelineAppStatePushCard || - next_state == TimelineAppStateNoEvents || - next_state == TimelineAppStateInactive); - case TimelineAppStateUpDown: - return (next_state == TimelineAppStateUpDown || - next_state == TimelineAppStateShowDaySeparator || - next_state == TimelineAppStateStationary); - case TimelineAppStateShowDaySeparator: - return (next_state == TimelineAppStateDaySeparator); - case TimelineAppStateDaySeparator: - return (next_state == TimelineAppStateHideDaySeparator); - case TimelineAppStateHideDaySeparator: - return (next_state == TimelineAppStateStationary); - case TimelineAppStatePushCard: - return (next_state == TimelineAppStateCard || - next_state == TimelineAppStatePopCard); - case TimelineAppStateCard: - return (next_state == TimelineAppStatePopCard || - next_state == TimelineAppStateStationary); - case TimelineAppStatePopCard: - return (next_state == TimelineAppStateStationary || - next_state == TimelineAppStatePushCard); - case TimelineAppStateNoEvents: - return (next_state == TimelineAppStateInactive); - case TimelineAppStateInactive: - case TimelineAppStateExit: - return false; - default: - WTF; - } -} - -static bool prv_set_state(TimelineAppData *data, TimelineAppState next_state) { - const bool can_transition = prv_can_transition_state(data, next_state); - PBL_LOG(LOG_LEVEL_DEBUG, "state transition %d->%d valid:%d", - data->state, next_state, can_transition); - if (can_transition) { - data->state = next_state; - } - return can_transition; -} - -///////////////////////////////////// -// Exit Animation & Inactivity Timer -///////////////////////////////////// - -T_STATIC void prv_init_peek_layer(TimelineAppData *data); - -static void prv_launch_watchface(void *data) { -#ifdef SHELL_SDK - // FIXME: We don't want to show off our unfinished animations in the sdkshell - watchface_launch_default(NULL); -#else - -#if PLATFORM_TINTIN - const CompositorTransition *transition = NULL; -#else - const bool is_future = (s_app_data->timeline_model.direction == TimelineIterDirectionFuture); - const bool to_timeline = false; - const CompositorTransition *transition = PBL_IF_RECT_ELSE( - compositor_slide_transition_timeline_get(is_future, to_timeline, timeline_model_is_empty()), - compositor_dot_transition_timeline_get(is_future, to_timeline)); -#endif - - watchface_launch_default(transition); -#endif -} - -static void prv_cleanup_timer(EventedTimerID *timer) { - if (evented_timer_exists(*timer)) { - evented_timer_cancel(*timer); - *timer = EVENTED_TIMER_INVALID_ID; - } -} - -#if ANIMATION_DOT -static void prv_exit_timer_callback(void *context) { - TimelineAppData *data = context; - data->timeline_layer.animating_intro_or_exit = false; - launcher_task_add_callback(prv_launch_watchface, data); -} -#endif - -static void prv_intro_or_exit_anim_started(Animation *anim, void *context) { - TimelineAppData *data = context; - data->timeline_layer.animating_intro_or_exit = true; -} - -#if ANIMATION_DOT -static void prv_exit_anim_stopped(Animation *animation, bool finished, void *context) { - // we must use a timer to allow the last frame to render - const int exit_timeout_ms = 2 * ANIMATION_TARGET_FRAME_INTERVAL_MS; - evented_timer_register(exit_timeout_ms, false, prv_exit_timer_callback, context); -} -#endif - -//! Used for setting the animation frame source and/or destination of the peek layer. -//! If use_pin is true, the animation frame size and position will be that of the first pin icon. -//! If shift_offscreen is true, the frame will be shifted by the screen row amount in a direction -//! depending on the scroll direction. -static void prv_get_icon_animation_frame(TimelineAppData *data, GRect *icon_frame_out, - bool use_pin, bool shift_offscreen) { -#if ANIMATION_DOT - const GRect *layer_frame = &data->timeline_window.layer.frame; - *icon_frame_out = (GRect) { - .origin.x = layer_frame->origin.x + (layer_frame->size.w - UNFOLD_DOT_SIZE_PX) / 2, - .origin.y = layer_frame->origin.y + (layer_frame->size.h - UNFOLD_DOT_SIZE_PX) / 2, - .size = UNFOLD_DOT_SIZE, - }; -#elif ANIMATION_SLIDE - GRect icon_frame; - TimelineLayout *first_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); - if (first_timeline_layout && use_pin) { - GRect frame; - timeline_layer_get_layout_frame(&data->timeline_layer, TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT, - &frame); - timeline_layout_get_icon_frame(&frame, data->timeline_layer.scroll_direction, &icon_frame); - } else { - // Since there is no pin, we need the peek size, which is the large size - icon_frame = (GRect) { .size = TIMELINE_LARGE_RESOURCE_SIZE }; - grect_align(&icon_frame, &data->peek_layer.layer.frame, GAlignCenter, false); - const TimelineAppStyle *style = prv_get_style(); - icon_frame.origin.y += style->peek_icon_offset_y; - } - if (shift_offscreen) { - if (data->timeline_model.direction == TimelineIterDirectionPast) { - icon_frame.origin.y -= DISP_ROWS; - } else { - icon_frame.origin.y += DISP_ROWS; - } - } - *icon_frame_out = icon_frame; -#endif -} - -#if ANIMATION_DOT -static Animation *prv_create_peek_exit_anim(TimelineAppData *data, TimelineAppState prev_state, - uint32_t duration) { - PeekLayer *peek_layer = &data->peek_layer; - if (prev_state == TimelineAppStateNoEvents || - prev_state == TimelineAppStatePeek || - prev_state == TimelineAppStateHidePeek) { - prv_cleanup_timer(&data->intro_timer_id); - } else if (prev_state == TimelineAppStateStationary || - prev_state == TimelineAppStateUpDown) { - TimelineLayout *first_timeline_layout = - timeline_layer_get_current_layout(&data->timeline_layer); - if (!first_timeline_layout) { - return NULL; - } - - prv_init_peek_layer(data); - - GRect icon_from; - layer_get_global_frame((Layer *)&first_timeline_layout->icon_layer, &icon_from); - - peek_layer_set_icon_with_size(peek_layer, &first_timeline_layout->icon_info, - TimelineResourceSizeTiny, icon_from); - } else { - return NULL; - } - - GRect icon_to; - const bool use_pin = true; - const bool shift_offscreen = true; - prv_get_icon_animation_frame(data, &icon_to, use_pin, shift_offscreen); - - peek_layer_clear_fields(peek_layer); - peek_layer_set_scale_to(peek_layer, icon_to); - peek_layer_set_duration(peek_layer, duration); - -#if PLATFORM_TINTIN - return (Animation *)peek_layer_create_play_animation(&data->peek_layer); -#else - // Play only a section to reduce the duration to the scaling, ignoring the PDCS duration - return (Animation *)peek_layer_create_play_section_animation(&data->peek_layer, 0, duration); -#endif -} -#endif - -static Animation *prv_create_sidebar_animation(TimelineAppData *data, bool open) { - int16_t to_sidebar_width; - if (open) { - to_sidebar_width = timeline_layer_get_ideal_sidebar_width(); - } else { - const GRect *layer_frame = &data->timeline_window.layer.frame; - to_sidebar_width = layer_frame->size.w; -#if PBL_ROUND - // Use a larger width to ensure we fill the entire screen since we use a circular background - to_sidebar_width += 25; -#endif - } - return timeline_layer_create_sidebar_animation(&data->timeline_layer, to_sidebar_width); -} - -static void prv_exit(TimelineAppData *data) { - PBL_UNUSED const TimelineAppState prev_state = data->state; - if (!prv_set_state(data, TimelineAppStateExit)) { - return; - } - -#if ANIMATION_SLIDE - prv_launch_watchface(data); -#elif ANIMATION_DOT - const uint32_t duration = interpolate_moook_in_duration(); - - animation_unschedule(data->current_animation); - layer_remove_child_layers((Layer *)&data->timeline_layer); - - Animation *sidebar_slide = prv_create_sidebar_animation(data, false /* open */); - animation_set_duration(sidebar_slide, duration); - animation_set_handlers(sidebar_slide, (AnimationHandlers) { - .started = prv_intro_or_exit_anim_started, - .stopped = prv_exit_anim_stopped, - }, data); - - Animation *peek_anim = prv_create_peek_exit_anim(data, prev_state, duration); - - // Just play them at the same time - animation_schedule(sidebar_slide); - if (peek_anim) { - animation_schedule(peek_anim); - } -#endif -} - -static void prv_inactive_timer_callack(void *data) { - prv_set_state(data, TimelineAppStateInactive); - prv_exit(data); -} - -static void prv_inactive_timer_refresh(TimelineAppData *data) { - static const uint32_t INACTIVITY_TIMEOUT_MS = 30 * 1000; - s_app_data->inactive_timer_id = evented_timer_register_or_reschedule( - s_app_data->inactive_timer_id, INACTIVITY_TIMEOUT_MS, prv_inactive_timer_callack, data); -} - -///////////////////////////////////// -// Pin View -///////////////////////////////////// - -static void prv_move_timeline_layer_stopped(Animation *animation, bool finished, void *context) { - TimelineAppData *data = context; - - // reset the timeline layer - data->timeline_layer.layer.bounds.origin.x = 0; - window_set_background_color(&data->timeline_window, GColorWhite); - - if (!finished) { - return; - } - - TimelineLayout *timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); - if (!timeline_layout) { - return; - } - - // cut to the card window - app_window_stack_push(&data->pin_window.window, false); - prv_set_state(data, TimelineAppStateCard); - - TimelineIterState *state = timeline_model_get_current_state(); - Uuid app_uuid; - timeline_get_originator_id(&state->pin, &app_uuid); - analytics_event_pin_open(state->pin.header.timestamp, &app_uuid); -} - -static Animation *prv_animate_to_pin_window(TimelineAppData *data) { - Layer *layer = &data->timeline_layer.layer; - GPoint to_origin = GPoint(-layer->bounds.size.w, 0); - Animation *animation = (Animation *)property_animation_create_bounds_origin(layer, NULL, - &to_origin); - animation_set_handlers(animation, (AnimationHandlers) { - .stopped = prv_move_timeline_layer_stopped, - }, data); - animation_set_duration(animation, TIMELINE_CARD_TRANSITION_MS / 2); - animation_set_custom_interpolation(animation, interpolate_moook); - animation_schedule(animation); - return animation; -} - -static void prv_push_pin_window(TimelineAppData *data, TimelineIterState *state, bool animated) { - TimelineLayout *timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); - if (!timeline_layout) { - return; - } - - // Animation structure: - // - Scheduled simultaneously - // - Transition pin to card - // - Move timeline layer to the left - - animation_unschedule(data->current_animation); - - // initialize the pin window with the card layout - timeline_pin_window_init(&data->pin_window, &state->pin, state->current_day); - - // match the card background color - const LayoutColors *colors = layout_get_colors((LayoutLayer *)timeline_layout); - window_set_background_color(&data->timeline_window, colors->bg_color); - - // animate the card from the right -#if !PLATFORM_TINTIN - TimelineLayout *card_timeline_layout = data->pin_window.item_detail_layer.timeline_layout; - timeline_layout_transition_pin_to_card(timeline_layout, card_timeline_layout); -#endif - - // animate the timeline to the left - data->current_animation = prv_animate_to_pin_window(data); -} - -static bool prv_pin_in_card(TimelineAppData *data, Uuid *uuid) { - if (!app_window_stack_contains_window((Window *)&data->pin_window)) { - return false; - } - - TimelineIterState *current_state = timeline_model_get_current_state(); - if (current_state == NULL) { - return false; - } - - return uuid_equal(¤t_state->pin.header.id, uuid); -} - -static void prv_refresh_pin(TimelineAppData *data, int idx) { - PBL_ASSERTN(idx >= 0); - TimelineIterState *state = timeline_model_get_iter_state(idx); - timeline_iter_refresh_pin(state); - - if (idx == 0 && prv_pin_in_card(data, &state->pin.header.id)) { - timeline_pin_window_set_item(&data->pin_window, &state->pin, state->current_day); - } -} - -///////////////////////////////////// -// Timeline Controller -///////////////////////////////////// - -T_STATIC void prv_setup_no_events_peek(TimelineAppData *data); - -static void prv_update_timeline_layer(TimelineAppData *data) { - TimelineLayer *timeline_layer = &data->timeline_layer; - if (data->state != TimelineAppStateStationary && - data->state != TimelineAppStateUpDown && - data->state != TimelineAppStateCard) { - return; - } - animation_unschedule(data->current_animation); - data->current_animation = NULL; - timeline_layer_reset(timeline_layer); - - if (timeline_model_is_empty() && - prv_set_state(data, TimelineAppStateNoEvents)) { - // Hide layouts and animate to "No events" - timeline_layer_set_layouts_hidden(&data->timeline_layer, true); - - prv_init_peek_layer(data); - prv_setup_no_events_peek(data); - peek_layer_play(&data->peek_layer); - - Animation *sidebar_slide = prv_create_sidebar_animation(data, false /* open */); - data->current_animation = sidebar_slide; - animation_schedule(sidebar_slide); - } -} - -static void prv_back_click_handler(ClickRecognizerRef recognizer, void *context) { - TimelineAppData *data = context; - prv_exit(data); -} - -static void prv_up_down_stopped(Animation *animation, bool finished, void *context) { - TimelineAppData *data = context; - if (finished) { - prv_set_state(data, TimelineAppStateStationary); - } -} - -static void prv_hide_day_sep_stopped(Animation *animation, bool finished, void *context) { - TimelineAppData *data = context; - if (!finished || !prv_set_state(data, TimelineAppStateStationary)) { - return; - } - - data->current_animation = NULL; - prv_update_timeline_layer(data); - - Animation *move_animation = timeline_layer_create_up_down_animation( - &data->timeline_layer, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS / 2, - timeline_animation_interpolate_moook_second_half); - animation_set_handlers(move_animation, (AnimationHandlers) { - .stopped = prv_up_down_stopped, - }, data); - - data->current_animation = move_animation; - animation_schedule(move_animation); -} - -static void prv_hide_day_sep(void *context) { - TimelineAppData *data = context; - data->day_separator_timer_id = EVENTED_TIMER_INVALID_ID; - if (!prv_set_state(data, TimelineAppStateHideDaySeparator)) { - return; - } - - animation_unschedule(data->current_animation); - - Animation *day_sep_hide = timeline_layer_create_day_sep_hide(&data->timeline_layer); - animation_set_handlers(day_sep_hide, (AnimationHandlers){ - .stopped = prv_hide_day_sep_stopped, - }, data); - data->current_animation = day_sep_hide; - animation_schedule(day_sep_hide); -} - -static bool prv_attempt_hide_day_sep(TimelineAppData *data) { - if (data->state == TimelineAppStateDaySeparator) { - prv_cleanup_timer(&data->day_separator_timer_id); - prv_hide_day_sep(data); - return true; - } - return false; -} - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { - TimelineAppData *data = context; - - prv_attempt_hide_day_sep(data); - - if (!prv_set_state(data, TimelineAppStatePushCard)) { - return; - } - - TimelineIterState *state = timeline_model_get_current_state(); - if (state) { - const bool animated = true; - prv_push_pin_window(data, state, animated); - } -} - -static void prv_set_day_sep_timer(TimelineAppData *data) { - const int DAY_SEP_TIMEOUT_MS = 1000; - data->day_separator_timer_id = evented_timer_register(DAY_SEP_TIMEOUT_MS, - false, - prv_hide_day_sep, - data); -} - -static void prv_day_sep_show_stopped(Animation *animation, bool finished, void *context) { - TimelineAppData *data = context; - if (!finished || !prv_set_state(data, TimelineAppStateDaySeparator)) { - return; - } - - // Pins will reappear after the day separator completes hiding in `prv_hide_day_sep_stopped` - timeline_layer_set_layouts_hidden(&data->timeline_layer, true); - - prv_set_day_sep_timer(data); -} - -static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { - TimelineAppData *data = context; - - prv_inactive_timer_refresh(data); - - ButtonId button = click_recognizer_get_button_id(recognizer); - const bool next = (button == BUTTON_ID_UP) ^ - (data->timeline_model.direction == TimelineIterDirectionFuture); - - // We want to know if it was stationary before transitioning - const bool was_stationary = (data->state == TimelineAppStateStationary); - - if (data->state == TimelineAppStateNoEvents) { - if (!next) { - prv_exit(data); - } - return; // There are no events - } else if (prv_attempt_hide_day_sep(data)) { - return; // Successfully interrupted the day separator, let it hide - } else if (!prv_set_state(data, TimelineAppStateUpDown)) { - return; // Not in a state able to scroll at the moment - } - - animation_unschedule(data->current_animation); - - int new_idx; - bool has_new; - if (next) { - if (!timeline_model_iter_next(&new_idx, &has_new)) { - prv_set_state(data, TimelineAppStateStationary); - goto done; - } - if (has_new) { - timeline_layer_set_next_item(&data->timeline_layer, new_idx); - } - timeline_layer_move_data(&data->timeline_layer, 1); - } else { - if (!timeline_model_iter_prev(&new_idx, &has_new)) { - prv_exit(data); - goto done; - } - if (has_new) { - timeline_layer_set_prev_item(&data->timeline_layer, new_idx); - } - timeline_layer_move_data(&data->timeline_layer, -1); - } - - // If we interrupted a previous scroll, hasten this scroll - const bool is_hasted = !was_stationary; - const uint32_t duration = TIMELINE_UP_DOWN_ANIMATION_DURATION_MS; - const InterpolateInt64Function interpolate = is_hasted ? - timeline_animation_interpolate_moook_second_half : - timeline_animation_interpolate_moook_soft; - Animation *move_animation = timeline_layer_create_up_down_animation( - &data->timeline_layer, duration, interpolate); - - if (timeline_layer_should_animate_day_separator(&data->timeline_layer) && - prv_set_state(data, TimelineAppStateShowDaySeparator)) { - Animation *day_sep_show = timeline_layer_create_day_sep_show(&data->timeline_layer); - move_animation = animation_spawn_create(move_animation, day_sep_show, NULL); - animation_set_handlers(move_animation, (AnimationHandlers) { - .stopped = prv_day_sep_show_stopped, - }, data); - } else { - animation_set_handlers(move_animation, (AnimationHandlers) { - .stopped = prv_up_down_stopped, - }, data); - } - - data->current_animation = move_animation; - animation_schedule(move_animation); - -done: - if (data->timeline_model.direction == TimelineIterDirectionPast) { - analytics_inc(ANALYTICS_DEVICE_METRIC_TIMELINE_PAST_NAVIGATION_COUNT, AnalyticsClient_System); - } else { - analytics_inc(ANALYTICS_DEVICE_METRIC_TIMELINE_FUTURE_NAVIGATION_COUNT, AnalyticsClient_System); - } -} - -static void prv_click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_BACK, prv_back_click_handler); - window_single_click_subscribe(BUTTON_ID_UP, prv_up_down_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_up_down_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); -} - -static void prv_blobdb_event_handler(PebbleEvent *event, void *context) { - TimelineAppData *data = context; - PebbleBlobDBEvent *blobdb_event = &event->blob_db; - if (blobdb_event->db_id != BlobDBIdPins) { - // we only care about pins - return; - } - - BlobDBEventType type = blobdb_event->type; - Uuid *id = (Uuid *)blobdb_event->key; - if (type == BlobDBEventTypeDelete) { - if (prv_pin_in_card(data, id)) { - // remove the pin window if we just removed the pin - app_window_stack_remove((Window *)&data->pin_window, false); - prv_set_state(data, TimelineAppStateStationary); - } - timeline_model_remove(id); - prv_update_timeline_layer(data); - } else if (type == BlobDBEventTypeInsert) { - for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - if (timeline_model_get_iter_state(i)->node && - uuid_equal(&timeline_model_get_iter_state(i)->pin.header.id, id)) { - prv_refresh_pin(data, i); - } - } - prv_update_timeline_layer(data); - } -} - -///////////////////////////////////// -// Intro Animation -///////////////////////////////////// - -static void prv_intro_anim_stopped(Animation *anim, bool finished, void *context) { - TimelineAppData *data = context; - i18n_free_all(&data->peek_layer); - peek_layer_deinit(&data->peek_layer); - window_set_click_config_provider_with_context(&data->timeline_window, prv_click_config_provider, - data); - data->timeline_layer.animating_intro_or_exit = false; - - if (!finished || (!prv_set_state(s_app_data, TimelineAppStateStationary) && - !prv_set_state(s_app_data, TimelineAppStateDaySeparator))) { - return; - } - - data->current_animation = NULL; - prv_update_timeline_layer(data); - - if (data->state == TimelineAppStateDaySeparator) { - // Hidden until the day separator hide animation stops - timeline_layer_set_layouts_hidden(&data->timeline_layer, true); -#if ANIMATION_DOT - timeline_layer_unfold_day_sep(&data->timeline_layer); -#elif ANIMATION_SLIDE - timeline_layer_slide_day_sep(&data->timeline_layer); -#endif - prv_set_day_sep_timer(data); - } else { -#if CAPABILITY_HAS_TIMELINE_PEEK - GPoint direction = GPoint(0, -1); -#else - GPoint direction = GPoint(1, 0); -#endif - Animation *layer_bounce = timeline_layer_create_bounce_back_animation(&data->timeline_layer, - direction); - data->current_animation = layer_bounce; - animation_schedule(layer_bounce); - } -} - -static Animation *prv_create_intro_animation(TimelineAppData *data, uint32_t duration, - bool was_mini_peek) { - // Animation structure: - // - Scheduled simultaneously - // - Spawn - // - Move peek layer to right (frame) - // - Resize sidebar from fullscreen to thin - // - After completion - // - Bounce back timeline pin layouts - // - Speed lines (if launching into a deep pin) - - // animate the peek layer to the right - GRect *start = &data->peek_layer.layer.frame; - GRect stop = { { was_mini_peek ? 0 : start->size.w , 0 }, start->size }; - Animation *peek_out = (Animation *)property_animation_create_layer_frame( - (Layer *)&data->peek_layer, start, &stop); - animation_set_duration(peek_out, duration); - animation_set_custom_interpolation(peek_out, interpolate_moook_in_only); - - // resize the sidebar from fullscreen to become thin on the right - Animation *sidebar_slide = prv_create_sidebar_animation(data, true /* open */); - animation_set_duration(sidebar_slide, duration); - - Animation *speed_lines = data->launch_into_deep_pin ? - timeline_layer_create_speed_lines_animation(&data->timeline_layer) : NULL; - - return animation_spawn_create(peek_out, sidebar_slide, speed_lines, NULL); -} - -static void prv_play_peek_in(TimelineAppData *data) { - // Skip the first frame since the icon is offscreen - const int num_frames_skip = 1; - // The peek layer scale animation has a bounce back effect, so the icon reaches the destination - // if set to exactly the short moook in duration, so extend with more frames - const int num_frames_extend = 3; - const uint32_t duration = interpolate_moook_in_duration(); - peek_layer_set_duration(&data->peek_layer, duration + ((num_frames_skip + num_frames_extend) * - ANIMATION_TARGET_FRAME_INTERVAL_MS)); - Animation *animation = (Animation *)peek_layer_create_play_animation(&data->peek_layer); - animation_schedule(animation); - animation_set_elapsed(animation, num_frames_skip * ANIMATION_TARGET_FRAME_INTERVAL_MS); -} - -static void prv_scale_peek_to_first_pin_icon(TimelineAppData *data, uint32_t duration, - bool was_mini_peek) { - TimelineLayout *first_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); - if (!first_timeline_layout) { - return; - } - - // scale the peek layer icon to the pin position - GRect frame; - layer_get_global_frame((Layer *)first_timeline_layout, &frame); - GRect icon_to; - timeline_layout_get_icon_frame(&frame, data->timeline_layer.scroll_direction, &icon_to); - const bool align_in_frame = true; - PeekLayer *peek_layer = &data->peek_layer; - peek_layer_set_scale_to_image(peek_layer, &first_timeline_layout->icon_info, - TimelineResourceSizeTiny, icon_to, align_in_frame); - -#if ANIMATION_SLIDE - if (was_mini_peek) { - prv_play_peek_in(data); - return; - } -#endif - - peek_layer_set_duration(peek_layer, duration); - peek_layer_play(peek_layer); -} - -static void prv_intro_timer_callback(void *context) { - TimelineAppData *data = context; - data->intro_timer_id = EVENTED_TIMER_INVALID_ID; - - // if we are already hiding the peek, we were in a mini peek - const bool was_mini_peek = (data->state == TimelineAppStateHidePeek); - - prv_set_state(data, TimelineAppStateHidePeek); - - if (data->state != TimelineAppStateHidePeek && - data->state != TimelineAppStateFarDayHidePeek) { - return; - } - - // hide the peek text - PeekLayer *peek_layer = &data->peek_layer; - peek_layer_clear_fields(peek_layer); - - animation_unschedule(data->current_animation); - - const uint32_t duration = was_mini_peek ? interpolate_moook_in_duration() : - TIMELINE_SLIDE_ANIMATION_MS; - Animation *intro = prv_create_intro_animation(data, duration, was_mini_peek); - animation_set_handlers(intro, (AnimationHandlers) { - .started = prv_intro_or_exit_anim_started, - .stopped = prv_intro_anim_stopped, - }, data); - - data->current_animation = intro; - animation_schedule(intro); - - if (!layer_get_hidden((Layer *)&data->peek_layer)) { - prv_scale_peek_to_first_pin_icon(data, duration, was_mini_peek); - } -} - -static void prv_open_did_focus_handler(PebbleEvent *e, void *context) { - TimelineAppData *data = context; - event_service_client_unsubscribe(&data->focus_event_info); - prv_intro_timer_callback(data); -} - -static void prv_peek_did_focus_handler(PebbleEvent *e, void *context) { - TimelineAppData *data = context; - event_service_client_unsubscribe(&data->focus_event_info); - -#if ANIMATION_DOT - peek_layer_play(&data->peek_layer); -#elif ANIMATION_SLIDE - prv_play_peek_in(data); -#endif - - if (data->state == TimelineAppStateNoEvents) { - window_set_click_config_provider_with_context(&data->timeline_window, prv_click_config_provider, - data); - } else if (data->state == TimelineAppStatePeek && - data->intro_timer_id == EVENTED_TIMER_INVALID_ID) { - data->intro_timer_id = evented_timer_register(PEEK_SHOW_TIME_MS, - false, - prv_intro_timer_callback, - data); - } -} - -static void prv_setup_peek_animation(TimelineAppData *data, TimelineResourceInfo *timeline_res, - bool use_pin) { - PeekLayer *peek_layer = &data->peek_layer; -#if ANIMATION_DOT - peek_layer_set_icon(peek_layer, timeline_res); -#elif ANIMATION_SLIDE - GRect icon_from; - GRect icon_to; - const bool shift_offscreen_from = true; - const bool shift_offscreen_to = false; - prv_get_icon_animation_frame(data, &icon_from, use_pin, shift_offscreen_from); - prv_get_icon_animation_frame(data, &icon_to, use_pin, shift_offscreen_to); - peek_layer_set_icon_with_size(peek_layer, timeline_res, TimelineResourceSizeLarge, icon_from); - peek_layer_set_scale_to(peek_layer, icon_to); - peek_layer_set_fields_hidden(peek_layer, true); -#endif -} - -T_STATIC void prv_setup_no_events_peek(TimelineAppData *data) { - PeekLayer *peek_layer = &data->peek_layer; - // set the text - peek_layer_set_fields(peek_layer, "", i18n_get("No events", peek_layer), ""); - // set the icon resource - TimelineResourceInfo timeline_res = { - .res_id = TIMELINE_RESOURCE_NO_EVENTS, - }; - const bool use_pin = false; - prv_setup_peek_animation(data, &timeline_res, use_pin); -} - -static void prv_setup_first_pin_peek(TimelineAppData *data) { - TimelineIterState *state = timeline_model_get_current_state(); - // TODO: PBL-22075 Refactor Timeline Model - // timeline_model_get_current_state explicitly tries to return NULL when supposedly empty, - // but this does not seem to actually happen - if (!state) { - return; - } - - TimelineItem *first_pin = &state->pin; - if (!first_pin) { - return; - } - - TimelineLayout *first_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); - if (!first_timeline_layout) { - return; - } - - PeekLayer *peek_layer = &s_app_data->peek_layer; - -#if PLATFORM_TINTIN - const bool is_mini_peek = false; -#else - // if we are hiding the peek, we are in a mini peek - const bool is_mini_peek = (data->state == TimelineAppStateHidePeek); -#endif - - // set the text - char number_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; // "11" - char word_buffer[TIME_STRING_REQUIRED_LENGTH] = {}; // "min to" - if (!is_mini_peek) { - clock_get_event_relative_time_string( - number_buffer, sizeof(number_buffer), word_buffer, sizeof(word_buffer), - first_pin->header.timestamp, first_pin->header.duration, state->current_day, - first_pin->header.all_day); - } - peek_layer_set_fields(peek_layer, number_buffer, word_buffer, ""); - - // set the icon - if (is_mini_peek) { - GRect icon_from; - const bool shift_offscreen = true; - const bool use_pin = true; - prv_get_icon_animation_frame(data, &icon_from, use_pin, shift_offscreen); - peek_layer_set_icon_with_size(peek_layer, &first_timeline_layout->icon_info, - TimelineResourceSizeTiny, icon_from); - } else { - const bool use_pin = false; - prv_setup_peek_animation(data, &first_timeline_layout->icon_info, use_pin); - } -} - -static void NOINLINE prv_setup_peek(TimelineAppData *data) { - TimelineIterState *state = timeline_model_get_current_state(); - TimelineItem *first_pin = state ? &state->pin : NULL; - EventServiceEventHandler focus_handler = prv_open_did_focus_handler; - - // we'll only show the first pin peek if timeline peek (aka quick view) isn't enabled - time_t now = rtc_get_time(); - if (!first_pin && prv_set_state(s_app_data, TimelineAppStateNoEvents)) { - layer_set_hidden((Layer *)&data->peek_layer, false); - prv_setup_no_events_peek(data); - focus_handler = prv_peek_did_focus_handler; -#if !CAPABILITY_HAS_TIMELINE_PEEK - } else if (first_pin && (first_pin->header.timestamp + SECONDS_PER_MINUTE * - first_pin->header.duration >= now) && - (first_pin->header.timestamp - SECONDS_PER_HOUR <= now) && - prv_set_state(data, TimelineAppStatePeek)) { - // ongoing or within the hour - prv_setup_first_pin_peek(data); - focus_handler = prv_peek_did_focus_handler; -#endif - } else if (state && (state->current_day != time_util_get_midnight_of(now)) && - prv_set_state(data, TimelineAppStateFarDayHidePeek)) { - // entering into a day that isn't today, setup the day separator - layer_set_hidden((Layer *)&data->peek_layer, true); -#if ANIMATION_DOT - timeline_layer_set_day_sep_frame(&data->timeline_layer, &data->timeline_layer.layer.frame); -#elif ANIMATION_SLIDE - GRect frame; - layer_get_frame(&data->timeline_layer.day_separator.layer, &frame); - const bool is_future = (s_app_data->timeline_model.direction == TimelineIterDirectionFuture); - frame.origin.y += is_future ? DISP_ROWS : -DISP_ROWS; - timeline_layer_set_day_sep_frame(&data->timeline_layer, &frame); -#endif - focus_handler = prv_open_did_focus_handler; - } else if (prv_set_state(data, TimelineAppStateHidePeek)) { - // setup mini-peek where the icon animates directly into the pin position - prv_setup_first_pin_peek(data); - focus_handler = prv_open_did_focus_handler; - } - - // set the did_focus handler - data->focus_event_info = (EventServiceInfo) { - .type = PEBBLE_APP_DID_CHANGE_FOCUS_EVENT, - .handler = focus_handler, - .context = s_app_data, - }; - event_service_client_subscribe(&s_app_data->focus_event_info); -} - -#if PBL_COLOR -static GColor prv_get_sidebar_color(TimelineAppData *data) { - if (s_app_data->timeline_model.direction == TimelineIterDirectionPast) { - return TIMELINE_PAST_COLOR; - } else { - return TIMELINE_FUTURE_COLOR; - } -} -#endif - -T_STATIC void prv_init_peek_layer(TimelineAppData *data) { - Window *window = &data->timeline_window; - PeekLayer *peek_layer = &data->peek_layer; - const TimelineAppStyle *style = prv_get_style(); - const GRect frame = { .origin.y = style->peek_offset_y, .size = window->layer.bounds.size }; - peek_layer_init(peek_layer, &frame); - peek_layer_set_icon_offset_y(peek_layer, style->peek_icon_offset_y); - peek_layer_set_frame(peek_layer, &frame); - peek_layer_set_background_color(peek_layer, GColorClear); - layer_add_child(&window->layer, &peek_layer->layer); -} - -static void prv_timeline_window_load(Window *window) { - TimelineLayer *timeline_layer = &s_app_data->timeline_layer; - TimelineScrollDirection scroll_direction; - if (s_app_data->timeline_model.direction == TimelineIterDirectionPast) { - scroll_direction = TimelineScrollDirectionUp; - } else { - scroll_direction = TimelineScrollDirectionDown; - } - - window_set_background_color(window, GColorWhite); - - // timeline layer - timeline_layer_init(timeline_layer, &window->layer.bounds, scroll_direction); - timeline_layer_set_sidebar_color(timeline_layer, - PBL_IF_COLOR_ELSE(prv_get_sidebar_color(s_app_data), - GColorLightGray)); - timeline_layer_set_layouts_hidden(timeline_layer, true); // hide until the peek is over - layer_set_hidden((Layer *)&timeline_layer->day_separator, true); - layer_add_child(&window->layer, (Layer *)timeline_layer); - - // peek layer - prv_init_peek_layer(s_app_data); - prv_setup_peek(s_app_data); -} - -static void prv_timeline_window_appear(Window *window) { - TimelineAppData *data = window_get_user_data(window); - - // re-enable the inactivity timer back in timeline view - prv_inactive_timer_refresh(data); -} - -static void prv_timeline_window_disappear(Window *window) { - TimelineAppData *data = window_get_user_data(window); - - // disable the inactivity timer when the user leaves - prv_cleanup_timer(&data->inactive_timer_id); -} - -static void prv_timeline_window_unload(Window *window) { - TimelineAppData *data = window_get_user_data(window); - - // clean up any running animations - animation_unschedule(data->current_animation); - prv_cleanup_timer(&data->day_separator_timer_id); -} - -static void prv_back_from_card_stopped(Animation *animation, bool finished, void *context) { - TimelineAppData *data = context; - if (!finished || !prv_set_state(data, TimelineAppStateStationary)) { - return; - } - - window_set_background_color(&data->timeline_window, GColorWhite); - - data->current_animation = NULL; - prv_update_timeline_layer(data); - - Animation *layer_bounce = timeline_layer_create_bounce_back_animation(&data->timeline_layer, - GPoint(1, 0)); - - data->current_animation = layer_bounce; - animation_schedule(layer_bounce); -} - -///////////////////////////////////// -// Public API -///////////////////////////////////// - -Animation *timeline_animate_back_from_card(void) { - TimelineAppData *data = s_app_data; - PBL_ASSERTN(data); - - if (!prv_set_state(data, TimelineAppStatePopCard)) { - return NULL; - } - - // Animation structure: - // - Scheduled simultaneously - // - Transition card to pin - // - Move timeline layer from the left - // - After completion - // - Bounce back the timeline layer - // - Move pin window to the right - - animation_unschedule(data->current_animation); - - timeline_layer_set_layouts_hidden(&data->timeline_layer, true); - window_set_background_color(&data->timeline_window, GColorWhite); - - -#if !PLATFORM_TINTIN - TimelineLayout *pin_timeline_layout = timeline_layer_get_current_layout(&data->timeline_layer); - if (pin_timeline_layout) { - // animation the pin icon - TimelineItemLayer *item_layer = &data->pin_window.item_detail_layer; - timeline_layout_transition_card_to_pin(item_layer->timeline_layout, pin_timeline_layout); - } -#endif - - // animate the timeline layer from the left - Layer *layer = &data->timeline_layer.layer; - GPoint from_origin = { -layer->bounds.size.w, 0 }; - Animation *layer_in = (Animation *)property_animation_create_bounds_origin(layer, &from_origin, - &GPointZero); - animation_set_duration(layer_in, TIMELINE_CARD_TRANSITION_MS / 2); - animation_set_custom_interpolation(layer_in, interpolate_moook); - animation_set_handlers(layer_in, (AnimationHandlers) { - .stopped = prv_back_from_card_stopped, - }, data); - - data->current_animation = layer_in; - animation_schedule(layer_in); - - // animate the card layout - timeline_pin_window_pop(&data->pin_window); - - return layer_in; -} - -///////////////////////////////////// -// App boilerplate -///////////////////////////////////// - -static bool NOINLINE prv_setup_timeline_app(void) { - TimelineAppData *data = app_malloc_check(sizeof(TimelineAppData)); - s_app_data = data; - *data = (TimelineAppData){}; - - data->blobdb_event_info = (EventServiceInfo) { - .type = PEBBLE_BLOBDB_EVENT, - .handler = prv_blobdb_event_handler, - .context = data, - }; - event_service_client_subscribe(&data->blobdb_event_info); - - const TimelineArgs *args = process_manager_get_current_process_args(); - Uuid app_uuid; - sys_get_app_uuid(&app_uuid); - if (uuid_equal(&app_uuid, &(Uuid)TIMELINE_PAST_UUID_INIT)) { - data->timeline_model.direction = TimelineIterDirectionPast; - } else if (args == NULL) { - data->timeline_model.direction = TimelineIterDirectionFuture; - } else { - data->timeline_model.direction = args->direction; - } - - // check if we were asked to launch into a specific item - time_t now = rtc_get_time(); - TimelineItem pin; - bool launch_into_pin = false; - if (args && args->launch_into_pin && - !uuid_is_invalid(&args->pin_id) && - pin_db_get(&args->pin_id, &pin) == S_SUCCESS) { - launch_into_pin = true; - if (!args->stay_in_list_view) { - // Launching directly into the pin, change the direction to match - data->timeline_model.direction = - timeline_direction_for_item(&pin, data->timeline_model.timeline, now); - } - timeline_item_free_allocated_buffer(&pin); - } - - timeline_model_init(now, &data->timeline_model); - - // if we're launching into a particular item, we iterate to it now - if (launch_into_pin) { - while (!uuid_equal(&timeline_model_get_current_state()->pin.header.id, &args->pin_id)) { - data->launch_into_deep_pin = true; - if (!timeline_model_iter_next(NULL, NULL)) { - // for some reason we can't find the pin we were asked to launch into - char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&args->pin_id, uuid_buffer); - PBL_LOG(LOG_LEVEL_ERROR, "Asked to launch into pin but can't find it %s", uuid_buffer); - launch_into_pin = false; - data->launch_into_deep_pin = false; - // we couldn't find the launch pin, go back to the present - while (timeline_model_iter_prev(NULL, NULL)) {} - break; - } - } - } - - Window *window = &data->timeline_window; - window_init(window, WINDOW_NAME("Timeline")); - window_set_user_data(window, data); - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_timeline_window_load, - .appear = prv_timeline_window_appear, - .disappear = prv_timeline_window_disappear, - .unload = prv_timeline_window_unload - }); - - return (launch_into_pin && !(args && args->stay_in_list_view)); -} - -T_STATIC void NOINLINE prv_init(void) { - bool do_push_pin_window = prv_setup_timeline_app(); - - app_window_stack_push(&s_app_data->timeline_window, true /* animated */); - - if (do_push_pin_window) { - prv_push_pin_window(s_app_data, timeline_model_get_current_state(), false /* animated */); - } - -#if CAPABILITY_HAS_TIMELINE_PEEK - if (!timeline_model_is_empty()) { - timeline_layer_set_sidebar_width(&s_app_data->timeline_layer, - timeline_layer_get_ideal_sidebar_width()); - } -#endif -} - -static void NOINLINE prv_deinit(void) { - prv_cleanup_timer(&s_app_data->intro_timer_id); - prv_cleanup_timer(&s_app_data->inactive_timer_id); - - event_service_client_unsubscribe(&s_app_data->focus_event_info); - event_service_client_unsubscribe(&s_app_data->blobdb_event_info); - - timeline_layer_deinit(&s_app_data->timeline_layer); - timeline_model_deinit(); - app_free(s_app_data); -} - -static void prv_main(void) { - prv_init(); - - app_event_loop(); - - prv_deinit(); -} - -const PebbleProcessMd *timeline_get_app_info() { - static const PebbleProcessMdSystem s_app_md = { - .common = { - .main_func = prv_main, - // uuid: 79C76B48-6111-4E80-8DEB-3119EEBEF33E - .uuid = {0x79, 0xC7, 0x6B, 0x48, 0x61, 0x11, 0x4E, 0x80, - 0x8D, 0xEB, 0x31, 0x19, 0xEE, 0xBE, 0xF3, 0x3E}, - .visibility = ProcessVisibilityHidden, - }, - .name = "Timeline", - }; - return &s_app_md.common; -} - -const PebbleProcessMd *timeline_past_get_app_info() { - static const PebbleProcessMdSystem s_app_md = { - .common = { - .main_func = prv_main, - .uuid = TIMELINE_PAST_UUID_INIT, - .visibility = ProcessVisibilityQuickLaunch, - }, - /// The title of Timeline Past in Quick Launch. If the translation is too long, cut out - /// Timeline and only translate "Past". - .name = i18n_noop("Timeline Past"), - }; - return &s_app_md.common; -} diff --git a/src/fw/apps/system_apps/timeline/timeline.h b/src/fw/apps/system_apps/timeline/timeline.h deleted file mode 100644 index 6afc3e9499..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "peek_layer.h" -#include "pin_window.h" -#include "timeline_model.h" -#include "timeline_layer.h" - -#include "applib/ui/action_menu_layer.h" -#include "applib/ui/ui.h" -#include "popups/timeline/timeline_item_layer.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "services/common/evented_timer.h" -#include "services/normal/timeline/timeline.h" - -typedef enum { - TimelineAppStateNone = 0, - TimelineAppStatePeek, - TimelineAppStateHidePeek, - TimelineAppStateStationary, - TimelineAppStateUpDown, - TimelineAppStateFarDayHidePeek, - TimelineAppStateShowDaySeparator, - TimelineAppStateDaySeparator, - TimelineAppStateHideDaySeparator, - TimelineAppStatePushCard, - TimelineAppStateCard, - TimelineAppStatePopCard, - TimelineAppStateNoEvents, - TimelineAppStateInactive, - TimelineAppStateExit, -} TimelineAppState; - -typedef struct { - TimelineDirection direction; - bool launch_into_pin; //!< Launch to a pin specified by `pin_id`. - bool stay_in_list_view; //!< Whether to stay in list view or launch into the detail view. - Uuid pin_id; -} TimelineArgs; - -typedef struct { - // Windows - Window timeline_window; - TimelinePinWindow pin_window; - - // Layers - TimelineLayer timeline_layer; - PeekLayer peek_layer; - - EventServiceInfo blobdb_event_info; - EventServiceInfo focus_event_info; - - EventedTimerID inactive_timer_id; //!< To go back to watchface after inactivity - EventedTimerID intro_timer_id; //!< To perform the intro animation after a peek - EventedTimerID day_separator_timer_id; //!< To hide the day separator after a moment - - TimelineModel timeline_model; - - Animation *current_animation; - - TimelineAppState state; - - bool launch_into_deep_pin; //!< Whether we launched directly into a pin that isn't the first - bool in_pin_view; //!< Whether we're in pin view -} TimelineAppData; - -Animation *timeline_animate_back_from_card(void); - -const PebbleProcessMd *timeline_get_app_info(void); -const PebbleProcessMd *timeline_past_get_app_info(void); diff --git a/src/fw/apps/system_apps/timeline/timeline_animations.c b/src/fw/apps/system_apps/timeline/timeline_animations.c deleted file mode 100644 index 7173130e5f..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_animations.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_animations.h" - -#include "applib/ui/animation_timing.h" -#include "applib/ui/property_animation.h" -#include "applib/ui/ui.h" - -#include - -int64_t timeline_animation_interpolate_moook_soft(int32_t normalized, - int64_t from, int64_t to) { - return interpolate_moook_soft(normalized, from, to, TIMELINE_NUM_MOOOK_FRAMES_MID); -} - -int64_t timeline_animation_interpolate_moook_second_half(int32_t normalized, - int64_t from, int64_t to) { - const int32_t cut = (normalized + ANIMATION_NORMALIZED_MAX) / 2; - return timeline_animation_interpolate_moook_soft(cut, from, to); -} - -void timeline_animation_layer_stopped_cut_to_end(Animation *animation, bool finished, - void *context) { - PropertyAnimation *property_animation = context; - if (finished) { - return; - } - - GRect to; - Layer *layer; - if (property_animation_get_to_grect(property_animation, &to) && - property_animation_get_subject(property_animation, (void **)&layer)) { - layer_set_frame(layer, &to); - } -} diff --git a/src/fw/apps/system_apps/timeline/timeline_animations.h b/src/fw/apps/system_apps/timeline/timeline_animations.h deleted file mode 100644 index f0a18eeef7..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_animations.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/animation.h" - -#define TIMELINE_NUM_MOOOK_FRAMES_MID 3 -#define TIMELINE_UP_DOWN_ANIMATION_DURATION_MS \ - (interpolate_moook_soft_duration(TIMELINE_NUM_MOOOK_FRAMES_MID)) - -int64_t timeline_animation_interpolate_moook_soft(int32_t normalized, - int64_t from, int64_t to); - -int64_t timeline_animation_interpolate_moook_second_half(int32_t normalized, - int64_t from, int64_t to); - -void timeline_animation_layer_stopped_cut_to_end(Animation *animation, bool finished, - void *context); diff --git a/src/fw/apps/system_apps/timeline/timeline_common.h b/src/fw/apps/system_apps/timeline/timeline_common.h deleted file mode 100644 index fab4d9716b..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_common.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/timeline/timeline.h" - -#define TIMELINE_NUM_VISIBLE_ITEMS (2) - -#define TIMELINE_PAST_COLOR PBL_IF_COLOR_ELSE(GColorChromeYellow, GColorLightGray) -#define TIMELINE_FUTURE_COLOR GColorVividCerulean -#define TIMELINE_DOT_COLOR GColorBlack - -typedef TimelineIterDirection TimelineDirection; diff --git a/src/fw/apps/system_apps/timeline/timeline_layer.c b/src/fw/apps/system_apps/timeline/timeline_layer.c deleted file mode 100644 index 5021c111cc..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_layer.c +++ /dev/null @@ -1,985 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_animations.h" -#include "timeline_layer.h" -#include "timeline_model.h" -#include "timeline_relbar.h" - -#include "applib/fonts/fonts.h" -#include "applib/graphics/gpath.h" -#include "applib/graphics/graphics.h" -#include "applib/preferred_content_size.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/kino/kino_reel/scale_segmented.h" -#include "applib/ui/property_animation.h" -#include "applib/ui/window.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/kernel_ui.h" -#include "kernel/ui/system_icons.h" -#include "popups/timeline/peek_animations.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/layout_layer.h" -#include "services/normal/timeline/timeline_layout.h" -#include "services/normal/timeline/timeline_resources.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/size.h" -#include "util/string.h" -#include "util/struct.h" -#include "util/trig.h" - -#include -#include - -#define PAST_TOP_MARGIN_EXTRA PBL_IF_RECT_ELSE(10, 38) -#define FUTURE_TOP_MARGIN_EXTRA PBL_IF_RECT_ELSE(10, 18) - -typedef struct TimelineLayerStyle { - GSize sidebar_arrow_size; - GPoint day_sep_offset; - uint16_t sidebar_width; - int16_t fin_offset_x; - int16_t past_fin_offset_y; - int16_t future_fin_offset_y; - uint16_t past_top_margin; - uint16_t past_thin_pin_margin; - uint16_t future_top_margin; - uint16_t left_margin; - uint16_t right_margin; - int16_t icon_offset_y; - uint16_t icon_right_margin; - uint16_t fat_pin_height; - uint16_t thin_pin_height; - uint16_t day_sep_dot_diameter; - uint16_t day_sep_subtitle_margin; - int16_t past_day_sep_dot_offset_y; - int16_t future_day_sep_dot_offset_y; -} TimelineLayerStyle; - -#define MARGIN_MEDIUM PBL_IF_RECT_ELSE(4, 13) - -static const TimelineLayerStyle s_style_medium = { - .sidebar_arrow_size.w = PBL_IF_RECT_ELSE(10, 7), - .sidebar_arrow_size.h = PBL_IF_RECT_ELSE(20, 28), - .sidebar_width = PBL_IF_RECT_ELSE(30, 48), - .past_fin_offset_y = PBL_IF_ROUND_ELSE(-12, 0), - .future_fin_offset_y = PBL_IF_ROUND_ELSE(-20, 0), - .past_top_margin = PBL_IF_RECT_ELSE(10, 18), - .future_top_margin = PBL_IF_RECT_ELSE(10, 39), - .left_margin = MARGIN_MEDIUM, - .right_margin = MARGIN_MEDIUM, - .icon_right_margin = MARGIN_MEDIUM, - .fat_pin_height = 110, - // PBL-42540: This property is dependent on the screen size. The thin pin height is the - // remainder of the screen space after the fat pin. - .thin_pin_height = PBL_IF_RECT_ELSE(66, 43), - .day_sep_dot_diameter = 9, - .day_sep_offset.x = PBL_IF_ROUND_ELSE(12, 0), - .day_sep_offset.y = -12, - .day_sep_subtitle_margin = PEEK_LAYER_SUBTITLE_MARGIN, - .past_day_sep_dot_offset_y = PBL_IF_ROUND_ELSE(-17, 0), - .future_day_sep_dot_offset_y = PBL_IF_ROUND_ELSE(-13, 0), -}; - -static const TimelineLayerStyle s_style_large = { - .sidebar_arrow_size = {14, 28}, - .sidebar_width = 34, - .fin_offset_x = 4, - .future_fin_offset_y = 37, - .past_top_margin = PBL_IF_RECT_ELSE(7, 18), - .past_thin_pin_margin = 11, - .future_top_margin = PBL_IF_RECT_ELSE(7, 39), - .left_margin = 9, - .right_margin = 14, - .icon_offset_y = 3, - .icon_right_margin = 6, - .fat_pin_height = 131, - // PBL-42540: This property is dependent on the screen size. - .thin_pin_height = 88, - .day_sep_dot_diameter = 12, - .day_sep_offset.y = -21, - .past_day_sep_dot_offset_y = -16, - .future_day_sep_dot_offset_y = 16, -}; - -static const TimelineLayerStyle * const s_styles[NumPreferredContentSizes] = { - [PreferredContentSizeSmall] = &s_style_medium, - [PreferredContentSizeMedium] = &s_style_medium, - [PreferredContentSizeLarge] = &s_style_large, - [PreferredContentSizeExtraLarge] = &s_style_large, -}; - -static int16_t s_height_offsets[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER]; -static const int s_visible_items[] = {1, 2}; -static const int s_nonvisible_items[] = {0, TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1}; - -static const TimelineLayerStyle *prv_get_style(void) { - return s_styles[PreferredContentSizeDefault]; -} - -uint16_t timeline_layer_get_fat_pin_height(void) { - return prv_get_style()->fat_pin_height; -} - -uint16_t timeline_layer_get_ideal_sidebar_width(void) { - return prv_get_style()->sidebar_width; -} - -/////////////////////////////////////////////////////////// -// Drawing functions -/////////////////////////////////////////////////////////// - -static int prv_get_scroll_delta(TimelineLayer *timeline_layer) { - return (timeline_layer->scroll_direction == TimelineScrollDirectionUp ? -1 : 1); -} - -static int prv_get_index_delta(TimelineLayer *timeline_layer) { - return timeline_layer->move_delta * prv_get_scroll_delta(timeline_layer); -} - -static void prv_set_layout_hidden(TimelineLayout *layout, bool hidden) { - layer_set_hidden((Layer *)layout, hidden); - layer_set_hidden((Layer *)&layout->icon_layer, hidden); -} - -static LayoutLayerMode prv_get_mode(int index) { - switch (index) { - default: - case 1: - return LayoutLayerModePinnedFat; - case 2: - return LayoutLayerModePinnedThin; - } -} - -static void prv_get_size_for(LayoutLayerMode layout_mode, const GRect *bounds, GSize *size) { - const TimelineLayerStyle *style = prv_get_style(); - const int16_t width = bounds->size.w - (style->left_margin + style->right_margin); - switch (layout_mode) { - default: - case LayoutLayerModePinnedFat: - *size = GSize(width, style->fat_pin_height); - break; - case LayoutLayerModePinnedThin: - *size = GSize(width, style->thin_pin_height); - break; - } -} - -static void prv_get_frame(TimelineLayer *layer, int index, GRect *frame) { - const TimelineLayerStyle *style = prv_get_style(); - index = CLIP(index, 0, (int)ARRAY_LENGTH(s_height_offsets) - 1); - const GRect *bounds = &layer->layer.bounds; - frame->origin = GPoint(style->left_margin, s_height_offsets[index]); - prv_get_size_for(prv_get_mode(index), bounds, &frame->size); -} - -void timeline_layer_get_layout_frame(TimelineLayer *layer, int index, GRect *frame_out) { - prv_get_frame(layer, index, frame_out); -} - -static void prv_get_icon_frame_exact(TimelineLayer *layer, int index, GRect *icon_frame) { - const TimelineLayerStyle *style = prv_get_style(); - GRect frame; - prv_get_frame(layer, index, &frame); - frame.origin.y += style->icon_offset_y; - // Remove sidebar and apply icon margin - frame.size.w += style->right_margin - style->icon_right_margin; - timeline_layout_get_icon_frame(&frame, layer->scroll_direction, icon_frame); -} - -#if PBL_ROUND -static void prv_get_icon_frame_centered(TimelineLayer *layer, int index, GRect *icon_frame) { - const int center_index = 1; - const GRect *bounds = &((Layer *)layer)->bounds; - prv_get_icon_frame_exact(layer, center_index, icon_frame); - icon_frame->origin.y += prv_get_scroll_delta(layer) * (index - center_index) * bounds->size.h / 2; -} -#endif - -void timeline_layer_get_icon_frame(TimelineLayer *layer, int index, GRect *icon_frame) { - return PBL_IF_RECT_ELSE(prv_get_icon_frame_exact, - prv_get_icon_frame_centered)(layer, index, icon_frame); -} - -static void prv_get_end_of_timeline_frame(TimelineLayer *layer, int index, GRect *frame) { - prv_get_frame(layer, index, frame); - const bool is_future = (layer->scroll_direction == TimelineScrollDirectionDown); - const TimelineLayerStyle *style = prv_get_style(); - gpoint_add_eq(&frame->origin, - GPoint(style->fin_offset_x, - is_future ? style->future_fin_offset_y : style->past_fin_offset_y)); - frame->size.w -= PBL_IF_RECT_ELSE(style->sidebar_width, 0); -} - -static void prv_get_day_sep_frame(TimelineLayer *layer, int index, GRect *frame) { - prv_get_frame(layer, index, frame); - const bool is_future = (layer->scroll_direction == TimelineScrollDirectionDown); - const TimelineLayerStyle *style = prv_get_style(); - frame->origin.y += is_future ? style->future_day_sep_dot_offset_y : - style->past_day_sep_dot_offset_y; - // Remove the built-in margins and subtract the sidebar - frame->origin.x -= style->left_margin; - frame->size.w += ((style->left_margin + style->right_margin) - - PBL_IF_RECT_ELSE(style->sidebar_width, 0)); -} - -static void prv_get_day_sep_show_frame(TimelineLayer *layer, GRect *frame) { - const GRect *bounds = &((Layer *)layer)->bounds; - const TimelineLayerStyle *style = prv_get_style(); - *frame = (GRect) { - .origin = gpoint_add(bounds->origin, style->day_sep_offset), - .size.w = bounds->size.w - style->sidebar_width, - .size.h = bounds->size.h, - }; -} - -static void prv_create_layout(TimelineLayer *layer, TimelineIterState *state, int index) { - TimelineItem *item = &state->pin; - TimelineLayoutInfo *info = app_malloc_check(sizeof(TimelineLayoutInfo)); - timeline_layout_init_info(info, item, state->current_day); - info->scroll_direction = layer->scroll_direction; - info->app_id = item->header.parent_id; - - GRect rect; - prv_get_frame(layer, index, &rect); - const LayoutLayerConfig config = { - .frame = &rect, - .attributes = &item->attr_list, - .mode = prv_get_mode(index), - .app_id = &item->header.parent_id, - .context = info, - }; - TimelineLayout *layout = (TimelineLayout *)layout_create(item->header.layout, &config); - layer_add_child(&layer->layouts_layer, (Layer *)layout); - GRect icon_rect; - timeline_layer_get_icon_frame(layer, index, &icon_rect); - layer_set_frame((Layer *)&layout->icon_layer, &icon_rect); - layer_add_child(&layer->layouts_layer, (Layer *)&layout->icon_layer); - layer->layouts[index] = layout; - layer->layouts_info[index] = info; -} - -static void prv_destroy_layout(TimelineLayer *layer, int index) { - TimelineLayout *timeline_layout = layer->layouts[index]; - timeline_layout->is_being_destroyed = true; - layer_remove_from_parent((Layer *)timeline_layout); - layout_destroy((LayoutLayer *)timeline_layout); - layer->layouts[index] = NULL; - - app_free(layer->layouts_info[index]); - layer->layouts_info[index] = NULL; -} - -static void prv_destroy_nonvisible_items(TimelineLayer *layer) { - for (int i = 0; i < (int)ARRAY_LENGTH(s_nonvisible_items); i++) { - TimelineLayout *timeline_layout = layer->layouts[s_nonvisible_items[i]]; - if (timeline_layout) { - prv_destroy_layout(layer, s_nonvisible_items[i]); - layer->layouts[s_nonvisible_items[i]] = NULL; - } - } -} - -static void prv_set_layouts_to_final_position(TimelineLayer *layer) { - for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - TimelineLayout *layout = layer->layouts[s_visible_items[i]]; - if (layout) { - GRect frame; - prv_get_frame(layer, i + 1, &frame); - layer_set_frame((Layer *)layout, &frame); - } - } -} - -static void prv_hide_non_current_day_items(TimelineLayer *layer) { - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { - TimelineLayout *layout = layer->layouts[i]; - TimelineIterState *state = timeline_model_get_iter_state(i - 1); - if (layout && state->current_day != layer->current_day) { - prv_set_layout_hidden(layout, true); - } - } -} - -static void prv_reset_layouts(TimelineLayer *layer) { - int num_items = timeline_model_get_num_items(); - for (int i = 0; i < (int)ARRAY_LENGTH(s_visible_items); i++) { - if (layer->layouts[s_visible_items[i]]) { - prv_destroy_layout(layer, s_visible_items[i]); - } - TimelineIterState *state = timeline_model_get_iter_state(i); - if (!state) { - continue; - } - TimelineNode *node = state->node; - if (i < num_items && node) { - prv_create_layout(layer, state, s_visible_items[i]); - } - } -} - -static void prv_update_pins_mode(TimelineLayer *layer) { - for (int i = 0; i < (int)ARRAY_LENGTH(s_visible_items); i++) { - TimelineLayout *timeline_layout = layer->layouts[s_visible_items[i]]; - if (timeline_layout) { - LayoutLayerMode mode = prv_get_mode(i + 1); - layout_set_mode((LayoutLayer *)timeline_layout, mode); - } - } -} - -/////////////////////////////////////////////////////////// -// Drawing functions -/////////////////////////////////////////////////////////// - -// An animation that moves a layer from an initial position to its final position -static Animation *prv_create_layout_up_down_animation( - TimelineLayout *timeline_layout, int to_index, TimelineLayer *timeline_layer, - uint32_t duration, InterpolateInt64Function interpolate) { - const int from_index = to_index + prv_get_index_delta(timeline_layer); - GRect from, to, icon_from, icon_to; - prv_get_frame(timeline_layer, from_index, &from); - prv_get_frame(timeline_layer, to_index, &to); - timeline_layer_get_icon_frame(timeline_layer, from_index, &icon_from); - timeline_layer_get_icon_frame(timeline_layer, to_index, &icon_to); - layer_set_frame((Layer *)timeline_layout, &from); - return timeline_layout_create_up_down_animation(timeline_layout, &from, &to, &icon_from, &icon_to, - duration, interpolate); -} - -static Animation *prv_create_end_of_timeline_animation( - TimelineLayer *layer, int to_index, uint32_t duration, InterpolateInt64Function interpolate) { - const int from_index = to_index + prv_get_index_delta(layer); - GRect from_frame, to_frame; - prv_get_end_of_timeline_frame(layer, from_index, &from_frame); - prv_get_end_of_timeline_frame(layer, to_index, &to_frame); - PropertyAnimation *prop_animation = property_animation_create_layer_frame( - (Layer *)&layer->end_of_timeline, &from_frame, &to_frame); - Animation *animation = property_animation_get_animation(prop_animation); - animation_set_duration(animation, duration); - animation_set_custom_interpolation(animation, interpolate); - animation_set_handlers(animation, (AnimationHandlers) { - .stopped = timeline_animation_layer_stopped_cut_to_end, - }, prop_animation); - return animation; -} - -Animation *timeline_layer_create_day_sep_hide(TimelineLayer *timeline_layer) { - GRect frame; - layer_get_global_frame((Layer *)&timeline_layer->day_separator, &frame); - const TimelineLayerStyle *style = prv_get_style(); - const int16_t expanded_layer_height = - frame.size.h + 2 * (style->left_margin + style->right_margin); - // move way off screen: 2x height * (1/2 - move_delta) in integer logic - const int16_t TARGET_Y = (2 * expanded_layer_height * (1 - 2 * timeline_layer->move_delta)) / 2; - const GRect scale_to = GRect(frame.origin.x + frame.size.w / 2, // go to the center - TARGET_Y, 0, 0); - peek_layer_set_scale_to(&timeline_layer->day_separator, scale_to); - - // out anim - GRect to = timeline_layer->day_separator.layer.frame; - to.origin = GPoint(0, frame.size.h * timeline_layer->move_delta); // all the way off screen - PropertyAnimation *prop_anim = - property_animation_create_layer_frame((Layer *)&timeline_layer->day_separator, NULL, &to); - Animation *anim = property_animation_get_animation(prop_anim); - animation_set_duration(anim, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS); - animation_set_custom_interpolation(anim, timeline_animation_interpolate_moook_soft); - - peek_layer_clear_fields(&timeline_layer->day_separator); - peek_layer_play(&timeline_layer->day_separator); - - return anim; -} - -void timeline_layer_set_day_sep_frame(TimelineLayer *timeline_layer, const GRect *frame) { - layer_set_hidden((Layer *)&timeline_layer->day_separator, false); - peek_layer_set_frame(&timeline_layer->day_separator, frame); -} - -static void prv_show_day_sep(TimelineLayer *timeline_layer, bool slide) { - // update the day to show the right date - timeline_layer->current_day = timeline_model_get_current_state()->current_day; - char friendly_date[TIME_STRING_REQUIRED_LENGTH]; - char month_and_day[TIME_STRING_REQUIRED_LENGTH]; - clock_get_friendly_date(friendly_date, TIME_STRING_REQUIRED_LENGTH, timeline_layer->current_day); - clock_get_month_named_date(month_and_day, TIME_STRING_REQUIRED_LENGTH, - timeline_layer->current_day); - - GRect frame; - layer_get_global_frame((Layer *)&timeline_layer->day_separator, &frame); - const GRect icon_from = { grect_center_point(&frame), GSizeZero }; - prv_get_day_sep_show_frame(timeline_layer, &frame); - peek_layer_set_frame(&timeline_layer->day_separator, &frame); - TimelineResourceInfo timeline_res = { - .res_id = TIMELINE_RESOURCE_DAY_SEPARATOR, - }; - peek_layer_set_icon_with_size(&timeline_layer->day_separator, &timeline_res, - TimelineResourceSizeLarge, icon_from); - - if (slide) { - const bool align_in_frame = true; - frame.origin.y += PEEK_LAYER_ICON_OFFSET_Y; - peek_layer_set_scale_to_image(&timeline_layer->day_separator, &timeline_res, - TimelineResourceSizeLarge, frame, align_in_frame); - peek_layer_set_fields_hidden(&timeline_layer->day_separator, true); - } - - peek_layer_set_fields(&timeline_layer->day_separator, "", friendly_date, month_and_day); - peek_layer_play(&timeline_layer->day_separator); -} - -void timeline_layer_unfold_day_sep(TimelineLayer *timeline_layer) { - const bool slide = false; - prv_show_day_sep(timeline_layer, slide); -} - -void timeline_layer_slide_day_sep(TimelineLayer *timeline_layer) { - const bool slide = true; - prv_show_day_sep(timeline_layer, slide); -} - -static void prv_day_sep_anim_stopped(Animation *anim, bool finished, void *context) { - TimelineLayer *timeline_layer = context; - if (finished) { - timeline_layer_unfold_day_sep(timeline_layer); - } -} - -// TODO: PBL-21717 Day separator on Spalding -Animation *timeline_layer_create_day_sep_show(TimelineLayer *timeline_layer) { - GRect *from = &((Layer *)&timeline_layer->day_separator)->frame; - GRect to; - prv_get_day_sep_show_frame(timeline_layer, &to); - // Keep the x-axis values until the actual unfold to maintain alignment - to.origin.x = from->origin.x; - to.size.w = from->size.w; - - PropertyAnimation *prop_anim = - property_animation_create_layer_frame((Layer *)&timeline_layer->day_separator, from, &to); - Animation *anim = property_animation_get_animation(prop_anim); - animation_set_handlers(anim, (AnimationHandlers) { - .stopped = prv_day_sep_anim_stopped, - }, timeline_layer); - animation_set_duration(anim, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS); - animation_set_custom_interpolation(anim, timeline_animation_interpolate_moook_soft); - return anim; -} - -#define MAX_UP_DOWN_ANIMATIONS (2 * TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER + 1) - -static Animation *prv_create_up_down_animation(TimelineLayer *layer, uint32_t duration, - InterpolateInt64Function interpolate) { - Animation *animations[MAX_UP_DOWN_ANIMATIONS] = {}; - int num_animations = 0; - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { - if (layer->layouts[i]) { - animations[num_animations++] = - prv_create_layout_up_down_animation(layer->layouts[i], i, layer, duration, interpolate); - animations[num_animations++] = - (Animation *)kino_layer_create_play_section_animation( - &layer->layouts[i]->icon_layer, 0, TIMELINE_UP_DOWN_ANIMATION_DURATION_MS); - } else if (i == 2 || i == 3) { - animations[num_animations++] = - prv_create_end_of_timeline_animation(layer, i, duration, interpolate); - break; - } - } - -// TODO: PBL-21982: Only support rectangular screen for now -#if PBL_RECT - Animation *relbar_animation = - timeline_relbar_layer_create_animation(layer, duration, interpolate); - if (relbar_animation) { - animations[num_animations++] = relbar_animation; - } -#endif - - Animation *animation = animation_spawn_create_from_array(animations, num_animations); - return animation; -} - -static void prv_place_day_separator(TimelineLayer *layer) { - if (!layer_get_hidden((Layer *)&layer->day_separator)) { - // already on screen - return; - } - - GRect day_sep_frame = ((Layer *)&layer->day_separator)->frame; - // substitute the day separator for the hidden pin - TimelineLayout *prev = layer->layouts[s_nonvisible_items[0]]; - TimelineLayout *next = layer->layouts[s_visible_items[1]]; - if (prev && layer_get_hidden(&prev->layout_layer.layer)) { - prv_get_day_sep_frame(layer, 0, &day_sep_frame); - } else if (next && layer_get_hidden(&next->layout_layer.layer)) { - prv_get_day_sep_frame(layer, 2, &day_sep_frame); - } else { - // don't show the day separator - return; - } - - layer_set_frame((Layer *)&layer->day_separator, &day_sep_frame); - layer_set_hidden((Layer *)&layer->day_separator, false); -} - -static void prv_place_end_of_timeline(TimelineLayer *timeline_layer) { - const bool was_hidden = layer_get_hidden((Layer *)&timeline_layer->end_of_timeline); - const bool is_hidden = (timeline_layer_should_animate_day_separator(timeline_layer) || - timeline_layer->layouts[2]); - layer_set_hidden((Layer *)&timeline_layer->end_of_timeline, is_hidden); - GRect frame; - prv_get_end_of_timeline_frame(timeline_layer, is_hidden ? 3 : 2, &frame); - layer_set_frame((Layer *)&timeline_layer->end_of_timeline, &frame); - if (was_hidden) { - kino_layer_rewind(&timeline_layer->end_of_timeline); - } - kino_layer_play(&timeline_layer->end_of_timeline); -}; - -static void prv_up_down_stopped(Animation *animation, bool is_finished, void *context) { - TimelineLayer *layer = context; - prv_update_pins_mode(layer); - prv_set_layouts_to_final_position(layer); - prv_destroy_nonvisible_items(layer); - prv_place_day_separator(layer); - prv_place_end_of_timeline(layer); -} - -static void prv_mode_change_update(Animation *animation, AnimationProgress normalized) { - TimelineLayer *timeline_layer = animation_get_context(animation); - const AnimationProgress bounce_back_length = - (interpolate_moook_out_duration() * ANIMATION_NORMALIZED_MAX) / interpolate_moook_duration(); - if (normalized >= ANIMATION_NORMALIZED_MAX - bounce_back_length) { - prv_update_pins_mode(timeline_layer); - animation_unschedule(animation); - } -} - -Animation *timeline_layer_create_up_down_animation(TimelineLayer *layer, uint32_t duration, - InterpolateInt64Function interpolate) { - Animation *animation = prv_create_up_down_animation(layer, duration, interpolate); - - animation_set_handlers(animation, (AnimationHandlers) { - .stopped = prv_up_down_stopped, - }, layer); - - static const AnimationImplementation s_mode_change_impl = { - .update = prv_mode_change_update, - }; - - Animation *mode_change = animation_create(); - animation_set_implementation(mode_change, &s_mode_change_impl); - animation_set_handlers(mode_change, (AnimationHandlers){ 0 }, layer); - - return animation_spawn_create(animation, mode_change, NULL); -} - -#if PBL_ROUND -static void prv_draw_round_flip(GContext *ctx, const GRect *layer_bounds, const int sidebar_x) { - // Use a radius larger than the screen's radius so we don't see the top/bottom of the circle - int16_t circle_radius = DISP_COLS * 3 / 4; - const int16_t flip_overlap_region_width = layer_bounds->size.w / 5; - const GPoint bounds_center = grect_center_point(layer_bounds); - const int16_t flip_point_x = bounds_center.x - (flip_overlap_region_width / 2); - // If the origin x value is not past the flip point, draw a colored circle starting at the x pos - if (sidebar_x <= flip_point_x) { - // Don't draw the circle in the flip region overlap; we want an instantaneous jump past this - // region during the flip - const int16_t circle_left_edge_x = MIN(sidebar_x, flip_point_x); - const GPoint circle_center = GPoint(circle_left_edge_x + circle_radius, bounds_center.y); - graphics_fill_circle(ctx, circle_center, circle_radius); - } else { - // Otherwise, use fill_radial to fill the sidebar as a radial on the right side of the screen - const GPoint circle_center = GPoint(sidebar_x - circle_radius, bounds_center.y); - // Add half the final sidebar width to the radius so we see a bounce-back effect at the end - const TimelineLayerStyle *style = prv_get_style(); - circle_radius += style->sidebar_width / 2; - graphics_fill_radial_internal(ctx, circle_center, circle_radius, - layer_bounds->size.w - circle_center.x, - 0, TRIG_MAX_ANGLE); - } -} -#endif - -static void prv_update_proc(struct Layer *layer, GContext* ctx) { - TimelineLayer *timeline_layer = (TimelineLayer *)layer; - const GRect *bounds = &layer->bounds; - - graphics_context_set_fill_color(ctx, GColorWhite); - graphics_fill_rect(ctx, &(GRect) { .size = bounds->size }); - - AnimationProgress progress; - if (timeline_layer->animating_intro_or_exit && - animation_get_progress(timeline_layer->animation, &progress)) { - const GPoint offset = { PEEK_ANIMATIONS_SPEED_LINES_OFFSET_X, - interpolate_int64_linear(progress, 0, -DISP_ROWS) }; - graphics_context_set_fill_color(ctx, GColorBlack); - peek_animations_draw_timeline_speed_lines(ctx, offset); - } - - const int16_t sidebar_width = timeline_layer->sidebar_width; - const GRect sidebar_rect = GRect(bounds->size.w - sidebar_width, 0, sidebar_width, - bounds->size.h); - graphics_context_set_fill_color(ctx, timeline_layer->sidebar_color); - - // On round displays, draw the round flip effect if we're animating the intro or exit and then - // return early so we don't draw the arrow notch -#if PBL_ROUND - if (timeline_layer->animating_intro_or_exit) { - prv_draw_round_flip(ctx, bounds, sidebar_rect.origin.x); - return; - } -#endif - - graphics_fill_rect(ctx, &sidebar_rect); - int16_t arrow_base_x = bounds->size.w - sidebar_width; -#if PBL_ROUND - // Nudge the arrow's base left on round displays by one pixel - arrow_base_x -= 1; -#endif - const TimelineLayerStyle *style = prv_get_style(); - const GSize arrow_size = style->sidebar_arrow_size; - const int16_t arrow_base_center_y = PBL_IF_RECT_ELSE(16, bounds->size.h / 2); - const int16_t arrow_point_x_offset = PBL_IF_RECT_ELSE(-arrow_size.w, arrow_size.w); - GPath arrow_path = { - .num_points = 3, - .points = (GPoint[]) { { arrow_base_x, arrow_base_center_y - (arrow_size.h / 2) }, - { arrow_base_x + arrow_point_x_offset, arrow_base_center_y }, - { arrow_base_x, arrow_base_center_y + (arrow_size.h / 2) } } - }; - - if (timeline_layer->scroll_direction == TimelineScrollDirectionUp) { - // arrow is in a different position for past & future, but only on rectangular displays - gpath_move_to(&arrow_path, - PBL_IF_RECT_ELSE(GPoint(0, (style->thin_pin_height + - style->past_thin_pin_margin)), GPointZero)); - } - - graphics_context_set_antialiased(ctx, true); - const GColor arrow_fill_color = PBL_IF_RECT_ELSE(timeline_layer->sidebar_color, GColorWhite); - graphics_context_set_fill_color(ctx, arrow_fill_color); - gpath_draw_filled(ctx, &arrow_path); - graphics_context_set_antialiased(ctx, false); -} - -///////////////////////////////////////// -// Public functions -///////////////////////////////////////// - -// when we create a new next or previous item, we want it out of view so we can animate it in -void timeline_layer_set_next_item(TimelineLayer *layer, int index) { - PBL_LOG(LOG_LEVEL_DEBUG, "Setting next item with index %d", index); - TimelineIterState *iter_state = timeline_model_get_iter_state_with_timeline_idx(index); - if (!iter_state) { - return; - } - if (layer->layouts[s_nonvisible_items[1]]) { - prv_destroy_layout(layer, s_nonvisible_items[1]); - } - prv_create_layout(layer, iter_state, s_nonvisible_items[1]); -} - -void timeline_layer_set_prev_item(TimelineLayer *layer, int index) { - PBL_LOG(LOG_LEVEL_DEBUG, "Setting prev item with index %d", index); - TimelineIterState *iter_state = timeline_model_get_iter_state_with_timeline_idx(index); - if (!iter_state) { - return; - } - if (layer->layouts[s_nonvisible_items[0]]) { - prv_destroy_layout(layer, s_nonvisible_items[0]); - } - prv_create_layout(layer, iter_state, s_nonvisible_items[0]); - - if (iter_state->current_day != layer->current_day) { - // we moved back to an item from the previous day, display the day separator - // by first hiding the pin of the "last" day that is about to come in and placing it there - if (layer->layouts[0]) { - prv_set_layout_hidden(layer->layouts[0], true); - } - prv_place_day_separator(layer); - } else { - // continue to hide the day separator - layer_set_hidden((Layer *)&layer->day_separator, true); - } -} - -bool timeline_layer_should_animate_day_separator(TimelineLayer *layer) { - return !layer_get_hidden((Layer *)&layer->day_separator); -} - -void timeline_layer_move_data(TimelineLayer *layer, int delta) { - PBL_ASSERTN(delta == 1 || delta == -1); - if (delta == 1) { - if (layer->layouts[s_nonvisible_items[0]]) { - prv_destroy_layout(layer, s_nonvisible_items[0]); - } - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1; i++) { - layer->layouts[i] = layer->layouts[i + 1]; - layer->layouts_info[i] = layer->layouts_info[i + 1]; - } - layer->layouts[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1] = NULL; - layer->layouts_info[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1] = NULL; - } else if (delta == -1) { - if (layer->layouts[s_nonvisible_items[1]]) { - prv_destroy_layout(layer, s_nonvisible_items[1]); - } - for (int i = TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER - 1; i > 0; i--) { - layer->layouts[i] = layer->layouts[i - 1]; - layer->layouts_info[i] = layer->layouts_info[i - 1]; - } - layer->layouts[0] = NULL; - layer->layouts_info[0] = NULL; - } - - layer->move_delta = delta * prv_get_scroll_delta(layer); - - // hide other day's pins before the animation shows them - prv_hide_non_current_day_items(layer); -} - -TimelineLayout *timeline_layer_get_current_layout(TimelineLayer *timeline_layer) { - return timeline_layer->layouts[TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT]; -} - -void timeline_layer_reset(TimelineLayer *layer) { - // reset the animation - animation_unschedule(layer->animation); - layer->animation = NULL; - - // reset the day separator - layer_set_hidden((Layer *)&layer->day_separator, true); - - TimelineResourceInfo timeline_res = { - .res_id = TIMELINE_RESOURCE_DAY_SEPARATOR, - }; - peek_layer_set_icon(&layer->day_separator, &timeline_res); - - // reset the layouts - prv_reset_layouts(layer); - prv_destroy_nonvisible_items(layer); - timeline_layer_set_layouts_hidden(layer, false); - - // PBL-18815: Reset the current day in case the pin was deleted. This should later be animated. - const int index = TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT; - if (layer->layouts[index]) { - layer->current_day = layer->layouts[index]->info->current_day; - } - - prv_hide_non_current_day_items(layer); - prv_place_day_separator(layer); - prv_place_end_of_timeline(layer); - -// TODO: PBL-21982: Only support rectangular screen for now -#if PBL_RECT - timeline_relbar_layer_reset(layer); -#endif -} - -void timeline_layer_set_sidebar_color(TimelineLayer *timeline_layer, GColor color) { - timeline_layer->sidebar_color = color; -} - -void timeline_layer_set_sidebar_width(TimelineLayer *timeline_layer, int16_t width) { - timeline_layer->sidebar_width = width; -} - -static void prv_sidebar_setter(void *context, int16_t value) { - TimelineLayer *timeline_layer = context; - timeline_layer->sidebar_width = value; - layer_mark_dirty((Layer *)timeline_layer); -} - -static int16_t prv_sidebar_getter(void *context) { - TimelineLayer *timeline_layer = context; - return timeline_layer->sidebar_width; -} - -Animation *timeline_layer_create_sidebar_animation(TimelineLayer *timeline_layer, - int16_t to_sidebar_width) { - static const PropertyAnimationImplementation s_implementation = { - .base.update = (AnimationUpdateImplementation) property_animation_update_int16, - .accessors.setter.int16 = prv_sidebar_setter, - .accessors.getter.int16 = prv_sidebar_getter, - }; - Animation *animation = (Animation *)property_animation_create( - &s_implementation, timeline_layer, &timeline_layer->sidebar_width, &to_sidebar_width); - animation_set_duration(animation, interpolate_moook_in_duration()); - animation_set_custom_interpolation(animation, interpolate_moook_in_only); - return animation; -} - -static void prv_speed_lines_update(Animation *animation, AnimationProgress progress) {} - -Animation *timeline_layer_create_speed_lines_animation(TimelineLayer *timeline_layer) { - static const AnimationImplementation s_speed_lines_impl = { - .update = prv_speed_lines_update, - }; - Animation *animation = animation_create(); - animation_set_implementation(animation, &s_speed_lines_impl); - const unsigned int num_jump_frames = 3; - animation_set_duration(animation, num_jump_frames * ANIMATION_TARGET_FRAME_INTERVAL_MS); - timeline_layer->animation = animation; - return animation; -} - -static Animation *prv_create_bounce_back_animation(Layer *layer, const GRect *to_orig, - GPoint direction) { - GRect from = *to_orig; - GRect to = *to_orig; - gpoint_add_eq(&from.origin, GPoint(direction.x * INTERPOLATE_MOOOK_BOUNCE_BACK, - direction.y * INTERPOLATE_MOOOK_BOUNCE_BACK)); - layer_set_frame(layer, &from); - Animation *animation = (Animation *)property_animation_create_layer_frame(layer, &from, &to); - animation_set_curve(animation, AnimationCurveEaseOut); - animation_set_duration(animation, TIMELINE_LAYER_SLIDE_MS); - animation_set_handlers(animation, (AnimationHandlers) { - .stopped = timeline_animation_layer_stopped_cut_to_end, - }, animation); - return animation; -} - -Animation *timeline_layer_create_bounce_back_animation(TimelineLayer *layer, GPoint direction) { - Animation *animations[TIMELINE_NUM_VISIBLE_ITEMS + 2] = {}; - int num_animations = 0; - - for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - TimelineLayout *layout = layer->layouts[s_visible_items[i]]; - if (layout) { - GRect frame; - prv_get_frame(layer, i + 1, &frame); - animations[num_animations++] = prv_create_bounce_back_animation((Layer *)layout, &frame, - direction); - } - } - - const GRect *day_sep_from = &((Layer *)&layer->day_separator)->frame; - animations[num_animations++] = prv_create_bounce_back_animation((Layer *)&layer->day_separator, - day_sep_from, direction); - - const GRect *fin_from = &layer->end_of_timeline.layer.frame; - animations[num_animations++] = prv_create_bounce_back_animation( - &layer->end_of_timeline.layer, fin_from, direction); - - Animation *animation = animation_spawn_create_from_array(animations, num_animations); - return animation; -} - -void timeline_layer_set_layouts_hidden(TimelineLayer *layer, bool hidden) { - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { - TimelineLayout *layout = layer->layouts[i]; - if (layout) { - prv_set_layout_hidden(layout, hidden); - } - } - - layer_set_hidden((Layer *)&layer->end_of_timeline, hidden); -} - -void timeline_layer_init(TimelineLayer *layer, const GRect *frame_ref, - TimelineScrollDirection scroll_direction) { - *layer = (TimelineLayer) {}; - // timeline layer - layer_init(&layer->layer, frame_ref); - layer_set_clips(&layer->layer, false); - layer_set_update_proc(&layer->layer, prv_update_proc); - TimelineIterState *state = timeline_model_get_current_state(); - layer->current_day = NULL_SAFE_FIELD_ACCESS(state, current_day, 0); - const TimelineLayerStyle *style = prv_get_style(); - // The arrow is inverted on round, so hide it by extending the width of the sidebar - layer->sidebar_width = frame_ref->size.w + PBL_IF_ROUND_ELSE(style->sidebar_arrow_size.w, 0); - // layouts - layer->scroll_direction = scroll_direction; - layer->move_delta = prv_get_scroll_delta(layer); - if (scroll_direction == TimelineScrollDirectionUp) { - s_height_offsets[0] = (PAST_TOP_MARGIN_EXTRA + style->thin_pin_height + - (2 * style->fat_pin_height)); - s_height_offsets[1] = (style->past_top_margin + style->thin_pin_height + - style->past_thin_pin_margin); - s_height_offsets[2] = style->past_top_margin; - s_height_offsets[3] = style->past_top_margin - 2 * style->fat_pin_height; - } else { - s_height_offsets[0] = FUTURE_TOP_MARGIN_EXTRA - 2 * style->fat_pin_height; - s_height_offsets[1] = style->future_top_margin; - s_height_offsets[2] = style->future_top_margin + style->fat_pin_height; - s_height_offsets[3] = (style->future_top_margin + style->fat_pin_height + - (2 * style->fat_pin_height)); - } - // layouts layer - contains all the pin - layer_init(&layer->layouts_layer, &(GRect) { .size = frame_ref->size }); - layer_set_clips(&layer->layouts_layer, false); - layer_add_child((Layer *)layer, (Layer *)&layer->layouts_layer); - - // day separator - GRect frame; - prv_get_day_sep_show_frame(layer, &frame); - peek_layer_init(&layer->day_separator, &frame); - const GFont title_font = - system_theme_get_font_for_size(PreferredContentSizeDefault, TextStyleFont_Title); - peek_layer_set_title_font(&layer->day_separator, title_font); - const GFont subtitle_font = - system_theme_get_font_for_size(PreferredContentSizeDefault, TextStyleFont_PinSubtitle); - peek_layer_set_subtitle_font(&layer->day_separator, subtitle_font, - style->day_sep_subtitle_margin); - - TimelineResourceInfo timeline_res = { - .res_id = TIMELINE_RESOURCE_DAY_SEPARATOR, - }; - peek_layer_set_icon(&layer->day_separator, &timeline_res); - peek_layer_set_background_color(&layer->day_separator, GColorClear); - peek_layer_set_dot_diameter(&layer->day_separator, style->day_sep_dot_diameter); - layer_set_hidden((Layer *)&layer->day_separator, true); - layer_add_child((Layer *)layer, (Layer *)&layer->day_separator); - - // end-of-timeline indicator - // TODO: PBL-21716 Fin icon layout on Spalding - prv_get_end_of_timeline_frame(layer, 3, &frame); - kino_layer_init(&layer->end_of_timeline, &frame); - kino_layer_set_reel_with_resource(&layer->end_of_timeline, RESOURCE_ID_END_OF_TIMELINE); - kino_layer_set_alignment(&layer->end_of_timeline, GAlignTop); - layer_add_child((Layer *)layer, (Layer *)&layer->end_of_timeline); - - // populate the timeline with items - timeline_layer_reset(layer); - -// TODO: PBL-21982: Only support rectangular screen for now -#if PBL_RECT - // Initialize Relationship bar - timeline_relbar_layer_init(layer); -#endif -} - -void timeline_layer_deinit(TimelineLayer *layer) { - animation_unschedule_all(); - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER; i++) { - if (layer->layouts[i]) { - prv_destroy_layout(layer, i); - } - } - peek_layer_deinit(&layer->day_separator); - kino_layer_deinit(&layer->end_of_timeline); - -// TODO: PBL-21982: Only support rectangular screen for now -#if PBL_RECT - timeline_relbar_layer_deinit(layer); -#endif -} diff --git a/src/fw/apps/system_apps/timeline/timeline_layer.h b/src/fw/apps/system_apps/timeline/timeline_layer.h deleted file mode 100644 index 8d94b0012d..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_layer.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "timeline_common.h" -#include "peek_layer.h" - -#include "applib/graphics/text.h" -#include "applib/ui/animation.h" -#include "applib/ui/layer.h" -#include "services/common/evented_timer.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/item.h" -#include "services/normal/timeline/timeline_layout.h" -#include "services/normal/timeline/timeline_layout_animations.h" - -#include - -#define TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER (TIMELINE_NUM_VISIBLE_ITEMS + 2) - -#define TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT 1 - -#define TIMELINE_LAYER_TEXT_ALIGNMENT \ - PBL_IF_RECT_ELSE(GTextAlignmentLeft, GTextAlignmentRight) -#define TIMELINE_LAYER_TEXT_VERTICAL_ALIGNMENT GVerticalAlignmentTop - -#define TIMELINE_LAYER_SLIDE_MS (150) - -//! Relationship bars describe the relationship between two Timeline events as a visual. -typedef enum { - //! The two timeline events are not in the same day. - RelationshipBarTypeNone = 0, - //! There is time between the end of the first event and the start of the second event. - RelationshipBarTypeFreeTime, - //! As soon as the first event starts, the second event ends. - RelationshipBarTypeBackToBack, - //! The first event is still in progress when the second event starts. - RelationshipBarTypeOverlap, -} RelationshipBarType; - -typedef struct { - RelationshipBarType rel_bar_type; - int16_t anim_offset; -} RelationshipBar; - -typedef struct { - Layer layer; - RelationshipBar prev_rel_bar; // Used for previous relationship bar animation exit - RelationshipBar curr_rel_bar; // Used for current on-screen relationship bar animation - EventedTimerID rel_bar_timer; // Used to show bars after user stops fast scrolling - void *timeline_layer; // Necessary for the layer update proc to access the TimelineLayer -} RelationshipBarLayer; - -// The timeline layer is the view(controller, sort of) for the timeline -- it uses -// TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER layout layers, timeline layouts and view slots -// layouts[1] is the first item shown, layouts[0] and layouts[TIMELINE_NUM_VISIBLE_ITEMS + 1] -// should be NULL most of the time and are used to animate out layers - -typedef struct { - Layer layer; - Layer layouts_layer; - TimelineLayout *layouts[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER]; - TimelineLayoutInfo *layouts_info[TIMELINE_NUM_ITEMS_IN_TIMELINE_LAYER]; - TimelineScrollDirection scroll_direction; - int16_t sidebar_width; - GColor sidebar_color; - KinoLayer end_of_timeline; - PeekLayer day_separator; - time_t current_day; - // TODO: PBL-22076 Remove Timeline Layer move_delta - // It is not good to keep too much questionably long lived state in views - int move_delta; - Animation *animation; - RelationshipBarLayer relbar_layer; - bool animating_intro_or_exit; -} TimelineLayer; - -void timeline_layer_reset(TimelineLayer *layer); - -void timeline_layer_set_next_item(TimelineLayer *layer, int index); - -void timeline_layer_set_prev_item(TimelineLayer *layer, int index); - -void timeline_layer_move_data(TimelineLayer *layer, int delta); - -uint16_t timeline_layer_get_fat_pin_height(void); - -uint16_t timeline_layer_get_ideal_sidebar_width(void); - -void timeline_layer_get_layout_frame(TimelineLayer *layer, int index, GRect *frame_out); - -void timeline_layer_get_icon_frame(TimelineLayer *layer, int index, GRect *frame_out); - -Animation *timeline_layer_create_up_down_animation(TimelineLayer *layer, uint32_t duration, - InterpolateInt64Function interpolate); - -// TODO: move animation logic to timeline_animations.c -// Returns whether the move animation should animate the day separator -bool timeline_layer_should_animate_day_separator(TimelineLayer *layer); - -void timeline_layer_set_day_sep_frame(TimelineLayer *timeline_layer, const GRect *frame); - -void timeline_layer_unfold_day_sep(TimelineLayer *timeline_layer); - -void timeline_layer_slide_day_sep(TimelineLayer *timeline_layer); - -Animation *timeline_layer_create_day_sep_show(TimelineLayer *timeline_layer); - -Animation *timeline_layer_create_day_sep_hide(TimelineLayer *timeline_layer); - -//! Initialize a timeline layer -//! @param layer a pointer to the TimelineLayer to initialize -//! @param frame the frame with which to initialize the layer -//! @param scroll_direction the direction to scroll for the next item -void timeline_layer_init(TimelineLayer *layer, const GRect *frame, - TimelineScrollDirection scroll_direction); - -//! Sets the sidebar color -void timeline_layer_set_sidebar_color(TimelineLayer *timeline_layer, GColor color); - -//! Sets the sidebar width -//! @param layer Pointer to the TimelineLayer. -//! @param width Width to set the TimelineLayer sidebar to. -void timeline_layer_set_sidebar_width(TimelineLayer *timeline_layer, int16_t width); - -//! Create a sidebar animation that changes the width -//! @param timeline_layer Pointer to the TimelineLayer. -//! @param to_sidebar_width Width to animate the TimelineLayer sidebar to. -Animation *timeline_layer_create_sidebar_animation(TimelineLayer *timeline_layer, - int16_t to_sidebar_width); - -//! Creates a speed lines animation that simulates scrolling through Timeline -//! @param timeline_layer Pointer to the TimelineLayer. -Animation *timeline_layer_create_speed_lines_animation(TimelineLayer *timeline_layer); - -//! Create a bounce back animation for all layouts -//! @param layer Pointer to the TimelineLayer. -//! @param direction The direction of the bounce back as a unit vector. -Animation *timeline_layer_create_bounce_back_animation(TimelineLayer *layer, GPoint direction); - -//! Sets whether the layout layers are hidden or not -void timeline_layer_set_layouts_hidden(TimelineLayer *layer, bool hidden); - -//! Get the current timeline layout -TimelineLayout *timeline_layer_get_current_layout(TimelineLayer *timeline_layer); - -//! Deinitialize a timeline item layer. -void timeline_layer_deinit(TimelineLayer *layer); diff --git a/src/fw/apps/system_apps/timeline/timeline_model.c b/src/fw/apps/system_apps/timeline/timeline_model.c deleted file mode 100644 index 4ef12aab60..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_model.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_model.h" - -#include "system/logging.h" -#include "system/passert.h" - -//////////////////////////////////////////////// -// Timeline model circular array of iters logic -//////////////////////////////////////////////// - -static TimelineModel *s_model_data; - -static int prv_get_idx_for_timeline_idx(int timeline_idx) { - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_MODEL; i++) { - if (s_model_data->states[i].index == timeline_idx) { - return i; - } - } - return -1; -} - -static int prv_raw_to_adj_idx(int raw_idx) { - return positive_modulo(raw_idx - s_model_data->first_index, TIMELINE_NUM_ITEMS_IN_MODEL); -} - -static int prv_adj_to_raw_idx(int adj_idx) { - return positive_modulo(adj_idx + s_model_data->first_index, TIMELINE_NUM_ITEMS_IN_MODEL); -} - -TimelineIterState *timeline_model_get_iter_state_with_timeline_idx(int index) { - int raw_idx = prv_get_idx_for_timeline_idx(index); - if (raw_idx == -1) { - return NULL; - } else { - return &s_model_data->states[raw_idx]; - } -} - -int timeline_model_get_idx_for_timeline_idx(int index) { - int raw_idx = prv_get_idx_for_timeline_idx(index); - if (raw_idx == -1) { - return -1; - } else { - return prv_raw_to_adj_idx(prv_get_idx_for_timeline_idx(index)); - } -} - -TimelineIterState *timeline_model_get_iter_state(int index) { - return &s_model_data->states[prv_adj_to_raw_idx(index)]; -} - -bool timeline_model_is_empty(void) { - return (timeline_model_get_num_items() == 0); -} - -int timeline_model_get_num_items(void) { - if (timeline_model_get_current_state() == NULL) { - return 0; - } - int num = positive_modulo(s_model_data->last_index - - s_model_data->first_index, TIMELINE_NUM_ITEMS_IN_MODEL) + 1; - // we always keep one slot marked empty so we can tell if we have zero items - if (num == TIMELINE_NUM_ITEMS_IN_MODEL) { - return 0; - } else { - return num; - } -} - -static int prv_get_next_item_idx(void) { - return positive_modulo(s_model_data->last_index + 1, - TIMELINE_NUM_ITEMS_IN_MODEL); -} - -static int prv_get_prev_item_idx(void) { - return positive_modulo(s_model_data->first_index - 1, - TIMELINE_NUM_ITEMS_IN_MODEL); -} - -static Iterator *prv_get_iter(int index) { - return &s_model_data->iters[prv_adj_to_raw_idx(index)]; -} - -TimelineIterState *timeline_model_get_current_state(void) { - if (timeline_model_get_iter_state(0)->node == NULL) { - return NULL; - } - return timeline_model_get_iter_state(0); -} - -static int prv_find_item_by_uuid(Uuid *id) { - for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - if (timeline_model_get_iter_state(i)->node && - uuid_equal(&timeline_model_get_iter_state(i)->pin.header.id, id)) { - return i; - } - } - return -1; -} - -#ifdef TIMELINE_DEBUG -static void prv_log_all_items(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "First item: %d, last item: %d", s_model_data->first_index, - s_model_data->last_index); - for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - TimelineItem *item = &timeline_model_get_iter_state(i)->pin; - PBL_LOG(LOG_LEVEL_DEBUG, "ID first byte: 0x%x", item->header.id.byte0); - PBL_LOG(LOG_LEVEL_DEBUG, "Address of node: %p", timeline_model_get_iter_state(i)->node); - PBL_LOG(LOG_LEVEL_DEBUG, "Timestamp: %ld", item->header.timestamp); - } -} -#endif - -static void prv_move_first_index(int delta) { - s_model_data->first_index = positive_modulo( - s_model_data->first_index + delta, TIMELINE_NUM_ITEMS_IN_MODEL); - PBL_LOG(LOG_LEVEL_DEBUG, "Set origin, initial item: %d, final item: %d", - s_model_data->first_index, s_model_data->last_index); -} - -bool timeline_model_iter_next(int *new_idx, bool *has_next) { - int next_idx = prv_get_next_item_idx(); - int last_idx = s_model_data->last_index; - timeline_iter_copy_state( - &s_model_data->states[next_idx], - &s_model_data->states[last_idx], - &s_model_data->iters[next_idx], - &s_model_data->iters[last_idx]); - bool rv = iter_next(&s_model_data->iters[next_idx]); - if (rv) { - if (has_next) { - *has_next = true; - } - s_model_data->last_index = next_idx; - prv_move_first_index(1); - if (new_idx) { - *new_idx = s_model_data->states[next_idx].index; - } - } else { - if (has_next) { - *has_next = false; - } - rv = timeline_model_get_num_items() > 1; - if (rv) { - prv_move_first_index(1); - } - } -#ifdef TIMELINE_DEBUG - prv_log_all_items(); -#endif - return rv; -} - -bool timeline_model_iter_prev(int *new_idx, bool *has_prev) { - int prev_idx = prv_get_prev_item_idx(); - int first_idx = s_model_data->first_index; - timeline_iter_copy_state( - &s_model_data->states[prev_idx], - &s_model_data->states[first_idx], - &s_model_data->iters[prev_idx], - &s_model_data->iters[first_idx]); - bool rv = iter_prev(&s_model_data->iters[prev_idx]); - if (rv) { - if (has_prev) { - *has_prev = true; - } - // bring the last_index back if we've succeeded iterating prev-wards and there are at least - // TIMELINE_NUM_VISIBLE_ITEMS items in the model. If there are fewer, we keep the last_index - // where it is so the model can "grow" to contain TIMELINE_NUM_VISIBLE_ITEMS - if (timeline_model_get_num_items() >= TIMELINE_NUM_VISIBLE_ITEMS) { - s_model_data->last_index = positive_modulo( - s_model_data->last_index - 1, TIMELINE_NUM_ITEMS_IN_MODEL); - } - if (new_idx) { - *new_idx = s_model_data->states[prev_idx].index; - } - prv_move_first_index(-1); - } else { - if (has_prev) { - *has_prev = false; - } - } -#ifdef TIMELINE_DEBUG - prv_log_all_items(); -#endif - return rv; -} - -// Initialize the TIMELINE_NUM_VISIBLE_ITEMS iterators and states -// Try to move the iterators except iters[0] next-wards the appropriate number of times -void timeline_model_init(time_t timestamp, TimelineModel *model) { - PBL_ASSERTN(model); - s_model_data = model; - - // build the timeline - status_t rv = timeline_init(&s_model_data->timeline); - PBL_ASSERTN(PASSED(rv)); - - s_model_data->first_index = 0; - s_model_data->last_index = TIMELINE_NUM_VISIBLE_ITEMS; - for (int i = 0; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - rv = timeline_iter_init(prv_get_iter(i), timeline_model_get_iter_state(i), - &s_model_data->timeline, s_model_data->direction, timestamp); - if (FAILED(rv)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timeline iterator failed to init!"); - } - if (FAILED(rv) || rv == S_NO_MORE_ITEMS) { - timeline_model_get_iter_state(i)->node = NULL; - } - bool iter_at_final_position = true; - for (int num_to_iter = 0; num_to_iter < i; num_to_iter++) { - iter_at_final_position = iter_at_final_position && iter_next(prv_get_iter(i)); - } - if (iter_at_final_position) { - s_model_data->last_index = prv_get_next_item_idx(); - } - } -#ifdef TIMELINE_DEBUG - prv_log_all_items(); -#endif -} - -void timeline_model_deinit(void) { - for (int i = 0; i < TIMELINE_NUM_ITEMS_IN_MODEL; i++) { - timeline_iter_deinit(&s_model_data->iters[i], - &s_model_data->states[i], &s_model_data->timeline); - s_model_data->states[i].node = NULL; - } - s_model_data->first_index = 0; - s_model_data->last_index = TIMELINE_NUM_VISIBLE_ITEMS; -} - -static void prv_remove_index_gracefully(int idx) { - PBL_ASSERTN(idx >= 0); - TimelineNode *node = timeline_model_get_iter_state(idx)->node; - if (iter_next(prv_get_iter(idx))) { - for (int i = idx + 1; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - // it's possible for an iter to be on a node that is no longer valid, which could leave - // multiple iterators starting off at different nodes but ending up on the same one - // after one iter_next, so try to separate them - do { - if (!iter_next(prv_get_iter(i))) { - break; - } - } while (timeline_nodes_equal(timeline_model_get_iter_state(i)->node, - timeline_model_get_iter_state(i - 1)->node)); - } - timeline_iter_remove_node(&s_model_data->timeline, node); - PBL_LOG(LOG_LEVEL_DEBUG, "Item to delete in view, iterating next"); - } else if (iter_prev(prv_get_iter(idx))) { - // prv_get_iter(idx) is at the end, so we have to move prev-wards - // if prv_get_iter(idx) is at the end, all iters > idx must also be at the end - // so iterate those prev-wards - for (int i = idx + 1; i < TIMELINE_NUM_VISIBLE_ITEMS; i++) { - iter_prev(prv_get_iter(i)); - } - timeline_iter_remove_node(&s_model_data->timeline, node); - PBL_LOG(LOG_LEVEL_DEBUG, "Item to delete in view, iterating prev"); - } else { - // if we can't iterate next or prev, we've deleted the only item - timeline_model_deinit(); - PBL_LOG(LOG_LEVEL_DEBUG, "Item to delete in view, deiniting "); - } -} - -void timeline_model_remove(Uuid *id) { - int item_idx; - // more than one item with the same ID is possible due to multi-day events - // remove them from our list first - while ((item_idx = prv_find_item_by_uuid(id)) != -1) { - prv_remove_index_gracefully(item_idx); - } - - // remove the rest from the iterator list - while (timeline_iter_remove_node_with_id(&s_model_data->timeline, id)) {} -} diff --git a/src/fw/apps/system_apps/timeline/timeline_model.h b/src/fw/apps/system_apps/timeline/timeline_model.h deleted file mode 100644 index b16ac8c5d1..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_model.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "timeline_common.h" - -#include "kernel/events.h" - -#define TIMELINE_NUM_ITEMS_IN_MODEL (TIMELINE_NUM_VISIBLE_ITEMS + 1) - -// Timeline is a circular array of TIMELINE_NUM_ITEMS_IN_MODEL iter states - -typedef struct { - TimelineNode *timeline; - TimelineDirection direction; - Iterator iters[TIMELINE_NUM_ITEMS_IN_MODEL]; - TimelineIterState states[TIMELINE_NUM_ITEMS_IN_MODEL]; - int first_index; - int last_index; -} TimelineModel; - -//! Get the iterator state with the timeline location index, i.e. the iterator with the given index -//! if it's within the model -//! These indices do not change when an iteration has occurred, i.e. -//! timeline_model_get_iter_state_timeline_idx(2) is the same before and after an iter_next or -//! iter_prev -//! @return NULL if the item with that index is not contained in the model's window of items -TimelineIterState *timeline_model_get_iter_state_with_timeline_idx(int index); - -//! Get the index with respect to the model of the timeline item with the given timeline index -//! @return -1 if the item with that index is not contained in the model's window of items -int timeline_model_get_idx_for_timeline_idx(int index); - -//! Get the iterator state with the index with respect to the model, i.e. the index that represents -//! which item in the model it is -//! These indices do change when an iteration has occurred, i.e. -//! timeline_model_get_iter_state_idx(0) changes after an iter_next or iter_prev -TimelineIterState *timeline_model_get_iter_state(int index); - -TimelineIterState *timeline_model_get_current_state(void); - -bool timeline_model_is_empty(void); - -int timeline_model_get_num_items(void); - -//! Iterate the model towards the "next" direction -//! @new_idx Set the raw index of the new iterator if new_idx is not NULL -//! @has_next, set to whether or not there is a new third item in the list, i.e. false = iterate but -//! show no more new items -//! @return whether or not the current item is at the end of the list, i.e. false = stop iterating -bool timeline_model_iter_next(int *new_idx, bool *has_next); - -//! Iterate the model towards the "prev" direction -//! @new_idx Set the raw index of the new iterator if new_idx is not NULL -//! @return whether or not the current item is at the beginning of the list, i.e. stop iterating -bool timeline_model_iter_prev(int *new_idx, bool *has_prev); - -void timeline_model_init(time_t timestamp, TimelineModel *model_data); - -void timeline_model_deinit(void); - -void timeline_model_remove(Uuid *id); diff --git a/src/fw/apps/system_apps/timeline/timeline_private.h b/src/fw/apps/system_apps/timeline/timeline_private.h deleted file mode 100644 index 49e71436c3..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_private.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - diff --git a/src/fw/apps/system_apps/timeline/timeline_relbar.c b/src/fw/apps/system_apps/timeline/timeline_relbar.c deleted file mode 100644 index 5bf1e5f27f..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_relbar.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_layer.h" -#include "timeline_animations.h" -#include "timeline_relbar.h" - -#include "applib/graphics/graphics.h" -#include "services/normal/timeline/timeline_layout.h" -#include "system/logging.h" - -#include - -#define TIMELINE_FAT_PIN_SIZE (timeline_layer_get_fat_pin_height()) -#define SIDEBAR_WIDTH (timeline_layer_get_ideal_sidebar_width()) - -/////////////////////////////////////////////////////////// -// Private functions -/////////////////////////////////////////////////////////// -static void prv_rel_bar_reset_offsets(RelationshipBarLayer *relbar_layer, - RelationshipBarOffsetType rel_bar_type) { - switch (rel_bar_type) { - case RelationshipBarOffsetTypeCurr: - relbar_layer->curr_rel_bar.anim_offset = 0; - break; - case RelationshipBarOffsetTypePrev: - relbar_layer->prev_rel_bar.anim_offset = 0; - break; - case RelationshipBarOffsetTypeBoth: - relbar_layer->curr_rel_bar.anim_offset = 0; - relbar_layer->prev_rel_bar.anim_offset = 0; - break; - } -} - -static void prv_prev_rel_bar_setter(void *context, int16_t value) { - TimelineLayer *timeline_layer = context; - timeline_layer->relbar_layer.prev_rel_bar.anim_offset = value; - layer_mark_dirty(&timeline_layer->layer); -} - -static int16_t prv_prev_rel_bar_getter(void *context) { - TimelineLayer *timeline_layer = context; - return timeline_layer->relbar_layer.prev_rel_bar.anim_offset; -} - -static void prv_curr_rel_bar_setter(void *context, int16_t value) { - TimelineLayer *timeline_layer = context; - timeline_layer->relbar_layer.curr_rel_bar.anim_offset = value; - layer_mark_dirty(&timeline_layer->layer); -} - -static int16_t prv_curr_rel_bar_getter(void *context) { - TimelineLayer *timeline_layer = context; - return timeline_layer->relbar_layer.curr_rel_bar.anim_offset; -} - -static RelationshipBarType prv_get_pin_relationship(TimelineLayoutInfo *current, - TimelineLayoutInfo *next) { - if ((!current) || (current->duration_s == 0) || (current->all_day) || (!next)) { - // No relationship bar shown - return RelationshipBarTypeNone; - } - - const time_t current_end = current->end_time; - if ((next->timestamp > current_end) && (current->current_day == next->current_day)) { - // Next pin starts after the end of the current pin - return RelationshipBarTypeFreeTime; - } else if ((next->timestamp == current_end) && (current->current_day == next->current_day)) { - // Next pin starts exactly at the end of the current pin - return RelationshipBarTypeBackToBack; - } else if (current->current_day != next->current_day) { - // Don't show relationship bar when event is across days - return RelationshipBarTypeNone; - } else { - // All other cases considered as overlapped - return RelationshipBarTypeOverlap; - } -} - -static void prv_rel_bar_stopped(Animation *animation, bool is_finished, void *context) { - // Don't show the rel bar if the animation was interrupted - if (!is_finished) { - TimelineLayer *layer = (TimelineLayer *)context; - prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeBoth); - } -} - -#define REL_BAR_VERT_MARGIN 14 -static int prv_get_line_length(TimelineLayer *timeline_layer, GRect *first_icon_frame_out, - GRect *second_icon_frame_out) { - GRect first_icon_frame; - GRect second_icon_frame; - timeline_layer_get_icon_frame(timeline_layer, TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT, - &first_icon_frame); - timeline_layer_get_icon_frame(timeline_layer, TIMELINE_LAYER_FIRST_VISIBLE_LAYOUT + 1, - &second_icon_frame); - if (first_icon_frame_out) { - *first_icon_frame_out = first_icon_frame; - } - if (second_icon_frame_out) { - *second_icon_frame_out = second_icon_frame; - } - - const int total_space = second_icon_frame.origin.y - grect_get_max_y(&first_icon_frame); - return total_space - (REL_BAR_VERT_MARGIN * 2); -} - -static int prv_get_overlap_line_length(TimelineLayer *layer, GRect *first_icon_frame_out, - GRect *second_icon_frame_out) { - const int full_line_length = - prv_get_line_length(layer, first_icon_frame_out, second_icon_frame_out); - return (3 * full_line_length) / 5; -} - -#define REL_BAR_BACK_TO_BACK_OFFSET 10 -#define REL_BAR_PREV_ANIM_OFFSET 10 - -// Create previous rel bar animation -static Animation *prv_create_prev_rel_bar_animation(TimelineLayer *layer, uint32_t duration, - InterpolateInt64Function interpolate) { - Animation *prev_rel_bar_anim = NULL; - prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypePrev); - int16_t prev_from_rel_bar_value = 0; - int16_t prev_to_rel_bar_value = 0; - static const PropertyAnimationImplementation prev_implementation = { - .base.update = (AnimationUpdateImplementation) property_animation_update_int16, - .accessors.setter.int16 = prv_prev_rel_bar_setter, - .accessors.getter.int16 = prv_prev_rel_bar_getter, - }; - - if (layer->relbar_layer.prev_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { - const int overlap_line_length = prv_get_overlap_line_length(layer, NULL, NULL); - layer->relbar_layer.prev_rel_bar.anim_offset = overlap_line_length; - prev_from_rel_bar_value = overlap_line_length; - prev_to_rel_bar_value = 0; - prev_rel_bar_anim = (Animation *)property_animation_create( - &prev_implementation, layer, &prev_from_rel_bar_value, &prev_to_rel_bar_value); - } else if ((layer->relbar_layer.prev_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || - (layer->relbar_layer.prev_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { - layer->relbar_layer.prev_rel_bar.anim_offset = REL_BAR_PREV_ANIM_OFFSET; - prev_from_rel_bar_value = REL_BAR_PREV_ANIM_OFFSET; - prev_to_rel_bar_value = 0; - prev_rel_bar_anim = (Animation *)property_animation_create( - &prev_implementation, layer, &prev_from_rel_bar_value, &prev_to_rel_bar_value); - } - if (prev_rel_bar_anim) { - // Delay to avoid overlapping moving icon with bars - animation_set_delay(prev_rel_bar_anim, 0); - animation_set_duration(prev_rel_bar_anim, duration / 3); - } - - return prev_rel_bar_anim; -} - -#define REL_BAR_CURR_OVERLAP_START_OFFSET 10 -#define REL_BAR_CURR_ANIM_DELAY(delay) ((2 * delay) / 3) -#define REL_BAR_CURR_ANIM_DURATION(duration) ((2 * duration) / 3) -// Create current rel bar animation -static Animation *prv_create_curr_rel_bar_animation(TimelineLayer *layer, uint32_t duration, - InterpolateInt64Function interpolate) { - Animation *curr_rel_bar_anim = NULL; - prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeCurr); - int16_t curr_from_rel_bar_value = 0; - int16_t curr_to_rel_bar_value = 0; - - static const PropertyAnimationImplementation curr_implementation = { - .base.update = (AnimationUpdateImplementation) property_animation_update_int16, - .accessors.setter.int16 = prv_curr_rel_bar_setter, - .accessors.getter.int16 = prv_curr_rel_bar_getter, - }; - - if (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { - curr_from_rel_bar_value = REL_BAR_CURR_OVERLAP_START_OFFSET; - curr_to_rel_bar_value = prv_get_overlap_line_length(layer, NULL, NULL); - curr_rel_bar_anim = (Animation *)property_animation_create( - &curr_implementation, layer, &curr_from_rel_bar_value, &curr_to_rel_bar_value); - } else if ((layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || - (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { - curr_from_rel_bar_value = 0; - curr_to_rel_bar_value = REL_BAR_BACK_TO_BACK_OFFSET; - curr_rel_bar_anim = (Animation *)property_animation_create( - &curr_implementation, layer, &curr_from_rel_bar_value, &curr_to_rel_bar_value); - } - - if (curr_rel_bar_anim) { - // Delay to avoid overlapping moving icon with bars - animation_set_delay(curr_rel_bar_anim, REL_BAR_CURR_ANIM_DELAY(duration)); - animation_set_duration(curr_rel_bar_anim, REL_BAR_CURR_ANIM_DURATION(duration)); - animation_set_custom_interpolation(curr_rel_bar_anim, interpolate); - } - - return curr_rel_bar_anim; -} - -#define REL_BAR_LINE_CHECK_LENGTH 6 -#define REL_BAR_LINE_WIDTH 2 -#define REL_BAR_LINE_HORIZ_OFFSET ((SIDEBAR_WIDTH / 2) + (REL_BAR_LINE_WIDTH / 2)) -#define REL_BAR_LINE_NOTCH_HORIZ_OFFSET ((REL_BAR_LINE_CHECK_LENGTH / 2) + (REL_BAR_LINE_WIDTH / 2)) -static void prv_draw_rel_bar_line(TimelineLayer *timeline_layer, GContext* ctx, - bool current, int16_t anim_offset) { - int16_t prev_offset = 0; // Used to animate the previous animation offset - if (!current) { - prev_offset = REL_BAR_PREV_ANIM_OFFSET - anim_offset; - prev_offset = (timeline_layer->move_delta > 0) ? prev_offset : -prev_offset; - } - - int16_t curr_offset = anim_offset; // Used to animate the previous animation offset - if ((current && (curr_offset <= 0)) || - ((!current) && ((prev_offset >= REL_BAR_PREV_ANIM_OFFSET) || - (prev_offset <= -REL_BAR_PREV_ANIM_OFFSET)))) { - return; - } - - // Choose which offset to use based on current or previous animation - if (!current) { - curr_offset = 0; - } else { - curr_offset = (-timeline_layer->move_delta) * (REL_BAR_BACK_TO_BACK_OFFSET - curr_offset); - prev_offset = 0; - } - - GRect first_icon_frame; - GRect second_icon_frame; - const int line_length = (prv_get_line_length(timeline_layer, &first_icon_frame, - &second_icon_frame) - REL_BAR_LINE_WIDTH) / 2; - - // Draw two lines that are centered in the side bar - // Bar 1 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - graphics_context_set_fill_color(ctx, GColorWhite); - GRect layer_bounds = timeline_layer->layer.bounds; - GRect line; - line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; - // Account for the size of the icon when positioning vertically - line.origin.y = - grect_get_max_y(&first_icon_frame) + REL_BAR_VERT_MARGIN - curr_offset - prev_offset; - line.size.w = REL_BAR_LINE_WIDTH; - line.size.h = line_length + curr_offset; - graphics_fill_rect(ctx, &line); - - // Bottom Notch for Bar 1 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - GRect line2; - line2.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; - line2.size.h = REL_BAR_LINE_WIDTH; - line2.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; - line2.origin.y = grect_get_max_y(&line) - line2.size.h; - graphics_fill_rect(ctx, &line2); - - // Bar 2 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; - line.origin.y = second_icon_frame.origin.y - (REL_BAR_VERT_MARGIN + line_length) - prev_offset; - line.size.w = REL_BAR_LINE_WIDTH; - line.size.h = line_length - curr_offset; - graphics_fill_rect(ctx, &line); - - // Top Notch for Bar 2 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - line2.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; - line2.origin.y = line.origin.y; - line2.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; - line2.size.h = REL_BAR_LINE_WIDTH; - graphics_fill_rect(ctx, &line2); -} - -#define REL_BAR_DOT_SIZE 2 -static void prv_draw_rel_bar_dotted(TimelineLayer *timeline_layer, GContext* ctx, - bool current, int16_t rel_bar_value) { - int16_t prev_offset = 0; // Used to animate the previous animation offset - if (!current) { - prev_offset = REL_BAR_PREV_ANIM_OFFSET - rel_bar_value; - prev_offset = (timeline_layer->move_delta > 0) ? prev_offset : -prev_offset; - } - - int16_t curr_offset = rel_bar_value; // Used to animate the current animation offset - if ((current && (curr_offset <= 0)) || - ((!current) && ((prev_offset >= REL_BAR_PREV_ANIM_OFFSET) || - (prev_offset <= -REL_BAR_PREV_ANIM_OFFSET)))) { - return; - } - - - // Choose which offset to use based on current or previous animation - if (!current) { - curr_offset = 0; - } else { - curr_offset = (-timeline_layer->move_delta) * (REL_BAR_BACK_TO_BACK_OFFSET - curr_offset); - prev_offset = 0; - } - - GRect first_icon_frame; - GRect second_icon_frame; - const int line_length = - prv_get_line_length(timeline_layer, &first_icon_frame, &second_icon_frame) / 3; - const int dot_padding = 1; - const int solid_line_length = line_length - dot_padding; - const int dot_line_length = line_length + dot_padding; - - // Bar 1 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - graphics_context_set_fill_color(ctx, GColorWhite); - GRect layer_bounds = timeline_layer->layer.bounds; - GRect line; - line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; - // Account for the size of the icon when positioning vertically - line.origin.y = grect_get_max_y(&first_icon_frame) + - REL_BAR_VERT_MARGIN - curr_offset - prev_offset; - line.size.w = REL_BAR_LINE_WIDTH; - line.size.h = solid_line_length + curr_offset; - graphics_fill_rect(ctx, &line); - - // Bottom Notch for Bar 1 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - GRect notch; - notch.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; - notch.origin.y = grect_get_max_y(&line) - REL_BAR_LINE_WIDTH; - notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; - notch.size.h = REL_BAR_LINE_WIDTH; - graphics_fill_rect(ctx, ¬ch); - - const int dot_origin_y_max = - grect_get_max_y(&line) + dot_line_length + curr_offset - prev_offset; - // Dots in between two bars - GRect dot = GRect(line.origin.x, grect_get_max_y(&line) + REL_BAR_LINE_WIDTH + dot_padding, - REL_BAR_DOT_SIZE, REL_BAR_DOT_SIZE); - const int dot_advance = 2 * REL_BAR_DOT_SIZE; - for (; dot.origin.y + dot_advance <= dot_origin_y_max; dot.origin.y += dot_advance) { - graphics_fill_rect(ctx, &dot); - } - - // Bar 2 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - layer_bounds = timeline_layer->layer.bounds; - line.origin.x = layer_bounds.origin.x + layer_bounds.size.w - REL_BAR_LINE_HORIZ_OFFSET; - line.origin.y = dot.origin.y + dot_padding; - line.size.w = REL_BAR_LINE_WIDTH; - line.size.h = solid_line_length - curr_offset; - graphics_fill_rect(ctx, &line); - - // Top Notch for Bar 2 - // Filled rect used to draw line of REL_BAR_LINE_WIDTH stroke width - notch.origin.x = line.origin.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; - notch.origin.y = line.origin.y; - notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; - notch.size.h = REL_BAR_LINE_WIDTH; - graphics_fill_rect(ctx, ¬ch); -} - -#define REL_BAR_OVERLAP_STROKE_WIDTH 2 -#define REL_BAR_OVERLAP_SIDE_MARGIN 2 -#define REL_BAR_OVERLAP_NUDGE_X 1 -#define REL_BAR_OVERLAP_LINE2_HORIZ_OFFSET ((2 * REL_BAR_OVERLAP_SIDE_MARGIN) + 1) -static void prv_draw_rel_bar_overlap(TimelineLayer *timeline_layer, GContext* ctx, - bool current, int16_t rel_bar_value) { - GRect first_icon_frame; - GRect second_icon_frame; - const int full_line_length = - prv_get_overlap_line_length(timeline_layer, &first_icon_frame, &second_icon_frame); - - int16_t line_length = rel_bar_value; - int16_t y_offset = 0; - if (!current) { - line_length = full_line_length; - y_offset = (((full_line_length - rel_bar_value) * REL_BAR_PREV_ANIM_OFFSET) / - full_line_length) * timeline_layer->move_delta; - } - - if (((line_length <= 0) && current) || - ((!current) && ((y_offset >= REL_BAR_PREV_ANIM_OFFSET) || - (y_offset <= -REL_BAR_PREV_ANIM_OFFSET)))) { - return; - } - - graphics_context_set_fill_color(ctx, GColorWhite); - graphics_context_set_antialiased(ctx, false); - GRect layer_bounds = timeline_layer->layer.bounds; - GPoint line1_start; - GPoint line2_start; - - // Draw down - line1_start.x = layer_bounds.origin.x + layer_bounds.size.w - (SIDEBAR_WIDTH / 2) - - REL_BAR_OVERLAP_NUDGE_X - REL_BAR_OVERLAP_SIDE_MARGIN; - line1_start.y = grect_get_max_y(&first_icon_frame) + REL_BAR_VERT_MARGIN - - y_offset + REL_BAR_LINE_WIDTH; - - graphics_fill_rect(ctx, &GRect(line1_start.x, line1_start.y, - REL_BAR_OVERLAP_STROKE_WIDTH, line_length)); - GRect notch = GRectZero; - notch.origin.x = line1_start.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; - notch.origin.y = line1_start.y - REL_BAR_LINE_WIDTH; - notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; - notch.size.h = REL_BAR_LINE_WIDTH; - graphics_fill_rect(ctx, ¬ch); - - // Draw up - line2_start.x = line1_start.x + REL_BAR_OVERLAP_LINE2_HORIZ_OFFSET; - line2_start.y = second_icon_frame.origin.y - REL_BAR_VERT_MARGIN - y_offset - - REL_BAR_LINE_WIDTH; - graphics_fill_rect(ctx, &GRect(line2_start.x, line2_start.y, - REL_BAR_OVERLAP_STROKE_WIDTH, -line_length)); - notch.origin.x = line2_start.x - REL_BAR_LINE_NOTCH_HORIZ_OFFSET + 1; - notch.origin.y = line2_start.y; - notch.size.w = REL_BAR_LINE_CHECK_LENGTH + REL_BAR_LINE_WIDTH; - notch.size.h = REL_BAR_LINE_WIDTH; - graphics_fill_rect(ctx, ¬ch); -} - -#define NUM_REL_BAR_ANIMATIONS 2 -#define CURRENT_REL_BAR_PIN_INDEX 1 -static void prv_update_proc(Layer *layer, GContext *ctx) { - RelationshipBarLayer *relbar_layer = (RelationshipBarLayer *)layer; - TimelineLayer *timeline_layer = (TimelineLayer *)relbar_layer->timeline_layer; - // Don't draw the relationship bars if they are meant to be hidden - // Currently drawing relationship bar for future only - no plan for past - if (layer_get_hidden(&relbar_layer->layer) || - (timeline_layer->scroll_direction == TimelineScrollDirectionUp)) { - return; - } - - for (int index = 0; index < NUM_REL_BAR_ANIMATIONS; index++) { - bool current = (index == CURRENT_REL_BAR_PIN_INDEX); - RelationshipBarType rel_bar = current ? - timeline_layer->relbar_layer.curr_rel_bar.rel_bar_type : - timeline_layer->relbar_layer.prev_rel_bar.rel_bar_type; - int16_t rel_bar_value = current ? timeline_layer->relbar_layer.curr_rel_bar.anim_offset : - timeline_layer->relbar_layer.prev_rel_bar.anim_offset; - switch (rel_bar) { - case RelationshipBarTypeFreeTime: - // Draw dotted line - prv_draw_rel_bar_dotted(timeline_layer, ctx, current, rel_bar_value); - break; - case RelationshipBarTypeOverlap: - // Draw overlapping lines - prv_draw_rel_bar_overlap(timeline_layer, ctx, current, rel_bar_value); - break; - case RelationshipBarTypeBackToBack: - prv_draw_rel_bar_line(timeline_layer, ctx, current, rel_bar_value); - break; - case RelationshipBarTypeNone: - // Draw nothing - break; - } - } -} - -// Timer callback that displays the current relationship bar if user has stopped fast clicking -void prv_rel_bar_show(void *context) { - TimelineLayer *layer = (TimelineLayer *)context; - if (timeline_layer_should_animate_day_separator(layer)) { - prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeCurr); - layer_set_hidden(&layer->relbar_layer.layer, true); - return; - } - - layer_set_hidden(&layer->relbar_layer.layer, false); - if (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { - layer->relbar_layer.curr_rel_bar.anim_offset = prv_get_overlap_line_length(layer, NULL, NULL); - } else if ((layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || - (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { - layer->relbar_layer.curr_rel_bar.anim_offset = REL_BAR_BACK_TO_BACK_OFFSET; - } - - layer_mark_dirty((Layer*) layer); -} - - -#define TIMELINE_NUM_REL_BARS (TIMELINE_NUM_VISIBLE_ITEMS + 1) -static void prv_update_rel_bars(TimelineLayer *layer) { - // Store prev, current, and next item relationship bar types - RelationshipBarType rel_bar_types[TIMELINE_NUM_REL_BARS]; - - layer->relbar_layer.prev_rel_bar.rel_bar_type = layer->relbar_layer.curr_rel_bar.rel_bar_type; - - for (int index = 0; index < TIMELINE_NUM_REL_BARS; index++) { - if (layer->layouts_info[index]) { - rel_bar_types[index] = prv_get_pin_relationship(layer->layouts_info[index], - layer->layouts_info[index + 1]); - } - } - layer->relbar_layer.curr_rel_bar.rel_bar_type = rel_bar_types[1]; - if (layer->layouts_info[1]) { - PBL_LOG(LOG_LEVEL_DEBUG, "Current rel bar %d, duration %"PRIu32, rel_bar_types[1], - layer->layouts_info[1]->duration_s); - } -} - -///////////////////////////////////////// -// Public functions -///////////////////////////////////////// -#define REL_BAR_TIMER_DELAY(delay) (delay / 3) // delay of timer is based on the duration -Animation *timeline_relbar_layer_create_animation(TimelineLayer *layer, uint32_t duration, - InterpolateInt64Function interpolate) { - bool force_rel_bar_anim = false; - // Cancel previous fast scroll timer since user has clicked again - evented_timer_cancel(layer->relbar_layer.rel_bar_timer); - if (timeline_animation_interpolate_moook_second_half == interpolate) { - force_rel_bar_anim = true; - if (duration == TIMELINE_UP_DOWN_ANIMATION_DURATION_MS) { - // Update current state of relationship bars - prv_update_rel_bars(layer); - - // Hide bars - layer_set_hidden(&layer->relbar_layer.layer, true); - - // Setup a timer to display bars after a fraction of the input duration (i.e. if the user - // has stopped scrolling fast) - layer->relbar_layer.rel_bar_timer = - evented_timer_register(REL_BAR_TIMER_DELAY(duration), false, prv_rel_bar_show, layer); - - // Don't schedule animation - return NULL; - } - } - - prv_update_rel_bars(layer); - - bool curr_anim_needed = true; - bool prev_anim_needed = true; - if (timeline_layer_should_animate_day_separator(layer)) { - curr_anim_needed = false; - prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypeCurr); - layer_set_hidden(&layer->relbar_layer.layer, true); - } else { - layer_set_hidden(&layer->relbar_layer.layer, false); - } - - // This check is to ensure rel bar does not show up while day separator is on screen. - // The force animation is used to ensure the rel bar shows up after the day separator is hidden - - // timeline_create_up_down_animation is called both when sliding between timeline items as well as - // during the day separator animation. - if (force_rel_bar_anim) { - prev_anim_needed = false; - prv_rel_bar_reset_offsets(&layer->relbar_layer, RelationshipBarOffsetTypePrev); - curr_anim_needed = true; - } - - // Create previous rel bar animation - Animation *prev_rel_bar_anim = NULL; - - if (prev_anim_needed) { - prev_rel_bar_anim = prv_create_prev_rel_bar_animation(layer, duration, interpolate); - } - - Animation *curr_rel_bar_anim = NULL; - - if (curr_anim_needed) { - curr_rel_bar_anim = prv_create_curr_rel_bar_animation(layer, duration, interpolate); - } - - Animation *rel_bar_anim = NULL; - if (prev_rel_bar_anim && curr_rel_bar_anim) { - rel_bar_anim = animation_spawn_create(prev_rel_bar_anim, curr_rel_bar_anim, NULL); - } else if (prev_rel_bar_anim) { - rel_bar_anim = prev_rel_bar_anim; - } else { - rel_bar_anim = curr_rel_bar_anim; - } - - if (rel_bar_anim) { - animation_set_handlers(rel_bar_anim, (AnimationHandlers) { - .stopped = prv_rel_bar_stopped, - }, layer); - } - - return rel_bar_anim; -} - -void timeline_relbar_layer_reset(TimelineLayer *layer) { - prv_update_rel_bars(layer); - if (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeOverlap) { - layer->relbar_layer.curr_rel_bar.anim_offset = prv_get_overlap_line_length(layer, NULL, NULL); - } else if ((layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeBackToBack) || - (layer->relbar_layer.curr_rel_bar.rel_bar_type == RelationshipBarTypeFreeTime)) { - layer->relbar_layer.curr_rel_bar.anim_offset = REL_BAR_BACK_TO_BACK_OFFSET; - } -} - -void timeline_relbar_layer_init(TimelineLayer *timeline_layer) { - RelationshipBarLayer *relbar_layer = &timeline_layer->relbar_layer; - *relbar_layer = (RelationshipBarLayer){}; - // init layer - layer_init(&relbar_layer->layer, &timeline_layer->layer.frame); - layer_set_update_proc(&relbar_layer->layer, prv_update_proc); - layer_add_child((Layer*)timeline_layer, (Layer *)&timeline_layer->relbar_layer); - relbar_layer->timeline_layer = timeline_layer; -} - -//! Deinitialize the timeline relationship bar layer within the \ref TimelineLayer -void timeline_relbar_layer_deinit(TimelineLayer *timeline_layer) { - layer_deinit(&timeline_layer->relbar_layer.layer); -} diff --git a/src/fw/apps/system_apps/timeline/timeline_relbar.h b/src/fw/apps/system_apps/timeline/timeline_relbar.h deleted file mode 100644 index 857b2ac648..0000000000 --- a/src/fw/apps/system_apps/timeline/timeline_relbar.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "timeline_layer.h" -#include "applib/ui/animation.h" - -typedef enum { - RelationshipBarOffsetTypePrev, - RelationshipBarOffsetTypeCurr, - RelationshipBarOffsetTypeBoth -} RelationshipBarOffsetType; - -//! Create the animations for the relationship bars -//! @param layer Pointer to the \ref TimelineLayer to create the relationship bar animation for -//! @param duration Duration in ms of the animation to create -//! @param interpolate Custom interpolation function to use for the animation -Animation *timeline_relbar_layer_create_animation(TimelineLayer *timeline_layer, uint32_t duration, - InterpolateInt64Function interpolate); - -//! Reset the relationship bar state -void timeline_relbar_layer_reset(TimelineLayer *timeline_layer); - -//! Initialize the timeline relationship bar layer within the \ref TimelineLayer -void timeline_relbar_layer_init(TimelineLayer *timeline_layer); - -//! Deinitialize the timeline relationship bar layer within the \ref TimelineLayer -void timeline_relbar_layer_deinit(TimelineLayer *timeline_layer); diff --git a/src/fw/apps/system_apps/toggle/airplane_mode.c b/src/fw/apps/system_apps/toggle/airplane_mode.c deleted file mode 100644 index b36ae7173d..0000000000 --- a/src/fw/apps/system_apps/toggle/airplane_mode.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "airplane_mode.h" - -#include "applib/app.h" -#include "applib/ui/action_toggle.h" -#include "process_management/app_manager.h" -#include "services/common/bluetooth/bluetooth_ctl.h" -#include "services/common/i18n/i18n.h" - -static bool prv_get_state(void *context) { - return bt_ctl_is_airplane_mode_on(); -} - -static void prv_set_state(bool enabled, void *context) { - bt_ctl_set_airplane_mode_async(!bt_ctl_is_airplane_mode_on()); -} - -static const ActionToggleImpl s_airplane_mode_action_toggle_impl = { - .window_name = "Airplane Mode Toggle", - .prompt_icon = RESOURCE_ID_AIRPLANE, - .result_icon = RESOURCE_ID_AIRPLANE, - // Toggling airplane mode involves locks which can block animation, don't animate - .result_icon_static = true, - .prompt_enable_message = i18n_noop("Turn On Airplane Mode?"), - .prompt_disable_message = i18n_noop("Turn Off Airplane Mode?"), - .result_enable_message = i18n_noop("Airplane\nMode On"), - .result_disable_message = i18n_noop("Airplane\nMode Off"), - .callbacks = { - .get_state = prv_get_state, - .set_state = prv_set_state, - }, -}; - -static void prv_main(void) { - action_toggle_push(&(ActionToggleConfig) { - .impl = &s_airplane_mode_action_toggle_impl, - .set_exit_reason = true, - }); - app_event_loop(); -} - -const PebbleProcessMd *airplane_mode_toggle_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common = { - .main_func = &prv_main, - .uuid = AIRPLANE_MODE_TOGGLE_UUID, - .visibility = ProcessVisibilityQuickLaunch, - }, - .name = i18n_noop("Airplane Mode"), - }; - return &s_app_info.common; -} diff --git a/src/fw/apps/system_apps/toggle/airplane_mode.h b/src/fw/apps/system_apps/toggle/airplane_mode.h deleted file mode 100644 index 49581c9242..0000000000 --- a/src/fw/apps/system_apps/toggle/airplane_mode.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/app_manager.h" - -#define AIRPLANE_MODE_TOGGLE_UUID {0x88, 0xc2, 0x8c, 0x12, 0x7f, 0x81, 0x42, 0xdb, \ - 0xaa, 0xa6, 0x14, 0xcc, 0xef, 0x6f, 0x27, 0xe5} - -const PebbleProcessMd *airplane_mode_toggle_get_app_info(void); diff --git a/src/fw/apps/system_apps/toggle/motion_backlight.c b/src/fw/apps/system_apps/toggle/motion_backlight.c deleted file mode 100644 index 8d0eb1bebb..0000000000 --- a/src/fw/apps/system_apps/toggle/motion_backlight.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "motion_backlight.h" - -#include "applib/app.h" -#include "applib/ui/action_toggle.h" -#include "process_management/app_manager.h" -#include "services/common/i18n/i18n.h" -#include "shell/prefs.h" - -static bool prv_get_state(void *context) { - return backlight_is_motion_enabled(); -} - -static void prv_set_state(bool enabled, void *context) { - backlight_set_motion_enabled(enabled); -} - -static const ActionToggleImpl s_motion_backlight_action_toggle_impl = { - .window_name = "Motion Backlight Toggle", - .prompt_icon = RESOURCE_ID_BACKLIGHT, - .result_icon = RESOURCE_ID_BACKLIGHT, - .prompt_enable_message = i18n_noop("Turn On Motion Backlight?"), - .prompt_disable_message = i18n_noop("Turn Off Motion Backlight?"), - .result_enable_message = i18n_noop("Motion\nBacklight On"), - .result_disable_message = i18n_noop("Motion\nBacklight Off"), - .callbacks = { - .get_state = prv_get_state, - .set_state = prv_set_state, - }, -}; - -static void prv_main(void) { - action_toggle_push(&(ActionToggleConfig) { - .impl = &s_motion_backlight_action_toggle_impl, - .set_exit_reason = true, - }); - app_event_loop(); -} - -const PebbleProcessMd *motion_backlight_toggle_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common = { - .main_func = &prv_main, - .uuid = MOTION_BACKLIGHT_TOGGLE_UUID, - .visibility = ProcessVisibilityQuickLaunch, - }, - .name = i18n_noop("Motion Backlight"), - }; - return &s_app_info.common; -} diff --git a/src/fw/apps/system_apps/toggle/motion_backlight.h b/src/fw/apps/system_apps/toggle/motion_backlight.h deleted file mode 100644 index 63f0b2139e..0000000000 --- a/src/fw/apps/system_apps/toggle/motion_backlight.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/app_manager.h" - -#define MOTION_BACKLIGHT_TOGGLE_UUID {0xd4, 0xf7, 0xbe, 0x63, 0x97, 0xe6, 0x49, 0x52, \ - 0xb2, 0x65, 0xdd, 0x4b, 0xce, 0x11, 0xc1, 0x55} - -const PebbleProcessMd *motion_backlight_toggle_get_app_info(void); diff --git a/src/fw/apps/system_apps/toggle/quiet_time.c b/src/fw/apps/system_apps/toggle/quiet_time.c deleted file mode 100644 index a5ebe96680..0000000000 --- a/src/fw/apps/system_apps/toggle/quiet_time.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "quiet_time.h" - -#include "applib/app.h" -#include "applib/ui/action_toggle.h" -#include "process_management/app_manager.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/notifications/do_not_disturb_toggle.h" - -static void prv_main(void) { - do_not_disturb_toggle_push(ActionTogglePrompt_Auto, true /* set_exit_reason */); - app_event_loop(); -} - -const PebbleProcessMd *quiet_time_toggle_get_app_info(void) { - static const PebbleProcessMdSystem s_app_info = { - .common = { - .main_func = &prv_main, - .uuid = QUIET_TIME_TOGGLE_UUID, - .visibility = ProcessVisibilityQuickLaunch, - }, - .name = i18n_noop("Quiet Time"), - }; - return &s_app_info.common; -} diff --git a/src/fw/apps/system_apps/toggle/quiet_time.h b/src/fw/apps/system_apps/toggle/quiet_time.h deleted file mode 100644 index baab759f93..0000000000 --- a/src/fw/apps/system_apps/toggle/quiet_time.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/app_manager.h" - -#define QUIET_TIME_TOGGLE_UUID {0x22, 0x20, 0xd8, 0x05, 0xcf, 0x9a, 0x4e, 0x12, \ - 0x92, 0xb9, 0x5c, 0xa7, 0x78, 0xaf, 0xf6, 0xbb} - -const PebbleProcessMd *quiet_time_toggle_get_app_info(void); diff --git a/src/fw/apps/system_apps/watchfaces.h b/src/fw/apps/system_apps/watchfaces.h deleted file mode 100644 index 7f3ffc3e8b..0000000000 --- a/src/fw/apps/system_apps/watchfaces.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -#define WATCHFACES_APP_COLOR_PRIMARY GColorJazzberryJam - -const PebbleProcessMd* watchfaces_get_app_info(); diff --git a/src/fw/apps/system_apps/weather/weather_app.c b/src/fw/apps/system_apps/weather/weather_app.c deleted file mode 100644 index e4599928d5..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_app.h" -#include "weather_app_layout.h" -#include "weather_app_splash_screen.h" -#include "weather_app_warning_dialog.h" - -#include "applib/app.h" -#include "applib/event_service_client.h" -#include "applib/ui/click.h" -#include "applib/ui/content_indicator.h" -#include "applib/ui/ui.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_md.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/weather/weather_service.h" -#include "services/normal/weather/weather_types.h" -#include "util/array.h" -#include "util/attributes.h" -#include "util/list.h" -#include "util/math.h" - -typedef struct WeatherAppData { - Window window; - WeatherAppLayout layout; - WeatherDataListNode *forecasts_list_head; - size_t forecasts_count; - unsigned int current_forecast_index; - EventServiceInfo weather_event_info; - WeatherAppWarningDialog *warning_dialog; -} WeatherAppData; - -static bool prv_is_weather_forecast_recent(WeatherLocationForecast *forecast) { - if (!forecast) { - return false; - } - const time_t current_time_utc = rtc_get_time(); - const int recent_threshold_seconds = 5 * SECONDS_PER_HOUR / 2; // 2.5 hours - const int seconds_since_forecast_was_updated = current_time_utc - forecast->time_updated_utc; - return (seconds_since_forecast_was_updated < recent_threshold_seconds); -} - -static void prv_warning_dialog_dismiss_cb(void) { - WeatherAppData *data = app_state_get_user_data(); - data->warning_dialog = NULL; -} - -static void prv_show_warning_dialog(WeatherAppData *data, bool exit_on_pop, - const char *localized_text) { - if (data->warning_dialog) { - return; // only show one dialog at a time - } - if (exit_on_pop) { - bool animated = false; - app_window_stack_pop_all(animated); - } - data->warning_dialog = weather_app_warning_dialog_push(localized_text, - prv_warning_dialog_dismiss_cb); -} - -static void prv_handle_weather(PebbleEvent *unused_event, void *unused_context) { - // Unschedule any ongoing animations that would try to touch the weather data we're about to - // update - animation_unschedule_all(); - - size_t forecasts_count_out = 0; - WeatherDataListNode *forecasts_list_head = - weather_service_locations_list_create(&forecasts_count_out); - - WeatherAppData *data = app_state_get_user_data(); - weather_service_locations_list_destroy(data->forecasts_list_head); - WeatherAppLayout *layout = &data->layout; - if (forecasts_count_out > 0) { - weather_app_layout_set_data(layout, &forecasts_list_head->forecast); - const bool multiple_forecasts_exist = (forecasts_count_out > 1); - weather_app_layout_set_down_arrow_visible(layout, multiple_forecasts_exist); - - data->forecasts_list_head = forecasts_list_head; - // Only show the first forecast if the number of forecasts has differed between fetches. - // i.e. assume that the same number of forecasts means the locations have remained the same. - if (data->forecasts_count != forecasts_count_out) { - data->forecasts_count = forecasts_count_out; - data->current_forecast_index = 0; - } - } else { - /// Shown when there are no forecasts available to show the user - const char *warning_text = i18n_get("No location information available. To see weather, add "\ - "locations in your Pebble mobile app.", data); - const bool exit_on_pop = true; - prv_show_warning_dialog(data, exit_on_pop, warning_text); - weather_app_layout_set_down_arrow_visible(layout, false); - weather_app_layout_set_data(layout, NULL); - } -} - -static void prv_main_window_appear(Window *window) { - WeatherAppData *data = app_state_get_user_data(); - data->weather_event_info = (EventServiceInfo) { - .type = PEBBLE_WEATHER_EVENT, - .handler = prv_handle_weather, - }; - event_service_client_subscribe(&data->weather_event_info); -} - -static void prv_main_window_load(Window *window) { - WeatherAppData *data = app_state_get_user_data(); - layer_add_child(&window->layer, &data->layout.root_layer); -} - -static void prv_main_window_disappear(Window *window) { - WeatherAppData *data = app_state_get_user_data(); - event_service_client_unsubscribe(&data->weather_event_info); -} - -static void prv_up_down_click_handler(ClickRecognizerRef recognizer, void *context) { - WeatherAppData *data = app_state_get_user_data(); - const bool not_enough_items_to_scroll = (data->forecasts_count <= 1); - if (not_enough_items_to_scroll) { - return; - } - - const bool is_down_pressed = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_DOWN); - const int delta = is_down_pressed ? 1 : -1; - data->current_forecast_index = positive_modulo(data->current_forecast_index + delta, - data->forecasts_count); - - WeatherDataListNode *node = - weather_service_locations_list_get_location_at_index(data->forecasts_list_head, - data->current_forecast_index); - weather_app_layout_animate(&data->layout, &node->forecast, is_down_pressed); -} - -static void prv_main_window_click_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, prv_up_down_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_up_down_click_handler); -} - -static void prv_main_window_unload(Window *window) { - WeatherAppData *data = app_state_get_user_data(); - weather_app_layout_deinit(&data->layout); -} - -static NOINLINE void prv_init(void) { - WeatherAppData *data = app_zalloc_check(sizeof(WeatherAppData)); - app_state_set_user_data(data); - - Window *window = &data->window; - window_init(window, WINDOW_NAME("Weather")); - const WindowHandlers window_handlers = { - .appear = prv_main_window_appear, - .load = prv_main_window_load, - .disappear = prv_main_window_disappear, - .unload = prv_main_window_unload, - }; - window_set_window_handlers(window, &window_handlers); - - window_set_click_config_provider(window, prv_main_window_click_provider); - window_set_user_data(window, data); - - const GRect *layout_frame = &window->layer.bounds; - WeatherAppLayout *layout = &data->layout; - weather_app_layout_init(layout, layout_frame); - window_set_user_data(window, layout); - - // Fetch initial data - prv_handle_weather(NULL, NULL); - - if (data->forecasts_count == 0) { - return; - } - - const bool animated = true; - app_window_stack_push(&data->window, animated); - - // Request the default forecast separately instead of using the forecast list in `data` to avoid - // any potential race conditions - WeatherLocationForecast *default_forecast = weather_service_create_default_forecast(); - const bool is_default_forecast_data_recent = prv_is_weather_forecast_recent(default_forecast); - weather_service_destroy_default_forecast(default_forecast); - - // TODO PBL-38484: Consider using a different dialog for when data is stale but phone is connected - if (is_default_forecast_data_recent || connection_service_peek_pebble_app_connection()) { - const uint32_t splash_screen_timeout_ms = 500; - weather_app_splash_screen_push(splash_screen_timeout_ms); - } else { - /// Shown when there is no connection to the phone and the data that we have is not recent - const char *warning_text = i18n_get("Unable to connect. Your weather data may be out of date; "\ - "try checking the connection on your phone.", data); - const bool exit_on_pop = false; - prv_show_warning_dialog(data, exit_on_pop, warning_text); - } -} - -static void prv_deinit(void) { - WeatherAppData *data = app_state_get_user_data(); - i18n_free_all(data); -} - -static void prv_main(void) { - prv_init(); - app_event_loop(); - prv_deinit(); -} - -const PebbleProcessMd* weather_app_get_info() { - const bool is_visible_in_launcher = weather_service_supported_by_phone(); - - static const PebbleProcessMdSystem s_weather_app_info = { - .common = { - .main_func = prv_main, - .uuid = UUID_WEATHER_DATA_SOURCE, - }, - .name = i18n_noop("Weather"), -#if CAPABILITY_HAS_APP_GLANCES - .icon_resource_id = RESOURCE_ID_GENERIC_WEATHER_TINY, -#endif - }; - - return is_visible_in_launcher ? (const PebbleProcessMd *)&s_weather_app_info : NULL; -} diff --git a/src/fw/apps/system_apps/weather/weather_app.h b/src/fw/apps/system_apps/weather/weather_app.h deleted file mode 100644 index 034a114ff6..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* weather_app_get_info(); diff --git a/src/fw/apps/system_apps/weather/weather_app_layout.c b/src/fw/apps/system_apps/weather/weather_app_layout.c deleted file mode 100644 index ad125a57e3..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app_layout.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_app_layout.h" - -#include "applib/fonts/fonts.h" -#include "applib/graphics/gcontext.h" -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/gpath.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/graphics_circle.h" -#include "applib/graphics/gtypes.h" -#include "applib/graphics/text.h" -#include "applib/ui/animation.h" -#include "applib/ui/animation_interpolate.h" -#include "applib/ui/animation_timing.h" -#include "applib/ui/content_indicator.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/kino/kino_reel/morph_square.h" -#include "applib/ui/kino/kino_reel/transform.h" -#include "applib/ui/window.h" -#include "apps/system_apps/timeline/text_node.h" -#include "font_resource_keys.auto.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/timeline_resources.h" -#include "services/normal/weather/weather_service.h" -#include "services/normal/weather/weather_types.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" -#include "util/trig.h" - -#include - -#define WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT (18) -#define WEATHER_APP_LAYOUT_TOP_PADDING PBL_IF_RECT_ELSE(0, 15) -#define WEATHER_APP_LAYOUT_TIMELINE_ICON_RESOURCE_SIZE (TimelineResourceSizeTiny) -#define WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET PBL_IF_RECT_ELSE(3, 23) - -static int prv_draw_text(GPoint offset, int max_width, GContext *context, - const char *text, const GFont font, - GColor font_color, GTextAlignment alignment) { - const int height = fonts_get_font_height(font); - const GRect box = (GRect) {offset, GSize(max_width, height)}; - - graphics_context_set_text_color(context, font_color); - graphics_draw_text(context, text, font, box, GTextOverflowModeFill, alignment, NULL); - - return height; -} - -static void prv_draw_weather_background(const GRect *circle_bounding_box, GContext *context, - GColor background_color) { - if (!gcolor_is_invisible(background_color)) { - graphics_context_set_fill_color(context, background_color); - graphics_fill_oval(context, *circle_bounding_box, GOvalScaleModeFitCircle); - } -} - -static void prv_fill_high_low_temp_buffer(const int high, const int low, char *buffer, - const size_t buffer_size, const void *i18n_owner) { - if ((high == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) && - (low == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP)) { - /// Shown when neither high nor low temperature is known - const char *both_temps_no_data = i18n_get("--° / --°", i18n_owner); - strncpy(buffer, both_temps_no_data, strlen(both_temps_no_data)); - buffer[buffer_size - 1] = '\0'; - } else if (low == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { - /// Shown when only the day's high temperature is known, (e.g. "68° / --°") - snprintf(buffer, buffer_size, i18n_get("%i° / --°", i18n_owner), high); - } else if (high == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { - /// Shown when only the day's low temperature is known (e.g. "--° / 52°") - snprintf(buffer, buffer_size, i18n_get("--° / %i°", i18n_owner), low); - } else { - /// A day's high and low temperature, separated by a foward slash (e.g. "68° / 52°") - snprintf(buffer, buffer_size, i18n_get("%i° / %i°", i18n_owner), high, low); - } -} - -#define GPS_ARROW_WIDTH (12) -#define GPS_ARROW_HEIGHT (14) - -static const GPoint s_gps_arrow_path_points[] = { - {0, GPS_ARROW_HEIGHT}, - {(GPS_ARROW_WIDTH / 2), 0}, - {GPS_ARROW_WIDTH, GPS_ARROW_HEIGHT}, - // This 6/7 height ratio for the arrow notch achieves the design spec - {(GPS_ARROW_WIDTH / 2), (GPS_ARROW_HEIGHT * 6 / 7)} -}; - -static void prv_draw_gps_arrow_node_callback(GContext *ctx, const GRect *rect, - PBL_UNUSED const GTextNodeDrawConfig *config, bool render, - GSize *size_out, PBL_UNUSED void *user_data) { - GPath gps_arrow_path = (GPath) { - .num_points = ARRAY_LENGTH(s_gps_arrow_path_points), - .points = (GPoint *)s_gps_arrow_path_points, - .offset = rect->origin, - // Ideal rotation would be 45 degrees, but the shape of the arrow matches the design best at - // 38 degrees - .rotation = DEG_TO_TRIGANGLE(38), - }; - - if (render) { - graphics_context_set_fill_color(ctx, GColorBlack); - gpath_draw_filled(ctx, &gps_arrow_path); - } - if (size_out) { - // Note that gpath_outer_rect() doesn't take into account the rotation; we'll add margin to the - // location text node to account for it - *size_out = gpath_outer_rect(&gps_arrow_path).size; - } -} - -static GTextNode *prv_create_location_name_area_node(const WeatherLocationForecast *forecast, - GFont location_font) { - const GTextAlignment location_name_alignment = PBL_IF_RECT_ELSE(GTextAlignmentLeft, - GTextAlignmentCenter); - - // One node for the location name text and one node for the possible GPS arrow - const size_t max_nodes = 2; - GTextNodeHorizontal *horizontal_node = graphics_text_node_create_horizontal(max_nodes); - horizontal_node->horizontal_alignment = location_name_alignment; - - GTextNodeText *location_text_node = graphics_text_node_create_text(0); - location_text_node->text = forecast->location_name; - location_text_node->font = location_font; - location_text_node->color = GColorBlack; - location_text_node->overflow = GTextOverflowModeTrailingEllipsis; - if (forecast->is_current_location) { - // Horizontal spacing between location name and GPS arrow is spec'd by design to be 11 pixels - location_text_node->node.margin = GSize(11, 0); - } - graphics_text_node_container_add_child(&horizontal_node->container, &location_text_node->node); - - if (forecast->is_current_location) { - GTextNodeCustom *arrow_node = graphics_text_node_create_custom(prv_draw_gps_arrow_node_callback, - NULL); - arrow_node->node.offset = - GPoint(0, (int16_t)(fonts_get_font_cap_offset(location_font) / 2)); - graphics_text_node_container_add_child(&horizontal_node->container, &arrow_node->node); - } - - return &horizontal_node->container.node; -} - -static GSize prv_draw_location_name_area(GPoint offset, int max_width, GContext *ctx, - GFont location_font, - const WeatherLocationForecast *forecast) { - GTextNode *location_name_area_node = prv_create_location_name_area_node(forecast, location_font); - - GRect location_name_area_rect = (GRect) { - .origin = offset, - .size = GSize(max_width, fonts_get_font_height(location_font)), - }; - -#if PBL_ROUND - // On round the location name text and arrow can be obscured by the edges of the bezel, so we - // horizontally inset the rectangle by a few pixels - const int16_t horizontal_inset = 5; - location_name_area_rect = grect_inset(location_name_area_rect, - GEdgeInsets(0, horizontal_inset, 0)); -#endif - - GSize location_name_area_size; - graphics_text_node_draw(location_name_area_node, ctx, &location_name_area_rect, NULL, - &location_name_area_size); - graphics_text_node_destroy(location_name_area_node); - return location_name_area_size; -} -// All text before the separator -static void prv_draw_top_half_text(const WeatherAppLayout *layout, GPoint *current_offset, - int content_width, - GContext *context) { - const WeatherLocationForecast *forecast = layout->forecast; - - current_offset->y += prv_draw_location_name_area(*current_offset, content_width, context, - layout->location_font, forecast).h; - - const int location_and_today_temperature_vertical_spacing = 7; - current_offset->y += location_and_today_temperature_vertical_spacing; - - char text_buffer[15] = {0}; - const size_t max_text_buff_size = ARRAY_LENGTH(text_buffer); - - if (forecast->current_temp == WEATHER_SERVICE_LOCATION_FORECAST_UNKNOWN_TEMP) { - /// Shown when today's current temperature is unknown - const char *unknown_temp_string = i18n_get("--°", layout); - strncpy(text_buffer, unknown_temp_string, strlen(unknown_temp_string)); - text_buffer[max_text_buff_size - 1] = '\0'; - } else { - /// Today's current temperature (e.g. "68°") - snprintf(text_buffer, max_text_buff_size, i18n_get("%i°", layout), forecast->current_temp); - } - current_offset->y += prv_draw_text(*current_offset, content_width, context, text_buffer, - layout->temperature_font, GColorBlack, GTextAlignmentLeft); - - prv_fill_high_low_temp_buffer(forecast->today_high, forecast->today_low, text_buffer, - max_text_buff_size, layout); - current_offset->y += prv_draw_text(*current_offset, content_width, context, text_buffer, - layout->high_low_phrase_font, GColorBlack, - GTextAlignmentLeft); - const int today_high_low_gap_vertical_spacing_reduction = 2; - current_offset->y -= today_high_low_gap_vertical_spacing_reduction; - - current_offset->y += prv_draw_text(*current_offset, content_width, context, - forecast->current_weather_phrase, layout->high_low_phrase_font, - GColorBlack, GTextAlignmentLeft); -} - -// All text after the separator -static void prv_draw_bottom_half_text(const WeatherAppLayout *layout, GPoint *current_offset, - int content_width, GContext *context) { - const WeatherLocationForecast *forecast = layout->forecast; - - const int separator_tomorrow_title_vertical_spacing = 6; - current_offset->y += separator_tomorrow_title_vertical_spacing; - current_offset->y += prv_draw_text(*current_offset, content_width, context, - /// Refers to the weather conditions for tomorrow - i18n_get("TOMORROW", layout), layout->tomorrow_font, - GColorBlack, GTextAlignmentLeft); - char text_buffer[15] = {0}; - const size_t max_text_buff_size = ARRAY_LENGTH(text_buffer); - prv_fill_high_low_temp_buffer(forecast->tomorrow_high, forecast->tomorrow_low, text_buffer, - max_text_buff_size, layout); - prv_draw_text(*current_offset, content_width, context, text_buffer, layout->high_low_phrase_font, - GColorBlack, GTextAlignmentLeft); -} - -static void prv_draw_weather_icon_backgrounds(const WeatherAppLayout *layout, - const GRect *content_bounds, GContext *context) { - const WeatherLocationForecast *forecast = layout->forecast; - // assume that both current and tomorrow weather icons are the same size - const GSize icon_size = layout->current_weather_icon_layer.layer.bounds.size; - const unsigned int weather_icon_bg_circle_diam = integer_sqrt(2 * icon_size.w * icon_size.h); - - const int today_weather_bg_circle_top_margin = 28; - GRect bg_circle_bounding_box = GRect(grect_get_max_x(content_bounds) - - weather_icon_bg_circle_diam, - today_weather_bg_circle_top_margin, - weather_icon_bg_circle_diam, weather_icon_bg_circle_diam); - - prv_draw_weather_background(&bg_circle_bounding_box, context, - weather_type_get_bg_color(forecast->current_weather_type)); - - const int weather_bg_circle_vertical_spacing = 40; - bg_circle_bounding_box.origin.y += weather_icon_bg_circle_diam + - weather_bg_circle_vertical_spacing; - prv_draw_weather_background(&bg_circle_bounding_box, context, - weather_type_get_bg_color(forecast->tomorrow_weather_type)); -} - - -static void prv_render_layout(Layer *layer, GContext *context) { - // "Content" refers to everything except the dot separator - const GRect content_bounds = - grect_inset(layer->bounds, GEdgeInsets(0, WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET, - 0)); - const int content_x_offset = content_bounds.origin.x; - const int content_width = content_bounds.size.w; - - const WeatherAppLayout *layout = window_get_user_data(layer_get_window(layer)); - const WeatherLocationForecast *forecast = layout->forecast; - - if (!forecast) { - // Nothing to draw. - return; - } - // start at 1 from the top to match design docs - GPoint current_offset = GPoint(content_x_offset, 1); - GPoint *offset = ¤t_offset; - - prv_draw_top_half_text(layout, offset, content_width, context); - - // dotted separator - const int phrase_separator_vertical_spacing = 10; - current_offset.y += phrase_separator_vertical_spacing; - - const GPoint separator_start = GPoint(0, current_offset.y); - graphics_context_set_stroke_width(context, 5); - graphics_context_set_stroke_color(context, PBL_IF_COLOR_ELSE(GColorLightGray, GColorBlack)); - graphics_draw_horizontal_line_dotted(context, separator_start, - layer->bounds.size.w); - - if (!layout->animation_state.hide_bottom_half_text) { - prv_draw_bottom_half_text(layout, offset, content_width, context); - } - prv_draw_weather_icon_backgrounds(layout, &content_bounds, context); -} - -static void prv_content_indicator_setup_direction(ContentIndicator *content_indicator, - Layer *indicator_layer, - ContentIndicatorDirection direction) { - content_indicator_configure_direction(content_indicator, direction, &(ContentIndicatorConfig) { - .layer = indicator_layer, - .colors.foreground = GColorBlack, - .colors.background = GColorLightGray, - }); -} - -void weather_app_layout_init(WeatherAppLayout *layout, const GRect *frame) { - layout->location_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - layout->temperature_font = fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM); - layout->high_low_phrase_font = fonts_get_system_font(FONT_KEY_GOTHIC_18); - layout->tomorrow_font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); - - Layer *root_layer = &layout->root_layer; - layer_init(root_layer, frame); - - Layer *down_arrow_layer = &layout->down_arrow_layer; - const GRect down_arrow_layer_frame = grect_inset( - *frame, GEdgeInsets(frame->size.h - WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT, 0, 0)); - layer_init(down_arrow_layer, &down_arrow_layer_frame); - layer_add_child(root_layer, down_arrow_layer); - - const int content_layer_side_padding = PBL_IF_RECT_ELSE(5, 12); - const GRect content_layer_frame = grect_inset(*frame, - GEdgeInsets(WEATHER_APP_LAYOUT_TOP_PADDING, - content_layer_side_padding, - WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT)); - - Layer *content_layer = &layout->content_layer; - layer_init(content_layer, &content_layer_frame); - layer_set_update_proc(content_layer, prv_render_layout); - layer_add_child(root_layer, content_layer); - - ContentIndicator *content_indicator = &layout->content_indicator; - content_indicator_init(content_indicator); - - prv_content_indicator_setup_direction(content_indicator, down_arrow_layer, - ContentIndicatorDirectionDown); - - const GSize icon_size = - timeline_resources_get_gsize(WEATHER_APP_LAYOUT_TIMELINE_ICON_RESOURCE_SIZE); - - const int icon_layer_margin_top = PBL_IF_RECT_ELSE(33, 18); - const int icon_layer_right_margin = 5; - GRect icon_layer_frame = (GRect) { - { - content_layer_frame.size.w - icon_size.w - WEATHER_APP_LAYOUT_CONTENT_LAYER_HORIZONTAL_INSET - - icon_layer_right_margin, - content_layer_frame.origin.y + icon_layer_margin_top - }, - icon_size - }; - - KinoLayer *current_weather_icon_layer = &layout->current_weather_icon_layer; - kino_layer_init(current_weather_icon_layer, &icon_layer_frame); - layer_add_child(content_layer, kino_layer_get_layer(current_weather_icon_layer)); - - const int icon_layer_spacing = 50; - icon_layer_frame.origin.y += icon_size.h + icon_layer_spacing; - - KinoLayer *tomorrow_weather_icon_layer = &layout->tomorrow_weather_icon_layer; - kino_layer_init(tomorrow_weather_icon_layer, &icon_layer_frame); - layer_add_child(content_layer, kino_layer_get_layer(tomorrow_weather_icon_layer)); -} - -static uint32_t prv_get_resource_id_for_weather_type(WeatherType type) { - const TimelineResourceInfo timeline_res = { - .res_id = weather_type_get_timeline_resource_id(type), - }; - AppResourceInfo icon_res_info; - timeline_resources_get_id(&timeline_res, WEATHER_APP_LAYOUT_TIMELINE_ICON_RESOURCE_SIZE, - &icon_res_info); - return icon_res_info.res_id; -} - -void weather_app_layout_set_data(WeatherAppLayout *layout, - const WeatherLocationForecast *forecast) { - layout->forecast = forecast; - - const uint32_t current_weather_res_id = forecast ? - prv_get_resource_id_for_weather_type(forecast->current_weather_type) : RESOURCE_ID_INVALID; - const uint32_t tomorrow_weather_res_id = forecast ? - prv_get_resource_id_for_weather_type(forecast->tomorrow_weather_type) : RESOURCE_ID_INVALID; - - kino_layer_set_reel_with_resource(&layout->current_weather_icon_layer, current_weather_res_id); - kino_layer_set_reel_with_resource(&layout->tomorrow_weather_icon_layer, tomorrow_weather_res_id); - - layer_mark_dirty(&layout->root_layer); -} - -void weather_app_layout_set_down_arrow_visible(WeatherAppLayout *layout, bool is_down_visible) { - content_indicator_set_content_available(&layout->content_indicator, ContentIndicatorDirectionDown, - is_down_visible); -} - -void weather_app_layout_deinit(WeatherAppLayout *layout) { - i18n_free_all(layout); - layer_deinit(&layout->root_layer); -} - -// Down arrow layer grows until a point, after which the entire content teleports to a height -// slightly higher than its resting position, then relaxes into place -static void prv_down_animation_update(Animation *animation, AnimationProgress normalized) { - WeatherAppLayout *layout = animation_get_context(animation); - // Progress at which to switch from the down arrow growing to entire content relaxing downwards - const AnimationProgress animation_cut_frame_progress = - (interpolate_moook_in_duration() * ANIMATION_NORMALIZED_MAX) / interpolate_moook_duration(); - // Progress at which to hide "TOMORROW" and tomorrow high / low temperature text - const AnimationProgress animation_hide_bottom_half_text_progress = - (animation_cut_frame_progress * 2) / 3; - - int down_arrow_layer_height = WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT; - layout->animation_state.hide_bottom_half_text = false; - - if (normalized <= animation_cut_frame_progress) { - if (normalized >= animation_hide_bottom_half_text_progress) { - layout->animation_state.hide_bottom_half_text = true; - } - // renormalize the progress so that interpolate_moook_in_only works as expected - int32_t new_normalized = animation_timing_scaled(normalized, ANIMATION_NORMALIZED_MIN, - animation_cut_frame_progress); - const int additional_down_arrow_height = 25; - // grow the down arrow layer - down_arrow_layer_height += interpolate_moook_in_only(new_normalized, 0, - additional_down_arrow_height); - } else { - // We've cut, so display the next forecast's data - if (layout->animation_state.next_forecast) { - weather_app_layout_set_data(layout, layout->animation_state.next_forecast); - layout->animation_state.next_forecast = NULL; - } - int32_t new_normalized = animation_timing_scaled(normalized, animation_cut_frame_progress, - ANIMATION_NORMALIZED_MAX); - - // Relax the content by changing its top margin - const int animation_margin_top_from = WEATHER_APP_LAYOUT_TOP_PADDING - PBL_IF_RECT_ELSE(10, 15); - const int animation_margin_top_to = WEATHER_APP_LAYOUT_TOP_PADDING; - const int num_frames_from = 1; - const bool bounce_back = false; - int animation_margin_top = interpolate_moook_out(new_normalized, animation_margin_top_from, - animation_margin_top_to, num_frames_from, - bounce_back); - layout->content_layer.frame.origin.y = animation_margin_top; - - // The down arrow's height follows the content margin. It starts off large, then goes back to - // its original size, as the content relaxes into place - down_arrow_layer_height += (-animation_margin_top + WEATHER_APP_LAYOUT_TOP_PADDING); - } - - const GRect down_arrow_layer_frame = grect_inset(layout->root_layer.frame, - GEdgeInsets(layout->root_layer.frame.size.h - down_arrow_layer_height, 0, 0)); - layer_set_frame(&layout->down_arrow_layer, &down_arrow_layer_frame); - - layer_mark_dirty(&layout->root_layer); -} - -// moves the entire root layer up back into place -static void prv_up_animation_update(Animation *animation, AnimationProgress normalized) { - const int root_layer_top_margin_from = (WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT * 2) / 3; - const int root_layer_top_margin_to = 0; - const int num_frames_from = 1; - const bool bounce_back = false; - int root_layer_top_margin = interpolate_moook_out(normalized, root_layer_top_margin_from, - root_layer_top_margin_to, num_frames_from, bounce_back); - - WeatherAppLayout *layout = animation_get_context(animation); - if (layout->animation_state.next_forecast) { - layout->forecast = layout->animation_state.next_forecast; - layout->animation_state.next_forecast = NULL; - } - - layout->root_layer.frame.origin.y = root_layer_top_margin; - layer_set_frame(&layout->root_layer, &layout->root_layer.frame); -} - -static void prv_animation_stopped(Animation *animation, bool finished, void *context) { - WeatherAppLayout *layout = context; - if (layout->animation_state.next_forecast) { - weather_app_layout_set_data(layout, layout->animation_state.next_forecast); - layout->animation_state.next_forecast = NULL; - } - layout->animation_state.hide_bottom_half_text = false; - - GRect *root_layer_frame = &layout->root_layer.frame; - root_layer_frame->origin.y = 0; - layer_set_frame(&layout->root_layer, root_layer_frame); - - GRect *content_layer_frame = &layout->content_layer.frame; - content_layer_frame->origin.y = WEATHER_APP_LAYOUT_TOP_PADDING; - layer_set_frame(&layout->content_layer, content_layer_frame); - - const GRect down_arrow_layer_frame = grect_inset(*root_layer_frame, - GEdgeInsets(root_layer_frame->size.h - WEATHER_APP_LAYOUT_ARROW_LAYER_HEIGHT, 0, 0)); - layer_set_frame(&layout->down_arrow_layer, &down_arrow_layer_frame); -} - -static const AnimationImplementation s_down_animation_implementation = { - .update = &prv_down_animation_update, -}; - -static const AnimationImplementation s_up_animation_implementation = { - .update = &prv_up_animation_update, -}; - -static const AnimationHandlers s_animation_handlers = { - .stopped = &prv_animation_stopped, -}; - -static void prv_morph_weather_icons(KinoLayer *icon_layer, WeatherType from, WeatherType to, - uint32_t duration) { - uint32_t from_image_res_id = - prv_get_resource_id_for_weather_type(from); - KinoReel *from_reel = kino_reel_create_with_resource(from_image_res_id); - KinoReel *to_reel = kino_reel_create_with_resource(prv_get_resource_id_for_weather_type(to)); - - KinoReel *icon_reel = kino_reel_morph_square_create(from_reel, true); - kino_reel_transform_set_to_reel(icon_reel, to_reel, true); - kino_reel_transform_set_transform_duration(icon_reel, duration); - - kino_layer_set_reel(icon_layer, icon_reel, true); - kino_layer_play(icon_layer); -} - -void weather_app_layout_animate(WeatherAppLayout *layout, WeatherLocationForecast *new_forecast, - bool animate_down) { - animation_unschedule_all(); - - const uint32_t anim_duration = animate_down ? interpolate_moook_duration() : - interpolate_moook_out_duration(); - layout->animation_state.next_forecast = new_forecast; - Animation *animation = animation_create(); - animation_set_duration(animation, anim_duration); - InterpolateInt64Function interpolation = animate_down ? interpolate_moook : - interpolate_moook_in_only; - animation_set_custom_interpolation(animation, interpolation); - animation_set_handlers(animation, s_animation_handlers, layout); - const AnimationImplementation *implementation = animate_down ? &s_down_animation_implementation : - &s_up_animation_implementation; - animation_set_implementation(animation, implementation); - animation_schedule(animation); - - prv_morph_weather_icons(&layout->current_weather_icon_layer, - layout->forecast->current_weather_type, - new_forecast->current_weather_type, anim_duration); - - prv_morph_weather_icons(&layout->tomorrow_weather_icon_layer, - layout->forecast->tomorrow_weather_type, - new_forecast->tomorrow_weather_type, anim_duration); -} diff --git a/src/fw/apps/system_apps/weather/weather_app_layout.h b/src/fw/apps/system_apps/weather/weather_app_layout.h deleted file mode 100644 index 6dd1e5e103..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app_layout.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gdraw_command_image.h" -#include "applib/ui/content_indicator_private.h" -#include "applib/ui/kino/kino_layer.h" -#include "services/normal/weather/weather_service.h" - -typedef struct WeatherAppLayout { - Layer root_layer; - Layer content_layer; - KinoLayer current_weather_icon_layer; - KinoLayer tomorrow_weather_icon_layer; - const WeatherLocationForecast *forecast; - GFont location_font; - GFont temperature_font; - GFont high_low_phrase_font; - GFont tomorrow_font; - Layer down_arrow_layer; - ContentIndicator content_indicator; - struct { // used during animations - const WeatherLocationForecast *next_forecast; - bool hide_bottom_half_text; - } animation_state; -} WeatherAppLayout; - -void weather_app_layout_init(WeatherAppLayout *layout, const GRect *frame); - -void weather_app_layout_set_data(WeatherAppLayout *layout, - const WeatherLocationForecast *forecast); - -void weather_app_layout_set_down_arrow_visible(WeatherAppLayout *layout, bool is_down_visible); - -void weather_app_layout_deinit(WeatherAppLayout *layout); - -void weather_app_layout_animate(WeatherAppLayout *layout, WeatherLocationForecast *new_forecast, - bool animate_down); diff --git a/src/fw/apps/system_apps/weather/weather_app_splash_screen.c b/src/fw/apps/system_apps/weather/weather_app_splash_screen.c deleted file mode 100644 index ce3ca0a100..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app_splash_screen.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_app_splash_screen.h" - -#include "applib/app.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" - -typedef struct SplashScreenData { - Window window; - KinoLayer logo_layer; - AppTimer *timer; - uint32_t timeout_ms; -} SplashScreenData; - -static void prv_splash_screen_finished_callback(void *cb_data) { - SplashScreenData *data = cb_data; - data->timer = NULL; - const bool animated = false; - app_window_stack_remove(&data->window, animated); -} - -static void prv_window_unload(Window *window) { - SplashScreenData *data = window_get_user_data(window); - kino_layer_deinit(&data->logo_layer); - // Execute conditional only if the user presses back while the splash screen is showing - if (data->timer) { - app_timer_cancel(data->timer); - const bool animated = true; - app_window_stack_pop_all(animated); - } - app_free(data); -} - -static void prv_window_load(Window *window) { - SplashScreenData *data = window_get_user_data(window); - Layer *window_root_layer = &window->layer; - KinoLayer *logo_layer = &data->logo_layer; - kino_layer_init(logo_layer, &window_root_layer->bounds); - kino_layer_set_reel_with_resource(logo_layer, - RESOURCE_ID_WEATHER_CHANNEL_LOGO); - layer_add_child(window_root_layer, - kino_layer_get_layer(logo_layer)); - - data->timer = app_timer_register(data->timeout_ms, - prv_splash_screen_finished_callback, - data); -} - -void weather_app_splash_screen_push(uint32_t timeout_ms) { - SplashScreenData *data = app_zalloc_check(sizeof(SplashScreenData)); - data->timeout_ms = timeout_ms; - - Window *window = &data->window; - window_init(window, WINDOW_NAME("Weather - Splash Screen")); - - const GColor background_color = PBL_IF_COLOR_ELSE(GColorBlue, GColorBlack); - window_set_background_color(window, background_color); - - const WindowHandlers window_handlers = { - .load = prv_window_load, - .unload = prv_window_unload, - }; - window_set_window_handlers(window, &window_handlers); - window_set_user_data(window, data); - - const bool animated = false; - app_window_stack_push(window, animated); -} diff --git a/src/fw/apps/system_apps/weather/weather_app_splash_screen.h b/src/fw/apps/system_apps/weather/weather_app_splash_screen.h deleted file mode 100644 index d207e05404..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app_splash_screen.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/ui.h" - -void weather_app_splash_screen_push(uint32_t timeout_ms); diff --git a/src/fw/apps/system_apps/weather/weather_app_warning_dialog.c b/src/fw/apps/system_apps/weather/weather_app_warning_dialog.c deleted file mode 100644 index 0a8c84328c..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app_warning_dialog.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_app_warning_dialog.h" - -#include "applib/ui/app_window_stack.h" -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/expandable_dialog.h" -#include "kernel/pbl_malloc.h" - -typedef struct WeatherAppWarningDialogData { - WeatherAppWarningDialogDismissedCallback dismissed_cb; -} WeatherAppWarningDialogData; - -static void prv_warning_dialog_unload(void *context) { - WeatherAppWarningDialogData *data = context; - if (data->dismissed_cb) { - data->dismissed_cb(); - } - task_free(data); -} - -static void prv_warning_dialog_select_handler(ClickRecognizerRef recognizer, void *context) { - ExpandableDialog *expandable_dialog = context; - expandable_dialog_pop(expandable_dialog); -} - -WeatherAppWarningDialog *weather_app_warning_dialog_push(const char *localized_string, - WeatherAppWarningDialogDismissedCallback dismissed_cb) { - WeatherAppWarningDialogData *data = task_zalloc_check(sizeof(WeatherAppWarningDialogData)); - ExpandableDialog *expandable_dialog = expandable_dialog_create("Weather - warning dialog"); - - Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog); - dialog_set_destroy_on_pop(dialog, false); - dialog_set_icon(dialog, RESOURCE_ID_GENERIC_WARNING_TINY); - dialog_set_text(dialog, localized_string); - const DialogCallbacks callbacks = { - .unload = prv_warning_dialog_unload, - }; - dialog_set_callbacks(dialog, &callbacks, data); - - expandable_dialog_show_action_bar(expandable_dialog, true); - expandable_dialog_set_select_action(expandable_dialog, RESOURCE_ID_ACTION_BAR_ICON_CHECK, - prv_warning_dialog_select_handler); - - data->dismissed_cb = dismissed_cb; - app_expandable_dialog_push(expandable_dialog); - - return expandable_dialog; -} diff --git a/src/fw/apps/system_apps/weather/weather_app_warning_dialog.h b/src/fw/apps/system_apps/weather/weather_app_warning_dialog.h deleted file mode 100644 index b5003d96ad..0000000000 --- a/src/fw/apps/system_apps/weather/weather_app_warning_dialog.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/dialogs/expandable_dialog.h" - -#include - -typedef ExpandableDialog WeatherAppWarningDialog; - -typedef void (*WeatherAppWarningDialogDismissedCallback)(void); - -WeatherAppWarningDialog *weather_app_warning_dialog_push(const char *localized_string, - WeatherAppWarningDialogDismissedCallback dismissed_cb); diff --git a/src/fw/apps/system_apps/workout/sports.h b/src/fw/apps/system_apps/workout/sports.h deleted file mode 100644 index 4a3219ffc4..0000000000 --- a/src/fw/apps/system_apps/workout/sports.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd *sports_app_get_info(void); diff --git a/src/fw/apps/system_apps/workout/workout.h b/src/fw/apps/system_apps/workout/workout.h deleted file mode 100644 index 1fbd9bb405..0000000000 --- a/src/fw/apps/system_apps/workout/workout.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -typedef enum { - WorkoutLaunchArg_EndWorkout = 1, -} WorkoutLaunchArg; - -void workout_push_summary_window(void); - -//! Call for system to obtain information about the application -//! @return System information about the app -const PebbleProcessMd *workout_app_get_info(void); diff --git a/src/fw/apps/system_apps/workout/workout_active.c b/src/fw/apps/system_apps/workout/workout_active.c deleted file mode 100644 index e9274103d2..0000000000 --- a/src/fw/apps/system_apps/workout/workout_active.c +++ /dev/null @@ -1,1068 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_active.h" -#include "workout_dialog.h" -#include "workout_summary.h" -#include "workout.h" - -#include "applib/app.h" -#include "applib/ui/action_menu_hierarchy.h" -#include "applib/ui/action_menu_window.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/ui.h" -#include "applib/ui/window_manager.h" -#include "apps/system_apps/timeline/text_node.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/health_util.h" -#include "services/normal/activity/hr_util.h" -#include "services/normal/activity/workout_service.h" -#include "system/logging.h" -#include "util/size.h" - -#include - -#define TEXT_COLOR (GColorBlack) -#define TEXT_ALIGNMENT (PBL_IF_RECT_ELSE(GTextAlignmentLeft, GTextAlignmentRight)) -#define BACKGROUND_COLOR PBL_IF_COLOR_ELSE(GColorYellow, GColorWhite) - -typedef enum WorkoutLayout { - WorkoutLayout_SingleMetric, - WorkoutLayout_StaticAndScrollable, - WorkoutLayout_TwoStaticAndScrollable, -} WorkoutLayout; - -typedef struct WorkoutActiveWindow { - Window window; - ActionBarLayer action_bar; - StatusBarLayer status_layer; - Layer base_layer; - Layer top_metric_layer; - Layer middle_metric_layer; - Layer scrollable_metric_layer; - WorkoutDialog end_workout_dialog; - - ButtonId pause_button; - - WorkoutController *workout_controller; - void *workout_data; - - WorkoutLayout layout; - - WorkoutMetricType top_metric; - WorkoutMetricType middle_metric; - - int num_scrollable_metrics; - int current_scrollable_metric; - WorkoutMetricType scrollable_metrics[WorkoutMetricTypeCount]; - - GBitmap *heart_icon; - GBitmap *hr_measuring_icon; - - GBitmap *action_bar_start; - GBitmap *action_bar_pause; - GBitmap *action_bar_stop; - GBitmap *action_bar_more; - GBitmap *action_bar_next; - - AppTimer *update_timer; - AppTimer *hr_measuring_timer; - - int cur_hr_measuring_width_idx; -} WorkoutActiveWindow; - -static const int s_hr_measuring_widths[] = {36, 0, 24, 28, 32}; - -static void prv_draw_heart_node_callback(GContext *ctx, const GRect *box, - const GTextNodeDrawConfig *config, bool render, - GSize *size_out, void *user_data); - -static void prv_draw_hr_measuring_node_callback(GContext *ctx, const GRect *box, - const GTextNodeDrawConfig *config, bool render, - GSize *size_out, void *user_data); - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Helpers - -static void prv_add_scrollable_metrics(WorkoutActiveWindow *active_window, - int num_scrollable_metrics, - WorkoutMetricType *metrics) { - for (int i = 0; i < num_scrollable_metrics; i++) { - active_window->scrollable_metrics[active_window->num_scrollable_metrics++] = metrics[i]; - } -} - -static const char* prv_get_label_for_hr_metric(int bpm) { - switch (hr_util_get_hr_zone(bpm)) { - case HRZone_Zone1: - /// Zone 1 HR Label - return i18n_noop("FAT BURN"); - case HRZone_Zone2: - /// Zone 2 HR Label - return i18n_noop("ENDURANCE"); - case HRZone_Zone3: - /// Zone 3 HR Label - return i18n_noop("PERFORMANCE"); - default: - /// Default/Zone 0 HR Label - return i18n_noop("HEART RATE"); - } -} - -static const char* prv_get_label_for_metric(WorkoutMetricType metric_type, - WorkoutActiveWindow *active_window) { - switch (metric_type) { - case WorkoutMetricType_Hr: - { - int bpm = active_window->workout_controller->get_metric_value(WorkoutMetricType_Hr, - active_window->workout_data); - return prv_get_label_for_hr_metric(bpm); - } - case WorkoutMetricType_Custom: - /// Custom Label from Sports App - return active_window->workout_controller->get_custom_metric_label_string(); - case WorkoutMetricType_Duration: - /// Duration Label - return i18n_noop("DURATION"); - case WorkoutMetricType_AvgPace: -#if PBL_RECT - /// Average Pace Label - return i18n_noop("AVG PACE"); -#else - /// Average Pace Label with units - return active_window->workout_controller->get_distance_string(i18n_noop("AVG PACE (/MI)"), - i18n_noop("AVG PACE (/KM)")); -#endif - case WorkoutMetricType_Pace: -#if PBL_RECT - /// Pace Label - return i18n_noop("PACE"); -#else - /// Pace Label with units - return active_window->workout_controller->get_distance_string(i18n_noop("PACE (/MI)"), - i18n_noop("PACE (/KM)")); -#endif - case WorkoutMetricType_Speed: -#if PBL_RECT - /// Speed Label - return i18n_noop("SPEED"); -#else - /// Speed Label with units - return active_window->workout_controller->get_distance_string(i18n_noop("SPEED (MPH)"), - i18n_noop("SPEED (KM/H)")); -#endif - case WorkoutMetricType_Distance: -#if PBL_RECT - /// Distance Label - return i18n_noop("DISTANCE"); -#else - /// Distance Label with units - return active_window->workout_controller->get_distance_string(i18n_noop("DISTANCE (MI)"), - i18n_noop("DISTANCE (KM)")); -#endif - case WorkoutMetricType_Steps: - /// Steps Label - return i18n_noop("STEPS"); - default: - return ""; - } -} - -static GColor prv_get_bg_color_for_metric(WorkoutMetricType metric_type, - WorkoutActiveWindow *active_window, - bool is_scrollable) { -#if PBL_BW - return GColorWhite; -#else - if (metric_type == WorkoutMetricType_Hr) { - switch (hr_util_get_hr_zone(active_window->workout_controller->get_metric_value( - metric_type, active_window->workout_data))) { - case HRZone_Zone0: - return GColorWhite; - case HRZone_Zone1: - return GColorMelon; - case HRZone_Zone2: - return GColorChromeYellow; - case HRZone_Zone3: - return GColorOrange; - default: - return BACKGROUND_COLOR; - } - } else { - return is_scrollable ? GColorPastelYellow : BACKGROUND_COLOR; - } -#endif -} - -static GFont prv_get_number_font(bool prefer_larger_font) { - return prefer_larger_font ? fonts_get_system_font(FONT_KEY_LECO_38_BOLD_NUMBERS) - : fonts_get_system_font(FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM); -} - -static GTextNode* prv_create_text_node(WorkoutActiveWindow *active_window, - WorkoutMetricType metric_type, - bool prefer_larger_font, - void *i18n_owner) { - GTextNodeHorizontal *horiz_container = graphics_text_node_create_horizontal(MAX_TEXT_NODES); - GTextNodeContainer *container = &horiz_container->container; - horiz_container->horizontal_alignment = TEXT_ALIGNMENT; - - const GFont number_font = prv_get_number_font(prefer_larger_font); - const GFont units_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - - const int units_offset_y = fonts_get_font_height(number_font) - fonts_get_font_height(units_font); - - switch (metric_type) { - case WorkoutMetricType_Hr: { - GPoint heart_node_offset = GPoint(2, prefer_larger_font ? 5 : 0); - GTextNodeCustom *heart_node; - if (active_window->workout_controller->get_metric_value( - metric_type, active_window->workout_data) > 0) { - const size_t buffer_size = sizeof("000"); - GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, - TEXT_COLOR, container); - active_window->workout_controller->metric_to_string(metric_type, - (char *) number_text_node->text, - buffer_size, i18n_owner, - active_window->workout_data); - heart_node_offset.y += fonts_get_font_cap_offset(number_font); - heart_node = graphics_text_node_create_custom(prv_draw_heart_node_callback, - active_window); - } else { - // if metric value is 0, we draw another icon that needs different offset - heart_node_offset.x += 2; - heart_node_offset.y += 7; - heart_node = graphics_text_node_create_custom(prv_draw_hr_measuring_node_callback, - active_window); - } - heart_node->node.offset = heart_node_offset; - graphics_text_node_container_add_child(container, &heart_node->node); - break; - } - case WorkoutMetricType_Steps: { - const size_t buffer_size = sizeof("000000"); - GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, - TEXT_COLOR, container); - active_window->workout_controller->metric_to_string(metric_type, - (char *) number_text_node->text, - buffer_size, i18n_owner, - active_window->workout_data); - break; - } - case WorkoutMetricType_Distance: { - GTextNodeText *number_text_node = health_util_create_text_node( - HEALTH_WHOLE_AND_DECIMAL_LENGTH, number_font, TEXT_COLOR, container); - active_window->workout_controller->metric_to_string(metric_type, - (char *) number_text_node->text, - HEALTH_WHOLE_AND_DECIMAL_LENGTH, - i18n_owner, - active_window->workout_data); - -#if PBL_RECT - /// MI/KM units string - const char *units_string = active_window->workout_controller->get_distance_string( - i18n_noop("MI"), i18n_noop("KM")); - GTextNodeText *units_text_node = health_util_create_text_node_with_text( - i18n_get(units_string, i18n_owner), units_font, TEXT_COLOR, container); - units_text_node->node.offset.y = units_offset_y; -#endif - break; - } - case WorkoutMetricType_Custom: - { - const size_t buffer_size = 20; - GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, - TEXT_COLOR, container); - number_text_node->overflow = GTextOverflowModeTrailingEllipsis; - active_window->workout_controller->metric_to_string(metric_type, - (char *) number_text_node->text, - buffer_size, i18n_owner, - active_window->workout_data); - if (strlen(number_text_node->text) > 5) { - number_text_node->font = prv_get_number_font(false); - } - break; - } - case WorkoutMetricType_Duration: - { - const size_t buffer_size = sizeof("00:00:00"); - GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, - TEXT_COLOR, container); - active_window->workout_controller->metric_to_string(metric_type, - (char *)number_text_node->text, buffer_size, i18n_owner, active_window->workout_data); - - if (strlen(number_text_node->text) > 5) { - // text is long so use smaller font - number_text_node->font = prv_get_number_font(false); - } - break; - } - case WorkoutMetricType_Pace: - case WorkoutMetricType_AvgPace: - { - if (active_window->workout_controller->get_metric_value( - metric_type, active_window->workout_data) >= SECONDS_PER_HOUR) { - GTextNodeText *text_node = - health_util_create_text_node_with_text(EM_DASH, units_font, TEXT_COLOR, container); - text_node->node.offset.x += 1; - text_node->node.offset.y = units_offset_y; - } else { - const size_t buffer_size = sizeof("00:00:00"); - GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, - TEXT_COLOR, container); - active_window->workout_controller->metric_to_string(metric_type, - (char *)number_text_node->text, buffer_size, i18n_owner, active_window->workout_data); - -#if PBL_RECT - GTextNodeText *divider_text_node = health_util_create_text_node_with_text( - "/", units_font, TEXT_COLOR, container); - divider_text_node->node.offset.y = units_offset_y; - - /// MI/KM units string - const char *units_string = - active_window->workout_controller->get_distance_string(i18n_noop("MI"), - i18n_noop("KM")); - GTextNodeText *units_text_node = health_util_create_text_node_with_text( - i18n_get(units_string, i18n_owner), units_font, TEXT_COLOR, container); - units_text_node->node.offset.y = units_offset_y; -#endif - } - break; - } - case WorkoutMetricType_Speed: - { - const size_t buffer_size = sizeof("00:00:00"); - GTextNodeText *number_text_node = health_util_create_text_node(buffer_size, number_font, - TEXT_COLOR, container); - active_window->workout_controller->metric_to_string(metric_type, - (char *)number_text_node->text, buffer_size, i18n_owner, active_window->workout_data); - -#if PBL_RECT - /// MI/KM units string - const char *units_string = - active_window->workout_controller->get_distance_string(i18n_noop("MPH"), - i18n_noop("KM/H")); - GTextNodeText *units_text_node = health_util_create_text_node_with_text( - i18n_get(units_string, i18n_owner), units_font, TEXT_COLOR, container); - units_text_node->node.offset.y = units_offset_y; -#endif - break; - } - // don't have default here so when we have a new type, we don't forget to add it here - case WorkoutMetricType_None: - case WorkoutMetricTypeCount: - break; - } - - return &container->node; -} - -static void prv_set_action_bar_icons(WorkoutActiveWindow *active_window) { - ActionBarLayer *action_bar = &active_window->action_bar; - bool is_paused = false; - bool can_stop = false; - if (active_window->workout_controller) { - is_paused = active_window->workout_controller->is_paused(); - can_stop = active_window->workout_controller->stop != NULL; - } - - if (is_paused) { - action_bar_layer_set_icon(action_bar, active_window->pause_button, - active_window->action_bar_start); - if (can_stop) { - action_bar_layer_set_icon(action_bar, BUTTON_ID_SELECT, active_window->action_bar_stop); - } - } else { - action_bar_layer_clear_icon(action_bar, BUTTON_ID_SELECT); - action_bar_layer_set_icon(action_bar, active_window->pause_button, - active_window->action_bar_pause); - } - - if (active_window->num_scrollable_metrics > 1) { - action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, active_window->action_bar_next); - } -} - -static void prv_update_ui(WorkoutActiveWindow *active_window) { - if (window_manager_is_window_visible(&active_window->window)) { - layer_mark_dirty(&active_window->base_layer); - - // Update the action bar in case another client updated the workout's status - prv_set_action_bar_icons(active_window); - } -} - -static void prv_hr_measuring_timer_callback(void *data) { - WorkoutActiveWindow *active_window = data; - - active_window->cur_hr_measuring_width_idx = (active_window->cur_hr_measuring_width_idx + 1) - % ARRAY_LENGTH(s_hr_measuring_widths); - - prv_update_ui(active_window); - - if (active_window->workout_controller->get_metric_value( - WorkoutMetricType_Hr, active_window->workout_data) == 0) { - int timeout_ms = (active_window->cur_hr_measuring_width_idx == 0) ? 800 : 200; - active_window->hr_measuring_timer = - app_timer_register(timeout_ms, prv_hr_measuring_timer_callback, active_window); - } else { - active_window->hr_measuring_timer = NULL; - } -} - -static void prv_update_timer_callback(void *data) { - WorkoutActiveWindow *active_window = data; - - if (active_window->workout_controller) { - active_window->workout_controller->update_data(active_window->workout_data); - } - - prv_update_ui(active_window); - active_window->update_timer = app_timer_register(1000, prv_update_timer_callback, active_window); - - const int bpm = active_window->workout_controller->get_metric_value( - WorkoutMetricType_Hr, active_window->workout_data); - if (bpm == 0 && !active_window->hr_measuring_timer) { - active_window->cur_hr_measuring_width_idx = 0; - prv_hr_measuring_timer_callback(active_window); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Drawing - -static void prv_draw_heart_icon(GContext *ctx, GBitmap *icon, const GRect *rect, - bool render, GSize *size_out) { - if (render) { - graphics_context_set_compositing_mode(ctx, GCompOpSet); - graphics_draw_bitmap_in_rect(ctx, icon, rect); - } - if (size_out) { - *size_out = rect->size; - } -} - -static void prv_draw_heart_node_callback(GContext *ctx, const GRect *box, - const GTextNodeDrawConfig *config, bool render, - GSize *size_out, void *user_data) { - WorkoutActiveWindow *active_window = user_data; - GRect bounds = gbitmap_get_bounds(active_window->heart_icon); - bounds.origin = box->origin; - prv_draw_heart_icon(ctx, active_window->heart_icon, &bounds, render, size_out); -} - -static void prv_draw_hr_measuring_node_callback(GContext *ctx, const GRect *box, - const GTextNodeDrawConfig *config, bool render, - GSize *size_out, void *user_data) { - WorkoutActiveWindow *active_window = user_data; - GRect bounds = gbitmap_get_bounds(active_window->hr_measuring_icon); - bounds.origin = box->origin; - bounds.size.w = s_hr_measuring_widths[active_window->cur_hr_measuring_width_idx]; - prv_draw_heart_icon(ctx, active_window->hr_measuring_icon, &bounds, render, size_out); -} - -static void prv_render_separator(GContext *ctx, Layer *layer) { - graphics_context_set_stroke_color(ctx, GColorBlack); - graphics_draw_horizontal_line_dotted(ctx, GPoint(0, layer->bounds.size.h - 1), - layer->bounds.size.w); -} - -static void prv_render_bg_color(GContext *ctx, GRect *bounds, GColor color) { - graphics_context_set_fill_color(ctx, color); - graphics_fill_rect(ctx, bounds); -} - -static void prv_render_metric_label(GContext *ctx, GRect *box, WorkoutMetricType metric_type, - WorkoutActiveWindow *active_window, void *i18n_owner) { - GRect label_box = *box; - GTextOverflowMode overflow_mode = GTextOverflowModeWordWrap; - if (metric_type == WorkoutMetricType_Custom) { - // I seriously have no idea why the height is hardcoded to 40 and overflow is set to word - // wrap when there's a note that says the height is being set to 40 to avoid wrapping. Also, - // with a font size of 18, I don't know how it wouldn't wrap. This fixes the inconsistent - // magic number problem for the WorkoutMetricType_Custom only - label_box.size.h = 20; - overflow_mode = GTextOverflowModeTrailingEllipsis; - } - - - graphics_context_set_text_color(ctx, TEXT_COLOR); - graphics_draw_text(ctx, - i18n_get(prv_get_label_for_metric(metric_type, active_window), i18n_owner), - fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD), - label_box, - overflow_mode, - TEXT_ALIGNMENT, - NULL); -} - -static void prv_render_hr_zones(GContext *ctx, GRect *box, WorkoutActiveWindow *active_window) { - graphics_context_set_stroke_color(ctx, GColorBlack); - graphics_context_set_fill_color(ctx, GColorBlack); - - GRect zone_rect = *box; - zone_rect.origin.x += PBL_IF_RECT_ELSE(1, 69); - // add some padding after the label - zone_rect.origin.y += 10; - // size of a zone rect - zone_rect.size = GSize(20, 8); - - const int zone_padding = 2; - - for (HRZone i = HRZone_Zone1; i < HRZoneCount; i++) { - if (i <= hr_util_get_hr_zone(active_window->workout_controller->get_metric_value( - WorkoutMetricType_Hr, active_window->workout_data))) { - graphics_fill_rect(ctx, &zone_rect); - } else { - // drawing it twice to draw a 2px border - GRect inner_rect = grect_inset(zone_rect, GEdgeInsets(1)); - graphics_draw_rect(ctx, &zone_rect); - graphics_draw_rect(ctx, &inner_rect); - } - // increment x to draw more zones - zone_rect.origin.x += zone_rect.size.w + zone_padding; - } -} - -static void prv_render_metric(GContext *ctx, WorkoutMetricType metric_type, Layer *layer, - GColor bg_color, bool draw_hr_zones, bool prefer_larger_font) { - WorkoutActiveWindow *active_window = window_get_user_data(layer_get_window(layer)); - - prv_render_bg_color(ctx, &layer->bounds, bg_color); - - const int16_t rl_margin = PBL_IF_RECT_ELSE(5, 23); - - GRect rect = grect_inset(layer->bounds, GEdgeInsets(0, rl_margin)); - - // set rect y depending on layout, primary metric and display shape - if (active_window->layout == WorkoutLayout_SingleMetric) { - rect.origin.y = PBL_IF_RECT_ELSE(35, 41); - } else if (active_window->layout == WorkoutLayout_StaticAndScrollable) { - rect.origin.y = prefer_larger_font ? PBL_IF_RECT_ELSE(2, 13) : PBL_IF_RECT_ELSE(5, 1); - } else if (active_window->layout == WorkoutLayout_TwoStaticAndScrollable) { - rect.origin.y = (&active_window->scrollable_metric_layer == layer) ? - PBL_IF_RECT_ELSE(-2, 0) : PBL_IF_RECT_ELSE(-4, -2); - } - - // set the rect height so we don't wrap text to the next line - rect.size.h = 40; - -#if PBL_ROUND - if (draw_hr_zones) { - // padding between text and zones is less on round - rect.origin.y -= 10; - } - rect.origin.x -= 24; -#endif - - prv_render_metric_label(ctx, &rect, metric_type, active_window, layer); - - // update rect y for the label height - if (active_window->layout == WorkoutLayout_TwoStaticAndScrollable) { - rect.origin.y += prefer_larger_font ? 11 : 15; - } else { - rect.origin.y += prefer_larger_font ? 12 : 15; - } - - if (draw_hr_zones) { - prv_render_hr_zones(ctx, &rect, active_window); - // update rect y for the zones height - rect.origin.y += PBL_IF_RECT_ELSE(18, 15); - } - - // adjust rect for drawing the text node - rect.origin.x -= PBL_IF_RECT_ELSE(1, 46); - rect.size.w += (rl_margin * 2); - - GTextNode *text_node = prv_create_text_node(active_window, metric_type, - prefer_larger_font, layer); - graphics_text_node_draw(text_node, ctx, &rect, NULL, NULL); - graphics_text_node_destroy(text_node); -} - -static void prv_static_layer_update_proc(struct Layer *layer, GContext *ctx) { - WorkoutActiveWindow *active_window = window_get_user_data(layer_get_window(layer)); - - WorkoutMetricType metric_type = WorkoutMetricType_None; - if (layer == &active_window->top_metric_layer) { - metric_type = active_window->top_metric; - } else if (layer == &active_window->middle_metric_layer) { - metric_type = active_window->middle_metric; - } - - GColor bg_color = prv_get_bg_color_for_metric(metric_type, active_window, false); - - HRZone hr_zone = hr_util_get_hr_zone(active_window->workout_controller->get_metric_value( - metric_type, active_window->workout_data)); - const bool draw_zones = (metric_type == WorkoutMetricType_Hr) && hr_zone > HRZone_Zone0; - const bool prefer_larger_font = active_window->layout == WorkoutLayout_SingleMetric || - active_window->layout == WorkoutLayout_StaticAndScrollable; - - prv_render_metric(ctx, metric_type, layer, bg_color, draw_zones, prefer_larger_font); - - if (layer == &active_window->top_metric_layer) { - status_bar_layer_set_colors(&active_window->status_layer, bg_color, GColorBlack); - } - - if (active_window->layout == WorkoutLayout_StaticAndScrollable || - (active_window->layout == WorkoutLayout_TwoStaticAndScrollable && - layer == &active_window->middle_metric_layer)) { - prv_render_separator(ctx, layer); - } -} - -static void prv_scrollable_layer_update_proc(struct Layer *layer, GContext *ctx) { - WorkoutActiveWindow *active_window = window_get_user_data(layer_get_window(layer)); - - if (!active_window->num_scrollable_metrics) { - return; - } - - WorkoutMetricType metric_type = - active_window->scrollable_metrics[active_window->current_scrollable_metric]; - - GColor bg_color = prv_get_bg_color_for_metric(metric_type, active_window, true); - - const bool draw_hr_zones = false; - const bool prefer_larger_font = false; - prv_render_metric(ctx, metric_type, layer, bg_color, draw_hr_zones, prefer_larger_font); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! End Workout - -static void prv_end_workout_up_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutActiveWindow *active_window = context; - - if (active_window->workout_controller) { - active_window->workout_controller->stop(); - } - - workout_push_summary_window(); - - workout_dialog_pop(&active_window->end_workout_dialog); - app_window_stack_remove(&active_window->window, false); -} - -static void prv_end_workout_down_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutActiveWindow *active_window = context; - - workout_dialog_pop(&active_window->end_workout_dialog); -} - -static void prv_end_workout_click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, prv_end_workout_up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_end_workout_down_click_handler); -} - -static void prv_end_workout(void *context) { - WorkoutActiveWindow *active_window = context; - - WorkoutDialog *workout_dialog = &active_window->end_workout_dialog; - - workout_dialog_init(workout_dialog, "Workout End"); - Dialog *dialog = workout_dialog_get_dialog(workout_dialog); - - dialog_show_status_bar_layer(dialog, true); - dialog_set_fullscreen(dialog, true); - dialog_set_text(dialog, i18n_get("End Workout?", workout_dialog)); - dialog_set_background_color(dialog, BACKGROUND_COLOR); - dialog_set_text_color(dialog, TEXT_COLOR); - dialog_set_icon(dialog, RESOURCE_ID_WORKOUT_APP_END); - dialog_set_icon_animate_direction(dialog, DialogIconAnimateNone); - dialog_set_destroy_on_pop(dialog, false); - - i18n_free_all(workout_dialog); - - workout_dialog_set_click_config_provider(workout_dialog, prv_end_workout_click_config_provider); - workout_dialog_set_click_config_context(workout_dialog, context); - - app_workout_dialog_push(workout_dialog); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Handlers - -static void prv_handle_pause_button(WorkoutActiveWindow *active_window) { - bool is_paused = false; - if (active_window->workout_controller) { - is_paused = active_window->workout_controller->is_paused(); - } - - if (active_window->workout_controller) { - active_window->workout_controller->pause(!is_paused); - } - - prv_update_ui(active_window); -} - -static void prv_handle_stop_button(WorkoutActiveWindow *active_window) { - bool is_paused = false; - bool can_stop = false; - if (active_window->workout_controller) { - is_paused = active_window->workout_controller->is_paused(); - can_stop = active_window->workout_controller->stop != NULL; - } - - if (!is_paused || !can_stop) { - return; - } - - prv_end_workout(active_window); -} - -static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutActiveWindow *active_window = context; - - if (active_window->pause_button == BUTTON_ID_UP) { - prv_handle_pause_button(active_window); - } -} - -static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutActiveWindow *active_window = context; - - if (active_window->pause_button == BUTTON_ID_SELECT) { - prv_handle_pause_button(active_window); - } else { - prv_handle_stop_button(active_window); - } -} - -static void prv_set_pause_button(WorkoutActiveWindow *active_window) { - bool can_stop = active_window->workout_controller->stop != NULL; - if (can_stop || active_window->num_scrollable_metrics > 1) { - active_window->pause_button = BUTTON_ID_UP; - } else { - active_window->pause_button = BUTTON_ID_SELECT; - } -} - -T_STATIC void prv_cycle_scrollable_metrics(WorkoutActiveWindow *active_window) { - active_window->current_scrollable_metric = - (active_window->current_scrollable_metric + 1) % active_window->num_scrollable_metrics; -} - -static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutActiveWindow *active_window = context; - prv_cycle_scrollable_metrics(active_window); -} - -static void prv_click_config_provider(void *context) { - window_set_click_context(BUTTON_ID_UP, context); - window_set_click_context(BUTTON_ID_SELECT, context); - window_set_click_context(BUTTON_ID_DOWN, context); - window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, prv_select_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); -} - -static void prv_window_unload_handler(Window *window) { - WorkoutActiveWindow *active_window = window_get_user_data(window); - if (active_window) { - app_timer_cancel(active_window->update_timer); - app_timer_cancel(active_window->hr_measuring_timer); - gbitmap_destroy(active_window->action_bar_start); - gbitmap_destroy(active_window->action_bar_pause); - gbitmap_destroy(active_window->action_bar_stop); - gbitmap_destroy(active_window->action_bar_more); - gbitmap_destroy(active_window->action_bar_next); - gbitmap_destroy(active_window->heart_icon); - gbitmap_destroy(active_window->hr_measuring_icon); - action_bar_layer_deinit(&active_window->action_bar); - status_bar_layer_deinit(&active_window->status_layer); - layer_deinit(&active_window->top_metric_layer); - layer_deinit(&active_window->scrollable_metric_layer); - layer_deinit(&active_window->base_layer); - window_deinit(&active_window->window); - i18n_free_all(active_window); - app_free(active_window); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Common Setup -static void prv_create_window_common(WorkoutActiveWindow *active_window, - void *workout_data, - WorkoutController *workout_controller) { - active_window->workout_data = workout_data; - active_window->workout_controller = workout_controller; - - Window *window = &active_window->window; - window_init(window, WINDOW_NAME("Workout Active Info")); - window_set_user_data(window, active_window); - window_set_background_color(window, BACKGROUND_COLOR); - window_set_window_handlers(window, &(WindowHandlers){ - .unload = prv_window_unload_handler, - }); - - GRect base_layer_bounds = window->layer.bounds; -#if PBL_RECT - base_layer_bounds.size.w -= ACTION_BAR_WIDTH; -#endif - - base_layer_bounds.origin.y = STATUS_BAR_LAYER_HEIGHT; - layer_init(&active_window->base_layer, &base_layer_bounds); - layer_add_child(&window->layer, &active_window->base_layer); - base_layer_bounds.origin.y = 0; - - if (active_window->layout == WorkoutLayout_SingleMetric) { - // Only 1 metric to show. It can have the whole screen - GRect metric_bounds = base_layer_bounds; - layer_init(&active_window->top_metric_layer, &metric_bounds); - layer_set_update_proc(&active_window->top_metric_layer, prv_static_layer_update_proc); - layer_add_child(&active_window->base_layer, &active_window->top_metric_layer); - } else if (active_window->layout == WorkoutLayout_StaticAndScrollable) { - // Two metrics. 1 big static metric above a smaller scrollable metric - GRect top_metric_bounds = base_layer_bounds; - top_metric_bounds.size.h = PBL_IF_RECT_ELSE(90, 77); - layer_init(&active_window->top_metric_layer, &top_metric_bounds); - layer_set_update_proc(&active_window->top_metric_layer, prv_static_layer_update_proc); - layer_add_child(&active_window->base_layer, &active_window->top_metric_layer); - - GRect scrollable_metric_bounds = top_metric_bounds; - scrollable_metric_bounds.origin.y = scrollable_metric_bounds.size.h; - scrollable_metric_bounds.size.h = window->layer.bounds.size.h - - scrollable_metric_bounds.origin.y; - layer_init(&active_window->scrollable_metric_layer, &scrollable_metric_bounds); - layer_set_update_proc(&active_window->scrollable_metric_layer, - prv_scrollable_layer_update_proc); - layer_add_child(&active_window->base_layer, &active_window->scrollable_metric_layer); - } else if (active_window->layout == WorkoutLayout_TwoStaticAndScrollable) { - // Three metrics. Two static metrics above a scrollable metric - const int layer_height = 51; - GRect top_metric_bounds = base_layer_bounds; - top_metric_bounds.size.h = layer_height; - layer_init(&active_window->top_metric_layer, &top_metric_bounds); - layer_set_update_proc(&active_window->top_metric_layer, prv_static_layer_update_proc); - layer_add_child(&active_window->base_layer, &active_window->top_metric_layer); - - GRect middle_metric_bounds = top_metric_bounds; - middle_metric_bounds.origin.y = top_metric_bounds.size.h; - middle_metric_bounds.size.h = layer_height - 2; - layer_init(&active_window->middle_metric_layer, &middle_metric_bounds); - layer_set_update_proc(&active_window->middle_metric_layer, prv_static_layer_update_proc); - layer_add_child(&active_window->base_layer, &active_window->middle_metric_layer); - - GRect scrollable_metric_bounds = middle_metric_bounds; - scrollable_metric_bounds.origin.y = top_metric_bounds.size.h + middle_metric_bounds.size.h; - scrollable_metric_bounds.size.h = layer_height + 10; - layer_init(&active_window->scrollable_metric_layer, &scrollable_metric_bounds); - layer_set_update_proc(&active_window->scrollable_metric_layer, - prv_scrollable_layer_update_proc); - layer_add_child(&active_window->base_layer, &active_window->scrollable_metric_layer); - } - - StatusBarLayer *status_layer = &active_window->status_layer; - status_bar_layer_init(status_layer); - status_bar_layer_set_colors(status_layer, GColorClear, GColorBlack); - layer_add_child(&window->layer, status_bar_layer_get_layer(status_layer)); - -#if PBL_RECT - GRect status_layer_bounds = window->layer.bounds; - status_layer_bounds.size.w -= ACTION_BAR_WIDTH; - layer_set_frame(&status_layer->layer, &status_layer_bounds); -#endif - - ActionBarLayer *action_bar = &active_window->action_bar; - action_bar_layer_init(action_bar); - action_bar_layer_set_context(action_bar, active_window); - action_bar_layer_set_click_config_provider(action_bar, prv_click_config_provider); - action_bar_layer_add_to_window(action_bar, window); - - active_window->heart_icon = gbitmap_create_with_resource(RESOURCE_ID_WORKOUT_APP_HEART), - active_window->hr_measuring_icon = - gbitmap_create_with_resource(RESOURCE_ID_WORKOUT_APP_MEASURING_HR), - - active_window->action_bar_start = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_START); - active_window->action_bar_pause = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_PAUSE); - active_window->action_bar_stop = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_STOP); - active_window->action_bar_more = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_MORE); - active_window->action_bar_next = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_TOGGLE); - - prv_set_pause_button(active_window); - prv_set_action_bar_icons(active_window); - - active_window->update_timer = app_timer_register(1000, prv_update_timer_callback, active_window); -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Public API - -WorkoutActiveWindow *workout_active_create_single_layout(WorkoutMetricType metric, - void *workout_data, - WorkoutController *workout_controller) { - if (metric == WorkoutMetricType_None) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid argument"); - return NULL; - } - - WorkoutActiveWindow *active_window = app_zalloc_check(sizeof(WorkoutActiveWindow)); - active_window->layout = WorkoutLayout_SingleMetric; - - active_window->top_metric = metric; - - prv_create_window_common(active_window, workout_data, workout_controller); - - return active_window; -} - -WorkoutActiveWindow *workout_active_create_double_layout(WorkoutMetricType top_metric, - int num_scrollable_metrics, - WorkoutMetricType *scrollable_metrics, - void *workout_data, - WorkoutController *workout_controller) { - if (top_metric == WorkoutMetricType_None || num_scrollable_metrics == 0 || !scrollable_metrics) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid argument(s)"); - return NULL; - } - - WorkoutActiveWindow *active_window = app_zalloc_check(sizeof(WorkoutActiveWindow)); - active_window->layout = WorkoutLayout_StaticAndScrollable; - - active_window->top_metric = top_metric; - prv_add_scrollable_metrics(active_window, num_scrollable_metrics, scrollable_metrics); - - prv_create_window_common(active_window, workout_data, workout_controller); - - return active_window; -} - -WorkoutActiveWindow *workout_active_create_tripple_layout(WorkoutMetricType top_metric, - WorkoutMetricType middle_metric, - int num_scrollable_metrics, - WorkoutMetricType *scrollable_metrics, - void *workout_data, - WorkoutController *workout_controller) { - if (top_metric == WorkoutMetricType_None || middle_metric == WorkoutMetricType_None || - (num_scrollable_metrics != 0 && !scrollable_metrics)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid argument(s)"); - return NULL; - } - - WorkoutActiveWindow *active_window = app_zalloc_check(sizeof(WorkoutActiveWindow)); - active_window->layout = WorkoutLayout_TwoStaticAndScrollable; - - active_window->top_metric = top_metric; - active_window->middle_metric = middle_metric; - prv_add_scrollable_metrics(active_window, num_scrollable_metrics, scrollable_metrics); - - prv_create_window_common(active_window, workout_data, workout_controller); - - return active_window; -} - -WorkoutActiveWindow *workout_active_create_for_activity_type(ActivitySessionType type, - void *workout_data, WorkoutController *workout_controller) { - const bool hrm_is_available = activity_is_hrm_present() && activity_prefs_heart_rate_is_enabled(); - - switch (type) { - case ActivitySessionType_Open: - { - if (hrm_is_available) { - WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Duration}; - return workout_active_create_double_layout(WorkoutMetricType_Hr, - ARRAY_LENGTH(scrollable_metrics), - scrollable_metrics, - workout_data, - workout_controller); - } else { - return workout_active_create_single_layout(WorkoutMetricType_Duration, - workout_data, - workout_controller); - } - } - case ActivitySessionType_Walk: - { - if (hrm_is_available) { - WorkoutMetricType top_metric = WorkoutMetricType_Hr; - WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Duration, - WorkoutMetricType_Distance, - WorkoutMetricType_AvgPace, - WorkoutMetricType_Steps}; - return workout_active_create_double_layout(top_metric, - ARRAY_LENGTH(scrollable_metrics), - scrollable_metrics, - workout_data, - workout_controller); - } else { - WorkoutMetricType top_metric = WorkoutMetricType_Duration; - WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Distance, - WorkoutMetricType_AvgPace, - WorkoutMetricType_Steps}; - return workout_active_create_double_layout(top_metric, - ARRAY_LENGTH(scrollable_metrics), - scrollable_metrics, - workout_data, - workout_controller); - } - } - case ActivitySessionType_Run: - { - if (hrm_is_available) { - WorkoutMetricType top_metric = WorkoutMetricType_Hr; - WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_Duration, - WorkoutMetricType_AvgPace, - WorkoutMetricType_Distance}; - return workout_active_create_double_layout(top_metric, - ARRAY_LENGTH(scrollable_metrics), - scrollable_metrics, - workout_data, - workout_controller); - } else { - WorkoutMetricType top_metric = WorkoutMetricType_Duration; - WorkoutMetricType scrollable_metrics[] = {WorkoutMetricType_AvgPace, - WorkoutMetricType_Distance}; - return workout_active_create_double_layout(top_metric, - ARRAY_LENGTH(scrollable_metrics), - scrollable_metrics, - workout_data, - workout_controller); - } - } - default: - return NULL; - } -} - -void workout_active_window_push(WorkoutActiveWindow *active_window) { - app_window_stack_push(&active_window->window, true); -} - -void workout_active_update_scrollable_metrics(WorkoutActiveWindow *active_window, - int num_scrollable_metrics, - WorkoutMetricType *scrollable_metrics) { - active_window->num_scrollable_metrics = 0; - prv_add_scrollable_metrics(active_window, num_scrollable_metrics, scrollable_metrics); - - prv_set_pause_button(active_window); - - if (active_window->current_scrollable_metric >= active_window->num_scrollable_metrics) { - active_window->current_scrollable_metric = 0; - } - - prv_update_ui(active_window); -} diff --git a/src/fw/apps/system_apps/workout/workout_active.h b/src/fw/apps/system_apps/workout/workout_active.h deleted file mode 100644 index 6097533f57..0000000000 --- a/src/fw/apps/system_apps/workout/workout_active.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "workout_controller.h" -#include "workout_metrics.h" - -#include "services/normal/activity/activity.h" - -typedef struct WorkoutActiveWindow WorkoutActiveWindow; - - -WorkoutActiveWindow *workout_active_create_single_layout(WorkoutMetricType metric, - void *workout_data, - WorkoutController *workout_controller); - -WorkoutActiveWindow *workout_active_create_double_layout(WorkoutMetricType top_metric, - int num_scrollable_metrics, - WorkoutMetricType *scrollable_metrics, - void *workout_data, - WorkoutController *workout_controller); - -WorkoutActiveWindow *workout_active_create_tripple_layout(WorkoutMetricType top_metric, - WorkoutMetricType middle_metric, - int num_scrollable_metrics, - WorkoutMetricType *scrollable_metrics, - void *workout_data, - WorkoutController *workout_controller); - -WorkoutActiveWindow *workout_active_create_for_activity_type(ActivitySessionType type, - void *workout_data, - WorkoutController *workout_controller); - -void workout_active_window_push(WorkoutActiveWindow *window); - -void workout_active_update_scrollable_metrics(WorkoutActiveWindow *active_window, - int num_scrollable_metrics, - WorkoutMetricType *scrollable_metrics); diff --git a/src/fw/apps/system_apps/workout/workout_controller.h b/src/fw/apps/system_apps/workout/workout_controller.h deleted file mode 100644 index bfa5289e9c..0000000000 --- a/src/fw/apps/system_apps/workout/workout_controller.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "workout_metrics.h" - -#include -#include -#include - -typedef struct WorkoutController { - bool (*is_paused)(void); - bool (*pause)(bool should_be_paused); - bool (*stop)(void); - - void (*update_data)(void *data); - void (*metric_to_string)(WorkoutMetricType type, char *buffer, - size_t buffer_size, void *i18n_owner, void *workout_data); - int32_t (*get_metric_value)(WorkoutMetricType type, void *data); - const char* (*get_distance_string)(const char *miles_string, const char *km_string); - char* (*get_custom_metric_label_string)(void); -} WorkoutController; diff --git a/src/fw/apps/system_apps/workout/workout_countdown.c b/src/fw/apps/system_apps/workout/workout_countdown.c deleted file mode 100644 index 435d33b176..0000000000 --- a/src/fw/apps/system_apps/workout/workout_countdown.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_countdown.h" - -#include "applib/ui/ui.h" -#include "applib/ui/kino/kino_layer.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" - -#define TIMER_DURATION (1000) -#define NUM_IMAGES (3) - -typedef struct WorkoutCountdownWindow { - Window window; - Layer base_layer; - - KinoReel *images[NUM_IMAGES]; - int current_image; - - StartWorkoutCallback start_workout_cb; - ActivitySessionType activity_type; - - AppTimer *timer; -} WorkoutCountdownWindow; - -static void prv_base_layer_update_proc(Layer *layer, GContext *ctx) { - WorkoutCountdownWindow *countdown_window = window_get_user_data(layer_get_window(layer)); - - KinoReel *image = countdown_window->images[countdown_window->current_image]; - - const GSize icon_size = kino_reel_get_size(image); - - GPoint offset; - offset.x = (layer->bounds.size.w / 2) - (icon_size.w / 2); - offset.y = (layer->bounds.size.h / 2) - (icon_size.h / 2); - kino_reel_draw(image, ctx, offset); -} - -static void prv_timer_callback(void *data) { - WorkoutCountdownWindow *countdown_window = data; - - if (countdown_window->current_image <= 0) { - countdown_window->start_workout_cb(countdown_window->activity_type); - window_stack_remove(&countdown_window->window, false); - vibes_long_pulse(); - return; - } - - countdown_window->current_image--; - - layer_mark_dirty(&countdown_window->base_layer); - - app_timer_register(TIMER_DURATION, prv_timer_callback, countdown_window); -} - -static void prv_window_unload_handler(Window *window) { - WorkoutCountdownWindow *countdown_window = window_get_user_data(window); - if (countdown_window) { - for (int i = 0; i < NUM_IMAGES; i++) { - kino_reel_destroy(countdown_window->images[i]); - } - app_timer_cancel(countdown_window->timer); - layer_deinit(&countdown_window->base_layer); - window_deinit(&countdown_window->window); - app_free(countdown_window); - } -} - -void workout_countdown_start(ActivitySessionType type, StartWorkoutCallback start_workout_cb) { - WorkoutCountdownWindow *countdown_window = app_zalloc_check(sizeof(WorkoutCountdownWindow)); - - countdown_window->start_workout_cb = start_workout_cb; - countdown_window->activity_type = type; - - Window *window = &countdown_window->window; - window_init(window, WINDOW_NAME("Workout Countdown")); - window_set_user_data(window, countdown_window); - window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorYellow, GColorDarkGray)); - window_set_window_handlers(window, &(WindowHandlers){ - .unload = prv_window_unload_handler, - }); - - layer_init(&countdown_window->base_layer, &window->layer.bounds); - layer_set_update_proc(&countdown_window->base_layer, prv_base_layer_update_proc); - layer_add_child(&window->layer, &countdown_window->base_layer); - - countdown_window->images[0] = kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_ONE); - countdown_window->images[1] = kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_TWO); - countdown_window->images[2] = kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_THREE); - - countdown_window->current_image = NUM_IMAGES - 1; - - app_timer_register(TIMER_DURATION, prv_timer_callback, countdown_window); - - app_window_stack_push(window, true); -} diff --git a/src/fw/apps/system_apps/workout/workout_countdown.h b/src/fw/apps/system_apps/workout/workout_countdown.h deleted file mode 100644 index 868230d991..0000000000 --- a/src/fw/apps/system_apps/workout/workout_countdown.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "workout_summary.h" - -#include "services/normal/activity/activity.h" - -void workout_countdown_start(ActivitySessionType type, StartWorkoutCallback start_workout_cb); diff --git a/src/fw/apps/system_apps/workout/workout_data.c b/src/fw/apps/system_apps/workout/workout_data.c deleted file mode 100644 index 26f9e04bf7..0000000000 --- a/src/fw/apps/system_apps/workout/workout_data.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_data.h" - -#include "services/normal/activity/health_util.h" -#include "services/normal/activity/workout_service.h" - -#include - -void workout_data_update(void *data) { - if (!data) { - return; - } - WorkoutData *workout_data = data; - - workout_service_get_current_workout_info(&workout_data->steps, &workout_data->duration_s, - &workout_data->distance_m, &workout_data->bpm, - &workout_data->hr_zone); - - if (workout_data->duration_s && workout_data->distance_m) { - workout_data->avg_pace = health_util_get_pace(workout_data->duration_s, - workout_data->distance_m); - } -} - -void workout_data_fill_metric_value(WorkoutMetricType type, char *buffer, size_t buffer_size, - void *i18n_owner, void *data) { - int32_t metric_value = workout_data_get_metric_value(type, data); - - switch (type) { - case WorkoutMetricType_Hr: - case WorkoutMetricType_Steps: - { - snprintf(buffer, buffer_size, "%"PRId32, metric_value); - break; - } - case WorkoutMetricType_Distance: - { - const int conversion_factor = health_util_get_distance_factor(); - health_util_format_whole_and_decimal(buffer, buffer_size, metric_value, conversion_factor); - break; - } - case WorkoutMetricType_Duration: - { - health_util_format_hours_minutes_seconds(buffer, buffer_size, metric_value, - true, i18n_owner); - break; - } - case WorkoutMetricType_Pace: - case WorkoutMetricType_AvgPace: - { - health_util_format_hours_minutes_seconds(buffer, buffer_size, metric_value, - false, i18n_owner); - break; - } - case WorkoutMetricType_Speed: - // Not part of the workout service yet - case WorkoutMetricType_Custom: - // Sports app only - case WorkoutMetricType_None: - case WorkoutMetricTypeCount: - break; - } -} - -int32_t workout_data_get_metric_value(WorkoutMetricType type, void *data) { - WorkoutData *workout_data = data; - - switch (type) { - case WorkoutMetricType_Hr: - return workout_data->bpm; - case WorkoutMetricType_Duration: - return workout_data->duration_s; - case WorkoutMetricType_AvgPace: - return workout_data->avg_pace; - case WorkoutMetricType_Distance: - return workout_data->distance_m; - case WorkoutMetricType_Steps: - return workout_data->steps; - default: - return 0; - } -} diff --git a/src/fw/apps/system_apps/workout/workout_data.h b/src/fw/apps/system_apps/workout/workout_data.h deleted file mode 100644 index 1479881ef0..0000000000 --- a/src/fw/apps/system_apps/workout/workout_data.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "workout_metrics.h" - -#include "services/normal/activity/hr_util.h" - -#include -#include -#include - -typedef struct WorkoutData { - int32_t steps; - int32_t duration_s; - int32_t distance_m; - int32_t avg_pace; - int32_t bpm; - HRZone hr_zone; -} WorkoutData; - -void workout_data_update(void *workout_data); - -void workout_data_fill_metric_value(WorkoutMetricType type, char *buffer, - size_t buffer_size, void *i18n_owner, void *workout_data); - -int32_t workout_data_get_metric_value(WorkoutMetricType type, void *workout_data); diff --git a/src/fw/apps/system_apps/workout/workout_dialog.c b/src/fw/apps/system_apps/workout/workout_dialog.c deleted file mode 100644 index e8044ee5a1..0000000000 --- a/src/fw/apps/system_apps/workout/workout_dialog.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_dialog.h" - -#include "applib/applib_malloc.auto.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/dialogs/dialog_private.h" -#include "kernel/ui/kernel_ui.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "system/passert.h" - -#define TEXT_FLOW_INSET_PX (PBL_IF_RECT_ELSE(0, 8)) - -static void prv_workout_dialog_load(Window *window) { - WorkoutDialog *workout_dialog = window_get_user_data(window); - Dialog *dialog = &workout_dialog->dialog; - Layer *window_root_layer = window_get_root_layer(window); - - // Ownership of icon is taken over by KinoLayer in dialog_init_icon_layer() call below - KinoReel *icon = dialog_create_icon(dialog); - const GSize icon_size = icon ? kino_reel_get_size(icon) : GSizeZero; - - const bool show_action_bar = !workout_dialog->hide_action_bar; - - const GRect *bounds = &window_root_layer->bounds; - const uint16_t icon_single_line_text_offset_px = 9; - const uint16_t small_icon_offset = (icon_size.h < 60) ? 7 : 0; - const uint16_t left_margin_px = PBL_IF_RECT_ELSE(5, 0); - const uint16_t action_bar_width = show_action_bar ? ACTION_BAR_WIDTH : 0; - const uint16_t content_and_action_bar_horizontal_spacing = - PBL_IF_RECT_ELSE(left_margin_px, show_action_bar ? 11 : left_margin_px); - const uint16_t right_margin_px = action_bar_width + content_and_action_bar_horizontal_spacing; - const uint16_t text_single_line_text_offset_px = 17; - const int16_t text_layer_line_spacing_delta = -4; - const GFont dialog_text_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - const GFont dialog_subtext_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - const int single_line_text_height_px = fonts_get_font_height(dialog_text_font); - const int max_text_line_height_px = 2 * single_line_text_height_px + 8; - - const uint16_t status_layer_offset = dialog->show_status_layer ? 6 : 0; - uint16_t text_top_margin_px = icon ? icon_size.h + 22 : 6; - uint16_t subtext_top_margin_px = text_top_margin_px + single_line_text_height_px; - uint16_t icon_top_margin_px = PBL_IF_RECT_ELSE(18, 22); - uint16_t text_height; - uint16_t x = 0; - uint16_t y = 0; - uint16_t w = PBL_IF_RECT_ELSE(bounds->size.w - action_bar_width, bounds->size.w); - uint16_t h = STATUS_BAR_LAYER_HEIGHT; - - if (dialog->show_status_layer) { - dialog_add_status_bar_layer(dialog, &GRect(x, y, w, h)); - } - - x = left_margin_px; - w = bounds->size.w - left_margin_px - right_margin_px; - - GTextAttributes *text_attributes = NULL; -#if PBL_ROUND - // Create a GTextAttributes for the TextLayer. Note that the matching - // graphics_text_attributes_destroy() will not need to be called here, as the ownership - // of text_attributes will be transferred to the TextLayer we assign it to. - text_attributes = graphics_text_attributes_create(); - graphics_text_attributes_enable_screen_text_flow(text_attributes, TEXT_FLOW_INSET_PX); -#endif - // Check if the text takes up more than one line. If the dialog has a single line of text, - // the icon and line of text are positioned lower so as to be more vertically centered. - GContext *ctx = graphics_context_get_current_context(); - const GTextAlignment text_alignment = PBL_IF_RECT_ELSE(GTextAlignmentCenter, - show_action_bar ? GTextAlignmentRight : GTextAlignmentCenter); - { - // do all this in a block so we enforce that nobody uses these variables outside of the block - // when dealing with round displays, sizes change depending on location. - const GRect probe_rect = GRect(x, y + text_single_line_text_offset_px, - w, max_text_line_height_px); - text_height = graphics_text_layout_get_max_used_size(ctx, - dialog->buffer, - dialog_text_font, - probe_rect, - GTextOverflowModeWordWrap, - text_alignment, - text_attributes).h; - if (text_height <= single_line_text_height_px) { - text_top_margin_px += text_single_line_text_offset_px; - icon_top_margin_px += icon_single_line_text_offset_px; - } else { - text_top_margin_px += status_layer_offset + small_icon_offset + 2; - icon_top_margin_px += status_layer_offset + small_icon_offset; - } - subtext_top_margin_px = text_top_margin_px + text_height + text_layer_line_spacing_delta; - } - - y = text_top_margin_px; - h = text_height; - - // Set up the text. - TextLayer *text_layer = &dialog->text_layer; - text_layer_init_with_parameters(text_layer, &GRect(x, y, w, h), - dialog->buffer, dialog_text_font, - dialog->text_color, GColorClear, text_alignment, - GTextOverflowModeWordWrap); -#if PBL_ROUND - text_layer_enable_screen_text_flow_and_paging(text_layer, TEXT_FLOW_INSET_PX); -#endif - text_layer_set_line_spacing_delta(text_layer, text_layer_line_spacing_delta); - - layer_add_child(&window->layer, &text_layer->layer); - - if (workout_dialog->subtext_buffer) { - y = subtext_top_margin_px; - - TextLayer *subtext_layer = &workout_dialog->subtext_layer; - text_layer_init_with_parameters(subtext_layer, &GRect(x, y, w, h), - workout_dialog->subtext_buffer, dialog_subtext_font, - dialog->text_color, GColorClear, text_alignment, - GTextOverflowModeWordWrap); -#if PBL_ROUND - text_layer_enable_screen_text_flow_and_paging(subtext_layer, TEXT_FLOW_INSET_PX); -#endif - - layer_add_child(&window->layer, &subtext_layer->layer); - } - - if (show_action_bar) { - action_bar_layer_add_to_window(&workout_dialog->action_bar, window); - } - - // Icon - // On rectangular displays we just center it horizontally b/w the left edge of the display and - // the left edge of the action bar -#if PBL_RECT - x = (grect_get_max_x(bounds) - action_bar_width - icon_size.w) / 2; -#else - // On round displays we right align it with respect to the same imaginary vertical line that the - // text is right aligned to if action bar is present otherwise do what rect does - if (show_action_bar) { - x = grect_get_max_x(bounds) - action_bar_width - content_and_action_bar_horizontal_spacing - - icon_size.w; - } else { - x = (grect_get_max_x(bounds) - action_bar_width - icon_size.w) / 2; - } -#endif - - y = icon_top_margin_px; - - if (dialog_init_icon_layer(dialog, icon, GPoint(x, y), false)) { - layer_add_child(window_root_layer, &dialog->icon_layer.layer); - } - - dialog_load(dialog); -} - -static void prv_workout_dialog_appear(Window *window) { - WorkoutDialog *workout_dialog = window_get_user_data(window); - dialog_appear(&workout_dialog->dialog); -} - -static void prv_workout_dialog_unload(Window *window) { - WorkoutDialog *workout_dialog = window_get_user_data(window); - dialog_unload(&workout_dialog->dialog); - - action_bar_layer_remove_from_window(&workout_dialog->action_bar); - action_bar_layer_deinit(&workout_dialog->action_bar); - - gbitmap_deinit(&workout_dialog->confirm_icon); - gbitmap_deinit(&workout_dialog->decline_icon); - - if (workout_dialog->subtext_buffer) { - applib_free(workout_dialog->subtext_buffer); - } - - if (workout_dialog->dialog.destroy_on_pop) { - applib_free(workout_dialog); - } -} - -void workout_dialog_init(WorkoutDialog *workout_dialog, const char *dialog_name) { - PBL_ASSERTN(workout_dialog); - *workout_dialog = (WorkoutDialog){}; - - dialog_init(&workout_dialog->dialog, dialog_name); - Window *window = &workout_dialog->dialog.window; - window_set_window_handlers(window, &(WindowHandlers) { - .load = prv_workout_dialog_load, - .unload = prv_workout_dialog_unload, - .appear = prv_workout_dialog_appear, - }); - window_set_user_data(window, workout_dialog); - - gbitmap_init_with_resource(&workout_dialog->confirm_icon, RESOURCE_ID_ACTION_BAR_ICON_CHECK); - gbitmap_init_with_resource(&workout_dialog->decline_icon, RESOURCE_ID_ACTION_BAR_ICON_X); - - ActionBarLayer *action_bar = &workout_dialog->action_bar; - action_bar_layer_init(action_bar); - action_bar_layer_set_icon(action_bar, BUTTON_ID_UP, &workout_dialog->confirm_icon); - action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, &workout_dialog->decline_icon); - action_bar_layer_set_background_color(action_bar, GColorBlack); - action_bar_layer_set_context(action_bar, workout_dialog); -} - -WorkoutDialog *workout_dialog_create(const char *dialog_name) { - // Note: Not exported so no need for padding. - WorkoutDialog *workout_dialog = applib_malloc(sizeof(WorkoutDialog)); - if (workout_dialog) { - workout_dialog_init(workout_dialog, dialog_name); - } - return workout_dialog; -} - -Dialog *workout_dialog_get_dialog(WorkoutDialog *workout_dialog) { - return &workout_dialog->dialog; -} - -ActionBarLayer *workout_dialog_get_action_bar(WorkoutDialog *workout_dialog) { - return &workout_dialog->action_bar; -} - -void workout_dialog_set_click_config_provider(WorkoutDialog *workout_dialog, - ClickConfigProvider click_config_provider) { - if (workout_dialog == NULL) { - return; - } - ActionBarLayer *action_bar = &workout_dialog->action_bar; - action_bar_layer_set_click_config_provider(action_bar, click_config_provider); -} - -void workout_dialog_set_click_config_context(WorkoutDialog *workout_dialog, void *context) { - if (workout_dialog == NULL) { - return; - } - ActionBarLayer *action_bar = &workout_dialog->action_bar; - action_bar_layer_set_context(action_bar, context); -} - -void workout_dialog_push(WorkoutDialog *workout_dialog, WindowStack *window_stack) { - dialog_push(&workout_dialog->dialog, window_stack); -} - -void app_workout_dialog_push(WorkoutDialog *workout_dialog) { - app_dialog_push(&workout_dialog->dialog); -} - -void workout_dialog_pop(WorkoutDialog *workout_dialog) { - dialog_pop(&workout_dialog->dialog); -} - -void workout_dialog_set_text(WorkoutDialog *workout_dialog, const char *text) { - dialog_set_text(&workout_dialog->dialog, text); -} - -void workout_dialog_set_subtext(WorkoutDialog *workout_dialog, const char *text) { - if (workout_dialog->subtext_buffer) { - applib_free(workout_dialog->subtext_buffer); - } - uint16_t len = strlen(text); - workout_dialog->subtext_buffer = applib_malloc(len + 1); - strncpy(workout_dialog->subtext_buffer, text, len + 1); -} - -void workout_dialog_set_action_bar_hidden(WorkoutDialog *workout_dialog, bool should_hide) { - workout_dialog->hide_action_bar = should_hide; -} diff --git a/src/fw/apps/system_apps/workout/workout_dialog.h b/src/fw/apps/system_apps/workout/workout_dialog.h deleted file mode 100644 index 8669e8efa6..0000000000 --- a/src/fw/apps/system_apps/workout/workout_dialog.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/action_bar_layer.h" -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/window_stack.h" - -typedef struct WorkoutDialog { - Dialog dialog; - ActionBarLayer action_bar; - GBitmap confirm_icon; - GBitmap decline_icon; - TextLayer subtext_layer; - char *subtext_buffer; - bool hide_action_bar; -} WorkoutDialog; - -void workout_dialog_init(WorkoutDialog *workout_dialog, const char *dialog_name); - -WorkoutDialog *workout_dialog_create(const char *dialog_name); - -Dialog *workout_dialog_get_dialog(WorkoutDialog *workout_dialog); - -ActionBarLayer *workout_dialog_get_action_bar(WorkoutDialog *workout_dialog); - -void workout_dialog_set_click_config_provider(WorkoutDialog *workout_dialog, - ClickConfigProvider click_config_provider); - -void workout_dialog_set_click_config_context(WorkoutDialog *workout_dialog, void *context); - -void workout_dialog_push(WorkoutDialog *workout_dialog, WindowStack *window_stack); - -void app_workout_dialog_push(WorkoutDialog *workout_dialog); - -void workout_dialog_pop(WorkoutDialog *workout_dialog); - -void workout_dialog_set_text(WorkoutDialog *workout_dialog, const char *text); - -void workout_dialog_set_subtext(WorkoutDialog *workout_dialog, const char *text); - -void workout_dialog_set_action_bar_hidden(WorkoutDialog *workout_dialog, bool should_hide); diff --git a/src/fw/apps/system_apps/workout/workout_metrics.h b/src/fw/apps/system_apps/workout/workout_metrics.h deleted file mode 100644 index b41656cef2..0000000000 --- a/src/fw/apps/system_apps/workout/workout_metrics.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef enum { - WorkoutMetricType_None = 0, - WorkoutMetricType_Hr, - WorkoutMetricType_Duration, - WorkoutMetricType_Distance, - WorkoutMetricType_Steps, - WorkoutMetricType_Pace, - WorkoutMetricType_AvgPace, - WorkoutMetricType_Speed, - WorkoutMetricType_Custom, - WorkoutMetricTypeCount, -} WorkoutMetricType; diff --git a/src/fw/apps/system_apps/workout/workout_selection.c b/src/fw/apps/system_apps/workout/workout_selection.c deleted file mode 100644 index 50496210ec..0000000000 --- a/src/fw/apps/system_apps/workout/workout_selection.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_selection.h" -#include "workout_utils.h" - -#include "applib/app.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" - -#include - -typedef enum { - WorkoutType_Run, - WorkoutType_Walk, - WorkoutType_OpenWorkout, - WorkoutTypeCount, -} WorkoutType; - -typedef struct WorkoutSelectionWindow { - Window window; - MenuLayer menu_layer; - GBitmap workout_icons[WorkoutTypeCount]; - SelectWorkoutCallback select_workout_cb; -} WorkoutSelectionWindow; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Helpers - -static uint32_t prv_get_icon_resource_id(WorkoutType workout_type) { - switch (workout_type) { - case WorkoutType_Run: - return RESOURCE_ID_WORKOUT_APP_RUN_SMALL; - case WorkoutType_Walk: - return RESOURCE_ID_WORKOUT_APP_WALK_SMALL; - case WorkoutType_OpenWorkout: - return RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL; - default: - return 0; - } -} - -static ActivitySessionType prv_get_activity_type(WorkoutType workout_type) { - switch (workout_type) { - case WorkoutType_Run: - return ActivitySessionType_Run; - case WorkoutType_Walk: - return ActivitySessionType_Walk; - case WorkoutType_OpenWorkout: - return ActivitySessionType_Open; - default: - return ActivitySessionType_Invalid; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Menu Layer Callbacks - -static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, - void *context) { - return WorkoutTypeCount; -} - -static int16_t prv_get_cell_height_callback(MenuLayer *menu_layer, MenuIndex *cell_index, - void *context) { -#if PBL_RECT - return 56; -#else - return menu_layer_is_index_selected(menu_layer, cell_index) ? 84 : 38; -#endif -} - -static void prv_draw_row_callback(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, - void *context) { - WorkoutSelectionWindow *selection_window = (WorkoutSelectionWindow *)context; - - ActivitySessionType activity_type = prv_get_activity_type(cell_index->row); - - const char *title = workout_utils_get_name_for_activity(activity_type); - const GBitmap *icon = &selection_window->workout_icons[cell_index->row]; - - const int icon_top_padding = 11; - const int title_top_padding = PBL_IF_RECT_ELSE(11, cell_layer->is_highlighted ? 40 : 2); - const int max_icon_w = PBL_IF_RECT_ELSE(55, cell_layer->bounds.size.w); - const int title_origin_x = PBL_IF_RECT_ELSE(max_icon_w, 0); - const GTextAlignment title_alignment = PBL_IF_RECT_ELSE(GTextAlignmentLeft, GTextAlignmentCenter); - const GFont title_font = PBL_IF_RECT_ELSE(fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), - cell_layer->is_highlighted ? fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD) - : fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - const int title_height = fonts_get_font_height(title_font); - - GRect image_bounds = gbitmap_get_bounds(icon); - image_bounds.origin.x = (max_icon_w / 2) - (image_bounds.size.w / 2); - image_bounds.origin.y = icon_top_padding; - -#if PBL_COLOR - GCompOp compositing_mode = GCompOpSet; -#else - GCompOp compositing_mode = cell_layer->is_highlighted ? GCompOpTintLuminance : GCompOpSet; - graphics_context_set_tint_color(ctx, (cell_layer->is_highlighted ? GColorWhite : GColorBlack)); -#endif - - graphics_context_set_compositing_mode(ctx, compositing_mode); - -#if PBL_ROUND - if (cell_layer->is_highlighted) { - graphics_draw_bitmap_in_rect(ctx, icon, &image_bounds); - } -#else - graphics_draw_bitmap_in_rect(ctx, icon, &image_bounds); -#endif - - GRect title_bounds = cell_layer->bounds; - title_bounds.origin.x = title_origin_x; - title_bounds.origin.y = title_top_padding; - title_bounds.size.w -= title_origin_x; - title_bounds.size.h = title_height; - - graphics_draw_text(ctx, i18n_get(title, selection_window), title_font, title_bounds, - GTextOverflowModeFill, title_alignment, NULL); -} - -static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { - WorkoutSelectionWindow *selection_window = (WorkoutSelectionWindow *)context; - - ActivitySessionType activity_type = prv_get_activity_type(cell_index->row); - selection_window->select_workout_cb(activity_type); - window_stack_remove(&selection_window->window, true); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Handlers - -static void prv_window_unload_handler(Window *window) { - WorkoutSelectionWindow *selection_window = window_get_user_data(window); - if (selection_window) { - for (int i = 0; i < WorkoutTypeCount; i++) { - gbitmap_deinit(&selection_window->workout_icons[i]); - } - menu_layer_deinit(&selection_window->menu_layer); - window_deinit(&selection_window->window); - i18n_free_all(selection_window); - app_free(selection_window); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Public API - -WorkoutSelectionWindow *workout_selection_push(SelectWorkoutCallback select_workout_cb) { - WorkoutSelectionWindow *selection_window = app_zalloc_check(sizeof(WorkoutSelectionWindow)); - - selection_window->select_workout_cb = select_workout_cb; - - Window *window = &selection_window->window; - window_init(window, WINDOW_NAME("Workout Selection")); - window_set_user_data(window, selection_window); - window_set_window_handlers(window, &(WindowHandlers){ - .unload = prv_window_unload_handler, - }); - - for (int i = 0; i < WorkoutTypeCount; i++) { - gbitmap_init_with_resource(&selection_window->workout_icons[i], prv_get_icon_resource_id(i)); - } - - MenuLayer *menu_layer = &selection_window->menu_layer; - menu_layer_init(menu_layer, &window->layer.bounds); - menu_layer_pad_bottom_enable(menu_layer, false); - menu_layer_set_callbacks(menu_layer, selection_window, &(MenuLayerCallbacks){ - .get_num_rows = prv_get_num_rows_callback, - .get_cell_height = prv_get_cell_height_callback, - .draw_row = prv_draw_row_callback, - .select_click = prv_select_callback, - }); - menu_layer_set_normal_colors(menu_layer, GColorWhite, GColorBlack); - menu_layer_set_highlight_colors(menu_layer, - PBL_IF_COLOR_ELSE(GColorYellow, GColorBlack), - PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)); - menu_layer_set_click_config_onto_window(menu_layer, &selection_window->window); - layer_add_child(&selection_window->window.layer, menu_layer_get_layer(menu_layer)); - - app_window_stack_push(&selection_window->window, true); - - return selection_window; -} diff --git a/src/fw/apps/system_apps/workout/workout_selection.h b/src/fw/apps/system_apps/workout/workout_selection.h deleted file mode 100644 index 0f7d040348..0000000000 --- a/src/fw/apps/system_apps/workout/workout_selection.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/activity/activity.h" - -typedef void (*SelectWorkoutCallback)(ActivitySessionType type); - -typedef struct WorkoutSelectionWindow WorkoutSelectionWindow; - -WorkoutSelectionWindow *workout_selection_push(SelectWorkoutCallback select_workout_cb); diff --git a/src/fw/apps/system_apps/workout/workout_summary.c b/src/fw/apps/system_apps/workout/workout_summary.c deleted file mode 100644 index 48c4cfbe7a..0000000000 --- a/src/fw/apps/system_apps/workout/workout_summary.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_summary.h" -#include "workout_countdown.h" -#include "workout_selection.h" -#include "workout_utils.h" - -#include "applib/app.h" -#include "applib/ui/action_menu_hierarchy.h" -#include "applib/ui/action_menu_window.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/health_util.h" -#include "services/normal/activity/workout_service.h" -#include "system/logging.h" - -#include - -#define BACKGROUND_COLOR PBL_IF_COLOR_ELSE(GColorYellow, GColorWhite) -#define TEXT_COLOR (gcolor_legible_over(BACKGROUND_COLOR)) - -typedef struct WorkoutSummaryWindow { - Window window; - ActionBarLayer action_bar; - StatusBarLayer status_layer; - Layer base_layer; - - GBitmap *action_bar_start; - GBitmap *action_bar_more; - - ActivitySessionType activity_type; - - KinoReel *icon; - const char *name; - - StartWorkoutCallback start_workout_cb; - SelectWorkoutCallback select_workout_cb; -} WorkoutSummaryWindow; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Helpers - -static KinoReel* prv_get_icon_for_activity(ActivitySessionType type) { - switch (type) { - case ActivitySessionType_Open: - return kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_WORKOUT); - case ActivitySessionType_Walk: - return kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_WALK); - case ActivitySessionType_Run: - default: - return kino_reel_create_with_resource(RESOURCE_ID_WORKOUT_APP_RUN); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Drawing - -static void prv_render_activity_type(GContext *ctx, Layer *layer, KinoReel *icon, - const char *name) { - const GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); - const GTextOverflowMode overflow_mode = GTextOverflowModeWordWrap; - const GTextAlignment alignment = GTextAlignmentCenter; - const int16_t rl_margin = PBL_IF_RECT_ELSE(4, 16); - - GRect drawing_rect = grect_inset(layer->bounds, GEdgeInsets(0, rl_margin)); - - const GSize icon_size = kino_reel_get_size(icon); - const int icon_x = drawing_rect.origin.x + PBL_IF_RECT_ELSE(0, (rl_margin / 2)) - + (drawing_rect.size.w / 2) - (icon_size.w / 2); - const int icon_y = PBL_IF_RECT_ELSE(45, 49); - kino_reel_draw(icon, ctx, GPoint(icon_x, icon_y)); - - const int name_x = drawing_rect.origin.x + PBL_IF_RECT_ELSE(0, (rl_margin / 2)); - const int name_y = PBL_IF_RECT_ELSE(107, 109); - GRect name_rect = GRect(name_x, name_y, drawing_rect.size.w, 32); - - graphics_context_set_text_color(ctx, TEXT_COLOR); - graphics_draw_text(ctx, name, font, name_rect, overflow_mode, alignment, NULL); -} - -static void prv_base_layer_update_proc(struct Layer *layer, GContext *ctx) { - WorkoutSummaryWindow *summary_window = window_get_user_data(layer_get_window(layer)); - - prv_render_activity_type(ctx, layer, summary_window->icon, summary_window->name); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Handlers - -static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutSummaryWindow *summary_window = context; - - workout_countdown_start(summary_window->activity_type, summary_window->start_workout_cb); - - window_stack_remove(&summary_window->window, false); -} - -static void prv_down_click_handler(ClickRecognizerRef recognizer, void *context) { - WorkoutSummaryWindow *summary_window = context; - - workout_selection_push(summary_window->select_workout_cb); -} - -static void prv_click_config_provider(void *context) { - window_set_click_context(BUTTON_ID_UP, context); - window_set_click_context(BUTTON_ID_DOWN, context); - window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); -} - -static void prv_window_unload_handler(Window *window) { - WorkoutSummaryWindow *summary_window = window_get_user_data(window); - if (summary_window) { - kino_reel_destroy(summary_window->icon); - gbitmap_destroy(summary_window->action_bar_more); - gbitmap_destroy(summary_window->action_bar_start); - action_bar_layer_deinit(&summary_window->action_bar); - status_bar_layer_deinit(&summary_window->status_layer); - layer_deinit(&summary_window->base_layer); - window_deinit(&summary_window->window); - i18n_free_all(summary_window); - app_free(summary_window); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//! Public API - -WorkoutSummaryWindow *workout_summary_window_create(ActivitySessionType activity_type, - StartWorkoutCallback start_workout_cb, - SelectWorkoutCallback select_workout_cb) { - WorkoutSummaryWindow *summary_window = app_zalloc_check(sizeof(WorkoutSummaryWindow)); - - summary_window->start_workout_cb = start_workout_cb; - summary_window->select_workout_cb = select_workout_cb; - - Window *window = &summary_window->window; - window_init(window, WINDOW_NAME("Workout Summary")); - window_set_user_data(window, summary_window); - window_set_background_color(window, BACKGROUND_COLOR); - window_set_window_handlers(window, &(WindowHandlers){ - .unload = prv_window_unload_handler, - }); - - GRect layer_bounds = window->layer.bounds; - layer_bounds.size.w -= ACTION_BAR_WIDTH; - - layer_init(&summary_window->base_layer, &layer_bounds); - layer_set_update_proc(&summary_window->base_layer, prv_base_layer_update_proc); - layer_add_child(&window->layer, &summary_window->base_layer); - - StatusBarLayer *status_layer = &summary_window->status_layer; - status_bar_layer_init(status_layer); - status_bar_layer_set_colors(status_layer, GColorClear, TEXT_COLOR); - layer_add_child(&window->layer, status_bar_layer_get_layer(status_layer)); - -#if PBL_RECT - GRect status_layer_bounds = window->layer.bounds; - status_layer_bounds.size.w -= ACTION_BAR_WIDTH; - layer_set_frame(&status_layer->layer, &status_layer_bounds); -#endif - - ActionBarLayer *action_bar = &summary_window->action_bar; - action_bar_layer_init(action_bar); - action_bar_layer_set_context(action_bar, summary_window); - action_bar_layer_set_click_config_provider(action_bar, prv_click_config_provider); - action_bar_layer_add_to_window(action_bar, window); - - summary_window->action_bar_start = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_START); - summary_window->action_bar_more = - gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_MORE); - - action_bar_layer_set_icon(action_bar, BUTTON_ID_UP, summary_window->action_bar_start); - action_bar_layer_set_icon(action_bar, BUTTON_ID_DOWN, summary_window->action_bar_more); - - workout_summary_update_activity_type(summary_window, activity_type); - - return summary_window; -} - -void workout_summary_window_push(WorkoutSummaryWindow *summary_window) { - app_window_stack_push(&summary_window->window, true); -} - -void workout_summary_update_activity_type(WorkoutSummaryWindow *summary_window, - ActivitySessionType activity_type) { - summary_window->activity_type = activity_type; - summary_window->icon = prv_get_icon_for_activity(activity_type); - summary_window->name = workout_utils_get_name_for_activity(activity_type); -} diff --git a/src/fw/apps/system_apps/workout/workout_summary.h b/src/fw/apps/system_apps/workout/workout_summary.h deleted file mode 100644 index 78eb33078d..0000000000 --- a/src/fw/apps/system_apps/workout/workout_summary.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "workout_selection.h" - -#include "services/normal/activity/activity.h" - -typedef void (*StartWorkoutCallback)(ActivitySessionType type); - -typedef struct WorkoutSummaryWindow WorkoutSummaryWindow; - -WorkoutSummaryWindow *workout_summary_window_create(ActivitySessionType activity_type, - StartWorkoutCallback start_workout_cb, - SelectWorkoutCallback select_workout_cb); - -void workout_summary_window_push(WorkoutSummaryWindow *window); - -void workout_summary_update_activity_type(WorkoutSummaryWindow *summary_window, - ActivitySessionType activity_type); diff --git a/src/fw/apps/system_apps/workout/workout_utils.c b/src/fw/apps/system_apps/workout/workout_utils.c deleted file mode 100644 index 4085d2e5be..0000000000 --- a/src/fw/apps/system_apps/workout/workout_utils.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_utils.h" -#include "workout.h" - -#include "kernel/pbl_malloc.h" -#include "resource/timeline_resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/workout_service.h" -#include "services/normal/timeline/timeline.h" -#include "system/passert.h" -#include "util/attributes.h" - -static TimelineItem *prv_create_abandoned_workout_notification(void) { - const char *msg = i18n_noop("Still sweating? Your workout is active and will be ended soon. " - "Open the workout to keep it going."); - - AttributeList notif_attr_list = {0}; - attribute_list_add_uint32(¬if_attr_list, AttributeIdIconTiny, TIMELINE_RESOURCE_ACTIVITY); - attribute_list_add_cstring(¬if_attr_list, AttributeIdBody, i18n_get(msg, ¬if_attr_list)); - attribute_list_add_uint8(¬if_attr_list, AttributeIdBgColor, - PBL_IF_COLOR_ELSE(GColorYellowARGB8, GColorDarkGrayARGB8)); - - AttributeList dismiss_attr_list = {0}; - attribute_list_add_cstring(&dismiss_attr_list, AttributeIdTitle, - i18n_get("Dismiss", ¬if_attr_list)); - - AttributeList end_workout_attr_list = {0}; - attribute_list_add_cstring(&end_workout_attr_list, AttributeIdTitle, - i18n_get("End Workout", ¬if_attr_list)); - attribute_list_add_uint32(&end_workout_attr_list, AttributeIdLaunchCode, - WorkoutLaunchArg_EndWorkout); - - AttributeList open_workout_attr_list = {0}; - attribute_list_add_cstring(&open_workout_attr_list, AttributeIdTitle, - i18n_get("Open Workout", ¬if_attr_list)); - - const int num_actions = 3; - TimelineItemActionGroup action_group = { - .num_actions = num_actions, - .actions = (TimelineItemAction[]) { - { - .id = 0, - .type = TimelineItemActionTypeDismiss, - .attr_list = dismiss_attr_list, - }, - { - .id = 1, - .type = TimelineItemActionTypeOpenWatchApp, - .attr_list = end_workout_attr_list, - }, - { - .id = 2, - .type = TimelineItemActionTypeOpenWatchApp, - .attr_list = open_workout_attr_list, - }, - }, - }; - - const time_t now_utc = rtc_get_time(); - - // Note: it's fine if this returns null, since the parent functions will check for a null pointer - TimelineItem *item = timeline_item_create_with_attributes(now_utc, 0, - TimelineItemTypeNotification, - LayoutIdNotification, ¬if_attr_list, - &action_group); - - i18n_free_all(¬if_attr_list); - attribute_list_destroy_list(¬if_attr_list); - attribute_list_destroy_list(&end_workout_attr_list); - attribute_list_destroy_list(&open_workout_attr_list); - - return item; -} - -void workout_utils_send_abandoned_workout_notification(void) { - TimelineItem *item = prv_create_abandoned_workout_notification(); - if (item) { - item->header.from_watch = true; - item->header.parent_id = (Uuid)UUID_WORKOUT_DATA_SOURCE; - notifications_add_notification(item); - timeline_item_destroy(item); - } -} - -const char* workout_utils_get_name_for_activity(ActivitySessionType type) { - switch (type) { - case ActivitySessionType_Open: - /// Workout Label - return i18n_noop("Workout"); - case ActivitySessionType_Walk: - /// Walk Label - return i18n_noop("Walk"); - case ActivitySessionType_Run: - /// Run Label - return i18n_noop("Run"); - case ActivitySessionType_Sleep: - case ActivitySessionType_RestfulSleep: - case ActivitySessionType_Nap: - case ActivitySessionType_RestfulNap: - case ActivitySessionType_None: - case ActivitySessionTypeCount: - // ActivitySessionType_Invalid should have the same value - break; - } - - WTF; -} - -const char* workout_utils_get_detection_text_for_activity(ActivitySessionType type) { - switch (type) { - case ActivitySessionType_Open: - /// Workout automatically detected dialog text - return i18n_noop("Workout\nDetected"); - case ActivitySessionType_Walk: - /// Walk automatically detected dialog text - return i18n_noop("Walk\nDetected"); - case ActivitySessionType_Run: - /// Run automatically detected dialog text - return i18n_noop("Run\nDetected"); - case ActivitySessionType_Sleep: - case ActivitySessionType_RestfulSleep: - case ActivitySessionType_Nap: - case ActivitySessionType_RestfulNap: - case ActivitySessionType_None: - case ActivitySessionTypeCount: - // ActivitySessionType_Invalid should have the same value - break; - } - - WTF; -} - -bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out) { - bool found_session = false; - - uint32_t num_sessions = ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT; - ActivitySession *sessions = app_zalloc_check(sizeof(ActivitySession) * - ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT); - activity_get_sessions(&num_sessions, sessions); - - for (int i = num_sessions; i >= 0; i--) { - if (workout_service_is_workout_type_supported(sessions[i].type) && sessions[i].ongoing) { - if (session_out) { - memcpy(session_out, &sessions[i], sizeof(ActivitySession)); - } - found_session = true; - break; - } - } - - app_free(sessions); - - return found_session; -} diff --git a/src/fw/apps/system_apps/workout/workout_utils.h b/src/fw/apps/system_apps/workout/workout_utils.h deleted file mode 100644 index 9b4be89b18..0000000000 --- a/src/fw/apps/system_apps/workout/workout_utils.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/activity/activity.h" - -void workout_utils_send_abandoned_workout_notification(void); - -const char* workout_utils_get_name_for_activity(ActivitySessionType type); - -const char* workout_utils_get_detection_text_for_activity(ActivitySessionType type); - -bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out); diff --git a/src/fw/apps/watch/kickstart/kickstart.c b/src/fw/apps/watch/kickstart/kickstart.c index e858258ae2..185fb47615 100644 --- a/src/fw/apps/watch/kickstart/kickstart.c +++ b/src/fw/apps/watch/kickstart/kickstart.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kickstart.h" @@ -21,13 +8,13 @@ #include "applib/tick_timer_service.h" #include "applib/ui/app_window_stack.h" #include "applib/ui/ui.h" -#include "apps/system_apps/timeline/text_node.h" +#include "apps/system/timeline/text_node.h" #include "kernel/pbl_malloc.h" #include "applib/pbl_std/pbl_std.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/normal/activity/health_util.h" +#include "pbl/services/clock.h" +#include "pbl/services/activity/health_util.h" #include "util/size.h" #include "util/string.h" #include "util/time/time.h" @@ -35,9 +22,10 @@ #include -#define ROBERT_SCREEN_RES (PBL_DISPLAY_WIDTH == 200 && PBL_DISPLAY_HEIGHT == 228) +#define EMERY_SCREEN_RES (PBL_DISPLAY_WIDTH == 200 && PBL_DISPLAY_HEIGHT == 228) #define SNOWY_SCREEN_RES (PBL_DISPLAY_WIDTH == 144 && PBL_DISPLAY_HEIGHT == 168) #define SPALDING_SCREEN_RES (PBL_DISPLAY_WIDTH == 180 && PBL_DISPLAY_HEIGHT == 180) +#define GETAFIX_SCREEN_RES (PBL_DISPLAY_WIDTH == 260 && PBL_DISPLAY_HEIGHT == 260) //////////////////////////////////////////////////////////////////////////////////////////////////// // UI Utils @@ -241,7 +229,7 @@ static void prv_draw_goal_line(GContext *ctx, int32_t current_progress, int32_t graphics_draw_line(ctx, line_inner_point, line_outer_point); } -#if ROBERT_SCREEN_RES +#if EMERY_SCREEN_RES static void prv_draw_seperator(GContext *ctx, GRect bounds, GColor color) { bounds.origin.y += 111; // top offset @@ -260,7 +248,7 @@ static void prv_draw_steps_and_shoe(GContext *ctx, const char *steps_buffer, GFo bool screen_is_obstructed, bool has_bpm) { #if PBL_BW bounds.origin.y += screen_is_obstructed ? (has_bpm ? 74 : 66) : (has_bpm ? 114 : 96); -#elif ROBERT_SCREEN_RES +#elif EMERY_SCREEN_RES bounds.origin.y += screen_is_obstructed ? 113 : 158; #elif SNOWY_SCREEN_RES if (screen_is_obstructed) { @@ -273,7 +261,7 @@ static void prv_draw_steps_and_shoe(GContext *ctx, const char *steps_buffer, GFo #if PBL_BW icon_bounds.origin.x += 23; // icon left offset icon_bounds.origin.y += 9; // icon top offset -#elif ROBERT_SCREEN_RES +#elif EMERY_SCREEN_RES icon_bounds.origin.y += (46 - icon_bounds.size.h); // icon top offest #elif SNOWY_SCREEN_RES icon_bounds.origin.x = screen_is_obstructed ? bounds.origin.x // icon_left offset @@ -282,6 +270,9 @@ static void prv_draw_steps_and_shoe(GContext *ctx, const char *steps_buffer, GFo #elif SPALDING_SCREEN_RES icon_bounds.origin.x = (bounds.size.w / 2) - (icon_bounds.size.w / 2); icon_bounds.origin.y += 27; // icon top offset +#elif GETAFIX_SCREEN_RES + icon_bounds.origin.x = (bounds.size.w / 2) - (icon_bounds.size.w / 2); + icon_bounds.origin.y += 39; // icon top offset #endif graphics_context_set_compositing_mode(ctx, GCompOpSet); @@ -290,7 +281,7 @@ static void prv_draw_steps_and_shoe(GContext *ctx, const char *steps_buffer, GFo #if PBL_BW const GTextAlignment alignment = GTextAlignmentLeft; bounds.origin.x += 62; // steps text left offset -#elif ROBERT_SCREEN_RES +#elif EMERY_SCREEN_RES const GTextAlignment alignment = GTextAlignmentRight; #elif SNOWY_SCREEN_RES const GTextAlignment alignment = screen_is_obstructed ? GTextAlignmentRight: GTextAlignmentCenter; @@ -298,6 +289,9 @@ static void prv_draw_steps_and_shoe(GContext *ctx, const char *steps_buffer, GFo #elif SPALDING_SCREEN_RES const GTextAlignment alignment = GTextAlignmentCenter; bounds.origin.y += 113; // steps text top offset +#elif GETAFIX_SCREEN_RES + const GTextAlignment alignment = GTextAlignmentCenter; + bounds.origin.y += 163; // steps text top offset #endif graphics_context_set_text_color(ctx, color); @@ -330,24 +324,26 @@ static void prv_draw_time(GContext *ctx, GFont time_font, GFont am_pm_font, GRec #if PBL_BW bounds.origin.y = screen_is_obstructed ? (has_bpm ? 13 : 23) : (has_bpm ? 36 : 53); -#elif ROBERT_SCREEN_RES +#elif EMERY_SCREEN_RES bounds.origin.y = screen_is_obstructed ? -12 : 6; #elif SNOWY_SCREEN_RES bounds.origin.y = screen_is_obstructed ? 4 : 47; #elif SPALDING_SCREEN_RES bounds.origin.y = 50; +#elif GETAFIX_SCREEN_RES + bounds.origin.y = 72; #endif graphics_text_node_draw(&container->node, ctx, &bounds, NULL, NULL); graphics_text_node_destroy(&container->node); } -#if PBL_BW || ROBERT_SCREEN_RES +#if PBL_BW || EMERY_SCREEN_RES static void prv_draw_bpm(GContext *ctx, int32_t current_bpm, GFont font, GBitmap *heart_icon, GRect bounds, bool screen_is_obstructed, void *i18n_owner) { #if PBL_BW bounds.origin.y += screen_is_obstructed ? 52 : 89; -#elif ROBERT_SCREEN_RES +#elif EMERY_SCREEN_RES bounds.origin.y += screen_is_obstructed ? 80 : 123; #endif @@ -395,10 +391,12 @@ static void prv_base_layer_update_proc(Layer *layer, GContext *ctx) { #if SNOWY_SCREEN_RES const int16_t fill_thickness = screen_is_obstructed ? 10 : 11; -#elif ROBERT_SCREEN_RES +#elif EMERY_SCREEN_RES const int16_t fill_thickness = screen_is_obstructed ? 5 : 13; #elif SPALDING_SCREEN_RES const int16_t fill_thickness = (bounds.size.h - grect_inset(bounds, GEdgeInsets(15)).size.h) / 2; +#elif GETAFIX_SCREEN_RES + const int16_t fill_thickness = (bounds.size.h - grect_inset(bounds, GEdgeInsets(20)).size.h) / 2; #endif #if PBL_COLOR @@ -439,7 +437,7 @@ static void prv_base_layer_update_proc(Layer *layer, GContext *ctx) { prv_draw_time(ctx, data->time_font, PBL_IF_COLOR_ELSE(data->am_pm_font, data->time_font), bounds, screen_is_obstructed, has_bpm); -#if ROBERT_SCREEN_RES +#if EMERY_SCREEN_RES bounds = grect_inset(bounds, GEdgeInsets(0, 25)); // draw deperator @@ -448,7 +446,7 @@ static void prv_base_layer_update_proc(Layer *layer, GContext *ctx) { } #endif -#if PBL_BW || ROBERT_SCREEN_RES +#if PBL_BW || EMERY_SCREEN_RES // draw bpm and heart if (has_bpm) { prv_draw_bpm(ctx, data->current_bpm, data->steps_font, &data->heart_icon, bounds, @@ -561,7 +559,7 @@ T_STATIC void prv_window_load_handler(Window *window) { #else gbitmap_init_with_resource(&data->shoe_blue, RESOURCE_ID_STRIDE_SHOE_BLUE); gbitmap_init_with_resource(&data->shoe_green, RESOURCE_ID_STRIDE_SHOE_GREEN); -#if ROBERT_SCREEN_RES +#if EMERY_SCREEN_RES || GETAFIX_SCREEN_RES gbitmap_init_with_resource(&data->heart_icon, RESOURCE_ID_STRIDE_HEART); data->steps_font = fonts_get_system_font(FONT_KEY_AGENCY_FB_46_NUMBERS_AM_PM); data->time_font = fonts_get_system_font(FONT_KEY_AGENCY_FB_88_NUMBERS_AM_PM); @@ -576,7 +574,7 @@ T_STATIC void prv_window_load_handler(Window *window) { data->am_pm_font = fonts_get_system_font(FONT_KEY_AGENCY_FB_60_THIN_NUMBERS_AM_PM); #else #error "Undefined screen size" -#endif // ROBERT_SCREEN_RES +#endif // EMERY_SCREEN_RES #endif // PBL_BW Layer *window_layer = window_get_root_layer(window); diff --git a/src/fw/apps/watch/kickstart/kickstart.h b/src/fw/apps/watch/kickstart/kickstart.h index 6ba40c4b84..c249a9ca7c 100644 --- a/src/fw/apps/watch/kickstart/kickstart.h +++ b/src/fw/apps/watch/kickstart/kickstart.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/apps/watch/low_power/face.c b/src/fw/apps/watch/low_power/face.c new file mode 100644 index 0000000000..77a34529ea --- /dev/null +++ b/src/fw/apps/watch/low_power/face.c @@ -0,0 +1,163 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "face.h" + +#include "applib/app.h" +#include "applib/graphics/gdraw_command_image.h" +#include "applib/graphics/text.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/kino/kino_layer.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/clock.h" +#include "util/time/time.h" + +#include + +typedef struct { + Window low_power_window; + TextLayer low_power_time_layer; + TextLayer low_power_date_layer; + KinoLayer low_power_kino_layer; + char time_text[6]; + char date_text[16]; +} LowPowerFaceData; + +static LowPowerFaceData *s_low_power_data; + +static void prv_minute_tick(struct tm *tick_time, TimeUnits units_changed) { + char *time_format; + + if (clock_is_24h_style()) { + time_format = "%R"; + } else { + time_format = "%I:%M"; + } + + strftime(s_low_power_data->time_text, sizeof(s_low_power_data->time_text), + time_format, tick_time); + + // Remove leading zero from hour in case of 12h mode + if (!clock_is_24h_style() && (s_low_power_data->time_text[0] == '0')) { + text_layer_set_text(&s_low_power_data->low_power_time_layer, + s_low_power_data->time_text+1); + } else { + text_layer_set_text(&s_low_power_data->low_power_time_layer, + s_low_power_data->time_text); + } + + if (units_changed & DAY_UNIT) { + clock_get_date_tm(s_low_power_data->date_text, sizeof(s_low_power_data->date_text), + tick_time); + text_layer_set_text(&s_low_power_data->low_power_date_layer, + s_low_power_data->date_text); + } +} + +static void deinit(void) { + tick_timer_service_unsubscribe(); + kino_layer_deinit(&s_low_power_data->low_power_kino_layer); + + app_free(s_low_power_data); +} + +static void init(void) { + s_low_power_data = app_malloc_check(sizeof(*s_low_power_data)); + + window_init(&s_low_power_data->low_power_window, "Low Power"); + window_set_background_color(&s_low_power_data->low_power_window, GColorLightGray); + app_window_stack_push(&s_low_power_data->low_power_window, true /* Animated */); + +#if PBL_DISPLAY_HEIGHT >= 200 + const GFont text_font = fonts_get_system_font(FONT_KEY_LECO_60_NUMBERS_AM_PM); +#else + const GFont text_font = fonts_get_system_font(FONT_KEY_LECO_42_NUMBERS); +#endif + const GTextAlignment text_alignment = GTextAlignmentCenter; + const unsigned int font_height = fonts_get_font_height(text_font); + const GTextOverflowMode text_overflow_mode = GTextOverflowModeTrailingEllipsis; + const GSize text_size = app_graphics_text_layout_get_content_size("00:00", text_font, + s_low_power_data->low_power_window.layer.bounds, text_overflow_mode, + text_alignment); +#if PBL_DISPLAY_HEIGHT >= 200 + const int text_pos_y_adjust = -9; +#else + const int text_pos_y_adjust = -20; +#endif + const int text_pos_y = (DISP_ROWS / 2) - (font_height / 2) + text_pos_y_adjust; + const GRect text_container_rect = GRect(0, text_pos_y, DISP_COLS, font_height); + GRect text_frame = (GRect) { .size = text_size }; + grect_align(&text_frame, &text_container_rect, GAlignTop, false); + + kino_layer_init(&s_low_power_data->low_power_kino_layer, + &s_low_power_data->low_power_window.layer.bounds); + kino_layer_set_reel_with_resource(&s_low_power_data->low_power_kino_layer, + RESOURCE_ID_BATTERY_NEEDS_CHARGING); + kino_layer_set_alignment(&s_low_power_data->low_power_kino_layer, GAlignBottom); + // TODO PBL-30180: Design needs to revise icon so it doesn't have a rounded cap at the bottom + s_low_power_data->low_power_kino_layer.layer.frame.origin.y += 2; + layer_add_child(&s_low_power_data->low_power_window.layer, + &s_low_power_data->low_power_kino_layer.layer); + + text_layer_init_with_parameters(&s_low_power_data->low_power_time_layer, + &s_low_power_data->low_power_window.layer.frame, NULL, text_font, + GColorBlack, GColorClear, text_alignment, text_overflow_mode); + layer_set_frame(&s_low_power_data->low_power_time_layer.layer, &text_frame); + layer_add_child(&s_low_power_data->low_power_window.layer, + &s_low_power_data->low_power_time_layer.layer); + +#if PBL_DISPLAY_HEIGHT >= 200 + const GFont date_font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD); +#else + const GFont date_font = fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD); +#endif + const unsigned int date_font_height = fonts_get_font_height(date_font); + const GRect icon_bounds = kino_layer_get_reel_bounds(&s_low_power_data->low_power_kino_layer); + const int icon_top_y = + s_low_power_data->low_power_kino_layer.layer.frame.origin.y + icon_bounds.origin.y; + const int time_bottom_y = text_frame.origin.y + text_frame.size.h; + const GRect date_frame = (GRect) { + .origin = { 0, time_bottom_y + (icon_top_y - time_bottom_y - (int)date_font_height) / 2 - 3 }, + .size = { DISP_COLS, date_font_height }, + }; + text_layer_init_with_parameters(&s_low_power_data->low_power_date_layer, + &date_frame, NULL, date_font, + GColorBlack, GColorClear, text_alignment, text_overflow_mode); + layer_add_child(&s_low_power_data->low_power_window.layer, + &s_low_power_data->low_power_date_layer.layer); + + // Because of the delay before the tick timer service first calls prv_minute_tick, + // we call it ourselves to update the time right away + struct tm current_time; + clock_get_time_tm(¤t_time); + prv_minute_tick(¤t_time, MINUTE_UNIT | HOUR_UNIT | DAY_UNIT); + + tick_timer_service_subscribe(MINUTE_UNIT, prv_minute_tick); +} + +static void low_power_main(void) { + init(); + + app_event_loop(); + + deinit(); +} + +const PebbleProcessMd* low_power_face_get_app_info() { + static const PebbleProcessMdSystem s_app_md = { + .common = { + // UUID: e9475244-5bbe-4e0f-a637-a218af4c3110 + .uuid = {0xe9, 0x47, 0x52, 0x44, 0x5b, 0xbe, 0x4e, 0x0f, 0xa6, 0x37, 0xa2, 0x18, 0xaf, 0x4c, 0x31, 0x10}, + .main_func = low_power_main, + .process_type = ProcessTypeWatchface, + .visibility = ProcessVisibilityHidden, + }, + .name = "Watch Only" + }; + return (const PebbleProcessMd*) &s_app_md; +} diff --git a/src/fw/apps/watch/low_power/face.h b/src/fw/apps/watch/low_power/face.h new file mode 100644 index 0000000000..6b81a7d1c9 --- /dev/null +++ b/src/fw/apps/watch/low_power/face.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "process_management/pebble_process_md.h" + +const PebbleProcessMd* low_power_face_get_app_info(); diff --git a/src/fw/apps/watch/low_power/low_power_face.c b/src/fw/apps/watch/low_power/low_power_face.c deleted file mode 100644 index bbc289dbd1..0000000000 --- a/src/fw/apps/watch/low_power/low_power_face.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "low_power_face.h" - -#include "applib/app.h" -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/text.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/kino/kino_layer.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/clock.h" -#include "util/time/time.h" - -#include - -typedef struct { - Window low_power_window; - TextLayer low_power_time_layer; - KinoLayer low_power_kino_layer; - char time_text[6]; -} LowPowerFaceData; - -static LowPowerFaceData *s_low_power_data; - -static void prv_minute_tick(struct tm *tick_time, TimeUnits units_changed) { - char *time_format; - - if (clock_is_24h_style()) { - time_format = "%R"; - } else { - time_format = "%I:%M"; - } - - strftime(s_low_power_data->time_text, sizeof(s_low_power_data->time_text), - time_format, tick_time); - - // Remove leading zero from hour in case of 12h mode - if (!clock_is_24h_style() && (s_low_power_data->time_text[0] == '0')) { - text_layer_set_text(&s_low_power_data->low_power_time_layer, - s_low_power_data->time_text+1); - } else { - text_layer_set_text(&s_low_power_data->low_power_time_layer, - s_low_power_data->time_text); - } - -} - -static void deinit(void) { - tick_timer_service_unsubscribe(); - kino_layer_deinit(&s_low_power_data->low_power_kino_layer); - - app_free(s_low_power_data); -} - -static void init(void) { - s_low_power_data = app_malloc_check(sizeof(*s_low_power_data)); - - window_init(&s_low_power_data->low_power_window, "Low Power"); - window_set_background_color(&s_low_power_data->low_power_window, GColorLightGray); - app_window_stack_push(&s_low_power_data->low_power_window, true /* Animated */); - - const GFont text_font = fonts_get_system_font(FONT_KEY_LECO_42_NUMBERS); - const GTextAlignment text_alignment = GTextAlignmentCenter; - const unsigned int font_height = fonts_get_font_height(text_font); - const GTextOverflowMode text_overflow_mode = GTextOverflowModeTrailingEllipsis; - const GSize text_size = app_graphics_text_layout_get_content_size("00:00", text_font, - s_low_power_data->low_power_window.layer.bounds, text_alignment, - text_overflow_mode); - const int text_pos_y_adjust = -9; // small vertical adjustment to match design specification - const int text_pos_y = (DISP_ROWS / 2) - (font_height / 2) + text_pos_y_adjust; - const GRect text_container_rect = GRect(0, text_pos_y, DISP_COLS, font_height); - GRect text_frame = (GRect) { .size = text_size }; - grect_align(&text_frame, &text_container_rect, GAlignTop, false); - - kino_layer_init(&s_low_power_data->low_power_kino_layer, - &s_low_power_data->low_power_window.layer.bounds); - kino_layer_set_reel_with_resource(&s_low_power_data->low_power_kino_layer, - RESOURCE_ID_BATTERY_NEEDS_CHARGING); - kino_layer_set_alignment(&s_low_power_data->low_power_kino_layer, GAlignBottom); - // TODO PBL-30180: Design needs to revise icon so it doesn't have a rounded cap at the bottom - s_low_power_data->low_power_kino_layer.layer.frame.origin.y += 2; - layer_add_child(&s_low_power_data->low_power_window.layer, - &s_low_power_data->low_power_kino_layer.layer); - - text_layer_init_with_parameters(&s_low_power_data->low_power_time_layer, - &s_low_power_data->low_power_window.layer.frame, NULL, text_font, - GColorBlack, GColorClear, text_alignment, text_overflow_mode); - layer_set_frame(&s_low_power_data->low_power_time_layer.layer, &text_frame); - layer_add_child(&s_low_power_data->low_power_window.layer, - &s_low_power_data->low_power_time_layer.layer); - - // Because of the delay before the tick timer service first calls prv_minute_tick, - // we call it ourselves to update the time right away - struct tm current_time; - clock_get_time_tm(¤t_time); - prv_minute_tick(¤t_time, HOUR_UNIT); - - tick_timer_service_subscribe(MINUTE_UNIT, prv_minute_tick); -} - -static void low_power_main(void) { - init(); - - app_event_loop(); - - deinit(); -} - -const PebbleProcessMd* low_power_face_get_app_info() { - static const PebbleProcessMdSystem s_app_md = { - .common = { - // UUID: e9475244-5bbe-4e0f-a637-a218af4c3110 - .uuid = {0xe9, 0x47, 0x52, 0x44, 0x5b, 0xbe, 0x4e, 0x0f, 0xa6, 0x37, 0xa2, 0x18, 0xaf, 0x4c, 0x31, 0x10}, - .main_func = low_power_main, - .process_type = ProcessTypeWatchface, - .visibility = ProcessVisibilityHidden, - }, - .name = "Watch Only" - }; - return (const PebbleProcessMd*) &s_app_md; -} diff --git a/src/fw/apps/watch/low_power/low_power_face.h b/src/fw/apps/watch/low_power/low_power_face.h deleted file mode 100644 index cd48482e47..0000000000 --- a/src/fw/apps/watch/low_power/low_power_face.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* low_power_face_get_app_info(); diff --git a/src/fw/apps/watch/tictoc/bw/tictoc_bw.c b/src/fw/apps/watch/tictoc/bw/tictoc_bw.c new file mode 100644 index 0000000000..3487243aaa --- /dev/null +++ b/src/fw/apps/watch/tictoc/bw/tictoc_bw.c @@ -0,0 +1,146 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app.h" +#include "applib/tick_timer_service.h" +#include "applib/ui/ui.h" +#include "applib/unobstructed_area_service.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "util/time/time.h" + +#include +#include + +typedef struct { + Window window; + TextLayer text_date_layer; + TextLayer text_time_layer; + Layer line_layer; + char time_text[6]; + char date_text[13]; +} TicTocData; + +static void prv_line_layer_update_callback(Layer *me, GContext* ctx) { + GRect bounds; + layer_get_bounds(me, &bounds); + GRect unobstructed_bounds; + layer_get_unobstructed_bounds(me, &unobstructed_bounds); + + // Calculate how much we need to shift up + int16_t obstruction = bounds.size.h - unobstructed_bounds.size.h; + int16_t extra_shift = obstruction > 0 ? 10 : 0; + int16_t line_y = 97 - obstruction + extra_shift; + + graphics_context_set_stroke_color(ctx, GColorWhite); + graphics_draw_line(ctx, GPoint(8, line_y), GPoint(131, line_y)); + graphics_draw_line(ctx, GPoint(8, line_y + 1), GPoint(131, line_y + 1)); +} + +static void prv_update_layer_positions(void) { + TicTocData *data = app_state_get_user_data(); + Layer *window_layer = window_get_root_layer(&data->window); + + GRect bounds; + layer_get_bounds(window_layer, &bounds); + GRect unobstructed_bounds; + layer_get_unobstructed_bounds(window_layer, &unobstructed_bounds); + + // Calculate how much we need to shift up (maintains spacing between elements) + int16_t obstruction = bounds.size.h - unobstructed_bounds.size.h; + // Add extra shift when obstructed to reduce bottom padding + int16_t extra_shift = obstruction > 0 ? 10 : 0; + + GRect date_frame; + layer_get_frame(&data->text_date_layer.layer, &date_frame); + date_frame.origin.y = 68 - obstruction + extra_shift; + layer_set_frame(&data->text_date_layer.layer, &date_frame); + + GRect time_frame; + layer_get_frame(&data->text_time_layer.layer, &time_frame); + time_frame.origin.y = 92 - obstruction + extra_shift; + layer_set_frame(&data->text_time_layer.layer, &time_frame); + + layer_mark_dirty(&data->line_layer); +} + +static void prv_unobstructed_area_change_handler(AnimationProgress progress, void *context) { + prv_update_layer_positions(); +} + +static void prv_minute_tick_handler(struct tm *tick_time, TimeUnits units_changed) { + TicTocData *data = app_state_get_user_data(); + + strftime(data->date_text, sizeof(data->date_text), i18n_get("%B %e", data), tick_time); + text_layer_set_text(&data->text_date_layer, data->date_text); + + strftime(data->time_text, sizeof(data->time_text), + clock_is_24h_style() ? "%R" : "%I:%M", tick_time); + + // Handle lack of non-padded hour format string for twelve hour clock. + char *start_time_text = data->time_text; + if (!clock_is_24h_style() && (data->time_text[0] == '0')) { + start_time_text++; + } + + text_layer_set_text(&data->text_time_layer, start_time_text); +} + +static void prv_deinit(void) { + TicTocData *data = app_state_get_user_data(); + app_unobstructed_area_service_unsubscribe(); + tick_timer_service_unsubscribe(); + i18n_free_all(data); + app_free(data); +} + +static void prv_init(void) { + TicTocData *data = app_zalloc_check(sizeof(TicTocData)); + app_state_set_user_data(data); + setlocale(LC_ALL, ""); + + window_init(&data->window, WINDOW_NAME("TicToc")); + window_set_background_color(&data->window, GColorBlack); + + text_layer_init(&data->text_date_layer, &GRect(8, 68, DISP_COLS - 8, DISP_ROWS - 68)); + text_layer_set_text_color(&data->text_date_layer, GColorWhite); + text_layer_set_background_color(&data->text_date_layer, GColorClear); + text_layer_set_font(&data->text_date_layer, fonts_get_system_font(FONT_KEY_ROBOTO_CONDENSED_21)); + layer_add_child(&data->window.layer, &data->text_date_layer.layer); + + text_layer_init(&data->text_time_layer, &GRect(7, 92, DISP_COLS - 7, DISP_ROWS - 92)); + text_layer_set_text_color(&data->text_time_layer, GColorWhite); + text_layer_set_background_color(&data->text_time_layer, GColorClear); + text_layer_set_font(&data->text_time_layer, + fonts_get_system_font(FONT_KEY_ROBOTO_BOLD_SUBSET_49)); + layer_add_child(&data->window.layer, &data->text_time_layer.layer); + + layer_init(&data->line_layer, &data->window.layer.frame); + data->line_layer.update_proc = &prv_line_layer_update_callback; + layer_add_child(&data->window.layer, &data->line_layer); + + tick_timer_service_subscribe(MINUTE_UNIT, prv_minute_tick_handler); + + struct tm time_struct; + rtc_get_time_tm(&time_struct); + prv_minute_tick_handler(&time_struct, MINUTE_UNIT); + + app_window_stack_push(&data->window, true); + + // Subscribe to unobstructed area changes + UnobstructedAreaHandlers unobstructed_handlers = { + .change = prv_unobstructed_area_change_handler + }; + app_unobstructed_area_service_subscribe(unobstructed_handlers, NULL); + + // Set initial positions based on unobstructed area + prv_update_layer_positions(); +} + +void tictoc_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} diff --git a/src/fw/apps/watch/tictoc/default/tictoc_default.c b/src/fw/apps/watch/tictoc/default/tictoc_default.c index 36cfb3f93b..2b7f48fbcb 100644 --- a/src/fw/apps/watch/tictoc/default/tictoc_default.c +++ b/src/fw/apps/watch/tictoc/default/tictoc_default.c @@ -1,31 +1,163 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app.h" -#include "applib/rockyjs/rocky.h" +#include "applib/app_focus_service.h" +#include "applib/tick_timer_service.h" #include "applib/ui/app_window_stack.h" -#include "resource/resource_ids.auto.h" +#include "applib/ui/ui.h" +#include "applib/unobstructed_area_service.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "util/time/time.h" +#include "util/trig.h" -void tictoc_main(void) { - // Push a window so we don't exit - Window *window = window_create(); - app_window_stack_push(window, false/*animated*/); -#if CAPABILITY_HAS_JAVASCRIPT - rocky_event_loop_with_system_resource(RESOURCE_ID_JS_TICTOC); +#if PBL_ROUND +static const int MINUTE_HAND_MARGIN = 16; +static const int HOUR_HAND_MARGIN = 14 * 4; #else - app_event_loop(); +static const int MINUTE_HAND_MARGIN = 10; +static const int HOUR_HAND_MARGIN = 10 * 4; #endif +static const int DOT_Y = 8; +static const int STROKE_WIDTH = 8; + +typedef struct { + int hours; + int minutes; +} Time; + +typedef struct { + Window window; + Layer canvas_layer; + Time last_time; +} TicTocData; + +static void prv_minute_tick_handler(struct tm *tick_time, TimeUnits changed) { + TicTocData *data = app_state_get_user_data(); + + // Store time + data->last_time.hours = tick_time->tm_hour; + data->last_time.hours -= (data->last_time.hours > 12) ? 12 : 0; + data->last_time.minutes = tick_time->tm_min; + + // Redraw + layer_mark_dirty(&data->canvas_layer); +} + +static void prv_did_focus_handler(bool in_focus) { + if (!in_focus) { + return; + } + TicTocData *data = app_state_get_user_data(); + layer_mark_dirty(&data->canvas_layer); +} + +static void prv_canvas_layer_update_proc(Layer *layer, GContext *ctx) { + TicTocData *data = app_state_get_user_data(); + + const GRect *bounds = &layer->bounds; + graphics_context_set_fill_color(ctx, GColorBlack); + graphics_context_set_stroke_width(ctx, STROKE_WIDTH); + graphics_context_set_antialiased(ctx, true); + + graphics_fill_rect(ctx, bounds); + + // Get unobstructed bounds to squish clock when obstructed + GRect unobstructed_bounds; + layer_get_unobstructed_bounds(layer, &unobstructed_bounds); + + GPoint center = grect_center_point(&unobstructed_bounds); + int clock_radius = MIN(unobstructed_bounds.size.h, unobstructed_bounds.size.w) / 2; + + Time mode_time = data->last_time; + + // Calculate hand angles + int minute_angle = TRIG_MAX_ANGLE * mode_time.minutes / 60; + int hour_angle = (TRIG_MAX_ANGLE * (mode_time.hours * 60 + mode_time.minutes)) / (12 * 60); + + // Calculate hand lengths (with margins) + int minute_hand_length = clock_radius - MINUTE_HAND_MARGIN; + int hour_hand_length = clock_radius - HOUR_HAND_MARGIN; + + // Plot hands + GPoint minute_hand = (GPoint) { + .x = (int16_t)(sin_lookup(minute_angle) * (int32_t)minute_hand_length + / TRIG_MAX_RATIO) + center.x, + .y = (int16_t)(-cos_lookup(minute_angle) * (int32_t)minute_hand_length + / TRIG_MAX_RATIO) + center.y + }; + GPoint hour_hand = (GPoint) { + .x = (int16_t)(sin_lookup(hour_angle) * (int32_t)hour_hand_length + / TRIG_MAX_RATIO) + center.x, + .y = (int16_t)(-cos_lookup(hour_angle) * (int32_t)hour_hand_length + / TRIG_MAX_RATIO) + center.y + }; + + // Draw hands with positive length only + if (clock_radius > MINUTE_HAND_MARGIN) { + graphics_context_set_stroke_color(ctx, GColorWhite); + graphics_draw_line(ctx, center, minute_hand); + } + + if (clock_radius > HOUR_HAND_MARGIN) { + graphics_context_set_stroke_color(ctx, PBL_IF_COLOR_ELSE(GColorRed, GColorWhite)); + graphics_draw_line(ctx, center, hour_hand); + // fill a circle to make a cleaner center + graphics_context_set_fill_color(ctx, PBL_IF_COLOR_ELSE(GColorRed, GColorWhite)); + graphics_fill_circle(ctx, center, STROKE_WIDTH / 2); + } + + // Draw 12 o'clock indicator dot + graphics_context_set_fill_color(ctx, GColorWhite); + int center_x = unobstructed_bounds.origin.x + unobstructed_bounds.size.w / 2; + graphics_fill_circle(ctx, GPoint(center_x, DOT_Y), 3); +} + +static void prv_window_load(Window *window) { + TicTocData *data = app_state_get_user_data(); + + Layer *window_layer = window_get_root_layer(window); + const GRect *window_bounds = &window_layer->bounds; + + layer_init(&data->canvas_layer, window_bounds); + layer_set_update_proc(&data->canvas_layer, prv_canvas_layer_update_proc); + layer_add_child(window_layer, &data->canvas_layer); + + AppFocusHandlers focus_handlers = { .did_focus = prv_did_focus_handler }; + app_focus_service_subscribe_handlers(focus_handlers); } + +static void prv_init() { + TicTocData *data = app_zalloc_check(sizeof(TicTocData)); + app_state_set_user_data(data); + + window_init(&data->window, WINDOW_NAME("TicToc")); + window_set_window_handlers(&data->window, &(WindowHandlers) { + .load = prv_window_load + }); + window_set_user_data(&data->window, data); + app_window_stack_push(&data->window, true); + + struct tm time_struct; + rtc_get_time_tm(&time_struct); + prv_minute_tick_handler(&time_struct, MINUTE_UNIT); + + tick_timer_service_subscribe(MINUTE_UNIT, prv_minute_tick_handler); +} + +static void prv_deinit() { + TicTocData *data = app_state_get_user_data(); + + app_focus_service_unsubscribe(); + tick_timer_service_unsubscribe(); + layer_deinit(&data->canvas_layer); + window_deinit(&data->window); + app_free(data); +} + +void tictoc_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} \ No newline at end of file diff --git a/src/fw/apps/watch/tictoc/round/tictoc_round.c b/src/fw/apps/watch/tictoc/round/tictoc_round.c new file mode 100644 index 0000000000..fcc2312b7b --- /dev/null +++ b/src/fw/apps/watch/tictoc/round/tictoc_round.c @@ -0,0 +1,314 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "watch_model.h" + +#include "applib/app.h" +#include "applib/fonts/fonts.h" +#include "applib/graphics/gpath.h" +#include "applib/graphics/graphics_circle.h" +#include "applib/graphics/text.h" +#include "util/trig.h" +#include "applib/ui/app_window_stack.h" +#include "applib/ui/ui.h" +#include "kernel/pbl_malloc.h" +#include "process_state/app_state/app_state.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "util/time/time.h" + +typedef struct { + Window window; + ClockModel clock_model; + GBitmap *bg_bitmap; + GBitmap *tz_bitmap[NUM_NON_LOCAL_CLOCKS]; +} MultiWatchData; + +static GPath* prv_pointed_hand_path(GContext *ctx, ClockHand *hand) { + uint32_t num_points = 5; + if (hand->backwards_extension > 0) num_points = 9; + + GPoint *points = (GPoint*)malloc(num_points * sizeof(GPoint)); + if (!points) return NULL; + + points[0] = GPoint(hand->thickness / -2, hand->thickness); // top left + points[1] = GPoint(hand->thickness / -2, -(hand->length - hand->thickness / 2)); // bottom left + points[2] = GPoint(0, -(hand->length)); // point + points[3] = GPoint(hand->thickness / 2, -(hand->length - hand->thickness / 2)); // bottom right + points[4] = GPoint(hand->thickness / 2, hand->thickness); // top right + if (hand->backwards_extension > 0) { + points[5] = GPoint(hand->thickness / 4, hand->thickness); // bottom right + points[6] = GPoint(hand->thickness / 4, hand->thickness + hand->backwards_extension); // top right + points[7] = GPoint(hand->thickness / -4, hand->thickness + hand->backwards_extension); // top left + points[8] = GPoint(hand->thickness / -4, hand->thickness); // bottom left + } + + GPathInfo info = { + .num_points = num_points, + .points = points, + }; + + GPath *path = gpath_create(&info); + return path; +} + +static GPath* prv_square_hand_path(GContext *ctx, ClockHand *hand) { + uint32_t num_points = 4; + if (hand->backwards_extension > 0) num_points = 8; + + GPoint *points = (GPoint*)malloc(num_points * sizeof(GPoint)); + if (!points) return NULL; + + points[0] = GPoint(hand->thickness / -2, hand->thickness); // top left + points[1] = GPoint(hand->thickness / -2, -(hand->length)); // bottom left + points[2] = GPoint(hand->thickness / 2, -(hand->length)); // bottom right + points[3] = GPoint(hand->thickness / 2, hand->thickness); // top right + if (hand->backwards_extension > 0) { + points[4] = GPoint(hand->thickness / 4, hand->thickness); // bottom right + points[5] = GPoint(hand->thickness / 4, hand->thickness + hand->backwards_extension); // top right + points[6] = GPoint(hand->thickness / -4, hand->thickness + hand->backwards_extension); // top left + points[7] = GPoint(hand->thickness / -4, hand->thickness); // bottom left + } + + GPathInfo info = { + .num_points = num_points, + .points = points, + }; + + GPath *path = gpath_create(&info); + return path; +} + +void watch_model_handle_change(ClockModel *model) { + MultiWatchData *data = app_state_get_user_data(); + data->clock_model = *model; + layer_mark_dirty(window_get_root_layer(&data->window)); +} + +static GPointPrecise prv_gpoint_from_polar(const GPointPrecise *center, uint32_t distance, + int32_t angle) { + return gpoint_from_polar_precise(center, distance << GPOINT_PRECISE_PRECISION, angle); +} + +static void prv_graphics_draw_centered_text(GContext *ctx, const GSize *max_size, + const GPoint *center, const GFont font, + const GColor color, const char *text) { + GSize text_size = app_graphics_text_layout_get_content_size( + text, font, (GRect) { .size = *max_size }, GTextOverflowModeFill, GTextAlignmentCenter); + GPoint text_center = *center; + text_center.x -= text_size.w / 2 + 1; + text_center.y -= text_size.h * 2 / 3; + graphics_context_set_text_color(ctx, color); + graphics_draw_text(ctx, text, font, (GRect) { .origin = text_center, .size = text_size }, + GTextOverflowModeFill, GTextAlignmentCenter, NULL); +} + +static void prv_draw_watch_hand_rounded(GContext *ctx, ClockHand *hand, GPointPrecise center) { + GPointPrecise watch_hand_end = prv_gpoint_from_polar(¢er, hand->length, hand->angle); + if (hand->style == CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT) { + graphics_context_set_stroke_color(ctx, GColorWhite); + graphics_line_draw_precise_stroked_aa(ctx, center, watch_hand_end, hand->thickness + 2); + } + graphics_context_set_stroke_color(ctx, hand->color); + graphics_line_draw_precise_stroked_aa(ctx, center, watch_hand_end, hand->thickness); +} + +static void prv_draw_watch_hand_pointed(GContext *ctx, ClockHand *hand, GPoint center) { + GPath *path = prv_pointed_hand_path(ctx, hand); + graphics_context_set_fill_color(ctx, hand->color); + gpath_rotate_to(path, hand->angle); + gpath_move_to(path, center); + gpath_draw_filled(ctx, path); + gpath_destroy(path); +} + +static void prv_draw_watch_hand_square(GContext *ctx, ClockHand *hand, GPoint center) { + GPath *path = prv_square_hand_path(ctx, hand); + graphics_context_set_fill_color(ctx, hand->color); + gpath_rotate_to(path, hand->angle); + gpath_move_to(path, center); + gpath_draw_filled(ctx, path); + gpath_destroy(path); +} + +static void prv_draw_watch_hand(GContext *ctx, ClockHand *hand, GPointPrecise center) { + switch (hand->style) { + case CLOCK_HAND_STYLE_POINTED: + prv_draw_watch_hand_pointed(ctx, hand, GPointFromGPointPrecise(center)); + break; + case CLOCK_HAND_STYLE_SQUARE: + prv_draw_watch_hand_square(ctx, hand, GPointFromGPointPrecise(center)); + break; + case CLOCK_HAND_STYLE_ROUNDED: + case CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT: + default: + prv_draw_watch_hand_rounded(ctx, hand, center); + break; + } +} + +static GPointPrecise prv_get_clock_center_point(ClockLocation location, const GRect *bounds) { + GPoint imprecise_center_point = {0}; + switch (location) { + case CLOCK_LOCATION_TOP: + imprecise_center_point = (GPoint) { + .x = bounds->size.w / 2, + .y = bounds->size.h / 4, + }; + break; + case CLOCK_LOCATION_RIGHT: + imprecise_center_point = (GPoint) { + .x = bounds->size.w * 3 / 4 - 5, + .y = bounds->size.h / 2, + }; + break; + case CLOCK_LOCATION_BOTTOM: + imprecise_center_point = (GPoint) { + .x = bounds->size.w / 2, + .y = bounds->size.h * 3 / 4 + 6, + }; + break; + case CLOCK_LOCATION_LEFT: + imprecise_center_point = (GPoint) { + .x = bounds->size.w / 4 + 4, + .y = bounds->size.h / 2, + }; + break; + default: + // aiming for width / 2 - 0.5 to get the true center + return (GPointPrecise) { + .x = { .integer = bounds->size.w / 2 - 1, .fraction = 3 }, + .y = { .integer = bounds->size.h / 2 - 1, .fraction = 3 } + }; + } + return GPointPreciseFromGPoint(imprecise_center_point); +} + +static void prv_draw_clock_text(GContext *ctx, ClockText text, GPoint center) { + MultiWatchData *data = app_state_get_user_data(); + const GRect *bounds = &window_get_root_layer(&data->window)->bounds; + switch(text.location) { + case CLOCK_TEXT_LOCATION_LEFT: + const GRect box = (GRect) { .origin = GPoint(center.x - text.offset - bounds->size.w, center.y - text.font_size * 2 / 3), .size = bounds->size }; + graphics_draw_text(ctx, text.buffer, text.font, box, + GTextOverflowModeFill, GTextAlignmentRight, NULL); + break; + case CLOCK_TEXT_LOCATION_BOTTOM: + default: + const GPoint point = (GPoint) { center.x, center.y + text.offset }; + prv_graphics_draw_centered_text(ctx, &bounds->size, &point, text.font, + text.color, text.buffer); + break; + } +} + +static void prv_draw_clock_face(GContext *ctx, ClockFace *face) { + MultiWatchData *data = app_state_get_user_data(); + const GRect *bounds = &window_get_root_layer(&data->window)->bounds; + const GPointPrecise center = prv_get_clock_center_point(face->location, bounds); + + // Draw text. + prv_draw_clock_text(ctx, face->text, GPointFromGPointPrecise(center)); + + // Draw hands. + prv_draw_watch_hand(ctx, &face->hour_hand, center); + prv_draw_watch_hand(ctx, &face->minute_hand, center); + + // Draw bob. + GRect bob_rect = (GRect) { + .origin = GPoint(GPointFromGPointPrecise(center).x - face->bob_radius, GPointFromGPointPrecise(center).y - face->bob_radius), + .size = GSize(face->bob_radius * 2, face->bob_radius * 2) + }; + GRect bob_center_rect = (GRect) { + .origin = GPoint(GPointFromGPointPrecise(center).x - face->bob_center_radius, GPointFromGPointPrecise(center).y - face->bob_center_radius), + .size = GSize(face->bob_center_radius * 2, face->bob_center_radius * 2) + }; + graphics_context_set_fill_color(ctx, face->bob_color); + graphics_fill_oval(ctx, bob_rect, GOvalScaleModeFitCircle); + graphics_context_set_fill_color(ctx, face->bob_center_color); + graphics_fill_oval(ctx, bob_center_rect, GOvalScaleModeFitCircle); +} + + +static void prv_update_proc(Layer *layer, GContext *ctx) { + MultiWatchData *data = app_state_get_user_data(); + + const GRect *bounds = &layer->bounds; + + // Background. + graphics_draw_bitmap_in_rect(ctx, data->bg_bitmap, bounds); + graphics_context_set_compositing_mode(ctx, GCompOpSet); + + ClockModel *clock_model = &data->clock_model; + + // Draw the clocks. + for (uint32_t i = 0; i < clock_model->num_non_local_clocks; ++i) { + // Draw clock background + GRect bitmap_bounds = gbitmap_get_bounds(data->tz_bitmap[i]); + GPointPrecise bitmap_center = prv_get_clock_center_point(clock_model->non_local_clock[i].location, bounds); + bitmap_bounds.origin.x = GPointFromGPointPrecise(bitmap_center).x - (bitmap_bounds.size.w / 2); + bitmap_bounds.origin.y = GPointFromGPointPrecise(bitmap_center).y - (bitmap_bounds.size.h / 2); + graphics_draw_bitmap_in_rect(ctx, data->tz_bitmap[i], &bitmap_bounds); + // Draw clock foreground + prv_draw_clock_face(ctx, &clock_model->non_local_clock[i]); + } + prv_draw_clock_face(ctx, &clock_model->local_clock); +} + +static void prv_window_load(Window *window) { + MultiWatchData *data = app_state_get_user_data(); + + layer_set_update_proc(window_get_root_layer(window), prv_update_proc); + + watch_model_init(); + + data->bg_bitmap = gbitmap_create_with_resource(data->clock_model.bg_bitmap_id); + for (uint32_t i = 0; i < data->clock_model.num_non_local_clocks; ++i) { + data->tz_bitmap[i] = gbitmap_create_with_resource(data->clock_model.non_local_clock[i].bg_bitmap_id); + } +} + +static void prv_window_unload(Window *window) { + MultiWatchData *data = app_state_get_user_data(); + gbitmap_destroy(data->bg_bitmap); + for (uint32_t i = 0; i < data->clock_model.num_non_local_clocks; ++i) { + gbitmap_destroy(data->tz_bitmap[i]); + } +} + +static void prv_app_did_focus(bool did_focus) { + if (!did_focus) { + return; + } + app_focus_service_unsubscribe(); + watch_model_start_intro(); +} + +static void prv_init(void) { + MultiWatchData *data = app_zalloc_check(sizeof(*data)); + app_state_set_user_data(data); + + window_init(&data->window, "TicToc"); + window_set_window_handlers(&data->window, &(WindowHandlers) { + .load = prv_window_load, + .unload = prv_window_unload, + }); + const bool animated = true; + app_window_stack_push(&data->window, animated); + + app_focus_service_subscribe_handlers((AppFocusHandlers) { + .did_focus = prv_app_did_focus, + }); +} + +static void prv_deinit(void) { + MultiWatchData *data = app_state_get_user_data(); + window_destroy(&data->window); + watch_model_cleanup(); +} + +void tictoc_main(void) { + prv_init(); + app_event_loop(); + prv_deinit(); +} diff --git a/src/fw/apps/watch/tictoc/round/watch_model.c b/src/fw/apps/watch/tictoc/round/watch_model.c new file mode 100644 index 0000000000..6851e34533 --- /dev/null +++ b/src/fw/apps/watch/tictoc/round/watch_model.c @@ -0,0 +1,321 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "watch_model.h" + +#include "util/trig.h" +#include "applib/app_watch_info.h" +#include "applib/pbl_std/pbl_std.h" +#include "applib/tick_timer_service.h" +#include "applib/platform.h" +#include "resource/resource_ids.auto.h" +#include "syscall/syscall.h" +#include "util/time/time.h" + +#include + +// TODO: Add seconds as an option +static void prv_calculate_hand_angles(struct tm *tick_time, int32_t *hour_angle, + int32_t *minute_angle) { + *hour_angle = (tick_time->tm_hour % 12) * TRIG_MAX_ANGLE / 12 + + tick_time->tm_min * TRIG_MAX_ANGLE / 60 / 12; + *minute_angle = tick_time->tm_min * TRIG_MAX_ANGLE / 60; +} + +static ClockFace prv_local_clock_face_default(struct tm *tick_time) { + int32_t hour_angle, minute_angle; + prv_calculate_hand_angles(tick_time, &hour_angle, &minute_angle); + + // TODO: Don't return by value. This thing is massive. + return (ClockFace) { + .hour_hand = { + .angle = hour_angle, + .backwards_extension = LOCAL_HOUR_HAND_BACK_EXT_DEFAULT, + .color = LOCAL_HOUR_HAND_COLOR_DEFAULT, + .length = LOCAL_HOUR_HAND_LENGTH_DEFAULT, + .style = CLOCK_HAND_STYLE_ROUNDED, + .thickness = LOCAL_HOUR_HAND_THICKNESS_DEFAULT, + }, + .minute_hand = { + .angle = minute_angle, + .backwards_extension = LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT, + .color = LOCAL_MINUTE_HAND_COLOR_DEFAULT, + .length = LOCAL_MINUTE_HAND_LENGTH_DEFAULT, + .style = CLOCK_HAND_STYLE_ROUNDED, + .thickness = LOCAL_MINUTE_HAND_THICKNESS_DEFAULT, + }, + .bob_radius = LOCAL_BOB_RADIUS_DEFAULT, + .bob_color = LOCAL_BOB_COLOR_DEFAULT, + .location = CLOCK_LOCATION_CENTER, + }; +} + +static ClockFace prv_configure_non_local_clock_face(int32_t utc_offset, const char *text, + GColor text_color, GColor hand_color, + uint32_t bg_bitmap_id, + ClockLocation location) { + time_t t = rtc_get_time(); + struct tm* tick_time = pbl_override_gmtime(&t); + // TODO: Make this work with non integer hour offsets + tick_time->tm_hour += utc_offset; // TODO check if this works properly + int32_t hour_angle, minute_angle; + prv_calculate_hand_angles(tick_time, &hour_angle, &minute_angle); + + // TODO: Don't return by value. This thing is massive. + ClockFace non_local_clock = (ClockFace) { + .hour_hand = { + .length = NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT, + .thickness = NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT, + .backwards_extension = 0, + .angle = hour_angle, + .color = hand_color, + .style = CLOCK_HAND_STYLE_ROUNDED, + }, + .minute_hand = { + .length = NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT, + .thickness = NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT, + .backwards_extension = 0, + .angle = minute_angle, + .color = hand_color, + .style = CLOCK_HAND_STYLE_ROUNDED, + }, + .location = location, + .text = { + .type = CLOCK_TEXT_TYPE_BUFFER, + .location = CLOCK_TEXT_LOCATION_BOTTOM, + .color = text_color, + .offset = NON_LOCAL_TEXT_OFFSET, + .font = fonts_get_system_font(NON_LOCAL_TEXT_FONT), + .font_size = NON_LOCAL_TEXT_FONT_SIZE, + }, + .bg_bitmap_id = bg_bitmap_id, + }; + strncpy(non_local_clock.text.buffer, text, sizeof(non_local_clock.text.buffer)); + return non_local_clock; +} + +// Configure the text displayed on the clock. +static ClockText prv_configure_clock_text(ClockTextType type, ClockTextLocation location, + GColor color, struct tm *tick_time) { + ClockText text = (ClockText) { + .location = location, + .color = color, + .offset = LOCAL_TEXT_OFFSET, + .font = fonts_get_system_font(LOCAL_TEXT_FONT), + .font_size = LOCAL_TEXT_FONT_SIZE, + }; + + switch(type) { + case CLOCK_TEXT_TYPE_TIME: + // TODO: Return system configured format + strftime(text.buffer, sizeof(text.buffer), "$l:%M%P", tick_time); + break; + case CLOCK_TEXT_TYPE_DATE: + default: + // TODO: Return localized format + strftime(text.buffer, sizeof(text.buffer), "%a %d", tick_time); + break; + } + + for (uint32_t i = 0; i < sizeof(text.buffer); i++) { + text.buffer[i] = toupper((unsigned char)text.buffer[i]); + } + + // TODO: Don't return a struct + return text; +} + +static ClockModel prv_clock_model_default(struct tm *tick_time) { + // Create a generic model and configure a default clock. + ClockModel model; + model.local_clock = prv_local_clock_face_default(tick_time); + + // Add watch-specific details. + const WatchInfoColor watch_color = sys_watch_info_get_color(); + + switch (watch_color) { +#ifdef CONFIG_BOARD_FAMILY_GETAFIX + case WATCH_INFO_COLOR_COREDEVICES_PR2_BLACK_20: + model.num_non_local_clocks = 2; + model.local_clock.hour_hand.thickness = 14; + model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_SQUARE; + model.local_clock.hour_hand.length = 80; + model.local_clock.minute_hand.thickness = 10; + model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_SQUARE; + model.local_clock.minute_hand.length = 105; + model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorDarkGray, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_20MM_BLACK, + CLOCK_LOCATION_LEFT); + model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorDarkGray, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_20MM_BLACK, + CLOCK_LOCATION_RIGHT); + model.local_clock.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM, + GColorWhite, tick_time); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_BLACK; + break; + case WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_14: + model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.hour_hand.color = GColorBlack; + model.local_clock.hour_hand.thickness = 14; + model.local_clock.hour_hand.length = 81; + model.local_clock.hour_hand.backwards_extension = 8; + model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.minute_hand.color = GColorCadetBlue; + model.local_clock.minute_hand.thickness = 14; + model.local_clock.minute_hand.length = 110; + model.num_non_local_clocks = 2; + model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorDarkGray, + GColorBlack, + RESOURCE_ID_MULTIWATCH_TIMEZONE_14MM_SILVER, + CLOCK_LOCATION_LEFT); + model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorDarkGray, + GColorBlack, + RESOURCE_ID_MULTIWATCH_TIMEZONE_14MM_SILVER, + CLOCK_LOCATION_RIGHT); + model.local_clock.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM, + GColorDarkGray, tick_time); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_SILVER; + break; + case WATCH_INFO_COLOR_COREDEVICES_PR2_SILVER_20: + model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.hour_hand.thickness = 14; + model.local_clock.hour_hand.length = 85; + model.local_clock.hour_hand.backwards_extension = 8; + model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.minute_hand.color = GColorRed; + model.local_clock.minute_hand.thickness = 14; + model.local_clock.minute_hand.length = 105; + model.local_clock.bob_color = GColorBlack; + model.num_non_local_clocks = 2; + model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorWhite, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_20MM_SILVER, + CLOCK_LOCATION_LEFT); + model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorWhite, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_20MM_SILVER, + CLOCK_LOCATION_RIGHT); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_SILVER; + break; + case WATCH_INFO_COLOR_COREDEVICES_PR2_GOLD_14: + default: + model.local_clock.bob_center_color = GColorOrange; + model.local_clock.minute_hand.color = GColorWhite; + model.local_clock.minute_hand.thickness = 3; + model.local_clock.minute_hand.length = 85; + model.local_clock.hour_hand.color = GColorBlack; + model.local_clock.hour_hand.thickness = 12; + model.local_clock.hour_hand.length = 60; + model.local_clock.bob_radius = 10; + model.local_clock.bob_center_radius = 4; + model.local_clock.bob_color = GColorWhite; + model.num_non_local_clocks = 2; + model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorBlack, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_14MM_ROSE_GOLD, + CLOCK_LOCATION_LEFT); + model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorBlack, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_14MM_ROSE_GOLD, + CLOCK_LOCATION_RIGHT); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_ROSE_GOLD; + break; +#else + case WATCH_INFO_COLOR_TIME_ROUND_BLACK_14: + model.local_clock.minute_hand.color = GColorBlue; + model.local_clock.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_LEFT, + GColorWhite, tick_time); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_BLACK_RED; + break; + case WATCH_INFO_COLOR_TIME_ROUND_BLACK_20: + model.num_non_local_clocks = 2; + model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorDarkGray, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_20MM_BLACK, + CLOCK_LOCATION_LEFT); + model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorDarkGray, + GColorWhite, + RESOURCE_ID_MULTIWATCH_TIMEZONE_20MM_BLACK, + CLOCK_LOCATION_RIGHT); + model.local_clock.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM, + GColorWhite, tick_time); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_BLACK; + break; + case WATCH_INFO_COLOR_TIME_ROUND_SILVER_14: + model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.hour_hand.color = GColorBlack; + model.local_clock.hour_hand.thickness = 10; + model.local_clock.hour_hand.length = 56; + model.local_clock.hour_hand.backwards_extension = 5; + model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.minute_hand.color = GColorCadetBlue; + model.local_clock.minute_hand.thickness = 10; + model.local_clock.minute_hand.length = 66; + model.local_clock.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM, + GColorDarkGray, tick_time); + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_SILVER; + break; + case WATCH_INFO_COLOR_TIME_ROUND_SILVER_20: + model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.hour_hand.thickness = 10; + model.local_clock.hour_hand.length = 56; + model.local_clock.hour_hand.backwards_extension = 5; + model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED; + model.local_clock.minute_hand.color = GColorRed; + model.local_clock.minute_hand.thickness = 10; + model.local_clock.minute_hand.length = 66; + model.local_clock.bob_color = GColorBlack; + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_SILVER_BROWN; + break; + case WATCH_INFO_COLOR_TIME_ROUND_ROSE_GOLD_14: + default: + model.local_clock.bob_center_color = GColorOrange; + model.local_clock.minute_hand.color = GColorWhite; + model.local_clock.minute_hand.thickness = 2; + model.local_clock.minute_hand.length = 54; + model.local_clock.hour_hand.color = GColorBlack; + model.local_clock.hour_hand.thickness = 8; + model.local_clock.hour_hand.length = 39; + model.local_clock.bob_radius = 7; + model.local_clock.bob_center_radius = 3; + model.local_clock.bob_color = GColorWhite; + model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_ROSE_GOLD; + break; +#endif + } + + // disable timezones until they can be configured by the user + model.num_non_local_clocks = 0; + + // TODO: Don't return a struct here + return model; +} + +static void prv_handle_time_update(struct tm *tick_time, TimeUnits units_changed) { + ClockModel model = prv_clock_model_default(tick_time); + watch_model_handle_change(&model); +} + +void watch_model_cleanup() { + tick_timer_service_unsubscribe(); +} + +static void prv_intro_animation_finished(Animation *animation) { + const time_t t = rtc_get_time(); + prv_handle_time_update(pbl_override_localtime(&t), (TimeUnits)0xff); + + tick_timer_service_subscribe(MINUTE_UNIT, prv_handle_time_update); +} + +void watch_model_start_intro() { + prv_intro_animation_finished(NULL); +} + +void watch_model_init(void) { + const time_t t = rtc_get_time(); + struct tm *tick_time = pbl_override_localtime(&t); + ClockModel model = prv_clock_model_default(tick_time); + watch_model_handle_change(&model); +} diff --git a/src/fw/apps/watch/tictoc/round/watch_model.h b/src/fw/apps/watch/tictoc/round/watch_model.h new file mode 100644 index 0000000000..53edeaeb47 --- /dev/null +++ b/src/fw/apps/watch/tictoc/round/watch_model.h @@ -0,0 +1,151 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "applib/graphics/gtypes.h" +#include "applib/fonts/fonts.h" + +#include + +#ifdef CONFIG_BOARD_FAMILY_GETAFIX + +#define LOCAL_HOUR_HAND_LENGTH_DEFAULT 74 +#define LOCAL_HOUR_HAND_THICKNESS_DEFAULT 9 +#define LOCAL_HOUR_HAND_COLOR_DEFAULT GColorWhite +#define LOCAL_HOUR_HAND_BACK_EXT_DEFAULT 0 + +#define LOCAL_MINUTE_HAND_LENGTH_DEFAULT 84 +#define LOCAL_MINUTE_HAND_THICKNESS_DEFAULT 9 +#define LOCAL_MINUTE_HAND_COLOR_DEFAULT GColorWhite +#define LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT 0 + +#define LOCAL_BOB_RADIUS_DEFAULT 9 +#define LOCAL_BOB_COLOR_DEFAULT GColorRed + +#define LOCAL_TEXT_OFFSET 60 +#define LOCAL_TEXT_COLOR GColorWhite +#define LOCAL_TEXT_FONT FONT_KEY_GOTHIC_24_BOLD +#define LOCAL_TEXT_FONT_SIZE 24 + +#define NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT 16 +#define NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT 4 + +#define NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT 30 +#define NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT 4 + +#define NON_LOCAL_TEXT_OFFSET 25 +#define NON_LOCAL_TEXT_COLOR GColorWhite +#define NON_LOCAL_TEXT_FONT FONT_KEY_GOTHIC_18_BOLD +#define NON_LOCAL_TEXT_FONT_SIZE 18 + +#else + +#define LOCAL_HOUR_HAND_LENGTH_DEFAULT 51 +#define LOCAL_HOUR_HAND_THICKNESS_DEFAULT 6 +#define LOCAL_HOUR_HAND_COLOR_DEFAULT GColorWhite +#define LOCAL_HOUR_HAND_BACK_EXT_DEFAULT 0 + +#define LOCAL_MINUTE_HAND_LENGTH_DEFAULT 58 +#define LOCAL_MINUTE_HAND_THICKNESS_DEFAULT 6 +#define LOCAL_MINUTE_HAND_COLOR_DEFAULT GColorWhite +#define LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT 0 + +#define LOCAL_BOB_RADIUS_DEFAULT 6 +#define LOCAL_BOB_COLOR_DEFAULT GColorRed + +#define LOCAL_TEXT_OFFSET 50 +#define LOCAL_TEXT_COLOR GColorWhite +#define LOCAL_TEXT_FONT FONT_KEY_GOTHIC_18_BOLD +#define LOCAL_TEXT_FONT_SIZE 18 + +#define NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT 11 +#define NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT 3 + +#define NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT 21 +#define NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT 3 + +#define NON_LOCAL_TEXT_OFFSET 15 +#define NON_LOCAL_TEXT_COLOR GColorWhite +#define NON_LOCAL_TEXT_FONT FONT_KEY_GOTHIC_18_BOLD +#define NON_LOCAL_TEXT_FONT_SIZE 18 + +#endif + +#define NUM_NON_LOCAL_CLOCKS 3 + +#define GLANCE_TIME_OUT_MS 8000 + +typedef enum { + CLOCK_TEXT_TYPE_NONE = 0, + CLOCK_TEXT_TYPE_TIME, + CLOCK_TEXT_TYPE_DATE, + CLOCK_TEXT_TYPE_BUFFER, +} ClockTextType; + +typedef enum { + CLOCK_TEXT_LOCATION_NONE = 0, + CLOCK_TEXT_LOCATION_BOTTOM, + CLOCK_TEXT_LOCATION_LEFT, +} ClockTextLocation; + +typedef enum { + CLOCK_HAND_STYLE_ROUNDED = 0, + CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT, + CLOCK_HAND_STYLE_POINTED, + CLOCK_HAND_STYLE_SQUARE, +} ClockHandStyle; + +typedef enum { + CLOCK_LOCATION_CENTER, + CLOCK_LOCATION_LEFT, + CLOCK_LOCATION_BOTTOM, + CLOCK_LOCATION_RIGHT, + CLOCK_LOCATION_TOP, +} ClockLocation; + +typedef struct { + uint16_t length; + uint16_t thickness; + uint16_t backwards_extension; + int32_t angle; + GColor color; + ClockHandStyle style; +} ClockHand; + +typedef struct { + ClockTextType type; + ClockTextLocation location; + char buffer[10]; // FIXME magic number + GColor color; + uint16_t offset; + GFont font; + uint16_t font_size; +} ClockText; + +typedef struct { + ClockHand hour_hand; + ClockHand minute_hand; + uint16_t bob_radius; + uint16_t bob_center_radius; + GColor bob_color; + GColor bob_center_color; + ClockLocation location; + ClockText text; + uint32_t bg_bitmap_id; +} ClockFace; + +typedef struct { + ClockFace local_clock; + uint32_t num_non_local_clocks; + ClockFace non_local_clock[NUM_NON_LOCAL_CLOCKS]; + uint32_t bg_bitmap_id; +} ClockModel; + +void watch_model_init(void); + +void watch_model_handle_change(ClockModel *model); + +void watch_model_start_intro(void); + +void watch_model_cleanup(void); diff --git a/src/fw/apps/watch/tictoc/spalding/tictoc_spalding.c b/src/fw/apps/watch/tictoc/spalding/tictoc_spalding.c deleted file mode 100644 index dd552c6a30..0000000000 --- a/src/fw/apps/watch/tictoc/spalding/tictoc_spalding.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "watch_model.h" - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/graphics/gpath.h" -#include "applib/graphics/graphics_circle.h" -#include "applib/graphics/text.h" -#include "util/trig.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/ui.h" -#include "kernel/pbl_malloc.h" -#include "process_state/app_state/app_state.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "util/time/time.h" - -typedef struct { - Window window; - GFont text_font; - ClockModel clock_model; - GBitmap *bg_bitmap; - GPath *hour_path; - GPath *minute_path; -} MultiWatchData; - -static const GPathInfo HOUR_PATH_INFO = { - .num_points = 9, - .points = (GPoint[]) { - {-5, 10}, {-2, 10}, {-2, 15}, {2, 15}, {2, 10}, {5, 10}, {5, -51}, {0, -56}, {-5, -51}, - }, -}; - -static const GPathInfo MINUTE_PATH_INFO = { - .num_points = 5, - .points = (GPoint[]) { - {-5, 10}, {5, 10}, {5, -61}, {0, -66}, {-5, -61}, - }, -}; - -void watch_model_handle_change(ClockModel *model) { - MultiWatchData *data = app_state_get_user_data(); - data->clock_model = *model; - layer_mark_dirty(window_get_root_layer(&data->window)); -} - -static GPointPrecise prv_gpoint_from_polar(const GPointPrecise *center, uint32_t distance, - int32_t angle) { - return gpoint_from_polar_precise(center, distance << GPOINT_PRECISE_PRECISION, angle); -} - -static void prv_graphics_draw_centered_text(GContext *ctx, const GSize *max_size, - const GPoint *center, const GFont font, - const GColor color, const char *text) { - GSize text_size = app_graphics_text_layout_get_content_size( - text, font, (GRect) { .size = *max_size }, GTextOverflowModeFill, GTextAlignmentCenter); - GPoint text_center = *center; - text_center.x -= text_size.w / 2 + 1; - text_center.y -= text_size.h * 2 / 3; - graphics_context_set_text_color(ctx, color); - graphics_draw_text(ctx, text, font, (GRect) { .origin = text_center, .size = text_size }, - GTextOverflowModeFill, GTextAlignmentCenter, NULL); -} - -static void prv_draw_watch_hand_rounded(GContext *ctx, ClockHand *hand, GPointPrecise center) { - GPointPrecise watch_hand_end = prv_gpoint_from_polar(¢er, hand->length, hand->angle); - if (hand->style == CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT) { - graphics_context_set_stroke_color(ctx, GColorWhite); - graphics_line_draw_precise_stroked_aa(ctx, center, watch_hand_end, hand->thickness + 2); - } - graphics_context_set_stroke_color(ctx, hand->color); - graphics_line_draw_precise_stroked_aa(ctx, center, watch_hand_end, hand->thickness); -} - -static void prv_draw_watch_hand_pointed(GContext *ctx, ClockHand *hand, GPoint center, - GPath *path) { - graphics_context_set_fill_color(ctx, hand->color); - gpath_rotate_to(path, hand->angle); - gpath_move_to(path, center); - gpath_draw_filled(ctx, path); -} - -static void prv_draw_watch_hand(GContext *ctx, ClockHand *hand, GPointPrecise center, GPath *path) { - switch (hand->style) { - case CLOCK_HAND_STYLE_POINTED: - prv_draw_watch_hand_pointed(ctx, hand, GPointFromGPointPrecise(center), path); - break; - case CLOCK_HAND_STYLE_ROUNDED: - case CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT: - default: - prv_draw_watch_hand_rounded(ctx, hand, center); - break; - } -} - -static GPointPrecise prv_get_clock_center_point(ClockLocation location, const GRect *bounds) { - GPoint imprecise_center_point = {0}; - switch (location) { - case CLOCK_LOCATION_TOP: - imprecise_center_point = (GPoint) { - .x = bounds->size.w / 2, - .y = bounds->size.h / 4, - }; - break; - case CLOCK_LOCATION_RIGHT: - imprecise_center_point = (GPoint) { - .x = bounds->size.w * 3 / 4 - 5, - .y = bounds->size.h / 2, - }; - break; - case CLOCK_LOCATION_BOTTOM: - imprecise_center_point = (GPoint) { - .x = bounds->size.w / 2, - .y = bounds->size.h * 3 / 4 + 6, - }; - break; - case CLOCK_LOCATION_LEFT: - imprecise_center_point = (GPoint) { - .x = bounds->size.w / 4 + 4, - .y = bounds->size.h / 2, - }; - break; - default: - // aiming for width / 2 - 0.5 to get the true center - return (GPointPrecise) { - .x = { .integer = bounds->size.w / 2 - 1, .fraction = 3 }, - .y = { .integer = bounds->size.h / 2 - 1, .fraction = 3 } - }; - } - return GPointPreciseFromGPoint(imprecise_center_point); -} - -static void prv_draw_clock_face(GContext *ctx, ClockFace *face) { - MultiWatchData *data = app_state_get_user_data(); - const GRect *bounds = &window_get_root_layer(&data->window)->bounds; - const GPointPrecise center = prv_get_clock_center_point(face->location, bounds); - - // Draw hands. - // TODO: Need to do something about the static GPaths used for watchands. This is very inflexible. - prv_draw_watch_hand(ctx, &face->hour_hand, center, data->hour_path); - prv_draw_watch_hand(ctx, &face->minute_hand, center, data->minute_path); - - // Draw bob. - GRect bob_rect = (GRect) { - .size = GSize(face->bob_radius * 2, face->bob_radius * 2) - }; - GRect bob_center_rect = (GRect) { - .size = GSize(face->bob_center_radius * 2, face->bob_center_radius * 2) - }; - grect_align(&bob_rect, bounds, GAlignCenter, false /* clips */); - grect_align(&bob_center_rect, bounds, GAlignCenter, false /* clips */); - graphics_context_set_fill_color(ctx, face->bob_color); - graphics_fill_oval(ctx, bob_rect, GOvalScaleModeFitCircle); - graphics_context_set_fill_color(ctx, face->bob_center_color); - graphics_fill_oval(ctx, bob_center_rect, GOvalScaleModeFitCircle); -} - -static void prv_draw_non_local_clock(GContext *ctx, NonLocalClockFace *clock) { - // TODO: The non-local clock text is currently baked into the background image. - prv_draw_clock_face(ctx, &clock->face); -} - - -static void prv_update_proc(Layer *layer, GContext *ctx) { - MultiWatchData *data = app_state_get_user_data(); - - const GRect *bounds = &layer->bounds; - - // Background. - graphics_draw_bitmap_in_rect(ctx, data->bg_bitmap, bounds); - - ClockModel *clock_model = &data->clock_model; - - // Watch text. TODO: Locate the text properly, rather than hard-coding. - if (clock_model->text.location == CLOCK_TEXT_LOCATION_BOTTOM) { - const GPoint point = (GPoint) { 90, 140 }; - prv_graphics_draw_centered_text(ctx, &bounds->size, &point, data->text_font, - clock_model->text.color, clock_model->text.buffer); - } else if (clock_model->text.location == CLOCK_TEXT_LOCATION_LEFT) { - const GRect box = (GRect) { .origin = GPoint(25, 78), .size = bounds->size }; - graphics_draw_text(ctx, clock_model->text.buffer, data->text_font, box, - GTextOverflowModeFill, GTextAlignmentLeft, NULL); - } - - // Draw the clocks. - for (uint32_t i = 0; i < clock_model->num_non_local_clocks; ++i) { - prv_draw_non_local_clock(ctx, &clock_model->non_local_clock[i]); - } - prv_draw_clock_face(ctx, &clock_model->local_clock); -} - -static void prv_window_load(Window *window) { - MultiWatchData *data = app_state_get_user_data(); - - layer_set_update_proc(window_get_root_layer(window), prv_update_proc); - - watch_model_init(); - - data->hour_path = gpath_create(&HOUR_PATH_INFO); - data->minute_path = gpath_create(&MINUTE_PATH_INFO); - data->bg_bitmap = gbitmap_create_with_resource(data->clock_model.bg_bitmap_id); -} - -static void prv_window_unload(Window *window) { - MultiWatchData *data = app_state_get_user_data(); - gpath_destroy(data->hour_path); - gpath_destroy(data->minute_path); - gbitmap_destroy(data->bg_bitmap); -} - -static void prv_app_did_focus(bool did_focus) { - if (!did_focus) { - return; - } - app_focus_service_unsubscribe(); - watch_model_start_intro(); -} - -static void prv_init(void) { - MultiWatchData *data = app_zalloc_check(sizeof(*data)); - app_state_set_user_data(data); - - data->text_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); - - window_init(&data->window, "TicToc"); - window_set_window_handlers(&data->window, &(WindowHandlers) { - .load = prv_window_load, - .unload = prv_window_unload, - }); - const bool animated = true; - app_window_stack_push(&data->window, animated); - - app_focus_service_subscribe_handlers((AppFocusHandlers) { - .did_focus = prv_app_did_focus, - }); -} - -static void prv_deinit(void) { - MultiWatchData *data = app_state_get_user_data(); - window_destroy(&data->window); - watch_model_cleanup(); -} - -void tictoc_main(void) { - prv_init(); - app_event_loop(); - prv_deinit(); -} diff --git a/src/fw/apps/watch/tictoc/spalding/watch_model.c b/src/fw/apps/watch/tictoc/spalding/watch_model.c deleted file mode 100644 index 324b5a7022..0000000000 --- a/src/fw/apps/watch/tictoc/spalding/watch_model.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "watch_model.h" - -#include "util/trig.h" -#include "applib/app_watch_info.h" -#include "applib/pbl_std/pbl_std.h" -#include "applib/tick_timer_service.h" -#include "resource/resource_ids.auto.h" -#include "syscall/syscall.h" -#include "util/time/time.h" - -#include - -static void prv_calculate_hand_angles(struct tm *tick_time, int32_t *hour_angle, - int32_t *minute_angle) { - *hour_angle = (tick_time->tm_hour % 12) * TRIG_MAX_ANGLE / 12 - + tick_time->tm_min * TRIG_MAX_ANGLE / 60 / 12; - *minute_angle = tick_time->tm_min * TRIG_MAX_ANGLE / 60; -} - -static ClockFace prv_local_clock_face_default(struct tm *tick_time) { - int32_t hour_angle, minute_angle; - prv_calculate_hand_angles(tick_time, &hour_angle, &minute_angle); - - // TODO: Don't return by value. This thing is massive. - return (ClockFace) { - .hour_hand = { - .angle = hour_angle, - .backwards_extension = LOCAL_HOUR_HAND_BACK_EXT_DEFAULT, - .color = LOCAL_HOUR_HAND_COLOR_DEFAULT, - .length = LOCAL_HOUR_HAND_LENGTH_DEFAULT, - .style = CLOCK_HAND_STYLE_ROUNDED, - .thickness = LOCAL_HOUR_HAND_THICKNESS_DEFAULT, - }, - .minute_hand = { - .angle = minute_angle, - .backwards_extension = LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT, - .color = LOCAL_MINUTE_HAND_COLOR_DEFAULT, - .length = LOCAL_MINUTE_HAND_LENGTH_DEFAULT, - .style = CLOCK_HAND_STYLE_ROUNDED, - .thickness = LOCAL_MINUTE_HAND_THICKNESS_DEFAULT, - }, - .bob_radius = LOCAL_BOB_RADIUS_DEFAULT, - .bob_color = LOCAL_BOB_COLOR_DEFAULT, - .location = CLOCK_LOCATION_CENTER, - }; -} - -static NonLocalClockFace prv_configure_non_local_clock_face(int32_t utc_offset, const char *text, - GColor text_color, GColor hand_color, - ClockTextLocation location) { - time_t t = rtc_get_time(); - struct tm* tick_time = pbl_override_gmtime(&t); - tick_time->tm_hour += utc_offset; // TODO check if this works properly - int32_t hour_angle, minute_angle; - prv_calculate_hand_angles(tick_time, &hour_angle, &minute_angle); - - // TODO: Don't return by value. This thing is massive. - NonLocalClockFace non_local_clock = (NonLocalClockFace) { - .face = { - .hour_hand = { - .length = NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT, - .thickness = NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT, - .backwards_extension = 0, - .angle = hour_angle, - .color = hand_color, - .style = CLOCK_HAND_STYLE_ROUNDED, - }, - .minute_hand = { - .length = NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT, - .thickness = NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT, - .backwards_extension = 0, - .angle = minute_angle, - .color = hand_color, - .style = CLOCK_HAND_STYLE_ROUNDED, - }, - .location = location, - }, - .text_color = text_color, - }; - strncpy(non_local_clock.buffer, text, sizeof(non_local_clock.buffer)); - return non_local_clock; -} - -// Configure the text displayed on the clock. -static ClockText prv_configure_clock_text(ClockTextType type, ClockTextLocation location, - GColor color, struct tm *tick_time) { - ClockText text = (ClockText) { - .location = location, - .color = color, - }; - - if (type == CLOCK_TEXT_TYPE_DATE) { - strftime(text.buffer, sizeof(text.buffer), "%a %d", tick_time); - } else if (type == CLOCK_TEXT_TYPE_TIME) { - strftime(text.buffer, sizeof(text.buffer), "$l:%M%P", tick_time); - } - - for (uint32_t i = 0; i < sizeof(text.buffer); i++) { - text.buffer[i] = toupper((unsigned char)text.buffer[i]); - } - - // TODO: Don't return a struct - return text; -} - -static ClockModel prv_clock_model_default(struct tm *tick_time) { - // Create a generic model and configure a default clock. - ClockModel model; - model.local_clock = prv_local_clock_face_default(tick_time); - - // Add watch-specific details. - const WatchInfoColor watch_color = sys_watch_info_get_color(); - switch (watch_color) { - case WATCH_INFO_COLOR_TIME_ROUND_BLACK_14: - model.local_clock.minute_hand.color = GColorBlue; - model.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_LEFT, - GColorWhite, tick_time); - model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_BLACK_RED; - break; - case WATCH_INFO_COLOR_TIME_ROUND_BLACK_20: - model.num_non_local_clocks = 2; - model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorDarkGray, - GColorWhite, - CLOCK_LOCATION_LEFT); - model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorDarkGray, - GColorWhite, - CLOCK_LOCATION_RIGHT); - model.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM, - GColorWhite, tick_time); - model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_BLACK; - break; - case WATCH_INFO_COLOR_TIME_ROUND_SILVER_14: - model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED; - model.local_clock.hour_hand.color = GColorBlack; - model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED; - model.local_clock.minute_hand.color = GColorCadetBlue; - model.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM, - GColorDarkGray, tick_time); - model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_SILVER; - break; - case WATCH_INFO_COLOR_TIME_ROUND_SILVER_20: - model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED; - model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED; - model.local_clock.minute_hand.color = GColorRed; - model.local_clock.bob_color = GColorBlack; - model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_SILVER_BROWN; - break; - case WATCH_INFO_COLOR_TIME_ROUND_ROSE_GOLD_14: - default: - model.local_clock.bob_center_color = GColorOrange; - model.local_clock.minute_hand.color = GColorWhite; - model.local_clock.minute_hand.thickness = 2; - model.local_clock.minute_hand.length = 54; - model.local_clock.hour_hand.color = GColorBlack; - model.local_clock.hour_hand.thickness = 8; - model.local_clock.hour_hand.length = 39; - model.local_clock.bob_radius = 7; - model.local_clock.bob_center_radius = 3; - model.local_clock.bob_color = GColorWhite; - model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_ROSE_GOLD; - break; - } - - // disable timezones until they can be configured by the user - model.num_non_local_clocks = 0; - - // TODO: Don't return a struct here - return model; -} - -static void prv_handle_time_update(struct tm *tick_time, TimeUnits units_changed) { - ClockModel model = prv_clock_model_default(tick_time); - watch_model_handle_change(&model); -} - -void watch_model_cleanup() { - tick_timer_service_unsubscribe(); -} - -static void prv_intro_animation_finished(Animation *animation) { - const time_t t = rtc_get_time(); - prv_handle_time_update(pbl_override_localtime(&t), (TimeUnits)0xff); - - tick_timer_service_subscribe(MINUTE_UNIT, prv_handle_time_update); -} - -void watch_model_start_intro() { - prv_intro_animation_finished(NULL); -} - -void watch_model_init(void) { - const time_t t = rtc_get_time(); - struct tm *tick_time = pbl_override_localtime(&t); - ClockModel model = prv_clock_model_default(tick_time); - watch_model_handle_change(&model); -} diff --git a/src/fw/apps/watch/tictoc/spalding/watch_model.h b/src/fw/apps/watch/tictoc/spalding/watch_model.h deleted file mode 100644 index 8b60cde508..0000000000 --- a/src/fw/apps/watch/tictoc/spalding/watch_model.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" - -#include - -#define LOCAL_HOUR_HAND_LENGTH_DEFAULT 51 -#define LOCAL_HOUR_HAND_THICKNESS_DEFAULT 6 -#define LOCAL_HOUR_HAND_COLOR_DEFAULT GColorWhite -#define LOCAL_HOUR_HAND_BACK_EXT_DEFAULT 0 - -#define LOCAL_MINUTE_HAND_LENGTH_DEFAULT 58 -#define LOCAL_MINUTE_HAND_THICKNESS_DEFAULT 6 -#define LOCAL_MINUTE_HAND_COLOR_DEFAULT GColorWhite -#define LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT 0 - -#define LOCAL_BOB_RADIUS_DEFAULT 6 -#define LOCAL_BOB_COLOR_DEFAULT GColorRed - -#define NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT 11 -#define NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT 3 - -#define NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT 21 -#define NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT 3 - -#define NUM_NON_LOCAL_CLOCKS 3 - -#define GLANCE_TIME_OUT_MS 8000 - -typedef enum { - CLOCK_TEXT_TYPE_NONE = 0, - CLOCK_TEXT_TYPE_TIME, - CLOCK_TEXT_TYPE_DATE, -} ClockTextType; - -typedef enum { - CLOCK_TEXT_LOCATION_NONE = 0, - CLOCK_TEXT_LOCATION_BOTTOM, - CLOCK_TEXT_LOCATION_LEFT, -} ClockTextLocation; - -typedef enum { - CLOCK_HAND_STYLE_ROUNDED = 0, - CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT, - CLOCK_HAND_STYLE_POINTED, -} ClockHandStyle; - -typedef enum { - CLOCK_LOCATION_CENTER, - CLOCK_LOCATION_LEFT, - CLOCK_LOCATION_BOTTOM, - CLOCK_LOCATION_RIGHT, - CLOCK_LOCATION_TOP, -} ClockLocation; - -typedef struct { - uint16_t length; - uint16_t thickness; - uint16_t backwards_extension; - int32_t angle; - GColor color; - ClockHandStyle style; -} ClockHand; - -typedef struct { - ClockHand hour_hand; - ClockHand minute_hand; - uint16_t bob_radius; - uint16_t bob_center_radius; - GColor bob_color; - GColor bob_center_color; - ClockLocation location; -} ClockFace; - -typedef struct { - ClockFace face; - char buffer[4]; - int32_t utc_offest; - GColor text_color; -} NonLocalClockFace; - -typedef struct { - ClockTextType type; - ClockTextLocation location; - char buffer[10]; // FIXME magic number - GColor color; -} ClockText; - -typedef struct { - ClockFace local_clock; - uint32_t num_non_local_clocks; - NonLocalClockFace non_local_clock[NUM_NON_LOCAL_CLOCKS]; - ClockText text; - uint32_t bg_bitmap_id; -} ClockModel; - -void watch_model_init(void); - -void watch_model_handle_change(ClockModel *model); - -void watch_model_start_intro(void); - -void watch_model_cleanup(void); diff --git a/src/fw/apps/watch/tictoc/tictoc.c b/src/fw/apps/watch/tictoc/tictoc.c index 48bfad852d..01273ebfc2 100644 --- a/src/fw/apps/watch/tictoc/tictoc.c +++ b/src/fw/apps/watch/tictoc/tictoc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "tictoc.h" @@ -26,9 +13,6 @@ const PebbleProcessMd* tictoc_get_app_info(void) { 0x91, 0xf5, 0x01, 0x60, 0x0c, 0x9b, 0xdc, 0x59 }, .main_func = tictoc_main, .process_type = ProcessTypeWatchface, -#if CAPABILITY_HAS_JAVASCRIPT && !defined(PLATFORM_SPALDING) - .is_rocky_app = true, -#endif }, .icon_resource_id = RESOURCE_ID_MENU_ICON_TICTOC_WATCH, .name = "TicToc", diff --git a/src/fw/apps/watch/tictoc/tictoc.h b/src/fw/apps/watch/tictoc/tictoc.h index 371cebc790..9557dff794 100644 --- a/src/fw/apps/watch/tictoc/tictoc.h +++ b/src/fw/apps/watch/tictoc/tictoc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/apps/wscript_build b/src/fw/apps/wscript_build new file mode 100644 index 0000000000..8a34862d11 --- /dev/null +++ b/src/fw/apps/wscript_build @@ -0,0 +1,6 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.recurse('demo') + +# vim:filetype=python diff --git a/src/fw/assert.c b/src/fw/assert.c index b4385e52d2..b43fb51b3f 100644 --- a/src/fw/assert.c +++ b/src/fw/assert.c @@ -1,23 +1,11 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system/logging.h" #include "system/passert.h" void __assert_func(const char *file, int line, const char *func, const char *e) { - PBL_LOG(LOG_LEVEL_ERROR, "assert at line %d, func: %s - %s", line, func, e); - WTF; + PBL_LOG_ERR("assert in %s:%d", file, line); + PBL_LOG_ERR("%s, expr: %s", func, e); + PBL_ASSERT(0, "libc assert()"); } diff --git a/src/fw/board/board.h b/src/fw/board/board.h index 612c503468..4529adb00e 100644 --- a/src/fw/board/board.h +++ b/src/fw/board/board.h @@ -1,37 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF52840_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - -#if defined(MICRO_FAMILY_STM32F2) -# include "board_stm32.h" -#elif defined(MICRO_FAMILY_STM32F4) -# include "board_stm32.h" -#elif defined(MICRO_FAMILY_STM32F7) -# include "board_stm32.h" -#elif defined(MICRO_FAMILY_NRF52840) +#if defined(CONFIG_QEMU) +# include "board_qemu.h" +#elif defined(CONFIG_SOC_NRF52) # include "board_nrf5.h" -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) # include "board_sf32lb52.h" #elif !defined(SDK) && !defined(UNITTEST) # error "Unknown or missing MICRO_FAMILY_* define" diff --git a/src/fw/board/board_definitions.h b/src/fw/board/board_definitions.h index 2c79740ddc..2a2c29737a 100644 --- a/src/fw/board/board_definitions.h +++ b/src/fw/board/board_definitions.h @@ -1,68 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once // FIXME: PBL-21049 Fix platform abstraction and board definition scheme -#if BOARD_EV2_4 -#include "boards/board_ev2_4.h" // shipped as Pebble 1.0 -#elif BOARD_BB2 -#include "boards/board_bb2.h" -#elif BOARD_V1_5 -#include "boards/board_v1_5.h" // prototypes for Pebble 1.3/Pebble 1.5 -#elif BOARD_V2_0 -#include "boards/board_v2_0.h" // prototypes for Pebble 2.0 -#elif BOARD_SNOWY_BB -#include "boards/board_snowy_bb.h" // prototypes for Snowy bigboard -#elif BOARD_SNOWY_EVT -#include "boards/board_snowy_evt.h" // prototypes for Snowy EVT -#elif BOARD_SNOWY_EVT2 -#include "boards/board_snowy.h" // prototypes for Snowy EVT2 -#elif BOARD_SNOWY_BB2 -#include "boards/board_snowy.h" // prototypes for Snowy BB2 are identical to EVT2 -#elif BOARD_SNOWY_DVT -#include "boards/board_snowy.h" // prototypes for DVT, electrically identical to EVT2 -#elif BOARD_SPALDING_BB2 -#include "boards/board_snowy.h" // prototypes for Spalding BB2, Snowy BB2s with a Spalding display -#elif BOARD_SPALDING_EVT -#include "boards/board_spalding_evt.h" // prototypes for Spalding EVT -#elif BOARD_SPALDING -#include "boards/board_spalding_evt.h" // prototypes for Spalding MP -#elif BOARD_SNOWY_S3 -#include "boards/board_snowy.h" // prototypes for Spalding EVT, electrically identical to Snowy -#elif BOARD_SILK_EVT -#include "boards/board_silk.h" -#elif BOARD_SILK_BB -#include "boards/board_silk.h" -#elif BOARD_SILK_BB2 -#include "boards/board_silk.h" -#elif BOARD_SILK -#include "boards/board_silk.h" -#elif BOARD_CUTTS_BB -#include "boards/board_robert.h" // prototypes for Cutts -#elif BOARD_ROBERT_BB -#include "boards/board_robert.h" // prototypes for Robert BB -#elif BOARD_ROBERT_BB2 -#include "boards/board_robert.h" // prototypes for Robert BB2 -#elif BOARD_ROBERT_EVT -#include "boards/board_robert.h" // prototypes for Robert EVT -#elif BOARD_ASTERIX +#ifdef CONFIG_BOARD_ASTERIX #include "boards/board_asterix.h" -#elif BOARD_OBELIX || BOARD_OBELIX_BB +#elif defined(CONFIG_BOARD_OBELIX_DVT) || defined(CONFIG_BOARD_OBELIX_PVT) || defined(CONFIG_BOARD_OBELIX_BB2) #include "boards/board_obelix.h" +#elif defined(CONFIG_BOARD_GETAFIX_EVT) || defined(CONFIG_BOARD_GETAFIX_DVT) || defined(CONFIG_BOARD_GETAFIX_DVT2) +#include "boards/board_getafix.h" +#elif defined(CONFIG_BOARD_QEMU_EMERY) +#include "boards/board_qemu_emery.h" +#elif defined(CONFIG_BOARD_QEMU_FLINT) +#include "boards/board_qemu_flint.h" +#elif defined(CONFIG_BOARD_QEMU_GABBRO) +#include "boards/board_qemu_gabbro.h" #else #error "Unknown board definition" #endif diff --git a/src/fw/board/board_nrf5.h b/src/fw/board/board_nrf5.h index fdb7516b61..3cd103d15b 100644 --- a/src/fw/board/board_nrf5.h +++ b/src/fw/board/board_nrf5.h @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #pragma once #include "display.h" @@ -5,9 +8,6 @@ #include "drivers/button_id.h" #include "debug/power_tracking.h" -#define NRF52840_COMPATIBLE -#include - #include #include @@ -29,8 +29,8 @@ // This is generated in order to faciliate the check within the IRQ_MAP macro below enum { #define IRQ_DEF(num, irq) IS_VALID_IRQ__##irq, -#if defined(MICRO_FAMILY_NRF52840) -# include "irq_nrf52840.def" +#if defined(CONFIG_SOC_NRF52) +# include "irq_nrf52.def" #else # error need IRQ table for new micro family #endif @@ -53,7 +53,7 @@ enum { /* * The above static assert checks that the requested IRQ is valid by checking that the enum * value (generated above) is declared. The static assert itself will not trip, but you will get - * a compilation error from that line if the IRQ does not exist within irq_stm32*.def. + * a compilation error from that line if the IRQ does not exist within irq_nrf52.def. */ // There are a lot of DMA streams and they are very straight-forward to define. Let's use some @@ -146,6 +146,9 @@ typedef struct { uint8_t tap_shock; uint8_t tap_quiet; uint8_t tap_dur; + // Default motion sensitivity (0-100), where 100 = most sensitive. + // A value of 0 means use the firmware default (85 = High). + uint8_t default_motion_sensitivity; } AccelConfig; typedef struct { @@ -173,7 +176,6 @@ typedef enum { typedef enum { ActuatorOptions_Ctl = 1 << 0, ///< GPIO is used to enable / disable vibe ActuatorOptions_Pwm = 1 << 1, ///< PWM control - ActuatorOptions_IssiI2C = 1 << 2, ///< I2C Device, currently used for V1_5 -> OG steel backlight ActuatorOptions_HBridge = 1 << 3, //< PWM actuates an H-Bridge, requires ActuatorOptions_PWM } ActuatorOptions; @@ -195,15 +197,7 @@ typedef struct { const GpioteConfig dbgserial_int; const InputConfig dbgserial_int_gpio; - // Display Configuration - ///////////////////////////////////////////////////////////////////////////// - const OutputConfig lcd_com; //!< This needs to be pulsed regularly to keep the sharp display fresh. - const uint8_t backlight_on_percent; // percent of max possible brightness - const uint8_t backlight_max_duty_cycle_percent; // Calibrated such that the preceived brightness - // of "backlight_on_percent = 100" (and all other values, to a reasonable - // tolerance) is identical across all platforms. >100% isn't possible, so - // future backlights must be at least as bright as Tintin's. } BoardConfig; // Button Configuration @@ -290,6 +284,7 @@ typedef const struct HRMDevice HRMDevice; typedef const struct MicDevice MicDevice; typedef const struct QSPIPort QSPIPort; typedef const struct QSPIFlash QSPIFlash; +typedef const struct AudioDevice AudioDevice; void board_early_init(void); void board_init(void); diff --git a/src/fw/board/board_qemu.h b/src/fw/board/board_qemu.h new file mode 100644 index 0000000000..3907856a45 --- /dev/null +++ b/src/fw/board/board_qemu.h @@ -0,0 +1,235 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "display.h" + +#include +#include +#include + +#include "drivers/button_id.h" + +#define IRQ_PRIORITY_INVALID (1 << __NVIC_PRIO_BITS) + +enum { + #define IRQ_DEF(num, irq) IS_VALID_IRQ__##irq, + #include "irq_qemu.def" + #undef IRQ_DEF +}; + +//! Creates a trampoline to the interrupt handler defined within the driver +#define IRQ_MAP(irq, handler, device) \ + void irq##_IRQHandler(void) { \ + handler(device); \ + } \ + _Static_assert(IS_VALID_IRQ__##irq || true, "(See comment below)") +/* + * The above static assert checks that the requested IRQ is valid by checking that the enum + * value (generated above) is declared. The static assert itself will not trip, but you will get + * a compilation error from that line if the IRQ does not exist within irq_qemu.def. + */ + +// Compatibility type for gpio.h (QEMU has no real GPIO peripheral struct) +typedef void GPIO_TypeDef; + +#define GPIO_Port_NULL NULL +#define GPIO_Pin_NULL 0U + +typedef enum { + GPIO_OType_PP, + GPIO_OType_OD, +} GPIOOType_TypeDef; + +typedef enum { + GPIO_PuPd_NOPULL, + GPIO_PuPd_UP, + GPIO_PuPd_DOWN, +} GPIOPuPd_TypeDef; + +typedef enum { + GPIO_Speed_2MHz, + GPIO_Speed_50MHz, + GPIO_Speed_200MHz +} GPIOSpeed_TypeDef; + +typedef struct { + void *const peripheral; + const uint32_t gpio_pin; + GPIOPuPd_TypeDef pull; +} ExtiConfig; + +typedef struct { + void *gpio; + uint8_t gpio_pin; +} InputConfig; + +typedef struct { + void *gpio; + uint8_t gpio_pin; + bool active_high; +} OutputConfig; + +typedef struct { + void *gpio; + uint8_t gpio_pin; +} AfConfig; + +typedef struct { + int pad; + int func; + int flags; +} Pinmux; + +typedef struct { + uint16_t value; + uint16_t resolution; + int enabled; + uint16_t channel; +} PwmState; + +typedef struct { + Pinmux pwm_pin; + PwmState *state; +} PwmConfig; + +typedef struct { +} TimerConfig; + +typedef enum { + ActuatorOptions_Ctl = 1 << 0, + ActuatorOptions_Pwm = 1 << 1, + ActuatorOptions_HBridge = 1 << 3, +} ActuatorOptions; + +typedef struct { + const ActuatorOptions options; + const OutputConfig ctl; + const PwmConfig pwm; +} BoardConfigActuator; + +typedef struct { + uint8_t backlight_on_percent; + uint32_t ambient_light_dark_threshold; + uint32_t ambient_k_delta_threshold; +#ifdef CONFIG_DYNAMIC_BACKLIGHT + uint32_t dynamic_backlight_min_threshold; +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + // Default RGB backlight color (packed 0x00RRGGBB), applied when no app + // override is set. User-preference overrides this via backlight_set_color(). + uint32_t backlight_default_color; +#endif + ExtiConfig dbgserial_int; + InputConfig dbgserial_int_gpio; +} BoardConfig; + +typedef struct { + const char *name; + void *const port; + uint8_t pin; + GPIOPuPd_TypeDef pull; + bool active_high; +} ButtonConfig; + +typedef struct { + ButtonConfig buttons[NUM_BUTTONS]; + void *timer; + int timer_irqn; +} BoardConfigButton; + +typedef struct { + ExtiConfig pmic_int; + const uint8_t low_power_threshold; + const uint16_t battery_capacity_hours; +} BoardConfigPower; + +typedef enum { + AccelThresholdLow, + AccelThresholdHigh, + AccelThreshold_Num, +} AccelThreshold; + +typedef struct { + int axes_offsets[3]; + bool axes_inverts[3]; + uint32_t shake_thresholds[AccelThreshold_Num]; + uint32_t double_tap_threshold; + uint8_t tap_shock; + uint8_t tap_quiet; + uint8_t tap_dur; + uint8_t default_motion_sensitivity; +} AccelConfig; + +typedef struct { + const AccelConfig accel_config; + const InputConfig accel_int_gpios[2]; + const ExtiConfig accel_ints[2]; +} BoardConfigAccel; + +typedef struct { + int axes_offsets[3]; + bool axes_inverts[3]; +} MagConfig; + +typedef struct { + const MagConfig mag_config; +} BoardConfigMag; + +typedef enum { + SpiPeriphClockAPB1, + SpiPeriphClockAPB2 +} SpiPeriphClock; + +// QEMU MMIO peripheral base addresses +#define QEMU_UART0_BASE 0x40000000 +#define QEMU_UART1_BASE 0x40001000 +#define QEMU_UART2_BASE 0x40002000 +#define QEMU_TIMER0_BASE 0x40003000 +#define QEMU_TIMER1_BASE 0x40004000 +#define QEMU_RTC_BASE 0x40005000 +#define QEMU_GPIO_BASE 0x40006000 +#define QEMU_SYSCTRL_BASE 0x40007000 +#define QEMU_DISPLAY_BASE 0x40008000 +#define QEMU_DISPLAY_FB_BASE 0x50000000 +#define QEMU_EXTFLASH_BASE 0x40010000 +#define QEMU_TOUCH_BASE 0x40011000 +#define QEMU_AUDIO_BASE 0x40012000 + +#define QEMU_EXTFLASH_XIP_BASE 0x10000000 + +// Forward-declare device types +typedef const struct UARTDevice UARTDevice; +typedef const struct SPIBus SPIBus; +typedef const struct SPISlavePort SPISlavePort; +typedef const struct I2CBus I2CBus; +typedef const struct I2CSlavePort I2CSlavePort; +typedef const struct QSPIPort QSPIPort; +typedef const struct QSPIFlash QSPIFlash; +typedef const struct HRMDevice HRMDevice; +typedef const struct MicDevice MicDevice; +typedef const struct AudioDevice AudioDevice; + +// QEMU display device +typedef struct QemuDisplayDevice { + uint32_t base_addr; + uint32_t fb_addr; + uint16_t width; + uint16_t height; + uint8_t bpp; + int irqn; + int irq_priority; +} QemuDisplayDevice; +typedef const QemuDisplayDevice DisplayDevice; + +extern AudioDevice *const AUDIO; + +void board_early_init(void); +void board_init(void); + +// QEMU doesn't have newlib-nano sniprintf; use standard snprintf +#include +#define sniprintf snprintf + +#include "board_definitions.h" diff --git a/src/fw/board/board_sf32lb52.h b/src/fw/board/board_sf32lb52.h index ea4cd4c305..5f461e2c61 100644 --- a/src/fw/board/board_sf32lb52.h +++ b/src/fw/board/board_sf32lb52.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -67,6 +54,7 @@ typedef enum { typedef struct { GPIO_TypeDef* const peripheral; ///< One of GPIOX. For example, GPIOA. const uint32_t gpio_pin; ///< One of GPIO_Pin_X. + GPIOPuPd_TypeDef pull; ///< Pull-up / pull-down configuration for the pin } ExtiConfig; typedef struct { @@ -112,7 +100,6 @@ typedef struct { typedef enum { ActuatorOptions_Ctl = 1 << 0, ///< GPIO is used to enable / disable vibe ActuatorOptions_Pwm = 1 << 1, ///< PWM control - ActuatorOptions_IssiI2C = 1 << 2, ///< I2C Device, currently used for V1_5 -> OG steel backlight ActuatorOptions_HBridge = 1 << 3, //< PWM actuates an H-Bridge, requires ActuatorOptions_PWM } ActuatorOptions; @@ -124,13 +111,18 @@ typedef struct { typedef struct { uint8_t backlight_on_percent; - uint8_t backlight_max_duty_cycle_percent; - ExtiConfig dbgserial_int; - InputConfig dbgserial_int_gpio; - OutputConfig lcd_com; //ambient light config uint32_t ambient_light_dark_threshold; uint32_t ambient_k_delta_threshold; +#ifdef CONFIG_DYNAMIC_BACKLIGHT + //dynamic backlight thresholds + uint32_t dynamic_backlight_min_threshold; +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + // Default RGB backlight color (packed 0x00RRGGBB), applied when no app + // override is set. User-preference overrides this via backlight_set_color(). + uint32_t backlight_default_color; +#endif } BoardConfig; typedef struct { @@ -152,7 +144,7 @@ typedef struct { //! Percentage for watch only mode const uint8_t low_power_threshold; //! Approximate hours of battery life - const uint8_t battery_capacity_hours; + const uint16_t battery_capacity_hours; } BoardConfigPower; typedef enum { @@ -174,6 +166,9 @@ typedef struct { uint8_t tap_shock; uint8_t tap_quiet; uint8_t tap_dur; + // Default motion sensitivity (0-100), where 100 = most sensitive. + // A value of 0 means use the firmware default (85 = High). + uint8_t default_motion_sensitivity; } AccelConfig; typedef struct { @@ -200,9 +195,10 @@ typedef enum { #include "drivers/flash/qspi_flash.h" #include "drivers/flash/qspi_flash_definitions.h" #include "drivers/qspi_definitions.h" -#include "drivers/sf32lb52/uart_definitions.h" -#include "drivers/sf32lb52/jdi_lpm015m135a.h" +#include "drivers/uart/sf32lb.h" +#include "drivers/display/sf32lb/display_jdi.h" #include "drivers/mic/sf32lb52/pdm_definitions.h" +#include "drivers/speaker/sf32lb52/audio_definitions.h" typedef const struct DMARequest DMARequest; typedef const struct UARTDevice UARTDevice; @@ -215,12 +211,12 @@ typedef const struct MicDevice MicDevice; typedef const struct QSPIPort QSPIPort; typedef const struct QSPIFlash QSPIFlash; typedef const struct DisplayJDIDevice DisplayJDIDevice; +typedef const struct AudioDevice AudioDevice; -#include "drivers/i2c_definitions.h" -#include "drivers/sf32lb52/i2c_hal_definitions.h" +#include "drivers/i2c/definitions.h" +#include "drivers/i2c/sf32lb.h" void board_early_init(void); void board_init(void); #include "board_definitions.h" - diff --git a/src/fw/board/board_stm32.h b/src/fw/board/board_stm32.h deleted file mode 100644 index 16a8bb4165..0000000000 --- a/src/fw/board/board_stm32.h +++ /dev/null @@ -1,378 +0,0 @@ -#pragma once - -#include "display.h" - -#include "drivers/button_id.h" -#include "debug/power_tracking.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include -#include - -#define GPIO_Port_NULL ((GPIO_TypeDef *) 0) -#define GPIO_Pin_NULL ((uint16_t)0x0000) -//! Guaranteed invalid IRQ priority -#define IRQ_PRIORITY_INVALID (1 << __NVIC_PRIO_BITS) - -// This is generated in order to faciliate the check within the IRQ_MAP macro below -enum { -#define IRQ_DEF(num, irq) IS_VALID_IRQ__##irq, -#include "irq_stm32.def" -#undef IRQ_DEF -}; - -//! Creates a trampoline to the interrupt handler defined within the driver -#define IRQ_MAP(irq, handler, device) \ - void irq##_IRQHandler(void) { \ - handler(device); \ - } \ - _Static_assert(IS_VALID_IRQ__##irq || true, "(See comment below)") -/* - * The above static assert checks that the requested IRQ is valid by checking that the enum - * value (generated above) is declared. The static assert itself will not trip, but you will get - * a compilation error from that line if the IRQ does not exist within irq_stm32*.def. - */ - -// There are a lot of DMA streams and they are very straight-forward to define. Let's use some -// macro magic to make it a bit less tedious and error-prone. -#define CREATE_DMA_STREAM(cnum, snum) \ - static DMAStreamState s_dma##cnum##_stream##snum##_state; \ - static DMAStream DMA##cnum##_STREAM##snum##_DEVICE = { \ - .state = &s_dma##cnum##_stream##snum##_state, \ - .controller = &DMA##cnum##_DEVICE, \ - .periph = DMA##cnum##_Stream##snum, \ - .irq_channel = DMA##cnum##_Stream##snum##_IRQn, \ - }; \ - IRQ_MAP(DMA##cnum##_Stream##snum, dma_stream_irq_handler, &DMA##cnum##_STREAM##snum##_DEVICE) - -typedef struct { - //! One of EXTI_PortSourceGPIOX - uint8_t exti_port_source; - - //! Value between 0-15 - uint8_t exti_line; -} ExtiConfig; - -typedef enum { - AccelThresholdLow, ///< A sensitive state used for stationary mode - AccelThresholdHigh, ///< The accelerometer's default sensitivity - AccelThreshold_Num, -} AccelThreshold; - -typedef struct { - const char* const name; ///< Name for debugging purposes. - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - ExtiConfig exti; - GPIOPuPd_TypeDef pull; -} ButtonConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} ButtonComConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. -} InputConfig; - -typedef struct { - ADC_TypeDef *const adc; ///< One of ADCX. For example ADC1. - const uint8_t adc_channel; ///< One of ADC_Channel_* - uint32_t clock_ctrl; ///< Peripheral clock control flag - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint16_t gpio_pin; ///< One of GPIO_Pin_* -} ADCInputConfig; - -typedef struct { - union { - TIM_TypeDef* const peripheral; ///< A TIMx peripheral -#if MICRO_FAMILY_STM32F7 - LPTIM_TypeDef* const lp_peripheral; ///< A LPTIMx peripheral -#endif - }; - const uint32_t config_clock; ///< One of RCC_APB1Periph_TIMx. For example, RCC_APB1Periph_TIM3. - void (* const init)(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); ///< One of TIM_OCxInit - void (* const preload)(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); ///< One of TIM_OCxPreloadConfig -} TimerConfig; - -typedef struct { - const TimerConfig timer; - const uint8_t irq_channel; -} TimerIrqConfig; - -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - bool active_high; ///< Pin is active high or active low -} OutputConfig; - -//! Alternate function pin configuration -//! Used to configure a pin for use by a peripheral -typedef struct { - GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. - const uint32_t gpio_pin; ///< One of GPIO_Pin_X. - const uint16_t gpio_pin_source; ///< One of GPIO_PinSourceX. - const uint8_t gpio_af; ///< One of GPIO_AF_X -} AfConfig; - -typedef struct { - OutputConfig output; - TimerConfig timer; - AfConfig afcfg; -} PwmConfig; - -typedef struct { - int axes_offsets[3]; - bool axes_inverts[3]; - uint32_t shake_thresholds[AccelThreshold_Num]; - uint32_t double_tap_threshold; -} AccelConfig; - -typedef struct { - int axes_offsets[3]; - bool axes_inverts[3]; -} MagConfig; - -typedef struct { - AfConfig i2s_ck; - AfConfig i2s_sd; - SPI_TypeDef *spi; - uint32_t spi_clock_ctrl; - uint16_t gain; - - //! Pin we use to control power to the microphone. Only used on certain boards. - OutputConfig mic_gpio_power; -} MicConfig; - -typedef enum { - OptionNotPresent = 0, // FIXME - OptionActiveLowOpenDrain, - OptionActiveHigh -} PowerCtl5VOptions; - -typedef enum { - ActuatorOptions_Ctl = 1 << 0, ///< GPIO is used to enable / disable vibe - ActuatorOptions_Pwm = 1 << 1, ///< PWM control - ActuatorOptions_IssiI2C = 1 << 2, ///< I2C Device, currently used for V1_5 -> OG steel backlight - ActuatorOptions_HBridge = 1 << 3, //< PWM actuates an H-Bridge, requires ActuatorOptions_PWM -} ActuatorOptions; - -typedef enum { - CC2564A = 0, - CC2564B, - DA14681, -} BluetoothController; - -typedef struct { - // Audio Configuration - ///////////////////////////////////////////////////////////////////////////// - const bool has_mic; - const MicConfig mic_config; - - // Ambient Light Configuration - ///////////////////////////////////////////////////////////////////////////// - const uint32_t ambient_light_dark_threshold; - const uint32_t ambient_k_delta_threshold; - const OutputConfig photo_en; - const bool als_always_on; - - // Debug Serial Configuration - ///////////////////////////////////////////////////////////////////////////// - const ExtiConfig dbgserial_int; - const InputConfig dbgserial_int_gpio; - - // MFi Configuration - ///////////////////////////////////////////////////////////////////////////// - const OutputConfig mfi_reset_pin; - - // Display Configuration - ///////////////////////////////////////////////////////////////////////////// - const OutputConfig lcd_com; //!< This needs to be pulsed regularly to keep the sharp display fresh. - - //! Controls power to the sharp display - const PowerCtl5VOptions power_5v0_options; - const OutputConfig power_ctl_5v0; - - const uint8_t backlight_on_percent; // percent of max possible brightness - const uint8_t backlight_max_duty_cycle_percent; // Calibrated such that the preceived brightness - // of "backlight_on_percent = 100" (and all other values, to a reasonable - // tolerance) is identical across all platforms. >100% isn't possible, so - // future backlights must be at least as bright as Tintin's. - - // FPC Pinstrap Configuration - ///////////////////////////////////////////////////////////////////////////// - const InputConfig fpc_pinstrap_1; - const InputConfig fpc_pinstrap_2; - - // GPIO Configuration - ///////////////////////////////////////////////////////////////////////////// - const uint16_t num_avail_gpios; -} BoardConfig; - -// Button Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ButtonConfig buttons[NUM_BUTTONS]; - const ButtonComConfig button_com; - const bool active_high; -} BoardConfigButton; - -typedef struct { - const uint32_t numerator; - const uint32_t denominator; -} VMonScale; - -// Power Configuration -///////////////////////////////////////////////////////////////////////////// -typedef struct { - const ExtiConfig pmic_int; - const InputConfig pmic_int_gpio; - - //! Voltage rail control lines - const OutputConfig rail_4V5_ctrl; - const OutputConfig rail_6V6_ctrl; - const GPIOOType_TypeDef rail_6V6_ctrl_otype; - - //! Scaling factor for battery vmon - const VMonScale battery_vmon_scale; - //! Tells us if the USB cable plugged in. - const InputConfig vusb_stat; - const ExtiConfig vusb_exti; - //! Tells us whether the charger thinks we're charging or not. - const InputConfig chg_stat; - //! Tell the charger to use 2x current to charge faster (MFG only). - const OutputConfig chg_fast; - //! Enable the charger. We may want to disable this in MFG, normally it's always on. - const OutputConfig chg_en; - - //! Interrupt that fires when the USB cable is plugged in - const bool has_vusb_interrupt; - - const bool wake_on_usb_power; - - const int charging_cutoff_voltage; - const int charging_status_led_voltage_compensation; - - //! Percentage for watch only mode - const uint8_t low_power_threshold; - - //! Approximate hours of battery life - const uint8_t battery_capacity_hours; -} BoardConfigPower; - -typedef struct { - const AccelConfig accel_config; - const InputConfig accel_int_gpios[2]; - const ExtiConfig accel_ints[2]; -} BoardConfigAccel; - -typedef struct { - const MagConfig mag_config; - const InputConfig mag_int_gpio; - const ExtiConfig mag_int; -} BoardConfigMag; - -typedef struct { - const ActuatorOptions options; - const OutputConfig ctl; - const PwmConfig pwm; - const uint16_t vsys_scale; //< Voltage to scale duty cycle to in mV. 0 if no scaling should occur. - //< For example, Silk VBat may droop to 3.3V, so we scale down vibe - //< duty cycle so that 100% duty cycle will always be 3.3V RMS. -} BoardConfigActuator; - -typedef struct { - const OutputConfig power_en; //< Enable power supply to the accessory connector. - const InputConfig int_gpio; - const ExtiConfig exti; -} BoardConfigAccessory; - -typedef struct { - const BluetoothController controller; - - union { - //! Used with CC2564x - const OutputConfig shutdown; - - //! Used with DA14681 - const OutputConfig reset; - }; - - struct { - const InputConfig int_gpio; - const ExtiConfig int_exti; - } wakeup; -} BoardConfigBTCommon; - -typedef struct { - const bool output_enabled; - const AfConfig af_cfg; - const InputConfig an_cfg; -} BoardConfigMCO1; - -typedef struct { - const OutputConfig cs; -} BoardConfigBTSPI; - -typedef struct { - const AfConfig rx_af_cfg; - const AfConfig tx_af_cfg; - - USART_TypeDef* const rx_uart; - USART_TypeDef* const tx_uart; - - const uint32_t rx_clk_control; - const uint32_t tx_clk_control; -} BoardConfigBTUART; - -// REVISIT: -// This enum exists to allow older roll-your-own SPI -// drivers to continue to work until they are reworked -// to use the new SPI API. It can go away once the -// new API is used exclusively - -typedef enum { - SpiPeriphClockAPB1, - SpiPeriphClockAPB2 -} SpiPeriphClock; - -typedef struct { - SPI_TypeDef *spi; - GPIO_TypeDef * const spi_gpio; - - const uint32_t spi_clk; - const SpiPeriphClock spi_clk_periph; - - const AfConfig mosi; - const AfConfig clk; - const OutputConfig cs; - - const OutputConfig on_ctrl; - const GPIOOType_TypeDef on_ctrl_otype; -} BoardConfigSharpDisplay; - -typedef const struct DMARequest DMARequest; -typedef const struct UARTDevice UARTDevice; -typedef const struct SPIBus SPIBus; -typedef const struct SPISlavePort SPISlavePort; -typedef const struct I2CBus I2CBus; -typedef const struct I2CSlavePort I2CSlavePort; -typedef const struct HRMDevice HRMDevice; -typedef const struct VoltageMonitorDevice VoltageMonitorDevice; -typedef const struct AnalogTemperatureSensor AnalogTemperatureSensor; -typedef const struct MicDevice MicDevice; -typedef const struct QSPIPort QSPIPort; -typedef const struct QSPIFlash QSPIFlash; -typedef const struct ICE40LPDevice ICE40LPDevice; -typedef const struct TouchSensor TouchSensor; - -void board_early_init(void); -void board_init(void); - -#include "board_definitions.h" diff --git a/src/fw/board/boards/board_asterix.c b/src/fw/board/boards/board_asterix.c index b2ee076aaf..0455a452a7 100644 --- a/src/fw/board/boards/board_asterix.c +++ b/src/fw/board/boards/board_asterix.c @@ -1,15 +1,19 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include #include "board/board.h" +#include "drivers/audio.h" #include "drivers/flash/qspi_flash_definitions.h" #include "drivers/gpio.h" #include "drivers/i2c.h" -#include "drivers/i2c_definitions.h" +#include "drivers/i2c/definitions.h" #include "drivers/mic.h" #include "drivers/mic/nrf5/pdm_definitions.h" -#include "drivers/nrf5/i2c_hal_definitions.h" -#include "drivers/nrf5/spi_definitions.h" -#include "drivers/nrf5/uart_definitions.h" +#include "drivers/speaker/nrf5/da7212_definitions.h" +#include "drivers/i2c/nrf5.h" +#include "drivers/uart/nrf5.h" #include "drivers/pmic/npm1300.h" #include "drivers/pwm.h" #include "drivers/qspi_definitions.h" @@ -31,7 +35,7 @@ static QSPIPortState s_qspi_port_state; static QSPIPort QSPI_PORT = { .state = &s_qspi_port_state, - .auto_polling_interval = 16, + .clk_freq_hz = 8000000UL, .cs_gpio = NRF_GPIO_PIN_MAP(0, 17), .clk_gpio = NRF_GPIO_PIN_MAP(0, 19), .data_gpio = @@ -48,13 +52,10 @@ static QSPIFlashState s_qspi_flash_state; static QSPIFlash QSPI_FLASH_DEVICE = { .state = &s_qspi_flash_state, .qspi = &QSPI_PORT, - .default_fast_read_ddr_enabled = false, .read_mode = QSPI_FLASH_READ_READ4IO, .write_mode = QSPI_FLASH_WRITE_PP4O, - .reset_gpio = {GPIO_Port_NULL}, }; QSPIFlash *const QSPI_FLASH = &QSPI_FLASH_DEVICE; -IRQ_MAP_NRFX(QSPI, nrfx_qspi_irq_handler); /* PERIPHERAL ID 43 */ static UARTDeviceState s_dbg_uart_state; @@ -197,9 +198,37 @@ static MicDevice s_mic_device = { .pdm_instance = NRFX_PDM_INSTANCE(0), .clk_pin = NRF_GPIO_PIN_MAP(1, 0), // P1.00 - PDM CLK .data_pin = NRF_GPIO_PIN_MAP(0, 24), // P0.24 - PDM DATA + .channels = 1, }; MicDevice * const MIC = &s_mic_device; +/* Speaker / audio output (DA7212 codec over I2S) */ +static AudioDeviceState s_audio_state_storage; +static void prv_audio_power_up(void) { + NPM1300_OPS.dischg_limit_ma_set(NPM1300_DISCHG_LIMIT_MA_MAX); +} +static void prv_audio_power_down(void) { + NPM1300_OPS.dischg_limit_ma_set(NPM1300_CONFIG.dischg_limit_ma); +} +static const BoardPowerOps s_audio_power_ops = { + .power_up = prv_audio_power_up, + .power_down = prv_audio_power_down, +}; +static const AudioDevice s_audio_device = { + .state = &s_audio_state_storage, + .i2s_instance = NRFX_I2S_INSTANCE(0), + .sck_pin = NRF_GPIO_PIN_MAP(0, 12), // P0.12 - I2S SCK -> DA7212 BCLK + .lrck_pin = NRF_GPIO_PIN_MAP(0, 7), // P0.07 - I2S LRCK -> DA7212 WCLK + .mck_pin = NRF_GPIO_PIN_MAP(1, 9), // P1.09 - I2S MCK -> DA7212 MCLK + .sdout_pin = NRF_GPIO_PIN_MAP(0, 13), // P0.13 - I2S SDOUT -> DA7212 DATA_IN + .sdin_pin = NRF_I2S_PIN_NOT_CONNECTED, // codec DATA_OUT unused for playback + .irq_priority = 5, + .codec = &I2C_SLAVE_DA7212, + .power_ops = &s_audio_power_ops, + .samplerate = 16000, +}; +AudioDevice * const AUDIO = (AudioDevice *)&s_audio_device; + /* sensor SPI bus */ /* asterix shares SPI with flash, which we don't support */ @@ -215,10 +244,11 @@ const Npm1300Config NPM1300_CONFIG = { .dischg_limit_ma = 200, .term_current_pct = 10, .thermistor_beta = 3380, + .ntc_hot_celsius = 45, }; void board_early_init(void) { - PBL_LOG(LOG_LEVEL_ERROR, "asterix early init"); + PBL_LOG_ERR("asterix early init"); NRF_NVMC->ICACHECNF |= NVMC_ICACHECNF_CACHEEN_Msk; @@ -242,7 +272,4 @@ void board_init(void) { i2c_use(I2C_DA7212); i2c_write_block(I2C_DA7212, 2, da7212_powerdown); i2c_release(I2C_DA7212); - - // XXX: FIRM-264: stop mode breaks NimBLE - stop_mode_disable(InhibitorMain); } diff --git a/src/fw/board/boards/board_asterix.h b/src/fw/board/boards/board_asterix.h index 44a87a2d0b..de26fe8f66 100644 --- a/src/fw/board/boards/board_asterix.h +++ b/src/fw/board/boards/board_asterix.h @@ -1,7 +1,8 @@ #pragma once +#include "drivers/backlight/pwm.h" #include "drivers/pmic/npm1300.h" -#include "services/imu/units.h" +#include "pbl/services/imu/units.h" #include "util/size.h" #define BT_VENDOR_ID 0x0EEA @@ -18,7 +19,6 @@ static const BoardConfig BOARD_CONFIG = { .als_always_on = true, .backlight_on_percent = 25, - .backlight_max_duty_cycle_percent = 67, .dbgserial_int = { .peripheral = NRFX_GPIOTE_INSTANCE(0), @@ -28,7 +28,7 @@ static const BoardConfig BOARD_CONFIG = { .has_mic = true, .mic_config = { - .gain = 65, + .gain = 40, } }; @@ -50,11 +50,8 @@ static const BoardConfigButton BOARD_CONFIG_BUTTON = { static const BoardConfigPower BOARD_CONFIG_POWER = { .pmic_int = { NRFX_GPIOTE_INSTANCE(0), 1, NRF_GPIO_PIN_MAP(1, 12) }, .pmic_int_gpio = { NRF5_GPIO_RESOURCE_EXISTS, NRF_GPIO_PIN_MAP(1, 12) }, - .low_power_threshold = 5, - // Current is not great but getting there. 1 mA or so on 130 mAh battery; - // Memfault reports 160h expected battery, but we'll conservatively - // estimate 130 hours - .battery_capacity_hours = 130, + .low_power_threshold = 2, + .battery_capacity_hours = 450, }; static const BoardConfigActuator BOARD_CONFIG_VIBE = { @@ -84,6 +81,7 @@ static const BoardConfigAccel BOARD_CONFIG_ACCEL = { .tap_shock = 0x03, .tap_quiet = 0x02, .tap_dur = 0x08, + .default_motion_sensitivity = 85, // High }, // Ideally we would configure both interrupt pins, but we have run out of GPIOTE channels. // We will use INT1 (connected to pin 13) for accelerometer interrupts, and leave INT2 (pin 11) unused. @@ -109,14 +107,14 @@ static const BoardConfigMag BOARD_CONFIG_MAG = { extern UARTDevice * const DBG_UART; extern PwmState BACKLIGHT_PWM_STATE; -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm | ActuatorOptions_Ctl, +static const BacklightPwmConfig BACKLIGHT_PWM = { .ctl = { NRF5_GPIO_RESOURCE_EXISTS, NRF_GPIO_PIN_MAP(1, 8), true }, .pwm = { .state = &BACKLIGHT_PWM_STATE, .output = { NRF5_GPIO_RESOURCE_EXISTS, NRF_GPIO_PIN_MAP(0, 26), true }, .peripheral = NRFX_PWM_INSTANCE(0) }, + .max_duty_cycle_percent = 67, }; static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { @@ -143,6 +141,7 @@ extern QSPIPort * const QSPI; extern QSPIFlash * const QSPI_FLASH; extern MicDevice * const MIC; +extern AudioDevice * const AUDIO; extern I2CSlavePort * const I2C_NPM1300; extern I2CSlavePort * const I2C_DRV2604; diff --git a/src/fw/board/boards/board_bb2.c b/src/fw/board/boards/board_bb2.c deleted file mode 100644 index 6073f51067..0000000000 --- a/src/fw/board/boards/board_bb2.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/voltage_monitor.h" - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 4); // DMA1_STREAM4_DEVICE - Sharp SPI TX - -// DMA Requests - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = 0, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_sharp_spi_tx_dma_request_state; -static DMARequest SHARP_SPI_TX_DMA_REQUEST = { - .state = &s_sharp_spi_tx_dma_request_state, - .stream = &DMA1_STREAM4_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const SHARP_SPI_TX_DMA = &SHARP_SPI_TX_DMA_REQUEST; - - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_USART3 - }, - .rx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - - -// I2C DEVICES - -static I2CBusState I2C_MAIN_BUS_STATE = {}; - -static const I2CBusHal I2C_MAIN_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_MAIN_BUS = { - .state = &I2C_MAIN_BUS_STATE, - .hal = &I2C_MAIN_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_MAIN" -}; - -extern void i2c_rail_ctl_pin(I2CBus *device, bool enable); - -static I2CBusState I2C_2V5_BUS_STATE = {}; - -static const I2CBusHal I2C_2V5_BUS_HAL = { - .i2c = I2C2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, -}; - -static const I2CBus I2C_2V5_BUS = { - .state = &I2C_2V5_BUS_STATE, - .hal = &I2C_2V5_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_I2C2 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_I2C2 - }, - .stop_mode_inhibitor = InhibitorI2C2, - .rail_gpio = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_0, - .active_high = true - }, - .rail_ctl_fn = i2c_rail_ctl_pin, - .name = "I2C_2V5" -}; - -static const I2CSlavePort I2C_SLAVE_LIS3DH = { - .bus = &I2C_MAIN_BUS, - .address = 0x32 -}; - -static const I2CSlavePort I2C_SLAVE_MFI = { - .bus = &I2C_2V5_BUS, - .address = 0x20 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_2V5_BUS, - .address = 0x1C -}; - -static const I2CSlavePort I2C_SLAVE_LED = { - .bus = &I2C_MAIN_BUS, - .address = 0xC8 -}; - -I2CSlavePort * const I2C_LIS3DH = &I2C_SLAVE_LIS3DH; -I2CSlavePort * const I2C_MFI = &I2C_SLAVE_MFI; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; -I2CSlavePort * const I2C_LED = &I2C_SLAVE_LED; - -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C2_EV, i2c_hal_event_irq_handler, &I2C_2V5_BUS); -IRQ_MAP(I2C2_ER, i2c_hal_error_irq_handler, &I2C_2V5_BUS); - - -// VOLTAGE MONITOR DEVICES -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_12, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_2, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_10, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_0, - }, -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; - -void board_early_init(void) { -} - -void board_init(void) { - i2c_init(&I2C_MAIN_BUS); - i2c_init(&I2C_2V5_BUS); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); -} diff --git a/src/fw/board/boards/board_bb2.h b/src/fw/board/boards/board_bb2.h deleted file mode 100644 index 9cf343651b..0000000000 --- a/src/fw/board/boards/board_bb2.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button.h" -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_ON - -static const BoardConfig BOARD_CONFIG = { - .ambient_light_dark_threshold = 3000, - .ambient_k_delta_threshold = 96, - .photo_en = { GPIOD, GPIO_Pin_2, true }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 11 }, - - .lcd_com = { GPIOB, GPIO_Pin_1, true }, - - .power_ctl_5v0 = { GPIOC, GPIO_Pin_5, false }, - - .backlight_on_percent = 25, - .backlight_max_duty_cycle_percent = 100, - - .power_5v0_options = OptionActiveLowOpenDrain, - - .has_mic = false, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3, { EXTI_PortSourceGPIOC, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2, { EXTI_PortSourceGPIOA, 2 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6, { EXTI_PortSourceGPIOC, 6 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1, { EXTI_PortSourceGPIOA, 1 }, GPIO_PuPd_NOPULL }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_13, - }, - .vusb_exti = { EXTI_PortSourceGPIOC, 13 }, - - .chg_stat = { GPIOH, GPIO_Pin_1 }, - .chg_fast = { GPIOB, GPIO_Pin_6 }, - .chg_en = { GPIOB, GPIO_Pin_9 }, - - .has_vusb_interrupt = true, - - .wake_on_usb_power = true, - - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 5, - .battery_capacity_hours = 144, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = true, - .shake_thresholds[AccelThresholdHigh] = 0x7f, - .shake_thresholds[AccelThresholdLow] = 0xa, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOC, 8 }, - [1] = { EXTI_PortSourceGPIOC, 9 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { // align raw mag data with accel coords (ENU) - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, - }, - .mag_int = { EXTI_PortSourceGPIOC, 4 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl, - .ctl = { GPIOB, GPIO_Pin_13, true }, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm, - .ctl = {0}, - .pwm = { - .output = { GPIOB, GPIO_Pin_5, true }, - .timer = { - .peripheral = TIM3, - .config_clock = RCC_APB1Periph_TIM3, - .init = TIM_OC2Init, - .preload = TIM_OC2PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_5, GPIO_PinSource5, GPIO_AF_TIM3 }, - }, -}; - -#define BOARD_BT_USART_IRQ_HANDLER USART1_IRQHandler -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564A, - .shutdown = { GPIOA, GPIO_Pin_3, false}, - .wakeup = { - .int_gpio = { GPIOC, GPIO_Pin_12 }, - .int_exti = { EXTI_PortSourceGPIOC, 12 }, - }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { - .spi = SPI2, - .spi_gpio = GPIOB, - .spi_clk = RCC_APB1Periph_SPI2, - .spi_clk_periph = SpiPeriphClockAPB1, - - .clk = { GPIOB, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF_SPI2 }, - .mosi = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .cs = { GPIOB, GPIO_Pin_12, true }, - - .on_ctrl = { GPIOB, GPIO_Pin_14, true }, - .on_ctrl_otype = GPIO_OType_OD, -}; - -extern DMARequest * const SHARP_SPI_TX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; - -extern I2CSlavePort * const I2C_LIS3DH; -extern I2CSlavePort * const I2C_MFI; -extern I2CSlavePort * const I2C_MAG3110; -extern I2CSlavePort * const I2C_LED; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; diff --git a/src/fw/board/boards/board_ev2_4.c b/src/fw/board/boards/board_ev2_4.c deleted file mode 100644 index f7ef759d69..0000000000 --- a/src/fw/board/boards/board_ev2_4.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/voltage_monitor.h" - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 4); // DMA1_STREAM4_DEVICE - Sharp SPI TX - -// DMA Requests - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_sharp_spi_tx_dma_request_state; -static DMARequest SHARP_SPI_TX_DMA_REQUEST = { - .state = &s_sharp_spi_tx_dma_request_state, - .stream = &DMA1_STREAM4_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const SHARP_SPI_TX_DMA = &SHARP_SPI_TX_DMA_REQUEST; - - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_USART3 - }, - .rx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - - -// I2C DEVICES - -static I2CBusState I2C_MAIN_BUS_STATE = {}; - -static const I2CBusHal I2C_MAIN_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_MAIN_BUS = { - .state = &I2C_MAIN_BUS_STATE, - .hal = &I2C_MAIN_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_MAIN" -}; - -extern void i2c_rail_ctl_pin(I2CBus *device, bool enable); - -static I2CBusState I2C_2V5_BUS_STATE = {}; - -static const I2CBusHal I2C_2V5_BUS_HAL = { - .i2c = I2C2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, -}; - -static const I2CBus I2C_2V5_BUS = { - .state = &I2C_2V5_BUS_STATE, - .hal = &I2C_2V5_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_I2C2 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_I2C2 - }, - .stop_mode_inhibitor = InhibitorI2C2, - .rail_gpio = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_0, - .active_high = true - }, - .rail_ctl_fn = i2c_rail_ctl_pin, - .name = "I2C_2V5" -}; - -static const I2CSlavePort I2C_SLAVE_LIS3DH = { - .bus = &I2C_MAIN_BUS, - .address = 0x32 -}; - -static const I2CSlavePort I2C_SLAVE_MFI = { - .bus = &I2C_2V5_BUS, - .address = 0x20 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_2V5_BUS, - .address = 0x1C -}; - -I2CSlavePort * const I2C_LIS3DH = &I2C_SLAVE_LIS3DH; -I2CSlavePort * const I2C_MFI = &I2C_SLAVE_MFI; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; - -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C2_EV, i2c_hal_event_irq_handler, &I2C_2V5_BUS); -IRQ_MAP(I2C2_ER, i2c_hal_error_irq_handler, &I2C_2V5_BUS); - - -// VOLTAGE MONITOR DEVICES -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_12, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_2, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_10, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_0, - }, -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; - -void board_early_init(void) { -} - -void board_init(void) { - i2c_init(&I2C_MAIN_BUS); - i2c_init(&I2C_2V5_BUS); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); -} diff --git a/src/fw/board/boards/board_ev2_4.h b/src/fw/board/boards/board_ev2_4.h deleted file mode 100644 index cadf0d96b0..0000000000 --- a/src/fw/board/boards/board_ev2_4.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/battery.h" -#include "drivers/button.h" -#include "drivers/imu/lis3dh/lis3dh.h" -#include "drivers/imu/mag3110/mag3110.h" -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_ON - -static const BoardConfig BOARD_CONFIG = { - .photo_en = { GPIOD, GPIO_Pin_2, true }, - .ambient_light_dark_threshold = 3180, - .ambient_k_delta_threshold = 96, - - .lcd_com = { GPIOB, GPIO_Pin_1, true }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 11 }, - - .power_5v0_options = OptionActiveLowOpenDrain, - .power_ctl_5v0 = { GPIOC, GPIO_Pin_5, false }, - - .backlight_on_percent = 25, - .backlight_max_duty_cycle_percent = 100, - - .has_mic = false, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3, { EXTI_PortSourceGPIOC, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2, { EXTI_PortSourceGPIOA, 2 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6, { EXTI_PortSourceGPIOC, 6 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1, { EXTI_PortSourceGPIOA, 1 }, GPIO_PuPd_NOPULL }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_12, - }, - .vusb_exti = { EXTI_PortSourceGPIOC, 12 }, - .chg_stat = { GPIOH, GPIO_Pin_1 }, - - .has_vusb_interrupt = true, - .charging_status_led_voltage_compensation = 0, - - .wake_on_usb_power = false, - - .low_power_threshold = 3, - .battery_capacity_hours = 144, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = true, - .shake_thresholds[AccelThresholdHigh] = 0x7f, - .shake_thresholds[AccelThresholdLow] = 0xa, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOC, 8 }, - [1] = { EXTI_PortSourceGPIOC, 9 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { // align raw mag data with accel coords (ENU) - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, - }, - .mag_int = { EXTI_PortSourceGPIOC, 4 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl, - .ctl = { GPIOB, GPIO_Pin_0, true }, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm, - .ctl = {0}, - .pwm = { - .output = { GPIOB, GPIO_Pin_5, true }, - .timer = { - .peripheral = TIM3, - .config_clock = RCC_APB1Periph_TIM3, - .init = TIM_OC2Init, - .preload = TIM_OC2PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_5, GPIO_PinSource5, GPIO_AF_TIM3 }, - }, -}; - -#define BOARD_BT_USART_IRQ_HANDLER USART1_IRQHandler -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564A, - .shutdown = { GPIOA, GPIO_Pin_3, false}, - .wakeup = { - .int_gpio = { GPIOC, GPIO_Pin_13 }, - .int_exti = { EXTI_PortSourceGPIOC, 13 }, - }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { - .spi = SPI2, - .spi_gpio = GPIOB, - .spi_clk = RCC_APB1Periph_SPI2, - .spi_clk_periph = SpiPeriphClockAPB1, - - .clk = { GPIOB, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF_SPI2 }, - .mosi = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .cs = { GPIOB, GPIO_Pin_12, true }, - - .on_ctrl = { GPIOB, GPIO_Pin_14, true }, - .on_ctrl_otype = GPIO_OType_OD, -}; - -extern DMARequest * const SHARP_SPI_TX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; - -extern I2CSlavePort * const I2C_LIS3DH; -extern I2CSlavePort * const I2C_MFI; -extern I2CSlavePort * const I2C_MAG3110; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; diff --git a/src/fw/board/boards/board_getafix.c b/src/fw/board/boards/board_getafix.c new file mode 100644 index 0000000000..627ecc65fc --- /dev/null +++ b/src/fw/board/boards/board_getafix.c @@ -0,0 +1,561 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "board/splash.h" +#include "drivers/sf32lb52/debounced_button_definitions.h" +#include "drivers/watchdog.h" +#include "system/passert.h" + +#include "bf0_hal.h" + +static UARTDeviceState s_dbg_uart_state = { + .huart = { + .Instance = USART1, + .Init = { + .BaudRate = 1000000, + .WordLength = UART_WORDLENGTH_8B, + .StopBits = UART_STOPBITS_1, + .Parity = UART_PARITY_NONE, + .HwFlowCtl = UART_HWCONTROL_NONE, + .OverSampling = UART_OVERSAMPLING_16, + }, + }, + .hdma = { + .Instance = DMA1_Channel1, + .Init = { + .Request = DMA_REQUEST_5, + .IrqPrio = 5, + }, + }, +}; + +static UARTDevice DBG_UART_DEVICE = { + .state = &s_dbg_uart_state, + .tx = { + .pad = PAD_PA19, + .func = USART1_TXD, + .flags = PIN_NOPULL, + }, + .rx = { + .pad = PAD_PA18, + .func = USART1_RXD, + .flags = PIN_PULLUP, + }, + .irqn = USART1_IRQn, + .irq_priority = 5, + .dma_irqn = DMAC1_CH1_IRQn, + .dma_irq_priority = 5, +}; + +UARTDevice *const DBG_UART = &DBG_UART_DEVICE; + +IRQ_MAP(USART1, uart_irq_handler, DBG_UART); +IRQ_MAP(DMAC1_CH1, uart_dma_irq_handler, DBG_UART); + +#ifdef NIMBLE_HCI_SF32LB52_TRACE_BINARY +static UARTDeviceState s_hci_trace_uart_state = { + .huart = { + .Instance = USART3, + .Init = { + .WordLength = UART_WORDLENGTH_8B, + .StopBits = UART_STOPBITS_1, + .Parity = UART_PARITY_NONE, + .HwFlowCtl = UART_HWCONTROL_NONE, + .OverSampling = UART_OVERSAMPLING_16, + }, + }, +}; + +static UARTDevice HCI_TRACE_UART_DEVICE = { + .state = &s_hci_trace_uart_state, + .tx = { + .pad = PAD_PA27, + .func = USART3_TXD, + .flags = PIN_NOPULL, + }, +}; +UARTDevice *const HCI_TRACE_UART = &HCI_TRACE_UART_DEVICE; +#endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY + +static QSPIPortState s_qspi_port_state = { + .cfg = { + .Instance = FLASH2, + .line = HAL_FLASH_QMODE, + .base = FLASH2_BASE_ADDR, + .msize = 32, + .SpiMode = SPI_MODE_NOR, + }, + .dma = { + .Instance = DMA1_Channel2, + .dma_irq = DMAC1_CH2_IRQn, + .request = DMA_REQUEST_1, + }, + .t_enter_deep_us = 3, + .t_exit_deep_us = 20, +}; + +static QSPIPort QSPI_PORT = { + .state = &s_qspi_port_state, + .clk_div = 0U, +}; +QSPIPort *const QSPI = &QSPI_PORT; + +static QSPIFlashState s_qspi_flash_state; +static QSPIFlash QSPI_FLASH_DEVICE = { + .state = &s_qspi_flash_state, + .qspi = &QSPI_PORT, +}; +QSPIFlash *const QSPI_FLASH = &QSPI_FLASH_DEVICE; + +static DisplayJDIState s_display_state = { + .hlcdc = { + .Instance = LCDC1, + .Init = { + .lcd_itf = LCDC_INTF_JDI_PARALLEL, + .color_mode = LCDC_PIXEL_FORMAT_RGB332, + .freq = 746268, // HCK frequency + .cfg = { + .jdi = { + .bank_col_head = 0, + .valid_columns = PBL_DISPLAY_WIDTH, + .bank_col_tail = 8, + .bank_row_head = 0, + .valid_rows = PBL_DISPLAY_HEIGHT, + .bank_row_tail = 4, + .enb_start_col = 6, + .enb_end_col = 128, + }, + }, + }, + }, +}; + +static DisplayJDIDevice s_display = { + .state = &s_display_state, + .irqn = LCDC1_IRQn, + .irq_priority = 5, + .vcom = { + .lptim = hwp_lptim2, + .freq_hz = 60U, + }, + .pinmux = { + .xrst = { + .pad = PAD_PA40, + .func = LCDC1_JDI_XRST, + .flags = PIN_NOPULL, + }, + .vst = { + .pad = PAD_PA08, + .func = LCDC1_JDI_VST, + .flags = PIN_NOPULL, + }, + .vck = { + .pad = PAD_PA39, + .func = LCDC1_JDI_VCK, + .flags = PIN_NOPULL, + }, + .enb = { + .pad = PAD_PA07, + .func = LCDC1_JDI_ENB, + .flags = PIN_NOPULL, + }, + .hst = { + .pad = PAD_PA06, + .func = LCDC1_JDI_HST, + .flags = PIN_NOPULL, + }, + .hck = { + .pad = PAD_PA41, + .func = LCDC1_JDI_HCK, + .flags = PIN_NOPULL, + }, + .r1 = { + .pad = PAD_PA05, + .func = LCDC1_JDI_R1, + .flags = PIN_NOPULL, + }, + .r2 = { + .pad = PAD_PA42, + .func = LCDC1_JDI_R2, + .flags = PIN_NOPULL, + }, + .g1 = { + .pad = PAD_PA04, + .func = LCDC1_JDI_G1, + .flags = PIN_NOPULL, + }, + .g2 = { + .pad = PAD_PA43, + .func = LCDC1_JDI_G2, + .flags = PIN_NOPULL, + }, + .b1 = { + .pad = PAD_PA03, + .func = LCDC1_JDI_B1, + .flags = PIN_NOPULL, + }, + .b2 = { + .pad = PAD_PA02, + .func = LCDC1_JDI_B2, + .flags = PIN_NOPULL, + }, + .vcom_frp = { + .pad = PAD_PA24, + .func = PBR_LPTIM2_OUT, + .flags = PIN_NOPULL, + }, + .xfrp = { + .pad = PAD_PA25, + .func = PBR_LPTIM2_INV_OUT, + .flags = PIN_NOPULL, + }, + }, + .vddp = {hwp_gpio1, 0, true}, + .vlcd = {hwp_gpio1, 9, true}, + .splash = { + .data = splash_bits, + .width = splash_width, + .height = splash_height, + }, +}; + +DisplayJDIDevice *const DISPLAY = &s_display; +IRQ_MAP(LCDC1, display_jdi_irq_handler, DISPLAY); + +const LedControllerAW9364E AW9364E = { + .gpio = { + .gpio = hwp_gpio1, + .gpio_pin = 1, + .active_high = true, + }, +}; + +static I2CBusHalState s_i2c_bus_hal_state_1 = { + .hdl = { + .Instance = I2C1, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, + }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; + +static struct I2CBusHal s_i2c_bus_hal_1 = { + .state = &s_i2c_bus_hal_state_1, + .scl = + { + .pad = PAD_PA32, + .func = I2C1_SCL, + .flags = PIN_NOPULL, + }, + .sda = + { + .pad = PAD_PA33, + .func = I2C1_SDA, + .flags = PIN_NOPULL, + }, + .module = RCC_MOD_I2C1, + .irqn = I2C1_IRQn, + .irq_priority = 5, +}; + +static I2CBusState s_i2c_bus_state_1; + +static I2CBus s_i2c_bus_1 = { + .hal = &s_i2c_bus_hal_1, + .name = "i2c1", + .state = &s_i2c_bus_state_1, + .stop_mode_inhibitor = InhibitorI2C1, +}; + +I2CBus *const I2C1_BUS = &s_i2c_bus_1; + +IRQ_MAP(I2C1, i2c_irq_handler, I2C1_BUS); + +static LIS2DW12State s_lis2dw12_state; + +static const LIS2DW12Config s_lis2dw12_config = { + .state = &s_lis2dw12_state, + .i2c = { + .bus = &s_i2c_bus_1, +#ifdef CONFIG_BOARD_GETAFIX_EVT + .address = 0x18, +#else + .address = 0x19, +#endif + }, + .int1 = { + .peripheral = hwp_gpio1, + .gpio_pin = 26, + }, + .axis_map = { + [AXIS_X] = 0, + [AXIS_Y] = 1, + [AXIS_Z] = 2, + }, + .axis_dir = { + [AXIS_X] = -1, + [AXIS_Y] = 1, + [AXIS_Z] = 1, + }, +}; + +const LIS2DW12Config *const LIS2DW12 = &s_lis2dw12_config; + +static const I2CSlavePort s_i2c_mmc5603nj = { + .bus = &s_i2c_bus_1, + .address = 0x30, +}; + +I2CSlavePort *const I2C_MMC5603NJ = &s_i2c_mmc5603nj; + +static I2CBusHalState s_i2c_bus_hal_state_2 = { + .hdl = { + .Instance = I2C2, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, + }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; + +static struct I2CBusHal s_i2c_bus_hal_2 = { + .state = &s_i2c_bus_hal_state_2, + .scl = + { + .pad = PAD_PA11, + .func = I2C2_SCL, + .flags = PIN_NOPULL, + }, + .sda = + { + .pad = PAD_PA10, + .func = I2C2_SDA, + .flags = PIN_NOPULL, + }, + .module = RCC_MOD_I2C2, + .irqn = I2C2_IRQn, + .irq_priority = 5, +}; + +static I2CBusState s_i2c_bus_state_2; + +static I2CBus s_i2c_bus_2 = { + .hal = &s_i2c_bus_hal_2, + .name = "i2c2", + .state = &s_i2c_bus_state_2, + .stop_mode_inhibitor = InhibitorI2C2, +}; + +I2CBus *const I2C2_BUS = &s_i2c_bus_2; +IRQ_MAP(I2C2, i2c_irq_handler, I2C2_BUS); + +static const I2CSlavePort s_i2c_cst816 = { + .bus = &s_i2c_bus_2, + .address = 0x15, +}; + +static const I2CSlavePort s_i2c_cst816_boot = { + .bus = &s_i2c_bus_2, + .address = 0x6A, +}; + +static const TouchSensor s_touch_cst816 = { + .i2c = &s_i2c_cst816, + .i2c_boot = &s_i2c_cst816_boot, + .int_exti = { + .peripheral = hwp_gpio1, + .gpio_pin = 38, + .pull = GPIO_PuPd_UP, + }, + .reset = { + .gpio = hwp_gpio1, + .gpio_pin = 28, + .active_high = false, + }, + .max_x = 260, + .max_y = 260, + .invert_x_axis = false, + .invert_y_axis = true, +}; + +const TouchSensor *CST816 = &s_touch_cst816; + +static I2CBusHalState s_i2c_bus_hal_state_3 = { + .hdl = { + .Instance = I2C3, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, + }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; + +static struct I2CBusHal s_i2c_bus_hal_3 = { + .state = &s_i2c_bus_hal_state_3, + .scl = + { + .pad = PAD_PA31, + .func = I2C3_SCL, + .flags = PIN_NOPULL, + }, + .sda = + { + .pad = PAD_PA30, + .func = I2C3_SDA, + .flags = PIN_NOPULL, + }, + .module = RCC_MOD_I2C3, + .irqn = I2C3_IRQn, + .irq_priority = 5, +}; + +static I2CBusState s_i2c_bus_state_3; + +static I2CBus s_i2c_bus_3 = { + .hal = &s_i2c_bus_hal_3, + .name = "i2c3", + .state = &s_i2c_bus_state_3, + .stop_mode_inhibitor = InhibitorI2C3, +}; + +I2CBus *const I2C3_BUS = &s_i2c_bus_3; +IRQ_MAP(I2C3, i2c_irq_handler, I2C3_BUS); + +static const I2CSlavePort s_i2c_npm1300 = { + .bus = &s_i2c_bus_3, + .address = 0x6B, +}; + +I2CSlavePort *const I2C_NPM1300 = &s_i2c_npm1300; + +static const I2CSlavePort s_i2c_w1160 = { + .bus = &s_i2c_bus_3, + .address = 0x48, +}; + +I2CSlavePort *const I2C_W1160 = &s_i2c_w1160; + +#ifdef CONFIG_BOARD_GETAFIX_DVT2 +static const I2CSlavePort s_i2c_aw86225 = { + .bus = &s_i2c_bus_3, + .address = 0x58, +}; + +I2CSlavePort *const I2C_AW86225 = &s_i2c_aw86225; + +static const AW86225Config s_aw86225_config = { + .lra_frequency_hz = 240, + .lra_frequency_tolerance_hz = 20, +}; + +const AW86225Config *const AW86225 = &s_aw86225_config; +#else +static const I2CSlavePort s_i2c_aw8623x = { + .bus = &s_i2c_bus_3, + .address = 0x5a, +}; + +I2CSlavePort *const I2C_AW8623X = &s_i2c_aw8623x; +#endif + +const BoardConfigActuator BOARD_CONFIG_VIBE = { + .ctl = {hwp_gpio1, 20, false}, +}; + +const Npm1300Config NPM1300_CONFIG = { + // 70mA = 1C (max limit from datasheet) + .chg_current_ma = 70, + .dischg_limit_ma = 200, + .term_current_pct = 10, + .thermistor_beta = 3380, + .ntc_hot_celsius = 45, + .vbus_current_lim0 = 500, + .vbus_current_startup = 500, +}; + +const BoardConfigPower BOARD_CONFIG_POWER = { + .pmic_int = { + .peripheral = hwp_gpio1, + .gpio_pin = 44, + }, + .low_power_threshold = 5U, + .battery_capacity_hours = 150U, +}; + +const BoardConfig BOARD_CONFIG = { + .backlight_on_percent = 25, + .ambient_light_dark_threshold = 150, + .ambient_k_delta_threshold = 25, +}; + +const BoardConfigButton BOARD_CONFIG_BUTTON = { + .buttons = { + [BUTTON_ID_BACK] = { "Back", hwp_gpio1, 34, GPIO_PuPd_NOPULL, true }, + [BUTTON_ID_UP] = { "Up", hwp_gpio1, 35, GPIO_PuPd_UP, false}, + [BUTTON_ID_SELECT] = { "Select", hwp_gpio1, 36, GPIO_PuPd_UP, false}, + [BUTTON_ID_DOWN] = { "Down", hwp_gpio1, 37, GPIO_PuPd_UP, false}, + }, + .timer = GPTIM2, + .timer_irqn = GPTIM2_IRQn, +}; +IRQ_MAP(GPTIM2, debounced_button_irq_handler, GPTIM2); + +static MicDeviceState mic_state = { + .hdma = { + .Instance = DMA1_Channel5, + .Init = { + .Request = DMA_REQUEST_36, + .IrqPrio = 5, + }, + }, +}; +static const MicDevice mic_device = { + .state = &mic_state, + .pdm_instance = hwp_pdm1, + .clk_gpio = { + .pad = PAD_PA22, + .func = PDM1_CLK, + .flags = PIN_NOPULL, + }, + .data_gpio = { + .pad = PAD_PA23, + .func = PDM1_DATA, + .flags = PIN_PULLDOWN, + }, + .pdm_dma_irq = DMAC1_CH5_IRQn, + .pdm_irq = PDM1_IRQn, + .pdm_irq_priority = 5, + .channels = 1, + .sample_rate = 16000, + .channel_depth = 16, +}; +const MicDevice* MIC = &mic_device; +IRQ_MAP(PDM1, pdm1_data_handler, MIC); +IRQ_MAP(DMAC1_CH5, pdm1_l_dma_handler, MIC); + +uint32_t BSP_GetOtpBase(void) { + return MPI2_MEM_BASE; +} + +void board_early_init(void) { + +} + +void board_init(void) { + i2c_init(I2C1_BUS); + i2c_init(I2C2_BUS); + i2c_init(I2C3_BUS); + + mic_init(MIC); +} diff --git a/src/fw/board/boards/board_getafix.h b/src/fw/board/boards/board_getafix.h new file mode 100644 index 0000000000..d683e4ca8e --- /dev/null +++ b/src/fw/board/boards/board_getafix.h @@ -0,0 +1,62 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "drivers/imu/lis2dw12/lis2dw12.h" +#include "drivers/backlight/aw9364e.h" +#include "drivers/pmic/npm1300.h" +#include "drivers/vibe/vibe_aw86225.h" +#include "drivers/touch/cst816/touch_sensor_definitions.h" +#include "pbl/services/imu/units.h" + +#define BT_VENDOR_ID 0x0EEA +#define BT_VENDOR_NAME "Core Devices LLC" + +extern UARTDevice * const DBG_UART; +#ifdef NIMBLE_HCI_SF32LB52_TRACE_BINARY +extern UARTDevice * const HCI_TRACE_UART; +#endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY +extern QSPIPort * const QSPI; +extern QSPIFlash * const QSPI_FLASH; +extern I2CBus *const I2C1_BUS; +extern I2CBus *const I2C2_BUS; +extern I2CBus *const I2C3_BUS; +extern const LIS2DW12Config *const LIS2DW12; +extern I2CSlavePort * const I2C_MMC5603NJ; +extern I2CSlavePort * const I2C_NPM1300; +extern I2CSlavePort *const I2C_W1160; +#ifdef CONFIG_BOARD_GETAFIX_DVT2 +extern I2CSlavePort *const I2C_AW86225; +extern const AW86225Config *const AW86225; +#else +extern I2CSlavePort *const I2C_AW8623X; +#endif +extern const Npm1300Config NPM1300_CONFIG; +extern DisplayJDIDevice *const DISPLAY; +extern const BoardConfigPower BOARD_CONFIG_POWER; +extern const BoardConfig BOARD_CONFIG; +extern const BoardConfigButton BOARD_CONFIG_BUTTON; +extern const MicDevice* MIC; +extern const TouchSensor *CST816; +extern const LedControllerAW9364E AW9364E; + +extern const BoardConfigActuator BOARD_CONFIG_VIBE; + +static const BoardConfigAccel BOARD_CONFIG_ACCEL = { + .accel_config = { + .default_motion_sensitivity = 55U, // Medium + }, +}; + +static const BoardConfigMag BOARD_CONFIG_MAG = { + // TODO(GETAFIX): Review if correct + .mag_config = { + .axes_offsets[AXIS_X] = 1, + .axes_offsets[AXIS_Y] = 0, + .axes_offsets[AXIS_Z] = 2, + .axes_inverts[AXIS_X] = false, + .axes_inverts[AXIS_Y] = true, + .axes_inverts[AXIS_Z] = false, + }, +}; diff --git a/src/fw/board/boards/board_obelix.c b/src/fw/board/boards/board_obelix.c index b3c873198d..5a5e84239f 100644 --- a/src/fw/board/boards/board_obelix.c +++ b/src/fw/board/boards/board_obelix.c @@ -1,32 +1,16 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "bf0_hal.h" -#include "bf0_hal_efuse.h" -#include "bf0_hal_hlp.h" -#include "bf0_hal_lcpu_config.h" -#include "bf0_hal_pmu.h" -#include "bf0_hal_rcc.h" #include "board/board.h" +#include "board/display.h" +#include "board/splash.h" +#include "drivers/backlight.h" +#include "drivers/pmic/npm1300.h" #include "drivers/sf32lb52/debounced_button_definitions.h" -#include "drivers/stubs/hrm.h" +#include "drivers/hrm/gh3x2x.h" #include "system/passert.h" - -#define HCPU_FREQ_MHZ 240 +#include "bf0_hal.h" static UARTDeviceState s_dbg_uart_state = { .huart = { @@ -72,81 +56,26 @@ UARTDevice *const DBG_UART = &DBG_UART_DEVICE; IRQ_MAP(USART1, uart_irq_handler, DBG_UART); IRQ_MAP(DMAC1_CH1, uart_dma_irq_handler, DBG_UART); -static PwmState s_pwm1_ch1_state = { - .handle = { - .Instance = hwp_gptim1, - .Init = { - .CounterMode = GPT_COUNTERMODE_UP, - }, - - }, - .clock_config = { - .ClockSource = GPT_CLOCKSOURCE_INTERNAL, - }, - .channel = 1, -}; - -static PwmState s_pwm1_ch2_state = { - .handle = { - .Instance = hwp_gptim1, - .Init = { - .CounterMode = GPT_COUNTERMODE_UP, - }, - - }, - .clock_config = { - .ClockSource = GPT_CLOCKSOURCE_INTERNAL, - }, - .channel = 2, -}; - -static PwmState s_pwm1_ch3_state = { - .handle = { - .Instance = hwp_gptim1, - .Init = { - .CounterMode = GPT_COUNTERMODE_UP, - }, - - }, - .clock_config = { - .ClockSource = GPT_CLOCKSOURCE_INTERNAL, - }, - .channel = 3, -}; - -const LedControllerPwm LED_CONTROLLER_PWM = { - .pwm = { - [0] = { - .pwm_pin = { - .pad = PAD_PA28, - .func = GPTIM1_CH1, - .flags = PIN_NOPULL, - }, - .state = &s_pwm1_ch1_state, - }, - [1] = { - .pwm_pin = { - .pad = PAD_PA29, - .func = GPTIM1_CH2, - .flags = PIN_NOPULL, - }, - .state = &s_pwm1_ch2_state, - }, - [2] = { - .pwm_pin = { - .pad = PAD_PA44, - .func = GPTIM1_CH3, - .flags = PIN_NOPULL, - }, - .state = &s_pwm1_ch3_state, - }, - }, - .initial_color = LED_WARM_WHITE, -}; - static DisplayJDIState s_display_state = { .hlcdc = { .Instance = LCDC1, + .Init = { + .lcd_itf = LCDC_INTF_JDI_PARALLEL, + .color_mode = LCDC_PIXEL_FORMAT_RGB332, + .freq = 746268, // HCK frequency + .cfg = { + .jdi = { + .bank_col_head = 2, + .valid_columns = PBL_DISPLAY_WIDTH, + .bank_col_tail = 6, + .bank_row_head = 0, + .valid_rows = PBL_DISPLAY_HEIGHT, + .bank_row_tail = 6, + .enb_start_col = 3, + .enb_end_col = 99, + }, + }, + }, }, }; @@ -219,21 +148,28 @@ static DisplayJDIDevice s_display = { .func = LCDC1_JDI_B2, .flags = PIN_NOPULL, }, - .vcom = { + .vcom_frp = { .pad = PAD_PA24, - .func = GPIO_A24, + .func = PBR_LPTIM2_OUT, .flags = PIN_NOPULL, }, - .va = { + .xfrp = { .pad = PAD_PA25, - .func = GPIO_A25, + .func = PBR_LPTIM2_INV_OUT, .flags = PIN_NOPULL, }, }, + .vddp = {hwp_gpio1, 28, true}, + .vlcd = {hwp_gpio1, 29, false}, + .splash = { + .data = splash_bits, + .width = splash_width, + .height = splash_height, + }, }; DisplayJDIDevice *const DISPLAY = &s_display; -IRQ_MAP(LCDC1, jdi_lpm015m135a_irq_handler, DISPLAY); +IRQ_MAP(LCDC1, display_jdi_irq_handler, DISPLAY); #ifdef NIMBLE_HCI_SF32LB52_TRACE_BINARY static UARTDeviceState s_hci_trace_uart_state = { @@ -260,9 +196,7 @@ static UARTDevice HCI_TRACE_UART_DEVICE = { UARTDevice *const HCI_TRACE_UART = &HCI_TRACE_UART_DEVICE; #endif // NIMBLE_HCI_SF32LB52_TRACE_BINARY -static QSPIPortState s_qspi_port_state; -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, +static QSPIPortState s_qspi_port_state = { .cfg = { .Instance = FLASH2, .line = HAL_FLASH_QMODE, @@ -270,12 +204,18 @@ static QSPIPort QSPI_PORT = { .msize = 16, .SpiMode = SPI_MODE_NOR, }, - .clk_div = 5U, .dma = { .Instance = DMA1_Channel2, .dma_irq = DMAC1_CH2_IRQn, .request = DMA_REQUEST_1, }, + .t_enter_deep_us = 3, + .t_exit_deep_us = 20, +}; + +static QSPIPort QSPI_PORT = { + .state = &s_qspi_port_state, + .clk_div = 0U, }; QSPIPort *const QSPI = &QSPI_PORT; @@ -286,25 +226,21 @@ static QSPIFlash QSPI_FLASH_DEVICE = { }; QSPIFlash *const QSPI_FLASH = &QSPI_FLASH_DEVICE; -static I2CDeviceState s_i2c_device_state_1 = { - .int_enabled = true, -}; - -static struct I2CBusHal s_i2c_bus_hal_1 = { - .i2c_state = &s_i2c_device_state_1, - .hi2c = - { - .Instance = I2C1, - .Init = { - .AddressingMode = I2C_ADDRESSINGMODE_7BIT, - .ClockSpeed = 400000, - .GeneralCallMode = I2C_GENERALCALL_DISABLE, - }, - .Mode = HAL_I2C_MODE_MASTER, - +static I2CBusHalState s_i2c_bus_hal_state_1 = { + .hdl = { + .Instance = I2C1, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; - .device_name = "i2c1", +static I2CBusHal s_i2c_bus_hal_1 = { + .state = &s_i2c_bus_hal_state_1, .scl = { .pad = PAD_PA31, @@ -317,11 +253,9 @@ static struct I2CBusHal s_i2c_bus_hal_1 = { .func = I2C1_SDA, .flags = PIN_NOPULL, }, - .core = CORE_ID_HCPU, .module = RCC_MOD_I2C1, .irqn = I2C1_IRQn, .irq_priority = 5, - .timeout = 5000, }; static I2CBusState s_i2c_bus_state_1; @@ -329,6 +263,7 @@ static I2CBusState s_i2c_bus_state_1; static I2CBus s_i2c_bus_1 = { .hal = &s_i2c_bus_hal_1, .state = &s_i2c_bus_state_1, + .name = "i2c1", .stop_mode_inhibitor = InhibitorI2C1, }; @@ -336,25 +271,49 @@ I2CBus *const I2C1_BUS = &s_i2c_bus_1; IRQ_MAP(I2C1, i2c_irq_handler, I2C1_BUS); -static I2CDeviceState s_i2c_device_state_2 = { - .int_enabled = true, +static const I2CSlavePort s_i2c_npm1300 = { + .bus = &s_i2c_bus_1, + .address = 0x6B, }; -static struct I2CBusHal s_i2c_bus_hal_2 = { - .i2c_state = &s_i2c_device_state_2, - .hi2c = - { - .Instance = I2C2, - .Init = { - .AddressingMode = I2C_ADDRESSINGMODE_7BIT, - .ClockSpeed = 400000, - .GeneralCallMode = I2C_GENERALCALL_DISABLE, - }, - .Mode = HAL_I2C_MODE_MASTER, +I2CSlavePort *const I2C_NPM1300 = &s_i2c_npm1300; + +static const I2CSlavePort s_i2c_aw86225 = { + .bus = &s_i2c_bus_1, + .address = 0x58, +}; + +I2CSlavePort *const I2C_AW86225 = &s_i2c_aw86225; + +static const AW86225Config s_aw86225_config = { + .lra_frequency_hz = 240, + .lra_frequency_tolerance_hz = 20, +}; + +const AW86225Config *const AW86225 = &s_aw86225_config; + +static const I2CSlavePort s_i2c_aw2016 = { + .bus = &s_i2c_bus_1, + .address = 0x64, +}; +I2CSlavePort *const I2C_AW2016 = &s_i2c_aw2016; + +static I2CBusHalState s_i2c_bus_hal_state_2 = { + .hdl = { + .Instance = I2C2, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; - .device_name = "i2c2", +static I2CBusHal s_i2c_bus_hal_2 = { + .state = &s_i2c_bus_hal_state_2, .scl = { .pad = PAD_PA32, @@ -367,11 +326,9 @@ static struct I2CBusHal s_i2c_bus_hal_2 = { .func = I2C2_SDA, .flags = PIN_NOPULL, }, - .core = CORE_ID_HCPU, .module = RCC_MOD_I2C2, .irqn = I2C2_IRQn, .irq_priority = 5, - .timeout = 5000, }; static I2CBusState s_i2c_bus_state_2; @@ -379,6 +336,7 @@ static I2CBusState s_i2c_bus_state_2; static I2CBus s_i2c_bus_2 = { .hal = &s_i2c_bus_hal_2, .state = &s_i2c_bus_state_2, + .name = "i2c2", .stop_mode_inhibitor = InhibitorI2C2, }; @@ -386,12 +344,55 @@ I2CBus *const I2C2_BUS = &s_i2c_bus_2; IRQ_MAP(I2C2, i2c_irq_handler, I2C2_BUS); -static const I2CSlavePort s_i2c_lsm6d = { +static LIS2DW12State s_lis2dw12_state; + +static const LIS2DW12Config s_lis2dw12_config = { + .state = &s_lis2dw12_state, + .i2c = { + .bus = &s_i2c_bus_2, +#if defined(CONFIG_BOARD_OBELIX_DVT) || defined(CONFIG_BOARD_OBELIX_BB2) + .address = 0x18, +#else + .address = 0x19, +#endif + }, + .int1 = { + .peripheral = hwp_gpio1, + .gpio_pin = 38, + }, +#ifdef CONFIG_IS_BIGBOARD + .axis_map = { + [AXIS_X] = 0, + [AXIS_Y] = 1, + [AXIS_Z] = 2, + }, + .axis_dir = { + [AXIS_X] = -1, + [AXIS_Y] = -1, + [AXIS_Z] = 1, + }, +#else + .axis_map = { + [AXIS_X] = 1, + [AXIS_Y] = 0, + [AXIS_Z] = 2, + }, + .axis_dir = { + [AXIS_X] = -1, + [AXIS_Y] = 1, + [AXIS_Z] = -1, + }, +#endif +}; + +const LIS2DW12Config *const LIS2DW12 = &s_lis2dw12_config; + +static const I2CSlavePort s_i2c_lsm6dso = { .bus = &s_i2c_bus_2, - .address = 0x6A, + .address = 0x6a, }; -I2CSlavePort *const I2C_LSM6D = &s_i2c_lsm6d; +I2CSlavePort *const I2C_LSM6DSO = &s_i2c_lsm6dso; static const I2CSlavePort s_i2c_mmc5603nj = { .bus = &s_i2c_bus_2, @@ -400,47 +401,21 @@ static const I2CSlavePort s_i2c_mmc5603nj = { I2CSlavePort *const I2C_MMC5603NJ = &s_i2c_mmc5603nj; -static const I2CSlavePort s_i2c_npm1300 = { - .bus = &s_i2c_bus_1, - .address = 0x6B, -}; - -I2CSlavePort *const I2C_NPM1300 = &s_i2c_npm1300; - -static const I2CSlavePort s_i2c_aw86225 = { - .bus = &s_i2c_bus_1, - .address = 0x58, -}; - -I2CSlavePort *const I2C_AW86225 = &s_i2c_aw86225; - -static HRMDeviceState s_hrm_state; -static HRMDevice s_hrm = { - .state = &s_hrm_state, -}; -HRMDevice * const HRM = &s_hrm; - -const BoardConfigActuator BOARD_CONFIG_VIBE = { - .ctl = {hwp_gpio1, 1, true}, -}; - -static I2CDeviceState s_i2c_device_state_3; - -static struct I2CBusHal s_i2c_bus_hal_3 = { - .i2c_state = &s_i2c_device_state_3, - .hi2c = - { - .Instance = I2C3, - .Init = { - .AddressingMode = I2C_ADDRESSINGMODE_7BIT, - .ClockSpeed = 400000, - .GeneralCallMode = I2C_GENERALCALL_DISABLE, - }, - .Mode = HAL_I2C_MODE_MASTER, - +static I2CBusHalState s_i2c_bus_hal_state_3 = { + .hdl = { + .Instance = I2C3, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; - .device_name = "i2c3", +static I2CBusHal s_i2c_bus_hal_3 = { + .state = &s_i2c_bus_hal_state_3, .scl = { .pad = PAD_PA11, @@ -453,11 +428,9 @@ static struct I2CBusHal s_i2c_bus_hal_3 = { .func = I2C3_SDA, .flags = PIN_NOPULL, }, - .core = CORE_ID_HCPU, .module = RCC_MOD_I2C3, .irqn = I2C3_IRQn, .irq_priority = 5, - .timeout = 5000, }; static I2CBusState s_i2c_bus_state_3; @@ -465,19 +438,21 @@ static I2CBusState s_i2c_bus_state_3; static I2CBus s_i2c_bus_3 = { .hal = &s_i2c_bus_hal_3, .state = &s_i2c_bus_state_3, + .name = "i2c3", .stop_mode_inhibitor = InhibitorI2C3, }; I2CBus *const I2C3_BUS = &s_i2c_bus_3; + IRQ_MAP(I2C3, i2c_irq_handler, I2C3_BUS); static const I2CSlavePort s_i2c_cst816 = { - .bus = I2C3_BUS, + .bus = &s_i2c_bus_3, .address = 0x15, }; static const I2CSlavePort s_i2c_cst816_boot = { - .bus = I2C3_BUS, + .bus = &s_i2c_bus_3, .address = 0x6A, }; @@ -487,17 +462,90 @@ static const TouchSensor touch_cst816 = { .int_exti = { .peripheral = hwp_gpio1, .gpio_pin = 27, + .pull = GPIO_PuPd_UP, }, }; + const TouchSensor *CST816 = &touch_cst816; +static I2CBusHalState s_i2c_bus_hal_state_4 = { + .hdl = { + .Instance = I2C4, + .Init = { + .AddressingMode = I2C_ADDRESSINGMODE_7BIT, + .ClockSpeed = 400000, + .GeneralCallMode = I2C_GENERALCALL_DISABLE, + }, + .Mode = HAL_I2C_MODE_MASTER, + .core = CORE_ID_HCPU, + }, +}; + +static I2CBusHal s_i2c_bus_hal_4 = { + .state = &s_i2c_bus_hal_state_4, + .scl = + { + .pad = PAD_PA09, + .func = I2C4_SCL, + .flags = PIN_NOPULL, + }, + .sda = + { + .pad = PAD_PA20, + .func = I2C4_SDA, + .flags = PIN_NOPULL, + }, + .module = RCC_MOD_I2C4, + .irqn = I2C4_IRQn, + .irq_priority = 5, +}; + +static I2CBusState s_i2c_bus_state_4; + +static I2CBus s_i2c_bus_4 = { + .hal = &s_i2c_bus_hal_4, + .state = &s_i2c_bus_state_4, + .name = "i2c4", + .stop_mode_inhibitor = InhibitorI2C4, +}; + +I2CBus *const I2C4_BUS = &s_i2c_bus_4; + +IRQ_MAP(I2C4, i2c_irq_handler, I2C4_BUS); + +static const I2CSlavePort s_i2c_gh3x2x = { + .bus = &s_i2c_bus_4, + .address = 0x14, +}; + +static HRMDeviceState s_hrm_state; +static HRMDevice s_hrm = { + .state = &s_hrm_state, + .i2c = &s_i2c_gh3x2x, + .int_exti = { + .peripheral = hwp_gpio1, + .gpio_pin = 44, + }, + .int_input = { + .gpio = hwp_gpio1, + .gpio_pin = 44, + }, +}; + +HRMDevice * const HRM = &s_hrm; + +const BoardConfigActuator BOARD_CONFIG_VIBE = { + .ctl = {hwp_gpio1, 1, false}, +}; + // TODO(OBELIX): Adjust to final battery parameters const Npm1300Config NPM1300_CONFIG = { - // 128mA = ~1C (rapid charge) - .chg_current_ma = 128, + // 190mA = 1C (rapid charge, max limit from datasheet) + .chg_current_ma = 190, .dischg_limit_ma = 200, .term_current_pct = 10, .thermistor_beta = 3380, + .ntc_hot_celsius = 45, .vbus_current_lim0 = 500, .vbus_current_startup = 500, }; @@ -514,35 +562,29 @@ const BoardConfigPower BOARD_CONFIG_POWER = { .peripheral = hwp_gpio1, .gpio_pin = 26, }, - .low_power_threshold = 5U, - .battery_capacity_hours = 100U, + .low_power_threshold = 4U, + .battery_capacity_hours = 400U, }; const BoardConfig BOARD_CONFIG = { - .backlight_on_percent = 25, + .backlight_on_percent = 45, .ambient_light_dark_threshold = 150, .ambient_k_delta_threshold = 25, + .dynamic_backlight_min_threshold = 5, + .backlight_default_color = BACKLIGHT_COLOR_WARM_WHITE, }; const BoardConfigButton BOARD_CONFIG_BUTTON = { .buttons = { [BUTTON_ID_BACK] = { "Back", hwp_gpio1, 34, GPIO_PuPd_NOPULL, true }, -#ifdef IS_BIGBOARD - [BUTTON_ID_UP] = { "Up", hwp_gpio1, 37, GPIO_PuPd_UP, false}, -#else [BUTTON_ID_UP] = { "Up", hwp_gpio1, 35, GPIO_PuPd_UP, false}, -#endif [BUTTON_ID_SELECT] = { "Select", hwp_gpio1, 36, GPIO_PuPd_UP, false}, -#ifdef IS_BIGBOARD - [BUTTON_ID_DOWN] = { "Down", hwp_gpio1, 35, GPIO_PuPd_UP, false}, -#else [BUTTON_ID_DOWN] = { "Down", hwp_gpio1, 37, GPIO_PuPd_UP, false}, -#endif }, - .timer = GPTIM1, - .timer_irqn = GPTIM1_IRQn, + .timer = GPTIM2, + .timer_irqn = GPTIM2_IRQn, }; -IRQ_MAP(GPTIM1, debounced_button_irq_handler, GPTIM1); +IRQ_MAP(GPTIM2, debounced_button_irq_handler, GPTIM2); static MicDeviceState mic_state = { .hdma = { @@ -559,7 +601,7 @@ static const MicDevice mic_device = { .clk_gpio = { .pad = PAD_PA22, .func = PDM1_CLK, - .flags = PIN_NOPULL, + .flags = PIN_NOPULL, }, .data_gpio = { .pad = PAD_PA23, @@ -569,7 +611,7 @@ static const MicDevice mic_device = { .pdm_dma_irq = DMAC1_CH5_IRQn, .pdm_irq = PDM1_IRQn, .pdm_irq_priority = 5, - .channels = 2, + .channels = 1, .sample_rate = 16000, .channel_depth = 16, }; @@ -577,76 +619,54 @@ const MicDevice* MIC = &mic_device; IRQ_MAP(PDM1, pdm1_data_handler, MIC); IRQ_MAP(DMAC1_CH5, pdm1_l_dma_handler, MIC); -uint32_t BSP_GetOtpBase(void) { - return MPI2_MEM_BASE; +static void prv_audio_power_up(void) { + NPM1300_OPS.dischg_limit_ma_set(NPM1300_DISCHG_LIMIT_MA_MAX); } -void board_early_init(void) { - HAL_StatusTypeDef ret; - uint32_t bootopt; - - // Adjust bootrom pull-up/down delays on PA21 (flash power control pin) so - // that the flash is properly power cycled on reset. A flash power cycle is - // needed if left in 4-byte addressing mode, as bootrom does not support it. - bootopt = HAL_Get_backup(RTC_BACKUP_BOOTOPT); - bootopt &= ~(RTC_BACKUP_BOOTOPT_PD_DELAY_Msk | RTC_BACKUP_BOOTOPT_PU_DELAY_Msk); - bootopt |= RTC_BACKUP_BOOTOPT_PD_DELAY_MS(100) | RTC_BACKUP_BOOTOPT_PU_DELAY_MS(10); - HAL_Set_backup(RTC_BACKUP_BOOTOPT, bootopt); - - if (HAL_RCC_HCPU_GetClockSrc(RCC_CLK_MOD_SYS) == RCC_SYSCLK_HRC48) { - HAL_HPAON_EnableXT48(); - HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_SYS, RCC_SYSCLK_HXT48); - } - - HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_HP_PERI, RCC_CLK_PERI_HXT48); - - // Halt LCPU first to avoid LCPU in running state - HAL_HPAON_WakeCore(CORE_ID_LCPU); - HAL_RCC_Reset_and_Halt_LCPU(1); - - // Load system configuration from EFUSE - BSP_System_Config(); - - HAL_HPAON_StartGTimer(); -#ifdef SF32LB52_USE_LXT - HAL_PMU_EnableRC32K(1); - - HAL_PMU_LpCLockSelect(PMU_LPCLK_RC32); -#else - HAL_PMU_LpCLockSelect(PMU_LPCLK_RC10); -#endif - HAL_PMU_EnableDLL(1); -#ifdef SF32LB52_USE_LXT - HAL_PMU_EnableXTAL32(); - ret = HAL_PMU_LXTReady(); - PBL_ASSERTN(ret == HAL_OK); - - HAL_RTC_ENABLE_LXT(); -#endif - - HAL_RCC_LCPU_ClockSelect(RCC_CLK_MOD_LP_PERI, RCC_CLK_PERI_HXT48); - - HAL_HPAON_CANCEL_LP_ACTIVE_REQUEST(); +static void prv_audio_power_down(void) { + NPM1300_OPS.dischg_limit_ma_set(NPM1300_CONFIG.dischg_limit_ma); +} - HAL_RCC_HCPU_ConfigHCLK(HCPU_FREQ_MHZ); +static const BoardPowerOps prv_audio_power_ops = { + .power_up = prv_audio_power_up, + .power_down = prv_audio_power_down, +}; - // Reset sysclk used by HAL_Delay_us - HAL_Delay_us(0); +static AudioDeviceState audio_state; +static const AudioDevice audio_device = { + .state = &audio_state, + .irq_priority = 5, + .channels = 1, + .samplerate = 16000, + .data_format = 16, + .audec_dma_irq = DMAC1_CH4_IRQn, + .audec_dma_channel = DMA1_Channel4, + .audec_dma_request = DMA_REQUEST_41, + + .pa_ctrl = { + .gpio = hwp_gpio1, + .gpio_pin = 0, + .active_high =true, + }, + .power_ops = &prv_audio_power_ops, +}; +const AudioDevice* AUDIO = &audio_device; +IRQ_MAP(DMAC1_CH4, audec_dac0_dma_irq_handler, AUDIO); - ret = HAL_RCC_CalibrateRC48(); - PBL_ASSERTN(ret == HAL_OK); +uint32_t BSP_GetOtpBase(void) { + return MPI2_MEM_BASE; +} - HAL_RCC_Init(); - HAL_PMU_Init(); +void board_early_init(void) { - __HAL_SYSCFG_CLEAR_SECURITY(); - HAL_EFUSE_Init(); } void board_init(void) { i2c_init(I2C1_BUS); i2c_init(I2C2_BUS); i2c_init(I2C3_BUS); + i2c_init(I2C4_BUS); mic_init(MIC); + audio_init(AUDIO); } diff --git a/src/fw/board/boards/board_obelix.h b/src/fw/board/boards/board_obelix.h index 1f316bd39d..7e5b65a225 100644 --- a/src/fw/board/boards/board_obelix.h +++ b/src/fw/board/boards/board_obelix.h @@ -1,25 +1,13 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "drivers/led_controller/pwm.h" +#include "drivers/imu/lis2dw12/lis2dw12.h" #include "drivers/pmic/npm1300.h" +#include "drivers/vibe/vibe_aw86225.h" #include "drivers/touch/cst816/touch_sensor_definitions.h" -#include "services/imu/units.h" +#include "pbl/services/imu/units.h" #define BT_VENDOR_ID 0x0EEA #define BT_VENDOR_NAME "Core Devices LLC" @@ -32,14 +20,16 @@ extern QSPIPort * const QSPI; extern QSPIFlash * const QSPI_FLASH; extern I2CBus *const I2C1_BUS; extern I2CBus *const I2C2_BUS; -extern I2CSlavePort * const I2C_LSM6D; +extern const LIS2DW12Config *const LIS2DW12; +extern I2CSlavePort *const I2C_LSM6DSO; extern I2CSlavePort * const I2C_MMC5603NJ; extern I2CSlavePort * const I2C_NPM1300; extern I2CSlavePort *const I2C_AW86225; +extern const AW86225Config *const AW86225; extern I2CSlavePort *const I2C_W1160; +extern I2CSlavePort *const I2C_AW2016; extern const Npm1300Config NPM1300_CONFIG; extern const BoardConfigActuator BOARD_CONFIG_VIBE; -extern const LedControllerPwm LED_CONTROLLER_PWM; extern PwmConfig *const PWM1_CH1; extern DisplayJDIDevice *const DISPLAY; extern const BoardConfigPower BOARD_CONFIG_POWER; @@ -48,54 +38,17 @@ extern const BoardConfigButton BOARD_CONFIG_BUTTON; extern const MicDevice* MIC; extern HRMDevice * const HRM; extern const TouchSensor *CST816; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_IssiI2C, -}; +extern const AudioDevice* AUDIO; static const BoardConfigAccel BOARD_CONFIG_ACCEL = { .accel_config = { -#ifdef IS_BIGBOARD - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = false, -#else - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, -#endif - // TODO(OBELIX): Needs calibration - .shake_thresholds[AccelThresholdHigh] = 64U, - .shake_thresholds[AccelThresholdLow] = 15U, - .double_tap_threshold = 12500U, - // LSM6DSO tap timing register values tuned for reliable double-tap: - // tap_shock (0-3): maximum duration (in ODR steps) where an over-threshold event is still - // considered a tap. Higher tolerates longer impacts. 3 = ~max; - // tap_quiet (0-3): quiet time after first tap during which accel must stay below threshold - // before second tap; balances rejection of long impacts vs responsiveness. 2 is moderate. - // tap_dur (0-15): maximum interval (in ODR steps) between first and second tap. 8 chosen to - // allow natural user double taps without allowing widely spaced taps. - .tap_shock = 0x03U, - .tap_quiet = 0x02U, - .tap_dur = 0x08U, - }, - .accel_int_gpios = { - [0] = { .gpio = hwp_gpio1, .gpio_pin = 38 }, - }, - .accel_ints = { - [0] = { .peripheral = hwp_gpio1, .gpio_pin = 38 }, + .default_motion_sensitivity = 55U, // Medium }, }; static const BoardConfigMag BOARD_CONFIG_MAG = { .mag_config = { -#ifdef IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD .axes_offsets[AXIS_X] = 1, .axes_offsets[AXIS_Y] = 0, .axes_offsets[AXIS_Z] = 2, @@ -111,4 +64,4 @@ static const BoardConfigMag BOARD_CONFIG_MAG = { .axes_inverts[AXIS_Z] = false, #endif }, -}; \ No newline at end of file +}; diff --git a/src/fw/board/boards/board_qemu_emery.c b/src/fw/board/boards/board_qemu_emery.c new file mode 100644 index 0000000000..a5df0ac3dc --- /dev/null +++ b/src/fw/board/boards/board_qemu_emery.c @@ -0,0 +1,101 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" + +// UART device for debug serial +#include "drivers/uart/qemu.h" +#include "drivers/speaker/qemu/audio.h" +#include "drivers/backlight.h" +#include "drivers/mic/qemu/mic_definitions.h" + +static UARTDeviceState s_dbg_uart_state = {}; + +static struct UARTDevice DBG_UART_DEVICE = { + .state = &s_dbg_uart_state, + .base_addr = QEMU_UART2_BASE, + .irqn = UART2_IRQn, + .irq_priority = 5, +}; + +UARTDevice *const DBG_UART = (UARTDevice *)&DBG_UART_DEVICE; + +// QEMU control protocol UART (UART1) +static UARTDeviceState s_qemu_uart_state = {}; + +static struct UARTDevice QEMU_UART_DEVICE = { + .state = &s_qemu_uart_state, + .base_addr = QEMU_UART1_BASE, + .irqn = UART1_IRQn, + .irq_priority = 6, +}; + +UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; + +// Display device - QEMU framebuffer at DISPLAY_BASE + 0x1000 +static QemuDisplayDevice s_display = { + .base_addr = QEMU_DISPLAY_BASE, + .fb_addr = QEMU_DISPLAY_FB_BASE, + .width = 200, + .height = 228, + .bpp = 8, + .irqn = DISPLAY_IRQn, + .irq_priority = 5, +}; + +DisplayDevice *const DISPLAY = &s_display; + +const BoardConfigActuator BOARD_CONFIG_VIBE = { + .options = 0, +}; + +const BoardConfigPower BOARD_CONFIG_POWER = { + .low_power_threshold = 2U, + .battery_capacity_hours = 168U, +}; + +const BoardConfig BOARD_CONFIG = { + .backlight_on_percent = 100, + .ambient_light_dark_threshold = 150, + .ambient_k_delta_threshold = 25, + .backlight_default_color = BACKLIGHT_COLOR_WHITE, +}; + +const BoardConfigButton BOARD_CONFIG_BUTTON = { + .buttons = { + [BUTTON_ID_BACK] = { "Back", NULL, 0, GPIO_PuPd_NOPULL, true }, + [BUTTON_ID_UP] = { "Up", NULL, 1, GPIO_PuPd_UP, false }, + [BUTTON_ID_SELECT] = { "Select", NULL, 2, GPIO_PuPd_UP, false }, + [BUTTON_ID_DOWN] = { "Down", NULL, 3, GPIO_PuPd_UP, false }, + }, + .timer = NULL, + .timer_irqn = TIMER0_IRQn, +}; + +// Audio device +static struct AudioState s_audio_state; +static struct AudioDevice AUDIO_DEVICE = { + .state = &s_audio_state, + .base_addr = QEMU_AUDIO_BASE, + .irqn = AUDIO_IRQn, +}; +AudioDevice *const AUDIO = (AudioDevice *)&AUDIO_DEVICE; + +// Microphone (QEMU stub — feeds silence on a timer) +static MicDeviceState s_mic_state; +static MicDevice MIC_DEVICE = { + .state = &s_mic_state, + .channels = 1, +}; +MicDevice * const MIC = &MIC_DEVICE; + +// IRQ handler trampolines +IRQ_MAP(UART2, uart_irq_handler, DBG_UART); +IRQ_MAP(UART1, uart_irq_handler, QEMU_UART); +IRQ_MAP(AUDIO, qemu_audio_irq_handler, AUDIO); + +void board_early_init(void) { +} + +void board_init(void) { +} diff --git a/src/fw/board/boards/board_qemu_emery.h b/src/fw/board/boards/board_qemu_emery.h new file mode 100644 index 0000000000..59308b40e9 --- /dev/null +++ b/src/fw/board/boards/board_qemu_emery.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define BT_VENDOR_ID 0x0000 +#define BT_VENDOR_NAME "QEMU" + +extern UARTDevice * const DBG_UART; +extern UARTDevice * const QEMU_UART; +extern DisplayDevice *const DISPLAY; +extern MicDevice * const MIC; +extern const BoardConfigActuator BOARD_CONFIG_VIBE; +extern const BoardConfigPower BOARD_CONFIG_POWER; +extern const BoardConfig BOARD_CONFIG; +extern const BoardConfigButton BOARD_CONFIG_BUTTON; + +static const BoardConfigAccel BOARD_CONFIG_ACCEL = { + .accel_config = { + .default_motion_sensitivity = 85U, + }, +}; + +static const BoardConfigMag BOARD_CONFIG_MAG = { + .mag_config = {{0}}, +}; diff --git a/src/fw/board/boards/board_qemu_flint.c b/src/fw/board/boards/board_qemu_flint.c new file mode 100644 index 0000000000..eb37b61f59 --- /dev/null +++ b/src/fw/board/boards/board_qemu_flint.c @@ -0,0 +1,99 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" + +// UART device for debug serial +#include "drivers/uart/qemu.h" +#include "drivers/speaker/qemu/audio.h" +#include "drivers/mic/qemu/mic_definitions.h" + +static UARTDeviceState s_dbg_uart_state = {}; + +static struct UARTDevice DBG_UART_DEVICE = { + .state = &s_dbg_uart_state, + .base_addr = QEMU_UART2_BASE, + .irqn = UART2_IRQn, + .irq_priority = 5, +}; + +UARTDevice *const DBG_UART = (UARTDevice *)&DBG_UART_DEVICE; + +// QEMU control protocol UART (UART1) +static UARTDeviceState s_qemu_uart_state = {}; + +static struct UARTDevice QEMU_UART_DEVICE = { + .state = &s_qemu_uart_state, + .base_addr = QEMU_UART1_BASE, + .irqn = UART1_IRQn, + .irq_priority = 6, +}; + +UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; + +// Display device - QEMU framebuffer +static QemuDisplayDevice s_display = { + .base_addr = QEMU_DISPLAY_BASE, + .fb_addr = QEMU_DISPLAY_FB_BASE, + .width = 144, + .height = 168, + .bpp = 1, + .irqn = DISPLAY_IRQn, + .irq_priority = 5, +}; + +DisplayDevice *const DISPLAY = &s_display; + +const BoardConfigActuator BOARD_CONFIG_VIBE = { + .options = 0, +}; + +const BoardConfigPower BOARD_CONFIG_POWER = { + .low_power_threshold = 2U, + .battery_capacity_hours = 168U, +}; + +const BoardConfig BOARD_CONFIG = { + .backlight_on_percent = 100, + .ambient_light_dark_threshold = 150, + .ambient_k_delta_threshold = 25, +}; + +const BoardConfigButton BOARD_CONFIG_BUTTON = { + .buttons = { + [BUTTON_ID_BACK] = { "Back", NULL, 0, GPIO_PuPd_NOPULL, true }, + [BUTTON_ID_UP] = { "Up", NULL, 1, GPIO_PuPd_UP, false }, + [BUTTON_ID_SELECT] = { "Select", NULL, 2, GPIO_PuPd_UP, false }, + [BUTTON_ID_DOWN] = { "Down", NULL, 3, GPIO_PuPd_UP, false }, + }, + .timer = NULL, + .timer_irqn = TIMER0_IRQn, +}; + +// Audio device +static struct AudioState s_audio_state; +static struct AudioDevice AUDIO_DEVICE = { + .state = &s_audio_state, + .base_addr = QEMU_AUDIO_BASE, + .irqn = AUDIO_IRQn, +}; +AudioDevice *const AUDIO = (AudioDevice *)&AUDIO_DEVICE; + +// Microphone (QEMU stub — feeds silence on a timer) +static MicDeviceState s_mic_state; +static MicDevice MIC_DEVICE = { + .state = &s_mic_state, + .channels = 1, +}; +MicDevice * const MIC = &MIC_DEVICE; + +// IRQ handler trampolines +IRQ_MAP(UART2, uart_irq_handler, DBG_UART); +IRQ_MAP(UART1, uart_irq_handler, QEMU_UART); +IRQ_MAP(AUDIO, qemu_audio_irq_handler, AUDIO); + +void board_early_init(void) { +} + +void board_init(void) { +} diff --git a/src/fw/board/boards/board_qemu_flint.h b/src/fw/board/boards/board_qemu_flint.h new file mode 100644 index 0000000000..59308b40e9 --- /dev/null +++ b/src/fw/board/boards/board_qemu_flint.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define BT_VENDOR_ID 0x0000 +#define BT_VENDOR_NAME "QEMU" + +extern UARTDevice * const DBG_UART; +extern UARTDevice * const QEMU_UART; +extern DisplayDevice *const DISPLAY; +extern MicDevice * const MIC; +extern const BoardConfigActuator BOARD_CONFIG_VIBE; +extern const BoardConfigPower BOARD_CONFIG_POWER; +extern const BoardConfig BOARD_CONFIG; +extern const BoardConfigButton BOARD_CONFIG_BUTTON; + +static const BoardConfigAccel BOARD_CONFIG_ACCEL = { + .accel_config = { + .default_motion_sensitivity = 85U, + }, +}; + +static const BoardConfigMag BOARD_CONFIG_MAG = { + .mag_config = {{0}}, +}; diff --git a/src/fw/board/boards/board_qemu_gabbro.c b/src/fw/board/boards/board_qemu_gabbro.c new file mode 100644 index 0000000000..f98f00a3c2 --- /dev/null +++ b/src/fw/board/boards/board_qemu_gabbro.c @@ -0,0 +1,88 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" + +// UART device for debug serial +#include "drivers/uart/qemu.h" +#include "drivers/mic/qemu/mic_definitions.h" + +static UARTDeviceState s_dbg_uart_state = {}; + +static struct UARTDevice DBG_UART_DEVICE = { + .state = &s_dbg_uart_state, + .base_addr = QEMU_UART2_BASE, + .irqn = UART2_IRQn, + .irq_priority = 5, +}; + +UARTDevice *const DBG_UART = (UARTDevice *)&DBG_UART_DEVICE; + +// QEMU control protocol UART (UART1) +static UARTDeviceState s_qemu_uart_state = {}; + +static struct UARTDevice QEMU_UART_DEVICE = { + .state = &s_qemu_uart_state, + .base_addr = QEMU_UART1_BASE, + .irqn = UART1_IRQn, + .irq_priority = 6, +}; + +UARTDevice *const QEMU_UART = (UARTDevice *)&QEMU_UART_DEVICE; + +// Display device - QEMU framebuffer +static QemuDisplayDevice s_display = { + .base_addr = QEMU_DISPLAY_BASE, + .fb_addr = QEMU_DISPLAY_FB_BASE, + .width = 260, + .height = 260, + .bpp = 8, + .irqn = DISPLAY_IRQn, + .irq_priority = 5, +}; + +DisplayDevice *const DISPLAY = &s_display; + +const BoardConfigActuator BOARD_CONFIG_VIBE = { + .options = 0, +}; + +const BoardConfigPower BOARD_CONFIG_POWER = { + .low_power_threshold = 2U, + .battery_capacity_hours = 168U, +}; + +const BoardConfig BOARD_CONFIG = { + .backlight_on_percent = 100, + .ambient_light_dark_threshold = 150, + .ambient_k_delta_threshold = 25, +}; + +const BoardConfigButton BOARD_CONFIG_BUTTON = { + .buttons = { + [BUTTON_ID_BACK] = { "Back", NULL, 0, GPIO_PuPd_NOPULL, true }, + [BUTTON_ID_UP] = { "Up", NULL, 1, GPIO_PuPd_UP, false }, + [BUTTON_ID_SELECT] = { "Select", NULL, 2, GPIO_PuPd_UP, false }, + [BUTTON_ID_DOWN] = { "Down", NULL, 3, GPIO_PuPd_UP, false }, + }, + .timer = NULL, + .timer_irqn = TIMER0_IRQn, +}; + +// Microphone (QEMU stub — feeds silence on a timer) +static MicDeviceState s_mic_state; +static MicDevice MIC_DEVICE = { + .state = &s_mic_state, + .channels = 1, +}; +MicDevice * const MIC = &MIC_DEVICE; + +// IRQ handler trampolines +IRQ_MAP(UART2, uart_irq_handler, DBG_UART); +IRQ_MAP(UART1, uart_irq_handler, QEMU_UART); + +void board_early_init(void) { +} + +void board_init(void) { +} diff --git a/src/fw/board/boards/board_qemu_gabbro.h b/src/fw/board/boards/board_qemu_gabbro.h new file mode 100644 index 0000000000..59308b40e9 --- /dev/null +++ b/src/fw/board/boards/board_qemu_gabbro.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define BT_VENDOR_ID 0x0000 +#define BT_VENDOR_NAME "QEMU" + +extern UARTDevice * const DBG_UART; +extern UARTDevice * const QEMU_UART; +extern DisplayDevice *const DISPLAY; +extern MicDevice * const MIC; +extern const BoardConfigActuator BOARD_CONFIG_VIBE; +extern const BoardConfigPower BOARD_CONFIG_POWER; +extern const BoardConfig BOARD_CONFIG; +extern const BoardConfigButton BOARD_CONFIG_BUTTON; + +static const BoardConfigAccel BOARD_CONFIG_ACCEL = { + .accel_config = { + .default_motion_sensitivity = 85U, + }, +}; + +static const BoardConfigMag BOARD_CONFIG_MAG = { + .mag_config = {{0}}, +}; diff --git a/src/fw/board/boards/board_robert.c b/src/fw/board/boards/board_robert.c deleted file mode 100644 index eee377b719..0000000000 --- a/src/fw/board/boards/board_robert.c +++ /dev/null @@ -1,828 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/display/ice40lp/ice40lp_definitions.h" -#include "drivers/exti.h" -#include "drivers/flash/qspi_flash_definitions.h" -#include "drivers/hrm/as7000/as7000.h" -#include "drivers/i2c_definitions.h" -#include "drivers/mic/stm32/dfsdm_definitions.h" -#include "drivers/pmic.h" -#include "drivers/qspi_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/spi_definitions.h" -#include "drivers/stm32f7/i2c_hal_definitions.h" -#include "drivers/stm32f7/uart_definitions.h" -#include "drivers/temperature/analog.h" -#include "drivers/touch/ewd1000/touch_sensor_definitions.h" -#include "drivers/voltage_monitor.h" -#include "flash_region/flash_region.h" -#include "util/units.h" - -#define DIALOG_SPI_DMA_PRIORITY (0x0b) -// Make sure that the DMA IRQ is handled before EXTI: -// See comments in host/host_transport.c prv_int_exti_cb() -_Static_assert(DIALOG_SPI_DMA_PRIORITY < EXTI_PRIORITY, "Dialog SPI DMA priority too low!"); - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 2); // DMA1_STREAM2_DEVICE - Accessory UART RX -CREATE_DMA_STREAM(2, 0); // DMA2_STREAM0_DEVICE - Dialog SPI RX -CREATE_DMA_STREAM(2, 1); // DMA2_STREAM1_DEVICE - Dialog SPI TX -CREATE_DMA_STREAM(2, 2); // DMA2_STREAM2_DEVICE - Compositor DMA -CREATE_DMA_STREAM(2, 4); // DMA2_STREAM4_DEVICE - DFSDM -CREATE_DMA_STREAM(2, 5); // DMA2_STREAM5_DEVICE - ICE40LP TX -CREATE_DMA_STREAM(2, 7); // DMA2_STREAM7_DEVICE - QSPI - -// DMA Requests -// - On DMA1 we have "Debug UART RX" and "Accessory UART RX". The former is never used in a sealed -// watch, and the latter is only sometimes used in a sealed watch. So, we don't really care about -// their priorities and set them both to "High". -// - On DMA2 we have "Dialog SPI RX", "Dialog SPI TX", "Compositor DMA", "DFSDM", "ICE40LP TX", and -// "QSPI". We want "DFSDM" and "Dialog SPI RX" to have a very high priority because their -// peripheral buffers may overflow if the DMA stream doesn't read from them in a while. After -// that, we want communication with the BLE chip and QSPI reads to be low-latency so give them a -// high priority. Lastly, writing to the display prevents us from rendering the next frame, so -// give the "ICE40LP TX" and "Compositor" DMAs a medium priority. - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_accessory_uart_dma_request_state; -static DMARequest ACCESSORY_UART_RX_DMA_REQUEST = { - .state = &s_accessory_uart_dma_request_state, - .stream = &DMA1_STREAM2_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_dialog_spi_rx_dma_request_state; -static DMARequest DIALOG_SPI_RX_DMA_REQUEST = { - .state = &s_dialog_spi_rx_dma_request_state, - .stream = &DMA2_STREAM0_DEVICE, - .channel = 4, - .irq_priority = DIALOG_SPI_DMA_PRIORITY, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_dialog_spi_tx_dma_request_state; -static DMARequest DIALOG_SPI_TX_DMA_REQUEST = { - .state = &s_dialog_spi_tx_dma_request_state, - .stream = &DMA2_STREAM1_DEVICE, - .channel = 4, - .irq_priority = DIALOG_SPI_DMA_PRIORITY, - .priority = DMARequestPriority_High, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_compositor_dma_request_state; -static DMARequest COMPOSITOR_DMA_REQUEST = { - .state = &s_compositor_dma_request_state, - .stream = &DMA2_STREAM2_DEVICE, - .channel = 0, - .irq_priority = 11, - .priority = DMARequestPriority_Medium, - .type = DMARequestType_MemoryToMemory, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const COMPOSITOR_DMA = &COMPOSITOR_DMA_REQUEST; - -static DMARequestState s_dfsdm_dma_request_state; -static DMARequest DFSDM_DMA_REQUEST = { - .state = &s_dfsdm_dma_request_state, - .stream = &DMA2_STREAM4_DEVICE, - .channel = 8, - .irq_priority = 0x0f, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Word, -}; - -static DMARequestState s_ice40lp_spi_tx_dma_request_state; -static DMARequest ICE40LP_SPI_TX_DMA_REQUEST = { - .state = &s_ice40lp_spi_tx_dma_request_state, - .stream = &DMA2_STREAM5_DEVICE, - .channel = 1, - // Use the same priority as the EXTI handlers so that the DMA-complete - // handler doesn't preempt the display BUSY (INTn) handler. - .irq_priority = 0x0e, - .priority = DMARequestPriority_Medium, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_qspi_dma_request_state; -static DMARequest QSPI_DMA_REQUEST = { - .state = &s_qspi_dma_request_state, - .stream = &DMA2_STREAM7_DEVICE, - .channel = 3, - .irq_priority = 0x0f, - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Word, -}; - - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF7_USART3 - }, - .rx_gpio = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF7_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - -static UARTDeviceState s_accessory_uart_state; -static UARTDevice ACCESSORY_UART_DEVICE = { - .state = &s_accessory_uart_state, - .half_duplex = true, - .tx_gpio = { -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_12, - .gpio_pin_source = GPIO_PinSource12, - .gpio_af = GPIO_AF6_UART4 -#elif BOARD_ROBERT_BB2 || BOARD_ROBERT_EVT - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF8_UART4 -#else -#error "Unknown board" -#endif - }, - .periph = UART4, - .irq_channel = UART4_IRQn, - .irq_priority = 0xb, - .rcc_apb_periph = RCC_APB1Periph_UART4, - .rx_dma = &ACCESSORY_UART_RX_DMA_REQUEST -}; -UARTDevice * const ACCESSORY_UART = &ACCESSORY_UART_DEVICE; -IRQ_MAP(UART4, uart_irq_handler, ACCESSORY_UART); - -static UARTDeviceState s_bt_bootrom_uart_state; -static UARTDevice BT_BOOTROM_UART_DEVICE = { - .state = &s_bt_bootrom_uart_state, -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB - .do_swap_rx_tx = true, -#elif BOARD_ROBERT_BB2 || BOARD_ROBERT_EVT - .do_swap_rx_tx = false, -#else -#error "Unknown board" -#endif - .rx_gpio = { GPIOE, GPIO_Pin_0, GPIO_PinSource0, GPIO_AF8_UART8 }, - .tx_gpio = { GPIOE, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF8_UART8 }, - .rcc_apb_periph = RCC_APB1Periph_UART8, - .periph = UART8, -}; - -UARTDevice * const BT_TX_BOOTROM_UART = &BT_BOOTROM_UART_DEVICE; -UARTDevice * const BT_RX_BOOTROM_UART = &BT_BOOTROM_UART_DEVICE; - -// I2C DEVICES - -#if BOARD_CUTTS_BB -static I2CBusState I2C_TOUCH_ALS_BUS_STATE = {}; - -static const I2CBusHal I2C_TOUCH_ALS_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .bus_mode = I2CBusMode_FastMode, - .clock_speed = 400000, - // TODO: These need to be measured. Just using PMIC_MAG values for now. - .rise_time_ns = 150, - .fall_time_ns = 6, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_TOUCH_ALS_BUS = { - .state = &I2C_TOUCH_ALS_BUS_STATE, - .hal = &I2C_TOUCH_ALS_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF4_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF4_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_TOUCH_ALS" -}; -#endif - -static I2CBusState I2C_HRM_BUS_STATE = {}; - -static const I2CBusHal I2C_HRM_BUS_HAL = { - .i2c = I2C2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .bus_mode = I2CBusMode_FastMode, - .clock_speed = 400000, -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB - // TODO: These need to be measured. Just using PMIC_MAG values for now. - .rise_time_ns = 150, - .fall_time_ns = 6, -#elif BOARD_ROBERT_BB2 - // TODO: These need to be measured. Just using PMIC_MAG values for now. - .rise_time_ns = 150, - .fall_time_ns = 6, -#elif BOARD_ROBERT_EVT - // TODO: These need to be measured. Just using PMIC_MAG values for now. - .rise_time_ns = 70, - .fall_time_ns = 5, -#else -#error "Unknown board" -#endif - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, -}; - -static const I2CBus I2C_HRM_BUS = { - .state = &I2C_HRM_BUS_STATE, - .hal = &I2C_HRM_BUS_HAL, - .scl_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF4_I2C2 - }, - .sda_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_0, - .gpio_pin_source = GPIO_PinSource0, - .gpio_af = GPIO_AF4_I2C2 - }, - .stop_mode_inhibitor = InhibitorI2C2, - .name = "I2C_HRM" -}; - -#if BOARD_CUTTS_BB -static I2CBusState I2C_NFC_BUS_STATE = {}; - -static const I2CBusHal I2C_NFC_BUS_HAL = { - .i2c = I2C3, - .clock_ctrl = RCC_APB1Periph_I2C3, - .bus_mode = I2CBusMode_FastMode, - .clock_speed = 400000, - // TODO: These need to be measured. Just using PMIC_MAG values for now. - .rise_time_ns = 150, - .fall_time_ns = 6, - .ev_irq_channel = I2C3_EV_IRQn, - .er_irq_channel = I2C3_ER_IRQn, -}; - -static const I2CBus I2C_NFC_BUS = { - .state = &I2C_NFC_BUS_STATE, - .hal = &I2C_NFC_BUS_HAL, - .scl_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF4_I2C3 - }, - .sda_gpio = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF4_I2C3 - }, - .stop_mode_inhibitor = InhibitorI2C3, - .name = "I2C_NFC" -}; -#endif - -static I2CBusState I2C_PMIC_MAG_BUS_STATE = {}; -static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = { - .i2c = I2C4, - .clock_ctrl = RCC_APB1Periph_I2C4, - .bus_mode = I2CBusMode_FastMode, - .clock_speed = 400000, -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB - // These rise and fall times were measured. - .rise_time_ns = 150, - .fall_time_ns = 6, -#elif BOARD_ROBERT_BB2 - // TODO: These rise and fall times are based on robert_bb and should be measured - .rise_time_ns = 150, - .fall_time_ns = 6, -#elif BOARD_ROBERT_EVT - // TODO: These are calculated and could potentially be measured. - .rise_time_ns = 70, - .fall_time_ns = 5, -#else -#error "Unknown board" -#endif - .ev_irq_channel = I2C4_EV_IRQn, - .er_irq_channel = I2C4_ER_IRQn, -}; - -static const I2CBus I2C_PMIC_MAG_BUS = { - .state = &I2C_PMIC_MAG_BUS_STATE, - .hal = &I2C_PMIC_MAG_BUS_HAL, - .scl_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_14, - .gpio_pin_source = GPIO_PinSource14, - .gpio_af = GPIO_AF4_I2C4 - }, - .sda_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_15, - .gpio_pin_source = GPIO_PinSource15, - .gpio_af = GPIO_AF4_I2C4 - }, - .stop_mode_inhibitor = InhibitorI2C4, - .name = "I2C_PMIC_MAG" -}; - -#if BOARD_CUTTS_BB -static const I2CSlavePort I2C_SLAVE_EWD1000 = { - .bus = &I2C_TOUCH_ALS_BUS, - .address = 0x2A -}; -#endif - -static const I2CSlavePort I2C_SLAVE_MAX14690 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x50 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x0e << 1 -}; - -static const I2CSlavePort I2C_SLAVE_AS7000 = { - .bus = &I2C_HRM_BUS, - .address = 0x60 -}; - -I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; -I2CSlavePort * const I2C_AS7000 = &I2C_SLAVE_AS7000; - -IRQ_MAP(I2C2_EV, i2c_hal_event_irq_handler, &I2C_HRM_BUS); -IRQ_MAP(I2C2_ER, i2c_hal_error_irq_handler, &I2C_HRM_BUS); -IRQ_MAP(I2C4_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS); -IRQ_MAP(I2C4_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS); -#if BOARD_CUTTS_BB -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_TOUCH_ALS_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_TOUCH_ALS_BUS); -#endif - - -// HRM DEVICE -static HRMDeviceState s_hrm_state; -static HRMDevice HRM_DEVICE = { - .state = &s_hrm_state, - .handshake_int = { EXTI_PortSourceGPIOI, 10 }, - .int_gpio = { - .gpio = GPIOI, - .gpio_pin = GPIO_Pin_10 - }, - .en_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_3, - .active_high = false, - }, - .i2c_slave = &I2C_SLAVE_AS7000, -}; -HRMDevice * const HRM = &HRM_DEVICE; - -#if BOARD_CUTTS_BB -static const TouchSensor EWD1000_DEVICE = { - .i2c = &I2C_SLAVE_EWD1000, - .int_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_7, - }, - .int_exti = { - .exti_port_source = EXTI_PortSourceGPIOB, - .exti_line = 7, - }, - .reset_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_5, - .active_high = true, - }, -}; - -TouchSensor * const EWD1000 = &EWD1000_DEVICE; -#endif - -// VOLTAGE MONITOR DEVICES - -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC3, - .adc_channel = ADC_Channel_14, - .clock_ctrl = RCC_APB2Periph_ADC3, - .input = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_4, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_9, - .clock_ctrl = RCC_APB2Periph_ADC1, - .input = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_1, - } -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_TEMPERATURE_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_TempSensor, - .clock_ctrl = RCC_APB2Periph_ADC1, - // .input not applicable -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_TEMPERATURE = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE; - - -// Temperature sensor - -const AnalogTemperatureSensor TEMPERATURE_SENSOR_DEVICE = { - .voltage_monitor = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE, - .millivolts_ref = 760, - .millidegrees_ref = 25000, - .slope_numerator = 5, - .slope_denominator = 2000, -}; -const AnalogTemperatureSensor * const TEMPERATURE_SENSOR = &TEMPERATURE_SENSOR_DEVICE; - - -// -// SPI Bus configuration -// - -static SPIBusState DIALOG_SPI_BUS_STATE = { }; -static const SPIBus DIALOG_SPI_BUS = { - .state = &DIALOG_SPI_BUS_STATE, - .spi = SPI4, - .spi_sclk = { GPIOE, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF5_SPI5 }, - .spi_miso = { GPIOE, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF5_SPI5 }, - .spi_mosi = { GPIOE, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF5_SPI5 }, - .spi_sclk_speed = GPIO_Speed_50MHz, - // DA14680_FS v1.4 page 89: - // "In slave mode the internal SPI clock must be more than four times the SPIx_CLK" - // The system clock is 16MHz, so don't use more than 4MHz. - .spi_clock_speed_hz = MHZ_TO_HZ(4) -}; - -static SPIBusState BMI160_SPI_BUS_STATE = {}; -static const SPIBus BMI160_SPI_BUS = { - .state = &BMI160_SPI_BUS_STATE, - .spi = SPI2, - .spi_sclk = { - .gpio = GPIOI, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF5_SPI2 - }, - .spi_miso = { - .gpio = GPIOI, - .gpio_pin = GPIO_Pin_2, - .gpio_pin_source = GPIO_PinSource2, - .gpio_af = GPIO_AF5_SPI2 - }, - .spi_mosi = { - .gpio = GPIOI, - .gpio_pin = GPIO_Pin_3, - .gpio_pin_source = GPIO_PinSource3, - .gpio_af = GPIO_AF5_SPI2 - }, - .spi_sclk_speed = GPIO_Speed_25MHz, - .spi_clock_speed_hz = MHZ_TO_HZ(5), -}; - -static SPIBusState ICE40LP_SPI_BUS_STATE = {}; -static const SPIBus ICE40LP_SPI_BUS = { - .state = &ICE40LP_SPI_BUS_STATE, - .spi = SPI6, - .spi_sclk = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_5, - .gpio_pin_source = GPIO_PinSource5, - .gpio_af = GPIO_AF8_SPI6 - }, - .spi_miso = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF8_SPI6 - }, - .spi_mosi = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF8_SPI6 - }, - .spi_sclk_speed = GPIO_Speed_25MHz, - .spi_clock_speed_hz = MHZ_TO_HZ(16), -}; - -// -// SPI Slave port configuration -// - -static SPISlavePortState BMI160_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort BMI160_SPI_SLAVE_PORT = { - .slave_state = &BMI160_SPI_SLAVE_PORT_STATE, - .spi_bus = &BMI160_SPI_BUS, - .spi_scs = { - .gpio = GPIOI, - .gpio_pin = GPIO_Pin_0, - .active_high = false - }, - .spi_direction = SpiDirection_2LinesFullDuplex, - .spi_cpol = SpiCPol_Low, - .spi_cpha = SpiCPha_1Edge, - .spi_first_bit = SpiFirstBit_MSB, -}; -SPISlavePort * const BMI160_SPI = &BMI160_SPI_SLAVE_PORT; - -static SPISlavePortState ICE40LP_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort ICE40LP_SPI_SLAVE_PORT = { - .slave_state = &ICE40LP_SPI_SLAVE_PORT_STATE, - .spi_bus = &ICE40LP_SPI_BUS, - .spi_scs = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_4, - .active_high = false - }, - .spi_direction = SpiDirection_1LineTx, - .spi_cpol = SpiCPol_High, - .spi_cpha = SpiCPha_2Edge, - .spi_first_bit = SpiFirstBit_MSB, - .tx_dma = &ICE40LP_SPI_TX_DMA_REQUEST -}; - -static SPISlavePortState DIALOG_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort DIALOG_SPI_SLAVE_PORT = { - .slave_state = &DIALOG_SPI_SLAVE_PORT_STATE, - .spi_bus = &DIALOG_SPI_BUS, - .spi_scs = { GPIOE, GPIO_Pin_11, false }, - .spi_direction = SpiDirection_2LinesFullDuplex, - .spi_cpol = SpiCPol_Low, - .spi_cpha = SpiCPha_1Edge, - .spi_first_bit = SpiFirstBit_MSB, - .rx_dma = &DIALOG_SPI_RX_DMA_REQUEST, - .tx_dma = &DIALOG_SPI_TX_DMA_REQUEST -}; -SPISlavePort * const DIALOG_SPI = &DIALOG_SPI_SLAVE_PORT; - - - -// -// iCE40LP configuration -// -static ICE40LPDeviceState s_ice40lp_state; -static ICE40LPDevice ICE40LP_DEVICE = { - .state = &s_ice40lp_state, - - .spi_port = &ICE40LP_SPI_SLAVE_PORT, - .base_spi_frequency = MHZ_TO_HZ(16), - .fast_spi_frequency = MHZ_TO_HZ(32), - .creset = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - .cdone = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_2, - }, - .busy = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_0, - }, - .cdone_exti = { - .exti_port_source = EXTI_PortSourceGPIOB, - .exti_line = 2, - }, - .busy_exti = { - .exti_port_source = EXTI_PortSourceGPIOB, - .exti_line = 0, - }, -#if BOARD_CUTTS_BB - .use_6v6_rail = true, -#elif BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_ROBERT_EVT - .use_6v6_rail = false, -#else -#error "Unknown board" -#endif -}; -ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE; - - -// QSPI -static QSPIPortState s_qspi_port_state; -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, - .clock_speed_hz = MHZ_TO_HZ(72), - .auto_polling_interval = 16, - .clock_ctrl = RCC_AHB3Periph_QSPI, - .cs_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - .clk_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - .data_gpio = { - { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF9_QUADSPI, - }, - { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - { -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_CUTTS_BB - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF9_QUADSPI, -#elif BOARD_ROBERT_EVT - .gpio = GPIOE, - .gpio_pin = GPIO_Pin_2, - .gpio_pin_source = GPIO_PinSource2, - .gpio_af = GPIO_AF9_QUADSPI, -#else -#error "Unknown board" -#endif - }, - { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF9_QUADSPI, - }, - }, - .dma = &QSPI_DMA_REQUEST, -}; -QSPIPort * const QSPI = &QSPI_PORT; - -static QSPIFlashState s_qspi_flash_state; -static QSPIFlash QSPI_FLASH_DEVICE = { - .state = &s_qspi_flash_state, - .qspi = &QSPI_PORT, - .default_fast_read_ddr_enabled = true, -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_CUTTS_BB - .reset_gpio = { GPIO_Port_NULL }, -#elif BOARD_ROBERT_EVT - .reset_gpio = { - .gpio = GPIOE, - .gpio_pin = GPIO_Pin_15, - .active_high = false, - }, -#else -#error "Unknown error" -#endif -}; -QSPIFlash * const QSPI_FLASH = &QSPI_FLASH_DEVICE; - - -static MicDeviceState DMA_BSS s_mic_state; -static MicDevice MIC_DEVICE = { - .state = &s_mic_state, - - .filter = DFSDM_Filter0, - .channel = DFSDM_Channel0, - .extremes_detector_channel = DFSDM_ExtremChannel0, - .regular_channel = DFSDM_RegularChannel0, - .pdm_frequency = MHZ_TO_HZ(2), - .rcc_apb_periph = RCC_APB2Periph_DFSDM, - .dma = &DFSDM_DMA_REQUEST, - .ck_gpio = { GPIOD, GPIO_Pin_3, GPIO_PinSource3, GPIO_AF3_DFSDM }, - .sd_gpio = { GPIOC, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF3_DFSDM }, -#if BOARD_ROBERT_BB || BOARD_ROBERT_EVT || BOARD_CUTTS_BB - .mic_power_state_fn = set_ldo3_power_state, -#elif BOARD_ROBERT_BB2 - // the mic is always powered -#else -#error "Unknown board" -#endif - .power_on_delay_ms = 30, - .settling_delay_ms = 100, - .default_volume = 64, - .final_right_shift = 11, -}; -MicDevice * const MIC = &MIC_DEVICE; - - -void board_early_init(void) { - spi_slave_port_init(ICE40LP->spi_port); -} - -void board_init(void) { -#if BOARD_CUTTS_BB - i2c_init(&I2C_TOUCH_ALS_BUS); - i2c_init(&I2C_NFC_BUS); -#endif - i2c_init(&I2C_HRM_BUS); - i2c_init(&I2C_PMIC_MAG_BUS); - spi_slave_port_init(BMI160_SPI); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); - - qspi_init(QSPI, BOARD_NOR_FLASH_SIZE); -} diff --git a/src/fw/board/boards/board_robert.h b/src/fw/board/boards/board_robert.h deleted file mode 100644 index 4f72c4c32c..0000000000 --- a/src/fw/board/boards/board_robert.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// ---------------------------------------------- -// Board definitions for robert_bb, robert_bb2, robert_evt, cutts_bb -// ---------------------------------------------- -// - -#include "drivers/imu/bmi160/bmi160.h" -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -static const BoardConfig BOARD_CONFIG = { - .ambient_light_dark_threshold = 3220, - .ambient_k_delta_threshold = 96, - .photo_en = { GPIOF, GPIO_Pin_5, true }, - .als_always_on = true, - - .dbgserial_int = { EXTI_PortSourceGPIOH, 9 }, - .dbgserial_int_gpio = { GPIOH, GPIO_Pin_9 }, - - // Only used with Sharp displays - .lcd_com = { 0 }, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .backlight_on_percent = 45, - .backlight_max_duty_cycle_percent = 100, - - .num_avail_gpios = 140, - - .has_mic = true, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB || BOARD_ROBERT_BB2 - [BUTTON_ID_BACK] = - { "Back", GPIOG, GPIO_Pin_6, { EXTI_PortSourceGPIOG, 6 }, GPIO_PuPd_UP }, - [BUTTON_ID_UP] = - { "Up", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_DOWN] = - { "Down", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_UP }, -#elif BOARD_ROBERT_EVT - [BUTTON_ID_BACK] = - { "Back", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = - { "Up", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_UP }, - [BUTTON_ID_DOWN] = - { "Down", GPIOG, GPIO_Pin_6, { EXTI_PortSourceGPIOG, 6 }, GPIO_PuPd_UP }, -#else -#error "Unknown board" -#endif - [BUTTON_ID_SELECT] = - { "Select", GPIOG, GPIO_Pin_5, { EXTI_PortSourceGPIOG, 5 }, GPIO_PuPd_UP }, - }, - - .button_com = { 0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOF, 12 }, - .pmic_int_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_12, - }, - - .rail_4V5_ctrl = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_5, - .active_high = true, - }, -#if BOARD_CUTTS_BB - .rail_6V6_ctrl = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - .rail_6V6_ctrl_otype = GPIO_OType_PP, -#elif BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_ROBERT_EVT - .rail_6V6_ctrl = { GPIO_Port_NULL }, -#else -#error "Unknown board" -#endif - - .battery_vmon_scale = { - // The PMIC divides the battery voltage by a ratio of 3:1 in order to move it down to a voltage - // our ADC is capable of reading. The battery voltage varies around 4V~ and we're only capable - // of reading up to 1.8V in the ADC. - .numerator = 3, - .denominator = 1, - }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - -#if defined(IS_BIGBOARD) && !defined(BATTERY_DEBUG) - .charging_cutoff_voltage = 4200, -#else - .charging_cutoff_voltage = 4300, -#endif - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 2, - .battery_capacity_hours = 204, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_CUTTS_BB - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = true, -#elif BOARD_ROBERT_EVT - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = false, -#else -#error "Unknown board" -#endif - .shake_thresholds[AccelThresholdHigh] = 0x64, - .shake_thresholds[AccelThresholdLow] = 0xf, - .double_tap_threshold = 12500, - }, - .accel_int_gpios = { - [0] = { GPIOH, GPIO_Pin_15 }, - [1] = { GPIOH, GPIO_Pin_14 }, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOH, 15 }, - [1] = { EXTI_PortSourceGPIOH, 14 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_CUTTS_BB - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, -#elif BOARD_ROBERT_EVT - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = false, -#else -#error "Unknown board" -#endif - }, - .mag_int_gpio = { GPIOF, GPIO_Pin_11 }, - .mag_int = { EXTI_PortSourceGPIOF, 11 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl | ActuatorOptions_Pwm | ActuatorOptions_HBridge, -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB - .ctl = { GPIOD, GPIO_Pin_14, true }, - .pwm = { - .output = { GPIOD, GPIO_Pin_12, true }, - .timer = { - .peripheral = TIM4, - .config_clock = RCC_APB1Periph_TIM4, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOD, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF2_TIM4 }, - }, -#elif BOARD_ROBERT_BB2 || BOARD_ROBERT_EVT - .ctl = { GPIOA, GPIO_Pin_12, true }, - .pwm = { - .output = { GPIOB, GPIO_Pin_14, true }, - .timer = { - .peripheral = TIM12, - .config_clock = RCC_APB1Periph_TIM12, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF9_TIM12 }, - }, -#else -#error "Unknown board" -#endif -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm, - .ctl = {0}, - .pwm = { - .output = { GPIOG, GPIO_Pin_13, true }, - .timer = { - .lp_peripheral = LPTIM1, - .config_clock = RCC_APB1Periph_LPTIM1, - }, - .afcfg = { GPIOG, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF3_LPTIM1 }, - }, -}; - -static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = { -#if BOARD_ROBERT_BB || BOARD_CUTTS_BB - .power_en = { GPIOA, GPIO_Pin_11, true }, -#elif BOARD_ROBERT_BB2 || BOARD_ROBERT_EVT - .power_en = { GPIOD, GPIO_Pin_2, true }, -#else -#error "Unknown board" -#endif - .int_gpio = { GPIOH, GPIO_Pin_13 }, - .exti = { EXTI_PortSourceGPIOH, 13 }, -}; - -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = DA14681, - .reset = { GPIOG, GPIO_Pin_0, true }, - .wakeup = { - .int_gpio = { GPIOG, GPIO_Pin_1 }, - .int_exti = { EXTI_PortSourceGPIOG, 1 }, - }, -}; - -static const BoardConfigBTUART BOARD_CONFIG_BT_UART = { - .rx_af_cfg = { GPIOE, GPIO_Pin_0, GPIO_PinSource0, GPIO_AF8_UART8 }, - .tx_af_cfg = { GPIOE, GPIO_Pin_1, GPIO_PinSource1, GPIO_AF8_UART8 }, - .rx_clk_control = RCC_APB1Periph_UART8, - .tx_clk_control = RCC_APB1Periph_UART8, - .rx_uart = UART8, - .tx_uart = UART8, -}; - -static const BoardConfigBTSPI BOARD_CONFIG_BT_SPI = { - .cs = { GPIOE, GPIO_Pin_11, false }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF0_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -#define DIALOG_TIMER_IRQ_HANDLER TIM6_DAC_IRQHandler -static const TimerIrqConfig BOARD_BT_WATCHDOG_TIMER = { - .timer = { - .peripheral = TIM6, - .config_clock = RCC_APB1Periph_TIM6, - }, - .irq_channel = TIM6_DAC_IRQn, -}; - -extern DMARequest * const COMPOSITOR_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; -extern UARTDevice * const ACCESSORY_UART; -extern UARTDevice * const BT_TX_BOOTROM_UART; -extern UARTDevice * const BT_RX_BOOTROM_UART; - -extern SPISlavePort * const BMI160_SPI; - -extern I2CSlavePort * const I2C_MAX14690; -extern I2CSlavePort * const I2C_MAG3110; -extern I2CSlavePort * const I2C_AS7000; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; - -extern AnalogTemperatureSensor * const TEMPERATURE_SENSOR; - -extern QSPIPort * const QSPI; -extern QSPIFlash * const QSPI_FLASH; - -extern ICE40LPDevice * const ICE40LP; -extern SPISlavePort * const DIALOG_SPI; - -extern MicDevice * const MIC; - -extern HRMDevice * const HRM; - -#if BOARD_CUTTS_BB -extern TouchSensor * const EWD1000; -#endif diff --git a/src/fw/board/boards/board_silk.c b/src/fw/board/boards/board_silk.c deleted file mode 100644 index beb502f914..0000000000 --- a/src/fw/board/boards/board_silk.c +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/exti.h" -#include "drivers/flash/qspi_flash_definitions.h" -#include "drivers/hrm/as7000/as7000.h" -#include "drivers/i2c_definitions.h" -#include "drivers/mic/stm32/dfsdm_definitions.h" -#include "drivers/qspi_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/spi_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/temperature/analog.h" -#include "drivers/voltage_monitor.h" -#include "flash_region/flash_region.h" -#include "util/units.h" - -#define DIALOG_SPI_DMA_PRIORITY (0x0b) -// Make sure that the DMA IRQ is handled before EXTI: -// See comments in host/host_transport.c prv_int_exti_cb() -_Static_assert(DIALOG_SPI_DMA_PRIORITY < EXTI_PRIORITY, "Dialog SPI DMA priority too low!"); - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 4); // DMA2_STREAM2_DEVICE - Sharp SPI TX -CREATE_DMA_STREAM(2, 1); // DMA1_STREAM2_DEVICE - Accessory UART RX -CREATE_DMA_STREAM(2, 2); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(2, 3); // DMA2_STREAM0_DEVICE - Dialog SPI RX -CREATE_DMA_STREAM(2, 5); // DMA2_STREAM1_DEVICE - Dialog SPI TX -CREATE_DMA_STREAM(2, 6); // DMA2_STREAM4_DEVICE - DFSDM -CREATE_DMA_STREAM(2, 7); // DMA2_STREAM7_DEVICE - QSPI - -// DMA Requests -// - On DMA1 we just have have "Sharp SPI TX" so just set its priority to "High" since it doesn't -// matter. -// - On DMA2 we have "Accessory UART RX", "Debug UART RX", "Dialog SPI RX", "DIALOG SPI TX", -// "DFSDM", and "QSPI". We want "DFSDM", "Accessory UART RX", "Debug UART RX", and "Dialog SPI RX" -// to have a very high priority because their peripheral buffers may overflow if the DMA stream -// doesn't read from them in a while. After that, give the remaining "Dialog SPI TX" and "QSPI" -// both a high priority. - -static DMARequestState s_sharp_spi_tx_dma_request_state; -static DMARequest SHARP_SPI_TX_DMA_REQUEST = { - .state = &s_sharp_spi_tx_dma_request_state, - .stream = &DMA1_STREAM4_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_High, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const SHARP_SPI_TX_DMA = &SHARP_SPI_TX_DMA_REQUEST; - -static DMARequestState s_accessory_uart_dma_request_state; -static DMARequest ACCESSORY_UART_RX_DMA_REQUEST = { - .state = &s_accessory_uart_dma_request_state, - .stream = &DMA2_STREAM1_DEVICE, - .channel = 5, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA2_STREAM2_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_dialog_spi_rx_dma_request_state; -static DMARequest DIALOG_SPI_RX_DMA_REQUEST = { - .state = &s_dialog_spi_rx_dma_request_state, - .stream = &DMA2_STREAM3_DEVICE, - .channel = 2, - .irq_priority = DIALOG_SPI_DMA_PRIORITY, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_dialog_spi_tx_dma_request_state; -static DMARequest DIALOG_SPI_TX_DMA_REQUEST = { - .state = &s_dialog_spi_tx_dma_request_state, - .stream = &DMA2_STREAM5_DEVICE, - .channel = 5, - .irq_priority = DIALOG_SPI_DMA_PRIORITY, - .priority = DMARequestPriority_High, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_dfsdm_dma_request_state; -static DMARequest DFSDM_DMA_REQUEST = { - .state = &s_dfsdm_dma_request_state, - .stream = &DMA2_STREAM6_DEVICE, - .channel = 3, - .irq_priority = 0x0f, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Word, -}; - -static DMARequestState s_qspi_dma_request_state; -static DMARequest QSPI_DMA_REQUEST = { - .state = &s_qspi_dma_request_state, - .stream = &DMA2_STREAM7_DEVICE, - .channel = 3, - .irq_priority = 0x0f, - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Word, -}; - - -// UART DEVICES - -static UARTDeviceState s_bt_bootrom_rx_uart_state; -static UARTDevice BT_RX_BOOTROM_UART_DEVICE = { - .state = &s_bt_bootrom_rx_uart_state, - .periph = USART6, - .rx_gpio = { GPIOA, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF_USART6 }, - .rcc_apb_periph = RCC_APB2Periph_USART6, - .tx_gpio = { 0 } -}; - -static UARTDeviceState s_bt_bootrom_tx_uart_state; -static UARTDevice BT_TX_BOOTROM_UART_DEVICE = { - .state = &s_bt_bootrom_tx_uart_state, - .periph = USART2, - .tx_gpio = { GPIOA, GPIO_Pin_2, GPIO_PinSource2, GPIO_AF_USART2 }, - .rcc_apb_periph = RCC_APB1Periph_USART2, - .rx_gpio = { 0 } -}; - -UARTDevice * const BT_TX_BOOTROM_UART = &BT_TX_BOOTROM_UART_DEVICE; -UARTDevice * const BT_RX_BOOTROM_UART = &BT_RX_BOOTROM_UART_DEVICE; - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_USART1 - }, - .rx_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_USART1 - }, - .periph = USART1, - .irq_channel = USART1_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB2Periph_USART1, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART1, uart_irq_handler, DBG_UART); - -static UARTDeviceState s_accessory_uart_state; -static UARTDevice ACCESSORY_UART_DEVICE = { - .state = &s_accessory_uart_state, - .half_duplex = true, - .tx_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART6 - }, - .periph = USART6, - .irq_channel = USART6_IRQn, - .irq_priority = 0xb, - .rcc_apb_periph = RCC_APB2Periph_USART6, - .rx_dma = &ACCESSORY_UART_RX_DMA_REQUEST -}; -UARTDevice * const ACCESSORY_UART = &ACCESSORY_UART_DEVICE; -IRQ_MAP(USART6, uart_irq_handler, ACCESSORY_UART); - - -// I2C DEVICES - -static I2CBusState I2C_PMIC_HRM_BUS_STATE = {}; - -static const I2CBusHal I2C_PMIC_HRM_BUS_HAL = { - .i2c = I2C3, - .clock_ctrl = RCC_APB1Periph_I2C3, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_2, - .ev_irq_channel = I2C3_EV_IRQn, - .er_irq_channel = I2C3_ER_IRQn, -}; - -static const I2CBus I2C_PMIC_HRM_BUS = { - .state = &I2C_PMIC_HRM_BUS_STATE, - .hal = &I2C_PMIC_HRM_BUS_HAL, - .scl_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_I2C3 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF9_I2C3 - }, - .stop_mode_inhibitor = InhibitorI2C3, - .name = "I2C_PMIC" -}; - -static const I2CSlavePort I2C_SLAVE_AS3701B = { - .bus = &I2C_PMIC_HRM_BUS, - .address = 0x80 -}; - -static const I2CSlavePort I2C_SLAVE_AS7000 = { - .bus = &I2C_PMIC_HRM_BUS, - .address = 0x60 -}; - -I2CSlavePort * const I2C_AS3701B = &I2C_SLAVE_AS3701B; -I2CSlavePort * const I2C_AS7000 = &I2C_SLAVE_AS7000; - -IRQ_MAP(I2C3_EV, i2c_hal_event_irq_handler, &I2C_PMIC_HRM_BUS); -IRQ_MAP(I2C3_ER, i2c_hal_error_irq_handler, &I2C_PMIC_HRM_BUS); - - -// VOLTAGE MONITOR DEVICES -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_13, - .clock_ctrl = RCC_APB2Periph_ADC1, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_3, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_5, - .clock_ctrl = RCC_APB2Periph_ADC1, - .input = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_5, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_TEMPERATURE_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_TempSensor, - .clock_ctrl = RCC_APB2Periph_ADC1, - // .input not applicable -}; - -const VoltageMonitorDevice * VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -const VoltageMonitorDevice * VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; -const VoltageMonitorDevice * VOLTAGE_MONITOR_TEMPERATURE = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE; - -// Temperature sensor -// STM32F412 datasheet rev 2 -// Section 6.3.21 -AnalogTemperatureSensor const TEMPERATURE_SENSOR_DEVICE = { - .voltage_monitor = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE, - .millivolts_ref = 760, - .millidegrees_ref = 25000, - .slope_numerator = 5, - .slope_denominator = 2000, -}; - -AnalogTemperatureSensor * const TEMPERATURE_SENSOR = &TEMPERATURE_SENSOR_DEVICE; - - -// -// SPI Bus configuration -// - -static SPIBusState DIALOG_SPI_BUS_STATE = { }; -static const SPIBus DIALOG_SPI_BUS = { - .state = &DIALOG_SPI_BUS_STATE, - .spi = SPI5, - .spi_sclk = { GPIOB, GPIO_Pin_0, GPIO_PinSource0, GPIO_AF6_SPI5 }, - .spi_miso = { GPIOA, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF6_SPI5 }, - .spi_mosi = { GPIOA, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF6_SPI5 }, - .spi_sclk_speed = GPIO_Speed_50MHz, - // DA14680_FS v1.4 page 89: - // "In slave mode the internal SPI clock must be more than four times the SPIx_CLK" - // The system clock is 16MHz, so don't use more than 4MHz. - .spi_clock_speed_hz = MHZ_TO_HZ(4) -}; - -// -// SPI Slave port configuration -// - -static SPISlavePortState DIALOG_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort DIALOG_SPI_SLAVE_PORT = { - .slave_state = &DIALOG_SPI_SLAVE_PORT_STATE, - .spi_bus = &DIALOG_SPI_BUS, - .spi_scs = { GPIOB, GPIO_Pin_1, false }, - .spi_direction = SpiDirection_2LinesFullDuplex, - .spi_cpol = SpiCPol_Low, - .spi_cpha = SpiCPha_1Edge, - .spi_first_bit = SpiFirstBit_MSB, - .rx_dma = &DIALOG_SPI_RX_DMA_REQUEST, - .tx_dma = &DIALOG_SPI_TX_DMA_REQUEST -}; -SPISlavePort * const DIALOG_SPI = &DIALOG_SPI_SLAVE_PORT; - - -// HRM DEVICE -static HRMDeviceState s_hrm_state; -static HRMDevice HRM_DEVICE = { - .state = &s_hrm_state, - .handshake_int = { EXTI_PortSourceGPIOA, 15 }, - .int_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_15 - }, - .en_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_1, - .active_high = false, - }, - .i2c_slave = &I2C_SLAVE_AS7000, -}; -HRMDevice * const HRM = &HRM_DEVICE; - - -// QSPI -static QSPIPortState s_qspi_port_state; -static QSPIPort QSPI_PORT = { - .state = &s_qspi_port_state, - .clock_speed_hz = MHZ_TO_HZ(50), - .auto_polling_interval = 16, - .clock_ctrl = RCC_AHB3Periph_QSPI, - .cs_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF10_QUADSPI, - }, - .clk_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_2, - .gpio_pin_source = GPIO_PinSource2, - .gpio_af = GPIO_AF9_QUADSPI, - }, - .data_gpio = { - { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF9_QUADSPI, - }, - { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF9_QUADSPI, - }, - { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF9_QUADSPI, - }, - { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF9_QUADSPI, - }, - }, - .dma = &QSPI_DMA_REQUEST, -}; -QSPIPort * const QSPI = &QSPI_PORT; - -static QSPIFlashState s_qspi_flash_state; -static QSPIFlash QSPI_FLASH_DEVICE = { - .state = &s_qspi_flash_state, - .qspi = &QSPI_PORT, - .default_fast_read_ddr_enabled = false, - .reset_gpio = { GPIO_Port_NULL }, -}; -QSPIFlash * const QSPI_FLASH = &QSPI_FLASH_DEVICE; - - -static MicDeviceState s_mic_state; -static MicDevice MIC_DEVICE = { - .state = &s_mic_state, - - .filter = (DFSDM_TypeDef *) DFSDM1_Filter0_BASE, - .channel = DFSDM1_Channel2, - .extremes_detector_channel = DFSDM_ExtremChannel2, - .regular_channel = DFSDM_RegularChannel2, - .pdm_frequency = MHZ_TO_HZ(2), - .rcc_apb_periph = RCC_APB2Periph_DFSDM, - .dma = &DFSDM_DMA_REQUEST, - .ck_gpio = { GPIOC, GPIO_Pin_2, GPIO_PinSource2, GPIO_AF8_DFSDM1 }, - .sd_gpio = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF8_DFSDM1 }, - .power_on_delay_ms = 50, - .settling_delay_ms = 0, - .default_volume = 64, - .final_right_shift = 11, -}; -MicDevice * const MIC = &MIC_DEVICE; - - -void board_early_init(void) { -} - -void board_init(void) { - i2c_init(&I2C_PMIC_HRM_BUS); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); - - qspi_init(QSPI, BOARD_NOR_FLASH_SIZE); -} diff --git a/src/fw/board/boards/board_silk.h b/src/fw/board/boards/board_silk.h deleted file mode 100644 index 3c3a4127d9..0000000000 --- a/src/fw/board/boards/board_silk.h +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -static const BoardConfig BOARD_CONFIG = { - .ambient_light_dark_threshold = 150, - .ambient_k_delta_threshold = 50, - .photo_en = { GPIOC, GPIO_Pin_0, true }, - .als_always_on = true, - - .dbgserial_int = { EXTI_PortSourceGPIOB, 5 }, - - // new sharp display requires 30/60Hz so we feed it directly from PMIC - .lcd_com = { 0 }, - - .backlight_on_percent = 25, - .backlight_max_duty_cycle_percent = 67, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .has_mic = true, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = - { "Back", GPIOC, GPIO_Pin_13, { EXTI_PortSourceGPIOC, 13 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = - { "Up", GPIOD, GPIO_Pin_2, { EXTI_PortSourceGPIOD, 2 }, GPIO_PuPd_DOWN }, - [BUTTON_ID_SELECT] = - { "Select", GPIOH, GPIO_Pin_0, { EXTI_PortSourceGPIOH, 0 }, GPIO_PuPd_DOWN }, - [BUTTON_ID_DOWN] = - { "Down", GPIOH, GPIO_Pin_1, { EXTI_PortSourceGPIOH, 1 }, GPIO_PuPd_DOWN }, - }, - .button_com = { 0 }, - .active_high = true, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOC, 7 }, - .pmic_int_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_7, - }, - - .battery_vmon_scale = { - // Battery voltage is scaled down by a pair of resistors: - // - R13 on the top @ 47k - // - R15 on the bottom @ 30.1k - // (R13 + R15) / R15 = 77.1 / 30.1 - .numerator = 771, - .denominator = 301, - }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - - .charging_status_led_voltage_compensation = 0, - -#if defined(IS_BIGBOARD) && !defined(BATTERY_DEBUG) - // We don't use the same batteries on all bigboards, so set a safe cutoff voltage of 4.2V. - // Please do not change this! - .charging_cutoff_voltage = 4200, -#else - .charging_cutoff_voltage = 4300, -#endif - - .low_power_threshold = 5, - - // Based on measurements from v4.0-beta16. - // Typical Connected Current at VBAT without HRM ~520uA - // Added draw with HRM on : ~1.5mA ==> Average impact (5% per hour + 1 hour continuous / day) - // (.05 * 23/24 + 1.0 * 1/24) * 1.5mA = ~134uA - // Assume ~150uA or so for notifications & user interaction - // Total Hours = 125 mA * hr / (.520 + .134 + 150)mA = 155 hours - .battery_capacity_hours = 155, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, -#if IS_BIGBOARD - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = false, -#else - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, -#endif - // This is affected by the acceleromter's configured ODR, so this value - // will need to be tuned again once we stop locking the BMA255 to an ODR of - // 125 Hz. - .shake_thresholds[AccelThresholdHigh] = 64, - .shake_thresholds[AccelThresholdLow] = 0xf, - .double_tap_threshold = 12500, - }, - .accel_int_gpios = { - [0] = { GPIOA, GPIO_Pin_6 }, - [1] = { GPIOA, GPIO_Pin_3 }, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOA, 6 }, - [1] = { EXTI_PortSourceGPIOA, 3 } - }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Pwm, - .ctl = { 0 }, - .pwm = { - .output = { GPIOA, GPIO_Pin_7, true }, - .timer = { - .peripheral = TIM14, - .config_clock = RCC_APB1Periph_TIM14, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOA, GPIO_Pin_7, GPIO_PinSource7, GPIO_AF_TIM14 }, - }, - .vsys_scale = 3300, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm | ActuatorOptions_Ctl, - .ctl = { GPIOB, GPIO_Pin_13, true }, - .pwm = { - .output = { GPIOC, GPIO_Pin_6, true }, - .timer = { - .peripheral = TIM3, - .config_clock = RCC_APB1Periph_TIM3, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOC, GPIO_Pin_6, GPIO_PinSource6, GPIO_AF_TIM3 }, - }, -}; - -#define ACCESSORY_UART_IS_SHARED_WITH_BT 1 -static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = { - .exti = { EXTI_PortSourceGPIOA, 11 }, -}; - -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = DA14681, - .reset = { GPIOC, GPIO_Pin_5, true }, - .wakeup = { - .int_gpio = { GPIOC, GPIO_Pin_4 }, - .int_exti = { EXTI_PortSourceGPIOC, 4 }, - }, -}; - -static const BoardConfigBTSPI BOARD_CONFIG_BT_SPI = { - .cs = { GPIOB, GPIO_Pin_1, false }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { - .spi = SPI2, - .spi_gpio = GPIOB, - .spi_clk = RCC_APB1Periph_SPI2, - .spi_clk_periph = SpiPeriphClockAPB1, - - .clk = { GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_SPI2 }, - .mosi = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .cs = { GPIOB, GPIO_Pin_9, true }, - - .on_ctrl = { GPIOA, GPIO_Pin_0, true }, - .on_ctrl_otype = GPIO_OType_PP, -}; - -#define DIALOG_TIMER_IRQ_HANDLER TIM6_IRQHandler -static const TimerIrqConfig BOARD_BT_WATCHDOG_TIMER = { - .timer = { - .peripheral = TIM6, - .config_clock = RCC_APB1Periph_TIM6, - }, - .irq_channel = TIM6_IRQn, -}; - -extern DMARequest * const COMPOSITOR_DMA; -extern DMARequest * const SHARP_SPI_TX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; -extern UARTDevice * const ACCESSORY_UART; - -extern UARTDevice * const BT_TX_BOOTROM_UART; -extern UARTDevice * const BT_RX_BOOTROM_UART; - -extern I2CSlavePort * const I2C_AS3701B; -extern I2CSlavePort * const I2C_AS7000; - -extern const VoltageMonitorDevice * VOLTAGE_MONITOR_ALS; -extern const VoltageMonitorDevice * VOLTAGE_MONITOR_BATTERY; - -extern const AnalogTemperatureSensor * const TEMPERATURE_SENSOR; - -extern HRMDevice * const HRM; - -extern QSPIPort * const QSPI; -extern QSPIFlash * const QSPI_FLASH; - -extern MicDevice * const MIC; - -extern SPISlavePort * const DIALOG_SPI; diff --git a/src/fw/board/boards/board_snowy.c b/src/fw/board/boards/board_snowy.c deleted file mode 100644 index f297d06e9d..0000000000 --- a/src/fw/board/boards/board_snowy.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/display/ice40lp/ice40lp_definitions.h" -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/spi_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/temperature/analog.h" -#include "drivers/voltage_monitor.h" -#include "util/units.h" - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 3); // DMA1_STREAM3_DEVICE - Mic I2S RX -CREATE_DMA_STREAM(1, 6); // DMA1_STREAM6_DEVICE - Accessory UART RX -CREATE_DMA_STREAM(2, 0); // DMA2_STREAM0_DEVICE - Compositor DMA -CREATE_DMA_STREAM(2, 5); // DMA2_STREAM5_DEVICE - ICE40LP TX -CREATE_DMA_STREAM(2, 2); // DMA2_STREAM4_DEVICE - Bluetooth UART RX - -// DMA Requests - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_mic_i2s_rx_dma_request_state; -static DMARequest MIC_I2S_RX_DMA_REQUEST = { - .state = &s_mic_i2s_rx_dma_request_state, - .stream = &DMA1_STREAM3_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_HalfWord, -}; -DMARequest * const MIC_I2S_RX_DMA = &MIC_I2S_RX_DMA_REQUEST; - -static DMARequestState s_accessory_uart_dma_request_state; -static DMARequest ACCESSORY_UART_RX_DMA_REQUEST = { - .state = &s_accessory_uart_dma_request_state, - .stream = &DMA1_STREAM6_DEVICE, - .channel = 5, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_compositor_dma_request_state; -static DMARequest COMPOSITOR_DMA_REQUEST = { - .state = &s_compositor_dma_request_state, - .stream = &DMA2_STREAM0_DEVICE, - .channel = 0, - .irq_priority = 11, - .priority = DMARequestPriority_High, - .type = DMARequestType_MemoryToMemory, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const COMPOSITOR_DMA = &COMPOSITOR_DMA_REQUEST; - -static DMARequestState s_ice40lp_spi_tx_dma_request_state; -static DMARequest ICE40LP_SPI_TX_DMA_REQUEST = { - .state = &s_ice40lp_spi_tx_dma_request_state, - .stream = &DMA2_STREAM5_DEVICE, - .channel = 1, - // Use the same priority as the EXTI handlers so that the DMA-complete - // handler doesn't preempt the display BUSY (INTn) handler. - .irq_priority = 0x0e, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_bluetooth_uart_rx_dma_request_state; -static DMARequest BLUETOOTH_UART_RX_DMA_REQUEST = { - .state = &s_bluetooth_uart_rx_dma_request_state, - .stream = &DMA2_STREAM2_DEVICE, - .channel = 4, - .irq_priority = 0x0e, - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_USART3 - }, - .rx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - -static UARTDeviceState s_accessory_uart_state; -static UARTDevice ACCESSORY_UART_DEVICE = { - .state = &s_accessory_uart_state, - .half_duplex = true, - .tx_gpio = { - .gpio = GPIOE, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF_UART8 - }, - .periph = UART8, - .irq_channel = UART8_IRQn, - .irq_priority = 0xb, - .rcc_apb_periph = RCC_APB1Periph_UART8, - .rx_dma = &ACCESSORY_UART_RX_DMA_REQUEST -}; -UARTDevice * const ACCESSORY_UART = &ACCESSORY_UART_DEVICE; -IRQ_MAP(UART8, uart_irq_handler, ACCESSORY_UART); - -static UARTDeviceState s_bluetooth_uart_state; -static UARTDevice BLUETOOTH_UART_DEVICE = { - .state = &s_bluetooth_uart_state, - .tx_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_USART1 - }, - .rx_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_USART1 - }, - .cts_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART1 - }, - .rts_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_12, - .gpio_pin_source = GPIO_PinSource12, - .gpio_af = GPIO_AF_USART1 - }, - .enable_flow_control = true, - .periph = USART1, - .irq_channel = USART1_IRQn, - .irq_priority = 0xe, - .rcc_apb_periph = RCC_APB2Periph_USART1, - // .rx_dma = &BLUETOOTH_UART_RX_DMA_REQUEST -}; -UARTDevice * const BLUETOOTH_UART = &BLUETOOTH_UART_DEVICE; -IRQ_MAP(USART1, uart_irq_handler, BLUETOOTH_UART); - -// I2C DEVICES - -static I2CBusState I2C_PMIC_MAG_BUS_STATE = {}; - -static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_PMIC_MAG_BUS = { - .state = &I2C_PMIC_MAG_BUS_STATE, - .hal = &I2C_PMIC_MAG_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_PMIC_MAG" -}; - -extern void i2c_rail_ctl_pmic(I2CBus *device, bool enable); - -static I2CBusState I2C_MFI_BUS_STATE = {}; - -static const I2CBusHal I2C_MFI_BUS_HAL = { - .i2c = I2C2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, -}; - -static const I2CBus I2C_MFI_BUS = { - .state = &I2C_MFI_BUS_STATE, - .hal = &I2C_MFI_BUS_HAL, - .scl_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF_I2C2 - }, - .sda_gpio = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_0, - .gpio_pin_source = GPIO_PinSource0, - .gpio_af = GPIO_AF_I2C2 - }, - .stop_mode_inhibitor = InhibitorI2C2, - .rail_ctl_fn = i2c_rail_ctl_pmic, - .name = "I2C_MFI" -}; - -static const I2CSlavePort I2C_SLAVE_MAX14690 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x50 -}; - -static const I2CSlavePort I2C_SLAVE_MFI = { - .bus = &I2C_MFI_BUS, - .address = 0x20 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x1C -}; - -I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690; -I2CSlavePort * const I2C_MFI = &I2C_SLAVE_MFI; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; - -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS); -IRQ_MAP(I2C2_EV, i2c_hal_event_irq_handler, &I2C_MFI_BUS); -IRQ_MAP(I2C2_ER, i2c_hal_error_irq_handler, &I2C_MFI_BUS); - - -// VOLTAGE MONITOR DEVICES - -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_2, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_2, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_1, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - } -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_TEMPERATURE_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_TempSensor, - .clock_ctrl = RCC_APB2Periph_ADC1, - // .input not applicable -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_TEMPERATURE = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE; - -// Temperature sensor -// STM32F439 datasheet rev 5 -// Section 6.3.22 -const AnalogTemperatureSensor TEMPERATURE_SENSOR_DEVICE = { - .voltage_monitor = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE, - .millivolts_ref = 760, - .millidegrees_ref = 25000, - .slope_numerator = 5, - .slope_denominator = 2000, -}; - -const AnalogTemperatureSensor * const TEMPERATURE_SENSOR = &TEMPERATURE_SENSOR_DEVICE; - -// -// SPI Bus configuration -// - -static SPIBusState BMI160_SPI_BUS_STATE = {}; -static const SPIBus BMI160_SPI_BUS = { - .state = &BMI160_SPI_BUS_STATE, - .spi = SPI1, - .spi_sclk = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_5, - .gpio_pin_source = GPIO_PinSource5, - .gpio_af = GPIO_AF_SPI1 - }, - .spi_miso = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF_SPI1 - }, - .spi_mosi = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_SPI1 - }, - .spi_sclk_speed = GPIO_Speed_50MHz, - .spi_clock_speed_hz = MHZ_TO_HZ(5), -}; - -static SPIBusState ICE40LP_SPI_BUS_STATE = {}; -static const SPIBus ICE40LP_SPI_BUS = { - .state = &ICE40LP_SPI_BUS_STATE, - .spi = SPI6, - .spi_sclk = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_13, - .gpio_pin_source = GPIO_PinSource13, - .gpio_af = GPIO_AF_SPI6 - }, - .spi_miso = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_12, - .gpio_pin_source = GPIO_PinSource12, - .gpio_af = GPIO_AF_SPI6 - }, - .spi_mosi = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_14, - .gpio_pin_source = GPIO_PinSource14, - .gpio_af = GPIO_AF_SPI6 - }, - .spi_sclk_speed = GPIO_Speed_25MHz, - .spi_clock_speed_hz = MHZ_TO_HZ(16), -}; - -// -// SPI Slave port configuration -// -static SPISlavePortState BMI160_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort BMI160_SPI_SLAVE_PORT = { - .slave_state = &BMI160_SPI_SLAVE_PORT_STATE, - .spi_bus = &BMI160_SPI_BUS, - .spi_scs = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_4, - .active_high = false - }, - .spi_direction = SpiDirection_2LinesFullDuplex, - .spi_cpol = SpiCPol_Low, - .spi_cpha = SpiCPha_1Edge, - .spi_first_bit = SpiFirstBit_MSB, -}; -SPISlavePort * const BMI160_SPI = &BMI160_SPI_SLAVE_PORT; - -static SPISlavePortState ICE40LP_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort ICE40LP_SPI_SLAVE_PORT = { - .slave_state = &ICE40LP_SPI_SLAVE_PORT_STATE, - .spi_bus = &ICE40LP_SPI_BUS, - .spi_scs = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_8, - .active_high = false - }, - .spi_direction = SpiDirection_1LineTx, - .spi_cpol = SpiCPol_High, - .spi_cpha = SpiCPha_2Edge, - .spi_first_bit = SpiFirstBit_MSB, - .tx_dma = &ICE40LP_SPI_TX_DMA_REQUEST -}; - -// -// iCE40LP configuration -// -static ICE40LPDeviceState s_ice40lp_state; -static ICE40LPDevice ICE40LP_DEVICE = { - .state = &s_ice40lp_state, - - .spi_port = &ICE40LP_SPI_SLAVE_PORT, - .base_spi_frequency = MHZ_TO_HZ(16), - .fast_spi_frequency = MHZ_TO_HZ(32), - .creset = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_15, - .active_high = true, - }, - .cdone = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_9, - }, - .busy = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_10, - }, - .cdone_exti = { - .exti_port_source = EXTI_PortSourceGPIOG, - .exti_line = 9, - }, - .busy_exti = { - .exti_port_source = EXTI_PortSourceGPIOG, - .exti_line = 10, - }, - .use_6v6_rail = true, -}; -ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE; - - -void board_early_init(void) { - spi_slave_port_init(ICE40LP->spi_port); -} - -void board_init(void) { - i2c_init(&I2C_PMIC_MAG_BUS); - i2c_init(&I2C_MFI_BUS); - spi_slave_port_init(BMI160_SPI); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); -} diff --git a/src/fw/board/boards/board_snowy.h b/src/fw/board/boards/board_snowy.h deleted file mode 100644 index f70bcaf3cb..0000000000 --- a/src/fw/board/boards/board_snowy.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// ---------------------------------------------- -// Board definitions for Snowy EVT2 and similar -// ---------------------------------------------- -// -// This includes snowy_evt2, snowy_dvt and snowy_bb2. Except for a couple of -// minor quirks, all of the boards using this file are electrically identical. - -#include "drivers/imu/bmi160/bmi160.h" -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -static const BoardConfig BOARD_CONFIG = { - .has_mic = true, - .mic_config = { - .i2s_ck = { GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_SPI2 }, - .i2s_sd = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .spi = SPI2, - .spi_clock_ctrl = RCC_APB1Periph_SPI2, -#ifdef IS_BIGBOARD - .gain = 100, -#else - .gain = 250, -#endif - }, - -#ifdef BOARD_SNOWY_S3 - .ambient_light_dark_threshold = 3220, -#else - .ambient_light_dark_threshold = 3130, -#endif - .ambient_k_delta_threshold = 96, - .photo_en = { GPIOA, GPIO_Pin_3, true }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 12 }, - .dbgserial_int_gpio = { GPIOC, GPIO_Pin_12 }, - - // Only used with Sharp displays - .lcd_com = { 0 }, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .backlight_on_percent = 45, - .backlight_max_duty_cycle_percent = 100, - - .fpc_pinstrap_1 = { GPIOB, GPIO_Pin_0 }, - .fpc_pinstrap_2 = { GPIOC, GPIO_Pin_5 }, - -#ifdef IS_BIGBOARD - .num_avail_gpios = 140, -#else - .num_avail_gpios = 114, -#endif -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_UP }, - [BUTTON_ID_SELECT] = { "Select", GPIOG, GPIO_Pin_1, { EXTI_PortSourceGPIOG, 1 }, GPIO_PuPd_UP }, - [BUTTON_ID_DOWN] = { "Down", GPIOG, GPIO_Pin_2, { EXTI_PortSourceGPIOG, 2 }, GPIO_PuPd_UP }, - }, - - .button_com = { 0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOG, 7 }, - .pmic_int_gpio = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_7, - }, - - .rail_4V5_ctrl = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_2, - .active_high = true, - }, - .rail_6V6_ctrl = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - .rail_6V6_ctrl_otype = GPIO_OType_OD, - - .battery_vmon_scale = { - // The PMIC divides the battery voltage by a ratio of 3:1 in order to move it down to a voltage - // our ADC is capable of reading. The battery voltage varies around 4V~ and we're only capable - // of reading up to 1.8V in the ADC. - .numerator = 3, - .denominator = 1, - }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - -#if defined(IS_BIGBOARD) && !defined(BATTERY_DEBUG) - .charging_cutoff_voltage = 4200, -#else - .charging_cutoff_voltage = 4300, -#endif - .charging_status_led_voltage_compensation = 0, - -#ifdef BOARD_SNOWY_S3 - .low_power_threshold = 2, - .battery_capacity_hours = 204, -#else - .low_power_threshold = 3, - .battery_capacity_hours = 144, -#endif -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { -#ifdef IS_BIGBOARD - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = true, -#else - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = false, -#endif - .shake_thresholds[AccelThresholdHigh] = 0x64, - .shake_thresholds[AccelThresholdLow] = 0xf, - .double_tap_threshold = 12500, - }, - .accel_int_gpios = { - [0] = { GPIOG, GPIO_Pin_5 }, - [1] = { GPIOG, GPIO_Pin_6 }, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOG, 5 }, - [1] = { EXTI_PortSourceGPIOG, 6 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { -#ifdef IS_BIGBOARD - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, -#else - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = false, -#endif - }, - .mag_int_gpio = { GPIOF, GPIO_Pin_14 }, - .mag_int = { EXTI_PortSourceGPIOF, 14 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl | ActuatorOptions_Pwm | ActuatorOptions_HBridge, - .ctl = { GPIOF, GPIO_Pin_4, true }, - .pwm = { - .output = { GPIOB, GPIO_Pin_8, true }, - .timer = { - .peripheral = TIM10, - .config_clock = RCC_APB2Periph_TIM10, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF_TIM10 }, - }, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm, - .ctl = {0}, - .pwm = { - .output = { GPIOB, GPIO_Pin_14, true }, - .timer = { - .peripheral = TIM12, - .config_clock = RCC_APB1Periph_TIM12, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF_TIM12 }, - }, -}; - -static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = { - .power_en = { GPIOF, GPIO_Pin_13, true }, - .int_gpio = { GPIOE, GPIO_Pin_0 }, - .exti = { EXTI_PortSourceGPIOE, 0 }, -}; - -#define BOARD_BT_USART_IRQ_HANDLER USART1_IRQHandler -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564B, - .shutdown = { GPIOB, GPIO_Pin_12, false }, - .wakeup = { - .int_gpio = { GPIOA, GPIO_Pin_11 }, - .int_exti = { EXTI_PortSourceGPIOA, 11 }, - }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -extern DMARequest * const COMPOSITOR_DMA; -extern DMARequest * const MIC_I2S_RX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; -extern UARTDevice * const ACCESSORY_UART; -extern UARTDevice * const BLUETOOTH_UART; - -extern SPISlavePort * const BMI160_SPI; - -extern I2CSlavePort * const I2C_MAG3110; -extern I2CSlavePort * const I2C_MAX14690; -extern I2CSlavePort * const I2C_MFI; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; - -extern const AnalogTemperatureSensor * const TEMPERATURE_SENSOR; - -static MicDevice * const MIC = (void *)0; - -extern ICE40LPDevice * const ICE40LP; diff --git a/src/fw/board/boards/board_spalding_evt.c b/src/fw/board/boards/board_spalding_evt.c deleted file mode 100644 index 3d45b37b59..0000000000 --- a/src/fw/board/boards/board_spalding_evt.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/display/ice40lp/ice40lp_definitions.h" -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/spi_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/temperature/analog.h" -#include "drivers/voltage_monitor.h" -#include "util/units.h" - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 3); // DMA1_STREAM3_DEVICE - Mic I2S RX -CREATE_DMA_STREAM(1, 6); // DMA1_STREAM6_DEVICE - Accessory UART RX -CREATE_DMA_STREAM(2, 0); // DMA2_STREAM0_DEVICE - Compositor DMA -CREATE_DMA_STREAM(2, 5); // DMA2_STREAM5_DEVICE - ICE40LP TX -CREATE_DMA_STREAM(2, 2); // DMA2_STREAM4_DEVICE - Bluetooth UART RX - -// DMA Requests - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_mic_i2s_rx_dma_request_state; -static DMARequest MIC_I2S_RX_DMA_REQUEST = { - .state = &s_mic_i2s_rx_dma_request_state, - .stream = &DMA1_STREAM3_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_HalfWord, -}; -DMARequest * const MIC_I2S_RX_DMA = &MIC_I2S_RX_DMA_REQUEST; - -static DMARequestState s_accessory_uart_dma_request_state; -static DMARequest ACCESSORY_UART_RX_DMA_REQUEST = { - .state = &s_accessory_uart_dma_request_state, - .stream = &DMA1_STREAM6_DEVICE, - .channel = 5, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_compositor_dma_request_state; -static DMARequest COMPOSITOR_DMA_REQUEST = { - .state = &s_compositor_dma_request_state, - .stream = &DMA2_STREAM0_DEVICE, - .channel = 0, - .irq_priority = 11, - .priority = DMARequestPriority_High, - .type = DMARequestType_MemoryToMemory, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const COMPOSITOR_DMA = &COMPOSITOR_DMA_REQUEST; - -static DMARequestState s_ice40lp_spi_tx_dma_request_state; -static DMARequest ICE40LP_SPI_TX_DMA_REQUEST = { - .state = &s_ice40lp_spi_tx_dma_request_state, - .stream = &DMA2_STREAM5_DEVICE, - .channel = 1, - // Use the same priority as the EXTI handlers so that the DMA-complete - // handler doesn't preempt the display BUSY (INTn) handler. - .irq_priority = 0x0e, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_bluetooth_uart_rx_dma_request_state; -static DMARequest BLUETOOTH_UART_RX_DMA_REQUEST = { - .state = &s_bluetooth_uart_rx_dma_request_state, - .stream = &DMA2_STREAM2_DEVICE, - .channel = 4, - .irq_priority = 0x0e, - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_USART3 - }, - .rx_gpio = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - -static UARTDeviceState s_accessory_uart_state; -static UARTDevice ACCESSORY_UART_DEVICE = { - .state = &s_accessory_uart_state, - .half_duplex = true, - .tx_gpio = { - .gpio = GPIOE, - .gpio_pin = GPIO_Pin_1, - .gpio_pin_source = GPIO_PinSource1, - .gpio_af = GPIO_AF_UART8 - }, - .periph = UART8, - .irq_channel = UART8_IRQn, - .irq_priority = 0xb, - .rcc_apb_periph = RCC_APB1Periph_UART8, - .rx_dma = &ACCESSORY_UART_RX_DMA_REQUEST -}; -UARTDevice * const ACCESSORY_UART = &ACCESSORY_UART_DEVICE; -IRQ_MAP(UART8, uart_irq_handler, ACCESSORY_UART); - -static UARTDeviceState s_bluetooth_uart_state; -static UARTDevice BLUETOOTH_UART_DEVICE = { - .state = &s_bluetooth_uart_state, - .tx_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_USART1 - }, - .rx_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_USART1 - }, - .cts_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_USART1 - }, - .rts_gpio = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_12, - .gpio_pin_source = GPIO_PinSource12, - .gpio_af = GPIO_AF_USART1 - }, - .enable_flow_control = true, - .periph = USART1, - .irq_channel = USART1_IRQn, - .irq_priority = 0xe, - .rcc_apb_periph = RCC_APB2Periph_USART1, - // .rx_dma = &BLUETOOTH_UART_RX_DMA_REQUEST -}; -UARTDevice * const BLUETOOTH_UART = &BLUETOOTH_UART_DEVICE; -IRQ_MAP(USART1, uart_irq_handler, BLUETOOTH_UART); - -// I2C DEVICES - -static I2CBusState I2C_PMIC_MAG_BUS_STATE = {}; - -static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_PMIC_MAG_BUS = { - .state = &I2C_PMIC_MAG_BUS_STATE, - .hal = &I2C_PMIC_MAG_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_PMIC_MAG" -}; - -extern void i2c_rail_ctl_pmic(I2CBus *device, bool enable); - -static const I2CSlavePort I2C_SLAVE_MAX14690 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x50 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_PMIC_MAG_BUS, - .address = 0x1C -}; - -I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; -I2CSlavePort * const I2C_MFI = NULL; - -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS); - - -// VOLTAGE MONITOR DEVICES - -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_2, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_2, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_1, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_1, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_TEMPERATURE_DEVICE = { - .adc = ADC1, - .adc_channel = ADC_Channel_TempSensor, - .clock_ctrl = RCC_APB2Periph_ADC1, - // .input not applicable -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_TEMPERATURE = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE; - -// Temperature sensor -// STM32F439 datasheet rev 5 -// Section 6.3.22 -const AnalogTemperatureSensor TEMPERATURE_SENSOR_DEVICE = { - .voltage_monitor = &VOLTAGE_MONITOR_TEMPERATURE_DEVICE, - .millivolts_ref = 760, - .millidegrees_ref = 25000, - .slope_numerator = 5, - .slope_denominator = 2000, -}; - -const AnalogTemperatureSensor * const TEMPERATURE_SENSOR = &TEMPERATURE_SENSOR_DEVICE; - -// -// SPI Bus configuration -// - -static SPIBusState BMI160_SPI_BUS_STATE = {}; -static const SPIBus BMI160_SPI_BUS = { - .state = &BMI160_SPI_BUS_STATE, - .spi = SPI1, - .spi_sclk = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_5, - .gpio_pin_source = GPIO_PinSource5, - .gpio_af = GPIO_AF_SPI1 - }, - .spi_miso = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_6, - .gpio_pin_source = GPIO_PinSource6, - .gpio_af = GPIO_AF_SPI1 - }, - .spi_mosi = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_SPI1 - }, - .spi_sclk_speed = GPIO_Speed_50MHz, - .spi_clock_speed_hz = MHZ_TO_HZ(5), -}; - -static SPIBusState ICE40LP_SPI_BUS_STATE = {}; -static const SPIBus ICE40LP_SPI_BUS = { - .state = &ICE40LP_SPI_BUS_STATE, - .spi = SPI6, - .spi_sclk = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_13, - .gpio_pin_source = GPIO_PinSource13, - .gpio_af = GPIO_AF_SPI6 - }, - .spi_miso = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_12, - .gpio_pin_source = GPIO_PinSource12, - .gpio_af = GPIO_AF_SPI6 - }, - .spi_mosi = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_14, - .gpio_pin_source = GPIO_PinSource14, - .gpio_af = GPIO_AF_SPI6 - }, - .spi_sclk_speed = GPIO_Speed_25MHz, - .spi_clock_speed_hz = MHZ_TO_HZ(16), -}; - -// -// SPI Slave port configuration -// -static SPISlavePortState BMI160_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort BMI160_SPI_SLAVE_PORT = { - .slave_state = &BMI160_SPI_SLAVE_PORT_STATE, - .spi_bus = &BMI160_SPI_BUS, - .spi_scs = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_4, - .active_high = false - }, - .spi_direction = SpiDirection_2LinesFullDuplex, - .spi_cpol = SpiCPol_Low, - .spi_cpha = SpiCPha_1Edge, - .spi_first_bit = SpiFirstBit_MSB, -}; -SPISlavePort * const BMI160_SPI = &BMI160_SPI_SLAVE_PORT; - -static SPISlavePortState ICE40LP_SPI_SLAVE_PORT_STATE = {}; -static SPISlavePort ICE40LP_SPI_SLAVE_PORT = { - .slave_state = &ICE40LP_SPI_SLAVE_PORT_STATE, - .spi_bus = &ICE40LP_SPI_BUS, - .spi_scs = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_8, - .active_high = false - }, - .spi_direction = SpiDirection_1LineTx, - .spi_cpol = SpiCPol_High, - .spi_cpha = SpiCPha_2Edge, - .spi_first_bit = SpiFirstBit_MSB, - .tx_dma = &ICE40LP_SPI_TX_DMA_REQUEST -}; - -// -// iCE40LP configuration -// -static ICE40LPDeviceState s_ice40lp_state; -static ICE40LPDevice ICE40LP_DEVICE = { - .state = &s_ice40lp_state, - - .spi_port = &ICE40LP_SPI_SLAVE_PORT, - .base_spi_frequency = MHZ_TO_HZ(16), - .fast_spi_frequency = MHZ_TO_HZ(32), - .creset = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_15, - .active_high = true, - }, - .cdone = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_9, - }, - .busy = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_10, - }, - .cdone_exti = { - .exti_port_source = EXTI_PortSourceGPIOG, - .exti_line = 9, - }, - .busy_exti = { - .exti_port_source = EXTI_PortSourceGPIOG, - .exti_line = 10, - }, - .use_6v6_rail = true, -}; -ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE; - - -void board_early_init(void) { - spi_slave_port_init(ICE40LP->spi_port); -} - -void board_init(void) { - i2c_init(&I2C_PMIC_MAG_BUS); - spi_slave_port_init(BMI160_SPI); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); -} diff --git a/src/fw/board/boards/board_spalding_evt.h b/src/fw/board/boards/board_spalding_evt.h deleted file mode 100644 index cb41696ca6..0000000000 --- a/src/fw/board/boards/board_spalding_evt.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// ------------------------------------ -// Board definitions for Spalding EVT -// ------------------------------------ - -#include "drivers/imu/bmi160/bmi160.h" -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_Bypass - -static const BoardConfig BOARD_CONFIG = { - .has_mic = true, - .mic_config = { - .i2s_ck = { GPIOB, GPIO_Pin_10, GPIO_PinSource10, GPIO_AF_SPI2 }, - .i2s_sd = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .spi = SPI2, - .spi_clock_ctrl = RCC_APB1Periph_SPI2, - .gain = 250, - }, - - .ambient_light_dark_threshold = 3130, - .ambient_k_delta_threshold = 96, - .photo_en = { GPIOA, GPIO_Pin_3, true }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 12 }, - .dbgserial_int_gpio = { GPIOC, GPIO_Pin_12 }, - - // Only used with Sharp displays - .lcd_com = { 0 }, - - .power_5v0_options = OptionNotPresent, - .power_ctl_5v0 = { 0 }, - - .backlight_on_percent = 25, - .backlight_max_duty_cycle_percent = 100, - - .fpc_pinstrap_1 = { GPIOB, GPIO_Pin_0 }, - .fpc_pinstrap_2 = { GPIOC, GPIO_Pin_5 }, - - .num_avail_gpios = 114, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { - "Back", GPIOG, GPIO_Pin_4, { EXTI_PortSourceGPIOG, 4 }, GPIO_PuPd_NOPULL - }, - [BUTTON_ID_UP] = { - "Up", GPIOG, GPIO_Pin_3, { EXTI_PortSourceGPIOG, 3 }, GPIO_PuPd_UP - }, - [BUTTON_ID_SELECT] = { - "Select", GPIOG, GPIO_Pin_1, { EXTI_PortSourceGPIOG, 1 }, GPIO_PuPd_UP - }, - [BUTTON_ID_DOWN] = { - "Down", GPIOG, GPIO_Pin_2, { EXTI_PortSourceGPIOG, 2 }, GPIO_PuPd_UP - }, - }, - - .button_com = { 0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .pmic_int = { EXTI_PortSourceGPIOG, 7 }, - .pmic_int_gpio = { - .gpio = GPIOG, - .gpio_pin = GPIO_Pin_7, - }, - - .rail_4V5_ctrl = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_2, - .active_high = true, - }, - .rail_6V6_ctrl = { - .gpio = GPIOF, - .gpio_pin = GPIO_Pin_3, - .active_high = true, - }, - - .battery_vmon_scale = { - // The PMIC divides the battery voltage by a ratio of 3:1 in order to move it down to a voltage - // our ADC is capable of reading. The battery voltage varies around 4V~ and we're only capable - // of reading up to 1.8V in the ADC. - .numerator = 3, - .denominator = 1, - }, - - .vusb_stat = { .gpio = GPIO_Port_NULL, }, - .chg_stat = { GPIO_Port_NULL }, - .chg_fast = { GPIO_Port_NULL }, - .chg_en = { GPIO_Port_NULL }, - .has_vusb_interrupt = false, - - .wake_on_usb_power = false, - - .charging_cutoff_voltage = 4300, - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 7, - .battery_capacity_hours = 44, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = false, - .shake_thresholds[AccelThresholdHigh] = 0x64, - .shake_thresholds[AccelThresholdLow] = 0xf, - .double_tap_threshold = 12500, - }, - .accel_int_gpios = { - [0] = { GPIOG, GPIO_Pin_5 }, - [1] = { GPIOG, GPIO_Pin_6 }, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOG, 5 }, - [1] = { EXTI_PortSourceGPIOG, 6 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { - .axes_offsets[AXIS_X] = 0, - .axes_offsets[AXIS_Y] = 1, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = false, - }, - .mag_int_gpio = { GPIOF, GPIO_Pin_14 }, - .mag_int = { EXTI_PortSourceGPIOF, 14 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl | ActuatorOptions_Pwm | ActuatorOptions_HBridge, - .ctl = { GPIOF, GPIO_Pin_4, true }, - .pwm = { - .output = { GPIOB, GPIO_Pin_8, true }, - .timer = { - .peripheral = TIM10, - .config_clock = RCC_APB2Periph_TIM10, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_8, GPIO_PinSource8, GPIO_AF_TIM10 }, - }, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm, - .ctl = {0}, - .pwm = { - .output = { GPIOB, GPIO_Pin_14, true }, - .timer = { - .peripheral = TIM12, - .config_clock = RCC_APB1Periph_TIM12, - .init = TIM_OC1Init, - .preload = TIM_OC1PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_14, GPIO_PinSource14, GPIO_AF_TIM12 }, - }, -}; - -#define ACCESSORY_DMA_CONTROLLER DMA1_CONTROLLER -static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = { - .power_en = { GPIOF, GPIO_Pin_13, true }, - .int_gpio = { GPIOE, GPIO_Pin_0 }, - .exti = { EXTI_PortSourceGPIOE, 0 }, -}; - -#define BOARD_BT_USART_IRQ_HANDLER USART1_IRQHandler -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564B, - .shutdown = { GPIOB, GPIO_Pin_12, false }, - .wakeup = { - .int_gpio = { GPIOA, GPIO_Pin_11 }, - .int_exti = { EXTI_PortSourceGPIOA, 11 }, - }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -extern DMARequest * const COMPOSITOR_DMA; -extern DMARequest * const MIC_I2S_RX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; -extern UARTDevice * const ACCESSORY_UART; -extern UARTDevice * const BLUETOOTH_UART; - -extern SPISlavePort * const BMI160_SPI; - -extern I2CSlavePort * const I2C_MAG3110; -extern I2CSlavePort * const I2C_MAX14690; -extern I2CSlavePort * const I2C_MFI; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_TEMPERATURE; - -extern const AnalogTemperatureSensor * const TEMPERATURE_SENSOR; - -static MicDevice * const MIC = (void *)0; - -extern ICE40LPDevice * const ICE40LP; diff --git a/src/fw/board/boards/board_v1_5.c b/src/fw/board/boards/board_v1_5.c deleted file mode 100644 index 86e1089234..0000000000 --- a/src/fw/board/boards/board_v1_5.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/voltage_monitor.h" - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 4); // DMA1_STREAM4_DEVICE - Sharp SPI TX - -// DMA Requests - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_sharp_spi_tx_dma_request_state; -static DMARequest SHARP_SPI_TX_DMA_REQUEST = { - .state = &s_sharp_spi_tx_dma_request_state, - .stream = &DMA1_STREAM4_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const SHARP_SPI_TX_DMA = &SHARP_SPI_TX_DMA_REQUEST; - - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_USART3 - }, - .rx_gpio = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - - -// I2C DEVICES - -static I2CBusState I2C_MAIN_BUS_STATE = {}; - -static const I2CBusHal I2C_MAIN_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_MAIN_BUS = { - .state = &I2C_MAIN_BUS_STATE, - .hal = &I2C_MAIN_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_MAIN" -}; - -extern void i2c_rail_ctl_pin(I2CBus *device, bool enable); - -static I2CBusState I2C_2V5_BUS_STATE = {}; - -static const I2CBusHal I2C_2V5_BUS_HAL = { - .i2c = I2C2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, -}; - -static const I2CBus I2C_2V5_BUS = { - .state = &I2C_2V5_BUS_STATE, - .hal = &I2C_2V5_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_I2C2 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_I2C2 - }, - .stop_mode_inhibitor = InhibitorI2C2, - .rail_gpio = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_0, - .active_high = true - }, - .rail_ctl_fn = i2c_rail_ctl_pin, - .name = "I2C_2V5" -}; - -static const I2CSlavePort I2C_SLAVE_LIS3DH = { - .bus = &I2C_MAIN_BUS, - .address = 0x32 -}; - -static const I2CSlavePort I2C_SLAVE_MFI = { - .bus = &I2C_2V5_BUS, - .address = 0x20 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_2V5_BUS, - .address = 0x1C -}; - -I2CSlavePort * const I2C_LIS3DH = &I2C_SLAVE_LIS3DH; -I2CSlavePort * const I2C_MFI = &I2C_SLAVE_MFI; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; - -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C2_EV, i2c_hal_event_irq_handler, &I2C_2V5_BUS); -IRQ_MAP(I2C2_ER, i2c_hal_error_irq_handler, &I2C_2V5_BUS); - - -// VOLTAGE MONITOR DEVICES -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_12, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_2, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_10, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_0, - }, -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; - -void board_early_init(void) { -} - -void board_init(void) { - i2c_init(&I2C_MAIN_BUS); - i2c_init(&I2C_2V5_BUS); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); -} diff --git a/src/fw/board/boards/board_v1_5.h b/src/fw/board/boards/board_v1_5.h deleted file mode 100644 index be93d4d755..0000000000 --- a/src/fw/board/boards/board_v1_5.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/battery.h" -#include "drivers/button.h" -#include "drivers/imu/mag3110/mag3110.h" -#include "drivers/imu/lis3dh/lis3dh.h" -#include "services/imu/units.h" -#include "util/size.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_ON - -static const BoardConfig BOARD_CONFIG = { - .ambient_light_dark_threshold = 3120, - .ambient_k_delta_threshold = 96, - .photo_en = { GPIOD, GPIO_Pin_2, true }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 11 }, - - .lcd_com = { GPIOB, GPIO_Pin_1, true }, - - .power_5v0_options = OptionActiveLowOpenDrain, - .power_ctl_5v0 = { GPIOC, GPIO_Pin_5, false }, - - .backlight_on_percent = 25, - .backlight_max_duty_cycle_percent = 100, - - .has_mic = false, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3, { EXTI_PortSourceGPIOC, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2, { EXTI_PortSourceGPIOA, 2 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6, { EXTI_PortSourceGPIOC, 6 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1, { EXTI_PortSourceGPIOA, 1 }, GPIO_PuPd_NOPULL }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_13, - }, - .vusb_exti = { EXTI_PortSourceGPIOC, 13 }, - .chg_stat = { GPIOH, GPIO_Pin_1 }, - .chg_fast = { GPIOB, GPIO_Pin_6 }, - .chg_en = { GPIOB, GPIO_Pin_9 }, - - .has_vusb_interrupt = true, - - .wake_on_usb_power = true, - - .charging_status_led_voltage_compensation = 0, - - .low_power_threshold = 3, - .battery_capacity_hours = 144, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = true, - .shake_thresholds[AccelThresholdHigh] = 0x7f, - .shake_thresholds[AccelThresholdLow] = 0xa, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOC, 8 }, - [1] = { EXTI_PortSourceGPIOC, 9 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { // align raw mag data with accel coords (ENU) - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, - }, - - .mag_int = { EXTI_PortSourceGPIOC, 4 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl, - .ctl = { GPIOB, GPIO_Pin_0, true }, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_Pwm, - .ctl = {0}, - .pwm = { - .output = { GPIOB, GPIO_Pin_5, true }, - .timer = { - .peripheral = TIM3, - .config_clock = RCC_APB1Periph_TIM3, - .init = TIM_OC2Init, - .preload = TIM_OC2PreloadConfig - }, - .afcfg = { GPIOB, GPIO_Pin_5, GPIO_PinSource5, GPIO_AF_TIM3 }, - }, -}; - -#define BOARD_BT_USART_IRQ_HANDLER USART1_IRQHandler -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564A, - .shutdown = { GPIOA, GPIO_Pin_3, false}, - .wakeup = { - .int_gpio = { GPIOC, GPIO_Pin_12 }, - .int_exti = { EXTI_PortSourceGPIOC, 12 }, - }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { - .spi = SPI2, - .spi_gpio = GPIOB, - .spi_clk = RCC_APB1Periph_SPI2, - .spi_clk_periph = SpiPeriphClockAPB1, - - .clk = { GPIOB, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF_SPI2 }, - .mosi = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .cs = { GPIOB, GPIO_Pin_12, true }, - - .on_ctrl = { GPIOB, GPIO_Pin_14, true }, - .on_ctrl_otype = GPIO_OType_OD, -}; - -extern DMARequest * const SHARP_SPI_TX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; - -extern I2CSlavePort * const I2C_LIS3DH; -extern I2CSlavePort * const I2C_MFI; -extern I2CSlavePort * const I2C_MAG3110; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; diff --git a/src/fw/board/boards/board_v2_0.c b/src/fw/board/boards/board_v2_0.c deleted file mode 100644 index 37d76bdf57..0000000000 --- a/src/fw/board/boards/board_v2_0.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" - -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "drivers/stm32f2/uart_definitions.h" -#include "drivers/voltage_monitor.h" - -// DMA Controllers - -static DMAControllerState s_dma1_state; -static DMAController DMA1_DEVICE = { - .state = &s_dma1_state, - .periph = DMA1, - .rcc_bit = RCC_AHB1Periph_DMA1, -}; - -static DMAControllerState s_dma2_state; -static DMAController DMA2_DEVICE = { - .state = &s_dma2_state, - .periph = DMA2, - .rcc_bit = RCC_AHB1Periph_DMA2, -}; - -// DMA Streams - -CREATE_DMA_STREAM(1, 1); // DMA1_STREAM1_DEVICE - Debug UART RX -CREATE_DMA_STREAM(1, 4); // DMA1_STREAM4_DEVICE - Sharp SPI TX - -// DMA Requests - -static DMARequestState s_dbg_uart_dma_request_state; -static DMARequest DBG_UART_RX_DMA_REQUEST = { - .state = &s_dbg_uart_dma_request_state, - .stream = &DMA1_STREAM1_DEVICE, - .channel = 4, - .irq_priority = IRQ_PRIORITY_INVALID, // no interrupts - .priority = DMARequestPriority_High, - .type = DMARequestType_PeripheralToMemory, - .data_size = DMARequestDataSize_Byte, -}; - -static DMARequestState s_sharp_spi_tx_dma_request_state; -static DMARequest SHARP_SPI_TX_DMA_REQUEST = { - .state = &s_sharp_spi_tx_dma_request_state, - .stream = &DMA1_STREAM4_DEVICE, - .channel = 0, - .irq_priority = 0x0f, - .priority = DMARequestPriority_VeryHigh, - .type = DMARequestType_MemoryToPeripheral, - .data_size = DMARequestDataSize_Byte, -}; -DMARequest * const SHARP_SPI_TX_DMA = &SHARP_SPI_TX_DMA_REQUEST; - - -// UART DEVICES - -#if TARGET_QEMU -static UARTDeviceState s_qemu_uart_state; -static UARTDevice QEMU_UART_DEVICE = { - .state = &s_qemu_uart_state, - // GPIO? Where we're going, we don't need GPIO. (connected to QEMU) - .periph = USART2, - .irq_channel = USART2_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART2 -}; -UARTDevice * const QEMU_UART = &QEMU_UART_DEVICE; -IRQ_MAP(USART2, uart_irq_handler, QEMU_UART); -#endif - -static UARTDeviceState s_dbg_uart_state; -static UARTDevice DBG_UART_DEVICE = { - .state = &s_dbg_uart_state, - .tx_gpio = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_USART3 - }, - .rx_gpio = { - .gpio = GPIOD, - .gpio_pin = GPIO_Pin_9, - .gpio_pin_source = GPIO_PinSource9, - .gpio_af = GPIO_AF_USART3 - }, - .periph = USART3, - .irq_channel = USART3_IRQn, - .irq_priority = 13, - .rcc_apb_periph = RCC_APB1Periph_USART3, - .rx_dma = &DBG_UART_RX_DMA_REQUEST -}; -UARTDevice * const DBG_UART = &DBG_UART_DEVICE; -IRQ_MAP(USART3, uart_irq_handler, DBG_UART); - - -// I2C DEVICES - -static I2CBusState I2C_MAIN_BUS_STATE = {}; - -static const I2CBusHal I2C_MAIN_BUS_HAL = { - .i2c = I2C1, - .clock_ctrl = RCC_APB1Periph_I2C1, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_16_9, - .ev_irq_channel = I2C1_EV_IRQn, - .er_irq_channel = I2C1_ER_IRQn, -}; - -static const I2CBus I2C_MAIN_BUS = { - .state = &I2C_MAIN_BUS_STATE, - .hal = &I2C_MAIN_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_I2C1 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_7, - .gpio_pin_source = GPIO_PinSource7, - .gpio_af = GPIO_AF_I2C1 - }, - .stop_mode_inhibitor = InhibitorI2C1, - .name = "I2C_MAIN" -}; - -extern void i2c_rail_ctl_pin(I2CBus *device, bool enable); - -static I2CBusState I2C_2V5_BUS_STATE = {}; - -static const I2CBusHal I2C_2V5_BUS_HAL = { - .i2c = I2C2, - .clock_ctrl = RCC_APB1Periph_I2C2, - .clock_speed = 400000, - .duty_cycle = I2CDutyCycle_2, - .ev_irq_channel = I2C2_EV_IRQn, - .er_irq_channel = I2C2_ER_IRQn, -}; - -static const I2CBus I2C_2V5_BUS = { - .state = &I2C_2V5_BUS_STATE, - .hal = &I2C_2V5_BUS_HAL, - .scl_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_10, - .gpio_pin_source = GPIO_PinSource10, - .gpio_af = GPIO_AF_I2C2 - }, - .sda_gpio = { - .gpio = GPIOB, - .gpio_pin = GPIO_Pin_11, - .gpio_pin_source = GPIO_PinSource11, - .gpio_af = GPIO_AF_I2C2 - }, - .stop_mode_inhibitor = InhibitorI2C2, - .rail_gpio = { - .gpio = GPIOH, - .gpio_pin = GPIO_Pin_0, - .active_high = true - }, - .rail_ctl_fn = i2c_rail_ctl_pin, - .name = "I2C_2V5" -}; - -static const I2CSlavePort I2C_SLAVE_LIS3DH = { - .bus = &I2C_MAIN_BUS, - .address = 0x32 -}; - -static const I2CSlavePort I2C_SLAVE_MFI = { - .bus = &I2C_2V5_BUS, - .address = 0x20 -}; - -static const I2CSlavePort I2C_SLAVE_MAG3110 = { - .bus = &I2C_2V5_BUS, - .address = 0x1C -}; - -static const I2CSlavePort I2C_SLAVE_LED = { - .bus = &I2C_MAIN_BUS, - .address = 0xC8 -}; - -I2CSlavePort * const I2C_LIS3DH = &I2C_SLAVE_LIS3DH; -I2CSlavePort * const I2C_MFI = &I2C_SLAVE_MFI; -I2CSlavePort * const I2C_MAG3110 = &I2C_SLAVE_MAG3110; -I2CSlavePort * const I2C_LED = &I2C_SLAVE_LED; - -IRQ_MAP(I2C1_EV, i2c_hal_event_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C1_ER, i2c_hal_error_irq_handler, &I2C_MAIN_BUS); -IRQ_MAP(I2C2_EV, i2c_hal_event_irq_handler, &I2C_2V5_BUS); -IRQ_MAP(I2C2_ER, i2c_hal_error_irq_handler, &I2C_2V5_BUS); - - -// VOLTAGE MONITOR DEVICES -static const VoltageMonitorDevice VOLTAGE_MONITOR_ALS_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_12, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_2, - }, -}; - -static const VoltageMonitorDevice VOLTAGE_MONITOR_BATTERY_DEVICE = { - .adc = ADC2, - .adc_channel = ADC_Channel_10, - .clock_ctrl = RCC_APB2Periph_ADC2, - .input = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_0, - }, -}; - -VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS = &VOLTAGE_MONITOR_ALS_DEVICE; -VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY = &VOLTAGE_MONITOR_BATTERY_DEVICE; - -void board_early_init(void) { -} - -void board_init(void) { - i2c_init(&I2C_MAIN_BUS); - i2c_init(&I2C_2V5_BUS); - - voltage_monitor_device_init(VOLTAGE_MONITOR_ALS); - voltage_monitor_device_init(VOLTAGE_MONITOR_BATTERY); -} diff --git a/src/fw/board/boards/board_v2_0.h b/src/fw/board/boards/board_v2_0.h deleted file mode 100644 index 60105539ed..0000000000 --- a/src/fw/board/boards/board_v2_0.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/battery.h" -#include "drivers/button.h" -#include "services/imu/units.h" -#include "util/size.h" - -#include "drivers/imu/mag3110/mag3110.h" -#include "drivers/imu/lis3dh/lis3dh.h" - -#define BT_VENDOR_ID 0x0154 -#define BT_VENDOR_NAME "Pebble Technology" - -#define BOARD_LSE_MODE RCC_LSE_ON - -static const BoardConfig BOARD_CONFIG = { - .ambient_light_dark_threshold = 3200, - .ambient_k_delta_threshold = 96, - .photo_en = { GPIOD, GPIO_Pin_2, true }, - - .dbgserial_int = { EXTI_PortSourceGPIOC, 11 }, - - .power_ctl_5v0 = { GPIOC, GPIO_Pin_5, false }, - .lcd_com = { GPIOB, GPIO_Pin_1, true }, - - .backlight_on_percent = 100, - .backlight_max_duty_cycle_percent = 100, - .power_5v0_options = OptionActiveLowOpenDrain, - - .has_mic = false, -}; - -static const BoardConfigButton BOARD_CONFIG_BUTTON = { - .buttons = { - [BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3, { EXTI_PortSourceGPIOC, 3 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2, { EXTI_PortSourceGPIOA, 2 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6, { EXTI_PortSourceGPIOC, 6 }, GPIO_PuPd_NOPULL }, - [BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1, { EXTI_PortSourceGPIOA, 1 }, GPIO_PuPd_NOPULL }, - }, - - .button_com = { GPIOA, GPIO_Pin_0 }, - .active_high = false, -}; - -static const BoardConfigPower BOARD_CONFIG_POWER = { - .vusb_stat = { - .gpio = GPIOC, - .gpio_pin = GPIO_Pin_13, - }, - .vusb_exti = { EXTI_PortSourceGPIOC, 13 }, - .chg_stat = { GPIOH, GPIO_Pin_1 }, - .chg_fast = { GPIOB, GPIO_Pin_6 }, - .chg_en = { GPIOB, GPIO_Pin_9 }, - - .has_vusb_interrupt = true, - - .charging_status_led_voltage_compensation = 30, - - .low_power_threshold = 6, - .battery_capacity_hours = 144, -}; - -static const BoardConfigAccel BOARD_CONFIG_ACCEL = { - .accel_config = { - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = true, - .axes_inverts[AXIS_Y] = false, - .axes_inverts[AXIS_Z] = true, - .shake_thresholds[AccelThresholdHigh] = 0x7f, - .shake_thresholds[AccelThresholdLow] = 0xa, - }, - .accel_ints = { - [0] = { EXTI_PortSourceGPIOC, 8 }, - [1] = { EXTI_PortSourceGPIOC, 9 } - }, -}; - -static const BoardConfigMag BOARD_CONFIG_MAG = { - .mag_config = { // align raw mag data with accel coords (ENU) - .axes_offsets[AXIS_X] = 1, - .axes_offsets[AXIS_Y] = 0, - .axes_offsets[AXIS_Z] = 2, - .axes_inverts[AXIS_X] = false, - .axes_inverts[AXIS_Y] = true, - .axes_inverts[AXIS_Z] = true, - }, - .mag_int = { EXTI_PortSourceGPIOC, 4 }, -}; - -static const BoardConfigActuator BOARD_CONFIG_VIBE = { - .options = ActuatorOptions_Ctl, - .ctl = { GPIOB, GPIO_Pin_0, true }, -}; - -static const BoardConfigActuator BOARD_CONFIG_BACKLIGHT = { - .options = ActuatorOptions_IssiI2C, - .ctl = { GPIOB, GPIO_Pin_5, true }, -}; - -#define BOARD_BT_USART_IRQ_HANDLER USART1_IRQHandler -static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564A, - .shutdown = { GPIOA, GPIO_Pin_3, false}, - .wakeup = { - .int_gpio = { GPIOC, GPIO_Pin_12 }, - .int_exti = { EXTI_PortSourceGPIOC, 12 }, - }, -}; - -static const BoardConfigMCO1 BOARD_CONFIG_MCO1 = { - .output_enabled = true, - .af_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - .gpio_pin_source = GPIO_PinSource8, - .gpio_af = GPIO_AF_MCO, - }, - .an_cfg = { - .gpio = GPIOA, - .gpio_pin = GPIO_Pin_8, - }, -}; - -static const BoardConfigSharpDisplay BOARD_CONFIG_DISPLAY = { - .spi = SPI2, - .spi_gpio = GPIOB, - .spi_clk = RCC_APB1Periph_SPI2, - .spi_clk_periph = SpiPeriphClockAPB1, - - .clk = { GPIOB, GPIO_Pin_13, GPIO_PinSource13, GPIO_AF_SPI2 }, - .mosi = { GPIOB, GPIO_Pin_15, GPIO_PinSource15, GPIO_AF_SPI2 }, - .cs = { GPIOB, GPIO_Pin_12, true }, - - .on_ctrl = { GPIOB, GPIO_Pin_14, true }, - .on_ctrl_otype = GPIO_OType_OD, -}; - -extern DMARequest * const SHARP_SPI_TX_DMA; - -extern UARTDevice * const QEMU_UART; -extern UARTDevice * const DBG_UART; - -extern I2CSlavePort * const I2C_LIS3DH; -extern I2CSlavePort * const I2C_MFI; -extern I2CSlavePort * const I2C_MAG3110; -extern I2CSlavePort * const I2C_LED; - -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_ALS; -extern VoltageMonitorDevice * const VOLTAGE_MONITOR_BATTERY; diff --git a/src/fw/board/boards/support/board_asterix_nrfjprog_config.toml b/src/fw/board/boards/support/board_asterix_nrfjprog_config.toml new file mode 100644 index 0000000000..287c23b233 --- /dev/null +++ b/src/fw/board/boards/support/board_asterix_nrfjprog_config.toml @@ -0,0 +1,45 @@ +[target] + family = "nRF52" + clockspeed = 4000 + +[qspi] + mem_size = 0x2000000 + read_mode = "READ4IO" + write_mode = "PP4O" + address_mode = "BIT32" + frequency = "M8" + spi_mode = "MODE0" + rx_delay = 2 + wip_index = 0 + page_program_size = "PAGE256" + retain_ram = false + [qspi.sck] + delay = 0 + pin = 19 + port = 0 + [qspi.csn] + pin = 17 + port = 0 + [qspi.dio0] + pin = 20 + port = 0 + [qspi.dio1] + pin = 21 + port = 0 + [qspi.dio2] + pin = 22 + port = 0 + [qspi.dio3] + pin = 23 + port = 0 + + [qspi.custom] + io2_level = "LEVEL_HIGH" + io3_level = "LEVEL_HIGH" + + instructions = [ + # Enable 32-bit addressing + {command=0xB7, data=[]}, + # Enable Quad IO + {command=0x01, data=[0x00, 0x02]} + ] diff --git a/src/fw/board/display.h b/src/fw/board/display.h index 36ff22ea78..1f34df6df1 100644 --- a/src/fw/board/display.h +++ b/src/fw/board/display.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,9 +8,9 @@ //! @internal //! data type that's used to store row data infos in a space-efficient manner typedef struct { - uint16_t offset; - uint8_t min_x; - uint8_t max_x; + uint32_t offset; // uint32_t needed for rectangular framebuffers > 65535 bytes + uint16_t min_x; + uint16_t max_x; } GBitmapDataRowInfoInternal; // FIXME: PBL-21055 Fix SDK exporter failing to crawl framebuffer headers @@ -35,56 +22,18 @@ typedef struct { // used by waftools/pebble_test.py to define these includes per test #else -#if BOARD_BIGBOARD -#include "displays/display_tintin.h" -#elif BOARD_EV2_4 -#include "displays/display_tintin.h" -#elif BOARD_BB2 -#include "displays/display_tintin.h" -#elif BOARD_V1_5 -#include "displays/display_tintin.h" -#elif BOARD_V2_0 -#include "displays/display_tintin.h" -#elif BOARD_SNOWY_BB -#include "displays/display_snowy.h" -#elif BOARD_SNOWY_EVT -#include "displays/display_snowy.h" -#elif BOARD_SNOWY_EVT2 -#include "displays/display_snowy.h" -#elif BOARD_SNOWY_BB2 -#include "displays/display_snowy.h" -#elif BOARD_SNOWY_DVT -#include "displays/display_snowy.h" -#elif BOARD_SNOWY_S3 -#include "displays/display_snowy.h" -#elif BOARD_SPALDING_BB2 -#include "displays/display_spalding.h" -#elif BOARD_SPALDING_EVT -#include "displays/display_spalding.h" -#elif BOARD_SPALDING -#include "displays/display_spalding.h" -#elif BOARD_SILK_EVT -#include "displays/display_silk.h" -#elif BOARD_SILK_BB -#include "displays/display_silk.h" -#elif BOARD_SILK_BB2 -#include "displays/display_silk.h" -#elif BOARD_SILK -#include "displays/display_silk.h" -#elif BOARD_CALVIN_BB -#include "displays/display_silk.h" -#elif BOARD_ASTERIX -#include "displays/display_silk.h" -#elif BOARD_OBELIX || BOARD_OBELIX_BB +#ifdef CONFIG_BOARD_ASTERIX +#include "displays/display_asterix.h" +#elif defined(CONFIG_BOARD_OBELIX_DVT) || defined(CONFIG_BOARD_OBELIX_PVT) || defined(CONFIG_BOARD_OBELIX_BB2) #include "displays/display_obelix.h" -#elif BOARD_CUTTS_BB -#include "displays/display_snowy.h" -#elif BOARD_ROBERT_BB -#include "displays/display_robert.h" -#elif BOARD_ROBERT_BB2 -#include "displays/display_robert.h" -#elif BOARD_ROBERT_EVT -#include "displays/display_robert_evt.h" +#elif defined(CONFIG_BOARD_GETAFIX_EVT) || defined(CONFIG_BOARD_GETAFIX_DVT) || defined(CONFIG_BOARD_GETAFIX_DVT2) +#include "displays/display_getafix.h" +#elif defined(CONFIG_BOARD_QEMU_EMERY) +#include "displays/display_qemu_emery.h" +#elif defined(CONFIG_BOARD_QEMU_FLINT) +#include "displays/display_qemu_flint.h" +#elif defined(CONFIG_BOARD_QEMU_GABBRO) +#include "displays/display_qemu_gabbro.h" #else #error "Unknown display definition for board" #endif // BOARD_* diff --git a/src/fw/board/displays/display_asterix.h b/src/fw/board/displays/display_asterix.h new file mode 100644 index 0000000000..6fa37e4157 --- /dev/null +++ b/src/fw/board/displays/display_asterix.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 +#define DISPLAY_ORIENTATION_ROTATED_180 1 +#define DISPLAY_ORIENTATION_ROW_MAJOR 0 +#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 0 + +#define PBL_BW 1 +#define PBL_COLOR 0 + +#define PBL_RECT 1 +#define PBL_ROUND 0 + +#define PBL_DISPLAY_WIDTH 144 +#define PBL_DISPLAY_HEIGHT 168 + +#define LEGACY_2X_DISP_COLS PBL_DISPLAY_WIDTH +#define LEGACY_2X_DISP_ROWS PBL_DISPLAY_HEIGHT +#define LEGACY_3X_DISP_COLS PBL_DISPLAY_WIDTH +#define LEGACY_3X_DISP_ROWS PBL_DISPLAY_HEIGHT + +#define DISPLAY_FRAMEBUFFER_BYTES \ + (ROUND_TO_MOD_CEIL(PBL_DISPLAY_WIDTH, 32) / 8 * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_getafix.c b/src/fw/board/displays/display_getafix.c new file mode 100644 index 0000000000..6489a5c42f --- /dev/null +++ b/src/fw/board/displays/display_getafix.c @@ -0,0 +1,526 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +// Getafix display pixel masks +// +// The Getafix round display is logically a square 260x260 display with +// some of the pixels hidden under a mask or missing entirely. The mask +// is symmetrical both horizontally and vertically: the masks on the +// left and right side of a line are equal, and the mask on the top half +// of the display is a mirror image of the bottom half. +// +// This array maps the number of pixels masked off for one quadrant of +// the display. Array element zero is the number of masked pixels from +// a display corner inwards. Subsequent array elements contain the mask +// for the adjacent rows or columns moving inwards towards the center +// of the display. + +#include "board/display.h" + +// g_gbitmap_data_row_infos was generated with this script: +// +// #!/bin/env python +// # See LS013B7DD02 datasheet Table 6-6-2 +// # Using RECTANGULAR offsets (y * 260) instead of packed circular offsets +// # to enable in-place pixel format conversion in display driver +// topleft_mask = [ +// 113, 107, 102, 98, 94, 90, 87, 85, 82, 80, 77, 75, 73, 71, 69, 67, 65, 64, +// 62, 60, 59, 57, 56, 54, 53, 52, 50, 49, 48, 46, 45, 44, 43, 42, 41, 40, 39, +// 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 29, 28, 27, 26, 26, 25, 24, 23, 23, +// 22, 21, 21, 20, 19, 19, 18, 18, 17, 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, +// 11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, +// 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, +// ] +// +// for i in range(0, 260): +// if i < 130: +// min_x = topleft_mask[i] +// else: +// min_x = topleft_mask[260 - i - 1] +// +// max_x = 260 - min_x - 1 +// offset = i * 260 # rectangular offset +// print( +// " /" +// + "* y = %3d */ {.offset = %5d, .min_x = %3d, .max_x = %3d}," +// % (i, offset, min_x, max_x) +// ) +// +// print("Rectangular Framebuffer has %d bytes" % (260 * 260)) + +const void *const g_gbitmap_data_row_infos = (const GBitmapDataRowInfoInternal[]){ + /* y = 0 */ {.offset = 0, .min_x = 113, .max_x = 146}, + /* y = 1 */ {.offset = 260, .min_x = 107, .max_x = 152}, + /* y = 2 */ {.offset = 520, .min_x = 102, .max_x = 157}, + /* y = 3 */ {.offset = 780, .min_x = 98, .max_x = 161}, + /* y = 4 */ {.offset = 1040, .min_x = 94, .max_x = 165}, + /* y = 5 */ {.offset = 1300, .min_x = 90, .max_x = 169}, + /* y = 6 */ {.offset = 1560, .min_x = 87, .max_x = 172}, + /* y = 7 */ {.offset = 1820, .min_x = 85, .max_x = 174}, + /* y = 8 */ {.offset = 2080, .min_x = 82, .max_x = 177}, + /* y = 9 */ {.offset = 2340, .min_x = 80, .max_x = 179}, + /* y = 10 */ {.offset = 2600, .min_x = 77, .max_x = 182}, + /* y = 11 */ {.offset = 2860, .min_x = 75, .max_x = 184}, + /* y = 12 */ {.offset = 3120, .min_x = 73, .max_x = 186}, + /* y = 13 */ {.offset = 3380, .min_x = 71, .max_x = 188}, + /* y = 14 */ {.offset = 3640, .min_x = 69, .max_x = 190}, + /* y = 15 */ {.offset = 3900, .min_x = 67, .max_x = 192}, + /* y = 16 */ {.offset = 4160, .min_x = 65, .max_x = 194}, + /* y = 17 */ {.offset = 4420, .min_x = 64, .max_x = 195}, + /* y = 18 */ {.offset = 4680, .min_x = 62, .max_x = 197}, + /* y = 19 */ {.offset = 4940, .min_x = 60, .max_x = 199}, + /* y = 20 */ {.offset = 5200, .min_x = 59, .max_x = 200}, + /* y = 21 */ {.offset = 5460, .min_x = 57, .max_x = 202}, + /* y = 22 */ {.offset = 5720, .min_x = 56, .max_x = 203}, + /* y = 23 */ {.offset = 5980, .min_x = 54, .max_x = 205}, + /* y = 24 */ {.offset = 6240, .min_x = 53, .max_x = 206}, + /* y = 25 */ {.offset = 6500, .min_x = 52, .max_x = 207}, + /* y = 26 */ {.offset = 6760, .min_x = 50, .max_x = 209}, + /* y = 27 */ {.offset = 7020, .min_x = 49, .max_x = 210}, + /* y = 28 */ {.offset = 7280, .min_x = 48, .max_x = 211}, + /* y = 29 */ {.offset = 7540, .min_x = 46, .max_x = 213}, + /* y = 30 */ {.offset = 7800, .min_x = 45, .max_x = 214}, + /* y = 31 */ {.offset = 8060, .min_x = 44, .max_x = 215}, + /* y = 32 */ {.offset = 8320, .min_x = 43, .max_x = 216}, + /* y = 33 */ {.offset = 8580, .min_x = 42, .max_x = 217}, + /* y = 34 */ {.offset = 8840, .min_x = 41, .max_x = 218}, + /* y = 35 */ {.offset = 9100, .min_x = 40, .max_x = 219}, + /* y = 36 */ {.offset = 9360, .min_x = 39, .max_x = 220}, + /* y = 37 */ {.offset = 9620, .min_x = 38, .max_x = 221}, + /* y = 38 */ {.offset = 9880, .min_x = 37, .max_x = 222}, + /* y = 39 */ {.offset = 10140, .min_x = 36, .max_x = 223}, + /* y = 40 */ {.offset = 10400, .min_x = 35, .max_x = 224}, + /* y = 41 */ {.offset = 10660, .min_x = 34, .max_x = 225}, + /* y = 42 */ {.offset = 10920, .min_x = 33, .max_x = 226}, + /* y = 43 */ {.offset = 11180, .min_x = 32, .max_x = 227}, + /* y = 44 */ {.offset = 11440, .min_x = 31, .max_x = 228}, + /* y = 45 */ {.offset = 11700, .min_x = 30, .max_x = 229}, + /* y = 46 */ {.offset = 11960, .min_x = 29, .max_x = 230}, + /* y = 47 */ {.offset = 12220, .min_x = 29, .max_x = 230}, + /* y = 48 */ {.offset = 12480, .min_x = 28, .max_x = 231}, + /* y = 49 */ {.offset = 12740, .min_x = 27, .max_x = 232}, + /* y = 50 */ {.offset = 13000, .min_x = 26, .max_x = 233}, + /* y = 51 */ {.offset = 13260, .min_x = 26, .max_x = 233}, + /* y = 52 */ {.offset = 13520, .min_x = 25, .max_x = 234}, + /* y = 53 */ {.offset = 13780, .min_x = 24, .max_x = 235}, + /* y = 54 */ {.offset = 14040, .min_x = 23, .max_x = 236}, + /* y = 55 */ {.offset = 14300, .min_x = 23, .max_x = 236}, + /* y = 56 */ {.offset = 14560, .min_x = 22, .max_x = 237}, + /* y = 57 */ {.offset = 14820, .min_x = 21, .max_x = 238}, + /* y = 58 */ {.offset = 15080, .min_x = 21, .max_x = 238}, + /* y = 59 */ {.offset = 15340, .min_x = 20, .max_x = 239}, + /* y = 60 */ {.offset = 15600, .min_x = 19, .max_x = 240}, + /* y = 61 */ {.offset = 15860, .min_x = 19, .max_x = 240}, + /* y = 62 */ {.offset = 16120, .min_x = 18, .max_x = 241}, + /* y = 63 */ {.offset = 16380, .min_x = 18, .max_x = 241}, + /* y = 64 */ {.offset = 16640, .min_x = 17, .max_x = 242}, + /* y = 65 */ {.offset = 16900, .min_x = 16, .max_x = 243}, + /* y = 66 */ {.offset = 17160, .min_x = 16, .max_x = 243}, + /* y = 67 */ {.offset = 17420, .min_x = 15, .max_x = 244}, + /* y = 68 */ {.offset = 17680, .min_x = 15, .max_x = 244}, + /* y = 69 */ {.offset = 17940, .min_x = 14, .max_x = 245}, + /* y = 70 */ {.offset = 18200, .min_x = 14, .max_x = 245}, + /* y = 71 */ {.offset = 18460, .min_x = 13, .max_x = 246}, + /* y = 72 */ {.offset = 18720, .min_x = 13, .max_x = 246}, + /* y = 73 */ {.offset = 18980, .min_x = 12, .max_x = 247}, + /* y = 74 */ {.offset = 19240, .min_x = 12, .max_x = 247}, + /* y = 75 */ {.offset = 19500, .min_x = 11, .max_x = 248}, + /* y = 76 */ {.offset = 19760, .min_x = 11, .max_x = 248}, + /* y = 77 */ {.offset = 20020, .min_x = 10, .max_x = 249}, + /* y = 78 */ {.offset = 20280, .min_x = 10, .max_x = 249}, + /* y = 79 */ {.offset = 20540, .min_x = 10, .max_x = 249}, + /* y = 80 */ {.offset = 20800, .min_x = 9, .max_x = 250}, + /* y = 81 */ {.offset = 21060, .min_x = 9, .max_x = 250}, + /* y = 82 */ {.offset = 21320, .min_x = 8, .max_x = 251}, + /* y = 83 */ {.offset = 21580, .min_x = 8, .max_x = 251}, + /* y = 84 */ {.offset = 21840, .min_x = 8, .max_x = 251}, + /* y = 85 */ {.offset = 22100, .min_x = 7, .max_x = 252}, + /* y = 86 */ {.offset = 22360, .min_x = 7, .max_x = 252}, + /* y = 87 */ {.offset = 22620, .min_x = 6, .max_x = 253}, + /* y = 88 */ {.offset = 22880, .min_x = 6, .max_x = 253}, + /* y = 89 */ {.offset = 23140, .min_x = 6, .max_x = 253}, + /* y = 90 */ {.offset = 23400, .min_x = 5, .max_x = 254}, + /* y = 91 */ {.offset = 23660, .min_x = 5, .max_x = 254}, + /* y = 92 */ {.offset = 23920, .min_x = 5, .max_x = 254}, + /* y = 93 */ {.offset = 24180, .min_x = 5, .max_x = 254}, + /* y = 94 */ {.offset = 24440, .min_x = 4, .max_x = 255}, + /* y = 95 */ {.offset = 24700, .min_x = 4, .max_x = 255}, + /* y = 96 */ {.offset = 24960, .min_x = 4, .max_x = 255}, + /* y = 97 */ {.offset = 25220, .min_x = 4, .max_x = 255}, + /* y = 98 */ {.offset = 25480, .min_x = 3, .max_x = 256}, + /* y = 99 */ {.offset = 25740, .min_x = 3, .max_x = 256}, + /* y = 100 */ {.offset = 26000, .min_x = 3, .max_x = 256}, + /* y = 101 */ {.offset = 26260, .min_x = 3, .max_x = 256}, + /* y = 102 */ {.offset = 26520, .min_x = 2, .max_x = 257}, + /* y = 103 */ {.offset = 26780, .min_x = 2, .max_x = 257}, + /* y = 104 */ {.offset = 27040, .min_x = 2, .max_x = 257}, + /* y = 105 */ {.offset = 27300, .min_x = 2, .max_x = 257}, + /* y = 106 */ {.offset = 27560, .min_x = 2, .max_x = 257}, + /* y = 107 */ {.offset = 27820, .min_x = 1, .max_x = 258}, + /* y = 108 */ {.offset = 28080, .min_x = 1, .max_x = 258}, + /* y = 109 */ {.offset = 28340, .min_x = 1, .max_x = 258}, + /* y = 110 */ {.offset = 28600, .min_x = 1, .max_x = 258}, + /* y = 111 */ {.offset = 28860, .min_x = 1, .max_x = 258}, + /* y = 112 */ {.offset = 29120, .min_x = 1, .max_x = 258}, + /* y = 113 */ {.offset = 29380, .min_x = 0, .max_x = 259}, + /* y = 114 */ {.offset = 29640, .min_x = 0, .max_x = 259}, + /* y = 115 */ {.offset = 29900, .min_x = 0, .max_x = 259}, + /* y = 116 */ {.offset = 30160, .min_x = 0, .max_x = 259}, + /* y = 117 */ {.offset = 30420, .min_x = 0, .max_x = 259}, + /* y = 118 */ {.offset = 30680, .min_x = 0, .max_x = 259}, + /* y = 119 */ {.offset = 30940, .min_x = 0, .max_x = 259}, + /* y = 120 */ {.offset = 31200, .min_x = 0, .max_x = 259}, + /* y = 121 */ {.offset = 31460, .min_x = 0, .max_x = 259}, + /* y = 122 */ {.offset = 31720, .min_x = 0, .max_x = 259}, + /* y = 123 */ {.offset = 31980, .min_x = 0, .max_x = 259}, + /* y = 124 */ {.offset = 32240, .min_x = 0, .max_x = 259}, + /* y = 125 */ {.offset = 32500, .min_x = 0, .max_x = 259}, + /* y = 126 */ {.offset = 32760, .min_x = 0, .max_x = 259}, + /* y = 127 */ {.offset = 33020, .min_x = 0, .max_x = 259}, + /* y = 128 */ {.offset = 33280, .min_x = 0, .max_x = 259}, + /* y = 129 */ {.offset = 33540, .min_x = 0, .max_x = 259}, + /* y = 130 */ {.offset = 33800, .min_x = 0, .max_x = 259}, + /* y = 131 */ {.offset = 34060, .min_x = 0, .max_x = 259}, + /* y = 132 */ {.offset = 34320, .min_x = 0, .max_x = 259}, + /* y = 133 */ {.offset = 34580, .min_x = 0, .max_x = 259}, + /* y = 134 */ {.offset = 34840, .min_x = 0, .max_x = 259}, + /* y = 135 */ {.offset = 35100, .min_x = 0, .max_x = 259}, + /* y = 136 */ {.offset = 35360, .min_x = 0, .max_x = 259}, + /* y = 137 */ {.offset = 35620, .min_x = 0, .max_x = 259}, + /* y = 138 */ {.offset = 35880, .min_x = 0, .max_x = 259}, + /* y = 139 */ {.offset = 36140, .min_x = 0, .max_x = 259}, + /* y = 140 */ {.offset = 36400, .min_x = 0, .max_x = 259}, + /* y = 141 */ {.offset = 36660, .min_x = 0, .max_x = 259}, + /* y = 142 */ {.offset = 36920, .min_x = 0, .max_x = 259}, + /* y = 143 */ {.offset = 37180, .min_x = 0, .max_x = 259}, + /* y = 144 */ {.offset = 37440, .min_x = 0, .max_x = 259}, + /* y = 145 */ {.offset = 37700, .min_x = 0, .max_x = 259}, + /* y = 146 */ {.offset = 37960, .min_x = 0, .max_x = 259}, + /* y = 147 */ {.offset = 38220, .min_x = 1, .max_x = 258}, + /* y = 148 */ {.offset = 38480, .min_x = 1, .max_x = 258}, + /* y = 149 */ {.offset = 38740, .min_x = 1, .max_x = 258}, + /* y = 150 */ {.offset = 39000, .min_x = 1, .max_x = 258}, + /* y = 151 */ {.offset = 39260, .min_x = 1, .max_x = 258}, + /* y = 152 */ {.offset = 39520, .min_x = 1, .max_x = 258}, + /* y = 153 */ {.offset = 39780, .min_x = 2, .max_x = 257}, + /* y = 154 */ {.offset = 40040, .min_x = 2, .max_x = 257}, + /* y = 155 */ {.offset = 40300, .min_x = 2, .max_x = 257}, + /* y = 156 */ {.offset = 40560, .min_x = 2, .max_x = 257}, + /* y = 157 */ {.offset = 40820, .min_x = 2, .max_x = 257}, + /* y = 158 */ {.offset = 41080, .min_x = 3, .max_x = 256}, + /* y = 159 */ {.offset = 41340, .min_x = 3, .max_x = 256}, + /* y = 160 */ {.offset = 41600, .min_x = 3, .max_x = 256}, + /* y = 161 */ {.offset = 41860, .min_x = 3, .max_x = 256}, + /* y = 162 */ {.offset = 42120, .min_x = 4, .max_x = 255}, + /* y = 163 */ {.offset = 42380, .min_x = 4, .max_x = 255}, + /* y = 164 */ {.offset = 42640, .min_x = 4, .max_x = 255}, + /* y = 165 */ {.offset = 42900, .min_x = 4, .max_x = 255}, + /* y = 166 */ {.offset = 43160, .min_x = 5, .max_x = 254}, + /* y = 167 */ {.offset = 43420, .min_x = 5, .max_x = 254}, + /* y = 168 */ {.offset = 43680, .min_x = 5, .max_x = 254}, + /* y = 169 */ {.offset = 43940, .min_x = 5, .max_x = 254}, + /* y = 170 */ {.offset = 44200, .min_x = 6, .max_x = 253}, + /* y = 171 */ {.offset = 44460, .min_x = 6, .max_x = 253}, + /* y = 172 */ {.offset = 44720, .min_x = 6, .max_x = 253}, + /* y = 173 */ {.offset = 44980, .min_x = 7, .max_x = 252}, + /* y = 174 */ {.offset = 45240, .min_x = 7, .max_x = 252}, + /* y = 175 */ {.offset = 45500, .min_x = 8, .max_x = 251}, + /* y = 176 */ {.offset = 45760, .min_x = 8, .max_x = 251}, + /* y = 177 */ {.offset = 46020, .min_x = 8, .max_x = 251}, + /* y = 178 */ {.offset = 46280, .min_x = 9, .max_x = 250}, + /* y = 179 */ {.offset = 46540, .min_x = 9, .max_x = 250}, + /* y = 180 */ {.offset = 46800, .min_x = 10, .max_x = 249}, + /* y = 181 */ {.offset = 47060, .min_x = 10, .max_x = 249}, + /* y = 182 */ {.offset = 47320, .min_x = 10, .max_x = 249}, + /* y = 183 */ {.offset = 47580, .min_x = 11, .max_x = 248}, + /* y = 184 */ {.offset = 47840, .min_x = 11, .max_x = 248}, + /* y = 185 */ {.offset = 48100, .min_x = 12, .max_x = 247}, + /* y = 186 */ {.offset = 48360, .min_x = 12, .max_x = 247}, + /* y = 187 */ {.offset = 48620, .min_x = 13, .max_x = 246}, + /* y = 188 */ {.offset = 48880, .min_x = 13, .max_x = 246}, + /* y = 189 */ {.offset = 49140, .min_x = 14, .max_x = 245}, + /* y = 190 */ {.offset = 49400, .min_x = 14, .max_x = 245}, + /* y = 191 */ {.offset = 49660, .min_x = 15, .max_x = 244}, + /* y = 192 */ {.offset = 49920, .min_x = 15, .max_x = 244}, + /* y = 193 */ {.offset = 50180, .min_x = 16, .max_x = 243}, + /* y = 194 */ {.offset = 50440, .min_x = 16, .max_x = 243}, + /* y = 195 */ {.offset = 50700, .min_x = 17, .max_x = 242}, + /* y = 196 */ {.offset = 50960, .min_x = 18, .max_x = 241}, + /* y = 197 */ {.offset = 51220, .min_x = 18, .max_x = 241}, + /* y = 198 */ {.offset = 51480, .min_x = 19, .max_x = 240}, + /* y = 199 */ {.offset = 51740, .min_x = 19, .max_x = 240}, + /* y = 200 */ {.offset = 52000, .min_x = 20, .max_x = 239}, + /* y = 201 */ {.offset = 52260, .min_x = 21, .max_x = 238}, + /* y = 202 */ {.offset = 52520, .min_x = 21, .max_x = 238}, + /* y = 203 */ {.offset = 52780, .min_x = 22, .max_x = 237}, + /* y = 204 */ {.offset = 53040, .min_x = 23, .max_x = 236}, + /* y = 205 */ {.offset = 53300, .min_x = 23, .max_x = 236}, + /* y = 206 */ {.offset = 53560, .min_x = 24, .max_x = 235}, + /* y = 207 */ {.offset = 53820, .min_x = 25, .max_x = 234}, + /* y = 208 */ {.offset = 54080, .min_x = 26, .max_x = 233}, + /* y = 209 */ {.offset = 54340, .min_x = 26, .max_x = 233}, + /* y = 210 */ {.offset = 54600, .min_x = 27, .max_x = 232}, + /* y = 211 */ {.offset = 54860, .min_x = 28, .max_x = 231}, + /* y = 212 */ {.offset = 55120, .min_x = 29, .max_x = 230}, + /* y = 213 */ {.offset = 55380, .min_x = 29, .max_x = 230}, + /* y = 214 */ {.offset = 55640, .min_x = 30, .max_x = 229}, + /* y = 215 */ {.offset = 55900, .min_x = 31, .max_x = 228}, + /* y = 216 */ {.offset = 56160, .min_x = 32, .max_x = 227}, + /* y = 217 */ {.offset = 56420, .min_x = 33, .max_x = 226}, + /* y = 218 */ {.offset = 56680, .min_x = 34, .max_x = 225}, + /* y = 219 */ {.offset = 56940, .min_x = 35, .max_x = 224}, + /* y = 220 */ {.offset = 57200, .min_x = 36, .max_x = 223}, + /* y = 221 */ {.offset = 57460, .min_x = 37, .max_x = 222}, + /* y = 222 */ {.offset = 57720, .min_x = 38, .max_x = 221}, + /* y = 223 */ {.offset = 57980, .min_x = 39, .max_x = 220}, + /* y = 224 */ {.offset = 58240, .min_x = 40, .max_x = 219}, + /* y = 225 */ {.offset = 58500, .min_x = 41, .max_x = 218}, + /* y = 226 */ {.offset = 58760, .min_x = 42, .max_x = 217}, + /* y = 227 */ {.offset = 59020, .min_x = 43, .max_x = 216}, + /* y = 228 */ {.offset = 59280, .min_x = 44, .max_x = 215}, + /* y = 229 */ {.offset = 59540, .min_x = 45, .max_x = 214}, + /* y = 230 */ {.offset = 59800, .min_x = 46, .max_x = 213}, + /* y = 231 */ {.offset = 60060, .min_x = 48, .max_x = 211}, + /* y = 232 */ {.offset = 60320, .min_x = 49, .max_x = 210}, + /* y = 233 */ {.offset = 60580, .min_x = 50, .max_x = 209}, + /* y = 234 */ {.offset = 60840, .min_x = 52, .max_x = 207}, + /* y = 235 */ {.offset = 61100, .min_x = 53, .max_x = 206}, + /* y = 236 */ {.offset = 61360, .min_x = 54, .max_x = 205}, + /* y = 237 */ {.offset = 61620, .min_x = 56, .max_x = 203}, + /* y = 238 */ {.offset = 61880, .min_x = 57, .max_x = 202}, + /* y = 239 */ {.offset = 62140, .min_x = 59, .max_x = 200}, + /* y = 240 */ {.offset = 62400, .min_x = 60, .max_x = 199}, + /* y = 241 */ {.offset = 62660, .min_x = 62, .max_x = 197}, + /* y = 242 */ {.offset = 62920, .min_x = 64, .max_x = 195}, + /* y = 243 */ {.offset = 63180, .min_x = 65, .max_x = 194}, + /* y = 244 */ {.offset = 63440, .min_x = 67, .max_x = 192}, + /* y = 245 */ {.offset = 63700, .min_x = 69, .max_x = 190}, + /* y = 246 */ {.offset = 63960, .min_x = 71, .max_x = 188}, + /* y = 247 */ {.offset = 64220, .min_x = 73, .max_x = 186}, + /* y = 248 */ {.offset = 64480, .min_x = 75, .max_x = 184}, + /* y = 249 */ {.offset = 64740, .min_x = 77, .max_x = 182}, + /* y = 250 */ {.offset = 65000, .min_x = 80, .max_x = 179}, + /* y = 251 */ {.offset = 65260, .min_x = 82, .max_x = 177}, + /* y = 252 */ {.offset = 65520, .min_x = 85, .max_x = 174}, + /* y = 253 */ {.offset = 65780, .min_x = 87, .max_x = 172}, + /* y = 254 */ {.offset = 66040, .min_x = 90, .max_x = 169}, + /* y = 255 */ {.offset = 66300, .min_x = 94, .max_x = 165}, + /* y = 256 */ {.offset = 66560, .min_x = 98, .max_x = 161}, + /* y = 257 */ {.offset = 66820, .min_x = 102, .max_x = 157}, + /* y = 258 */ {.offset = 67080, .min_x = 107, .max_x = 152}, + /* y = 259 */ {.offset = 67340, .min_x = 113, .max_x = 146}, +}; + +// g_gbitmap_legacy_3x_data_row_infos was generated with this script: +// +// #!/bin/env python +// # Using RECTANGULAR offsets (y * 180) instead of packed circular offsets +// # to enable in-place pixel format conversion in display driver +// topleft_mask = [ +// 76, 71, 66, 63, 60, 57, 55, 52, 50, 48, 46, 45, 43, 41, 40, 38, 37, +// 36, 34, 33, 32, 31, 29, 28, 27, 26, 25, 24, 23, 22, 22, 21, 20, 19, +// 18, 18, 17, 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, +// 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// ] +// +// for i in range(0, 180): +// if i < 90: +// min_x = topleft_mask[i] +// else: +// min_x = topleft_mask[180 - i - 1] +// +// max_x = 180 - min_x - 1 +// offset = i * 180 # rectangular offset +// print( +// " /" +// + "* y = %3d */ {.offset = %5d, .min_x = %3d, .max_x = %3d}," +// % (i, offset, min_x, max_x) +// ) +// +// print("Rectangular Framebuffer has %d bytes" % (180 * 180)) + +const void * const g_gbitmap_legacy_3x_data_row_infos = (const GBitmapDataRowInfoInternal[]) { + /* y = 0 */ {.offset = 0, .min_x = 76, .max_x = 103}, + /* y = 1 */ {.offset = 180, .min_x = 71, .max_x = 108}, + /* y = 2 */ {.offset = 360, .min_x = 66, .max_x = 113}, + /* y = 3 */ {.offset = 540, .min_x = 63, .max_x = 116}, + /* y = 4 */ {.offset = 720, .min_x = 60, .max_x = 119}, + /* y = 5 */ {.offset = 900, .min_x = 57, .max_x = 122}, + /* y = 6 */ {.offset = 1080, .min_x = 55, .max_x = 124}, + /* y = 7 */ {.offset = 1260, .min_x = 52, .max_x = 127}, + /* y = 8 */ {.offset = 1440, .min_x = 50, .max_x = 129}, + /* y = 9 */ {.offset = 1620, .min_x = 48, .max_x = 131}, + /* y = 10 */ {.offset = 1800, .min_x = 46, .max_x = 133}, + /* y = 11 */ {.offset = 1980, .min_x = 45, .max_x = 134}, + /* y = 12 */ {.offset = 2160, .min_x = 43, .max_x = 136}, + /* y = 13 */ {.offset = 2340, .min_x = 41, .max_x = 138}, + /* y = 14 */ {.offset = 2520, .min_x = 40, .max_x = 139}, + /* y = 15 */ {.offset = 2700, .min_x = 38, .max_x = 141}, + /* y = 16 */ {.offset = 2880, .min_x = 37, .max_x = 142}, + /* y = 17 */ {.offset = 3060, .min_x = 36, .max_x = 143}, + /* y = 18 */ {.offset = 3240, .min_x = 34, .max_x = 145}, + /* y = 19 */ {.offset = 3420, .min_x = 33, .max_x = 146}, + /* y = 20 */ {.offset = 3600, .min_x = 32, .max_x = 147}, + /* y = 21 */ {.offset = 3780, .min_x = 31, .max_x = 148}, + /* y = 22 */ {.offset = 3960, .min_x = 29, .max_x = 150}, + /* y = 23 */ {.offset = 4140, .min_x = 28, .max_x = 151}, + /* y = 24 */ {.offset = 4320, .min_x = 27, .max_x = 152}, + /* y = 25 */ {.offset = 4500, .min_x = 26, .max_x = 153}, + /* y = 26 */ {.offset = 4680, .min_x = 25, .max_x = 154}, + /* y = 27 */ {.offset = 4860, .min_x = 24, .max_x = 155}, + /* y = 28 */ {.offset = 5040, .min_x = 23, .max_x = 156}, + /* y = 29 */ {.offset = 5220, .min_x = 22, .max_x = 157}, + /* y = 30 */ {.offset = 5400, .min_x = 22, .max_x = 157}, + /* y = 31 */ {.offset = 5580, .min_x = 21, .max_x = 158}, + /* y = 32 */ {.offset = 5760, .min_x = 20, .max_x = 159}, + /* y = 33 */ {.offset = 5940, .min_x = 19, .max_x = 160}, + /* y = 34 */ {.offset = 6120, .min_x = 18, .max_x = 161}, + /* y = 35 */ {.offset = 6300, .min_x = 18, .max_x = 161}, + /* y = 36 */ {.offset = 6480, .min_x = 17, .max_x = 162}, + /* y = 37 */ {.offset = 6660, .min_x = 16, .max_x = 163}, + /* y = 38 */ {.offset = 6840, .min_x = 15, .max_x = 164}, + /* y = 39 */ {.offset = 7020, .min_x = 15, .max_x = 164}, + /* y = 40 */ {.offset = 7200, .min_x = 14, .max_x = 165}, + /* y = 41 */ {.offset = 7380, .min_x = 13, .max_x = 166}, + /* y = 42 */ {.offset = 7560, .min_x = 13, .max_x = 166}, + /* y = 43 */ {.offset = 7740, .min_x = 12, .max_x = 167}, + /* y = 44 */ {.offset = 7920, .min_x = 12, .max_x = 167}, + /* y = 45 */ {.offset = 8100, .min_x = 11, .max_x = 168}, + /* y = 46 */ {.offset = 8280, .min_x = 10, .max_x = 169}, + /* y = 47 */ {.offset = 8460, .min_x = 10, .max_x = 169}, + /* y = 48 */ {.offset = 8640, .min_x = 9, .max_x = 170}, + /* y = 49 */ {.offset = 8820, .min_x = 9, .max_x = 170}, + /* y = 50 */ {.offset = 9000, .min_x = 8, .max_x = 171}, + /* y = 51 */ {.offset = 9180, .min_x = 8, .max_x = 171}, + /* y = 52 */ {.offset = 9360, .min_x = 7, .max_x = 172}, + /* y = 53 */ {.offset = 9540, .min_x = 7, .max_x = 172}, + /* y = 54 */ {.offset = 9720, .min_x = 7, .max_x = 172}, + /* y = 55 */ {.offset = 9900, .min_x = 6, .max_x = 173}, + /* y = 56 */ {.offset = 10080, .min_x = 6, .max_x = 173}, + /* y = 57 */ {.offset = 10260, .min_x = 5, .max_x = 174}, + /* y = 58 */ {.offset = 10440, .min_x = 5, .max_x = 174}, + /* y = 59 */ {.offset = 10620, .min_x = 5, .max_x = 174}, + /* y = 60 */ {.offset = 10800, .min_x = 4, .max_x = 175}, + /* y = 61 */ {.offset = 10980, .min_x = 4, .max_x = 175}, + /* y = 62 */ {.offset = 11160, .min_x = 4, .max_x = 175}, + /* y = 63 */ {.offset = 11340, .min_x = 3, .max_x = 176}, + /* y = 64 */ {.offset = 11520, .min_x = 3, .max_x = 176}, + /* y = 65 */ {.offset = 11700, .min_x = 3, .max_x = 176}, + /* y = 66 */ {.offset = 11880, .min_x = 2, .max_x = 177}, + /* y = 67 */ {.offset = 12060, .min_x = 2, .max_x = 177}, + /* y = 68 */ {.offset = 12240, .min_x = 2, .max_x = 177}, + /* y = 69 */ {.offset = 12420, .min_x = 2, .max_x = 177}, + /* y = 70 */ {.offset = 12600, .min_x = 2, .max_x = 177}, + /* y = 71 */ {.offset = 12780, .min_x = 1, .max_x = 178}, + /* y = 72 */ {.offset = 12960, .min_x = 1, .max_x = 178}, + /* y = 73 */ {.offset = 13140, .min_x = 1, .max_x = 178}, + /* y = 74 */ {.offset = 13320, .min_x = 1, .max_x = 178}, + /* y = 75 */ {.offset = 13500, .min_x = 1, .max_x = 178}, + /* y = 76 */ {.offset = 13680, .min_x = 0, .max_x = 179}, + /* y = 77 */ {.offset = 13860, .min_x = 0, .max_x = 179}, + /* y = 78 */ {.offset = 14040, .min_x = 0, .max_x = 179}, + /* y = 79 */ {.offset = 14220, .min_x = 0, .max_x = 179}, + /* y = 80 */ {.offset = 14400, .min_x = 0, .max_x = 179}, + /* y = 81 */ {.offset = 14580, .min_x = 0, .max_x = 179}, + /* y = 82 */ {.offset = 14760, .min_x = 0, .max_x = 179}, + /* y = 83 */ {.offset = 14940, .min_x = 0, .max_x = 179}, + /* y = 84 */ {.offset = 15120, .min_x = 0, .max_x = 179}, + /* y = 85 */ {.offset = 15300, .min_x = 0, .max_x = 179}, + /* y = 86 */ {.offset = 15480, .min_x = 0, .max_x = 179}, + /* y = 87 */ {.offset = 15660, .min_x = 0, .max_x = 179}, + /* y = 88 */ {.offset = 15840, .min_x = 0, .max_x = 179}, + /* y = 89 */ {.offset = 16020, .min_x = 0, .max_x = 179}, + /* y = 90 */ {.offset = 16200, .min_x = 0, .max_x = 179}, + /* y = 91 */ {.offset = 16380, .min_x = 0, .max_x = 179}, + /* y = 92 */ {.offset = 16560, .min_x = 0, .max_x = 179}, + /* y = 93 */ {.offset = 16740, .min_x = 0, .max_x = 179}, + /* y = 94 */ {.offset = 16920, .min_x = 0, .max_x = 179}, + /* y = 95 */ {.offset = 17100, .min_x = 0, .max_x = 179}, + /* y = 96 */ {.offset = 17280, .min_x = 0, .max_x = 179}, + /* y = 97 */ {.offset = 17460, .min_x = 0, .max_x = 179}, + /* y = 98 */ {.offset = 17640, .min_x = 0, .max_x = 179}, + /* y = 99 */ {.offset = 17820, .min_x = 0, .max_x = 179}, + /* y = 100 */ {.offset = 18000, .min_x = 0, .max_x = 179}, + /* y = 101 */ {.offset = 18180, .min_x = 0, .max_x = 179}, + /* y = 102 */ {.offset = 18360, .min_x = 0, .max_x = 179}, + /* y = 103 */ {.offset = 18540, .min_x = 0, .max_x = 179}, + /* y = 104 */ {.offset = 18720, .min_x = 1, .max_x = 178}, + /* y = 105 */ {.offset = 18900, .min_x = 1, .max_x = 178}, + /* y = 106 */ {.offset = 19080, .min_x = 1, .max_x = 178}, + /* y = 107 */ {.offset = 19260, .min_x = 1, .max_x = 178}, + /* y = 108 */ {.offset = 19440, .min_x = 1, .max_x = 178}, + /* y = 109 */ {.offset = 19620, .min_x = 2, .max_x = 177}, + /* y = 110 */ {.offset = 19800, .min_x = 2, .max_x = 177}, + /* y = 111 */ {.offset = 19980, .min_x = 2, .max_x = 177}, + /* y = 112 */ {.offset = 20160, .min_x = 2, .max_x = 177}, + /* y = 113 */ {.offset = 20340, .min_x = 2, .max_x = 177}, + /* y = 114 */ {.offset = 20520, .min_x = 3, .max_x = 176}, + /* y = 115 */ {.offset = 20700, .min_x = 3, .max_x = 176}, + /* y = 116 */ {.offset = 20880, .min_x = 3, .max_x = 176}, + /* y = 117 */ {.offset = 21060, .min_x = 4, .max_x = 175}, + /* y = 118 */ {.offset = 21240, .min_x = 4, .max_x = 175}, + /* y = 119 */ {.offset = 21420, .min_x = 4, .max_x = 175}, + /* y = 120 */ {.offset = 21600, .min_x = 5, .max_x = 174}, + /* y = 121 */ {.offset = 21780, .min_x = 5, .max_x = 174}, + /* y = 122 */ {.offset = 21960, .min_x = 5, .max_x = 174}, + /* y = 123 */ {.offset = 22140, .min_x = 6, .max_x = 173}, + /* y = 124 */ {.offset = 22320, .min_x = 6, .max_x = 173}, + /* y = 125 */ {.offset = 22500, .min_x = 7, .max_x = 172}, + /* y = 126 */ {.offset = 22680, .min_x = 7, .max_x = 172}, + /* y = 127 */ {.offset = 22860, .min_x = 7, .max_x = 172}, + /* y = 128 */ {.offset = 23040, .min_x = 8, .max_x = 171}, + /* y = 129 */ {.offset = 23220, .min_x = 8, .max_x = 171}, + /* y = 130 */ {.offset = 23400, .min_x = 9, .max_x = 170}, + /* y = 131 */ {.offset = 23580, .min_x = 9, .max_x = 170}, + /* y = 132 */ {.offset = 23760, .min_x = 10, .max_x = 169}, + /* y = 133 */ {.offset = 23940, .min_x = 10, .max_x = 169}, + /* y = 134 */ {.offset = 24120, .min_x = 11, .max_x = 168}, + /* y = 135 */ {.offset = 24300, .min_x = 12, .max_x = 167}, + /* y = 136 */ {.offset = 24480, .min_x = 12, .max_x = 167}, + /* y = 137 */ {.offset = 24660, .min_x = 13, .max_x = 166}, + /* y = 138 */ {.offset = 24840, .min_x = 13, .max_x = 166}, + /* y = 139 */ {.offset = 25020, .min_x = 14, .max_x = 165}, + /* y = 140 */ {.offset = 25200, .min_x = 15, .max_x = 164}, + /* y = 141 */ {.offset = 25380, .min_x = 15, .max_x = 164}, + /* y = 142 */ {.offset = 25560, .min_x = 16, .max_x = 163}, + /* y = 143 */ {.offset = 25740, .min_x = 17, .max_x = 162}, + /* y = 144 */ {.offset = 25920, .min_x = 18, .max_x = 161}, + /* y = 145 */ {.offset = 26100, .min_x = 18, .max_x = 161}, + /* y = 146 */ {.offset = 26280, .min_x = 19, .max_x = 160}, + /* y = 147 */ {.offset = 26460, .min_x = 20, .max_x = 159}, + /* y = 148 */ {.offset = 26640, .min_x = 21, .max_x = 158}, + /* y = 149 */ {.offset = 26820, .min_x = 22, .max_x = 157}, + /* y = 150 */ {.offset = 27000, .min_x = 22, .max_x = 157}, + /* y = 151 */ {.offset = 27180, .min_x = 23, .max_x = 156}, + /* y = 152 */ {.offset = 27360, .min_x = 24, .max_x = 155}, + /* y = 153 */ {.offset = 27540, .min_x = 25, .max_x = 154}, + /* y = 154 */ {.offset = 27720, .min_x = 26, .max_x = 153}, + /* y = 155 */ {.offset = 27900, .min_x = 27, .max_x = 152}, + /* y = 156 */ {.offset = 28080, .min_x = 28, .max_x = 151}, + /* y = 157 */ {.offset = 28260, .min_x = 29, .max_x = 150}, + /* y = 158 */ {.offset = 28440, .min_x = 31, .max_x = 148}, + /* y = 159 */ {.offset = 28620, .min_x = 32, .max_x = 147}, + /* y = 160 */ {.offset = 28800, .min_x = 33, .max_x = 146}, + /* y = 161 */ {.offset = 28980, .min_x = 34, .max_x = 145}, + /* y = 162 */ {.offset = 29160, .min_x = 36, .max_x = 143}, + /* y = 163 */ {.offset = 29340, .min_x = 37, .max_x = 142}, + /* y = 164 */ {.offset = 29520, .min_x = 38, .max_x = 141}, + /* y = 165 */ {.offset = 29700, .min_x = 40, .max_x = 139}, + /* y = 166 */ {.offset = 29880, .min_x = 41, .max_x = 138}, + /* y = 167 */ {.offset = 30060, .min_x = 43, .max_x = 136}, + /* y = 168 */ {.offset = 30240, .min_x = 45, .max_x = 134}, + /* y = 169 */ {.offset = 30420, .min_x = 46, .max_x = 133}, + /* y = 170 */ {.offset = 30600, .min_x = 48, .max_x = 131}, + /* y = 171 */ {.offset = 30780, .min_x = 50, .max_x = 129}, + /* y = 172 */ {.offset = 30960, .min_x = 52, .max_x = 127}, + /* y = 173 */ {.offset = 31140, .min_x = 55, .max_x = 124}, + /* y = 174 */ {.offset = 31320, .min_x = 57, .max_x = 122}, + /* y = 175 */ {.offset = 31500, .min_x = 60, .max_x = 119}, + /* y = 176 */ {.offset = 31680, .min_x = 63, .max_x = 116}, + /* y = 177 */ {.offset = 31860, .min_x = 66, .max_x = 113}, + /* y = 178 */ {.offset = 32040, .min_x = 71, .max_x = 108}, + /* y = 179 */ {.offset = 32220, .min_x = 76, .max_x = 103}, +}; diff --git a/src/fw/board/displays/display_getafix.h b/src/fw/board/displays/display_getafix.h new file mode 100755 index 0000000000..4928ae6257 --- /dev/null +++ b/src/fw/board/displays/display_getafix.h @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 +#define DISPLAY_ORIENTATION_ROTATED_180 1 +#define DISPLAY_ORIENTATION_ROW_MAJOR 0 +#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1 + +#define PBL_BW 0 +#define PBL_COLOR 1 + +#define PBL_RECT 0 +#define PBL_ROUND 1 + +#define PBL_DISPLAY_WIDTH 260 +#define PBL_DISPLAY_HEIGHT 260 + +// Getafix doesn't support 2x apps, but need to define these anyways so it builds +#define LEGACY_2X_DISP_COLS 180 +#define LEGACY_2X_DISP_ROWS 180 +#define LEGACY_3X_DISP_COLS 180 +#define LEGACY_3X_DISP_ROWS 180 + +// Use rectangular framebuffer to enable in-place pixel format conversion in display driver +// This uses more memory than circular (67600 vs 53786) but saves the separate display buffer +#define DISPLAY_FRAMEBUFFER_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) + +extern const void * const g_gbitmap_data_row_infos; +extern const void * const g_gbitmap_legacy_3x_data_row_infos; \ No newline at end of file diff --git a/src/fw/board/displays/display_obelix.h b/src/fw/board/displays/display_obelix.h index 825170df12..55a3bb8738 100755 --- a/src/fw/board/displays/display_obelix.h +++ b/src/fw/board/displays/display_obelix.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/board/displays/display_qemu_emery.h b/src/fw/board/displays/display_qemu_emery.h new file mode 100644 index 0000000000..fa3dffb518 --- /dev/null +++ b/src/fw/board/displays/display_qemu_emery.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 +#define DISPLAY_ORIENTATION_ROTATED_180 0 +#define DISPLAY_ORIENTATION_ROW_MAJOR 0 +#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1 + +#define PBL_BW 0 +#define PBL_COLOR 1 + +#define PBL_RECT 1 +#define PBL_ROUND 0 + +#define PBL_DISPLAY_WIDTH 200 +#define PBL_DISPLAY_HEIGHT 228 + +#define LEGACY_2X_DISP_COLS 144 +#define LEGACY_2X_DISP_ROWS 168 +#define LEGACY_3X_DISP_COLS LEGACY_2X_DISP_COLS +#define LEGACY_3X_DISP_ROWS LEGACY_2X_DISP_ROWS + +#define DISPLAY_FRAMEBUFFER_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_qemu_flint.h b/src/fw/board/displays/display_qemu_flint.h new file mode 100644 index 0000000000..6da718d93c --- /dev/null +++ b/src/fw/board/displays/display_qemu_flint.h @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 +#define DISPLAY_ORIENTATION_ROTATED_180 1 +#define DISPLAY_ORIENTATION_ROW_MAJOR 0 +#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 0 + +#define PBL_BW 1 +#define PBL_COLOR 0 + +#define PBL_RECT 1 +#define PBL_ROUND 0 + +#define PBL_DISPLAY_WIDTH 144 +#define PBL_DISPLAY_HEIGHT 168 + +#define LEGACY_2X_DISP_COLS PBL_DISPLAY_WIDTH +#define LEGACY_2X_DISP_ROWS PBL_DISPLAY_HEIGHT +#define LEGACY_3X_DISP_COLS PBL_DISPLAY_WIDTH +#define LEGACY_3X_DISP_ROWS PBL_DISPLAY_HEIGHT + +#define DISPLAY_FRAMEBUFFER_BYTES \ + (ROUND_TO_MOD_CEIL(PBL_DISPLAY_WIDTH, 32) / 8 * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_qemu_gabbro.h b/src/fw/board/displays/display_qemu_gabbro.h new file mode 100644 index 0000000000..ec44415f4c --- /dev/null +++ b/src/fw/board/displays/display_qemu_gabbro.h @@ -0,0 +1,28 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 +#define DISPLAY_ORIENTATION_ROTATED_180 1 +#define DISPLAY_ORIENTATION_ROW_MAJOR 0 +#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1 + +#define PBL_BW 0 +#define PBL_COLOR 1 + +#define PBL_RECT 0 +#define PBL_ROUND 1 + +#define PBL_DISPLAY_WIDTH 260 +#define PBL_DISPLAY_HEIGHT 260 + +#define LEGACY_2X_DISP_COLS 180 +#define LEGACY_2X_DISP_ROWS 180 +#define LEGACY_3X_DISP_COLS 180 +#define LEGACY_3X_DISP_ROWS 180 + +#define DISPLAY_FRAMEBUFFER_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) + +extern const void * const g_gbitmap_data_row_infos; +extern const void * const g_gbitmap_legacy_3x_data_row_infos; diff --git a/src/fw/board/displays/display_robert.h b/src/fw/board/displays/display_robert.h deleted file mode 100644 index 1e865ab601..0000000000 --- a/src/fw/board/displays/display_robert.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 -#define DISPLAY_ORIENTATION_ROTATED_180 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1 - -#define PBL_BW 0 -#define PBL_COLOR 1 - -#define PBL_RECT 1 -#define PBL_ROUND 0 - -#define PBL_DISPLAY_WIDTH 200 -#define PBL_DISPLAY_HEIGHT 228 - -#define LEGACY_2X_DISP_COLS 144 -#define LEGACY_2X_DISP_ROWS 168 -#define LEGACY_3X_DISP_COLS LEGACY_2X_DISP_COLS -#define LEGACY_3X_DISP_ROWS LEGACY_2X_DISP_ROWS - -#define DISPLAY_FRAMEBUFFER_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_robert_evt.h b/src/fw/board/displays/display_robert_evt.h deleted file mode 100644 index f14a5940ae..0000000000 --- a/src/fw/board/displays/display_robert_evt.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 -#define DISPLAY_ORIENTATION_ROTATED_180 1 -#define DISPLAY_ORIENTATION_ROW_MAJOR 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1 - -#define PBL_BW 0 -#define PBL_COLOR 1 - -#define PBL_RECT 1 -#define PBL_ROUND 0 - -#define PBL_DISPLAY_WIDTH 200 -#define PBL_DISPLAY_HEIGHT 228 - -#define LEGACY_2X_DISP_COLS 144 -#define LEGACY_2X_DISP_ROWS 168 -#define LEGACY_3X_DISP_COLS LEGACY_2X_DISP_COLS -#define LEGACY_3X_DISP_ROWS LEGACY_2X_DISP_ROWS - -#define DISPLAY_FRAMEBUFFER_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_silk.h b/src/fw/board/displays/display_silk.h deleted file mode 100644 index ef2846dd8a..0000000000 --- a/src/fw/board/displays/display_silk.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 -#define DISPLAY_ORIENTATION_ROTATED_180 1 -#define DISPLAY_ORIENTATION_ROW_MAJOR 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 0 - -#define PBL_BW 1 -#define PBL_COLOR 0 - -#define PBL_RECT 1 -#define PBL_ROUND 0 - -#define PBL_DISPLAY_WIDTH 144 -#define PBL_DISPLAY_HEIGHT 168 - -#define LEGACY_2X_DISP_COLS PBL_DISPLAY_WIDTH -#define LEGACY_2X_DISP_ROWS PBL_DISPLAY_HEIGHT -#define LEGACY_3X_DISP_COLS PBL_DISPLAY_WIDTH -#define LEGACY_3X_DISP_ROWS PBL_DISPLAY_HEIGHT - -#define DISPLAY_FRAMEBUFFER_BYTES \ - (ROUND_TO_MOD_CEIL(PBL_DISPLAY_WIDTH, 32) / 8 * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_snowy.h b/src/fw/board/displays/display_snowy.h deleted file mode 100644 index ecf9d7f9a4..0000000000 --- a/src/fw/board/displays/display_snowy.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 1 -#define DISPLAY_ORIENTATION_ROTATED_180 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 0 - -#define PBL_BW 0 -#define PBL_COLOR 1 - -#define PBL_RECT 1 -#define PBL_ROUND 0 - -#define PBL_DISPLAY_WIDTH 144 -#define PBL_DISPLAY_HEIGHT 168 - -#define LEGACY_2X_DISP_COLS DISP_COLS -#define LEGACY_2X_DISP_ROWS DISP_ROWS -#define LEGACY_3X_DISP_COLS DISP_COLS -#define LEGACY_3X_DISP_ROWS DISP_ROWS - -#define DISPLAY_FRAMEBUFFER_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/displays/display_spalding.c b/src/fw/board/displays/display_spalding.c deleted file mode 100644 index 33ee9a1dad..0000000000 --- a/src/fw/board/displays/display_spalding.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Spalding display pixel masks -// -// The Spalding round display is logically a square 180x180 display with -// some of the pixels hidden under a mask or missing entirely. The mask -// is symmetrical both horizontally and vertically: the masks on the -// left and right side of a line are equal, and the mask on the top half -// of the display is a mirror image of the bottom half. -// -// This array maps the number of pixels masked off for one quadrant of -// the display. Array element zero is the number of masked pixels from -// a display corner inwards. Subsequent array elements contain the mask -// for the adjacent rows or columns moving inwards towards the center -// of the display. - -#include "board/display.h" - -// g_gbitmap_spalding_data_row_infos was generated with this script: -// -// #!/bin/env python -// topleft_mask = [ 76, 71, 66, 63, 60, 57, 55, 52, 50, 48, 46, 45, 43, 41, 40, 38, 37, -// 36, 34, 33, 32, 31, 29, 28, 27, 26, 25, 24, 23, 22, 22, 21, 20, 19, -// 18, 18, 17, 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, -// 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -// -// offset = 76 #pad the offset with 76 so we don't underflow on the first row -// for i in range(0, 180): -// if (i < 90): -// min_x = topleft_mask[i] -// else : -// min_x = topleft_mask[180 - i - 1] -// -// width = 90 - min_x -// max_x = 180 - min_x - 1 -// #individual rows are the current offset minus the min_y to get to the first usable byte -// print(" /" + "* y = %3d */ {.offset = %5d, .min_x = %2d, .max_x = %3d}," % -// (i, offset - min_x, min_x, max_x)) -// # total offset is usable bytes in the row, so accumulate that -// offset += (max_x - min_x + 1) -// -// # pad the size of the buffer before and after by 76 bytes so -// # framebuffer row reads are never accessing memory beyond buffer -// print ("Circular Framebuffer has %d bytes" % (offset + topleft_mask[0])) - -const void * const g_gbitmap_spalding_data_row_infos = (const GBitmapDataRowInfoInternal[]) { - /* y = 0 */ {.offset = 0, .min_x = 76, .max_x = 103}, - /* y = 1 */ {.offset = 33, .min_x = 71, .max_x = 108}, - /* y = 2 */ {.offset = 76, .min_x = 66, .max_x = 113}, - /* y = 3 */ {.offset = 127, .min_x = 63, .max_x = 116}, - /* y = 4 */ {.offset = 184, .min_x = 60, .max_x = 119}, - /* y = 5 */ {.offset = 247, .min_x = 57, .max_x = 122}, - /* y = 6 */ {.offset = 315, .min_x = 55, .max_x = 124}, - /* y = 7 */ {.offset = 388, .min_x = 52, .max_x = 127}, - /* y = 8 */ {.offset = 466, .min_x = 50, .max_x = 129}, - /* y = 9 */ {.offset = 548, .min_x = 48, .max_x = 131}, - /* y = 10 */ {.offset = 634, .min_x = 46, .max_x = 133}, - /* y = 11 */ {.offset = 723, .min_x = 45, .max_x = 134}, - /* y = 12 */ {.offset = 815, .min_x = 43, .max_x = 136}, - /* y = 13 */ {.offset = 911, .min_x = 41, .max_x = 138}, - /* y = 14 */ {.offset = 1010, .min_x = 40, .max_x = 139}, - /* y = 15 */ {.offset = 1112, .min_x = 38, .max_x = 141}, - /* y = 16 */ {.offset = 1217, .min_x = 37, .max_x = 142}, - /* y = 17 */ {.offset = 1324, .min_x = 36, .max_x = 143}, - /* y = 18 */ {.offset = 1434, .min_x = 34, .max_x = 145}, - /* y = 19 */ {.offset = 1547, .min_x = 33, .max_x = 146}, - /* y = 20 */ {.offset = 1662, .min_x = 32, .max_x = 147}, - /* y = 21 */ {.offset = 1779, .min_x = 31, .max_x = 148}, - /* y = 22 */ {.offset = 1899, .min_x = 29, .max_x = 150}, - /* y = 23 */ {.offset = 2022, .min_x = 28, .max_x = 151}, - /* y = 24 */ {.offset = 2147, .min_x = 27, .max_x = 152}, - /* y = 25 */ {.offset = 2274, .min_x = 26, .max_x = 153}, - /* y = 26 */ {.offset = 2403, .min_x = 25, .max_x = 154}, - /* y = 27 */ {.offset = 2534, .min_x = 24, .max_x = 155}, - /* y = 28 */ {.offset = 2667, .min_x = 23, .max_x = 156}, - /* y = 29 */ {.offset = 2802, .min_x = 22, .max_x = 157}, - /* y = 30 */ {.offset = 2938, .min_x = 22, .max_x = 157}, - /* y = 31 */ {.offset = 3075, .min_x = 21, .max_x = 158}, - /* y = 32 */ {.offset = 3214, .min_x = 20, .max_x = 159}, - /* y = 33 */ {.offset = 3355, .min_x = 19, .max_x = 160}, - /* y = 34 */ {.offset = 3498, .min_x = 18, .max_x = 161}, - /* y = 35 */ {.offset = 3642, .min_x = 18, .max_x = 161}, - /* y = 36 */ {.offset = 3787, .min_x = 17, .max_x = 162}, - /* y = 37 */ {.offset = 3934, .min_x = 16, .max_x = 163}, - /* y = 38 */ {.offset = 4083, .min_x = 15, .max_x = 164}, - /* y = 39 */ {.offset = 4233, .min_x = 15, .max_x = 164}, - /* y = 40 */ {.offset = 4384, .min_x = 14, .max_x = 165}, - /* y = 41 */ {.offset = 4537, .min_x = 13, .max_x = 166}, - /* y = 42 */ {.offset = 4691, .min_x = 13, .max_x = 166}, - /* y = 43 */ {.offset = 4846, .min_x = 12, .max_x = 167}, - /* y = 44 */ {.offset = 5002, .min_x = 12, .max_x = 167}, - /* y = 45 */ {.offset = 5159, .min_x = 11, .max_x = 168}, - /* y = 46 */ {.offset = 5318, .min_x = 10, .max_x = 169}, - /* y = 47 */ {.offset = 5478, .min_x = 10, .max_x = 169}, - /* y = 48 */ {.offset = 5639, .min_x = 9, .max_x = 170}, - /* y = 49 */ {.offset = 5801, .min_x = 9, .max_x = 170}, - /* y = 50 */ {.offset = 5964, .min_x = 8, .max_x = 171}, - /* y = 51 */ {.offset = 6128, .min_x = 8, .max_x = 171}, - /* y = 52 */ {.offset = 6293, .min_x = 7, .max_x = 172}, - /* y = 53 */ {.offset = 6459, .min_x = 7, .max_x = 172}, - /* y = 54 */ {.offset = 6625, .min_x = 7, .max_x = 172}, - /* y = 55 */ {.offset = 6792, .min_x = 6, .max_x = 173}, - /* y = 56 */ {.offset = 6960, .min_x = 6, .max_x = 173}, - /* y = 57 */ {.offset = 7129, .min_x = 5, .max_x = 174}, - /* y = 58 */ {.offset = 7299, .min_x = 5, .max_x = 174}, - /* y = 59 */ {.offset = 7469, .min_x = 5, .max_x = 174}, - /* y = 60 */ {.offset = 7640, .min_x = 4, .max_x = 175}, - /* y = 61 */ {.offset = 7812, .min_x = 4, .max_x = 175}, - /* y = 62 */ {.offset = 7984, .min_x = 4, .max_x = 175}, - /* y = 63 */ {.offset = 8157, .min_x = 3, .max_x = 176}, - /* y = 64 */ {.offset = 8331, .min_x = 3, .max_x = 176}, - /* y = 65 */ {.offset = 8505, .min_x = 3, .max_x = 176}, - /* y = 66 */ {.offset = 8680, .min_x = 2, .max_x = 177}, - /* y = 67 */ {.offset = 8856, .min_x = 2, .max_x = 177}, - /* y = 68 */ {.offset = 9032, .min_x = 2, .max_x = 177}, - /* y = 69 */ {.offset = 9208, .min_x = 2, .max_x = 177}, - /* y = 70 */ {.offset = 9384, .min_x = 2, .max_x = 177}, - /* y = 71 */ {.offset = 9561, .min_x = 1, .max_x = 178}, - /* y = 72 */ {.offset = 9739, .min_x = 1, .max_x = 178}, - /* y = 73 */ {.offset = 9917, .min_x = 1, .max_x = 178}, - /* y = 74 */ {.offset = 10095, .min_x = 1, .max_x = 178}, - /* y = 75 */ {.offset = 10273, .min_x = 1, .max_x = 178}, - /* y = 76 */ {.offset = 10452, .min_x = 0, .max_x = 179}, - /* y = 77 */ {.offset = 10632, .min_x = 0, .max_x = 179}, - /* y = 78 */ {.offset = 10812, .min_x = 0, .max_x = 179}, - /* y = 79 */ {.offset = 10992, .min_x = 0, .max_x = 179}, - /* y = 80 */ {.offset = 11172, .min_x = 0, .max_x = 179}, - /* y = 81 */ {.offset = 11352, .min_x = 0, .max_x = 179}, - /* y = 82 */ {.offset = 11532, .min_x = 0, .max_x = 179}, - /* y = 83 */ {.offset = 11712, .min_x = 0, .max_x = 179}, - /* y = 84 */ {.offset = 11892, .min_x = 0, .max_x = 179}, - /* y = 85 */ {.offset = 12072, .min_x = 0, .max_x = 179}, - /* y = 86 */ {.offset = 12252, .min_x = 0, .max_x = 179}, - /* y = 87 */ {.offset = 12432, .min_x = 0, .max_x = 179}, - /* y = 88 */ {.offset = 12612, .min_x = 0, .max_x = 179}, - /* y = 89 */ {.offset = 12792, .min_x = 0, .max_x = 179}, - /* y = 90 */ {.offset = 12972, .min_x = 0, .max_x = 179}, - /* y = 91 */ {.offset = 13152, .min_x = 0, .max_x = 179}, - /* y = 92 */ {.offset = 13332, .min_x = 0, .max_x = 179}, - /* y = 93 */ {.offset = 13512, .min_x = 0, .max_x = 179}, - /* y = 94 */ {.offset = 13692, .min_x = 0, .max_x = 179}, - /* y = 95 */ {.offset = 13872, .min_x = 0, .max_x = 179}, - /* y = 96 */ {.offset = 14052, .min_x = 0, .max_x = 179}, - /* y = 97 */ {.offset = 14232, .min_x = 0, .max_x = 179}, - /* y = 98 */ {.offset = 14412, .min_x = 0, .max_x = 179}, - /* y = 99 */ {.offset = 14592, .min_x = 0, .max_x = 179}, - /* y = 100 */ {.offset = 14772, .min_x = 0, .max_x = 179}, - /* y = 101 */ {.offset = 14952, .min_x = 0, .max_x = 179}, - /* y = 102 */ {.offset = 15132, .min_x = 0, .max_x = 179}, - /* y = 103 */ {.offset = 15312, .min_x = 0, .max_x = 179}, - /* y = 104 */ {.offset = 15491, .min_x = 1, .max_x = 178}, - /* y = 105 */ {.offset = 15669, .min_x = 1, .max_x = 178}, - /* y = 106 */ {.offset = 15847, .min_x = 1, .max_x = 178}, - /* y = 107 */ {.offset = 16025, .min_x = 1, .max_x = 178}, - /* y = 108 */ {.offset = 16203, .min_x = 1, .max_x = 178}, - /* y = 109 */ {.offset = 16380, .min_x = 2, .max_x = 177}, - /* y = 110 */ {.offset = 16556, .min_x = 2, .max_x = 177}, - /* y = 111 */ {.offset = 16732, .min_x = 2, .max_x = 177}, - /* y = 112 */ {.offset = 16908, .min_x = 2, .max_x = 177}, - /* y = 113 */ {.offset = 17084, .min_x = 2, .max_x = 177}, - /* y = 114 */ {.offset = 17259, .min_x = 3, .max_x = 176}, - /* y = 115 */ {.offset = 17433, .min_x = 3, .max_x = 176}, - /* y = 116 */ {.offset = 17607, .min_x = 3, .max_x = 176}, - /* y = 117 */ {.offset = 17780, .min_x = 4, .max_x = 175}, - /* y = 118 */ {.offset = 17952, .min_x = 4, .max_x = 175}, - /* y = 119 */ {.offset = 18124, .min_x = 4, .max_x = 175}, - /* y = 120 */ {.offset = 18295, .min_x = 5, .max_x = 174}, - /* y = 121 */ {.offset = 18465, .min_x = 5, .max_x = 174}, - /* y = 122 */ {.offset = 18635, .min_x = 5, .max_x = 174}, - /* y = 123 */ {.offset = 18804, .min_x = 6, .max_x = 173}, - /* y = 124 */ {.offset = 18972, .min_x = 6, .max_x = 173}, - /* y = 125 */ {.offset = 19139, .min_x = 7, .max_x = 172}, - /* y = 126 */ {.offset = 19305, .min_x = 7, .max_x = 172}, - /* y = 127 */ {.offset = 19471, .min_x = 7, .max_x = 172}, - /* y = 128 */ {.offset = 19636, .min_x = 8, .max_x = 171}, - /* y = 129 */ {.offset = 19800, .min_x = 8, .max_x = 171}, - /* y = 130 */ {.offset = 19963, .min_x = 9, .max_x = 170}, - /* y = 131 */ {.offset = 20125, .min_x = 9, .max_x = 170}, - /* y = 132 */ {.offset = 20286, .min_x = 10, .max_x = 169}, - /* y = 133 */ {.offset = 20446, .min_x = 10, .max_x = 169}, - /* y = 134 */ {.offset = 20605, .min_x = 11, .max_x = 168}, - /* y = 135 */ {.offset = 20762, .min_x = 12, .max_x = 167}, - /* y = 136 */ {.offset = 20918, .min_x = 12, .max_x = 167}, - /* y = 137 */ {.offset = 21073, .min_x = 13, .max_x = 166}, - /* y = 138 */ {.offset = 21227, .min_x = 13, .max_x = 166}, - /* y = 139 */ {.offset = 21380, .min_x = 14, .max_x = 165}, - /* y = 140 */ {.offset = 21531, .min_x = 15, .max_x = 164}, - /* y = 141 */ {.offset = 21681, .min_x = 15, .max_x = 164}, - /* y = 142 */ {.offset = 21830, .min_x = 16, .max_x = 163}, - /* y = 143 */ {.offset = 21977, .min_x = 17, .max_x = 162}, - /* y = 144 */ {.offset = 22122, .min_x = 18, .max_x = 161}, - /* y = 145 */ {.offset = 22266, .min_x = 18, .max_x = 161}, - /* y = 146 */ {.offset = 22409, .min_x = 19, .max_x = 160}, - /* y = 147 */ {.offset = 22550, .min_x = 20, .max_x = 159}, - /* y = 148 */ {.offset = 22689, .min_x = 21, .max_x = 158}, - /* y = 149 */ {.offset = 22826, .min_x = 22, .max_x = 157}, - /* y = 150 */ {.offset = 22962, .min_x = 22, .max_x = 157}, - /* y = 151 */ {.offset = 23097, .min_x = 23, .max_x = 156}, - /* y = 152 */ {.offset = 23230, .min_x = 24, .max_x = 155}, - /* y = 153 */ {.offset = 23361, .min_x = 25, .max_x = 154}, - /* y = 154 */ {.offset = 23490, .min_x = 26, .max_x = 153}, - /* y = 155 */ {.offset = 23617, .min_x = 27, .max_x = 152}, - /* y = 156 */ {.offset = 23742, .min_x = 28, .max_x = 151}, - /* y = 157 */ {.offset = 23865, .min_x = 29, .max_x = 150}, - /* y = 158 */ {.offset = 23985, .min_x = 31, .max_x = 148}, - /* y = 159 */ {.offset = 24102, .min_x = 32, .max_x = 147}, - /* y = 160 */ {.offset = 24217, .min_x = 33, .max_x = 146}, - /* y = 161 */ {.offset = 24330, .min_x = 34, .max_x = 145}, - /* y = 162 */ {.offset = 24440, .min_x = 36, .max_x = 143}, - /* y = 163 */ {.offset = 24547, .min_x = 37, .max_x = 142}, - /* y = 164 */ {.offset = 24652, .min_x = 38, .max_x = 141}, - /* y = 165 */ {.offset = 24754, .min_x = 40, .max_x = 139}, - /* y = 166 */ {.offset = 24853, .min_x = 41, .max_x = 138}, - /* y = 167 */ {.offset = 24949, .min_x = 43, .max_x = 136}, - /* y = 168 */ {.offset = 25041, .min_x = 45, .max_x = 134}, - /* y = 169 */ {.offset = 25130, .min_x = 46, .max_x = 133}, - /* y = 170 */ {.offset = 25216, .min_x = 48, .max_x = 131}, - /* y = 171 */ {.offset = 25298, .min_x = 50, .max_x = 129}, - /* y = 172 */ {.offset = 25376, .min_x = 52, .max_x = 127}, - /* y = 173 */ {.offset = 25449, .min_x = 55, .max_x = 124}, - /* y = 174 */ {.offset = 25517, .min_x = 57, .max_x = 122}, - /* y = 175 */ {.offset = 25580, .min_x = 60, .max_x = 119}, - /* y = 176 */ {.offset = 25637, .min_x = 63, .max_x = 116}, - /* y = 177 */ {.offset = 25688, .min_x = 66, .max_x = 113}, - /* y = 178 */ {.offset = 25731, .min_x = 71, .max_x = 108}, - /* y = 179 */ {.offset = 25764, .min_x = 76, .max_x = 103}, -}; diff --git a/src/fw/board/displays/display_spalding.h b/src/fw/board/displays/display_spalding.h deleted file mode 100644 index 7757176f29..0000000000 --- a/src/fw/board/displays/display_spalding.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 -#define DISPLAY_ORIENTATION_ROTATED_180 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR 1 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 0 - -#define PBL_BW 0 -#define PBL_COLOR 1 - -#define PBL_RECT 0 -#define PBL_ROUND 1 - -#define PBL_DISPLAY_WIDTH 180 -#define PBL_DISPLAY_HEIGHT 180 - -// Spalding doesn't support 2x apps, but need to define these anyways so it builds -#define LEGACY_2X_DISP_COLS DISP_COLS -#define LEGACY_2X_DISP_ROWS DISP_ROWS -#define LEGACY_3X_DISP_COLS DISP_COLS -#define LEGACY_3X_DISP_ROWS DISP_ROWS - -// all pixels + 76 padding pixels before the first/after the last row -#define DISPLAY_FRAMEBUFFER_BYTES 25944 - -extern const void * const g_gbitmap_spalding_data_row_infos; diff --git a/src/fw/board/displays/display_tintin.h b/src/fw/board/displays/display_tintin.h deleted file mode 100644 index e6e9358d3e..0000000000 --- a/src/fw/board/displays/display_tintin.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0 -#define DISPLAY_ORIENTATION_ROTATED_180 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR 0 -#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 0 - -#define PBL_BW 1 -#define PBL_COLOR 0 - -#define PBL_RECT 1 -#define PBL_ROUND 0 - -#define PBL_DISPLAY_WIDTH 144 -#define PBL_DISPLAY_HEIGHT 168 - -#define LEGACY_2X_DISP_COLS PBL_DISPLAY_WIDTH -#define LEGACY_2X_DISP_ROWS PBL_DISPLAY_HEIGHT -// tintin won't get 4x but set legacy3x values anyways to keep it building -#define LEGACY_3X_DISP_COLS PBL_DISPLAY_WIDTH -#define LEGACY_3X_DISP_ROWS PBL_DISPLAY_HEIGHT - -#define DISPLAY_FRAMEBUFFER_BYTES \ - (ROUND_TO_MOD_CEIL(PBL_DISPLAY_WIDTH, 32) / 8 * PBL_DISPLAY_HEIGHT) diff --git a/src/fw/board/splash.h b/src/fw/board/splash.h new file mode 100644 index 0000000000..2cc3be9cf3 --- /dev/null +++ b/src/fw/board/splash.h @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#if defined(CONFIG_BOARD_OBELIX_DVT) || defined(CONFIG_BOARD_OBELIX_PVT) || defined(CONFIG_BOARD_OBELIX_BB2) +#include "splash/splash_obelix.xbm" +#elif defined(CONFIG_BOARD_GETAFIX_EVT) || defined(CONFIG_BOARD_GETAFIX_DVT) || defined(CONFIG_BOARD_GETAFIX_DVT2) +#include "splash/splash_getafix.xbm" +#elif defined(CONFIG_BOARD_QEMU_EMERY) +#include "splash/splash_obelix.xbm" +#elif defined(CONFIG_BOARD_QEMU_FLINT) +#include "splash/splash_obelix.xbm" +#elif defined(CONFIG_BOARD_QEMU_GABBRO) +#include "splash/splash_obelix.xbm" +#else +#error "Unknown splash definition for board" +#endif // BOARD_* diff --git a/src/fw/board/splash/splash_getafix.xbm b/src/fw/board/splash/splash_getafix.xbm new file mode 100644 index 0000000000..e949f386da --- /dev/null +++ b/src/fw/board/splash/splash_getafix.xbm @@ -0,0 +1,37 @@ +#define splash_width 112 +#define splash_height 29 +static const unsigned char splash_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0xfc, 0xf1, + 0x07, 0x3e, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, + 0xfc, 0xe0, 0x07, 0x7f, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, + 0x00, 0x00, 0x0c, 0x00, 0x86, 0x63, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, + 0x00, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x86, 0x01, 0xf0, 0x01, 0xf8, 0xc0, + 0x7c, 0x60, 0x3e, 0x30, 0xe0, 0x03, 0x0c, 0x00, 0x86, 0x01, 0xfc, 0x07, + 0xfe, 0xc3, 0xff, 0xe1, 0xff, 0x30, 0xf8, 0x0f, 0x4e, 0x10, 0x8e, 0x03, + 0x0e, 0x0e, 0x07, 0xc7, 0x83, 0xe3, 0xc1, 0x31, 0x1c, 0x1c, 0x46, 0x10, + 0x0c, 0x0f, 0x06, 0x0c, 0x03, 0xc6, 0x01, 0xe3, 0x80, 0x31, 0x0c, 0x18, + 0x43, 0x10, 0x18, 0x3e, 0x03, 0x98, 0x81, 0xcf, 0x00, 0x66, 0x00, 0x33, + 0x06, 0xbe, 0x03, 0x00, 0x38, 0x78, 0x03, 0x98, 0xf9, 0xcf, 0x00, 0x66, + 0x00, 0x33, 0xe6, 0x3f, 0x43, 0x10, 0x18, 0x60, 0x03, 0x98, 0xff, 0xc0, + 0x00, 0x66, 0x00, 0x33, 0xfe, 0x03, 0x86, 0x0f, 0x0c, 0xc0, 0x03, 0x98, + 0x0f, 0xc0, 0x00, 0x66, 0x00, 0x33, 0x3e, 0x00, 0x0e, 0x00, 0x0e, 0xc0, + 0x03, 0x98, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x06, 0x30, 0x0c, 0x00, + 0xc6, 0xc0, 0x07, 0x0c, 0x03, 0x86, 0x01, 0xc3, 0x80, 0x31, 0x0c, 0x18, + 0x0c, 0x00, 0xc6, 0xc0, 0x0f, 0x0e, 0x8f, 0x87, 0x83, 0xc3, 0xc1, 0x31, + 0x1c, 0x1c, 0x0c, 0x00, 0x86, 0x61, 0xff, 0x07, 0xfe, 0x03, 0xff, 0x81, + 0xff, 0x70, 0xf8, 0x0f, 0xfc, 0xe0, 0x87, 0x7f, 0xf3, 0x01, 0xf8, 0x00, + 0x7c, 0x00, 0x3e, 0x60, 0xe0, 0x03, 0xfc, 0xf1, 0x07, 0x1e, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, + 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x04, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; diff --git a/src/fw/board/splash/splash_obelix.xbm b/src/fw/board/splash/splash_obelix.xbm new file mode 100644 index 0000000000..e949f386da --- /dev/null +++ b/src/fw/board/splash/splash_obelix.xbm @@ -0,0 +1,37 @@ +#define splash_width 112 +#define splash_height 29 +static const unsigned char splash_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, 0xfc, 0xf1, + 0x07, 0x3e, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x00, + 0xfc, 0xe0, 0x07, 0x7f, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, + 0x00, 0x00, 0x0c, 0x00, 0x86, 0x63, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x60, + 0x00, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x86, 0x01, 0xf0, 0x01, 0xf8, 0xc0, + 0x7c, 0x60, 0x3e, 0x30, 0xe0, 0x03, 0x0c, 0x00, 0x86, 0x01, 0xfc, 0x07, + 0xfe, 0xc3, 0xff, 0xe1, 0xff, 0x30, 0xf8, 0x0f, 0x4e, 0x10, 0x8e, 0x03, + 0x0e, 0x0e, 0x07, 0xc7, 0x83, 0xe3, 0xc1, 0x31, 0x1c, 0x1c, 0x46, 0x10, + 0x0c, 0x0f, 0x06, 0x0c, 0x03, 0xc6, 0x01, 0xe3, 0x80, 0x31, 0x0c, 0x18, + 0x43, 0x10, 0x18, 0x3e, 0x03, 0x98, 0x81, 0xcf, 0x00, 0x66, 0x00, 0x33, + 0x06, 0xbe, 0x03, 0x00, 0x38, 0x78, 0x03, 0x98, 0xf9, 0xcf, 0x00, 0x66, + 0x00, 0x33, 0xe6, 0x3f, 0x43, 0x10, 0x18, 0x60, 0x03, 0x98, 0xff, 0xc0, + 0x00, 0x66, 0x00, 0x33, 0xfe, 0x03, 0x86, 0x0f, 0x0c, 0xc0, 0x03, 0x98, + 0x0f, 0xc0, 0x00, 0x66, 0x00, 0x33, 0x3e, 0x00, 0x0e, 0x00, 0x0e, 0xc0, + 0x03, 0x98, 0x01, 0xcc, 0x00, 0x66, 0x00, 0x33, 0x06, 0x30, 0x0c, 0x00, + 0xc6, 0xc0, 0x07, 0x0c, 0x03, 0x86, 0x01, 0xc3, 0x80, 0x31, 0x0c, 0x18, + 0x0c, 0x00, 0xc6, 0xc0, 0x0f, 0x0e, 0x8f, 0x87, 0x83, 0xc3, 0xc1, 0x31, + 0x1c, 0x1c, 0x0c, 0x00, 0x86, 0x61, 0xff, 0x07, 0xfe, 0x03, 0xff, 0x81, + 0xff, 0x70, 0xf8, 0x0f, 0xfc, 0xe0, 0x87, 0x7f, 0xf3, 0x01, 0xf8, 0x00, + 0x7c, 0x00, 0x3e, 0x60, 0xe0, 0x03, 0xfc, 0xf1, 0x07, 0x1e, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, + 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x04, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; diff --git a/src/fw/board/wscript_build b/src/fw/board/wscript_build index b5216bc209..5798e89c7f 100644 --- a/src/fw/board/wscript_build +++ b/src/fw/board/wscript_build @@ -1,97 +1,56 @@ -board = bld.get_board() -if board in ('bb2',): +if bld.env.CONFIG_BOARD_FAMILY_ASTERIX: bld.objects( name='board', source=[ - 'boards/board_bb2.c', - ], - use=[ - 'fw_includes', - 'drivers', - ], - ) -elif board in ('ev2_4',): - bld.objects( - name='board', - source=[ - 'boards/board_ev2_4.c', - ], - use=[ - 'fw_includes', - 'drivers', - ], - ) -elif board in ('v1_5',): - bld.objects( - name='board', - source=[ - 'boards/board_v1_5.c', - ], - use=[ - 'fw_includes', - 'drivers', - ], - ) -elif board in ('v2_0',): - bld.objects( - name='board', - source=[ - 'boards/board_v2_0.c', - ], - use=[ - 'fw_includes', - 'drivers', - ], - ) -elif board in ('snowy_bb2','snowy_evt2','snowy_dvt','snowy_s3'): - bld.objects( - name='board', - source=[ - 'boards/board_snowy.c', + 'boards/board_asterix.c', ], use=[ 'fw_includes', + 'freertos', 'drivers', ], ) -elif board in ('spalding_evt','spalding', 'spalding_bb2'): +elif bld.env.CONFIG_BOARD_FAMILY_OBELIX: bld.objects( name='board', source=[ - 'boards/board_spalding_evt.c', + 'boards/board_obelix.c', ], use=[ 'fw_includes', + 'freertos', 'drivers', ], ) -elif board in ('silk_evt', 'silk_bb', 'silk_bb2', 'silk',): +elif bld.env.CONFIG_BOARD_FAMILY_GETAFIX: bld.objects( name='board', source=[ - 'boards/board_silk.c', + 'boards/board_getafix.c', ], use=[ 'fw_includes', + 'freertos', 'drivers', ], ) -elif board in ('cutts_bb','robert_bb','robert_bb2','robert_evt'): +elif bld.env.CONFIG_BOARD_QEMU_EMERY: bld.objects( name='board', source=[ - 'boards/board_robert.c', + 'boards/board_qemu_emery.c', ], use=[ 'fw_includes', + 'freertos', 'drivers', ], ) -elif board in ('asterix'): +elif bld.env.CONFIG_BOARD_QEMU_FLINT: bld.objects( name='board', source=[ - 'boards/board_asterix.c', + 'boards/board_qemu_flint.c', ], use=[ 'fw_includes', @@ -99,11 +58,11 @@ elif board in ('asterix'): 'drivers', ], ) -elif board in ('obelix', 'obelix_bb'): +elif bld.env.CONFIG_BOARD_QEMU_GABBRO: bld.objects( name='board', source=[ - 'boards/board_obelix.c', + 'boards/board_qemu_gabbro.c', ], use=[ 'fw_includes', @@ -112,7 +71,6 @@ elif board in ('obelix', 'obelix_bb'): ], ) else: - bld.fatal('src/fw/board/wscript_build: ' - 'Unknown board {}'.format(board)) + bld.fatal('src/fw/board/wscript_build: Unknown board {}'.format(bld.env.BOARD)) # vim:filetype=python diff --git a/src/fw/comm/ble/ble_log.h b/src/fw/comm/ble/ble_log.h deleted file mode 100644 index aa9dd809c2..0000000000 --- a/src/fw/comm/ble/ble_log.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define FILE_LOG_COLOR LOG_COLOR_BLUE -#include "system/logging.h" -#include "system/passert.h" -#include "system/hexdump.h" - -#define BLE_LOG_DEBUG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_BT, LOG_LEVEL_DEBUG, fmt, ## args) -#define BLE_LOG_VERBOSE(fmt, args...) PBL_LOG_D(LOG_DOMAIN_BT, LOG_LEVEL_DEBUG_VERBOSE, fmt, ## args) -#define BLE_HEXDUMP(data, length) PBL_HEXDUMP_D(LOG_DOMAIN_BT, LOG_LEVEL_DEBUG, data, length) -#define BLE_HEXDUMP_VERBOSE(data, length) PBL_HEXDUMP_D(LOG_DOMAIN_BT, LOG_LEVEL_DEBUG_VERBOSE, data, length) diff --git a/src/fw/comm/ble/gap_le.c b/src/fw/comm/ble/gap_le.c index 1a9d947fdd..a2fedd8b72 100644 --- a/src/fw/comm/ble/gap_le.c +++ b/src/fw/comm/ble/gap_le.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le.h" diff --git a/src/fw/comm/ble/gap_le.h b/src/fw/comm/ble/gap_le.h index f44d8139c9..d69beaee31 100644 --- a/src/fw/comm/ble/gap_le.h +++ b/src/fw/comm/ble/gap_le.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gap_le_advert.c b/src/fw/comm/ble/gap_le_advert.c index bfc1e23f55..26e2e68359 100644 --- a/src/fw/comm/ble/gap_le_advert.c +++ b/src/fw/comm/ble/gap_le_advert.c @@ -1,32 +1,22 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_advert.h" #include "gap_le_connect.h" #include +#include -#include "comm/ble/ble_log.h" #include "comm/bt_lock.h" #include "kernel/pbl_malloc.h" -#include "services/common/regular_timer.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/regular_timer.h" #include "system/logging.h" #include "system/passert.h" #include "util/list.h" +PBL_LOG_MODULE_DECLARE(bt, CONFIG_BT_LOG_LEVEL); + //! CC2564 / HCI Advertising Limitation: //! ------------------------------ //! The Bluetooth chip can accept only one advertising payload, one @@ -57,7 +47,12 @@ //! - ble_discoverability/pairability.c //! - Use private addresses for privacy / harder tracebility. -#define GAP_LE_ADVERT_LOG_LEVEL LOG_LEVEL_DEBUG +// Advertising interval parameters in ms, indexed by GAPLEAdvertisingInterval. +// Values comply with Apple Accessory Design Guidelines. +static const uint32_t s_interval_ms[] = { + [GAPLEAdvertisingInterval_Short] = 20, // 20ms + [GAPLEAdvertisingInterval_Long] = 1022, // 1022.5ms (truncated to ms) +}; typedef struct GAPLEAdvertisingJob { ListNode node; @@ -114,6 +109,25 @@ static int8_t s_tx_power_cached = 12; static void prv_perform_next_job(bool force_refresh); +// ----------------------------------------------------------------------------- +//! Analytics helpers for tracking advertising interval time. + +static void prv_analytics_stop_timers(void) { + PBL_ANALYTICS_TIMER_STOP(ble_adv_short_intvl_time_ms); + PBL_ANALYTICS_TIMER_STOP(ble_adv_long_intvl_time_ms); +} + +static void prv_analytics_start_timer(GAPLEAdvertisingInterval interval) { + switch (interval) { + case GAPLEAdvertisingInterval_Short: + PBL_ANALYTICS_TIMER_START(ble_adv_short_intvl_time_ms); + break; + case GAPLEAdvertisingInterval_Long: + PBL_ANALYTICS_TIMER_START(ble_adv_long_intvl_time_ms); + break; + } +} + // ----------------------------------------------------------------------------- static const char * prv_string_for_debug_tag(GAPLEAdvertisingJobTag tag) { @@ -191,14 +205,14 @@ static void prv_increment_elapsed_time_for_job(GAPLEAdvertisingJob **job_ptr, bo if (job->cur_term < job->num_terms) { // Take care of GAPLE_ADVERTISING_DURATION_LOOP_AROUND: if (job->terms[job->cur_term].duration_secs == GAPLE_ADVERTISING_DURATION_LOOP_AROUND) { - BLE_LOG_DEBUG("Job looped around to term %"PRIu16, - job->terms[job->cur_term].loop_around_index); + PBL_LOG_DBG("Job looped around to term %"PRIu16, + job->terms[job->cur_term].loop_around_index); job->cur_term = job->terms[job->cur_term].loop_around_index; } job->term_time_elapsed_secs = 0; - BLE_LOG_DEBUG("Job is performing next advertising term (%d/%d)", - job->cur_term, job->num_terms); + PBL_LOG_DBG("Job is performing next advertising term (%d/%d)", + job->cur_term, job->num_terms); // force an update to make sure the new requested term takes if (has_new_term) { *has_new_term = true; @@ -214,8 +228,8 @@ static void prv_increment_elapsed_time_for_job(GAPLEAdvertisingJob **job_ptr, bo job->unscheduled_callback(job, true /* completed */, job->unscheduled_callback_data); } - BLE_LOG_DEBUG("Unscheduled advertising completed job: %s", - prv_string_for_debug_tag(job->tag)); + PBL_LOG_DBG("Unscheduled advertising completed job: %s", + prv_string_for_debug_tag(job->tag)); kernel_free(job->terms); kernel_free(job); *job_ptr = NULL; @@ -263,7 +277,7 @@ static void prv_cycle_timer_callback(void *unused) { //! bt_lock is expected to be taken! static void prv_timer_start(void) { if (regular_timer_is_scheduled(&s_cycle_regular_timer)) { - PBL_LOG(LOG_LEVEL_ERROR, "Advertising timer already started"); + PBL_LOG_ERR("Advertising timer already started"); regular_timer_remove_callback(&s_cycle_regular_timer); } @@ -308,8 +322,9 @@ static void prv_perform_next_job(bool force_refresh) { if (s_is_advertising) { // Controller needs to stop advertising before we can start a new job: - PBL_LOG(GAP_LE_ADVERT_LOG_LEVEL, "Disable last Ad job"); + PBL_LOG_DBG("Disable last Ad job"); bt_driver_advert_advertising_disable(); + prv_analytics_stop_timers(); s_is_advertising = false; } } @@ -324,20 +339,22 @@ static void prv_perform_next_job(bool force_refresh) { if (s_current_ad_data != &next->payload) { // Give the advertisement data to the BT controller: - bt_driver_advert_set_advertising_data(&next->payload); - s_current_ad_data = &next->payload; + bool result = bt_driver_advert_set_advertising_data(&next->payload); + if (result) { + s_current_ad_data = &next->payload; + } } - // One slot is 625us: - const uint32_t min_interval_ms = ((next->terms[next->cur_term].min_interval_slots * 5) / 8); - const uint32_t max_interval_ms = ((next->terms[next->cur_term].max_interval_slots * 5) / 8); + const GAPLEAdvertisingInterval interval = next->terms[next->cur_term].interval; + const uint32_t min_interval_ms = s_interval_ms[interval]; + const uint32_t max_interval_ms = s_interval_ms[interval]; - BLE_LOG_DEBUG("Enable Ad job %s", prv_string_for_debug_tag(next->tag)); + PBL_LOG_DBG("Enable Ad job %s", prv_string_for_debug_tag(next->tag)); bool result = bt_driver_advert_advertising_enable(min_interval_ms, max_interval_ms); if (result) { s_is_advertising = true; - PBL_LOG(GAP_LE_ADVERT_LOG_LEVEL, "Airing advertising job: %s ", - prv_string_for_debug_tag(next->tag)); + prv_analytics_start_timer(interval); + PBL_LOG_DBG("Airing advertising job: %s ", prv_string_for_debug_tag(next->tag)); } } @@ -368,7 +385,7 @@ GAPLEAdvertisingJobRef gap_le_advert_schedule(const BLEAdData *payload, const bool is_loop_around = (terms[i].duration_secs == GAPLE_ADVERTISING_DURATION_LOOP_AROUND); if (is_loop_around) { if (i == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Loop-around term cannot be the first term"); + PBL_LOG_ERR("Loop-around term cannot be the first term"); return NULL; } continue; @@ -399,7 +416,7 @@ GAPLEAdvertisingJobRef gap_le_advert_schedule(const BLEAdData *payload, memcpy(job->payload.data, payload->data, payload->ad_data_length + payload->scan_resp_data_length); - PBL_LOG(LOG_LEVEL_INFO, "Scheduling advertising job: %s", + PBL_LOG_INFO("Scheduling advertising job: %s", prv_string_for_debug_tag(job->tag)); // Schedule @@ -436,7 +453,7 @@ void gap_le_advert_unschedule(GAPLEAdvertisingJobRef job) { is_registered = prv_is_registered_job(job); if (is_registered) { - PBL_LOG(LOG_LEVEL_INFO, "Unscheduling advertising job: %s", + PBL_LOG_INFO("Unscheduling advertising job: %s", prv_string_for_debug_tag(job->tag)); prv_unlink_job(job); @@ -483,8 +500,8 @@ void gap_le_advert_unschedule_job_types( for (size_t i = 0; i < num_types; i++) { if (job->tag == tag_types[i]) { - BLE_LOG_DEBUG("Removing advertisement of type %s", - prv_string_for_debug_tag(job->tag)); + PBL_LOG_DBG("Removing advertisement of type %s", + prv_string_for_debug_tag(job->tag)); gap_le_advert_unschedule(job); } } @@ -519,7 +536,7 @@ void gap_le_advert_init(void) { bt_lock(); { if (s_gap_le_advert_is_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "gap le advert has already been initialized"); + PBL_LOG_ERR("gap le advert has already been initialized"); goto unlock; } @@ -570,6 +587,7 @@ void gap_le_advert_handle_connect_as_slave(void) { // handler (kernel_le_client.c) will unschedule jobs accordingly and we // want to avoid unnecessary refreshes of the advertising state s_is_advertising = false; + prv_analytics_stop_timers(); s_is_connected = true; } @@ -595,16 +613,26 @@ void gap_le_advert_handle_disconnect_as_slave(void) { bt_unlock(); } -GAPLEAdvertisingJobRef gap_le_advert_get_current_job(void) { - return s_current; -} +// ----------------------------------------------------------------------------- +void bt_driver_handle_host_resynced(void) { + bt_lock(); + { + if (!s_gap_le_advert_is_initialized) { + goto unlock; + } -GAPLEAdvertisingJobRef gap_le_advert_get_jobs(void) { - return s_jobs; -} + // The controller's advertising state was wiped by the host re-sync, so any + // cached pointer to ad data we already pushed is stale and any prior + // adv-enable failed mid-flight. + s_current_ad_data = NULL; + s_is_advertising = false; -GAPLEAdvertisingJobTag gap_le_advert_get_job_tag(GAPLEAdvertisingJobRef job) { - return job->tag; + if (s_current && !s_is_connected) { + prv_perform_next_job(true /* force refresh */); + } + } +unlock: + bt_unlock(); } #undef GAP_LE_ADVERT_LOG_LEVEL diff --git a/src/fw/comm/ble/gap_le_advert.h b/src/fw/comm/ble/gap_le_advert.h index cf3ab46c5f..4612f028b2 100644 --- a/src/fw/comm/ble/gap_le_advert.h +++ b/src/fw/comm/ble/gap_le_advert.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,6 +10,14 @@ typedef enum { GAPLEAdvertisingJobTagReconnection, } GAPLEAdvertisingJobTag; +//! Advertising interval preset, compliant with Apple Accessory Design Guidelines. +typedef enum { + //! 20ms interval (Apple ADG fast advertising) + GAPLEAdvertisingInterval_Short, + //! 1022.5ms interval (Apple ADG slow advertising) + GAPLEAdvertisingInterval_Long, +} GAPLEAdvertisingInterval; + struct GAPLEAdvertisingJob; //! Opaque reference to an advertising job. @@ -37,13 +32,8 @@ typedef struct GAPLEAdvertisingJobTerm { uint16_t duration_secs; union { - struct { - //! Advertising interval range in slots: - //! @note Use GAPLE_ADVERTISING_INFINITE_INTERVAL_SLOTS to indicate - //! the term should be "silent". - uint16_t min_interval_slots; - uint16_t max_interval_slots; - }; + //! Advertising interval preset. + GAPLEAdvertisingInterval interval; //! The index to loop back to. //! @note only valid when duration_secs is GAPLE_ADVERTISING_DURATION_LOOP_AROUND. uint16_t loop_around_index; @@ -85,13 +75,10 @@ typedef void (*GAPLEAdvertisingJobUnscheduleCallback)(GAPLEAdvertisingJobRef job //! @param payload The payload with the advertising and scan response data to //! be scheduled for air-time. @see ble_ad_parse.h for functions to build the //! payload. -//! @param terms A combination of minimum advertisement interval, maximum advertisement -//! interval and duration. Each term is run in the order that they appear in the terms array. -//! The minimum advertisement interval for each term must be at minumum 32 slots (20ms), or -//! 160 slots (100ms) when there is a scan response. The maximum advertisement interval must -//! be larger than or equal to its corresponding min_interval_slots. The duration is the -//! minimum number of seconds that the term will be active. The sum of all the durations is -//! the minimum number of seconds that the advertisement payload has to be on-air. +//! @param terms A combination of advertisement interval preset and duration. Each term is run +//! in the order that they appear in the terms array. The duration is the minimum number of +//! seconds that the term will be active. The sum of all the durations is the minimum number +//! of seconds that the advertisement payload has to be on-air. //! The job is not guaranteed to get a consecutive period of air-time nor is it guaranteed that //! it will get air-time immediately after returning from this function. //! @param callback Pointer to a function that should be called when the job diff --git a/src/fw/comm/ble/gap_le_connect.c b/src/fw/comm/ble/gap_le_connect.c index a22eccded9..f395aa2065 100644 --- a/src/fw/comm/ble/gap_le_connect.c +++ b/src/fw/comm/ble/gap_le_connect.c @@ -1,22 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_connect.h" -#include "ble_log.h" #include "comm/bluetooth_analytics.h" #include "comm/bt_conn_mgr.h" #include "comm/bt_lock.h" @@ -26,8 +12,8 @@ #include "gap_le_task.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/normal/bluetooth/ble_hrm.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/ble_hrm.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/passert.h" @@ -37,6 +23,8 @@ #include #include +PBL_LOG_MODULE_DECLARE(bt, CONFIG_BT_LOG_LEVEL); + #if BLE_MASTER_CONNECT_SUPPORT // FIXME: Shouldn't be needed after PBL-32761 extern unsigned int bt_stack_id(void); #endif @@ -316,15 +304,13 @@ void bt_driver_handle_le_connection_handle_update_address(const BleAddressChange { GAPLEConnection *connection = gap_le_connection_by_device(&e->device); if (!connection) { - PBL_LOG(LOG_LEVEL_ERROR, - "Got address update for non-existent connection. " + PBL_LOG_ERR("Got address update for non-existent connection. " "Old addr:"BT_DEVICE_ADDRESS_FMT, BT_DEVICE_ADDRESS_XPLODE(e->device.address)); goto unlock; } connection->device = e->new_device; - PBL_LOG(LOG_LEVEL_INFO, - "Updated address to "BT_DEVICE_ADDRESS_FMT, + PBL_LOG_INFO("Updated address to "BT_DEVICE_ADDRESS_FMT, BT_DEVICE_ADDRESS_XPLODE(connection->device.address)); } unlock: @@ -336,12 +322,12 @@ void bt_driver_handle_le_connection_handle_update_irk(const BleIRKChange *e) { { GAPLEConnection *connection = gap_le_connection_by_device(&e->device); if (!connection) { - PBL_LOG(LOG_LEVEL_ERROR, "Got IRK update for non-existent connection"); + PBL_LOG_ERR("Got IRK update for non-existent connection"); goto unlock; } if (connection->irk) { - PBL_LOG(LOG_LEVEL_WARNING, "Connection already has IRK!?"); + PBL_LOG_WRN("Connection already has IRK!?"); } gap_le_connection_set_irk(connection, e->irk_valid ? &e->irk : NULL); @@ -357,7 +343,7 @@ void bt_driver_handle_peer_version_info_event(const BleRemoteVersionInfoReceived if (connection) { const BleRemoteVersionInfo *info = &e->remote_version_info; connection->remote_version_info = *info; - PBL_LOG(LOG_LEVEL_DEBUG, "Remote Vers Info: VersNr: %d, CompId: 0x%x, SubVersNr: 0x%x", + PBL_LOG_DBG("Remote Vers Info: VersNr: %d, CompId: 0x%x, SubVersNr: 0x%x", (int)info->version_number, (int)info->company_identifier, (int)info->subversion_number); } @@ -366,14 +352,24 @@ void bt_driver_handle_peer_version_info_event(const BleRemoteVersionInfoReceived //! bt_lock is assumed to be taken before calling this function. void bt_driver_handle_le_connection_complete_event(const BleConnectionCompleteEvent *event) { + // Create timer outside of bt_lock to avoid deadlock with NimbleHost. + // new_timer_create() acquires TaskTimerManager mutex, which may be held by NimbleHost + // when it's trying to acquire bt_lock, leading to a lock ordering deadlock. + TimerID param_watchdog_timer = TIMER_INVALID_ID; + if (event->status == HciStatusCode_Success) { + param_watchdog_timer = new_timer_create(); + if (!param_watchdog_timer) { + PBL_LOG_ERR("Failed to create timer for connection params"); + return; + } + } + bt_lock(); const BleConnectionParams *params = &event->conn_params; - PBL_LOG(LOG_LEVEL_INFO, - "LE Conn Compl: addr="BT_DEVICE_ADDRESS_FMT", is_random_addr=%u,", + PBL_LOG_INFO("LE Conn Compl: addr="BT_DEVICE_ADDRESS_FMT", is_random_addr=%u,", BT_DEVICE_ADDRESS_XPLODE(event->peer_address.address), event->peer_address.is_random_address); - PBL_LOG(LOG_LEVEL_INFO, - " hdl=%u, status=0x%02x, master=%u, %u, slave lat=%u, " + PBL_LOG_INFO(" hdl=%u, status=0x%02x, master=%u, %u, slave lat=%u, " "supervision timeout=%u, is_resolved=%c", event->handle, event->status, event->is_master, params->conn_interval_1_25ms, params->slave_latency_events, params->supervision_timeout_10ms, @@ -400,15 +396,16 @@ void bt_driver_handle_le_connection_complete_event(const BleConnectionCompleteEv // but the watch has not yet. In practice, I think the only way it could happen is if a // user is sitting in the Bluetooth settings menu and walking in and out of range. If it // does take place, let's trigger a disconnect to try and put us back into a sane state - PBL_LOG(LOG_LEVEL_ERROR, - "Not adding connection for device. It is already connected .. disconnecting"); + PBL_LOG_ERR("Not adding connection for device. It is already connected .. disconnecting"); bt_driver_gap_le_disconnect(&event->peer_address); + param_watchdog_timer = TIMER_INVALID_ID; // Don't use timer, will clean up below break; } const SMIdentityResolvingKey *remote_irk = event->is_resolved ? &event->irk : NULL; GAPLEConnection *connection = gap_le_connection_add(&event->peer_address, remote_irk, - local_is_master); + local_is_master, param_watchdog_timer); + param_watchdog_timer = TIMER_INVALID_ID; // Timer now owned by connection // Cache the BLE connection parameters connection->conn_params = *params; connection->gatt_mtu = event->mtu; @@ -454,11 +451,11 @@ void bt_driver_handle_le_connection_complete_event(const BleConnectionCompleteEv // There is no connection intent from our end. This could be the phone that is connecting // for the first time. Let the connection watchdog (TODO: PBL-11236) take care of // disconnecting at some point, if the connection ends up being unused. - PBL_LOG(LOG_LEVEL_INFO, "No intent for connection"); + PBL_LOG_INFO("No intent for connection"); bluetooth_analytics_handle_no_intent_for_connection(); } -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW // In PRF, stick to shortest connection interval indefinitely: conn_mgr_set_ble_conn_response_time(connection, BtConsumerPRF, ResponseTimeMin, MAX_PERIOD_RUN_FOREVER); @@ -473,7 +470,7 @@ void bt_driver_handle_le_connection_complete_event(const BleConnectionCompleteEv } default: { - PBL_LOG(LOG_LEVEL_ERROR, "Connection Complete Event status: 0x%x", + PBL_LOG_ERR("Connection Complete Event status: 0x%x", event->status); break; } @@ -482,6 +479,11 @@ void bt_driver_handle_le_connection_complete_event(const BleConnectionCompleteEv // Continue initiating connections to disconnected devices: prv_start_connecting_if_needed(); bt_unlock(); + + // Clean up timer if we didn't use it (e.g., connection failed or already connected) + if (param_watchdog_timer != TIMER_INVALID_ID) { + new_timer_delete(param_watchdog_timer); + } } //! bt_lock is assumed to be taken before calling this function. @@ -492,21 +494,21 @@ void bt_driver_handle_le_disconnection_complete_event(const BleDisconnectionComp case HciStatusCode_Success: { // Disconnection! Update our records: GAPLEConnection *connection = gap_le_connection_by_device(&event->peer_address); -#if CAPABILITY_HAS_BUILTIN_HRM +#if defined(CONFIG_HRM) && !defined(CONFIG_RECOVERY_FW) ble_hrm_handle_disconnection(connection); #endif const bool local_is_master = connection->local_is_master; - PBL_LOG(LOG_LEVEL_INFO, "LE Disconn: addr="BT_DEVICE_ADDRESS_FMT", is_random_addr=%u,", + PBL_LOG_INFO("LE Disconn: addr="BT_DEVICE_ADDRESS_FMT", is_random_addr=%u,", BT_DEVICE_ADDRESS_XPLODE(event->peer_address.address), event->peer_address.is_random_address); - PBL_LOG(LOG_LEVEL_INFO, " hdl=%u, status=0x%02x, reason=0x%02x, master=%u", + PBL_LOG_INFO(" hdl=%u, status=0x%02x, reason=0x%02x, master=%u", event->handle, event->status, event->reason, local_is_master); bluetooth_analytics_handle_disconnect(local_is_master); bluetooth_analytics_handle_connection_disconnection_event( - AnalyticsEvent_BtLeDisconnect, event->reason, &connection->remote_version_info); + event->reason, &connection->remote_version_info); if (!local_is_master) { s_is_connected_as_slave = false; @@ -543,7 +545,7 @@ void bt_driver_handle_le_disconnection_complete_event(const BleDisconnectionComp } default: { - PBL_LOG(LOG_LEVEL_ERROR, "Disconnection Complete Event status: 0x%x", + PBL_LOG_ERR("Disconnection Complete Event status: 0x%x", event->status); break; } @@ -581,7 +583,7 @@ void bt_driver_handle_le_encryption_change_event(const BleEncryptionChange *even if (!is_encrypted) { // The "Encryption Change" event can only enable encryption, there's no inverse, // so there must be an error: - PBL_LOG(LOG_LEVEL_ERROR, "LE encryption change: failed (%u)", event->status); + PBL_LOG_ERR("LE encryption change: failed (%u)", event->status); goto unlock; } @@ -589,7 +591,7 @@ void bt_driver_handle_le_encryption_change_event(const BleEncryptionChange *even // gap_le_connection_by_device() will fail. GAPLEConnection *connection = gap_le_connection_by_addr(&event->dev_address); if (connection->is_encrypted) { - PBL_LOG(LOG_LEVEL_INFO, "LE encryption change: refreshed"); + PBL_LOG_INFO("LE encryption change: refreshed"); goto unlock; } @@ -597,7 +599,7 @@ void bt_driver_handle_le_encryption_change_event(const BleEncryptionChange *even connection->is_encrypted = true; if (!local_is_master) { - PBL_LOG(LOG_LEVEL_INFO, "LE encryption change: encrypted"); + PBL_LOG_INFO("LE encryption change: encrypted"); bluetooth_analytics_handle_encryption_change(); bt_driver_pebble_pairing_service_handle_status_change(connection); } @@ -614,14 +616,14 @@ void bt_driver_handle_le_encryption_change_event(const BleEncryptionChange *even static void prv_start_connecting(void) { #if !BLE_MASTER_CONNECT_SUPPORT // PBL-32761 - PBL_LOG(LOG_LEVEL_WARNING, "Watch driven BLE connection unimplemented"); + PBL_LOG_WRN("Watch driven BLE connection unimplemented"); #else if (s_has_pending_create_connection) { - PBL_LOG(LOG_LEVEL_ERROR, "Already connecting..."); + PBL_LOG_ERR("Already connecting..."); return; } - BLE_LOG_DEBUG("Starting connecting.."); + PBL_LOG_DBG("Starting connecting.."); unsigned int stack_id = bt_stack_id(); // See Bluetooth Spec 4.0, Volume 2, Part E, Chapter 7.8.12: const GAP_LE_Address_Type_t local_addr_type = BleAddressType_Random; @@ -640,7 +642,7 @@ static void prv_start_connecting(void) { gap_le_connect_bluetopia_connection_callback, 0 /* callback context: unused */); if (r) { - PBL_LOG(LOG_LEVEL_ERROR, "GAP_LE_Create_Connection (r=%d)", r); + PBL_LOG_ERR("GAP_LE_Create_Connection (r=%d)", r); } else { s_has_pending_create_connection = true; } @@ -649,17 +651,17 @@ static void prv_start_connecting(void) { static void prv_stop_connecting(void) { #if !BLE_MASTER_CONNECT_SUPPORT // PBL-32761 - PBL_LOG(LOG_LEVEL_WARNING, "Watch driven BLE connection cancel unimplemented"); + PBL_LOG_WRN("Watch driven BLE connection cancel unimplemented"); #else if (!s_has_pending_create_connection) { return; } unsigned int stack_id = bt_stack_id(); - BLE_LOG_DEBUG("Stopping connecting..."); + PBL_LOG_DBG("Stopping connecting..."); // See Bluetooth Spec 4.0, Volume 2, Part E, Chapter 7.8.13: const int r = GAP_LE_Cancel_Create_Connection(stack_id); if (r) { - PBL_LOG(LOG_LEVEL_ERROR, "GAP_LE_Cancel_Create_Connection (r=%d)", r); + PBL_LOG_ERR("GAP_LE_Cancel_Create_Connection (r=%d)", r); } else { // Update the state right away (don't wait for the Connection Complete event // with HCI_ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER status): @@ -670,11 +672,11 @@ static void prv_stop_connecting(void) { static void prv_mutate_whitelist(const BTDeviceInternal *device, bool is_adding) { #if !BLE_MASTER_CONNECT_SUPPORT // PBL-32761 - PBL_LOG(LOG_LEVEL_WARNING, "BLE whitelist mutation unimplemented"); + PBL_LOG_WRN("BLE whitelist mutation unimplemented"); #else unsigned int stack_id = bt_stack_id(); - BLE_LOG_DEBUG("Mutating white-list (adding=%u): " BD_ADDR_FMT, - is_adding, BT_DEVICE_ADDRESS_XPLODE(device->address)); + PBL_LOG_DBG("Mutating white-list (adding=%u): " BD_ADDR_FMT, + is_adding, BT_DEVICE_ADDRESS_XPLODE(device->address)); // See Bluetooth Spec 4.0, Volume 2, Part E, Chapter 7.8.15: uint8_t status = 0; const uint8_t addr_type = device->is_random_address ? 0x01 : 0x00; @@ -683,8 +685,7 @@ static void prv_mutate_whitelist(const BTDeviceInternal *device, bool is_adding) const int r = mutator(stack_id, addr_type, BTDeviceAddressToBDADDR(device->address), &status); if (r) { - PBL_LOG(LOG_LEVEL_ERROR, - "HCI_LE_..._Device_To_White_List (is_adding=%u, r=%d, status=0x%x)", + PBL_LOG_ERR("HCI_LE_..._Device_To_White_List (is_adding=%u, r=%d, status=0x%x)", is_adding, r, status); } #endif @@ -705,7 +706,7 @@ static bool prv_intent_matches_connection(const GAPLEConnectionIntent *intent, if (!connection->irk) { // are we looking for a bonding which did not exchange an irk? if (sm_is_pairing_info_irk_not_used(&intent->bonding->irk)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Bonding does not have irk ... comparing identity address"); + PBL_LOG_DBG("Bonding does not have irk ... comparing identity address"); return (0 == memcmp(&connection->device.opaque, &intent->bonding->device.opaque, sizeof(connection->device.opaque))); } @@ -855,7 +856,7 @@ static BTErrno prv_register_intent(struct RegisterIntentRequest *request, const GAPLEConnection *connection = gap_le_connection_find_by_irk(&request->bonding.irk); if (!connection) { if (sm_is_pairing_info_irk_not_used(&request->bonding.irk)) { - PBL_LOG(LOG_LEVEL_DEBUG, "register_intent: IRK not used, searching by addr"); + PBL_LOG_DBG("register_intent: IRK not used, searching by addr"); connection = gap_le_connection_by_device(&request->bonding.device); } } @@ -974,7 +975,7 @@ static BTErrno prv_unregister_intent(GAPLEConnectionIntent *intent, // so don't disconnect. const int result = bt_driver_gap_le_disconnect(device); if (result != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Ble disconnect failed: %d", result); + PBL_LOG_ERR("Ble disconnect failed: %d", result); } } else { if (prv_is_intent_using_whitelist(intent)) { @@ -1128,7 +1129,7 @@ BTErrno gap_le_connect_cancel_by_bonding(BTBondingID bonding_id, GAPLEClient cli void gap_le_connect_cancel_all(GAPLEClient client) { bt_lock(); { - BLE_LOG_DEBUG("Cancel connecting all for client %u...", client); + PBL_LOG_DBG("Cancel connecting all for client %u...", client); GAPLEConnectionIntent *intent = s_intents; while (intent) { diff --git a/src/fw/comm/ble/gap_le_connect.h b/src/fw/comm/ble/gap_le_connect.h index dbc30b380b..196f0d58de 100644 --- a/src/fw/comm/ble/gap_le_connect.h +++ b/src/fw/comm/ble/gap_le_connect.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include "gap_le_task.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" #define GAP_LE_CONNECT_MASTER_MAX_CONNECTION_INTENTS (5) diff --git a/src/fw/comm/ble/gap_le_connect_params.c b/src/fw/comm/ble/gap_le_connect_params.c index 69a6df2eac..9c3e7ed61d 100644 --- a/src/fw/comm/ble/gap_le_connect_params.c +++ b/src/fw/comm/ble/gap_le_connect_params.c @@ -1,20 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_connect_params.h" #include "gap_le_connection.h" @@ -26,7 +11,8 @@ #include "comm/bt_lock.h" #include "drivers/rtc.h" #include "kernel/pbl_malloc.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/new_timer/new_timer.h" #include "system/logging.h" #include "util/time/time.h" @@ -84,20 +70,17 @@ //! Try 3 times before giving up. #define MAX_UPDATE_REQUEST_ATTEMPTS (3) -// TODO: Check slave_latency_events values. We currently observe some connection -// drops that *may* be related to the slave latency being "too high". Let's just -// remove slave latency for now, and see if that helps. static const GAPLEConnectRequestParams s_default_connection_params_table[NumResponseTimeState] = { [ResponseTimeMax] = { - .slave_latency_events = 0, - .connection_interval_min_1_25ms = 120, // 150ms - .connection_interval_max_1_25ms = 144, // 180ms + .slave_latency_events = 3, + .connection_interval_min_1_25ms = 24, // 30ms + .connection_interval_max_1_25ms = 36, // 45ms .supervision_timeout_10ms = 600, // 6s }, [ResponseTimeMiddle] = { - .slave_latency_events = 0, - .connection_interval_min_1_25ms = 120, // 150ms - .connection_interval_max_1_25ms = 144, // 180ms + .slave_latency_events = 3, + .connection_interval_min_1_25ms = 24, // 30ms + .connection_interval_max_1_25ms = 36, // 45ms .supervision_timeout_10ms = 600, // 6s }, [ResponseTimeMin] = { @@ -113,6 +96,48 @@ extern void conn_mgr_handle_desired_state_granted(GAPLEConnection *hdl, static void prv_watchdog_timer_callback(void *ctx); +// ----------------------------------------------------------------------------- +//! Analytics helpers for tracking connection interval time. + +static void prv_analytics_stop_conn_interval_timers(void) { + PBL_ANALYTICS_TIMER_STOP(ble_conn_itvl_min_time_ms); + PBL_ANALYTICS_TIMER_STOP(ble_conn_itvl_mid_time_ms); + PBL_ANALYTICS_TIMER_STOP(ble_conn_itvl_max_time_ms); +} + +//! Classify the actual connection interval into a ResponseTimeState based on +//! the default connection params table ranges. +static ResponseTimeState prv_classify_conn_interval(uint16_t conn_interval_1_25ms) { + // Check from fastest (Min) to slowest (Max) so we pick the tightest match + for (int state = ResponseTimeMin; state >= ResponseTimeMax; --state) { + const GAPLEConnectRequestParams *params = &s_default_connection_params_table[state]; + if (conn_interval_1_25ms >= params->connection_interval_min_1_25ms && + conn_interval_1_25ms <= params->connection_interval_max_1_25ms) { + return (ResponseTimeState)state; + } + } + // Outside all known ranges, assume Max (slowest) + return ResponseTimeMax; +} + +static void prv_analytics_update_conn_interval(uint16_t conn_interval_1_25ms) { + prv_analytics_stop_conn_interval_timers(); + + switch (prv_classify_conn_interval(conn_interval_1_25ms)) { + case ResponseTimeMin: + PBL_ANALYTICS_TIMER_START(ble_conn_itvl_min_time_ms); + break; + case ResponseTimeMiddle: + PBL_ANALYTICS_TIMER_START(ble_conn_itvl_mid_time_ms); + break; + case ResponseTimeMax: + PBL_ANALYTICS_TIMER_START(ble_conn_itvl_max_time_ms); + break; + default: + break; + } +} + static const GAPLEConnectRequestParams *prv_params_for_state(const GAPLEConnection *connection, ResponseTimeState state) { if (connection->connection_parameter_sets) { @@ -170,7 +195,7 @@ static void prv_request_params_update(GAPLEConnection *connection, // [MT]: I've hit this once now. When this happened the TI CC2564B became unresponsive. // From the iOS side, it appeared as a connection timeout. A little while after this happened, // the BT chip auto-reset work-around kicked in. - PBL_LOG(LOG_LEVEL_ERROR, "Max attempts reached, giving up. desired_state=%u", state); + PBL_LOG_ERR("Max attempts reached, giving up. desired_state=%u", state); bluetooth_analytics_handle_param_update_failed(); return; } @@ -210,7 +235,7 @@ static void prv_watchdog_timer_callback(void *ctx) { // Retry with most recently requested latency: const ResponseTimeState state = conn_mgr_get_latency_for_le_connection(connection, NULL); if (connection->param_update_info.attempts > 0) { - PBL_LOG(LOG_LEVEL_INFO, "Conn param request timed out: re-requesting %u", state); + PBL_LOG_INFO("Conn param request timed out: re-requesting %u", state); } prv_request_params_update(connection, state); } @@ -225,12 +250,13 @@ void gap_le_connect_params_request(GAPLEConnection *connection, prv_request_params_update(connection, desired_state); } -void gap_le_connect_params_setup_connection(GAPLEConnection *connection) { - connection->param_update_info.watchdog_timer = new_timer_create(); +void gap_le_connect_params_setup_connection(GAPLEConnection *connection, TimerID timer) { + connection->param_update_info.watchdog_timer = timer; } void gap_le_connect_params_cleanup_by_connection(GAPLEConnection *connection) { new_timer_delete(connection->param_update_info.watchdog_timer); + prv_analytics_stop_conn_interval_timers(); } // ------------------------------------------------------------------------------------------------- @@ -256,12 +282,12 @@ static void prv_evaluate(GAPLEConnection *connection, ResponseTimeState desired_ // Connection parameters are updated, but they don't match the desired parameters. // (Re)request a parameter update: - PBL_LOG(LOG_LEVEL_INFO, "Connection parameters do not match desired state: %u", desired_state); + PBL_LOG_INFO("Connection parameters do not match desired state: %u", desired_state); prv_request_params_update(connection, desired_state); } // ------------------------------------------------------------------------------------------------- -//! Extern'd for and used by services/common/bluetooth/pebble_pairing_service.c +//! Extern'd for and used by services/bluetooth/pebble_pairing_service.c //! bt_lock is assumed to be taken before calling this function. //! Forces the module to re-evaluate whether the current parameters match the desired ones. //! This is used when the set of desired request params are changed through Pebble Pairing Service. @@ -285,7 +311,7 @@ void bt_driver_handle_le_conn_params_update_event(const BleConnectionUpdateCompl GAPLEConnection *connection = gap_le_connection_by_addr(&event->dev_address); if (!connection) { - PBL_LOG(LOG_LEVEL_DEBUG, "Receiving conn param update but connection is no longer open"); + PBL_LOG_DBG("Receiving conn param update but connection is no longer open"); goto unlock; } @@ -298,6 +324,7 @@ void bt_driver_handle_le_conn_params_update_event(const BleConnectionUpdateCompl const bool local_is_master = connection->local_is_master; if (!local_is_master) { bluetooth_analytics_handle_connection_params_update(params); + prv_analytics_update_conn_interval(params->conn_interval_1_25ms); } prv_evaluate(connection, desired_state); diff --git a/src/fw/comm/ble/gap_le_connect_params.h b/src/fw/comm/ble/gap_le_connect_params.h index 45824d9d4f..f13ced61d6 100644 --- a/src/fw/comm/ble/gap_le_connect_params.h +++ b/src/fw/comm/ble/gap_le_connect_params.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gap_le_connection.c b/src/fw/comm/ble/gap_le_connection.c index ed97768e76..d4d218e78f 100644 --- a/src/fw/comm/ble/gap_le_connection.c +++ b/src/fw/comm/ble/gap_le_connection.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_connection.h" @@ -21,7 +8,7 @@ #include "kernel/pbl_malloc.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" #include "system/passert.h" #include "system/logging.h" @@ -47,7 +34,7 @@ extern void gatt_client_discovery_cleanup_by_connection(GAPLEConnection *connect extern void gatt_client_cleanup_discovery_jobs(GAPLEConnection *connection); // Defined in gap_le_connect_params.c -extern void gap_le_connect_params_setup_connection(GAPLEConnection *connection); +extern void gap_le_connect_params_setup_connection(GAPLEConnection *connection, TimerID timer); extern void gap_le_connect_params_cleanup_by_connection(GAPLEConnection *connection); // ------------------------------------------------------------------------------------------------- @@ -130,7 +117,8 @@ void gap_le_connection_set_irk(GAPLEConnection *connection, const SMIdentityReso GAPLEConnection *gap_le_connection_add(const BTDeviceInternal *device, const SMIdentityResolvingKey *irk, - bool local_is_master) { + bool local_is_master, + TimerID param_watchdog_timer) { bt_lock_assert_held(true /* is_held */); PBL_ASSERTN(!gap_le_connection_is_connected(device)); @@ -150,7 +138,7 @@ GAPLEConnection *gap_le_connection_add(const BTDeviceInternal *device, s_connections = (GAPLEConnection *) list_prepend(&s_connections->node, &connection->node); - gap_le_connect_params_setup_connection(connection); + gap_le_connect_params_setup_connection(connection, param_watchdog_timer); return connection; } diff --git a/src/fw/comm/ble/gap_le_connection.h b/src/fw/comm/ble/gap_le_connection.h index 81cd958d19..2716c182d0 100644 --- a/src/fw/comm/ble/gap_le_connection.h +++ b/src/fw/comm/ble/gap_le_connection.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -24,7 +11,7 @@ #include "gatt_client_discovery.h" #include "gatt_client_subscriptions.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include #include @@ -32,7 +19,7 @@ // FIXME: Including this header results in a compile time failure because the // chain eventually includes a Bluetopia API. Figure out why this is problematic -// #include "services/common/bluetooth/bluetooth_persistent_storage.h" +// #include "pbl/services/bluetooth/bluetooth_persistent_storage.h" // void gap_le_connection_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op); // ----------------------------------------------------------------------------- @@ -157,7 +144,8 @@ typedef struct GAPLEConnection { GAPLEConnection *gap_le_connection_add(const BTDeviceInternal *device, const SMIdentityResolvingKey *irk, - bool local_is_master); + bool local_is_master, + TimerID param_watchdog_timer); //! Checks to see if the LE connection is in our list of currently tracked //! connections diff --git a/src/fw/comm/ble/gap_le_device_name.c b/src/fw/comm/ble/gap_le_device_name.c index bd855f3bf2..afd4ee7c7a 100644 --- a/src/fw/comm/ble/gap_le_device_name.c +++ b/src/fw/comm/ble/gap_le_device_name.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_device_name.h" #include "bluetooth/gap_le_device_name.h" @@ -20,7 +7,7 @@ #include "comm/bt_lock.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" BTBondingID prv_get_bonding_id_and_name_from_address_safe(void *ctx, char* device_name) { BTBondingID bonding_id = BT_BONDING_ID_INVALID; diff --git a/src/fw/comm/ble/gap_le_device_name.h b/src/fw/comm/ble/gap_le_device_name.h index 3ce7461066..48094ea009 100644 --- a/src/fw/comm/ble/gap_le_device_name.h +++ b/src/fw/comm/ble/gap_le_device_name.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gap_le_scan.c b/src/fw/comm/ble/gap_le_scan.c index 28cf3bbbd2..6b3b8577ee 100644 --- a/src/fw/comm/ble/gap_le_scan.c +++ b/src/fw/comm/ble/gap_le_scan.c @@ -1,20 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bluetooth/gap_le_scan.h" #include "kernel/pbl_malloc.h" @@ -91,7 +76,7 @@ bool gap_le_stop_scan(void) { s_is_scanning = false; if (s_dropped_reports) { - PBL_LOG(LOG_LEVEL_INFO, "LE Scan -- Dropped reports: %" PRIu32, s_dropped_reports); + PBL_LOG_INFO("LE Scan -- Dropped reports: %" PRIu32, s_dropped_reports); } } } diff --git a/src/fw/comm/ble/gap_le_scan.h b/src/fw/comm/ble/gap_le_scan.h index a87c1efac7..4edc88fb08 100644 --- a/src/fw/comm/ble/gap_le_scan.h +++ b/src/fw/comm/ble/gap_le_scan.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gap_le_slave_discovery.c b/src/fw/comm/ble/gap_le_slave_discovery.c index 37aae04341..9c6e40b767 100644 --- a/src/fw/comm/ble/gap_le_slave_discovery.c +++ b/src/fw/comm/ble/gap_le_slave_discovery.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" @@ -21,7 +8,6 @@ #include "applib/bluetooth/ble_ad_parse.h" -#include "comm/ble/ble_log.h" #include "comm/bt_lock.h" #include "git_version.auto.h" @@ -30,8 +16,8 @@ #include "mfg/mfg_serials.h" -#include "services/common/bluetooth/local_id.h" -#include "services/normal/bluetooth/ble_hrm.h" +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/bluetooth/ble_hrm.h" #include "system/passert.h" #include "system/version.h" @@ -67,7 +53,11 @@ static void prv_schedule_ad_job(void) { // central is only doing a scan request if the Service UUID matches with their // interests, to save radio time / battery life we keep the advertisement part // as "small" as possible (21 bytes currently). - ble_ad_set_flags(ad, GAP_LE_AD_FLAGS_GEN_DISCOVERABLE_MASK); + // Advertise "BR/EDR Not Supported" alongside General Discoverable: these are + // BLE-only watches, so dual-mode hosts must connect over LE instead of attempting + // a classic page (which would time out). + ble_ad_set_flags(ad, GAP_LE_AD_FLAGS_GEN_DISCOVERABLE_MASK | + GAP_LE_AD_FLAGS_BR_EDR_NOT_SUPPORTED_MASK); // *DO NOT* use pebble_bt_uuid_expand() here! // ble_ad_set_service_uuids() will be "smart" and include only the 16-bit UUID, but only if the @@ -75,7 +65,7 @@ static void prv_schedule_ad_job(void) { Uuid service_uuids[2]; size_t num_uuids = 0; -#if CAPABILITY_HAS_BUILTIN_HRM +#if defined(CONFIG_HRM) && !defined(CONFIG_RECOVERY_FW) // NOTE: The HRM service has to be first in the list because otherwise the Pebble won't // show up as an HRM device in Strava for Android... if (ble_hrm_is_supported_and_enabled()) { @@ -135,20 +125,17 @@ static void prv_schedule_ad_job(void) { (const uint8_t *) &mfg_data, sizeof(struct ManufacturerSpecificData)); - // Values chosen according to: - // "Accessory Design Guidelies for Apple Devices" 55.4 Advertising Data + // Values chosen according to Apple Accessory Design Guidelines. const GAPLEAdvertisingJobTerm advert_terms[] = { { // Extend this term from recommended 30s to 5min so user has e.g. time // to download or open mobile app. .duration_secs = 5 * 60, - .min_interval_slots = 32, // 20ms - .max_interval_slots = 32, // 20ms + .interval = GAPLEAdvertisingInterval_Short, }, { .duration_secs = GAPLE_ADVERTISING_DURATION_INFINITE, - .min_interval_slots = 1636, // 1022.5ms - .max_interval_slots = 1636, // 1022.5ms + .interval = GAPLEAdvertisingInterval_Long, }, }; diff --git a/src/fw/comm/ble/gap_le_slave_discovery.h b/src/fw/comm/ble/gap_le_slave_discovery.h index d122986cbd..f24eaeddf9 100644 --- a/src/fw/comm/ble/gap_le_slave_discovery.h +++ b/src/fw/comm/ble/gap_le_slave_discovery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gap_le_slave_reconnect.c b/src/fw/comm/ble/gap_le_slave_reconnect.c index d19702bd2f..bae78661a6 100644 --- a/src/fw/comm/ble/gap_le_slave_reconnect.c +++ b/src/fw/comm/ble/gap_le_slave_reconnect.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_slave_reconnect.h" @@ -22,12 +9,12 @@ #include "gap_le_advert.h" #include "gap_le_connect.h" -#include "comm/ble/ble_log.h" +#include "system/logging.h" #include "comm/bt_lock.h" #include "kernel/event_loop.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/regular_timer.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/regular_timer.h" #include "util/size.h" #include @@ -76,16 +63,16 @@ static void prv_unschedule_adv_if_needed(void) { static void prv_evaluate(ReconnectType prev_type) { ReconnectType cur_type = prv_current_reconnect_type(); if (cur_type == prev_type) { - PBL_LOG(LOG_LEVEL_DEBUG, "Reconnect type unchanged: %d", cur_type); + PBL_LOG_DBG("Reconnect type unchanged: %d", cur_type); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Reconnect type changed: %d -> %d", prev_type, cur_type); + PBL_LOG_DBG("Reconnect type changed: %d -> %d", prev_type, cur_type); if (cur_type != ReconnectType_None) { prv_unschedule_adv_if_needed(); -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM const bool use_hrm_payload = (cur_type == ReconnectType_BleHrm); #else const bool use_hrm_payload = false; @@ -96,7 +83,9 @@ static void prv_evaluate(ReconnectType prev_type) { // Create adv payload with only flags + HR service UUID. This is enough for various mobile // fitness apps to be able to reconnect to Pebble as BLE HRM. ad = ble_ad_create(); - ble_ad_set_flags(ad, GAP_LE_AD_FLAGS_GEN_DISCOVERABLE_MASK); + // BLE-only watch: advertise "BR/EDR Not Supported" so dual-mode hosts use LE. + ble_ad_set_flags(ad, GAP_LE_AD_FLAGS_GEN_DISCOVERABLE_MASK | + GAP_LE_AD_FLAGS_BR_EDR_NOT_SUPPORTED_MASK); Uuid service_uuid = bt_uuid_expand_16bit(0x180D); ble_ad_set_service_uuids(ad, &service_uuid, 1); } else { @@ -123,18 +112,15 @@ static void prv_evaluate(ReconnectType prev_type) { ad = &payload; } - // Values chosen according to: - // "Accessory Design Guidelies for Apple Devices" 55.4 Advertising Data + // Values chosen according to Apple Accessory Design Guidelines const GAPLEAdvertisingJobTerm advert_terms[] = { { .duration_secs = 30, - .min_interval_slots = 32, // 20ms - .max_interval_slots = 32, // 20ms + .interval = GAPLEAdvertisingInterval_Short, }, { .duration_secs = GAPLE_ADVERTISING_DURATION_INFINITE, - .min_interval_slots = 1636, // 1022.5ms - .max_interval_slots = 1636, // 1022.5ms + .interval = GAPLEAdvertisingInterval_Long, }, }; @@ -167,24 +153,24 @@ void gap_le_slave_reconnect_stop(void) { // ----------------------------------------------------------------------------- void gap_le_slave_reconnect_start(void) { -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW return; // Only use discoverable packet for PRF #endif bt_lock(); { if (prv_is_advertising_for_reconnection()) { - PBL_LOG(LOG_LEVEL_DEBUG, "Already advertising for reconnection"); + PBL_LOG_DBG("Already advertising for reconnection"); goto unlock; } if (gap_le_connect_is_connected_as_slave()) { - PBL_LOG(LOG_LEVEL_DEBUG, "Already connected as slave"); + PBL_LOG_DBG("Already connected as slave"); goto unlock; } if (!bt_persistent_storage_has_active_ble_gateway_bonding() && !bt_persistent_storage_has_ble_ancs_bonding()) { - PBL_LOG(LOG_LEVEL_DEBUG, "No bonded master device"); + PBL_LOG_DBG("No bonded master device"); goto unlock; } @@ -194,7 +180,7 @@ void gap_le_slave_reconnect_start(void) { bt_unlock(); } -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM #define RECONNECT_HRM_TIMEOUT_SECS (60) diff --git a/src/fw/comm/ble/gap_le_slave_reconnect.h b/src/fw/comm/ble/gap_le_slave_reconnect.h index 7187ca85bf..ec1687b777 100644 --- a/src/fw/comm/ble/gap_le_slave_reconnect.h +++ b/src/fw/comm/ble/gap_le_slave_reconnect.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -37,7 +24,7 @@ void gap_le_slave_reconnect_stop(void); //! - When Bluetooth is turned on void gap_le_slave_reconnect_start(void); -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM //! Start advertising for reconnection using a payload containing the Heart Rate Service UUID. //! It will automatically stop after 60 seconds, in case gap_le_slave_reconnect_hrm_stop() is not diff --git a/src/fw/comm/ble/gap_le_task.c b/src/fw/comm/ble/gap_le_task.c index 05688049af..9fb616217b 100644 --- a/src/fw/comm/ble/gap_le_task.c +++ b/src/fw/comm/ble/gap_le_task.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gap_le_task.h" diff --git a/src/fw/comm/ble/gap_le_task.h b/src/fw/comm/ble/gap_le_task.h index de4101f3dc..4bb5dc25fb 100644 --- a/src/fw/comm/ble/gap_le_task.h +++ b/src/fw/comm/ble/gap_le_task.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gatt.c b/src/fw/comm/ble/gatt.c index 3ac44e1cb7..a2e62068b6 100644 --- a/src/fw/comm/ble/gatt.c +++ b/src/fw/comm/ble/gatt.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include -#include "comm/ble/ble_log.h" +#include "system/logging.h" #include "comm/ble/gap_le_connection.h" #include "comm/ble/gatt_service_changed.h" #include "comm/bt_lock.h" @@ -24,6 +11,8 @@ #include +PBL_LOG_MODULE_DECLARE(bt, CONFIG_BT_LOG_LEVEL); + //! @see comment in gatt_client_subscriptions.c extern void gatt_client_subscriptions_handle_server_notification(GAPLEConnection *connection, uint16_t att_handle, @@ -41,8 +30,8 @@ void bt_driver_cb_gatt_handle_connect(const GattDeviceConnectionEvent *event) { } connection->gatt_connection_id = event->connection_id; connection->gatt_mtu = event->mtu; - BLE_LOG_DEBUG("GATT Connection for " BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE(event->dev_address)); + PBL_LOG_DBG("GATT Connection for " BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE(event->dev_address)); } unlock: bt_unlock(); @@ -57,8 +46,8 @@ void bt_driver_cb_gatt_handle_disconnect(const GattDeviceDisconnectionEvent *eve } connection->gatt_connection_id = 0; connection->gatt_mtu = 0; - BLE_LOG_DEBUG("GATT Disconnection for " BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE(event->dev_address)); + PBL_LOG_DBG("GATT Disconnection for " BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE(event->dev_address)); } unlock: bt_unlock(); @@ -72,7 +61,7 @@ void bt_driver_cb_gatt_handle_mtu_update(const GattDeviceMtuUpdateEvent *event) goto unlock; } - PBL_LOG(LOG_LEVEL_INFO, "Handle MTU change from %d to %d bytes", + PBL_LOG_INFO("Handle MTU change from %d to %d bytes", connection->gatt_mtu, event->mtu); connection->gatt_mtu = event->mtu; } @@ -96,7 +85,7 @@ void bt_driver_cb_gatt_handle_notification(const GattServerNotifIndicEvent *even event->attr_handle, event->attr_val, event->attr_val_len); - BLE_LOG_VERBOSE("GATT Server Notification for handle %u " BT_DEVICE_ADDRESS_FMT, + PBL_LOG_VERBOSE("GATT Server Notification for handle %u " BT_DEVICE_ADDRESS_FMT, event->attr_handle, BT_DEVICE_ADDRESS_XPLODE(event->dev_address)); } @@ -107,7 +96,7 @@ void bt_driver_cb_gatt_handle_indication(const GattServerNotifIndicEvent *event) { connection = gap_le_connection_by_addr(&event->dev_address); - BLE_LOG_VERBOSE("GATT Server Indication for handle %u " BT_DEVICE_ADDRESS_FMT, + PBL_LOG_VERBOSE("GATT Server Indication for handle %u " BT_DEVICE_ADDRESS_FMT, event->attr_handle, BT_DEVICE_ADDRESS_XPLODE(event->dev_address)); diff --git a/src/fw/comm/ble/gatt_client_accessors.c b/src/fw/comm/ble/gatt_client_accessors.c index c1e6c3d67b..792264dc27 100644 --- a/src/fw/comm/ble/gatt_client_accessors.c +++ b/src/fw/comm/ble/gatt_client_accessors.c @@ -1,20 +1,6 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "system/logging.h" #include "gatt_client_accessors.h" @@ -166,7 +152,7 @@ static bool prv_iter_service_node(const GATTServiceNode *service_node, if (inc_service_node) { callbacks->included_services_iterator(inc_service_node, cb_data); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Included Service with handle %u not found!", handle[h]); + PBL_LOG_DBG("Included Service with handle %u not found!", handle[h]); } } } @@ -364,7 +350,7 @@ uint8_t gatt_client_copy_service_refs_by_discovery_generation( { GAPLEConnection *connection = gap_le_connection_by_device(device); if (!connection) { - PBL_LOG(LOG_LEVEL_ERROR, "Disconnected in the mean time..."); + PBL_LOG_ERR("Disconnected in the mean time..."); goto unlock; } GATTServiceNode *node = connection->gatt_remote_services; @@ -395,7 +381,7 @@ uint8_t gatt_client_copy_service_refs_matching_uuid(const BTDeviceInternal *devi { GAPLEConnection *connection = gap_le_connection_by_device(device); if (!connection) { - PBL_LOG(LOG_LEVEL_ERROR, "Disconnected in the mean time..."); + PBL_LOG_ERR("Disconnected in the mean time..."); goto unlock; } GATTServiceNode *node = connection->gatt_remote_services; diff --git a/src/fw/comm/ble/gatt_client_accessors.h b/src/fw/comm/ble/gatt_client_accessors.h index fbd1b49294..7581a19abf 100644 --- a/src/fw/comm/ble/gatt_client_accessors.h +++ b/src/fw/comm/ble/gatt_client_accessors.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gatt_client_discovery.c b/src/fw/comm/ble/gatt_client_discovery.c index 793092f2b8..29ef196d7c 100644 --- a/src/fw/comm/ble/gatt_client_discovery.c +++ b/src/fw/comm/ble/gatt_client_discovery.c @@ -1,24 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gatt_client_discovery.h" #include "gatt_service_changed.h" #include "gap_le_connection.h" -#include "ble_log.h" #include "comm/bt_lock.h" #include "comm/bt_conn_mgr.h" @@ -28,11 +14,14 @@ #include "kernel/pbl_malloc.h" #include "gatt_client_accessors.h" #include "system/logging.h" +#include "drivers/rtc.h" #include #include #include +#include + // TODO: virtualize the gatt_client_discovery_discover_all() call //! Defined in gatt_client_subscriptions.c. Should only be called when receiving @@ -94,6 +83,8 @@ void gatt_client_discovery_discover_range(GAPLEConnection *connection, ATTHandle // assumes bt lock is held static BTErrno prv_run_next_job(GAPLEConnection *connection) { + bt_lock_assert_held(true); + DiscoveryJobQueue *node = connection->discovery_jobs; if (!node) { return BTErrnoOK; // no more jobs to run @@ -103,15 +94,23 @@ static BTErrno prv_run_next_job(GAPLEConnection *connection) { // has finished or error'ed out. That way the watchdog retry mechanism // can simply call this routine again to kick off another discovery attempt - PBL_LOG(LOG_LEVEL_INFO, "Starting BLE Service Discovery: 0x%x to 0x%x", + PBL_LOG_INFO("Starting BLE Service Discovery: 0x%x to 0x%x", node->hdl.start, node->hdl.end); ATTHandleRange hdl = { .start = node->hdl.start, .end = node->hdl.end }; + // Release bt_lock before calling into Nimble to avoid deadlock. + // bt_driver_gatt_start_discovery_range calls pebble_device_to_nimble_conn_handle + // which needs ble_hs_mutex. + bt_unlock(); + BTErrno rv = bt_driver_gatt_start_discovery_range(connection, &hdl); + // Re-acquire bt_lock before modifying connection state + bt_lock(); + if (rv == BTErrnoOK) { // if we are back here because a timeout occurred, let the // driver handle resetting the watchdog timer (cc2564x issue) @@ -128,21 +127,28 @@ static bool prv_discovery_handle_timeout(GAPLEConnection *connection, BTErrno *e bool retry_started = false; BTErrno finalize_result = BTErrnoOK; // Executing on NewTimer task, so need to bt_lock(): - PBL_LOG(LOG_LEVEL_WARNING, "Service Discovery Watchdog Timeout"); + PBL_LOG_WRN("Service Discovery Watchdog Timeout"); bt_lock(); { if (!gap_le_connection_is_valid(connection)) { goto unlock; } - if (bt_driver_gatt_stop_discovery(connection) != BTErrnoOK) { + // Release bt_lock before calling into Nimble to avoid deadlock. + // bt_driver_gatt_stop_discovery calls pebble_device_to_nimble_conn_handle + // which needs ble_hs_mutex. + bt_unlock(); + BTErrno stop_result = bt_driver_gatt_stop_discovery(connection); + bt_lock(); + + if (stop_result != BTErrnoOK) { // Handle the race: Bluetopia service discovery has stopped in the mean time, for example // because of a disconnection, internal error or it completed right when the timer fired. goto unlock; } if (connection->gatt_service_discovery_retries == GATT_CLIENT_DISCOVERY_MAX_RETRY) { -#if !RELEASE && !UNITTEST +#if !defined(CONFIG_RELEASE) && !UNITTEST core_dump_reset(true /* is_forced */); #endif // Done retrying, just error out: @@ -197,7 +203,7 @@ static void prv_send_services_added_event( list_count(&connection->gatt_remote_services->node) : 0; if (num_services_changed > BLE_GATT_MAX_SERVICES_CHANGED) { - PBL_LOG(LOG_LEVEL_ERROR, "Remote has %u services, more than we can handle.", + PBL_LOG_ERR("Remote has %u services, more than we can handle.", num_services_changed); num_services_changed = BLE_GATT_MAX_SERVICES_CHANGED; } @@ -401,6 +407,9 @@ bool bt_driver_cb_gatt_client_discovery_complete(GAPLEConnection *connection, BT } if (errno == BTErrnoOK) { + const uint32_t discovery_ms = + (rtc_get_ticks() - connection->ticks_since_connection) * 1000 / RTC_TICKS_HZ; + PBL_LOG_INFO("GATT service discovery completed in %"PRIu32"ms", discovery_ms); // Completion of service discovery implies we are about to have more BLE // traffic (for example, ANCS notifications, PPoG communication). Keep the // channel at a high throughput speed for a little bit longer to handle these bursts. @@ -473,7 +482,13 @@ BTErrno gatt_client_discovery_rediscover_all(const BTDeviceInternal *device) { // Remove any partial jobs which may be pending // since we are going to rediscover everything gatt_client_cleanup_discovery_jobs(connection); + + // Release bt_lock before calling into Nimble to avoid deadlock. + // bt_driver_gatt_stop_discovery calls pebble_device_to_nimble_conn_handle + // which needs ble_hs_mutex. + bt_unlock(); bt_driver_gatt_stop_discovery(connection); + bt_lock(); } else { // Queue up CCCD writes to unsubscribe all the subscriptions: gatt_client_subscriptions_cleanup_by_connection(connection, true /* should_unsubscribe */); diff --git a/src/fw/comm/ble/gatt_client_discovery.h b/src/fw/comm/ble/gatt_client_discovery.h index 9579c0e8a7..22eb58de1e 100644 --- a/src/fw/comm/ble/gatt_client_discovery.h +++ b/src/fw/comm/ble/gatt_client_discovery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gatt_client_operations.c b/src/fw/comm/ble/gatt_client_operations.c index 17f09b891a..903e413a90 100644 --- a/src/fw/comm/ble/gatt_client_operations.c +++ b/src/fw/comm/ble/gatt_client_operations.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -198,30 +185,42 @@ static BTErrno prv_read(uintptr_t obj_ref, GAPLEClient client, HandleAndConnectionGetter handle_getter, PebbleBLEGATTClientEventType subtype) { BTErrno ret_val = BTErrnoOK; + GAPLEConnection *connection; + uint16_t att_handle; + GattClientEventContext *data; + bt_lock(); { - GAPLEConnection *connection; - const uint16_t att_handle = handle_getter(obj_ref, &connection); + att_handle = handle_getter(obj_ref, &connection); if (!att_handle) { - ret_val = BTErrnoInvalidParameter; - goto unlock; + bt_unlock(); + return BTErrnoInvalidParameter; } - GattClientEventContext *data = prv_create_event_context(client); + data = prv_create_event_context(client); if (!data) { - ret_val = BTErrnoNotEnoughResources; - goto unlock; + bt_unlock(); + return BTErrnoNotEnoughResources; } // Zero'd out and added to list in `prv_create_event_context` data->client = client; data->subtype = subtype; data->obj_ref = obj_ref; - - ret_val = bt_driver_gatt_read(connection, att_handle, data); } -unlock: + // Release bt_lock BEFORE calling into NimBLE to avoid deadlock. + // If the connection becomes invalid after releasing the lock, the NimBLE driver + // will fail to look up the conn_handle and return an error. bt_unlock(); + + ret_val = bt_driver_gatt_read(connection, att_handle, data); + if (ret_val != BTErrnoOK) { + // Clean up the context we created if the driver call failed + bt_lock(); + list_remove(&data->node, (ListNode **)&s_client_event_ctxs[client], NULL); + kernel_free(data); + bt_unlock(); + } return ret_val; } @@ -229,30 +228,42 @@ static BTErrno prv_write(uintptr_t obj_ref, const uint8_t *value, size_t value_l GAPLEClient client, HandleAndConnectionGetter handle_getter, PebbleBLEGATTClientEventType subtype) { BTErrno ret_val = BTErrnoOK; + GAPLEConnection *connection; + uint16_t att_handle; + GattClientEventContext *data; + bt_lock(); { - GAPLEConnection *connection; - const uint16_t att_handle = handle_getter(obj_ref, &connection); + att_handle = handle_getter(obj_ref, &connection); if (!att_handle) { - ret_val = BTErrnoInvalidParameter; - goto unlock; + bt_unlock(); + return BTErrnoInvalidParameter; } - GattClientEventContext *data = prv_create_event_context(client); + data = prv_create_event_context(client); if (!data) { - ret_val = BTErrnoNotEnoughResources; - goto unlock; + bt_unlock(); + return BTErrnoNotEnoughResources; } // Zero'd out and added to list in `prv_create_event_context` data->client = client; data->subtype = subtype; data->obj_ref = obj_ref; - - ret_val = bt_driver_gatt_write(connection, value, value_length, att_handle, data); } -unlock: + // Release bt_lock BEFORE calling into NimBLE to avoid deadlock. + // If the connection becomes invalid after releasing the lock, the NimBLE driver + // will fail to look up the conn_handle and return an error. bt_unlock(); + + ret_val = bt_driver_gatt_write(connection, value, value_length, att_handle, data); + if (ret_val != BTErrnoOK) { + // Clean up the context we created if the driver call failed + bt_lock(); + list_remove(&data->node, (ListNode **)&s_client_event_ctxs[client], NULL); + kernel_free(data); + bt_unlock(); + } return ret_val; } @@ -299,22 +310,24 @@ BTErrno gatt_client_op_write_without_response(BLECharacteristic characteristic, const uint8_t *value, size_t value_length, GAPLEClient client) { - BTErrno ret_val = BTErrnoOK; + GAPLEConnection *connection; + uint16_t att_handle; + bt_lock(); { - GAPLEConnection *connection; - const uint16_t att_handle = + att_handle = gatt_client_characteristic_get_handle_and_connection(characteristic, &connection); if (!att_handle) { - ret_val = BTErrnoInvalidParameter; - goto unlock; + bt_unlock(); + return BTErrnoInvalidParameter; } - - ret_val = bt_driver_gatt_write_without_response(connection, value, value_length, att_handle); } -unlock: + // Release bt_lock BEFORE calling into NimBLE to avoid deadlock. + // If the connection becomes invalid after releasing the lock, the NimBLE driver + // will fail to look up the conn_handle and return BTErrnoInvalidState. bt_unlock(); - return ret_val; + + return bt_driver_gatt_write_without_response(connection, value, value_length, att_handle); } BTErrno gatt_client_op_write_descriptor(BLEDescriptor descriptor, diff --git a/src/fw/comm/ble/gatt_client_operations.h b/src/fw/comm/ble/gatt_client_operations.h index 14585d725f..f306a36139 100644 --- a/src/fw/comm/ble/gatt_client_operations.h +++ b/src/fw/comm/ble/gatt_client_operations.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/gatt_client_subscriptions.c b/src/fw/comm/ble/gatt_client_subscriptions.c index bb49b5c32d..2d9c420cc0 100644 --- a/src/fw/comm/ble/gatt_client_subscriptions.c +++ b/src/fw/comm/ble/gatt_client_subscriptions.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gatt_client_subscriptions.h" #include "gatt_client_accessors.h" @@ -29,7 +16,7 @@ #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" #include "system/logging.h" #include "system/passert.h" @@ -42,10 +29,6 @@ #include "FreeRTOS.h" #include "semphr.h" -//! Time to wait/block for when the buffer is full and needs to be drained by the client. -//! Note that bt_lock() is held while waiting, so this has to be rather small. -#define GATT_CLIENT_SUBSCRIPTIONS_WRITE_TIMEOUT_MS (100) - // TODO: // - Intercept "manual" CCCD writes from the app, error for now? or translate to // ble_client_subscribe calls? @@ -172,10 +155,8 @@ static bool prv_wait_until_write_space_available(const CircularBuffer *buffer, prv_unlock(); if (LIKELY(write_space >= required_length)) { if (UNLIKELY(did_stall)) { - PBL_LOG(LOG_LEVEL_DEBUG, "GATT notification stalled for %d ms...", + PBL_LOG_DBG("GATT notification stalled for %d ms...", (int)(timeout_ms - ticks_to_milliseconds(timeout_end_ticks - rtc_get_ticks()))); - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_GATT_STALLED_NOTIFICATIONS_COUNT, - AnalyticsClient_System); } return true; } @@ -220,7 +201,7 @@ void gatt_client_subscriptions_handle_server_notification(GAPLEConnection *conne // Only log the same handle once. Logging to flash adds enough of a delay to cause the // Bluetopia Mailbox to get backed up quicker when running at a 15ms connection interval. s_last_logged_handle = att_handle; - PBL_LOG(LOG_LEVEL_ERROR, "No subscription found for ATT handle %u", att_handle); + PBL_LOG_ERR("No subscription found for ATT handle %u", att_handle); } goto unlock; } @@ -244,17 +225,14 @@ void gatt_client_subscriptions_handle_server_notification(GAPLEConnection *conne // If we do not hold the bt_lock() at this point it's safe to block for a little bit waiting // for notifications to be consumed - uint32_t write_timeout = bt_lock_is_held() ? 0 : GATT_CLIENT_SUBSCRIPTIONS_WRITE_TIMEOUT_MS; + uint32_t write_timeout = bt_lock_is_held() ? 0 : CONFIG_BLE_GATT_NOTIF_WRITE_TIMEOUT_MS; bool consumed = prv_wait_until_write_space_available(buffer, (sizeof(header) + length), write_timeout); bt_lock(); if (!consumed) { - PBL_LOG(LOG_LEVEL_ERROR, - "Subscription buffer full. Dropping GATT notification of %u bytes (bt_lock held: %s)", + PBL_LOG_ERR("Subscription buffer full. Dropping GATT notification of %u bytes (bt_lock held: %s)", length, bt_lock_is_held() ? "yes" : "no"); - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_GATT_DROPPED_NOTIFICATIONS_COUNT, - AnalyticsClient_System); continue; } prv_lock(); @@ -300,8 +278,7 @@ void gatt_client_subscriptions_handle_write_cccd_response(BLEDescriptor cccd, BL prv_find_subscription_and_connection_for_cccd(cccd, &connection); if (!subscription || !connection) { // FIXME: When unsubscribing, the GATTClientSubscriptionNode is already removed at this point - PBL_LOG(LOG_LEVEL_DEBUG, - "No subscription and/or connection found for CCCD write response (%u)", error); + PBL_LOG_DBG("No subscription and/or connection found for CCCD write response (%u)", error); return; } @@ -342,7 +319,7 @@ void gatt_client_subscriptions_handle_write_cccd_response(BLEDescriptor cccd, BL static bool prv_check_buffer(GAPLEClient client) { if (s_circular_buffer[client] == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "App attempted to consume notifications without buffer."); + PBL_LOG_ERR("App attempted to consume notifications without buffer."); return false; } return true; @@ -413,13 +390,13 @@ uint16_t gatt_client_subscriptions_consume_notification(BLECharacteristic *chara value_out, header.value_length); if (UNLIKELY(copied_length != header.value_length)) { - PBL_LOG(LOG_LEVEL_ERROR, "Couldn't copy the number of requested byes (%u vs %u)", + PBL_LOG_ERR("Couldn't copy the number of requested byes (%u vs %u)", header.value_length, copied_length); } *characteristic_ref_out = header.characteristic; *value_length_in_out = copied_length; } else { - PBL_LOG(LOG_LEVEL_ERROR, "Client didn't provide buffer that was big enough (%u vs %u)", + PBL_LOG_ERR("Client didn't provide buffer that was big enough (%u vs %u)", *value_length_in_out, header.value_length); *characteristic_ref_out = BLE_CHARACTERISTIC_INVALID; *value_length_in_out = 0; @@ -428,7 +405,7 @@ uint16_t gatt_client_subscriptions_consume_notification(BLECharacteristic *chara circular_buffer_consume(s_circular_buffer[client], sizeof(header) + header.value_length); } else { - PBL_LOG(LOG_LEVEL_WARNING, "Consume called while no notifications in buffer"); + PBL_LOG_WRN("Consume called while no notifications in buffer"); *characteristic_ref_out = BLE_CHARACTERISTIC_INVALID; *value_length_in_out = 0; } @@ -637,7 +614,7 @@ static BTErrno prv_subscribe(BLECharacteristic characteristic_ref, connection->gatt_subscriptions = (GATTClientSubscriptionNode *) list_prepend(head, &subscription->node); - PBL_LOG(LOG_LEVEL_DEBUG, "Added BLE subscription for handle 0x%x", att_handle); + PBL_LOG_DBG("Added BLE subscription for handle 0x%x", att_handle); did_create_new_subscription = true; } @@ -657,8 +634,19 @@ static BTErrno prv_subscribe(BLECharacteristic characteristic_ref, // Write to the Client Configuration Characteristic Descriptor on the // remote to change the subscription: const uint16_t value = subscription_type; + + // Release bt_lock before writing CCCD, as this calls into NimBLE. + // prv_write() (gatt_client_operations.c) releases its own bt_lock level before + // calling NimBLE, but if callers hold bt_lock recursively, the recursive mutex + // won't actually release. Dropping our level here ensures the lock is fully + // released when NimBLE is called, avoiding a lock ordering deadlock with + // ble_hs_mutex. + bt_unlock(); + ret_val = gatt_client_op_write_descriptor_cccd(cccd_ref, &value); + bt_lock(); + if (ret_val != BTErrnoOK) { // Write failed, bail out! if (did_create_new_subscription) { diff --git a/src/fw/comm/ble/gatt_client_subscriptions.h b/src/fw/comm/ble/gatt_client_subscriptions.h index 3fa96404a6..7d0738a36d 100644 --- a/src/fw/comm/ble/gatt_client_subscriptions.h +++ b/src/fw/comm/ble/gatt_client_subscriptions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -27,7 +14,8 @@ struct GAPLEConnection; #define MAX_ATT_WRITE_PAYLOAD_SIZE (ATT_MAX_SUPPORTED_MTU - 3) #define GATT_CLIENT_SUBSCRIPTIONS_BUFFER_SIZE \ - ((MAX_ATT_WRITE_PAYLOAD_SIZE + sizeof(GATTBufferedNotificationHeader)) * 4) + ((MAX_ATT_WRITE_PAYLOAD_SIZE + sizeof(GATTBufferedNotificationHeader)) * \ + CONFIG_BLE_GATT_SUBSCRIPTION_DEPTH) //! Data structure representing a subscription of a specific client for //! noticications or indications of a GATT characteristic for a specific diff --git a/src/fw/comm/ble/gatt_service_changed.c b/src/fw/comm/ble/gatt_service_changed.c index 8adb5b2ea2..d93dc5ec33 100644 --- a/src/fw/comm/ble/gatt_service_changed.c +++ b/src/fw/comm/ble/gatt_service_changed.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "gatt_service_changed.h" @@ -22,8 +9,8 @@ #include "kernel/pbl_malloc.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "util/net.h" @@ -48,7 +35,7 @@ static void prv_rediscover_kernelbg_cb(void *data) { const BTErrno e = gatt_client_discovery_rediscover_all(device); kernel_free(device); if (e != BTErrnoOK) { - PBL_LOG(LOG_LEVEL_ERROR, "Service Changed couldn't restart discovery: %i", e); + PBL_LOG_ERR("Service Changed couldn't restart discovery: %i", e); } } @@ -60,13 +47,13 @@ bool gatt_service_changed_client_handle_indication(struct GAPLEConnection *conne return false; } if (value_length != sizeof(ATTHandleRange)) { - PBL_LOG(LOG_LEVEL_ERROR, "Service Changed Indication incorrect length: %u", value_length); + PBL_LOG_ERR("Service Changed Indication incorrect length: %u", value_length); // Pretend we ate the indication. There will be no GAPLECharacteristic in the system that will // match this ATT handle anyway. return true; } ATTHandleRange *range = (ATTHandleRange *) value; - PBL_LOG(LOG_LEVEL_DEBUG, "Service Changed Indication: %x - %x", range->start, range->end); + PBL_LOG_DBG("Service Changed Indication: %x - %x", range->start, range->end); // Initiate rediscovery on KernelBG if the Server is asking us to rediscover everything // (See "2.5.2 Attribute Caching" in BT Core Specification) @@ -121,7 +108,7 @@ void gatt_service_changed_server_handle_fw_update(void) { void bt_driver_cb_gatt_service_changed_server_confirmation( const GattServerChangedConfirmationEvent *event) { if (event->status_code != HciStatusCode_Success) { - PBL_LOG(LOG_LEVEL_ERROR, "Service Changed indication confirmation failure (timed out?) %"PRIu32, + PBL_LOG_ERR("Service Changed indication confirmation failure (timed out?) %"PRIu32, (uint32_t)event->status_code); } } @@ -134,6 +121,33 @@ void gatt_service_changed_server_cleanup_by_connection(GAPLEConnection *connecti } static void prv_send_service_changed_indication(void *ctx) { + GAPLEConnection *connection = (GAPLEConnection *)ctx; + + BTDeviceInternal device; + bt_lock(); + { + // The connection may have been torn down between the timer firing and this + // callback running, leaving a dangling pointer. Bail if it is no longer live. + if (!connection || !gap_le_connection_is_valid(connection)) { + bt_unlock(); + return; + } + connection->gatt_service_changed_indication_timer = TIMER_INVALID_ID; + // Copy the device address while holding bt_lock — the driver resolves the + // NimBLE connection handle through its own connection table, so no bt_lock + // is needed in the driver path. + device = connection->device; + } + bt_unlock(); + + // Invalidate the remote's entire attribute cache so it rediscovers all of our + // services (see "2.5.2 Attribute Caching" in the BT Core Specification). This + // mirrors what the client side treats as a "rediscover everything" request. + const ATTHandleRange range = { + .start = 0x0001, + .end = 0xFFFF, + }; + bt_driver_gatt_send_changed_indication(&device, &range); } static void prv_send_indication_timer_cb(void *ctx) { @@ -143,11 +157,22 @@ static void prv_send_indication_timer_cb(void *ctx) { void bt_driver_cb_gatt_service_changed_server_subscribe( const GattServerSubscribeEvent *event) { + // Create timer outside of bt_lock to avoid deadlock with NimbleHost. + // new_timer_create() acquires TaskTimerManager mutex, which may be held by NimbleHost + // when it's trying to acquire bt_lock, leading to a lock ordering deadlock. + TimerID timer = TIMER_INVALID_ID; + const bool subscribed = event->is_subscribing; + if (subscribed) { + timer = new_timer_create(); + if (!timer) { + return; + } + } + bt_lock(); { - const bool subscribed = event->is_subscribing; if (subscribed) { - PBL_LOG(LOG_LEVEL_DEBUG, "Remote subscribed to Service Changed characteristic"); + PBL_LOG_DBG("Remote subscribed to Service Changed characteristic"); GAPLEConnection *connection = gap_le_connection_by_addr(&event->dev_address); if (!connection || connection->has_sent_gatt_service_changed_indication) { @@ -156,19 +181,20 @@ void bt_driver_cb_gatt_service_changed_server_subscribe( } // PRF will always send a "Service Changed" indication: -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) if (s_service_changed_indications_left <= 0) { goto unlock; } #endif - PBL_LOG(LOG_LEVEL_INFO, "Indicating Service Changed to remote device"); + PBL_LOG_INFO("Indicating Service Changed to remote device"); // Work-around for iOS issue (see comment above), send the indication after a short delay: - connection->gatt_service_changed_indication_timer = new_timer_create(); + connection->gatt_service_changed_indication_timer = timer; new_timer_start(connection->gatt_service_changed_indication_timer, GATT_SERVICE_CHANGED_INDICATION_DELAY_MS, prv_send_indication_timer_cb, connection, 0); + timer = TIMER_INVALID_ID; // Timer now owned by connection, don't delete it below --s_service_changed_indications_left; // Don't send again for this connection: connection->has_sent_gatt_service_changed_indication = true; @@ -176,6 +202,11 @@ void bt_driver_cb_gatt_service_changed_server_subscribe( } unlock: bt_unlock(); + + // Clean up timer if we didn't use it + if (timer != TIMER_INVALID_ID) { + new_timer_delete(timer); + } } void bt_driver_cb_gatt_service_changed_server_read_subscription( diff --git a/src/fw/comm/ble/gatt_service_changed.h b/src/fw/comm/ble/gatt_service_changed.h index aa39c750da..07c177d440 100644 --- a/src/fw/comm/ble/gatt_service_changed.h +++ b/src/fw/comm/ble/gatt_service_changed.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams.c b/src/fw/comm/ble/kernel_le_client/ams/ams.c index 90689e7364..f87be3d95c 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams.c +++ b/src/fw/comm/ble/kernel_le_client/ams/ams.c @@ -1,24 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ams.h" #include "ams_analytics.h" #include "ams_util.h" -#include "comm/ble/ble_log.h" #include "comm/ble/gap_le_connection.h" #include "comm/ble/gatt_client_accessors.h" #include "comm/ble/gatt_client_operations.h" @@ -29,8 +15,7 @@ #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics_event.h" -#include "services/normal/music_internal.h" +#include "pbl/services/music_internal.h" #include "system/logging.h" #include "system/hexdump.h" @@ -42,6 +27,8 @@ #include +PBL_LOG_MODULE_DECLARE(bt, CONFIG_BT_LOG_LEVEL); + // ------------------------------------------------------------------------------------------------- // Function prototypes @@ -169,10 +156,6 @@ static const MusicServerImplementation s_ams_music_implementation = { // ------------------------------------------------------------------------------------------------- // Internal helpers -static void prv_analytics_log_event_with_info(AMSAnalyticsEvent event, int32_t aux_info) { - analytics_event_ams(event, aux_info); -} - static void prv_perform_on_kernel_main_task(void (*callback)(void *), void *data) { const bool is_kernel_main = (pebble_task_get_current() == PebbleTask_KernelMain); if (is_kernel_main) { @@ -274,9 +257,8 @@ static void prv_register_next_entity(void *unused) { launcher_task_add_callback(&prv_register_next_entity, NULL); } else { // Most likely the LE connection got busted, don't think retrying will help. - PBL_LOG(LOG_LEVEL_ERROR, "Write failed %i", e); + PBL_LOG_ERR("Write failed %i", e); } - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorRegisterEntityWrite, e); } } @@ -288,12 +270,17 @@ static bool prv_set_connected(bool connected) { const bool has_error = !music_set_connected_server(&s_ams_music_implementation, connected); if (has_error) { s_ams_client->connected = false; - PBL_LOG(LOG_LEVEL_ERROR, "AMS could not (dis)connect to music service (%u)", connected); - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorMusicServiceConnect, connected ? 1 : 2); + PBL_LOG_ERR("AMS could not (dis)connect to music service (%u)", connected); } return !has_error; } +void ams_music_disconnect(void) { + if (s_ams_client && s_ams_client->connected) { + prv_set_connected(false); + } +} + // ------------------------------------------------------------------------------------------------- // Player entity update handlers @@ -330,12 +317,11 @@ static bool prv_handle_player_playback_info_value(const char *value, uint32_t va }; if (value_length && !ams_util_float_string_parse(value, value_length, multiplier[idx], &value_out)) { - PBL_LOG(LOG_LEVEL_ERROR, "AMS playback info value failed to parse: %s", value); - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorPlayerPlaybackInfoFloatParse, idx); + PBL_LOG_ERR("AMS playback info value failed to parse: %s", value); return false /* should_continue */; } - PBL_LOG(LOG_LEVEL_DEBUG, "Playback info value update %"PRId32"=%"PRId32, idx, value_out); + PBL_LOG_DBG("Playback info value update %"PRId32"=%"PRId32, idx, value_out); MusicPlayerStateUpdate *state = (MusicPlayerStateUpdate *)context; switch (idx) { @@ -367,9 +353,8 @@ static void prv_handle_player_playback_info_update(const AMSEntityUpdateNotifica if (success) { music_update_player_playback_state(&state); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Expected CSV with 3 values:"); + PBL_LOG_ERR("Expected CSV with 3 values:"); PBL_HEXDUMP(LOG_LEVEL_ERROR, (const uint8_t *) update->value_str, value_length); - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorPlayerPlaybackInfoUpdate, num_results); } } @@ -377,7 +362,7 @@ static bool prv_float_string_parse(const char *value, const uint16_t value_lengt int32_t multiplier, int32_t *value_in_out) { if (value_length && !ams_util_float_string_parse(value, value_length, multiplier, value_in_out)) { - PBL_LOG(LOG_LEVEL_ERROR, "AMS float failed to parse:"); + PBL_LOG_ERR("AMS float failed to parse:"); PBL_HEXDUMP(LOG_LEVEL_ERROR, (const uint8_t *)value, value_length); return false; } @@ -390,8 +375,6 @@ static void prv_handle_player_volume_update(const AMSEntityUpdateNotification *u const bool success = prv_float_string_parse(update->value_str, value_length, 100, &value_out); if (success) { music_update_player_volume_percent(value_out); - } else { - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorPlayerVolumeUpdate, value_length); } } @@ -407,28 +390,28 @@ static int32_t prv_parse_queue_value(const char *value, const uint16_t value_len static void prv_handle_queue_index_update(const AMSEntityUpdateNotification *update, const uint16_t value_length) { const int32_t idx = prv_parse_queue_value(update->value_str, value_length); - PBL_LOG(LOG_LEVEL_DEBUG, "Queue index update: %"PRId32, idx); + PBL_LOG_DBG("Queue index update: %"PRId32, idx); // TODO: Do something with this info } static void prv_handle_queue_count_update(const AMSEntityUpdateNotification *update, const uint16_t value_length) { const int32_t count = prv_parse_queue_value(update->value_str, value_length); - PBL_LOG(LOG_LEVEL_DEBUG, "Queue count update: %"PRId32, count); + PBL_LOG_DBG("Queue count update: %"PRId32, count); // TODO: Do something with this info } static void prv_handle_queue_shuffle_mode_update(const AMSEntityUpdateNotification *update, const uint16_t value_length) { const AMSShuffleMode shuffle_mode = prv_parse_queue_value(update->value_str, value_length); - PBL_LOG(LOG_LEVEL_DEBUG, "Queue shuffle mode update: %d", shuffle_mode); + PBL_LOG_DBG("Queue shuffle mode update: %d", shuffle_mode); // TODO: Do something with this info } static void prv_handle_queue_repeat_mode_update(const AMSEntityUpdateNotification *update, const uint16_t value_length) { const AMSRepeatMode repeat_mode = prv_parse_queue_value(update->value_str, value_length); - PBL_LOG(LOG_LEVEL_DEBUG, "Queue repeat mode update: %d", repeat_mode); + PBL_LOG_DBG("Queue repeat mode update: %d", repeat_mode); // TODO: Do something with this info } @@ -459,8 +442,7 @@ static void prv_handle_track_duration_update(const AMSEntityUpdateNotification * if (success) { music_update_track_duration(duration_ms); } else { - PBL_LOG(LOG_LEVEL_ERROR, "AMS duration failed to parse"); - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorTrackDurationUpdate, value_length); + PBL_LOG_ERR("AMS duration failed to parse"); } } @@ -536,7 +518,7 @@ static void prv_handle_update(const AMSEntityUpdateNotification *update, break; } - PBL_LOG(LOG_LEVEL_ERROR, "Unknown EntityID:%u + AttrID:%u", + PBL_LOG_ERR("Unknown EntityID:%u + AttrID:%u", update->entity_id, update->attribute_id); } @@ -570,11 +552,11 @@ void ams_handle_service_discovered(BLECharacteristic *characteristics) { return; } - BLE_LOG_DEBUG("In AMS service discovery CB"); + PBL_LOG_DBG("In AMS service discovery CB"); PBL_ASSERTN(characteristics); if (s_ams_client->characteristics[0] != BLE_CHARACTERISTIC_INVALID) { - PBL_LOG(LOG_LEVEL_WARNING, "Multiple AMS instances registered!?"); + PBL_LOG_WRN("Multiple AMS instances registered!?"); return; } @@ -587,7 +569,13 @@ void ams_handle_service_discovered(BLECharacteristic *characteristics) { const BTErrno e = gatt_client_subscriptions_subscribe(entity_update_characteristic, BLESubscriptionNotifications, GAPLEClientKernel); - PBL_ASSERTN(e == BTErrnoOK); + // Reject the service if subscribing fails instead of asserting (e.g. a "fake + // AMS" without CCCD). + if (e != BTErrnoOK) { + PBL_LOG_WRN("Failed to subscribe AMS (err=%d), ignoring service", e); + ams_invalidate_all_references(); + return; + } } bool ams_can_handle_characteristic(BLECharacteristic characteristic) { @@ -611,13 +599,12 @@ void ams_handle_subscribe(BLECharacteristic subscribed_characteristic, } if (error != BLEGATTErrorSuccess) { - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorSubscribe, error); - PBL_LOG(LOG_LEVEL_ERROR, "Failed to subscribe AMS"); + PBL_LOG_ERR("Failed to subscribe AMS"); return; } - PBL_LOG(LOG_LEVEL_INFO, "Hurray! AMS subscribed"); + PBL_LOG_INFO("Hurray! AMS subscribed"); if (!prv_set_connected(true)) { - PBL_LOG(LOG_LEVEL_ERROR, "Another music service was already connected. Aborting AMS setup."); + PBL_LOG_ERR("Another music service was already connected. Aborting AMS setup."); return; } prv_register_next_entity(NULL); @@ -631,12 +618,6 @@ void ams_handle_write_response(BLECharacteristic characteristic, BLEGATTError er (characteristic == s_ams_client->characteristics[AMSCharacteristicEntityUpdate]); const bool has_error = (error != BLEGATTErrorSuccess); - if (has_error) { - const AMSAnalyticsEvent event = is_entity_update_characteristic ? - AMSAnalyticsEventErrorRegisterEntityWriteResponse : - AMSAnalyticsEventErrorOtherWriteResponse; - prv_analytics_log_event_with_info(event, error); - } if (!is_entity_update_characteristic) { // We only need to act upon getting a write response of the Entity Update characteristic. @@ -645,12 +626,12 @@ void ams_handle_write_response(BLECharacteristic characteristic, BLEGATTError er } const AMSEntityID entity_id = s_ams_client->next_entity_to_register; if (has_error) { - PBL_LOG(LOG_LEVEL_ERROR, "AMS Failed to register entity_id=%u: %u", entity_id, error); + PBL_LOG_ERR("AMS Failed to register entity_id=%u: %u", entity_id, error); // TODO: Log error event // Don't retry here, chances of succeeding are slim. return; } - PBL_LOG(LOG_LEVEL_DEBUG, "AMS Registered for entity_id=%u", entity_id); + PBL_LOG_DBG("AMS Registered for entity_id=%u", entity_id); ++s_ams_client->next_entity_to_register; prv_register_next_entity(NULL); } @@ -659,7 +640,7 @@ void ams_handle_read_or_notification(BLECharacteristic characteristic, const uin size_t value_length, BLEGATTError error) { if (!s_ams_client || s_ams_client->characteristics[AMSCharacteristicEntityUpdate] != characteristic) { - PBL_LOG(LOG_LEVEL_ERROR, "Unexpected characteristic (s_ams_client=%p)", s_ams_client); + PBL_LOG_ERR("Unexpected characteristic (s_ams_client=%p)", s_ams_client); return; } PBL_HEXDUMP(LOG_LEVEL_DEBUG, value, value_length); @@ -687,8 +668,7 @@ static void prv_send_command_kernel_main_task_cb(void *data) { (const uint8_t *) &command_id, 1, GAPLEClientKernel); const bool has_error = (error != BTErrnoOK); if (has_error) { - PBL_LOG(LOG_LEVEL_ERROR, "Couldn't write command: %d", error); - prv_analytics_log_event_with_info(AMSAnalyticsEventErrorSendRemoteCommand, error); + PBL_LOG_ERR("Couldn't write command: %d", error); } } diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams.h b/src/fw/comm/ble/kernel_le_client/ams/ams.h index 33e9d8d636..5413d923b9 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams.h +++ b/src/fw/comm/ble/kernel_le_client/ams/ams.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -88,6 +75,13 @@ void ams_handle_read_or_notification(BLECharacteristic characteristic, const uin //! Must only be called from KernelMain! void ams_destroy(void); +//! Disconnect AMS from the music service if currently registered. No-op otherwise. +//! Some Android phones expose something that looks like an AMS GATT service, +//! letting AMS grab the music slot before we learn the remote is Android. The PP +//! music endpoint calls this on Android-detect so it can take over the slot. +//! Must only be called from KernelMain! +void ams_music_disconnect(void); + //! This function is exported only for (unit) testing purposes! //! OK to call from any task. void ams_send_command(AMSRemoteCommandID command_id); diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams_analytics.h b/src/fw/comm/ble/kernel_le_client/ams/ams_analytics.h index a01a8f38b2..4d7a8e35c5 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams_analytics.h +++ b/src/fw/comm/ble/kernel_le_client/ams/ams_analytics.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams_definition.h b/src/fw/comm/ble/kernel_le_client/ams/ams_definition.h index 916ad8163e..436e1af5c2 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams_definition.h +++ b/src/fw/comm/ble/kernel_le_client/ams/ams_definition.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams_types.h b/src/fw/comm/ble/kernel_le_client/ams/ams_types.h index eafaa5e442..04550addf8 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams_types.h +++ b/src/fw/comm/ble/kernel_le_client/ams/ams_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams_util.c b/src/fw/comm/ble/kernel_le_client/ams/ams_util.c index c55b80d277..494c8b4c22 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams_util.c +++ b/src/fw/comm/ble/kernel_le_client/ams/ams_util.c @@ -1,20 +1,6 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "system/logging.h" #include "ams_util.h" diff --git a/src/fw/comm/ble/kernel_le_client/ams/ams_util.h b/src/fw/comm/ble/kernel_le_client/ams/ams_util.h index 95370a22dd..37393aadaa 100644 --- a/src/fw/comm/ble/kernel_le_client/ams/ams_util.h +++ b/src/fw/comm/ble/kernel_le_client/ams/ams_util.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs.c b/src/fw/comm/ble/kernel_le_client/ancs/ancs.c index 9bf8703dd7..b9b06e3a0f 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs.c +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs.c @@ -1,20 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ancs.h" #include "ancs_app_name_storage.h" @@ -22,7 +7,6 @@ #include "ancs_util.h" #include "ancs_definition.h" -#include "comm/ble/ble_log.h" #include "comm/ble/gatt_client_subscriptions.h" #include "comm/ble/gatt_client_operations.h" #include "comm/ble/kernel_le_client/dis/dis.h" @@ -30,11 +14,11 @@ #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics.h" -#include "services/common/evented_timer.h" -#include "services/normal/notifications/ancs/ancs_notifications.h" -#include "services/common/regular_timer.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/notifications/ancs/ancs_notifications.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/timeline/timeline.h" #include "system/hexdump.h" #include "system/passert.h" @@ -46,6 +30,8 @@ #include +PBL_LOG_MODULE_DECLARE(bt, CONFIG_BT_LOG_LEVEL); + // ----------------------------------------------------------------------------- // Static function prototypes @@ -68,14 +54,16 @@ static void prv_perform_action(uint32_t notification_uid, ActionId action_id); // // All accesses to these variables should happen from the KernelMain task, // therefore no concurrent accesses can happen and no lock is needed. -// The only exception is the s_ns_flags_used_bitset, which gets read/set in -// analytics_external_collect_ancs_info from KernelBG. Since it's only one byte -// it should be fine. #define INVALID_NOTIFICATION_UID 0xFFFFFFFF #define ANCS_RETRY_TIME_MS (5 * MS_PER_SECOND) +// Bounds the notification queue so a post-reconnect ANCS burst from many +// chatty apps cannot grow it without limit. Action ops are not capped — they +// are user-initiated and rare. +#define ANCS_NOTIF_QUEUE_MAX_DEPTH 16 + typedef struct { uint8_t command_id; union { @@ -114,13 +102,13 @@ typedef struct ANCSClient { NotificationQueueNode *queue; bool alive_check_pending; ANCSVersion version; + // Alive-check intervals with no NS traffic; reset by any NS notification. + // Triggers a CCCD re-subscribe at the threshold to recover silent NS. + uint8_t alive_checks_without_ns; } ANCSClient; static ANCSClient *s_ancs_client; -// Keeps track of used NS flags for analytics purposes: -static uint8_t s_ns_flags_used_bitset; - // ----------------------------------------------------------------------------- // State Machine @@ -205,7 +193,7 @@ static void prv_notif_queue_reset(void) { static void prv_notif_queue_push_common(NotificationQueueNode *node) { if (prv_notif_queue_find(node)) { // already in the queue - PBL_LOG(LOG_LEVEL_WARNING, "ANCS item already in Queue"); + PBL_LOG_WRN("ANCS item already in Queue"); kernel_free(node); return; } @@ -221,7 +209,11 @@ static void prv_notif_queue_push_common(NotificationQueueNode *node) { } static void prv_notif_queue_push_action(uint32_t uid, ActionId action_id) { - NotificationQueueNode *node = kernel_malloc_check(sizeof(NotificationQueueNode)); + NotificationQueueNode *node = kernel_malloc(sizeof(NotificationQueueNode)); + if (!node) { + PBL_LOG_WRN("ANCS action alloc failed, dropping (uid=%"PRIu32")", uid); + return; + } *node = (NotificationQueueNode) { .op = NotificationQueueOpPerformAction, .uid = uid, @@ -232,7 +224,16 @@ static void prv_notif_queue_push_action(uint32_t uid, ActionId action_id) { } static void prv_notif_queue_push_attr_request(uint32_t uid, ANCSProperty properties) { - NotificationQueueNode *node = kernel_malloc_check(sizeof(NotificationQueueNode)); + if (list_count((ListNode *)s_ancs_client->queue) >= ANCS_NOTIF_QUEUE_MAX_DEPTH) { + PBL_LOG_WRN("ANCS queue full, dropping notif (uid=%"PRIu32")", uid); + return; + } + + NotificationQueueNode *node = kernel_malloc(sizeof(NotificationQueueNode)); + if (!node) { + PBL_LOG_WRN("ANCS attr-request alloc failed, dropping (uid=%"PRIu32")", uid); + return; + } *node = (NotificationQueueNode) { .op = NotificationQueueOpGetAttributes, .uid = uid, @@ -295,8 +296,6 @@ static void prv_reset_and_flush(void) { } static void prv_reset_due_to_parse_error(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_PARSE_ERROR_COUNT, - AnalyticsClient_System); prv_reset_and_next(); } @@ -308,9 +307,18 @@ static void prv_reset_due_to_bt_error(void) { // Is Alive Logic #define ANCS_INVALID_PARAM 0xA2 -#define ANCS_IS_ALIVE_NEXT_CHECK_TIME_MINUTES 60 // 1 hour (60 minutes) +#define ANCS_IS_ALIVE_NEXT_CHECK_TIME_MINUTES 15 // Check every 15 minutes for faster recovery #define ANCS_IS_ALIVE_RESPONSE_WAIT_TIME_SECONDS 5 // 5 seconds +// Force a CCCD refresh after this many consecutive alive-check intervals +// without any NS traffic. 6 h is short enough to recover the same day for a +// typical user but long enough that DND / overnight don't trigger spurious +// refreshes -- iOS handles redundant CCCD writes silently. Derived from the +// alive-check interval so future tuning of one doesn't desync the other. +#define ANCS_NO_NS_TIMEOUT_HOURS 6 +#define ANCS_ALIVE_CHECKS_WITHOUT_NS_BEFORE_RESUBSCRIBE \ + ((ANCS_NO_NS_TIMEOUT_HOURS * 60) / ANCS_IS_ALIVE_NEXT_CHECK_TIME_MINUTES) + static void prv_is_ancs_alive_cb(void *data); static void prv_is_ancs_alive_response_timeout(void *data); @@ -342,12 +350,45 @@ static void prv_ancs_is_alive_start_tracking(void) { prv_ancs_is_alive_stop_timer(); } else { // Not scheduled, so analytics stopwatch would have been stopped - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_CONNECT_TIME, - AnalyticsClient_System); } prv_ancs_is_alive_schedule_next_check(); } +static void prv_resubscribe_to_ancs(void) { + if (!s_ancs_client) { + return; + } + + // Check if we have valid characteristic handles to re-subscribe to + if (s_ancs_client->characteristics[ANCSCharacteristicNotification] == BLE_CHARACTERISTIC_INVALID) { + PBL_LOG_WRN("Cannot resubscribe to ANCS: no valid characteristic handles"); + return; + } + + PBL_LOG_INFO("Re-subscribing to ANCS characteristics"); + + // Unsubscribe first to clear local subscription state, then re-subscribe. + // Without this, gatt_client_subscriptions_subscribe() sees the existing local + // subscription and returns BTErrnoInvalidState without writing the CCCD to + // the remote device. iOS may have silently dropped its subscription state + // (e.g. during a PPoGATT reset), so we must re-write the CCCD to restore it. + for (int c = ANCSCharacteristicData; c >= ANCSCharacteristicNotification; --c) { + BLECharacteristic charx = s_ancs_client->characteristics[c]; + if (charx != BLE_CHARACTERISTIC_INVALID) { + // Unsubscribe to clear the local state (ignore errors -- the subscription + // may already have been cleaned up) + gatt_client_subscriptions_subscribe(charx, BLESubscriptionNone, GAPLEClientKernel); + + const BTErrno e = gatt_client_subscriptions_subscribe(charx, + BLESubscriptionNotifications, + GAPLEClientKernel); + if (e != BTErrnoOK) { + PBL_LOG_ERR("Failed to resubscribe to ANCS charx %d: %d", c, e); + } + } + } +} + static void prv_is_ancs_alive_response_timeout_launcher_task_cb(void *data) { if (!s_ancs_client) { return; @@ -357,17 +398,30 @@ static void prv_is_ancs_alive_response_timeout_launcher_task_cb(void *data) { // Stop the wait for response timer prv_ancs_is_alive_stop_timer(); + + // Try to recover by re-subscribing to ANCS characteristics + // This handles the case where iOS has dropped the GATT subscription + // without the watch knowing about it (common during PPoGATT resets) + prv_resubscribe_to_ancs(); + + // Always re-arm the alive-check timer here, even though a successful + // resubscribe will also re-arm it from ancs_handle_subscribe(). Recovery is + // not guaranteed: characteristics may be invalidated, the CCCD write may + // fail synchronously, or iOS may never reply with a CCCD response. Without + // this re-arm those paths leave the timer permanently stopped and the watch + // stays connected but silent until reboot. The call is idempotent -- + // prv_ancs_is_alive_start_tracking() cancels and reschedules. + prv_ancs_is_alive_schedule_next_check(); } static void prv_is_ancs_alive_response_timeout(void *data) { - PBL_LOG(LOG_LEVEL_DEBUG, "ANCS isn't alive"); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_CONNECT_TIME); + PBL_LOG_DBG("ANCS isn't alive"); launcher_task_add_callback(prv_is_ancs_alive_response_timeout_launcher_task_cb, data); } static void prv_ancs_is_alive(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "ANCS is alive!"); + PBL_LOG_DBG("ANCS is alive!"); // Restart analytics tracking (if it stopped) and the 'is alive' timer prv_ancs_is_alive_start_tracking(); @@ -379,6 +433,23 @@ T_STATIC void prv_check_ancs_alive(void) { if (s_ancs_client) { s_ancs_client->alive_check_pending = false; + + // If iOS has been silent on NS for too long, the CCCD subscription is + // likely wedged on iOS' side even though CP responses look fine. Force a + // re-subscribe to nudge iOS into resuming pushes, and skip this round's CP + // probe -- the next 15 min alive check will verify recovery. + if (++s_ancs_client->alive_checks_without_ns >= + ANCS_ALIVE_CHECKS_WITHOUT_NS_BEFORE_RESUBSCRIBE) { + PBL_LOG_INFO("ANCS NS silent for %u alive checks; forcing CCCD resubscribe", + s_ancs_client->alive_checks_without_ns); + s_ancs_client->alive_checks_without_ns = 0; + prv_resubscribe_to_ancs(); + // ancs_handle_subscribe() will re-arm on success; schedule a backup so + // we still tick if the CCCD response never lands. + prv_ancs_is_alive_schedule_next_check(); + return; + } + prv_set_state(ANCSClientStateAliveCheck); //! Sends an ANCS attribute fetch (to the Control Point). The notification UID is invalid, ANCS //! will reply with 0xA2 (invalid param) @@ -490,9 +561,6 @@ static bool prv_reassembly_is_complete(const uint8_t* data, const size_t length, } static void prv_reassembly_handle_complete_response(const uint8_t* data, const size_t length) { - - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_COUNT, AnalyticsClient_System); - switch (prv_current_command_id(data)) { case CommandIDGetNotificationAttributes: prv_handle_notification_attributes_response(data, length); @@ -521,7 +589,7 @@ static void prv_reassemble_ds_notification(uint32_t length, const uint8_t *data) // could also occur if the iPhone restarts after sending us an incomplete // message, then we re-subscribe and start over from a different state if (!is_success) { - PBL_LOG(LOG_LEVEL_ERROR, "ANCS reassembly buffer overflow; resetting ctx"); + PBL_LOG_ERR("ANCS reassembly buffer overflow; resetting ctx"); // TODO: separate analytics trackers instead of piling onto "parse error count" prv_reset_due_to_parse_error(); return; @@ -545,7 +613,7 @@ static void prv_reassemble_ds_notification(uint32_t length, const uint8_t *data) if (!is_complete) { // Keep waiting - BLE_LOG_DEBUG("Incomplete response. Waiting for another DS notification."); + PBL_LOG_DBG("Incomplete response. Waiting for another DS notification."); return; } @@ -586,7 +654,7 @@ static void prv_handle_app_attributes_response(const uint8_t *data, size_t lengt app_attrs, &error); if (!complete || error) { - PBL_LOG(LOG_LEVEL_WARNING, "Error parsing app attributes"); + PBL_LOG_WRN("Error parsing app attributes"); goto fail; } @@ -672,7 +740,7 @@ static void prv_get_app_attributes(const ANCSAttribute *app_id) { kernel_free(request); if (!success) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to fetch app attributes for notification"); + PBL_LOG_WRN("Failed to fetch app attributes for notification"); ANCSAttribute *empty_attrs[NUM_FETCHED_APP_ATTRIBUTES] = {0}; // we failed to fetch the app, but we got a notification prv_put_ancs_message(empty_attrs); @@ -725,7 +793,7 @@ static void prv_handle_notification_attributes_response(const uint8_t *data, siz s_ancs_client->attributes, &error); if (!did_get_attrs || error) { - PBL_LOG(LOG_LEVEL_ERROR, "Error parsing attributes: %u, %u", did_get_attrs, error); + PBL_LOG_ERR("Error parsing attributes: %u, %u", did_get_attrs, error); prv_reset_and_next(); return; } @@ -770,30 +838,15 @@ void ancs_handle_subscribe(BLECharacteristic subscribed_characteristic, WTF; } - static const AnalyticsMetric metric_matrix[2][2] = { - [ANCSCharacteristicNotification] = { - [0] = ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_SUBSCRIBE_COUNT, - [1] = ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_SUBSCRIBE_FAIL_COUNT, - }, - [ANCSCharacteristicData] = { - [0] = ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DS_SUBSCRIBE_COUNT, - [1] = ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DS_SUBSCRIBE_FAIL_COUNT, - } - }; - - const bool no_error = (error == BLEGATTErrorSuccess); - AnalyticsMetric metric = metric_matrix[characteristic_id][no_error ? 0 : 1]; - analytics_inc(metric, AnalyticsClient_System); - - if (no_error) { - PBL_LOG(LOG_LEVEL_INFO, "Hurray! ANCS subscribed: %u", characteristic_id); + if (error == BLEGATTErrorSuccess) { + PBL_LOG_INFO("Hurray! ANCS subscribed: %u", characteristic_id); if (characteristic_id == ANCSCharacteristicData) { prv_ancs_is_alive_start_tracking(); prv_start_temp_notification_connection_delay_timer(); } } else { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to subscribe charx: %u (error=%u)", characteristic_id, error); + PBL_LOG_ERR("Failed to subscribe charx: %u (error=%u)", characteristic_id, error); } } @@ -812,15 +865,14 @@ void ancs_handle_service_removed(BLECharacteristic *characteristics, uint8_t num } void ancs_handle_service_discovered(BLECharacteristic *characteristics) { - BLE_LOG_DEBUG("In ANCS service discovery CB"); + PBL_LOG_DBG("In ANCS service discovery CB"); PBL_ASSERTN(characteristics); // should only be called if we found something! - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DISCOVERED_COUNT, AnalyticsClient_System); // Pause while re-subscribing, it will be resumed when re-subscribed: prv_ancs_is_alive_stop_timer(); if (s_ancs_client->characteristics[0] != BLE_CHARACTERISTIC_INVALID) { - PBL_LOG(LOG_LEVEL_WARNING, "Multiple ANCS services registered?!"); + PBL_LOG_WRN("Multiple ANCS services registered?!"); ancs_invalidate_all_references(); } @@ -828,12 +880,17 @@ void ancs_handle_service_discovered(BLECharacteristic *characteristics) { memcpy(s_ancs_client->characteristics, characteristics, sizeof(BLECharacteristic) * NumANCSCharacteristic); - // Subscribe to Data, then to Notification characteristics: + // Subscribe to Data, then to Notification characteristics. Reject the service + // if subscribing fails instead of asserting (e.g. a "fake ANCS" without CCCD). for (int c = ANCSCharacteristicData; c >= ANCSCharacteristicNotification; --c) { const BTErrno e = gatt_client_subscriptions_subscribe(characteristics[c], BLESubscriptionNotifications, GAPLEClientKernel); - PBL_ASSERTN(e == BTErrnoOK); + if (e != BTErrnoOK) { + PBL_LOG_WRN("Failed to subscribe ANCS charx %d (err=%d), ignoring service", c, e); + ancs_invalidate_all_references(); + return; + } } } @@ -855,24 +912,25 @@ bool ancs_can_handle_characteristic(BLECharacteristic characteristic) { static void prv_handle_ns_notification(uint32_t length, const uint8_t *notification) { PBL_ASSERTN(notification != NULL); - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_COUNT, AnalyticsClient_System); - analytics_add(ANALYTICS_DEVICE_METRIC_NOTIFICATION_BYTE_IN_COUNT, length, AnalyticsClient_System); - if (length != sizeof(NSNotification)) { - PBL_LOG(LOG_LEVEL_ERROR, "Received invalid ANCS NS Notification length=<%"PRIu32">", length); + PBL_LOG_ERR("Received invalid ANCS NS Notification length=<%"PRIu32">", length); return; } + // Any NS traffic (add/modify/remove) proves the subscription is still + // delivering, so the silent-NS resubscribe watchdog can stand down. + s_ancs_client->alive_checks_without_ns = 0; + NSNotification* nsnotification = (NSNotification*) notification; ANCSProperty properties = ANCSProperty_None; - BLE_LOG_VERBOSE("NSNotification: "); - BLE_LOG_VERBOSE("> EventID: %d", nsnotification->event_id); - BLE_LOG_VERBOSE("> EventFlags: <%d>", nsnotification->event_flags); - BLE_LOG_VERBOSE("> CategoryID: <%d>", nsnotification->category_id); - BLE_LOG_VERBOSE("> CategoryCount: <%d>", nsnotification->category_count); - BLE_LOG_VERBOSE("> NotificationUID: <%"PRIu32">", nsnotification->uid); - BLE_HEXDUMP_VERBOSE((uint8_t *)nsnotification, sizeof(NSNotification)); + PBL_LOG_VERBOSE("NSNotification: "); + PBL_LOG_VERBOSE("> EventID: %d", nsnotification->event_id); + PBL_LOG_VERBOSE("> EventFlags: <%d>", nsnotification->event_flags); + PBL_LOG_VERBOSE("> CategoryID: <%d>", nsnotification->category_id); + PBL_LOG_VERBOSE("> CategoryCount: <%d>", nsnotification->category_count); + PBL_LOG_VERBOSE("> NotificationUID: <%"PRIu32">", nsnotification->uid); + PBL_HEXDUMP(LOG_LEVEL_DEBUG_VERBOSE, (uint8_t *)nsnotification, sizeof(NSNotification)); // Handle the CategoryID if (nsnotification->category_id == CategoryIDMissedCall) { @@ -901,22 +959,19 @@ static void prv_handle_ns_notification(uint32_t length, const uint8_t *notificat // we got in the past 2 hours. To get past this ignore notifications for the first couple // seconds after connecting if (s_just_connected && (nsnotification->event_flags & EventFlagPreExisting)) { - BLE_LOG_DEBUG("Ignoring notification because we just connected and PreExisting"); + PBL_LOG_DBG("Ignoring notification because we just connected and PreExisting"); } else { - BLE_LOG_DEBUG("Added ANCS notification!"); + PBL_LOG_DBG("Added ANCS notification!"); prv_notif_queue_push_attr_request(nsnotification->uid, properties); } - // See analytics_external_collect_ancs_info() - s_ns_flags_used_bitset |= nsnotification->event_flags; - break; case EventIDNotificationModified: - BLE_LOG_DEBUG("Modified ANCS notification!"); + PBL_LOG_DBG("Modified ANCS notification!"); prv_notif_queue_push_attr_request(nsnotification->uid, properties); break; case EventIDNotificationRemoved: - BLE_LOG_DEBUG("Removed ANCS notification"); + PBL_LOG_DBG("Removed ANCS notification"); ancs_notifications_handle_notification_removed(nsnotification->uid, properties); break; } @@ -925,15 +980,12 @@ static void prv_handle_ns_notification(uint32_t length, const uint8_t *notificat static void prv_handle_ds_notification(uint32_t length, const uint8_t *data) { PBL_ASSERTN(data != NULL); - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DS_COUNT, AnalyticsClient_System); if (length < 1) { - PBL_LOG(LOG_LEVEL_ERROR, "Received ANCS DS notification of length 0"); + PBL_LOG_ERR("Received ANCS DS notification of length 0"); return; } - analytics_add(ANALYTICS_DEVICE_METRIC_NOTIFICATION_BYTE_IN_COUNT, length, AnalyticsClient_System); - if (s_ancs_client->state == ANCSClientStateRequestedApp) { prv_handle_app_attributes_response(data, length); } else if (s_ancs_client->state == ANCSClientStateRequestedNotification || @@ -945,7 +997,7 @@ static void prv_handle_ds_notification(uint32_t length, const uint8_t *data) { void ancs_handle_read_or_notification(BLECharacteristic characteristic, const uint8_t *value, size_t value_length, BLEGATTError error) { if (error != BLEGATTErrorSuccess) { - PBL_LOG(LOG_LEVEL_ERROR, "Read or notification error: %d", error); + PBL_LOG_ERR("Read or notification error: %d", error); prv_reset_due_to_bt_error(); return; } @@ -981,12 +1033,12 @@ void ancs_handle_write_response(BLECharacteristic characteristic, BLEGATTError e } if (error != BLEGATTErrorSuccess) { - PBL_LOG(LOG_LEVEL_ERROR, "Control point error response: %d", error); + PBL_LOG_ERR("Control point error response: %d", error); prv_reset_due_to_bt_error(); return; } - BLE_LOG_DEBUG("Got ACK for Control Point write"); + PBL_LOG_DBG("Got ACK for Control Point write"); if (s_ancs_client->queue && (s_ancs_client->queue->op == NotificationQueueOpPerformAction)) { // The action was successful @@ -998,11 +1050,11 @@ static bool prv_write_control_point_request(const CPDSMessage *cmd, size_t size) const BLECharacteristic cp = s_ancs_client->characteristics[ANCSCharacteristicControl]; const BTErrno error = gatt_client_op_write(cp, (const uint8_t *) cmd, size, GAPLEClientKernel); - BLE_LOG_DEBUG("Writing to control point:"); + PBL_LOG_DBG("Writing to control point:"); PBL_HEXDUMP(LOG_LEVEL_DEBUG, (const uint8_t *) cmd, size); if (error != BTErrnoOK) { - BLE_LOG_DEBUG("Control point write error: %d", error); + PBL_LOG_DBG("Control point write error: %d", error); return false; } @@ -1020,7 +1072,7 @@ static void prv_perform_action(uint32_t notification_uid, ActionId action_id) { .action_id = action_id, }; - BLE_LOG_DEBUG("Taking action <%u> upon UID: %"PRIu32, action_id, + PBL_LOG_DBG("Taking action <%u> upon UID: %"PRIu32, action_id, notification_uid); const bool success = prv_write_control_point_request((const CPDSMessage *) &action_msg, @@ -1032,7 +1084,7 @@ static void prv_perform_action(uint32_t notification_uid, ActionId action_id) { static void prv_serialize_action(const PerformNotificationActionMsg *action_msg) { if (!s_ancs_client) { - PBL_LOG(LOG_LEVEL_ERROR, "No ANCS client"); + PBL_LOG_ERR("No ANCS client"); return; } @@ -1084,7 +1136,6 @@ void ancs_destroy(void) { if (!s_ancs_client) { return; } - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_CONNECT_TIME); prv_ancs_is_alive_stop_timer(); ancs_app_name_storage_deinit(); @@ -1094,14 +1145,3 @@ void ancs_destroy(void) { s_ancs_client = NULL; prv_put_ancs_disconnected_event(); } - -// ------------------------------------------------------------------------------------------------- -// Analytics - -void analytics_external_collect_ancs_info(void) { - // Keep track of bits that are used by this version of ANCS, we log this to analytics so we get - // an indication of upcoming extensions to ANCS early on: - analytics_set(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_FLAGS_BITSET, - s_ns_flags_used_bitset, AnalyticsClient_System); - s_ns_flags_used_bitset = 0; -} diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs.h b/src/fw/comm/ble/kernel_le_client/ancs/ancs.h index 7da1846fab..bfce372465 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs.h +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.c b/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.c index 4a5b7d9ef7..2f014afe4a 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.c +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ancs_app_name_storage.h" diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.h b/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.h index 83c7d5b5e4..fe627c4ec4 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.h +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs_definition.h b/src/fw/comm/ble/kernel_le_client/ancs/ancs_definition.h index 1b4c873400..19468a8ba5 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs_definition.h +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs_definition.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs_types.h b/src/fw/comm/ble/kernel_le_client/ancs/ancs_types.h index 915db8fd41..49db29dc2a 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs_types.h +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -149,7 +136,10 @@ typedef enum { // FIXME AS: APP ID max length determined by looking through installed apps on iOS. Not sure what actual maximum is #define APP_ID_MAX_LENGTH (60) -#define TITLE_MAX_LENGTH (40) +// Long enough that notification filtering rules can match text near the end +// of verbose titles (e.g. Discord's "Sender (#channel · Server Name)"). +// Must fit FetchedAttribute.max_length (uint8_t). +#define TITLE_MAX_LENGTH (128) #define SUBTITLE_MAX_LENGTH (40) #define MESSAGE_MAX_LENGTH (200) #define MESSAGE_SIZE_MAX_LENGTH (3) diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c b/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c index b22cdb4ae6..bba9f022b1 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c @@ -1,25 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ancs_util.h" #include "ancs_types.h" -#include "comm/ble/ble_log.h" #include "syscall/syscall.h" @@ -74,7 +58,7 @@ bool ancs_util_get_attr_ptrs(const uint8_t* data, const size_t length, const Fet const uint8_t* iter = data; if (length < sizeof(ANCSAttribute)) { - PBL_LOG(LOG_LEVEL_INFO, "ANCS data length is too small. Length: %d, sizeof(ANCSAttribute): %d", + PBL_LOG_INFO("ANCS data length is too small. Length: %d, sizeof(ANCSAttribute): %d", (int)length, (int)sizeof(ANCSAttribute)); *out_error = true; return false; @@ -97,7 +81,7 @@ bool ancs_util_get_attr_ptrs(const uint8_t* data, const size_t length, const Fet // Check that attribute length is valid bool attr_length_invalid = (attr_list[i].max_length != 0) && (attr->length > attr_list[i].max_length); if (attr_length_invalid) { - PBL_LOG(LOG_LEVEL_INFO, "Length of ANCS attribute %d is invalid: length: %d, max_length: %d", + PBL_LOG_INFO("Length of ANCS attribute %d is invalid: length: %d, max_length: %d", attr->id, attr->length, attr_list[i].max_length); *out_error = true; return false; @@ -113,7 +97,7 @@ bool ancs_util_get_attr_ptrs(const uint8_t* data, const size_t length, const Fet if (!is_found) { // The attribute was unexpected, the dictionary is malformed - PBL_LOG(LOG_LEVEL_INFO, "Unexpected ANCS attribute. ID = %d. The dictionary is malformed", + PBL_LOG_INFO("Unexpected ANCS attribute. ID = %d. The dictionary is malformed", attr->id); *out_error = true; return false; diff --git a/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.h b/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.h index c542e44974..f7149aa1f9 100644 --- a/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.h +++ b/src/fw/comm/ble/kernel_le_client/ancs/ancs_util.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "ancs_types.h" -#include "services/normal/notifications/notifications.h" +#include "pbl/services/notifications/notifications.h" #include diff --git a/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.c b/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.c index 7546ad43bb..2dc45abc75 100644 --- a/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.c +++ b/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.c @@ -1,27 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_launch.h" #include "comm/ble/gatt_client_operations.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" #include "system/logging.h" #include "system/passert.h" @@ -38,7 +22,7 @@ void app_launch_handle_service_discovered(BLECharacteristic *characteristics) { PBL_ASSERTN(characteristics); if (s_app_launch_characteristic != BLE_CHARACTERISTIC_INVALID) { - PBL_LOG(LOG_LEVEL_WARNING, "Multiple app launch services!? Will use most recent one."); + PBL_LOG_WRN("Multiple app launch services!? Will use most recent one."); } s_app_launch_characteristic = *characteristics; @@ -69,12 +53,9 @@ bool app_launch_can_handle_characteristic(BLECharacteristic characteristic) { void app_launch_handle_read_or_notification(BLECharacteristic characteristic, const uint8_t *value, size_t value_length, BLEGATTError error) { // If error is BLEGATTErrorSuccess, it means the Pebble app responded. - PBL_LOG(LOG_LEVEL_INFO, "App relaunch result: %u", error); + PBL_LOG_INFO("App relaunch result: %u", error); if (error == BLEGATTErrorSuccess) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BT_PEBBLE_APP_LAUNCH_SUCCESS_COUNT, - AnalyticsClient_System); } else { - analytics_event_bt_app_launch_error(error); } } @@ -92,6 +73,6 @@ void app_launch_trigger(void) { } BTErrno err = gatt_client_op_read(s_app_launch_characteristic, GAPLEClientKernel); if (err != BTErrnoOK) { - PBL_LOG(LOG_LEVEL_ERROR, "App relaunch failed: %u", err); + PBL_LOG_ERR("App relaunch failed: %u", err); } } diff --git a/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.h b/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.h index 913880555a..ad8c640bfd 100644 --- a/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.h +++ b/src/fw/comm/ble/kernel_le_client/app_launch/app_launch.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/app_launch/app_launch_definition.h b/src/fw/comm/ble/kernel_le_client/app_launch/app_launch_definition.h index cb7025999d..e7c98f24d8 100644 --- a/src/fw/comm/ble/kernel_le_client/app_launch/app_launch_definition.h +++ b/src/fw/comm/ble/kernel_le_client/app_launch/app_launch_definition.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/dis/dis.c b/src/fw/comm/ble/kernel_le_client/dis/dis.c index 51b39f1014..52f92e0eef 100644 --- a/src/fw/comm/ble/kernel_le_client/dis/dis.c +++ b/src/fw/comm/ble/kernel_le_client/dis/dis.c @@ -1,28 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dis.h" -#include "comm/ble/ble_log.h" #include "comm/ble/gap_le_connection.h" #include "comm/ble/kernel_le_client/ancs/ancs.h" #include "comm/bt_lock.h" #include "system/logging.h" #include "system/passert.h" +PBL_LOG_MODULE_DECLARE(bt, CONFIG_BT_LOG_LEVEL); + // ------------------------------------------------------------------------------------------------- // Interface towards kernel_le_client.c @@ -35,7 +23,7 @@ void dis_handle_service_removed(BLECharacteristic *characteristics, uint8_t num_ } void dis_handle_service_discovered(BLECharacteristic *characteristics) { - BLE_LOG_DEBUG("In DIS service discovery CB"); + PBL_LOG_DBG("In DIS service discovery CB"); PBL_ASSERTN(characteristics); ancs_handle_ios9_or_newer_detected(); diff --git a/src/fw/comm/ble/kernel_le_client/dis/dis.h b/src/fw/comm/ble/kernel_le_client/dis/dis.h index 274477ebe7..683c616dd7 100644 --- a/src/fw/comm/ble/kernel_le_client/dis/dis.h +++ b/src/fw/comm/ble/kernel_le_client/dis/dis.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/dis/dis_definition.h b/src/fw/comm/ble/kernel_le_client/dis/dis_definition.h index aad5470e79..9564047dc5 100644 --- a/src/fw/comm/ble/kernel_le_client/dis/dis_definition.h +++ b/src/fw/comm/ble/kernel_le_client/dis/dis_definition.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/kernel_le_client.c b/src/fw/comm/ble/kernel_le_client/kernel_le_client.c index 22889e7d63..609f21a72a 100644 --- a/src/fw/comm/ble/kernel_le_client/kernel_le_client.c +++ b/src/fw/comm/ble/kernel_le_client/kernel_le_client.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel_le_client.h" @@ -45,9 +32,7 @@ #include "comm/ble/gatt_client_operations.h" #include "comm/ble/gatt_client_subscriptions.h" -#include #include -#include #define MAX_SERVICE_INSTANCES (8) @@ -182,51 +167,28 @@ static const KernelLEClient s_clients[KernelLEClientNum] = { #endif // UNITTEST }; -#if 0 // TODO: PBL-21864 - Disconnect BT Classic when PPoGATT is used -// Disconnect BT Classic (for iAP) if connected and make this LE device the active gateway, -// to prevent that iAP gets reconnected in the future: -static void prv_set_active_gateway_and_disconn_bt_classic(const BTDeviceInternal *gateway_device) { - BTBondingID bonding_id = BT_BONDING_ID_INVALID; - bt_lock(); - // Find the Bonding ID for the LE connection that supports PPoGATT: - GAPLEConnection *connection = gap_le_connection_by_device(gateway_device); - // It's possible the connection is gone in the mean time; this runs on KernelMain. - if (connection) { - bonding_id = connection->bonding_id; - } - bt_unlock(); - - // don't hold bt_lock while calling bt_persistent_storage_... because it accesses flash - if (bonding_id != BT_BONDING_ID_INVALID) { - bt_persistent_storage_set_active_gateway(bonding_id); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Not bonded or disconnected (%p)", connection); - } - - bt_lock(); - bt_driver_classic_disconnect(NULL); - bt_unlock(); -} -#endif - static void prv_handle_services_removed(PebbleBLEGATTClientServicesRemoved *services_removed) { PebbleBLEGATTClientServiceHandles *service_remove_info = &services_removed->handles[0]; for (int s = 0; s < services_removed->num_services_removed; s++) { +#ifndef CONFIG_RELEASE bool removed = false; +#endif for (int c = 0; c < KernelLEClientNum; c++) { const KernelLEClient * const client = &s_clients[c]; if (uuid_equal(&service_remove_info->uuid, client->service_uuid)) { +#ifndef CONFIG_RELEASE removed = true; +#endif client->handle_service_removed( (BLECharacteristic *)&service_remove_info->char_and_desc_handles[0], service_remove_info->num_characteristics); } } -#if !RELEASE +#ifndef CONFIG_RELEASE char uuid_string[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(&service_remove_info->uuid, uuid_string); - PBL_LOG(LOG_LEVEL_INFO, "%s removed: %d", uuid_string, (int)removed); + PBL_LOG_INFO("%s removed: %d", uuid_string, (int)removed); #endif int num_hdls = service_remove_info->num_descriptors + service_remove_info->num_characteristics; @@ -266,26 +228,21 @@ static void prv_handle_services_added( client->num_characteristics); if (num_characteristics != client->num_characteristics) { - PBL_LOG(LOG_LEVEL_ERROR, "Found %s, but only %u characteristics...", + PBL_LOG_ERR("Found %s, but only %u characteristics...", client->debug_name, num_characteristics); continue; } -#if 0 // TODO: PBL-21864 - Disconnect BT Classic when PPoGATT is used - if (c == KernelLEClientPPoGATT) { - prv_set_active_gateway_and_disconn_bt_classic(&device); - } -#endif #if !UNITTEST ATTHandleRange range = { }; gatt_client_service_get_handle_range(added_services->services[s], &range); if (c == KernelLEClientPPoGATT) { // We are trying to track down an issue on iOS where PPoGATT doesn't get opened (PBL-40084) // This message should help us determine if iOS is publishing the service - PBL_LOG(LOG_LEVEL_INFO, "Found an instance of %s at 0x%"PRIx16"-0x%"PRIx16"!", + PBL_LOG_INFO("Found an instance of %s at 0x%"PRIx16"-0x%"PRIx16"!", client->debug_name, range.start, range.end); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Found an instance of %s at 0x%"PRIx16"-0x%"PRIx16"!", + PBL_LOG_DBG("Found an instance of %s at 0x%"PRIx16"-0x%"PRIx16"!", client->debug_name, range.start, range.end); } #endif @@ -309,7 +266,7 @@ static void prv_handle_gatt_service_discovery_event(const PebbleBLEGATTClientSer if (event_info->type != PebbleServicesRemoved) { // For removals, we log info in the handler routine - PBL_LOG(LOG_LEVEL_INFO, "Service changed Indication: type: %d status: %d", + PBL_LOG_INFO("Service changed Indication: type: %d status: %d", event_info->type, event_info->status); } @@ -354,7 +311,7 @@ static void prv_consume_read_response(const PebbleBLEGATTClientEvent *event, // TODO: https://pebbletechnology.atlassian.net/browse/PBL-14164 buffer = (uint8_t *) kernel_malloc(value_length); if (UNLIKELY(!buffer)) { - PBL_LOG(LOG_LEVEL_ERROR, "OOM for GATT read response - %d bytes", (int)value_length); + PBL_LOG_ERR("OOM for GATT read response - %d bytes", (int)value_length); return; } gatt_client_consume_read_response(event->object_ref, @@ -389,7 +346,7 @@ static void prv_consume_notifications(const PebbleBLEGATTClientEvent *event) { // TODO: https://pebbletechnology.atlassian.net/browse/PBL-14164 uint8_t *buffer = (uint8_t *) kernel_malloc(header.value_length); if (UNLIKELY(header.value_length && !buffer)) { - PBL_LOG(LOG_LEVEL_ERROR, "OOM for GATT notification"); + PBL_LOG_ERR("OOM for GATT notification"); return; } @@ -403,7 +360,7 @@ static void prv_consume_notifications(const PebbleBLEGATTClientEvent *event) { client->handle_read_or_notification(header.characteristic, buffer, header.value_length, BLEGATTErrorSuccess); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "No client to handle GATT notification from characteristic %p", + PBL_LOG_DBG("No client to handle GATT notification from characteristic %p", (void*) header.characteristic); } kernel_free(buffer); @@ -458,8 +415,7 @@ static void prv_handle_gatt_event(const PebbleBLEGATTClientEvent *event) { } log_error: - PBL_LOG(LOG_LEVEL_ERROR, - "Unhandled GATT event:%u ref:%"PRIu32" err:%"PRIu16" len:%"PRIu16" cl:%p", + PBL_LOG_ERR("Unhandled GATT event:%u ref:%"PRIu32" err:%"PRIu16" len:%"PRIu16" cl:%p", event->subtype, (uint32_t)event->object_ref, (uint16_t)event->gatt_error, @@ -468,7 +424,7 @@ static void prv_handle_gatt_event(const PebbleBLEGATTClientEvent *event) { } static void prv_handle_connection_event(const PebbleBLEConnectionEvent *event) { - PBL_LOG(LOG_LEVEL_DEBUG, "PEBBLE_BLE_CONNECTION_EVENT: reason=0x%x, conn=%u, bond=%u", + PBL_LOG_DBG("PEBBLE_BLE_CONNECTION_EVENT: reason=0x%x, conn=%u, bond=%u", event->hci_reason, event->connected, event->bonding_id); const bool connected = event->connected; @@ -482,7 +438,7 @@ static void prv_handle_connection_event(const PebbleBLEConnectionEvent *event) { const BTDeviceInternal device = PebbleEventToBTDeviceInternal(event); if (connected) { - PBL_LOG(LOG_LEVEL_DEBUG, "Connected to Gateway!"); + PBL_LOG_DBG("Connected to Gateway!"); ancs_create(); ams_create(); @@ -491,15 +447,8 @@ static void prv_handle_connection_event(const PebbleBLEConnectionEvent *event) { gap_le_slave_reconnect_stop(); gatt_client_discovery_discover_all(&device); - const bool gateway_is_classic_paired = true; // TODO - if (gateway_is_classic_paired) { - // [MT] Kick reconnection for BT Classic when BLE comes up. - // If BLE is able to reconnect, chances are BT Classic is able too, so try - // immediately instead of waiting for reconnect.c's timer to fire. - bt_driver_reconnect_try_now(false /*ignore_paused*/); - } } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Disconnected from Gateway!"); + PBL_LOG_DBG("Disconnected from Gateway!"); ppogatt_destroy(); ams_destroy(); ancs_destroy(); @@ -513,7 +462,7 @@ static void prv_handle_connection_event(const PebbleBLEConnectionEvent *event) { void kernel_le_client_handle_event(const PebbleEvent *e) { switch (e->type) { case PEBBLE_BLE_SCAN_EVENT: - PBL_LOG(LOG_LEVEL_DEBUG, "PEBBLE_BLE_SCAN_EVENT"); + PBL_LOG_DBG("PEBBLE_BLE_SCAN_EVENT"); return; case PEBBLE_BLE_CONNECTION_EVENT: diff --git a/src/fw/comm/ble/kernel_le_client/kernel_le_client.h b/src/fw/comm/ble/kernel_le_client/kernel_le_client.h index 7a305ce446..598869414c 100644 --- a/src/fw/comm/ble/kernel_le_client/kernel_le_client.h +++ b/src/fw/comm/ble/kernel_le_client/kernel_le_client.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" //! @file kernel_le_client.h //! Module that is responsible of connecting to the BLE gateway (aka "the phone") in order to: diff --git a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.c b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.c index a8abf71418..5256332bd7 100644 --- a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.c +++ b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.c @@ -1,20 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ppogatt.h" #include "ppogatt_internal.h" @@ -23,12 +8,14 @@ #include "comm/ble/gatt_client_operations.h" #include "comm/bt_lock.h" +#include + #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session_transport.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session_transport.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" #include "system/hexdump.h" #include "system/logging.h" @@ -39,11 +26,14 @@ #include +#include "drivers/rtc.h" + //! See https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=22511665 //! for detailed information regarding the PPoGATT protocol state machine. typedef enum { StateDisconnectedReadingMeta, + StateDisconnectedAwaitingMetaRetry, StateDisconnectedSubscribingData, // StateConnectedClosedAwaitingResetRequest, // Server-only state StateConnectedClosedAwaitingResetCompleteSelfInitiatedReset, @@ -125,6 +115,9 @@ typedef struct PPoGATTClient { //! Number of consecutive resets so far uint8_t resets_counter; + //! Number of meta read retry attempts + uint8_t meta_read_retries; + bool disconnect_requested; //! True if the client requested a disconnect TimerID rx_ack_timer; //! Timer to ensure Acks for data are dispatched regularly @@ -136,6 +129,9 @@ typedef struct PPoGATTClient { //! @note Each PPoGATT client (transport) is responsible for managing the CommSession's lifecycle, //! by calling comm_session_open / comm_session_close at the appropriate times. CommSession *session; + + //! Tick count when this client was created, for measuring handshake duration + RtcTicks created_ticks; } PPoGATTClient; // ------------------------------------------------------------------------------------------------- @@ -151,17 +147,29 @@ static uint8_t s_timer_ticks; static uint8_t s_disconnect_counter; +//! Caps rediscovery on stale meta handles to once per BLE connection. +static bool s_rediscovery_requested_this_connection; + // ------------------------------------------------------------------------------------------------- // Function Prototypes static void prv_send_next_packets(PPoGATTClient *client); static void prv_start_reset(PPoGATTClient *client); +static void prv_request_meta_rediscovery(PPoGATTClient *client); + +extern BTErrno gatt_client_discovery_rediscover_all(const BTDeviceInternal *device); //! Gets the GAPLEConnection associated with the characteristic reference. //! @return The connection or NULL in case it could not be found. //! @note The caller MUST own bt_lock() extern GAPLEConnection *gatt_client_characteristic_get_connection(BLECharacteristic characteristic); +//! Gets the att_handle and connection for a characteristic. +//! @return The att_handle or 0 if not found. Connection is returned via out parameter. +//! @note The caller MUST own bt_lock() +extern uint16_t gatt_client_characteristic_get_handle_and_connection( + BLECharacteristic characteristic_ref, GAPLEConnection **connection_out); + // ------------------------------------------------------------------------------------------------- void ppogatt_reset_disconnect_counter(void) { @@ -285,12 +293,12 @@ static void prv_reset_ack_timeout(PPoGATTClient *client) { static void prv_roll_back(PPoGATTClient *client, uint32_t sn) { if (++client->out.timeouts_counter >= PPOGATT_TIMEOUT_COUNT_MAX) { - PBL_LOG(LOG_LEVEL_ERROR, "Resetting because max timeouts reached..."); + PBL_LOG_ERR("Resetting because max timeouts reached..."); prv_start_reset(client); return; } - PBL_LOG(LOG_LEVEL_WARNING, "Rolling back from (%u, %u) to %"PRIu32, + PBL_LOG_WRN("Rolling back from (%u, %u) to %"PRIu32, client->out.next_data_sn, client->out.next_expected_ack_sn, sn); // Go back and send again: @@ -320,7 +328,7 @@ static void prv_check_timeouts(PPoGATTClient *client) { client->state == StateConnectedClosedAwaitingResetCompleteRemoteInitiatedReset) { if (prv_has_timeout(client)) { // We've timed out waiting for a reset to be completed, start over: - PBL_LOG(LOG_LEVEL_INFO, "Timed out waiting for Reset Complete, Resetting again..."); + PBL_LOG_INFO("Timed out waiting for Reset Complete, Resetting again..."); prv_start_reset(client); } return; @@ -352,14 +360,15 @@ static void prv_timer_callback(void *unused) { // ------------------------------------------------------------------------------------------------- -static PPoGATTClient *prv_create_client(void) { +static PPoGATTClient *prv_create_client(TimerID timer) { PPoGATTClient *client = kernel_malloc(sizeof(PPoGATTClient)); if (!client) { return NULL; } *client = (PPoGATTClient){}; client->app_uuid = UUID_INVALID; - client->rx_ack_timer = new_timer_create(); + client->rx_ack_timer = timer; + client->created_ticks = rtc_get_ticks(); s_ppogatt_head = (PPoGATTClient *) list_prepend((ListNode *)s_ppogatt_head, &client->node); if (!regular_timer_is_scheduled(&s_ack_timer)) { s_ack_timer.cb = prv_timer_callback; @@ -371,10 +380,16 @@ static PPoGATTClient *prv_create_client(void) { // ------------------------------------------------------------------------------------------------- static void prv_delete_client(PPoGATTClient *client, bool is_disconnected, DeleteReason reason) { + const uint32_t elapsed_ms = (rtc_get_ticks() - client->created_ticks) * 1000 / RTC_TICKS_HZ; + PBL_LOG_INFO("PPoGATT client deleted: state=%u reason=%u disconnected=%u after %"PRIu32"ms", + client->state, reason, is_disconnected, elapsed_ms); // Unsubscribe from Data characteristic: if (client->state > StateDisconnectedSubscribingData && !is_disconnected) { - gatt_client_subscriptions_subscribe(client->characteristics.data, - BLESubscriptionNone, GAPLEClientKernel); + BLECharacteristic data_char = client->characteristics.data; + // Release bt_lock before calling into NimBLE to avoid deadlock with ble_hs_mutex. + bt_unlock(); + gatt_client_subscriptions_subscribe(data_char, BLESubscriptionNone, GAPLEClientKernel); + bt_lock(); } if (client->state == StateConnectedOpen) { @@ -518,14 +533,13 @@ static void prv_start_reset(PPoGATTClient *client) { // If we have disconnected too many times, do not disconnect and leave the client in a // "stalled" state, so that we have the option to "unstall" by sending a remote reset client->state = StateConnectedClosedAwaitingResetCompleteSelfInitiatedResetStalled; - PBL_LOG(LOG_LEVEL_WARNING, "Reset request stalled, not disconnecting"); + PBL_LOG_WRN("Reset request stalled, not disconnecting"); return; } - PBL_LOG(LOG_LEVEL_ERROR, "Disconnecting because max resets reached..."); + PBL_LOG_ERR("Disconnecting because max resets reached..."); // Record the time of this disconnect request - analytics_event_PPoGATT_disconnect(rtc_get_time(), false); bt_lock(); const BLECharacteristic characteristic = client->characteristics.meta; @@ -545,13 +559,13 @@ static void prv_start_reset(PPoGATTClient *client) { static void prv_handle_reset_request(PPoGATTClient *client) { if (client->state == StateConnectedClosedAwaitingResetCompleteSelfInitiatedReset) { // Already in self-initated reset procedure, client should ignore the request from the server. - PBL_LOG(LOG_LEVEL_INFO, "Ignoring reset request because local client already requested."); + PBL_LOG_INFO("Ignoring reset request because local client already requested."); return; } if (client->state == StateConnectedClosedAwaitingResetCompleteRemoteInitiatedReset) { // Already in remote-initiated reset procedure, server retrying? // See https://pebbletechnology.atlassian.net/browse/PBL-12424 - PBL_LOG(LOG_LEVEL_INFO, "Ignoring reset request because remote server already requested."); + PBL_LOG_INFO("Ignoring reset request because remote server already requested."); return; } prv_enter_awaiting_reset_complete(client, false /* self_initiated */); @@ -571,7 +585,6 @@ static void prv_handle_reset_complete(PPoGATTClient *client, const PPoGATTPacket // Possibly successful disconnect? if (s_disconnect_counter) { - analytics_event_PPoGATT_disconnect(rtc_get_time(), true); } ppogatt_reset_disconnect_counter(); client->resets_counter = 0; @@ -589,14 +602,14 @@ static void prv_handle_reset_complete(PPoGATTClient *client, const PPoGATTPacket if (prv_client_supports_enhanced_throughput_features(client)) { if (payload_length < sizeof(PPoGATTResetCompleteClientIDPayloadV1)) { - PBL_LOG(LOG_LEVEL_WARNING, "Unexpected PPoGatt Reset Complete Payload Size: %"PRIu16, + PBL_LOG_WRN("Unexpected PPoGatt Reset Complete Payload Size: %"PRIu16, payload_length); // Be defensive, and use the original window size client->out.tx_window_size = client->out.rx_window_size = PPOGATT_V0_WINDOW_SIZE; } else { PPoGATTResetCompleteClientIDPayloadV1 *payload = (PPoGATTResetCompleteClientIDPayloadV1 *)&packet->payload[0]; - PBL_LOG(LOG_LEVEL_DEBUG, "PPoGATT Remote RxWindow: %d TxWindow %d", + PBL_LOG_DBG("PPoGATT Remote RxWindow: %d TxWindow %d", payload->ppogatt_max_rx_window, payload->ppogatt_max_tx_window); client->out.tx_window_size = MIN(client->out.tx_window_size, payload->ppogatt_max_rx_window); @@ -604,8 +617,12 @@ static void prv_handle_reset_complete(PPoGATTClient *client, const PPoGATTPacket } } - PBL_LOG(LOG_LEVEL_DEBUG, "Hurray! PPoGATT Session is opened (Vers: %d TXW: %d RXW: %d)!", - client->version, client->out.tx_window_size, client->out.rx_window_size); + { + const uint32_t elapsed_ms = + (rtc_get_ticks() - client->created_ticks) * 1000 / RTC_TICKS_HZ; + PBL_LOG_INFO("Hurray! PPoGATT Session is opened (Vers: %d TXW: %d RXW: %d) after %"PRIu32"ms!", + client->version, client->out.tx_window_size, client->out.rx_window_size, elapsed_ms); + } } // ------------------------------------------------------------------------------------------------- @@ -643,9 +660,9 @@ static void prv_handle_ack(PPoGATTClient *client, uint32_t sn) { // Don't roll back directly to avoid creating an Sorcerer's Apprentice bug // https://en.wikipedia.org/wiki/Sorcerer%27s_Apprentice_Syndrome // We'll rely on the ACK timeout for the next data packet to fire and roll back. - PBL_LOG(LOG_LEVEL_WARNING, "Received retransmitted Ack for sn:%"PRIu32". Ignoring it.", sn); + PBL_LOG_WRN("Received retransmitted Ack for sn:%"PRIu32". Ignoring it.", sn); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Ack'd packet out of range %"PRIu32", [%u-%u].", + PBL_LOG_ERR("Ack'd packet out of range %"PRIu32", [%u-%u].", sn, client->out.next_expected_ack_sn, client->out.next_data_sn); prv_start_reset(client); } @@ -664,9 +681,9 @@ static void prv_handle_data(PPoGATTClient *client, client->in.next_expected_data_sn = prv_next_sn(client->in.next_expected_data_sn); comm_session_receive_router_write(client->session, packet->payload, payload_length); -// PBL_LOG(LOG_LEVEL_DEBUG, "Got PP data (sn=%u, %u bytes)", packet->sn, payload_length); +// PBL_LOG_DBG("Got PP data (sn=%u, %u bytes)", packet->sn, payload_length); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "packet->sn != next_expected_data_sn (%u != %u)", + PBL_LOG_DBG("packet->sn != next_expected_data_sn (%u != %u)", packet->sn, client->in.next_expected_data_sn); // Rely on the server retransmitting on Ack time-out } @@ -676,20 +693,20 @@ static void prv_handle_data(PPoGATTClient *client, static void prv_handle_data_notification(PPoGATTClient *client, const uint8_t *value, uint16_t value_length) { -// PBL_LOG(LOG_LEVEL_DEBUG, "IN:"); +// PBL_LOG_DBG("IN:"); // PBL_HEXDUMP(LOG_LEVEL_DEBUG, value, value_length); if (UNLIKELY(value_length == 0)) { - PBL_LOG(LOG_LEVEL_ERROR, "Zero length packet"); + PBL_LOG_ERR("Zero length packet"); return; } const PPoGATTPacket *packet = (const PPoGATTPacket *) value; if (UNLIKELY(packet->type >= PPoGATTPacketTypeInvalidRangeStart)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid type %u", packet->type); + PBL_LOG_ERR("Invalid type %u", packet->type); return; } if (UNLIKELY(packet->type) == PPoGATTPacketTypeResetRequest) { - PBL_LOG(LOG_LEVEL_INFO, "Got reset request!"); + PBL_LOG_INFO("Got reset request!"); prv_handle_reset_request(client); return; } @@ -699,7 +716,7 @@ static void prv_handle_data_notification(PPoGATTClient *client, } else if (LIKELY(packet->type == PPoGATTPacketTypeAck)) { prv_handle_ack(client, packet->sn); } else if (UNLIKELY(packet->type == PPoGATTPacketTypeResetComplete)) { - PBL_LOG(LOG_LEVEL_ERROR, "Got reset complete while open!?"); + PBL_LOG_ERR("Got reset complete while open!?"); } } else if (client->state == StateConnectedClosedAwaitingResetCompleteSelfInitiatedReset || client->state == StateConnectedClosedAwaitingResetCompleteSelfInitiatedResetStalled || @@ -707,21 +724,90 @@ static void prv_handle_data_notification(PPoGATTClient *client, if (LIKELY(packet->type == PPoGATTPacketTypeResetComplete)) { prv_handle_reset_complete(client, packet, value_length - sizeof(PPoGATTPacket)); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Resetting, ignoring data/ack packets (%u)", packet->type); + PBL_LOG_DBG("Resetting, ignoring data/ack packets (%u)", packet->type); } } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring all packets in state %u", client->state); + PBL_LOG_DBG("Ignoring all packets in state %u", client->state); } } // ------------------------------------------------------------------------------------------------- +static void prv_rediscover_kernelbg_cb(void *data) { + BTDeviceInternal *device = (BTDeviceInternal *)data; + const BTErrno e = gatt_client_discovery_rediscover_all(device); + kernel_free(device); + if (e != BTErrnoOK) { + PBL_LOG_ERR("PPoGATT couldn't restart discovery: %d", (int)e); + } +} + +static void prv_request_meta_rediscovery(PPoGATTClient *client) { + bt_lock_assert_held(true); + + GAPLEConnection *connection = + gatt_client_characteristic_get_connection(client->characteristics.meta); + if (!connection) { + return; + } + + BTDeviceInternal *device = kernel_malloc(sizeof(BTDeviceInternal)); + if (!device) { + return; + } + *device = connection->device; + system_task_add_callback(prv_rediscover_kernelbg_cb, device); +} + +static void prv_retry_meta_read(PPoGATTClient *client) { + bt_lock_assert_held(true); + + if (!client || client->state != StateDisconnectedAwaitingMetaRetry) { + return; + } + + PBL_LOG_INFO("Retrying PPoGATT meta read (attempt %u/%u)", + client->meta_read_retries + 1, PPOGATT_META_READ_RETRY_COUNT_MAX); + + client->state = StateDisconnectedReadingMeta; + BLECharacteristic meta = client->characteristics.meta; + + // Release bt_lock before calling gatt_client_op_read to avoid recursive lock deadlock. + // gatt_client_op_read manages bt_lock internally. + bt_unlock(); + + BTErrno result = gatt_client_op_read(meta, GAPLEClientKernel); + + // Re-acquire bt_lock before accessing client state again + bt_lock(); + + if (result != BTErrnoOK) { + // Read failed to start, delete the client + PBL_LOG_ERR("Failed to initiate meta read retry"); + prv_delete_client(client, false /* is_disconnected */, DeleteReason_MetaDataReadFailure); + } +} + +static void prv_meta_read_retry_timer_cb(void *data) { + PPoGATTClient *client = (PPoGATTClient *)data; + bt_lock(); + prv_retry_meta_read(client); + bt_unlock(); +} + +// ------------------------------------------------------------------------------------------------- + static void prv_handle_meta_read(PPoGATTClient *client, const uint8_t *value, size_t value_length, BLEGATTError error) { + const uint32_t elapsed_ms = (rtc_get_ticks() - client->created_ticks) * 1000 / RTC_TICKS_HZ; PBL_ASSERTN(client->state == StateDisconnectedReadingMeta); if (error != BLEGATTErrorSuccess) { - goto handle_error; + PBL_LOG_ERR("PPoGATT meta read failed: err=0x%x after %"PRIu32"ms (retry %u/%u)", + error, elapsed_ms, client->meta_read_retries, PPOGATT_META_READ_RETRY_COUNT_MAX); + // GATT read failed - this is retriable since the mobile app may not be ready yet + goto handle_retriable_error; } + PBL_LOG_INFO("PPoGATT meta read success after %"PRIu32"ms, len=%zu", elapsed_ms, value_length); if (value_length < sizeof(PPoGATTMetaV0)) { goto handle_error; } @@ -731,12 +817,12 @@ static void prv_handle_meta_read(PPoGATTClient *client, const uint8_t *value, goto handle_error; } if (uuid_is_invalid(&meta->app_uuid)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid UUID"); + PBL_LOG_ERR("Invalid UUID"); goto handle_error; } -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW if (!uuid_is_system(&meta->app_uuid)) { - PBL_LOG(LOG_LEVEL_ERROR, "Found PPoGATT server from non-Pebble app, not connecting in PRF.."); + PBL_LOG_ERR("Found PPoGATT server from non-Pebble app, not connecting in PRF.."); goto handle_error; } #endif @@ -749,29 +835,52 @@ static void prv_handle_meta_read(PPoGATTClient *client, const uint8_t *value, if (/*meta->ppogatt_max_version >= 0x01 &&*/ value_length >= sizeof(PPoGATTMetaV1)) { const PPoGATTMetaV1 *meta_v1 = (const PPoGATTMetaV1 *)value; if (meta_v1->pp_session_type >= PPoGATTSessionTypeCount) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid session type %u", meta_v1->pp_session_type); + PBL_LOG_ERR("Invalid session type %u", meta_v1->pp_session_type); goto handle_error; } session_type = meta_v1->pp_session_type; } - BTErrno e = gatt_client_subscriptions_subscribe(client->characteristics.data, + // Save data needed across the bt_lock release + BLECharacteristic data_char = client->characteristics.data; + Uuid app_uuid = meta->app_uuid; + + // Release bt_lock before calling gatt_client_subscriptions_subscribe, which + // eventually calls into NimBLE. Holding bt_lock when calling into NimBLE + // creates a lock ordering deadlock with ble_hs_mutex (NimBLE store callbacks + // acquire bt_lock while holding ble_hs_mutex). + // See prv_retry_meta_read for the same pattern. + bt_unlock(); + + BTErrno e = gatt_client_subscriptions_subscribe(data_char, BLESubscriptionNotifications, GAPLEClientKernel); + + // Re-acquire bt_lock before accessing client state + bt_lock(); + + // Re-validate client pointer after releasing lock, as it may have been removed + bool is_data_unused; + client = prv_find_client_with_characteristic(data_char, &is_data_unused); + if (!client) { + return; + } + if (e == BTErrnoOK) { // Delete any existing client with this UUID, last one wins. // iOS behavior is a bit strange when it comes to service persistence. When an app crashes or // gets killed through Xcode, the service records persist. When the app is relaunched again, // a new service will get added again. The old one remains when it was killed through Xcode // before. The old one seems to go away *after* the new one gets added in the crash scenario. - PPoGATTClient *existing_client = prv_find_client_with_uuid(&meta->app_uuid); + PPoGATTClient *existing_client = prv_find_client_with_uuid(&app_uuid); if (existing_client) { - PBL_LOG(LOG_LEVEL_ERROR, - "Found PPoGATT server with same UUID. Keeping only the last one."); + PBL_LOG_ERR("Found PPoGATT server with same UUID. Keeping only the last one."); prv_delete_client(existing_client, true /* is_disconnected */, DeleteReason_DuplicateServer); } client->state = StateDisconnectedSubscribingData; - client->app_uuid = meta->app_uuid; + client->app_uuid = app_uuid; + // Reset retry counter on success + client->meta_read_retries = 0; if (session_type == PPoGATTSessionType_Hybrid) { client->destination = TransportDestinationHybrid; @@ -782,8 +891,28 @@ static void prv_handle_meta_read(PPoGATTClient *client, const uint8_t *value, return; } +handle_retriable_error: + // GATT read failed - schedule a retry if we haven't exceeded the max retry count + if (++client->meta_read_retries < PPOGATT_META_READ_RETRY_COUNT_MAX) { + PBL_LOG_WRN("PPoGATT meta read failed (err=%x), scheduling retry %u/%u", + error, client->meta_read_retries, PPOGATT_META_READ_RETRY_COUNT_MAX); + client->state = StateDisconnectedAwaitingMetaRetry; + new_timer_start(client->rx_ack_timer, PPOGATT_META_READ_RETRY_DELAY_MS, + prv_meta_read_retry_timer_cb, client, 0); + return; + } + PBL_LOG_ERR("PPoGATT meta read failed after %u retries", + PPOGATT_META_READ_RETRY_COUNT_MAX); + + // Cached handle may be stale; try once with fresh discovery. + if (!s_rediscovery_requested_this_connection) { + PBL_LOG_INFO("Triggering GATT rediscovery for fresh PPoGATT handles"); + s_rediscovery_requested_this_connection = true; + prv_request_meta_rediscovery(client); + } + handle_error: - PBL_LOG(LOG_LEVEL_ERROR, "Failed handling PPoGATT meta: len=%u ver=%x err=%x", + PBL_LOG_ERR("Failed handling PPoGATT meta: len=%u ver=%x err=%x", (unsigned int) value_length, value_length ? value[0] : ~0, error); prv_delete_client(client, false /* is_disconnected */, DeleteReason_MetaDataInvalid); } @@ -797,6 +926,7 @@ void ppogatt_create(void) { PBL_ASSERT_TASK(PebbleTask_KernelMain); PBL_ASSERTN(!s_ppogatt_head); s_timer_ticks = 0; + s_rediscovery_requested_this_connection = false; } bt_unlock(); } @@ -840,7 +970,7 @@ void ppogatt_handle_service_removed( BLECharacteristic char1 = num_characteristics > 0 ? characteristics[0] : 0; BLECharacteristic char2 = num_characteristics > 1 ? characteristics[1] : 0; - PBL_LOG(LOG_LEVEL_WARNING, "No ppog client removed? 0x%x 0x%x vs 0x%x 0x%x", + PBL_LOG_WRN("No ppog client removed? 0x%x 0x%x vs 0x%x 0x%x", (int)meta, (int)data, (int)char1, (int)char2); } } @@ -861,14 +991,40 @@ void ppogatt_invalidate_all_references(void) { } void ppogatt_handle_service_discovered(BLECharacteristic *characteristics) { + PBL_LOG_INFO("PPoGATT service discovered, starting handshake"); + + // Create timer outside of bt_lock to avoid deadlock with NimbleHost. + // new_timer_create() acquires TaskTimerManager mutex, which may be held by NimbleHost + // when it's trying to acquire bt_lock, leading to a lock ordering deadlock. + TimerID timer = new_timer_create(); + PBL_ASSERTN(timer); + bt_lock(); { // Create new clients: - PPoGATTClient *client = prv_create_client(); + PPoGATTClient *client = prv_create_client(timer); + if (!client) { + bt_unlock(); + new_timer_delete(timer); + return; + } BLECharacteristic meta = characteristics[PPoGATTCharacteristicMeta]; client->characteristics.meta = characteristics[PPoGATTCharacteristicMeta]; client->characteristics.data = characteristics[PPoGATTCharacteristicData]; - if (gatt_client_op_read(meta, GAPLEClientKernel) != BTErrnoOK) { + + PBL_LOG_DBG("PPoGATT reading meta characteristic"); + + // Release bt_lock before calling gatt_client_op_read to avoid recursive lock deadlock. + // gatt_client_op_read manages bt_lock internally. + bt_unlock(); + + BTErrno result = gatt_client_op_read(meta, GAPLEClientKernel); + + // Re-acquire bt_lock before accessing client state again + bt_lock(); + + if (result != BTErrnoOK) { + PBL_LOG_ERR("PPoGATT meta read initiation failed: err=0x%x", result); // Read failed, probably disconnected or insufficient resources prv_delete_client(client, false /* is_disconnected */, DeleteReason_MetaDataReadFailure); } @@ -894,14 +1050,19 @@ void ppogatt_handle_subscribe(BLECharacteristic characteristic, const bool is_subscribed = (subscription_type != BLESubscriptionNone); PPoGATTClient *client = prv_find_client_with_characteristic(characteristic, NULL); if (!client && is_subscribed) { - PBL_LOG(LOG_LEVEL_ERROR, "PPoGATT Client could be found, unsubscribing"); - // Attempt to unsubscribe to avoid wasting bandwidth: + PBL_LOG_ERR("PPoGATT Client could be found, unsubscribing"); + // Attempt to unsubscribe to avoid wasting bandwidth. + // Release bt_lock before calling into NimBLE to avoid deadlock with ble_hs_mutex. + bt_unlock(); gatt_client_subscriptions_subscribe(characteristic, BLESubscriptionNone, GAPLEClientKernel); + bt_lock(); goto unlock; } PBL_ASSERTN(client->state == StateDisconnectedSubscribingData); if (error) { - PBL_LOG(LOG_LEVEL_ERROR, "PPoGATT Client failed to subscribe to Data"); + const uint32_t elapsed_ms = + (rtc_get_ticks() - client->created_ticks) * 1000 / RTC_TICKS_HZ; + PBL_LOG_ERR("PPoGATT subscribe failed: err=0x%x after %"PRIu32"ms", error, elapsed_ms); prv_delete_client(client, false /* is_disconnected */, DeleteReason_SubscribeFailure); goto unlock; } @@ -909,6 +1070,12 @@ void ppogatt_handle_subscribe(BLECharacteristic characteristic, // Unsubscribed due to removed client goto unlock; } + { + const uint32_t elapsed_ms = + (rtc_get_ticks() - client->created_ticks) * 1000 / RTC_TICKS_HZ; + PBL_LOG_INFO("PPoGATT subscribed to Data after %"PRIu32"ms, sending ResetRequest", + elapsed_ms); + } prv_start_reset(client); } unlock: @@ -924,7 +1091,7 @@ void ppogatt_handle_read_or_notification(BLECharacteristic characteristic, const bool is_data = false; PPoGATTClient *client = prv_find_client_with_characteristic(characteristic, &is_data); if (!client) { - PBL_LOG(LOG_LEVEL_DEBUG, "Got notification/read for unknown client"); + PBL_LOG_DBG("Got notification/read for unknown client"); goto unlock; } if (is_data) { @@ -1131,19 +1298,66 @@ static void prv_send_next_packets(PPoGATTClient *client) { uint8_t loop_count = 0; while ((packet = prv_prepare_next_packet(client, &heap_packet, &payload_size))) { ++loop_count; - const BTErrno e = gatt_client_op_write_without_response(client->characteristics.data, - (const uint8_t *) packet, + + // Get the connection and handle while holding bt_lock + GAPLEConnection *connection; + uint16_t att_handle; + bool lock_was_held = bt_lock_is_held(); + + if (lock_was_held) { + att_handle = gatt_client_characteristic_get_handle_and_connection( + client->characteristics.data, &connection); + if (!att_handle) { + // Invalid characteristic, bail out + break; + } + // Release bt_lock BEFORE calling into NimBLE to avoid deadlock. + // See comment in gatt_client_op_write_without_response. + bt_unlock(); + } else { + // If bt_lock wasn't held, we can call gatt_client_op_write_without_response directly + // (it will manage the lock itself) + const BTErrno e = gatt_client_op_write_without_response(client->characteristics.data, + (const uint8_t *) packet, + sizeof(PPoGATTPacket) + payload_size, + GAPLEClientKernel); + if (e == BTErrnoNotEnoughResources) { + // Need to wait for "Buffer Empty" event (see ppogatt_handle_buffer_empty) + break; + } else if (e != BTErrnoOK) { + // Most likely the LE connection got busted, don't think retrying will help. + PBL_LOG_ERR("Write failed %i", e); + break; + } else { + // Packet successfully queued + prv_finalize_queued_packet(client, payload_size); + } + + const uint8_t max_loop_count = 10; + if (loop_count > max_loop_count) { + prv_send_next_packets_async(client); + break; + } + continue; + } + + // Call into NimBLE without holding bt_lock + const BTErrno e = bt_driver_gatt_write_without_response(connection, (const uint8_t *) packet, sizeof(PPoGATTPacket) + payload_size, - GAPLEClientKernel); + att_handle); + + // Re-acquire bt_lock before accessing client state + bt_lock(); + if (e == BTErrnoNotEnoughResources) { // Need to wait for "Buffer Empty" event (see ppogatt_handle_buffer_empty) break; } else if (e != BTErrnoOK) { // Most likely the LE connection got busted, don't think retrying will help. - PBL_LOG(LOG_LEVEL_ERROR, "Write failed %i", e); + PBL_LOG_ERR("Write failed %i", e); break; } else { -// PBL_LOG(LOG_LEVEL_DEBUG, "OUT:"); +// PBL_LOG_DBG("OUT:"); // PBL_HEXDUMP(LOG_LEVEL_DEBUG, (const uint8_t *) packet, sizeof(PPoGATTPacket) + payload_size); // Packet successfully queued @@ -1218,16 +1432,26 @@ void ppogatt_reset(struct Transport *transport) { // ------------------------------------------------------------------------------------------------- void ppogatt_destroy(void) { + bool self_initiated_disconnect = false; bt_lock(); { PPoGATTClient *client = s_ppogatt_head; while (client) { PPoGATTClient *next = (PPoGATTClient *) client->node.next; + self_initiated_disconnect = self_initiated_disconnect || client->disconnect_requested; prv_delete_client(client, true /* is_disconnected */, DeleteReason_DestroyCalled); client = next; } + s_rediscovery_requested_this_connection = false; } bt_unlock(); + + // Only reset the disconnect counter if this was an external disconnect (e.g. RF interference, + // phone out of range). If we initiated the disconnect ourselves due to max resets, keep the + // counter so the stall protection can kick in after repeated failures. + if (!self_initiated_disconnect) { + ppogatt_reset_disconnect_counter(); + } } // ------------------------------------------------------------------------------------------------- diff --git a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.h b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.h index b10ebf82c9..d159047d69 100644 --- a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.h +++ b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_definition.h b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_definition.h index bb6b855248..d878498ea8 100644 --- a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_definition.h +++ b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_definition.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_internal.h b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_internal.h index 9fae8893b4..167261eed9 100644 --- a/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_internal.h +++ b/src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -44,6 +31,11 @@ //! Maximum amount of time PPoGATT will wait before sending an Ack for received data #define PPOGATT_MAX_DATA_ACK_LATENCY_MS (200) +//! Number of maximum retries for reading the meta characteristic +#define PPOGATT_META_READ_RETRY_COUNT_MAX (3) +//! Delay in milliseconds before retrying a failed meta characteristic read +#define PPOGATT_META_READ_RETRY_DELAY_MS (500) + typedef enum { PPoGATTPacketTypeData = 0x0, PPoGATTPacketTypeAck = 0x1, diff --git a/src/fw/comm/ble/kernel_le_client/test/test_definition.h b/src/fw/comm/ble/kernel_le_client/test/test_definition.h index 6b9394f07e..f7f69d9f21 100644 --- a/src/fw/comm/ble/kernel_le_client/test/test_definition.h +++ b/src/fw/comm/ble/kernel_le_client/test/test_definition.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/ble/pebble_bt.c b/src/fw/comm/ble/pebble_bt.c index 0621e2b2b0..2527fc3d5e 100644 --- a/src/fw/comm/ble/pebble_bt.c +++ b/src/fw/comm/ble/pebble_bt.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/src/fw/comm/bluetooth_analytics.c b/src/fw/comm/bluetooth_analytics.c index 21dea5dedc..cf83778e8e 100644 --- a/src/fw/comm/bluetooth_analytics.c +++ b/src/fw/comm/bluetooth_analytics.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bluetooth_analytics.h" #include "comm/ble/gap_le_connection.h" #include "comm/bt_lock.h" #include "drivers/rtc.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/bluetooth_ctl.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/bluetooth_ctl.h" +#include "pbl/services/comm_session/session.h" #include "system/logging.h" #include "util/bitset.h" #include "util/math.h" @@ -56,42 +43,18 @@ static void prv_update_conn_params(uint16_t slave_latency_events, bt_unlock(); } -static void prv_update_conn_event_timer(uint32_t interval_1_25ms, bool stop) { - bt_lock(); - static bool s_analytic_conn_timer_running = false; - if (stop || s_analytic_conn_timer_running) { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_CONN_EVENT_COUNT); - s_analytic_conn_timer_running = false; - } - - if (!stop) { - // track (# connection attempts * 10^3) / sec - uint32_t conn_attempts_per_sec = ((1000 * 1000 * 5) / (interval_1_25ms)) / 4; - analytics_stopwatch_start_at_rate( - ANALYTICS_DEVICE_METRIC_BLE_CONN_EVENT_COUNT, - conn_attempts_per_sec, AnalyticsClient_System); - s_analytic_conn_timer_running = true; - } - bt_unlock(); -} - void bluetooth_analytics_handle_param_update_failed(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_CONN_PARAM_UPDATE_FAILED_COUNT, - AnalyticsClient_System); } //! only called when we are connected as a slave void bluetooth_analytics_handle_connection_params_update(const BleConnectionParams *params) { // When connected as a slave device, the 'Slave Latency' connection parameter allows // the controller to skip the connection sync for that number of connection events. - uint32_t effective_interval = params->conn_interval_1_25ms * (1 + params->slave_latency_events); - - prv_update_conn_event_timer(effective_interval, false); prv_update_conn_params(params->slave_latency_events, params->supervision_timeout_10ms); } void bluetooth_analytics_handle_connection_disconnection_event( - AnalyticsEvent type, uint8_t reason, const BleRemoteVersionInfo *vers_info) { + uint8_t reason, const BleRemoteVersionInfo *vers_info) { static uint32_t last_reset_counter_ticks = 0; static uint8_t num_events_logged = 0; @@ -102,6 +65,29 @@ void bluetooth_analytics_handle_connection_disconnection_event( last_reset_counter_ticks = rtc_get_ticks(); } + // Per-reason counters — see nimble/ble.h ble_error_codes. The NimBLE BLE_HS_HCI_ERR + // (0x200) prefix is dropped by the uint8_t narrowing, so the raw HCI status remains. + switch (reason) { + case 0x08: // BLE_ERR_CONN_SPVN_TMO + PBL_ANALYTICS_ADD(ble_disconnect_conn_spvn_tmo_count, 1); + break; + case 0x13: // BLE_ERR_REM_USER_CONN_TERM + PBL_ANALYTICS_ADD(ble_disconnect_rem_user_term_count, 1); + break; + case 0x16: // BLE_ERR_CONN_TERM_LOCAL + PBL_ANALYTICS_ADD(ble_disconnect_conn_term_local_count, 1); + break; + case 0x22: // BLE_ERR_LMP_LL_RSP_TMO + PBL_ANALYTICS_ADD(ble_disconnect_lmp_ll_rsp_tmo_count, 1); + break; + case 0x3e: // BLE_ERR_CONN_ESTABLISHMENT + PBL_ANALYTICS_ADD(ble_disconnect_conn_establishment_count, 1); + break; + default: + PBL_ANALYTICS_ADD(ble_disconnect_other_count, 1); + break; + } + if (num_events_logged > 100) { // don't log a ridiculous amount of tightly looped disconnects return; } @@ -109,16 +95,8 @@ void bluetooth_analytics_handle_connection_disconnection_event( // It's okay to log to analytics directly from the BT02 callback thread // because flash writes are dispatched to KernelBG if the datalogging session // is buffered - if (type != AnalyticsEvent_BtLeDisconnect) { - analytics_event_bt_connection_or_disconnection(type, reason); - } else { - if (!vers_info) { // We expect version info - PBL_LOG(LOG_LEVEL_WARNING, "Le Disconnect but no version info?"); - } else { - analytics_event_bt_le_disconnection(reason, vers_info->version_number, - vers_info->company_identifier, - vers_info->subversion_number); - } + if (!vers_info) { // We expect version info + PBL_LOG_WRN("Le Disconnect but no version info?"); } num_events_logged++; @@ -126,94 +104,33 @@ void bluetooth_analytics_handle_connection_disconnection_event( void bluetooth_analytics_handle_connect( const BTDeviceInternal *peer_addr, const BleConnectionParams *conn_params) { - - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_COUNT, AnalyticsClient_System); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_TIME, AnalyticsClient_System); - bluetooth_analytics_handle_connection_params_update(conn_params); - - uint8_t link_quality = 0; - int8_t rssi = 0; - bool success = bt_driver_analytics_get_connection_quality(peer_addr, &link_quality, &rssi); - - if (success) { - PBL_LOG(LOG_LEVEL_DEBUG, "Link quality: %x, RSSI: %d", link_quality, rssi); - analytics_add(ANALYTICS_DEVICE_METRIC_BLE_LINK_QUALITY_SUM, - link_quality, AnalyticsClient_System); - analytics_add(ANALYTICS_DEVICE_METRIC_BLE_RSSI_SUM, - ABS(rssi), AnalyticsClient_System); - } } void bluetooth_analytics_handle_disconnect(bool local_is_master) { if (!local_is_master) { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_TIME); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_ENCRYPTED_TIME); - - prv_update_conn_event_timer(0, true); } } void bluetooth_analytics_handle_encryption_change(void) { - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_ENCRYPTED_TIME, - AnalyticsClient_System); } void bluetooth_analytics_handle_no_intent_for_connection(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_NO_INTENT_COUNT, AnalyticsClient_System); } void bluetooth_analytics_handle_ble_pairing_request(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_COUNT, AnalyticsClient_System); -} - -void bluetooth_analytics_handle_bt_classic_pairing_request(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BT_PAIRING_COUNT, AnalyticsClient_System); } void bluetooth_analytics_handle_ble_pairing_error(uint32_t error) { - analytics_event_bt_error(AnalyticsEvent_BtLePairingError, error); -} - -void bluetooth_analytics_handle_bt_classic_pairing_error(uint32_t error) { - analytics_event_bt_error(AnalyticsEvent_BtClassicPairingError, error); } void bluetooth_analytics_ble_mic_error(uint32_t num_sequential_mic_errors) { - PBL_LOG(LOG_LEVEL_INFO, "MIC Error detected ... %"PRIu32" packets", num_sequential_mic_errors); - analytics_event_bt_error(AnalyticsEvent_BtLeMicError, num_sequential_mic_errors); + PBL_LOG_INFO("MIC Error detected ... %"PRIu32" packets", num_sequential_mic_errors); } -static uint32_t prv_calc_other_errors(const SlaveConnEventStats *stats) { - return (stats->num_type_errors + stats->num_len_errors + stats->num_crc_errors + - stats->num_mic_errors); -} static bool prv_calc_stats_and_print(const SlaveConnEventStats *orig_stats, SlaveConnEventStats *stats_buf, bool is_putbytes) { - if (bt_driver_analytics_get_conn_event_stats(stats_buf)) { - stats_buf->num_conn_events = - serial_distance32(orig_stats->num_conn_events, stats_buf->num_conn_events); - stats_buf->num_sync_errors = - serial_distance32(orig_stats->num_sync_errors, stats_buf->num_sync_errors); - stats_buf->num_conn_events_skipped = - serial_distance32(orig_stats->num_conn_events_skipped, stats_buf->num_conn_events_skipped); - stats_buf->num_type_errors = - serial_distance32(orig_stats->num_type_errors, stats_buf->num_type_errors); - stats_buf->num_len_errors = - serial_distance32(orig_stats->num_len_errors, stats_buf->num_len_errors); - stats_buf->num_crc_errors = - serial_distance32(orig_stats->num_crc_errors, stats_buf->num_crc_errors); - stats_buf->num_mic_errors = - serial_distance32(orig_stats->num_mic_errors, stats_buf->num_mic_errors); - - PBL_LOG(LOG_LEVEL_INFO, "%sBytes Conn Stats: Events: %"PRIu32", Sync Errs: %"PRIu32 - ", Skipped Events: %"PRIu32" Other Errs: %"PRIu32, is_putbytes ? "Put" : "Get", - stats_buf->num_conn_events, stats_buf->num_sync_errors, - stats_buf->num_conn_events_skipped, prv_calc_other_errors(stats_buf)); - - return true; - } return false; } @@ -223,11 +140,6 @@ void bluetooth_analytics_handle_put_bytes_stats(bool successful, uint8_t type, u SlaveConnEventStats new_stats = {}; prv_calc_stats_and_print(orig_stats, &new_stats, true /* is_putbytes */); - analytics_event_put_byte_stats( - comm_session_get_system_session(), successful, type, - total_size, elapsed_time_ms, new_stats.num_conn_events, - new_stats.num_sync_errors, new_stats.num_conn_events_skipped, - prv_calc_other_errors(&new_stats)); } void bluetooth_analytics_handle_get_bytes_stats(uint8_t type, uint32_t total_size, @@ -236,46 +148,4 @@ void bluetooth_analytics_handle_get_bytes_stats(uint8_t type, uint32_t total_siz SlaveConnEventStats new_stats = {}; prv_calc_stats_and_print(orig_stats, &new_stats, false /* is_putbytes */); - analytics_event_get_bytes_stats( - comm_session_get_system_session(), type, - total_size, elapsed_time_ms, new_stats.num_conn_events, - new_stats.num_sync_errors, new_stats.num_conn_events_skipped, - prv_calc_other_errors(&new_stats)); -} - -void analytics_external_collect_ble_parameters(void) { - bt_lock(); - { - GAPLEConnection *connection = gap_le_connection_get_gateway(); - if (!connection) { - goto unlock; - } - - LEChannelMap le_channel_map; - const bool success = - bt_driver_analytics_collect_ble_parameters(&connection->device, &le_channel_map); - if (success) { - analytics_set(ANALYTICS_DEVICE_METRIC_BLE_CHAN_USE_COUNT, - count_bits_set((uint8_t *)&le_channel_map, NUM_LE_CHANNELS), - AnalyticsClient_System); - } - } -unlock: - bt_unlock(); -} - -void analytics_external_collect_chip_specific_parameters(void) { - bt_lock(); - bt_driver_analytics_external_collect_chip_specific_parameters(); - bt_unlock(); -} - -void analytics_external_collect_bt_chip_heartbeat(void) { -// TODO: PBL-38365: Re-enable this once it is fixed :( -#if 0 - if (bt_ctl_is_bluetooth_running()) { - // No need for lock - bt_driver_analytics_external_collect_bt_chip_heartbeat(); - } -#endif } diff --git a/src/fw/comm/bluetooth_analytics.h b/src/fw/comm/bluetooth_analytics.h index 49ff47bbdb..245ff827ab 100644 --- a/src/fw/comm/bluetooth_analytics.h +++ b/src/fw/comm/bluetooth_analytics.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,6 @@ #include #include "bluetooth/gap_le_connect.h" -#include "services/common/analytics/analytics_event.h" typedef struct SlaveConnEventStats SlaveConnEventStats; @@ -41,14 +27,10 @@ void bluetooth_analytics_handle_no_intent_for_connection(void); void bluetooth_analytics_handle_ble_pairing_request(void); -void bluetooth_analytics_handle_bt_classic_pairing_request(void); - void bluetooth_analytics_handle_ble_pairing_error(uint32_t error); -void bluetooth_analytics_handle_bt_classic_pairing_error(uint32_t error); - void bluetooth_analytics_handle_connection_disconnection_event( - AnalyticsEvent type, uint8_t reason, const BleRemoteVersionInfo *vers_info); + uint8_t reason, const BleRemoteVersionInfo *vers_info); void bluetooth_analytics_handle_put_bytes_stats(bool successful, uint8_t type, uint32_t total_size, uint32_t elapsed_time_ms, diff --git a/src/fw/comm/bt_conn_mgr.h b/src/fw/comm/bt_conn_mgr.h index d050967258..074f2ea8f7 100644 --- a/src/fw/comm/bt_conn_mgr.h +++ b/src/fw/comm/bt_conn_mgr.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/bt_conn_mgr_impl.h b/src/fw/comm/bt_conn_mgr_impl.h index 88b0b14b6d..2bd3de376b 100644 --- a/src/fw/comm/bt_conn_mgr_impl.h +++ b/src/fw/comm/bt_conn_mgr_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/bt_lock.c b/src/fw/comm/bt_lock.c index 9714abcc12..68983f7222 100644 --- a/src/fw/comm/bt_lock.c +++ b/src/fw/comm/bt_lock.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bt_lock.h" diff --git a/src/fw/comm/bt_lock.h b/src/fw/comm/bt_lock.h index 9d47d68686..cf34095d28 100644 --- a/src/fw/comm/bt_lock.h +++ b/src/fw/comm/bt_lock.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/comm/internals/bt_conn_mgr.c b/src/fw/comm/internals/bt_conn_mgr.c index 0c584594d1..f961e80fea 100644 --- a/src/fw/comm/internals/bt_conn_mgr.c +++ b/src/fw/comm/internals/bt_conn_mgr.c @@ -1,20 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -25,9 +10,9 @@ #include "drivers/rtc.h" #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" #include "util/list.h" @@ -146,7 +131,7 @@ static void prv_handle_response_latency_for_le_conn(GAPLEConnection *hdl) { uint16_t secs_til_max_latency; ResponseTimeState state; BtConsumer responsible_consumer; -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW // We don't care if we burn up some power from PRF and we want FW to update quickly secs_til_max_latency = MAX_PERIOD_RUN_FOREVER; state = ResponseTimeMin; @@ -158,7 +143,7 @@ static void prv_handle_response_latency_for_le_conn(GAPLEConnection *hdl) { // actually request the mode if it has changed: if (hdl->conn_mgr_info->curr_requested_state != state) { - PBL_LOG(LOG_LEVEL_INFO, "LE: Requesting state %d for %d secs, due to %u", + PBL_LOG_INFO("LE: Requesting state %d for %d secs, due to %u", state, secs_til_max_latency, responsible_consumer); gap_le_connect_params_request(hdl, state); } @@ -249,7 +234,7 @@ void conn_mgr_set_ble_conn_response_time_ext( uint16_t max_period_secs, ResponsivenessGrantedHandler granted_handler) { ConnectionMgrInfo *conn_mgr_info; if (!hdl || !((conn_mgr_info = hdl->conn_mgr_info))) { - PBL_LOG(LOG_LEVEL_ERROR, "GAP Handle not properly intialized"); + PBL_LOG_ERR("GAP Handle not properly intialized"); return; } diff --git a/src/fw/comm/prf_stubs/ams.c b/src/fw/comm/prf_stubs/ams.c index 7c8d671bad..70ef0b1482 100644 --- a/src/fw/comm/prf_stubs/ams.c +++ b/src/fw/comm/prf_stubs/ams.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ams/ams.h" @@ -46,5 +33,8 @@ void ams_handle_service_removed(BLECharacteristic *characteristics, uint8_t num_ void ams_destroy(void) { } +void ams_music_disconnect(void) { +} + void ams_send_command(AMSRemoteCommandID command_id) { } diff --git a/src/fw/comm/prf_stubs/ancs.c b/src/fw/comm/prf_stubs/ancs.c index f62a5e78b5..66269bc6de 100644 --- a/src/fw/comm/prf_stubs/ancs.c +++ b/src/fw/comm/prf_stubs/ancs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ancs/ancs.h" @@ -61,11 +48,4 @@ void ancs_destroy(void) { } void ancs_handle_ios9_or_newer_detected(void) { -} - -// ------------------------------------------------------------------------------------------------- -// Analytics - -void analytics_external_collect_ancs_info(void) { - return; -} +} \ No newline at end of file diff --git a/src/fw/console/best_effort_transport.c b/src/fw/console/best_effort_transport.c index 4e8761675e..1bbc59b6e0 100644 --- a/src/fw/console/best_effort_transport.c +++ b/src/fw/console/best_effort_transport.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if PULSE_EVERYWHERE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifdef CONFIG_PULSE_EVERYWHERE #include "pulse_protocol_impl.h" diff --git a/src/fw/console/cobs.c b/src/fw/console/cobs.c index ed7a2c3397..68d4106b3e 100644 --- a/src/fw/console/cobs.c +++ b/src/fw/console/cobs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/cobs.h" diff --git a/src/fw/console/cobs.h b/src/fw/console/cobs.h index f467f086f8..8aeca818da 100644 --- a/src/fw/console/cobs.h +++ b/src/fw/console/cobs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/console_internal.h b/src/fw/console/console_internal.h index 46d003577e..6c5e3e6768 100644 --- a/src/fw/console/console_internal.h +++ b/src/fw/console/console_internal.h @@ -1,30 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include + typedef enum SerialConsoleState { SERIAL_CONSOLE_STATE_PROMPT, SERIAL_CONSOLE_STATE_LOGGING, -#ifdef UI_DEBUG - SERIAL_CONSOLE_STATE_LAYER_NUDGING, -#endif - SERIAL_CONSOLE_STATE_HCI_PASSTHROUGH, - SERIAL_CONSOLE_STATE_ACCESSORY_PASSTHROUGH, - SERIAL_CONSOLE_STATE_PROFILER, SERIAL_CONSOLE_STATE_PULSE, SERIAL_CONSOLE_NUM_STATES } SerialConsoleState; @@ -33,3 +16,5 @@ typedef enum SerialConsoleState { void serial_console_set_state(SerialConsoleState new_state); SerialConsoleState serial_console_get_state(void); + +void serial_console_set_rx_enabled(bool enabled); \ No newline at end of file diff --git a/src/fw/console/control_protocol.c b/src/fw/console/control_protocol.c index 05b3b6ae57..344f8097a1 100644 --- a/src/fw/console/control_protocol.c +++ b/src/fw/console/control_protocol.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/control_protocol.h" #include "console/control_protocol_impl.h" @@ -20,7 +7,7 @@ #include "console/pulse2_transport_impl.h" #include "kernel/events.h" #include "kernel/util/sleep.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "system/logging.h" #include "system/passert.h" #include @@ -93,7 +80,7 @@ static void prv_send_configure_ack(PPPControlProtocol *this, struct LCPPacket *triggering_packet) { if (ntoh16(triggering_packet->length) > pulse_link_max_send_size()) { // Too big to send and truncation will corrupt the packet. - PBL_LOG(LOG_LEVEL_ERROR, "Configure-Request too large to Ack"); + PBL_LOG_ERR("Configure-Request too large to Ack"); return; } struct LCPPacket *packet = pulse_link_send_begin(this->protocol_number); @@ -107,7 +94,7 @@ static void prv_send_configure_reject(PPPControlProtocol *this, if (ntoh16(bad_packet->length) > pulse_link_max_send_size()) { // Too big to send and truncation will corrupt the packet. // There isn't really anything we can do. - PBL_LOG(LOG_LEVEL_ERROR, "Configure-Request too large to Reject"); + PBL_LOG_ERR("Configure-Request too large to Reject"); return; } struct LCPPacket *packet = pulse_link_send_begin(this->protocol_number); @@ -253,8 +240,7 @@ static void prv_on_configure_ack(PPPControlProtocol *this, // If the length is greater than four, there are options in the Ack // which means that the Ack'ed options list does not match the // options list from the request. The Ack packet is invalid. - PBL_LOG(LOG_LEVEL_WARNING, - "Configure-Ack received with options list which differs from " + PBL_LOG_WRN("Configure-Ack received with options list which differs from " "the sent Configure-Request. Discarding."); return; } @@ -274,7 +260,7 @@ static void prv_on_configure_ack(PPPControlProtocol *this, break; case LinkState_AckReceived: case LinkState_Opened: - PBL_LOG(LOG_LEVEL_WARNING, "Unexpected duplicate Configure-Ack"); + PBL_LOG_WRN("Unexpected duplicate Configure-Ack"); prv_send_configure_request(this); prv_transition_to(this, LinkState_RequestSent); break; @@ -315,8 +301,7 @@ static void prv_on_configure_nak_or_reject(PPPControlProtocol *this, // fallthrough case LinkState_AckReceived: case LinkState_Opened: - PBL_LOG(LOG_LEVEL_WARNING, - "Unexpected Configure-Nak/Rej received after Ack"); + PBL_LOG_WRN("Unexpected Configure-Nak/Rej received after Ack"); prv_handle_nak_or_reject(this, packet); prv_transition_to(this, LinkState_RequestSent); break; @@ -350,7 +335,7 @@ static void prv_on_terminate_ack(PPPControlProtocol *this, } else if (this->state->link_state == LinkState_AckReceived) { prv_transition_to(this, LinkState_RequestSent); } else if (this->state->link_state == LinkState_Opened) { - PBL_LOG(LOG_LEVEL_WARNING, "Terminate-Ack received on an open connection"); + PBL_LOG_WRN("Terminate-Ack received on an open connection"); prv_send_configure_request(this); prv_transition_to(this, LinkState_RequestSent); } diff --git a/src/fw/console/control_protocol.h b/src/fw/console/control_protocol.h index e83f21cc53..2931288045 100644 --- a/src/fw/console/control_protocol.h +++ b/src/fw/console/control_protocol.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/control_protocol_impl.h b/src/fw/console/control_protocol_impl.h index c095889112..d62b206dde 100644 --- a/src/fw/console/control_protocol_impl.h +++ b/src/fw/console/control_protocol_impl.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "os/mutex.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include #include diff --git a/src/fw/console/dbgserial.c b/src/fw/console/dbgserial.c index 72700558c7..94da865ac5 100644 --- a/src/fw/console/dbgserial.c +++ b/src/fw/console/dbgserial.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dbgserial.h" @@ -23,7 +10,7 @@ #include -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE #define DEFAULT_SERIAL_BAUD_RATE 1000000 #else #define DEFAULT_SERIAL_BAUD_RATE 230400 @@ -31,7 +18,11 @@ void dbgserial_init(void) { +#if !defined(CONFIG_RELEASE) || defined(CONFIG_MFG) uart_init(DBG_UART); +#else + uart_init_tx_only(DBG_UART); +#endif dbgserial_restore_baud_rate(); } diff --git a/src/fw/console/dbgserial.h b/src/fw/console/dbgserial.h index 4a44ecaa51..5b56e0ce55 100644 --- a/src/fw/console/dbgserial.h +++ b/src/fw/console/dbgserial.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/dbgserial_input.c b/src/fw/console/dbgserial_input.c index ec8700612c..e490962160 100644 --- a/src/fw/console/dbgserial_input.c +++ b/src/fw/console/dbgserial_input.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dbgserial_input.h" @@ -22,29 +9,35 @@ #include "drivers/uart.h" #include "kernel/util/stop.h" #include "os/tick.h" -#include "services/common/system_task.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/new_timer/new_timer.h" #include "system/passert.h" #include "util/attributes.h" #include "util/likely.h" #include "drivers/gpio.h" +#if !defined(CONFIG_RELEASE) || defined(CONFIG_MFG) + #define STOP_MODE_TIMEOUT_MS (2000) +#ifndef CONFIG_SOC_SF32LB52 static void dbgserial_interrupt_handler(bool *should_context_switch); +#endif static DbgSerialCharacterCallback s_character_callback; +#ifndef CONFIG_SOC_SF32LB52 static TimerID s_stop_mode_timeout_timer; //! Use a seperate variable so it's safe to check from the ISR. static bool s_stop_mode_inhibited = false; +#endif //! We DMA into this buffer as a circular buffer #define DMA_BUFFER_LENGTH (200) -static uint8_t DMA_BSS s_dma_buffer[DMA_BUFFER_LENGTH] __attribute__((aligned(4))); +static uint8_t s_dma_buffer[DMA_BUFFER_LENGTH] __attribute__((aligned(4))); static bool s_dma_enabled = false; - +#ifndef CONFIG_SOC_SF32LB52 static void stop_mode_timeout_timer_callback(void* cb_data) { // re-enable stop mode if (s_stop_mode_inhibited) { @@ -52,6 +45,7 @@ static void stop_mode_timeout_timer_callback(void* cb_data) { s_stop_mode_inhibited = false; } } +#endif static bool prv_uart_irq_handler(UARTDevice *dev, uint8_t data, const UARTRXErrorFlags *err_flags) { bool should_context_switch = false; @@ -62,31 +56,38 @@ static bool prv_uart_irq_handler(UARTDevice *dev, uint8_t data, const UARTRXErro } void dbgserial_input_init(void) { +#ifndef CONFIG_SOC_SF32LB52 exti_configure_pin(BOARD_CONFIG.dbgserial_int, ExtiTrigger_Falling, dbgserial_interrupt_handler); // some platforms have a seperate pin for the EXTI int and the USART if (BOARD_CONFIG.dbgserial_int_gpio.gpio != NULL) { gpio_input_init(&BOARD_CONFIG.dbgserial_int_gpio); } +#endif // set up the USART interrupt on RX uart_set_rx_interrupt_handler(DBG_UART, prv_uart_irq_handler); uart_set_rx_interrupt_enabled(DBG_UART, true); +#ifndef CONFIG_SOC_SF32LB52 s_stop_mode_timeout_timer = new_timer_create(); // Enable receive interrupts dbgserial_enable_rx_exti(); +#endif } void dbgserial_enable_rx_exti(void) { +#ifndef CONFIG_SOC_SF32LB52 exti_enable(BOARD_CONFIG.dbgserial_int); +#endif } void dbgserial_register_character_callback(DbgSerialCharacterCallback callback) { s_character_callback = callback; } +#ifndef CONFIG_SOC_SF32LB52 // This callback gets installed by dbgserial_interrupt_handler() // using system_task_add_callback_from_isr(). // It is used to start up our timer since doing so from an ISR is not allowed. @@ -111,12 +112,9 @@ static void dbgserial_interrupt_handler(bool *should_context_switch) { s_stop_mode_inhibited = true; } } +#endif void dbgserial_set_rx_dma_enabled(bool enabled) { -#if TARGET_QEMU - // we can't use DMA on QEMU - enabled = false; -#endif if (enabled == s_dma_enabled) { return; } @@ -128,8 +126,11 @@ void dbgserial_set_rx_dma_enabled(bool enabled) { } } -#if MICRO_FAMILY_NRF5 +void dbgserial_set_input_enabled(bool enabled) { + uart_set_rx_interrupt_enabled(DBG_UART, enabled); +} +#ifdef CONFIG_SOC_NRF52 void dbgserial_disable_rx_dma_before_stop() { // We will have an EXTI wake us if something happens. We'll lose the // first byte anyway, but probably we would have on STM32 also -- and @@ -146,3 +147,21 @@ void dbgserial_enable_rx_dma_after_stop() { } #endif + +#else +void dbgserial_input_init(void) {} + +void dbgserial_enable_rx_exti(void) {} + +void dbgserial_register_character_callback(DbgSerialCharacterCallback callback) {} + +void dbgserial_set_rx_dma_enabled(bool enabled) {} + +void dbgserial_set_input_enabled(bool enabled) {} + +#ifdef CONFIG_SOC_NRF52 +void dbgserial_disable_rx_dma_before_stop() {} + +void dbgserial_enable_rx_dma_after_stop() {} +#endif +#endif diff --git a/src/fw/console/dbgserial_input.h b/src/fw/console/dbgserial_input.h index 6f03969374..f139c03e62 100644 --- a/src/fw/console/dbgserial_input.h +++ b/src/fw/console/dbgserial_input.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -34,7 +21,9 @@ void dbgserial_enable_rx_exti(void); //! Enables/disables DMA-based receiving void dbgserial_set_rx_dma_enabled(bool enabled); -#if MICRO_FAMILY_NRF5 +void dbgserial_set_input_enabled(bool enabled); + +#ifdef CONFIG_SOC_NRF52 void dbgserial_disable_rx_dma_before_stop(); void dbgserial_enable_rx_dma_after_stop(); #endif diff --git a/src/fw/console/prompt.c b/src/fw/console/prompt.c index 82c0d38635..796a047ca4 100644 --- a/src/fw/console/prompt.c +++ b/src/fw/console/prompt.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "prompt.h" #include "prompt_commands.h" @@ -26,7 +13,7 @@ #include "system/passert.h" #include "util/likely.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include #include @@ -338,7 +325,7 @@ void prompt_command_finish(void) { // PULSE infrastructure ///////////////////////////////////////////////////////////////// -#if !PULSE_EVERYWHERE +#ifndef CONFIG_PULSE_EVERYWHERE static uint16_t s_latest_cookie = UINT16_MAX; typedef struct __attribute__((__packed__)) PromptCommand { @@ -355,7 +342,7 @@ typedef struct __attribute__((__packed__)) PromptResponseContents { static void pulse_send_message(const int message_type, const char *response) { size_t response_length = 0; -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE PromptResponseContents *contents = pulse_reliable_send_begin( PULSE2_RELIABLE_PROMPT_PROTOCOL); if (!contents) { @@ -383,7 +370,7 @@ static void pulse_send_message(const int message_type, const char *response) { if (response_length > 0) { strncpy(contents->message, response, response_length); } -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE pulse_reliable_send(contents, total_size); #else pulse_best_effort_send(contents, total_size); @@ -395,11 +382,11 @@ static void prv_pulse_done_command(void) { s_executing_command = ExecutingCommandNone; } -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE void pulse2_prompt_packet_handler(void *packet, size_t length) { if (prompt_command_is_executing()) { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring prompt command as another command is " + PBL_LOG_DBG("Ignoring prompt command as another command is " "currently executing"); return; } diff --git a/src/fw/console/prompt.h b/src/fw/console/prompt.h index a61150dcd1..6655888374 100644 --- a/src/fw/console/prompt.h +++ b/src/fw/console/prompt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/prompt_commands.c b/src/fw/console/prompt_commands.c index 57ba6f2b46..9dbeff5bbb 100644 --- a/src/fw/console/prompt_commands.c +++ b/src/fw/console/prompt_commands.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "prompt_commands.h" @@ -35,14 +22,13 @@ #include "kernel/util/factory_reset.h" #include "kernel/util/sleep.h" #include "kernel/util/stop.h" -#include "mfg/mfg_apps/mfg_flash_test.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" #include "prompt.h" #include "resource/resource_storage_flash.h" -#include "services/common/compositor/compositor.h" -#include "services/common/system_task.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/system_task.h" +#include "pbl/services/filesystem/pfs.h" #include "syscall/syscall.h" #include "system/bootbits.h" #include "system/hexdump.h" @@ -54,10 +40,8 @@ #include "util/net.h" #include "util/string.h" -#define CMSIS_COMPATIBLE -#include +#include -#include #include #include @@ -148,7 +132,7 @@ void command_dump_flash(const char* address_str, const char* length_str) { uint32_t chunk_size = MIN(length, 128); flash_read_bytes(buffer, address, chunk_size); - PBL_LOG(LOG_LEVEL_ALWAYS, "Data at address 0x%"PRIx32, address); + PBL_LOG_ALWAYS("Data at address 0x%"PRIx32, address); hexdump_log(LOG_LEVEL_ALWAYS, buffer, chunk_size); address += chunk_size; @@ -265,29 +249,6 @@ void command_flash_fill (const char* address_str, const char* length_str, const } } -// Pass in test case number and number of iterations to run -// Currently iterations only applies to FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST -// All other tests run once -void command_flash_test(const char* test_case_num_str, const char* iterations_str) { - int32_t test_case_num = atoi(test_case_num_str); - int32_t iterations = atoi(iterations_str); - - int32_t status = FLASH_TEST_ERR_OTHER; - if (!((test_case_num == FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST) && (iterations <= 0))) { - // Check to make sure stress test has at least 1 iteration or its another test case - status = run_flash_test_case(test_case_num, iterations); - } - - char buffer[80]; - if (status == 0) { - prompt_send_response_fmt(buffer, sizeof(buffer), "PASS: TEST CASE %"PRId32, test_case_num); - } else { - prompt_send_response_fmt(buffer, sizeof(buffer), - ">FAIL: TEST CASE %"PRId32", Status: %"PRId32, - test_case_num, status); - } -} - void command_flash_validate(void) { // just test one sector, which is probably less than the size of the region const uint32_t TEST_ADDR = FLASH_REGION_FIRMWARE_DEST_BEGIN; @@ -414,7 +375,7 @@ void command_flash_show_erased_sectors(const char *arg) { } } -#if CAPABILITY_HAS_FLASH_OTP +#ifdef CONFIG_OTP_FLASH void command_flash_sec_read(const char *address_str) { uint32_t address = strtoul(address_str, NULL, 0); uint8_t val; @@ -474,8 +435,6 @@ void command_flash_sec_wipe(void) { void command_flash_sec_info(void) { const FlashSecurityRegisters *info = flash_security_registers_info(); - bool locked; - status_t ret; char buf[64]; if (info->sec_regs == NULL) { @@ -483,31 +442,34 @@ void command_flash_sec_info(void) { return; } - ret = flash_security_registers_are_locked(&locked); - if (ret != S_SUCCESS) { - prompt_send_response("FAIL: Unable to check security register lock status"); - return; - } - - prompt_send_response_fmt(buf, sizeof(buf), "Security registers are %slocked", - locked ? "" : "not "); prompt_send_response_fmt(buf, sizeof(buf), "Number of security registers: %d", info->num_sec_regs); for (int i = 0; i < info->num_sec_regs; i++) { - prompt_send_response_fmt(buf, sizeof(buf), "Security register %d: 0x%08lx", i, info->sec_regs[i]); + bool locked; + status_t ret; + + ret = flash_security_register_is_locked(info->sec_regs[i], &locked); + if (ret != S_SUCCESS) { + prompt_send_response("FAIL: Unable to check security register lock status"); + return; + } + + prompt_send_response_fmt(buf, sizeof(buf), "Security register %d: 0x%08lx (locked: %u)", + i, info->sec_regs[i], locked); } } -#ifdef RECOVERY_FW -void command_flash_sec_lock(const char *password) { +#ifdef CONFIG_RECOVERY_FW +void command_flash_sec_lock(const char *address_str, const char *password) { if (strcmp(password, "l0ckm3f0r3v3r") == 0) { - flash_lock_security_registers(); + uint32_t address = strtoul(address_str, NULL, 0); + flash_lock_security_register(address); prompt_send_response("OK"); } else { prompt_send_response("FAIL: Invalid password"); } } -#endif // RECOVERY_FW -#endif // CAPABILITY_HAS_FLASH_OTP +#endif // CONFIG_RECOVERY_FW +#endif // CONFIG_OTP_FLASH #include "util/rand.h" @@ -525,14 +487,14 @@ static void prv_flash_stress_callback(void *data) { int iters = (int)data; if (iters == 0) { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test complete"); + PBL_LOG_ALWAYS("flash stress test complete"); return; } int bufsz = rand32() % 1024; uint8_t *buf = kernel_malloc(bufsz); if (!buf) { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test: malloc of size %d failed", bufsz); + PBL_LOG_ALWAYS("flash stress test: malloc of size %d failed", bufsz); system_task_add_callback(prv_flash_stress_callback, (void *)(iters - 1)); return; } @@ -552,11 +514,11 @@ static void prv_flash_stress_callback(void *data) { uint32_t sector_address = flash_get_sector_base_address(flash_addr + bufsz); // the beginning has already been erased, since we are always smaller than a sector if (sector_address != s_flash_stress_last_sector) { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test: erasing flash address %lx", sector_address); + PBL_LOG_ALWAYS("flash stress test: erasing flash address %lx", sector_address); flash_erase_sector_blocking(sector_address); s_flash_stress_last_sector = sector_address; if (!prv_is_really_erased(sector_address, 0)) { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test: flash address %lx erase failed!", sector_address); + PBL_LOG_ALWAYS("flash stress test: flash address %lx erase failed!", sector_address); miscompare = -1; goto bailout; } @@ -578,7 +540,7 @@ static void prv_flash_stress_callback(void *data) { for (int i = 0; i < bufsz; i++) { if (buf[i] != (lfsr_cur & 0xFF)) { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test: readback %d: miscompare at offset %d (%lx): expected 0x%02lx, found 0x%02x", j, i, flash_addr + i, lfsr_cur & 0xFF, buf[i]); + PBL_LOG_ALWAYS("flash stress test: readback %d: miscompare at offset %d (%lx): expected 0x%02lx, found 0x%02x", j, i, flash_addr + i, lfsr_cur & 0xFF, buf[i]); miscompare++; } lfsr_cur = prv_xorshift32(lfsr_cur); @@ -591,9 +553,9 @@ static void prv_flash_stress_callback(void *data) { kernel_free(buf); if (miscompare) { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test: %d miscompares on %d byte chunk at address %lx! giving up", miscompare, bufsz, flash_addr); + PBL_LOG_ALWAYS("flash stress test: %d miscompares on %d byte chunk at address %lx! giving up", miscompare, bufsz, flash_addr); } else { - PBL_LOG(LOG_LEVEL_ALWAYS, "flash stress test: %d bytes at address %lx OK; %d to go", bufsz, flash_addr, iters - 1); + PBL_LOG_ALWAYS("flash stress test: %d bytes at address %lx OK; %d to go", bufsz, flash_addr, iters - 1); system_task_add_callback(prv_flash_stress_callback, (void *)(iters - 1)); } } @@ -607,6 +569,59 @@ void command_flash_stress(const char *n) { system_task_add_callback(prv_flash_stress_callback, (void *)count); } +static void s_flash_benchmark(size_t sz) { + uint32_t flash_addr = FLASH_REGION_FIRMWARE_DEST_BEGIN; + + char rbuf[64]; + + void *buf = kernel_malloc(sz); + if (!buf) { + prompt_send_response("OOM allocating read buffer"); + return; + } + + prompt_send_response_fmt(rbuf, sizeof(rbuf), "benchmarking %d bytes...", sz); + + RtcTicks ticks_elapsed; + int iters = 2048; + + do { + RtcTicks ticks_start = rtc_get_ticks(); + + iters *= 2; + for (int i = 0; i < iters; i++) { + flash_read_bytes(buf, flash_addr, sz); + flash_addr += sz; + flash_addr &= ~3; /* keep us aligned */ + flash_addr &= ~(SUBSECTOR_SIZE_BYTES << 1); /* keep us from wrapping too far */ + } + + ticks_elapsed = rtc_get_ticks() - ticks_start; + } while (ticks_elapsed < 300); + + uint32_t us_per_tick = ticks_elapsed * 1000000 / (iters * RTC_TICKS_HZ); + prompt_send_response_fmt(rbuf, sizeof(rbuf), " -> %d bytes: %d iters in %lld ticks = %ld us/iter", sz, iters, ticks_elapsed, us_per_tick); + + free(buf); + + /* this could take a while -- don't crash! */ + task_watchdog_bit_set(pebble_task_get_current()); +} + +void command_flash_benchmark() { + prompt_send_response("running flash read benchmark"); + + s_flash_benchmark(4); + s_flash_benchmark(5); + s_flash_benchmark(16); + s_flash_benchmark(64); + s_flash_benchmark(128); + s_flash_benchmark(256); + s_flash_benchmark(512); + s_flash_benchmark(1024); +} + + void command_reset() { prompt_command_finish(); @@ -651,7 +666,7 @@ void stuck_timer_cb(void* data) { while(1); } -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" void command_stuck_timer(void) { TimerID timer = new_timer_create(); new_timer_start(timer, 10, stuck_timer_cb, NULL, 0 /*flags*/); @@ -1062,7 +1077,7 @@ void command_waste_time(const char *count_arg, const char *delay_arg) { } } -#ifndef RELEASE +#ifndef CONFIG_RELEASE #include "system/profiler.h" void command_audit_delay_us(void) { profiler_init(); @@ -1090,16 +1105,26 @@ void command_audit_delay_us(void) { __enable_irq(); } +#if !defined(CONFIG_RELEASE) && defined(CONFIG_DISPLAY_JDI_SF32LB) +#include "drivers/display/sf32lb/display_jdi.h" + +// Arms the JDI display driver to drop the next LCDC transfer-complete +// callback, simulating the silent-loss failure mode (e.g. SiFli HAL ICB +// overflow). The silent-loss timer should fire ~500ms later and PBL_CROAK, +// producing a Memfault coredump and a watch reboot. +void command_display_drop_complete(void) { + display_jdi_test_drop_next_complete(); + prompt_send_response("display: armed drop of next LCDC complete; PBL_CROAK in ~500ms"); +} +#endif + +#ifndef CONFIG_SOC_SF32LB52 // Simply parks the chip permanently in stop mode in whatever state it's currently in. This can be // pretty handy when trying to profile power of the chip under certains states -// NOTE: If you did not configure with `--nowatchdog`, the HW watchdog will reboot you in ~8s +// NOTE: If you did not configure with CONFIG_NO_WATCHDOG=y, the HW watchdog will reboot you in ~8s void command_enter_stop(void) { dbgserial_putstr("Entering stop mode indefinitely ... reboot your board to get out!!"); __disable_irq(); -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - RTC_ITConfig(RTC_IT_WUT, DISABLE); - RTC_WakeUpCmd(DISABLE); -#endif // disable all IRQn_Type >= 0 interrupts for (size_t i = 0; i < ARRAY_LENGTH(NVIC->ISER); i++) { NVIC->ICER[i] = NVIC->ISER[i]; @@ -1113,20 +1138,28 @@ void command_enter_stop(void) { dbgserial_putstr("woah, failed to enter stop mode"); while (1) { } } +#endif -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW // Create a bunch of fragmentation in the filesystem by creating a large number -// of small files and only deleting a small number of them -void command_litter_filesystem(void) { - char name[10]; - for (int i = 0; i < 100; i++) { +// of files and only deleting a small number of them +void command_litter_filesystem(const char *s_number, const char *s_size) { + char name[20]; + int number = atoi(s_number); + size_t size = (size_t)atoi(s_size); + for (int i = 0; i < number; i++) { snprintf(name, sizeof(name), "litter%d", i); - int fd = pfs_open(name, OP_FLAG_WRITE, FILE_TYPE_STATIC, 300); + PBL_LOG_DBG("Creating %s", name); + int fd = pfs_open(name, OP_FLAG_WRITE, FILE_TYPE_STATIC, size); if (i % 5 == 0) { pfs_close_and_remove(fd); + PBL_LOG_DBG("Removed %s", name); } else { pfs_close(fd); + PBL_LOG_DBG("Closed %s", name); } + + task_watchdog_bit_set(pebble_task_get_current()); } } #endif @@ -1218,27 +1251,15 @@ void command_ble_logging_get_level(void) { prompt_send_response_fmt(buffer, 32, "Ble Log level: %d", log_level); } } -// ARG: -// 0 - Request BLE firmware to coredump -// 1 - Force BLE firmware to hard fault -// 2 - Force the BLE chip to watchdog (by wedging a task) -void command_ble_core_dump(const char *command) { - int option = atoi(command); - if ((option < 0) || (option >= BtleCoreDumpCount)) { - prompt_send_response("Invalid BLE core command"); - return; - } - - bt_driver_core_dump(option); -} #if MEMFAULT void command_mflt_export(void) { memfault_data_export_dump_chunks(); } -void command_mflt_heartbeat(void) { - memfault_metrics_heartbeat_debug_trigger(); +void command_mflt_collect(void) { + void memfault_chunk_collect(void); + memfault_chunk_collect(); } void command_mflt_metrics_dump(void) { @@ -1251,7 +1272,7 @@ void command_mflt_device_info(void) { } #endif // MEMFAULT -#if PERFORMANCE_TESTS +#ifdef CONFIG_PERFORMANCE_TESTS // for task_watchdog_bit_set_all #include "drivers/task_watchdog.h" // For taskYIELD() @@ -1369,14 +1390,10 @@ static const PerftestTextString s_perftest_text_strings[TestStringCount] = { "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM", .lengths = { -#if PLATFORM_ROBERT || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) [TestStringFont_Gothic18] = 204, [TestStringFont_Gothic24B] = 144, [TestStringFont_Other] = STRING_LENGTH_MAX, -#elif PLATFORM_SNOWY - [TestStringFont_Gothic18] = 109, - [TestStringFont_Gothic24B] = 78, - [TestStringFont_Other] = STRING_LENGTH_MAX, #endif }, }, @@ -1392,14 +1409,10 @@ static const PerftestTextString s_perftest_text_strings[TestStringCount] = { "をんアイウエオサシスセソタチツテトナニヌネノ" "ハヒフヘホマミムメモヤユヨラリルレロワヲン", .lengths = { -#if PLATFORM_ROBERT || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) [TestStringFont_Gothic18] = 579, [TestStringFont_Gothic24B] = 291, [TestStringFont_Other] = STRING_LENGTH_MAX, -#elif PLATFORM_SNOWY - [TestStringFont_Gothic18] = 256, - [TestStringFont_Gothic24B] = 113, - [TestStringFont_Other] = STRING_LENGTH_MAX, #endif }, }, @@ -1410,14 +1423,10 @@ static const PerftestTextString s_perftest_text_strings[TestStringCount] = { "FLASH access on Robe" "\xe2\x80\xa6", .lengths = { -#if PLATFORM_ROBERT || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) [TestStringFont_Gothic18] = 134, [TestStringFont_Gothic24B] = 134, [TestStringFont_Other] = STRING_LENGTH_MAX, -#elif PLATFORM_SNOWY - [TestStringFont_Gothic18] = 134, - [TestStringFont_Gothic24B] = 112, - [TestStringFont_Other] = STRING_LENGTH_MAX, #endif }, }, @@ -1600,3 +1609,29 @@ void command_perftest_text_all(void) { } } #endif + +static TimerID s_console_disable_rx_timer = TIMER_INVALID_ID; + +static void prv_console_disable_rx_timer_cb(void *data) { + serial_console_set_rx_enabled(true); +} + +void command_console_disable_rx(const char *seconds_str) { + int seconds = atoi(seconds_str); + if (seconds <= 0) { + prompt_send_response("Invalid seconds value"); + return; + } + + if (s_console_disable_rx_timer == TIMER_INVALID_ID) { + s_console_disable_rx_timer = new_timer_create(); + } + + serial_console_set_rx_enabled(false); + + char buf[64]; + prompt_send_response_fmt(buf, sizeof(buf), "Console RX disabled for %d seconds", seconds); + + new_timer_start(s_console_disable_rx_timer, seconds * 1000, + prv_console_disable_rx_timer_cb, NULL, 0 /*flags*/); +} diff --git a/src/fw/console/prompt_commands.h b/src/fw/console/prompt_commands.h index 49e155ad53..fa8bfcd673 100644 --- a/src/fw/console/prompt_commands.h +++ b/src/fw/console/prompt_commands.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "console/prompt.h" #include "console/pulse_internal.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "system/logging.h" #include "util/size.h" @@ -64,10 +51,8 @@ extern void command_dump_malloc_bt(void); extern void command_read_word(const char*); -extern void command_power_2v5(const char*); - extern void command_backlight_ctl(const char*); -extern void command_rgb_set_color(const char*); +extern void command_backlight_set_color(const char*); extern void command_battery_charge_option(const char*); @@ -76,7 +61,6 @@ extern void command_compass_peek(void); extern void command_accel_peek(void); extern void command_accel_num_samples(char *num_samples); extern void command_accel_status(void); -extern void command_accel_selftest(void); extern void command_accel_softreset(void); extern void command_dump_flash(const char*, const char*); @@ -86,25 +70,25 @@ extern void command_erase_flash(const char*, const char*); extern void command_flash_read(const char*, const char*); extern void command_flash_switch_mode(const char*); extern void command_flash_fill(const char*, const char*, const char*); -extern void command_flash_test(const char* test_case_num_str, const char* iterations_str); extern void command_flash_test_locked_sectors(void); extern void command_flash_stress(const char *); +extern void command_flash_benchmark(void); extern void command_flash_validate(void); extern void command_flash_apicheck(const char *len); extern void command_flash_unprotect(void); //extern void command_flash_signal_test_init(void); //extern void command_flash_signal_test_run(void); extern void command_flash_show_erased_sectors(const char *arg); -#if CAPABILITY_HAS_FLASH_OTP +#ifdef CONFIG_OTP_FLASH extern void command_flash_sec_read(const char *); extern void command_flash_sec_write(const char *, const char *); extern void command_flash_sec_erase(const char *); extern void command_flash_sec_wipe(void); extern void command_flash_sec_info(void); -#if defined(RECOVERY_FW) -extern void command_flash_sec_lock(const char *); -#endif // RECOVERY_FW -#endif // CAPABILITY_HAS_FLASH_OTP +#if defined(CONFIG_RECOVERY_FW) +extern void command_flash_sec_lock(const char *, const char *); +#endif // CONFIG_RECOVERY_FW +#endif // CONFIG_OTP_FLASH extern void command_get_time(void); extern void command_set_time(const char *arg); @@ -118,7 +102,6 @@ extern void command_timers(void); extern void command_bt_airplane_mode(const char*); extern void command_bt_prefs_wipe(void); extern void command_bt_print_mac(void); -extern void command_bt_set_addr(const char *bd_addr); extern void command_bt_set_name(const char *bt_name); extern void command_bt_status(void); @@ -150,29 +133,19 @@ extern void command_sim_panic(const char*); extern void command_alarm(void); -extern void command_bt_test_start(void); -extern void command_bt_test_stop(void); - -extern void command_bt_test_hci_passthrough(); -extern void command_bt_test_bt_sig_rf_mode(); - extern void command_watch(void); extern void command_print_now_playing(void); -extern void command_selftest(void); extern void command_enter_mfg(void); extern void command_enter_standby(void); extern void command_enter_consumer_mode(void); extern void command_power_5v(const char*); -extern void command_accessory_imaging_start(void); - extern void command_serial_read(void); extern void command_hwver_read(void); extern void command_pcba_serial_read(void); extern void command_color_read(void); -extern void command_disp_offset_read(void); extern void command_rtcfreq_read(void); extern void command_model_read(void); @@ -180,11 +153,8 @@ extern void command_serial_write(const char*); extern void command_hwver_write(const char*); extern void command_pcba_serial_write(const char*); extern void command_color_write(const char*); -extern void command_disp_offset_write(const char*); extern void command_rtcfreq_write(const char*); extern void command_model_write(const char*); -extern void command_bootloader_test(const char*); - extern void command_version_info(void); extern void command_als_read(void); @@ -227,11 +197,6 @@ extern void command_pmic_read_registers(void); extern void command_ping_send(void); extern void command_display_set(const char *color); -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR -extern void command_accessory_power_set(const char *on); -extern void command_accessory_stress_test(void); -extern void command_smartstrap_status(void); -#endif extern void command_mic_start(char *timeout_str, char *sample_size_str, char *sample_rate_str, char *volume_str); extern void command_mic_read(void); @@ -241,7 +206,7 @@ extern void dump_current_runtime_stats(void); extern void command_set_runlevel(const char *runlevel); -extern void command_litter_filesystem(void); +extern void command_litter_filesystem(const char *s_number, const char *s_size); typedef struct Command { char* cmd_str; @@ -270,9 +235,7 @@ extern void command_ble_send_service_changed_indication(void); extern void command_ble_rediscover(void); extern void command_ble_logging_set_level(const char *level); extern void command_ble_logging_get_level(void); -extern void command_ble_core_dump(const char *command); - -extern void command_low_power_debug(char *enable_arg); +extern void command_ble_host_reset(void); extern void command_audit_delay_us(void); extern void command_enter_stop(void); @@ -295,14 +258,8 @@ extern void command_btle_pa_set(char *option); extern void command_btle_unmod_tx_start(char *tx_channel); extern void command_btle_unmod_tx_stop(void); -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM extern void command_hrm_read(void); -extern void command_hrm_wipe(void); -extern void command_hrm_freeze(void); -#endif - -#if MFG_INFO_RECORDS_TEST_RESULTS -extern void command_mfg_info_test_results(void); #endif extern void command_perftest_line(const char *, const char *); @@ -314,22 +271,28 @@ extern void command_bt_sleep_check(const char *iters); #if MEMFAULT extern void command_mflt_export(void); -extern void command_mflt_heartbeat(void); +extern void command_mflt_collect(void); extern void command_mflt_metrics_dump(void); extern void command_mflt_device_info(void); #endif -#if PLATFORM_TINTIN && !TARGET_QEMU -// We don't have space for anything that's not absolutely required for firmware development -// (imaging resources over PULSE). Rip it all out. Note that this breaks test automation on tintin, -// but QEMU will be used as a placeholder. We plan on reintroducing test automation support through -// continued space savings efforts and by introducing RPC commands over PULSE. Stay tuned. -// For reference, this saves about 12k of space. If we leave just the test automation commands in -// roughly 7k of space is saved. -#define KEEP_NON_ESSENTIAL_COMMANDS 0 -#else -#define KEEP_NON_ESSENTIAL_COMMANDS 1 +#ifdef ANALYTICS_NATIVE +extern void command_analytics_native_metrics_dump(void); +#endif + +extern void command_analytics_heartbeat(void); + +extern void command_console_disable_rx(const char *seconds_str); + +#ifdef CONFIG_SOC_SF32LB52 +extern void command_force_deepwfi(const char *arg); +#endif + +#if !defined(CONFIG_RELEASE) && defined(CONFIG_DISPLAY_JDI_SF32LB) +extern void command_display_drop_complete(void); #endif + +#define KEEP_NON_ESSENTIAL_COMMANDS 1 static const Command s_prompt_commands[] = { // PULSE entry point, needed for anything PULSE-related to work { "PULSEv1", pulse_start, 0 }, @@ -343,7 +306,7 @@ static const Command s_prompt_commands[] = { { "reset", command_reset, 0 }, { "crash", command_crash, 0 }, { "hard crash", command_hard_crash, 0 }, -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW { "factory reset fast", command_factory_reset_fast, 0 }, #endif { "factory reset", command_factory_reset, 0 }, @@ -356,18 +319,19 @@ static const Command s_prompt_commands[] = { { "bt airplane mode", command_bt_airplane_mode, 1 }, { "bt prefs wipe", command_bt_prefs_wipe, 0 }, { "bt mac", command_bt_print_mac, 0 }, - { "bt set addr", command_bt_set_addr, 1 }, { "bt set name", command_bt_set_name, 1 }, { "bt cp set", command_bt_conn_param_set, 4 }, { "bt disc start", command_bt_disc_start, 2 }, { "bt disc stop", command_bt_disc_stop, 0 }, { "timezone clear", command_timezone_clear, 0 }, { "battery status", command_print_battery_status, 0 }, -#ifndef RELEASE +#ifndef CONFIG_RELEASE { "audit delay", command_audit_delay_us, 0 }, +#ifndef CONFIG_SOC_SF32LB52 { "enter stop", command_enter_stop, 0}, #endif -#ifndef RECOVERY_FW +#endif +#ifndef CONFIG_RECOVERY_FW { "app list", command_app_list, 0 }, { "app launch", command_app_launch, 1 }, { "app remove", command_app_remove, 1 }, @@ -377,13 +341,11 @@ static const Command s_prompt_commands[] = { { "erase flash", command_erase_flash, 2 }, { "crc flash", command_crc_flash, 2 }, -#ifndef RECOVERY_FW -#if CAPABILITY_HAS_TEMPERATURE +#ifndef CONFIG_RECOVERY_FW { "temp read", command_temperature_read, 0 }, -#endif { "als read", command_als_read, 0}, -#ifndef RELEASE - { "litter pfs", command_litter_filesystem, 0 }, +#ifndef CONFIG_RELEASE + { "litter pfs", command_litter_filesystem, 2 }, #endif #endif @@ -391,11 +353,7 @@ static const Command s_prompt_commands[] = { // Following commands are used for manufacturing. We use a PRF firmware for manufacturing, so // we can only include these commands when we're building for PRF. Some of the commands are // specific to snowy manufacturing as well -#ifdef RECOVERY_FW -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - { "accessory imaging start", command_accessory_imaging_start, 0 }, -#endif - +#ifdef CONFIG_RECOVERY_FW { "info", command_version_info, 0 }, { "enter mfg", command_enter_mfg, 0 }, @@ -408,109 +366,62 @@ static const Command s_prompt_commands[] = { { "hwver read", command_hwver_read, 0 }, { "pcbaserial read", command_pcba_serial_read, 0 }, { "color read", command_color_read, 0 }, -#if PBL_ROUND - { "disp offset read", command_disp_offset_read, 0 }, -#endif { "rtcfreq read", command_rtcfreq_read, 0 }, { "model read", command_model_read, 0 }, -#if CAPABILITY_HAS_FLASH_OTP - { "flash sec lock", command_flash_sec_lock, 1}, -#endif // CAPABILITY_HAS_FLASH_OTP +#ifdef CONFIG_OTP_FLASH + { "flash sec lock", command_flash_sec_lock, 2}, +#endif // CONFIG_OTP_FLASH { "serial write", command_serial_write, 1 }, { "hwver write", command_hwver_write, 1 }, { "pcbaserial write", command_pcba_serial_write, 1 }, -#if MANUFACTURING_FW +#ifdef CONFIG_MFG { "color write", command_color_write, 1 }, -#if PBL_ROUND - { "disp offset write", command_disp_offset_write, 2 }, -#endif { "rtcfreq write", command_rtcfreq_write, 1 }, { "model write", command_model_write, 1 }, -#endif // MANUFACTURING_FW - { "bootloader test", command_bootloader_test, 1 }, - - { "scheduler force active", command_scheduler_force_active, 0 }, - { "scheduler resume normal", command_scheduler_resume_normal, 0 }, +#endif // CONFIG_MFG { "bt status", command_bt_status, 0 }, - { "bt test start", command_bt_test_start, 0 }, - { "bt test stop", command_bt_test_stop, 0 }, - { "bt test hcipass", command_bt_test_hci_passthrough, 0 }, - - { "bt test bt_sig_rf", command_bt_test_bt_sig_rf_mode, 0}, - - { "backlight", command_backlight_ctl, 1 }, { "button read", command_button_read, 1 }, -#if CAPABILITY_HAS_MAGNETOMETER +#ifdef CONFIG_MAG { "compass peek", command_compass_peek, 0 }, -#endif // CAPABILITY_HAS_MAGNETOMETER +#endif { "accel read", command_accel_peek, 0 }, { "als read", command_als_read, 0}, -#ifdef PLATFORM_TINTIN // TINTIN/BIANCA only - { "power 2.5", command_power_2v5, 1 }, -#else - { "selftest", command_selftest, 0 }, - { "flash read", command_flash_read, 2}, { "flash switchmode", command_flash_switch_mode, 1}, { "flash fill", command_flash_fill, 3}, -#if CAPABILITY_USE_PARALLEL_FLASH - { "flash test", command_flash_test, 2}, -#endif { "flash validate", command_flash_validate, 0}, { "flash erased_sectors", command_flash_show_erased_sectors, 1}, -#if !RELEASE && (PLATFORM_SILK || PLATFORM_ROBERT || PLATFORM_CALCULUS) - { "flash apicheck", command_flash_apicheck, 1}, - //{ "flash signal test init", command_flash_signal_test_init, 0 }, - //{ "flash signal test run", command_flash_signal_test_run, 0 }, -#endif -#if CAPABILITY_HAS_FLASH_OTP +#ifdef CONFIG_OTP_FLASH { "flash sec read", command_flash_sec_read, 1}, { "flash sec write", command_flash_sec_write, 2}, { "flash sec erase", command_flash_sec_erase, 1}, { "flash sec wipe", command_flash_sec_wipe, 0}, { "flash sec info", command_flash_sec_info, 0}, -#endif // CAPABILITY_HAS_FLASH_OTP +#endif // CONFIG_OTP_FLASH //{ "pmic rails", command_pmic_rails, 0}, - +#ifdef CONFIG_MFG { "disp", command_display_set, 1}, +#endif +#endif // CONFIG_RECOVERY_FW -#if MFG_INFO_RECORDS_TEST_RESULTS - { "mfg ui test results", command_mfg_info_test_results, 0 }, -#endif // MFG_INFO_RECORDS_TEST_RESULTS - -#endif // PLATFORM_TINTIN -#endif // RECOVERY_FW - -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM { "hrm read", command_hrm_read, 0}, -#if PLATFORM_SILK || PLATFORM_ROBERT - { "hrm wipe", command_hrm_wipe, 0}, - { "hrm freeze", command_hrm_freeze, 0}, -#endif // PLATFORM_SILK || PLATFORM_ROBERT #endif -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - { "accessory power", command_accessory_power_set, 1 }, - { "accessory stress", command_accessory_stress_test, 0 }, -#if !RELEASE && !RECOVERY_FW - { "smartstrap status", command_smartstrap_status, 0 }, -#endif // RELEASE -#endif // CAPABILITY_HAS_ACCESSORY_CONNECTOR - -#if CAPABILITY_HAS_PMIC +#ifdef CONFIG_PMIC {"pmic regs", command_pmic_read_registers, 0}, #endif -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC { "mic start", command_mic_start, 4}, { "mic read", command_mic_read, 0}, @@ -526,7 +437,9 @@ static const Command s_prompt_commands[] = { // Meta { "help", command_help, 0 }, - { "lowpowerdebug", command_low_power_debug, 1 }, + + { "scheduler force active", command_scheduler_force_active, 0 }, + { "scheduler resume normal", command_scheduler_resume_normal, 0 }, { "log level set", command_log_level_set, 1 }, { "log level get", command_log_level_get, 0 }, @@ -541,7 +454,7 @@ static const Command s_prompt_commands[] = { { "ble rediscover", command_ble_rediscover, 0 }, { "ble set log level", command_ble_logging_set_level, 1}, { "ble get log level", command_ble_logging_get_level, 0}, - { "ble core dump", command_ble_core_dump, 1 }, + { "ble host reset", command_ble_host_reset, 0}, /* { "stats dump now", command_stats_dump_now, 0 }, @@ -565,11 +478,11 @@ static const Command s_prompt_commands[] = { */ { "croak", command_croak, 0 }, -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION { "dump malloc kernel", command_dump_malloc_kernel, 0 }, { "dump malloc app", command_dump_malloc_app, 0 }, { "dump malloc worker", command_dump_malloc_worker, 0 }, -#endif /* MALLOC_INSTRUMENTATION */ +#endif /* CONFIG_MALLOC_INSTRUMENTATION */ /* { "read word", command_read_word, 1 }, @@ -577,14 +490,17 @@ static const Command s_prompt_commands[] = { { "remote os", command_get_connected_os, 0 }, */ -#ifdef UI_DEBUG +#ifdef CONFIG_UI_DEBUG { "window dump", command_dump_window, 0 }, { "layer nudge", command_layer_nudge, 1 }, #endif + { "backlight level", command_backlight_ctl, 1 }, +#ifdef CONFIG_BACKLIGHT_HAS_COLOR // Drivers - //{ "rgb", command_rgb_set_color, 1 }, + { "backlight color", command_backlight_set_color, 1 }, +#endif // { "watch", command_watch, 0 }, @@ -592,11 +508,9 @@ static const Command s_prompt_commands[] = { { "dump flash", command_dump_flash, 2 }, // { "format flash", command_format_flash, 0 }, -#if !PLATFORM_TINTIN { "flash unprotect", command_flash_unprotect, 0 }, -#endif -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW { "worker launch", command_worker_launch, 1 }, { "worker kill", command_worker_kill, 0}, #endif @@ -620,7 +534,7 @@ static const Command s_prompt_commands[] = { //{ "bt active exit", command_bt_active_exit, 0 }, -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) { "get active app metadata", command_get_active_app_metadata, 0 }, #endif // { "boot bits get", command_boot_bits_get, 0 }, @@ -631,11 +545,11 @@ static const Command s_prompt_commands[] = { // { "animations_l2", command_legacy2_animations_info, 0 }, -// #if !defined(RECOVERY_FW) +// #if !defined(CONFIG_RECOVERY_FW) // { "sim panic", command_sim_panic, 1 }, // #endif -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) { "alarm", command_alarm, 0 }, //{ "now playing", command_print_now_playing, 0 }, @@ -645,11 +559,11 @@ static const Command s_prompt_commands[] = { { "dls wipe", command_dls_erase_all, 0 }, { "dls send", command_dls_send_all, 0 }, -#endif // !RECOVERY_FW +#endif // !defined(CONFIG_RECOVERY_FW) { "dump mpu", memory_layout_dump_mpu_regions_to_dbgserial, 0 }, -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW {"pfs format", pfs_command_fs_format, 1}, {"pfs ls", pfs_command_fs_ls, 0}, // {"pfs cat", pfs_command_cat, 2}, @@ -661,13 +575,14 @@ static const Command s_prompt_commands[] = { // This command is dangerous to your flash. Be careful. {"flash stress", command_flash_stress, 1 }, + {"flash benchmark", command_flash_benchmark, 0 }, #endif { "ping", command_ping_send, 0}, { "runlevel", command_set_runlevel, 1 }, -#if defined(PROFILER) +#if defined(CONFIG_PROFILER) { "profiler start", command_profiler_start, 0 }, { "profiler stop", command_profiler_stop, 0 }, { "profiler stats", command_profiler_stats, 0 }, @@ -678,9 +593,9 @@ static const Command s_prompt_commands[] = { // Removing it will save ~2400 bytes but it is super useful for BT bringup debug! { "gapdb dump", command_gapdb_dump, 0 }, { "sprf nuke", command_bt_sprf_nuke, 0 }, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) { "sprf sync", command_force_shared_prf_flush, 0}, -#endif // !RECOVERY_FW +#endif // !defined(CONFIG_RECOVERY_FW) #endif #if 0 @@ -690,11 +605,11 @@ static const Command s_prompt_commands[] = { #endif { "waste time", command_waste_time, 2 }, -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) { "dump notif_pref_db", command_dump_notif_pref_db, 0 }, #endif -#if PERFORMANCE_TESTS +#ifdef CONFIG_PERFORMANCE_TESTS { "perftest all line", command_perftest_line_all, 0 }, { "perftest all text", command_perftest_text_all, 0 }, { "perftest line", command_perftest_line, 2 }, @@ -702,20 +617,27 @@ static const Command s_prompt_commands[] = { #endif #endif // KEEP_NON_ESSENTIAL_COMMANDS -#if PLATFORM_SILK && !TARGET_QEMU - { "accel samp", command_accel_num_samples, 1 }, - { "accel status", command_accel_status, 0 }, - { "accel selftest", command_accel_selftest, 0 }, - { "accel reset", command_accel_softreset, 0 }, -#endif // PLATFORM_SILK { "vibe", command_vibe_ctl, 1 }, + { "console disable rx", command_console_disable_rx, 1 }, +#ifdef CONFIG_SOC_SF32LB52 + { "force deepwfi", command_force_deepwfi, 1 }, +#endif +#if !defined(CONFIG_RELEASE) && defined(CONFIG_DISPLAY_JDI_SF32LB) + { "display drop_complete", command_display_drop_complete, 0 }, +#endif #if MEMFAULT { "mflt export", command_mflt_export, 0 }, - { "mflt heartbeat", command_mflt_heartbeat, 0 }, + { "mflt collect", command_mflt_collect, 0 }, { "mflt metrics_dump", command_mflt_metrics_dump, 0 }, { "mflt device_info", command_mflt_device_info, 0 }, #endif // MEMFAULT + +#if ANALYTICS_NATIVE + { "analytics native metrics_dump", command_analytics_native_metrics_dump, 0 }, +#endif + + { "analytics heartbeat", command_analytics_heartbeat, 0 }, }; #define NUM_PROMPT_COMMANDS ARRAY_LENGTH(s_prompt_commands) diff --git a/src/fw/console/pulse.c b/src/fw/console/pulse.c index 7de47f46fd..1f35e12997 100644 --- a/src/fw/console/pulse.c +++ b/src/fw/console/pulse.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !PULSE_EVERYWHERE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef CONFIG_PULSE_EVERYWHERE #include "pulse.h" @@ -30,8 +17,8 @@ #include "console/pulse_protocol_impl.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" #include "system/passert.h" #include "util/attributes.h" #include "util/legacy_checksum.h" @@ -170,9 +157,6 @@ void pulse_end(void) { serial_console_set_state(SERIAL_CONSOLE_STATE_LOGGING); } -void pulse_prepare_to_crash(void) { -} - static void prv_process_received_frame(void *frame_ptr) { IncomingPulseFrame *frame = frame_ptr; uint32_t fcs; diff --git a/src/fw/console/pulse.h b/src/fw/console/pulse.h index 7f3cb17b06..d4cc129918 100644 --- a/src/fw/console/pulse.h +++ b/src/fw/console/pulse.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/pulse2.c b/src/fw/console/pulse2.c index 7c807906c8..89849331f1 100644 --- a/src/fw/console/pulse2.c +++ b/src/fw/console/pulse2.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if PULSE_EVERYWHERE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifdef CONFIG_PULSE_EVERYWHERE #include "pulse.h" #include "pulse2_reliable_retransmit_timer.h" @@ -32,7 +19,7 @@ #include "kernel/pebble_tasks.h" #include "mcu/interrupts.h" #include "os/mutex.h" -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" #include "system/passert.h" #include "util/attributes.h" #include "util/crc32.h" @@ -386,11 +373,6 @@ void pulse_end(void) { ppp_control_protocol_close(PULSE2_LCP, PPPCPCloseWait_WaitForClosed); } -void pulse_prepare_to_crash(void) { - // We're crashing so it's not safe to use control_protocol APIs. - prv_forge_terminate_ack(); -} - static void prv_assert_tx_buffer(void *buf) { // Ensure the buffer is actually a PULSE transmit buffer bool buf_valid = false; diff --git a/src/fw/console/pulse2_reliable_protocol_registry.def b/src/fw/console/pulse2_reliable_protocol_registry.def index 37627d5180..03c6cbcccb 100644 --- a/src/fw/console/pulse2_reliable_protocol_registry.def +++ b/src/fw/console/pulse2_reliable_protocol_registry.def @@ -37,7 +37,7 @@ // // void transport_state_handler(void); -#if !DISABLE_PROMPT +#ifdef CONFIG_PROMPT ON_PACKET(PULSE2_RELIABLE_PROMPT_PROTOCOL, pulse2_prompt_packet_handler) #endif diff --git a/src/fw/console/pulse2_reliable_retransmit_timer.h b/src/fw/console/pulse2_reliable_retransmit_timer.h index f38c4b1093..a2753c08bd 100644 --- a/src/fw/console/pulse2_reliable_retransmit_timer.h +++ b/src/fw/console/pulse2_reliable_retransmit_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Retransmit timer for PULSEv2 Reliable Transport diff --git a/src/fw/console/pulse2_transport_impl.h b/src/fw/console/pulse2_transport_impl.h index 150141479b..6fb7154588 100644 --- a/src/fw/console/pulse2_transport_impl.h +++ b/src/fw/console/pulse2_transport_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/pulse_bulkio.c b/src/fw/console/pulse_bulkio.c index 42d8976827..0027c178fe 100644 --- a/src/fw/console/pulse_bulkio.c +++ b/src/fw/console/pulse_bulkio.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/pulse_bulkio_domain_handler.h" #include "console/pulse_protocol_impl.h" @@ -22,7 +9,7 @@ #include #include "kernel/pbl_malloc.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "system/passert.h" #include "util/attributes.h" #include "util/crc32.h" diff --git a/src/fw/console/pulse_bulkio_coredump.c b/src/fw/console/pulse_bulkio_coredump.c index 61f6938633..77ff312103 100644 --- a/src/fw/console/pulse_bulkio_coredump.c +++ b/src/fw/console/pulse_bulkio_coredump.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_bulkio_domain_handler.h" diff --git a/src/fw/console/pulse_bulkio_domain_handler.h b/src/fw/console/pulse_bulkio_domain_handler.h index 6bec3ca847..382355abd2 100644 --- a/src/fw/console/pulse_bulkio_domain_handler.h +++ b/src/fw/console/pulse_bulkio_domain_handler.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system/status_codes.h" diff --git a/src/fw/console/pulse_bulkio_external_flash.c b/src/fw/console/pulse_bulkio_external_flash.c index 243c394012..601255f7f2 100644 --- a/src/fw/console/pulse_bulkio_external_flash.c +++ b/src/fw/console/pulse_bulkio_external_flash.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_bulkio_domain_handler.h" diff --git a/src/fw/console/pulse_bulkio_framebuffer.c b/src/fw/console/pulse_bulkio_framebuffer.c index 648b015f13..f1f32f5a02 100644 --- a/src/fw/console/pulse_bulkio_framebuffer.c +++ b/src/fw/console/pulse_bulkio_framebuffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_bulkio_domain_handler.h" @@ -21,8 +8,8 @@ #include "console/pulse_protocol_impl.h" #include "drivers/display/display.h" #include "kernel/event_loop.h" -#include "services/common/compositor/compositor.h" -#include "services/common/compositor/compositor_display.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/compositor/compositor_display.h" #include "system/status_codes.h" #include "util/attributes.h" @@ -32,8 +19,8 @@ typedef struct PACKED FramebufferStatResp { uint8_t flags; - uint8_t width; - uint8_t height; + uint16_t width; + uint16_t height; uint8_t bpp; uint32_t length; } FramebufferStatResp; @@ -59,7 +46,7 @@ static int framebuffer_domain_stat(uint8_t *resp, size_t resp_max_len, void *con .length = FRAMEBUFFER_SIZE_BYTES, .width = DISP_COLS, .height = DISP_ROWS, - .bpp = SCREEN_COLOR_DEPTH_BITS + .bpp = CONFIG_SCREEN_COLOR_DEPTH_BITS }; return sizeof(FramebufferStatResp); diff --git a/src/fw/console/pulse_bulkio_handler.def b/src/fw/console/pulse_bulkio_handler.def index bba2f99008..6ae05caf6a 100644 --- a/src/fw/console/pulse_bulkio_handler.def +++ b/src/fw/console/pulse_bulkio_handler.def @@ -8,6 +8,6 @@ REGISTER_BULKIO_HANDLER(ExternalFlash, 2, pulse_bulkio_domain_external_flash) REGISTER_BULKIO_HANDLER(Framebuffer, 3, pulse_bulkio_domain_framebuffer) REGISTER_BULKIO_HANDLER(Coredump, 4, pulse_bulkio_domain_coredump) -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) REGISTER_BULKIO_HANDLER(PFS, 5, pulse_bulkio_domain_pfs) #endif diff --git a/src/fw/console/pulse_bulkio_memory.c b/src/fw/console/pulse_bulkio_memory.c index d4b783ba7e..68ddf20e4b 100644 --- a/src/fw/console/pulse_bulkio_memory.c +++ b/src/fw/console/pulse_bulkio_memory.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_bulkio_domain_handler.h" diff --git a/src/fw/console/pulse_bulkio_pfs.c b/src/fw/console/pulse_bulkio_pfs.c index cb13b71d1c..5e466a9b02 100644 --- a/src/fw/console/pulse_bulkio_pfs.c +++ b/src/fw/console/pulse_bulkio_pfs.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_bulkio_domain_handler.h" #include "console/pulse_protocol_impl.h" #include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "system/passert.h" #include "system/status_codes.h" #include "util/attributes.h" @@ -39,7 +26,7 @@ typedef struct PACKED PFSOpenOptions { char filename[0]; } PFSOpenOptions; -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) static int prv_open_file(void *packet_data, size_t length) { PFSOpenOptions *options = packet_data; diff --git a/src/fw/console/pulse_control_message_protocol.c b/src/fw/console/pulse_control_message_protocol.c index 0a7bbd95d4..25cefc8cb3 100644 --- a/src/fw/console/pulse_control_message_protocol.c +++ b/src/fw/console/pulse_control_message_protocol.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE #include "pulse_control_message_protocol.h" diff --git a/src/fw/console/pulse_control_message_protocol.h b/src/fw/console/pulse_control_message_protocol.h index 3593c203e8..e5aff32ad4 100644 --- a/src/fw/console/pulse_control_message_protocol.h +++ b/src/fw/console/pulse_control_message_protocol.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/pulse_flash_imaging.c b/src/fw/console/pulse_flash_imaging.c index 4f0a54d5bd..fe0b342257 100644 --- a/src/fw/console/pulse_flash_imaging.c +++ b/src/fw/console/pulse_flash_imaging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/pulse_protocol_impl.h" @@ -23,7 +10,7 @@ #include "flash_region/flash_region.h" #include "kernel/util/sleep.h" #include "resource/resource_storage_flash.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "system/bootbits.h" #include "util/attributes.h" #include "util/math.h" diff --git a/src/fw/console/pulse_internal.h b/src/fw/console/pulse_internal.h index d9f09d8ca8..cb74918733 100644 --- a/src/fw/console/pulse_internal.h +++ b/src/fw/console/pulse_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include #define PULSE_KEEPALIVE_TIMEOUT_DECISECONDS (30) -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE #define PULSE_MAX_RECEIVE_UNIT (1500) #define PULSE_MIN_FRAME_LENGTH (6) #else @@ -34,9 +21,6 @@ void pulse_start(void); //! End a PULSE session on dbgserial. void pulse_end(void); -//! Terminate the PULSE session in preparation for the firmware to crash. -void pulse_prepare_to_crash(void); - //! PULSE ISR receive character handler. void pulse_handle_character(char c, bool *should_context_switch); diff --git a/src/fw/console/pulse_llc.c b/src/fw/console/pulse_llc.c index d5a740cf91..f8e4e16d5c 100644 --- a/src/fw/console/pulse_llc.c +++ b/src/fw/console/pulse_llc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/src/fw/console/pulse_llc.h b/src/fw/console/pulse_llc.h index b1887dcc21..a259fe33c0 100644 --- a/src/fw/console/pulse_llc.h +++ b/src/fw/console/pulse_llc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/pulse_pp.c b/src/fw/console/pulse_pp.c index ccb376a85f..8c1ca57496 100644 --- a/src/fw/console/pulse_pp.c +++ b/src/fw/console/pulse_pp.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -25,7 +12,7 @@ #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session_transport.h" +#include "pbl/services/comm_session/session_transport.h" #include "system/passert.h" #include "system/logging.h" @@ -91,7 +78,7 @@ static void prv_send_next(Transport *transport) { } static void prv_reset(Transport *transport) { - PBL_LOG(LOG_LEVEL_INFO, "Unimplemented"); + PBL_LOG_INFO("Unimplemented"); } static void prv_granted_kernel_main_cb(void *ctx) { @@ -155,7 +142,7 @@ void pulse_transport_set_connected(bool is_connected) { &s_pulse_transport_implementation, TransportDestinationHybrid); if (!s_transport.session) { - PBL_LOG(LOG_LEVEL_ERROR, "CommSession couldn't be opened"); + PBL_LOG_ERR("CommSession couldn't be opened"); send_event = false; } @@ -196,7 +183,7 @@ static void prv_pulse_pp_handle_data(void *data, size_t length) { bt_lock(); if (!s_transport.session) { - PBL_LOG(LOG_LEVEL_ERROR, "Received PULSE serial data, but session not connected!"); + PBL_LOG_ERR("Received PULSE serial data, but session not connected!"); goto unlock; } comm_session_receive_router_write(s_transport.session, data, length); diff --git a/src/fw/console/pulse_protocol_impl.h b/src/fw/console/pulse_protocol_impl.h index da03276a71..bc271d4626 100644 --- a/src/fw/console/pulse_protocol_impl.h +++ b/src/fw/console/pulse_protocol_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -31,7 +18,7 @@ typedef enum { //! Retrieve the buffer to fill the frame. //! //! @param protocol protocol number -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE void *pulse_best_effort_send_begin(uint16_t protocol); #else void *pulse_best_effort_send_begin(uint8_t protocol); @@ -60,7 +47,7 @@ void pulse_reliable_send_cancel(void *buf); size_t pulse_reliable_max_send_size(void); -#if !PULSE_EVERYWHERE +#ifndef CONFIG_PULSE_EVERYWHERE // PULSEv1 has no equivalent to the PUSH protocol. #define pulse_push_send_begin pulse_best_effort_send_begin #define pulse_push_send pulse_best_effort_send diff --git a/src/fw/console/pulse_protocol_registry.def b/src/fw/console/pulse_protocol_registry.def index e07545590b..886ac70694 100644 --- a/src/fw/console/pulse_protocol_registry.def +++ b/src/fw/console/pulse_protocol_registry.def @@ -23,7 +23,7 @@ // definition. // DO NOT REMOVE THIS PROTOCOL! -#if !PULSE_EVERYWHERE +#ifndef CONFIG_PULSE_EVERYWHERE REGISTER_PROTOCOL(PULSE_PROTOCOL_LLC, pulse_llc_handler, pulse_llc_link_state_handler) REGISTER_PROTOCOL(PULSE_PROTOCOL_PROMPT, pulse_prompt_handler, pulse_prompt_link_state_handler) #endif diff --git a/src/fw/console/push_transport.c b/src/fw/console/push_transport.c index 56fc01c442..a53e822f8e 100644 --- a/src/fw/console/push_transport.c +++ b/src/fw/console/push_transport.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if PULSE_EVERYWHERE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifdef CONFIG_PULSE_EVERYWHERE #include "pulse_protocol_impl.h" diff --git a/src/fw/console/reliable_transport.c b/src/fw/console/reliable_transport.c index 8bc17ff756..8e4d7f2748 100644 --- a/src/fw/console/reliable_transport.c +++ b/src/fw/console/reliable_transport.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if PULSE_EVERYWHERE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifdef CONFIG_PULSE_EVERYWHERE #include "pulse_protocol_impl.h" #include "pulse2_reliable_retransmit_timer.h" @@ -26,7 +13,7 @@ #include "console/pulse_control_message_protocol.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "system/passert.h" #include #include @@ -168,7 +155,7 @@ void pulse2_reliable_transport_on_command_packet( } if (length < sizeof((ReliablePacket){0}.s)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received malformed command packet"); + PBL_LOG_DBG("Received malformed command packet"); prv_bounce_ncp_state(); return; } @@ -177,7 +164,7 @@ void pulse2_reliable_transport_on_command_packet( if (packet->is_supervisory) { if (packet->s.kind != SupervisoryKind_ReceiveReady && packet->s.kind != SupervisoryKind_Reject) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received a command packet of type %" PRIu8 + PBL_LOG_DBG("Received a command packet of type %" PRIu8 " which is not supported by this implementation.", (uint8_t)packet->s.kind); // Pretend it is an RR packet @@ -189,7 +176,7 @@ void pulse2_reliable_transport_on_command_packet( } } else { // Information transfer packet if (length < sizeof(ReliablePacket)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received malformed Information packet"); + PBL_LOG_DBG("Received malformed Information packet"); prv_bounce_ncp_state(); return; } @@ -227,7 +214,7 @@ void pulse2_reliable_transport_on_command_packet( } } } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Received truncated or corrupt info packet " + PBL_LOG_DBG("Received truncated or corrupt info packet " "field (expeced %" PRIu16 ", got %" PRIu16 " data bytes). " "Discarding.", ntoh16(packet->i.length), (uint16_t)length); return; @@ -244,14 +231,14 @@ void pulse2_reliable_transport_on_response_packet( } if (length < sizeof((ReliablePacket){0}.s)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received malformed response packet"); + PBL_LOG_DBG("Received malformed response packet"); prv_bounce_ncp_state(); return; } ReliablePacket *packet = raw_packet; if (!packet->is_supervisory) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received Information packet response; this is " + PBL_LOG_DBG("Received Information packet response; this is " "not permitted by the protocol (Information packets can only be " "commands). Discarding."); return; @@ -261,7 +248,7 @@ void pulse2_reliable_transport_on_response_packet( if (packet->s.kind != SupervisoryKind_ReceiveReady && packet->s.kind != SupervisoryKind_Reject) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received a command packet of type %" PRIu8 + PBL_LOG_DBG("Received a command packet of type %" PRIu8 " which is not supported by this implementation.", (uint8_t)packet->s.kind); } @@ -283,7 +270,7 @@ void pulse2_reliable_retransmit_timer_expired_handler( s_tx_buffer->length); prv_start_retransmit_timer(retransmit_sequence_number); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Reached maximum number of retransmit attempts."); + PBL_LOG_DBG("Reached maximum number of retransmit attempts."); prv_bounce_ncp_state(); } } @@ -309,7 +296,7 @@ void *pulse_reliable_send_begin(const uint16_t app_protocol) { xSemaphoreTake(s_tx_lock, portMAX_DELAY); if (!s_layer_up) { // Transport went down while waiting for the lock - PBL_LOG(LOG_LEVEL_DEBUG, "Transport went down while waiting for lock"); + PBL_LOG_DBG("Transport went down while waiting for lock"); xSemaphoreGive(s_tx_lock); return NULL; } @@ -324,7 +311,7 @@ void pulse_reliable_send_cancel(void *buf) { void pulse_reliable_send(void *buf, const size_t length) { if (!s_layer_up) { - PBL_LOG(LOG_LEVEL_DEBUG, "Transport went down before send"); + PBL_LOG_DBG("Transport went down before send"); return; } prv_assert_reliable_buffer(buf); diff --git a/src/fw/console/serial_console.c b/src/fw/console/serial_console.c index 477e84d1a9..bc1f2d9147 100644 --- a/src/fw/console/serial_console.c +++ b/src/fw/console/serial_console.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "serial_console.h" @@ -20,7 +7,6 @@ #include "console/pulse_protocol_impl.h" #include "console_internal.h" #include "prompt.h" -#include "ui_nudge.h" #include "console/pulse_internal.h" #include "drivers/mic.h" @@ -30,22 +16,20 @@ #include "system/logging.h" #include "system/passert.h" -#include - SerialConsoleState s_serial_console_state = SERIAL_CONSOLE_STATE_LOGGING; static bool s_serial_console_initialized; static bool s_prompt_enabled = false; static void logging_handle_character(char c, bool* should_context_switch) { -#ifdef DISABLE_PROMPT +#ifndef CONFIG_PROMPT return; #endif // Remember, you're in an interrupt here! if (c == 0x3) { // CTRL-C if (!s_prompt_enabled) { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring prompt request, not yet ready!"); + PBL_LOG_DBG("Ignoring prompt request, not yet ready!"); return; } console_switch_to_prompt(); @@ -103,7 +87,7 @@ void serial_console_set_state(SerialConsoleState new_state) { return; } -#if !PULSE_EVERYWHERE +#ifndef CONFIG_PULSE_EVERYWHERE if (new_state == SERIAL_CONSOLE_STATE_LOGGING) { stop_mode_enable(InhibitorDbgSerial); dbgserial_enable_rx_exti(); @@ -115,7 +99,7 @@ void serial_console_set_state(SerialConsoleState new_state) { s_serial_console_state = new_state; switch (s_serial_console_state) { -#if !DISABLE_PROMPT +#ifdef CONFIG_PROMPT case SERIAL_CONSOLE_STATE_PROMPT: dbgserial_register_character_callback(prompt_handle_character); dbgserial_set_rx_dma_enabled(false); @@ -125,16 +109,6 @@ void serial_console_set_state(SerialConsoleState new_state) { dbgserial_register_character_callback(logging_handle_character); dbgserial_set_rx_dma_enabled(false); break; -#ifdef UI_DEBUG - case SERIAL_CONSOLE_STATE_LAYER_NUDGING: - dbgserial_register_character_callback(layer_debug_nudging_handle_character); - dbgserial_set_rx_dma_enabled(false); - break; -#endif - case SERIAL_CONSOLE_STATE_HCI_PASSTHROUGH: - dbgserial_register_character_callback(bt_driver_test_handle_hci_passthrough_character); - dbgserial_set_rx_dma_enabled(false); - break; case SERIAL_CONSOLE_STATE_PULSE: dbgserial_register_character_callback(pulse_handle_character); dbgserial_set_rx_dma_enabled(true); @@ -152,3 +126,8 @@ SerialConsoleState serial_console_get_state(void) { return state; } +void serial_console_set_rx_enabled(bool enabled) { + portENTER_CRITICAL(); + dbgserial_set_input_enabled(enabled); + portEXIT_CRITICAL(); +} diff --git a/src/fw/console/serial_console.h b/src/fw/console/serial_console.h index aa2ac45480..a91e529ee5 100644 --- a/src/fw/console/serial_console.h +++ b/src/fw/console/serial_console.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/console/ui_nudge.c b/src/fw/console/ui_nudge.c deleted file mode 100644 index 84a92b40cf..0000000000 --- a/src/fw/console/ui_nudge.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef UI_DEBUG -#include "ui_nudge.h" - -#include "console_internal.h" -#include "dbgserial.h" - -#include "applib/ui/layer.h" -#include "applib/ui/ui_debug.h" -#include "applib/ui/app_window_stack.h" -#include "applib/ui/window_stack_private.h" - -#include "kernel/events.h" - -#include "services/common/compositor/compositor.h" -#include "util/string.h" - -static Layer *s_nudge_layer = NULL; - -static void prv_flush_kernel_main_cb(void *unused) { - // FIXME compositor_flush(); -} - -void layer_debug_nudging_handle_character(char c, bool *should_context_switch) { - GRect frame = s_nudge_layer->frame; - GRect bounds = s_nudge_layer->bounds; - switch (c) { - case 0x3: // CTRL - C - s_nudge_layer = NULL; - // Back to log mode: - serial_console_set_state(SERIAL_CONSOLE_STATE_LOGGING); - // Dump window: - command_dump_window(); - return; - - case 'A': - case 'a': - --frame.origin.x; - break; - - case 'D': - case 'd': - ++frame.origin.x; - break; - - case 'W': - case 'w': - ++frame.origin.y; - break; - - case 'S': - case 's': - --frame.origin.y; - break; - - case '[': - --frame.size.w; - bounds.size = frame.size; - break; - - case ']': - ++frame.size.w; - bounds.size = frame.size; - break; - - case '{': - --frame.size.h; - bounds.size = frame.size; - break; - - case '}': - ++frame.size.h; - bounds.size = frame.size; - break; - - default: - break; - } - layer_set_frame(s_nudge_layer, &frame); - layer_set_bounds(s_nudge_layer, &bounds); - - PebbleEvent event = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_flush_kernel_main_cb, - .data = NULL, - }, - }; - *should_context_switch = event_put_isr(&event); -} - -void command_layer_nudge(const char *address_str) { - intptr_t address = str_to_address(address_str); - if (address == -1) { - return; - } - // Simple sanity check: - if (((Layer *)address)->window != app_window_stack_get_top_window()) { - dbgserial_putstr("Specify valid Layer address!"); - return; - } - s_nudge_layer = (Layer *)address; - - dbgserial_putstr("Layer nudging mode, CTRL-C to stop"); - dbgserial_putstr("Keys:\nWASD: Move frame.origin\n[]: Change frame.size.w & bounds.size.w\n{}: Change frame.size.h & bounds.size.h"); - serial_console_set_state(SERIAL_CONSOLE_STATE_LAYER_NUDGING); -} -#endif /* UI_DEBUG */ diff --git a/src/fw/console/ui_nudge.h b/src/fw/console/ui_nudge.h deleted file mode 100644 index de00e43e06..0000000000 --- a/src/fw/console/ui_nudge.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include - -//! Enters nudging mode with the layer at given address -void command_layer_nudge(const char *address_str); - -//! Prompt character handler -void layer_debug_nudging_handle_character(char c, bool *should_context_switch); diff --git a/src/fw/debug/advanced_logging.c b/src/fw/debug/advanced_logging.c index 23421c5c83..f1db21df28 100644 --- a/src/fw/debug/advanced_logging.c +++ b/src/fw/debug/advanced_logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "flash_logging.h" @@ -22,7 +9,7 @@ #include "system/passert.h" #include "util/shared_circular_buffer.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" static SharedCircularBuffer s_buffer; static SharedCircularBufferClient s_buffer_client; @@ -86,10 +73,30 @@ static void handle_buffer_sync(void *data) { mutex_lock(s_flash_write_mutex); mutex_lock(s_buffer_mutex); - while (shared_circular_buffer_get_read_space_remaining(&s_buffer, &s_buffer_client) > 0) { + // Bound the drain to what was already pending when this callback ran. Each + // write_message() releases the buffer mutex around the flash write so other + // tasks (BT, KernelMain) can keep producing log lines; if we kept the outer + // while running on the live "remaining" count, a chatty producer can keep + // this callback executing on KernelBackground forever. That starves every + // other system-task callback (PutBytes processing in particular) and the + // emulator wedges with no progress and no watchdog reboot. Drain the + // initial backlog and leave any newly-arrived bytes for the next callback; + // the s_is_flash_write_scheduled flag below ensures producers re-arm us. + size_t budget = shared_circular_buffer_get_read_space_remaining(&s_buffer, &s_buffer_client); + while (budget > 0 && + shared_circular_buffer_get_read_space_remaining(&s_buffer, &s_buffer_client) > 0) { + size_t before = shared_circular_buffer_get_read_space_remaining(&s_buffer, &s_buffer_client); write_message(); // The above function mucks with the mutex mutex_assert_held_by_curr_task(s_buffer_mutex, true /* is_held */); + size_t after = shared_circular_buffer_get_read_space_remaining(&s_buffer, &s_buffer_client); + if (after >= before) { + // write_message() didn't make progress (e.g. partial message not ready); + // bail rather than spinning. + break; + } + size_t consumed = before - after; + budget = (consumed >= budget) ? 0 : budget - consumed; } if (is_async) { diff --git a/src/fw/debug/advanced_logging.h b/src/fw/debug/advanced_logging.h index 84d08b7d80..d65f6f3d48 100644 --- a/src/fw/debug/advanced_logging.h +++ b/src/fw/debug/advanced_logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/debug/app_logging.c b/src/fw/debug/app_logging.c index 10c0971965..f6527a15d7 100644 --- a/src/fw/debug/app_logging.c +++ b/src/fw/debug/app_logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/attributes.h" #include "system/logging.h" @@ -23,13 +10,17 @@ #include "kernel/logging_private.h" #include "kernel/memory_layout.h" #include "kernel/util/stack_info.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" #include "syscall/syscall_internal.h" static const uint16_t APP_LOGGING_ENDPOINT = 2006; static AppLoggingMode s_app_logging_mode = AppLoggingDisabled; +bool app_log_is_bt_enabled(void) { + return s_app_logging_mode == AppLoggingEnabled; +} + static const uint32_t MIN_STACK_FOR_SEND_DATA = 400; DEFINE_SYSCALL(void, sys_app_log, size_t length, void *log_buffer) { @@ -76,7 +67,7 @@ void app_log_protocol_msg_callback(CommSession *session, const uint8_t *data, co s_app_logging_mode = AppLoggingDisabled; break; default: - PBL_LOG(LOG_LEVEL_WARNING, "Invalid app log command 0x%x", command->commandType); + PBL_LOG_WRN("Invalid app log command 0x%x", command->commandType); } } diff --git a/src/fw/debug/debug.c b/src/fw/debug/debug.c index a823d355ab..9011a9b5dd 100644 --- a/src/fw/debug/debug.c +++ b/src/fw/debug/debug.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug.h" #include "advanced_logging.h" @@ -31,11 +18,11 @@ #endif #include "mfg/mfg_serials.h" #include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/system_task.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/process_management/app_storage.h" #include "system/bootbits.h" #include "system/hexdump.h" #include "system/logging.h" @@ -85,7 +72,7 @@ static bool prv_bt_log_dump_line_cb(uint8_t *message, uint32_t total_length) { SendBuffer *sb = comm_session_send_buffer_begin_write(session, ENDPOINT_ID, required_length, COMM_SESSION_DEFAULT_TIMEOUT); if (!sb) { - PBL_LOG(LOG_LEVEL_DEBUG, "Failed to get send buffer"); + PBL_LOG_DBG("Failed to get send buffer"); return false; } @@ -131,7 +118,7 @@ static void prv_flash_logging_bluetooth_dump( CommSession *session, int generation, uint32_t cookie) { PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground); if (s_bt_dump_chunk_callback_data.in_progress) { - PBL_LOG(LOG_LEVEL_ERROR, "Already in the middle of dumping logs"); + PBL_LOG_ERR("Already in the middle of dumping logs"); return; } @@ -155,7 +142,7 @@ void dump_log_protocol_msg_callback(CommSession *session, const uint8_t* data, s int generation = 0; if (data[0] == 0x10 || data[0] == 0x11) { if (length != 6) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid dump log message received -- length %u", length); + PBL_LOG_ERR("Invalid dump log message received -- length %u", length); return; } @@ -163,7 +150,7 @@ void dump_log_protocol_msg_callback(CommSession *session, const uint8_t* data, s cookie = *((uint32_t*) (data + 2)); } else { if (length != 5) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid dump log message received -- length %u", length); + PBL_LOG_ERR("Invalid dump log message received -- length %u", length); return; } @@ -191,17 +178,20 @@ void debug_init(McuRebootReason mcu_reboot_reason) { advanced_logging_init(); // Log the firmware version in the first flash log line: - DEBUG_LOG(LOG_LEVEL_INFO, "%s (platform: %u, hw: %s, sn: %s, pcba: %s)", - TINTIN_METADATA.version_tag, - TINTIN_METADATA.hw_platform, - mfg_get_hw_version(), - mfg_get_serial_number(), - mfg_get_pcba_serial_number()); + PBL_LOG_ALWAYS("Firmware version: %s", TINTIN_METADATA.version_tag); + PBL_LOG_ALWAYS("Platform: %u, hw: %s, sn: %s", + TINTIN_METADATA.hw_platform, + mfg_get_hw_version(), + mfg_get_serial_number()); // Log the firmware build id to flash: char build_id_string[64]; version_copy_current_build_id_hex_string(build_id_string, 64); - DEBUG_LOG(LOG_LEVEL_INFO, "BUILD ID: %s", build_id_string); + PBL_LOG_ALWAYS("BUILD ID: %s", build_id_string); + +#ifdef CONFIG_PBLBOOT + PBL_LOG_ALWAYS("Boot slot: %d", TINTIN_METADATA.is_slot_0 ? 0 : 1); +#endif #if MEMFAULT // This must be called before debug_reboot_reason_print which resets the reason @@ -218,7 +208,7 @@ void debug_print_last_launched_app(void) { // check if last app launched was a system app if (last_launched_app_slot == (uint32_t)SYSTEM_APP_BANK_ID) { - DEBUG_LOG(LOG_LEVEL_INFO, "Last launched app: "); + PBL_LOG_INFO("Last launched app: "); } else if ((last_launched_app_slot != (uint32_t)INVALID_BANK_ID)) { PebbleProcessInfo last_launched_app; uint8_t build_id[BUILD_ID_EXPECTED_LEN]; @@ -228,7 +218,7 @@ void debug_print_last_launched_app(void) { PebbleTask_App); if (result == GET_APP_INFO_SUCCESS) { - DEBUG_LOG(LOG_LEVEL_INFO, "Last launched app: %s", last_launched_app.name); + PBL_LOG_INFO("Last launched app: %s", last_launched_app.name); PBL_HEXDUMP(LOG_LEVEL_INFO, build_id, sizeof(build_id)); } } diff --git a/src/fw/debug/debug.h b/src/fw/debug/debug.h index 628f78055d..e6108cef91 100644 --- a/src/fw/debug/debug.h +++ b/src/fw/debug/debug.h @@ -1,33 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "drivers/mcu_reboot_reason.h" #include -// TODO: Eventually move debug logging back to hashed logging -// Currently broken out to directly log strings without hashing -#ifdef PBL_LOG_ENABLED - #define DEBUG_LOG(level, fmt, ...) \ - pbl_log(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__) -#else - #define DEBUG_LOG(level, fmt, ...) -#endif - void debug_init(McuRebootReason reason); void debug_print_last_launched_app(void); diff --git a/src/fw/debug/debug_reboot_reason.c b/src/fw/debug/debug_reboot_reason.c index e206ecd4f9..da80d04dc0 100644 --- a/src/fw/debug/debug_reboot_reason.c +++ b/src/fw/debug/debug_reboot_reason.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug.h" #include "advanced_logging.h" @@ -26,16 +13,10 @@ #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "popups/crashed_ui.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" #include "system/logging.h" #include "system/reboot_reason.h" -static void log_reboot_reason_cb(void *reason) { - AnalyticsEventCrash *crash_report = (AnalyticsEventCrash *)reason; - analytics_event_crash(crash_report->crash_code, crash_report->link_register); - kernel_free(crash_report); -} - static RebootReasonCode s_last_reboot_reason_code = RebootReasonCode_Unknown; RebootReasonCode reboot_reason_get_last_reboot_reason(void) { return s_last_reboot_reason_code; @@ -44,7 +25,6 @@ RebootReasonCode reboot_reason_get_last_reboot_reason(void) { void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { RebootReason reason; reboot_reason_get(&reason); - bool show_reset_alert = !reason.restarted_safely; s_last_reboot_reason_code = reason.code; // We're out of flash space, scrape a few bytes back! @@ -55,18 +35,12 @@ void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { restarted_safely_string = "Dangerously"; } - // Keep hourly logging to keep track of hours without crashes. - analytics_set(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_CODE, - 0xDEAD0000 | reason.code, AnalyticsClient_System); - uint32_t lr = reason.extra.value; - // Leave this NULL to do your own printing. const char *reason_string = NULL; switch (reason.code) { // Normal stuff case RebootReasonCode_Unknown: reason_string = "We don't know why we %s rebooted."; - lr = mcu_reboot_reason.reset_mask; break; case RebootReasonCode_LowBattery: reason_string = "%s%sLowBattery"; @@ -77,7 +51,6 @@ void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { break; case RebootReasonCode_ResetButtonsHeld: // Since we forced the reset, it isn't unexpected - show_reset_alert = false; reason_string = "%s%sResetButtonsHeld"; break; case RebootReasonCode_ShutdownMenuItem: @@ -106,30 +79,24 @@ void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { break; // Error occurred case RebootReasonCode_Assert: - show_reset_alert = true; reason_string = "%s%sAssert: LR %#"PRIxPTR; break; case RebootReasonCode_HardFault: - show_reset_alert = true; reason_string = "%s%sHardFault: LR %#"PRIxPTR; break; case RebootReasonCode_LauncherPanic: - show_reset_alert = true; reason_string = "%s%sLauncherPanic: code 0x%"PRIx32; break; case RebootReasonCode_ClockFailure: reason_string = "%s%sClock Failure"; break; case RebootReasonCode_WorkerHardFault: - show_reset_alert = true; reason_string = "%s%sWorker HardFault"; break; case RebootReasonCode_OutOfMemory: - show_reset_alert = true; reason_string = "%s%sOOM"; break; case RebootReasonCode_BtCoredump: - show_reset_alert = true; reason_string = "%s%sBT Coredump"; break; default: @@ -137,30 +104,27 @@ void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { break; // Error occurred case RebootReasonCode_Watchdog: - show_reset_alert = true; - DEBUG_LOG(LOG_LEVEL_INFO, "%s%sWatchdog: Bits 0x%" PRIx8 ", Mask 0x%" PRIx8, + PBL_LOG_WRN("%s%sWatchdog: Bits 0x%" PRIx8 ", Mask 0x%" PRIx8, restarted_safely_string, rebooted_due_to, reason.data8[0], reason.data8[1]); if (reason.watchdog.stuck_task_pc != 0) { - DEBUG_LOG(LOG_LEVEL_INFO, "Stuck task PC: 0x%" PRIx32 ", LR: 0x%" PRIx32, + PBL_LOG_WRN("Stuck task PC: 0x%" PRIx32 ", LR: 0x%" PRIx32, reason.watchdog.stuck_task_pc, reason.watchdog.stuck_task_lr); if (reason.watchdog.stuck_task_callback) { - DEBUG_LOG(LOG_LEVEL_INFO, "Stuck callback: 0x%" PRIx32, + PBL_LOG_WRN("Stuck callback: 0x%" PRIx32, reason.watchdog.stuck_task_callback); } } break; case RebootReasonCode_StackOverflow: - show_reset_alert = true; PebbleTask task = (PebbleTask) reason.data8[0]; - DEBUG_LOG(LOG_LEVEL_INFO, "%s%sStackOverflow: Task #%d (%s)", restarted_safely_string, - rebooted_due_to, task, pebble_task_get_name(task)); + PBL_LOG_WRN("%s%sStackOverflow: Task #%d", restarted_safely_string, + rebooted_due_to, task); break; case RebootReasonCode_EventQueueFull: - show_reset_alert = true; - DEBUG_LOG(LOG_LEVEL_INFO, "%s%sEvent Queue Full", restarted_safely_string, rebooted_due_to); - DEBUG_LOG(LOG_LEVEL_INFO, "LR: 0x%"PRIx32" Current: 0x%"PRIx32" Dropped: 0x%"PRIx32, + PBL_LOG_WRN("%s%sEvent Queue Full", restarted_safely_string, rebooted_due_to); + PBL_LOG_WRN("LR: 0x%"PRIx32" Current: 0x%"PRIx32" Dropped: 0x%"PRIx32, reason.event_queue.push_lr, reason.event_queue.current_event, reason.event_queue.dropped_event); @@ -168,29 +132,19 @@ void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { } // Generic reason string if (reason_string) { - DEBUG_LOG(LOG_LEVEL_INFO, reason_string, restarted_safely_string, rebooted_due_to, - reason.extra.value); + pbl_log(LOG_LEVEL_WARNING, __FILE__, __LINE__, reason_string, + restarted_safely_string, rebooted_due_to, reason.extra.value); } - analytics_set(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_LR, lr, AnalyticsClient_System); - - // We need to wait for the logging service to initialize. - AnalyticsEventCrash *crash_report = kernel_malloc_check(sizeof(AnalyticsEventCrash)); - *crash_report = (AnalyticsEventCrash) { - .crash_code = reason.code, - .link_register = lr - }; - launcher_task_add_callback(log_reboot_reason_cb, crash_report); - if (is_unread_coredump_available()) { - DEBUG_LOG(LOG_LEVEL_INFO, "Unread coredump file is present!"); + PBL_LOG_INFO("Unread coredump file is present!"); } - DEBUG_LOG(LOG_LEVEL_INFO, "MCU reset reason mask: 0x%x", (int)mcu_reboot_reason.reset_mask); -#if CAPABILITY_HAS_PMIC + PBL_LOG_INFO("MCU reset reason mask: 0x%x", (int)mcu_reboot_reason.reset_mask); +#ifdef CONFIG_PMIC uint32_t pmic_reset_reason = pmic_get_last_reset_reason(); if (pmic_reset_reason != 0) { - DEBUG_LOG(LOG_LEVEL_INFO, "PMIC reset reason mask: 0x%x", (int)pmic_reset_reason); + PBL_LOG_INFO("PMIC reset reason mask: 0x%x", (int)pmic_reset_reason); } #endif @@ -199,13 +153,5 @@ void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) { crashed_ui_show_forced_core_dump(); } -#ifdef SHOW_PEBBLE_JUST_RESET_ALERT - // Trigger an alert display so that the user knows the watch rebooted due to a crash. This event - // will be caught and handled by the launcher.c event loop. - if (show_reset_alert && reason.code != RebootReasonCode_ForcedCoreDump) { - crashed_ui_show_pebble_reset(); - } -#endif - reboot_reason_clear(); } diff --git a/src/fw/debug/debug_reboot_reason.h b/src/fw/debug/debug_reboot_reason.h index 8d1583f03d..164396a3c6 100644 --- a/src/fw/debug/debug_reboot_reason.h +++ b/src/fw/debug/debug_reboot_reason.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/debug/default/flash_logging.c b/src/fw/debug/default/flash_logging.c index c823e67ff5..77925012f6 100644 --- a/src/fw/debug/default/flash_logging.c +++ b/src/fw/debug/default/flash_logging.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug/flash_logging.h" #include "drivers/flash.h" #include "flash_region/flash_region.h" #include "kernel/pbl_malloc.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "syscall/syscall.h" #include "system/logging.h" #include "system/passert.h" @@ -168,9 +155,7 @@ static uint8_t prv_get_next_log_file_id(uint8_t file_id) { } static uint32_t prv_get_unit_base_address(uint32_t addr) { -#if PLATFORM_SNOWY || PLATFORM_SPALDING - return flash_get_sector_base_address(addr); -#elif PLATFORM_SILK || PLATFORM_ASTERIX || PLATFORM_OBELIX || PLATFORM_CALCULUS || PLATFORM_ROBERT +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) || defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_FLINT) || defined(CONFIG_BOARD_QEMU_GABBRO) return flash_get_subsector_base_address(addr); #else #error "Invalid platform!" @@ -178,9 +163,7 @@ static uint32_t prv_get_unit_base_address(uint32_t addr) { } static void prv_erase_unit(uint32_t addr) { -#if PLATFORM_SNOWY || PLATFORM_SPALDING - flash_erase_sector_blocking(addr); -#elif PLATFORM_SILK || PLATFORM_ASTERIX || PLATFORM_OBELIX || PLATFORM_CALCULUS || PLATFORM_ROBERT +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) || defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_FLINT) || defined(CONFIG_BOARD_QEMU_GABBRO) flash_erase_subsector_blocking(addr); #else #error "Invalid platform!" @@ -324,7 +307,8 @@ void flash_logging_init(void) { bool multiple_gens_found = false; for (uint32_t offset = 0; offset < LOG_REGION_SIZE; offset += LOG_PAGE_SIZE) { - uint32_t flash_addr = prv_get_page_addr(first_used_region, offset); + uint32_t flash_addr = + prv_get_page_addr(FLASH_REGION_DEBUG_DB_BEGIN + first_used_region, offset); FlashLoggingHeader hdr; flash_read_bytes((uint8_t *)&hdr, flash_addr, sizeof(hdr)); @@ -519,7 +503,7 @@ static void prv_dump_log_system_cb(void *context) { status = DumpStatus_DoneSuccess; } else { status = DumpStatus_InProgress; - PBL_LOG(LOG_LEVEL_DEBUG, "Dumping page %d of %d", state->page_index, state->num_pages-1); + PBL_LOG_DBG("Dumping page %d of %d", state->page_index, state->num_pages-1); } } @@ -540,7 +524,7 @@ bool flash_dump_log_file(int generation, DumpLineCallback line_cb, uint32_t log_start_addr; int num_log_pages = prv_get_start_of_log_file(log_file_id, &log_start_addr); - PBL_LOG(LOG_LEVEL_DEBUG, "Dumping generation %d, %d pages", generation, num_log_pages); + PBL_LOG_DBG("Dumping generation %d, %d pages", generation, num_log_pages); if (num_log_pages == 0) { completed_cb(false); diff --git a/src/fw/debug/flash_logging.h b/src/fw/debug/flash_logging.h index 2783d3d5e4..6f186da0f7 100644 --- a/src/fw/debug/flash_logging.h +++ b/src/fw/debug/flash_logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/debug/legacy/debug_db.c b/src/fw/debug/legacy/debug_db.c index b45d25853e..dfbcd3b7df 100644 --- a/src/fw/debug/legacy/debug_db.c +++ b/src/fw/debug/legacy/debug_db.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug_db.h" @@ -160,7 +147,7 @@ void debug_db_init(void) { debug_db_determine_current_index(file_id, &s_current_file_index, &s_current_file_id); - PBL_LOG(LOG_LEVEL_DEBUG, "Found files {%u, %u, %u, %u}, using file %u with new id %u", + PBL_LOG_DBG("Found files {%u, %u, %u, %u}, using file %u with new id %u", file_id[0], file_id[1], file_id[2], file_id[3], s_current_file_index, s_current_file_id); debug_db_reformat_header_section(); @@ -181,7 +168,7 @@ bool debug_db_is_generation_valid(int file_generation) { } if (file_header.file_id != (s_current_file_id - file_generation)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Id: %"PRIu8" Expected: %u", file_header.file_id, (s_current_file_id - file_generation)); + PBL_LOG_DBG("Id: %"PRIu8" Expected: %u", file_header.file_id, (s_current_file_id - file_generation)); return false; } diff --git a/src/fw/debug/legacy/debug_db.h b/src/fw/debug/legacy/debug_db.h index 8dabe9c33d..6d72e0d377 100644 --- a/src/fw/debug/legacy/debug_db.h +++ b/src/fw/debug/legacy/debug_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/debug/legacy/flash_logging.c b/src/fw/debug/legacy/flash_logging.c index 720aff7845..db6fe0e05a 100644 --- a/src/fw/debug/legacy/flash_logging.c +++ b/src/fw/debug/legacy/flash_logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug_db.h" diff --git a/src/fw/debug/power_tracking.c b/src/fw/debug/power_tracking.c index 93973b87d9..da776ad554 100644 --- a/src/fw/debug/power_tracking.c +++ b/src/fw/debug/power_tracking.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug/power_tracking.h" #include "drivers/rtc.h" -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" #include "system/logging.h" #include "system/passert.h" @@ -70,11 +57,7 @@ static DiscreteSystemProfile s_discrete_consumer_profiles[num_power_systems] = { [PowerSystemMcuI2C1] = { "McuI2C1", 0, 0, false}, [PowerSystemMcuI2C2] = { "McuI2C2", 0, 0, false}, [PowerSystemMcuSpi1] = { "McuSpi1", 0, 0, false}, -#ifdef PLATFORM_TINTIN - [PowerSystemMcuSpi2] = { "McuSpi2", 0, 0, false}, -#else [PowerSystemMcuSpi6] = { "McuSpi6", 0, 0, false}, -#endif [PowerSystemMcuAdc1] = { "McuAdc1", 0, 0, false}, [PowerSystemMcuAdc2] = { "McuAdc2", 0, 0, false}, [PowerSystemFlashRead] = { "FlashRead", 0, 0, false}, @@ -148,7 +131,7 @@ void power_tracking_start(PowerSystem system) { DiscreteSystemProfile *current_profile = &s_discrete_consumer_profiles[system]; if (current_profile->start_ticks != 0) { - PBL_LOG(LOG_LEVEL_WARNING, "repeat call to start ticks without stopping from %s", current_profile->name); + PBL_LOG_WRN("repeat call to start ticks without stopping from %s", current_profile->name); // Someone was careless: two cases: // 1) someone forgot to call stop // 2) someone re-enters a function that calls start before stop is called. @@ -169,7 +152,7 @@ void power_tracking_stop(PowerSystem system) { DiscreteSystemProfile *current_profile = &s_discrete_consumer_profiles[system]; if (current_profile->start_ticks == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Stop ticks before start called: probably losing profile accuracy in %s", current_profile->name); + PBL_LOG_WRN("Stop ticks before start called: probably losing profile accuracy in %s", current_profile->name); // Someone was careless: two cases: // 1) someone forgot to call start // 2) someone re-entered a function that called stop already, so it is called twice. diff --git a/src/fw/debug/power_tracking.h b/src/fw/debug/power_tracking.h index 947886bdf3..522dc2c765 100644 --- a/src/fw/debug/power_tracking.h +++ b/src/fw/debug/power_tracking.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -67,11 +54,7 @@ typedef enum { PowerSystemMcuI2C1, // Main I2C PowerSystemMcuI2C2, // 2V5 I2C PowerSystemMcuSpi1, // FLASH -#if PLATFORM_TINTIN || PLATFORM_SILK - PowerSystemMcuSpi2, // LCD -#else PowerSystemMcuSpi6, // LCD -#endif PowerSystemMcuAdc1, // Voltage monitoring & ambient light sensing PowerSystemMcuAdc2, // Voltage monitoring & ambient light sensing PowerSystemFlashRead, diff --git a/src/fw/debug/setup.c b/src/fw/debug/setup.c deleted file mode 100644 index e8458716b4..0000000000 --- a/src/fw/debug/setup.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "setup.h" - -#include "kernel/util/stop.h" -#include "system/logging.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - -void enable_mcu_debugging(void) { -#ifndef RELEASE -#if defined(MICRO_FAMILY_NRF52840) -// NRF_APPROTECT->APPROTECT.DISABLE = 1; -#elif defined(MICRO_FAMILY_SF32LB52) -// TODO(SF32LB52): implement -#else - DBGMCU_Config(DBGMCU_SLEEP | DBGMCU_STOP, ENABLE); - // Stop RTC, IWDG & TIM2 during debugging - // Note: TIM2 is used by the task watchdog - DBGMCU_APB1PeriphConfig(DBGMCU_RTC_STOP | DBGMCU_TIM2_STOP | DBGMCU_IWDG_STOP, - ENABLE); -#endif -#endif -} - -void disable_mcu_debugging(void) { -#if defined(MICRO_FAMILY_NRF52840) -// NRF_APPROTECT->APPROTECT.DISABLE = 0; -#elif MICRO_FAMILY_SF32LB52 -// TODO(SF32LB52): implement -#else - DBGMCU->CR = 0; - DBGMCU->APB1FZ = 0; - DBGMCU->APB2FZ = 0; -#endif -} - -void command_low_power_debug(char *cmd) { - bool low_power_debug_on = (strcmp(cmd, "on") == 0); - -#ifdef MICRO_FAMILY_STM32F4 - sleep_mode_enable(!low_power_debug_on); -#endif - - if (low_power_debug_on) { - enable_mcu_debugging(); - } else { - disable_mcu_debugging(); - } -} diff --git a/src/fw/debug/setup.h b/src/fw/debug/setup.h deleted file mode 100644 index 427ffd8de3..0000000000 --- a/src/fw/debug/setup.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void enable_mcu_debugging(void); - -void disable_mcu_debugging(void); - diff --git a/src/fw/drivers/Kconfig b/src/fw/drivers/Kconfig new file mode 100644 index 0000000000..623a8627d8 --- /dev/null +++ b/src/fw/drivers/Kconfig @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menu "Drivers" + +rsource "ambient/Kconfig" +rsource "backlight/Kconfig" +rsource "battery/Kconfig" +rsource "cpumode/Kconfig" +rsource "display/Kconfig" +rsource "exti/Kconfig" +rsource "flash/Kconfig" +rsource "gpio/Kconfig" +rsource "hrm/Kconfig" +rsource "i2c/Kconfig" +rsource "imu/Kconfig" +rsource "mic/Kconfig" +rsource "otp/Kconfig" +rsource "pmic/Kconfig" +rsource "pressure/Kconfig" +rsource "pwm/Kconfig" +rsource "rng/Kconfig" +rsource "rtc/Kconfig" +rsource "speaker/Kconfig" +rsource "temperature/Kconfig" +rsource "touch/Kconfig" +rsource "uart/Kconfig" +rsource "vibe/Kconfig" +rsource "watchdog/Kconfig" + +endmenu diff --git a/src/fw/drivers/README.md b/src/fw/drivers/README.md deleted file mode 100644 index d493b055be..0000000000 --- a/src/fw/drivers/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Before editing a driver or writing a new one, please read through the -[driver coding guidelines on Confluence](https://pebbletechnology.atlassian.net/wiki/x/tIFQAw). diff --git a/src/fw/drivers/accel.h b/src/fw/drivers/accel.h index dd9fc8e119..ea69c938d9 100644 --- a/src/fw/drivers/accel.h +++ b/src/fw/drivers/accel.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/imu/units.h" +#include "pbl/services/imu/units.h" #include #include @@ -50,23 +37,6 @@ * and the higher-level code can work unmodified with different accelerometers. */ -//! Information which varies by accelerometer driver. -typedef struct { - //! Highest sample interval (slowest rate) supported by the driver. - uint32_t sample_interval_max; - //! Recommended sample interval for low-power use, around 100,000 us. - uint32_t sample_interval_low_power; - //! Recommended sample interval for interactive use, around 250,000 us. - uint32_t sample_interval_ui; - //! Recommended sample interval for games and fast interactivity, - //! around 20,000 us. - uint32_t sample_interval_game; - //! Lowest sample interval (fastest rate) supported by the driver. - uint32_t sample_interval_min; -} AccelDriverInfo; - -extern const AccelDriverInfo ACCEL_DRIVER_INFO; - typedef struct { //! Timestamp of when the sample was taken in microseconds since the epoch. //! The precision of the timestamp is not guaranteed. @@ -79,6 +49,20 @@ typedef struct { int16_t z; } AccelDriverSample; +//! Initialize accelerometer +void accel_init(void); + +//! Power up the accelerometer hardware +void accel_power_up(void); + +//! Power down the accelerometer hardware +void accel_power_down(void); + +//! Set the accelerometer axes to be rotated 180deg +//! +//! @param rotated Axes are rotated or not (true/false). +void accel_set_rotated(bool rotated); + //! Sets the accelerometer sampling interval. //! //! Not all sampling intervals are supported by all drivers. The driver must @@ -199,12 +183,19 @@ extern void accel_cb_double_tap_detected(IMUCoordinateAxis axis, int32_t directi typedef void (*AccelOffloadCallback)(void); extern void accel_offload_work_from_isr(AccelOffloadCallback cb, bool *should_context_switch); -//! Function runs a diagnostic test on the accelerometer hardware to confirm it -//! works as expected -extern bool accel_run_selftest(void); +//! Function called by driver when it needs to offload work. +//! It is up to the implementer to decide how this should work +//! +//! @param cb The callback to be invoked from a thread context +extern void accel_offload_work(AccelOffloadCallback cb); //! The accelerometer supports a changeable sensitivity for shake detection. This call will //! select whether we want the accelerometer to enter a highly sensitive state with a low //! threshold, where any minor amount of motion would trigger the system shake event. //! Note: Setting this value does not ensure that shake detection is enabled. void accel_set_shake_sensitivity_high(bool sensitivity_high); + + +//! Update the accelerometer shake sensitivity as a percentage value from 0 to 100. +//! Note: Setting this value does not ensure that shake detection is enabled. +void accel_set_shake_sensitivity_percent(uint8_t percent); diff --git a/src/fw/drivers/accessory.c b/src/fw/drivers/accessory.c deleted file mode 100644 index d958af9a0c..0000000000 --- a/src/fw/drivers/accessory.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/accessory.h" - -#include "board/board.h" -#include "console/console_internal.h" -#include "console/serial_console.h" -#include "console/prompt.h" -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/exti.h" -#include "drivers/periph_config.h" -#include "drivers/uart.h" -#include "kernel/util/stop.h" -#include "mcu/interrupts.h" -#include "os/mutex.h" -#include "os/tick.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/delay.h" -#include "kernel/util/sleep.h" -#include "util/attributes.h" -#include "util/likely.h" -#include "util/size.h" - -#include "FreeRTOS.h" /* FreeRTOS Kernal Prototypes/Constants. */ -#include "semphr.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include -#include -#include - - -//! The default baudrate for the accessory UART. -#define DEFAULT_BAUD AccessoryBaud115200 -//! How long each interval should be in milliseconds. -#define ACCESSORY_STOP_INTERVAL_PERIOD_MS (250) -//! How many intervals we should wait outside of stop mode when we first see any noise on the -//! serial port. -#define ACCESSORY_INITIAL_STOP_INTERVALS (500 / ACCESSORY_STOP_INTERVAL_PERIOD_MS) -//! How many intervals we should wait outside of stop mode when we first see valid data on the -//! serial port. -#define ACCESSORY_VALID_DATA_STOP_INTERVALS (3000 / ACCESSORY_STOP_INTERVAL_PERIOD_MS) -//! How many bytes of send history to keep. This needs to be 3 bytes because the TX buffer will be -//! moved into the shift register (with a new byte being loaded into the buffer) before we receive -//! the byte we previously sent. So, when we receive a byte, we will have sent 2 more bytes by then. -#define SEND_HISTORY_LENGTH (1) -//! Within accessory_send_stream(), how long we wait for a byte to be sent before timing-out. -#define SEND_BYTE_TIMEOUT_MS (100) - -//! We DMA into this buffer as a circular buffer -#define RX_BUFFER_LENGTH (200) -static uint8_t DMA_BSS s_rx_buffer[RX_BUFFER_LENGTH]; - -//! The current baud rate -static uint32_t s_baudrate; -//! Whether or not the accessory power is enabled -static bool s_power_enabled; -//! Whether or not we are in input mode (receiving) -static bool s_input_enabled; -//! We'll store up to the last 3 bytes which were sent for detecting bus contention -typedef struct { - uint8_t data; - bool has_data; -} SendHistory; -static volatile SendHistory s_send_history; -//! Flag which states whether or not we've detected bus contention since last disabling input -static volatile bool s_bus_contention_detected; -//! Whether or not we sent data since disabling input -static bool s_sent_data; -//! The callback for a stream being sent via accessory_send_stream() -static volatile AccessoryDataStreamCallback s_stream_cb; -//! Context passed to accessory_send_stream() -static void *s_stream_context; -//! Semaphore used for accessory_send_stream() -static SemaphoreHandle_t s_send_semaphore; -//! Mutex used for accessory_block() / accessory_unblock() -static PebbleRecursiveMutex *s_blocked_lock; -//! Used to track whether or not the accessory_send_stream callback sent a new byte via -//! accessory_send_byte() -static volatile bool s_did_send_byte; -//! Whether or not we should use DMA for receiving -static bool s_use_dma; -//! Whether or not DMA is enabled -static bool s_dma_enabled; -//! Used by accessory_send_stream() to track whether or not we've sent a byte recently -static volatile bool s_has_sent_byte; - -//! We need to disable stop mode in order to receive data on the accessory connector. To do this, -//! we set up an exti that kicks us out of stop mode when data is seen. Then, we schedule a timer -//! to check for additional data being seen on the connector. If we go 5 seconds without seeing -//! data, we can go back into stop mode. -static struct { - //! If the accessory connector is currently active... - bool active; - - //! The timer that will fire once a second while we're active - TimerID timer; - - //! How many intervals have gone by without data being seen - int intervals_without_data; - - //! How many intervals we should wait for without data before going back into stop mode - int max_intervals_without_data; - - //! If we saw data on the connector since the last time the timer fired. - bool data_seen_this_interval; -} s_stop_mode_monitor; - -static bool prv_rx_irq_handler(UARTDevice *dev, uint8_t data, const UARTRXErrorFlags *err_flags); -static bool prv_tx_irq_handler(UARTDevice *dev); - - -static void prv_lock(void) { - if (mcu_state_is_isr()) { - // assume we're in an ISR for the UART and don't need to worry about being blocked - return; - } - mutex_lock_recursive(s_blocked_lock); -} - -static void prv_unlock(void) { - if (mcu_state_is_isr()) { - // assume we're in an ISR for the UART and don't need to worry about being blocked - return; - } - mutex_unlock_recursive(s_blocked_lock); -} - -static void prv_enable_dma(void) { - PBL_ASSERTN(!s_dma_enabled); - s_dma_enabled = true; - uart_start_rx_dma(ACCESSORY_UART, s_rx_buffer, sizeof(s_rx_buffer)); -} - -static void prv_disable_dma(void) { - if (!s_dma_enabled) { - return; - } - s_dma_enabled = false; - uart_stop_rx_dma(ACCESSORY_UART); -} - -//! The interval timer callback. -static void prv_timer_interval_expired_cb(void *data) { - if (!s_stop_mode_monitor.data_seen_this_interval) { - // The accessory connector didn't have any data since the last time this callback fired. - ++s_stop_mode_monitor.intervals_without_data; - - if (s_stop_mode_monitor.intervals_without_data >= - s_stop_mode_monitor.max_intervals_without_data) { - // Enough intervals have passed and we should now turn stop mode back on. - stop_mode_enable(InhibitorAccessory); - - s_stop_mode_monitor.active = false; - s_stop_mode_monitor.intervals_without_data = 0; - s_stop_mode_monitor.max_intervals_without_data = 0; - - new_timer_stop(s_stop_mode_monitor.timer); - } - } else { - // Data was seen, reset the interval counter - s_stop_mode_monitor.intervals_without_data = 0; - } - - // Regardless of what happened, this interval is over and should be reset - s_stop_mode_monitor.data_seen_this_interval = false; -} - -static void prv_start_timer_cb(void *context) { - new_timer_start(s_stop_mode_monitor.timer, ACCESSORY_STOP_INTERVAL_PERIOD_MS, - prv_timer_interval_expired_cb, NULL, TIMER_START_FLAG_REPEATING); -} - -//! Callback run whenever the EXTI fires -static void prv_exti_cb(bool *should_context_switch) { - if (!s_stop_mode_monitor.active) { - // First time seeing data, let's go active - - s_stop_mode_monitor.active = true; - s_stop_mode_monitor.intervals_without_data = 0; - s_stop_mode_monitor.max_intervals_without_data = ACCESSORY_INITIAL_STOP_INTERVALS; - - stop_mode_disable(InhibitorAccessory); - - // Need to flip tasks because we can't start a timer from an interrupt - system_task_add_callback_from_isr(prv_start_timer_cb, NULL, should_context_switch); - } - - s_stop_mode_monitor.data_seen_this_interval = true; -} - -//! The UART peripheral only runs if the accessory is not in stop mode. We listen to the txrx -//! pin on the accessory connector and if we see anything we'll disable stop mode for a few -//! seconds to see if anyone has something to say. -static void prv_initialize_exti(void) { - s_stop_mode_monitor.timer = new_timer_create(); - - gpio_input_init(&BOARD_CONFIG_ACCESSORY.int_gpio); - exti_configure_pin(BOARD_CONFIG_ACCESSORY.exti, ExtiTrigger_Falling, prv_exti_cb); - exti_enable(BOARD_CONFIG_ACCESSORY.exti); -} - -static void prv_initialize_uart(uint32_t baudrate) { -#if RECOVERY_FW - // In PRF / MFG, we'll have a strong (2k) external pull-up, so we should always be open-drain - const bool is_open_drain = true; -#else - // If we raise the baud rate above 115200 we need to configure as push-pull to ensure we are - // sufficiently driving the line. Ideally, the accessory would have a strong-enough pull-up, but - // now that we've released use of the accessory port via the smartstrap APIs, we can't easily - // change this. - const bool is_open_drain = (baudrate <= 115200); -#endif - s_baudrate = baudrate; - if (is_open_drain) { - uart_init_open_drain(ACCESSORY_UART); - } else { - uart_init(ACCESSORY_UART); - } - uart_set_rx_interrupt_handler(ACCESSORY_UART, prv_rx_irq_handler); - uart_set_tx_interrupt_handler(ACCESSORY_UART, prv_tx_irq_handler); - uart_set_baud_rate(ACCESSORY_UART, s_baudrate); - uart_set_rx_interrupt_enabled(ACCESSORY_UART, true); -} - -static void prv_initialize_hardware(void) { - periph_config_acquire_lock(); - - gpio_output_init(&BOARD_CONFIG_ACCESSORY.power_en, GPIO_OType_PP, GPIO_Speed_2MHz); - gpio_output_set(&BOARD_CONFIG_ACCESSORY.power_en, false); // Turn power off - - accessory_set_baudrate(DEFAULT_BAUD); - - periph_config_release_lock(); - - prv_initialize_exti(); -} - -static void prv_set_baudrate(uint32_t baudrate, bool force_update) { - if ((baudrate != s_baudrate) || force_update) { - PBL_LOG(LOG_LEVEL_DEBUG, "Changing accessory connector baud rate to %"PRIu32, baudrate); - prv_initialize_uart(baudrate); - if (s_dma_enabled) { - // we need to reset DMA after resetting the UART - prv_disable_dma(); - prv_enable_dma(); - } - } -} - -void accessory_init(void) { - s_send_semaphore = xSemaphoreCreateBinary(); - xSemaphoreGive(s_send_semaphore); - s_blocked_lock = mutex_create_recursive(); - prv_initialize_hardware(); - accessory_set_power(false); - accessory_enable_input(); -} - -void accessory_block(void) { - prv_lock(); - accessory_send_stream_stop(); - uart_deinit(ACCESSORY_UART); -} - -void accessory_unblock(void) { - // We want to restore the previous baudrate, but clear s_baudrate in order to force a complete - // re-init of the peripheral. - prv_set_baudrate(s_baudrate, true /* force_update */); - prv_unlock(); -} - -void accessory_send_byte(uint8_t data) { - // NOTE: this may be run within an ISR - prv_lock(); - s_has_sent_byte = true; - s_did_send_byte = true; - PBL_ASSERTN(!s_input_enabled); - while (!(uart_is_tx_ready(ACCESSORY_UART))) continue; - // this section needs to be atomic since the UART IRQ also modifies these variables - portENTER_CRITICAL(); - if (s_send_history.has_data) { - // The send buffer is full. This means that the receive interrupt hasn't fired to clear the - // buffer which indicates that there is bus contention preventing a stop bit from occuring. - s_bus_contention_detected = true; - } else { - s_send_history.data = data; - s_send_history.has_data = true; - } - portEXIT_CRITICAL(); - uart_write_byte(ACCESSORY_UART, data); - s_sent_data = true; - prv_unlock(); -} - -void accessory_send_data(const uint8_t *data, size_t length) { - // NOTE: this may be run within an ISR - prv_lock(); - // When sending data, we need to temporarily disable input, as there's only one data line for - // both directions and any data we send on that line will also be interpreted as data we can - // read. This means there's a bit of overhead for sending data as we have to also make sure - // we don't accidentally read it back. If you're going to be sending a large amount of data, - // calling accessory_disable_input before will give you a nice speed boost as we don't have - // to wait for it to be safe to turn the input back on after each byte. - - const bool temporarily_disabled = s_input_enabled; - if (UNLIKELY(temporarily_disabled)) { - accessory_disable_input(); - } - - for (size_t i = 0; i < length; ++i) { - accessory_send_byte(data[i]); - } - - if (UNLIKELY(temporarily_disabled)) { - accessory_enable_input(); - } - prv_unlock(); -} - -bool accessory_send_stream(AccessoryDataStreamCallback stream_callback, void *context) { - bool success = true; - prv_lock(); - PBL_ASSERTN(xSemaphoreTake(s_send_semaphore, portMAX_DELAY) == pdPASS); - PBL_ASSERTN(stream_callback != NULL); - PBL_ASSERTN(!s_input_enabled); - if (s_dma_enabled) { - uart_clear_rx_dma_buffer(ACCESSORY_UART); - } - s_stream_context = context; - s_stream_cb = stream_callback; - s_has_sent_byte = false; - uart_set_tx_interrupt_enabled(ACCESSORY_UART, true); - // Block until the sending is complete, but timeout if we aren't able to send a byte for a while. - while (xSemaphoreTake(s_send_semaphore, milliseconds_to_ticks(SEND_BYTE_TIMEOUT_MS)) != pdPASS) { - if (!s_has_sent_byte) { - // we haven't sent a byte in the last timeout period, so time out the whole send - s_stream_cb = NULL; - s_stream_context = NULL; - success = false; - PBL_LOG(LOG_LEVEL_ERROR, "Timed-out while sending"); - break; - } - s_has_sent_byte = false; - } - xSemaphoreGive(s_send_semaphore); - prv_unlock(); - return success; -} - -void accessory_send_stream_stop(void) { - prv_lock(); - if (s_stream_cb) { - // wait for any in-progress write to finish - PBL_ASSERTN(xSemaphoreTake(s_send_semaphore, portMAX_DELAY) == pdPASS); - xSemaphoreGive(s_send_semaphore); - } - uart_set_tx_interrupt_enabled(ACCESSORY_UART, false); - s_stream_cb = NULL; - s_stream_context = NULL; - prv_unlock(); -} - -void accessory_disable_input(void) { - // NOTE: This function may be called from an ISR - prv_lock(); - PBL_ASSERTN(s_input_enabled); - - s_input_enabled = false; - s_send_history.has_data = false; - s_bus_contention_detected = false; - prv_unlock(); -} - -void accessory_enable_input(void) { - // NOTE: This function may be called from an ISR - if (s_input_enabled) { - return; - } - - prv_lock(); - if (s_sent_data) { - // wait for the TC flag to be set - uart_wait_for_tx_complete(ACCESSORY_UART); - // wait a little for the lines to settle down - const uint32_t us_to_wait = (1000000 / s_baudrate) * 2; - delay_us(us_to_wait); - s_sent_data = false; - } - - // Read data and throw it away to clear the state. We don't want to handle data we received - // while input was disabled - uart_read_byte(ACCESSORY_UART); - - s_input_enabled = true; - prv_unlock(); -} - -void accessory_use_dma(bool use_dma) { - prv_lock(); - s_use_dma = use_dma; - if (s_use_dma) { - prv_enable_dma(); - } else { - prv_disable_dma(); - } - prv_unlock(); -} - -bool accessory_bus_contention_detected(void) { - return s_bus_contention_detected; -} - -static uint32_t prv_get_baudrate(AccessoryBaud baud_select) { - const uint32_t BAUDS[] = { 9600, 14400, 19200, 28800, 38400, 57600, 62500, 115200, 125000, 230400, - 250000, 460800, 921600 }; - _Static_assert(ARRAY_LENGTH(BAUDS) == AccessoryBaudInvalid, - "bauds table doesn't match up with AccessoryBaud enum"); - return BAUDS[baud_select]; -} - -void accessory_set_baudrate(AccessoryBaud baud_select) { - prv_lock(); - PBL_ASSERTN(baud_select < AccessoryBaudInvalid); - prv_set_baudrate(prv_get_baudrate(baud_select), false /* !force_update */); - prv_unlock(); -} - -void accessory_set_power(bool on) { - if (on == s_power_enabled) { - return; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Setting accessory power %s", on?"on":"off"); - s_power_enabled = on; - gpio_output_set(&BOARD_CONFIG_ACCESSORY.power_en, on); -} - -bool accessory_is_present(void) { - accessory_set_power(true); - gpio_input_init_pull_up_down(&BOARD_CONFIG_ACCESSORY.int_gpio, GPIO_PuPd_DOWN); - // budget for a capacitance up to ~1uF and a resistance of 10kOhm - psleep(10); - bool result = (gpio_input_read(&BOARD_CONFIG_ACCESSORY.int_gpio) == SET); - gpio_input_init(&BOARD_CONFIG_ACCESSORY.int_gpio); - return result; -} - -// ISRs -//////////////////////////////////////////////////////////////////// - -static bool prv_rx_irq_handler(UARTDevice *dev, uint8_t data, const UARTRXErrorFlags *err_flags) { - bool should_context_switch = false; - // We've now seen valid data on the serial port, make sure we stay out of stop mode for a - // longer period of time. - s_stop_mode_monitor.max_intervals_without_data = ACCESSORY_VALID_DATA_STOP_INTERVALS; - if (s_input_enabled) { - // we are receiving data from the accessory - if (!err_flags->framing_error) { - should_context_switch = accessory_manager_handle_character_from_isr((char)data); - } else if (data == 0x00) { - should_context_switch = accessory_manager_handle_break_from_isr(); - } - } else { - // we are receiving data we just sent since the RX/TX lines are tied together - if (s_send_history.has_data) { - if (s_send_history.data != data) { - // The byte we are receiving doesn't match the next byte in the send queue. - s_bus_contention_detected = true; - } - s_send_history.has_data = false; - } else { - // We received a byte without sending and the input is not enabled. This typically indicates - // a race condition between when we disable input and start sending, or between when we - // finish sending and enable input. Either way, we can't trust this data so treat it as bus - // contention. - s_bus_contention_detected = true; - } - } - if (s_stream_cb) { - // enable the TXE interrupt for sending the next byte - uart_set_tx_interrupt_enabled(dev, true); - } - return should_context_switch; -} - -static bool prv_tx_irq_handler(UARTDevice *dev) { - bool should_context_switch = false; - if (s_stream_cb && !s_send_history.has_data) { - s_did_send_byte = false; - if (s_stream_cb(s_stream_context)) { - // the callback MUST send a byte in order for this interrupt to trigger again - PBL_ASSERTN(s_did_send_byte); - } else { - // we're done sending - portBASE_TYPE was_higher_task_woken = pdFALSE; - xSemaphoreGiveFromISR(s_send_semaphore, &was_higher_task_woken); - should_context_switch = (was_higher_task_woken != pdFALSE); - uart_set_tx_interrupt_enabled(dev, false); - s_stream_cb = NULL; - s_stream_context = NULL; - } - } else { - // we haven't yet received back the byte we sent - uart_set_tx_interrupt_enabled(dev, false); - } - return should_context_switch; -} - -// Commands -//////////////////////////////////////////////////////////////////// -void command_accessory_power_set(const char *on) { - if (!strcmp(on, "on")) { - accessory_set_power(true); - } else if (!strcmp(on, "off")) { - accessory_set_power(false); - } else { - prompt_send_response("Usage: accessory power (on|off)"); - } -} - -static volatile int32_t s_num_test_bytes; -static bool prv_test_send_stream(void *context) { - accessory_send_byte((uint8_t)s_num_test_bytes); - if (accessory_bus_contention_detected()) { - return false; - } - return (--s_num_test_bytes > 0); -} - -void command_accessory_stress_test(void) { - if (s_baudrate != prv_get_baudrate(DEFAULT_BAUD)) { - prompt_send_response("FAILED: accessory port is busy"); - return; - } - - // send 1 second worth of data - s_num_test_bytes = 46080; - accessory_use_dma(true); - accessory_set_baudrate(AccessoryBaud460800); - accessory_disable_input(); - const bool success = accessory_send_stream(prv_test_send_stream, NULL); - accessory_enable_input(); - accessory_set_baudrate(DEFAULT_BAUD); - accessory_use_dma(false); - - char buffer[50]; - if (!success) { - prompt_send_response_fmt(buffer, sizeof(buffer), "FAILED: send timed-out"); - } else if (s_num_test_bytes == 0) { - prompt_send_response_fmt(buffer, sizeof(buffer), "PASS!"); - } else { - prompt_send_response_fmt(buffer, sizeof(buffer), "FAILED: %"PRId32" bytes left!", - s_num_test_bytes); - } -} diff --git a/src/fw/drivers/accessory.h b/src/fw/drivers/accessory.h deleted file mode 100644 index bfd1208ba4..0000000000 --- a/src/fw/drivers/accessory.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "services/common/system_task.h" - -//! Different speeds we support running the accessory connector at. -//! -//! @internal -//! Please keep this enum in order from lowest speed to highest. -typedef enum { - AccessoryBaud9600, - AccessoryBaud14400, - AccessoryBaud19200, - AccessoryBaud28800, - AccessoryBaud38400, - AccessoryBaud57600, - AccessoryBaud62500, - AccessoryBaud115200, - AccessoryBaud125000, - AccessoryBaud230400, - AccessoryBaud250000, - AccessoryBaud460800, - AccessoryBaud921600, - - AccessoryBaudInvalid -} AccessoryBaud; - -//! The type of function used for ISR-based sending via accessory_send_stream(). This function MUST -//! send a single byte by calling accessory_send_byte() and/or return false to indicate that there -//! is no more data to be sent. -typedef bool (*AccessoryDataStreamCallback)(void *context); - -//! Initialize the accessory driver -void accessory_init(void); - -//! Blocks the accessory port from being used -void accessory_block(void); - -//! Unblocks the accessory port and allows it to be used -void accessory_unblock(void); - -//! Enable power output on the accessory connector. -void accessory_set_power(bool on); - -//! Send a single byte synchronously out the accessory connector. Input must be disabled before -//! calling this function. -void accessory_send_byte(uint8_t data); - -//! Send data synchronously out the accessory connector. Will return once all data has been sent. -void accessory_send_data(const uint8_t *data, size_t length); - -//! Sends data using ISRs by calling the provided function to send the next byte until the stream -//! callback returns false to indicate sending is complete or bus contention is detected -bool accessory_send_stream(AccessoryDataStreamCallback stream_callback, void *context); - -//! Stops any ISR-based sending which is in progress -void accessory_send_stream_stop(void); - -//! Stop the driver from reading any input on the accessory port. When input is disabled we can -//! write out the accessory port at higher rates as we don't have to worry about supressing -//! reading back our own output. -void accessory_disable_input(void); - -//! Allow the driver to start receiving input again. Only valid after calling -//! accessory_disable_input. -void accessory_enable_input(void); - -//! Set the baudrate -void accessory_set_baudrate(AccessoryBaud baud_select); - -//! Called from the accessory UART interrupt. The manager is responsible for implementing this -//! function. -//! @return whether we need to trigger a context switch based on handling this character -bool accessory_manager_handle_character_from_isr(char c); - -//! Called from the accessory UART interrupt. The manager is responsible for implementing this -//! function. -//! @return whether we need to trigger a context switch based on handling this character -bool accessory_manager_handle_break_from_isr(void); - -//! Returns whether or not there has been bus contention detected since accessory_disable_input() -//! was last called. -bool accessory_bus_contention_detected(void); - -//! Checks if the pull-up resistor which is required for smarstraps is present -bool accessory_is_present(void); - -//! Uses DMA for receiving from the peripheral -void accessory_use_dma(bool use_dma); diff --git a/src/fw/drivers/ambient/Kconfig b/src/fw/drivers/ambient/Kconfig new file mode 100644 index 0000000000..c7198f1ac8 --- /dev/null +++ b/src/fw/drivers/ambient/Kconfig @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig AMBIENT_LIGHT + bool "Ambient Light Sensor" + help + Ambient Light Sensor Drivers + +if AMBIENT_LIGHT + +config AMBIENT_LIGHT_OPT3001 + bool "TI OPT3001" + help + Support for the TI OPT3001 ambient light sensor. + +config AMBIENT_LIGHT_W1160 + bool "W1160" + help + Support for the W1160 ambient light sensor. + +config AMBIENT_LIGHT_BITS + int "Ambient light sensor resolution (bits)" + default 12 if AMBIENT_LIGHT_OPT3001 + default 16 if AMBIENT_LIGHT_W1160 + help + Number of bits in the ambient light sensor reading. Used to derive + AMBIENT_LIGHT_LEVEL_MAX, which bounds the dark/backlight thresholds + exposed to the shell and settings UI. + +module = DRIVER_AMBIENT +module-str = Ambient Light Sensor +source "src/fw/Kconfig.template.log_level" + +endif # AMBIENT_LIGHT diff --git a/src/fw/drivers/ambient/ambient_light.c b/src/fw/drivers/ambient/ambient_light.c deleted file mode 100644 index 8e70d6b3a6..0000000000 --- a/src/fw/drivers/ambient/ambient_light.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/ambient_light.h" -#include "drivers/gpio.h" -#include "drivers/voltage_monitor.h" -#include "drivers/periph_config.h" -#include "kernel/util/sleep.h" -#include "mfg/mfg_info.h" -#include "system/logging.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include - -static uint32_t s_sensor_light_dark_threshold; -static bool s_initialized = false; - -static uint32_t prv_get_default_ambient_light_dark_threshold(void) { - switch (mfg_info_get_watch_color()) { - // stepped white bezel - case WATCH_INFO_COLOR_TIME_ROUND_ROSE_GOLD_14: - case WATCH_INFO_COLOR_TIME_ROUND_SILVER_14: - return 3200; - case WATCH_INFO_COLOR_TIME_ROUND_BLACK_14: - case WATCH_INFO_COLOR_TIME_ROUND_SILVER_20: - return 3330; - case WATCH_INFO_COLOR_TIME_ROUND_BLACK_20: - return 3430; - default: - PBL_ASSERTN(BOARD_CONFIG.ambient_light_dark_threshold != 0); - return BOARD_CONFIG.ambient_light_dark_threshold; - } -} - -//! Turn on or off the ambient light sensor. -//! @param enable True to turn the light sensor on, false to turn it off. -static void prv_sensor_enable(bool enable) { - const BitAction set = enable ? Bit_SET : Bit_RESET; - gpio_use(BOARD_CONFIG.photo_en.gpio); - GPIO_WriteBit(BOARD_CONFIG.photo_en.gpio, BOARD_CONFIG.photo_en.gpio_pin, set); - gpio_release(BOARD_CONFIG.photo_en.gpio); -} - -void ambient_light_init(void) { - s_sensor_light_dark_threshold = prv_get_default_ambient_light_dark_threshold(); - - periph_config_acquire_lock(); - - // Initialize light sensor enable - { - gpio_use(BOARD_CONFIG.photo_en.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG.photo_en.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; - GPIO_Init(BOARD_CONFIG.photo_en.gpio, &GPIO_InitStructure); - - GPIO_WriteBit(BOARD_CONFIG.photo_en.gpio, BOARD_CONFIG.photo_en.gpio_pin, Bit_RESET); - - gpio_release(BOARD_CONFIG.photo_en.gpio); - } - - if (BOARD_CONFIG.als_always_on) { - prv_sensor_enable(true); - } - - periph_config_release_lock(); - - s_initialized = true; -} - -uint32_t ambient_light_get_light_level(void) { - if (!s_initialized) { - return BOARD_CONFIG.ambient_light_dark_threshold; - } - - if (!BOARD_CONFIG.als_always_on) { - prv_sensor_enable(true); - } - - VoltageReading reading; - voltage_monitor_read(VOLTAGE_MONITOR_ALS, &reading); - - if (!BOARD_CONFIG.als_always_on) { - prv_sensor_enable(false); - } - - // Multiply vmon/vref * 2/3 to find a percentage of the full scale and then - // multiply it back by 4096 to get a full 12-bit light level. - uint32_t value = (reading.vmon_total * 4096 * 2) / (reading.vref_total * 3); - return value; -} - -void command_als_read(void) { - char buffer[16]; - prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRIu32"", ambient_light_get_light_level()); -} - -uint32_t ambient_light_get_dark_threshold(void) { - return s_sensor_light_dark_threshold; -} - -void ambient_light_set_dark_threshold(uint32_t new_threshold) { - PBL_ASSERTN(new_threshold <= AMBIENT_LIGHT_LEVEL_MAX); - s_sensor_light_dark_threshold = new_threshold; -} - -bool ambient_light_is_light(void) { - // if the sensor is not enabled, always return that it is dark - return s_initialized && ambient_light_get_light_level() > s_sensor_light_dark_threshold; -} - -AmbientLightLevel ambient_light_level_to_enum(uint32_t light_level) { - if (!s_initialized) { - // if the sensor is not enabled, always return that it is very dark - return AmbientLightLevelUnknown; - } - - const uint32_t k_delta_threshold = BOARD_CONFIG.ambient_k_delta_threshold; - if (light_level < (s_sensor_light_dark_threshold - k_delta_threshold)) { - return AmbientLightLevelVeryDark; - } else if (light_level < s_sensor_light_dark_threshold) { - return AmbientLightLevelDark; - } else if (light_level < (s_sensor_light_dark_threshold + k_delta_threshold)) { - return AmbientLightLevelLight; - } else { - return AmbientLightLevelVeryLight; - } -} diff --git a/src/fw/drivers/ambient/ambient_light_opt3001.c b/src/fw/drivers/ambient/ambient_light_opt3001.c index 814fe2e9c0..2ad7aa51fe 100644 --- a/src/fw/drivers/ambient/ambient_light_opt3001.c +++ b/src/fw/drivers/ambient/ambient_light_opt3001.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" #include "console/prompt.h" @@ -26,6 +13,8 @@ #include +PBL_LOG_MODULE_DEFINE(driver_ambient_opt3001, CONFIG_DRIVER_AMBIENT_LOG_LEVEL); + static uint32_t s_sensor_light_dark_threshold; static bool s_initialized = false; @@ -70,16 +59,16 @@ void ambient_light_init(void) { uint16_t mf, id; bool ok = prv_read_register(OPT3001_MFGID, &mf) && prv_read_register(OPT3001_DEVID, &id); if (!ok) { - PBL_LOG(LOG_LEVEL_ERROR, "failed to read OPT3001 ID registers"); + PBL_LOG_ERR("failed to read OPT3001 ID registers"); return; } if (mf != OPT3001_MFGID_VAL || id != OPT3001_DEVID_VAL) { - PBL_LOG(LOG_LEVEL_INFO, "OPT3001 read successfully, but had incorrect manuf %04x, id %04x", mf, id); + PBL_LOG_INFO("OPT3001 read successfully, but had incorrect manuf %04x, id %04x", mf, id); return; } - PBL_LOG(LOG_LEVEL_INFO, "found OPT3001 with manuf %04x, id %04x", mf, id); + PBL_LOG_INFO("found OPT3001 with manuf %04x, id %04x", mf, id); if (BOARD_CONFIG.als_always_on) { prv_write_register(OPT3001_CONFIG, OPT3001_CONFIG_RANGE_AUTO | OPT3001_CONFIG_CONVTIME_100MSEC | OPT3001_CONFIG_MODE_CONTINUOUS); diff --git a/src/fw/drivers/ambient/ambient_light_w1160.c b/src/fw/drivers/ambient/ambient_light_w1160.c index 021b54bd58..9fe25753d4 100644 --- a/src/fw/drivers/ambient/ambient_light_w1160.c +++ b/src/fw/drivers/ambient/ambient_light_w1160.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" #include "console/prompt.h" @@ -26,6 +13,8 @@ #include +PBL_LOG_MODULE_DEFINE(driver_ambient_w1160, CONFIG_DRIVER_AMBIENT_LOG_LEVEL); + // Registers #define W1160_STATE_REG 0x00 #define W1160_IT_FAST1_REG 0x02 @@ -75,12 +64,18 @@ #define W1160_SLOW_ST_CONFIG1 (0x03) /* 480ms */ #define W1160_SLOW_ST_CONFIG2 (0xFF) #define W1160_SAMPLING_EN (1<<1) /* 1:en 0:dis */ +#define W1160_SAMPLING_DIS (0<<1) /* 1:en 0:dis */ #define W1160_CHIP_ID (0xE5) +#define W1160_FLG_ALS_DR (1<<7) #define W1160_RESULT_EXPONENT_SHIFT (12) #define W1160_RESULT_MANTISSA_MASK (0x0FFF) #define W1160_ADC2LUX_COEF (3U) +#define W1160_ALS_POLL_DELAY_MS (5) /* ms between data-ready polls */ +#define W1160_ALS_POLL_TIMEOUT_MS (200) /* max wait for ALS data-ready */ + +static bool s_initialized; static uint32_t s_sensor_light_dark_threshold; static bool prv_read_register(uint8_t register_address, uint8_t *result) { @@ -98,13 +93,25 @@ static bool prv_write_register(uint8_t register_address, uint8_t datum) { } void ambient_light_init(void) { + uint8_t chip_id; + bool rv; + s_sensor_light_dark_threshold = BOARD_CONFIG.ambient_light_dark_threshold; + psleep(W1160_POR_WAIT_TIME); - uint8_t chip_id; - bool rv = prv_read_register(W1160_PDT_ID_REG, &chip_id); - PBL_ASSERT(rv && W1160_CHIP_ID==chip_id, "Failed to get W1160 chip ID"); - rv &= prv_write_register(W1160_AGCCTRL1_REG, W1160_AGCCTRL1_AGC_EN); + rv = prv_read_register(W1160_PDT_ID_REG, &chip_id); + if (!rv) { + PBL_LOG_ERR("Could not read W1160 chip ID"); + return; + } + + if (chip_id != W1160_CHIP_ID) { + PBL_LOG_ERR("Unexpected W1160 chip ID: 0x%02x", chip_id); + return; + } + + rv = prv_write_register(W1160_AGCCTRL1_REG, W1160_AGCCTRL1_AGC_EN); rv &= prv_write_register(W1160_MANUAL_GAIN_CTRL_REG, W1160_AGCCTRL1_MGC_EN); rv &= prv_write_register(W1160_DATA_GC_REG, W1160_DATA_GC_LVL); rv &= prv_write_register(W1160_AGCCTRL2_REG, W1160_AGCCTRL2_SEL_MODE|W1160_AGCCTRL2_12B_MODE); @@ -114,21 +121,64 @@ void ambient_light_init(void) { rv &= prv_write_register(W1160_IT_SLOW2_REG, W1160_SLOW_IT_CONFIG2); rv &= prv_write_register(W1160_SAMPLE_SLOW1_REG, W1160_SLOW_ST_CONFIG1); rv &= prv_write_register(W1160_SAMPLE_SLOW2_REG, W1160_SLOW_ST_CONFIG2); - rv &= prv_write_register(W1160_STATE_REG, W1160_SAMPLING_EN); PBL_ASSERT(rv, "Failed to initialize W1160"); + + s_initialized = true; } uint32_t ambient_light_get_light_level(void) { uint8_t result[2] = {0}; + uint16_t als; bool rv; + + if (!s_initialized) { + return 0UL; + } + + rv = prv_write_register(W1160_STATE_REG, W1160_SAMPLING_EN); + if (!rv) { + PBL_LOG_ERR("Could not enable W1160 sampling"); + return 0UL; + } + + uint32_t elapsed = 0; + do { + rv = prv_read_register(W1160_FLAG1_REG, &result[0]); + if (!rv) { + PBL_LOG_ERR("Could not read W1160 FLAG1"); + goto disable_and_fail; + } + if ((result[0] & W1160_FLG_ALS_DR) == 0U) { + if (elapsed >= W1160_ALS_POLL_TIMEOUT_MS) { + PBL_LOG_ERR("W1160 ALS data-ready timeout"); + goto disable_and_fail; + } + psleep(W1160_ALS_POLL_DELAY_MS); + elapsed += W1160_ALS_POLL_DELAY_MS; + } + } while ((result[0] & W1160_FLG_ALS_DR) == 0U); + rv = prv_read_register(W1160_DATA1_ALS_REG, &result[1]); rv &= prv_read_register(W1160_DATA2_ALS_REG, &result[0]); - PBL_ASSERT(rv, "Failed to read als data"); + if (!rv) { + PBL_LOG_ERR("Could not obtain W1160 data"); + goto disable_and_fail; + } - uint16_t als = (((uint16_t)(result[1])) << 8) | result[0]; + rv = prv_write_register(W1160_STATE_REG, W1160_SAMPLING_DIS); + if (!rv) { + PBL_LOG_ERR("Could not disable W1160 sampling"); + return 0UL; + } + + als = (((uint16_t)(result[1])) << 8) | result[0]; return als; + +disable_and_fail: + (void)prv_write_register(W1160_STATE_REG, W1160_SAMPLING_DIS); + return 0UL; } void command_als_read(void) { diff --git a/src/fw/drivers/ambient/wscript_build b/src/fw/drivers/ambient/wscript_build new file mode 100644 index 0000000000..88c8222d64 --- /dev/null +++ b/src/fw/drivers/ambient/wscript_build @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_AMBIENT_LIGHT_OPT3001: + sources.append('ambient_light_opt3001.c') +elif bld.env.CONFIG_AMBIENT_LIGHT_W1160: + sources.append('ambient_light_w1160.c') + +bld.objects( + name='driver_ambient_light', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_ambient_light') diff --git a/src/fw/drivers/ambient_light.h b/src/fw/drivers/ambient_light.h index 25fe3695e1..2a06ae2863 100644 --- a/src/fw/drivers/ambient_light.h +++ b/src/fw/drivers/ambient_light.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -30,7 +17,12 @@ typedef enum AmbientLightLevel { #define AMBIENT_LIGHT_LEVEL_ENUM_COUNT (AmbientLightLevelVeryLight + 1) -static const uint32_t AMBIENT_LIGHT_LEVEL_MAX = 4096; // max 12 bits +#ifndef CONFIG_AMBIENT_LIGHT_BITS +// Fallback for header parsers (e.g. SDK generator) that don't preload autoconf.h. +#define CONFIG_AMBIENT_LIGHT_BITS 12 +#endif + +static const uint32_t AMBIENT_LIGHT_LEVEL_MAX = (1U << CONFIG_AMBIENT_LIGHT_BITS); /** Initialize the ambient light sensor */ void ambient_light_init(void); diff --git a/src/fw/drivers/audio.h b/src/fw/drivers/audio.h new file mode 100644 index 0000000000..48c051d684 --- /dev/null +++ b/src/fw/drivers/audio.h @@ -0,0 +1,24 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef const struct AudioDevice AudioDevice; +typedef void (*AudioTransCB)(uint32_t *free_size); + +//! Optional board-level power hooks. Either callback may be NULL. +//! power_up runs before the consumer enables (e.g. raise PMIC discharge limit); +//! power_down runs after the consumer disables (e.g. restore the limit). +typedef struct BoardPowerOps { + void (*power_up)(void); + void (*power_down)(void); +} BoardPowerOps; + +extern void audio_init(AudioDevice* audio_device); +extern void audio_start(AudioDevice* audio_device, AudioTransCB cb); +extern uint32_t audio_write(AudioDevice* audio_device, void *writeBuf, uint32_t size); +//audio volume from 0~100 +extern void audio_set_volume(AudioDevice* audio_device, int volume); +extern void audio_stop(AudioDevice* audio_device); \ No newline at end of file diff --git a/src/fw/drivers/backlight.c b/src/fw/drivers/backlight.c deleted file mode 100644 index 9979dff9ba..0000000000 --- a/src/fw/drivers/backlight.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/backlight.h" - -#include -#include - -#include "board/board.h" -#include "debug/power_tracking.h" -#include "console/prompt.h" -#include "drivers/gpio.h" -#include "drivers/led_controller.h" -#include "drivers/periph_config.h" -#include "drivers/pwm.h" -#include "drivers/timer.h" -#include "kernel/util/stop.h" -#include "system/logging.h" -#include "system/passert.h" - -// Parameters to a timer based PWM. -// -// ---------------- ---------- ---------- ---------- ---------- -// | | | | | | | | -// | | | | | | | | -// | | | | | | | | -// | | | | | | | | -// | | | | | | | | -// | | | | | | | | -// | | | | | | | | -// ------- ------- ------- -------- -// -// The resulting waveform has a frequency of PWM_OUTPUT_FREQUENCY_HZ. Inside each period, the timer -// counts up to TIMER_PERIOD_RESOLUTION. This means the counter increments at a rate of -// PWM_OUTPUT_FREQUENCY_HZ * TIMER_PERIOD_RESOLUTION, which is the frequency that our timer -// prescalar has to calculate. The duty cycle is defined by the TIM_Pulse parameter, which -// controls after which counter value the output waveform will become active. For example, a -// TIM_Pulse value of TIMER_PERIOD_RESOLUTION / 4 will result in an output waveform that will go -// active after 25% of it's period has elapsed. - -//! The counter reload value. The timer will count from 0 to this value and then reset again. -//! The TIM_Pulse member below controls for how many of these counts the resulting PWM signal is -//! active for. -static const uint32_t TIMER_PERIOD_RESOLUTION = 1024; - -//! The number of periods we have per second. -//! Note that we want BOARD_CONFIG_BACKLIGHT.timer.peripheral to have as short a period as -//! possible for power reasons. -static const uint32_t PWM_OUTPUT_FREQUENCY_HZ = 256; - -static bool s_initialized = false; - -static bool s_backlight_pwm_enabled = false; - -//! Bitmask of who wants to hold the LED enable on. -//! see \ref led_enable, \ref led_disable, \ref LEDEnabler -static uint32_t s_led_enable; - -static void prv_backlight_pwm_enable(bool on) { - pwm_enable(&BOARD_CONFIG_BACKLIGHT.pwm, on); - - if (on != s_backlight_pwm_enabled) { - if (on) { - stop_mode_disable(InhibitorBacklight); - } else { - stop_mode_enable(InhibitorBacklight); - } - } - - s_backlight_pwm_enabled = on; -} - -void backlight_init(void) { - if (s_initialized) { - return; - } - - s_led_enable = 0; - - if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Ctl) { - periph_config_acquire_lock(); - gpio_output_init(&BOARD_CONFIG_BACKLIGHT.ctl, GPIO_OType_PP, GPIO_Speed_2MHz); - gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, false); - periph_config_release_lock(); - s_initialized = true; - } - - if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Pwm) { - periph_config_acquire_lock(); - pwm_init(&BOARD_CONFIG_BACKLIGHT.pwm, - TIMER_PERIOD_RESOLUTION, - TIMER_PERIOD_RESOLUTION * PWM_OUTPUT_FREQUENCY_HZ); - periph_config_release_lock(); - s_initialized = true; - } - - if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C) { - led_controller_init(); - s_initialized = true; - } -} - -// TODO: PBL-36077 Move to a generic 4v5 enable -void led_enable(LEDEnabler enabler) { - if (s_led_enable == 0) { - gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, true); - } - s_led_enable |= enabler; -} - -// TODO: PBL-36077 Move to a generic 4v5 disable -void led_disable(LEDEnabler enabler) { - s_led_enable &= ~enabler; - if (s_led_enable == 0) { - gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, false); - } -} - -void backlight_set_brightness(uint16_t brightness) { - if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Ctl) { - if (brightness == 0) { - led_disable(LEDEnablerBacklight); - } else { - led_enable(LEDEnablerBacklight); - } - } - - if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_Pwm) { - if (brightness == 0) { - if (s_backlight_pwm_enabled) { - prv_backlight_pwm_enable(false); - } - PWR_TRACK_BACKLIGHT("OFF", PWM_OUTPUT_FREQUENCY_HZ, 0); - } else { - if (!s_backlight_pwm_enabled) { - prv_backlight_pwm_enable(true); - } - - // By setting higher values in the TIM_Pulse register, we're causing the output waveform - // to be low for a longer period of time, which causes the backlight to be brighter. - // - // The brightness value has a range of 0 to 0x3fff which is 2^15. The period of the timer - // counter is 2^10. We want to rescale the brightness range into a subset of the timer - // counter range. Different boards will have a different duty cycle that represent the - // "fully on" state. - const uint32_t pwm_scaling_factor = BACKLIGHT_BRIGHTNESS_MAX / TIMER_PERIOD_RESOLUTION; - const uint32_t desired_duty_cycle = brightness * BOARD_CONFIG.backlight_max_duty_cycle_percent - / pwm_scaling_factor / 100; - pwm_set_duty_cycle(&BOARD_CONFIG_BACKLIGHT.pwm, desired_duty_cycle); - PWR_TRACK_BACKLIGHT("ON", PWM_OUTPUT_FREQUENCY_HZ, - (desired_duty_cycle * 100) / TIMER_PERIOD_RESOLUTION); - } - } - - if (BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C) { - led_controller_backlight_set_brightness(brightness * 100 / BACKLIGHT_BRIGHTNESS_MAX); - } -} - -void command_backlight_ctl(const char *arg) { - const int bright_percent = atoi(arg); - if (bright_percent < 0 || bright_percent > 100) { - prompt_send_response("Invalid Brightness"); - return; - } - backlight_set_brightness((BACKLIGHT_BRIGHTNESS_MAX * bright_percent) / 100); - prompt_send_response("OK"); -} diff --git a/src/fw/drivers/backlight.h b/src/fw/drivers/backlight.h index 4687bbf85c..13d40b2664 100644 --- a/src/fw/drivers/backlight.h +++ b/src/fw/drivers/backlight.h @@ -1,43 +1,29 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include #include -typedef enum { - LEDEnablerNone = (1 << 0), - LEDEnablerBacklight = (1 << 1), - LEDEnablerHRM = (1 << 2), -} LEDEnabler; - -// The light brightness can vary between LIGHT_BRIGHTNESS_OFF and LIGHT_BRIGHTNESS_ON -#define BACKLIGHT_BRIGHTNESS_OFF 0x0000 -#define BACKLIGHT_BRIGHTNESS_MAX 0x4000 +//! FIXME: These colors are not gamma-corrected +#define BACKLIGHT_COLOR_RED 0xFF0000 +#define BACKLIGHT_COLOR_GREEN 0x00FF00 +#define BACKLIGHT_COLOR_BLUE 0x0000FF +#define BACKLIGHT_COLOR_BLACK 0x000000 +#define BACKLIGHT_COLOR_WHITE 0xFFFFFF +#define BACKLIGHT_COLOR_WARM_WHITE 0xFFBFA2 void backlight_init(void); -//! @param brightness a number between BACKLIGHT_BRIGHTNESS_OFF and BACKLIGHT_BRIGHTNESS_ON -void backlight_set_brightness(uint16_t brightness); +//! @param brightness Brightness level (0-100) +void backlight_set_brightness(uint8_t brightness); + +//! Re-apply the cached driver state to the backlight hardware. No-op if the +//! firmware believes the backlight is already off. +void backlight_refresh(void); -//! On some boards, the LED enable gpio is actually a toggle for 4.5v. -//! Other portions of the system may require this to be enabled in order to function. -//! Keep track of who is using the LED enable so that way we don't turn it off on them. -//! Those components which may need to toggle LED Enable are listed in \ref LEDEnabler. -void led_enable(LEDEnabler enabler); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +void backlight_set_color(uint32_t rgb_color); -void led_disable(LEDEnabler enabler); +uint32_t backlight_get_color(void); +#endif \ No newline at end of file diff --git a/src/fw/drivers/backlight/Kconfig b/src/fw/drivers/backlight/Kconfig new file mode 100644 index 0000000000..04cbe5adab --- /dev/null +++ b/src/fw/drivers/backlight/Kconfig @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig BACKLIGHT + bool "Backlight" + help + Backlight Drivers + +if BACKLIGHT + +config BACKLIGHT_HAS_COLOR + bool + help + Backlight drivers that support color should select this Kconfig symbol. + +config BACKLIGHT_AW2016 + bool "AW2016" + depends on I2C + select BACKLIGHT_HAS_COLOR + help + Support for the AW2016 LED controller. + +config BACKLIGHT_AW9364E + bool "AW9364E" + help + Support for the AW9364E LED controller. + +config BACKLIGHT_PWM + bool "PWM" + help + Support for a backlight driven by a PWM peripheral. + +config BACKLIGHT_QEMU + bool "QEMU" + help + Emulated backlight controller that writes to the QEMU + pebble-display MMIO registers. + +config BACKLIGHT_QEMU_COLOR + bool "QEMU Color" + depends on BACKLIGHT_QEMU + select BACKLIGHT_HAS_COLOR + help + Enable support for RGB backlight colors in the QEMU backlight driver. + +endif # BACKLIGHT \ No newline at end of file diff --git a/src/fw/drivers/backlight/aw2016.c b/src/fw/drivers/backlight/aw2016.c new file mode 100644 index 0000000000..0d9bf9f6cd --- /dev/null +++ b/src/fw/drivers/backlight/aw2016.c @@ -0,0 +1,155 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/backlight.h" + +#include "board/board.h" +#include "drivers/i2c.h" +#include "system/passert.h" + +#define AW2016_REG_RSTR 0x00U +#define AW2016_REG_RSTR_CHIP_ID 0x09U +#define AW2016_REG_RSTR_RST 0x55U + +#define AW2016_REG_GCR1 0x01U +#define AW2016_REG_GCR1_CHGDIS_DIS (1U << 1U) +#define AW2016_REG_GCR1_CHIPEN_EN (1U << 0U) +#define AW2016_REG_GCR1_CHIPEN_DIS 0U + +#define AW2016_REG_GCR2 0x04U +#define AW2016_REG_GCR2_IMAX_30MA 1U + +#define AW2016_REG_LCTR (0x30) +#define AW2016_REG_LCTR_EXP_LINEAR (1U << 3U) +#define AW2016_REG_LCTR_LE3_EN (1U << 2U) +#define AW2016_REG_LCTR_LE2_EN (1U << 1U) +#define AW2016_REG_LCTR_LE1_EN (1U << 0U) + +#define AW2016_REG_LCFG1 0x31U +#define AW2016_REG_LCFG1_CUR_MAX 0x0FU + +#define AW2016_REG_LCFG2 0x32U +#define AW2016_REG_LCFG2_CUR_MAX 0x0FU + +#define AW2016_REG_LCFG3 0x33U +#define AW2016_REG_LCFG3_CUR_MAX 0x0FU + +#define AW2016_REG_PWM1 0x34U + +#define AW2016_REG_PWM2 0x35U + +#define AW2016_REG_PWM3 0x36U + +static uint8_t s_brightness; +static uint32_t s_rgb_current_color = BACKLIGHT_COLOR_WARM_WHITE; + +static bool prv_read_register(uint8_t register_address, uint8_t *value) { + bool ret; + + i2c_use(I2C_AW2016); + ret = i2c_read_register_block(I2C_AW2016, register_address, 1, value); + i2c_release(I2C_AW2016); + + return ret; +} + +static bool prv_write_register(uint8_t register_address, uint8_t value) { + bool ret; + + i2c_use(I2C_AW2016); + ret = i2c_write_register_block(I2C_AW2016, register_address, 1, &value); + i2c_release(I2C_AW2016); + + return ret; +} + +static bool prv_configure_registers(void) { + bool ret; + ret = prv_write_register(AW2016_REG_GCR2, AW2016_REG_GCR2_IMAX_30MA); + ret &= prv_write_register(AW2016_REG_LCTR, AW2016_REG_LCTR_EXP_LINEAR | AW2016_REG_LCTR_LE3_EN | + AW2016_REG_LCTR_LE2_EN | AW2016_REG_LCTR_LE1_EN); + ret &= prv_write_register(AW2016_REG_LCFG1, AW2016_REG_LCFG1_CUR_MAX); + ret &= prv_write_register(AW2016_REG_LCFG2, AW2016_REG_LCFG2_CUR_MAX); + ret &= prv_write_register(AW2016_REG_LCFG3, AW2016_REG_LCFG3_CUR_MAX); + return ret; +} + +void backlight_init(void) { + uint8_t value; + bool ret; + + ret = prv_read_register(AW2016_REG_RSTR, &value); + PBL_ASSERTN(ret && (value == AW2016_REG_RSTR_CHIP_ID)); + + ret = prv_write_register(AW2016_REG_RSTR, AW2016_REG_RSTR_RST); + PBL_ASSERTN(ret); + + ret = prv_write_register(AW2016_REG_GCR1, AW2016_REG_GCR1_CHGDIS_DIS); + PBL_ASSERTN(ret); +} + +void backlight_set_brightness(uint8_t brightness) { + bool ret; + + if (brightness > 100U) { + brightness = 100U; + } + + if (s_brightness == brightness) { + return; + } + + const uint8_t previous_brightness = s_brightness; + s_brightness = brightness; + + if (brightness == 0U) { + ret = prv_write_register(AW2016_REG_GCR1, + AW2016_REG_GCR1_CHGDIS_DIS | AW2016_REG_GCR1_CHIPEN_DIS); + PBL_ASSERTN(ret); + } else { + if (previous_brightness == 0U) { + ret = prv_write_register(AW2016_REG_GCR1, + AW2016_REG_GCR1_CHGDIS_DIS | AW2016_REG_GCR1_CHIPEN_EN); + ret &= prv_configure_registers(); + PBL_ASSERTN(ret); + } + + backlight_set_color(s_rgb_current_color); + } +} + +void backlight_set_color(uint32_t rgb_color) { + bool ret; + uint8_t red, green, blue; + + red = ((rgb_color & 0x00FF0000) >> 16) * s_brightness / 100; + green = ((rgb_color & 0x0000FF00) >> 8) * s_brightness / 100; + blue = (rgb_color & 0x000000FF) * s_brightness / 100; + + ret = prv_write_register(AW2016_REG_PWM1, red); + ret &= prv_write_register(AW2016_REG_PWM2, green); + ret &= prv_write_register(AW2016_REG_PWM3, blue); + + PBL_ASSERTN(ret); + + s_rgb_current_color = rgb_color; +} + +uint32_t backlight_get_color(void) { + return s_rgb_current_color; +} + +void backlight_refresh(void) { + bool ret; + + if (s_brightness == 0U) { + return; + } + + ret = prv_write_register(AW2016_REG_GCR1, + AW2016_REG_GCR1_CHGDIS_DIS | AW2016_REG_GCR1_CHIPEN_EN); + ret &= prv_configure_registers(); + PBL_ASSERTN(ret); + + backlight_set_color(s_rgb_current_color); +} diff --git a/src/fw/drivers/backlight/aw9364e.c b/src/fw/drivers/backlight/aw9364e.c new file mode 100644 index 0000000000..6e98c5fda3 --- /dev/null +++ b/src/fw/drivers/backlight/aw9364e.c @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "drivers/gpio.h" +#include "drivers/backlight.h" +#include "kernel/util/delay.h" +#include "system/logging.h" +#include "util/math.h" + +// AW9364E 1-wire dimming protocol implementation +// The AW9364E uses pulse counting for brightness control: +// - 1 pulse = 20mA (brightest) +// - 16 pulses = 1.25mA (dimmest) +// Timing: THI > 0.5us, 0.5us < TLO < 500μs +// Shutdown: EN low for > 2.5ms + +#define AW9364E_TON_US 20U +#define AW9364E_THI_US 1U +#define AW9364E_TLO_US 1U +#define AW9364E_MAX_PULSES 16U +#define AW9364E_OFF_TIME_US 2600U + +void backlight_init(void) { + gpio_output_init(&AW9364E.gpio, GPIO_OType_PP, GPIO_Speed_2MHz); +} + +void backlight_set_brightness(uint8_t brightness) { + uint8_t pulse_count; + + if (brightness > 100) { + brightness = 100; + } + + pulse_count = AW9364E_MAX_PULSES - DIVIDE_CEIL(brightness * AW9364E_MAX_PULSES, 100U) + 1U; + + gpio_output_set(&AW9364E.gpio, false); + delay_us(AW9364E_OFF_TIME_US); + + if (pulse_count > AW9364E_MAX_PULSES) { + return; + } + + for (uint8_t i = 0U; i < pulse_count; i++) { + gpio_output_set(&AW9364E.gpio, false); + delay_us(AW9364E_TLO_US); + gpio_output_set(&AW9364E.gpio, true); + delay_us(i == 0U ? AW9364E_TON_US : AW9364E_THI_US); + } +} + +void backlight_refresh(void) { +} diff --git a/src/fw/drivers/backlight/aw9364e.h b/src/fw/drivers/backlight/aw9364e.h new file mode 100644 index 0000000000..b72d65e294 --- /dev/null +++ b/src/fw/drivers/backlight/aw9364e.h @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef struct LedControllerAW9364E { + OutputConfig gpio; +} LedControllerAW9364E; \ No newline at end of file diff --git a/src/fw/drivers/backlight/console.c b/src/fw/drivers/backlight/console.c new file mode 100644 index 0000000000..56648f6404 --- /dev/null +++ b/src/fw/drivers/backlight/console.c @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "console/prompt.h" +#include "drivers/backlight.h" + +void command_backlight_ctl(const char *arg) { + const int bright_percent = atoi(arg); + if (bright_percent < 0 || bright_percent > 100) { + prompt_send_response("Invalid Brightness"); + return; + } + + backlight_set_brightness(bright_percent); + prompt_send_response("OK"); +} + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +void command_backlight_set_color(const char *color) { + uint32_t color_val = strtol(color, NULL, 16); + + backlight_set_color(color_val); +} +#endif \ No newline at end of file diff --git a/src/fw/drivers/backlight/pwm.c b/src/fw/drivers/backlight/pwm.c new file mode 100644 index 0000000000..ddfdf9c8be --- /dev/null +++ b/src/fw/drivers/backlight/pwm.c @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/backlight.h" +#include "drivers/gpio.h" + +#include "board/board.h" +#include "drivers/pwm.h" + +//! The counter reload value. The timer will count from 0 to this value and then reset again. +static const uint32_t TIMER_PERIOD_RESOLUTION = 1024; + +//! The number of periods we have per second. +static const uint32_t PWM_OUTPUT_FREQUENCY_HZ = 256; + +void backlight_init(void) { + if (BACKLIGHT_PWM.ctl.gpio != NULL) { + gpio_output_init(&BACKLIGHT_PWM.ctl, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&BACKLIGHT_PWM.ctl, false); + } + + pwm_init(&BACKLIGHT_PWM.pwm, TIMER_PERIOD_RESOLUTION, + TIMER_PERIOD_RESOLUTION * PWM_OUTPUT_FREQUENCY_HZ); +} + +void backlight_set_brightness(uint8_t brightness) { + if (brightness == 0) { + pwm_enable(&BACKLIGHT_PWM.pwm, false); + if (BACKLIGHT_PWM.ctl.gpio != NULL) { + gpio_output_set(&BACKLIGHT_PWM.ctl, false); + } + } else { + if (BACKLIGHT_PWM.ctl.gpio != NULL) { + gpio_output_set(&BACKLIGHT_PWM.ctl, true); + } + + pwm_enable(&BACKLIGHT_PWM.pwm, true); + + const uint32_t desired_duty_cycle = brightness * BACKLIGHT_PWM.max_duty_cycle_percent * + TIMER_PERIOD_RESOLUTION / 10000; + pwm_set_duty_cycle(&BACKLIGHT_PWM.pwm, desired_duty_cycle); + } +} + +void backlight_refresh(void) { +} \ No newline at end of file diff --git a/src/fw/drivers/backlight/pwm.h b/src/fw/drivers/backlight/pwm.h new file mode 100644 index 0000000000..51e4e99c9b --- /dev/null +++ b/src/fw/drivers/backlight/pwm.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef struct { + const OutputConfig ctl; + const PwmConfig pwm; + uint8_t max_duty_cycle_percent; +} BacklightPwmConfig; \ No newline at end of file diff --git a/src/fw/drivers/backlight/qemu.c b/src/fw/drivers/backlight/qemu.c new file mode 100644 index 0000000000..2662d5cb47 --- /dev/null +++ b/src/fw/drivers/backlight/qemu.c @@ -0,0 +1,98 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/backlight.h" + +#include "board/board.h" +#include "console/prompt.h" +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +#include "drivers/backlight.h" +#endif + +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// Display register offsets (must match QEMU pebble-display) +#define DISP_CTRL 0x000 +#define DISP_BRIGHTNESS 0x018 +#define DISP_BL_RED 0x024 +#define DISP_BL_GREEN 0x028 +#define DISP_BL_BLUE 0x02C +#define CTRL_UPDATE (1 << 1) + +// Brightness levels for QEMU display grayscale path +#define BACKLIGHT_OFF_LEVEL 180 +#define BACKLIGHT_ON_LEVEL 255 + +static bool s_initialized; + +#ifdef CONFIG_BACKLIGHT_QEMU_COLOR +static uint8_t s_brightness = 100U; +static uint32_t s_rgb_current_color = BACKLIGHT_COLOR_WHITE; +#endif + +#ifdef CONFIG_BACKLIGHT_QEMU_COLOR +static void prv_write_channels(uint8_t r, uint8_t g, uint8_t b) { + REG32(QEMU_DISPLAY_BASE + DISP_BL_RED) = r; + REG32(QEMU_DISPLAY_BASE + DISP_BL_GREEN) = g; + REG32(QEMU_DISPLAY_BASE + DISP_BL_BLUE) = b; +} +#endif + +void backlight_init(void) { +#ifdef CONFIG_BACKLIGHT_QEMU_COLOR + prv_write_channels(0xFFU, 0xFFU, 0xFFU); +#else + REG32(QEMU_DISPLAY_BASE + DISP_BRIGHTNESS) = BACKLIGHT_OFF_LEVEL; + REG32(QEMU_DISPLAY_BASE + DISP_CTRL) |= CTRL_UPDATE; +#endif + s_initialized = true; +} + +void backlight_set_brightness(uint8_t brightness) { + if (!s_initialized) { + return; + } + + if (brightness > 100U) { + brightness = 100U; + } + +#ifdef CONFIG_BACKLIGHT_QEMU_COLOR + s_brightness = brightness; + backlight_set_color(s_rgb_current_color); +#else + uint32_t level; + if (brightness == 0) { + level = BACKLIGHT_OFF_LEVEL; + } else { + uint32_t range = BACKLIGHT_ON_LEVEL - BACKLIGHT_OFF_LEVEL; + level = BACKLIGHT_OFF_LEVEL + ((uint32_t)brightness * range) / 100; + if (level > BACKLIGHT_ON_LEVEL) { + level = BACKLIGHT_ON_LEVEL; + } + } + + REG32(QEMU_DISPLAY_BASE + DISP_BRIGHTNESS) = level; + REG32(QEMU_DISPLAY_BASE + DISP_CTRL) |= CTRL_UPDATE; +#endif +} + +void backlight_refresh(void) { +} + +#ifdef CONFIG_BACKLIGHT_QEMU_COLOR +void backlight_set_color(uint32_t rgb_color) { + uint8_t r = ((rgb_color >> 16) & 0xFFU) * s_brightness / 100U; + uint8_t g = ((rgb_color >> 8) & 0xFFU) * s_brightness / 100U; + uint8_t b = (rgb_color & 0xFFU) * s_brightness / 100U; + + prv_write_channels(r, g, b); + s_rgb_current_color = rgb_color; +} + +uint32_t backlight_get_color(void) { + return s_rgb_current_color; +} +#endif \ No newline at end of file diff --git a/src/fw/drivers/backlight/wscript_build b/src/fw/drivers/backlight/wscript_build new file mode 100644 index 0000000000..0c12fae6dd --- /dev/null +++ b/src/fw/drivers/backlight/wscript_build @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = ['console.c'] +use = ['fw_includes'] + +if bld.env.CONFIG_BACKLIGHT_AW2016: + sources.append('aw2016.c') +elif bld.env.CONFIG_BACKLIGHT_AW9364E: + sources.append('aw9364e.c') +elif bld.env.CONFIG_BACKLIGHT_PWM: + sources.append('pwm.c') +elif bld.env.CONFIG_BACKLIGHT_QEMU: + sources.append('qemu.c') + +bld.objects( + name='driver_backlight', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_backlight') \ No newline at end of file diff --git a/src/fw/drivers/battery.h b/src/fw/drivers/battery.h index f46a81e53f..f5254080a4 100644 --- a/src/fw/drivers/battery.h +++ b/src/fw/drivers/battery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/src/fw/drivers/battery/Kconfig b/src/fw/drivers/battery/Kconfig new file mode 100644 index 0000000000..59047f1305 --- /dev/null +++ b/src/fw/drivers/battery/Kconfig @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig BATTERY + bool "Battery" + help + Battery Drivers + +if BATTERY + +config BATTERY_PMIC + bool "PMIC-based" + help + Battery management via external PMIC. Used on platforms where the + PMIC handles battery monitoring (asterix, obelix, getafix). + +config BATTERY_QEMU + bool "QEMU Battery" + help + Stub battery driver for QEMU (fixed values). + +endif # BATTERY diff --git a/src/fw/drivers/battery/battery_adc_conversion.c b/src/fw/drivers/battery/battery_adc_conversion.c deleted file mode 100644 index 88d3eda791..0000000000 --- a/src/fw/drivers/battery/battery_adc_conversion.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/battery.h" - -uint32_t battery_convert_reading_to_millivolts(ADCVoltageMonitorReading reading, - uint32_t numerator, uint32_t denominator) { - // The result from the ADC is 0-1.8V, but scaled into a 12bit number. That means a value of - // zero indicates 0V and a value of 4095 (2^12 - 1) indicates a reading of 1.8V. - - // The ADC is only capable of measuring between 0 and 1.8V, so we expect the thing providing - // a voltage to the monitor pin to be scaling it in some way. This scaling factor is captured - // in the numerator and denominator arguments. - - // Therefore, whatever 12-bit number we read from the ADC needs to be converted to a voltage by - // multiplying it by 1.8/4095, and then further scaled into it's final voltage by multiplying by - // numerator and dividing by the denominator. - - // Finally, our reading contains a sum of many readings from both the monitor pin as well as - // an internal 1.2V reference voltage. The reason for this is that these pins will have noise - // on them and we can assume that any ripple on the mon rail will also occur on the 1.2V internal - // reference voltage. So, we can measure the voltage synchronously on both ADCs and then - // calculate a relative voltage. Therefore, the actual monitor voltage can be estimated by - // calculating 1800 * (vmon_mv_sum / (vref_mv_sum * 1800 / 1200) or - // 1800 * (vmon_mv_sum * 1200) / (vref_mv_sum * 1800). - - // Convert from 12-bit to millivolts by multiplying by 1800/4095 which is the same as 40/91 - const uint32_t vref_mv_sum = reading.vref_total * 40 / 91; - const uint32_t vmon_mv_sum = reading.vmon_total * 40 / 91; - - // Use the reference voltage to convert a single smoothed out mv reading. - // Multiply vmon/vref * 2/3 to find a percentage of the full scale and then multiply it back - // by 1800 to get back to mV. - const uint32_t millivolts = ((vmon_mv_sum * 1800 * 2) / (vref_mv_sum * 3)); - - // Finally, hit it with the scaling factors. - const uint32_t scaled_millivolts = millivolts * numerator / denominator; - - return scaled_millivolts; -} diff --git a/src/fw/drivers/battery/battery_common.c b/src/fw/drivers/battery/battery_common.c index d32960780f..7e7a46af87 100644 --- a/src/fw/drivers/battery/battery_common.c +++ b/src/fw/drivers/battery/battery_common.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/battery.h" diff --git a/src/fw/drivers/battery/battery_pmic.c b/src/fw/drivers/battery/battery_pmic.c deleted file mode 100644 index d1e8a7779d..0000000000 --- a/src/fw/drivers/battery/battery_pmic.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/battery.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "system/logging.h" -#include "system/passert.h" - -void battery_init(void) { - // pmic_init() needs to happen, but I think it will happen elsewhere first - // It may be okay to just init the pmic here again - return; -} - -int battery_get_millivolts(void) { - if (!pmic_enable_battery_measure()) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to enable battery measure. " - "Battery voltage reading will be bogus."); - } - ADCVoltageMonitorReading info = battery_read_voltage_monitor(); - pmic_disable_battery_measure(); - - PBL_ASSERTN(BOARD_CONFIG_POWER.battery_vmon_scale.denominator); - return battery_convert_reading_to_millivolts(info, - BOARD_CONFIG_POWER.battery_vmon_scale.numerator, - BOARD_CONFIG_POWER.battery_vmon_scale.denominator); -} - -bool battery_charge_controller_thinks_we_are_charging_impl(void) { - return pmic_is_charging(); -} - -bool battery_is_usb_connected_impl(void) { - return pmic_is_usb_connected(); -} - -void battery_set_charge_enable(bool charging_enabled) { - pmic_set_charger_state(charging_enabled); -} - -// TODO -// This is my understanding from Figure 9 of the datasheet: -// Charger off -> Pre charge -> Fast Charge (constant current) -> -// Fast Charge (constant voltage) -> Maintain Charge -> Maintain Charge Done -// -// The Pre Charge and Charge Termination currents are programmed via I2C -// The Fast Charge current is determined by Rset -// -// There doesn't seem to be a way to change the current in constant current mode -void battery_set_fast_charge(bool fast_charge_enabled) { - return; -} diff --git a/src/fw/drivers/battery/battery_tintin.c b/src/fw/drivers/battery/battery_tintin.c deleted file mode 100644 index 957b70131d..0000000000 --- a/src/fw/drivers/battery/battery_tintin.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/battery.h" - -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "board/board.h" -#include "drivers/otp.h" -#include "drivers/periph_config.h" -#include "system/logging.h" - -#define STM32F2_COMPATIBLE -#include - -#include -#include - -#include "kernel/events.h" -#include "services/common/system_task.h" -#include "services/common/new_timer/new_timer.h" -#include "os/tick.h" - -#include "FreeRTOS.h" -#include "timers.h" - - -static const uint32_t USB_CONN_DEBOUNCE_MS = 400; -static TimerID s_debounce_timer_handle = TIMER_INVALID_ID; -static bool s_debounced_is_usb_connected = false; - -static bool battery_is_usb_connected_raw(void); - -static void battery_vusb_interrupt_handler(bool *should_context_switch); - -static void battery_conn_debounce_callback(void* data) { - s_debounced_is_usb_connected = battery_is_usb_connected_raw(); - - if (!s_debounced_is_usb_connected) { - // disconnection event - // - put the watch charger into a sane state - // - disable fast-charge and re-enable the charger - battery_set_charge_enable(true); - battery_set_fast_charge(false); - } - - PebbleEvent event = { - .type = PEBBLE_BATTERY_CONNECTION_EVENT, - .battery_connection = { - .is_connected = s_debounced_is_usb_connected, - } - }; - - event_put(&event); -} - -static bool board_has_chg_fast() { - return BOARD_CONFIG_POWER.chg_fast.gpio != 0; -} - -static bool board_has_chg_en() { - return BOARD_CONFIG_POWER.chg_en.gpio != 0; -} - -// These are the guts of battery_set_charge_enable(), called when we already have periph_config_acquire_lock -static void prv_battery_set_charge_enable(bool charging_enabled) { - if (board_has_chg_en()) { - gpio_use(BOARD_CONFIG_POWER.chg_en.gpio); - - GPIO_WriteBit(BOARD_CONFIG_POWER.chg_en.gpio, BOARD_CONFIG_POWER.chg_en.gpio_pin, charging_enabled?Bit_SET:Bit_RESET); - - gpio_release(BOARD_CONFIG_POWER.chg_en.gpio); - PBL_LOG(LOG_LEVEL_DEBUG, "Charging:%s", charging_enabled?"enabled":"disabled"); - } -} - -// These are the guts of battery_set_fast_charge(), called when we already have periph_config_acquire_lock -static void prv_battery_set_fast_charge(bool fast_charge_enabled) { - if (board_has_chg_fast()) { - gpio_use(BOARD_CONFIG_POWER.chg_fast.gpio); - - GPIO_WriteBit(BOARD_CONFIG_POWER.chg_fast.gpio, BOARD_CONFIG_POWER.chg_fast.gpio_pin, fast_charge_enabled?Bit_RESET:Bit_SET); - - gpio_release(BOARD_CONFIG_POWER.chg_fast.gpio); - PBL_LOG(LOG_LEVEL_DEBUG, "Fastcharge %s", fast_charge_enabled?"enabled":"disabled"); - } -} - -void battery_init(void) { - s_debounce_timer_handle = new_timer_create(); - - periph_config_acquire_lock(); - gpio_use(BOARD_CONFIG_POWER.vusb_stat.gpio); - gpio_use(BOARD_CONFIG_POWER.chg_stat.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.vusb_stat.gpio_pin; - GPIO_Init(BOARD_CONFIG_POWER.vusb_stat.gpio, &GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.chg_stat.gpio_pin; - GPIO_Init(BOARD_CONFIG_POWER.chg_stat.gpio, &GPIO_InitStructure); - - if (board_has_chg_fast() || board_has_chg_en()) { - // Initialize PD2 as the sensor enable - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - - if (board_has_chg_fast()) { - gpio_use(BOARD_CONFIG_POWER.chg_fast.gpio); - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.chg_fast.gpio_pin; - GPIO_Init(BOARD_CONFIG_POWER.chg_fast.gpio, &GPIO_InitStructure); - prv_battery_set_fast_charge(false); - gpio_release(BOARD_CONFIG_POWER.chg_fast.gpio); - } - - if (board_has_chg_en()) { - gpio_use(BOARD_CONFIG_POWER.chg_en.gpio); - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.chg_en.gpio_pin; - GPIO_Init(BOARD_CONFIG_POWER.chg_en.gpio, &GPIO_InitStructure); - prv_battery_set_charge_enable(true); - gpio_release(BOARD_CONFIG_POWER.chg_en.gpio); - } - } - - if (BOARD_CONFIG_POWER.has_vusb_interrupt) { - periph_config_release_lock(); - - exti_configure_pin(BOARD_CONFIG_POWER.vusb_exti, ExtiTrigger_RisingFalling, - battery_vusb_interrupt_handler); - exti_enable(BOARD_CONFIG_POWER.vusb_exti); - - periph_config_acquire_lock(); - } else { - // TODO: Start polling vusb_stat - } - - gpio_release(BOARD_CONFIG_POWER.vusb_stat.gpio); - gpio_release(BOARD_CONFIG_POWER.chg_stat.gpio); - - periph_config_release_lock(); - - if (BOARD_CONFIG_POWER.has_vusb_interrupt) { - // Prime the debounced state. - s_debounced_is_usb_connected = battery_is_usb_connected_raw(); - } -} - -bool battery_charge_controller_thinks_we_are_charging_impl(void) { - periph_config_acquire_lock(); - gpio_use(BOARD_CONFIG_POWER.chg_stat.gpio); - bool state = !GPIO_ReadInputDataBit(BOARD_CONFIG_POWER.chg_stat.gpio, BOARD_CONFIG_POWER.chg_stat.gpio_pin); - gpio_release(BOARD_CONFIG_POWER.chg_stat.gpio); - periph_config_release_lock(); - return state; -} - -static bool battery_is_usb_connected_raw(void) { - periph_config_acquire_lock(); - gpio_use(BOARD_CONFIG_POWER.vusb_stat.gpio); - bool state = !GPIO_ReadInputDataBit(BOARD_CONFIG_POWER.vusb_stat.gpio, BOARD_CONFIG_POWER.vusb_stat.gpio_pin); - gpio_release(BOARD_CONFIG_POWER.vusb_stat.gpio); - periph_config_release_lock(); - return state; -} - -bool battery_is_usb_connected_impl(void) { - if (BOARD_CONFIG_POWER.has_vusb_interrupt) { - return s_debounced_is_usb_connected; - } else { - return battery_is_usb_connected_raw(); - } -} - - -// This callback gets installed by DBG_SERIAL_FREERTOS_IRQHandler() using system_task_add_callback_from_isr(). -// It is used to start up our timer since doing so from an ISR is not allowed. -static void prv_start_timer_sys_task_callback(void* data) { - new_timer_start(s_debounce_timer_handle, USB_CONN_DEBOUNCE_MS, battery_conn_debounce_callback, NULL, 0 /*flags*/); -} - -static void battery_vusb_interrupt_handler(bool *should_context_switch) { - // Start the timer from a system task callback - not allowed to do so from an ISR - system_task_add_callback_from_isr(prv_start_timer_sys_task_callback, NULL, should_context_switch); -} - -int battery_get_millivolts(void) { - ADCVoltageMonitorReading info = battery_read_voltage_monitor(); - - // Apologies for the madness numbers. - // The previous implementation had some approximations in it. The battery voltage is scaled - // down by a pair of resistors (750k at the top, 470k at the bottom), resulting in a required - // scaling of (75 + 47) / 47 or roughly 2.56x, but the previous implementation also required - // fudging the numbers a bit in order to approximate for leakage current (a 73/64 multiple - // was arbitrarily increased to 295/256). In order to match this previous arbitrary scaling - // I've chosen new numbers that provide 2.62x scaling, which is the previous 2.56x with the - // same amount of fudging applied. - - return battery_convert_reading_to_millivolts(info, 3599, 1373); -} - -extern void command_sim_battery_connection(const char *bool_str) { - bool value = atoi(bool_str); - - PebbleEvent event = { - .type = PEBBLE_BATTERY_CONNECTION_EVENT, - .battery_connection = { - .is_connected = value, - } - }; - event_put(&event); -} - -void battery_set_charge_enable(bool charging_enabled) { - periph_config_acquire_lock(); - prv_battery_set_charge_enable(charging_enabled); - periph_config_release_lock(); -} - -void battery_set_fast_charge(bool fast_charge_enabled) { - periph_config_acquire_lock(); - prv_battery_set_fast_charge(fast_charge_enabled); - periph_config_release_lock(); -} - diff --git a/src/fw/drivers/battery/battery_voltage_monitor.c b/src/fw/drivers/battery/battery_voltage_monitor.c deleted file mode 100644 index f3e648cff5..0000000000 --- a/src/fw/drivers/battery/battery_voltage_monitor.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/battery.h" - -#include "board/board.h" -#include "drivers/voltage_monitor.h" - -ADCVoltageMonitorReading battery_read_voltage_monitor(void) { - VoltageReading info; - voltage_monitor_read(VOLTAGE_MONITOR_BATTERY, &info); - return (ADCVoltageMonitorReading) { - .vref_total = info.vref_total, - .vmon_total = info.vmon_total, - }; -} \ No newline at end of file diff --git a/src/fw/drivers/battery/wscript_build b/src/fw/drivers/battery/wscript_build new file mode 100644 index 0000000000..aeda4c81b2 --- /dev/null +++ b/src/fw/drivers/battery/wscript_build @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = ['battery_common.c'] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_BATTERY_QEMU: + sources.extend([ + '../qemu/qemu_battery.c', + ]) + use.extend([ + 'driver_qemu_serial', + 'driver_qemu_settings', + ]) +elif bld.env.CONFIG_BATTERY_PMIC: + pass + +bld.objects( + name='driver_battery', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_battery') diff --git a/src/fw/drivers/button.c b/src/fw/drivers/button.c deleted file mode 100644 index 6f6c71d11a..0000000000 --- a/src/fw/drivers/button.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/button.h" - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" - -static void initialize_button_common(void) { - if (!BOARD_CONFIG_BUTTON.button_com.gpio) { - // This board doesn't use a button common pin. - return; - } - - // Configure BUTTON_COM to drive low. When the button - // is pressed this pin will be connected to the pin for the - // button. - gpio_use(BOARD_CONFIG_BUTTON.button_com.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_BUTTON.button_com.gpio_pin; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_Init(BOARD_CONFIG_BUTTON.button_com.gpio, &GPIO_InitStructure); - GPIO_WriteBit(BOARD_CONFIG_BUTTON.button_com.gpio, BOARD_CONFIG_BUTTON.button_com.gpio_pin, 0); - - gpio_release(BOARD_CONFIG_BUTTON.button_com.gpio); -} - -static void initialize_button(const ButtonConfig* config) { - // Configure the pin itself - gpio_use(config->gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_StructInit(&GPIO_InitStructure); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStructure.GPIO_PuPd = config->pull; - GPIO_InitStructure.GPIO_Pin = config->gpio_pin; - GPIO_Init(config->gpio, &GPIO_InitStructure); - - gpio_release(config->gpio); -} - -bool button_is_pressed(ButtonId id) { - const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id]; - gpio_use(button_config->gpio); - uint8_t bit = GPIO_ReadInputDataBit(button_config->gpio, button_config->gpio_pin); - gpio_release(button_config->gpio); - return (BOARD_CONFIG_BUTTON.active_high) ? bit : !bit; -} - -uint8_t button_get_state_bits(void) { - uint8_t button_state = 0x00; - for (int i = 0; i < NUM_BUTTONS; ++i) { - button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i; - } - return button_state; -} - -void button_init(void) { - periph_config_acquire_lock(); - - periph_config_enable(SYSCFG, RCC_APB2Periph_SYSCFG); - - initialize_button_common(); - for (int i = 0; i < NUM_BUTTONS; ++i) { - initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]); - } - - periph_config_disable(SYSCFG, RCC_APB2Periph_SYSCFG); - - periph_config_release_lock(); -} - -bool button_selftest(void) { - return button_get_state_bits() == 0; -} - -void command_button_read(const char* button_id_str) { - int button = atoi(button_id_str); - - if (button < 0 || button >= NUM_BUTTONS) { - prompt_send_response("Invalid button"); - return; - } - - if (button_is_pressed(button)) { - prompt_send_response("down"); - } else { - prompt_send_response("up"); - } -} - diff --git a/src/fw/drivers/button.h b/src/fw/drivers/button.h index 98ede8bb89..e1f58f6764 100644 --- a/src/fw/drivers/button.h +++ b/src/fw/drivers/button.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,7 +10,7 @@ void button_init(void); +void button_set_rotated(bool rotated); + bool button_is_pressed(ButtonId id); uint8_t button_get_state_bits(void); - -bool button_selftest(void); diff --git a/src/fw/drivers/button_id.h b/src/fw/drivers/button_id.h index 02aa1c75d8..069d8e99d2 100644 --- a/src/fw/drivers/button_id.h +++ b/src/fw/drivers/button_id.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/clocksource.h b/src/fw/drivers/clocksource.h index 000463d4c3..132d1d2106 100644 --- a/src/fw/drivers/clocksource.h +++ b/src/fw/drivers/clocksource.h @@ -1,41 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#if MICRO_FAMILY_STM32F2 || MICRO_FAMILY_STM32F4 || MICRO_FAMILY_STM32F7 - -//! Configure and start the 32 kHz LSE clock. -void clocksource_lse_configure(void); - -//! Returns true iff LSE is running. -bool clocksource_is_lse_started(void); - -//! Enable or disable 32 kHz clock output on pin MCO1. -//! -//! Prerequisite: clocksource_lse_configure() must be called beforehand to -//! enable the 32 kHz clock. -void clocksource_MCO1_enable(bool on); - -#endif - -#if MICRO_FAMILY_NRF5 - +#ifdef CONFIG_SOC_NRF52 /** @brief Request HFXO clock (reference counted). */ void clocksource_hfxo_request(void); diff --git a/src/fw/drivers/cpumode.h b/src/fw/drivers/cpumode.h new file mode 100644 index 0000000000..6b20369b2d --- /dev/null +++ b/src/fw/drivers/cpumode.h @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +typedef enum { + CPUMode_LowPower = 0, + CPUMode_HighPerformance = 1, +} CPUMode; + +void cpumode_set(CPUMode mode); + diff --git a/src/fw/drivers/cpumode/Kconfig b/src/fw/drivers/cpumode/Kconfig new file mode 100644 index 0000000000..913b9d51dd --- /dev/null +++ b/src/fw/drivers/cpumode/Kconfig @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig CPUMODE + bool "CPU Mode" + help + CPU performance mode + +if CPUMODE + +config CPUMODE_SF32LB + bool "SF32LB" + help + CPU performance mode for SF32LB platforms. + +config CPUMODE_STUB + bool "Stub" + help + Stub CPU performance mode. + +endif # CPUMODE \ No newline at end of file diff --git a/src/fw/drivers/cpumode/sf32lb.c b/src/fw/drivers/cpumode/sf32lb.c new file mode 100644 index 0000000000..e7a38a0f16 --- /dev/null +++ b/src/fw/drivers/cpumode/sf32lb.c @@ -0,0 +1,23 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/cpumode.h" + +#include + +#define HCPU_FREQ_LP_MHZ 48 +#define HCPU_FREQ_HP_MHZ 240 + +void cpumode_set(CPUMode mode) { + __disable_irq(); + + if (mode == CPUMode_LowPower) { + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_HP_TICK, RCC_CLK_TICK_HRC48); + HAL_RCC_HCPU_ConfigHCLK(HCPU_FREQ_LP_MHZ); + } else { + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_HP_TICK, RCC_CLK_TICK_HXT48); + HAL_RCC_HCPU_ConfigHCLK(HCPU_FREQ_HP_MHZ); + } + + __enable_irq(); +} \ No newline at end of file diff --git a/src/fw/drivers/cpumode/stub.c b/src/fw/drivers/cpumode/stub.c new file mode 100644 index 0000000000..11028858b8 --- /dev/null +++ b/src/fw/drivers/cpumode/stub.c @@ -0,0 +1,7 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/cpumode.h" + +void cpumode_set(CPUMode mode) { +} \ No newline at end of file diff --git a/src/fw/drivers/cpumode/wscript_build b/src/fw/drivers/cpumode/wscript_build new file mode 100644 index 0000000000..ec66850120 --- /dev/null +++ b/src/fw/drivers/cpumode/wscript_build @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes'] + +if bld.env.CONFIG_CPUMODE_SF32LB: + sources.append('sf32lb.c') + use.append('hal_sifli') +elif bld.env.CONFIG_CPUMODE_STUB: + sources.append('stub.c') + +bld.objects( + name='driver_cpumode', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_cpumode') diff --git a/src/fw/drivers/debounced_button.c b/src/fw/drivers/debounced_button.c deleted file mode 100644 index 196ff7237e..0000000000 --- a/src/fw/drivers/debounced_button.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/debounced_button.h" - -#include "board/board.h" -#include "drivers/button.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/timer.h" -#include "kernel/events.h" -#include "kernel/util/stop.h" -#include "system/bootbits.h" -#include "system/reset.h" -#include "util/bitset.h" -#include "kernel/util/sleep.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include "projdefs.h" - -// We want TIM4 to run at 32KHz -static const uint32_t TIMER_FREQUENCY_HZ = 32000; -// Sample the buttons every 2ms to debounce -static const uint32_t TIMER_PERIOD_TICKS = 64; -// A button must be stable for 20 samples (40ms) to be accepted. -static const uint32_t NUM_DEBOUNCE_SAMPLES = 20; - -#define RESET_BUTTONS ((1 << BUTTON_ID_SELECT) | (1 << BUTTON_ID_BACK)) - -#define DEBOUNCE_SAMPLES_PER_SECOND (TIMER_FREQUENCY_HZ / TIMER_PERIOD_TICKS) - -// This reset-buttons-held timeout must be lower than the PMIC's back-button-reset timeout, -// which is ~8-11s. The spacing between these timeouts should be large enough to avoid accidentally -// shutting down the device when a customer is attempting to reset. Therefore the FW's -// reset-buttons-held timeout is set to 5 seconds: -#define RESET_THRESHOLD_SAMPLES (5 * DEBOUNCE_SAMPLES_PER_SECOND) - - -static void initialize_button_timer(void) { - periph_config_enable(TIM4, RCC_APB1Periph_TIM4); - - NVIC_InitTypeDef NVIC_InitStructure; - /* Enable the TIM4 gloabal Interrupt */ - TIM_ClearITPendingBit(TIM4, TIM_IT_Update); - NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0b; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - TIM_TimeBaseInitTypeDef tim_config; - TIM_TimeBaseStructInit(&tim_config); - tim_config.TIM_Period = TIMER_PERIOD_TICKS; - // The timer is on ABP1 which is clocked by PCLK1 - TimerConfig tc = { - .peripheral = TIM4, - .config_clock = RCC_APB1Periph_TIM4, - }; - tim_config.TIM_Prescaler = timer_find_prescaler(&tc, - TIMER_FREQUENCY_HZ); - tim_config.TIM_CounterMode = TIM_CounterMode_Up; - TIM_TimeBaseInit(TIM4, &tim_config); - - periph_config_disable(TIM4, RCC_APB1Periph_TIM4); -} - -static bool prv_check_timer_enabled(void) { - // We're only enabled if all the configuration is correct. - - return (TIM4->CR1 & TIM_CR1_CEN) && // TIM_Cmd - (TIM4->DIER & TIM_IT_Update) && // TIM_ITConfig - (RCC->APB1ENR & RCC_APB1Periph_TIM4); // RCC_APB1PeriphClockCmd -} - -static void disable_button_timer(void) { - if (prv_check_timer_enabled()) { - TIM_Cmd(TIM4, DISABLE); - TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); - periph_config_disable(TIM4, RCC_APB1Periph_TIM4); - - // Allow us to enter stop mode - stop_mode_enable(InhibitorButton); - } -} - -static void prv_enable_button_timer(void) { - // Don't let the timer interrupt us while we're mucking with it. - __disable_irq(); - if (!prv_check_timer_enabled()) { - periph_config_enable(TIM4, RCC_APB1Periph_TIM4); - TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); - TIM_Cmd(TIM4, ENABLE); - - // Prevent us from entering stop mode (and disabling the clock timer) - stop_mode_disable(InhibitorButton); - } - __enable_irq(); -} - -static void prv_button_interrupt_handler(bool *should_context_switch) { - prv_enable_button_timer(); -} - -static void clear_stuck_button(ButtonId button_id) { - __disable_irq(); - - const uint32_t button_counter_register = RTC_ReadBackupRegister(STUCK_BUTTON_REGISTER); - if (button_counter_register != 0) { - // Create bitmask with all 1s, except on the counter byte for this button_id in button_counter_register. AND to mask out: - const uint32_t updated_button_counter_register = button_counter_register & ~(0xff << (button_id << 3)); - if (button_counter_register != updated_button_counter_register) { - RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, updated_button_counter_register); - } - } - - __enable_irq(); -} - -void debounced_button_init(void) { - button_init(); - -#if defined(BOARD_SNOWY_BB2) || defined(BOARD_SPALDING_BB2) - // Snowy BB2s have a capacitor that results in a really slow rise time (~0.4ms). Sleep for - // at least 1 ms to prevent fake button events - psleep(2); -#endif - - for (int i = 0; i < NUM_BUTTONS; ++i) { - const ExtiConfig config = BOARD_CONFIG_BUTTON.buttons[i].exti; - exti_configure_pin(config, ExtiTrigger_RisingFalling, prv_button_interrupt_handler); - exti_enable(config); - } - - initialize_button_timer(); - - // If someone is holding down a button, we need to start up the timer immediately ourselves as - // we won't get a button down interrupt to start it. - if (button_get_state_bits() != 0) { - prv_enable_button_timer(); - } -} - - -// Interrupt Service Routines -/////////////////////////////////////////////////////////// -void TIM4_IRQHandler(void) { - // This array holds the number of samples we have for the button being in a different state than - // the current debounced state of the button. - static uint32_t s_button_timers[] = {0, 0, 0, 0}; - // A bitset of the current states of the buttons after the debouncing is done. - static uint32_t s_debounced_button_state = 0; - - // Should we tell the scheduler to attempt to context switch after this function has completed? - bool should_context_switch = pdFALSE; - // Should we power down this interrupt timer once we're done here or should we leave it on? - bool can_power_down_tim4 = true; - - // We handle all 4 buttons every time this interrupt is fired. - for (int i = 0; i < NUM_BUTTONS; ++i) { - // What stable state is the button in, according to the debouncing algorithm? - bool debounced_button_state = bitset32_get(&s_debounced_button_state, i); - // What is the current physical state of the button? - bool is_pressed = button_is_pressed(i); - - if (is_pressed == debounced_button_state) { - // If the state is not changing, skip this button. - s_button_timers[i] = 0; - continue; - } - - // Leave the timer running so we can track this button that's changing state. - can_power_down_tim4 = false; - - s_button_timers[i] += 1; - - // If the button has been in a stable state that's different than the debounced state for enough - // samples, change the debounced state to the stable state and generate an event. - if (s_button_timers[i] == NUM_DEBOUNCE_SAMPLES) { - s_button_timers[i] = 0; - - bitset32_update(&s_debounced_button_state, i, is_pressed); - - if (!is_pressed) { - // A button has been released. Make sure we weren't tracking this as a stuck button. - clear_stuck_button(i); - } - - PebbleEvent e = { - .type = (is_pressed) ? PEBBLE_BUTTON_DOWN_EVENT : PEBBLE_BUTTON_UP_EVENT, - .button.button_id = i - }; - should_context_switch = event_put_isr(&e); - } - } - -#if !defined(MANUFACTURING_FW) - // Now that s_debounced_button_state is updated, check to see if the user is holding down the reset - // combination. - static uint32_t s_hard_reset_timer = 0; - if ((s_debounced_button_state & RESET_BUTTONS) == RESET_BUTTONS) { - s_hard_reset_timer += 1; - can_power_down_tim4 = false; - - if (s_hard_reset_timer > RESET_THRESHOLD_SAMPLES) { - __disable_irq(); - - // If the UP button is held at the moment the timeout is hit, set the force-PRF bootbit: - const bool force_prf = (s_debounced_button_state & (1 << BUTTON_ID_UP)); - if (force_prf) { - boot_bit_set(BOOT_BIT_FORCE_PRF); - } - - RebootReason reason = { - .code = force_prf ? RebootReasonCode_PrfResetButtonsHeld : - RebootReasonCode_ResetButtonsHeld - }; - reboot_reason_set(&reason); - - // Don't use system_reset here. This back door absolutely must work. Just hard reset. - system_hard_reset(); - } - } else { - s_hard_reset_timer = 0; - } -#endif - - - if (can_power_down_tim4) { - __disable_irq(); - disable_button_timer(); - __enable_irq(); - } - - TIM_ClearITPendingBit(TIM4, TIM_IT_Update); - - portEND_SWITCHING_ISR(should_context_switch); -} - -// Serial commands -/////////////////////////////////////////////////////////// -void command_put_raw_button_event(const char* button_index, const char* is_button_down_event) { - PebbleEvent e; - int is_down = atoi(is_button_down_event); - int button = atoi(button_index); - - if ((button < 0 || button > NUM_BUTTONS) || (is_down != 1 && is_down != 0)) { - return; - } - e.type = (is_down) ? PEBBLE_BUTTON_DOWN_EVENT : PEBBLE_BUTTON_UP_EVENT; - e.button.button_id = button; - event_put(&e); -} diff --git a/src/fw/drivers/debounced_button.h b/src/fw/drivers/debounced_button.h index cd14b2bdf1..449c2e00de 100644 --- a/src/fw/drivers/debounced_button.h +++ b/src/fw/drivers/debounced_button.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/display/Kconfig b/src/fw/drivers/display/Kconfig new file mode 100644 index 0000000000..f3b5992d02 --- /dev/null +++ b/src/fw/drivers/display/Kconfig @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig DISPLAY + bool "Display" + help + Display Drivers + +if DISPLAY + +config DISPLAY_JDI_SF32LB + bool "JDI (SiFli SF32LB)" + select HAL_SIFLI_LCD + help + Support for JDI display via SiFli SF32LB. + +config DISPLAY_SHARP_LS013B7DH01_NRF5 + bool "Sharp LS013B7DH01 (nRF5)" + help + Support for the Sharp LS013B7DH01 display via nRF5. + +config DISPLAY_QEMU + bool "QEMU Framebuffer Display" + help + Support for the QEMU framebuffer display peripheral. + +module = DRIVER_DISPLAY +module-str = Display +source "src/fw/Kconfig.template.log_level" + +endif # DISPLAY diff --git a/src/fw/drivers/display/display.h b/src/fw/drivers/display/display.h index 30abf9bdfe..eaf2b62adf 100644 --- a/src/fw/drivers/display/display.h +++ b/src/fw/drivers/display/display.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,37 +10,27 @@ #include typedef struct { - uint8_t address; + uint16_t address; uint8_t* data; } DisplayRow; typedef bool(*NextRowCallback)(DisplayRow* row); typedef void(*UpdateCompleteCallback)(void); -//! Show the splash screen before the display has been fully initialized. -void display_show_splash_screen(void); +//! Update the display with a boot animation frame. +//! This is a simple interface for the boot animation service to send frames +//! to the display before the compositor is initialized. +//! @param framebuffer Pointer to the framebuffer data (DISPLAY_FRAMEBUFFER_BYTES) +void display_update_boot_frame(uint8_t *framebuffer); void display_init(void); -uint32_t display_baud_rate_change(uint32_t new_frequency_hz); - void display_clear(void); void display_set_enabled(bool enabled); -void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb); - -bool display_update_in_progress(void); - -void display_pulse_vcom(void); +void display_set_rotated(bool rotated); -//! Show the panic screen. -//! -//! This function is only defined if the display hardware and driver support it. -void display_show_panic_screen(uint32_t error_code); - -typedef struct GPoint GPoint; - -void display_set_offset(GPoint offset); +void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb); -GPoint display_get_offset(void); +bool display_update_in_progress(void); \ No newline at end of file diff --git a/src/fw/drivers/display/ice40lp/ice40lp.c b/src/fw/drivers/display/ice40lp/ice40lp.c deleted file mode 100644 index a6199fa170..0000000000 --- a/src/fw/drivers/display/ice40lp/ice40lp.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/display/ice40lp/ice40lp.h" - -#include -#include -#include -#include - -#include "FreeRTOS.h" -#include "applib/graphics/framebuffer.h" -#include "applib/graphics/gtypes.h" -#include "board/board.h" -#include "board/display.h" -#include "debug/power_tracking.h" -#include "drivers/clocksource.h" -#include "drivers/display/ice40lp/fpga_bitstream.auto.h" -#include "drivers/display/ice40lp/ice40lp_definitions.h" -#include "drivers/display/ice40lp/ice40lp_internal.h" -#include "drivers/display/ice40lp/snowy_boot.h" -#include "drivers/dma.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "drivers/spi.h" -#include "drivers/spi_dma.h" -#include "kernel/events.h" -#include "kernel/event_loop.h" -#include "kernel/util/sleep.h" -#include "kernel/util/stop.h" -#include "os/mutex.h" -#include "os/tick.h" -#include "semphr.h" -#include "services/common/analytics/analytics.h" -#include "services/common/compositor/compositor.h" -#include "services/common/compositor/compositor_display.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/profiler.h" -#include "task.h" -#include "util/attributes.h" -#include "util/net.h" -#include "util/reverse.h" -#include "util/size.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#if DISPLAY_ORIENTATION_ROW_MAJOR || DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED -#define DISP_LINES DISP_ROWS -#define DISP_PIXELS DISP_COLS -#elif DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED -#define DISP_LINES DISP_COLS -#define DISP_PIXELS DISP_ROWS -#else -#error "Unknown or missing display orientation define" -#endif - -typedef void (*PopulateLineCB)( - int column, uint8_t * restrict line_buffer, const void *cb_data); - -//! 2 buffers to hold line data being transferred. -static uint8_t DMA_READ_BSS s_line_buffer[2][DISP_PIXELS]; -//! buffer index keeps track of which line buffer is in use -static uint32_t s_buffer_idx; -//! line index is the line of the display currently being updated -static uint32_t s_line_index; - -//! offset for shifting the image origin from the display's origin -//! display coordinates (0,0) are top-left, -//! offset positive values shift right and down -static GPoint s_disp_offset; - -static bool s_update_in_progress; -static bool s_terminate_pending; - -static RtcTicks s_start_ticks; - -static bool s_initialized = false; - -//! lockout to prevent display updates when the panic screen is shown -static bool s_panic_screen_lockout; - -static PebbleMutex *s_display_update_mutex; -static SemaphoreHandle_t s_fpga_busy; - -static UpdateCompleteCallback s_update_complete_callback; - -static void prv_start_dma_transfer(uint8_t *addr, uint32_t length); -static void display_interrupt_intn(bool *should_context_switch); - -static inline OPTIMIZE_FUNC(2) void prv_pixel_scramble( - uint8_t * restrict line_buf, const uint8_t px_odd, const uint8_t px_even, - const int offset) { - uint8_t msb, lsb; - msb = (px_odd & 0b00101010) | (px_even & 0b00101010) >> 1; - lsb = (px_odd & 0b00010101) << 1 | (px_even & 0b00010101); - line_buf[offset/2] = msb; - line_buf[offset/2 + DISP_PIXELS/2] = lsb; -} - -static inline OPTIMIZE_FUNC(2) void prv_row_major_get_line( - uint8_t * restrict line, const uint8_t * restrict image_buf, - int index) { -#if DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED - // Optimized line renderer for Robert. - // Could easily apply to the other screens, but only Robert really needs it. - // By loading both pixels with a single load, we can cut down code size (cache benefit) - // and decrease number of bus accesses. - // Theoretically loading 4 pixels at a time should be better, but GCC generated much - // worse code that way. - -#if DISPLAY_ORIENTATION_ROTATED_180 - // Setup srcbuf to be the end, since we need to scan backwards horizontally - const uint16_t *srcbuf = (const uint16_t *)(image_buf + DISP_PIXELS * index); - for (int dst_offset = 0; dst_offset < DISP_PIXELS/2; dst_offset++) { - // Get the two pixels - const uint16_t pix = *srcbuf++; - - // Swizzle the pixels - line[0] = ((pix) & 0b101010) | ((pix >> 9) & 0b010101); - line[DISP_PIXELS/2] = ((pix << 1) & 0b101010) | ((pix >> 8) & 0b010101); - line++; - } -#else - // Setup srcbuf to be the end, since we need to scan backwards horizontally - const uint16_t *srcbuf = (const uint16_t *)(image_buf + DISP_PIXELS * (DISP_LINES - index) - 2); - for (int dst_offset = 0; dst_offset < DISP_PIXELS/2; dst_offset++) { - // Get the two pixels - const uint16_t pix = *srcbuf--; - - // Swizzle the pixels - line[0] = ((pix >> 8) & 0b101010) | ((pix >> 1) & 0b010101); - line[DISP_PIXELS/2] = ((pix >> 7) & 0b101010) | ((pix) & 0b010101); - line++; - } -#endif // DISPLAY_ORIENTATION_ROTATED_180 -#else - // adjust line index according to display offset, - // populate blank (black) line if this exceeds the source framebuffer - index -= s_disp_offset.y; - if (!WITHIN(index, 0, DISP_LINES - 1)) { - memset(line, 0, DISP_PIXELS); - return; - } - - uint8_t odd, even; -#if PLATFORM_SPALDING - const GBitmapDataRowInfoInternal *row_infos = g_gbitmap_spalding_data_row_infos; - const uint8_t *row_start = image_buf + row_infos[index].offset; -#endif - - // Line starts with MSB of each color in all pixels - // Line finishes with LSB of each color in all pixels - // separate src_offset is adjusted according to manufacturing offset, - // loop condition/continue makes sure we don't read past boundaries of src framebuffer - for (int src_offset = -s_disp_offset.x, dst_offset = 0; - src_offset < DISP_PIXELS && dst_offset < DISP_PIXELS; - src_offset += 2, dst_offset += 2) { -#if !PLATFORM_SPALDING - if (src_offset < 0) { - continue; - } -#endif - -#if DISPLAY_ORIENTATION_ROW_MAJOR - #if PLATFORM_SPALDING - even = WITHIN(src_offset + 1, row_infos[index].min_x, row_infos[index].max_x) ? - row_start[src_offset + 1] : 0; - odd = WITHIN(src_offset, row_infos[index].min_x, row_infos[index].max_x) ? - row_start[src_offset] : 0; - #else - #error Unsupported display - #endif -#elif DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED - even = image_buf[DISP_COLS * (DISP_ROWS-2 - src_offset) + index]; - odd = image_buf[DISP_COLS * (DISP_ROWS-2 - src_offset + 1) + index]; -#endif - prv_pixel_scramble(line, odd, even, dst_offset); - } -#endif -} - -static void prv_framebuffer_populate_line( - int index, uint8_t * restrict line) { - const uint8_t *frame_buffer = compositor_get_framebuffer()->buffer; - prv_row_major_get_line(line, frame_buffer, index); -} - -static void enable_display_dma_clock(void) { - power_tracking_start(PowerSystemMcuDma2); -} - -static void disable_display_dma(void) { - // Properly disable DMA interrupts and deinitialize the DMA controller to prevent pending - // interrupts from firing when the clock is re-enabled (this could possibly cause a stray - // terminate callback being added to kernel main) - - spi_ll_slave_write_dma_stop(ICE40LP->spi_port); - power_tracking_stop(PowerSystemMcuDma2); -} - -static void prv_terminate_transfer(void *data) { - if (s_panic_screen_lockout) { - return; - } - - // Only need intn when communicating with the display. - // Disable EXTI interrupt before ending the frame to prevent possible race condition resulting - // from a almost empty FIFO on the FPGA triggering a terminate call before the interrupt is - // disabled - exti_disable(ICE40LP->busy_exti); - - disable_display_dma(); - display_spi_end_transaction(); - - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME); - - s_update_in_progress = false; - s_terminate_pending = false; - - mutex_unlock(s_display_update_mutex); - - // Store temporary variable, then NULL, to protect against the case where the compositor calls - // into the display driver from the callback, then we NULL out the update complete callback - // afterwards. - UpdateCompleteCallback update_complete_cb = s_update_complete_callback; - s_update_complete_callback = NULL; - if (update_complete_cb) { - update_complete_cb(); - } -} - -static uint32_t prv_get_next_buffer_idx(uint32_t idx) { - return (idx + 1) % ARRAY_LENGTH(s_line_buffer); -} - -//! Wait for the FPGA to finish updating the display. -//! @returns true if the FPGA is busy on exit -static bool prv_wait_busy(void) { - // Make sure that semaphore token count is zero before we wait on it and before we check the state - // of the FPGA busy line to prevent the semaphore take/give from getting out of sync (not exactly - // sure what race condition causes the out of sync bug, but it seems to happen after a while). - // See https://pebbletechnology.atlassian.net/browse/PBL-21904 - xSemaphoreTake(s_fpga_busy, 0); - - if (!display_busy()) { - return false; - } - - // A full frame should take no longer than 33 msec to draw. If we are waiting - // longer than that, something is very wrong. - TickType_t max_wait_time_ticks = milliseconds_to_ticks(40); - bool busy_on_exit = false; - if (xSemaphoreTake(s_fpga_busy, max_wait_time_ticks) != pdTRUE) { - PBL_LOG(LOG_LEVEL_ERROR, "Display not coming out of a busy state."); - // Nothing needs to be done to recover the FPGA from a bad state. The - // falling edge of SCS (to start a new frame) resets the FPGA logic, - // clearing the error state. - busy_on_exit = true; - } - return busy_on_exit; -} - -static void prv_reprogram_display(void) { - // CDONE is expected to go low during reprogramming. Don't pollute the logs - // with "CDONE has gone low" messages. - analytics_inc(ANALYTICS_DEVICE_METRIC_FPGA_REPROGRAM_COUNT, - AnalyticsClient_System); - exti_disable(ICE40LP->cdone_exti); - display_program(s_fpga_bitstream, sizeof(s_fpga_bitstream)); - exti_enable(ICE40LP->cdone_exti); -} - -static void prv_cdone_low_handler(void *context) { - PBL_LOG(LOG_LEVEL_ERROR, - "CDONE has gone low. The FPGA has lost its configuration."); - - if (!mutex_lock_with_timeout(s_display_update_mutex, 200)) { - PBL_LOG(LOG_LEVEL_DEBUG, - "Couldn't lock out display driver to reprogram FPGA."); - return; - } - prv_reprogram_display(); - PBL_ASSERTN(!display_busy()); - mutex_unlock(s_display_update_mutex); -} - -static void prv_cdone_low_isr(bool *should_context_switch) { - system_task_add_callback_from_isr(prv_cdone_low_handler, NULL, - should_context_switch); -} - -void display_init(void) { - if (s_initialized) { - return; - } - - clocksource_MCO1_enable(true); - - s_panic_screen_lockout = false; - s_display_update_mutex = mutex_create(); - s_fpga_busy = xSemaphoreCreateBinary(); - s_update_in_progress = false; - s_terminate_pending = false; - s_update_complete_callback = NULL; - - display_start(); - display_program(s_fpga_bitstream, sizeof(s_fpga_bitstream)); - // enable the power rails - display_power_enable(); - - // Set up our INT_N interrupt, aka the "busy line" from the FPGA - exti_configure_pin(ICE40LP->busy_exti, ExtiTrigger_Falling, display_interrupt_intn); - // Set up an interrupt to detect the FPGA forgetting its configuration due to - // e.g. an ESD event. - exti_configure_pin(ICE40LP->cdone_exti, ExtiTrigger_Falling, prv_cdone_low_isr); - exti_enable(ICE40LP->cdone_exti); - - s_initialized = true; -} - -bool display_update_in_progress(void) { - // Set this timeout to a relatively large value so that we don't unlock the mutex too early when - // the DMA controller that is used by the display is being heavily used by another driver (e.g. - // the bluetooth HCI port) and delays the completion of the update or kernel_main is busy with - // other tasks (e.g. voice encoding). - // (see https://pebbletechnology.atlassian.net/browse/PBL-21923) - static const RtcTicks MAX_BUSY_TICKS = 200; - - bool in_progress = !mutex_lock_with_timeout(s_display_update_mutex, 0); - if (!in_progress) { - mutex_unlock(s_display_update_mutex); - } else if (!s_panic_screen_lockout) { - if ((rtc_get_ticks() - s_start_ticks) > MAX_BUSY_TICKS) { - // Ensure that terminate transfer is not enqueued on kernel_main twice when it is busy to - // prevent terminate transfer from being invoked twice - // see https://pebbletechnology.atlassian.net/browse/PBL-22084 - // Read and set the termination flag in a critical section to prevent an interrupt pending - // a transfer if these events occur simultaneously - bool pend_terminate = false; - portENTER_CRITICAL(); - if (!s_terminate_pending) { - s_terminate_pending = true; - pend_terminate = true; - } - portEXIT_CRITICAL(); - if (pend_terminate) { - PROFILER_NODE_STOP(display_transfer); - launcher_task_add_callback(prv_terminate_transfer, NULL); - } - } - } - return in_progress; -} - -static void prv_do_display_update(void) { - - if (!mutex_lock_with_timeout(s_display_update_mutex, 0)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Couldn't start update."); - return; - } - if (s_panic_screen_lockout) { - mutex_unlock(s_display_update_mutex); - return; - } - - analytics_stopwatch_start(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME, PebbleTask_App); - analytics_inc(ANALYTICS_DEVICE_METRIC_DISPLAY_UPDATES_PER_HOUR, AnalyticsClient_System); - - // Communicating with the display, need intn. - exti_enable(ICE40LP->busy_exti); - - enable_display_dma_clock(); - - // Send the first line... - prv_framebuffer_populate_line(0, s_line_buffer[s_buffer_idx]); - - prv_wait_busy(); - display_spi_begin_transaction(); - display_start_frame(); - if (display_busy()) { - // If the FPGA was stuck busy before, starting the frame (SCS falling edge) - // should get it unstuck. If BUSY is still asserted, the FPGA might be - // unprogrammed or malfunctioning. Either way, reprogramming it should get - // it back into working order. - PBL_LOG(LOG_LEVEL_WARNING, - "Reprogramming FPGA because busy is stuck asserted"); - prv_reprogram_display(); - bool is_busy = display_busy(); -#ifdef TARGET_QEMU - // Bold light-red text on a black background -#define M(s) PBL_LOG(LOG_LEVEL_ALWAYS, "\033[1;91;40m" s "\033[0m") - if (is_busy) { - M("################################################"); - M("# THIS IS A QEMU BUILD #"); - M("################################################"); - M("# #"); - M("# The QEMU display driver \033[1;4mdoes not work\033[24m on #"); - M("# physical hardware. You must build without #"); - M("# the --qemu switch when flashing a bigboard. #"); - M("################################################"); -#undef M - psleep(3000); - } -#endif - PBL_ASSERTN(!is_busy); - // The SPI clock is disabled by prv_reprogram_display. - display_spi_begin_transaction(); - display_start_frame(); - } - // set line index after waiting for display to free up - uint32_t current_idx = s_buffer_idx; - s_buffer_idx = prv_get_next_buffer_idx(s_buffer_idx); - - // populate the second line and set the next line to be processed as the third line - prv_framebuffer_populate_line(1, s_line_buffer[s_buffer_idx]); - s_line_index = 2; - - stop_mode_disable(InhibitorDisplay); - - PROFILER_NODE_START(display_transfer); - - s_update_in_progress = true; - s_start_ticks = rtc_get_ticks(); - // Start the DMA last to prevent possible race conditions caused by unfortunately timed context - // switch - prv_start_dma_transfer(s_line_buffer[current_idx], DISP_PIXELS); -} - -//! -//! Starts a redraw of the entire framebuffer to the screen. -//! -//! Currently does NOT: -//! - make use of nrcb due to rotation requirements; instead accesses the framebuffer directly -//! - support partial screen updates -//! -void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { - PBL_ASSERTN(uccb != NULL); - - s_update_complete_callback = uccb; - prv_do_display_update(); -} - -static void prv_do_display_update_cb(void *ignored) { - prv_do_display_update(); -} - -void display_clear(void) { - // Set compositor buffer to the powered off color (black) and redraw. - // Note that compositor owns this framebuffer! - memset(compositor_get_framebuffer()->buffer, 0x00, FRAMEBUFFER_SIZE_BYTES); - - // The display ISRs pend events on KernelMain and thus implicitly assume - // that the display update operation began on KernelMain. If we are already - // running on KernelMain, then just run the display update, otherwise schedule - // a callback to run on KernelMain that performs the update - if (pebble_task_get_current() == PebbleTask_KernelMain) { - prv_do_display_update(); - } else { - launcher_task_add_callback(prv_do_display_update_cb, NULL); - } -} - -void display_set_enabled(bool enabled) { - // TODO: Implement this function to enable/disable the display. -} - -void display_pulse_vcom(void) { } - -//! @return false if there are no more lines to transfer, true if a new line transfer was started -static bool prv_write_next_line(bool *should_context_switch) { - if (s_line_index == 0 && (!s_terminate_pending)) { - s_terminate_pending = true; - PROFILER_NODE_STOP(display_transfer); - - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_terminate_transfer, - }, - }; - *should_context_switch = event_put_isr(&e); - return false; - } - - prv_start_dma_transfer(s_line_buffer[s_buffer_idx], DISP_PIXELS); - - if (s_line_index < DISP_LINES) { - s_buffer_idx = prv_get_next_buffer_idx(s_buffer_idx); - prv_framebuffer_populate_line(s_line_index, s_line_buffer[s_buffer_idx]); - s_line_index++; - } else { - // done - s_line_index = 0; - } - return true; -} - -//! When the FPGA leaves the busy state while frame data is being sent, this interrupt will -//! signal that the next line can be sent to the display. -static void display_interrupt_intn(bool *should_context_switch) { - if (s_update_in_progress) { - if (!spi_ll_slave_dma_in_progress(ICE40LP->spi_port)) { - // DMA transfer is complete - if (prv_write_next_line(should_context_switch)) { - stop_mode_disable(InhibitorDisplay); - } - } - } else { - // Only release the semaphore after the end of an update - signed portBASE_TYPE was_higher_priority_task_woken = pdFALSE; - xSemaphoreGiveFromISR(s_fpga_busy, &was_higher_priority_task_woken); - *should_context_switch = (*should_context_switch) || - (was_higher_priority_task_woken != pdFALSE); - } -} - -// DMA -////////////////// - -//! This interrupt fires when the transfer of a line has completed. -static bool prv_write_dma_irq_handler(const SPISlavePort *request, void *context) { - PROFILER_NODE_START(framebuffer_dma); - bool should_context_switch = false; - if (display_busy() || !prv_write_next_line(&should_context_switch)) { - stop_mode_enable(InhibitorDisplay); - } - PROFILER_NODE_STOP(framebuffer_dma); - return should_context_switch; -} - -static void prv_start_dma_transfer(uint8_t *addr, uint32_t length) { - spi_ll_slave_write_dma_start(ICE40LP->spi_port, addr, length, prv_write_dma_irq_handler, NULL); -} - -void display_show_panic_screen(uint32_t error_code) { - // Lock out display driver from performing further updates - if (!mutex_lock_with_timeout(s_display_update_mutex, 200)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Couldn't lock out display driver."); - return; - } - s_panic_screen_lockout = true; - - exti_disable(ICE40LP->cdone_exti); - // Work around an issue which some boards exhibit where there is about a 50% - // probability that FPGA malfunctions and draw-scene command doesn't work. - // This can be detected in software as the FPGA asserts BUSY indefinitely. - int retries; - for (retries = 0; retries <= 20; ++retries) { - if (!display_switch_to_bootloader_mode()) { - // Probably an unconfigured FPGA. Nothing we can do about that. - break; - } - if (boot_display_show_error_code(error_code)) { - // Success! - if (retries > 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Took %d retries to display panic screen.", - retries); - } - break; - } - } - exti_enable(ICE40LP->cdone_exti); - - mutex_unlock(s_display_update_mutex); -} - -void display_show_splash_screen(void) { - // Assumes that the FPGA is already in bootloader mode but the SPI peripheral - // and GPIOs are not yet configured; exactly the state that the system is - // expected to be in before display_init() is called. - display_start(); - display_spi_configure_default(); - boot_display_show_boot_splash(); -} - -void display_set_offset(GPoint offset) { - s_disp_offset = offset; -} - -GPoint display_get_offset(void) { - return s_disp_offset; -} - -void analytics_external_collect_display_offset(void) { - analytics_set(ANALYTICS_DEVICE_METRIC_DISPLAY_OFFSET_X, s_disp_offset.x, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_DISPLAY_OFFSET_Y, s_disp_offset.y, AnalyticsClient_System); -} diff --git a/src/fw/drivers/display/ice40lp/ice40lp.h b/src/fw/drivers/display/ice40lp/ice40lp.h deleted file mode 100644 index b470d40648..0000000000 --- a/src/fw/drivers/display/ice40lp/ice40lp.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "../display.h" - -typedef enum { - DISP_COLOR_BLACK = 0, - DISP_COLOR_WHITE, - DISP_COLOR_RED, - DISP_COLOR_GREEN, - DISP_COLOR_BLUE, - DISP_COLOR_MAX -} DispColor; - -static const uint8_t s_display_colors[DISP_COLOR_MAX] = { 0x00, 0xff, 0xc0, 0x30, 0x0c }; - -void display_fill_color(uint8_t color_value); -void display_fill_stripes(); diff --git a/src/fw/drivers/display/ice40lp/ice40lp_definitions.h b/src/fw/drivers/display/ice40lp/ice40lp_definitions.h deleted file mode 100644 index d20f750624..0000000000 --- a/src/fw/drivers/display/ice40lp/ice40lp_definitions.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include -#include - -// TODO: Calling this ICE40LP kind of sucks, but I can't think of anything better without doing a -// whole big display system refactor, so we're keeping it as ICE40LP. -typedef struct ICE40LPDeviceState { -} ICE40LPDeviceState; - -typedef const struct ICE40LPDevice { - ICE40LPDeviceState *state; - - SPISlavePort *spi_port; - uint32_t base_spi_frequency; - uint32_t fast_spi_frequency; - - const OutputConfig creset; - const InputConfig cdone; - const InputConfig busy; - const ExtiConfig cdone_exti; - const ExtiConfig busy_exti; - - bool use_6v6_rail; -} ICE40LPDevice; diff --git a/src/fw/drivers/display/ice40lp/ice40lp_internal.c b/src/fw/drivers/display/ice40lp/ice40lp_internal.c deleted file mode 100644 index 812619ece9..0000000000 --- a/src/fw/drivers/display/ice40lp/ice40lp_internal.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/display/ice40lp/ice40lp_internal.h" - -#include "board/board.h" -#include "debug/power_tracking.h" -#include "drivers/display/ice40lp/ice40lp_definitions.h" -#include "drivers/periph_config.h" -#include "drivers/gpio.h" -#include "drivers/spi.h" -#include "drivers/exti.h" -#include "drivers/pmic.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/delay.h" -#include "util/size.h" -#include "util/sle.h" -#include "kernel/util/sleep.h" -#include "util/units.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include - -bool display_busy(void) { - return gpio_input_read(&ICE40LP->busy); -} - -void display_spi_begin_transaction(void) { - spi_ll_slave_acquire(ICE40LP->spi_port); - spi_ll_slave_scs_assert(ICE40LP->spi_port); - power_tracking_start(PowerSystemMcuSpi6); -} - -void display_spi_end_transaction(void) { - spi_ll_slave_scs_deassert(ICE40LP->spi_port); - spi_ll_slave_release(ICE40LP->spi_port); - power_tracking_stop(PowerSystemMcuSpi6); -} - -// Temporary code to support prv_do_display_update() logic that attempts to use the -// bootloader error display -void display_spi_configure_default(void) { - spi_slave_set_frequency(ICE40LP->spi_port, ICE40LP->base_spi_frequency); -} - -void display_start() { - periph_config_acquire_lock(); - - gpio_output_init(&ICE40LP->creset, GPIO_OType_OD, GPIO_Speed_25MHz); - gpio_input_init(&ICE40LP->cdone); - gpio_input_init(&ICE40LP->busy); - - periph_config_release_lock(); -} - -static bool prv_spin_until_creset_is(bool level) { - int timeout_us = 500 * 1000; - InputConfig creset_input = { - .gpio = ICE40LP->creset.gpio, - .gpio_pin = ICE40LP->creset.gpio_pin, - }; - while (timeout_us > 0) { - if (gpio_input_read(&creset_input) == level) return true; - delay_us(100); - timeout_us -= 100; - } - return false; -} - -static bool prv_wait_programmed(void) { - // The datasheet lists the typical NVCM configuration time as 56 ms. - // Something is wrong if it takes more than twice that time. - int timeout = 100 * 10; // * 100 microseconds - while (!gpio_input_read(&ICE40LP->cdone)) { - if (timeout-- == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "FPGA CDONE timeout expired!"); - return false; - } - delay_us(100); - } - return true; -} - -static bool prv_try_program(const uint8_t *fpga_bitstream, - uint32_t bitstream_size) { - display_spi_configure_default(); - spi_ll_slave_acquire(ICE40LP->spi_port); - spi_ll_slave_scs_assert(ICE40LP->spi_port); - - gpio_output_set(&ICE40LP->creset, false); // CRESET LOW - -#if !defined(TARGET_QEMU) - // Wait until we succeed in pulling CRESET down against the external pull-up - // and other external circuitry which is fighting against us. - PBL_ASSERT(prv_spin_until_creset_is(false), "CRESET not low during reset"); - - // CRESET needs to be low for 200 ns to actually reset the FPGA. - delay_us(10); -#endif - - gpio_output_set(&ICE40LP->creset, true); // CRESET -> HIGH - -#if !defined(TARGET_QEMU) - PBL_ASSERT(!gpio_input_read(&ICE40LP->cdone), "CDONE not low after reset"); - - // Wait until CRESET goes high again. It's open-drain (and someone with - // tweezers might be grounding it) so it may take some time. - PBL_ASSERT(prv_spin_until_creset_is(true), "CRESET not high after reset"); - - // iCE40 Programming and Configuration manual specifies that the iCE40 needs - // 800 µs for "housekeeping" after reset is released before it is ready to - // receive its configuration. - delay_us(1000); -#endif - - - SLEDecodeContext sle_ctx; - sle_decode_init(&sle_ctx, fpga_bitstream); - uint8_t byte; - while (sle_decode(&sle_ctx, &byte)) { - spi_ll_slave_write(ICE40LP->spi_port, byte); - } - - // Set SCS high so that we don't process any of these clocks as commands. - spi_ll_slave_scs_deassert(ICE40LP->spi_port); - - static const uint8_t spi_zeros[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - - // 49+ SCLK cycles to tell FPGA we're done configuration. - spi_ll_slave_burst_write(ICE40LP->spi_port, spi_zeros, ARRAY_LENGTH(spi_zeros)); - - spi_ll_slave_release(ICE40LP->spi_port); - - // PBL-19516 -#if !defined(TARGET_QEMU) - prv_wait_programmed(); - if (!gpio_input_read(&ICE40LP->cdone)) { - PBL_LOG(LOG_LEVEL_ERROR, "CDONE not high after programming"); - return false; - } -#endif - return true; -} - -void display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size) { - periph_config_acquire_lock(); - - int attempt = 1; - while (1) { - if (prv_try_program(fpga_bitstream, bitstream_size)) { - break; - } - if (attempt++ >= 3) { - PBL_CROAK("Too many failed FPGA programming attempts"); - } - } - - spi_slave_set_frequency(ICE40LP->spi_port, ICE40LP->fast_spi_frequency); - - periph_config_release_lock(); -} - -bool display_switch_to_bootloader_mode(void) { - // Reset the FPGA and wait for it to program itself via NVCM. - // NVCM configuration is initiated by pulling CRESET high while SCS is high. - periph_config_acquire_lock(); - // SCS will already be high here. - - // CRESET needs to be low for at least 200 ns - gpio_output_set(&ICE40LP->creset, false); - delay_us(1000); - gpio_output_set(&ICE40LP->creset, true); - bool success = prv_wait_programmed(); - if (success) { - display_spi_configure_default(); - } - periph_config_release_lock(); - return success; -} - -void display_power_enable(void) { - // The display requires us to wait 1ms between each power rail coming up. The PMIC - // initialization brings up the 3.2V rail (VLCD on the display, LD02 on the PMIC) for us, but - // we still need to wait before turning on the subsequent rails. - psleep(2); - - if (ICE40LP->use_6v6_rail) { - PBL_LOG(LOG_LEVEL_DEBUG, "Enabling 6v6 (Display VDDC)"); - set_6V6_power_state(true); - - psleep(2); - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Enabling 4v5 (Display VDDP)"); - set_4V5_power_state(true); -} - -void display_power_disable(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "Disabling 4v5 (Display VDDP)"); - set_4V5_power_state(false); - - psleep(2); - - if (ICE40LP->use_6v6_rail) { - PBL_LOG(LOG_LEVEL_DEBUG, "Disabling 6v6 (Display VDDC)"); - set_6V6_power_state(false); - - psleep(2); - } -} - -//! -//! Starts a frame. -//! -void display_start_frame(void) { - // The iCE40UL framebuffer FPGA (S4) configuration requires a short delay - // after asserting SCS before it is ready for a command. - delay_us(5); - - spi_ll_slave_write(ICE40LP->spi_port, CMD_FRAME_BEGIN); - - // Make sure command has been transferred. - spi_slave_wait_until_idle_blocking(ICE40LP->spi_port); -} diff --git a/src/fw/drivers/display/ice40lp/ice40lp_internal.h b/src/fw/drivers/display/ice40lp/ice40lp_internal.h deleted file mode 100644 index 82078b7866..0000000000 --- a/src/fw/drivers/display/ice40lp/ice40lp_internal.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/gpio.h" - -#include - -typedef enum { - CMD_FRAME_BEGIN = 0x5, -} DisplayCmd; - - - -void display_spi_begin_transaction(void); -void display_spi_end_transaction(void); -void display_spi_configure_default(void); -bool display_busy(void); -void display_start(void); -void display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size); -void display_send_clocks(uint32_t count); -void display_start_frame(void); -void display_write_byte(uint8_t d); -void display_send_cmd(DisplayCmd cmd); -void display_power_enable(void); -void display_power_disable(void); - -//! Reset the FPGA into bootloader mode. -//! -//! @return true if successful, false if the NVCM is not programmed. -bool display_switch_to_bootloader_mode(void); diff --git a/src/fw/drivers/display/ice40lp/snowy_boot.c b/src/fw/drivers/display/ice40lp/snowy_boot.c deleted file mode 100644 index 3ce024016f..0000000000 --- a/src/fw/drivers/display/ice40lp/snowy_boot.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/display/ice40lp/snowy_boot.h" -#include "drivers/display/ice40lp/ice40lp_definitions.h" -#include "drivers/display/ice40lp/ice40lp_internal.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "drivers/spi.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/delay.h" - -#include -#include - -#define CMD_NULL (0) -#define CMD_SET_PARAMETER (1) -#define CMD_DISPLAY_OFF (2) -#define CMD_DISPLAY_ON (3) -#define CMD_DRAW_SCENE (4) - -#define SCENE_BLACK (0) -#define SCENE_SPLASH (1) -#define SCENE_UPDATE (2) -#define SCENE_ERROR (3) - -#define UPDATE_PROGRESS_MAX (93) - -static void prv_start_command(uint8_t cmd) { - display_spi_begin_transaction(); - spi_ll_slave_write(ICE40LP->spi_port, cmd); -} - -static void prv_send_command_arg(uint8_t arg) { - spi_ll_slave_write(ICE40LP->spi_port, arg); -} - -static void prv_end_command(void) { - display_spi_end_transaction(); -} - -static bool prv_wait_busy(void) { - // The display should come out of busy within 35 milliseconds; - // it is a waste of time to wait more than twice that. - int timeout = 50 * 10; - while (display_busy()) { - if (timeout-- == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Display busy-wait timeout expired!"); - return false; - } - delay_us(100); - } - return true; -} - -static void prv_screen_on(void) { - prv_start_command(CMD_DISPLAY_ON); - prv_end_command(); -} - -static void prv_screen_off(void) { - prv_start_command(CMD_DISPLAY_OFF); - prv_end_command(); -} - -void prv_draw_scene(uint8_t scene) { - prv_start_command(CMD_DRAW_SCENE); - prv_send_command_arg(scene); - prv_end_command(); -} -void prv_set_parameter(uint32_t param) { - prv_start_command(CMD_SET_PARAMETER); - // Send in little-endian byte order - prv_send_command_arg(param & 0xff); - prv_send_command_arg((param >> 8) & 0xff); - prv_send_command_arg((param >> 16) & 0xff); - prv_send_command_arg((param >> 24) & 0xff); - prv_end_command(); -} - -void boot_display_show_boot_splash(void) { - prv_wait_busy(); - prv_draw_scene(SCENE_SPLASH); - // Don't turn the screen on until the boot-splash is fully drawn. - prv_wait_busy(); - prv_screen_on(); -} - -void boot_display_show_firmware_update_progress( - uint32_t numerator, uint32_t denominator) { - static uint8_t last_bar_fill = UINT8_MAX; - // Scale progress to the number of pixels in the progress bar, - // rounding half upwards. - uint8_t bar_fill = - ((numerator * UPDATE_PROGRESS_MAX) + ((denominator+1)/2)) / denominator; - // Don't waste time and power redrawing the same screen repeatedly. - if (bar_fill != last_bar_fill) { - last_bar_fill = bar_fill; - prv_set_parameter(bar_fill); - prv_draw_scene(SCENE_UPDATE); - } -} - -bool boot_display_show_error_code(uint32_t error_code) { - prv_set_parameter(error_code); - prv_draw_scene(SCENE_ERROR); - if (prv_wait_busy()) { - prv_screen_on(); - return true; - } else { - return false; - } -} - -void boot_display_screen_off(void) { - prv_screen_off(); - prv_draw_scene(SCENE_BLACK); - prv_wait_busy(); -} diff --git a/src/fw/drivers/display/ice40lp/snowy_boot.h b/src/fw/drivers/display/ice40lp/snowy_boot.h deleted file mode 100644 index a8f79664cc..0000000000 --- a/src/fw/drivers/display/ice40lp/snowy_boot.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -//! Functions for controlling the display FPGA in bootloader mode, such as -//! early in the boot process before it is reconfigured in framebuffer mode. -//! -//! These functions all assume that all necessary GPIOs and the SPI peripheral -//! are configured correctly, and that the bootloader is already in bootloader -//! mode. - -//! Display the Pebble logo and turn on the screen. -void boot_display_show_boot_splash(void); - -//! Show the Pebble logo with a progress bar. -void boot_display_show_firmware_update_progress( - uint32_t numerator, uint32_t denominator); - -//! Show a sad-watch error. -bool boot_display_show_error_code(uint32_t error_code); - -//! Black out the screen and prepare for power down. -void boot_display_screen_off(void); diff --git a/src/fw/drivers/display/sf32lb/display_jdi.c b/src/fw/drivers/display/sf32lb/display_jdi.c new file mode 100644 index 0000000000..fe173542f8 --- /dev/null +++ b/src/fw/drivers/display/sf32lb/display_jdi.c @@ -0,0 +1,528 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "display_jdi.h" + +#include "board/board.h" +#include "board/display.h" +#include "drivers/display/display.h" +#include "drivers/gpio.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/delay.h" +#include "kernel/util/stop.h" +#include "kernel/coredump_extra_regions.h" +#include "drivers/rtc.h" +#include "mcu/cache.h" +#include "os/mutex.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/logging.h" +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "semphr.h" + +#include "bf0_hal.h" +#include "bf0_hal_lcdc.h" +#include "bf0_hal_lptim.h" +#include "bf0_hal_rtc.h" + +PBL_LOG_MODULE_DEFINE(driver_display_jdi, CONFIG_DRIVER_DISPLAY_LOG_LEVEL); + +#define POWER_SEQ_DELAY_TIME_US 11000 +#define POWER_RESET_CYCLE_DELAY_TIME_US 500000 + +// Timeout for detecting the SiFli HAL silent-loss bug: the LCDC kicks off a +// transfer but never delivers the EOF callback, so the compositor would block +// forever. A normal full-frame transfer takes well under 20ms; 500ms is a +// conservative threshold that won't fire on transient stalls but catches a +// real wedge well before the user perceives the freeze. +#define DISPLAY_SILENT_LOSS_TIMEOUT_MS 500 + +// Bytes of the LCDC peripheral register file snapshotted into BSS before +// crashing. The PebbleOS coredump captures all of HPSYS SRAM but does not +// capture MMIO; copying registers into SRAM gets them into the coredump that +// gets shared with Sifli. 2KB covers IRQ/SETTING/COMMAND/CANVAS/LAYER0/JDI_PAR +// (lcd_if.h struct ends around offset 0x100). +#define DISPLAY_LCDC_REG_DUMP_BYTES 2048 + +// Pointer to the compositor's framebuffer - we convert in-place to save 44KB RAM +static uint8_t *s_framebuffer; +static uint16_t s_update_y0; +static uint16_t s_update_y1; +static bool s_initialized; +static bool s_updating; +static UpdateCompleteCallback s_uccb; +static SemaphoreHandle_t s_sem; +static TimerID s_silent_loss_timer = TIMER_INVALID_ID; +// Set from HAL_LCDC_SendLayerDataCpltCbk (ISR). The silent-loss handler checks +// this to distinguish "EOF truly never fired" (real bug → crash) from "EOF +// fired but terminate hasn't drained off the KernelMain queue yet" (race +// against scheduler latency → don't crash). +static volatile bool s_eof_observed; +// Captured at silent-loss time so the coredump shipped to Sifli carries the +// LCDC register state at the moment of the wedge. uint32_t storage (not +// uint8_t) so the linker word-aligns it — prv_snapshot_lcdc_regs reads MMIO +// as 32-bit volatiles and would fault on a misaligned destination. Marked +// volatile because the only consumer is the postmortem coredump tool, not +// any C code in this image — without it, the compiler eliminates the stores. +static volatile uint32_t s_lcdc_pre_crash_regs[DISPLAY_LCDC_REG_DUMP_BYTES / sizeof(uint32_t)]; + +// Ring buffer of recent LCDC interrupts, recorded by display_jdi_irq_handler +#define DISPLAY_IRQ_LOG_ENTRIES 32 + +typedef struct { + uint32_t timestamp; // rtc_get_ticks() low 32 bits + uint32_t irq_before; // LCD_IF.IRQ as the HAL handler will read it + uint32_t irq_after; // LCD_IF.IRQ after the HAL handler cleared bits + uint32_t jdi_par_ctrl; // LCD_IF.JDI_PAR_CTRL before the handler (INT_LINE_NUM) + uint32_t status; // LCD_IF.STATUS before the handler + uint32_t flags; // bit0: EOF callback fired; bit1: s_updating at entry +} DisplayIrqLogEntry; + +typedef struct { + uint32_t write_count; // total IRQs logged; newest = (write_count-1) % N + DisplayIrqLogEntry entries[DISPLAY_IRQ_LOG_ENTRIES]; +} DisplayIrqLog; + +static volatile DisplayIrqLog s_lcdc_irq_log; + +// Set by HAL_LCDC_SendLayerDataCpltCbk (the EOF callback) so the IRQ logger can +// record, per interrupt, whether the HAL reached the completion path. +static volatile bool s_lcdc_eof_cb_fired; + +// Called from coredump_extra_regions_init() in main.c boot path so the +// snapshot buffer rides in Memfault coredumps. The default Memfault +// reconstruction only forwards thread stacks + log buffers; without this +// the LCDC register dump captured by prv_silent_loss_handler stays in flash +// and never reaches Sifli. +void display_jdi_register_coredump_regions(void) { + coredump_extra_regions_register("lcdc_pre_crash_regs", + (const void *)s_lcdc_pre_crash_regs, + sizeof(s_lcdc_pre_crash_regs)); + coredump_extra_regions_register("lcdc_irq_log", + (const void *)&s_lcdc_irq_log, + sizeof(s_lcdc_irq_log)); +} + +#ifndef CONFIG_RELEASE +// Test hook: arm a one-shot drop of the next LCDC transfer-complete callback, +// simulating the silent-loss failure mode. The silent-loss timer should fire +// ~DISPLAY_SILENT_LOSS_TIMEOUT_MS later and PBL_CROAK. +static volatile bool s_test_drop_next_complete; +#endif + +#if DISPLAY_ORIENTATION_ROTATED_180 +static bool s_rotated_180 = true; +#else +static bool s_rotated_180 = false; +#endif + +static void prv_power_cycle(void){ + OutputConfig cfg = { + .gpio = hwp_gpio1, + .active_high = true, + }; + + // This will disable all JDI pull-ups/downs so that VLCD can fully turn off, + // allowing for a clean power cycle. + + cfg.gpio_pin = DISPLAY->pinmux.b1.pad - PAD_PA00; + gpio_output_init(&cfg, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&cfg, false); + + cfg.gpio_pin = DISPLAY->pinmux.vck.pad - PAD_PA00; + gpio_output_init(&cfg, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&cfg, false); + + cfg.gpio_pin = DISPLAY->pinmux.xrst.pad - PAD_PA00; + gpio_output_init(&cfg, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&cfg, false); + + cfg.gpio_pin = DISPLAY->pinmux.hck.pad - PAD_PA00; + gpio_output_init(&cfg, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&cfg, false); + + cfg.gpio_pin = DISPLAY->pinmux.r2.pad - PAD_PA00; + gpio_output_init(&cfg, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&cfg, false); + + gpio_output_set(&DISPLAY->vddp, false); + gpio_output_set(&DISPLAY->vlcd, false); + + delay_us(POWER_RESET_CYCLE_DELAY_TIME_US); +} + +static void prv_display_on() { + gpio_output_set(&DISPLAY->vlcd, true); + delay_us(POWER_SEQ_DELAY_TIME_US); + gpio_output_set(&DISPLAY->vddp, true); + delay_us(POWER_SEQ_DELAY_TIME_US); + + LPTIM_TypeDef *lptim = DISPLAY->vcom.lptim; + + lptim->CFGR |= LPTIM_INTCLOCKSOURCE_LPCLOCK; + lptim->ARR = RC10K_FREQ / DISPLAY->vcom.freq_hz; + lptim->CMP = lptim->ARR / 2; + lptim->CR |= LPTIM_CR_ENABLE; + lptim->CR |= LPTIM_CR_CNTSTRT; +} + +static void prv_display_off() { + DisplayJDIState *state = DISPLAY->state; + HAL_LCDC_DeInit(&state->hlcdc); + + LPTIM_TypeDef *lptim = DISPLAY->vcom.lptim; + + lptim->CR &= ~LPTIM_CR_ENABLE; + lptim->CR &= ~LPTIM_CR_CNTSTRT; + + delay_us(POWER_SEQ_DELAY_TIME_US); + gpio_output_set(&DISPLAY->vddp, false); + delay_us(POWER_SEQ_DELAY_TIME_US); + gpio_output_set(&DISPLAY->vlcd, false); +} + +static HAL_StatusTypeDef prv_display_update_start(void) { + DisplayJDIState *state = DISPLAY->state; + + // The LCDC reads the framebuffer over DMA, which bypasses the D-cache. + // Flush dirty lines for the rows we're about to send so the LCDC sees + // the 332-converted pixels instead of stale SRAM. + uintptr_t fb_addr = (uintptr_t)&s_framebuffer[s_update_y0 * PBL_DISPLAY_WIDTH]; + size_t fb_size = (size_t)(s_update_y1 - s_update_y0 + 1) * PBL_DISPLAY_WIDTH; + dcache_align(&fb_addr, &fb_size); + dcache_flush((const void *)fb_addr, fb_size); + + // Only send the dirty region that was converted to RGB332 format + HAL_LCDC_SetROIArea(&state->hlcdc, 0, s_update_y0, PBL_DISPLAY_WIDTH - 1, s_update_y1); + HAL_LCDC_LayerSetData(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, s_framebuffer, 0, s_update_y0, + PBL_DISPLAY_WIDTH - 1, s_update_y1); + return HAL_LCDC_SendLayerData_IT(&state->hlcdc); +} + +static void prv_handle_send_failure(const char *ctx, HAL_StatusTypeDef status) { + DisplayJDIState *state = DISPLAY->state; + PBL_LOG_ERR("display: %s SendLayerData_IT=%d State=%d ErrorCode=0x%08lx", + ctx, (int)status, (int)state->hlcdc.State, + (unsigned long)state->hlcdc.ErrorCode); + state->hlcdc.State = HAL_LCDC_STATE_READY; + state->hlcdc.ErrorCode = HAL_LCDC_ERROR_NONE; +} + +static void prv_snapshot_lcdc_regs(const LCDC_HandleTypeDef *hlcdc) { + // Peripheral MMIO must be read as aligned 32-bit volatile loads; plain + // memcpy may emit byte-wise loads on some toolchains and fault. + const volatile uint32_t *src = (const volatile uint32_t *)hlcdc->Instance; + for (size_t i = 0; i < DISPLAY_LCDC_REG_DUMP_BYTES / sizeof(uint32_t); i++) { + s_lcdc_pre_crash_regs[i] = src[i]; + } +} + +// Runs on PebbleTask_NewTimers if no LCDC EOF callback arrives within +// DISPLAY_SILENT_LOSS_TIMEOUT_MS of kickoff. Known trigger: SiFli HAL's +// ICB-overflow path (bf0_hal_lcdc.c HAL_LCDC_IRQHandler) sets +// HAL_LCDC_ERROR_OVERFLOW without invoking XferCpltCallback / XferErrorCallback, +// so the firmware silently loses the completion and the compositor wedges. +// Capture LCDC registers into BSS so they ride in the coredump, then crash — +// the user sees a reboot instead of a frozen screen, and Memfault captures +// the state Sifli needs to diagnose the underlying HAL bug. +static void prv_silent_loss_handler(void *data) { + if (s_eof_observed) { + // EOF fired between us arming the timer and the timeout — terminate is + // queued on KernelMain but hasn't run yet. Don't crash on a scheduler + // latency artifact. + return; + } + DisplayJDIState *state = DISPLAY->state; + prv_snapshot_lcdc_regs(&state->hlcdc); + // HPSYS SRAM is Normal cacheable (since the MPU region shrink in + // hal_sifli/sf32lb52 system_bf0_ap.c), so the snapshot stores above land + // in D-cache. PebbleOS captures coredump RAM after the crash path may + // have already invalidated the cache, so flush the dirty lines back to + // physical SRAM before we trip PBL_CROAK. + uintptr_t snap_addr = (uintptr_t)s_lcdc_pre_crash_regs; + size_t snap_size = sizeof(s_lcdc_pre_crash_regs); + dcache_align(&snap_addr, &snap_size); + dcache_flush((const void *)snap_addr, snap_size); + PBL_CROAK("LCDC silent loss: no EOF in %ums (State=%d Err=0x%lx y=%u..%u)", + (unsigned)DISPLAY_SILENT_LOSS_TIMEOUT_MS, + (int)state->hlcdc.State, + (unsigned long)state->hlcdc.ErrorCode, + (unsigned)s_update_y0, (unsigned)s_update_y1); +} + +static void prv_display_update_terminate(void *data) { + new_timer_stop(s_silent_loss_timer); + + // Convert the updated region back from 332 to 222 format + for (uint16_t y = s_update_y0; y <= s_update_y1; y++) { + uint8_t *row = &s_framebuffer[y * PBL_DISPLAY_WIDTH]; + + if (s_rotated_180) { + // Undo HMirror before converting back + for (uint16_t x = 0; x < PBL_DISPLAY_WIDTH / 2; x++) { + uint8_t tmp = row[x]; + row[x] = row[PBL_DISPLAY_WIDTH - 1 - x]; + row[PBL_DISPLAY_WIDTH - 1 - x] = tmp; + } + } + + // Convert this row in-place from 332 to 222 using word-level bit manipulation + // 332 format: RR 0G GG BB (bits 7-6 R, 4-3 G, 1-0 B) + // 222 format: XX RR GG BB (bits 7-6 unused, 5-4 R, 3-2 G, 1-0 B) + uint32_t *row32 = (uint32_t *)row; + for (uint16_t x = 0; x < PBL_DISPLAY_WIDTH / 4; x++) { + uint32_t p = row32[x]; + row32[x] = ((p >> 2) & 0x30303030) | // R: bits 6-7 → 4-5 + ((p >> 1) & 0x0C0C0C0C) | // G: bits 3-4 → 2-3 + (p & 0x03030303); // B: bits 0-1 stay + } + } + + s_updating = false; + s_uccb(); + stop_mode_enable(InhibitorDisplay); +} + +void display_jdi_irq_handler(DisplayJDIDevice *disp) { + DisplayJDIState *state = DISPLAY->state; + LCD_IF_TypeDef *regs = state->hlcdc.Instance; + + // Snapshot LCDC state before/after the HAL handler into the ring buffer so a + // coredump shows the interrupt interleaving behind the lost-EOF wedge. + volatile DisplayIrqLogEntry *entry = + &s_lcdc_irq_log.entries[s_lcdc_irq_log.write_count % DISPLAY_IRQ_LOG_ENTRIES]; + entry->timestamp = (uint32_t)rtc_get_ticks(); + entry->irq_before = regs->IRQ; + entry->jdi_par_ctrl = regs->JDI_PAR_CTRL; + entry->status = regs->STATUS; + entry->flags = s_updating ? 0x2u : 0x0u; + + s_lcdc_eof_cb_fired = false; + HAL_LCDC_IRQHandler(&state->hlcdc); + + entry->irq_after = regs->IRQ; + if (s_lcdc_eof_cb_fired) { + entry->flags |= 0x1u; + } + s_lcdc_irq_log.write_count++; +} + +void HAL_LCDC_SendLayerDataCpltCbk(LCDC_HandleTypeDef *lcdc) { + portBASE_TYPE woken = pdFALSE; + + // Tell the IRQ logger the HAL reached the completion path for this interrupt. + s_lcdc_eof_cb_fired = true; + +#ifndef CONFIG_RELEASE + if (s_test_drop_next_complete && s_updating) { + s_test_drop_next_complete = false; + // Simulate the lost-completion failure mode: leave s_eof_observed false + // and don't post the terminate event. The silent-loss timer should fire + // ~DISPLAY_SILENT_LOSS_TIMEOUT_MS later and PBL_CROAK. + portEND_SWITCHING_ISR(woken); + return; + } +#endif + + // Mark EOF before doing anything else so the silent-loss handler's race + // check sees it even if the terminate event sits on the KernelMain queue + // longer than the timeout. + s_eof_observed = true; + + if (s_updating) { + PebbleEvent e = { + .type = PEBBLE_CALLBACK_EVENT, + .callback = + { + .callback = prv_display_update_terminate, + }, + }; + + woken = event_put_isr(&e) ? pdTRUE : pdFALSE; + } else { + xSemaphoreGiveFromISR(s_sem, &woken); + } + + portEND_SWITCHING_ISR(woken); +} + +void display_init(void) { + if (s_initialized) { + return; + } + + DisplayJDIState *state = DISPLAY->state; + + gpio_output_init(&DISPLAY->vddp, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_init(&DISPLAY->vlcd, GPIO_OType_PP, GPIO_Speed_2MHz); + + prv_power_cycle(); + + HAL_PIN_Set(DISPLAY->pinmux.xrst.pad, DISPLAY->pinmux.xrst.func, DISPLAY->pinmux.xrst.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.vst.pad, DISPLAY->pinmux.vst.func, DISPLAY->pinmux.vst.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.vck.pad, DISPLAY->pinmux.vck.func, DISPLAY->pinmux.vck.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.enb.pad, DISPLAY->pinmux.enb.func, DISPLAY->pinmux.enb.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.hst.pad, DISPLAY->pinmux.hst.func, DISPLAY->pinmux.hst.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.hck.pad, DISPLAY->pinmux.hck.func, DISPLAY->pinmux.hck.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.r1.pad, DISPLAY->pinmux.r1.func, DISPLAY->pinmux.r1.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.r2.pad, DISPLAY->pinmux.r2.func, DISPLAY->pinmux.r2.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.g1.pad, DISPLAY->pinmux.g1.func, DISPLAY->pinmux.g1.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.g2.pad, DISPLAY->pinmux.g2.func, DISPLAY->pinmux.g2.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.b1.pad, DISPLAY->pinmux.b1.func, DISPLAY->pinmux.b1.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.b2.pad, DISPLAY->pinmux.b2.func, DISPLAY->pinmux.b2.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.vcom_frp.pad, DISPLAY->pinmux.vcom_frp.func, DISPLAY->pinmux.vcom_frp.flags, 1); + HAL_PIN_Set(DISPLAY->pinmux.xfrp.pad, DISPLAY->pinmux.xfrp.func, DISPLAY->pinmux.xfrp.flags, 1); + + HAL_LCDC_Init(&state->hlcdc); + HAL_LCDC_LayerReset(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT); + HAL_LCDC_LayerSetCmpr(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, 0); + HAL_LCDC_LayerSetFormat(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, LCDC_PIXEL_FORMAT_RGB332); + HAL_LCDC_LayerVMirror(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, s_rotated_180); + + HAL_NVIC_SetPriority(DISPLAY->irqn, DISPLAY->irq_priority, 0); + HAL_NVIC_EnableIRQ(DISPLAY->irqn); + + s_sem = xSemaphoreCreateBinary(); + + prv_display_on(); + + s_initialized = true; +} + +void display_set_enabled(bool enabled) { + if (enabled) { + prv_display_on(); + } else { + prv_display_off(); + } +} + +bool display_update_in_progress(void) { + return s_updating; +} + +void display_set_rotated(bool rotated) { + DisplayJDIState *state = DISPLAY->state; + +#if DISPLAY_ORIENTATION_ROTATED_180 + s_rotated_180 = !rotated; +#else + s_rotated_180 = rotated; +#endif + HAL_LCDC_LayerVMirror(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, s_rotated_180); + +} + +void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { + DisplayRow row; + bool first_row = true; + + PBL_ASSERTN(!s_updating); + + // Convert rows in-place from 222 to 332 format + // We use the compositor's framebuffer directly to save RAM + while (nrcb(&row)) { + if (first_row) { + // Capture pointer to compositor's framebuffer from first row + s_framebuffer = row.data; + s_update_y0 = row.address; + first_row = false; + } + s_update_y1 = row.address; + + // Convert this row in-place from 222 to 332 using word-level bit manipulation + // 222 format: XX RR GG BB (bits 7-6 unused, 5-4 R, 3-2 G, 1-0 B) + // 332 format: RR 0G GG BB (bits 7-6 R, 4-3 G, 1-0 B) + uint32_t *row32 = (uint32_t *)row.data; + for (uint16_t x = 0; x < PBL_DISPLAY_WIDTH / 4; x++) { + uint32_t p = row32[x]; + row32[x] = ((p & 0x30303030) << 2) | // R: bits 4-5 → 6-7 + ((p & 0x0C0C0C0C) << 1) | // G: bits 2-3 → 3-4 + (p & 0x03030303); // B: bits 0-1 stay + } + + if (s_rotated_180) { + // HMirror in software (VMirror is done by hardware) + uint8_t *row_data = row.data; + for (uint16_t x = 0; x < PBL_DISPLAY_WIDTH / 2; x++) { + uint8_t tmp = row_data[x]; + row_data[x] = row_data[PBL_DISPLAY_WIDTH - 1 - x]; + row_data[PBL_DISPLAY_WIDTH - 1 - x] = tmp; + } + } + } + + if (first_row) { + // No rows to update + uccb(); + return; + } + + // Adjust framebuffer pointer to start of buffer (row 0) + s_framebuffer = s_framebuffer - (s_update_y0 * PBL_DISPLAY_WIDTH); + + // Lazy-create the silent-loss timer. display_init runs via boot_splash_start + // before new_timer_service_init, so creating it in display_init would + // silently fail. The compositor doesn't reach this path until well after + // the timer service is up. + if (s_silent_loss_timer == TIMER_INVALID_ID) { + s_silent_loss_timer = new_timer_create(); + } + + s_uccb = uccb; + s_updating = true; + s_eof_observed = false; + + stop_mode_disable(InhibitorDisplay); + // Arm the timer before kickoff so the EOF IRQ, which can fire as soon as + // SendLayerData_IT returns, never races us into a state where the timer + // isn't yet armed. prv_display_update_terminate stops it on the normal + // completion path; the kickoff-failure path below stops it via the same + // terminate call. + new_timer_start(s_silent_loss_timer, DISPLAY_SILENT_LOSS_TIMEOUT_MS, + prv_silent_loss_handler, NULL, 0); + HAL_StatusTypeDef status = prv_display_update_start(); + if (status != HAL_OK) { + prv_handle_send_failure("update", status); + prv_display_update_terminate(NULL); + } +} + +void display_update_boot_frame(uint8_t *framebuffer) { + if (s_rotated_180) { + // HMirror in software (VMirror is done by hardware) + for (uint16_t y = 0; y < PBL_DISPLAY_HEIGHT; y++) { + uint8_t *row = &framebuffer[y * PBL_DISPLAY_WIDTH]; + for (uint16_t x = 0; x < PBL_DISPLAY_WIDTH / 2; x++) { + uint8_t tmp = row[x]; + row[x] = row[PBL_DISPLAY_WIDTH - 1 - x]; + row[PBL_DISPLAY_WIDTH - 1 - x] = tmp; + } + } + } + + s_framebuffer = framebuffer; + s_update_y0 = 0; + s_update_y1 = PBL_DISPLAY_HEIGHT - 1; + + stop_mode_disable(InhibitorDisplay); + HAL_StatusTypeDef status = prv_display_update_start(); + if (status == HAL_OK) { + xSemaphoreTake(s_sem, portMAX_DELAY); + } else { + // Without this guard a failed kickoff would block boot forever on s_sem, + // since the EOF IRQ that gives the semaphore never fires. + prv_handle_send_failure("boot", status); + } + stop_mode_enable(InhibitorDisplay); +} + +void display_clear(void) {} + +#ifndef CONFIG_RELEASE +void display_jdi_test_drop_next_complete(void) { + s_test_drop_next_complete = true; +} +#endif \ No newline at end of file diff --git a/src/fw/drivers/display/sf32lb/display_jdi.h b/src/fw/drivers/display/sf32lb/display_jdi.h new file mode 100755 index 0000000000..24e328ca9b --- /dev/null +++ b/src/fw/drivers/display/sf32lb/display_jdi.h @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "board/board.h" + +#include "bf0_hal.h" +#include "bf0_hal_lcdc.h" + +typedef struct DisplayJDIState { + LCDC_HandleTypeDef hlcdc; +} DisplayJDIState; + +typedef struct DisplayJDISplash { + const uint8_t *data; + uint16_t width; + uint16_t height; +} DisplayJDISplash; + +typedef const struct DisplayJDIDevice { + DisplayJDIState *state; + IRQn_Type irqn; + uint8_t irq_priority; + struct { + LPTIM_TypeDef *lptim; + uint8_t freq_hz; + } vcom; + struct { + Pinmux xrst; + Pinmux vst; + Pinmux vck; + Pinmux enb; + Pinmux hst; + Pinmux hck; + Pinmux r1; + Pinmux r2; + Pinmux g1; + Pinmux g2; + Pinmux b1; + Pinmux b2; + Pinmux vcom_frp; + Pinmux xfrp; + } pinmux; + OutputConfig vddp; + OutputConfig vlcd; + DisplayJDISplash splash; +} DisplayJDIDevice; + +void display_jdi_irq_handler(DisplayJDIDevice *disp); + +#ifndef CONFIG_RELEASE +//! Test hook: arm a one-shot drop of the next LCDC transfer-complete callback, +//! simulating the silent-loss failure mode (e.g. SiFli HAL ICB overflow). The +//! silent-loss timer should fire ~500ms later and PBL_CROAK, producing a +//! Memfault coredump and a watch reboot. Intended for verifying the +//! detect-and-crash path on hardware. +void display_jdi_test_drop_next_complete(void); +#endif diff --git a/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.c b/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.c deleted file mode 100644 index 302700b681..0000000000 --- a/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sharp_ls013b7dh01.h" - -#include "applib/graphics/gtypes.h" -#include "board/board.h" -#include "debug/power_tracking.h" -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/spi.h" -#include "kernel/util/sleep.h" -#include "kernel/util/stop.h" -#include "os/tick.h" -#include "services/common/analytics/analytics.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/bitset.h" -#include "util/net.h" -#include "util/reverse.h" -#include "util/units.h" - - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -#include "misc.h" - -#include "FreeRTOS.h" -#include "task.h" -#include "semphr.h" - -#include -#include -#include -#include - -// GPIO constants -static const unsigned int DISP_MODE_STATIC = 0x00; -static const unsigned int DISP_MODE_WRITE = 0x80; -static const unsigned int DISP_MODE_CLEAR = 0x20; - -// We want the SPI clock to run at 2MHz by default -static uint32_t s_spi_clock_hz; - -// DMA constants -static DMA_Stream_TypeDef* DISPLAY_DMA_STREAM = DMA1_Stream4; -static const uint32_t DISPLAY_DMA_CLOCK = RCC_AHB1Periph_DMA1; - -static bool s_initialized = false; - -// DMA state -static DisplayContext s_display_context; -static uint32_t s_dma_line_buffer[DISP_DMA_BUFFER_SIZE_WORDS]; - -static SemaphoreHandle_t s_dma_update_in_progress_semaphore; - -static void prv_display_write_byte(uint8_t d); -static void prv_display_context_init(DisplayContext* context); -static void prv_setup_dma_transfer(uint8_t* framebuffer_addr, int framebuffer_size); -static bool prv_do_dma_update(void); - - -static void prv_enable_display_spi_clock() { - periph_config_enable(BOARD_CONFIG_DISPLAY.spi, BOARD_CONFIG_DISPLAY.spi_clk); - power_tracking_start(PowerSystemMcuSpi2); -} - -static void prv_disable_display_spi_clock() { - periph_config_disable(BOARD_CONFIG_DISPLAY.spi, BOARD_CONFIG_DISPLAY.spi_clk); - power_tracking_stop(PowerSystemMcuSpi2); -} - -static void prv_enable_chip_select(void) { - gpio_output_set(&BOARD_CONFIG_DISPLAY.cs, true); - // setup time > 3us - // this produces a setup time of ~7us - for (volatile int i = 0; i < 32; i++); -} - -static void prv_disable_chip_select(void) { - // delay while last byte is emitted by the SPI peripheral (~7us) - for (volatile int i = 0; i < 48; i++); - gpio_output_set(&BOARD_CONFIG_DISPLAY.cs, false); - // hold time > 1us - // this produces a delay of ~3.5us - for (volatile int i = 0; i < 16; i++); -} - - -static void prv_display_start(void) { - periph_config_acquire_lock(); - - gpio_af_init(&BOARD_CONFIG_DISPLAY.clk, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&BOARD_CONFIG_DISPLAY.mosi, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_output_init(&BOARD_CONFIG_DISPLAY.cs, GPIO_OType_PP, GPIO_Speed_50MHz); - gpio_output_init(&BOARD_CONFIG_DISPLAY.on_ctrl, - BOARD_CONFIG_DISPLAY.on_ctrl_otype, - GPIO_Speed_50MHz); - - if (BOARD_CONFIG.power_5v0_options != OptionNotPresent) { - GPIOOType_TypeDef otype = (BOARD_CONFIG.power_5v0_options == OptionActiveLowOpenDrain) - ? GPIO_OType_OD : GPIO_OType_PP; - gpio_output_init(&BOARD_CONFIG.power_ctl_5v0, otype, GPIO_Speed_50MHz); - } - - if (BOARD_CONFIG.lcd_com.gpio) { - gpio_output_init(&BOARD_CONFIG.lcd_com, GPIO_OType_PP, GPIO_Speed_50MHz); - } - - // Set up a SPI bus on SPI2 - SPI_InitTypeDef spi_cfg; - SPI_I2S_DeInit(BOARD_CONFIG_DISPLAY.spi); - SPI_StructInit(&spi_cfg); - spi_cfg.SPI_Direction = SPI_Direction_1Line_Tx; // Write-only SPI - spi_cfg.SPI_Mode = SPI_Mode_Master; - spi_cfg.SPI_DataSize = SPI_DataSize_8b; - spi_cfg.SPI_CPOL = SPI_CPOL_Low; - spi_cfg.SPI_CPHA = SPI_CPHA_1Edge; - spi_cfg.SPI_NSS = SPI_NSS_Soft; - spi_cfg.SPI_BaudRatePrescaler = - spi_find_prescaler(s_spi_clock_hz, BOARD_CONFIG_DISPLAY.spi_clk_periph); - spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB; - SPI_Init(BOARD_CONFIG_DISPLAY.spi, &spi_cfg); - - gpio_use(BOARD_CONFIG_DISPLAY.spi_gpio); - SPI_Cmd(BOARD_CONFIG_DISPLAY.spi, ENABLE); - gpio_release(BOARD_CONFIG_DISPLAY.spi_gpio); - - if (BOARD_CONFIG.power_5v0_options != OptionNotPresent) { - // +5V to 5V_EN pin - gpio_output_set(&BOARD_CONFIG.power_ctl_5v0, true); - } - - // +5V to LCD_DISP pin (Set this pin low to turn off the display) - gpio_output_set(&BOARD_CONFIG_DISPLAY.on_ctrl, true); - - periph_config_release_lock(); -} - -uint32_t display_baud_rate_change(uint32_t new_frequency_hz) { - // Take the semaphore so that we can be sure that we are not interrupting a transfer - xSemaphoreTake(s_dma_update_in_progress_semaphore, portMAX_DELAY); - - uint32_t old_spi_clock_hz = s_spi_clock_hz; - s_spi_clock_hz = new_frequency_hz; - prv_enable_display_spi_clock(); - prv_display_start(); - prv_disable_display_spi_clock(); - - xSemaphoreGive(s_dma_update_in_progress_semaphore); - return old_spi_clock_hz; -} - -void display_init(void) { - if (s_initialized) { - return; - } - - s_spi_clock_hz = MHZ_TO_HZ(2); - - prv_display_context_init(&s_display_context); - - vSemaphoreCreateBinary(s_dma_update_in_progress_semaphore); - - dma_request_init(SHARP_SPI_TX_DMA); - - prv_enable_display_spi_clock(); - - prv_display_start(); - - prv_disable_display_spi_clock(); - s_initialized = true; -} - -static void prv_display_context_init(DisplayContext* context) { - context->state = DISPLAY_STATE_IDLE; - context->get_next_row = NULL; - context->complete = NULL; -} - -// Clear-all mode is entered by sending 0x04 to the panel -void display_clear(void) { - prv_enable_display_spi_clock(); - prv_enable_chip_select(); - - prv_display_write_byte(DISP_MODE_CLEAR); - prv_display_write_byte(0x00); - - prv_disable_chip_select(); - prv_disable_display_spi_clock(); -} - -void display_set_enabled(bool enabled) { - gpio_output_set(&BOARD_CONFIG_DISPLAY.on_ctrl, enabled); -} - -bool display_update_in_progress(void) { - if (xSemaphoreTake(s_dma_update_in_progress_semaphore, 0) == pdPASS) { - xSemaphoreGive(s_dma_update_in_progress_semaphore); - return false; - } - return true; -} - -void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { - PBL_ASSERTN(nrcb != NULL); - PBL_ASSERTN(uccb != NULL); - stop_mode_disable(InhibitorDisplay); - xSemaphoreTake(s_dma_update_in_progress_semaphore, portMAX_DELAY); - analytics_stopwatch_start(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME, AnalyticsClient_App); - analytics_inc(ANALYTICS_DEVICE_METRIC_DISPLAY_UPDATES_PER_HOUR, AnalyticsClient_System); - - prv_enable_display_spi_clock(); - power_tracking_start(PowerSystemMcuDma1); - SPI_I2S_DMACmd(BOARD_CONFIG_DISPLAY.spi, SPI_I2S_DMAReq_Tx, ENABLE); - - prv_display_context_init(&s_display_context); - s_display_context.get_next_row = nrcb; - s_display_context.complete = uccb; - - prv_do_dma_update(); - - // Block while we wait for the update to finish. - TickType_t ticks = milliseconds_to_ticks(4000); // DMA should be fast - if (xSemaphoreTake(s_dma_update_in_progress_semaphore, ticks) != pdTRUE) { - // something went wrong, gather some debug info & reset - int dma_status = DMA_GetITStatus(DISPLAY_DMA_STREAM, DMA_IT_TCIF4); - uint32_t spi_clock_status = (RCC->APB1ENR & BOARD_CONFIG_DISPLAY.spi_clk); - uint32_t dma_clock_status = (RCC->AHB1ENR & DISPLAY_DMA_CLOCK); - uint32_t pri_mask = __get_PRIMASK(); - PBL_CROAK("display DMA failed: 0x%" PRIx32 " %d 0x%lx 0x%lx", pri_mask, - dma_status, spi_clock_status, dma_clock_status); - } - - power_tracking_stop(PowerSystemMcuDma1); - prv_disable_display_spi_clock(); - - xSemaphoreGive(s_dma_update_in_progress_semaphore); - stop_mode_enable(InhibitorDisplay); - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME); -} - -// Static mode is entered by sending 0x00 to the panel -static void prv_display_enter_static(void) { - prv_enable_chip_select(); - - prv_display_write_byte(DISP_MODE_STATIC); - prv_display_write_byte(0x00); - prv_display_write_byte(0x00); - - prv_disable_chip_select(); -} - -void display_pulse_vcom(void) { - PBL_ASSERTN(BOARD_CONFIG.lcd_com.gpio != 0); - gpio_output_set(&BOARD_CONFIG.lcd_com, true); - // the spec requires at least 1us; this provides ~2 so should be safe - for (volatile int i = 0; i < 8; i++); - gpio_output_set(&BOARD_CONFIG.lcd_com, false); -} - - -static bool prv_dma_handler(DMARequest *request, void *context) { - return prv_do_dma_update(); -} - -#if DISPLAY_ORIENTATION_ROTATED_180 -//! -//! memcpy the src buffer to dst and reverse the bits -//! to match the display order -//! -static void prv_memcpy_reverse_bytes(uint8_t* dst, uint8_t* src, int bytes) { - // Skip the mode selection and column address bytes - dst+=2; - while (bytes--) { - *dst++ = reverse_byte(*src++); - } -} -#else -//! -//! memcpy the src buffer to dst backwards (i.e. the highest src byte -//! is the lowest byte in dst. -//! -static void prv_memcpy_backwards(uint32_t* dst, uint32_t* src, int length) { - dst += length - 1; - while (length--) { - *dst-- = ntohl(*src++); - } -} -#endif - - -//! -//! Write a single byte synchronously to the display. Use this -//! sparingly, as it will tie up the micro duing the write. -//! -static void prv_display_write_byte(uint8_t d) { - // Block until the tx buffer is empty - SPI_I2S_SendData(BOARD_CONFIG_DISPLAY.spi, d); - while (!SPI_I2S_GetFlagStatus(BOARD_CONFIG_DISPLAY.spi, SPI_I2S_FLAG_TXE)) {} -} - -static bool prv_do_dma_update(void) { - DisplayRow r; - - PBL_ASSERTN(s_display_context.get_next_row != NULL); - bool is_end_of_buffer = !s_display_context.get_next_row(&r); - - switch (s_display_context.state) { - case DISPLAY_STATE_IDLE: - { - if (is_end_of_buffer) { - // If nothing has been modified, bail out early - return false; - } - - // Enable display slave select - prv_enable_chip_select(); - - s_display_context.state = DISPLAY_STATE_WRITING; - -#if DISPLAY_ORIENTATION_ROTATED_180 - prv_memcpy_reverse_bytes((uint8_t*)s_dma_line_buffer, r.data, DISP_LINE_BYTES); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= (DISP_MODE_WRITE | reverse_byte(r.address + 1) << 8); -#else - prv_memcpy_backwards(s_dma_line_buffer, (uint32_t*)r.data, DISP_LINE_WORDS); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= (DISP_MODE_WRITE | reverse_byte(167 - r.address + 1) << 8); -#endif - prv_setup_dma_transfer(((uint8_t*) s_dma_line_buffer), DISP_DMA_BUFFER_SIZE_BYTES); - - break; - } - case DISPLAY_STATE_WRITING: - { - if (is_end_of_buffer) { - prv_display_write_byte(0x00); - - // Disable display slave select - prv_disable_chip_select(); - - prv_display_enter_static(); - - s_display_context.complete(); - - signed portBASE_TYPE was_higher_priority_task_woken = pdFALSE; - xSemaphoreGiveFromISR(s_dma_update_in_progress_semaphore, &was_higher_priority_task_woken); - - return was_higher_priority_task_woken != pdFALSE; - } - -#if DISPLAY_ORIENTATION_ROTATED_180 - prv_memcpy_reverse_bytes((uint8_t*)s_dma_line_buffer, r.data, DISP_LINE_BYTES); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= (DISP_MODE_WRITE | reverse_byte(r.address + 1) << 8); -#else - prv_memcpy_backwards(s_dma_line_buffer, (uint32_t*)r.data, DISP_LINE_WORDS); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= reverse_byte(167 - r.address + 1) << 8; -#endif - prv_setup_dma_transfer(((uint8_t*) s_dma_line_buffer) + 1, DISP_DMA_BUFFER_SIZE_BYTES - 1); - break; - } - default: - WTF; - } - return false; -} - -static void prv_setup_dma_transfer(uint8_t *framebuffer_addr, int framebuffer_size) { - void *dst = (void *)&(BOARD_CONFIG_DISPLAY.spi->DR); - dma_request_start_direct(SHARP_SPI_TX_DMA, dst, framebuffer_addr, framebuffer_size, - prv_dma_handler, NULL); -} - -void display_show_splash_screen(void) { - // The bootloader has already drawn the splash screen for us; nothing to do! -} - -// Stubs for display offset -void display_set_offset(GPoint offset) {} - -GPoint display_get_offset(void) { return GPointZero; } diff --git a/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.h b/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.h index 0cbb7c23a3..39d2537da0 100644 --- a/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.h +++ b/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c b/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c index 7b4cc37ddf..3cda8f38e7 100644 --- a/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c +++ b/src/fw/drivers/display/sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c @@ -1,148 +1,41 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "sharp_ls013b7dh01.h" +#include +#include +#include + #include "applib/graphics/gtypes.h" #include "board/board.h" -#include "debug/power_tracking.h" -#include "drivers/dma.h" #include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pwm.h" -#include "drivers/spi.h" -#include "kernel/util/sleep.h" +#include "kernel/events.h" #include "kernel/util/stop.h" -#include "os/tick.h" -#include "services/common/analytics/analytics.h" -#include "system/logging.h" +#include "os/mutex.h" #include "system/passert.h" -#include "util/bitset.h" -#include "util/net.h" #include "util/reverse.h" -#include "util/units.h" - -#define NRF5_COMPATIBLE -#include #include #include #include #include +#include #include "FreeRTOS.h" #include "task.h" #include "semphr.h" -#include -#include -#include -#include - -// GPIO constants -static const unsigned int DISP_MODE_STATIC = 0x00; -static const unsigned int DISP_MODE_WRITE = 0x80; -static const unsigned int DISP_MODE_CLEAR = 0x20; - -// We want the SPI clock to run at 1MHz by default -static uint32_t s_spi_clock_hz; - -static bool s_initialized = false; - -static volatile int s_spidma_waiting = 0; -static volatile int s_spidma_immediate = 0; - -// DMA state -static DisplayContext s_display_context; -static uint32_t s_dma_line_buffer[DISP_DMA_BUFFER_SIZE_WORDS]; - -static SemaphoreHandle_t s_dma_update_in_progress_semaphore; - -static void prv_spim_evt_handler(nrfx_spim_evt_t const *evt, void *ctx); -static void prv_display_context_init(DisplayContext* context); -static bool prv_do_dma_update(void); - -static bool s_spim_initialized = false; +#define DISP_MODE_WRITE 0x01U +#define DISP_MODE_CLEAR 0x04U -// Broadly, these two routines bracket enable_ and disable_chip_select, but -// in the full update use case, we only set up and tear down once to save -// some cycles (and avoid having to set up and tear down once per scanline). -static void prv_enable_spim(void) { - PBL_ASSERTN(!s_spim_initialized); - - // Due to nRF52840 erratum 195, SPIM3 needs to be fully disabled - // (uninit'ed) to stop drawing current, so rather than just configuring - // the SPI peripheral once on boot, we configure it before every - // transaction, then shut it down again. - - nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG( - BOARD_CONFIG_DISPLAY.clk.gpio_pin, - BOARD_CONFIG_DISPLAY.mosi.gpio_pin, - NRF_SPIM_PIN_NOT_CONNECTED, - NRF_SPIM_PIN_NOT_CONNECTED); - config.frequency = NRFX_MHZ_TO_HZ(2); - - /* spim4 has hardware SS but it is tricky to convince NRFX to expose it to - * us; for now, we use the classic enable chip select mechanism */ -#if 0 - config.use_hw_ss = 1; - config.ss_duration = 256; /* 4 us * 64MHz */ -#endif - - nrfx_err_t err = nrfx_spim_init(&BOARD_CONFIG_DISPLAY.spi, &config, prv_spim_evt_handler, NULL); - PBL_ASSERTN(err == NRFX_SUCCESS); - - s_spim_initialized = true; -} - -static void prv_disable_spim(void) { - PBL_ASSERTN(s_spim_initialized); - - // includes WAR for erratum 195, in nrfx driver - nrfx_spim_uninit(&BOARD_CONFIG_DISPLAY.spi); - - s_spim_initialized = false; -} - -static void prv_enable_chip_select(void) { - gpio_output_set(&BOARD_CONFIG_DISPLAY.cs, true); - // setup time > 3us - // this produces a setup time of ~7us - for (volatile int i = 0; i < 32; i++); -} - -static void prv_disable_chip_select(void) { - // delay while last byte is emitted by the SPI peripheral (~7us) - for (volatile int i = 0; i < 48; i++); - gpio_output_set(&BOARD_CONFIG_DISPLAY.cs, false); - // hold time > 1us - // this produces a delay of ~3.5us - for (volatile int i = 0; i < 16; i++); -} +static uint8_t s_buf[2 + ((DISP_LINE_BYTES + 2) * PBL_DISPLAY_HEIGHT)]; +static bool s_updating; +static UpdateCompleteCallback s_uccb; +static SemaphoreHandle_t s_sem; - -static void prv_display_start(void) { - periph_config_acquire_lock(); - - gpio_output_init(&BOARD_CONFIG_DISPLAY.cs, GPIO_OType_PP, GPIO_Speed_50MHz); - - gpio_output_init(&BOARD_CONFIG_DISPLAY.on_ctrl, - BOARD_CONFIG_DISPLAY.on_ctrl_otype, - GPIO_Speed_50MHz); - - // +5V to LCD_DISP pin (Set this pin low to turn off the display) - gpio_output_set(&BOARD_CONFIG_DISPLAY.on_ctrl, true); - - periph_config_release_lock(); -} - -uint32_t display_baud_rate_change(uint32_t new_frequency_hz) { - // Take the semaphore so that we can be sure that we are not interrupting a transfer - xSemaphoreTake(s_dma_update_in_progress_semaphore, portMAX_DELAY); - - uint32_t old_spi_clock_hz = s_spi_clock_hz; - s_spi_clock_hz = new_frequency_hz; - - xSemaphoreGive(s_dma_update_in_progress_semaphore); - return old_spi_clock_hz; -} +// watch rotation +static bool s_rotated_180 = false; static void prv_extcomin_init(void) { nrfx_err_t err; @@ -177,15 +70,17 @@ static void prv_extcomin_init(void) { // Period end (CC0) sets GPIO, clears RTC evt_addr = nrf_rtc_event_address_get(extcomin->rtc, nrf_rtc_compare_event_get(0)); - task_addr = nrf_gpiote_task_address_get(extcomin->gpiote, nrf_gpiote_set_task_get(extcomin->gpiote_ch)); + task_addr = + nrf_gpiote_task_address_get(extcomin->gpiote, nrf_gpiote_set_task_get(extcomin->gpiote_ch)); nrfx_gppi_channel_endpoints_setup(ppi_ch[0], evt_addr, task_addr); - task_addr = nrf_rtc_event_address_get(extcomin->rtc, NRF_RTC_TASK_CLEAR); + task_addr = nrf_rtc_task_address_get(extcomin->rtc, NRF_RTC_TASK_CLEAR); nrfx_gppi_fork_endpoint_setup(ppi_ch[0], task_addr); // Pulse end (CC1) clears GPIO evt_addr = nrf_rtc_event_address_get(extcomin->rtc, nrf_rtc_compare_event_get(1)); - task_addr = nrf_gpiote_task_address_get(extcomin->gpiote, nrf_gpiote_clr_task_get(extcomin->gpiote_ch)); + task_addr = + nrf_gpiote_task_address_get(extcomin->gpiote, nrf_gpiote_clr_task_get(extcomin->gpiote_ch)); nrfx_gppi_channel_endpoints_setup(ppi_ch[1], evt_addr, task_addr); nrfx_gppi_channels_enable((1UL << ppi_ch[0]) | (1UL << ppi_ch[1])); @@ -193,239 +88,146 @@ static void prv_extcomin_init(void) { nrf_rtc_task_trigger(extcomin->rtc, NRF_RTC_TASK_START); } -void display_init(void) { - if (s_initialized) { - return; - } - - s_spi_clock_hz = MHZ_TO_HZ(1); - - prv_display_context_init(&s_display_context); - - vSemaphoreCreateBinary(s_dma_update_in_progress_semaphore); - - prv_display_start(); - - prv_extcomin_init(); - - s_initialized = true; +static inline void prv_enable_spim(void) { + nrf_spim_enable(BOARD_CONFIG_DISPLAY.spi.p_reg); } -static void prv_display_context_init(DisplayContext* context) { - context->state = DISPLAY_STATE_IDLE; - context->get_next_row = NULL; - context->complete = NULL; -} +static inline void prv_disable_spim(void) { + nrf_spim_disable(BOARD_CONFIG_DISPLAY.spi.p_reg); -static void prv_spim_evt_handler(nrfx_spim_evt_t const *evt, void *ctx) { - s_spidma_waiting = 0; - if (!s_spidma_immediate) { - bool needs_switch = prv_do_dma_update(); - portEND_SWITCHING_ISR(needs_switch); + // Workaround for nRF52840 anomaly 195 + if (BOARD_CONFIG_DISPLAY.spi.p_reg == NRF_SPIM3) { + *(volatile uint32_t *)0x4002F004 = 1; } } -static void prv_display_write_async(const uint8_t *buf, size_t len) { - nrfx_spim_xfer_desc_t desc = { - .p_tx_buffer = buf, - .tx_length = len - }; - - PBL_ASSERTN(!s_spidma_waiting); - - s_spidma_waiting = 1; - s_spidma_immediate = 0; - - nrfx_err_t err = nrfx_spim_xfer(&BOARD_CONFIG_DISPLAY.spi, &desc, 0); - PBL_ASSERTN(err == NRFX_SUCCESS); +static inline void prv_enable_chip_select(void) { + gpio_output_set(&BOARD_CONFIG_DISPLAY.cs, true); } -static void prv_display_write_sync(const uint8_t *buf, size_t len) { - nrfx_spim_xfer_desc_t desc = { - .p_tx_buffer = buf, - .tx_length = len - }; - - PBL_ASSERTN(!s_spidma_waiting); - - s_spidma_waiting = 1; - s_spidma_immediate = 1; - - nrfx_err_t err = nrfx_spim_xfer(&BOARD_CONFIG_DISPLAY.spi, &desc, 0); - PBL_ASSERTN(err == NRFX_SUCCESS); - - while (s_spidma_waiting) - /* XXX: ... yield, or something. maybe a semaphore would be nicer here. it should be fast, though */; - s_spidma_immediate = 0; +static inline void prv_disable_chip_select(void) { + gpio_output_set(&BOARD_CONFIG_DISPLAY.cs, false); } -// Clear-all mode is entered by sending 0x04 to the panel -void display_clear(void) { - uint8_t buf[] = { DISP_MODE_CLEAR, 0x00 }; - prv_enable_spim(); - prv_enable_chip_select(); - prv_display_write_sync(buf, sizeof(buf)); +static void prv_terminate_transfer(void *data) { + s_updating = false; + prv_disable_chip_select(); prv_disable_spim(); -} -void display_set_enabled(bool enabled) { - gpio_output_set(&BOARD_CONFIG_DISPLAY.on_ctrl, enabled); + s_uccb(); } -bool display_update_in_progress(void) { - if (xSemaphoreTake(s_dma_update_in_progress_semaphore, 0) == pdPASS) { - xSemaphoreGive(s_dma_update_in_progress_semaphore); - return false; +static void prv_spim_evt_handler(nrfx_spim_evt_t const *evt, void *ctx) { + portBASE_TYPE woken = pdFALSE; + + if (s_updating) { + PebbleEvent e = { + .type = PEBBLE_CALLBACK_EVENT, + .callback = + { + .callback = prv_terminate_transfer, + }, + }; + + woken = event_put_isr(&e) ? pdTRUE : pdFALSE; + } else { + xSemaphoreGiveFromISR(s_sem, &woken); } - return true; -} -// Static mode is entered by sending 0x00 to the panel -static void prv_display_enter_static(void) { - uint8_t buf[] = { DISP_MODE_STATIC, 0x00, 0x00 }; - prv_enable_spim(); - prv_enable_chip_select(); - prv_display_write_sync(buf, sizeof(buf)); - prv_disable_chip_select(); - prv_disable_spim(); + portEND_SWITCHING_ISR(woken); } -void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { - PBL_ASSERTN(nrcb != NULL); - PBL_ASSERTN(uccb != NULL); - stop_mode_disable(InhibitorDisplay); - xSemaphoreTake(s_dma_update_in_progress_semaphore, portMAX_DELAY); - analytics_stopwatch_start(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME, AnalyticsClient_App); - analytics_inc(ANALYTICS_DEVICE_METRIC_DISPLAY_UPDATES_PER_HOUR, AnalyticsClient_System); +void display_init(void) { + nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG( + BOARD_CONFIG_DISPLAY.clk.gpio_pin, BOARD_CONFIG_DISPLAY.mosi.gpio_pin, + NRF_SPIM_PIN_NOT_CONNECTED, NRF_SPIM_PIN_NOT_CONNECTED); + config.frequency = NRFX_MHZ_TO_HZ(1); + config.bit_order = NRF_SPIM_BIT_ORDER_LSB_FIRST; - power_tracking_start(PowerSystemMcuDma1); + nrfx_err_t err = nrfx_spim_init(&BOARD_CONFIG_DISPLAY.spi, &config, prv_spim_evt_handler, NULL); + PBL_ASSERTN(err == NRFX_SUCCESS); - prv_enable_spim(); + gpio_output_init(&BOARD_CONFIG_DISPLAY.cs, GPIO_OType_PP, GPIO_Speed_50MHz); - prv_display_context_init(&s_display_context); - s_display_context.get_next_row = nrcb; - s_display_context.complete = uccb; + gpio_output_init(&BOARD_CONFIG_DISPLAY.on_ctrl, (GPIOOType_TypeDef)BOARD_CONFIG_DISPLAY.on_ctrl_otype, + GPIO_Speed_50MHz); + gpio_output_set(&BOARD_CONFIG_DISPLAY.on_ctrl, true); - prv_do_dma_update(); + prv_extcomin_init(); - // Block while we wait for the update to finish. - TickType_t ticks = milliseconds_to_ticks(4000); // DMA should be fast - if (xSemaphoreTake(s_dma_update_in_progress_semaphore, ticks) != pdTRUE) { - uint32_t pri_mask = __get_PRIMASK(); - PBL_CROAK("display DMA failed: 0x%" PRIx32, pri_mask); - } + s_sem = xSemaphoreCreateBinary(); +} - power_tracking_stop(PowerSystemMcuDma1); +void display_clear(void) { + uint8_t buf[] = {DISP_MODE_CLEAR, 0x00}; + nrfx_spim_xfer_desc_t desc = {.p_tx_buffer = buf, .tx_length = sizeof(buf)}; - /* needs to not happen from the ISR, because write_sync depends on the ISR to be called again */ - uint8_t buf[] = { 0x00 }; - prv_display_write_sync(buf, sizeof(buf)); - prv_disable_chip_select(); + PBL_ASSERTN(!s_updating); - prv_disable_spim(); + prv_enable_spim(); + prv_enable_chip_select(); - prv_display_enter_static(); + nrfx_err_t err = nrfx_spim_xfer(&BOARD_CONFIG_DISPLAY.spi, &desc, 0); + PBL_ASSERTN(err == NRFX_SUCCESS); + xSemaphoreTake(s_sem, portMAX_DELAY); - xSemaphoreGive(s_dma_update_in_progress_semaphore); - stop_mode_enable(InhibitorDisplay); - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME); + prv_disable_chip_select(); + prv_disable_spim(); } -void display_pulse_vcom(void) { +void display_set_enabled(bool enabled) { + gpio_output_set(&BOARD_CONFIG_DISPLAY.on_ctrl, enabled); } -#if DISPLAY_ORIENTATION_ROTATED_180 -//! -//! memcpy the src buffer to dst and reverse the bits -//! to match the display order -//! -static void prv_memcpy_reverse_bytes(uint8_t* dst, uint8_t* src, int bytes) { - // Skip the mode selection and column address bytes - dst+=2; - while (bytes--) { - *dst++ = reverse_byte(*src++); - } -} -#else -//! -//! memcpy the src buffer to dst backwards (i.e. the highest src byte -//! is the lowest byte in dst. -//! -static void prv_memcpy_backwards(uint32_t* dst, uint32_t* src, int length) { - dst += length - 1; - while (length--) { - *dst-- = ntohl(*src++); - } +void display_set_rotated(bool rotated) { + s_rotated_180 = rotated; } -#endif - - -static bool prv_do_dma_update(void) { - DisplayRow r; - - PBL_ASSERTN(s_display_context.get_next_row != NULL); - bool is_end_of_buffer = !s_display_context.get_next_row(&r); - switch (s_display_context.state) { - case DISPLAY_STATE_IDLE: - { - if (is_end_of_buffer) { - // If nothing has been modified, bail out early - return false; +void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { + DisplayRow row; + uint8_t *pbuf = s_buf; + nrfx_spim_xfer_desc_t desc = {.p_tx_buffer = pbuf}; + + PBL_ASSERTN(!s_updating); + + // write command (write) + *pbuf++ = DISP_MODE_WRITE; + desc.tx_length++; + + while (nrcb(&row)) { + // write row address, data and trailing dummy + *pbuf++ = s_rotated_180 ? (PBL_DISPLAY_HEIGHT - 1) - row.address + 1 : row.address + 1; + if (s_rotated_180) { + for (int i = DISP_LINE_BYTES - 1; i >= 0; --i) { + *pbuf++ = reverse_byte(row.data[i]); + } + } else { + memcpy(pbuf, row.data, DISP_LINE_BYTES); + pbuf += DISP_LINE_BYTES; } - - prv_enable_chip_select(); - - s_display_context.state = DISPLAY_STATE_WRITING; - -#if DISPLAY_ORIENTATION_ROTATED_180 - prv_memcpy_reverse_bytes((uint8_t*)s_dma_line_buffer, r.data, DISP_LINE_BYTES); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= (DISP_MODE_WRITE | reverse_byte(r.address + 1) << 8); -#else - prv_memcpy_backwards(s_dma_line_buffer, (uint32_t*)r.data, DISP_LINE_WORDS); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= (DISP_MODE_WRITE | reverse_byte(167 - r.address + 1) << 8); -#endif - prv_display_write_async(((uint8_t*) s_dma_line_buffer), DISP_DMA_BUFFER_SIZE_BYTES); - - break; + *pbuf++ = 0x00; + desc.tx_length += DISP_LINE_BYTES + 2; } - case DISPLAY_STATE_WRITING: - { - if (is_end_of_buffer) { - s_display_context.complete(); - signed portBASE_TYPE was_higher_priority_task_woken = pdFALSE; - xSemaphoreGiveFromISR(s_dma_update_in_progress_semaphore, &was_higher_priority_task_woken); + // write last trailing dummy + *pbuf++ = 0x00; + desc.tx_length++; - return was_higher_priority_task_woken != pdFALSE; - } + prv_enable_spim(); + prv_enable_chip_select(); -#if DISPLAY_ORIENTATION_ROTATED_180 - prv_memcpy_reverse_bytes((uint8_t*)s_dma_line_buffer, r.data, DISP_LINE_BYTES); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= (DISP_MODE_WRITE | reverse_byte(r.address + 1) << 8); -#else - prv_memcpy_backwards(s_dma_line_buffer, (uint32_t*)r.data, DISP_LINE_WORDS); - s_dma_line_buffer[0] &= ~(0xffff); - s_dma_line_buffer[0] |= reverse_byte(167 - r.address + 1) << 8; -#endif - prv_display_write_async(((uint8_t*) s_dma_line_buffer) + 1, DISP_DMA_BUFFER_SIZE_BYTES - 1); - break; - } - default: - WTF; - } - return false; + s_uccb = uccb; + s_updating = true; + + nrfx_err_t err = nrfx_spim_xfer(&BOARD_CONFIG_DISPLAY.spi, &desc, 0); + PBL_ASSERTN(err == NRFX_SUCCESS); } -void display_show_splash_screen(void) { - // The bootloader has already drawn the splash screen for us; nothing to do! +bool display_update_in_progress(void) { + return s_updating; } -// Stubs for display offset -void display_set_offset(GPoint offset) {} +/* stubs */ -GPoint display_get_offset(void) { return GPointZero; } +void display_update_boot_frame(uint8_t *framebuffer) {} diff --git a/src/fw/drivers/display/wscript_build b/src/fw/drivers/display/wscript_build new file mode 100644 index 0000000000..3eea9399e1 --- /dev/null +++ b/src/fw/drivers/display/wscript_build @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['freertos', 'fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_DISPLAY_QEMU: + sources.append('../qemu/qemu_display_hal.c') +elif bld.env.CONFIG_DISPLAY_JDI_SF32LB: + sources.append('sf32lb/display_jdi.c') + use.append('hal_sifli') +elif bld.env.CONFIG_DISPLAY_SHARP_LS013B7DH01_NRF5: + sources.append('sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c') + use.extend([ + 'driver_gpio', + 'hal_nordic', + ]) + +bld.objects( + name='driver_display', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_display') diff --git a/src/fw/drivers/dma.h b/src/fw/drivers/dma.h index 53a4dfc0a3..13d865fb28 100644 --- a/src/fw/drivers/dma.h +++ b/src/fw/drivers/dma.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/exti.h b/src/fw/drivers/exti.h index cd0b7865cb..274e39df83 100644 --- a/src/fw/drivers/exti.h +++ b/src/fw/drivers/exti.h @@ -1,61 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "board/board.h" -//! For simplicity we just configure all our EXTI-related interrupts to the same priority. This -//! way we don't have to worry about different lines wanting differing priorities when they share -//! the same NVIC channel (and therefore the same priority) -#define EXTI_PRIORITY (0x0e) - typedef enum { ExtiTrigger_Rising, ExtiTrigger_Falling, ExtiTrigger_RisingFalling } ExtiTrigger; -//! See section 12.2.5 "External interrupt/event line mapping" in the STM32F2 reference manual -typedef enum { - ExtiLineOther_RTCAlarm = 17, - ExtiLineOther_RTCWakeup = 22 -} ExtiLineOther; - typedef void (*ExtiHandlerCallback)(bool *should_context_switch); -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb); -//! Configures the given EXTI and NVIC for the given configuration. -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger); - -#if !defined(MICRO_FAMILY_NRF5) && !defined(MICRO_FAMILY_SF32LB52) -static inline void exti_enable(ExtiConfig config); -static inline void exti_disable(ExtiConfig config); -#else void exti_enable(ExtiConfig config); void exti_disable(ExtiConfig config); -#endif - -void exti_enable_other(ExtiLineOther exti_line); -void exti_disable_other(ExtiLineOther exti_line); - -void exti_set_pending(ExtiConfig cfg); - -void exti_clear_pending_other(ExtiLineOther exti_line); - -#include "exti.inl.h" +//! Configures the given EXTI and NVIC for the given configuration. +void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb); \ No newline at end of file diff --git a/src/fw/drivers/exti.inl.h b/src/fw/drivers/exti.inl.h deleted file mode 100644 index 518a966972..0000000000 --- a/src/fw/drivers/exti.inl.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! @file exti.inl.h -//! -//! Helper functions intended to be inlined into the calling code. - -#if !defined(MICRO_FAMILY_NRF5) && !defined(MICRO_FAMILY_SF32LB52) -static inline void exti_enable(ExtiConfig config) { - exti_enable_other(config.exti_line); -} - -static inline void exti_disable(ExtiConfig config) { - exti_disable_other(config.exti_line); -} -#endif - diff --git a/src/fw/drivers/exti/Kconfig b/src/fw/drivers/exti/Kconfig new file mode 100644 index 0000000000..3c078da530 --- /dev/null +++ b/src/fw/drivers/exti/Kconfig @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig EXTI + bool "External Interrupt" + help + External Interrupt Drivers + +if EXTI + +config EXTI_NRF5 + bool "Nordic nRF5" + help + Support for the Nordic nRF5 external interrupt controller. + +config EXTI_SF32LB + bool "SiFli SF32LB" + select HAL_SIFLI_GPIO + help + Support for the SiFli SF32LB external interrupt controller. + +config EXTI_QEMU + bool "QEMU External Interrupt" + help + Support for the QEMU external interrupt peripheral. + +module = DRIVER_EXTI +module-str = External Interrupt +source "src/fw/Kconfig.template.log_level" + +endif # EXTI diff --git a/src/fw/drivers/exti/nrf5.c b/src/fw/drivers/exti/nrf5.c new file mode 100644 index 0000000000..163603393a --- /dev/null +++ b/src/fw/drivers/exti/nrf5.c @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/exti.h" + +#include "board/board.h" +#include "drivers/periph_config.h" +#include "kernel/events.h" +#include "mcu/interrupts.h" +#include "system/passert.h" + +#include + +#include + +// NRF5 emulates EXTI using GPIOTE + +static void prv_exti_handler(nrfx_gpiote_pin_t pin, nrfx_gpiote_trigger_t trigger, void *p_context) { + ExtiHandlerCallback cb = (ExtiHandlerCallback) p_context; + + bool should_context_switch = false; + cb(&should_context_switch); + + portEND_SWITCHING_ISR(should_context_switch); +} + +void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb) { + nrfx_err_t err; + if (!nrfx_gpiote_init_check(&cfg.peripheral)) { + err = nrfx_gpiote_init(&cfg.peripheral, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY); + PBL_ASSERTN(err == NRFX_SUCCESS); + } + uint8_t channel = cfg.channel; + nrfx_gpiote_trigger_config_t tcfg = { + .trigger = trigger == ExtiTrigger_Rising ? NRFX_GPIOTE_TRIGGER_LOTOHI : + trigger == ExtiTrigger_Falling ? NRFX_GPIOTE_TRIGGER_HITOLO : + trigger == ExtiTrigger_RisingFalling ? NRFX_GPIOTE_TRIGGER_TOGGLE : + NRFX_GPIOTE_TRIGGER_NONE, + .p_in_channel = &channel + }; + nrfx_gpiote_handler_config_t hcfg = { + .handler = prv_exti_handler, + .p_context = cb + }; + nrfx_gpiote_input_pin_config_t pcfg = { + .p_pull_config = NULL, + .p_trigger_config = &tcfg, + .p_handler_config = &hcfg + }; + + err = nrfx_gpiote_input_configure(&cfg.peripheral, cfg.gpio_pin, &pcfg); + PBL_ASSERTN(err == NRFX_SUCCESS); + + nrfx_gpiote_trigger_disable(&cfg.peripheral, cfg.gpio_pin); +} + +void exti_enable(ExtiConfig cfg) { + nrfx_gpiote_trigger_enable(&cfg.peripheral, cfg.gpio_pin, true /* int_enable */); +} + +void exti_disable(ExtiConfig cfg) { + nrfx_gpiote_trigger_disable(&cfg.peripheral, cfg.gpio_pin); +} + diff --git a/src/fw/drivers/exti/sf32lb.c b/src/fw/drivers/exti/sf32lb.c new file mode 100644 index 0000000000..d7a54bf7a2 --- /dev/null +++ b/src/fw/drivers/exti/sf32lb.c @@ -0,0 +1,137 @@ +/* SPDX-FileCopyrightText: 2025 SiFli Technologies(Nanjing) Co., Ltd */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/exti.h" + +#include + +#include "board/board.h" +#include "drivers/periph_config.h" +#include "kernel/events.h" +#include "mcu/interrupts.h" +#include "system/logging.h" +#include "system/passert.h" + +PBL_LOG_MODULE_DEFINE(driver_exti_sf32lb, CONFIG_DRIVER_EXTI_LOG_LEVEL); + +#define EXTI_MAX_GPIO1_PIN_NUM 16 + +typedef struct { + uint32_t gpio_pin; + ExtiHandlerCallback callback; +} ExtiHandlerConfig_t; + +static ExtiHandlerConfig_t s_exti_gpio1_handler_configs[EXTI_MAX_GPIO1_PIN_NUM]; +static bool s_should_context_switch; + +static GPIO_TypeDef *prv_gpio_get_instance(GPIO_TypeDef *hgpio, uint16_t gpio_pin, + uint16_t *offset) { + uint16_t inst_idx; + GPIO_TypeDef *gpiox; + + HAL_ASSERT(gpio_pin < GPIO1_PIN_NUM); + + if (gpio_pin >= GPIO1_PIN_NUM) { + return (GPIO_TypeDef *)NULL; + } + + // There are many groups of similar registers in the GPIO, and because of register length limitations, up to 32 gpio can be operated in each group. + inst_idx = gpio_pin >> 5; + *offset = gpio_pin & 31; + + gpiox = (GPIO_TypeDef *)hgpio + inst_idx; + + return gpiox; +} + +static void prv_insert_handler(GPIO_TypeDef *hgpio, uint8_t gpio_pin, ExtiHandlerCallback cb) { + // Find the handler index for this pin + uint8_t index = 0; + while (index < EXTI_MAX_GPIO1_PIN_NUM && + s_exti_gpio1_handler_configs[index].callback != NULL) { + index++; + } + if (index >= EXTI_MAX_GPIO1_PIN_NUM) { + // No available slot + return; + } + // Store the callback and index + s_exti_gpio1_handler_configs[index].gpio_pin = gpio_pin; + s_exti_gpio1_handler_configs[index].callback = cb; +} + +void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb) { + GPIO_InitTypeDef init; + int flags; + + init.Pin = cfg.gpio_pin; + init.Pull = GPIO_NOPULL; + + switch (cfg.pull) { + case GPIO_PuPd_UP: + flags = PIN_PULLUP; + break; + case GPIO_PuPd_DOWN: + flags = PIN_PULLDOWN; + break; + default: + flags = PIN_NOPULL; + break; + } + + switch (trigger) { + case ExtiTrigger_Rising: + init.Mode = GPIO_MODE_IT_RISING; + break; + case ExtiTrigger_Falling: + init.Mode = GPIO_MODE_IT_FALLING; + break; + case ExtiTrigger_RisingFalling: + init.Mode = GPIO_MODE_IT_RISING_FALLING; + break; + } + + HAL_NVIC_DisableIRQ(GPIO1_IRQn); + + HAL_PIN_Set(PAD_PA00 + cfg.gpio_pin, GPIO_A0 + cfg.gpio_pin, flags, 1); + HAL_GPIO_Init(cfg.peripheral, &init); + + prv_insert_handler(cfg.peripheral, cfg.gpio_pin, cb); + + HAL_NVIC_SetPriority(GPIO1_IRQn, 6, 0); + HAL_NVIC_EnableIRQ(GPIO1_IRQn); +} + +void exti_enable(ExtiConfig cfg) { + uint16_t offset; + GPIO_TypeDef *gpiox = prv_gpio_get_instance(cfg.peripheral, cfg.gpio_pin, &offset); + gpiox->IESR = (1 << offset); +} + +void exti_disable(ExtiConfig cfg) { + uint16_t offset; + GPIO_TypeDef *gpiox = prv_gpio_get_instance(cfg.peripheral, cfg.gpio_pin, &offset); + gpiox->IECR = (1 << offset); + gpiox->ISR = (1 << offset); +} + +void HAL_GPIO_EXTI_Callback(GPIO_TypeDef *hgpio, uint16_t GPIO_Pin) { + for (uint8_t index = 0; index < EXTI_MAX_GPIO1_PIN_NUM; index++) { + if (s_exti_gpio1_handler_configs[index].callback != NULL && + s_exti_gpio1_handler_configs[index].gpio_pin == GPIO_Pin) { + bool should_context_switch = false; + + s_exti_gpio1_handler_configs[index].callback(&should_context_switch); + s_should_context_switch |= should_context_switch; + return; + } + } + + PBL_LOG_WRN("No handler found for GPIO pin %u", GPIO_Pin); +} + +void GPIO1_IRQHandler(void) { + s_should_context_switch = false; + HAL_GPIO_IRQHandler(hwp_gpio1); + portEND_SWITCHING_ISR(s_should_context_switch); +} \ No newline at end of file diff --git a/src/fw/drivers/exti/wscript_build b/src/fw/drivers/exti/wscript_build new file mode 100644 index 0000000000..f39a3c40ef --- /dev/null +++ b/src/fw/drivers/exti/wscript_build @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['freertos', 'fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_EXTI_QEMU: + sources.append('../stubs/exti.c') +elif bld.env.CONFIG_EXTI_NRF5: + sources.append('nrf5.c') + use.append('hal_nordic') +elif bld.env.CONFIG_EXTI_SF32LB: + sources.append('sf32lb.c') + +bld.objects( + name='driver_exti', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_exti') diff --git a/src/fw/drivers/flash.h b/src/fw/drivers/flash.h index 4c71b6566f..d6f37ca813 100644 --- a/src/fw/drivers/flash.h +++ b/src/fw/drivers/flash.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -225,13 +212,14 @@ void flash_release_many(uint32_t num_locks); //! @retval StatusCode if the read failed status_t flash_read_security_register(uint32_t addr, uint8_t *val); -//! Check if the security registers are locked +//! Check if a security register is locked //! +//! @param addr The address of the security register to check. //! @param [out] locked True if the security registers are locked //! //! @retval S_SUCCESS if the check was successful //! @retval StatusCode if the check failed -status_t flash_security_registers_are_locked(bool *locked); +status_t flash_security_register_is_locked(uint32_t addr, bool *locked); //! Erase security register //! @@ -256,12 +244,14 @@ status_t flash_write_security_register(uint32_t addr, uint8_t val); //! @returns The information about the security registers. const FlashSecurityRegisters *flash_security_registers_info(void); -#ifdef RECOVERY_FW -//! Lock security registers +#ifdef CONFIG_RECOVERY_FW +//! Lock security register //! //! @warning This is a one time operation and will permanently lock the security registers. //! +//! @param addr The address of the security register to lock. +//! //! @retval S_SUCCESS if the lock was successful //! @retval StatusCode if the lock failed -status_t flash_lock_security_registers(void); -#endif // RECOVERY_FW +status_t flash_lock_security_register(uint32_t addr); +#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/drivers/flash/Kconfig b/src/fw/drivers/flash/Kconfig new file mode 100644 index 0000000000..bcfc239543 --- /dev/null +++ b/src/fw/drivers/flash/Kconfig @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig FLASH + bool "Flash" + help + Flash Drivers + +if FLASH + +config FLASH_GD25LQ255E + bool "GigaDevice GD25LQ255E" + help + Support for the GigaDevice GD25LQ255E NOR flash. + +config FLASH_GD25Q256E + bool "GigaDevice GD25Q256E" + help + Support for the GigaDevice GD25Q256E NOR flash. + +config FLASH_QEMU + bool "QEMU External Flash" + help + Support for the QEMU external flash peripheral. + +module = DRIVER_FLASH +module-str = Flash +source "src/fw/Kconfig.template.log_level" + +endif # FLASH diff --git a/src/fw/drivers/flash/cd_flash_driver.c b/src/fw/drivers/flash/cd_flash_driver.c index 892a9c27bd..2d2cf9668b 100644 --- a/src/fw/drivers/flash/cd_flash_driver.c +++ b/src/fw/drivers/flash/cd_flash_driver.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -111,6 +98,6 @@ status_t cd_flash_read_security_register(uint32_t addr, uint8_t *val) { return flash_impl_read_security_register(addr, val); } -status_t cd_flash_security_registers_are_locked(bool *locked) { - return flash_impl_security_registers_are_locked(locked); +status_t cd_flash_security_register_is_locked(uint32_t addr, bool *locked) { + return flash_impl_security_register_is_locked(addr, locked); } \ No newline at end of file diff --git a/src/fw/drivers/flash/flash_api.c b/src/fw/drivers/flash/flash_api.c index 1ab1ed7201..3234b4b6b2 100644 --- a/src/fw/drivers/flash/flash_api.c +++ b/src/fw/drivers/flash/flash_api.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/flash.h" #include "drivers/flash/flash_internal.h" @@ -29,8 +16,8 @@ #include "os/mutex.h" #include "os/tick.h" #include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/analytics/analytics.h" #include "system/logging.h" #include "system/passert.h" #include "kernel/util/sleep.h" @@ -38,6 +25,8 @@ #include "FreeRTOS.h" #include "semphr.h" +PBL_LOG_MODULE_DEFINE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); + #define MAX_ERASE_RETRIES (3) static PebbleMutex *s_flash_lock; @@ -58,119 +47,17 @@ static struct FlashEraseContext { static TimerID s_erase_poll_timer; static TimerID s_erase_suspend_timer; -static uint32_t s_analytics_read_count; -static uint32_t s_analytics_read_bytes_count; -static uint32_t s_analytics_write_bytes_count; - -static uint32_t s_system_analytics_read_bytes_count = 0; -static uint32_t s_system_analytics_write_bytes_count = 0; -static uint8_t s_system_analytics_erase_count = 0; - -void analytics_external_collect_system_flash_statistics(void) { - analytics_set(ANALYTICS_DEVICE_METRIC_FLASH_READ_BYTES_COUNT, - s_system_analytics_read_bytes_count, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_FLASH_WRITE_BYTES_COUNT, - s_system_analytics_write_bytes_count, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_FLASH_ERASE_COUNT, - s_system_analytics_erase_count, AnalyticsClient_System); - - s_system_analytics_read_bytes_count = 0; - s_system_analytics_write_bytes_count = 0; - s_system_analytics_erase_count = 0; -} - -void analytics_external_collect_app_flash_read_stats(void) { - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_COUNT, - s_analytics_read_count, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_BYTES_COUNT, - s_analytics_read_bytes_count, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_FLASH_WRITE_BYTES_COUNT, - s_analytics_write_bytes_count, AnalyticsClient_App); - - // The overhead cost of tracking whether each flash read was due to the foreground - // or background app is large, so the best we can do is to attribute to both of them - if (worker_manager_get_current_worker_md() != NULL) { - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_COUNT, - s_analytics_read_count, AnalyticsClient_Worker); - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_BYTES_COUNT, - s_analytics_read_bytes_count, AnalyticsClient_Worker); - analytics_set(ANALYTICS_APP_METRIC_FLASH_WRITE_BYTES_COUNT, - s_analytics_write_bytes_count, AnalyticsClient_Worker); - } - - s_analytics_read_count = 0; - s_analytics_read_bytes_count = 0; - s_analytics_write_bytes_count = 0; -} - -//! Assumes that s_flash_lock is held. -static status_t prv_try_restart_interrupted_erase(bool is_subsector, - uint32_t addr) { - status_t status = is_subsector? flash_impl_erase_subsector_begin(addr) - : flash_impl_erase_sector_begin(addr); - if (FAILED(status)) { - PBL_LOG(LOG_LEVEL_ERROR, "Got error trying to reissue interrupted erase: " - "%"PRIi32, status); - return status; - } - - // Hopefully the task watchdog isn't enabled; this could take a while. - while (1) { - psleep(10); - status = flash_impl_get_erase_status(); - if (status != E_BUSY && status != E_AGAIN) { - // Success or failure - return status; - } - } -} void flash_init(void) { - if (s_flash_lock) { - return; // Already initialized - } + flash_impl_init(false /* coredump_mode */); + s_flash_lock = mutex_create(); s_erase_semphr = xSemaphoreCreateBinary(); xSemaphoreGive(s_erase_semphr); s_erase_poll_timer = new_timer_create(); s_erase_suspend_timer = new_timer_create(); - flash_erase_init(); - mutex_lock(s_flash_lock); - flash_impl_init(false /* coredump_mode */); - - uint32_t erase_in_progress_address = 0; - bool is_subsector = false; - if (flash_impl_get_nvram_erase_status( - &is_subsector, &erase_in_progress_address) == S_TRUE) { - // An erase was interrupted by e.g. a crash. Retry the erase so the - // incompletely-erased sector doesn't cause us trouble later on. - PBL_LOG(LOG_LEVEL_WARNING, "Flash erase to 0x%"PRIx32" was interrupted " - "last boot. Restarting the erase...", erase_in_progress_address); - - // When an erase is interrupted, subsequent erases of the same sector tend to - // take an exceptionally long time and may even outright fail. Try the erase a - // few times before giving up. - int attempt = 1; - while (true) { - status_t status = prv_try_restart_interrupted_erase( - is_subsector, erase_in_progress_address); - if (PASSED(status)) { - break; - } - PBL_LOG(LOG_LEVEL_ERROR, "Flash erase failed, status %"PRIi32, status); - if (attempt++ >= MAX_ERASE_RETRIES) { - // We've tried all we can. No point in croaking; it's not like a reboot - // is going to fix anything. Best we can do is pretend like nothing is - // wrong and hope for the best. - PBL_LOG(LOG_LEVEL_ERROR, "Giving up on restarting the flash erase."); - break; - } - } - } - - flash_impl_clear_nvram_erase_status(); - mutex_unlock(s_flash_lock); + flash_erase_init(); } #if UNITTEST @@ -220,16 +107,17 @@ static void prv_erase_suspend_timer_cb(void *unused) { void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { mutex_lock(s_flash_lock); - s_analytics_read_count++; - s_analytics_read_bytes_count += buffer_size; - s_system_analytics_read_bytes_count += buffer_size; // TODO: use DMA when possible // TODO: be smarter about pausing erases. Some flash chips allow concurrent // reads while an erase is in progress, as long as the read is to another bank // than the one being erased. prv_erase_pause(); - new_timer_start(s_erase_suspend_timer, 5, prv_erase_suspend_timer_cb, NULL, 0); + if (s_erase.suspended) { + new_timer_start(s_erase_suspend_timer, 5, prv_erase_suspend_timer_cb, NULL, 0); + } + stop_mode_disable(InhibitorFlash); flash_impl_read_sync(buffer, start_addr, buffer_size); + stop_mode_enable(InhibitorFlash); mutex_unlock(s_flash_lock); } @@ -244,10 +132,13 @@ void flash_write_bytes(const uint8_t *buffer, uint32_t start_addr, uint32_t buffer_size) { mutex_lock(s_flash_lock); stop_mode_disable(InhibitorFlash); // FIXME: PBL-18028 - s_analytics_write_bytes_count += buffer_size; - s_system_analytics_write_bytes_count += buffer_size; prv_erase_pause(); - new_timer_start(s_erase_suspend_timer, 50, prv_erase_suspend_timer_cb, NULL, 0); + if (s_erase.suspended) { + new_timer_start(s_erase_suspend_timer, 50, prv_erase_suspend_timer_cb, NULL, 0); + } + + PBL_ANALYTICS_ADD(flash_spi_write_bytes, buffer_size); + while (buffer_size) { int written = flash_impl_write_page_begin(buffer, start_addr, buffer_size); PBL_ASSERT( @@ -330,14 +221,10 @@ static uint32_t prv_flash_erase_start(uint32_t addr, return 0; } - analytics_inc(ANALYTICS_APP_METRIC_FLASH_SUBSECTOR_ERASE_COUNT, - AnalyticsClient_CurrentTask); - s_system_analytics_erase_count++; status = is_subsector? flash_impl_erase_subsector_begin(addr) : flash_impl_erase_sector_begin(addr); if (PASSED(status)) { - flash_impl_set_nvram_erase_status(is_subsector, addr); mutex_unlock(s_flash_lock); return (s_erase.expected_duration * 7 / 8); } else { @@ -375,7 +262,6 @@ static uint32_t prv_flash_erase_poll(void) { if (erase_finished) { stop_mode_enable(InhibitorFlash); s_erase.in_progress = false; - flash_impl_clear_nvram_erase_status(); } mutex_unlock(s_flash_lock); @@ -386,12 +272,16 @@ static uint32_t prv_flash_erase_poll(void) { xSemaphoreGive(s_erase_semphr); if (status == E_ERROR && saved_ctx.retries < MAX_ERASE_RETRIES) { // Try issuing the erase again. It might succeed this time around. - PBL_LOG(LOG_LEVEL_DEBUG, "Erase of 0x%"PRIx32" failed (attempt %d)." + PBL_LOG_DBG("Erase of 0x%"PRIx32" failed (attempt %d)." " Trying again...", saved_ctx.address, saved_ctx.retries); return prv_flash_erase_start( saved_ctx.address, saved_ctx.on_complete_cb, saved_ctx.cb_context, saved_ctx.is_subsector, saved_ctx.retries + 1); } else { + if (status == S_SUCCESS) { + PBL_ANALYTICS_ADD(flash_spi_erase_bytes, + saved_ctx.is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES); + } // Only run the callback with no locks held so that the callback won't // deadlock if it kicks off another sector erase. saved_ctx.on_complete_cb(saved_ctx.cb_context, status); @@ -453,7 +343,7 @@ static void prv_flash_erase_blocking(uint32_t sector_addr, bool is_subsector) { // the watchdog kill us in this case. static const uint32_t FLASH_ERASE_BLOCKING_TIMEOUT_MS = 5000; if (total_time_spent_waiting_ms < FLASH_ERASE_BLOCKING_TIMEOUT_MS) { -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD // Our bigboards have had a hard life and they have some fairly abused flash chips, and we // run into 5+ second erases pretty regularly. We're not holding the flash lock while we're // doing this, so other threads are allowed to use flash, but it's pretty common to hold @@ -602,11 +492,11 @@ status_t flash_read_security_register(uint32_t addr, uint8_t *val) { return status; } -status_t flash_security_registers_are_locked(bool *locked) { +status_t flash_security_register_is_locked(uint32_t address, bool *locked) { status_t status; mutex_lock(s_flash_lock); - status = flash_impl_security_registers_are_locked(locked); + status = flash_impl_security_register_is_locked(address, locked); mutex_unlock(s_flash_lock); return status; @@ -636,17 +526,17 @@ const FlashSecurityRegisters *flash_security_registers_info(void) { return flash_impl_security_registers_info(); } -#ifdef RECOVERY_FW -status_t flash_lock_security_registers(void) { +#ifdef CONFIG_RECOVERY_FW +status_t flash_lock_security_register(uint32_t addr) { status_t status; mutex_lock(s_flash_lock); - status = flash_impl_lock_security_registers(); + status = flash_impl_lock_security_register(addr); mutex_unlock(s_flash_lock); return status; } -#endif // RECOVERY_FW +#endif // CONFIG_RECOVERY_FW #include "console/prompt.h" void command_flash_unprotect(void) { diff --git a/src/fw/drivers/flash/flash_crc.c b/src/fw/drivers/flash/flash_crc.c index e7a0b87153..ba7b511cfa 100644 --- a/src/fw/drivers/flash/flash_crc.c +++ b/src/fw/drivers/flash/flash_crc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/flash.h" @@ -23,13 +10,15 @@ #include +PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); + static size_t prv_allocate_crc_buffer(void **buffer) { // Try to allocate a big buffer for reading flash data. If we can't, // use a smaller one. unsigned int chunk_size = 1024; *buffer = kernel_malloc(chunk_size); if (!*buffer) { - PBL_LOG(LOG_LEVEL_WARNING, "Insufficient memory for a large CRC buffer, going slow"); + PBL_LOG_WRN("Insufficient memory for a large CRC buffer, going slow"); chunk_size = 128; *buffer = kernel_malloc_check(chunk_size); diff --git a/src/fw/drivers/flash/flash_erase.c b/src/fw/drivers/flash/flash_erase.c index 73d66e00cb..7b0e699c1a 100644 --- a/src/fw/drivers/flash/flash_erase.c +++ b/src/fw/drivers/flash/flash_erase.c @@ -1,24 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/flash.h" #include "drivers/flash/flash_internal.h" #include "flash_region/flash_region.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/logging.h" #include "system/passert.h" #include "util/attributes.h" #include "util/math.h" @@ -28,6 +16,8 @@ #include +PBL_LOG_MODULE_DECLARE(driver_flash, CONFIG_DRIVER_FLASH_LOG_LEVEL); + static SemaphoreHandle_t s_erase_mutex = NULL; static struct FlashRegionEraseState { @@ -63,7 +53,7 @@ static void prv_async_erase_done_cb(void *ignored, status_t result) { // potential for a stack overflow) if the flash_erase_sector calls the // completion callback asynchronously. if (!new_timer_add_work_callback(prv_erase_next_async, NULL)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to enqueue callback; aborting erase"); + PBL_LOG_ERR("Failed to enqueue callback; aborting erase"); prv_unlock_erase_mutex(); s_erase_state.on_complete(s_erase_state.on_complete_context, E_INTERNAL); } diff --git a/src/fw/drivers/flash/flash_impl.h b/src/fw/drivers/flash/flash_impl.h index cdc0aa2aaf..7157f3211b 100644 --- a/src/fw/drivers/flash/flash_impl.h +++ b/src/fw/drivers/flash/flash_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -281,43 +268,6 @@ status_t flash_impl_blank_check_subsector(FlashAddress addr); //! sector is not erased fully before it is written to. status_t flash_impl_blank_check_sector(FlashAddress addr); -//! Save the address of an erase in progress to a nonvolatile location. The -//! erase address, along with the fact that an erase is in progress, must be -//! able to survive a system crash and reboot. -//! -//! @note Writing this data to the same flash array that is being erased is -//! almost certainly a bad idea. -//! -//! @param is_subsector True if the erase is a subsector. -//! -//! @param addr The address being erased. -//! -//! @return S_SUCCESS if the data was successfully stored. -status_t flash_impl_set_nvram_erase_status(bool is_subsector, - FlashAddress addr); - -//! Save to a nonvolatile location the fact that no erase is in progress. -//! -//! @return S_SUCCESS if the status was successfully stored. -status_t flash_impl_clear_nvram_erase_status(void); - -//! Retrieve the erase status previously set by -//! flash_impl_set_nvram_erase_status or flash_impl_clear_nvram_erase_status. -//! -//! @param [out] is_subsector The value of is_subsector passed to the most -//! most recent call to flash_impl_set_nvram_erase_status if the status was -//! not subsequently cleared by flash_impl_clear_nvram_erase_status. The -//! pointer should not be written to if the erase status was cleared. -//! -//! @param [out] addr The address passed to the most recent call to -//! flash_impl_set_nvram_erase_status if the status was not subsequently -//! cleared by flash_impl_clear_nvram_erase_status. The address should not be -//! written to if the erase status was cleared. -//! -//! @return S_TRUE if an erase was in progress; S_FALSE otherwise. -status_t flash_impl_get_nvram_erase_status(bool *is_subsector, - FlashAddress *addr); - void flash_impl_use(void); void flash_impl_release(void); void flash_impl_release_many(uint32_t num_locks); @@ -332,13 +282,14 @@ void flash_impl_release_many(uint32_t num_locks); //! @retval StatusCode if the read failed status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val); -//! Check if the security registers are locked +//! Check if a security register is locked //! +//! @param address The address of the security register to check. //! @param [out] locked True if the security registers are locked //! //! @retval S_SUCCESS if the check was successful //! @retval StatusCode if the check failed -status_t flash_impl_security_registers_are_locked(bool *locked); +status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked); //! Erase security register //! @@ -363,12 +314,14 @@ status_t flash_impl_write_security_register(uint32_t addr, uint8_t val); //! @returns The information about the security registers. const FlashSecurityRegisters *flash_impl_security_registers_info(void); -#ifdef RECOVERY_FW -//! Lock security registers +#ifdef CONFIG_RECOVERY_FW +//! Lock security register //! //! @warning This is a one time operation and will permanently lock the security registers. //! +//! @param address The address of the security register to lock. +//! //! @retval S_SUCCESS if the lock was successful //! @retval StatusCode if the lock failed -status_t flash_impl_lock_security_registers(void); -#endif // RECOVERY_FW +status_t flash_impl_lock_security_register(uint32_t address); +#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/drivers/flash/flash_internal.h b/src/fw/drivers/flash/flash_internal.h index eb6200a3e5..dc2b789b4d 100644 --- a/src/fw/drivers/flash/flash_internal.h +++ b/src/fw/drivers/flash/flash_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/flash/gd25lq255e.c b/src/fw/drivers/flash/gd25lq255e.c index 70a81b5ed5..6c11861f21 100644 --- a/src/fw/drivers/flash/gd25lq255e.c +++ b/src/fw/drivers/flash/gd25lq255e.c @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "board/board.h" #include "drivers/flash/flash_impl.h" #include "drivers/flash/qspi_flash.h" @@ -8,9 +11,6 @@ #include "util/math.h" #include "util/size.h" -#define NRF5_COMPATIBLE -#include - static bool s_protected; static FlashAddress s_protected_start; static FlashAddress s_protected_end; @@ -93,8 +93,6 @@ static status_t prv_flash_check_protected(FlashAddress addr) { return E_INVALID_OPERATION; } -bool flash_check_whoami(void) { return qspi_flash_check_whoami(QSPI_FLASH); } - FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { return (addr & SECTOR_ADDR_MASK); } @@ -212,8 +210,8 @@ status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { return qspi_flash_read_security_register(QSPI_FLASH, addr, val); } -status_t flash_impl_security_registers_are_locked(bool *locked) { - return qspi_flash_security_registers_are_locked(QSPI_FLASH, locked); +status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { + return qspi_flash_security_register_is_locked(QSPI_FLASH, address, locked); } status_t flash_impl_erase_security_register(uint32_t addr) { @@ -228,8 +226,8 @@ const FlashSecurityRegisters *flash_impl_security_registers_info(void) { return qspi_flash_security_registers_info(QSPI_FLASH); } -#ifdef RECOVERY_FW -status_t flash_impl_lock_security_registers(void) { - return qspi_flash_lock_security_registers(QSPI_FLASH); +#ifdef CONFIG_RECOVERY_FW +status_t flash_impl_lock_security_register(uint32_t address) { + return qspi_flash_lock_security_register(QSPI_FLASH, address); } #endif \ No newline at end of file diff --git a/src/fw/drivers/flash/gd25q256e.c b/src/fw/drivers/flash/gd25q256e.c index 1e69b7aeaf..51df81a07b 100644 --- a/src/fw/drivers/flash/gd25q256e.c +++ b/src/fw/drivers/flash/gd25q256e.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" #include "drivers/flash/flash_impl.h" @@ -24,9 +11,6 @@ #include "util/math.h" #include "util/size.h" -#define SF32LB52_COMPATIBLE -#include - static bool s_protected; static FlashAddress s_protected_start; static FlashAddress s_protected_end; @@ -109,8 +93,6 @@ static status_t prv_flash_check_protected(FlashAddress addr) { return E_INVALID_OPERATION; } -bool flash_check_whoami(void) { return qspi_flash_check_whoami(QSPI_FLASH); } - FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { return (addr & SECTOR_ADDR_MASK); } @@ -228,8 +210,8 @@ status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { return qspi_flash_read_security_register(QSPI_FLASH, addr, val); } -status_t flash_impl_security_registers_are_locked(bool *locked) { - return qspi_flash_security_registers_are_locked(QSPI_FLASH, locked); +status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { + return qspi_flash_security_register_is_locked(QSPI_FLASH, address, locked); } status_t flash_impl_erase_security_register(uint32_t addr) { @@ -244,8 +226,8 @@ const FlashSecurityRegisters *flash_impl_security_registers_info(void) { return qspi_flash_security_registers_info(QSPI_FLASH); } -#ifdef RECOVERY_FW -status_t flash_impl_lock_security_registers(void) { - return qspi_flash_lock_security_registers(QSPI_FLASH); +#ifdef CONFIG_RECOVERY_FW +status_t flash_impl_lock_security_register(uint32_t address) { + return qspi_flash_lock_security_register(QSPI_FLASH, address); } #endif \ No newline at end of file diff --git a/src/fw/drivers/flash/micron_n25q/cd_flash_driver.c b/src/fw/drivers/flash/micron_n25q/cd_flash_driver.c deleted file mode 100644 index fbcd237952..0000000000 --- a/src/fw/drivers/flash/micron_n25q/cd_flash_driver.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "drivers/flash.h" -#include "drivers/flash/micron_n25q/flash_private.h" -#include "drivers/watchdog.h" -#include "flash_region/flash_region.h" -#include "kernel/util/delay.h" -#include "util/math.h" - -#include "kernel/core_dump_private.h" - -//! We have our own flash driver for coredump support because it must not use -//! any FreeRTOS constructs & we want to keep it as simple as possible. In -//! addition we want the flexibility to be able to reset the flash driver to -//! get it into a working state - -static void prv_flash_start_cmd(void) { - GPIO_ResetBits(FLASH_GPIO, FLASH_PIN_SCS); -} - -static void prv_flash_end_cmd(void) { - GPIO_SetBits(FLASH_GPIO, FLASH_PIN_SCS); - - // 50ns required between SCS going high and low again, so just delay here to be safe - delay_us(1); -} - -static uint8_t prv_flash_send_and_receive_byte(uint8_t byte) { - // Ensure that there are no other write operations in progress - while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET) { - continue; - } - // Send the byte on the SPI bus - SPI_I2S_SendData(FLASH_SPI, byte); - - // Wait for the response byte to be received - while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET) { - continue; - } - // Return the byte - return SPI_I2S_ReceiveData(FLASH_SPI); -} - -static uint8_t prv_flash_read_next_byte(void) { - uint8_t result = prv_flash_send_and_receive_byte(FLASH_CMD_DUMMY); - return result; -} - -static void prv_flash_wait_for_write_bounded(volatile int cycles_to_wait) { - prv_flash_start_cmd(); - - prv_flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG); - - uint8_t status_register = 0; - do { - if (cycles_to_wait-- < 1) { - break; - } - status_register = prv_flash_read_next_byte(); - } while (status_register & 0x1); - - prv_flash_end_cmd(); -} - -// Init the flash hardware -void cd_flash_init(void) { - // Enable the SPI clock - RCC_APB2PeriphClockCmd(FLASH_SPI_CLOCK, ENABLE); - - // Enable the GPIO clock - uint8_t idx = ((((uint32_t)FLASH_GPIO) - AHB1PERIPH_BASE) / 0x0400); - SET_BIT(RCC->AHB1ENR, (0x1 << idx)); - - // Init the flash hardware - flash_hw_init(); - - // Make sure we are not in deep sleep - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_WAKE); - prv_flash_end_cmd(); - - // See if we can successfully access the flash - // TODO: Will we successfully recover if the flash HW was left midstream in a command from - // before? - prv_flash_wait_for_write_bounded(64000000); - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_READ_ID); - uint32_t manufacturer = prv_flash_read_next_byte(); - uint32_t type = prv_flash_read_next_byte(); - uint32_t capacity = prv_flash_read_next_byte(); - prv_flash_end_cmd(); - - // If we can't ready the flash info correctly, bail - CD_ASSERTN(manufacturer == 0x20 && type == 0xbb && (capacity >= 0x16)); -} - -static void prv_flash_write_enable(void) { - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_WRITE_ENABLE); - prv_flash_end_cmd(); -} - -static void prv_flash_send_24b_address(uint32_t start_addr) { - // Ensure the high bits are not set. - prv_flash_send_and_receive_byte((start_addr & 0xFF0000) >> 16); - prv_flash_send_and_receive_byte((start_addr & 0x00FF00) >> 8); - prv_flash_send_and_receive_byte((start_addr & 0x0000FF)); -} - -static void prv_flash_wait_for_write(void) { - prv_flash_start_cmd(); - - prv_flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG); - - uint8_t status_register = 0; - do { - status_register = prv_flash_read_next_byte(); - } while (status_register & 0x1); - - prv_flash_end_cmd(); -} - -static void prv_flash_write_page(const uint8_t* buffer, uint32_t start_addr, uint16_t buffer_size) { - // Ensure that we're not trying to write more data than a single page (256 bytes) - CD_ASSERTN(buffer_size <= FLASH_PAGE_SIZE); - CD_ASSERTN(buffer_size); - - // Writing a zero-length buffer is a no-op. - if (buffer_size < 1) { - return; - } - - prv_flash_write_enable(); - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_PAGE_PROGRAM); - prv_flash_send_24b_address(start_addr); - - while (buffer_size--) { - prv_flash_send_and_receive_byte(*buffer); - buffer++; - } - prv_flash_end_cmd(); - prv_flash_wait_for_write(); -} - -void cd_flash_read_bytes(void* buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - uint8_t* buffer = buffer_ptr; - prv_flash_wait_for_write(); - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_READ); - prv_flash_send_24b_address(start_addr); - - while (buffer_size--) { - *buffer = prv_flash_read_next_byte(); - buffer++; - } - prv_flash_end_cmd(); -} - -uint32_t cd_flash_write_bytes(const void* buffer_ptr, uint32_t start_addr, uint32_t buffer_size) { - CD_ASSERTN((start_addr + buffer_size <= CORE_DUMP_FLASH_END) && - (int)start_addr >= CORE_DUMP_FLASH_START); - - const uint8_t* buffer = buffer_ptr; - const uint32_t total_bytes = buffer_size; - uint32_t first_page_available_bytes = FLASH_PAGE_SIZE - (start_addr % FLASH_PAGE_SIZE); - uint32_t bytes_to_write = MIN(buffer_size, first_page_available_bytes); - - while (bytes_to_write) { - prv_flash_write_page(buffer, start_addr, bytes_to_write); - start_addr += bytes_to_write; - buffer += bytes_to_write; - buffer_size -= bytes_to_write; - bytes_to_write = MIN(buffer_size, FLASH_PAGE_SIZE); - } - watchdog_feed(); - return total_bytes; -} - -static void prv_flash_erase_sector(uint32_t sector_addr) { - prv_flash_write_enable(); - - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_ERASE_SECTOR); - prv_flash_send_24b_address(sector_addr); - prv_flash_end_cmd(); - - prv_flash_wait_for_write(); -} - -static void prv_flash_erase_subsector(uint32_t sector_addr) { - prv_flash_write_enable(); - - prv_flash_start_cmd(); - prv_flash_send_and_receive_byte(FLASH_CMD_ERASE_SUBSECTOR); - prv_flash_send_24b_address(sector_addr); - prv_flash_end_cmd(); - - prv_flash_wait_for_write(); -} - -// Erase a region comprised of 1 or more sub-sectors. This will erase sectors at a time if -// the address and size allow. -void cd_flash_erase_region(uint32_t start_addr, uint32_t total_bytes) { - CD_ASSERTN(((start_addr & SUBSECTOR_ADDR_MASK) == start_addr) - && ((total_bytes & SUBSECTOR_ADDR_MASK) == total_bytes)); - - while (total_bytes > 0) { - if (((start_addr & SECTOR_ADDR_MASK) == start_addr) && (total_bytes >= SECTOR_SIZE_BYTES)) { - prv_flash_erase_sector(start_addr); - total_bytes -= SECTOR_SIZE_BYTES; - start_addr += SECTOR_SIZE_BYTES; - } else { - prv_flash_erase_subsector(start_addr); - total_bytes -= SUBSECTOR_SIZE_BYTES; - start_addr += SUBSECTOR_SIZE_BYTES; - } - watchdog_feed(); - } -} diff --git a/src/fw/drivers/flash/micron_n25q/flash.c b/src/fw/drivers/flash/micron_n25q/flash.c deleted file mode 100644 index 032d0b26cd..0000000000 --- a/src/fw/drivers/flash/micron_n25q/flash.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "board/board.h" -#include "drivers/dma.h" -#include "drivers/flash.h" -#include "drivers/flash/micron_n25q/flash_private.h" -#include "kernel/util/stop.h" -#include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" -#include "os/mutex.h" -#include "kernel/util/delay.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#include "FreeRTOS.h" -#include "semphr.h" - -/* - * Each peripheral has a dma channel / stream it works with - * c.f. section 9.3.3 in stm32 reference manual - */ -/* RX DMA */ -static DMA_Stream_TypeDef* FLASH_DMA_STREAM = DMA2_Stream0; -static const uint32_t FLASH_DMA_CHANNEL = DMA_Channel_3; -static const uint32_t FLASH_DMA_IRQn = DMA2_Stream0_IRQn; -static const uint32_t FLASH_DATA_REGISTER_ADDR = (uint32_t)&(SPI1->DR); -/* TX DMA */ -static DMA_Stream_TypeDef* FLASH_TX_DMA_STREAM = DMA2_Stream3; -static const uint32_t FLASH_TX_DMA_CHANNEL = DMA_Channel_3; - -static uint32_t analytics_read_count; -static uint32_t analytics_read_bytes_count; -static uint32_t analytics_write_bytes_count; - -void analytics_external_collect_system_flash_statistics(void) { - // TODO: Add support back to tintin -} - -void analytics_external_collect_app_flash_read_stats(void) { - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_COUNT, analytics_read_count, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_BYTES_COUNT, analytics_read_bytes_count, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_FLASH_WRITE_BYTES_COUNT, analytics_write_bytes_count, AnalyticsClient_App); - - // The overhead cost of tracking whether each flash read was due to the foreground - // or background app is large, so the best we can do is to attribute to both of them - if (worker_manager_get_current_worker_md() != NULL) { - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_COUNT, analytics_read_count, AnalyticsClient_Worker); - analytics_set(ANALYTICS_APP_METRIC_FLASH_READ_BYTES_COUNT, analytics_read_bytes_count, AnalyticsClient_Worker); - analytics_set(ANALYTICS_APP_METRIC_FLASH_WRITE_BYTES_COUNT, analytics_write_bytes_count, AnalyticsClient_Worker); - } - - analytics_read_count = 0; - analytics_read_bytes_count = 0; - analytics_write_bytes_count = 0; -} - -struct FlashState { - bool enabled; - bool sleep_when_idle; - bool deep_sleep; - PebbleMutex * mutex; - SemaphoreHandle_t dma_semaphore; -} s_flash_state; - -static void flash_deep_sleep_enter(void); -static void flash_deep_sleep_exit(void); - - -void assert_usable_state(void) { - PBL_ASSERTN(s_flash_state.mutex != 0); -} - -static void enable_flash_dma_clock(void) { - // TINTINHACK: Rather than update this file to use the new DMA driver, just rely on the fact that - // this is the only consumer of DMA2. - periph_config_enable(DMA2, RCC_AHB1Periph_DMA2); -} - -static void disable_flash_dma_clock(void) { - // TINTINHACK: Rather than update this file to use the new DMA driver, just rely on the fact that - // this is the only consumer of DMA2. - periph_config_disable(DMA2, RCC_AHB1Periph_DMA2); -} - -static void setup_dma_read(uint8_t *buffer, int size) { - DMA_InitTypeDef dma_config; - - DMA_DeInit(FLASH_DMA_STREAM); - DMA_DeInit(FLASH_TX_DMA_STREAM); - - /* RX DMA config */ - DMA_StructInit(&dma_config); - dma_config.DMA_Channel = FLASH_DMA_CHANNEL; - dma_config.DMA_DIR = DMA_DIR_PeripheralToMemory; - dma_config.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; - dma_config.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; - dma_config.DMA_Mode = DMA_Mode_Normal; - dma_config.DMA_PeripheralBaseAddr = FLASH_DATA_REGISTER_ADDR; - dma_config.DMA_PeripheralInc = DMA_PeripheralInc_Disable; - dma_config.DMA_MemoryInc = DMA_MemoryInc_Enable; - dma_config.DMA_Priority = DMA_Priority_High; - dma_config.DMA_FIFOMode = DMA_FIFOMode_Disable; - dma_config.DMA_MemoryBurst = DMA_MemoryBurst_Single; - dma_config.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - dma_config.DMA_Memory0BaseAddr = (uint32_t)buffer; - dma_config.DMA_BufferSize = size; - - DMA_Init(FLASH_DMA_STREAM, &dma_config); - - /* TX DMA config */ - dma_config.DMA_Channel = FLASH_TX_DMA_CHANNEL; - dma_config.DMA_DIR = DMA_DIR_MemoryToPeripheral; - dma_config.DMA_PeripheralBaseAddr = FLASH_DATA_REGISTER_ADDR; - dma_config.DMA_MemoryInc = DMA_MemoryInc_Disable; - dma_config.DMA_Priority = DMA_Priority_High; - dma_config.DMA_Memory0BaseAddr = (uint32_t)&FLASH_CMD_DUMMY; - dma_config.DMA_BufferSize = size; - - DMA_Init(FLASH_TX_DMA_STREAM, &dma_config); - - /* Setup DMA interrupts */ - NVIC_InitTypeDef nvic_config; - nvic_config.NVIC_IRQChannel = FLASH_DMA_IRQn; - nvic_config.NVIC_IRQChannelPreemptionPriority = 0x0f; - nvic_config.NVIC_IRQChannelSubPriority = 0x00; - nvic_config.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&nvic_config); - - DMA_ITConfig(FLASH_DMA_STREAM, DMA_IT_TC, ENABLE); - - // enable the DMA stream to start the transfer - SPI_I2S_DMACmd(FLASH_SPI, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE); -} - -static void do_dma_transfer(void) { - xSemaphoreTake(s_flash_state.dma_semaphore, portMAX_DELAY); - stop_mode_disable(InhibitorFlash); - DMA_Cmd(FLASH_DMA_STREAM, ENABLE); - DMA_Cmd(FLASH_TX_DMA_STREAM, ENABLE); - xSemaphoreTake(s_flash_state.dma_semaphore, portMAX_DELAY); - stop_mode_enable(InhibitorFlash); - xSemaphoreGive(s_flash_state.dma_semaphore); -} - -void DMA2_Stream0_IRQHandler(void) { - if (DMA_GetITStatus(FLASH_DMA_STREAM, DMA_IT_TCIF3)) { - DMA_ClearITPendingBit(FLASH_DMA_STREAM, DMA_IT_TCIF3); - NVIC_DisableIRQ(FLASH_DMA_IRQn); - signed portBASE_TYPE was_higher_priority_task_woken = pdFALSE; - xSemaphoreGiveFromISR(s_flash_state.dma_semaphore, &was_higher_priority_task_woken); - portEND_SWITCHING_ISR(was_higher_priority_task_woken); - return; //notreached - } -} - -static void flash_deep_sleep_enter(void) { - assert_usable_state(); - - if (!s_flash_state.deep_sleep) { - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_DEEP_SLEEP); - flash_end_cmd(); - - // guarantee we have actually transitioned to deep sleep - delay_us(5); - s_flash_state.deep_sleep = true; - } -} - -static void flash_deep_sleep_exit(void) { - assert_usable_state(); - - if (s_flash_state.deep_sleep) { - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_WAKE); - flash_end_cmd(); - - // wait a sufficient amount of time to enter standby mode - // It appears violating these timing conditions can lead to - // random bit corruptions on flash writes! - delay_us(100); - s_flash_state.deep_sleep = false; - } -} - -void handle_sleep_when_idle_begin(void) { - if (s_flash_state.sleep_when_idle) { - flash_deep_sleep_exit(); - } -} - -void flash_power_down_for_stop_mode(void) { - if (s_flash_state.sleep_when_idle) { - if (s_flash_state.enabled) { - enable_flash_spi_clock(); - flash_deep_sleep_enter(); - disable_flash_spi_clock(); - } - } -} - -void flash_power_up_after_stop_mode(void) { - // no need here as this platform doesn't support memory-mappable flash -} - -uint32_t flash_get_sector_base_address(uint32_t addr) { - return addr & ~(SECTOR_SIZE_BYTES - 1); -} - -// This simply issues a command to read a specific register -static uint8_t prv_flash_get_register(uint8_t command) { - flash_start_cmd(); - flash_send_and_receive_byte(command); - uint8_t reg = flash_read_next_byte(); - flash_end_cmd(); - return reg; -} - -// This will read the flag status register and check it for the SectorLockStatus flag -void prv_check_protection_flag() { - uint8_t flag_status_register = prv_flash_get_register(FLASH_CMD_READ_FLAG_STATUS_REG); - // assert if we found the flag to be enabled - PBL_ASSERTN(!(flag_status_register & N25QFlagStatusBit_SectorLockStatus)); -} - -// This will clear the protection flag error from a previous error. -// We call this because the error bits persist across reboots -static void prv_clear_flag_status_register(void) { - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_CLEAR_FLAG_STATUS_REG); - flash_end_cmd(); -} - -/** - * Write up to 1 page (256B) of data to flash. start_addr DOES NOT - * need to be paged aligned. When writing into the middle of a page - * (addr & 0xFFF > 0), overrunning the length of the page will cause - * the write to "wrap around" and will modify (i.e. corrupt) data - * stored before the starting address within the page. - * - */ -static void flash_write_page(const uint8_t* buffer, uint32_t start_addr, uint16_t buffer_size) { - // Ensure that we're not trying to write more data than a single page (256 bytes) - PBL_ASSERTN(buffer_size <= FLASH_PAGE_SIZE); - PBL_ASSERTN(buffer_size); - mutex_assert_held_by_curr_task(s_flash_state.mutex, true /* is_held */); - - // Writing a zero-length buffer is a no-op. - if (buffer_size < 1) { - return; - } - - flash_write_enable(); - - flash_start_cmd(); - - flash_send_and_receive_byte(FLASH_CMD_PAGE_PROGRAM); - flash_send_24b_address(start_addr); - - while (buffer_size--) { - flash_send_and_receive_byte(*buffer); - buffer++; - } - - flash_end_cmd(); - flash_wait_for_write(); - - prv_check_protection_flag(); -} - -// Public interface -// From here on down, make sure you're taking the s_flash_state.mutex before doing anything to the SPI peripheral. - -void flash_enable_write_protection(void) { - return; -} - -void flash_lock(void) { - mutex_lock(s_flash_state.mutex); -} - -void flash_unlock(void) { - mutex_unlock(s_flash_state.mutex); -} - -bool flash_is_enabled(void) { - return (s_flash_state.enabled); -} - -void flash_init(void) { - if (s_flash_state.mutex != 0) { - return; // Already initialized. - } - - s_flash_state.mutex = mutex_create(); - vSemaphoreCreateBinary(s_flash_state.dma_semaphore); - flash_lock(); - - enable_flash_spi_clock(); - - flash_start(); - - s_flash_state.enabled = true; - s_flash_state.sleep_when_idle = false; - - // Assume that last time we shut down we were asleep. Come back out. - s_flash_state.deep_sleep = true; - flash_deep_sleep_exit(); - - prv_clear_flag_status_register(); - - disable_flash_spi_clock(); - flash_unlock(); - - flash_whoami(); - - PBL_LOG_VERBOSE("Detected SPI Flash Size: %u bytes", flash_get_size()); -} - -void flash_stop(void) { - if (s_flash_state.mutex == NULL) { - return; - } - - flash_lock(); - s_flash_state.enabled = false; - flash_unlock(); -} - -void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { - if (!buffer_size) { - return; - } - - assert_usable_state(); - - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - analytics_read_count++; - analytics_read_bytes_count += buffer_size; - power_tracking_start(PowerSystemFlashRead); - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - flash_wait_for_write(); - - flash_start_cmd(); - - flash_send_and_receive_byte(FLASH_CMD_READ); - flash_send_24b_address(start_addr); - - // There is delay associated with setting up the stm32 dma, using FreeRTOS - // sempahores, handling ISRs, etc. Thus for short reads, the cost of using - // DMA is far more expensive than the read being performed. Reads greater - // than 34 was empirically determined to be the point at which using the DMA - // engine is advantageous -#if !defined(TARGET_QEMU) - const uint32_t num_reads_dma_cutoff = 34; -#else - // We are disabling DMA reads when running under QEMU for now because they are not reliable. - const uint32_t num_reads_dma_cutoff = buffer_size + 1; -#endif - if (buffer_size < num_reads_dma_cutoff) { - while (buffer_size--) { - *buffer = flash_read_next_byte(); - buffer++; - } - } else { - enable_flash_dma_clock(); - setup_dma_read(buffer, buffer_size); - do_dma_transfer(); - disable_flash_dma_clock(); - } - - flash_end_cmd(); - - disable_flash_spi_clock(); - - power_tracking_stop(PowerSystemFlashRead); - flash_unlock(); -} - -void flash_write_bytes(const uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) { - if (!buffer_size) { - return; - } - - PBL_ASSERTN((start_addr + buffer_size) <= BOARD_NOR_FLASH_SIZE); - - assert_usable_state(); - - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - analytics_write_bytes_count += buffer_size; - power_tracking_start(PowerSystemFlashWrite); - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - uint32_t first_page_available_bytes = FLASH_PAGE_SIZE - (start_addr % FLASH_PAGE_SIZE); - uint32_t bytes_to_write = MIN(buffer_size, first_page_available_bytes); - - if (first_page_available_bytes < FLASH_PAGE_SIZE) { - PBL_LOG_VERBOSE("Address is not page-aligned; first write will be %"PRId32"B at address 0x%"PRIX32, - first_page_available_bytes, start_addr); - } - - while (bytes_to_write) { - flash_write_page(buffer, start_addr, bytes_to_write); - - start_addr += bytes_to_write; - buffer += bytes_to_write; - buffer_size -= bytes_to_write; - bytes_to_write = MIN(buffer_size, FLASH_PAGE_SIZE); - } - - disable_flash_spi_clock(); - - power_tracking_stop(PowerSystemFlashWrite); - flash_unlock(); -} - -void flash_erase_subsector_blocking(uint32_t subsector_addr) { - assert_usable_state(); - - PBL_LOG(LOG_LEVEL_DEBUG, "Erasing subsector 0x%"PRIx32" (0x%"PRIx32" - 0x%"PRIx32")", - subsector_addr, - subsector_addr & SUBSECTOR_ADDR_MASK, - (subsector_addr & SUBSECTOR_ADDR_MASK) + SUBSECTOR_SIZE_BYTES); - - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - analytics_inc(ANALYTICS_APP_METRIC_FLASH_SUBSECTOR_ERASE_COUNT, AnalyticsClient_CurrentTask); - power_tracking_start(PowerSystemFlashErase); - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - flash_write_enable(); - - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_ERASE_SUBSECTOR); - flash_send_24b_address(subsector_addr); - flash_end_cmd(); - - flash_wait_for_write(); - - prv_check_protection_flag(); - - disable_flash_spi_clock(); - - power_tracking_stop(PowerSystemFlashErase); - flash_unlock(); -} - -void flash_erase_sector_blocking(uint32_t sector_addr) { - assert_usable_state(); - - PBL_LOG(LOG_LEVEL_DEBUG, "Erasing sector 0x%"PRIx32" (0x%"PRIx32" - 0x%"PRIx32")", - sector_addr, - sector_addr & SECTOR_ADDR_MASK, - (sector_addr & SECTOR_ADDR_MASK) + SECTOR_SIZE_BYTES); - - if (flash_sector_is_erased(sector_addr)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Sector %#"PRIx32" already erased", sector_addr); - return; - } - - flash_lock(); - - if (!flash_is_enabled()) { - flash_unlock(); - return; - } - - power_tracking_start(PowerSystemFlashErase); - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - flash_write_enable(); - - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_ERASE_SECTOR); - flash_send_24b_address(sector_addr); - flash_end_cmd(); - - flash_wait_for_write(); - - prv_check_protection_flag(); - - disable_flash_spi_clock(); - - power_tracking_stop(PowerSystemFlashErase); - flash_unlock(); -} - -// It is dangerous to leave this built in by default. -#if 0 -void flash_erase_bulk(void) { - assert_usable_state(); - - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - flash_prf_set_protection(false); - - power_tracking_start(PowerSystemFlashErase); - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - flash_write_enable(); - - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_ERASE_BULK); - flash_end_cmd(); - - flash_wait_for_write(); - - flash_prf_set_protection(true); - - disable_flash_spi_clock(); - - power_tracking_stop(PowerSystemFlashErase); - flash_unlock(); -} -#endif - -void flash_sleep_when_idle(bool enable) { - if (enable == s_flash_state.sleep_when_idle) { - return; - } - - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - enable_flash_spi_clock(); - - s_flash_state.sleep_when_idle = enable; - - if (enable) { - if (!s_flash_state.deep_sleep) { - flash_deep_sleep_enter(); - } - } else { - if (s_flash_state.deep_sleep) { - flash_deep_sleep_exit(); - } - } - - disable_flash_spi_clock(); - flash_unlock(); -} - -bool flash_get_sleep_when_idle(void) { - bool result; - flash_lock(); - result = s_flash_state.deep_sleep; - flash_unlock(); - return result; -} - -void debug_flash_dump_registers(void) { -#ifdef PBL_LOG_ENABLED - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - uint8_t status_register = prv_flash_get_register(FLASH_CMD_READ_STATUS_REG); - uint8_t lock_register = prv_flash_get_register(FLASH_CMD_READ_LOCK_REGISTER); - uint8_t flag_status_register = prv_flash_get_register(FLASH_CMD_READ_FLAG_STATUS_REG); - uint8_t nonvolatile_config_register = - prv_flash_get_register(FLASH_CMD_READ_NONVOLATILE_CONFIG_REGISTER); - uint8_t volatile_config_register = - prv_flash_get_register(FLASH_CMD_READ_VOLATILE_CONFIG_REGISTER); - - disable_flash_spi_clock(); - flash_unlock(); - - PBL_LOG(LOG_LEVEL_DEBUG, "Status Register: 0x%x", status_register); - PBL_LOG(LOG_LEVEL_DEBUG, "Lock Register: 0x%x", lock_register); - PBL_LOG(LOG_LEVEL_DEBUG, "Flag Status Register: 0x%x", flag_status_register); - PBL_LOG(LOG_LEVEL_DEBUG, "Nonvolatile Configuration Register: 0x%x", nonvolatile_config_register); - PBL_LOG(LOG_LEVEL_DEBUG, "Volatile Configuration Register: 0x%x", volatile_config_register); -#endif -} - -bool flash_is_initialized(void) { - return (s_flash_state.mutex != 0); -} - -size_t flash_get_size(void) { - uint32_t spi_flash_id = flash_whoami(); - if (!check_whoami(spi_flash_id)) { - // Zero bytes is the best size to report if the flash is corrupted - return 0; - } - - // capcity_megabytes = 2^(capacity in whoami) - uint32_t capacity = spi_flash_id & 0x000000FF; - // get the capacity of the flash in bytes - return 1 << capacity; -} - -void flash_prf_set_protection(bool do_protect) { - assert_usable_state(); - - flash_lock(); - - if (!s_flash_state.enabled) { - flash_unlock(); - return; - } - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - flash_write_enable(); - - const uint32_t start_addr = FLASH_REGION_SAFE_FIRMWARE_BEGIN; - const uint32_t end_addr = FLASH_REGION_SAFE_FIRMWARE_END; - const uint8_t lock_bits = do_protect ? N25QLockBit_SectorWriteLock : 0; - for (uint32_t addr = start_addr; addr < end_addr; addr += SECTOR_SIZE_BYTES) { - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_WRITE_LOCK_REGISTER); - flash_send_24b_address(addr); - flash_send_and_receive_byte(lock_bits); - flash_end_cmd(); - } - - disable_flash_spi_clock(); - - flash_unlock(); -} - -void flash_erase_sector(uint32_t sector_addr, - FlashOperationCompleteCb on_complete_cb, - void *context) { - // TODO: implement nonblocking erase - flash_erase_sector_blocking(sector_addr); - on_complete_cb(context, S_SUCCESS); -} - -void flash_erase_subsector(uint32_t sector_addr, - FlashOperationCompleteCb on_complete_cb, - void *context) { - // TODO: implement nonblocking erase - flash_erase_subsector_blocking(sector_addr); - on_complete_cb(context, S_SUCCESS); -} diff --git a/src/fw/drivers/flash/micron_n25q/flash_core.c b/src/fw/drivers/flash/micron_n25q/flash_core.c deleted file mode 100644 index faf419d563..0000000000 --- a/src/fw/drivers/flash/micron_n25q/flash_core.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/flash.h" -#include "kernel/util/delay.h" -#include "system/passert.h" -#include "util/units.h" - -#include -#include -#include - -#include "drivers/flash/micron_n25q/flash_private.h" - -void enable_flash_spi_clock(void) { - periph_config_enable(FLASH_SPI, FLASH_SPI_CLOCK); -} - -void disable_flash_spi_clock(void) { - periph_config_disable(FLASH_SPI, FLASH_SPI_CLOCK); -} - -// IMPORTANT: This method is also used by the core dump logic in order to re-initialize the flash hardware -// to prepare for writing the core dump. For this reason, it can NOT use any FreeRTOS functions, mess with -// the interrupt priority, primask, etc. -void flash_hw_init(void) { - // Connect PA5 to SPI1_SCLK - GPIO_PinAFConfig(FLASH_GPIO, GPIO_PinSource5, GPIO_AF_SPI1); - - // Connect PA6 to SPI1_MISO - GPIO_PinAFConfig(FLASH_GPIO, GPIO_PinSource6, GPIO_AF_SPI1); - - // Connect PA7 to SPI1_MOSI - GPIO_PinAFConfig(FLASH_GPIO, GPIO_PinSource7, GPIO_AF_SPI1); - - GPIO_InitTypeDef gpio_cfg; - gpio_cfg.GPIO_OType = GPIO_OType_PP; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Mode = GPIO_Mode_AF; - gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz; - gpio_cfg.GPIO_Pin = FLASH_PIN_MISO | FLASH_PIN_MOSI; - GPIO_Init(FLASH_GPIO, &gpio_cfg); - - // Configure the SCLK pin to have a weak pull-down to put it in a known state - // when SCS is toggled - gpio_cfg.GPIO_PuPd = GPIO_PuPd_DOWN; - gpio_cfg.GPIO_Pin = FLASH_PIN_SCLK; - GPIO_Init(FLASH_GPIO, &gpio_cfg); - - // Configure SCS to be controlled in software; pull up to high when inactive - gpio_cfg.GPIO_Mode = GPIO_Mode_OUT; - gpio_cfg.GPIO_Pin = FLASH_PIN_SCS; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(FLASH_GPIO, &gpio_cfg); - - // Set up a SPI bus on SPI1 - SPI_InitTypeDef spi_cfg; - SPI_I2S_DeInit(FLASH_SPI); - spi_cfg.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - spi_cfg.SPI_Mode = SPI_Mode_Master; - spi_cfg.SPI_DataSize = SPI_DataSize_8b; - spi_cfg.SPI_CPOL = SPI_CPOL_Low; - spi_cfg.SPI_CPHA = SPI_CPHA_1Edge; - spi_cfg.SPI_NSS = SPI_NSS_Soft; - spi_cfg.SPI_BaudRatePrescaler = spi_find_prescaler(MHZ_TO_HZ(54), FLASH_SPI_CLOCK_PERIPH); // max read freq for the flash - spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB; - spi_cfg.SPI_CRCPolynomial = 7; - SPI_Init(FLASH_SPI, &spi_cfg); - - SPI_Cmd(FLASH_SPI, ENABLE); -} - -void flash_start(void) { - periph_config_acquire_lock(); - gpio_use(FLASH_GPIO); - - // Init the hardware - flash_hw_init(); - - gpio_release(FLASH_GPIO); - periph_config_release_lock(); -} - -void flash_start_cmd(void) { - gpio_use(FLASH_GPIO); - GPIO_ResetBits(FLASH_GPIO, FLASH_PIN_SCS); - gpio_release(FLASH_GPIO); -} - -void flash_end_cmd(void) { - gpio_use(FLASH_GPIO); - GPIO_SetBits(FLASH_GPIO, FLASH_PIN_SCS); - gpio_release(FLASH_GPIO); - - // 50ns required between SCS going high and low again, so just delay here to be safe - delay_us(1); -} - -uint8_t flash_send_and_receive_byte(uint8_t byte) { - // Ensure that there are no other write operations in progress - while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET); - // Send the byte on the SPI bus - SPI_I2S_SendData(FLASH_SPI, byte); - - // Wait for the response byte to be received - while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET); - // Return the byte - return SPI_I2S_ReceiveData(FLASH_SPI); -} - -void flash_write_enable(void) { - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_WRITE_ENABLE); - flash_end_cmd(); -} - -void flash_send_24b_address(uint32_t start_addr) { - // Ensure the high bits are not set. - PBL_ASSERTN(!(start_addr & 0xFF000000)); - - flash_send_and_receive_byte((start_addr & 0xFF0000) >> 16); - flash_send_and_receive_byte((start_addr & 0x00FF00) >> 8); - flash_send_and_receive_byte((start_addr & 0x0000FF)); -} - -uint8_t flash_read_next_byte(void) { - uint8_t result = flash_send_and_receive_byte(FLASH_CMD_DUMMY); - return result; -} - -void flash_wait_for_write_bounded(volatile int cycles_to_wait) { - flash_start_cmd(); - - flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG); - - uint8_t status_register = 0; - do { - if (cycles_to_wait-- < 1) { - break; - } - status_register = flash_read_next_byte(); - } while (status_register & N25QStatusBit_WriteInProgress); - - flash_end_cmd(); -} - -void flash_wait_for_write(void) { - flash_start_cmd(); - - flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG); - - uint8_t status_register = 0; - do { - status_register = flash_read_next_byte(); - } while (status_register & N25QStatusBit_WriteInProgress); - - flash_end_cmd(); -} - -bool flash_sector_is_erased(uint32_t sector_addr) { - const uint32_t bufsize = 128; - uint8_t buffer[bufsize]; - sector_addr &= SECTOR_ADDR_MASK; - for (uint32_t offset = 0; offset < SECTOR_SIZE_BYTES; offset += bufsize) { - flash_read_bytes(buffer, sector_addr + offset, bufsize); - for (uint32_t i = 0; i < bufsize; i++) { - if (buffer[i] != 0xff) { - return false; - } - } - } - return true; -} - -uint32_t flash_whoami(void) { - assert_usable_state(); - - flash_lock(); - - if (!flash_is_enabled()) { - flash_unlock(); - return 0; - } - - enable_flash_spi_clock(); - handle_sleep_when_idle_begin(); - - flash_wait_for_write_bounded(64000000); - - flash_start_cmd(); - flash_send_and_receive_byte(FLASH_CMD_READ_ID); - uint32_t manufacturer = flash_read_next_byte(); - uint32_t type = flash_read_next_byte(); - uint32_t capacity = flash_read_next_byte(); - flash_end_cmd(); - - disable_flash_spi_clock(); - flash_unlock(); - - return ((manufacturer << 16) | (type << 8) | capacity); -} - -bool check_whoami(uint32_t spi_flash_id) { - return spi_flash_id == EXPECTED_SPI_FLASH_ID_32MBIT || - spi_flash_id == EXPECTED_SPI_FLASH_ID_64MBIT; -} - -bool flash_is_whoami_correct(void) { - uint32_t spi_flash_id = flash_whoami(); - return check_whoami(spi_flash_id); -} - -void flash_switch_mode(FlashModeType mode) { -} - diff --git a/src/fw/drivers/flash/micron_n25q/flash_private.h b/src/fw/drivers/flash/micron_n25q/flash_private.h deleted file mode 100644 index a17fa5397b..0000000000 --- a/src/fw/drivers/flash/micron_n25q/flash_private.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/spi.h" -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/logging.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -#include "debug/power_tracking.h" - -/* GPIO */ -static GPIO_TypeDef* const FLASH_GPIO = GPIOA; -/* SPI */ -static SPI_TypeDef* const FLASH_SPI = SPI1; -static const uint32_t FLASH_SPI_CLOCK = RCC_APB2Periph_SPI1; -static const SpiPeriphClock FLASH_SPI_CLOCK_PERIPH = SpiPeriphClockAPB2; - - -/* Pin Defintions */ -static const uint32_t FLASH_PIN_SCS = GPIO_Pin_4; -static const uint32_t FLASH_PIN_SCLK = GPIO_Pin_5; -static const uint32_t FLASH_PIN_MISO = GPIO_Pin_6; -static const uint32_t FLASH_PIN_MOSI = GPIO_Pin_7; - -/* Flash SPI commands */ -static const uint8_t FLASH_CMD_WRITE_ENABLE = 0x06; -static const uint8_t FLASH_CMD_WRITE_DISABLE = 0x04; -static const uint8_t FLASH_CMD_READ_STATUS_REG = 0x05; -static const uint8_t FLASH_CMD_READ_FLAG_STATUS_REG = 0x70; -static const uint8_t FLASH_CMD_CLEAR_FLAG_STATUS_REG = 0x50; -static const uint8_t FLASH_CMD_READ = 0x03; -static const uint8_t FLASH_CMD_READ_ID = 0x9F; -static const uint8_t FLASH_CMD_PAGE_PROGRAM = 0x02; -static const uint8_t FLASH_CMD_ERASE_SUBSECTOR = 0x20; -static const uint8_t FLASH_CMD_ERASE_SECTOR = 0xD8; -static const uint8_t FLASH_CMD_ERASE_BULK = 0xC7; -static const uint8_t FLASH_CMD_DEEP_SLEEP = 0xB9; -static const uint8_t FLASH_CMD_WAKE = 0xAB; -static const uint8_t FLASH_CMD_DUMMY = 0xA9; -static const uint8_t FLASH_CMD_WRITE_LOCK_REGISTER = 0xE5; -static const uint8_t FLASH_CMD_READ_LOCK_REGISTER = 0xE8; -static const uint8_t FLASH_CMD_READ_NONVOLATILE_CONFIG_REGISTER = 0xB5; -static const uint8_t FLASH_CMD_READ_VOLATILE_CONFIG_REGISTER = 0x85; - -static const uint16_t FLASH_PAGE_SIZE = 0x100; - -typedef enum N25QFlagStatusBit { - // Bit 0 is reserved - N25QFlagStatusBit_SectorLockStatus = (1 << 1), - N25QFlagStatusBit_ProgramSuspended = (1 << 2), - N25QFlagStatusBit_VppStatus = (1 << 3), - N25QFlagStatusBit_ProgramStatus = (1 << 4), - N25QFlagStatusBit_EraseStatus = (1 << 5), - N25QFlagStatusBit_EraseSuspended = (1 << 6), - N25QFlagStatusBit_DeviceReady = (1 << 7), -} N25QFlagStatusBit; - -typedef enum N25QStatusBit { - N25QStatusBit_WriteInProgress = (1 << 0), - N25QStatusBit_WriteEnableLatch = (1 << 1), - N25QStatusBit_BlockProtect0 = (1 << 2), - N25QStatusBit_BlockProtect1 = (1 << 3), - N25QStatusBit_BlockProtect2 = (1 << 4), - N25QStatusBit_ProtectTopBottom = (1 << 5), - // Bit 6 is reserved - N25QStatusBit_StatusRegisterWrite = (1 << 7), -} N25QStatusBit; - -typedef enum N25QLockBit { - N25QLockBit_SectorWriteLock = (1 << 0), - N25QLockBit_SectorLockDown = (1 << 1), - // Bits 2-7 are reserved -} N25QLockBit; - -// Method shared with flash.c and the core dump logic in core_dump.c -void flash_hw_init(void); -void assert_usable_state(void); -void flash_lock(void); -void flash_unlock(void); -bool flash_is_enabled(void); -void handle_sleep_when_idle_begin(void); -void enable_flash_spi_clock(void); -void disable_flash_spi_clock(void); -void flash_start(void); -void flash_start_cmd(void); -void flash_end_cmd(void); -uint8_t flash_send_and_receive_byte(uint8_t byte); -void flash_write_enable(void); -void flash_send_24b_address(uint32_t start_addr); -uint8_t flash_read_next_byte(void); -void flash_wait_for_write_bounded(volatile int cycles_to_wait); -void flash_wait_for_write(void); -bool check_whoami(uint32_t spi_flash_id); -bool flash_is_whoami_correct(void); diff --git a/src/fw/drivers/flash/mt25q.c b/src/fw/drivers/flash/mt25q.c deleted file mode 100644 index ffa5d80df0..0000000000 --- a/src/fw/drivers/flash/mt25q.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "drivers/flash/flash_impl.h" -#include "drivers/flash/qspi_flash.h" -#include "drivers/flash/qspi_flash_part_definitions.h" -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "util/math.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -static QSPIFlashPart QSPI_FLASH_PART = { - .instructions = - { - .fast_read = 0x0B, - .fast_read_ddr = 0x0D, - .pp = 0x02, - .erase_sector_4k = 0x20, - .erase_block_64k = 0xD8, - .write_enable = 0x06, - .write_disable = 0x04, - .rdsr1 = 0x05, - .rdsr2 = 0x70, - .erase_suspend = 0x75, - .erase_resume = 0x7A, - .enter_low_power = 0xB9, - .exit_low_power = 0xAB, - .enter_quad_mode = 0x35, - .reset_enable = 0x66, - .reset = 0x99, - .qspi_id = 0xAF, - - .block_lock = 0xE5, - .block_lock_status = 0xE8, - }, - .status_bit_masks = - { - .busy = 1 << 0, - .write_enable = 1 << 1, - }, - .flag_status_bit_masks = - { - .erase_suspend = 1 << 6, - }, - .dummy_cycles = - { - .fast_read = 10, - .fast_read_ddr = 8, - }, - .block_lock = - { - .has_lock_data = true, - .lock_data = 0x1, - .locked_check = 0x1, - }, - .reset_latency_ms = 51, - .suspend_to_read_latency_us = 0, - .standby_to_low_power_latency_us = 3, - .low_power_to_standby_latency_us = 30, - .supports_fast_read_ddr = true, - .supports_block_lock = true, -#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2 || BOARD_CUTTS_BB - .qspi_id_value = 0x19BB20, - .name = "MT25Q256", -#elif BOARD_ROBERT_EVT - .qspi_id_value = 0x18BB20, - .name = "MT25Q128", -#else -#error "Unsupported board" -#endif -}; - -bool flash_check_whoami(void) { return qspi_flash_check_whoami(QSPI_FLASH); } - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return (addr & SECTOR_ADDR_MASK); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return (addr & SUBSECTOR_ADDR_MASK); -} - -void flash_impl_enable_write_protection(void) {} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - FlashAddress block_addr = start_sector; - while (block_addr <= end_sector) { - uint32_t block_size; - if (WITHIN(block_addr, SECTOR_SIZE_BYTES, BOARD_NOR_FLASH_SIZE - SECTOR_SIZE_BYTES - 1)) { - // Middle of flash has 64k lock units - block_addr = flash_impl_get_sector_base_address(block_addr); - block_size = SECTOR_SIZE_BYTES; - } else { - // Start and end of flash have 1 sector of 4k lock units - block_addr = flash_impl_get_subsector_base_address(block_addr); - block_size = SUBSECTOR_SIZE_BYTES; - } - const status_t sc = qspi_flash_lock_sector(QSPI_FLASH, block_addr); - if (FAILED(sc)) { - return sc; - } - block_addr += block_size; - } - - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - // No way to unprotect all of flash. This requires a full reset of the mt25q - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, qspi_flash_is_in_coredump_mode(QSPI_FLASH)); - return S_SUCCESS; -} - -static void prv_set_high_drive_strength(void) { - // Match the impedance of the traces (~50 ohms) by configuring the drive strength of the data - // output pins on the MT25Q to the 45 ohm setting This avoids signal integreity issues. This is - // done by setting bits 2:0 in the "Enhanced Volatile Configuration Register" to 101b. - const uint8_t read_instruction = 0x65; - const uint8_t write_instruction = 0x61; - const uint8_t mask = 0x7; - const uint8_t value = 0x5; - qspi_flash_ll_set_register_bits(QSPI_FLASH, read_instruction, write_instruction, value, mask); -} - -status_t flash_impl_init(bool coredump_mode) { - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, coredump_mode); -#if BOARD_CUTTS_BB || BOARD_ROBERT_BB || BOARD_ROBERT_EVT - prv_set_high_drive_strength(); -#endif - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { return qspi_flash_is_erase_complete(QSPI_FLASH); } - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, subsector_addr, true /* is_subsector */); -} -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, sector_addr, false /* !is_subsector */); -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - return qspi_flash_erase_suspend(QSPI_FLASH, sector_addr); -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - qspi_flash_erase_resume(QSPI_FLASH, sector_addr); - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - PBL_ASSERT(buffer_size > 0, "flash_impl_read_sync() called with 0 bytes to read"); - qspi_flash_read_blocking(QSPI_FLASH, start_addr, buffer_ptr, buffer_size); - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *buffer, const FlashAddress start_addr, size_t len) { - return qspi_flash_write_page_begin(QSPI_FLASH, buffer, start_addr, len); -} - -status_t flash_impl_get_write_status(void) { return qspi_flash_get_write_status(QSPI_FLASH); } - -status_t flash_impl_enter_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, true); - return S_SUCCESS; -} -status_t flash_impl_exit_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, false); - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - // NYI - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, false /* !is_subsector */); -} -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, true /* is_subsector */); -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 150; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 50; } diff --git a/src/fw/drivers/flash/mx25u.c b/src/fw/drivers/flash/mx25u.c deleted file mode 100644 index 8f6c12b3dc..0000000000 --- a/src/fw/drivers/flash/mx25u.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "drivers/flash/flash_impl.h" -#include "drivers/flash/qspi_flash.h" -#include "drivers/flash/qspi_flash_part_definitions.h" -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "system/version.h" -#include "util/math.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -static QSPIFlashPart QSPI_FLASH_PART = { - .instructions = - { - .fast_read = 0x0B, - .pp = 0x02, - .erase_sector_4k = 0x20, - .erase_block_64k = 0xD8, - .write_enable = 0x06, - .write_disable = 0x04, - .rdsr1 = 0x05, - .rdsr2 = 0x2B, - .erase_suspend = 0xB0, - .erase_resume = 0x30, - .enter_low_power = 0xB9, - .exit_low_power = 0xAB, - .enter_quad_mode = 0x35, - .reset_enable = 0x66, - .reset = 0x99, - .qspi_id = 0xAF, - - .block_lock = 0x36, - .block_lock_status = 0x3C, - .block_unlock_all = 0x98, - - .write_protection_enable = 0x68, - .read_protection_status = 0x2B, - }, - .status_bit_masks = - { - .busy = 1 << 0, - .write_enable = 1 << 1, - }, - .flag_status_bit_masks = - { - .erase_suspend = 1 << 3, - }, - .dummy_cycles = - { - .fast_read = 4, - }, - .block_lock = - { - .has_lock_data = false, - .locked_check = 0xff, - - .protection_enabled_mask = (1 << 7), - }, - .reset_latency_ms = 13, - .suspend_to_read_latency_us = 20, - .standby_to_low_power_latency_us = 10, - .low_power_to_standby_latency_us = 30, - .supports_fast_read_ddr = false, - .supports_block_lock = true, - .qspi_id_value = 0x3725c2, - .name = "MX25U64", -}; - -//! Any PRF built after this timestamp supports mx25u flash protection -#define MIN_PRF_TIMESTAMP_SUPPORTING_PROTECTION (1466531458) - -//! True if the installed PRF version supports flash protection -static bool s_flash_protection_supported = false; - -bool flash_check_whoami(void) { return qspi_flash_check_whoami(QSPI_FLASH); } - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return (addr & SECTOR_ADDR_MASK); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return (addr & SUBSECTOR_ADDR_MASK); -} - -static bool prv_prf_supports_flash_protection(void) { -#if IS_BIGBOARD - // Bigboards should always exercise flash protection - return true; -#else - FirmwareMetadata prf; - if (!version_copy_recovery_fw_metadata(&prf)) { - return false; - } - return (prf.version_timestamp > MIN_PRF_TIMESTAMP_SUPPORTING_PROTECTION); -#endif -} - -void flash_impl_enable_write_protection(void) { - s_flash_protection_supported = prv_prf_supports_flash_protection(); - - if (s_flash_protection_supported) { - // Ensure that write protection is enabled on the mx25u - if (qspi_flash_write_protection_enable(QSPI_FLASH) == S_SUCCESS) { - // after flash protection is enabled, full array is locked. Unlock it. - qspi_flash_unlock_all(QSPI_FLASH); - } - } -} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - if (!s_flash_protection_supported) { - return S_SUCCESS; // If not supported, pretend protection succeeded. - } - - FlashAddress block_addr = start_sector; - while (block_addr <= end_sector) { - uint32_t block_size; - if (WITHIN(block_addr, SECTOR_SIZE_BYTES, BOARD_NOR_FLASH_SIZE - SECTOR_SIZE_BYTES - 1)) { - // Middle of flash has 64k lock units - block_addr = flash_impl_get_sector_base_address(block_addr); - block_size = SECTOR_SIZE_BYTES; - } else { - // Start and end of flash have 1 sector of 4k lock units - block_addr = flash_impl_get_subsector_base_address(block_addr); - block_size = SUBSECTOR_SIZE_BYTES; - } - const status_t sc = qspi_flash_lock_sector(QSPI_FLASH, block_addr); - if (FAILED(sc)) { - return sc; - } - block_addr += block_size; - } - - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { return qspi_flash_unlock_all(QSPI_FLASH); } - -status_t flash_impl_init(bool coredump_mode) { - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, coredump_mode); - qspi_flash_unlock_all(QSPI_FLASH); - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { return qspi_flash_is_erase_complete(QSPI_FLASH); } - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, subsector_addr, true /* is_subsector */); -} -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, sector_addr, false /* !is_subsector */); -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - return qspi_flash_erase_suspend(QSPI_FLASH, sector_addr); -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - qspi_flash_erase_resume(QSPI_FLASH, sector_addr); - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - PBL_ASSERT(buffer_size > 0, "flash_impl_read_sync() called with 0 bytes to read"); - qspi_flash_read_blocking(QSPI_FLASH, start_addr, buffer_ptr, buffer_size); - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *buffer, const FlashAddress start_addr, size_t len) { - return qspi_flash_write_page_begin(QSPI_FLASH, buffer, start_addr, len); -} - -status_t flash_impl_get_write_status(void) { return qspi_flash_get_write_status(QSPI_FLASH); } - -status_t flash_impl_enter_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, true); - return S_SUCCESS; -} -status_t flash_impl_exit_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, false); - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - // NYI - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, false /* !is_subsector */); -} -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, true /* is_subsector */); -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 400; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 40; } diff --git a/src/fw/drivers/flash/nvram_bkp.c b/src/fw/drivers/flash/nvram_bkp.c deleted file mode 100644 index 986158adc8..0000000000 --- a/src/fw/drivers/flash/nvram_bkp.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/flash/flash_impl.h" - -#include "system/passert.h" -#include "system/rtc_registers.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include -#include - -// RTC backup registers are reset to zero on a cold boot (power up from a dead -// battery) so the value stored in the backup register corresponding to no erase -// in progress must also be zero. We need to use a nonzero value to store an -// erase to sector zero, so we can't simply store just the address. -// -// We want to store whether an erase is in progress (1 bit), whether the erase -// is for a sector or a subsector (1 bit), and the address being erased (32 -// bits) in a single 32-bit RTC register. Since we can't magically compress 34 -// bits into 32, we'll need to play some tricks. The address is going to almost -// certainly be less than 32 bits long; we aren't going to be using -// gigabyte-sized flash memories any time soon (at least not with this -// homegrown API), leaving bits free on the high end. - -#define ERASE_IN_PROGRESS 0x80000000 -#define ERASE_IS_SUBSECTOR 0x40000000 -#define ERASE_FLAGS_MASK (ERASE_IN_PROGRESS | ERASE_IS_SUBSECTOR) -#define ERASE_ADDRESS_MASK 0x3FFFFFFF - -status_t flash_impl_set_nvram_erase_status(bool is_subsector, - FlashAddress addr) { - PBL_ASSERTN((addr & ERASE_FLAGS_MASK) == 0); // "Flash address too large to store" - uint32_t reg = addr | ERASE_IN_PROGRESS; - if (is_subsector) { - reg |= ERASE_IS_SUBSECTOR; - } - RTC_WriteBackupRegister(RTC_BKP_FLASH_ERASE_PROGRESS, reg); - return S_SUCCESS; -} - -status_t flash_impl_clear_nvram_erase_status(void) { - RTC_WriteBackupRegister(RTC_BKP_FLASH_ERASE_PROGRESS, 0); - return S_SUCCESS; -} - -status_t flash_impl_get_nvram_erase_status(bool *is_subsector, - FlashAddress *addr) { - uint32_t reg = RTC_ReadBackupRegister(RTC_BKP_FLASH_ERASE_PROGRESS); - if (reg == 0) { - return S_FALSE; - } - - *addr = reg & ERASE_ADDRESS_MASK; - *is_subsector = (reg & ERASE_IS_SUBSECTOR) != 0; - return S_TRUE; -} diff --git a/src/fw/drivers/flash/py25q128ha.c b/src/fw/drivers/flash/py25q128ha.c index 68302e2186..e63aceef66 100644 --- a/src/fw/drivers/flash/py25q128ha.c +++ b/src/fw/drivers/flash/py25q128ha.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" #include "drivers/flash/flash_impl.h" @@ -90,8 +77,6 @@ static QSPIFlashPart QSPI_FLASH_PART = { .name = "PY25Q128HA", }; -bool flash_check_whoami(void) { return qspi_flash_check_whoami(QSPI_FLASH); } - FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { return (addr & SECTOR_ADDR_MASK); } @@ -174,8 +159,8 @@ status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { return qspi_flash_read_security_register(QSPI_FLASH, addr, val); } -status_t flash_impl_security_registers_are_locked(bool *locked) { - return qspi_flash_security_registers_are_locked(QSPI_FLASH, locked); +status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { + return qspi_flash_security_register_is_locked(QSPI_FLASH, address, locked); } status_t flash_impl_erase_security_register(uint32_t addr) { @@ -190,8 +175,8 @@ const FlashSecurityRegisters *flash_impl_security_registers_info(void) { return qspi_flash_security_registers_info(QSPI_FLASH); } -#ifdef RECOVERY_FW -status_t flash_impl_lock_security_registers(void) { - return qspi_flash_lock_security_registers(QSPI_FLASH); +#ifdef CONFIG_RECOVERY_FW +status_t flash_impl_lock_security_register(uint32_t address) { + return qspi_flash_lock_security_register(QSPI_FLASH, address); } #endif \ No newline at end of file diff --git a/src/fw/drivers/flash/qspi_flash.c b/src/fw/drivers/flash/qspi_flash.c deleted file mode 100644 index 4d3550410e..0000000000 --- a/src/fw/drivers/flash/qspi_flash.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "qspi_flash.h" - -#include "board/board.h" -#include "drivers/flash/flash_impl.h" -#include "drivers/gpio.h" -#include "drivers/qspi.h" -#include "flash_region/flash_region.h" -#include "kernel/util/delay.h" -#include "kernel/util/sleep.h" -#include "mcu/cache.h" -#include "qspi_flash_definitions.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "util/math.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#define FLASH_RESET_WORD_VALUE (0xffffffff) - -static void prv_read_register(QSPIFlash *dev, uint8_t instruction, uint8_t *data, uint32_t length) { - qspi_indirect_read_no_addr(dev->qspi, instruction, 0, data, length, false /* !is_ddr */); -} - -static void prv_write_cmd_no_addr(QSPIFlash *dev, uint8_t cmd) { - qspi_indirect_write_no_addr(dev->qspi, cmd, NULL, 0); -} - -static void prv_write_enable(QSPIFlash *dev) { - prv_write_cmd_no_addr(dev, dev->state->part->instructions.write_enable); - // wait for writing to be enabled - qspi_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.write_enable, true /* set */, QSPI_NO_TIMEOUT); -} - -static bool prv_check_whoami(QSPIFlash *dev) { - // The WHOAMI is 3 bytes - const uint32_t whoami_length = 3; - uint32_t read_whoami = 0; - prv_read_register(dev, dev->state->part->instructions.qspi_id, (uint8_t *)&read_whoami, - whoami_length); - - if (read_whoami == dev->state->part->qspi_id_value) { - PBL_LOG(LOG_LEVEL_INFO, "Flash is %s", dev->state->part->name); - return true; - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Flash isn't expected %s (whoami: 0x%" PRIx32 ")", - dev->state->part->name, read_whoami); - return false; - } - qspi_release(dev->qspi); -} - -bool qspi_flash_check_whoami(QSPIFlash *dev) { - qspi_use(dev->qspi); - bool result = prv_check_whoami(dev); - qspi_release(dev->qspi); - return result; -} - -static void prv_set_fast_read_ddr_enabled(QSPIFlash *dev, bool enabled) { - // If we're supposed to use DDR for fast read, make sure the part can support it - PBL_ASSERTN(!enabled || dev->state->part->supports_fast_read_ddr); - dev->state->fast_read_ddr_enabled = enabled; -} - -bool qspi_flash_is_in_coredump_mode(QSPIFlash *dev) { return dev->state->coredump_mode; } - -void qspi_flash_init(QSPIFlash *dev, QSPIFlashPart *part, bool coredump_mode) { - dev->state->part = part; - dev->state->coredump_mode = coredump_mode; - prv_set_fast_read_ddr_enabled(dev, dev->default_fast_read_ddr_enabled); - qspi_use(dev->qspi); - - if (dev->reset_gpio.gpio) { - gpio_output_init(&dev->reset_gpio, GPIO_OType_PP, GPIO_Speed_2MHz); - gpio_output_set(&dev->reset_gpio, false); - } - - // Must call quad_enable first, all commands are QSPI - qspi_indirect_write_no_addr_1line(dev->qspi, dev->state->part->instructions.enter_quad_mode); - - // Reset the flash to stop any program's or erase in progress from before reboot - prv_write_cmd_no_addr(dev, dev->state->part->instructions.reset_enable); - prv_write_cmd_no_addr(dev, dev->state->part->instructions.reset); - - if (coredump_mode) { - delay_us(dev->state->part->reset_latency_ms * 1000); - } else { - psleep(dev->state->part->reset_latency_ms); - } - - // Return the flash to Quad SPI mode, all our commands are quad-spi and it'll just cause - // problems/bugs for someone if it comes back in single spi mode - qspi_indirect_write_no_addr_1line(dev->qspi, dev->state->part->instructions.enter_quad_mode); - - if (!coredump_mode) { - prv_check_whoami(dev); - } - - qspi_release(dev->qspi); -} - -status_t qspi_flash_is_erase_complete(QSPIFlash *dev) { - qspi_use(dev->qspi); - - uint8_t status_reg; - uint8_t flag_status_reg; - prv_read_register(dev, dev->state->part->instructions.rdsr1, &status_reg, 1); - prv_read_register(dev, dev->state->part->instructions.rdsr2, &flag_status_reg, 1); - - qspi_release(dev->qspi); - - if (status_reg & dev->state->part->status_bit_masks.busy) { - return E_BUSY; - } else if (flag_status_reg & dev->state->part->flag_status_bit_masks.erase_suspend) { - return E_AGAIN; - } else { - return S_SUCCESS; - } -} - -status_t qspi_flash_erase_begin(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - uint8_t instruction; - if (is_subsector) { - instruction = dev->state->part->instructions.erase_sector_4k; - } else { - instruction = dev->state->part->instructions.erase_block_64k; - } - - qspi_use(dev->qspi); - prv_write_enable(dev); - qspi_indirect_write(dev->qspi, instruction, addr, NULL, 0); - // wait for busy to be set indicating the erase has started - const uint32_t busy_timeout_us = 500; - const bool result = - qspi_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.busy, true /* set */, busy_timeout_us); - qspi_release(dev->qspi); - - return result ? S_SUCCESS : E_ERROR; -} - -status_t qspi_flash_erase_suspend(QSPIFlash *dev, uint32_t addr) { - qspi_use(dev->qspi); - - uint8_t status_reg; - prv_read_register(dev, dev->state->part->instructions.rdsr1, &status_reg, 1); - if (!(status_reg & dev->state->part->status_bit_masks.busy)) { - // no erase in progress - qspi_release(dev->qspi); - return S_NO_ACTION_REQUIRED; - } - - prv_write_cmd_no_addr(dev, dev->state->part->instructions.erase_suspend); - - qspi_release(dev->qspi); - - if (dev->state->part->suspend_to_read_latency_us) { - delay_us(dev->state->part->suspend_to_read_latency_us); - } - - return S_SUCCESS; -} - -void qspi_flash_erase_resume(QSPIFlash *dev, uint32_t addr) { - qspi_use(dev->qspi); - prv_write_cmd_no_addr(dev, dev->state->part->instructions.erase_resume); - // wait for the erase_suspend bit to be cleared - qspi_poll_bit(dev->qspi, dev->state->part->instructions.rdsr2, - dev->state->part->flag_status_bit_masks.erase_suspend, false /* !set */, - QSPI_NO_TIMEOUT); - qspi_release(dev->qspi); -} - -static void prv_get_fast_read_params(QSPIFlash *dev, uint8_t *instruction, uint8_t *dummy_cycles, - bool *is_ddr) { - if (dev->state->fast_read_ddr_enabled) { - *instruction = dev->state->part->instructions.fast_read_ddr; - *dummy_cycles = dev->state->part->dummy_cycles.fast_read_ddr; - *is_ddr = true; - } else { - *instruction = dev->state->part->instructions.fast_read; - *dummy_cycles = dev->state->part->dummy_cycles.fast_read; - *is_ddr = false; - } -} - -static void prv_read_mmap_with_params(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length, - uint8_t instruction, uint8_t dummy_cycles, bool is_ddr) { - qspi_mmap_start(dev->qspi, instruction, addr, dummy_cycles, length, is_ddr); - - // Point the buffer at the QSPI region - memcpy(buffer, (uint32_t *)(QSPI_MMAP_BASE_ADDRESS + addr), length); - - // stop memory mapped mode - qspi_mmap_stop(dev->qspi); -} - -static void prv_read_mmap(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length) { - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - - prv_read_mmap_with_params(dev, addr, buffer, length, instruction, dummy_cycles, is_ddr); -} - -void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length) { - // TODO: Figure out what thresholds we should use when switching between memory mapping, DMA, & - // polling PBL-37438 - bool should_use_dma = length > 128 && !dev->state->coredump_mode; - bool should_use_memmap = length > 128; - -#if QSPI_DMA_DISABLE - // Known issues with some platforms, see PBL-37278 as an example - should_use_dma = false; -#endif - -#if TARGET_QEMU - // QEMU doesn't yet support DMA or memory-mapping - should_use_dma = should_use_memmap = false; -#endif - - qspi_use(dev->qspi); - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - if (should_use_dma) { - qspi_indirect_read_dma(dev->qspi, instruction, addr, dummy_cycles, buffer, length, is_ddr); - } else if (should_use_memmap) { - prv_read_mmap_with_params(dev, addr, buffer, length, instruction, dummy_cycles, is_ddr); - } else { - qspi_indirect_read(dev->qspi, instruction, addr, dummy_cycles, buffer, length, is_ddr); - } - qspi_release(dev->qspi); -} - -int qspi_flash_write_page_begin(QSPIFlash *dev, const void *buffer, uint32_t addr, - uint32_t length) { - const uint32_t offset_in_page = addr % PAGE_SIZE_BYTES; - const uint32_t bytes_in_page = MIN(PAGE_SIZE_BYTES - offset_in_page, length); - - qspi_use(dev->qspi); - prv_write_enable(dev); - qspi_indirect_write(dev->qspi, dev->state->part->instructions.pp, addr, buffer, bytes_in_page); - qspi_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.busy, false /* !set */, QSPI_NO_TIMEOUT); - qspi_release(dev->qspi); - - return bytes_in_page; -} - -status_t qspi_flash_get_write_status(QSPIFlash *dev) { - qspi_use(dev->qspi); - uint8_t status_reg; - prv_read_register(dev, dev->state->part->instructions.rdsr1, &status_reg, 1); - qspi_release(dev->qspi); - return (status_reg & dev->state->part->status_bit_masks.busy) ? E_BUSY : S_SUCCESS; -} - -void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active) { - qspi_use(dev->qspi); - uint8_t instruction; - uint32_t delay; - if (active) { - instruction = dev->state->part->instructions.enter_low_power; - delay = dev->state->part->standby_to_low_power_latency_us; - } else { - instruction = dev->state->part->instructions.exit_low_power; - delay = dev->state->part->low_power_to_standby_latency_us; - } - prv_write_cmd_no_addr(dev, instruction); - qspi_release(dev->qspi); - if (delay) { - delay_us(delay); - } -} - -#if TARGET_QEMU -// While this works with normal hardware, it has a large stack requirment and I can't -// see a compelling reason to use it over the mmap blank check variant -static bool prv_blank_check_poll(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - const uint32_t size_bytes = is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES; - const uint32_t BUF_SIZE_BYTES = 128; - const uint32_t BUF_SIZE_WORDS = BUF_SIZE_BYTES / sizeof(uint32_t); - uint32_t buffer[BUF_SIZE_WORDS]; - for (uint32_t offset = 0; offset < size_bytes; offset += BUF_SIZE_BYTES) { - flash_impl_read_sync(buffer, addr + offset, BUF_SIZE_BYTES); - for (uint32_t i = 0; i < BUF_SIZE_WORDS; ++i) { - if (buffer[i] != FLASH_RESET_WORD_VALUE) { - return false; - } - } - } - return true; -} -#endif - -static bool prv_blank_check_mmap(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - const uint32_t size_bytes = is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES; - bool result = true; - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - qspi_mmap_start(dev->qspi, instruction, addr, dummy_cycles, size_bytes, is_ddr); - - // Point the buffer at the QSPI region - uint32_t const volatile *const buffer = (uint32_t *)(QSPI_MMAP_BASE_ADDRESS + addr); - uint32_t size_words = size_bytes / sizeof(uint32_t); - for (uint32_t i = 0; i < size_words; ++i) { - if (buffer[i] != FLASH_RESET_WORD_VALUE) { - result = false; - break; - } - } - - // stop memory mapped mode - qspi_mmap_stop(dev->qspi); - return result; -} -status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - qspi_use(dev->qspi); -#if TARGET_QEMU - // QEMU doesn't support memory-mapping the FLASH - const bool result = prv_blank_check_poll(dev, addr, is_subsector); -#else - const bool result = prv_blank_check_mmap(dev, addr, is_subsector); -#endif - qspi_release(dev->qspi); - return result ? S_TRUE : S_FALSE; -} - -void qspi_flash_ll_set_register_bits(QSPIFlash *dev, uint8_t read_instruction, - uint8_t write_instruction, uint8_t value, uint8_t mask) { - // make sure we're not trying to set any bits not within the mask - PBL_ASSERTN((value & mask) == value); - - qspi_use(dev->qspi); - - // first read the register - uint8_t reg_value; - prv_read_register(dev, read_instruction, ®_value, 1); - - // set the desired bits - reg_value = (reg_value & ~mask) | value; - - // enable writing and write the register value - prv_write_cmd_no_addr(dev, dev->state->part->instructions.write_enable); - qspi_indirect_write_no_addr(dev->qspi, write_instruction, ®_value, 1); - - qspi_release(dev->qspi); -} - -static bool prv_protection_is_enabled(QSPIFlash *dev) { - uint8_t status; - prv_read_register(dev, dev->state->part->instructions.read_protection_status, &status, 1); - return (status & dev->state->part->block_lock.protection_enabled_mask); -} - -status_t qspi_flash_write_protection_enable(QSPIFlash *dev) { -#if TARGET_QEMU - return S_NO_ACTION_REQUIRED; -#endif - qspi_use(dev->qspi); - prv_write_enable(dev); - const bool already_enabled = prv_protection_is_enabled(dev); - if (already_enabled == false) { - PBL_LOG(LOG_LEVEL_INFO, "Enabling flash protection"); - // Enable write protection - prv_write_cmd_no_addr(dev, dev->state->part->instructions.write_protection_enable); - - // Poll busy status until done - qspi_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.busy, false /* !set */, QSPI_NO_TIMEOUT); - } - qspi_release(dev->qspi); - - return (already_enabled) ? S_NO_ACTION_REQUIRED : S_SUCCESS; -} - -status_t qspi_flash_lock_sector(QSPIFlash *dev, uint32_t addr) { -#if TARGET_QEMU - return S_SUCCESS; -#endif - qspi_use(dev->qspi); - - prv_write_enable(dev); - - // Lock or unlock the sector - const uint8_t instruction = dev->state->part->instructions.block_lock; - if (dev->state->part->block_lock.has_lock_data) { - qspi_indirect_write(dev->qspi, instruction, addr, &dev->state->part->block_lock.lock_data, 1); - } else { - qspi_indirect_write(dev->qspi, instruction, addr, NULL, 0); - } - - // Poll busy status until done - qspi_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.busy, false /* !set */, QSPI_NO_TIMEOUT); - - // Read lock status - uint8_t status; - qspi_indirect_read(dev->qspi, dev->state->part->instructions.block_lock_status, addr, 0, &status, - sizeof(status), false); - - qspi_release(dev->qspi); - - return (status == dev->state->part->block_lock.locked_check) ? S_SUCCESS : E_ERROR; -} - -status_t qspi_flash_unlock_all(QSPIFlash *dev) { -#if TARGET_QEMU - return S_SUCCESS; -#endif - qspi_use(dev->qspi); - prv_write_enable(dev); - prv_write_cmd_no_addr(dev, dev->state->part->instructions.block_unlock_all); - qspi_release(dev->qspi); - return S_SUCCESS; -} - -#if !RELEASE -#include "console/prompt.h" -#include "drivers/flash.h" -#include "kernel/pbl_malloc.h" -#include "system/profiler.h" -#include "util/size.h" - -static bool prv_flash_read_verify(QSPIFlash *dev, int size, int offset) { - bool success = true; - char *buffer_dma_ptr = kernel_malloc_check(size + offset + 3); - char *buffer_pol = kernel_malloc_check(size + 3); - char *buffer_mmap = kernel_malloc_check(size + 3); - - char *buffer_dma = buffer_dma_ptr + offset; - - // The buffers need to be different, so when compared against each other we can make - // sure the write functions wrote the same thing. - memset(buffer_dma, 0xA5, size); - memset(buffer_pol, 0xCC, size); - memset(buffer_mmap, 0x33, size); - - profiler_start(); - prv_read_mmap(dev, 0, buffer_mmap, size); - profiler_stop(); - uint32_t mmap_time = profiler_get_total_duration(true); - - profiler_start(); - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - qspi_indirect_read_dma(dev->qspi, instruction, 0, dummy_cycles, buffer_dma, size, is_ddr); - profiler_stop(); - uint32_t dma_time = profiler_get_total_duration(true); - - profiler_start(); - qspi_indirect_read(dev->qspi, instruction, 0, dummy_cycles, buffer_pol, size, is_ddr); - profiler_stop(); - uint32_t pol_time = profiler_get_total_duration(true); - - if (memcmp(buffer_dma, buffer_pol, size) != 0) { - prompt_send_response("FAILURE: buffer_dma != buffer_pol"); - success = false; - } - if (memcmp(buffer_dma, buffer_mmap, size) != 0) { - prompt_send_response("FAILURE: buffer_dma != buffer_mmap"); - success = false; - } - - const int buf_size = 64; - char buf[buf_size]; - prompt_send_response_fmt(buf, buf_size, - "Size: %d DMA: %" PRIu32 " POL: %" PRIu32 " MMP: %" PRIu32, size, - dma_time, pol_time, mmap_time); - - kernel_free(buffer_dma_ptr); - kernel_free(buffer_pol); - kernel_free(buffer_mmap); - - return success; -} - -struct FlashReadTestValues { - int size; - int offset; -}; - -const struct FlashReadTestValues FLASH_READ_TEST_TABLE[] = { - {.size = 1024, .offset = 0}, {.size = 1025, .offset = 0}, {.size = 1026, .offset = 0}, - {.size = 1027, .offset = 0}, {.size = 1024, .offset = 1}, {.size = 1025, .offset = 2}, - {.size = 1026, .offset = 3}, {.size = 4, .offset = 0}, {.size = 20, .offset = 0}, - {.size = 60, .offset = 0}, {.size = 127, .offset = 0}, {.size = 128, .offset = 0}, -}; - -void command_flash_apicheck(const char *len_str) { - QSPIFlash *dev = QSPI_FLASH; - const int buf_size = 64; - char buf[buf_size]; - int failures = 0; - int passes = 0; - - profiler_init(); - - prompt_send_response("Check whoami"); - if (!qspi_flash_check_whoami(dev)) { - ++failures; - prompt_send_response("ERROR: Who am I failed"); - } else { - ++passes; - } - - prompt_send_response("Enter low power mode"); - flash_impl_enter_low_power_mode(); - - // WHOAMI should fail in low-power mode - prompt_send_response("Check whoami, should fail in low power mode"); - if (qspi_flash_check_whoami(dev)) { - ++failures; - prompt_send_response("ERROR: Who am I failed"); - } else { - ++passes; - } - - prompt_send_response("Exit low power mode"); - flash_impl_exit_low_power_mode(); - - prompt_send_response("Start flash_read_verify test"); - qspi_use(dev->qspi); - - const int final_size = atoi(len_str); - - // If size is 0 run through a pre-defined table - if (final_size == 0) { - for (unsigned int i = 0; i < ARRAY_LENGTH(FLASH_READ_TEST_TABLE); ++i) { - bool result = prv_flash_read_verify(dev, FLASH_READ_TEST_TABLE[i].size, - FLASH_READ_TEST_TABLE[i].offset); - if (!result) { - ++failures; - } else { - ++passes; - } - } - - } else { - if (prv_flash_read_verify(dev, final_size, 3)) { - ++passes; - } else { - ++failures; - prompt_send_response("ERROR: flash_read_verify failed"); - } - } - - qspi_release(dev->qspi); - - bool was_busy = false; - - // write a few bytes to the sector we're going to erase so it's not empty - uint8_t dummy_data = 0x55; - flash_write_bytes(&dummy_data, FLASH_REGION_FIRMWARE_DEST_BEGIN, sizeof(dummy_data)); - profiler_start(); - status_t result = flash_impl_erase_sector_begin(FLASH_REGION_FIRMWARE_DEST_BEGIN); - flash_impl_get_erase_status(); - if (result == S_SUCCESS) { - while (flash_impl_get_erase_status() == E_BUSY) { - was_busy = true; - } - } - profiler_stop(); - uint32_t duration = profiler_get_total_duration(true); - prompt_send_response_fmt(buf, buf_size, "Erase took: %" PRIu32, duration); - - // Fash erases take at least ~100ms, if we're too short we probably didn't erase - const uint32_t min_erase_time = 10000; - if (result != S_SUCCESS) { - ++failures; - prompt_send_response_fmt(buf, buf_size, "FAILURE: erase did not report success %" PRIi32, - result); - } else if (was_busy == false) { - ++failures; - prompt_send_response("FAILURE: Flash never became busy, but we should be busy for 300ms."); - prompt_send_response("FAILURE: Flash probably never did an erase."); - } else if (duration < min_erase_time) { - ++failures; - prompt_send_response("FAILURE: Flash erase completed way to quickly to have succeeded."); - } else { - ++passes; - } - - // must call blank_check_poll by hand, otherwise we'll get the dma version - profiler_start(); - qspi_use(dev->qspi); - bool is_blank = - qspi_flash_blank_check(QSPI_FLASH, FLASH_REGION_FIRMWARE_DEST_BEGIN, SUBSECTOR_SIZE_BYTES); - qspi_release(dev->qspi); - profiler_stop(); - - uint32_t blank = profiler_get_total_duration(true); - prompt_send_response_fmt(buf, buf_size, "Sector blank check via read took: %" PRIu32, blank); - if (is_blank != S_TRUE) { - ++failures; - prompt_send_response("FAILURE: sector not blank!?!"); - } else { - ++passes; - } - - profiler_start(); - is_blank = flash_impl_blank_check_subsector(FLASH_REGION_FIRMWARE_DEST_BEGIN); - profiler_stop(); - - blank = profiler_get_total_duration(true); - prompt_send_response_fmt(buf, buf_size, "Subsector blank check via read took: %" PRIu32, blank); - if (is_blank != S_TRUE) { - ++failures; - prompt_send_response("FAILURE: sector not blank!?!"); - } else { - ++passes; - } - - if (failures == 0) { - prompt_send_response_fmt(buf, buf_size, "SUCCESS: run %d tests and all passeed", passes); - } else { - prompt_send_response_fmt(buf, buf_size, "FAILED: run %d tests and %d failed", passes + failures, - failures); - } -} - -#endif - -#if RECOVERY_FW -#include "console/prompt.h" -#include "drivers/flash.h" - -#define SIGNAL_TEST_MAGIC_PATTERN (0xA5) -#define TEST_BUFFER_SIZE (1024) -static uint8_t s_test_buffer[TEST_BUFFER_SIZE]; -static const uint32_t s_test_addr = FLASH_REGION_FIRMWARE_DEST_END - SECTOR_SIZE_BYTES; -static bool s_signal_test_initialized; - -void command_flash_signal_test_init(void) { - // just test one sector, which is probably less than the size of the region - - // erase the sector - flash_erase_sector_blocking(s_test_addr); - - // set the contents of the sector such that we will end up reading alternating 1s and 0s - memset(s_test_buffer, SIGNAL_TEST_MAGIC_PATTERN, sizeof(s_test_buffer)); - flash_write_bytes(s_test_buffer, s_test_addr, sizeof(s_test_buffer)); - - QSPIFlash *dev = QSPI_FLASH; - // Ensure DDR is disabled for write check - prv_set_fast_read_ddr_enabled(dev, false); - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - PBL_ASSERTN(!is_ddr); - - qspi_use(dev->qspi); - qspi_indirect_read(dev->qspi, instruction, s_test_addr, dummy_cycles, s_test_buffer, - sizeof(s_test_buffer), is_ddr); - - prv_set_fast_read_ddr_enabled(dev, dev->default_fast_read_ddr_enabled); - qspi_release(dev->qspi); - - bool success = true; - for (uint32_t i = 0; i < sizeof(s_test_buffer); ++i) { - if (s_test_buffer[i] != SIGNAL_TEST_MAGIC_PATTERN) { - success = false; - break; - } - } - - if (success) { - prompt_send_response("Done!"); - s_signal_test_initialized = true; - } else { - prompt_send_response("ERROR: Data read (SDR mode) did not match data written!"); - } -} - -void command_flash_signal_test_run(void) { - if (!s_signal_test_initialized) { - prompt_send_response("ERROR: 'flash signal test init' must be run first!"); - return; - } - - QSPIFlash *dev = QSPI_FLASH; - qspi_use(dev->qspi); - - // set to DDR - prv_set_fast_read_ddr_enabled(dev, true); - - // issue the read - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - PBL_ASSERTN(is_ddr); - qspi_indirect_read(dev->qspi, instruction, s_test_addr, dummy_cycles, s_test_buffer, - sizeof(s_test_buffer), is_ddr); - - bool success = true; - for (uint32_t i = 0; i < sizeof(s_test_buffer); ++i) { - if (s_test_buffer[i] != SIGNAL_TEST_MAGIC_PATTERN) { - success = false; - break; - } - } - - // set back to default mode - prv_set_fast_read_ddr_enabled(dev, dev->default_fast_read_ddr_enabled); - qspi_release(dev->qspi); - - if (success) { - prompt_send_response("Ok"); - } else { - prompt_send_response("ERROR: Read value didn't match!"); - } -} -#endif diff --git a/src/fw/drivers/flash/qspi_flash.h b/src/fw/drivers/flash/qspi_flash.h index e84cf670ce..317913cc11 100644 --- a/src/fw/drivers/flash/qspi_flash.h +++ b/src/fw/drivers/flash/qspi_flash.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -86,8 +73,8 @@ status_t qspi_flash_unlock_all(QSPIFlash *dev); //! Read security register status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_t *val); -//! Check if the security registers are locked -status_t qspi_flash_security_registers_are_locked(QSPIFlash *dev, bool *locked); +//! Check if the given security registers is locked +status_t qspi_flash_security_register_is_locked(QSPIFlash *dev, uint32_t address, bool *locked); //! Erase security register status_t qspi_flash_erase_security_register(QSPIFlash *dev, uint32_t addr); @@ -98,8 +85,8 @@ status_t qspi_flash_write_security_register(QSPIFlash *dev, uint32_t addr, uint8 //! Obtain security registers information const FlashSecurityRegisters *qspi_flash_security_registers_info(QSPIFlash *dev); -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW //! Lock security registers //! @warning This is a one time operation and will permanently lock the security registers. -status_t qspi_flash_lock_security_registers(QSPIFlash *dev); -#endif // RECOVERY_FW +status_t qspi_flash_lock_security_register(QSPIFlash *dev, uint32_t address); +#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/drivers/flash/qspi_flash_definitions.h b/src/fw/drivers/flash/qspi_flash_definitions.h index bfb37a52d1..8ec67ec4a9 100644 --- a/src/fw/drivers/flash/qspi_flash_definitions.h +++ b/src/fw/drivers/flash/qspi_flash_definitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/flash/qspi_flash_part_definitions.h b/src/fw/drivers/flash/qspi_flash_part_definitions.h index b62b086388..4fae76748b 100644 --- a/src/fw/drivers/flash/qspi_flash_part_definitions.h +++ b/src/fw/drivers/flash/qspi_flash_part_definitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/flash/spansion_s29vs.c b/src/fw/drivers/flash/spansion_s29vs.c deleted file mode 100644 index cb8eec26a0..0000000000 --- a/src/fw/drivers/flash/spansion_s29vs.c +++ /dev/null @@ -1,898 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "drivers/flash/flash_impl.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "flash_region/flash_region.h" -#include "kernel/util/delay.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/size.h" -#include "util/units.h" - -#define STM32F4_COMPATIBLE -#include - -//! This is the memory mapped region that's mapped to the parallel flash. -static const uintptr_t FMC_BANK_1_BASE_ADDRESS = 0x60000000; - -//! This is the unit that we use for writing -static const uint32_t PAGE_SIZE_BYTES = 64; - -//! Different commands we can send to the flash -typedef enum S29VSCommand { - S29VSCommand_WriteBufferLoad = 0x25, - S29VSCommand_BufferToFlash = 0x29, - S29VSCommand_EraseResume = 0x30, - S29VSCommand_SectorBlank = 0x33, - S29VSCommand_SectorLock = 0x60, - S29VSCommand_SectorLockRangeArg = 0x61, - S29VSCommand_ReadStatusRegister = 0x70, - S29VSCommand_ClearStatusRegister = 0x71, - S29VSCommand_EraseSetup = 0x80, - S29VSCommand_DeviceIDEntry = 0x90, - S29VSCommand_EraseSuspend = 0xB0, - S29VSCommand_ConfigureRegisterEntry = 0xD0, - S29VSCommand_SoftwareReset = 0xF0 -} S29VSCommand; - -//! Arguments to the S29VSCommand_EraseSetup command -typedef enum S29VSCommandEraseAguments { - S29VSCommandEraseAguments_ChipErase = 0x10, - S29VSCommandEraseAguments_SectorErase = 0x30 -} S29VSCommandEraseAguments; - -//! The bitset stored in the status register, see prv_read_status_register -typedef enum S29VSStatusBit { - S29VSStatusBit_BankStatus = (1 << 0), - S29VSStatusBit_SectorLockStatus = (1 << 1), - S29VSStatusBit_ProgramSuspended = (1 << 2), - // Bit 3 is reserved - S29VSStatusBit_ProgramStatus = (1 << 4), - S29VSStatusBit_EraseStatus = (1 << 5), - S29VSStatusBit_EraseSuspended = (1 << 6), - S29VSStatusBit_DeviceReady = (1 << 7), -} S29VSStatusBit; -static const uint16_t SPANSION_MANUFACTURER_ID = 0x01; -static const uint16_t MACRONIX_MANUFACTURER_ID = 0xc2; -static const GPIO_InitTypeDef s_default_at_flash_cfg = {.GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = GPIO_Speed_100MHz, - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL}; - -static void prv_issue_command_argument(FlashAddress sector_address, uint16_t cmd_arg); -static void prv_issue_command(FlashAddress sector_address, S29VSCommand cmd); - -// puts gpios into or out of analog to save power when idle/in use respectively -static void prv_flash_idle_gpios(bool enable_gpios) { - static bool gpios_idled = false; - if (gpios_idled == enable_gpios) { - return; - } - gpios_idled = enable_gpios; - - gpio_use(GPIOB); - gpio_use(GPIOD); - gpio_use(GPIOE); - - GPIO_InitTypeDef gpio_init; - if (enable_gpios) { - gpio_init = s_default_at_flash_cfg; - } else { - gpio_init = (GPIO_InitTypeDef){ - .GPIO_Mode = GPIO_Mode_AN, .GPIO_Speed = GPIO_Speed_2MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL}; - } - - // leave RESET_N and CE: they need to retain their state - // Configure the rest as analog inputs to save as much power as possible - // D2 - Reset - GPIO Reset line - // D7 - FMC CE - FMC Chip Enable - gpio_init.GPIO_Pin = GPIO_Pin_7; - GPIO_Init(GPIOB, &gpio_init); - - gpio_init.GPIO_Pin = GPIO_Pin_All & (~GPIO_Pin_2) & (~GPIO_Pin_7); - GPIO_Init(GPIOD, &gpio_init); - - gpio_init.GPIO_Pin = GPIO_Pin_All & (~GPIO_Pin_0) & (~GPIO_Pin_1); - GPIO_Init(GPIOE, &gpio_init); - - gpio_release(GPIOE); - gpio_release(GPIOD); - gpio_release(GPIOB); -} - -static uint32_t s_num_flash_uses = 0; - -void flash_impl_use(void) { - if (s_num_flash_uses == 0) { - periph_config_enable(FMC_Bank1, RCC_AHB3Periph_FMC); // FIXME - prv_flash_idle_gpios(true); - } - s_num_flash_uses++; -} - -void flash_impl_release_many(uint32_t num_locks) { - PBL_ASSERTN(s_num_flash_uses >= num_locks); - s_num_flash_uses -= num_locks; - if (s_num_flash_uses == 0) { - periph_config_disable(FMC_Bank1, RCC_AHB3Periph_FMC); // FIXME - } -} - -void flash_impl_release(void) { flash_impl_release_many(1); } - -static uint16_t flash_s29vs_read_short(FlashAddress addr) { - return *((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + addr)); -} - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - if (addr < BOTTOM_BOOT_REGION_END) { - return addr & ~(BOTTOM_BOOT_SECTOR_SIZE - 1); - } - - return addr & ~(SECTOR_SIZE_BYTES - 1); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return flash_impl_get_sector_base_address(addr); -} - -static uint8_t prv_read_status_register(FlashAddress sector_base_addr) { - prv_issue_command(sector_base_addr, S29VSCommand_ReadStatusRegister); - return flash_s29vs_read_short(sector_base_addr); -} - -static uint8_t prv_poll_for_ready(FlashAddress sector_base_addr) { - // TODO: We should probably just assert if this takes too long - uint8_t status; - while (((status = prv_read_status_register(sector_base_addr)) & S29VSStatusBit_DeviceReady) == - 0) { - delay_us(10); - } - - return (status); -} - -//! Issue the second part of a two-cycle command. This is not merged with the -//! prv_issue_command as not all commands have an argument. -//! -//! @param sector_address The address of the start of the sector to write the command to. -//! @param cmd_arg The command argument to write. -static void prv_issue_command_argument(FlashAddress sector_address, uint16_t cmd_arg) { - // The offset in the sector we write the second part of commands to. Note that this is a 16-bit - // word aligned address as opposed to a byte address. - static const uint32_t COMMAND_ARGUMENT_ADDRESS = 0x2AA; - - ((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + sector_address))[COMMAND_ARGUMENT_ADDRESS] = cmd_arg; -} - -//! @param sector_address The address of the start of the sector to write the command to. -//! @param cmd The command to write. -static void prv_issue_command(FlashAddress sector_address, S29VSCommand cmd) { - // The offset in the sector we write the first part of commands to. Note that this is a 16-bit - // word aligned address as opposed to a byte address. - static const uint32_t COMMAND_ADDRESS = 0x555; - - ((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + sector_address))[COMMAND_ADDRESS] = cmd; -} - -static void prv_software_reset(void) { prv_issue_command(0, S29VSCommand_SoftwareReset); } - -// Note: If this command has been executed at least once, all sectors are -// locked. They then must be unlocked before and relocked after each program -// operation (i.e write or erase). The chip only allows for one sector to be -// unlocked at any given time. For sector ranges which have been protected using -// the "Sector Lock Range Command", this function will have no effect. -static void prv_allow_write_if_sector_is_not_protected(bool lock, uint32_t sector_addr) { - prv_issue_command(0, S29VSCommand_SectorLock); - prv_issue_command_argument(0, S29VSCommand_SectorLock); - - int lock_flag = (lock ? 0 : 1) << 7; // set A6 to 0 to lock and 1 to unlock - ((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + sector_addr + lock_flag))[0] = - S29VSCommand_SectorLock; -} - -static uint16_t prv_read_manufacturer_id(void) { - // Issue the DeviceIDEntry command to change to the ID-CFI Address Map. This means that reading - // from the bank will give us ID-CFI information instead of the normal flash contents. See - // Table 11.2 (ID/CFI Data) for all the content you can read here. Reset the state afterwards to - // return to the default address map. - - flash_impl_use(); - prv_issue_command(0, S29VSCommand_DeviceIDEntry); - uint16_t result = flash_s29vs_read_short(0x0); - prv_software_reset(); - flash_impl_release(); - return result; -} - -static uint16_t prv_read_configuration_register(void) { - prv_issue_command(0, S29VSCommand_ConfigureRegisterEntry); - uint16_t result = flash_s29vs_read_short(0x0); - prv_software_reset(); - return result; -} - -static void prv_write_configuration_register(uint16_t data) { - // See section 5.8.1 of data sheet for command sequence - prv_issue_command(0, S29VSCommand_ConfigureRegisterEntry); - - // Cycle 1: SA+Address 555h & Data 25h - // Cycle 2: SA+Address 2AAh & Data 00h - // Cycle 3: SA+Address X00h & PD - // Cycle 4: SA+ Address 555h & Data 29h - prv_issue_command(0, S29VSCommand_WriteBufferLoad); - prv_issue_command_argument(0, 0); - ((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS))[0] = data; - prv_issue_command(0, S29VSCommand_BufferToFlash); - - prv_software_reset(); -} - -// Use the "Sector Lock Range Command" (section 8.2 of data sheet) to block -// writes or erases to the PRF image residing on the flash. The only way to undo -// this is to issue a HW reset or pull power -static void prv_flash_protect_range(uint32_t start_sector, uint32_t end_sector) { - PBL_ASSERTN(start_sector <= end_sector); - - flash_impl_use(); - - prv_issue_command(0, S29VSCommand_SectorLock); - prv_issue_command_argument(0, S29VSCommand_SectorLock); - - start_sector = flash_impl_get_sector_base_address(start_sector); - end_sector = flash_impl_get_sector_base_address(end_sector); - - ((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + start_sector))[0] = S29VSCommand_SectorLockRangeArg; - ((__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + end_sector))[0] = S29VSCommand_SectorLockRangeArg; - - flash_impl_release(); -} - -void flash_s29vs_hw_init(void) { - // Configure the reset pin (D2) - GPIO_InitTypeDef gpio_init = {.GPIO_Pin = GPIO_Pin_2, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = GPIO_Speed_100MHz, - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL}; - GPIO_Init(GPIOD, &gpio_init); - - GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET); - - // Configure pins relating to the FMC peripheral (30 pins!) - - // B7 - FMC AVD - FMC Address Valid aka Latch - // D0-D1, D8-D15, E2-15 - FMC A, AD - FMC Address and Address/Data lines - // D2 - Reset - GPIO Reset line - // D3 - FMC CLK - // D4 - FMC OE - FMC Output Enable - // D5 - FMC WE - FMC Write Enable - // D6 - FMC RDY - FMC Ready line - // D7 - FMC CE - FMC Chip Enable - - GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_FMC); - gpio_init = s_default_at_flash_cfg; - gpio_init.GPIO_Pin = GPIO_Pin_7; - GPIO_Init(GPIOB, &gpio_init); - - for (uint8_t pin_source = 0; pin_source < 16; ++pin_source) { - if (pin_source == 2) { - continue; - } - GPIO_PinAFConfig(GPIOD, pin_source, GPIO_AF_FMC); - } - gpio_init.GPIO_Pin = GPIO_Pin_All & (~GPIO_Pin_2); - GPIO_Init(GPIOD, &gpio_init); - - for (uint8_t pin_source = 2; pin_source < 16; ++pin_source) { - GPIO_PinAFConfig(GPIOE, pin_source, GPIO_AF_FMC); - } - gpio_init.GPIO_Pin = GPIO_Pin_All & (~GPIO_Pin_0) & (~GPIO_Pin_1); - GPIO_Init(GPIOE, &gpio_init); - - // We have configured the pins, lets perform a full HW reset to put the chip - // in a good state - GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_RESET); - delay_us(10); // only needs to be 50ns according to data sheet - GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET); - delay_us(30); // need 200ns + 10us before CE can be pulled low - - flash_impl_set_burst_mode(false); -} - -static void prv_flash_reset(void) { - s_num_flash_uses = 0; - gpio_use(GPIOB); - gpio_use(GPIOD); - gpio_use(GPIOE); - flash_impl_use(); - - flash_s29vs_hw_init(); - - flash_impl_release(); - gpio_release(GPIOE); - gpio_release(GPIOD); - gpio_release(GPIOB); -} - -void flash_impl_enable_write_protection(void) {} - -// Protects start_sector - end_sector, inclusive, from any kind of program -// operation -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { - prv_flash_reset(); - prv_flash_protect_range(start_sector, end_sector); - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - // The only way to undo sector protection is to pull power from the chip or - // issue a hardware reset - prv_flash_reset(); - return S_SUCCESS; -} - -status_t flash_impl_init(bool coredump_mode) { - // Don't need to do anything to enable coredump mode. - - prv_flash_reset(); - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { - flash_impl_use(); - uint8_t status = prv_read_status_register(0); - flash_impl_release(); - - if ((status & S29VSStatusBit_DeviceReady) == 0) return E_BUSY; - if ((status & S29VSStatusBit_EraseSuspended) != 0) return E_AGAIN; - if ((status & S29VSStatusBit_EraseStatus) != 0) return E_ERROR; - return S_SUCCESS; -} - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - return flash_impl_erase_sector_begin(subsector_addr); -} - -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - status_t result = E_UNKNOWN; - - // FIXME: We should just assert that the address is already aligned. If - // someone is depending on this behaviour without already knowing the range - // that's being erased they're going to have a bad time. This will probably - // cause some client fallout though, so tackle this later. - sector_addr = flash_impl_get_sector_base_address(sector_addr); - - flash_impl_use(); - prv_issue_command(sector_addr, S29VSCommand_ClearStatusRegister); - - // Some sanity checks - { - status_t error = S_SUCCESS; - const uint8_t sr = prv_read_status_register(sector_addr); - if ((sr & S29VSStatusBit_DeviceReady) == 0) { - // Another operation is already in progress. - error = E_BUSY; - } else if (sr & S29VSStatusBit_EraseSuspended) { - // Cannot program while another program operation is suspended. - error = E_INVALID_OPERATION; - } - if (FAILED(error)) { - result = error; - goto done; - } - } - - prv_allow_write_if_sector_is_not_protected(false, sector_addr); - - prv_issue_command(sector_addr, S29VSCommand_EraseSetup); - prv_issue_command_argument(sector_addr, S29VSCommandEraseAguments_SectorErase); - prv_allow_write_if_sector_is_not_protected(true, sector_addr); - - // Check the status register to make sure that the erase has started. - const uint8_t sr = prv_read_status_register(sector_addr); - if ((sr & S29VSStatusBit_DeviceReady) == 0) { - // Program or erase operation in progress. Is it in the current bank? - result = ((sr & S29VSStatusBit_BankStatus) == 0) ? S_SUCCESS : E_BUSY; - } else { - // Operation hasn't started. Something is wrong. - if (sr & S29VSStatusBit_SectorLockStatus) { - // Sector is write-protected. - result = E_INVALID_OPERATION; - } else if (sr & S29VSStatusBit_EraseStatus) { - // Erase failed for some reason. - result = E_ERROR; - } else { - // The erase has either completed in the time between starting the erase - // and polling the status register, or the erase was never started. The - // former case could be due to a context switch at the worst time and - // subsequent task starvation, or being run in QEMU. The latter could be - // due to a software bug or hardware failure. It would be possible to tell - // the two situations apart by performing a blank check, but that takes - // more time than a nonblocking erase should require. Let the upper layers - // verify that the erase succeeded if they care about it. - result = S_SUCCESS; - } - } - -done: - flash_impl_release(); - return result; -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - status_t status = E_INTERNAL; - sector_addr = flash_impl_get_sector_base_address(sector_addr); - flash_impl_use(); - const uint8_t sr = prv_read_status_register(sector_addr); - // Is an operation in progress? - if ((sr & S29VSStatusBit_DeviceReady) != 0) { - // No erase in progress to suspend. Maybe the erase completed before this - // call. - status = S_NO_ACTION_REQUIRED; - } else if ((sr & S29VSStatusBit_BankStatus) != 0) { - // Operation is in a different bank than the given address. - status = E_INVALID_ARGUMENT; - } else { - // All clear. - prv_issue_command(sector_addr, S29VSCommand_EraseSuspend); - if (prv_poll_for_ready(sector_addr) & S29VSStatusBit_EraseSuspended) { - status = S_SUCCESS; - } else { - // The erase must have completed between the status register read and - // the EraseSuspend command. - status = S_NO_ACTION_REQUIRED; - } - } - flash_impl_release(); - return status; -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - status_t status = E_INTERNAL; - sector_addr = flash_impl_get_sector_base_address(sector_addr); - flash_impl_use(); - uint8_t sr = prv_read_status_register(sector_addr); - if ((sr & S29VSStatusBit_DeviceReady) != 0 && (sr & S29VSStatusBit_EraseSuspended) != 0) { - prv_issue_command(sector_addr, S29VSCommand_EraseResume); - status = S_SUCCESS; - } else { - // Device busy or no suspended erase to resume. - status = E_INVALID_OPERATION; - } - flash_impl_release(); - return status; -} - -// It is dangerous to leave this built in by default. -#if 0 -status_t flash_impl_erase_bulk_begin(void) { - flash_s29vs_use(); - - prv_issue_command(0, S29VSCommand_EraseSetup); - prv_issue_command_argument(0, S29VSCommandEraseAguments_ChipErase); - - flash_s29vs_release(); -} -#endif - -static void prv_read_words_pio(uint16_t *buffer, uint16_t *flash_data_region, uint32_t num_words) { - for (uint32_t words_read = 0; words_read < num_words; words_read++) { - buffer[words_read] = flash_data_region[words_read]; - } -} - -// Currently this implementation reads halfwords at a time (16-bits). Burst -// length is currently 1 for synchronous reads. This can be optimized in future -// to do larger burst sizes and/or unrolling larger transfer sizes into 32-bit -// reads. -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - uint8_t *buffer = buffer_ptr; - flash_impl_use(); - - uint32_t flash_data_addr = (FMC_BANK_1_BASE_ADDRESS + start_addr); - bool odd_start_addr = ((start_addr % 2) == 1); - uint32_t bytes_read = 0; - - uint16_t *buff_ptr = (uint16_t *)&buffer[bytes_read]; - if (odd_start_addr) { - // read first byte into a temporary buffer but read from source on aligned word boundary - uint16_t temp_buffer = *(__IO uint16_t *)(flash_data_addr - 1); - buffer[bytes_read++] = (uint8_t)((temp_buffer >> 8) & 0xFF); - } - - // At this point, flash_data_addr is now halfword aligned - buff_ptr = (uint16_t *)&buffer[bytes_read]; - bool odd_buff_addr = ((((uint32_t)buff_ptr) % 2) == 1); - if (buffer_size - bytes_read >= 2) { - // if at least one halfword to read - if (!odd_buff_addr) { - // Both flash_data_addr and buffer are aligned - uint32_t num_words = (buffer_size - bytes_read) / 2; - prv_read_words_pio(buff_ptr, (uint16_t *)(flash_data_addr + bytes_read), num_words); - bytes_read += num_words * 2; - } else { - // Not aligned - read into temporary buffer and copy over - __IO uint16_t *flash_data_region = (__IO uint16_t *)(flash_data_addr + bytes_read); - uint32_t num_words = (buffer_size - bytes_read) / 2; - for (uint32_t words_read = 0; words_read < num_words; words_read++) { - uint16_t temp_buffer = flash_data_region[words_read]; - buffer[bytes_read++] = (uint8_t)(temp_buffer & 0xFF); - buffer[bytes_read++] = (uint8_t)((temp_buffer >> 8) & 0xFF); - } - } - } - - buff_ptr = (uint16_t *)&buffer[bytes_read]; - // See if there are any remaining bytes left - at this point - flash_data_addr is still halfword - // aligned - if (buffer_size - bytes_read == 1) { - uint16_t temp_buffer = *(__IO uint16_t *)(flash_data_addr + bytes_read); - buffer[bytes_read++] = (uint8_t)(temp_buffer & 0xFF); - } else if (buffer_size - bytes_read != 0) { - // Should not reach here - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid data length read"); - } - - flash_impl_release(); - - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *vp_buffer, const FlashAddress start_addr, size_t len) { - if (!len) { - return E_INVALID_ARGUMENT; - } - const uint8_t *buffer = vp_buffer; - // Flash write transactions can only write one page at a time, where each - // page is 64 bytes in size. Split up our transactions into pages and then - // write one page. - const uint32_t offset_in_page = start_addr % PAGE_SIZE_BYTES; - const uint32_t bytes_in_page = MIN(PAGE_SIZE_BYTES - offset_in_page, len); - - // We're only allowed to write whole 16-bit words during a write operation. - // Therefore we'll need to pad out our write if it's not perfectly aligned at - // the start or the end. - int num_shorts = bytes_in_page / 2; - - // 4 cases - // Perfectly aligned - No additional writes - // Unaligned start, even length - Need to pad both ends - // Unaligned start, odd length - Pad the start - // Aligned start, odd length - Pad the end - if (start_addr & 0x1 || bytes_in_page & 0x1) { - ++num_shorts; - } - - const FlashAddress sector_addr = flash_impl_get_sector_base_address(start_addr); - - flash_impl_use(); - prv_issue_command(sector_addr, S29VSCommand_ClearStatusRegister); - - // Some sanity checks - { - status_t error = S_SUCCESS; - const uint8_t sr = prv_read_status_register(sector_addr); - if ((sr & S29VSStatusBit_DeviceReady) == 0) { - // Another operation is already in progress. - error = E_BUSY; - } else if (sr & S29VSStatusBit_ProgramSuspended) { - // Cannot program while another program operation is suspended. - error = E_INVALID_OPERATION; - } - if (FAILED(error)) { - flash_impl_release(); - return error; - } - } - - prv_allow_write_if_sector_is_not_protected(false, sector_addr); - prv_issue_command(sector_addr, S29VSCommand_WriteBufferLoad); - prv_issue_command_argument(sector_addr, num_shorts - 1); - - // We're now ready to write the words. Subsequent writes to the sector will - // actually write the data through to the write buffer. - - __IO uint16_t *flash_write_dest = - (__IO uint16_t *)(FMC_BANK_1_BASE_ADDRESS + (start_addr & ~0x1)); - uint32_t bytes_remaining = bytes_in_page; - - // Handle leading byte - if (start_addr & 0x1) { - // Handle a buffer with an unaligned start. Write 0xff for the first byte - // since flash can only flip ones to zeros, and no data will be lost. - const uint16_t first_short_value = 0xFF | ((*buffer) << 8); - *flash_write_dest = first_short_value; - - // Now for the rest of the function let's pretend this never happened. - ++flash_write_dest; - ++buffer; - --bytes_remaining; - } - - // Handle body words - for (; bytes_remaining >= 2; bytes_remaining -= 2, buffer += 2) { - uint16_t buffer_word; - memcpy(&buffer_word, buffer, sizeof buffer_word); - *flash_write_dest++ = buffer_word; - } - - // Handle trailing byte if present. This will be present if we started out - // aligned and we wrote an odd number of bytes or if we started out unaligned - // and wrote an even number of bytes. - if (bytes_remaining) { - // We need to write only a single byte, but we're only allowed to write - // words. If we write a single byte followed by 0xFFFF, we won't modify the - // second byte as bits are only allowed to be written from 1 -> 0. 1s will - // stay 1s, and 0s will stay 0s. - const uint16_t trailing_short_value = *buffer | 0xFF00; - *flash_write_dest = trailing_short_value; - } - - // Buffer writing is complete, issue the buffer to flash command to actually - // commit the changes to memory. - prv_issue_command(sector_addr, S29VSCommand_BufferToFlash); - - // Check the status register to make sure that the write has started. - status_t result = E_UNKNOWN; - const uint8_t sr = prv_read_status_register(sector_addr); - if ((sr & S29VSStatusBit_DeviceReady) == 0) { - // Program or erase operation in progress. Is it in the current bank? - result = ((sr & S29VSStatusBit_BankStatus) == 0) ? S_SUCCESS : E_BUSY; - } else { - // Operation hasn't started. Something is wrong. - if (sr & S29VSStatusBit_SectorLockStatus) { - // Sector is write-protected. - result = E_INVALID_OPERATION; - } else if (sr & S29VSStatusBit_ProgramStatus) { - // Programming failed for some reason. - result = E_ERROR; - } else { - // The flash never appeared to go busy and there is no error. Either the - // flash write completed between the write command and the status register - // read (inopportune context switch or running in QEMU), or the write - // never started. It's possible to tell them apart by validating that the - // data was actually written to flash, but that adds even more complexity - // to this function. Let the upper layers verify that the write succeeded - // if they are concerned about reliability. - result = S_SUCCESS; - } - } - - prv_allow_write_if_sector_is_not_protected(true, sector_addr); - flash_impl_release(); - return FAILED(result) ? result : (int)bytes_in_page; -} - -status_t flash_impl_get_write_status(void) { - flash_impl_use(); - const uint8_t status = prv_read_status_register(0); - flash_impl_release(); - - if ((status & S29VSStatusBit_DeviceReady) == 0) return E_BUSY; - if ((status & S29VSStatusBit_ProgramSuspended) != 0) return E_AGAIN; - if ((status & S29VSStatusBit_ProgramStatus) != 0) return E_ERROR; - return S_SUCCESS; -} - -uint8_t pbl_28517_flash_impl_get_status_register(uint32_t sector_addr) { - flash_impl_use(); - - const FlashAddress base_addr = flash_impl_get_sector_base_address(sector_addr); - const uint8_t status = prv_read_status_register(base_addr); - - flash_impl_release(); - - return status; -} - -status_t flash_impl_enter_low_power_mode(void) { - prv_flash_idle_gpios(false); - return S_SUCCESS; -} - -status_t flash_impl_exit_low_power_mode(void) { - // it's ok to access s_num_flash_uses here directly, as only caller enter_stop_mode() is called - // only while interrupts are disabled - prv_flash_idle_gpios(s_num_flash_uses > 0); - return S_SUCCESS; -} - -static void prv_switch_flash_mode(FMC_NORSRAMInitTypeDef *nor_init) { - FMC_NORSRAMCmd(FMC_Bank1_NORSRAM1, DISABLE); - FMC_NORSRAMInit(nor_init); - FMC_NORSRAMCmd(FMC_Bank1_NORSRAM1, ENABLE); -} - -static uint16_t prv_get_num_wait_cycles(uint32_t flash_clock_freq) { - // wait_cycle table based on frequency (table 7.1) - // NOTE: 27MHZ frequency skipped due to data latency being 4 smaller than the wait_cycle - uint32_t wait_cycle[] = {40000000, 54000000, 66000000, 80000000, 95000000, 104000000, 120000000}; - // find number wait states based on table - uint32_t wait_state; - for (wait_state = 4; wait_state < (ARRAY_LENGTH(wait_cycle) + 4); wait_state++) { - if (flash_clock_freq < wait_cycle[wait_state - 4]) { - break; - } - } - return wait_state; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - const uint32_t MAX_FREQ = MHZ_TO_HZ(108); // max frequency of the flash 108MHZ - const uint32_t TAVDP_MIN = 60; // min addr setup time in tenths of ns - const uint32_t TADVO_MIN = 40; // min addr hold time in tenths - - const uint32_t SETUP_STEP = MHZ_TO_HZ(16); // for data setup equation - - const uint16_t WAIT_STATE_MASK = 0x7800; // mask for wait state binary for sync burst - - flash_impl_use(); - - // get system clock tick speed - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - uint32_t h_clock = clocks.HCLK_Frequency; // frequency in hertz - uint32_t time_per_cycle = ((uint64_t)(10000000000)) / h_clock; // period in 1/10th ns - - FMC_NORSRAMTimingInitTypeDef nor_timing_init = { - // time between address write and address latch (AVD high) - // tAAVDS on datasheet, min 4 ns - // - // AVD low time - // tAVDP on datasheet, min 6 ns - .FMC_AddressSetupTime = (TAVDP_MIN / time_per_cycle) + 1, // give setup of min 6ns - - // time between AVD high (address is available) and OE low (memory can write) - // tAVDO on the datasheet, min 4 ns - .FMC_AddressHoldTime = (TADVO_MIN / time_per_cycle) + 1, // gives hold of min 4ns - - // time between OE low (memory can write) and valid data being available - // FIXME: optimize this equation - // current linear equation has slope of 1 cycle/SETUP_STEP, with initial value 1 - // setupTime based on h_clock frequency - // equation derived from existing working values; 5 at 64Mhz, 8 at 128 Mhz - // the data was then interpolated into a line, with a padded value of 1 - .FMC_DataSetupTime = (h_clock / SETUP_STEP) + 1, - - // Time between chip selects - // not on the datasheet, picked a random safe number - // FIXME: at high bus frequencies, more than one cycle may be needed - .FMC_BusTurnAroundDuration = 1, // TODO: actually ok? See back-to-back Read/Write Cycle - - .FMC_CLKDivision = 15, // Not used for async NOR - .FMC_DataLatency = 15, // Not used for async NOR - .FMC_AccessMode = FMC_AccessMode_A // Only used for ExtendedMode == FMC_ExtendedMode_Enable, - // which we don't use - }; - - FMC_NORSRAMInitTypeDef nor_init = {.FMC_Bank = FMC_Bank1_NORSRAM1, - .FMC_DataAddressMux = FMC_DataAddressMux_Enable, - .FMC_MemoryType = FMC_MemoryType_NOR, - .FMC_MemoryDataWidth = FMC_NORSRAM_MemoryDataWidth_16b, - .FMC_BurstAccessMode = FMC_BurstAccessMode_Disable, - .FMC_AsynchronousWait = FMC_AsynchronousWait_Disable, - .FMC_WaitSignalPolarity = FMC_WaitSignalPolarity_Low, - .FMC_WrapMode = FMC_WrapMode_Disable, - .FMC_WaitSignalActive = FMC_WaitSignalActive_BeforeWaitState, - .FMC_WriteOperation = FMC_WriteOperation_Enable, - .FMC_WaitSignal = FMC_WaitSignal_Enable, - .FMC_ExtendedMode = FMC_ExtendedMode_Disable, - .FMC_WriteBurst = FMC_WriteBurst_Disable, - .FMC_ContinousClock = FMC_CClock_SyncOnly, - .FMC_ReadWriteTimingStruct = &nor_timing_init}; - - // configure the peripheral before we try to read from it - prv_switch_flash_mode(&nor_init); - - uint16_t configuration_register = prv_read_configuration_register(); - // clear bits that are about to be set - configuration_register &= 0x0278; // clear bits [15:10], [8:7], [2:0] - - // add one. This way, if (h_clock < MAX_FREQ), only divide by one (use h_clock as is) - // else divide by whatever is needed to be under MAX_FREQ - uint32_t clk_division = (h_clock / (MAX_FREQ + 1)) + 1; - - // Update necessary parameters for synchronous modes - if (burst_mode) { - nor_init.FMC_BurstAccessMode = FMC_BurstAccessMode_Enable; - nor_init.FMC_WaitSignalActive = FMC_WaitSignalActive_DuringWaitState; - - nor_timing_init.FMC_BusTurnAroundDuration = 1; - - // nor_timing_init.FMC_DataSetupTime = 1; // FIXME: originally set to 1 for 64Mhz - // but sync burst was not working at this value; - // commented out so the DataSetupTime for ASYNC (up above) is used instead - // this is to ensure sync_burst works with dynamic changes to h_clk frequency - - nor_timing_init.FMC_CLKDivision = clk_division; // divide h_clock if h_clock > 108MHZ - - uint16_t wait_state = prv_get_num_wait_cycles(h_clock / clk_division); - // testing shows that a difference of 4 needs to be maintained between wait_state and latency - nor_timing_init.FMC_DataLatency = wait_state - 4; - - // Set bits according to value needed - see Table 7.11 in data sheet - // [15] Device Read Mode 0b0 Synchronous Read Mode - // [14:11] Programmable Read Wait States 0bXXXX N wait cycles, wait states set to (N - 2) - // [10] RDY Polarity 0b1 RDY signal is active high (default) - // [8] RDY Timing 0b0 RDY active once cycle before data (default) - // [7] Output Drive Strength 0b0 Full Drive=Current Driver Strength (default) - // [2:0] Burst Length 0b000 Continuous (default) - configuration_register |= 0x400 | (((wait_state - 2) << 11) & (WAIT_STATE_MASK)); - } else { - // Set bits according to value needed - see Table 7.11 in data sheet - // [15] Device Read Mode 0b1 Asynchronous Read Mode - // [14:11] Programmable Read Wait States 0b1011 13 wait cycles (default) - // [10] RDY Polarity 0b1 RDY signal is active high (default) - // [8] RDY Timing 0b1 RDY active with data (default) - // [7] Output Drive Strength 0b0 Full Drive=Current Driver Strength (default) - // [2:0] Burst Length 0b000 Continuous (default) - configuration_register |= 0xDD00; - } - - prv_write_configuration_register(configuration_register); - - prv_switch_flash_mode(&nor_init); - - prv_poll_for_ready(0); - flash_impl_release(); - - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - // FIXME: Blank check operation is only allowed in asynchronous mode. Fall - // back to a software blank check in synchronous mode. - const FlashAddress base_addr = flash_impl_get_sector_base_address(addr); - status_t ret = E_INTERNAL; - - flash_impl_use(); - uint8_t status = prv_read_status_register(base_addr); - if ((status & S29VSStatusBit_DeviceReady) == 0 || - (status & (S29VSStatusBit_EraseSuspended | S29VSStatusBit_ProgramSuspended)) != 0) { - ret = E_BUSY; - goto done; - } - - prv_issue_command(base_addr, S29VSCommand_SectorBlank); - status = prv_poll_for_ready(base_addr); - ret = ((status & S29VSStatusBit_EraseStatus) == 0) ? S_TRUE : S_FALSE; - -done: - flash_impl_release(); - return ret; -} - -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return flash_impl_blank_check_sector(addr); -} - -bool flash_check_whoami(void) { - uint16_t manufacturer_id = prv_read_manufacturer_id(); - PBL_LOG(LOG_LEVEL_DEBUG, "Flash Manufacturer ID: 0x%" PRIx16, manufacturer_id); - - return manufacturer_id == SPANSION_MANUFACTURER_ID || manufacturer_id == MACRONIX_MANUFACTURER_ID; -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 800; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 800; } diff --git a/src/fw/drivers/flash/wscript_build b/src/fw/drivers/flash/wscript_build new file mode 100644 index 0000000000..7fd45a6eb4 --- /dev/null +++ b/src/fw/drivers/flash/wscript_build @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'cd_flash_driver.c', + 'flash_api.c', + 'flash_crc.c', + 'flash_erase.c', +] +use = [ + 'driver_task_watchdog', + 'driver_watchdog', + 'freertos', + 'fw_includes', + 'fw_services', + 'pbl_includes', +] + +if bld.env.CONFIG_FLASH_QEMU: + sources.append('../qemu/qemu_flash_hal.c') +elif bld.env.CONFIG_FLASH_GD25LQ255E: + sources.append('gd25lq255e.c') + use.append('driver_qspi') +elif bld.env.CONFIG_FLASH_GD25Q256E: + sources.append('gd25q256e.c') + use.append('driver_qspi') +bld.objects( + name='driver_flash', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_flash') diff --git a/src/fw/drivers/flash/xt25f64b.c b/src/fw/drivers/flash/xt25f64b.c deleted file mode 100644 index b2a8ad844e..0000000000 --- a/src/fw/drivers/flash/xt25f64b.c +++ /dev/null @@ -1,158 +0,0 @@ -#include "board/board.h" -#include "drivers/flash/flash_impl.h" -#include "drivers/flash/qspi_flash.h" -#include "drivers/flash/qspi_flash_part_definitions.h" -#include "flash_region/flash_region.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "util/math.h" - -#define NRF5_COMPATIBLE -#include - -static QSPIFlashPart QSPI_FLASH_PART = { - .instructions = - { - .fast_read = 0x0B, - .pp = 0x02, - .erase_sector_4k = 0x20, - .erase_block_64k = 0xD8, - .write_enable = 0x06, - .write_disable = 0x04, - .rdsr1 = 0x05, - .rdsr2 = 0x35, - .erase_suspend = 0x75, - .erase_resume = 0x7A, - .enter_low_power = 0xB9, - .exit_low_power = 0xAB, - .enter_quad_mode = 0x38, - .reset_enable = 0x66, - .reset = 0x99, - .qspi_id = 0x9F, /* single SPI ID */ - }, - .status_bit_masks = - { - .busy = 1 << 0, - .write_enable = 1 << 1, - }, - .flag_status_bit_masks = - { - .erase_suspend = 0, /* erase suspend not supported */ - }, - .dummy_cycles = - { - .fast_read = 4, - }, - .supports_block_lock = false, - .reset_latency_ms = 12, - .suspend_to_read_latency_us = 20, - .standby_to_low_power_latency_us = 3, - .low_power_to_standby_latency_us = 20, - .supports_fast_read_ddr = false, - .qspi_id_value = 0x17400b, - .name = "XT25F64B", -}; - -bool flash_check_whoami(void) { return qspi_flash_check_whoami(QSPI_FLASH); } - -FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { - return (addr & SECTOR_ADDR_MASK); -} - -FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { - return (addr & SUBSECTOR_ADDR_MASK); -} - -void flash_impl_enable_write_protection(void) {} - -status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { -#if 0 - FlashAddress block_addr = start_sector; - while (block_addr <= end_sector) { - uint32_t block_size; - if (WITHIN(block_addr, SECTOR_SIZE_BYTES, BOARD_NOR_FLASH_SIZE - SECTOR_SIZE_BYTES - 1)) { - // Middle of flash has 64k lock units - block_addr = flash_impl_get_sector_base_address(block_addr); - block_size = SECTOR_SIZE_BYTES; - } else { - // Start and end of flash have 1 sector of 4k lock units - block_addr = flash_impl_get_subsector_base_address(block_addr); - block_size = SUBSECTOR_SIZE_BYTES; - } - const status_t sc = qspi_flash_lock_sector(QSPI_FLASH, block_addr); - if (FAILED(sc)) { - return sc; - } - block_addr += block_size; - } -#endif - return S_SUCCESS; -} - -status_t flash_impl_unprotect(void) { - // No way to unprotect all of flash. This requires a full reset of the mt25q -#if 0 - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, qspi_flash_is_in_coredump_mode(QSPI_FLASH)); -#endif - return S_SUCCESS; -} - -status_t flash_impl_init(bool coredump_mode) { - qspi_flash_init(QSPI_FLASH, &QSPI_FLASH_PART, coredump_mode); - return S_SUCCESS; -} - -status_t flash_impl_get_erase_status(void) { return qspi_flash_is_erase_complete(QSPI_FLASH); } - -status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, subsector_addr, true /* is_subsector */); -} -status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { - return qspi_flash_erase_begin(QSPI_FLASH, sector_addr, false /* !is_subsector */); -} - -status_t flash_impl_erase_suspend(FlashAddress sector_addr) { - return qspi_flash_erase_suspend(QSPI_FLASH, sector_addr); -} - -status_t flash_impl_erase_resume(FlashAddress sector_addr) { - qspi_flash_erase_resume(QSPI_FLASH, sector_addr); - return S_SUCCESS; -} - -status_t flash_impl_read_sync(void *buffer_ptr, FlashAddress start_addr, size_t buffer_size) { - PBL_ASSERT(buffer_size > 0, "flash_impl_read_sync() called with 0 bytes to read"); - qspi_flash_read_blocking(QSPI_FLASH, start_addr, buffer_ptr, buffer_size); - return S_SUCCESS; -} - -int flash_impl_write_page_begin(const void *buffer, const FlashAddress start_addr, size_t len) { - return qspi_flash_write_page_begin(QSPI_FLASH, buffer, start_addr, len); -} - -status_t flash_impl_get_write_status(void) { return qspi_flash_get_write_status(QSPI_FLASH); } - -status_t flash_impl_enter_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, true); - return S_SUCCESS; -} -status_t flash_impl_exit_low_power_mode(void) { - qspi_flash_set_lower_power_mode(QSPI_FLASH, false); - return S_SUCCESS; -} - -status_t flash_impl_set_burst_mode(bool burst_mode) { - // NYI - return S_SUCCESS; -} - -status_t flash_impl_blank_check_sector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, false /* !is_subsector */); -} -status_t flash_impl_blank_check_subsector(FlashAddress addr) { - return qspi_flash_blank_check(QSPI_FLASH, addr, true /* is_subsector */); -} - -uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { return 150; } - -uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { return 50; } diff --git a/src/fw/drivers/fpc_pinstrap.h b/src/fw/drivers/fpc_pinstrap.h deleted file mode 100644 index d7b963ca3f..0000000000 --- a/src/fw/drivers/fpc_pinstrap.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! @file fpc_pinstrap.h -//! -//! This file implements an API to read the pinstrap values for an attached FPC (flexible printed -//! circuit). These values are used for version identification so we can figure out which version -//! of FPC is connected to our main PCB. - -#define FPC_PINSTRAP_NOT_AVAILABLE 0xff - -//! @return uint8_t a value between 0 and 8 to represent the pinstrap value. If the pinstrap -//! value isn't valid on this platform, FPC_PINSTRAP_NOT_AVAILABLE is returned. -uint8_t fpc_pinstrap_get_value(void); diff --git a/src/fw/drivers/fpc_pinstrap/fpc_pinstrap_snowy.c b/src/fw/drivers/fpc_pinstrap/fpc_pinstrap_snowy.c deleted file mode 100644 index a9a356b8ee..0000000000 --- a/src/fw/drivers/fpc_pinstrap/fpc_pinstrap_snowy.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/fpc_pinstrap.h" - -#include "board/board.h" -#include "drivers/gpio.h" - -static uint8_t prv_read_pinstrap_pin(InputConfig pin) { - // Read the pin value with it pulled up - gpio_input_init_pull_up_down(&pin, GPIO_PuPd_UP); - const bool pull_up_value = gpio_input_read(&pin); - - // If the pull up was high, that either means it's actually high or floating. Read the - // pin again with a pull down to differentiate. - - gpio_input_init_pull_up_down(&pin, GPIO_PuPd_DOWN); - const bool pull_down_value = gpio_input_read(&pin); - - // Reset the pin to an analog input when we're not using it to reduce power draw. - gpio_analog_init(&pin); - - if (pull_down_value != pull_up_value) { - // The value changed based on the pullup so it's floating. - return 2; - } - // It's not floating, return what the initial read told us. - return pull_up_value ? 1 : 0; -} - -uint8_t fpc_pinstrap_get_value(void) { - // This is an uncommon operation so just configure the GPIOs as needed. - - if (BOARD_CONFIG.fpc_pinstrap_1.gpio == GPIO_Port_NULL) { - return FPC_PINSTRAP_NOT_AVAILABLE; - } - - return (prv_read_pinstrap_pin(BOARD_CONFIG.fpc_pinstrap_1) * 3) - + prv_read_pinstrap_pin(BOARD_CONFIG.fpc_pinstrap_2); -} diff --git a/src/fw/drivers/gpio.h b/src/fw/drivers/gpio.h index 944dbe305b..2ad98da4c3 100644 --- a/src/fw/drivers/gpio.h +++ b/src/fw/drivers/gpio.h @@ -1,33 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - #include "board/board.h" -#ifdef MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 #include typedef enum { @@ -49,7 +29,7 @@ typedef enum { #endif -#ifdef MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 void gpio_use(uint32_t pin); void gpio_release(uint32_t pin); @@ -77,7 +57,7 @@ void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, //! is true, and drives it low if pin_config.active_high is false. void gpio_output_set(const OutputConfig *pin_config, bool asserted); -#ifndef MICRO_FAMILY_NRF5 +#ifndef CONFIG_SOC_NRF52 //! Configure a GPIO alternate function. //! diff --git a/src/fw/drivers/gpio/Kconfig b/src/fw/drivers/gpio/Kconfig new file mode 100644 index 0000000000..b2631e3771 --- /dev/null +++ b/src/fw/drivers/gpio/Kconfig @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig GPIO + bool "GPIO" + help + GPIO Drivers + +if GPIO + +config GPIO_NRF5 + bool "Nordic nRF5" + help + Support for the Nordic nRF5 GPIO controller. + +config GPIO_SF32LB + bool "SiFli SF32LB" + select HAL_SIFLI_GPIO + help + Support for the SiFli SF32LB GPIO controller. + +config GPIO_QEMU + bool "QEMU GPIO" + help + Support for the QEMU GPIO peripheral. + +endif # GPIO diff --git a/src/fw/drivers/gpio/nrf5.c b/src/fw/drivers/gpio/nrf5.c new file mode 100644 index 0000000000..b5236c0dd8 --- /dev/null +++ b/src/fw/drivers/gpio/nrf5.c @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/gpio.h" +#include "system/passert.h" + +#include + +void gpio_input_init(const InputConfig *pin_config) { + nrf_gpio_pin_dir_set(pin_config->gpio_pin, NRF_GPIO_PIN_DIR_INPUT); +} + +void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, + GPIOSpeed_TypeDef speed) { + if (otype == GPIO_OType_OD) + WTF; + + /* XXX: speed */ + nrf_gpio_cfg(pin_config->gpio_pin, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE); +} + +void gpio_output_set(const OutputConfig *pin_config, bool asserted) { + if (!pin_config->active_high) { + asserted = !asserted; + } + nrf_gpio_pin_write(pin_config->gpio_pin, !!asserted); +} diff --git a/src/fw/drivers/gpio/sf32lb.c b/src/fw/drivers/gpio/sf32lb.c new file mode 100644 index 0000000000..0ce2b32006 --- /dev/null +++ b/src/fw/drivers/gpio/sf32lb.c @@ -0,0 +1,92 @@ +/* SPDX-FileCopyrightText: 2025 SiFli Technologies(Nanjing) Co., Ltd */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/gpio.h" +#include "system/passert.h" +#include "board/board.h" + +#include "FreeRTOS.h" + +#include + +static RCC_MODULE_TYPE prv_get_gpio_rcc_module(GPIO_TypeDef *GPIOx) { + if (GPIOx == hwp_gpio1) { + return RCC_MOD_GPIO1; + } else if (GPIOx == hwp_gpio2) { + return RCC_MOD_GPIO2; + } else { + WTF; + } + return 0; +} + +void gpio_use(GPIO_TypeDef *GPIOx) { + RCC_MODULE_TYPE rcc_module = prv_get_gpio_rcc_module(GPIOx); + portENTER_CRITICAL(); + HAL_RCC_EnableModule(rcc_module); + portEXIT_CRITICAL(); +} + +void gpio_release(GPIO_TypeDef *GPIOx) { + RCC_MODULE_TYPE rcc_module = prv_get_gpio_rcc_module(GPIOx); + portENTER_CRITICAL(); + HAL_RCC_DisableModule(rcc_module); + portEXIT_CRITICAL(); +} + +void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, + GPIOSpeed_TypeDef speed) { + (void)speed; + gpio_use(pin_config->gpio); + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.Pin = pin_config->gpio_pin; + if (otype == GPIO_OType_OD) { + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; + } else if (otype == GPIO_OType_PP) { + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT; + } else { + WTF; + } + HAL_PIN_Set(PAD_PA00 + pin_config->gpio_pin, GPIO_A0 + pin_config->gpio_pin, PIN_NOPULL, 1); + GPIO_InitStruct.Pull = GPIO_NOPULL; + + HAL_GPIO_Init(pin_config->gpio, &GPIO_InitStruct); +} + +void gpio_input_init(const InputConfig *input_cfg) { + gpio_input_init_pull_up_down(input_cfg, GPIO_PuPd_NOPULL); +} + +void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd) { + gpio_use(input_cfg->gpio); + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.Pin = input_cfg->gpio_pin; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + + int flag = 0; + /* pullup/down is handled in pinmux hal. */ + if (pupd == GPIO_PuPd_UP) { + flag = PIN_PULLUP; + } else if (pupd == GPIO_PuPd_DOWN) { + flag = PIN_PULLDOWN; + } else { + flag = PIN_NOPULL; + } + + HAL_PIN_Set(PAD_PA00 + input_cfg->gpio_pin, GPIO_A0 + input_cfg->gpio_pin, flag, 1); + HAL_GPIO_Init(input_cfg->gpio, &GPIO_InitStruct); +} + +bool gpio_input_read(const InputConfig *input_cfg) { + bool value = HAL_GPIO_ReadPin(input_cfg->gpio, input_cfg->gpio_pin); + return value; +} + +void gpio_output_set(const OutputConfig *pin_config, bool asserted) { + if (!pin_config->active_high) { + asserted = !asserted; + } + + HAL_GPIO_WritePin(pin_config->gpio, pin_config->gpio_pin, asserted); +} \ No newline at end of file diff --git a/src/fw/drivers/gpio/wscript_build b/src/fw/drivers/gpio/wscript_build new file mode 100644 index 0000000000..45d7f7ac65 --- /dev/null +++ b/src/fw/drivers/gpio/wscript_build @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_GPIO_QEMU: + sources.append('../qemu/qemu_gpio_hal.c') +elif bld.env.CONFIG_GPIO_NRF5: + sources.append('nrf5.c') + use.append('hal_nordic') +elif bld.env.CONFIG_GPIO_SF32LB: + sources.append('sf32lb.c') + +bld.objects( + name='driver_gpio', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_gpio') diff --git a/src/fw/drivers/hrm.h b/src/fw/drivers/hrm.h index e9b1ee5804..2542d365ed 100644 --- a/src/fw/drivers/hrm.h +++ b/src/fw/drivers/hrm.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -24,7 +11,8 @@ void hrm_init(HRMDevice *dev); //! Enable the HRM -void hrm_enable(HRMDevice *dev); +//! @return true if successfully enabled, false if initialization failed +bool hrm_enable(HRMDevice *dev); //! Disable the HRM void hrm_disable(HRMDevice *dev); diff --git a/src/fw/drivers/hrm/Kconfig b/src/fw/drivers/hrm/Kconfig new file mode 100644 index 0000000000..7a633b3a08 --- /dev/null +++ b/src/fw/drivers/hrm/Kconfig @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig HRM + bool "Heart Rate Monitor" + help + Heart Rate Monitor Drivers + +if HRM + +config HRM_GH3X2X + bool "Goodix GH3X2X" + help + Support for the Goodix GH3X2X heart rate sensor. + +config HRM_STUB + bool "Stub" + help + Stub heart rate monitor implementation. + +module = DRIVER_HRM +module-str = Heart Rate Monitor +source "src/fw/Kconfig.template.log_level" + +endif # HRM diff --git a/src/fw/drivers/hrm/as7000/README_AS7000.md b/src/fw/drivers/hrm/as7000/README_AS7000.md deleted file mode 100644 index 77469442fd..0000000000 --- a/src/fw/drivers/hrm/as7000/README_AS7000.md +++ /dev/null @@ -1,130 +0,0 @@ -Some Information About the AMS AS7000 -===================================== - -The documentation about the AS7000 can be a bit hard to follow and a bit -incomplete at times. This document aims to fill in the gaps, using -knowledge gleaned from the datasheet, Communication Protocol document -and the SDK docs and sources. - -What is the AS7000 exactly? ---------------------------- - -The AS7000 is a Cortex-M0 SoC with some very specialized peripherals. -It can be programmed with a firmware to have it function as a heart-rate -monitor. It doesn't necessarily come with the HRM firmware preloaded, so -it is not one of those devices that you can treat as a black-box piece -of hardware that you just power up and talk to. - -There is 32 kB of flash for the main application, and some "reserved" -flash containing a loader application. There is also a ROM first-stage -bootloader. - -Boot Process ------------- - -The chip is woken from shutdown by pulling the chip's GPIO8 pin low. The -CPU core executes the ROM bootloader after powerup or wake from -shutdown. The bootloader's logic is apparently as follows: - - if loader is available: - jump to loader - elif application is valid: - remap flash to address 0 - jump to application - else: - wait on UART?? (not documented further) - -We will probably always be using parts with the loader application -preprogrammed by AMS so the existence of the bootloader just makes the -communication protocol document a bit more confusing to understand. It -can be safely ignored and we can pretend that the loader application is -the only bootloader. - -Once control is passed from the ROM bootloader to the loader, the loader -performs the following: - - wait 30 ms - if application is valid and GPIO8 is low: - remap flash to address 0 - jump to application - else: - run loader application - -The reason for the 30 ms wait and check for GPIO8 is to provide an -escape hatch for getting into the loader if a misbehaving application -is programmed which doesn't allow for a command to be used to enter the -loader. - -The "Application is valid" check is apparently to check that address -0x7FFC (top of main flash) contains the bytes 72 75 6C 75. - -The Loader ----------- - -The loader allows new applications to be programmed into the main flash -over I2C by sending Intel HEX records(!). The communication protocol -document describes the protocol well enough, though it is a bit lacking -in detail as to what HEX records it accepts. Luckily the SDK comes with -a header file which fills in some of the details, and the SDK Getting -Started document has some more details. - - - The supported record types are 00 (Data), 01 (EOF), 04 (Extended - Linear Address) and 05 (Start Linear Address). - - The HEX records must specify addresses in the aliased range 0-0x7FFF - - HEX records must be sent in order of strictly increasing addresses. - -A comment in loader.h from the SDK states that the maximum record size -supported by the loader can be configured as 256, 128, 64 or 32. The -`#define` in the same header which sets the max record length is set -to 256, strongly implying that this is the record length limit for the -loader firmware which is already programmed into the parts. Programming -an application firmware is successful when using records of length 203, -which seems to confirm that the max record length is indeed 256. - -The HEX files provided by AMS appear to be standard HEX files generated -by the Keil MDK-ARM toolchain. http://www.keil.com/support/docs/1584.htm - -The Start Linear Address record contains the address of the "pre-main" -function, which is not the reset vector. Since the reset vector is -already written as data in the application's vector table, the record is -unnecessary for flash loading and is likely just thrown away by the -loader. - -Empirical testing has confirmed that neither the Start Linear Address -nor Extended Linear Address records need to be sent for flashing to -succeed. It is acceptable to send only a series of Data records followed -by an EOF record. - -The loader will exit if one of the following conditions is true: - - - An EOF record is sent - - A valid HEX record is sent which the loader doesn't understand - - Data is sent which is not a valid HEX record - - No records are sent for approximately ten seconds - -The loader exits by waiting one second for the host controller to read -out the exit code register, then performs a system reset. - -The Application ---------------- - -Due to the simplicity of the CM0 and the limited resources available on -the chip, the application firmware takes full control of the system. The -applications in the SDK are written as a straightforward mainloop with -no OS. Think of it as a very simple form of cooperative multitasking. - -The "mandatory" I2C register map mentioned in the Communication Protocol -document is merely a part of the protocol; it is entirely up to the -application firmware to properly implement it. The Application ID -register doesn't do anything special on its own: the mainloop simply -polls the register value at each iteration to see if it needs to start -or stop any "apps" (read: modules). The application firmware could -misbehave, resulting in writes to that register having no effect. - -Writing 0x01 to the Application ID register isn't special either; it is -up to the application to recognize that value and reset into the loader. -That's why the escape hatch is necessary, and the fundamental reason why -the loader application cannot run at the same time as any other -application. - -GPIO8 isn't special while the firmware is running, either. diff --git a/src/fw/drivers/hrm/as7000/as7000.c b/src/fw/drivers/hrm/as7000/as7000.c deleted file mode 100644 index 2ab757d5a0..0000000000 --- a/src/fw/drivers/hrm/as7000/as7000.c +++ /dev/null @@ -1,1120 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_GREEN -#include "as7000.h" - -#include "board/board.h" -#include "drivers/backlight.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "kernel/events.h" -#include "kernel/util/sleep.h" -#include "kernel/util/interval_timer.h" -#include "mfg/mfg_info.h" -#include "resource/resource.h" -#include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/system_task.h" -#include "services/common/hrm/hrm_manager.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/profiler.h" -#include "util/attributes.h" -#include "util/ihex.h" -#include "util/math.h" -#include "util/net.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -// Enable this to get some very verbose logs about collecting PPG data from the HRM -// Bump this up to 2 to get very verbose logs -#define PPG_DEBUG 0 - -#if PPG_DEBUG -#define PPG_DBG(...) \ - do { \ - PBL_LOG(LOG_LEVEL_DEBUG, __VA_ARGS__); \ - } while (0); -#else -#define PPG_DBG(...) -#endif - -#if PPG_DEBUG == 2 -#define PPG_DBG_VERBOSE(...) \ - do { \ - PBL_LOG_VERBOSE(LOG_LEVEL_DEBUG, __VA_ARGS__); \ - } while (0); -#else -#define PPG_DBG_VERBOSE(...) -#endif - - - -// The datasheet recommends waiting for 250ms for the chip to boot -#define NORMAL_BOOT_DELAY_MS (250) -// We need to wait an extra second for the loader to time-out -#define LOADER_REBOOT_DELAY_MS (NORMAL_BOOT_DELAY_MS + 1000) -// Usually takes a couple ms after writing a record, but spikes of ~20ms have been observed. Let's -// be conservative. -#define LOADER_READY_MAX_DELAY_MS (50) -// Give the sensor this much time to tear down the current app and go back to the idle mode -#define SHUT_DOWN_DELAY_MS (1000) -// Number of handshakes before samples are expected -#define WARMUP_HANDSHAKES (2) - -#define EXPECTED_PROTOCOL_VERSION_MAJOR (2) - -// White Threshold is 5000 -// Black Threshold is 3500 -// Value stored in the register is in units of 64 ADC counts -// e.g. 78 * 64 = 4992 ADC-counts -// Refer to AS7000 SW Communication Protocol section 6.7 -#define PRES_DETECT_THRSH_WHITE 78 // (5000 / 64) -#define PRES_DETECT_THRSH_BLACK 54 // (3500 / 64) - -// register addresses -#define ADDR_LOADER_STATUS (0x00) -#define ADDR_INFO_START (0x00) -#define ADDR_APP_IDS (0x04) - -#define ADDR_ACCEL_SAMPLE_FREQ_MSB (0x08) -#define ADDR_ACCEL_SAMPLE_FREQ_LSB (0x09) - -// Register that allows us to compensate for clock skew between us (the host) and the sensor. The -// sensor doesn't track time accurately, and gives us a heart rate value that's in the sensors -// time domain, which will need to be translated into "real time" according to our time domain. -// If we use these registers to tell the sensor how frequently it's handshaking with us in our -// time domain, this will let the sensor do this compensation for us. -// The value programmed in here is in units of 0.1ms (value of 10000 = 1 second). -#define ADDR_HOST_ONE_SECOND_TIME_MSB (0x0a) -#define ADDR_HOST_ONE_SECOND_TIME_LSB (0x0b) - -#define ADDR_NUM_ACCEL_SAMPLES (0x0e) -#define ADDR_NUM_PPG_SAMPLES (0x0f) - -#define ADDR_ACCEL_SAMPLE_IDX (0x14) -#define ADDR_ACCEL_X_MSB (0x15) -#define ADDR_ACCEL_Y_MSB (0x17) -#define ADDR_ACCEL_Z_MSB (0x19) - -#define ADDR_PPG_IDX (0x1b) -#define ADDR_PPG_MSB (0x1c) -#define ADDR_PPG_LSB (0x1d) -#define ADDR_TIA_MSB (0x1e) -#define ADDR_TIA_LSB (0x1f) - -#define ADDR_PRES_DETECT_THRSH (0x26) - -#define ADDR_LED_CURRENT_MSB (0x34) -#define ADDR_LED_CURRENT_LSB (0x35) -#define ADDR_HRM_STATUS (0x36) -#define ADDR_HRM_BPM (0x37) -#define ADDR_HRM_SQI (0x38) - -#define ADDR_SYNC (0x39) - -// The AS7000 wants Accel Frequency given in 0.001Hz increments, this can be used to scale -#define AS7000_ACCEL_FREQUENCY_SCALE (1000) - -//! Thresholds for quality conversion. These are upper bounds on readings. -enum AS7000SQIThreshold { - AS7000SQIThreshold_Excellent = 2, - AS7000SQIThreshold_Good = 5, - AS7000SQIThreshold_Acceptable = 8, - AS7000SQIThreshold_Poor = 10, - AS7000SQIThreshold_Worst = 20, - - AS7000SQIThreshold_OffWrist = 254, - - AS7000SQIThresholdInvalid, -}; - -enum AS7000Status { - AS7000Status_OK = 0, - AS7000Status_IllegalParameter = 1, - AS7000Status_LostData = 2, - AS7000Status_NoAccel = 4, -}; - -typedef enum AS7000AppId { - AS7000AppId_Idle = 0x00, - AS7000AppId_Loader = 0x01, - AS7000AppId_HRM = 0x02, - AS7000AppId_PRV = 0x04, - AS7000AppId_GSR = 0x08, - AS7000AppId_NTC = 0x10, -} AS7000AppId; - -typedef enum AS7000LoaderStatus { - AS7000LoaderStatus_Ready = 0x00, - AS7000LoaderStatus_Busy1 = 0x3A, - AS7000LoaderStatus_Busy2 = 0xFF, - // all other values indicate an error -} AS7000LoaderStatus; - -typedef struct PACKED AS7000FWUpdateHeader { - uint8_t sw_version_major; - uint8_t sw_version_minor; -} AS7000FWUpdateHeader; - -typedef struct PACKED AS7000FWSegmentHeader { - uint16_t address; - uint16_t len_minus_1; -} AS7000FWSegmentHeader; - -//! The maximum number of data bytes to include in a reconstituted -//! Intel HEX Data record when updating the HRM firmware. -//! This is the size of the binary data encoded in the record, __NOT__ -//! the size of the HEX record encoding the data. The HEX record itself -//! will be IHEX_RECORD_LENGTH(MAX_HEX_DATA_BYTES) -//! (MAX_HEX_DATA_BYTES*2 + 11) bytes in size. -#define MAX_HEX_DATA_BYTES (96) - -// The AS7000 loader cannot accept HEX records longer than 256 bytes. -_Static_assert(IHEX_RECORD_LENGTH(MAX_HEX_DATA_BYTES) <= 256, - "The value of MAX_HEX_DATA_BYTES will result in HEX records " - "which are longer than the AS7000 loader can handle."); - - -// The sw_version_major field is actually a bitfield encoding both the -// major and minor components of the SDK version number. Define macros -// to extract the components for logging purposes. -#define HRM_SW_VERSION_PART_MAJOR(v) (v >> 6) -#define HRM_SW_VERSION_PART_MINOR(v) (v & 0x3f) - -// If this many watchdog interrupts occur before we receive an interrupt from the sensor, -// we assume the sensor requires a reset -#define AS7000_MAX_WATCHDOG_INTERRUPTS 5 - -// We use this regular timer as a watchdog for the sensor. We have seen cases where the sensor -// becomes unresponsive (PBL-40008). This timer watches to see if we have stopped receiving -// sensor interrupts and will trigger logic to reset the sensor if necessary. -static RegularTimerInfo s_as7000_watchdog_timer; - -// Incremented by s_as7000_watchdog_timer. Reset to 0 by our interrupt handler. -static uint8_t s_missing_interrupt_count; - -//! Interval timer to track how frequently the as7000 is handshaking with us -static IntervalTimer s_handshake_interval_timer; - -static void prv_enable_timer_cb(void *context); -static void prv_disable_watchdog(HRMDevice *dev); - -static bool prv_write_register(HRMDevice *dev, uint8_t register_address, uint8_t value) { - i2c_use(dev->i2c_slave); - bool rv = i2c_write_register(dev->i2c_slave, register_address, value); - i2c_release(dev->i2c_slave); - return rv; -} - -static bool prv_write_register_block(HRMDevice *dev, uint8_t register_address, - const void *buffer, uint32_t length) { - i2c_use(dev->i2c_slave); - bool rv = i2c_write_register_block(dev->i2c_slave, register_address, length, buffer); - i2c_release(dev->i2c_slave); - return rv; -} - -static bool prv_read_register(HRMDevice *dev, uint8_t register_address, uint8_t *value) { - i2c_use(dev->i2c_slave); - bool rv = i2c_read_register(dev->i2c_slave, register_address, value); - i2c_release(dev->i2c_slave); - return rv; -} - -static bool prv_read_register_block(HRMDevice *dev, uint8_t register_address, void *buffer, - uint32_t length) { - i2c_use(dev->i2c_slave); - bool rv = i2c_read_register_block(dev->i2c_slave, register_address, length, buffer); - i2c_release(dev->i2c_slave); - return rv; -} - -static bool prv_set_host_one_second_time_register(HRMDevice *dev, uint32_t average_ms) { - PPG_DBG("host one second time: %"PRIu32" ms", average_ms); - - // Register takes a reading in 0.1ms increments - uint16_t value = average_ms * 10; - - const uint8_t msb = (value >> 8) & 0xff; - const uint8_t lsb = value & 0xff; - return prv_write_register(dev, ADDR_HOST_ONE_SECOND_TIME_MSB, msb) - && prv_write_register(dev, ADDR_HOST_ONE_SECOND_TIME_LSB, lsb); -} - -static void prv_read_ppg_data(HRMDevice *dev, HRMPPGData *data_out) { - uint8_t num_ppg_samples; - prv_read_register(HRM, ADDR_NUM_PPG_SAMPLES, &num_ppg_samples); - num_ppg_samples = MIN(num_ppg_samples, MAX_PPG_SAMPLES); - - for (int i = 0; i < num_ppg_samples; ++i) { - struct PACKED { - uint8_t idx; - uint16_t ppg; - uint16_t tia; - } ppg_reading; - - // Reading PPG data from the chip is a little weird. We need to read the PPG block of registers - // which maps to the ppg_reading struct above. We then need to verify that the index that we - // read matches the one that we expect. If we attempt to read the registers too quickly back to - // back that means that the AS7000 failed to update the value in time and we just need to try - // again. Limit this to a fixed number of attempts to make sure we don't infinite loop. - const int NUM_ATTEMPTS = 3; - bool success = false; - for (int j = 0; j < NUM_ATTEMPTS; ++j) { - prv_read_register_block(HRM, ADDR_PPG_IDX, &ppg_reading, sizeof(ppg_reading)); - if (ppg_reading.idx == i + 1) { - data_out->indexes[i] = ppg_reading.idx; - data_out->ppg[i] = ntohs(ppg_reading.ppg); - data_out->tia[i] = ntohs(ppg_reading.tia); - - success = true; - break; - } - - PPG_DBG_VERBOSE("FAIL: got %"PRIu16" expected %u tia %"PRIu16, - ppg_reading.idx, i + 1, ntohs(ppg_reading.tia)); - // Keep trying... - } - - if (!success) { - // We didn't find a sample, just give up on reading PPG for this handshake - break; - } - - data_out->num_samples++; - } - - PPG_DBG("num_samples reg: %"PRIu8" read: %u", - num_ppg_samples, data_out->num_samples); -} - -static void prv_write_accel_sample(HRMDevice *dev, uint8_t sample_idx, AccelRawData *data) { - struct PACKED { - uint8_t sample_idx; - net16 accel_x; - net16 accel_y; - net16 accel_z; - } sample_data = { - .sample_idx = sample_idx, - .accel_x = hton16(data->x * 2), // Accel service supplies mGs, AS7000 expects lsb = 0.5 mG - .accel_y = hton16(data->y * 2), - .accel_z = hton16(data->z * 2) - }; - prv_write_register_block(dev, ADDR_ACCEL_SAMPLE_IDX, &sample_data, sizeof(sample_data)); -} - -static void prv_read_hrm_data(HRMDevice *dev, HRMData *data) { - struct PACKED { - uint16_t led_current; - uint8_t hrm_status; - uint8_t bpm; - uint8_t sqi; - } hrm_data_regs; - - prv_read_register_block(dev, ADDR_LED_CURRENT_MSB, &hrm_data_regs, sizeof(hrm_data_regs)); - - data->led_current_ua = ntohs(hrm_data_regs.led_current); - data->hrm_status = hrm_data_regs.hrm_status; - data->hrm_bpm = hrm_data_regs.bpm; - - if (data->hrm_status & AS7000Status_NoAccel) { - data->hrm_quality = HRMQuality_NoAccel; - } else if (hrm_data_regs.sqi <= AS7000SQIThreshold_Excellent) { - data->hrm_quality = HRMQuality_Excellent; - } else if (hrm_data_regs.sqi <= AS7000SQIThreshold_Good) { - data->hrm_quality = HRMQuality_Good; - } else if (hrm_data_regs.sqi <= AS7000SQIThreshold_Acceptable) { - data->hrm_quality = HRMQuality_Acceptable; - } else if (hrm_data_regs.sqi <= AS7000SQIThreshold_Poor) { - data->hrm_quality = HRMQuality_Poor; - } else if (hrm_data_regs.sqi <= AS7000SQIThreshold_Worst) { - data->hrm_quality = HRMQuality_Worst; - } else if (hrm_data_regs.sqi == AS7000SQIThreshold_OffWrist) { - data->hrm_quality = HRMQuality_OffWrist; - } else { - data->hrm_quality = HRMQuality_NoSignal; - } -} - -// Sequence of events for handshake pulse (when in one-second burst mode): -// - [optional] Host writes the one-second time (registers 10,11) measured for the last 20 -// samples (about one second). -// - Host reads any data/HRV-result/LED-current, as needed (see registers [14...53]) -// - Host reads the HRM-result/SYNC-byte (registers [54...57]). -// If not in HRM-mode, the host can just read the SYNC-byte (register 57). -// Reading the SYNC-byte causes the AS7000 to release the handshake-signal -// and allows deep-sleep mode (if the AS7000 is configured for this). -// This step must be the last read for this handshake-pulse. -static void prv_handle_handshake_pulse(void *unused_data) { - PPG_DBG("Handshake handle"); - - mutex_lock(HRM->state->lock); - if (!hrm_is_enabled(HRM)) { - mutex_unlock(HRM->state->lock); - return; - } - - // We keep track of the number of handshakes so that we know when to expect samples - const bool should_expect_samples = (HRM->state->handshake_count > WARMUP_HANDSHAKES); - - HRMData data = (HRMData) {}; - - // Immediately read the PPG data. The timing constraints are pretty tight (we need to read this - // within 30ms~ of getting the handshake or else we'll lose PPG data). The other registers can - // be read at anytime before the next handshake, so it's ok to do this first. - prv_read_ppg_data(HRM, &data.ppg_data); - - if (should_expect_samples) { - interval_timer_take_sample(&s_handshake_interval_timer); - } - - // Send the accel data out to the AS7000 - HRMAccelData *accel_data = hrm_manager_get_accel_data(); - const uint8_t num_samples = accel_data->num_samples; - prv_write_register(HRM, ADDR_NUM_ACCEL_SAMPLES, num_samples); - for (uint32_t i = 0; i < num_samples; ++i) { - prv_write_accel_sample(HRM, i + 1, &accel_data->data[i]); - } - data.accel_data = *accel_data; - hrm_manager_release_accel_data(); - - // Read the rest of the HRM data fields. - prv_read_hrm_data(HRM, &data); - - // Handle the clock skew register - uint32_t average_handshake_interval_ms; - uint32_t num_intervals = interval_timer_get(&s_handshake_interval_timer, - &average_handshake_interval_ms); - // Try to write the register frequently early on, and then every half second to accommodate - // changes over time. - if (num_intervals == 2 || - num_intervals == 10 || - (num_intervals % 30) == 0) { - prv_set_host_one_second_time_register(HRM, average_handshake_interval_ms); - } - - // Read the SYNC byte to release handshake signal and enter deep sleep mode. - uint8_t unused; - prv_read_register(HRM, ADDR_SYNC, &unused); - - - PPG_DBG("Handshake handle done"); - HRM->state->handshake_count++; - - - PROFILER_NODE_STOP(hrm_handling); - mutex_unlock(HRM->state->lock); - - - // PPG_DBG log out each PPG data sample that we recorded - for (int i = 0; i < data.ppg_data.num_samples; i++) { - PPG_DBG_VERBOSE("idx %-2"PRIu8" ppg %-6"PRIu16" tia %-6"PRIu16, - data.ppg_data.indexes[i], data.ppg_data.ppg[i], data.ppg_data.tia[i]); - } - - hrm_manager_new_data_cb(&data); - - if (num_samples == 0 && should_expect_samples) { - analytics_inc(ANALYTICS_DEVICE_METRIC_HRM_ACCEL_DATA_MISSING, AnalyticsClient_System); - PBL_LOG(LOG_LEVEL_WARNING, "Falling behind: HRM got 0 accel samples"); - } - -} - -static void prv_as7000_interrupt_handler(bool *should_context_switch) { - PPG_DBG("Handshake interrupt"); - - PROFILER_NODE_START(hrm_handling); // Starting to respond to handshake toggle - - // Reset the watchdog counter - s_missing_interrupt_count = 0; - - *should_context_switch = new_timer_add_work_callback_from_isr(prv_handle_handshake_pulse, NULL); -} - -static void prv_interrupts_enable(HRMDevice *dev, bool enable) { - mutex_assert_held_by_curr_task(dev->state->lock, true); - exti_configure_pin(dev->handshake_int, ExtiTrigger_Falling, prv_as7000_interrupt_handler); - exti_enable(dev->handshake_int); -} - -static void prv_log_running_apps(HRMDevice *dev) { - uint8_t app_ids = 0; - if (!prv_read_register(dev, ADDR_APP_IDS, &app_ids)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to get running apps"); - return; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Running applications:"); - if (app_ids == AS7000AppId_Idle) { - PBL_LOG(LOG_LEVEL_DEBUG, " - None (idle)"); - } else { - if (app_ids & AS7000AppId_Loader) { - PBL_LOG(LOG_LEVEL_DEBUG, " - Loader"); - } - if (app_ids & AS7000AppId_HRM) { - PBL_LOG(LOG_LEVEL_DEBUG, " - HRM"); - } - if (app_ids & AS7000AppId_PRV) { - PBL_LOG(LOG_LEVEL_DEBUG, " - PRV"); - } - if (app_ids & AS7000AppId_GSR) { - PBL_LOG(LOG_LEVEL_DEBUG, " - GSR"); - } - if (app_ids & AS7000AppId_NTC) { - PBL_LOG(LOG_LEVEL_DEBUG, " - NTC"); - } - } -} - -static bool prv_get_and_log_device_info(HRMDevice *dev, AS7000InfoRecord *info, - bool log_version) { - // get the device info - if (!prv_read_register_block(dev, ADDR_INFO_START, info, sizeof(AS7000InfoRecord))) { - return false; - } - - if (log_version) { - // print out the version information - PBL_LOG(LOG_LEVEL_INFO, "AS7000 enabled! Protocol v%" PRIu8 ".%" PRIu8 - ", SW v%" PRIu8 ".%" PRIu8 ".%" PRIu8 ", HW Rev %" PRIu8, - info->protocol_version_major, info->protocol_version_minor, - HRM_SW_VERSION_PART_MAJOR(info->sw_version_major), - HRM_SW_VERSION_PART_MINOR(info->sw_version_major), - info->sw_version_minor, info->hw_revision); - } - prv_log_running_apps(dev); - return true; -} - -static bool prv_is_app_running(HRMDevice *dev, AS7000AppId app) { - uint8_t running_apps = 0; - if (!prv_read_register(dev, ADDR_APP_IDS, &running_apps)) { - return false; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Apps running: 0x%"PRIx8, running_apps); - if (app == AS7000AppId_Idle) { - // no apps should be running - return running_apps == AS7000AppId_Idle; - } - return running_apps & app; -} - -//! Set the applications that should be running on the HRM. -//! -//! This commands the HRM to start or continue running any apps whose -//! flags are set, and to stop all apps whose flags are unset. Depending -//! on the firmware loaded onto the HRM, multiple apps can be run -//! concurrently by setting the logical OR of the App IDs. -static bool prv_set_running_apps(HRMDevice *dev, AS7000AppId apps) { - return prv_write_register(dev, ADDR_APP_IDS, apps); -} - -// Wait for the INT line to go low. Return true if it went low before timing out -static bool prv_wait_int_low(HRMDevice *dev) { - const int max_attempts = 2000; - int attempt; - for (attempt = 0; attempt < max_attempts; attempt++) { - if (!gpio_input_read(&dev->int_gpio)) { - break; - } - system_task_watchdog_feed(); - psleep(1); - } - return (attempt < max_attempts); -} - -// Wait for the INT line to go high. Return true if it went high before timing out -static bool prv_wait_int_high(HRMDevice *dev) { - const int max_attempts = 300; - int attempt; - for (attempt = 0; attempt < max_attempts; attempt++) { - if (gpio_input_read(&dev->int_gpio)) { - break; - } - system_task_watchdog_feed(); - psleep(1); - } - return (attempt < max_attempts); -} - -// NOTE: the caller must hold the device's state lock -static void prv_disable(HRMDevice *dev) { - mutex_assert_held_by_curr_task(dev->state->lock, true); - - // Turn off our watchdog timer - prv_disable_watchdog(dev); - - // Make sure interrupts are fully disabled before changing state - prv_interrupts_enable(dev, false); - // Put the INT pin back into a low power state that won't interfere with jtag using the pin - gpio_analog_init(&dev->int_gpio); - - PBL_LOG(LOG_LEVEL_DEBUG, "Shutting down device."); - switch (dev->state->enabled_state) { - case HRMEnabledState_PoweringOn: - new_timer_stop(dev->state->timer); - // Delay a bit so that we don't deassert the enable GPIO while in - // the loader and unintentionally activate force loader mode. - psleep(LOADER_READY_MAX_DELAY_MS); - // fallthrough - case HRMEnabledState_Enabled: - gpio_output_set(&dev->en_gpio, false); - dev->state->enabled_state = HRMEnabledState_Disabled; - break; - case HRMEnabledState_Disabled: - // nothing to do - break; - case HRMEnabledState_Uninitialized: - // the lock isn't even created yet - should never get here - // fallthrough - default: - WTF; - } - led_disable(LEDEnablerHRM); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_HRM_ON_TIME); -} - -// NOTE: the caller must hold the device's state lock -static void prv_enable(HRMDevice *dev) { - mutex_assert_held_by_curr_task(dev->state->lock, true); - if (dev->state->enabled_state == HRMEnabledState_Uninitialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Trying to enable HRM before initialization."); - - } else if (dev->state->enabled_state == HRMEnabledState_Disabled) { - led_enable(LEDEnablerHRM); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_HRM_ON_TIME, AnalyticsClient_System); - - // Enable the device and schedule a timer callback for when we can start communicating with it. - gpio_output_set(&dev->en_gpio, true); - dev->state->enabled_state = HRMEnabledState_PoweringOn; - dev->state->handshake_count = 0; - new_timer_start(dev->state->timer, NORMAL_BOOT_DELAY_MS, prv_enable_timer_cb, (void *)dev, - 0 /* flags */); - - interval_timer_init(&s_handshake_interval_timer, 900, 1100, 8); - - PBL_LOG(LOG_LEVEL_DEBUG, "Enabling AS7000..."); - } -} - -// This system task callback is triggered by the watchdog interrupt handler when we detect -// a frozen sensor -static void prv_watchdog_timer_system_cb(void *data) { - HRMDevice *dev = (HRMDevice *)data; - mutex_lock(dev->state->lock); - if (dev->state->enabled_state != HRMEnabledState_Enabled) { - goto exit; - } - - // If we have gone too long without getting an interrupt, let's reset the device - if (s_missing_interrupt_count >= AS7000_MAX_WATCHDOG_INTERRUPTS) { - PBL_LOG(LOG_LEVEL_ERROR, "Watchdog logic detected frozen sensor. Resetting now."); - analytics_inc(ANALYTICS_DEVICE_METRIC_HRM_WATCHDOG_TIMEOUT, AnalyticsClient_System); - prv_disable(dev); - psleep(SHUT_DOWN_DELAY_MS); - prv_enable(dev); - } -exit: - mutex_unlock(dev->state->lock); -} - -// This regular timer callback executes once a second. It is part of the watchdog logic used to -// detect if the sensor becomes unresponsive. -static void prv_watchdog_timer_cb(void *data) { - HRMDevice *dev = (HRMDevice *)data; - if (++s_missing_interrupt_count >= AS7000_MAX_WATCHDOG_INTERRUPTS) { - system_task_add_callback(prv_watchdog_timer_system_cb, (void *)dev); - } - if (s_missing_interrupt_count > 1) { - PBL_LOG(LOG_LEVEL_DEBUG, "Missing interrupt count: %"PRIu8" ", s_missing_interrupt_count); - } -} - -// Enable the watchdog timer. This gets enabled when we enable the sensor and detects if -// the sensor stops generating interrupts. -static void prv_enable_watchdog(HRMDevice *dev) { - mutex_assert_held_by_curr_task(dev->state->lock, true); - s_as7000_watchdog_timer = (RegularTimerInfo) { - .cb = prv_watchdog_timer_cb, - .cb_data = (void *)dev, - }; - s_missing_interrupt_count = 0; - regular_timer_add_seconds_callback(&s_as7000_watchdog_timer); -} - -static void prv_disable_watchdog(HRMDevice *dev) { - mutex_assert_held_by_curr_task(dev->state->lock, true); - regular_timer_remove_callback(&s_as7000_watchdog_timer); - s_missing_interrupt_count = 0; -} - -static bool prv_start_loader(HRMDevice *dev) { - // check if the loader is already running - if (!prv_is_app_running(dev, AS7000AppId_Loader)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Switching to loader"); - // we need to start the loader - if (!prv_set_running_apps(dev, AS7000AppId_Loader)) { - return false; - } - psleep(35); - - // make sure the loader is running - if (!prv_is_app_running(dev, AS7000AppId_Loader)) { - return false; - } - } - prv_log_running_apps(dev); - return true; -} - -static uint64_t prv_get_time_ms(void) { - time_t time_s; - uint16_t time_ms; - rtc_get_time_ms(&time_s, &time_ms); - return ((uint64_t)time_s) * 1000 + time_ms; -} - -static bool prv_wait_for_loader_ready(HRMDevice *dev) { - uint64_t end_time_ms = prv_get_time_ms() + LOADER_READY_MAX_DELAY_MS; - - do { - uint8_t status = 0; - if (!prv_read_register(dev, ADDR_LOADER_STATUS, &status)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed reading status"); - return false; - } - - if (status == AS7000LoaderStatus_Ready) { - // ready - return true; - } else if ((status != AS7000LoaderStatus_Busy1) && (status != AS7000LoaderStatus_Busy2)) { - // error - PBL_LOG(LOG_LEVEL_ERROR, "Error status: %"PRIx8, status); - return false; - } - psleep(1); - } while (prv_get_time_ms() < end_time_ms); - - PBL_LOG(LOG_LEVEL_ERROR, "Timed out waiting for the loader to be ready!"); - return false; -} - -static bool prv_flash_fw(HRMDevice *dev) { - // switch to the loader - if (!prv_start_loader(dev)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to start loader"); - return false; - } - - // wait for the loader to be ready - if (!prv_wait_for_loader_ready(dev)) { - PBL_LOG(LOG_LEVEL_ERROR, "Loader not ready"); - return false; - } - - const uint32_t image_length = - resource_size(SYSTEM_APP, RESOURCE_ID_AS7000_FW_IMAGE); - PBL_ASSERTN(image_length); - PBL_LOG(LOG_LEVEL_DEBUG, - "Loading FW image (%"PRIu32" bytes encoded)", image_length); - // Skip over the image header. - uint32_t cursor = sizeof(AS7000FWUpdateHeader); - while (cursor < image_length) { - // Make sure we can load enough data for a valid segment. There is - // always at least one data byte in each segment, so there must be - // strictly more data to read past the end of the header. - PBL_ASSERTN((image_length - cursor) > sizeof(AS7000FWSegmentHeader)); - // Read the header. - AS7000FWSegmentHeader segment_header; - if (resource_load_byte_range_system( - SYSTEM_APP, RESOURCE_ID_AS7000_FW_IMAGE, cursor, - (uint8_t *)&segment_header, sizeof(segment_header)) != - sizeof(segment_header)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read FW image! " - "(segment header @ 0x%" PRIx32 ")", cursor); - return false; - } - cursor += sizeof(segment_header); - // Write all the data bytes in the segment to the HRM. - uint16_t write_address = segment_header.address; - uint32_t bytes_remaining = segment_header.len_minus_1 + 1; - while (bytes_remaining) { - uint8_t chunk[MAX_HEX_DATA_BYTES]; - const size_t load_length = MIN(MAX_HEX_DATA_BYTES, bytes_remaining); - if (resource_load_byte_range_system( - SYSTEM_APP, RESOURCE_ID_AS7000_FW_IMAGE, cursor, chunk, load_length) - != load_length) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read FW image! " - "(segment data @ 0x%" PRIx32 ")", cursor); - return false; - } - - // Encode the chunk into an Intel HEX record and send it to the - // AS7000 loader. - uint8_t data_record[IHEX_RECORD_LENGTH(MAX_HEX_DATA_BYTES)]; - ihex_encode(data_record, IHEX_TYPE_DATA, write_address, - chunk, load_length); - if (!prv_write_register_block(dev, ADDR_LOADER_STATUS, data_record, - IHEX_RECORD_LENGTH(load_length))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to write hex record"); - return false; - } - - // Wait for the loader to be ready, indicating that the last - // record was successfully written. - if (!prv_wait_for_loader_ready(dev)) { - PBL_LOG(LOG_LEVEL_ERROR, "Loader not ready"); - return false; - } - - system_task_watchdog_feed(); - - cursor += load_length; - write_address += load_length; - bytes_remaining -= load_length; - } - } - - // Write the EOF record, telling the loader that the image has been - // fully written. - uint8_t eof_record[IHEX_RECORD_LENGTH(0)]; - ihex_encode(eof_record, IHEX_TYPE_EOF, 0, NULL, 0); - if (!prv_write_register_block(dev, ADDR_LOADER_STATUS, - eof_record, IHEX_RECORD_LENGTH(0))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to write EOF record"); - return false; - } - - return true; -} - -static bool prv_set_accel_sample_frequency(HRMDevice *dev, uint16_t freq) { - const uint8_t msb = (freq >> 8) & 0xff; - const uint8_t lsb = freq & 0xff; - return prv_write_register(dev, ADDR_ACCEL_SAMPLE_FREQ_MSB, msb) - && prv_write_register(dev, ADDR_ACCEL_SAMPLE_FREQ_LSB, lsb); -} - -static void prv_enable_system_task_cb(void *context) { - HRMDevice *dev = context; - mutex_lock(dev->state->lock); - if (dev->state->enabled_state == HRMEnabledState_Disabled) { - // Enable was cancelled before this callback fired. - goto done; - } else if (dev->state->enabled_state != HRMEnabledState_PoweringOn) { - PBL_LOG(LOG_LEVEL_ERROR, "Enable KernelBG callback fired while HRM was in " - "an unexpected state: %u", (unsigned int)dev->state->enabled_state); - WTF; - } - - AS7000InfoRecord info; - if (!prv_get_and_log_device_info(dev, &info, false /* log_version */)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to query AS7000 device info"); - goto failed; - } - - if (info.application_id == AS7000AppId_Loader) { - // This shouldn't happen. The application firmware should have been - // flashed during boot. - PBL_LOG(LOG_LEVEL_ERROR, - "AS7000 booted into loader! Something is very wrong."); - goto failed; - } - - // check that we can communicate with this chip - if (info.protocol_version_major != EXPECTED_PROTOCOL_VERSION_MAJOR) { - // we don't know how to talk with this chip, so bail - PBL_LOG(LOG_LEVEL_ERROR, "Unexpected protocol version!"); - goto failed; - } - - if (info.application_id != AS7000AppId_Idle) { - PBL_LOG(LOG_LEVEL_ERROR, - "Unexpected application running: 0x%" PRIx8, info.application_id); - goto failed; - } - - // the INT line should be low - if (gpio_input_read(&dev->int_gpio)) { - PBL_LOG(LOG_LEVEL_ERROR, "INT line is not low!"); - goto failed; - } - - // Set the accelerometer sample frequency - PBL_LOG(LOG_LEVEL_DEBUG, "Setting accel frequency"); - PBL_ASSERTN(HRM_MANAGER_ACCEL_RATE_MILLIHZ >= 10000 && HRM_MANAGER_ACCEL_RATE_MILLIHZ <= 20000); - if (!prv_set_accel_sample_frequency(dev, HRM_MANAGER_ACCEL_RATE_MILLIHZ)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to set accel frequency"); - goto failed; - } - - // Set the presence detection threshold - uint8_t pres_detect_thrsh; - WatchInfoColor model_color = mfg_info_get_watch_color(); - switch (model_color) { - case WATCH_INFO_COLOR_PEBBLE_2_HR_BLACK: - case WATCH_INFO_COLOR_PEBBLE_2_HR_FLAME: - pres_detect_thrsh = PRES_DETECT_THRSH_BLACK; - break; - case WATCH_INFO_COLOR_PEBBLE_2_HR_WHITE: - case WATCH_INFO_COLOR_PEBBLE_2_HR_LIME: - case WATCH_INFO_COLOR_PEBBLE_2_HR_AQUA: - pres_detect_thrsh = PRES_DETECT_THRSH_WHITE; - break; - default: - pres_detect_thrsh = 1; - break; - } - if (!prv_write_register(dev, ADDR_PRES_DETECT_THRSH, pres_detect_thrsh)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to set presence detection threshold"); - goto failed; - } - - // start the HRM app - PBL_LOG(LOG_LEVEL_DEBUG, "Starting HRM app"); - if (!prv_set_running_apps(dev, AS7000AppId_HRM)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to start HRM app!"); - goto failed; - } - - // Configure the int_gpio pin only when we're going to use it, as this pin is shared with - // the jtag pins and therefore can cause issues when flashing firmwares onto bigboards. - gpio_input_init_pull_up_down(&dev->int_gpio, GPIO_PuPd_UP); - - // wait for the INT line to go high indicating the Idle app has ended - if (!prv_wait_int_high(dev)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timed-out waiting for the Idle app to end but we " - "probably just missed it"); - // TODO: The line only goes high for a few ms. If there is any kind of context switch while we - // wait for the line to go high we will miss this. Let's fix this the right way in PBL-41812 - // (check for this change via an ISR) for 4.2 but just go with the smallest change for 4.1 - } - - // wait for the INT line to go low indicating the HRM app is ready - if (!prv_wait_int_low(dev)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timed-out waiting for the HRM app to be ready"); - goto failed; - } - - // get the running apps (also triggers the app to start) - prv_log_running_apps(dev); - - // HRM app is ready, enable handshake interrupts - prv_interrupts_enable(dev, true); - - // We are now fully enabled - dev->state->enabled_state = HRMEnabledState_Enabled; - - // Enable the watchdog - prv_enable_watchdog(dev); - - goto done; - -failed: - prv_disable(dev); -done: - mutex_unlock(dev->state->lock); -} - -static void prv_enable_timer_cb(void *context) { - system_task_add_callback(prv_enable_system_task_cb, context); -} - -void hrm_init(HRMDevice *dev) { - PBL_ASSERTN(dev->state->enabled_state == HRMEnabledState_Uninitialized); - - dev->state->lock = mutex_create(); - dev->state->timer = new_timer_create(); - dev->state->enabled_state = HRMEnabledState_Disabled; - - // Boot up the HRM so that we can read off the firmware version to see - // if it needs to be updated. - - // First, read the version from the firmware update resource. - const uint32_t update_length = resource_size( - SYSTEM_APP, RESOURCE_ID_AS7000_FW_IMAGE); - if (update_length == 0) { - // We don't have a firmware to write so there's no point in booting - // the HRM. - PBL_LOG(LOG_LEVEL_DEBUG, "No HRM FW update available"); - return; - } - - AS7000FWUpdateHeader image_header; - if (resource_load_byte_range_system( - SYSTEM_APP, RESOURCE_ID_AS7000_FW_IMAGE, 0, (uint8_t *)&image_header, - sizeof(image_header)) != sizeof(image_header)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read HRM FW image header!"); - return; - } - PBL_LOG(LOG_LEVEL_DEBUG, "FW update image is v%" PRIu8 ".%" PRIu8 ".%" PRIu8, - HRM_SW_VERSION_PART_MAJOR(image_header.sw_version_major), - HRM_SW_VERSION_PART_MINOR(image_header.sw_version_major), - image_header.sw_version_minor); - - // Now that we know what version the image is, actually boot up the - // HRM so we can read off the version. - - PBL_LOG(LOG_LEVEL_DEBUG, "Booting AS7000..."); - - gpio_output_init(&dev->en_gpio, GPIO_OType_PP, GPIO_Speed_2MHz); -#if HRM_FORCE_FLASH - // Force the HRM into loader mode which will cause the firmware to be - // reflashed on every boot. If the HRM is loaded with a broken - // firmware which doesn't enter standby when the enable pin is high, - // the board will need to be power-cycled (entering standby/shutdown - // is sufficient) in order to get force-flashing to succeed. - gpio_output_set(&dev->en_gpio, false); - psleep(50); - gpio_output_set(&dev->en_gpio, true); - psleep(20); - gpio_output_set(&dev->en_gpio, false); - psleep(20); -#else - gpio_output_set(&dev->en_gpio, true); - psleep(NORMAL_BOOT_DELAY_MS); -#endif - - AS7000InfoRecord hrm_info; - if (!prv_get_and_log_device_info(dev, &hrm_info, true /* log_version */)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read AS7000 version info!"); - goto cleanup; - } - - if (hrm_info.application_id == AS7000AppId_Loader || - hrm_info.sw_version_major != image_header.sw_version_major || - hrm_info.sw_version_minor != image_header.sw_version_minor) { - // We technically could leave the firmware on the HRM alone if the - // minor version in the chip is newer than in the update image, but - // for sanity's sake let's always make sure the HRM firmware is in - // sync with the version shipped with the Pebble firmware. - PBL_LOG(LOG_LEVEL_DEBUG, "AS7000 firmware version mismatch. Flashing..."); - if (!prv_flash_fw(dev)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to flash firmware"); - goto cleanup; - } - // We need to wait for the HRM to reboot into the application before - // releasing the enable GPIO. If the loader sees the GPIO released - // during boot, it will activate "force loader mode" and fall back - // into the loader. Since we're waiting anyway, we might as well - // query the version info again to make sure the update took. - PBL_LOG(LOG_LEVEL_DEBUG, "Firmware flashed! Waiting for reboot..."); - gpio_output_set(&dev->en_gpio, true); - psleep(LOADER_REBOOT_DELAY_MS); - if (!prv_get_and_log_device_info(dev, &hrm_info, true /* log_version */)) { - PBL_LOG(LOG_LEVEL_ERROR, - "Failed to read AS7000 version info after flashing!"); - goto cleanup; - } - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "AS7000 firmware is up to date."); - } - -cleanup: - // At this point the HRM should either be booted and running the - // application firmware, at which point deasserting the enable GPIO - // will signal it to shut down, or the firmware update failed and the - // loader is running, where deasserting the GPIO will not do much. - gpio_output_set(&dev->en_gpio, false); -} - -void hrm_enable(HRMDevice *dev) { - if (!dev->state->lock) { - PBL_LOG(LOG_LEVEL_DEBUG, "Not an HRM Device."); - return; - } - - mutex_lock(dev->state->lock); - prv_enable(dev); - mutex_unlock(dev->state->lock); -} - -void hrm_disable(HRMDevice *dev) { - if (!dev->state->lock) { - PBL_LOG(LOG_LEVEL_DEBUG, "Not an HRM Device."); - return; - } - - mutex_lock(dev->state->lock); - prv_disable(dev); - mutex_unlock(dev->state->lock); -} - -bool hrm_is_enabled(HRMDevice *dev) { - return (dev->state->enabled_state == HRMEnabledState_Enabled - || dev->state->enabled_state == HRMEnabledState_PoweringOn); -} - -void as7000_get_version_info(HRMDevice *dev, AS7000InfoRecord *info_out) { - if (!dev->state->lock) { - PBL_LOG(LOG_LEVEL_DEBUG, "Not an HRM Device."); - return; - } - - mutex_lock(dev->state->lock); - if (!prv_get_and_log_device_info(dev, info_out, true /* log_version */)) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to read AS7000 version info"); - } - mutex_unlock(dev->state->lock); -} - -// Prompt Commands -// =============== - -#include "console/prompt.h" -#include - -void command_hrm_wipe(void) { - // HEX records to write 0xFFFFFFFF to the magic number region. - const char *erase_magic_record = ":047FFC00FFFFFFFF85"; - const char *eof_record = ":00000001FF"; - - mutex_lock(HRM->state->lock); - gpio_output_set(&HRM->en_gpio, true); - psleep(NORMAL_BOOT_DELAY_MS); - - bool success = prv_start_loader(HRM) && - prv_wait_for_loader_ready(HRM) && - prv_write_register_block(HRM, ADDR_LOADER_STATUS, - erase_magic_record, - strlen(erase_magic_record)) && - prv_wait_for_loader_ready(HRM) && - prv_write_register_block(HRM, ADDR_LOADER_STATUS, - eof_record, strlen(eof_record)) && - prv_wait_for_loader_ready(HRM); - - gpio_output_set(&HRM->en_gpio, false); - mutex_unlock(HRM->state->lock); - - prompt_send_response(success? "HRM Firmware invalidated" : "ERROR"); -} - -// Simulate a frozen sensor for testing the watchdog recovery logic -void command_hrm_freeze(void) { - HRMDevice *dev = HRM; - mutex_lock(dev->state->lock); - if (dev->state->enabled_state == HRMEnabledState_Enabled) { - prv_interrupts_enable(dev, false); - gpio_analog_init(&dev->int_gpio); - led_disable(LEDEnablerHRM); - } - mutex_unlock(dev->state->lock); -} diff --git a/src/fw/drivers/hrm/as7000/as7000.h b/src/fw/drivers/hrm/as7000/as7000.h deleted file mode 100644 index b15b3d3455..0000000000 --- a/src/fw/drivers/hrm/as7000/as7000.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/hrm.h" - -#include "board/board.h" -#include "drivers/i2c_definitions.h" -#include "os/mutex.h" -#include "services/common/new_timer/new_timer.h" -#include "util/attributes.h" - -#include - - -typedef enum HRMEnabledState { - HRMEnabledState_Uninitialized = 0, - HRMEnabledState_Disabled, - HRMEnabledState_PoweringOn, - HRMEnabledState_Enabled, -} HRMEnabledState; - -typedef struct HRMDeviceState { - HRMEnabledState enabled_state; - PebbleMutex *lock; - TimerID timer; - uint32_t handshake_count; -} HRMDeviceState; - -typedef const struct HRMDevice { - HRMDeviceState *state; - ExtiConfig handshake_int; - InputConfig int_gpio; - OutputConfig en_gpio; - I2CSlavePort *i2c_slave; -} HRMDevice; - -typedef struct PACKED AS7000InfoRecord { - uint8_t protocol_version_major; - uint8_t protocol_version_minor; - uint8_t sw_version_major; - uint8_t sw_version_minor; - uint8_t application_id; - uint8_t hw_revision; -} AS7000InfoRecord; - -//! Fills a struct which contains version info about the AS7000 -//! This should probably only be used by the HRM Demo app -void as7000_get_version_info(HRMDevice *dev, AS7000InfoRecord *info_out); diff --git a/src/fw/drivers/hrm/gh3x2x.c b/src/fw/drivers/hrm/gh3x2x.c new file mode 100644 index 0000000000..66b3a6235d --- /dev/null +++ b/src/fw/drivers/hrm/gh3x2x.c @@ -0,0 +1,490 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "gh3x2x.h" + +#include "drivers/hrm.h" +#include "board/board.h" +#include "kernel/util/sleep.h" +#include "system/logging.h" + +#ifdef HRM_USE_GH3X2X +#include "math.h" +#include "kernel/util/delay.h" +#include "kernel/events.h" +#include "pbl/services/system_task.h" +#include "pbl/services/hrm/hrm_manager.h" + +#include "gh_demo.h" +#include "gh_demo_inner.h" +#include "gh3x2x_demo_mp.h" +#endif // HRM_USE_GH3X2X + +PBL_LOG_MODULE_DEFINE(driver_hrm_gh3x2x, CONFIG_DRIVER_HRM_LOG_LEVEL); + +void gh3026_reset_pin_ctrl(uint8_t pin_level) { +#if GH3X2X_RESET_PIN_CTRLBY_NPM1300 + NPM1300_OPS.gpio_set(Npm1300_Gpio3, pin_level); +#endif +} + +#ifdef HRM_USE_GH3X2X + +#define GH3X2X_LOG_ENABLE 0 +#define GH3X2X_FIFO_WATERMARK_CONFIG 80 +#define GH3X2X_HR_SAMPLING_RATE 25 + +static volatile uint32_t s_hrm_int_flag = false; +static volatile uint32_t s_hrm_timer_flag = false; + +// GH3X2X library glue code + +void gh3026_reset_pin_init(void) {} + +void gh3026_i2c_init(void) {} + +void gh3026_i2c_write(uint8_t device_id, const uint8_t write_buffer[], uint16_t length) { + i2c_use(HRM->i2c); + i2c_write_block(HRM->i2c, length, write_buffer); + i2c_release(HRM->i2c); +} + +void gh3026_i2c_read(uint8_t device_id, const uint8_t write_buffer[], uint16_t write_length, + uint8_t read_buffer[], uint16_t read_length) { + i2c_use(HRM->i2c); + i2c_write_block(HRM->i2c, write_length, write_buffer); + i2c_read_block(HRM->i2c, read_length, read_buffer); + i2c_release(HRM->i2c); +} + +static void prv_conv_fs4g_mg_to_lsb512(AccelRawData* data) { + //hrm use 512lsb/g, so convert the mg data to lsb by coef 1.953f(1000/512) + data->x = (int16_t)data->x/1.953f; + data->y = (int16_t)data->y/1.953f; + data->z = (int16_t)data->z/1.953f; +} + +void gh3026_gsensor_data_get(STGsensorRawdata gsensor_buffer[], GU16 *gsensor_buffer_index) { + HRMAccelData* acc = hrm_manager_get_accel_data(); + GU16 count = *gsensor_buffer_index = acc->num_samples; + if(count > __GSENSOR_DATA_BUFFER_SIZE__) count = __GSENSOR_DATA_BUFFER_SIZE__; + for (uint16_t i = 0; i < count; ++i) { + prv_conv_fs4g_mg_to_lsb512(&acc->data[i]); + memcpy(&gsensor_buffer[i], &acc->data[i], sizeof(STGsensorRawdata)); + } + hrm_manager_release_accel_data(); +} + +static void gh3026_int_callback_function(void *context) { + s_hrm_int_flag = false; + Gh3x2xDemoInterruptProcess(); +} + +static void gh3026_int_irq_callback(bool *should_context_switch) { + hal_gh3x2x_int_handler_call_back(); + + if (s_hrm_int_flag == false) { + if (system_task_add_callback_from_isr(gh3026_int_callback_function, NULL, + should_context_switch)) { + s_hrm_int_flag = true; + } + } else { + *should_context_switch = false; + } +} + +void gh3026_int_pin_init(void) { + exti_configure_pin(HRM->int_exti, ExtiTrigger_Falling, gh3026_int_irq_callback); + exti_enable(HRM->int_exti); +} + +void gh3x2x_print_fmt(const char *fmt, ...) { +#if GH3X2X_LOG_ENABLE + char buffer[128]; + va_list ap; + va_start(ap, fmt); + vsniprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + PBL_LOG_ALWAYS("%s", buffer); +#endif +} + +void gh3x2x_hr_result_report(uint8_t bpm, uint8_t quality) { + HRMData hrm_data = {0}; + + PBL_LOG_DBG("GH3X2X BPM %" PRIu8 " (quality=%" PRIu8 ", wear=%u)", bpm, quality, HRM->state->is_wear); + + hrm_data.features = HRMFeature_BPM; + + if (!HRM->state->is_wear) { + hrm_data.hrm_quality = HRMQuality_OffWrist; + } else { + hrm_data.hrm_bpm = bpm; + + if (quality >= 98U) { + hrm_data.hrm_quality = HRMQuality_Excellent; + } else if (quality >= 90U) { + hrm_data.hrm_quality = HRMQuality_Good; + } else if (quality >= 80U) { + hrm_data.hrm_quality = HRMQuality_Acceptable; + } else if (quality >= 70U) { + hrm_data.hrm_quality = HRMQuality_Poor; + } else { + hrm_data.hrm_quality = HRMQuality_Worst; + } + } + + hrm_manager_new_data_cb(&hrm_data); +} + +void gh3x2x_spo2_result_report(uint8_t pct, uint8_t quality) { + HRMData hrm_data = {0}; + + PBL_LOG_DBG("GH3X2X SpO2 %" PRIu8 " (quality=%" PRIu8 ", wear=%u)", pct, quality, HRM->state->is_wear); + + hrm_data.features = HRMFeature_SpO2; + + if (!HRM->state->is_wear) { + hrm_data.spo2_quality = HRMQuality_OffWrist; + } else { + hrm_data.spo2_percent = pct; + + if (quality >= 98U) { + hrm_data.spo2_quality = HRMQuality_Excellent; + } else if (quality >= 90U) { + hrm_data.spo2_quality = HRMQuality_Good; + } else if (quality >= 80U) { + hrm_data.spo2_quality = HRMQuality_Acceptable; + } else if (quality >= 70U) { + hrm_data.spo2_quality = HRMQuality_Poor; + } else { + hrm_data.spo2_quality = HRMQuality_Worst; + } + } + + hrm_manager_new_data_cb(&hrm_data); +} + +void gh3x2x_wear_evt_notify(bool is_wear) { + PBL_LOG_DBG("GH3X2X wear state: %d", is_wear); + + HRM->state->is_wear = is_wear; +} + +#ifdef CONFIG_GH3X2X_TUNING_SERVICE_ENABLED +void gh3x2x_timer_init(uint32_t period_ms) { + if (HRM) { + HRM->state->timer_period_ms = period_ms; + } +} + +static void gh3x2x_timer_callback(void* data) { + uint32_t param = (uint32_t)data; + if (param != 0x87965421) { + // Coalesce repeated timer firings - only queue one callback at a time + if (s_hrm_timer_flag == false) { + if (system_task_add_callback(gh3x2x_timer_callback, (void*)0x87965421)) { + s_hrm_timer_flag = true; + } + } + return; + } + s_hrm_timer_flag = false; + Gh3x2xSerialSendTimerHandle(); +} + +static void gh3x2x_timer_start_handle(void* arg) { + if (HRM == NULL || HRM->state->timer != NULL) { + return; + } + if (HRM->state->timer_period_ms == 0) { + return; + } + HRM->state->timer = app_timer_register_repeatable(HRM->state->timer_period_ms, gh3x2x_timer_callback, NULL, true); +} + +static void gh3x2x_timer_stop_handle(void* arg) { + if (HRM && HRM->state->timer) { + app_timer_cancel(HRM->state->timer); + HRM->state->timer = NULL; + } +} + +void gh3x2x_timer_start(void) { + return; + PebbleEvent e = { + .type = PEBBLE_CALLBACK_EVENT, + .callback.callback = gh3x2x_timer_start_handle, + }; + event_put(&e); +} + +void gh3x2x_timer_stop(void) { + return; + PebbleEvent e = { + .type = PEBBLE_CALLBACK_EVENT, + .callback.callback = gh3x2x_timer_stop_handle, + }; + event_put(&e); +} + +static void gh3x2x_ble_data_recv_handle(void *context) { + if (context == NULL) { + return; + } + + uint32_t data_len; + uint8_t *p_data = (uint8_t*)context; + memcpy(&data_len, p_data, sizeof(uint32_t)); + p_data += sizeof(uint32_t); + Gh3x2xDemoProtocolProcess((GU8*)p_data, data_len); + free(context); +} + +bool gh3x2x_ble_data_recv(void* context) { + if (context == NULL) { + return false; + } + + if (!system_task_add_callback(gh3x2x_ble_data_recv_handle, context)) { + return false; + } + return true; +} +#endif + +// GH3X2X calibration/factory testing + +void gh3x2x_rawdata_notify(uint32_t *p_rawdata, uint32_t data_count) { +#ifdef CONFIG_MFG + HRMDevice* p_dev = HRM; + if (p_dev == NULL || p_dev->state->enabled == false) { + return; + } + + GH3x2xFTData* p_factory = p_dev->state->factory; + if (p_factory == NULL) { + return; + } + uint32_t mode = p_factory->test_mode; + if (mode == 0) { + return; + } + + // save ppg raw data, 80 samples for each channel + uint32_t idx; + data_count /= HRM_PPG_CH_NUM; + while (data_count--) { + if (p_factory->drop_count > 0) { + p_factory->drop_count--; + p_rawdata += HRM_PPG_CH_NUM; + } else { + if (p_factory->wpos >= HRM_PPG_FACTORY_TEST_FIFO_LEN) { + p_factory->wpos = 0; + } + for (idx=0; idxppg_array[idx][p_factory->wpos] = *p_rawdata++; + } + + p_factory->wpos++; + if (p_factory->count < HRM_PPG_FACTORY_TEST_FIFO_LEN) { + p_factory->count++; + } + } + } + if (p_factory->count < HRM_PPG_FACTORY_TEST_FIFO_LEN) { + return; + } + + uint32_t i; + uint32_t ppg_avg[HRM_PPG_CH_NUM]; + uint64_t total[HRM_PPG_CH_NUM]; + HRMData hrm_data = {0}; + memset(total, 0, sizeof(total)); + for (idx=0; idxcount; ++i) { + total[idx] += p_factory->ppg_array[idx][i]; + } + // calcu avr for each channel + ppg_avg[idx] = total[idx] / p_factory->count; + } + + //let keep the factory test data report 2hz + static int cnt = 0; + if (cnt++ % 25) return; + if (mode == GH3X2X_FUNCTION_TEST1) { + hrm_data.features = HRMFeature_CTR; + // calcu CTR: result = ((ppg_avg-2^23))*1800*1000/(20*10*2*(2^23)); + // Green >= 28; IR >= 36; Red >= 36 + for (i=0; iresult[i] = ((double)(ppg_avg[i] - (1<<23)) * 4500) / (1<<23); + hrm_data.ctr[i] = p_factory->result[i]; + } + hrm_manager_new_data_cb(&hrm_data); + } else if (mode == GH3X2X_FUNCTION_TEST2) { + hrm_data.features = HRMFeature_Leakage; + // calcu leakage: result = (ppg_avg-(2^23))*1800*1000/(20*100*2*(2^23)); + for (i=0; iresult[i] = ((double)(ppg_avg[i] - (1<<23)) * 450) / (1<<23); + hrm_data.leakage[i] = p_factory->result[i]; + } + hrm_manager_new_data_cb(&hrm_data); + } else { + + ; + } +#endif +} + +#ifdef CONFIG_MFG +void gh3x2x_factory_test_enable(HRMDevice *dev, GH3x2xFTType test_type) { + uint32_t mode = 0; + if (test_type == HRM_FACTORY_TEST_CTR) { // CTR + mode = GH3X2X_FUNCTION_TEST1; + } else if (test_type == HRM_FACTORY_TEST_LIGHT_LEAK) { // leakage + mode = GH3X2X_FUNCTION_TEST2; + } else if (test_type == HRM_FACTORY_TEST_HSM) { // noise + mode = GH3X2X_FUNCTION_HSM; + } else { + return; + } + + uint32_t* ppg_data; + GH3x2xFTData* p_factory = (GH3x2xFTData*)malloc(sizeof(GH3x2xFTData) + sizeof(uint32_t)*HRM_PPG_FACTORY_TEST_FIFO_LEN*HRM_PPG_CH_NUM); + if (p_factory == NULL) { + PBL_LOG_ERR("malloc failed."); + return; + } + memset(p_factory, 0, sizeof(GH3x2xFTData)); + p_factory->drop_count = 30; + p_factory->test_mode = mode; + if (dev->state->factory != NULL) { + free(dev->state->factory); + } + ppg_data = (uint32_t*)(p_factory + 1); + for (uint32_t i=0; ippg_array[i] = ppg_data + HRM_PPG_FACTORY_TEST_FIFO_LEN*i; + } + dev->state->factory = p_factory; + + dev->state->enabled = true; + s_hrm_int_flag = false; + Gh3x2xDemoStopSampling(0xFFFFFFFF); + Gh3x2xDemoStartSamplingWithCfgSwitch(mode, 1); +} + +//shoud be called in system task +static void gh3x2x_ft_ctr_start_handle(void* data) { + gh3x2x_factory_test_enable(HRM, HRM_FACTORY_TEST_CTR); +} + +void gh3x2x_start_ft_ctr(void) { + system_task_add_callback(gh3x2x_ft_ctr_start_handle, NULL); +} + +//shoud be called in system task +static void gh3x2x_ft_leakage_start_handle(void* data) { + gh3x2x_factory_test_enable(HRM, HRM_FACTORY_TEST_LIGHT_LEAK); +} + +void gh3x2x_start_ft_leakage(void) { + system_task_add_callback(gh3x2x_ft_leakage_start_handle, NULL); +} + +//shoud be called in system task +static void gh3x2x_factory_test_disable_handle(void *data) { + HRMDevice *dev = (HRMDevice *)data; + dev->state->enabled = false; + Gh3x2xDemoStopSampling(0xFFFFFFFF); + if (dev->state->factory != NULL) { + free(dev->state->factory); + dev->state->factory = NULL; + } +} + +void gh3x2x_factory_test_disable(void) { + system_task_add_callback(gh3x2x_factory_test_disable_handle, (void*)HRM); +} + +uint8_t gh3x2x_factory_result_get(float* p_result) +{ + HRMDevice* p_dev = HRM; + if (p_result) { + GH3x2xFTData* p_factory = p_dev->state->factory; + if (p_factory != NULL && p_factory->count >= HRM_PPG_FACTORY_TEST_FIFO_LEN) { + memcpy(p_result, p_factory->result, sizeof(float)*HRM_PPG_FACTORY_TEST_FIFO_LEN); + return HRM_PPG_FACTORY_TEST_FIFO_LEN; + } + } + return 0; +} + +void gh3x2x_set_work_mode(int32_t mode) { + HRMDeviceState* state = HRM->state; + //always enable soft adt + state->work_mode = mode | GH3X2X_FUNCTION_SOFT_ADT_IR; +} +#endif // CONFIG_MFG + +#endif // HRM_USE_GH3X2X + +// HRM interface + +void hrm_init(HRMDevice *dev) { +#ifdef HRM_USE_GH3X2X + int ret; + + ret = Gh3x2xDemoInit(); + if (ret != 0) { + PBL_LOG_ERR("GH3X2X failed to initialize"); + return; + } +#else + gh3026_reset_pin_ctrl(0); + gpio_input_init_pull_up_down(&dev->int_input, GPIO_PuPd_DOWN); +#endif + + dev->state->is_wear = false; + dev->state->initialized = true; +} + +bool hrm_enable(HRMDevice *dev) { +#ifdef HRM_USE_GH3X2X + if (!dev->state->initialized) { + return false; + } + + s_hrm_int_flag = false; + + dev->state->work_mode = GH3X2X_FUNCTION_HR | GH3X2X_FUNCTION_SOFT_ADT_GREEN; +#ifdef CONFIG_MFG + dev->state->work_mode = GH3X2X_FUNCTION_HR | GH3X2X_FUNCTION_SPO2 | GH3X2X_FUNCTION_SOFT_ADT_IR; +#endif + + GH3X2X_FifoWatermarkThrConfig(GH3X2X_FIFO_WATERMARK_CONFIG); + GH3X2X_SetSoftEvent(GH3X2X_SOFT_EVENT_NEED_FORCE_READ_FIFO); + Gh3x2xDemoFunctionSampleRateSet(GH3X2X_FUNCTION_HR, GH3X2X_HR_SAMPLING_RATE); + Gh3x2xDemoStartSampling(dev->state->work_mode); + + dev->state->enabled = true; + return true; +#else + return false; +#endif +} + +void hrm_disable(HRMDevice *dev) { +#ifdef HRM_USE_GH3X2X + if (!dev->state->initialized) { + return; + } + + Gh3x2xDemoStopSampling(0xFFFFFFFF); +#endif + dev->state->enabled = false; +} + +bool hrm_is_enabled(HRMDevice *dev) { + return dev->state->enabled; +} \ No newline at end of file diff --git a/src/fw/drivers/hrm/gh3x2x.h b/src/fw/drivers/hrm/gh3x2x.h new file mode 100644 index 0000000000..6a99923dda --- /dev/null +++ b/src/fw/drivers/hrm/gh3x2x.h @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include +#include "drivers/exti.h" +#include "drivers/gpio.h" +#include "drivers/i2c.h" +#include "applib/app_timer.h" + +#ifdef CONFIG_BOARD_FAMILY_OBELIX +// FIXME(OBELIX): Provide proper GPIO layer abstraction +#define GH3X2X_RESET_PIN_CTRLBY_NPM1300 1 +#endif + +#define HRM_PPG_CH_NUM 6 +#define HRM_PPG_FACTORY_TEST_FIFO_LEN 80 +typedef struct { + double result[HRM_PPG_CH_NUM]; + uint16_t test_mode; + uint16_t drop_count; + uint16_t wpos; + uint16_t count; + uint32_t *ppg_array[HRM_PPG_CH_NUM]; +} GH3x2xFTData; + +typedef struct HRMDeviceState { + bool enabled; + bool is_wear; + int32_t work_mode; + uint16_t timer_period_ms; + AppTimer *timer; + GH3x2xFTData* factory; + bool initialized; +} HRMDeviceState; + +typedef const struct HRMDevice { + HRMDeviceState *state; + I2CSlavePort *i2c; + ExtiConfig int_exti; + InputConfig int_input; + OutputConfig reset_gpio; +} HRMDevice; + + +bool gh3x2x_ble_data_recv(void* context); +void gh3x2x_wear_evt_notify(bool is_wear); +void gh3x2x_rawdata_notify(uint32_t *p_rawdata, uint32_t data_count); + +void gh3x2x_wear_evt_notify(bool is_wear); +bool gh3x2x_is_wear_get(void); + +typedef enum { + HRM_FACTORY_TEST_NONE, + HRM_FACTORY_TEST_CTR, + HRM_FACTORY_TEST_LIGHT_LEAK, + HRM_FACTORY_TEST_HSM, +} GH3x2xFTType; + +void gh3x2x_start_ft_ctr(void); +void gh3x2x_start_ft_leakage(void); +void gh3x2x_factory_test_disable(); +void gh3x2x_set_work_mode(int32_t mode); + diff --git a/src/fw/drivers/hrm/stub.c b/src/fw/drivers/hrm/stub.c new file mode 100644 index 0000000000..d8c47077d7 --- /dev/null +++ b/src/fw/drivers/hrm/stub.c @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "stub.h" + +void hrm_init(HRMDevice *dev) { +} + +bool hrm_enable(HRMDevice *dev) { + dev->state->enabled = true; + return true; +} + +void hrm_disable(HRMDevice *dev) { + dev->state->enabled = false; +} + +bool hrm_is_enabled(HRMDevice *dev) { + return dev->state->enabled; +} diff --git a/src/fw/drivers/hrm/stub.h b/src/fw/drivers/hrm/stub.h new file mode 100644 index 0000000000..631ac78340 --- /dev/null +++ b/src/fw/drivers/hrm/stub.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "drivers/hrm.h" + +typedef struct HRMDeviceState { + bool enabled; +} HRMDeviceState; + +typedef const struct HRMDevice { + HRMDeviceState *state; +} HRMDevice; diff --git a/src/fw/drivers/hrm/wscript_build b/src/fw/drivers/hrm/wscript_build new file mode 100644 index 0000000000..d5d2a94079 --- /dev/null +++ b/src/fw/drivers/hrm/wscript_build @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes'] + +if bld.env.CONFIG_HRM_GH3X2X: + sources.append('gh3x2x.c') + if bld.env.VARIANT != 'prf' or bld.env.CONFIG_MFG: + bld.env.DEFINES.append('HRM_USE_GH3X2X') + use.append('gh3x2x') +elif bld.env.CONFIG_HRM_STUB: + sources.append('stub.c') + +bld.objects( + name='driver_hrm', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_hrm') diff --git a/src/fw/drivers/i2c.c b/src/fw/drivers/i2c.c deleted file mode 100644 index 1534b4ad23..0000000000 --- a/src/fw/drivers/i2c.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "i2c.h" -#include "i2c_definitions.h" -#include "i2c_hal.h" - -#include "board/board.h" -#include "debug/power_tracking.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/rtc.h" -#include "FreeRTOS.h" -#include "kernel/pbl_malloc.h" -#include "os/tick.h" -#include "kernel/util/sleep.h" -#include "kernel/util/stop.h" -#include "os/mutex.h" -#include "semphr.h" -#include "services/common/analytics/analytics.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#if CAPABILITY_HAS_PMIC -#include "drivers/pmic.h" -#endif - -#include - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - -#define I2C_ERROR_TIMEOUT_MS (1000) -#define I2C_TIMEOUT_ATTEMPTS_MAX (2 * 1000 * 1000) - -// MFI NACKs while busy. We delay ~1ms between retries so this is approximately a 1000ms timeout. -// The longest operation of the MFi chip is "start signature generation", which seems to take -// 223-224 NACKs, but sometimes for unknown reasons it can take much longer. -#define I2C_NACK_COUNT_MAX (1000) - -typedef enum { - Read, - Write -} TransferDirection; - -typedef enum { - SendRegisterAddress, // Send a register address, followed by a repeat start for reads - NoRegisterAddress // Do not send a register address -} TransferType; - -static uint32_t s_max_transfer_duration_ticks; - -static void prv_analytics_track_i2c_error(void); - -/*----------------SEMAPHORE/LOCKING FUNCTIONS--------------------------*/ - -static bool prv_semaphore_take(I2CBusState *bus) { - return (xSemaphoreTake(bus->event_semaphore, 0) == pdPASS); -} - -static bool prv_semaphore_wait(I2CBusState *bus) { - TickType_t timeout_ticks = milliseconds_to_ticks(I2C_ERROR_TIMEOUT_MS); - return (xSemaphoreTake(bus->event_semaphore, timeout_ticks) == pdPASS); -} - -static void prv_semaphore_give(I2CBusState *bus) { - // If this fails, something is very wrong - (void)xSemaphoreGive(bus->event_semaphore); -} - -static portBASE_TYPE prv_semaphore_give_from_isr(I2CBusState *bus) { - portBASE_TYPE should_context_switch = pdFALSE; - (void)xSemaphoreGiveFromISR(bus->event_semaphore, &should_context_switch); - return should_context_switch; -} - -/*-------------------BUS/PIN CONFIG FUNCTIONS--------------------------*/ -// FIXME: These rail control functions should be moved to board-specific implementations -// https://pebbletechnology.atlassian.net/browse/PBL-32232 - -#if CAPABILITY_HAS_PMIC -void i2c_rail_ctl_pmic(I2CBus *bus, bool enable) { - set_ldo3_power_state(enable); -} -#endif - -void i2c_rail_ctl_pin(I2CBus *bus, bool enable) { - gpio_output_set(&bus->rail_gpio, enable); -} - -static void prv_rail_ctl(I2CBus *bus, bool enable) { - bus->rail_ctl_fn(bus, enable); - if (enable) { - // FIXME: The power tracking data is going to be bogus for any board with more than one bus - // with controllable power. - // https://pebbletechnology.atlassian.net/browse/PBL-32232 should address this - power_tracking_start(PowerSystem2v5Reg); - // wait for the bus supply to stabilize and the peripherals to start up. - // the MFI chip requires its reset pin to be stable for at least 10ms from startup. - psleep(20); - } else { - power_tracking_stop(PowerSystem2v5Reg); - } -} - -//! Power down I2C bus power supply -//! Always lock bus and peripheral config access before use -static void prv_bus_rail_power_down(I2CBus *bus) { - if (!bus->rail_ctl_fn) { - return; - } - prv_rail_ctl(bus, false); - -#if MICRO_FAMILY_NRF5 || MICRO_FAMILY_SF32LB52 - i2c_hal_pins_set_gpio(bus); -#endif - - // Drain through pull-ups - OutputConfig out_scl = { - .gpio = bus->scl_gpio.gpio, - .gpio_pin = bus->scl_gpio.gpio_pin, - .active_high = true - }; - gpio_output_init(&out_scl, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz); - gpio_output_set(&out_scl, false); - - OutputConfig out_sda = { - .gpio = bus->sda_gpio.gpio, - .gpio_pin = bus->sda_gpio.gpio_pin, - .active_high = true - }; - gpio_output_init(&out_sda, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz); - gpio_output_set(&out_sda, false); - - bus->state->last_rail_stop_ticks = rtc_get_ticks(); -} - -//! Configure bus pins for use by I2C peripheral -//! Lock bus and peripheral config access before configuring pins -static void prv_bus_pins_cfg_i2c(I2CBus *bus) { -#if MICRO_FAMILY_NRF5 || MICRO_FAMILY_SF32LB52 - i2c_hal_pins_set_i2c(bus); -#else - gpio_af_init(&bus->scl_gpio, GPIO_OType_OD, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&bus->sda_gpio, GPIO_OType_OD, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); -#endif -} - -static void prv_bus_pins_cfg_input(I2CBus *bus) { -#if MICRO_FAMILY_NRF5 || MICRO_FAMILY_SF32LB52 - i2c_hal_pins_set_gpio(bus); -#endif - - InputConfig in_scl = { - .gpio = bus->scl_gpio.gpio, - .gpio_pin = bus->scl_gpio.gpio_pin, - }; - gpio_input_init(&in_scl); - - InputConfig in_sda = { - .gpio = bus->sda_gpio.gpio, - .gpio_pin = bus->sda_gpio.gpio_pin, - }; - gpio_input_init(&in_sda); -} - -//! Power up I2C bus power supply -//! Always lock bus and peripheral config access before use -static void prv_bus_rail_power_up(I2CBus *bus) { - if (!bus->rail_ctl_fn) { - return; - } - - static const uint32_t MIN_STOP_TIME_MS = 10; - - // check that at least enough time has elapsed since the last turn-off - RtcTicks time_stopped_ms = - ((rtc_get_ticks() - bus->state->last_rail_stop_ticks) * RTC_TICKS_HZ) / 1000; - I2C_DEBUG("Bus %s rail start after a delay of %"PRIu32"ms", bus->name, - (uint32_t)time_stopped_ms); - - if (time_stopped_ms < MIN_STOP_TIME_MS) { - I2C_DEBUG("Waiting %"PRIu32"ms before enabling I2C bus %s rail.", - (uint32_t)(MIN_STOP_TIME_MS - time_stopped_ms), bus->name); - psleep(MIN_STOP_TIME_MS - time_stopped_ms); - } - - prv_bus_pins_cfg_input(bus); - - prv_rail_ctl(bus, true); -} - -//! Configure the bus pins, enable the peripheral clock and initialize the I2C peripheral. -//! Always lock the bus and peripheral config access before enabling it -static void prv_bus_enable(I2CBus *bus) { - // Don't power up rail if the bus is already in use (enable can be called to reset bus) - if (bus->state->user_count == 0) { - prv_bus_rail_power_up(bus); - } - - prv_bus_pins_cfg_i2c(bus); - - i2c_hal_enable(bus); -} - -//! De-initialize and gate the clock to the peripheral -//! Power down rail if the bus supports that and no devices are using it -//! Always lock the bus and peripheral config access before disabling it -static void prv_bus_disable(I2CBus *bus) { - i2c_hal_disable(bus); - - // Do not de-power rail if there are still devices using bus (just reset peripheral and pin - // configuration during a bus reset) - if (bus->state->user_count == 0) { - prv_bus_rail_power_down(bus); - } else { - prv_bus_pins_cfg_input(bus); - } -} - -//! Perform a soft reset of the bus -//! Always lock the bus before reset -static void prv_bus_reset(I2CBus *bus) { - prv_bus_disable(bus); - prv_bus_enable(bus); -} - -/*---------------INIT/USE/RELEASE/RESET FUNCTIONS----------------------*/ - -void i2c_init(I2CBus *bus) { - PBL_ASSERTN(bus); - - *bus->state = (I2CBusState) { - .event_semaphore = xSemaphoreCreateBinary(), - .bus_mutex = mutex_create(), - }; - - // Must give token before one can be taken without blocking - xSemaphoreGive(bus->state->event_semaphore); - - i2c_hal_init(bus); - - if (bus->rail_gpio.gpio) { - gpio_output_init(&bus->rail_gpio, GPIO_OType_PP, GPIO_Speed_2MHz); - } - prv_bus_rail_power_down(bus); -} - -void i2c_use(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - mutex_lock(slave->bus->state->bus_mutex); - - if (slave->bus->state->user_count == 0) { - prv_bus_enable(slave->bus); - } - slave->bus->state->user_count++; - - mutex_unlock(slave->bus->state->bus_mutex); -} - -void i2c_release(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - mutex_lock(slave->bus->state->bus_mutex); - - if (slave->bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted release of disabled bus %s", slave->bus->name); - mutex_unlock(slave->bus->state->bus_mutex); - return; - } - - slave->bus->state->user_count--; - if (slave->bus->state->user_count == 0) { - prv_bus_disable(slave->bus); - } - - mutex_unlock(slave->bus->state->bus_mutex); -} - -void i2c_reset(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - - // Take control of bus; only one task may use bus at a time - mutex_lock(slave->bus->state->bus_mutex); - - if (slave->bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted reset of disabled bus %s when still in use by " - "another bus", slave->bus->name); - mutex_unlock(slave->bus->state->bus_mutex); - return; - } - - PBL_LOG(LOG_LEVEL_WARNING, "Resetting I2C bus %s", slave->bus->name); - - // decrement user count for reset so that if this user is the only user, the - // bus will be powered down during the reset - slave->bus->state->user_count--; - - // Reset and reconfigure bus and pins - prv_bus_reset(slave->bus); - - // Restore user count - slave->bus->state->user_count++; - - mutex_unlock(slave->bus->state->bus_mutex); -} - -bool i2c_bitbang_recovery(I2CSlavePort *slave) { - PBL_ASSERTN(slave); - - static const int MAX_TOGGLE_COUNT = 10; - static const int TOGGLE_DELAY = 10; - - // Protect access to bus - mutex_lock(slave->bus->state->bus_mutex); - - if (slave->bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted bitbang recovery on disabled bus %s", slave->bus->name); - mutex_unlock(slave->bus->state->bus_mutex); - return false; - } - -#if MICRO_FAMILY_NRF5 || MICRO_FAMILY_SF32LB52 - i2c_hal_pins_set_gpio(slave->bus); -#endif - - InputConfig in_sda = { - .gpio = slave->bus->sda_gpio.gpio, - .gpio_pin = slave->bus->sda_gpio.gpio_pin, - }; - gpio_input_init(&in_sda); - - OutputConfig out_scl = { - .gpio = slave->bus->scl_gpio.gpio, - .gpio_pin = slave->bus->scl_gpio.gpio_pin, - .active_high = true - }; - gpio_output_init(&out_scl, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz); - gpio_output_set(&out_scl, true); - - bool recovered = false; - for (int i = 0; i < MAX_TOGGLE_COUNT; ++i) { - gpio_output_set(&out_scl, false); - psleep(TOGGLE_DELAY); - gpio_output_set(&out_scl, true); - psleep(TOGGLE_DELAY); - - if (gpio_input_read(&in_sda)) { - recovered = true; - break; - } - } - if (recovered) { - PBL_LOG(LOG_LEVEL_DEBUG, "I2C Bus %s recovered", slave->bus->name); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "I2C Bus %s still hung after bitbang reset", slave->bus->name); - } - - prv_bus_pins_cfg_i2c(slave->bus); - prv_bus_reset(slave->bus); - - mutex_unlock(slave->bus->state->bus_mutex); - - return recovered; -} - -/*--------------------DATA TRANSFER FUNCTIONS--------------------------*/ - -//! Wait a short amount of time for busy bit to clear -static bool prv_wait_for_not_busy(I2CBus *bus) { - static const int WAIT_DELAY = 10; // milliseconds - - if (i2c_hal_is_busy(bus)) { - psleep(WAIT_DELAY); - if (i2c_hal_is_busy(bus)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timed out waiting for bus %s to become non-busy", bus->name); - return false; - } - } - - return true; -} - -//! Set up and start a transfer to a bus, wait for it to finish and clean up after the transfer -//! has completed -static bool prv_do_transfer(I2CBus *bus, TransferDirection direction, uint16_t device_address, - uint8_t register_address, uint32_t size, uint8_t *data, - TransferType type) { - // Take control of bus; only one task may use bus at a time - mutex_lock(bus->state->bus_mutex); - - if (bus->state->user_count == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted access to disabled bus %s", bus->name); - mutex_unlock(bus->state->bus_mutex); - return false; - } - - // Disable stop mode while the I2C transfer is in progress - stop mode disables I2C peripheral - stop_mode_disable(bus->stop_mode_inhibitor); - - // If bus is busy (it shouldn't be as this function waits for the bus to report a non-idle state - // before exiting) reset the bus and wait for it to become not-busy - // Exit if bus remains busy. User module should reset the I2C module at this point - if (i2c_hal_is_busy(bus)) { - prv_bus_reset(bus); - - if (!prv_wait_for_not_busy(bus)) { - // Bus did not recover after reset - stop_mode_enable(bus->stop_mode_inhibitor); - mutex_unlock(bus->state->bus_mutex); - PBL_LOG(LOG_LEVEL_ERROR, "I2C bus did not recover after reset (%s)", bus->name); - prv_analytics_track_i2c_error(); - return false; - } - } - - // Take binary semaphore so that next take will block - PBL_ASSERT(prv_semaphore_take(bus->state), "Could not acquire semaphore token"); - - // Set up transfer - bus->state->transfer = (I2CTransfer) { - .device_address = device_address, - .register_address = register_address, - .direction = direction, - .type = type, - .size = size, - .idx = 0, - .data = data, - }; - - i2c_hal_init_transfer(bus); - - bus->state->transfer_nack_count = 0; - bus->state->transfer_start_ticks = rtc_get_ticks(); - - bool result = false; - bool complete = false; - do { - i2c_hal_start_transfer(bus); - - // Wait on semaphore until it is released by interrupt or a timeout occurs - if (prv_semaphore_wait(bus->state)) { - if ((bus->state->transfer_event == I2CTransferEvent_TransferComplete) || - (bus->state->transfer_event == I2CTransferEvent_Error)) { - // Track the max transfer duration so we can keep tabs on the MFi chip's nacking behavior - const uint32_t duration_ticks = rtc_get_ticks() - bus->state->transfer_start_ticks; - if (duration_ticks > s_max_transfer_duration_ticks) { - s_max_transfer_duration_ticks = duration_ticks; - } - - if (bus->state->transfer_event == I2CTransferEvent_Error) { - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error on bus %s", bus->name); - } - complete = true; - result = (bus->state->transfer_event == I2CTransferEvent_TransferComplete); - } else if (bus->state->transfer_nack_count < I2C_NACK_COUNT_MAX) { - // NACK received after start condition sent: the MFI chip NACKs start conditions whilst it - // is busy - // Retry start condition after a short delay. - // A NACK count is incremented for each NACK received, so that legitimate NACK - // errors cause the transfer to be aborted (after the NACK count max has been reached). - - bus->state->transfer_nack_count++; - - // Wait 1-2ms: - psleep(2); - - } else { - // Too many NACKs received, abort transfer - i2c_hal_abort_transfer(bus); - complete = true; - PBL_LOG(LOG_LEVEL_ERROR, "I2C Error: too many NACKs received on bus %s", bus->name); - break; - } - - } else { - // Timeout, abort transfer - i2c_hal_abort_transfer(bus); - complete = true; - PBL_LOG(LOG_LEVEL_ERROR, "Transfer timed out on bus %s", bus->name); - break; - } - } while (!complete); - - // Return semaphore token so another transfer can be started - prv_semaphore_give(bus->state); - - // Wait for bus to to clear the busy flag before a new transfer starts - // Theoretically a transfer could complete successfully, but the busy flag never clears, - // which would cause the next transfer to fail - if (!prv_wait_for_not_busy(bus)) { - // Reset I2C bus if busy flag does not clear - prv_bus_reset(bus); - } - - stop_mode_enable(bus->stop_mode_inhibitor); - - mutex_unlock(bus->state->bus_mutex); - - if (!result) { - prv_analytics_track_i2c_error(); - } - return result; -} - -bool i2c_read_register(I2CSlavePort *slave, uint8_t register_address, uint8_t *result) { - return i2c_read_register_block(slave, register_address, 1, result); -} - -bool i2c_read_register_block(I2CSlavePort *slave, uint8_t register_address_start, - uint32_t read_size, uint8_t* result_buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(result_buffer); - // Do transfer locks the bus - bool result = prv_do_transfer(slave->bus, Read, slave->address, register_address_start, read_size, - result_buffer, SendRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Read failed on bus %s", slave->bus->name); - } - - return result; -} - -bool i2c_read_block(I2CSlavePort *slave, uint32_t read_size, uint8_t* result_buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(result_buffer); - - bool result = prv_do_transfer(slave->bus, Read, slave->address, 0, read_size, result_buffer, - NoRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Block read failed on bus %s", slave->bus->name); - } - - return result; -} - -bool i2c_write_register(I2CSlavePort *slave, uint8_t register_address, uint8_t value) { - return i2c_write_register_block(slave, register_address, 1, &value); -} - -bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_start, - uint32_t write_size, const uint8_t* buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(buffer); - // Do transfer locks the bus - bool result = prv_do_transfer(slave->bus, Write, slave->address, register_address_start, - write_size, (uint8_t*)buffer, SendRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Write failed on bus %s", slave->bus->name); - } - - return result; -} - -bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer) { - PBL_ASSERTN(slave); - PBL_ASSERTN(buffer); - - // Do transfer locks the bus - bool result = prv_do_transfer(slave->bus, Write, slave->address, 0, write_size, (uint8_t*)buffer, - NoRegisterAddress); - - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Block write failed on bus %s", slave->bus->name); - } - - return result; -} - -/*----------------------HAL INTERFACE--------------------------------*/ - -portBASE_TYPE i2c_handle_transfer_event(I2CBus *bus, I2CTransferEvent event) { - bus->state->transfer_event = event; - return prv_semaphore_give_from_isr(bus->state); -} - -/*------------------------ANALYTICS----------------------------------*/ - -static void prv_analytics_track_i2c_error(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_I2C_ERROR_COUNT, AnalyticsClient_System); -} - -void analytics_external_collect_i2c_stats(void) { - analytics_set(ANALYTICS_DEVICE_METRIC_I2C_MAX_TRANSFER_DURATION_TICKS, - s_max_transfer_duration_ticks, AnalyticsClient_System); - s_max_transfer_duration_ticks = 0; -} - -/*------------------------COMMAND FUNCTIONS--------------------------*/ -// FIXME: Move to board-specific implementations -// https://pebbletechnology.atlassian.net/browse/PBL-32232 -#if PLATFORM_TINTIN -void command_power_2v5(char *arg) { - // Intentionally ignore the s_running_count and make it so! - // This is intended for low level electrical test only - if (!strcmp("on", arg)) { - prv_bus_rail_power_up(I2C_MFI->bus); - } else { - prv_bus_rail_power_down(I2C_MFI->bus); - } -} -#endif diff --git a/src/fw/drivers/i2c.h b/src/fw/drivers/i2c.h index 1096d7174b..d3d9b0ae11 100644 --- a/src/fw/drivers/i2c.h +++ b/src/fw/drivers/i2c.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -95,3 +82,17 @@ bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_star //! @param buffer Pointer to source buffer //! @return true if transfer succeeded, false if error occurred bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer); + +//! Write a block of data then read a block of data in a single atomic transaction. +//! This function is thread-safe and holds the bus lock throughout both operations. +//! Must not be called before \ref i2c_use has been called for the slave. +//! Performs a write followed by read while holding the bus mutex, useful for devices that require +//! sending a command/register address before reading data without allowing other transactions in between. +//! @param slave I2C slave to communicate with +//! @param write_size Number of bytes to write +//! @param write_buffer Pointer to source buffer for write +//! @param read_size Number of bytes to read +//! @param read_buffer Pointer to destination buffer for read +//! @return true if transfer succeeded, false if error occurred +bool i2c_write_read_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* write_buffer, + uint32_t read_size, uint8_t* read_buffer); diff --git a/src/fw/drivers/i2c/Kconfig b/src/fw/drivers/i2c/Kconfig new file mode 100644 index 0000000000..6584a9c787 --- /dev/null +++ b/src/fw/drivers/i2c/Kconfig @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig I2C + bool "I2C" + help + I2C Drivers + +if I2C + +config I2C_NRF5 + bool "Nordic nRF5 I2C Controller" + help + Support for the Nordic nRF5 I2C controller. + +config I2C_SF32LB + bool "SiFli SF32LB I2C Controller" + select HAL_SIFLI_I2C + help + Support for the SiFli SF32LB I2C controller. + +module = DRIVER_I2C +module-str = I2C +source "src/fw/Kconfig.template.log_level" + +endif # I2C diff --git a/src/fw/drivers/i2c/common.c b/src/fw/drivers/i2c/common.c new file mode 100644 index 0000000000..9e29cd0277 --- /dev/null +++ b/src/fw/drivers/i2c/common.c @@ -0,0 +1,413 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/i2c.h" +#include "definitions.h" +#include "hal.h" + +#include "board/board.h" +#include "debug/power_tracking.h" +#include "drivers/gpio.h" +#include "drivers/periph_config.h" +#include "drivers/rtc.h" +#include "FreeRTOS.h" +#include "kernel/pbl_malloc.h" +#include "os/tick.h" +#include "kernel/util/sleep.h" +#include "kernel/util/stop.h" +#include "os/mutex.h" +#include "semphr.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" + +#ifdef CONFIG_PMIC +#include "drivers/pmic.h" +#endif + +#include + +PBL_LOG_MODULE_DEFINE(driver_i2c, CONFIG_DRIVER_I2C_LOG_LEVEL); + +#define I2C_ERROR_TIMEOUT_MS (1000) +#define I2C_TIMEOUT_ATTEMPTS_MAX (2 * 1000 * 1000) + +// MFI NACKs while busy. We delay ~1ms between retries so this is approximately a 1000ms timeout. +// The longest operation of the MFi chip is "start signature generation", which seems to take +// 223-224 NACKs, but sometimes for unknown reasons it can take much longer. +#define I2C_NACK_COUNT_MAX (1000) + +#define Read I2CTransferDirection_Read +#define Write I2CTransferDirection_Write +#define SendRegisterAddress I2CTransferType_SendRegisterAddress +#define NoRegisterAddress I2CTransferType_NoRegisterAddress + +/*----------------SEMAPHORE/LOCKING FUNCTIONS--------------------------*/ + +static bool prv_semaphore_take(I2CBusState *bus) { + return (xSemaphoreTake(bus->event_semaphore, 0) == pdPASS); +} + +static bool prv_semaphore_wait(I2CBusState *bus) { + TickType_t timeout_ticks = milliseconds_to_ticks(I2C_ERROR_TIMEOUT_MS); + return (xSemaphoreTake(bus->event_semaphore, timeout_ticks) == pdPASS); +} + +static void prv_semaphore_give(I2CBusState *bus) { + // If this fails, something is very wrong + (void)xSemaphoreGive(bus->event_semaphore); +} + +static portBASE_TYPE prv_semaphore_give_from_isr(I2CBusState *bus) { + portBASE_TYPE should_context_switch = pdFALSE; + (void)xSemaphoreGiveFromISR(bus->event_semaphore, &should_context_switch); + return should_context_switch; +} + +/*-------------------BUS/PIN CONFIG FUNCTIONS--------------------------*/ +// FIXME: These rail control functions should be moved to board-specific implementations +// https://pebbletechnology.atlassian.net/browse/PBL-32232 + +#ifdef CONFIG_PMIC +void i2c_rail_ctl_pmic(I2CBus *bus, bool enable) { + set_ldo3_power_state(enable); +} +#endif + +//! Configure the bus pins, enable the peripheral clock and initialize the I2C peripheral. +//! Always lock the bus and peripheral config access before enabling it +static void prv_bus_enable(I2CBus *bus) { + i2c_hal_enable(bus); +} + +//! De-initialize and gate the clock to the peripheral +//! Power down rail if the bus supports that and no devices are using it +//! Always lock the bus and peripheral config access before disabling it +static void prv_bus_disable(I2CBus *bus) { + i2c_hal_disable(bus); +} + +//! Perform a soft reset of the bus +//! Always lock the bus before reset +static void prv_bus_reset(I2CBus *bus) { + prv_bus_disable(bus); + prv_bus_enable(bus); +} + +/*---------------INIT/USE/RELEASE/RESET FUNCTIONS----------------------*/ + +void i2c_init(I2CBus *bus) { + PBL_ASSERTN(bus); + + *bus->state = (I2CBusState) { + .event_semaphore = xSemaphoreCreateBinary(), + .bus_mutex = mutex_create(), + }; + + // Must give token before one can be taken without blocking + xSemaphoreGive(bus->state->event_semaphore); + + i2c_hal_init(bus); +} + +void i2c_use(I2CSlavePort *slave) { + PBL_ASSERTN(slave); + mutex_lock(slave->bus->state->bus_mutex); + + if (slave->bus->state->user_count == 0) { + prv_bus_enable(slave->bus); + } + slave->bus->state->user_count++; + + mutex_unlock(slave->bus->state->bus_mutex); +} + +void i2c_release(I2CSlavePort *slave) { + PBL_ASSERTN(slave); + mutex_lock(slave->bus->state->bus_mutex); + + if (slave->bus->state->user_count == 0) { + PBL_LOG_ERR("Attempted release of disabled bus %s", slave->bus->name); + mutex_unlock(slave->bus->state->bus_mutex); + return; + } + + slave->bus->state->user_count--; + if (slave->bus->state->user_count == 0) { + prv_bus_disable(slave->bus); + } + + mutex_unlock(slave->bus->state->bus_mutex); +} + +void i2c_reset(I2CSlavePort *slave) { + PBL_ASSERTN(slave); + + // Take control of bus; only one task may use bus at a time + mutex_lock(slave->bus->state->bus_mutex); + + if (slave->bus->state->user_count == 0) { + PBL_LOG_ERR("Attempted reset of disabled bus %s when still in use by " + "another bus", slave->bus->name); + mutex_unlock(slave->bus->state->bus_mutex); + return; + } + + PBL_LOG_WRN("Resetting I2C bus %s", slave->bus->name); + + // decrement user count for reset so that if this user is the only user, the + // bus will be powered down during the reset + slave->bus->state->user_count--; + + // Reset and reconfigure bus and pins + prv_bus_reset(slave->bus); + + // Restore user count + slave->bus->state->user_count++; + + mutex_unlock(slave->bus->state->bus_mutex); +} + +bool i2c_bitbang_recovery(I2CSlavePort *slave) { + PBL_LOG_ERR("I2C bitbang recovery not supported on this platform"); + return false; +} + +/*--------------------DATA TRANSFER FUNCTIONS--------------------------*/ + +//! Wait a short amount of time for busy bit to clear +static bool prv_wait_for_not_busy(I2CBus *bus) { + static const int WAIT_DELAY = 10; // milliseconds + + if (i2c_hal_is_busy(bus)) { + psleep(WAIT_DELAY); + if (i2c_hal_is_busy(bus)) { + PBL_LOG_ERR("Timed out waiting for bus %s to become non-busy", bus->name); + return false; + } + } + + return true; +} + +//! Set up and start a transfer to a bus, wait for it to finish and clean up after the transfer +//! has completed +//! Caller must hold bus mutex +static bool prv_do_transfer_locked(I2CBus *bus, I2CTransferDirection direction, uint16_t device_address, + uint8_t register_address, uint32_t size, uint8_t *data, + I2CTransferType type) { + if (bus->state->user_count == 0) { + PBL_LOG_ERR("Attempted access to disabled bus %s", bus->name); + return false; + } + + // If bus is busy (it shouldn't be as this function waits for the bus to report a non-idle state + // before exiting) reset the bus and wait for it to become not-busy + // Exit if bus remains busy. User module should reset the I2C module at this point + if (i2c_hal_is_busy(bus)) { + prv_bus_reset(bus); + + if (!prv_wait_for_not_busy(bus)) { + // Bus did not recover after reset + PBL_LOG_ERR("I2C bus did not recover after reset (%s)", bus->name); + return false; + } + } + + // Take binary semaphore so that next take will block + PBL_ASSERT(prv_semaphore_take(bus->state), "Could not acquire semaphore token"); + + // Set up transfer + bus->state->transfer = (I2CTransfer) { + .device_address = device_address, + .register_address = register_address, + .direction = direction, + .type = type, + .size = size, + .idx = 0, + .data = data, + }; + + i2c_hal_init_transfer(bus); + + bus->state->transfer_nack_count = 0; + bus->state->transfer_start_ticks = rtc_get_ticks(); + + bool result = false; + bool complete = false; + do { + i2c_hal_start_transfer(bus); + + // Wait on semaphore until it is released by interrupt or a timeout occurs + if (prv_semaphore_wait(bus->state)) { + if ((bus->state->transfer_event == I2CTransferEvent_TransferComplete) || + (bus->state->transfer_event == I2CTransferEvent_Error)) { + if (bus->state->transfer_event == I2CTransferEvent_Error) { + PBL_LOG_ERR("I2C Error on bus %s", bus->name); + } + complete = true; + result = (bus->state->transfer_event == I2CTransferEvent_TransferComplete); + } else if (bus->state->transfer_nack_count < I2C_NACK_COUNT_MAX) { + // NACK received after start condition sent: the MFI chip NACKs start conditions whilst it + // is busy + // Retry start condition after a short delay. + // A NACK count is incremented for each NACK received, so that legitimate NACK + // errors cause the transfer to be aborted (after the NACK count max has been reached). + + bus->state->transfer_nack_count++; + + // Wait 1-2ms: + psleep(2); + + } else { + // Too many NACKs received, abort transfer + i2c_hal_abort_transfer(bus); + complete = true; + PBL_LOG_ERR("I2C Error: too many NACKs received on bus %s", bus->name); + break; + } + + } else { + // Timeout, abort transfer + i2c_hal_abort_transfer(bus); + complete = true; + PBL_LOG_ERR("Transfer timed out on bus %s", bus->name); + break; + } + } while (!complete); + + // Return semaphore token so another transfer can be started + prv_semaphore_give(bus->state); + + // Wait for bus to to clear the busy flag before a new transfer starts + // Theoretically a transfer could complete successfully, but the busy flag never clears, + // which would cause the next transfer to fail + if (!prv_wait_for_not_busy(bus)) { + // Reset I2C bus if busy flag does not clear + prv_bus_reset(bus); + } + + return result; +} + +//! Wrapper that manages locking for prv_do_transfer_locked +static bool prv_do_transfer(I2CBus *bus, I2CTransferDirection direction, uint16_t device_address, + uint8_t register_address, uint32_t size, uint8_t *data, + I2CTransferType type) { + mutex_lock(bus->state->bus_mutex); + stop_mode_disable(bus->stop_mode_inhibitor); + + bool result = prv_do_transfer_locked(bus, direction, device_address, register_address, size, + data, type); + + stop_mode_enable(bus->stop_mode_inhibitor); + mutex_unlock(bus->state->bus_mutex); + + return result; +} + +bool i2c_read_register(I2CSlavePort *slave, uint8_t register_address, uint8_t *result) { + return i2c_read_register_block(slave, register_address, 1, result); +} + +bool i2c_read_register_block(I2CSlavePort *slave, uint8_t register_address_start, + uint32_t read_size, uint8_t* result_buffer) { + PBL_ASSERTN(slave); + PBL_ASSERTN(result_buffer); + // Do transfer locks the bus + bool result = prv_do_transfer(slave->bus, Read, slave->address, register_address_start, read_size, + result_buffer, SendRegisterAddress); + + if (!result) { + PBL_LOG_ERR("Read failed on bus %s", slave->bus->name); + } + + return result; +} + +bool i2c_read_block(I2CSlavePort *slave, uint32_t read_size, uint8_t* result_buffer) { + PBL_ASSERTN(slave); + PBL_ASSERTN(result_buffer); + + bool result = prv_do_transfer(slave->bus, Read, slave->address, 0, read_size, result_buffer, + NoRegisterAddress); + + if (!result) { + PBL_LOG_ERR("Block read failed on bus %s", slave->bus->name); + } + + return result; +} + +bool i2c_write_register(I2CSlavePort *slave, uint8_t register_address, uint8_t value) { + return i2c_write_register_block(slave, register_address, 1, &value); +} + +bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_start, + uint32_t write_size, const uint8_t* buffer) { + PBL_ASSERTN(slave); + PBL_ASSERTN(buffer); + // Do transfer locks the bus + bool result = prv_do_transfer(slave->bus, Write, slave->address, register_address_start, + write_size, (uint8_t*)buffer, SendRegisterAddress); + + if (!result) { + PBL_LOG_ERR("Write failed on bus %s", slave->bus->name); + } + + return result; +} + +bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer) { + PBL_ASSERTN(slave); + PBL_ASSERTN(buffer); + + // Do transfer locks the bus + bool result = prv_do_transfer(slave->bus, Write, slave->address, 0, write_size, (uint8_t*)buffer, + NoRegisterAddress); + + if (!result) { + PBL_LOG_ERR("Block write failed on bus %s", slave->bus->name); + } + + return result; +} + +bool i2c_write_read_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* write_buffer, + uint32_t read_size, uint8_t* read_buffer) { + PBL_ASSERTN(slave); + PBL_ASSERTN(write_buffer); + PBL_ASSERTN(read_buffer); + + I2CBus *bus = slave->bus; + + // Take control of bus; only one task may use bus at a time + mutex_lock(bus->state->bus_mutex); + stop_mode_disable(bus->stop_mode_inhibitor); + + // Perform write transfer + bool result = prv_do_transfer_locked(bus, Write, slave->address, 0, write_size, + (uint8_t*)write_buffer, NoRegisterAddress); + + // Only proceed with read if write succeeded + if (result) { + result = prv_do_transfer_locked(bus, Read, slave->address, 0, read_size, + read_buffer, NoRegisterAddress); + } + + stop_mode_enable(bus->stop_mode_inhibitor); + mutex_unlock(bus->state->bus_mutex); + + if (!result) { + PBL_LOG_ERR("Write-read block failed on bus %s", bus->name); + } + + return result; +} + +/*----------------------HAL INTERFACE--------------------------------*/ + +portBASE_TYPE i2c_handle_transfer_event(I2CBus *bus, I2CTransferEvent event) { + bus->state->transfer_event = event; + return prv_semaphore_give_from_isr(bus->state); +} diff --git a/src/fw/drivers/i2c/definitions.h b/src/fw/drivers/i2c/definitions.h new file mode 100644 index 0000000000..2664228413 --- /dev/null +++ b/src/fw/drivers/i2c/definitions.h @@ -0,0 +1,92 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "drivers/rtc.h" +#include "os/mutex.h" +#include "kernel/util/stop.h" + +#include "freertos_types.h" +#include "portmacro.h" + +#include +#include + +typedef enum I2CTransferEvent { + I2CTransferEvent_Timeout, + I2CTransferEvent_TransferComplete, + I2CTransferEvent_NackReceived, + I2CTransferEvent_Error, +} I2CTransferEvent; + +typedef enum { + I2CTransferDirection_Read, + I2CTransferDirection_Write +} I2CTransferDirection; + +typedef enum { + // Send a register address, followed by a repeat start for reads + I2CTransferType_SendRegisterAddress, + + // Do not send a register address; used for block writes/reads + I2CTransferType_NoRegisterAddress +} I2CTransferType; + +typedef enum I2CTransferState { + I2CTransferState_WriteAddressTx, + I2CTransferState_WriteRegAddress, + I2CTransferState_RepeatStart, + I2CTransferState_WriteAddressRx, + I2CTransferState_WaitForData, + I2CTransferState_ReadData, + I2CTransferState_WriteData, + I2CTransferState_EndWrite, + I2CTransferState_Complete, +} I2CTransferState; + +typedef struct I2CTransfer { + I2CTransferState state; + uint16_t device_address; + I2CTransferDirection direction; + I2CTransferType type; + uint8_t register_address; + uint32_t size; + uint32_t idx; + uint8_t *data; +} I2CTransfer; + +typedef struct I2CBusState { + I2CTransfer transfer; + I2CTransferEvent transfer_event; + int transfer_nack_count; + RtcTicks transfer_start_ticks; + int user_count; + SemaphoreHandle_t event_semaphore; + PebbleMutex *bus_mutex; +} I2CBusState; + +struct I2CBus { + I2CBusState *const state; + const struct I2CBusHal *const hal; +#ifdef CONFIG_SOC_NRF52 + AfConfig scl_gpio; ///< Alternate Function configuration for SCL pin + AfConfig sda_gpio; ///< Alternate Function configuration for SDA pin +#endif + StopModeInhibitor stop_mode_inhibitor; + const char *name; //! Device ID for logging purposes +}; + +struct I2CSlavePort { + const I2CBus *bus; + uint16_t address; +}; + +//! Initialize the I2C driver. +void i2c_init(I2CBus *bus); + +//! Transfer event handler implemented in i2c.c and called by HAL implementation +portBASE_TYPE i2c_handle_transfer_event(I2CBus *device, I2CTransferEvent event); + +#define I2C_DEBUG(fmt, args...) \ + PBL_LOG_D_DBG(LOG_DOMAIN_I2C, fmt, ## args) diff --git a/src/fw/drivers/i2c/hal.h b/src/fw/drivers/i2c/hal.h new file mode 100644 index 0000000000..61be4b20cd --- /dev/null +++ b/src/fw/drivers/i2c/hal.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "board/board.h" + +#include + +void i2c_hal_init(I2CBus *bus); + +void i2c_hal_enable(I2CBus *bus); + +void i2c_hal_disable(I2CBus *bus); + +bool i2c_hal_is_busy(I2CBus *bus); + +void i2c_hal_abort_transfer(I2CBus *bus); + +void i2c_hal_init_transfer(I2CBus *bus); + +void i2c_hal_start_transfer(I2CBus *bus); diff --git a/src/fw/drivers/i2c/nrf5.c b/src/fw/drivers/i2c/nrf5.c new file mode 100644 index 0000000000..08e59c7bad --- /dev/null +++ b/src/fw/drivers/i2c/nrf5.c @@ -0,0 +1,92 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "hal.h" +#include "definitions.h" +#include "nrf5.h" + +#include "system/passert.h" + +#include "drivers/periph_config.h" +#include "FreeRTOS.h" + +#include + +#define I2C_IRQ_PRIORITY (0xc) +#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000) +#define I2C_READ_WRITE_BIT (0x01) + +static void prv_twim_evt_handler(nrfx_twim_evt_t const *evt, void *ctx) { + I2CBus *bus = (I2CBus *) ctx; + bool success = evt->type == NRFX_TWIM_EVT_DONE; + I2CTransferEvent event = success ? I2CTransferEvent_TransferComplete : I2CTransferEvent_Error; + bool should_csw = i2c_handle_transfer_event(bus, event); + portEND_SWITCHING_ISR(should_csw); +} + +static void prv_twim_init(I2CBus *bus) { + nrfx_twim_config_t config = NRFX_TWIM_DEFAULT_CONFIG( + bus->scl_gpio.gpio_pin, bus->sda_gpio.gpio_pin); + config.frequency = bus->hal->frequency; + config.hold_bus_uninit = true; + + nrfx_err_t err = nrfx_twim_init(&bus->hal->twim, &config, prv_twim_evt_handler, (void *)bus); + PBL_ASSERTN(err == NRFX_SUCCESS); +} + +void i2c_hal_init(I2CBus *bus) { + prv_twim_init(bus); + nrfx_twim_uninit(&bus->hal->twim); +} + +void i2c_hal_enable(I2CBus *bus) { + prv_twim_init(bus); + nrfx_twim_enable(&bus->hal->twim); +} + +void i2c_hal_disable(I2CBus *bus) { + nrfx_twim_disable(&bus->hal->twim); + nrfx_twim_uninit(&bus->hal->twim); +} + +bool i2c_hal_is_busy(I2CBus *bus) { + return nrfx_twim_is_busy(&bus->hal->twim); +} + +void i2c_hal_abort_transfer(I2CBus *bus) { + nrfx_twim_disable(&bus->hal->twim); + nrfx_twim_enable(&bus->hal->twim); +} + +void i2c_hal_init_transfer(I2CBus *bus) { +} + +void i2c_hal_start_transfer(I2CBus *bus) { + nrfx_twim_xfer_desc_t desc; + + desc.address = bus->state->transfer.device_address >> 1; + if (bus->state->transfer.type == I2CTransferType_SendRegisterAddress) { + if (bus->state->transfer.direction == I2CTransferDirection_Read) { + desc.type = NRFX_TWIM_XFER_TXRX; + } else { + desc.type = NRFX_TWIM_XFER_TXTX; + } + desc.primary_length = 1; + desc.p_primary_buf = &bus->state->transfer.register_address; + + desc.secondary_length = bus->state->transfer.size; + desc.p_secondary_buf = bus->state->transfer.data; + } else { + if (bus->state->transfer.direction == I2CTransferDirection_Read) { + desc.type = NRFX_TWIM_XFER_RX; + } else { + desc.type = NRFX_TWIM_XFER_TX; + } + desc.primary_length = bus->state->transfer.size; + desc.p_primary_buf = bus->state->transfer.data; + desc.secondary_length = 0; + } + + nrfx_err_t rv = nrfx_twim_xfer(&bus->hal->twim, &desc, 0); + PBL_ASSERTN(rv == NRFX_SUCCESS); +} diff --git a/src/fw/drivers/i2c/nrf5.h b/src/fw/drivers/i2c/nrf5.h new file mode 100644 index 0000000000..cc943d324d --- /dev/null +++ b/src/fw/drivers/i2c/nrf5.h @@ -0,0 +1,17 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#include +#pragma GCC diagnostic pop + +typedef struct I2CBusHal { + nrfx_twim_t twim; + nrf_twim_frequency_t frequency; ///< Bus clock speed +} I2CBusHal; diff --git a/src/fw/drivers/i2c/sf32lb.c b/src/fw/drivers/i2c/sf32lb.c new file mode 100644 index 0000000000..d57d98b7c6 --- /dev/null +++ b/src/fw/drivers/i2c/sf32lb.c @@ -0,0 +1,114 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "sf32lb.h" +#include "definitions.h" +#include "hal.h" + +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "semphr.h" + +#include "bf0_hal.h" + +void i2c_irq_handler(I2CBus *bus) { + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &hal->state->hdl; + HAL_I2C_StateTypeDef state; + I2CTransferEvent event; + portBASE_TYPE woken; + + (void)hdl->XferISR(hdl, 0, 0); + + state = HAL_I2C_GetState(hdl); + if ((state == HAL_I2C_STATE_BUSY_TX) || (state == HAL_I2C_STATE_BUSY_RX)) { + return; + } else if (state == HAL_I2C_STATE_READY) { + event = I2CTransferEvent_TransferComplete; + } else { + event = I2CTransferEvent_Error; + } + + woken = i2c_handle_transfer_event(bus, event); + portEND_SWITCHING_ISR(woken); +} + +void i2c_hal_init_transfer(I2CBus *bus) {} + +void i2c_hal_abort_transfer(I2CBus *bus) { + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &hal->state->hdl; + + HAL_I2C_Reset(hdl); +} + +void i2c_hal_start_transfer(I2CBus *bus) { + HAL_StatusTypeDef ret; + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &hal->state->hdl; + I2CTransfer *transfer = &bus->state->transfer; + + if (transfer->type == I2CTransferType_SendRegisterAddress) { + if (transfer->direction == I2CTransferDirection_Read) { + ret = HAL_I2C_Mem_Read_IT(hdl, transfer->device_address, transfer->register_address, + I2C_MEMADD_SIZE_8BIT, transfer->data, transfer->size); + } else { + ret = HAL_I2C_Mem_Write_IT(hdl, transfer->device_address, transfer->register_address, + I2C_MEMADD_SIZE_8BIT, transfer->data, transfer->size); + } + } else { + if (transfer->direction == I2CTransferDirection_Read) { + ret = + HAL_I2C_Master_Receive_IT(hdl, transfer->device_address, transfer->data, transfer->size); + } else { + ret = + HAL_I2C_Master_Transmit_IT(hdl, transfer->device_address, transfer->data, transfer->size); + } + } + + if (ret != HAL_OK) { + HAL_I2C_Reset(hdl); + bus->state->transfer_event = I2CTransferEvent_Error; + xSemaphoreGive(bus->state->event_semaphore); + } +} + +void i2c_hal_enable(I2CBus *bus) { + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &bus->hal->state->hdl; + + HAL_RCC_EnableModule(hal->module); + __HAL_I2C_ENABLE(hdl); +} + +void i2c_hal_disable(I2CBus *bus) { + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &bus->hal->state->hdl; + + __HAL_I2C_DISABLE(hdl); + HAL_RCC_DisableModule(hal->module); +} + +bool i2c_hal_is_busy(I2CBus *bus) { + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &hal->state->hdl; + + return HAL_I2C_GetState(hdl) != HAL_I2C_STATE_READY; +} + +void i2c_hal_init(I2CBus *bus) { + HAL_StatusTypeDef ret; + I2CBusHal *hal = bus->hal; + I2C_HandleTypeDef *hdl = &hal->state->hdl; + + HAL_PIN_Set(hal->scl.pad, hal->scl.func, hal->scl.flags, 1); + HAL_PIN_Set(hal->sda.pad, hal->sda.func, hal->sda.flags, 1); + + HAL_RCC_EnableModule(hal->module); + ret = HAL_I2C_Init(hdl); + PBL_ASSERTN(ret == HAL_OK); + + HAL_NVIC_SetPriority(hal->irqn, hal->irq_priority, 0); + NVIC_EnableIRQ(hal->irqn); +} diff --git a/src/fw/drivers/i2c/sf32lb.h b/src/fw/drivers/i2c/sf32lb.h new file mode 100644 index 0000000000..bd03c450a9 --- /dev/null +++ b/src/fw/drivers/i2c/sf32lb.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "board/board.h" +#include "definitions.h" + +typedef struct I2CBusHalState { + I2C_HandleTypeDef hdl; +} I2CBusHalState; + +typedef const struct I2CBusHal { + I2CBusHalState *state; + Pinmux scl; + Pinmux sda; + RCC_MODULE_TYPE module; + IRQn_Type irqn; + uint8_t irq_priority; +} I2CBusHal; + +void i2c_irq_handler(I2CBus *bus); diff --git a/src/fw/drivers/i2c/wscript_build b/src/fw/drivers/i2c/wscript_build new file mode 100644 index 0000000000..a7b4baa869 --- /dev/null +++ b/src/fw/drivers/i2c/wscript_build @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +use = ['freertos', 'fw_includes'] +sources = ['common.c'] + +if bld.env.CONFIG_I2C_NRF5: + use.append('hal_nordic') + sources.append('nrf5.c') +elif bld.env.CONFIG_I2C_SF32LB: + use.append('hal_sifli') + sources.append('sf32lb.c') + +bld.objects( + name='driver_i2c', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_i2c') \ No newline at end of file diff --git a/src/fw/drivers/i2c_definitions.h b/src/fw/drivers/i2c_definitions.h deleted file mode 100644 index 3c9e63946f..0000000000 --- a/src/fw/drivers/i2c_definitions.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/rtc.h" -#include "os/mutex.h" -#include "kernel/util/stop.h" - -#include "freertos_types.h" -#include "portmacro.h" - -#include -#include - -typedef enum I2CTransferEvent { - I2CTransferEvent_Timeout, - I2CTransferEvent_TransferComplete, - I2CTransferEvent_NackReceived, - I2CTransferEvent_Error, -} I2CTransferEvent; - -typedef enum { - I2CTransferDirection_Read, - I2CTransferDirection_Write -} I2CTransferDirection; - -typedef enum { - // Send a register address, followed by a repeat start for reads - I2CTransferType_SendRegisterAddress, - - // Do not send a register address; used for block writes/reads - I2CTransferType_NoRegisterAddress -} I2CTransferType; - -typedef enum I2CTransferState { - I2CTransferState_WriteAddressTx, - I2CTransferState_WriteRegAddress, - I2CTransferState_RepeatStart, - I2CTransferState_WriteAddressRx, - I2CTransferState_WaitForData, - I2CTransferState_ReadData, - I2CTransferState_WriteData, - I2CTransferState_EndWrite, - I2CTransferState_Complete, -} I2CTransferState; - -typedef struct I2CTransfer { - I2CTransferState state; - uint16_t device_address; - I2CTransferDirection direction; - I2CTransferType type; - uint8_t register_address; - uint32_t size; - uint32_t idx; - uint8_t *data; -} I2CTransfer; - -typedef struct I2CBusState { - I2CTransfer transfer; - I2CTransferEvent transfer_event; - int transfer_nack_count; - RtcTicks transfer_start_ticks; - int user_count; - RtcTicks last_rail_stop_ticks; - SemaphoreHandle_t event_semaphore; - PebbleMutex *bus_mutex; - int should_be_init; //! for pin connect/disconnect recovery on nRF5 -} I2CBusState; - -struct I2CBus { - I2CBusState *const state; - const struct I2CBusHal *const hal; - AfConfig scl_gpio; ///< Alternate Function configuration for SCL pin - AfConfig sda_gpio; ///< Alternate Function configuration for SDA pin - OutputConfig rail_gpio; ///< Control pin for rail - void (* const rail_ctl_fn)(I2CBus *device, bool enabled); ///< Control function for this rail. - StopModeInhibitor stop_mode_inhibitor; - const char *name; //! Device ID for logging purposes -}; - -struct I2CSlavePort { - const I2CBus *bus; - uint16_t address; -}; - -//! Initialize the I2C driver. -void i2c_init(I2CBus *bus); - -//! Transfer event handler implemented in i2c.c and called by HAL implementation -portBASE_TYPE i2c_handle_transfer_event(I2CBus *device, I2CTransferEvent event); - -#define I2C_DEBUG(fmt, args...) \ - PBL_LOG_COLOR_D(LOG_DOMAIN_I2C, LOG_LEVEL_DEBUG, LOG_COLOR_LIGHT_MAGENTA, fmt, ## args) diff --git a/src/fw/drivers/i2c_hal.h b/src/fw/drivers/i2c_hal.h deleted file mode 100644 index d807a8eaab..0000000000 --- a/src/fw/drivers/i2c_hal.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include - -void i2c_hal_init(I2CBus *bus); - -void i2c_hal_enable(I2CBus *bus); - -void i2c_hal_disable(I2CBus *bus); - -bool i2c_hal_is_busy(I2CBus *bus); - -void i2c_hal_abort_transfer(I2CBus *bus); - -void i2c_hal_init_transfer(I2CBus *bus); - -void i2c_hal_start_transfer(I2CBus *bus); - -#if MICRO_FAMILY_NRF5 || MICRO_FAMILY_SF32LB52 -void i2c_hal_pins_set_gpio(I2CBus *bus); -void i2c_hal_pins_set_i2c(I2CBus *bus); -#endif diff --git a/src/fw/drivers/imu.h b/src/fw/drivers/imu.h deleted file mode 100644 index b6a68f8dee..0000000000 --- a/src/fw/drivers/imu.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void imu_init(void); - -//! Put all IMU devices into normal mode -void imu_power_up(void); -//! Put all IMU devices into low-power mode -void imu_power_down(void); - -bool imu_self_test(void); - -bool imu_sanity_check(void); diff --git a/src/fw/drivers/imu/Kconfig b/src/fw/drivers/imu/Kconfig new file mode 100644 index 0000000000..ff219f61ce --- /dev/null +++ b/src/fw/drivers/imu/Kconfig @@ -0,0 +1,111 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menu "IMU" + +choice ACCEL + prompt "Accelerometer" + optional + +config ACCEL_BMA255 + bool "Bosch BMA255" + help + Support for the Bosch BMA255 accelerometer. + +config ACCEL_BMI160 + bool "Bosch BMI160" + help + Support for the Bosch BMI160 accelerometer/gyroscope. + +config ACCEL_LIS2DW12 + bool "ST LIS2DW12" + help + Support for the ST LIS2DW12 accelerometer. + +config ACCEL_LSM6DSO + bool "ST LSM6DSO" + help + Support for the ST LSM6DSO accelerometer/gyroscope. + +endchoice + +if ACCEL_LIS2DW12 + +config ACCEL_LIS2DW12_DISABLE_ADDR_PULLUP + bool "Disable ADDR pin pull-up" + help + Disable the internal pull-up resistor on the ADDR pin. + +config ACCEL_LIS2DW12_WK_DUR_DEFAULT + int "Default wake duration" + range 0 3 + default 0 + help + Default wake duration applied at init. + +config ACCEL_LIS2DW12_WK_THS_MIN + int "Minimum wake threshold" + range 0 63 + default 0 + help + Minimum (highest sensitivity) wake-up threshold. + +config ACCEL_LIS2DW12_WK_THS_MAX + int "Maximum wake threshold" + range 0 63 + default 63 + help + Maximum (lowest sensitivity) wake-up threshold. + +config ACCEL_LIS2DW12_WK_THS_DEFAULT + int "Default wake threshold" + range 0 63 + default 0 + help + Default wake-up threshold applied at init. + +config ACCEL_LIS2DW12_SCALE_MG + int "Full-scale range (mg)" + default 2000 + help + Acceleration full-scale range in mg (2000, 4000, 8000 or 16000). + +config ACCEL_LIS2DW12_FIFO_THRESHOLD + int "FIFO threshold" + range 0 32 + default 0 + help + FIFO watermark threshold in samples. Must be chosen so the FIFO can be + drained before overrun occurs at the maximum ODR. + +endif # ACCEL_LIS2DW12 + +menuconfig MAG + bool "Magnetometer" + help + Magnetometer Drivers + +if MAG + +choice MAG_DRIVER + prompt "Magnetometer Driver" + +config MAG_MAG3110 + bool "Freescale MAG3110" + help + Support for the Freescale MAG3110 magnetometer. + +config MAG_MMC5603NJ + bool "MEMSIC MMC5603NJ" + help + Support for the MEMSIC MMC5603NJ magnetometer. + +endchoice + +endif # MAG + +module = DRIVER_IMU +module-str = IMU +source "src/fw/Kconfig.template.log_level" + +endmenu diff --git a/src/fw/drivers/imu/bma255/bma255.c b/src/fw/drivers/imu/bma255/bma255.c deleted file mode 100644 index 94413179c8..0000000000 --- a/src/fw/drivers/imu/bma255/bma255.c +++ /dev/null @@ -1,898 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bma255.h" -#include "bma255_regs.h" -#include "bma255_private.h" - -#include "console/prompt.h" -#include "drivers/accel.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/rtc.h" -#include "drivers/spi.h" -#include "kernel/util/delay.h" -#include "kernel/util/sleep.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/units.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -#define BMA255_DEBUG 0 - -#if BMA255_DEBUG -#define BMA255_DBG(msg, ...) \ - do { \ - PBL_LOG(LOG_LEVEL_DEBUG, msg, __VA_ARGS__); \ - } while (0); -#else -#define BMA255_DBG(msg, ...) -#endif - - -#define SELFTEST_SIGN_POSITIVE (0x1 << 2) -#define SELFTEST_SIGN_NEGATIVE (0x0) - -// The BMA255 is capable of storing up to 32 frames. -// Conceptually each frame consists of three 16-bit words corresponding to the x, y and z- axis. -#define BMA255_FIFO_MAX_FRAMES (32) -#define BMA255_FIFO_FRAME_SIZE_BYTES (3 * 2) -#define BMA255_FIFO_SIZE_BYTES (BMA255_FIFO_MAX_FRAMES * BMA255_FIFO_FRAME_SIZE_BYTES) - - -// Driver state -static BMA255PowerMode s_accel_power_mode = BMA255PowerMode_Normal; -static bool s_fifo_is_enabled = false; -static bool s_shake_detection_enabled = false; -static bool s_accel_outstanding_motion_work = false; -static bool s_accel_outstanding_data_work = false; -static bool s_fifo_overrun_detected = false; - - -// Forward declarations -static void prv_configure_operating_mode(void); -static void prv_bma255_IRQ1_handler(bool *should_context_switch); -static void prv_bma255_IRQ2_handler(bool *should_context_switch); -static void prv_set_accel_power_mode(BMA255PowerMode mode); - - -// The BMA255 reports each G in powers of 2 with full deflection at +-2^11 -// So scale all readings by (scale)/(2^11) to get G -// And scale the result by 1000 to allow for easier interger math -typedef enum { - BMA255Scale_2G = 980, // 2000/2048 - BMA255Scale_4G = 1953, // 4000/2048 - BMA255Scale_8G = 3906, // 8000/2048 - BMA255Scale_16G = 7813, // 16000/2048 -} BMA255Scale; - -static int16_t s_raw_unit_to_mgs = BMA255Scale_2G; - -typedef enum { - AccelOperatingMode_Data, - AccelOperatingMode_ShakeDetection, - AccelOperatingMode_DoubleTapDetection, - - AccelOperatingModeCount, -} AccelOperatingMode; - -static struct { - bool enabled; - bool using_interrupts; - BMA255ODR odr; -} s_operating_states[AccelOperatingModeCount] = { - [AccelOperatingMode_Data] = { - .enabled = false, - .using_interrupts = false, - .odr = BMA255ODR_125HZ, - }, - [AccelOperatingMode_ShakeDetection] = { - .enabled = false, - .using_interrupts = false, - .odr = BMA255ODR_125HZ, - }, - [AccelOperatingMode_DoubleTapDetection] = { - .enabled = false, - .using_interrupts = false, - .odr = BMA255ODR_125HZ, - }, -}; - -void bma255_init(void) { - bma255_gpio_init(); - if (!bma255_query_whoami()) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to query BMA255"); - return; - } - const bool pass = bma255_selftest(); - if (pass) { - PBL_LOG(LOG_LEVEL_DEBUG, "BMA255 self test pass, all 3 axis"); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "BMA255 self test failed one or more axis"); - } - - // Workaround to fix FIFO Frame Leakage: Disable temperature sensor (we're not using it anyways) - // See Section 2.2.1 of https://drive.google.com/a/pebble.com/file/d/0B9tTN3OlYns3bEZaczdoZUU3UEk/view - bma255_write_register(BMA255Register_EXTENDED_MEMORY_MAP, BMA255_EXTENDED_MEMORY_MAP_OPEN); - bma255_write_register(BMA255Register_EXTENDED_MEMORY_MAP, BMA255_EXTENDED_MEMORY_MAP_OPEN); - bma255_write_register(BMA255Register_TEMPERATURE_SENSOR_CTRL, BMA255_TEMPERATURE_SENSOR_DISABLE); - bma255_write_register(BMA255Register_EXTENDED_MEMORY_MAP, BMA255_EXTENDED_MEMORY_MAP_CLOSE); - - exti_configure_pin(BOARD_CONFIG_ACCEL.accel_ints[0], ExtiTrigger_Rising, prv_bma255_IRQ1_handler); - exti_configure_pin(BOARD_CONFIG_ACCEL.accel_ints[1], ExtiTrigger_Rising, prv_bma255_IRQ2_handler); -} - -bool bma255_query_whoami(void) { - const uint8_t chip_id = bma255_read_register(BMA255Register_BGW_CHIP_ID); - PBL_LOG(LOG_LEVEL_DEBUG, "Read BMA255 whoami byte 0x%"PRIx8", expecting 0x%"PRIx8, - chip_id, BMA255_CHIP_ID); - return (chip_id == BMA255_CHIP_ID); -} - -static uint64_t prv_get_curr_system_time_ms(void) { - time_t time_s; - uint16_t time_ms; - rtc_get_time_ms(&time_s, &time_ms); - return (((uint64_t)time_s) * 1000 + time_ms); -} - -void bma255_set_scale(BMA255Scale scale) { - uint8_t value = 0; - switch (scale) { - case BMA255Scale_2G: - value = 0x3; - break; - case BMA255Scale_4G: - value = 0x5; - break; - case BMA255Scale_8G: - value = 0x8; - break; - case BMA255Scale_16G: - value = 0xc; - break; - default: - WTF; - } - bma255_write_register(BMA255Register_PMU_RANGE, value); - s_raw_unit_to_mgs = scale; -} - -static int16_t prv_raw_to_mgs(int16_t raw_val) { - int16_t mgs = ((int32_t)raw_val * s_raw_unit_to_mgs) / 1000; - return mgs; -} - -static int16_t prv_conv_raw_to_12bit(const uint8_t registers[2]) { - int16_t reading = ((registers[0] >> 4) & 0x0F) | ((int16_t)registers[1] << 4); - if (reading & 0x0800) { - reading |= 0xF000; - } - return reading; -} - -static void prv_convert_accel_raw_data_to_mgs(const uint8_t *buf, AccelDriverSample *data) { - int16_t readings[3]; - for (int i = 0; i < 3; ++i) { - readings[i] = prv_conv_raw_to_12bit(&buf[i*2]); - } - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - *data = (AccelDriverSample) { - .x = (cfg->axes_inverts[AXIS_X] ? -1 : 1) * - prv_raw_to_mgs(readings[cfg->axes_offsets[AXIS_X]]), - .y = (cfg->axes_inverts[AXIS_Y] ? -1 : 1) * - prv_raw_to_mgs(readings[cfg->axes_offsets[AXIS_Y]]), - .z = (cfg->axes_inverts[AXIS_Z] ? -1 : 1) * - prv_raw_to_mgs(readings[cfg->axes_offsets[AXIS_Z]]), - }; -} - -static void prv_read_curr_accel_data(AccelDriverSample *data) { - uint8_t raw_buf[BMA255_FIFO_FRAME_SIZE_BYTES]; - bma255_burst_read(BMA255Register_ACCD_X_LSB, raw_buf, sizeof(raw_buf)); - - prv_convert_accel_raw_data_to_mgs(raw_buf, data); - // FIXME: assuming the timestamp on the samples is NOW! PBL-33765 - data->timestamp_us = prv_get_curr_system_time_ms() * 1000; - - BMA255_DBG("%"PRId16" %"PRId16" %"PRId16, data->x, data->y, data->z); -} - -typedef enum { - BMA255Axis_X = 0, - BMA255Axis_Y, - BMA255Axis_Z, -} BMA255Axis; - -static void prv_drain_fifo(void) { - // TODO: I think the ideal thing to do here would be to invoke the accel_cb_new_sample() while - // the SPI transaction is in progress so we don't need a static ~500 byte buffer. (This is what - // we do in the bmi160 driver) However, since we are oversampling super aggressively with the - // bma255, I'm concerned about changing the timing of how fast we drain things. Thus, just use a - // static buffer for now. This should be safe because only one thread should be draining the data. - static AccelDriverSample data[BMA255_FIFO_MAX_FRAMES]; - const uint64_t timestamp_us = prv_get_curr_system_time_ms() * 1000; - const uint32_t sampling_interval_us = accel_get_sampling_interval(); - - uint8_t fifo_status = bma255_read_register(BMA255Register_FIFO_STATUS); - BMA255_DBG("Drain %"PRIu8" samples", num_samples_available); - - const uint8_t num_samples_available = fifo_status & 0x3f; - if (num_samples_available == 0) { - return; - } - - bma255_prepare_txn(BMA255Register_FIFO_DATA | BMA255_READ_FLAG); - for (int i = 0; i < num_samples_available; ++i) { - uint8_t buf[BMA255_FIFO_FRAME_SIZE_BYTES]; - for (int j = 0; j < BMA255_FIFO_FRAME_SIZE_BYTES; ++j) { - buf[j] = bma255_send_and_receive_byte(0); - } - prv_convert_accel_raw_data_to_mgs(buf, &data[i]); - } - bma255_end_txn(); - - // Timestamp & Dispatch data - for (int i = 0; i < num_samples_available; ++i) { - // Make a timestamp approximation based on the current time, the sample - // being processed and the sampling interval. - data[i].timestamp_us = timestamp_us - ((num_samples_available - i) * sampling_interval_us); - BMA255_DBG("%2d: %"PRId16" %"PRId16" %"PRId16" %"PRIu32, - i, data[i].x, data[i].y, data[i].z, (uint32_t)data[i].timestamp_us); - accel_cb_new_sample(&data[i]); - } - - // clear of fifo overrun flag must happen after draining samples, also the samples available will - // get drained too! - if ((fifo_status & 0x80) && !s_fifo_overrun_detected) { - s_fifo_overrun_detected = true; - // We don't clear the interrupt here because you are only supposed to touch the fifo config - // registers while in standby mode. - PBL_LOG(LOG_LEVEL_WARNING, "bma255 fifo overrun detected: 0x%x!", fifo_status); - } -} - -static void prv_handle_data(void) { - s_accel_outstanding_data_work = false; - if (s_fifo_is_enabled) { - prv_drain_fifo(); - return; - } - - AccelDriverSample data; - prv_read_curr_accel_data(&data); - accel_cb_new_sample(&data); -} - -static void prv_handle_motion_interrupts(void) { - s_accel_outstanding_motion_work = false; - - const uint8_t int0_status = bma255_read_register(BMA255Register_INT_STATUS_0); - const uint8_t int2_status = bma255_read_register(BMA255Register_INT_STATUS_2); - - bool anymotion = (int0_status & BMA255_INT_STATUS_0_SLOPE_MASK); - if (anymotion) { - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - IMUCoordinateAxis axis = AXIS_X; - bool invert = false; - - if (int2_status & BMA255_INT_STATUS_2_SLOPE_FIRST_X) { - axis = AXIS_X; - invert = cfg->axes_inverts[AXIS_X]; - } else if (int2_status & BMA255_INT_STATUS_2_SLOPE_FIRST_Y) { - axis = AXIS_Y; - invert = cfg->axes_inverts[AXIS_Y]; - } else if (int2_status & BMA255_INT_STATUS_2_SLOPE_FIRST_Z) { - axis = AXIS_Z; - invert = cfg->axes_inverts[AXIS_Z]; - } else { - BMA255_DBG("No Axis?: 0x%"PRIx8" 0x%"PRIx8, int0_status, int2_status); - } - int32_t direction = ((int2_status & BMA255_INT_STATUS_2_SLOPE_SIGN) == 0) ? 1 : -1; - direction *= (invert ? -1 : 1); - - accel_cb_shake_detected(axis, direction); - } -} - -// Services tap/motion interrupts -static void prv_bma255_IRQ1_handler(bool *should_context_switch) { - BMA255_DBG("Slope Int"); - if (!s_accel_outstanding_motion_work) { - s_accel_outstanding_motion_work = true; - accel_offload_work_from_isr(prv_handle_motion_interrupts, should_context_switch); - } -} - -// Services data / fifo interrupts. -// NOTE: The BMA255 Errata specifically states that we should keep the fifo / -// data interrupt on INT2 to avoid "data inconsistencies" which arise when -// we have it fifo / data interrupt on INT1. -static void prv_bma255_IRQ2_handler(bool *should_context_switch) { - BMA255_DBG("Data Int"); - if (!s_accel_outstanding_data_work) { - s_accel_outstanding_data_work = true; - accel_offload_work_from_isr(prv_handle_data, should_context_switch); - } -} - -static void prv_update_accel_interrupts(bool enable, AccelOperatingMode mode) { - s_operating_states[mode].using_interrupts = enable; - - bool enable_interrupts = false; - for (uint32_t i = 0; i < ARRAY_LENGTH(s_operating_states); i++) { - if (s_operating_states[i].using_interrupts) { - enable_interrupts = true; - break; - } - } - - if (enable_interrupts) { - exti_enable(BOARD_CONFIG_ACCEL.accel_ints[0]); - exti_enable(BOARD_CONFIG_ACCEL.accel_ints[1]); - } else { - exti_disable(BOARD_CONFIG_ACCEL.accel_ints[0]); - exti_disable(BOARD_CONFIG_ACCEL.accel_ints[1]); - } -} - -uint32_t accel_get_sampling_interval(void) { - BMA255ODR odr_max = 0; - for (uint32_t i = 0; i < ARRAY_LENGTH(s_operating_states); i++) { - if (s_operating_states[i].enabled) { - odr_max = MAX(odr_max, s_operating_states[i].odr); - } - } - - uint32_t sample_interval = 0; - switch (odr_max) { - case BMA255ODR_1HZ: - sample_interval = BMA255SampleInterval_1HZ; - break; - case BMA255ODR_10HZ: - sample_interval = BMA255SampleInterval_10HZ; - break; - case BMA255ODR_19HZ: - sample_interval = BMA255SampleInterval_19HZ; - break; - case BMA255ODR_83HZ: - sample_interval = BMA255SampleInterval_83HZ; - break; - case BMA255ODR_125HZ: - sample_interval = BMA255SampleInterval_125HZ; - break; - case BMA255ODR_166HZ: - sample_interval = BMA255SampleInterval_166HZ; - break; - case BMA255ODR_250HZ: - sample_interval = BMA255SampleInterval_250HZ; - break; - default: - WTF; - } - return sample_interval; -} - -//! Set the LOW_POWER and LPW registers as required. -//! The LPW register is masked because it contains the sleep duration for our desired ODR. -static void prv_enter_power_mode(BMA255PowerMode mode) { - bma255_write_register(BMA255Register_PMU_LOW_POWER, - s_power_mode[mode].low_power << BMA255_LOW_POWER_SHIFT); - bma255_read_modify_write(BMA255Register_PMU_LPW, - s_power_mode[mode].lpw << BMA255_LPW_POWER_SHIFT, - BMA255_LPW_POWER_MASK); - - // Workaround for error in transition to Suspend / Standby - if (mode == BMA255PowerMode_Suspend || mode == BMA255PowerMode_Standby) { - // Write to FIFO_CONFIG_1 to exit some unknown "intermittent state" - // NOTE: This will clear the FIFO & FIFO status. - bma255_read_modify_write(BMA255Register_FIFO_CONFIG_1, 0, 0); - } -} - -static void prv_set_accel_power_mode(BMA255PowerMode mode) { - PBL_ASSERTN(mode == BMA255PowerMode_Normal || - mode == BMA255PowerMode_LowPower1 || - mode == BMA255PowerMode_Standby); - - // Workaround for entering Normal Mode - // LPM1 => Normal requires us to go through Suspend mode - // LPM2 => Normal requires us to go through Standby mode - if (mode == BMA255PowerMode_Normal) { - if (s_accel_power_mode == BMA255PowerMode_LowPower1) { - prv_enter_power_mode(BMA255PowerMode_Suspend); - } else if (s_accel_power_mode == BMA255PowerMode_LowPower2) { - prv_enter_power_mode(BMA255PowerMode_Standby); - } - } - - prv_enter_power_mode(mode); - - BMA255_DBG("BMA555: power level set to: 0x%x and 0x%x", - bma255_read_register(BMA255Register_PMU_LPW), - bma255_read_register(BMA255Register_PMU_LOW_POWER)); - - s_accel_power_mode = mode; -} - -static BMA255ODR prv_get_odr(BMA255SampleInterval sample_interval) { - BMA255ODR odr = 0; - switch (sample_interval) { - case BMA255SampleInterval_1HZ: - odr = BMA255ODR_1HZ; - break; - case BMA255SampleInterval_10HZ: - odr = BMA255ODR_10HZ; - break; - case BMA255SampleInterval_19HZ: - odr = BMA255ODR_19HZ; - break; - case BMA255SampleInterval_83HZ: - odr = BMA255ODR_83HZ; - break; - case BMA255SampleInterval_125HZ: - odr = BMA255ODR_125HZ; - break; - case BMA255SampleInterval_166HZ: - odr = BMA255ODR_166HZ; - break; - case BMA255SampleInterval_250HZ: - odr = BMA255ODR_250HZ; - break; - default: - WTF; - } - return odr; -} - -static BMA255SampleInterval prv_get_supported_sampling_interval(uint32_t interval_us) { - BMA255SampleInterval sample_interval; - if (BMA255SampleInterval_1HZ <= interval_us) { - sample_interval = BMA255SampleInterval_1HZ; - } else if (BMA255SampleInterval_10HZ <= interval_us) { - sample_interval = BMA255SampleInterval_10HZ; - } else if (BMA255SampleInterval_19HZ <= interval_us) { - sample_interval = BMA255SampleInterval_19HZ; - } else if (BMA255SampleInterval_83HZ <= interval_us) { - sample_interval = BMA255SampleInterval_83HZ; - } else if (BMA255SampleInterval_125HZ <= interval_us) { - sample_interval = BMA255SampleInterval_125HZ; - } else if (BMA255SampleInterval_166HZ <= interval_us) { - sample_interval = BMA255SampleInterval_166HZ; - } else if (BMA255SampleInterval_250HZ <= interval_us) { - sample_interval = BMA255SampleInterval_250HZ; - } else { - sample_interval = BMA255SampleInterval_250HZ; - } - return sample_interval; -} - -static void prv_enable_operating_mode(AccelOperatingMode mode, - BMA255SampleInterval sample_interval) { - s_operating_states[mode].enabled = true; - s_operating_states[mode].odr = prv_get_odr(sample_interval); - prv_configure_operating_mode(); -} - -static void prv_disable_operating_mode(AccelOperatingMode mode) { - s_operating_states[mode].enabled = false; - prv_configure_operating_mode(); -} - -uint32_t accel_set_sampling_interval(uint32_t interval_us) { - BMA255SampleInterval actual_interval = prv_get_supported_sampling_interval(interval_us); - - // FIXME: For now, tie us to 125Hz. 125Hz is a rate that is easy enough to - // subsample to all of our supported accel service rates, and also cuts down power consumption - // from the 140uA range to 100uA. - // Being able to sample at a lower rate like 38Hz will be able to get us down into the 40uA range. - // - // By forcing a sample interval of 125Hz here, we will never use a different - // rate, and the accelerometer service will be made aware of our running rate. - actual_interval = BMA255SampleInterval_125HZ; - - prv_enable_operating_mode(AccelOperatingMode_Data, actual_interval); - - return accel_get_sampling_interval(); -} - -static void prv_configure_operating_mode(void) { - BMA255SampleInterval interval_us = accel_get_sampling_interval(); - const uint8_t odr = (uint8_t)prv_get_odr(interval_us); - const uint8_t bw = s_odr_settings[odr].bw; - const uint8_t tsleep = s_odr_settings[odr].tsleep; - - // Set the BW and TSleep to get the ODR we expect. - bma255_write_register(BMA255Register_PMU_BW, bw); - bma255_read_modify_write(BMA255Register_PMU_LPW, - tsleep << BMA255_LPW_SLEEP_DUR_SHIFT, - BMA255_LPW_SLEEP_DUR_MASK); - - PBL_LOG(LOG_LEVEL_DEBUG, "Set sampling rate to %"PRIu32, (uint32_t)(1000000/interval_us)); - - if (s_accel_power_mode == BMA255PowerMode_Normal) { - // This should only execute on startup or if the power mode - // is left in normal power mode for some reason - PBL_LOG(LOG_LEVEL_DEBUG, "Enable low power mode"); - prv_set_accel_power_mode(BMA255PowerMode_LowPower1); - } -} - -int accel_peek(AccelDriverSample *data) { - prv_read_curr_accel_data(data); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// FIFO Support -//////////////////////////////////////////////////////////////////////////////// - -static void prv_program_fifo_register(uint8_t address, uint8_t data) { - // To prevent lockups of the fifo, the fifo config registers should only be programmed - // while in standby mode - PBL_ASSERTN(s_accel_power_mode == BMA255PowerMode_Standby); - const int retries = 2; - uint8_t value; - for (int i = 0; i <= retries; i++) { - bma255_write_register(address, data); - value = bma255_read_register(address); - if (value == data) { - return; // Write took, we are good - } - PBL_LOG(LOG_LEVEL_DEBUG, "FIFO config write failed, initiating workaround ..."); - - // FIXME: Sometimes writes to the FIFO registers fail. I am suspicious that the bma255 enters - // suspend mode instead of standby mode. (The datasheet states that FIFO_CONFIG registers - // accesses fail in suspend mode). It seems like the issue can be worked around by attempting - // to enter standby mode again. Hopefully, bosch can illuminate for us what is going on here - // but in the meantime let's use this workaround. - prv_set_accel_power_mode(BMA255PowerMode_Normal); - prv_set_accel_power_mode(BMA255PowerMode_Standby); - } - - PBL_LOG(LOG_LEVEL_WARNING, "Failed to program fifo reg, 0x%"PRIx8" = 0x%"PRIx8, address, data); -} - -static void prv_set_fifo_mode(BMA255FifoMode mode) { - BMA255_DBG("Set Fifo Mode: 0x%x", mode); - const uint8_t out = - (mode << BMA255_FIFO_MODE_SHIFT) | (BMA255FifoDataSel_XYZ << BMA255_FIFO_DATA_SEL_SHIFT); - prv_program_fifo_register(BMA255Register_FIFO_CONFIG_1, out); - // If the fifo had overflowed, the write above will have cleared the flag - s_fifo_overrun_detected = false; -} - -static void prv_configure_fifo_interrupts(bool enable_int, bool use_fifo) { - BMA255_DBG("Enabling FIFO Interrupts: %d %d", (int)enable_int, (int)use_fifo); - uint8_t map_value; - uint8_t en_value; - if (!enable_int) { - map_value = 0; - en_value = 0; - } else if (!use_fifo) { - map_value = BMA255_INT_MAP_1_INT2_DATA; - en_value = BMA255_INT_EN_1_DATA; - } else { - map_value = BMA255_INT_MAP_1_INT2_FIFO_WATERMARK; - en_value = BMA255_INT_EN_1_FIFO_WATERMARK; - } - - bma255_write_register(BMA255Register_INT_MAP_1, map_value); - bma255_write_register(BMA255Register_INT_EN_1, en_value); - - prv_update_accel_interrupts(enable_int, AccelOperatingMode_Data); -} - -void accel_set_num_samples(uint32_t num_samples) { - // Disable interrupts so they won't fire while we change sampling rate - prv_configure_fifo_interrupts(false, false); - - // Workaround some bma255 issues: - // Need to use Standby Mode to read/write the FIFO_CONFIG registers. - prv_set_accel_power_mode(BMA255PowerMode_Normal); // Need to transition to Normal first - prv_set_accel_power_mode(BMA255PowerMode_Standby); - - if (num_samples > BMA255_FIFO_MAX_FRAMES) { - num_samples = BMA255_FIFO_MAX_FRAMES; - } - BMA255_DBG("Setting num samples to: %"PRIu32, num_samples); - - // Note that with the bma255, we do not want to use Bypass mode when - // collecting a single sample as this will result in uneven sampling. - // The accelerometer will wake up, provide several samples in quick - // succession, and then go back to sleep for a period. Looking at the INT2 - // line shows similar to this: - // _ _ _ _ _ _ - // .... ____| |_| |_| |______________________| |_| |_| |_________ ..... - // - // By using a FIFO of depth 1, the bma255 respects EST mode and will provide - // samples at a predictable interval and rate. - const bool use_fifo = (num_samples > 0); - - if (use_fifo) { - PBL_LOG(LOG_LEVEL_DEBUG, "Enabling FIFO"); - // Watermark is the number of samples to be collected - prv_program_fifo_register(BMA255Register_FIFO_CONFIG_0, num_samples); - prv_set_fifo_mode(BMA255FifoMode_Fifo); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Disabling FIFO"); - prv_set_fifo_mode(BMA255FifoMode_Bypass); - } - - prv_set_accel_power_mode(BMA255PowerMode_Normal); - prv_set_accel_power_mode(BMA255PowerMode_LowPower1); - - const bool enable_int = (num_samples != 0); - prv_configure_fifo_interrupts(enable_int, use_fifo); - - s_fifo_is_enabled = use_fifo; -} - -//////////////////////////////////////////////////////////////////////////////// -// Shake Detection -//////////////////////////////////////////////////////////////////////////////// - -static void prv_enable_shake_detection(void) { - bma255_write_register(BMA255Register_INT_EN_0, BMA255_INT_EN_0_SLOPE_X_EN | - BMA255_INT_EN_0_SLOPE_Y_EN | - BMA255_INT_EN_0_SLOPE_Z_EN); - - bma255_write_register(BMA255Register_INT_MAP_0, BMA255_INT_MAP_0_INT1_SLOPE); - - // configure the anymotion interrupt to fire after 4 successive - // samples are over the threhold specified - accel_set_shake_sensitivity_high(false /* sensitivity_high */); - bma255_write_register(BMA255Register_INT_5, - BMA255_INT_5_SLOPE_DUR_MASK << BMA255_INT_5_SLOPE_DUR_SHIFT); - - prv_enable_operating_mode(AccelOperatingMode_ShakeDetection, BMA255SampleInterval_83HZ); -} - -static void prv_disable_shake_detection(void) { - // Don't worry about the configuration registers but disable interrupts from the accel - bma255_write_register(BMA255Register_INT_EN_0, 0); - - prv_disable_operating_mode(AccelOperatingMode_ShakeDetection); -} - -void accel_enable_shake_detection(bool enable) { - if (s_shake_detection_enabled == enable) { - // the requested change matches what we already have! - return; - } - PBL_LOG(LOG_LEVEL_DEBUG, "%s shake detection", enable ? "Enabling" : "Disabling"); - - prv_update_accel_interrupts(enable, AccelOperatingMode_ShakeDetection); - if (enable) { - prv_enable_shake_detection(); - } else { - prv_disable_shake_detection(); - } - - s_shake_detection_enabled = enable; -} - -void accel_set_shake_sensitivity_high(bool sensitivity_high) { - // Configure the threshold level at which the BMI160 will think shake has occurred - if (sensitivity_high) { - bma255_write_register(BMA255Register_INT_6, - BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdLow]); - } else { - bma255_write_register(BMA255Register_INT_6, - BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdHigh]); - } -} - -bool accel_get_shake_detection_enabled(void) { - return s_shake_detection_enabled; -} - -//////////////////////////////////////////////////////////////////////////////// -// Selftest Support -//////////////////////////////////////////////////////////////////////////////// - -static void prv_soft_reset(void) { - bma255_write_register(BMA255Register_BGW_SOFTRESET, BMA255_SOFT_RESET_VALUE); - psleep(4); -} - -// Minimum thresholds for axis delta in mgs at 4G scale -static const uint16_t SELFTEST_THRESHOLDS[] = { - [BMA255Axis_X] = 800, - [BMA255Axis_Y] = 800, - [BMA255Axis_Z] = 400, -}; - -static const char AXIS_NAMES[] = { - [BMA255Axis_X] = 'X', - [BMA255Axis_Y] = 'Y', - [BMA255Axis_Z] = 'Z', -}; - -static const uint8_t AXIS_REGISTERS[] = { - [BMA255Axis_X] = BMA255Register_ACCD_X_LSB, - [BMA255Axis_Y] = BMA255Register_ACCD_Y_LSB, - [BMA255Axis_Z] = BMA255Register_ACCD_Z_LSB, -}; - -static int16_t prv_read_axis(BMA255Axis axis, uint8_t *new_data) { - uint8_t raw_buf[2]; - bma255_burst_read(AXIS_REGISTERS[axis], raw_buf, sizeof(raw_buf)); - int16_t reading = prv_conv_raw_to_12bit(raw_buf); - if (new_data) { - *new_data = raw_buf[0] & 0x01; - } - return reading; -} - -static bool prv_selftest_axis(BMA255Axis axis) { - uint8_t axis_bits; - switch (axis) { - case BMA255Axis_X: - axis_bits = 0x01; - break; - case BMA255Axis_Y: - axis_bits = 0x02; - break; - case BMA255Axis_Z: - axis_bits = 0x03; - break; - default: - WTF; - } - - - // g-range should be 4g for self-test - bma255_set_scale(BMA255Scale_4G); - - psleep(2); // wait for a new sample - - uint8_t new_data; - int16_t before = prv_read_axis(axis, &new_data); - before = prv_raw_to_mgs(before); - - // Positive axis - bma255_write_register(BMA255Register_PMU_SELFTEST, axis_bits | SELFTEST_SIGN_POSITIVE); - psleep(50); - uint8_t new_positive; - int16_t positive = prv_read_axis(axis, &new_positive); - positive = prv_raw_to_mgs(positive); - - prv_soft_reset(); - bma255_set_scale(BMA255Scale_4G); - - // Negative axis - bma255_write_register(BMA255Register_PMU_SELFTEST, axis_bits | SELFTEST_SIGN_NEGATIVE); - psleep(50); - uint8_t new_negative; - int16_t negative = prv_read_axis(axis, &new_negative); - negative = prv_raw_to_mgs(negative); - - prv_soft_reset(); - - int delta = positive - negative; - delta = abs(delta); - - PBL_LOG(LOG_LEVEL_DEBUG, - "Self test axis %c: %d Pos: %d Neg: %d Delta: %d (required %d)", - AXIS_NAMES[axis], before, positive, - negative, delta, SELFTEST_THRESHOLDS[axis]); - - if (delta < SELFTEST_THRESHOLDS[axis]) { - PBL_LOG(LOG_LEVEL_ERROR, "Self test failed for axis %c: %d < %d", - AXIS_NAMES[axis], delta, SELFTEST_THRESHOLDS[axis]); - return false; - } - - if ((new_data + new_negative + new_positive) != 3) { - PBL_LOG(LOG_LEVEL_ERROR, "Self test problem? Not logging data? %d %d %d", - new_data, new_positive, new_negative); - } - - return true; -} - -bool bma255_selftest(void) { - // calling selftest_axis resets the device - bool pass = true; - pass &= prv_selftest_axis(BMA255Axis_X); - pass &= prv_selftest_axis(BMA255Axis_Y); - pass &= prv_selftest_axis(BMA255Axis_Z); - - // g-range should be 4g to copy the BMI160 - bma255_set_scale(BMA255Scale_4G); - - return pass; -} - -bool accel_run_selftest(void) { - return bma255_selftest(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Debug Commands -//////////////////////////////////////////////////////////////////////////////// - -void command_accel_status(void) { - const uint8_t bw = bma255_read_register(BMA255Register_PMU_BW); - const uint8_t lpw = bma255_read_register(BMA255Register_PMU_LPW); - const uint8_t lp = bma255_read_register(BMA255Register_PMU_LOW_POWER); - const uint8_t fifo_cfg0 = bma255_read_register(BMA255Register_FIFO_CONFIG_0); - const uint8_t fifo_cfg1 = bma255_read_register(BMA255Register_FIFO_CONFIG_1); - const uint8_t fifo_status = bma255_read_register(BMA255Register_FIFO_STATUS); - const uint8_t int_map_0 = bma255_read_register(BMA255Register_INT_MAP_0); - const uint8_t int_en_0 = bma255_read_register(BMA255Register_INT_EN_0); - const uint8_t int_map_1 = bma255_read_register(BMA255Register_INT_MAP_1); - const uint8_t int_en_1 = bma255_read_register(BMA255Register_INT_EN_1); - const uint8_t int_map_2 = bma255_read_register(BMA255Register_INT_MAP_2); - const uint8_t int_en_2 = bma255_read_register(BMA255Register_INT_EN_2); - const uint8_t int_status_0 = bma255_read_register(BMA255Register_INT_STATUS_0); - const uint8_t int_status_1 = bma255_read_register(BMA255Register_INT_STATUS_1); - const uint8_t int_status_2 = bma255_read_register(BMA255Register_INT_STATUS_2); - const uint8_t int_status_3 = bma255_read_register(BMA255Register_INT_STATUS_3); - - char buf[64]; - prompt_send_response_fmt(buf, 64, "(0x10) Bandwidth: 0x%"PRIx8, bw); - - prompt_send_response_fmt(buf, 64, "(0x11) LPW: 0x%"PRIx8, lpw); - prompt_send_response_fmt(buf, 64, " suspend: 0x%"PRIx8, (lpw & (1 << 7)) != 0); - prompt_send_response_fmt(buf, 64, " lowpower_en: 0x%"PRIx8, (lpw & (1 << 6)) != 0); - prompt_send_response_fmt(buf, 64, " deep_suspend: 0x%"PRIx8, (lpw & (1 << 5)) != 0); - prompt_send_response_fmt(buf, 64, " sleep_dur: 0x%"PRIx8, (lpw & 0b11110) >> 1); - - prompt_send_response_fmt(buf, 64, "(0x12) Low_Power: 0x%"PRIx8, lp); - prompt_send_response_fmt(buf, 64, " lowpower_mode: 0x%"PRIx8, (lp & (1 << 6)) != 0); - prompt_send_response_fmt(buf, 64, " sleeptimer_mode: 0x%"PRIx8, (lp & (1 << 5)) != 0); - - prompt_send_response_fmt(buf, 64, "(0x30) FIFO Config 0: 0x%"PRIx8, fifo_cfg0); - prompt_send_response_fmt(buf, 64, " Watermark: 0x%"PRIx8, fifo_cfg0 & 0b111111); - - prompt_send_response_fmt(buf, 64, "(0x3e) FIFO Config 1: 0x%"PRIx8, fifo_cfg1); - prompt_send_response_fmt(buf, 64, " Mode: 0x%"PRIx8, (fifo_cfg1 & (0x3 << 6)) >> 6); - prompt_send_response_fmt(buf, 64, " Data Select: 0x%"PRIx8, fifo_cfg1 & 0x3); - - prompt_send_response_fmt(buf, 64, "(0x0e) Fifo Status: 0x%"PRIx8, fifo_status); - prompt_send_response_fmt(buf, 64, " Num Samples: 0x%"PRIx8, (fifo_status & 0x3f)); - - prompt_send_response_fmt(buf, 64, "(0x19) Int Map 0: 0x%"PRIx8, int_map_0); - prompt_send_response_fmt(buf, 64, "(0x16) Int EN 0: 0x%"PRIx8, int_en_0); - - prompt_send_response_fmt(buf, 64, "(0x1a) Int Map 1: 0x%"PRIx8, int_map_1); - prompt_send_response_fmt(buf, 64, "(0x17) Int EN 1: 0x%"PRIx8, int_en_1); - - prompt_send_response_fmt(buf, 64, "(0x1b) Int Map 2: 0x%"PRIx8, int_map_2); - prompt_send_response_fmt(buf, 64, "(0x18) Int EN 2: 0x%"PRIx8, int_en_2); - - prompt_send_response_fmt(buf, 64, "(0x0a) Int Status 0: 0x%"PRIx8, int_status_0); - prompt_send_response_fmt(buf, 64, "(0x0a) Int Status 1: 0x%"PRIx8, int_status_1); - prompt_send_response_fmt(buf, 64, "(0x0b) Int Status 2: 0x%"PRIx8, int_status_2); - prompt_send_response_fmt(buf, 64, "(0x0c) Int Status 3: 0x%"PRIx8, int_status_3); -} - -void command_accel_selftest(void) { - const bool success = accel_run_selftest(); - char *response = (success) ? "Pass" : "Fail"; - prompt_send_response(response); -} - -void command_accel_softreset(void) { - prv_soft_reset(); -} diff --git a/src/fw/drivers/imu/bma255/bma255.h b/src/fw/drivers/imu/bma255/bma255.h deleted file mode 100644 index be5757e375..0000000000 --- a/src/fw/drivers/imu/bma255/bma255.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void bma255_init(void); -bool bma255_query_whoami(void); - -//! Power Modes -//! These are the supported power modes, and some rough estimates on power consumption. -//! There is a small set of transitions between power modes that are supported. To make life -//! easy, we will always go through Normal Mode, which allows transition to/from all power modes. -//! Use this enum to index into the \ref s_power_mode table, which contains configurations for each. -typedef enum { - BMA255PowerMode_Normal = 0, // 130uA - BMA255PowerMode_Suspend, // 2.1uA - BMA255PowerMode_Standby, // 62uA - BMA255PowerMode_DeepSuspend, // 1uA - BMA255PowerMode_LowPower1, - BMA255PowerMode_LowPower2, - - BMA255PowerModeCount -} BMA255PowerMode; - -//! Tsleep values. -//! These are defined to the value we pur into the PMU_LPW register. -//! See Table 3 of datasheet: "Sleep Phase Duration" -typedef enum { - BMA255SleepDuration_0p5ms = 5, - BMA255SleepDuration_1ms = 6, - BMA255SleepDuration_2ms = 7, - BMA255SleepDuration_4ms = 8, - BMA255SleepDuration_6ms = 9, - BMA255SleepDuration_10ms = 10, - BMA255SleepDuration_25ms = 11, - BMA255SleepDuration_50ms = 12, - BMA255SleepDuration_100ms = 13, - BMA255SleepDuration_500ms = 14, - BMA255SleepDuration_1000ms = 15, - - BMA255SleepDurationCount -} BMA255SleepDuration; - -//! These are the natively supported filter bandwidths of the bma255. -//! Note that power consumption is tightly tied to the filter bandwidth. In -//! order to run acceptably, we need to keep a bandwidth up in the 500Hz ~ 1kHz range. -//! Please see discussion below for more information on Bandwith, TSleep and ODR. -typedef enum { - BMA255Bandwidth_7p81HZ = 8, - BMA255Bandwidth_15p63HZ = 9, - BMA255Bandwidth_31p25HZ = 10, - BMA255Bandwidth_62p5HZ = 11, - BMA255Bandwidth_125HZ = 12, - BMA255Bandwidth_250HZ = 13, - BMA255Bandwidth_500HZ = 14, - BMA255Bandwidth_1000HZ = 15, - - BMA255BandwidthCount -} BMA255Bandwidth; - -//! In order to acheive low power consumptions, the BMA255 Output Data Rate (ODR) -//! is determined by a combination of: -//! - high-bandwidth operating rate: -//! Less filtering is done on the bma255, which has a direct impact on power consumption. -//! This gives a lower "update time", which in turn means less "active time" of the device. -//! The trade-off here is that accelerometer data is a bit more susceptible to noise. -//! - sleep time: -//! The longer the sleep duration, the less power the device consums. -//! After tsleep ms, a sample is taken, and then the device goes back to sleep. -//! -//! Power measurements on the board have shown we ideally want to run at a BW of 500Hz or 1000Hz. -//! Unfortunately, there is an issue with data jumps when running in low power modes. -//! At 4G sensitivity, we need to run at a bandwidth lower than 500Hz in order to minimize -//! jitter in readings. This means we probably want to stay at 250Hz. -//! -//! We are using Equidistant Sampling Mode (EST) to ensure that samples are taken -//! at equal time distances. See Figure 4 in the datasheet for an explanation of this. -//! In EST, a sample is taken every tsample ms, where tsample = (tsleep + wkup_time) [1] -//! -//! We can _approximate_ actual ODR as the following: [2] -//! ODR = (1000 / (tsleep + wkup_time)) -//! where tsleep holds the property that: -//! N = (2 * bw) * (tsleep / 1000) such that N is an Integer. [3][4] -//! and wkup_time is taken for the corresponding bandwidth from Table 4 of the datasheet. -//! -//! [1] This is the best we can gather as a good approximation after a meeting with Bosch. -//! [2] This is only an approximation as the BMA255 part is only guaranteed to have -//! Bandwidth accuracy within +/- 10% -//! [3] See p.16 of datasheet. Note that the formula in the datasheet is confirmed wrong by Bosch. -//! [4] Take note that all tsleep values are supported when running at 500Hz -//! -typedef enum { - BMA255ODR_1HZ = 0, - BMA255ODR_10HZ, - BMA255ODR_19HZ, - BMA255ODR_83HZ, - BMA255ODR_125HZ, - BMA255ODR_166HZ, - BMA255ODR_250HZ, - - BMA255ODRCount -} BMA255ODR; - -//! Note that these sample intervals are approximations. -typedef enum { - BMA255SampleInterval_1HZ = (1000000 / 1), - BMA255SampleInterval_10HZ = (1000000 / 10), - BMA255SampleInterval_19HZ = (1000000 / 19), - BMA255SampleInterval_83HZ = (1000000 / 83), - BMA255SampleInterval_125HZ = (1000000 / 125), - BMA255SampleInterval_166HZ = (1000000 / 166), - BMA255SampleInterval_250HZ = (1000000 / 250), -} BMA255SampleInterval; diff --git a/src/fw/drivers/imu/bma255/bma255_private.h b/src/fw/drivers/imu/bma255/bma255_private.h deleted file mode 100644 index c629cea87f..0000000000 --- a/src/fw/drivers/imu/bma255/bma255_private.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/spi.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -bool bma255_selftest(void); - -void bma255_gpio_init(void); - -void bma255_enable_spi_clock(void); - -void bma255_disable_spi_clock(void); - -uint8_t bma255_send_and_receive_byte(uint8_t byte); - -void bma255_send_byte(uint8_t byte); - -void bma255_prepare_txn(uint8_t address); - -void bma255_end_txn(void); - -void bma255_burst_read(uint8_t address, uint8_t *data, size_t len); - -uint8_t bma255_read_register(uint8_t address); - -void bma255_write_register(uint8_t address, uint8_t data); - -void bma255_read_modify_write(uint8_t reg, uint8_t value, uint8_t mask); diff --git a/src/fw/drivers/imu/bma255/bma255_regs.h b/src/fw/drivers/imu/bma255/bma255_regs.h deleted file mode 100644 index 9ab4afbeb7..0000000000 --- a/src/fw/drivers/imu/bma255/bma255_regs.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "bma255.h" - -#include "util/attributes.h" - -#include - -// Read & Write flags to be masked onto register addresses for raw spi transactions -static const uint8_t BMA255_READ_FLAG = 0x80; -static const uint8_t BMA255_WRITE_FLAG = 0x00; - -// BMI255 Register Map -typedef enum { - BMA255Register_BGW_CHIP_ID = 0x00, - BMA255Register_ACCD_X_LSB = 0x02, - BMA255Register_ACCD_X_MSB = 0x03, - BMA255Register_ACCD_Y_LSB = 0x04, - BMA255Register_ACCD_Y_MSB = 0x05, - BMA255Register_ACCD_Z_LSB = 0x06, - BMA255Register_ACCD_Z_MSB = 0x07, - BMA255Register_ACCD_TEMP = 0x08, - BMA255Register_INT_STATUS_0 = 0x09, - BMA255Register_INT_STATUS_1 = 0x0a, - BMA255Register_INT_STATUS_2 = 0x0b, - BMA255Register_INT_STATUS_3 = 0x0c, - BMA255Register_FIFO_STATUS = 0x0e, - BMA255Register_PMU_RANGE = 0x0f, - BMA255Register_PMU_BW = 0x10, - BMA255Register_PMU_LPW = 0x11, - BMA255Register_PMU_LOW_POWER = 0x12, - BMA255Register_ACCD_HBW = 0x13, - BMA255Register_BGW_SOFTRESET = 0x14, - BMA255Register_INT_EN_0 = 0x16, - BMA255Register_INT_EN_1 = 0x17, - BMA255Register_INT_EN_2 = 0x18, - BMA255Register_INT_MAP_0 = 0x19, - BMA255Register_INT_MAP_1 = 0x1a, - BMA255Register_INT_MAP_2 = 0x1b, - BMA255Register_INT_SRC = 0x1e, - BMA255Register_INT_OUT_CTRL = 0x20, - BMA255Register_INT_RST_LATCH = 0x21, - BMA255Register_INT_0 = 0x22, - BMA255Register_INT_1 = 0x23, - BMA255Register_INT_2 = 0x24, - BMA255Register_INT_3 = 0x25, - BMA255Register_INT_4 = 0x26, - BMA255Register_INT_5 = 0x27, - BMA255Register_INT_6 = 0x28, - BMA255Register_INT_7 = 0x29, - BMA255Register_INT_8 = 0x2a, - BMA255Register_INT_9 = 0x2b, - BMA255Register_INT_a = 0x2c, - BMA255Register_INT_b = 0x2d, - BMA255Register_INT_c = 0x2e, - BMA255Register_INT_d = 0x2f, - BMA255Register_FIFO_CONFIG_0 = 0x30, - BMA255Register_PMU_SELFTEST = 0x32, - BMA255Register_TRIM_NVM_CTRL = 0x33, - BMA255Register_BGW_SPI3_WDT = 0x34, - BMA255Register_OFC_CTRL = 0x36, - BMA255Register_OFC_SETTINGS = 0x37, - BMA255Register_OFC_OFFSET_X = 0x38, - BMA255Register_OFC_OFFSET_Y = 0x39, - BMA255Register_OFC_OFFSET_Z = 0x3a, - BMA255Register_TRIM_GPO0 = 0x3b, - BMA255Register_TRIM_GP1 = 0x3c, - BMA255Register_FIFO_CONFIG_1 = 0x3e, - BMA255Register_FIFO_DATA = 0x3f, - - BMA255Register_EXTENDED_MEMORY_MAP = 0x35, - BMA255Register_TEMPERATURE_SENSOR_CTRL = 0x4f, -} BMA255Register; - -static const uint8_t BMA255_EXTENDED_MEMORY_MAP_OPEN = 0xca; -static const uint8_t BMA255_EXTENDED_MEMORY_MAP_CLOSE = 0x0a; - -static const uint8_t BMA255_TEMPERATURE_SENSOR_DISABLE = 0x0; - -static const uint8_t BMA255_CHIP_ID = 0xfa; -static const uint8_t BMA255_ACC_CONF_PMU_BW_MASK = 0x1f; -static const uint8_t BMA255_SOFT_RESET_VALUE = 0xb6; - -static const uint8_t BMA255_FIFO_MODE_SHIFT = 6; -static const uint8_t BMA255_FIFO_MODE_MASK = 0xc0; - -static const uint8_t BMA255_FIFO_DATA_SEL_SHIFT = 0; -static const uint8_t BMA255_FIFO_DATA_SEL_MASK = 0x03; - -static const uint8_t BMA255_INT_STATUS_0_SLOPE_MASK = (1 << 2); - -static const uint8_t BMA255_INT_STATUS_2_SLOPE_SIGN = (1 << 3); -static const uint8_t BMA255_INT_STATUS_2_SLOPE_FIRST_X = (1 << 0); -static const uint8_t BMA255_INT_STATUS_2_SLOPE_FIRST_Y = (1 << 1); -static const uint8_t BMA255_INT_STATUS_2_SLOPE_FIRST_Z = (1 << 2); - -static const uint8_t BMA255_INT_MAP_1_INT2_FIFO_FULL = (0x1 << 5); -static const uint8_t BMA255_INT_MAP_1_INT2_FIFO_WATERMARK = (0x1 << 6); -static const uint8_t BMA255_INT_MAP_1_INT2_DATA = (0x1 << 7); - -static const uint8_t BMA255_INT_MAP_0_INT1_SLOPE = (0x1 << 2); - -static const uint8_t BMA255_INT_EN_0_SLOPE_X_EN = (1 << 0); -static const uint8_t BMA255_INT_EN_0_SLOPE_Y_EN = (1 << 1); -static const uint8_t BMA255_INT_EN_0_SLOPE_Z_EN = (1 << 2); - -static const uint8_t BMA255_INT_EN_1_DATA = (0x1 << 4); -static const uint8_t BMA255_INT_EN_1_FIFO_FULL = (0x1 << 5); -static const uint8_t BMA255_INT_EN_1_FIFO_WATERMARK = (0x1 << 6); - -static const uint8_t BMA255_INT_5_SLOPE_DUR_SHIFT = 0; -static const uint8_t BMA255_INT_5_SLOPE_DUR_MASK = 0x3; - -static const uint8_t BMA255_LPW_SLEEP_DUR_SHIFT = 1; -static const uint8_t BMA255_LPW_SLEEP_DUR_MASK = (0xf << 1); - -static const uint8_t BMA255_LPW_POWER_SHIFT = 5; -static const uint8_t BMA255_LPW_POWER_MASK = (0x7 << 5); - -static const uint8_t BMA255_LOW_POWER_SHIFT = 5; -static const uint8_t BMA255_LOW_POWER_MASK = (0x3 << 5); - -typedef struct PACKED { - uint16_t x; - uint16_t y; - uint16_t z; -} BMA255AccelData; - -typedef enum { - BMA255FifoMode_Bypass = 0x00, - BMA255FifoMode_Fifo = 0x01, - BMA255FifoMode_Stream = 0x02, -} BMA255FifoMode; - -typedef enum { - BMA255FifoDataSel_XYZ = 0x00, - BMA255FifoDataSel_X = 0x01, - BMA255FifoDataSel_Y = 0x02, - BMA255FifoDataSel_Z = 0x03, -} BMA255FifoDataSel; - -//! Configuration to be used to enter each of the supported power modes. -//! Make sure that the PMU_LOW_POWER register is always set prior to the PMU_LPW -//! register, as the bma255 uses this restriction. -static const struct { - uint8_t low_power; //!< PMU_LOW_POWER register - uint8_t lpw; //!< PMU_LPW register -} s_power_mode[BMA255PowerModeCount] = { - [BMA255PowerMode_Normal] = { .low_power = 0x0, .lpw = 0x0 }, - [BMA255PowerMode_Suspend] = { .low_power = 0x0, .lpw = 0x4 }, - [BMA255PowerMode_Standby] = { .low_power = 0x2, .lpw = 0x4 }, - [BMA255PowerMode_DeepSuspend] = { .low_power = 0x0, .lpw = 0x1 }, - [BMA255PowerMode_LowPower1] = { .low_power = 0x1, .lpw = 0x2 }, - [BMA255PowerMode_LowPower2] = { .low_power = 0x3, .lpw = 0x2 }, -}; - -//! Configuration to be used for each ODR we will be using. -//! This involves some native bma255 bandwidth and a tsleep value. -//! Errata states that at 4G sensitivity, we need to run at a bandwidth of 250HZ or lower. -//! See the discussion around \ref BMA255ODR for more information. -static const struct { - BMA255Bandwidth bw; - BMA255SleepDuration tsleep; -} s_odr_settings[BMA255ODRCount] = { - [BMA255ODR_1HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_1000ms }, - [BMA255ODR_10HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_100ms }, - [BMA255ODR_19HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_50ms }, - [BMA255ODR_83HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_10ms }, - [BMA255ODR_125HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_6ms }, - [BMA255ODR_166HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_4ms }, - [BMA255ODR_250HZ] = { BMA255Bandwidth_250HZ, BMA255SleepDuration_2ms }, -}; diff --git a/src/fw/drivers/imu/bma255/bma255_spi.c b/src/fw/drivers/imu/bma255/bma255_spi.c deleted file mode 100644 index 464b89605b..0000000000 --- a/src/fw/drivers/imu/bma255/bma255_spi.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/rtc.h" -#include "drivers/spi.h" -#include "kernel/util/sleep.h" -#include "util/units.h" - - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -#include "bma255_private.h" -#include "bma255_regs.h" - -#define BMA255_SPI SPI3 -static const uint32_t BMA255_PERIPH_CLOCK = RCC_APB1Periph_SPI3; -static const SpiPeriphClock BMA255_SPI_CLOCK = SpiPeriphClockAPB1; - -static const AfConfig BMA255_SCLK_CONFIG = { GPIOB, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF7_SPI3 }; -static const AfConfig BMA255_MISO_CONFIG = { GPIOC, GPIO_Pin_11, GPIO_PinSource11, GPIO_AF_SPI3 }; -static const AfConfig BMA255_MOSI_CONFIG = { GPIOC, GPIO_Pin_12, GPIO_PinSource12, GPIO_AF_SPI3 }; -static const OutputConfig BMA255_SCS_CONFIG = { GPIOA, GPIO_Pin_4, false }; - -// Need to wait a minimum of 450µs after a write. -// Due to RTC resolution, we need to make sure that the tick counter has -// incremented twice so we can be certain at least one full tick-period has elapsed. -#define MIN_TICKS_AFTER_WRITE 2 -static RtcTicks s_last_write_ticks = 0; -_Static_assert(RTC_TICKS_HZ < (1000000 / 450), "Tick period < 450µs"); - -void bma255_gpio_init(void) { - periph_config_acquire_lock(); - - gpio_af_init(&BMA255_SCLK_CONFIG, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&BMA255_MISO_CONFIG, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&BMA255_MOSI_CONFIG, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_output_init(&BMA255_SCS_CONFIG, GPIO_OType_PP, GPIO_Speed_50MHz); - - SPI_InitTypeDef spi_cfg; - SPI_I2S_DeInit(BMA255_SPI); - spi_cfg.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - spi_cfg.SPI_Mode = SPI_Mode_Master; - spi_cfg.SPI_DataSize = SPI_DataSize_8b; - spi_cfg.SPI_CPOL = SPI_CPOL_Low; - spi_cfg.SPI_CPHA = SPI_CPHA_1Edge; - spi_cfg.SPI_NSS = SPI_NSS_Soft; - // Max SCLK frequency for the BMA255 is 10 MHz - spi_cfg.SPI_BaudRatePrescaler = spi_find_prescaler(MHZ_TO_HZ(5), BMA255_SPI_CLOCK); - spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB; - spi_cfg.SPI_CRCPolynomial = 7; - - bma255_enable_spi_clock(); - SPI_Init(BMA255_SPI, &spi_cfg); - SPI_Cmd(BMA255_SPI, ENABLE); - bma255_disable_spi_clock(); - - periph_config_release_lock(); -} - -void bma255_enable_spi_clock(void) { - periph_config_enable(BMA255_SPI, BMA255_PERIPH_CLOCK); -} - -void bma255_disable_spi_clock(void) { - periph_config_disable(BMA255_SPI, BMA255_PERIPH_CLOCK); -} - -uint8_t bma255_send_and_receive_byte(uint8_t byte) { - // Ensure that there are no other write operations in progress - while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET) {} - // Send the byte on the SPI bus - SPI_I2S_SendData(BMA255_SPI, byte); - - // Wait for the response byte to be received - while (SPI_I2S_GetFlagStatus(BMA255_SPI, SPI_I2S_FLAG_RXNE) == RESET) {} - // Return the byte - return SPI_I2S_ReceiveData(BMA255_SPI); -} - -void bma255_send_byte(uint8_t byte) { - // Ensure that there are no other write operations in progress - while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET) {} - // Send the byte on the SPI bus - SPI_I2S_SendData(BMA255_SPI, byte); -} - -//! Set SCS for transaction, start spi clock, and send address -void bma255_prepare_txn(uint8_t address) { - while (rtc_get_ticks() < (s_last_write_ticks + MIN_TICKS_AFTER_WRITE)) { - psleep(1); - } - - gpio_output_set(&BMA255_SCS_CONFIG, true); - bma255_enable_spi_clock(); - bma255_send_and_receive_byte(address); -} - -//! Disables spi clock and sets SCS to end txn -void bma255_end_txn(void) { - bma255_disable_spi_clock(); - gpio_output_set(&BMA255_SCS_CONFIG, false); -} - -void bma255_burst_read(uint8_t address, uint8_t *data, size_t len) { - const uint8_t reg = address | BMA255_READ_FLAG; - - bma255_prepare_txn(reg); - for (size_t i = 0; i < len; ++i) { - data[i] = bma255_send_and_receive_byte(0); - } - bma255_end_txn(); -} - -uint8_t bma255_read_register(uint8_t address) { - const uint8_t reg = address | BMA255_READ_FLAG; - - bma255_prepare_txn(reg); - // Read data - uint8_t data = bma255_send_and_receive_byte(0); - bma255_end_txn(); - - return data; -} - -void bma255_write_register(uint8_t address, uint8_t data) { - const uint8_t reg = address | BMA255_WRITE_FLAG; - - bma255_prepare_txn(reg); - bma255_send_and_receive_byte(data); - bma255_end_txn(); - - s_last_write_ticks = rtc_get_ticks(); -} - -void bma255_read_modify_write(uint8_t reg, uint8_t value, uint8_t mask) { - uint8_t new_val = bma255_read_register(reg); - new_val &= ~mask; - new_val |= value; - bma255_write_register(reg, new_val); -} diff --git a/src/fw/drivers/imu/bmi160/bmi160.c b/src/fw/drivers/imu/bmi160/bmi160.c deleted file mode 100644 index 8d9c1fef18..0000000000 --- a/src/fw/drivers/imu/bmi160/bmi160.c +++ /dev/null @@ -1,1078 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bmi160.h" -#include "bmi160_private.h" -#include "drivers/accel.h" - -#include "bmi160_regs.h" -#include "drivers/exti.h" -#include "drivers/rtc.h" -#include "system/passert.h" -#include "system/logging.h" -#include "kernel/util/delay.h" -#include "util/math.h" -#include "util/size.h" -#include "kernel/util/sleep.h" - -#include -#include -#include - -// Note: Before adding a new header, be sure you actually need it! The goal -// is to keep the driver as unreliant on higher level constructs as possible -#ifdef BMI160_DEBUG -#include "console/dbgserial.h" -#define BMI160_DBG(msg, ...) \ - do { \ - char _buf[80]; \ - dbgserial_putstr_fmt(_buf, sizeof(_buf), msg, __VA_ARGS__); \ - } while (0) -#else -#define BMI160_DBG(msg, ...) -#endif - -#define NUM_AVERAGED_SAMPLES (4) - -typedef enum { - BMI160_SCALE_2G = 2, - BMI160_SCALE_4G = 4, - BMI160_SCALE_8G = 8, - BMI160_SCALE_16G = 16, -} Bmi160Scale; - -static int16_t s_raw_unit_to_mgs = 8192; - -static BMI160AccelPowerMode s_accel_power_mode = BMI160_Accel_Mode_Suspend; -static BMI160GyroPowerMode s_gyro_power_mode = BMI160_Gyro_Mode_Suspend; - -static bool s_accel_outstanding_motion_work = false; -static bool s_accel_outstanding_data_work = false; -static bool s_fifo_in_use = false; -static uint8_t curr_fifo_num_samples_wm = 0; - -static bool s_double_tap_detection_enabled = false; -static bool s_shake_detection_enabled = false; - -// Accelerometer configuration criteria -// Each operating mode can be enabled and disabled independently from each other and the driver -// will configure the accelerometer in the highest power mode and with the highest sampling rate -// required according which operating modes are enabled and what the requirements are thereof. - -typedef enum { - AccelOperatingModeData, - AccelOperatingModeShakeDetection, - AccelOperatingModeDoubleTapDetection, - - AccelOperatingMode_Num, -} AccelOperatingMode; - -typedef enum { - AccelPowerModeLowPower, - AccelPowerModeNormal, - - AccelPowerMode_Num, -} AccelPowerMode; - -static struct { - bool enabled; - BMI160SampleRate sample_interval; -} s_operating_states[] = { - [AccelOperatingModeData] = { - .enabled = false, - .sample_interval = BMI160SampleRate_25_HZ, - }, - [AccelOperatingModeShakeDetection] = { - .enabled = false, - .sample_interval = BMI160SampleRate_25_HZ, - }, - [AccelOperatingModeDoubleTapDetection] = { - .enabled = false, - .sample_interval = BMI160SampleRate_200_HZ, - }, -}; - -#define HZ_TO_US(hz) 1000000 / (hz) - -static void prv_write_reg(uint8_t reg, uint8_t value) { - bmi160_write_reg(reg, value); - // Wait 2 us (active mode) or 450 us (suspend mode) - // before issuing the next read or write command. - // - // TODO: I'm pretty sure if commands are specifically targetting - // a unit in suspend mode, we will need to delay for 450us even if - // the other unit is powered up in Normal mode - if (s_accel_power_mode == BMI160_Accel_Mode_Normal - || s_gyro_power_mode == BMI160_Gyro_Mode_Normal) { - // Apparently this delays for ~3.5 us. Unconfirmed. - delay_us(5); - } else { - psleep(2); // must sleep >= 450us - } -} - - -static void prv_burst_read(uint8_t reg, void *buf, size_t len) { - reg |= BMI160_READ_FLAG; - SPIScatterGather sg_info[2] = { - {.sg_len = 1, .sg_out = ®, .sg_in = NULL}, // address - {.sg_len = len, .sg_out = NULL, .sg_in = buf} // read data - }; - spi_slave_burst_read_write_scatter(BMI160_SPI, sg_info, ARRAY_LENGTH(sg_info)); -} - -static void prv_read_modify_write(uint8_t reg, uint8_t value, uint8_t mask) { - uint8_t new_val = bmi160_read_reg(reg); - new_val &= ~mask; - new_val |= value; - prv_write_reg(reg, new_val); -} - -static void prv_run_command(uint8_t command) { - prv_write_reg(BMI160_REG_CMD, command); - if (command == BMI160_CMD_SOFTRESET) { - s_accel_power_mode = BMI160_Accel_Mode_Suspend; - s_gyro_power_mode = BMI160_Gyro_Mode_Suspend; - } -} - -static Bmi160Scale prv_get_accel_scale(void) { - uint8_t scale_reg_val = bmi160_read_reg(BMI160_REG_ACC_RANGE) & 0xf; - - if (scale_reg_val == BMI160_ACC_RANGE_2G) { - return BMI160_SCALE_2G; - } else if (scale_reg_val == BMI160_ACC_RANGE_4G) { - return BMI160_SCALE_4G; - } else if (scale_reg_val == BMI160_ACC_RANGE_8G) { - return BMI160_SCALE_8G; - } else if (scale_reg_val == BMI160_ACC_RANGE_16G) { - return BMI160_SCALE_16G; - } - - WTF; - return 0; -} - -static void prv_set_accel_scale(Bmi160Scale scale) { - uint8_t cfg_val; - switch (scale) { - case BMI160_SCALE_2G: - cfg_val = BMI160_ACC_RANGE_2G; - break; - case BMI160_SCALE_4G: - cfg_val = BMI160_ACC_RANGE_4G; - break; - case BMI160_SCALE_8G: - cfg_val = BMI160_ACC_RANGE_8G; - break; - case BMI160_SCALE_16G: - cfg_val = BMI160_ACC_RANGE_16G; - break; - default: - WTF; - } - prv_write_reg(BMI160_REG_ACC_RANGE, cfg_val); - s_raw_unit_to_mgs = 32768 / scale; -} - -static int16_t prv_raw_to_mgs(int16_t raw_val) { - int16_t mgs = (raw_val * 1000) / s_raw_unit_to_mgs; - return mgs; -} - -static void prv_convert_accel_raw_data_to_mgs(const uint8_t *raw_buf, - AccelDriverSample *data) { - int16_t readings[3]; - for (unsigned int i = 0; i < ARRAY_LENGTH(readings); i++) { - int base = i * 2; - readings[i] = raw_buf[base] | (raw_buf[base + 1] << 8); - } - - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - - data->x = (cfg->axes_inverts[AXIS_X] ? -1 : 1) * - prv_raw_to_mgs(readings[cfg->axes_offsets[AXIS_X]]); - data->y = (cfg->axes_inverts[AXIS_Y] ? -1 : 1) * - prv_raw_to_mgs(readings[cfg->axes_offsets[AXIS_Y]]); - data->z = (cfg->axes_inverts[AXIS_Z] ? -1 : 1) * - prv_raw_to_mgs(readings[cfg->axes_offsets[AXIS_Z]]); -} - -static uint64_t prv_get_curr_system_time_ms(void) { - time_t time_s; - uint16_t time_ms; - rtc_get_time_ms(&time_s, &time_ms); - return (((uint64_t)time_s) * 1000 + time_ms); -} - -static uint32_t prv_sensortime_to_timestamp(uint8_t sensor_time[3]) { - return (sensor_time[0] | (sensor_time[1] << 8) | (sensor_time[2] << 16)); -} - -static uint32_t prv_get_min_sampling_interval_us(void) { - BMI160SampleRate sample_rate_max = BMI160SampleRate_12p5_HZ; - for (uint32_t i = 0; i < ARRAY_LENGTH(s_operating_states); i++) { - if (s_operating_states[i].enabled) { - // use MIN because sample rate enum value is actually the sampling interval in us - sample_rate_max = MIN(sample_rate_max, s_operating_states[i].sample_interval); - } - } - return (uint32_t)sample_rate_max; -} - -// Determine the bit that flips when the sample is collected (as sensor events -// are synchronous to this register) -static uint8_t prv_get_sample_collection_bit(void) { - return (31 - (__builtin_clz(prv_get_min_sampling_interval_us() / - BMI160_SENSORTIME_RESOLUTION_US))); -} - -// Converts BMI160 sensortime data to a uint32_t (units incremented every 39us) -static uint32_t prv_get_time_since_sample(uint8_t sensor_time[3]) { - uint32_t sensor_timestamp = prv_sensortime_to_timestamp(sensor_time); - - uint8_t sample_time_bit = prv_get_sample_collection_bit(); - - uint64_t time_since_data_collection_us = (sensor_timestamp & - ((0x1 << sample_time_bit) - 1)) * BMI160_SENSORTIME_RESOLUTION_US; - - return time_since_data_collection_us; -} - -// determines if a new sample was collected between the two sensor timestamps -// provided -static bool prv_new_sample_collected(uint8_t sensor_timestamp_before[3], - uint8_t sensor_timestamp_after[3]) { - - uint32_t start_time = prv_sensortime_to_timestamp(sensor_timestamp_before); - uint32_t end_time = prv_sensortime_to_timestamp(sensor_timestamp_after); - - uint32_t sample_time_bit = prv_get_sample_collection_bit(); - - // see if the upper bits oveflowed - uint32_t upper_bits_mask = ~((0x1 << sample_time_bit) - 1); - start_time &= upper_bits_mask; - end_time &= upper_bits_mask; - - return (start_time != end_time); -} - -// Converts the sensor time from the BMI160 captured at the time the sample -// was collected to the actual system time -static uint64_t prv_get_sample_time_us(uint8_t sensor_time[3]) { - uint64_t curr_time_us = prv_get_curr_system_time_ms() * 1000; - - uint32_t time_since_data_collection_us = prv_get_time_since_sample(sensor_time); - BMI160_DBG("%"PRIu32" us delay since sample was collected", - time_since_data_collection_us); - - return (curr_time_us - time_since_data_collection_us); -} - -static void prv_read_curr_accel_data(AccelDriverSample *data) { - uint8_t res[9]; /* x, y, z & timestamp */ - prv_burst_read(BMI160_REG_ACC_X_LSB, res, sizeof(res)); - - *data = (AccelDriverSample){}; - prv_convert_accel_raw_data_to_mgs(res, data); - - data->timestamp_us = prv_get_sample_time_us(&res[6]); - - BMI160_DBG("%"PRId16" %"PRId16" %"PRId16, data->x, data->y, data->z); -} - -static IMUCoordinateAxis prv_get_axis_direction(uint8_t int0_status, uint8_t int2_status, - uint8_t mask, uint8_t shift, int32_t *direction) { - *direction = ((int2_status & BMI160_INT_STATUS_2_ANYM_SIGN) == 0) ? 1 : -1; - IMUCoordinateAxis axis = AXIS_X; - const AccelConfig *cfg = &BOARD_CONFIG_ACCEL.accel_config; - - bool invert = false; - - if ((int2_status & (shift << cfg->axes_offsets[AXIS_X])) != 0) { - axis = AXIS_X; - invert = cfg->axes_inverts[AXIS_X]; - } else if ((int2_status & (shift << cfg->axes_offsets[AXIS_Y])) != 0) { - axis = AXIS_Y; - invert = cfg->axes_inverts[AXIS_Y]; - } else if ((int2_status & (shift << cfg->axes_offsets[AXIS_Z])) != 0) { - axis = AXIS_Z; - invert = cfg->axes_inverts[AXIS_Z]; - } else { - BMI160_DBG("No Axis?: 0x%"PRIx8" 0x%"PRIx8, int0_status, int2_status); - } - *direction *= (invert ? -1 : 1); - return axis; -} - -static void prv_dump_int_stats(void); - -static void prv_handle_motion_interrupts(void) { - s_accel_outstanding_motion_work = false; - // Interestingly, the status registers for tap interrupts are updated _after_ - // the EXTI fires. Low power mode toggles between suspend and normal mode. I - // believe the updates to the registers only occur during the run cycles - // which occur at an interval dependent on the sampling frequency - bool toggled_power_mode = (s_accel_power_mode == BMI160_Accel_Mode_Low); - if (toggled_power_mode) { - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Normal); - } - - uint8_t int0_status = bmi160_read_reg(BMI160_REG_INT_STATUS_0); - uint8_t int2_status = bmi160_read_reg(BMI160_REG_INT_STATUS_2); - prv_run_command(BMI160_CMD_INT_RESET); - - // debug - bool anymotion = ((int0_status & BMI160_INT_STATUS_0_ANYM_MASK) != 0); - if (anymotion) { - int32_t direction; - IMUCoordinateAxis axis = prv_get_axis_direction(int0_status, int2_status, - BMI160_INT_STATUS_2_ANYM_SIGN, - BMI160_INT_STATUS_2_ANYM_FIRST_X, &direction); - BMI160_DBG("Anymotion on axis %"PRIu8" in direction %"PRId32, axis, direction); - accel_cb_shake_detected(axis, direction); - } - bool double_tap = ((int0_status & BMI160_INT_STATUS_0_D_TAP_MASK) != 0); - if (double_tap) { - int32_t direction; - IMUCoordinateAxis axis = prv_get_axis_direction(int0_status, int2_status, - BMI160_INT_STATUS_2_TAP_SIGN, - BMI160_INT_STATUS_2_TAP_FIRST_X, &direction); - if (double_tap) { - BMI160_DBG("Double tap on axis %"PRIu8" in direction %"PRId32, axis, direction); - accel_cb_double_tap_detected(axis, direction); - } - } else if (!anymotion) { - BMI160_DBG("Wahh, no motion/tap?: INT0: 0x%"PRIx8", INT2: 0x%"PRIx8, int0_status, int2_status); - prv_dump_int_stats(); - } - - if (toggled_power_mode) { - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Low); - } -} - -// TODO: strictly for debug, remove when done -static void prv_dump_int_stats(void) { - for (uint8_t addr = 0x1b; addr <= 0x1f; addr++) { - BMI160_DBG("0x%"PRIx8" = 0x%"PRIx8, addr, bmi160_read_reg(addr)); - } - BMI160_DBG("Latched = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_LATCH)); - BMI160_DBG("Err reg = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_ERR)); - BMI160_DBG("INT_MAP[0] = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_MAP_0)); - BMI160_DBG("INT_MAP[1] = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_MAP_1)); - BMI160_DBG("INT_EN[0] = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_EN_0)); - BMI160_DBG("INT_EN[1] = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_EN_1)); -} - -// should be servicing tap/motion interrupts -static void bmi160_IRQ1_handler(bool *should_context_switch) { - if (!s_accel_outstanding_motion_work) { - s_accel_outstanding_motion_work = true; - accel_offload_work_from_isr(prv_handle_motion_interrupts, should_context_switch); - } else { - BMI160_DBG("%s", "We fell behind on motion interrupt handling!"); - } -} - -static uint8_t prv_get_fifo_frame_size(void) { - return 6; // we are just storing {x, y, z} accel data in the fifo today -} - -static uint16_t prv_get_current_fifo_length_and_timestamp(uint64_t *timestamp_us) { - int retries = 0; - while (retries < 10) { - uint8_t ts_before[3], ts_after[3]; - - // We want to find the timestamp of the latest sample in the fifo so: - // 1. read the current sensor timestamp - // 2. read the current length of the fifo - // 3. read the sensor timestamp again - // Since new sample collection is synchronous with a particular bit of the - // sensor timestamp, we can see if that bit overflowed between 1 & 3 to see - // if a new sample was appended. Continue this process until there is no - // overflow between the readings in 1 & 3. - - prv_burst_read(BMI160_REG_SENSORTIME_0, &ts_before[0], sizeof(ts_before)); - uint64_t sample_time_before = prv_get_sample_time_us(ts_before); - uint16_t num_samples = bmi160_read_16bit_reg(BMI160_REG_FIFO_LENGTH_LSB); - prv_burst_read(BMI160_REG_SENSORTIME_0, &ts_after[0], sizeof(ts_after)); - - // check to see if we rolled - if (!prv_new_sample_collected(ts_before, ts_after)) { - *timestamp_us = sample_time_before; - return (num_samples); - } - - retries++; - }; - - // something has gone wrong if we fail to recover the right length & timestamp - PBL_ASSERTN(retries < 10); - return 0; -} - -static void prv_process_fifo_frame(const uint8_t *frame_buf, AccelDriverSample *data) { - const int a_begin = 0; // index within the frame where accel data starts - prv_convert_accel_raw_data_to_mgs(&frame_buf[a_begin], data); -} - -static void prv_drain_fifo(void) { - // we can't drain the fifo if we are in low power mode so we have - // to temporarily enter normal mode - bool was_low_power = (s_accel_power_mode == BMI160_Accel_Mode_Low); - if (was_low_power) { - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Normal); - } - - // get the FIFO length - uint64_t last_frame_time = 0; - uint16_t len = prv_get_current_fifo_length_and_timestamp(&last_frame_time); - BMI160_DBG("Reading %d bytes", len); - - uint8_t fifo_frame_len = prv_get_fifo_frame_size(); - - bmi160_begin_burst(BMI160_REG_FIFO_DATA | BMI160_READ_FLAG); - - uint8_t curr_num_samples = (len / fifo_frame_len); - uint32_t curr_sampling_interval_us = prv_get_min_sampling_interval_us(); - uint64_t start_time = last_frame_time - curr_num_samples * curr_sampling_interval_us; - - for (int i = 0; i < len; i += fifo_frame_len) { - uint8_t burst_buf[fifo_frame_len]; - spi_ll_slave_burst_read(BMI160_SPI, &burst_buf[0], fifo_frame_len); - - AccelDriverSample data; - prv_process_fifo_frame(burst_buf, &data); - data.timestamp_us = start_time; - start_time += curr_sampling_interval_us; - - BMI160_DBG("%2d: %"PRId16" %"PRId16" %"PRId16, i, data.x, data.y, data.z); - accel_cb_new_sample(&data); - } - bmi160_end_burst(); - - BMI160_DBG("%d bytes remain", prv_get_current_fifo_length_and_timestamp(&last_frame_time)); - - if (was_low_power) { - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Low); - } -} - -static void prv_handle_data(void) { - s_accel_outstanding_data_work = false; - - // if the task draining the fifo gets swapped out for a long enough duration, - // its possible the fifo watermark interrupt may fire multiple times. Once - // the task finishes draining the fifo, the interrupt will be cleared but a - // fifo drain callback could have already been scheduled so don't check the - // interrupt status - if (s_fifo_in_use) { - prv_drain_fifo(); - return; - } - - // the int_status for drdy is not latched so we check the status register - // instead to confirm new data accel data is available - - uint8_t status = bmi160_read_reg(BMI160_REG_STATUS); - if ((status & BMI160_STATUS_DRDY_ACC_MASK) != 0) { - AccelDriverSample data; - prv_read_curr_accel_data(&data); - accel_cb_new_sample(&data); - } else { - BMI160_DBG("Unexpected int status: 0x%"PRIx8" 0x%x", - bmi160_read_reg(BMI160_REG_INT_STATUS_1), status); - } -} - -static void bmi160_IRQ2_handler(bool *should_context_switch) { - if (!s_accel_outstanding_data_work) { - s_accel_outstanding_data_work = true; - accel_offload_work_from_isr(prv_handle_data, should_context_switch); - } else { - BMI160_DBG("%s", "We fell behind on data handling"); - } -} - -// in order to actually enter 'low power' mode, we have to set up accel to do -// undersampling. The more samples we use for one reading, the higher the power -// consumption but the lower the RMS noise -static void prv_accel_enable_undersampling(bool enable) { - const uint8_t acc_us_bwp_mask = - (BMI160_ACC_CONF_ACC_BWP_MASK << BMI160_ACC_CONF_ACC_BWP_SHIFT) | - (BMI160_ACC_CONF_ACC_US_MASK << BMI160_ACC_CONF_ACC_US_SHIFT); - - uint8_t acc_us_bwp; - if (!enable) { - acc_us_bwp = 0x2 << BMI160_ACC_CONF_ACC_BWP_SHIFT; - } else { - _Static_assert(NUM_AVERAGED_SAMPLES <= 128, "Number of averaged samples " - "must be <= 128"); - const uint8_t acc_bwp = 31 - __builtin_clz((uint32_t)NUM_AVERAGED_SAMPLES); - acc_us_bwp = (0x1 << BMI160_ACC_CONF_ACC_US_SHIFT) | - (acc_bwp << BMI160_ACC_CONF_ACC_BWP_SHIFT); - } - - prv_read_modify_write(BMI160_REG_ACC_CONF, acc_us_bwp, acc_us_bwp_mask); -} - -static void prv_update_accel_interrupts(bool enable) { - for (unsigned int i = 0; i < ARRAY_LENGTH(BOARD_CONFIG_ACCEL.accel_ints); i++) { - uint8_t int_cfg = 0; - uint8_t int_mask = 0xf << (i * 4); - if (enable) { - int_cfg = 0xA << (i * 4); // INT EN, Push-Pull, Active High, Level Triggered - exti_enable(BOARD_CONFIG_ACCEL.accel_ints[i]); - } else { // disable - exti_disable(BOARD_CONFIG_ACCEL.accel_ints[i]); - } - - prv_read_modify_write(BMI160_REG_INT_OUT_CTRL, int_cfg, int_mask); - BMI160_DBG("INT_OUT_CTRL = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_OUT_CTRL)); - } -} - -void bmi160_init(void) { - gpio_input_init(&BOARD_CONFIG_ACCEL.accel_int_gpios[0]); - gpio_input_init(&BOARD_CONFIG_ACCEL.accel_int_gpios[1]); - - exti_configure_pin(BOARD_CONFIG_ACCEL.accel_ints[0], ExtiTrigger_Rising, - bmi160_IRQ1_handler); - exti_configure_pin(BOARD_CONFIG_ACCEL.accel_ints[1], ExtiTrigger_Rising, - bmi160_IRQ2_handler); - - bmi160_enable_spi_mode(); - if (bmi160_query_whoami()) { - prv_run_command(BMI160_CMD_SOFTRESET); - bmi160_enable_spi_mode(); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to query BMI160"); - } - - prv_set_accel_scale(BMI160_SCALE_4G); -} - -bool bmi160_query_whoami(void) { - uint8_t whoami = bmi160_read_reg(BMI160_REG_CHIP_ID); - PBL_LOG(LOG_LEVEL_DEBUG, "Read BMI160 whoami byte 0x%"PRIx8", expecting 0x%"PRIx8, - whoami, BMI160_CHIP_ID); - return (whoami == BMI160_CHIP_ID); -} - -// TODO/NOTE: The accel & gyro self test routines changes some of the BMI160 -// configuration state. In the future we could update them so they do not -// destroy the state - -bool accel_run_selftest(void) { - prv_update_accel_interrupts(false); - - prv_run_command(BMI160_CMD_SOFTRESET); - psleep(50); - - bmi160_enable_spi_mode(); - - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Normal); - psleep(10); - - // Set to 8g range, as required for the self test mode - prv_set_accel_scale(BMI160_SCALE_8G); - - // Set ODR to 1600Hz - accel_set_sampling_interval(BMI160SampleRate_1600_HZ); - prv_accel_enable_undersampling(false); - - PBL_LOG(LOG_LEVEL_DEBUG, "Self Test: Negative offset"); - - // Enable self test with high amplitude in the negative direction - prv_write_reg(BMI160_REG_SELF_TEST, 0x8 | 0b01); - psleep(50); - - struct { - char axis_name; - - uint8_t register_address; - int pass_threshold; - - int16_t negative_value; - int16_t positive_value; - } accel_test_axis[] = { - { - .axis_name = 'X', - .register_address = BMI160_REG_ACC_X_LSB, - .pass_threshold = 3277 - }, { - .axis_name = 'Y', - .register_address = BMI160_REG_ACC_Y_LSB, - .pass_threshold = 3277 - }, { - .axis_name = 'Z', - .register_address = BMI160_REG_ACC_Z_LSB, - .pass_threshold = 1639 - } - }; - - // Collect data with the negative offset applied - for (unsigned int i = 0; i < ARRAY_LENGTH(accel_test_axis); ++i) { - accel_test_axis[i].negative_value = bmi160_read_16bit_reg(accel_test_axis[i].register_address); - PBL_LOG(LOG_LEVEL_DEBUG, "- %c: %"PRId16, - accel_test_axis[i].axis_name, accel_test_axis[i].negative_value); - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Self Test: Positive offset"); - - // Flip sign bit from negative to positive while leaving self test mode on at high amplitude - prv_write_reg(BMI160_REG_SELF_TEST, 0x8 | 0x4 | 0b01); - - psleep(50); - - // Collect data with the positive offset applied - for (unsigned int i = 0; i < ARRAY_LENGTH(accel_test_axis); ++i) { - accel_test_axis[i].positive_value = bmi160_read_16bit_reg(accel_test_axis[i].register_address); - PBL_LOG(LOG_LEVEL_DEBUG, "+ %c: %"PRId16, - accel_test_axis[i].axis_name, accel_test_axis[i].positive_value); - } - - // Verify each axis saw a big enough delta in response to the self test mode. - // NOTE! For some reason, applying a "positive" force makes the number go lower and applying - // a "negative" force makes the number go higher. And then, for some reason, we abs() it when - // calculating a delta to hide the fact that it's backwards. This is all documented in a - // document called "How to perform BMI160 accelerometer self-test" provided by Bosch, so I guess - // it's the right thing to do. This document is attached to PBL-10951. - bool pass = true; - for (unsigned int i = 0; i < ARRAY_LENGTH(accel_test_axis); ++i) { - int axis_delta = accel_test_axis[i].positive_value - accel_test_axis[i].negative_value; - axis_delta = abs(axis_delta); - - if (axis_delta < accel_test_axis[i].pass_threshold) { - PBL_LOG(LOG_LEVEL_WARNING, "Self test failed for axis %c: %d < %d", - accel_test_axis[i].axis_name, axis_delta, accel_test_axis[i].pass_threshold); - pass = false; - } - } - - prv_run_command(BMI160_CMD_SOFTRESET); - psleep(50); - - bmi160_enable_spi_mode(); - - return pass; -} - -bool gyro_run_selftest(void) { - prv_update_accel_interrupts(false); - - prv_run_command(BMI160_CMD_SOFTRESET); - psleep(50); - - bmi160_enable_spi_mode(); - - bmi160_set_gyro_power_mode(BMI160_Gyro_Mode_Normal); - - // Write the gyr_self_test_start bit - prv_write_reg(BMI160_REG_SELF_TEST, 0x10); - psleep(50); - - const uint8_t status = bmi160_read_reg(BMI160_REG_SELF_TEST); - - // power down the gyro - bmi160_set_gyro_power_mode(BMI160_Gyro_Mode_Suspend); - if (status | 0x2) { - return true; - } - return false; -} - -void bmi160_set_accel_power_mode(BMI160AccelPowerMode mode) { - uint8_t status = 0; - - int retries = 10; - prv_run_command(BMI160_CMD_ACC_SET_PMU_MODE | mode); - while (retries--) { - // Takes 3.2 to 3.8ms according to datasheet - status = bmi160_read_reg(BMI160_REG_PMU_STATUS) >> 4; - if (status == mode) { - break; - } - BMI160_DBG("ACCEL: want mode %d, actual %d", mode, status); - } -#ifdef BMI160_DEBUG - PBL_ASSERT(retries > 0, "Could not set power mode to %d", mode); -#endif - s_accel_power_mode = mode; - BMI160_DBG("PMU_STATUS: 0x%x ACC_CONF: 0x%x", - bmi160_read_reg(BMI160_REG_PMU_STATUS), bmi160_read_reg(BMI160_REG_ACC_CONF)); -} - -void bmi160_set_gyro_power_mode(BMI160GyroPowerMode mode) { - int retries = 20; - prv_run_command(BMI160_CMD_GYR_SET_PMU_MODE | mode); - while (retries--) { - uint8_t status = 0; - // can take up to 80ms to power up - status = bmi160_read_reg(BMI160_REG_PMU_STATUS) >> 2; - if (status == mode) { - break; - } - psleep(5); - BMI160_DBG("GYRO: want mode %d, actual %d", mode, status); - } - PBL_ASSERT(retries > 0, "Gyro: Could not set power mode to %d", mode); - - s_gyro_power_mode = mode; - BMI160_DBG("PMU_STATUS: 0x%x", bmi160_read_reg(BMI160_REG_PMU_STATUS)); -} - -/* - * accel.h driver interface exposed to higher level code - */ - -static uint32_t prv_get_sampling_interval_from_hw(void) { - uint8_t acc_cfg = bmi160_read_reg(BMI160_REG_ACC_CONF); - acc_cfg = (acc_cfg >> BMI160_ACC_CONF_ACC_ODR_SHIFT) & - BMI160_ACC_CONF_ACC_ODR_MASK; - - // sample interval (us) = 10000 * (2 ^ (8 - val(acc_odr))) - int shift_val = 8 - acc_cfg; - uint32_t interval = 10000; - if (shift_val >= 0) { - interval <<= shift_val; - } else { - interval >>= -shift_val; - } - - return interval; -} - -static BMI160AccODR prv_get_odr(BMI160SampleRate sample_rate) { - // sample rate = 100 / 2^(8- val(acc_odr)) - - BMI160AccODR acc_odr = 0; - if (BMI160SampleRate_12p5_HZ == sample_rate) { - acc_odr = BMI160AccODR_12p5_HZ; - } else if (BMI160SampleRate_25_HZ == sample_rate) { - acc_odr = BMI160AccODR_25_HZ; - } else if (BMI160SampleRate_50_HZ == sample_rate) { - acc_odr = BMI160AccODR_50_HZ; - } else if (BMI160SampleRate_100_HZ == sample_rate) { - acc_odr = BMI160AccODR_100_HZ; - } else if (BMI160SampleRate_200_HZ == sample_rate) { - acc_odr = BMI160AccODR_200_HZ; - } else if (BMI160SampleRate_400_HZ == sample_rate) { - acc_odr = BMI160AccODR_400_HZ; - } else if (BMI160SampleRate_800_HZ == sample_rate) { - acc_odr = BMI160AccODR_800_HZ; - } else { // any interval < min supported must saturate to 1600Hz - acc_odr = BMI160AccODR_1600_HZ; - } - - return acc_odr; -} - -static BMI160SampleRate prv_get_supported_sample_rate(uint32_t interval_us) { - BMI160SampleRate sample_rate; - if (BMI160SampleRate_12p5_HZ <= interval_us) { - sample_rate = BMI160SampleRate_12p5_HZ; - } else if (BMI160SampleRate_25_HZ <= interval_us) { - sample_rate = BMI160SampleRate_25_HZ; - } else if (BMI160SampleRate_50_HZ <= interval_us) { - sample_rate = BMI160SampleRate_50_HZ; - } else if (BMI160SampleRate_100_HZ <= interval_us) { - sample_rate = BMI160SampleRate_100_HZ; - } else if (BMI160SampleRate_200_HZ <= interval_us) { - sample_rate = BMI160SampleRate_200_HZ; - } else if (BMI160SampleRate_400_HZ <= interval_us) { - sample_rate = BMI160SampleRate_400_HZ; - } else if (BMI160SampleRate_800_HZ <= interval_us) { - sample_rate = BMI160SampleRate_800_HZ; - } else { // any interval < min supported must saturate to 1600Hz - sample_rate = BMI160SampleRate_1600_HZ; - } - return sample_rate; -} - -static void prv_configure_operating_mode(void) { - BMI160SampleRate interval_us = prv_get_min_sampling_interval_us(); - uint8_t acc_odr = (uint8_t)prv_get_odr(interval_us); - - // should be able to write the sample range at any time - prv_read_modify_write(BMI160_REG_ACC_CONF, acc_odr, - BMI160_ACC_CONF_ACC_ODR_MASK << BMI160_ACC_CONF_ACC_ODR_SHIFT); - -#ifdef BMI160_DEBUG - PBL_ASSERTN(interval_us == prv_get_sampling_interval_from_hw()); -#endif - - BMI160_DBG("Set sampling rate to %"PRIu32, (uint32_t)(1000000/interval_us)); - - if (s_accel_power_mode == BMI160_Accel_Mode_Normal) { - // This should only execute on startup or if the power mode is left in normal power mode for - // some reason - prv_accel_enable_undersampling(true); - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Low); - BMI160_DBG("%s", "Enable low power mode"); - } - - // TODO: If we aren't doing anything else, suspend the chip? -} - -static void prv_enable_operating_mode(AccelOperatingMode mode, BMI160SampleRate sample_rate) { - s_operating_states[mode].enabled = true; - s_operating_states[mode].sample_interval = sample_rate; - prv_configure_operating_mode(); -} - -static void prv_disable_operating_mode(AccelOperatingMode mode) { - s_operating_states[mode].enabled = false; - prv_configure_operating_mode(); -} - -uint32_t accel_set_sampling_interval(uint32_t interval_us) { - BMI160SampleRate sample_rate = prv_get_supported_sample_rate(interval_us); - prv_enable_operating_mode(AccelOperatingModeData, sample_rate); - return prv_get_min_sampling_interval_us(); -} - -uint32_t accel_get_sampling_interval(void) { - uint32_t curr_sampling_interval_us = prv_get_min_sampling_interval_us(); -#ifdef BMI160_DEBUG - PBL_ASSERTN(curr_sampling_interval_us == prv_get_sampling_interval_from_hw()); -#endif - return curr_sampling_interval_us; -} - -static void prv_configure_accel_sampling(bool enable_int, bool use_fifo) { - static bool int_enabled = false; - - if ((int_enabled == enable_int) && (s_fifo_in_use == use_fifo)) { - return; // No changes to make so don't redo writes - } - - uint8_t map_mask = - BMI160_INT_MAP_1_INT2_DATA_READY | BMI160_INT_MAP_1_INT2_FIFO_WATERMARK; - uint8_t int_en_mask = - BMI160_INT_EN_1_DRDY_EN | BMI160_INT_EN_1_FWM_EN; - - uint8_t map_val; - uint8_t int_en_val; - if (!enable_int) { - map_val = int_en_val = 0; - } else if (!use_fifo) { - map_val = BMI160_INT_MAP_1_INT2_DATA_READY; - int_en_val = BMI160_INT_EN_1_DRDY_EN; - } else { - map_val = BMI160_INT_MAP_1_INT2_FIFO_WATERMARK; - int_en_val = BMI160_INT_EN_1_FWM_EN; - } - - prv_read_modify_write(BMI160_REG_INT_MAP_1, map_val, map_mask); - prv_read_modify_write(BMI160_REG_INT_EN_1, int_en_val, int_en_mask); - - BMI160_DBG("INT_MAP[1] = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_MAP_1)); - BMI160_DBG("INT_EN[1] = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_INT_EN_1)); - int_enabled = enable_int; - s_fifo_in_use = use_fifo; -} - -void accel_set_num_samples(uint32_t num_samples) { - uint8_t fifo_frame_size = prv_get_fifo_frame_size(); // x,y,z accel data - uint16_t max_num_samples = (BMI160_FIFO_LEN_BYTES / fifo_frame_size); - max_num_samples = (max_num_samples / BMI160_FIFO_WM_UNIT_BYTES) * - BMI160_FIFO_WM_UNIT_BYTES; - - if (num_samples > max_num_samples) { - num_samples = max_num_samples; - } - - uint16_t curr_sample_size = bmi160_read_reg(BMI160_REG_FIFO_CONFIG_0) * - BMI160_FIFO_WM_UNIT_BYTES; - if (curr_sample_size > num_samples) { // flush what we have in the fifo, if any - BMI160_DBG("Curr Sample Size = %"PRId16, curr_sample_size); - prv_drain_fifo(); - } - - if (num_samples < 2) { - prv_write_reg(BMI160_REG_FIFO_CONFIG_1, 0); // power down the fifo - curr_fifo_num_samples_wm = 0; - } else { - curr_fifo_num_samples_wm = num_samples; - // Set the new fifo watermark - // TODO: I think we will want to try and make this a multiple of the frame size - uint8_t fifo_wm = (num_samples * fifo_frame_size) / BMI160_FIFO_WM_UNIT_BYTES; - prv_write_reg(BMI160_REG_FIFO_CONFIG_0, fifo_wm); - BMI160_DBG("FWM = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_FIFO_CONFIG_0)); - - uint8_t curr_frame_cfg = bmi160_read_reg(BMI160_REG_FIFO_CONFIG_1); - uint8_t desired_cfg = BMI160_FIFO_CONFIG_1_ACC_EN; - - if (curr_frame_cfg != desired_cfg) { - prv_run_command(BMI160_CMD_FIFO_FLUSH); // clear any lingering entries - prv_write_reg(BMI160_REG_FIFO_CONFIG_1, desired_cfg); - } - } - bool enable_int = num_samples != 0; - bool use_fifo = num_samples > 1; - prv_configure_accel_sampling(enable_int, use_fifo); -} - -int accel_peek(AccelDriverSample *data) { - prv_read_curr_accel_data(data); - return 0; -} - -void accel_set_shake_sensitivity_high(bool sensitivity_high) { - // Configure the threshold level at which the BMI160 will think shake has occurred - if (sensitivity_high) { - prv_write_reg(BMI160_REG_INT_MOTION_1, - BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdLow]); - } else { - prv_write_reg(BMI160_REG_INT_MOTION_1, - BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdHigh]); - } -} - -static void prv_enable_shake_detection(void) { - - // don't automatically power-up the gryo when an anymotion interrupt fires! - prv_write_reg(BMI160_REG_PMU_TRIGGER, 0x0); - - // Enable the anymotion detection interrupt - uint8_t en_val = BMI160_INT_MAP_ANYMOTION_EN_MASK; - prv_read_modify_write(BMI160_REG_INT_MAP_0, en_val, en_val); - - // Actually enable the anymotion interrupt - uint8_t int_en = (BMI160_INT_EN_0_ANYMOTION_Z_EN | - BMI160_INT_EN_0_ANYMOTION_Y_EN | BMI160_INT_EN_0_ANYMOTION_X_EN); - prv_read_modify_write(BMI160_REG_INT_EN_0, int_en, int_en); - - // configure the anymotion interrupt to fire after 4 successcive - // samples are over the threhold specified - accel_set_shake_sensitivity_high(false /* sensitivity_high */); - prv_write_reg(BMI160_REG_INT_MOTION_0, - 0x3 << BMI160_INT_MOTION_1_ANYM_DUR_SHIFT); - - // We temporarily latch the interrupt & do not clear it for anymotion interrupts to - // limit the number of anymotion interrupts to 1 per / 1.28 seconds - prv_write_reg(BMI160_REG_INT_LATCH, 0xd); - - prv_enable_operating_mode(AccelOperatingModeShakeDetection, BMI160SampleRate_25_HZ); - - BMI160_DBG("ACC_CONF = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_ACC_CONF)); -} - -static void prv_disable_shake_detection(void) { - // don't worry about the configuration registers but disable interrupts - // generated for taps from the accel - - uint8_t dis_mask = BMI160_INT_MAP_ANYMOTION_EN_MASK; - prv_read_modify_write(BMI160_REG_INT_MAP_0, 0, dis_mask); - - dis_mask = (BMI160_INT_EN_0_ANYMOTION_X_EN | BMI160_INT_EN_0_ANYMOTION_Y_EN | - BMI160_INT_EN_0_ANYMOTION_Z_EN); - prv_read_modify_write(BMI160_REG_INT_EN_0, 0x0, dis_mask); - - prv_disable_operating_mode(AccelOperatingModeShakeDetection); -} - -static void prv_enable_double_tap_detection(void) { - uint8_t tap_0_cfg = - (0x1 << BMI160_INT_TAP_QUIET_SHIFT) | // 0 = 20 ms, 1 = 30ms - (0x1 << BMI160_INT_TAP_SHOCK_SHIFT) | // 0 = 50 ms, 1 = 75ms - (0x4 << BMI160_INT_TAP_DUR_SHIFT); // 4 = 300 ms - prv_write_reg(BMI160_REG_INT_TAP_0, tap_0_cfg); - - // get the current scale - Bmi160Scale scale = prv_get_accel_scale(); - - // TODO: 4 or 5 bit granularity? - data sheet ambiguous, assume 5 - uint32_t threshold = BOARD_CONFIG_ACCEL.accel_config.double_tap_threshold; - const uint32_t STEP = 625; // 62.5 mg step at 2g range - - // calculate setting for 2g, then scale to higher g - uint32_t setting = (threshold / STEP) / (scale / 2); - uint8_t tap_1_cfg = (uint8_t)setting; - prv_write_reg(BMI160_REG_INT_TAP_1, tap_1_cfg); - - // Enable the double tap detection interrupt - uint8_t en_val = BMI160_INT_MAP_DOUBLE_TAP_EN_MASK; - prv_read_modify_write(BMI160_REG_INT_MAP_0, en_val, en_val); - - // Actually enable the double tap interrupt - uint8_t int_en = BMI160_INT_EN_0_D_TAP_EN; - prv_read_modify_write(BMI160_REG_INT_EN_0, int_en, int_en); - - BMI160_DBG("ACC_CONF = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_ACC_CONF)); - prv_enable_operating_mode(AccelOperatingModeDoubleTapDetection, BMI160SampleRate_200_HZ); - BMI160_DBG("ACC_CONF = 0x%"PRIx8, bmi160_read_reg(BMI160_REG_ACC_CONF)); -} - -static void prv_disable_double_tap_detection(void) { - uint8_t dis_mask = BMI160_INT_MAP_SINGLE_TAP_EN_MASK | BMI160_INT_MAP_DOUBLE_TAP_EN_MASK; - prv_read_modify_write(BMI160_REG_INT_MAP_0, 0, dis_mask); - - dis_mask = BMI160_INT_EN_0_S_TAP_EN | BMI160_INT_EN_0_D_TAP_EN; - prv_read_modify_write(BMI160_REG_INT_EN_0, 0x0, dis_mask); - - prv_disable_operating_mode(AccelOperatingModeDoubleTapDetection); -} - -void accel_enable_shake_detection(bool on) { - PBL_LOG(LOG_LEVEL_DEBUG, "enable shake detection %d", on); - if (s_shake_detection_enabled == on) { - // the requested change matches what we already have! - return; - } - - prv_update_accel_interrupts(on); - if (on) { - prv_enable_shake_detection(); - } else { - prv_disable_shake_detection(); - } - - s_shake_detection_enabled = on; -} - -void accel_enable_double_tap_detection(bool on) { - PBL_LOG(LOG_LEVEL_DEBUG, "enable double tap detection %d", on); - if (s_double_tap_detection_enabled == on) { - // the requested change matches what we already have! - return; - } - - prv_update_accel_interrupts(on); - if (on) { - prv_enable_double_tap_detection(); - } else { - prv_disable_double_tap_detection(); - } - - s_double_tap_detection_enabled = on; -} - -bool accel_get_shake_detection_enabled(void) { - return s_shake_detection_enabled; -} - -bool accel_get_double_tap_detection_enabled(void) { - return s_double_tap_detection_enabled; -} - -void accel_toggle_power_mode(void) { - static bool enable_low_power = false; - prv_accel_enable_undersampling(enable_low_power); - enable_low_power = !enable_low_power; -} diff --git a/src/fw/drivers/imu/bmi160/bmi160.h b/src/fw/drivers/imu/bmi160/bmi160.h deleted file mode 100644 index 1784b266a4..0000000000 --- a/src/fw/drivers/imu/bmi160/bmi160.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum { - BMI160_Accel_Mode_Suspend = 0b00, - BMI160_Accel_Mode_Normal = 0b01, - BMI160_Accel_Mode_Low = 0b10, -} BMI160AccelPowerMode; - -typedef enum { - BMI160_Gyro_Mode_Suspend = 0b00, - BMI160_Gyro_Mode_Normal = 0b01, - BMI160_Gyro_Mode_FastStartup = 0b11 -} BMI160GyroPowerMode; - -void bmi160_init(void); -bool bmi160_query_whoami(void); -void bmi160_set_accel_power_mode(BMI160AccelPowerMode mode); -void bmi160_set_gyro_power_mode(BMI160GyroPowerMode mode); diff --git a/src/fw/drivers/imu/bmi160/bmi160_private.h b/src/fw/drivers/imu/bmi160/bmi160_private.h deleted file mode 100644 index 9cf54bcb49..0000000000 --- a/src/fw/drivers/imu/bmi160/bmi160_private.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/spi.h" - -//! Ask the chip to accept input from the SPI bus. Required after issuing a soft reset. -void bmi160_enable_spi_mode(void); -void bmi160_begin_burst(uint8_t addr); -void bmi160_end_burst(void); - -uint8_t bmi160_read_reg(uint8_t reg); -uint16_t bmi160_read_16bit_reg(uint8_t reg); -void bmi160_write_reg(uint8_t reg, uint8_t value); diff --git a/src/fw/drivers/imu/bmi160/bmi160_regs.h b/src/fw/drivers/imu/bmi160/bmi160_regs.h deleted file mode 100644 index 8203b06cd0..0000000000 --- a/src/fw/drivers/imu/bmi160/bmi160_regs.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// BMI160 Register Map -static const uint8_t BMI160_REG_CHIP_ID = 0x00; -static const uint8_t BMI160_REG_ERR = 0x02; -static const uint8_t BMI160_REG_PMU_STATUS = 0x03; - -// Current sensor data -static const uint8_t BMI160_REG_DATA_0 = 0x04; -// Register names differ from those in datasheet -static const uint8_t BMI160_REG_MAG_X_LSB = 0x04; -static const uint8_t BMI160_REG_MAG_X_MSB = 0x05; -static const uint8_t BMI160_REG_MAG_Y_LSB = 0x06; -static const uint8_t BMI160_REG_MAG_Y_MSB = 0x07; -static const uint8_t BMI160_REG_MAG_Z_LSB = 0x08; -static const uint8_t BMI160_REG_MAG_Z_MSB = 0x09; -static const uint8_t BMI160_REG_RHALL_LSB = 0x0A; -static const uint8_t BMI160_REG_RHALL_MSB = 0x0B; -static const uint8_t BMI160_REG_GYR_X_LSB = 0x0C; -static const uint8_t BMI160_REG_GYR_X_MSB = 0x0D; -static const uint8_t BMI160_REG_GYR_Y_LSB = 0x0E; -static const uint8_t BMI160_REG_GYR_Y_MSB = 0x0F; -static const uint8_t BMI160_REG_GYR_Z_LSB = 0x10; -static const uint8_t BMI160_REG_GYR_Z_MSB = 0x11; -static const uint8_t BMI160_REG_ACC_X_LSB = 0x12; -static const uint8_t BMI160_REG_ACC_X_MSB = 0x13; -static const uint8_t BMI160_REG_ACC_Y_LSB = 0x14; -static const uint8_t BMI160_REG_ACC_Y_MSB = 0x15; -static const uint8_t BMI160_REG_ACC_Z_LSB = 0x16; -static const uint8_t BMI160_REG_ACC_Z_MSB = 0x17; -// Sensor time is stored in 24-bit little-endian format (LSB in BMI160_REG_SENSORTIME_0) -static const uint8_t BMI160_REG_SENSORTIME_0 = 0x18; -static const uint8_t BMI160_REG_SENSORTIME_1 = 0x19; -static const uint8_t BMI160_REG_SENSORTIME_2 = 0x1A; - -static const uint8_t BMI160_SENSORTIME_RESOLUTION_US = 39; - -// Status registers -static const uint8_t BMI160_REG_STATUS = 0x1B; -static const uint8_t BMI160_REG_INT_STATUS_0 = 0x1C; -static const uint8_t BMI160_REG_INT_STATUS_1 = 0x1D; -static const uint8_t BMI160_REG_INT_STATUS_2 = 0x1E; -static const uint8_t BMI160_REG_INT_STATUS_3 = 0x1F; -static const uint8_t BMI160_REG_TEMPERATURE_LSB = 0x20; -static const uint8_t BMI160_REG_TEMPERATURE_MSB = 0x21; - -// FIFO -static const uint8_t BMI160_REG_FIFO_LENGTH_LSB = 0x22; -static const uint8_t BMI160_REG_FIFO_LENGTH_MSB = 0x23; -static const uint8_t BMI160_REG_FIFO_DOWNS = 0x45; -static const uint8_t BMI160_REG_FIFO_CONFIG_0 = 0x46; -static const uint8_t BMI160_REG_FIFO_CONFIG_1 = 0x47; -static const uint8_t BMI160_REG_FIFO_DATA = 0x24; - -static const uint8_t BMI160_REG_ACC_CONF = 0x40; -static const uint8_t BMI160_REG_ACC_RANGE = 0x41; -static const uint8_t BMI160_REG_GYR_CONF = 0x42; -static const uint8_t BMI160_REG_GYR_RANGE = 0x43; -static const uint8_t BMI160_REG_MAG_CONF = 0x44; - -// Magnetometer interface configuration -static const uint8_t BMI160_REG_MAG_I2C_DEVICE_ADDR = 0x4B; -static const uint8_t BMI160_REG_MAG_IF_1 = 0x4C; -static const uint8_t BMI160_MAG_IF_1_MANUAL_MODE_ENABLE = 0x80; - -static const uint8_t BMI160_REG_MAG_READ_ADDR = 0x4D; -static const uint8_t BMI160_REG_MAG_WRITE_ADDR = 0x4E; -static const uint8_t BMI160_REG_MAG_WRITE_DATA = 0x4F; - -// Interrupt configuration -static const uint8_t BMI160_REG_INT_EN_0 = 0x50; -static const uint8_t BMI160_REG_INT_EN_1 = 0x51; -static const uint8_t BMI160_REG_INT_EN_2 = 0x52; -static const uint8_t BMI160_REG_INT_OUT_CTRL = 0x53; -static const uint8_t BMI160_REG_INT_LATCH = 0x54; -static const uint8_t BMI160_REG_INT_MAP_0 = 0x55; -static const uint8_t BMI160_REG_INT_MAP_1 = 0x56; -static const uint8_t BMI160_REG_INT_MAP_2 = 0x57; -static const uint8_t BMI160_REG_INT_DATA_0 = 0x58; -static const uint8_t BMI160_REG_INT_DATA_1 = 0x59; -static const uint8_t BMI160_REG_INT_LOWHIGH_0 = 0x5A; -static const uint8_t BMI160_REG_INT_LOWHIGH_1 = 0x5B; -static const uint8_t BMI160_REG_INT_LOWHIGH_2 = 0x5C; -static const uint8_t BMI160_REG_INT_LOWHIGH_3 = 0x5D; -static const uint8_t BMI160_REG_INT_LOWHIGH_4 = 0x5E; -static const uint8_t BMI160_REG_INT_MOTION_0 = 0x5F; -static const uint8_t BMI160_REG_INT_MOTION_1 = 0x60; -static const uint8_t BMI160_REG_INT_MOTION_2 = 0x61; -static const uint8_t BMI160_REG_INT_MOTION_3 = 0x62; -static const uint8_t BMI160_REG_INT_TAP_0 = 0x63; -static const uint8_t BMI160_REG_INT_TAP_1 = 0x64; -static const uint8_t BMI160_REG_INT_ORIENT_0 = 0x65; -static const uint8_t BMI160_REG_INT_ORIENT_1 = 0x66; -static const uint8_t BMI160_REG_INT_FLAT_0 = 0x67; -static const uint8_t BMI160_REG_INT_FLAT_1 = 0x68; - -static const uint8_t BMI160_REG_FOC_CONF = 0x69; -static const uint8_t BMI160_REG_CONF = 0x6A; - -static const uint8_t BMI160_REG_IF_CONF = 0x6B; -static const uint8_t BMI160_IF_CONF_MAG_ENABLE = 0x20; // Undocumented - -static const uint8_t BMI160_REG_PMU_TRIGGER = 0x6C; -static const uint8_t BMI160_REG_SELF_TEST = 0x6D; -static const uint8_t BMI160_REG_NV_CONF = 0x70; - -static const uint8_t BMI160_REG_OFFSET_ACC_X = 0x71; -static const uint8_t BMI160_REG_OFFSET_ACC_Y = 0x72; -static const uint8_t BMI160_REG_OFFSET_ACC_Z = 0x73; -static const uint8_t BMI160_REG_OFFSET_GYR_X_LSB = 0x74; -static const uint8_t BMI160_REG_OFFSET_GYR_Y_LSB = 0x75; -static const uint8_t BMI160_REG_OFFSET_GYR_Z_LSB = 0x76; -static const uint8_t BMI160_REG_OFFSET_7 = 0x77; - -static const uint8_t BMI160_REG_STEPCOUNTER_LSB = 0x78; -static const uint8_t BMI160_REG_STEPCOUNTER_MSB = 0x79; -static const uint8_t BMI160_REG_INT_STEP_DET_CONF_0 = 0x7A; -static const uint8_t BMI160_REG_STEPCOUNTER_CONF = 0x7B; - -static const uint8_t BMI160_REG_CMD = 0x7E; -static const uint8_t BMI160_CMD_START_FOC = 0x03; -// To set the PMU mode, bitwise-or the command with the desired mode -// from the BMI160*PowerMode enums. -static const uint8_t BMI160_CMD_ACC_SET_PMU_MODE = 0x10; -static const uint8_t BMI160_CMD_GYR_SET_PMU_MODE = 0x14; -static const uint8_t BMI160_CMD_MAG_SET_PMU_MODE = 0x18; - -static const uint8_t BMI160_CMD_PROG_NVM = 0xA0; -static const uint8_t BMI160_CMD_FIFO_FLUSH = 0xB0; -static const uint8_t BMI160_CMD_INT_RESET = 0xB1; -static const uint8_t BMI160_CMD_SOFTRESET = 0xB6; -static const uint8_t BMI160_CMD_STEP_CNT_CLR = 0xB2; - -// Command sequence to enable "extended mode" -static const uint8_t BMI160_CMD_EXTMODE_EN_FIRST = 0x37; -static const uint8_t BMI160_CMD_EXTMODE_EN_MIDDLE = 0x9A; -static const uint8_t BMI160_CMD_EXTMODE_EN_LAST = 0xc0; - -// Extended mode register; see Bosch Android driver -static const uint8_t BMI160_REG_EXT_MODE = 0x7F; -static const uint8_t BMI160_EXT_MODE_PAGING_EN = 0x80; -static const uint8_t BMI160_EXT_MODE_TARGET_PAGE_1 = 0x10; - -// Constants -static const uint8_t BMI160_CHIP_ID = 0xD1; - -static const uint8_t BMI160_REG_MASK = 0x7F; // Register address is 7 bits wide -static const uint8_t BMI160_READ_FLAG = 0x80; - -/* - * Register Definitions - */ - -// ACC_CONF -static const uint8_t BMI160_ACC_CONF_ACC_US_MASK = 0x1; -static const uint8_t BMI160_ACC_CONF_ACC_US_SHIFT = 7; -static const uint8_t BMI160_ACC_CONF_ACC_BWP_SHIFT = 4; -static const uint8_t BMI160_ACC_CONF_ACC_BWP_MASK = 0x7; -static const uint8_t BMI160_ACC_CONF_ACC_ODR_SHIFT = 0; -static const uint8_t BMI160_ACC_CONF_ACC_ODR_MASK = 0xf; - -// Hz = 100 / 2 ^ (8 - ACC_ODR_VAL) -typedef enum { /* value is the interval in microseconds */ - BMI160SampleRate_12p5_HZ = 80000, - BMI160SampleRate_25_HZ = 40000, - BMI160SampleRate_50_HZ = 20000, - BMI160SampleRate_100_HZ = 10000, - BMI160SampleRate_200_HZ = 5000, - BMI160SampleRate_400_HZ = 2500, - BMI160SampleRate_800_HZ = 1250, - BMI160SampleRate_1600_HZ = 625, -} BMI160SampleRate; - -typedef enum { /* value matches ACC_CONF ODR setting that must be programmed */ - BMI160AccODR_12p5_HZ = 5, - BMI160AccODR_25_HZ = 6, - BMI160AccODR_50_HZ = 7, - BMI160AccODR_100_HZ = 8, - BMI160AccODR_200_HZ = 9, - BMI160AccODR_400_HZ = 10, - BMI160AccODR_800_HZ = 11, - BMI160AccODR_1600_HZ = 12, -} BMI160AccODR; -// TODO: Create a better way to change the frequency - -#define BMI160_ACC_CONF_ODR_RESET_VALUE_US BMI160SampleRate_100_HZ - -#define BMI160_ACC_CONF_NORMAL_BODE_US_AND_BWP 0x2 -#define BMI160_DEFAULT_ACC_CONF_VALUE \ - ((BMI160_ACC_CONF_NORMAL_BODE_US_AND_BWP << BMI160_ACC_CONF_ACC_BWP_SHIFT) | \ - (BMI160AccODR_50_HZ << BMI160_ACC_CONF_ACC_ODR_SHIFT)) - -#define BMI160_ACC_CONF_SELF_TEST_VALUE 0x2c // See 2.8.1 of bmi160 data sheet - -// Values for BMI160_REG_ACC_RANGE -static const uint8_t BMI160_ACC_RANGE_2G = 0x3; -static const uint8_t BMI160_ACC_RANGE_4G = 0x5; -static const uint8_t BMI160_ACC_RANGE_8G = 0x8; -static const uint8_t BMI160_ACC_RANGE_16G = 0xC; - -// STATUS -static const uint8_t BMI160_STATUS_DRDY_ACC_MASK = (1 << 7); -static const uint8_t BMI160_STATUS_DRDY_GYR_MASK = (1 << 6); -static const uint8_t BMI160_STATUS_DRDY_MAG_MASK = (1 << 5); -static const uint8_t BMI160_STATUS_NVM_RDY_MASK = (1 << 4); -static const uint8_t BMI160_STATUS_FOC_RDY_MASK = (1 << 3); -static const uint8_t BMI160_STATUS_MAG_MAN_OP_MASK = (1 << 2); -static const uint8_t BMI160_STATUS_GYR_SELF_TEST_OK_MASK = (1 << 1); -static const uint8_t BMI160_STATUS_GYR_POR_DETECTED_MASK = (1 << 0); - -// INT_TAP[0] -static const uint8_t BMI160_INT_TAP_QUIET_SHIFT = 7; -static const uint8_t BMI160_INT_TAP_SHOCK_SHIFT = 6; -// bits 5:3 reserved -static const uint8_t BMI160_INT_TAP_DUR_SHIFT = 0; - -// INT_TAP[1] Register Definition -// bit 7 reserved -static const uint8_t BMI160_INT_TAP_THRESH_SHIFT = 4; - -// INT_MAP[0] Register -static const uint8_t BMI160_INT_MAP_FLAT_EN_MASK = (1 << 7); -static const uint8_t BMI160_INT_MAP_ORIENTATION_EN_MASK = (1 << 6); -static const uint8_t BMI160_INT_MAP_SINGLE_TAP_EN_MASK = (1 << 5); -static const uint8_t BMI160_INT_MAP_DOUBLE_TAP_EN_MASK = (1 << 4); -static const uint8_t BMI160_INT_MAP_NO_MOTION_EN_MASK = (1 << 3); -static const uint8_t BMI160_INT_MAP_ANYMOTION_EN_MASK = (1 << 2); -static const uint8_t BMI160_INT_MAP_HIGHG_EN_MASK = (1 << 1); -static const uint8_t BMI160_INT_MAP_LOWG_MASK = (1 << 0); - -// INT_MAP[1] Register -static const uint8_t BMI160_INT_MAP_1_INT1_DATA_READY = (1 << 7); -static const uint8_t BMI160_INT_MAP_1_INT1_FIFO_WATERMARK = (1 << 6); -static const uint8_t BMI160_INT_MAP_1_INT1_FIFO_FULL = (1 << 5); -static const uint8_t BMI160_INT_MAP_1_INT1_PMU_TRIGGER = (1 << 4); -static const uint8_t BMI160_INT_MAP_1_INT2_DATA_READY = (1 << 3); -static const uint8_t BMI160_INT_MAP_1_INT2_FIFO_WATERMARK = (1 << 2); -static const uint8_t BMI160_INT_MAP_1_INT2_FIFO_FULL = (1 << 1); -static const uint8_t BMI160_INT_MAP_1_INT2_PMU_TRIGGER = (1 << 0); - -// INT_STATUS[0] -static const uint8_t BMI160_INT_STATUS_0_FLAT_MASK = (1 << 7); -static const uint8_t BMI160_INT_STATUS_0_ORIENT_MASK = (1 << 6); -static const uint8_t BMI160_INT_STATUS_0_S_TAP_MASK = (1 << 5); -static const uint8_t BMI160_INT_STATUS_0_D_TAP_MASK = (1 << 4); -static const uint8_t BMI160_INT_STATUS_0_PMU_TRIGGER_MASK = (1 << 3); -static const uint8_t BMI160_INT_STATUS_0_ANYM_MASK = (1 << 2); -static const uint8_t BMI160_INT_STATUS_0_SIGMOT_MASK = (1 << 1); -static const uint8_t BMI160_INT_STATUS_0_STEP_INT_MASK = (1 << 0); - -// INT_STATUS[1] -static const uint8_t BMI160_INT_STATUS_1_NOMO_MASK = (1 << 7); -static const uint8_t BMI160_INT_STATUS_1_FWM_MASK = (1 << 6); -static const uint8_t BMI160_INT_STATUS_1_FFULL_MASK = (1 << 5); -static const uint8_t BMI160_INT_STATUS_1_DRDY_MASK = (1 << 4); -static const uint8_t BMI160_INT_STATUS_1_LOWG_MASK = (1 << 3); -static const uint8_t BMI160_INT_STATUS_1_HIGHG_Z_MASK = (1 << 2); -// bit 1 & 0 reserved - -// INT_STATUS[2] -static const uint8_t BMI160_INT_STATUS_2_TAP_SIGN = (1 << 7); -static const uint8_t BMI160_INT_STATUS_2_TAP_FIRST_Z = (1 << 6); -static const uint8_t BMI160_INT_STATUS_2_TAP_FIRST_Y = (1 << 5); -static const uint8_t BMI160_INT_STATUS_2_TAP_FIRST_X = (1 << 4); -static const uint8_t BMI160_INT_STATUS_2_ANYM_SIGN = (1 << 3); -static const uint8_t BMI160_INT_STATUS_2_ANYM_FIRST_Z = (1 << 2); -static const uint8_t BMI160_INT_STATUS_2_ANYM_FIRST_Y = (1 << 1); -static const uint8_t BMI160_INT_STATUS_2_ANYM_FIRST_X = (1 << 0); - -// INT_EN[0] -static const uint8_t BMI160_INT_EN_0_FLAT_EN = (1 << 7); -static const uint8_t BMI160_INT_EN_0_ORIENT_EN = (1 << 6); -static const uint8_t BMI160_INT_EN_0_S_TAP_EN = (1 << 5); -static const uint8_t BMI160_INT_EN_0_D_TAP_EN = (1 << 4); -// bit 3 reserved -static const uint8_t BMI160_INT_EN_0_ANYMOTION_Z_EN = (1 << 2); -static const uint8_t BMI160_INT_EN_0_ANYMOTION_Y_EN = (1 << 1); -static const uint8_t BMI160_INT_EN_0_ANYMOTION_X_EN = (1 << 0); - -// INT_EN[1] -// bit 7 reserved -static const uint8_t BMI160_INT_EN_1_FWM_EN = (1 << 6); -static const uint8_t BMI160_INT_EN_1_FFUL_EN = (1 << 5); -static const uint8_t BMI160_INT_EN_1_DRDY_EN = (1 << 4); -static const uint8_t BMI160_INT_EN_1_LOW_EN = (1 << 3); -static const uint8_t BMI160_INT_EN_1_HIGHZ_EN = (1 << 2); -static const uint8_t BMI160_INT_EN_1_HIGHY_EN = (1 << 1); -static const uint8_t BMI160_INT_EN_1_HIGHX_EN = (1 << 0); - -// FIFO_CONFIG[0] -static const uint8_t BMI160_FIFO_CONFIG_0_FWM_SHIFT = 0; -static const uint8_t BMI160_FIFO_CONFIG_0_FWM_MASK = 0xff; - -#define BMI160_FIFO_LEN_BYTES 1024 -#define BMI160_FIFO_WM_UNIT_BYTES 4 - -// FIFO_CONFIG[1] -static const uint8_t BMI160_FIFO_CONFIG_1_GYR_EN = (1 << 7); -static const uint8_t BMI160_FIFO_CONFIG_1_ACC_EN = (1 << 6); -static const uint8_t BMI160_FIFO_CONFIG_1_MAG_EN = (1 << 5); -static const uint8_t BMI160_FIFO_CONFIG_1_HDR_EN = (1 << 4); -static const uint8_t BMI160_FIFO_CONFIG_1_TAG_INT1_EN = (1 << 3); -static const uint8_t BMI160_FIFO_CONFIG_1_TAG_INT2_EN = (1 << 2); -static const uint8_t BMI160_FIFO_CONFIG_1_TIME_EN = (1 << 1); -// bit 0 reserved - -// INT_MOTION[0] -static const uint8_t BMI160_INT_MOTION_1_ANYM_DUR_SHIFT = 0; -static const uint8_t BMI160_INT_MOTION_1_ANYM_DUR_MASK = 0x3; -static const uint8_t BMI160_INT_MOTION_1_SLOWM_SHIFT = 2; -static const uint8_t BMI160_INT_MOTION_1_SLOWM_MASK = 0xfc; - -// INT_MOTION[1] -static const uint8_t BMI160_INT_MOTION_1_ANYM_THRESH_SHIFT = 0; -static const uint8_t BMI160_INT_MOTION_1_ANYM_THRESH_MASK = 0xff; diff --git a/src/fw/drivers/imu/bmi160/bmi160_spi.c b/src/fw/drivers/imu/bmi160/bmi160_spi.c deleted file mode 100644 index 4a3a181d22..0000000000 --- a/src/fw/drivers/imu/bmi160/bmi160_spi.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "kernel/util/sleep.h" -#include "drivers/spi.h" -#include "board/board.h" - -#include "bmi160_private.h" -#include "bmi160_regs.h" - -void bmi160_begin_burst(uint8_t addr) { - spi_ll_slave_acquire(BMI160_SPI); - spi_ll_slave_scs_assert(BMI160_SPI); - spi_ll_slave_read_write(BMI160_SPI, addr); -} - -void bmi160_end_burst(void) { - spi_ll_slave_scs_deassert(BMI160_SPI); - spi_ll_slave_release(BMI160_SPI); -} - -uint8_t bmi160_read_reg(uint8_t reg) { - uint8_t value; - // Registers are read when the address MSB=1 - reg |= BMI160_READ_FLAG; - SPIScatterGather sg_info[2] = { - {.sg_len = 1, .sg_out = ®, .sg_in = NULL}, // address - {.sg_len = 1, .sg_out = NULL, .sg_in = &value} // 8 bit register read - }; - spi_slave_burst_read_write_scatter(BMI160_SPI, sg_info, ARRAY_LENGTH(sg_info)); - return value; -} - -uint16_t bmi160_read_16bit_reg(uint8_t reg) { - // 16-bit registers are in little-endian format - uint16_t value; - reg |= BMI160_READ_FLAG; - SPIScatterGather sg_info[2] = { - {.sg_len = 1, .sg_out = ®, .sg_in = NULL}, // address - {.sg_len = 2, .sg_out = NULL, .sg_in = &value} // 16 bit register read - }; - spi_slave_burst_read_write_scatter(BMI160_SPI, sg_info, ARRAY_LENGTH(sg_info)); - return value; -} - -void bmi160_write_reg(uint8_t reg, uint8_t value) { - reg &= BMI160_REG_MASK; - SPIScatterGather sg_info[2] = { - {.sg_len = 1, .sg_out = ®, .sg_in = NULL}, // address - {.sg_len = 1, .sg_out = &value, .sg_in = NULL} // register write - }; - spi_slave_burst_read_write_scatter(BMI160_SPI, sg_info, ARRAY_LENGTH(sg_info)); -} - -void bmi160_enable_spi_mode(void) { - // The BMI160 needs a rising edge on the SCS pin to switch into SPI mode. - // The datasheet recommends performing a read to register 0x7F (reserved) - // to put the chip into SPI mode. - bmi160_read_reg(0x7f); - - psleep(2); // Necessary on cold boots; not sure why -} diff --git a/src/fw/drivers/imu/imu_asterix.c b/src/fw/drivers/imu/imu_asterix.c deleted file mode 100644 index c398fcd6b3..0000000000 --- a/src/fw/drivers/imu/imu_asterix.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "board/board.h" -#include "drivers/accel.h" -#include "drivers/i2c.h" -#include "drivers/imu.h" -#include "drivers/imu/lsm6dso/lsm6dso.h" -#include "drivers/imu/mmc5603nj/mmc5603nj.h" -#include "drivers/spi.h" -#include "kernel/util/delay.h" -#include "system/logging.h" - -#define BMP390_CHIP_ID 0x00 -#define BMP390_CHIP_ID_VALUE 0x60 -#define BMP390_PWR_CTRL 0x1B - - -static bool prv_read_register(I2CSlavePort *i2c, uint8_t register_address, uint8_t *result) { - i2c_use(i2c); - bool rv = i2c_write_block(i2c, 1, ®ister_address); - if (rv) - rv = i2c_read_block(i2c, 1, result); - i2c_release(i2c); - return rv; -} - -static bool prv_write_register(I2CSlavePort *i2c, uint8_t register_address, uint8_t datum) { - i2c_use(i2c); - uint8_t d[2] = { register_address, datum }; - bool rv = i2c_write_block(i2c, 2, d); - i2c_release(i2c); - return rv; -} - -void imu_init(void) { - bool rv; - uint8_t result; - - rv = prv_read_register(I2C_BMP390, BMP390_CHIP_ID, &result); - if (!rv || result != BMP390_CHIP_ID_VALUE) { - PBL_LOG(LOG_LEVEL_DEBUG, "BMP390 probe failed; rv %d, result 0x%02x", rv, result); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "found the BMP390, setting to low power"); - (void) prv_write_register(I2C_BMP390, BMP390_PWR_CTRL, 0); - } - - lsm6dso_init(); - mmc5603nj_init(); -} - -void imu_power_up(void) { - lsm6dso_power_up(); -} - -void imu_power_down(void) { - lsm6dso_power_down(); -} diff --git a/src/fw/drivers/imu/imu_obelix.c b/src/fw/drivers/imu/imu_obelix.c deleted file mode 100644 index 39131c567d..0000000000 --- a/src/fw/drivers/imu/imu_obelix.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/imu.h" -#include "drivers/imu/lsm6dso/lsm6dso.h" -#include "drivers/imu/mmc5603nj/mmc5603nj.h" - -void imu_init(void) { - lsm6dso_init(); - mmc5603nj_init(); -} - -void imu_power_up(void) { - lsm6dso_power_up(); -} - -void imu_power_down(void) { - lsm6dso_power_down(); -} diff --git a/src/fw/drivers/imu/imu_qemu.c b/src/fw/drivers/imu/imu_qemu.c deleted file mode 100644 index b7bcbef42c..0000000000 --- a/src/fw/drivers/imu/imu_qemu.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/imu.h" -#include "drivers/imu/mag3110/mag3110.h" -#include "drivers/mag.h" -#include "drivers/qemu/qemu_accel.h" - -void imu_init(void) { - qemu_accel_init(); -#if CAPABILITY_HAS_MAGNETOMETER - mag3110_init(); -#endif -} - -void imu_power_up(void) { -} - -void imu_power_down(void) { -} - -bool gyro_run_selftest(void) { - return true; -} diff --git a/src/fw/drivers/imu/imu_silk.c b/src/fw/drivers/imu/imu_silk.c deleted file mode 100644 index 71a3214d7a..0000000000 --- a/src/fw/drivers/imu/imu_silk.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/accel.h" -#include "drivers/imu.h" -#include "drivers/imu/bma255/bma255.h" - -void imu_init(void) { - bma255_init(); -} - -void imu_power_up(void) { - // NYI -} - -void imu_power_down(void) { - // NYI -} - -#if !TARGET_QEMU - -//////////////////////////////////// -// Accel -// -//////////////////////////////////// - -void accel_enable_double_tap_detection(bool on) { -} - -bool accel_get_double_tap_detection_enabled(void) { - return false; -} - -#endif /* !TARGET_QEMU */ diff --git a/src/fw/drivers/imu/imu_snowy_evt.c b/src/fw/drivers/imu/imu_snowy_evt.c deleted file mode 100644 index e2dbdb2449..0000000000 --- a/src/fw/drivers/imu/imu_snowy_evt.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "system/logging.h" -#include "drivers/imu/bmi160/bmi160.h" -#include "drivers/mag.h" - -#include -#include -#include - - -void imu_init(void) { - bmi160_init(); - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Normal); -} - -void imu_power_up(void) { - // Unused in snowy as PMIC turns on everything -} - -void imu_power_down(void) { - // Unused in snowy as PMIC turns off everything -} - -// We don't actually support the mag at all on snowy_evt, as we tried a more complicated -// arrangement where the mag was a slave of the bmi160 chip. We never fully implemented it, and -// as we abandoned that approach there's no point in ever implementing it. Below are a bunch of -// stubs that do nothing on this board. - -void mag_use(void) { -} - -void mag_start_sampling(void) { -} - -void mag_release(void) { -} - -MagReadStatus mag_read_data(MagData *data) { - return MagReadCommunicationFail; -} - -bool mag_change_sample_rate(MagSampleRate rate) { - return false; -} - diff --git a/src/fw/drivers/imu/imu_snowy_evt2.c b/src/fw/drivers/imu/imu_snowy_evt2.c deleted file mode 100644 index 27b356286a..0000000000 --- a/src/fw/drivers/imu/imu_snowy_evt2.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/imu/bmi160/bmi160.h" -#include "drivers/imu/mag3110/mag3110.h" - -#include "system/logging.h" - -#include -#include -#include - - -void imu_init(void) { - bmi160_init(); - bmi160_set_accel_power_mode(BMI160_Accel_Mode_Normal); - - mag3110_init(); -} - -void imu_power_up(void) { - // Unused in snowy as PMIC turns on everything -} - -void imu_power_down(void) { - // Unused in snowy as PMIC turns off everything -} diff --git a/src/fw/drivers/imu/imu_tintin.c b/src/fw/drivers/imu/imu_tintin.c deleted file mode 100644 index 19c0c55060..0000000000 --- a/src/fw/drivers/imu/imu_tintin.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "system/logging.h" -#include "drivers/imu/lis3dh/lis3dh.h" -#include "drivers/imu/mag3110/mag3110.h" - - -void imu_init(void) { - // Init accelerometer - lis3dh_init(); - // Init magnetometer - mag3110_init(); -} - -void imu_power_up(void) { - lis3dh_power_up(); -} - -void imu_power_down(void) { - lis3dh_power_down(); -} diff --git a/src/fw/drivers/imu/lis2dw12/lis2dw12.c b/src/fw/drivers/imu/lis2dw12/lis2dw12.c new file mode 100644 index 0000000000..fbc86c2e8d --- /dev/null +++ b/src/fw/drivers/imu/lis2dw12/lis2dw12.c @@ -0,0 +1,816 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "drivers/accel.h" +#include "drivers/exti.h" +#include "drivers/i2c.h" +#include "drivers/rtc.h" +#include "drivers/gpio.h" +#include "pbl/services/imu/units.h" +#include "pbl/services/regular_timer.h" +#include "system/logging.h" +#include "system/status_codes.h" +#include "kernel/util/delay.h" +#include "kernel/util/sleep.h" +#include "util/math.h" + +PBL_LOG_MODULE_DEFINE(driver_accel_lis2dw12, CONFIG_DRIVER_IMU_LOG_LEVEL); + +// Implementation notes: +// +// - Peeking returns the last FIFO sample when sampling is active, otherwise +// single-shot mode is used to perform the measurement +// - Low-power mode 1 (12-bit) is always used (minimum power mode) +// - ODR is limited to the [12.5, 200] Hz range +// - Shake detection uses 12.5Hz when no active sampling is ongoing +// - Wake-up duration absolute time depends on the ODR, a parameter that can +// be changed depending on the sampling interval configuration. Value is NOT +// adjusted automatically when ODR changes (we just have 2 bits...), so it is +// possible to notice sensitivity changes when changing sampling interval. +// - For some reason (needs more investigation), INT1 is sometimes left HIGH +// due to FIFO overruns, and without edge change, we cannot detect such +// events. To mitigate this, there is a watchdog timer that re-arms FIFO +// if no INT1 event is detected within the expected time window based on the +// ODR and FIFO threshold. + +// Time to wait after reset (us) +#define LIS2DW12_RESET_TIME_US 5 + +// DRDY polling parameters for accel_peek single-shot mode +#define LIS2DW12_DRDY_POLL_DELAY_MS (5) /* ms between data-ready polls */ +#define LIS2DW12_DRDY_POLL_TIMEOUT_MS (100) /* max wait (~5x 20ms at 50Hz ODR) */ + +// Scale range when in 12-bit mode (low-power mode 1) +#define LIS2DW12_S12_SCALE_RANGE (1U << (12U - 1U)) + +// Registers +#define LIS2DW12_WHO_AM_I 0x0FU +#define LIS2DW12_UNDOC 0x17U +#define LIS2DW12_CTRL1 0x20U +#define LIS2DW12_CTRL2 0x21U +#define LIS2DW12_CTRL3 0x22U +#define LIS2DW12_CTRL4_INT1_PAD_CTRL 0x23U +#define LIS2DW12_CTRL5_INT2_PAD_CTRL 0x24U +#define LIS2DW12_CTRL6 0x25U +#define LIS2DW12_STATUS 0x27U +#define LIS2DW12_OUT_X_L 0x28U +#define LIS2DW12_FIFO_CTRL 0x2EU +#define LIS2DW12_FIFO_SAMPLES 0x2FU +#define LIS2DW12_WAKE_UP_THS 0x34U +#define LIS2DW12_WAKE_UP_DUR 0x35U +#define LIS2DW12_ALL_INT_SRC 0x3BU +#define LIS2DW12_CTRL7 0x3FU + +// WHO_AM_I fields +#define LIS2DW12_WHO_AM_I_VAL 0x44U + +// UNDOC fields +#define LIS2DW12_UNDOC_ADDR_PULLUP_DIS (1U << 6U) + +// CTRL1 fields +#define LIS2DW12_CTRL1_LP_MODE1 (0U << 0U) +#define LIS2DW12_CTRL1_MODE_LP (0U << 2U) +#define LIS2DW12_CTRL1_MODE_SINGLE (2U << 2U) +#define LIS2DW12_CTRL1_ODR_PD (0x0U << 4U) +#define LIS2DW12_CTRL1_ODR_1HZ6_LP_ONLY (0x1U << 4U) +#define LIS2DW12_CTRL1_ODR_12HZ5 (0x2U << 4U) +#define LIS2DW12_CTRL1_ODR_25HZ (0x3U << 4U) +#define LIS2DW12_CTRL1_ODR_50HZ (0x4U << 4U) +#define LIS2DW12_CTRL1_ODR_100HZ (0x5U << 4U) +#define LIS2DW12_CTRL1_ODR_200HZ (0x6U << 4U) +#define LIS2DW12_CTRL1_ODR_400HZ_HP_ONLY (0x7U << 4U) +#define LIS2DW12_CTRL1_ODR_800HZ_HP_ONLY (0x8U << 4U) +#define LIS2DW12_CTRL1_ODR_1K6HZ_HP_ONLY (0x9U << 4U) + +// CTRL2 fields +#define LIS2DW12_CTRL2_SOFT_RESET (1U << 6U) +#define LIS2DW12_CTRL2_BOOT (1U << 7U) + +// CTRL3 fields +#define LIS2DW12_CTRL3_SLP_MODE_1 (1U << 0U) +#define LIS2DW12_CTRL3_SLP_MODE_SEL_SLP_MODE_1 (1U << 1U) +#define LIS2DW12_CTRL3_LIR (1U << 4U) + +// CTRL4_INT1_PAD_CTRL fields +#define LIS2DW12_CTRL4_INT1_PAD_CTRL_INT1_WU (1U << 5U) +#define LIS2DW12_CTRL4_INT1_PAD_CTRL_INT1_FTH (1U << 1U) + +// CTRL5_INT2_PAD_CTRL fields +#define LIS2DW12_CTRL5_INT2_PAD_CTRL_INT2_OVR (1U << 3U) + +// CTRL6 fields +#define LIS2DW12_CTRL6_FS_2G (0U << 4U) +#define LIS2DW12_CTRL6_FS_4G (1U << 4U) +#define LIS2DW12_CTRL6_FS_8G (2U << 4U) +#define LIS2DW12_CTRL6_FS_16G (3U << 4U) + +// STATUS fields +#define LIS2DW12_STATUS_DRDY (1U << 0U) + +// FIFO_CTRL fields +#define LIS2DW12_FIFO_CTRL_FTH_POS 0U +#define LIS2DW12_FIFO_CTRL_FTH_MASK 0x1FU +#define LIS2DW12_FIFO_CTRL_FTH(val) \ + (((val) << LIS2DW12_FIFO_CTRL_FTH_POS) & LIS2DW12_FIFO_CTRL_FTH_MASK) +#define LIS2DW12_FIFO_CTRL_FIFO_MODE_BYPASS (0x0U << 5U) +#define LIS2DW12_FIFO_CTRL_FIFO_MODE_FIFO (0x1U << 5U) +#define LIS2DW12_FIFO_CTRL_FIFO_MODE_CONT (0x6U << 5U) + +// FIFO_SAMPLES fields +#define LIS2DW12_FIFO_SAMPLES_DIFF_POS 0U +#define LIS2DW12_FIFO_SAMPLES_DIFF_MASK 0x3FU +#define LIS2DW12_FIFO_SAMPLES_DIFF_GET(val) \ + (((val) & LIS2DW12_FIFO_SAMPLES_DIFF_MASK) >> LIS2DW12_FIFO_SAMPLES_DIFF_POS) +#define LIS2DW12_FIFO_SAMPLES_FIFO_OVR (1U << 6U) +#define LIS2DW12_FIFO_SAMPLES_FIFO_FTH (1U << 7U) + +// WAKE_UP_THS fields +#define LIS2DW12_WAKE_UP_THS_WK_THS_POS 0U +#define LIS2DW12_WAKE_UP_THS_WK_THS_MASK 0x3FU +#define LIS2DW12_WAKE_UP_THS_WK_THS(val) \ + (((val) << LIS2DW12_WAKE_UP_THS_WK_THS_POS) & LIS2DW12_WAKE_UP_THS_WK_THS_MASK) + +// WAKE_UP_DUR fields +#define LIS2DW12_WAKE_UP_DUR_WAKE_DUR_POS 5U +#define LIS2DW12_WAKE_UP_DUR_WAKE_DUR_MASK 0x60U +#define LIS2DW12_WAKE_UP_DUR_WAKE_DUR(val) \ + (((val) << LIS2DW12_WAKE_UP_DUR_WAKE_DUR_POS) & LIS2DW12_WAKE_UP_DUR_WAKE_DUR_MASK) + +// ALL_INT_SRC fields +#define LIS2DW12_ALL_INT_SRC_WU_IA (1U << 1U) + +// CTRL7 fields +#define LIS2DW12_CTRL7_INTERRUPTS_ENABLE (1U << 5U) +#define LIS2DW12_CTRL7_INT2_ON_INT1 (1U << 6U) + +//////////////////////////////////////////////////////////////////////////////// +// Private +//////////////////////////////////////////////////////////////////////////////// + +static bool prv_lis2dw12_write(uint8_t reg, const uint8_t *data, uint16_t len) { + bool ret; + + i2c_use(&LIS2DW12->i2c); + ret = i2c_write_register_block(&LIS2DW12->i2c, reg, len, data); + i2c_release(&LIS2DW12->i2c); + + return ret; +} + +static bool prv_lis2dw12_read(uint8_t reg, uint8_t *data, uint16_t len) { + bool ret; + + i2c_use(&LIS2DW12->i2c); + ret = i2c_read_register_block(&LIS2DW12->i2c, reg, len, data); + i2c_release(&LIS2DW12->i2c); + + return ret; +} + +static int16_t prv_raw_to_s12(const uint8_t *raw) { + uint16_t val; + + val = ((raw[0] >> 4U) & 0xFU) | (raw[1] << 4U); + if (val & 0x0800U) { + val |= 0xF000U; + } + + return (int16_t)val; +} + +static int16_t prv_axis_raw_mg(IMUCoordinateAxis axis, const uint8_t *raw) { + uint8_t offset; + int16_t val; + + offset = LIS2DW12->axis_map[axis]; + + val = LIS2DW12->axis_dir[axis] * + (prv_raw_to_s12(&raw[offset * 2U]) * (int16_t)CONFIG_ACCEL_LIS2DW12_SCALE_MG) / + (int16_t)LIS2DW12_S12_SCALE_RANGE; + + if (LIS2DW12->state->rotated && (axis == AXIS_X || axis == AXIS_Y)) { + val *= -1; + } + + return val; +} + +static void prv_raw_to_mg(const uint8_t *raw, AccelDriverSample *sample) { + sample->x = prv_axis_raw_mg(AXIS_X, raw); + sample->y = prv_axis_raw_mg(AXIS_Y, raw); + sample->z = prv_axis_raw_mg(AXIS_Z, raw); +} + +static uint64_t prv_get_curr_system_time_us(void) { + time_t time_s; + uint16_t time_ms; + + rtc_get_time_ms(&time_s, &time_ms); + + return (((uint64_t)time_s) * 1000 + time_ms) * 1000ULL; +} + +static void prv_lis2dw12_read_samples(uint8_t num_samples) { + uint64_t timestamp_us; + + if (!prv_lis2dw12_read(LIS2DW12_OUT_X_L, LIS2DW12->state->raw_sample_buf, + num_samples * LIS2DW12_SAMPLE_SIZE_BYTES)) { + PBL_LOG_ERR("Failed to read samples"); + return; + } + + timestamp_us = prv_get_curr_system_time_us(); + + AccelDriverSample sample = {0}; + + for (uint8_t i = 0U; i < num_samples; ++i) { + uint8_t *raw; + + raw = &LIS2DW12->state->raw_sample_buf[i * LIS2DW12_SAMPLE_SIZE_BYTES]; + prv_raw_to_mg(raw, &sample); + sample.timestamp_us = timestamp_us + i * LIS2DW12->state->sampling_interval_us; + + accel_cb_new_sample(&sample); + } + + LIS2DW12->state->last_sample = sample; + LIS2DW12->state->last_sample_valid = true; +} + +static bool prv_lis2dw12_enable_fifo(uint8_t num_samples) { + bool ret; + uint8_t val; + + val = LIS2DW12_FIFO_CTRL_FIFO_MODE_BYPASS; + ret = prv_lis2dw12_write(LIS2DW12_FIFO_CTRL, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write FIFO_CTRL register"); + return ret; + } + + val = LIS2DW12_FIFO_CTRL_FTH(num_samples) | LIS2DW12_FIFO_CTRL_FIFO_MODE_CONT; + ret = prv_lis2dw12_write(LIS2DW12_FIFO_CTRL, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write FIFO_CTRL register"); + return ret; + } + + PBL_LOG_DBG("FIFO enabled with threshold %" PRIu8, num_samples); + + return true; +} + +static void prv_lis2dw12_int1_work_handler(void) { + bool ret; + uint8_t val; + bool action_taken = false; + + if (LIS2DW12->state->num_samples > 0U) { + ret = prv_lis2dw12_read(LIS2DW12_FIFO_SAMPLES, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not read FIFO_SAMPLES register"); + return; + } + + if ((val & LIS2DW12_FIFO_SAMPLES_FIFO_OVR) != 0U) { + PBL_LOG_WRN("FIFO overrun detected, re-arming"); + prv_lis2dw12_enable_fifo(LIS2DW12->state->num_samples); + action_taken = true; + } else if ((val & LIS2DW12_FIFO_SAMPLES_FIFO_FTH) != 0U) { + uint8_t samples; + + samples = LIS2DW12_FIFO_SAMPLES_DIFF_GET(val); + if (samples > 0U) { + prv_lis2dw12_read_samples(samples); + action_taken = true; + } + } + } + + if (LIS2DW12->state->shake_detection_enabled) { + ret = prv_lis2dw12_read(LIS2DW12_ALL_INT_SRC, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not read ALL_INT_SRC register"); + return; + } + + if ((val & LIS2DW12_ALL_INT_SRC_WU_IA) != 0U) { + PBL_LOG_DBG("Shake detected"); + // TODO: provide more info about the shake (axis, direction, etc.) or + // refactor shake to be non-dimensional + accel_cb_shake_detected(AXIS_Z, 0); + action_taken = true; + } + } + + if (!action_taken) { + PBL_LOG_WRN("INT1 triggered but no action taken"); + } +} + +static void prv_lis2dw12_int1_irq_handler(bool *should_context_switch) { + LIS2DW12->state->last_int1_tick = rtc_get_ticks(); + accel_offload_work_from_isr(prv_lis2dw12_int1_work_handler, should_context_switch); +} + +static bool prv_configure_odr(uint32_t sampling_interval_us, bool shake_detection_enabled) { + uint8_t val; + bool ret; + + // If shake detection is enabled, ensure a minimum ODR of 12.5Hz (80ms) + if (shake_detection_enabled && (sampling_interval_us == 0UL)) { + sampling_interval_us = 80000UL; + } + + val = LIS2DW12_CTRL1_LP_MODE1 | LIS2DW12_CTRL1_MODE_LP; + + if (sampling_interval_us == 0U) { + val |= LIS2DW12_CTRL1_ODR_PD; + sampling_interval_us = 0UL; + } else if (sampling_interval_us >= 80000UL) { + val |= LIS2DW12_CTRL1_ODR_12HZ5; + sampling_interval_us = 80000UL; + } else if (sampling_interval_us >= 40000UL) { + val |= LIS2DW12_CTRL1_ODR_25HZ; + sampling_interval_us = 40000UL; + } else if (sampling_interval_us >= 20000UL) { + val |= LIS2DW12_CTRL1_ODR_50HZ; + sampling_interval_us = 20000UL; + } else if (sampling_interval_us >= 10000UL) { + val |= LIS2DW12_CTRL1_ODR_100HZ; + sampling_interval_us = 10000UL; + } else { + val |= LIS2DW12_CTRL1_ODR_200HZ; + sampling_interval_us = 5000UL; + } + + PBL_LOG_DBG("Configuring ODR to %" PRIu32 " ms (%" PRIu32 " mHz)", + sampling_interval_us / 1000UL, + sampling_interval_us > 0UL ? 1000000000UL / sampling_interval_us : 0UL); + + ret = prv_lis2dw12_write(LIS2DW12_CTRL1, &val, 1); + if (!ret) { + return ret; + } + + LIS2DW12->state->sampling_interval_us = sampling_interval_us; + + return true; +} + +static bool prv_configure_int1(bool shake_detection_enabled, bool fifo_enabled) { + bool ret; + uint8_t ctrl4; + uint8_t ctrl5; + uint8_t ctrl7; + + ctrl4 = 0U; + ctrl5 = 0U; + + if (shake_detection_enabled) { + ctrl4 |= LIS2DW12_CTRL4_INT1_PAD_CTRL_INT1_WU; + } + + if (fifo_enabled) { + ctrl4 |= LIS2DW12_CTRL4_INT1_PAD_CTRL_INT1_FTH; + ctrl5 |= LIS2DW12_CTRL5_INT2_PAD_CTRL_INT2_OVR; + } + + ret = prv_lis2dw12_write(LIS2DW12_CTRL4_INT1_PAD_CTRL, &ctrl4, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL4_INT1_PAD_CTRL register"); + return ret; + } + + PBL_LOG_DBG("INT1 configured: %02" PRIx8, ctrl4); + + ret = prv_lis2dw12_write(LIS2DW12_CTRL5_INT2_PAD_CTRL, &ctrl5, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL5_INT2_PAD_CTRL register"); + return ret; + } + + PBL_LOG_DBG("INT2 configured: %02" PRIx8, ctrl5); + + ctrl7 = (ctrl4 == 0U && ctrl5 == 0U) + ? 0U + : LIS2DW12_CTRL7_INTERRUPTS_ENABLE | LIS2DW12_CTRL7_INT2_ON_INT1; + ret = prv_lis2dw12_write(LIS2DW12_CTRL7, &ctrl7, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL7 register"); + return ret; + } + + PBL_LOG_DBG("Enabled interrupts: %u", ctrl7 != 0U); + + return true; +} + +static void prv_int1_wdt_work_cb(void) { + RtcTicks now_tick = rtc_get_ticks(); + RtcTicks ticks_since_last_int1 = now_tick - LIS2DW12->state->last_int1_tick; + uint32_t ms_since_last_int1 = (ticks_since_last_int1 * 1000) / RTC_TICKS_HZ; + + if (ms_since_last_int1 >= LIS2DW12->state->int1_period_ms) { + bool ret; + uint8_t val; + + PBL_LOG_WRN("INT1 not received in %" PRIu32 " ms", ms_since_last_int1); + + // Re-enable FIFO, and clear any event INT source + ret = prv_lis2dw12_enable_fifo(LIS2DW12->state->num_samples); + if (!ret) { + PBL_LOG_ERR("Failed to re-enable FIFO"); + return; + } + + ret = prv_lis2dw12_read(LIS2DW12_ALL_INT_SRC, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not read ALL_INT_SRC register"); + return; + } + } +} + +static void prv_int1_wdt_cb(void *data) { + accel_offload_work(prv_int1_wdt_work_cb); +} + +//////////////////////////////////////////////////////////////////////////////// +// Accelerometer interface +//////////////////////////////////////////////////////////////////////////////// + +void accel_init(void) { + bool ret; + uint8_t val; + + // Check device ID + ret = prv_lis2dw12_read(LIS2DW12_WHO_AM_I, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not read WHO_AM_I register"); + return; + } + + if (val != LIS2DW12_WHO_AM_I_VAL) { + PBL_LOG_ERR("Unexpected id: 0x%02X!=0x%02X", val, LIS2DW12_WHO_AM_I_VAL); + return; + } + + // Perform a software reset (so we can rely on defaults) + val = LIS2DW12_CTRL2_SOFT_RESET; + ret = prv_lis2dw12_write(LIS2DW12_CTRL2, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL2 register"); + return; + } + + delay_us(LIS2DW12_RESET_TIME_US); + + do { + ret = prv_lis2dw12_read(LIS2DW12_CTRL2, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not read CTRL2 register"); + return; + } + } while ((val & LIS2DW12_CTRL2_BOOT) != 0U); + + // Disable ADDR pull-up if requested + // NOTE: This is an undocumented register (provided by FAE) +#ifdef CONFIG_ACCEL_LIS2DW12_DISABLE_ADDR_PULLUP + ret = prv_lis2dw12_read(LIS2DW12_UNDOC, &val, 1); + if (!ret) { + PBL_LOG_ERR("Failed to read LIS2DW12 register 0x17"); + return; + } + + val |= LIS2DW12_UNDOC_ADDR_PULLUP_DIS; + ret = prv_lis2dw12_write(LIS2DW12_UNDOC, &val, 1); + if (!ret) { + PBL_LOG_ERR("Failed to write LIS2DW12 register 0x17"); + return; + } +#endif + + // Single-data conversion via SLP_MODE_1, latch function IRQ + val = LIS2DW12_CTRL3_SLP_MODE_SEL_SLP_MODE_1 | LIS2DW12_CTRL3_LIR; + ret = prv_lis2dw12_write(LIS2DW12_CTRL3, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL3 register"); + return; + } + + // Configure scale + switch (CONFIG_ACCEL_LIS2DW12_SCALE_MG) { + case 2000U: + val = LIS2DW12_CTRL6_FS_2G; + break; + case 4000U: + val = LIS2DW12_CTRL6_FS_4G; + break; + case 8000U: + val = LIS2DW12_CTRL6_FS_8G; + break; + case 16000U: + val = LIS2DW12_CTRL6_FS_16G; + break; + default: + PBL_LOG_ERR("Invalid scale: %d", CONFIG_ACCEL_LIS2DW12_SCALE_MG); + return; + } + + ret = prv_lis2dw12_write(LIS2DW12_CTRL6, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL6 register"); + return; + } + + // Configure wake-up threshold defaults + val = LIS2DW12_WAKE_UP_DUR_WAKE_DUR(CONFIG_ACCEL_LIS2DW12_WK_DUR_DEFAULT); + ret = prv_lis2dw12_write(LIS2DW12_WAKE_UP_DUR, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write WAKE_UP_DUR register"); + return; + } + + val = LIS2DW12_WAKE_UP_THS_WK_THS(CONFIG_ACCEL_LIS2DW12_WK_THS_DEFAULT); + ret = prv_lis2dw12_write(LIS2DW12_WAKE_UP_THS, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write WAKE_UP_THS register"); + return; + } + + LIS2DW12->state->wk_ths_curr = CONFIG_ACCEL_LIS2DW12_WK_THS_DEFAULT; + + // Enable INT1 external interrupt + exti_configure_pin(LIS2DW12->int1, ExtiTrigger_Rising, prv_lis2dw12_int1_irq_handler); + exti_enable(LIS2DW12->int1); + + LIS2DW12->state->int1_wdt_timer.cb = prv_int1_wdt_cb; + LIS2DW12->state->initialized = true; +} + +void accel_power_up(void) { + // Driver automatically keeps the sensor active as needed +} + +void accel_power_down(void) { + // Driver automatically keeps the sensor in lowest power mode +} + +uint32_t accel_set_sampling_interval(uint32_t interval_us) { + if (!LIS2DW12->state->initialized) { + // Just pretend we can achieve any requested interval + LIS2DW12->state->sampling_interval_us = interval_us; + } else { + // FIXME: we should technically stop and drain the FIFO here, otherwise + // we may report existing samples in the FIFO buffer with an incorrect timestamp + + if (!prv_configure_odr(interval_us, LIS2DW12->state->shake_detection_enabled)) { + PBL_LOG_ERR("Could not configure ODR"); + } + } + + PBL_LOG_DBG("Set sampling interval to %" PRIu32 " us", + LIS2DW12->state->sampling_interval_us); + + return LIS2DW12->state->sampling_interval_us; +} + +uint32_t accel_get_sampling_interval(void) { + return LIS2DW12->state->sampling_interval_us; +} + +void accel_set_num_samples(uint32_t num_samples) { + bool ret; + uint8_t val; + + if (!LIS2DW12->state->initialized) { + return; + } + + // Limit to FIFO threshold + if (num_samples > CONFIG_ACCEL_LIS2DW12_FIFO_THRESHOLD) { + num_samples = CONFIG_ACCEL_LIS2DW12_FIFO_THRESHOLD; + } + + // Disable all INT1 before changing FIFO threshold + prv_configure_int1(false, false); + + if (num_samples == 0U) { + // Bypass FIFO (disable) + val = LIS2DW12_FIFO_CTRL_FIFO_MODE_BYPASS; + if (!prv_lis2dw12_write(LIS2DW12_FIFO_CTRL, &val, 1)) { + PBL_LOG_ERR("Could not write FIFO_CTRL register"); + } + + regular_timer_remove_callback(&LIS2DW12->state->int1_wdt_timer); + } else { + // FIXME: we should ideally drain the FIFO here to not discard existing samples + + // Configure FIFO in CONT mode with threshold + ret = prv_lis2dw12_enable_fifo((uint8_t)num_samples); + if (!ret) { + PBL_LOG_ERR("Could not enable FIFO"); + return; + } + + LIS2DW12->state->last_sample_valid = false; + LIS2DW12->state->last_int1_tick = rtc_get_ticks(); + LIS2DW12->state->int1_period_ms = (LIS2DW12->state->sampling_interval_us * num_samples) / 1000; + regular_timer_add_multisecond_callback(&LIS2DW12->state->int1_wdt_timer, + DIVIDE_CEIL(LIS2DW12->state->int1_period_ms, 1000UL)); + } + + // Re-configure INT1 + ret = prv_configure_int1(LIS2DW12->state->shake_detection_enabled, num_samples > 0U); + if (!ret) { + PBL_LOG_ERR("Could not configure INT1"); + return; + } + + LIS2DW12->state->num_samples = num_samples; + + PBL_LOG_DBG("Set number of samples to %" PRIu32, num_samples); +} + +int accel_peek(AccelDriverSample *data) { + int err = 0; + bool ret; + uint8_t ctrl1; + uint8_t ctrl1_bck; + uint8_t ctrl3; + uint8_t status; + uint8_t raw[LIS2DW12_SAMPLE_SIZE_BYTES]; + + if (!LIS2DW12->state->initialized) { + return E_ERROR; + } + + // If sampling is active, return the last obtained sample + if (LIS2DW12->state->num_samples > 0U) { + if (!LIS2DW12->state->last_sample_valid) { + return E_ERROR; + } + *data = LIS2DW12->state->last_sample; + return 0; + } + + // Save CTRL1 + ret = prv_lis2dw12_read(LIS2DW12_CTRL1, &ctrl1_bck, 1); + if (!ret) { + PBL_LOG_ERR("Could not read CTRL1 register"); + return E_ERROR; + } + + // Configure single mode, ODR@50Hz (recommended ODR, see DT0102 rev1) + ctrl1 = LIS2DW12_CTRL1_MODE_SINGLE | LIS2DW12_CTRL1_ODR_50HZ; + ret = prv_lis2dw12_write(LIS2DW12_CTRL1, &ctrl1, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL1 register"); + return E_ERROR; + } + + // Trigger single measurement by setting SLP_MODE_1 bit + ret = prv_lis2dw12_read(LIS2DW12_CTRL3, &ctrl3, 1); + if (!ret) { + PBL_LOG_ERR("Could not read CTRL3 register"); + return E_ERROR; + } + + ctrl3 |= LIS2DW12_CTRL3_SLP_MODE_1; + ret = prv_lis2dw12_write(LIS2DW12_CTRL3, &ctrl3, 1); + if (!ret) { + PBL_LOG_ERR("Could not write CTRL3 register"); + return E_ERROR; + } + + // Poll for data ready (timeout after 100ms, ~5x the expected 20ms at 50Hz ODR) + uint32_t elapsed_ms = 0; + do { + ret = prv_lis2dw12_read(LIS2DW12_STATUS, &status, 1); + if (!ret) { + PBL_LOG_ERR("Could not read STATUS register"); + err = E_ERROR; + goto end; + } + if ((status & LIS2DW12_STATUS_DRDY) == 0U) { + if (elapsed_ms >= LIS2DW12_DRDY_POLL_TIMEOUT_MS) { + PBL_LOG_ERR("DRDY timeout after %" PRIu32 " ms", elapsed_ms); + err = E_ERROR; + goto end; + } + psleep(LIS2DW12_DRDY_POLL_DELAY_MS); + elapsed_ms += LIS2DW12_DRDY_POLL_DELAY_MS; + } + } while ((status & LIS2DW12_STATUS_DRDY) == 0U); + + // Read sample + ret = prv_lis2dw12_read(LIS2DW12_OUT_X_L, raw, sizeof(raw)); + if (!ret) { + PBL_LOG_ERR("Failed to read sample"); + err = E_ERROR; + goto end; + } + + // Convert to mg and populate timestamp + prv_raw_to_mg(raw, data); + data->timestamp_us = prv_get_curr_system_time_us(); + +end: + // Restore CTRL1 (back to previous state, e.g. power-down or shake ODR) + (void)prv_lis2dw12_write(LIS2DW12_CTRL1, &ctrl1_bck, 1); + + return err; +} + +void accel_enable_shake_detection(bool on) { + bool ret; + + if (!LIS2DW12->state->initialized) { + return; + } + + // Configure ODR (use current interval, will be adjusted if < 12.5Hz) + ret = prv_configure_odr(LIS2DW12->state->sampling_interval_us, on); + if (!ret) { + PBL_LOG_ERR("Could not configure ODR"); + return; + } + + // Configure INT1 + ret = prv_configure_int1(on, LIS2DW12->state->num_samples > 0U); + if (!ret) { + PBL_LOG_ERR("Could not configure INT1"); + return; + } + + LIS2DW12->state->shake_detection_enabled = on; + + PBL_LOG_DBG("%s shake detection", on ? "Enabled" : "Disabled"); +} + +bool accel_get_shake_detection_enabled(void) { + return LIS2DW12->state->shake_detection_enabled; +} + +void accel_set_shake_sensitivity_high(bool sensitivity_high) { + bool ret; + uint8_t val; + + if (!LIS2DW12->state->initialized) { + return; + } + + val = LIS2DW12_WAKE_UP_THS_WK_THS(sensitivity_high ? CONFIG_ACCEL_LIS2DW12_WK_THS_MIN + : LIS2DW12->state->wk_ths_curr); + ret = prv_lis2dw12_write(LIS2DW12_WAKE_UP_THS, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write WAKE_UP_THS register"); + return; + } + + PBL_LOG_DBG("Configured shake sensitivity to %s", + sensitivity_high ? "high" : "normal"); +} + +void accel_set_shake_sensitivity_percent(uint8_t percent) { + bool ret; + uint8_t val; + uint8_t raw; + + if (!LIS2DW12->state->initialized) { + return; + } + + // Reverse mapping: 0 = max sensitivity (MIN threshold), 100 = min sensitivity (MAX threshold) + // [0, 100] -> [wk_ths_max, wk_ths_min] + raw = CONFIG_ACCEL_LIS2DW12_WK_THS_MAX - + (percent * (CONFIG_ACCEL_LIS2DW12_WK_THS_MAX - CONFIG_ACCEL_LIS2DW12_WK_THS_MIN)) / 100U; + + val = LIS2DW12_WAKE_UP_THS_WK_THS(raw); + ret = prv_lis2dw12_write(LIS2DW12_WAKE_UP_THS, &val, 1); + if (!ret) { + PBL_LOG_ERR("Could not write WAKE_UP_THS register"); + return; + } + + LIS2DW12->state->wk_ths_curr = raw; + + PBL_LOG_DBG("Configured shake sensitivity to %" PRIu8 " (%" PRIu8 ")", percent, raw); +} + +void accel_enable_double_tap_detection(bool on) { + // TODO: Implement + PBL_LOG_WRN("Double-tap detection not implemented"); +} + +bool accel_get_double_tap_detection_enabled(void) { + // TODO: Implement + return false; +} + +void accel_set_rotated(bool rotated) { + LIS2DW12->state->rotated = rotated; + PBL_LOG_DBG("Set rotated state to %s", rotated ? "true" : "false"); +} diff --git a/src/fw/drivers/imu/lis2dw12/lis2dw12.h b/src/fw/drivers/imu/lis2dw12/lis2dw12.h new file mode 100644 index 0000000000..354d47d4a2 --- /dev/null +++ b/src/fw/drivers/imu/lis2dw12/lis2dw12.h @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "drivers/accel.h" +#include "drivers/rtc.h" +#include "pbl/services/regular_timer.h" + +#define LIS2DW12_FIFO_SIZE 32 +#define LIS2DW12_SAMPLE_SIZE_BYTES 6 + +typedef struct LIS2DW12State { + bool initialized; + bool rotated; + bool shake_detection_enabled; + bool double_tap_detection_enabled; + uint32_t sampling_interval_us; + uint8_t num_samples; + uint8_t raw_sample_buf[LIS2DW12_FIFO_SIZE * LIS2DW12_SAMPLE_SIZE_BYTES]; + RegularTimerInfo int1_wdt_timer; + RtcTicks last_int1_tick; + uint32_t int1_period_ms; + uint32_t num_recoveries; + uint8_t wk_ths_curr; + AccelDriverSample last_sample; + bool last_sample_valid; +} LIS2DW12State; + +typedef struct LIS2DW12Config { + //! Driver state + LIS2DW12State *state; + //! I2C slave port configuration + I2CSlavePort i2c; + //! INT1 EXTI configuration + ExtiConfig int1; + //! Axis mapping (0: X, 1: Y, 2: Z) + uint8_t axis_map[3]; + //! Axis direction (1 upside, -1 downside) + int8_t axis_dir[3]; +} LIS2DW12Config; \ No newline at end of file diff --git a/src/fw/drivers/imu/lis3dh/config.c b/src/fw/drivers/imu/lis3dh/config.c deleted file mode 100644 index 2721140536..0000000000 --- a/src/fw/drivers/imu/lis3dh/config.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "lis3dh.h" -#include "registers.h" - -#include "drivers/i2c.h" -#include "drivers/legacy/accel.h" -#include "system/logging.h" -#include "util/math.h" -#include "util/size.h" - -//! @file accel_config.c -//! procedures for dealing with I2C communication with the accel - -struct I2CCommand { - uint8_t register_address; - uint8_t value; -}; - -// -// Boiler plate functions for talking over i2c -// - -static uint8_t prv_read_reg(uint8_t address) { - uint8_t reg; - lis3dh_read(address, 1, ®); - - return (reg); -} - -static bool prv_write_reg(uint8_t address, uint8_t value) { - return (lis3dh_write(address, 1, &value)); -} - -static bool send_i2c_commands(struct I2CCommand* commands, int num_commands) { - for (int i = 0; i < num_commands; ++i) { - bool result = prv_write_reg(commands[i].register_address, commands[i].value); - - if (!result) { - return false; - } - } - return true; -} - -void lis3dh_enable_fifo(void) { - uint8_t ctrl_reg5 = prv_read_reg(LIS3DH_CTRL_REG5); - ctrl_reg5 |= FIFO_EN; - prv_write_reg(LIS3DH_CTRL_REG5, ctrl_reg5); -} - -void lis3dh_disable_fifo(void) { - uint8_t ctrl_reg5 = prv_read_reg(LIS3DH_CTRL_REG5); - ctrl_reg5 &= ~FIFO_EN; - prv_write_reg(LIS3DH_CTRL_REG5, ctrl_reg5); -} - -bool lis3dh_is_fifo_enabled(void) { - uint8_t ctrl_reg5 = prv_read_reg(LIS3DH_CTRL_REG5); - return (ctrl_reg5 & FIFO_EN); -} - -void lis3dh_disable_click(void) { - uint8_t ctrl_reg3 = prv_read_reg(LIS3DH_CTRL_REG3); - ctrl_reg3 &= ~I1_CLICK; - prv_write_reg(LIS3DH_CTRL_REG3, ctrl_reg3); -} - -void lis3dh_enable_click(void) { - uint8_t ctrl_reg3 = prv_read_reg(LIS3DH_CTRL_REG3); - ctrl_reg3 |= I1_CLICK; - prv_write_reg(LIS3DH_CTRL_REG3, ctrl_reg3); -} - -// -// Accel config Getter/Setters -// - -void lis3dh_set_interrupt_axis(AccelAxisType axis, bool double_click) { - // get the current state of the registers - uint8_t reg_1 = prv_read_reg(LIS3DH_CTRL_REG1); - - // clear the axis-enable bits - reg_1 = reg_1 & ~(0x1 | 0x2 | 0x4); - uint8_t click_cfg = 0; - - switch (axis) { - case ACCEL_AXIS_X: - reg_1 |= 0x01; - click_cfg = 0x01; - break; - case ACCEL_AXIS_Y: - reg_1 |= 0x02; - click_cfg = 0x04; - break; - case ACCEL_AXIS_Z: - reg_1 |= 0x04; - click_cfg = 0x10; - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Unknown axis"); - } - - if (double_click) { - click_cfg <<= 1; - } - - if ((!prv_write_reg(LIS3DH_CTRL_REG1, reg_1)) - || (!prv_write_reg(LIS3DH_CLICK_CFG, click_cfg))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to write axis selection"); - } -} - -uint8_t lis3dh_get_click_window() { - return prv_read_reg(LIS3DH_TIME_WINDOW); -} -void lis3dh_set_click_window(uint8_t window) { - if (!prv_write_reg(LIS3DH_TIME_WINDOW, MIN(window, LIS3DH_MAX_CLICK_WINDOW))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to write click latency"); - } -} - -uint8_t lis3dh_get_click_latency() { - return prv_read_reg(LIS3DH_TIME_LATENCY); -} -void lis3dh_set_click_latency(uint8_t latency) { - if (!prv_write_reg(LIS3DH_TIME_LATENCY, MIN(latency, LIS3DH_MAX_CLICK_LATENCY))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to write click latency"); - } -} - -uint8_t lis3dh_get_interrupt_threshold() { - return prv_read_reg(LIS3DH_CLICK_THS); -} -void lis3dh_set_interrupt_threshold(uint8_t threshold) { - if (!prv_write_reg(LIS3DH_CLICK_THS, MIN(threshold, LIS3DH_MAX_THRESHOLD))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to set interrupt threshold"); - } -} - -uint8_t lis3dh_get_interrupt_time_limit() { - return prv_read_reg(LIS3DH_TIME_LIMIT); -} -void lis3dh_set_interrupt_time_limit(uint8_t time_limit) { - if (!prv_write_reg(LIS3DH_TIME_LIMIT, MIN(time_limit, LIS3DH_MAX_TIME_LIMIT))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to set interrupt time limit"); - } -} - -bool lis3dh_set_fifo_wtm(uint8_t wtm) { - uint8_t fifo_ctrl_reg = prv_read_reg(LIS3DH_FIFO_CTRL_REG); - fifo_ctrl_reg &= ~THR_MASK; - fifo_ctrl_reg |= (wtm & THR_MASK); - - return (prv_write_reg(LIS3DH_FIFO_CTRL_REG, fifo_ctrl_reg)); -} - -uint8_t lis3dh_get_fifo_wtm(void) { - uint8_t fifo_ctrl_reg = prv_read_reg(LIS3DH_FIFO_CTRL_REG); - return (fifo_ctrl_reg & THR_MASK); -} - -AccelSamplingRate accel_get_sampling_rate(void) { - uint8_t odr = ODR_MASK & prv_read_reg(LIS3DH_CTRL_REG1); - - if (odr == (ODR2 | ODR0)) { - return (ACCEL_SAMPLING_100HZ); - } else if (odr == ODR2) { - return (ACCEL_SAMPLING_50HZ); - } else if (odr == (ODR1 | ODR0)) { - return (ACCEL_SAMPLING_25HZ); - } else if (odr == (ODR1)) { - return (ACCEL_SAMPLING_10HZ); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Unrecognized ODR value %d", odr); - return (0); - } -} - -bool accel_set_sampling_rate(AccelSamplingRate rate) { - uint8_t odr; - - switch (rate) { - case ACCEL_SAMPLING_100HZ: - odr = ODR2 | ODR0; - break; - case ACCEL_SAMPLING_50HZ: - odr = ODR2; - break; - case ACCEL_SAMPLING_25HZ: - odr = ODR1 | ODR0; - break; - case ACCEL_SAMPLING_10HZ: - odr = ODR1; - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Unsupported sampling rate %d", rate); - return (false); - } - - uint8_t ctrl_reg_1 = prv_read_reg(LIS3DH_CTRL_REG1); - ctrl_reg_1 &= ~ODR_MASK; - ctrl_reg_1 |= (odr & ODR_MASK); - //TODO: fix hack below (enabling axes after lis3dh_power_down) - ctrl_reg_1 |= (Xen | Yen | Zen); //enable x, y and z axis - bool res = prv_write_reg(LIS3DH_CTRL_REG1, ctrl_reg_1); - - // Update the click limit based on sampling frequency - uint8_t time_limit = rate * LIS3DH_TIME_LIMIT_MULT / LIS3DH_TIME_LIMIT_DIV; - lis3dh_set_interrupt_time_limit(time_limit); - PBL_LOG(LOG_LEVEL_DEBUG, "setting click time limit to 0x%x", - lis3dh_get_interrupt_time_limit()); - - // Update click latency - uint8_t time_latency = rate * LIS3DH_TIME_LATENCY_MULT / LIS3DH_TIME_LATENCY_DIV; - lis3dh_set_click_latency(time_latency); - PBL_LOG(LOG_LEVEL_DEBUG, "setting click time latency to 0x%x", - lis3dh_get_click_latency()); - - // Update click window - uint32_t time_window = rate * LIS3DH_TIME_WINDOW_MULT / LIS3DH_TIME_WINDOW_DIV; - time_window = MIN(time_window, 0xff); - lis3dh_set_click_window(time_window); - PBL_LOG(LOG_LEVEL_DEBUG, "setting click time window to 0x%x", - lis3dh_get_click_window()); - - - return (res); -} - -Lis3dhScale accel_get_scale(void) { - uint8_t fs = FS_MASK & prv_read_reg(LIS3DH_CTRL_REG4); - - if (fs == (FS0 | FS1)) { - return (LIS3DH_SCALE_16G); - } else if (fs == FS1) { - return (LIS3DH_SCALE_8G); - } else if (fs == FS0) { - return (LIS3DH_SCALE_4G); - } else if (fs == 0) { - return (LIS3DH_SCALE_2G); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Unrecognized FS value %d", fs); - return (LIS3DH_SCALE_UNKNOWN); - } -} - -bool accel_set_scale(Lis3dhScale scale) { - uint8_t fs; - - switch (scale) { - case LIS3DH_SCALE_16G: - fs = (FS0 | FS1); - break; - case LIS3DH_SCALE_8G: - fs = FS1; - break; - case LIS3DH_SCALE_4G: - fs = FS0; - break; - case LIS3DH_SCALE_2G: - fs = 0; - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Unsupported scale %d", scale); - return (false); - } - - uint8_t ctrl_reg_4 = prv_read_reg(LIS3DH_CTRL_REG4); - ctrl_reg_4 &= ~FS_MASK; - ctrl_reg_4 |= (fs & FS_MASK); - bool res = prv_write_reg(LIS3DH_CTRL_REG4, ctrl_reg_4); - - lis3dh_set_interrupt_threshold(scale * LIS3DH_THRESHOLD_MULT - / LIS3DH_THRESHOLD_DIV); - PBL_LOG(LOG_LEVEL_DEBUG, "setting click threshold to 0x%x", - lis3dh_get_interrupt_threshold()); - return (res); -} - -bool lis3dh_set_fifo_mode(uint8_t mode) { - uint8_t fifo_ctrl_reg = prv_read_reg(LIS3DH_FIFO_CTRL_REG); - fifo_ctrl_reg &= ~MODE_MASK; - fifo_ctrl_reg |= (mode & MODE_MASK); - return (prv_write_reg(LIS3DH_FIFO_CTRL_REG, fifo_ctrl_reg)); -} - -uint8_t lis3dh_get_fifo_mode(void) { - uint8_t fifo_ctrl_reg = prv_read_reg(LIS3DH_FIFO_CTRL_REG); - return (fifo_ctrl_reg & MODE_MASK); -} - -//! Configure the accel to run "Self Test 0". See S3.2.2 of the accel datasheet for more information -bool lis3dh_enter_self_test_mode(SelfTestMode mode) { - uint8_t reg4 = 0x8; - - switch (mode) { - case SELF_TEST_MODE_ONE: - reg4 |= 0x2; - break; - case SELF_TEST_MODE_TWO: - reg4 |= (0x2 | 0x4); - break; - default: - break; - } - - struct I2CCommand test_mode_config[] = { - { LIS3DH_CTRL_REG1, 0x9f }, - { LIS3DH_CTRL_REG3, 0x00 }, - { LIS3DH_CTRL_REG4, reg4 } - }; - - return send_i2c_commands(test_mode_config, ARRAY_LENGTH(test_mode_config)); -} - -void lis3dh_exit_self_test_mode(void) { - lis3dh_config_set_defaults(); -} - -// -// Boot-time config -// - -//! Ask the accel for a 8-bit value that's programmed into the IC at the -//! factory. Useful as a sanity check to make sure everything came up properly. -bool lis3dh_sanity_check(void) { - uint8_t whoami = prv_read_reg(LIS3DH_WHO_AM_I); - PBL_LOG(LOG_LEVEL_DEBUG, "Read accel whomai byte 0x%x, expecting 0x%x", whoami, LIS3DH_WHOAMI_BYTE); - return (whoami == LIS3DH_WHOAMI_BYTE); -} - -bool lis3dh_config_set_defaults() { - // Follow the startup sequence from AN3308 - struct I2CCommand accel_init_commands[] = { - { LIS3DH_CTRL_REG1, (ODR1 | ODR0 | Zen | Yen | Xen) }, // 25MHz, Enable X,Y,Z Axes - { LIS3DH_CTRL_REG2, 0x00 }, - { LIS3DH_CTRL_REG3, I1_WTM }, // FIFO Watermark on INT1 - { LIS3DH_CTRL_REG4, (BDU | FS0 | HR) }, // Block Read, +/- 4g sensitivity - { LIS3DH_CTRL_REG6, I2_CLICK }, // Click on INT2 - - { LIS3DH_INT1_THS, 0x20 }, // intertial threshold (MAX 0x7f) - { LIS3DH_INT1_DURATION, 0x10 }, // interrupt duration (units of 1/(update frequency) [See CTRL_REG1]) - { LIS3DH_INT1_CFG, 0x00 }, // no inertial interrupts - - // click threshold (MAX 0x7f) - { LIS3DH_CLICK_THS, LIS3DH_SCALE_4G * LIS3DH_THRESHOLD_MULT - / LIS3DH_THRESHOLD_DIV }, - - // click time limit (units of 1/(update frequency) [See CTRL_REG1]) - { LIS3DH_TIME_LIMIT, ACCEL_DEFAULT_SAMPLING_RATE * LIS3DH_TIME_LIMIT_MULT - / LIS3DH_TIME_LIMIT_DIV}, - - { LIS3DH_CLICK_CFG, (XS | YS | ZS) }, // single click detection on the X axis - - { LIS3DH_FIFO_CTRL_REG, MODE_BYPASS | 0x19 }, // BYPASS MODE and 25 samples per interrupt - - // time latency, ie "debounce time" after the first of a double click - // (units of 1/(update frequency) [See CTRL_REG1]) - { LIS3DH_TIME_LATENCY, ACCEL_DEFAULT_SAMPLING_RATE * LIS3DH_TIME_LATENCY_MULT - / LIS3DH_TIME_LATENCY_DIV }, - - // max time allowed between clicks for a double click (end to start) - // (units of 1/(update frequency) [See CTRL_REG1]) - { LIS3DH_TIME_WINDOW, ACCEL_DEFAULT_SAMPLING_RATE * LIS3DH_TIME_WINDOW_MULT - / LIS3DH_TIME_WINDOW_DIV} - }; - - if (!send_i2c_commands(accel_init_commands, ARRAY_LENGTH(accel_init_commands))) { - accel_stop(); - PBL_LOG(LOG_LEVEL_WARNING, "Failed to initialize accelerometer"); - return false; - } - - return true; -} diff --git a/src/fw/drivers/imu/lis3dh/lis3dh.c b/src/fw/drivers/imu/lis3dh/lis3dh.c deleted file mode 100644 index 9e2a64f037..0000000000 --- a/src/fw/drivers/imu/lis3dh/lis3dh.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "lis3dh.h" - -#include "board/board.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/legacy/accel.h" -#include "drivers/periph_config.h" -#include "drivers/vibe.h" -#include "kernel/events.h" -#include "pebble_errors.h" -#include "registers.h" -#include "services/common/accel_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/vibe_pattern.h" -#include "services/imu/units.h" -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" -#include "util/size.h" -#include "kernel/util/sleep.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -#include -#include -#include - -#define ACCEL_MAX_IDLE_DELTA 100 - -// State -static bool s_initialized = false; -static bool s_running = false; -static const Lis3dhScale s_accel_scale = LIS3DH_SCALE_4G; -static PebbleMutex * s_accel_mutex; -static uint64_t s_latest_timestamp; -static uint8_t s_pending_accel_event = false; -static bool s_is_idle = false; -static AccelRawData s_last_analytics_position; -static AccelRawData s_latest_reading; - -// Buffer for holding the accel data -static SharedCircularBuffer s_buffer; -static uint8_t s_buffer_storage[50*sizeof(AccelRawData)]; // 400 bytes (~1s of data at 50Hz) - -static void lis3dh_IRQ1_handler(bool *should_context_switch); -static void lis3dh_IRQ2_handler(bool *should_context_switch); - -static void prv_accel_configure_interrupts(void) { - exti_configure_pin(BOARD_CONFIG_ACCEL.accel_ints[0], ExtiTrigger_Rising, lis3dh_IRQ1_handler); - exti_configure_pin(BOARD_CONFIG_ACCEL.accel_ints[1], ExtiTrigger_Rising, lis3dh_IRQ2_handler); -} - -static void disable_accel_interrupts(void) { - for (unsigned int i = 0; i < ARRAY_LENGTH(BOARD_CONFIG_ACCEL.accel_ints); i++) { - exti_disable(BOARD_CONFIG_ACCEL.accel_ints[i]); - } -} - -static void enable_accel_interrupts(void) { - for (unsigned int i = 0; i < ARRAY_LENGTH(BOARD_CONFIG_ACCEL.accel_ints); i++) { - exti_enable(BOARD_CONFIG_ACCEL.accel_ints[i]); - } -} - -static void clear_accel_interrupts(void) { - for (unsigned int i = 0; i < ARRAY_LENGTH(BOARD_CONFIG_ACCEL.accel_ints); i++) { - EXTI_ClearFlag(BOARD_CONFIG_ACCEL.accel_ints[i].exti_line); - EXTI_ClearITPendingBit(BOARD_CONFIG_ACCEL.accel_ints[i].exti_line); - } -} - - -static int16_t raw_to_mgs(int16_t raw, Lis3dhScale scale) { - int16_t mgs_per_digit; - switch (scale) { - case LIS3DH_SCALE_2G: - mgs_per_digit = 1; - break; - case LIS3DH_SCALE_4G: - mgs_per_digit = 2; - break; - case LIS3DH_SCALE_8G: - mgs_per_digit = 4; - break; - case LIS3DH_SCALE_16G: - mgs_per_digit = 12; - break; - default: - WTF; - } - - // least significant 4 bits need to be removed - return ((raw >> 4) * mgs_per_digit); -} - -static int16_t get_axis_data(AccelAxisType axis, uint8_t *raw_data) { - // each sample is 2 bytes for each axis - int offset = 2 * BOARD_CONFIG_ACCEL.accel_config.axes_offsets[axis]; - int invert = BOARD_CONFIG_ACCEL.accel_config.axes_inverts[axis]; - int16_t raw = (((int16_t)raw_data[offset + 1]) << 8) | raw_data[offset]; - int16_t converted = (invert ? -1 : 1) * raw_to_mgs(raw, s_accel_scale); - return (converted); -} - -// Simple read register command with no error handling -static bool prv_read_register(uint8_t register_address, uint8_t *result) { - return i2c_read_register(I2C_LIS3DH, register_address, result); -} - -// Simple write register command with no error handling -static bool prv_write_register(uint8_t register_address, uint8_t value) { - return i2c_write_register(I2C_LIS3DH, register_address, value); -} - -static void prv_clear_fifo(void) { - // Use I2C calls instead of accel wrappers to avoid recursion (reset called from lis3dh_read/accel_write) - uint8_t mode; - if (!prv_read_register(LIS3DH_FIFO_CTRL_REG, &mode)) { - return; - } - if (mode != MODE_BYPASS) { - uint8_t fifo_ctrl_reg = mode & ~MODE_MASK; - fifo_ctrl_reg |= (MODE_BYPASS & MODE_MASK); - if (!prv_write_register(LIS3DH_FIFO_CTRL_REG, fifo_ctrl_reg)) { - return; - } - if (!prv_write_register(LIS3DH_FIFO_CTRL_REG, mode)) { - return; - } - } - -} - -static void prv_reset(void) { - lis3dh_lock(); - if (i2c_bitbang_recovery(I2C_LIS3DH)) { - prv_clear_fifo(); - } - lis3dh_unlock(); - analytics_inc(ANALYTICS_DEVICE_METRIC_ACCEL_RESET_COUNT, AnalyticsClient_System); -} - -bool lis3dh_read(uint8_t register_address, uint8_t read_size, uint8_t *buffer) { - bool running = s_running; - - if (!running) { - if (!accel_start()) { - // couldn't start the accel - return (false); - } - } - - if (!i2c_read_register_block(I2C_LIS3DH, register_address, read_size, buffer)) { - prv_reset(); - return (false); - } - - if (!running) { - accel_stop(); - } - - return (true); -} - -bool lis3dh_write(uint8_t address, uint8_t write_size, const uint8_t *buffer) { - bool running = accel_running(); - - if (!running) { - if (!accel_start()) { - // couldn't start the accel - return (false); - } - } - - if (!i2c_write_register_block(I2C_LIS3DH, address, write_size, buffer)) { - prv_reset(); - return (false); - } - - if (!running) { - accel_stop(); - } - - return true; -} - - -AccelRawData s_accel_data; -void accel_get_last_data(AccelRawData* data) { - *data = s_accel_data; -} - -void accel_get_data(AccelRawData* data, int num_samples) { - if (!s_running) { - PBL_LOG(LOG_LEVEL_ERROR, "Accel Not Running"); - return; - } - - // accel output registers have adjacent addresses - // MSB enables address auto-increment - int num_bytes = 6 * num_samples; - - // Overflow bit doesn't get cleared until number of samples in fifo goes below - // the watermark. Read an extra item from the fifo and just throw it away. - int read_num_bytes = num_bytes + 6; - - uint8_t start_addr = 1 << 7 | LIS3DH_OUT_X_L; - uint8_t buffer[read_num_bytes]; - lis3dh_read(start_addr, read_num_bytes, buffer); - for (uint8_t *ptr = buffer; ptr < (buffer + num_bytes); ptr+=6) { - data->x = get_axis_data(ACCEL_AXIS_X, ptr); - data->y = get_axis_data(ACCEL_AXIS_Y, ptr); - data->z = get_axis_data(ACCEL_AXIS_Z, ptr); - s_accel_data = *data; - data++; - } -} - -void lis3dh_init(void) { - PBL_ASSERTN(!s_initialized); - - lis3dh_init_mutex(); - - s_initialized = true; - - if (!accel_start()) { - s_initialized = false; - return; - } - - if (!lis3dh_config_set_defaults()) { - // accel write will call reset if it fails, so just try again - if (!lis3dh_config_set_defaults()) { - s_initialized = false; - return; - } - } - shared_circular_buffer_init(&s_buffer, s_buffer_storage, sizeof(s_buffer_storage)); - - // Test out the peripheral real quick - if (!lis3dh_sanity_check()) { - s_initialized = false; - return; - } - - accel_stop(); - - prv_accel_configure_interrupts(); -} - -void lis3dh_power_up(void) { - if (accel_start()) { - uint8_t ctrl_reg1; - if (prv_read_register(LIS3DH_CTRL_REG1, &ctrl_reg1)) { - ctrl_reg1 &= ~LPen; - if (prv_write_register(LIS3DH_CTRL_REG1, ctrl_reg1)) { - // Write successful, low power mode disabled - return; - } - } - } - PBL_LOG(LOG_LEVEL_ERROR, "Failed to exit low power mode"); -} - -void lis3dh_power_down(void) { - if (accel_start()) { - uint8_t ctrl_reg1; - if (prv_read_register(LIS3DH_CTRL_REG1, &ctrl_reg1)) { - ctrl_reg1 |= LPen; - if (prv_write_register(LIS3DH_CTRL_REG1, ctrl_reg1)) { - // Write successful, low power mode enabled - accel_stop(); - return; - } - } - } - PBL_LOG(LOG_LEVEL_ERROR, "Failed to enter low power mode"); -} - -bool accel_running(void) { - return (s_running); -} - -bool accel_start(void) { - if (!s_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to start accel, not yet initialized"); - return false; - } - - if (s_running) { - return true; // Already running - } - - i2c_use(I2C_LIS3DH); - - s_running = true; - - return true; -} - -void accel_stop(void) { - PBL_ASSERTN(s_initialized); - if (s_running) { - disable_accel_interrupts(); - clear_accel_interrupts(); - - i2c_release(I2C_LIS3DH); - - enable_accel_interrupts(); - - s_running = false; - } -} - -void lis3dh_init_mutex(void) { - s_accel_mutex = mutex_create(); -} - -void lis3dh_lock(void) { - mutex_lock(s_accel_mutex); -} - -void lis3dh_unlock(void) { - mutex_unlock(s_accel_mutex); -} - -static void prv_handle_tap(void *data) { - IMUCoordinateAxis axis; - int direction; - - if (s_running) { - uint8_t click_src; - lis3dh_read(LIS3DH_CLICK_SRC, 1, &click_src); - - if (click_src & (1 << BOARD_CONFIG_ACCEL.accel_config.axes_offsets[AXIS_X])) { - axis = AXIS_X; - } else if (click_src & (1 << BOARD_CONFIG_ACCEL.accel_config.axes_offsets[AXIS_Y])) { - axis = AXIS_Y; - } else if (click_src & (1 << BOARD_CONFIG_ACCEL.accel_config.axes_offsets[AXIS_Z])) { - axis = AXIS_Z; - } else { - // something has reset the register, ignore - return; - } - // sign bit is zero if positive, 1 if negative - direction = (click_src & Sign) ? -1 : 1; - } else { - // when no-one has subscribed, we only listen to the x axis - axis = AXIS_X; - // no sign info - direction = 0; - } - - PebbleEvent e = { - .type = PEBBLE_ACCEL_SHAKE_EVENT, - .accel_tap = { - .axis = axis, - .direction = direction, - }, - }; - - event_put(&e); -} - -static void lis3dh_IRQ2_handler(bool *should_context_switch) { - // vibe sometimes triggers the tap interrupt. - // if vibe is on, we disregard the interrupt - if (vibes_get_vibe_strength() == VIBE_STRENGTH_OFF) { - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_handle_tap, - .data = NULL - } - }; - - *should_context_switch = event_put_isr(&e); - } -} - -void accel_set_running(bool running) { - s_running = running; -} - -void accel_set_num_samples(uint8_t num_samples) { - if (num_samples == 0) { - // peek mode, no FIFO - lis3dh_set_fifo_mode(MODE_BYPASS); - lis3dh_disable_fifo(); - } else { - lis3dh_set_fifo_wtm(num_samples - 1); - // clear fifo - lis3dh_set_fifo_mode(MODE_BYPASS); - // wait 1 ms - psleep(10); - lis3dh_set_fifo_mode(MODE_STREAM); - lis3dh_enable_fifo(); - } - -} - -static void prv_read_samples(void *data) { - uint8_t src_reg; - lis3dh_read(LIS3DH_FIFO_SRC_REG, 1, &src_reg); - uint8_t num_samples = src_reg & FSS_MASK; - - AccelRawData accel_raw_data[num_samples]; - if (src_reg & FIFO_OVRN) { - PBL_LOG(LOG_LEVEL_ERROR, "Fifo overrun"); - analytics_inc(ANALYTICS_DEVICE_METRIC_ACCEL_FIFO_OVERRUN_COUNT, AnalyticsClient_System); - } - - if (src_reg & FIFO_WTM) { - accel_get_data(accel_raw_data, num_samples); - if (num_samples > 0) { - s_latest_reading = accel_raw_data[num_samples-1]; - } - lis3dh_lock(); - if (s_buffer.clients) { - // Only buffer the data if we have clients that are subscribed. - if (!shared_circular_buffer_write(&s_buffer, (uint8_t *)accel_raw_data, num_samples * sizeof(AccelRawData), - false /*advance_slackers*/)) { - // Buffer is full, one or more clients will get dropped data - - // We have one or more clients who fell behind reading out of the buffer. Try again, but this time - // resetting the slowest clients until there is room. - PBL_ASSERTN(shared_circular_buffer_write(&s_buffer, (uint8_t *)accel_raw_data, num_samples * sizeof(AccelRawData), - true /*advance_slackers*/)); - } - } - lis3dh_unlock(); - } - - // Record timestamp of newest data in the queue - time_t time_s; - uint16_t time_ms; - rtc_get_time_ms(&time_s, &time_ms); - s_latest_timestamp = ((uint64_t)time_s) * 1000 + time_ms; - - if (num_samples == 0) { - accel_reset_pending_accel_event(); - return; - } - - accel_manager_dispatch_data(); -} - -uint64_t accel_get_latest_timestamp(void) { - return s_latest_timestamp; -} - -static void lis3dh_IRQ1_handler(bool *should_context_switch) { - // It's possible that this interrupt could be leftover after turning accel off. - if (!s_running) { - return; - } - - // Only post a new event if the prior one has been picked up. This prevents us from flooding the KernelMain - // queue - if (!s_pending_accel_event) { - s_pending_accel_event = true; - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_read_samples, - .data = NULL - } - }; - *should_context_switch = event_put_isr(&e); - } -} - -//! Returns the latest accel reading -void accel_get_latest_reading(AccelRawData *data) { - *data = s_latest_reading; -} - -//! Clears the pending accel event boolean. Called by KernelMain once it receives the accel_manager_dispatch_data -//! callback -void accel_reset_pending_accel_event(void) { - s_pending_accel_event = false; -} - -//! Adds a consumer to the circular buffer -//! @client which client to add -void accel_add_consumer(SharedCircularBufferClient *client) { - lis3dh_lock(); - PBL_ASSERTN(shared_circular_buffer_add_client(&s_buffer, client)); - lis3dh_unlock(); -} - - -//! Removes a consumer from the circular buffer -//! @client which client to remove -void accel_remove_consumer(SharedCircularBufferClient *client) { - lis3dh_lock(); - shared_circular_buffer_remove_client(&s_buffer, client); - lis3dh_unlock(); -} - - -//! Returns number of samples actually read. -//! @param data The buffer to read the data into -//! @client which client is reading -//! @param max_samples Size of buffer in samples -//! @param subsample_num Subsampling numerator -//! @param subsample_den Subsampling denominator -//! @return The actual number of samples read -uint32_t accel_consume_data(AccelRawData *data, SharedCircularBufferClient *client, uint32_t max_samples, - uint16_t subsample_num, uint16_t subsample_den) { - uint16_t items_read; - PBL_ASSERTN(accel_running()); - lis3dh_lock(); - { - shared_circular_buffer_subsample_items(&s_buffer, client, sizeof(AccelRawData), max_samples, subsample_num, - subsample_den, (uint8_t *)data, &items_read); - } - lis3dh_unlock(); - ACCEL_LOG_DEBUG("%"PRIu16" samples (from %"PRIu32" requested) were read for %p", - items_read, max_samples, client); - return (items_read); -} - - -int accel_peek(AccelData* data) { - if (!s_running) { - return (-1); - } - - // No peeking if we're in FIFO mode. - if (lis3dh_get_fifo_mode() == MODE_STREAM) { - return (-2); - } - - accel_get_data((AccelRawData*)data, 1); - - return (0); -} - - -// Compute and return the device's delta position to help determine movement as idle. -static uint32_t prv_compute_delta_pos(AccelRawData *cur_pos, AccelRawData *last_pos) { - return abs(last_pos->x - cur_pos->x) + abs(last_pos->y - cur_pos->y) + abs(last_pos->z - cur_pos->z); -} - - -// Return true if we are "idle". We check for no movement for at least the last hour (the analytics snapshot -// position is updated once/hour). -bool accel_is_idle(void) { - if (!s_is_idle) { - return false; - } - - // It was idle recently, see if it's still idle. Note we are avoiding reading the accel hardwware again here - // to keep this call as lightweight as possible. Instead we are just comparing the last read value with - // the value last captured by analytics (which does so on an hourly heartbeat). - AccelRawData accel_data; - accel_get_last_data((AccelRawData*)&accel_data); - s_is_idle = (prv_compute_delta_pos(&accel_data, &s_last_analytics_position) < ACCEL_MAX_IDLE_DELTA); - return s_is_idle; -} - - -static bool prv_get_accel_data(AccelRawData *accel_data) { - bool running = accel_running(); - if (!running) { - if (!accel_start()) { - return false; - } - } - if (lis3dh_get_fifo_mode() != MODE_STREAM) { - accel_get_data((AccelRawData*)accel_data, 1); - } else { - accel_get_last_data((AccelRawData*)accel_data); - } - if (!running) { - accel_stop(); - } - return true; -} - -// Analytics Metrics -////////////////////////////////////////////////////////////////////// -void analytics_external_collect_accel_xyz_delta(void) { - AccelRawData accel_data; - if (prv_get_accel_data(&accel_data)) { - uint32_t delta = prv_compute_delta_pos(&accel_data, &s_last_analytics_position); - s_is_idle = (delta < ACCEL_MAX_IDLE_DELTA); - s_last_analytics_position = accel_data; - analytics_set(ANALYTICS_DEVICE_METRIC_ACCEL_XYZ_DELTA, delta, AnalyticsClient_System); - } -} - - -// Self Test -////////////////////////////////////////////////////////////////////// - -bool accel_self_test(void) { - AccelRawData data; - AccelRawData data_st; - - if (!accel_start()) { - PBL_LOG(LOG_LEVEL_ERROR, "Self test failed, could not start accel"); - return false; - } - - psleep(10); - - accel_get_data(&data, 1); - - lis3dh_enter_self_test_mode(SELF_TEST_MODE_ONE); - // ST recommends sleeping for 1ms after programming the module to - // enter self-test mode; a 100x factor of safety ought to be - // sufficient - psleep(100); - - accel_get_data(&data_st, 1); - - lis3dh_exit_self_test_mode(); - accel_stop(); - - // [MJZ] I have no idea how to interpret the data coming out of the - // accel's self-test mode. If I could make sense of the - // incomprehensible datasheet, I would be able to check if the accel - // output matches the expected values - return ABS(data_st.x) > ABS(data.x); -} - -void accel_set_shake_sensitivity_high(bool sensitivity_high) { - // Configure the threshold level at which the LIS3DH will think motion has occurred - if (sensitivity_high) { - lis3dh_set_interrupt_threshold( - BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdLow]); - } else { - lis3dh_set_interrupt_threshold( - BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdHigh]); - } -} diff --git a/src/fw/drivers/imu/lis3dh/lis3dh.h b/src/fw/drivers/imu/lis3dh/lis3dh.h deleted file mode 100644 index 6c80bfb92f..0000000000 --- a/src/fw/drivers/imu/lis3dh/lis3dh.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "applib/accel_service.h" - -static const int16_t LIS3DH_COUNTS_PER_G = 4096; -static const int16_t LIS3DH_SAMPLING_RATE_HZ = 50; - -static const int LIS3DH_MIN_VALUE = -32768; -static const int LIS3DH_MAX_VALUE = 32767; -static const uint8_t LIS3DH_WHOAMI_BYTE = 0x33; - -// Computing AccelSamplingRate * LIS3DH_TIME_LIMIT_MULT / LIS3DH_TIME_LIMIT_DIV -// yields the correct setting for the TIME_LIMIT register -static const int LIS3DH_TIME_LIMIT_MULT = 2240; -static const int LIS3DH_TIME_LIMIT_DIV = 1000; -// Computing AccelSamplingRate * LIS3DH_TIME_LATENCY_MULT / LIS3DH_TIME_LATENCY_DIV -// yields the correct setting for the TIME_LIMIT register -static const int LIS3DH_TIME_LATENCY_MULT = 1280; -static const int LIS3DH_TIME_LATENCY_DIV = 1000; -// Computing AccelSamplingRate * LIS3DH_TIME_WINDOW_MULT / LIS3DH_TIME_WINDOW_DIV -// yields the correct setting for the TIME_WINDOW register -static const int LIS3DH_TIME_WINDOW_MULT = 5120; -static const int LIS3DH_TIME_WINDOW_DIV = 1000; -// Computing AccelScale * LIS3DH_THRESHOLD_MULT / LIS3DH_THRESHOLD_DIV -// yields the correct setting for the CLICK_THS register -static const int LIS3DH_THRESHOLD_MULT = 24; -static const int LIS3DH_THRESHOLD_DIV = 1; - -typedef enum { - SELF_TEST_MODE_OFF, - SELF_TEST_MODE_ONE, - SELF_TEST_MODE_TWO, - SELF_TEST_MODE_COUNT -} SelfTestMode; - -//! Valid accelerometer scales, in g's -typedef enum { - LIS3DH_SCALE_UNKNOWN = 0, - LIS3DH_SCALE_16G = 16, - LIS3DH_SCALE_8G = 8, - LIS3DH_SCALE_4G = 4, - LIS3DH_SCALE_2G = 2, -} Lis3dhScale; - -void lis3dh_init(void); - -void lis3dh_lock(void); -void lis3dh_unlock(void); -void lis3dh_init_mutex(void); - -void enable_lis3dh_interrupts(void); -void disable_lis3dh_interrupts(void); - -bool lis3dh_sanity_check(void); - -// Poke specific registers -void lis3dh_disable_click(void); -void lis3dh_enable_click(void); - -void lis3dh_disable_fifo(void); -void lis3dh_enable_fifo(void); -bool lis3dh_is_fifo_enabled(void); - -void lis3dh_power_up(void); -void lis3dh_power_down(void); - -void lis3dh_set_interrupt_axis(AccelAxisType axis, bool double_click); - -uint8_t lis3dh_get_interrupt_threshold(); -static const uint8_t LIS3DH_MAX_THRESHOLD = 0x7f; -void lis3dh_set_interrupt_threshold(uint8_t threshold); - -uint8_t lis3dh_get_interrupt_time_limit(); -static const uint8_t LIS3DH_MAX_TIME_LIMIT = 0x7f; -void lis3dh_set_interrupt_time_limit(uint8_t time_limit); - -uint8_t lis3dh_get_click_latency(); -static const uint8_t LIS3DH_MAX_CLICK_LATENCY = 0xff; -void lis3dh_set_click_latency(uint8_t latency); - -uint8_t lis3dh_get_click_window(); -static const uint8_t LIS3DH_MAX_CLICK_WINDOW = 0xff; -void lis3dh_set_click_window(uint8_t window); - -bool lis3dh_set_fifo_mode(uint8_t); -uint8_t lis3dh_get_fifo_mode(void); - -bool lis3dh_set_fifo_wtm(uint8_t); -uint8_t lis3dh_get_fifo_wtm(void); - - -bool lis3dh_enter_self_test_mode(SelfTestMode mode); -void lis3dh_exit_self_test_mode(void); -bool lis3dh_self_test(void); - -bool lis3dh_config_set_defaults(); - -bool lis3dh_read(uint8_t address, uint8_t read_size, uint8_t *buffer); -bool lis3dh_write(uint8_t address, uint8_t write_size, const uint8_t *buffer); diff --git a/src/fw/drivers/imu/lis3dh/registers.h b/src/fw/drivers/imu/lis3dh/registers.h deleted file mode 100644 index 8fde884a32..0000000000 --- a/src/fw/drivers/imu/lis3dh/registers.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// 0x00 - 0x06: reserved -static const uint8_t LIS3DH_STATUS_REG_AUX = 0x07; -static const uint8_t LIS3DH_OUT_ADC1_L = 0x08; -static const uint8_t LIS3DH_OUT_ADC1_H = 0x09; -static const uint8_t LIS3DH_OUT_ADC2_L = 0x0a; -static const uint8_t LIS3DH_OUT_ADC2_H = 0x0b; -static const uint8_t LIS3DH_OUT_ADC3_L = 0x0c; -static const uint8_t LIS3DH_OUT_ADC3_H = 0x0d; -static const uint8_t LIS3DH_INT_COUNTER_REG = 0x0e; -static const uint8_t LIS3DH_WHO_AM_I = 0x0f; -// 0x10 - 0x1E: reserved -static const uint8_t LIS3DH_TEMP_CFG_REG = 0x1f; -static const uint8_t LIS3DH_CTRL_REG1 = 0x20; -static const uint8_t LIS3DH_CTRL_REG2 = 0x21; -static const uint8_t LIS3DH_CTRL_REG3 = 0x22; -static const uint8_t LIS3DH_CTRL_REG4 = 0x23; -static const uint8_t LIS3DH_CTRL_REG5 = 0x24; -static const uint8_t LIS3DH_CTRL_REG6 = 0x25; -static const uint8_t LIS3DH_REFERENCE = 0x26; -static const uint8_t LIS3DH_STATUS_REG2 = 0x27; -static const uint8_t LIS3DH_OUT_X_L = 0x28; -static const uint8_t LIS3DH_OUT_X_H = 0x29; -static const uint8_t LIS3DH_OUT_Y_L = 0x2a; -static const uint8_t LIS3DH_OUT_Y_H = 0x2b; -static const uint8_t LIS3DH_OUT_Z_L = 0x2c; -static const uint8_t LIS3DH_OUT_Z_H = 0x2d; -static const uint8_t LIS3DH_FIFO_CTRL_REG = 0x2e; -static const uint8_t LIS3DH_FIFO_SRC_REG = 0x2f; -static const uint8_t LIS3DH_INT1_CFG = 0x30; -static const uint8_t LIS3DH_INT1_SRC = 0x31; -static const uint8_t LIS3DH_INT1_THS = 0x32; -static const uint8_t LIS3DH_INT1_DURATION = 0x33; -// 0x34 - 0x37: reserved -static const uint8_t LIS3DH_CLICK_CFG = 0x38; -static const uint8_t LIS3DH_CLICK_SRC = 0x39; -static const uint8_t LIS3DH_CLICK_THS = 0x3a; -static const uint8_t LIS3DH_TIME_LIMIT = 0x3b; -static const uint8_t LIS3DH_TIME_LATENCY = 0x3c; -static const uint8_t LIS3DH_TIME_WINDOW = 0x3d; -static const uint8_t LIS3DH_ACT_THS = 0x3e; -static const uint8_t LIS3DH_INACT_DUR = 0x3f; - -// CTRL_REG1 -static const uint8_t ODR3 = (1 << 7); -static const uint8_t ODR2 = (1 << 6); -static const uint8_t ODR1 = (1 << 5); -static const uint8_t ODR0 = (1 << 4); -static const uint8_t ODR_MASK = (0xf0); -static const uint8_t LPen = (1 << 3); -static const uint8_t Zen = (1 << 2); -static const uint8_t Yen = (1 << 1); -static const uint8_t Xen = (1 << 0); - -// CTRL_REG3 -static const uint8_t I1_CLICK = (1 << 7); -static const uint8_t I1_AOI1 = (1 << 6); -static const uint8_t I1_DTRDY = (1 << 4); -static const uint8_t I1_WTM = (1 << 2); -static const uint8_t I1_OVRN = (1 << 1); - -//CTRL_REG4 -static const uint8_t BDU = (1 << 7); -static const uint8_t BLE = (1 << 6); -static const uint8_t FS1 = (1 << 5); -static const uint8_t FS0 = (1 << 4); -static const uint8_t HR = (1 << 3); -static const uint8_t ST1 = (1 << 2); -static const uint8_t ST0 = (1 << 1); -static const uint8_t SIM = (1 << 0); -static const uint8_t FS_MASK = 0x30; - -//CTRL_REG5 -static const uint8_t FIFO_EN = (1 << 6); - -//CTRL_REG6 -static const uint8_t I2_CLICK = (1 << 7); - -// CLICK_CFG -static const uint8_t ZD = (1 << 5); -static const uint8_t ZS = (1 << 4); -static const uint8_t YD = (1 << 3); -static const uint8_t YS = (1 << 2); -static const uint8_t XD = (1 << 1); -static const uint8_t XS = (1 << 0); - -// CLICK_SRC -static const uint8_t IA = 1 << 6; -static const uint8_t DCLICK = 1 << 5; -static const uint8_t SCLICK = 1 << 4; -static const uint8_t Sign = 1 << 3; -static const uint8_t ZClick = 1 << 2; -static const uint8_t YClick = 1 << 1; -static const uint8_t XClick = 1 << 0; - -// FIFO_CTRL_REG -static const uint8_t MODE_BYPASS = 0x0; -static const uint8_t MODE_FIFO = (0x1 << 6); -static const uint8_t MODE_STREAM = (0x1 << 7); -static const uint8_t MODE_MASK = 0xc0; -static const uint8_t THR_MASK = 0x1f; - -// FIFO_SRC_REG -static const uint8_t FIFO_WTM = (0x1 << 7); -static const uint8_t FIFO_OVRN = (0x1 << 6); -static const uint8_t FIFO_EMPTY = (0x1 << 5); -static const uint8_t FSS_MASK = 0x1f; diff --git a/src/fw/drivers/imu/lsm6dso/lsm6dso.c b/src/fw/drivers/imu/lsm6dso/lsm6dso.c index 698a7ee1c1..81bdae2353 100644 --- a/src/fw/drivers/imu/lsm6dso/lsm6dso.c +++ b/src/fw/drivers/imu/lsm6dso/lsm6dso.c @@ -1,19 +1,5 @@ -/* - * Copyright 2025 Matthew Wardrop - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +/* SPDX-FileCopyrightText: 2025 Matthew Wardrop */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/accel.h" #include "drivers/i2c.h" @@ -21,14 +7,16 @@ #include "drivers/rtc.h" #include "drivers/vibe.h" #include "kernel/util/sleep.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/vibe_pattern.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/vibe_pattern.h" #include "system/logging.h" #include "util/math.h" #include "lsm6dso_reg.h" #include "lsm6dso.h" +PBL_LOG_MODULE_DEFINE(driver_accel_lsm6dso, CONFIG_DRIVER_IMU_LOG_LEVEL); + // Forward declaration of private functions defined below public functions static int32_t prv_lsm6dso_read(void *handle, uint8_t reg_addr, uint8_t *buffer, uint16_t read_size); @@ -44,10 +32,9 @@ static void prv_lsm6dso_configure_double_tap(bool enable); static void prv_lsm6dso_configure_shake(bool enable, bool sensitivity_high); static void prv_lsm6dso_interrupt_handler(bool *should_context_switch); static void prv_lsm6dso_process_interrupts(void); +static void prv_lsm6dso_interrupt_watchdog_callback(void *data); +static bool prv_lsm6dso_force_reinit(void); static bool prv_is_vibing(void); -static void prv_lsm6dso_retry_interrupt_config(void *unused); -static bool prv_lsm6dso_health_check(void); -static bool prv_lsm6dso_attempt_recovery(void); typedef struct { lsm6dso_odr_xl_t odr; lsm6dso_xl_hm_mode_t power_mode; @@ -92,7 +79,10 @@ lsm6dso_state_t s_lsm6dso_state_target = {0}; static uint32_t s_tap_threshold = BOARD_CONFIG_ACCEL.accel_config.double_tap_threshold / 1250; static bool s_fifo_in_use = false; // true when we have enabled FIFO batching static uint32_t s_last_vibe_detected = 0; -static bool s_interrupt_config_retry_scheduled = false; + +// User-configured sensitivity percentage (0-100), where 100 = most sensitive +// Default to 100% (maximum sensitivity) to maintain current behavior +static uint8_t s_user_sensitivity_percent = 100; // Error tracking and recovery static uint32_t s_i2c_error_count = 0; @@ -101,8 +91,6 @@ static uint32_t s_consecutive_errors = 0; static bool s_sensor_health_ok = true; static int16_t s_last_sample_mg[3] = {0}; static uint64_t s_last_sample_timestamp_ms = 0; -static uint32_t s_watchdog_event_count = 0; -static uint32_t s_recovery_success_count = 0; // Interrupt activity instrumentation so we can spot when the sensor stops firing INT1. static uint64_t s_last_interrupt_ms = 0; static uint64_t s_last_wake_event_ms = 0; @@ -110,13 +98,16 @@ static uint64_t s_last_double_tap_ms = 0; static uint32_t s_interrupt_count = 0; static uint32_t s_wake_event_count = 0; static uint32_t s_double_tap_event_count = 0; +static uint32_t s_watchdog_recovery_attempts = 0; + +// Interrupt watchdog timer +static RegularTimerInfo s_interrupt_watchdog_timer = { + .cb = prv_lsm6dso_interrupt_watchdog_callback, + .cb_data = NULL +}; -#if CAPABILITY_NEEDS_FIRM_579_STATS -/* Counters exported to memfault heartbeat (declared as extern where used). - * These must be non-static so they are visible to other translation units. */ -uint32_t metric_firm_579_log_events = 0; -uint32_t metric_firm_579_attempted_recoveries = 0; -#endif +// watch rotation +static bool s_rotated_180 = false; // Maximum FIFO watermark supported by hardware (diff_fifo is 10 bits -> 0..1023) #define LSM6DSO_FIFO_MAX_WATERMARK 1023 @@ -129,44 +120,33 @@ uint32_t metric_firm_579_attempted_recoveries = 0; // Error recovery thresholds and watchdog timeouts #define LSM6DSO_MAX_CONSECUTIVE_FAILURES 3 -#define LSM6DSO_HEALTH_CHECK_INTERVAL_MS 5000 // Check sensor health every 5 seconds -#define LSM6DSO_MAX_SILENT_PERIOD_MS 30000 // Max time without successful read -#define LSM6DSO_WATCHDOG_TIMEOUT_MS 5000 // Watchdog timeout for detecting unresponsive sensor -#define LSM6DSO_MAX_CONSECUTIVE_ERRORS 5 // Maximum consecutive errors before attempting recovery #define LSM6DSO_INTERRUPT_GAP_LOG_THRESHOLD_MS 3000 -#define LSM6DSO_INTERRUPT_LOCKUP_THRESHOLD_MS 15000 // Max time without interrupt for motion detection +#define LSM6DSO_INTERRUPT_WATCHDOG_MS 10000 //run watchdog every 10 seconds +#define LSM6DSO_INTERRUPT_WATCHDOG_TIMEOUT_MS 5000 // but count as failure if no interrupt in 5 seconds +#define LSM6DSO_INTERRUPT_WATCHDOG_MS_NO_SAMPLES 600000 //if no samples are requested, every 10 minutes is fine // LSM6DSO configuration entrypoints -void lsm6dso_init(void) { +void accel_init(void) { // Initialize the LSM6DSO sensor to a powered down state. prv_lsm6dso_init(); } -void lsm6dso_power_up(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Powering up accelerometer"); +void accel_power_up(void) { s_lsm6dso_enabled = true; prv_lsm6dso_chase_target_state(); } -void lsm6dso_power_down(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Powering down accelerometer"); +void accel_power_down(void) { + PBL_LOG_DBG("LSM6DSO: Powering down accelerometer"); s_lsm6dso_enabled = false; prv_lsm6dso_chase_target_state(); } // accel.h implementation -const AccelDriverInfo ACCEL_DRIVER_INFO = { - .sample_interval_max = 625000, // 1.6 Hz - .sample_interval_low_power = 80000, // 12.5Hz - .sample_interval_ui = 80000, // 12.5Hz - .sample_interval_game = 19231, // 52Hz - .sample_interval_min = 150, // 6667Hz -}; - uint32_t accel_set_sampling_interval(uint32_t interval_us) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Requesting update of sampling interval to %lu us", + PBL_LOG_DBG("LSM6DSO: Requesting update of sampling interval to %lu us", interval_us); s_lsm6dso_state_target.sampling_interval_us = interval_us; prv_lsm6dso_chase_target_state(); @@ -176,7 +156,7 @@ uint32_t accel_set_sampling_interval(uint32_t interval_us) { uint32_t accel_get_sampling_interval(void) { return s_lsm6dso_state.sampling_interval_us; } void accel_set_num_samples(uint32_t num_samples) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Setting number of samples to %lu", num_samples); + PBL_LOG_DBG("LSM6DSO: Setting number of samples to %lu", num_samples); s_lsm6dso_state_target.num_samples = num_samples; prv_lsm6dso_chase_target_state(); } @@ -184,7 +164,7 @@ void accel_set_num_samples(uint32_t num_samples) { int accel_peek(AccelDriverSample *data) { return prv_lsm6dso_read_sample(data); } void accel_enable_shake_detection(bool on) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: %s shake detection.", on ? "Enabling" : "Disabling"); + PBL_LOG_DBG("LSM6DSO: %s shake detection.", on ? "Enabling" : "Disabling"); s_lsm6dso_state_target.shake_detection_enabled = on; prv_lsm6dso_chase_target_state(); } @@ -192,7 +172,7 @@ void accel_enable_shake_detection(bool on) { bool accel_get_shake_detection_enabled(void) { return s_lsm6dso_state.shake_detection_enabled; } void accel_enable_double_tap_detection(bool on) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: %s double tap detection.", on ? "Enabling" : "Disabling"); + PBL_LOG_DBG("LSM6DSO: %s double tap detection.", on ? "Enabling" : "Disabling"); s_lsm6dso_state_target.double_tap_detection_enabled = on; prv_lsm6dso_chase_target_state(); } @@ -202,12 +182,27 @@ bool accel_get_double_tap_detection_enabled(void) { } void accel_set_shake_sensitivity_high(bool sensitivity_high) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Setting shake sensitivity to %s.", + PBL_LOG_DBG("LSM6DSO: Setting shake sensitivity to %s.", sensitivity_high ? "high" : "normal"); s_lsm6dso_state_target.shake_sensitivity_high = sensitivity_high; prv_lsm6dso_chase_target_state(); } +void accel_set_shake_sensitivity_percent(uint8_t percent) { + if (percent > 100) { + percent = 100; // Clamp to max + } + + s_user_sensitivity_percent = percent; + + // Reconfigure shake detection if it's currently enabled + if (s_lsm6dso_state.shake_detection_enabled) { + prv_lsm6dso_configure_shake(true, s_lsm6dso_state.shake_sensitivity_high); + } + + PBL_LOG_INFO("LSM6DSO: User sensitivity set to %u percent", percent); +} + // HAL context implementations static int32_t prv_lsm6dso_read(void *handle, uint8_t reg_addr, uint8_t *buffer, @@ -220,11 +215,11 @@ static int32_t prv_lsm6dso_read(void *handle, uint8_t reg_addr, uint8_t *buffer, if (!result) { s_i2c_error_count++; s_consecutive_errors++; - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: I2C read failed (reg=0x%02x, count=%lu)", + PBL_LOG_ERR("LSM6DSO: I2C read failed (reg=0x%02x, count=%lu)", reg_addr, s_consecutive_errors); if (s_consecutive_errors >= LSM6DSO_MAX_CONSECUTIVE_FAILURES) { s_sensor_health_ok = false; - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Sensor health degraded after %lu failures", + PBL_LOG_ERR("LSM6DSO: Sensor health degraded after %lu failures", s_consecutive_errors); } return -1; @@ -249,7 +244,7 @@ static int32_t prv_lsm6dso_write(void *handle, uint8_t reg_addr, const uint8_t * if (!result) { s_i2c_error_count++; s_consecutive_errors++; - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: I2C write failed (reg=0x%02x)", reg_addr); + PBL_LOG_ERR("LSM6DSO: I2C write failed (reg=0x%02x)", reg_addr); return -1; } else { s_consecutive_errors = 0; @@ -277,21 +272,20 @@ static void prv_lsm6dso_init(void) { // Verify sensor is present and functioning uint8_t whoami; if (lsm6dso_device_id_get(&lsm6dso_ctx, &whoami)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to read WHO_AM_I register"); + PBL_LOG_ERR("LSM6DSO: Failed to read WHO_AM_I register"); return; } if (whoami != LSM6DSO_ID) { - PBL_LOG(LOG_LEVEL_ERROR, - "LSM6DSO: Sensor not detected or malfunctioning (WHO_AM_I=0x%02x, expecting 0x%02x)", + PBL_LOG_ERR("LSM6DSO: Sensor not detected or malfunctioning (WHO_AM_I=0x%02x, expecting 0x%02x)", whoami, LSM6DSO_ID); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Sensor detected successfully (WHO_AM_I=0x%02x)", whoami); + PBL_LOG_DBG("LSM6DSO: Sensor detected successfully (WHO_AM_I=0x%02x)", whoami); // Reset sensor to known state if (lsm6dso_reset_set(&lsm6dso_ctx, PROPERTY_ENABLE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to reset sensor"); + PBL_LOG_ERR("LSM6DSO: Failed to reset sensor"); return; } uint8_t rst; @@ -299,58 +293,58 @@ static void prv_lsm6dso_init(void) { do { // Wait for reset to complete with timeout psleep(1); if (lsm6dso_reset_get(&lsm6dso_ctx, &rst) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to read reset status"); + PBL_LOG_ERR("LSM6DSO: Failed to read reset status"); return; } reset_timeout--; } while (rst && reset_timeout > 0); if (reset_timeout == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Reset timeout - sensor may be unresponsive"); + PBL_LOG_ERR("LSM6DSO: Reset timeout - sensor may be unresponsive"); return; } // Disable I3C interface if (lsm6dso_i3c_disable_set(&lsm6dso_ctx, LSM6DSO_I3C_DISABLE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to disable I3C interface"); + PBL_LOG_ERR("LSM6DSO: Failed to disable I3C interface"); return; } // Enable Block Data Update if (lsm6dso_block_data_update_set(&lsm6dso_ctx, PROPERTY_ENABLE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to enable block data update"); + PBL_LOG_ERR("LSM6DSO: Failed to enable block data update"); return; } // Enable Auto Increment if (lsm6dso_auto_increment_set(&lsm6dso_ctx, PROPERTY_ENABLE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to enable auto increment"); + PBL_LOG_ERR("LSM6DSO: Failed to enable auto increment"); return; } // Set FIFO mode to bypass (will be reconfigured as necessary later) if (lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set FIFO mode to bypass"); + PBL_LOG_ERR("LSM6DSO: Failed to set FIFO mode to bypass"); return; } // Set default full scale if (lsm6dso_xl_full_scale_set(&lsm6dso_ctx, LSM6DSO_4g)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set accelerometer full scale"); + PBL_LOG_ERR("LSM6DSO: Failed to set accelerometer full scale"); return; } if (lsm6dso_gy_full_scale_set(&lsm6dso_ctx, LSM6DSO_250dps)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set gyroscope full scale"); + PBL_LOG_ERR("LSM6DSO: Failed to set gyroscope full scale"); return; } // Set output rate to zero (disabling sensors) if (lsm6dso_xl_data_rate_set(&lsm6dso_ctx, LSM6DSO_XL_ODR_OFF)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set accelerometer ODR"); + PBL_LOG_ERR("LSM6DSO: Failed to set accelerometer ODR"); return; } if (lsm6dso_gy_data_rate_set(&lsm6dso_ctx, LSM6DSO_GY_ODR_OFF)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set gyroscope ODR"); + PBL_LOG_ERR("LSM6DSO: Failed to set gyroscope ODR"); return; } @@ -365,65 +359,27 @@ static void prv_lsm6dso_init(void) { // these to pulsed so that if we miss an interrupt due to timing issues we do // not miss subsequent ones. if (lsm6dso_data_ready_mode_set(&lsm6dso_ctx, LSM6DSO_DRDY_PULSED)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set data ready mode"); + PBL_LOG_ERR("LSM6DSO: Failed to set data ready mode"); return; } if (lsm6dso_int_notification_set(&lsm6dso_ctx, LSM6DSO_ALL_INT_PULSED)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to configure interrupt notification"); + PBL_LOG_ERR("LSM6DSO: Failed to configure interrupt notification"); return; } s_lsm6dso_initialized = true; s_last_successful_read_ms = prv_get_timestamp_ms(); s_consecutive_errors = 0; - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Initialization complete"); + PBL_LOG_DBG("LSM6DSO: Initialization complete"); } //! Synchronize the LSM6DSO state with the desired target state. static void prv_lsm6dso_chase_target_state(void) { if (!s_lsm6dso_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Cannot chase target state before initialization"); + PBL_LOG_ERR("LSM6DSO: Cannot chase target state before initialization"); return; } - // Check for unresponsive sensor and attempt recovery - uint32_t now = prv_get_timestamp_ms(); - if (s_lsm6dso_running && s_last_successful_read_ms > 0) { - // Only treat a quiet bus as a watchdog failure when we actively stream samples; - // motion-only use (shake/tap) can legitimately go several seconds with no reads. - const bool streaming_samples = (s_lsm6dso_state.num_samples > 0); - const bool watchdog_expired = - streaming_samples && (now - s_last_successful_read_ms > LSM6DSO_WATCHDOG_TIMEOUT_MS); - const bool too_many_errors = (s_consecutive_errors >= LSM6DSO_MAX_CONSECUTIVE_ERRORS); - - // Check for interrupt lockup - when motion detection is enabled but no interrupts occur - const bool motion_detection_active = (s_lsm6dso_state.shake_detection_enabled || - s_lsm6dso_state.double_tap_detection_enabled); - const bool interrupt_lockup = motion_detection_active && s_last_interrupt_ms > 0 && - (now - s_last_interrupt_ms > LSM6DSO_INTERRUPT_LOCKUP_THRESHOLD_MS); - - if (watchdog_expired || too_many_errors || interrupt_lockup) { - s_watchdog_event_count++; - if (interrupt_lockup) { - PBL_LOG(LOG_LEVEL_WARNING, "LSM6DSO: Interrupt lockup detected (last interrupt: %lu ms ago)", - (unsigned long)(now - s_last_interrupt_ms)); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "LSM6DSO: Sensor appears unresponsive (last_read: %lu ms ago, errors: %lu)", - now - s_last_successful_read_ms, s_consecutive_errors); - } - - if (!prv_lsm6dso_attempt_recovery()) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Recovery failed, disabling sensor"); - s_lsm6dso_enabled = false; - return; - } - - // Reset state and continue with normal configuration - s_lsm6dso_state = (lsm6dso_state_t){0}; - s_lsm6dso_running = false; - } - } - bool update_interrupts = false; // Check whether we should be spinning up the accelerometer @@ -434,16 +390,21 @@ static void prv_lsm6dso_chase_target_state(void) { if (!should_be_running || !s_lsm6dso_enabled) { if (s_lsm6dso_running) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Stopping accelerometer"); + PBL_LOG_DBG("LSM6DSO: Stopping accelerometer"); lsm6dso_xl_data_rate_set(&lsm6dso_ctx, LSM6DSO_XL_ODR_OFF); s_lsm6dso_running = false; s_lsm6dso_state = (lsm6dso_state_t){0}; prv_lsm6dso_configure_interrupts(); + // Stop the interrupt watchdog when sensor is stopped + regular_timer_remove_callback(&s_interrupt_watchdog_timer); } return; } else if (!s_lsm6dso_running) { s_lsm6dso_running = true; update_interrupts = true; + // Start the interrupt watchdog when sensor starts running + regular_timer_add_multisecond_callback(&s_interrupt_watchdog_timer, + LSM6DSO_INTERRUPT_WATCHDOG_MS / 1000); } // Update number of samples @@ -504,8 +465,7 @@ static void prv_lsm6dso_chase_target_state(void) { // where new target changes during this function execution could be lost. // Instead, only sync the fields that were actually processed. - PBL_LOG(LOG_LEVEL_DEBUG, - "LSM6DSO: Reached target state: sampling_interval_us=%lu, num_samples=%lu, " + PBL_LOG_DBG("LSM6DSO: Reached target state: sampling_interval_us=%lu, num_samples=%lu, " "shake_detection_enabled=%d, shake_high_sensitivity=%d, double_tap_detection_enabled=%d", s_lsm6dso_state.sampling_interval_us, s_lsm6dso_state.num_samples, s_lsm6dso_state.shake_detection_enabled, s_lsm6dso_state.shake_sensitivity_high, @@ -515,27 +475,28 @@ static void prv_lsm6dso_chase_target_state(void) { static void prv_lsm6dso_configure_interrupts(void) { // Disable interrupts during configuration to prevent race conditions // and ensure atomic configuration updates - - // Clear any outstanding retry flag now that we're actively reconfiguring. - s_interrupt_config_retry_scheduled = false; bool should_enable_interrupts = s_lsm6dso_enabled && (s_lsm6dso_state.num_samples || s_lsm6dso_state.shake_detection_enabled || s_lsm6dso_state.double_tap_detection_enabled); - + // Always disable interrupts first to ensure clean state exti_disable(BOARD_CONFIG_ACCEL.accel_ints[0]); - + if (!should_enable_interrupts) { // Also disable all interrupt sources in the sensor to prevent phantom interrupts lsm6dso_pin_int1_route_t int1_routes = {0}; // All disabled - lsm6dso_pin_int1_route_set(&lsm6dso_ctx, int1_routes); + if (lsm6dso_pin_int1_route_set(&lsm6dso_ctx, int1_routes)) { + PBL_LOG_ERR("LSM6DSO: Failed to disable INT1 routes while turning off sensor"); + } return; } + bool routing_configured = true; + lsm6dso_pin_int1_route_t int1_routes = {0}; bool use_fifo = s_lsm6dso_state.num_samples > 1; // batching requested - + // Configure FIFO first, then set up interrupt routing if (use_fifo) { prv_lsm6dso_configure_fifo(true); @@ -548,40 +509,28 @@ static void prv_lsm6dso_configure_interrupts(void) { int1_routes.fifo_th = 0; int1_routes.fifo_ovr = 0; } - + int1_routes.double_tap = s_lsm6dso_state.double_tap_detection_enabled; int1_routes.wake_up = s_lsm6dso_state.shake_detection_enabled; // use wake-up (any-motion) // Configure interrupt routing atomically if (lsm6dso_pin_int1_route_set(&lsm6dso_ctx, int1_routes)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to configure interrupts (retrying)"); - - if (should_enable_interrupts) { - // Keep external interrupt alive using the previous sensor routing. - exti_enable(BOARD_CONFIG_ACCEL.accel_ints[0]); - } - - if (!s_interrupt_config_retry_scheduled) { - if (!new_timer_add_work_callback(prv_lsm6dso_retry_interrupt_config, NULL)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to queue interrupt reconfiguration retry"); - } else { - s_interrupt_config_retry_scheduled = true; - } + PBL_LOG_ERR("LSM6DSO: Failed to configure INT1 routes; re-enabling external interrupt"); + routing_configured = false; + } else { + // Clear any pending interrupt sources before enabling external interrupt + lsm6dso_all_sources_t all_sources; + if (lsm6dso_all_sources_get(&lsm6dso_ctx, &all_sources)) { + PBL_LOG_WRN("LSM6DSO: Failed to clear pending interrupt sources after routing update"); } - return; } - // Clear any pending interrupt sources before enabling external interrupt - lsm6dso_all_sources_t all_sources; - lsm6dso_all_sources_get(&lsm6dso_ctx, &all_sources); // This clears pending sources - - // Finally enable the external interrupt + // Always re-enable the external interrupt so we do not lose future INT1 edges exti_enable(BOARD_CONFIG_ACCEL.accel_ints[0]); -} -static void prv_lsm6dso_retry_interrupt_config(void *unused) { - s_interrupt_config_retry_scheduled = false; - prv_lsm6dso_configure_interrupts(); + if (!routing_configured) { + PBL_LOG_WRN("LSM6DSO: INT1 routing not updated; external interrupt left enabled for recovery"); + } } // Map output data rate (interval) to FIFO batching rate enum @@ -615,32 +564,33 @@ static void prv_lsm6dso_configure_fifo(bool enable) { if (watermark == 0) watermark = 1; // minimum if (watermark > LSM6DSO_FIFO_MAX_WATERMARK) watermark = LSM6DSO_FIFO_MAX_WATERMARK; - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Setting FIFO watermark to %lu (requested %lu samples)", + PBL_LOG_DBG("LSM6DSO: Setting FIFO watermark to %lu (requested %lu samples)", watermark, s_lsm6dso_state.num_samples); if (lsm6dso_fifo_watermark_set(&lsm6dso_ctx, (uint16_t)watermark)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set FIFO watermark"); + PBL_LOG_ERR("LSM6DSO: Failed to set FIFO watermark"); } // Enable accelerometer batching at (approx) current ODR lsm6dso_bdr_xl_t batch_rate = prv_get_fifo_batch_rate(s_lsm6dso_state.sampling_interval_us); if (lsm6dso_fifo_xl_batch_set(&lsm6dso_ctx, batch_rate)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set FIFO batch rate"); + PBL_LOG_ERR("LSM6DSO: Failed to set FIFO batch rate"); } // Disable gyro batching to save FIFO space lsm6dso_fifo_gy_batch_set(&lsm6dso_ctx, LSM6DSO_GY_NOT_BATCHED); - - // If FIFO was previously disabled, enable it cleanly - if (!s_fifo_in_use) { - // Clear FIFO before enabling to start fresh - lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE); - psleep(1); // Allow time for FIFO to clear - // Put FIFO in stream mode so we keep collecting samples and get periodic watermark interrupts - if (lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_STREAM_MODE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to enable FIFO stream mode"); - } + // Always clear and re-enable FIFO to ensure clean state after configuration changes. + // This is critical when watermark changes while FIFO is already enabled, as stale + // samples in the FIFO can prevent new watermark interrupts from being generated. + // For example, if FIFO has 25 samples and watermark is lowered to 3, the sensor + // won't generate an interrupt because the FIFO already exceeds the watermark. + lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE); + psleep(1); // Allow time for FIFO to clear + + // Put FIFO in stream mode so we keep collecting samples and get periodic watermark interrupts + if (lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_STREAM_MODE)) { + PBL_LOG_ERR("LSM6DSO: Failed to enable FIFO stream mode"); } } else { if (s_fifo_in_use) { @@ -648,13 +598,13 @@ static void prv_lsm6dso_configure_fifo(bool enable) { lsm6dso_fifo_xl_batch_set(&lsm6dso_ctx, LSM6DSO_XL_NOT_BATCHED); lsm6dso_fifo_gy_batch_set(&lsm6dso_ctx, LSM6DSO_GY_NOT_BATCHED); if (lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE)) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to disable FIFO"); + PBL_LOG_ERR("LSM6DSO: Failed to disable FIFO"); } } } s_fifo_in_use = enable; - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: FIFO %s (wm=%lu)", enable ? "enabled" : "disabled", + PBL_LOG_DBG("LSM6DSO: FIFO %s (wm=%lu)", enable ? "enabled" : "disabled", (unsigned long)s_lsm6dso_state.num_samples); } @@ -707,36 +657,44 @@ static void prv_lsm6dso_configure_shake(bool enable, bool sensitivity_high) { // Duration: increase a bit to reduce spurious triggers lsm6dso_wkup_dur_set(&lsm6dso_ctx, sensitivity_high ? 0 : 1); - // Threshold: derive from board config; clamp into 0..63 + // Threshold calculation: + // - Board config provides Low (15) and High (64) thresholds + // - sensitivity_high flag indicates stationary mode (use low threshold for any movement) + // - s_user_sensitivity_percent (0-100) controls normal mode threshold + // * 100% = most sensitive = use Low threshold (15) + // * 50% = medium = interpolate between Low and High (~40) + // * 0% = least sensitive = use High threshold (64) + uint32_t raw_high = BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdHigh]; uint32_t raw_low = BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdLow]; - uint32_t raw = sensitivity_high ? raw_high : raw_low; - // Increase sensitivity: scale threshold down (halve). Ensure at least 2 to avoid noise storms. - raw = (raw + 1) / 2; // divide by 2 rounding up + uint32_t raw; + + if (sensitivity_high) { + // Stationary mode: always use low threshold for maximum sensitivity + raw = raw_low; + } else { + // Normal mode: interpolate based on user preference + // Invert the percentage: 100% sensitive = low threshold, 0% sensitive = high threshold + uint32_t inverted_percent = 100 - s_user_sensitivity_percent; + raw = raw_low + ((raw_high - raw_low) * inverted_percent) / 100; + } + + // Clamp to valid range if (raw > 63) raw = 63; // lsm6dso wk_ths is 6 bits - // Sanity fallback if 0 (avoid constant triggers) choose very low but non-zero - if (raw == 0) raw = 2; + if (raw < 2) raw = 2; // Avoid noise storms with very low thresholds + lsm6dso_wkup_threshold_set(&lsm6dso_ctx, (uint8_t)raw); + + PBL_LOG_DBG("LSM6DSO: Shake threshold set to %lu (sensitivity_high=%d, user_percent=%u)", + raw, sensitivity_high, s_user_sensitivity_percent); } static void prv_lsm6dso_interrupt_handler(bool *should_context_switch) { - // Always process interrupts immediately to prevent lost events - // The LSM6DSO can miss events if interrupts are ignored due to pending flags - - // Clear the hardware interrupt sources immediately to prevent sensor lockup - // This is done in ISR context to minimize latency - static volatile bool interrupt_active = false; - - // Prevent recursive calls but ensure we don't lose interrupts - if (interrupt_active) { - // If already processing, queue another work item to catch any new events - accel_offload_work_from_isr(prv_lsm6dso_process_interrupts, should_context_switch); - return; - } - - interrupt_active = true; + // Offload processing to a worker. The LSM6DSO can miss events if interrupts + // are ignored due to pending flags, so it is important to process them + // quickly. The actual clearing of the interrupt flags will happen in the + // worker via an I2C transaction. accel_offload_work_from_isr(prv_lsm6dso_process_interrupts, should_context_switch); - interrupt_active = false; } static void prv_lsm6dso_process_interrupts(void) { @@ -744,17 +702,17 @@ static void prv_lsm6dso_process_interrupts(void) { const uint64_t previous_interrupt_ms = s_last_interrupt_ms; s_last_interrupt_ms = now_ms; s_interrupt_count++; + s_watchdog_recovery_attempts = 0; uint32_t gap_ms = 0; if (previous_interrupt_ms == 0) { - PBL_LOG(LOG_LEVEL_INFO, "LSM6DSO: First INT1 service (count=%lu)", + PBL_LOG_INFO("LSM6DSO: First INT1 service (count=%lu)", (unsigned long)s_interrupt_count); } else { uint64_t raw_gap_ms = now_ms - previous_interrupt_ms; gap_ms = (raw_gap_ms > UINT32_MAX) ? UINT32_MAX : (uint32_t)raw_gap_ms; if (gap_ms >= LSM6DSO_INTERRUPT_GAP_LOG_THRESHOLD_MS) { - PBL_LOG(LOG_LEVEL_INFO, - "LSM6DSO: INT1 gap %lu ms (count=%lu wake=%lu tap=%lu)", + PBL_LOG_INFO("LSM6DSO: INT1 gap %lu ms (count=%lu wake=%lu tap=%lu)", (unsigned long)gap_ms, (unsigned long)s_interrupt_count, (unsigned long)s_wake_event_count, (unsigned long)s_double_tap_event_count); } @@ -779,11 +737,11 @@ static void prv_lsm6dso_process_interrupts(void) { } while (read_attempts < max_read_attempts); if (read_attempts >= max_read_attempts) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to read interrupt sources after retries"); + PBL_LOG_ERR("LSM6DSO: Failed to read interrupt sources after retries"); s_consecutive_errors++; if (s_consecutive_errors >= LSM6DSO_MAX_CONSECUTIVE_FAILURES) { s_sensor_health_ok = false; - PBL_LOG(LOG_LEVEL_WARNING, "LSM6DSO: Interrupt processing failed, sensor health degraded"); + PBL_LOG_WRN("LSM6DSO: Interrupt processing failed, sensor health degraded"); } return; } @@ -794,7 +752,7 @@ static void prv_lsm6dso_process_interrupts(void) { // Prevent FIFO overflow by proper watermark management // FIFO overflow causes the sensor to stop generating interrupts if (all_sources.fifo_ovr || all_sources.fifo_full) { - PBL_LOG(LOG_LEVEL_WARNING, "LSM6DSO: FIFO overflow/full detected, clearing FIFO"); + PBL_LOG_WRN("LSM6DSO: FIFO overflow/full detected, clearing FIFO"); // Properly clear FIFO without losing configuration uint16_t current_watermark; @@ -824,7 +782,7 @@ static void prv_lsm6dso_process_interrupts(void) { lsm6dso_fifo_xl_batch_set(&lsm6dso_ctx, current_batch_rate); lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_STREAM_MODE); - PBL_LOG(LOG_LEVEL_INFO, "LSM6DSO: Reduced FIFO watermark from %u to %u to prevent future overflow", + PBL_LOG_INFO("LSM6DSO: Reduced FIFO watermark from %u to %u to prevent future overflow", current_watermark, reduced_watermark); } @@ -852,7 +810,7 @@ static void prv_lsm6dso_process_interrupts(void) { if (all_sources.double_tap) { s_double_tap_event_count++; s_last_double_tap_ms = now_ms; - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Double tap interrupt triggered"); + PBL_LOG_DBG("LSM6DSO: Double tap interrupt triggered"); // Handle double tap detection axis_t axis; if (all_sources.tap_x) { @@ -862,7 +820,7 @@ static void prv_lsm6dso_process_interrupts(void) { } else if (all_sources.tap_z) { axis = Z_AXIS; } else { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: No tap axis detected"); + PBL_LOG_DBG("LSM6DSO: No tap axis detected"); return; // No valid tap detected } @@ -870,7 +828,7 @@ static void prv_lsm6dso_process_interrupts(void) { uint8_t axis_direction = (BOARD_CONFIG_ACCEL.accel_config.axes_inverts[axis] ? -1 : 1) * (all_sources.tap_sign ? -1 : 1); - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Double tap interrupt triggered; axis=%d, direction=%d", + PBL_LOG_DBG("LSM6DSO: Double tap interrupt triggered; axis=%d, direction=%d", axis_offset, axis_direction); accel_cb_double_tap_detected(axis_offset, axis_direction); } @@ -904,7 +862,7 @@ static void prv_lsm6dso_process_interrupts(void) { int16_t mg_z = prv_get_axis_projection_mg(Z_AXIS, accel_raw); prv_note_new_sample_mg(mg_x, mg_y, mg_z); } - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Shake detected; axis=%d, direction=%lu", axis, direction); + PBL_LOG_DBG("LSM6DSO: Shake detected; axis=%d, direction=%lu", axis, direction); accel_cb_shake_detected(axis, direction); } } @@ -925,6 +883,69 @@ static bool prv_is_vibing(void) { return false; } +static bool prv_lsm6dso_force_reinit(void) { + PBL_LOG_WRN("LSM6DSO: Performing forced sensor reinitialization"); + + // Stop the watchdog timer before clearing state to prevent double-registration + regular_timer_remove_callback(&s_interrupt_watchdog_timer); + + // Prevent spurious edges while the device is reconfigured + exti_disable(BOARD_CONFIG_ACCEL.accel_ints[0]); + + s_lsm6dso_initialized = false; + s_lsm6dso_running = false; + s_fifo_in_use = false; + s_sensor_health_ok = false; + s_consecutive_errors = 0; + + prv_lsm6dso_init(); + if (!s_lsm6dso_initialized) { + PBL_LOG_ERR("LSM6DSO: Forced reinit failed; sensor still unresponsive"); + return false; + } + + s_lsm6dso_state = (lsm6dso_state_t){0}; + + prv_lsm6dso_chase_target_state(); + + return s_lsm6dso_running; +} + +static void prv_lsm6dso_interrupt_watchdog_callback(void *data) { + PBL_LOG_DBG("LSM6DSO: Watchdog callback running"); + + // Check if interrupts have stopped for too long + const uint64_t now_ms = prv_get_timestamp_ms(); + const uint32_t interrupt_age_ms = prv_compute_age_ms(now_ms, s_last_interrupt_ms); + + PBL_LOG_DBG("LSM6DSO: Interrupt age: %" PRIu32 " ms", interrupt_age_ms); + + if ((interrupt_age_ms >= LSM6DSO_INTERRUPT_WATCHDOG_TIMEOUT_MS && s_lsm6dso_state.num_samples > 0) || + (interrupt_age_ms >= LSM6DSO_INTERRUPT_WATCHDOG_MS_NO_SAMPLES && s_lsm6dso_state.num_samples == 0)) { + PBL_LOG_WRN("LSM6DSO: Interrupt watchdog triggered - no interrupts for %" PRIu32 " ms, count=%lu; forcing reinit", + interrupt_age_ms, (unsigned long)s_interrupt_count); + // Mark sensor as unhealthy + s_sensor_health_ok = false; + + if (!s_lsm6dso_running) { + return; + } + + // Always escalate to forced reinitialization on watchdog expiry + if (prv_lsm6dso_force_reinit()) { + s_sensor_health_ok = true; + // Reset interrupt timestamp and count to avoid repeated watchdog triggers + s_last_interrupt_ms = now_ms; + s_interrupt_count = 0; + if (s_lsm6dso_running) { + prv_lsm6dso_configure_interrupts(); + } + } else { + PBL_LOG_ERR("LSM6DSO: Forced sensor reinitialization failed"); + } + } +} + // Sampling interval configuration static odr_xl_interval_t prv_get_odr_for_interval(uint32_t interval_us) { @@ -943,7 +964,7 @@ static odr_xl_interval_t prv_get_odr_for_interval(uint32_t interval_us) { static int32_t prv_lsm6dso_set_sampling_interval(uint32_t interval_us) { if (!s_lsm6dso_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Not initialized, cannot set sampling interval"); + PBL_LOG_ERR("LSM6DSO: Not initialized, cannot set sampling interval"); return -1; } @@ -961,13 +982,13 @@ static int32_t prv_lsm6dso_set_sampling_interval(uint32_t interval_us) { lsm6dso_odr_xl_t old_odr; if (lsm6dso_xl_data_rate_get(&lsm6dso_ctx, &old_odr) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: failed to read old ODR"); + PBL_LOG_ERR("LSM6DSO: failed to read old ODR"); return -1; } lsm6dso_xl_hm_mode_t old_power_mode; if (lsm6dso_xl_power_mode_get(&lsm6dso_ctx, &old_power_mode) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: failed to read old power mode"); + PBL_LOG_ERR("LSM6DSO: failed to read old power mode"); return -1; } @@ -977,7 +998,7 @@ static int32_t prv_lsm6dso_set_sampling_interval(uint32_t interval_us) { lsm6dso_xl_hm_mode_t new_power_mode = odr_interval.power_mode; if (old_odr == odr_interval.odr && old_power_mode == new_power_mode) { - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: we were already in that sampling mode, so we're good"); + PBL_LOG_DBG("LSM6DSO: we were already in that sampling mode, so we're good"); return odr_interval.interval_us; } @@ -985,20 +1006,20 @@ static int32_t prv_lsm6dso_set_sampling_interval(uint32_t interval_us) { // Section 6.2.1: you have to power down the accel before switching ULP // mode if (lsm6dso_xl_data_rate_set(&lsm6dso_ctx, LSM6DSO_XL_ODR_OFF) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: failed to power off before changing power mode"); + PBL_LOG_ERR("LSM6DSO: failed to power off before changing power mode"); return -1; } if (lsm6dso_xl_power_mode_set(&lsm6dso_ctx, new_power_mode) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: failed to set power mode"); + PBL_LOG_ERR("LSM6DSO: failed to set power mode"); return -1; } - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: switched to accelerometer power mode lsm6dso_xl_hm_mode_t = %d", new_power_mode); + PBL_LOG_DBG("LSM6DSO: switched to accelerometer power mode lsm6dso_xl_hm_mode_t = %d", new_power_mode); } if (lsm6dso_xl_data_rate_set(&lsm6dso_ctx, odr_interval.odr) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to set ODR"); + PBL_LOG_ERR("LSM6DSO: Failed to set ODR"); return -1; } @@ -1007,7 +1028,7 @@ static int32_t prv_lsm6dso_set_sampling_interval(uint32_t interval_us) { psleep(10); // Allow time for ODR change to stabilize } - PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Set sampling interval to %lu us (requested %lu us)", + PBL_LOG_DBG("LSM6DSO: Set sampling interval to %lu us (requested %lu us)", odr_interval.interval_us, interval_us); return odr_interval.interval_us; } @@ -1025,7 +1046,7 @@ static void prv_lsm6dso_read_samples(void) { // Drain FIFO uint16_t fifo_level = 0; if (lsm6dso_fifo_data_level_get(&lsm6dso_ctx, &fifo_level) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to read FIFO level"); + PBL_LOG_ERR("LSM6DSO: Failed to read FIFO level"); // Reset FIFO on communication error lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE); if (s_fifo_in_use) { @@ -1039,7 +1060,7 @@ static void prv_lsm6dso_read_samples(void) { // Prevent infinite loops on stuck FIFO if (fifo_level > LSM6DSO_FIFO_MAX_WATERMARK) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: FIFO level too high (%u), resetting", fifo_level); + PBL_LOG_ERR("LSM6DSO: FIFO level too high (%u), resetting", fifo_level); lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE); if (s_fifo_in_use) { lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_STREAM_MODE); @@ -1053,7 +1074,7 @@ static void prv_lsm6dso_read_samples(void) { for (uint16_t i = 0; i < fifo_level; ++i) { uint8_t raw_bytes[7]; if (lsm6dso_read_reg(&lsm6dso_ctx, LSM6DSO_FIFO_DATA_OUT_TAG, raw_bytes, sizeof(raw_bytes)) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to read FIFO sample (%u/%u)", i, fifo_level); + PBL_LOG_ERR("LSM6DSO: Failed to read FIFO sample (%u/%u)", i, fifo_level); // Reset FIFO on communication error lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE); if (s_fifo_in_use) { @@ -1088,36 +1109,23 @@ static void prv_lsm6dso_read_samples(void) { static uint8_t prv_lsm6dso_read_sample(AccelDriverSample *data) { if (!s_lsm6dso_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Not initialized, cannot read sample"); + PBL_LOG_ERR("LSM6DSO: Not initialized, cannot read sample"); return -1; } - // Check sensor health periodically - static uint32_t s_last_health_check_ms = 0; - uint32_t now_ms = prv_get_timestamp_ms(); - if (now_ms - s_last_health_check_ms > LSM6DSO_HEALTH_CHECK_INTERVAL_MS) { - s_last_health_check_ms = now_ms; - if (!prv_lsm6dso_health_check()) { - if (s_sensor_health_ok) { - // Health just degraded, attempt recovery - prv_lsm6dso_attempt_recovery(); - } - s_sensor_health_ok = false; - return -1; - } - } - // TODO: Handle case when accelerometer is not enabled or running (by briefly // enabling it. int16_t accel_raw[3]; if (lsm6dso_acceleration_raw_get(&lsm6dso_ctx, accel_raw) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to read accelerometer data"); + PBL_LOG_ERR("LSM6DSO: Failed to read accelerometer data"); return -1; } - data->x = prv_get_axis_projection_mg(X_AXIS, accel_raw); - data->y = prv_get_axis_projection_mg(Y_AXIS, accel_raw); + data->x = s_rotated_180 ? prv_get_axis_projection_mg(X_AXIS, accel_raw) * -1 + : prv_get_axis_projection_mg(X_AXIS, accel_raw); + data->y = s_rotated_180 ? prv_get_axis_projection_mg(Y_AXIS, accel_raw) * -1 + : prv_get_axis_projection_mg(Y_AXIS, accel_raw); data->z = prv_get_axis_projection_mg(Z_AXIS, accel_raw); data->timestamp_us = prv_get_timestamp_ms() * 1000; @@ -1187,254 +1195,6 @@ static uint32_t prv_compute_age_ms(uint64_t now_ms, uint64_t then_ms) { return (uint32_t)delta; } -// Helper to grab one raw sample set (blocking) and convert to mg (board axis adjusted) -static int prv_get_sample_mg(int16_t out_mg[3]) { - int16_t raw[3]; - if (lsm6dso_acceleration_raw_get(&lsm6dso_ctx, raw) != 0) { - return -1; - } - out_mg[0] = prv_get_axis_projection_mg(X_AXIS, raw); - out_mg[1] = prv_get_axis_projection_mg(Y_AXIS, raw); - out_mg[2] = prv_get_axis_projection_mg(Z_AXIS, raw); - return 0; -} - -// Self-test implementation -// -// Reference: LSM6DSO datasheet / application notes. Procedure (simplified): -// 1. Configure XL @ 52Hz, FS=4g. Collect a small set of samples (ST disabled). -// 2. Enable self-test (positive) and wait for output to settle. Collect samples. -// 3. Compute absolute delta per axis (ON - OFF) in mg and compare against threshold. -// 4. Disable self-test and restore previous configuration. -// -// We only enforce a minimum delta (lower bound) which indicates the internal actuation worked. -// Chosen conservative thresholds (mg) based on typical min values from datasheet; may be tuned. -// - -bool accel_run_selftest(void) { - if (!s_lsm6dso_initialized) { - // Attempt init if not already done - prv_lsm6dso_init(); - if (!s_lsm6dso_initialized) { - return false; - } - } - - // Save current target/current state so we can restore later - const lsm6dso_state_t saved_state = s_lsm6dso_state; - const lsm6dso_state_t saved_target = s_lsm6dso_state_target; - const bool saved_enabled = s_lsm6dso_enabled; - - // Ensure accelerometer enabled & running at known configuration - s_lsm6dso_enabled = true; - s_lsm6dso_state_target.sampling_interval_us = 19231; // ~52Hz per mapping - s_lsm6dso_state_target.num_samples = 0; // disable callbacks during test - s_lsm6dso_state_target.shake_detection_enabled = false; - s_lsm6dso_state_target.double_tap_detection_enabled = false; - prv_lsm6dso_chase_target_state(); - - // Force FS=4g (required for mg conversion helper used elsewhere) - lsm6dso_xl_full_scale_set(&lsm6dso_ctx, LSM6DSO_4g); - - // Collect baseline (self-test disabled) - (void)lsm6dso_xl_self_test_set(&lsm6dso_ctx, LSM6DSO_XL_ST_DISABLE); - psleep(100); // allow settling - int32_t sum_off[3] = {0}; - const int kNumSamples = 5; - int collected = 0; - for (int i = 0; i < kNumSamples; ++i) { - int16_t mg[3]; - if (prv_get_sample_mg(mg) != 0) { - break; - } - sum_off[0] += mg[0]; - sum_off[1] += mg[1]; - sum_off[2] += mg[2]; - ++collected; - psleep(20); // ~1 sample period @52Hz (19ms) - } - if (collected == 0) { - // restore state - s_lsm6dso_state = saved_state; - s_lsm6dso_state_target = saved_target; - s_lsm6dso_enabled = saved_enabled; - prv_lsm6dso_chase_target_state(); - return false; - } - int32_t avg_off[3] = {sum_off[0] / collected, sum_off[1] / collected, sum_off[2] / collected}; - - // Enable positive self-test stimulus - if (lsm6dso_xl_self_test_set(&lsm6dso_ctx, LSM6DSO_XL_ST_POSITIVE) != 0) { - // restore and fail - s_lsm6dso_state = saved_state; - s_lsm6dso_state_target = saved_target; - s_lsm6dso_enabled = saved_enabled; - prv_lsm6dso_chase_target_state(); - return false; - } - psleep(100); // settling per app note - - int32_t sum_on[3] = {0}; - collected = 0; - for (int i = 0; i < kNumSamples; ++i) { - int16_t mg[3]; - if (prv_get_sample_mg(mg) != 0) { - break; - } - sum_on[0] += mg[0]; - sum_on[1] += mg[1]; - sum_on[2] += mg[2]; - ++collected; - psleep(20); - } - int32_t avg_on[3] = {0}; - if (collected > 0) { - avg_on[0] = sum_on[0] / collected; - avg_on[1] = sum_on[1] / collected; - avg_on[2] = sum_on[2] / collected; - } - - // Disable self-test - lsm6dso_xl_self_test_set(&lsm6dso_ctx, LSM6DSO_XL_ST_DISABLE); - - // Thresholds (mg) - conservative lower bounds - const int kMinDeltaXY_mg = 90; // datasheet min typically ~90 mg - const int kMinDeltaZ_mg = 90; // Z similar / slightly different; keep same for simplicity - - bool pass = true; - int32_t delta_x = ABS(avg_on[0] - avg_off[0]); - int32_t delta_y = ABS(avg_on[1] - avg_off[1]); - int32_t delta_z = ABS(avg_on[2] - avg_off[2]); - if (delta_x < kMinDeltaXY_mg) { - pass = false; - } - if (delta_y < kMinDeltaXY_mg) { - pass = false; - } - if (delta_z < kMinDeltaZ_mg) { - pass = false; - } - - PBL_LOG(LOG_LEVEL_DEBUG, - "LSM6DSO: Self-test deltas mg X=%" PRId32 " Y=%" PRId32 " Z=%" PRId32 - " (min XY=%d Z=%d) => %s", - delta_x, delta_y, delta_z, kMinDeltaXY_mg, kMinDeltaZ_mg, pass ? "PASS" : "FAIL"); - - // Restore previous configuration (best-effort) - s_lsm6dso_state = saved_state; - s_lsm6dso_state_target = saved_target; - s_lsm6dso_enabled = saved_enabled; - prv_lsm6dso_chase_target_state(); - - return pass; -} - -// Health monitoring and recovery functions - -// Health check function to verify sensor is still responsive -static bool prv_lsm6dso_health_check(void) { - uint8_t whoami; - if (lsm6dso_device_id_get(&lsm6dso_ctx, &whoami) != 0) { - PBL_LOG(LOG_LEVEL_WARNING, "LSM6DSO: Health check failed - cannot read WHO_AM_I"); - return false; - } - if (whoami != LSM6DSO_ID) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Health check failed - wrong WHO_AM_I (0x%02x)", whoami); - return false; - } - return true; -} - -// Recovery mechanism for unresponsive sensor -static bool prv_lsm6dso_attempt_recovery(void) { - PBL_LOG(LOG_LEVEL_WARNING, "LSM6DSO: Attempting sensor recovery"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_attempted_recoveries++; -#endif - - // Reset the sensor - if (lsm6dso_reset_set(&lsm6dso_ctx, PROPERTY_ENABLE) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to reset sensor during recovery"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_log_events++; -#endif - return false; - } - - // Wait for reset to complete - uint8_t rst; - int timeout = 100; // 100ms timeout - do { - psleep(1); - if (lsm6dso_reset_get(&lsm6dso_ctx, &rst) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to check reset status during recovery"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_log_events++; -#endif - return false; - } - timeout--; - } while (rst && timeout > 0); - - if (timeout == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Reset timeout during recovery"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_log_events++; -#endif - return false; - } - - // Re-initialize basic settings - if (lsm6dso_i3c_disable_set(&lsm6dso_ctx, LSM6DSO_I3C_DISABLE) != 0 || - lsm6dso_block_data_update_set(&lsm6dso_ctx, PROPERTY_ENABLE) != 0 || - lsm6dso_auto_increment_set(&lsm6dso_ctx, PROPERTY_ENABLE) != 0 || - lsm6dso_xl_full_scale_set(&lsm6dso_ctx, LSM6DSO_4g) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Failed to restore basic settings during recovery"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_log_events++; -#endif - return false; - } - - // Reset FIFO to bypass mode to clear any stuck state - lsm6dso_fifo_mode_set(&lsm6dso_ctx, LSM6DSO_BYPASS_MODE); - - // Reset interrupt configuration to ensure clean state - lsm6dso_pin_int1_route_t int1_routes = {0}; // All disabled - lsm6dso_pin_int1_route_set(&lsm6dso_ctx, int1_routes); - - // Configure pulsed interrupts to prevent lockup - lsm6dso_data_ready_mode_set(&lsm6dso_ctx, LSM6DSO_DRDY_PULSED); - lsm6dso_int_notification_set(&lsm6dso_ctx, LSM6DSO_ALL_INT_PULSED); - - // Clear any pending interrupt sources - lsm6dso_all_sources_t clear_sources; - lsm6dso_all_sources_get(&lsm6dso_ctx, &clear_sources); - - // Reset external interrupt pin - exti_disable(BOARD_CONFIG_ACCEL.accel_ints[0]); - psleep(2); - exti_enable(BOARD_CONFIG_ACCEL.accel_ints[0]); - - s_consecutive_errors = 0; - s_last_successful_read_ms = prv_get_timestamp_ms(); - - // Final health check - if (!prv_lsm6dso_health_check()) { - PBL_LOG(LOG_LEVEL_ERROR, "LSM6DSO: Recovery failed - health check failed"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_log_events++; -#endif - return false; - } - - PBL_LOG(LOG_LEVEL_INFO, "LSM6DSO: Sensor recovery successful"); -#if CAPABILITY_NEEDS_FIRM_579_STATS - metric_firm_579_log_events++; -#endif - s_recovery_success_count++; - return true; -} void lsm6dso_get_diagnostics(Lsm6dsoDiagnostics *diagnostics) { if (!diagnostics) { @@ -1457,8 +1217,6 @@ void lsm6dso_get_diagnostics(Lsm6dsoDiagnostics *diagnostics) { diagnostics->i2c_error_count = s_i2c_error_count; diagnostics->consecutive_error_count = s_consecutive_errors; - diagnostics->watchdog_event_count = s_watchdog_event_count; - diagnostics->recovery_success_count = s_recovery_success_count; diagnostics->interrupt_count = s_interrupt_count; diagnostics->wake_event_count = s_wake_event_count; diagnostics->double_tap_event_count = s_double_tap_event_count; @@ -1482,3 +1240,7 @@ void lsm6dso_get_diagnostics(Lsm6dsoDiagnostics *diagnostics) { diagnostics->state_flags = flags; } + +void accel_set_rotated(bool rotated) { + s_rotated_180 = rotated; +} \ No newline at end of file diff --git a/src/fw/drivers/imu/lsm6dso/lsm6dso.h b/src/fw/drivers/imu/lsm6dso/lsm6dso.h index 90d856fcc5..0b0766c098 100644 --- a/src/fw/drivers/imu/lsm6dso/lsm6dso.h +++ b/src/fw/drivers/imu/lsm6dso/lsm6dso.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Matthew Wardrop - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Matthew Wardrop */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -55,4 +42,4 @@ void lsm6dso_power_up(void); void lsm6dso_power_down(void); //! Retrieve a snapshot of sensor diagnostics for telemetry. -void lsm6dso_get_diagnostics(Lsm6dsoDiagnostics *diagnostics); +void lsm6dso_get_diagnostics(Lsm6dsoDiagnostics *diagnostics); \ No newline at end of file diff --git a/src/fw/drivers/imu/mag3110/mag3110.c b/src/fw/drivers/imu/mag3110/mag3110.c deleted file mode 100644 index ffee50c0a3..0000000000 --- a/src/fw/drivers/imu/mag3110/mag3110.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mag3110.h" - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/mag.h" -#include "drivers/periph_config.h" -#include "kernel/events.h" -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" -#include "kernel/util/sleep.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include -#include - -static PebbleMutex *s_mag_mutex; - -static bool s_initialized = false; - -static int s_use_refcount = 0; - -// MAG3110 Register Address Map -#define DR_STATUS_REG 0x00 -#define OUT_X_MSB_REG 0x01 // a 6-byte read here will return X, Y, Z data -#define WHO_AM_I_REG 0x07 -#define SYSMOD_REG 0x08 -#define CTRL_REG1 0x10 -#define CTRL_REG2 0x11 - -static bool mag3110_read(uint8_t reg_addr, uint8_t data_len, uint8_t *data) { - return i2c_read_register_block(I2C_MAG3110, reg_addr, data_len, data); -} - -static bool mag3110_write(uint8_t reg_addr, uint8_t data) { - return i2c_write_register_block(I2C_MAG3110, reg_addr, 1, &data); -} - -static void mag3110_interrupt_handler(bool *should_context_switch) { - if (s_use_refcount == 0) { - // Spurious interrupt firing after we've already turned off the mag. Just ignore. - return; - } - - // TODO: May want to use a timer, lowers worst case latency - PebbleEvent e = { - .type = PEBBLE_ECOMPASS_SERVICE_EVENT, - }; - - *should_context_switch = event_put_isr(&e); -} - -//! Move the mag into standby mode, which is a low power mode where we're not actively sampling -//! the sensor or firing interrupts. -static bool prv_enter_standby_mode(void) { - // Ask to enter standby mode - if (!mag3110_write(CTRL_REG1, 0x00)) { - return false; - } - - // Wait for the sysmod register to read that we're now in standby mode. This can take up to - // 1/ODR to respond. Since we only support speeds as slow as 5Hz, that means we may be waiting - // for up to 200ms for this part to become ready. - const int NUM_ATTEMPTS = 300; // 200ms + some padding for safety - for (int i = 0; i < NUM_ATTEMPTS; ++i) { - uint8_t sysmod = 0; - if (!mag3110_read(SYSMOD_REG, 1, &sysmod)) { - return false; - } - - if (sysmod == 0) { - // We're done and we're now in standby! - return true; - } - - // Wait at least 1ms before asking again - psleep(2); - } - - return false; -} - -// Ask the compass for a 8-bit value that's programmed into the IC at the -// factory. Useful as a sanity check to make sure everything came up properly. -bool mag3110_check_whoami(void) { - static const uint8_t COMPASS_WHOAMI_BYTE = 0xc4; - - uint8_t whoami = 0; - - mag_use(); - mag3110_read(WHO_AM_I_REG, 1, &whoami); - mag_release(); - - PBL_LOG(LOG_LEVEL_DEBUG, "Read compass whoami byte 0x%x, expecting 0x%x", - whoami, COMPASS_WHOAMI_BYTE); - - return (whoami == COMPASS_WHOAMI_BYTE); -} - -void mag3110_init(void) { - if (s_initialized) { - return; - } - s_mag_mutex = mutex_create(); - - s_initialized = true; - - if (!mag3110_check_whoami()) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to query Mag"); - } - gpio_input_init(&BOARD_CONFIG_MAG.mag_int_gpio); - - exti_configure_pin(BOARD_CONFIG_MAG.mag_int, ExtiTrigger_Rising, mag3110_interrupt_handler); -} - -void mag_use(void) { - PBL_ASSERTN(s_initialized); - - mutex_lock(s_mag_mutex); - - if (s_use_refcount == 0) { - i2c_use(I2C_MAG3110); - exti_enable(BOARD_CONFIG_MAG.mag_int); - } - ++s_use_refcount; - - mutex_unlock(s_mag_mutex); -} - -void mag_release(void) { - PBL_ASSERTN(s_initialized && s_use_refcount != 0); - - mutex_lock(s_mag_mutex); - - --s_use_refcount; - if (s_use_refcount == 0) { - // We need to put the magnetometer into standby mode and read the data register to reset - // the state so it's ready for next time. - prv_enter_standby_mode(); - - uint8_t raw_data[7]; - // DR_STATUS_REG is immediately before data registers - mag3110_read(DR_STATUS_REG, sizeof(raw_data), raw_data); - - // Now we can actually remove power and disable the interrupt - i2c_release(I2C_MAG3110); - exti_disable(BOARD_CONFIG_MAG.mag_int); - } - - mutex_unlock(s_mag_mutex); -} - -// aligns magnetometer data with the coordinate system we have adopted -// for the watch -static int16_t align_coord_system(int axis, uint8_t *raw_data) { - int offset = 2 * BOARD_CONFIG_MAG.mag_config.axes_offsets[axis]; - bool do_invert = BOARD_CONFIG_MAG.mag_config.axes_inverts[axis]; - int16_t mag_field_strength = ((raw_data[offset] << 8) | raw_data[offset + 1]); - mag_field_strength *= (do_invert ? -1 : 1); - return (mag_field_strength); -} - -// callers responsibility to know if there is valid data to be read -MagReadStatus mag_read_data(MagData *data) { - mutex_lock(s_mag_mutex); - - if (s_use_refcount == 0) { - mutex_unlock(s_mag_mutex); - return (MagReadMagOff); - } - - MagReadStatus rv = MagReadSuccess; - uint8_t raw_data[7]; - - // DR_STATUS_REG is immediately before data registers - if (!mag3110_read(DR_STATUS_REG, sizeof(raw_data), raw_data)) { - rv = MagReadCommunicationFail; - goto done; - } - - // TODO: shouldn't happen at low sample rate, but handle case where some data - // is overwritten - if ((raw_data[0] & 0xf0) != 0) { - PBL_LOG(LOG_LEVEL_INFO, "Some Mag Sample Data was overwritten, " - "dr_status=0x%x", raw_data[0]); - rv = MagReadClobbered; // we still need to read the data to clear the int - } - - // map raw data to watch coord system - data->x = align_coord_system(0, &raw_data[1]); - data->y = align_coord_system(1, &raw_data[1]); - data->z = align_coord_system(2, &raw_data[1]); - -done: - mutex_unlock(s_mag_mutex); - return (rv); -} - -bool mag_change_sample_rate(MagSampleRate rate) { - mutex_lock(s_mag_mutex); - - if (s_use_refcount == 0) { - mutex_unlock(s_mag_mutex); - return (true); - } - - bool success = false; - - // Enter standby state since we can only change sample rate in this mode. - if (!prv_enter_standby_mode()) { - goto done; - } - - // See Table 25 in the data sheet for these values for the CTRL_REG1 register. We leave the - // oversampling values at zero and just set the data rate bits. - uint8_t new_sample_rate_value = 0; - switch(rate) { - case MagSampleRate20Hz: - new_sample_rate_value = 0x1 << 6; - break; - case MagSampleRate5Hz: - new_sample_rate_value = 0x2 << 6; - break; - } - - // Write the new sample rate as well as set the bottom bit of the ctrl register to put us into - // active mode. - if (!mag3110_write(CTRL_REG1, new_sample_rate_value | 0x01)) { - goto done; - } - - success = true; -done: - mutex_unlock(s_mag_mutex); - - return (success); -} - -void mag_start_sampling(void) { - mag_use(); - - // enable automatic magnetic sensor reset & RAW mode - mag3110_write(CTRL_REG2, 0xA0); - - mag_change_sample_rate(MagSampleRate5Hz); -} diff --git a/src/fw/drivers/imu/mag3110/mag3110.h b/src/fw/drivers/imu/mag3110/mag3110.h deleted file mode 100644 index 24d99103f3..0000000000 --- a/src/fw/drivers/imu/mag3110/mag3110.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void mag3110_init(void); - diff --git a/src/fw/drivers/imu/mag_null.c b/src/fw/drivers/imu/mag_null.c deleted file mode 100644 index 7044a26409..0000000000 --- a/src/fw/drivers/imu/mag_null.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Null Magnetometer Driver - -#if CAPABILITY_HAS_MAGNETOMETER -#error This driver is only intended for boards without a magnetometer -#endif - -#include "drivers/mag.h" - -void mag_use(void) { -} - -void mag_start_sampling(void) { -} - -void mag_release(void) { -} - -MagReadStatus mag_read_data(MagData *data) { - return MagReadNoMag; -} - -bool mag_change_sample_rate(MagSampleRate rate) { - return false; -} diff --git a/src/fw/drivers/imu/mmc5603nj/mmc5603nj.c b/src/fw/drivers/imu/mmc5603nj/mmc5603nj.c index 5bb173acb7..c2610d4dbf 100644 --- a/src/fw/drivers/imu/mmc5603nj/mmc5603nj.c +++ b/src/fw/drivers/imu/mmc5603nj/mmc5603nj.c @@ -1,20 +1,6 @@ -/* - * Copyright 2025 Matthew Wardrop - * Copyright 2025 Bob Wei - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +/* SPDX-FileCopyrightText: 2025 Matthew Wardrop */ +/* SPDX-FileCopyrightText: 2025 Bob Wei */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/i2c.h" #include "drivers/mag.h" @@ -23,12 +9,14 @@ #include "os/mutex.h" #include "system/logging.h" #include "system/passert.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "util/math.h" #include "mmc5603nj.h" #include "registers.h" +PBL_LOG_MODULE_DEFINE(driver_mag_mmc5603nj, CONFIG_DRIVER_IMU_LOG_LEVEL); + // Forward declarations of private methods static bool prv_mmc5603nj_read(uint8_t reg_addr, uint8_t data_len, uint8_t *data); static bool prv_mmc5603nj_write(uint8_t reg_addr, uint8_t data); @@ -36,8 +24,10 @@ static bool prv_mmc5603nj_init(void); static bool prv_mmc5603nj_check_whoami(void); static bool prv_mmc5603nj_reset(void); static bool prv_mmc5603nj_set_sample_rate_hz(uint8_t rate_hz); +#ifndef CONFIG_RECOVERY_FW static bool prv_configure_polling(void); static void prv_mmc5603nj_polling_callback(void *data); +#endif static bool prv_mmc5603nj_is_data_ready(void); static MagReadStatus prv_mmc5603nj_get_sample(MagData *sample); typedef enum { @@ -52,18 +42,23 @@ static bool s_initialized = false; static int s_use_refcount = 0; static PebbleMutex *s_mag_mutex; static uint8_t s_sample_rate_hz = 0; +#ifndef CONFIG_RECOVERY_FW static TimerID s_polling_timer = TIMER_INVALID_ID; static uint16_t s_polling_interval_ms = 0; +#endif static bool s_measurement_ready = false; +// watch rotation +static bool s_rotated_180 = false; + // MMC5603NJ entrypoints -void mmc5603nj_init(void) { +void mag_init(void) { s_mag_mutex = mutex_create(); if (prv_mmc5603nj_init()) { - PBL_LOG(LOG_LEVEL_DEBUG, "MMC5603NJ: Initialization complete"); + PBL_LOG_DBG("MMC5603NJ: Initialization complete"); } else { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Initialization failed"); + PBL_LOG_ERR("MMC5603NJ: Initialization failed"); } } @@ -82,12 +77,13 @@ void mag_start_sampling(void) { } void mag_release(void) { - PBL_ASSERTN(s_initialized && s_use_refcount != 0); + PBL_ASSERTN(s_initialized); mutex_lock(s_mag_mutex); + PBL_ASSERTN(s_use_refcount != 0); --s_use_refcount; if (s_use_refcount == 0) { if (!prv_mmc5603nj_set_sample_rate_hz(0)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to disable sensor on release"); + PBL_LOG_ERR("MMC5603NJ: Failed to disable sensor on release"); } } mutex_unlock(s_mag_mutex); @@ -133,11 +129,11 @@ static bool prv_mmc5603nj_read(uint8_t reg_addr, uint8_t data_len, uint8_t *data i2c_use(I2C_MMC5603NJ); bool rv = i2c_write_block(I2C_MMC5603NJ, 1, ®_addr); if (!rv) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: I2C write failed for register 0x%02x", reg_addr); + PBL_LOG_ERR("MMC5603NJ: I2C write failed for register 0x%02x", reg_addr); } rv = i2c_read_block(I2C_MMC5603NJ, data_len, data); if (!rv) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: I2C data read failed for register 0x%02x", reg_addr); + PBL_LOG_ERR("MMC5603NJ: I2C data read failed for register 0x%02x", reg_addr); } i2c_release(I2C_MMC5603NJ); return rv; @@ -148,7 +144,7 @@ static bool prv_mmc5603nj_write(uint8_t reg_addr, uint8_t data) { uint8_t d[2] = {reg_addr, data}; bool rv = i2c_write_block(I2C_MMC5603NJ, 2, d); if (!rv) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: I2C write failed for register 0x%02x", reg_addr); + PBL_LOG_ERR("MMC5603NJ: I2C write failed for register 0x%02x", reg_addr); } i2c_release(I2C_MMC5603NJ); return rv; @@ -162,13 +158,13 @@ static bool prv_mmc5603nj_init(void) { } if (!prv_mmc5603nj_check_whoami()) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: WHO_AM_I check failed. Wrong device?"); + PBL_LOG_ERR("MMC5603NJ: WHO_AM_I check failed. Wrong device?"); return false; } // Reset the device if (!prv_mmc5603nj_reset()) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to reset"); + PBL_LOG_ERR("MMC5603NJ: Failed to reset"); return false; } @@ -190,21 +186,21 @@ static bool prv_mmc5603nj_reset(void) { // Software reset if (!prv_mmc5603nj_write(MMC5603NJ_REG_CTRL1, MMC5603NJ_CTRL1_BANDWIDTH_6ms6 | MMC5603NJ_CTRL1_SW_RESET)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to reset device."); + PBL_LOG_ERR("MMC5603NJ: Failed to reset device."); return false; } psleep(MMC5603NJ_SW_RESET_DELAY_MS); // Set operation if (!prv_mmc5603nj_write(MMC5603NJ_REG_CTRL0, MMC5603NJ_CTRL0_DO_SET)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to set coils."); + PBL_LOG_ERR("MMC5603NJ: Failed to set coils."); return false; } psleep(MMC5603NJ_SET_DELAY_MS); // Reset operation if (!prv_mmc5603nj_write(MMC5603NJ_REG_CTRL0, MMC5603NJ_CTRL0_DO_RESET)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to reset coils."); + PBL_LOG_ERR("MMC5603NJ: Failed to reset coils."); return false; } psleep(MMC5603NJ_SET_DELAY_MS); @@ -218,7 +214,7 @@ bool prv_mmc5603nj_set_sample_rate_hz(uint8_t rate_hz) { return true; } - PBL_LOG(LOG_LEVEL_DEBUG, "MMC5603NJ: Setting sample rate to %d Hz", rate_hz); + PBL_LOG_DBG("MMC5603NJ: Setting sample rate to %d Hz", rate_hz); // Reset device runtime status (disabling continuous mode/etc) if (!prv_mmc5603nj_write(MMC5603NJ_REG_CTRL2, 0x00)) { @@ -232,14 +228,14 @@ bool prv_mmc5603nj_set_sample_rate_hz(uint8_t rate_hz) { if (rate_hz > 0) { // Set new sampling rate if (!prv_mmc5603nj_write(MMC5603NJ_REG_ODR, rate_hz)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to update ODR."); + PBL_LOG_ERR("MMC5603NJ: Failed to update ODR."); return false; } // Retrigger calculation of measurements rates if (!prv_mmc5603nj_write(MMC5603NJ_REG_CTRL0, MMC5603NJ_CTRL0_AUTO_SR_EN | MMC5603NJ_CTRL0_CMM_FREQ_EN)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to trigger measurement calculation update."); + PBL_LOG_ERR("MMC5603NJ: Failed to trigger measurement calculation update."); return false; } @@ -247,19 +243,24 @@ bool prv_mmc5603nj_set_sample_rate_hz(uint8_t rate_hz) { if (!prv_mmc5603nj_write(MMC5603NJ_REG_CTRL2, MMC5603NJ_CTRL2_AUTOSET_PRD_100 | MMC5603NJ_CTRL2_PRD_SET_EN | MMC5603NJ_CTRL2_CMM_EN)) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to start continuous mode."); + PBL_LOG_ERR("MMC5603NJ: Failed to start continuous mode."); return false; } } s_sample_rate_hz = rate_hz; + +#ifndef CONFIG_RECOVERY_FW if (!prv_configure_polling()) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to configure polling"); + PBL_LOG_ERR("MMC5603NJ: Failed to configure polling"); return false; } +#endif + return true; } +#ifndef CONFIG_RECOVERY_FW // Configure polling (to simulate data-ready interrupts) static bool prv_configure_polling(void) { @@ -278,7 +279,7 @@ static bool prv_configure_polling(void) { if (polling_interval_ms > 0) { s_polling_timer = new_timer_create(); if (s_polling_timer == TIMER_INVALID_ID) { - PBL_LOG(LOG_LEVEL_ERROR, "MMC5603NJ: Failed to create polling timer"); + PBL_LOG_ERR("MMC5603NJ: Failed to create polling timer"); return false; } new_timer_start(s_polling_timer, polling_interval_ms, prv_mmc5603nj_polling_callback, NULL, @@ -303,6 +304,7 @@ static void prv_mmc5603nj_polling_callback(void *data) { event_put(&e); } } +#endif // Samples bool prv_mmc5603nj_is_data_ready(void) { @@ -341,13 +343,26 @@ static MagReadStatus prv_mmc5603nj_get_sample(MagData *sample) { (int16_t)(raw_axis_value - (1 << 15)); // offset by 2^15 for uint -> int alignment } - sample->x = prv_get_axis_projection(X_AXIS, raw_vector); - sample->y = prv_get_axis_projection(Y_AXIS, raw_vector); - sample->z = prv_get_axis_projection(Z_AXIS, raw_vector); + // Convert raw counts to milliGauss (mG) + // Sensitivity: 1024 counts/G (16-bit operation) + // Formula: mG = (raw_counts * 1000) / 1024 + int16_t raw_x = s_rotated_180 ? prv_get_axis_projection(X_AXIS, raw_vector) * -1 + : prv_get_axis_projection(X_AXIS, raw_vector); + int16_t raw_y = s_rotated_180 ? prv_get_axis_projection(Y_AXIS, raw_vector) * -1 + : prv_get_axis_projection(Y_AXIS, raw_vector); + int16_t raw_z = prv_get_axis_projection(Z_AXIS, raw_vector); + + sample->x = ((int32_t)raw_x * 1000) / 1024; + sample->y = ((int32_t)raw_y * 1000) / 1024; + sample->z = ((int32_t)raw_z * 1000) / 1024; return MagReadSuccess; } +void mag_set_rotated(bool rotated) { + s_rotated_180 = rotated; +} + static int16_t prv_get_axis_projection(axis_t axis, int16_t *raw_vector) { uint8_t axis_offset = BOARD_CONFIG_MAG.mag_config.axes_offsets[axis]; bool invert = BOARD_CONFIG_MAG.mag_config.axes_inverts[axis]; diff --git a/src/fw/drivers/imu/mmc5603nj/mmc5603nj.h b/src/fw/drivers/imu/mmc5603nj/mmc5603nj.h index f28379ca90..18a508b00a 100644 --- a/src/fw/drivers/imu/mmc5603nj/mmc5603nj.h +++ b/src/fw/drivers/imu/mmc5603nj/mmc5603nj.h @@ -1,21 +1,9 @@ -/* - * Copyright 2025 Matthew Wardrop - * Copyright 2025 Bob Wei - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +/* SPDX-FileCopyrightText: 2025 Matthew Wardrop */ +/* SPDX-FileCopyrightText: 2025 Bob Wei */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once void mmc5603nj_init(void); + +void mag_set_rotated(bool rotated); diff --git a/src/fw/drivers/imu/mmc5603nj/registers.h b/src/fw/drivers/imu/mmc5603nj/registers.h index 9fa3ed0827..a746c9910c 100644 --- a/src/fw/drivers/imu/mmc5603nj/registers.h +++ b/src/fw/drivers/imu/mmc5603nj/registers.h @@ -1,19 +1,5 @@ -/* - * Copyright 2025 Matthew Wardrop - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +/* SPDX-FileCopyrightText: 2025 Matthew Wardrop */ +/* SPDX-License-Identifier: Apache-2.0 */ # pragma once diff --git a/src/fw/drivers/imu/wscript_build b/src/fw/drivers/imu/wscript_build new file mode 100644 index 0000000000..539b7e8175 --- /dev/null +++ b/src/fw/drivers/imu/wscript_build @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +# Accelerometer + +accel_sources = [] +accel_use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_QEMU: + accel_sources.append('../qemu/qemu_accel.c') + accel_use.append('driver_qemu_serial') +elif bld.env.CONFIG_ACCEL_LIS2DW12: + accel_sources.append('lis2dw12/lis2dw12.c') +elif bld.env.CONFIG_ACCEL_LSM6DSO: + accel_sources.append('lsm6dso/lsm6dso.c') + accel_use.append('hal_lsm6dso') + +if accel_sources: + bld.objects( + name='driver_accel', + source=accel_sources, + use=accel_use, + ) + bld.env.DRIVERS.append('driver_accel') + +# Magnetometer + +mag_sources = [] +mag_use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_MAG_MMC5603NJ: + mag_sources.append('mmc5603nj/mmc5603nj.c') + +if mag_sources: + bld.objects( + name='driver_mag', + source=mag_sources, + use=mag_use, + ) + bld.env.DRIVERS.append('driver_mag') diff --git a/src/fw/drivers/led_controller.h b/src/fw/drivers/led_controller.h deleted file mode 100644 index 71ba38f34e..0000000000 --- a/src/fw/drivers/led_controller.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! FIXME: These colors are not gamma-corrected, so will not match normal -//! RGB color values, 100 is max brightness -#define LED_BLACK 0x00000000 -#define LED_RED 0x00640000 -#define LED_GREEN 0x00006400 -#define LED_BLUE 0x00000064 -#define LED_ORANGE 0x00285F00 -#define LED_WHITE 0x00FFFFFF -#define LED_WARM_WHITE 0x00F0D0B0 - -#define LED_DIM_GREEN 0x00003C00 // Low power version for charging indicator -#define LED_DIM_ORANGE 0x000F2300 // Low power version for charging indicator - -void led_controller_init(void); - -// Not sure these are the correct functions to define atm, but it is fine as a first pass -void led_controller_backlight_set_brightness(uint8_t brightness); - -void led_controller_rgb_set_color(uint32_t rgb_color); - -uint32_t led_controller_rgb_get_color(void); - diff --git a/src/fw/drivers/led_controller/is31fl3196.c b/src/fw/drivers/led_controller/is31fl3196.c deleted file mode 100644 index f11e363c1b..0000000000 --- a/src/fw/drivers/led_controller/is31fl3196.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/led_controller.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "system/logging.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -enum { - RegShutdown = 0x00, - RegLedCtrl = 0x01, - RegConfig1 = 0x03, - RegConfig2 = 0x04, - RegRampingMode = 0x05, - RegBreathingMark = 0x06, - RegPwmOut1 = 0x07, - RegPwmOut2 = 0x08, - RegPwmOut3 = 0x09, - RegPwmOut4 = 0x0a, - RegPwmOut5 = 0x0b, - RegPwmOut6 = 0x0c, - RegDataUpdate = 0x10, - RegT0Out1 = 0x11, - RegT0Out2 = 0x12, - RegT0Out3 = 0x13, - RegT0Out4 = 0x14, - RegT0Out5 = 0x15, - RegT0Out6 = 0x16, - RegT1T3Rgb1 = 0x1a, - RegT1T3Rgb2 = 0x1b, - RegT4Out1 = 0x1d, - RegT4Out2 = 0x1e, - RegT4Out3 = 0x1f, - RegT4Out4 = 0x20, - RegT4Out5 = 0x21, - RegT4Out6 = 0x22, - RegTimeUpdate = 0x26, - RegReset = 0xff -}; - -static bool s_backlight_off; -static bool s_initialized = false; -static uint32_t s_rgb_current_color = LED_BLACK; - -static bool write_register(uint8_t register_address, uint8_t value) { - return i2c_write_register(I2C_LED, register_address, value); -} - -// used to bring hardware shutdown pin on led controller low, this brings down our shutdown current -static void prv_shutdown(bool shutdown) { - periph_config_acquire_lock(); - gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, !shutdown); - periph_config_release_lock(); -} - -static void prv_init_pins(void) { - periph_config_acquire_lock(); - gpio_output_init(&BOARD_CONFIG_BACKLIGHT.ctl, GPIO_OType_PP, GPIO_Speed_2MHz); - gpio_output_set(&BOARD_CONFIG_BACKLIGHT.ctl, false); - periph_config_release_lock(); -} - -void led_controller_init(void) { - PBL_ASSERTN(BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C); - - prv_init_pins(); - prv_shutdown(false); - - i2c_use(I2C_LED); - - // reset the LED controller - if (!write_register(RegReset, 0xaa)) { - PBL_LOG(LOG_LEVEL_ERROR, "LED Controller is MIA"); - goto cleanup; - } else { - s_initialized = true; - } - - // take the led controller out of shutdown - write_register(RegShutdown, 0x01); - - // set config1 to 0x00 (PWM, Audio disable, AGC enable, AGC fast mode) - write_register(RegConfig1, 0x00); - - // set config2 to 0x40 (master control, 25mA drive, 0dB gain) - write_register(RegConfig2, 0x70); - - // set ramping_mode to 0x00 (disable ramping) - // TODO: this is potentially quite useful for us - write_register(RegRampingMode, 0x00); - - // set breathing mark to 0x00 (disable breathing) - // TODO: this is potentially quite useful for us for the RGB LEDs - write_register(RegBreathingMark, 0x00); - - s_backlight_off = true; - s_rgb_current_color = LED_BLACK; - -cleanup: - i2c_release(I2C_LED); - prv_shutdown(true); -} - -void led_controller_backlight_set_brightness(uint8_t brightness) { - if ((BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C) == 0 || !s_initialized) { - return; - } - - prv_shutdown(false); - i2c_use(I2C_LED); - - write_register(RegPwmOut1, brightness); - write_register(RegPwmOut2, brightness); - write_register(RegPwmOut3, brightness); - - write_register(RegDataUpdate, 0xaa); - - i2c_release(I2C_LED); - - s_backlight_off = (brightness == 0); - - if (s_backlight_off && s_rgb_current_color == LED_BLACK) { - prv_shutdown(true); - } -} - - -void led_controller_rgb_set_color(uint32_t rgb_color) { - if ((BOARD_CONFIG_BACKLIGHT.options & ActuatorOptions_IssiI2C) == 0 || !s_initialized) { - return; - } - - s_rgb_current_color = rgb_color; - - uint8_t red = (s_rgb_current_color & 0x00FF0000) >> 16; - uint8_t green = (s_rgb_current_color & 0x0000FF00) >> 8; - uint8_t blue = (s_rgb_current_color & 0x000000FF); - - prv_shutdown(false); - i2c_use(I2C_LED); - - write_register(RegPwmOut4, red); - write_register(RegPwmOut6, blue); - write_register(RegPwmOut5, green); - - write_register(RegDataUpdate, 0xaa); - - i2c_release(I2C_LED); - - if (s_backlight_off && s_rgb_current_color == LED_BLACK) { - prv_shutdown(true); - } -} - -uint32_t led_controller_rgb_get_color(void) { - return s_rgb_current_color; -} - -void command_rgb_set_color(const char* color) { - uint32_t color_val = strtol(color, NULL, 16); - - led_controller_rgb_set_color(color_val); -} - diff --git a/src/fw/drivers/led_controller/pwm.c b/src/fw/drivers/led_controller/pwm.c deleted file mode 100644 index 576cb87962..0000000000 --- a/src/fw/drivers/led_controller/pwm.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/led_controller.h" - -#include "board/board.h" -#include "drivers/pwm.h" - -static const uint32_t TIMER_PERIOD_RESOLUTION = 1024; -static const uint32_t PWM_OUTPUT_FREQUENCY_HZ = 256; - -static uint32_t s_rgb_current_color; -static uint8_t s_brightness; - -void led_controller_init(void) { - pwm_init(&LED_CONTROLLER_PWM.pwm[0], TIMER_PERIOD_RESOLUTION, - TIMER_PERIOD_RESOLUTION * PWM_OUTPUT_FREQUENCY_HZ); - pwm_init(&LED_CONTROLLER_PWM.pwm[1], TIMER_PERIOD_RESOLUTION, - TIMER_PERIOD_RESOLUTION * PWM_OUTPUT_FREQUENCY_HZ); - pwm_init(&LED_CONTROLLER_PWM.pwm[2], TIMER_PERIOD_RESOLUTION, - TIMER_PERIOD_RESOLUTION * PWM_OUTPUT_FREQUENCY_HZ); - - s_rgb_current_color = LED_CONTROLLER_PWM.initial_color; -} - -void led_controller_backlight_set_brightness(uint8_t brightness) { - if (brightness > 100) { - brightness = 100; - } - - if (s_brightness == brightness) { - return; - } - - s_brightness = brightness; - - if (s_brightness == 0) { - pwm_enable(&LED_CONTROLLER_PWM.pwm[0], false); - pwm_enable(&LED_CONTROLLER_PWM.pwm[1], false); - pwm_enable(&LED_CONTROLLER_PWM.pwm[2], false); - return; - } else { - pwm_enable(&LED_CONTROLLER_PWM.pwm[0], true); - pwm_enable(&LED_CONTROLLER_PWM.pwm[1], true); - pwm_enable(&LED_CONTROLLER_PWM.pwm[2], true); - led_controller_rgb_set_color(s_rgb_current_color); - } -} - -void led_controller_rgb_set_color(uint32_t rgb_color) { - s_rgb_current_color = rgb_color; - - if (s_brightness == 0) { - return; - } - - uint8_t r = (s_rgb_current_color & 0x00FF0000) >> 16; - uint8_t g = (s_rgb_current_color & 0x0000FF00) >> 8; - uint8_t b = (s_rgb_current_color & 0x000000FF); - - pwm_set_duty_cycle(&LED_CONTROLLER_PWM.pwm[0], - (r * TIMER_PERIOD_RESOLUTION * s_brightness) / 100 / UINT8_MAX); - pwm_set_duty_cycle(&LED_CONTROLLER_PWM.pwm[1], - (g * TIMER_PERIOD_RESOLUTION * s_brightness) / 100 / UINT8_MAX); - pwm_set_duty_cycle(&LED_CONTROLLER_PWM.pwm[2], - (b * TIMER_PERIOD_RESOLUTION * s_brightness) / 100 / UINT8_MAX); -} - -uint32_t led_controller_rgb_get_color(void) { return s_rgb_current_color; } diff --git a/src/fw/drivers/led_controller/pwm.h b/src/fw/drivers/led_controller/pwm.h deleted file mode 100644 index 6186493898..0000000000 --- a/src/fw/drivers/led_controller/pwm.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/pwm.h" -#include "drivers/led_controller.h" - -typedef struct LedControllerPwm { - PwmConfig pwm[3]; - uint32_t initial_color; -} LedControllerPwm; \ No newline at end of file diff --git a/src/fw/drivers/legacy/accel.h b/src/fw/drivers/legacy/accel.h deleted file mode 100644 index d0656a5149..0000000000 --- a/src/fw/drivers/legacy/accel.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "applib/accel_service.h" -#include "util/shared_circular_buffer.h" - -bool accel_start(void); -void accel_stop(void); -bool accel_running(void); - -AccelSamplingRate accel_get_sampling_rate(void); -bool accel_set_sampling_rate(AccelSamplingRate); - -void accel_reset_pending_accel_event(void); -uint64_t accel_get_latest_timestamp(void); -void accel_get_latest_reading(AccelRawData *data); -void accel_add_consumer(SharedCircularBufferClient *client); -void accel_remove_consumer(SharedCircularBufferClient *client); - -uint32_t accel_consume_data(AccelRawData *data, SharedCircularBufferClient *client, - uint32_t max_samples, uint16_t subsample_num, uint16_t subsample_den); - -//! @return result, negative means failure and 0 means pass. See implementation for details -int accel_peek(AccelData *data); - -void accel_set_running(bool running); -void accel_set_num_samples(uint8_t num_samples); - -// This call is designed to be as lightweight as possible and does not access the hardware. -// Instead, it looks at the last values read from the hardware during normal usage. -bool accel_is_idle(void); - -bool accel_update_and_check_is_stationary(void); - -extern void accel_manager_dispatch_data(void); - -//! The accelerometer supports a changeable sensitivity for shake detection. This call will -//! select whether we want the accelerometer to enter a highly sensitive state with a low -//! threshold, where any minor amount of motion would trigger the system tap event. -//! Note: Setting this value does not ensure that tap detection is enabled. -void accel_set_shake_sensitivity_high(bool sensitivity_high); diff --git a/src/fw/drivers/lptim_systick.h b/src/fw/drivers/lptim_systick.h deleted file mode 100644 index 514b222674..0000000000 --- a/src/fw/drivers/lptim_systick.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void lptim_systick_init(void); - -bool lptim_systick_is_initialized(void); - -void lptim_systick_enable(void); - -void lptim_systick_pause(void); - -void lptim_systick_resume(void); - -void lptim_systick_tickless_idle(uint32_t ticks_from_now); - -uint32_t lptim_systick_get_elapsed_ticks(void); diff --git a/src/fw/drivers/mag.h b/src/fw/drivers/mag.h index fea183947f..5f05cb7913 100644 --- a/src/fw/drivers/mag.h +++ b/src/fw/drivers/mag.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -45,6 +32,9 @@ typedef enum { MagSampleRate5Hz } MagSampleRate; +//! Initialize magnetometer +void mag_init(void); + //! Enable the mag hardware and increment the refcount. Must be matched with a call to //! mag_release. void mag_use(void); diff --git a/src/fw/drivers/mcu.h b/src/fw/drivers/mcu.h index 04ce3a65f4..b0b5fcc23d 100644 --- a/src/fw/drivers/mcu.h +++ b/src/fw/drivers/mcu.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/mcu_reboot_reason.h b/src/fw/drivers/mcu_reboot_reason.h index e180e3a310..4d753e4fe3 100644 --- a/src/fw/drivers/mcu_reboot_reason.h +++ b/src/fw/drivers/mcu_reboot_reason.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/mic.h b/src/fw/drivers/mic.h index 4621e75abb..9336df4c57 100644 --- a/src/fw/drivers/mic.h +++ b/src/fw/drivers/mic.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -47,3 +34,7 @@ void mic_stop(MicDevice *this); //! Indicates whether the mic is running bool mic_is_running(MicDevice *this); + +//! Get the number of audio channels (1 for mono, 2 for stereo) +//! @return number of channels, or 1 if not specified +uint32_t mic_get_channels(MicDevice *this); diff --git a/src/fw/drivers/mic/Kconfig b/src/fw/drivers/mic/Kconfig new file mode 100644 index 0000000000..a1729ca1da --- /dev/null +++ b/src/fw/drivers/mic/Kconfig @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig MIC + bool "Microphone" + help + Microphone Drivers + +if MIC + +config MIC_NRF5 + bool "Nordic nRF5 PDM" + help + Support for the Nordic nRF5 PDM microphone interface. + +config MIC_SF32LB + bool "SiFli SF32LB PDM" + select HAL_SIFLI_AUDCODEC + select HAL_SIFLI_PDM + help + Support for the SiFli SF32LB PDM microphone interface. + +config MIC_QEMU + bool "QEMU stub" + help + Software-only microphone stub for QEMU. Delivers silence at + MIC_SAMPLE_RATE so the voice service can run end-to-end against + `pebble transcribe`, which ignores audio frames and replies with + a canned transcription on a timer. + +module = DRIVER_MIC +module-str = Microphone +source "src/fw/Kconfig.template.log_level" + +endif # MIC diff --git a/src/fw/drivers/mic/mic_command.c b/src/fw/drivers/mic/mic_command.c deleted file mode 100644 index 49cdadaecc..0000000000 --- a/src/fw/drivers/mic/mic_command.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "drivers/mic.h" -#include "drivers/accessory.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#if RECOVERY_FW -#include "services/prf/accessory/accessory_manager.h" -#else -#include "services/normal/accessory/accessory_manager.h" -#endif -#include "system/logging.h" -#include "system/passert.h" -#include "util/circular_buffer.h" -#include "util/legacy_checksum.h" -#include "system/profiler.h" - -static int s_timeout = 0; -static bool s_is_8_bit; -static bool s_is_8khz; -static TimerID s_start_timer; -static int16_t *s_test_buffer; - -static void prv_put_byte(uint8_t datum) { - accessory_send_data(&datum, 1); -} - -static const uint8_t HDLC_START = 0x7E; -static const uint8_t HDLC_ESCAPE = 0x7D; -static const uint8_t HDLC_ESCAPE_MASK = 0x20; - -static void prv_put_hdlc_frame_delimiter(void) { - prv_put_byte(HDLC_START); -} - -static void prv_put_byte_hdlc(uint8_t datum) { - if ((datum == HDLC_ESCAPE) || (datum == HDLC_START)) { - prv_put_byte(HDLC_ESCAPE); - prv_put_byte(datum ^ HDLC_ESCAPE_MASK); - } else { - prv_put_byte(datum); - } -} - -static void prv_prompt_output_cb(int16_t *samples, size_t sample_count, void *context) { - const int OUTPUT_SAMPLE_RATE = (s_is_8khz) ? 8000 : 16000; - - int to_process = MIN((int)sample_count, s_timeout); - s_timeout -= to_process; - - if (to_process > 0) { - // Groups of samples are encapsulated in HDLC-like framing in order to verify the integrity of - // samples - prv_put_hdlc_frame_delimiter(); - - int num_bytes = (to_process * (s_is_8_bit ? 1 : 2)) / (MIC_SAMPLE_RATE/OUTPUT_SAMPLE_RATE); - - // Store frame in temporary buffer in order to calculate CRC after subsample and/or bit-width - // conversion - uint8_t buf[num_bytes]; - int buf_idx = 0; - - uint8_t *d = (uint8_t *)samples; - // Subsample by skipping every second sample if output rate is set to 8kHz - for (int i = 0; i < to_process * 2; i += 2 * (MIC_SAMPLE_RATE/OUTPUT_SAMPLE_RATE)) { - if (s_is_8_bit) { - // Convert 16-bit PCM representation to 8-bit PCM representation - uint8_t b = d[i + 1] ^ 0x80; - buf[buf_idx++] = b; - prv_put_byte_hdlc(b); - } else { - buf[buf_idx++] = d[i]; - prv_put_byte_hdlc(d[i]); - buf[buf_idx++] = d[i + 1]; - prv_put_byte_hdlc(d[i + 1]); - } - } - - uint32_t crc = legacy_defective_checksum_memory(buf, num_bytes); - prv_put_byte_hdlc((uint8_t) crc); - prv_put_byte_hdlc((uint8_t) (crc >> 8)); - prv_put_byte_hdlc((uint8_t) (crc >> 16)); - prv_put_byte_hdlc((uint8_t) (crc >> 24)); - - prv_put_hdlc_frame_delimiter(); - } - - if (s_timeout == 0) { - mic_stop(MIC); - PROFILER_STOP; - PROFILER_PRINT_STATS; - - kernel_free(s_test_buffer); - - accessory_enable_input(); -#if RECOVERY_FW - const AccessoryInputState input_state = AccessoryInputStateMfg; -#else - const AccessoryInputState input_state = AccessoryInputStateIdle; -#endif - PBL_ASSERTN(accessory_manager_set_state(input_state)); - } -} - -static void prv_mic_start(void *data) { - const size_t BUFFER_SIZE = 24; - - new_timer_delete(s_start_timer); - - s_test_buffer = kernel_malloc(BUFFER_SIZE * sizeof(int16_t)); - - if (!s_test_buffer) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to malloc buffer for 'mic start' command"); - return; - } - - uint32_t width = s_is_8_bit ? 8 : 16; - uint32_t rate = s_is_8khz ? 8 : 16; - PBL_LOG(LOG_LEVEL_ALWAYS, "Starting mic recording: %"PRIu32"-bit @ %"PRIu32"kHz for %"PRIu32 - " samples", width, rate, (s_timeout / (MIC_SAMPLE_RATE / (rate * 1000)))); - - PROFILER_INIT; - PROFILER_START; - if (!mic_start(MIC, &prv_prompt_output_cb, NULL, s_test_buffer, BUFFER_SIZE)) { - kernel_free(s_test_buffer); - } -} - -void command_mic_start(char *timeout_str, char *sample_size_str, char *sample_rate_str, - char *volume_str) { - static const int MAX_TIMEOUT = 60; - - if (!accessory_manager_set_state(AccessoryInputStateMic)) { - PBL_LOG(LOG_LEVEL_ERROR, "The accessory is already in use!"); - return; - } - - s_timeout = strtol(timeout_str, NULL, 10); - if (s_timeout <= 0) { - s_timeout = 1; - } else if (s_timeout > MAX_TIMEOUT) { - s_timeout = MAX_TIMEOUT; - } - int volume = strtol(volume_str, NULL, 10); - mic_set_volume(MIC, MIN(volume, 1024)); - - s_is_8_bit = strtol(sample_size_str, NULL, 10) == 8; // assume 16 bit if not 8 bit - s_is_8khz = strtol(sample_rate_str, NULL, 10) == 8000; // assume 16kHz if not set to 8000 - - // Convert timeout in seconds to sample count so that the exact amount of samples are sent - s_timeout *= MIC_SAMPLE_RATE; - - // Boost the accessory connector baud rate if necessary - accessory_disable_input(); - if ((!s_is_8_bit) && (!s_is_8khz)) { - accessory_set_baudrate(AccessoryBaud460800); - } else if ((!s_is_8_bit) || (!s_is_8khz)) { - accessory_set_baudrate(AccessoryBaud230400); - } - - // Start timer after a short delay to allow receiving end to switch baud rate - s_start_timer = new_timer_create(); - new_timer_start(s_start_timer, 500, &prv_mic_start, NULL, 0); -} - -void command_mic_read() { - command_mic_start("3", "16", "16000", "100"); -} diff --git a/src/fw/drivers/mic/nrf5/pdm.c b/src/fw/drivers/mic/nrf5/pdm.c index d4437159ee..6efd818f60 100644 --- a/src/fw/drivers/mic/nrf5/pdm.c +++ b/src/fw/drivers/mic/nrf5/pdm.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Joshua Jun - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Joshua Jun */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/mic.h" #include "drivers/mic/nrf5/pdm_definitions.h" @@ -20,12 +7,15 @@ #include "board/board.h" #include "drivers/clocksource.h" #include "kernel/events.h" +#include "kernel/kernel_heap.h" #include "kernel/pbl_malloc.h" #include "kernel/util/sleep.h" #include "os/mutex.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" #include "util/circular_buffer.h" +#include "util/heap.h" #include "util/math.h" #include "util/size.h" #include "util/time/time.h" @@ -33,9 +23,10 @@ #include "hal/nrf_clock.h" #include "nrfx_pdm.h" +PBL_LOG_MODULE_DEFINE(driver_mic_nrf5, CONFIG_DRIVER_MIC_LOG_LEVEL); + static void prv_pdm_event_handler(nrfx_pdm_evt_t const *p_evt); -static void prv_dispatch_samples_main(void *data); -static void prv_dispatch_samples_common(void); +static void prv_dispatch_samples_system_task(void *data); static bool prv_allocate_buffers(MicDeviceState *state); static void prv_free_buffers(MicDeviceState *state); @@ -49,19 +40,44 @@ static bool prv_is_valid_buffer(MicDeviceState *state, int16_t *buffer) { } static bool prv_allocate_buffers(MicDeviceState *state) { - // Allocate circular buffer storage - state->circ_buffer_storage = kernel_malloc(CIRCULAR_BUF_SIZE_BYTES); - if (!state->circ_buffer_storage) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to allocate circular buffer storage"); + // The kernel heap fragments over time, so a single 10 KB contiguous alloc + // can fail even when plenty of memory is free. Shrink the request in 32 ms + // steps until it fits or we hit the 128 ms floor. + uint16_t try_size = CIRCULAR_BUF_SIZE_BYTES; + uint8_t *storage = NULL; + + while (try_size >= CIRCULAR_BUF_MIN_SIZE_BYTES) { + storage = kernel_malloc(try_size); + if (storage) { + break; + } + try_size -= CIRCULAR_BUF_STEP_BYTES; + } + + if (!storage) { + unsigned int used, free_bytes, max_free; + heap_calc_totals(kernel_heap_get(), &used, &free_bytes, &max_free); + PBL_LOG_ERR("Failed to allocate PDM circular buffer (min %u B, max_free %u B)", + (unsigned)CIRCULAR_BUF_MIN_SIZE_BYTES, max_free); return false; } + if (try_size < CIRCULAR_BUF_SIZE_BYTES) { + unsigned int used, free_bytes, max_free; + heap_calc_totals(kernel_heap_get(), &used, &free_bytes, &max_free); + PBL_LOG_WRN("PDM circular buffer fell back to %u B (requested %u, max_free %u)", + (unsigned)try_size, (unsigned)CIRCULAR_BUF_SIZE_BYTES, max_free); + } + + state->circ_buffer_storage = storage; + state->circ_buffer_size = try_size; + // Allocate PDM buffers for (int i = 0; i < PDM_BUFFER_COUNT; i++) { size_t buffer_size = PDM_BUFFER_SIZE_SAMPLES * sizeof(int16_t); state->pdm_buffers[i] = kernel_malloc(buffer_size); if (!state->pdm_buffers[i]) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to allocate PDM buffer %d", i); + PBL_LOG_ERR("Failed to allocate PDM buffer %d", i); // Free any previously allocated buffers prv_free_buffers(state); return false; @@ -71,7 +87,7 @@ static bool prv_allocate_buffers(MicDeviceState *state) { } // Initialize circular buffer with allocated storage - circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, CIRCULAR_BUF_SIZE_BYTES); + circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, try_size); return true; } @@ -82,6 +98,7 @@ static void prv_free_buffers(MicDeviceState *state) { kernel_free(state->circ_buffer_storage); state->circ_buffer_storage = NULL; } + state->circ_buffer_size = 0; // Free PDM buffers for (int i = 0; i < PDM_BUFFER_COUNT; i++) { @@ -95,29 +112,58 @@ static void prv_free_buffers(MicDeviceState *state) { static void prv_process_pdm_buffer(MicDeviceState *state, int16_t *pdm_data) { // Ensure we're still running and have valid state if (!state->is_running) { - PBL_LOG(LOG_LEVEL_DEBUG, "prv_process_pdm_buffer: Not running, ignoring data"); + PBL_LOG_DBG("prv_process_pdm_buffer: Not running, ignoring data"); return; } // Ensure circular buffer storage is allocated if (!state->circ_buffer_storage) { - PBL_LOG(LOG_LEVEL_DEBUG, "prv_process_pdm_buffer: No circular buffer storage, ignoring data"); + PBL_LOG_DBG("prv_process_pdm_buffer: No circular buffer storage, ignoring data"); return; } // Ensure we have valid audio buffer info if (!state->audio_buffer || state->audio_buffer_len == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "prv_process_pdm_buffer: No audio buffer configured, ignoring data"); + PBL_LOG_DBG("prv_process_pdm_buffer: No audio buffer configured, ignoring data"); return; } // Write samples to circular buffer + uint32_t samples_written = 0; for (int i = 0; i < PDM_BUFFER_SIZE_SAMPLES; i++) { - if (!circular_buffer_write(&state->circ_buffer, - (const uint8_t *)&pdm_data[i], + if (!circular_buffer_write(&state->circ_buffer, + (const uint8_t *)&pdm_data[i], sizeof(int16_t))) { break; // Buffer is full, drop remaining samples } + samples_written++; + } + + // Monitor for buffer overruns (dropped samples) + static uint32_t total_samples = 0, dropped_samples = 0; + total_samples += PDM_BUFFER_SIZE_SAMPLES; + dropped_samples += (PDM_BUFFER_SIZE_SAMPLES - samples_written); + + // Monitor buffer utilization + uint16_t buffer_free = circular_buffer_get_write_space_remaining(&state->circ_buffer); + uint16_t buffer_total = state->circ_buffer_size; + uint16_t buffer_used = buffer_total - buffer_free; + uint16_t buffer_utilization = buffer_total ? (buffer_used * 100) / buffer_total : 0; + + // Log dropout statistics periodically + static uint32_t log_counter = 0; + if (++log_counter >= 100) { // Every 100 buffers (~2 seconds) + if (dropped_samples > 0) { + // Calculate percentage using integer arithmetic (x10 for one decimal place) + uint32_t percent_x10 = (dropped_samples * 1000) / total_samples; + PBL_LOG_DBG("Audio dropouts: %"PRIu32"/%"PRIu32" samples dropped (%"PRIu32".%"PRIu32" percent), buffer util: %"PRIu16, + dropped_samples, total_samples, percent_x10 / 10, percent_x10 % 10, buffer_utilization); + } else { + PBL_LOG_DBG("Audio buffer utilization: %"PRIu16" (%"PRIu16"/%"PRIu16" bytes)", + buffer_utilization, buffer_used, buffer_total); + } + log_counter = 0; + total_samples = dropped_samples = 0; // Reset counters } // Check if we have enough data for a complete frame @@ -126,15 +172,10 @@ static void prv_process_pdm_buffer(MicDeviceState *state, int16_t *pdm_data) { if (available_data >= frame_size_bytes && !state->main_pending) { state->main_pending = true; - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_dispatch_samples_main, - .data = NULL - } - }; - - if (!event_put_isr(&e)) { + + // Dispatch to low-priority system task instead of kernel event queue + bool should_context_switch = false; + if (!system_task_add_callback_from_isr(prv_dispatch_samples_system_task, NULL, &should_context_switch)) { state->main_pending = false; } } @@ -147,7 +188,7 @@ static void prv_pdm_event_handler(nrfx_pdm_evt_t const *p_evt) { // Don't assert on is_running during shutdown - the PDM might send final events if (!state->is_running) { - PBL_LOG(LOG_LEVEL_DEBUG, "prv_pdm_event_handler: Microphone stopped, ignoring event"); + PBL_LOG_DBG("prv_pdm_event_handler: Microphone stopped, ignoring event"); return; } @@ -197,61 +238,83 @@ void mic_init(const MicDevice *this) { // Initialize PDM driver once during init nrfx_err_t err = nrfx_pdm_init(&this->pdm_instance, &state->pdm_config, prv_pdm_event_handler); if (err != NRFX_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to initialize PDM: %d", err); + PBL_LOG_ERR("Failed to initialize PDM: %d", err); return; } state->is_initialized = true; } -static void prv_dispatch_samples_common(void) { +// Process at most this many frames per system task callback to allow +// other tasks (especially Bluetooth) to run and prevent send buffer overflow +// This needs to be high enough to keep up with real-time audio (~50 frames/sec) +// but low enough to allow BT to drain its send buffer between batches +#define MAX_FRAMES_PER_SYSTEM_TASK_CALLBACK 64 + +static void prv_dispatch_samples_system_task(void *data) { MicDeviceState *state = MIC->state; - + + // Defensive check + if (!state || !state->is_initialized) { + return; + } + mutex_lock_recursive(state->mutex); - // Only process if we have exactly one complete frame available and buffers are allocated + // Process a limited number of frames to provide backpressure + // This prevents overwhelming the Bluetooth send buffer if (state->is_running && state->data_handler && state->audio_buffer && state->circ_buffer_storage) { - - // Check if we have enough data for exactly one frame + size_t frame_size_bytes = state->audio_buffer_len * sizeof(int16_t); - - // Use the circular buffer API to check available data - uint16_t available_data = circular_buffer_get_read_space_remaining(&state->circ_buffer); - - if (available_data >= frame_size_bytes) { - // Copy exactly one frame + int frames_processed = 0; + + while (state->is_running && state->data_handler && frames_processed < MAX_FRAMES_PER_SYSTEM_TASK_CALLBACK) { + // Check if we have enough data for a complete frame + uint16_t available_data = circular_buffer_get_read_space_remaining(&state->circ_buffer); + + if (available_data < frame_size_bytes) { + break; // Not enough data for another frame + } + + // Copy one frame uint16_t bytes_copied = circular_buffer_copy(&state->circ_buffer, (uint8_t *)state->audio_buffer, frame_size_bytes); if (bytes_copied == frame_size_bytes) { - // Call callback with exactly one frame + // Call callback with the frame state->data_handler(state->audio_buffer, state->audio_buffer_len, state->handler_context); - - // Consume exactly the frame we processed + + // Consume the frame we processed circular_buffer_consume(&state->circ_buffer, bytes_copied); + + frames_processed++; + + // Feed the system task watchdog periodically during long processing + system_task_watchdog_feed(); + } else { + break; // Failed to copy, stop processing } } - } - - mutex_unlock_recursive(state->mutex); -} -static void prv_dispatch_samples_main(void *data) { - MicDeviceState *state = MIC->state; - - // Defensive check and clear pending flag - if (!state || !state->is_initialized) { - return; - } - - // Always clear the pending flag, even if we can't process - state->main_pending = false; - - // Only process if still running and properly initialized - if (state->is_running) { - prv_dispatch_samples_common(); + // If we still have data available after processing, reschedule immediately + // to continue processing on the next system task cycle + uint16_t remaining_data = circular_buffer_get_read_space_remaining(&state->circ_buffer); + if (remaining_data >= frame_size_bytes && state->is_running && !state->main_pending) { + state->main_pending = true; + if (!system_task_add_callback(prv_dispatch_samples_system_task, NULL)) { + state->main_pending = false; + } + } else { + // Clear pending flag only if we're done processing + state->main_pending = false; + } + } else { + // Clear pending flag if we can't process + state->main_pending = false; } + + mutex_unlock_recursive(state->mutex); } void mic_set_volume(const MicDevice *this, uint16_t volume) { @@ -261,7 +324,7 @@ void mic_set_volume(const MicDevice *this, uint16_t volume) { MicDeviceState *state = this->state; if (state->is_running) { - PBL_LOG(LOG_LEVEL_WARNING, "Cannot set volume while microphone is running"); + PBL_LOG_WRN("Cannot set volume while microphone is running"); return; } @@ -294,7 +357,7 @@ static bool prv_start_pdm_capture(const MicDevice *this) { // Check if buffers are valid if (!state->pdm_buffers[0] || !state->pdm_buffers[1]) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid PDM buffers: [0]=%p [1]=%p", + PBL_LOG_ERR("Invalid PDM buffers: [0]=%p [1]=%p", state->pdm_buffers[0], state->pdm_buffers[1]); return false; } @@ -302,7 +365,7 @@ static bool prv_start_pdm_capture(const MicDevice *this) { // Try starting PDM first, then set buffer in event handler nrfx_err_t err = nrfx_pdm_start(&this->pdm_instance); if (err != NRFX_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to start PDM: %d", err); + PBL_LOG_ERR("Failed to start PDM: %d", err); return false; } @@ -322,26 +385,25 @@ bool mic_start(const MicDevice *this, MicDataHandlerCB data_handler, void *conte mutex_lock_recursive(state->mutex); if (state->is_running) { - PBL_LOG(LOG_LEVEL_WARNING, "Microphone is already running"); + PBL_LOG_WRN("Microphone is already running"); mutex_unlock_recursive(state->mutex); return false; } if (!state->is_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Microphone not initialized"); + PBL_LOG_ERR("Microphone not initialized"); mutex_unlock_recursive(state->mutex); return false; } - // Allocate buffers dynamically + // Allocate buffers dynamically. prv_allocate_buffers also initializes the + // circular buffer with the actual (possibly shrunk) size. if (!prv_allocate_buffers(state)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to allocate microphone buffers"); + PBL_LOG_ERR("Failed to allocate microphone buffers"); mutex_unlock_recursive(state->mutex); return false; } - // Reset state - circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, CIRCULAR_BUF_SIZE_BYTES); state->data_handler = data_handler; state->handler_context = context; state->audio_buffer = audio_buffer; @@ -363,7 +425,7 @@ bool mic_start(const MicDevice *this, MicDataHandlerCB data_handler, void *conte return false; } - PBL_LOG(LOG_LEVEL_INFO, "Microphone started"); + PBL_LOG_INFO("Microphone started"); mutex_unlock_recursive(state->mutex); return true; @@ -405,7 +467,7 @@ void mic_stop(const MicDevice *this) { state->audio_buffer_len = 0; state->main_pending = false; - PBL_LOG(LOG_LEVEL_INFO, "Microphone stopped"); + PBL_LOG_INFO("Microphone stopped"); mutex_unlock_recursive(state->mutex); } @@ -433,3 +495,8 @@ bool mic_is_running(const MicDevice *this) { return this->state->is_running; } + +uint32_t mic_get_channels(const MicDevice *this) { + PBL_ASSERTN(this); + return this->channels ? this->channels : 1; +} diff --git a/src/fw/drivers/mic/nrf5/pdm_definitions.h b/src/fw/drivers/mic/nrf5/pdm_definitions.h index 9e48a3ef07..42aac60ab2 100644 --- a/src/fw/drivers/mic/nrf5/pdm_definitions.h +++ b/src/fw/drivers/mic/nrf5/pdm_definitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Joshua Jun - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Joshua Jun */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -32,10 +19,21 @@ #define PDM_GAIN_DEFAULT (NRF_PDM_GAIN_DEFAULT) // Circular buffer configuration -#define CIRCULAR_BUF_SIZE_MS (20) +#define CIRCULAR_BUF_SIZE_MS (320) #define CIRCULAR_BUF_SIZE_SAMPLES ((MIC_SAMPLE_RATE * CIRCULAR_BUF_SIZE_MS) / 1000) #define CIRCULAR_BUF_SIZE_BYTES (CIRCULAR_BUF_SIZE_SAMPLES * sizeof(int16_t)) +// Minimum fallback size when the heap is fragmented and the full circular +// buffer alloc fails. Below this transcription quality degrades noticeably. +#define CIRCULAR_BUF_MIN_SIZE_MS (128) +#define CIRCULAR_BUF_MIN_SIZE_SAMPLES ((MIC_SAMPLE_RATE * CIRCULAR_BUF_MIN_SIZE_MS) / 1000) +#define CIRCULAR_BUF_MIN_SIZE_BYTES (CIRCULAR_BUF_MIN_SIZE_SAMPLES * sizeof(int16_t)) + +// 32 ms shrink per retry gives ~7 attempts between 320 ms and 128 ms. +#define CIRCULAR_BUF_STEP_MS (32) +#define CIRCULAR_BUF_STEP_SAMPLES ((MIC_SAMPLE_RATE * CIRCULAR_BUF_STEP_MS) / 1000) +#define CIRCULAR_BUF_STEP_BYTES (CIRCULAR_BUF_STEP_SAMPLES * sizeof(int16_t)) + typedef struct { nrfx_pdm_config_t pdm_config; int16_t *pdm_buffers[PDM_BUFFER_COUNT]; @@ -50,6 +48,7 @@ typedef struct { // Intermediate storage CircularBuffer circ_buffer; uint8_t *circ_buffer_storage; + uint16_t circ_buffer_size; // State management PebbleRecursiveMutex *mutex; @@ -65,4 +64,5 @@ typedef const struct MicDevice { const nrfx_pdm_t pdm_instance; uint32_t clk_pin; uint32_t data_pin; + uint32_t channels; } MicDevice; diff --git a/src/fw/drivers/mic/qemu/mic.c b/src/fw/drivers/mic/qemu/mic.c new file mode 100644 index 0000000000..7f7f554c40 --- /dev/null +++ b/src/fw/drivers/mic/qemu/mic.c @@ -0,0 +1,124 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/mic.h" +#include "drivers/mic/qemu/mic_definitions.h" + +#include "board/board.h" +#include "console/prompt.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/logging.h" +#include "system/passert.h" + +#include +#include + +PBL_LOG_MODULE_DEFINE(driver_mic_qemu, CONFIG_DRIVER_MIC_LOG_LEVEL); + +static void prv_timer_cb(void *data) { + MicDevice *this = data; + MicDeviceState *state = this->state; + + if (!state->is_running || !state->data_handler || !state->audio_buffer) { + return; + } + + // QEMU has no real microphone — feed the consumer silence at the expected + // cadence so the voice service stays in lockstep with `pebble transcribe`. + memset(state->audio_buffer, 0, state->audio_buffer_len * sizeof(int16_t)); + state->data_handler(state->audio_buffer, state->audio_buffer_len, state->handler_context); +} + +void mic_init(MicDevice *this) { + PBL_ASSERTN(this); + PBL_ASSERTN(this->state); + + MicDeviceState *state = this->state; + if (state->is_initialized) { + return; + } + + state->timer = new_timer_create(); + PBL_ASSERTN(state->timer != TIMER_INVALID_ID); + state->is_initialized = true; +} + +void mic_set_volume(MicDevice *this, uint16_t volume) { + // No gain stage to tweak on the QEMU stub. +} + +bool mic_start(MicDevice *this, MicDataHandlerCB data_handler, void *context, + int16_t *audio_buffer, size_t audio_buffer_len) { + PBL_ASSERTN(this); + PBL_ASSERTN(this->state); + PBL_ASSERTN(data_handler); + PBL_ASSERTN(audio_buffer); + PBL_ASSERTN(audio_buffer_len > 0); + + MicDeviceState *state = this->state; + PBL_ASSERTN(state->is_initialized); + + if (state->is_running) { + return false; + } + + state->data_handler = data_handler; + state->handler_context = context; + state->audio_buffer = audio_buffer; + state->audio_buffer_len = audio_buffer_len; + + // Match the wall-clock cadence of a real mic: one buffer's worth of samples + // per period at MIC_SAMPLE_RATE. Round up to keep the period >= 1 ms. + uint32_t period_ms = (audio_buffer_len * 1000U + MIC_SAMPLE_RATE - 1U) / MIC_SAMPLE_RATE; + if (period_ms == 0) { + period_ms = 1; + } + state->period_ms = period_ms; + state->is_running = true; + + if (!new_timer_start(state->timer, period_ms, prv_timer_cb, (void *)this, + TIMER_START_FLAG_REPEATING)) { + state->is_running = false; + return false; + } + + PBL_LOG_INFO("QEMU mic stub started (period=%" PRIu32 "ms)", period_ms); + return true; +} + +void mic_stop(MicDevice *this) { + PBL_ASSERTN(this); + PBL_ASSERTN(this->state); + + MicDeviceState *state = this->state; + if (!state->is_running) { + return; + } + + state->is_running = false; + new_timer_stop(state->timer); + state->data_handler = NULL; + state->handler_context = NULL; + state->audio_buffer = NULL; + state->audio_buffer_len = 0; +} + +bool mic_is_running(MicDevice *this) { + PBL_ASSERTN(this); + PBL_ASSERTN(this->state); + return this->state->is_running; +} + +uint32_t mic_get_channels(MicDevice *this) { + PBL_ASSERTN(this); + return this->channels ? this->channels : 1; +} + +void command_mic_start(char *timeout_str, char *sample_size_str, char *sample_rate_str, + char *format_str) { + prompt_send_response("Microphone console commands not supported on QEMU"); +} + +void command_mic_read(void) { + prompt_send_response("Microphone read command not supported on QEMU"); +} diff --git a/src/fw/drivers/mic/qemu/mic_definitions.h b/src/fw/drivers/mic/qemu/mic_definitions.h new file mode 100644 index 0000000000..7b38283e97 --- /dev/null +++ b/src/fw/drivers/mic/qemu/mic_definitions.h @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "drivers/mic.h" +#include "pbl/services/new_timer/new_timer.h" + +#include +#include +#include + +typedef struct { + TimerID timer; + MicDataHandlerCB data_handler; + void *handler_context; + int16_t *audio_buffer; + size_t audio_buffer_len; + uint32_t period_ms; + bool is_initialized; + bool is_running; +} MicDeviceState; + +typedef const struct MicDevice { + MicDeviceState *state; + uint32_t channels; +} MicDevice; diff --git a/src/fw/drivers/mic/sf32lb52/pdm.c b/src/fw/drivers/mic/sf32lb52/pdm.c index d1821a29e0..0d4a076368 100644 --- a/src/fw/drivers/mic/sf32lb52/pdm.c +++ b/src/fw/drivers/mic/sf32lb52/pdm.c @@ -1,35 +1,52 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/mic.h" +#include "drivers/pmic/npm1300.h" #include "board/board.h" +#include "kernel/kernel_heap.h" #include "kernel/pbl_malloc.h" +#include "mcu/cache.h" #include "system/logging.h" -#include "kernel/events.h" #include "os/mutex.h" #include "system/passert.h" #include "util/circular_buffer.h" +#include "util/heap.h" #include "kernel/util/sleep.h" +#include "kernel/util/stop.h" #include "pdm_definitions.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "FreeRTOS.h" -#define CFG_AUDIO_RECORD_PIPE_SIZE (288) -#define CFG_AUDIO_RECORD_GAIN_DEFAULT (30) -#define CFG_AUDIO_RECORD_GAIN_MAX (127) +PBL_LOG_MODULE_DEFINE(driver_mic_sf32lb, CONFIG_DRIVER_MIC_LOG_LEVEL); + +// HACK alert, we need proper regulator abstraction +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) +#define PDM_POWER_NPM1300_LDO2 1 +#endif + +#define PDM_AUDIO_RECORD_PIPE_SIZE (288) +#define PDM_AUDIO_RECORD_GAIN_DEFAULT (90) +#define PDM_AUDIO_RECORD_GAIN_MAX (120) + +// PDM Configuration +#define PDM_BUFFER_SIZE_SAMPLES (320) + +// Circular buffer configuration +#define PDM_CIRCULAR_BUF_SIZE_MS (320) +#define PDM_CIRCULAR_BUF_SIZE_SAMPLES ((MIC_SAMPLE_RATE * PDM_CIRCULAR_BUF_SIZE_MS) / 1000) + +// Minimum fallback size +// If it is any smaller than this, the transcription wont work well +#define PDM_CIRCULAR_BUF_MIN_SIZE_MS (128) +#define PDM_CIRCULAR_BUF_MIN_SIZE_SAMPLES ((MIC_SAMPLE_RATE * PDM_CIRCULAR_BUF_MIN_SIZE_MS) / 1000) + +// Fallback step. 32 ms shrink per retry gives us ~7 attempts between 320 ms and 128 ms +#define PDM_CIRCULAR_BUF_STEP_MS (32) +#define PDM_CIRCULAR_BUF_STEP_SAMPLES ((MIC_SAMPLE_RATE * PDM_CIRCULAR_BUF_STEP_MS) / 1000) + +#define PDM_CIRCULAR_BUF_BYTES(samples, channels) \ + ((size_t)(samples) * sizeof(int16_t) * (channels)) static PDM_HandleTypeDef s_hpdm; static MicDeviceState* s_state; @@ -43,9 +60,14 @@ void mic_init(const MicDevice *this) { return; } +#if PDM_POWER_NPM1300_LDO2 + (void)NPM1300_OPS.ldo2_set_enabled(false); +#endif + // Create mutex for thread safety state->mutex = mutex_create_recursive(); PBL_ASSERTN(state->mutex); + state->volume = PDM_AUDIO_RECORD_GAIN_DEFAULT; //Pinmux configuration HAL_PIN_Set(this->clk_gpio.pad, this->clk_gpio.func, this->clk_gpio.flags, 1); @@ -60,38 +82,63 @@ void mic_init(const MicDevice *this) { hpdm->Init.Channels = this->channels; hpdm->Init.SampleRate = this->sample_rate; hpdm->Init.ChannelDepth = this->channel_depth; + hpdm->Init.clkSrc = 9600000; HAL_NVIC_SetPriority(this->pdm_irq, this->pdm_irq_priority, 0); state->is_initialized = true; } +//volume from 0~100 void mic_set_volume(const MicDevice *this, uint16_t volume) { PBL_ASSERTN(this); PBL_ASSERTN(this->state); MicDeviceState *state = this->state; - PDM_HandleTypeDef* hpdm = state->hpdm; - if (state->is_running) { - PBL_LOG(LOG_LEVEL_WARNING, "Cannot set volume while microphone is running"); + PBL_LOG_WRN("Cannot set volume while microphone is running"); return; } - // volume form 0~127 - if(volume > CFG_AUDIO_RECORD_GAIN_MAX) volume = CFG_AUDIO_RECORD_GAIN_MAX; - HAL_PDM_Set_Gain(hpdm, this->channels, volume); + volume = volume * PDM_AUDIO_RECORD_GAIN_MAX/100; + // volume form 0~120 on HAL + if(volume > PDM_AUDIO_RECORD_GAIN_MAX) volume = PDM_AUDIO_RECORD_GAIN_MAX; + state->volume = volume; } -static bool prv_allocate_buffers(MicDeviceState *state) { - // Allocate circular buffer storage - state->circ_buffer_storage = kernel_malloc(CIRCULAR_BUF_SIZE_BYTES); - if (!state->circ_buffer_storage) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to allocate circular buffer storage"); +static bool prv_allocate_buffers(const MicDevice *this) { + MicDeviceState *state = this->state; + const uint32_t channels = this->channels ? this->channels : 1; + const size_t requested = PDM_CIRCULAR_BUF_BYTES(PDM_CIRCULAR_BUF_SIZE_SAMPLES, channels); + const size_t floor = PDM_CIRCULAR_BUF_BYTES(PDM_CIRCULAR_BUF_MIN_SIZE_SAMPLES, channels); + const size_t step = PDM_CIRCULAR_BUF_BYTES(PDM_CIRCULAR_BUF_STEP_SAMPLES, channels); + + size_t try_size = requested; + uint8_t *storage = NULL; + + while (try_size >= floor) { + storage = kernel_malloc(try_size); + if (storage) { + break; + } + try_size -= step; + } + + if (!storage) { + unsigned int used, free_bytes, max_free; + heap_calc_totals(kernel_heap_get(), &used, &free_bytes, &max_free); + PBL_LOG_ERR("Failed to allocate PDM circular buffer (min %u B, max_free %u B)", + (unsigned)floor, max_free); return false; } - - // Initialize circular buffer with allocated storage - circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, CIRCULAR_BUF_SIZE_BYTES); - + + if (try_size < requested) { + unsigned int used, free_bytes, max_free; + heap_calc_totals(kernel_heap_get(), &used, &free_bytes, &max_free); + PBL_LOG_WRN("PDM circular buffer fell back to %u B (requested %u, max_free %u)", + (unsigned)try_size, (unsigned)requested, max_free); + } + + state->circ_buffer_storage = storage; + circular_buffer_init(&state->circ_buffer, storage, (uint16_t)try_size); return true; } @@ -103,93 +150,118 @@ static void prv_free_buffers(MicDeviceState *state) { } } -static void prv_dispatch_samples_common(void) { +// Process at most this many frames per system task callback to allow +// other tasks (especially Bluetooth) to run and prevent send buffer overflow +#define MAX_FRAMES_PER_SYSTEM_TASK_CALLBACK 5 + +static void prv_dispatch_samples_system_task(void *data) { + // Defensive check + if (!s_state || !s_state->is_initialized) { + return; + } + mutex_lock_recursive(s_state->mutex); - // Only process if we have exactly one complete frame available and buffers are allocated + // Process a limited number of frames to provide backpressure if (s_state->is_running && s_state->data_handler && s_state->audio_buffer && s_state->circ_buffer_storage) { - // Check if we have enough data for exactly one frame size_t frame_size_bytes = s_state->audio_buffer_len * sizeof(int16_t); + int frames_processed = 0; - // Use the circular buffer API to check available data - uint16_t available_data = circular_buffer_get_read_space_remaining(&s_state->circ_buffer); - - while (available_data >= frame_size_bytes) { - // Copy exactly one frame + while (s_state->is_running && s_state->data_handler && frames_processed < MAX_FRAMES_PER_SYSTEM_TASK_CALLBACK) { + // Check if we have enough data for a complete frame + uint16_t available_data = circular_buffer_get_read_space_remaining(&s_state->circ_buffer); + + if (available_data < frame_size_bytes) { + break; // Not enough data for another frame + } + + // Copy one frame uint16_t bytes_copied = circular_buffer_copy(&s_state->circ_buffer, (uint8_t *)s_state->audio_buffer, frame_size_bytes); if (bytes_copied == frame_size_bytes) { - // Call callback with exactly one frame + // Call callback with the frame s_state->data_handler(s_state->audio_buffer, s_state->audio_buffer_len, s_state->handler_context); - // Consume exactly the frame we processed + // Consume the frame we processed circular_buffer_consume(&s_state->circ_buffer, bytes_copied); + + frames_processed++; + + // Feed the system task watchdog periodically during long processing + system_task_watchdog_feed(); + } else { + break; // Failed to copy, stop processing + } + } + + // If we still have data available after processing, reschedule immediately + uint16_t remaining_data = circular_buffer_get_read_space_remaining(&s_state->circ_buffer); + if (remaining_data >= frame_size_bytes && s_state->is_running && !s_state->main_pending) { + s_state->main_pending = true; + if (!system_task_add_callback(prv_dispatch_samples_system_task, NULL)) { + s_state->main_pending = false; } - available_data -= frame_size_bytes; + } else { + // Clear pending flag only if we're done processing + s_state->main_pending = false; } + } else { + // Clear pending flag if we can't process + s_state->main_pending = false; } mutex_unlock_recursive(s_state->mutex); } -static void prv_dispatch_samples_main(void *data) { - // Defensive check and clear pending flag - if (!s_state || !s_state->is_initialized) { - return; - } - - // Always clear the pending flag, even if we can't process - s_state->main_pending = false; - - // Only process if still running and properly initialized - if (s_state->is_running) { - prv_dispatch_samples_common(); - } -} - static void prv_dma_data_processing(uint8_t* data, uint16_t size) { // Don't assert on is_running during shutdown - the PDM might send final events if (!s_state->is_running) { - PBL_LOG(LOG_LEVEL_ERROR, "Microphone stopped, ignoring event"); + PBL_LOG_ERR("Microphone stopped, ignoring event"); return; } // Ensure circular buffer storage is allocated if (!s_state->circ_buffer_storage) { - PBL_LOG(LOG_LEVEL_ERROR, "No circular buffer storage, ignoring data"); + PBL_LOG_ERR("No circular buffer storage, ignoring data"); return; } - + // Ensure we have valid audio buffer info if (!s_state->audio_buffer || s_state->audio_buffer_len == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "No audio buffer configured, ignoring data"); + PBL_LOG_ERR("No audio buffer configured, ignoring data"); return; } - - // Write samples to circular buffer - if (!circular_buffer_write(&s_state->circ_buffer, data, size)) { - PBL_LOG(LOG_LEVEL_ERROR, "circular buffer full"); - return; // Buffer is full, drop remaining samples + + // PDM DMA writes straight to RAM, bypassing D-cache. Drop any stale lines so + // the CPU re-fetches the freshly captured samples instead of pre-DMA contents. + // Buffer base is cache-line aligned at allocation time and the half-buffer + // stride is a multiple of the cache line size, so this invalidate cannot + // straddle neighboring allocations. + dcache_invalidate(data, size); + + // Write samples directly to circular buffer + // If buffer is full, drop oldest data to make room for fresh audio + uint16_t write_space = circular_buffer_get_write_space_remaining(&s_state->circ_buffer); + if (write_space < size) { + uint16_t to_drop = size - write_space; + circular_buffer_consume(&s_state->circ_buffer, to_drop); + PBL_LOG_WRN("Dropping %u bytes of old audio", to_drop); } + circular_buffer_write(&s_state->circ_buffer, data, size); // Check if we have enough data for a complete frame size_t frame_size_bytes = s_state->audio_buffer_len * sizeof(int16_t); uint16_t available_data = circular_buffer_get_read_space_remaining(&s_state->circ_buffer); if (available_data >= frame_size_bytes && !s_state->main_pending) { s_state->main_pending = true; - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_dispatch_samples_main, - .data = NULL - } - }; - if (!event_put_isr(&e)) { + // Dispatch to system task instead of kernel event queue (matches asterix behavior) + bool should_context_switch = false; + if (!system_task_add_callback_from_isr(prv_dispatch_samples_system_task, NULL, &should_context_switch)) { s_state->main_pending = false; } } @@ -222,8 +294,21 @@ static bool prv_start_pdm_capture(const MicDevice *this) HAL_StatusTypeDef res; HAL_RCC_EnableModule(RCC_MOD_PDM1); res = HAL_PDM_Init(hpdm); + if (this->channels ==1) { + hpdm->Init.Channels = PDM_CHANNEL_LEFT_ONLY; + } else { + hpdm->Init.Channels = PDM_CHANNEL_STEREO; + } + hpdm->Init.SampleRate = this->sample_rate; + hpdm->Init.ChannelDepth = (uint32_t) this->channel_depth; HAL_PDM_Config(hpdm, PDM_CFG_CHANNEL | PDM_CFG_SAMPLERATE | PDM_CFG_DEPTH); - HAL_PDM_Set_Gain(hpdm, this->channels, CFG_AUDIO_RECORD_GAIN_DEFAULT); + HAL_PDM_Set_Gain(hpdm, PDM_CHANNEL_STEREO, this->state->volume); + + // 3.072M = 49.152M(audpll)/16, 96k sampling use 3.072M as clock. + if (hpdm->Init.clkSrc == 3072000 || hpdm->Init.SampleRate == PDM_SAMPLE_96KHZ) + { + bf0_enable_pll(hpdm->Init.SampleRate, 0); + } HAL_NVIC_EnableIRQ(this->pdm_dma_irq); HAL_NVIC_EnableIRQ(this->pdm_irq); res |= HAL_PDM_Receive_DMA(hpdm, hpdm->pRxBuffPtr, hpdm->RxXferSize); @@ -249,38 +334,63 @@ bool mic_start(const MicDevice *this, MicDataHandlerCB data_handler, void *conte return false; } if (!state->is_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Microphone not initialized"); + PBL_LOG_ERR("Microphone not initialized"); mutex_unlock_recursive(state->mutex); return false; } // Allocate buffers dynamically - if (!prv_allocate_buffers(state)) { + if (!prv_allocate_buffers(this)) { mutex_unlock_recursive(state->mutex); return false; } - hpdm->RxXferSize = this->channels * CFG_AUDIO_RECORD_PIPE_SIZE * 2; - hpdm->pRxBuffPtr = kernel_malloc(hpdm->RxXferSize); - PBL_ASSERT(hpdm->pRxBuffPtr, "Can not allocate buffer"); + hpdm->RxXferSize = this->channels * PDM_AUDIO_RECORD_PIPE_SIZE * sizeof(int16_t); + // Over-allocate by one cache line so the DMA buffer can start on a line + // boundary. dcache_invalidate() in the IRQ path would otherwise risk + // destroying dirty bytes in lines shared with neighboring allocations. + const size_t cache_align = dcache_line_size(); + state->raw_dma_buffer = kernel_malloc(hpdm->RxXferSize + cache_align - 1U); + PBL_ASSERT(state->raw_dma_buffer, "Can not allocate buffer"); + hpdm->pRxBuffPtr = (uint8_t *)(((uintptr_t)state->raw_dma_buffer + cache_align - 1U) & + ~(uintptr_t)(cache_align - 1U)); - // Reset state - circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, CIRCULAR_BUF_SIZE_BYTES); state->data_handler = data_handler; state->handler_context = context; state->audio_buffer = audio_buffer; state->audio_buffer_len = audio_buffer_len; state->main_pending = false; +#if PDM_POWER_NPM1300_LDO2 + (void)NPM1300_OPS.ldo2_set_enabled(true); +#endif // Set is_running to true BEFORE starting PDM, since the event handler will be called immediately state->is_running = true; + + // Prevent CPU from entering stop mode during audio capture + stop_mode_disable(InhibitorMic); + // Start PDM capture if (!prv_start_pdm_capture(this)) { - state->is_running = false; // Reset on failure + HAL_NVIC_DisableIRQ(this->pdm_dma_irq); + HAL_NVIC_DisableIRQ(this->pdm_irq); + HAL_PDM_DMAStop(hpdm); + HAL_PDM_DeInit(hpdm); + HAL_RCC_DisableModule(RCC_MOD_PDM1); + + kernel_free(state->raw_dma_buffer); + state->raw_dma_buffer = NULL; + hpdm->pRxBuffPtr = NULL; + + stop_mode_enable(InhibitorMic); + state->is_running = false; // Reset on failure +#if PDM_POWER_NPM1300_LDO2 + (void)NPM1300_OPS.ldo2_set_enabled(false); +#endif prv_free_buffers(state); mutex_unlock_recursive(state->mutex); return false; } - + mutex_unlock_recursive(state->mutex); return true; } @@ -309,16 +419,24 @@ void mic_stop(const MicDevice *this) { // Free dynamically allocated buffers prv_free_buffers(state); - kernel_free(hpdm->pRxBuffPtr); + kernel_free(state->raw_dma_buffer); + state->raw_dma_buffer = NULL; hpdm->pRxBuffPtr = NULL; - + // Clear state state->data_handler = NULL; state->handler_context = NULL; state->audio_buffer = NULL; state->audio_buffer_len = 0; state->main_pending = false; - + +#if PDM_POWER_NPM1300_LDO2 + (void)NPM1300_OPS.ldo2_set_enabled(false); +#endif + + // Allow CPU to enter stop mode again + stop_mode_enable(InhibitorMic); + mutex_unlock_recursive(state->mutex); } @@ -341,3 +459,8 @@ bool mic_is_running(const MicDevice *this) { return this->state->is_running; } + +uint32_t mic_get_channels(const MicDevice *this) { + PBL_ASSERTN(this); + return this->channels ? this->channels : 1; +} diff --git a/src/fw/drivers/mic/sf32lb52/pdm_definitions.h b/src/fw/drivers/mic/sf32lb52/pdm_definitions.h index 4fb438154d..6922ccb473 100644 --- a/src/fw/drivers/mic/sf32lb52/pdm_definitions.h +++ b/src/fw/drivers/mic/sf32lb52/pdm_definitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -24,21 +11,14 @@ #include #include -// PDM Configuration -#define PDM_BUFFER_SIZE_SAMPLES (320) -#define PDM_BUFFER_COUNT (2) -#define PDM_GAIN_DEFAULT (30) - -// Circular buffer configuration -#define CIRCULAR_BUF_SIZE_MS (128) -#define CIRCULAR_BUF_SIZE_SAMPLES ((MIC_SAMPLE_RATE * CIRCULAR_BUF_SIZE_MS) / 1000) -#define CIRCULAR_BUF_SIZE_BYTES (CIRCULAR_BUF_SIZE_SAMPLES * sizeof(int16_t)) - -// Note: If the MCU has cache, this needs to be placed in DMA_BSS. typedef struct MicState { - uint8_t *circ_buffer_storage; + uint8_t *circ_buffer_storage; CircularBuffer circ_buffer; DMA_HandleTypeDef hdma; + //! Raw (unaligned) pointer returned by kernel_malloc for the PDM DMA buffer. + //! hpdm->pRxBuffPtr is bumped up to a cache-line boundary so the CPU can + //! invalidate it without clobbering adjacent dirty data. + uint8_t *raw_dma_buffer; // User interface MicDataHandlerCB data_handler; diff --git a/src/fw/drivers/mic/stm32/dfsdm.c b/src/fw/drivers/mic/stm32/dfsdm.c deleted file mode 100644 index f8f6e1d2a0..0000000000 --- a/src/fw/drivers/mic/stm32/dfsdm.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/mic.h" - -#include "dfsdm_definitions.h" - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "kernel/events.h" -#include "kernel/util/sleep.h" -#include "kernel/util/stop.h" -#include "os/mutex.h" -#include "os/tick.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/profiler.h" -#include "util/circular_buffer.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include "FreeRTOS.h" -#include "semphr.h" - -#define MAX_VOLUME (256) -#define LFSR_SEED (0x3AEF) - -static bool prv_dma_handler(DMARequest *request, void *context, bool is_complete); - -static void prv_enable_clocks(MicDevice *this) { - // Enable the device clocks - periph_config_acquire_lock(); - periph_config_enable(this->filter, this->rcc_apb_periph); - periph_config_release_lock(); -} - -static void prv_disable_clocks(MicDevice *this) { - // Disable DFSDM clock - periph_config_acquire_lock(); - periph_config_disable(this->filter, this->rcc_apb_periph); - periph_config_release_lock(); -} - -//! Configure GPIOs for DFSDM use -static void prv_enable_gpio(MicDevice *this, GPIOPuPd_TypeDef data_pupd) { - gpio_af_init(&this->ck_gpio, GPIO_OType_PP, - GPIO_Medium_Speed, GPIO_PuPd_NOPULL); - - // During normal operation it is probably more power-efficient to let - // the data pin float (no pull) as no current would be wasted pulling - // against the data signal. But the mic's data output pin goes Hi-Z - // for half of each clock cycle, so power could be wasted on the - // input if the signal voltage moves around too much while the mic - // data output is Hi-Z. - // - // During self-test we want to enable a pull resistor so that we can - // accurately detect the absence of the mic. - gpio_af_init(&this->sd_gpio, GPIO_OType_PP, - GPIO_Medium_Speed, data_pupd); -} - -//! Configure GPIOs for lowest power consumption -static void prv_disable_gpio(MicDevice *this) { - // Configure the clock pin as an output driving low so the microphone - // won't see any unintentional clock edges which would wake it up - // from sleep mode. - gpio_af_configure_fixed_output(&this->ck_gpio, false); - - // Configure the data pin as an analog input, which is the lowest - // power state it can be in. The mic's data pin goes into Hi-Z mode - // when the mic is asleep, so the signal could float around and waste - // power if the pin is configured as a digital input. - gpio_af_configure_low_power(&this->sd_gpio); -} - -static void prv_dfsdm_configure(MicDevice *this) { - const uint32_t k_max_sinc4_osr = 255; - - PBL_ASSERTN(this->pdm_frequency > 0); - PBL_ASSERTN(this->pdm_frequency % MIC_SAMPLE_RATE == 0); - uint32_t oversampling_ratio = this->pdm_frequency / MIC_SAMPLE_RATE; - uint32_t sinc_order = (oversampling_ratio <= k_max_sinc4_osr) ? DFSDM_SincOrder_Sinc4 : - DFSDM_SincOrder_Sinc3; - - // Calculate the right shift needed to contain the final value within 24 bits - int num_bits = ceil_log_two(oversampling_ratio); - num_bits *= (sinc_order == DFSDM_SincOrder_Sinc4) ? 4 : 3; - uint32_t right_shift = MAX(num_bits - 24, 0); - - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - uint32_t prescaler = clocks.PCLK2_Frequency / this->pdm_frequency; - - // Disable the device before changing the config - DFSDM_Command(DISABLE); - DFSDM_ChannelCmd(this->channel, DISABLE); - DFSDM_FilterCmd(this->filter, DISABLE); - - DFSDM_TransceiverInitTypeDef DFSDM_InitStruct; - DFSDM_TransceiverStructInit(&DFSDM_InitStruct); - DFSDM_InitStruct.DFSDM_Interface = DFSDM_Interface_SPI_RisingEdge; - DFSDM_InitStruct.DFSDM_Clock = DFSDM_Clock_Internal; - DFSDM_InitStruct.DFSDM_Input = DFSDM_Input_External; - DFSDM_InitStruct.DFSDM_Redirection = DFSDM_Redirection_Disabled; - DFSDM_InitStruct.DFSDM_PackingMode = DFSDM_PackingMode_Standard; - DFSDM_InitStruct.DFSDM_DataRightShift = right_shift; - DFSDM_InitStruct.DFSDM_Offset = 0; - DFSDM_InitStruct.DFSDM_CLKAbsenceDetector = DFSDM_CLKAbsenceDetector_Disable; - DFSDM_InitStruct.DFSDM_ShortCircuitDetector = DFSDM_ShortCircuitDetector_Enable; - - DFSDM_TransceiverInit(this->channel, &DFSDM_InitStruct); - - DFSDM_FilterInitTypeDef DFSDM_FilterInitStruct; - DFSDM_FilterStructInit(&DFSDM_FilterInitStruct); - DFSDM_FilterInitStruct.DFSDM_SincOrder = sinc_order; - DFSDM_FilterInitStruct.DFSDM_FilterOversamplingRatio = oversampling_ratio; - DFSDM_FilterInitStruct.DFSDM_IntegratorOversamplingRatio = 1; - DFSDM_FilterInit(this->filter, &DFSDM_FilterInitStruct); - - DFSDM_ConfigClkOutputSource(DFSDM_ClkOutSource_SysClock); - - DFSDM_ConfigClkOutputDivider(prescaler); - DFSDM_SelectRegularChannel(this->filter, this->regular_channel); - - DFSDM_FastModeCmd(this->filter, ENABLE); -} - -// must have initialized both the DFSDM and DMA -static void prv_dfsdm_enable(MicDevice *this) { - // Enable DFSDM and DMA - DFSDM_Command(ENABLE); - DFSDM_ChannelCmd(this->channel, ENABLE); - - // Wait for microphone to power up - psleep(this->power_on_delay_ms); - - // Configure DFSDM to use DMA and start the filter + DMA - DFSDM_DMATransferConfig(this->filter, DFSDM_DMAConversionMode_Regular, ENABLE); - DFSDM_FilterCmd(this->filter, ENABLE); - DFSDM_RegularContinuousModeCmd(this->filter, ENABLE); - DFSDM_StartSoftwareRegularConversion(this->filter); - dma_request_start_circular(this->dma, this->state->in_buffer, (void *)&this->filter->FLTRDATAR, - sizeof(this->state->in_buffer), prv_dma_handler, (void *)this); -} - -static void prv_dfsdm_disable(MicDevice *this) { - // Disable DMA and DFSDM - dma_request_stop(this->dma); - DFSDM_ChannelCmd(this->channel, DISABLE); - DFSDM_Command(DISABLE); - - prv_disable_clocks(this); -} - -void mic_init(MicDevice *this) { - PBL_ASSERTN(!this->state->initialized); - - this->state->main_pending = false; - this->state->bg_pending = false; - this->state->running = false; - this->state->subscriber = (struct MicSubscriber){0}; - - this->state->volume = this->default_volume; - - prv_disable_gpio(this); - dma_request_init(this->dma); - - this->state->mic_mutex = mutex_create_recursive(); - this->state->initialized = true; -} - -void mic_set_volume(MicDevice *this, uint16_t volume) { - this->state->volume = MIN(MAX_VOLUME, volume); -} - -bool mic_start(MicDevice *this, MicDataHandlerCB data_handler, void *context, - int16_t *audio_buffer, size_t audio_buffer_len) { - PBL_ASSERTN(this->state->initialized); - mutex_lock_recursive(this->state->mic_mutex); - - bool success = false; - - if (this->state->running) { - goto unlock; - } - - circular_buffer_init(&this->state->circ_buffer, - this->state->circ_buf_store, - DFSDM_CIRC_BUFFER_SIZE); - - this->state->subscriber = (struct MicSubscriber) { - .callback = data_handler, - .buffer = audio_buffer, - .context = context, - .size = audio_buffer_len, - .idx = 0 - }; - this->state->overflow_cnt = 0; - this->state->bytes_received = 0; - this->state->samples_to_discard = (MIC_SAMPLE_RATE * this->settling_delay_ms) / MS_PER_SECOND; - - this->state->hpf_y1 = 0; - - if (this->mic_power_state_fn) { - this->mic_power_state_fn(true); - } - - // Seed the LFSR random number generator - this->state->prev_r = LFSR_SEED; - - prv_enable_gpio(this, GPIO_PuPd_NOPULL); - prv_enable_clocks(this); - prv_dfsdm_configure(this); - prv_dfsdm_enable(this); - - DFSDM_RegularContinuousModeCmd(this->filter, ENABLE); - DFSDM_StartSoftwareRegularConversion(this->filter); - - stop_mode_disable(InhibitorMic); - this->state->running = true; - success = true; - -unlock: - mutex_unlock_recursive(this->state->mic_mutex); - - return success; -} - -void mic_stop(MicDevice *this) { - mutex_lock_recursive(this->state->mic_mutex); - - if (!this->state->running) { - goto unlock; - } - - prv_dfsdm_disable(this); - prv_disable_gpio(this); - - if (this->mic_power_state_fn) { - this->mic_power_state_fn(false); - } - - stop_mode_enable(InhibitorMic); - this->state->running = false; - - PBL_LOG(LOG_LEVEL_DEBUG, "Stopped microphone, dropped samples: %"PRIu32" bytes received: %"PRIu32, - this->state->overflow_cnt, - this->state->bytes_received); - -unlock: - mutex_unlock_recursive(this->state->mic_mutex); -} - -bool mic_is_running(MicDevice *this) { - return this->state->running; -} - -static void prv_dispatch_samples(void *context_ptr) { - MicDevice *this = context_ptr; - mutex_lock_recursive(this->state->mic_mutex); - - // if this->running is set to false (mic_stop is called) - // while the loop is running, the remaining samples must be discarded. - // If mic_stop is called from the subscriber callback, no more samples - // must be read into the subscriber buffer (it is assumed to be invalid - // memory at that point) - while (this->state->running) { - uint16_t size = circular_buffer_copy( - &this->state->circ_buffer, - (uint8_t *)&this->state->subscriber.buffer[this->state->subscriber.idx], - ((this->state->subscriber.size - this->state->subscriber.idx) * - sizeof(int16_t))); - if (size == 0) { - break; - } - - // Only call the subscriber when the buffer is full. This takes away the - // overhead of handling this in the subscriber module - this->state->subscriber.idx += (size / sizeof(int16_t)); - if (this->state->subscriber.idx == this->state->subscriber.size) { - this->state->subscriber.callback(this->state->subscriber.buffer, - this->state->subscriber.idx, - this->state->subscriber.context); - this->state->subscriber.idx = 0; - } - - // Make sure to maintain correct alignment when consuming bytes - size -= size % sizeof(int16_t); - circular_buffer_consume(&this->state->circ_buffer, size); - } - mutex_unlock_recursive(this->state->mic_mutex); -} - -static void prv_dispatch_samples_main(void *context_ptr) { - MicDevice *this = context_ptr; - this->state->main_pending = false; - prv_dispatch_samples(context_ptr); -} - -static void prv_dispatch_samples_bg(void *context_ptr) { - MicDevice *this = context_ptr; - this->state->bg_pending = false; - prv_dispatch_samples(context_ptr); -} - -// Interrupt functions -//////////////////////////////////////////////////////////////////////////////////////////////// - -// Galois LFSR random number generator for dithering -static int16_t prv_get_lfsr(int16_t prev) { - uint16_t lfsr = (uint16_t)prev; - lfsr = (lfsr >> 1) ^ (-(lfsr & 1) & 0xB400); - return (int16_t)lfsr; -} - -static bool prv_dma_handler(DMARequest *request, void *context, bool is_complete) { - MicDevice *this = context; - bool should_context_switch = false; - - PROFILER_NODE_START(mic); - - const int32_t *dfsdm_buffer = this->state->in_buffer[is_complete ? 1 : 0]; - if (this->state->bytes_received == 0) { - // Seed the filter state to prevent transient at the beginning of recording - this->state->hpf_y1 = dfsdm_buffer[0] >> 8; - } - - this->state->bytes_received += DFSDM_BUFFER_LENGTH * sizeof(*dfsdm_buffer); - - for (int i = 0; i < DFSDM_BUFFER_LENGTH; i++) { - // move 24-bit value stored in upper 24-bits down to lower 24 bits - int32_t sample = dfsdm_buffer[i] >> 8; - - // Single pole IIR filter to remove DC offset (cutoff frequency: 10Hz) - // Filter coefficients pre-calculated - const int64_t b1 = 32639; // b1 = exp(2 * PI * Fc) where Fc is 10/16000, scaled by 2^15 - const int64_t a0 = 129; // a0 = 1 - b1, scaled by 2^15 - - // Filter calculation - int64_t y = a0 * (int64_t)sample + b1 * this->state->hpf_y1; - // Scale down the value - this->state->hpf_y1 = y >> 15; - // HPF derived from low pass filter result - sample -= this->state->hpf_y1; - - // Gain control - multiply the sample by up to MAX_VOLUME (256). This adds at most 8 bits to - // the signal for full 32-bit resolution. Afterwards, add dither to reduce quantization noise, - // then shift the signal down to fit it into 16 bits. - // Note: we do not shift by the full 16 bits because, for normal speech, the received signal - // is closer to 20/21 bits on bigboards. - - // Apply volume control - sample *= this->state->volume; - - // Dither the sample by adding 2-LSB (post-shift) random TPDF (Triangular Probability Density - // Function) noise. Create TPDF noise by summing 2 random numbers together. Use the previously - // generated random number for computational efficiency (low pass filters the noise somewhat, - // but the result is good). Shift the value - int16_t r = prv_get_lfsr(this->state->prev_r); - - // divide by 2 to prevent overflows - int16_t tpdf = (r / 2) + (this->state->prev_r / 2); - - this->state->prev_r = r; - tpdf >>= this->final_right_shift - 1; // Shift to 2-LSB size relative to sample - sample += tpdf; - - // Shift result to final bit depth and clip - sample >>= this->final_right_shift; - __SSAT(sample, 16); // saturate because we're clipping off the top of a 32-bit value - - if (this->state->samples_to_discard > 0) { - this->state->samples_to_discard--; - } else { - if (!circular_buffer_write(&this->state->circ_buffer, - (const uint8_t *) &sample, - sizeof(int16_t))) { - this->state->overflow_cnt++; - break; - } - } - } - - // Post a callback to KernelBG and KernelMain for faster servicing (check out PBL-40943 for more - // details) - bool main_switch_context = false; - bool system_task_switch_context = false; - if (!this->state->main_pending) { - this->state->main_pending = true; - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_dispatch_samples_main, - .data = (void *)this - } - }; - main_switch_context = event_put_isr(&e); - } - - if (!this->state->bg_pending) { - this->state->bg_pending = true; - system_task_add_callback_from_isr(prv_dispatch_samples_bg, (void *)this, - &system_task_switch_context); - } - - PROFILER_NODE_STOP(mic); - return system_task_switch_context || main_switch_context; -} - -extern MicDevice * const MIC; - -bool mic_selftest(void) { - stop_mode_disable(InhibitorMic); - - // Configure mic serial data pin with pull-down so that the GPIO does - // not float if there is an open-circuit condition on the pin. - prv_enable_gpio(MIC, GPIO_PuPd_DOWN); - - // Configure the DFSDM peripheral with short-circuit detection. - prv_enable_clocks(MIC); - // Set the short-circut threshold length to its maximum value. - DFSDM_ConfigShortCircuitThreshold(DFSDM1_Channel2, 255); - prv_dfsdm_configure(MIC); - - // Start DFSDM conversion without DMA. Throw out the samples; we only - // care about the short-circuit detection. - DFSDM_Command(ENABLE); - DFSDM_ChannelCmd(DFSDM1_Channel2, ENABLE); - DFSDM_FilterCmd((DFSDM_TypeDef *) DFSDM1_Filter0_BASE, ENABLE); - DFSDM_RegularContinuousModeCmd((DFSDM_TypeDef *) DFSDM1_Filter0_BASE, ENABLE); - DFSDM_StartSoftwareRegularConversion((DFSDM_TypeDef *) DFSDM1_Filter0_BASE); - - // Wait until the microphone wakes up. The datasheet max wakeup time - // for the two microphones we may use on Silk is around 30 ms. Add in - // some extra margin in case we make a running change and use another - // microphone that is even slower to wake up. - psleep(50); - - // Run the actual test. - DFSDM_ClearShortCircuitFlag(DFSDM_CLEARF_SCD_Channel2); - psleep(10); - const bool test_pass = DFSDM_GetShortCircuitFlagStatus(DFSDM_IT_SCD_Channel2) != SET; - - prv_dfsdm_disable(MIC); - prv_disable_gpio(MIC); - stop_mode_enable(InhibitorMic); - - return test_pass; -} diff --git a/src/fw/drivers/mic/stm32/dfsdm_definitions.h b/src/fw/drivers/mic/stm32/dfsdm_definitions.h deleted file mode 100644 index e1dbe49001..0000000000 --- a/src/fw/drivers/mic/stm32/dfsdm_definitions.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" -#include "drivers/mic.h" -#include -#include - -#include -#include - -#define DFSDM_BUFFER_LENGTH (256) // Each buffer fills up 64 times per second -#define DFSDM_CIRC_BUFFER_SIZE (DFSDM_BUFFER_LENGTH * sizeof(uint16_t) * 2) - -// Note: If the MCU has cache, this needs to be placed in DMA_BSS. -typedef struct MicState { - int32_t in_buffer[2][DFSDM_BUFFER_LENGTH]; // 2k - uint8_t circ_buf_store[DFSDM_CIRC_BUFFER_SIZE]; // 1k - CircularBuffer circ_buffer; - - bool initialized; - bool running; - bool main_pending; - bool bg_pending; - uint32_t overflow_cnt; - uint32_t bytes_received; - int samples_to_discard; - - int64_t hpf_y1; // Previous value of HPF output - int16_t prev_r; // Previous random number generated for dithering - - uint16_t volume; - - // A mutex is needed to protect against a race condition between - // mic_stop and the dispatch routine potentially resulting in the - // deallocation of the subscriber module's receive buffer while the - // dispatch routine is still running. - PebbleRecursiveMutex *mic_mutex; - - struct MicSubscriber { - MicDataHandlerCB callback; - int16_t *buffer; - void *context; - size_t size; - size_t idx; - } subscriber; -} MicDeviceState; - -typedef const struct MicDevice { - MicDeviceState *state; - - DFSDM_TypeDef *filter; - DFSDM_Channel_TypeDef *channel; - uint32_t extremes_detector_channel; - uint32_t regular_channel; - uint32_t pdm_frequency; - uint32_t rcc_apb_periph; - DMARequest *dma; - AfConfig ck_gpio; - AfConfig sd_gpio; - - int power_on_delay_ms; - int settling_delay_ms; - - // Volume scalar (max 256) - uint16_t default_volume; - - // Final right shift after applying gain control. It should be adjusted per watch family such that - // a volume of 128 provides approximate half of full-scale deflection for normal speech on a - // bigboard - int final_right_shift; - - //! Function pointer to power the microphone hardware on or off. - //! May be NULL. - void (*mic_power_state_fn)(bool enabled); -} MicDevice; diff --git a/src/fw/drivers/mic/stm32/pdm.c b/src/fw/drivers/mic/stm32/pdm.c deleted file mode 100644 index 3065335257..0000000000 --- a/src/fw/drivers/mic/stm32/pdm.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/mic.h" - -#include "board/board.h" -#include "drivers/accessory.h" -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pmic.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/stop.h" -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "services/common/system_task.h" -#if RECOVERY_FW -#include "services/prf/accessory/accessory_manager.h" -#else -#include "services/normal/accessory/accessory_manager.h" -#endif -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" -#include "system/profiler.h" -#include "util/circular_buffer.h" -#include "util/legacy_checksum.h" -#include "util/math.h" -#include "util/net.h" -#include "util/size.h" - -#define STM32F4_COMPATIBLE -#include - -#include "OpenPDMFilter.h" - -#define DECIMATION_FACTOR (64) -#define IN_BUF_BATCH_SIZE (8) -#define CIRCULAR_BUF_BATCH_SIZE (10) - -// This gives two 1K byte buffers for DMA, which will each fill in about 1/64s -#define IN_BUFFER_LENGTH (DECIMATION_FACTOR * IN_BUF_BATCH_SIZE) - -static uint16_t s_in_buffer[2][IN_BUFFER_LENGTH]; - -static uint8_t s_circ_buf_store[(MIC_SAMPLE_RATE / 1000) * CIRCULAR_BUF_BATCH_SIZE * 4 - * sizeof(uint16_t)]; - -static TPDMFilter_InitStruct s_pdm_filter; - -static uint16_t s_volume; - -static bool s_running = false; - -static bool s_main_pending = false; -static bool s_bg_pending = false; -static int s_overflow_cnt; -static bool s_initialized = false; -static uint8_t s_discarded; -static CircularBuffer s_circ_buffer; - -// A mutex is needed to protect against a race condition between mic_stop and the dispatch routine -// potentially resulting in the deallocation of the subscriber module's receive buffer while the -// dispatch routine is still running. -static PebbleRecursiveMutex *s_mic_mutex; - -static struct Subscriber { - MicDataHandlerCB callback; - int16_t *buffer; - void *context; - size_t size; - size_t idx; -} s_subscriber; - -static bool prv_dma_handler(DMARequest *request, void *context, bool is_complete); - -//! Initialize power management for the microphone. Note that different boards have different -//! ways of configuring power to the mic. -static void prv_mic_power_init(void) { - if (BOARD_CONFIG.mic_config.mic_gpio_power.gpio) { - gpio_use(BOARD_CONFIG.mic_config.mic_gpio_power.gpio); - - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed; - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG.mic_config.mic_gpio_power.gpio_pin; - GPIO_Init(GPIOF, &GPIO_InitStructure); - - gpio_output_set(&BOARD_CONFIG.mic_config.mic_gpio_power, false); - - gpio_release(BOARD_CONFIG.mic_config.mic_gpio_power.gpio); - } -} - -static void prv_i2s_gpio_init(void) { - // Enable the SPI clock - periph_config_acquire_lock(); - - // [AS] TODO: If I2S is moved to SPI1, this RCC function needs to be abstracted to board.h - PBL_ASSERTN(BOARD_CONFIG.mic_config.spi_clock_ctrl != RCC_APB2Periph_SPI1); - periph_config_enable(BOARD_CONFIG.mic_config.spi, - BOARD_CONFIG.mic_config.spi_clock_ctrl); - periph_config_release_lock(); - - gpio_use(BOARD_CONFIG.mic_config.i2s_ck.gpio); - gpio_use(BOARD_CONFIG.mic_config.i2s_sd.gpio); - - // Configure pins as SPI/I2S pins - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; - GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed; - - // I2S CK - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG.mic_config.i2s_ck.gpio_pin; - GPIO_Init(BOARD_CONFIG.mic_config.i2s_ck.gpio, &GPIO_InitStructure); - GPIO_PinAFConfig(BOARD_CONFIG.mic_config.i2s_ck.gpio, - BOARD_CONFIG.mic_config.i2s_ck.gpio_pin_source, BOARD_CONFIG.mic_config.i2s_ck.gpio_af); - - // I2S SD - GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG.mic_config.i2s_sd.gpio_pin; - GPIO_Init(BOARD_CONFIG.mic_config.i2s_sd.gpio, &GPIO_InitStructure); - GPIO_PinAFConfig(BOARD_CONFIG.mic_config.i2s_sd.gpio, - BOARD_CONFIG.mic_config.i2s_sd.gpio_pin_source, BOARD_CONFIG.mic_config.i2s_sd.gpio_af); - - gpio_release(BOARD_CONFIG.mic_config.i2s_ck.gpio); - gpio_release(BOARD_CONFIG.mic_config.i2s_sd.gpio); - - // I2S configuration - SPI_I2S_DeInit(BOARD_CONFIG.mic_config.spi); - I2S_InitTypeDef I2S_InitStructure; - I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_32k; - I2S_InitStructure.I2S_Standard = I2S_Standard_LSB; - I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b; - I2S_InitStructure.I2S_CPOL = I2S_CPOL_High; - I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx; - I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable; - I2S_Init(BOARD_CONFIG.mic_config.spi, &I2S_InitStructure); - - periph_config_acquire_lock(); - periph_config_disable(BOARD_CONFIG.mic_config.spi, - BOARD_CONFIG.mic_config.spi_clock_ctrl); - periph_config_release_lock(); -} - -static void prv_mic_power_enable(void) { - if (BOARD_CONFIG.mic_config.mic_gpio_power.gpio) { - gpio_output_set(&BOARD_CONFIG.mic_config.mic_gpio_power, true); - } else { - set_ldo3_power_state(true); - } -} - -static void prv_mic_power_disable(void) { - if (BOARD_CONFIG.mic_config.mic_gpio_power.gpio) { - gpio_output_set(&BOARD_CONFIG.mic_config.mic_gpio_power, false); - } else { - set_ldo3_power_state(false); - } -} - -void mic_init(MicDevice *this) { - PBL_ASSERTN(!s_initialized); - - s_main_pending = false; - s_bg_pending = false; - s_running = false; - s_subscriber = (struct Subscriber){0}; - - s_volume = BOARD_CONFIG.mic_config.gain; - - s_pdm_filter.Fs = MIC_SAMPLE_RATE; - s_pdm_filter.LP_HZ = 8000; - s_pdm_filter.HP_HZ = 10; - s_pdm_filter.Out_MicChannels = 1; - s_pdm_filter.In_MicChannels = 1; - s_pdm_filter.Decimation = DECIMATION_FACTOR; - s_pdm_filter.MaxVolume = 64; - - dma_request_init(MIC_I2S_RX_DMA); - - prv_i2s_gpio_init(); - prv_mic_power_init(); - - s_mic_mutex = mutex_create_recursive(); - - s_initialized = true; -} - -void mic_set_volume(MicDevice *this, uint16_t volume) { - s_volume = volume; -} - -bool mic_start(MicDevice *mic, MicDataHandlerCB data_handler, void *context, - int16_t *audio_buffer, size_t audio_buffer_len) { - PBL_ASSERTN(s_initialized); - mutex_lock_recursive(s_mic_mutex); - - bool success = false; - - if (s_running) { - goto unlock; - } - - prv_mic_power_enable(); - - circular_buffer_init(&s_circ_buffer, s_circ_buf_store, sizeof(s_circ_buf_store)); - s_subscriber = (struct Subscriber) { - .callback = data_handler, - .buffer = audio_buffer, - .context = context, - .size = audio_buffer_len, - .idx = 0 - }; - s_overflow_cnt = 0; - s_discarded = 0; - - // The filter library checks that the CRC is present on the platform. Yay DRM - periph_config_enable(CRC, RCC_AHB1Periph_CRC); - CRC_ResetDR(); - Open_PDM_Filter_Init(&s_pdm_filter); - periph_config_disable(CRC, RCC_AHB1Periph_CRC); - - //Enable I2S PLL - RCC_PLLI2SCmd(ENABLE); - while (RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY) == RESET) {} - - // Enable I2S clock - periph_config_acquire_lock(); - periph_config_enable(BOARD_CONFIG.mic_config.spi, BOARD_CONFIG.mic_config.spi_clock_ctrl); - periph_config_release_lock(); - - // Configure I2S to use DMA - SPI_I2S_DMACmd(BOARD_CONFIG.mic_config.spi, SPI_I2S_DMAReq_Rx, ENABLE); - - // Enable I2S - I2S_Cmd(BOARD_CONFIG.mic_config.spi, ENABLE); - - // DMA config - use single buffer circular mode. Pointer to buffer is a 2-D array for ease of - // access - void *periph_addr = (void *)&BOARD_CONFIG.mic_config.spi->DR; - stop_mode_disable(InhibitorMic); - dma_request_start_circular(MIC_I2S_RX_DMA, s_in_buffer, periph_addr, sizeof(s_in_buffer), - prv_dma_handler, NULL); - - s_running = true; - success = true; - -unlock: - mutex_unlock_recursive(s_mic_mutex); - - return success; -} - -void mic_stop(MicDevice *this) { - mutex_lock_recursive(s_mic_mutex); - - if (!s_running) { - goto unlock; - } - - // Disable DMA and I2S - dma_request_stop(MIC_I2S_RX_DMA); - I2S_Cmd(BOARD_CONFIG.mic_config.spi, DISABLE); - - // Disable I2S clock - periph_config_acquire_lock(); - periph_config_disable(BOARD_CONFIG.mic_config.spi, - BOARD_CONFIG.mic_config.spi_clock_ctrl); - periph_config_release_lock(); - - // Disable I2S PLL - RCC_PLLI2SCmd(DISABLE); - - prv_mic_power_disable(); - stop_mode_enable(InhibitorMic); - s_running = false; - - PBL_LOG(LOG_LEVEL_DEBUG, "Stopped microphone, dropped samples: %d", s_overflow_cnt); - -unlock: - mutex_unlock_recursive(s_mic_mutex); -} - -bool mic_is_running(MicDevice *this) { - return s_running; -} - -static void prv_dispatch_samples_common(void) { - mutex_lock_recursive(s_mic_mutex); - - // if s_running is set to false (mic_stop is called) while the loop is running, the - // remaining samples must be discarded. If mic_stop is called from the subscriber callback, - // no more samples must be read into the subscriber buffer (it is assumed to be invalid memory at - // that point) - while (s_running) { - uint16_t size = circular_buffer_copy(&s_circ_buffer, - (uint8_t *) &s_subscriber.buffer[s_subscriber.idx], - ((s_subscriber.size - s_subscriber.idx) * sizeof(int16_t))); - - if (size == 0) { - break; - } - - // Only call the subscriber when the buffer is full. This takes away the - // overhead of handling this in the subscriber module - s_subscriber.idx += (size / sizeof(int16_t)); - if (s_subscriber.idx == s_subscriber.size) { - s_subscriber.callback(s_subscriber.buffer, s_subscriber.idx, s_subscriber.context); - s_subscriber.idx = 0; - } - - // Make sure to maintain correct alignment when consuming bytes - size -= size % sizeof(int16_t); - circular_buffer_consume(&s_circ_buffer, size); - } - - mutex_unlock_recursive(s_mic_mutex); -} - -static void prv_dispatch_samples_main(void* data) { - // Setting this to false before we process the data means that we'll have at most 2 callbacks on - // the queue. Putting it after the processing step means that there is a possible race - // condition with setting and clearing the flag that could result in overflow - s_main_pending = false; - - prv_dispatch_samples_common(); -} - -static void prv_dispatch_samples_bg(void* data) { - // Setting this to false before we process the data means that we'll have at most 2 callbacks on - // the queue. Putting it after the processing step means that there is a possible race - // condition with setting and clearing the flag that could result in overflow - s_bg_pending = false; - - prv_dispatch_samples_common(); -} - - -// Interrupt functions -//////////////////////////////////////////////////////////////////////////////////////////////// - -static bool prv_dma_handler(DMARequest *request, void *context, bool is_complete) { - const uint8_t MS_TO_SETTLE = 100; - PROFILER_NODE_START(mic); - - uint16_t *pdm_buffer = s_in_buffer[is_complete ? 1 : 0]; - // byte endianness needs to be swapped for the filter library - for (size_t i = 0; i < ARRAY_LENGTH(s_in_buffer[0]); i++) { - pdm_buffer[i] = HTONS(pdm_buffer[i]); - } - - bool overflow = false; - uint16_t pcm16_buffer[MIC_SAMPLE_RATE / 1000]; // Store enough for 1 millisecond of data - uint16_t *pdm_end = pdm_buffer + ARRAY_LENGTH(s_in_buffer[0]); - while (pdm_buffer < pdm_end) { - // Process one millisecond of data per call - Open_PDM_Filter_64((uint8_t *)pdm_buffer, pcm16_buffer, s_volume, &s_pdm_filter); - pdm_buffer += DECIMATION_FACTOR; - - // while the filter is settling discard samples (about 100 ms) - // each iteration of this loop writes 1 ms of data to the buffer - if (s_discarded < MS_TO_SETTLE) { - s_discarded++; - } else if (!circular_buffer_write(&s_circ_buffer, (const uint8_t *) pcm16_buffer, - sizeof(pcm16_buffer))) { - overflow = true; - } - } - - // Only count one overflow per interrupt - if (overflow) { - s_overflow_cnt++; - } - - // We post an event to both KernelMain and KernelBG. It is critical that the microphone - // data be processed quickly so that we don't encounter buffer overruns. Occasionally - // KernelMain can be busy for long periods of time (24ms to do a display DMA for example) - // so we also post to KernelBG. Whichever happens to get to the event first will process the - // buffer and the other task will quickly find that the buffer has already been emptied. - bool main_switch_context = false; - bool system_task_switch_context = false; - if (!s_main_pending) { - // Only post a callback event if one is not already pending - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_dispatch_samples_main, - .data = NULL - } - }; - s_main_pending = true; - main_switch_context = event_put_isr(&e); - } - - if (!s_bg_pending) { - // Only post a callback event if one is not already pending - s_bg_pending = true; - system_task_add_callback_from_isr(prv_dispatch_samples_bg, NULL, - &system_task_switch_context); - } - - PROFILER_NODE_STOP(mic); - return main_switch_context || system_task_switch_context; -} diff --git a/src/fw/drivers/mic/wscript_build b/src/fw/drivers/mic/wscript_build new file mode 100644 index 0000000000..7853d8d5c8 --- /dev/null +++ b/src/fw/drivers/mic/wscript_build @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['freertos', 'fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_MIC_NRF5: + sources.append('nrf5/pdm.c') + use.append('hal_nordic') +elif bld.env.CONFIG_MIC_SF32LB: + sources.append('sf32lb52/pdm.c') + use.append('hal_sifli') +elif bld.env.CONFIG_MIC_QEMU: + sources.append('qemu/mic.c') + +bld.objects( + name='driver_mic', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_mic') diff --git a/src/fw/drivers/mpu.c b/src/fw/drivers/mpu.c index b7a72f6260..ae63f80c8c 100644 --- a/src/fw/drivers/mpu.c +++ b/src/fw/drivers/mpu.c @@ -1,272 +1,32 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mpu.h" #include "mcu/cache.h" #include "system/passert.h" -#include "util/size.h" - -#include #include "FreeRTOS.h" #include "task.h" #include "portmacro.h" -#define CMSIS_COMPATIBLE -#include +#include extern const uint32_t __SRAM_size__[]; #if !defined(SRAM_BASE) -// On the STM32F2, SRAM_BASE is not defined, but is equal to SRAM1_BASE -#if defined(MICRO_FAMILY_NRF52840) +#if defined(CONFIG_SOC_NRF52) #include #define SRAM_BASE (0x20000000UL) -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) #define SRAM_BASE (0x20000000UL) -#else -#define SRAM_BASE SRAM1_BASE #endif #endif #define SRAM_END (SRAM_BASE + (uint32_t)__SRAM_size__) -typedef struct PermissionMapping { - bool priv_read:1; - bool priv_write:1; - bool user_read:1; - bool user_write:1; -#ifdef MPU_ARMV8 - uint8_t value:2; -#else - uint8_t value:3; -#endif -} PermissionMapping; - -static const PermissionMapping s_permission_mappings[] = { -#ifdef MPU_ARMV8 - // NOTE(1): we cannot have all accesses disabled, keep RO by privileged code only. - // NOTE(2): we cannot have different write access for priv/unpriv, allow R/W to any level - { false, false, false, false, 0x2 }, // AP=0b10: RO by privileged code only (1) - { true, true, false, false, 0x0 }, // AP=0b00: R/W by privileged code only - { true, true, true, false, 0x1 }, // AP=0b01: R/W by any privilege level (2) - { true, true, true, true, 0x1 }, // AP=0b01: R/W by any privilege level - { true, false, false, false, 0x2 }, // AP=0b10: RO by privileged code only - { true, false, true, false, 0x3 }, // AP=0b11: RO by by any privilege level -#else - { false, false, false, false, 0x0 }, - { true, true, false, false, 0x1 }, - { true, true, true, false, 0x2 }, - { true, true, true, true, 0x3 }, - { true, false, false, false, 0x5 }, - { true, false, true, false, 0x6 }, - { true, false, true, false, 0x7 } // Both 0x6 and 0x7 map to the same permissions. -#endif -}; - -static const uint32_t s_cache_settings[] = { -#ifdef MPU_ARMV8 - [MpuCachePolicy_NotCacheable] = ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, - ARM_MPU_ATTR_NON_CACHEABLE), - [MpuCachePolicy_WriteThrough] = ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), - ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0)), - [MpuCachePolicy_WriteBackWriteAllocate] = ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), - ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1)), - [MpuCachePolicy_WriteBackNoWriteAllocate] = ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 0, 1), - ARM_MPU_ATTR_MEMORY_(1, 1, 0, 1)) -#else - [MpuCachePolicy_NotCacheable] = (0x1 << MPU_RASR_TEX_Pos) | (MPU_RASR_S_Msk), - [MpuCachePolicy_WriteThrough] = (MPU_RASR_S_Msk | MPU_RASR_C_Msk), - [MpuCachePolicy_WriteBackWriteAllocate] = - (0x1 << MPU_RASR_TEX_Pos) | (MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk), - [MpuCachePolicy_WriteBackNoWriteAllocate] = - (MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk), -#endif -}; - -static uint8_t get_permission_value(const MpuRegion* region) { - for (unsigned int i = 0; i < ARRAY_LENGTH(s_permission_mappings); ++i) { - if (s_permission_mappings[i].priv_read == region->priv_read && - s_permission_mappings[i].priv_write == region->priv_write && - s_permission_mappings[i].user_read == region->user_read && - s_permission_mappings[i].user_write == region->user_write) { - return s_permission_mappings[i].value; - } - } - WTF; - return 0; -} - -#ifndef MPU_ARMV8 -static uint32_t get_size_field(const MpuRegion* region) { - unsigned int size = 32; - int result = 4; - while (size != region->size) { - PBL_ASSERT(size < region->size || size == 0x400000, "Invalid region size: %"PRIu32, - region->size); - - size *= 2; - ++result; - } - - return result; -} -#endif - -void mpu_enable(void) { -#ifdef MPU_ARMV8 - ARM_MPU_SetMemAttr(MpuCachePolicy_NotCacheable, - s_cache_settings[MpuCachePolicy_NotCacheable]); - ARM_MPU_SetMemAttr(MpuCachePolicy_WriteThrough, - s_cache_settings[MpuCachePolicy_WriteThrough]); - ARM_MPU_SetMemAttr(MpuCachePolicy_WriteBackWriteAllocate, - s_cache_settings[MpuCachePolicy_WriteBackWriteAllocate]); - ARM_MPU_SetMemAttr(MpuCachePolicy_WriteBackNoWriteAllocate, - s_cache_settings[MpuCachePolicy_WriteBackNoWriteAllocate]); -#endif - - ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk); -} - void mpu_disable(void) { ARM_MPU_Disable(); } -// Get the required region base address and region attribute register settings for the given region. -// These are the values which should written to the RBAR and RASR registers to configure that -// region. -void mpu_get_register_settings(const MpuRegion* region, uint32_t *base_address_reg, - uint32_t *attributes_reg) { - PBL_ASSERTN(region); - PBL_ASSERTN((region->base_address & 0x1f) == 0); - PBL_ASSERTN((region->region_num & ~0xf) == 0); - PBL_ASSERTN((region->cache_policy < ARRAY_LENGTH(s_cache_settings))); - -#ifdef MPU_ARMV8 - PBL_ASSERTN((region->size & 0x1f) == 0); - - *base_address_reg = ((region->base_address & MPU_RBAR_BASE_Msk) | - ((ARM_MPU_SH_INNER << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk) | - ((get_permission_value(region) << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk)); - *attributes_reg = (((region->base_address + region->size - 1U) & MPU_RLAR_LIMIT_Msk) | - ((region->cache_policy << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | - ((region->enabled << MPU_RLAR_EN_Pos) & MPU_RLAR_EN_Msk)); -#else - // MPU Region Base Address Register - // | Addr (27 bits) | Region Valid Bit | Region Num (4 bits) | - // The address is unshifted, we take the top bits of the address and assume everything below - // is zero, since the address must be power of 2 size aligned. - *base_address_reg = region->base_address | - 0x1 << 4 | - region->region_num; - - // MPU Region Attribute and Size Register - // A lot of stuff here! Split into bytes... - // | Reserved (3 bits) | XN Bit | Reserved Bit | Permission Field (3 bits) | - // | Reserved (2 bits) | TEX (3 bits) | S | C | B | - // | Subregion Disable Byte | - // | Reserved (2 bits) | Size Field (5 bits) | Enable Bit | - *attributes_reg = (get_permission_value(region) << 24) | - s_cache_settings[region->cache_policy] | - region->disabled_subregions << 8 | // Disabled subregions - (get_size_field(region) << 1) | - region->enabled; // Enabled -#endif -} - - -void mpu_set_region(const MpuRegion* region) { - uint32_t base_reg, attr_reg; - - mpu_get_register_settings(region, &base_reg, &attr_reg); - -#ifdef MPU_ARMV8 - ARM_MPU_SetRegion(region->region_num, base_reg, attr_reg); -#else - MPU->RBAR = base_reg; - MPU->RASR = attr_reg; -#endif -} - - -MpuRegion mpu_get_region(int region_num) { -#ifdef MPU_ARMV8 - MpuRegion region; - uint32_t rbar, rlar; - uint8_t access_permissions; - - region.region_num = region_num; - - MPU->RNR = region_num; - rbar = MPU->RBAR; - rlar = MPU->RLAR; - - region.base_address = rbar & MPU_RBAR_BASE_Msk; - - access_permissions = (rbar & MPU_RBAR_AP_Msk) >> MPU_RBAR_AP_Pos; - for (size_t i = 0; i < ARRAY_LENGTH(s_permission_mappings); ++i) { - if (s_permission_mappings[i].value == access_permissions) { - region.priv_read = s_permission_mappings[i].priv_read; - region.priv_write = s_permission_mappings[i].priv_write; - region.user_read = s_permission_mappings[i].user_read; - region.user_write = s_permission_mappings[i].user_write; - break; - } - } - - region.size = (rlar & MPU_RLAR_LIMIT_Msk) - region.base_address + 0x20; - region.enabled = (rlar & MPU_RLAR_EN_Msk) != 0; - region.cache_policy = (rlar & MPU_RLAR_AttrIndx_Msk) >> MPU_RLAR_AttrIndx_Pos; - - return region; -#else - MpuRegion region = { .region_num = region_num }; - - MPU->RNR = region_num; - - const uint32_t attributes = MPU->RASR; - - region.enabled = attributes & 0x1; - - if (region.enabled) { - const uint8_t size_field = (attributes >> 1) & 0x1f; - region.size = 32 << (size_field - 4); - - region.disabled_subregions = (attributes & 0x0000ff00) >> 8; - - const uint32_t raw_base_address = MPU->RBAR; - region.base_address = raw_base_address & ~(region.size - 1); - - const uint8_t access_permissions = (attributes >> 24) & 0x7; - - for (unsigned int i = 0; i < ARRAY_LENGTH(s_permission_mappings); ++i) { - if (s_permission_mappings[i].value == access_permissions) { - region.priv_read = s_permission_mappings[i].priv_read; - region.priv_write = s_permission_mappings[i].priv_write; - region.user_read = s_permission_mappings[i].user_read; - region.user_write = s_permission_mappings[i].user_write; - break; - } - } - } - - return region; -#endif -} - - // Fill in the task parameters for a new task with the configurable memory regions we want. void mpu_set_task_configurable_regions(MemoryRegion_t *memory_regions, const MpuRegion **region_ptrs) { @@ -282,6 +42,7 @@ void mpu_set_task_configurable_regions(MemoryRegion_t *memory_regions, // If not region defined, use unused if (mpu_region == NULL) { mpu_region = &unused_region; + base_reg = 0; attr_reg = 0; // Has a 0 in the enable bit, so this region won't be enabled. } else { // Make sure that the region numbers passed in jive with the configurable region numbers. @@ -292,8 +53,18 @@ void mpu_set_task_configurable_regions(MemoryRegion_t *memory_regions, mpu_get_register_settings(mpu_region, &base_reg, &attr_reg); } + // On ARMv8-M the FreeRTOS port writes pvBaseAddress straight into + // MPU_RBAR, which carries the SH/AP/XN bits. On ARMv7-M the port ORs in + // VALID and the region number, so pass only the aligned block base from + // mpu_get_register_settings(). +#ifdef CONFIG_MPU_TYPE_ARMV8M + uintptr_t base_address = base_reg; +#else + uintptr_t base_address = base_reg & ~(MPU_RBAR_VALID_Msk | MPU_RBAR_REGION_Msk); +#endif + memory_regions[region_idx] = (MemoryRegion_t) { - .pvBaseAddress = (void *)mpu_region->base_address, + .pvBaseAddress = (void *)base_address, .ulLengthInBytes = mpu_region->size, .ulParameters = attr_reg, }; @@ -312,7 +83,10 @@ bool mpu_memory_is_cachable(const void *addr) { } void mpu_init_region_from_region(MpuRegion *copy, const MpuRegion *from, bool allow_user_access) { + // Caller-side invariant: `from` is a PrivRW region (App/Worker RAM). + // Toggle user RW based on which task is about to run. + PBL_ASSERTN(from->permissions == MpuPermissions_PrivRW); *copy = *from; - copy->user_read = allow_user_access; - copy->user_write = allow_user_access; + copy->permissions = allow_user_access ? MpuPermissions_PrivRW_UserRW + : MpuPermissions_PrivRW; } diff --git a/src/fw/drivers/mpu.h b/src/fw/drivers/mpu.h index 3c5788675d..18bf5f67eb 100644 --- a/src/fw/drivers/mpu.h +++ b/src/fw/drivers/mpu.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,38 +8,48 @@ #include "freertos_types.h" -#ifdef MICRO_FAMILY_SF32LB52 -#define MPU_ARMV8 -#endif - typedef enum MpuCachePolicy { - // FIXME(SF32LB52): system_bf0_ap.c uses now up to 4 attributes as MPU is not fully implemented. -#ifdef MICRO_FAMILY_SF32LB52 - MpuCachePolicy_Reserved0, - MpuCachePolicy_Reserved1, - MpuCachePolicy_Reserved2, - MpuCachePolicy_Reserved3, -#endif MpuCachePolicy_NotCacheable, MpuCachePolicy_WriteThrough, MpuCachePolicy_WriteBackWriteAllocate, MpuCachePolicy_WriteBackNoWriteAllocate, } MpuCachePolicy; +// Symbolic access-permission set. The backends map these to the hardware AP +// encoding (3 bits on ARMv7-M, 2 bits on ARMv8-M). Two values are degraded +// on ARMv8-M because the two-bit AP field cannot express them precisely: +// - NoAccess: ARMv8-M has no "deny everything" encoding; the closest +// match is PrivRO, which still allows privileged reads. Don't rely on +// this for thread stack-overflow detection -- use PSPLIM instead. +// - PrivRW_UserRO: ARMv8-M cannot split write access by privilege; this +// is aliased to PrivRW_UserRW (user gains write access too). +typedef enum MpuPermissions { + MpuPermissions_NoAccess, + MpuPermissions_PrivRW, + MpuPermissions_PrivRW_UserRO, + MpuPermissions_PrivRW_UserRW, + MpuPermissions_PrivRO, + MpuPermissions_PrivRO_UserRO, + MpuPermissionsCount, +} MpuPermissions; + +// Describes a memory region by its real (base, size). The ARMv7-M backend +// internally rounds up to a power-of-two block and computes the subregion +// mask required by the hardware; the ARMv8-M backend programs the limit +// register directly. Callers never see these implementation details. typedef struct MpuRegion { uint8_t region_num:4; bool enabled:1; + // Allow instruction fetch from this region. Maps to XN=0 in the + // hardware encoding (RASR.XN on ARMv7-M, RBAR.XN on ARMv8-M). The + // default (false) sets XN=1 -- safer, since most regions hold data + // and shouldn't be executable. Flash, App RAM, Worker RAM and any + // text section relocated to RAM need to opt in. + bool executable:1; uintptr_t base_address; uint32_t size; MpuCachePolicy cache_policy; - bool priv_read:1; - bool priv_write:1; - bool user_read:1; - bool user_write:1; - // FIXME(SF32LB52): ARMv8 MPU does not support subregions, analyze possible solutions -#ifndef MPU_ARMV8 - uint8_t disabled_subregions; // 8 bits, each disables 1/8 of the region. -#endif + MpuPermissions permissions; } MpuRegion; void mpu_enable(void); diff --git a/src/fw/drivers/mpu/mpu_armv7m.c b/src/fw/drivers/mpu/mpu_armv7m.c new file mode 100644 index 0000000000..746f77d991 --- /dev/null +++ b/src/fw/drivers/mpu/mpu_armv7m.c @@ -0,0 +1,197 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/mpu.h" + +#include "system/passert.h" +#include "util/size.h" + +#include + +#include + +// ARMv7-M has a 3-bit AP field, so every MpuPermissions value maps to a +// unique encoding (0x4 is reserved; 0x6/0x7 both decode to "RO any priv", +// the table picks 0x6 canonically and mpu_get_region() accepts either). +static const uint8_t s_permission_to_ap[MpuPermissionsCount] = { + [MpuPermissions_NoAccess] = 0x0, + [MpuPermissions_PrivRW] = 0x1, + [MpuPermissions_PrivRW_UserRO] = 0x2, + [MpuPermissions_PrivRW_UserRW] = 0x3, + [MpuPermissions_PrivRO] = 0x5, + [MpuPermissions_PrivRO_UserRO] = 0x6, +}; + +static const uint32_t s_cache_settings[] = { + [MpuCachePolicy_NotCacheable] = (0x1 << MPU_RASR_TEX_Pos) | (MPU_RASR_S_Msk), + [MpuCachePolicy_WriteThrough] = (MPU_RASR_S_Msk | MPU_RASR_C_Msk), + [MpuCachePolicy_WriteBackWriteAllocate] = + (0x1 << MPU_RASR_TEX_Pos) | (MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk), + [MpuCachePolicy_WriteBackNoWriteAllocate] = + (MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk), +}; + +static uint8_t get_permission_value(const MpuRegion* region) { + PBL_ASSERTN(region->permissions < MpuPermissionsCount); + return s_permission_to_ap[region->permissions]; +} + +static MpuPermissions decode_permission_value(uint8_t ap) { + // 0x4 is reserved; 0x7 aliases 0x6 (PrivRO_UserRO). + switch (ap & 0x7) { + case 0x0: return MpuPermissions_NoAccess; + case 0x1: return MpuPermissions_PrivRW; + case 0x2: return MpuPermissions_PrivRW_UserRO; + case 0x3: return MpuPermissions_PrivRW_UserRW; + case 0x5: return MpuPermissions_PrivRO; + case 0x6: + case 0x7: return MpuPermissions_PrivRO_UserRO; + default: return MpuPermissions_NoAccess; + } +} + +// ARMv7-M MPU regions must be power-of-two sized and naturally aligned. To +// cover an arbitrary (base, size) range we pick the smallest power-of-two +// block (32B..4MB) that: +// - covers [base, base+size), +// - is naturally aligned to its own size, +// - has [base, base+size) starting and ending on subregion boundaries +// (subregion = block/8). +// The 8-bit subregion-disable mask then masks off the parts of the block +// that fall outside [base, base+size). +#define MPU_ARMV7M_MIN_REGION_SIZE 32u +#define MPU_ARMV7M_MAX_REGION_SIZE (4u * 1024u * 1024u) +#define MPU_ARMV7M_NUM_SUBREGIONS 8u + +typedef struct { + uintptr_t block_base; + uint32_t block_size; + uint8_t disabled_subregions; +} BlockLayout; + +static BlockLayout compute_block_layout(uintptr_t base, uint32_t size) { + PBL_ASSERT(size >= MPU_ARMV7M_MIN_REGION_SIZE, "MPU region too small: %"PRIu32, size); + + uint32_t block = MPU_ARMV7M_MIN_REGION_SIZE; + while (block < size) { + block <<= 1; + } + while (block <= MPU_ARMV7M_MAX_REGION_SIZE) { + const uint32_t subregion = block / MPU_ARMV7M_NUM_SUBREGIONS; + const uintptr_t start_in_block = base & (block - 1); + const uintptr_t end_in_block = start_in_block + size; + if ((start_in_block & (subregion - 1)) == 0 && + (end_in_block & (subregion - 1)) == 0 && + end_in_block <= block) { + const unsigned start_sub = start_in_block / subregion; + const unsigned end_sub = end_in_block / subregion; + uint8_t mask = 0xFF; + for (unsigned i = start_sub; i < end_sub; i++) { + mask &= (uint8_t)~(1u << i); + } + return (BlockLayout) { + .block_base = base - start_in_block, + .block_size = block, + .disabled_subregions = mask, + }; + } + block <<= 1; + } + PBL_CROAK("MPU region cannot fit subregion alignment: base=0x%08"PRIxPTR" size=0x%"PRIx32, + base, size); +} + +static uint32_t get_size_field(uint32_t block_size) { + unsigned int size = MPU_ARMV7M_MIN_REGION_SIZE; + int result = 4; + while (size != block_size) { + size *= 2; + ++result; + } + return result; +} + +void mpu_enable(void) { + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk); +} + +// Get the required region base address and region attribute register settings for the given region. +// These are the values which should written to the RBAR and RASR registers to configure that +// region. +void mpu_get_register_settings(const MpuRegion* region, uint32_t *base_address_reg, + uint32_t *attributes_reg) { + PBL_ASSERTN(region); + PBL_ASSERTN((region->base_address & 0x1f) == 0); + PBL_ASSERTN((region->region_num & ~0xf) == 0); + PBL_ASSERTN((region->cache_policy < ARRAY_LENGTH(s_cache_settings))); + + const BlockLayout layout = compute_block_layout(region->base_address, region->size); + + // MPU Region Base Address Register + // | Addr (27 bits) | Region Valid Bit | Region Num (4 bits) | + // The address is unshifted, we take the top bits of the address and assume everything below + // is zero, since the address must be power of 2 size aligned. + *base_address_reg = layout.block_base | + 0x1 << 4 | + region->region_num; + + // MPU Region Attribute and Size Register + // A lot of stuff here! Split into bytes... + // | Reserved (3 bits) | XN Bit | Reserved Bit | Permission Field (3 bits) | + // | Reserved (2 bits) | TEX (3 bits) | S | C | B | + // | Subregion Disable Byte | + // | Reserved (2 bits) | Size Field (5 bits) | Enable Bit | + *attributes_reg = ((region->executable ? 0u : 1u) << 28) | + (get_permission_value(region) << 24) | + s_cache_settings[region->cache_policy] | + layout.disabled_subregions << 8 | + (get_size_field(layout.block_size) << 1) | + region->enabled; +} + +void mpu_set_region(const MpuRegion* region) { + uint32_t base_reg, attr_reg; + + mpu_get_register_settings(region, &base_reg, &attr_reg); + + MPU->RBAR = base_reg; + MPU->RASR = attr_reg; +} + +MpuRegion mpu_get_region(int region_num) { + MpuRegion region = { .region_num = region_num }; + + MPU->RNR = region_num; + + const uint32_t attributes = MPU->RASR; + + region.enabled = attributes & 0x1; + region.executable = ((attributes >> 28) & 0x1) == 0; + + if (region.enabled) { + const uint8_t size_field = (attributes >> 1) & 0x1f; + const uint32_t block_size = 32u << (size_field - 4); + const uint8_t disabled_subregions = (attributes & 0x0000ff00) >> 8; + const uint32_t raw_base_address = MPU->RBAR; + const uintptr_t block_base = raw_base_address & ~(block_size - 1); + + // Decode the (contiguous) enabled-subregion range back into a real + // (base, size). Matches the layout produced by compute_block_layout(). + const uint32_t subregion = block_size / MPU_ARMV7M_NUM_SUBREGIONS; + unsigned start_sub = 0; + while (start_sub < MPU_ARMV7M_NUM_SUBREGIONS && (disabled_subregions & (1u << start_sub))) { + start_sub++; + } + unsigned end_sub = start_sub; + while (end_sub < MPU_ARMV7M_NUM_SUBREGIONS && !(disabled_subregions & (1u << end_sub))) { + end_sub++; + } + region.base_address = block_base + (uintptr_t)start_sub * subregion; + region.size = (end_sub - start_sub) * subregion; + + const uint8_t access_permissions = (attributes >> 24) & 0x7; + region.permissions = decode_permission_value(access_permissions); + } + + return region; +} diff --git a/src/fw/drivers/mpu/mpu_armv8m.c b/src/fw/drivers/mpu/mpu_armv8m.c new file mode 100644 index 0000000000..8470fb3011 --- /dev/null +++ b/src/fw/drivers/mpu/mpu_armv8m.c @@ -0,0 +1,149 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/mpu.h" + +#include "system/passert.h" +#include "util/size.h" + +#include + +#include + +// On SF32LB52 the SiFli vendor code (system_bf0_ap.c) programs its own MPU +// regions in SystemInit() and burns MAIR indices 0..2 (code / ram / device) +// for those. To avoid clobbering them we shift our own MpuCachePolicy values +// into MAIR indices MAIR_INDEX_BASE..MAIR_INDEX_BASE+3. +#ifdef CONFIG_SOC_SF32LB52 +#define MAIR_INDEX_BASE 4U +#else +#define MAIR_INDEX_BASE 0U +#endif + +static inline uint8_t mair_index(uint32_t cache_policy) { + return (uint8_t)(cache_policy + MAIR_INDEX_BASE); +} + +// ARMv8-M has a 2-bit AP field, which cannot express two of the +// MpuPermissions values precisely: +// - NoAccess: there is no "deny everything" encoding; we degrade to +// AP=0b10 (RO priv only), the same encoding used for PrivRO. Don't +// rely on this for thread-stack-overflow detection -- use PSPLIM. +// - PrivRW_UserRO: the AP field cannot split write access by privilege. +// We pick AP=0b01 (R/W any privilege level), which gives the user +// write access too. See the MpuPermissions doc in drivers/mpu.h. +static const uint8_t s_permission_to_ap[MpuPermissionsCount] = { + [MpuPermissions_NoAccess] = 0x2, + [MpuPermissions_PrivRW] = 0x0, + [MpuPermissions_PrivRW_UserRO] = 0x1, + [MpuPermissions_PrivRW_UserRW] = 0x1, + [MpuPermissions_PrivRO] = 0x2, + [MpuPermissions_PrivRO_UserRO] = 0x3, +}; + +static const uint32_t s_cache_settings[] = { + [MpuCachePolicy_NotCacheable] = ARM_MPU_ATTR(ARM_MPU_ATTR_NON_CACHEABLE, + ARM_MPU_ATTR_NON_CACHEABLE), + [MpuCachePolicy_WriteThrough] = ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0), + ARM_MPU_ATTR_MEMORY_(1, 0, 1, 0)), + [MpuCachePolicy_WriteBackWriteAllocate] = ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1), + ARM_MPU_ATTR_MEMORY_(1, 1, 1, 1)), + [MpuCachePolicy_WriteBackNoWriteAllocate] = ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(1, 1, 0, 1), + ARM_MPU_ATTR_MEMORY_(1, 1, 0, 1)), +}; + +static uint8_t get_permission_value(const MpuRegion* region) { + PBL_ASSERTN(region->permissions < MpuPermissionsCount); + return s_permission_to_ap[region->permissions]; +} + +static MpuPermissions decode_permission_value(uint8_t ap) { + // ARMv8-M's two-bit AP loses information: AP=0b10 could have been set + // for either NoAccess or PrivRO. Decode to PrivRO canonically. + switch (ap & 0x3) { + case 0x0: return MpuPermissions_PrivRW; + case 0x1: return MpuPermissions_PrivRW_UserRW; + case 0x2: return MpuPermissions_PrivRO; + case 0x3: return MpuPermissions_PrivRO_UserRO; + default: return MpuPermissions_NoAccess; + } +} + +void mpu_enable(void) { + ARM_MPU_SetMemAttr(mair_index(MpuCachePolicy_NotCacheable), + s_cache_settings[MpuCachePolicy_NotCacheable]); + ARM_MPU_SetMemAttr(mair_index(MpuCachePolicy_WriteThrough), + s_cache_settings[MpuCachePolicy_WriteThrough]); + ARM_MPU_SetMemAttr(mair_index(MpuCachePolicy_WriteBackWriteAllocate), + s_cache_settings[MpuCachePolicy_WriteBackWriteAllocate]); + ARM_MPU_SetMemAttr(mair_index(MpuCachePolicy_WriteBackNoWriteAllocate), + s_cache_settings[MpuCachePolicy_WriteBackNoWriteAllocate]); + + // ARM_MPU_Enable() writes MPU_CTRL directly, which would clobber bits set + // by another module (e.g. SiFli's SystemInit() turns on HFNMIENA). If the + // MPU is already enabled, just OR in PRIVDEFENA so we keep whatever flags + // are already there. + if (MPU->CTRL & MPU_CTRL_ENABLE_Msk) { + MPU->CTRL |= MPU_CTRL_PRIVDEFENA_Msk; + } else { + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk); + } +} + +void mpu_get_register_settings(const MpuRegion* region, uint32_t *base_address_reg, + uint32_t *attributes_reg) { + PBL_ASSERTN(region); + PBL_ASSERTN((region->base_address & 0x1f) == 0); + PBL_ASSERTN((region->region_num & ~0xf) == 0); + PBL_ASSERTN((region->cache_policy < ARRAY_LENGTH(s_cache_settings))); + PBL_ASSERTN((region->size & 0x1f) == 0); + + *base_address_reg = ((region->base_address & MPU_RBAR_BASE_Msk) | + ((ARM_MPU_SH_INNER << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk) | + ((get_permission_value(region) << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk) | + ((region->executable ? 0u : 1u) << MPU_RBAR_XN_Pos)); + *attributes_reg = (((region->base_address + region->size - 1U) & MPU_RLAR_LIMIT_Msk) | + ((mair_index(region->cache_policy) << MPU_RLAR_AttrIndx_Pos) & + MPU_RLAR_AttrIndx_Msk) | + ((region->enabled << MPU_RLAR_EN_Pos) & MPU_RLAR_EN_Msk)); +} + +void mpu_set_region(const MpuRegion* region) { + uint32_t base_reg, attr_reg; + + mpu_get_register_settings(region, &base_reg, &attr_reg); + + ARM_MPU_SetRegion(region->region_num, base_reg, attr_reg); +} + +MpuRegion mpu_get_region(int region_num) { + MpuRegion region; + uint32_t rbar, rlar; + uint8_t access_permissions; + + region.region_num = region_num; + + MPU->RNR = region_num; + rbar = MPU->RBAR; + rlar = MPU->RLAR; + + region.base_address = rbar & MPU_RBAR_BASE_Msk; + region.executable = ((rbar >> MPU_RBAR_XN_Pos) & 0x1) == 0; + + access_permissions = (rbar & MPU_RBAR_AP_Msk) >> MPU_RBAR_AP_Pos; + region.permissions = decode_permission_value(access_permissions); + + region.size = (rlar & MPU_RLAR_LIMIT_Msk) - region.base_address + 0x20; + region.enabled = (rlar & MPU_RLAR_EN_Msk) != 0; + // Strip the MAIR_INDEX_BASE offset so the returned cache_policy round-trips + // through MpuCachePolicy. Regions programmed by another module that use + // raw MAIR indices below MAIR_INDEX_BASE will report as MpuCachePolicy 0. + uint8_t attr_idx = (uint8_t)((rlar & MPU_RLAR_AttrIndx_Msk) >> MPU_RLAR_AttrIndx_Pos); +#if MAIR_INDEX_BASE > 0 + region.cache_policy = (attr_idx >= MAIR_INDEX_BASE) ? (attr_idx - MAIR_INDEX_BASE) : 0; +#else + region.cache_policy = attr_idx; +#endif + + return region; +} diff --git a/src/fw/drivers/nrf5/button.c b/src/fw/drivers/nrf5/button.c index e1cec30323..2909091d2c 100644 --- a/src/fw/drivers/nrf5/button.c +++ b/src/fw/drivers/nrf5/button.c @@ -7,8 +7,21 @@ #include "kernel/events.h" #include "system/passert.h" +// watch rotation +static bool s_rotated_180 = false; + +void button_set_rotated(bool rotated) { + s_rotated_180 = rotated; +} + bool button_is_pressed(ButtonId id) { - const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id]; + if (s_rotated_180 && id == BUTTON_ID_UP) { + id = BUTTON_ID_DOWN; + } else if (s_rotated_180 && id == BUTTON_ID_DOWN) { + id = BUTTON_ID_UP; + } + + const ButtonConfig *button_config = &BOARD_CONFIG_BUTTON.buttons[id]; uint32_t bit = nrf_gpio_pin_read(button_config->gpiote.gpio_pin); return (BOARD_CONFIG_BUTTON.active_high) ? bit : !bit; @@ -31,10 +44,6 @@ void button_init(void) { } } -bool button_selftest(void) { - return button_get_state_bits() == 0; -} - void command_button_read(const char* button_id_str) { int button = atoi(button_id_str); diff --git a/src/fw/drivers/nrf5/debounced_button.c b/src/fw/drivers/nrf5/debounced_button.c index cf3039d10d..93686fcee8 100644 --- a/src/fw/drivers/nrf5/debounced_button.c +++ b/src/fw/drivers/nrf5/debounced_button.c @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "drivers/debounced_button.h" #include "board/board.h" @@ -13,8 +16,7 @@ #include "util/bitset.h" #include "kernel/util/sleep.h" -#define NRF5_COMPATIBLE -#include +#include #include "projdefs.h" @@ -39,7 +41,7 @@ static void prv_timer_handler(nrf_timer_event_t evt, void *ctx); static void initialize_button_timer(void) { nrfx_timer_config_t config = { - .frequency = 31250, + .frequency = TIMER_FREQUENCY_HZ, .mode = NRF_TIMER_MODE_TIMER, .bit_width = NRF_TIMER_BIT_WIDTH_32, .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, @@ -96,12 +98,6 @@ static void clear_stuck_button(ButtonId button_id) { void debounced_button_init(void) { button_init(); -#if defined(BOARD_SNOWY_BB2) || defined(BOARD_SPALDING_BB2) - // Snowy BB2s have a capacitor that results in a really slow rise time (~0.4ms). Sleep for - // at least 1 ms to prevent fake button events - psleep(2); -#endif - for (int i = 0; i < NUM_BUTTONS; ++i) { const ExtiConfig config = BOARD_CONFIG_BUTTON.buttons[i].gpiote; exti_configure_pin(config, ExtiTrigger_RisingFalling, prv_button_interrupt_handler); @@ -170,7 +166,7 @@ static void prv_timer_handler(nrf_timer_event_t evt, void *ctx) { } } -#if !defined(MANUFACTURING_FW) +#if !defined(CONFIG_MFG) // Now that s_debounced_button_state is updated, check to see if the user is holding down the reset // combination. static uint32_t s_hard_reset_timer = 0; diff --git a/src/fw/drivers/nrf5/exti.c b/src/fw/drivers/nrf5/exti.c deleted file mode 100644 index d3ecd6e279..0000000000 --- a/src/fw/drivers/nrf5/exti.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "drivers/exti.h" - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "kernel/events.h" -#include "mcu/interrupts.h" -#include "system/passert.h" - -#define NRF5_COMPATIBLE -#include - -#include - -// NRF5 emulates EXTI using GPIOTE - -static void prv_exti_handler(nrfx_gpiote_pin_t pin, nrfx_gpiote_trigger_t trigger, void *p_context) { - ExtiHandlerCallback cb = (ExtiHandlerCallback) p_context; - - bool should_context_switch = false; - cb(&should_context_switch); - - portEND_SWITCHING_ISR(should_context_switch); -} - -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb) { - nrfx_err_t err; - if (!nrfx_gpiote_init_check(&cfg.peripheral)) { - err = nrfx_gpiote_init(&cfg.peripheral, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY); - PBL_ASSERTN(err == NRFX_SUCCESS); - } - uint8_t channel = cfg.channel; - nrfx_gpiote_trigger_config_t tcfg = { - .trigger = trigger == ExtiTrigger_Rising ? NRFX_GPIOTE_TRIGGER_LOTOHI : - trigger == ExtiTrigger_Falling ? NRFX_GPIOTE_TRIGGER_HITOLO : - trigger == ExtiTrigger_RisingFalling ? NRFX_GPIOTE_TRIGGER_TOGGLE : - NRFX_GPIOTE_TRIGGER_NONE, - .p_in_channel = &channel - }; - nrfx_gpiote_handler_config_t hcfg = { - .handler = prv_exti_handler, - .p_context = cb - }; - nrfx_gpiote_input_pin_config_t pcfg = { - .p_pull_config = NULL, - .p_trigger_config = &tcfg, - .p_handler_config = &hcfg - }; - - err = nrfx_gpiote_input_configure(&cfg.peripheral, cfg.gpio_pin, &pcfg); - PBL_ASSERTN(err == NRFX_SUCCESS); - - nrfx_gpiote_trigger_disable(&cfg.peripheral, cfg.gpio_pin); -} - -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger) { - WTF; -} - -void exti_enable_other(ExtiLineOther exti_line) { - WTF; -} - -void exti_disable_other(ExtiLineOther exti_line) { - WTF; -} - -void exti_set_pending(ExtiConfig cfg) { - WTF; -} - -void exti_clear_pending_other(ExtiLineOther exti_line) { - WTF; -} - -void exti_enable(ExtiConfig cfg) { - nrfx_gpiote_trigger_enable(&cfg.peripheral, cfg.gpio_pin, true /* int_enable */); -} - -void exti_disable(ExtiConfig cfg) { - nrfx_gpiote_trigger_disable(&cfg.peripheral, cfg.gpio_pin); -} - diff --git a/src/fw/drivers/nrf5/gpio.c b/src/fw/drivers/nrf5/gpio.c deleted file mode 100644 index de0fc10680..0000000000 --- a/src/fw/drivers/nrf5/gpio.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "drivers/gpio.h" -#include "system/passert.h" - -#include - -void gpio_input_init(const InputConfig *pin_config) { - nrf_gpio_pin_dir_set(pin_config->gpio_pin, NRF_GPIO_PIN_DIR_INPUT); -} - -void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed) { - if (otype == GPIO_OType_OD) - WTF; - - /* XXX: speed */ - nrf_gpio_cfg(pin_config->gpio_pin, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE); -} - -void gpio_output_set(const OutputConfig *pin_config, bool asserted) { - if (!pin_config->active_high) { - asserted = !asserted; - } - nrf_gpio_pin_write(pin_config->gpio_pin, !!asserted); -} diff --git a/src/fw/drivers/nrf5/hfxo.c b/src/fw/drivers/nrf5/hfxo.c index 09ab6cab63..1ed731ca16 100644 --- a/src/fw/drivers/nrf5/hfxo.c +++ b/src/fw/drivers/nrf5/hfxo.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/src/fw/drivers/nrf5/i2c_hal.c b/src/fw/drivers/nrf5/i2c_hal.c deleted file mode 100644 index 995a4e49cf..0000000000 --- a/src/fw/drivers/nrf5/i2c_hal.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "drivers/i2c_hal.h" -#include "drivers/i2c_definitions.h" -#include "drivers/nrf5/i2c_hal_definitions.h" -#include "system/passert.h" - -#include "drivers/periph_config.h" -#include "FreeRTOS.h" - -#define NRF5_COMPATIBLE -#include - -#define I2C_IRQ_PRIORITY (0xc) -#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000) -#define I2C_READ_WRITE_BIT (0x01) - -static void prv_twim_evt_handler(nrfx_twim_evt_t const *evt, void *ctx) { - I2CBus *bus = (I2CBus *) ctx; - bool success = evt->type == NRFX_TWIM_EVT_DONE; - I2CTransferEvent event = success ? I2CTransferEvent_TransferComplete : I2CTransferEvent_Error; - bool should_csw = i2c_handle_transfer_event(bus, event); - portEND_SWITCHING_ISR(should_csw); -} - -static void prv_twim_init(I2CBus *bus) { - nrfx_twim_config_t config = NRFX_TWIM_DEFAULT_CONFIG( - bus->scl_gpio.gpio_pin, bus->sda_gpio.gpio_pin); - config.frequency = bus->hal->frequency; - config.hold_bus_uninit = true; - - nrfx_err_t err = nrfx_twim_init(&bus->hal->twim, &config, prv_twim_evt_handler, (void *)bus); - PBL_ASSERTN(err == NRFX_SUCCESS); -} - -void i2c_hal_init(I2CBus *bus) { - prv_twim_init(bus); - nrfx_twim_uninit(&bus->hal->twim); - bus->state->should_be_init = 0; -} - -void i2c_hal_enable(I2CBus *bus) { - prv_twim_init(bus); - nrfx_twim_enable(&bus->hal->twim); - bus->state->should_be_init = 1; -} - -void i2c_hal_disable(I2CBus *bus) { - nrfx_twim_disable(&bus->hal->twim); - nrfx_twim_uninit(&bus->hal->twim); - bus->state->should_be_init = 0; -} - -bool i2c_hal_is_busy(I2CBus *bus) { - return nrfx_twim_is_busy(&bus->hal->twim); -} - -void i2c_hal_abort_transfer(I2CBus *bus) { - nrfx_twim_disable(&bus->hal->twim); - nrfx_twim_enable(&bus->hal->twim); -} - -void i2c_hal_init_transfer(I2CBus *bus) { -} - -void i2c_hal_start_transfer(I2CBus *bus) { - nrfx_twim_xfer_desc_t desc; - - desc.address = bus->state->transfer.device_address >> 1; - if (bus->state->transfer.type == I2CTransferType_SendRegisterAddress) { - if (bus->state->transfer.direction == I2CTransferDirection_Read) { - desc.type = NRFX_TWIM_XFER_TXRX; - } else { - desc.type = NRFX_TWIM_XFER_TXTX; - } - desc.primary_length = 1; - desc.p_primary_buf = &bus->state->transfer.register_address; - - desc.secondary_length = bus->state->transfer.size; - desc.p_secondary_buf = bus->state->transfer.data; - } else { - if (bus->state->transfer.direction == I2CTransferDirection_Read) { - desc.type = NRFX_TWIM_XFER_RX; - } else { - desc.type = NRFX_TWIM_XFER_TX; - } - desc.primary_length = bus->state->transfer.size; - desc.p_primary_buf = bus->state->transfer.data; - desc.secondary_length = 0; - } - - nrfx_err_t rv = nrfx_twim_xfer(&bus->hal->twim, &desc, 0); - PBL_ASSERTN(rv == NRFX_SUCCESS); -} - -void i2c_hal_pins_set_gpio(I2CBus *bus) { - nrfx_twim_uninit(&bus->hal->twim); -} - -void i2c_hal_pins_set_i2c(I2CBus *bus) { - if (bus->state->should_be_init) { - /* only put it back if we need to, otherwise, leave it as is */ - i2c_hal_enable(bus); - } -} diff --git a/src/fw/drivers/nrf5/i2c_hal_definitions.h b/src/fw/drivers/nrf5/i2c_hal_definitions.h deleted file mode 100644 index cf42e7640e..0000000000 --- a/src/fw/drivers/nrf5/i2c_hal_definitions.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" -#include -#pragma GCC diagnostic pop - -typedef struct I2CBusHal { - nrfx_twim_t twim; - nrf_twim_frequency_t frequency; ///< Bus clock speed - int should_be_init; -} I2CBusHal; diff --git a/src/fw/drivers/nrf5/pwm.c b/src/fw/drivers/nrf5/pwm.c deleted file mode 100644 index b134642eb6..0000000000 --- a/src/fw/drivers/nrf5/pwm.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pwm.h" -#include "drivers/timer.h" -#include "system/passert.h" - -#define NRF5_COMPATIBLE -#include - -void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { - nrfx_pwm_config_t config = NRFX_PWM_DEFAULT_CONFIG( - pwm->output.gpio_pin, - NRF_PWM_PIN_NOT_CONNECTED, - NRF_PWM_PIN_NOT_CONNECTED, - NRF_PWM_PIN_NOT_CONNECTED); - // this is hokey and imprecise, but oh well - if (frequency >= 16000000) config.base_clock = NRF_PWM_CLK_16MHz; - else if (frequency >= 8000000) config.base_clock = NRF_PWM_CLK_8MHz; - else if (frequency >= 4000000) config.base_clock = NRF_PWM_CLK_4MHz; - else if (frequency >= 2000000) config.base_clock = NRF_PWM_CLK_2MHz; - else if (frequency >= 1000000) config.base_clock = NRF_PWM_CLK_1MHz; - else if (frequency >= 500000) config.base_clock = NRF_PWM_CLK_500kHz; - else if (frequency >= 250000) config.base_clock = NRF_PWM_CLK_250kHz; - else if (frequency >= 125000) config.base_clock = NRF_PWM_CLK_125kHz; - else WTF; - config.count_mode = NRF_PWM_MODE_UP; - config.top_value = resolution; - config.load_mode = NRF_PWM_LOAD_COMMON; - config.step_mode = NRF_PWM_STEP_TRIGGERED; - - nrfx_err_t rv = nrfx_pwm_init(&pwm->peripheral, &config, NULL, NULL); - PBL_ASSERTN(rv == NRFX_SUCCESS); - - pwm->state->enabled = 0; - pwm->state->value = 0; - pwm->state->seq = (nrf_pwm_sequence_t) { .values = { .p_common = &pwm->state->value }, .length = 1, .repeats = 0, .end_delay = 0 }; - pwm->state->resolution = resolution; -} - -void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { - PBL_ASSERTN(duty_cycle < 0xFFFF); - pwm->state->value = pwm->state->resolution - duty_cycle; - if (pwm->state->enabled) - nrfx_pwm_simple_playback(&pwm->peripheral, &pwm->state->seq, 1, 0); - if (duty_cycle == 0) - nrfx_pwm_stop(&pwm->peripheral, 0); -} - -void pwm_enable(const PwmConfig *pwm, bool enable) { - if (enable) { - nrfx_pwm_simple_playback(&pwm->peripheral, &pwm->state->seq, 1, 0); - } else { - // no need to set the output to low; I think the nRF does it for us? - nrfx_pwm_stop(&pwm->peripheral, 0); - } - pwm->state->enabled = !!enable; -} diff --git a/src/fw/drivers/nrf5/qspi.c b/src/fw/drivers/nrf5/qspi.c index 054896d9e0..884dae2223 100644 --- a/src/fw/drivers/nrf5/qspi.c +++ b/src/fw/drivers/nrf5/qspi.c @@ -1,136 +1,218 @@ -#include "drivers/qspi.h" +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include #include "board/board.h" -#include "drivers/dma.h" -#include "drivers/flash/flash_impl.h" +#include "drivers/qspi.h" #include "drivers/flash/qspi_flash.h" -#include "drivers/flash/qspi_flash_definitions.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" +#include "drivers/flash/flash_impl.h" #include "drivers/qspi_definitions.h" #include "flash_region/flash_region.h" #include "kernel/util/delay.h" #include "kernel/util/sleep.h" -#include "kernel/util/stop.h" -#include "mcu/cache.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" -#define NRF5_COMPATIBLE -#include +#include #include -#include #include "FreeRTOS.h" #include "semphr.h" -/* nRF5's QSPI controller is different enough from STM32's that we - * reimplement qspi_flash.c, not stm32/qspi.c. */ +// NOTE: This driver does not cover anomaly 244, which may cause data corruption +// if HF clock source is switching between HFXO and HFINT (e.g. by BLE). This +// issue has not been observed at operating speeds <= 8MHz, therefore, no +// workaround is implemented here. A warning log will be emitted if driver is +// initialized at higher frequencies. -#define FLASH_RESET_WORD_VALUE (0xffffffff) +// Asynchronous read/write can actually add overhead for small sizes due to context +// switching and semaphore handling, so we define a minimum size for async ops. +#define MIN_RW_ASYNC_SIZE 256U -static uint8_t s_qspi_ram_buffer[32]; +// Obtain security register index (zero-indexed) from address +// Bits 15-12 of the address correspond to the security register index (one-indexed) +#define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) -status_t flash_impl_set_nvram_erase_status(bool is_subsector, FlashAddress addr) { - return S_SUCCESS; +// Minimum address to enable 4-byte addressing +#define ADDR_4BYTE_THRESHOLD 0x1000000UL + +static uint8_t __attribute__((aligned(4))) s_bounce_buf[32]; + +void QSPI_IRQHandler(void) { + BaseType_t woken = pdFALSE; + + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + xSemaphoreGiveFromISR(QSPI_FLASH->qspi->state->sem, &woken); + portYIELD_FROM_ISR(woken); } -status_t flash_impl_clear_nvram_erase_status(void) { return S_SUCCESS; } +// ----------------------------------------------------------------------------- +// Internal helpers -status_t flash_impl_get_nvram_erase_status(bool *is_subsector, FlashAddress *addr) { - // XXX - return S_FALSE; +// Workaround for nRF52 Anomaly 215: +// +// Symptoms: +// CPU halts. +// +// Conditions: +// Init and start QSPI, use XIP, then write to or read any QSPI register with an +// offset above 0x600. +// +// Consequences: +// CPU halts. +// +// Workaround: +// Trigger QSPI TASKS_ACTIVATE after XIP is used and wait for QSPI EVENTS_READY +// before accessing any QSPI register with an offset above 0x600. +static void prv_workaround_215_apply(void) { + nrf_qspi_pins_t pins; + nrf_qspi_pins_t disconnected_pins = { + .sck_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .csn_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io0_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io1_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io2_pin = NRF_QSPI_PIN_NOT_CONNECTED, + .io3_pin = NRF_QSPI_PIN_NOT_CONNECTED, + }; + + // Disconnect pins to not wait for response from external memory + nrf_qspi_pins_get(NRF_QSPI, &pins); + nrf_qspi_pins_set(NRF_QSPI, &disconnected_pins); + + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + + // Restore previous pins + nrf_qspi_pins_set(NRF_QSPI, &pins); } -static void prv_read_register(QSPIPort *dev, uint8_t instruction, uint8_t *data, uint32_t length) { - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(instruction, length + 1); - instr.io2_level = true; - instr.io3_level = true; - nrfx_err_t err = nrfx_qspi_cinstr_xfer(&instr, NULL, data); - PBL_ASSERTN(err == NRFX_SUCCESS); +static void prv_cinstr_write_read(QSPIFlash *dev, uint8_t instr, const void *data, void *buf, + size_t len) { + nrf_qspi_cinstr_conf_t conf = { + .opcode = instr, + .length = len + 1U, + .io2_level = true, + .io3_level = true, + }; + + PBL_ASSERTN(len <= 8U); + + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + // Accessing registers with offset > 0x600 below, requires workaround for Anomaly 215 + prv_workaround_215_apply(); + if (data != NULL) { + nrf_qspi_cinstrdata_set(NRF_QSPI, conf.length, data); + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_cinstr_transfer_start(NRF_QSPI, &conf); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + if (buf != NULL) { + nrf_qspi_cinstrdata_get(NRF_QSPI, conf.length, buf); + } } -static void prv_write_register(QSPIPort *dev, uint8_t instruction, const uint8_t *data, - uint32_t length) { - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(instruction, length + 1); - instr.io2_level = true; - instr.io3_level = true; - instr.wren = true; - nrfx_err_t err = nrfx_qspi_cinstr_xfer(&instr, data, NULL); - PBL_ASSERTN(err == NRFX_SUCCESS); +static inline void prv_cinstr(QSPIFlash *dev, uint8_t instr) { + prv_cinstr_write_read(dev, instr, NULL, NULL, 0U); } -static void prv_write_cmd_no_addr(QSPIPort *dev, uint8_t cmd) { - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(cmd, 1); - instr.io2_level = true; - instr.io3_level = true; - nrfx_err_t err = nrfx_qspi_cinstr_xfer(&instr, NULL, NULL); - PBL_ASSERTN(err == NRFX_SUCCESS); +static inline void prv_cinstr_read(QSPIFlash *dev, uint8_t instr, void *buf, size_t len) { + prv_cinstr_write_read(dev, instr, NULL, buf, len); } -static bool prv_poll_bit(QSPIPort *dev, uint8_t instruction, uint8_t bit_mask, bool should_be_set, - uint32_t timeout_us) { - uint32_t loops = 0; - uint8_t val; - while (1) { - prv_read_register(dev, instruction, &val, 1); - if ((!!(val & bit_mask)) == should_be_set) break; - if ((timeout_us != QSPI_NO_TIMEOUT) && (++loops > timeout_us)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timeout waiting for a bit!?!?"); - return false; - } - delay_us(1); +static inline void prv_cinstr_write(QSPIFlash *dev, uint8_t instr, const void *data, size_t len) { + prv_cinstr_write_read(dev, instr, data, NULL, len); +} + +static void prv_read(QSPIFlash *dev, void *buf, size_t len, uint32_t addr) { + if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + } else { + nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); } - return true; -} + nrf_qspi_read_buffer_set(NRF_QSPI, buf, len, addr); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_READSTART); -static void prv_write_enable(QSPIFlash *dev) { - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.write_enable); - // wait for writing to be enabled - prv_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.write_enable, true /* set */, QSPI_NO_TIMEOUT); + if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + } else { + xSemaphoreTake(dev->qspi->state->sem, portMAX_DELAY); + } } -static void prv_wait_for_completion(QSPIFlash *dev) { - if (!dev->state->coredump_mode) { - xSemaphoreTake(dev->qspi->state->dma_semaphore, portMAX_DELAY); +static void prv_write(QSPIFlash *dev, const void *buf, size_t len, uint32_t addr) { + if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + } else { + nrf_qspi_int_enable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); } -} -static bool prv_check_whoami(QSPIFlash *dev) { - // The WHOAMI is 3 bytes - const uint32_t whoami_length = 3; - uint32_t read_whoami = 0; - prv_read_register(dev->qspi, dev->state->part->instructions.qspi_id, (uint8_t *)&read_whoami, - whoami_length); + nrf_qspi_write_buffer_set(NRF_QSPI, buf, len, addr); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_WRITESTART); - if (read_whoami == dev->state->part->qspi_id_value) { - PBL_LOG(LOG_LEVEL_INFO, "Flash is %s", dev->state->part->name); - return true; + if ((len <= MIN_RW_ASYNC_SIZE) || dev->state->coredump_mode) { + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Flash isn't expected %s (whoami: 0x%" PRIx32 ")", - dev->state->part->name, read_whoami); - return false; + xSemaphoreTake(dev->qspi->state->sem, portMAX_DELAY); } } -bool qspi_flash_check_whoami(QSPIFlash *dev) { return prv_check_whoami(dev); } +static inline void prv_write_enable(QSPIFlash *dev) { + QSPIFlashPart *part = dev->state->part; + + prv_cinstr(dev, part->instructions.write_enable); +} -bool qspi_flash_is_in_coredump_mode(QSPIFlash *dev) { return dev->state->coredump_mode; } +static inline void prv_read_sr1(QSPIFlash *dev, uint8_t *sr1) { + QSPIFlashPart *part = dev->state->part; -static void _flash_handler(nrfx_qspi_evt_t event, void *ctx) { - QSPIFlash *dev = (QSPIFlash *)ctx; - BaseType_t woken = pdFALSE; + prv_cinstr_read(dev, part->instructions.rdsr1, sr1, 1U); +} - PBL_ASSERTN(event == NRFX_QSPI_EVENT_DONE); +static inline void prv_read_sr2(QSPIFlash *dev, uint8_t *sr2) { + QSPIFlashPart *part = dev->state->part; - xSemaphoreGiveFromISR(dev->qspi->state->dma_semaphore, &woken); - portYIELD_FROM_ISR(woken); + prv_cinstr_read(dev, part->instructions.rdsr2, sr2, 1U); +} + +static inline void prv_write_sr(QSPIFlash *dev, const uint8_t *sr, size_t len) { + QSPIFlashPart *part = dev->state->part; + + prv_cinstr_write(dev, part->instructions.wrsr, sr, len); +} + +static inline void prv_write_sr2(QSPIFlash *dev, uint8_t sr2) { + QSPIFlashPart *part = dev->state->part; + + prv_cinstr_write(dev, part->instructions.wrsr2, &sr2, 1U); +} + +static inline bool prv_wip_check(QSPIFlash *dev) { + QSPIFlashPart *part = dev->state->part; + uint8_t sr1; + + prv_read_sr1(dev, &sr1); + + return (sr1 & part->status_bit_masks.busy) != 0U; } static void prv_configure_qe(QSPIFlash *dev) { + QSPIFlashPart *part = dev->state->part; uint8_t sr[2]; // Check first if read/write mode requires QE to be set @@ -141,179 +223,249 @@ static void prv_configure_qe(QSPIFlash *dev) { } // Check if QE is needed - if (dev->state->part->qer_type == JESD216_DW15_QER_NONE) { + if (part->qer_type == JESD216_DW15_QER_NONE) { return; } // Enable QE bit - switch (dev->state->part->qer_type) { + switch (part->qer_type) { case JESD216_DW15_QER_S1B6: - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr1, sr, 1); - sr[0] |= (1 << 6); - prv_write_register(dev->qspi, dev->state->part->instructions.wrsr, sr, 1); + prv_read_sr1(dev, &sr[0]); + sr[0] |= (1U << 6U); + prv_write_sr(dev, sr, 1U); break; case JESD216_DW15_QER_S2B1v1: case JESD216_DW15_QER_S2B1v4: case JESD216_DW15_QER_S2B1v5: // Writing SR2 requires writing SR1 as well - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr1, &sr[0], 1); - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr2, &sr[1], 1); - sr[1] |= (1 << 1); - prv_write_register(dev->qspi, dev->state->part->instructions.wrsr, sr, 2); + prv_read_sr1(dev, &sr[0]); + prv_read_sr2(dev, &sr[1]); + sr[1] |= (1U << 1U); + prv_write_sr(dev, sr, 2U); break; case JESD216_DW15_QER_S2B1v6: // We can write SR2 without writing SR1 - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr2, sr, 1); - sr[0] |= (1 << 1); - prv_write_register(dev->qspi, dev->state->part->instructions.wrsr2, sr, 1); + prv_read_sr2(dev, &sr[1]); + sr[1] |= (1U << 1U); + prv_write_sr2(dev, sr[1]); break; default: - PBL_ASSERT(false, "Unsupported QER type %d", dev->state->part->qer_type); + PBL_ASSERTN(false); + } +} + +status_t prv_qspi_security_register_check(QSPIFlash *dev, uint32_t addr) { + QSPIFlashPart *part = dev->state->part; + bool addr_valid = false; + + for (uint8_t i = 0U; i < part->sec_registers.num_sec_regs; ++i) { + if (addr >= part->sec_registers.sec_regs[i] && + addr < part->sec_registers.sec_regs[i] + part->sec_registers.sec_reg_size) { + addr_valid = true; + break; + } + } + + if (!addr_valid) { + return E_INVALID_ARGUMENT; } + + return S_SUCCESS; } +// ----------------------------------------------------------------------------- +// QSPI interface + void qspi_flash_init(QSPIFlash *dev, QSPIFlashPart *part, bool coredump_mode) { - static int was_init = 0; + nrf_qspi_pins_t conf_pins; + nrf_qspi_prot_conf_t conf_prot; + nrf_qspi_phy_conf_t conf_phy; dev->state->part = part; dev->state->coredump_mode = coredump_mode; - // Init the DMA semaphore, used for I/O ops - if (!coredump_mode) dev->qspi->state->dma_semaphore = xSemaphoreCreateBinary(); + if (dev->qspi->state->initialized) { + return; + } + + // QSPI clock is 32MHz, we have dividers from 1 to 16 + PBL_ASSERTN(dev->qspi->clk_freq_hz <= 32000000UL && dev->qspi->clk_freq_hz >= 200000UL); + if (dev->qspi->clk_freq_hz > 8000000UL) { + PBL_LOG_WRN( + "QSPI initialized at %lu Hz, which may cause data corruption if HF clock source switches " + "(anomaly 244)", + dev->qspi->clk_freq_hz); + } + + // QSPI pins + conf_pins.sck_pin = dev->qspi->clk_gpio; + conf_pins.csn_pin = dev->qspi->cs_gpio; + conf_pins.io0_pin = dev->qspi->data_gpio[0]; + conf_pins.io1_pin = dev->qspi->data_gpio[1]; + conf_pins.io2_pin = dev->qspi->data_gpio[2]; + conf_pins.io3_pin = dev->qspi->data_gpio[3]; - nrfx_qspi_config_t config = NRFX_QSPI_DEFAULT_CONFIG( - dev->qspi->clk_gpio, dev->qspi->cs_gpio, dev->qspi->data_gpio[0], dev->qspi->data_gpio[1], - dev->qspi->data_gpio[2], dev->qspi->data_gpio[3]); - config.phy_if.sck_freq = NRF_QSPI_FREQ_DIV4; + nrf_qspi_pins_set(NRF_QSPI, &conf_pins); + // QSPI protocol configuration switch (dev->read_mode) { case QSPI_FLASH_READ_READ2O: - config.prot_if.readoc = NRF_QSPI_READOC_READ2O; + conf_prot.readoc = NRF_QSPI_READOC_READ2O; break; case QSPI_FLASH_READ_READ2IO: - config.prot_if.readoc = NRF_QSPI_READOC_READ2IO; + conf_prot.readoc = NRF_QSPI_READOC_READ2IO; break; case QSPI_FLASH_READ_READ4O: - config.prot_if.readoc = NRF_QSPI_READOC_READ4O; + conf_prot.readoc = NRF_QSPI_READOC_READ4O; break; case QSPI_FLASH_READ_READ4IO: - config.prot_if.readoc = NRF_QSPI_READOC_READ4IO; + conf_prot.readoc = NRF_QSPI_READOC_READ4IO; break; default: - config.prot_if.readoc = NRF_QSPI_READOC_FASTREAD; + conf_prot.readoc = NRF_QSPI_READOC_FASTREAD; break; } switch (dev->write_mode) { case QSPI_FLASH_WRITE_PP2O: - config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP2O; + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP2O; break; case QSPI_FLASH_WRITE_PP4O: - config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP4O; + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4O; break; case QSPI_FLASH_WRITE_PP4IO: - config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP4IO; + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP4IO; break; default: - config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP; + conf_prot.writeoc = NRF_QSPI_WRITEOC_PP; break; } - if (dev->state->part->size > 0x1000000) { - config.prot_if.addrmode = NRF_QSPI_ADDRMODE_32BIT; + if (part->size > ADDR_4BYTE_THRESHOLD) { + conf_prot.addrmode = NRF_QSPI_ADDRMODE_32BIT; } else { - config.prot_if.addrmode = NRF_QSPI_ADDRMODE_24BIT; + conf_prot.addrmode = NRF_QSPI_ADDRMODE_24BIT; } - nrfx_err_t err; - if (was_init) { - nrfx_qspi_uninit(); - } - err = nrfx_qspi_init(&config, coredump_mode ? NULL : _flash_handler, (void *)dev); - PBL_ASSERTN(err == NRFX_SUCCESS); - was_init = 1; + conf_prot.dpmconfig = false; - if (dev->reset_gpio.gpio) { - WTF; - } + nrf_qspi_ifconfig0_set(NRF_QSPI, &conf_prot); - // Reset the flash to stop any program's or erase in progress from before reboot - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.reset_enable); - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.reset); + // QSPI PHY configuration + conf_phy.sck_delay = 5U; + conf_phy.dpmen = false; + conf_phy.spi_mode = NRF_QSPI_MODE_0; + conf_phy.sck_freq = (32000000UL / dev->qspi->clk_freq_hz) - 1U; - if (coredump_mode) { - delay_us(dev->state->part->reset_latency_ms * 1000); - } else { - psleep(dev->state->part->reset_latency_ms); - } + nrf_qspi_ifconfig1_set(NRF_QSPI, &conf_phy); - if (!coredump_mode) { - prv_check_whoami(dev); + // Enable QSPI peripheral + nrf_qspi_enable(NRF_QSPI); + + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - if (config.prot_if.addrmode == NRF_QSPI_ADDRMODE_32BIT) { - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.en4b); + // Reset the flash to stop any program's or erase in progress from before reboot + prv_cinstr(dev, part->instructions.reset_enable); + prv_cinstr(dev, part->instructions.reset); + + psleep(part->reset_latency_ms); + + // Enable 4-byte addressing if needed + if (conf_prot.addrmode == NRF_QSPI_ADDRMODE_32BIT) { + prv_cinstr(dev, part->instructions.en4b); } + // Configure QE if needed prv_configure_qe(dev); + + NVIC_SetPriority(QSPI_IRQn, 5); + NVIC_EnableIRQ(QSPI_IRQn); + + dev->qspi->state->sem = xSemaphoreCreateBinary(); + dev->qspi->state->initialized = true; } -status_t qspi_flash_is_erase_complete(QSPIFlash *dev) { - uint8_t status_reg; - uint8_t flag_status_reg; - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr1, &status_reg, 1); - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr2, &flag_status_reg, 1); +bool qspi_flash_check_whoami(QSPIFlash *dev) { + QSPIFlashPart *part = dev->state->part; + uint32_t val; - if (status_reg & dev->state->part->status_bit_masks.busy) { - return E_BUSY; - } else if (flag_status_reg & dev->state->part->flag_status_bit_masks.erase_suspend) { - return E_AGAIN; - } else { - return S_SUCCESS; - } + prv_cinstr_read(dev, part->instructions.qspi_id, &val, 3U); + + return val == part->qspi_id_value; +} + +bool qspi_flash_is_in_coredump_mode(QSPIFlash *dev) { + return dev->state->coredump_mode; } status_t qspi_flash_erase_begin(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - prv_write_enable(dev); + nrf_qspi_erase_len_t len; - nrfx_err_t err = - nrfx_qspi_erase(is_subsector ? NRF_QSPI_ERASE_LEN_4KB : NRF_QSPI_ERASE_LEN_64KB, addr); - PBL_ASSERTN(err == NRFX_SUCCESS); + // address must be word aligned + if ((addr & 0x3U) != 0U) { + return E_INVALID_ARGUMENT; + } - prv_wait_for_completion(dev); + if (is_subsector) { + len = NRF_QSPI_ERASE_LEN_4KB; + } else { + len = NRF_QSPI_ERASE_LEN_64KB; + } + + prv_write_enable(dev); - // wait for busy to be set indicating the erase has started - const uint32_t busy_timeout_us = 500; - const bool result = - prv_poll_bit(dev->qspi, dev->state->part->instructions.rdsr1, - dev->state->part->status_bit_masks.busy, true /* set */, busy_timeout_us); + nrf_qspi_erase_ptr_set(NRF_QSPI, addr, len); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ERASESTART); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); - return result ? S_SUCCESS : E_ERROR; + return S_SUCCESS; } status_t qspi_flash_erase_suspend(QSPIFlash *dev, uint32_t addr) { - uint8_t status_reg; - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr1, &status_reg, 1); - if (!(status_reg & dev->state->part->status_bit_masks.busy)) { - // no erase in progress + QSPIFlashPart *part = dev->state->part; + + if (!prv_wip_check(dev)) { return S_NO_ACTION_REQUIRED; } - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.erase_suspend); + prv_cinstr(dev, part->instructions.erase_suspend); - if (dev->state->part->suspend_to_read_latency_us) { - delay_us(dev->state->part->suspend_to_read_latency_us); + if (part->suspend_to_read_latency_us) { + delay_us(part->suspend_to_read_latency_us); } return S_SUCCESS; } void qspi_flash_erase_resume(QSPIFlash *dev, uint32_t addr) { - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.erase_resume); - // wait for the erase_suspend bit to be cleared - prv_poll_bit(dev->qspi, dev->state->part->instructions.rdsr2, - dev->state->part->flag_status_bit_masks.erase_suspend, false /* !set */, - QSPI_NO_TIMEOUT); + QSPIFlashPart *part = dev->state->part; + + prv_cinstr(dev, part->instructions.erase_resume); +} + +status_t qspi_flash_is_erase_complete(QSPIFlash *dev) { + QSPIFlashPart *part = dev->state->part; + uint8_t sr2; + + // erase ongoing + if (prv_wip_check(dev)) { + return E_BUSY; + } + + // erase suspended + prv_read_sr2(dev, &sr2); + if ((sr2 & part->flag_status_bit_masks.erase_suspend) != 0U) { + return E_AGAIN; + } + + return S_SUCCESS; } void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint32_t length) { @@ -321,7 +473,6 @@ void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint3 uint8_t buf_pre; uint8_t buf_suf; uint32_t buf_mid; - nrfx_err_t err; buf_pre = (4U - (uint8_t)((uint32_t)buffer % 4U)) % 4U; if (buf_pre > length) { @@ -332,28 +483,23 @@ void qspi_flash_read_blocking(QSPIFlash *dev, uint32_t addr, void *buffer, uint3 buf_mid = length - buf_pre - buf_suf; if (buf_pre != 0U) { - err = nrfx_qspi_read(b_buf, 4U, addr); - prv_wait_for_completion(dev); - PBL_ASSERTN(err == NRFX_SUCCESS); + prv_read(dev, b_buf, 4U, addr); memcpy(buffer, b_buf, buf_pre); + addr += buf_pre; buffer = ((uint8_t *)buffer) + buf_pre; } if (buf_mid != 0U) { - err = nrfx_qspi_read(buffer, buf_mid, addr); - prv_wait_for_completion(dev); - PBL_ASSERTN(err == NRFX_SUCCESS); + prv_read(dev, buffer, buf_mid, addr); addr += buf_mid; buffer = ((uint8_t *)buffer) + buf_mid; } if (buf_suf != 0U) { - err = nrfx_qspi_read(b_buf, 4U, addr); - prv_wait_for_completion(dev); - PBL_ASSERTN(err == NRFX_SUCCESS); + prv_read(dev, b_buf, 4U, addr); memcpy(buffer, b_buf, buf_suf); } @@ -365,18 +511,19 @@ int qspi_flash_write_page_begin(QSPIFlash *dev, const void *buffer, uint32_t add uint8_t buf_pre; uint8_t buf_suf; uint32_t buf_mid; - nrfx_err_t err; // we can write from start address up to the end of the page length = MIN(length, PAGE_SIZE_BYTES - (addr % PAGE_SIZE_BYTES)); // bounce data to RAM if not in RAM if (!nrfx_is_in_ram(buffer)) { - length = MIN(length, sizeof(s_qspi_ram_buffer)); - memcpy(s_qspi_ram_buffer, buffer, length); - buffer = s_qspi_ram_buffer; + length = MIN(length, sizeof(s_bounce_buf)); + memcpy(s_bounce_buf, buffer, length); + buffer = s_bounce_buf; } + // buffer needs to be word aligned, if it is not, split the write into chunks + // (prefix/middle/suffix) with word-aligned buffers buf_pre = (4U - (uint8_t)((uint32_t)buffer % 4U)) % 4U; if (buf_pre > length) { buf_pre = length; @@ -391,162 +538,116 @@ int qspi_flash_write_page_begin(QSPIFlash *dev, const void *buffer, uint32_t add memset(&b_buf[buf_pre], 0xff, sizeof(b_buf) - buf_pre); memcpy(b_buf, buffer, buf_pre); - err = nrfx_qspi_write(b_buf, 4U, addr); - prv_wait_for_completion(dev); - PBL_ASSERTN(err == NRFX_SUCCESS); + prv_write(dev, b_buf, 4U, addr); addr += buf_pre; buffer = ((uint8_t *)buffer) + buf_pre; } if (buf_mid != 0U) { - while (nrfx_qspi_mem_busy_check() == NRFX_ERROR_BUSY) { + while (prv_wip_check(dev)) { } - err = nrfx_qspi_write(buffer, buf_mid, addr); - prv_wait_for_completion(dev); - PBL_ASSERTN(err == NRFX_SUCCESS); + prv_write(dev, buffer, buf_mid, addr); addr += buf_mid; buffer = ((uint8_t *)buffer) + buf_mid; } if (buf_suf != 0U) { - while (nrfx_qspi_mem_busy_check() == NRFX_ERROR_BUSY) { + while (prv_wip_check(dev)) { } memset(&b_buf[buf_suf], 0xff, 4U - buf_suf); memcpy(b_buf, buffer, buf_suf); - err = nrfx_qspi_write(b_buf, 4U, addr); - prv_wait_for_completion(dev); - PBL_ASSERTN(err == NRFX_SUCCESS); + prv_write(dev, b_buf, 4U, addr); } return length; } status_t qspi_flash_get_write_status(QSPIFlash *dev) { - return nrfx_qspi_mem_busy_check() == NRFX_SUCCESS ? S_SUCCESS : E_BUSY; + return prv_wip_check(dev) ? E_BUSY : S_SUCCESS; } void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active) { - uint8_t instruction; - uint32_t delay; + QSPIFlashPart *part = dev->state->part; + if (active) { - instruction = dev->state->part->instructions.enter_low_power; - delay = dev->state->part->standby_to_low_power_latency_us; + prv_cinstr(dev, part->instructions.enter_low_power); + if (part->standby_to_low_power_latency_us) { + delay_us(part->standby_to_low_power_latency_us); + } + + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_DEACTIVATE); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + nrf_qspi_disable(NRF_QSPI); } else { - instruction = dev->state->part->instructions.exit_low_power; - delay = dev->state->part->low_power_to_standby_latency_us; - } - prv_write_cmd_no_addr(dev->qspi, instruction); - if (delay) { - delay_us(delay); + nrf_qspi_enable(NRF_QSPI); + + nrf_qspi_int_disable(NRF_QSPI, NRF_QSPI_INT_READY_MASK); + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + while (!nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + } + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + + prv_cinstr(dev, part->instructions.exit_low_power); + if (part->low_power_to_standby_latency_us) { + delay_us(part->low_power_to_standby_latency_us); + } } } -static bool prv_blank_check_poll(QSPIFlash *dev, uint32_t addr, bool is_subsector) { +status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector) { const uint32_t size_bytes = is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES; const uint32_t BUF_SIZE_BYTES = 128; const uint32_t BUF_SIZE_WORDS = BUF_SIZE_BYTES / sizeof(uint32_t); uint32_t buffer[BUF_SIZE_WORDS]; - for (uint32_t offset = 0; offset < size_bytes; offset += BUF_SIZE_BYTES) { - flash_impl_read_sync(buffer, addr + offset, BUF_SIZE_BYTES); - for (uint32_t i = 0; i < BUF_SIZE_WORDS; ++i) { - if (buffer[i] != FLASH_RESET_WORD_VALUE) { - return false; - } - } - } - return true; -} - -status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector) { - const bool result = prv_blank_check_poll(dev, addr, is_subsector); - return result ? S_TRUE : S_FALSE; -} - -void qspi_flash_ll_set_register_bits(QSPIFlash *dev, uint8_t read_instruction, - uint8_t write_instruction, uint8_t value, uint8_t mask) { - // make sure we're not trying to set any bits not within the mask - PBL_ASSERTN((value & mask) == value); - - // first read the register - uint8_t reg_value; - prv_read_register(dev->qspi, read_instruction, ®_value, 1); - - // set the desired bits - reg_value = (reg_value & ~mask) | value; - - // enable writing and write the register value - prv_write_cmd_no_addr(dev->qspi, dev->state->part->instructions.write_enable); - qspi_indirect_write_no_addr(dev->qspi, write_instruction, ®_value, 1); -} - -status_t qspi_flash_write_protection_enable(QSPIFlash *dev) { return S_NO_ACTION_REQUIRED; } - -status_t qspi_flash_lock_sector(QSPIFlash *dev, uint32_t addr) { return S_SUCCESS; } - -status_t qspi_flash_unlock_all(QSPIFlash *dev) { return S_SUCCESS; } -status_t prv_qspi_security_register_check(QSPIFlash *dev, uint32_t addr) { - bool addr_valid = false; - - if (dev->state->part->sec_registers.num_sec_regs == 0U) { - return E_INVALID_OPERATION; - } - - for (uint8_t i = 0U; i < dev->state->part->sec_registers.num_sec_regs; ++i) { - if (addr >= dev->state->part->sec_registers.sec_regs[i] && - addr < dev->state->part->sec_registers.sec_regs[i] + - dev->state->part->sec_registers.sec_reg_size) { - addr_valid = true; - break; + for (uint32_t offset = 0U; offset < size_bytes; offset += BUF_SIZE_BYTES) { + qspi_flash_read_blocking(dev, addr + offset, buffer, BUF_SIZE_BYTES); + for (uint32_t i = 0U; i < BUF_SIZE_WORDS; ++i) { + if (buffer[i] != 0xFFFFFFFFUL) { + return S_FALSE; + } } } - if (!addr_valid) { - return E_INVALID_ARGUMENT; - } - - return S_SUCCESS; + return S_TRUE; } status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_t *val) { + QSPIFlashPart *part = dev->state->part; status_t ret; - nrfx_err_t err; - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(dev->state->part->instructions.read_sec, 0); uint8_t out[6]; uint8_t in[6]; + uint8_t len; ret = prv_qspi_security_register_check(dev, addr); if (ret != S_SUCCESS) { return ret; } - instr.io2_level = true; - instr.io3_level = true; - - if (dev->state->part->size > 0x1000000) { - instr.length = 7; + if (part->size > ADDR_4BYTE_THRESHOLD) { + len = 6; out[0] = (addr >> 24U); out[1] = (addr >> 16U) & 0xFFU; out[2] = (addr >> 8U) & 0xFFU; out[3] = addr & 0xFFU; } else { - instr.length = 6; + len = 5; out[0] = (addr >> 16U) & 0xFFU; out[1] = (addr >> 8U) & 0xFFU; out[2] = addr & 0xFFU; } - err = nrfx_qspi_cinstr_xfer(&instr, out, in); - if (err != NRFX_SUCCESS) { - return E_ERROR; - } + prv_cinstr_write_read(dev, part->instructions.read_sec, out, in, len); - if (dev->state->part->size > 0x1000000) { + if (part->size > ADDR_4BYTE_THRESHOLD) { *val = in[5]; } else { *val = in[4]; @@ -555,382 +656,128 @@ status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_ return 0; } -status_t qspi_flash_security_registers_are_locked(QSPIFlash *dev, bool *locked) { +status_t qspi_flash_security_register_is_locked(QSPIFlash *dev, uint32_t addr, bool *locked) { + QSPIFlashPart *part = dev->state->part; uint8_t sr2; + status_t res; - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr2, &sr2, 1); + res = prv_qspi_security_register_check(dev, addr); + if (res != S_SUCCESS) { + return res; + } + + prv_cinstr_read(dev, part->instructions.rdsr2, &sr2, 1); - *locked = !!(sr2 & dev->state->part->flag_status_bit_masks.sec_lock); + *locked = !!(sr2 & ((1U << SEC_ADDR_TO_IDX(addr)) << 3U)); return 0; } status_t qspi_flash_erase_security_register(QSPIFlash *dev, uint32_t addr) { + QSPIFlashPart *part = dev->state->part; status_t ret; - nrfx_err_t err; - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(dev->state->part->instructions.erase_sec, 0); uint8_t out[4]; + uint8_t len; ret = prv_qspi_security_register_check(dev, addr); if (ret != S_SUCCESS) { return ret; } - instr.io2_level = true; - instr.io3_level = true; - instr.wren = true; - - if (dev->state->part->size > 0x1000000) { - instr.length = 5; + if (part->size > ADDR_4BYTE_THRESHOLD) { + len = 4U; out[0] = (addr >> 24U); out[1] = (addr >> 16U) & 0xFFU; out[2] = (addr >> 8U) & 0xFFU; out[3] = addr & 0xFFU; } else { - instr.length = 4; + len = 3U; out[0] = (addr >> 16U) & 0xFFU; out[1] = (addr >> 8U) & 0xFFU; out[2] = addr & 0xFFU; } - err = nrfx_qspi_cinstr_xfer(&instr, out, NULL); - if (err != NRFX_SUCCESS) { - return E_ERROR; - } + prv_write_enable(dev); + + prv_cinstr_write(dev, part->instructions.erase_sec, out, len); - while (nrfx_qspi_mem_busy_check() == NRFX_ERROR_BUSY) { + while (prv_wip_check(dev)) { } return 0; } status_t qspi_flash_write_security_register(QSPIFlash *dev, uint32_t addr, uint8_t val) { + QSPIFlashPart *part = dev->state->part; status_t ret; - nrfx_err_t err; - nrf_qspi_cinstr_conf_t instr = NRFX_QSPI_DEFAULT_CINSTR(dev->state->part->instructions.program_sec, 0); uint8_t out[5]; + uint8_t len; ret = prv_qspi_security_register_check(dev, addr); if (ret != S_SUCCESS) { return ret; } - instr.io2_level = true; - instr.io3_level = true; - instr.wren = true; - - if (dev->state->part->size > 0x1000000) { - instr.length = 6; + if (part->size > ADDR_4BYTE_THRESHOLD) { + len = 5U; out[0] = (addr >> 24U); out[1] = (addr >> 16U) & 0xFFU; out[2] = (addr >> 8U) & 0xFFU; out[3] = addr & 0xFFU; out[4] = val; } else { - instr.length = 5; + len = 4U; out[0] = (addr >> 16U) & 0xFFU; out[1] = (addr >> 8U) & 0xFFU; out[2] = addr & 0xFFU; out[3] = val; } - err = nrfx_qspi_cinstr_xfer(&instr, out, NULL); - if (err != NRFX_SUCCESS) { - return E_ERROR; - } + prv_write_enable(dev); + + prv_cinstr_write(dev, part->instructions.program_sec, out, len); - while (nrfx_qspi_mem_busy_check() == NRFX_ERROR_BUSY) { + while (prv_wip_check(dev)) { } return 0; } const FlashSecurityRegisters *qspi_flash_security_registers_info(QSPIFlash *dev) { - return &dev->state->part->sec_registers; -} - -#ifdef RECOVERY_FW -status_t qspi_flash_lock_security_registers(QSPIFlash *dev) { - uint8_t sr[2]; - - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr1, &sr[0], 1); - prv_read_register(dev->qspi, dev->state->part->instructions.rdsr2, &sr[1], 1); - - sr[1] |= dev->state->part->flag_status_bit_masks.sec_lock; + QSPIFlashPart *part = dev->state->part; - prv_write_register(dev->qspi, dev->state->part->instructions.wrsr, sr, 2); - - return 0; -} -#endif // RECOVERY_FW - -#if !RELEASE -#include "console/prompt.h" -#include "drivers/flash.h" -#include "kernel/pbl_malloc.h" -#include "system/profiler.h" -#include "util/size.h" - -static bool prv_flash_read_verify(QSPIFlash *dev, int size, int offset) { - bool success = true; - - profiler_start(); - // prv_read_mmap(dev, 0, buffer_mmap, size); - profiler_stop(); - uint32_t mmap_time = profiler_get_total_duration(true); - - const int buf_size = 64; - char buf[buf_size]; - prompt_send_response_fmt(buf, buf_size, "Size: %d UNIMPL: MMP: %" PRIu32, size, mmap_time); - - return success; + return &part->sec_registers; } -struct FlashReadTestValues { - int size; - int offset; -}; - -const struct FlashReadTestValues FLASH_READ_TEST_TABLE[] = { - {.size = 1024, .offset = 0}, {.size = 1025, .offset = 0}, {.size = 1026, .offset = 0}, - {.size = 1027, .offset = 0}, {.size = 1024, .offset = 1}, {.size = 1025, .offset = 2}, - {.size = 1026, .offset = 3}, {.size = 4, .offset = 0}, {.size = 20, .offset = 0}, - {.size = 60, .offset = 0}, {.size = 127, .offset = 0}, {.size = 128, .offset = 0}, -}; - -void command_flash_apicheck(const char *len_str) { - QSPIFlash *dev = QSPI_FLASH; - const int buf_size = 64; - char buf[buf_size]; - int failures = 0; - int passes = 0; - - profiler_init(); - - prompt_send_response("Check whoami"); - if (!qspi_flash_check_whoami(dev)) { - ++failures; - prompt_send_response("ERROR: Who am I failed"); - } else { - ++passes; - } - - prompt_send_response("Enter low power mode"); - flash_impl_enter_low_power_mode(); - - // WHOAMI should fail in low-power mode - prompt_send_response("Check whoami, should fail in low power mode"); - if (qspi_flash_check_whoami(dev)) { - ++failures; - prompt_send_response("ERROR: Who am I failed"); - } else { - ++passes; - } - - prompt_send_response("Exit low power mode"); - flash_impl_exit_low_power_mode(); - - prompt_send_response("Start flash_read_verify test"); - - const int final_size = atoi(len_str); - - // If size is 0 run through a pre-defined table - if (final_size == 0) { - for (unsigned int i = 0; i < ARRAY_LENGTH(FLASH_READ_TEST_TABLE); ++i) { - bool result = prv_flash_read_verify(dev, FLASH_READ_TEST_TABLE[i].size, - FLASH_READ_TEST_TABLE[i].offset); - if (!result) { - ++failures; - } else { - ++passes; - } - } - - } else { - if (prv_flash_read_verify(dev, final_size, 3)) { - ++passes; - } else { - ++failures; - prompt_send_response("ERROR: flash_read_verify failed"); - } - } - - bool was_busy = false; - - // write a few bytes to the sector we're going to erase so it's not empty - uint8_t dummy_data = 0x55; - flash_write_bytes(&dummy_data, FLASH_REGION_FIRMWARE_DEST_BEGIN, sizeof(dummy_data)); - profiler_start(); - status_t result = flash_impl_erase_sector_begin(FLASH_REGION_FIRMWARE_DEST_BEGIN); - flash_impl_get_erase_status(); - if (result == S_SUCCESS) { - while (flash_impl_get_erase_status() == E_BUSY) { - was_busy = true; - } - } - profiler_stop(); - uint32_t duration = profiler_get_total_duration(true); - prompt_send_response_fmt(buf, buf_size, "Erase took: %" PRIu32, duration); - - // Fash erases take at least ~100ms, if we're too short we probably didn't erase - const uint32_t min_erase_time = 10000; - if (result != S_SUCCESS) { - ++failures; - prompt_send_response_fmt(buf, buf_size, "FAILURE: erase did not report success %" PRIi32, - result); - } else if (was_busy == false) { - ++failures; - prompt_send_response("FAILURE: Flash never became busy, but we should be busy for 300ms."); - prompt_send_response("FAILURE: Flash probably never did an erase."); - } else if (duration < min_erase_time) { - ++failures; - prompt_send_response("FAILURE: Flash erase completed way to quickly to have succeeded."); - } else { - ++passes; - } - - // must call blank_check_poll by hand, otherwise we'll get the dma version - profiler_start(); - bool is_blank = - qspi_flash_blank_check(QSPI_FLASH, FLASH_REGION_FIRMWARE_DEST_BEGIN, SUBSECTOR_SIZE_BYTES); - profiler_stop(); - - uint32_t blank = profiler_get_total_duration(true); - prompt_send_response_fmt(buf, buf_size, "Sector blank check via read took: %" PRIu32, blank); - if (is_blank != S_TRUE) { - ++failures; - prompt_send_response("FAILURE: sector not blank!?!"); - } else { - ++passes; - } - - profiler_start(); - is_blank = flash_impl_blank_check_subsector(FLASH_REGION_FIRMWARE_DEST_BEGIN); - profiler_stop(); +#ifdef CONFIG_RECOVERY_FW +status_t qspi_flash_lock_security_register(QSPIFlash *dev, uint32_t addr) { + uint8_t sr[2]; + status_t res; - blank = profiler_get_total_duration(true); - prompt_send_response_fmt(buf, buf_size, "Subsector blank check via read took: %" PRIu32, blank); - if (is_blank != S_TRUE) { - ++failures; - prompt_send_response("FAILURE: sector not blank!?!"); - } else { - ++passes; + res = prv_qspi_security_register_check(dev, addr); + if (res != S_SUCCESS) { + return res; } - if (failures == 0) { - prompt_send_response_fmt(buf, buf_size, "SUCCESS: run %d tests and all passeed", passes); - } else { - prompt_send_response_fmt(buf, buf_size, "FAILED: run %d tests and %d failed", passes + failures, - failures); - } -} - -#endif + prv_read_sr1(dev, &sr[0]); + prv_read_sr2(dev, &sr[1]); -#if RECOVERY_FW -#include "console/prompt.h" -#include "drivers/flash.h" + sr[1] |= (1U << SEC_ADDR_TO_IDX(addr)) << 3U; -#define SIGNAL_TEST_MAGIC_PATTERN (0xA5) -#define TEST_BUFFER_SIZE (1024) -static uint8_t s_test_buffer[TEST_BUFFER_SIZE]; -static const uint32_t s_test_addr = FLASH_REGION_FIRMWARE_DEST_END - SECTOR_SIZE_BYTES; -static bool s_signal_test_initialized; + prv_write_sr(dev, sr, 2U); -static void prv_get_fast_read_params(QSPIFlash *dev, uint8_t *instruction, uint8_t *dummy_cycles, - bool *is_ddr) { - if (dev->state->fast_read_ddr_enabled) { - *instruction = dev->state->part->instructions.fast_read_ddr; - *dummy_cycles = dev->state->part->dummy_cycles.fast_read_ddr; - *is_ddr = true; - } else { - *instruction = dev->state->part->instructions.fast_read; - *dummy_cycles = dev->state->part->dummy_cycles.fast_read; - *is_ddr = false; - } + return 0; } +#endif // CONFIG_RECOVERY_FW -static void prv_set_fast_read_ddr_enabled(QSPIFlash *dev, bool enabled) { - // If we're supposed to use DDR for fast read, make sure the part can support it - PBL_ASSERTN(!enabled || dev->state->part->supports_fast_read_ddr); - dev->state->fast_read_ddr_enabled = enabled; +status_t qspi_flash_write_protection_enable(QSPIFlash *dev) { + return S_NO_ACTION_REQUIRED; } -void command_flash_signal_test_init(void) { - // just test one sector, which is probably less than the size of the region - - // erase the sector - flash_erase_sector_blocking(s_test_addr); - - // set the contents of the sector such that we will end up reading alternating 1s and 0s - memset(s_test_buffer, SIGNAL_TEST_MAGIC_PATTERN, sizeof(s_test_buffer)); - flash_write_bytes(s_test_buffer, s_test_addr, sizeof(s_test_buffer)); - - QSPIFlash *dev = QSPI_FLASH; - // Ensure DDR is disabled for write check - prv_set_fast_read_ddr_enabled(dev, false); - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - PBL_ASSERTN(!is_ddr); - - qspi_indirect_read(dev->qspi, instruction, s_test_addr, dummy_cycles, s_test_buffer, - sizeof(s_test_buffer), is_ddr); - - prv_set_fast_read_ddr_enabled(dev, dev->default_fast_read_ddr_enabled); - - bool success = true; - for (uint32_t i = 0; i < sizeof(s_test_buffer); ++i) { - if (s_test_buffer[i] != SIGNAL_TEST_MAGIC_PATTERN) { - success = false; - break; - } - } - - if (success) { - prompt_send_response("Done!"); - s_signal_test_initialized = true; - } else { - prompt_send_response("ERROR: Data read (SDR mode) did not match data written!"); - } +status_t qspi_flash_lock_sector(QSPIFlash *dev, uint32_t addr) { + return S_SUCCESS; } -void command_flash_signal_test_run(void) { - if (!s_signal_test_initialized) { - prompt_send_response("ERROR: 'flash signal test init' must be run first!"); - return; - } - - QSPIFlash *dev = QSPI_FLASH; - - // set to DDR - prv_set_fast_read_ddr_enabled(dev, true); - - // issue the read - uint8_t instruction; - uint8_t dummy_cycles; - bool is_ddr; - prv_get_fast_read_params(dev, &instruction, &dummy_cycles, &is_ddr); - PBL_ASSERTN(is_ddr); - qspi_indirect_read(dev->qspi, instruction, s_test_addr, dummy_cycles, s_test_buffer, - sizeof(s_test_buffer), is_ddr); - - bool success = true; - for (uint32_t i = 0; i < sizeof(s_test_buffer); ++i) { - if (s_test_buffer[i] != SIGNAL_TEST_MAGIC_PATTERN) { - success = false; - break; - } - } - - // set back to default mode - prv_set_fast_read_ddr_enabled(dev, dev->default_fast_read_ddr_enabled); - - if (success) { - prompt_send_response("Ok"); - } else { - prompt_send_response("ERROR: Read value didn't match!"); - } -} -#endif +status_t qspi_flash_unlock_all(QSPIFlash *dev) { + return S_SUCCESS; +} \ No newline at end of file diff --git a/src/fw/drivers/nrf5/rtc.c b/src/fw/drivers/nrf5/rtc.c deleted file mode 100644 index 2a97f24e7e..0000000000 --- a/src/fw/drivers/nrf5/rtc.c +++ /dev/null @@ -1,463 +0,0 @@ -#include "drivers/rtc.h" -#include "drivers/rtc_private.h" -#include "drivers/stm32f2/rtc_calibration.h" - -#include "console/dbgserial.h" - -#include "drivers/exti.h" -#include "drivers/periph_config.h" -#include "drivers/watchdog.h" -#include "drivers/task_watchdog.h" - -#include "kernel/util/stop.h" -#include "mcu/interrupts.h" - -#include "services/common/regular_timer.h" - -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" - -#include "util/time/time.h" - -#include "FreeRTOS.h" - -#define NRF5_COMPATIBLE -#include -#include - -#include -#include -#include - -//! The type of a raw reading from the RTC (masked to 0xFFFFFF). -typedef uint32_t RtcIntervalTicks; - -//! How frequently we save the time state to the backup registers in ticks. -#define SAVE_TIME_FREQUENCY (30 * RTC_TICKS_HZ) -static RtcIntervalTicks prv_get_last_save_time_ticks(void); -static void prv_save_rtc_time_state(RtcIntervalTicks current_rtc_ticks); - -#define TICKS_IN_AN_INTERVAL (RTC_COUNTER_COUNTER_Msk + 1) - -static RtcIntervalTicks prv_elapsed_ticks(RtcIntervalTicks before, RtcIntervalTicks after) { - int32_t result = after - before; - if (result < 0) { - result = (TICKS_IN_AN_INTERVAL - before) + after; - } - return result; -} - -static RtcIntervalTicks prv_get_rtc_interval_ticks(void) { - return nrf_rtc_counter_get(BOARD_RTC_INST); -} - -/*** - * Logic associated with keeping raw coarse / fine RTC ticks -- the - * monotonic RtcTicks counter. - */ - -//! The value of the RTC registers last time we checked them. -static uint32_t s_last_ticks = 0; -//! This value is added to the current value of the RTC ticks to get the number -//! of ticks since system start. Incremented whenever we detect a rollover. -static RtcTicks s_coarse_ticks = 1; - -static bool s_did_init_rtc = false; - -//! did we boot with a full reset that brought RTC ticks to 0? -static bool s_had_amnesia_on_boot = false; - -static void prv_check_and_handle_rollover(RtcIntervalTicks rtc_ticks) { - bool save_needed = false; - - const RtcIntervalTicks last_ticks = s_last_ticks; - s_last_ticks = rtc_ticks; - - if (rtc_ticks < last_ticks) { - // We've wrapped. Add on the RTC wrap length to the base number. On - // nRF5, this is 0xFFFFFF; that's only 4.5 hours (at 1.024 kHz), - // compared to STM32's available SECONDS_IN_A_DAY. Sucks for us; oh - // well. - - s_coarse_ticks += TICKS_IN_AN_INTERVAL; - - save_needed = true; - } else if (prv_elapsed_ticks(prv_get_last_save_time_ticks(), rtc_ticks) > SAVE_TIME_FREQUENCY) { - // If we didn't do this, we would have an edge case where if the watch reset - // immediately before rollover and then rolled over before we booted again, - // we wouldn't be able to detect the rollover and we'd think the saved state - // is very fresh, when really it's over an interval old. By saving multiple - // times an interval this is still possible to happen, but it's much less likely. - // We would need to be shutdown for (RTC_COUNTER_COUNTER_Msk - SAVE_TIME_FREQUENCY) ticks - // for this to happen. - save_needed = true; - } - - if (save_needed) { - prv_save_rtc_time_state(rtc_ticks); - } -} - -RtcTicks rtc_get_ticks(void) { - // Prevent this from being interrupted - bool ints_enabled = mcu_state_are_interrupts_enabled(); - if (ints_enabled) { - __disable_irq(); - } - - RtcTicks rtc_interval_ticks = prv_get_rtc_interval_ticks(); - prv_check_and_handle_rollover(rtc_interval_ticks); - - if (ints_enabled) { - __enable_irq(); - } - - return s_coarse_ticks + rtc_interval_ticks; -} - -/*** - * Logic associated with converting extended RTC ticks to wall clock time. - */ - -//! This variable is a UNIX timestamp of what the current wall clock time was at tick s_time_tick_base. -static time_t s_time_base = 0; -//! This variable is the tick where the wall clock time was equal to s_time_base. If you subtract this variable -//! from the current tick count, you'll get the number of ticks that have elapsed since s_time_base, which will -//! allow you to calculate the current wall clock time. Note that this value may be negative on startup, see -//! prv_restore_rtc_time_state -static int64_t s_time_tick_base = 0; - -static time_t prv_ticks_to_time(RtcTicks ticks) { - return s_time_base + ((ticks - s_time_tick_base) / RTC_TICKS_HZ); -} - -void rtc_set_time(time_t time) { -#ifdef PBL_LOG_ENABLED - char buffer[TIME_STRING_BUFFER_SIZE]; - PBL_LOG(LOG_LEVEL_INFO, "Setting time to %lu <%s>", time, time_t_to_string(buffer, time)); -#endif - - s_time_base = time; - s_time_tick_base = rtc_get_ticks(); - - prv_save_rtc_time_state(s_time_tick_base - s_coarse_ticks); -} - -time_t rtc_get_time(void) { - return prv_ticks_to_time(rtc_get_ticks()); -} - -void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) { - RtcTicks ticks = rtc_get_ticks(); - - RtcTicks ticks_since_time_base = (ticks - s_time_tick_base); - *out_seconds = s_time_base + (ticks_since_time_base / RTC_TICKS_HZ); - - RtcTicks ticks_this_second = ticks_since_time_base % RTC_TICKS_HZ; - *out_ms = (ticks_this_second * 1000) / RTC_TICKS_HZ; -} - -/*** - * Logic associated with saving the RTC-tick-to-wallclock conversion factor - * to retained-RAM. - */ - -static void prv_restore_rtc_time_state(void) { - // Recover the previously set time from the RTC backup registers. - RtcIntervalTicks last_save_time_ticks = retained_read(CURRENT_INTERVAL_TICKS_REGISTER); - time_t last_save_time = retained_read(CURRENT_TIME_REGISTER); - - if (s_had_amnesia_on_boot) { - /* We have no idea what time it might be. The closest we got is the - * last time we saved. */ - s_time_base = last_save_time; - s_time_tick_base = 0; - PBL_LOG(LOG_LEVEL_INFO, "Restore RTC: we are on our way up with amnesia"); - } else { - RtcIntervalTicks current_ticks = prv_get_rtc_interval_ticks(); - const int32_t ticks_since_last_save = prv_elapsed_ticks(last_save_time_ticks * RTC_TICKS_HZ, current_ticks); - s_time_base = last_save_time + (ticks_since_last_save / RTC_TICKS_HZ); - s_time_tick_base = -(((int64_t)current_ticks) % RTC_TICKS_HZ); - PBL_LOG(LOG_LEVEL_INFO, "Restore RTC: we are on our way up with interval_ticks = %"PRIu32, current_ticks); - PBL_LOG(LOG_LEVEL_INFO, "Restore RTC: saved: %"PRIu32" diff: %"PRIu32, last_save_time_ticks, ticks_since_last_save); - } - - char buffer[TIME_STRING_BUFFER_SIZE]; - PBL_LOG(LOG_LEVEL_INFO, "Restore RTC: saved_time: %s raw: %lu", time_t_to_string(buffer, last_save_time), last_save_time); - PBL_LOG(LOG_LEVEL_INFO, "Restore RTC: current time: %s", time_t_to_string(buffer, s_time_base)); -} - -static RtcIntervalTicks prv_get_last_save_time_ticks(void) { - return retained_read(CURRENT_INTERVAL_TICKS_REGISTER); -} - -static void prv_save_rtc_time_state_exact(RtcIntervalTicks current_rtc_ticks, time_t time) { - retained_write(CURRENT_TIME_REGISTER, time); - retained_write(CURRENT_INTERVAL_TICKS_REGISTER, current_rtc_ticks); -} - -static void prv_save_rtc_time_state(RtcIntervalTicks current_rtc_ticks) { - if (!s_did_init_rtc) { - return; - } - - // Floor it to the latest second - const RtcIntervalTicks current_rtc_ticks_at_second = (current_rtc_ticks / RTC_TICKS_HZ) * RTC_TICKS_HZ; - - prv_save_rtc_time_state_exact(current_rtc_ticks_at_second, prv_ticks_to_time(s_coarse_ticks + current_rtc_ticks)); -} - -/*** Logic that ought be refactored into rtc_common, were it not stm32-only. ***/ - -bool rtc_sanitize_struct_tm(struct tm *t) { - // These values come from time_t (which suffers from the 2038 problem) and our hardware which - // only stores a 2 digit year, so we only represent values after 2000. - - // Remember tm_year is years since 1900. - if (t->tm_year < 100) { - // Bump it up to the year 2000 to work with our hardware. - t->tm_year = 100; - return true; - } else if (t->tm_year > 137) { - t->tm_year = 137; - return true; - } - return false; -} - -bool rtc_sanitize_time_t(time_t *t) { - struct tm time_struct; - gmtime_r(t, &time_struct); - - const bool result = rtc_sanitize_struct_tm(&time_struct); - *t = mktime(&time_struct); - - return result; -} - -void rtc_get_time_tm(struct tm* time_tm) { - time_t t = rtc_get_time(); - localtime_r(&t, time_tm); -} - -const char *rtc_get_time_string(char *buffer) { - return time_t_to_string(buffer, rtc_get_time()); -} - -const char *time_t_to_string(char *buffer, time_t t) { - struct tm time; - localtime_r(&t, &time); - - strftime(buffer, TIME_STRING_BUFFER_SIZE, "%c", &time); - - return buffer; -} - - -//! We attempt to save registers by placing both the timezone abbreviation -//! timezone index and the daylight_savingtime into the same register set -void rtc_set_timezone(TimezoneInfo *tzinfo) { - uint32_t *raw = (uint32_t*)tzinfo; - _Static_assert(sizeof(TimezoneInfo) <= 5 * sizeof(uint32_t), - "RTC Set Timezone invalid data size"); - - retained_write(RTC_TIMEZONE_ABBR_START, raw[0]); - retained_write(RTC_TIMEZONE_ABBR_END_TZID_DSTID, raw[1]); - retained_write(RTC_TIMEZONE_GMTOFFSET, raw[2]); - retained_write(RTC_TIMEZONE_DST_START, raw[3]); - retained_write(RTC_TIMEZONE_DST_END, raw[4]); -} - -void rtc_get_timezone(TimezoneInfo *tzinfo) { - uint32_t *raw = (uint32_t*)tzinfo; - - raw[0] = retained_read(RTC_TIMEZONE_ABBR_START); - raw[1] = retained_read(RTC_TIMEZONE_ABBR_END_TZID_DSTID); - raw[2] = retained_read(RTC_TIMEZONE_GMTOFFSET); - raw[3] = retained_read(RTC_TIMEZONE_DST_START); - raw[4] = retained_read(RTC_TIMEZONE_DST_END); -} - -void rtc_timezone_clear(void) { - retained_write(RTC_TIMEZONE_ABBR_START, 0); - retained_write(RTC_TIMEZONE_ABBR_END_TZID_DSTID, 0); - retained_write(RTC_TIMEZONE_GMTOFFSET, 0); - retained_write(RTC_TIMEZONE_DST_START, 0); - retained_write(RTC_TIMEZONE_DST_END, 0); -} - -uint16_t rtc_get_timezone_id(void) { - return ((retained_read(RTC_TIMEZONE_ABBR_END_TZID_DSTID) >> 16) & 0xFFFF); -} - -bool rtc_is_timezone_set(void) { - // True if the timezone abbreviation has been set (including UNK for unknown) - return (retained_read(RTC_TIMEZONE_ABBR_START) != 0); -} - -void rtc_enable_backup_regs(void) { - /* we always use retained ram for this, so no problem */ -} - -void rtc_calibrate_frequency(uint32_t frequency) { - /* On nRF5, there is no way to calibrate the RTC. That crystal had better - * be accurate! - */ -} - -void rtc_init(void) { - /* May have been initialized out of sequence by FreeRTOS startup -- if so, - * no worries. */ - if (s_did_init_rtc) { - return; - } - -#ifndef NRF_RTC_FREQ_TO_PRESCALER -#define NRF_RTC_FREQ_TO_PRESCALER RTC_FREQ_TO_PRESCALER -#endif - if (prv_get_rtc_interval_ticks() == 0) { - s_had_amnesia_on_boot = true; - PBL_LOG(LOG_LEVEL_INFO, "RTC appears to have been reset :( hope you have your phone connected"); - } - - nrf_rtc_prescaler_set(BOARD_RTC_INST, NRF_RTC_FREQ_TO_PRESCALER(RTC_TICKS_HZ)); - - nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); - nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); - nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); - nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE1_MASK); - nrf_rtc_cc_set(BOARD_RTC_INST, 1, (RTC_TICKS_HZ / (1000 / TASK_WATCHDOG_FEED_PERIOD_MS)) - 1); - - nrf_rtc_task_trigger(BOARD_RTC_INST, NRF_RTC_TASK_START); - - prv_restore_rtc_time_state(); - s_did_init_rtc = true; - - NVIC_SetPriority(BOARD_RTC_IRQN, configKERNEL_INTERRUPT_PRIORITY); - NVIC_EnableIRQ(BOARD_RTC_IRQN); - -#if TEST_RTC_FREQ - // FIXME: can be removed after FIRM-121 is fixed - extern void board_early_init(); - board_early_init(); - for (int i = 0; i < 100; i++) { - uint32_t ctr0 = nrf_rtc_counter_get(BOARD_RTC_INST); - while (nrf_rtc_counter_get(BOARD_RTC_INST) == ctr0) - ; - ctr0 = nrf_rtc_counter_get(BOARD_RTC_INST) + 100; - uint32_t iters = 0; - while (nrf_rtc_counter_get(BOARD_RTC_INST) != ctr0) - iters++; - PBL_LOG(LOG_LEVEL_INFO, "RTC: 100 RTC ticks took %"PRIu32" iters", iters); - } -#endif -} - -void rtc_enable_synthetic_systick(void) { - // Now that the RTC is awake, we can switch from SysTick to RTC interrupt - // ticks. We need to do this so that we actually get ticks in wfi, since - // nRF5 stops SysTick in sleep. - _Static_assert(RTC_TICKS_HZ == configTICK_RATE_HZ); - if (!s_did_init_rtc) { - rtc_init(); - } - - nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); - nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_TICK_MASK); -} - -void rtc_systick_pause(void) { - // We don't want the fine-grained interrupts at 100Hz when we're in stop - // mode -- we have a timer set for that, after all. - nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); - nrf_rtc_int_disable(BOARD_RTC_INST, NRF_RTC_INT_TICK_MASK); -} - -void rtc_systick_resume(void) { - nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); - nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); - nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_TICK_MASK); -} - -//! Our RTC tick counter can overflow if nobody asks about it. This -//! repeating callback allows us to make sure this doesn't happen. -static void prv_rtc_resync_timer_callback() { - rtc_get_ticks(); -} - -void rtc_init_timers(void) { - static RegularTimerInfo rtc_sync_timer = { .list_node = { 0, 0 }, .cb = prv_rtc_resync_timer_callback}; - regular_timer_add_minutes_callback(&rtc_sync_timer); -} - -/*** Logic to control RTC wakeup timer. ***/ - -static RtcTicks s_alarm_set_time = 0; -static RtcTicks s_alarm_expiry_time = 0; -static bool s_tick_alarm_initialized = false; - -void rtc_alarm_init(void) { - nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); - nrf_rtc_int_disable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE0_MASK); - s_tick_alarm_initialized = true; -} - -void rtc_alarm_set(RtcTicks num_ticks) { - PBL_ASSERTN(s_tick_alarm_initialized); - - nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); - nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); - - s_alarm_set_time = rtc_get_ticks(); - s_alarm_expiry_time = s_alarm_set_time + num_ticks - 1; - - /* We're bounded by the regular_timer_add_minutes_callback for the - * rtc_alarm_set, so we're not going to wrap around more than once -- one - * minute is always less than 4.5 hours. - */ - nrf_rtc_cc_set(BOARD_RTC_INST, 0, s_alarm_expiry_time & RTC_COUNTER_COUNTER_Msk); - - nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); - nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE0_MASK); -} - -RtcTicks rtc_alarm_get_elapsed_ticks(void) { - return rtc_get_ticks() - s_alarm_set_time; -} - -bool rtc_alarm_is_initialized(void) { - return s_tick_alarm_initialized; -} - -//! Handler for the RTC alarm interrupt. We don't actually have to do -//! anything in that handler, just the interrupt firing is enough to bring -//! us out of stop mode. But once we switch into RTC-provided-systick mode, -//! we *do* need to call into systick! -void rtc_irq_handler(void) { - if (nrf_rtc_event_check(BOARD_RTC_INST, NRF_RTC_EVENT_TICK)) { - extern void SysTick_Handler(); - - nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); - SysTick_Handler(); - } - - if (nrf_rtc_event_check(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0)) { - nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); - nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); - nrf_rtc_int_disable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE0_MASK); - } - - if (nrf_rtc_event_check(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1)) { - task_watchdog_feed(); - - nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); - - uint32_t next_feed_ticks = nrf_rtc_counter_get(BOARD_RTC_INST) + - (RTC_TICKS_HZ / (1000 / TASK_WATCHDOG_FEED_PERIOD_MS)); - nrf_rtc_cc_set(BOARD_RTC_INST, 1, next_feed_ticks & RTC_COUNTER_COUNTER_Msk); - } - - NVIC_ClearPendingIRQ(BOARD_RTC_IRQN); -} diff --git a/src/fw/drivers/nrf5/spi.c b/src/fw/drivers/nrf5/spi.c deleted file mode 100644 index 1c6d72d561..0000000000 --- a/src/fw/drivers/nrf5/spi.c +++ /dev/null @@ -1,463 +0,0 @@ -#include "spi_definitions.h" -#include "drivers/spi.h" -#include "drivers/spi_dma.h" - -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/units.h" - -#define NRF5_COMPATIBLE -#include - -/* XXX: this really needs to be refactored to allow mutual exclusion between two SPISlavePorts on a single SPIBus (and other things) */ - -static void prv_spi_bus_deinit(const SPIBus *bus) { - bus->state->initialized = false; -} - -void prv_spi_bus_init(const SPIBus *bus) { - if (bus->state->initialized) { - return; - } - // copy the speed over to the transient state since the slave port can change it - bus->state->spi_clock_speed_hz = bus->spi_clock_speed_hz; - bus->state->initialized = true; -} - -static void prv_spi_slave_init(const SPISlavePort *slave, bool is_reinit) { - SPIBus *bus = slave->spi_bus; - - nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG( - bus->spi_sclk, bus->spi_mosi, bus->spi_miso, NRF_SPIM_PIN_NOT_CONNECTED); - config.frequency = bus->state->spi_clock_speed_hz; - config.mode = (slave->spi_cpol == SpiCPol_Low && slave->spi_cpha == SpiCPha_1Edge) ? NRF_SPIM_MODE_0 : - (slave->spi_cpol == SpiCPol_Low && slave->spi_cpha == SpiCPha_2Edge) ? NRF_SPIM_MODE_1 : - (slave->spi_cpol == SpiCPol_High && slave->spi_cpha == SpiCPha_1Edge) ? NRF_SPIM_MODE_2 : - NRF_SPIM_MODE_3; - config.bit_order = slave->spi_first_bit == SpiFirstBit_MSB ? NRF_SPIM_BIT_ORDER_MSB_FIRST : NRF_SPIM_BIT_ORDER_LSB_FIRST; - - nrfx_err_t rv; - if (is_reinit) { - nrfx_spim_uninit(&bus->spi); - rv = nrfx_spim_init(&bus->spi, &config, NULL /* always in blocking mode! */, NULL); - } else { - rv = nrfx_spim_init(&bus->spi, &config, NULL /* always in blocking mode! */, NULL); - } - PBL_ASSERTN(rv == NRFX_SUCCESS); -} - -static void prv_spi_slave_deinit(const SPISlavePort *slave) { - spi_ll_slave_acquire(slave); - SPIBus *bus = slave->spi_bus; - nrfx_spim_uninit(&bus->spi); - spi_ll_slave_release(slave); -} - -// -//! High level slave port interface -//! This part of the API can be used for fairly straightforward SPI interactions -//! The assertion and deassertion of the SCS line is automatic -// - -void spi_slave_port_deinit(const SPISlavePort *slave) { - // don't deinitialize twice - if (!slave->slave_state->initialized) { - return; - } - prv_spi_slave_deinit(slave); - prv_spi_bus_deinit(slave->spi_bus); - slave->slave_state->initialized = false; -} - -void spi_slave_port_init(const SPISlavePort *slave) { - // don't initialize twice - if (slave->slave_state->initialized) { - return; - } - slave->slave_state->initialized = true; - slave->slave_state->acquired = false; - slave->slave_state->scs_selected = false; - prv_spi_bus_init(slave->spi_bus); - - // SCS - gpio_output_init(&slave->spi_scs, GPIO_OType_PP, slave->spi_bus->spi_sclk_speed); - gpio_output_set(&slave->spi_scs, false); // SCS not asserted (high) - - // Set up an SPI - prv_spi_slave_deinit(slave); - - prv_spi_slave_init(slave, false /* initial init */); -} - -static void prv_spi_acquire_helper(const SPISlavePort *slave) { - spi_ll_slave_acquire(slave); - spi_ll_slave_scs_assert(slave); -} - -static void prv_spi_release_helper(const SPISlavePort *slave) { - spi_ll_slave_scs_deassert(slave); - spi_ll_slave_release(slave); -} - -uint8_t spi_slave_read_write(const SPISlavePort *slave, uint8_t out) { - prv_spi_acquire_helper(slave); - uint8_t ret = spi_ll_slave_read_write(slave, out); - prv_spi_release_helper(slave); - return ret; -} - -void spi_slave_write(const SPISlavePort *slave, uint8_t out) { - prv_spi_acquire_helper(slave); - spi_ll_slave_write(slave, out); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_read(const SPISlavePort *slave, void *in, size_t len) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_read(slave, in, len); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_write(const SPISlavePort *slave, const void *out, size_t len) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_write(slave, out, len); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_read_write(const SPISlavePort *slave, const void *out, void *in, size_t len) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_read_write(slave, out, in, len); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_read_write_scatter(const SPISlavePort *slave, - const SPIScatterGather *sc_info, - size_t num_sg) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_read_write_scatter(slave, sc_info, num_sg); - prv_spi_release_helper(slave); -} - -void spi_slave_set_frequency(const SPISlavePort *slave, uint32_t frequency_hz) { - slave->spi_bus->state->spi_clock_speed_hz = frequency_hz; - prv_spi_slave_init(slave, true /* is_reinit */); -} - -void spi_slave_wait_until_idle_blocking(const SPISlavePort *slave) { - // "always has been!" -} - -#if 0 -uint32_t spi_get_dma_base_address(const SPISlavePort *slave) { - return (uint32_t)&(slave->spi_bus->spi->DR); -} -#endif - -// -//! Low level slave port interface -//! This part of the API can be used for slightly more complex SPI operations -//! (such as piecemeal reads or writes). Assertion and deassertion of SCS -//! is up to the caller. Asserts in the code will help to ensure that the -//! API is used correctly. -// - -void spi_ll_slave_acquire(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired == false); - slave->slave_state->acquired = true; - spi_ll_slave_spi_enable(slave); -} - -void spi_ll_slave_release(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - spi_ll_slave_spi_disable(slave); - slave->slave_state->acquired = false; -} - -void spi_ll_slave_spi_enable(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); -} - -void spi_ll_slave_spi_disable(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); -} - -void spi_ll_slave_scs_assert(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected == false); - slave->slave_state->scs_selected = true; - gpio_output_set(&slave->spi_scs, true); // SCS asserted (low) -} - -void spi_ll_slave_scs_deassert(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - slave->slave_state->scs_selected = false; - gpio_output_set(&slave->spi_scs, false); // SCS not asserted (high) -} - -uint8_t spi_ll_slave_read_write(const SPISlavePort *slave, uint8_t out) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - - uint8_t in; - nrfx_spim_xfer_desc_t xfer; - xfer.p_tx_buffer = &out; - xfer.tx_length = 1; - xfer.p_rx_buffer = ∈ - xfer.rx_length = 1; - - nrfx_err_t rv = nrfx_spim_xfer(&slave->spi_bus->spi, &xfer, 0); - PBL_ASSERTN(rv == NRFX_SUCCESS); - - return in; -} - -void spi_ll_slave_write(const SPISlavePort *slave, uint8_t out) { - (void) spi_ll_slave_read_write(slave, out); -} - -void spi_ll_slave_burst_read(const SPISlavePort *slave, void *in, size_t len) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - uint8_t *cptr = in; - while (len--) { - *(cptr++) = spi_ll_slave_read_write(slave, 0); // useless write-data - } -} - -void spi_ll_slave_burst_write(const SPISlavePort *slave, const void *out, size_t len) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - - nrfx_spim_xfer_desc_t xfer; - xfer.p_tx_buffer = out; - xfer.tx_length = len; - xfer.p_rx_buffer = NULL; - xfer.rx_length = 0; - - nrfx_err_t rv = nrfx_spim_xfer(&slave->spi_bus->spi, &xfer, 0); - PBL_ASSERTN(rv == NRFX_SUCCESS); -} - -void spi_ll_slave_burst_read_write(const SPISlavePort *slave, - const void *out, - void *in, - size_t len) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - - - nrfx_spim_xfer_desc_t xfer; - xfer.p_tx_buffer = out; - xfer.tx_length = len; - xfer.p_rx_buffer = in; - xfer.rx_length = len; - - nrfx_err_t rv = nrfx_spim_xfer(&slave->spi_bus->spi, &xfer, 0); - PBL_ASSERTN(rv == NRFX_SUCCESS); -} - -void spi_ll_slave_burst_read_write_scatter(const SPISlavePort *slave, - const SPIScatterGather *sc_info, - size_t num_sg) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - for (size_t elem = 0; elem < num_sg; ++elem) { - const SPIScatterGather *sg = &sc_info[elem]; - spi_ll_slave_burst_read_write(slave, sg->sg_out, sg->sg_in, sg->sg_len); - } -} - -#if 0 -/* SPI DMA is not supported on nRF5 yet */ - -static bool prv_dma_irq_handler(DMARequest *request, void *context) { - const SPISlavePort *slave = context; - PBL_ASSERTN(slave); - bool is_done = false; - switch (slave->slave_state->dma_state) { - case SPISlavePortDMAState_Read: - case SPISlavePortDMAState_Write: - case SPISlavePortDMAState_ReadWriteOneInterrupt: - slave->slave_state->dma_state = SPISlavePortDMAState_Idle; - is_done = true; - break; - case SPISlavePortDMAState_ReadWrite: - slave->slave_state->dma_state = SPISlavePortDMAState_ReadWriteOneInterrupt; - break; - case SPISlavePortDMAState_Idle: - default: - WTF; - } - SPIDMACompleteHandler handler = slave->slave_state->dma_complete_handler; - if (is_done && handler) { - return handler(slave, slave->slave_state->dma_complete_context); - } - return false; -} - -void spi_ll_slave_read_dma_start(const SPISlavePort *slave, void *in, size_t len, - SPIDMACompleteHandler handler, void *context) { - PBL_ASSERTN(slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->dma_state == SPISlavePortDMAState_Idle); - slave->slave_state->dma_state = SPISlavePortDMAState_Read; - slave->slave_state->dma_complete_handler = handler; - slave->slave_state->dma_complete_context = context; - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, true); - dma_request_start_direct(slave->rx_dma, in, (void *)&slave->spi_bus->spi->DR, len, - prv_dma_irq_handler, (void *)slave); -} - -void spi_ll_slave_read_dma_stop(const SPISlavePort *slave) { - if (slave->slave_state->dma_state != SPISlavePortDMAState_Read) { - return; - } - dma_request_stop(slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, false); - slave->slave_state->dma_complete_handler = NULL; - slave->slave_state->dma_complete_context = NULL; -} - -void spi_ll_slave_write_dma_start(const SPISlavePort *slave, const void *out, size_t len, - SPIDMACompleteHandler handler, void *context) { - PBL_ASSERTN(slave->tx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->dma_state == SPISlavePortDMAState_Idle); - slave->slave_state->dma_state = SPISlavePortDMAState_Write; - slave->slave_state->dma_complete_handler = handler; - slave->slave_state->dma_complete_context = context; - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, true); - dma_request_start_direct(slave->tx_dma, (void *)&slave->spi_bus->spi->DR, out, len, - prv_dma_irq_handler, (void *)slave); -} - -void spi_ll_slave_write_dma_stop(const SPISlavePort *slave) { - if (slave->slave_state->dma_state != SPISlavePortDMAState_Write) { - return; - } - dma_request_stop(slave->tx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, false); - slave->slave_state->dma_complete_handler = NULL; - slave->slave_state->dma_complete_context = NULL; -} - -void spi_ll_slave_read_write_dma_start(const SPISlavePort *slave, const void *out, void *in, - size_t len, SPIDMACompleteHandler handler, void *context) { - PBL_ASSERTN(slave->rx_dma && slave->tx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->dma_state == SPISlavePortDMAState_Idle); - slave->slave_state->dma_complete_handler = handler; - slave->slave_state->dma_complete_context = context; - - if (out) { - dma_request_set_memory_increment_disabled(slave->tx_dma, false); - } else { - dma_request_set_memory_increment_disabled(slave->tx_dma, true); - static const uint8_t s_zero = 0; - out = &s_zero; - } - - if (in) { - slave->slave_state->dma_state = SPISlavePortDMAState_ReadWrite; - // start the read DMA - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, true); - dma_request_start_direct(slave->rx_dma, in, (void *)&slave->spi_bus->spi->DR, len, - prv_dma_irq_handler, (void *)slave); - } else { - slave->slave_state->dma_state = SPISlavePortDMAState_ReadWriteOneInterrupt; - } - // start the write DMA - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, true); - dma_request_start_direct(slave->tx_dma, (void *)&slave->spi_bus->spi->DR, out, len, - prv_dma_irq_handler, (void *)slave); -} - -void spi_ll_slave_read_write_dma_stop(const SPISlavePort *slave) { - if ((slave->slave_state->dma_state != SPISlavePortDMAState_ReadWrite) && - (slave->slave_state->dma_state != SPISlavePortDMAState_ReadWriteOneInterrupt)) { - return; - } - PBL_ASSERTN(slave->tx_dma && slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - dma_request_stop(slave->rx_dma); - dma_request_stop(slave->tx_dma); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, false); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, false); - slave->slave_state->dma_complete_handler = NULL; - slave->slave_state->dma_complete_context = NULL; -} - -bool spi_ll_slave_dma_in_progress(const SPISlavePort *slave) { - PBL_ASSERTN(slave->tx_dma || slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - return (slave->rx_dma && dma_request_in_progress(slave->rx_dma)) || - (slave->tx_dma && dma_request_in_progress(slave->tx_dma)); -} - -void spi_ll_slave_set_tx_dma(const SPISlavePort *slave, bool enable) { - PBL_ASSERTN(slave->slave_state->initialized); - spi_ll_slave_acquire(slave); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, enable); - spi_ll_slave_release(slave); -} - -void spi_ll_slave_set_rx_dma(const SPISlavePort *slave, bool enable) { - PBL_ASSERTN(slave->slave_state->initialized); - spi_ll_slave_acquire(slave); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, enable); - spi_ll_slave_release(slave); -} - - -void spi_ll_slave_drive_clock(const SPISlavePort *slave, bool enable) { - const AfConfig *spi_sclk = &slave->spi_bus->spi_sclk; - - if (enable) { - OutputConfig clk_as_gpio = { - .gpio = spi_sclk->gpio, - .gpio_pin = spi_sclk->gpio_pin, - .active_high = true, - }; - gpio_output_init(&clk_as_gpio, GPIO_OType_PP, GPIO_Speed_50MHz); - gpio_output_set(&clk_as_gpio, false); - } else { - prv_configure_spi_sclk(spi_sclk, slave->spi_bus->spi_sclk_speed); - } -} - -void spi_ll_slave_clear_errors(const SPISlavePort *slave) { - // First, empty the RX FIFO by reading the data. If in TX-only mode, it's possible that - // received data (0x00s) will be left in the RX FIFO. - - // NOTE: Obviously, do not call this function with transfer in progress. - while (slave->spi_bus->spi->SR & SPI_SR_RXNE) { - (void)slave->spi_bus->spi->DR; - } - - // If the FIFO overflowed, the OVR error will be flagged. Clear the error. - (void)slave->spi_bus->spi->DR; - (void)slave->spi_bus->spi->SR; -} - -#endif diff --git a/src/fw/drivers/nrf5/spi_definitions.h b/src/fw/drivers/nrf5/spi_definitions.h deleted file mode 100644 index 4d223a015e..0000000000 --- a/src/fw/drivers/nrf5/spi_definitions.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include "drivers/spi.h" - -#include "board/board.h" - -//! Generic(ish) definitions of how we wish a particular SPI to be configured -//! (Initially based on ST configuration and registers) -//! board.h and board_xxxx.h will use these definitions to configure each SPI -//! spi.c will use these definitions to program the device - -// REVISIT: We may like to split the definition and control of the SCS -// signal out of the main spi driver and into a separate driver so -// that if we ever share an SPI and use multiple SCS bits to select -// the destination we can control them individually. As it stands now -// we have exactly one. - -//! SPI transmission modes (unidirectional/bidirectional etc) -typedef enum SpiDirection { - SpiDirection_2LinesFullDuplex = 0x0000, - SpiDirection_2LinesRxOnly = 0x0400, - SpiDirection_1LineRx = 0x8000, - SpiDirection_1LineTx = 0xC000 -} SpiDirection; - -//! SPI Clock Polarity -typedef enum SpiCPol { - SpiCPol_Low = 0x0, - SpiCPol_High = 0x2 -} SpiCPol; - -//! SPI Clock Phase -typedef enum SpiCPha { - SpiCPha_1Edge = 0x0, - SpiCPha_2Edge = 0x1 -} SpiCPha; - -//! SPI MSB / LSB First Bit Transmission -typedef enum SpiFirstBit { - SpiFirstBit_MSB = 0x0000, - SpiFirstBit_LSB = 0x0080 -} SpiFirstBit; - -//! SPI / I2S Flags -typedef enum SpiI2sFlag { - SpiI2sFlag_RXNE = 0x0001, - SpiI2sFlag_TXE = 0x0002, - I2sFlag_CHSIDE = 0x0004, - I2sFlag_UDR = 0x0008, - SpiFlag_CRCERR = 0x0010, - SpiFlag_MODF = 0x0020, - SpiI2sFlag_OVR = 0x0040, - SpiI2sFlag_BSY = 0x0080, - SpiI2sFlag_TIFRFE = 0x0100 -} SpiI2sFlag; - -typedef struct SPIBusState { - uint32_t spi_clock_speed_hz; // can be changed by slave port - uint32_t spi_clock_periph; // mapped to SPI peripheral - uint32_t spi_clock_periph_speed; - bool initialized; -} SPIBusState; - -//! An SPI Bus specifies an SPI Instance and the I/O pins -//! used for the CLK, MOSI and MISO pins -//! The communication specific parameters (direction and -//! phase etc) and the pin to use for slave select are set per -//! SPISlavePort -//! REVISIT: There is currently no arbitration between possible -//! slave ports on the same bus - since for now all of our -//! SPI devices are point-to-point -typedef const struct SPIBus { - SPIBusState *state; - nrfx_spim_t spi; - uint32_t spi_sclk; - uint32_t spi_miso; - uint32_t spi_mosi; - uint16_t spi_sclk_speed; -// uint32_t spi_clock_ctrl; - uint32_t spi_clock_speed_hz; -} SPIBus; - -typedef enum SPISlavePortDMAState { - SPISlavePortDMAState_Idle, - SPISlavePortDMAState_Read, - SPISlavePortDMAState_Write, - SPISlavePortDMAState_ReadWrite, - SPISlavePortDMAState_ReadWriteOneInterrupt, -} SPISlavePortDMAState; - -typedef struct SPISlavePortState { - bool initialized; - bool acquired; - bool scs_selected; - SPIDMACompleteHandler dma_complete_handler; - void *dma_complete_context; - SPISlavePortDMAState dma_state; -} SPISlavePortState; - -typedef const struct SPISlavePort { - SPISlavePortState *slave_state; - SPIBus *spi_bus; - OutputConfig spi_scs; - SpiDirection spi_direction; - SpiCPol spi_cpol; - SpiCPha spi_cpha; - SpiFirstBit spi_first_bit; -} SPISlavePort; diff --git a/src/fw/drivers/nrf5/uart.c b/src/fw/drivers/nrf5/uart.c deleted file mode 100644 index 9414fed968..0000000000 --- a/src/fw/drivers/nrf5/uart.c +++ /dev/null @@ -1,367 +0,0 @@ -#include "uart_definitions.h" -#include "drivers/uart.h" - -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "system/passert.h" - -#include "FreeRTOS.h" - -#define NRF5_COMPATIBLE -#include -#include - -#include -#include -#ifdef NRF_PPI_BASE -#include -#else -#include -#endif - - -// UART: 8n1, duplex - -static void _uart_event_handler(const nrfx_uarte_event_t *event, void *ctx); - -static void _timer_event_handler(nrf_timer_event_t event_type, void *ctx) { } - -void uart_init(UARTDevice *dev) { - nrfx_uarte_config_t config = { - .txd_pin = dev->tx_gpio, - .rxd_pin = dev->rx_gpio, - .rts_pin = dev->rts_gpio, - .cts_pin = dev->cts_gpio, - .p_context = (void *)dev, - .tx_cache = { .p_buffer = (uint8_t *) dev->state->tx_cache_buffer, .length = sizeof(dev->state->tx_cache_buffer) }, - .rx_cache = { .p_buffer = (uint8_t *) dev->state->rx_cache_buffer, .length = sizeof(dev->state->rx_cache_buffer) }, - .baudrate = NRF_UARTE_BAUDRATE_1000000, - .config = { - .hwfc = NRF_UARTE_HWFC_DISABLED, - .parity = NRF_UARTE_PARITY_EXCLUDED, - .stop = NRF_UARTE_STOP_ONE - }, - .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, - }; - - nrfx_err_t err = nrfx_uarte_init(&dev->periph, &config, _uart_event_handler); - PBL_ASSERTN(err == NRFX_SUCCESS); - - /* Roughly patterned off of https://devzone.nordicsemi.com/f/nordic-q-a/28420/uarte-in-circular-mode */ - nrfx_timer_config_t tconfig = { - .frequency = 1000000, /* dummy */ - .mode = NRF_TIMER_MODE_COUNTER, - .bit_width = NRF_TIMER_BIT_WIDTH_32, - .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, - .p_context = NULL, - }; - err = nrfx_timer_init(&dev->counter, &tconfig, _timer_event_handler); - PBL_ASSERTN(err == NRFX_SUCCESS); - - -#ifdef NRF_PPI_BASE - nrf_ppi_channel_t rxdrdy_count_channel; - nrf_ppi_channel_t endrx_clear_channel; - - err = nrfx_ppi_channel_alloc(&rxdrdy_count_channel); - PBL_ASSERTN(err == NRFX_SUCCESS); - nrfx_ppi_channel_assign(rxdrdy_count_channel, nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_RXDRDY), nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_COUNT)); - nrfx_ppi_channel_enable(rxdrdy_count_channel); - - err = nrfx_ppi_channel_alloc(&endrx_clear_channel); - PBL_ASSERTN(err == NRFX_SUCCESS); - nrfx_ppi_channel_assign(endrx_clear_channel, nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_ENDRX), nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_CLEAR)); - nrfx_ppi_channel_enable(endrx_clear_channel); -#else - uint8_t rxdrdy_count_channel; - uint8_t endrx_clear_channel; - - err = nrfx_dppi_channel_alloc(&rxdrdy_count_channel); - PBL_ASSERTN(err == NRFX_SUCCESS); - NRF_DPPI_ENDPOINT_SETUP(nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_RXDRDY), rxdrdy_count_channel); - NRF_DPPI_ENDPOINT_SETUP(nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_COUNT), rxdrdy_count_channel); - nrfx_dppi_channel_enable(rxdrdy_count_channel); - - err = nrfx_dppi_channel_alloc(&endrx_clear_channel); - PBL_ASSERTN(err == NRFX_SUCCESS); - NRF_DPPI_ENDPOINT_SETUP(nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_ENDRX), endrx_clear_channel); - NRF_DPPI_ENDPOINT_SETUP(nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_CLEAR), endrx_clear_channel); - nrfx_dppi_channel_enable(endrx_clear_channel); -#endif - - - dev->state->initialized = true; -} - -void uart_init_open_drain(UARTDevice *dev) { - WTF; /* unimplemented, for now */ -} - -void uart_init_tx_only(UARTDevice *dev) { - WTF; /* unimplemented, for now */ -} - -void uart_init_rx_only(UARTDevice *dev) { - WTF; /* unimplemented, for now */ -} - -void uart_deinit(UARTDevice *dev) { - nrfx_uarte_uninit(&dev->periph); -} - -void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { - nrf_uarte_baudrate_t baud_cfg = -#define MKBAUD(b) (baud_rate == b) ? NRF_UARTE_BAUDRATE_##b : - MKBAUD(1200) - MKBAUD(2400) - MKBAUD(4800) - MKBAUD(9600) - MKBAUD(14400) - MKBAUD(19200) - MKBAUD(28800) - MKBAUD(31250) - MKBAUD(38400) - MKBAUD(56000) - MKBAUD(57600) - MKBAUD(76800) - MKBAUD(115200) - MKBAUD(230400) - MKBAUD(250000) - MKBAUD(460800) - MKBAUD(921600) - MKBAUD(1000000) - -1; - if (baud_cfg == (nrf_uarte_baudrate_t)-1) - WTF; - nrfx_uarte_config_t config = { - .txd_pin = dev->tx_gpio, - .rxd_pin = dev->rx_gpio, - .rts_pin = dev->rts_gpio, - .cts_pin = dev->cts_gpio, - .p_context = (void *)dev, - .tx_cache = { .p_buffer = (uint8_t *) dev->state->tx_cache_buffer, .length = sizeof(dev->state->tx_cache_buffer) }, - //.rx_cache = { .p_buffer = (uint8_t *) dev->state->rx_cache_buffer, .length = sizeof(dev->state->rx_cache_buffer) }, - .baudrate = baud_cfg, - .config = { - .hwfc = NRF_UARTE_HWFC_DISABLED, - .parity = NRF_UARTE_PARITY_EXCLUDED, - .stop = NRF_UARTE_STOP_ONE - }, - .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, - }; - - nrfx_err_t err = nrfx_uarte_reconfigure(&dev->periph, &config); - if (err != NRFX_SUCCESS) - WTF; -} - - -// Read / Write APIs -//////////////////////////////////////////////////////////////////////////////// - -void uart_write_byte(UARTDevice *dev, uint8_t data) { - /* XXX: nRF5 can run either a PIO UART or a DMA, but not tx-as-PIO / - * rx-as-DMA. we could create our own linked TX buffer, but it is not - * really performance critical for now. so for now we will do a blocking - * send on every byte. */ - nrfx_uarte_tx(&dev->periph, &data, 1, NRFX_UARTE_TX_BLOCKING); -} - -uint8_t uart_read_byte(UARTDevice *dev) { - /* NYI for now, only accessory uses it */ - WTF; - return 0; -} - -UARTRXErrorFlags uart_has_errored_out(UARTDevice *dev) { -#if 0 - uint16_t errors = dev->periph->SR; - UARTRXErrorFlags flags = { - .parity_error = (errors & USART_FLAG_PE) != 0, - .overrun_error = (errors & USART_FLAG_ORE) != 0, - .framing_error = (errors & USART_FLAG_FE) != 0, - .noise_detected = (errors & USART_FLAG_NE) != 0, - }; -#endif - UARTRXErrorFlags flags = {}; - return flags; -} - -bool uart_is_rx_ready(UARTDevice *dev) { - // return dev->periph->SR & USART_SR_RXNE; - /* NYI: used only in dialog_bootrom.c */ - WTF; - return false; -} - -bool uart_has_rx_overrun(UARTDevice *dev) { - // return dev->periph->SR & USART_SR_ORE; - WTF; /* NYI: used only in has_errored_out */ - return false; -} - -bool uart_has_rx_framing_error(UARTDevice *dev) { - // return dev->periph->SR & USART_SR_FE; - WTF; /* NYU: used only in has_errored_out */ - return false; -} - -bool uart_is_tx_ready(UARTDevice *dev) { - // return dev->periph->SR & USART_SR_TXE; - /* NYI for now, only accessory uses it */ - WTF; - return false; -} - -bool uart_is_tx_complete(UARTDevice *dev) { - return true; - //return nrfx_uarte_tx_in_progress(&dev->periph); -} - -void uart_wait_for_tx_complete(UARTDevice *dev) { - while (!uart_is_tx_complete(dev)) continue; -} - - -void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->rx_irq_handler = irq_handler; -} - -void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - WTF; /* accessory only, for now */ - dev->state->tx_irq_handler = irq_handler; -} - -void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - dev->state->rx_int_enabled = enabled; -} - -void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - WTF; -} - -void uart_clear_all_interrupt_flags(UARTDevice *dev) { - WTF; /* only used internally? */ -} - - -// DMA -//////////////////////////////////////////////////////////////////////////////// - -#define DMA_BUFFERS 4 - -#define GET_SUBBUF_P(dev, n) (dev->state->rx_dma_buffer + dev->state->rx_dma_length * (n)) - -static void _uart_event_handler(const nrfx_uarte_event_t *event, void *ctx) { - UARTDevice *dev = (UARTDevice *)ctx; - bool should_context_switch = false; - - switch (event->type) { - case NRFX_UARTE_EVT_RX_BUF_REQUEST: - dev->state->rx_dma_index = (dev->state->rx_dma_index + 1) % DMA_BUFFERS; - nrfx_uarte_rx_buffer_set(&dev->periph, GET_SUBBUF_P(dev, dev->state->rx_dma_index), dev->state->rx_dma_length); -#ifdef DEBUG_UART - PBL_LOG(LOG_LEVEL_INFO, "rxbuf req %p", GET_SUBBUF_P(dev, dev->state->rx_dma_index)); -#endif - break; - case NRFX_UARTE_EVT_RX_BYTE: - /* we'll catch this up in the ring buffer catchup below */ - break; - case NRFX_UARTE_EVT_RX_DONE: { -#ifdef DEBUG_UART - uint8_t *buf = event->data.rx.p_buffer; - PBL_LOG(LOG_LEVEL_INFO, "rxbuf done %p (hopefully %p)", buf, GET_SUBBUF_P(dev, dev->state->rx_prod_index)); -#endif - dev->state->rx_prod_index = (dev->state->rx_prod_index + 1) % DMA_BUFFERS; - break; - } - default: - break; - } - - /* catch up on any completed buffers */ - for (; dev->state->rx_cons_index != dev->state->rx_prod_index; dev->state->rx_cons_index = (dev->state->rx_cons_index + 1) % DMA_BUFFERS) { - uint8_t *buf = GET_SUBBUF_P(dev, dev->state->rx_cons_index); - size_t ofs = dev->state->rx_cons_pos; - dev->state->rx_cons_pos = 0; - - if (ofs == dev->state->rx_dma_length) /* already consumed */ - continue; - -#ifdef DEBUG_UART - uint8_t *bufx = buf + ofs; - PBL_LOG(LOG_LEVEL_INFO, "consume complete %p with %lu bytes left: : %02x %02x %02x %02x %02x %02x %02x %02x", buf, dev->state->rx_dma_length - ofs, - bufx[0], bufx[1], bufx[2], bufx[3], bufx[4], bufx[5], bufx[6], bufx[7]); -#endif - - const UARTRXErrorFlags err_flags = {}; /* ignored, for now */ - for (; ofs < dev->state->rx_dma_length; ofs++) { - if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { - should_context_switch |= dev->state->rx_irq_handler(dev, buf[ofs], &err_flags); - } - } - } - - uint32_t curpos = nrfx_timer_capture(&dev->counter, NRF_TIMER_CC_CHANNEL0); - if (dev->state->rx_cons_pos < curpos) { /* if it is greater, then we have wrapped and we will catch it on the completed buffer irq later */ - uint8_t *buf = GET_SUBBUF_P(dev, dev->state->rx_cons_index); - -#ifdef DEBUG_UART - uint8_t *bufx = buf + dev->state->rx_cons_pos; - PBL_LOG(LOG_LEVEL_INFO, "consume %ld bytes: %02x %02x %02x %02x %02x %02x %02x %02x", curpos - dev->state->rx_cons_pos, - bufx[0], bufx[1], bufx[2], bufx[3], bufx[4], bufx[5], bufx[6], bufx[7]); -#endif - - const UARTRXErrorFlags err_flags = {}; /* ignored, for now */ - for (; dev->state->rx_cons_pos < curpos; dev->state->rx_cons_pos++) { - if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { - should_context_switch |= dev->state->rx_irq_handler(dev, buf[dev->state->rx_cons_pos], &err_flags); - } - } - } - - portEND_SWITCHING_ISR(should_context_switch); -} - -void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { - /* the nRF5 model of DMA is sort of annoying. what we do is we split the - * buffer in half, and double-buffer, swapping back and forth while - * triggering the RXSTOP -> RXSTART shortcut; every time we get a RXDRDY, - * we trigger a RXSTOP, eat the old buffer, and open the new buffer. ugh! - */ - PBL_ASSERTN((((uint32_t) buffer) & 3) == 0); -#ifdef DEBUG_UART - PBL_LOG(LOG_LEVEL_INFO, "start_rx_dma"); -#endif - dev->state->rx_dma_buffer = buffer; - dev->state->rx_dma_length = length / DMA_BUFFERS; - if (dev->state->rx_dma_length % 4) - dev->state->rx_dma_length -= dev->state->rx_dma_length % 4; - dev->state->rx_dma_index = 0; - dev->state->rx_prod_index = 0; - dev->state->rx_cons_index = 0; - dev->state->rx_cons_pos = 0; - - nrfx_timer_enable(&dev->counter); - nrfx_timer_clear(&dev->counter); - - nrfx_uarte_rxdrdy_enable(&dev->periph); - nrfx_uarte_rx_buffer_set(&dev->periph, dev->state->rx_dma_buffer, dev->state->rx_dma_length); - nrfx_uarte_rx_enable(&dev->periph, NRFX_UARTE_RX_ENABLE_CONT | NRFX_UARTE_RX_ENABLE_KEEP_FIFO_CONTENT); -} - -void uart_stop_rx_dma(UARTDevice *dev) { -#ifdef DEBUG_UART - PBL_LOG(LOG_LEVEL_INFO, "stop_rx_dma"); -#endif - nrfx_uarte_rx_abort(&dev->periph, true, true); - nrfx_timer_disable(&dev->counter); -} - -void uart_clear_rx_dma_buffer(UARTDevice *dev) { -} diff --git a/src/fw/drivers/nrf5/uart_definitions.h b/src/fw/drivers/nrf5/uart_definitions.h deleted file mode 100644 index 2733b82725..0000000000 --- a/src/fw/drivers/nrf5/uart_definitions.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "drivers/uart.h" - -#include "board/board.h" - -#include -#include - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" -#include -#include -#pragma GCC diagnostic pop - -typedef struct UARTState { - bool initialized; - UARTRXInterruptHandler rx_irq_handler; - UARTTXInterruptHandler tx_irq_handler; - bool rx_int_enabled; - bool tx_int_enabled; - bool rx_done_pending; - uint8_t *rx_dma_buffer; - uint32_t rx_dma_length; - uint32_t rx_dma_index; - uint32_t rx_prod_index; - uint32_t rx_cons_index; - uint32_t rx_cons_pos; - uint32_t tx_cache_buffer[8]; - uint32_t rx_cache_buffer[8]; - } UARTDeviceState; - -typedef const struct UARTDevice { - UARTDeviceState *state; - bool half_duplex; - uint32_t tx_gpio; - uint32_t rx_gpio; - uint32_t rts_gpio; - uint32_t cts_gpio; - nrfx_uarte_t periph; - nrfx_timer_t counter; -} UARTDevice; - -// thinly wrapped by the IRQ handler in board_*.c -void uart_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/nrf5/watchdog.c b/src/fw/drivers/nrf5/watchdog.c deleted file mode 100644 index 323e20d5af..0000000000 --- a/src/fw/drivers/nrf5/watchdog.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "drivers/watchdog.h" - -#include "util/bitset.h" -#include "system/logging.h" - -#define NRF5_COMPATIBLE -#include - -#include -#include -#include - -#include - -void watchdog_init(void) { - nrf_wdt_reload_request_enable(NRF_WDT, NRF_WDT_RR0); - /* WDT expiration: 8s */ - nrf_wdt_reload_value_set(NRF_WDT, 32768 * 8); -} - -void watchdog_start(void) { - nrf_wdt_task_trigger(NRF_WDT, NRF_WDT_TASK_START); -} - -void watchdog_feed(void) { - nrf_wdt_reload_request_set(NRF_WDT, NRF_WDT_RR0); -} - -bool watchdog_check_reset_flag(void) { - return (nrfx_reset_reason_get() & NRFX_RESET_REASON_DOG_MASK) != 0; -} - -McuRebootReason watchdog_clear_reset_flag(void) { - uint32_t reason = nrfx_reset_reason_get(); - nrfx_reset_reason_clear(0xFFFFFFFF); - - McuRebootReason mcu_reboot_reason = { - .brown_out_reset = 0, - .pin_reset = (reason & NRFX_RESET_REASON_RESETPIN_MASK) != 0, - .power_on_reset = (reason & NRFX_RESET_REASON_VBUS_MASK) != 0, - .software_reset = (reason & NRFX_RESET_REASON_SREQ_MASK) != 0, - .independent_watchdog_reset = (reason & NRFX_RESET_REASON_DOG_MASK) != 0, - .window_watchdog_reset = 0, - .low_power_manager_reset = 0, - }; - - return mcu_reboot_reason; -} diff --git a/src/fw/drivers/otp.h b/src/fw/drivers/otp.h index 37b160e737..4fbc33897f 100644 --- a/src/fw/drivers/otp.h +++ b/src/fw/drivers/otp.h @@ -1,61 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include -#if PLATFORM_TINTIN || PLATFORM_SNOWY || PLATFORM_SPALDING -enum { - // ML/FL / 1.0 and later: - OTP_SERIAL1 = 0, - OTP_HWVER1 = 1, - OTP_PCBA_SERIAL1 = 2, - // Quanta / HW 1.3 and later: - OTP_SERIAL2 = 3, - OTP_SERIAL3 = 4, - OTP_SERIAL4 = 5, - OTP_SERIAL5 = 6, - OTP_PCBA_SERIAL2 = 7, - OTP_PCBA_SERIAL3 = 8, - - NUM_OTP_SLOTS = 16, -}; -#elif PLATFORM_SILK || PLATFORM_CALCULUS || PLATFORM_ROBERT -enum { - OTP_HWVER1 = 0, - OTP_HWVER2 = 1, - OTP_HWVER3 = 2, - OTP_HWVER4 = 3, - OTP_HWVER5 = 4, - - OTP_SERIAL1 = 5, - OTP_SERIAL2 = 6, - OTP_SERIAL3 = 7, - OTP_SERIAL4 = 8, - OTP_SERIAL5 = 9, - - OTP_PCBA_SERIAL1 = 10, - OTP_PCBA_SERIAL2 = 11, - OTP_PCBA_SERIAL3 = 12, - - NUM_OTP_SLOTS = 16, -}; -#elif PLATFORM_ASTERIX || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) || defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_FLINT) || defined(CONFIG_BOARD_QEMU_GABBRO) enum { OTP_HWVER = 0, OTP_SERIAL = 1, diff --git a/src/fw/drivers/otp/Kconfig b/src/fw/drivers/otp/Kconfig new file mode 100644 index 0000000000..36a5c721ce --- /dev/null +++ b/src/fw/drivers/otp/Kconfig @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig OTP + bool "OTP Storage" + help + One-Time Programmable storage for serial numbers, HW revision, etc. + +if OTP + +config OTP_FLASH + bool "Flash security registers" + help + OTP backed by SPI NOR flash security registers + (e.g. GD25 / GD25LQ family). Driver: otp_flash.c. + +endif # OTP diff --git a/src/fw/drivers/otp/otp_flash.c b/src/fw/drivers/otp/otp_flash.c new file mode 100644 index 0000000000..ee91878a7f --- /dev/null +++ b/src/fw/drivers/otp/otp_flash.c @@ -0,0 +1,108 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/otp.h" +#include "drivers/flash.h" + +#define FLASH_ERASE_VAL 0xFFU +#define OTP_SLOT_SIZE 32U +#define SEC_REG_IDX 0x0U + +bool cd_flash_active(void); +status_t cd_flash_read_security_register(uint32_t addr, uint8_t *val); +status_t cd_flash_security_register_is_locked(uint32_t addr, bool *locked); + +static char s_slot[NUM_OTP_SLOTS][OTP_SLOT_SIZE]; + +char * otp_get_slot(const uint8_t index) { + const FlashSecurityRegisters *info; + status_t ret; + + if (index >= NUM_OTP_SLOTS) { + return NULL; + } + + info = flash_security_registers_info(); + if (info->num_sec_regs == 0U) { + return NULL; + } + + for (uint8_t i = 0U; i < OTP_SLOT_SIZE; i++) { + if (cd_flash_active()) { + ret = cd_flash_read_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, + (uint8_t *)&s_slot[index][i]); + } else { + ret = flash_read_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, + (uint8_t *)&s_slot[index][i]); + } + if (ret != S_SUCCESS) { + return NULL; + } + } + + return s_slot[index]; +} + +uint8_t * otp_get_lock(const uint8_t index) { + return NULL; +} + +bool otp_is_locked(const uint8_t index) { + const FlashSecurityRegisters *info; + status_t ret; + bool locked; + + info = flash_security_registers_info(); + if (info->num_sec_regs == 0U) { + return false; + } + + if (cd_flash_active()) { + ret = cd_flash_security_register_is_locked(info->sec_regs[SEC_REG_IDX], &locked); + } else { + ret = flash_security_register_is_locked(info->sec_regs[SEC_REG_IDX], &locked); + } + if (ret != S_SUCCESS) { + return false; + } + + return locked; +} + +OtpWriteResult otp_write_slot(const uint8_t index, const char *value) { + const FlashSecurityRegisters *info; + char *existing_val; + status_t ret; + size_t len; + + if (index >= NUM_OTP_SLOTS) { + return OtpWriteFailCorrupt; + } + + len = strlen(value); + if (len >= OTP_SLOT_SIZE) { + return OtpWriteFailCorrupt; + } + + existing_val = otp_get_slot(index); + for (size_t i = 0U; i < OTP_SLOT_SIZE; i++) { + if ((uint8_t)existing_val[i] != FLASH_ERASE_VAL) { + return OtpWriteFailAlreadyWritten; + } + } + + info = flash_security_registers_info(); + if (info->num_sec_regs == 0U) { + return OtpWriteFailCorrupt; + } + + for (size_t i = 0U; i <= len; i++) { + ret = flash_write_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, + (uint8_t)value[i]); + if (ret != S_SUCCESS) { + return OtpWriteFailCorrupt; + } + } + + return OtpWriteSuccess; +} diff --git a/src/fw/drivers/otp/wscript_build b/src/fw/drivers/otp/wscript_build new file mode 100644 index 0000000000..f70aaea27b --- /dev/null +++ b/src/fw/drivers/otp/wscript_build @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes'] + +if bld.env.CONFIG_OTP_FLASH: + sources.append('otp_flash.c') + use.append('driver_flash') + +bld.objects( + name='driver_otp', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_otp') diff --git a/src/fw/drivers/otp_flash.c b/src/fw/drivers/otp_flash.c deleted file mode 100644 index 24a45a4bae..0000000000 --- a/src/fw/drivers/otp_flash.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/otp.h" -#include "drivers/flash.h" - -#define FLASH_ERASE_VAL 0xFFU -#define OTP_SLOT_SIZE 32U -#define SEC_REG_IDX 0x0U - -bool cd_flash_active(void); -status_t cd_flash_read_security_register(uint32_t addr, uint8_t *val); -status_t cd_flash_security_registers_are_locked(bool *locked); - -static char s_slot[NUM_OTP_SLOTS][OTP_SLOT_SIZE]; - -char * otp_get_slot(const uint8_t index) { - const FlashSecurityRegisters *info; - status_t ret; - - if (index >= NUM_OTP_SLOTS) { - return NULL; - } - - info = flash_security_registers_info(); - if (info->num_sec_regs == 0U) { - return NULL; - } - - for (uint8_t i = 0U; i < OTP_SLOT_SIZE; i++) { - if (cd_flash_active()) { - ret = cd_flash_read_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, - (uint8_t *)&s_slot[index][i]); - } else { - ret = flash_read_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, - (uint8_t *)&s_slot[index][i]); - } - if (ret != S_SUCCESS) { - return NULL; - } - } - - return s_slot[index]; -} - -uint8_t * otp_get_lock(const uint8_t index) { - return NULL; -} - -bool otp_is_locked(const uint8_t index) { - status_t ret; - bool locked; - - if (cd_flash_active()) { - ret = cd_flash_security_registers_are_locked(&locked); - } else { - ret = flash_security_registers_are_locked(&locked); - } - if (ret != S_SUCCESS) { - return false; - } - - return locked; -} - -OtpWriteResult otp_write_slot(const uint8_t index, const char *value) { - const FlashSecurityRegisters *info; - char *existing_val; - status_t ret; - size_t len; - - if (index >= NUM_OTP_SLOTS) { - return OtpWriteFailCorrupt; - } - - len = strlen(value); - if (len >= OTP_SLOT_SIZE) { - return OtpWriteFailCorrupt; - } - - existing_val = otp_get_slot(index); - for (size_t i = 0U; i < OTP_SLOT_SIZE; i++) { - if ((uint8_t)existing_val[i] != FLASH_ERASE_VAL) { - return OtpWriteFailAlreadyWritten; - } - } - - info = flash_security_registers_info(); - if (info->num_sec_regs == 0U) { - return OtpWriteFailCorrupt; - } - - for (size_t i = 0U; i <= len; i++) { - ret = flash_write_security_register(info->sec_regs[SEC_REG_IDX] + index * OTP_SLOT_SIZE + i, - (uint8_t)value[i]); - if (ret != S_SUCCESS) { - return OtpWriteFailCorrupt; - } - } - - return OtpWriteSuccess; -} diff --git a/src/fw/drivers/periph_config.h b/src/fw/drivers/periph_config.h index 80b6c75b04..67b2a8a628 100644 --- a/src/fw/drivers/periph_config.h +++ b/src/fw/drivers/periph_config.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/pmic.h b/src/fw/drivers/pmic.h index e3ba85953a..b4ee805e7b 100644 --- a/src/fw/drivers/pmic.h +++ b/src/fw/drivers/pmic.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/pmic/Kconfig b/src/fw/drivers/pmic/Kconfig new file mode 100644 index 0000000000..e06f6aeaf6 --- /dev/null +++ b/src/fw/drivers/pmic/Kconfig @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PMIC + bool "PMIC" + help + PMIC Drivers + +if PMIC + +config PMIC_NPM1300 + bool "Nordic nPM1300" + help + Support for the Nordic nPM1300 PMIC. + +module = DRIVER_PMIC +module-str = PMIC +source "src/fw/Kconfig.template.log_level" + +endif # PMIC diff --git a/src/fw/drivers/pmic/as3701b.c b/src/fw/drivers/pmic/as3701b.c deleted file mode 100644 index e13a849af1..0000000000 --- a/src/fw/drivers/pmic/as3701b.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/pmic.h" - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/battery.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/periph_config.h" -#include "kernel/events.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "stm32f4xx_rcc.h" -#include "stm32f4xx_gpio.h" -#include "stm32f4xx_adc.h" - -static TimerID s_debounce_charger_timer = TIMER_INVALID_ID; -#define CHARGER_DEBOUNCE_MS 400 - -//! Remember GPIO output states so we can change the state of individual GPIOs -//! without having to do a read-modify-write. -static uint8_t s_pmic_gpio_output_state = 0; - -typedef enum PmicGpio { - PmicGpio1 = (1 << 0), - PmicGpio2 = (1 << 1), - PmicGpio3 = (1 << 2), - PmicGpio4 = (1 << 3), - PmicGpio5 = (1 << 4), -} PmicGpio; - -typedef enum { - PmicRegisters_SD1_VOLTAGE = 0x01, - PmicRegisters_LDO1_VOLTAGE = 0x02, - PmicRegisters_LDO2_VOLTAGE = 0x03, - - PmicRegisters_GPIO1_CNTL = 0x09, - PmicRegisters_GPIO2_CNTL = 0x0a, - PmicRegisters_GPIO3_CNTL = 0x0b, - PmicRegisters_GPIO4_CNTL = 0x0c, - PmicRegisters_GPIO5_CNTL = 0x0d, - PmicRegisters_GPIO_SIG_OUT = 0x20, - PmicRegisters_GPIO_SIG_IN = 0x21, - - PmicRegisters_REG1_VOLTAGE = 0x22, - PmicRegisters_REG2_VOLTAGE = 0x23, - PmicRegisters_REG_CNTL = 0x24, - - PmicRegisters_GPIO_CNTL1 = 0x25, - PmicRegisters_GPIO_CNTL2 = 0x26, - PmicRegisters_SD_CNTL1 = 0x30, - - PmicRegisters_BATT_VOLTAGE_MON = 0x32, - PmicRegisters_STARTUP_CNTL = 0x33, - PmicRegisters_REFERENCE_CNTL = 0x35, - PmicRegisters_RESET_CNTL = 0x36, - PmicRegisters_OVERTEMP_CNTL = 0x37, - PmicRegisters_REG_STANDBY_MOD1 = 0x39, - - PmicRegisters_PWM_CNTL_L = 0x41, - PmicRegisters_PWM_CNTL_H = 0x42, - - PmicRegisters_CURR1_VAL = 0x43, - PmicRegisters_CURR2_VAL = 0x44, - - PmicRegisters_REG_STATUS = 0x73, - PmicRegisters_INT_MASK_1 = 0x74, - PmicRegisters_INT_MASK_2 = 0x75, - PmicRegisters_INT_STATUS_1 = 0x77, - PmicRegisters_INT_STATUS_2 = 0x78, - PmicRegisters_CHARGE_CNTL = 0x80, - PmicRegisters_CHARGE_VOLTAGE_CNTL = 0x81, - PmicRegisters_CHARGE_CURRENT_CNTL = 0x82, - PmicRegisters_CHARGE_CONFIG_1 = 0x83, - PmicRegisters_CHARGE_CONFIG_2 = 0x84, - PmicRegisters_CHARGE_SUPERVISION = 0x85, - PmicRegisters_CHARGE_STATUS_1 = 0x86, - PmicRegisters_CHARGE_STATUS_2 = 0x87, - - PmicRegisters_LOCK_REG = 0x8e, - - PmicRegisters_CHIP_ID = 0x90, - PmicRegisters_CHIP_REV = 0x91, - - PmicRegisters_FUSE_5 = 0xa5, - PmicRegisters_FUSE_6 = 0xa6, - PmicRegisters_FUSE_7 = 0xa7, - PmicRegisters_FUSE_8 = 0xa8, - PmicRegisters_FUSE_9 = 0xa9, - PmicRegisters_FUSE_10 = 0xaa, - PmicRegisters_FUSE_11 = 0xab, - PmicRegisters_FUSE_12 = 0xac, - PmicRegisters_FUSE_13 = 0xad, - PmicRegisters_FUSE_14 = 0xae, - PmicRegisters_FUSE_15 = 0xaf, -} PmicRegisters; - -static const PmicRegisters s_registers[] = { - 0x01, 0x02, 0x03, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x20, 0x21, - 0x22, 0x23, 0x24, 0x25, 0x26, 0x30, 0x32, 0x33, 0x35, 0x36, - 0x37, 0x39, 0x41, 0x42, 0x43, 0x44, 0x73, 0x74, 0x75, 0x77, - 0x78, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x8e, - 0x90, 0x91, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, - 0xad, 0xae, 0xaf, -}; - -static bool prv_init_gpio(void) { - periph_config_acquire_lock(); - - // Init PMIC_INTN - gpio_input_init_pull_up_down(&BOARD_CONFIG_POWER.pmic_int_gpio, GPIO_PuPd_UP); - - periph_config_release_lock(); - return true; -} - -//! Interrupt masks for InterruptStatus1 and InterruptMask1 registers -enum PmicInt1 { - PmicInt1_Trickle = (1 << 0), //!< Trickle charge - PmicInt1_NoBat = (1 << 1), //!< Battery detached - PmicInt1_Resume = (1 << 2), //!< Resuming charge on drop after full - PmicInt1_EOC = (1 << 3), //!< End of charge - PmicInt1_ChDet = (1 << 4), //!< Charger detected - PmicInt1_OnKey = (1 << 5), //!< On Key held - PmicInt1_OvTemp = (1 << 6), //!< Set when 110deg is exceeded - PmicInt1_LowBat = (1 << 7), //!< Low Battery detected. Set when BSUP drops below ResVoltFall -}; - -enum PmicRail { - PmicRail_SD1, //!< 1.8V - PmicRail_LDO1, //!< 3.0V - PmicRail_LDO2, //!< 2.0V -}; - -#define AS3701B_CHIP_ID 0x11 - -static bool prv_read_register(uint8_t register_address, uint8_t *result) { - i2c_use(I2C_AS3701B); - bool rv = i2c_read_register(I2C_AS3701B, register_address, result); - i2c_release(I2C_AS3701B); - return rv; -} - -static bool prv_write_register(uint8_t register_address, uint8_t value) { - i2c_use(I2C_AS3701B); - bool rv = i2c_write_register(I2C_AS3701B, register_address, value); - i2c_release(I2C_AS3701B); - return rv; -} - -static bool prv_set_register_bit(uint8_t register_address, uint8_t bit, bool enable) { - uint8_t val; - if (!prv_read_register(register_address, &val)) { - return false; - } - if (enable) { - val |= (1 << bit); - } else { - val &= ~(1 << bit); - } - return prv_write_register(register_address, val); -} - -static bool prv_set_pmic_gpio_outputs(PmicGpio set_mask, PmicGpio clear_mask) { - PBL_ASSERTN((set_mask & clear_mask) == 0); - uint8_t new_output_state = s_pmic_gpio_output_state; - new_output_state |= set_mask; - new_output_state &= ~clear_mask; - if (prv_write_register(PmicRegisters_GPIO_SIG_OUT, new_output_state)) { - s_pmic_gpio_output_state = new_output_state; - return true; - } - return false; -} - -static void prv_init_pmic_gpio_outputs(void) { - // Sync the state of the PMIC GPIO output register with the value that we - // think it has. - if (!prv_set_pmic_gpio_outputs(0, 0)) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not initialize PMIC GPIO outputs"); - } -} - -static void prv_handle_charge_state_change(void *null) { - const bool is_charging = pmic_is_charging(); - const bool is_connected = pmic_is_usb_connected(); - PBL_LOG(LOG_LEVEL_DEBUG, "AS3701b Interrupt: Charging? %s Plugged? %s", - is_charging ? "YES" : "NO", is_connected ? "YES" : "NO"); - - PebbleEvent event = { - .type = PEBBLE_BATTERY_CONNECTION_EVENT, - .battery_connection = { - .is_connected = battery_is_usb_connected(), - }, - }; - event_put(&event); -} - -// Read the interrupt status registers to clear pending bits. -static void prv_clear_pending_interrupts(void) { - uint8_t throwaway_read_result; - prv_read_register(PmicRegisters_INT_STATUS_1, &throwaway_read_result); - prv_read_register(PmicRegisters_INT_STATUS_2, &throwaway_read_result); -} - -static void prv_pmic_state_change_cb(void *null) { - prv_clear_pending_interrupts(); - new_timer_start(s_debounce_charger_timer, CHARGER_DEBOUNCE_MS, - prv_handle_charge_state_change, NULL, 0 /*flags*/); -} - -static void prv_as3701b_interrupt_handler(bool *should_context_switch) { - system_task_add_callback_from_isr(prv_pmic_state_change_cb, NULL, should_context_switch); -} - -static void prv_configure_interrupts(void) { - // Clear pending interrupts in case we were woken from standby - prv_clear_pending_interrupts(); - - exti_configure_pin( - BOARD_CONFIG_POWER.pmic_int, ExtiTrigger_Falling, prv_as3701b_interrupt_handler); - exti_enable(BOARD_CONFIG_POWER.pmic_int); - - const uint8_t mask = (uint8_t) ~(PmicInt1_LowBat | PmicInt1_ChDet | PmicInt1_EOC); - prv_write_register(PmicRegisters_INT_MASK_1, mask); - prv_write_register(PmicRegisters_INT_MASK_2, ~0); -} - -// Set up 160Hz clock which is used for VCOM. -// This setting is a divisor of 16 and a high/low duration of 195us, as -// given in the following: 1000000 / (16 * 195 * 2) = ~160Hz -static void prv_start_160hz_clock(void) { - const uint8_t pwm_high_low_time_us = (195 - 1); - prv_write_register(PmicRegisters_PWM_CNTL_H, pwm_high_low_time_us); - prv_write_register(PmicRegisters_PWM_CNTL_L, pwm_high_low_time_us); - - bool success = false; - uint8_t ref_cntl; - if (prv_read_register(PmicRegisters_REFERENCE_CNTL, &ref_cntl)) { - ref_cntl |= 0x3; // Divisor of 16 - prv_write_register(PmicRegisters_REFERENCE_CNTL, ref_cntl); - - // Enable PWM Output on GPIO2 (Fig. 64) - // Bits 6-4: Mode, 0x1 = Output - // Bits 0-3: iosf, 0xe = PWM - uint8_t val = (1 << 4) | 0x0e; - success = prv_write_register(PmicRegisters_GPIO2_CNTL, val); - } - PBL_ASSERT(success, "Failed to start PMIC 120Hz PWM"); -} - -static void prv_configure_charging(void) { - // Set charge control to low current range, constant current ctl to 118mA. - bool success = false; - if (prv_set_register_bit(PmicRegisters_CHARGE_CNTL, 7, true)) { - uint8_t cntl; - if (prv_read_register(PmicRegisters_CHARGE_CURRENT_CNTL, &cntl)) { - cntl = (cntl & 0xf0) | 0x09; // 118mA when cc_range_select = 1 - success = prv_write_register(PmicRegisters_CHARGE_CURRENT_CNTL, cntl); - } - } - if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not set pmic charge current."); - } - - // Set EOC current to 5% of ConstantCurrent - prv_set_register_bit(PmicRegisters_CHARGE_CONFIG_2, 5, false); - - if (BOARD_CONFIG_POWER.charging_cutoff_voltage == 4300) { - // Set EOC to 4.30V, keep Vsup_min at 4.20V - // EOC = 3.82V + 0.02V * N - prv_write_register(PmicRegisters_CHARGE_VOLTAGE_CNTL, 0x18 | (1 << 6)); - } - - pmic_set_charger_state(true); - - // Enable AutoResume: Resumes charging on voltage drop after EOC - prv_set_register_bit(PmicRegisters_CHARGE_CNTL, 6, true); -} - -static void prv_configure_battery_measure(void) { - // Set PMIC GPIO5 (the battery measure enable pin) as an open-drain output - // with no pull and inverted output. Setting the output to 1 will drive GPIO5 - // low, and setting it to 0 will cause it to float. - bool success = prv_write_register(PmicRegisters_GPIO5_CNTL, 0b10100000) && - prv_set_pmic_gpio_outputs(0, PmicGpio5); - if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not configure the battery measure control GPIO"); - } -} - -static bool prv_is_alive(void) { - uint8_t chip_id; - if (!prv_read_register(PmicRegisters_CHIP_ID, &chip_id)) { - return false; - } - const bool found = (chip_id == AS3701B_CHIP_ID); - if (found) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found the as3701b"); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Error: read as3701b whoami byte 0x%x, expecting 0x11", chip_id); - } - return found; -} - -static void prv_set_sd1_voltage(void) { - // STM32F4 running at 1.76V may trigger a Power Down Reset (PDR). The power supply has a - // tolerance of 3%. Set the voltage rail to 1.825V so our theoretical minimum should be 1.77V - uint8_t sd1_vsel; - if (prv_read_register(PmicRegisters_SD1_VOLTAGE, &sd1_vsel)) { - const uint8_t sd1_vsel_mask = 0x3f; // sd1_vsel is in first 6 bits - sd1_vsel &= ~sd1_vsel_mask; - // V_SD1 = 1.4V + (sd1_vsel - 0x40) * 25mV = 1.4V + (0x51 - 0x40) * 25mV = 1.825V - sd1_vsel |= (0x51 & sd1_vsel_mask); - prv_write_register(PmicRegisters_SD1_VOLTAGE, sd1_vsel); - } -} - -static uint8_t s_last_reset_reason = 0; -static void prv_stash_last_reset_reason(void) { - uint8_t reset_cntl; - if (!prv_read_register(PmicRegisters_RESET_CNTL, &reset_cntl)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read the RESET_CNTL register"); - return; - } - - s_last_reset_reason = reset_cntl >> 4; -} - -uint32_t pmic_get_last_reset_reason(void) { - return s_last_reset_reason; -} - -bool pmic_init(void) { - s_debounce_charger_timer = new_timer_create(); - - prv_init_gpio(); - if (!prv_is_alive()) { - return false; - } - prv_stash_last_reset_reason(); - prv_init_pmic_gpio_outputs(); - prv_set_sd1_voltage(); - - prv_start_160hz_clock(); - - prv_configure_battery_measure(); - prv_configure_interrupts(); - prv_configure_charging(); - - // Override OTP setting for 'onkey_lpress_ reset=1' so that we shutdown instead of triggering a - // reset on a long button hold - prv_set_register_bit(PmicRegisters_REFERENCE_CNTL, 5, false); - - return true; -} - -// On the as3701b, a power_off will cut power to all rails. We want to keep the -// RTC alive, so rather than performing a sw_power_off, enter the pmic's standby -// mode, powering down all but LDO2. -bool pmic_power_off(void) { - // Only enable interrupts that should be able to wake us out of standby - // - Wake on charger detect - const uint8_t int_mask = (uint8_t)~(PmicInt1_ChDet); - prv_write_register(PmicRegisters_INT_MASK_1, int_mask); - prv_write_register(PmicRegisters_INT_MASK_2, 0xff); - - // Clear interrupt status so we're not woken immediately (read the regs) - prv_clear_pending_interrupts(); - - // Set Reg_Standby_mod1 to specify which rails to turn off / keep on - // - SD1, LDO1 off - // - LDO2 on - // - Disable regulator pulldowns - prv_write_register(PmicRegisters_REG_STANDBY_MOD1, 0xa); - - // Set standby_mode_on (bit 4) in ReferenceControl to 1 (See Fig. 78) - if (prv_set_register_bit(PmicRegisters_REFERENCE_CNTL, 4, true)) { - while (1) {} - __builtin_unreachable(); - } - return false; -} - -// This is a hard power off, resulting in all rails being disabled. -// Generally, this is not desirable since we'll lose the backup domain. -// You're *probably* looking for pmic_power_off. -bool pmic_full_power_off(void) { - // ResetControl (Fig. 79) - // Bit 1: power_off - Start a reset cycle, and wait for ON or charger to complete the reset. - if (prv_set_register_bit(PmicRegisters_RESET_CNTL, 1, true)) { - while (1) {} - __builtin_unreachable(); - } - return false; -} - -// We have no way of directly reading Vsup with as3701b on Silk. Just assume -// that we are getting what we've configured as regulated Vsup. -uint16_t pmic_get_vsys(void) { - uint8_t cfg; - prv_read_register(PmicRegisters_CHARGE_CONFIG_1, &cfg); - const uint8_t vsup_voltage = (cfg & 0x6) >> 1; - switch (vsup_voltage) { - case 0: return 4400; - case 1: return 4500; - case 2: return 4600; - case 3: return 4700; - case 4: return 4800; - case 5: return 4900; - case 6: return 5000; - case 7: return 5500; - } - WTF; -} - -bool pmic_set_charger_state(bool enable) { - // ChargerControl (Fig. 91) - // Bit 5: Enable battery charging from USB charger. - return prv_set_register_bit(PmicRegisters_CHARGE_CNTL, 5, enable); -} - -bool pmic_is_charging(void) { - uint8_t status; - if (!prv_read_register(PmicRegisters_CHARGE_STATUS_1, &status)) { -#if defined (TARGET_QEMU) - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are charging - return true; -#else - PBL_LOG(LOG_LEVEL_DEBUG, "Failed to read charging status 1 register."); - return false; -#endif - } - // ChargerStatus1 (Fig. 97) - // Bit 0: CC - // 1: Maintain / Resume charge - // 2: Trickle charge - // 3: CV - return (status & 0x0f); -} - -bool pmic_is_usb_connected(void) { - uint8_t status; - if (!prv_read_register(PmicRegisters_CHARGE_STATUS_2, &status)) { -#if TARGET_QEMU - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are connected to a USB cable - return true; -#endif - PBL_LOG(LOG_LEVEL_WARNING, "Failed to read charging status 2 register."); - return false; - } - // ChargerStatus2 (Fig. 98) - // Bit 2: Charger detected - return status & (1 << 2); -} - -void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision, uint8_t *buck1_vset) { - prv_read_register(PmicRegisters_CHIP_ID, chip_id); - prv_read_register(PmicRegisters_CHIP_REV, chip_revision); - prv_read_register(PmicRegisters_SD1_VOLTAGE, buck1_vset); -} - -bool pmic_enable_battery_measure(void) { - // GPIO 5 on the pmic driven low is battery measure enable. - return prv_set_pmic_gpio_outputs(PmicGpio5, 0); -} - -bool pmic_disable_battery_measure(void) { - // Set GPIO5 floating to disable battery measure. - return prv_set_pmic_gpio_outputs(0, PmicGpio5); -} - -void set_ldo3_power_state(bool enabled) { -} - -void set_4V5_power_state(bool enabled) { -} - -void set_6V6_power_state(bool enabled) { -} - - -void command_pmic_read_registers(void) { - int reg; - uint8_t val; - char buffer[16]; - for (uint8_t i = 0; i < ARRAY_LENGTH(s_registers); ++i) { - reg = s_registers[i]; - prv_read_register(reg, &val); - prompt_send_response_fmt(buffer, sizeof(buffer), "Reg 0x%02X: 0x%02X", reg, val); - } -} - -void command_pmic_status(void) { - uint8_t id, rev, buck1; - pmic_read_chip_info(&id, &rev, &buck1); - PBL_LOG(LOG_LEVEL_DEBUG, "ID: 0x%"PRIx8" REV: 0x%"PRIx8" BUCK1: 0x%"PRIx8, id, rev, buck1); - bool connected = pmic_is_usb_connected(); - PBL_LOG(LOG_LEVEL_DEBUG, "USB Status: %s", (connected) ? "Connected" : "Disconnected"); - bool charging = pmic_is_charging(); - PBL_LOG(LOG_LEVEL_DEBUG, "Charging? %s", (charging) ? "true" : "false"); -} - -void command_pmic_rails(void) { - // TODO: Implement. -} diff --git a/src/fw/drivers/pmic/max14690_pmic.c b/src/fw/drivers/pmic/max14690_pmic.c deleted file mode 100644 index 45d814af7f..0000000000 --- a/src/fw/drivers/pmic/max14690_pmic.c +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* This file should probably go in the stm32f4 folder */ - -#include "drivers/pmic.h" - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/battery.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/exti.h" -#include "drivers/periph_config.h" -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" -#include "kernel/util/delay.h" -#include "util/size.h" -#include "kernel/util/sleep.h" - -#include "kernel/events.h" -#include "services/common/system_task.h" -#include "services/common/new_timer/new_timer.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include - -//! The addresses of the registers that we can read using i2c -typedef enum PmicRegisters { - PmicRegisters_CHIP_ID = 0x00, - PmicRegisters_CHIP_REV = 0x01, - PmicRegisters_STATUSA = 0x02, - PmicRegisters_STATUSB = 0x03, - PmicRegisters_INTA = 0x05, - PmicRegisters_INTB = 0x06, - PmicRegisters_INT_MASK_A = 0x07, - PmicRegisters_INT_MASK_B = 0x08, - PmicRegisters_CHG_CNTL_A = 0x0A, - PmicRegisters_CHG_CNTL_B = 0x0B, - PmicRegisters_CH_TMR = 0x0C, - PmicRegisters_BUCK1_CONFIG = 0x0D, - PmicRegisters_BUCK1_VSET = 0x0E, - PmicRegisters_BUCK2_CONFIG = 0x0F, - PmicRegisters_LDO1_CONFIG = 0x12, - PmicRegisters_LDO2_CONFIG = 0x14, - PmicRegisters_LDO3_CONFIG = 0x16, - PmicRegisters_MON_CFG = 0x19, - PmicRegisters_HAND_SHK = 0x1D, - PmicRegisters_PWR_CFG = 0x1F -} PmicRegisters; - -//! The different power rails that our PMIC controls -typedef enum PmicRail { - PmicRail_BUCK1, //!< 1.2V - PmicRail_BUCK2, //!< 1.8V - PmicRail_LDO1, //!< 2.0V - Auto - RTC - PmicRail_LDO2, //!< 3.2V - Manual - FPGA - - //! snowy_bb: 2.5V - Manual - MFi, Magnetometer - //! snowy_evt: 1.8V - Manual - MFi - PmicRail_LDO3 -} PmicRail; - -//! Gives configuration information for reading a given rail through the monitor pin. -typedef struct { - const char* name; //!< Name for the rail. - - //! What ratio we need to divide by in order to bring it into the range we can sense. We can - //! only read between 0 and 1.8Vs, so we need to use the PMIC hardware to divide it down before - //! sending it to us. Valid values are 1-4. - uint8_t ratio; - - //! The binary value we need to put in the register to select the rail. - uint8_t source_config; -} PmicMonConfig; - -// Using the Binary constants GCC extension here, supported in GCC and Clang -// https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html -static const PmicMonConfig MON_CONFIG[] = { - { "+VBAT", 3, 0b001 }, // 3:1 - { "+VSYS", 4, 0b010 }, // 4:1 - -// We only care about non-battery rails in MFG where we have the command_pmic_rails function. -#ifdef RECOVERY_FW - { "+1V2", 1, 0b011 }, // 1:1, BUCK1 - { "+1V8", 2, 0b100 }, // 2:1, BUCK2 - { "+2V0_RTC", 2, 0b101 }, // 2:1, LDO1 - { "+3V2", 2, 0b110 }, // 2:1, LDO2 -#ifdef BOARD_SNOWY_BB - { "+2V5", 2, 0b111 }, // 2:1, LDO3 -#else - { "+1V8_MFI_MIC", 2, 0b111 }, // 2:1, LDO3 -#endif // BOARD_SNOWY_BB -#endif // RECOVERY_FW -}; - -//! Mutex to make sure two threads aren't working with the PMIC mon value at the same time. -static PebbleMutex *s_mon_config_mutex; - -static const int PMIC_MON_CONFIG_VBAT_INDEX = 0; -static const int PMIC_MON_CONFIG_VSYS_INDEX = 1; - -//! Debounce timer for USB connections -static TimerID s_debounce_usb_conn_timer = TIMER_INVALID_ID; -static const int USB_CONN_DEBOUNCE_MS = 1000; -static volatile int s_interrupt_bounce_count; - -/* Private Function Definitions */ -static bool prv_is_alive(void); -static bool prv_set_pin_config(void); -static void prv_register_dump(int start_reg, int stop_reg); -static void prv_initialize_interrupts(void); - -//! Request that the rail be used or released. Internally refcounted per rail so you don't have -//! to worry about turning this off on another client. -static bool prv_update_rail_state(PmicRail rail, bool enable); - -static void prv_mon_config_lock(void) { - mutex_lock(s_mon_config_mutex); -} - -static void prv_mon_config_unlock(void) { - mutex_unlock(s_mon_config_mutex); -} - -static bool prv_read_register(uint8_t register_address, uint8_t *result) { - i2c_use(I2C_MAX14690); - bool rv = i2c_read_register(I2C_MAX14690, register_address, result); - i2c_release(I2C_MAX14690); - return (rv); -} - -static bool prv_write_register(uint8_t register_address, uint8_t value) { - i2c_use(I2C_MAX14690); - bool rv = i2c_write_register(I2C_MAX14690, register_address, value); - i2c_release(I2C_MAX14690); - return (rv); -} - -// Configure PMIC's charger settings (different from defaults - -// see https://pebbletechnology.atlassian.net/browse/PBL-15134) -static bool prv_config_charger(void) { - const uint8_t CHARGE_VOLTAGE_4300 = 0b101; - const uint8_t CHARGE_VOLTAGE_4200 = 0b011; - - // [AS] HACK alert! (see PBL-19186) - // The MAX14690 state machine is stupid and kicks us into a charge complete state when the charger - // is connected and the battery voltage is within the range VBATREG < x < VBATREG - VBATRECHG - // (where VBATREG = 4.30V and VBATRECHG = 70mV (previously 170mV) for our setup). This is quite - // a likely situation because the DC internal resistance of the battery is quite high (~1Ω) and - // we reach the termination voltage at around 70% SOC. To workaround this, we set VBATREG to 4.35V - // and VBATRECHG to 70mV, turn the charger off and on again, then configure the charger to our - // desired settings. The PMIC then recovers into a charge state. This will hopefully work for - // most watches. - prv_write_register(PmicRegisters_CHG_CNTL_A, 0xCD); - pmic_set_charger_state(false); - pmic_set_charger_state(true); - - const uint8_t bat_reg = (BOARD_CONFIG_POWER.charging_cutoff_voltage == 4300) ? - CHARGE_VOLTAGE_4300 : CHARGE_VOLTAGE_4200; - uint8_t chg_ctrl_a = 1 << 7 | // 1: Enable Auto-stop (default) - 1 << 6 | // 1: Enable Auto-restart (default) - 0 << 4 | // 0: Set battery recharge threshold to 70mV - bat_reg << 1 | // bat_Reg: Set battery charge complete voltage - 1 << 0; // 1: Enable charger (default) - if (!prv_write_register(PmicRegisters_CHG_CNTL_A, chg_ctrl_a)) { - return false; - } - - uint8_t chg_ctrl_b = 6 << 4 | // 6: Set precharge voltage threshold to 3.00V (default) - 1 << 2 | // 1: Set precharge current to 0.1C - 1 << 0; // 1: Set charge done current to 0.1C (default) - if (!prv_write_register(PmicRegisters_CHG_CNTL_B, chg_ctrl_b)) { - return false; - } - - uint8_t ch_tmr = 1 << 4 | // 1: Set maintain charge timeout to 15 min - 2 << 2 | // 2: Set fast charge timeout to 300 min - 0 << 0; // 0: Set precharge timeout to 30 min - - return prv_write_register(PmicRegisters_CH_TMR, ch_tmr); -} - -/* Public Functions */ - -uint32_t pmic_get_last_reset_reason(void) { - // TODO: Look into if this pmic has a reset reason register which would be useful for debug - return 0; -} - -bool pmic_init(void) { - s_mon_config_mutex = mutex_create(); - - s_debounce_usb_conn_timer = new_timer_create(); - - if (!prv_set_pin_config()) { - return false; - } - - if (!prv_is_alive()) { - return false; - } - - prv_config_charger(); - - prv_initialize_interrupts(); - - prv_update_rail_state(PmicRail_LDO2, true); // FW should bring this up -#if BOARD_ROBERT_BB2 - // On Robert BB2, the BLE chip is behind LDO3, which should always be on. - prv_update_rail_state(PmicRail_LDO3, true); -#endif - - if (BOARD_CONFIG.mfi_reset_pin.gpio) { - // We have access to the reset pin on the MFi. Need to hold it low before powering the 2V5 - // rail in order to get the MFi into a working state. - // In the future if the MFi becomes janky again we can use this to later pull the power. - gpio_use(BOARD_CONFIG.mfi_reset_pin.gpio); - - GPIO_InitTypeDef gpio_cfg; - gpio_cfg.GPIO_OType = GPIO_OType_PP; - gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL; - gpio_cfg.GPIO_Mode = GPIO_Mode_OUT; - gpio_cfg.GPIO_Speed = GPIO_Speed_25MHz; - gpio_cfg.GPIO_Pin = BOARD_CONFIG.mfi_reset_pin.gpio_pin; - GPIO_Init(BOARD_CONFIG.mfi_reset_pin.gpio, &gpio_cfg); - - GPIO_WriteBit(BOARD_CONFIG.mfi_reset_pin.gpio, BOARD_CONFIG.mfi_reset_pin.gpio_pin, Bit_RESET); - - gpio_release(BOARD_CONFIG.mfi_reset_pin.gpio); - } - - return true; -} - -static bool prv_update_rail_state(PmicRail rail, bool enable) { - static int8_t s_ldo2_ref_count = 0; - static int8_t s_ldo3_ref_count = 0; - - int8_t *ref_count; - uint8_t rail_control_reg; - - if (rail == PmicRail_LDO2) { - rail_control_reg = PmicRegisters_LDO2_CONFIG; - ref_count = &s_ldo2_ref_count; - } else if (rail == PmicRail_LDO3) { - rail_control_reg = PmicRegisters_LDO3_CONFIG; - ref_count = &s_ldo3_ref_count; - } else { - WTF; - } - - uint8_t register_value; - bool success = prv_read_register(rail_control_reg, ®ister_value); - - if (!success) { - // Failed to read the current register value - return false; - } - - if (enable) { - if (*ref_count) { - (*ref_count)++; - return true; - } else { - // Set the register byte to XXXXX01X to enable the rail, mask and set - register_value = (register_value & ~0x06) | 0x02; - - success = prv_write_register(rail_control_reg, register_value); - - if (success) { - // We enabled the rail! - *ref_count = 1; - - // We need to wait a bit for the rail to stabilize before continuing to use the device. - // It takes 2.6ms for the LDO rails to ramp. - psleep(3); - - return true; - } - return false; - } - } else { - if (*ref_count <= 1) { - // Set the register byte to XXXXX00X to disable the rail, just mask - register_value = (register_value & ~0x06); - - success = prv_write_register(rail_control_reg, register_value); - - if (success) { - // We disabled the rail! - *ref_count = 0; - return true; - } - return false; - } else { - (*ref_count)--; - return true; - } - } -} - -bool pmic_power_off(void) { - bool ret = prv_write_register(PmicRegisters_PWR_CFG, 0xB2); - - if (ret) { - // Goodbye cruel world. The PMIC should be removing our power at any time now. - - while(1); - __builtin_unreachable(); - } - - return false; -} - -static bool prv_set_mon_config_register(uint8_t value) { - return prv_write_register(PmicRegisters_MON_CFG, value); -} - -static bool prv_set_mon_config(const PmicMonConfig *config) { - const uint8_t ratio_config = 4 - config->ratio; // 4:1 is 0b00, 1:1 is 0b11. - - const uint8_t register_value = (ratio_config << 4) | config->source_config; - bool result = prv_set_mon_config_register(register_value); - - // Need to wait a short period of time for the reading to settle due to capacitance on the line. - delay_us(200); - - return result; -} - -bool pmic_enable_battery_measure(void) { - prv_mon_config_lock(); - - return prv_set_mon_config(&MON_CONFIG[PMIC_MON_CONFIG_VBAT_INDEX]); - - // Don't prv_unlock, we don't want anyone else mucking with the mon config until - // pmic_disable_battery_measure is called. -} - -bool pmic_disable_battery_measure(void) { - bool result = prv_set_mon_config_register(0); - - // Releases the lock that was previously aquired in pmic_enable_battery_measure. - prv_mon_config_unlock(); - - return result; -} - -uint16_t pmic_get_vsys(void) { - prv_mon_config_lock(); - const PmicMonConfig *mon_config = &MON_CONFIG[PMIC_MON_CONFIG_VSYS_INDEX]; - prv_set_mon_config(mon_config); - - ADCVoltageMonitorReading reading = battery_read_voltage_monitor(); - uint32_t millivolts = battery_convert_reading_to_millivolts(reading, mon_config->ratio, 1); - - prv_set_mon_config_register(0); - prv_mon_config_unlock(); - - return (uint16_t)millivolts; -} - -bool pmic_set_charger_state(bool enable) { - // Defaults to ON - // LSB is enable bit - uint8_t register_value; - if (!prv_read_register(PmicRegisters_CHG_CNTL_A, ®ister_value)) { - return false; - } - if (enable) { - register_value |= 0x01; - } else { - register_value &= ~0x01; - } - - bool result = prv_write_register(PmicRegisters_CHG_CNTL_A, register_value); - - return result; -} - - -bool pmic_is_charging(void) { - uint8_t val; - - if (!prv_read_register(PmicRegisters_STATUSA, &val)) { -#if defined (TARGET_QEMU) - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are charging - return true; -#else - PBL_LOG(LOG_LEVEL_DEBUG, "Failed to read charging status A register"); - return false; -#endif - } - - uint8_t chgstat = val & 0x07; // Mask off only charging status - - if (chgstat == 0x02 || // Pre-charge in progress - chgstat == 0x03 || // Fast charge, CC - chgstat == 0x04 || // Fast charge, CV - chgstat == 0x05) { // Maintain charge - return true; - } else { - return false; - } -} - -bool pmic_is_usb_connected(void) { - uint8_t val; - if (!prv_read_register(PmicRegisters_STATUSB, &val)) { -#if defined (TARGET_QEMU) - // NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed - // i2c read means we are connected to a USB cable - return true; -#else - PBL_LOG(LOG_LEVEL_DEBUG, "Failed to read charging status B register"); - return false; -#endif - } - - bool usb_connected = (val >> 3) & 1; - - return usb_connected; -} - -void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision, uint8_t *buck1_vset) { - prv_read_register(PmicRegisters_CHIP_ID, chip_id); - prv_read_register(PmicRegisters_CHIP_REV, chip_revision); - prv_read_register(PmicRegisters_BUCK1_VSET, buck1_vset); -} - -static void prv_clear_any_pending_interrupts(void) { - // Read the Int status registers to clear any pending bits. - // An interrupt wont fire if the matching bit is already set. - uint8_t throwaway_read_result; - prv_read_register(PmicRegisters_INTA, &throwaway_read_result); - prv_read_register(PmicRegisters_INTB, &throwaway_read_result); -} - -static void prv_log_status_registers(const char *preamble) { - uint8_t status_a; - uint8_t status_b; - - if (!prv_read_register(PmicRegisters_STATUSA, &status_a) || - !prv_read_register(PmicRegisters_STATUSB, &status_b)) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to read status registers"); - return; - } - - PBL_LOG(LOG_LEVEL_INFO, "%s: StatusA = 0x%"PRIx8"; StatusB = 0x%"PRIx8, preamble, status_a, - status_b); -} - -static void prv_debounce_callback(void* data) { - bool is_connected = pmic_is_usb_connected(); - - PBL_LOG(LOG_LEVEL_DEBUG, "Got PMIC debounced interrupt, plugged?: %s bounces: %u", - is_connected ? "YES" : "NO", s_interrupt_bounce_count); - s_interrupt_bounce_count = 0; - - if (is_connected) { - // Configure our charging parameters when the charging cable is connected - prv_config_charger(); - prv_log_status_registers("PMIC charger configured after charger connected"); - } else { - prv_log_status_registers("PMIC charge/connection status changed"); - } - - PebbleEvent event = { - .type = PEBBLE_BATTERY_CONNECTION_EVENT, - .battery_connection = { - .is_connected = is_connected, - } - }; - - event_put(&event); -} - -static void prv_handle_pmic_interrupt(void *data) { - prv_clear_any_pending_interrupts(); - - ++s_interrupt_bounce_count; - - new_timer_start(s_debounce_usb_conn_timer, USB_CONN_DEBOUNCE_MS, prv_debounce_callback, - NULL, 0 /*flags*/); -} - -static void pmic_interrupt_handler(bool *should_context_switch) { - system_task_add_callback_from_isr(prv_handle_pmic_interrupt, NULL, should_context_switch); -} - - -/* Private Function Implementations */ -static bool prv_is_alive(void) { - uint8_t val; - prv_read_register(0x00, &val); - - if (val == 0x01) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found the max14690"); - return true; - } else { - PBL_LOG(LOG_LEVEL_DEBUG, - "Error: read max14690 whomai byte 0x%x, expecting 0x%x", val, 0x01); - return false; - } -} - -static bool prv_set_pin_config(void) { - periph_config_acquire_lock(); - - // Initialize the GPIOs for the 4V5 & 6V6 rails - gpio_output_init(&BOARD_CONFIG_POWER.rail_4V5_ctrl, GPIO_OType_OD, GPIO_Speed_50MHz); - if (BOARD_CONFIG_POWER.rail_6V6_ctrl.gpio) { - gpio_output_init(&BOARD_CONFIG_POWER.rail_6V6_ctrl, BOARD_CONFIG_POWER.rail_6V6_ctrl_otype, - GPIO_Speed_50MHz); - } - - // Interrupt config - gpio_input_init_pull_up_down(&BOARD_CONFIG_POWER.pmic_int_gpio, GPIO_PuPd_UP); - - periph_config_release_lock(); - - return true; -} - -static void prv_initialize_interrupts(void) { - exti_configure_pin(BOARD_CONFIG_POWER.pmic_int, ExtiTrigger_Falling, pmic_interrupt_handler); - exti_enable(BOARD_CONFIG_POWER.pmic_int); - - // Enable the UsbOk interrupt in the IntMaskA register - prv_write_register(PmicRegisters_INT_MASK_A, 0x08); - - prv_clear_any_pending_interrupts(); -} - -static void register_dump(int start_reg, int stop_reg) { - int reg; - uint8_t val; - char buffer[16]; - for (reg = start_reg; reg <= stop_reg; reg ++) { - prv_read_register(reg, &val); - prompt_send_response_fmt(buffer, sizeof(buffer), "Reg 0x%02X: 0x%02X", reg, val); - } -} - -void command_pmic_read_registers(void) { - register_dump(0x00, 0x1F); -} - -#ifdef RECOVERY_FW -void command_pmic_rails(void) { - prv_mon_config_lock(); - - // Make sure the LDO3 rail is on before measuring it. - set_ldo3_power_state(true); - - for (size_t i = 0; i < ARRAY_LENGTH(MON_CONFIG); ++i) { - prv_set_mon_config(&MON_CONFIG[i]); - ADCVoltageMonitorReading reading = battery_read_voltage_monitor(); - uint32_t millivolts = battery_convert_reading_to_millivolts(reading, MON_CONFIG[i].ratio, 1); - - char buffer[40]; - prompt_send_response_fmt(buffer, sizeof(buffer), "%-15s: %"PRIu32" mV", - MON_CONFIG[i].name, millivolts); - } - - // Turn this off again now that we're done measuring. This is refcounted so there's no concern - // that we may be turning it off if it was on before we started measuring. - set_ldo3_power_state(false); - - prv_mon_config_unlock(); -} -#endif // RECOVERY_FW - -void set_ldo3_power_state(bool enabled) { - i2c_use(I2C_MAX14690); - prv_update_rail_state(PmicRail_LDO3, enabled); - i2c_release(I2C_MAX14690); -} - -void set_4V5_power_state(bool enabled) { - gpio_output_set(&BOARD_CONFIG_POWER.rail_4V5_ctrl, enabled); -} - -void set_6V6_power_state(bool enabled) { - PBL_ASSERTN(BOARD_CONFIG_POWER.rail_6V6_ctrl.gpio); - gpio_output_set(&BOARD_CONFIG_POWER.rail_6V6_ctrl, enabled); -} - diff --git a/src/fw/drivers/pmic/npm1300.c b/src/fw/drivers/pmic/npm1300.c index 1d863b3753..ff3012070b 100644 --- a/src/fw/drivers/pmic/npm1300.c +++ b/src/fw/drivers/pmic/npm1300.c @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + /* Because nPM1300 also has the battery monitor, we implement both the * pmic_* and the battery_* API here. */ @@ -15,14 +18,19 @@ #include "drivers/periph_config.h" #include "kernel/events.h" #include "kernel/util/delay.h" +#include "kernel/util/sleep.h" #include "os/mutex.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" +PBL_LOG_MODULE_DEFINE(driver_pmic_npm1300, CONFIG_DRIVER_PMIC_LOG_LEVEL); + #define CHARGER_DEBOUNCE_MS 400 +#define ADC_POLL_DELAY_MS 5 // Delay between ADC poll iterations to reduce I2C traffic +#define ADC_POLL_TIMEOUT_MS 100 // Max time to wait for ADC measurement static TimerID s_debounce_charger_timer = TIMER_INVALID_ID; -static PebbleMutex *s_i2c_lock; +static uint32_t s_dischg_limit_ma; typedef enum { PmicRegisters_MAIN_EVENTSADCCLR = 0x0003, @@ -57,11 +65,15 @@ typedef enum { PmicRegisters_BCHARGER_BCHGISETDISCHARGELSB = 0x30B, PmicRegisters_BCHARGER_BCHGVTERM = 0x030CU, PmicRegisters_BCHARGER_BCHGVTERM__BCHGVTERMNORM_4V20 = 0x8U, + PmicRegisters_BCHARGER_BCHGVTERM__BCHGVTERMNORM_4V35 = 0xBU, + PmicRegisters_BCHARGER_BCHGVTERM__BCHGVTERMNORM_4V45 = 0xDU, PmicRegisters_BCHARGER_BCHGVTERMR = 0x030DU, PmicRegisters_BCHARGER_BCHGVTERMR__BCHGVTERMREDUCED_4V00 = 0x4U, PmicRegisters_BCHARGER_BCHGITERMSEL = 0x030F, PmicRegisters_BCHARGER_BCHGITERMSEL__SEL10 = 0U, PmicRegisters_BCHARGER_BCHGITERMSEL__SEL20 = 1U, + PmicRegisters_BCHARGER_NTCHOT = 0x0316U, + PmicRegisters_BCHARGER_NTCHOTLSB = 0x0317U, PmicRegisters_BCHARGER_BCHGCHARGESTATUS = 0x0334, PmicRegisters_BCHARGER_BCHGCHARGESTATUS__COMPLETED = 2, PmicRegisters_BCHARGER_BCHGCHARGESTATUS__TRICKLECHARGE = 4, @@ -104,12 +116,19 @@ typedef enum { PmicRegisters_GPIOS_GPIOPUEN2 = 0x060C, PmicRegisters_GPIOS_GPIOPUEN__EN = 1, PmicRegisters_GPIOS_GPIOPUEN__DIS = 0, + PmicRegisters_GPIOS_GPIOMODE3 = 0x0603, + PmicRegisters_GPIOS_GPIOPUEN3 = 0x060D, PmicRegisters_GPIOS_GPIOOPENDRAIN1 = 0x0615, PmicRegisters_ERRLOG_SCRATCH0 = 0x0E01, PmicRegisters_ERRLOG_SCRATCH1 = 0x0E02, + PmicRegisters_BUCK_BUCK1ENACLR = 0x0401, PmicRegisters_BUCK_BUCK1NORMVOUT = 0x0408, PmicRegisters_BUCK_BUCK2NORMVOUT = 0x040A, PmicRegisters_BUCK_BUCKSWCTRLSEL = 0x040F, + PmicRegisters_BUCK_BUCKSWCTRLSEL__BUCK1SWCTRLSEL_SWCTRL = 0x01, + PmicRegisters_BUCK_BUCKSWCTRLSEL__BUCK2SWCTRLSEL_SWCTRL = 0x02, + PmicRegisters_BUCK_BUCK1VOUTSTATUS = 0x0410, + PmicRegisters_BUCK_BUCK2VOUTSTATUS = 0x0411, PmicRegisters_BUCK_BUCKSTATUS = 0x0434, PmicRegisters_LDSW_TASKLDSW1SET = 0x0800, PmicRegisters_LDSW_TASKLDSW1CLR = 0x0801, @@ -120,6 +139,7 @@ typedef enum { PmicRegisters_LDSW_LDSWCONFIG = 0x0807, PmicRegisters_LDSW_LDSW1LDOSEL = 0x0808, PmicRegisters_LDSW_LDSW2LDOSEL = 0x0809, + PmicRegisters_LDSW_LDSW2LDOSEL__LDSW_MODE = 0, PmicRegisters_LDSW_LDSW2LDOSEL__LDO_MODE = 1, PmicRegisters_LDSW_LDSW1VOUTSEL = 0x080C, PmicRegisters_LDSW_LDSW2VOUTSEL = 0x080D, @@ -146,6 +166,15 @@ typedef enum { #define NPM1300_ADC_MSB_SHIFT 2U #define NPM1300_VBUS_CURRENT_DIVISOR 100U +static bool dischg_limit_ma_set(uint32_t dischg_limit_ma); + +static uint16_t prv_ntc_threshold_code(uint8_t celsius) { + // Ref: PS v1.1 Section 6.2.5: K_NTCTEMP = round(1024 * R_T / (R_T + R_B)) + float t_k = (float)celsius + 273.15f; + float exponent = (float)NPM1300_CONFIG.thermistor_beta * + ((1.f / 298.15f) - (1.f / t_k)); + return (uint16_t)((1024.0f / (1.0f + exp(exponent))) + 0.5f); +} void battery_init(void) { } @@ -155,31 +184,55 @@ uint32_t pmic_get_last_reset_reason(void) { } static bool prv_read_register(uint16_t register_address, uint8_t *result) { - mutex_lock(s_i2c_lock); i2c_use(I2C_NPM1300); uint8_t regad[2] = { register_address >> 8, register_address & 0xFF }; - bool rv = i2c_write_block(I2C_NPM1300, 2, regad); - if (rv) - rv = i2c_read_block(I2C_NPM1300, 1, result); + bool rv = i2c_write_read_block(I2C_NPM1300, 2, regad, 1, result); i2c_release(I2C_NPM1300); - mutex_unlock(s_i2c_lock); return rv; } static bool prv_write_register(uint16_t register_address, uint8_t datum) { - mutex_lock(s_i2c_lock); i2c_use(I2C_NPM1300); uint8_t d[3] = { register_address >> 8, register_address & 0xFF, datum }; bool rv = i2c_write_block(I2C_NPM1300, 3, d); i2c_release(I2C_NPM1300); - mutex_unlock(s_i2c_lock); return rv; } +// Anomaly 27 workaround: when switching BUCKn to SW control, if BUCKnNORMVOUT +// equals the VSET pin value (BUCKnVOUTSTATUS), quiescent current increases by +// 1mA. To avoid this, first set BUCKnNORMVOUT to a different value, switch to +// SW control, then set the desired voltage. +static bool prv_buck_set_sw_ctrl(uint16_t normvout_reg, uint16_t voutstatus_reg, + uint8_t swctrlsel_bit, uint8_t desired_vout) { + uint8_t voutstatus; + if (!prv_read_register(voutstatus_reg, &voutstatus)) { + return false; + } + + // Ensure NORMVOUT differs from VOUTSTATUS before enabling SW control + uint8_t initial_vout = (desired_vout != voutstatus) ? desired_vout : (desired_vout ^ 1); + bool ok = prv_write_register(normvout_reg, initial_vout); + + // Read current SWCTRLSEL and set our bit + uint8_t swctrlsel; + if (!prv_read_register(PmicRegisters_BUCK_BUCKSWCTRLSEL, &swctrlsel)) { + return false; + } + ok &= prv_write_register(PmicRegisters_BUCK_BUCKSWCTRLSEL, swctrlsel | swctrlsel_bit); + + // Now set the actual desired voltage + if (initial_vout != desired_vout) { + ok &= prv_write_register(normvout_reg, desired_vout); + } + + return ok; +} + static void prv_handle_charge_state_change(void *null) { const bool is_charging = pmic_is_charging(); const bool is_connected = pmic_is_usb_connected(); - PBL_LOG(LOG_LEVEL_DEBUG, "nPM1300 Interrupt: Charging? %s Plugged? %s", + PBL_LOG_DBG("nPM1300 Interrupt: Charging? %s Plugged? %s", is_charging ? "YES" : "NO", is_connected ? "YES" : "NO"); if (is_connected && NPM1300_CONFIG.vbus_current_lim0 != 0) { @@ -188,7 +241,7 @@ static void prv_handle_charge_state_change(void *null) { ok &= prv_write_register(PmicRegisters_VBUSIN_TASKUPDATELIMSW, PmicRegisters_VBUSIN_TASKUPDATELIMSW__EN); if (!ok) { - PBL_LOG(LOG_LEVEL_ERROR, "config vbus limite0 failed"); + PBL_LOG_ERR("config vbus limite0 failed"); } } @@ -227,28 +280,22 @@ bool pmic_init(void) { bool ok = true; uint8_t val; - s_i2c_lock = mutex_create(); s_debounce_charger_timer = new_timer_create(); // TODO(NPM1300): This needs to be configurable at board level -#if PLATFORM_ASTERIX - uint8_t buck_out; - if (!prv_read_register(PmicRegisters_BUCK_BUCK1NORMVOUT, &buck_out)) { - PBL_LOG(LOG_LEVEL_ERROR, "failed to read BUCK1NORMVOUT"); - return false; - } - PBL_LOG(LOG_LEVEL_DEBUG, "found the nPM1300, BUCK1NORMVOUT = 0x%x", buck_out); - - // work around erratum 27 for nPM1300 rev1, which we tripped in the bootloader (oops) - ok &= prv_write_register(PmicRegisters_BUCK_BUCK1NORMVOUT, 9 /* 1.9V */); - ok &= prv_write_register(PmicRegisters_BUCK_BUCK2NORMVOUT, 21 /* 3.1V */); - ok &= prv_write_register(PmicRegisters_BUCK_BUCKSWCTRLSEL, 3 /* both of them, load */); - ok &= prv_write_register(PmicRegisters_BUCK_BUCK1NORMVOUT, 8 /* 1.8V */); - ok &= prv_write_register(PmicRegisters_BUCK_BUCK2NORMVOUT, 20 /* 3.0V */); - ok &= prv_write_register(PmicRegisters_BUCK_BUCKSWCTRLSEL, 3 /* both of them, load */); +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + // Anomaly 27: set BUCK1/BUCK2 to SW control with workaround + ok &= prv_buck_set_sw_ctrl(PmicRegisters_BUCK_BUCK1NORMVOUT, + PmicRegisters_BUCK_BUCK1VOUTSTATUS, + PmicRegisters_BUCK_BUCKSWCTRLSEL__BUCK1SWCTRLSEL_SWCTRL, + 8 /* 1.8V */); + ok &= prv_buck_set_sw_ctrl(PmicRegisters_BUCK_BUCK2NORMVOUT, + PmicRegisters_BUCK_BUCK2VOUTSTATUS, + PmicRegisters_BUCK_BUCKSWCTRLSEL__BUCK2SWCTRLSEL_SWCTRL, + 20 /* 3.0V */); if (!prv_read_register(PmicRegisters_LDSW_LDSWSTATUS, &val)) { - PBL_LOG(LOG_LEVEL_ERROR, "failed to read LDSWSTATUS"); + PBL_LOG_ERR("failed to read LDSWSTATUS"); return false; } @@ -262,8 +309,14 @@ bool pmic_init(void) { } #endif -// FIXME(OBELIX): Needs to be configurable at board level -#if PLATFORM_OBELIX +// FIXME(OBELIX,GETAFIX): Needs to be configurable at board level +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + // Anomaly 27: set BUCK1 to SW control with workaround, then disable it + ok &= prv_buck_set_sw_ctrl(PmicRegisters_BUCK_BUCK1NORMVOUT, + PmicRegisters_BUCK_BUCK1VOUTSTATUS, + PmicRegisters_BUCK_BUCKSWCTRLSEL__BUCK1SWCTRLSEL_SWCTRL, + 8 /* 1.8V */); + ok &= prv_write_register(PmicRegisters_BUCK_BUCK1ENACLR, 1); //enable 1.8V@LDO1 ok &= prv_write_register(PmicRegisters_LDSW_LDSW1LDOSEL, 1); //LDO ok &= prv_write_register(PmicRegisters_LDSW_LDSW1VOUTSEL, 8); //1.8V @@ -285,7 +338,7 @@ bool pmic_init(void) { if ((NPM1300_CONFIG.chg_current_ma < 32U) || (NPM1300_CONFIG.chg_current_ma > 800U) || (NPM1300_CONFIG.chg_current_ma % 2U != 0U)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid charge current: %d mA", NPM1300_CONFIG.chg_current_ma); + PBL_LOG_ERR("Invalid charge current: %d mA", NPM1300_CONFIG.chg_current_ma); return false; } @@ -295,19 +348,39 @@ bool pmic_init(void) { ok &= prv_write_register(PmicRegisters_BCHARGER_TASKRELEASEERROR, 1); // FIXME: this needs to be configurable at board level -#if PLATFORM_ASTERIX || PLATFORM_OBELIX +#ifdef CONFIG_BOARD_FAMILY_OBELIX + ok &= prv_write_register(PmicRegisters_ADC_ADCNTCRSEL, PmicRegisters_ADC_ADCNTCRSEL__ADCNTCRSEL_10K); + + ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVTERM, PmicRegisters_BCHARGER_BCHGVTERM__BCHGVTERMNORM_4V35); + ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVTERMR, PmicRegisters_BCHARGER_BCHGVTERMR__BCHGVTERMREDUCED_4V00); +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) + ok &= prv_write_register(PmicRegisters_ADC_ADCNTCRSEL, PmicRegisters_ADC_ADCNTCRSEL__ADCNTCRSEL_10K); + + ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVTERM, PmicRegisters_BCHARGER_BCHGVTERM__BCHGVTERMNORM_4V45); + ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVTERMR, PmicRegisters_BCHARGER_BCHGVTERMR__BCHGVTERMREDUCED_4V00); +#elif defined(CONFIG_BOARD_FAMILY_ASTERIX) ok &= prv_write_register(PmicRegisters_ADC_ADCNTCRSEL, PmicRegisters_ADC_ADCNTCRSEL__ADCNTCRSEL_10K); ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVTERM, PmicRegisters_BCHARGER_BCHGVTERM__BCHGVTERMNORM_4V20); ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVTERMR, PmicRegisters_BCHARGER_BCHGVTERMR__BCHGVTERMREDUCED_4V00); #endif + { + uint16_t code = prv_ntc_threshold_code(NPM1300_CONFIG.ntc_hot_celsius); + ok &= prv_write_register(PmicRegisters_BCHARGER_NTCHOT, (uint8_t)(code >> 2)); + ok &= prv_write_register(PmicRegisters_BCHARGER_NTCHOTLSB, (uint8_t)(code & 0x3U)); + } + // FIXME: this needs to be configurable at board level -#if PLATFORM_OBELIX +#ifdef CONFIG_BOARD_FAMILY_OBELIX //3.3V @ LDO2 ok &= prv_write_register(PmicRegisters_LDSW_LDSW2LDOSEL, PmicRegisters_LDSW_LDSW2LDOSEL__LDO_MODE); ok &= prv_write_register(PmicRegisters_LDSW_LDSW2VOUTSEL, PmicRegisters_LDSW_LDSW2VOUTSEL__3V3); - ok &= prv_write_register(PmicRegisters_LDSW_TASKLDSW2SET, 1); + ok &= prv_write_register(PmicRegisters_LDSW_TASKLDSW2CLR, 1); +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) + // LDSW2 (3.3V for PDM) + ok &= prv_write_register(PmicRegisters_LDSW_LDSW2LDOSEL, PmicRegisters_LDSW_LDSW2LDOSEL__LDSW_MODE); + ok &= prv_write_register(PmicRegisters_LDSW_TASKLDSW2CLR, 1); #endif val = (uint8_t)(NPM1300_CONFIG.chg_current_ma / 4U); @@ -315,20 +388,7 @@ bool pmic_init(void) { val = (NPM1300_CONFIG.chg_current_ma / 2U) % 2U; ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGISETLSB, val); - if (NPM1300_CONFIG.dischg_limit_ma == 200) { - ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGEMSB, - NPM1300_BCHGISETDISCHARGEMSB_200MA); - ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGELSB, - NPM1300_BCHGISETDISCHARGELSB_200MA); - } else if (NPM1300_CONFIG.dischg_limit_ma == 1000) { - ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGEMSB, - NPM1300_BCHGISETDISCHARGEMSB_1000MA); - ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGELSB, - NPM1300_BCHGISETDISCHARGELSB_1000MA); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid discharge limit: %d mA", NPM1300_CONFIG.dischg_limit_ma); - return false; - } + ok &= dischg_limit_ma_set(NPM1300_CONFIG.dischg_limit_ma); if (NPM1300_CONFIG.vbus_current_startup != 0) { ok &= prv_write_register(PmicRegisters_VBUSIN_VBUSINILIMSTARTUP, @@ -342,7 +402,7 @@ bool pmic_init(void) { ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGITERMSEL, PmicRegisters_BCHARGER_BCHGITERMSEL__SEL20); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid termination current: %d", NPM1300_CONFIG.term_current_pct); + PBL_LOG_ERR("Invalid termination current: %d", NPM1300_CONFIG.term_current_pct); return false; } @@ -360,12 +420,10 @@ bool pmic_init(void) { ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGVBATLOWCHARGE, 1); - ok &= prv_write_register(PmicRegisters_BCHARGER_BCHGENABLESET, 1); - prv_configure_interrupts(); if (!ok) { - PBL_LOG(LOG_LEVEL_ERROR, "one or more PMIC transactions failed"); + PBL_LOG_ERR("one or more PMIC transactions failed"); } return ok; @@ -374,12 +432,12 @@ bool pmic_init(void) { bool pmic_power_off(void) { // TODO: review implementation, see GH-238 if (pmic_is_usb_connected()) { - PBL_LOG(LOG_LEVEL_ERROR, "USB is connected, cannot power off"); + PBL_LOG_ERR("USB is connected, cannot power off"); return false; } if (!prv_write_register(PmicRegisters_SHIP_TASKENTERSHIPMODE, 1)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to enter ship mode"); + PBL_LOG_ERR("Failed to enter ship mode"); return false; } @@ -403,10 +461,18 @@ uint16_t pmic_get_vsys(void) { return 0; } uint8_t reg = 0; + uint32_t elapsed = 0; while ((reg & 0x08) == 0) { + if (elapsed >= ADC_POLL_TIMEOUT_MS) { + return 0; // Timeout waiting for ADC + } if (!prv_read_register(PmicRegisters_MAIN_EVENTSADCCLR, ®)) { return 0; } + if ((reg & 0x08) == 0) { + psleep(ADC_POLL_DELAY_MS); + elapsed += ADC_POLL_DELAY_MS; + } } uint8_t vsys_msb; @@ -431,10 +497,18 @@ int battery_get_millivolts(void) { return 0; } uint8_t reg = 0; + uint32_t elapsed = 0; while ((reg & 0x01) == 0) { + if (elapsed >= ADC_POLL_TIMEOUT_MS) { + return 0; // Timeout waiting for ADC + } if (!prv_read_register(PmicRegisters_MAIN_EVENTSADCCLR, ®)) { return 0; } + if ((reg & 0x01) == 0) { + psleep(ADC_POLL_DELAY_MS); + elapsed += ADC_POLL_DELAY_MS; + } } uint8_t vbat_msb; @@ -471,7 +545,7 @@ int battery_get_constants(BatteryConstants *constants) { NPM1300_BCHARGER_ADC_CALC_CHARGE_DIV; } else { full_scale_ua = - ((int32_t)NPM1300_CONFIG.dischg_limit_ma * 1000 * NPM1300_BCHARGER_ADC_CALC_DISCHARGE_MUL) / + ((int32_t)s_dischg_limit_ma * 1000 * NPM1300_BCHARGER_ADC_CALC_DISCHARGE_MUL) / NPM1300_BCHARGER_ADC_CALC_DISCHARGE_DIV; } @@ -495,10 +569,18 @@ int battery_get_constants(BatteryConstants *constants) { // Process the VBAT measurement reg = 0U; + uint32_t elapsed = 0; while ((reg & PmicRegisters_MAIN_EVENTSADCCLR__EVENTADCVBATRDY) == 0U) { + if (elapsed >= ADC_POLL_TIMEOUT_MS) { + return -1; // Timeout waiting for VBAT ADC + } if (!prv_read_register(PmicRegisters_MAIN_EVENTSADCCLR, ®)) { return -1; } + if ((reg & PmicRegisters_MAIN_EVENTSADCCLR__EVENTADCVBATRDY) == 0U) { + psleep(ADC_POLL_DELAY_MS); + elapsed += ADC_POLL_DELAY_MS; + } } if (!prv_read_register(PmicRegisters_ADC_ADCVBATRESULTMSB, &msb)) { @@ -516,10 +598,18 @@ int battery_get_constants(BatteryConstants *constants) { constants->v_mv = (int32_t)(raw * NPM1300_ADC_VFS_VBAT_MV) / NPM1300_BCHARGER_ADC_BITS_RESOLUTION; // Process the IBAT measurement + elapsed = 0; while ((reg & PmicRegisters_MAIN_EVENTSADCCLR__EVENTADCIBATRDY) == 0U) { + if (elapsed >= ADC_POLL_TIMEOUT_MS) { + return -1; // Timeout waiting for IBAT ADC + } if (!prv_read_register(PmicRegisters_MAIN_EVENTSADCCLR, ®)) { return -1; } + if ((reg & PmicRegisters_MAIN_EVENTSADCCLR__EVENTADCIBATRDY) == 0U) { + psleep(ADC_POLL_DELAY_MS); + elapsed += ADC_POLL_DELAY_MS; + } } if (!prv_read_register(PmicRegisters_ADC_ADCVBAT2RESULTMSB, &msb)) { @@ -537,10 +627,18 @@ int battery_get_constants(BatteryConstants *constants) { constants->i_ua = ((int32_t)raw * full_scale_ua) / NPM1300_BCHARGER_ADC_BITS_RESOLUTION; // Process the NTC measurement + elapsed = 0; while ((reg & PmicRegisters_MAIN_EVENTSADCCLR__EVENTADCNTCRDY) == 0U) { + if (elapsed >= ADC_POLL_TIMEOUT_MS) { + return -1; // Timeout waiting for NTC ADC + } if (!prv_read_register(PmicRegisters_MAIN_EVENTSADCCLR, ®)) { return -1; } + if ((reg & PmicRegisters_MAIN_EVENTSADCCLR__EVENTADCNTCRDY) == 0U) { + psleep(ADC_POLL_DELAY_MS); + elapsed += ADC_POLL_DELAY_MS; + } } if (!prv_read_register(PmicRegisters_ADC_ADCNTCRESULTMSB, &lsb)) { @@ -684,7 +782,13 @@ static bool gpio_set(Npm1300GpioId_t id, bool is_high) { rv &= prv_write_register(PmicRegisters_GPIOS_GPIOPUEN2, is_high ? PmicRegisters_GPIOS_GPIOPUEN__EN : PmicRegisters_GPIOS_GPIOPUEN__DIS); break; - + case Npm1300_Gpio3: { + rv = prv_write_register(PmicRegisters_GPIOS_GPIOMODE3, + is_high ? PmicRegisters_GPIOS_GPIOMODE__OUTPUT_HIGH : PmicRegisters_GPIOS_GPIOMODE__OUTPUT_LOW); + rv &= prv_write_register(PmicRegisters_GPIOS_GPIOPUEN3, + is_high ? PmicRegisters_GPIOS_GPIOPUEN__EN : PmicRegisters_GPIOS_GPIOPUEN__DIS); + break; + } default: break; } @@ -692,6 +796,57 @@ static bool gpio_set(Npm1300GpioId_t id, bool is_high) { return rv; } +static bool ldo2_set_enabled(bool enabled) { + if (enabled) { + return prv_write_register(PmicRegisters_LDSW_TASKLDSW2SET, 1); + } else { + return prv_write_register(PmicRegisters_LDSW_TASKLDSW2CLR, 1); + } +} + +static bool dischg_limit_ma_set(uint32_t dischg_limit_ma) { + bool ret; + + if (s_dischg_limit_ma == dischg_limit_ma) { + return true; + } + + if (dischg_limit_ma == 200) { + ret = prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGEMSB, + NPM1300_BCHGISETDISCHARGEMSB_200MA); + if (!ret) { + return ret; + } + + ret = prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGELSB, + NPM1300_BCHGISETDISCHARGELSB_200MA); + if (!ret) { + return ret; + } + } else if (dischg_limit_ma == 1000) { + ret = prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGEMSB, + NPM1300_BCHGISETDISCHARGEMSB_1000MA); + if (!ret) { + return ret; + } + + ret = prv_write_register(PmicRegisters_BCHARGER_BCHGISETDISCHARGELSB, + NPM1300_BCHGISETDISCHARGELSB_1000MA); + if (!ret) { + return ret; + } + } else { + PBL_LOG_ERR("Invalid discharge limit: %" PRIu32 " mA", dischg_limit_ma); + return false; + } + + s_dischg_limit_ma = dischg_limit_ma; + + return true; +} + Npm1300Ops_t NPM1300_OPS = { .gpio_set = gpio_set, -}; \ No newline at end of file + .ldo2_set_enabled = ldo2_set_enabled, + .dischg_limit_ma_set = dischg_limit_ma_set, +}; diff --git a/src/fw/drivers/pmic/npm1300.h b/src/fw/drivers/pmic/npm1300.h index a7b0219e24..5c739a5183 100644 --- a/src/fw/drivers/pmic/npm1300.h +++ b/src/fw/drivers/pmic/npm1300.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -26,6 +13,8 @@ typedef struct { uint8_t term_current_pct; //! Thermistor beta value uint16_t thermistor_beta; + //! NTC HOT threshold in Celsius (charging stops above this) + uint8_t ntc_hot_celsius; //! Vbus current limite0 uint16_t vbus_current_lim0; //! Vbus current limite startup @@ -40,9 +29,14 @@ typedef enum { Npm1300_Gpio4, }Npm1300GpioId_t; +//! Maximum discharge current in mA +#define NPM1300_DISCHG_LIMIT_MA_MAX 1000UL + //! nPM1300 ops typedef struct { bool (*gpio_set)(Npm1300GpioId_t id, bool is_high); + bool (*ldo2_set_enabled)(bool enabled); + bool (*dischg_limit_ma_set)(uint32_t ilim_ma); }Npm1300Ops_t; extern Npm1300Ops_t NPM1300_OPS; \ No newline at end of file diff --git a/src/fw/drivers/pmic/wscript_build b/src/fw/drivers/pmic/wscript_build new file mode 100644 index 0000000000..0b69e4c11f --- /dev/null +++ b/src/fw/drivers/pmic/wscript_build @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_PMIC_NPM1300: + sources.append('npm1300.c') + +bld.objects( + name='driver_pmic', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_pmic') diff --git a/src/fw/drivers/pressure.h b/src/fw/drivers/pressure.h new file mode 100644 index 0000000000..05a5837489 --- /dev/null +++ b/src/fw/drivers/pressure.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +//! Initialize the pressure sensor driver. Call this once at startup. +void pressure_init(void); diff --git a/src/fw/drivers/pressure/Kconfig b/src/fw/drivers/pressure/Kconfig new file mode 100644 index 0000000000..b1d8a8b74c --- /dev/null +++ b/src/fw/drivers/pressure/Kconfig @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PRESSURE + bool "Pressure Sensor" + help + Pressure Sensor Drivers + +if PRESSURE + +config PRESSURE_BMP390 + bool "Bosch BMP390" + help + Support for the Bosch BMP390 barometric pressure sensor. + +module = DRIVER_PRESSURE +module-str = Pressure Sensor +source "src/fw/Kconfig.template.log_level" + +endif # PRESSURE diff --git a/src/fw/drivers/pressure/bmp390.c b/src/fw/drivers/pressure/bmp390.c new file mode 100644 index 0000000000..468d1e34c5 --- /dev/null +++ b/src/fw/drivers/pressure/bmp390.c @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "drivers/pressure.h" +#include "drivers/i2c.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DEFINE(driver_pressure_bmp390, CONFIG_DRIVER_PRESSURE_LOG_LEVEL); + +#define BMP390_CHIP_ID 0x00 +#define BMP390_CHIP_ID_VALUE 0x60 +#define BMP390_PWR_CTRL 0x1B + + +static bool prv_read_register(I2CSlavePort *i2c, uint8_t register_address, uint8_t *result) { + i2c_use(i2c); + bool rv = i2c_write_block(i2c, 1, ®ister_address); + if (rv) + rv = i2c_read_block(i2c, 1, result); + i2c_release(i2c); + return rv; +} + +static bool prv_write_register(I2CSlavePort *i2c, uint8_t register_address, uint8_t datum) { + i2c_use(i2c); + uint8_t d[2] = { register_address, datum }; + bool rv = i2c_write_block(i2c, 2, d); + i2c_release(i2c); + return rv; +} + +void pressure_init(void) { + bool rv; + uint8_t result; + + rv = prv_read_register(I2C_BMP390, BMP390_CHIP_ID, &result); + if (!rv || result != BMP390_CHIP_ID_VALUE) { + PBL_LOG_DBG("BMP390 probe failed; rv %d, result 0x%02x", rv, result); + } else { + PBL_LOG_DBG("found the BMP390, setting to low power"); + (void) prv_write_register(I2C_BMP390, BMP390_PWR_CTRL, 0); + } +} diff --git a/src/fw/drivers/pressure/wscript_build b/src/fw/drivers/pressure/wscript_build new file mode 100644 index 0000000000..5cb771c0a5 --- /dev/null +++ b/src/fw/drivers/pressure/wscript_build @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes'] + +if bld.env.CONFIG_PRESSURE_BMP390: + sources.append('bmp390.c') + +bld.objects( + name='driver_pressure', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_pressure') diff --git a/src/fw/drivers/pwm.h b/src/fw/drivers/pwm.h index 0ec9f66209..35b8ffe67e 100644 --- a/src/fw/drivers/pwm.h +++ b/src/fw/drivers/pwm.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/pwm/Kconfig b/src/fw/drivers/pwm/Kconfig new file mode 100644 index 0000000000..567be5bc5e --- /dev/null +++ b/src/fw/drivers/pwm/Kconfig @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PWM + bool "PWM" + help + PWM Drivers + +if PWM + +config PWM_NRF5 + bool "Nordic nRF5" + help + Support for the Nordic nRF5 PWM controller. + +config PWM_SF32LB + bool "SiFli SF32LB" + select HAL_SIFLI_GPT + help + Support for the SiFli SF32LB PWM controller. + +config PWM_QEMU + bool "QEMU PWM" + help + Support for the QEMU PWM peripheral. + +endif # PWM diff --git a/src/fw/drivers/pwm/nrf5.c b/src/fw/drivers/pwm/nrf5.c new file mode 100644 index 0000000000..b48a722af3 --- /dev/null +++ b/src/fw/drivers/pwm/nrf5.c @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/gpio.h" +#include "drivers/periph_config.h" +#include "drivers/pwm.h" +#include "drivers/timer.h" +#include "system/passert.h" + +#include + +void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { + nrfx_pwm_config_t config = NRFX_PWM_DEFAULT_CONFIG( + pwm->output.gpio_pin, + NRF_PWM_PIN_NOT_CONNECTED, + NRF_PWM_PIN_NOT_CONNECTED, + NRF_PWM_PIN_NOT_CONNECTED); + // this is hokey and imprecise, but oh well + if (frequency >= 16000000) config.base_clock = NRF_PWM_CLK_16MHz; + else if (frequency >= 8000000) config.base_clock = NRF_PWM_CLK_8MHz; + else if (frequency >= 4000000) config.base_clock = NRF_PWM_CLK_4MHz; + else if (frequency >= 2000000) config.base_clock = NRF_PWM_CLK_2MHz; + else if (frequency >= 1000000) config.base_clock = NRF_PWM_CLK_1MHz; + else if (frequency >= 500000) config.base_clock = NRF_PWM_CLK_500kHz; + else if (frequency >= 250000) config.base_clock = NRF_PWM_CLK_250kHz; + else if (frequency >= 125000) config.base_clock = NRF_PWM_CLK_125kHz; + else WTF; + config.count_mode = NRF_PWM_MODE_UP; + config.top_value = resolution; + config.load_mode = NRF_PWM_LOAD_COMMON; + config.step_mode = NRF_PWM_STEP_TRIGGERED; + + nrfx_err_t rv = nrfx_pwm_init(&pwm->peripheral, &config, NULL, NULL); + PBL_ASSERTN(rv == NRFX_SUCCESS); + + pwm->state->enabled = 0; + pwm->state->value = 0; + pwm->state->seq = (nrf_pwm_sequence_t) { .values = { .p_common = &pwm->state->value }, .length = 1, .repeats = 0, .end_delay = 0 }; + pwm->state->resolution = resolution; +} + +void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { + PBL_ASSERTN(duty_cycle < 0xFFFF); + pwm->state->value = pwm->state->resolution - duty_cycle; + if (pwm->state->enabled) + nrfx_pwm_simple_playback(&pwm->peripheral, &pwm->state->seq, 1, 0); + if (duty_cycle == 0) + nrfx_pwm_stop(&pwm->peripheral, 0); +} + +void pwm_enable(const PwmConfig *pwm, bool enable) { + if (enable) { + nrfx_pwm_simple_playback(&pwm->peripheral, &pwm->state->seq, 1, 0); + } else { + // no need to set the output to low; I think the nRF does it for us? + nrfx_pwm_stop(&pwm->peripheral, 0); + } + pwm->state->enabled = !!enable; +} diff --git a/src/fw/drivers/pwm/sf32lb.c b/src/fw/drivers/pwm/sf32lb.c new file mode 100644 index 0000000000..e283e86fa8 --- /dev/null +++ b/src/fw/drivers/pwm/sf32lb.c @@ -0,0 +1,130 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "board/board.h" +#include "drivers/pwm.h" +#include "system/passert.h" +#include "kernel/util/stop.h" + +#include "bf0_hal_tim.h" + +#define MAX_PERIOD_GPT 0xFFFFU +#define MAX_PERIOD_ATM 0xFFFFFFFFU +#define MIN_PERIOD 3U +#define MIN_PULSE 1U + +void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { + GPT_HandleTypeDef *htim = &pwm->state->handle; + uint32_t period, pulse; + uint32_t gpt_clock, psc; + uint32_t channel; + uint32_t max_period; + HAL_StatusTypeDef ret; + + // converts the channel number to the channel number of HAL library + channel = (pwm->state->channel - 1U) << 2U; + + if (IS_GPT_ADVANCED_INSTANCE(htim->Instance) != RESET) { + max_period = MAX_PERIOD_ATM; + } else { + max_period = MAX_PERIOD_GPT; + } + + if (htim->Instance == hwp_gptim2) { + gpt_clock = 24000000U; + } else { + gpt_clock = HAL_RCC_GetPCLKFreq(htim->core, 1); + } + + // Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns + gpt_clock /= 1000000UL; + period = (uint64_t)pwm->state->value * gpt_clock / 1000ULL; + psc = period / max_period + 1U; + period = period / psc; + __HAL_GPT_SET_PRESCALER(htim, psc - 1U); + + if (period < MIN_PERIOD) { + period = MIN_PERIOD; + } + __HAL_GPT_SET_AUTORELOAD(htim, period - 1U); + + // transfer cycle to ns + pulse = duty_cycle * pwm->state->value / pwm->state->resolution; + pulse = (uint64_t)pulse * gpt_clock / psc / 1000ULL; + + if (pulse < MIN_PULSE) { + pulse = MIN_PULSE; + } else if (pulse >= period) { + // if pulse reach to 100%, need set pulse = period + 1, because pulse = + // period, the real percentage = 99.9983% + pulse = period + 1U; + } + __HAL_GPT_SET_COMPARE(htim, channel, pulse - 1); + + // Update frequency value + ret = HAL_GPT_GenerateEvent(htim, GPT_EVENTSOURCE_UPDATE); + PBL_ASSERTN(ret == HAL_OK); +} + +void pwm_enable(const PwmConfig *pwm, bool enable) { + GPT_HandleTypeDef *htim = &pwm->state->handle; + HAL_StatusTypeDef ret; + uint32_t channel; + + /* Converts the channel number to the channel number of Hal library */ + channel = (pwm->state->channel - 1U) << 2U; + + if (enable) { + GPT_OC_InitTypeDef oc_config = {0}; + + oc_config.OCMode = GPT_OCMODE_PWM1; + oc_config.Pulse = __HAL_GPT_GET_COMPARE(htim, channel); + oc_config.OCPolarity = GPT_OCPOLARITY_HIGH; + oc_config.OCFastMode = GPT_OCFAST_DISABLE; + + ret = HAL_GPT_PWM_ConfigChannel(htim, &oc_config, channel); + PBL_ASSERTN(ret == HAL_OK); + + ret = HAL_GPT_PWM_Start(htim, channel); + PBL_ASSERTN(ret == HAL_OK); + + if (!pwm->state->enabled) { + pwm->state->enabled = true; + stop_mode_disable(InhibitorPWM); + } + } else { + ret = HAL_GPT_PWM_Stop(htim, channel); + PBL_ASSERTN(ret == HAL_OK); + + if (pwm->state->enabled) { + pwm->state->enabled = false; + stop_mode_enable(InhibitorPWM); + } + } +} + +void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { + GPT_HandleTypeDef *htim = &pwm->state->handle; + GPT_ClockConfigTypeDef *clock_config = &pwm->state->clock_config; + HAL_StatusTypeDef ret; + + PBL_ASSERTN((resolution != 0U) && (frequency != 0U)); + + pwm->state->resolution = resolution; + pwm->state->value = 1000000000UL / (frequency); + + HAL_PIN_Set(pwm->pwm_pin.pad, pwm->pwm_pin.func, pwm->pwm_pin.flags, 1); + + ret = HAL_GPT_Base_Init(htim); + PBL_ASSERTN(ret == HAL_OK); + + ret = HAL_GPT_ConfigClockSource(htim, clock_config); + PBL_ASSERTN(ret == HAL_OK); + + ret = HAL_GPT_PWM_Init(htim); + PBL_ASSERTN(ret == HAL_OK); + + __HAL_GPT_URS_ENABLE(htim); +} diff --git a/src/fw/drivers/pwm/wscript_build b/src/fw/drivers/pwm/wscript_build new file mode 100644 index 0000000000..bc3d8d5709 --- /dev/null +++ b/src/fw/drivers/pwm/wscript_build @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_PWM_QEMU: + sources.append('../qemu/qemu_pwm_hal.c') +elif bld.env.CONFIG_PWM_NRF5: + sources.append('nrf5.c') + use.append('hal_nordic') +elif bld.env.CONFIG_PWM_SF32LB: + sources.append('sf32lb.c') + use.append('hal_sifli') + +bld.objects( + name='driver_pwm', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_pwm') diff --git a/src/fw/drivers/pwr.h b/src/fw/drivers/pwr.h index 749e273e90..bf60b0e94e 100644 --- a/src/fw/drivers/pwr.h +++ b/src/fw/drivers/pwr.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/qemu/qemu_accel.c b/src/fw/drivers/qemu/qemu_accel.c index 357dfa3013..0f482c03b1 100644 --- a/src/fw/drivers/qemu/qemu_accel.c +++ b/src/fw/drivers/qemu/qemu_accel.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! The QEMU accelerometer driver is pretty broken, but it requires //! a complete overhaul of both the QEMU Serial messages and all senders @@ -55,12 +42,10 @@ //! without speeding up or slowing down the signal during replay. #include "drivers/accel.h" -#include "drivers/imu/bmi160/bmi160.h" #include "drivers/qemu/qemu_serial.h" #include "drivers/rtc.h" #include "os/mutex.h" -#include "pebble_errors.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" @@ -68,9 +53,7 @@ #include - -#define ACCEL_LOG_DEBUG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_ACCEL, LOG_LEVEL_DEBUG, fmt, ## args) - +PBL_LOG_MODULE_DECLARE(imu, CONFIG_DRIVER_IMU_LOG_LEVEL); static bool s_initialized; static PebbleMutex * s_accel_mutex; @@ -135,7 +118,7 @@ static void prv_timer_cb(void *data) { if (s_num_fifo_samples > 0) { AccelDriverSample sample; prv_construct_driver_sample(&sample); - ACCEL_LOG_DEBUG("Accel sample to manager: %d, %d, %d", sample.x, sample.y, sample.z); + PBL_LOG_VERBOSE("Accel sample to manager: %d, %d, %d", sample.x, sample.y, sample.z); accel_cb_new_sample(&sample); } @@ -167,10 +150,10 @@ void qemu_accel_msg_callack(const uint8_t *data, uint32_t len) { // Validate the packet uint32_t data_bytes = hdr->num_samples * sizeof(AccelRawData); if (data_bytes != len - sizeof(QemuProtocolAccelHeader)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet received"); + PBL_LOG_ERR("Invalid packet received"); return; } - ACCEL_LOG_DEBUG("Got accel msg from host: num samples: %d", hdr->num_samples); + PBL_LOG_VERBOSE("Got accel msg from host: num samples: %d", hdr->num_samples); // Copy the received samples into the s_rcv_buffer #if QEMU_ACCEL_RCV_BUFFER_SAMPLES < 256 @@ -186,7 +169,7 @@ void qemu_accel_msg_callack(const uint8_t *data, uint32_t len) { s_rcv_buffer[i].x = ntohs(hdr->samples[i].x); s_rcv_buffer[i].y = ntohs(hdr->samples[i].y); s_rcv_buffer[i].z = ntohs(hdr->samples[i].z); - ACCEL_LOG_DEBUG(" x,y,z from host: %d, %d, %d", s_rcv_buffer[i].x, + PBL_LOG_VERBOSE(" x,y,z from host: %d, %d, %d", s_rcv_buffer[i].x, s_rcv_buffer[i].y, s_rcv_buffer[i].z); } @@ -206,7 +189,7 @@ void qemu_accel_msg_callack(const uint8_t *data, uint32_t len) { } -void qemu_accel_init(void) { +void accel_init(void) { PBL_ASSERTN(!s_initialized); s_initialized = true; s_latest_reading = s_default_sample; @@ -214,6 +197,11 @@ void qemu_accel_init(void) { s_timer_id = new_timer_create(); } +void accel_power_up(void) { +} + +void accel_power_down(void) { +} uint32_t accel_set_sampling_interval(uint32_t interval_us) { mutex_lock(s_accel_mutex); @@ -279,6 +267,7 @@ bool accel_get_double_tap_detection_enabled(void) { void accel_set_shake_sensitivity_high(bool sensitivity_high) { } -bool accel_run_selftest(void) { - return true; -} + +void accel_set_shake_sensitivity_percent(uint8_t percent) { + (void)percent; +} \ No newline at end of file diff --git a/src/fw/drivers/qemu/qemu_accel.h b/src/fw/drivers/qemu/qemu_accel.h index 7bf26abd38..fe21d4d04b 100644 --- a/src/fw/drivers/qemu/qemu_accel.h +++ b/src/fw/drivers/qemu/qemu_accel.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/qemu/qemu_battery.c b/src/fw/drivers/qemu/qemu_battery.c index d6f381e4d5..606649ceac 100644 --- a/src/fw/drivers/qemu/qemu_battery.c +++ b/src/fw/drivers/qemu/qemu_battery.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/battery.h" #include "drivers/qemu/qemu_serial.h" #include "drivers/qemu/qemu_settings.h" #include "system/passert.h" -#include "services/common/battery/battery_state.h" -#include "services/common/battery/battery_curve.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/battery/battery_curve.h" #include "system/logging.h" #include "util/math.h" @@ -55,14 +42,18 @@ void battery_set_fast_charge(bool fast_charge_enabled) { } +uint8_t qemu_battery_get_percent(void) { + return s_percent; +} + void qemu_battery_msg_callack(const uint8_t *data, uint32_t len) { QemuProtocolBatteryHeader *hdr = (QemuProtocolBatteryHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Got battery msg: pct: %d, charger_connected:%d", + PBL_LOG_DBG("Got battery msg: pct: %d, charger_connected:%d", hdr->battery_pct, hdr->charger_connected); s_percent = MIN(100, hdr->battery_pct); diff --git a/src/fw/drivers/qemu/qemu_battery.h b/src/fw/drivers/qemu/qemu_battery.h index 5eeaf205d9..7e30cb7c7e 100644 --- a/src/fw/drivers/qemu/qemu_battery.h +++ b/src/fw/drivers/qemu/qemu_battery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,3 +8,7 @@ //! Handler called by qemu_serial driver when we receive a QemuProtocol_Battery message //! over the qemu serial connection. void qemu_battery_msg_callack(const uint8_t *data, uint32_t len); + +//! Returns the exact percent last set via `pebble emu-battery --percent N`. +//! Lets battery_state.c bypass the lossy voltage-curve roundtrip on QEMU. +uint8_t qemu_battery_get_percent(void); diff --git a/src/fw/drivers/qemu/qemu_button.c b/src/fw/drivers/qemu/qemu_button.c new file mode 100644 index 0000000000..35c5195f0e --- /dev/null +++ b/src/fw/drivers/qemu/qemu_button.c @@ -0,0 +1,136 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/button.h" +#include "drivers/debounced_button.h" + +#include "board/board.h" +#include "console/prompt.h" +#include "kernel/events.h" +#include "system/passert.h" + +#include "FreeRTOS.h" + +#include +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// QEMU GPIO register offsets (must match pebble-gpio device) +#define GPIO_BTN_STATE 0x00 +#define GPIO_BTN_EDGE 0x04 +#define GPIO_INTCTRL 0x08 +#define GPIO_INTSTAT 0x0C + +// Button bit positions (must match QEMU pebble-gpio device) +#define BTN_BIT_BACK (1 << 0) +#define BTN_BIT_UP (1 << 1) +#define BTN_BIT_SELECT (1 << 2) +#define BTN_BIT_DOWN (1 << 3) + +static const uint32_t s_button_bits[NUM_BUTTONS] = { + [BUTTON_ID_BACK] = BTN_BIT_BACK, + [BUTTON_ID_UP] = BTN_BIT_UP, + [BUTTON_ID_SELECT] = BTN_BIT_SELECT, + [BUTTON_ID_DOWN] = BTN_BIT_DOWN, +}; + +static uint32_t s_last_state; + +static void prv_gpio_irq_handler(void) { + uint32_t base = QEMU_GPIO_BASE; + + // Read which buttons changed + uint32_t edge = REG32(base + GPIO_BTN_EDGE); + // Clear edge flags + REG32(base + GPIO_INTSTAT) = edge; + + // Read current state + uint32_t state = REG32(base + GPIO_BTN_STATE); + uint32_t changed = s_last_state ^ state; + s_last_state = state; + + // Generate button events for each changed button + bool should_context_switch = false; + for (int i = 0; i < NUM_BUTTONS; i++) { + if (changed & s_button_bits[i]) { + bool is_pressed = (state & s_button_bits[i]) != 0; + PebbleEvent e = { + .type = is_pressed ? PEBBLE_BUTTON_DOWN_EVENT : PEBBLE_BUTTON_UP_EVENT, + .button.button_id = i, + }; + should_context_switch |= event_put_isr(&e); + } + } + + portEND_SWITCHING_ISR(should_context_switch); +} + +// IRQ trampoline for GPIO IRQ (IRQ 6) +void GPIO_IRQHandler(void) { + prv_gpio_irq_handler(); +} + +void button_init(void) { + uint32_t base = QEMU_GPIO_BASE; + + // Clear any pending edge flags + REG32(base + GPIO_INTSTAT) = 0xF; + + // Enable edge interrupt + REG32(base + GPIO_INTCTRL) = 1; + + // Enable GPIO IRQ in NVIC - priority must be >= configMAX_SYSCALL_INTERRUPT_PRIORITY + // to safely call FreeRTOS API from ISR. Use priority 6 (lower urgency than max syscall). + NVIC_SetPriority(GPIO_IRQn, 6); + NVIC_EnableIRQ(GPIO_IRQn); + + s_last_state = REG32(base + GPIO_BTN_STATE); +} + +bool button_is_pressed(ButtonId id) { + if (id >= NUM_BUTTONS) { + return false; + } + uint32_t state = REG32(QEMU_GPIO_BASE + GPIO_BTN_STATE); + return (state & s_button_bits[id]) != 0; +} + +uint8_t button_get_state_bits(void) { + return (uint8_t)(REG32(QEMU_GPIO_BASE + GPIO_BTN_STATE) & 0xF); +} + +void button_set_rotated(bool rotated) { + (void)rotated; +} + +void command_button_read(const char *button_id_str) { + int button = atoi(button_id_str); + if (button < 0 || button >= NUM_BUTTONS) { + prompt_send_response("Invalid button"); + return; + } + if (button_is_pressed(button)) { + prompt_send_response("down"); + } else { + prompt_send_response("up"); + } +} + +void debounced_button_init(void) { + // QEMU handles debounce in the GPIO device itself + button_init(); +} + +void command_put_raw_button_event(const char *button_index, const char *is_button_down_event) { + int button = atoi(button_index); + int is_down = atoi(is_button_down_event); + if (button < 0 || button >= NUM_BUTTONS) { + return; + } + PebbleEvent e = { + .type = is_down ? PEBBLE_BUTTON_DOWN_EVENT : PEBBLE_BUTTON_UP_EVENT, + .button.button_id = button, + }; + event_put(&e); +} diff --git a/src/fw/drivers/qemu/qemu_display_hal.c b/src/fw/drivers/qemu/qemu_display_hal.c new file mode 100644 index 0000000000..8114ca05a9 --- /dev/null +++ b/src/fw/drivers/qemu/qemu_display_hal.c @@ -0,0 +1,149 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/display/display.h" + +#include "board/board.h" +#include "board/display.h" +#include "system/passert.h" + +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// Display MMIO register offsets (must match QEMU pebble-display device) +#define DISP_CTRL 0x000 +#define DISP_STATUS 0x004 +#define DISP_WIDTH 0x008 +#define DISP_HEIGHT 0x00C +#define DISP_FORMAT 0x010 +#define DISP_FLAGS 0x014 +#define DISP_BRIGHTNESS 0x018 +#define DISP_INT_STATUS 0x01C +#define DISP_INT_CTRL 0x020 + +// CTRL register bits +#define CTRL_ENABLE (1 << 0) +#define CTRL_UPDATE_REQUEST (1 << 1) + +// STATUS register bits +#define STATUS_BUSY (1 << 0) +#define STATUS_UPDATE_DONE (1 << 1) + +// INT bits +#define INT_UPDATE_DONE_IE (1 << 0) +#define INT_UPDATE_DONE_PENDING (1 << 0) + +static bool s_enabled; +static bool s_updating; + +void display_init(void) { + uint32_t base = DISPLAY->base_addr; + + // Clear any pending interrupts + REG32(base + DISP_INT_STATUS) = INT_UPDATE_DONE_PENDING; + + // Enable the display + REG32(base + DISP_CTRL) = CTRL_ENABLE; + s_enabled = true; + s_updating = false; +} + +void display_clear(void) { + uint32_t fb_addr = DISPLAY->fb_addr; + uint16_t width = DISPLAY->width; + uint16_t height = DISPLAY->height; + +#if PBL_BW + uint32_t fb_size = ROUND_TO_MOD_CEIL_U(width, 32) / 8 * height; +#else + uint32_t fb_size = width * height; +#endif + memset((void *)fb_addr, 0, fb_size); + + // Request the QEMU display to update + REG32(DISPLAY->base_addr + DISP_CTRL) |= CTRL_UPDATE_REQUEST; +} + +void display_set_enabled(bool enabled) { + uint32_t base = DISPLAY->base_addr; + uint32_t ctrl = REG32(base + DISP_CTRL); + + if (enabled) { + ctrl |= CTRL_ENABLE; + } else { + ctrl &= ~CTRL_ENABLE; + } + REG32(base + DISP_CTRL) = ctrl; + s_enabled = enabled; +} + +void display_set_rotated(bool rotated) { + // QEMU display does not support rotation + (void)rotated; +} + +bool display_update_in_progress(void) { + return s_updating; +} + +void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { + PBL_ASSERTN(nrcb != NULL); + PBL_ASSERTN(uccb != NULL); + + uint32_t fb_addr = DISPLAY->fb_addr; + uint16_t width = DISPLAY->width; + DisplayRow row; + + s_updating = true; + + // Iterate through rows provided by the compositor +#if PBL_BW + uint16_t row_bytes = ROUND_TO_MOD_CEIL_U(width, 32) / 8; +#endif + while (nrcb(&row)) { +#if PBL_BW + // 1bpp: copy packed row data directly to QEMU framebuffer + volatile uint8_t *dst = (volatile uint8_t *)(fb_addr + (row.address * row_bytes)); + memcpy((void *)dst, row.data, row_bytes); +#else + // 8bpp: copy row data directly + volatile uint8_t *dst = (volatile uint8_t *)(fb_addr + (row.address * width)); + memcpy((void *)dst, row.data, width); +#endif + } + + // Tell QEMU to refresh the display + REG32(DISPLAY->base_addr + DISP_CTRL) |= CTRL_UPDATE_REQUEST; + + s_updating = false; + + // In QEMU the update is immediate, so call the completion callback directly + uccb(); +} + +void display_update_boot_frame(uint8_t *framebuffer) { + uint32_t fb_addr = DISPLAY->fb_addr; + uint16_t width = DISPLAY->width; + uint16_t height = DISPLAY->height; + +#if PBL_BW + // Boot splash provides 8bpp data; convert to 1bpp packed for QEMU + uint16_t row_bytes = ROUND_TO_MOD_CEIL_U(width, 32) / 8; + volatile uint8_t *dst = (volatile uint8_t *)fb_addr; + memset((void *)dst, 0, row_bytes * height); + for (uint16_t y = 0; y < height; y++) { + for (uint16_t x = 0; x < width; x++) { + uint8_t pixel = framebuffer[y * width + x]; + if (pixel != 0xFF) { // Non-white = black pixel + dst[y * row_bytes + x / 8] |= (1 << (x & 7)); + } + } + } +#else + memcpy((void *)fb_addr, framebuffer, width * height); +#endif + + // Request display refresh + REG32(DISPLAY->base_addr + DISP_CTRL) |= CTRL_UPDATE_REQUEST; +} diff --git a/src/fw/drivers/qemu/qemu_flash_hal.c b/src/fw/drivers/qemu/qemu_flash_hal.c new file mode 100644 index 0000000000..4cc1741cc5 --- /dev/null +++ b/src/fw/drivers/qemu/qemu_flash_hal.c @@ -0,0 +1,265 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/flash.h" +#include "drivers/flash/flash_impl.h" + +#include "board/board.h" +#include "system/passert.h" +#include "system/status_codes.h" + +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// External flash controller MMIO register offsets +#define FLASH_CMD 0x00 +#define FLASH_ADDR 0x04 +#define FLASH_STATUS 0x08 +#define FLASH_INT_CTRL 0x0C +#define FLASH_INT_STATUS 0x10 +#define FLASH_SIZE 0x14 +// Range-based persistence handshake with the QEMU pebble-extflash device. +// The device used to auto-flush its in-RAM XIP storage on SIGTERM, but that +// captured torn writes (flash_logging journal mid-update, PFS OVERWRITE_STARTED +// with no matching COMPLETE, etc.) and wedged the next boot. Now we drive +// persistence ourselves after each individual page write: +// 1. write FLASH_SYNC_LEN = bytes_written +// 2. write FLASH_SYNC = XIP address of the start of that range +// QEMU then blk_pwrite()s just that range to the backing file. Erases are +// auto-flushed in QEMU (the geometry is implicit in FLASH_ADDR + CMD), so we +// don't need to SYNC them from here. +#define FLASH_SYNC_LEN 0x18 +#define FLASH_SYNC 0x1C + +// CMD values +#define CMD_ERASE_SUBSECTOR 1 +#define CMD_ERASE_SECTOR 2 +#define CMD_WRITE_ENABLE 3 + +// STATUS bits +#define STATUS_BUSY (1 << 0) +#define STATUS_COMPLETE (1 << 1) + +// Standard flash geometry +#define QEMU_SECTOR_SIZE 0x10000 // 64 KB +#define QEMU_SUBSECTOR_SIZE 0x1000 // 4 KB +#define QEMU_PAGE_SIZE 256 + +static bool s_initialized; + +status_t flash_impl_init(bool coredump_mode) { + (void)coredump_mode; + s_initialized = true; + return S_SUCCESS; +} + +status_t flash_impl_set_burst_mode(bool enable) { + (void)enable; + return S_SUCCESS; +} + +FlashAddress flash_impl_get_sector_base_address(FlashAddress addr) { + return addr & ~(QEMU_SECTOR_SIZE - 1); +} + +FlashAddress flash_impl_get_subsector_base_address(FlashAddress addr) { + return addr & ~(QEMU_SUBSECTOR_SIZE - 1); +} + +size_t flash_impl_get_capacity(void) { + return REG32(QEMU_EXTFLASH_BASE + FLASH_SIZE); +} + +status_t flash_impl_enter_low_power_mode(void) { + return S_SUCCESS; +} + +status_t flash_impl_exit_low_power_mode(void) { + return S_SUCCESS; +} + +status_t flash_impl_read_sync(void *buffer, FlashAddress addr, size_t len) { + // Flash addresses already include the XIP base (0x10000000), read directly + const uint8_t *src = (const uint8_t *)(uintptr_t)addr; + memcpy(buffer, src, len); + return S_SUCCESS; +} + +status_t flash_impl_read_dma_begin(void *buffer, FlashAddress addr, size_t len) { + // No DMA in QEMU; do a synchronous read and report completion + status_t result = flash_impl_read_sync(buffer, addr, len); + flash_impl_on_read_dma_complete_from_isr(result); + return result; +} + +void flash_impl_enable_write_protection(void) { + // No-op for QEMU +} + +status_t flash_impl_write_protect(FlashAddress start_sector, FlashAddress end_sector) { + (void)start_sector; + (void)end_sector; + return S_SUCCESS; +} + +status_t flash_impl_unprotect(void) { + return S_SUCCESS; +} + +static size_t s_last_write_len; + +int flash_impl_write_page_begin(const void *buffer, FlashAddress addr, size_t len) { + // Calculate how many bytes we can write in this page + size_t page_offset = addr & (QEMU_PAGE_SIZE - 1); + size_t page_remaining = QEMU_PAGE_SIZE - page_offset; + size_t write_len = (len < page_remaining) ? len : page_remaining; + + // Write directly to XIP region - addr already includes base + volatile uint8_t *dst = (volatile uint8_t *)(uintptr_t)addr; + const uint8_t *src = (const uint8_t *)buffer; + for (size_t i = 0; i < write_len; i++) { + dst[i] = src[i]; + } + + REG32(QEMU_EXTFLASH_BASE + FLASH_SYNC_LEN) = (uint32_t)write_len; + REG32(QEMU_EXTFLASH_BASE + FLASH_SYNC) = (uint32_t)addr; + s_last_write_len = write_len; + return (int)write_len; +} + +status_t flash_impl_get_write_status(void) { + // Writes complete immediately in QEMU + return S_SUCCESS; +} + +status_t flash_impl_write_suspend(FlashAddress addr) { + (void)addr; + return S_SUCCESS; +} + +status_t flash_impl_write_resume(FlashAddress addr) { + (void)addr; + return S_SUCCESS; +} + +status_t flash_impl_erase_subsector_begin(FlashAddress subsector_addr) { + REG32(QEMU_EXTFLASH_BASE + FLASH_ADDR) = subsector_addr; + REG32(QEMU_EXTFLASH_BASE + FLASH_CMD) = CMD_ERASE_SUBSECTOR; + return S_SUCCESS; +} + +status_t flash_impl_erase_sector_begin(FlashAddress sector_addr) { + REG32(QEMU_EXTFLASH_BASE + FLASH_ADDR) = sector_addr; + REG32(QEMU_EXTFLASH_BASE + FLASH_CMD) = CMD_ERASE_SECTOR; + return S_SUCCESS; +} + +status_t flash_impl_erase_bulk_begin(void) { + // Erase the entire flash by erasing sector by sector + size_t capacity = flash_impl_get_capacity(); + for (size_t addr = 0; addr < capacity; addr += QEMU_SECTOR_SIZE) { + flash_impl_erase_sector_begin(addr); + // Wait for completion + while (flash_impl_get_erase_status() == E_BUSY) { + // spin + } + } + return S_SUCCESS; +} + +status_t flash_impl_get_erase_status(void) { + uint32_t status = REG32(QEMU_EXTFLASH_BASE + FLASH_STATUS); + if (status & STATUS_BUSY) { + return E_BUSY; + } + return S_SUCCESS; +} + +uint32_t flash_impl_get_typical_subsector_erase_duration_ms(void) { + return 1; // QEMU erases are instantaneous +} + +uint32_t flash_impl_get_typical_sector_erase_duration_ms(void) { + return 1; // QEMU erases are instantaneous +} + +status_t flash_impl_erase_suspend(FlashAddress addr) { + (void)addr; + // Check if erase already completed + if (flash_impl_get_erase_status() == S_SUCCESS) { + return S_NO_ACTION_REQUIRED; + } + return S_SUCCESS; +} + +status_t flash_impl_erase_resume(FlashAddress addr) { + (void)addr; + return S_SUCCESS; +} + +status_t flash_impl_blank_check_subsector(FlashAddress addr) { + FlashAddress base = flash_impl_get_subsector_base_address(addr); + const uint8_t *p = (const uint8_t *)(uintptr_t)base; + for (size_t i = 0; i < QEMU_SUBSECTOR_SIZE; i++) { + if (p[i] != 0xFF) { + return S_FALSE; + } + } + return S_TRUE; +} + +status_t flash_impl_blank_check_sector(FlashAddress addr) { + FlashAddress base = flash_impl_get_sector_base_address(addr); + const uint8_t *p = (const uint8_t *)(uintptr_t)base; + for (size_t i = 0; i < QEMU_SECTOR_SIZE; i++) { + if (p[i] != 0xFF) { + return S_FALSE; + } + } + return S_TRUE; +} + +void flash_impl_use(void) { + // No power management needed for QEMU flash +} + +void flash_impl_release(void) { +} + +void flash_impl_release_many(uint32_t num_locks) { + (void)num_locks; +} + +status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { + (void)addr; + *val = 0xFF; + return S_SUCCESS; +} + +status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { + (void)address; + *locked = false; + return S_SUCCESS; +} + +status_t flash_impl_erase_security_register(uint32_t addr) { + (void)addr; + return S_SUCCESS; +} + +status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { + (void)addr; + (void)val; + return S_SUCCESS; +} + +static const FlashSecurityRegisters s_security_regs = { + .sec_regs = NULL, + .num_sec_regs = 0, + .sec_reg_size = 0, +}; + +const FlashSecurityRegisters *flash_impl_security_registers_info(void) { + return &s_security_regs; +} diff --git a/src/fw/drivers/qemu/qemu_gpio_hal.c b/src/fw/drivers/qemu/qemu_gpio_hal.c new file mode 100644 index 0000000000..3850b5ac05 --- /dev/null +++ b/src/fw/drivers/qemu/qemu_gpio_hal.c @@ -0,0 +1,85 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/gpio.h" + +#include "board/board.h" + +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// GPIO MMIO register offsets +#define GPIO_STATE 0x00 // r: bit per button +#define GPIO_OUTPUT 0x04 // w: output state bits + +void gpio_use(GPIO_TypeDef *gpios) { + (void)gpios; +} + +void gpio_release(GPIO_TypeDef *gpios) { + (void)gpios; +} + +void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, + GPIOSpeed_TypeDef speed) { + (void)pin_config; + (void)otype; + (void)speed; +} + +void gpio_output_set(const OutputConfig *pin_config, bool asserted) { + if (pin_config == NULL) { + return; + } + uint32_t output = REG32(QEMU_GPIO_BASE + GPIO_OUTPUT); + bool drive_high = pin_config->active_high ? asserted : !asserted; + if (drive_high) { + output |= (1U << pin_config->gpio_pin); + } else { + output &= ~(1U << pin_config->gpio_pin); + } + REG32(QEMU_GPIO_BASE + GPIO_OUTPUT) = output; +} + +void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype, + GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd) { + (void)af_config; + (void)otype; + (void)speed; + (void)pupd; +} + +void gpio_af_configure_low_power(const AfConfig *af_config) { + (void)af_config; +} + +void gpio_af_configure_fixed_output(const AfConfig *af_config, bool asserted) { + (void)af_config; + (void)asserted; +} + +void gpio_init_all(void) { + // Nothing to configure for QEMU +} + +void gpio_input_init(const InputConfig *input_cfg) { + (void)input_cfg; +} + +void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd) { + (void)input_cfg; + (void)pupd; +} + +bool gpio_input_read(const InputConfig *input_cfg) { + if (input_cfg == NULL) { + return false; + } + uint32_t state = REG32(QEMU_GPIO_BASE + GPIO_STATE); + return (state & (1U << input_cfg->gpio_pin)) != 0; +} + +void gpio_analog_init(const InputConfig *input_cfg) { + (void)input_cfg; +} diff --git a/src/fw/drivers/qemu/qemu_pwm_hal.c b/src/fw/drivers/qemu/qemu_pwm_hal.c new file mode 100644 index 0000000000..782cc8324f --- /dev/null +++ b/src/fw/drivers/qemu/qemu_pwm_hal.c @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/pwm.h" + +#include "board/board.h" + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// Display brightness register offset (must match QEMU pebble-display) +#define DISP_BRIGHTNESS 0x018 +#define DISP_CTRL 0x000 +#define CTRL_UPDATE (1 << 1) + +// Minimum brightness when backlight is "off" - simulates ambient visibility +#define BACKLIGHT_OFF_BRIGHTNESS 40 + +static uint32_t s_resolution = 1024; + +void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { + (void)pwm; + (void)frequency; + if (resolution > 0) { + s_resolution = resolution; + } +} + +void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { + (void)pwm; + // Map duty_cycle (0..s_resolution) to brightness range + // 0 duty = dim (BACKLIGHT_OFF_BRIGHTNESS), full duty = full bright (255) + uint32_t range = 255 - BACKLIGHT_OFF_BRIGHTNESS; + uint32_t brightness = BACKLIGHT_OFF_BRIGHTNESS + (duty_cycle * range) / s_resolution; + if (brightness > 255) { + brightness = 255; + } + REG32(QEMU_DISPLAY_BASE + DISP_BRIGHTNESS) = brightness; + REG32(QEMU_DISPLAY_BASE + DISP_CTRL) |= CTRL_UPDATE; +} + +void pwm_enable(const PwmConfig *pwm, bool enable) { + (void)pwm; + uint32_t brightness = enable ? 255 : BACKLIGHT_OFF_BRIGHTNESS; + REG32(QEMU_DISPLAY_BASE + DISP_BRIGHTNESS) = brightness; + REG32(QEMU_DISPLAY_BASE + DISP_CTRL) |= CTRL_UPDATE; +} diff --git a/src/fw/drivers/qemu/qemu_rtc_hal.c b/src/fw/drivers/qemu/qemu_rtc_hal.c new file mode 100644 index 0000000000..b3dffc3a86 --- /dev/null +++ b/src/fw/drivers/qemu/qemu_rtc_hal.c @@ -0,0 +1,185 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/rtc.h" + +#include "board/board.h" +#include "system/passert.h" +#include "system/logging.h" + +#include +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// RTC MMIO register offsets (must match QEMU pebble-rtc device) +#define RTC_TIME_LO 0x00 // Unix timestamp low 32 bits (r/w) +#define RTC_TIME_HI 0x04 // Unix timestamp high 32 bits (r) +#define RTC_ALARM 0x08 +#define RTC_CTRL 0x0C +#define RTC_TICKS_REG 0x10 // Monotonic 1000Hz tick counter (r) +#define RTC_BACKUP_BASE 0x40 + +// CTRL bits +#define CTRL_ALARM_IE (1 << 0) + +// STATUS bits +#define STATUS_ALARM_PENDING (1 << 0) + +// Number of backup registers +#define NUM_BACKUP_REGS 16 + +// Backup register access macros +#define RTC_BACKUP_REG(n) (QEMU_RTC_BASE + RTC_BACKUP_BASE + ((n) * 4)) + +// RTC_WriteBackupRegister / RTC_ReadBackupRegister compatibility +// These are used by the common RTC code (rtc/common.c) for timezone storage. +void RTC_WriteBackupRegister(uint32_t reg_id, uint32_t value) { + if (reg_id < NUM_BACKUP_REGS) { + REG32(RTC_BACKUP_REG(reg_id)) = value; + } +} + +uint32_t RTC_ReadBackupRegister(uint32_t reg_id) { + if (reg_id < NUM_BACKUP_REGS) { + return REG32(RTC_BACKUP_REG(reg_id)); + } + return 0; +} + +void rtc_init(void) { + // Clear alarm IRQ pending and disable alarm + REG32(QEMU_RTC_BASE + RTC_CTRL) = CTRL_ALARM_IE; // w1c the IRQ bit + REG32(QEMU_RTC_BASE + RTC_CTRL) = 0; +} + +void rtc_calibrate_frequency(uint32_t frequency) { + // No calibration needed for QEMU + (void)frequency; +} + +void rtc_init_timers(void) { + // No additional timers needed for QEMU RTC +} + +void rtc_set_time(time_t time) { + REG32(QEMU_RTC_BASE + RTC_TIME_LO) = (uint32_t)time; +} + +time_t rtc_get_time(void) { + return (time_t)REG32(QEMU_RTC_BASE + RTC_TIME_LO); +} + +void rtc_set_time_tm(struct tm *time_tm) { + time_t t = mktime(time_tm); + rtc_set_time(t); +} + +void rtc_get_time_tm(struct tm *time_tm) { + time_t t = rtc_get_time(); + localtime_r(&t, time_tm); +} + +void rtc_get_time_ms(time_t *out_seconds, uint16_t *out_ms) { + *out_seconds = rtc_get_time(); + // Use ticks to compute sub-second portion + uint32_t ticks = REG32(QEMU_RTC_BASE + RTC_TICKS_REG); + *out_ms = (uint16_t)(ticks % 1000); +} + +bool rtc_sanitize_struct_tm(struct tm *t) { + // Remember tm_year is years since 1900. + if (t->tm_year < 100) { + t->tm_year = 100; + return true; + } else if (t->tm_year > 137) { + t->tm_year = 137; + return true; + } + return false; +} + +bool rtc_sanitize_time_t(time_t *t) { + struct tm time_struct; + gmtime_r(t, &time_struct); + + const bool result = rtc_sanitize_struct_tm(&time_struct); + *t = mktime(&time_struct); + + return result; +} + +RtcTicks rtc_get_ticks(void) { + // Read 32-bit tick counter from QEMU (1000Hz) + return (RtcTicks)REG32(QEMU_RTC_BASE + RTC_TICKS_REG); +} + +void rtc_alarm_init(void) { + // Enable alarm interrupt support +} + +void rtc_alarm_set(RtcTicks num_ticks) { + RtcTicks current = rtc_get_ticks(); + REG32(QEMU_RTC_BASE + RTC_ALARM) = (uint32_t)(current + num_ticks); + REG32(QEMU_RTC_BASE + RTC_CTRL) |= CTRL_ALARM_IE; +} + +RtcTicks rtc_alarm_get_elapsed_ticks(void) { + return 0; +} + +bool rtc_alarm_is_initialized(void) { + return true; +} + +// Timezone uses backup registers 11-15 to avoid conflicts with bootbits (0-10) +#define TZ_BACKUP_BASE 11 + +void rtc_set_timezone(TimezoneInfo *tzinfo) { + uint32_t *raw = (uint32_t *)tzinfo; + _Static_assert(sizeof(TimezoneInfo) <= 5 * sizeof(uint32_t), + "RTC Set Timezone invalid data size"); + + RTC_WriteBackupRegister(TZ_BACKUP_BASE + 0, raw[0]); + RTC_WriteBackupRegister(TZ_BACKUP_BASE + 1, raw[1]); + RTC_WriteBackupRegister(TZ_BACKUP_BASE + 2, raw[2]); + RTC_WriteBackupRegister(TZ_BACKUP_BASE + 3, raw[3]); + RTC_WriteBackupRegister(TZ_BACKUP_BASE + 4, raw[4]); +} + +void rtc_get_timezone(TimezoneInfo *tzinfo) { + uint32_t *raw = (uint32_t *)tzinfo; + + raw[0] = RTC_ReadBackupRegister(TZ_BACKUP_BASE + 0); + raw[1] = RTC_ReadBackupRegister(TZ_BACKUP_BASE + 1); + raw[2] = RTC_ReadBackupRegister(TZ_BACKUP_BASE + 2); + raw[3] = RTC_ReadBackupRegister(TZ_BACKUP_BASE + 3); + raw[4] = RTC_ReadBackupRegister(TZ_BACKUP_BASE + 4); +} + +uint16_t rtc_get_timezone_id(void) { + // timezone_id is stored in the upper 16 bits of register 1 (matching common.c layout) + return ((RTC_ReadBackupRegister(TZ_BACKUP_BASE + 1) >> 16) & 0xFFFF); +} + +bool rtc_is_timezone_set(void) { + // True if the timezone abbreviation has been set + return (RTC_ReadBackupRegister(TZ_BACKUP_BASE + 0) != 0); +} + +const char *rtc_get_time_string(char *buffer) { + return time_t_to_string(buffer, rtc_get_time()); +} + +const char *time_t_to_string(char *buffer, time_t t) { + struct tm time; + localtime_r(&t, &time); + strftime(buffer, TIME_STRING_BUFFER_SIZE, "%c", &time); + return buffer; +} + +void rtc_timezone_clear(void) { + for (int i = 0; i < 5; i++) { + RTC_WriteBackupRegister(TZ_BACKUP_BASE + i, 0); + } +} diff --git a/src/fw/drivers/qemu/qemu_serial.c b/src/fw/drivers/qemu/qemu_serial.c index e3bd701332..ec4ca8c7ce 100644 --- a/src/fw/drivers/qemu/qemu_serial.c +++ b/src/fw/drivers/qemu/qemu_serial.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/qemu/qemu_accel.h" #include "drivers/qemu/qemu_battery.h" @@ -25,8 +12,8 @@ #include "popups/timeline/peek.h" #include "process_management/app_manager.h" #include "shell/system_theme.h" -#include "services/common/clock.h" -#include "services/common/system_task.h" +#include "pbl/services/clock.h" +#include "pbl/services/system_task.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/passert.h" @@ -54,11 +41,11 @@ static QemuSerialGlobals s_qemu_state; static void prv_tap_msg_callback(const uint8_t *data, uint32_t len) { QemuProtocolTapHeader *hdr = (QemuProtocolTapHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - QEMU_LOG_DEBUG("Got tap msg: axis: %d, direction: %d", hdr->axis, hdr->direction); + PBL_LOG_DBG("Got tap msg: axis: %d, direction: %d", hdr->axis, hdr->direction); PebbleEvent e = { .type = PEBBLE_ACCEL_SHAKE_EVENT, .accel_tap = { @@ -76,11 +63,11 @@ static void prv_tap_msg_callback(const uint8_t *data, uint32_t len) { static void prv_bluetooth_connection_msg_callback(const uint8_t *data, uint32_t len) { QemuProtocolBluetoothConnectionHeader *hdr = (QemuProtocolBluetoothConnectionHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - QEMU_LOG_DEBUG("Got bluetooth connection msg: connected:%d", hdr->connected); + PBL_LOG_DBG("Got bluetooth connection msg: connected:%d", hdr->connected); bool current_status = qemu_transport_is_connected(); bool new_status = (hdr->connected != 0); @@ -98,11 +85,11 @@ static void prv_bluetooth_connection_msg_callback(const uint8_t *data, uint32_t static void prv_compass_msg_callback(const uint8_t *data, uint32_t len) { QemuProtocolCompassHeader *hdr = (QemuProtocolCompassHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - QEMU_LOG_DEBUG("Got compass msg: magnetic_heading: %"PRId32", calib_status:%u", + PBL_LOG_DBG("Got compass msg: magnetic_heading: %"PRId32", calib_status:%u", ntohl(hdr->magnetic_heading), hdr->calib_status); PebbleEvent e = { .type = PEBBLE_COMPASS_DATA_EVENT, @@ -121,11 +108,11 @@ static void prv_compass_msg_callback(const uint8_t *data, uint32_t len) { static void prv_time_format_msg_callback(const uint8_t *data, uint32_t len) { QemuProtocolTimeFormatHeader *hdr = (QemuProtocolTimeFormatHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Got time format msg: is 24 hour: %d", hdr->is_24_hour); + PBL_LOG_DBG("Got time format msg: is 24 hour: %d", hdr->is_24_hour); clock_set_24h_style(hdr->is_24_hour); } @@ -135,12 +122,12 @@ static void prv_time_format_msg_callback(const uint8_t *data, uint32_t len) { static void prv_timeline_peek_msg_callback(const uint8_t *data, uint32_t len) { QemuProtocolTimelinePeekHeader *hdr = (QemuProtocolTimelinePeekHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Got timeline peek msg: enabled: %d", hdr->enabled); -#if !RECOVERY_FW && CAPABILITY_HAS_TIMELINE_PEEK + PBL_LOG_DBG("Got timeline peek msg: enabled: %d", hdr->enabled); +#if !defined(CONFIG_RECOVERY_FW) timeline_peek_set_enabled(hdr->enabled); #endif } @@ -149,12 +136,12 @@ static void prv_timeline_peek_msg_callback(const uint8_t *data, uint32_t len) { static void prv_content_size_msg_callback(const uint8_t *data, uint32_t len) { QemuProtocolContentSizeHeader *hdr = (QemuProtocolContentSizeHeader *)data; if (len != sizeof(*hdr)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid packet length"); + PBL_LOG_ERR("Invalid packet length"); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Got content size msg: size: %d", hdr->size); -#if !RECOVERY_FW + PBL_LOG_DBG("Got content size msg: size: %d", hdr->size); +#if !defined(CONFIG_RECOVERY_FW) system_theme_set_content_size(hdr->size); // Exit out of any currently running app so we force the UI to update to the new content size @@ -222,6 +209,14 @@ static void prv_process_receive_buffer(void *context) { uint32_t msg_bytes; uint16_t protocol; + // Clear the pending flag at entry so the next ISR-side trigger (footer + // sighting, buffer-full, recv error) can queue another drain callback. + // Without this, the flag stays true forever after the first callback, + // any later "interesting" ISR event silently drops its callback request, + // and once the ISR buffer fills the RX IRQ is disabled — no further drain + // happens and the protocol stack wedges mid-transfer. + s_qemu_state.callback_pending = false; + // Process ISR receive buffer, see if we have a complete message // Prevent our ISR from putting more characters in while we muck with the receive buffer by // disabling UART interrupts while we process it. @@ -234,11 +229,11 @@ static void prv_process_receive_buffer(void *context) { } // Dispatch the received message - PBL_LOG(LOG_LEVEL_DEBUG, "Dispatching msg of len %"PRIu32" for protocol %d", msg_bytes, + PBL_LOG_DBG("Dispatching msg of len %"PRIu32" for protocol %d", msg_bytes, protocol); const QemuMessageHandler* handler = prv_find_handler(protocol); if (!handler) { - PBL_LOG(LOG_LEVEL_WARNING, "No handler for protocol: %d", protocol); + PBL_LOG_WRN("No handler for protocol: %d", protocol); } else { handler->callback(msg_ptr, msg_bytes); } @@ -260,7 +255,7 @@ static bool prv_uart_irq_handler(UARTDevice *dev, uint8_t byte, const UARTRXErro bool success = shared_circular_buffer_write(&s_qemu_state.isr_buffer, &byte, 1, false/*advance_slackers*/); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "ISR buf too small 0x%x", byte); + PBL_LOG_ERR("ISR buf too small 0x%x", byte); s_qemu_state.recv_error_count++; } @@ -299,8 +294,8 @@ static bool prv_uart_irq_handler(UARTDevice *dev, uint8_t byte, const UARTRXErro // ----------------------------------------------------------------------------------------- static void prv_send(const uint8_t *data, uint32_t len) { - QEMU_LOG_DEBUG("Sending data:"); - QEMU_HEXDUMP(data, len); + PBL_LOG_VERBOSE("Sending data:"); + PBL_HEXDUMP(LOG_LEVEL_DEBUG_VERBOSE, data, len); while (len--) { uart_write_byte(QEMU_UART, *data++); diff --git a/src/fw/drivers/qemu/qemu_serial.h b/src/fw/drivers/qemu/qemu_serial.h index d42b2debca..462aa97369 100644 --- a/src/fw/drivers/qemu/qemu_serial.h +++ b/src/fw/drivers/qemu/qemu_serial.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/qemu/qemu_serial_private.h b/src/fw/drivers/qemu/qemu_serial_private.h index eb2abf2484..58b8ad974c 100644 --- a/src/fw/drivers/qemu/qemu_serial_private.h +++ b/src/fw/drivers/qemu/qemu_serial_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -95,13 +82,6 @@ typedef struct { #define QEMU_HEADER_LSB ((uint8_t)(QEMU_HEADER_SIGNATURE & 0x00FF)) -#define QEMU_LOG_DEBUG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_QEMU_COMM, LOG_LEVEL_DEBUG, fmt, ## args) -#define QEMU_LOG_ERROR(fmt, args...) PBL_LOG_D(LOG_DOMAIN_QEMU_COMM, LOG_LEVEL_ERROR, fmt, ## args) -#define QEMU_HEXDUMP(data, length) \ - PBL_HEXDUMP_D(LOG_DOMAIN_QEMU_COMM, LOG_LEVEL_DEBUG, data, length) - - - // ----------------------------------------------------------------------------------- void qemu_serial_private_init_state(QemuSerialGlobals *state); diff --git a/src/fw/drivers/qemu/qemu_serial_util.c b/src/fw/drivers/qemu/qemu_serial_util.c index e5b16a585b..1932d21c13 100644 --- a/src/fw/drivers/qemu/qemu_serial_util.c +++ b/src/fw/drivers/qemu/qemu_serial_util.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/pbl_malloc.h" @@ -74,19 +61,19 @@ uint8_t *qemu_serial_private_assemble_message(QemuSerialGlobals *state, uint32_t if (state->recv_state != QemuRecvState_WaitingHdrSignatureMSB && cur_time > state->start_recv_packet_time + QEMU_RECV_PACKET_TIMEOUT_SEC) { state->recv_state = QemuRecvState_WaitingHdrSignatureMSB; - PBL_LOG(LOG_LEVEL_WARNING, "Resetting receive state - max packet time expired"); + PBL_LOG_WRN("Resetting receive state - max packet time expired"); } state->callback_pending = false; uint16_t bytes_avail = shared_circular_buffer_get_read_space_remaining(&state->isr_buffer, &state->isr_buffer_client); - QEMU_LOG_DEBUG("prv_assemble_packet, state:%d, bytes:%d", state->recv_state, + PBL_LOG_VERBOSE("prv_assemble_packet, state:%d, bytes:%d", state->recv_state, bytes_avail); // Log message if we detected any receive errors if (state->recv_error_count) { - PBL_LOG(LOG_LEVEL_ERROR, "%"PRIu32" receive errors detected", state->recv_error_count); + PBL_LOG_ERR("%"PRIu32" receive errors detected", state->recv_error_count); state->recv_error_count = 0; } @@ -98,7 +85,7 @@ uint8_t *qemu_serial_private_assemble_message(QemuSerialGlobals *state, uint32_t &bytes_read); bytes_avail -= bytes_read; if (byte == QEMU_HEADER_MSB) { - QEMU_LOG_DEBUG("got header signature MSB"); + PBL_LOG_VERBOSE("got header signature MSB"); state->recv_state = QemuRecvState_WaitingHdrSignatureLSB; state->start_recv_packet_time = cur_time; } @@ -111,7 +98,7 @@ uint8_t *qemu_serial_private_assemble_message(QemuSerialGlobals *state, uint32_t bytes_avail -= bytes_read; if (byte == QEMU_HEADER_LSB) { state->recv_state = QemuRecvState_WaitingHdr; - QEMU_LOG_DEBUG("got header signature LSB"); + PBL_LOG_VERBOSE("got header signature LSB"); } else { state->recv_state = QemuRecvState_WaitingHdr; } @@ -136,10 +123,10 @@ uint8_t *qemu_serial_private_assemble_message(QemuSerialGlobals *state, uint32_t // Validity checking if (state->hdr.len > QEMU_MAX_DATA_LEN) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid header data size %d", state->hdr.len); + PBL_LOG_ERR("Invalid header data size %d", state->hdr.len); state->recv_state = QemuRecvState_WaitingHdrSignatureMSB; } else { - QEMU_LOG_DEBUG("got header: protocol: %d, len: %d", state->hdr.protocol, state->hdr.len); + PBL_LOG_VERBOSE("got header: protocol: %d, len: %d", state->hdr.protocol, state->hdr.len); state->recv_state = QemuRecvState_WaitingData; } } @@ -153,7 +140,7 @@ uint8_t *qemu_serial_private_assemble_message(QemuSerialGlobals *state, uint32_t state->msg_buffer_bytes += bytes_read; bytes_avail -= bytes_read; - QEMU_LOG_DEBUG("received %d bytes of msg data, need %d more", bytes_read, + PBL_LOG_VERBOSE("received %d bytes of msg data, need %d more", bytes_read, state->hdr.len - state->msg_buffer_bytes); // Got the complete message? @@ -175,7 +162,7 @@ uint8_t *qemu_serial_private_assemble_message(QemuSerialGlobals *state, uint32_t bytes_avail -= bytes_read; footer.signature = ntohs(footer.signature); if (footer.signature != QEMU_FOOTER_SIGNATURE) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid footer signature"); + PBL_LOG_WRN("Invalid footer signature"); } state->recv_state = QemuRecvState_WaitingHdrSignatureMSB; } diff --git a/src/fw/drivers/qemu/qemu_settings.c b/src/fw/drivers/qemu/qemu_settings.c index a7d87530f9..bd00c39c33 100644 --- a/src/fw/drivers/qemu/qemu_settings.c +++ b/src/fw/drivers/qemu/qemu_settings.c @@ -1,31 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/qemu/qemu_settings.h" #include "system/passert.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - #include #include #include +#include "board/board.h" + +// QEMU RTC backup registers at RTC_BASE + 0x40 +#define QEMU_RTC_BACKUP_BASE (QEMU_RTC_BASE + 0x40) +#define QEMU_BACKUP_REG(n) (*(volatile uint32_t *)(QEMU_RTC_BACKUP_BASE + (n) * 4)) // QEMU backup registers and bit indices. These are also defined in the qemu project in // hw/arm/pebble.c @@ -36,6 +23,9 @@ // ------------------------------------------------------------------------------------- // Read a QEMU specific register from the RTC backup register area static uint32_t prv_rtc_read_qemu_register(uint32_t qemu_register) { +#if defined(CONFIG_QEMU) + return QEMU_BACKUP_REG(qemu_register); +#else __IO uint32_t tmp = 0; // The first qemu_register (0) starts 1 past the implemented registers in the STM @@ -46,6 +36,7 @@ static uint32_t prv_rtc_read_qemu_register(uint32_t qemu_register) { // Read the specified register return (*(__IO uint32_t *)tmp); +#endif } diff --git a/src/fw/drivers/qemu/qemu_settings.h b/src/fw/drivers/qemu/qemu_settings.h index bc07b64176..70f35513ca 100644 --- a/src/fw/drivers/qemu/qemu_settings.h +++ b/src/fw/drivers/qemu/qemu_settings.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/qemu/qemu_touch.c b/src/fw/drivers/qemu/qemu_touch.c index 5ca45ec473..20f6812022 100644 --- a/src/fw/drivers/qemu/qemu_touch.c +++ b/src/fw/drivers/qemu/qemu_touch.c @@ -1,18 +1,68 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "drivers/touch/touch_sensor.h" +#include "pbl/services/system_task.h" +#include "pbl/services/touch/touch.h" + +#include "FreeRTOS.h" + +#include +#include +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// QEMU touch register offsets (must match pebble-touch device) +#define TOUCH_STATE 0x00 +#define TOUCH_X 0x04 +#define TOUCH_Y 0x08 +#define TOUCH_INTCTRL 0x0C +#define TOUCH_INTSTAT 0x10 + +#define INT_TOUCH_EVENT (1u << 0) + +static bool s_callback_scheduled = false; + +static void prv_process_touch_update(void *unused) { + s_callback_scheduled = false; + + const uint32_t base = QEMU_TOUCH_BASE; + const uint32_t state = REG32(base + TOUCH_STATE); + const int16_t x = (int16_t)REG32(base + TOUCH_X); + const int16_t y = (int16_t)REG32(base + TOUCH_Y); + + if (state & INT_TOUCH_EVENT) { + touch_handle_update(TouchState_FingerDown, x, y); + } else { + touch_handle_update(TouchState_FingerUp, x, y); + } +} + +void TOUCH_IRQHandler(void) { + REG32(QEMU_TOUCH_BASE + TOUCH_INTSTAT) = INT_TOUCH_EVENT; + + bool should_context_switch = false; + if (!s_callback_scheduled) { + if (system_task_add_callback_from_isr(prv_process_touch_update, NULL, + &should_context_switch)) { + s_callback_scheduled = true; + } + } + portEND_SWITCHING_ISR(should_context_switch); +} void touch_sensor_init(void) { + const uint32_t base = QEMU_TOUCH_BASE; + + REG32(base + TOUCH_INTSTAT) = INT_TOUCH_EVENT; + REG32(base + TOUCH_INTCTRL) = INT_TOUCH_EVENT; + + NVIC_SetPriority(TOUCH_IRQn, 6); + NVIC_EnableIRQ(TOUCH_IRQn); +} + +void touch_sensor_set_enabled(bool enabled) { + REG32(QEMU_TOUCH_BASE + TOUCH_INTCTRL) = enabled ? INT_TOUCH_EVENT : 0; } diff --git a/src/fw/drivers/qemu/qemu_vibe.c b/src/fw/drivers/qemu/qemu_vibe.c new file mode 100644 index 0000000000..da6679d5d8 --- /dev/null +++ b/src/fw/drivers/qemu/qemu_vibe.c @@ -0,0 +1,70 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/vibe.h" + +#include "drivers/qemu/qemu_serial.h" +#include "console/prompt.h" + +#include + +static bool s_vibe_on; + +void vibe_init(void) { + s_vibe_on = false; +} + +void vibe_set_strength(int8_t strength) { + (void)strength; +} + +void vibe_ctl(bool on) { + if (s_vibe_on == on) { + return; + } + s_vibe_on = on; + + // Notify QEMU host of vibration state change + QemuProtocolVibrationNotificationHeader notification = { + .on = on ? 1 : 0, + }; + qemu_serial_send(QemuProtocol_Vibration, + (const uint8_t *)¬ification, + sizeof(notification)); +} + +void vibe_force_off(void) { + vibe_ctl(false); +} + +int8_t vibe_get_braking_strength(void) { + return VIBE_STRENGTH_OFF; +} + +status_t vibe_calibrate(void) { + return E_INVALID_OPERATION; +} + +uint8_t vibe_get_calibration(void) { + return 0xFF; +} + +void vibe_apply_calibration(uint8_t cali) { +} + +void command_vibe_ctl(const char *arg) { + int strength = atoi(arg); + + const bool out_of_bounds = ((strength < 0) || (strength > VIBE_STRENGTH_MAX)); + const bool not_a_number = (strength == 0 && arg[0] != '0'); + if (out_of_bounds || not_a_number) { + prompt_send_response("Invalid argument"); + return; + } + + vibe_set_strength(strength); + + const bool turn_on = strength != 0; + vibe_ctl(turn_on); + prompt_send_response("OK"); +} diff --git a/src/fw/drivers/qspi.h b/src/fw/drivers/qspi.h index 928bb9d66f..09a429214e 100644 --- a/src/fw/drivers/qspi.h +++ b/src/fw/drivers/qspi.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/qspi_definitions.h b/src/fw/drivers/qspi_definitions.h index 147fd67291..cbba8e4d6e 100644 --- a/src/fw/drivers/qspi_definitions.h +++ b/src/fw/drivers/qspi_definitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,7 +10,7 @@ #include #include -#if MICRO_FAMILY_SF32LB52 +#ifdef CONFIG_SOC_SF32LB52 #include "bf0_hal_dma.h" #include "bf0_hal_mpi.h" #endif @@ -31,9 +18,16 @@ #define QSPI_NUM_DATA_PINS (4) typedef struct QSPIPortState { -#if MICRO_FAMILY_SF32LB52 +#ifdef CONFIG_SOC_NRF52 + SemaphoreHandle_t sem; + bool initialized; +#elif defined(CONFIG_SOC_SF32LB52) QSPI_FLASH_CTX_T ctx; DMA_HandleTypeDef hdma; + qspi_configure_t cfg; + struct dma_config dma; + uint32_t t_enter_deep_us; + uint32_t t_exit_deep_us; bool initialized; #else SemaphoreHandle_t dma_semaphore; @@ -43,15 +37,13 @@ typedef struct QSPIPortState { typedef const struct QSPIPort { QSPIPortState *state; -#if MICRO_FAMILY_NRF5 - uint16_t auto_polling_interval; +#ifdef CONFIG_SOC_NRF52 + uint32_t clk_freq_hz; uint32_t cs_gpio; uint32_t clk_gpio; uint32_t data_gpio[QSPI_NUM_DATA_PINS]; -#elif MICRO_FAMILY_SF32LB52 - qspi_configure_t cfg; +#elif defined(CONFIG_SOC_SF32LB52) uint16_t clk_div; - struct dma_config dma; #else uint16_t auto_polling_interval; uint32_t clock_speed_hz; diff --git a/src/fw/drivers/rng.h b/src/fw/drivers/rng.h index 81529e2448..6e09823b88 100644 --- a/src/fw/drivers/rng.h +++ b/src/fw/drivers/rng.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include -//! @param rand_out Storage for the 32-bit random number generated by the STM32 +//! @param rand_out Storage for the 32-bit random number generated //! @return True if a random number was succesfully generated bool rng_rand(uint32_t *rand_out); diff --git a/src/fw/drivers/rng/Kconfig b/src/fw/drivers/rng/Kconfig new file mode 100644 index 0000000000..eec2de9775 --- /dev/null +++ b/src/fw/drivers/rng/Kconfig @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig RNG + bool "Random Number Generator" + help + RNG Drivers + +if RNG + +config RNG_SF32LB + bool "SiFli SF32LB" + select HAL_SIFLI_RNG + help + Support for the SiFli SF32LB RNG. + +config RNG_STUB + bool "Stub" + help + Stub RNG implementation. + +module = DRIVER_RNG +module-str = Random Number Generator +source "src/fw/Kconfig.template.log_level" + +endif # RNG diff --git a/src/fw/drivers/rng/sf32lb.c b/src/fw/drivers/rng/sf32lb.c new file mode 100644 index 0000000000..fb6ed52921 --- /dev/null +++ b/src/fw/drivers/rng/sf32lb.c @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/rng.h" +#include "system/logging.h" +#include "system/passert.h" + +#include "bf0_hal_rcc.h" +#include "bf0_hal_rng.h" + +PBL_LOG_MODULE_DEFINE(driver_rng_sf32lb, CONFIG_DRIVER_RNG_LOG_LEVEL); + +static bool s_inited; +static RNG_HandleTypeDef s_rng_hdl = { + .Instance = hwp_trng, +}; + +bool rng_rand(uint32_t *rand_out) { + HAL_StatusTypeDef status; + + HAL_RCC_EnableModule(RCC_MOD_TRNG); + + if (!s_inited) { + status = HAL_RNG_Init(&s_rng_hdl); + PBL_ASSERTN(status == HAL_OK); + + status = HAL_RNG_GenerateRandomSeed(&s_rng_hdl, rand_out); + PBL_ASSERTN(status == HAL_OK); + + s_inited = true; + } + + status = HAL_RNG_GenerateRandomNumber(&s_rng_hdl, rand_out); + if (status != HAL_OK) { + PBL_LOG_ERR("HAL_RNG_GenerateRandomNumber failed: %d", status); + } + + HAL_RCC_DisableModule(RCC_MOD_TRNG); + + return status == HAL_OK; +} diff --git a/src/fw/drivers/rng/stubs.c b/src/fw/drivers/rng/stubs.c new file mode 100644 index 0000000000..0f2707f296 --- /dev/null +++ b/src/fw/drivers/rng/stubs.c @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/rng.h" + +bool rng_rand(uint32_t *rand_out) { + return false; +} diff --git a/src/fw/drivers/rng/wscript_build b/src/fw/drivers/rng/wscript_build new file mode 100644 index 0000000000..6411f018c2 --- /dev/null +++ b/src/fw/drivers/rng/wscript_build @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_RNG_SF32LB: + sources.append('sf32lb.c') + use.append('hal_sifli') +elif bld.env.CONFIG_RNG_STUB: + sources.append('stubs.c') + +bld.objects( + name='driver_rng', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_rng') diff --git a/src/fw/drivers/rtc.h b/src/fw/drivers/rtc.h index ffd7e2bc70..9198ab9dd7 100644 --- a/src/fw/drivers/rtc.h +++ b/src/fw/drivers/rtc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,11 +10,14 @@ typedef uint64_t RtcTicks; -#if !defined(MICRO_FAMILY_SF32LB52) -#define RTC_TICKS_HZ (1024u) -#else +#if defined(CONFIG_QEMU) +// QEMU RTC provides a 1000Hz tick counter. +#define RTC_TICKS_HZ (1000u) +#elif defined(CONFIG_SOC_SF32LB52) // SF32lb52 lptim using RC10K. #define RTC_TICKS_HZ (1000u) +#else +#define RTC_TICKS_HZ (1024u) #endif //! Initialize the RTC driver at startup. Note that this runs very early in the startup process @@ -120,7 +110,7 @@ bool rtc_alarm_is_initialized(void); //! @param buffer Buffer used to write the string into. Must be at least TIME_STRING_BUFFER_SIZE const char* time_t_to_string(char* buffer, time_t t); -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 void rtc_irq_handler(void); void rtc_enable_synthetic_systick(void); void rtc_systick_pause(void); diff --git a/src/fw/drivers/rtc/Kconfig b/src/fw/drivers/rtc/Kconfig new file mode 100644 index 0000000000..c7269f3c17 --- /dev/null +++ b/src/fw/drivers/rtc/Kconfig @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig RTC + bool "RTC" + help + RTC Drivers + +if RTC + +config RTC_NRF5 + bool "Nordic nRF5" + help + Support for the Nordic nRF5 RTC. + +config RTC_SF32LB + bool "SiFli SF32LB" + select HAL_SIFLI_LRC_CAL + select HAL_SIFLI_RTC + help + Support for the SiFli SF32LB RTC. + +config RTC_QEMU + bool "QEMU RTC" + help + Support for the QEMU RTC peripheral. + +module = DRIVER_RTC +module-str = RTC +source "src/fw/Kconfig.template.log_level" + +endif # RTC diff --git a/src/fw/drivers/rtc/nrf5.c b/src/fw/drivers/rtc/nrf5.c new file mode 100644 index 0000000000..ce40357152 --- /dev/null +++ b/src/fw/drivers/rtc/nrf5.c @@ -0,0 +1,464 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/rtc.h" + +#include "console/dbgserial.h" + +#include "drivers/exti.h" +#include "drivers/periph_config.h" +#include "drivers/watchdog.h" +#include "drivers/task_watchdog.h" + +#include "kernel/util/stop.h" +#include "mcu/interrupts.h" + +#include "pbl/services/regular_timer.h" + +#include "system/bootbits.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/reset.h" + +#include "util/time/time.h" + +#include "FreeRTOS.h" + +#include + +#include +#include +#include + +PBL_LOG_MODULE_DEFINE(driver_rtc_nrf5, CONFIG_DRIVER_RTC_LOG_LEVEL); + +//! The type of a raw reading from the RTC (masked to 0xFFFFFF). +typedef uint32_t RtcIntervalTicks; + +//! How frequently we save the time state to the backup registers in ticks. +#define SAVE_TIME_FREQUENCY (30 * RTC_TICKS_HZ) +static RtcIntervalTicks prv_get_last_save_time_ticks(void); +static void prv_save_rtc_time_state(RtcIntervalTicks current_rtc_ticks); + +#define TICKS_IN_AN_INTERVAL (RTC_COUNTER_COUNTER_Msk + 1) + +static RtcIntervalTicks prv_elapsed_ticks(RtcIntervalTicks before, RtcIntervalTicks after) { + int32_t result = after - before; + if (result < 0) { + result = (TICKS_IN_AN_INTERVAL - before) + after; + } + return result; +} + +static RtcIntervalTicks prv_get_rtc_interval_ticks(void) { + return nrf_rtc_counter_get(BOARD_RTC_INST); +} + +/*** + * Logic associated with keeping raw coarse / fine RTC ticks -- the + * monotonic RtcTicks counter. + */ + +//! The value of the RTC registers last time we checked them. +static uint32_t s_last_ticks = 0; +//! This value is added to the current value of the RTC ticks to get the number +//! of ticks since system start. Incremented whenever we detect a rollover. +static RtcTicks s_coarse_ticks = 1; + +static bool s_did_init_rtc = false; + +//! did we boot with a full reset that brought RTC ticks to 0? +static bool s_had_amnesia_on_boot = false; + +static void prv_check_and_handle_rollover(RtcIntervalTicks rtc_ticks) { + bool save_needed = false; + + const RtcIntervalTicks last_ticks = s_last_ticks; + s_last_ticks = rtc_ticks; + + if (rtc_ticks < last_ticks) { + // We've wrapped. Add on the RTC wrap length to the base number. On + // nRF5, this is 0xFFFFFF; that's only 4.5 hours (at 1.024 kHz), + // compared to STM32's available SECONDS_IN_A_DAY. Sucks for us; oh + // well. + + s_coarse_ticks += TICKS_IN_AN_INTERVAL; + + save_needed = true; + } else if (prv_elapsed_ticks(prv_get_last_save_time_ticks(), rtc_ticks) > SAVE_TIME_FREQUENCY) { + // If we didn't do this, we would have an edge case where if the watch reset + // immediately before rollover and then rolled over before we booted again, + // we wouldn't be able to detect the rollover and we'd think the saved state + // is very fresh, when really it's over an interval old. By saving multiple + // times an interval this is still possible to happen, but it's much less likely. + // We would need to be shutdown for (RTC_COUNTER_COUNTER_Msk - SAVE_TIME_FREQUENCY) ticks + // for this to happen. + save_needed = true; + } + + if (save_needed) { + prv_save_rtc_time_state(rtc_ticks); + } +} + +RtcTicks rtc_get_ticks(void) { + // Prevent this from being interrupted + bool ints_enabled = mcu_state_are_interrupts_enabled(); + if (ints_enabled) { + __disable_irq(); + } + + RtcTicks rtc_interval_ticks = prv_get_rtc_interval_ticks(); + prv_check_and_handle_rollover(rtc_interval_ticks); + + if (ints_enabled) { + __enable_irq(); + } + + return s_coarse_ticks + rtc_interval_ticks; +} + +/*** + * Logic associated with converting extended RTC ticks to wall clock time. + */ + +//! This variable is a UNIX timestamp of what the current wall clock time was at tick s_time_tick_base. +static time_t s_time_base = 0; +//! This variable is the tick where the wall clock time was equal to s_time_base. If you subtract this variable +//! from the current tick count, you'll get the number of ticks that have elapsed since s_time_base, which will +//! allow you to calculate the current wall clock time. Note that this value may be negative on startup, see +//! prv_restore_rtc_time_state +static int64_t s_time_tick_base = 0; + +static time_t prv_ticks_to_time(RtcTicks ticks) { + return s_time_base + ((ticks - s_time_tick_base) / RTC_TICKS_HZ); +} + +void rtc_set_time(time_t time) { +#ifdef CONFIG_LOG + char buffer[TIME_STRING_BUFFER_SIZE]; + PBL_LOG_INFO("Setting time to %lu <%s>", time, time_t_to_string(buffer, time)); +#endif + + s_time_base = time; + s_time_tick_base = rtc_get_ticks(); + + prv_save_rtc_time_state(s_time_tick_base - s_coarse_ticks); +} + +time_t rtc_get_time(void) { + return prv_ticks_to_time(rtc_get_ticks()); +} + +void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) { + RtcTicks ticks = rtc_get_ticks(); + + RtcTicks ticks_since_time_base = (ticks - s_time_tick_base); + *out_seconds = s_time_base + (ticks_since_time_base / RTC_TICKS_HZ); + + RtcTicks ticks_this_second = ticks_since_time_base % RTC_TICKS_HZ; + *out_ms = (ticks_this_second * 1000) / RTC_TICKS_HZ; +} + +/*** + * Logic associated with saving the RTC-tick-to-wallclock conversion factor + * to retained-RAM. + */ + +static void prv_restore_rtc_time_state(void) { + // Recover the previously set time from the RTC backup registers. + RtcIntervalTicks last_save_time_ticks = retained_read(CURRENT_INTERVAL_TICKS_REGISTER); + time_t last_save_time = retained_read(CURRENT_TIME_REGISTER); + + if (s_had_amnesia_on_boot) { + /* We have no idea what time it might be. The closest we got is the + * last time we saved. */ + s_time_base = last_save_time; + s_time_tick_base = 0; + PBL_LOG_INFO("Restore RTC: we are on our way up with amnesia"); + } else { + RtcIntervalTicks current_ticks = prv_get_rtc_interval_ticks(); + const int32_t ticks_since_last_save = prv_elapsed_ticks(last_save_time_ticks * RTC_TICKS_HZ, current_ticks); + s_time_base = last_save_time + (ticks_since_last_save / RTC_TICKS_HZ); + s_time_tick_base = -(((int64_t)current_ticks) % RTC_TICKS_HZ); + PBL_LOG_INFO("Restore RTC: we are on our way up with interval_ticks = %"PRIu32, current_ticks); + PBL_LOG_INFO("Restore RTC: saved: %"PRIu32" diff: %"PRIu32, last_save_time_ticks, ticks_since_last_save); + } + + char buffer[TIME_STRING_BUFFER_SIZE]; + PBL_LOG_INFO("Restore RTC: saved_time: %s raw: %lu", time_t_to_string(buffer, last_save_time), last_save_time); + PBL_LOG_INFO("Restore RTC: current time: %s", time_t_to_string(buffer, s_time_base)); +} + +static RtcIntervalTicks prv_get_last_save_time_ticks(void) { + return retained_read(CURRENT_INTERVAL_TICKS_REGISTER); +} + +static void prv_save_rtc_time_state_exact(RtcIntervalTicks current_rtc_ticks, time_t time) { + retained_write(CURRENT_TIME_REGISTER, time); + retained_write(CURRENT_INTERVAL_TICKS_REGISTER, current_rtc_ticks); +} + +static void prv_save_rtc_time_state(RtcIntervalTicks current_rtc_ticks) { + if (!s_did_init_rtc) { + return; + } + + // Floor it to the latest second + const RtcIntervalTicks current_rtc_ticks_at_second = (current_rtc_ticks / RTC_TICKS_HZ) * RTC_TICKS_HZ; + + prv_save_rtc_time_state_exact(current_rtc_ticks_at_second, prv_ticks_to_time(s_coarse_ticks + current_rtc_ticks)); +} + +/*** Logic that ought be refactored into rtc_common, were it not stm32-only. ***/ + +bool rtc_sanitize_struct_tm(struct tm *t) { + // These values come from time_t (which suffers from the 2038 problem) and our hardware which + // only stores a 2 digit year, so we only represent values after 2000. + + // Remember tm_year is years since 1900. + if (t->tm_year < 100) { + // Bump it up to the year 2000 to work with our hardware. + t->tm_year = 100; + return true; + } else if (t->tm_year > 137) { + t->tm_year = 137; + return true; + } + return false; +} + +bool rtc_sanitize_time_t(time_t *t) { + struct tm time_struct; + gmtime_r(t, &time_struct); + + const bool result = rtc_sanitize_struct_tm(&time_struct); + *t = mktime(&time_struct); + + return result; +} + +void rtc_get_time_tm(struct tm* time_tm) { + time_t t = rtc_get_time(); + localtime_r(&t, time_tm); +} + +const char *rtc_get_time_string(char *buffer) { + return time_t_to_string(buffer, rtc_get_time()); +} + +const char *time_t_to_string(char *buffer, time_t t) { + struct tm time; + localtime_r(&t, &time); + + strftime(buffer, TIME_STRING_BUFFER_SIZE, "%c", &time); + + return buffer; +} + + +//! We attempt to save registers by placing both the timezone abbreviation +//! timezone index and the daylight_savingtime into the same register set +void rtc_set_timezone(TimezoneInfo *tzinfo) { + uint32_t *raw = (uint32_t*)tzinfo; + _Static_assert(sizeof(TimezoneInfo) <= 5 * sizeof(uint32_t), + "RTC Set Timezone invalid data size"); + + retained_write(RTC_TIMEZONE_ABBR_START, raw[0]); + retained_write(RTC_TIMEZONE_ABBR_END_TZID_DSTID, raw[1]); + retained_write(RTC_TIMEZONE_GMTOFFSET, raw[2]); + retained_write(RTC_TIMEZONE_DST_START, raw[3]); + retained_write(RTC_TIMEZONE_DST_END, raw[4]); +} + +void rtc_get_timezone(TimezoneInfo *tzinfo) { + uint32_t *raw = (uint32_t*)tzinfo; + + raw[0] = retained_read(RTC_TIMEZONE_ABBR_START); + raw[1] = retained_read(RTC_TIMEZONE_ABBR_END_TZID_DSTID); + raw[2] = retained_read(RTC_TIMEZONE_GMTOFFSET); + raw[3] = retained_read(RTC_TIMEZONE_DST_START); + raw[4] = retained_read(RTC_TIMEZONE_DST_END); +} + +void rtc_timezone_clear(void) { + retained_write(RTC_TIMEZONE_ABBR_START, 0); + retained_write(RTC_TIMEZONE_ABBR_END_TZID_DSTID, 0); + retained_write(RTC_TIMEZONE_GMTOFFSET, 0); + retained_write(RTC_TIMEZONE_DST_START, 0); + retained_write(RTC_TIMEZONE_DST_END, 0); +} + +uint16_t rtc_get_timezone_id(void) { + return ((retained_read(RTC_TIMEZONE_ABBR_END_TZID_DSTID) >> 16) & 0xFFFF); +} + +bool rtc_is_timezone_set(void) { + // True if the timezone abbreviation has been set (including UNK for unknown) + return (retained_read(RTC_TIMEZONE_ABBR_START) != 0); +} + +void rtc_enable_backup_regs(void) { + /* we always use retained ram for this, so no problem */ +} + +void rtc_calibrate_frequency(uint32_t frequency) { + /* On nRF5, there is no way to calibrate the RTC. That crystal had better + * be accurate! + */ +} + +void rtc_init(void) { + /* May have been initialized out of sequence by FreeRTOS startup -- if so, + * no worries. */ + if (s_did_init_rtc) { + return; + } + +#ifndef NRF_RTC_FREQ_TO_PRESCALER +#define NRF_RTC_FREQ_TO_PRESCALER RTC_FREQ_TO_PRESCALER +#endif + if (prv_get_rtc_interval_ticks() == 0) { + s_had_amnesia_on_boot = true; + PBL_LOG_INFO("RTC appears to have been reset :( hope you have your phone connected"); + } + + nrf_rtc_prescaler_set(BOARD_RTC_INST, NRF_RTC_FREQ_TO_PRESCALER(RTC_TICKS_HZ)); + + nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); + nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); + nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); + nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE1_MASK); + nrf_rtc_cc_set(BOARD_RTC_INST, 1, (RTC_TICKS_HZ / (1000 / TASK_WATCHDOG_FEED_PERIOD_MS)) - 1); + + nrf_rtc_task_trigger(BOARD_RTC_INST, NRF_RTC_TASK_START); + + prv_restore_rtc_time_state(); + s_did_init_rtc = true; + + NVIC_SetPriority(BOARD_RTC_IRQN, configKERNEL_INTERRUPT_PRIORITY); + NVIC_EnableIRQ(BOARD_RTC_IRQN); + +#if TEST_RTC_FREQ + // FIXME: can be removed after FIRM-121 is fixed + extern void board_early_init(); + board_early_init(); + for (int i = 0; i < 100; i++) { + uint32_t ctr0 = nrf_rtc_counter_get(BOARD_RTC_INST); + while (nrf_rtc_counter_get(BOARD_RTC_INST) == ctr0) + ; + ctr0 = nrf_rtc_counter_get(BOARD_RTC_INST) + 100; + uint32_t iters = 0; + while (nrf_rtc_counter_get(BOARD_RTC_INST) != ctr0) + iters++; + PBL_LOG_INFO("RTC: 100 RTC ticks took %"PRIu32" iters", iters); + } +#endif +} + +void rtc_enable_synthetic_systick(void) { + // Now that the RTC is awake, we can switch from SysTick to RTC interrupt + // ticks. We need to do this so that we actually get ticks in wfi, since + // nRF5 stops SysTick in sleep. + _Static_assert(RTC_TICKS_HZ == configTICK_RATE_HZ); + if (!s_did_init_rtc) { + rtc_init(); + } + + nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); + nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_TICK_MASK); +} + +void rtc_systick_pause(void) { + // We don't want the fine-grained interrupts at 100Hz when we're in stop + // mode -- we have a timer set for that, after all. + nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); + nrf_rtc_int_disable(BOARD_RTC_INST, NRF_RTC_INT_TICK_MASK); +} + +void rtc_systick_resume(void) { + nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); + nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); + nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_TICK_MASK); +} + +//! Our RTC tick counter can overflow if nobody asks about it. This +//! repeating callback allows us to make sure this doesn't happen. +static void prv_rtc_resync_timer_callback() { + rtc_get_ticks(); +} + +void rtc_init_timers(void) { + static RegularTimerInfo rtc_sync_timer = { .list_node = { 0, 0 }, .cb = prv_rtc_resync_timer_callback}; + regular_timer_add_minutes_callback(&rtc_sync_timer); +} + +/*** Logic to control RTC wakeup timer. ***/ + +static RtcTicks s_alarm_set_time = 0; +static RtcTicks s_alarm_expiry_time = 0; +static bool s_tick_alarm_initialized = false; + +void rtc_alarm_init(void) { + nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); + nrf_rtc_int_disable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE0_MASK); + s_tick_alarm_initialized = true; +} + +void rtc_alarm_set(RtcTicks num_ticks) { + PBL_ASSERTN(s_tick_alarm_initialized); + + nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); + nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); + + s_alarm_set_time = rtc_get_ticks(); + s_alarm_expiry_time = s_alarm_set_time + num_ticks - 1; + + /* We're bounded by the regular_timer_add_minutes_callback for the + * rtc_alarm_set, so we're not going to wrap around more than once -- one + * minute is always less than 4.5 hours. + */ + nrf_rtc_cc_set(BOARD_RTC_INST, 0, s_alarm_expiry_time & RTC_COUNTER_COUNTER_Msk); + + nrf_rtc_event_enable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); + nrf_rtc_int_enable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE0_MASK); +} + +RtcTicks rtc_alarm_get_elapsed_ticks(void) { + return rtc_get_ticks() - s_alarm_set_time; +} + +bool rtc_alarm_is_initialized(void) { + return s_tick_alarm_initialized; +} + +//! Handler for the RTC alarm interrupt. We don't actually have to do +//! anything in that handler, just the interrupt firing is enough to bring +//! us out of stop mode. But once we switch into RTC-provided-systick mode, +//! we *do* need to call into systick! +void rtc_irq_handler(void) { + if (nrf_rtc_event_check(BOARD_RTC_INST, NRF_RTC_EVENT_TICK)) { + extern void SysTick_Handler(); + + nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_TICK); + SysTick_Handler(); + } + + if (nrf_rtc_event_check(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0)) { + nrf_rtc_event_disable(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); + nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_0); + nrf_rtc_int_disable(BOARD_RTC_INST, NRF_RTC_INT_COMPARE0_MASK); + } + + if (nrf_rtc_event_check(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1)) { + task_watchdog_feed(); + + nrf_rtc_event_clear(BOARD_RTC_INST, NRF_RTC_EVENT_COMPARE_1); + + uint32_t next_feed_ticks = nrf_rtc_counter_get(BOARD_RTC_INST) + + (RTC_TICKS_HZ / (1000 / TASK_WATCHDOG_FEED_PERIOD_MS)); + nrf_rtc_cc_set(BOARD_RTC_INST, 1, next_feed_ticks & RTC_COUNTER_COUNTER_Msk); + } + + NVIC_ClearPendingIRQ(BOARD_RTC_IRQN); +} diff --git a/src/fw/drivers/rtc/sf32lb.c b/src/fw/drivers/rtc/sf32lb.c new file mode 100644 index 0000000000..3498e56fd8 --- /dev/null +++ b/src/fw/drivers/rtc/sf32lb.c @@ -0,0 +1,482 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "board/board.h" +#include "drivers/flash.h" +#include "drivers/rtc.h" +#include "flash_region/flash_region.h" +#include "kernel/events.h" +#include "mcu/interrupts.h" +#include "system/passert.h" +#include "util/time/time.h" +#include "system/logging.h" +#include "pbl/services/new_timer/new_timer.h" + +#include "FreeRTOS.h" +#include "task.h" + +#include "bf0_hal_rtc.h" + +PBL_LOG_MODULE_DEFINE(driver_rtc_sf32lb, CONFIG_DRIVER_RTC_LOG_LEVEL); + +// The RTC clock, CLK_RTC, can be configured to use the LXT32 (32.768 kHz) or +// LRC10 (9.8 kHz). The prescaler values need to be set such that the CLK1S +// event runs at 1 Hz. The formula that relates prescaler values with the +// clock frequency is as follows: +// +// F(CLK1S) = CLK_RTC / (DIV_A_INT + DIV_A_FRAC / 2^14) / DIV_B + +#ifndef SF32LB52_USE_LXT +#define DIV_A_INT 38U +#define DIV_A_FRAC 4608U +#define DIV_B 256U + +// Default RC10K cycles value on 48MHz clock +#define RC10K_DEFAULT_CYCLES 1200000UL + +// The deviation limit of the current calibration value of RC10K relative to the average +// calibration value. +#define MAX_DELTA_BETWEEN_RTC_AVE (HAL_RC_CAL_GetLPCycle() / 2) + +// Calibration period in milliseconds +#define RTC_CAL_PERIOD_MS 60000 + +// Maximum reasonable correction in seconds. If the calculated correction exceeds this, +// something is wrong and we should reset calibration state instead of applying it. +// 60 seconds is generous - normal drift should be milliseconds per calibration cycle. +#define MAX_REASONABLE_CORRECTION_SECS 60 + +static TimerID s_rtc_cal_timer; + +// Calibration state - must be reset when RTC time is set externally +static uint32_t s_rtc_cycle_count_init = 0; +static double s_rtc_a = 0.0; +static double s_delta_total = 0.0; + +static void prv_reset_calibration_state(void) { + s_rtc_cycle_count_init = 0; + s_rtc_a = 0.0; + s_delta_total = 0.0; +} + +// Forward declaration - defined later in file +static void prv_rtc_set_time_no_cal_reset(time_t time); +#else +#define DIV_A_INT 128U +#define DIV_A_FRAC 0U +#define DIV_B 256U +#endif + +static RTC_HandleTypeDef RTC_Handler = { + .Instance = (RTC_TypeDef*)RTC_BASE, + .Init = + { + .HourFormat = RTC_HOURFORMAT_24, + .DivAInt = DIV_A_INT, + .DivAFrac = DIV_A_FRAC, + .DivB = DIV_B, + }, +}; + +static bool s_initialized = false; + +#ifndef SF32LB52_USE_LXT +static uint32_t prv_rtc_get_lpcycle() { + uint32_t value; + + value = HAL_RC_CAL_get_average_cycle_on_48M(); + if (value == 0UL) { + value = RC10K_DEFAULT_CYCLES; + } + + HAL_Set_backup(RTC_BACKUP_LPCYCLE, value); + + return value; +} + +void prv_rtc_rc10_calculate_div(RTC_HandleTypeDef* hdl, uint32_t value) { + hdl->Init.DivB = RC10K_SUB_SEC_DIVB; + + // 1 seconds has total 1/(x/(48*8))/256=1.5M/x cycles, times 2^14 for DIVA + uint32_t divider = RTC_Handler.Init.DivB * value; + value = (48000000ULL * HAL_RC_CAL_GetLPCycle() * (1 << 14) + (divider >> 1)) / divider; + hdl->Init.DivAInt = (uint32_t)(value >> 14); + hdl->Init.DivAFrac = (uint32_t)(value & ((1 << 14) - 1)); +} + +static void prv_rtc_reconfig() { + uint32_t cur_ave; + HAL_StatusTypeDef ret; + cur_ave = prv_rtc_get_lpcycle(); + prv_rtc_rc10_calculate_div(&RTC_Handler, cur_ave); + + ret = HAL_RTC_Init(&RTC_Handler, RTC_INIT_REINIT); + PBL_ASSERTN(ret == HAL_OK); +} + +static void prv_rtc_cal_timer_cb(void* data) { + if (s_rtc_cycle_count_init == 0) { + uint16_t sub; + time_t t; + + prv_rtc_reconfig(); + // Get initial lpcycle, RTC is running based on it. + s_rtc_cycle_count_init = HAL_Get_backup(RTC_BACKUP_LPCYCLE); + s_delta_total = 0.0; + rtc_get_time_ms(&t, &sub); + s_rtc_a = 1.0 * t + ((float)(1.0 * sub)) / RC10K_SUB_SEC_DIVB; + } else { + uint16_t sub2 = 0; + double rtc_cal = 0.0; + double delta = 0.0; + time_t t2; + uint32_t cur_ave; + uint32_t ref_cycle; + double rtc_b; + + rtc_get_time_ms(&t2, &sub2); + cur_ave = HAL_RC_CAL_get_average_cycle_on_48M(); + ref_cycle = cur_ave; + rtc_b = 1.0 * t2 + ((double)(1.0 * sub2)) / RC10K_SUB_SEC_DIVB; + + // Delta time between s_rtc_a to rtc_b, in seconds. + delta = rtc_b - s_rtc_a; + // Calculate accurate rtc_b + rtc_cal = delta * ref_cycle / s_rtc_cycle_count_init + s_rtc_a; + // Detla time of accurrate rtc_b and current rtc_b + delta = rtc_cal - rtc_b; + + // Accumulate error + s_delta_total += delta; + + // Sanity check: if accumulated error is unreasonably large, the RTC time was likely + // changed externally. Reset calibration state instead of applying a bogus correction. + if (s_delta_total > MAX_REASONABLE_CORRECTION_SECS || + s_delta_total < -MAX_REASONABLE_CORRECTION_SECS) { + PBL_LOG_WRN("RTC calibration: delta_sum=%d exceeds max, resetting calibration state", + (int)(s_delta_total * 1000)); + prv_reset_calibration_state(); + return; + } + + if (s_delta_total > 1.0 || s_delta_total < -1.0) { + // Accurate time + rtc_cal = s_delta_total + rtc_b; + // Apply integral part difference. + prv_rtc_set_time_no_cal_reset((uint32_t)rtc_cal); + // Continue with subseconds + s_delta_total = rtc_cal - (uint32_t)rtc_cal; + // Next interval start time + s_rtc_a = (uint32_t)rtc_cal; + if ((cur_ave > s_rtc_cycle_count_init && + (cur_ave - s_rtc_cycle_count_init) > MAX_DELTA_BETWEEN_RTC_AVE) || + (cur_ave < s_rtc_cycle_count_init && + (s_rtc_cycle_count_init - cur_ave) > MAX_DELTA_BETWEEN_RTC_AVE)) { + prv_rtc_reconfig(); + s_rtc_cycle_count_init = HAL_Get_backup(RTC_BACKUP_LPCYCLE); + } + } else { + // Next interval start time + s_rtc_a = rtc_b; + } + + PBL_LOG_DBG("origin: f=%dHz,cycle=%d avr: f=%dHz cycle_ave=%d delta=%d, delta_sum=%d\n", + (int)(48000000ULL * HAL_RC_CAL_GetLPCycle() / s_rtc_cycle_count_init), + (int)s_rtc_cycle_count_init, + (int)(48000000ULL * HAL_RC_CAL_GetLPCycle() / ref_cycle), + (int)ref_cycle, + (int)(delta * 1000), (int)(s_delta_total * 1000)); + } +} +#endif + +void rtc_init(void) { +#ifdef SF32LB52_USE_LXT + HAL_StatusTypeDef ret; + + ret = HAL_PMU_LXTReady(); + PBL_ASSERTN(ret == HAL_OK); + + ret = HAL_RTC_Init(&RTC_Handler, RTC_INIT_NORMAL); + PBL_ASSERTN(ret == HAL_OK); +#else + prv_rtc_reconfig(); +#endif + + s_initialized = true; +} + +void rtc_init_timers(void) {} + +static RtcTicks get_ticks(void) { + static TickType_t s_last_freertos_tick_count = 0; + static RtcTicks s_coarse_ticks = 0; + + bool ints_enabled = mcu_state_are_interrupts_enabled(); + if (ints_enabled) { + __disable_irq(); + } + + TickType_t freertos_tick_count = xTaskGetTickCount(); + if (freertos_tick_count < s_last_freertos_tick_count) { + TickType_t rollover_amount = -1; + s_coarse_ticks += rollover_amount; + } + + s_last_freertos_tick_count = freertos_tick_count; + RtcTicks ret_value = freertos_tick_count + s_coarse_ticks; + + if (ints_enabled) { + __enable_irq(); + } + + return ret_value; +} + +// Internal function to set RTC time without resetting calibration state. +// Used by the calibration code when applying corrections. +static void prv_rtc_set_time_no_cal_reset(time_t time) { + // Capture old time before changing it to send proper event + time_t old_time = rtc_get_time(); + + struct tm t; + gmtime_r(&time, &t); + + PBL_ASSERTN(!rtc_sanitize_struct_tm(&t)); + + RTC_TimeTypeDef rtc_time_struct = {.Hours = t.tm_hour, .Minutes = t.tm_min, .Seconds = t.tm_sec}; + + RTC_DateTypeDef rtc_date_struct = { + .Month = t.tm_mon + 1, + .Date = t.tm_mday, + .Year = t.tm_year % 100, + }; + + HAL_RTC_SetTime(&RTC_Handler, &rtc_time_struct, RTC_FORMAT_BIN); + HAL_RTC_SetDate(&RTC_Handler, &rtc_date_struct, RTC_FORMAT_BIN); + + PBL_LOG_INFO("RTC set time to %lu", time); + PBL_LOG_INFO("%u:%u:%u, %u/%u/%u (%u)", + rtc_time_struct.Hours, rtc_time_struct.Minutes, rtc_time_struct.Seconds, + rtc_date_struct.Month, rtc_date_struct.Date, rtc_date_struct.Year, + rtc_date_struct.WeekDay); + + // Send clock change event to notify system components (e.g., DND timer scheduler) + // This ensures long-duration timers are properly rescheduled after calibration adjustments + int32_t time_delta = (int32_t)(time - old_time); + if (time_delta != 0) { + PebbleEvent e = { + .type = PEBBLE_SET_TIME_EVENT, + .set_time_info = { + .utc_time_delta = time_delta, + .gmt_offset_delta = 0, + .dst_changed = false, + } + }; + event_put(&e); + } +} + +void rtc_set_time(time_t time) { +#ifndef SF32LB52_USE_LXT + // Reset calibration state when time is set externally to prevent + // the calibration algorithm from computing bogus corrections based on + // stale reference times. + prv_reset_calibration_state(); +#endif + prv_rtc_set_time_no_cal_reset(time); +} + +void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) { + RTC_DateTypeDef rtc_date; + RTC_TimeTypeDef rtc_time; + + if (s_initialized) { + while((RTC_Handler.Instance->ISR & RTC_ISR_RSF) == (uint32_t)RESET) { + // Wait for RTC registers to synchronize + } + } + + HAL_RTC_GetTime(&RTC_Handler, &rtc_time, RTC_FORMAT_BIN); + while (HAL_RTC_GetDate(&RTC_Handler, &rtc_date, RTC_FORMAT_BIN) == HAL_ERROR) { + // HAL_ERROR is returned if a rollover occurs, so just keep trying + HAL_RTC_GetTime(&RTC_Handler, &rtc_time, RTC_FORMAT_BIN); + }; + + struct tm current_time = { + .tm_sec = rtc_time.Seconds, + .tm_min = rtc_time.Minutes, + .tm_hour = rtc_time.Hours, + .tm_mday = rtc_date.Date, + .tm_mon = rtc_date.Month - 1, + .tm_year = rtc_date.Year + 100, + .tm_wday = rtc_date.WeekDay, + .tm_yday = 0, + .tm_isdst = 0, + }; + + *out_seconds = mktime(¤t_time); + *out_ms = (uint16_t)((rtc_time.SubSeconds * 1000) / DIV_B); +} + +time_t rtc_get_time(void) { + time_t seconds; + uint16_t ms; + + rtc_get_time_ms(&seconds, &ms); + + return seconds; +} + +RtcTicks rtc_get_ticks(void) { + return get_ticks(); +} + +void rtc_alarm_init(void) {} + +void rtc_alarm_set(RtcTicks num_ticks) {} + +RtcTicks rtc_alarm_get_elapsed_ticks(void) { + return 0; +} + +bool rtc_alarm_is_initialized(void) { + return true; +} + +bool rtc_sanitize_struct_tm(struct tm* t) { + // These values come from time_t (which suffers from the 2038 problem) and our hardware which + // only stores a 2 digit year, so we only represent values after 2000. + + // Remember tm_year is years since 1900. + if (t->tm_year < 100) { + // Bump it up to the year 2000 to work with our hardware. + t->tm_year = 100; + return true; + } else if (t->tm_year > 137) { + t->tm_year = 137; + return true; + } + return false; +} + +bool rtc_sanitize_time_t(time_t* t) { + struct tm time_struct; + gmtime_r(t, &time_struct); + + const bool result = rtc_sanitize_struct_tm(&time_struct); + *t = mktime(&time_struct); + + return result; +} + +void rtc_get_time_tm(struct tm* time_tm) { + time_t t = rtc_get_time(); + localtime_r(&t, time_tm); +} + +const char* rtc_get_time_string(char* buffer) { + return time_t_to_string(buffer, rtc_get_time()); +} + +const char* time_t_to_string(char* buffer, time_t t) { + struct tm time; + localtime_r(&t, &time); + + strftime(buffer, TIME_STRING_BUFFER_SIZE, "%c", &time); + + return buffer; +} + +//! We store timezone info in the flash TZINFO region + +//! Versioned storage structure for timezone info in flash +//! This allows for future migrations and avoids struct alignment issues +typedef struct __attribute__((packed)) { + uint8_t version; // Version number for future migrations + char tm_zone[TZ_LEN - 1]; // Up to 5 character timezone abbreviation + uint8_t dst_id; // Daylight savings time zone index + int16_t timezone_id; // Olson index of timezone + int32_t tm_gmtoff; // GMT time offset + time_t dst_start; // Timestamp of start of DST period (0 if none) + time_t dst_end; // Timestamp of end of DST period (0 if none) +} TzinfoFlashStorage; + +#define TZINFO_VERSION 1 + +void rtc_set_timezone(TimezoneInfo* tzinfo) { + _Static_assert(sizeof(TzinfoFlashStorage) <= SUBSECTOR_SIZE_BYTES, + "TzinfoFlashStorage must fit in TZINFO flash region (4KB)"); + + // Copy to versioned buffer + TzinfoFlashStorage storage = { + .version = TZINFO_VERSION, + .dst_id = tzinfo->dst_id, + .timezone_id = tzinfo->timezone_id, + .tm_gmtoff = tzinfo->tm_gmtoff, + .dst_start = tzinfo->dst_start, + .dst_end = tzinfo->dst_end, + }; + memcpy(storage.tm_zone, tzinfo->tm_zone, TZ_LEN - 1); + + flash_erase_subsector_blocking(FLASH_REGION_TZINFO_BEGIN); + flash_write_bytes((const uint8_t*)&storage, FLASH_REGION_TZINFO_BEGIN, sizeof(TzinfoFlashStorage)); +} + +void rtc_get_timezone(TimezoneInfo* tzinfo) { + TzinfoFlashStorage storage; + + flash_read_bytes((uint8_t*)&storage, FLASH_REGION_TZINFO_BEGIN, sizeof(TzinfoFlashStorage)); + + if (storage.version != TZINFO_VERSION) { + // Future versions can handle migrations here + // For now, treat invalid version as unset + memset(tzinfo, 0, sizeof(TimezoneInfo)); + return; + } + + memcpy(tzinfo->tm_zone, storage.tm_zone, TZ_LEN - 1); + tzinfo->dst_id = storage.dst_id; + tzinfo->timezone_id = storage.timezone_id; + tzinfo->tm_gmtoff = storage.tm_gmtoff; + tzinfo->dst_start = storage.dst_start; + tzinfo->dst_end = storage.dst_end; +} + +void rtc_timezone_clear(void) { + flash_erase_subsector_blocking(FLASH_REGION_TZINFO_BEGIN); +} + +uint16_t rtc_get_timezone_id(void) { + TimezoneInfo tzinfo; + + rtc_get_timezone(&tzinfo); + + return (uint16_t)tzinfo.timezone_id; +} + +bool rtc_is_timezone_set(void) { + uint8_t version; + + flash_read_bytes((uint8_t*)&version, FLASH_REGION_TZINFO_BEGIN, sizeof(version)); + + return version == TZINFO_VERSION; +} + +void rtc_enable_backup_regs(void) {} + +void rtc_calibrate_frequency(uint32_t frequency) { +#ifndef SF32LB52_USE_LXT + prv_rtc_cal_timer_cb(NULL); + + s_rtc_cal_timer = new_timer_create(); + PBL_ASSERTN(s_rtc_cal_timer != TIMER_INVALID_ID); + + bool success = new_timer_start(s_rtc_cal_timer, RTC_CAL_PERIOD_MS, + prv_rtc_cal_timer_cb, NULL, + TIMER_START_FLAG_REPEATING); + PBL_ASSERTN(success); +#endif +} diff --git a/src/fw/drivers/rtc/wscript_build b/src/fw/drivers/rtc/wscript_build new file mode 100644 index 0000000000..16a89d75e2 --- /dev/null +++ b/src/fw/drivers/rtc/wscript_build @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['freertos', 'fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_RTC_QEMU: + sources.append('../qemu/qemu_rtc_hal.c') +elif bld.env.CONFIG_RTC_NRF5: + sources.append('nrf5.c') +elif bld.env.CONFIG_RTC_SF32LB: + sources.append('sf32lb.c') + use.append('hal_sifli') + +bld.objects( + name='driver_rtc', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_rtc') diff --git a/src/fw/drivers/rtc_common.c b/src/fw/drivers/rtc_common.c deleted file mode 100644 index ff3e620332..0000000000 --- a/src/fw/drivers/rtc_common.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "rtc_private.h" -#include "drivers/rtc.h" - -#include "drivers/gpio.h" -#include "drivers/pwr.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/reset.h" - -#include - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -bool rtc_sanitize_struct_tm(struct tm *t) { - // These values come from time_t (which suffers from the 2038 problem) and our hardware which - // only stores a 2 digit year, so we only represent values after 2000. - - // Remember tm_year is years since 1900. - if (t->tm_year < 100) { - // Bump it up to the year 2000 to work with our hardware. - t->tm_year = 100; - return true; - } else if (t->tm_year > 137) { - t->tm_year = 137; - return true; - } - return false; -} - -bool rtc_sanitize_time_t(time_t *t) { - struct tm time_struct; - gmtime_r(t, &time_struct); - - const bool result = rtc_sanitize_struct_tm(&time_struct); - *t = mktime(&time_struct); - - return result; -} - -void rtc_get_time_tm(struct tm* time_tm) { - time_t t = rtc_get_time(); - localtime_r(&t, time_tm); -} - -const char* rtc_get_time_string(char* buffer) { - return time_t_to_string(buffer, rtc_get_time()); -} - -const char* time_t_to_string(char* buffer, time_t t) { - struct tm time; - localtime_r(&t, &time); - - strftime(buffer, TIME_STRING_BUFFER_SIZE, "%c", &time); - - return buffer; -} - - -//! We attempt to save registers by placing both the timezone abbreviation -//! timezone index and the daylight_savingtime into the same register set -void rtc_set_timezone(TimezoneInfo *tzinfo) { - uint32_t *raw = (uint32_t*)tzinfo; - _Static_assert(sizeof(TimezoneInfo) <= 5 * sizeof(uint32_t), - "RTC Set Timezone invalid data size"); - - RTC_WriteBackupRegister(RTC_TIMEZONE_ABBR_START, raw[0]); - RTC_WriteBackupRegister(RTC_TIMEZONE_ABBR_END_TZID_DSTID, raw[1]); - RTC_WriteBackupRegister(RTC_TIMEZONE_GMTOFFSET, raw[2]); - RTC_WriteBackupRegister(RTC_TIMEZONE_DST_START, raw[3]); - RTC_WriteBackupRegister(RTC_TIMEZONE_DST_END, raw[4]); -} - - -void rtc_get_timezone(TimezoneInfo *tzinfo) { - uint32_t *raw = (uint32_t*)tzinfo; - - raw[0] = RTC_ReadBackupRegister(RTC_TIMEZONE_ABBR_START); - raw[1] = RTC_ReadBackupRegister(RTC_TIMEZONE_ABBR_END_TZID_DSTID); - raw[2] = RTC_ReadBackupRegister(RTC_TIMEZONE_GMTOFFSET); - raw[3] = RTC_ReadBackupRegister(RTC_TIMEZONE_DST_START); - raw[4] = RTC_ReadBackupRegister(RTC_TIMEZONE_DST_END); -} - -void rtc_timezone_clear(void) { - RTC_WriteBackupRegister(RTC_TIMEZONE_ABBR_START, 0); - RTC_WriteBackupRegister(RTC_TIMEZONE_ABBR_END_TZID_DSTID, 0); - RTC_WriteBackupRegister(RTC_TIMEZONE_GMTOFFSET, 0); - RTC_WriteBackupRegister(RTC_TIMEZONE_DST_START, 0); - RTC_WriteBackupRegister(RTC_TIMEZONE_DST_END, 0); -} - -uint16_t rtc_get_timezone_id(void) { - return ((RTC_ReadBackupRegister(RTC_TIMEZONE_ABBR_END_TZID_DSTID) >> 16) & 0xFFFF); -} - -bool rtc_is_timezone_set(void) { - // True if the timezone abbreviation has been set (including UNK for unknown) - return (RTC_ReadBackupRegister(RTC_TIMEZONE_ABBR_START) != 0); -} - -void rtc_enable_backup_regs(void) { - pwr_access_backup_domain(true); -} diff --git a/src/fw/drivers/rtc_private.h b/src/fw/drivers/rtc_private.h deleted file mode 100644 index 3d516a2c55..0000000000 --- a/src/fw/drivers/rtc_private.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! @file rtc_private.h -//! -//! Functions private to implementations of RTC drivers for stm32 platforms - -//! Called by rtc_init to initialize the clock source. -//! -//! Warning! In some cases this function may detect an edge case and reset the system to address -//! it! See the implementation for details. -//! -//! @return True if we managed to correctly get onto the LSE, false otherwise -bool rtc_init_config_clock_source(void); - -//! Configure the LSE oscillator component of the RCC -void rtc_init_config_lse_clock_source(void); - -//! Verify that the clock source is set up correctly. -//! -//! Warning! In some cases this function may detect an edge case and reset the system to address -//! it! See the implementation for details. -void rtc_init_verify_clock_source_config(void); - -//! Enable access to the RTC's backup registers -void rtc_enable_backup_regs(void); diff --git a/src/fw/drivers/sf32lb52/button.c b/src/fw/drivers/sf32lb52/button.c index 968a05ebba..c7353be474 100644 --- a/src/fw/drivers/sf32lb52/button.c +++ b/src/fw/drivers/sf32lb52/button.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 SiFli Technologies(Nanjing) Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 SiFli Technologies(Nanjing) Co., Ltd */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/button.h" @@ -23,7 +10,19 @@ #include "kernel/events.h" #include "system/passert.h" +static bool s_rotated_180 = false; + +void button_set_rotated(bool rotated_180) { + s_rotated_180 = rotated_180; +} + bool button_is_pressed(ButtonId id) { + if (s_rotated_180 && (id == BUTTON_ID_UP)) { + id = BUTTON_ID_DOWN; + } else if (s_rotated_180 && (id == BUTTON_ID_DOWN)) { + id = BUTTON_ID_UP; + } + const InputConfig config = { .gpio = BOARD_CONFIG_BUTTON.buttons[id].port, .gpio_pin = BOARD_CONFIG_BUTTON.buttons[id].pin, @@ -50,10 +49,6 @@ void button_init(void) { } } -bool button_selftest(void) { - return button_get_state_bits() == 0; -} - void command_button_read(const char* button_id_str) { int button = atoi(button_id_str); diff --git a/src/fw/drivers/sf32lb52/debounced_button.c b/src/fw/drivers/sf32lb52/debounced_button.c index 792629aaf8..054ea9acd9 100644 --- a/src/fw/drivers/sf32lb52/debounced_button.c +++ b/src/fw/drivers/sf32lb52/debounced_button.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 SiFli Technologies(Nanjing) Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 SiFli Technologies(Nanjing) Co., Ltd */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/debounced_button.h" @@ -31,9 +18,9 @@ #include "FreeRTOS.h" -/* Timer period 1us, auto reload is 2ms. */ -#define TIMER_FREQUENCY_HZ 1000000 -#define TIMER_PERIOD_TICKS 2000 +/* Timer period 100us, auto reload is 2ms. */ +#define TIMER_FREQUENCY_HZ 10000 +#define TIMER_PERIOD_TICKS 20 #define RESET_BUTTONS ((1 << BUTTON_ID_SELECT) | (1 << BUTTON_ID_BACK)) @@ -48,30 +35,30 @@ // A button must be stable for 20 samples (40ms) to be accepted. static const uint32_t s_num_debounce_samples = 20; static GPT_HandleTypeDef s_tim_hdl = {0}; +static bool s_timer_enabled = false; static void prv_timer_handler(void); static void initialize_button_timer(void) { s_tim_hdl.Instance = BOARD_CONFIG_BUTTON.timer; - s_tim_hdl.Init.Prescaler = HAL_RCC_GetPCLKFreq(CORE_ID_HCPU, 1) / TIMER_FREQUENCY_HZ - 1; + s_tim_hdl.Init.Prescaler = 24000000U / (TIMER_FREQUENCY_HZ - 1); // GPTIM2 clock is 24MHz s_tim_hdl.core = CORE_ID_HCPU; s_tim_hdl.Init.CounterMode = GPT_COUNTERMODE_UP; s_tim_hdl.Init.RepetitionCounter = 0; + s_tim_hdl.Init.Period = TIMER_PERIOD_TICKS; HAL_GPT_Base_Init(&s_tim_hdl); HAL_NVIC_SetPriority(BOARD_CONFIG_BUTTON.timer_irqn, 7, 0); HAL_NVIC_EnableIRQ(BOARD_CONFIG_BUTTON.timer_irqn); - __HAL_GPT_SET_AUTORELOAD(&s_tim_hdl, TIMER_PERIOD_TICKS); + __HAL_GPT_CLEAR_FLAG(&s_tim_hdl, GPT_FLAG_UPDATE); + __HAL_GPT_URS_ENABLE(&s_tim_hdl); __HAL_GPT_SET_MODE(&s_tim_hdl, GPT_OPMODE_REPETITIVE); } -static bool prv_check_timer_enabled(void) { - return (HAL_GPT_Base_GetState(&s_tim_hdl) != HAL_GPT_STATE_READY); -} - static void disable_button_timer(void) { - if (prv_check_timer_enabled()) { + if (s_timer_enabled) { + s_timer_enabled = false; HAL_GPT_Base_Stop_IT(&s_tim_hdl); stop_mode_enable(InhibitorButton); } @@ -79,7 +66,8 @@ static void disable_button_timer(void) { static void prv_enable_button_timer(void) { __disable_irq(); - if (!prv_check_timer_enabled()) { + if (!s_timer_enabled) { + s_timer_enabled = true; HAL_GPT_Base_Start_IT(&s_tim_hdl); stop_mode_disable(InhibitorButton); } @@ -96,7 +84,8 @@ void debounced_button_init(void) { for (int i = 0; i < NUM_BUTTONS; ++i) { const ExtiConfig config = { .peripheral = BOARD_CONFIG_BUTTON.buttons[i].port, - .gpio_pin = BOARD_CONFIG_BUTTON.buttons[i].pin + .gpio_pin = BOARD_CONFIG_BUTTON.buttons[i].pin, + .pull = BOARD_CONFIG_BUTTON.buttons[i].pull, }; exti_configure_pin(config, ExtiTrigger_RisingFalling, prv_button_interrupt_handler); exti_enable(config); @@ -152,7 +141,7 @@ static void prv_timer_handler(void) { } } -#if !defined(MANUFACTURING_FW) +#if !defined(CONFIG_MFG) // Now that s_debounced_button_state is updated, check to see if the user is holding down the reset // combination. static uint32_t s_hard_reset_timer = 0; diff --git a/src/fw/drivers/sf32lb52/debounced_button_definitions.h b/src/fw/drivers/sf32lb52/debounced_button_definitions.h index 8384715937..05cc68f83f 100644 --- a/src/fw/drivers/sf32lb52/debounced_button_definitions.h +++ b/src/fw/drivers/sf32lb52/debounced_button_definitions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 SiFli Technologies(Nanjing) Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 SiFli Technologies(Nanjing) Co., Ltd */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/sf32lb52/exti.c b/src/fw/drivers/sf32lb52/exti.c deleted file mode 100644 index a4117702f8..0000000000 --- a/src/fw/drivers/sf32lb52/exti.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2025 SiFli Technologies(Nanjing) Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/exti.h" - -#include - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "kernel/events.h" -#include "mcu/interrupts.h" -#include "system/passert.h" - -#define EXTI_MAX_GPIO1_PIN_NUM 8 -#define EXTI_MAX_GPIO2_PIN_NUM 1 - -typedef struct { - uint32_t gpio_pin; - ExtiHandlerCallback callback; -} ExtiHandlerConfig_t; - -static ExtiHandlerConfig_t s_exti_gpio1_handler_configs[EXTI_MAX_GPIO1_PIN_NUM]; -static ExtiHandlerConfig_t s_exti_gpio2_handler_configs[EXTI_MAX_GPIO2_PIN_NUM]; - -static GPIO_TypeDef *prv_gpio_get_instance(GPIO_TypeDef *hgpio, uint16_t gpio_pin, - uint16_t *offset) { - uint16_t max_num; - uint16_t inst_idx; - GPIO_TypeDef *gpiox; - - if ((GPIO_TypeDef *)hwp_gpio1 == hgpio) { - max_num = GPIO1_PIN_NUM; - } else { - max_num = GPIO2_PIN_NUM; - } - - HAL_ASSERT(gpio_pin < max_num); - - if (gpio_pin >= max_num) { - return (GPIO_TypeDef *)NULL; - } - - // There are many groups of similar registers in the GPIO, and because of register length limitations, up to 32 gpio can be operated in each group. - inst_idx = gpio_pin >> 5; - *offset = gpio_pin & 31; - - gpiox = (GPIO_TypeDef *)hgpio + inst_idx; - - return gpiox; -} - -static void prv_insert_handler(GPIO_TypeDef *hgpio, uint8_t gpio_pin, ExtiHandlerCallback cb) { - // Find the handler index for this pin - uint8_t index = 0; - while (index < (hgpio == hwp_gpio1 ? EXTI_MAX_GPIO1_PIN_NUM : EXTI_MAX_GPIO2_PIN_NUM) && - s_exti_gpio1_handler_configs[index].callback != NULL) { - index++; - } - if (index >= (hgpio == hwp_gpio1 ? EXTI_MAX_GPIO1_PIN_NUM : EXTI_MAX_GPIO2_PIN_NUM)) { - // No available slot - return; - } - // Store the callback and index - s_exti_gpio1_handler_configs[index].gpio_pin = gpio_pin; - s_exti_gpio1_handler_configs[index].callback = cb; -} - -static void prv_delete_handler(GPIO_TypeDef *hgpio, uint8_t gpio_pin) { - // Find the handler index for this pin - uint8_t index = 0; - while (index < (hgpio == hwp_gpio1 ? EXTI_MAX_GPIO1_PIN_NUM : EXTI_MAX_GPIO2_PIN_NUM) && - s_exti_gpio1_handler_configs[index].callback != NULL && - s_exti_gpio1_handler_configs[index].gpio_pin != gpio_pin) { - index++; - } - if (index >= (hgpio == hwp_gpio1 ? EXTI_MAX_GPIO1_PIN_NUM : EXTI_MAX_GPIO2_PIN_NUM)) { - // Handler not found - return; - } - // Clear the callback and index - s_exti_gpio1_handler_configs[index].callback = NULL; - s_exti_gpio1_handler_configs[index].gpio_pin = 0; -} - -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb) { - prv_insert_handler(cfg.peripheral, cfg.gpio_pin, cb); - - uint16_t offset; - GPIO_TypeDef *gpiox = prv_gpio_get_instance(cfg.peripheral, cfg.gpio_pin, &offset); - - switch (trigger) { - case ExtiTrigger_Rising: - gpiox->ITSR |= (1UL << offset); - gpiox->IPHSR = (1UL << offset); - gpiox->IPLCR = (1UL << offset); - break; - case ExtiTrigger_Falling: - gpiox->ITSR |= (1UL << offset); - gpiox->IPHCR = (1UL << offset); - gpiox->IPLSR = (1UL << offset); - break; - case ExtiTrigger_RisingFalling: - gpiox->ITSR |= (1UL << offset); - gpiox->IPHSR = (1UL << offset); - gpiox->IPLSR = (1UL << offset); - break; - } -} - -void exti_enable(ExtiConfig cfg) { - uint16_t offset; - GPIO_TypeDef *gpiox = prv_gpio_get_instance(cfg.peripheral, cfg.gpio_pin, &offset); - if (cfg.peripheral == hwp_gpio1) { - // Enable the EXTI line for GPIO1 - gpiox->IESR |= (1 << offset); - } else { - gpiox->IESR_EXT |= (1 << offset); - } - - HAL_NVIC_SetPriority(GPIO1_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(GPIO1_IRQn); -} - -void exti_disable(ExtiConfig cfg) { - uint16_t offset; - GPIO_TypeDef *gpiox = prv_gpio_get_instance(cfg.peripheral, cfg.gpio_pin, &offset); - if (cfg.peripheral == hwp_gpio1) { - // Disable the EXTI line for GPIO1 - gpiox->IECR |= (1 << offset); - } else { - gpiox->IECR_EXT |= (1 << offset); - } -} - -void HAL_GPIO_EXTI_Callback(GPIO_TypeDef *hgpio, uint16_t GPIO_Pin) { - int index = 0; - ExtiHandlerCallback cb = NULL; - if (hgpio == hwp_gpio1) { - while (index < EXTI_MAX_GPIO1_PIN_NUM && s_exti_gpio1_handler_configs[index].callback != NULL) { - if (s_exti_gpio1_handler_configs[index].gpio_pin == GPIO_Pin) { - cb = s_exti_gpio1_handler_configs[index].callback; - break; - } - index++; - } - } - - if (cb != NULL) { - bool should_context_switch = false; - cb(&should_context_switch); - if (should_context_switch) { - portEND_SWITCHING_ISR(should_context_switch); - } - } -} - -void GPIO1_IRQHandler(void) { HAL_GPIO_IRQHandler(hwp_gpio1); } - -void GPIO2_IRQHandler( - void) // Define the interrupt siervice routine (ISR) according to the interrupt vector table -{ - HAL_GPIO_IRQHandler(hwp_gpio2); -} - -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger) {} - -void exti_enable_other(ExtiLineOther exti_line) {} - -void exti_disable_other(ExtiLineOther exti_line) {} - -void exti_set_pending(ExtiConfig cfg) {} - -void exti_clear_pending_other(ExtiLineOther exti_line) {} diff --git a/src/fw/drivers/sf32lb52/gpio.c b/src/fw/drivers/sf32lb52/gpio.c deleted file mode 100644 index 8b7c609769..0000000000 --- a/src/fw/drivers/sf32lb52/gpio.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2025 SiFli Technologies(Nanjing) Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" -#include "system/passert.h" -#include "board/board.h" - -#include "FreeRTOS.h" - -#include - -static RCC_MODULE_TYPE prv_get_gpio_rcc_module(GPIO_TypeDef *GPIOx) { - if (GPIOx == hwp_gpio1) { - return RCC_MOD_GPIO1; - } else if (GPIOx == hwp_gpio2) { - return RCC_MOD_GPIO2; - } else { - WTF; - } - return 0; -} - -void gpio_use(GPIO_TypeDef *GPIOx) { - RCC_MODULE_TYPE rcc_module = prv_get_gpio_rcc_module(GPIOx); - portENTER_CRITICAL(); - HAL_RCC_EnableModule(rcc_module); - portEXIT_CRITICAL(); -} - -void gpio_release(GPIO_TypeDef *GPIOx) { - RCC_MODULE_TYPE rcc_module = prv_get_gpio_rcc_module(GPIOx); - portENTER_CRITICAL(); - HAL_RCC_DisableModule(rcc_module); - portEXIT_CRITICAL(); -} - -void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed) { - (void)speed; - gpio_use(pin_config->gpio); - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Pin = pin_config->gpio_pin; - if (otype == GPIO_OType_OD) { - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; - } else if (otype == GPIO_OType_PP) { - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT; - } else { - WTF; - } - HAL_PIN_Set(PAD_PA00 + pin_config->gpio_pin, GPIO_A0 + pin_config->gpio_pin, PIN_NOPULL, 1); - GPIO_InitStruct.Pull = GPIO_NOPULL; - - HAL_GPIO_Init(pin_config->gpio, &GPIO_InitStruct); -} - -void gpio_input_init(const InputConfig *input_cfg) { - gpio_input_init_pull_up_down(input_cfg, GPIO_PuPd_NOPULL); -} - -void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd) { - gpio_use(input_cfg->gpio); - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.Pin = input_cfg->gpio_pin; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; - - int flag = 0; - /* pullup/down is handled in pinmux hal. */ - if (pupd == GPIO_PuPd_UP) { - flag = PIN_PULLUP; - } else if (pupd == GPIO_PuPd_DOWN) { - flag = PIN_PULLDOWN; - } else { - flag = PIN_NOPULL; - } - - HAL_PIN_Set(PAD_PA00 + input_cfg->gpio_pin, GPIO_A0 + input_cfg->gpio_pin, flag, 1); - HAL_GPIO_Init(input_cfg->gpio, &GPIO_InitStruct); -} - -bool gpio_input_read(const InputConfig *input_cfg) { - bool value = HAL_GPIO_ReadPin(input_cfg->gpio, input_cfg->gpio_pin); - return value; -} - -void gpio_output_set(const OutputConfig *pin_config, bool asserted) { - HAL_GPIO_WritePin(pin_config->gpio, pin_config->gpio_pin, asserted); -} \ No newline at end of file diff --git a/src/fw/drivers/sf32lb52/i2c_hal.c b/src/fw/drivers/sf32lb52/i2c_hal.c deleted file mode 100644 index 822f229f74..0000000000 --- a/src/fw/drivers/sf32lb52/i2c_hal.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "FreeRTOS.h" -#include "bf0_pin_const.h" -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/i2c_definitions.h" -#include "drivers/periph_config.h" -#include "i2c_hal_definitions.h" -#include "os/mutex.h" -#include "os/tick.h" -#include "queue.h" -#include "semphr.h" -#include "system/passert.h" - -#define RT_I2C_WR 0x0000 -#define RT_I2C_RD (1u << 0) -#define RT_I2C_ADDR_10BIT (1u << 2) /* this is a ten bit chip address */ -#define RT_I2C_NO_START (1u << 4) -#define RT_I2C_IGNORE_NACK (1u << 5) -#define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */ - -/* read/write specified memory address, - in this mode, no STOP condition is inserted between memory address and data */ -#define RT_I2C_MEM_ACCESS (1u << 7) - -typedef struct I2CMsg { - uint16_t addr; - uint16_t mem_addr; - uint16_t mem_addr_size; - uint16_t flags; - uint16_t len; - uint8_t *buf; -} I2CDeviceMsg; - -I2CDeviceMsg msgs[2]; -uint32_t msgs_num; - -static void hal_semaphore_give(I2CBusState *bus_state) { - xSemaphoreGive(bus_state->event_semaphore); -} -static portBASE_TYPE hal_semaphore_give_from_isr(I2CBusState *bus) { - portBASE_TYPE should_context_switch = pdFALSE; - (void)xSemaphoreGiveFromISR(bus->event_semaphore, &should_context_switch); - return should_context_switch; -} - -void i2c_irq_handler(I2CBus *bus) { - I2C_HandleTypeDef *handle = (I2C_HandleTypeDef *)&(bus->hal->hi2c); - - if (handle->XferISR != NULL) { - handle->XferISR(handle, 0, 0); - } - - if ((HAL_I2C_STATE_BUSY_TX != handle->State) && (HAL_I2C_STATE_BUSY_RX != handle->State)) { - hal_semaphore_give_from_isr(bus->state); - HAL_I2C_StateTypeDef i2c_state = HAL_I2C_GetState(handle); - if (i2c_state == HAL_I2C_STATE_READY) - bus->state->transfer_event = I2CTransferEvent_TransferComplete; - else if (i2c_state == HAL_I2C_STATE_TIMEOUT) - bus->state->transfer_event = I2CTransferEvent_Timeout; - else - bus->state->transfer_event = I2CTransferEvent_Error; - __HAL_I2C_DISABLE(handle); - } -} - -void i2c_dma_irq_handler(I2CBus *bus) { - I2C_HandleTypeDef *handle = (I2C_HandleTypeDef *)&(bus->hal->hi2c); - if (handle->State == HAL_I2C_STATE_BUSY_TX) { - HAL_DMA_IRQHandler(handle->hdmatx); - } else if (handle->State == HAL_I2C_STATE_BUSY_RX) { - HAL_DMA_IRQHandler(handle->hdmarx); - } else { - if (handle->hdmatx != NULL) - if (HAL_DMA_STATE_BUSY == handle->hdmatx->State) HAL_DMA_IRQHandler(handle->hdmatx); - - if (handle->hdmarx != NULL) - if (HAL_DMA_STATE_BUSY == handle->hdmarx->State) HAL_DMA_IRQHandler(handle->hdmarx); - } -} - -static HAL_StatusTypeDef i2c_hal_master_xfer(I2CDeviceBusHal *i2c_hal, I2CDeviceMsg msgs[], - uint32_t num) { - uint32_t index = 0; - I2CDeviceBusHal *hal = NULL; - I2CDeviceMsg *msg = NULL; - HAL_StatusTypeDef status = HAL_ERROR; - uint16_t mem_addr_type; - - PBL_ASSERTN(i2c_hal != NULL); - hal = i2c_hal; - I2C_HandleTypeDef *handle = (I2C_HandleTypeDef *)&hal->hi2c; - __HAL_I2C_ENABLE(handle); - - for (index = 0; index < num; index++) { - msg = (I2CDeviceMsg *)&msgs[index]; - if (msg->flags & RT_I2C_MEM_ACCESS) { - if (8 >= msg->mem_addr_size) { - mem_addr_type = I2C_MEMADD_SIZE_8BIT; - } else { - mem_addr_type = I2C_MEMADD_SIZE_16BIT; - } - if (msg->flags & RT_I2C_RD) { - if (hal->hdma.Instance) { - HAL_DMA_Init(hal->hi2c.hdmarx); - mpu_dcache_invalidate(msg->buf, msg->len); - status = HAL_I2C_Mem_Read_DMA(handle, msg->addr, msg->mem_addr, mem_addr_type, msg->buf, - msg->len); - } else if (hal->i2c_state->int_enabled) { - status = HAL_I2C_Mem_Read_IT(handle, msg->addr, msg->mem_addr, mem_addr_type, msg->buf, - msg->len); - } else { - status = HAL_I2C_Mem_Read(handle, msg->addr, msg->mem_addr, mem_addr_type, msg->buf, - msg->len, hal->timeout); - } - } else { - if (hal->hdma.Instance) { - HAL_DMA_Init(hal->hi2c.hdmatx); - status = HAL_I2C_Mem_Write_DMA(handle, msg->addr, msg->mem_addr, mem_addr_type, msg->buf, - msg->len); - } else if (hal->i2c_state->int_enabled) { - status = HAL_I2C_Mem_Write_IT(handle, msg->addr, msg->mem_addr, mem_addr_type, msg->buf, - msg->len); - } else { - status = HAL_I2C_Mem_Write(handle, msg->addr, msg->mem_addr, mem_addr_type, msg->buf, - msg->len, hal->timeout); - } - } - } else { - if (msg->flags & RT_I2C_RD) { - if (hal->hdma.Instance) { - HAL_DMA_Init(hal->hi2c.hdmarx); - mpu_dcache_invalidate(msg->buf, msg->len); - status = HAL_I2C_Master_Receive_DMA(handle, msg->addr, msg->buf, msg->len); - } else if (hal->i2c_state->int_enabled) { - status = HAL_I2C_Master_Receive_IT(handle, msg->addr, msg->buf, msg->len); - } else { - status = HAL_I2C_Master_Receive(handle, msg->addr, msg->buf, msg->len, hal->timeout); - } - } else { - if (hal->hdma.Instance) { - HAL_DMA_Init(hal->hi2c.hdmatx); - status = HAL_I2C_Master_Transmit_DMA(handle, msg->addr, msg->buf, msg->len); - } else if (hal->i2c_state->int_enabled) { - status = HAL_I2C_Master_Transmit_IT(handle, msg->addr, msg->buf, msg->len); - } else { - status = HAL_I2C_Master_Transmit(handle, msg->addr, msg->buf, msg->len, hal->timeout); - } - } - } - if (HAL_OK != status) goto exit; - - while (1) { - HAL_I2C_StateTypeDef i2c_state = HAL_I2C_GetState(handle); - - if (HAL_I2C_STATE_READY == i2c_state) { - status = HAL_OK; - } else if (HAL_I2C_STATE_TIMEOUT == i2c_state) { - status = HAL_TIMEOUT; - } else if ((HAL_I2C_STATE_BUSY_TX == i2c_state) || - (HAL_I2C_STATE_BUSY_RX == i2c_state)) // Interrupt or DMA mode, wait semaphore - { - status = HAL_BUSY; - } else { - status = HAL_ERROR; - } - - break; - } - if (HAL_OK != status) goto exit; - if (hal->hi2c.ErrorCode) goto exit; - - hal->hi2c.Instance->CR |= I2C_CR_UR; - HAL_Delay_us(1); // Delay at least 9 cycle. - hal->hi2c.Instance->CR &= ~I2C_CR_UR; - } - -exit: - - if (status != HAL_BUSY) __HAL_I2C_DISABLE(handle); - return status; -} - -void i2c_hal_init_transfer(I2CBus *bus) { - if (I2CTransferType_SendRegisterAddress == bus->state->transfer.type) { - if (bus->state->transfer.direction == I2CTransferDirection_Write) { - msgs[0].addr = bus->state->transfer.device_address; - msgs[0].mem_addr = bus->state->transfer.register_address; - msgs[0].mem_addr_size = 8; // 8bit address - msgs[0].flags = RT_I2C_WR | RT_I2C_MEM_ACCESS; - msgs[0].len = bus->state->transfer.size; - msgs[0].buf = bus->state->transfer.data; - msgs_num = 1; - } else { - msgs[0].addr = bus->state->transfer.device_address; - msgs[0].mem_addr = bus->state->transfer.register_address; - msgs[0].mem_addr_size = 8; // 8bit address - msgs[0].flags = RT_I2C_RD | RT_I2C_MEM_ACCESS; - msgs[0].len = bus->state->transfer.size; - msgs[0].buf = bus->state->transfer.data; - msgs_num = 1; - } - - } else { - if (bus->state->transfer.direction == I2CTransferDirection_Write) { - msgs[0].addr = bus->state->transfer.device_address; - msgs[0].flags = RT_I2C_WR; - msgs[0].len = bus->state->transfer.size; - msgs[0].buf = bus->state->transfer.data; - msgs_num = 1; - } else { - msgs[0].addr = bus->state->transfer.device_address; - msgs[0].flags = RT_I2C_RD; - msgs[0].len = bus->state->transfer.size; - msgs[0].buf = bus->state->transfer.data; - msgs_num = 1; - } - } -} - -void i2c_hal_abort_transfer(I2CBus *bus) { - struct I2CBusHal *hal = (struct I2CBusHal *)bus->hal; - HAL_I2C_Reset(&(hal->hi2c)); - PBL_LOG_D(LOG_DOMAIN_I2C, LOG_LEVEL_INFO, "reset and send 9 clks"); - __HAL_I2C_DISABLE(&(hal->hi2c)); -} - -void i2c_hal_start_transfer(I2CBus *bus) { - struct I2CBusHal *hal = (struct I2CBusHal *)bus->hal; - HAL_StatusTypeDef status = i2c_hal_master_xfer(hal, &msgs[0], msgs_num); - if (status == HAL_BUSY) { - return; - } - - if (status == HAL_OK) { - bus->state->transfer_event = I2CTransferEvent_TransferComplete; - if (((hal->hdma.Instance == NULL)) && (hal->i2c_state->int_enabled == false)) - hal_semaphore_give(bus->state); - } else if (status == HAL_TIMEOUT) - bus->state->transfer_event = I2CTransferEvent_Timeout; - else - bus->state->transfer_event = I2CTransferEvent_Error; - return; -} - -int i2c_hal_configure(I2CDeviceBusHal *i2c_hal) { - HAL_StatusTypeDef ret = HAL_OK; - PBL_ASSERTN(i2c_hal != NULL); - I2C_HandleTypeDef *handle = (I2C_HandleTypeDef *)&(i2c_hal->hi2c); - - HAL_RCC_EnableModule(i2c_hal->module); - ret = HAL_I2C_Init(handle); - - if (ret != HAL_OK) { - PBL_LOG_D(LOG_DOMAIN_I2C, LOG_LEVEL_ERROR, "I2C [%s] bus_configure fail!", - i2c_hal->device_name); - return -1; - } - PBL_LOG_D(LOG_DOMAIN_I2C, LOG_LEVEL_INFO, "I2C [%s] bus_configure ok!", i2c_hal->device_name); - return 0; -} - -void i2c_hal_enable(I2CBus *bus) { HAL_RCC_EnableModule(bus->hal->module); } - -void i2c_hal_disable(I2CBus *bus) { HAL_RCC_DisableModule(bus->hal->module); } - -bool i2c_hal_is_busy(I2CBus *bus) { - bool ret = false; - struct I2CBusHal *hal = (struct I2CBusHal *)bus->hal; - if (HAL_I2C_GetState(&(hal->hi2c)) != HAL_I2C_STATE_READY) ret = true; - return ret; -} - -static int i2c_hal_hw_init(struct I2CBusHal *i2c_hal) { - int ret = 0; - struct dma_config dma_rtx_config; - - if (i2c_hal->hdma.Instance != NULL) { - __HAL_LINKDMA(&(i2c_hal->hi2c), hdmarx, i2c_hal->hdma); - __HAL_LINKDMA(&(i2c_hal->hi2c), hdmatx, i2c_hal->hdma); - dma_rtx_config.Instance = i2c_hal->hdma.Instance; - dma_rtx_config.request = i2c_hal->hdma.Init.Request; - HAL_I2C_DMA_Init(&(i2c_hal->hi2c), &dma_rtx_config, &dma_rtx_config); - - HAL_NVIC_SetPriority(i2c_hal->dma_irqn, i2c_hal->dma_irq_priority, 0); - NVIC_EnableIRQ(i2c_hal->dma_irqn); - - } else if (i2c_hal->i2c_state->int_enabled) { - HAL_NVIC_SetPriority(i2c_hal->irqn, i2c_hal->irq_priority, 0); - NVIC_EnableIRQ(i2c_hal->irqn); - } - ret = i2c_hal_configure(i2c_hal); - - if (ret < 0) { - return ret; - } - return ret; -} - -void i2c_hal_init(I2CBus *bus) { - int ret = 0; - PBL_ASSERTN(bus != NULL); - ret = i2c_hal_hw_init((struct I2CBusHal *)bus->hal); - if (ret < 0) { - PBL_LOG_D(LOG_DOMAIN_I2C, LOG_LEVEL_ERROR, "I2C [%s] hw init fail!", bus->hal->device_name); - } else { - PBL_LOG_D(LOG_DOMAIN_I2C, LOG_LEVEL_INFO, "I2C [%s] hw init ok!", bus->hal->device_name); - } - return; -} - -void i2c_hal_pins_set_gpio(I2CBus *bus) {} - -void i2c_hal_pins_set_i2c(I2CBus *bus) { - HAL_PIN_Set(bus->hal->scl.pad, bus->hal->scl.func, bus->hal->scl.flags, 1); - HAL_PIN_Set(bus->hal->sda.pad, bus->hal->sda.func, bus->hal->sda.flags, 1); -} diff --git a/src/fw/drivers/sf32lb52/i2c_hal_definitions.h b/src/fw/drivers/sf32lb52/i2c_hal_definitions.h deleted file mode 100644 index 298b49f8e4..0000000000 --- a/src/fw/drivers/sf32lb52/i2c_hal_definitions.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -#include "board/board.h" -#include "drivers/i2c_definitions.h" - -typedef struct I2CState { - bool int_enabled; - bool initialized; -} I2CDeviceState; - -typedef const struct I2CBusHal { - I2CDeviceState *i2c_state; - I2C_HandleTypeDef hi2c; - DMA_HandleTypeDef hdma; - const void *dev; - const char *device_name; - Pinmux scl; - Pinmux sda; - uint8_t core; - RCC_MODULE_TYPE module; - IRQn_Type irqn; - uint8_t irq_priority; - IRQn_Type dma_irqn; - uint8_t dma_irq_priority; - uint32_t timeout; - -} I2CDeviceBusHal; - -void i2c_irq_handler(I2CBus *bus); -void i2c_dma_irq_handler(I2CBus *bus); diff --git a/src/fw/drivers/sf32lb52/jdi_lpm015m135a.c b/src/fw/drivers/sf32lb52/jdi_lpm015m135a.c deleted file mode 100644 index 7b2ec6ba9c..0000000000 --- a/src/fw/drivers/sf32lb52/jdi_lpm015m135a.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jdi_lpm015m135a.h" - -#include "board/board.h" -#include "board/display.h" -#include "drivers/display/display.h" -#include "kernel/util/stop.h" -#include "os/mutex.h" -#include "system/logging.h" - -#include "FreeRTOS.h" -#include "semphr.h" - -#include "bf0_hal.h" -#include "bf0_hal_lcdc.h" -#include "bf0_hal_lptim.h" - -#define BYTE_222_TO_332(data) ((((data) & 0x30) << 2) | (((data) & 0x0c) << 1) | ((data) & 0x03)) - -static uint8_t s_framebuffer[DISPLAY_FRAMEBUFFER_BYTES]; -static PebbleMutex *s_update; -static SemaphoreHandle_t s_write_done; - -static void prv_fb_222_to_332(const DisplayRow *row) { - for (uint16_t count = 0U; count < PBL_DISPLAY_WIDTH; count++) { - s_framebuffer[row->address * PBL_DISPLAY_WIDTH + count] = BYTE_222_TO_332(*(row->data + count)); - } -} - -// TODO(SF32LB52): Improve/clarify display on/off code -static void prv_display_on() { - LPTIM_TypeDef *lptim = DISPLAY->vcom.lptim; - - lptim->CFGR |= LPTIM_INTLOCKSOURCE_APBCLOCK; - lptim->ARR = 3750000 / DISPLAY->vcom.freq_hz; - lptim->CMP = lptim->ARR / 2; - lptim->CR |= LPTIM_CR_ENABLE; - lptim->CR |= LPTIM_CR_CNTSTRT; - - MODIFY_REG(hwp_hpsys_aon->CR1, HPSYS_AON_CR1_PINOUT_SEL0_Msk, 3 << HPSYS_AON_CR1_PINOUT_SEL0_Pos); - MODIFY_REG(hwp_hpsys_aon->CR1, HPSYS_AON_CR1_PINOUT_SEL1_Msk, 3 << HPSYS_AON_CR1_PINOUT_SEL1_Pos); - - MODIFY_REG(hwp_rtc->PBR0R, RTC_PBR0R_SEL_Msk, 3 << RTC_PBR0R_SEL_Pos); - MODIFY_REG(hwp_rtc->PBR1R, RTC_PBR1R_SEL_Msk, 2 << RTC_PBR1R_SEL_Pos); - - MODIFY_REG(hwp_rtc->PBR0R, RTC_PBR0R_OE_Msk, 1 << RTC_PBR0R_OE_Pos); - MODIFY_REG(hwp_rtc->PBR1R, RTC_PBR1R_OE_Msk, 1 << RTC_PBR1R_OE_Pos); -} - -static void prv_display_off() { - LPTIM_TypeDef *lptim = DISPLAY->vcom.lptim; - - lptim->CR &= ~LPTIM_CR_ENABLE; - lptim->CR &= ~LPTIM_CR_CNTSTRT; - - MODIFY_REG(hwp_hpsys_aon->CR1, HPSYS_AON_CR1_PINOUT_SEL0_Msk, 0 << HPSYS_AON_CR1_PINOUT_SEL0_Pos); - MODIFY_REG(hwp_hpsys_aon->CR1, HPSYS_AON_CR1_PINOUT_SEL1_Msk, 0 << HPSYS_AON_CR1_PINOUT_SEL1_Pos); - - MODIFY_REG(hwp_rtc->PBR0R, RTC_PBR0R_SEL_Msk | RTC_PBR0R_OE_Msk, 0); - MODIFY_REG(hwp_rtc->PBR1R, RTC_PBR1R_SEL_Msk | RTC_PBR1R_OE_Msk, 0); - - // IE=0, PE=0, OE=0 - MODIFY_REG(hwp_rtc->PBR0R, RTC_PBR0R_IE_Msk | RTC_PBR0R_PE_Msk | RTC_PBR0R_OE_Msk, 0); - MODIFY_REG(hwp_rtc->PBR1R, RTC_PBR1R_IE_Msk | RTC_PBR1R_PE_Msk | RTC_PBR1R_OE_Msk, 0); -} - -void jdi_lpm015m135a_irq_handler(DisplayJDIDevice *disp) { - DisplayJDIState *state = DISPLAY->state; - HAL_LCDC_IRQHandler(&state->hlcdc); -} - -void HAL_LCDC_SendLayerDataCpltCbk(LCDC_HandleTypeDef *lcdc) { - BaseType_t woken; - xSemaphoreGiveFromISR(s_write_done, &woken); - portYIELD_FROM_ISR(woken); -} - -void display_init(void) { - DisplayJDIState *state = DISPLAY->state; - - HAL_PIN_Set(DISPLAY->pinmux.xrst.pad, DISPLAY->pinmux.xrst.func, DISPLAY->pinmux.xrst.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.vst.pad, DISPLAY->pinmux.vst.func, DISPLAY->pinmux.vst.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.vck.pad, DISPLAY->pinmux.vck.func, DISPLAY->pinmux.vck.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.enb.pad, DISPLAY->pinmux.enb.func, DISPLAY->pinmux.enb.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.hst.pad, DISPLAY->pinmux.hst.func, DISPLAY->pinmux.hst.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.hck.pad, DISPLAY->pinmux.hck.func, DISPLAY->pinmux.hck.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.r1.pad, DISPLAY->pinmux.r1.func, DISPLAY->pinmux.r1.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.r2.pad, DISPLAY->pinmux.r2.func, DISPLAY->pinmux.r2.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.g1.pad, DISPLAY->pinmux.g1.func, DISPLAY->pinmux.g1.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.g2.pad, DISPLAY->pinmux.g2.func, DISPLAY->pinmux.g2.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.b1.pad, DISPLAY->pinmux.b1.func, DISPLAY->pinmux.b1.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.b2.pad, DISPLAY->pinmux.b2.func, DISPLAY->pinmux.b2.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.vcom.pad, DISPLAY->pinmux.vcom.func, DISPLAY->pinmux.vcom.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.va.pad, DISPLAY->pinmux.va.func, DISPLAY->pinmux.va.flags, 1); - HAL_PIN_Set(DISPLAY->pinmux.vb.pad, DISPLAY->pinmux.vb.func, DISPLAY->pinmux.vb.flags, 1); - - state->hlcdc.Init = (LCDC_InitTypeDef){ - .lcd_itf = LCDC_INTF_JDI_PARALLEL, - .color_mode = LCDC_PIXEL_FORMAT_RGB332, - .freq = 746268, // HCK frequency - .cfg = - { - .jdi = - (JDI_LCD_CFG){ - .bank_col_head = 2, - .valid_columns = PBL_DISPLAY_WIDTH, - .bank_col_tail = 6, - .bank_row_head = 0, - .valid_rows = PBL_DISPLAY_HEIGHT, - .bank_row_tail = 6, - .enb_start_col = 3, - .enb_end_col = 99, - }, - }, - }; - - - HAL_LCDC_Init(&state->hlcdc); - HAL_LCDC_LayerReset(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT); - HAL_LCDC_LayerSetCmpr(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, 0); - HAL_LCDC_LayerSetFormat(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, LCDC_PIXEL_FORMAT_RGB332); - - HAL_NVIC_SetPriority(DISPLAY->irqn, DISPLAY->irq_priority, 0); - HAL_NVIC_EnableIRQ(DISPLAY->irqn); - - HAL_LCDC_Enter_LP(&state->hlcdc); - - s_update = mutex_create(); - vSemaphoreCreateBinary(s_write_done); - - display_clear(); - prv_display_on(); -} - -void display_clear(void) { - DisplayJDIState *state = DISPLAY->state; - - mutex_lock(s_update); - - memset(s_framebuffer, 0xFF, DISPLAY_FRAMEBUFFER_BYTES); - - HAL_LCDC_Exit_LP(&state->hlcdc); - HAL_LCDC_SetROIArea(&state->hlcdc, 0, 0, PBL_DISPLAY_WIDTH - 1, PBL_DISPLAY_HEIGHT - 1); - HAL_LCDC_LayerSetData(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, s_framebuffer, 0, 0, - PBL_DISPLAY_WIDTH - 1, PBL_DISPLAY_HEIGHT - 1); - HAL_LCDC_SendLayerData_IT(&state->hlcdc); - xSemaphoreTake(s_write_done, portMAX_DELAY); - HAL_LCDC_Enter_LP(&state->hlcdc); - - mutex_unlock(s_update); -} - -void display_set_enabled(bool enabled) { - if (enabled) { - prv_display_on(); - } else { - prv_display_off(); - } -} - -bool display_update_in_progress(void) { - bool in_progress = !mutex_lock_with_timeout(s_update, 0); - if (!in_progress) { - mutex_unlock(s_update); - } - - return in_progress; -} - -void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { - DisplayJDIState *state = DISPLAY->state; - DisplayRow row; - uint16_t rows = 0U; - uint16_t y0 = 0U; - bool y0_set = false; - - mutex_lock(s_update); - - stop_mode_disable(InhibitorDisplay); - - // convert all rows requiring an update to 332 format - while (nrcb(&row)) { - if (!y0_set) { - y0 = row.address; - y0_set = true; - } - - prv_fb_222_to_332(&row); - - rows++; - } - - if (rows > 0U) { - HAL_LCDC_Exit_LP(&state->hlcdc); - HAL_LCDC_SetROIArea(&state->hlcdc, 0, y0, PBL_DISPLAY_WIDTH - 1, y0 + rows - 1); - HAL_LCDC_LayerSetData(&state->hlcdc, HAL_LCDC_LAYER_DEFAULT, - &s_framebuffer[y0 * PBL_DISPLAY_WIDTH], 0, y0, PBL_DISPLAY_WIDTH - 1, - y0 + rows - 1); - HAL_LCDC_SendLayerData_IT(&state->hlcdc); - xSemaphoreTake(s_write_done, portMAX_DELAY); - HAL_LCDC_Enter_LP(&state->hlcdc); - } - - if (uccb) { - uccb(); - } - - stop_mode_enable(InhibitorDisplay); - - mutex_unlock(s_update); -} - -void display_pulse_vcom(void) {} - -void display_show_splash_screen(void) {} - -void display_show_panic_screen(uint32_t error_code) {} - -uint32_t display_baud_rate_change(uint32_t new_frequency_hz) { return 0U; } - -// Stubs for display offset -void display_set_offset(GPoint offset) {} - -GPoint display_get_offset(void) { return GPointZero; } diff --git a/src/fw/drivers/sf32lb52/jdi_lpm015m135a.h b/src/fw/drivers/sf32lb52/jdi_lpm015m135a.h deleted file mode 100755 index 4fa600b7bd..0000000000 --- a/src/fw/drivers/sf32lb52/jdi_lpm015m135a.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "board/board.h" - -#include "bf0_hal.h" -#include "bf0_hal_lcdc.h" - -typedef struct DisplayJDIState { - LCDC_HandleTypeDef hlcdc; -} DisplayJDIState; - -typedef const struct DisplayJDIDevice { - DisplayJDIState *state; - IRQn_Type irqn; - uint8_t irq_priority; - struct { - LPTIM_TypeDef *lptim; - uint8_t freq_hz; - } vcom; - struct { - Pinmux xrst; - Pinmux vst; - Pinmux vck; - Pinmux enb; - Pinmux hst; - Pinmux hck; - Pinmux r1; - Pinmux r2; - Pinmux g1; - Pinmux g2; - Pinmux b1; - Pinmux b2; - Pinmux vcom; - Pinmux va; - Pinmux vb; - } pinmux; -} DisplayJDIDevice; - -void jdi_lpm015m135a_irq_handler(DisplayJDIDevice *disp); diff --git a/src/fw/drivers/sf32lb52/lptim_systick.c b/src/fw/drivers/sf32lb52/lptim_systick.c deleted file mode 100644 index b6abb77f13..0000000000 --- a/src/fw/drivers/sf32lb52/lptim_systick.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "FreeRTOS.h" -#include "task.h" -#include "mcu/interrupts.h" -#include "drivers/rtc.h" -#include "drivers/lptim_systick.h" -#include "system/logging.h" - -#include "bf0_hal_lptim.h" -#include "bf0_hal_aon.h" - -/* LPRC10K frequency nearly 8~9 KHz, fixed to 8KHz for systick */ -#define SYSTICK_CLOCK_HZ 8000 -#define SYSTICK_ONE_TICK_HZ (SYSTICK_CLOCK_HZ / RTC_TICKS_HZ) - -#if !defined(configUSE_TICKLESS_IDLE) || (configUSE_TICKLESS_IDLE != 2) -#error "lptim systick requires configUSE_TICKLESS_IDLE=2" -#endif - -#ifdef SF32LB52_USE_LXT -#error "lptim systick not compatible with LXT" -#endif - -static LPTIM_HandleTypeDef s_lptim1_handle = {0}; -static bool s_lptim_systick_initialized = false; -static uint32_t s_last_idle_counter = 0; - -void lptim_systick_init(void) -{ - HAL_LPTIM_InitDefault(&s_lptim1_handle); - s_lptim1_handle.Instance = LPTIM1; - // Using RC10K as LPTIM1 clock source. - s_lptim1_handle.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; - s_lptim1_handle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1; - s_lptim1_handle.Init.Clock.IntSource = LPTIM_INTCLOCKSOURCE_LPCLOCK; - s_lptim1_handle.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL; - HAL_LPTIM_Init(&s_lptim1_handle); - - NVIC_SetPriority(LPTIM1_IRQn, configKERNEL_INTERRUPT_PRIORITY); - NVIC_EnableIRQ(LPTIM1_IRQn); - - HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LPTIM1, AON_PIN_MODE_HIGH); // LPPTIM1 OC wakeup - HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LP2HP_IRQ, AON_PIN_MODE_HIGH); // LP2HP mailbox interrupt - HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LP2HP_REQ, AON_PIN_MODE_HIGH); // LP2HP manual wakeup - - s_lptim_systick_initialized = true; -} - -bool lptim_systick_is_initialized(void) -{ - return s_lptim_systick_initialized; -} - -void lptim_systick_enable(void) -{ - __HAL_LPTIM_ENABLE(&s_lptim1_handle); - __HAL_LPTIM_COUNTRST_RESET(&s_lptim1_handle); - __HAL_LPTIM_AUTORELOAD_SET(&s_lptim1_handle, 0xFFFF); - __HAL_LPTIM_COMPARE_SET(&s_lptim1_handle, SYSTICK_ONE_TICK_HZ); - __HAL_LPTIM_ENABLE_IT(&s_lptim1_handle, LPTIM_IT_OCIE); - - __HAL_LPTIM_START_CONTINUOUS(&s_lptim1_handle); -} - -void lptim_systick_pause(void) -{ - /* NOP */ -} - -void lptim_systick_resume(void) -{ - /* NOP */ -} - -void lptim_systick_tickless_idle(uint32_t ticks_from_now) -{ - // In tickless idle mode, use OCWE instead. - uint32_t counter = LPTIM1->CNT; - s_last_idle_counter = counter; - - counter += ticks_from_now * SYSTICK_ONE_TICK_HZ; - if (counter >= 0xFFFF) { - counter -= 0xFFFF; - } - - __HAL_LPTIM_COMPARE_SET(&s_lptim1_handle, counter); - __HAL_LPTIM_ENABLE_IT(&s_lptim1_handle, LPTIM_IT_OCWE); -} - -uint32_t lptim_systick_get_elapsed_ticks(void) -{ - uint32_t counter = LPTIM1->CNT; - - if (counter < s_last_idle_counter) { - counter += 0x10000; - } - - return (counter - s_last_idle_counter) / SYSTICK_ONE_TICK_HZ; -} - -static inline void lptim_systick_next_tick_setup(void) -{ - uint32_t counter = LPTIM1->CNT; - - counter += SYSTICK_ONE_TICK_HZ; - if (counter >= 0xFFFF) { - counter -= 0xFFFF; - } - - __HAL_LPTIM_COMPARE_SET(&s_lptim1_handle, counter); -} - -void LPTIM1_IRQHandler(void) -{ - if (__HAL_LPTIM_GET_FLAG(&s_lptim1_handle, LPTIM_FLAG_OC) != RESET) { - __HAL_LPTIM_CLEAR_FLAG(&s_lptim1_handle, LPTIM_IT_OCIE); - lptim_systick_next_tick_setup(); - - // If not in tickless idle mode, call SysTick_Handler directly. - if (__HAL_LPTIM_GET_FLAG(&s_lptim1_handle, LPTIM_FLAG_OCWKUP) == RESET) { - extern void SysTick_Handler(); - SysTick_Handler(); - } - } - - if (__HAL_LPTIM_GET_FLAG(&s_lptim1_handle, LPTIM_FLAG_OCWKUP) != RESET) { - __HAL_LPTIM_DISABLE_IT(&s_lptim1_handle, LPTIM_IT_OCWE); - __HAL_LPTIM_CLEAR_FLAG(&s_lptim1_handle, LPTIM_ICR_WKUPCLR); - } -} - -void AON_IRQHandler(void) -{ - NVIC_DisableIRQ(AON_IRQn); - HAL_HPAON_CLEAR_POWER_MODE(); - - HAL_HPAON_CLEAR_WSR(0); -} diff --git a/src/fw/drivers/sf32lb52/mcu.c b/src/fw/drivers/sf32lb52/mcu.c index 2d06a4f3d8..890b63ddde 100644 --- a/src/fw/drivers/sf32lb52/mcu.c +++ b/src/fw/drivers/sf32lb52/mcu.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/mcu.h" diff --git a/src/fw/drivers/sf32lb52/pwm.c b/src/fw/drivers/sf32lb52/pwm.c deleted file mode 100644 index dd264b8d84..0000000000 --- a/src/fw/drivers/sf32lb52/pwm.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "board/board.h" -#include "drivers/pwm.h" -#include "system/passert.h" -#include "kernel/util/stop.h" - -#include "bf0_hal_tim.h" - -#define MAX_PERIOD_GPT 0xFFFFU -#define MAX_PERIOD_ATM 0xFFFFFFFFU -#define MIN_PERIOD 3U -#define MIN_PULSE 1U - -void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { - GPT_HandleTypeDef *htim = &pwm->state->handle; - uint32_t period, pulse; - uint32_t gpt_clock, psc; - uint32_t channel; - uint32_t max_period; - HAL_StatusTypeDef ret; - - // converts the channel number to the channel number of HAL library - channel = (pwm->state->channel - 1U) << 2U; - - if (IS_GPT_ADVANCED_INSTANCE(htim->Instance) != RESET) { - max_period = MAX_PERIOD_ATM; - } else { - max_period = MAX_PERIOD_GPT; - } - - if (htim->Instance == hwp_gptim2) { - gpt_clock = 24000000U; - } else { - gpt_clock = HAL_RCC_GetPCLKFreq(htim->core, 1); - } - - // Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns - gpt_clock /= 1000000UL; - period = (uint64_t)pwm->state->value * gpt_clock / 1000ULL; - psc = period / max_period + 1U; - period = period / psc; - __HAL_GPT_SET_PRESCALER(htim, psc - 1U); - - if (period < MIN_PERIOD) { - period = MIN_PERIOD; - } - __HAL_GPT_SET_AUTORELOAD(htim, period - 1U); - - // transfer cycle to ns - pulse = duty_cycle * pwm->state->value / pwm->state->resolution; - pulse = (uint64_t)pulse * gpt_clock / psc / 1000ULL; - - if (pulse < MIN_PULSE) { - pulse = MIN_PULSE; - } else if (pulse >= period) { - // if pulse reach to 100%, need set pulse = period + 1, because pulse = - // period, the real percentage = 99.9983% - pulse = period + 1U; - } - __HAL_GPT_SET_COMPARE(htim, channel, pulse - 1); - - // Update frequency value - ret = HAL_GPT_GenerateEvent(htim, GPT_EVENTSOURCE_UPDATE); - PBL_ASSERTN(ret == HAL_OK); -} - -void pwm_enable(const PwmConfig *pwm, bool enable) { - GPT_HandleTypeDef *htim = &pwm->state->handle; - HAL_StatusTypeDef ret; - uint32_t channel; - - /* Converts the channel number to the channel number of Hal library */ - channel = (pwm->state->channel - 1U) << 2U; - - if (enable) { - GPT_OC_InitTypeDef oc_config = {0}; - - oc_config.OCMode = GPT_OCMODE_PWM1; - oc_config.Pulse = __HAL_GPT_GET_COMPARE(htim, channel); - oc_config.OCPolarity = GPT_OCPOLARITY_HIGH; - oc_config.OCFastMode = GPT_OCFAST_DISABLE; - - ret = HAL_GPT_PWM_ConfigChannel(htim, &oc_config, channel); - PBL_ASSERTN(ret == HAL_OK); - - ret = HAL_GPT_PWM_Start(htim, channel); - PBL_ASSERTN(ret == HAL_OK); - - stop_mode_disable(InhibitorPWM); - } else { - ret = HAL_GPT_PWM_Stop(htim, channel); - PBL_ASSERTN(ret == HAL_OK); - - stop_mode_enable(InhibitorPWM); - } -} - -void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { - GPT_HandleTypeDef *htim = &pwm->state->handle; - GPT_ClockConfigTypeDef *clock_config = &pwm->state->clock_config; - HAL_StatusTypeDef ret; - - PBL_ASSERTN((resolution != 0U) && (frequency != 0U)); - - pwm->state->resolution = resolution; - pwm->state->value = 1000000000UL / (frequency); - - HAL_PIN_Set(pwm->pwm_pin.pad, pwm->pwm_pin.func, pwm->pwm_pin.flags, 1); - - ret = HAL_GPT_Base_Init(htim); - PBL_ASSERTN(ret == HAL_OK); - - ret = HAL_GPT_ConfigClockSource(htim, clock_config); - PBL_ASSERTN(ret == HAL_OK); - - ret = HAL_GPT_PWM_Init(htim); - PBL_ASSERTN(ret == HAL_OK); - - __HAL_GPT_URS_ENABLE(htim); -} diff --git a/src/fw/drivers/sf32lb52/qspi.c b/src/fw/drivers/sf32lb52/qspi.c index d763839bd9..ebe8462804 100644 --- a/src/fw/drivers/sf32lb52/qspi.c +++ b/src/fw/drivers/sf32lb52/qspi.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" #include "drivers/flash/flash_impl.h" @@ -20,10 +7,13 @@ #include "drivers/flash/qspi_flash_part_definitions.h" #include "flash_region/flash_region.h" #include "kernel/pbl_malloc.h" +#include "mcu/cache.h" #include "system/passert.h" #include "system/status_codes.h" #include "util/math.h" +#define SEC_ADDR_TO_IDX(addr) (((addr) >> 12U) - 1U) + static bool prv_blank_check_poll(uint32_t addr, bool is_subsector) { const uint32_t size_bytes = is_subsector ? SUBSECTOR_SIZE_BYTES : SECTOR_SIZE_BYTES; const uint32_t BUF_SIZE_BYTES = 128; @@ -65,15 +55,25 @@ static int prv_erase_nor(QSPIFlash *dev, uint32_t addr, uint32_t size) { while (remain > 0) { portENTER_CRITICAL(); - res = HAL_QSPIEX_SECT_ERASE(hflash, taddr); - portEXIT_CRITICAL(); - if (res != 0) { - res = -1; - goto end; + if ((taddr & (SECTOR_SIZE_BYTES - 1)) == 0 && remain >= SECTOR_SIZE_BYTES) { + res = HAL_QSPIEX_BLK64_ERASE(hflash, taddr); + portEXIT_CRITICAL(); + if (res != 0) { + res = -1; + goto end; + } + remain -= SECTOR_SIZE_BYTES; + taddr += SECTOR_SIZE_BYTES; + } else { + res = HAL_QSPIEX_SECT_ERASE(hflash, taddr); + portEXIT_CRITICAL(); + if (res != 0) { + res = -1; + goto end; + } + remain -= SUBSECTOR_SIZE_BYTES; + taddr += SUBSECTOR_SIZE_BYTES; } - - remain -= SUBSECTOR_SIZE_BYTES; - taddr += SUBSECTOR_SIZE_BYTES; } end: @@ -104,7 +104,15 @@ static int prv_write_nor(QSPIFlash *dev, uint32_t addr, uint8_t *buf, uint32_t s } else { tbuf = buf; } - + + // The QSPI write path reads `tbuf` over DMA, which bypasses the D-cache. + // Push any CPU-dirtied lines back to SRAM first so the flash receives the + // freshly-prepared bytes rather than stale memory. + uintptr_t flush_addr = (uintptr_t)tbuf; + size_t flush_size = size; + dcache_align(&flush_addr, &flush_size); + dcache_flush((const void *)flush_addr, flush_size); + taddr = addr - hflash->base; remain = size; @@ -162,10 +170,10 @@ bool qspi_flash_check_whoami(QSPIFlash *dev) { uint32_t id = ctx->dev_id; if (id == dev->state->part->qspi_id_value) { - PBL_LOG(LOG_LEVEL_INFO, "Flash is %s", dev->state->part->name); + PBL_LOG_INFO("Flash is %s", dev->state->part->name); return true; } else { - PBL_LOG(LOG_LEVEL_ERROR, "Flash isn't expected %s (whoami: 0x%" PRIx32 ")", + PBL_LOG_ERR("Flash isn't expected %s (whoami: 0x%" PRIx32 ")", dev->state->part->name, id); return false; } @@ -193,12 +201,14 @@ void qspi_flash_init(QSPIFlash *dev, QSPIFlashPart *part, bool coredump_mode) { dev->state->part = part; dev->qspi->state->ctx.dual_mode = 1; - res = HAL_FLASH_Init(&dev->qspi->state->ctx, (qspi_configure_t *)&dev->qspi->cfg, - &dev->qspi->state->hdma, (struct dma_config *)&dev->qspi->dma, + res = HAL_FLASH_Init(&dev->qspi->state->ctx, &dev->qspi->state->cfg, + &dev->qspi->state->hdma, &dev->qspi->state->dma, dev->qspi->clk_div); PBL_ASSERT(res == HAL_OK, "HAL_FLASH_Init failed"); + qspi_flash_check_whoami(dev); + dev->qspi->state->initialized = true; } @@ -241,22 +251,16 @@ status_t qspi_flash_get_write_status(QSPIFlash *dev) { return S_SUCCESS; } -void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active) {} +void qspi_flash_set_lower_power_mode(QSPIFlash *dev, bool active) { + if (active) { + HAL_FLASH_NOP_CMD(&dev->qspi->state->ctx.handle); + } +} status_t qspi_flash_blank_check(QSPIFlash *dev, uint32_t addr, bool is_subsector) { return prv_blank_check_poll(addr, is_subsector); } -status_t flash_impl_set_nvram_erase_status(bool is_subsector, FlashAddress addr) { - return S_SUCCESS; -} - -status_t flash_impl_clear_nvram_erase_status(void) { return S_SUCCESS; } - -status_t flash_impl_get_nvram_erase_status(bool *is_subsector, FlashAddress *addr) { - return S_FALSE; -} - status_t prv_qspi_security_register_check(QSPIFlash *dev, uint32_t addr) { bool addr_valid = false; @@ -294,7 +298,10 @@ status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_ uint32_t offset = addr % 4; uint32_t base_addr = addr - offset; + portENTER_CRITICAL(); res = HAL_QSPI_READ_OTP(hflash, base_addr, values, 4); + portEXIT_CRITICAL(); + if (res != 4) { return E_ERROR; } @@ -304,23 +311,26 @@ status_t qspi_flash_read_security_register(QSPIFlash *dev, uint32_t addr, uint8_ return S_SUCCESS; } -status_t qspi_flash_security_registers_are_locked(QSPIFlash *dev, bool *locked) { +status_t qspi_flash_security_register_is_locked(QSPIFlash *dev, uint32_t addr, bool *locked) { FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; uint8_t opt_val = 0; /* OPT operation are synchronous, one match means all matched. */ - opt_val = HAL_QSPI_GET_OTP_LB(hflash, dev->state->part->sec_registers.sec_regs[0]); + portENTER_CRITICAL(); + opt_val = HAL_QSPI_GET_OTP_LB(hflash, addr); + portEXIT_CRITICAL(); + if (opt_val == 0xff) { return E_ERROR; } - if (opt_val != 0) { + /* Security registers address*/ + if ((opt_val & (1U << SEC_ADDR_TO_IDX(addr))) != 0U) { *locked = true; - return S_SUCCESS; + } else { + *locked = false; } - *locked = false; - return S_SUCCESS; } @@ -333,7 +343,10 @@ status_t qspi_flash_erase_security_register(QSPIFlash *dev, uint32_t addr) { return res; } + portENTER_CRITICAL(); res = HAL_QSPI_ERASE_OTP(hflash, addr); + portEXIT_CRITICAL(); + if (res != 0) { return E_ERROR; } @@ -350,7 +363,10 @@ status_t qspi_flash_write_security_register(QSPIFlash *dev, uint32_t addr, uint8 return res; } + portENTER_CRITICAL(); res = HAL_QSPI_WRITE_OTP(hflash, addr, &val, 1); + portEXIT_CRITICAL(); + if (res != 1) { return E_ERROR; } @@ -362,14 +378,19 @@ const FlashSecurityRegisters *qspi_flash_security_registers_info(QSPIFlash *dev) return &dev->state->part->sec_registers; } -#ifdef RECOVERY_FW -status_t qspi_flash_lock_security_registers(QSPIFlash *dev) { +#ifdef CONFIG_RECOVERY_FW +status_t qspi_flash_lock_security_register(QSPIFlash *dev, uint32_t addr) { FLASH_HandleTypeDef *hflash = &dev->qspi->state->ctx.handle; + int res; - for (uint8_t i = 0U; i < dev->state->part->sec_registers.num_sec_regs; ++i) { - HAL_QSPI_LOCK_OTP(hflash, dev->state->part->sec_registers.sec_regs[i]); + portENTER_CRITICAL(); + res = HAL_QSPI_LOCK_OTP(hflash, addr); + portEXIT_CRITICAL(); + + if (res != 0) { + return E_ERROR; } return S_SUCCESS; } -#endif // RECOVERY_FW \ No newline at end of file +#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/drivers/sf32lb52/rc10k.c b/src/fw/drivers/sf32lb52/rc10k.c new file mode 100644 index 0000000000..55910ecc20 --- /dev/null +++ b/src/fw/drivers/sf32lb52/rc10k.c @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/rtc.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/passert.h" + +#include "bf0_hal.h" + +#define RC10K_DEFAULT_FREQ_HZ 10000UL +#define RC10K_CAL_PERIOD_MS 15000U + +static TimerID s_rc10k_cal_timer; + +static void prv_rc10k_cal_timer_cb(void *data) { + uint8_t lp_cycle; + + lp_cycle = HAL_RC_CAL_GetLPCycle(); + HAL_RC_CAL_update_reference_cycle_on_48M(lp_cycle); +} + +void rc10k_init(void) { + prv_rc10k_cal_timer_cb(NULL); + + s_rc10k_cal_timer = new_timer_create(); + PBL_ASSERTN(s_rc10k_cal_timer != TIMER_INVALID_ID); + + bool success = new_timer_start(s_rc10k_cal_timer, RC10K_CAL_PERIOD_MS, prv_rc10k_cal_timer_cb, + NULL, TIMER_START_FLAG_REPEATING); + PBL_ASSERTN(success); +} + +uint32_t rc10k_get_freq_hz(void) { + uint32_t hxt48_cyc; + + hxt48_cyc = HAL_RC_CAL_get_average_cycle_on_48M(); + if (hxt48_cyc == 0UL) { + return RC10K_DEFAULT_FREQ_HZ; + } else { + return (48000000ULL * HAL_RC_CAL_GetLPCycle()) / hxt48_cyc; + } +} + +uint32_t rc10k_cyc_to_milli_ticks(uint32_t rc10k_cyc) { + uint32_t hxt48_cyc; + + hxt48_cyc = HAL_RC_CAL_get_average_cycle_on_48M(); + if (hxt48_cyc == 0UL) { + return (1000ULL * RTC_TICKS_HZ * rc10k_cyc) / RC10K_DEFAULT_FREQ_HZ; + } else { + return (1000ULL * RTC_TICKS_HZ * rc10k_cyc * hxt48_cyc) / (48000000ULL * HAL_RC_CAL_GetLPCycle()); + } +} \ No newline at end of file diff --git a/src/fw/drivers/sf32lb52/rc10k.h b/src/fw/drivers/sf32lb52/rc10k.h new file mode 100644 index 0000000000..419833afc1 --- /dev/null +++ b/src/fw/drivers/sf32lb52/rc10k.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/** + * @brief Initialize RC10K with periodic calibration. + */ +void rc10k_init(void); + +/** + * @brief Get the current RC10K frequency in Hz. + * @retval Frequency in Hz. + */ +uint32_t rc10k_get_freq_hz(void); + +/** + * @brief Convert RC10K cycles to milli-ticks + * @param rc10k_cyc Number of RC10K cycles. + * @retval Corresponding milli-ticks. + */ +uint32_t rc10k_cyc_to_milli_ticks(uint32_t rc10k_cyc); \ No newline at end of file diff --git a/src/fw/drivers/sf32lb52/rng.c b/src/fw/drivers/sf32lb52/rng.c deleted file mode 100644 index dea26c2188..0000000000 --- a/src/fw/drivers/sf32lb52/rng.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/rng.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "bf0_hal_rcc.h" -#include "bf0_hal_rng.h" - -static bool s_inited; -static RNG_HandleTypeDef s_rng_hdl = { - .Instance = hwp_trng, -}; - -bool rng_rand(uint32_t *rand_out) { - HAL_StatusTypeDef status; - - HAL_RCC_EnableModule(RCC_MOD_TRNG); - - if (!s_inited) { - status = HAL_RNG_Init(&s_rng_hdl); - PBL_ASSERTN(status == HAL_OK); - - status = HAL_RNG_GenerateRandomSeed(&s_rng_hdl, rand_out); - PBL_ASSERTN(status == HAL_OK); - - s_inited = true; - } - - status = HAL_RNG_GenerateRandomNumber(&s_rng_hdl, rand_out); - if (status != HAL_OK) { - PBL_LOG(LOG_LEVEL_ERROR, "HAL_RNG_GenerateRandomNumber failed: %d", status); - } - - HAL_RCC_DisableModule(RCC_MOD_TRNG); - - return status == HAL_OK; -} diff --git a/src/fw/drivers/sf32lb52/rtc.c b/src/fw/drivers/sf32lb52/rtc.c deleted file mode 100644 index 66e37c7b7c..0000000000 --- a/src/fw/drivers/sf32lb52/rtc.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "board/board.h" -#include "drivers/rtc.h" -#include "drivers/rtc_private.h" -#include "mcu/interrupts.h" -#include "system/passert.h" -#include "util/time/time.h" - -#include "FreeRTOS.h" -#include "task.h" - -#include "bf0_hal_rtc.h" - -#define LXT_LP_CYCLE 200 - -// The RTC clock, CLK_RTC, can be configured to use the LXT32 (32.768 kHz) or -// LRC10 (9.8 kHz). The prescaler values need to be set such that the CLK1S -// event runs at 1 Hz. The formula that relates prescaler values with the -// clock frequency is as follows: -// -// F(CLK1S) = CLK_RTC / (DIV_A_INT + DIV_A_FRAC / 2^14) / DIV_B -#define DIV_A_INT 128 -#define DIV_A_FRAC 0 -#define DIV_B 256 - -static RTC_HandleTypeDef RTC_Handler = { - .Instance = (RTC_TypeDef*)RTC_BASE, - .Init = - { - .HourFormat = RTC_HOURFORMAT_24, - .DivAInt = DIV_A_INT, - .DivAFrac = DIV_A_FRAC, - .DivB = DIV_B, - }, -}; - -#ifndef SF32LB52_USE_LXT -static uint32_t prv_rtc_get_lpcycle() { - uint32_t value; - - value = HAL_Get_backup(RTC_BACKUP_LPCYCLE_AVE); - if (value == 0) { - value = 1200000; - } - - value += 1; // Calibrate in initial with 8 cycle - HAL_Set_backup(RTC_BACKUP_LPCYCLE, (uint32_t)value); - - return value; -} - -void prv_rtc_rc10_calculate_div(RTC_HandleTypeDef* hdl, uint32_t value) { - hdl->Init.DivB = RC10K_SUB_SEC_DIVB; - - // 1 seconds has total 1/(x/(48*8))/256=1.5M/x cycles, times 2^14 for DIVA - uint32_t divider = RTC_Handler.Init.DivB * value; - value = ((uint64_t)48000000 * LXT_LP_CYCLE * (1 << 14) + (divider >> 1)) / divider; - hdl->Init.DivAInt = (uint32_t)(value >> 14); - hdl->Init.DivAFrac = (uint32_t)(value & ((1 << 14) - 1)); -} -#endif - -void rtc_init(void) { - HAL_StatusTypeDef ret; - -#ifdef SF32LB52_USE_LXT - ret = HAL_PMU_LXTReady(); - PBL_ASSERTN(ret == HAL_OK); -#else - // If LXT is disabled, we need to use the RC10K as the clock source. - // The RC10K needs to be initialized in board_x.c before it can be used - uint32_t value; - value = prv_rtc_get_lpcycle(); - if (value != 0U) { - prv_rtc_rc10_calculate_div(&RTC_Handler, value); - } -#endif - - ret = HAL_RTC_Init(&RTC_Handler, RTC_INIT_NORMAL); - PBL_ASSERTN(ret == HAL_OK); -} - -void rtc_init_timers(void) {} - -static RtcTicks get_ticks(void) { - static TickType_t s_last_freertos_tick_count = 0; - static RtcTicks s_coarse_ticks = 0; - - bool ints_enabled = mcu_state_are_interrupts_enabled(); - if (ints_enabled) { - __disable_irq(); - } - - TickType_t freertos_tick_count = xTaskGetTickCount(); - if (freertos_tick_count < s_last_freertos_tick_count) { - TickType_t rollover_amount = -1; - s_coarse_ticks += rollover_amount; - } - - s_last_freertos_tick_count = freertos_tick_count; - RtcTicks ret_value = freertos_tick_count + s_coarse_ticks; - - if (ints_enabled) { - __enable_irq(); - } - - return ret_value; -} - -void rtc_set_time(time_t time) { - struct tm t; - gmtime_r(&time, &t); - - PBL_ASSERTN(!rtc_sanitize_struct_tm(&t)); - - RTC_TimeTypeDef rtc_time_struct = {.Hours = t.tm_hour, .Minutes = t.tm_min, .Seconds = t.tm_sec}; - - RTC_DateTypeDef rtc_date_struct = { - .Month = t.tm_mon + 1, - .Date = t.tm_mday, - .Year = t.tm_year % 100, - }; - - HAL_RTC_SetTime(&RTC_Handler, &rtc_time_struct, RTC_FORMAT_BIN); - HAL_RTC_SetDate(&RTC_Handler, &rtc_date_struct, RTC_FORMAT_BIN); -} - -void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) { - RTC_DateTypeDef rtc_date; - RTC_TimeTypeDef rtc_time; - - HAL_RTC_GetTime(&RTC_Handler, &rtc_time, RTC_FORMAT_BIN); - while (HAL_RTC_GetDate(&RTC_Handler, &rtc_date, RTC_FORMAT_BIN) == HAL_ERROR) { - // HAL_ERROR is returned if a rollover occurs, so just keep trying - HAL_RTC_GetTime(&RTC_Handler, &rtc_time, RTC_FORMAT_BIN); - }; - - struct tm current_time = { - .tm_sec = rtc_time.Seconds, - .tm_min = rtc_time.Minutes, - .tm_hour = rtc_time.Hours, - .tm_mday = rtc_date.Date, - .tm_mon = rtc_date.Month - 1, - .tm_year = rtc_date.Year + 100, - .tm_wday = rtc_date.WeekDay, - .tm_yday = 0, - .tm_isdst = 0, - }; - - *out_seconds = mktime(¤t_time); - *out_ms = (uint16_t)((rtc_time.SubSeconds * 1000) / DIV_B); -} - -time_t rtc_get_time(void) { - time_t seconds; - uint16_t ms; - - rtc_get_time_ms(&seconds, &ms); - - return seconds; -} - -RtcTicks rtc_get_ticks(void) { return get_ticks(); } - -void rtc_alarm_init(void) {} - -void rtc_alarm_set(RtcTicks num_ticks) {} - -RtcTicks rtc_alarm_get_elapsed_ticks(void) { return 0; } - -bool rtc_alarm_is_initialized(void) { return true; } - -bool rtc_sanitize_struct_tm(struct tm* t) { - // These values come from time_t (which suffers from the 2038 problem) and our hardware which - // only stores a 2 digit year, so we only represent values after 2000. - - // Remember tm_year is years since 1900. - if (t->tm_year < 100) { - // Bump it up to the year 2000 to work with our hardware. - t->tm_year = 100; - return true; - } else if (t->tm_year > 137) { - t->tm_year = 137; - return true; - } - return false; -} - -bool rtc_sanitize_time_t(time_t* t) { - struct tm time_struct; - gmtime_r(t, &time_struct); - - const bool result = rtc_sanitize_struct_tm(&time_struct); - *t = mktime(&time_struct); - - return result; -} - -void rtc_get_time_tm(struct tm* time_tm) { - time_t t = rtc_get_time(); - localtime_r(&t, time_tm); -} - -const char* rtc_get_time_string(char* buffer) { return time_t_to_string(buffer, rtc_get_time()); } - -const char* time_t_to_string(char* buffer, time_t t) { - struct tm time; - localtime_r(&t, &time); - - strftime(buffer, TIME_STRING_BUFFER_SIZE, "%c", &time); - - return buffer; -} - -//! We attempt to save registers by placing both the timezone abbreviation -//! timezone index and the daylight_savingtime into the same register set -void rtc_set_timezone(TimezoneInfo* tzinfo) {} - -void rtc_get_timezone(TimezoneInfo* tzinfo) {} - -void rtc_timezone_clear(void) {} - -uint16_t rtc_get_timezone_id(void) { return 0; } - -bool rtc_is_timezone_set(void) { return 0; } - -void rtc_enable_backup_regs(void) {} - -void rtc_calibrate_frequency(uint32_t frequency) {} diff --git a/src/fw/drivers/sf32lb52/temperature.c b/src/fw/drivers/sf32lb52/temperature.c deleted file mode 100644 index 44b915ab91..0000000000 --- a/src/fw/drivers/sf32lb52/temperature.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2025 SiFli Technologies(Nanjing) Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/temperature.h" - -#include "bf0_hal.h" -#include "board/board.h" -#include "console/prompt.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/delay.h" - -#define SLOPE_NUM (2971) // approximate slope molecule -#define SLOPE_DEN (40) // approximate slope denominator -#define OFFSET (277539) -#define ROUND_ADD (SLOPE_DEN / 2) - -void temperature_init(void) { - HAL_RCC_EnableModule(RCC_MOD_TSEN); - hwp_hpsys_cfg->ANAU_CR |= HPSYS_CFG_ANAU_CR_EN_BG; -} - -static void prv_tsen_enable(TSEN_TypeDef *tsen) { - tsen->TSEN_CTRL_REG &= ~TSEN_TSEN_CTRL_REG_ANAU_TSEN_RSTB; - tsen->TSEN_CTRL_REG |= TSEN_TSEN_CTRL_REG_ANAU_TSEN_EN | TSEN_TSEN_CTRL_REG_ANAU_TSEN_PU; - tsen->TSEN_CTRL_REG |= TSEN_TSEN_CTRL_REG_ANAU_TSEN_RSTB; - delay_us(20); - tsen->TSEN_CTRL_REG |= TSEN_TSEN_CTRL_REG_ANAU_TSEN_RUN; -} - -static void prv_tsen_disable(TSEN_TypeDef *tsen) { - tsen->TSEN_CTRL_REG &= ~(TSEN_TSEN_CTRL_REG_ANAU_TSEN_EN | TSEN_TSEN_CTRL_REG_ANAU_TSEN_PU); -} - -int32_t temperature_read(void) { - int32_t temp = 0; - - uint32_t count = 0; - prv_tsen_enable(hwp_tsen); - while ((hwp_tsen->TSEN_IRQ & TSEN_TSEN_IRQ_TSEN_IRSR) == 0) { - HAL_Delay(1); - count++; - if (count > HAL_TSEN_MAX_DELAY) { - temp = INT32_MIN; - break; - } - } - hwp_tsen->TSEN_IRQ |= TSEN_TSEN_IRQ_TSEN_ICR; - if (temp != INT32_MIN) { - // The Celsius conversion formula is: (DATA + 3000)/10100 * 749.2916 − 277.5391 - // In order to calculate milli-Celsius degrees, we can perform the following conversions: - // COEF_NUM/COEF_DEN ≃ 749.2916/10100 × 1000 - // (DATA+3000)*COEF_NUM/COEF_DEN - OFFSET - uint32_t raw = hwp_tsen->TSEN_RDATA; - uint32_t D = raw + 3000; // D = DATA + 3000 - uint32_t num = D * SLOPE_NUM + ROUND_ADD; // discard four, but treat five as whole - uint32_t tmp = num / SLOPE_DEN; - temp = tmp - OFFSET; - } - prv_tsen_disable(hwp_tsen); - - return temp; -} - -void command_temperature_read(void) { - char buffer[32]; - prompt_send_response_fmt(buffer, sizeof(buffer), "%" PRId32 " ", temperature_read()); -} diff --git a/src/fw/drivers/sf32lb52/uart.c b/src/fw/drivers/sf32lb52/uart.c deleted file mode 100644 index 43b99fc2b0..0000000000 --- a/src/fw/drivers/sf32lb52/uart.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "uart_definitions.h" - -#include "drivers/uart.h" -#include "system/passert.h" - -#include "FreeRTOS.h" -#include "bf0_hal_dma.h" -#include "bf0_hal_uart.h" - -#include "util/misc.h" - -static void prv_init(UARTDevice *dev, uint32_t mode) { - HAL_StatusTypeDef ret; - - dev->state->huart.Init.Mode = mode; - dev->state->dev = dev; - ret = HAL_UART_Init(&dev->state->huart); - PBL_ASSERTN(ret == HAL_OK); - - switch (mode) { - case UART_MODE_TX_RX: - HAL_PIN_Set(dev->tx.pad, dev->tx.func, dev->tx.flags, 1); - HAL_PIN_Set(dev->rx.pad, dev->rx.func, dev->rx.flags, 1); - break; - case UART_MODE_TX: - HAL_PIN_Set(dev->tx.pad, dev->tx.func, dev->tx.flags, 1); - break; - case UART_MODE_RX: - HAL_PIN_Set(dev->rx.pad, dev->rx.func, dev->rx.flags, 1); - break; - default: - WTF; - break; - } - - dev->state->initialized = true; - - if (dev->state->hdma.Instance != NULL) { - __HAL_LINKDMA(&dev->state->huart, hdmarx, dev->state->hdma); - - HAL_NVIC_SetPriority(dev->dma_irqn, dev->dma_irq_priority, 0); - HAL_NVIC_EnableIRQ(dev->dma_irqn); - - __HAL_UART_ENABLE_IT(&dev->state->huart, UART_IT_IDLE); - } -} - -void uart_init(UARTDevice *dev) { prv_init(dev, UART_MODE_TX_RX); } - -void uart_init_open_drain(UARTDevice *dev) { WTF; } - -void uart_init_tx_only(UARTDevice *dev) { prv_init(dev, UART_MODE_TX); } - -void uart_init_rx_only(UARTDevice *dev) { prv_init(dev, UART_MODE_RX); } - -void uart_deinit(UARTDevice *dev) { HAL_UART_DeInit(&dev->state->huart); } - -void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { - HAL_StatusTypeDef ret; - - PBL_ASSERTN(dev->state->initialized); - - HAL_UART_DeInit(&dev->state->huart); - - dev->state->huart.Init.BaudRate = baud_rate; - ret = HAL_UART_Init(&dev->state->huart); - PBL_ASSERTN(ret == HAL_OK); -} - -// Read / Write APIs -//////////////////////////////////////////////////////////////////////////////// - -void uart_write_byte(UARTDevice *dev, uint8_t data) { - HAL_UART_Transmit(&dev->state->huart, &data, 1, HAL_MAX_DELAY); -} - -uint8_t uart_read_byte(UARTDevice *dev) { - HAL_StatusTypeDef ret; - uint8_t data; - - ret = HAL_UART_Receive(&dev->state->huart, &data, 1, HAL_MAX_DELAY); - // PBL_ASSERTN(ret == HAL_OK); - - return data; -} - -bool uart_is_rx_ready(UARTDevice *dev) { - return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_RXNE; -} - -bool uart_has_rx_overrun(UARTDevice *dev) { - return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_ORE; -} - -bool uart_has_rx_framing_error(UARTDevice *dev) { - return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_FE; -} - -bool uart_is_tx_ready(UARTDevice *dev) { - return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_TXE; -} - -bool uart_is_tx_complete(UARTDevice *dev) { - return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_TC; -} - -void uart_wait_for_tx_complete(UARTDevice *dev) { - while (!uart_is_tx_complete(dev)) continue; -} - -// Interrupts -//////////////////////////////////////////////////////////////////////////////// - -static void prv_set_interrupt_enabled(UARTDevice *dev, bool enabled) { - if (enabled) { - PBL_ASSERTN(dev->state->tx_irq_handler || dev->state->rx_irq_handler); - HAL_NVIC_SetPriority(dev->irqn, dev->irq_priority, 0); - HAL_NVIC_EnableIRQ(dev->irqn); - } else { - HAL_NVIC_DisableIRQ(dev->irqn); - } -} - -void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->rx_irq_handler = irq_handler; -} - -void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->tx_irq_handler = irq_handler; -} - -void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - if (enabled) { - dev->state->rx_int_enabled = true; - SET_BIT(dev->state->huart.Instance->CR1, USART_CR1_RXNEIE); - prv_set_interrupt_enabled(dev, true); - } else { - // disable interrupt if TX is also disabled - prv_set_interrupt_enabled(dev, dev->state->tx_int_enabled); - CLEAR_BIT(dev->state->huart.Instance->CR1, USART_CR1_RXNEIE); - dev->state->rx_int_enabled = false; - } -} - -void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - if (enabled) { - dev->state->tx_int_enabled = true; - SET_BIT(dev->state->huart.Instance->CR1, USART_CR1_TXEIE); - prv_set_interrupt_enabled(dev, true); - } else { - // disable interrupt if RX is also disabled - prv_set_interrupt_enabled(dev, dev->state->rx_int_enabled); - CLEAR_BIT(dev->state->huart.Instance->CR1, USART_CR1_TXEIE); - dev->state->tx_int_enabled = false; - } -} - -void uart_irq_handler(UARTDevice *dev) { - PBL_ASSERTN(dev->state->initialized); - bool should_context_switch = false; - uint32_t idx; - - if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { - const UARTRXErrorFlags err_flags = { - .overrun_error = uart_has_rx_overrun(dev), - .framing_error = uart_has_rx_framing_error(dev), - }; - // DMA - if (dev->state->rx_dma_buffer && (__HAL_UART_GET_FLAG(&dev->state->huart, UART_FLAG_IDLE) != RESET) && - (__HAL_UART_GET_IT_SOURCE(&dev->state->huart, UART_IT_IDLE) != RESET)) { - // process bytes from the DMA buffer - const uint32_t dma_length = dev->state->rx_dma_length; - const uint32_t recv_total_index = dma_length - __HAL_DMA_GET_COUNTER(&dev->state->hdma); - int32_t recv_len = recv_total_index - dev->state->rx_dma_index; - if (recv_len < 0) { - recv_len += dma_length; - } - - idx = dev->state->rx_dma_index; - for (int32_t i = 0; i < recv_len; i++) { - uint8_t data; - data = dev->state->rx_dma_buffer[idx]; - if (dev->state->rx_irq_handler(dev, data, &err_flags)) { - should_context_switch = true; - } - idx++; - if (idx >= dma_length) { - idx = 0; - } - } - dev->state->rx_dma_index = recv_total_index; - if (dev->state->rx_dma_index >= dma_length) { - dev->state->rx_dma_index = 0; - } - uart_clear_all_interrupt_flags(dev); - __HAL_UART_CLEAR_IDLEFLAG(&dev->state->huart); - } else { - const bool has_byte = uart_is_rx_ready(dev); - // read the data register regardless to clear the error flags - const uint8_t data = uart_read_byte(dev); - if (has_byte) { - if (dev->state->rx_irq_handler(dev, data, &err_flags)) { - should_context_switch = true; - } - } - } - } - if (dev->state->tx_irq_handler && dev->state->tx_int_enabled && uart_is_tx_ready(dev)) { - if (dev->state->tx_irq_handler(dev)) { - should_context_switch = true; - } - } - portEND_SWITCHING_ISR(should_context_switch); -} - -void uart_clear_all_interrupt_flags(UARTDevice *dev) { - UART_HandleTypeDef *uart = &dev->state->huart; - if (__HAL_UART_GET_FLAG(uart, UART_FLAG_ORE) != RESET) { - __HAL_UART_CLEAR_OREFLAG(uart); - } - if (__HAL_UART_GET_FLAG(uart, UART_FLAG_NE) != RESET) { - __HAL_UART_CLEAR_NEFLAG(uart); - } - if (__HAL_UART_GET_FLAG(uart, UART_FLAG_FE) != RESET) { - __HAL_UART_CLEAR_FEFLAG(uart); - } - if (__HAL_UART_GET_FLAG(uart, UART_FLAG_PE) != RESET) { - __HAL_UART_CLEAR_PEFLAG(uart); - } -} - -void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { - size_t recv_len; - size_t recv_total_index; - uint32_t idx; - bool should_context_switch = false; - - UARTDeviceState *state = container_of(huart, UARTDeviceState, huart); - UARTDevice *dev = (UARTDevice *)state->dev; - - recv_total_index = state->rx_dma_length - __HAL_DMA_GET_COUNTER(&state->hdma); - if (recv_total_index < state->rx_dma_index) - recv_len = state->rx_dma_length + recv_total_index - state->rx_dma_index; - else - recv_len = recv_total_index - state->rx_dma_index; - - idx = state->rx_dma_index; - state->rx_dma_index = recv_total_index; - if (recv_len) { - for (size_t i = 0; i < recv_len; i++) { - uint8_t data; - data = state->rx_dma_buffer[idx]; - if (state->rx_irq_handler(dev, data, NULL)) { - should_context_switch = true; - } - idx++; - if (idx >= state->rx_dma_length) { - idx = 0; - } - } - } - portEND_SWITCHING_ISR(should_context_switch); -} - -void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { - HAL_UART_RxHalfCpltCallback(huart); -} - -// DMA -//////////////////////////////////////////////////////////////////////////////// - -void uart_dma_irq_handler(UARTDevice *dev) { - HAL_DMA_IRQHandler(&dev->state->hdma); -} - -void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { - dev->state->rx_dma_buffer = buffer; - dev->state->rx_dma_length = length; - dev->state->rx_dma_index = 0; - __HAL_UART_ENABLE_IT(&dev->state->huart, UART_IT_IDLE); - HAL_UART_DmaTransmit(&dev->state->huart, buffer, length, DMA_PERIPH_TO_MEMORY); -} - -void uart_stop_rx_dma(UARTDevice *dev) { - dev->state->rx_dma_buffer = NULL; - dev->state->rx_dma_length = 0; - HAL_UART_DMAPause(&dev->state->huart); -} - -void uart_clear_rx_dma_buffer(UARTDevice *dev) { - dev->state->rx_dma_index = dev->state->rx_dma_length - __HAL_DMA_GET_COUNTER(&dev->state->hdma); -} diff --git a/src/fw/drivers/sf32lb52/uart_definitions.h b/src/fw/drivers/sf32lb52/uart_definitions.h deleted file mode 100644 index 1be2515c0f..0000000000 --- a/src/fw/drivers/sf32lb52/uart_definitions.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include - -#include "board/board.h" -#include "drivers/uart.h" - -typedef struct UARTState { - bool initialized; - UARTRXInterruptHandler rx_irq_handler; - UARTTXInterruptHandler tx_irq_handler; - bool rx_int_enabled; - bool tx_int_enabled; - uint8_t *rx_dma_buffer; - uint32_t rx_dma_length; - uint32_t rx_dma_index; - UART_HandleTypeDef huart; - DMA_HandleTypeDef hdma; - const void *dev; -} UARTDeviceState; - -typedef const struct UARTDevice { - UARTDeviceState *state; - Pinmux rx; - Pinmux tx; - IRQn_Type irqn; - uint8_t irq_priority; - IRQn_Type dma_irqn; - uint8_t dma_irq_priority; -} UARTDevice; - -// thinly wrapped by the IRQ handler in board_*.c -void uart_irq_handler(UARTDevice *dev); -void uart_dma_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/speaker/Kconfig b/src/fw/drivers/speaker/Kconfig new file mode 100644 index 0000000000..28f2b9ab6b --- /dev/null +++ b/src/fw/drivers/speaker/Kconfig @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig SPEAKER + bool "Speaker" + help + Speaker (audio output) Drivers + +if SPEAKER + +config SPEAKER_DA7212 + bool "Dialog DA7212 codec (nRF5 I2S)" + help + Support for the Dialog DA7212 audio codec driven over nRF5 I2S. + +config SPEAKER_SF32LB + bool "SiFli SF32LB AUDEC" + select HAL_SIFLI_AUDCODEC + help + Support for the SiFli SF32LB audio codec / DAC speaker output. + +config SPEAKER_QEMU + bool "QEMU audio" + help + QEMU audio device backing the speaker for emulated builds. + +module = DRIVER_SPEAKER +module-str = Speaker +source "src/fw/Kconfig.template.log_level" + +endif # SPEAKER diff --git a/src/fw/drivers/speaker/nrf5/da7212.c b/src/fw/drivers/speaker/nrf5/da7212.c new file mode 100644 index 0000000000..307b23ec04 --- /dev/null +++ b/src/fw/drivers/speaker/nrf5/da7212.c @@ -0,0 +1,589 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "da7212_definitions.h" + +#include "board/board.h" +#include "drivers/audio.h" +#include "drivers/clocksource.h" +#include "drivers/i2c.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" +#include "os/mutex.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/circular_buffer.h" +#include "util/math.h" + +#include "nrfx_i2s.h" + +PBL_LOG_MODULE_DEFINE(driver_speaker_da7212, CONFIG_DRIVER_SPEAKER_LOG_LEVEL); + +// Hold the codec + I2S warm for this long after audio_stop before powering +// down. Rapid play/pause spamming used to phaser the output until hard power +// cycle; deferring the SYSTEM_ACTIVE=0 keeps the LINE output continuous +// across pauses and the analog domain never gets disturbed. +#define AUDIO_IDLE_SHUTDOWN_MS 1000 + +typedef enum { + AudioPwrCold, + AudioPwrWarm, + AudioPwrActive, +} AudioPwrState; + +static AudioPwrState s_pwr_state = AudioPwrCold; +static TimerID s_idle_timer = TIMER_INVALID_ID; +// Serializes cold/warm/active transitions and their I/O so audio_start, +// audio_stop, and prv_idle_shutdown can't race across threads. +static PebbleMutex *s_audio_mutex; + +static void prv_idle_shutdown(void *data); + +// --------------------------------------------------------------------------- +// DA7212 codec registers used by the driver. +// --------------------------------------------------------------------------- +#define DA7212_PLL_STATUS 0x03 +#define DA7212_CIF_CTRL 0x1D +#define DA7212_DIG_ROUTING_DAI 0x21 +#define DA7212_SR 0x22 +#define DA7212_REFERENCES 0x23 +#define DA7212_PLL_FRAC_TOP 0x24 +#define DA7212_PLL_FRAC_BOT 0x25 +#define DA7212_PLL_INTEGER 0x26 +#define DA7212_PLL_CTRL 0x27 +#define DA7212_DAI_CLK_MODE 0x28 +#define DA7212_DAI_CTRL 0x29 +#define DA7212_DIG_ROUTING_DAC 0x2A +#define DA7212_DAC_FILTERS5 0x40 +#define DA7212_DAC_R_GAIN 0x46 +#define DA7212_LINE_GAIN 0x4A +#define DA7212_MIXOUT_R_SELECT 0x4C +#define DA7212_SYSTEM_MODES_OUTPUT 0x51 +#define DA7212_DAC_R_CTRL 0x6A +#define DA7212_LINE_CTRL 0x6D +#define DA7212_MIXOUT_R_CTRL 0x6F +#define DA7212_LDO_CTRL 0x90 +#define DA7212_GAIN_RAMP_CTRL 0x92 +#define DA7212_SYSTEM_ACTIVE 0xFD + +// DAC_R_GAIN value corresponding to 0 dB per DA7212 datasheet. +#define DA7212_DAC_R_GAIN_0DB 0x6f +// Attenuation span, in 0.75 dB DAC_R_GAIN steps, that volume 1..100 maps +// onto (64 steps = 48 dB). +#define DA7212_DAC_GAIN_VOL_RANGE_STEPS 64 + +#define I2S_BUF_SAMPLES_STEREO (NRF5_AUDIO_I2S_BUF_SAMPLES_MONO * 2) +#define I2S_BUF_SIZE_BYTES (I2S_BUF_SAMPLES_STEREO * sizeof(int16_t)) +// nrfx_i2s buffer_size is counted in 32-bit words. +#define I2S_BUF_SIZE_WORDS (I2S_BUF_SIZE_BYTES / sizeof(uint32_t)) + +static void prv_i2s_data_handler(nrfx_i2s_buffers_t const *p_released, uint32_t status); + +// --------------------------------------------------------------------------- +// DA7212 helpers. +// --------------------------------------------------------------------------- + +static void prv_codec_write(AudioDevice *dev, uint8_t reg, uint8_t value) { + uint8_t data[2] = { reg, value }; + i2c_use(dev->codec); + bool ok = i2c_write_block(dev->codec, sizeof(data), data); + i2c_release(dev->codec); + PBL_ASSERTN(ok); +} + +static uint8_t prv_codec_read(AudioDevice *dev, uint8_t reg) { + uint8_t value = 0; + i2c_use(dev->codec); + bool ok = i2c_read_register(dev->codec, reg, &value); + i2c_release(dev->codec); + PBL_ASSERTN(ok); + return value; +} + +// DAC_R_GAIN is 0.75 dB per step with 0x6F = 0 dB. Volume 100 maps to 0 dB +// (codes above 0x6F amplify and can clip full-scale samples); lower volumes +// attenuate linearly in dB, which tracks perceived loudness better than +// scaling the register code linearly. +static uint8_t prv_volume_to_dac_gain(uint8_t volume) { + return DA7212_DAC_R_GAIN_0DB - + (uint8_t)(((100 - volume) * DA7212_DAC_GAIN_VOL_RANGE_STEPS) / 100); +} + +// Push the cached volume to the codec: soft-mute at 0, otherwise the mapped +// DAC gain with soft-mute cleared. +static void prv_apply_volume(AudioDevice *dev) { + AudioDeviceState *state = dev->state; + if (state->volume == 0) { + prv_codec_write(dev, DA7212_DAC_FILTERS5, 0x80); + return; + } + prv_codec_write(dev, DA7212_DAC_R_GAIN, prv_volume_to_dac_gain(state->volume)); + prv_codec_write(dev, DA7212_DAC_FILTERS5, 0x00); +} + +// Bring the DA7212 out of standby, lock its PLL and configure the DAC/LINE +// path — but leave the DAI (BCLK/WCLK) disabled. The caller starts the nRF +// I2S peripheral in slave mode in between, then calls prv_codec_start_dai() +// below to have the codec begin driving BCLK/WCLK into the nRF. +// +// We run the codec as I2S master because the nRF's 32 MHz clock tree can't +// divide down to exactly 16 kHz LRCK, while the DA7212 PLL can synthesize +// exactly 12.288 MHz system clock from our 4 MHz MCK and emit a true 16 kHz +// WCLK when SR=0x05. Register values are taken from +// src/fw/apps/prf/mfg_mic_asterix.c (PLL setup) and +// src/fw/apps/prf/mfg_speaker_asterix.c (codec-master DAI mode). +static void prv_codec_prepare(AudioDevice *dev) { + prv_codec_write(dev, DA7212_CIF_CTRL, 0x80); + psleep(10); + + prv_codec_write(dev, DA7212_SYSTEM_ACTIVE, 0x01); + prv_codec_write(dev, DA7212_REFERENCES, 0x08); + psleep(30); + + prv_codec_write(dev, DA7212_LDO_CTRL, 0x80); + + // PLL: MCLK=4MHz (input divider 2), target system clock 12.288MHz for + // SR=16kHz. VCO = 98.304MHz => integer=49, frac=1245 (0x4dd). + prv_codec_write(dev, DA7212_PLL_FRAC_TOP, 0x04); + prv_codec_write(dev, DA7212_PLL_FRAC_BOT, 0xdd); + prv_codec_write(dev, DA7212_PLL_INTEGER, 0x31); + prv_codec_write(dev, DA7212_PLL_CTRL, 0xC0); + + // DA7212 rev 3.6 13.29 workaround for 2-5MHz MCLK range. + prv_codec_write(dev, 0xF0, 0x8B); + prv_codec_write(dev, 0xF2, 0x03); + prv_codec_write(dev, 0xF0, 0x00); + psleep(40); + + PBL_ASSERT(prv_codec_read(dev, DA7212_PLL_STATUS) == 0x07, + "DA7212 PLL not locked"); + + // Gain ramp off: the mfg test uses a multi-second ramp for smooth + // mic-capture playback, but for the speaker service we want audio as soon + // as the stream opens. + prv_codec_write(dev, DA7212_GAIN_RAMP_CTRL, 0x00); + prv_codec_write(dev, DA7212_SR, 0x05); + + prv_codec_write(dev, DA7212_DIG_ROUTING_DAI, 0x32); + prv_codec_write(dev, DA7212_DIG_ROUTING_DAC, 0xba); + + prv_codec_write(dev, DA7212_DAC_R_CTRL, 0x80); + prv_codec_write(dev, DA7212_MIXOUT_R_SELECT, 0x08); + // amp + mix enable, softmix off (softmix slow-ramps each routing change). + prv_codec_write(dev, DA7212_MIXOUT_R_CTRL, 0x90); + + prv_codec_write(dev, DA7212_LINE_GAIN, 0x30); + prv_codec_write(dev, DA7212_LINE_CTRL, 0x80); + + prv_codec_write(dev, DA7212_SYSTEM_MODES_OUTPUT, 0x89); + + // Apply the cached volume (gain + soft-mute state) now that the path is up + // (DAI still disabled). + prv_apply_volume(dev); +} + +// Enable the DA7212 DAI as I2S master. This is the point at which BCLK/WCLK +// begin toggling on P0.12 / P0.07; the nRF I2S must already be armed in slave +// mode when this runs. +static void prv_codec_start_dai(AudioDevice *dev) { + // DAI_CTRL: enable, 16-bit samples. Codec is still in slave-clock mode at + // this point, so its DAI output pins remain high-Z. + prv_codec_write(dev, DA7212_DAI_CTRL, 0x80); + // DAI_CLK_MODE: master + BCLKS_PER_WCLK=001 (64 BCLK per WCLK frame). The + // codec now drives BCLK and WCLK; nRF I2S slave picks them up. + prv_codec_write(dev, DA7212_DAI_CLK_MODE, 0x81); +} + +static void prv_codec_mute(AudioDevice *dev) { + prv_codec_write(dev, DA7212_DAC_FILTERS5, 0x80); +} + +static void prv_codec_power_down(AudioDevice *dev) { + prv_codec_write(dev, DA7212_SYSTEM_ACTIVE, 0x00); +} + +// --------------------------------------------------------------------------- +// Buffer management. +// --------------------------------------------------------------------------- + +static bool prv_allocate_buffers(AudioDeviceState *state) { + state->circ_buffer_storage = kernel_malloc(NRF5_AUDIO_CIRC_BUF_SIZE_BYTES); + if (!state->circ_buffer_storage) { + PBL_LOG_ERR("Failed to allocate audio circular buffer"); + return false; + } + + for (int i = 0; i < NRF5_AUDIO_I2S_BUF_COUNT; i++) { + state->i2s_bufs[i] = kernel_malloc(I2S_BUF_SIZE_BYTES); + if (!state->i2s_bufs[i]) { + PBL_LOG_ERR("Failed to allocate I2S buffer %d", i); + for (int j = 0; j < i; j++) { + kernel_free(state->i2s_bufs[j]); + state->i2s_bufs[j] = NULL; + } + kernel_free(state->circ_buffer_storage); + state->circ_buffer_storage = NULL; + return false; + } + memset(state->i2s_bufs[i], 0, I2S_BUF_SIZE_BYTES); + } + + circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, + NRF5_AUDIO_CIRC_BUF_SIZE_BYTES); + return true; +} + +static void prv_free_buffers(AudioDeviceState *state) { + for (int i = 0; i < NRF5_AUDIO_I2S_BUF_COUNT; i++) { + if (state->i2s_bufs[i]) { + kernel_free(state->i2s_bufs[i]); + state->i2s_bufs[i] = NULL; + } + } + if (state->circ_buffer_storage) { + kernel_free(state->circ_buffer_storage); + state->circ_buffer_storage = NULL; + } +} + +// Fill a stereo I2S buffer by draining mono samples from the circular buffer +// and duplicating each to both channels. Any shortfall is padded with silence. +// Runs in the nrfx_i2s ISR. +static void prv_fill_i2s_buffer(AudioDeviceState *state, int16_t *out) { + const uint32_t n_mono = NRF5_AUDIO_I2S_BUF_SAMPLES_MONO; + const uint32_t mono_bytes = n_mono * sizeof(int16_t); + + uint16_t available = circular_buffer_get_read_space_remaining(&state->circ_buffer); + uint16_t to_copy = (available > mono_bytes) ? mono_bytes : available; + uint32_t samples_copied = 0; + + if (to_copy > 0) { + uint16_t copied = circular_buffer_copy(&state->circ_buffer, out, to_copy); + circular_buffer_consume(&state->circ_buffer, copied); + samples_copied = copied / sizeof(int16_t); + } + + // Zero the unfilled tail of the mono portion. + for (uint32_t i = samples_copied; i < n_mono; i++) { + out[i] = 0; + } + + // Expand mono -> stereo in place, iterating back-to-front so reads from + // out[i] happen before writes at the paired stereo slots overwrite them. + for (int32_t i = (int32_t)n_mono - 1; i >= 0; i--) { + int16_t s = out[i]; + out[2 * i] = s; + out[2 * i + 1] = s; + } +} + +// --------------------------------------------------------------------------- +// trans_cb dispatch (runs on the system task). +// --------------------------------------------------------------------------- + +static void prv_audio_trans_bg(void *data) { + AudioDeviceState *state = (AudioDeviceState *)data; + state->callback_pending = false; + if (state->is_running && state->trans_cb) { + uint32_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer); + state->trans_cb(&free_size); + } +} + +static void prv_maybe_request_refill_from_isr(AudioDeviceState *state) { + if (!state->trans_cb || state->callback_pending || !state->is_running) { + return; + } + uint16_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer); + if (free_size < NRF5_AUDIO_REFILL_THRESHOLD_BYTES) { + return; + } + + state->callback_pending = true; + bool should_context_switch = false; + if (!system_task_add_callback_from_isr(prv_audio_trans_bg, state, + &should_context_switch)) { + state->callback_pending = false; + } +} + +// --------------------------------------------------------------------------- +// nrfx_i2s data handler. +// --------------------------------------------------------------------------- + +// nrfx_i2s_data_handler_t takes no user data, so we stash the currently active +// device here while it's running. +static AudioDevice *s_active_device; + +static void prv_i2s_data_handler(nrfx_i2s_buffers_t const *p_released, uint32_t status) { + AudioDevice *dev = s_active_device; + if (!dev) { + return; + } + AudioDeviceState *state = dev->state; + + if (status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED) { + int16_t *fill_buf = state->i2s_bufs[state->buf_idx]; + prv_fill_i2s_buffer(state, fill_buf); + + nrfx_i2s_buffers_t next = { + .p_tx_buffer = (uint32_t *)fill_buf, + .p_rx_buffer = NULL, + .buffer_size = I2S_BUF_SIZE_WORDS, + }; + (void)nrfx_i2s_next_buffers_set(&dev->i2s_instance, &next); + state->buf_idx = (state->buf_idx + 1) % NRF5_AUDIO_I2S_BUF_COUNT; + + prv_maybe_request_refill_from_isr(state); + } +} + +// --------------------------------------------------------------------------- +// Public API. +// --------------------------------------------------------------------------- + +void audio_init(AudioDevice *audio_device) { + PBL_ASSERTN(audio_device); + AudioDeviceState *state = audio_device->state; + + if (s_audio_mutex == NULL) { + s_audio_mutex = mutex_create(); + PBL_ASSERTN(s_audio_mutex != INVALID_MUTEX_HANDLE); + } + + // Skip the memset on a warm restart — the buffers and live ISR state are + // still in use and would be leaked / clobbered. + if (s_pwr_state == AudioPwrCold) { + memset(state, 0, sizeof(*state)); + state->volume = 100; + } +} + +void audio_start(AudioDevice *audio_device, AudioTransCB cb) { + PBL_ASSERTN(audio_device); + AudioDeviceState *state = audio_device->state; + + mutex_lock(s_audio_mutex); + + if (state->is_running) { + mutex_unlock(s_audio_mutex); + PBL_LOG_WRN("Audio already running"); + return; + } + + // Cancel any pending deferred shutdown so we keep the codec up. + if (s_idle_timer != TIMER_INVALID_ID) { + new_timer_stop(s_idle_timer); + } + + if (s_pwr_state == AudioPwrWarm) { + // Codec, DAI, and I2S have been running continuously, streaming zeros + // from the ISR's auto-fill. Just resume writes and the next refill will + // land real audio. + state->trans_cb = cb; + state->callback_pending = false; + state->is_running = true; + s_pwr_state = AudioPwrActive; + mutex_unlock(s_audio_mutex); + PBL_LOG_DBG("Audio started (warm)"); + return; + } + + if (!prv_allocate_buffers(state)) { + mutex_unlock(s_audio_mutex); + return; + } + + state->trans_cb = cb; + state->callback_pending = false; + state->buf_idx = 0; + + s_active_device = audio_device; + + if (audio_device->power_ops && audio_device->power_ops->power_up) { + audio_device->power_ops->power_up(); + } + + // The DA7212 PLL references our MCK, which must be stable before we try to + // lock it. HFXO is also needed by the I2S peripheral for MCK generation. + clocksource_hfxo_request(); + + nrfx_i2s_config_t cfg = NRFX_I2S_DEFAULT_CONFIG( + audio_device->sck_pin, audio_device->lrck_pin, audio_device->mck_pin, + audio_device->sdout_pin, audio_device->sdin_pin); + cfg.irq_priority = audio_device->irq_priority; + cfg.channels = NRF_I2S_CHANNELS_STEREO; + cfg.sample_width = NRF_I2S_SWIDTH_16BIT; + cfg.format = NRF_I2S_FORMAT_I2S; + cfg.alignment = NRF_I2S_ALIGN_LEFT; + // nRF is the I2S slave: SCK/LRCK come from the DA7212 (codec is master). + // MCK is still generated by nRF at 4 MHz and fed into the codec PLL; the + // codec divides it down to exactly 16 kHz WCLK internally, avoiding the + // ~2% mismatch we'd get from nRF-as-master with any available MDIV/ratio. + cfg.mode = NRF_I2S_MODE_SLAVE; + cfg.mck_setup = NRF_I2S_MCK_32MDIV8; + // Ratio is unused for LRCK in slave mode; pick a value that nrfx accepts + // for 16-bit stereo (validate_config only enforces this in master mode). + cfg.ratio = NRF_I2S_RATIO_256X; + + nrfx_err_t err = nrfx_i2s_init(&audio_device->i2s_instance, &cfg, + prv_i2s_data_handler); + PBL_ASSERT(err == NRFX_SUCCESS, "nrfx_i2s_init failed: %d", err); + + // Prime the first buffer with silence; real samples arrive via audio_write. + memset(state->i2s_bufs[0], 0, I2S_BUF_SIZE_BYTES); + nrfx_i2s_buffers_t initial = { + .p_tx_buffer = (uint32_t *)state->i2s_bufs[0], + .p_rx_buffer = NULL, + .buffer_size = I2S_BUF_SIZE_WORDS, + }; + state->buf_idx = 1; + + state->is_running = true; + + // Arm the nRF I2S in slave mode. This also starts MCK toggling on the mck + // pin — required before the codec PLL can lock. The peripheral will sit + // idle until the codec starts driving BCLK/WCLK in phase 2. + err = nrfx_i2s_start(&audio_device->i2s_instance, &initial, 0); + PBL_ASSERT(err == NRFX_SUCCESS, "nrfx_i2s_start failed: %d", err); + + // Codec phase 1: PLL lock against the now-running MCK + full path config. + // DAI output stays disabled so P0.12/P0.07 remain high-Z (nRF has them + // configured as inputs in slave mode). + prv_codec_prepare(audio_device); + + // Codec phase 2: enable the DAI as master. Clocks start toggling now and + // the nRF begins consuming the silence buffer. + prv_codec_start_dai(audio_device); + + s_pwr_state = AudioPwrActive; + mutex_unlock(s_audio_mutex); + + PBL_LOG_DBG("Audio started"); +} + +uint32_t audio_write(AudioDevice *audio_device, void *write_buf, uint32_t size) { + PBL_ASSERTN(audio_device); + AudioDeviceState *state = audio_device->state; + + if (!state->is_running || !state->circ_buffer_storage) { + return 0; + } + + uint16_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer); + uint16_t to_write = (size > free_size) ? free_size : (uint16_t)size; + if (to_write > 0) { + (void)circular_buffer_write(&state->circ_buffer, write_buf, to_write); + } + return circular_buffer_get_write_space_remaining(&state->circ_buffer); +} + +void audio_set_volume(AudioDevice *audio_device, int volume) { + PBL_ASSERTN(audio_device); + AudioDeviceState *state = audio_device->state; + + state->volume = (uint8_t)MIN(MAX(volume, 0), 100); + + if (s_pwr_state == AudioPwrCold) { + // Codec is powered down (or audio_init hasn't run yet); + // prv_codec_prepare() applies the cached value on the next start. + return; + } + + mutex_lock(s_audio_mutex); + if (s_pwr_state != AudioPwrCold) { + prv_apply_volume(audio_device); + } + mutex_unlock(s_audio_mutex); +} + +void audio_stop(AudioDevice *audio_device) { + PBL_ASSERTN(audio_device); + AudioDeviceState *state = audio_device->state; + + mutex_lock(s_audio_mutex); + + if (!state->is_running) { + mutex_unlock(s_audio_mutex); + return; + } + + // Warm pause: just stop feeding new audio. The ISR's prv_fill_i2s_buffer + // sees an empty circular buffer and auto-zero-fills, so the codec keeps + // receiving uninterrupted clocks and a continuous silent data stream — no + // DAC mute, no I2S teardown, no LINE-output transitions. The actual + // teardown happens later on the idle timer. + state->is_running = false; + state->trans_cb = NULL; + s_pwr_state = AudioPwrWarm; + + if (s_idle_timer == TIMER_INVALID_ID) { + s_idle_timer = new_timer_create(); + } + if (s_idle_timer != TIMER_INVALID_ID) { + new_timer_start(s_idle_timer, AUDIO_IDLE_SHUTDOWN_MS, prv_idle_shutdown, + (void *)(uintptr_t)audio_device, 0); + mutex_unlock(s_audio_mutex); + PBL_LOG_DBG("Audio paused (warm)"); + return; + } + + // Couldn't get a timer — fall through to immediate shutdown. + prv_codec_mute(audio_device); + + nrfx_i2s_stop(&audio_device->i2s_instance); + nrfx_i2s_uninit(&audio_device->i2s_instance); + + prv_codec_power_down(audio_device); + + clocksource_hfxo_release(); + + if (audio_device->power_ops && audio_device->power_ops->power_down) { + audio_device->power_ops->power_down(); + } + + s_active_device = NULL; + prv_free_buffers(state); + + s_pwr_state = AudioPwrCold; + mutex_unlock(s_audio_mutex); + + PBL_LOG_DBG("Audio stopped"); +} + +static void prv_idle_shutdown(void *data) { + AudioDevice *audio_device = (AudioDevice *)data; + + mutex_lock(s_audio_mutex); + + if (s_pwr_state != AudioPwrWarm) { + // audio_start beat us to the mutex, or we're already cold. + mutex_unlock(s_audio_mutex); + return; + } + + AudioDeviceState *state = audio_device->state; + + prv_codec_mute(audio_device); + + nrfx_i2s_stop(&audio_device->i2s_instance); + nrfx_i2s_uninit(&audio_device->i2s_instance); + + prv_codec_power_down(audio_device); + + clocksource_hfxo_release(); + + if (audio_device->power_ops && audio_device->power_ops->power_down) { + audio_device->power_ops->power_down(); + } + + s_active_device = NULL; + prv_free_buffers(state); + + s_pwr_state = AudioPwrCold; + mutex_unlock(s_audio_mutex); + + PBL_LOG_DBG("Audio fully stopped"); +} diff --git a/src/fw/drivers/speaker/nrf5/da7212_definitions.h b/src/fw/drivers/speaker/nrf5/da7212_definitions.h new file mode 100644 index 0000000000..ed3cefd7a9 --- /dev/null +++ b/src/fw/drivers/speaker/nrf5/da7212_definitions.h @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "board/board.h" +#include "drivers/audio.h" +#include "util/circular_buffer.h" + +#include +#include + +#include "nrfx_i2s.h" + +// Number of mono samples per I2S half-buffer. The stereo buffer holds 2x this +// many 16-bit samples. With SAMPLE_RATE=16000, 512 mono samples = 32ms. +#define NRF5_AUDIO_I2S_BUF_SAMPLES_MONO 512 +#define NRF5_AUDIO_I2S_BUF_COUNT 2 + +// Mono bytes held in the circular buffer (caller <-> DMA). 4096 bytes = 2048 +// mono samples = 128ms at 16kHz. +#define NRF5_AUDIO_CIRC_BUF_SIZE_BYTES 4096 + +// Request a refill from the caller whenever this much mono-data space is free. +#define NRF5_AUDIO_REFILL_THRESHOLD_BYTES 1024 + +typedef struct AudioDeviceState { + AudioTransCB trans_cb; + bool is_running; + bool callback_pending; + uint8_t buf_idx; + + // Requested volume in percent, cached so it can be applied whenever the + // codec is (re)powered. + uint8_t volume; + + int16_t *i2s_bufs[NRF5_AUDIO_I2S_BUF_COUNT]; + + uint8_t *circ_buffer_storage; + CircularBuffer circ_buffer; +} AudioDeviceState; + +typedef const struct AudioDevice { + AudioDeviceState *state; + + nrfx_i2s_t i2s_instance; + uint32_t sck_pin; + uint32_t lrck_pin; + uint32_t mck_pin; + uint32_t sdout_pin; + uint32_t sdin_pin; + uint8_t irq_priority; + + I2CSlavePort *codec; + + const BoardPowerOps *power_ops; + + uint32_t samplerate; +} AudioDevice; diff --git a/src/fw/drivers/speaker/qemu/audio.c b/src/fw/drivers/speaker/qemu/audio.c new file mode 100644 index 0000000000..0143520c5c --- /dev/null +++ b/src/fw/drivers/speaker/qemu/audio.c @@ -0,0 +1,99 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/speaker/qemu/audio.h" + +#include "services/system_task.h" + +#include "FreeRTOS.h" + +#include +#include + +// QEMU audio device register offsets (must match pebble-audio QEMU device) +#define AUDIO_CTRL 0x00 +#define AUDIO_STATUS 0x04 +#define AUDIO_SAMPLERATE 0x08 +#define AUDIO_DATA 0x0C +#define AUDIO_INTCTRL 0x10 +#define AUDIO_INTSTAT 0x14 +#define AUDIO_BUFAVAIL 0x18 +#define AUDIO_VOLUME 0x1C + +// Interrupt bits +#define INT_BUFAVAIL (1 << 0) + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +void audio_init(AudioDevice *dev) { + REG32(dev->base_addr + AUDIO_CTRL) = 0; + REG32(dev->base_addr + AUDIO_SAMPLERATE) = 16000; + dev->state->trans_cb = NULL; +} + +static void prv_audio_system_task_cb(void *data) { + AudioDeviceState *state = (AudioDeviceState *)data; + if (state->trans_cb) { + // Read how many samples the QEMU ring buffer can accept. + // We don't have the base_addr here directly, but the callback + // will trigger audio_write() which checks BUFAVAIL itself. + // Pass a generous free size — the actual limit is enforced by + // audio_write() returning when the QEMU buffer is full. + uint32_t free_bytes = 4096 * sizeof(int16_t); + state->trans_cb(&free_bytes); + } +} + +void audio_start(AudioDevice *dev, AudioTransCB cb) { + dev->state->trans_cb = cb; + + // Set sample rate and enable the device + REG32(dev->base_addr + AUDIO_SAMPLERATE) = 16000; + REG32(dev->base_addr + AUDIO_INTSTAT) = INT_BUFAVAIL; // clear pending + REG32(dev->base_addr + AUDIO_INTCTRL) = INT_BUFAVAIL; // enable IRQ + REG32(dev->base_addr + AUDIO_CTRL) = 1; // enable + + // Enable NVIC IRQ + NVIC_SetPriority(dev->irqn, 5); + NVIC_EnableIRQ(dev->irqn); +} + +uint32_t audio_write(AudioDevice *dev, void *buf, uint32_t size) { + int16_t *samples = (int16_t *)buf; + uint32_t num_samples = size / sizeof(int16_t); + + for (uint32_t i = 0; i < num_samples; i++) { + REG32(dev->base_addr + AUDIO_DATA) = (uint32_t)(uint16_t)samples[i]; + } + + // Return how many bytes of free space remain + uint32_t avail = REG32(dev->base_addr + AUDIO_BUFAVAIL); + return avail * sizeof(int16_t); +} + +void audio_set_volume(AudioDevice *dev, int volume) { + REG32(dev->base_addr + AUDIO_VOLUME) = (uint32_t)volume; +} + +void audio_stop(AudioDevice *dev) { + REG32(dev->base_addr + AUDIO_CTRL) = 0; + REG32(dev->base_addr + AUDIO_INTCTRL) = 0; + + NVIC_DisableIRQ(dev->irqn); + + dev->state->trans_cb = NULL; +} + +void qemu_audio_irq_handler(AudioDevice *dev) { + // Clear the interrupt + REG32(dev->base_addr + AUDIO_INTSTAT) = INT_BUFAVAIL; + + // Schedule callback on system task + if (dev->state->trans_cb) { + bool should_context_switch = false; + system_task_add_callback_from_isr(prv_audio_system_task_cb, + (void *)dev->state, + &should_context_switch); + portEND_SWITCHING_ISR(should_context_switch); + } +} diff --git a/src/fw/drivers/speaker/qemu/audio.h b/src/fw/drivers/speaker/qemu/audio.h new file mode 100644 index 0000000000..b2413ebd7c --- /dev/null +++ b/src/fw/drivers/speaker/qemu/audio.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +#include "drivers/audio.h" + +typedef struct AudioState { + AudioTransCB trans_cb; +} AudioDeviceState; + +struct AudioDevice { + AudioDeviceState *state; + uint32_t base_addr; + int irqn; +}; + +void qemu_audio_irq_handler(AudioDevice *dev); diff --git a/src/fw/drivers/speaker/sf32lb52/audec.c b/src/fw/drivers/speaker/sf32lb52/audec.c new file mode 100644 index 0000000000..d5d944873b --- /dev/null +++ b/src/fw/drivers/speaker/sf32lb52/audec.c @@ -0,0 +1,481 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "audio_definitions.h" +#include "kernel/pbl_malloc.h" +#include "mcu/cache.h" +#include "system/passert.h" +#include "system/logging.h" +#include "util/misc.h" +#include "pbl/services/system_task.h" +#include "kernel/util/stop.h" + +PBL_LOG_MODULE_DEFINE(driver_speaker_sf32lb, CONFIG_DRIVER_SPEAKER_LOG_LEVEL); + +//AVDD 3V3 for obelix +#define BSP_AVDD_V18_ENABLE 0 +#if BSP_AVDD_V18_ENABLE + #define SINC_GAIN 0xa0 +#else + #define SINC_GAIN 0x14D +#endif + +static const AUDCODE_DAC_CLK_CONFIG_TYPE codec_dac_clk_config[9] = +{ +#if ALL_CLK_USING_PLL + {48000, 1, 1, 0, SINC_GAIN, 1, 5, 4, 2, 20, 20, 0}, + {32000, 1, 1, 1, SINC_GAIN, 1, 5, 4, 2, 20, 20, 0}, + {24000, 1, 1, 5, SINC_GAIN, 1, 10, 2, 2, 10, 10, 1}, + {16000, 1, 1, 4, SINC_GAIN, 1, 5, 4, 2, 20, 20, 0}, + {12000, 1, 1, 7, SINC_GAIN, 1, 20, 2, 1, 5, 5, 1}, + { 8000, 1, 1, 8, SINC_GAIN, 1, 10, 2, 2, 10, 10, 1}, +#else + {48000, 0, 1, 0, SINC_GAIN, 0, 5, 4, 2, 20, 20, 0}, + {32000, 0, 1, 1, SINC_GAIN, 0, 5, 4, 2, 20, 20, 0}, + {24000, 0, 1, 5, SINC_GAIN, 0, 10, 2, 2, 10, 10, 1}, + {16000, 0, 1, 4, SINC_GAIN, 0, 5, 4, 2, 20, 20, 0}, + {12000, 0, 1, 7, SINC_GAIN, 0, 20, 2, 1, 5, 5, 1}, + { 8000, 0, 1, 8, SINC_GAIN, 0, 10, 2, 2, 10, 10, 1}, +#endif + {44100, 1, 1, 0, SINC_GAIN, 1, 5, 4, 2, 20, 20, 0}, + {22050, 1, 1, 5, SINC_GAIN, 1, 10, 2, 2, 10, 10, 1}, + {11025, 1, 1, 7, SINC_GAIN, 1, 20, 2, 1, 5, 5, 1}, +}; +#define DAC_CLK_CONFIG_SIZE (sizeof(codec_dac_clk_config)/sizeof(AUDCODE_DAC_CLK_CONFIG_TYPE)) + +typedef struct pll_vco +{ + uint32_t freq; + uint32_t vco_value; + uint32_t target_cnt; +} pll_vco_t; + +static pll_vco_t g_pll_vco_tab[2] = +{ + {48, 0, 2001}, + {44, 0, 1834}, +}; + +static void prv_bf0_disable_pll(AudioDeviceState* state) +{ + HAL_TURN_OFF_PLL(); + state->pll_state = AUDIO_PLL_CLOSED; +} + +static int prv_bf0_pll_calibration() +{ + uint32_t pll_cnt; + uint32_t fc_vco; + uint32_t fc_vco_min; + uint32_t fc_vco_max; + uint32_t delta_cnt = 0; + uint32_t delta_cnt_min = 0; + uint32_t delta_cnt_max = 0; + uint32_t delta_fc_vco; + uint32_t target_cnt; + + HAL_PMU_EnableAudio(1); + HAL_RCC_EnableModule(RCC_MOD_AUDCODEC_HP); + HAL_RCC_EnableModule(RCC_MOD_AUDCODEC_LP); + + HAL_TURN_ON_PLL(); + + // VCO freq calibration + hwp_audcodec->PLL_CFG0 |= AUDCODEC_PLL_CFG0_OPEN; + hwp_audcodec->PLL_CFG2 |= AUDCODEC_PLL_CFG2_EN_LF_VCIN; + hwp_audcodec->PLL_CAL_CFG = (0 << AUDCODEC_PLL_CAL_CFG_EN_Pos) | + (2000 << AUDCODEC_PLL_CAL_CFG_LEN_Pos); + for (uint8_t i = 0; i < sizeof(g_pll_vco_tab) / sizeof(g_pll_vco_tab[0]); i++) + { + target_cnt = g_pll_vco_tab[i].target_cnt; + fc_vco = 16; + delta_fc_vco = 8; + /* setup calibration and run + ** target pll_cnt = ceil(46MHz/48MHz*2000)+1 = 1918 + ** target difference between pll_cnt and xtal_cnt should be less than 1 */ + while (delta_fc_vco != 0) + { + hwp_audcodec->PLL_CFG0 &= ~AUDCODEC_PLL_CFG0_FC_VCO; + hwp_audcodec->PLL_CFG0 |= (fc_vco << AUDCODEC_PLL_CFG0_FC_VCO_Pos); + hwp_audcodec->PLL_CAL_CFG |= AUDCODEC_PLL_CAL_CFG_EN; + while (!(hwp_audcodec->PLL_CAL_CFG & AUDCODEC_PLL_CAL_CFG_DONE_Msk)); + pll_cnt = (hwp_audcodec->PLL_CAL_RESULT >> AUDCODEC_PLL_CAL_RESULT_PLL_CNT_Pos); + hwp_audcodec->PLL_CAL_CFG &= ~AUDCODEC_PLL_CAL_CFG_EN; + if (pll_cnt < target_cnt) + { + fc_vco = fc_vco + delta_fc_vco; + delta_cnt = target_cnt - pll_cnt; + } + else if (pll_cnt > target_cnt) + { + fc_vco = fc_vco - delta_fc_vco; + delta_cnt = pll_cnt - target_cnt; + } + delta_fc_vco = delta_fc_vco >> 1; + } + + if (fc_vco == 0) + { + fc_vco_min = 0; + } + else + { + fc_vco_min = fc_vco - 1; + } + if (fc_vco == 31) + { + fc_vco_max = fc_vco; + } + else + { + fc_vco_max = fc_vco + 1; + } + + hwp_audcodec->PLL_CFG0 &= ~AUDCODEC_PLL_CFG0_FC_VCO; + hwp_audcodec->PLL_CFG0 |= (fc_vco_min << AUDCODEC_PLL_CFG0_FC_VCO_Pos); + hwp_audcodec->PLL_CAL_CFG |= AUDCODEC_PLL_CAL_CFG_EN; + + while (!(hwp_audcodec->PLL_CAL_CFG & AUDCODEC_PLL_CAL_CFG_DONE_Msk)); + pll_cnt = (hwp_audcodec->PLL_CAL_RESULT >> AUDCODEC_PLL_CAL_RESULT_PLL_CNT_Pos); + hwp_audcodec->PLL_CAL_CFG &= ~AUDCODEC_PLL_CAL_CFG_EN; + if (pll_cnt < target_cnt) + { + delta_cnt_min = target_cnt - pll_cnt; + } + else if (pll_cnt > target_cnt) + { + delta_cnt_min = pll_cnt - target_cnt; + } + + hwp_audcodec->PLL_CFG0 &= ~AUDCODEC_PLL_CFG0_FC_VCO; + hwp_audcodec->PLL_CFG0 |= (fc_vco_max << AUDCODEC_PLL_CFG0_FC_VCO_Pos); + hwp_audcodec->PLL_CAL_CFG |= AUDCODEC_PLL_CAL_CFG_EN; + while (!(hwp_audcodec->PLL_CAL_CFG & AUDCODEC_PLL_CAL_CFG_DONE_Msk)); + pll_cnt = (hwp_audcodec->PLL_CAL_RESULT >> AUDCODEC_PLL_CAL_RESULT_PLL_CNT_Pos); + hwp_audcodec->PLL_CAL_CFG &= ~AUDCODEC_PLL_CAL_CFG_EN; + if (pll_cnt < target_cnt) + { + delta_cnt_max = target_cnt - pll_cnt; + } + else if (pll_cnt > target_cnt) + { + delta_cnt_max = pll_cnt - target_cnt; + } + + if (delta_cnt_min <= delta_cnt && delta_cnt_min <= delta_cnt_max) + { + g_pll_vco_tab[i].vco_value = fc_vco_min; + } + else if (delta_cnt_max <= delta_cnt && delta_cnt_max <= delta_cnt_min) + { + g_pll_vco_tab[i].vco_value = fc_vco_max; + } + else + { + g_pll_vco_tab[i].vco_value = fc_vco; + } + } + hwp_audcodec->PLL_CFG2 &= ~AUDCODEC_PLL_CFG2_EN_LF_VCIN; + hwp_audcodec->PLL_CFG0 &= ~AUDCODEC_PLL_CFG0_OPEN; + + HAL_TURN_OFF_PLL(); + + return 0; +} + +static int prv_bf0_enable_pll(uint32_t freq, uint8_t type) +{ + uint8_t freq_type; + uint8_t test_result = -1; + uint8_t vco_index = 0; + + freq_type = type << 1; + if ((freq == 44100) || (freq == 22050) || (freq == 11025)) + { + vco_index = 1; + freq_type += 1; + } + + HAL_TURN_ON_PLL(); + + hwp_audcodec->PLL_CFG0 &= ~AUDCODEC_PLL_CFG0_FC_VCO; + hwp_audcodec->PLL_CFG0 |= (g_pll_vco_tab[vco_index].vco_value << AUDCODEC_PLL_CFG0_FC_VCO_Pos); + + do + { + test_result = updata_pll_freq(freq_type); + } + while (test_result != 0); + + return test_result; +} + +static int prv_bf0_update_pll(uint32_t freq, uint8_t type) +{ + uint8_t freq_type; + uint8_t test_result = -1; + uint8_t vco_index = 0; + + freq_type = type << 1; + if ((freq == 44100) || (freq == 22050) || (freq == 11025)) + { + vco_index = 1; + freq_type += 1; + } + + hwp_audcodec->PLL_CFG0 &= ~AUDCODEC_PLL_CFG0_FC_VCO; + hwp_audcodec->PLL_CFG0 |= (g_pll_vco_tab[vco_index].vco_value << AUDCODEC_PLL_CFG0_FC_VCO_Pos); + + do + { + test_result = updata_pll_freq(freq_type); + } + while (test_result != 0); + + return test_result; +} + +static void prv_bf0_audio_pll_config(AudioDevice* audio_device, const AUDCODE_DAC_CLK_CONFIG_TYPE *dac_cfg) { + AudioDeviceState* state = audio_device->state; + if (dac_cfg->clk_src_sel) //pll + { + if (state->pll_state == AUDIO_PLL_CLOSED) + { + prv_bf0_enable_pll(dac_cfg->samplerate, 1); + state->pll_state = AUDIO_PLL_ENABLE; + state->pll_samplerate = dac_cfg->samplerate; + } + else + { + prv_bf0_update_pll(dac_cfg->samplerate, 1); + state->pll_state = AUDIO_PLL_ENABLE; + state->pll_samplerate = dac_cfg->samplerate; + } + } + else //xtal + { + if (state->pll_state == AUDIO_PLL_CLOSED) + { + HAL_TURN_ON_PLL(); + state->pll_state = AUDIO_PLL_OPEN; + } + } +} + +static bool prv_allocate_buffers(AudioDeviceState *state) { + // Allocate circular buffer storage + state->circ_buffer_storage = kernel_malloc(CIRCULAR_BUF_SIZE_BYTES); + if (!state->circ_buffer_storage) { + PBL_LOG_ERR("Failed to allocate circular buffer storage"); + return false; + } + + // Initialize circular buffer with allocated storage + circular_buffer_init(&state->circ_buffer, state->circ_buffer_storage, CIRCULAR_BUF_SIZE_BYTES); + + return true; +} + +static void prv_free_buffers(AudioDeviceState *state) { + // Free circular buffer storage + if (state->circ_buffer_storage) { + kernel_free(state->circ_buffer_storage); + state->circ_buffer_storage = NULL; + } +} + +bool audec_init(AudioDevice* audio_device) { + AudioDeviceState* state = audio_device->state; + AUDCODEC_HandleTypeDef *haudcodec = &state->audcodec; + haudcodec->Instance = hwp_audcodec; + memset(&state->dac_dma_handle, 0, sizeof(state->dac_dma_handle)); + haudcodec->hdma[HAL_AUDCODEC_DAC_CH0] = &state->dac_dma_handle; + + haudcodec->hdma[HAL_AUDCODEC_DAC_CH0]->Instance = audio_device->audec_dma_channel; + haudcodec->hdma[HAL_AUDCODEC_DAC_CH0]->Init.Request = audio_device->audec_dma_request; + haudcodec->hdma[HAL_AUDCODEC_DAC_CH0]->Init.IrqPrio = audio_device->irq_priority; + + haudcodec->Init.en_dly_sel = 0; + haudcodec->Init.dac_cfg.opmode = 1; + haudcodec->Init.adc_cfg.opmode = 1; + haudcodec->bufSize = CFG_AUDIO_PLAYBACK_PIPE_SIZE * 2; + state->audec_queue_buf[HAL_AUDCODEC_DAC_CH0] = NULL; + + HAL_PMU_EnableAudio(1); + HAL_RCC_EnableModule(RCC_MOD_AUDCODEC_HP); + HAL_RCC_EnableModule(RCC_MOD_AUDCODEC_LP); + HAL_AUDCODEC_Init(haudcodec); + + for (uint8_t i = 0; i < DAC_CLK_CONFIG_SIZE; i++) + { + if (audio_device->samplerate == codec_dac_clk_config[i].samplerate) + { + haudcodec->Init.samplerate_index = i; + haudcodec->Init.dac_cfg.dac_clk = (AUDCODE_DAC_CLK_CONFIG_TYPE *)&codec_dac_clk_config[i]; + break; + } + } + + if (haudcodec->buf[HAL_AUDCODEC_DAC_CH0] == NULL) + { + // Over-allocate so the DMA buffer can start on a cache-line boundary. + // Each half is consumed by DMA while the CPU fills the other half; + // dcache_flush() of one half must not touch lines that belong to the + // half currently being DMA'd, so both halves need to be aligned. + const size_t cache_align = dcache_line_size(); + state->raw_dac_buffer = kernel_malloc(haudcodec->bufSize + cache_align - 1U); + PBL_ASSERT(state->raw_dac_buffer, "allocated mem error"); + haudcodec->buf[HAL_AUDCODEC_DAC_CH0] = + (uint8_t *)(((uintptr_t)state->raw_dac_buffer + cache_align - 1U) & + ~(uintptr_t)(cache_align - 1U)); + memset(haudcodec->buf[HAL_AUDCODEC_DAC_CH0], 0, haudcodec->bufSize); + } + state->queue_buf[HAL_AUDCODEC_DAC_CH0] = haudcodec->buf[HAL_AUDCODEC_DAC_CH0]; + HAL_AUDCODEC_Config_TChanel(haudcodec, 0, &haudcodec->Init.dac_cfg); + state->tx_instanc = HAL_AUDCODEC_DAC_CH0; + + prv_bf0_pll_calibration(); + + return true; +} + +void audec_start(AudioDevice* audio_device, AudioTransCB cb) { + AudioDeviceState* state = audio_device->state; + AUDCODEC_HandleTypeDef *haudcodec = &state->audcodec; + state->trans_cb = cb; + + stop_mode_disable(InhibitorAudio); + + prv_allocate_buffers(state); + + prv_bf0_audio_pll_config(audio_device, &codec_dac_clk_config[haudcodec->Init.samplerate_index]); + HAL_AUDCODEC_Config_TChanel(haudcodec, 0, &haudcodec->Init.dac_cfg); + HAL_NVIC_SetPriority(audio_device->audec_dma_irq, audio_device->irq_priority, 0); + // The buffer was memset() to zero by the CPU at init and is again at stop; + // those writes may still be sitting in D-cache. Push them to RAM so the + // codec DMA reads silence on the first transfer instead of stale memory. + dcache_flush(haudcodec->buf[HAL_AUDCODEC_DAC_CH0], haudcodec->bufSize); + HAL_AUDCODEC_Transmit_DMA(haudcodec, haudcodec->buf[HAL_AUDCODEC_DAC_CH0], haudcodec->bufSize, HAL_AUDCODEC_DAC_CH0); + HAL_NVIC_EnableIRQ(audio_device->audec_dma_irq); + state->tx_instanc = HAL_AUDCODEC_DAC_CH0; + + /* enable AUDCODEC at last*/ + __HAL_AUDCODEC_DAC_ENABLE(haudcodec); + + HAL_AUDCODEC_Config_DACPath(haudcodec, 1); + HAL_AUDCODEC_Config_Analog_DACPath(haudcodec->Init.dac_cfg.dac_clk); + HAL_AUDCODEC_Config_DACPath(haudcodec, 0); +} + +uint32_t audec_write(AudioDevice* audio_device, void *writeBuf, uint32_t size) { + AudioDeviceState* state = audio_device->state; + if (state->circ_buffer_storage) { + uint32_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer); + uint16_t to_write = (size > free_size) ? (uint16_t)free_size : (uint16_t)size; + if (to_write > 0) { + circular_buffer_write(&state->circ_buffer, writeBuf, to_write); + } + return circular_buffer_get_write_space_remaining(&state->circ_buffer); + } + + return 0; +} + +void audec_set_vol(AudioDevice* audio_device, int volume) { + AUDCODEC_HandleTypeDef *haudcodec = &audio_device->state->audcodec; + #define MIN_VOLUME 0 + #define MAX_VOLUME 100 + if (volume > MAX_VOLUME) + volume = MAX_VOLUME; + if (volume < MIN_VOLUME) + volume = MIN_VOLUME; + + if (volume == 0) { + HAL_AUDCODEC_Mute_DACPath(haudcodec, 1); + } else { + HAL_AUDCODEC_Mute_DACPath(haudcodec, 0); + //convert to HAL decoder range (-36~54)*2 + volume -= 36; + volume *= 2; + HAL_AUDCODEC_Config_DACPath_Volume(haudcodec, 0, volume); + HAL_AUDCODEC_Config_DACPath_Volume(haudcodec, 1, volume); + } +} + +void audec_stop(AudioDevice* audio_device) { + AudioDeviceState* state = audio_device->state; + AUDCODEC_HandleTypeDef *haudcodec = &state->audcodec; + + prv_bf0_disable_pll(state); + + HAL_NVIC_DisableIRQ(audio_device->audec_dma_irq); + HAL_AUDCODEC_DMAStop(haudcodec, HAL_AUDCODEC_DAC_CH0); + haudcodec->channel_ref &= ~(1 << HAL_AUDCODEC_DAC_CH0); + haudcodec->State[HAL_AUDCODEC_DAC_CH0] = HAL_AUDCODEC_STATE_READY; + + HAL_AUDCODEC_Config_DACPath(haudcodec, 1); + HAL_AUDCODEC_Close_Analog_DACPath(); + __HAL_AUDCODEC_DAC_DISABLE(haudcodec); + HAL_AUDCODEC_Clear_All_Channel(haudcodec, 1); + + prv_free_buffers(state); + memset(haudcodec->buf[HAL_AUDCODEC_DAC_CH0], 0, haudcodec->bufSize); + + stop_mode_enable(InhibitorAudio); +} + +void audec_dac0_dma_irq_handler(AudioDevice* audio_device) +{ + HAL_DMA_IRQHandler(audio_device->state->audcodec.hdma[0]); +} + +static void prv_audio_trans_bg(void* data) { + AudioDeviceState* state = (AudioDeviceState*) data; + uint32_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer); + state->trans_cb(&free_size); +} + +static void prv_dma_request_processing(AudioDeviceState* state) { + if (!state->circ_buffer_storage) return; + + uint32_t available_data = circular_buffer_get_read_space_remaining(&state->circ_buffer); + uint32_t trans_size = CFG_AUDIO_PLAYBACK_PIPE_SIZE; + if (available_data < CFG_AUDIO_PLAYBACK_PIPE_SIZE) { + PBL_LOG_DBG("audio data not enough remain:%" PRIu32 "", available_data); + memset(state->queue_buf[HAL_AUDCODEC_DAC_CH0], 0, CFG_AUDIO_PLAYBACK_PIPE_SIZE); + trans_size = available_data; + } + if (trans_size > 0) { + uint16_t bytes_copied = circular_buffer_copy(&state->circ_buffer, + state->queue_buf[HAL_AUDCODEC_DAC_CH0], trans_size); + PBL_ASSERT(bytes_copied == trans_size, "circ buffer read err"); + circular_buffer_consume(&state->circ_buffer, bytes_copied); + } + // Codec DMA reads this half-buffer next time it wraps; flush the CPU-side + // writes (memset for underrun and circular_buffer_copy above) so the DAC + // doesn't replay stale RAM contents. We always flush a full half because + // any bytes we didn't touch were already memset() to silence. + dcache_flush(state->queue_buf[HAL_AUDCODEC_DAC_CH0], CFG_AUDIO_PLAYBACK_PIPE_SIZE); + uint32_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer); + if(state->trans_cb && free_size >= CFG_AUDIO_PLAYBACK_PIPE_SIZE) { + bool system_task_switch_context = false; + system_task_add_callback_from_isr(prv_audio_trans_bg, (void*)state, + &system_task_switch_context); + } +} + +void HAL_AUDCODEC_TxCpltCallback(AUDCODEC_HandleTypeDef *haprc, int cid) +{ + AudioDeviceState* state = container_of(haprc, struct AudioState, audcodec); + + state->queue_buf[HAL_AUDCODEC_DAC_CH0] = haprc->buf[HAL_AUDCODEC_DAC_CH0] + haprc->bufSize/2; + prv_dma_request_processing(state); +} + +void HAL_AUDCODEC_TxHalfCpltCallback(AUDCODEC_HandleTypeDef *haprc, int cid) +{ + AudioDeviceState* state = container_of(haprc, struct AudioState, audcodec); + + state->queue_buf[HAL_AUDCODEC_DAC_CH0] = haprc->buf[HAL_AUDCODEC_DAC_CH0]; + prv_dma_request_processing(state); +} \ No newline at end of file diff --git a/src/fw/drivers/speaker/sf32lb52/audio.c b/src/fw/drivers/speaker/sf32lb52/audio.c new file mode 100644 index 0000000000..16d6bf8259 --- /dev/null +++ b/src/fw/drivers/speaker/sf32lb52/audio.c @@ -0,0 +1,47 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "audio_definitions.h" +#include "drivers/gpio.h" +#include "kernel/util/delay.h" +#include "drivers/audio.h" +#include "sf32lb_audio.h" + +#define PA_POWER_DELAY_TIME (200) /* us */ + +void audio_init(AudioDevice* audio_device) { + gpio_output_init(&audio_device->pa_ctrl, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&audio_device->pa_ctrl, false); + delay_us(PA_POWER_DELAY_TIME*10); + audec_init(audio_device); +} + +void audio_start(AudioDevice* audio_device, AudioTransCB cb) { + if (audio_device->power_ops && audio_device->power_ops->power_up) { + audio_device->power_ops->power_up(); + } + + gpio_output_set(&audio_device->pa_ctrl, true); + delay_us(PA_POWER_DELAY_TIME); + gpio_output_set(&audio_device->pa_ctrl, false); + delay_us(PA_POWER_DELAY_TIME); + gpio_output_set(&audio_device->pa_ctrl, true); + audec_start(audio_device, cb); +} + +uint32_t audio_write(AudioDevice* audio_device, void *writeBuf, uint32_t size) { + return audec_write(audio_device, writeBuf, size); +} + +void audio_set_volume(AudioDevice* audio_device, int volume) { + audec_set_vol(audio_device, volume); +} + +void audio_stop(AudioDevice* audio_device) { + audec_stop(audio_device); + gpio_output_set(&audio_device->pa_ctrl, false); + + if (audio_device->power_ops && audio_device->power_ops->power_down) { + audio_device->power_ops->power_down(); + } +} diff --git a/src/fw/drivers/speaker/sf32lb52/audio_definitions.h b/src/fw/drivers/speaker/sf32lb52/audio_definitions.h new file mode 100644 index 0000000000..dafe81d96a --- /dev/null +++ b/src/fw/drivers/speaker/sf32lb52/audio_definitions.h @@ -0,0 +1,67 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "board/board.h" +#include "drivers/audio.h" +#include +#include + +#include +#include + +#define CFG_AUDIO_PLAYBACK_PIPE_SIZE (1024) + +// Circular buffer configuration +#define CIRCULAR_BUF_SIZE_MS (128) +#define CIRCULAR_BUF_SIZE_SAMPLES ((MIC_SAMPLE_RATE * CIRCULAR_BUF_SIZE_MS) / 1000) +#define CIRCULAR_BUF_SIZE_BYTES (CIRCULAR_BUF_SIZE_SAMPLES * sizeof(int16_t)) + +typedef enum AUDIO_PLL_STATE_TAG +{ + AUDIO_PLL_CLOSED, + AUDIO_PLL_OPEN, + AUDIO_PLL_ENABLE, +} AUDIO_PLL_STATE; + +typedef struct AudioState { + AUDCODEC_HandleTypeDef audcodec; + DMA_HandleTypeDef dac_dma_handle; + AUDPRC_HandleTypeDef audprc; + uint32_t slot_valid; + uint8_t *queue_buf[HAL_AUDPRC_INSTANC_CNT]; + uint8_t *audec_queue_buf[HAL_AUDCODEC_INSTANC_CNT]; + AUDIO_PLL_STATE pll_state; + uint32_t pll_samplerate; + uint8_t tx_instanc; + bool tx_rbf_enable; + uint16_t tx_buffer_size; + uint8_t *circ_buffer_storage; + CircularBuffer circ_buffer; + AudioTransCB trans_cb; + //! Raw (unaligned) pointer returned by kernel_malloc for the AUDCODEC DAC + //! DMA buffer. haudcodec->buf[] is bumped up to a cache-line boundary so + //! dcache_flush() of one half can't touch the other half's lines. + uint8_t *raw_dac_buffer; +} AudioDeviceState; + +typedef const struct AudioDevice { + AudioDeviceState *state; + uint32_t irq_priority; + DMA_Channel_TypeDef *audprc_dma_channel; + uint32_t audprc_dma_request; + IRQn_Type audprc_dma_irq; + DMA_Channel_TypeDef *audec_dma_channel; + uint32_t audec_dma_request; + IRQn_Type audec_dma_irq; + OutputConfig pa_ctrl; + const BoardPowerOps *power_ops; + uint8_t data_format; + uint8_t data_mode; + uint32_t samplerate; + uint32_t channels; +} AudioDevice; + +extern void audprc_dma_iqr_handler(AudioDevice* audio_device); +extern void audec_dac0_dma_irq_handler(AudioDevice* audio_device); diff --git a/src/fw/drivers/speaker/sf32lb52/sf32lb_audio.h b/src/fw/drivers/speaker/sf32lb52/sf32lb_audio.h new file mode 100644 index 0000000000..84710781db --- /dev/null +++ b/src/fw/drivers/speaker/sf32lb52/sf32lb_audio.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +extern bool audec_init(AudioDevice* audio_device); +extern void audec_start(AudioDevice* audio_device, AudioTransCB cb); +extern uint32_t audec_write(AudioDevice* audio_device, void *writeBuf, uint32_t size); +extern void audec_set_vol(AudioDevice* audio_device, int volume); +extern void audec_stop(AudioDevice* audio_device); \ No newline at end of file diff --git a/src/fw/drivers/speaker/wscript_build b/src/fw/drivers/speaker/wscript_build new file mode 100644 index 0000000000..7ad34a9284 --- /dev/null +++ b/src/fw/drivers/speaker/wscript_build @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_SPEAKER_DA7212: + sources.append('nrf5/da7212.c') + use.extend([ + 'driver_i2c', + 'driver_nrf5_hfxo', + 'freertos', + 'hal_nordic', + ]) +elif bld.env.CONFIG_SPEAKER_SF32LB: + sources.extend([ + 'sf32lb52/audec.c', + 'sf32lb52/audio.c', + ]) + use.extend([ + 'driver_gpio', + 'hal_sifli', + ]) +elif bld.env.CONFIG_SPEAKER_QEMU: + sources.append('qemu/audio.c') + use.append('freertos') + +bld.objects( + name='driver_audio', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_audio') diff --git a/src/fw/drivers/spi.h b/src/fw/drivers/spi.h index 9756268944..62489ef7ae 100644 --- a/src/fw/drivers/spi.h +++ b/src/fw/drivers/spi.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/spi_dma.h b/src/fw/drivers/spi_dma.h index 5fa3d86d7b..c7d0ae0c5b 100644 --- a/src/fw/drivers/spi_dma.h +++ b/src/fw/drivers/spi_dma.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/stm32f2/clocksource.c b/src/fw/drivers/stm32f2/clocksource.c deleted file mode 100644 index d6a9dec4c7..0000000000 --- a/src/fw/drivers/stm32f2/clocksource.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "drivers/clocksource.h" - -#include "board/board.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/watchdog.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/delay.h" - -#include "FreeRTOS.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -//! How long to wait for the LSE to start. Empirically about 4 seconds. -//! STM32 datasheet says typical max is 2. -static const int LSE_READY_TIMEOUT_MS = 5000; - -void clocksource_lse_configure(void) { - // Only start the LSE oscillator if it is not already running. The oscillator - // will normally be running even during standby mode to keep the RTC ticking; - // it is only disabled when the microcontroller completely loses power. - if (clocksource_is_lse_started()) { - PBL_LOG(LOG_LEVEL_INFO, "LSE oscillator already running"); - } else { - PBL_LOG(LOG_LEVEL_INFO, "Starting LSE oscillator"); - RCC_LSEConfig(BOARD_LSE_MODE); - for (int i = 0; i < LSE_READY_TIMEOUT_MS; ++i) { - if (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET) { - PBL_LOG(LOG_LEVEL_INFO, "LSE oscillator started after %d ms", i); - break; - } - - delay_us(1000); - watchdog_feed(); - } - if (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) { - PBL_LOG(LOG_LEVEL_ERROR, "LSE oscillator did not start"); - } - } -} - -bool clocksource_is_lse_started(void) { - return (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET); -} - -void clocksource_MCO1_enable(bool on) { - static int8_t s_refcount = 0; - - portENTER_CRITICAL(); - - PBL_ASSERTN(BOARD_CONFIG_MCO1.output_enabled); - if (on) { - gpio_af_init( - &BOARD_CONFIG_MCO1.af_cfg, GPIO_OType_PP, GPIO_Speed_2MHz, GPIO_PuPd_NOPULL); - // LSE is 32kHz, we want 32kHz for our external clock and is used by: - // - The cc2564 bluetooth module - // - Snowy / Spalding display VCOM - RCC_MCO1Config(RCC_MCO1Source_LSE, RCC_MCO1Div_1); - ++s_refcount; - } else { - PBL_ASSERTN(s_refcount > 0); - --s_refcount; - if (s_refcount == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "Disabling MCO1"); - gpio_analog_init(&BOARD_CONFIG_MCO1.an_cfg); - } - } - - portEXIT_CRITICAL(); -} diff --git a/src/fw/drivers/stm32f2/dma.c b/src/fw/drivers/stm32f2/dma.c deleted file mode 100644 index 9e3e23cc11..0000000000 --- a/src/fw/drivers/stm32f2/dma.c +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/dma.h" - -#include "drivers/mpu.h" -#include "drivers/periph_config.h" -#include "drivers/stm32f2/dma_definitions.h" -#include "mcu/cache.h" -#include "system/passert.h" - -#define CMSIS_COMPATIBLE -#include - -#include "FreeRTOS.h" - -#include - - -#define CHSEL_OFFSET __builtin_ctz(DMA_SxCR_CHSEL) - -// all interrupt flags for streams 0-3 -#define ALL_INTERRUPT_FLAGS_L(s) (DMA_LIFCR_CTCIF##s | DMA_LIFCR_CHTIF##s | DMA_LIFCR_CTEIF##s | \ - DMA_LIFCR_CDMEIF##s | DMA_LIFCR_CFEIF##s) -// all interrupt flags for streams 4-7 -#define ALL_INTERRUPT_FLAGS_H(s) (DMA_HIFCR_CTCIF##s | DMA_HIFCR_CHTIF##s | DMA_HIFCR_CTEIF##s | \ - DMA_HIFCR_CDMEIF##s | DMA_HIFCR_CFEIF##s) - - -// Stream Interrupt flag helper functions -//////////////////////////////////////////////////////////////////////////////// - -static void prv_clear_all_interrupt_flags(DMARequest *this) { - switch ((uintptr_t)this->stream->periph) { - case (uintptr_t)DMA1_Stream0: - case (uintptr_t)DMA2_Stream0: - this->stream->controller->periph->LIFCR = ALL_INTERRUPT_FLAGS_L(0); - break; - case (uintptr_t)DMA1_Stream1: - case (uintptr_t)DMA2_Stream1: - this->stream->controller->periph->LIFCR = ALL_INTERRUPT_FLAGS_L(1); - break; - case (uintptr_t)DMA1_Stream2: - case (uintptr_t)DMA2_Stream2: - this->stream->controller->periph->LIFCR = ALL_INTERRUPT_FLAGS_L(2); - break; - case (uintptr_t)DMA1_Stream3: - case (uintptr_t)DMA2_Stream3: - this->stream->controller->periph->LIFCR = ALL_INTERRUPT_FLAGS_L(3); - break; - case (uintptr_t)DMA1_Stream4: - case (uintptr_t)DMA2_Stream4: - this->stream->controller->periph->HIFCR = ALL_INTERRUPT_FLAGS_H(4); - break; - case (uintptr_t)DMA1_Stream5: - case (uintptr_t)DMA2_Stream5: - this->stream->controller->periph->HIFCR = ALL_INTERRUPT_FLAGS_H(5); - break; - case (uintptr_t)DMA1_Stream6: - case (uintptr_t)DMA2_Stream6: - this->stream->controller->periph->HIFCR = ALL_INTERRUPT_FLAGS_H(6); - break; - case (uintptr_t)DMA1_Stream7: - case (uintptr_t)DMA2_Stream7: - this->stream->controller->periph->HIFCR = ALL_INTERRUPT_FLAGS_H(7); - break; - default: - WTF; - } -} - -static void prv_get_and_clear_interrupt_flags(DMARequest *this, bool *has_tc, bool *has_ht) { - switch ((uintptr_t)this->stream->periph) { - case (uintptr_t)DMA1_Stream0: - case (uintptr_t)DMA2_Stream0: - *has_tc = this->stream->controller->periph->LISR & DMA_LISR_TCIF0; - *has_ht = this->stream->controller->periph->LISR & DMA_LISR_HTIF0; - break; - case (uintptr_t)DMA1_Stream1: - case (uintptr_t)DMA2_Stream1: - *has_tc = this->stream->controller->periph->LISR & DMA_LISR_TCIF1; - *has_ht = this->stream->controller->periph->LISR & DMA_LISR_HTIF1; - break; - case (uintptr_t)DMA1_Stream2: - case (uintptr_t)DMA2_Stream2: - *has_tc = this->stream->controller->periph->LISR & DMA_LISR_TCIF2; - *has_ht = this->stream->controller->periph->LISR & DMA_LISR_HTIF2; - break; - case (uintptr_t)DMA1_Stream3: - case (uintptr_t)DMA2_Stream3: - *has_tc = this->stream->controller->periph->LISR & DMA_LISR_TCIF3; - *has_ht = this->stream->controller->periph->LISR & DMA_LISR_HTIF3; - break; - case (uintptr_t)DMA1_Stream4: - case (uintptr_t)DMA2_Stream4: - *has_tc = this->stream->controller->periph->HISR & DMA_HISR_TCIF4; - *has_ht = this->stream->controller->periph->HISR & DMA_HISR_HTIF4; - break; - case (uintptr_t)DMA1_Stream5: - case (uintptr_t)DMA2_Stream5: - *has_tc = this->stream->controller->periph->HISR & DMA_HISR_TCIF5; - *has_ht = this->stream->controller->periph->HISR & DMA_HISR_HTIF5; - break; - case (uintptr_t)DMA1_Stream6: - case (uintptr_t)DMA2_Stream6: - *has_tc = this->stream->controller->periph->HISR & DMA_HISR_TCIF6; - *has_ht = this->stream->controller->periph->HISR & DMA_HISR_HTIF6; - break; - case (uintptr_t)DMA1_Stream7: - case (uintptr_t)DMA2_Stream7: - *has_tc = this->stream->controller->periph->HISR & DMA_HISR_TCIF7; - *has_ht = this->stream->controller->periph->HISR & DMA_HISR_HTIF7; - break; - default: - WTF; - } - prv_clear_all_interrupt_flags(this); -} - - -// Controller clock control -//////////////////////////////////////////////////////////////////////////////// - -static void prv_use_controller(DMAController *this) { - const int old_refcount = atomic_fetch_add(&this->state->refcount, 1); - if (old_refcount == 0) { - periph_config_enable(this->periph, this->rcc_bit); - } -} - -static void prv_release_controller(DMAController *this) { - const int refcount = atomic_fetch_sub(&this->state->refcount, 1) - 1; - PBL_ASSERT(refcount >= 0, "Attempted to release a DMA controller that is not in use!"); - if (refcount == 0) { - periph_config_disable(this->periph, this->rcc_bit); - } -} - - -// Initialization -//////////////////////////////////////////////////////////////////////////////// - -static uint32_t prv_get_data_size_bytes(DMARequest *this) { - switch (this->data_size) { - case DMARequestDataSize_Byte: - return 1; - case DMARequestDataSize_HalfWord: - return 2; - case DMARequestDataSize_Word: - return 4; - default: - WTF; - return 0; - } -} - -static void prv_set_constant_config(DMARequest *this) { - uint32_t cr_value = 0; - uint32_t fcr_value = 0; - - // set the channel - PBL_ASSERTN((this->channel & (DMA_SxCR_CHSEL >> CHSEL_OFFSET)) == this->channel); - cr_value |= this->channel << CHSEL_OFFSET; - - // set the priority - PBL_ASSERTN((this->priority & DMA_SxCR_PL) == this->priority); - cr_value |= this->priority; - - // set the data size - PBL_ASSERTN((this->data_size & (DMA_SxCR_MSIZE | DMA_SxCR_PSIZE)) == this->data_size); - cr_value |= this->data_size; - - // set the direction - PBL_ASSERTN((this->type & (DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1)) == this->type); - cr_value |= this->type; - - // set the incrementing modes, FIFO, and burst sizes - switch (this->type) { - case DMARequestType_MemoryToMemory: - // memory and peripheral burst of 8 (found to be fastest based on testing on Snowy / Robert) - cr_value |= DMA_SxCR_MBURST_1 | DMA_SxCR_PBURST_1; - // memory and peripheral incrementing enabled - cr_value |= DMA_SxCR_MINC | DMA_SxCR_PINC; - // enable the FIFO with a threshold of 1/2 full - fcr_value |= DMA_SxFCR_DMDIS; - fcr_value |= DMA_SxFCR_FTH_0; - break; - case DMARequestType_MemoryToPeripheral: - case DMARequestType_PeripheralToMemory: - // just enable incrementing of memory address (no FIFO, single burst) - cr_value |= DMA_SxCR_MINC; - break; - default: - WTF; - } - - prv_use_controller(this->stream->controller); - // make sure the stream is disabled before trying to configure - PBL_ASSERTN((this->stream->periph->CR & DMA_SxCR_EN) == 0); - this->stream->periph->CR = cr_value; - this->stream->periph->FCR = fcr_value; - prv_release_controller(this->stream->controller); - - // Configure and enable the IRQ if necessary (DMA interrupts enabled later) - if (this->irq_priority != IRQ_PRIORITY_INVALID) { - NVIC_SetPriority(this->stream->irq_channel, this->irq_priority); - NVIC_EnableIRQ(this->stream->irq_channel); - } -} - -void dma_request_init(DMARequest *this) { - if (this->state->initialized) { - return; - } - - // we only support 1 transfer per stream so assert that the stream isn't already initialized - PBL_ASSERTN(!this->stream->state->initialized); - // sanity check that the stream and controller are valid - switch ((uintptr_t)this->stream->periph) { - case (uintptr_t)DMA1_Stream0: - case (uintptr_t)DMA1_Stream1: - case (uintptr_t)DMA1_Stream2: - case (uintptr_t)DMA1_Stream3: - case (uintptr_t)DMA1_Stream4: - case (uintptr_t)DMA1_Stream5: - case (uintptr_t)DMA1_Stream6: - case (uintptr_t)DMA1_Stream7: - PBL_ASSERTN(this->stream->controller->periph == DMA1); - break; - case (uintptr_t)DMA2_Stream0: - case (uintptr_t)DMA2_Stream1: - case (uintptr_t)DMA2_Stream2: - case (uintptr_t)DMA2_Stream3: - case (uintptr_t)DMA2_Stream4: - case (uintptr_t)DMA2_Stream5: - case (uintptr_t)DMA2_Stream6: - case (uintptr_t)DMA2_Stream7: - PBL_ASSERTN(this->stream->controller->periph == DMA2); - break; - default: - WTF; - } - this->stream->state->initialized = true; - - prv_set_constant_config(this); - this->state->initialized = true; -} - - -// Transfer APIs -//////////////////////////////////////////////////////////////////////////////// - -static void prv_validate_memory(DMARequest *this, void *dst, const void *src, uint32_t length) { - if (mpu_memory_is_cachable(src)) { - // Flush the source buffer from cache so that SRAM has the correct data. - uintptr_t aligned_src = (uintptr_t)src; - size_t aligned_length = length; - dcache_align(&aligned_src, &aligned_length); - dcache_flush((const void *)aligned_src, aligned_length); - } - - const uint32_t alignment_mask = prv_get_data_size_bytes(this) - 1; - if (mpu_memory_is_cachable(dst)) { - // If a cache line within the dst gets evicted while we do the transfer, it'll corrupt SRAM, so - // just invalidate it now. - dcache_invalidate(dst, length); - // since the dst address is cachable, it needs to be aligned to a cache line and - // the length must be an even multiple of cache lines - const uint32_t dst_alignment_mask = dcache_alignment_mask_minimum(alignment_mask); - PBL_ASSERTN(((length & dst_alignment_mask) == 0) && - (((uintptr_t)dst & dst_alignment_mask) == 0) && - (((uintptr_t)src & alignment_mask) == 0)); - } else { - PBL_ASSERTN(((length & alignment_mask) == 0) && - (((uintptr_t)dst & alignment_mask) == 0) && - (((uintptr_t)src & alignment_mask) == 0)); - } - -#if PLATFORM_ROBERT - // There is an erratum in the STM32F7xx MCUs in which causes DMA transfers - // that read from the DTCM to read corrupted data if the MCU enters sleep mode - // during the transfer. Note that writes to DTCM will not be corrupted. - extern const char __DTCM_RAM_size__[]; - PBL_ASSERT((uintptr_t)src >= (RAMDTCM_BASE + (uintptr_t)__DTCM_RAM_size__) || - ((uintptr_t)src + length) <= RAMDTCM_BASE, - "DMA transfer will be corrupted if MCU enters sleep mode"); -#endif -} - -static void prv_request_start(DMARequest *this, void *dst, const void *src, uint32_t length, - DMARequestTransferType transfer_type) { - this->state->transfer_dst = dst; - this->state->transfer_length = length; - prv_validate_memory(this, dst, src, length); - prv_use_controller(this->stream->controller); - - // set the data length in terms of units (we assert that it's an even number in - // prv_validate_memory above) - this->stream->periph->NDTR = length / prv_get_data_size_bytes(this); - - // set the addresses - switch (this->type) { - case DMARequestType_MemoryToMemory: - case DMARequestType_PeripheralToMemory: - this->stream->periph->PAR = (uint32_t)src; - this->stream->periph->M0AR = (uint32_t)dst; - break; - case DMARequestType_MemoryToPeripheral: - this->stream->periph->PAR = (uint32_t)dst; - this->stream->periph->M0AR = (uint32_t)src; - break; - default: - WTF; - } - - // set the mode and enable the appropriate interrupts - switch (transfer_type) { - case DMARequestTransferType_Direct: - this->stream->periph->CR &= ~DMA_SxCR_CIRC; - this->stream->periph->CR |= DMA_SxCR_TCIE; - break; - case DMARequestTransferType_Circular: - this->stream->periph->CR |= DMA_SxCR_CIRC | DMA_SxCR_HTIE | DMA_SxCR_TCIE; - break; - case DMARequestTransferType_None: - default: - WTF; - } - - // "As a general recommendation, it is advised to clear all flags in the DMA_LIFCR and - // DMA_HIFCR registers before starting a new transfer." -- STM32 AN4031 (DM00046011.pdf) - // "Before setting EN bit to '1' to start a new transfer, the event flags corresponding to the - // stream in DMA_LISR or DMA_HISR register must be" -- Page 213, STM RM0402 - prv_clear_all_interrupt_flags(this); - - // Start the DMA transfer - this->stream->periph->CR |= DMA_SxCR_EN; -} - -void dma_request_start_direct(DMARequest *this, void *dst, const void *src, uint32_t length, - DMADirectRequestHandler handler, void *context) { - PBL_ASSERTN(this->state->initialized); - - PBL_ASSERTN(this->state->transfer_type == DMARequestTransferType_None); - this->state->transfer_type = DMARequestTransferType_Direct; - - this->state->direct_transfer_handler = handler; - this->state->context = context; - PBL_ASSERTN(!this->stream->state->current_request); - this->stream->state->current_request = this; - - prv_request_start(this, dst, src, length, DMARequestTransferType_Direct); -} - -void dma_request_start_circular(DMARequest *this, void *dst, const void *src, uint32_t length, - DMACircularRequestHandler handler, void *context) { - PBL_ASSERTN(this->state->initialized); - - PBL_ASSERTN(this->state->transfer_type == DMARequestTransferType_None); - this->state->transfer_type = DMARequestTransferType_Circular; - - this->state->circular_transfer_handler = handler; - this->state->context = context; - PBL_ASSERTN(!this->stream->state->current_request); - this->stream->state->current_request = this; - - // TODO: We don't currently support DMA'ing into a cachable region of memory (i.e. SRAM) for - // circular transfers. The reason is that it gets complicated because the consumer might be - // reading from the buffer at any time (as UART does), as opposed to direct transfers where the - // consumer is always reading only after the transfer has completed. - PBL_ASSERTN(!mpu_memory_is_cachable(dst)); - prv_request_start(this, dst, src, length, DMARequestTransferType_Circular); -} - -void dma_request_stop(DMARequest *this) { - if (this->state->transfer_type == DMARequestTransferType_None) { - return; - } - - this->stream->periph->CR &= ~(DMA_SxCR_HTIE | DMA_SxCR_TCIE); - - // disable the stream - this->stream->periph->CR &= ~DMA_SxCR_EN; - while ((this->stream->periph->CR & DMA_SxCR_EN) != 0) {} - prv_release_controller(this->stream->controller); - - // clean up our state - this->state->transfer_dst = NULL; - this->state->transfer_length = 0; - this->state->direct_transfer_handler = NULL; - this->state->circular_transfer_handler = NULL; - this->state->context = NULL; - this->state->transfer_type = DMARequestTransferType_None; - this->stream->state->current_request = NULL; -} - - -uint32_t dma_request_get_current_data_counter(DMARequest *this) { - return this->stream->periph->NDTR; -} - -bool dma_request_in_progress(DMARequest *this) { - return this->state->transfer_type != DMARequestTransferType_None; -} - -void dma_request_set_memory_increment_disabled(DMARequest *this, bool disabled) { - prv_use_controller(this->stream->controller); - if (disabled) { - this->stream->periph->CR &= ~DMA_SxCR_MINC; - } else { - this->stream->periph->CR |= DMA_SxCR_MINC; - } - prv_release_controller(this->stream->controller); -} - - -// ISR -//////////////////////////////////////////////////////////////////////////////// - -void dma_stream_irq_handler(DMAStream *stream) { - bool should_context_switch = false; - - DMARequest *this = stream->state->current_request; - PBL_ASSERTN(this); - PBL_ASSERTN(this->stream == stream); - - bool has_tc; - bool has_ht; - prv_get_and_clear_interrupt_flags(this, &has_tc, &has_ht); - if (!has_tc && !has_ht) { - // we shouldn't be here - portEND_SWITCHING_ISR(should_context_switch); - return; - } - - switch (this->state->transfer_type) { - case DMARequestTransferType_Direct: - if (has_tc) { - if (mpu_memory_is_cachable(this->state->transfer_dst)) { - dcache_invalidate(this->state->transfer_dst, this->state->transfer_length); - } - - // Automatically stop the transfer before calling the handler so that the handler can start - // another transfer immediately. We need to preserve the handler and context first. - DMADirectRequestHandler handler = this->state->direct_transfer_handler; - void *context = this->state->context; - dma_request_stop(this); - - if (handler && handler(this, context)) { - should_context_switch = true; - } - } - break; - case DMARequestTransferType_Circular: - if (this->state->circular_transfer_handler) { - if (this->state->circular_transfer_handler(this, this->state->context, has_tc)) { - should_context_switch = true; - } - } - break; - case DMARequestTransferType_None: - default: - WTF; - } - portEND_SWITCHING_ISR(should_context_switch); -} diff --git a/src/fw/drivers/stm32f2/dma_definitions.h b/src/fw/drivers/stm32f2/dma_definitions.h deleted file mode 100644 index 4c8832d5d7..0000000000 --- a/src/fw/drivers/stm32f2/dma_definitions.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" -#include "drivers/dma.h" -#include "kernel/util/stop.h" - -typedef struct DMAControllerState { - int refcount; -} DMAControllerState; - -typedef const struct DMAController { - DMAControllerState *state; - DMA_TypeDef *periph; - uint32_t rcc_bit; -} DMAController; - -typedef struct DMAStreamState { - bool initialized; - //! The current in-progress request (or NULL if not in progress) - DMARequest *current_request; -} DMAStreamState; - -typedef const struct DMAStream { - DMAStreamState *state; - DMAController *controller; - DMA_Stream_TypeDef *periph; - uint8_t irq_channel; -} DMAStream; - -typedef enum DMARequestPriority { - DMARequestPriority_Low = 0, - DMARequestPriority_Medium = DMA_SxCR_PL_0, - DMARequestPriority_High = DMA_SxCR_PL_1, - DMARequestPriority_VeryHigh = DMA_SxCR_PL_0 | DMA_SxCR_PL_1, -} DMARequestPriority; - -typedef enum DMARequestType { - //! Transfers from one memory buffer to another (essentially a memcpy) - DMARequestType_MemoryToMemory = DMA_SxCR_DIR_1, - //! Transfers from a peripheral's data register to a memory buffer - DMARequestType_PeripheralToMemory = 0, - //! Transfers from memory buffer to a peripheral's data buffer - DMARequestType_MemoryToPeripheral = DMA_SxCR_DIR_0, -} DMARequestType; - -typedef enum DMARequestDataSize { - DMARequestDataSize_Byte = 0, - DMARequestDataSize_HalfWord = DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0, - DMARequestDataSize_Word = DMA_SxCR_MSIZE_1 | DMA_SxCR_PSIZE_1, -} DMARequestDataSize; - -typedef enum DMARequestTransferType { - DMARequestTransferType_None = 0, - DMARequestTransferType_Direct, - DMARequestTransferType_Circular, -} DMARequestTransferType; - -typedef struct DMARequestState { - bool initialized; - //! The type of request currently in-progress - DMARequestTransferType transfer_type; - //! The destination and length of the current transfer - void *transfer_dst; - uint32_t transfer_length; - //! The handler for transfer events - union { - DMADirectRequestHandler direct_transfer_handler; - DMACircularRequestHandler circular_transfer_handler; - }; - void *context; -} DMARequestState; - -typedef const struct DMARequest { - DMARequestState *state; - DMAStream *stream; - uint32_t channel; - uint32_t irq_priority; - DMARequestPriority priority; - DMARequestType type; - DMARequestDataSize data_size; -} DMARequest; - - -void dma_stream_irq_handler(DMAStream *this); diff --git a/src/fw/drivers/stm32f2/exti.c b/src/fw/drivers/stm32f2/exti.c deleted file mode 100644 index d700883db0..0000000000 --- a/src/fw/drivers/stm32f2/exti.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/exti.h" - -#include "board/board.h" -#include "drivers/periph_config.h" -#include "kernel/events.h" -#include "mcu/interrupts.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include - -//! Tracks whether we've disabled interrupts as part of locking out other people from our EXTI -//! registers. -static bool s_exti_locked = false; - -//! Have we already configured the EXTI9_5_IRQn interrupt? -static bool s_9_5_nvic_configured = false; -//! Have we already configured the EXTI15_10_IRQn interrupt? -static bool s_15_10_nvic_configured = false; - -static ExtiHandlerCallback s_exti_handlers[16]; - -//! Convert a exti number (value 0 to 22) to one of the EXTI_LineX defines -static uint32_t prv_exti_line_to_bit(int exti_line) { - return 0x1 << exti_line; -} - -static void prv_lock(void) { - if (mcu_state_are_interrupts_enabled()) { - __disable_irq(); - s_exti_locked = true; - } -} - -static void prv_unlock(void) { - if (s_exti_locked) { - __enable_irq(); - s_exti_locked = false; - } -} - -static IRQn_Type prv_get_irq_enum(int exti_line) { - if (exti_line <= 4) { - return EXTI0_IRQn + exti_line; - } - if (exti_line <= 9) { - return EXTI9_5_IRQn; - } - if (exti_line <= 15) { - return EXTI15_10_IRQn; - } - if (exti_line == ExtiLineOther_RTCAlarm) { - return RTC_Alarm_IRQn; - } - if (exti_line == ExtiLineOther_RTCWakeup) { - return RTC_WKUP_IRQn; - } - WTF; -} - -static void prv_configure_nvic_channel(IRQn_Type irqn) { - NVIC_SetPriority(irqn, EXTI_PRIORITY); - NVIC_EnableIRQ(irqn); -} - -static void prv_check_nvic_channel(IRQn_Type irqn) { - // Make sure we haven't already set up the channel in question. - if (irqn == EXTI9_5_IRQn) { - if (s_9_5_nvic_configured) { - return; // Already configured - } - s_9_5_nvic_configured = true; - } else if (irqn == EXTI15_10_IRQn) { - if (s_15_10_nvic_configured) { - return; // Already configured - } - s_15_10_nvic_configured = true; - } - - prv_configure_nvic_channel(irqn); -} - -void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb) { - periph_config_acquire_lock(); - periph_config_enable(SYSCFG, RCC_APB2Periph_SYSCFG); - - // Select which GPIO to monitor - SYSCFG->EXTICR[cfg.exti_line >> 0x02] &= - ~(((uint32_t)0x0F) << (0x04 * (cfg.exti_line & (uint8_t)0x03))); - SYSCFG->EXTICR[cfg.exti_line >> 0x02] |= - (((uint32_t)cfg.exti_port_source) << (0x04 * (cfg.exti_line & (uint8_t)0x03))); - - periph_config_disable(SYSCFG, RCC_APB2Periph_SYSCFG); - periph_config_release_lock(); - - s_exti_handlers[cfg.exti_line] = cb; - - // Do the rest of the configuration - exti_configure_other(cfg.exti_line, trigger); -} - -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger) { - // Clear IT Pending bit - EXTI->PR = prv_exti_line_to_bit(exti_line); - - prv_lock(); - - switch (trigger) { - case ExtiTrigger_Rising: - EXTI->RTSR |= prv_exti_line_to_bit(exti_line); - EXTI->FTSR &= ~prv_exti_line_to_bit(exti_line); - break; - case ExtiTrigger_Falling: - EXTI->RTSR &= ~prv_exti_line_to_bit(exti_line); - EXTI->FTSR |= prv_exti_line_to_bit(exti_line); - break; - case ExtiTrigger_RisingFalling: - EXTI->RTSR |= prv_exti_line_to_bit(exti_line); - EXTI->FTSR |= prv_exti_line_to_bit(exti_line); - break; - } - - prv_unlock(); - - periph_config_acquire_lock(); - prv_check_nvic_channel(prv_get_irq_enum(exti_line)); - periph_config_release_lock(); -} - -void exti_enable_other(ExtiLineOther exti_line) { - prv_lock(); - - EXTI->IMR |= prv_exti_line_to_bit(exti_line); - - prv_unlock(); -} - -void exti_disable_other(ExtiLineOther exti_line) { - prv_lock(); - - uint32_t exti_bit = prv_exti_line_to_bit(exti_line); - - EXTI->IMR &= ~exti_bit; - EXTI->PR = exti_bit; - - // No need to disable the NVIC ISR. If all the EXTIs that feed a given shared ISR are disabled - // the ISR won't fire. - - prv_unlock(); -} - -void exti_set_pending(ExtiConfig cfg) { - IRQn_Type irq; - switch (cfg.exti_line) { - case 0: irq = EXTI0_IRQn; break; - case 1: irq = EXTI1_IRQn; break; - case 2: irq = EXTI2_IRQn; break; - case 3: irq = EXTI3_IRQn; break; - case 4: irq = EXTI4_IRQn; break; - case 5 ... 9: irq = EXTI9_5_IRQn; break; - case 10 ... 15: irq = EXTI15_10_IRQn; break; - default: - WTF; - } - NVIC_SetPendingIRQ(irq); -} - -void exti_clear_pending_other(ExtiLineOther exti_line) { - uint32_t exti_bit = prv_exti_line_to_bit(exti_line); - EXTI->PR = exti_bit; -} - -// Helper functions for handling ISRs -/////////////////////////////////////////////////////////////////////////////// - -static void prv_handle_exti(int exti_line) { - // Clear IT Pending bit - EXTI->PR = prv_exti_line_to_bit(exti_line); - - const ExtiHandlerCallback cb = s_exti_handlers[exti_line]; - if (cb) { - bool should_context_switch = false; - cb(&should_context_switch); - portEND_SWITCHING_ISR(should_context_switch); - } -} - -static void prv_check_handle_exti(int exti_line) { - if (EXTI->PR & prv_exti_line_to_bit(exti_line)) { - prv_handle_exti(exti_line); - } -} - - -// Actual ISR functions -/////////////////////////////////////////////////////////////////////////////// - -void EXTI0_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI0_IRQn); - prv_handle_exti(0); -} - -void EXTI1_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI1_IRQn); - prv_handle_exti(1); -} - -void EXTI2_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI2_IRQn); - prv_handle_exti(2); -} - -void EXTI3_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI3_IRQn); - prv_handle_exti(3); -} - -void EXTI4_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI4_IRQn); - prv_handle_exti(4); -} - -void EXTI9_5_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI9_5_IRQn); - for (int i = 5; i <= 9; ++i) { - prv_check_handle_exti(i); - } -} - -void EXTI15_10_IRQHandler(void) { - NVIC_ClearPendingIRQ(EXTI15_10_IRQn); - for (int i = 10; i <= 15; ++i) { - prv_check_handle_exti(i); - } -} - diff --git a/src/fw/drivers/stm32f2/gpio.c b/src/fw/drivers/stm32f2/gpio.c deleted file mode 100644 index b47922c3da..0000000000 --- a/src/fw/drivers/stm32f2/gpio.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" - -#include - -#define MAX_GPIO (9) - -#include "FreeRTOS.h" -#include "debug/power_tracking.h" - -static const PowerSystem s_gpio_to_power_system_map[MAX_GPIO] = { - [0] = PowerSystemMcuGpioA, - [1] = PowerSystemMcuGpioB, - [2] = PowerSystemMcuGpioC, - [3] = PowerSystemMcuGpioD, - [7] = PowerSystemMcuGpioH, -}; - -static void prv_init_common(const InputConfig *input_config, GPIO_InitTypeDef *gpio_init) { - gpio_use(input_config->gpio); - GPIO_Init(input_config->gpio, gpio_init); - gpio_release(input_config->gpio); -} - -static uint8_t s_gpio_clock_count[MAX_GPIO]; - -void gpio_use(GPIO_TypeDef* GPIOx) { - uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - portENTER_CRITICAL(); - if((idx < MAX_GPIO) && !(s_gpio_clock_count[idx]++)) { - SET_BIT(RCC->AHB1ENR, (0x1 << idx)); - power_tracking_start(s_gpio_to_power_system_map[idx]); - } - portEXIT_CRITICAL(); -} - -void gpio_release(GPIO_TypeDef* GPIOx) { - uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400); - portENTER_CRITICAL(); - if((idx < MAX_GPIO) && s_gpio_clock_count[idx] && !(--s_gpio_clock_count[idx])) { - CLEAR_BIT(RCC->AHB1ENR, (0x1 << idx)); - power_tracking_stop(s_gpio_to_power_system_map[idx]); - } - portEXIT_CRITICAL(); -} - -void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed) { - GPIO_InitTypeDef init = { - .GPIO_Pin = pin_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(pin_config->gpio); - GPIO_Init(pin_config->gpio, &init); - gpio_release(pin_config->gpio); -} - -void gpio_output_set(const OutputConfig *pin_config, bool asserted) { - if (!pin_config->active_high) { - asserted = !asserted; - } - gpio_use(pin_config->gpio); - GPIO_WriteBit(pin_config->gpio, pin_config->gpio_pin, - asserted? Bit_SET : Bit_RESET); - gpio_release(pin_config->gpio); -} - -void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype, - GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AF, - .GPIO_Speed = speed, - .GPIO_OType = otype, - .GPIO_PuPd = pupd - }; - - gpio_use(af_config->gpio); - GPIO_PinAFConfig(af_config->gpio, af_config->gpio_pin_source, - af_config->gpio_af); - GPIO_Init(af_config->gpio, &init); - gpio_release(af_config->gpio); -} - -void gpio_af_configure_low_power(const AfConfig *af_config) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AN, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(af_config->gpio); - GPIO_Init(af_config->gpio, &init); - gpio_release(af_config->gpio); -} - -void gpio_af_configure_fixed_output(const AfConfig *af_config, bool asserted) { - GPIO_InitTypeDef init = { - .GPIO_Pin = af_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_OUT, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_OType = GPIO_OType_PP, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - gpio_use(af_config->gpio); - GPIO_Init(af_config->gpio, &init); - GPIO_WriteBit(af_config->gpio, af_config->gpio_pin, - asserted? Bit_SET : Bit_RESET); - gpio_release(af_config->gpio); -} - -void gpio_input_init(const InputConfig *input_config) { - if (input_config->gpio == NULL) { - // TODO: PBL-15063 - return; - } - - gpio_input_init_pull_up_down(input_config, GPIO_PuPd_NOPULL); -} - -void gpio_input_init_pull_up_down(const InputConfig *input_config, GPIOPuPd_TypeDef pupd) { - GPIO_InitTypeDef gpio_init = { - .GPIO_Mode = GPIO_Mode_IN, - .GPIO_PuPd = pupd, - .GPIO_Pin = input_config->gpio_pin - }; - - prv_init_common(input_config, &gpio_init); -} - -bool gpio_input_read(const InputConfig *input_config) { - gpio_use(input_config->gpio); - uint8_t bit = GPIO_ReadInputDataBit(input_config->gpio, input_config->gpio_pin); - gpio_release(input_config->gpio); - - return bit != 0; -} - -void gpio_analog_init(const InputConfig *input_config) { - GPIO_InitTypeDef gpio_init = { - .GPIO_Pin = input_config->gpio_pin, - .GPIO_Mode = GPIO_Mode_AN, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - prv_init_common(input_config, &gpio_init); -} diff --git a/src/fw/drivers/stm32f2/gpio_defaults.c b/src/fw/drivers/stm32f2/gpio_defaults.c deleted file mode 100644 index cd2799211f..0000000000 --- a/src/fw/drivers/stm32f2/gpio_defaults.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -void gpio_init_all(void) { - // TODO - PBL-15063 -} diff --git a/src/fw/drivers/stm32f2/i2c_hal.c b/src/fw/drivers/stm32f2/i2c_hal.c deleted file mode 100644 index e7581d4db8..0000000000 --- a/src/fw/drivers/stm32f2/i2c_hal.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/i2c_hal.h" -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f2/i2c_hal_definitions.h" -#include "system/passert.h" - -#include "drivers/periph_config.h" -#include "FreeRTOS.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -#define I2C_IRQ_PRIORITY (0xc) -#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000) -#define I2C_READ_WRITE_BIT (0x01) - - -static uint32_t s_guard_events[] = { - I2C_EVENT_MASTER_MODE_SELECT, - I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, - I2C_EVENT_MASTER_BYTE_TRANSMITTED, - I2C_EVENT_MASTER_MODE_SELECT, - I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, - I2C_EVENT_MASTER_BYTE_RECEIVED, - I2C_EVENT_MASTER_BYTE_TRANSMITTING, - I2C_EVENT_MASTER_BYTE_TRANSMITTED, -}; - -void i2c_hal_init(I2CBus *bus) { - NVIC_SetPriority(bus->hal->ev_irq_channel, I2C_IRQ_PRIORITY); - NVIC_SetPriority(bus->hal->er_irq_channel, I2C_IRQ_PRIORITY); - NVIC_EnableIRQ(bus->hal->ev_irq_channel); - NVIC_EnableIRQ(bus->hal->er_irq_channel); - I2C_DeInit(bus->hal->i2c); -} - -static uint32_t prv_get_apb1_frequency(void) { - RCC_ClocksTypeDef rcc_clocks; - RCC_GetClocksFreq(&rcc_clocks); - return rcc_clocks.PCLK1_Frequency; -} - -static const int DUTY_CYCLE_DIVIDERS[] = { - [I2CDutyCycle_16_9] = 25, - [I2CDutyCycle_2] = 3 -}; - -static uint32_t prv_prescalar_to_frequency(I2CDutyCycle duty_cycle, uint32_t prescalar) { - const uint32_t pclk1 = prv_get_apb1_frequency(); - return pclk1 / (prescalar * DUTY_CYCLE_DIVIDERS[duty_cycle]); -} - -//! @return A prescalar that will result in a frequency that's close to but not greater than -//! desired_maximum_frequency. -static uint32_t prv_frequency_to_prescalar(I2CDutyCycle duty_cycle, - uint32_t desired_maximum_frequency) { - const uint32_t pclk1 = prv_get_apb1_frequency(); - - uint32_t prescalar = - pclk1 / (desired_maximum_frequency * DUTY_CYCLE_DIVIDERS[duty_cycle]); - - // Check to see what frequency our calculated prescalar is actually going to give us. If the - // numbers don't divide evenly, that means we'll have a prescalar that's too low, and will end - // up giving us a speed that's faster than we wanted. Add one to the prescalar in this case - // to make sure we stay within spec. - const uint32_t remainder = - pclk1 % (desired_maximum_frequency * DUTY_CYCLE_DIVIDERS[duty_cycle]); - if (remainder != 0) { - prescalar += 1; - } - - return prescalar; -} - -void i2c_hal_enable(I2CBus *bus) { - periph_config_enable(bus->hal->i2c, bus->hal->clock_ctrl); - - I2C_InitTypeDef I2C_init_struct; - I2C_StructInit(&I2C_init_struct); - - if (bus->hal->clock_speed > I2C_NORMAL_MODE_CLOCK_SPEED_MAX) { // Fast mode - switch (bus->hal->duty_cycle) { - case I2CDutyCycle_16_9: - I2C_init_struct.I2C_DutyCycle = I2C_DutyCycle_16_9; - break; - case I2CDutyCycle_2: - I2C_init_struct.I2C_DutyCycle = I2C_DutyCycle_2; - break; - default: - WTF; - } - } - - // Calculate the prescalar we're going to end up using to get as close as possible to - // bus->hal->clock_speed without going over. - const uint32_t prescalar = prv_frequency_to_prescalar(bus->hal->duty_cycle, - bus->hal->clock_speed); - - // Convert it back to a frequency since the I2C_Init function wants a frequency, not a raw - // prescalar value. - const uint32_t frequency = prv_prescalar_to_frequency(bus->hal->duty_cycle, - prescalar); - I2C_init_struct.I2C_ClockSpeed = frequency; - - I2C_init_struct.I2C_Ack = I2C_Ack_Enable; - - I2C_Init(bus->hal->i2c, &I2C_init_struct); - I2C_Cmd(bus->hal->i2c, ENABLE); -} - -void i2c_hal_disable(I2CBus *bus) { - periph_config_disable(bus->hal->i2c, bus->hal->clock_ctrl); - I2C_DeInit(bus->hal->i2c); -} - -bool i2c_hal_is_busy(I2CBus *bus) { - return ((bus->hal->i2c->SR2 & I2C_SR2_BUSY) != 0); -} - -void prv_disable_all_interrupts(I2CBus *bus) { - bus->hal->i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | I2C_CR2_ITBUFEN); -} - -void i2c_hal_abort_transfer(I2CBus *bus) { - // Disable all interrupts on the bus - prv_disable_all_interrupts(bus); - // Generate a stop condition - bus->hal->i2c->CR1 |= I2C_CR1_STOP; -} - -void i2c_hal_init_transfer(I2CBus *bus) { - // Enable Acks - bus->hal->i2c->CR1 |= I2C_CR1_ACK; - bus->state->transfer.state = I2CTransferState_WriteAddressTx; -} - -void i2c_hal_start_transfer(I2CBus *bus) { - // Generate start event - bus->hal->i2c->CR1 |= I2C_CR1_START; - // Enable event and error interrupts - bus->hal->i2c->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN; -} - -/*------------------------INTERRUPT FUNCTIONS--------------------------*/ - -//! End a transfer and disable further interrupts -//! Only call from interrupt functions -static portBASE_TYPE prv_end_transfer_irq(I2CBus *bus, bool result) { - prv_disable_all_interrupts(bus); - - // Generate stop condition - bus->hal->i2c->CR1 |= I2C_CR1_STOP; - bus->state->transfer.state = I2CTransferState_Complete; - - I2CTransferEvent event = result ? I2CTransferEvent_TransferComplete : I2CTransferEvent_Error; - return i2c_handle_transfer_event(bus, event); -} - -//! Pause a transfer, disabling interrupts during the pause -//! Only call from interrupt functions -static portBASE_TYPE prv_pause_transfer_irq(I2CBus *bus) { - prv_disable_all_interrupts(bus); - return i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived); -} - -//! Handle an IRQ event on the specified \a bus -static portBASE_TYPE prv_event_irq_handler(I2CBus *bus) { - I2C_TypeDef *i2c = bus->hal->i2c; - I2CTransfer *transfer = &bus->state->transfer; - - if (transfer->state == I2CTransferState_Complete) { - // Disable interrupts if spurious interrupt received - i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN); - return pdFALSE; - } - - // Check that the expected event occurred - if (I2C_CheckEvent(i2c, s_guard_events[transfer->state]) == ERROR) { - // Ignore interrupt - A spurious byte transmitted event as well as an interrupt with no - // discernible event associated with it occur after repeat start events are generated - return pdFALSE; - } - portBASE_TYPE should_context_switch = pdFALSE; - - switch (transfer->state) { - case I2CTransferState_WriteAddressTx: - if (transfer->type == I2CTransferType_SendRegisterAddress) { - // Write the I2C bus address to select it in write mode. - i2c->DR = transfer->device_address & ~I2C_READ_WRITE_BIT; - transfer->state = I2CTransferState_WriteRegAddress; - } else { - if (transfer->direction == I2CTransferDirection_Read) { - // Write the I2C bus address to select it in read mode. - i2c->DR = transfer->device_address | I2C_READ_WRITE_BIT; - transfer->state = I2CTransferState_WaitForData; - } else { - // Write the I2C bus address to select it in write mode. - i2c->DR = transfer->device_address & ~I2C_READ_WRITE_BIT; - transfer->state = I2CTransferState_WriteData; - } - } - break; - - case I2CTransferState_WriteRegAddress: - // Write the register address - i2c->DR = transfer->register_address; - - if (transfer->direction == I2CTransferDirection_Read) { - transfer->state = I2CTransferState_RepeatStart; - } else { - // Enable TXE interrupt for writing - i2c->CR2 |= I2C_CR2_ITBUFEN; - transfer->state = I2CTransferState_WriteData; - } - break; - - case I2CTransferState_RepeatStart: - // Generate a repeat start - i2c->CR1 |= I2C_CR1_START; - transfer->state = I2CTransferState_WriteAddressRx; - break; - - case I2CTransferState_WriteAddressRx: - // Write the I2C bus address again, but this time in read mode. - i2c->DR = transfer->device_address | I2C_READ_WRITE_BIT; - if (transfer->size == 1) { - // Last byte, we want to NACK this one to tell the slave to stop sending us bytes. - i2c->CR1 &= ~I2C_CR1_ACK; - } - transfer->state = I2CTransferState_WaitForData; - break; - - case I2CTransferState_WaitForData: - // This state just ensures that the transition to receive mode event happened - - // Enable RXNE interrupt for writing - i2c->CR2 |= I2C_CR2_ITBUFEN; - transfer->state = I2CTransferState_ReadData; - break; - - case I2CTransferState_ReadData: - transfer->data[transfer->idx] = i2c->DR; - transfer->idx++; - - if (transfer->idx + 1 == transfer->size) { - // Last byte, we want to NACK this one to tell the slave to stop sending us bytes. - i2c->CR1 &= ~I2C_CR1_ACK; - } - else if (transfer->idx == transfer->size) { - // End transfer after all bytes have been received - i2c->CR2 &= ~I2C_CR2_ITBUFEN; - should_context_switch = prv_end_transfer_irq(bus, true); - break; - } - - break; - - case I2CTransferState_WriteData: - i2c->DR = transfer->data[transfer->idx]; - transfer->idx++; - if (transfer->idx == transfer->size) { - i2c->CR2 &= ~I2C_CR2_ITBUFEN; - transfer->state = I2CTransferState_EndWrite; - break; - } - break; - - case I2CTransferState_EndWrite: - // End transfer after all bytes have been sent - should_context_switch = prv_end_transfer_irq(bus, true); - break; - - default: - // Should never reach here (state machine logic broken) - WTF; - break; - } - - return should_context_switch; -} - -//! Handle error interrupt on the specified \a bus -static portBASE_TYPE prv_error_irq_handler(I2CBus *bus) { - I2C_TypeDef *i2c = bus->hal->i2c; - I2CTransfer *transfer = &bus->state->transfer; - - if (transfer->state == I2CTransferState_Complete) { - // Disable interrupts if spurious interrupt received - i2c->CR2 &= ~I2C_CR2_ITERREN; - return pdFALSE; - } - - // Data overrun and bus errors can only really be handled by terminating the transfer and - // trying to recover the bus to an idle state. Each error will be logged. In each case a stop - // condition will be sent and then we will wait on the busy flag to clear (if it doesn't, - // a soft reset of the bus will be performed (handled in wait I2C_do_transfer). - - if ((i2c->SR1 & I2C_SR1_OVR) != 0) { - // Data overrun - i2c->SR1 &= ~I2C_SR1_OVR; - - I2C_DEBUG("Data overrun during I2C transaction; Bus: %s", bus->name); - } - if ((i2c->SR1 & I2C_SR1_BERR) != 0) { - i2c->SR1 &= ~I2C_SR1_BERR; - - // Bus error: invalid start or stop condition detected - I2C_DEBUG("Bus error detected during I2C transaction; Bus: %s", bus->name); - } - if ((i2c->SR1 & I2C_SR1_AF) != 0) { - i2c->SR1 &= ~I2C_SR1_AF; - - // NACK received. - // - // The MFI chip will cause NACK errors during read operations after writing a start bit (first - // start or repeat start indicating that it is busy. The transfer must be paused and the start - // condition sent again after a delay and the state machine set back a step. - // - // If the NACK is received after any other action log an error and abort the transfer - - if (transfer->state == I2CTransferState_WaitForData) { - transfer->state = I2CTransferState_WriteAddressRx; - return prv_pause_transfer_irq(bus); - } - else if (transfer->state == I2CTransferState_WriteRegAddress) { - transfer->state = I2CTransferState_WriteAddressTx; - return prv_pause_transfer_irq(bus); - } - else { - I2C_DEBUG("NACK received during I2C transfer; Bus: %s", bus->name); - } - } - - return prv_end_transfer_irq(bus, false); -} - -void i2c_hal_event_irq_handler(I2CBus *bus) { - portEND_SWITCHING_ISR(prv_event_irq_handler(bus)); -} - -void i2c_hal_error_irq_handler(I2CBus *bus) { - portEND_SWITCHING_ISR(prv_error_irq_handler(bus)); -} diff --git a/src/fw/drivers/stm32f2/i2c_hal_definitions.h b/src/fw/drivers/stm32f2/i2c_hal_definitions.h deleted file mode 100644 index 3143f2a557..0000000000 --- a/src/fw/drivers/stm32f2/i2c_hal_definitions.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum I2CDutyCycle { - I2CDutyCycle_16_9, - I2CDutyCycle_2 -} I2CDutyCycle; - -typedef struct I2CBusHal { - I2C_TypeDef *const i2c; - uint32_t clock_ctrl; ///< Peripheral clock control flag - uint32_t clock_speed; ///< Bus clock speed - I2CDutyCycle duty_cycle; ///< Bus clock duty cycle in fast mode - IRQn_Type ev_irq_channel; ///< I2C Event interrupt (One of X_IRQn). For example, I2C1_EV_IRQn. - IRQn_Type er_irq_channel; ///< I2C Error interrupt (One of X_IRQn). For example, I2C1_ER_IRQn. -} I2CBusHal; - -void i2c_hal_event_irq_handler(I2CBus *device); -void i2c_hal_error_irq_handler(I2CBus *device); diff --git a/src/fw/drivers/stm32f2/mcu.c b/src/fw/drivers/stm32f2/mcu.c deleted file mode 100644 index eb7a7248e7..0000000000 --- a/src/fw/drivers/stm32f2/mcu.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/mcu.h" -#include - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#if MICRO_FAMILY_STM32F7 -static const uint8_t STM32_UNIQUE_DEVICE_ID_ADDR[] = {0x1f, 0xf0, 0xf4, 0x20}; -#else -static const uint8_t STM32_UNIQUE_DEVICE_ID_ADDR[] = {0x1f, 0xff, 0x7a, 0x10}; -#endif - -StatusCode mcu_get_serial(void *buf, size_t *buf_sz) { - if (*buf_sz < sizeof(STM32_UNIQUE_DEVICE_ID_ADDR)) { - return E_OUT_OF_MEMORY; - } - - memcpy(buf, STM32_UNIQUE_DEVICE_ID_ADDR, sizeof(STM32_UNIQUE_DEVICE_ID_ADDR)); - *buf_sz = sizeof(STM32_UNIQUE_DEVICE_ID_ADDR); - - return S_SUCCESS; -} - -uint32_t mcu_cycles_to_milliseconds(uint64_t cpu_ticks) { - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - return ((cpu_ticks * 1000) / clocks.HCLK_Frequency); -} diff --git a/src/fw/drivers/stm32f2/otp.c b/src/fw/drivers/stm32f2/otp.c deleted file mode 100644 index f7ca6cb591..0000000000 --- a/src/fw/drivers/stm32f2/otp.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/otp.h" - -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include -#include -#include -#include -#include - -#if defined(MICRO_FAMILY_STM32F7) -// See page 83 of STM Reference Manual RM0410: -# define OTP_SLOTS_BASE_ADDR (0x1FF0F000) -# define OTP_LOCKS_BASE_ADDR (0x1FF0F400) -# define OTP_SLOT_SIZE_BYTES (64) -#else -// See page 53 of STM Reference Manual RM0033: -# define OTP_SLOTS_BASE_ADDR (0x1FFF7800) -# define OTP_LOCKS_BASE_ADDR (0x1FFF7A00) -# define OTP_SLOT_SIZE_BYTES (32) -#endif - -char * otp_get_slot(const uint8_t index) { - PBL_ASSERTN(index < NUM_OTP_SLOTS); - return (char * const) (OTP_SLOTS_BASE_ADDR + (OTP_SLOT_SIZE_BYTES * index)); -} - -uint8_t * otp_get_lock(const uint8_t index) { - PBL_ASSERTN(index < NUM_OTP_SLOTS); - return (uint8_t * const) (OTP_LOCKS_BASE_ADDR + index); -} - -bool otp_is_locked(const uint8_t index) { - return (*otp_get_lock(index) == 0); -} - -OtpWriteResult otp_write_slot(const uint8_t index, const char *value) { - if (otp_is_locked(index)) { - return OtpWriteFailAlreadyWritten; - } - char * const field = otp_get_slot(index); - uint8_t * const lock = otp_get_lock(index); - - FLASH_Unlock(); - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); - bool failed = false; // Because it's OTP we need to keep trying - // and report failure afterwards for the whole operation - for(size_t i = 0; i < strlen(value) + 1; i++) { - if (FLASH_ProgramByte((uint32_t)&field[i], value[i]) != FLASH_COMPLETE) { - failed = true; - } - } - // Lock the OTP sector - if (FLASH_ProgramByte((uint32_t)lock, 0) != FLASH_COMPLETE) { - failed = true; - } - - FLASH_Lock(); - - if(failed) { - return OtpWriteFailCorrupt; - } else { - return OtpWriteSuccess; - } -} diff --git a/src/fw/drivers/stm32f2/periph_config.c b/src/fw/drivers/stm32f2/periph_config.c deleted file mode 100644 index dce294bb38..0000000000 --- a/src/fw/drivers/stm32f2/periph_config.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/periph_config.h" -#include "os/mutex.h" -#include "system/logging.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include "FreeRTOS.h" - -#define PERIPH_CONFIG_DEBUG 0 - -#if PERIPH_CONFIG_DEBUG -#define PERIPH_CONFIG_LOG(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) -#else -#define PERIPH_CONFIG_LOG(fmt, args...) -#endif - -typedef void (*ClockCmd)(uint32_t periph, FunctionalState state); - -static PebbleMutex * s_periph_config_mutex; - -#if PERIPH_CONFIG_DEBUG -static const char *prv_string_for_cmd(ClockCmd cmd) { - if (cmd == RCC_APB1PeriphClockCmd) { - return "APB1"; - } else if (cmd == RCC_APB2PeriphClockCmd) { - return "APB2"; - } else if (cmd == RCC_AHB1PeriphClockCmd) { - return "AHB1"; - } else if (cmd == RCC_AHB2PeriphClockCmd) { - return "AHB2"; - } else { - return NULL; - } -} -#endif - -// F(S)MC is the only AHB3 peripheral -#ifdef FMC_R_BASE -#define AHB3_BASE FMC_R_BASE -#else -#define AHB3_BASE FSMC_R_BASE -#endif - -_Static_assert(APB1PERIPH_BASE < APB2PERIPH_BASE, "Clock mapping assumptions don't hold"); -_Static_assert(APB2PERIPH_BASE < AHB1PERIPH_BASE, "Clock mapping assumptions don't hold"); -_Static_assert(AHB1PERIPH_BASE < AHB2PERIPH_BASE, "Clock mapping assumptions don't hold"); -_Static_assert(AHB2PERIPH_BASE < AHB3_BASE, "Clock mapping assumptions don't hold"); - -// Note: this works only with peripheral (<...>Typedef_t *) defines, not with RCC defines -static ClockCmd prv_get_clock_cmd(uintptr_t periph_addr) { - PBL_ASSERTN(periph_addr >= APB1PERIPH_BASE); - if (periph_addr < APB2PERIPH_BASE) { - return RCC_APB1PeriphClockCmd; - } else if (periph_addr < AHB1PERIPH_BASE) { - return RCC_APB2PeriphClockCmd; - } else if (periph_addr < AHB2PERIPH_BASE) { - return RCC_AHB1PeriphClockCmd; - } else if (periph_addr < AHB3_BASE) { - return RCC_AHB2PeriphClockCmd; - } else { - return RCC_AHB3PeriphClockCmd; - } -} - -void periph_config_init(void) { - s_periph_config_mutex = mutex_create(); -} - -void periph_config_acquire_lock(void) { - mutex_lock(s_periph_config_mutex); -} - -void periph_config_release_lock(void) { - mutex_unlock(s_periph_config_mutex); -} - -void periph_config_enable(void *periph, uint32_t rcc_bit) { - ClockCmd clock_cmd = prv_get_clock_cmd((uintptr_t)periph); -#if PERIPH_CONFIG_DEBUG - if (prv_string_for_cmd(clock_cmd)) - PERIPH_CONFIG_LOG("Enabling clock %s", prv_string_for_cmd(clock_cmd)); -#endif - portENTER_CRITICAL(); - clock_cmd(rcc_bit, ENABLE); - portEXIT_CRITICAL(); -} - -void periph_config_disable(void *periph, uint32_t rcc_bit) { - ClockCmd clock_cmd = prv_get_clock_cmd((uintptr_t)periph); -#if PERIPH_CONFIG_DEBUG - if (prv_string_for_cmd(clock_cmd)) - PERIPH_CONFIG_LOG("Disabling clock %s", prv_string_for_cmd(clock_cmd)); -#endif - portENTER_CRITICAL(); - clock_cmd(rcc_bit, DISABLE); - portEXIT_CRITICAL(); -} diff --git a/src/fw/drivers/stm32f2/pwm.c b/src/fw/drivers/stm32f2/pwm.c deleted file mode 100644 index d3f013a100..0000000000 --- a/src/fw/drivers/stm32f2/pwm.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pwm.h" -#include "drivers/timer.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { - periph_config_enable(pwm->timer.peripheral, pwm->timer.config_clock); - - // Initialize PWM Timer - TIM_TimeBaseInitTypeDef tim_config; - TIM_TimeBaseStructInit(&tim_config); - tim_config.TIM_Period = resolution - 1; - tim_config.TIM_Prescaler = timer_find_prescaler(&pwm->timer, frequency); - tim_config.TIM_CounterMode = TIM_CounterMode_Up; - tim_config.TIM_ClockDivision = 0; - TIM_TimeBaseInit(pwm->timer.peripheral, &tim_config); - - // PWM Mode configuration - TIM_OCInitTypeDef tim_oc_init; - TIM_OCStructInit(&tim_oc_init); - tim_oc_init.TIM_OCMode = TIM_OCMode_PWM1; - tim_oc_init.TIM_OutputState = TIM_OutputState_Enable; - tim_oc_init.TIM_Pulse = 0; - tim_oc_init.TIM_OCPolarity = TIM_OCPolarity_High; - pwm->timer.init(pwm->timer.peripheral, &tim_oc_init); - - pwm->timer.preload(pwm->timer.peripheral, TIM_OCPreload_Enable); - TIM_ARRPreloadConfig(pwm->timer.peripheral, ENABLE); - - periph_config_disable(pwm->timer.peripheral, pwm->timer.config_clock); -} - -void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { - TIM_OCInitTypeDef tim_oc_init; - TIM_OCStructInit(&tim_oc_init); - tim_oc_init.TIM_OCMode = TIM_OCMode_PWM1; - tim_oc_init.TIM_OutputState = TIM_OutputState_Enable; - tim_oc_init.TIM_Pulse = duty_cycle; - tim_oc_init.TIM_OCPolarity = TIM_OCPolarity_High; - pwm->timer.init(pwm->timer.peripheral, &tim_oc_init); -} - -void pwm_enable(const PwmConfig *pwm, bool enable) { - if (enable) { - gpio_af_init(&pwm->afcfg, GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_DOWN); - periph_config_enable(pwm->timer.peripheral, pwm->timer.config_clock); - } else { - periph_config_disable(pwm->timer.peripheral, pwm->timer.config_clock); - gpio_output_init(&pwm->output, GPIO_OType_PP, GPIO_Speed_100MHz); - gpio_output_set(&pwm->output, false /* force low */); - } - - const FunctionalState state = (enable) ? ENABLE : DISABLE; - TIM_Cmd(pwm->timer.peripheral, state); -} diff --git a/src/fw/drivers/stm32f2/pwr.c b/src/fw/drivers/stm32f2/pwr.c deleted file mode 100644 index 1dd2591044..0000000000 --- a/src/fw/drivers/stm32f2/pwr.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/pwr.h" - -#include "drivers/periph_config.h" - -#define STM32F2_COMPATIBLE -#include - -void pwr_enable_wakeup(bool enable) { - PWR_WakeUpPinCmd(enable ? ENABLE : DISABLE); -} - -void pwr_flash_power_down_stop_mode(bool power_down) { - PWR_FlashPowerDownCmd(power_down ? ENABLE : DISABLE); -} - -void pwr_access_backup_domain(bool enable_access) { - periph_config_enable(PWR, RCC_APB1Periph_PWR); - PWR_BackupAccessCmd(enable_access ? ENABLE : DISABLE); - periph_config_disable(PWR, RCC_APB1Periph_PWR); -} diff --git a/src/fw/drivers/stm32f2/rng.c b/src/fw/drivers/stm32f2/rng.c deleted file mode 100644 index 8b675bfca8..0000000000 --- a/src/fw/drivers/stm32f2/rng.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/rng.h" - -#include "drivers/periph_config.h" -#include "system/passert.h" -#include "kernel/util/sleep.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - - -bool rng_rand(uint32_t *rand_out) { -#ifdef TARGET_QEMU - return false; -#endif - PBL_ASSERTN(rand_out); - - bool success = false; - // maximum number of seed errors we allow before giving up: - uint8_t attempts_left = 3; - uint8_t non_equal_count = 0; - uint32_t previous_value = 0; - - periph_config_acquire_lock(); - periph_config_enable(RNG, RCC_AHB2Periph_RNG); - RNG->CR |= RNG_CR_RNGEN; - - while (true) { - // Poll the status register's ready bit: - while (attempts_left) { - const uint32_t status = RNG->SR; - // Check clock flags, they would indicate programmer error. - PBL_ASSERTN((status & (RNG_SR_CECS | RNG_SR_CEIS)) == 0); - - // First check the seed error bits: - // We're checking both the interrupt flag and status flag, it's not very clear from the docs - // what the right thing to do is. - if (status & (RNG_SR_SECS | RNG_SR_SEIS)) { - // When there is a seed error, ST recommends clearing SEI, - // then disabling / re-enabling the peripheral: - RNG->SR &= ~RNG_SR_SEIS; - RNG->CR &= ~RNG_CR_RNGEN; - RNG->CR |= RNG_CR_RNGEN; - - non_equal_count = 0; - previous_value = 0; - --attempts_left; - continue; - } - if (status & RNG_SR_DRDY) { - break; // The next random number is ready - } - } - - if (!attempts_left) { - break; - } - - // As per Cory's and the ST reference manual's suggestion: "As required by the FIPS PUB - // (Federal Information Processing Standard Publication) 140-2, the first random number - // generated after setting the RNGEN bit should not be used, but saved for comparison with the - // next generated random number. Each subsequent generated random number has to be compared with - // the previously generated number. The test fails if any two compared numbers are equal - // (continuous random number generator test)." - *rand_out = RNG->DR; - if (*rand_out != previous_value) { - ++non_equal_count; - if (non_equal_count >= 2) { - success = true; - break; - } - } - previous_value = *rand_out; - } - - RNG->CR &= ~RNG_CR_RNGEN; - periph_config_disable(RNG, RCC_AHB2Periph_RNG); - periph_config_release_lock(); - return success; -} diff --git a/src/fw/drivers/stm32f2/rtc.c b/src/fw/drivers/stm32f2/rtc.c deleted file mode 100644 index ccbaaa0bb9..0000000000 --- a/src/fw/drivers/stm32f2/rtc.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/rtc.h" -#include "drivers/rtc_private.h" -#include "drivers/stm32f2/rtc_calibration.h" - -#include "console/dbgserial.h" - -#include "drivers/exti.h" -#include "drivers/periph_config.h" -#include "drivers/watchdog.h" - -#include "kernel/util/stop.h" -#include "mcu/interrupts.h" - -#include "services/common/regular_timer.h" - -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" - -#include "util/time/time.h" - -#define STM32F2_COMPATIBLE -#include - -#include -#include -#include - -typedef uint32_t RtcIntervalTicks; - -static const unsigned int LSE_FREQUENCY_HZ = 32768; -#define SECONDS_IN_A_DAY (60 * 60 * 24) -#define TICKS_IN_AN_INTERVAL SECONDS_IN_A_DAY - -//! This variable is a UNIX timestamp of what the current wall clock time was at tick s_time_tick_base. -static time_t s_time_base = 0; -//! This variable is the tick where the wall clock time was equal to s_time_base. If you subtract this variable -//! from the current tick count, you'll get the number of ticks that have elapsed since s_time_base, which will -//! allow you to calculate the current wall clock time. Note that this value may be negative on startup, see -//! restore_rtc_time_state -static int64_t s_time_tick_base = 0; - -//! The value of the RTC registers last time we checked them. -static RtcIntervalTicks s_last_ticks = 0; -//! This value is added to the current value of the RTC ticks to get the number -//! of ticks since system start. Incremented whenever we detect a rollover. -static RtcTicks s_coarse_ticks = 1; - -//! The time that we last set the alarm at. See rtc_alarm_set and rtc_alarm_get_elapsed_ticks. -static RtcTicks s_alarm_set_time = 0; - -static bool s_tick_alarm_initialized = false; - -static void save_rtc_time_state(RtcIntervalTicks current_rtc_ticks); - -void rtc_calibrate_frequency(uint32_t frequency) { - RTCCalibConfig config = rtc_calibration_get_config(frequency, LSE_FREQUENCY_HZ * 1000); - - PBL_LOG(LOG_LEVEL_DEBUG, "Calibrating RTC by %s%"PRIu32" units", - (config.sign == RTC_CalibSign_Positive) ? "+" : "-", config.units); - - // This is a no-op if RTC_CALIBRATION_TESTING is undefined. - rtc_calibration_init_timer(); - - RTC_CoarseCalibConfig(config.sign, config.units); - RTC_CoarseCalibCmd(ENABLE); -} - -//! Our RTC tick counter actually overflows once every 86 seconds. If we don't call rtc_get_ticks() every 86 seconds, -//! the counter may roll over multiple times, causing our clock to appear to have gaps. This repeating callback allows -//! us to make sure this doesn't happen. -static void rtc_resync_timer_callback() { - rtc_get_ticks(); -} - -static uint8_t BcdToByte(uint8_t Value) { - const uint8_t tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10; - return (tmp + (Value & (uint8_t)0x0F)); -} - -static RtcIntervalTicks get_rtc_interval_ticks(void) { - uint32_t time_register = RTC->TR; - - const uint8_t hours = BcdToByte((time_register & (RTC_TR_HT | RTC_TR_HU)) >> 16); - const uint8_t minutes = BcdToByte((time_register & (RTC_TR_MNT | RTC_TR_MNU)) >> 8); - const uint8_t seconds = BcdToByte(time_register & (RTC_TR_ST | RTC_TR_SU)); - - return (((hours * 60) + minutes) * 60) + seconds; -} - -static RtcIntervalTicks elapsed_ticks(RtcIntervalTicks before, RtcIntervalTicks after) { - int32_t result = after - before; - if (result < 0) { - result = (TICKS_IN_AN_INTERVAL - before) + after; - } - return result; -} - -static void restore_rtc_time_state(void) { - // Recover the previously set time from the RTC backup registers. - RtcIntervalTicks last_save_time_ticks = RTC_ReadBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER); - time_t last_save_time = RTC_ReadBackupRegister(CURRENT_TIME_REGISTER); - - RtcIntervalTicks current_ticks = get_rtc_interval_ticks(); - const int32_t ticks_since_last_save = elapsed_ticks(last_save_time_ticks, current_ticks); - s_time_base = last_save_time + (ticks_since_last_save / RTC_TICKS_HZ); - - s_time_tick_base = -(((int64_t)current_ticks) % RTC_TICKS_HZ); - -#ifdef VERBOSE_LOGGING - char buffer[TIME_STRING_BUFFER_SIZE]; - PBL_LOG_VERBOSE("Restore RTC: saved: %"PRIu32" diff: %"PRIu32, last_save_time_ticks, ticks_since_last_save); - PBL_LOG_VERBOSE("Restore RTC: saved_time: %s raw: %lu", time_t_to_string(buffer, last_save_time), last_save_time); - PBL_LOG_VERBOSE("Restore RTC: current time: %s", time_t_to_string(buffer, s_time_base)); - PBL_LOG_VERBOSE("Restore RTC: s_time_tick_base: %"PRId64, s_time_tick_base); -#endif -} - -static time_t ticks_to_time(RtcTicks ticks) { - return s_time_base + ((ticks - s_time_tick_base) / RTC_TICKS_HZ); -} - -static RtcIntervalTicks get_last_save_time_ticks(void) { - return RTC_ReadBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER); -} - -static void save_rtc_time_state_exact(RtcIntervalTicks current_rtc_ticks, time_t time) { - RTC_WriteBackupRegister(CURRENT_TIME_REGISTER, time); - RTC_WriteBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER, current_rtc_ticks); - - // Dbgserial instead of PBL_LOG to avoid infinite recursion due to PBL_LOG wanting to know - // the current ticks. - //char buffer[128]; - //dbgserial_putstr_fmt(buffer, 128, "Saving RTC state: ticks: %"PRIu32" time: %s raw: %lu", current_rtc_ticks, time_t_to_string(time), time); - //itoa(time, buffer, sizeof(buffer)); - //dbgserial_putstr(buffer); - //dbgserial_putstr("Done"); -} - -static void save_rtc_time_state(RtcIntervalTicks current_rtc_ticks) { - // Floor it to the latest second - const RtcIntervalTicks current_rtc_ticks_at_second = (current_rtc_ticks / RTC_TICKS_HZ) * RTC_TICKS_HZ; - - save_rtc_time_state_exact(current_rtc_ticks_at_second, ticks_to_time(s_coarse_ticks + current_rtc_ticks)); -} - -static void initialize_fast_mode_state(void) { - RtcIntervalTicks before_ticks = get_rtc_interval_ticks(); - - // Set the RTC to value 0 so we start from scratch nicely - RTC_TimeTypeDef rtc_time; - RTC_TimeStructInit(&rtc_time); - RTC_SetTime(RTC_Format_BIN, &rtc_time); - - // Reset the last ticks counter so we don't rollover prematurely. - // This value will be set to non-zero if anyone asked for the tick count - // before this point. - s_last_ticks = 0; - - // Refresh the saved time so it's more current. - save_rtc_time_state_exact(TICKS_IN_AN_INTERVAL - (RTC_TICKS_HZ - (before_ticks % RTC_TICKS_HZ)), ticks_to_time(s_coarse_ticks)); - //save_rtc_time_state(0); -} - -void rtc_init(void) { - periph_config_acquire_lock(); - rtc_enable_backup_regs(); - periph_config_release_lock(); - - restore_rtc_time_state(); - initialize_fast_mode_state(); - -#ifdef PBL_LOG_ENABLED - char buffer[TIME_STRING_BUFFER_SIZE]; - PBL_LOG(LOG_LEVEL_DEBUG, "Current time is <%s>", rtc_get_time_string(buffer)); -#endif -} - -void rtc_init_timers(void) { - static RegularTimerInfo rtc_sync_timer = { .list_node = { 0, 0 }, .cb = rtc_resync_timer_callback}; - regular_timer_add_minutes_callback(&rtc_sync_timer); -} - -//! How frequently we save the time state to the backup registers in ticks. -#define SAVE_TIME_FREQUENCY (30 * RTC_TICKS_HZ) - -static void check_and_handle_rollover(RtcIntervalTicks rtc_ticks) { - bool save_needed = false; - - const RtcIntervalTicks last_ticks = s_last_ticks; - s_last_ticks = rtc_ticks; - - if (rtc_ticks < last_ticks) { - // We've wrapped. Add on the number of seconds in a day to the base number. - s_coarse_ticks += TICKS_IN_AN_INTERVAL; - - save_needed = true; - } else if (elapsed_ticks(get_last_save_time_ticks(), rtc_ticks) > SAVE_TIME_FREQUENCY) { - // If we didn't do this, we would have an edge case where if the watch reset - // immediately before rollover and then rolled over before we booted again, - // we wouldn't be able to detect the rollover and we'd think the saved state - // is very fresh, when really it's over an interval old. By saving multiple - // times an interval this is still possible to happen, but it's much less likely. - // We would need to be shutdown for (SECONDS_IN_A_DAY - SAVE_TIME_FREQUENCY) ticks - // for this to happen. - save_needed = true; - } - - - if (save_needed) { - save_rtc_time_state(rtc_ticks); - } -} - -static RtcTicks get_ticks(void) { - - // Prevent this from being interrupted - bool ints_enabled = mcu_state_are_interrupts_enabled(); - if (ints_enabled) { - __disable_irq(); - } - - RtcTicks rtc_interval_ticks = get_rtc_interval_ticks(); - check_and_handle_rollover(rtc_interval_ticks); - - if (ints_enabled) { - __enable_irq(); - } - - return s_coarse_ticks + rtc_interval_ticks; -} - -void rtc_set_time(time_t time) { -#ifdef PBL_LOG_ENABLED - char buffer[TIME_STRING_BUFFER_SIZE]; - PBL_LOG(LOG_LEVEL_INFO, "Setting time to %lu <%s>", time, time_t_to_string(buffer, time)); -#endif - - s_time_base = time; - s_time_tick_base = get_ticks(); - - save_rtc_time_state(s_time_tick_base - s_coarse_ticks); -} - -time_t rtc_get_time(void) { - return ticks_to_time(get_ticks()); -} - -void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) { - RtcTicks ticks = get_ticks(); - - RtcTicks ticks_since_time_base = (ticks - s_time_tick_base); - *out_seconds = s_time_base + (ticks_since_time_base / RTC_TICKS_HZ); - - RtcTicks ticks_this_second = ticks_since_time_base % RTC_TICKS_HZ; - *out_ms = (ticks_this_second * 1000) / RTC_TICKS_HZ; -} - -RtcTicks rtc_get_ticks(void) { - return get_ticks(); -} - - -void rtc_alarm_init(void) { - RTC_ITConfig(RTC_IT_ALRA, DISABLE); - RTC_AlarmCmd(RTC_Alarm_A, DISABLE); - - RTC_ClearITPendingBit(RTC_IT_ALRA); - - exti_configure_other(ExtiLineOther_RTCAlarm, ExtiTrigger_Rising); - exti_enable_other(ExtiLineOther_RTCAlarm); - - s_tick_alarm_initialized = true; -} - -void rtc_alarm_set(RtcTicks num_ticks) { - PBL_ASSERTN(s_tick_alarm_initialized); - - RTC_ITConfig(RTC_IT_ALRA, DISABLE); - RTC_AlarmCmd(RTC_Alarm_A, DISABLE); - - RTC_AlarmTypeDef alarm_config; - RTC_AlarmStructInit(&alarm_config); - alarm_config.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay; - - s_alarm_set_time = rtc_get_ticks(); - - RtcTicks alarm_expiry_time = s_alarm_set_time + num_ticks; - - uint32_t days, hours, minutes, seconds; - time_util_split_seconds_into_parts(alarm_expiry_time, &days, &hours, &minutes, &seconds); - - (void) days; // Don't care about days. - alarm_config.RTC_AlarmTime.RTC_Hours = hours; - alarm_config.RTC_AlarmTime.RTC_Minutes = minutes; - alarm_config.RTC_AlarmTime.RTC_Seconds = seconds; - - RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &alarm_config); - - RTC_ITConfig(RTC_IT_ALRA, ENABLE); - RTC_AlarmCmd(RTC_Alarm_A, ENABLE); - - RTC_ClearFlag(RTC_FLAG_ALRAF); - EXTI_ClearITPendingBit(EXTI_Line17); - RTC_ClearITPendingBit(RTC_IT_ALRA); -} - -RtcTicks rtc_alarm_get_elapsed_ticks(void) { - return rtc_get_ticks() - s_alarm_set_time; -} - -bool rtc_alarm_is_initialized(void) { - return s_tick_alarm_initialized; -} - - -//! Handler for the RTC alarm interrupt. We don't actually have to do anything in this handler, -//! just the interrupt firing is enough to bring us out of stop mode. -void RTC_Alarm_IRQHandler(void) { - if (RTC_GetITStatus(RTC_IT_ALRA) != RESET) { - RTC_AlarmCmd(RTC_Alarm_A, DISABLE); - - RTC_ClearITPendingBit(RTC_IT_ALRA); - EXTI_ClearITPendingBit(EXTI_Line17); - } -} diff --git a/src/fw/drivers/stm32f2/rtc_calibration.c b/src/fw/drivers/stm32f2/rtc_calibration.c deleted file mode 100644 index 5483ce1ff0..0000000000 --- a/src/fw/drivers/stm32f2/rtc_calibration.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "rtc_calibration.h" - -#include "system/logging.h" -#include "util/math.h" - -#define STM32F2_COMPATIBLE -#include - - -RTCCalibConfig rtc_calibration_get_config(uint32_t frequency, uint32_t target) { - if (frequency == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "RTC frequency invalid - Skipping calibration"); - return (RTCCalibConfig) { - .sign = RTC_CalibSign_Positive, - .units = 0 - }; - } - - // Difference in frequency in mHz (ex. 224 = .224 Hz off from target frequency) - const int32_t rtc_freq_diff = target - frequency; - - // RTC_CoarseCalibConfig uses units of +4.069ppm or -2.035ppm. - // Formula: - // ppm = 1e6(target - frequency)/(target) - // positive units = ppm / 4.069 - // negative units = ppm / -2.035 - uint32_t rtc_calib_sign, rtc_calib_units; - const uint64_t numerator = 1000000000 * (uint64_t)ABS(rtc_freq_diff); - uint64_t divisor; - - if (rtc_freq_diff >= 0) { - divisor = 4069; - rtc_calib_sign = RTC_CalibSign_Positive; - } else { - divisor = 2035; - rtc_calib_sign = RTC_CalibSign_Negative; - } - - rtc_calib_units = ROUND(numerator, divisor * target); - - return (RTCCalibConfig) { - .sign = rtc_calib_sign, - // Coarse calibration has a range of -63ppm to 126ppm. - .units = MIN(rtc_calib_units, 31) - }; -} - -// For RTC calibration testing -#ifdef RTC_CALIBRATION_TESTING - -#include "drivers/rtc.h" -#include "drivers/periph_config.h" -#include "system/passert.h" - -void rtc_calibration_init_timer(void) { - const uint32_t timer_clock_hz = 32000; - - // The timer is on ABP1 which is clocked by PCLK1 - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - uint32_t timer_clock = clocks.PCLK1_Frequency; // Hz - - uint32_t prescale = RCC->CFGR & RCC_CFGR_PPRE1; - if (prescale != RCC_CFGR_PPRE1_DIV1) { - // per the stm32 'clock tree' diagram, if the prescaler for APBx is not 1, then - // the timer clock is at double the APBx frequency - timer_clock *= 2; - } - - // Clock frequency to run the timer at - uint32_t prescaler = timer_clock / timer_clock_hz; - uint32_t period = timer_clock_hz; - - // period & prescaler values are 16 bits, check for configuration errors - PBL_ASSERTN(period <= UINT16_MAX && prescaler <= UINT16_MAX); - - periph_config_enable(TIM7, RCC_APB1Periph_TIM7); - - NVIC_InitTypeDef NVIC_InitStructure; - /* Enable the TIM7 gloabal Interrupt */ - TIM_ClearITPendingBit(TIM7, TIM_IT_Update); - NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0b; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - // Set up a timer that runs at 1Hz and activates once every second - TIM_TimeBaseInitTypeDef tim_config; - TIM_TimeBaseStructInit(&tim_config); - tim_config.TIM_Period = period; - // The timer is on ABP1 which is clocked by PCLK1 - tim_config.TIM_Prescaler = prescaler; - // tim_config.TIM_ClockDivision = TIM_CKD_DIV4; - tim_config.TIM_CounterMode = TIM_CounterMode_Up; - TIM_TimeBaseInit(TIM7, &tim_config); - - TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); - TIM_Cmd(TIM7, ENABLE); -} - -static void prv_delta_ticks(void) { - static uint64_t last_tick = 0; - - uint64_t rtc_ticks = rtc_get_ticks(); - PBL_LOG(LOG_LEVEL_INFO, "RTC tick delta: %d", rtc_ticks - last_tick); - - last_tick = rtc_ticks; -} - -void TIM7_IRQHandler(void) { - static uint8_t count = 0; - - // Workaround M3 bug that causes interrupt to fire twice: - // https://my.st.com/public/Faq/Lists/faqlst/DispForm.aspx?ID=143 - TIM_ClearITPendingBit(TIM7, TIM_IT_Update); - - if (count == 0) { - prv_delta_ticks(); - } - - // Log delta ticks every ~60 seconds - count++; - count %= 60; -} - -#else - -void rtc_calibration_init_timer(void) {} - -#endif diff --git a/src/fw/drivers/stm32f2/rtc_calibration.h b/src/fw/drivers/stm32f2/rtc_calibration.h deleted file mode 100644 index 1b2bbe6074..0000000000 --- a/src/fw/drivers/stm32f2/rtc_calibration.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef struct RTCCalibConfig { - uint32_t sign; - uint32_t units; -} RTCCalibConfig; - -//! Calculate the appropriate coarse calibration config given the measured and target frequencies -//! (in mHz) -RTCCalibConfig rtc_calibration_get_config(uint32_t frequency, uint32_t target); - -void rtc_calibration_init_timer(void); diff --git a/src/fw/drivers/stm32f2/spi.c b/src/fw/drivers/stm32f2/spi.c deleted file mode 100644 index 4469ceee86..0000000000 --- a/src/fw/drivers/stm32f2/spi.c +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "spi_definitions.h" -#include "drivers/spi.h" -#include "drivers/spi_dma.h" - -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/units.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -//! Deduced by looking at the prescalers in stm32f2xx_spi.h -#define SPI_FREQ_LOG_TO_PRESCALER(LG) (((LG) - 1) * 0x8) -//! Bits in CR1 we intend to keep when updating it -#define CR1_CLEAR_MASK ((uint16_t)0x3040) - -//! SPI / I2S DMA definitions -typedef enum SpiI2sDma { - SpiI2sDma_ReqTx = 0x0002, - SpiI2sDma_ReqRx = 0x0001 -} SpiI2sDma; - -//! SPI Master/Slave -typedef enum SpiMode { - SpiMode_Master = 0x0104, - SpiMode_Slave = 0x0000 -} SpiMode; - -//! SPI Data Size -typedef enum SpiDataSize { - SpiDataSize_16b = 0x0800, - SpiDataSize_8b = 0x0000 -} SpiDataSize; - -//! SPI Slave Select -typedef enum SpiSlaveSelect { - SpiSlaveSelect_Soft = 0x0200, - SpiSlaveSelect_Hard = 0x0000 -} SpiSlaveSelect; - -typedef enum { - SpiDisable = 0, - SpiEnable -} SpiFunctionalState; - -// -// Private SPI bus functions. No higher level code should -// get access to SPIBus functions or data directly -// - -static bool prv_spi_get_flag_status(const SPIBus *bus, SpiI2sFlag flag) { - /* Check the status of the specified SPI flag */ - return (bus->spi->SR & (uint16_t)flag) != 0; -} - -static bool prv_spi_transmit_is_idle(const SPIBus *bus) { - return prv_spi_get_flag_status(bus, SpiI2sFlag_TXE); -} - -static bool prv_spi_receive_is_ready(const SPIBus *bus) { - return prv_spi_get_flag_status(bus, SpiI2sFlag_RXNE); -} - -void prv_spi_send_data(const SPIBus *bus, uint16_t Data) { -#if MICRO_FAMILY_STM32F7 - // STM32F7 needs to access as 8 bits in order to actually do 8 bits. - // This _does_ work on F4, but QEMU doesn't agree, so let's just do it safely. - *(volatile uint8_t*)&bus->spi->DR = Data; -#else - bus->spi->DR = Data; -#endif -} - -uint16_t prv_spi_receive_data(const SPIBus *bus) { -#if MICRO_FAMILY_STM32F7 - // STM32F7 needs to access as 8 bits in order to actually do 8 bits. - // This _does_ work on F4, but QEMU doesn't agree, so let's just do it safely. - return *(volatile uint8_t*)&bus->spi->DR; -#else - return bus->spi->DR; -#endif -} - -void prv_spi_enable_peripheral_clock(const SPIBus *bus) { - periph_config_enable(bus->spi, bus->state->spi_clock_periph); -} - -void prv_spi_disable_peripheral_clock(const SPIBus *bus) { - periph_config_disable(bus->spi, bus->state->spi_clock_periph); -} - -static void prv_spi_clear_flags(const SPIBus *bus) { - prv_spi_receive_data(bus); - prv_spi_get_flag_status(bus, (SpiI2sFlag)0); -} - -static void prv_spi_dma_cmd(const SPIBus *bus, SpiI2sDma dma_bits, bool enable) { - if (enable) { - bus->spi->CR2 |= (uint16_t)dma_bits; - } else { - bus->spi->CR2 &= (uint16_t)~dma_bits; - } -} - -static void prv_spi_cmd(const SPIBus *bus, SpiFunctionalState state) { - if (state != SpiDisable) { - /* Enable the selected SPI peripheral */ - bus->spi->CR1 |= SPI_CR1_SPE; - } else { - /* Disable the selected SPI peripheral */ - bus->spi->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE); - } -} - -void prv_spi_pick_peripheral(const SPIBus *bus) { - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - if (bus->spi == SPI1) { - bus->state->spi_clock_periph = RCC_APB2Periph_SPI1; - bus->state->spi_clock_periph_speed = clocks.PCLK2_Frequency; - bus->state->spi_apb = SpiAPB_2; - } else if (bus->spi == SPI2) { - bus->state->spi_clock_periph = RCC_APB1Periph_SPI2; - bus->state->spi_clock_periph_speed = clocks.PCLK1_Frequency; - bus->state->spi_apb = SpiAPB_1; - } else if (bus->spi == SPI3) { - bus->state->spi_clock_periph = RCC_APB1Periph_SPI3; - bus->state->spi_clock_periph_speed = clocks.PCLK1_Frequency; - bus->state->spi_apb = SpiAPB_1; -#ifdef SPI4 - } else if (bus->spi == SPI4) { - bus->state->spi_clock_periph = RCC_APB2Periph_SPI4; - bus->state->spi_clock_periph_speed = clocks.PCLK2_Frequency; - bus->state->spi_apb = SpiAPB_2; -#endif -#ifdef SPI5 - } else if (bus->spi == SPI5) { - bus->state->spi_clock_periph = RCC_APB2Periph_SPI5; - bus->state->spi_clock_periph_speed = clocks.PCLK2_Frequency; - bus->state->spi_apb = SpiAPB_2; -#endif -#ifdef SPI6 - } else if (bus->spi == SPI6) { - bus->state->spi_clock_periph = RCC_APB2Periph_SPI6; - bus->state->spi_clock_periph_speed = clocks.PCLK2_Frequency; - bus->state->spi_apb = SpiAPB_2; -#endif - } -} - -static uint16_t prv_spi_find_prescaler(const SPIBus *bus) { - int lg; - if (bus->state->spi_clock_speed_hz > (bus->state->spi_clock_periph_speed / 2)) { - lg = 1; // Underclock to the highest possible frequency - } else { - uint32_t divisor = bus->state->spi_clock_periph_speed / bus->state->spi_clock_speed_hz; - lg = ceil_log_two(divisor); - } - - // Prescalers only exists for values in [2 - 256] range - PBL_ASSERTN(lg > 0); - PBL_ASSERTN(lg < 9); - - // return prescaler - return (SPI_FREQ_LOG_TO_PRESCALER(lg)); -} - -void prv_spi_transmit_flush_blocking(const SPIBus *bus) { - while (!prv_spi_transmit_is_idle(bus)) continue; -} - -void prv_spi_receive_wait_ready_blocking(const SPIBus *bus) { - while (!prv_spi_receive_is_ready(bus)) continue; -} - -static void prv_configure_spi_sclk(const AfConfig *clk_pin, uint16_t spi_sclk_speed) { - gpio_af_init(clk_pin, GPIO_OType_PP, spi_sclk_speed, GPIO_PuPd_NOPULL); -} - -static void prv_spi_bus_deinit(const SPIBus *bus, bool is_bidirectional) { - // The pins are no longer in use so reconfigure as analog inputs to save some power - - // SCLK - InputConfig sclk = { .gpio = bus->spi_sclk.gpio, .gpio_pin = bus->spi_sclk.gpio_pin }; - gpio_analog_init(&sclk); - - // MOSI - InputConfig mosi = { .gpio = bus->spi_mosi.gpio, .gpio_pin = bus->spi_mosi.gpio_pin }; - gpio_analog_init(&mosi); - - // MISO - if (is_bidirectional) { - InputConfig miso = { .gpio = bus->spi_miso.gpio, .gpio_pin = bus->spi_miso.gpio_pin }; - gpio_analog_init(&miso); - } - - bus->state->initialized = false; -} - -void prv_spi_bus_init(const SPIBus *bus, bool is_bidirectional) { - if (bus->state->initialized) { - return; - } - // copy the speed over to the transient state since the slave port can change it - bus->state->spi_clock_speed_hz = bus->spi_clock_speed_hz; - prv_spi_pick_peripheral(bus); - bus->state->initialized = true; - // SCLK - prv_configure_spi_sclk(&bus->spi_sclk, bus->spi_sclk_speed); - // MOSI - gpio_af_init(&bus->spi_mosi, GPIO_OType_PP, bus->spi_sclk_speed, GPIO_PuPd_NOPULL); - // MISO - if (is_bidirectional) { - gpio_af_init(&bus->spi_miso, GPIO_OType_PP, bus->spi_sclk_speed, GPIO_PuPd_NOPULL); - } -} - -static void prv_spi_slave_init(const SPISlavePort *slave) { - prv_spi_enable_peripheral_clock(slave->spi_bus); - SPIBus *bus = slave->spi_bus; - // Grab existing configuration - uint16_t tmpreg = bus->spi->CR1; - // Clear BIDIMode, BIDIOE, RxONLY, SSM, SSI, LSBFirst, BR, MSTR, CPOL and CPHA bits - tmpreg &= CR1_CLEAR_MASK; - // get the baudrate prescaler - uint32_t prescaler = prv_spi_find_prescaler(bus); - // Master mode, 8 bit Data Size and Soft Slave select are hardcoded - // Direction, CPOL, CPHA, baudrate prescaler and first-bit come from the device config - tmpreg |= (uint16_t)((uint32_t)slave->spi_direction | SpiMode_Master | - SpiDataSize_8b | slave->spi_cpol | - slave->spi_cpha | SpiSlaveSelect_Soft | - prescaler | slave->spi_first_bit); - // Write result back to CR1 - bus->spi->CR1 = tmpreg; - -#if MICRO_FAMILY_STM32F7 - // On STM32F7 we need to set FRXTH in order to do 8-bit transfers. - // If we don't, the MCU always tries to read 16-bits even though we - // specified that the data is 8-bits. - // Why clear isn't 8-bit is beyond me, but ok. - bus->spi->CR2 |= SPI_CR2_FRXTH; -#endif - - // Activate the SPI mode (Reset I2SMOD bit in I2SCFGR register) - bus->spi->I2SCFGR &= (uint16_t)~((uint16_t)SPI_I2SCFGR_I2SMOD); - - prv_spi_disable_peripheral_clock(slave->spi_bus); -} - -static void prv_spi_slave_deinit(const SPISlavePort *slave) { - spi_ll_slave_acquire(slave); - SPIBus *bus = slave->spi_bus; - if (bus->state->spi_apb == SpiAPB_1) { - // Enable SPIx reset state - RCC_APB1PeriphResetCmd(bus->state->spi_clock_periph, ENABLE); - // Release SPIx from reset state - RCC_APB1PeriphResetCmd(bus->state->spi_clock_periph, DISABLE); - } else if (bus->state->spi_apb == SpiAPB_2) { - // Enable SPIx reset state - RCC_APB2PeriphResetCmd(bus->state->spi_clock_periph, ENABLE); - // Release SPIx from reset state - RCC_APB2PeriphResetCmd(bus->state->spi_clock_periph, DISABLE); - } - spi_ll_slave_release(slave); -} - -// -//! High level slave port interface -//! This part of the API can be used for fairly straightforward SPI interactions -//! The assertion and deassertion of the SCS line is automatic -// - -static bool prv_is_bidrectional(const SPISlavePort *slave) { - bool is_bidirectional = (slave->spi_direction == SpiDirection_2LinesFullDuplex) || - (slave->spi_direction == SpiDirection_2LinesRxOnly); - - return (is_bidirectional); -} - -void spi_slave_port_deinit(const SPISlavePort *slave) { - // don't deinitialize twice - if (!slave->slave_state->initialized) { - return; - } - prv_spi_slave_deinit(slave); - prv_spi_bus_deinit(slave->spi_bus, prv_is_bidrectional(slave)); - slave->slave_state->initialized = false; -} - -void spi_slave_port_init(const SPISlavePort *slave) { - // don't initialize twice - if (slave->slave_state->initialized) { - return; - } - slave->slave_state->initialized = true; - slave->slave_state->acquired = false; - slave->slave_state->scs_selected = false; - prv_spi_bus_init(slave->spi_bus, prv_is_bidrectional(slave)); - - // SCS - gpio_output_init(&slave->spi_scs, GPIO_OType_PP, slave->spi_bus->spi_sclk_speed); - gpio_output_set(&slave->spi_scs, false); // SCS not asserted (high) - - // Set up an SPI - prv_spi_slave_deinit(slave); - - prv_spi_slave_init(slave); - - // Set up DMA - if (slave->rx_dma) { - dma_request_init(slave->rx_dma); - } - if (slave->tx_dma) { - dma_request_init(slave->tx_dma); - } -} - -static void prv_spi_acquire_helper(const SPISlavePort *slave) { - spi_ll_slave_acquire(slave); - spi_ll_slave_scs_assert(slave); -} - -static void prv_spi_release_helper(const SPISlavePort *slave) { - spi_ll_slave_scs_deassert(slave); - spi_ll_slave_release(slave); -} - -uint8_t spi_slave_read_write(const SPISlavePort *slave, uint8_t out) { - prv_spi_acquire_helper(slave); - uint8_t ret = spi_ll_slave_read_write(slave, out); - prv_spi_release_helper(slave); - return ret; -} - -void spi_slave_write(const SPISlavePort *slave, uint8_t out) { - prv_spi_acquire_helper(slave); - spi_ll_slave_write(slave, out); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_read(const SPISlavePort *slave, void *in, size_t len) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_read(slave, in, len); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_write(const SPISlavePort *slave, const void *out, size_t len) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_write(slave, out, len); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_read_write(const SPISlavePort *slave, const void *out, void *in, size_t len) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_read_write(slave, out, in, len); - prv_spi_release_helper(slave); -} - -void spi_slave_burst_read_write_scatter(const SPISlavePort *slave, - const SPIScatterGather *sc_info, - size_t num_sg) { - prv_spi_acquire_helper(slave); - spi_ll_slave_burst_read_write_scatter(slave, sc_info, num_sg); - prv_spi_release_helper(slave); -} - -void spi_slave_set_frequency(const SPISlavePort *slave, uint32_t frequency_hz) { - slave->spi_bus->state->spi_clock_speed_hz = frequency_hz; - prv_spi_slave_init(slave); -} - -void spi_slave_wait_until_idle_blocking(const SPISlavePort *slave) { - while (prv_spi_get_flag_status(slave->spi_bus, SpiI2sFlag_BSY)) continue; -} - -uint32_t spi_get_dma_base_address(const SPISlavePort *slave) { - return (uint32_t)&(slave->spi_bus->spi->DR); -} -// -//! Low level slave port interface -//! This part of the API can be used for slightly more complex SPI operations -//! (such as piecemeal reads or writes). Assertion and deassertion of SCS -//! is up to the caller. Asserts in the code will help to ensure that the -//! API is used correctly. -// - -void spi_ll_slave_acquire(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired == false); - prv_spi_enable_peripheral_clock(slave->spi_bus); - prv_spi_clear_flags(slave->spi_bus); - slave->slave_state->acquired = true; - spi_ll_slave_spi_enable(slave); -} - -void spi_ll_slave_release(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - spi_slave_wait_until_idle_blocking(slave); - prv_spi_clear_flags(slave->spi_bus); - spi_ll_slave_spi_disable(slave); - slave->slave_state->acquired = false; - prv_spi_disable_peripheral_clock(slave->spi_bus); -} - -void spi_ll_slave_spi_enable(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - prv_spi_cmd(slave->spi_bus, SpiEnable); -} - -void spi_ll_slave_spi_disable(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - prv_spi_cmd(slave->spi_bus, SpiDisable); -} - -void spi_ll_slave_scs_assert(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected == false); - slave->slave_state->scs_selected = true; - gpio_output_set(&slave->spi_scs, true); // SCS asserted (low) -} - -void spi_ll_slave_scs_deassert(const SPISlavePort *slave) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - slave->slave_state->scs_selected = false; - gpio_output_set(&slave->spi_scs, false); // SCS not asserted (high) -} - -uint8_t spi_ll_slave_read_write(const SPISlavePort *slave, uint8_t out) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - prv_spi_transmit_flush_blocking(slave->spi_bus); - prv_spi_send_data(slave->spi_bus, out); - prv_spi_receive_wait_ready_blocking(slave->spi_bus); - return prv_spi_receive_data(slave->spi_bus); -} - -void spi_ll_slave_write(const SPISlavePort *slave, uint8_t out) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - prv_spi_transmit_flush_blocking(slave->spi_bus); - prv_spi_send_data(slave->spi_bus, out); -} - -void spi_ll_slave_burst_read(const SPISlavePort *slave, void *in, size_t len) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->scs_selected); - uint8_t *cptr = in; - while (len--) { - *(cptr++) = spi_ll_slave_read_write(slave, 0); // useless write-data - } -} - -void spi_ll_slave_burst_write(const SPISlavePort *slave, const void *out, size_t len) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - const uint8_t *cptr = out; - while (len--) { - prv_spi_send_data(slave->spi_bus, *(cptr++)); - prv_spi_transmit_flush_blocking(slave->spi_bus); - } -} - -void spi_ll_slave_burst_read_write(const SPISlavePort *slave, - const void *out, - void *in, - size_t len) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - const uint8_t *outp = out; - uint8_t *inp = in; - for (size_t n = 0; n < len; ++n) { - uint8_t byte_out = outp ? *(outp++) : 0; - uint8_t byte_in = spi_ll_slave_read_write(slave, byte_out); - if (inp) { - *(inp++) = byte_in; - } - } -} - -void spi_ll_slave_burst_read_write_scatter(const SPISlavePort *slave, - const SPIScatterGather *sc_info, - size_t num_sg) { - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - for (size_t elem = 0; elem < num_sg; ++elem) { - const SPIScatterGather *sg = &sc_info[elem]; - spi_ll_slave_burst_read_write(slave, sg->sg_out, sg->sg_in, sg->sg_len); - } -} - -static bool prv_dma_irq_handler(DMARequest *request, void *context) { - const SPISlavePort *slave = context; - PBL_ASSERTN(slave); - bool is_done = false; - switch (slave->slave_state->dma_state) { - case SPISlavePortDMAState_Read: - case SPISlavePortDMAState_Write: - case SPISlavePortDMAState_ReadWriteOneInterrupt: - slave->slave_state->dma_state = SPISlavePortDMAState_Idle; - is_done = true; - break; - case SPISlavePortDMAState_ReadWrite: - slave->slave_state->dma_state = SPISlavePortDMAState_ReadWriteOneInterrupt; - break; - case SPISlavePortDMAState_Idle: - default: - WTF; - } - SPIDMACompleteHandler handler = slave->slave_state->dma_complete_handler; - if (is_done && handler) { - return handler(slave, slave->slave_state->dma_complete_context); - } - return false; -} - -void spi_ll_slave_read_dma_start(const SPISlavePort *slave, void *in, size_t len, - SPIDMACompleteHandler handler, void *context) { - PBL_ASSERTN(slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->dma_state == SPISlavePortDMAState_Idle); - slave->slave_state->dma_state = SPISlavePortDMAState_Read; - slave->slave_state->dma_complete_handler = handler; - slave->slave_state->dma_complete_context = context; - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, true); - dma_request_start_direct(slave->rx_dma, in, (void *)&slave->spi_bus->spi->DR, len, - prv_dma_irq_handler, (void *)slave); -} - -void spi_ll_slave_read_dma_stop(const SPISlavePort *slave) { - if (slave->slave_state->dma_state != SPISlavePortDMAState_Read) { - return; - } - dma_request_stop(slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, false); - slave->slave_state->dma_complete_handler = NULL; - slave->slave_state->dma_complete_context = NULL; -} - -void spi_ll_slave_write_dma_start(const SPISlavePort *slave, const void *out, size_t len, - SPIDMACompleteHandler handler, void *context) { - PBL_ASSERTN(slave->tx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->dma_state == SPISlavePortDMAState_Idle); - slave->slave_state->dma_state = SPISlavePortDMAState_Write; - slave->slave_state->dma_complete_handler = handler; - slave->slave_state->dma_complete_context = context; - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, true); - dma_request_start_direct(slave->tx_dma, (void *)&slave->spi_bus->spi->DR, out, len, - prv_dma_irq_handler, (void *)slave); -} - -void spi_ll_slave_write_dma_stop(const SPISlavePort *slave) { - if (slave->slave_state->dma_state != SPISlavePortDMAState_Write) { - return; - } - dma_request_stop(slave->tx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, false); - slave->slave_state->dma_complete_handler = NULL; - slave->slave_state->dma_complete_context = NULL; -} - -void spi_ll_slave_read_write_dma_start(const SPISlavePort *slave, const void *out, void *in, - size_t len, SPIDMACompleteHandler handler, void *context) { - PBL_ASSERTN(slave->rx_dma && slave->tx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - PBL_ASSERTN(slave->slave_state->dma_state == SPISlavePortDMAState_Idle); - slave->slave_state->dma_complete_handler = handler; - slave->slave_state->dma_complete_context = context; - - if (out) { - dma_request_set_memory_increment_disabled(slave->tx_dma, false); - } else { - dma_request_set_memory_increment_disabled(slave->tx_dma, true); - static const uint8_t s_zero = 0; - out = &s_zero; - } - - if (in) { - slave->slave_state->dma_state = SPISlavePortDMAState_ReadWrite; - // start the read DMA - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, true); - dma_request_start_direct(slave->rx_dma, in, (void *)&slave->spi_bus->spi->DR, len, - prv_dma_irq_handler, (void *)slave); - } else { - slave->slave_state->dma_state = SPISlavePortDMAState_ReadWriteOneInterrupt; - } - // start the write DMA - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, true); - dma_request_start_direct(slave->tx_dma, (void *)&slave->spi_bus->spi->DR, out, len, - prv_dma_irq_handler, (void *)slave); -} - -void spi_ll_slave_read_write_dma_stop(const SPISlavePort *slave) { - if ((slave->slave_state->dma_state != SPISlavePortDMAState_ReadWrite) && - (slave->slave_state->dma_state != SPISlavePortDMAState_ReadWriteOneInterrupt)) { - return; - } - PBL_ASSERTN(slave->tx_dma && slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - dma_request_stop(slave->rx_dma); - dma_request_stop(slave->tx_dma); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, false); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, false); - slave->slave_state->dma_complete_handler = NULL; - slave->slave_state->dma_complete_context = NULL; -} - -bool spi_ll_slave_dma_in_progress(const SPISlavePort *slave) { - PBL_ASSERTN(slave->tx_dma || slave->rx_dma); - PBL_ASSERTN(slave->slave_state->initialized); - PBL_ASSERTN(slave->slave_state->acquired); - return (slave->rx_dma && dma_request_in_progress(slave->rx_dma)) || - (slave->tx_dma && dma_request_in_progress(slave->tx_dma)); -} - -void spi_ll_slave_set_tx_dma(const SPISlavePort *slave, bool enable) { - PBL_ASSERTN(slave->slave_state->initialized); - spi_ll_slave_acquire(slave); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqTx, enable); - spi_ll_slave_release(slave); -} - -void spi_ll_slave_set_rx_dma(const SPISlavePort *slave, bool enable) { - PBL_ASSERTN(slave->slave_state->initialized); - spi_ll_slave_acquire(slave); - prv_spi_dma_cmd(slave->spi_bus, SpiI2sDma_ReqRx, enable); - spi_ll_slave_release(slave); -} - -void spi_ll_slave_drive_clock(const SPISlavePort *slave, bool enable) { - const AfConfig *spi_sclk = &slave->spi_bus->spi_sclk; - - if (enable) { - OutputConfig clk_as_gpio = { - .gpio = spi_sclk->gpio, - .gpio_pin = spi_sclk->gpio_pin, - .active_high = true, - }; - gpio_output_init(&clk_as_gpio, GPIO_OType_PP, GPIO_Speed_50MHz); - gpio_output_set(&clk_as_gpio, false); - } else { - prv_configure_spi_sclk(spi_sclk, slave->spi_bus->spi_sclk_speed); - } -} - -void spi_ll_slave_clear_errors(const SPISlavePort *slave) { - // First, empty the RX FIFO by reading the data. If in TX-only mode, it's possible that - // received data (0x00s) will be left in the RX FIFO. - - // NOTE: Obviously, do not call this function with transfer in progress. - while (slave->spi_bus->spi->SR & SPI_SR_RXNE) { - (void)slave->spi_bus->spi->DR; - } - - // If the FIFO overflowed, the OVR error will be flagged. Clear the error. - (void)slave->spi_bus->spi->DR; - (void)slave->spi_bus->spi->SR; -} diff --git a/src/fw/drivers/stm32f2/spi_definitions.h b/src/fw/drivers/stm32f2/spi_definitions.h deleted file mode 100644 index b87b6345ac..0000000000 --- a/src/fw/drivers/stm32f2/spi_definitions.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/spi.h" - -#include "board/board.h" - -//! Generic(ish) definitions of how we wish a particular SPI to be configured -//! (Initially based on ST configuration and registers) -//! board.h and board_xxxx.h will use these definitions to configure each SPI -//! spi.c will use these definitions to program the device - -// REVISIT: We may like to split the definition and control of the SCS -// signal out of the main spi driver and into a separate driver so -// that if we ever share an SPI and use multiple SCS bits to select -// the destination we can control them individually. As it stands now -// we have exactly one. - -//! SPI transmission modes (unidirectional/bidirectional etc) -typedef enum SpiDirection { - SpiDirection_2LinesFullDuplex = 0x0000, - SpiDirection_2LinesRxOnly = 0x0400, - SpiDirection_1LineRx = 0x8000, - SpiDirection_1LineTx = 0xC000 -} SpiDirection; - -//! SPI Clock Polarity -typedef enum SpiCPol { - SpiCPol_Low = 0x0, - SpiCPol_High = 0x2 -} SpiCPol; - -//! SPI Clock Phase -typedef enum SpiCPha { - SpiCPha_1Edge = 0x0, - SpiCPha_2Edge = 0x1 -} SpiCPha; - -//! SPI MSB / LSB First Bit Transmission -typedef enum SpiFirstBit { - SpiFirstBit_MSB = 0x0000, - SpiFirstBit_LSB = 0x0080 -} SpiFirstBit; - -//! SPI / I2S Flags -typedef enum SpiI2sFlag { - SpiI2sFlag_RXNE = 0x0001, - SpiI2sFlag_TXE = 0x0002, - I2sFlag_CHSIDE = 0x0004, - I2sFlag_UDR = 0x0008, - SpiFlag_CRCERR = 0x0010, - SpiFlag_MODF = 0x0020, - SpiI2sFlag_OVR = 0x0040, - SpiI2sFlag_BSY = 0x0080, - SpiI2sFlag_TIFRFE = 0x0100 -} SpiI2sFlag; - -typedef enum SpiAPB { - SpiAPB_1, - SpiAPB_2 -} SpiAPB; - -typedef struct SPIBusState { - uint32_t spi_clock_speed_hz; // can be changed by slave port - uint32_t spi_clock_periph; // mapped to SPI peripheral - uint32_t spi_clock_periph_speed; - SpiAPB spi_apb; - bool initialized; -} SPIBusState; - -//! An SPI Bus specifies an SPI Instance and the I/O pins -//! used for the CLK, MOSI and MISO pins -//! The communication specific parameters (direction and -//! phase etc) and the pin to use for slave select are set per -//! SPISlavePort -//! REVISIT: There is currently no arbitration between possible -//! slave ports on the same bus - since for now all of our -//! SPI devices are point-to-point -typedef const struct SPIBus { - SPIBusState *state; - SPI_TypeDef *const spi; - AfConfig spi_sclk; - AfConfig spi_miso; - AfConfig spi_mosi; - uint16_t spi_sclk_speed; -// uint32_t spi_clock_ctrl; - uint32_t spi_clock_speed_hz; -} SPIBus; - -typedef enum SPISlavePortDMAState { - SPISlavePortDMAState_Idle, - SPISlavePortDMAState_Read, - SPISlavePortDMAState_Write, - SPISlavePortDMAState_ReadWrite, - SPISlavePortDMAState_ReadWriteOneInterrupt, -} SPISlavePortDMAState; - -typedef struct SPISlavePortState { - bool initialized; - bool acquired; - bool scs_selected; - SPIDMACompleteHandler dma_complete_handler; - void *dma_complete_context; - SPISlavePortDMAState dma_state; -} SPISlavePortState; - -typedef const struct SPISlavePort { - SPISlavePortState *slave_state; - SPIBus *spi_bus; - OutputConfig spi_scs; - SpiDirection spi_direction; - SpiCPol spi_cpol; - SpiCPha spi_cpha; - SpiFirstBit spi_first_bit; - DMARequest *rx_dma; - DMARequest *tx_dma; -} SPISlavePort; diff --git a/src/fw/drivers/stm32f2/spi_legacy.c b/src/fw/drivers/stm32f2/spi_legacy.c deleted file mode 100644 index 24247987e6..0000000000 --- a/src/fw/drivers/stm32f2/spi_legacy.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// REVISIT: -// This is a legacy implementation of the prescaler calculation -// code used by the roll-your-own SPI implementations. -// Once the new driver is used for all SPI interaction, this -// function can go away. - -#include "system/passert.h" -#include "util/math.h" -#include "board/board.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -// Deduced by looking at the prescalers in stm32f2xx_spi.h -#define SPI_FREQ_LOG_TO_PRESCALER(LG) (((LG) - 1) * 0x8) - -uint16_t spi_find_prescaler(uint32_t bus_frequency, SpiPeriphClock periph_clock) { - // Get the clocks - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - - uint32_t clock = 0; - // Find which peripheral clock we belong to - if (periph_clock == SpiPeriphClockAPB1) { - clock = clocks.PCLK1_Frequency; - } else if (periph_clock == SpiPeriphClockAPB2) { - clock = clocks.PCLK2_Frequency; - } else { - WTF; - } - - int lg; - if (bus_frequency > (clock / 2)) { - lg = 1; // Underclock to the highest possible frequency - } else { - uint32_t divisor = clock / bus_frequency; - lg = ceil_log_two(divisor); - } - - // Prescalers only exists for values in [2 - 256] range - PBL_ASSERTN(lg > 0); - PBL_ASSERTN(lg < 9); - - // return prescaler - return (SPI_FREQ_LOG_TO_PRESCALER(lg)); -} diff --git a/src/fw/drivers/stm32f2/system_flash.c b/src/fw/drivers/stm32f2/system_flash.c deleted file mode 100644 index 1e0141f099..0000000000 --- a/src/fw/drivers/stm32f2/system_flash.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/system_flash.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include "system/logging.h" - -void system_flash_erase(uint16_t sector) { - PBL_LOG_VERBOSE("system_flash_erase"); - - FLASH_Unlock(); - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); - - if (FLASH_EraseSector(sector, VoltageRange_1) != FLASH_COMPLETE) { - PBL_LOG(LOG_LEVEL_ALWAYS, "failed to erase sector %u", sector); - return; - } -} - -void system_flash_write_byte(uint32_t address, uint8_t data) { - FLASH_Unlock(); - FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); - - if (FLASH_ProgramByte(address, data) != FLASH_COMPLETE) { - PBL_LOG(LOG_LEVEL_DEBUG, "failed to write address %p", (void*) address); - return; - } -} - -uint32_t system_flash_read(uint32_t address) { - uint32_t data = *(volatile uint32_t*) address; - return data; -} diff --git a/src/fw/drivers/stm32f2/timer.c b/src/fw/drivers/stm32f2/timer.c deleted file mode 100644 index 8e545f9b99..0000000000 --- a/src/fw/drivers/stm32f2/timer.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/timer.h" - -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -static uint32_t prv_adjust_frequency(TIM_TypeDef *stm32_timer) { -#ifdef MICRO_FAMILY_STM32F4 - PBL_ASSERTN((RCC->DCKCFGR & RCC_DCKCFGR_TIMPRE) != RCC_DCKCFGR_TIMPRE); -#endif - - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - - uint32_t ppre_mask; - uint32_t clock_freq; - uint32_t ppre_div1_mask; - if ((uintptr_t)stm32_timer < AHB2PERIPH_BASE) { - clock_freq = clocks.PCLK1_Frequency; - ppre_mask = RCC_CFGR_PPRE1; - ppre_div1_mask = RCC_CFGR_PPRE1_DIV1; - } else { // AHB2 - clock_freq = clocks.PCLK2_Frequency; - ppre_mask = RCC_CFGR_PPRE1; - ppre_div1_mask = RCC_CFGR_PPRE1_DIV1; - } - - // From STM32F2xx Reference manual, section 5.2 (Clocks): - // The timer clock frequencies are automatically set by hardware. - // There are two cases: - // 1. If the APB prescaler is 1, the timer clock frequencies are set to the - // same frequency as that of the APB domain to which the timers are - // connected. - // 2. Otherwise, they are set to twice (×2) the frequency of the APB domain - // to which the timers are connected. - if ((RCC->CFGR & ppre_mask) == ppre_div1_mask) { - return clock_freq; - } else { - return clock_freq * 2; - } -} - -uint16_t timer_find_prescaler(const TimerConfig *timer, uint32_t frequency) { - uint32_t timer_clock = prv_adjust_frequency(timer->peripheral); - PBL_ASSERT(timer_clock >= frequency, "Timer clock frequency too low (LR %p)", - __builtin_return_address(0)); - return (timer_clock / frequency) - 1; -} - diff --git a/src/fw/drivers/stm32f2/uart.c b/src/fw/drivers/stm32f2/uart.c deleted file mode 100644 index c40c103e81..0000000000 --- a/src/fw/drivers/stm32f2/uart.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "uart_definitions.h" -#include "drivers/uart.h" - -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "system/passert.h" - -#include "FreeRTOS.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include -#include - -// The STM32F2 standard peripheral library uses a precision of 100 which is plenty, so we'll do the -// same. -#define DIV_PRECISION (100) - - -// Initialization / Configuration APIs -//////////////////////////////////////////////////////////////////////////////// - -typedef enum UARTCR1Flags { - UARTCR1Flags_Duplex = USART_CR1_TE | USART_CR1_RE, - UARTCR1Flags_TE = USART_CR1_TE, - UARTCR1Flags_RE = USART_CR1_RE, -} UARTCR1Flags; - -static void prv_init(UARTDevice *dev, bool is_open_drain, UARTCR1Flags cr1_extra_flags) { - // Enable peripheral clock - periph_config_enable(dev->periph, dev->rcc_apb_periph); - - // configure GPIO - const GPIOOType_TypeDef otype = is_open_drain ? GPIO_OType_OD : GPIO_OType_PP; - if (dev->tx_gpio.gpio) { - gpio_af_init(&dev->tx_gpio, otype, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - } - if (dev->rx_gpio.gpio) { - // half-duplex should only define a TX pin - PBL_ASSERTN(!dev->half_duplex); - gpio_af_init(&dev->rx_gpio, otype, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - } - if (dev->enable_flow_control) { - PBL_ASSERTN(dev->cts_gpio.gpio && dev->rts_gpio.gpio); - gpio_af_init(&dev->cts_gpio, otype, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&dev->rts_gpio, otype, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - } - - // configure the UART peripheral control registers - // - 8-bit word length - // - no parity - // - RX / TX enabled - // - 1 stop bit - // - no flow control - dev->periph->CR1 = cr1_extra_flags; - dev->periph->CR2 = 0; - dev->periph->CR3 = (dev->half_duplex ? USART_CR3_HDSEL : 0); - - if (dev->enable_flow_control) { - dev->periph->CR3 |= USART_CR3_CTSE | USART_CR3_RTSE; - } - - // QEMU doesn't want you to read the DR while the UART is not enabled, but it - // should be fine to clear errors this way -#if !TARGET_QEMU - // Clear any stale errors that may be in the registers. This can be accomplished - // by reading the status register followed by the data register - (void)dev->periph->SR; - (void)dev->periph->DR; -#endif - - dev->periph->CR1 |= USART_CR1_UE; - - dev->state->initialized = true; - - // initialize the DMA request - if (dev->rx_dma) { - dma_request_init(dev->rx_dma); - } -} - -void uart_init(UARTDevice *dev) { - prv_init(dev, false /* !is_open_drain */, UARTCR1Flags_Duplex); -} - -void uart_init_open_drain(UARTDevice *dev) { - prv_init(dev, true /* is_open_drain */, UARTCR1Flags_Duplex); -} - -void uart_init_tx_only(UARTDevice *dev) { - prv_init(dev, false /* !is_open_drain */, UARTCR1Flags_TE); -} - -void uart_init_rx_only(UARTDevice *dev) { - prv_init(dev, false /* !is_open_drain */, UARTCR1Flags_RE); -} - -void uart_deinit(UARTDevice *dev) { - dev->periph->CR1 &= ~USART_CR1_UE; - periph_config_disable(dev->periph, dev->rcc_apb_periph); - // Change the pins to be digital inputs rather than AF pins. We can't change to analog inputs - // because those aren't 5V tolerant which these pins may need to be. - if (dev->tx_gpio.gpio) { - const InputConfig input_config = { - .gpio = dev->tx_gpio.gpio, - .gpio_pin = dev->tx_gpio.gpio_pin - }; - gpio_input_init(&input_config); - } - if (dev->rx_gpio.gpio) { - const InputConfig input_config = { - .gpio = dev->rx_gpio.gpio, - .gpio_pin = dev->rx_gpio.gpio_pin - }; - gpio_input_init(&input_config); - } -} - -void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { - PBL_ASSERTN(dev->state->initialized); - - RCC_ClocksTypeDef RCC_ClocksStatus; - RCC_GetClocksFreq(&RCC_ClocksStatus); - uint64_t scaled_apbclock = DIV_PRECISION; - if ((dev->periph == USART1) || (dev->periph == USART6)) { - scaled_apbclock *= RCC_ClocksStatus.PCLK2_Frequency; - } else { - scaled_apbclock *= RCC_ClocksStatus.PCLK1_Frequency; - } - - // We need to calculate the divider to get from the clock frequency down to the sampling - // frequency (samples * baud_rate) and store it in USART_BBR as a fixed-point number with a - // franctional component equal to the number of samples per symbol. In other words, if OVER8=0, - // the fractional component will be 4 bits, and if OVER8=1, it will be 3 bits. - // The formula works out to: DIV = f_clk / (samples * BAUD) - - const bool over8 = dev->periph->CR1 & USART_CR1_OVER8; - const uint32_t samples = over8 ? 8 : 16; - - // calculate the divider multiplied by DIV_PRECISION - const uint32_t div_temp = scaled_apbclock / (samples * baud_rate); - - // calculate the mantissa component of BRR - const uint32_t mantissa = div_temp / DIV_PRECISION; - // isolate the fraction component by subtracting the mantissa component - uint32_t fraction = div_temp - mantissa * DIV_PRECISION; - // convert the fractional component to be in terms of the number of samples (with rounding) - fraction = (fraction * samples + (DIV_PRECISION / 2)) / DIV_PRECISION; - - if (over8) { - // 3 bits of fraction - dev->periph->BRR = (mantissa << 3) | (fraction & 0x7); - } else { - // 4 bits of fraction - dev->periph->BRR = (mantissa << 4) | (fraction & 0xF); - } -} - - -// Read / Write APIs -//////////////////////////////////////////////////////////////////////////////// - -void uart_write_byte(UARTDevice *dev, uint8_t data) { - PBL_ASSERTN(dev->state->initialized); - - // wait for us to be ready to send - while (!uart_is_tx_ready(dev)) continue; - - dev->periph->DR = data; -} - -uint8_t uart_read_byte(UARTDevice *dev) { - // read the data regardless since it will clear interrupt flags - return dev->periph->DR; -} - -UARTRXErrorFlags uart_has_errored_out(UARTDevice *dev) { - uint16_t errors = dev->periph->SR; - UARTRXErrorFlags flags = { - .parity_error = (errors & USART_FLAG_PE) != 0, - .overrun_error = (errors & USART_FLAG_ORE) != 0, - .framing_error = (errors & USART_FLAG_FE) != 0, - .noise_detected = (errors & USART_FLAG_NE) != 0, - }; - - return flags; -} - -bool uart_is_rx_ready(UARTDevice *dev) { - return dev->periph->SR & USART_SR_RXNE; -} - -bool uart_has_rx_overrun(UARTDevice *dev) { - return dev->periph->SR & USART_SR_ORE; -} - -bool uart_has_rx_framing_error(UARTDevice *dev) { - return dev->periph->SR & USART_SR_FE; -} - -bool uart_is_tx_ready(UARTDevice *dev) { - return dev->periph->SR & USART_SR_TXE; -} - -bool uart_is_tx_complete(UARTDevice *dev) { - return dev->periph->SR & USART_SR_TC; -} - -void uart_wait_for_tx_complete(UARTDevice *dev) { - while (!uart_is_tx_complete(dev)) continue; -} - - -// Interrupts -//////////////////////////////////////////////////////////////////////////////// - -static void prv_set_interrupt_enabled(UARTDevice *dev, bool enabled) { - if (enabled) { - PBL_ASSERTN(dev->state->tx_irq_handler || dev->state->rx_irq_handler); - // enable the interrupt - NVIC_SetPriority(dev->irq_channel, dev->irq_priority); - NVIC_EnableIRQ(dev->irq_channel); - } else { - // disable the interrupt - NVIC_DisableIRQ(dev->irq_channel); - } -} - -void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->rx_irq_handler = irq_handler; -} - -void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->tx_irq_handler = irq_handler; -} - -void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - if (enabled) { - dev->state->rx_int_enabled = true; - dev->periph->CR1 |= USART_CR1_RXNEIE; - prv_set_interrupt_enabled(dev, true); - } else { - // disable interrupt if TX is also disabled - prv_set_interrupt_enabled(dev, dev->state->tx_int_enabled); - dev->periph->CR1 &= ~USART_CR1_RXNEIE; - dev->state->rx_int_enabled = false; - } -} - -void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - if (enabled) { - dev->state->tx_int_enabled = true; - dev->periph->CR1 |= USART_CR1_TXEIE; - prv_set_interrupt_enabled(dev, true); - } else { - // disable interrupt if RX is also disabled - prv_set_interrupt_enabled(dev, dev->state->rx_int_enabled); - dev->periph->CR1 &= ~USART_CR1_TXEIE; - dev->state->tx_int_enabled = false; - } -} - -void uart_irq_handler(UARTDevice *dev) { - PBL_ASSERTN(dev->state->initialized); - bool should_context_switch = false; - if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { - const UARTRXErrorFlags err_flags = { - .overrun_error = uart_has_rx_overrun(dev), - .framing_error = uart_has_rx_framing_error(dev), - }; - if (dev->state->rx_dma_buffer) { - // process bytes from the DMA buffer - const uint32_t dma_length = dev->state->rx_dma_length; - const uint32_t next_idx = dma_length - dma_request_get_current_data_counter(dev->rx_dma); - // make sure we didn't underflow the index - PBL_ASSERTN(next_idx < dma_length); - while (dev->state->rx_dma_index != next_idx) { - const uint8_t data = dev->state->rx_dma_buffer[dev->state->rx_dma_index]; - if (dev->state->rx_irq_handler(dev, data, &err_flags)) { - should_context_switch = true; - } - if (++dev->state->rx_dma_index == dma_length) { - dev->state->rx_dma_index = 0; - } - } - // explicitly clear error flags since we're not reading from the data register - uart_clear_all_interrupt_flags(dev); - } else { - const bool has_byte = uart_is_rx_ready(dev); - // read the data register regardless to clear the error flags - const uint8_t data = uart_read_byte(dev); - if (has_byte) { - if (dev->state->rx_irq_handler(dev, data, &err_flags)) { - should_context_switch = true; - } - } - } - } - if (dev->state->tx_irq_handler && dev->state->tx_int_enabled && uart_is_tx_ready(dev)) { - if (dev->state->tx_irq_handler(dev)) { - should_context_switch = true; - } - } - portEND_SWITCHING_ISR(should_context_switch); -} - -void uart_clear_all_interrupt_flags(UARTDevice *dev) { - dev->periph->SR &= ~(USART_SR_TXE | USART_SR_RXNE | USART_SR_ORE); -} - - -// DMA -//////////////////////////////////////////////////////////////////////////////// - -void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { - dev->periph->CR3 |= USART_CR3_DMAR; - dma_request_start_circular(dev->rx_dma, buffer, (void *)&dev->periph->DR, length, NULL, NULL); - dev->state->rx_dma_index = 0; - dev->state->rx_dma_length = length; - dev->state->rx_dma_buffer = buffer; -} - -void uart_stop_rx_dma(UARTDevice *dev) { - dev->state->rx_dma_buffer = NULL; - dev->state->rx_dma_length = 0; - dma_request_stop(dev->rx_dma); - dev->periph->CR3 &= ~USART_CR3_DMAR; -} - -void uart_clear_rx_dma_buffer(UARTDevice *dev) { - dev->state->rx_dma_index = dev->state->rx_dma_length - - dma_request_get_current_data_counter(dev->rx_dma); -} diff --git a/src/fw/drivers/stm32f2/uart_definitions.h b/src/fw/drivers/stm32f2/uart_definitions.h deleted file mode 100644 index 7022a99307..0000000000 --- a/src/fw/drivers/stm32f2/uart_definitions.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/uart.h" - -#include "board/board.h" - -#include -#include - - -typedef struct UARTState { - bool initialized; - UARTRXInterruptHandler rx_irq_handler; - UARTTXInterruptHandler tx_irq_handler; - bool rx_int_enabled; - bool tx_int_enabled; - uint8_t *rx_dma_buffer; - uint32_t rx_dma_length; - uint32_t rx_dma_index; -} UARTDeviceState; - -typedef const struct UARTDevice { - UARTDeviceState *state; - bool half_duplex; - bool enable_flow_control; - AfConfig tx_gpio; - AfConfig rx_gpio; - AfConfig cts_gpio; - AfConfig rts_gpio; - USART_TypeDef *periph; - uint32_t rcc_apb_periph; - uint8_t irq_channel; - uint8_t irq_priority; - DMARequest *rx_dma; -} UARTDevice; - -// thinly wrapped by the IRQ handler in board_*.c -void uart_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/stm32f2/voltage_monitor.c b/src/fw/drivers/stm32f2/voltage_monitor.c deleted file mode 100644 index 1890ae7e4c..0000000000 --- a/src/fw/drivers/stm32f2/voltage_monitor.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/voltage_monitor.h" -#include "kernel/util/delay.h" -#include "os/mutex.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -// All boards use ADC1 solely for Vref, so we should never be using it for anything else. -#define VREF_ADC ADC1 -#define VREF_ADC_CLOCK RCC_APB2Periph_ADC1 - -static PebbleMutex *s_adc_mutex; - -void voltage_monitor_init(void) { - s_adc_mutex = mutex_create(); -} - -void voltage_monitor_device_init(VoltageMonitorDevice *device) { - gpio_analog_init(&device->input); -} - -// It takes ~12µs to get our ADC readings. From time to time, we're busy -// processing elsewhere for upwards of 25µs and end up getting overrun issues. -// In the case that overrun occurs, clean the flag and return false so that we -// know to restart the sample group. -static bool prv_wait_for_conversion(ADC_TypeDef *ADCx) { - while (ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) == RESET) { - if (ADC_GetFlagStatus(ADCx, ADC_FLAG_OVR) == SET) { - ADC_ClearFlag(ADCx, ADC_FLAG_OVR); - return false; - } - } - return true; -} - -void voltage_monitor_read(VoltageMonitorDevice *device, VoltageReading *reading_out) { - mutex_lock(s_adc_mutex); - - bool same_adc = (device->adc == VREF_ADC); - - // Enable ADC's APB interface clock - periph_config_enable(VREF_ADC, VREF_ADC_CLOCK); - if (!same_adc) { - periph_config_enable(device->adc, device->clock_ctrl); - } - ADC_TempSensorVrefintCmd(ENABLE); - - // Common configuration (applicable for all ADCs) - ADC_CommonInitTypeDef ADC_CommonInitStruct; - ADC_CommonStructInit(&ADC_CommonInitStruct); - // Single ADC mode - ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent; - // ADCCLK = PCLK2/2 - ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4; - // Available only for multi ADC mode - ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; - // Delay between 2 sampling phases - ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; - ADC_CommonInit(&ADC_CommonInitStruct); - - ADC_InitTypeDef ADC_InitStruct; - ADC_StructInit(&ADC_InitStruct); - ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; - ADC_InitStruct.ADC_ScanConvMode = same_adc ? ENABLE : DISABLE; - ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; - ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; - ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; - ADC_InitStruct.ADC_NbrOfConversion = same_adc ? 2 : 1; - - ADC_Init(VREF_ADC, &ADC_InitStruct); - if (!same_adc) { - ADC_Init(device->adc, &ADC_InitStruct); - } - // Regular channel configuration - ADC_RegularChannelConfig(VREF_ADC, ADC_Channel_Vrefint, 1, ADC_SampleTime_144Cycles); - ADC_RegularChannelConfig(device->adc, device->adc_channel, same_adc ? 2 : 1, - ADC_SampleTime_144Cycles); - if (same_adc) { - // ScanConvMode enabled, so need to request EOC on each channel conversion - ADC_EOCOnEachRegularChannelCmd(VREF_ADC, ENABLE); - } - - ADC_Cmd(VREF_ADC, ENABLE); - if (!same_adc) { - ADC_Cmd(device->adc, ENABLE); - } - delay_us(10); // Tstab (ADC stabilization) needs 3us and temp sensor Tstart is 10us - - *reading_out = (VoltageReading) {}; - - int i = 0; - while (i < NUM_CONVERSIONS) { - ADC_SoftwareStartConv(VREF_ADC); - if (!prv_wait_for_conversion(VREF_ADC)) { - continue; - } - uint32_t vref = ADC_GetConversionValue(VREF_ADC); - - ADC_SoftwareStartConv(device->adc); - if (!prv_wait_for_conversion(device->adc)) { - continue; - } - uint32_t vmon = ADC_GetConversionValue(device->adc); - - // Only save values and increment counter if both reads were successful - reading_out->vref_total += vref; - reading_out->vmon_total += vmon; - ++i; - } - - ADC_Cmd(VREF_ADC, DISABLE); - if (!same_adc) { - ADC_Cmd(device->adc, DISABLE); - } - ADC_TempSensorVrefintCmd(DISABLE); - periph_config_disable(VREF_ADC, VREF_ADC_CLOCK); - if (!same_adc) { - periph_config_disable(device->adc, device->clock_ctrl); - } - - mutex_unlock(s_adc_mutex); -} diff --git a/src/fw/drivers/stm32f2/watchdog.c b/src/fw/drivers/stm32f2/watchdog.c deleted file mode 100644 index 3621029d4e..0000000000 --- a/src/fw/drivers/stm32f2/watchdog.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/watchdog.h" - -#include "util/bitset.h" -#include "system/logging.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include - -void watchdog_init(void) { - IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); - - IWDG_SetPrescaler(IWDG_Prescaler_64); // ~8 seconds - IWDG_SetReload(0xfff); - - IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); - - DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP, ENABLE); -} - -void watchdog_start(void) { - IWDG_Enable(); - watchdog_feed(); -} - -// This behaves differently from the bootloader and the firmware. -void watchdog_feed(void) { - IWDG_ReloadCounter(); -} - -bool watchdog_check_reset_flag(void) { - return RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET; -} - -McuRebootReason watchdog_clear_reset_flag(void) { - McuRebootReason mcu_reboot_reason = { - .brown_out_reset = (RCC_GetFlagStatus(RCC_FLAG_BORRST) != RESET), - .pin_reset = (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET), - .power_on_reset = (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET), - .software_reset = (RCC_GetFlagStatus(RCC_FLAG_SFTRST) != RESET), - .independent_watchdog_reset = (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET), - .window_watchdog_reset = (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) != RESET), - .low_power_manager_reset = (RCC_GetFlagStatus(RCC_FLAG_LPWRRST) != RESET) - }; - - RCC_ClearFlag(); - - return mcu_reboot_reason; -} diff --git a/src/fw/drivers/stm32f4/gpio_defaults.c b/src/fw/drivers/stm32f4/gpio_defaults.c deleted file mode 100644 index fcef2b1580..0000000000 --- a/src/fw/drivers/stm32f4/gpio_defaults.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" -#include "board/board.h" - -void gpio_init_all(void) { - GPIO_InitTypeDef gpio_init = (GPIO_InitTypeDef) { - .GPIO_Mode = GPIO_Mode_AN, - .GPIO_Speed = GPIO_Speed_2MHz, - .GPIO_PuPd = GPIO_PuPd_NOPULL - }; - - // We program all the pins to be analog inputs to save power. - // We expect the following configuration after initialization code has run: - // - // GPIOA - don't touch PA0 (WKUP), PA13 (JTMS), PA14 (JTCK), PA15 (JTDI), - // PA1 & PA2 will be configured as analog pins. - // Expected non-analog mask: 0xff.f9 - // - // GPIOB - don't touch PB3 (JTDO), PB4 (NJTRST). PB0, PB1, PB2, PB5, PB11, - // PB13 unused. Expected non-analog mask: 0xd7.d8 - // - // GPIOC - PC0-PC9 are unused, PC14 (OSC32_IN) ok to set (see 8.3.13 - // of ref manual) Expected non-analog mask: 0x1c.00 - // - // GPIOD - PD0-PD15 are all for the parallel flash. - // Expected non-analog mask: 0xff.ff - // - // GPIOE - PE0-PE1 are accessory connector, PE2-PE15 are for flash. - // Expected non-analog mask: 0xff.ff - // - // GPIOF - PF6-PF9 (Audio SAI, not used?), PF5, PF10-PF12, PF15 unused. - // Expected non-analog mask: 0x60.1f - // - // GPIOG - PG0 unused, PG11 (PROG_SO) unused? Expected non-analog mask: 0xf7.fe - // - // GPIOH - Only PH0-PH1 on actual watch, PH2-PH15 unused on BB. - // Expected non-analog mask: 0x00.00 - // - // GPIOI - Only on BB, nothing used. Expected non-analog mask: 0x00.00 - - int tot_gpios = BOARD_CONFIG.num_avail_gpios; - const int num_gpios_per_port = 16; - for (uint32_t gpio_addr = (uint32_t)GPIOA; - (gpio_addr <= (uint32_t)GPIOK) && (tot_gpios > 0); gpio_addr += 0x400) { - if (gpio_addr == (uint32_t)GPIOA) { - gpio_init.GPIO_Pin = - GPIO_Pin_All & ~(GPIO_Pin_0 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15); - } else if (gpio_addr == (uint32_t)GPIOB) { - gpio_init.GPIO_Pin = GPIO_Pin_All & ~(GPIO_Pin_3 | GPIO_Pin_4); - } else { - gpio_init.GPIO_Pin = GPIO_Pin_All; - } - - gpio_use((GPIO_TypeDef *)gpio_addr); - GPIO_Init((GPIO_TypeDef *)gpio_addr, &gpio_init); - gpio_release((GPIO_TypeDef *)gpio_addr); - - tot_gpios -= num_gpios_per_port; - } -} diff --git a/src/fw/drivers/stm32f4/pwr.c b/src/fw/drivers/stm32f4/pwr.c deleted file mode 100644 index 353070a012..0000000000 --- a/src/fw/drivers/stm32f4/pwr.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/pwr.h" - -#include "drivers/periph_config.h" - -#define STM32F4_COMPATIBLE -#include - -void pwr_enable_wakeup(bool enable) { -#if PLATFORM_SILK - PWR_WakeUpPinCmd(PWR_WakeUp_Pin1, enable ? ENABLE : DISABLE); -#else - PWR_WakeUpPinCmd(enable ? ENABLE : DISABLE); -#endif -} - -void pwr_flash_power_down_stop_mode(bool power_down) { - PWR_FlashPowerDownCmd(power_down ? ENABLE : DISABLE); -} - -void pwr_access_backup_domain(bool enable_access) { - periph_config_enable(PWR, RCC_APB1Periph_PWR); - PWR_BackupAccessCmd(enable_access ? ENABLE : DISABLE); - periph_config_disable(PWR, RCC_APB1Periph_PWR); -} diff --git a/src/fw/drivers/stm32f4/rtc.c b/src/fw/drivers/stm32f4/rtc.c deleted file mode 100644 index cbe35f2c2b..0000000000 --- a/src/fw/drivers/stm32f4/rtc.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/rtc.h" -#include "drivers/rtc_private.h" - -#include "drivers/clocksource.h" -#include "drivers/exti.h" -#include "drivers/periph_config.h" -#include "drivers/rtc.h" - -#include "mcu/interrupts.h" -#include "system/logging.h" -#include "system/passert.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include "FreeRTOS.h" -#include "task.h" - -#define LSE_FREQUENCY_HZ 32768 - -static uint64_t s_alarm_set_time_milliseconds_since_epoch; - -static const int RTC_CLOCK_ASYNC_PRESCALER = 127; -static const int RTC_CLOCK_SYNC_PRESCALER = 255; - -void rtc_init(void) { - periph_config_acquire_lock(); - rtc_enable_backup_regs(); - - clocksource_lse_configure(); - - // Only initialize the RTC periphieral if it's not already enabled. - if (!(RCC->BDCR & RCC_BDCR_RTCEN)) { - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); - RCC_RTCCLKCmd(ENABLE); - - RTC_InitTypeDef rtc_init_struct; - RTC_StructInit(&rtc_init_struct); - rtc_init_struct.RTC_AsynchPrediv = RTC_CLOCK_ASYNC_PRESCALER; - rtc_init_struct.RTC_SynchPrediv = RTC_CLOCK_SYNC_PRESCALER; - RTC_Init(&rtc_init_struct); - } - - RTC_WaitForSynchro(); - - periph_config_release_lock(); - -#ifdef PBL_LOG_ENABLED - char buffer[TIME_STRING_BUFFER_SIZE]; - PBL_LOG(LOG_LEVEL_DEBUG, "Current time is <%s>", rtc_get_time_string(buffer)); -#endif -} - -void rtc_calibrate_frequency(uint32_t frequency) { - // Nothing to do here! (yet) -} - -void rtc_init_timers(void) { - // Nothing to do here! -} - -RtcTicks rtc_get_ticks(void) { - static TickType_t s_last_freertos_tick_count = 0; - static RtcTicks s_coarse_ticks = 0; - - bool ints_enabled = mcu_state_are_interrupts_enabled(); - if (ints_enabled) { - __disable_irq(); - } - - TickType_t freertos_tick_count = xTaskGetTickCount(); - if (freertos_tick_count < s_last_freertos_tick_count) { - // We rolled over! Note this will happen every 2^32 / 1024 (tick rate) seconds, which is about - // 49 days. If this function doesn't get called for 49 days we'll miss a rollover but that's - // extremely unlikely. - TickType_t rollover_amount = -1; - s_coarse_ticks += rollover_amount; - } - - s_last_freertos_tick_count = freertos_tick_count; - RtcTicks ret_value = freertos_tick_count + s_coarse_ticks; - - if (ints_enabled) { - __enable_irq(); - } - return ret_value; -} - -void rtc_set_time(time_t time) { - struct tm t; - gmtime_r(&time, &t); - - // Just die if sanitization is necessary. - PBL_ASSERTN(!rtc_sanitize_struct_tm(&t)); - - RTC_TimeTypeDef rtc_time_struct = { - .RTC_Hours = t.tm_hour, - .RTC_Minutes = t.tm_min, - .RTC_Seconds = t.tm_sec - }; - - RTC_DateTypeDef rtc_date_struct = { - .RTC_Month = t.tm_mon + 1, // RTC_Month is 1-12, tm_mon is 0-11 - .RTC_Date = t.tm_mday, - .RTC_Year = (t.tm_year % 100) // tm_year is years since 1900, RTC_Year is just 0-99 - }; - - RTC_SetTime(RTC_Format_BIN, &rtc_time_struct); - RTC_SetDate(RTC_Format_BIN, &rtc_date_struct); -} - -void rtc_get_time_ms(time_t* out_seconds, uint16_t* out_ms) { - uint32_t sub_seconds = 0; - RTC_DateTypeDef rtc_date; - RTC_TimeTypeDef rtc_time; - - // NOTE: There is a tricky rollover situation that can occur here if the date rolls over - // between when we read the date and time registers. For example: - // read date: 1/1/14 (actual time 11:59:59 PM) - // [date rolls over] - // read time: 12:00:00 AM (actual date now 1/2/14) - // At the end of this, we would think the date and time is 1/1/14 12:00:00 AM and we - // would be 24 hours behind the actual date and time. - // A similar issue can occur if the seconds change right after we've read the time register - // and before we've read the subsecond register - // To eliminate these possibilities, we read the time register both before and after - // we read the date and subsecond registers and only exit this method if we are in the same - // second both before and after. - int max_loops = 4; // If we loop more than this many times, something is seriously wrong - while (--max_loops) { - RTC_TimeTypeDef rtc_time_before; - RTC_GetTime(RTC_Format_BIN, &rtc_time_before); - RTC_GetDate(RTC_Format_BIN, &rtc_date); - - // See reference manual section 26.6.11 for SSR to milliseconds conversion - sub_seconds = RTC_GetSubSecond(); - - // Make sure neither date nor time rolled over since we read them. - RTC_GetTime(RTC_Format_BIN, &rtc_time); - // we need to read the DR again because reading the RTC_TR or RTC_SSR locks the shadow register - // for RTC_DR and leaves it in a stale state unless we read from it again - // This causes time to go backwards once a day unless we unlock it after reading from RTC_TR - RTC_GetDate(RTC_Format_BIN, &rtc_date); - if (rtc_time.RTC_Seconds == rtc_time_before.RTC_Seconds) { - break; - } - } - PBL_ASSERTN(max_loops > 0); - - struct tm current_time = { - .tm_sec = rtc_time.RTC_Seconds, - .tm_min = rtc_time.RTC_Minutes, - .tm_hour = rtc_time.RTC_Hours, - .tm_mday = rtc_date.RTC_Date, - .tm_mon = rtc_date.RTC_Month - 1, // RTC_Month is 1-12, tm_mon is 0-11 - .tm_year = rtc_date.RTC_Year + 100, // RTC_Year is 0-99, tm_year is years since 1900. - // Assumes 2000+ year, we may guess 20th or 21st century. - .tm_wday = rtc_date.RTC_WeekDay, - .tm_yday = 0, - .tm_isdst = 0 - }; - - // Verify the registers have valid values. While rtc_set_time_ms above prevents us from setting - // invalid values I want to be safe against other firmwares that we've upgraded from seeding - // bad values in our RTC registers which could lead to a reboot loop. - bool sanitization_done = rtc_sanitize_struct_tm(¤t_time); - - // Calculate our results - *out_seconds = mktime(¤t_time); - *out_ms = (uint16_t)( - ((RTC_CLOCK_SYNC_PRESCALER - sub_seconds) * 1000) / (RTC_CLOCK_SYNC_PRESCALER + 1)); - - if (sanitization_done) { - // Fix up the underlying registers so we don't have to do this again. - rtc_set_time(*out_seconds); - } -} - - -time_t rtc_get_time(void) { - time_t seconds; - uint16_t ms; - - rtc_get_time_ms(&seconds, &ms); - return seconds; -} - - -//! Tracks whether we've successfully initialized the wakeup functionality -static bool s_rtc_wakeup_initialized = false; - -static const int RTC_WAKEUP_HZ = LSE_FREQUENCY_HZ / 2; - -void rtc_alarm_init(void) { - RTC_ITConfig(RTC_IT_WUT, DISABLE); - RTC_WakeUpCmd(DISABLE); - - // Make sure this in in sync with the definition of LSE_FREQUENCY_HZ. This is the lowest setting - // for the highest frequency and therefore the highest accurracy. However, it limits us to only - // setting wakeup timers for up to 4s~ (2^16 max counter value / (32768 / 2)) in the future. - // This is fine for now as we have a regular timer register each second, so we'll never want to - // stop for more than a single second. - RTC_WakeUpClockConfig(RTC_WakeUpClock_RTCCLK_Div2); - - exti_configure_other(ExtiLineOther_RTCWakeup, ExtiTrigger_Rising); - exti_enable_other(ExtiLineOther_RTCWakeup); - - s_rtc_wakeup_initialized = true; -} - -static uint64_t prv_get_time_milliseconds_since_epoch(void) { - time_t seconds; - uint16_t milliseconds; - rtc_get_time_ms(&seconds, &milliseconds); - - return ((uint64_t)seconds * 1000) + milliseconds; -} - -void rtc_alarm_set(RtcTicks num_ticks) { - PBL_ASSERTN(s_rtc_wakeup_initialized); - - uint32_t wakeup_counter = num_ticks * RTC_WAKEUP_HZ / RTC_TICKS_HZ; - - // From 26.6.6 of the STM32F4 reference manual. - // "Note: The first assertion of WUTF occurs (WUT+1) ck_wut cycles after WUTE is set." - wakeup_counter -= 1; - - // We can only count up to a certain number. If we need to set an alarm for a longer period - // of time we need to decrease the RTC_WAKEUP_HZ value at the cost of some accuracy. - PBL_ASSERTN(wakeup_counter < 65536); - - RTC_ITConfig(RTC_IT_WUT, DISABLE); - - RTC_WakeUpCmd(DISABLE); - - RTC_SetWakeUpCounter(wakeup_counter); - - RTC_ClearFlag(RTC_FLAG_WUTF); - exti_clear_pending_other(ExtiLineOther_RTCWakeup); - RTC_ClearITPendingBit(RTC_IT_WUT); - - RTC_WakeUpCmd(ENABLE); - RTC_ITConfig(RTC_IT_WUT, ENABLE); - - s_alarm_set_time_milliseconds_since_epoch = prv_get_time_milliseconds_since_epoch(); -} - -RtcTicks rtc_alarm_get_elapsed_ticks(void) { - uint64_t now = prv_get_time_milliseconds_since_epoch(); - PBL_ASSERTN(now >= s_alarm_set_time_milliseconds_since_epoch); - uint64_t milliseconds_elapsed = now - s_alarm_set_time_milliseconds_since_epoch; - return (milliseconds_elapsed * RTC_TICKS_HZ) / 1000; -} - -bool rtc_alarm_is_initialized(void) { - return s_rtc_wakeup_initialized; -} - -void RTC_WKUP_IRQHandler(void) { - if (RTC_GetITStatus(RTC_IT_WUT) != RESET) { - RTC_WakeUpCmd(DISABLE); - - RTC_ClearITPendingBit(RTC_IT_WUT); - exti_clear_pending_other(ExtiLineOther_RTCWakeup); - } -} diff --git a/src/fw/drivers/stm32f4/temperature.c b/src/fw/drivers/stm32f4/temperature.c deleted file mode 100644 index 859b1d7da8..0000000000 --- a/src/fw/drivers/stm32f4/temperature.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "console/prompt.h" -#include "drivers/gpio.h" -#include "drivers/temperature.h" -#include "drivers/temperature/analog.h" -#include "drivers/voltage_monitor.h" -#include "drivers/periph_config.h" -#include "kernel/util/sleep.h" -#include "mfg/mfg_info.h" -#include "services/common/regular_timer.h" -#include "system/logging.h" -#include "system/passert.h" - -#include - -void temperature_init(void) { - -} - -int32_t temperature_read(void) { - VoltageReading reading; - voltage_monitor_read(TEMPERATURE_SENSOR->voltage_monitor, &reading); - - - // See battery_adc_conversion.c for more details on how this works - // convert from sum-of-12-bits to sum-of-mVs - // by multiplying by 1800/4095 which is the same as 40/91 - const uint32_t vref_mv_sum = reading.vref_total * 40 / 91; - const uint32_t vmon_mv_sum = reading.vmon_total * 40 / 91; - - // Multiply vmon/vref * 2/3 to find a percentage of the full scale and then multiply it back - // by 1800 to get back to mV. - int32_t millivolts = ((vmon_mv_sum * 1800 * 2) / (vref_mv_sum * 3)); - - // convert to temperature (see stm32f4 ref manual, section 13.10) - int32_t millidegreesC = ((millivolts - TEMPERATURE_SENSOR->millivolts_ref) * - TEMPERATURE_SENSOR->slope_denominator / - TEMPERATURE_SENSOR->slope_numerator) + - TEMPERATURE_SENSOR->millidegrees_ref; - - - return millidegreesC; -} - -void command_temperature_read(void) { - char buffer[32]; - prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRId32" ", temperature_read()); -} diff --git a/src/fw/drivers/stm32f412/qspi.c b/src/fw/drivers/stm32f412/qspi.c deleted file mode 100644 index 00bbca2df8..0000000000 --- a/src/fw/drivers/stm32f412/qspi.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/qspi.h" -#include "drivers/qspi_definitions.h" - -#include "board/board.h" -#include "drivers/dma.h" -#include "drivers/flash/flash_impl.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "kernel/util/delay.h" -#include "kernel/util/stop.h" -#include "mcu/cache.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#include - -#include "FreeRTOS.h" -#include "semphr.h" - -//! Address value which signifies no address being sent -#define QSPI_ADDR_NO_ADDR (UINT32_MAX) -//! Word size for DMA reads -#define QSPI_DMA_READ_WORD_SIZE (4) - - -void qspi_init(QSPIPort *dev, uint32_t flash_size) { - // Init the DMA semaphore, used for read - dev->state->dma_semaphore = xSemaphoreCreateBinary(); - dma_request_init(dev->dma); - - // init GPIOs - gpio_af_init(&dev->cs_gpio, GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_NOPULL); - gpio_af_init(&dev->clk_gpio, GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_NOPULL); - for (int i = 0; i < QSPI_NUM_DATA_PINS; i++) { - gpio_af_init(&dev->data_gpio[i], GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_NOPULL); - } - - // calculate the prescaler - RCC_ClocksTypeDef RCC_ClocksStatus; - RCC_GetClocksFreq(&RCC_ClocksStatus); - int prescaler = RCC_ClocksStatus.HCLK_Frequency / dev->clock_speed_hz; - if ((RCC_ClocksStatus.HCLK_Frequency / prescaler) > dev->clock_speed_hz) { - // The desired prescaler is not an integer, so we'll round up so that the clock is never - // faster than the desired frequency. - prescaler++; - } - - // enable clock while we initialize QSPI - qspi_use(dev); - - // round the flash size up to the nearest power of 2 and calculate QSPI_FSize - uint32_t fsize_value = ceil_log_two(flash_size) - 1; - PBL_ASSERTN(flash_size == (uint32_t)1 << ceil_log_two(flash_size)); - - // Init QSPI peripheral - QSPI_InitTypeDef qspi_config; - QSPI_StructInit(&qspi_config); - qspi_config.QSPI_SShift = QSPI_SShift_HalfCycleShift; - // QSPI clock = AHB / (1 + QSPI_Prescaler) - qspi_config.QSPI_Prescaler = prescaler - 1; - qspi_config.QSPI_CKMode = QSPI_CKMode_Mode0; - qspi_config.QSPI_CSHTime = QSPI_CSHTime_1Cycle; - qspi_config.QSPI_FSize = fsize_value; - qspi_config.QSPI_FSelect = QSPI_FSelect_1; - qspi_config.QSPI_DFlash = QSPI_DFlash_Disable; - QSPI_Init(&qspi_config); - QSPI_Cmd(ENABLE); - - qspi_release(dev); -} - -void qspi_use(QSPIPort *dev) { - if (dev->state->use_count++ == 0) { - periph_config_enable(QUADSPI, dev->clock_ctrl); - } -} - -void qspi_release(QSPIPort *dev) { - PBL_ASSERTN(dev->state->use_count > 0); - if (--dev->state->use_count == 0) { - periph_config_disable(QUADSPI, dev->clock_ctrl); - } -} - -static void prv_set_num_data_bytes(uint32_t length) { - // From the docs: QSPI_DataLength: Number of data to be retrieved, value+1. - // so 0 is 1 byte, so we substract 1 from the length. -1 is read the entire flash length. - PBL_ASSERTN(length > 0); - - QSPI_SetDataLength(length - 1); -} - -#if DEBUG_QSPI_WAITS -#define QSPI_WAIT_TIME (100000) - -// These are a bit dangerous on long erase commands, but they can also be very useful to find out -// why the QSPI driver is locking up when doing development - -static void prv_wait_for_transfer_complete(void) { - int i = 0; - while (QSPI_GetFlagStatus(QSPI_FLAG_TC) == RESET) { - if (++i > QSPI_WAIT_TIME) { - break; - } - } - PBL_ASSERT(i < QSPI_WAIT_TIME, "Waited too long for the QSPI transfer to complete"); -} - -static void prv_wait_for_not_busy(void) { - int i = 0; - while (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) != RESET) { - if (++i > QSPI_WAIT_TIME) { - break; - } - } - PBL_ASSERT(i < QSPI_WAIT_TIME, "Waited too long for the QSPI to become not busy"); -} - -#else - -static void prv_wait_for_transfer_complete(void) { - while (QSPI_GetFlagStatus(QSPI_FLAG_TC) == RESET) { } -} - -static void prv_wait_for_not_busy(void) { - while (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) != RESET) { } -} - -#endif - -static void prv_read_bytes(uint8_t *buffer, size_t buffer_size) { - for (size_t i = 0; i < buffer_size; ++i) { - buffer[i] = QSPI_ReceiveData8(); - } -} - -static void prv_set_ddr_enabled(bool enabled) { - PBL_ASSERTN(!QSPI_GetFlagStatus(QSPI_FLAG_BUSY)); - if (enabled) { - QUADSPI->CR &= ~QUADSPI_CR_SSHIFT; - } else { - QUADSPI->CR |= QUADSPI_CR_SSHIFT; - } -} - -// CCR register Bits from LSB to MSB -// INSTRUCTION[7:0]: Instruction -// IMODE[1:0]: Instruction Mode -// ADMODE[1:0]: Address Mode -// ADSIZE[1:0]: Address Size -// ABMODE[1:0]: Alternate Bytes Mode -// ABSIZE[1:0]: Instruction Mode -// DCYC[4:0]: Dummy Cycles -// RESERVED -// DMODE[1:0]: Data Mode -// FMODE[1:0]: Functional Mode -// SIOO: Send Instruction Only Once Mode -// RESERVED -// DHHC: Delay Half Hclk Cycle -// DDRM: Double Data Rate Mode - -//! Mask to clear out the valid bits while leaving the reserved bits untouched -#define QSPI_CCR_CLEAR_MASK (~(QUADSPI_CCR_INSTRUCTION | \ - QUADSPI_CCR_IMODE | \ - QUADSPI_CCR_ADMODE | \ - QUADSPI_CCR_ADSIZE | \ - QUADSPI_CCR_ABMODE | \ - QUADSPI_CCR_ABSIZE | \ - QUADSPI_CCR_DCYC | \ - QUADSPI_CCR_DMODE | \ - QUADSPI_CCR_FMODE | \ - QUADSPI_CCR_SIOO | \ - QUADSPI_CCR_DHHC | \ - QUADSPI_CCR_DDRM)) - -static void prv_set_comm_config(uint32_t modes_bitset, uint32_t dummy_cycles) { - uint32_t ccr = QUADSPI->CCR; - ccr &= QSPI_CCR_CLEAR_MASK; - ccr |= modes_bitset; - ccr |= (dummy_cycles << 18); - QUADSPI->CCR = ccr; -} - -static bool prv_dma_irq_handler(DMARequest *request, void *context) { - QSPIPort *dev = context; - QSPI_DMACmd(DISABLE); - - signed portBASE_TYPE was_higher_priority_task_woken = pdFALSE; - xSemaphoreGiveFromISR(dev->state->dma_semaphore, &was_higher_priority_task_woken); - return was_higher_priority_task_woken != pdFALSE; -} - -static void prv_config_indirect_read(QSPIPort *dev, uint8_t instruction, uint32_t addr, - uint8_t dummy_cycles, bool is_ddr) { - prv_set_ddr_enabled(is_ddr); - - uint32_t modes_bitset = 0; - modes_bitset |= is_ddr ? QSPI_ComConfig_DDRMode_Enable : QSPI_ComConfig_DDRMode_Disable; - modes_bitset |= is_ddr ? QSPI_ComConfig_DHHC_Enable : QSPI_ComConfig_DHHC_Disable; - modes_bitset |= QSPI_ComConfig_FMode_Indirect_Read; - modes_bitset |= QSPI_ComConfig_DMode_4Line; - modes_bitset |= QSPI_ComConfig_IMode_4Line; - modes_bitset |= instruction; - if (addr != QSPI_ADDR_NO_ADDR) { - modes_bitset |= QSPI_ComConfig_ADMode_4Line; - modes_bitset |= QSPI_ComConfig_ADSize_24bit; - } - prv_set_comm_config(modes_bitset, dummy_cycles); - - if (addr != QSPI_ADDR_NO_ADDR) { - QSPI_SetAddress(addr); - } -} - -static void prv_indirect_read(QSPIPort *dev, uint8_t instruction, uint32_t addr, - uint8_t dummy_cycles, void *buffer, uint32_t length, bool is_ddr) { - prv_set_num_data_bytes(length); - - prv_config_indirect_read(dev, instruction, addr, dummy_cycles, is_ddr); - - prv_read_bytes(buffer, length); - - QSPI_ClearFlag(QSPI_FLAG_TC); - prv_wait_for_not_busy(); -} - -void qspi_indirect_read_no_addr(QSPIPort *dev, uint8_t instruction, uint8_t dummy_cycles, - void *buffer, uint32_t length, bool is_ddr) { - prv_indirect_read(dev, instruction, QSPI_ADDR_NO_ADDR, dummy_cycles, buffer, length, is_ddr); -} -void qspi_indirect_read(QSPIPort *dev, uint8_t instruction, uint32_t addr, uint8_t dummy_cycles, - void *buffer, uint32_t length, bool is_ddr) { - prv_indirect_read(dev, instruction, addr, dummy_cycles, buffer, length, is_ddr); -} - -void qspi_indirect_read_dma(QSPIPort *dev, uint8_t instruction, uint32_t start_addr, - uint8_t dummy_cycles, void *buffer, uint32_t length, bool is_ddr) { - // DMA reads are most efficient when doing 32bits at a time. The QSPI bus runs at 100Mhz - // and we need to be efficient in handling the data to use it to its full capability. - // - // So this function is broken into 3 parts: - // 1. Do reads 1 byte at a time until buffer_ptr is word-aligned - // 2. Do 32-bit DMA transfers for as much as possible - // 3. Do reads 1 bytes at a time to deal with non-aligned acceses at the end - - const uint32_t word_mask = dcache_alignment_mask_minimum(QSPI_DMA_READ_WORD_SIZE); - const uintptr_t buffer_address = (uintptr_t)buffer; - - const uintptr_t last_address = buffer_address + length; - const uintptr_t last_address_aligned = last_address & (~word_mask); - - const uintptr_t start_address_aligned = ((buffer_address + word_mask) & (~word_mask)); - - uint32_t leading_bytes = start_address_aligned - buffer_address; - uint32_t trailing_bytes = last_address - last_address_aligned; - - uint32_t dma_size = last_address_aligned - start_address_aligned; - - if (last_address_aligned < start_address_aligned) { - dma_size = 0; - leading_bytes = length; - trailing_bytes = 0; - } - - prv_set_num_data_bytes(length); - - prv_config_indirect_read(dev, instruction, start_addr, dummy_cycles, is_ddr); - - if (leading_bytes > 0) { - prv_read_bytes(buffer, leading_bytes); - } - - if (dma_size > 0) { - // Do 4 bytes at a time for DMA - QSPI_SetFIFOThreshold(QSPI_DMA_READ_WORD_SIZE); - - QSPI_DMACmd(ENABLE); - stop_mode_disable(InhibitorFlash); - dma_request_start_direct(dev->dma, (void *)start_address_aligned, (void *)&QUADSPI->DR, - dma_size, prv_dma_irq_handler, (void *)dev); - - xSemaphoreTake(dev->state->dma_semaphore, portMAX_DELAY); - stop_mode_enable(InhibitorFlash); - } - - if (trailing_bytes > 0) { - prv_read_bytes((void *)last_address_aligned, trailing_bytes); - } -} - -static void prv_indirect_write(QSPIPort *dev, uint8_t instruction, uint32_t addr, - const void *buffer, uint32_t length) { - if (length) { - PBL_ASSERTN(buffer); - prv_set_num_data_bytes(length); - } else { - PBL_ASSERTN(!buffer); - } - - prv_set_ddr_enabled(false); - - uint32_t modes_bitset = 0; - modes_bitset |= QSPI_ComConfig_FMode_Indirect_Write; - modes_bitset |= QSPI_ComConfig_IMode_4Line; - modes_bitset |= instruction; - if (addr != QSPI_ADDR_NO_ADDR) { - modes_bitset |= QSPI_ComConfig_ADMode_4Line; - modes_bitset |= QSPI_ComConfig_ADSize_24bit; - } - if (length) { - modes_bitset |= QSPI_ComConfig_DMode_4Line; - } - prv_set_comm_config(modes_bitset, 0); - - if (addr != QSPI_ADDR_NO_ADDR) { - QSPI_SetAddress(addr); - } - - const uint8_t *read_ptr = buffer; - for (uint32_t i = 0; i < length; ++i) { - // Note: this will stall the CPU when the FIFO gets full while data is being sent. - // For performance reasons, we should replace it with DMA in the future - // PBL-28805 - QSPI_SendData8(read_ptr[i]); - } - - prv_wait_for_transfer_complete(); - QSPI_ClearFlag(QSPI_FLAG_TC); - prv_wait_for_not_busy(); -} - -void qspi_indirect_write_no_addr(QSPIPort *dev, uint8_t instruction, const void *buffer, - uint32_t length) { - prv_indirect_write(dev, instruction, QSPI_ADDR_NO_ADDR, buffer, length); -} - -void qspi_indirect_write(QSPIPort *dev, uint8_t instruction, uint32_t addr, const void *buffer, - uint32_t length) { - prv_indirect_write(dev, instruction, addr, buffer, length); -} - -void qspi_indirect_write_no_addr_1line(QSPIPort *dev, uint8_t instruction) { - prv_set_ddr_enabled(false); - - uint32_t modes_bitset = 0; - modes_bitset |= QSPI_ComConfig_FMode_Indirect_Write; - modes_bitset |= QSPI_ComConfig_IMode_1Line; - modes_bitset |= instruction; - prv_set_comm_config(modes_bitset, 0); - - prv_wait_for_transfer_complete(); - QSPI_ClearFlag(QSPI_FLAG_TC); - prv_wait_for_not_busy(); -} - -bool qspi_poll_bit(QSPIPort *dev, uint8_t instruction, uint8_t bit_mask, bool should_be_set, - uint32_t timeout_us) { - prv_set_num_data_bytes(1); - - // Set autopolling on the register - QSPI_AutoPollingMode_SetInterval(dev->auto_polling_interval); - QSPI_AutoPollingMode_Config(should_be_set ? bit_mask : 0, bit_mask, QSPI_PMM_AND); - QSPI_AutoPollingModeStopCmd(ENABLE); - - prv_set_ddr_enabled(false); - - // Prepare transaction - uint32_t modes_bitset = 0; - modes_bitset |= QSPI_ComConfig_FMode_Auto_Polling; - modes_bitset |= QSPI_ComConfig_DMode_4Line; - modes_bitset |= QSPI_ComConfig_IMode_4Line; - modes_bitset |= instruction; - prv_set_comm_config(modes_bitset, 0); - - uint32_t loops = 0; - while (QSPI_GetFlagStatus(QSPI_FLAG_SM) == RESET) { - if ((timeout_us != QSPI_NO_TIMEOUT) && (++loops > timeout_us)) { - PBL_LOG(LOG_LEVEL_ERROR, "Timeout waiting for a bit!?!?"); - return false; - } - delay_us(1); - } - - // stop polling mode - QSPI_AbortRequest(); - prv_wait_for_not_busy(); - - return true; -} - -void qspi_mmap_start(QSPIPort *dev, uint8_t instruction, uint32_t addr, uint8_t dummy_cycles, - uint32_t length, bool is_ddr) { - dcache_invalidate((void *)(uintptr_t)(QSPI_MMAP_BASE_ADDRESS + addr), length); - - prv_set_ddr_enabled(is_ddr); - - uint32_t modes_bitset = 0; - modes_bitset |= is_ddr ? QSPI_ComConfig_DDRMode_Enable : QSPI_ComConfig_DDRMode_Disable; - modes_bitset |= is_ddr ? QSPI_ComConfig_DHHC_Enable : QSPI_ComConfig_DHHC_Disable; - modes_bitset |= QSPI_ComConfig_FMode_Memory_Mapped; - modes_bitset |= QSPI_ComConfig_DMode_4Line; - modes_bitset |= QSPI_ComConfig_IMode_4Line; - modes_bitset |= QSPI_ComConfig_ADMode_4Line; - modes_bitset |= QSPI_ComConfig_ADSize_24bit; - modes_bitset |= instruction; - - prv_set_comm_config(modes_bitset, dummy_cycles); - - // The QSPI will prefetch bytes as long as nCS is low. This causes the flash part to draw a lot - // more power (10mA vs 10uA in the case of Silk). Set the timeout such that the prefetch will - // stop after 10 clock cycles of inactivity. - QSPI_MemoryMappedMode_SetTimeout(10); - - // HACK ALERT: It seems like the MCU may send the wrong address for the first MMAP after certain - // flash operations (we have seen it with an indirect read). To work around this, kick off one - // read sufficiently far away from the area we want to read. This seems to reset the QSPI - // controller back into a good state. This workaround is a little wasteful as it kicks off a 32 - // byte flash read but at 50MHz that should only take ~1.5us: - // ((1byte +3byteaddr + 32bytes data) * 2 clocks/byte + 4 dummy_clocks) / 50Mhz = 1.52us - - volatile uint8_t *qspi_wa_addr = (uint8_t *)(QSPI_MMAP_BASE_ADDRESS + ((addr > 128) ? 0 : 256)); - dcache_invalidate((void *)qspi_wa_addr, 1); - (void)*qspi_wa_addr; -} - -void qspi_mmap_stop(QSPIPort *dev) { - QSPI_AbortRequest(); - prv_wait_for_not_busy(); -} diff --git a/src/fw/drivers/stm32f412/voltage_monitor.c b/src/fw/drivers/stm32f412/voltage_monitor.c deleted file mode 100644 index 45aa4f517c..0000000000 --- a/src/fw/drivers/stm32f412/voltage_monitor.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/voltage_monitor.h" -#include "kernel/util/delay.h" -#include "os/mutex.h" -#include "system/passert.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#include - -static PebbleMutex *s_adc_mutex; - -void voltage_monitor_init(void) { - s_adc_mutex = mutex_create(); -} - -void voltage_monitor_device_init(const VoltageMonitorDevice *device) { - gpio_analog_init(&device->input); -} - -//! It takes ~12µs to get our ADC readings. From time to time, we're busy -//! processing elsewhere for upwards of 25µs and end up getting overrun issues. -//! -//! When OVR occurs, we clear both the OVR flag and the EOC flag. -//! The OVR flag always needs to be cleared so that conversion can be restarted. -//! -//! -//! For the first conversion, it is possible that OVR can occur between -//! seeing EOC being set and then actually reading the conversion value. When -//! that occurs, we will catch the OVR when waiting for the next conversion, and -//! restart the group. In this case, it is mandatory to clear the EOC, so that -//! we can restart the conversion group. Clearing EOC on OVR is always safe when -//! using only two channels since clearing EOC will not start a new conversion. -//! -//! If we make it to the last conversion without seeing OVR, then we know that -//! no OVR will occur and we don't need to worry about overrun before reading -//! the data back. -static bool prv_wait_for_conversion(void) { - while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET) { - if (ADC_GetFlagStatus(ADC1, ADC_FLAG_OVR) == SET) { - ADC_ClearFlag(ADC1, ADC_FLAG_OVR); - ADC_ClearFlag(ADC1, ADC_FLAG_EOC); - return false; - } - } - return true; -} - -void voltage_monitor_read(const VoltageMonitorDevice *device, VoltageReading *reading_out) { - PBL_ASSERTN(device->adc == ADC1); - mutex_lock(s_adc_mutex); - - periph_config_enable(ADC1, RCC_APB2Periph_ADC1); - ADC_TempSensorVrefintCmd(ENABLE); - - ADC_CommonInitTypeDef ADC_CommonInitStruct; - // Single ADC mode - ADC_CommonStructInit(&ADC_CommonInitStruct); - ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent; - // ADCCLK = PCLK2/2 - ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4; - // Available only for multi ADC mode - ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; - // Delay between 2 sampling phases - ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; - ADC_CommonInit(&ADC_CommonInitStruct); - - ADC_InitTypeDef ADC_InitStruct; - ADC_StructInit(&ADC_InitStruct); - ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; - // Scan multiple channels on ADC1 - ADC_InitStruct.ADC_ScanConvMode = ENABLE; - ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; - ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; - ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; - ADC_InitStruct.ADC_NbrOfConversion = 2; - - ADC_Init(ADC1, &ADC_InitStruct); - ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 1, ADC_SampleTime_144Cycles); - ADC_RegularChannelConfig(ADC1, device->adc_channel, 2, ADC_SampleTime_144Cycles); - // ScanConvMode enabled, so need to request EOC on each channel conversion - ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE); - - ADC_Cmd(ADC1, ENABLE); - - delay_us(3); // Wait Tstab = 3us for ADC to stabilize - - *reading_out = (VoltageReading) {}; - - int i = 0; - while (i < NUM_CONVERSIONS) { - ADC_SoftwareStartConv(ADC1); // Restart the conversion group - if (!prv_wait_for_conversion()) { - continue; - } - const uint16_t vref = ADC_GetConversionValue(ADC1); - - if (!prv_wait_for_conversion()) { - continue; - } - const uint16_t vmon = ADC_GetConversionValue(ADC1); - - // Only save values and increment counter if both reads were successful - reading_out->vref_total += vref; - reading_out->vmon_total += vmon; - ++i; - } - - ADC_Cmd(ADC1, DISABLE); - ADC_TempSensorVrefintCmd(DISABLE); - periph_config_disable(ADC1, RCC_APB2Periph_ADC1); - - mutex_unlock(s_adc_mutex); -} diff --git a/src/fw/drivers/stm32f7/i2c_hal.c b/src/fw/drivers/stm32f7/i2c_hal.c deleted file mode 100644 index 85f6218963..0000000000 --- a/src/fw/drivers/stm32f7/i2c_hal.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/i2c_hal.h" -#include "drivers/i2c_definitions.h" -#include "drivers/stm32f7/i2c_hal_definitions.h" -#include "drivers/stm32f7/i2c_timingr.h" - -#include "drivers/periph_config.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "FreeRTOS.h" - -#define STM32F7_COMPATIBLE -#include - -#define I2C_IRQ_PRIORITY (0xc) - -#define CR1_CLEAR_MASK (0x00CFE0FF) - -#define CR2_CLEAR_MASK (0x07FF7FFF) -#define CR2_NBYTES_OFFSET (16) -#define CR2_TRANSFER_SETUP_MASK (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | \ - I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | \ - I2C_CR2_STOP) - -static void prv_i2c_deinit(I2CBus *bus) { - // Reset the clock to the peripheral - RCC_APB1PeriphResetCmd(bus->hal->clock_ctrl, ENABLE); - RCC_APB1PeriphResetCmd(bus->hal->clock_ctrl, DISABLE); -} - -void i2c_hal_init(I2CBus *bus) { - NVIC_SetPriority(bus->hal->ev_irq_channel, I2C_IRQ_PRIORITY); - NVIC_SetPriority(bus->hal->er_irq_channel, I2C_IRQ_PRIORITY); - NVIC_EnableIRQ(bus->hal->ev_irq_channel); - NVIC_EnableIRQ(bus->hal->er_irq_channel); - prv_i2c_deinit(bus); -} - -void i2c_hal_enable(I2CBus *bus) { - const I2CBusHal *hal = bus->hal; - periph_config_enable(hal->i2c, hal->clock_ctrl); - - // Soft reset of the state machine and status bits by disabling the peripheral. - // Note: PE must be low for 3 APB cycles after this is done for the reset to be successful - hal->i2c->CR1 &= ~I2C_CR1_PE; - - hal->i2c->CR1 &= ~CR1_CLEAR_MASK; - - // Set the timing register - RCC_ClocksTypeDef rcc_clocks; - RCC_GetClocksFreq(&rcc_clocks); - const uint32_t timingr = i2c_timingr_calculate( - rcc_clocks.PCLK1_Frequency, hal->bus_mode, hal->clock_speed, - hal->rise_time_ns, hal->fall_time_ns); - PBL_ASSERT(timingr != I2C_TIMINGR_INVALID_VALUE, "Could not calculate TIMINGR values!"); - hal->i2c->TIMINGR = timingr; - - // I2C only used as a master; disable slave address acknowledgement - hal->i2c->OAR1 = 0; - hal->i2c->OAR2 = 0; - - // Enable i2c Peripheral; clear any configured interrupt bits; use analog filter - hal->i2c->CR1 |= I2C_CR1_PE; - - // Clear CR2, making it ready for the next transaction - hal->i2c->CR2 &= ~CR2_CLEAR_MASK; -} - -void i2c_hal_disable(I2CBus *bus) { - periph_config_disable(bus->hal->i2c, bus->hal->clock_ctrl); - prv_i2c_deinit(bus); -} - -bool i2c_hal_is_busy(I2CBus *bus) { - return ((bus->hal->i2c->ISR & I2C_ISR_BUSY) != 0); -} - -static void prv_disable_all_interrupts(I2CBus *bus) { - bus->hal->i2c->CR1 &= ~(I2C_CR1_TXIE | - I2C_CR1_RXIE | - I2C_CR1_TCIE | - I2C_CR1_NACKIE | - I2C_CR1_ERRIE); -} - -void i2c_hal_abort_transfer(I2CBus *bus) { - // Disable all interrupts on the bus - prv_disable_all_interrupts(bus); - // Generate a stop condition - bus->hal->i2c->CR2 |= I2C_CR2_STOP; -} - -void i2c_hal_init_transfer(I2CBus *bus) { - I2CTransfer *transfer = &bus->state->transfer; - - if (transfer->type == I2CTransferType_SendRegisterAddress) { - transfer->state = I2CTransferState_WriteRegAddress; - } else { - if (transfer->direction == I2CTransferDirection_Read) { - transfer->state = I2CTransferState_ReadData; - } else { - transfer->state = I2CTransferState_WriteData; - } - } -} - -static void prv_enable_interrupts(I2CBus *bus) { - bus->hal->i2c->CR1 |= I2C_CR1_ERRIE | // Enable error interrupt - I2C_CR1_NACKIE | // Enable NACK interrupt - I2C_CR1_TCIE | // Enable transfer complete interrupt - I2C_CR1_TXIE; // Enable transmit interrupt - if (bus->state->transfer.direction == I2CTransferDirection_Read) { - bus->hal->i2c->CR1 |= I2C_CR1_RXIE; // Enable receive interrupt - } -} - -static void prv_resume_transfer(I2CBus *bus, bool generate_start) { - const I2CTransfer *transfer = &bus->state->transfer; - uint32_t cr2_value = transfer->device_address & I2C_CR2_SADD; - - if ((transfer->direction == I2CTransferDirection_Read) && - (transfer->state != I2CTransferState_WriteRegAddress)) { - cr2_value |= I2C_CR2_RD_WRN; - } - - const uint32_t remaining = bus->state->transfer.size - bus->state->transfer.idx; - if (remaining > UINT8_MAX) { - cr2_value |= I2C_CR2_RELOAD; - cr2_value |= I2C_CR2_NBYTES; - } else { - cr2_value |= (remaining << CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES; - } - - if (generate_start) { - cr2_value |= I2C_CR2_START; - } - - bus->hal->i2c->CR2 = cr2_value; -} - -void i2c_hal_start_transfer(I2CBus *bus) { - prv_enable_interrupts(bus); - if (bus->state->transfer.state == I2CTransferState_WriteRegAddress) { - // For writes, we'll reload with the payload once we send the address. Otherwise, we'd need to - // send a repeated start, which we don't want to do. - const bool reload = bus->state->transfer.direction == I2CTransferDirection_Write; - bus->hal->i2c->CR2 = (bus->state->transfer.device_address & I2C_CR2_SADD) | - (1 << CR2_NBYTES_OFFSET) | - (reload ? I2C_CR2_RELOAD : 0) | - I2C_CR2_START; - } else { - prv_resume_transfer(bus, true /* generate_start */); - } -} - -/*------------------------INTERRUPT FUNCTIONS--------------------------*/ -static portBASE_TYPE prv_end_transfer_irq(I2CBus *bus, I2CTransferEvent event) { - prv_disable_all_interrupts(bus); - - // Generate stop condition - bus->hal->i2c->CR2 |= I2C_CR2_STOP; - bus->state->transfer.state = I2CTransferState_Complete; - - return i2c_handle_transfer_event(bus, event); -} - -//! Handle an IRQ event on the specified \a bus -static portBASE_TYPE prv_event_irq_handler(I2CBus *bus) { - I2C_TypeDef *i2c = bus->hal->i2c; - I2CTransfer *transfer = &bus->state->transfer; - switch (transfer->state) { - case I2CTransferState_WriteRegAddress: - if ((i2c->ISR & I2C_ISR_TXIS) != 0) { - i2c->TXDR = transfer->register_address; - } - if ((transfer->direction == I2CTransferDirection_Read) && (i2c->ISR & I2C_ISR_TC)) { - // done writing the register address for a read request - start a read request - transfer->state = I2CTransferState_ReadData; - prv_resume_transfer(bus, true /* generate_start */); - } else if ((transfer->direction == I2CTransferDirection_Write) && (i2c->ISR & I2C_ISR_TCR)) { - // done writing the register address for a write request - "reload" the write payload - transfer->state = I2CTransferState_WriteData; - prv_resume_transfer(bus, false /* !generate_start */); - } - if ((i2c->ISR & I2C_ISR_NACKF) != 0) { - i2c->ICR |= I2C_ICR_NACKCF; - return i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived); - } - break; - - case I2CTransferState_ReadData: - if ((i2c->ISR & I2C_ISR_RXNE) != 0) { - transfer->data[transfer->idx++] = i2c->RXDR; - } - if ((i2c->ISR & I2C_ISR_TCR) != 0) { - prv_resume_transfer(bus, false /* !generate_start */); - } - if ((i2c->ISR & I2C_ISR_TC) != 0) { - return prv_end_transfer_irq(bus, I2CTransferEvent_TransferComplete); - } - break; - - case I2CTransferState_WriteData: - if ((i2c->ISR & I2C_ISR_TXIS) != 0) { - i2c->TXDR = transfer->data[transfer->idx++]; - } - if ((i2c->ISR & I2C_ISR_NACKF) != 0) { - i2c->ICR |= I2C_ICR_NACKCF; - return i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived); - } - if ((i2c->ISR & I2C_ISR_TCR) != 0) { - prv_resume_transfer(bus, false /* !generate_start */); - } - if ((i2c->ISR & I2C_ISR_TC) != 0) { - return prv_end_transfer_irq(bus, I2CTransferEvent_TransferComplete); - } - break; - - case I2CTransferState_Complete: - if (i2c->ISR & I2C_ISR_TXE) { - // We seem to get a spurious interrupt after the last byte is sent - // There is no bit to specifically disable this interrupt and the interrupt may have already - // been pended when we would disable it, so just handle it silently. - break; - } - // Fallthrough - - // These extra states were defined for the F4 implementation but are not necessary for the F7, - // because the interrupt scheme is a lot nicer. - case I2CTransferState_RepeatStart: - case I2CTransferState_EndWrite: - case I2CTransferState_WaitForData: - case I2CTransferState_WriteAddressRx: - case I2CTransferState_WriteAddressTx: - default: - WTF; - } - - return pdFALSE; -} - -static portBASE_TYPE prv_error_irq_handler(I2CBus *bus) { - I2C_TypeDef *i2c = bus->hal->i2c; - if ((i2c->ISR & I2C_ISR_BERR) != 0) { - i2c->ICR |= I2C_ICR_BERRCF; - } - if ((i2c->ISR & I2C_ISR_OVR) != 0) { - i2c->ICR |= I2C_ICR_OVRCF; - } - if ((i2c->ISR & I2C_ISR_ARLO) != 0) { - i2c->ICR |= I2C_ICR_ARLOCF; - } - return prv_end_transfer_irq(bus, I2CTransferEvent_Error); -} - -void i2c_hal_event_irq_handler(I2CBus *bus) { - portEND_SWITCHING_ISR(prv_event_irq_handler(bus)); -} - -void i2c_hal_error_irq_handler(I2CBus *bus) { - portEND_SWITCHING_ISR(prv_error_irq_handler(bus)); -} diff --git a/src/fw/drivers/stm32f7/i2c_hal_definitions.h b/src/fw/drivers/stm32f7/i2c_hal_definitions.h deleted file mode 100644 index 01e9987dfd..0000000000 --- a/src/fw/drivers/stm32f7/i2c_hal_definitions.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/stm32f7/i2c_timingr.h" - -#include -#include - -typedef struct I2CBusHal { - I2C_TypeDef *const i2c; - uint32_t clock_ctrl; ///< Peripheral clock control flag - I2CBusMode bus_mode; - uint32_t clock_speed; ///< Bus clock speed - uint32_t rise_time_ns; ///< SCL/SDA rise time in nanoseconds - uint32_t fall_time_ns; ///< SCL/SDA fall time in nanoseconds - IRQn_Type ev_irq_channel; ///< I2C Event interrupt (One of X_IRQn). For example, I2C1_EV_IRQn. - IRQn_Type er_irq_channel; ///< I2C Error interrupt (One of X_IRQn). For example, I2C1_ER_IRQn. -} I2CBusHal; - -void i2c_hal_event_irq_handler(I2CBus *device); -void i2c_hal_error_irq_handler(I2CBus *device); diff --git a/src/fw/drivers/stm32f7/i2c_timingr.c b/src/fw/drivers/stm32f7/i2c_timingr.c deleted file mode 100644 index 388401eb89..0000000000 --- a/src/fw/drivers/stm32f7/i2c_timingr.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "i2c_timingr.h" - -#include "util/attributes.h" -#include "util/math.h" -#include "util/units.h" - -static const struct { - uint32_t clock_speed_max; - uint32_t min_scl_low_ps; - uint32_t min_scl_high_ps; - uint32_t min_sda_setup_ps; -} s_timing_data[] = { - [I2CBusMode_Standard] = { - .clock_speed_max = 100000, - .min_scl_low_ps = 4700000, - .min_scl_high_ps = 4000000, - .min_sda_setup_ps = 250000, - }, - [I2CBusMode_FastMode] = { - .clock_speed_max = 400000, - .min_scl_low_ps = 1300000, - .min_scl_high_ps = 600000, - .min_sda_setup_ps = 100000, - }, -}; - -// Per the STM32F7 reference manual, the I2C peripheral adds 2-3 cycles to sync SCL with I2CCLK. In -// practice, 3 has been observed. -#define I2C_SYNC_CYCLES (3) - -#define TIMINGR_MAX_SCLL (0x100) // 8 bits storing (SCLL - 1) -#define TIMINGR_MAX_SCLH (0x100) // 8 bits storing (SCLH - 1) -#define TIMINGR_MAX_SCLDEL (0x10) // 4 bits storing (SCLDEL - 1) -#define TIMINGR_MAX_PRESC (0x10) // 4 bits storing (PRESC - 1) - -typedef union PACKED TIMINGR { - struct { - uint32_t SCLL:8; - uint32_t SCLH:8; - uint32_t SDADEL:4; - uint32_t SCLDEL:4; - uint32_t reserved:4; - uint32_t PRESC:4; - }; - uint32_t reg; -} TIMINGR; -_Static_assert(sizeof(TIMINGR) == sizeof(uint32_t), "Invalid TIMINGR size"); - -uint32_t i2c_timingr_calculate(uint32_t i2c_clk_frequency, - I2CBusMode bus_mode, - uint32_t target_bus_frequency, - uint32_t rise_time_ns, - uint32_t fall_time_ns) { - if (bus_mode != I2CBusMode_Standard && bus_mode != I2CBusMode_FastMode) { - // This is FM+ (or higher) and is not currently supported. - return I2C_TIMINGR_INVALID_VALUE; - } - if (target_bus_frequency > s_timing_data[bus_mode].clock_speed_max) { - return I2C_TIMINGR_INVALID_VALUE; - } - - uint32_t min_scl_low_ps = s_timing_data[bus_mode].min_scl_low_ps; - uint32_t min_scl_high_ps = s_timing_data[bus_mode].min_scl_high_ps; - - for (uint32_t prescaler = 1; prescaler <= TIMINGR_MAX_PRESC; ++prescaler) { - const uint32_t base_frequency = i2c_clk_frequency / prescaler; - const uint64_t base_period_ps = PS_PER_S / base_frequency; - - // Calculate what the total SCL period should be in terms of base clock cycles and then - // recalculate the target frequency based on that value. The result will be the highest - // target frequency we can obtain without going over. - uint32_t total_scl_cycles = base_frequency / target_bus_frequency; - - // Calculate the number of overhead cycles. This includes the rise time, fall time, and sync - // cycles for both SCLL and SCLH. - const uint32_t overhead_i2cclk_cycles = - DIVIDE_CEIL((rise_time_ns + fall_time_ns) * PS_PER_NS, - PS_PER_S / i2c_clk_frequency) + - I2C_SYNC_CYCLES * 2; - const uint32_t overhead_cycles = DIVIDE_CEIL(overhead_i2cclk_cycles, prescaler); - - // Figure out how many base clock cycles the minimum SCL periods correspond to. - uint32_t scl_low = DIVIDE_CEIL(min_scl_low_ps, base_period_ps); - uint32_t scl_high = DIVIDE_CEIL(min_scl_high_ps, base_period_ps); - - // Calculate the number of extra cycles we have. - const int32_t extra_cycles = total_scl_cycles - scl_low - scl_high - overhead_cycles; - if (extra_cycles < 0) { - // The base frequency is too slow to satisfy the target frequency, and continuing will only - // get slower, so give up. - return I2C_TIMINGR_INVALID_VALUE; - } - - // Split up the extra cycles evenly between the low and high periods. If necessary, give the - // extra one to the high period arbitrarily. - scl_low += extra_cycles / 2; - scl_high += extra_cycles - (extra_cycles / 2); - - // Calculate the SDA setup time delay, which is confusingly referred to as SCLDEL. - uint32_t scl_delay = DIVIDE_CEIL( - (rise_time_ns * PS_PER_NS) + s_timing_data[bus_mode].min_sda_setup_ps, - base_period_ps); - - // Check if the computed values are valid. If they aren't valid, we'll increase the prescaler - // try again with a higher prescaler. - // NOTE: We could end up in a situation where it is not valid, but could be if we split up the - // extra cycles differently. We're not going to worry about this because it doesn't currently - // happen in practice, and if it does, the next prescaler value will give us a valid (although - // slightly sub-optimal) result. - if ((scl_low <= TIMINGR_MAX_SCLL) && (scl_high <= TIMINGR_MAX_SCLH) && - (scl_delay <= TIMINGR_MAX_SCLDEL)) { - // 1 less than the SCLL / SCLH / SCLDEL / PRESC values should be stored in the register. - const TIMINGR result = (TIMINGR) { - .SCLL = scl_low - 1, - .SCLH = scl_high - 1, - .SCLDEL = scl_delay - 1, - .PRESC = prescaler - 1, - }; - return result.reg; - } - } - - // We tried every possible prescaler and couldn't find valid TIMINGR values. - return I2C_TIMINGR_INVALID_VALUE; -} diff --git a/src/fw/drivers/stm32f7/i2c_timingr.h b/src/fw/drivers/stm32f7/i2c_timingr.h deleted file mode 100644 index c7e8b78cad..0000000000 --- a/src/fw/drivers/stm32f7/i2c_timingr.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// This is always invalid because it inclues a value being set in the reserved field. -#define I2C_TIMINGR_INVALID_VALUE (0xffffffff) - -typedef enum I2CBusMode { - I2CBusMode_Standard, ///< I2C Standard Mode (up to 100 kHz) - I2CBusMode_FastMode, ///< I2C Fast Mode (up to 400 kHz) - I2CBusMode_FastModePlus, ///< I2C Fast Mode Plus (up to 1 MHz) -} I2CBusMode; - -uint32_t i2c_timingr_calculate(uint32_t i2c_clk_frequency, - I2CBusMode bus_mode, - uint32_t target_bus_frequency, - uint32_t rise_time_ns, - uint32_t fall_time_ns); diff --git a/src/fw/drivers/stm32f7/pwm.c b/src/fw/drivers/stm32f7/pwm.c deleted file mode 100644 index 8472f9d116..0000000000 --- a/src/fw/drivers/stm32f7/pwm.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "drivers/pwm.h" -#include "drivers/timer.h" - -#define STM32F7_COMPATIBLE -#include - -#define IS_LPTIM(periph) IS_LPTIM_ALL_PERIPH((LPTIM_TypeDef *)(periph)) - -void pwm_init(const PwmConfig *pwm, uint32_t resolution, uint32_t frequency) { - periph_config_enable(pwm->timer.peripheral, pwm->timer.config_clock); - - if (IS_LPTIM(pwm->timer.peripheral)) { - // Initialize low power timer - LPTIM_InitTypeDef config = { - .LPTIM_ClockSource = LPTIM_ClockSource_APBClock_LPosc, - .LPTIM_Prescaler = LPTIM_Prescaler_DIV128, - .LPTIM_Waveform = LPTIM_Waveform_PWM_OnePulse, - // low polarity means it'll be high for the specified duty cycle - .LPTIM_OutputPolarity = LPTIM_OutputPolarity_Low - }; - LPTIM_Init(pwm->timer.lp_peripheral, &config); - LPTIM_SelectSoftwareStart(pwm->timer.lp_peripheral); - // The timer must be enabled before setting the auto-reload value. - LPTIM_Cmd(pwm->timer.lp_peripheral, ENABLE); - LPTIM_SetAutoreloadValue(pwm->timer.lp_peripheral, resolution); - // Wait for the Auto-Reload value to be applied before disabling the timer. -#if !TARGET_QEMU - while (LPTIM_GetFlagStatus(pwm->timer.lp_peripheral, - LPTIM_FLAG_ARROK) == RESET) continue; -#endif - LPTIM_Cmd(pwm->timer.lp_peripheral, DISABLE); - } else { - // Initialize regular timer - TIM_TimeBaseInitTypeDef tim_config; - TIM_TimeBaseStructInit(&tim_config); - tim_config.TIM_Period = resolution - 1; - tim_config.TIM_Prescaler = timer_find_prescaler(&pwm->timer, frequency); - tim_config.TIM_CounterMode = TIM_CounterMode_Up; - tim_config.TIM_ClockDivision = 0; - TIM_TimeBaseInit(pwm->timer.peripheral, &tim_config); - - // PWM Mode configuration - TIM_OCInitTypeDef tim_oc_init; - TIM_OCStructInit(&tim_oc_init); - tim_oc_init.TIM_OCMode = TIM_OCMode_PWM1; - tim_oc_init.TIM_OutputState = TIM_OutputState_Enable; - tim_oc_init.TIM_Pulse = 0; - tim_oc_init.TIM_OCPolarity = TIM_OCPolarity_High; - pwm->timer.init(pwm->timer.peripheral, &tim_oc_init); - - pwm->timer.preload(pwm->timer.peripheral, TIM_OCPreload_Enable); - TIM_ARRPreloadConfig(pwm->timer.peripheral, ENABLE); - } - - periph_config_disable(pwm->timer.peripheral, pwm->timer.config_clock); -} - -void pwm_set_duty_cycle(const PwmConfig *pwm, uint32_t duty_cycle) { - if (IS_LPTIM(pwm->timer.peripheral)) { - LPTIM_SetCompareValue(pwm->timer.lp_peripheral, duty_cycle); - LPTIM_SelectOperatingMode(pwm->timer.lp_peripheral, LPTIM_Mode_Continuous); - } else { - TIM_OCInitTypeDef tim_oc_init; - TIM_OCStructInit(&tim_oc_init); - tim_oc_init.TIM_OCMode = TIM_OCMode_PWM1; - tim_oc_init.TIM_OutputState = TIM_OutputState_Enable; - tim_oc_init.TIM_Pulse = duty_cycle; - tim_oc_init.TIM_OCPolarity = TIM_OCPolarity_High; - pwm->timer.init(pwm->timer.peripheral, &tim_oc_init); - } -} - -void pwm_enable(const PwmConfig *pwm, bool enable) { - if (enable) { - gpio_af_init(&pwm->afcfg, GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_DOWN); - periph_config_enable(pwm->timer.peripheral, pwm->timer.config_clock); - } else { - periph_config_disable(pwm->timer.peripheral, pwm->timer.config_clock); - gpio_output_init(&pwm->output, GPIO_OType_PP, GPIO_Speed_100MHz); - // The ".active_high" attribute determines the idle state of the PWM output - gpio_output_set(&pwm->output, false /* force low */); - } - - const FunctionalState state = (enable) ? ENABLE : DISABLE; - if (IS_LPTIM(pwm->timer.peripheral)) { - LPTIM_Cmd(pwm->timer.lp_peripheral, state); - } else { - TIM_Cmd(pwm->timer.peripheral, state); - } -} diff --git a/src/fw/drivers/stm32f7/pwr.c b/src/fw/drivers/stm32f7/pwr.c deleted file mode 100644 index ffcbba48d8..0000000000 --- a/src/fw/drivers/stm32f7/pwr.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/pwr.h" - -#include "drivers/periph_config.h" - -#define STM32F7_COMPATIBLE -#include - -void pwr_enable_wakeup(bool enable) { - if (enable) { - __atomic_or_fetch(&PWR->CSR2, PWR_CSR2_EWUP1, __ATOMIC_RELAXED); - } else { - __atomic_and_fetch(&PWR->CSR2, ~PWR_CSR2_EWUP1, __ATOMIC_RELAXED); - } -} - -void pwr_flash_power_down_stop_mode(bool power_down) { - if (power_down) { - __atomic_or_fetch(&PWR->CR1, PWR_CR1_FPDS, __ATOMIC_RELAXED); - } else { - __atomic_and_fetch(&PWR->CR1, ~PWR_CR1_FPDS, __ATOMIC_RELAXED); - } -} - -void pwr_access_backup_domain(bool enable_access) { - periph_config_enable(PWR, RCC_APB1Periph_PWR); - if (enable_access) { - __atomic_or_fetch(&PWR->CR1, PWR_CR1_DBP, __ATOMIC_RELAXED); - } else { - __atomic_and_fetch(&PWR->CR1, ~PWR_CR1_DBP, __ATOMIC_RELAXED); - } - periph_config_disable(PWR, RCC_APB1Periph_PWR); -} diff --git a/src/fw/drivers/stm32f7/uart.c b/src/fw/drivers/stm32f7/uart.c deleted file mode 100644 index 71c33df45c..0000000000 --- a/src/fw/drivers/stm32f7/uart.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "uart_definitions.h" -#include "drivers/uart.h" - -#include "drivers/dma.h" -#include "drivers/gpio.h" -#include "drivers/periph_config.h" -#include "system/passert.h" - -#include "FreeRTOS.h" - -#define STM32F7_COMPATIBLE -#include -#include - -// The STM32F2 standard peripheral library uses a precision of 100 which is plenty, so we'll do the -// same. -#define DIV_PRECISION (100) - - -// Initialization / Configuration APIs -//////////////////////////////////////////////////////////////////////////////// - -typedef enum UARTCR1Flags { - UARTCR1Flags_Duplex = USART_CR1_TE | USART_CR1_RE, - UARTCR1Flags_TE = USART_CR1_TE, - UARTCR1Flags_RE = USART_CR1_RE, -} UARTCR1Flags; - -static void prv_clear_all_errors(UARTDevice *dev) { - dev->periph->ICR |= (USART_ICR_ORECF | USART_ICR_PECF | USART_ICR_NCF | USART_ICR_FECF); -} - -static void prv_init(UARTDevice *dev, bool is_open_drain, UARTCR1Flags cr1_extra_flags) { - // Enable peripheral clock - periph_config_enable(dev->periph, dev->rcc_apb_periph); - - // configure GPIO - const GPIOOType_TypeDef otype = is_open_drain ? GPIO_OType_OD : GPIO_OType_PP; - if (dev->tx_gpio.gpio) { - gpio_af_init(&dev->tx_gpio, otype, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - } - if (dev->rx_gpio.gpio) { - // half-duplex should only define a TX pin - PBL_ASSERTN(!dev->half_duplex); - gpio_af_init(&dev->rx_gpio, otype, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL); - } - - // clear any lingering errors - prv_clear_all_errors(dev); - - // configure the UART peripheral control registers - // - 8-bit word length - // - no parity - // - RX / TX enabled - // - 1 stop bit - // - no flow control - dev->periph->CR1 &= ~USART_CR1_UE; - dev->periph->CR2 = (dev->do_swap_rx_tx ? USART_CR2_SWAP : 0); - dev->periph->CR3 = (dev->half_duplex ? USART_CR3_HDSEL : 0); - - dev->periph->CR1 = cr1_extra_flags | USART_CR1_UE; - dev->state->initialized = true; - - // initialize the DMA request - if (dev->rx_dma) { - dma_request_init(dev->rx_dma); - } -} - -void uart_init(UARTDevice *dev) { - prv_init(dev, false /* !is_open_drain */, UARTCR1Flags_Duplex); -} - -void uart_init_open_drain(UARTDevice *dev) { - prv_init(dev, true /* is_open_drain */, UARTCR1Flags_Duplex); -} - -void uart_init_tx_only(UARTDevice *dev) { - prv_init(dev, false /* !is_open_drain */, UARTCR1Flags_TE); -} - -void uart_init_rx_only(UARTDevice *dev) { - prv_init(dev, false /* !is_open_drain */, UARTCR1Flags_RE); -} - -void uart_deinit(UARTDevice *dev) { - dev->periph->CR1 &= ~USART_CR1_UE; - periph_config_disable(dev->periph, dev->rcc_apb_periph); - // Change the pins to be digital inputs rather than AF pins. We can't change to analog inputs - // because those aren't 5V tolerant which these pins may need to be. - if (dev->tx_gpio.gpio) { - const InputConfig input_config = { - .gpio = dev->tx_gpio.gpio, - .gpio_pin = dev->tx_gpio.gpio_pin - }; - gpio_input_init(&input_config); - } - if (dev->rx_gpio.gpio) { - const InputConfig input_config = { - .gpio = dev->rx_gpio.gpio, - .gpio_pin = dev->rx_gpio.gpio_pin - }; - gpio_input_init(&input_config); - } -} - -void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { - PBL_ASSERTN(dev->state->initialized); - - RCC_ClocksTypeDef RCC_ClocksStatus; - RCC_GetClocksFreq(&RCC_ClocksStatus); - uint64_t scaled_apbclock = DIV_PRECISION; - if ((dev->periph == USART1) || (dev->periph == USART6)) { - scaled_apbclock *= RCC_ClocksStatus.PCLK2_Frequency; - } else { - scaled_apbclock *= RCC_ClocksStatus.PCLK1_Frequency; - } - - if (dev->periph->CR1 & USART_CR1_OVER8) { - scaled_apbclock <<= 1; - } - - // calculate the baud rate value - const uint32_t div = (scaled_apbclock / baud_rate); - const uint32_t brr = (div & 0xFFF0) | ((div & 0xF) >> 1); - - // we can only change the baud rate when the UART is disabled - dev->periph->CR1 &= ~USART_CR1_UE; - dev->periph->BRR = brr / DIV_PRECISION; - dev->periph->CR1 |= USART_CR1_UE; -} - - -// Read / Write APIs -//////////////////////////////////////////////////////////////////////////////// - -void uart_write_byte(UARTDevice *dev, uint8_t data) { - PBL_ASSERTN(dev->state->initialized); - - // wait for us to be ready to send - while (!uart_is_tx_ready(dev)) continue; - - dev->periph->TDR = data; -} - -uint8_t uart_read_byte(UARTDevice *dev) { - // explicitly clear the error flags to match up with F4 behavior - prv_clear_all_errors(dev); - - // read the data regardless since it will clear interrupt flags - return dev->periph->RDR; -} - -UARTRXErrorFlags uart_has_errored_out(UARTDevice *dev) { - uint16_t errors = dev->periph->ISR; - UARTRXErrorFlags flags = { - .parity_error = (errors & USART_ISR_PE) != 0, - .overrun_error = (errors & USART_ISR_ORE) != 0, - .framing_error = (errors & USART_ISR_FE) != 0, - .noise_detected = (errors & USART_ISR_NE) != 0, - }; - - return flags; -} - -bool uart_is_rx_ready(UARTDevice *dev) { - return dev->periph->ISR & USART_ISR_RXNE; -} - -bool uart_has_rx_overrun(UARTDevice *dev) { - return dev->periph->ISR & USART_ISR_ORE; -} - -bool uart_has_rx_framing_error(UARTDevice *dev) { - return dev->periph->ISR & USART_ISR_FE; -} - -bool uart_is_tx_ready(UARTDevice *dev) { - return dev->periph->ISR & USART_ISR_TXE; -} - -bool uart_is_tx_complete(UARTDevice *dev) { - return dev->periph->ISR & USART_ISR_TC; -} - -void uart_wait_for_tx_complete(UARTDevice *dev) { - while (!uart_is_tx_complete(dev)) continue; -} - - -// Interrupts -//////////////////////////////////////////////////////////////////////////////// - -static void prv_set_interrupt_enabled(UARTDevice *dev, bool enabled) { - if (enabled) { - PBL_ASSERTN(dev->state->tx_irq_handler || dev->state->rx_irq_handler); - // enable the interrupt - NVIC_SetPriority(dev->irq_channel, dev->irq_priority); - NVIC_EnableIRQ(dev->irq_channel); - } else { - // disable the interrupt - NVIC_DisableIRQ(dev->irq_channel); - } -} - -void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->rx_irq_handler = irq_handler; -} - -void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { - PBL_ASSERTN(dev->state->initialized); - dev->state->tx_irq_handler = irq_handler; -} - -void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - if (enabled) { - dev->state->rx_int_enabled = true; - dev->periph->CR1 |= USART_CR1_RXNEIE; - prv_set_interrupt_enabled(dev, true); - } else { - // disable interrupt if TX is also disabled - prv_set_interrupt_enabled(dev, dev->state->tx_int_enabled); - dev->periph->CR1 &= ~USART_CR1_RXNEIE; - dev->state->rx_int_enabled = false; - } -} - -void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { - PBL_ASSERTN(dev->state->initialized); - if (enabled) { - dev->state->tx_int_enabled = true; - dev->periph->CR1 |= USART_CR1_TXEIE; - prv_set_interrupt_enabled(dev, true); - } else { - // disable interrupt if RX is also disabled - prv_set_interrupt_enabled(dev, dev->state->rx_int_enabled); - dev->periph->CR1 &= ~USART_CR1_TXEIE; - dev->state->tx_int_enabled = false; - } -} - -void uart_irq_handler(UARTDevice *dev) { - PBL_ASSERTN(dev->state->initialized); - bool should_context_switch = false; - if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { - const UARTRXErrorFlags err_flags = { - .overrun_error = uart_has_rx_overrun(dev), - .framing_error = uart_has_rx_framing_error(dev), - }; - if (dev->state->rx_dma_buffer) { - // process bytes from the DMA buffer - const uint32_t dma_length = dev->state->rx_dma_length; - const uint32_t next_idx = dma_length - dma_request_get_current_data_counter(dev->rx_dma); - // make sure we didn't underflow the index - PBL_ASSERTN(next_idx < dma_length); - while (dev->state->rx_dma_index != next_idx) { - const uint8_t data = dev->state->rx_dma_buffer[dev->state->rx_dma_index]; - if (dev->state->rx_irq_handler(dev, data, &err_flags)) { - should_context_switch = true; - } - if (++dev->state->rx_dma_index == dma_length) { - dev->state->rx_dma_index = 0; - } - } - // explicitly clear error flags since we're not reading from the data register - uart_clear_all_interrupt_flags(dev); - } else { - const bool has_byte = uart_is_rx_ready(dev); - // read the data register regardless to clear the error flags - const uint8_t data = uart_read_byte(dev); - if (has_byte) { - if (dev->state->rx_irq_handler(dev, data, &err_flags)) { - should_context_switch = true; - } - } - } - } - if (dev->state->tx_irq_handler && dev->state->tx_int_enabled && uart_is_tx_ready(dev)) { - if (dev->state->tx_irq_handler(dev)) { - should_context_switch = true; - } - } - portEND_SWITCHING_ISR(should_context_switch); -} - -void uart_clear_all_interrupt_flags(UARTDevice *dev) { - dev->periph->RQR |= USART_RQR_RXFRQ; - dev->periph->ICR |= USART_ICR_ORECF; -} - - -// DMA -//////////////////////////////////////////////////////////////////////////////// - -void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { - dev->periph->CR3 |= USART_CR3_DMAR; - dma_request_start_circular(dev->rx_dma, buffer, (void *)&dev->periph->RDR, length, NULL, NULL); - dev->state->rx_dma_index = 0; - dev->state->rx_dma_length = length; - dev->state->rx_dma_buffer = buffer; -} - -void uart_stop_rx_dma(UARTDevice *dev) { - dev->state->rx_dma_buffer = NULL; - dev->state->rx_dma_length = 0; - dma_request_stop(dev->rx_dma); - dev->periph->CR3 &= ~USART_CR3_DMAR; -} - -void uart_clear_rx_dma_buffer(UARTDevice *dev) { - dev->state->rx_dma_index = dev->state->rx_dma_length - - dma_request_get_current_data_counter(dev->rx_dma); -} diff --git a/src/fw/drivers/stm32f7/uart_definitions.h b/src/fw/drivers/stm32f7/uart_definitions.h deleted file mode 100644 index 761fbf8188..0000000000 --- a/src/fw/drivers/stm32f7/uart_definitions.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/uart.h" - -#include "board/board.h" - -#include -#include - - -typedef struct UARTState { - bool initialized; - UARTRXInterruptHandler rx_irq_handler; - UARTTXInterruptHandler tx_irq_handler; - bool rx_int_enabled; - bool tx_int_enabled; - uint8_t *rx_dma_buffer; - uint32_t rx_dma_length; - uint32_t rx_dma_index; -} UARTDeviceState; - -typedef const struct UARTDevice { - UARTDeviceState *state; - bool half_duplex; - // Conveniently, the F7 supports swapping RX/TX if they get laid out incorrectly :) - bool do_swap_rx_tx; - AfConfig tx_gpio; - AfConfig rx_gpio; - USART_TypeDef *periph; - uint32_t rcc_apb_periph; - uint8_t irq_channel; - uint8_t irq_priority; - DMARequest *rx_dma; -} UARTDevice; - -// thinly wrapped by the IRQ handler in board_*.c -void uart_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/stubs/accel.c b/src/fw/drivers/stubs/accel.c index a23d476c7b..ab5be49c64 100644 --- a/src/fw/drivers/stubs/accel.c +++ b/src/fw/drivers/stubs/accel.c @@ -42,15 +42,8 @@ bool accel_get_double_tap_detection_enabled(void) { void accel_set_shake_sensitivity_high(bool sensitivity_high) { } -bool accel_run_selftest(void) { - return true; -} - void command_accel_status(void) { } -void command_accel_selftest(void) { -} - void command_accel_softreset(void) { } diff --git a/src/fw/drivers/stubs/backlight.c b/src/fw/drivers/stubs/backlight.c index fa1652eee9..b3fd025942 100644 --- a/src/fw/drivers/stubs/backlight.c +++ b/src/fw/drivers/stubs/backlight.c @@ -1,17 +1,15 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "drivers/backlight.h" void backlight_init(void) { } -// TODO: PBL-36077 Move to a generic 4v5 enable -void led_enable(LEDEnabler enabler) { -} - -// TODO: PBL-36077 Move to a generic 4v5 disable -void led_disable(LEDEnabler enabler) { +void backlight_set_brightness(uint8_t brightness) { } -void backlight_set_brightness(uint16_t brightness) { +void backlight_refresh(void) { } void command_backlight_ctl(const char *arg) { diff --git a/src/fw/drivers/stubs/button.c b/src/fw/drivers/stubs/button.c index 2f8f056444..bc5e4e0298 100644 --- a/src/fw/drivers/stubs/button.c +++ b/src/fw/drivers/stubs/button.c @@ -14,8 +14,8 @@ uint8_t button_get_state_bits(void) { void button_init(void) { } -bool button_selftest(void) { - return true; +void button_set_rotated(bool rotated) { + } void command_button_read(const char* button_id_str) { diff --git a/src/fw/drivers/stubs/display.c b/src/fw/drivers/stubs/display.c index 2c2042bb1b..36e8a982a3 100644 --- a/src/fw/drivers/stubs/display.c +++ b/src/fw/drivers/stubs/display.c @@ -1,9 +1,5 @@ #include "drivers/display/display.h" -uint32_t display_baud_rate_change(uint32_t new_frequency_hz) { - return new_frequency_hz; -} - void display_init(void) { } @@ -13,6 +9,10 @@ void display_clear(void) { void display_set_enabled(bool enabled) { } +void display_set_rotated(bool rotated) { + +} + bool display_update_in_progress(void) { return true; } @@ -20,18 +20,6 @@ bool display_update_in_progress(void) { void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) { } -void display_pulse_vcom(void) { -} - -void display_show_splash_screen(void) { +void display_update_boot_frame(void) { // The bootloader has already drawn the splash screen for us; nothing to do! -} - -void display_show_panic_screen(uint32_t error_code) -{ -} - -// Stubs for display offset -void display_set_offset(GPoint offset) {} - -GPoint display_get_offset(void) { return GPointZero; } +} \ No newline at end of file diff --git a/src/fw/drivers/stubs/exti.c b/src/fw/drivers/stubs/exti.c index cb4fab6d69..359da00691 100644 --- a/src/fw/drivers/stubs/exti.c +++ b/src/fw/drivers/stubs/exti.c @@ -1,17 +1,15 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "drivers/exti.h" void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb) { - } -void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger) { - +void exti_enable(ExtiConfig config) { + (void)config; } -void exti_enable_other(ExtiLineOther exti_line) { - -} - -void exti_disable_other(ExtiLineOther exti_line) { - +void exti_disable(ExtiConfig config) { + (void)config; } \ No newline at end of file diff --git a/src/fw/drivers/stubs/gpio_defaults.c b/src/fw/drivers/stubs/gpio_defaults.c deleted file mode 100644 index f0fc3d77f0..0000000000 --- a/src/fw/drivers/stubs/gpio_defaults.c +++ /dev/null @@ -1,2 +0,0 @@ -void gpio_init_all(void) { -} diff --git a/src/fw/drivers/stubs/hrm.c b/src/fw/drivers/stubs/hrm.c deleted file mode 100644 index 7cb63b9f9a..0000000000 --- a/src/fw/drivers/stubs/hrm.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "hrm.h" - -void hrm_init(HRMDevice *dev) { -} - -void hrm_enable(HRMDevice *dev) { - dev->state->enabled = true; -} - -void hrm_disable(HRMDevice *dev) { - dev->state->enabled = false; -} - -bool hrm_is_enabled(HRMDevice *dev) { - return dev->state->enabled; -} \ No newline at end of file diff --git a/src/fw/drivers/stubs/hrm.h b/src/fw/drivers/stubs/hrm.h deleted file mode 100644 index f2aff169f4..0000000000 --- a/src/fw/drivers/stubs/hrm.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/hrm.h" - -typedef struct HRMDeviceState { - bool enabled; -} HRMDeviceState; - -typedef const struct HRMDevice { - HRMDeviceState *state; -} HRMDevice; diff --git a/src/fw/drivers/stubs/imu.c b/src/fw/drivers/stubs/imu.c deleted file mode 100644 index 198de13cc5..0000000000 --- a/src/fw/drivers/stubs/imu.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -void imu_init(void) { -} - -void imu_power_up(void) { -} - -void imu_power_down(void) { -} - diff --git a/src/fw/drivers/stubs/mcu.c b/src/fw/drivers/stubs/mcu.c index d10d2d785e..55ff43ad90 100644 --- a/src/fw/drivers/stubs/mcu.c +++ b/src/fw/drivers/stubs/mcu.c @@ -1,7 +1,9 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "drivers/mcu.h" -#define CMSIS_COMPATIBLE -#include +#include StatusCode mcu_get_serial(void *buf, size_t *buf_sz) { return E_DOES_NOT_EXIST; diff --git a/src/fw/drivers/stubs/periph_config.c b/src/fw/drivers/stubs/periph_config.c deleted file mode 100644 index b0bb312e3d..0000000000 --- a/src/fw/drivers/stubs/periph_config.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "drivers/periph_config.h" - -void periph_config_init(void) { -} - -void periph_config_acquire_lock(void) { -} - -void periph_config_release_lock(void) { -} - -void periph_config_enable(void *periph, uint32_t rcc_bit) { -} - -void periph_config_disable(void *periph, uint32_t rcc_bit) { -} diff --git a/src/fw/drivers/stubs/qspi.c b/src/fw/drivers/stubs/qspi.c deleted file mode 100644 index 7227c5d56b..0000000000 --- a/src/fw/drivers/stubs/qspi.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "drivers/qspi.h" -#include "drivers/qspi_definitions.h" - -#include "drivers/flash/qspi_flash.h" -#include "drivers/flash/qspi_flash_definitions.h" - -#include "drivers/flash/flash_impl.h" - -status_t flash_impl_set_nvram_erase_status(bool is_subsector, - FlashAddress addr) { - return S_SUCCESS; -} - -status_t flash_impl_clear_nvram_erase_status(void) { - return S_SUCCESS; -} - -status_t flash_impl_get_nvram_erase_status(bool *is_subsector, - FlashAddress *addr) { - // XXX - return S_FALSE; -} diff --git a/src/fw/drivers/stubs/rng.c b/src/fw/drivers/stubs/rng.c deleted file mode 100644 index 0ec1dc72da..0000000000 --- a/src/fw/drivers/stubs/rng.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "drivers/rng.h" - -bool rng_rand(uint32_t *rand_out) { - return false; -} diff --git a/src/fw/drivers/stubs/temperature.c b/src/fw/drivers/stubs/temperature.c deleted file mode 100644 index ded0987ed6..0000000000 --- a/src/fw/drivers/stubs/temperature.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include "drivers/temperature.h" -#include "console/prompt.h" - -void temperature_init(void) { - -} - -int32_t temperature_read(void) { - return 0; -} - -void command_temperature_read(void) { - char buffer[32]; - prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRId32" ", temperature_read()); -} diff --git a/src/fw/drivers/stubs/touch.c b/src/fw/drivers/stubs/touch.c index 4fc6c5fabf..1d7ece7e48 100644 --- a/src/fw/drivers/stubs/touch.c +++ b/src/fw/drivers/stubs/touch.c @@ -1,18 +1,10 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include void touch_sensor_init(void) { } + +void touch_sensor_set_enabled(bool enabled) { +} diff --git a/src/fw/drivers/stubs/vibe.c b/src/fw/drivers/stubs/vibe.c index e70c5482de..f866b24da4 100644 --- a/src/fw/drivers/stubs/vibe.c +++ b/src/fw/drivers/stubs/vibe.c @@ -20,6 +20,16 @@ int8_t vibe_get_braking_strength(void) { return VIBE_STRENGTH_OFF; } +status_t vibe_calibrate(void) { + return E_INVALID_OPERATION; +} + +uint8_t vibe_get_calibration(void) { + return 0xFF; +} + +void vibe_apply_calibration(uint8_t cali) { +} void command_vibe_ctl(const char *arg) { int strength = atoi(arg); diff --git a/src/fw/drivers/stubs/watchdog.c b/src/fw/drivers/stubs/watchdog.c index 1cdd9f4479..a39a11980f 100644 --- a/src/fw/drivers/stubs/watchdog.c +++ b/src/fw/drivers/stubs/watchdog.c @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "drivers/watchdog.h" void watchdog_init(void) { @@ -6,6 +9,9 @@ void watchdog_init(void) { void watchdog_start(void) { } +void watchdog_stop(void) { +} + void watchdog_feed(void) { } @@ -13,8 +19,10 @@ bool watchdog_check_reset_flag(void) { return 0; } +static McuRebootReason s_cached_reset_flag; + McuRebootReason watchdog_clear_reset_flag(void) { - McuRebootReason mcu_reboot_reason = { + s_cached_reset_flag = (McuRebootReason){ .brown_out_reset = 0, .pin_reset = 0, .power_on_reset = 1, @@ -24,5 +32,9 @@ McuRebootReason watchdog_clear_reset_flag(void) { .low_power_manager_reset = 0, }; - return mcu_reboot_reason; + return s_cached_reset_flag; +} + +McuRebootReason watchdog_get_reset_flag(void) { + return s_cached_reset_flag; } diff --git a/src/fw/drivers/system_flash.h b/src/fw/drivers/system_flash.h deleted file mode 100644 index b28ff44ed7..0000000000 --- a/src/fw/drivers/system_flash.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// FIXME: This interface totally sucks. It's really just me hacking around. I'll clean it up I promise. - -#define FLASH_Sector_0 ((uint16_t)0x0000) /*!< Sector Number 0 */ -#define FLASH_Sector_1 ((uint16_t)0x0008) /*!< Sector Number 1 */ -#define FLASH_Sector_2 ((uint16_t)0x0010) /*!< Sector Number 2 */ -#define FLASH_Sector_3 ((uint16_t)0x0018) /*!< Sector Number 3 */ -#define FLASH_Sector_4 ((uint16_t)0x0020) /*!< Sector Number 4 */ -#define FLASH_Sector_5 ((uint16_t)0x0028) /*!< Sector Number 5 */ -#define FLASH_Sector_6 ((uint16_t)0x0030) /*!< Sector Number 6 */ -#define FLASH_Sector_7 ((uint16_t)0x0038) /*!< Sector Number 7 */ -#define FLASH_Sector_8 ((uint16_t)0x0040) /*!< Sector Number 8 */ -#define FLASH_Sector_9 ((uint16_t)0x0048) /*!< Sector Number 9 */ -#define FLASH_Sector_10 ((uint16_t)0x0050) /*!< Sector Number 10 */ -#define FLASH_Sector_11 ((uint16_t)0x0058) /*!< Sector Number 11 */ - -#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */ -#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */ -#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */ - -// stm32f2xx only has 512k of system flash, these sectors don't exist -#if defined(MICRO_FAMILY_STM32F4) -#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */ -#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */ -#endif - -void system_flash_erase(uint16_t sector); - -void system_flash_write_byte(uint32_t address, uint8_t data); - -uint32_t system_flash_read(uint32_t address); diff --git a/src/fw/drivers/task_watchdog.c b/src/fw/drivers/task_watchdog.c index b4e8df23f6..f9e73440f1 100644 --- a/src/fw/drivers/task_watchdog.c +++ b/src/fw/drivers/task_watchdog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/task_watchdog.h" @@ -23,22 +10,14 @@ #include "kernel/pebble_tasks.h" #include "os/mutex.h" #include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" #include "system/bootbits.h" #include "system/die.h" #include "system/logging.h" #include "system/passert.h" #include "util/size.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - #include "FreeRTOS.h" #include "task.h" @@ -46,12 +25,12 @@ #include #include -#ifdef NO_WATCHDOG -#include "debug/setup.h" +#ifdef CONFIG_SOC_NRF52 +#include #endif -#if MICRO_FAMILY_NRF5 -#include +#ifdef CONFIG_SOC_SF32LB52 +#include #endif #define APP_THROTTLE_TIME_MS 300 @@ -71,20 +50,23 @@ static TimerID s_throttle_timer_id = TIMER_INVALID_ID; // How often we want the interrupt to fire #define TIMER_INTERRUPT_HZ (1000 / TASK_WATCHDOG_FEED_PERIOD_MS) -// The frequency to run the peripheral at -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 -#define TIMER_CLOCK_HZ 32000 -// The number of timer ticks that should elapse before the timer interrupt fires -#define TIME_PERIOD (TIMER_CLOCK_HZ / TIMER_INTERRUPT_HZ) -#endif // How many ticks have elapsed since we fed the HW watchdog static uint8_t s_ticks_since_successful_feed = 0; +// Pause state: number of ticks remaining in pause +static uint32_t s_pause_ticks_remaining = 0; + // We use this interrupt vector for our lower priority interrupts -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 #define WATCHDOG_FREERTOS_IRQn QDEC_IRQn #define WATCHDOG_FREERTOS_IRQHandler QDEC_IRQHandler +#elif defined(CONFIG_SOC_SF32LB52) +#define WATCHDOG_FREERTOS_IRQn USART5_IRQn +#define WATCHDOG_FREERTOS_IRQHandler USART5_IRQHandler +#elif defined(CONFIG_QEMU) +#define WATCHDOG_FREERTOS_IRQn WATCHDOG_IRQn +#define WATCHDOG_FREERTOS_IRQHandler WATCHDOG_IRQHandler #else #define WATCHDOG_FREERTOS_IRQn CAN2_SCE_IRQn #define WATCHDOG_FREERTOS_IRQHandler CAN2_SCE_IRQHandler @@ -96,11 +78,11 @@ static void prv_log_stuck_timer_task(RebootReason *reboot_reason) { void* current_cb = new_timer_debug_get_current_callback(); if (!current_cb) { - PBL_LOG_SYNC(LOG_LEVEL_WARNING, "No timer in progress."); + PBL_LOG_SYNC_WRN("No timer in progress."); return; } - PBL_LOG_SYNC(LOG_LEVEL_WARNING, "Timer callback %p", current_cb); + PBL_LOG_SYNC_WRN("Timer callback %p", current_cb); reboot_reason->watchdog.stuck_task_callback = (uint32_t)current_cb; } @@ -108,11 +90,11 @@ static void prv_log_stuck_system_task(RebootReason *reboot_reason) { void *current_cb = system_task_get_current_callback(); if (!current_cb) { - PBL_LOG_SYNC(LOG_LEVEL_WARNING, "No system task callback in progress."); + PBL_LOG_SYNC_WRN("No system task callback in progress."); return; } - PBL_LOG_SYNC(LOG_LEVEL_WARNING, "System task callback: %p", current_cb); + PBL_LOG_SYNC_WRN("System task callback: %p", current_cb); reboot_reason->watchdog.stuck_task_callback = (uint32_t)current_cb; } @@ -121,14 +103,36 @@ static void prv_log_stuck_task(RebootReason *reboot_reason, PebbleTask task) { void *current_lr = (void*) ulTaskDebugGetStackedLR(task_handle); void *current_pc = (void*) ulTaskDebugGetStackedPC(task_handle); - PBL_LOG_SYNC(LOG_LEVEL_WARNING, "Task <%s> stuck: LR: %p PC: %p", pebble_task_get_name(task), current_lr, current_pc); + PBL_LOG_SYNC_WRN("Task <%s> stuck: LR: %p PC: %p", pebble_task_get_name(task), current_lr, current_pc); reboot_reason->watchdog.stuck_task_pc = (uint32_t)current_pc; reboot_reason->watchdog.stuck_task_lr = (uint32_t)current_lr; } +#ifndef CONFIG_NO_WATCHDOG +// TCB-only variant of prv_log_failed_message; no logging or FreeRTOS calls +// so it's safe from above configMAX_SYSCALL_INTERRUPT_PRIORITY. +static void prv_capture_stuck_task_info(RebootReason *reboot_reason) { + const PebbleTask tasks_in_reverse_priority[] = { + PebbleTask_KernelBackground, + PebbleTask_KernelMain, + PebbleTask_PULSE, + PebbleTask_NewTimers + }; + + for (unsigned int i = 0; i < ARRAY_LENGTH(tasks_in_reverse_priority); ++i) { + const uint8_t task_index = tasks_in_reverse_priority[i]; + const PebbleTaskBitset task_mask = (1 << task_index); + if ((s_watchdog_mask & task_mask) && !(s_watchdog_bits & task_mask)) { + TaskHandle_t *task_handle = pebble_task_get_handle_for_task(task_index); + reboot_reason->watchdog.stuck_task_pc = (uint32_t)ulTaskDebugGetStackedPC(task_handle); + reboot_reason->watchdog.stuck_task_lr = (uint32_t)ulTaskDebugGetStackedLR(task_handle); + } + } +} +#endif + static void prv_log_failed_message(RebootReason *reboot_reason) { - PBL_LOG_SYNC(LOG_LEVEL_WARNING, - "Watchdog feed failed, last feed %dms ago, current status 0x%"PRIx16" mask 0x%"PRIx16, + PBL_LOG_SYNC_WRN("Watchdog feed failed, last feed %dms ago, current status 0x%"PRIx16" mask 0x%"PRIx16, (s_ticks_since_successful_feed * 1000) / TIMER_INTERRUPT_HZ, s_watchdog_bits, s_watchdog_mask); @@ -159,24 +163,10 @@ static void prv_log_failed_message(RebootReason *reboot_reason) { } } -// ------------------------------------------------------------------------------------------------- -// The Timer ISR. This runs at super high priority (higher than configMAX_SYSCALL_INTERRUPT_PRIORITY), so -// it is not safe to call ANY FreeRTOS functions from here. -#if MICRO_FAMILY_SF32LB52 -// TODO(SF32LB52): Add implementation -#elif !MICRO_FAMILY_NRF5 -void TIM2_IRQHandler(void) { - // Workaround M3 bug that causes interrupt to fire twice: - // https://my.st.com/public/Faq/Lists/faqlst/DispForm.aspx?ID=143 - TIM_ClearITPendingBit(TIM2, TIM_IT_Update); - task_watchdog_feed(); -} -#endif - static void prv_app_task_throttle_end(void *data) { vTaskPrioritySet(pebble_task_get_handle_for_task(PebbleTask_App), APP_TASK_PRIORITY | portPRIVILEGE_BIT); - PBL_LOG(LOG_LEVEL_DEBUG, "Ending App Throttling"); + PBL_LOG_DBG("Ending App Throttling"); } static void prv_app_task_throttle_start(void) { @@ -187,12 +177,11 @@ static void prv_app_task_throttle_start(void) { // once to aid in debug if (strcmp(last_throttled_task, curr_task) != 0) { strcpy(last_throttled_task, curr_task); - PBL_LOG(LOG_LEVEL_INFO, "Starting App Throttling for %s", curr_task); + PBL_LOG_INFO("Starting App Throttling for %s", curr_task); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting App Throttling for %s", curr_task); + PBL_LOG_DBG("Starting App Throttling for %s", curr_task); } - analytics_inc(ANALYTICS_DEVICE_METRIC_APP_THROTTLED_COUNT, AnalyticsClient_System); vTaskPrioritySet(pebble_task_get_handle_for_task(PebbleTask_App), tskIDLE_PRIORITY | portPRIVILEGE_BIT); } @@ -214,7 +203,7 @@ static void prv_system_task_starved_callback(void *data) { // This is a lower priority interrupt (at configMAX_SYSCALL_INTERRUPT_PRIORITY) that we trigger // when we need to perform logging. void WATCHDOG_FREERTOS_IRQHandler(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "WD: low priority ISR"); + PBL_LOG_DBG("WD: low priority ISR"); // Are we rebooting because of watch dog? RebootReason reason; @@ -243,18 +232,14 @@ void WATCHDOG_FREERTOS_IRQHandler(void) { // If getting reset by the watchdog timer is imminent (it will reset the // CPU if not fed at least once every 7 seconds), then just coredump now if (s_ticks_since_successful_feed >= (6 * TIMER_INTERRUPT_HZ)) { -#if defined(NO_WATCHDOG) - PBL_LOG(LOG_LEVEL_DEBUG, - "Would have coredumped if built with watchdogs ... enabling lowpowerdebug!"); - enable_mcu_debugging(); -#else +#if !defined(CONFIG_NO_WATCHDOG) reset_due_to_software_failure(); #endif } } else if (reason.code == 0) { - PBL_LOG_SYNC(LOG_LEVEL_WARNING, "Recovered from task watchdog stall."); + PBL_LOG_SYNC_WRN("Recovered from task watchdog stall."); } } @@ -265,68 +250,11 @@ void WATCHDOG_FREERTOS_IRQHandler(void) { // Setup a very high priority interrupt to fire periodically. This ISR will call task_watchdog_feed() // which resets the watchdog timer if it detects that none of our watchable tasks are stuck. void task_watchdog_init(void) { -#if MICRO_FAMILY_SF32LB52 -// TODO(SF32LB52): Add implementation -#elif !MICRO_FAMILY_NRF5 - // The timer is on ABP1 which is clocked by PCLK1 - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - uint32_t timer_clock = clocks.PCLK1_Frequency; // Hz - - uint32_t prescale = RCC->CFGR & RCC_CFGR_PPRE1; - if (prescale != RCC_CFGR_PPRE1_DIV1) { - // per the stm32 'clock tree' diagram, if the prescaler for APBx is not 1, then - // the timer clock is at double the APBx frequency - timer_clock *= 2; - } - - // Enable the timer clock - periph_config_enable(TIM2, RCC_APB1Periph_TIM2); - - // Setup timer 6 to generate very high priority interrupts - NVIC_InitTypeDef NVIC_InitStructure; - TIM_ClearITPendingBit(TIM2, TIM_IT_Update); - NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = TASK_WATCHDOG_PRIORITY; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - // Setup timer 2 for periodic interrupts at TIMER_INTERRUPT_HZ - TIM_TimeBaseInitTypeDef tim_config; - TIM_TimeBaseStructInit(&tim_config); - - // Clock frequency to run the timer at - uint32_t prescaler = timer_clock / TIMER_CLOCK_HZ; - - // period & prescaler values are 16 bits, check for configuration errors - PBL_ASSERTN(TIME_PERIOD <= UINT16_MAX && prescaler <= UINT16_MAX); - - tim_config.TIM_Period = TIME_PERIOD; - tim_config.TIM_Prescaler = prescaler; - tim_config.TIM_CounterMode = TIM_CounterMode_Up; - TIM_TimeBaseInit(TIM2, &tim_config); - - TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); - TIM_Cmd(TIM2, ENABLE); -#endif - // Setup another unused interrupt vector to handle our low priority interrupts. When we need to do higher // level functions (like PBL_LOG), we trigger this lower-priority interrupt to fire. Since it runs at // configMAX_SYSCALL_INTERRUPT_PRIORITY or lower, it can at least call FreeRTOS ISR functions. -#if MICRO_FAMILY_NRF5 NVIC_SetPriority(WATCHDOG_FREERTOS_IRQn, configMAX_SYSCALL_INTERRUPT_PRIORITY); NVIC_EnableIRQ(WATCHDOG_FREERTOS_IRQn); -#elif MICRO_FAMILY_SF32LB52 - // TODO(SF32LB52): Add implementation -#else - NVIC_InitStructure.NVIC_IRQChannel = WATCHDOG_FREERTOS_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY >> 4; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - NVIC_EnableIRQ(WATCHDOG_FREERTOS_IRQn); -#endif // create the app throttling timer s_throttle_timer_id = new_timer_create(); @@ -338,21 +266,11 @@ void task_watchdog_feed(void) { } static void task_watchdog_disable_interrupt() { -#if MICRO_FAMILY_SF32LB52 -// TODO(SF32LB52): Add implementation -#elif !MICRO_FAMILY_NRF5 - NVIC_DisableIRQ(TIM2_IRQn); -#endif taskENTER_CRITICAL(); } static void task_watchdog_enable_interrupt() { taskEXIT_CRITICAL(); -#if MICRO_FAMILY_SF32LB52 -// TODO(SF32LB52): Add implementation -#elif !MICRO_FAMILY_NRF5 - NVIC_EnableIRQ(TIM2_IRQn); -#endif } void task_watchdog_bit_set_all(void) { @@ -386,22 +304,19 @@ void task_watchdog_mask_clear(PebbleTask task) { task_watchdog_enable_interrupt(); } -void task_watchdog_step_elapsed_time_ms(uint32_t elapsed_ms) { - // nRF5 has the RTC running during sleep, and needs no help here -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - uint32_t timer_ticks = (elapsed_ms * TIMER_CLOCK_HZ) / 1000; - timer_ticks += TIM2->CNT; - - uint8_t timer_ticks_elapsed = timer_ticks / TIME_PERIOD; - if (timer_ticks_elapsed > 0) { - // we don't want the interrupt to fire while we are editing the feed count - TIM_Cmd(TIM2, DISABLE); - s_ticks_since_successful_feed += timer_ticks_elapsed; - TIM_Cmd(TIM2, ENABLE); - } +void task_watchdog_pause(unsigned int seconds) { + task_watchdog_disable_interrupt(); + s_pause_ticks_remaining = seconds * TIMER_INTERRUPT_HZ; + task_watchdog_enable_interrupt(); +} - TIM2->CNT = timer_ticks % TIME_PERIOD; -#endif +void task_watchdog_resume(void) { + task_watchdog_disable_interrupt(); + s_pause_ticks_remaining = 0; + task_watchdog_enable_interrupt(); +} + +void task_watchdog_step_elapsed_time_ms(uint32_t elapsed_ms) { prv_task_watchdog_feed(); } @@ -421,6 +336,14 @@ static void prv_task_watchdog_feed(void) { // bigboards through serial or using flash logging. To accomplish this trigger a lower priority interrupt to fire, // which is at or below configMAX_SYSCALL_INTERRUPT_PRIORITY and make our logging calls from there. + // Handle pause state + if (s_pause_ticks_remaining > 0) { + s_pause_ticks_remaining--; + watchdog_feed(); + s_ticks_since_successful_feed = 0; + return; + } + static int s_last_warning_message_tick_time = 0; //!< Used to rate limit the warning message if ((s_watchdog_bits & s_watchdog_mask) == s_watchdog_mask) { // All tasks have checked in, feed the actual watchdog and clear any state. @@ -435,18 +358,10 @@ static void prv_task_watchdog_feed(void) { reboot_reason_clear(); // Trigger our lower priority interrupt to fire. If it fires when reboot reason is not RebootReasonCode_Watchdog, // it simply logs a message that the we recovered from a watchdog stall -#if !MICRO_FAMILY_SF32LB52 NVIC_SetPendingIRQ(WATCHDOG_FREERTOS_IRQn); -#endif s_last_warning_message_tick_time = 0; } - -#if defined(TARGET_QEMU) - // Investigating PBL-29422 - extern volatile int g_qemu_num_skipped_ticks; - g_qemu_num_skipped_ticks = 0; -#endif // defined(TARGET_QEMU) } // If we haven't fed the watchdog in the last 5 seconds and we haven't @@ -467,18 +382,18 @@ static void prv_task_watchdog_feed(void) { // Trigger our lower priority interrupt to fire. When it sees // RebootReasonCode_Watchdog in the reboot reason, it logs information // about the stuck task -#if !MICRO_FAMILY_SF32LB52 NVIC_SetPendingIRQ(WATCHDOG_FREERTOS_IRQn); -#endif // If the low priority interrupt hasn't reset us by the time 6.5 seconds // rolls around (it will issue the reset if executed at least 6 seconds // after s_last_successful_feed_time), it likely means that we are stuck in // an ISR or low priority interrupts are disabled, so coredump now if (s_ticks_since_successful_feed >= WATCHDOG_COREDUMP_TICK_CNT) { -#if defined(NO_WATCHDOG) - dbgserial_putstr("Would have coredumped if built with watchdogs ... enabling lowpowerdebug!"); - enable_mcu_debugging(); -#else +#if !defined(CONFIG_NO_WATCHDOG) + // Low-pri handler didn't run; capture stuck_task_pc/lr ourselves so + // Memfault has a real PC to fingerprint on. + prv_capture_stuck_task_info(&reboot_reason); + reboot_reason_clear(); + reboot_reason_set(&reboot_reason); reset_due_to_software_failure(); #endif } diff --git a/src/fw/drivers/task_watchdog.h b/src/fw/drivers/task_watchdog.h index 0767cc2839..ca9403ccdd 100644 --- a/src/fw/drivers/task_watchdog.h +++ b/src/fw/drivers/task_watchdog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -31,6 +18,14 @@ void task_watchdog_init(void); //! with TASK_WATCHDOG_FEED_PERIOD_MS to ensure the hardware watchdog is fed. void task_watchdog_feed(void); +//! Pause the task watchdog for a certain number of seconds. This is useful if you know +//! you're going to be doing something that will take a long time and you don't want the +//! watchdog to trigger a reboot. +void task_watchdog_pause(unsigned int seconds); + +//! Resume the task watchdog after a call to task_watchdog_pause. +void task_watchdog_resume(void); + //! Feed the watchdog for a particular task. If a task doesn't call this function frequently //! enough and it's mask is set we will eventually trigger a reboot. void task_watchdog_bit_set(PebbleTask task); diff --git a/src/fw/drivers/temperature.h b/src/fw/drivers/temperature.h index 5f17a37b54..87d77575c9 100644 --- a/src/fw/drivers/temperature.h +++ b/src/fw/drivers/temperature.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/temperature/Kconfig b/src/fw/drivers/temperature/Kconfig new file mode 100644 index 0000000000..403b2bd962 --- /dev/null +++ b/src/fw/drivers/temperature/Kconfig @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig TEMPERATURE + bool "Temperature Sensor" + help + Temperature Sensor Drivers + +if TEMPERATURE + +config TEMPERATURE_SF32LB + bool "SiFli SF32LB" + help + Support for the SiFli SF32LB temperature sensor. + +config TEMPERATURE_STUB + bool "Stub" + help + Stub temperature sensor implementation. + +endif # TEMPERATURE diff --git a/src/fw/drivers/temperature/analog.h b/src/fw/drivers/temperature/analog.h index b578c0094a..696a61454c 100644 --- a/src/fw/drivers/temperature/analog.h +++ b/src/fw/drivers/temperature/analog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/temperature/sf32lb.c b/src/fw/drivers/temperature/sf32lb.c new file mode 100644 index 0000000000..0b55db9013 --- /dev/null +++ b/src/fw/drivers/temperature/sf32lb.c @@ -0,0 +1,68 @@ +/* SPDX-FileCopyrightText: 2025 SiFli Technologies(Nanjing) Co., Ltd */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/temperature.h" + +#include "bf0_hal.h" +#include "board/board.h" +#include "console/prompt.h" +#include "system/logging.h" +#include "system/passert.h" +#include "kernel/util/delay.h" + +#define SLOPE_NUM (2971) // approximate slope molecule +#define SLOPE_DEN (40) // approximate slope denominator +#define OFFSET (277539) +#define ROUND_ADD (SLOPE_DEN / 2) + +void temperature_init(void) { + HAL_RCC_EnableModule(RCC_MOD_TSEN); + hwp_hpsys_cfg->ANAU_CR |= HPSYS_CFG_ANAU_CR_EN_BG; +} + +static void prv_tsen_enable(TSEN_TypeDef *tsen) { + tsen->TSEN_CTRL_REG &= ~TSEN_TSEN_CTRL_REG_ANAU_TSEN_RSTB; + tsen->TSEN_CTRL_REG |= TSEN_TSEN_CTRL_REG_ANAU_TSEN_EN | TSEN_TSEN_CTRL_REG_ANAU_TSEN_PU; + tsen->TSEN_CTRL_REG |= TSEN_TSEN_CTRL_REG_ANAU_TSEN_RSTB; + delay_us(20); + tsen->TSEN_CTRL_REG |= TSEN_TSEN_CTRL_REG_ANAU_TSEN_RUN; +} + +static void prv_tsen_disable(TSEN_TypeDef *tsen) { + tsen->TSEN_CTRL_REG &= ~(TSEN_TSEN_CTRL_REG_ANAU_TSEN_EN | TSEN_TSEN_CTRL_REG_ANAU_TSEN_PU); +} + +int32_t temperature_read(void) { + int32_t temp = 0; + + uint32_t count = 0; + prv_tsen_enable(hwp_tsen); + while ((hwp_tsen->TSEN_IRQ & TSEN_TSEN_IRQ_TSEN_IRSR) == 0) { + HAL_Delay(1); + count++; + if (count > HAL_TSEN_MAX_DELAY) { + temp = INT32_MIN; + break; + } + } + hwp_tsen->TSEN_IRQ |= TSEN_TSEN_IRQ_TSEN_ICR; + if (temp != INT32_MIN) { + // The Celsius conversion formula is: (DATA + 3000)/10100 * 749.2916 − 277.5391 + // In order to calculate milli-Celsius degrees, we can perform the following conversions: + // COEF_NUM/COEF_DEN ≃ 749.2916/10100 × 1000 + // (DATA+3000)*COEF_NUM/COEF_DEN - OFFSET + uint32_t raw = hwp_tsen->TSEN_RDATA; + uint32_t D = raw + 3000; // D = DATA + 3000 + uint32_t num = D * SLOPE_NUM + ROUND_ADD; // discard four, but treat five as whole + uint32_t tmp = num / SLOPE_DEN; + temp = tmp - OFFSET; + } + prv_tsen_disable(hwp_tsen); + + return temp; +} + +void command_temperature_read(void) { + char buffer[32]; + prompt_send_response_fmt(buffer, sizeof(buffer), "%" PRId32 " ", temperature_read()); +} diff --git a/src/fw/drivers/temperature/stubs.c b/src/fw/drivers/temperature/stubs.c new file mode 100644 index 0000000000..d7f9bb332c --- /dev/null +++ b/src/fw/drivers/temperature/stubs.c @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "drivers/temperature.h" +#include "console/prompt.h" + +void temperature_init(void) { + +} + +int32_t temperature_read(void) { + return 0; +} + +void command_temperature_read(void) { + char buffer[32]; + prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRId32" ", temperature_read()); +} diff --git a/src/fw/drivers/temperature/wscript_build b/src/fw/drivers/temperature/wscript_build new file mode 100644 index 0000000000..30b636c8d2 --- /dev/null +++ b/src/fw/drivers/temperature/wscript_build @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes'] + +if bld.env.CONFIG_TEMPERATURE_SF32LB: + sources.append('sf32lb.c') +elif bld.env.CONFIG_TEMPERATURE_STUB: + sources.append('stubs.c') + +bld.objects( + name='driver_temperature', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_temperature') diff --git a/src/fw/drivers/timer.h b/src/fw/drivers/timer.h index 9bfa829098..6f03642099 100644 --- a/src/fw/drivers/timer.h +++ b/src/fw/drivers/timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/touch/Kconfig b/src/fw/drivers/touch/Kconfig new file mode 100644 index 0000000000..1e4fc8bed3 --- /dev/null +++ b/src/fw/drivers/touch/Kconfig @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig TOUCH + bool "Touchscreen" + help + Touchscreen Drivers + +if TOUCH + +config TOUCH_CST816 + bool "Hynitron CST816" + help + Support for the Hynitron CST816 touch controller. + +config TOUCH_QEMU + bool "QEMU Touchscreen" + help + Support for the QEMU touchscreen peripheral. + +module = DRIVER_TOUCH +module-str = Touchscreen +source "src/fw/Kconfig.template.log_level" + +endif # TOUCH diff --git a/src/fw/drivers/touch/cst816/cst816.c b/src/fw/drivers/touch/cst816/cst816.c index 3e4ddc5504..b228b308d3 100644 --- a/src/fw/drivers/touch/cst816/cst816.c +++ b/src/fw/drivers/touch/cst816/cst816.c @@ -1,41 +1,45 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "board/board.h" #include "drivers/exti.h" #include "drivers/gpio.h" #include "drivers/i2c.h" +#include "drivers/touch/touch_sensor.h" #include "kernel/events.h" #include "kernel/util/sleep.h" #include "os/tick.h" -#include "services/common/touch/touch.h" -#include "services/common/system_task.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/touch/touch.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" +#include "util/math.h" + #include "cst816_fw.h" +PBL_LOG_MODULE_DEFINE(driver_touch_cst816, CONFIG_DRIVER_TOUCH_LOG_LEVEL); + #define CST816_RESET_CYCLE_TIME 10 /* ms */ #define CST816_POR_DELAY_TIME 110 /* ms */ #define CST816_REG_WR_DELAY_TIME 2 /* ms */ #define CST816_FW_CHECKSUM_CAL_TIME 500 /* ms */ +#define CST816_POWER_MODE_REG 0xE5 +#define CST816_POWER_MODE_SLEEP 0x03 #define CST816_CHIP_ID_REG 0xA7 #define CST816_FW_VERSION_REG 0xA9 #define CST816_TOUCH_DATA_REG 0x02 #define CST816_TOUCH_DATA_SIZE 5 +#define CST816_GESTURE_ID 0x01 +#define CST816_GESTURE_NONE 0x00 +#define CST816_GESTURE_RIGHT 0x01 +#define CST816_GESTURE_LEFT 0x02 +#define CST816_GESTURE_DOWN 0x03 +#define CST816_GESTURE_UP 0x04 +#define CST816_GESTURE_CLICK 0x05 +#define CST816_GESTURE_DOUBLE_CLICK 0x0B +#define CST816_GESTURE_LONG_PRESS 0x0C #define CST816_BOOT_MODE_REG 0xA001 #define CST816_BOOT_MODE_CMD 0xAB @@ -53,11 +57,23 @@ #define CST816_FW_CHECKSUM_REG 0xA008 #define CST816_FW_VER_INFO_INDEX (-11) +/* Workaround: the CST816 occasionally wedges and stops asserting its INT line. + * If no touch activity is seen between two watchdog checks, hard-reset it. */ +#define CST816_WATCHDOG_PERIOD_MIN 30 + static bool s_callback_scheduled = false; +static bool s_enabled = false; +static bool s_reset_scheduled = false; +static bool s_activity_since_check = false; static PebbleMutex *s_i2c_lock; static void prv_exti_cb(bool *should_context_switch); static void cst816_hw_reset(void); +static void prv_watchdog_cb(void *data); + +static RegularTimerInfo s_watchdog_timer = { + .cb = prv_watchdog_cb, +}; static bool prv_read_data(uint16_t register_address, uint8_t *result, uint16_t size, bool is_work_mode) { mutex_lock(s_i2c_lock); @@ -103,6 +119,11 @@ static bool cst816_enter_bootmode(void) { psleep(CST816_RESET_CYCLE_TIME); NPM1300_OPS.gpio_set(Npm1300_Gpio2, 1); psleep(CST816_RESET_CYCLE_TIME); +#else + gpio_output_set(&CST816->reset, true); + psleep(CST816_RESET_CYCLE_TIME); + gpio_output_set(&CST816->reset, false); + psleep(CST816_RESET_CYCLE_TIME); #endif uint8_t retry_cnt = 10; @@ -143,13 +164,13 @@ static bool cst816_fw_update(void) { uint16_t fw_offset = 6; while (length) { - PBL_LOG(LOG_LEVEL_DEBUG, "fw start_addr:%d length:%d", start_addr, length); + PBL_LOG_DBG("fw start_addr:%d length:%d", start_addr, length); uint8_t addr[2] = {start_addr&0xff, start_addr>>8}; bool rv = prv_write_data(CST816_FW_START_ADDR_REG, addr, 2, 0); psleep(CST816_REG_WR_DELAY_TIME); if(!prv_write_data(CST816_FW_PAGE_REG, app_bin+fw_offset, length>=CST816_FW_PAGE_SIZE?CST816_FW_PAGE_SIZE:length , 0)) { - PBL_LOG(LOG_LEVEL_ERROR, "cst816 update fw error by iic"); + PBL_LOG_ERR("cst816 update fw error by iic"); return false; } psleep(CST816_REG_WR_DELAY_TIME); @@ -158,7 +179,7 @@ static bool cst816_fw_update(void) { psleep(CST816_FW_WR_TIME); for (int t=0;; t++) { if(t > 50) { - PBL_LOG(LOG_LEVEL_ERROR, "cst816 update fw error by writing timeout"); + PBL_LOG_ERR("cst816 update fw error by writing timeout"); return false; } psleep(CST816_RESET_CYCLE_TIME); @@ -178,75 +199,136 @@ static bool cst816_fw_update(void) { uint8_t boot_exit_cmd = CST816_BOOT_EXIT_VAL; bool rv = prv_write_data(CST816_BOOT_EXIT_REG, &boot_exit_cmd, 1, 0); if (!rv) { - PBL_LOG(LOG_LEVEL_ERROR, "exit boot failed"); + PBL_LOG_ERR("exit boot failed"); return false; } cst816_hw_reset(); return true; } - PBL_LOG(LOG_LEVEL_ERROR, "cst816 update fw error by checksum:%x read:%x", checksum, checksum_read); + PBL_LOG_ERR("cst816 update fw error by checksum:%x read:%x", checksum, checksum_read); } return false; } static void cst816_hw_reset(void) { -#if RESET_PIN_CTRLBY_NPM1300 +#ifdef RESET_PIN_CTRLBY_NPM1300 NPM1300_OPS.gpio_set(Npm1300_Gpio2, 0); psleep(CST816_RESET_CYCLE_TIME); NPM1300_OPS.gpio_set(Npm1300_Gpio2, 1); psleep(CST816_POR_DELAY_TIME); +#else + gpio_output_set(&CST816->reset, true); + psleep(CST816_RESET_CYCLE_TIME); + gpio_output_set(&CST816->reset, false); + psleep(CST816_POR_DELAY_TIME); #endif } void touch_sensor_init(void) { - s_i2c_lock = mutex_create(); - cst816_hw_reset(); - uint8_t chip_id; uint8_t fw_version; bool rv; + + s_i2c_lock = mutex_create(); + +#ifndef RESET_PIN_CTRLBY_NPM1300 + gpio_output_init(&CST816->reset, GPIO_OType_PP, GPIO_Speed_2MHz); +#endif + + cst816_hw_reset(); + rv = prv_read_data(CST816_CHIP_ID_REG, &chip_id, 1, 1); - rv &= prv_read_data(CST816_FW_VERSION_REG, &fw_version, 1, 1); if (!rv) { - PBL_ASSERT(rv, "cst816 read chip ID error"); + PBL_LOG_ERR("Could not read CST816 chip ID"); + return; + } + + rv = prv_read_data(CST816_FW_VERSION_REG, &fw_version, 1, 1); + if (!rv) { + PBL_LOG_ERR("Could not read CST816 firmware version"); + return; } - PBL_LOG(LOG_LEVEL_DEBUG, "cst816 fw version:0x%x", fw_version); + + PBL_LOG_DBG("CST816 firmware: 0x%02X", fw_version); uint8_t target_ver = app_bin[sizeof(app_bin) + CST816_FW_VER_INFO_INDEX]; if (target_ver != fw_version) { if (cst816_enter_bootmode()) { - cst816_fw_update(); + rv = cst816_fw_update(); + if (!rv) { + return; + } } else { - PBL_LOG(LOG_LEVEL_ERROR, "cst816 enter boot mode failed"); + PBL_LOG_ERR("Could not enter CST816 boot mode"); + return; } } // initialize exti exti_configure_pin(CST816->int_exti, ExtiTrigger_Falling, prv_exti_cb); - exti_enable(CST816->int_exti); + + touch_sensor_set_enabled(false); } static void prv_process_pending_messages(void* context) { + bool rv; s_callback_scheduled = false; + // Any interrupt means the chip is alive; pet the idle watchdog. + s_activity_since_check = true; + + uint8_t id; + rv = prv_read_data(CST816_GESTURE_ID, &id, 1, 1); + if (!rv) { + PBL_LOG_ERR("Failed to read gesture ID, trying to recover"); + touch_handle_update(TouchState_FingerUp, 0, 0); + exti_disable(CST816->int_exti); + touch_sensor_set_enabled(true); + return; + } + uint8_t data[CST816_TOUCH_DATA_SIZE] = {0}; - bool rv = prv_read_data(CST816_TOUCH_DATA_REG, data, CST816_TOUCH_DATA_SIZE, 1); - PBL_ASSERT(rv, "get touch data error"); + rv = prv_read_data(CST816_TOUCH_DATA_REG, data, CST816_TOUCH_DATA_SIZE, 1); + if (!rv) { + PBL_LOG_ERR("Failed to read touch data, trying to recover"); + touch_handle_update(TouchState_FingerUp, 0, 0); + exti_disable(CST816->int_exti); + touch_sensor_set_enabled(true); + return; + } - const uint64_t current_time_ms = ticks_to_milliseconds(rtc_get_ticks()); uint8_t press = data[0] & 0x0F; GPoint point = { .x = (((uint16_t)(data[1] & 0x0F)) << 8) | data[2], .y = (((uint16_t)(data[3] & 0X0F)) << 8) | data[4], }; + if (CST816->invert_x_axis) { + point.x = CST816->max_x - point.x; + } + + if (CST816->invert_y_axis) { + point.y = CST816->max_y - point.y; + } + + switch (id) { + case CST816_GESTURE_CLICK: + touch_handle_gesture(TouchGesture_Tap, point.x, point.y); + break; + case CST816_GESTURE_DOUBLE_CLICK: + touch_handle_gesture(TouchGesture_DoubleTap, point.x, point.y); + break; + default: + break; + } + if (press == 0x01) { - touch_handle_update(0, TouchState_FingerDown, &point, 0, current_time_ms); + touch_handle_update(TouchState_FingerDown, point.x, point.y); } else { - touch_handle_update(0, TouchState_FingerUp, NULL, 0, current_time_ms); + touch_handle_update(TouchState_FingerUp, point.x, point.y); } } @@ -258,3 +340,53 @@ static void prv_exti_cb(bool *should_context_switch) { system_task_add_callback_from_isr(prv_process_pending_messages, NULL, should_context_switch); s_callback_scheduled = true; } + +// Runs on the system task: the actual recovery reset (cst816_hw_reset() sleeps +// ~120ms, so it must not run on the regular-timer task). +static void prv_idle_reset_worker(void *context) { + s_reset_scheduled = false; + if (!s_enabled) { + return; + } + + exti_disable(CST816->int_exti); + cst816_hw_reset(); + s_callback_scheduled = false; + s_activity_since_check = true; + exti_enable(CST816->int_exti); +} + +// Runs on the regular-timer (NewTimers) task; keep it cheap, just offload. +static void prv_watchdog_cb(void *data) { + if (!s_enabled || s_reset_scheduled) { + return; + } + + if (s_activity_since_check) { + s_activity_since_check = false; + return; + } + + s_reset_scheduled = true; + system_task_add_callback(prv_idle_reset_worker, NULL); +} + +void touch_sensor_set_enabled(bool enabled) { + if (enabled) { + cst816_hw_reset(); + exti_enable(CST816->int_exti); + s_enabled = true; + s_activity_since_check = true; + if (!regular_timer_is_scheduled(&s_watchdog_timer)) { + regular_timer_add_multiminute_callback(&s_watchdog_timer, CST816_WATCHDOG_PERIOD_MIN); + } + } else { + s_enabled = false; + if (regular_timer_is_scheduled(&s_watchdog_timer)) { + regular_timer_remove_callback(&s_watchdog_timer); + } + uint8_t data = CST816_POWER_MODE_SLEEP; + prv_write_data(CST816_POWER_MODE_REG, &data, 1, 1); + exti_disable(CST816->int_exti); + } +} diff --git a/src/fw/drivers/touch/cst816/touch_sensor_definitions.h b/src/fw/drivers/touch/cst816/touch_sensor_definitions.h index b704baea8a..dcf3173301 100644 --- a/src/fw/drivers/touch/cst816/touch_sensor_definitions.h +++ b/src/fw/drivers/touch/cst816/touch_sensor_definitions.h @@ -1,26 +1,20 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "board/board.h" +#ifdef CONFIG_BOARD_FAMILY_OBELIX #define RESET_PIN_CTRLBY_NPM1300 1 +#endif typedef struct { I2CSlavePort *i2c; I2CSlavePort *i2c_boot; ExtiConfig int_exti; + OutputConfig reset; + uint16_t max_x; + uint16_t max_y; + bool invert_x_axis; + bool invert_y_axis; } TouchSensor; diff --git a/src/fw/drivers/touch/ewd1000/ewd1000.c b/src/fw/drivers/touch/ewd1000/ewd1000.c deleted file mode 100644 index 9b72919c15..0000000000 --- a/src/fw/drivers/touch/ewd1000/ewd1000.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "touch_sensor_definitions.h" - -#include "drivers/touch/touch_sensor.h" - -#include "board/board.h" -#include "drivers/exti.h" -#include "drivers/gpio.h" -#include "drivers/i2c.h" -#include "drivers/rtc.h" -#include "kernel/events.h" -#include "kernel/util/delay.h" -#include "os/tick.h" -#include "services/common/touch/touch.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/net.h" - -#include - -// general constants for the controller -#define INIT_TIMEOUT_S (1) - -// definition of active region on touch panel -#define MIN_RAW_X (220) -#define MAX_RAW_X (820) -#define MIN_RAW_Y (120) -#define MAX_RAW_Y (820) - -// packet ids -#define PACKET_ID_STATUS_RESPONSE (0x52) -#define PACKET_ID_STATUS_READ (0x53) -#define PACKET_ID_STATUS_WRITE (0x54) -#define PACKET_ID_RAM_RESPONSE (0x95) -#define PACKET_ID_RAM_READ (0x96) -#define PACKET_ID_RAM_WRITE (0x97) -#define PACKET_ID_FLASH_RESPONSE (0x97) -#define PACKET_ID_FLASH_READ (0x98) -#define PACKET_ID_HELLO (0x55) -#define PACKET_ID_TOUCH_STATUS (0x5A) -#define PACKET_ID_PALM_DETECTION (0xBA) - -// packet footers -#define PACKET_FOOTER (0x01) -#define RAM_FLASH_FOOTER (0xF1) - -// message-related defines -#define HELLO_MESSAGE_DATA (0x55555555) -#define MESSAGE_PADDING (0xFFFFFFFF) - -// addresses in RAM of interesting settings -#define RAM_ADDR_UNLOCK (0xFFF1) -#define RAM_ADDR_LOCK (0xFFF0) -#define RAM_ADDR_PALM_DETECTION (0x04F1) -#define RAM_ADDR_TOUCHDOWN_RETRIES (0x0474) -#define RAM_ADDR_LIFTOFF_RETRIES (0x04D3) - -// magic number for enabling palm detection + reporting -#define RAM_VALUE_ENABLE_PALM_DETECTION (0x02BC) - -// If we don't service an interrupt, the controller will retry every 10ms. We never want to lose a -// touch event, so we set the number of retries to a very high value (currently 1 minute worth). -// HACK WARNING: This is a bit of a hack to get around their very low default retry count which -// could cause us to lose events if the system is busy doing something else. -#define NUM_RETRIES (0x10CC) - -// data lengths -#define DATA_LEN_FINGER (3) -#define DATA_LEN_STATUS_RESPONSE (3) - -typedef union { - uint8_t packet_id; - struct PACKED { - uint32_t data; - uint32_t padding; - } raw; - struct PACKED { - uint8_t packet_id; - uint8_t finger_data[MAX_NUM_TOUCHES][DATA_LEN_FINGER]; - uint8_t active_fingers; - } touch; -} EventMessage; -_Static_assert(sizeof(EventMessage) == 8, "eWD1000 event messages should be 8 bytes."); - -#if 0 -// TODO: used by prv_status_register_read() -typedef enum { - StatusReadType_8bit, - StatusReadType_12bit, - StatusReadType_16bit -} StatusReadType; - -typedef struct PACKED { - uint8_t packet_id; - uint8_t address; - uint8_t padding; - uint8_t footer; -} StatusRegisterRequest; - -typedef struct PACKED { - uint8_t packet_id; - uint8_t data[DATA_LEN_STATUS_RESPONSE]; - uint32_t padding; -} StatusRegisterResponse; -#endif - -typedef struct PACKED { - uint8_t packet_id; - uint16_t address; - uint16_t value; - uint8_t footer; -} MemoryPacket; - -static bool s_callback_scheduled = false; - - -// Low-level helper functions -//////////////////////////////////////////////////////////////////////////////// - -static void prv_write_data(const uint8_t *data, size_t len) { - i2c_use(EWD1000->i2c); - PBL_ASSERTN(i2c_write_block(EWD1000->i2c, len, data)); - i2c_release(EWD1000->i2c); -} - -static void prv_read_data(uint8_t *data, size_t len) { - i2c_use(EWD1000->i2c); - PBL_ASSERTN(i2c_read_block(EWD1000->i2c, len, data)); - i2c_release(EWD1000->i2c); -} - -static void prv_wait_for_interrupt(void) { - const RtcTicks timeout = rtc_get_ticks() + RTC_TICKS_HZ * INIT_TIMEOUT_S; - while (gpio_input_read(&EWD1000->int_gpio)) { - if (rtc_get_ticks() > timeout) { - PBL_CROAK("Touch controller didn't respond!"); - } - } -} - - -// Status Register Operations -//////////////////////////////////////////////////////////////////////////////// - -#if 0 -// TODO: this will be used to get FW information from the controller -static void prv_status_register_read(uint8_t address, void *result, StatusReadType type) { - const StatusRegisterRequest request = { - .packet_id = PACKET_ID_STATUS_READ, - .address = address, - .padding = 0, - // 12-bit reads set the footer to 0 - .footer = type == StatusReadType_12bit ? 0x00 : PACKET_FOOTER - }; - prv_write_data((uint8_t *)&request, sizeof(request)); - prv_wait_for_interrupt(); - - StatusRegisterResponse response = { }; - prv_read_data((uint8_t *)&response, sizeof(response)); - PBL_ASSERTN(response.packet_id == PACKET_ID_STATUS_RESPONSE); - // TODO: remove this assert and footer ones below once we're sure the controller FW is stable - PBL_ASSERTN(response.padding == MESSAGE_PADDING); - - // What follows is some rather hacky code to get the data into a nice and neat integer. - // More info: https://pebbletechnology.atlassian.net/wiki/display/PRODUCT/Elan+Protocol - if (type == StatusReadType_8bit) { - // The 3 data bytes are ST PQ 01 (in hex) with ST being the address and PQ being the value. - PBL_ASSERTN(response.data[0] == address); - PBL_ASSERTN(response.data[2] == PACKET_FOOTER); - *(uint8_t *)result = response.data[1]; - } else if (type == StatusReadType_12bit) { - // The 3 data bytes are ST PQ R1 (in hex) with ST being the address and RPQ being the value. - PBL_ASSERTN(response.data[0] == address); - PBL_ASSERTN((response.data[2] & 0x0F) == PACKET_FOOTER); - *(uint16_t *)result = response.data[1] | ((uint16_t)(response.data[2] & 0xF0) << 4); - } else if (type == StatusReadType_16bit) { - // The 3 data bytes are SP QR S1 (in hex) with S being the first 4-bits of the address and PQRS - // being the value. - PBL_ASSERTN((response.data[0] & 0xF0) == (address & 0xF0)); - PBL_ASSERTN((response.data[2] & 0x0F) == PACKET_FOOTER); - *(uint16_t *)result = (response.data[0] << 4) | (response.data[1] >> 4); - *(uint16_t *)result <<= 8; - *(uint16_t *)result |= (response.data[1] << 4) | (response.data[2] >> 4); - } else { - // invalid type - WTF; - } -} -#endif - - -// Memory Operations -//////////////////////////////////////////////////////////////////////////////// - -#if 0 -// TODO: we might want to use these this to verify configuration settings -static uint16_t prv_read_ram(uint16_t address) { - address = htons(address); - MemoryPacket packet = { - .packet_id = PACKET_ID_RAM_READ, - .address = address, - .value = 0x00, - .footer = RAM_FLASH_FOOTER - }; - prv_write_data((uint8_t *)&packet, sizeof(packet)); - prv_wait_for_interrupt(); - prv_read_data((uint8_t *)&packet, sizeof(packet)); - PBL_ASSERTN(packet.packet_id == PACKET_ID_RAM_RESPONSE); - PBL_ASSERTN(packet.address == address); - PBL_ASSERTN(packet.footer == RAM_FLASH_FOOTER); - return ntohs(packet.value); -} -#endif - -static void prv_write_ram(uint16_t address, uint16_t value) { - address = htons(address); - value = htons(value); - MemoryPacket data = { - .packet_id = PACKET_ID_RAM_WRITE, - .address = address, - .value = value, - .footer = RAM_FLASH_FOOTER - }; - prv_write_data((uint8_t *)&data, sizeof(data)); -} - -#if 0 -// TODO: we will use this to verify firmware when upgrading -static uint16_t prv_read_flash(uint16_t address) { - address = htons(address); - MemoryPacket packet = { - .packet_id = PACKET_ID_FLASH_READ, - .address = address, - .value = 0x00, - .footer = RAM_FLASH_FOOTER - }; - prv_write_data((uint8_t *)&packet, sizeof(packet)); - prv_wait_for_interrupt(); - prv_read_data((uint8_t *)&packet, sizeof(packet)); - PBL_ASSERTN(packet.packet_id == PACKET_ID_FLASH_RESPONSE); - PBL_ASSERTN(packet.address == address); - PBL_ASSERTN(packet.footer == RAM_FLASH_FOOTER); - return ntohs(packet.value); -} -#endif - - -// Interrupt / Callback -//////////////////////////////////////////////////////////////////////////////// - -static void prv_scale_position(GPoint *pos) { - // swap X and Y since the screen is rotated - int32_t x = pos->y; - int32_t y = pos->x; - - // clip down to the box we care about on the screen - x = CLIP(x, MIN_RAW_X, MAX_RAW_X-1) - MIN_RAW_X; - y = CLIP(y, MIN_RAW_Y, MAX_RAW_Y-1) - MIN_RAW_Y; - - // scale to our screen size - x = (x * DISP_COLS) / (MAX_RAW_X - MIN_RAW_X); - y = (y * DISP_ROWS) / (MAX_RAW_Y - MIN_RAW_Y); - - // fix the fact that Y is inverted - y = DISP_ROWS - 1 - y; - - // set the result - *pos = GPoint(x, y); -} - -static void prv_process_pending_messages(void *context) { - s_callback_scheduled = false; - - const uint64_t current_time_ms = ticks_to_milliseconds(rtc_get_ticks()); - while (!gpio_input_read(&EWD1000->int_gpio)) { - EventMessage message = { }; - prv_read_data((uint8_t *)&message, sizeof(message)); - - // Packet format: https://pebbletechnology.atlassian.net/wiki/display/PRODUCT/Elan+Protocol - if (message.packet_id == PACKET_ID_TOUCH_STATUS) { - for (uint32_t i = 0; i < ARRAY_LENGTH(message.touch.finger_data); i++) { - const uint8_t msbs = message.touch.finger_data[i][0]; - const uint8_t x_lsb = message.touch.finger_data[i][1]; - const uint8_t y_lsb = message.touch.finger_data[i][2]; - GPoint point = { - .x = ((uint16_t)msbs & 0xF0) << 4 | x_lsb, - .y = ((uint16_t)msbs & 0x0F) << 8 | y_lsb - }; - prv_scale_position(&point); - if (message.touch.active_fingers & (1 << i)) { - touch_handle_update(i, TouchState_FingerDown, &point, 0, current_time_ms); - } else { - touch_handle_update(i, TouchState_FingerUp, NULL, 0, current_time_ms); - } - } - } else if (message.packet_id == PACKET_ID_PALM_DETECTION) { - touch_handle_driver_event(TouchDriverEvent_PalmDetect); - } else if (message.packet_id == PACKET_ID_HELLO) { - // TODO: PBL-29944 handle this gracefully by re-initializing - should "never" happen - PBL_CROAK("Touch controller reset!"); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Got unexpected packet (%"PRIx8")", message.packet_id); - } - } -} - -static void prv_exti_cb(bool *should_context_switch) { - if (s_callback_scheduled) { - return; - } - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback.callback = prv_process_pending_messages - }; - *should_context_switch = event_put_isr(&e); - s_callback_scheduled = true; -} - - -// Initialization -//////////////////////////////////////////////////////////////////////////////// - -void touch_sensor_init(void) { - // configure INT and RESET pins and INT exti - // TODO: PBL-29944 Is this pull-up needed? - gpio_input_init_pull_up_down(&EWD1000->int_gpio, GPIO_PuPd_UP); - gpio_output_init(&EWD1000->reset_gpio, GPIO_OType_PP, GPIO_Speed_2MHz); - - // toggle the reset line and wait for the "Hello" message - gpio_output_set(&EWD1000->reset_gpio, false); - delay_us(1000); - gpio_output_set(&EWD1000->reset_gpio, true); - prv_wait_for_interrupt(); - - // read the "Hello" message explicitly - EventMessage message = { }; - prv_read_data((uint8_t *)&message, sizeof(message)); - PBL_ASSERTN(message.raw.data == HELLO_MESSAGE_DATA); - // TODO: remove this assert once we're sure the controller FW is stable - PBL_ASSERTN(message.raw.padding == MESSAGE_PADDING); - - // unlock the ram so we can modify it - prv_write_ram(RAM_ADDR_UNLOCK, 0); - - // enable palm detection reporting - prv_write_ram(RAM_ADDR_PALM_DETECTION, RAM_VALUE_ENABLE_PALM_DETECTION); - - // increase the retries - prv_write_ram(RAM_ADDR_LIFTOFF_RETRIES, NUM_RETRIES); - - // lock the ram again - prv_write_ram(RAM_ADDR_LOCK, 0); - - // initialize exti - exti_configure_pin(EWD1000->int_exti, ExtiTrigger_Falling, prv_exti_cb); - exti_enable(EWD1000->int_exti); - PBL_LOG(LOG_LEVEL_DEBUG, "Initialized eWD1000 touch controller"); -} diff --git a/src/fw/drivers/touch/ewd1000/touch_sensor_definitions.h b/src/fw/drivers/touch/ewd1000/touch_sensor_definitions.h deleted file mode 100644 index f58de4efbe..0000000000 --- a/src/fw/drivers/touch/ewd1000/touch_sensor_definitions.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "board/board.h" - -#include -#include - -typedef const struct TouchSensor { - I2CSlavePort *i2c; - InputConfig int_gpio; - ExtiConfig int_exti; - OutputConfig reset_gpio; -} TouchSensor; diff --git a/src/fw/drivers/touch/touch_sensor.h b/src/fw/drivers/touch/touch_sensor.h index b2d2356660..5693f444dd 100644 --- a/src/fw/drivers/touch/touch_sensor.h +++ b/src/fw/drivers/touch/touch_sensor.h @@ -1,19 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once void touch_sensor_init(void); + +//! Enable or disable touch sensor interrupts. +//! When disabled, no touch events will be processed. +//! @param enabled true to enable, false to disable +void touch_sensor_set_enabled(bool enabled); diff --git a/src/fw/drivers/touch/wscript_build b/src/fw/drivers/touch/wscript_build new file mode 100644 index 0000000000..c9fc527d0a --- /dev/null +++ b/src/fw/drivers/touch/wscript_build @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_TOUCH_QEMU: + sources.append('../qemu/qemu_touch.c') + use.extend([ + 'freertos', + ]) +elif bld.env.CONFIG_TOUCH_CST816: + sources.append('cst816/cst816.c') + use.extend([ + 'cst816_fw', + 'driver_gpio', + 'freertos', + ]) + +bld.objects( + name='driver_touch', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_touch') diff --git a/src/fw/drivers/uart.h b/src/fw/drivers/uart.h index 264adcae1f..c49ac0dbc4 100644 --- a/src/fw/drivers/uart.h +++ b/src/fw/drivers/uart.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/drivers/uart/Kconfig b/src/fw/drivers/uart/Kconfig new file mode 100644 index 0000000000..02242fd361 --- /dev/null +++ b/src/fw/drivers/uart/Kconfig @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig UART + bool "UART" + help + UART Drivers + +if UART + +config UART_NRF5 + bool "Nordic nRF5 UART Controller" + help + Support for the Nordic nRF5 UART controller. + +config UART_SF32LB + bool "SiFli SF32LB UART Controller" + select HAL_SIFLI_UART + help + Support for the SiFli SF32LB UART controller. + +config UART_QEMU + bool "QEMU Simple UART" + help + Support for the QEMU simple UART peripheral. + +module = DRIVER_UART +module-str = UART +source "src/fw/Kconfig.template.log_level" + +endif # UART diff --git a/src/fw/drivers/uart/nrf5.c b/src/fw/drivers/uart/nrf5.c new file mode 100644 index 0000000000..7c7f251d4e --- /dev/null +++ b/src/fw/drivers/uart/nrf5.c @@ -0,0 +1,390 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "nrf5.h" +#include "drivers/uart.h" + +#include "drivers/dma.h" +#include "drivers/gpio.h" +#include "drivers/periph_config.h" +#include "system/logging.h" +#include "system/passert.h" + +#include "FreeRTOS.h" + +#include + +#include +#include +#ifdef NRF_PPI_BASE +#include +#else +#include +#endif + +PBL_LOG_MODULE_DEFINE(driver_uart_nrf5, CONFIG_DRIVER_UART_LOG_LEVEL); + + +// UART: 8n1, duplex + +static void _uart_event_handler(const nrfx_uarte_event_t *event, void *ctx); + +static void _timer_event_handler(nrf_timer_event_t event_type, void *ctx) { } + +void uart_init(UARTDevice *dev) { + nrfx_uarte_config_t config = { + .txd_pin = dev->tx_gpio, + .rxd_pin = dev->rx_gpio, + .rts_pin = dev->rts_gpio, + .cts_pin = dev->cts_gpio, + .p_context = (void *)dev, + .tx_cache = { .p_buffer = (uint8_t *) dev->state->tx_cache_buffer, .length = sizeof(dev->state->tx_cache_buffer) }, + .rx_cache = { .p_buffer = (uint8_t *) dev->state->rx_cache_buffer, .length = sizeof(dev->state->rx_cache_buffer) }, + .baudrate = NRF_UARTE_BAUDRATE_1000000, + .config = { + .hwfc = NRF_UARTE_HWFC_DISABLED, + .parity = NRF_UARTE_PARITY_EXCLUDED, + .stop = NRF_UARTE_STOP_ONE + }, + .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, + }; + + nrfx_err_t err = nrfx_uarte_init(&dev->periph, &config, _uart_event_handler); + PBL_ASSERTN(err == NRFX_SUCCESS); + + /* Roughly patterned off of https://devzone.nordicsemi.com/f/nordic-q-a/28420/uarte-in-circular-mode */ + nrfx_timer_config_t tconfig = { + .frequency = 1000000, /* dummy */ + .mode = NRF_TIMER_MODE_COUNTER, + .bit_width = NRF_TIMER_BIT_WIDTH_32, + .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, + .p_context = NULL, + }; + err = nrfx_timer_init(&dev->counter, &tconfig, _timer_event_handler); + PBL_ASSERTN(err == NRFX_SUCCESS); + + +#ifdef NRF_PPI_BASE + nrf_ppi_channel_t rxdrdy_count_channel; + nrf_ppi_channel_t endrx_clear_channel; + + err = nrfx_ppi_channel_alloc(&rxdrdy_count_channel); + PBL_ASSERTN(err == NRFX_SUCCESS); + nrfx_ppi_channel_assign(rxdrdy_count_channel, nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_RXDRDY), nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_COUNT)); + nrfx_ppi_channel_enable(rxdrdy_count_channel); + + err = nrfx_ppi_channel_alloc(&endrx_clear_channel); + PBL_ASSERTN(err == NRFX_SUCCESS); + nrfx_ppi_channel_assign(endrx_clear_channel, nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_ENDRX), nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_CLEAR)); + nrfx_ppi_channel_enable(endrx_clear_channel); +#else + uint8_t rxdrdy_count_channel; + uint8_t endrx_clear_channel; + + err = nrfx_dppi_channel_alloc(&rxdrdy_count_channel); + PBL_ASSERTN(err == NRFX_SUCCESS); + NRF_DPPI_ENDPOINT_SETUP(nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_RXDRDY), rxdrdy_count_channel); + NRF_DPPI_ENDPOINT_SETUP(nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_COUNT), rxdrdy_count_channel); + nrfx_dppi_channel_enable(rxdrdy_count_channel); + + err = nrfx_dppi_channel_alloc(&endrx_clear_channel); + PBL_ASSERTN(err == NRFX_SUCCESS); + NRF_DPPI_ENDPOINT_SETUP(nrfx_uarte_event_address_get(&dev->periph, NRF_UARTE_EVENT_ENDRX), endrx_clear_channel); + NRF_DPPI_ENDPOINT_SETUP(nrfx_timer_task_address_get(&dev->counter, NRF_TIMER_TASK_CLEAR), endrx_clear_channel); + nrfx_dppi_channel_enable(endrx_clear_channel); +#endif + + + dev->state->initialized = true; +} + +void uart_init_open_drain(UARTDevice *dev) { + WTF; /* unimplemented, for now */ +} + +void uart_init_tx_only(UARTDevice *dev) { + nrfx_uarte_config_t config = { + .txd_pin = dev->tx_gpio, + .rxd_pin = NRF_UARTE_PSEL_DISCONNECTED, + .rts_pin = NRF_UARTE_PSEL_DISCONNECTED, + .cts_pin = NRF_UARTE_PSEL_DISCONNECTED, + .p_context = (void *)dev, + .tx_cache = { .p_buffer = (uint8_t *) dev->state->tx_cache_buffer, .length = sizeof(dev->state->tx_cache_buffer) }, + .baudrate = NRF_UARTE_BAUDRATE_1000000, + .config = { + .hwfc = NRF_UARTE_HWFC_DISABLED, + .parity = NRF_UARTE_PARITY_EXCLUDED, + .stop = NRF_UARTE_STOP_ONE + }, + .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, + }; + + nrfx_err_t err = nrfx_uarte_init(&dev->periph, &config, _uart_event_handler); + PBL_ASSERTN(err == NRFX_SUCCESS); + + dev->state->initialized = true; +} + +void uart_init_rx_only(UARTDevice *dev) { + WTF; /* unimplemented, for now */ +} + +void uart_deinit(UARTDevice *dev) { + nrfx_uarte_uninit(&dev->periph); +} + +void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { + nrf_uarte_baudrate_t baud_cfg = +#define MKBAUD(b) (baud_rate == b) ? NRF_UARTE_BAUDRATE_##b : + MKBAUD(1200) + MKBAUD(2400) + MKBAUD(4800) + MKBAUD(9600) + MKBAUD(14400) + MKBAUD(19200) + MKBAUD(28800) + MKBAUD(31250) + MKBAUD(38400) + MKBAUD(56000) + MKBAUD(57600) + MKBAUD(76800) + MKBAUD(115200) + MKBAUD(230400) + MKBAUD(250000) + MKBAUD(460800) + MKBAUD(921600) + MKBAUD(1000000) + -1; + if (baud_cfg == (nrf_uarte_baudrate_t)-1) + WTF; + nrfx_uarte_config_t config = { + .txd_pin = dev->tx_gpio, + .rxd_pin = dev->rx_gpio, + .rts_pin = dev->rts_gpio, + .cts_pin = dev->cts_gpio, + .p_context = (void *)dev, + .tx_cache = { .p_buffer = (uint8_t *) dev->state->tx_cache_buffer, .length = sizeof(dev->state->tx_cache_buffer) }, + //.rx_cache = { .p_buffer = (uint8_t *) dev->state->rx_cache_buffer, .length = sizeof(dev->state->rx_cache_buffer) }, + .baudrate = baud_cfg, + .config = { + .hwfc = NRF_UARTE_HWFC_DISABLED, + .parity = NRF_UARTE_PARITY_EXCLUDED, + .stop = NRF_UARTE_STOP_ONE + }, + .interrupt_priority = NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY, + }; + + nrfx_err_t err = nrfx_uarte_reconfigure(&dev->periph, &config); + if (err != NRFX_SUCCESS) + WTF; +} + + +// Read / Write APIs +//////////////////////////////////////////////////////////////////////////////// + +void uart_write_byte(UARTDevice *dev, uint8_t data) { + /* XXX: nRF5 can run either a PIO UART or a DMA, but not tx-as-PIO / + * rx-as-DMA. we could create our own linked TX buffer, but it is not + * really performance critical for now. so for now we will do a blocking + * send on every byte. */ + nrfx_uarte_tx(&dev->periph, &data, 1, NRFX_UARTE_TX_BLOCKING); +} + +uint8_t uart_read_byte(UARTDevice *dev) { + /* NYI for now, only accessory uses it */ + WTF; + return 0; +} + +UARTRXErrorFlags uart_has_errored_out(UARTDevice *dev) { +#if 0 + uint16_t errors = dev->periph->SR; + UARTRXErrorFlags flags = { + .parity_error = (errors & USART_FLAG_PE) != 0, + .overrun_error = (errors & USART_FLAG_ORE) != 0, + .framing_error = (errors & USART_FLAG_FE) != 0, + .noise_detected = (errors & USART_FLAG_NE) != 0, + }; +#endif + UARTRXErrorFlags flags = {}; + return flags; +} + +bool uart_is_rx_ready(UARTDevice *dev) { + // return dev->periph->SR & USART_SR_RXNE; + /* NYI: used only in dialog_bootrom.c */ + WTF; + return false; +} + +bool uart_has_rx_overrun(UARTDevice *dev) { + // return dev->periph->SR & USART_SR_ORE; + WTF; /* NYI: used only in has_errored_out */ + return false; +} + +bool uart_has_rx_framing_error(UARTDevice *dev) { + // return dev->periph->SR & USART_SR_FE; + WTF; /* NYU: used only in has_errored_out */ + return false; +} + +bool uart_is_tx_ready(UARTDevice *dev) { + // return dev->periph->SR & USART_SR_TXE; + /* NYI for now, only accessory uses it */ + WTF; + return false; +} + +bool uart_is_tx_complete(UARTDevice *dev) { + return true; + //return nrfx_uarte_tx_in_progress(&dev->periph); +} + +void uart_wait_for_tx_complete(UARTDevice *dev) { + while (!uart_is_tx_complete(dev)) continue; +} + + +void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { + PBL_ASSERTN(dev->state->initialized); + dev->state->rx_irq_handler = irq_handler; +} + +void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { + PBL_ASSERTN(dev->state->initialized); + WTF; /* accessory only, for now */ + dev->state->tx_irq_handler = irq_handler; +} + +void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { + PBL_ASSERTN(dev->state->initialized); + dev->state->rx_int_enabled = enabled; +} + +void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { + PBL_ASSERTN(dev->state->initialized); + WTF; +} + +void uart_clear_all_interrupt_flags(UARTDevice *dev) { + WTF; /* only used internally? */ +} + + +// DMA +//////////////////////////////////////////////////////////////////////////////// + +#define DMA_BUFFERS 4 + +#define GET_SUBBUF_P(dev, n) (dev->state->rx_dma_buffer + dev->state->rx_dma_length * (n)) + +static void _uart_event_handler(const nrfx_uarte_event_t *event, void *ctx) { + UARTDevice *dev = (UARTDevice *)ctx; + bool should_context_switch = false; + + switch (event->type) { + case NRFX_UARTE_EVT_RX_BUF_REQUEST: + dev->state->rx_dma_index = (dev->state->rx_dma_index + 1) % DMA_BUFFERS; + nrfx_uarte_rx_buffer_set(&dev->periph, GET_SUBBUF_P(dev, dev->state->rx_dma_index), dev->state->rx_dma_length); +#ifdef DEBUG_UART + PBL_LOG_INFO("rxbuf req %p", GET_SUBBUF_P(dev, dev->state->rx_dma_index)); +#endif + break; + case NRFX_UARTE_EVT_RX_BYTE: + /* we'll catch this up in the ring buffer catchup below */ + break; + case NRFX_UARTE_EVT_RX_DONE: { +#ifdef DEBUG_UART + uint8_t *buf = event->data.rx.p_buffer; + PBL_LOG_INFO("rxbuf done %p (hopefully %p)", buf, GET_SUBBUF_P(dev, dev->state->rx_prod_index)); +#endif + dev->state->rx_prod_index = (dev->state->rx_prod_index + 1) % DMA_BUFFERS; + break; + } + default: + break; + } + + /* catch up on any completed buffers */ + for (; dev->state->rx_cons_index != dev->state->rx_prod_index; dev->state->rx_cons_index = (dev->state->rx_cons_index + 1) % DMA_BUFFERS) { + uint8_t *buf = GET_SUBBUF_P(dev, dev->state->rx_cons_index); + size_t ofs = dev->state->rx_cons_pos; + dev->state->rx_cons_pos = 0; + + if (ofs == dev->state->rx_dma_length) /* already consumed */ + continue; + +#ifdef DEBUG_UART + uint8_t *bufx = buf + ofs; + PBL_LOG_INFO("consume complete %p with %lu bytes left: : %02x %02x %02x %02x %02x %02x %02x %02x", buf, dev->state->rx_dma_length - ofs, + bufx[0], bufx[1], bufx[2], bufx[3], bufx[4], bufx[5], bufx[6], bufx[7]); +#endif + + const UARTRXErrorFlags err_flags = {}; /* ignored, for now */ + for (; ofs < dev->state->rx_dma_length; ofs++) { + if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { + should_context_switch |= dev->state->rx_irq_handler(dev, buf[ofs], &err_flags); + } + } + } + + uint32_t curpos = nrfx_timer_capture(&dev->counter, NRF_TIMER_CC_CHANNEL0); + if (dev->state->rx_cons_pos < curpos) { /* if it is greater, then we have wrapped and we will catch it on the completed buffer irq later */ + uint8_t *buf = GET_SUBBUF_P(dev, dev->state->rx_cons_index); + +#ifdef DEBUG_UART + uint8_t *bufx = buf + dev->state->rx_cons_pos; + PBL_LOG_INFO("consume %ld bytes: %02x %02x %02x %02x %02x %02x %02x %02x", curpos - dev->state->rx_cons_pos, + bufx[0], bufx[1], bufx[2], bufx[3], bufx[4], bufx[5], bufx[6], bufx[7]); +#endif + + const UARTRXErrorFlags err_flags = {}; /* ignored, for now */ + for (; dev->state->rx_cons_pos < curpos; dev->state->rx_cons_pos++) { + if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { + should_context_switch |= dev->state->rx_irq_handler(dev, buf[dev->state->rx_cons_pos], &err_flags); + } + } + } + + portEND_SWITCHING_ISR(should_context_switch); +} + +void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { + /* the nRF5 model of DMA is sort of annoying. what we do is we split the + * buffer in half, and double-buffer, swapping back and forth while + * triggering the RXSTOP -> RXSTART shortcut; every time we get a RXDRDY, + * we trigger a RXSTOP, eat the old buffer, and open the new buffer. ugh! + */ + PBL_ASSERTN((((uint32_t) buffer) & 3) == 0); +#ifdef DEBUG_UART + PBL_LOG_INFO("start_rx_dma"); +#endif + dev->state->rx_dma_buffer = buffer; + dev->state->rx_dma_length = length / DMA_BUFFERS; + if (dev->state->rx_dma_length % 4) + dev->state->rx_dma_length -= dev->state->rx_dma_length % 4; + dev->state->rx_dma_index = 0; + dev->state->rx_prod_index = 0; + dev->state->rx_cons_index = 0; + dev->state->rx_cons_pos = 0; + + nrfx_timer_enable(&dev->counter); + nrfx_timer_clear(&dev->counter); + + nrfx_uarte_rxdrdy_enable(&dev->periph); + nrfx_uarte_rx_buffer_set(&dev->periph, dev->state->rx_dma_buffer, dev->state->rx_dma_length); + nrfx_uarte_rx_enable(&dev->periph, NRFX_UARTE_RX_ENABLE_CONT | NRFX_UARTE_RX_ENABLE_KEEP_FIFO_CONTENT); +} + +void uart_stop_rx_dma(UARTDevice *dev) { +#ifdef DEBUG_UART + PBL_LOG_INFO("stop_rx_dma"); +#endif + nrfx_uarte_rx_abort(&dev->periph, true, true); + nrfx_timer_disable(&dev->counter); +} + +void uart_clear_rx_dma_buffer(UARTDevice *dev) { +} diff --git a/src/fw/drivers/uart/nrf5.h b/src/fw/drivers/uart/nrf5.h new file mode 100644 index 0000000000..3b759f88fa --- /dev/null +++ b/src/fw/drivers/uart/nrf5.h @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "drivers/uart.h" + +#include "board/board.h" + +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#include +#include +#pragma GCC diagnostic pop + +typedef struct UARTState { + bool initialized; + UARTRXInterruptHandler rx_irq_handler; + UARTTXInterruptHandler tx_irq_handler; + bool rx_int_enabled; + bool tx_int_enabled; + bool rx_done_pending; + uint8_t *rx_dma_buffer; + uint32_t rx_dma_length; + uint32_t rx_dma_index; + uint32_t rx_prod_index; + uint32_t rx_cons_index; + uint32_t rx_cons_pos; + uint32_t tx_cache_buffer[8]; + uint32_t rx_cache_buffer[8]; + } UARTDeviceState; + +typedef const struct UARTDevice { + UARTDeviceState *state; + bool half_duplex; + uint32_t tx_gpio; + uint32_t rx_gpio; + uint32_t rts_gpio; + uint32_t cts_gpio; + nrfx_uarte_t periph; + nrfx_timer_t counter; +} UARTDevice; + +// thinly wrapped by the IRQ handler in board_*.c +void uart_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/uart/qemu.c b/src/fw/drivers/uart/qemu.c new file mode 100644 index 0000000000..95e5c9e8c7 --- /dev/null +++ b/src/fw/drivers/uart/qemu.c @@ -0,0 +1,203 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/uart.h" +#include "drivers/uart/qemu.h" + +#include "board/board.h" +#include "system/passert.h" + +#include "FreeRTOS.h" + +#include +#include + +#define REG32(addr) (*(volatile uint32_t *)(addr)) + +// QEMU pebble-simple-uart register offsets +#define UART_DATA 0x00 +#define UART_STATE 0x04 +#define UART_CTRL 0x08 +#define UART_INT 0x0C + +// STATE register bits +#define STATE_TX_READY (1 << 0) +#define STATE_RX_READY (1 << 1) + +// CTRL register bits (must match QEMU pebble-simple-uart) +#define CTRL_TX_IE (1 << 0) +#define CTRL_RX_IE (1 << 1) + +// INT register bits (must match QEMU pebble-simple-uart) +#define INT_TX_PENDING (1 << 0) +#define INT_RX_PENDING (1 << 1) + +void uart_init(UARTDevice *dev) { + // Clear any pending interrupts + REG32(dev->base_addr + UART_INT) = INT_RX_PENDING | INT_TX_PENDING; + // Disable interrupts initially + REG32(dev->base_addr + UART_CTRL) = 0; + // Enable this UART's IRQ in the NVIC + NVIC_SetPriority(dev->irqn, dev->irq_priority); + NVIC_EnableIRQ(dev->irqn); +} + +void uart_init_open_drain(UARTDevice *dev) { + uart_init(dev); +} + +void uart_init_tx_only(UARTDevice *dev) { + uart_init(dev); +} + +void uart_init_rx_only(UARTDevice *dev) { + uart_init(dev); +} + +void uart_deinit(UARTDevice *dev) { + REG32(dev->base_addr + UART_CTRL) = 0; + REG32(dev->base_addr + UART_INT) = INT_RX_PENDING | INT_TX_PENDING; +} + +void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { + // QEMU UART does not have a real baud rate; ignore + (void)dev; + (void)baud_rate; +} + +void uart_write_byte(UARTDevice *dev, uint8_t data) { + // Wait for TX ready + while (!(REG32(dev->base_addr + UART_STATE) & STATE_TX_READY)) { + // spin + } + REG32(dev->base_addr + UART_DATA) = data; +} + +uint8_t uart_read_byte(UARTDevice *dev) { + return (uint8_t)(REG32(dev->base_addr + UART_DATA) & 0xFF); +} + +bool uart_is_rx_ready(UARTDevice *dev) { + return (REG32(dev->base_addr + UART_STATE) & STATE_RX_READY) != 0; +} + +bool uart_has_rx_overrun(UARTDevice *dev) { + (void)dev; + return false; +} + +bool uart_has_rx_framing_error(UARTDevice *dev) { + (void)dev; + return false; +} + +bool uart_is_tx_ready(UARTDevice *dev) { + return (REG32(dev->base_addr + UART_STATE) & STATE_TX_READY) != 0; +} + +bool uart_is_tx_complete(UARTDevice *dev) { + return (REG32(dev->base_addr + UART_STATE) & STATE_TX_READY) != 0; +} + +void uart_wait_for_tx_complete(UARTDevice *dev) { + while (!uart_is_tx_complete(dev)) { + // spin + } +} + +UARTRXErrorFlags uart_has_errored_out(UARTDevice *dev) { + (void)dev; + UARTRXErrorFlags flags = {}; + return flags; +} + +void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { + dev->state->rx_irq_handler = irq_handler; +} + +void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { + dev->state->tx_irq_handler = irq_handler; +} + +void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { + // Order matters: the QEMU pebble-simple-uart is level-sensitive on RX, so + // as long as CTRL_RX_IE is set and rx_count > 0 the IRQ keeps re-firing. + // The ISR uses dev->state->rx_int_enabled to decide whether to drain bytes. + // + // If we set the flag false BEFORE clearing CTRL_RX_IE, an incoming IRQ + // re-enters the handler, sees the flag false, skips the drain, returns — + // and immediately re-fires because CTRL_RX_IE is still set and rx_count is + // still > 0. KernelMain only executes one instruction per re-entry cycle + // (just enough to crawl forward) and the install effectively wedges. + // + // For disable: clear hardware first so no more IRQs fire, THEN drop the + // flag. For enable: set the flag first so the handler will drain when the + // hardware IRQ asserts. + uint32_t ctrl = REG32(dev->base_addr + UART_CTRL); + if (enabled) { + dev->state->rx_int_enabled = true; + REG32(dev->base_addr + UART_CTRL) = ctrl | CTRL_RX_IE; + } else { + REG32(dev->base_addr + UART_CTRL) = ctrl & ~CTRL_RX_IE; + dev->state->rx_int_enabled = false; + } +} + +void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { + dev->state->tx_int_enabled = enabled; + uint32_t ctrl = REG32(dev->base_addr + UART_CTRL); + if (enabled) { + ctrl |= CTRL_TX_IE; + } else { + ctrl &= ~CTRL_TX_IE; + } + REG32(dev->base_addr + UART_CTRL) = ctrl; +} + +void uart_clear_all_interrupt_flags(UARTDevice *dev) { + REG32(dev->base_addr + UART_INT) = INT_RX_PENDING | INT_TX_PENDING; +} + +void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { + // DMA not supported on QEMU UART + (void)dev; + (void)buffer; + (void)length; +} + +void uart_stop_rx_dma(UARTDevice *dev) { + (void)dev; +} + +void uart_clear_rx_dma_buffer(UARTDevice *dev) { + (void)dev; +} + +// Called from the IRQ handler trampoline defined via IRQ_MAP in the board file +void uart_irq_handler(UARTDevice *dev) { + uint32_t int_status = REG32(dev->base_addr + UART_INT); + bool should_context_switch = false; + + if (int_status & INT_RX_PENDING) { + REG32(dev->base_addr + UART_INT) = INT_RX_PENDING; + if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { + // Stop if the handler disables RX interrupts (e.g. ISR buffer full); + // remaining bytes stay in the UART FIFO for the next interrupt. + while (dev->state->rx_int_enabled && + (REG32(dev->base_addr + UART_STATE) & STATE_RX_READY)) { + uint8_t byte = (uint8_t)(REG32(dev->base_addr + UART_DATA) & 0xFF); + UARTRXErrorFlags err = {}; + should_context_switch |= dev->state->rx_irq_handler(dev, byte, &err); + } + } + } + + if (int_status & INT_TX_PENDING) { + REG32(dev->base_addr + UART_INT) = INT_TX_PENDING; + if (dev->state->tx_irq_handler && dev->state->tx_int_enabled) { + should_context_switch |= dev->state->tx_irq_handler(dev); + } + } + + portEND_SWITCHING_ISR(should_context_switch); +} diff --git a/src/fw/drivers/uart/qemu.h b/src/fw/drivers/uart/qemu.h new file mode 100644 index 0000000000..0a6217be42 --- /dev/null +++ b/src/fw/drivers/uart/qemu.h @@ -0,0 +1,29 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "board/board.h" +#include "drivers/uart.h" + +typedef struct UARTDeviceState { + UARTRXInterruptHandler rx_irq_handler; + UARTTXInterruptHandler tx_irq_handler; + bool rx_int_enabled; + bool tx_int_enabled; +} UARTDeviceState; + +// UARTDevice is already forward-declared as a typedef in board_qemu.h; +// provide the full struct definition here. +struct UARTDevice { + UARTDeviceState *state; + uint32_t base_addr; + int irqn; + int irq_priority; +}; + +// Called from the IRQ handler trampoline (IRQ_MAP) +void uart_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/uart/sf32lb.c b/src/fw/drivers/uart/sf32lb.c new file mode 100644 index 0000000000..800faf8ad6 --- /dev/null +++ b/src/fw/drivers/uart/sf32lb.c @@ -0,0 +1,304 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "sf32lb.h" + +#include "drivers/uart.h" +#include "kernel/util/stop.h" +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "bf0_hal_dma.h" +#include "bf0_hal_uart.h" + +#include "util/misc.h" + +static void prv_init(UARTDevice *dev, uint32_t mode) { + HAL_StatusTypeDef ret; + + dev->state->huart.Init.Mode = mode; + dev->state->dev = dev; + ret = HAL_UART_Init(&dev->state->huart); + PBL_ASSERTN(ret == HAL_OK); + + switch (mode) { + case UART_MODE_TX_RX: + HAL_PIN_Set(dev->tx.pad, dev->tx.func, dev->tx.flags, 1); + HAL_PIN_Set(dev->rx.pad, dev->rx.func, dev->rx.flags, 1); + break; + case UART_MODE_TX: + HAL_PIN_Set(dev->tx.pad, dev->tx.func, dev->tx.flags, 1); + break; + case UART_MODE_RX: + HAL_PIN_Set(dev->rx.pad, dev->rx.func, dev->rx.flags, 1); + break; + default: + WTF; + break; + } + + dev->state->initialized = true; + + if (dev->state->hdma.Instance != NULL) { + __HAL_LINKDMA(&dev->state->huart, hdmarx, dev->state->hdma); + + HAL_NVIC_SetPriority(dev->dma_irqn, dev->dma_irq_priority, 0); + HAL_NVIC_EnableIRQ(dev->dma_irqn); + + __HAL_UART_ENABLE_IT(&dev->state->huart, UART_IT_IDLE); + } +} + +void uart_init(UARTDevice *dev) { prv_init(dev, UART_MODE_TX_RX); } + +void uart_init_open_drain(UARTDevice *dev) { WTF; } + +void uart_init_tx_only(UARTDevice *dev) { prv_init(dev, UART_MODE_TX); } + +void uart_init_rx_only(UARTDevice *dev) { prv_init(dev, UART_MODE_RX); } + +void uart_deinit(UARTDevice *dev) { + HAL_UART_DeInit(&dev->state->huart); +} + +void uart_set_baud_rate(UARTDevice *dev, uint32_t baud_rate) { + HAL_StatusTypeDef ret; + + PBL_ASSERTN(dev->state->initialized); + + HAL_UART_DeInit(&dev->state->huart); + + dev->state->huart.Init.BaudRate = baud_rate; + ret = HAL_UART_Init(&dev->state->huart); + PBL_ASSERTN(ret == HAL_OK); +} + +// Read / Write APIs +//////////////////////////////////////////////////////////////////////////////// + +void uart_write_byte(UARTDevice *dev, uint8_t data) { + HAL_UART_Transmit(&dev->state->huart, &data, 1, HAL_MAX_DELAY); +} + +uint8_t uart_read_byte(UARTDevice *dev) { + return __HAL_UART_GETC(&dev->state->huart); +} + +bool uart_is_rx_ready(UARTDevice *dev) { + return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_RXNE; +} + +bool uart_has_rx_overrun(UARTDevice *dev) { + return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_ORE; +} + +bool uart_has_rx_framing_error(UARTDevice *dev) { + return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_FE; +} + +bool uart_is_tx_ready(UARTDevice *dev) { + return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_TXE; +} + +bool uart_is_tx_complete(UARTDevice *dev) { + return READ_REG(dev->state->huart.Instance->ISR) & USART_ISR_TC; +} + +void uart_wait_for_tx_complete(UARTDevice *dev) { + while (!uart_is_tx_complete(dev)) continue; +} + +// Interrupts +//////////////////////////////////////////////////////////////////////////////// + +static void prv_set_interrupt_enabled(UARTDevice *dev, bool enabled) { + if (enabled) { + PBL_ASSERTN(dev->state->tx_irq_handler || dev->state->rx_irq_handler); + HAL_NVIC_SetPriority(dev->irqn, dev->irq_priority, 0); + HAL_NVIC_EnableIRQ(dev->irqn); + } else { + HAL_NVIC_DisableIRQ(dev->irqn); + } +} + +void uart_set_rx_interrupt_handler(UARTDevice *dev, UARTRXInterruptHandler irq_handler) { + PBL_ASSERTN(dev->state->initialized); + dev->state->rx_irq_handler = irq_handler; +} + +void uart_set_tx_interrupt_handler(UARTDevice *dev, UARTTXInterruptHandler irq_handler) { + PBL_ASSERTN(dev->state->initialized); + dev->state->tx_irq_handler = irq_handler; +} + +void uart_set_rx_interrupt_enabled(UARTDevice *dev, bool enabled) { + PBL_ASSERTN(dev->state->initialized); + if (enabled) { + stop_mode_disable(InhibitorUARTRX); + dev->state->rx_int_enabled = true; + SET_BIT(dev->state->huart.Instance->CR1, USART_CR1_RXNEIE); + prv_set_interrupt_enabled(dev, true); + } else { + // disable interrupt if TX is also disabled + prv_set_interrupt_enabled(dev, dev->state->tx_int_enabled); + CLEAR_BIT(dev->state->huart.Instance->CR1, USART_CR1_RXNEIE); + dev->state->rx_int_enabled = false; + stop_mode_enable(InhibitorUARTRX); + } +} + +void uart_set_tx_interrupt_enabled(UARTDevice *dev, bool enabled) { + PBL_ASSERTN(dev->state->initialized); + if (enabled) { + dev->state->tx_int_enabled = true; + SET_BIT(dev->state->huart.Instance->CR1, USART_CR1_TXEIE); + prv_set_interrupt_enabled(dev, true); + } else { + // disable interrupt if RX is also disabled + prv_set_interrupt_enabled(dev, dev->state->rx_int_enabled); + CLEAR_BIT(dev->state->huart.Instance->CR1, USART_CR1_TXEIE); + dev->state->tx_int_enabled = false; + } +} + +void uart_irq_handler(UARTDevice *dev) { + PBL_ASSERTN(dev->state->initialized); + bool should_context_switch = false; + uint32_t idx; + + if (dev->state->rx_irq_handler && dev->state->rx_int_enabled) { + const UARTRXErrorFlags err_flags = { + .overrun_error = uart_has_rx_overrun(dev), + .framing_error = uart_has_rx_framing_error(dev), + }; + // DMA + if (dev->state->rx_dma_buffer && (__HAL_UART_GET_FLAG(&dev->state->huart, UART_FLAG_IDLE) != RESET) && + (__HAL_UART_GET_IT_SOURCE(&dev->state->huart, UART_IT_IDLE) != RESET)) { + // process bytes from the DMA buffer + const uint32_t dma_length = dev->state->rx_dma_length; + const uint32_t recv_total_index = dma_length - __HAL_DMA_GET_COUNTER(&dev->state->hdma); + int32_t recv_len = recv_total_index - dev->state->rx_dma_index; + if (recv_len < 0) { + recv_len += dma_length; + } + + idx = dev->state->rx_dma_index; + for (int32_t i = 0; i < recv_len; i++) { + uint8_t data; + data = dev->state->rx_dma_buffer[idx]; + if (dev->state->rx_irq_handler(dev, data, &err_flags)) { + should_context_switch = true; + } + idx++; + if (idx >= dma_length) { + idx = 0; + } + } + dev->state->rx_dma_index = recv_total_index; + if (dev->state->rx_dma_index >= dma_length) { + dev->state->rx_dma_index = 0; + } + uart_clear_all_interrupt_flags(dev); + __HAL_UART_CLEAR_IDLEFLAG(&dev->state->huart); + } else { + const bool has_byte = uart_is_rx_ready(dev); + // read the data register regardless to clear the error flags + const uint8_t data = uart_read_byte(dev); + if (has_byte) { + if (dev->state->rx_irq_handler(dev, data, &err_flags)) { + should_context_switch = true; + } + } + } + } + if (dev->state->tx_irq_handler && dev->state->tx_int_enabled && uart_is_tx_ready(dev)) { + if (dev->state->tx_irq_handler(dev)) { + should_context_switch = true; + } + } + portEND_SWITCHING_ISR(should_context_switch); +} + +void uart_clear_all_interrupt_flags(UARTDevice *dev) { + UART_HandleTypeDef *uart = &dev->state->huart; + if (__HAL_UART_GET_FLAG(uart, UART_FLAG_ORE) != RESET) { + __HAL_UART_CLEAR_OREFLAG(uart); + } + if (__HAL_UART_GET_FLAG(uart, UART_FLAG_NE) != RESET) { + __HAL_UART_CLEAR_NEFLAG(uart); + } + if (__HAL_UART_GET_FLAG(uart, UART_FLAG_FE) != RESET) { + __HAL_UART_CLEAR_FEFLAG(uart); + } + if (__HAL_UART_GET_FLAG(uart, UART_FLAG_PE) != RESET) { + __HAL_UART_CLEAR_PEFLAG(uart); + } +} + +void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { + size_t recv_len; + size_t recv_total_index; + uint32_t idx; + bool should_context_switch = false; + + UARTDeviceState *state = container_of(huart, UARTDeviceState, huart); + UARTDevice *dev = (UARTDevice *)state->dev; + + recv_total_index = state->rx_dma_length - __HAL_DMA_GET_COUNTER(&state->hdma); + if (recv_total_index < state->rx_dma_index) + recv_len = state->rx_dma_length + recv_total_index - state->rx_dma_index; + else + recv_len = recv_total_index - state->rx_dma_index; + + idx = state->rx_dma_index; + state->rx_dma_index = recv_total_index; + if (recv_len) { + for (size_t i = 0; i < recv_len; i++) { + uint8_t data; + data = state->rx_dma_buffer[idx]; + if (state->rx_irq_handler(dev, data, NULL)) { + should_context_switch = true; + } + idx++; + if (idx >= state->rx_dma_length) { + idx = 0; + } + } + } + portEND_SWITCHING_ISR(should_context_switch); +} + +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { + HAL_UART_RxHalfCpltCallback(huart); +} + +// DMA +//////////////////////////////////////////////////////////////////////////////// + +void uart_dma_irq_handler(UARTDevice *dev) { + HAL_DMA_IRQHandler(&dev->state->hdma); +} + +// FIXME(SF32LB52): the IRQ paths above read `rx_dma_buffer[idx]` straight +// without invalidating D-cache, so the CPU could pick up stale pre-DMA bytes. +// There is no active SF32LB52 caller today (dbgserial_set_rx_dma_enabled is a +// no-op for CONFIG_SOC_SF32LB52), so this hasn't been wired up. Before +// enabling, the buffer needs to be cache-line aligned/sized and the callbacks +// must dcache_invalidate the freshly-DMA'd range. +void uart_start_rx_dma(UARTDevice *dev, void *buffer, uint32_t length) { + dev->state->rx_dma_buffer = buffer; + dev->state->rx_dma_length = length; + dev->state->rx_dma_index = 0; + __HAL_UART_ENABLE_IT(&dev->state->huart, UART_IT_IDLE); + HAL_UART_DmaTransmit(&dev->state->huart, buffer, length, DMA_PERIPH_TO_MEMORY); +} + +void uart_stop_rx_dma(UARTDevice *dev) { + dev->state->rx_dma_buffer = NULL; + dev->state->rx_dma_length = 0; + HAL_UART_DMAPause(&dev->state->huart); +} + +void uart_clear_rx_dma_buffer(UARTDevice *dev) { + dev->state->rx_dma_index = dev->state->rx_dma_length - __HAL_DMA_GET_COUNTER(&dev->state->hdma); +} diff --git a/src/fw/drivers/uart/sf32lb.h b/src/fw/drivers/uart/sf32lb.h new file mode 100644 index 0000000000..7b205157e1 --- /dev/null +++ b/src/fw/drivers/uart/sf32lb.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#include "board/board.h" +#include "drivers/uart.h" + +typedef struct UARTState { + bool initialized; + UARTRXInterruptHandler rx_irq_handler; + UARTTXInterruptHandler tx_irq_handler; + bool rx_int_enabled; + bool tx_int_enabled; + uint8_t *rx_dma_buffer; + uint32_t rx_dma_length; + uint32_t rx_dma_index; + UART_HandleTypeDef huart; + DMA_HandleTypeDef hdma; + const void *dev; +} UARTDeviceState; + +typedef const struct UARTDevice { + UARTDeviceState *state; + Pinmux rx; + Pinmux tx; + IRQn_Type irqn; + uint8_t irq_priority; + IRQn_Type dma_irqn; + uint8_t dma_irq_priority; +} UARTDevice; + +// thinly wrapped by the IRQ handler in board_*.c +void uart_irq_handler(UARTDevice *dev); +void uart_dma_irq_handler(UARTDevice *dev); diff --git a/src/fw/drivers/uart/wscript_build b/src/fw/drivers/uart/wscript_build new file mode 100644 index 0000000000..cfd6ed3319 --- /dev/null +++ b/src/fw/drivers/uart/wscript_build @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +use = ['freertos', 'fw_includes'] +sources = [] + +if bld.env.CONFIG_UART_QEMU: + sources.append('qemu.c') +elif bld.env.CONFIG_UART_NRF5: + use.append('hal_nordic') + sources.append('nrf5.c') +elif bld.env.CONFIG_UART_SF32LB: + use.append('hal_sifli') + sources.append('sf32lb.c') + +bld.objects( + name='driver_uart', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_uart') \ No newline at end of file diff --git a/src/fw/drivers/vibe.h b/src/fw/drivers/vibe.h index d28e325c92..7d31edbcf6 100644 --- a/src/fw/drivers/vibe.h +++ b/src/fw/drivers/vibe.h @@ -1,24 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include +#include "system/status_codes.h" + // motor full power #define VIBE_STRENGTH_MAX 100 // motor full reverse @@ -33,3 +22,19 @@ void vibe_set_strength(int8_t strength); //! Return the strength that should be used for braking the motor to a stop. int8_t vibe_get_braking_strength(void); + +//! Calibrate the vibe motor driver for optimal performance. +//! +//! If vibration motor has NV memory, calibration data will be stored there on +//! a successful calibration. +//! +//! @retval S_SUCCESS indicating success or failure of calibration. +//! @retval error Code indicating failure of calibration. +status_t vibe_calibrate(void); + +//! Return the driver-specific calibration value computed by the last +//! successful vibe_calibrate(), for persisting to non-volatile storage. +uint8_t vibe_get_calibration(void); + +//! Apply a previously stored calibration value to the driver. +void vibe_apply_calibration(uint8_t cali); diff --git a/src/fw/drivers/vibe/Kconfig b/src/fw/drivers/vibe/Kconfig new file mode 100644 index 0000000000..1f1187aefc --- /dev/null +++ b/src/fw/drivers/vibe/Kconfig @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig VIBE + bool "Vibration Motor" + help + Vibration Motor Drivers + +if VIBE + +config VIBE_AW86225 + bool "Awinic AW86225" + help + Support for the Awinic AW86225 haptic driver. + +config VIBE_AW8623X + bool "Awinic AW8623X" + help + Support for the Awinic AW8623X haptic driver. + +config VIBE_DRV2604 + bool "TI DRV2604" + help + Support for the TI DRV2604 haptic driver. + +config VIBE_PWM + bool "PWM-based vibration" + help + Support for PWM-based vibration motor control. + +module = DRIVER_VIBE +module-str = Vibration Motor +source "src/fw/Kconfig.template.log_level" + +endif # VIBE diff --git a/src/fw/drivers/vibe/vibe.c b/src/fw/drivers/vibe/vibe.c index 1e52bad92d..dc6cb62afc 100644 --- a/src/fw/drivers/vibe/vibe.c +++ b/src/fw/drivers/vibe/vibe.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/vibe.h" @@ -28,13 +15,15 @@ #include "system/passert.h" #include "util/math.h" -#include "services/common/analytics/analytics.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/battery/battery_state.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/analytics/analytics.h" #include +PBL_LOG_MODULE_DEFINE(driver_vibe, CONFIG_DRIVER_VIBE_LOG_LEVEL); + //! Make a resolution of 100. Working in integer duty cycles on the following ranges: //! @@ -137,7 +126,7 @@ static void prv_vibe_raw_ctl(bool on) { if (BOARD_CONFIG_VIBE.options & ActuatorOptions_Pwm) { const uint32_t duty_cycle = (on) ? s_vibe_duty_cycle : PWM_DUTY_CYCLE_OFF; prv_vibe_pwm_enable(on); - pwm_set_duty_cycle(&BOARD_CONFIG_VIBE.pwm, s_vibe_duty_cycle); + pwm_set_duty_cycle(&BOARD_CONFIG_VIBE.pwm, duty_cycle); } if (BOARD_CONFIG_VIBE.options & ActuatorOptions_Ctl) { @@ -159,18 +148,7 @@ void vibe_ctl(bool on) { on = false; } - static bool s_on = false; - if (on && !s_on) { - analytics_inc(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_COUNT, AnalyticsClient_System); - analytics_stopwatch_start(ANALYTICS_APP_METRIC_VIBRATOR_ON_TIME, AnalyticsClient_App); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME, AnalyticsClient_System); - } else if (!on && s_on) { - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_VIBRATOR_ON_TIME); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME); - } - s_on = on; - - PBL_LOG(LOG_LEVEL_DEBUG, "Vibe status <%s>", on ? "on" : "off"); + PBL_LOG_DBG("Vibe status <%s>", on ? "on" : "off"); prv_vibe_raw_ctl(on); } @@ -193,6 +171,16 @@ int8_t vibe_get_braking_strength(void) { } } +status_t vibe_calibrate(void) { + return E_INVALID_OPERATION; +} + +uint8_t vibe_get_calibration(void) { + return 0xFF; +} + +void vibe_apply_calibration(uint8_t cali) { +} void command_vibe_ctl(const char *arg) { int strength = atoi(arg); diff --git a/src/fw/drivers/vibe/vibe_aw86225.c b/src/fw/drivers/vibe/vibe_aw86225.c index 0816110017..fc79976f3b 100644 --- a/src/fw/drivers/vibe/vibe_aw86225.c +++ b/src/fw/drivers/vibe/vibe_aw86225.c @@ -1,75 +1,113 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/vibe.h" #include "board/board.h" #include "console/prompt.h" #include "drivers/gpio.h" #include "drivers/i2c.h" +#include "drivers/vibe/vibe_aw86225.h" #include "system/logging.h" #include "system/passert.h" #include "kernel/util/sleep.h" -#define AW862XX_REG_ID (0x00) +PBL_LOG_MODULE_DEFINE(driver_vibe_aw86225, CONFIG_DRIVER_VIBE_LOG_LEVEL); + +#define AW862XX_REG_SRST (0x00) +#define AW862XX_REG_PLAYCFG2 (0x07) +#define AW862XX_REG_PLAYCFG3 (0x08) +#define AW862XX_REG_PLAYCFG4 (0x09) +#define AW862XX_REG_WAVCFG1 (0x0A) +#define AW862XX_REG_WAVCFG2 (0x0B) +#define AW862XX_REG_WAVCFG9 (0x12) #define AW862XX_REG_CONTCFG1 (0x18) #define AW862XX_REG_CONTCFG2 (0x19) #define AW862XX_REG_CONTCFG3 (0x1A) -#define AW862XX_REG_CONTCFG4 (0x1B) -#define AW862XX_REG_CONTCFG5 (0x1C) #define AW862XX_REG_CONTCFG6 (0x1D) #define AW862XX_REG_CONTCFG7 (0x1E) #define AW862XX_REG_CONTCFG8 (0x1F) #define AW862XX_REG_CONTCFG9 (0x20) #define AW862XX_REG_CONTCFG10 (0x21) #define AW862XX_REG_CONTCFG11 (0x22) -#define AW862XX_REG_CONTCFG12 (0x23) -#define AW862XX_REG_CONTCFG13 (0x24) -#define AW862XX_REG_PLAYCFG3 (0x08) -#define AW862XX_REG_PLAYCFG4 (0x09) +#define AW862XX_REG_CONTRD14 (0x25) +#define AW862XX_REG_CONTRD15 (0x26) +#define AW862XX_REG_CONTRD16 (0x27) +#define AW862XX_REG_CONTRD17 (0x28) +#define AW862XX_REG_RTPCFG1 (0x2D) +#define AW862XX_REG_RTPCFG2 (0x2E) +#define AW862XX_REG_RTPCFG3 (0x2F) +#define AW862XX_REG_GLBRD5 (0x3F) +#define AW862XX_REG_RAMADDRH (0x40) +#define AW862XX_REG_RAMDATA (0x42) #define AW862XX_REG_SYSCTRL1 (0x43) +#define AW862XX_REG_SYSCTRL2 (0x44) +#define AW862XX_REG_TRIMCFG3 (0x5A) +#define AW862XX_REG_CHIPID (0x64) #define AW862XX_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) -#define AW862XX_BIT_PLAYCFG3_BRK (1<<2) #define AW862XX_BIT_PLAYCFG3_BRK_ENABLE (1<<2) -#define AW862XX_BIT_PLAYCFG3_BRK_DISABLE (0<<2) #define AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) -#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) #define AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) -#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) #define AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) /* PLAYCFG4: reg 0x09 RW */ -#define AW862XX_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) #define AW862XX_BIT_PLAYCFG4_STOP_ON (1<<1) -#define AW862XX_BIT_PLAYCFG4_STOP_OFF (0<<1) -#define AW862XX_BIT_PLAYCFG4_GO_MASK (~(1<<0)) #define AW862XX_BIT_PLAYCFG4_GO_ON (1<<0) -#define AW862XX_BIT_PLAYCFG4_GO_OFF (0<<0) -#define AW862XX_CONTCFG1_EDGE_FREQ (0xC0) -#define AW862XX_CONTCFG1_WAVE_MODE (0x01) /* 0:sine; 1:cos */ -#define AW862XX_CONTCFG2_CONF_F0 (102) /* REG = 24,000/f0 */ -#define AW862XX_CONTCFG3_DRV_WIDTH (209) /* f0-8-track_margin-brk_gain*/ +#define AW862XX_F0_CALI_LSB_PERMYRIAD (24) +#define AW862XX_CONTCFG1_EDGE_FREQ_NONE (0x00) +#define AW862XX_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW862XX_CONTCFG1_EN_F0_DET (1<<3) +#define AW862XX_CONTCFG2_CONF_F0 (24000U / s_drive_frequency_hz) +#define AW862XX_CONTCFG3_DRV_WIDTH (24000U / s_drive_frequency_hz - 8U - 8U - 15U) +#define AW862XX_CONTCFG3_F0_DET_DRV_WIDTH (24000U / AW86225->lra_frequency_hz - 8U - 8U - 15U) #define AW862XX_CONTCFG7_FULL_SCALE (0x7FL) -#define AW862XX_CONTCFG6_TRACK_MASK (~(1<<7)) +#define AW862XX_CONTCFG7_DRV2_LEVEL AW862XX_CONTCFG7_FULL_SCALE +#define AW862XX_CONTCFG8_DRV1_TIME (0x04U) +#define AW862XX_CONTCFG9_DRV2_TIME_MAX (0xFFU) +#define AW862XX_CONTCFG10_BRK_TIME (0x08U) +#define AW862XX_CONTCFG11_TRACK_MARGIN (0x0FU) #define AW862XX_CONTCFG6_TRACK_EN (1<<7) +#define AW862XX_RAM_BASE_ADDR (0x0010U) +#define AW862XX_RAM_WAVEFORM_SEQ (0x01U) +#define AW862XX_PLAYCFG2_GAIN_MAX (0x80U) +#define AW862XX_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) +#define AW862XX_GLBRD5_STATE_MASK (0x0F) +#define AW862XX_GLBRD5_STATE_STANDBY (0x00) +#define AW862XX_TRIMCFG3_TRIM_LRA_MASK (~(0x3F)) +#define AW862XX_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW862XX_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW862XX_SYSCTRL1_RAMINIT_OFF (0<<3) #define AW862XX_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) #define AW862XX_SYSCTRL1_VBAT_MODE_EN (1<<7) +#define AW862XX_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW862XX_SYSCTRL2_STANDBY_ON (1<<6) +#define AW862XX_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW862XX_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW862XX_SYSCTRL2_RATE_12K (2<<0) -#define AW862XX_POR_WAIT_TIME (2) /* ms */ +#define AW862XX_PWR_OFF_TIME (2) /* ms */ +#define AW862XX_PWR_ON_TIME (8) /* ms */ +#define AW862XX_STOP_STANDBY_RETRIES (40) +#define AW862XX_STOP_STANDBY_POLL_MS (2) +#define AW862XX_F0_DET_STANDBY_RETRIES (200) +#define AW862XX_F0_DET_STANDBY_POLL_MS (10) +#define AW862XX_TRIM_LRA_INVALID (0xFF) + +static bool s_initialized = false; +static int8_t s_target_strength = VIBE_STRENGTH_MAX; +static uint16_t s_drive_frequency_hz; +static uint8_t s_trim_lra = AW862XX_TRIM_LRA_INVALID; + +static const uint8_t s_aw86225_ram_waveform[] = { + 0x55, 0x00, 0x15, 0x00, 0x46, + 0x00, 0x10, 0x20, 0x2e, 0x3c, 0x49, 0x54, 0x5e, 0x67, 0x6d, + 0x72, 0x75, 0x77, 0x77, 0x75, 0x72, 0x6d, 0x67, 0x5e, 0x54, + 0x49, 0x3c, 0x2e, 0x20, 0x10, 0x00, 0xf0, 0xe0, 0xd2, 0xc4, + 0xb7, 0xac, 0xa2, 0x99, 0x93, 0x8e, 0x8b, 0x89, 0x89, 0x8b, + 0x8e, 0x93, 0x99, 0xa2, 0xac, 0xb7, 0xc4, 0xd2, 0xe0, 0xf0, +}; static bool prv_read_register(uint8_t register_address, uint8_t* data) { i2c_use(I2C_AW86225); @@ -85,59 +123,258 @@ static bool prv_write_register(uint8_t register_address, uint8_t datum) { return rv; } -void prv_modify_reg(uint8_t reg_addr, uint32_t mask, uint8_t reg_data) +static bool prv_write_register_block(uint8_t register_address, const uint8_t *data, size_t length) { + i2c_use(I2C_AW86225); + bool rv = i2c_write_register_block(I2C_AW86225, register_address, length, data); + i2c_release(I2C_AW86225); + return rv; +} + +bool prv_modify_reg(uint8_t reg_addr, uint32_t mask, uint8_t reg_data) { uint8_t reg_val = 0; uint8_t reg_mask = (uint8_t)mask; - prv_read_register(reg_addr, ®_val); + if (!prv_read_register(reg_addr, ®_val)) { + return false; + } reg_val &= reg_mask; reg_val |= (reg_data & (~reg_mask)); - prv_write_register(reg_addr, reg_val); + return prv_write_register(reg_addr, reg_val); } static void prv_aw862xx_play_go(bool flag) { uint8_t val; - if (flag) { + if (flag) { val = AW862XX_BIT_PLAYCFG4_GO_ON; prv_write_register(AW862XX_REG_PLAYCFG4, val); } else { - val = AW862XX_BIT_PLAYCFG4_STOP_ON; + bool standby = false; + prv_modify_reg(AW862XX_REG_SYSCTRL1, AW862XX_SYSCTRL1_RAMINIT_MASK, + AW862XX_SYSCTRL1_RAMINIT_ON); + prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_STOP); + val = AW862XX_BIT_PLAYCFG4_GO_ON; prv_write_register(AW862XX_REG_PLAYCFG4, val); + prv_modify_reg(AW862XX_REG_SYSCTRL1, AW862XX_SYSCTRL1_RAMINIT_MASK, + AW862XX_SYSCTRL1_RAMINIT_OFF); + for (int i = 0; i < AW862XX_STOP_STANDBY_RETRIES; ++i) { + if (!prv_read_register(AW862XX_REG_GLBRD5, &val)) { + break; + } + if ((val & AW862XX_GLBRD5_STATE_MASK) == AW862XX_GLBRD5_STATE_STANDBY) { + standby = true; + break; + } + psleep(AW862XX_STOP_STANDBY_POLL_MS); + } + if (!standby) { + prv_modify_reg(AW862XX_REG_SYSCTRL2, AW862XX_SYSCTRL2_STANDBY_MASK, + AW862XX_SYSCTRL2_STANDBY_ON); + prv_modify_reg(AW862XX_REG_SYSCTRL2, AW862XX_SYSCTRL2_STANDBY_MASK, + AW862XX_SYSCTRL2_STANDBY_OFF); + } } } +static bool prv_config_cont_mode(uint8_t drv1_time, uint8_t drv2_time) { + uint8_t drv1_level = ((uint16_t)s_target_strength * AW862XX_CONTCFG7_FULL_SCALE) / 100U; + uint8_t drv2_level = ((uint16_t)s_target_strength * AW862XX_CONTCFG7_DRV2_LEVEL) / 100U; + bool ret = prv_write_register(AW862XX_REG_CONTCFG1, + AW862XX_CONTCFG1_EDGE_FREQ_NONE | + AW862XX_CONTCFG1_SIN_MODE_COS); + ret &= prv_write_register(AW862XX_REG_CONTCFG2, AW862XX_CONTCFG2_CONF_F0); + ret &= prv_write_register(AW862XX_REG_CONTCFG3, AW862XX_CONTCFG3_DRV_WIDTH); + ret &= prv_write_register(AW862XX_REG_CONTCFG7, drv2_level); + ret &= prv_write_register(AW862XX_REG_CONTCFG6, AW862XX_CONTCFG6_TRACK_EN | drv1_level); + ret &= prv_write_register(AW862XX_REG_CONTCFG8, drv1_time); + ret &= prv_write_register(AW862XX_REG_CONTCFG9, drv2_time); + ret &= prv_write_register(AW862XX_REG_CONTCFG10, AW862XX_CONTCFG10_BRK_TIME); + ret &= prv_write_register(AW862XX_REG_CONTCFG11, AW862XX_CONTCFG11_TRACK_MARGIN); + ret &= prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, 0); + ret &= prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT); + ret &= prv_modify_reg(AW862XX_REG_SYSCTRL1, AW862XX_SYSCTRL1_VBAT_MODE_MASK, + AW862XX_SYSCTRL1_VBAT_MODE_EN); + return ret; +} + +static bool prv_load_ram_waveform(void) { + uint8_t addr[] = { AW862XX_RAM_BASE_ADDR >> 8, AW862XX_RAM_BASE_ADDR & 0xFF }; + uint8_t fifo[] = { + (((AW862XX_RAM_BASE_ADDR >> 1) >> 4) & 0xF0) | + (((AW862XX_RAM_BASE_ADDR - (AW862XX_RAM_BASE_ADDR >> 2)) >> 8) & 0x0F), + (AW862XX_RAM_BASE_ADDR >> 1) & 0xFF, + (AW862XX_RAM_BASE_ADDR - (AW862XX_RAM_BASE_ADDR >> 2)) & 0xFF, + }; + + prv_aw862xx_play_go(false); + bool ret = prv_modify_reg(AW862XX_REG_SYSCTRL1, AW862XX_SYSCTRL1_RAMINIT_MASK, + AW862XX_SYSCTRL1_RAMINIT_ON); + ret &= prv_modify_reg(AW862XX_REG_RTPCFG1, AW862XX_RTPCFG1_ADDRH_MASK, + AW862XX_RAM_BASE_ADDR >> 8); + ret &= prv_write_register(AW862XX_REG_RTPCFG2, AW862XX_RAM_BASE_ADDR & 0xFF); + ret &= prv_write_register_block(AW862XX_REG_RTPCFG3, fifo, sizeof(fifo)); + ret &= prv_write_register_block(AW862XX_REG_RAMADDRH, addr, sizeof(addr)); + ret &= prv_write_register_block(AW862XX_REG_RAMDATA, s_aw86225_ram_waveform, + sizeof(s_aw86225_ram_waveform)); + ret &= prv_modify_reg(AW862XX_REG_SYSCTRL1, AW862XX_SYSCTRL1_RAMINIT_MASK, + AW862XX_SYSCTRL1_RAMINIT_OFF); + return ret; +} + +static bool prv_config_ram_loop_mode(void) { + uint8_t gain = ((uint16_t)s_target_strength * AW862XX_PLAYCFG2_GAIN_MAX) / 100U; + bool ret = prv_modify_reg(AW862XX_REG_SYSCTRL2, AW862XX_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_SYSCTRL2_RATE_12K); + ret &= prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_ENABLE); + ret &= prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + ret &= prv_write_register(AW862XX_REG_WAVCFG1, AW862XX_RAM_WAVEFORM_SEQ); + ret &= prv_write_register(AW862XX_REG_WAVCFG2, 0); + ret &= prv_write_register(AW862XX_REG_WAVCFG9, 0xF0); + ret &= prv_write_register(AW862XX_REG_PLAYCFG2, gain); + return ret; +} + +static int prv_f0_detection(void) +{ + int f0 = 0; + uint8_t reg_val = 0; + uint16_t f0_reg = 0; + uint16_t cont_f0_reg = 0; + bool standby = false; + + prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT); + prv_modify_reg(AW862XX_REG_CONTCFG1, ~AW862XX_CONTCFG1_EN_F0_DET, + AW862XX_CONTCFG1_EN_F0_DET); + prv_modify_reg(AW862XX_REG_CONTCFG6, ~AW862XX_CONTCFG6_TRACK_EN, AW862XX_CONTCFG6_TRACK_EN); + prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_ENABLE); + prv_modify_reg(AW862XX_REG_CONTCFG6, ~AW862XX_CONTCFG7_FULL_SCALE, + AW862XX_CONTCFG7_FULL_SCALE); + prv_write_register(AW862XX_REG_CONTCFG7, AW862XX_CONTCFG7_FULL_SCALE); + prv_write_register(AW862XX_REG_CONTCFG8, 0x04); + prv_write_register(AW862XX_REG_CONTCFG9, 0x14); + prv_write_register(AW862XX_REG_CONTCFG11, AW862XX_CONTCFG11_TRACK_MARGIN); + prv_write_register(AW862XX_REG_CONTCFG3, AW862XX_CONTCFG3_F0_DET_DRV_WIDTH); + + prv_write_register(AW862XX_REG_PLAYCFG4, AW862XX_BIT_PLAYCFG4_GO_ON); + psleep(AW862XX_F0_DET_STANDBY_POLL_MS * 2); + + for (int i = 0; i < AW862XX_F0_DET_STANDBY_RETRIES; ++i) { + if (!prv_read_register(AW862XX_REG_GLBRD5, ®_val)) { + break; + } + + if ((reg_val & AW862XX_GLBRD5_STATE_MASK) == AW862XX_GLBRD5_STATE_STANDBY) { + standby = true; + break; + } + + psleep(AW862XX_F0_DET_STANDBY_POLL_MS); + } + + if (!standby) { + PBL_LOG_ERR("AW86225: F0 detect did not reach standby"); + prv_write_register(AW862XX_REG_PLAYCFG4, AW862XX_BIT_PLAYCFG4_STOP_ON); + } + + bool ret = prv_read_register(AW862XX_REG_CONTRD14, ®_val); + f0_reg = reg_val << 8; + ret &= prv_read_register(AW862XX_REG_CONTRD15, ®_val); + f0_reg |= reg_val; + if (ret && f0_reg == 0) { + ret = prv_read_register(AW862XX_REG_CONTRD16, ®_val); + cont_f0_reg = reg_val << 8; + ret &= prv_read_register(AW862XX_REG_CONTRD17, ®_val); + cont_f0_reg |= reg_val; + f0_reg = cont_f0_reg; + } + if (!ret || f0_reg == 0) { + PBL_LOG_ERR("AW86225: F0 readback failed (i2c=%d, det=0x%04x, cont=0x%04x)", ret, + f0_reg, cont_f0_reg); + prv_modify_reg(AW862XX_REG_CONTCFG1, ~AW862XX_CONTCFG1_EN_F0_DET, 0); + prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, 0); + return -1; + } + f0 = 384000 / f0_reg; + + prv_modify_reg(AW862XX_REG_CONTCFG1, ~AW862XX_CONTCFG1_EN_F0_DET, 0); + prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, 0); + + return f0; +} + void vibe_init(void) { + PBL_ASSERTN(AW86225->lra_frequency_hz > 0); + PBL_ASSERTN(AW86225->lra_frequency_tolerance_hz > 0); + if (s_drive_frequency_hz == 0) { + s_drive_frequency_hz = AW86225->lra_frequency_hz; + } + gpio_output_init(&BOARD_CONFIG_VIBE.ctl, GPIO_OType_PP, GPIO_Speed_2MHz); + gpio_output_set(&BOARD_CONFIG_VIBE.ctl, true); - psleep(AW862XX_POR_WAIT_TIME); - + psleep(AW862XX_PWR_OFF_TIME); + gpio_output_set(&BOARD_CONFIG_VIBE.ctl, false); + psleep(AW862XX_PWR_ON_TIME); + uint8_t chip_id; - bool ret = prv_read_register(AW862XX_REG_ID, &chip_id); - PBL_ASSERT(ret, "Failed to get AW86225 chip ID"); + bool ret = prv_read_register(AW862XX_REG_CHIPID, &chip_id); + if (!ret) { + PBL_LOG_ERR("AW86225: chip ID read failed (I2C error)"); + return; + } - ret &= prv_write_register(AW862XX_REG_CONTCFG1, AW862XX_CONTCFG1_EDGE_FREQ|AW862XX_CONTCFG1_WAVE_MODE); - ret &= prv_write_register(AW862XX_REG_CONTCFG2, AW862XX_CONTCFG2_CONF_F0); - ret &= prv_write_register(AW862XX_REG_CONTCFG3, AW862XX_CONTCFG3_DRV_WIDTH); - ret &= prv_write_register(AW862XX_REG_CONTCFG7, AW862XX_CONTCFG7_FULL_SCALE); - - prv_modify_reg(AW862XX_REG_CONTCFG6, AW862XX_CONTCFG6_TRACK_MASK, AW862XX_CONTCFG6_TRACK_EN); - prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, AW862XX_BIT_PLAYCFG3_BRK_ENABLE); - prv_modify_reg(AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT); - prv_modify_reg(AW862XX_REG_SYSCTRL1, AW862XX_SYSCTRL1_VBAT_MODE_MASK, AW862XX_SYSCTRL1_VBAT_MODE_EN); + ret &= prv_load_ram_waveform(); + if (s_trim_lra != AW862XX_TRIM_LRA_INVALID) { + ret &= prv_modify_reg(AW862XX_REG_TRIMCFG3, AW862XX_TRIMCFG3_TRIM_LRA_MASK, s_trim_lra); + } + ret &= prv_config_ram_loop_mode(); - PBL_ASSERT(ret, "Failed to initialize AW86225"); + if (!ret) { + PBL_LOG_ERR("AW86225: register configuration failed"); + return; + } + + s_initialized = true; } void vibe_set_strength(int8_t strength) { - uint32_t scale = strength * AW862XX_CONTCFG7_FULL_SCALE / 100UL; - prv_write_register(AW862XX_REG_CONTCFG7, (uint8_t)scale); + if (strength < 0) { + strength = -strength; + } + if (strength > VIBE_STRENGTH_MAX) { + strength = VIBE_STRENGTH_MAX; + } + s_target_strength = strength; + + if (!s_initialized) { + return; + } + + uint8_t gain = ((uint16_t)strength * AW862XX_PLAYCFG2_GAIN_MAX) / 100U; + bool ret = prv_write_register(AW862XX_REG_PLAYCFG2, gain); + if (!ret) { + PBL_LOG_ERR("AW86225: strength write failed"); + } } void vibe_ctl(bool on) { + if (!s_initialized) { + return; + } + if (on) { + if (!prv_config_ram_loop_mode()) { + PBL_LOG_ERR("AW86225: playback configuration failed"); + return; + } prv_aw862xx_play_go(true); } else { prv_aw862xx_play_go(false); @@ -146,18 +383,122 @@ void vibe_ctl(bool on) { } void vibe_force_off(void) { + if (!s_initialized) { + return; + } + prv_aw862xx_play_go(false); } int8_t vibe_get_braking_strength(void) { + if (!s_initialized) { + return 0; + } + uint8_t value; prv_read_register(AW862XX_REG_CONTCFG7, &value); uint8_t strength = value * 100UL/AW862XX_CONTCFG7_FULL_SCALE; return strength; } +// Refer to DG_AW862XX_Software_Design_Guide_CN_V1.1 +status_t vibe_calibrate(void) { + char f0_cali_lra = 0; + int f0_cali_step = 0; + int f0_cali_min = 0; + int f0_cali_max = 0; + int f0; + + if (!s_initialized) { + return E_INVALID_OPERATION; + } + + // Measure F0 with a neutral trim. + prv_modify_reg(AW862XX_REG_TRIMCFG3, AW862XX_TRIMCFG3_TRIM_LRA_MASK, 0); + + f0 = prv_f0_detection(); + if (f0 < 0) { + PBL_LOG_ERR("AW86225: F0 detection failed"); + return E_ERROR; + } + + /** + * TRIM_LRA[0:5] is used to calibration the f0 frequency. + * + * The TRIM_LRA LSB is 0.24%, up to 31, down to -32, calibration range + * is nearly +/- 7%. + * + * Below code calibrate the f0 to match f0_pre as possible. + */ + f0_cali_min = AW86225->lra_frequency_hz - AW86225->lra_frequency_tolerance_hz; + f0_cali_max = AW86225->lra_frequency_hz + AW86225->lra_frequency_tolerance_hz; + if (f0 < f0_cali_min || f0 > f0_cali_max) { + PBL_LOG_ERR("AW86225: F0 out of range (measured %d Hz, expected %d +/- %d Hz)", f0, + AW86225->lra_frequency_hz, AW86225->lra_frequency_tolerance_hz); + return E_ERROR; + } + + f0_cali_step = 100000 * ((int)f0 - (int)AW86225->lra_frequency_hz) / + ((int)AW86225->lra_frequency_hz * AW862XX_F0_CALI_LSB_PERMYRIAD); + if (f0_cali_step >= 0) { + if (f0_cali_step % 10 >= 5) { + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + } else { + f0_cali_step = 32 + f0_cali_step / 10; + } + + } else { + if (f0_cali_step % 10 <= -5) { + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + } else { + f0_cali_step = 32 + f0_cali_step / 10; + } + } + + if (f0_cali_step > 31) { + f0_cali_lra = (char)f0_cali_step - 32; + } else { + f0_cali_lra = (char)f0_cali_step + 32; + } + + s_trim_lra = f0_cali_lra & 0x3F; + prv_modify_reg(AW862XX_REG_TRIMCFG3, AW862XX_TRIMCFG3_TRIM_LRA_MASK, s_trim_lra); + s_drive_frequency_hz = f0; + prv_config_cont_mode(AW862XX_CONTCFG8_DRV1_TIME, AW862XX_CONTCFG9_DRV2_TIME_MAX); + PBL_LOG_DBG("AW86225: F0 cali measured %d Hz, trim=0x%02x", f0, s_trim_lra); + + return S_SUCCESS; +} + +uint8_t vibe_get_calibration(void) { + return s_trim_lra; +} + +void vibe_apply_calibration(uint8_t cali) { + if (!s_initialized) { + return; + } + + s_trim_lra = cali & 0x3F; + if (!prv_modify_reg(AW862XX_REG_TRIMCFG3, AW862XX_TRIMCFG3_TRIM_LRA_MASK, s_trim_lra)) { + PBL_LOG_ERR("AW86225: failed to apply stored calibration"); + return; + } + PBL_LOG_DBG("AW86225: applied stored calibration trim=0x%02x", s_trim_lra); +} void command_vibe_ctl(const char *arg) { + if (!strcmp(arg, "cal")) { + status_t rc = vibe_calibrate(); + if (rc != S_SUCCESS) { + prompt_send_response("F0 cali fail"); + } else { + prompt_send_response("F0 cali success"); + } + + return; + } + int strength = atoi(arg); const bool out_of_bounds = ((strength < 0) || (strength > VIBE_STRENGTH_MAX)); diff --git a/src/fw/drivers/vibe/vibe_aw86225.h b/src/fw/drivers/vibe/vibe_aw86225.h new file mode 100644 index 0000000000..3f5259b8a2 --- /dev/null +++ b/src/fw/drivers/vibe/vibe_aw86225.h @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include + +typedef struct AW86225Config { + uint16_t lra_frequency_hz; + uint16_t lra_frequency_tolerance_hz; +} AW86225Config; diff --git a/src/fw/drivers/vibe/vibe_aw8623x.c b/src/fw/drivers/vibe/vibe_aw8623x.c new file mode 100644 index 0000000000..aa514df682 --- /dev/null +++ b/src/fw/drivers/vibe/vibe_aw8623x.c @@ -0,0 +1,213 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "console/prompt.h" +#include "drivers/gpio.h" +#include "drivers/i2c.h" +#include "drivers/vibe.h" +#include "kernel/util/sleep.h" +#include "system/logging.h" +#include "system/passert.h" + +PBL_LOG_MODULE_DEFINE(driver_vibe_aw8623x, CONFIG_DRIVER_VIBE_LOG_LEVEL); + +#define AW8623X_PLAYCFG3 0x08U +#define AW8623X_PLAYCFG3_BRK_EN (1U << 2U) +#define AW8623X_PLAYCFG3_PLAY_MODE_CONT (2U << 0U) + +#define AW8623X_PLAYCFG4 0x09U +#define AW8623X_PLAYCFG4_STOP (1U << 1U) +#define AW8623X_PLAYCFG4_GO (1U << 0U) + +#define AW8623X_CONTCFG1 0x17U +#define AW8623X_CONTCFG1_EDGE_FRE_NONE 0x0U +#define AW8623X_CONTCFG1_SIN_MODE_COS (1U << 4U) +#define AW8623X_CONTCFG1_EN_F0_DET (1U << 5U) + +#define AW8623X_CONTCFG2 0x18U +#define AW8623X_CONTCFG2_CONF_F0(freq) (24000U / (freq)) + +#define AW8623X_CONTCFG3 0x19U +#define AW8623X_CONTCFG3_DRV_WIDTH(freq) (48000U / (freq)) + +#define AW8623X_CONTCFG6 0x1CU +#define AW8623X_CONTCFG6_DRV1_LVL_MAX 0x7FU +#define AW8623X_CONTCFG6_TRACK_EN (1U << 7U) + +#define AW8623X_CONTCFG7 0x1DU +#define AW8623X_CONTCFG7_DRV2_LVL_MAX 0x7FU + +#define AW8623X_CONTCFG8 0x1EU +#define AW8623X_CONTCFG8_DRV1_TIME_MAX 0xFFU + +#define AW8623X_CONTCFG9 0x1FU +#define AW8623X_CONTCFG9_DRV2_TIME_MAX 0xFFU + +#define AW8623X_VBATCTRL 0x4EU +#define AW8623X_VBATCTRL_VBAT_MODE_HW (1U << 6U) + +#define AW8623X_IDH 0x57U +#define AW8623X_IDH_CHIPID_H 0x23U + +#define AW8623X_IDL 0x69U +#define AW8623X_IDL_CHIPID_L 0x40U + +#define AW8623X_PWR_OFF_TIME_MS 2 +#define AW8623X_PWR_ON_TIME_MS 3 + +static bool s_initialized = false; + +static bool prv_read_register(uint8_t addr, uint8_t *data) { + bool ret; + + i2c_use(I2C_AW8623X); + ret = i2c_read_register_block(I2C_AW8623X, addr, 1, data); + i2c_release(I2C_AW8623X); + + return ret; +} + +static bool prv_write_register(uint8_t addr, uint8_t data) { + bool ret; + + i2c_use(I2C_AW8623X); + ret = i2c_write_register_block(I2C_AW8623X, addr, 1, &data); + i2c_release(I2C_AW8623X); + + return ret; +} + +void vibe_init(void) { + bool ret; + uint8_t val; + + gpio_output_init(&BOARD_CONFIG_VIBE.ctl, GPIO_OType_PP, GPIO_Speed_2MHz); + + gpio_output_set(&BOARD_CONFIG_VIBE.ctl, true); + psleep(AW8623X_PWR_OFF_TIME_MS); + gpio_output_set(&BOARD_CONFIG_VIBE.ctl, false); + psleep(AW8623X_PWR_ON_TIME_MS); + + // Verify chip ID + ret = prv_read_register(AW8623X_IDH, &val); + if (!ret || val != AW8623X_IDH_CHIPID_H) { + PBL_LOG_ERR("Failed to read AW8623X chip ID high byte"); + return; + } + + ret = prv_read_register(AW8623X_IDL, &val); + if (!ret || val != AW8623X_IDL_CHIPID_L) { + PBL_LOG_ERR("Failed to read AW8623X chip ID low byte"); + return; + } + + ret &= prv_write_register(AW8623X_CONTCFG1, AW8623X_CONTCFG1_EDGE_FRE_NONE | + AW8623X_CONTCFG1_SIN_MODE_COS | + AW8623X_CONTCFG1_EN_F0_DET); + ret &= prv_write_register(AW8623X_CONTCFG2, AW8623X_CONTCFG2_CONF_F0(235U)); + ret &= prv_write_register(AW8623X_CONTCFG3, AW8623X_CONTCFG3_DRV_WIDTH(235U)); + ret &= prv_write_register(AW8623X_CONTCFG6, + AW8623X_CONTCFG6_DRV1_LVL_MAX | AW8623X_CONTCFG6_TRACK_EN); + ret &= prv_write_register(AW8623X_CONTCFG7, AW8623X_CONTCFG7_DRV2_LVL_MAX); + + ret &= prv_write_register(AW8623X_PLAYCFG3, + AW8623X_PLAYCFG3_BRK_EN | AW8623X_PLAYCFG3_PLAY_MODE_CONT); + ret &= prv_write_register(AW8623X_VBATCTRL, AW8623X_VBATCTRL_VBAT_MODE_HW); + + PBL_ASSERTN(ret); + + s_initialized = true; +} + +void vibe_set_strength(int8_t strength) { + bool ret; + uint8_t scale; + + if (!s_initialized) { + return; + } + + if (strength < 0) { + strength = -strength; + } + + scale = ((uint16_t)strength * AW8623X_CONTCFG7_DRV2_LVL_MAX) / 100U; + + ret &= prv_write_register(AW8623X_CONTCFG6, scale | AW8623X_CONTCFG6_TRACK_EN); + ret = prv_write_register(AW8623X_CONTCFG7, scale); + PBL_ASSERTN(ret); +} + +void vibe_ctl(bool on) { + bool ret; + + if (!s_initialized) { + return; + } + + if (on) { + ret = prv_write_register(AW8623X_CONTCFG8, AW8623X_CONTCFG8_DRV1_TIME_MAX); + ret &= prv_write_register(AW8623X_CONTCFG9, AW8623X_CONTCFG9_DRV2_TIME_MAX); + ret &= prv_write_register(AW8623X_PLAYCFG4, AW8623X_PLAYCFG4_GO); + } else { + ret = prv_write_register(AW8623X_PLAYCFG4, AW8623X_PLAYCFG4_STOP); + } + + PBL_ASSERTN(ret); +} + +void vibe_force_off(void) { + vibe_ctl(false); +} + +int8_t vibe_get_braking_strength(void) { + bool ret; + uint8_t val; + + if (!s_initialized) { + return 0; + } + + ret = prv_read_register(AW8623X_CONTCFG7, &val); + PBL_ASSERTN(ret); + + return ((int16_t)val * 100) / AW8623X_CONTCFG7_DRV2_LVL_MAX; +} + +status_t vibe_calibrate(void) { + return E_INVALID_OPERATION; +} + +uint8_t vibe_get_calibration(void) { + return 0xFF; +} + +void vibe_apply_calibration(uint8_t cali) { +} + +void command_vibe_ctl(const char *arg) { + int8_t strength; + + if (strcmp(arg, "cal") == 0) { + status_t ret = vibe_calibrate(); + if (ret != S_SUCCESS) { + prompt_send_response("Calibration failed"); + } else { + prompt_send_response("Calibration succeeded"); + } + + return; + } + + strength = (int8_t)atoi(arg); + if ((strength < VIBE_STRENGTH_MIN) || (strength > VIBE_STRENGTH_MAX)) { + prompt_send_response("Invalid argument"); + return; + } + + vibe_set_strength(strength); + vibe_ctl(strength != 0); + + prompt_send_response("OK"); +} diff --git a/src/fw/drivers/vibe/vibe_drv2604.c b/src/fw/drivers/vibe/vibe_drv2604.c index dbc07450eb..42ff779bc0 100644 --- a/src/fw/drivers/vibe/vibe_drv2604.c +++ b/src/fw/drivers/vibe/vibe_drv2604.c @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "drivers/vibe.h" #include "board/board.h" @@ -13,13 +16,13 @@ #include "system/passert.h" #include "util/math.h" -#include "services/common/analytics/analytics.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/battery/battery_state.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/battery/battery_state.h" #include +PBL_LOG_MODULE_DEFINE(driver_vibe_drv2604, CONFIG_DRIVER_VIBE_LOG_LEVEL); + /* XXX: tune RATED_VOLTAGE? / OD_CLAMP? */ #define DRV2604_STATUS 0x00 @@ -74,9 +77,9 @@ void vibe_init(void) { uint8_t rv; bool found = prv_read_register(DRV2604_STATUS, &rv); if (found) { - PBL_LOG(LOG_LEVEL_INFO, "Found DRV2604 with STATUS register %02x", rv); + PBL_LOG_INFO("Found DRV2604 with STATUS register %02x", rv); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read the STATUS register"); + PBL_LOG_ERR("Failed to read the STATUS register"); } /* calibration table maybe should live in the board file? */ @@ -94,7 +97,7 @@ void vibe_init(void) { for (size_t i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) { if (!prv_write_register(regs[i][0], regs[i][1])) { - PBL_LOG(LOG_LEVEL_ERROR, "failed to write register %02x on DRV2604", regs[i][0]); + PBL_LOG_ERR("failed to write register %02x on DRV2604", regs[i][0]); gpio_output_set(&BOARD_CONFIG_VIBE.ctl, false); return; } @@ -129,18 +132,7 @@ void vibe_ctl(bool on) { on = false; } - static bool s_on = false; - if (on && !s_on) { - analytics_inc(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_COUNT, AnalyticsClient_System); - analytics_stopwatch_start(ANALYTICS_APP_METRIC_VIBRATOR_ON_TIME, AnalyticsClient_App); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME, AnalyticsClient_System); - } else if (!on && s_on) { - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_VIBRATOR_ON_TIME); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME); - } - s_on = on; - - PBL_LOG(LOG_LEVEL_DEBUG, "Vibe status <%s>", on ? "on" : "off"); + PBL_LOG_DBG("Vibe status <%s>", on ? "on" : "off"); if (!on) { prv_write_register(DRV2604_MODE, DRV2604_MODE_STANDBY | DRV2604_MODE_RTP); /* enter standby even if the enable GPIO is not hooked up */ @@ -166,21 +158,41 @@ int8_t vibe_get_braking_strength(void) { return VIBE_STRENGTH_MIN; } +status_t vibe_calibrate(void) { + bool bad = false; + bad |= !prv_write_register(DRV2604_MODE, DRV2604_MODE_AUTOCAL); + bad |= !prv_write_register(DRV2604_FBCTL, DRV2604_FBCTL_LRA | DRV2604_FBCTL_FB_BRAKE_FACTOR(2) | DRV2604_FBCTL_LOOP_GAIN(2)); + bad |= !prv_write_register(DRV2604_RATED_VOLTAGE, 0x3F); /* default value */ + bad |= !prv_write_register(DRV2604_OD_CLAMP, 0x89); /* default value */ + bad |= !prv_write_register(DRV2604_CONTROL1, DRV2604_CONTROL1_STARTUP_BOOST | DRV2604_CONTROL1_DRIVE_TIME(0x10 /* 2.1 ms */)); + bad |= !prv_write_register(DRV2604_CONTROL2, DRV2604_CONTROL2_BIDIR_INPUT | DRV2604_CONTROL2_BRAKE_STABILIZER | DRV2604_CONTROL2_SAMPLE_TIME(3) | DRV2604_CONTROL2_BLANKING_TIME(1) | DRV2604_CONTROL2_IDISS_TIME(1)); + bad |= !prv_write_register(DRV2604_CONTROL4, DRV2604_CONTROL4_AUTO_CAL_TIME(3)); + bad |= !prv_write_register(DRV2604_GO, 1); /* GO */ + + return bad ? E_ERROR : S_SUCCESS; +} + +uint8_t vibe_get_calibration(void) { + return 0xFF; +} + +void vibe_apply_calibration(uint8_t cali) { +} void command_vibe_ctl(const char *arg) { if (!strcmp(arg, "cal")) { + status_t res; char buf[64]; + prompt_send_response("vibe cal..."); - bool bad = false; - bad |= !prv_write_register(DRV2604_MODE, DRV2604_MODE_AUTOCAL); - bad |= !prv_write_register(DRV2604_FBCTL, DRV2604_FBCTL_LRA | DRV2604_FBCTL_FB_BRAKE_FACTOR(2) | DRV2604_FBCTL_LOOP_GAIN(2)); - bad |= !prv_write_register(DRV2604_RATED_VOLTAGE, 0x3F); /* default value */ - bad |= !prv_write_register(DRV2604_OD_CLAMP, 0x89); /* default value */ - bad |= !prv_write_register(DRV2604_CONTROL1, DRV2604_CONTROL1_STARTUP_BOOST | DRV2604_CONTROL1_DRIVE_TIME(0x10 /* 2.1 ms */)); - bad |= !prv_write_register(DRV2604_CONTROL2, DRV2604_CONTROL2_BIDIR_INPUT | DRV2604_CONTROL2_BRAKE_STABILIZER | DRV2604_CONTROL2_SAMPLE_TIME(3) | DRV2604_CONTROL2_BLANKING_TIME(1) | DRV2604_CONTROL2_IDISS_TIME(1)); - bad |= !prv_write_register(DRV2604_CONTROL4, DRV2604_CONTROL4_AUTO_CAL_TIME(3)); - bad |= !prv_write_register(DRV2604_GO, 1); /* GO */ - prompt_send_response_fmt(buf, 64, "vibe cal write bad %d", bad); + + res = vibe_calibrate(); + if (res != S_SUCCESS) { + prompt_send_response_fmt(buf, 64, "vibe cal failed"); + } else { + prompt_send_response_fmt(buf, 64, "vibe cal succeeded"); + } + return; } diff --git a/src/fw/drivers/vibe/wscript_build b/src/fw/drivers/vibe/wscript_build new file mode 100644 index 0000000000..6e183259ed --- /dev/null +++ b/src/fw/drivers/vibe/wscript_build @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes'] + +if bld.env.CONFIG_VIBE_AW86225: + sources.append('vibe_aw86225.c') + use.append('driver_gpio') +elif bld.env.CONFIG_VIBE_AW8623X: + sources.append('vibe_aw8623x.c') + use.append('driver_gpio') +elif bld.env.CONFIG_VIBE_DRV2604: + sources.append('vibe_drv2604.c') + use.extend([ + 'driver_gpio', + 'driver_periph_config', + 'pbl_includes', + ]) +elif bld.env.CONFIG_VIBE_PWM: + sources.append('vibe.c') + use.extend([ + 'driver_gpio', + 'driver_periph_config', + 'driver_pwm', + 'freertos', + 'pbl_includes', + ]) + +bld.objects( + name='driver_vibe', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_vibe') diff --git a/src/fw/drivers/voltage_monitor.h b/src/fw/drivers/voltage_monitor.h index f22315d7ca..cc396e94b6 100644 --- a/src/fw/drivers/voltage_monitor.h +++ b/src/fw/drivers/voltage_monitor.h @@ -1,36 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - #include "board/board.h" #define NUM_CONVERSIONS 40 -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) # include typedef const struct VoltageMonitorDevice { @@ -39,9 +19,8 @@ typedef const struct VoltageMonitorDevice { const nrf_saadc_input_t input; } VoltageMonitorDevice; -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) || defined(CONFIG_QEMU) -// TODO(SF32LB52): Add implementation typedef const struct VoltageMonitorDevice { } VoltageMonitorDevice; @@ -68,14 +47,6 @@ void voltage_monitor_init(void); void voltage_monitor_device_init(const VoltageMonitorDevice *device); //! Get a voltage reading from the given ADC. -//! Implementation is hardware specific, since Vref is only available on ADC1. -//! -//! On the STM32F412xG, which only has a single ADC: -//! - ADC1 is configured in scan mode, and will scan the ADC channel given in \ref adc, and Vref. -//! -//! On all other F2 and F4 platforms, which have multiple ADCs: -//! - ADC1 is configured only to pull Vref. -//! - The given adc must not be ADC1 //! //! @param device Pointer to the Voltage Monitor Device to be used. //! @param reading_out Pointer to a VoltageReading struct which the results will be returned in. diff --git a/src/fw/drivers/watchdog.h b/src/fw/drivers/watchdog.h index 331983a771..bbecc2543d 100644 --- a/src/fw/drivers/watchdog.h +++ b/src/fw/drivers/watchdog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,8 +8,12 @@ void watchdog_init(void); void watchdog_start(void); +void watchdog_stop(void); void watchdog_feed(void); bool watchdog_check_reset_flag(void); McuRebootReason watchdog_clear_reset_flag(void); + +//! Returns the cached value from the most recent watchdog_clear_reset_flag(). +McuRebootReason watchdog_get_reset_flag(void); diff --git a/src/fw/drivers/watchdog/Kconfig b/src/fw/drivers/watchdog/Kconfig new file mode 100644 index 0000000000..fa48717d4c --- /dev/null +++ b/src/fw/drivers/watchdog/Kconfig @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig WATCHDOG + bool "Watchdog" + help + Watchdog Drivers + +if WATCHDOG + +config WATCHDOG_NRF5 + bool "Nordic nRF5" + help + Support for the Nordic nRF5 watchdog. + +config WATCHDOG_SF32LB + bool "SiFli SF32LB" + help + Support for the SiFli SF32LB watchdog. + +config WATCHDOG_QEMU + bool "QEMU Watchdog" + help + Support for the QEMU watchdog (no-op). + +endif # WATCHDOG diff --git a/src/fw/drivers/watchdog/nrf5.c b/src/fw/drivers/watchdog/nrf5.c new file mode 100644 index 0000000000..817976fdec --- /dev/null +++ b/src/fw/drivers/watchdog/nrf5.c @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/watchdog.h" + +#include "util/bitset.h" +#include "system/logging.h" + +#include +#include +#include + +#include + +void watchdog_init(void) { + nrf_wdt_reload_request_enable(NRF_WDT, NRF_WDT_RR0); + /* WDT expiration: 8s */ + nrf_wdt_reload_value_set(NRF_WDT, 32768 * 8); +} + +void watchdog_start(void) { + nrf_wdt_task_trigger(NRF_WDT, NRF_WDT_TASK_START); +} + +void watchdog_feed(void) { + nrf_wdt_reload_request_set(NRF_WDT, NRF_WDT_RR0); +} + +bool watchdog_check_reset_flag(void) { + return (nrfx_reset_reason_get() & NRFX_RESET_REASON_DOG_MASK) != 0; +} + +static McuRebootReason s_cached_reset_flag; + +McuRebootReason watchdog_clear_reset_flag(void) { + uint32_t reason = nrfx_reset_reason_get(); + nrfx_reset_reason_clear(0xFFFFFFFF); + + s_cached_reset_flag = (McuRebootReason){ + .brown_out_reset = 0, + .pin_reset = (reason & NRFX_RESET_REASON_RESETPIN_MASK) != 0, + .power_on_reset = (reason & NRFX_RESET_REASON_VBUS_MASK) != 0, + .software_reset = (reason & NRFX_RESET_REASON_SREQ_MASK) != 0, + .independent_watchdog_reset = (reason & NRFX_RESET_REASON_DOG_MASK) != 0, + .window_watchdog_reset = 0, + .low_power_manager_reset = 0, + }; + + return s_cached_reset_flag; +} + +McuRebootReason watchdog_get_reset_flag(void) { + return s_cached_reset_flag; +} diff --git a/src/fw/drivers/watchdog/sf32lb.c b/src/fw/drivers/watchdog/sf32lb.c new file mode 100644 index 0000000000..cbb4c0bb96 --- /dev/null +++ b/src/fw/drivers/watchdog/sf32lb.c @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/watchdog.h" +#include "system/logging.h" + +#include "bf0_hal.h" + +#define WDT_TIMEOUT_S 10U + +static WDT_HandleTypeDef hwdt = { + .Instance = hwp_wdt1, +}; + +static McuRebootReason s_cached_reset_flag; + +void watchdog_init(void) { + // On PebbleOS, we use RC32K as WDT clock source. + hwdt.Init.Reload = 32000 * WDT_TIMEOUT_S; + + __HAL_WDT_STOP(&hwdt); + __HAL_WDT_INT(&hwdt, 0); + __HAL_WDT_RELOAD_COUNTER(&hwdt); + + HAL_PMU_SetWdt((uint32_t)hwdt.Instance); + __HAL_SYSCFG_Enable_WDT_REBOOT(1); +} + +void watchdog_start(void) { + __HAL_WDT_START(&hwdt); +} + +void watchdog_stop(void) { + __HAL_WDT_STOP(&hwdt); +} + +void watchdog_feed(void) { + __HAL_WDT_START(&hwdt); +} + +bool watchdog_check_reset_flag(void) { + return (HAL_PMU_GET_WSR() & PMUC_WSR_WDT1) != 0; +} + +McuRebootReason watchdog_clear_reset_flag(void) { + uint32_t wsr = HAL_PMU_GET_WSR(); + pm_power_on_mode_t boot = SystemPowerOnModeGet(); + HAL_PMU_CLEAR_WSR(0xFFFFFFFF); + + s_cached_reset_flag = (McuRebootReason){ + .brown_out_reset = 0, + .pin_reset = (((wsr & PMUC_WSR_PIN0) != 0) || ((wsr & PMUC_WSR_PIN1) != 0)), + .power_on_reset = (boot & PM_COLD_BOOT) != 0, + .software_reset = (boot & PM_REBOOT_BOOT) != 0, + .independent_watchdog_reset = 0, + .window_watchdog_reset = (wsr & PMUC_WSR_WDT1) != 0, + .low_power_manager_reset = 0, + }; + + return s_cached_reset_flag; +} + +McuRebootReason watchdog_get_reset_flag(void) { + return s_cached_reset_flag; +} diff --git a/src/fw/drivers/watchdog/wscript_build b/src/fw/drivers/watchdog/wscript_build new file mode 100644 index 0000000000..3ccf0e2585 --- /dev/null +++ b/src/fw/drivers/watchdog/wscript_build @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [] +use = ['fw_includes', 'pbl_includes'] + +if bld.env.CONFIG_WATCHDOG_QEMU: + sources.append('../stubs/watchdog.c') +elif bld.env.CONFIG_WATCHDOG_NRF5: + sources.append('nrf5.c') + use.append('hal_nordic') +elif bld.env.CONFIG_WATCHDOG_SF32LB: + sources.append('sf32lb.c') + use.append('hal_sifli') + +bld.objects( + name='driver_watchdog', + source=sources, + use=use, +) + +bld.env.DRIVERS.append('driver_watchdog') diff --git a/src/fw/drivers/wscript_build b/src/fw/drivers/wscript_build index 8dd635b2c8..017c72aee2 100644 --- a/src/fw/drivers/wscript_build +++ b/src/fw/drivers/wscript_build @@ -1,1567 +1,201 @@ -mcu_family = bld.env.MICRO_FAMILY -if mcu_family not in ('STM32F2', 'STM32F4', 'STM32F7', 'NRF52840', 'SF32LB52'): - bld.fatal('src/fw/drivers/wscript: ' - 'Unknown MICRO_FAMILY {}'.format(mcu_family)) +bld.env.DRIVERS = [] + +if bld.env.CONFIG_QEMU: + bld.env.DRIVERS.extend([ + 'driver_button', + 'driver_mcu', + 'driver_mpu', + 'driver_pwr', + 'driver_task_watchdog', + 'driver_qemu_stubs', + ]) -if bld.is_tintin(): - bld( - name='drivers', - use=[ - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_clocksource', - 'driver_display', - 'driver_flash', - 'driver_gpio_defaults', - 'driver_hci_passthrough', - 'driver_imu', - 'driver_led_controller', - 'driver_mcu', - 'driver_mpu', - 'driver_otp', - 'driver_pwr', - 'driver_rng', - 'driver_rtc', - 'driver_system_flash', - 'driver_task_watchdog', - 'driver_uart', - 'driver_vibe', - 'driver_voltage_monitor', - 'driver_watchdog', - ], - ) - -elif bld.is_silk(): - bld( - name='drivers', - use=[ - 'driver_accessory', - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_clocksource', - 'driver_crc', - 'driver_display', - 'driver_flash', - 'driver_gpio_defaults', - 'driver_hci_passthrough', - 'driver_hrm', - 'driver_imu', - 'driver_led_controller', - 'driver_mcu', - 'driver_mic', - 'driver_mpu', - 'driver_otp', - 'driver_pmic', - 'driver_pwr', - 'driver_rng', - 'driver_rtc', - 'driver_system_flash', - 'driver_task_watchdog', - 'driver_temperature', - 'driver_uart', - 'driver_vibe', - 'driver_voltage_monitor', - 'driver_watchdog', - ], - ) - -elif bld.is_snowy_compatible(): - bld( - name='drivers', - use=[ - 'driver_accessory', - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_clocksource', - 'driver_display', - 'driver_flash', - 'driver_fpc_pinstrap', - 'driver_gpio_defaults', - 'driver_hci_passthrough', - 'driver_imu', - 'driver_led_controller', - 'driver_mcu', - 'driver_mic', - 'driver_mpu', - 'driver_otp', - 'driver_pmic', - 'driver_pwr', - 'driver_rng', - 'driver_rtc', - 'driver_system_flash', - 'driver_task_watchdog', - 'driver_temperature', - 'driver_touch', - 'driver_uart', - 'driver_vibe', - 'driver_voltage_monitor', - 'driver_watchdog', - ], - ) - -elif bld.is_cutts(): - bld( - name='drivers', - use=[ - 'driver_accessory', - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_clocksource', - 'driver_display', - 'driver_flash', - 'driver_gpio_defaults', - 'driver_hci_passthrough', - 'driver_imu', - 'driver_led_controller', - 'driver_mcu', - 'driver_mic', - 'driver_mpu', - 'driver_otp', - 'driver_pmic', - 'driver_pwr', - 'driver_rng', - 'driver_rtc', - 'driver_system_flash', - 'driver_task_watchdog', - 'driver_temperature', - 'driver_touch', - 'driver_uart', - 'driver_vibe', - 'driver_voltage_monitor', - 'driver_watchdog', - ], - ) - -elif bld.is_robert(): - bld( - name='drivers', - use=[ - 'driver_accessory', - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_clocksource', - 'driver_display', - 'driver_flash', - 'driver_gpio_defaults', - 'driver_hci_passthrough', - 'driver_hrm', - 'driver_imu', - 'driver_led_controller', - 'driver_mcu', - 'driver_mic', - 'driver_mpu', - 'driver_otp', - 'driver_pmic', - 'driver_pwr', - 'driver_rng', - 'driver_rtc', - 'driver_system_flash', - 'driver_task_watchdog', - 'driver_temperature', - 'driver_uart', - 'driver_vibe', - 'driver_voltage_monitor', - 'driver_watchdog', - ], - ) - -elif bld.is_asterix(): - bld( - name='drivers', - use=[ - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_display', - 'driver_flash', - 'driver_gpio_defaults', - 'driver_imu', - 'driver_mcu', - 'driver_mic', - 'driver_mpu', - 'driver_nrf5_hfxo', - 'driver_otp', - 'driver_periph_config', - 'driver_pmic', - 'driver_pwr', - 'driver_rng', - 'driver_rtc', - 'driver_task_watchdog', - 'driver_temperature', - 'driver_uart', - 'driver_vibe', - ], - ) - -elif bld.is_obelix(): - bld( - name='drivers', - use=[ - 'driver_ambient_light', - 'driver_backlight', - 'driver_battery', - 'driver_button', - 'driver_display', - 'driver_gpio', - 'driver_flash', - 'driver_gpio_defaults', - 'driver_hrm', - 'driver_i2c', - 'driver_imu', - 'driver_mcu', - 'driver_mpu', - 'driver_otp', - 'driver_periph_config', - 'driver_pmic', - 'driver_pwr', - 'driver_qspi', - 'driver_rng', - 'driver_rtc', - 'driver_lptim_systick', - 'driver_task_watchdog', - 'driver_temperature', - 'driver_touch', - 'driver_uart', - 'driver_vibe', - 'driver_pwm', - 'driver_mic', - 'driver_touch', - ], - ) -## ---------- - -if bld.is_snowy_compatible() or bld.is_silk() or bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_accessory', - source=[ - 'accessory.c', - ], - use=[ - 'driver_dma', - 'driver_exti', - 'driver_gpio', - 'driver_periph_config', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if bld.capability('HAS_ALS_OPT3001'): - bld.objects( - name='driver_ambient_light', - source=[ - 'ambient/ambient_light_opt3001.c', - ], - use=[ - 'driver_i2c', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.capability('HAS_ALS_W1160'): - bld.objects( - name='driver_ambient_light', - source=[ - 'ambient/ambient_light_w1160.c', - ], - use=[ - 'driver_i2c', - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_ambient_light', - source=[ - 'ambient/ambient_light.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family in ('NRF52840', 'SF32LB52'): - # FIXME(NRF52840,SF32LB52): provide working implementation - bld.objects( - name='driver_ambient_light', - source=[ - 'stubs/ambient_light.c', - ], - use=[ - 'fw_includes', - ], - ) - -if mcu_family in ('STM32F4', 'STM32F7'): - bld.objects( - name='driver_temperature', - source=[ - 'stm32f4/temperature.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family in ('NRF52840'): - # FIXME(NRF52840): provide working implementation - bld.objects( - name='driver_temperature', - source=[ - 'stubs/temperature.c', - ], - use=[ - 'fw_includes', - ], - ) -elif mcu_family in ('SF32LB52'): - bld.objects( - name='driver_temperature', - source=[ - 'sf32lb52/temperature.c', - ], - use=[ - 'fw_includes', - ], - ) - -# FIXME: break dependency on driver_led_controller when not needed -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_backlight', - source=[ - 'backlight.c', - ], - use=[ - 'driver_pwm', - 'driver_gpio', - 'driver_led_controller', - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_backlight', - source=[ - 'backlight.c', - ], - use=[ - 'driver_pwm', - 'driver_gpio', - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_backlight', - source=[ - 'backlight.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'driver_led_controller', - ], - ) - -if bld.env.QEMU: - bld.objects( - name='driver_battery', - source=[ - 'battery/battery_adc_conversion.c', - 'battery/battery_common.c', - 'battery/battery_voltage_monitor.c', - 'qemu/qemu_battery.c', - ], - use=[ - 'driver_qemu_serial', - 'driver_qemu_settings', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_tintin(): - bld.objects( - name='driver_battery', - source=[ - 'battery/battery_adc_conversion.c', - 'battery/battery_common.c', - 'battery/battery_tintin.c', - 'battery/battery_voltage_monitor.c', - ], - use=[ - 'driver_exti', - 'driver_gpio', - 'driver_otp', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_asterix() or bld.is_obelix(): - bld.objects( - name='driver_battery', - source=[ - 'battery/battery_common.c', - # PMIC provides this implementation - ], - use=[ - 'fw_includes', - 'root_includes', - ], - ) -else: - bld.objects( - name='driver_battery', - source=[ - 'battery/battery_adc_conversion.c', - 'battery/battery_common.c', - 'battery/battery_pmic.c', - 'battery/battery_voltage_monitor.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_pmic', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if (bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert()) and not bld.env.QEMU: - bld.objects( - name='driver_bmi160', - source=[ - 'imu/bmi160/bmi160.c', - 'imu/bmi160/bmi160_spi.c', - ], - use=[ - 'driver_exti', - 'driver_rtc', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if bld.is_silk() and not bld.env.QEMU: - bld.objects( - name='driver_bma255', - source=[ - 'imu/bma255/bma255.c', - 'imu/bma255/bma255_spi.c', - ], - use=[ - 'driver_gpio', - ], - ) - -if bld.is_asterix() or bld.is_obelix(): - bld.objects( - name='driver_lsm6dso', - source=[ - 'imu/lsm6dso/lsm6dso.c', - ], - use=[ - 'hal_lsm6dso', - 'fw_includes', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_button', - source=[ - 'button.c', - 'debounced_button.c', - ], - use=[ - 'driver_exti', - 'driver_gpio', - 'driver_periph_config', - 'driver_timer', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_button', - source=[ - 'nrf5/button.c', - 'nrf5/debounced_button.c', - ], - use=[ - 'driver_exti', - 'freertos', - 'fw_includes', - 'root_includes', - 'hal_nordic', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_button', - source=[ - 'sf32lb52/button.c', - 'sf32lb52/debounced_button.c', - ], - use=[ - 'driver_exti', - 'driver_gpio', - 'freertos', - 'fw_includes', - 'hal_sifli', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_clocksource', - source=[ - 'stm32f2/clocksource.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if bld.is_tintin() or bld.is_silk(): - bld.objects( - name='driver_display', - source=[ - 'display/sharp_ls013b7dh01/sharp_ls013b7dh01.c', - ], - use=[ - 'driver_dma', - 'driver_gpio', - 'driver_periph_config', - 'driver_spi', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_display', - source=[ - 'display/ice40lp/ice40lp.c', - 'display/ice40lp/ice40lp_internal.c', - 'display/ice40lp/snowy_boot.c', - ], - use=[ - 'driver_dma', - 'driver_exti', - 'driver_gpio', - 'driver_periph_config', - 'driver_pmic', - 'driver_spi', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_asterix(): - # FIXME: driver should use platform agnostic SPI interface - bld.objects( - name='driver_display', - source=[ - 'display/sharp_ls013b7dh01/sharp_ls013b7dh01_nrf5.c', - ], - use=[ - 'driver_gpio', - 'freertos', - 'fw_includes', - 'root_includes', - 'hal_nordic', - ], - ) -elif bld.is_obelix(): - bld.objects( - name='driver_display', - source=[ - 'sf32lb52/jdi_lpm015m135a.c', - ], - use=[ - 'fw_includes', - 'freertos', - 'hal_sifli', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_dma', - source=[ - 'stm32f2/dma.c', - ], - use=[ - 'driver_periph_config', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_exti', - source=[ - 'stm32f2/exti.c', - ], - use=[ - 'driver_periph_config', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_exti', - source=[ - 'nrf5/exti.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'hal_nordic', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_exti', - source=[ - 'sf32lb52/exti.c', - ], - use=[ - 'fw_includes', - ], - ) - -if bld.is_tintin(): - bld.objects( - name='driver_flash', - source=[ - 'flash/flash_crc.c', - 'flash/micron_n25q/cd_flash_driver.c', - 'flash/micron_n25q/flash.c', - 'flash/micron_n25q/flash_core.c', - ], - use=[ - 'driver_dma', - 'driver_gpio', - 'driver_periph_config', - 'driver_spi', - 'driver_watchdog', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_silk(): - bld.objects( - name='driver_flash', - source=[ - 'flash/cd_flash_driver.c', - 'flash/flash_api.c', - 'flash/flash_crc.c', - 'flash/flash_erase.c', - 'flash/mx25u.c', - 'flash/nvram_bkp.c', - 'flash/qspi_flash.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_qspi', - 'driver_task_watchdog', - 'driver_watchdog', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_flash', - source=[ - 'flash/cd_flash_driver.c', - 'flash/flash_api.c', - 'flash/flash_crc.c', - 'flash/flash_erase.c', - 'flash/mt25q.c', - 'flash/nvram_bkp.c', - 'flash/qspi_flash.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_qspi', - 'driver_task_watchdog', - 'driver_watchdog', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_snowy_compatible(): - bld.objects( - name='driver_flash', - source=[ - 'flash/cd_flash_driver.c', - 'flash/flash_api.c', - 'flash/flash_crc.c', - 'flash/flash_erase.c', - 'flash/nvram_bkp.c', - 'flash/spansion_s29vs.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_task_watchdog', - 'driver_watchdog', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_asterix(): - bld.objects( - name='driver_flash', - source=[ - 'flash/flash_api.c', - 'flash/flash_crc.c', - 'flash/flash_erase.c', - 'flash/cd_flash_driver.c', - 'flash/gd25lq255e.c', - ], - use=[ - 'driver_qspi', - 'freertos', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_obelix(): - bld.objects( - name='driver_flash', - source=[ - 'flash/flash_api.c', - 'flash/flash_crc.c', - 'flash/flash_erase.c', - 'flash/cd_flash_driver.c', - 'flash/gd25q256e.c', - ], - use=[ - 'driver_qspi', - 'freertos', - 'fw_includes', - 'root_includes', - ], - ) - -if bld.is_snowy_compatible(): - bld.objects( - name='driver_fpc_pinstrap', - source=[ - 'fpc_pinstrap/fpc_pinstrap_snowy.c', - ], - use=[ - 'driver_gpio', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_gpio', - source=[ - 'stm32f2/gpio.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_gpio', - source=[ - 'nrf5/gpio.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'hal_nordic', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_gpio', - source=[ - 'sf32lb52/gpio.c', - ], - use=[ - 'fw_includes', - ], - ) - -if mcu_family == 'STM32F7': - bld.objects( - name='driver_pwr', - source=[ - 'stm32f7/pwr.c', - ], - use=[ - 'fw_includes', - ], - ) -elif mcu_family == 'STM32F4': - bld.objects( - name='driver_pwr', - source=[ - 'stm32f4/pwr.c', - ], - use=[ - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family == 'STM32F2': - bld.objects( - name='driver_pwr', - source=[ - 'stm32f2/pwr.c', - ], - use=[ - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family in ('NRF52840', 'SF32LB52'): - bld.objects( - name='driver_pwr', - source=[ - 'stubs/pwr.c', - ], - use=[ - 'fw_includes', - ], - ) - -# FIXME: More of a board config than driver -if mcu_family == 'STM32F2': - bld.objects( - name='driver_gpio_defaults', - source=[ - 'stm32f2/gpio_defaults.c', - ], - use=[ - 'driver_gpio', - ], - ) -elif mcu_family in ('STM32F4', 'STM32F7'): - bld.objects( - name='driver_gpio_defaults', - source=[ - 'stm32f4/gpio_defaults.c', - ], - use=[ - 'driver_gpio', - ], - ) -elif mcu_family == 'NRF52840': - # FIXME: provide working implementation - bld.objects( - name='driver_gpio_defaults', - source=[ - 'stubs/gpio_defaults.c', - ], - use=[ - 'fw_includes', - ], - ) -elif mcu_family == 'SF32LB52': - # FIXME(SF32LB52): provide working implementation - bld.objects( - name='driver_gpio_defaults', - source=[ - 'stubs/gpio_defaults.c', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4'): - bld.objects( - name='driver_i2c', - source=[ - 'i2c.c', - 'stm32f2/i2c_hal.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_rtc', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -elif mcu_family in ('STM32F7',): - bld.objects( - name='driver_i2c', - source=[ - 'i2c.c', - 'stm32f7/i2c_hal.c', - 'stm32f7/i2c_timingr.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_rtc', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_i2c', - source=[ - 'i2c.c', - 'nrf5/i2c_hal.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'hal_nordic', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_i2c', - source=[ - 'i2c.c', - 'sf32lb52/i2c_hal.c', - ], - use=[ - 'fw_includes', - 'freertos', - 'hal_sifli', - ], - ) - -if bld.env.QEMU: - mag_driver = ('driver_mag3110' if bld.capability('HAS_MAGNETOMETER') - else 'driver_mag_null') - bld.objects( - name='driver_imu', - source=[ - 'imu/imu_qemu.c', - 'qemu/qemu_accel.c', - ], - use=[ - mag_driver, - 'driver_qemu_serial', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_tintin(): - bld.objects( - name='driver_imu', - source=[ - 'imu/imu_tintin.c', - ], - use=[ - 'driver_lis3dh', - 'driver_mag3110', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_imu', - source=[ - 'imu/imu_snowy_evt2.c', - ], - use=[ - 'driver_bmi160', - 'driver_mag3110', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_silk(): - bld.objects( - name='driver_imu', - source=[ - 'imu/imu_silk.c', - ], - use=[ - 'driver_bma255', - 'driver_mag_null', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_asterix(): - bld.objects( - name='driver_imu', - source=[ - 'imu/imu_asterix.c', - ], - use=[ - 'driver_lsm6dso', - 'driver_mmc5603nj', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_obelix(): - bld.objects( - name='driver_imu', - source=[ - 'imu/imu_obelix.c', - ], - use=[ - 'driver_lsm6dso', - 'driver_mmc5603nj', - 'fw_includes', - 'root_includes', - ], - ) - -if bld.env.BOARD == "v2_0": - bld.objects( - name='driver_led_controller', - source=[ - 'led_controller/is31fl3196.c', - ], - use=[ - 'driver_gpio', - 'driver_i2c', - 'driver_periph_config', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_obelix(): - bld.objects( - name='driver_led_controller', - source=[ - 'led_controller/pwm.c', - ], - use=[ - 'driver_pwm', - ], - ) - -if bld.is_tintin() and not bld.env.QEMU: - bld.objects( - name='driver_lis3dh', - source=[ - 'imu/lis3dh/config.c', - 'imu/lis3dh/lis3dh.c', - ], - use=[ - 'driver_exti', - 'driver_gpio', - 'driver_i2c', - 'driver_periph_config', - 'driver_vibe', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if bld.is_tintin() or bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_mag3110', - source=[ - 'imu/mag3110/mag3110.c', - ], - use=[ - 'driver_exti', - 'driver_gpio', - 'driver_i2c', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if bld.is_asterix() or bld.is_obelix(): - bld.objects( - name='driver_mmc5603nj', - source=[ - 'imu/mmc5603nj/mmc5603nj.c', - ], - use=[ - 'driver_i2c', - 'fw_includes', - 'root_includes', - ], - ) - -if not bld.capability('HAS_MAGNETOMETER'): - bld.objects( - name='driver_mag_null', - source=[ - 'imu/mag_null.c', - ], - use=[ - 'fw_includes', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_mcu', - source=[ - 'stm32f2/mcu.c', - ], - use=[ - 'freertos', - ], - ) -elif mcu_family == 'NRF52840': - # FIXME: provide working implementation - bld.objects( - name='driver_mcu', - source=[ - 'stubs/mcu.c', - ], - use=[ - 'fw_includes', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_mcu', - source=[ - 'sf32lb52/mcu.c', - ], - use=[ - 'fw_includes', - 'hal_sifli', - ], - ) - -if bld.is_snowy_compatible(): - bld.objects( - name='driver_mic', - source=[ - 'mic/mic_command.c', - 'mic/stm32/pdm.c', - ], - use=[ - 'driver_accessory', - 'driver_dma', - 'driver_gpio', - 'driver_periph_config', - 'driver_pmic', - 'fw_includes', - 'root_includes', - 'freertos', - 'stm32_stdlib', - ], - ) -elif bld.is_silk() or bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_mic', - source=[ - 'mic/mic_command.c', - 'mic/stm32/dfsdm.c', - ], - use=[ - 'driver_accessory', - 'driver_dma', - 'driver_gpio', - 'driver_periph_config', - 'driver_pmic', - 'fw_includes', - 'root_includes', - 'freertos', - 'stm32_stdlib', - ], - ) -elif bld.is_asterix(): - bld.objects( - name='driver_mic', - source=[ - 'mic/nrf5/pdm.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'freertos', - 'hal_nordic', - ], - ) -elif bld.is_obelix(): - bld.objects( - name='driver_mic', - source=[ - 'mic/sf32lb52/pdm.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'hal_sifli', - ], - ) - - -# FIXME: make generic to Cortex-M -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7', 'NRF52840', 'SF32LB52'): - bld.objects( - name='driver_mpu', - source=[ - 'mpu.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_otp', - source=[ - 'stm32f2/otp.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.capability('HAS_FLASH_OTP'): - bld.objects( - name='driver_otp', - source=[ - 'otp_flash.c', - ], - use=[ - 'fw_includes', - 'driver_flash', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_periph_config', - source=[ - 'stm32f2/periph_config.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - # FIXME: provide working implementation - bld.objects( - name='driver_periph_config', - source=[ - 'stubs/periph_config.c', - ], - use=[ - 'fw_includes', - ], - ) -elif mcu_family == 'SF32LB52': - # FIXME(SF32LB52): provide working implementation - bld.objects( - name='driver_periph_config', - source=[ - 'stubs/periph_config.c', - ], - use=[ - 'fw_includes', - ], - ) - -if bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert(): - bld.objects( - name='driver_pmic', - source=[ - 'pmic/max14690_pmic.c', - ], - use=[ - 'driver_exti', - 'driver_gpio', - 'driver_i2c', - 'driver_periph_config', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif bld.is_silk(): - bld.objects( - name='driver_pmic', - source=[ - 'pmic/as3701b.c', - ], - use=[ - 'driver_gpio', - 'driver_i2c', - 'fw_includes', - 'root_includes', - ], - ) -elif bld.is_asterix() or bld.is_obelix(): - bld.objects( - name='driver_pmic', - source=[ - 'pmic/npm1300.c', - ], - use=[ - 'driver_i2c', - 'fw_includes', - 'root_includes', - ], - ) - -if bld.env.QEMU: - bld.objects( - name='driver_qemu_serial', - source=[ - 'qemu/qemu_serial.c', - 'qemu/qemu_serial_util.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - 'root_includes', - ], - ) - -if bld.env.QEMU: - bld.objects( - name='driver_qemu_settings', - source=[ - 'qemu/qemu_settings.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) - -if mcu_family in ('STM32F2', 'STM32F4'): - bld.objects( - name='driver_pwm', - source=[ - 'stm32f2/pwm.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ] - ) -elif mcu_family in ('STM32F7',): - bld.objects( - name='driver_pwm', - source=[ - 'stm32f7/pwm.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ] - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_pwm', - source=[ - 'nrf5/pwm.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'hal_nordic', - ] - ) -elif mcu_family == 'SF32LB52': bld.objects( - name='driver_pwm', + name='driver_qemu_stubs', source=[ - 'sf32lb52/pwm.c', + 'stubs/ambient_light.c', + 'qemu/qemu_vibe.c', + 'stubs/otp.c', ], use=[ + 'driver_qemu_serial', 'fw_includes', - 'hal_sifli', - ] + 'pbl_includes', + ], ) -if 'STM32F412xG' in bld.env.DEFINES or mcu_family in ('STM32F7',): + # periph_config stub for QEMU (required by qemu_serial) bld.objects( - name='driver_qspi', + name='driver_periph_config', + source=[], + use=['fw_includes'], + ) + +elif bld.env.CONFIG_BOARD_FAMILY_ASTERIX: + bld.env.DRIVERS.extend([ + 'driver_button', + 'driver_mcu', + 'driver_mpu', + 'driver_nrf5_hfxo', + 'driver_otp', + 'driver_periph_config', + 'driver_pwr', + 'driver_task_watchdog', + ]) + +elif bld.env.CONFIG_BOARD_FAMILY_OBELIX: + bld.env.DRIVERS.extend([ + 'driver_button', + 'driver_mcu', + 'driver_mpu', + 'driver_periph_config', + 'driver_pwr', + 'driver_qspi', + 'driver_rc10k', + 'driver_task_watchdog', + ]) + +elif bld.env.CONFIG_BOARD_FAMILY_GETAFIX: + bld.env.DRIVERS.extend([ + 'driver_button', + 'driver_mcu', + 'driver_mpu', + 'driver_periph_config', + 'driver_pwr', + 'driver_qspi', + 'driver_rc10k', + 'driver_task_watchdog', + ]) +## ---------- + +if bld.env.CONFIG_SPEAKER: + bld.recurse('speaker') + +if bld.env.CONFIG_AMBIENT_LIGHT: + bld.recurse('ambient') + +if bld.env.CONFIG_TEMPERATURE: + bld.recurse('temperature') + +if bld.env.CONFIG_BATTERY: + bld.recurse('battery') + +bld.recurse('imu') + +if bld.env.CONFIG_QEMU: + bld.objects( + name='driver_button', source=[ - 'stm32f412/qspi.c', + 'qemu/qemu_button.c', ], use=[ - 'driver_dma', - 'driver_periph_config', 'freertos', 'fw_includes', - 'root_includes', - 'stm32_stdlib', + 'pbl_includes', ], ) -elif mcu_family == 'NRF52840': +elif bld.env.CONFIG_SOC_NRF52: bld.objects( - name='driver_qspi', + name='driver_button', source=[ - 'nrf5/qspi.c', + 'nrf5/button.c', + 'nrf5/debounced_button.c', ], use=[ + 'driver_exti', 'freertos', 'fw_includes', - 'root_includes', + 'pbl_includes', 'hal_nordic', ], ) -elif mcu_family == 'SF32LB52': +elif bld.env.CONFIG_SOC_SF32LB52: bld.objects( - name='driver_qspi', + name='driver_button', source=[ - 'sf32lb52/qspi.c', + 'sf32lb52/button.c', + 'sf32lb52/debounced_button.c', ], use=[ + 'driver_exti', + 'driver_gpio', + 'freertos', 'fw_includes', - 'root_includes', 'hal_sifli', ], ) -if 'STM32F412xG' in bld.env.DEFINES: - bld.env.DEFINES.append('HAS_DRIVER_VOLTAGE_MONITOR') - bld.objects( - name='driver_voltage_monitor', - source=[ - 'stm32f412/voltage_monitor.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ] - ) -elif mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.env.DEFINES.append('HAS_DRIVER_VOLTAGE_MONITOR') - bld.objects( - name='driver_voltage_monitor', - source=[ - 'stm32f2/voltage_monitor.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ] - ) +if bld.env.CONFIG_DISPLAY: + bld.recurse('display') + +if bld.env.CONFIG_EXTI: + bld.recurse('exti') + +if bld.env.CONFIG_FLASH: + bld.recurse('flash') + +if bld.env.CONFIG_GPIO: + bld.recurse('gpio') +bld.objects( + name='driver_pwr', + source=[ + 'stubs/pwr.c', + ], + use=[ + 'fw_includes', + ], +) -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): +if bld.env.CONFIG_I2C: + bld.recurse('i2c') + +if bld.env.CONFIG_QEMU: + if bld.env.CONFIG_MAG: + bld.objects( + name='driver_mag3110', + source=[ + 'imu/mag3110/mag3110.c', + ], + use=[ + 'driver_qemu_serial', + 'fw_includes', + 'pbl_includes', + ], + ) + +if bld.env.CONFIG_BACKLIGHT: + bld.recurse('backlight') + +if bld.env.CONFIG_PRESSURE: + bld.recurse('pressure') + +if bld.env.CONFIG_QEMU: bld.objects( - name='driver_rng', + name='driver_mcu', source=[ - 'stm32f2/rng.c', + 'stubs/mcu.c', ], use=[ - 'driver_periph_config', 'fw_includes', - 'root_includes', - 'stm32_stdlib', ], ) -elif mcu_family == 'NRF52840': +elif bld.env.CONFIG_SOC_NRF52: # FIXME: provide working implementation bld.objects( - name='driver_rng', + name='driver_mcu', source=[ - 'stubs/rng.c', + 'stubs/mcu.c', ], use=[ 'fw_includes', ], ) -elif mcu_family == 'SF32LB52': +elif bld.env.CONFIG_SOC_SF32LB52: bld.objects( - name='driver_rng', + name='driver_mcu', source=[ - 'sf32lb52/rng.c', + 'sf32lb52/mcu.c', ], use=[ 'fw_includes', @@ -1569,399 +203,182 @@ elif mcu_family == 'SF32LB52': ], ) -if mcu_family == 'STM32F2': +if bld.env.CONFIG_MIC: + bld.recurse('mic') + +if bld.env.CONFIG_MPU_TYPE_ARMV8M: + mpu_arch_source = 'mpu/mpu_armv8m.c' +else: + mpu_arch_source = 'mpu/mpu_armv7m.c' +bld.objects( + name='driver_mpu', + source=[ + 'mpu.c', + mpu_arch_source, + ], + use=[ + 'freertos', + 'fw_includes', + 'pbl_includes', + ], +) + +if bld.env.CONFIG_OTP: + bld.recurse('otp') + +if bld.env.CONFIG_PMIC: + bld.recurse('pmic') + +if bld.env.CONFIG_QEMU: bld.objects( - name='driver_rtc', + name='driver_qemu_serial', source=[ - 'rtc_common.c', - 'stm32f2/rtc.c', - 'stm32f2/rtc_calibration.c', + 'qemu/qemu_serial.c', + 'qemu/qemu_serial_util.c', ], use=[ - 'driver_clocksource', 'driver_gpio', - 'driver_pwr', - 'driver_periph_config', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family in ('STM32F4', 'STM32F7'): - bld.objects( - name='driver_rtc', - source=[ - 'rtc_common.c', - 'stm32f4/rtc.c', - ], - use=[ - 'driver_pwr', - 'driver_clocksource', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_rtc', - source=[ - 'nrf5/rtc.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_rtc', - source=[ - 'sf32lb52/rtc.c', - ], - use=[ - 'freertos', + 'driver_uart', 'fw_includes', - 'root_includes', - 'hal_sifli', + 'pbl_includes', ], ) -if mcu_family == 'SF32LB52': +if bld.env.CONFIG_QEMU: bld.objects( - name='driver_lptim_systick', + name='driver_qemu_settings', source=[ - 'sf32lb52/lptim_systick.c', + 'qemu/qemu_settings.c', ], use=[ 'fw_includes', - 'freertos', - 'root_includes', - 'hal_sifli', + 'pbl_includes', ], ) -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_spi', - source=[ - 'stm32f2/spi.c', - 'stm32f2/spi_legacy.c' - ], - use=[ - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) +if bld.env.CONFIG_PWM: + bld.recurse('pwm') -if mcu_family == 'STM32F7': - bld.objects( - name='driver_spi', - source=[ - 'stm32f2/spi.c', - ], - use=[ - 'fw_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': +if bld.env.CONFIG_SOC_NRF52: bld.objects( - name='driver_spi', + name='driver_qspi', source=[ - 'nrf5/spi.c', + 'nrf5/qspi.c', ], use=[ + 'freertos', 'fw_includes', + 'pbl_includes', 'hal_nordic', ], ) - -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): +elif bld.env.CONFIG_SOC_SF32LB52: bld.objects( - name='driver_system_flash', + name='driver_qspi', source=[ - 'stm32f2/system_flash.c', + 'sf32lb52/qspi.c', ], use=[ 'fw_includes', - 'root_includes', - 'stm32_stdlib', + 'pbl_includes', + 'hal_sifli', ], ) -# FIXME: not really a driver -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): +if bld.env.CONFIG_RNG: + bld.recurse('rng') + +if bld.env.CONFIG_RTC: + bld.recurse('rtc') + +if bld.env.CONFIG_SOC_NRF52: bld.objects( name='driver_task_watchdog', source=[ 'task_watchdog.c', ], use=[ - 'driver_periph_config', - 'driver_watchdog', + 'freertos', 'fw_includes', - 'root_includes', - 'stm32_stdlib', + 'pbl_includes', + 'hal_nordic', ], ) -elif mcu_family == 'NRF52840': +elif bld.env.CONFIG_SOC_SF32LB52: bld.objects( name='driver_task_watchdog', source=[ 'task_watchdog.c', ], use=[ - 'driver_watchdog', + 'freertos', 'fw_includes', - 'root_includes', - 'hal_nordic', + 'pbl_includes', ], ) -elif mcu_family == 'SF32LB52': +elif bld.env.CONFIG_QEMU: bld.objects( name='driver_task_watchdog', source=[ 'task_watchdog.c', ], use=[ - 'driver_watchdog', + 'freertos', 'fw_includes', - 'root_includes', + 'pbl_includes', ], ) -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_timer', - source=[ - 'stm32f2/timer.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) +if bld.env.CONFIG_TOUCH: + bld.recurse('touch') -if bld.capability('HAS_TOUCHSCREEN'): - if bld.env.QEMU: - bld.objects( - name='driver_touch', - source=[ - 'stubs/touch.c', - ], - ) - elif bld.is_obelix(): - bld.objects( - name='driver_touch', - source=['touch/cst816/cst816.c'], - use=[ - 'cst816_fw', - 'freertos', - 'fw_includes', - 'root_includes', - 'driver_i2c', - 'driver_gpio', - ], - ) - elif bld.is_cutts(): - bld.objects( - name='driver_touch', - source=bld.path.ant_glob('touch/ewd1000/*.c'), - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - ], - ) +if bld.env.CONFIG_UART: + bld.recurse('uart') -if mcu_family in ('STM32F2', 'STM32F4'): - bld.objects( - name='driver_uart', - source=[ - 'stm32f2/uart.c', - ], - use=[ - 'driver_gpio', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'STM32F7': - bld.objects( - name='driver_uart', - source=[ - 'stm32f7/uart.c', - ], - use=[ - 'driver_gpio', - 'freertos', - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': - bld.objects( - name='driver_uart', - source=[ - 'nrf5/uart.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'hal_nordic', - ], - ) -elif mcu_family == 'SF32LB52': - bld.objects( - name='driver_uart', - source=[ - 'sf32lb52/uart.c', - ], - use=[ - 'freertos', - 'fw_includes', - 'root_includes', - 'hal_sifli', - ], - ) +if bld.env.CONFIG_VIBE: + bld.recurse('vibe') -if bld.capability('HAS_VIBE_DRV2604'): - bld.objects( - name='driver_vibe', - source=[ - 'vibe/vibe_drv2604.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_i2c' - 'fw_includes', - 'root_includes', - ], - ) -if bld.capability('HAS_VIBE_AW86225'): - bld.objects( - name='driver_vibe', - source=[ - 'vibe/vibe_aw86225.c', - ], - use=[ - 'fw_includes', - 'driver_i2c', - 'driver_gpio', - ], - ) -elif mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_vibe', - source=[ - 'vibe/vibe.c', - ], - use=[ - 'driver_gpio', - 'driver_periph_config', - 'driver_pwm' - 'freertos', - 'fw_includes', - 'root_includes', - ], - ) -elif mcu_family == 'SF32LB52': - # FIXME(SF32LB52): provide working implementation - bld.objects( - name='driver_vibe', - source=[ - 'stubs/vibe.c', - ], - use=[ - 'fw_includes', - ], - ) +if bld.env.CONFIG_WATCHDOG: + bld.recurse('watchdog') -if mcu_family in ('STM32F2', 'STM32F4', 'STM32F7'): - bld.objects( - name='driver_watchdog', - source=[ - 'stm32f2/watchdog.c', - ], - use=[ - 'fw_includes', - 'root_includes', - 'stm32_stdlib', - ], - ) -elif mcu_family == 'NRF52840': +if bld.env.CONFIG_HRM: + bld.recurse('hrm') + +if bld.env.CONFIG_SOC_NRF52: bld.objects( - name='driver_watchdog', + name='driver_nrf5_hfxo', source=[ - 'nrf5/watchdog.c', + 'nrf5/hfxo.c', ], use=[ 'fw_includes', - 'root_includes', + 'freertos', 'hal_nordic', ], ) -elif mcu_family == 'SF32LB52': - # FIXME(SF32LB52): provide working implementation - bld.objects( - name='driver_watchdog', - source=[ - 'stubs/watchdog.c', - ], - use=[ - 'fw_includes', - ], - ) -if bld.get_hrm() == 'AS7000': - bld.objects( - name='driver_hrm', - source=[ - 'hrm/as7000/as7000.c', - ], - use=[ - 'driver_gpio', - 'driver_i2c', - 'fw_includes', - 'stm32_stlib' - ], - ) -elif bld.get_hrm() == 'STUB': +if bld.env.CONFIG_SOC_SF32LB52: bld.objects( - name='driver_hrm', + name='driver_rc10k', source=[ - 'stubs/hrm.c', + 'sf32lb52/rc10k.c', ], use=[ 'fw_includes', + 'hal_sifli', ], ) -if mcu_family == 'NRF52840': - bld.objects( - name='driver_nrf5_hfxo', - source=[ - 'nrf5/hfxo.c', - ], - use=[ - 'fw_includes', - 'freertos', - 'hal_nordic', - ], - ) +if bld.env.CONFIG_CPUMODE: + bld.recurse('cpumode') + +bld( + name='drivers', + use=bld.env.DRIVERS, +) # vim:filetype=python diff --git a/src/fw/flash_region/filesystem_regions.c b/src/fw/flash_region/filesystem_regions.c index 4dc363fb81..d41d749a5f 100644 --- a/src/fw/flash_region/filesystem_regions.c +++ b/src/fw/flash_region/filesystem_regions.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "flash_region/filesystem_regions.h" diff --git a/src/fw/flash_region/filesystem_regions.h b/src/fw/flash_region/filesystem_regions.h index ed00e3f8cf..5c0aef6a0d 100644 --- a/src/fw/flash_region/filesystem_regions.h +++ b/src/fw/flash_region/filesystem_regions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -40,16 +27,10 @@ typedef struct FSRegion { // time and are thus non-contiguous. For layouts with more than one region you will find their // header included below. -#if PLATFORM_TINTIN -#include "filesystem_regions_n25q.h" -#else - // Typical single region filesystem layout #define FILE_SYSTEM_REGIONS(MACRO_OPERATOR) \ MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_BEGIN, FLASH_REGION_FILESYSTEM_END) -#endif - // Notes: // In this file we check that individual region entries are sector aligned at the beginning and // the end because the filesystem only performs _sector_ erases. diff --git a/src/fw/flash_region/filesystem_regions_n25q.h b/src/fw/flash_region/filesystem_regions_n25q.h index 684c3517b1..90f8afd9c2 100644 --- a/src/fw/flash_region/filesystem_regions_n25q.h +++ b/src/fw/flash_region/filesystem_regions_n25q.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,7 +8,7 @@ //! Flash regions used for the filesystem used by the serial flash chip that we're using in //! Tintin/Bianca. Only included by core/flash_region/filesystem_regions.c -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) #define FILE_SYSTEM_REGIONS(MACRO_OPERATOR) \ MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_BEGIN, FLASH_REGION_FILESYSTEM_END) \ MACRO_OPERATOR(FLASH_REGION_FILESYSTEM_2_BEGIN, FLASH_REGION_FILESYSTEM_2_END) \ diff --git a/src/fw/flash_region/flash_region.c b/src/fw/flash_region/flash_region.c index f4ad3e22d7..e4bbfba274 100644 --- a/src/fw/flash_region/flash_region.c +++ b/src/fw/flash_region/flash_region.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "flash_region.h" @@ -47,7 +34,7 @@ static void prv_erase_upkeep(int *erase_count, bool feed_watchdog) { static void prv_erase_optimal_range(uint32_t min_start, uint32_t max_start, uint32_t min_end, uint32_t max_end, bool feed_watchdog) { - PBL_LOG(LOG_LEVEL_DEBUG, "flash_region_erase_optimal_range, 0x%"PRIx32" 0x%"PRIx32" 0x%"PRIx32" 0x%"PRIx32, + PBL_LOG_DBG("flash_region_erase_optimal_range, 0x%"PRIx32" 0x%"PRIx32" 0x%"PRIx32" 0x%"PRIx32, min_start, max_start, min_end, max_end); PBL_ASSERTN(((min_start & (~SUBSECTOR_ADDR_MASK)) == 0) && diff --git a/src/fw/flash_region/flash_region.h b/src/fw/flash_region/flash_region.h index f695a1cc83..2a4b9a3124 100644 --- a/src/fw/flash_region/flash_region.h +++ b/src/fw/flash_region/flash_region.h @@ -1,55 +1,22 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include -#if PLATFORM_TINTIN -// v2_0 and v1_5 have 8MB flash chips instead of 4MB. In the following definition, -// BOARD_NOR_FLASH_SIZE is set to allow 6MB of the flash chip to be used. The extra 2MB tacked -// onto the end will be used for the filesystem and is being added to help with storing large -// language packs (ex. Chinese). If the entire 8MB needs to be used, this variable will have to -// be changed. Migrations are likely as well. -// -// On watches with only 4MB of flash, the region will have a size of zero and be ignored by the -// fileystem. -#if defined(BOARD_V2_0) || defined(BOARD_V1_5) || defined(LARGE_SPI_FLASH) -#define BOARD_NOR_FLASH_SIZE 0x600000 -#else -#define BOARD_NOR_FLASH_SIZE 0x400000 -#endif - -#include "flash_region_n25q.h" -#elif PLATFORM_SILK -#include "flash_region_mx25u.h" -#elif PLATFORM_ASTERIX +#ifdef CONFIG_FLASH_QEMU +#include "flash_region_qemu.h" +#elif defined(CONFIG_FLASH_GD25LQ255E) #include "flash_region_gd25lq255e.h" -#elif PLATFORM_CALCULUS || PLATFORM_ROBERT -#include "flash_region_mt25q.h" -#elif PLATFORM_SNOWY || PLATFORM_SPALDING -#include "flash_region_s29vs.h" -#elif PLATFORM_OBELIX +#elif defined(CONFIG_FLASH_GD25Q256E) #include "flash_region_gd25q256e.h" #endif -#if CAPABILITY_HAS_PBLBOOT +#ifdef CONFIG_PBLBOOT // We assume that if we have pblboot, we use the two slots with direct XIP -#ifdef RECOVERY_FW +#if defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_MFG) && !defined(RECOVERY_FW_AS_FW) // On recovery we always write to slot 0 #define FLASH_REGION_FIRMWARE_DEST_BEGIN FLASH_REGION_FIRMWARE_SLOT_0_BEGIN #define FLASH_REGION_FIRMWARE_DEST_END FLASH_REGION_FIRMWARE_SLOT_0_END @@ -68,6 +35,10 @@ // We assume that if we don't have pblboot, we only have one slot (scratch area) #define FLASH_REGION_FIRMWARE_DEST_BEGIN FLASH_REGION_FIRMWARE_SLOT_1_BEGIN #define FLASH_REGION_FIRMWARE_DEST_END FLASH_REGION_FIRMWARE_SLOT_1_END + +// If we don't have pblboot, use firmware slot to store CD +#define FLASH_REGION_CD_BEGIN FLASH_REGION_FIRMWARE_DEST_BEGIN +#define FLASH_REGION_CD_END FLASH_REGION_FIRMWARE_DEST_END #endif #define FLASH_REGION_FIRMWARE_DEST_START (FLASH_REGION_FIRMWARE_DEST_BEGIN + FIRMWARE_OFFSET) diff --git a/src/fw/flash_region/flash_region_def_helper.h b/src/fw/flash_region/flash_region_def_helper.h index ab21812f9f..80716f5293 100644 --- a/src/fw/flash_region/flash_region_def_helper.h +++ b/src/fw/flash_region/flash_region_def_helper.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ enum { #define FLASH_REGION_LIST(name, size, arg) FlashRegion_##name, diff --git a/src/fw/flash_region/flash_region_gd25lq255e.h b/src/fw/flash_region/flash_region_gd25lq255e.h index 5106c3fe29..f92b39a562 100644 --- a/src/fw/flash_region/flash_region_gd25lq255e.h +++ b/src/fw/flash_region/flash_region_gd25lq255e.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -39,7 +26,9 @@ MACRO(FILESYSTEM, 0x19D0000 /* 26432K */, arg) /* 0x0400000 - 0x1DCFFFF */ \ MACRO(RSVD2, 0x0200000 /* 2048K */, arg) /* 0x1DD0000 - 0x1FCFFFF */ \ MACRO(DEBUG_DB, 0x0020000 /* 128K */, arg) /* 0x1FD0000 - 0x1FEFFFF */ \ - MACRO(RSVD3, 0x000F000 /* 60K */, arg) /* 0x1FF0000 - 0x1FFEFFF */ \ + MACRO(RSVD3, 0x000D000 /* 52K */, arg) /* 0x1FF0000 - 0x1FFCFFF */ \ + MACRO(MFG_RESULTS, 0x0001000 /* 4K */, arg) /* 0x1FFD000 - 0x1FFDFFF */ \ + MACRO(MFG_BATTERY_STATE, 0x0001000 /* 4K */, arg) /* 0x1FFE000 - 0x1FFEFFF */ \ MACRO(SHARED_PRF_STORAGE, 0x0001000 /* 4K */, arg) /* 0x1FFF000 - 0x1FFFFFF */ #include "flash_region_def_helper.h" @@ -67,6 +56,12 @@ #define FLASH_REGION_FILESYSTEM_END FLASH_REGION_END_ADDR(FILESYSTEM) #define FLASH_FILESYSTEM_BLOCK_SIZE SUBSECTOR_SIZE_BYTES +#define FLASH_REGION_MFG_RESULTS_BEGIN FLASH_REGION_START_ADDR(MFG_RESULTS) +#define FLASH_REGION_MFG_RESULTS_END FLASH_REGION_END_ADDR(MFG_RESULTS) + +#define FLASH_REGION_MFG_BATTERY_STATE_BEGIN FLASH_REGION_START_ADDR(MFG_BATTERY_STATE) +#define FLASH_REGION_MFG_BATTERY_STATE_END FLASH_REGION_END_ADDR(MFG_BATTERY_STATE) + #define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN FLASH_REGION_START_ADDR(SHARED_PRF_STORAGE) #define FLASH_REGION_SHARED_PRF_STORAGE_END FLASH_REGION_END_ADDR(SHARED_PRF_STORAGE) diff --git a/src/fw/flash_region/flash_region_gd25q256e.h b/src/fw/flash_region/flash_region_gd25q256e.h index ad2f5725d8..dedcf61bdb 100644 --- a/src/fw/flash_region/flash_region_gd25q256e.h +++ b/src/fw/flash_region/flash_region_gd25q256e.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -36,11 +23,15 @@ MACRO(FIRMWARE_SLOT_1, 0x0300000 /* 3072K */, arg) /* 0x12320000 - 0x1261FFFF */ \ MACRO(SYSTEM_RESOURCES_BANK_0, 0x0200000 /* 2048K */, arg) /* 0x12620000 - 0x1281FFFF */ \ MACRO(SYSTEM_RESOURCES_BANK_1, 0x0200000 /* 2048K */, arg) /* 0x12820000 - 0x12A1FFFF */ \ - MACRO(SAFE_FIRMWARE, 0x0080000 /* 512K */, arg) /* 0x12A20000 - 0x12A9FFFF */ \ - MACRO(FILESYSTEM, 0x1520000 /* 21632K */, arg) /* 0x12AA0000 - 0x13FBFFFF */ \ + MACRO(SAFE_FIRMWARE, 0x0090000 /* 576K */, arg) /* 0x12A20000 - 0x12AAFFFF */ \ + MACRO(FILESYSTEM, 0x1490000 /* 21056K */, arg) /* 0x12AB0000 - 0x13F3FFFF */ \ + MACRO(CD, 0x0080000 /* 512K */, arg) /* 0x12A40000 - 0x13FBFFFF */ \ MACRO(RSVD1, 0x000F000 /* 60K */, arg) /* 0x13FC0000 - 0x13FCEFFF */ \ MACRO(DEBUG_DB, 0x0020000 /* 128K */, arg) /* 0x13FCF000 - 0x13FEEFFF */ \ - MACRO(RSVD2, 0x000F000 /* 58K */, arg) /* 0x13FEF000 - 0x13FFDFFF */ \ + MACRO(RSVD2, 0x000C000 /* 48K */, arg) /* 0x13FEF000 - 0x13FFAFFF */ \ + MACRO(MFG_RESULTS, 0x0001000 /* 4K */, arg) /* 0x13FFB000 - 0x13FFBFFF */ \ + MACRO(MFG_BATTERY_STATE, 0x0001000 /* 4K */, arg) /* 0x13FFC000 - 0x13FFCFFF */ \ + MACRO(TZINFO, 0x0001000 /* 4K */, arg) /* 0x13FFD000 - 0x13FFDFFF */ \ MACRO(MFG_INFO, 0x0001000 /* 4K */, arg) /* 0x13FFE000 - 0x13FFEFFF */ \ MACRO(SHARED_PRF_STORAGE, 0x0001000 /* 4K */, arg) /* 0x13FFF000 - 0x13FFFFFF */ @@ -74,10 +65,22 @@ #define FLASH_REGION_FILESYSTEM_END FLASH_REGION_END_ADDR(FILESYSTEM) #define FLASH_FILESYSTEM_BLOCK_SIZE SUBSECTOR_SIZE_BYTES +#define FLASH_REGION_CD_BEGIN FLASH_REGION_START_ADDR(CD) +#define FLASH_REGION_CD_END FLASH_REGION_END_ADDR(CD) + #define FLASH_REGION_DEBUG_DB_BEGIN FLASH_REGION_START_ADDR(DEBUG_DB) #define FLASH_REGION_DEBUG_DB_END FLASH_REGION_END_ADDR(DEBUG_DB) #define FLASH_DEBUG_DB_BLOCK_SIZE SUBSECTOR_SIZE_BYTES +#define FLASH_REGION_MFG_RESULTS_BEGIN FLASH_REGION_START_ADDR(MFG_RESULTS) +#define FLASH_REGION_MFG_RESULTS_END FLASH_REGION_END_ADDR(MFG_RESULTS) + +#define FLASH_REGION_MFG_BATTERY_STATE_BEGIN FLASH_REGION_START_ADDR(MFG_BATTERY_STATE) +#define FLASH_REGION_MFG_BATTERY_STATE_END FLASH_REGION_END_ADDR(MFG_BATTERY_STATE) + +#define FLASH_REGION_TZINFO_BEGIN FLASH_REGION_START_ADDR(TZINFO) +#define FLASH_REGION_TZINFO_END FLASH_REGION_END_ADDR(TZINFO) + #define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO) #define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO) diff --git a/src/fw/flash_region/flash_region_mt25q.h b/src/fw/flash_region/flash_region_mt25q.h deleted file mode 100644 index ce73f52b7d..0000000000 --- a/src/fw/flash_region/flash_region_mt25q.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define PAGE_SIZE_BYTES (0x100) - -#define SECTOR_SIZE_BYTES (0x10000) -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define SUBSECTOR_SIZE_BYTES (0x1000) -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) - - -// A bit of preprocessor magic to help with automatically calculating flash region addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_DEF(MACRO, arg) \ - MACRO(FIRMWARE_SLOT_1, 0x200000 /* 2048k */, arg) \ - MACRO(SYSTEM_RESOURCES_BANK_0, 0x100000 /* 1024k */, arg) \ - MACRO(SYSTEM_RESOURCES_BANK_1, 0x100000 /* 1024k */, arg) \ - MACRO(SAFE_FIRMWARE, 0x080000 /* 512k */, arg) \ - MACRO(DEBUG_DB, 0x020000 /* 128k */, arg) \ - MACRO(MFG_INFO, 0x020000 /* 128k */, arg) \ - MACRO(FILESYSTEM, 0xB30000 /* 11456k */, arg) \ - MACRO(RSVD, 0x00F000 /* 60k */, arg) \ - MACRO(SHARED_PRF_STORAGE, 0x001000 /* 4k */, arg) - -#include "flash_region_def_helper.h" - - -// Flash region _BEGIN and _END addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SLOT_1) -#define FLASH_REGION_FIRMWARE_SLOT_1_END FLASH_REGION_END_ADDR(FIRMWARE_SLOT_1) - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_0) -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_0) - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_1) -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_1) - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN FLASH_REGION_START_ADDR(SAFE_FIRMWARE) -#define FLASH_REGION_SAFE_FIRMWARE_END FLASH_REGION_END_ADDR(SAFE_FIRMWARE) - -#define FLASH_REGION_DEBUG_DB_BEGIN FLASH_REGION_START_ADDR(DEBUG_DB) -#define FLASH_REGION_DEBUG_DB_END FLASH_REGION_END_ADDR(DEBUG_DB) -#define FLASH_DEBUG_DB_BLOCK_SIZE SUBSECTOR_SIZE_BYTES - -#define FLASH_REGION_FILESYSTEM_BEGIN FLASH_REGION_START_ADDR(FILESYSTEM) -#define FLASH_REGION_FILESYSTEM_END FLASH_REGION_END_ADDR(FILESYSTEM) -#define FLASH_FILESYSTEM_BLOCK_SIZE 0x2000 // 8k - -#define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN FLASH_REGION_START_ADDR(SHARED_PRF_STORAGE) -#define FLASH_REGION_SHARED_PRF_STORAGE_END FLASH_REGION_END_ADDR(SHARED_PRF_STORAGE) - -#define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO) -#define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO) - -#define BOARD_NOR_FLASH_SIZE FLASH_REGION_START_ADDR(_COUNT) - - -// Static asserts to make sure everything worked out -////////////////////////////////////////////////////////////////////////////// - -// make sure all the sizes are multiples of the subsector size (4k) -FLASH_REGION_SIZE_CHECK(SUBSECTOR_SIZE_BYTES) - -// make sure the shared PRF storage is within the last 64k sector so we can protect them. -_Static_assert(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN >= BOARD_NOR_FLASH_SIZE - SECTOR_SIZE_BYTES, - "Shared PRF storage should be within the last 64k of flash"); - -// make sure the total size is what we expect (16MB for robert) -_Static_assert(BOARD_NOR_FLASH_SIZE == 0x1000000, "Flash size should be 16MB"); diff --git a/src/fw/flash_region/flash_region_mx25u.h b/src/fw/flash_region/flash_region_mx25u.h deleted file mode 100644 index a51e4a98c7..0000000000 --- a/src/fw/flash_region/flash_region_mx25u.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define PAGE_SIZE_BYTES (0x100) - -#define SECTOR_SIZE_BYTES (0x10000) -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define SUBSECTOR_SIZE_BYTES (0x1000) -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) - - -// A bit of preprocessor magic to help with automatically calculating flash region addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_DEF(MACRO, arg) \ - MACRO(FIRMWARE_SLOT_1, 0x100000 /* 1024k */, arg) /* 0x0 - 0x100000 */ \ - MACRO(SYSTEM_RESOURCES_BANK_0, 0x080000 /* 512K */, arg) /* 0x100000 - 0x180000 */ \ - MACRO(SYSTEM_RESOURCES_BANK_1, 0x080000 /* 512K */, arg) /* 0x180000 - 0x200000 */ \ - MACRO(SAFE_FIRMWARE, 0x080000 /* 512k */, arg) /* 0x200000 - 0x280000 */ \ - MACRO(DEBUG_DB, 0x020000 /* 128k */, arg) /* 0x280000 - 0x2A0000 */ \ - MACRO(FILESYSTEM, 0x550000 /* 5440k */, arg) /* 0x2A0000 - 0x7F0000 */ \ - MACRO(RSVD, 0x00E000 /* 56k */, arg) /* 0x7F0000 - 0x7FE000 */ \ - MACRO(SHARED_PRF_STORAGE, 0x001000 /* 4k */, arg) /* 0x7FE000 - 0x7FF000 */ \ - MACRO(MFG_INFO, 0x001000 /* 4k */, arg) /* 0x7FF000 - 0x800000 */ - -#include "flash_region_def_helper.h" - - -// Flash region _BEGIN and _END addresses -////////////////////////////////////////////////////////////////////////////// - -#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SLOT_1) -#define FLASH_REGION_FIRMWARE_SLOT_1_END FLASH_REGION_END_ADDR(FIRMWARE_SLOT_1) - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_0) -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_0) - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_1) -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_1) - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN FLASH_REGION_START_ADDR(SAFE_FIRMWARE) -#define FLASH_REGION_SAFE_FIRMWARE_END FLASH_REGION_END_ADDR(SAFE_FIRMWARE) - -#define FLASH_REGION_DEBUG_DB_BEGIN FLASH_REGION_START_ADDR(DEBUG_DB) -#define FLASH_REGION_DEBUG_DB_END FLASH_REGION_END_ADDR(DEBUG_DB) -#define FLASH_DEBUG_DB_BLOCK_SIZE SUBSECTOR_SIZE_BYTES - -#define FLASH_REGION_FILESYSTEM_BEGIN FLASH_REGION_START_ADDR(FILESYSTEM) -#define FLASH_REGION_FILESYSTEM_END FLASH_REGION_END_ADDR(FILESYSTEM) -#define FLASH_FILESYSTEM_BLOCK_SIZE SUBSECTOR_SIZE_BYTES - -#define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN FLASH_REGION_START_ADDR(SHARED_PRF_STORAGE) -#define FLASH_REGION_SHARED_PRF_STORAGE_END FLASH_REGION_END_ADDR(SHARED_PRF_STORAGE) - -#define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO) -#define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO) - -#define BOARD_NOR_FLASH_SIZE FLASH_REGION_START_ADDR(_COUNT) - - -// Static asserts to make sure everything worked out -////////////////////////////////////////////////////////////////////////////// - -// make sure all the sizes are multiples of the subsector size (4k) -FLASH_REGION_SIZE_CHECK(SUBSECTOR_SIZE_BYTES) - -// make sure the PRF and MFG regions are within the last 64k sector so we can protect them. -_Static_assert(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN >= BOARD_NOR_FLASH_SIZE - SECTOR_SIZE_BYTES, - "Shared PRF storage should be within the last 64k of flash"); -_Static_assert(FLASH_REGION_MFG_INFO_BEGIN >= BOARD_NOR_FLASH_SIZE - SECTOR_SIZE_BYTES, - "MFG info should be within the last 64k of flash"); - -// make sure the total size is what we expect (8mb for silk) -_Static_assert(BOARD_NOR_FLASH_SIZE == 0x800000, "Flash size should be 8mb"); diff --git a/src/fw/flash_region/flash_region_n25q.h b/src/fw/flash_region/flash_region_n25q.h index ce964c2b23..980689fb6b 100644 --- a/src/fw/flash_region/flash_region_n25q.h +++ b/src/fw/flash_region/flash_region_n25q.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/flash_region/flash_region_py25q128ha.h b/src/fw/flash_region/flash_region_py25q128ha.h index 7ce09eb8a0..8dc388bcff 100644 --- a/src/fw/flash_region/flash_region_py25q128ha.h +++ b/src/fw/flash_region/flash_region_py25q128ha.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/flash_region/flash_region_qemu.h b/src/fw/flash_region/flash_region_qemu.h new file mode 100644 index 0000000000..e523ce1697 --- /dev/null +++ b/src/fw/flash_region/flash_region_qemu.h @@ -0,0 +1,92 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define PAGE_SIZE_BYTES (0x100) + +#define SECTOR_SIZE_BYTES (0x10000) +#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) + +#define SUBSECTOR_SIZE_BYTES (0x1000) +#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) + +// QEMU external flash XIP base address +#define FLASH_REGION_BASE_ADDRESS 0x10000000 + +// Flash layout for QEMU (32MB external flash, same layout as obelix/gd25q256e) +#define FLASH_REGION_DEF(MACRO, arg) \ + MACRO(PTABLE, 0x0010000 /* 64K */, arg) /* 0x10000000 - 0x1000FFFF */ \ + MACRO(BOOTLOADER, 0x0010000 /* 64K */, arg) /* 0x10010000 - 0x1001FFFF */ \ + MACRO(FIRMWARE_SLOT_0, 0x0300000 /* 3072K */, arg) /* 0x10020000 - 0x1031FFFF */ \ + MACRO(FIRMWARE_SLOT_1, 0x0300000 /* 3072K */, arg) /* 0x10320000 - 0x1061FFFF */ \ + MACRO(SYSTEM_RESOURCES_BANK_0, 0x0200000 /* 2048K */, arg) /* 0x10620000 - 0x1081FFFF */ \ + MACRO(SYSTEM_RESOURCES_BANK_1, 0x0200000 /* 2048K */, arg) /* 0x10820000 - 0x10A1FFFF */ \ + MACRO(SAFE_FIRMWARE, 0x0090000 /* 576K */, arg) /* 0x10A20000 - 0x10AAFFFF */ \ + MACRO(FILESYSTEM, 0x1490000 /* 21056K */, arg) /* 0x10AB0000 - 0x11F3FFFF */ \ + MACRO(CD, 0x0080000 /* 512K */, arg) /* 0x11F40000 - 0x11FBFFFF */ \ + MACRO(RSVD1, 0x000F000 /* 60K */, arg) /* 0x11FC0000 - 0x11FCEFFF */ \ + MACRO(DEBUG_DB, 0x0020000 /* 128K */, arg) /* 0x11FCF000 - 0x11FEEFFF */ \ + MACRO(RSVD2, 0x000C000 /* 48K */, arg) /* 0x11FEF000 - 0x11FFAFFF */ \ + MACRO(MFG_RESULTS, 0x0001000 /* 4K */, arg) /* 0x11FFB000 - 0x11FFBFFF */ \ + MACRO(MFG_BATTERY_STATE, 0x0001000 /* 4K */, arg) /* 0x11FFC000 - 0x11FFCFFF */ \ + MACRO(TZINFO, 0x0001000 /* 4K */, arg) /* 0x11FFD000 - 0x11FFDFFF */ \ + MACRO(MFG_INFO, 0x0001000 /* 4K */, arg) /* 0x11FFE000 - 0x11FFEFFF */ \ + MACRO(SHARED_PRF_STORAGE, 0x0001000 /* 4K */, arg) /* 0x11FFF000 - 0x11FFFFFF */ + +#include "flash_region_def_helper.h" + +// Flash region _BEGIN and _END addresses +////////////////////////////////////////////////////////////////////////////// + +#define FLASH_REGION_PTABLE_BEGIN FLASH_REGION_START_ADDR(PTABLE) +#define FLASH_REGION_PTABLE_END FLASH_REGION_END_ADDR(PTABLE) + +#define FLASH_REGION_BOOTLOADER_BEGIN FLASH_REGION_START_ADDR(BOOTLOADER) +#define FLASH_REGION_BOOTLOADER_END FLASH_REGION_END_ADDR(BOOTLOADER) + +#define FLASH_REGION_FIRMWARE_SLOT_0_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SLOT_0) +#define FLASH_REGION_FIRMWARE_SLOT_0_END FLASH_REGION_END_ADDR(FIRMWARE_SLOT_0) + +#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SLOT_1) +#define FLASH_REGION_FIRMWARE_SLOT_1_END FLASH_REGION_END_ADDR(FIRMWARE_SLOT_1) + +#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_0) +#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_0) + +#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN FLASH_REGION_START_ADDR(SYSTEM_RESOURCES_BANK_1) +#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END FLASH_REGION_END_ADDR(SYSTEM_RESOURCES_BANK_1) + +#define FLASH_REGION_SAFE_FIRMWARE_BEGIN FLASH_REGION_START_ADDR(SAFE_FIRMWARE) +#define FLASH_REGION_SAFE_FIRMWARE_END FLASH_REGION_END_ADDR(SAFE_FIRMWARE) + +#define FLASH_REGION_FILESYSTEM_BEGIN FLASH_REGION_START_ADDR(FILESYSTEM) +#define FLASH_REGION_FILESYSTEM_END FLASH_REGION_END_ADDR(FILESYSTEM) +#define FLASH_FILESYSTEM_BLOCK_SIZE SUBSECTOR_SIZE_BYTES + +// CD region is defined by flash_region.h based on CONFIG_PBLBOOT + +#define FLASH_REGION_DEBUG_DB_BEGIN FLASH_REGION_START_ADDR(DEBUG_DB) +#define FLASH_REGION_DEBUG_DB_END FLASH_REGION_END_ADDR(DEBUG_DB) +#define FLASH_DEBUG_DB_BLOCK_SIZE SUBSECTOR_SIZE_BYTES + +#define FLASH_REGION_MFG_RESULTS_BEGIN FLASH_REGION_START_ADDR(MFG_RESULTS) +#define FLASH_REGION_MFG_RESULTS_END FLASH_REGION_END_ADDR(MFG_RESULTS) + +#define FLASH_REGION_MFG_BATTERY_STATE_BEGIN FLASH_REGION_START_ADDR(MFG_BATTERY_STATE) +#define FLASH_REGION_MFG_BATTERY_STATE_END FLASH_REGION_END_ADDR(MFG_BATTERY_STATE) + +#define FLASH_REGION_TZINFO_BEGIN FLASH_REGION_START_ADDR(TZINFO) +#define FLASH_REGION_TZINFO_END FLASH_REGION_END_ADDR(TZINFO) + +#define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO) +#define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO) + +#define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN FLASH_REGION_START_ADDR(SHARED_PRF_STORAGE) +#define FLASH_REGION_SHARED_PRF_STORAGE_END FLASH_REGION_END_ADDR(SHARED_PRF_STORAGE) + +#define BOARD_NOR_FLASH_SIZE (FLASH_REGION_START_ADDR(_COUNT) - FLASH_REGION_BASE_ADDRESS) + +// Static asserts +FLASH_REGION_SIZE_CHECK(SUBSECTOR_SIZE_BYTES) +_Static_assert(BOARD_NOR_FLASH_SIZE == 0x2000000, "Flash size should be 32mb"); diff --git a/src/fw/flash_region/flash_region_s29vs.h b/src/fw/flash_region/flash_region_s29vs.h deleted file mode 100644 index c6c9002d38..0000000000 --- a/src/fw/flash_region/flash_region_s29vs.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define SECTOR_SIZE_BYTES 0x20000 -#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1)) - -#define FLASH_FILESYSTEM_BLOCK_SIZE 0x2000 - -#define SUBSECTOR_SIZE_BYTES 0x20000 -#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1)) - -// FMC_BANK_1_BASE_ADDRESS -#define FLASH_MEMORY_MAPPABLE_ADDRESS 0x60000000 -#define FLASH_MEMORY_MAPPABLE_SIZE BOARD_NOR_FLASH_SIZE - -// Filesystem layout -/////////////////////////////////////// - -// Space for our flash logs -// NOTE: This range of memory is actually in the special "bottom boot" area of our flash chip -// where the erase sectors are smaller (32k instead of 128k everywhere else). -#define FLASH_REGION_DEBUG_DB_BEGIN 0x0 -#define FLASH_REGION_DEBUG_DB_END 0x20000 // 128k -#define FLASH_DEBUG_DB_BLOCK_SIZE BOTTOM_BOOT_SECTOR_SIZE - -#define BOTTOM_BOOT_REGION_END 0x20000 // 128k -#define BOTTOM_BOOT_SECTOR_SIZE 0x8000 // 32k - -// Regions after this point are in standard, 128kb sized sectors. - -// 640kb gap here. We should save some space for non-filesystem things. It also aligns the -// subsequent sectors nicely. - -// 1 128kb sector for storing bluetooth parings which are shared between normal fw and prf -#define FLASH_REGION_SHARED_PRF_STORAGE_BEGIN 0x0C0000 -#define FLASH_REGION_SHARED_PRF_STORAGE_END 0x0E0000 - -// 1 128kb sector for storing mfg info, see fw/mfg/snowy/mfg_info.c -#define FLASH_REGION_MFG_INFO_BEGIN 0x0E0000 -#define FLASH_REGION_MFG_INFO_END 0x100000 - -// Scratch space for firmware images (normal and recovery). -#define FLASH_REGION_FIRMWARE_SLOT_1_BEGIN 0x100000 -#define FLASH_REGION_FIRMWARE_SLOT_1_END 0x200000 // 1024k - -#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x200000 -#define FLASH_REGION_SAFE_FIRMWARE_END 0x300000 // 1024k - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_BEGIN 0x300000 -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_0_END 0x380000 // 512k - -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_BEGIN 0x380000 -#define FLASH_REGION_SYSTEM_RESOURCES_BANK_1_END 0x400000 // 512k - -#define FLASH_REGION_FILESYSTEM_BEGIN 0x0400000 -#define FLASH_REGION_FILESYSTEM_END 0x1000000 // 8mb+, aka the rest! - -// Constants used for testing flash interface -// NOTE: This purposely overlaps the file system region since the flash test requires a non-critical -// region to operate on. Data in this region will get corrupted and will not get restored after the -// test runs. Any data in this region will have to be manually restored or reinitialized. -#define FLASH_TEST_ADDR_START 0x0800000 // 8MB -#define FLASH_TEST_ADDR_END 0x1000000 // 16MB -#define FLASH_TEST_ADDR_MSK 0x1FFFFFF // test all bits in the 16MB range - -#define BOARD_NOR_FLASH_SIZE 0x1000000 - -#if ((FLASH_REGION_FILESYSTEM_BEGIN > FLASH_TEST_ADDR_START) || (FLASH_REGION_FILESYSTEM_END < FLASH_TEST_ADDR_END)) -#error "ERROR: Flash Test space not withing expected range" -#endif - -// 0x1000000 is the end of the SPI flash address space. - diff --git a/src/fw/freertos_application.c b/src/fw/freertos_application.c index 7fb1d2b962..f6dd0e8746 100644 --- a/src/fw/freertos_application.c +++ b/src/fw/freertos_application.c @@ -1,25 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include "debug/power_tracking.h" #include "drivers/mcu.h" #include "drivers/rtc.h" -#include "drivers/lptim_systick.h" #include "drivers/task_watchdog.h" #include "console/prompt.h" @@ -29,22 +15,13 @@ #include "kernel/util/stop.h" #include "kernel/util/wfi.h" #include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" #include "system/logging.h" #include "util/math.h" -#if defined(MICRO_FAMILY_SF32LB52) -#include -#endif - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include +#include -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) #include #endif @@ -52,63 +29,40 @@ #include "task.h" #include "freertos_application.h" -static uint64_t s_analytics_device_sleep_cpu_cycles = 0; -static RtcTicks s_analytics_device_stop_ticks = 0; - -static uint64_t s_analytics_app_sleep_cpu_cycles = 0; -static RtcTicks s_analytics_app_stop_ticks = 0; +#if !defined(CONFIG_SOC_SF32LB52) +static RtcTicks s_analytics_sleep_ticks = 0; +static RtcTicks s_analytics_stop_ticks = 0; static uint32_t s_last_ticks_elapsed_in_stop = 0; static uint32_t s_last_ticks_commanded_in_stop = 0; static uint32_t s_ticks_corrected = 0; +#endif // We need different timings for our different platforms since we use different mechanisms to keep -// time and to wake us up out of stop mode. On stm32f2 we don't have a millisecond register so we -// use the "retina rtc" and a RTC Alarm peripheral. On stm32f4 we do have a millisecond register -// so use the RTC running at normal speed and a RTC Wakeup peripheral. These have different -// accuracies when going into and out of stop mode. -#if defined(MICRO_FAMILY_STM32F2) +// time and to wake us up out of stop mode. +#if defined(CONFIG_SOC_NRF52) //! Stop mode until this number of ticks before the next scheduled task static const RtcTicks EARLY_WAKEUP_TICKS = 2; -// slightly larger than the 2 permitted by FreeRTOS in tasks.c -static const RtcTicks MIN_STOP_TICKS = 5; -#elif defined(MICRO_FAMILY_STM32F4) || defined(MICRO_FAMILY_STM32F7) -//! Stop mode until this number of ticks before the next scheduled task -static const RtcTicks EARLY_WAKEUP_TICKS = 4; -//! Stop mode until this number of ticks before the next scheduled task -static const RtcTicks MIN_STOP_TICKS = 8; -#elif defined(MICRO_FAMILY_NRF5) //! Stop mode until this number of ticks before the next scheduled task +static const RtcTicks MIN_STOP_TICKS = 5; +#elif defined(CONFIG_QEMU) static const RtcTicks EARLY_WAKEUP_TICKS = 2; -//! Stop mode until this number of ticks before the next scheduled task static const RtcTicks MIN_STOP_TICKS = 5; -#elif defined(MICRO_FAMILY_SF32LB52) -//! Stop mode until this number of ticks before the next scheduled task -static const RtcTicks EARLY_WAKEUP_TICKS = 10; // relative large to avid tasks.c:1960 assert -//! Stop mode until this number of ticks before the next scheduled task -static const RtcTicks MIN_STOP_TICKS = 15; -#else -#error "Unknown micro family" #endif +#if !defined(CONFIG_SOC_SF32LB52) // 1 second ticks so that we only wake up once every regular timer interval. static const RtcTicks MAX_STOP_TICKS = RTC_TICKS_HZ; +#endif +#if !defined(CONFIG_SOC_SF32LB52) extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { -#if !defined(MICRO_FAMILY_SF32LB52) if (!rtc_alarm_is_initialized() || !sleep_mode_is_allowed()) { // the RTC is not yet initialized to the point where it can wake us from sleep or sleep/stop // is disabled. Just returning will cause a busy loop where the caller thought we slept for // 0 ticks and will reevaluate what to do next (probably just try again). return; } -#else - if (!lptim_systick_is_initialized() || !sleep_mode_is_allowed() || - !ipc_queue_check_idle()) { - // To avoid LCPU enter incorrect state, make sure ipc queue is empty before enter stop mode. - return; - } -#endif // Note: all tasks are suspended at this point, but we can still be interrupted // so the critical section is necessary. taskENTER_CRITICAL() is not used here @@ -122,7 +76,7 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { // See: http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/BABGGICD.html#BGBHDHAI __disable_irq(); -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) // We're going to sleep, so turn off the caches (they consume quiescent // power). It's more efficient to have them on when we're awake, but for // now, they gotta go. This holds true even if we're not going to sleep @@ -134,39 +88,7 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { if (eTaskConfirmSleepModeStatus() != eAbortSleep) { if (xExpectedIdleTime < MIN_STOP_TICKS || !stop_mode_is_allowed()) { -#if defined(MICRO_FAMILY_NRF5) - // We'd like to count how long we were asleep for, but on nRF5, - // systick is suppressed in sleep mode so that the 64MHz core clock - // doesn't have to be running. We can't use the PebbleOS system RTC - // to measure how long we were asleep for because it is not - // sufficiently granular -- it's running at the system tick rate, so - // all we will learn is either 'we slept until the tick timer went - // off', or 'we got interrupted before the end of the tick'. - // - // It would be nice to get a higher resolution timer of how long we - // were asleep for, though. Luckily, when NimBLE is active, it's - // using RTC0 in 32 kHz mode, and we can use that to measure how long - // we were asleep for! This is, of course, kind of a hack -- it works - // only while NimBLE is running. But we don't have any dramtically - // better options, and this is definitely better than nothing. - // - // This is used *only* here, and is used *only* for statistics, so - // it's OK if this is brittle (and, it is!). If NimBLE is not running - // (and RTC0 is shut down), then we just end up measuring 0 here -- no - // harm, no foul. - uint32_t rtc_start = NRF_RTC0->COUNTER; -#elif defined(MICRO_FAMILY_SF32LB52) - uint32_t counter_start = LPTIM1->CNT; -#else - // We assume that a WFI to trigger sleep mode will not last longer than 1 - // SysTick. (The SysTick INT doesn't automatically get suppressed) Thus, - // we use the SysTick timer to get a better estimate of our sleep time - // - // TODO: It would be nice if there was a clean way to actually 'suppress - // ticks' while in sleep mode. If we figure that out, we would likely - // need to update how this calculation works - uint32_t systick_start = SysTick->VAL; -#endif + RtcTicks sleep_start_ticks = rtc_get_ticks(); power_tracking_start(PowerSystemMcuCoreSleep); __DSB(); // Drain any pending memory writes before entering sleep. @@ -174,48 +96,18 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { __ISB(); // Let the pipeline catch up (force the WFI to activate before moving on). power_tracking_stop(PowerSystemMcuCoreSleep); -#if defined(MICRO_FAMILY_NRF5) - uint32_t rtc_end = NRF_RTC0->COUNTER; - - if (rtc_end < rtc_start) /* NimBLE uses RTC0 (24 bits), 32 kiHz */ - rtc_end += 0x1000000; - uint32_t rtc_elapsed = rtc_end - rtc_start; - uint32_t cycles_elapsed = rtc_elapsed * SystemCoreClock / 32768; -#elif defined(MICRO_FAMILY_SF32LB52) - uint32_t counter_stop = LPTIM1->CNT; - if (counter_stop < counter_start) { - counter_stop += 0x10000; - } - uint32_t counter_elapsed = counter_stop - counter_start; - uint32_t cycles_elapsed = (counter_elapsed * RTC_TICKS_HZ) / 8000; -#else - uint32_t systick_stop = SysTick->VAL; - uint32_t cycles_elapsed; - if (systick_stop < systick_start) { - cycles_elapsed = systick_start - systick_stop; - } else { - cycles_elapsed = (SysTick->LOAD - systick_stop) + systick_start; - } -#endif - - s_analytics_device_sleep_cpu_cycles += cycles_elapsed; - s_analytics_app_sleep_cpu_cycles += cycles_elapsed; + s_analytics_sleep_ticks += rtc_get_ticks() - sleep_start_ticks; } else { const RtcTicks stop_duration = MIN(xExpectedIdleTime - EARLY_WAKEUP_TICKS, MAX_STOP_TICKS); // Go into stop mode until the wakeup_tick. s_last_ticks_commanded_in_stop = stop_duration; -#if !defined(MICRO_FAMILY_SF32LB52) + rtc_alarm_set(stop_duration); enter_stop_mode(); RtcTicks ticks_elapsed = rtc_alarm_get_elapsed_ticks(); -#else - lptim_systick_tickless_idle((uint32_t)stop_duration); - enter_stop_mode(); - uint32_t ticks_elapsed = lptim_systick_get_elapsed_ticks(); -#endif s_last_ticks_elapsed_in_stop = ticks_elapsed; vTaskStepTick(ticks_elapsed); @@ -224,19 +116,19 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { // incremented task_watchdog_step_elapsed_time_ms((ticks_elapsed * 1000) / RTC_TICKS_HZ); - s_analytics_device_stop_ticks += ticks_elapsed; - s_analytics_app_stop_ticks += ticks_elapsed; + s_analytics_stop_ticks += ticks_elapsed; } } power_tracking_start(PowerSystemMcuCoreRun); -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) NRF_NVMC->ICACHECNF |= NVMC_ICACHECNF_CACHEEN_Msk; #endif __enable_irq(); } +#endif void vApplicationStackOverflowHook(TaskHandle_t task_handle, signed char *name) { PebbleTask task = pebble_task_get_task_for_handle(task_handle); @@ -246,7 +138,7 @@ void vApplicationStackOverflowHook(TaskHandle_t task_handle, signed char *name) // area in fault_handling.c has the logic to safely kill those user tasks without forcing // a reboot. if ((task != PebbleTask_App) && (task != PebbleTask_Worker)) { - PBL_LOG_SYNC(LOG_LEVEL_ERROR, "Stack overflow [task: %s]", name); + PBL_LOG_SYNC_ERR("Stack overflow [task: %s]", name); RebootReason reason = { .code = RebootReasonCode_StackOverflow, .data8[0] = task @@ -285,7 +177,7 @@ void* pvPortMalloc(size_t xSize) { return kernel_malloc(xSize); } - +#if !defined(CONFIG_SOC_SF32LB52) // Called from the SysTick handler ISR to adjust ticks for situations where the CPU might // occasionally fall behind and miss some tick interrupts (like when running under emulation). bool vPortCorrectTicks(void) { @@ -327,46 +219,46 @@ bool vPortCorrectTicks(void) { } return need_context_switch; } +#endif +#if !defined(CONFIG_SOC_SF32LB52) bool vPortEnableTimer() { -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) rtc_enable_synthetic_systick(); return true; -#elif defined(MICRO_FAMILY_SF32LB52) - lptim_systick_enable(); - return true; #else return false; #endif } +#endif // CPU analytics /////////////////////////////////////////////////////////// +#if !defined(CONFIG_SOC_SF32LB52) static uint32_t s_last_ticks = 0; + void dump_current_runtime_stats(void) { - uint32_t stop_ms = ticks_to_milliseconds(s_analytics_device_stop_ticks); - uint32_t sleep_ms = mcu_cycles_to_milliseconds(s_analytics_device_sleep_cpu_cycles); + uint32_t stop_ticks = s_analytics_stop_ticks; + uint32_t sleep_ticks = s_analytics_sleep_ticks; uint32_t now_ticks = rtc_get_ticks(); - uint32_t running_ms = - ticks_to_milliseconds(now_ticks - s_last_ticks) - stop_ms - sleep_ms; - - uint32_t tot_time = running_ms + sleep_ms + stop_ms; + uint32_t total_ticks = now_ticks - s_last_ticks; + uint32_t running_ticks = total_ticks - stop_ticks - sleep_ticks; char buf[160]; - snprintf(buf, sizeof(buf), "Run: %"PRIu32" ms (%"PRIu32" %%)", - running_ms, (running_ms * 100) / tot_time); + snprintf(buf, sizeof(buf), "Run: %"PRIu32" ticks (%"PRIu32" %%)", + running_ticks, (running_ticks * 100) / total_ticks); prompt_send_response(buf); - snprintf(buf, sizeof(buf), "Sleep: %"PRIu32" ms (%"PRIu32" %%)", - sleep_ms, (sleep_ms * 100) / tot_time); + snprintf(buf, sizeof(buf), "Sleep: %"PRIu32" ticks (%"PRIu32" %%)", + sleep_ticks, (sleep_ticks * 100) / total_ticks); prompt_send_response(buf); - snprintf(buf, sizeof(buf), "Stop: %"PRIu32" ms (%"PRIu32" %%)", - stop_ms, (stop_ms * 100) / tot_time); + snprintf(buf, sizeof(buf), "Stop: %"PRIu32" ticks (%"PRIu32" %%)", + stop_ticks, (stop_ticks * 100) / total_ticks); prompt_send_response(buf); - snprintf(buf, sizeof(buf), "Tot: %"PRIu32" ms", tot_time); + snprintf(buf, sizeof(buf), "Tot: %"PRIu32" ticks", total_ticks); prompt_send_response(buf); - + uint32_t rtc_ticks = rtc_get_ticks(); uint32_t rtos_ticks = xTaskGetTickCount(); snprintf(buf, sizeof(buf), "RTC ticks: %"PRIu32", RTOS ticks: %"PRIu32 ", ticks corrected: %"PRIu32 ", last ticks stopped: %"PRIu32 " / %"PRIu32, @@ -374,47 +266,33 @@ void dump_current_runtime_stats(void) { prompt_send_response(buf); } -void analytics_external_collect_cpu_stats(void) { - uint32_t stop_ms = ticks_to_milliseconds(s_analytics_device_stop_ticks); - uint32_t sleep_ms = mcu_cycles_to_milliseconds(s_analytics_device_sleep_cpu_cycles); - - analytics_set(ANALYTICS_DEVICE_METRIC_CPU_STOP_TIME, stop_ms, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_CPU_SLEEP_TIME, sleep_ms, AnalyticsClient_System); - - uint32_t now_ticks = rtc_get_ticks(); - uint32_t ms_running = - ticks_to_milliseconds(now_ticks - s_last_ticks) - stop_ms - sleep_ms; - analytics_set(ANALYTICS_DEVICE_METRIC_CPU_RUNNING_TIME, ms_running, AnalyticsClient_System); - - s_last_ticks = now_ticks; - s_analytics_device_sleep_cpu_cycles = 0; - s_analytics_device_stop_ticks = 0; -} +void pbl_analytics_external_collect_cpu_stats(void) { + uint32_t stop_ticks = s_analytics_stop_ticks; + uint32_t sleep_ticks = s_analytics_sleep_ticks; -void analytics_external_collect_app_cpu_stats(void) { - static uint32_t s_last_ticks = 0; + RtcTicks now_ticks = rtc_get_ticks(); + uint32_t total_ticks = (uint32_t)(now_ticks - s_last_ticks); + uint32_t running_ticks = total_ticks - stop_ticks - sleep_ticks; - uint32_t sleep_ms = mcu_cycles_to_milliseconds(s_analytics_app_sleep_cpu_cycles); + // Calculate percentages + uint16_t running_pct = 0; + uint16_t stop_pct = 0; + uint16_t sleep_pct = 0; - uint32_t now_ticks = rtc_get_ticks(); - uint32_t stop_ms = ticks_to_milliseconds(s_analytics_app_stop_ticks); - uint32_t awake_ms = ticks_to_milliseconds(now_ticks - s_last_ticks) - stop_ms - sleep_ms; - - analytics_set(ANALYTICS_APP_METRIC_CPU_RUNNING_TIME, awake_ms, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_CPU_SLEEP_TIME, - mcu_cycles_to_milliseconds(s_analytics_app_sleep_cpu_cycles), - AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_CPU_STOP_TIME, stop_ms, AnalyticsClient_App); - - // NOTE: When we are running, we can't really tell how much of the time was spent in each task, so - // the best we can do as attribute the elapsed running time to both the foreground and background worker - if (worker_manager_get_current_worker_md() != NULL) { - analytics_set(ANALYTICS_APP_METRIC_BG_CPU_RUNNING_TIME, awake_ms, AnalyticsClient_Worker); - analytics_set(ANALYTICS_APP_METRIC_BG_CPU_SLEEP_TIME, sleep_ms, AnalyticsClient_Worker); - analytics_set(ANALYTICS_APP_METRIC_BG_CPU_STOP_TIME, stop_ms, AnalyticsClient_Worker); + if (total_ticks > 0) { + running_pct = (uint16_t)((running_ticks * 10000ULL) / total_ticks); + stop_pct = (uint16_t)((stop_ticks * 10000ULL) / total_ticks); + sleep_pct = (uint16_t)((sleep_ticks * 10000ULL) / total_ticks); } + // NRF5: sleep0 = light sleep (WFI), sleep1 = stop mode, sleep2 = unused + PBL_ANALYTICS_SET_UNSIGNED(cpu_running_pct, running_pct); + PBL_ANALYTICS_SET_UNSIGNED(cpu_sleep0_pct, sleep_pct); + PBL_ANALYTICS_SET_UNSIGNED(cpu_sleep1_pct, stop_pct); + PBL_ANALYTICS_SET_UNSIGNED(cpu_sleep2_pct, 0); + s_last_ticks = now_ticks; - s_analytics_app_sleep_cpu_cycles = 0; - s_analytics_app_stop_ticks = 0; + s_analytics_sleep_ticks = 0; + s_analytics_stop_ticks = 0; } +#endif \ No newline at end of file diff --git a/src/fw/freertos_application.h b/src/fw/freertos_application.h index a8130d85f8..3b1932669d 100644 --- a/src/fw/freertos_application.h +++ b/src/fw/freertos_application.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/freertos_types.h b/src/fw/freertos_types.h index 10e1b62c6c..9d4681677a 100644 --- a/src/fw/freertos_types.h +++ b/src/fw/freertos_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/fw_common.ld b/src/fw/fw_common.ld index 68525c81df..d529dfab96 100644 --- a/src/fw/fw_common.ld +++ b/src/fw/fw_common.ld @@ -28,17 +28,15 @@ ASSERT(ORIGIN(KERNEL_RAM) != ORIGIN(WORKER_RAM), ASSERT(ORIGIN(APP_RAM) != ORIGIN(WORKER_RAM), "APP_RAM cannot alias WORKER_RAM"); -/* PBL-40376: Temp hack: put .rocky_bss at end of APP_RAM: - Interim solution until all statics are removed from applib & jerry */ -__ROCKY_BSS_size__ = 1512; - __SRAM_size__ = LENGTH(KERNEL_RAM) + LENGTH(WORKER_RAM) + LENGTH(APP_RAM); __WORKER_RAM__ = ORIGIN(WORKER_RAM); __WORKER_RAM_end__ = __WORKER_RAM__ + LENGTH(WORKER_RAM); +__WORKER_RAM_size__ = LENGTH(WORKER_RAM); __APP_RAM__ = ORIGIN(APP_RAM); __APP_RAM_end__ = __APP_RAM__ + LENGTH(APP_RAM); +__APP_RAM_size__ = LENGTH(APP_RAM); __KERNEL_RAM_start__ = ORIGIN(KERNEL_RAM); __KERNEL_RAM_size__ = LENGTH(KERNEL_RAM); @@ -116,18 +114,7 @@ SECTIONS { . = ALIGN(8); } >FLASH - /* PBL-40376: Temp hack: put .rocky_bss at end of APP_RAM: - Interim solution until all statics are removed from applib & jerry */ - .app_ram (NOLOAD) : { - . = . + LENGTH(APP_RAM) - __ROCKY_BSS_size__; - . = ALIGN(4); - __ROCKY_BSS__ = ABSOLUTE(.); - third_party/jerryscript/jerryscript/libjerry_core.a:(.bss .bss.*) - . = ALIGN(4); - *(.rocky_bss) - } >APP_RAM - - .kernel_data : ALIGN(8) { + .kernel_data : ALIGN(32) { __data_start = .; /* This is used by the startup in order to initialize the .data secion */ *(.data) @@ -176,7 +163,7 @@ SECTIONS { . = ALIGN(__stack_guard_size__); __kernel_main_stack_start__ = .; - . = . + 2048; + . = . + 4096; } >REGION_KERNEL_STACKS __kernel_main_stack_size__ = . - __kernel_main_stack_start__; @@ -184,7 +171,7 @@ SECTIONS { . = ALIGN(__stack_guard_size__); __kernel_bg_stack_start__ = .; - . = . + 1536; + . = . + 4096; } >REGION_KERNEL_STACKS __kernel_bg_stack_size__ = . - __kernel_bg_stack_start__; diff --git a/src/fw/hardfault_handler.c b/src/fw/hardfault_handler.c index d3e7130081..b216cd4e58 100644 --- a/src/fw/hardfault_handler.c +++ b/src/fw/hardfault_handler.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/logging_private.h" #include "system/die.h" @@ -23,8 +10,7 @@ #include "util/size.h" #include "util/string.h" -#define CMSIS_COMPATIBLE -#include +#include #include #include @@ -146,9 +132,18 @@ void fault_handler_dump(char buffer[80], unsigned int *stacked_args) { } static void hard_fault_handler_c(unsigned int* hardfault_args) { - // Log the lr instead of the pc. We frequently crash due to PC being madness. While the lr may be a little further - // than the actual crash, it should give us enough context. - const unsigned int stacked_lr = ((unsigned long) hardfault_args[5]); + // Prefer LR (PC is often madness on a hardfault). Fall back through PC, + // BFAR, MMFAR so Memfault always has a non-zero address to fingerprint on. + unsigned int stacked_lr = ((unsigned long) hardfault_args[5]); + if (stacked_lr == 0) { + stacked_lr = ((unsigned long) hardfault_args[6]); + } + if (stacked_lr == 0 && (SCB->CFSR & (1 << 15))) { // BFARVALID + stacked_lr = SCB->BFAR; + } + if (stacked_lr == 0 && (SCB->CFSR & (1 << 7))) { // MMFARVALID + stacked_lr = SCB->MMFAR; + } RebootReason reason = { .code = RebootReasonCode_HardFault, .extra = { .value = stacked_lr } }; reboot_reason_set(&reason); diff --git a/src/fw/interrupt_stubs.c b/src/fw/interrupt_stubs.c index de7b43bb67..06f0945c2b 100644 --- a/src/fw/interrupt_stubs.c +++ b/src/fw/interrupt_stubs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ void DebugMon_Handler(void) { } diff --git a/src/fw/irq_nrf52840.def b/src/fw/irq_nrf52.def similarity index 100% rename from src/fw/irq_nrf52840.def rename to src/fw/irq_nrf52.def diff --git a/src/fw/irq_qemu.def b/src/fw/irq_qemu.def new file mode 100644 index 0000000000..d3d949460a --- /dev/null +++ b/src/fw/irq_qemu.def @@ -0,0 +1,12 @@ +IRQ_DEF(0, UART0) +IRQ_DEF(1, UART1) +IRQ_DEF(2, UART2) +IRQ_DEF(3, TIMER0) +IRQ_DEF(4, TIMER1) +IRQ_DEF(5, RTC) +IRQ_DEF(6, GPIO) +IRQ_DEF(7, DISPLAY) +IRQ_DEF(8, EXTFLASH) +IRQ_DEF(9, TOUCH) +IRQ_DEF(10, AUDIO) +IRQ_DEF(11, WATCHDOG) diff --git a/src/fw/irq_stm32.def b/src/fw/irq_stm32.def deleted file mode 100644 index 4dc6b92374..0000000000 --- a/src/fw/irq_stm32.def +++ /dev/null @@ -1,7 +0,0 @@ -#if MICRO_FAMILY_STM32F2 || MICRO_FAMILY_STM32F4 -#include "irq_stm32f2_f4.def" -#elif MICRO_FAMILY_STM32F7 -#include "irq_stm32f7.def" -#else -#error "Unrecognized or no microcontroller family configured" -#endif diff --git a/src/fw/irq_stm32f2_f4.def b/src/fw/irq_stm32f2_f4.def deleted file mode 100644 index d7232304fd..0000000000 --- a/src/fw/irq_stm32f2_f4.def +++ /dev/null @@ -1,116 +0,0 @@ -IRQ_DEF(0, WWDG) // Window WatchDog -IRQ_DEF(1, PVD) // PVD through EXTI Line detection -IRQ_DEF(2, TAMP_STAMP) // Tamper and TimeStamps through the EXTI line -IRQ_DEF(3, RTC_WKUP) // RTC Wakeup through the EXTI line -IRQ_DEF(4, FLASH) // FLASH -IRQ_DEF(5, RCC) // RCC -IRQ_DEF(6, EXTI0) // EXTI Line0 -IRQ_DEF(7, EXTI1) // EXTI Line1 -IRQ_DEF(8, EXTI2) // EXTI Line2 -IRQ_DEF(9, EXTI3) // EXTI Line3 -IRQ_DEF(10, EXTI4) // EXTI Line4 -IRQ_DEF(11, DMA1_Stream0) // DMA1 Stream 0 -IRQ_DEF(12, DMA1_Stream1) // DMA1 Stream 1 -IRQ_DEF(13, DMA1_Stream2) // DMA1 Stream 2 -IRQ_DEF(14, DMA1_Stream3) // DMA1 Stream 3 -IRQ_DEF(15, DMA1_Stream4) // DMA1 Stream 4 -IRQ_DEF(16, DMA1_Stream5) // DMA1 Stream 5 -IRQ_DEF(17, DMA1_Stream6) // DMA1 Stream 6 -IRQ_DEF(18, ADC) // ADC1, ADC2 and ADC3s -IRQ_DEF(19, CAN1_TX) // CAN1 TX -IRQ_DEF(20, CAN1_RX0) // CAN1 RX0 -IRQ_DEF(21, CAN1_RX1) // CAN1 RX1 -IRQ_DEF(22, CAN1_SCE) // CAN1 SCE -IRQ_DEF(23, EXTI9_5) // External Line[9:5]s -IRQ_DEF(24, TIM1_BRK_TIM9) // TIM1 Break and TIM9 -IRQ_DEF(25, TIM1_UP_TIM10) // TIM1 Update and TIM10 -IRQ_DEF(26, TIM1_TRG_COM_TIM11) // TIM1 Trigger and Commutation and TIM11 -IRQ_DEF(27, TIM1_CC) // TIM1 Capture Compare -IRQ_DEF(28, TIM2) // TIM2 -IRQ_DEF(29, TIM3) // TIM3 -IRQ_DEF(30, TIM4) // TIM4 -IRQ_DEF(31, I2C1_EV) // I2C1 Event -IRQ_DEF(32, I2C1_ER) // I2C1 Error -IRQ_DEF(33, I2C2_EV) // I2C2 Event -IRQ_DEF(34, I2C2_ER) // I2C2 Error -IRQ_DEF(35, SPI1) // SPI1 -IRQ_DEF(36, SPI2) // SPI2 -IRQ_DEF(37, USART1) // USART1 -IRQ_DEF(38, USART2) // USART2 -IRQ_DEF(39, USART3) // USART3 -IRQ_DEF(40, EXTI15_10) // External Line[15:10]s -IRQ_DEF(41, RTC_Alarm) // RTC Alarm (A and B) through EXTI Line -IRQ_DEF(42, OTG_FS_WKUP) // USB OTG FS Wakeup through EXTI line -IRQ_DEF(43, TIM8_BRK_TIM12) // TIM8 Break and TIM12 -IRQ_DEF(44, TIM8_UP_TIM13) // TIM8 Update and TIM13 -IRQ_DEF(45, TIM8_TRG_COM_TIM14) // TIM8 Trigger and Commutation and TIM14 -IRQ_DEF(46, TIM8_CC) // TIM8 Capture Compare -IRQ_DEF(47, DMA1_Stream7) // DMA1 Stream7 -IRQ_DEF(48, FSMC) // FSMC -IRQ_DEF(49, SDIO) // SDIO -IRQ_DEF(50, TIM5) // TIM5 -IRQ_DEF(51, SPI3) // SPI3 -#if !defined(STM32F412xG) -IRQ_DEF(52, UART4) // UART4 -IRQ_DEF(53, UART5) // UART5 -IRQ_DEF(54, TIM6_DAC) // TIM6 and DAC1&2 underrun errors -#else -IRQ_DEF(54, TIM6) // TIM6 -#endif -IRQ_DEF(55, TIM7) // TIM7 -IRQ_DEF(56, DMA2_Stream0) // DMA2 Stream 0 -IRQ_DEF(57, DMA2_Stream1) // DMA2 Stream 1 -IRQ_DEF(58, DMA2_Stream2) // DMA2 Stream 2 -IRQ_DEF(59, DMA2_Stream3) // DMA2 Stream 3 -IRQ_DEF(60, DMA2_Stream4) // DMA2 Stream 4 -#if !defined(STM32F412xG) -IRQ_DEF(61, ETH) // Ethernet -IRQ_DEF(62, ETH_WKUP) // Ethernet Wakeup through EXTI line -#else -IRQ_DEF(61, DFSDM1) // DFSDM1 -IRQ_DEF(62, DFSDM2) // DFSDM2 -#endif -IRQ_DEF(63, CAN2_TX) // CAN2 TX -IRQ_DEF(64, CAN2_RX0) // CAN2 RX0 -IRQ_DEF(65, CAN2_RX1) // CAN2 RX1 -IRQ_DEF(66, CAN2_SCE) // CAN2 SCE -IRQ_DEF(67, OTG_FS) // USB OTG FS -IRQ_DEF(68, DMA2_Stream5) // DMA2 Stream 5 -IRQ_DEF(69, DMA2_Stream6) // DMA2 Stream 6 -IRQ_DEF(70, DMA2_Stream7) // DMA2 Stream 7 -IRQ_DEF(71, USART6) // USART6 -IRQ_DEF(72, I2C3_EV) // I2C3 event -IRQ_DEF(73, I2C3_ER) // I2C3 error -#if !defined(STM32F412xG) -IRQ_DEF(74, OTG_HS_EP1_OUT) // USB OTG HS End Point 1 Out -IRQ_DEF(75, OTG_HS_EP1_IN) // USB OTG HS End Point 1 In -IRQ_DEF(76, OTG_HS_WKUP) // USB OTG HS Wakeup through EXTI -IRQ_DEF(77, OTG_HS) // USB OTG HS -IRQ_DEF(78, DCMI) // DCMI -IRQ_DEF(79, CRYP) // CRYP crypto -#endif -#if !defined(STM32F412xG) -IRQ_DEF(80, HASH_RNG) // Hash and Rng -#else -IRQ_DEF(80, RNG) // Rng -#endif -#if !defined(STM32F2XX) // STM32F2 IRQs end here -IRQ_DEF(81, FPU) // FPU -#if !defined(STM32F412xG) -IRQ_DEF(82, UART7) // UART7 -IRQ_DEF(83, UART8) // UART8 -#endif -IRQ_DEF(84, SPI4) // SPI4 -IRQ_DEF(85, SPI5) // SPI5 -#if !defined(STM32F412xG) -IRQ_DEF(86, SPI6) // SPI6 -IRQ_DEF(87, SAI1) // SAI1 -IRQ_DEF(88, LTDC) // LTDC -IRQ_DEF(89, LTDC_ER) // LTDC_ER -IRQ_DEF(90, DMA2D) // DMA2D -#else -IRQ_DEF(92, QUADSPI) // QUADSPI -IRQ_DEF(95, FMPI2C1_EV) // FMPI2C1 Event -IRQ_DEF(96, FMPI2C1_ER) // FMPI2C1 Error -#endif -#endif // !defined(STM32F2XX) diff --git a/src/fw/irq_stm32f7.def b/src/fw/irq_stm32f7.def deleted file mode 100644 index 4ba0dc703e..0000000000 --- a/src/fw/irq_stm32f7.def +++ /dev/null @@ -1,109 +0,0 @@ -IRQ_DEF(0, WWDG) // Window WatchDog -IRQ_DEF(1, PVD) // PVD through EXTI Line detection -IRQ_DEF(2, TAMP_STAMP) // Tamper and TimeStamps through the EXTI line -IRQ_DEF(3, RTC_WKUP) // RTC Wakeup through the EXTI line -IRQ_DEF(4, FLASH) // FLASH -IRQ_DEF(5, RCC) // RCC -IRQ_DEF(6, EXTI0) // EXTI Line0 -IRQ_DEF(7, EXTI1) // EXTI Line1 -IRQ_DEF(8, EXTI2) // EXTI Line2 -IRQ_DEF(9, EXTI3) // EXTI Line3 -IRQ_DEF(10, EXTI4) // EXTI Line4 -IRQ_DEF(11, DMA1_Stream0) // DMA1 Stream 0 -IRQ_DEF(12, DMA1_Stream1) // DMA1 Stream 1 -IRQ_DEF(13, DMA1_Stream2) // DMA1 Stream 2 -IRQ_DEF(14, DMA1_Stream3) // DMA1 Stream 3 -IRQ_DEF(15, DMA1_Stream4) // DMA1 Stream 4 -IRQ_DEF(16, DMA1_Stream5) // DMA1 Stream 5 -IRQ_DEF(17, DMA1_Stream6) // DMA1 Stream 6 -IRQ_DEF(18, ADC) // ADC1, ADC2 and ADC3s -IRQ_DEF(19, CAN1_TX) // CAN1 TX -IRQ_DEF(20, CAN1_RX0) // CAN1 RX0 -IRQ_DEF(21, CAN1_RX1) // CAN1 RX1 -IRQ_DEF(22, CAN1_SCE) // CAN1 SCE -IRQ_DEF(23, EXTI9_5) // External Line[9:5]s -IRQ_DEF(24, TIM1_BRK_TIM9) // TIM1 Break and TIM9 -IRQ_DEF(25, TIM1_UP_TIM10) // TIM1 Update and TIM10 -IRQ_DEF(26, TIM1_TRG_COM_TIM11) // TIM1 Trigger and Commutation and TIM11 -IRQ_DEF(27, TIM1_CC) // TIM1 Capture Compare -IRQ_DEF(28, TIM2) // TIM2 -IRQ_DEF(29, TIM3) // TIM3 -IRQ_DEF(30, TIM4) // TIM4 -IRQ_DEF(31, I2C1_EV) // I2C1 Event -IRQ_DEF(32, I2C1_ER) // I2C1 Error -IRQ_DEF(33, I2C2_EV) // I2C2 Event -IRQ_DEF(34, I2C2_ER) // I2C2 Error -IRQ_DEF(35, SPI1) // SPI1 -IRQ_DEF(36, SPI2) // SPI2 -IRQ_DEF(37, USART1) // USART1 -IRQ_DEF(38, USART2) // USART2 -IRQ_DEF(39, USART3) // USART3 -IRQ_DEF(40, EXTI15_10) // External Line[15:10]s -IRQ_DEF(41, RTC_Alarm) // RTC Alarm (A and B) through EXTI Line -IRQ_DEF(42, OTG_FS_WKUP) // USB OTG FS Wakeup through EXTI line -IRQ_DEF(43, TIM8_BRK_TIM12) // TIM8 Break and TIM12 -IRQ_DEF(44, TIM8_UP_TIM13) // TIM8 Update and TIM13 -IRQ_DEF(45, TIM8_TRG_COM_TIM14) // TIM8 Trigger and Commutation and TIM14 -IRQ_DEF(46, TIM8_CC) // TIM8 Capture Compare -IRQ_DEF(47, DMA1_Stream7) // DMA1 Stream7 -IRQ_DEF(48, FMC) // FMC -IRQ_DEF(49, SDMMC1) // SDMMC1 -IRQ_DEF(50, TIM5) // TIM5 -IRQ_DEF(51, SPI3) // SPI3 -IRQ_DEF(52, UART4) // UART4 -IRQ_DEF(53, UART5) // UART5 -IRQ_DEF(54, TIM6_DAC) // TIM6 and DAC1&2 underrun errors -IRQ_DEF(55, TIM7) // TIM7 -IRQ_DEF(56, DMA2_Stream0) // DMA2 Stream 0 -IRQ_DEF(57, DMA2_Stream1) // DMA2 Stream 1 -IRQ_DEF(58, DMA2_Stream2) // DMA2 Stream 2 -IRQ_DEF(59, DMA2_Stream3) // DMA2 Stream 3 -IRQ_DEF(60, DMA2_Stream4) // DMA2 Stream 4 -IRQ_DEF(61, ETH) // Ethernet -IRQ_DEF(62, ETH_WKUP) // Ethernet Wakeup through EXTI line -IRQ_DEF(63, CAN2_TX) // CAN2 TX -IRQ_DEF(64, CAN2_RX0) // CAN2 RX0 -IRQ_DEF(65, CAN2_RX1) // CAN2 RX1 -IRQ_DEF(66, CAN2_SCE) // CAN2 SCE -IRQ_DEF(67, OTG_FS) // USB OTG FS -IRQ_DEF(68, DMA2_Stream5) // DMA2 Stream 5 -IRQ_DEF(69, DMA2_Stream6) // DMA2 Stream 6 -IRQ_DEF(70, DMA2_Stream7) // DMA2 Stream 7 -IRQ_DEF(71, USART6) // USART6 -IRQ_DEF(72, I2C3_EV) // I2C3 event -IRQ_DEF(73, I2C3_ER) // I2C3 error -IRQ_DEF(74, OTG_HS_EP1_OUT) // USB OTG HS End Point 1 Out -IRQ_DEF(75, OTG_HS_EP1_IN) // USB OTG HS End Point 1 In -IRQ_DEF(76, OTG_HS_WKUP) // USB OTG HS Wakeup through EXTI -IRQ_DEF(77, OTG_HS) // USB OTG HS -IRQ_DEF(78, DCMI) // DCMI -IRQ_DEF(79, CRYP) // CRYP crypto -IRQ_DEF(80, HASH_RNG) // Hash and Rng -IRQ_DEF(81, FPU) // FPU -IRQ_DEF(82, UART7) // UART7 -IRQ_DEF(83, UART8) // UART8 -IRQ_DEF(84, SPI4) // SPI4 -IRQ_DEF(85, SPI5) // SPI5 -IRQ_DEF(86, SPI6) // SPI6 -IRQ_DEF(87, SAI1) // SAI1 -IRQ_DEF(88, LTDC) // LTDC -IRQ_DEF(89, LTDC_ER) // LTDC_ER -IRQ_DEF(90, DMA2D) // DMA2D -IRQ_DEF(91, SAI2) // SAI2 -IRQ_DEF(92, QUADSPI) // Quad SPI -IRQ_DEF(93, LPTIM1) // LP TIM1 -IRQ_DEF(94, CEC) // HDMI-CEC -IRQ_DEF(95, I2C4_EV) // I2C4 Event -IRQ_DEF(96, I2C4_ER) // I2C4 Error -IRQ_DEF(97, SPDIF_RX) // SPDIF-RX -IRQ_DEF(99, DFSDM0) // DFSDM Filter1 -IRQ_DEF(100, DFSDM1) // DFSDM Filter2 -IRQ_DEF(101, DFSDM2) // DFSDM Filter3 -IRQ_DEF(102, DFSDM3) // DFSDM Filter4 -IRQ_DEF(103, SDMMC2) // SDMMC2 -IRQ_DEF(104, CAN3_TX) // CAN3 TX -IRQ_DEF(105, CAN3_RX0) // CAN3 RX0 -IRQ_DEF(106, CAN3_RX1) // CAN3 RX1 -IRQ_DEF(107, CAN3_SCE) // CAN3 SCE -IRQ_DEF(108, JPEG) // JPEG -IRQ_DEF(109, MDIOS) // MDIO Slave diff --git a/src/fw/kernel/Kconfig b/src/fw/kernel/Kconfig new file mode 100644 index 0000000000..06a4f7eff0 --- /dev/null +++ b/src/fw/kernel/Kconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config TASK_TIMER_POOL_SIZE + int "TaskTimer slab pool size" + default 192 + help + Number of TaskTimer slots reserved in .kernel_bss diff --git a/src/fw/kernel/core_dump.c b/src/fw/kernel/core_dump.c index 2e0ccce543..ec3b332d3c 100644 --- a/src/fw/kernel/core_dump.c +++ b/src/fw/kernel/core_dump.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /* * This module contains the core dump logic which writes the core dump to SPI flash. It operates @@ -25,6 +12,8 @@ * driver, etc. */ +#include + #include "kernel/core_dump.h" #include "kernel/core_dump_private.h" @@ -43,7 +32,7 @@ #include "mfg/mfg_serials.h" #include "pebbleos/chip_id.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" #include "system/bootbits.h" #include "system/passert.h" @@ -58,12 +47,11 @@ #include "util/size.h" #include "util/string.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include +#include + +#ifdef CONFIG_SOC_NRF52 +#include +#endif #include "FreeRTOS.h" /* FreeRTOS Kernal Prototypes/Constants. */ #include "task.h" /* FreeRTOS Task Prototypes/Constants. */ @@ -116,38 +104,19 @@ typedef struct { // Memory regions to dump static const MemoryRegion MEMORY_REGIONS_DUMP[] = { -#if MICRO_FAMILY_STM32F2 - { .start = (void *)SRAM_BASE, .length = COREDUMP_RAM_SIZE }, -#elif MICRO_FAMILY_NRF52840 || MICRO_FAMILY_SF32LB52 +#if CONFIG_SOC_NRF52 || CONFIG_SOC_SF32LB52 || CONFIG_QEMU { .start = (void *)0x20000000, .length = COREDUMP_RAM_SIZE }, -#else - { .start = (void *)SRAM1_BASE, .length = COREDUMP_RAM_SIZE }, -#endif -#if PLATFORM_SNOWY || PLATFORM_SPALDING - { .start = (void *)CCMDATARAM_BASE, .length = (uint32_t)__CCM_RAM_size__ }, -#endif -#if MICRO_FAMILY_STM32F7 - { .start = (void *)RAMDTCM_BASE, .length = (uint32_t)__DTCM_RAM_size__ }, -#endif -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - { .start = (void *)RCC, .length = sizeof(*RCC) }, #endif { .start = (void *)&NVIC->ISER, .length = sizeof(NVIC->ISER) }, // Enabled interrupts { .start = (void *)&NVIC->ISPR, .length = sizeof(NVIC->ISPR) }, // Pending interrupts { .start = (void *)&NVIC->IABR, .length = sizeof(NVIC->IABR) }, // Active interrupts -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - { .start = (void *)&NVIC->IP, .length = sizeof(NVIC->IP) }, // Interrupt priorities - { .start = (void *)RTC, .length = sizeof(*RTC) }, - { .start = (void*)DMA1_BASE, .length = 0xD0, .word_reads_only = true }, - { .start = (void*)DMA2_BASE, .length = 0xD0, .word_reads_only = true }, -#endif }; -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 -static struct { - RCC_TypeDef rcc; - SPI_TypeDef spi1; -} s_stash_data; +#if defined(CONFIG_SOC_SF32LB52) +// LCPU RAM is dumped last and only when its domain is up; see prv_dump_lcpu_ram(). +static const MemoryRegion LCPU_MEMORY_REGION = { + .start = (void *)COREDUMP_LCPU_RAM_START, .length = COREDUMP_LCPU_RAM_SIZE, +}; #endif // ------------------------------------------------------------------------------------------------- @@ -193,7 +162,7 @@ static void prv_debug_str(const char* msg) { // NOTE: We are explicitly avoiding use of vsniprintf and cohorts to reduce our stack // requirements static void prv_debug_str_str(const char* msg, const char* s) { -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE void *ctx = pulse_logging_log_sync_begin(LOG_LEVEL_ALWAYS, __FILE_NAME__, 0); pulse_logging_log_sync_append(ctx, msg); pulse_logging_log_sync_append(ctx, s); @@ -212,11 +181,18 @@ static void prv_debug_str_str(const char* msg, const char* s) { // ------------------------------------------------------------------------------------------------- // NOTE: We are explicitly avoiding use of vsniprintf and cohorts to reduce our stack // requirements -static void prv_debug_str_int(const char* msg, uint32_t i) { +static void prv_debug_str_int(const char* msg, uint32_t i, int base) { char buffer[12]; - itoa(i, buffer, sizeof(buffer)); -#if PULSE_EVERYWHERE + if (base == 16) { + buffer[0] = '0'; + buffer[1] = 'x'; + itoa(i, &buffer[2], base); + } else { + itoa(i, buffer, base); + } + +#ifdef CONFIG_PULSE_EVERYWHERE void *ctx = pulse_logging_log_sync_begin(LOG_LEVEL_ALWAYS, __FILE_NAME__, 0); pulse_logging_log_sync_append(ctx, msg); pulse_logging_log_sync_append(ctx, buffer); @@ -238,21 +214,11 @@ static NORETURN prv_reset(void) { // ----------------------------------------------------------------------------------------- void coredump_assert(int line) { - prv_debug_str_int("CD: assert - line ", line); + prv_debug_str_int("CD: assert - line ", line, 10); boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); prv_reset(); } -// ------------------------------------------------------------------------------------------------- -// Stash the flash status registers and peripheral clock state before the flash -// driver messes with them. -static void prv_stash_regions(void) { -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - memcpy(&s_stash_data.rcc, RCC, sizeof(RCC_TypeDef)); - memcpy(&s_stash_data.spi1, SPI1, sizeof(SPI_TypeDef)); -#endif -} - // ----------------------------------------------------------------------------------------------- // Return the start address of the flash region containing the core dump image. We write the core image to // different regions in flash to avoid premature burnout of any particular region. @@ -480,6 +446,18 @@ static void prv_write_memory_regions(const MemoryRegion *regions, unsigned int c } } +#if defined(CONFIG_SOC_SF32LB52) +// Wake the LCPU so its LPSYS RAM is reachable, letting BLE crashes (e.g. NimBLE +// host asserts) capture the controller RAM. HAL_HPAON_WakeCore busy-waits for +// the LCPU to ack; if it is fully powered down this blocks until the watchdog +// reboots us, costing only this dump. We reset right after, so the wake request +// is never balanced. +static void prv_dump_lcpu_ram(uint32_t flash_base) { + HAL_HPAON_WakeCore(CORE_ID_LCPU); + prv_write_memory_regions(&LCPU_MEMORY_REGION, 1, flash_base); +} +#endif + // Write the Core Dump Image Header // Returns number of bytes written @ flash_addr static uint32_t prv_write_image_header(uint32_t flash_addr, uint8_t core_number, @@ -601,8 +579,6 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) { // Feed the watchdog so that we don't get watchdog reset in the middle of dumping the core watchdog_feed(); - prv_stash_regions(); - // Init the flash and SPI bus s_use_cd_flash_driver = true; cd_flash_init(); @@ -614,14 +590,14 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) { if (!s_core_dump_is_forced && flash_base != CORE_DUMP_FLASH_INVALID_ADDR) { CoreDumpFlashRegionHeader region_hdr; CoreDumpImageHeader image_hdr; - prv_debug_str_int("CD: Checking: ", flash_base); + prv_debug_str_int("CD: Checking: ", flash_base, 16); prv_flash_read_bytes(®ion_hdr, flash_base, sizeof(region_hdr)); prv_flash_read_bytes(&image_hdr, flash_base + sizeof(region_hdr), sizeof(image_hdr)); if ((image_hdr.magic == CORE_DUMP_MAGIC) && region_hdr.unread && ((s_time_stamp - image_hdr.time_stamp) < CORE_DUMP_MIN_AGE_SECONDS)) { prv_debug_str("CD: Still fresh"); - #ifndef IS_BIGBOARD + #ifndef CONFIG_IS_BIGBOARD prv_reset(); #else prv_debug_str("CD: BigBoard, forcing dump"); @@ -631,7 +607,7 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) { // Get flash address to save new image to. This method also pre-erases the region for us. flash_base = prv_flash_start_address(true /*new*/); - prv_debug_str_int("CD: Saving to: ", flash_base); + prv_debug_str_int("CD: Saving to: ", flash_base, 16); // --------------------------------------------------------------------------------------- // Dump RAM and thread info into flash. We store data in flash using the following format: @@ -699,6 +675,11 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) { prvTaskInfoCallback(&task_info, NULL); } +#if defined(CONFIG_SOC_SF32LB52) + // Last: its read can hang/fault, so do it after the essential chunks are saved. + prv_dump_lcpu_ram(flash_base); +#endif + // Write out chunk terminator chunk_hdr.key = CORE_DUMP_CHUNK_KEY_TERMINATOR; chunk_hdr.size = 0; @@ -706,6 +687,8 @@ EXTERNALLY_VISIBLE void core_dump_handler_c(void) { sizeof(chunk_hdr)); // Reset! + uint32_t total_size = s_flash_addr - flash_base; + prv_debug_str_int("CD: size (bytes) ", total_size, 10); prv_debug_str("CD: completed"); prv_reset(); } @@ -792,7 +775,7 @@ bool core_dump_reserve_ble_slot(uint32_t *flash_base, uint32_t *max_size, } // -------------------------------------------------------------------------------------------------- -// Used by unit tests in to cause fw/apps/demo_apps/test_core_dump_app to encounter a bus fault during the core dump +// Used by unit tests in to cause fw/apps/demo/test_core_dump_app to encounter a bus fault during the core dump void core_dump_test_force_bus_fault(void) { s_test_force_bus_fault = true; } diff --git a/src/fw/kernel/core_dump.h b/src/fw/kernel/core_dump.h index cb533bca84..c045402a46 100644 --- a/src/fw/kernel/core_dump.h +++ b/src/fw/kernel/core_dump.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/core_dump_private.h b/src/fw/kernel/core_dump_private.h index 98f03bfd84..19368f7b44 100644 --- a/src/fw/kernel/core_dump_private.h +++ b/src/fw/kernel/core_dump_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -26,30 +13,34 @@ // Size of RAM // TODO: Do we have an equate for the total size of RAM somewhere else? -#if PLATFORM_CALCULUS || PLATFORM_ROBERT -#define COREDUMP_RAM_SIZE (384 * 1024) -#elif PLATFORM_SILK || PLATFORM_ASTERIX || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_GABBRO) +#define COREDUMP_RAM_SIZE (276 * 1024) +#elif defined(CONFIG_BOARD_QEMU_FLINT) #define COREDUMP_RAM_SIZE (256 * 1024) -#elif PLATFORM_SNOWY || PLATFORM_SPALDING -#define COREDUMP_RAM_SIZE (192 * 1024) -#elif PLATFORM_TINTIN -#define COREDUMP_RAM_SIZE (128 * 1024) +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) +// Main RAM on SF32LB52 extends to _heap_end (0x20045000). +// Thread stacks can be allocated above the 256KB mark. +#define COREDUMP_RAM_SIZE (276 * 1024) +#elif defined(CONFIG_BOARD_FAMILY_ASTERIX) +#define COREDUMP_RAM_SIZE (256 * 1024) +#endif + +// LCPU (BLE coprocessor) RAM, in the LPSYS domain outside main RAM. +#if defined(CONFIG_SOC_SF32LB52) +#define COREDUMP_LCPU_RAM_START (0x20400000) +#define COREDUMP_LCPU_RAM_SIZE (24 * 1024) #endif // Max number of core dump images we can fit in our allocated space -#define CORE_DUMP_FLASH_START FLASH_REGION_FIRMWARE_DEST_BEGIN -#define CORE_DUMP_FLASH_END FLASH_REGION_FIRMWARE_DEST_END +#define CORE_DUMP_FLASH_START FLASH_REGION_CD_BEGIN +#define CORE_DUMP_FLASH_END FLASH_REGION_CD_END #define CORE_DUMP_FLASH_SIZE (CORE_DUMP_FLASH_END - CORE_DUMP_FLASH_START) -#if defined(MICRO_FAMILY_STM32F2) -#define CORE_DUMP_MAX_IMAGES 3 -#elif defined(MICRO_FAMILY_STM32F4) -#define CORE_DUMP_MAX_IMAGES 2 -#elif defined(MICRO_FAMILY_STM32F7) -#define CORE_DUMP_MAX_IMAGES 3 -#elif defined(MICRO_FAMILY_NRF52840) -#define CORE_DUMP_MAX_IMAGES 2 -#elif defined(MICRO_FAMILY_SF32LB52) +#if defined(CONFIG_SOC_NRF52) #define CORE_DUMP_MAX_IMAGES 2 +#elif defined(CONFIG_SOC_SF32LB52) +#define CORE_DUMP_MAX_IMAGES 1 +#elif defined(CONFIG_QEMU) +#define CORE_DUMP_MAX_IMAGES 1 #else #error "Unsupported micro family" #endif diff --git a/src/fw/kernel/coredump_extra_regions.c b/src/fw/kernel/coredump_extra_regions.c new file mode 100644 index 0000000000..0f1fdbde78 --- /dev/null +++ b/src/fw/kernel/coredump_extra_regions.c @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "kernel/coredump_extra_regions.h" + +#include "kernel/core_dump_private.h" +#include "system/logging.h" + +static CoredumpExtraRegion s_regions[COREDUMP_EXTRA_REGIONS_MAX]; +static size_t s_count; + +void coredump_extra_regions_register(const char *name, const void *addr, size_t size) { + if (s_count >= COREDUMP_EXTRA_REGIONS_MAX) { + PBL_LOG_ERR("coredump_extra_regions: registry full, dropping %s", name); + return; + } + s_regions[s_count++] = (CoredumpExtraRegion){ + .name = name, + .addr = (uintptr_t)addr, + .size = size, + }; +} + +const CoredumpExtraRegion *coredump_extra_regions_get(size_t *out_count) { + *out_count = s_count; + return s_regions; +} + +// Forward declarations for per-driver registration helpers. Each driver +// provides this function (under its own platform guard); we call them all +// here under matching guards. +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) +void display_jdi_register_coredump_regions(void); +#endif + +void coredump_extra_regions_init(void) { +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + display_jdi_register_coredump_regions(); +#endif +#if defined(CONFIG_SOC_SF32LB52) + coredump_extra_regions_register("lcpu_ram", (const void *)COREDUMP_LCPU_RAM_START, + COREDUMP_LCPU_RAM_SIZE); +#endif +} diff --git a/src/fw/kernel/coredump_extra_regions.h b/src/fw/kernel/coredump_extra_regions.h new file mode 100644 index 0000000000..9313e37834 --- /dev/null +++ b/src/fw/kernel/coredump_extra_regions.h @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! Driver-registered RAM regions that the Memfault coredump reconstruction +//! forwards to the cloud in addition to thread stacks and log buffers. +//! +//! The Memfault port (third_party/memfault/port/src/memfault_pebble_coredump.c) +//! packages only a small slice of RAM for cloud upload by default. Drivers +//! that snapshot diagnostic state into a fixed BSS buffer ahead of a fault +//! (e.g. a peripheral register dump captured before PBL_CROAK) register that +//! buffer here so it rides along in the Memfault upload. +//! +//! Registration order: +//! 1. main.c calls coredump_extra_regions_init() early in boot. +//! 2. coredump_extra_regions_init() calls per-driver register helpers under +//! platform conditionals. +//! 3. main.c calls pbl_analytics_init(), which triggers Memfault coredump +//! reconstruction. The reconstruction iterates the registry via +//! coredump_extra_regions_get() and includes each region as a cached +//! memory region in the upload. +//! +//! Keep regions small — every byte adds to the BLE upload size. Sub-KB +//! buffers of bounded, fault-relevant state are the intended use. + +#pragma once + +#include +#include + +#define COREDUMP_EXTRA_REGIONS_MAX 8 + +typedef struct { + const char *name; + uintptr_t addr; + size_t size; +} CoredumpExtraRegion; + +//! Add a region to the coredump registry. Drops silently (logged) if the +//! registry is full — bump COREDUMP_EXTRA_REGIONS_MAX if you hit that. +void coredump_extra_regions_register(const char *name, const void *addr, size_t size); + +//! Iterate the registry. Out-parameter receives the number of registered +//! regions; the returned pointer is valid until the next register() call. +const CoredumpExtraRegion *coredump_extra_regions_get(size_t *out_count); + +//! Called once from main.c before pbl_analytics_init() to wire up all +//! per-driver registrations under platform conditionals. +void coredump_extra_regions_init(void); diff --git a/src/fw/kernel/event_loop.c b/src/fw/kernel/event_loop.c index 270a27f97e..afa1ee1a31 100644 --- a/src/fw/kernel/event_loop.c +++ b/src/fw/kernel/event_loop.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "event_loop.h" #include "events.h" @@ -50,39 +37,39 @@ #include "kernel/ui/modals/modal_manager.h" #include "kernel/util/factory_reset.h" #include "mcu/fpu.h" -#include "pebble_errors.h" #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" #include "process_management/app_run_state.h" #include "process_management/process_manager.h" #include "process_management/worker_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/battery/battery_state.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/compositor/compositor.h" -#include "services/common/cron.h" -#include "services/common/debounced_connection_service.h" -#include "services/common/ecompass.h" -#include "services/common/event_service.h" -#include "services/common/evented_timer.h" -#include "services/common/firmware_update.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/put_bytes/put_bytes.h" -#include "services/common/status_led.h" -#include "services/common/system_task.h" -#include "services/common/vibe_pattern.h" -#include "services/normal/accessory/accessory_manager.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/app_fetch_endpoint.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/stationary.h" -#include "services/normal/timeline/reminders.h" -#include "services/normal/wakeup.h" -#include "services/runlevel.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/cron.h" +#include "pbl/services/debounced_connection_service.h" +#include "pbl/services/ecompass.h" +#include "pbl/services/event_service.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/put_bytes/put_bytes.h" +#include "pbl/services/system_task.h" +#ifdef CONFIG_TOUCH +#include "pbl/services/touch/touch.h" +#endif +#include "pbl/services/vibe_pattern.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/app_fetch_endpoint.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/stationary.h" +#include "pbl/services/wakeup.h" +#include "pbl/services/runlevel.h" #include "shell/normal/app_idle_timeout.h" #include "shell/normal/watchface.h" #include "shell/prefs.h" @@ -97,18 +84,18 @@ #include "util/struct.h" #include "system/version.h" -#include - #include "FreeRTOS.h" #include "task.h" static const uint32_t FORCE_QUIT_HOLD_MS = 1500; static int s_back_hold_timer = TIMER_INVALID_ID; +#ifndef CONFIG_SHELL_SDK static const uint32_t BACK_QUICKPRESS_INTERVAL_TICKS = 300; static const int BACK_QUICKPRESS_COREDUMP_PRESSES = 10; static RtcTicks s_back_quickpress_last = 0; static int s_back_quickpress_count = 0; +#endif void launcher_task_add_callback(void (*callback)(void *data), void *data) { PebbleEvent event = { @@ -166,24 +153,20 @@ void launcher_cancel_force_quit(void) { new_timer_stop(s_back_hold_timer); } -// Export the count of how many times this has happened to Memfault. -uint32_t metric_firm_425_back_button_long_presses_cancelled = 0; - static void launcher_force_quit_app(void *data) { if (s_force_quit_was_cancelled) { // If you see this in logs after FIRM-556 (general system sluggishness) // is fixed, please file a bug! - PBL_LOG(LOG_LEVEL_ERROR, "NewTimer event fired for force quit, but the back button was released before we went to deal with it! Wow, we must have been really slow! Please file a bug!"); - metric_firm_425_back_button_long_presses_cancelled++; + PBL_LOG_ERR("NewTimer event fired for force quit, but the back button was released before we went to deal with it! Wow, we must have been really slow! Please file a bug!"); return; } if (low_power_is_active() || factory_reset_ongoing()) { - PBL_LOG(LOG_LEVEL_DEBUG, "Forcekill disabled due to low-power or factory-reset"); + PBL_LOG_DBG("Forcekill disabled due to low-power or factory-reset"); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Force killing app."); + PBL_LOG_DBG("Force killing app."); app_manager_force_quit_to_launcher(); } @@ -200,7 +183,7 @@ static void launcher_handle_button_event(PebbleEvent* e) { // trigger the backlight on any button down event if (e->type == PEBBLE_BUTTON_DOWN_EVENT) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BUTTON_PRESSED_COUNT, AnalyticsClient_System); + PBL_ANALYTICS_ADD(button_pressed_count, 1); if (button_id == BUTTON_ID_BACK && !watchface_running && process_metadata_get_run_level( @@ -211,7 +194,8 @@ static void launcher_handle_button_event(PebbleEvent* e) { 0 /*flags*/); PBL_ASSERTN(success); } - + +#ifndef CONFIG_SHELL_SDK // 10 quick-presses of the back button triggers a manual coredump, if // that feature is enabled in system settings. if (button_id == BUTTON_ID_BACK) { @@ -222,10 +206,11 @@ static void launcher_handle_button_event(PebbleEvent* e) { s_back_quickpress_last = now; s_back_quickpress_count++; if (s_back_quickpress_count >= BACK_QUICKPRESS_COREDUMP_PRESSES && shell_prefs_can_coredump_on_request()) { - PBL_LOG(LOG_LEVEL_INFO, "triggering core dump because you asked for it!"); + PBL_LOG_INFO("triggering core dump because you asked for it!"); core_dump_reset(true /* is_forced */); } } +#endif // !defined(CONFIG_SHELL_SDK) light_button_pressed(); } else if (e->type == PEBBLE_BUTTON_UP_EVENT) { @@ -274,12 +259,7 @@ static NOINLINE void prv_minimal_event_handler(PebbleEvent* e) { if (is_connected) { light_enable_interaction(); } else { - // Chances are the Pebble of our dear customer has been charging away - // from the phone and is disconnected because of that. Try reconnecting - // immediately upon disconnecting the charger: - bt_driver_reconnect_reset_interval(); - bt_driver_reconnect_try_now(false /*ignore_paused*/); - } + } #if STATIONARY_MODE stationary_handle_battery_connection_change_event(); #endif @@ -288,7 +268,7 @@ static NOINLINE void prv_minimal_event_handler(PebbleEvent* e) { case PEBBLE_BATTERY_STATE_CHANGE_EVENT: battery_monitor_handle_state_change_event(e->battery_state.new_state); -#if CAPABILITY_HAS_MAGNETOMETER +#ifdef CONFIG_MAG ecompass_handle_battery_state_change_event(e->battery_state.new_state); #endif return; @@ -298,11 +278,76 @@ static NOINLINE void prv_minimal_event_handler(PebbleEvent* e) { return; case PEBBLE_ACCEL_SHAKE_EVENT: - analytics_inc(ANALYTICS_DEVICE_METRIC_ACCEL_SHAKE_COUNT, AnalyticsClient_System); if (backlight_is_motion_enabled()) { - light_enable_interaction(); +#ifndef CONFIG_RECOVERY_FW + const bool dnd_suppresses_backlight = do_not_disturb_is_active() && + !alerts_preferences_dnd_get_motion_backlight(); + if (!dnd_suppresses_backlight) +#endif + { + light_enable_interaction(); + } + } + return; + +#ifdef CONFIG_TOUCH + case PEBBLE_TOUCH_EVENT: { + // When an app subscribes to touch events we ignore the global + // wake-on-touch preference and instead tie the backlight to the touch + // itself: forced on while a finger is down, then timed out after liftoff. + if (!touch_has_app_subscribers()) { + return; + } +#ifndef CONFIG_RECOVERY_FW + const bool dnd_suppresses_backlight = do_not_disturb_is_active() && + !alerts_preferences_dnd_get_touch_backlight(); + if (dnd_suppresses_backlight) { + return; + } +#endif + if (e->touch.event.type == TouchEvent_Touchdown) { + light_button_pressed(); + } else if (e->touch.event.type == TouchEvent_Liftoff) { + light_button_released(); } return; + } +#endif + + case PEBBLE_GESTURE_EVENT: { +#ifdef CONFIG_TOUCH + // While an app is subscribed the touch event handler drives the + // backlight, so skip gesture-based wake to avoid a redundant trigger + // (and to keep double-tap from waking when only single-tap is wanted). + if (touch_has_app_subscribers()) { + return; + } +#endif + bool wake_on_gesture = false; + switch (backlight_get_touch_wake()) { + case BacklightTouchWake_Tap: + wake_on_gesture = (e->gesture.event.type == GestureEvent_Tap || + e->gesture.event.type == GestureEvent_DoubleTap); + break; + case BacklightTouchWake_DoubleTap: + wake_on_gesture = (e->gesture.event.type == GestureEvent_DoubleTap); + break; + case BacklightTouchWake_Off: + default: + break; + } + if (wake_on_gesture) { +#ifndef CONFIG_RECOVERY_FW + const bool dnd_suppresses_backlight = do_not_disturb_is_active() && + !alerts_preferences_dnd_get_touch_backlight(); + if (!dnd_suppresses_backlight) +#endif + { + light_enable_interaction(); + } + } + return; + } case PEBBLE_PANIC_EVENT: launcher_panic(e->panic.error_code); @@ -310,9 +355,21 @@ static NOINLINE void prv_minimal_event_handler(PebbleEvent* e) { case PEBBLE_APP_LAUNCH_EVENT: if (!app_install_is_app_running(e->launch_app.id)) { + const LaunchConfigCommon common = + NULL_SAFE_FIELD_ACCESS(e->launch_app.data, common, (LaunchConfigCommon) {}); process_manager_launch_process(&(ProcessLaunchConfig) { .id = e->launch_app.id, - .common = NULL_SAFE_FIELD_ACCESS(e->launch_app.data, common, (LaunchConfigCommon) {}), + .common = common, +#ifdef CONFIG_SHELL_SDK + // Dev iteration: when the phone sends AppRunStateStart (typically + // `pebble install` over pypkjs), force-kill the current app instead + // of waiting up to 3s for graceful deinit. Misbehaving third-party + // watchfaces that don't promptly dequeue DEINIT (e.g. blocked inside + // a PEBBLE_SET_TIME_EVENT handler triggered by pypkjs's SetUTC right + // before install) otherwise stall the install until the dev manually + // toggles to the launcher. Real-user normal shell keeps graceful. + .forcefully = (common.reason == APP_LAUNCH_PHONE), +#endif }); } return; @@ -366,7 +423,7 @@ static NOINLINE void prv_extended_event_handler(PebbleEvent* e) { case PEBBLE_PUT_BYTES_EVENT: // TODO: inform the other things interested in put_bytes (apps?) firmware_update_pb_event_handler(&e->put_bytes); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW app_fetch_put_bytes_event_handler(&e->put_bytes); #endif return; @@ -376,14 +433,14 @@ static NOINLINE void prv_extended_event_handler(PebbleEvent* e) { return; case PEBBLE_ECOMPASS_SERVICE_EVENT: -#if CAPABILITY_HAS_MAGNETOMETER +#ifdef CONFIG_MAG ecompass_service_handle(); #endif return; case PEBBLE_SET_TIME_EVENT: { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW PebbleSetTimeEvent *set_time_info = &e->set_time_info; // The phone and watch time may be out of sync by a second or two (since @@ -395,13 +452,18 @@ static NOINLINE void prv_extended_event_handler(PebbleEvent* e) { set_time_info->dst_changed || ABS(set_time_info->utc_time_delta) > 15) { alarm_handle_clock_change(); - wakeup_handle_clock_change(); + wakeup_handle_significant_clock_change(); cron_service_handle_clock_change(set_time_info); } + // Always reschedule wakeup timers on any time change to prevent timers from + // firing early/late. Wakeup timers use tick-based relative scheduling, so even + // small time changes (or time resets from crashes) can cause significant skew. + // This is critical for app wakeup events that must fire at precise times. + wakeup_handle_clock_change(); + // TODO: evaluate if these need to change on every time update do_not_disturb_handle_clock_change(); - reminders_update_timer(); #endif return; } @@ -415,7 +477,7 @@ static NOINLINE void prv_extended_event_handler(PebbleEvent* e) { PebbleCommSessionEvent *comm_session_event = &e->bluetooth.comm_session_event; debounced_connection_service_handle_event(comm_session_event); put_bytes_handle_comm_session_event(comm_session_event); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW if (comm_session_event->is_system) { // tell the phone which app is running const Uuid *running_uuid = &app_manager_get_current_app_md()->uuid; @@ -468,16 +530,12 @@ static NOINLINE void prv_launcher_main_loop_init(void) { vibes_init(); battery_monitor_init(); evented_timer_init(); -#if CAPABILITY_HAS_MAGNETOMETER +#ifdef CONFIG_MAG ecompass_service_init(); #endif tick_timer_service_init(); debounced_connection_service_init(); event_service_system_init(); -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - accessory_manager_init(); -#endif - modal_manager_init(); shell_event_loop_init(); @@ -497,20 +555,20 @@ static NOINLINE void prv_launcher_main_loop_init(void) { light_button_pressed(); light_button_released(); -#ifndef RECOVERY_FW - i18n_set_resource(RESOURCE_ID_STRINGS); +#ifndef CONFIG_RECOVERY_FW + i18n_set_resource(shell_prefs_get_language_resource_id()); #endif app_manager_start_first_app(); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW // Launch the default worker. If any of the buttons are down, or we hit 2 strikes already, // skip this. This insures that we don't enter PRF for a bad worker. if (launcher_panic_get_current_error()) { - PBL_LOG(LOG_LEVEL_INFO, "Not launching worker because launcher panic"); + PBL_LOG_INFO("Not launching worker because launcher panic"); } else if (button_get_state_bits() != 0) { - PBL_LOG(LOG_LEVEL_INFO, "Not launching worker because button held"); + PBL_LOG_INFO("Not launching worker because button held"); } else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) { - PBL_LOG(LOG_LEVEL_INFO, "Not launching worker because of 2 strikes"); + PBL_LOG_INFO("Not launching worker because of 2 strikes"); } else { process_manager_launch_process(&(ProcessLaunchConfig) { .id = worker_manager_get_default_install_id(), @@ -524,7 +582,7 @@ static NOINLINE void prv_launcher_main_loop_init(void) { } void launcher_main_loop(void) { - PBL_LOG(LOG_LEVEL_ALWAYS, "Starting Launcher"); + PBL_LOG_ALWAYS("Starting Launcher"); prv_launcher_main_loop_init(); diff --git a/src/fw/kernel/event_loop.h b/src/fw/kernel/event_loop.h index a6232a853d..e269883d8e 100644 --- a/src/fw/kernel/event_loop.h +++ b/src/fw/kernel/event_loop.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/events.c b/src/fw/kernel/events.c index 23cb234bb1..b930f71a91 100644 --- a/src/fw/kernel/events.c +++ b/src/fw/kernel/events.c @@ -1,23 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "events.h" -#include "debug/setup.h" - #include "system/logging.h" #include "system/passert.h" #include "system/reset.h" @@ -25,7 +10,7 @@ #include "kernel/pbl_malloc.h" #include "os/tick.h" -#include "services/normal/app_outbox_service.h" +#include "pbl/services/app_outbox_service.h" #include "syscall/syscall.h" #include "FreeRTOS.h" @@ -58,14 +43,53 @@ static const int MAX_FROM_KERNEL_MAIN_EVENTS = 14; uint32_t s_current_event; +// Breakdown of kernel event queue entries by callback function pointer. +// Visible in coredumps to diagnose EventQueueFull reboots. +#define CALLBACK_TRACKER_MAX_ENTRIES 16 +typedef struct { + uintptr_t callback; + uint8_t count; +} CallbackTrackerEntry; +static CallbackTrackerEntry s_callback_tracker[CALLBACK_TRACKER_MAX_ENTRIES]; + +static void prv_callback_tracker_push(uintptr_t cb) { + // Try to find existing entry or an empty slot + int empty_slot = -1; + for (int i = 0; i < CALLBACK_TRACKER_MAX_ENTRIES; i++) { + if (s_callback_tracker[i].callback == cb) { + s_callback_tracker[i].count++; + return; + } + if (empty_slot < 0 && s_callback_tracker[i].count == 0) { + empty_slot = i; + } + } + if (empty_slot >= 0) { + s_callback_tracker[empty_slot].callback = cb; + s_callback_tracker[empty_slot].count = 1; + } + // If full, silently drop — the per-type count is still accurate +} + +static void prv_callback_tracker_pop(uintptr_t cb) { + for (int i = 0; i < CALLBACK_TRACKER_MAX_ENTRIES; i++) { + if (s_callback_tracker[i].callback == cb) { + if (--s_callback_tracker[i].count == 0) { + s_callback_tracker[i].callback = 0; + } + return; + } + } +} + #define EVENT_DEBUG 0 #if EVENT_DEBUG static void prv_queue_dump(QueueHandle_t queue) { PebbleEvent event; - PBL_LOG(LOG_LEVEL_DEBUG, "Dumping queue:"); + PBL_LOG_DBG("Dumping queue:"); while (xQueueReceive(queue, &event, 0) == pdTRUE) { - PBL_LOG(LOG_LEVEL_DEBUG, "Event type: %u", event.type); + PBL_LOG_DBG("Event type: %u", event.type); } for(;;); } @@ -76,7 +100,7 @@ void events_init(void) { // This assert is to make sure we don't accidentally bloat our PebbleEvent unecessarily. If you hit this // assert and you have a good reason for making the event bigger, feel free to relax the restriction. - //PBL_LOG(LOG_LEVEL_DEBUG, "PebbleEvent size is %u", sizeof(PebbleEvent)); + //PBL_LOG_DBG("PebbleEvent size is %u", sizeof(PebbleEvent)); // FIXME: _Static_assert(sizeof(PebbleEvent) <= 12, "You made the PebbleEvent bigger! It should be no more than 12"); @@ -126,8 +150,18 @@ static uint32_t prv_get_fancy_type_from_event(const PebbleEvent *event) { return event->type; } +static void prv_log_kernel_queue_contents(void) { + for (int i = 0; i < CALLBACK_TRACKER_MAX_ENTRIES; i++) { + if (s_callback_tracker[i].count > 0) { + PBL_LOG_ERR(" callback %p: %d", (void *)s_callback_tracker[i].callback, + s_callback_tracker[i].count); + } + } +} + static void prv_log_event_put_failure(const char *queue_name, uintptr_t saved_lr, const PebbleEvent *event) { - PBL_LOG(LOG_LEVEL_ERROR, "Error, %s queue full. Type %u", queue_name, event->type); + PBL_LOG_ERR("Error, %s queue full. Type %u", queue_name, event->type); + prv_log_kernel_queue_contents(); RebootReason reason = { .code = RebootReasonCode_EventQueueFull, @@ -148,20 +182,27 @@ static bool prv_event_put_isr(QueueHandle_t queue, const char* queue_type, uintp if (!xQueueSendToBackFromISR(queue, event, &should_context_switch)) { prv_log_event_put_failure(queue_type, saved_lr, event); -#ifdef NO_WATCHDOG - enable_mcu_debugging(); +#ifdef CONFIG_NO_WATCHDOG while (1); #endif reset_due_to_software_failure(); } + if (queue == s_kernel_event_queue && event->type == PEBBLE_CALLBACK_EVENT) { + prv_callback_tracker_push((uintptr_t)event->callback.callback); + } + return should_context_switch; } static bool prv_try_event_put(QueueHandle_t queue, PebbleEvent *event) { PBL_ASSERTN(queue); - return (xQueueSendToBack(queue, event, milliseconds_to_ticks(3000)) == pdTRUE); + bool success = (xQueueSendToBack(queue, event, milliseconds_to_ticks(3000)) == pdTRUE); + if (success && queue == s_kernel_event_queue && event->type == PEBBLE_CALLBACK_EVENT) { + prv_callback_tracker_push((uintptr_t)event->callback.callback); + } + return success; } static void prv_event_put(QueueHandle_t queue, @@ -184,6 +225,10 @@ static void prv_event_put(QueueHandle_t queue, reset_due_to_software_failure(); } + + if (queue == s_kernel_event_queue && event->type == PEBBLE_CALLBACK_EVENT) { + prv_callback_tracker_push((uintptr_t)event->callback.callback); + } } void event_deinit(PebbleEvent* event) { @@ -195,8 +240,8 @@ void event_deinit(PebbleEvent* event) { } void event_put(PebbleEvent* event) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; + // Caller LR; more reliable than reading lr register from a deeper helper. + uintptr_t saved_lr = (uintptr_t)__builtin_return_address(0); // If we are posting from the KernelMain task, use the dedicated s_from_kernel_event_queue queue for that // See comments above where s_from_kernel_event_queue is declared. if (pebble_task_get_current() == PebbleTask_KernelMain) { @@ -207,15 +252,13 @@ void event_put(PebbleEvent* event) { } bool event_put_isr(PebbleEvent* event) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; + uintptr_t saved_lr = (uintptr_t)__builtin_return_address(0); return prv_event_put_isr(s_kernel_event_queue, "kernel", saved_lr, event); } void event_put_from_process(PebbleTask task, PebbleEvent* event) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; + uintptr_t saved_lr = (uintptr_t)__builtin_return_address(0); QueueHandle_t queue = event_get_to_kernel_queue(task); prv_event_put(queue, "from app", saved_lr, event); @@ -250,7 +293,11 @@ bool event_take_timeout(PebbleEvent* event, int timeout_ms) { // Always service the kernel queue first. This prevents a misbehaving app from starving us. // If we're a little lazy servicing the app, the app will just block itself when the queue gets full. - if (xQueueReceive(s_kernel_event_queue, event, 0) == pdFALSE) { + if (xQueueReceive(s_kernel_event_queue, event, 0) != pdFALSE) { + if (event->type == PEBBLE_CALLBACK_EVENT) { + prv_callback_tracker_pop((uintptr_t)event->callback.callback); + } + } else { // Process the activated queue. This insures that events are handled in FIFO order from the app and worker // tasks. Note that sometimes the activated_queue can be the s_kernel_event_queue, even though // the above xQueueReceive returned no event @@ -312,13 +359,15 @@ void **event_get_buffer(PebbleEvent *event) { return (void **)(&event->bluetooth.le.gatt_client_service.info); } break; - +#ifdef CONFIG_MFG case PEBBLE_HRM_EVENT: - if (event->hrm.event_type == HRMEvent_Diagnostics) { - return (void **)(&event->hrm.debug); + if (event->hrm.event_type == HRMEvent_CTR) { + return (void **)(&event->hrm.ctr); + } else if (event->hrm.event_type == HRMEvent_Leakage) { + return (void **)(&event->hrm.leakage); } break; - +#endif case PEBBLE_APP_GLANCE_EVENT: return (void **)&event->app_glance.app_uuid; @@ -335,7 +384,7 @@ void **event_get_buffer(PebbleEvent *event) { void event_cleanup(PebbleEvent* event) { event_deinit(event); -#ifndef RELEASE +#ifndef CONFIG_RELEASE // Hopefully this will catch some use after free evil *event = (PebbleEvent){}; #endif @@ -394,7 +443,7 @@ BaseType_t event_queue_cleanup_and_reset(QueueHandle_t queue) { PBL_ASSERTN(xQueueReceive(queue, &event, 0) != pdFAIL); // event service does some book-keeping about events, notify it that we're dropping these. sys_event_service_cleanup(&event); -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) // app outbox service messages need to be cleaned up: app_outbox_service_cleanup_event(&event); #endif diff --git a/src/fw/kernel/events.h b/src/fw/kernel/events.h index f4d60c51c9..f44a1ade51 100644 --- a/src/fw/kernel/events.h +++ b/src/fw/kernel/events.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,33 +7,32 @@ #include "applib/app_launch_button.h" #include "applib/app_launch_reason.h" #include "applib/app_outbox.h" -#include "applib/app_smartstrap.h" #include "applib/bluetooth/ble_client.h" #include "applib/health_service.h" #include "applib/plugin_service.h" #include "applib/tick_timer_service.h" #include "applib/voice/dictation_session.h" #include "applib/ui/click.h" -#include "apps/system_apps/app_fetch_ui.h" +#include "apps/system/app_fetch_ui.h" #include "drivers/battery.h" #include "drivers/button_id.h" #include "process_management/app_install_types.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/bluetooth/bluetooth_ctl.h" -#include "services/common/comm_session/session_remote_os.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/common/hrm/hrm_manager.h" -#include "services/common/put_bytes/put_bytes.h" -#include "services/common/touch/touch_event.h" -#include "services/imu/units.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/music.h" -#include "services/normal/notifications/notifications.h" -#include "services/normal/voice/voice.h" -#include "services/normal/wakeup.h" -#include "services/normal/timeline/peek.h" -#include "services/normal/timeline/reminders.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/bluetooth/bluetooth_ctl.h" +#include "pbl/services/comm_session/session_remote_os.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/put_bytes/put_bytes.h" +#include "pbl/services/touch/touch_event.h" +#include "pbl/services/touch/gesture_event.h" +#include "pbl/services/imu/units.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/music.h" +#include "pbl/services/notifications/notifications.h" +#include "pbl/services/voice/voice.h" +#include "pbl/services/wakeup.h" +#include "pbl/services/timeline/peek.h" +#include "pbl/services/timeline/reminders.h" #include "kernel/pebble_tasks.h" #include "util/attributes.h" @@ -119,7 +105,6 @@ typedef enum { PEBBLE_REMINDER_EVENT, PEBBLE_CALENDAR_EVENT, PEBBLE_PANIC_EVENT, - PEBBLE_SMARTSTRAP_EVENT, //! Event sent back to the app to let them know the result of their sent message. PEBBLE_APP_OUTBOX_SENT_EVENT, //! A request from the app to the outbox service to handle a message. @@ -129,6 +114,7 @@ typedef enum { PEBBLE_APP_OUTBOX_MSG_EVENT, PEBBLE_HEALTH_SERVICE_EVENT, PEBBLE_TOUCH_EVENT, + PEBBLE_GESTURE_EVENT, PEBBLE_CAPABILITIES_CHANGED_EVENT, // Emitted when ANCS disconnects or is invalidated PEBBLE_ANCS_DISCONNECTED_EVENT, @@ -140,6 +126,9 @@ typedef enum { PEBBLE_APP_CACHE_EVENT, PEBBLE_ACTIVITY_EVENT, PEBBLE_WORKOUT_EVENT, + PEBBLE_PREF_CHANGE_EVENT, + PEBBLE_SPEAKER_EVENT, + PEBBLE_BACKLIGHT_EVENT, PEBBLE_NUM_EVENTS } PebbleEventType; @@ -494,6 +483,24 @@ typedef struct PACKED { // 7 bytes uint8_t key_len; } PebbleBlobDBEvent; +typedef struct PACKED { // 5 bytes + const char *key; //!< The preference key that changed (null-terminated) + uint8_t key_len; //!< Length of the key including null terminator +} PebblePrefChangeEvent; + +typedef enum { + SpeakerEventFinished = 0, +} SpeakerEventType; + +typedef struct PACKED { // 2 bytes + SpeakerEventType type:8; + uint8_t finish_reason; // SpeakerFinishReason +} PebbleSpeakerEvent; + +typedef struct PACKED { // 1 byte + bool is_on; +} PebbleBacklightEvent; + typedef enum { VoiceEventTypeSessionSetup, VoiceEventTypeSessionResult, @@ -576,41 +583,20 @@ typedef struct PACKED { // 1 byte bool is_event_ongoing; } PebbleCalendarEvent; -typedef enum { - SmartstrapConnectionEvent, - SmartstrapDataSentEvent, - SmartstrapDataReceivedEvent, - SmartstrapNotifyEvent -} SmartstrapEventType; - -typedef struct PACKED { // 9 bytes - SmartstrapEventType type:4; - SmartstrapProfile profile:4; - SmartstrapResult result:8; - uint16_t read_length; - union { - SmartstrapServiceId service_id; - SmartstrapAttribute *attribute; - }; -} PebbleSmartstrapEvent; - typedef struct PACKED { // 9 bytes int utc_time_delta; int gmt_offset_delta; bool dst_changed; } PebbleSetTimeEvent; -typedef enum PebbleTouchEventType { - PebbleTouchEvent_TouchesAvailable, - PebbleTouchEvent_TouchesCancelled, - PebbleTouchEvent_PalmDetected -} PebbleTouchEventType; - -typedef struct PACKED { // 2 bytes - PebbleTouchEventType type:8; - TouchIdx touch_idx; +typedef struct PACKED { // 5 bytes + TouchEvent event; } PebbleTouchEvent; +typedef struct PACKED { // 5 bytes + GestureEvent event; +} PebbleGestureEvent; + typedef struct PACKED { // 8 bytes PebbleProtocolCapabilities flags_diff; } PebbleCapabilitiesChangedEvent; @@ -635,18 +621,20 @@ typedef struct HRMHRVData { // 3 bytes HRMQuality quality:8; } HRMHRVData; -typedef struct HRMLEDData { // 4 bytes - uint16_t current_ua; - uint16_t tia; //!< Transimpendance Amplifier value. - //!< This is used with thresholds (provided by AMS) to verify the part is - //!< functioning within specification. +typedef struct HRMSpO2Data { // 2 bytes + uint8_t percent; + HRMQuality quality:8; +} HRMSpO2Data; -} HRMLEDData; +#ifdef CONFIG_MFG +typedef struct HRMCTRData { // 6 bytes + double ctr[6]; +} HRMCTRData; -typedef struct HRMDiagnosticsData { - HRMPPGData ppg_data; - HRMAccelData accel_data; -} HRMDiagnosticsData; +typedef struct HRMLeakageData { // 6 bytes + double leakage[6]; +} HRMLeakageData; +#endif typedef struct HRMSubscriptionExpiringData { // 4 bytes HRMSessionRef session_ref; @@ -655,8 +643,11 @@ typedef struct HRMSubscriptionExpiringData { // 4 bytes typedef enum HRMEventType { HRMEvent_BPM = 0, HRMEvent_HRV, - HRMEvent_LEDCurrent, - HRMEvent_Diagnostics, + HRMEvent_SpO2, +#ifdef CONFIG_MFG + HRMEvent_CTR, + HRMEvent_Leakage, +#endif HRMEvent_SubscriptionExpiring } HRMEventType; @@ -665,8 +656,11 @@ typedef struct PACKED PebbleHRMEvent { // 5 bytes union { HRMBPMData bpm; HRMHRVData hrv; - HRMLEDData led; - HRMDiagnosticsData *debug; + HRMSpO2Data spo2; +#ifdef CONFIG_MFG + HRMCTRData* ctr; + HRMLeakageData* leakage; +#endif HRMSubscriptionExpiringData expiring; }; } PebbleHRMEvent; @@ -803,8 +797,8 @@ typedef struct PACKED { PebbleReminderEvent reminder; PebbleCalendarEvent calendar; PebbleHealthEvent health_event; - PebbleSmartstrapEvent smartstrap; PebbleTouchEvent touch; + PebbleGestureEvent gesture; PebbleCapabilitiesChangedEvent capabilities; PebbleWeatherEvent weather; PebbleHRMEvent hrm; @@ -814,6 +808,9 @@ typedef struct PACKED { PebbleAppCacheEvent app_cache_event; PebbleActivityEvent activity_event; PebbleWorkoutEvent workout; + PebblePrefChangeEvent pref_change; + PebbleSpeakerEvent speaker; + PebbleBacklightEvent backlight; }; PebbleTaskBitset task_mask; // 1 == filter out, 0 == leave in // NOTE: we put this 8 bit field at the end so that we can pack this structure and still keep the diff --git a/src/fw/kernel/fault_handling.c b/src/fw/kernel/fault_handling.c index 98b2f5afe0..00eb301dd1 100644 --- a/src/fw/kernel/fault_handling.c +++ b/src/fw/kernel/fault_handling.c @@ -1,39 +1,25 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "kernel/core_dump.h" #include "kernel/logging_private.h" #include "process_management/process_manager.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" #include "applib/app_logging.h" -#include "applib/app_heap_analytics.h" #include "kernel/memory_layout.h" #include "mcu/privilege.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "syscall/syscall.h" #include "syscall/syscall_internal.h" #include "system/logging.h" +#include "system/reboot_reason.h" #include "syscall/syscall.h" #include -#define CMSIS_COMPATIBLE -#include +#include #include "FreeRTOS.h" #include "portmacro.h" @@ -59,6 +45,10 @@ void enable_fault_handlers(void) { SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk; SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk; + + SCB->CCR &= ~SCB_CCR_DIV_0_TRP_Msk; + __DSB(); + __ISB(); } @@ -70,7 +60,6 @@ typedef struct CrashInfo { uintptr_t pc; bool lr_known; bool pc_known; - bool is_rocky_app; } CrashInfo; CrashInfo make_crash_info_pc(uintptr_t pc) { @@ -112,13 +101,9 @@ static void prv_log_app_lr_and_pc_system_task(void *data) { APP_LOG(APP_LOG_LEVEL_ERROR, "%s fault! %s PC: %s LR: %s", process_string, buffer, pc_str, lr_str); - PBL_LOG(LOG_LEVEL_ERROR, "%s fault! %s", process_string, buffer); - PBL_LOG(LOG_LEVEL_ERROR, " --> PC: %s LR: %s", pc_str, lr_str); + PBL_LOG_ERR("%s fault! %s", process_string, buffer); + PBL_LOG_ERR(" --> PC: %s LR: %s", pc_str, lr_str); - analytics_event_app_crash(&crash_info->app_uuid, - (crash_info->pc_known) ? crash_info->pc : 0, - (crash_info->lr_known) ? crash_info->lr : 0, - crash_info->build_id, crash_info->is_rocky_app); } //! Converts an address from an absolute address in our memory space to one that's relative to the start @@ -137,7 +122,6 @@ static void setup_log_app_crash_info(CrashInfo crash_info) { const PebbleProcessMd *md = sys_process_manager_get_current_process_md(); s_current_app_crash_info.app_uuid = md->uuid; - s_current_app_crash_info.is_rocky_app = md->is_rocky_app; const uint8_t *build_id = process_metadata_get_build_id(md); if (build_id) { @@ -153,7 +137,12 @@ static void setup_log_app_crash_info(CrashInfo crash_info) { static NORETURN kernel_fault(RebootReasonCode reason_code, uint32_t lr) { RebootReason reason = { .code = reason_code, .extra = { .value = lr } }; reboot_reason_set(&reason); - reset_due_to_software_failure(); + if (reason_code == RebootReasonCode_Assert) { + prepare_for_software_failure(); + core_dump_reset(false /* is_forced */); + } else { + reset_due_to_software_failure(); + } } // TODO: Can we tell if it was the worker and not the app? @@ -179,7 +168,6 @@ NORETURN trigger_oom_fault(size_t bytes, uint32_t lr, Heap *heap_ptr) { reboot_reason_set(&reason); reset_due_to_software_failure(); } else { - app_heap_analytics_log_native_heap_oom_fault(bytes, heap_ptr); sys_app_fault(lr); } } @@ -230,7 +218,15 @@ DEFINE_SYSCALL(NORETURN, sys_app_fault, uint32_t stashed_lr) { static void hardware_fault_landing_zone(void) { prv_log_app_lr_and_pc_system_task(&s_current_app_crash_info); - prv_kill_user_process(0); + // Pass the original LR/PC down so a Worker hardfault gets a real address + // when it routes to kernel_fault. + uintptr_t lr = 0; + if (s_current_app_crash_info.lr_known && s_current_app_crash_info.lr != 0) { + lr = s_current_app_crash_info.lr; + } else if (s_current_app_crash_info.pc_known && s_current_app_crash_info.pc != 0) { + lr = s_current_app_crash_info.pc; + } + prv_kill_user_process(lr); } static void prv_return_to_landing_zone(uintptr_t stacked_pc, uintptr_t stacked_lr, unsigned int* stacked_args) { @@ -268,13 +264,17 @@ static void prv_return_to_landing_zone(uintptr_t stacked_pc, uintptr_t stacked_l // Now return to hardware_fault_landing_zone... } -static void attempt_handle_stack_overflow(unsigned int* stacked_args) { +static void attempt_handle_stack_overflow(unsigned int* stacked_args, uintptr_t fault_pc) { PebbleTask task = pebble_task_get_current(); - PBL_LOG_SYNC(LOG_LEVEL_ERROR, "Stack overflow [task: %s]", pebble_task_get_name(task)); + PBL_LOG_SYNC_ERR("Stack overflow [task: %s]", pebble_task_get_name(task)); if (mcu_state_is_thread_privileged()) { // We're hosed! We can't recover so just reboot everything. - RebootReason reason = { .code = RebootReasonCode_StackOverflow, .data8[0] = task }; + RebootReason reason = { + .code = RebootReasonCode_StackOverflow, + .data8[0] = task, + .extra = { .value = fault_pc }, + }; reboot_reason_set(&reason); reset_due_to_software_failure(); return; @@ -291,7 +291,7 @@ static void attempt_handle_generic_fault(unsigned int* stacked_args) { if (mcu_state_is_thread_privileged()) { // We're hosed! We can't recover so just reboot everything. - kernel_fault(RebootReasonCode_HardFault, stacked_lr); + kernel_fault(RebootReasonCode_HardFault, stacked_lr ? stacked_lr : stacked_pc); return; } @@ -348,13 +348,23 @@ static void mem_manage_handler_c(unsigned int* stacked_args, unsigned int lr) { // the cfsr. fault_handler_dump_cfsr(buffer); + // Read user PC before moving SP up; only safe if MMSTKERR is clear + // (frame was pushed). Otherwise fall back to MMFAR. + uintptr_t fault_pc = 0; + if (!(mmfsr & (1 << 4)) /* MMSTKERR */) { + fault_pc = (uintptr_t)stacked_args[6]; + } + if (fault_pc == 0) { + fault_pc = SCB->MMFAR; + } + stacked_args += 256; // Should be enough to get above the guard region and execute hardware_fault_landing_zone if (lr & 0x04) { __set_PSP((uint32_t)stacked_args); } else { __set_MSP((uint32_t)stacked_args); } - attempt_handle_stack_overflow(stacked_args); + attempt_handle_stack_overflow(stacked_args, fault_pc); } else { prv_save_debug_registers(stacked_args); @@ -403,8 +413,31 @@ void BusFault_Handler(void) { "b %0\n" :: "i" (busfault_handler_c)); } -static void usagefault_handler_c(unsigned int* stacked_args) { +// STKOF = bit 4 of UFSR = bit 20 of CFSR (ARMv8-M only, reads as 0 on CM3/CM4) +#ifndef SCB_CFSR_STKOF_Msk +#define SCB_CFSR_STKOF_Msk (1UL << 20) +#endif + +static void usagefault_handler_c(unsigned int* stacked_args, unsigned int lr) { PBL_LOG_FROM_FAULT_HANDLER("\r\n\r\n[UsageFault_Handler!]"); + + const uint32_t cfsr = SCB->CFSR; + + // STKOF: stack limit violation (PSPLIM/MSPLIM) + if (cfsr & SCB_CFSR_STKOF_Msk) { + SCB->CFSR = SCB_CFSR_STKOF_Msk; // Clear by writing 1 + + // No exception frame stacked on STKOF, so no PC available. + stacked_args += 256; // Back up SP to give landing zone room (see mem_manage_handler_c) + if (lr & 0x04) { + __set_PSP((uint32_t)stacked_args); + } else { + __set_MSP((uint32_t)stacked_args); + } + attempt_handle_stack_overflow(stacked_args, 0); + return; + } + prv_save_debug_registers(stacked_args); char buffer[80]; @@ -420,6 +453,7 @@ void UsageFault_Handler(void) { "ite eq\n" "mrseq r0, msp\n" "mrsne r0, psp\n" + "mov r1, lr\n" "b %0\n" :: "i" (usagefault_handler_c)); } diff --git a/src/fw/kernel/fault_handling.h b/src/fw/kernel/fault_handling.h index 46a19fd41e..dd9317f545 100644 --- a/src/fw/kernel/fault_handling.h +++ b/src/fw/kernel/fault_handling.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "system/reboot_reason.h" diff --git a/src/fw/kernel/kernel_applib_state.c b/src/fw/kernel/kernel_applib_state.c index 4bb3604f18..f5a2ba41e9 100644 --- a/src/fw/kernel/kernel_applib_state.c +++ b/src/fw/kernel/kernel_applib_state.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel_applib_state.h" @@ -146,6 +133,12 @@ TickTimerServiceState* kernel_applib_get_tick_timer_service_state(void) { return &s_tick_timer_service_state; } +// -------------------------------------------------------------------------------------------- +TouchServiceState* kernel_applib_get_touch_service_state(void) { + static TouchServiceState s_touch_service_state; + return &s_touch_service_state; +} + // ----------------------------------------------------------------------------------------------------------- ConnectionServiceState* kernel_applib_get_connection_service_state(void) { static ConnectionServiceState s_connection_service_state; diff --git a/src/fw/kernel/kernel_applib_state.h b/src/fw/kernel/kernel_applib_state.h index 353037b7c6..4ef1278846 100644 --- a/src/fw/kernel/kernel_applib_state.h +++ b/src/fw/kernel/kernel_applib_state.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,6 +7,7 @@ #include "kernel/logging_private.h" #include "applib/accel_service_private.h" #include "applib/tick_timer_service_private.h" +#include "applib/touch_service_private.h" #include "applib/compass_service_private.h" #include "applib/battery_state_service.h" #include "applib/battery_state_service_private.h" @@ -40,6 +28,8 @@ EventServiceInfo* kernel_applib_get_event_service_state(void); TickTimerServiceState *kernel_applib_get_tick_timer_service_state(void); +TouchServiceState *kernel_applib_get_touch_service_state(void); + ConnectionServiceState* kernel_applib_get_connection_service_state(void); BatteryStateServiceState* kernel_applib_get_battery_state_service_state(void); diff --git a/src/fw/kernel/kernel_heap.c b/src/fw/kernel/kernel_heap.c index f159a61c95..a0944df67c 100644 --- a/src/fw/kernel/kernel_heap.c +++ b/src/fw/kernel/kernel_heap.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/task_watchdog.h" #include "kernel_heap.h" #include "mcu/interrupts.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" +#include "util/heap.h" -#define CMSIS_COMPATIBLE -#include +#include static Heap s_kernel_heap; static bool s_interrupts_disabled_by_heap; @@ -58,13 +45,22 @@ void kernel_heap_init(void) { }); } -void analytics_external_collect_kernel_heap_stats(void) { +void pbl_analytics_external_collect_kernel_heap_stats(void) { uint32_t headroom = heap_get_minimum_headroom(&s_kernel_heap); + size_t total_size = heap_size(&s_kernel_heap); + uint32_t headroom_pct = (total_size > 0) ? (headroom * 100) / total_size : 0; + PBL_ANALYTICS_SET_UNSIGNED(memory_pct_max, headroom_pct); + + // Report the largest contiguous free block as a fraction of total heap. Peak + // usage alone (memory_pct_max above) hides fragmentation + unsigned int used, free_bytes, max_free; + heap_calc_totals(&s_kernel_heap, &used, &free_bytes, &max_free); + uint32_t largest_free_pct = (total_size > 0) ? ((uint32_t)max_free * 100) / total_size : 0; + PBL_ANALYTICS_SET_UNSIGNED(memory_largest_free_pct, largest_free_pct); + // Reset the high water mark so we can see if there are certain periods of time // where we really tax the heap s_kernel_heap.high_water_mark = s_kernel_heap.current_size; - analytics_set(ANALYTICS_DEVICE_METRIC_KERNEL_HEAP_MIN_HEADROOM_BYTES, headroom, - AnalyticsClient_System); } Heap* kernel_heap_get(void) { @@ -73,7 +69,7 @@ Heap* kernel_heap_get(void) { // Serial Commands /////////////////////////////////////////////////////////// -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION void command_dump_malloc_kernel(void) { heap_dump_malloc_instrumentation_to_dbgserial(&s_kernel_heap); } diff --git a/src/fw/kernel/kernel_heap.h b/src/fw/kernel/kernel_heap.h index 37430c39c2..16bbffc433 100644 --- a/src/fw/kernel/kernel_heap.h +++ b/src/fw/kernel/kernel_heap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/logging.c b/src/fw/kernel/logging.c index 576be415ca..fcb78144bb 100644 --- a/src/fw/kernel/logging.c +++ b/src/fw/kernel/logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_logging.h" @@ -54,7 +41,7 @@ static bool prv_check_serial_log_enabled(int level) { (level <= g_pbl_log_level)); } -#if !PULSE_EVERYWHERE +#ifndef CONFIG_PULSE_EVERYWHERE #define TIMESTAMP_BUFFER_SIZE 40 static void prv_log_timestamp(void) { // Enough stack space to use sprintfs? @@ -87,10 +74,14 @@ static void prv_log_serial( // Log the log level and the current task+privilege level { +#ifdef CONFIG_LOG_TASK_PREFIX unsigned char task_char = pebble_task_get_char(pebble_task_get_current()); if (mcu_state_is_privileged()) { task_char = toupper(task_char); } +#else + unsigned char task_char = '-'; +#endif char buffer[] = { pbl_log_get_level_char(log_level), ' ', task_char, ' ', 0 }; serial_console_write_log_message(buffer); @@ -118,14 +109,14 @@ static void prv_log_serial( // Append our newlines and our trailing null serial_console_write_log_message("\r\n"); } -#endif // PULSE_EVERYWHERE +#endif // CONFIG_PULSE_EVERYWHERE void kernel_pbl_log_serial(LogBinaryMessage *log_message, bool async) { if (!prv_check_serial_log_enabled(log_message->log_level)) { return; } -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE if (async) { pulse_logging_log(log_message->log_level, log_message->filename, htons(log_message->line_number), log_message->message); @@ -161,7 +152,7 @@ void kernel_pbl_log(LogBinaryMessage* log_message, bool async) { void kernel_pbl_log_from_fault_handler( const char *src_filename, uint16_t src_line_number, const char *message) { -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE pulse_logging_log_sync(LOG_LEVEL_ALWAYS, src_filename, src_line_number, message); #else diff --git a/src/fw/kernel/logging_private.h b/src/fw/kernel/logging_private.h index 3b13bfd662..6bbda6027d 100644 --- a/src/fw/kernel/logging_private.h +++ b/src/fw/kernel/logging_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/low_power.c b/src/fw/kernel/low_power.c index ff0c39ce4e..40aa4afa91 100644 --- a/src/fw/kernel/low_power.c +++ b/src/fw/kernel/low_power.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/low_power.h" -#include "apps/prf_apps/prf_low_power_app.h" +#include "apps/prf/low_power.h" #include "drivers/rtc.h" #include "kernel/event_loop.h" #include "kernel/ui/modals/modal_manager.h" @@ -24,9 +11,9 @@ #include "mfg/mfg_mode/mfg_factory_mode.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/system_task.h" +#include "pbl/services/runlevel.h" #include @@ -40,12 +27,11 @@ static void prv_low_power_launcher_task_callback(void *unused) { } if (s_low_power_active) { - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_WATCH_ONLY_TIME, - AnalyticsClient_System); + PBL_ANALYTICS_TIMER_START(low_power_time_ms); worker_manager_disable(); services_set_runlevel(RunLevel_LowPower); } else { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_WATCH_ONLY_TIME); + PBL_ANALYTICS_TIMER_STOP(low_power_time_ms); worker_manager_enable(); services_set_runlevel(RunLevel_Normal); } @@ -70,7 +56,7 @@ static void prv_low_power_transition(bool active) { NULL, 0 /*flags*/); // FIXME PBL-XXXXX: This should be in a shell/prf/battery_ui_fsm.c -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW if (active) { app_manager_launch_new_app(&(AppLaunchConfig) { .md = prf_low_power_app_get_info(), @@ -86,12 +72,12 @@ static void prv_low_power_transition(bool active) { } void low_power_standby(void) { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_WATCH_ONLY_TIME); + PBL_ANALYTICS_TIMER_STOP(low_power_time_ms); enter_standby(RebootReasonCode_LowBattery); } void low_power_enter(void) { -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW if (mfg_is_mfg_mode()) { return; } diff --git a/src/fw/kernel/low_power.h b/src/fw/kernel/low_power.h index 82bd3b728e..38d299dedf 100644 --- a/src/fw/kernel/low_power.h +++ b/src/fw/kernel/low_power.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/memory_layout.c b/src/fw/kernel/memory_layout.c index 2a51a148c7..4d3e645126 100644 --- a/src/fw/kernel/memory_layout.c +++ b/src/fw/kernel/memory_layout.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "memory_layout.h" @@ -22,15 +9,16 @@ #include "util/size.h" #include "util/string.h" -#include "kernel/mpu_regions.auto.h" - #include #include static const char* const MEMORY_REGION_NAMES[] = { - // FIXME(SF32LB52): system_bf0_ap.c uses now up to 4 regions as MPU is not fully implemented. -#ifdef MICRO_FAMILY_SF32LB52 + // Keep the four RESERVED entries in lockstep with MemoryRegion_Reserved* + // in memory_layout.h. SiFli's fifth region (mailbox) overlaps with the + // "UNPRIV_FLASH" slot at index 4 -- Pebble never programs that index on + // SF32LB52, so the cosmetic label is the only casualty. +#ifdef CONFIG_SOC_SF32LB52 "RESERVED0", "RESERVED1", "RESERVED2", @@ -47,6 +35,19 @@ static const char* const MEMORY_REGION_NAMES[] = { }; +static const char *prv_permissions_str(MpuPermissions p) { + switch (p) { + case MpuPermissions_NoAccess: return "NoAccess"; + case MpuPermissions_PrivRW: return "PrivRW"; + case MpuPermissions_PrivRW_UserRO: return "PrivRW_UserRO"; + case MpuPermissions_PrivRW_UserRW: return "PrivRW_UserRW"; + case MpuPermissions_PrivRO: return "PrivRO"; + case MpuPermissions_PrivRO_UserRO: return "PrivRO_UserRO"; + case MpuPermissionsCount: break; + } + return "?"; +} + void memory_layout_dump_mpu_regions_to_dbgserial(void) { char buffer[90]; @@ -60,18 +61,9 @@ void memory_layout_dump_mpu_regions_to_dbgserial(void) { PBL_LOG_FROM_FAULT_HANDLER_FMT( buffer, sizeof(buffer), - "%u < %-22s>: Addr %p Size 0x%08"PRIx32" Priv: %c%c User: %c%c", + "%u < %-22s>: Addr %p Size 0x%08"PRIx32" %s Perms: %s", i, MEMORY_REGION_NAMES[i], (void*) region.base_address, region.size, - region.priv_read ? 'R' : ' ', region.priv_write ? 'W' : ' ', - region.user_read ? 'R' : ' ', region.user_write ? 'W' : ' '); - -#ifndef MPU_ARMV8 - if (region.disabled_subregions) { - PBL_LOG_FROM_FAULT_HANDLER_FMT( - buffer, sizeof(buffer), - " Disabled Subregions: %02x", region.disabled_subregions); - } -#endif + region.executable ? "X" : "-", prv_permissions_str(region.permissions)); } } @@ -84,7 +76,9 @@ static const uint32_t __isr_stack_start__ = 0; static const uint32_t __stack_guard_size__ = 0; static const uint32_t __APP_RAM__ = 0; +static const uint32_t __APP_RAM_size__ = 0; static const uint32_t __WORKER_RAM__ = 0; +static const uint32_t __WORKER_RAM_size__ = 0; static const uint32_t __FLASH_start__ = 0; static const uint32_t __FLASH_size__ = 0; @@ -100,7 +94,9 @@ extern const uint32_t __isr_stack_start__[]; extern const uint32_t __stack_guard_size__[]; extern const uint32_t __APP_RAM__[]; +extern const uint32_t __APP_RAM_size__[]; extern const uint32_t __WORKER_RAM__[]; +extern const uint32_t __WORKER_RAM_size__[]; extern const uint32_t __FLASH_start__[]; extern const uint32_t __FLASH_size__[]; @@ -116,12 +112,10 @@ static const MpuRegion s_readonly_bss_region = { .base_address = (uint32_t) __unpriv_ro_bss_start__, .size = (uint32_t) __unpriv_ro_bss_size__, .cache_policy = MpuCachePolicy_WriteBackWriteAllocate, - .priv_read = true, - .priv_write = true, - .user_read = true, - .user_write = false + .permissions = MpuPermissions_PrivRW_UserRO, }; +#ifndef CONFIG_SOC_SF32LB52 // ISR stack guard static const MpuRegion s_isr_stack_guard_region = { .region_num = MemoryRegion_IsrStackGuard, @@ -129,11 +123,9 @@ static const MpuRegion s_isr_stack_guard_region = { .base_address = (uint32_t) __isr_stack_start__, .size = (uint32_t) __stack_guard_size__, .cache_policy = MpuCachePolicy_NotCacheable, - .priv_read = false, - .priv_write = false, - .user_read = false, - .user_write = false + .permissions = MpuPermissions_NoAccess, }; +#endif static const MpuRegion s_app_stack_guard_region = { .region_num = MemoryRegion_TaskStackGuard, @@ -141,10 +133,7 @@ static const MpuRegion s_app_stack_guard_region = { .base_address = (uint32_t) __APP_RAM__, .size = (uint32_t) __stack_guard_size__, .cache_policy = MpuCachePolicy_NotCacheable, - .priv_read = false, - .priv_write = false, - .user_read = false, - .user_write = false + .permissions = MpuPermissions_NoAccess, }; static const MpuRegion s_worker_stack_guard_region = { @@ -153,48 +142,41 @@ static const MpuRegion s_worker_stack_guard_region = { .base_address = (uint32_t) __WORKER_RAM__, .size = (uint32_t) __stack_guard_size__, .cache_policy = MpuCachePolicy_NotCacheable, - .priv_read = false, - .priv_write = false, - .user_read = false, - .user_write = false + .permissions = MpuPermissions_NoAccess, }; static const MpuRegion s_app_region = { .region_num = MemoryRegion_AppRAM, .enabled = true, - .base_address = MPU_REGION_APP_BASE_ADDRESS, - .size = MPU_REGION_APP_SIZE, -#ifndef MPU_ARMV8 - .disabled_subregions = MPU_REGION_APP_DISABLED_SUBREGIONS, -#endif + // App process .text is relocated into App RAM, so the App task needs + // to be able to execute from this region. + .executable = true, + .base_address = (uintptr_t) __APP_RAM__, + .size = (uint32_t) __APP_RAM_size__, .cache_policy = MpuCachePolicy_WriteBackWriteAllocate, - .priv_read = true, - .priv_write = true, + .permissions = MpuPermissions_PrivRW, }; static const MpuRegion s_worker_region = { .region_num = MemoryRegion_WorkerRAM, .enabled = true, - .base_address = MPU_REGION_WORKER_BASE_ADDRESS, - .size = MPU_REGION_WORKER_SIZE, -#ifndef MPU_ARMV8 - .disabled_subregions = MPU_REGION_WORKER_DISABLED_SUBREGIONS, -#endif + // Worker process .text is relocated into Worker RAM. + .executable = true, + .base_address = (uintptr_t) __WORKER_RAM__, + .size = (uint32_t) __WORKER_RAM_size__, .cache_policy = MpuCachePolicy_WriteBackWriteAllocate, - .priv_read = true, - .priv_write = true, + .permissions = MpuPermissions_PrivRW, }; static const MpuRegion s_microflash_region = { .region_num = MemoryRegion_Flash, .enabled = true, + // The firmware itself runs from flash. + .executable = true, .base_address = (uint32_t) __FLASH_start__, .size = (uint32_t) __FLASH_size__, .cache_policy = MpuCachePolicy_WriteThrough, - .priv_read = true, - .priv_write = false, - .user_read = true, - .user_write = false + .permissions = MpuPermissions_PrivRO_UserRO, }; static const MpuRegion s_kernel_main_stack_guard_region = { @@ -203,10 +185,7 @@ static const MpuRegion s_kernel_main_stack_guard_region = { .base_address = (uint32_t) __kernel_main_stack_start__, .size = (uint32_t) __stack_guard_size__, .cache_policy = MpuCachePolicy_NotCacheable, - .priv_read = false, - .priv_write = false, - .user_read = false, - .user_write = false + .permissions = MpuPermissions_NoAccess, }; static const MpuRegion s_kernel_bg_stack_guard_region = { @@ -215,25 +194,31 @@ static const MpuRegion s_kernel_bg_stack_guard_region = { .base_address = (uint32_t) __kernel_bg_stack_start__, .size = (uint32_t) __stack_guard_size__, .cache_policy = MpuCachePolicy_NotCacheable, - .priv_read = false, - .priv_write = false, - .user_read = false, - .user_write = false + .permissions = MpuPermissions_NoAccess, }; void memory_layout_setup_mpu(void) { // Flash parts... // Read only for executing code and loading data out of. -#ifndef MICRO_FAMILY_SF32LB52 // Unprivileged flash, by default anyone can read any part of flash. + // On SF32LB52 the SiFli HAL already programs a user-RO executable region + // covering __FLASH_start__..(__FLASH_start__ + __FLASH_size__ - 1) in + // SystemInit(), so we skip our own. +#ifndef CONFIG_SOC_SF32LB52 mpu_set_region(&s_microflash_region); +#endif // RAM parts // The background memory map only allows privileged access. We need to add aditional regions to // enable access to unprivileged code. mpu_set_region(&s_readonly_bss_region); + // ARMv8-M tracks per-task stack overflow via PSPLIM in the FreeRTOS CM33 + // port, so the dedicated ISR stack guard region (no-access) is redundant + // and the AP encoding can't even express "no access" precisely. Skip it + // on SF32LB52 to reclaim the slot. +#ifndef CONFIG_SOC_SF32LB52 mpu_set_region(&s_isr_stack_guard_region); #endif diff --git a/src/fw/kernel/memory_layout.h b/src/fw/kernel/memory_layout.h index 0e6f7884ac..a21c44e568 100644 --- a/src/fw/kernel/memory_layout.h +++ b/src/fw/kernel/memory_layout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,8 +10,15 @@ #define KERNEL_READONLY_DATA SECTION(".kernel_unpriv_ro_bss") enum MemoryRegionAssignments { - // FIXME(SF32LB52): system_bf0_ap.c uses now up to 4 regions as MPU is not fully implemented. -#ifdef MICRO_FAMILY_SF32LB52 + // SF32LB52: SiFli's system_bf0_ap.c programs MPU regions 0..4 in + // SystemInit() (flash, peripherals, .ramfunc, LPSYS RAM, HCPU<->LCPU + // mailbox). Reserve four slots here so the per-task configurable + // regions still land at indices 8..11 -- matching the FreeRTOS port's + // FREERTOS_FIRST_MPU_REGION=8. SiFli's fifth region (mailbox at index + // 4) shares its slot with the unused-on-SF32LB52 MemoryRegion_Flash + // value; Pebble never programs region 4, so SiFli's mailbox config + // stays intact. +#ifdef CONFIG_SOC_SF32LB52 MemoryRegion_Reserved0, MemoryRegion_Reserved1, MemoryRegion_Reserved2, diff --git a/src/fw/kernel/mpu_regions.template.h b/src/fw/kernel/mpu_regions.template.h deleted file mode 100644 index 407f94542a..0000000000 --- a/src/fw/kernel/mpu_regions.template.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define MPU_REGION_APP_BASE_ADDRESS @APP_BASE_ADDRESS@ -#define MPU_REGION_APP_SIZE @APP_SIZE@ -#define MPU_REGION_APP_DISABLED_SUBREGIONS @APP_DISABLED_SUBREGIONS@ - -#define MPU_REGION_WORKER_BASE_ADDRESS @WORKER_BASE_ADDRESS@ -#define MPU_REGION_WORKER_SIZE @WORKER_SIZE@ -#define MPU_REGION_WORKER_DISABLED_SUBREGIONS @WORKER_DISABLED_SUBREGIONS@ diff --git a/src/fw/kernel/panic.c b/src/fw/kernel/panic.c index dac4ce7b9f..b400f52efc 100644 --- a/src/fw/kernel/panic.c +++ b/src/fw/kernel/panic.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/panic.h" @@ -30,7 +17,7 @@ void launcher_panic(uint32_t error_code) { s_current_error = error_code; - PBL_LOG(LOG_LEVEL_ERROR, "!!!SAD WATCH 0x%"PRIX32" SAD WATCH!!!", error_code); + PBL_LOG_ERR("!!!SAD WATCH 0x%"PRIX32" SAD WATCH!!!", error_code); if (modal_manager_get_top_window()) { modal_manager_pop_all(); diff --git a/src/fw/kernel/panic.h b/src/fw/kernel/panic.h index 4e886f03f5..dd6daa785a 100644 --- a/src/fw/kernel/panic.h +++ b/src/fw/kernel/panic.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/pbl_malloc.c b/src/fw/kernel/pbl_malloc.c index fafca5dbeb..56e1d3bd4b 100644 --- a/src/fw/kernel/pbl_malloc.c +++ b/src/fw/kernel/pbl_malloc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pbl_malloc.h" @@ -46,7 +33,7 @@ static char* prv_strdup(Heap *heap, const char* s, uintptr_t lr) { // task_* functions that map to other heaps depending on the current task /////////////////////////////////////////////////////////// -#if defined(MALLOC_INSTRUMENTATION) +#if defined(CONFIG_MALLOC_INSTRUMENTATION) void *task_malloc_with_pc(size_t bytes, uintptr_t client_pc) { return heap_malloc(task_heap_get_for_current_task(), bytes, client_pc); } @@ -72,7 +59,7 @@ void *task_malloc_check(size_t bytes) { return mem; } -#if defined(MALLOC_INSTRUMENTATION) +#if defined(CONFIG_MALLOC_INSTRUMENTATION) void task_free_with_pc(void *ptr, uintptr_t client_pc) { heap_free(task_heap_get_for_current_task(), ptr, client_pc); } diff --git a/src/fw/kernel/pbl_malloc.h b/src/fw/kernel/pbl_malloc.h index fd4aef3a92..2dfb4ffb89 100644 --- a/src/fw/kernel/pbl_malloc.h +++ b/src/fw/kernel/pbl_malloc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -31,7 +18,7 @@ Heap *task_heap_get_for_current_task(void); // task_* functions map to app_* or kernel_* based on which task we're calling it on. void *task_malloc(size_t bytes); -#if defined(MALLOC_INSTRUMENTATION) +#if defined(CONFIG_MALLOC_INSTRUMENTATION) void *task_malloc_with_pc(size_t bytes, uintptr_t client_pc); #endif void *task_malloc_check(size_t bytes); @@ -41,7 +28,7 @@ void *task_zalloc_check(size_t size); void *task_calloc(size_t count, size_t size); void *task_calloc_check(size_t count, size_t size); void task_free(void *ptr); -#if defined(MALLOC_INSTRUMENTATION) +#if defined(CONFIG_MALLOC_INSTRUMENTATION) void task_free_with_pc(void *ptr, uintptr_t client_pc); #endif char* task_strdup(const char* s); diff --git a/src/fw/kernel/pebble_tasks.c b/src/fw/kernel/pebble_tasks.c index a0323be3f2..b26d645c39 100644 --- a/src/fw/kernel/pebble_tasks.c +++ b/src/fw/kernel/pebble_tasks.c @@ -1,27 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pebble_tasks.h" #include "kernel/memory_layout.h" +#include "kernel/pbl_malloc.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_metric.h" +#include "pbl/services/analytics/analytics.h" +#include "syscall/syscall_internal.h" #include "system/passert.h" #include "util/size.h" @@ -31,12 +19,44 @@ TaskHandle_t g_task_handles[NumPebbleTask] KERNEL_READONLY_DATA = { 0 }; +// Cycles consumed by tasks that have already been destroyed in each slot. +// Captured at unregister time so the analytics heartbeat can keep accounting +// for App/Worker activity across short-lived task instances. +static uint32_t s_dead_task_cycles[NumPebbleTask]; + static void prv_task_register(PebbleTask task, TaskHandle_t task_handle) { g_task_handles[task] = task_handle; } +static uint32_t prv_read_task_run_time(TaskHandle_t handle) { + UBaseType_t num_tasks = uxTaskGetNumberOfTasks(); + TaskStatus_t *statuses = kernel_malloc(num_tasks * sizeof(TaskStatus_t)); + if (!statuses) { + return 0; + } + UBaseType_t count = uxTaskGetSystemState(statuses, num_tasks, NULL); + uint32_t cycles = 0; + for (UBaseType_t i = 0; i < count; i++) { + if (statuses[i].xHandle == handle) { + cycles = statuses[i].ulRunTimeCounter; + break; + } + } + kernel_free(statuses); + return cycles; +} + void pebble_task_unregister(PebbleTask task) { + TaskHandle_t handle = g_task_handles[task]; + if (handle == NULL) { + return; + } + uint32_t cycles = prv_read_task_run_time(handle); + // Clear the handle before crediting the cycles: the collector reads + // s_dead_task_cycles before walking the task list, so this ordering + // ensures cycles are never seen in both buckets simultaneously. g_task_handles[task] = NULL; + s_dead_task_cycles[task] += cycles; } const char* pebble_task_get_name(PebbleTask task) { @@ -115,21 +135,82 @@ void pebble_task_suspend(PebbleTask task) { vTaskSuspend(g_task_handles[task]); } -void analytics_external_collect_stack_free(void) { - analytics_set(ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_MAIN, - prv_task_get_stack_free(PebbleTask_KernelMain), AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_BACKGROUND, - prv_task_get_stack_free(PebbleTask_KernelBackground), AnalyticsClient_System); - - analytics_set(ANALYTICS_DEVICE_METRIC_STACK_FREE_BLUETOPIA_BIG, - prv_task_get_stack_free(PebbleTask_BTHost), AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_STACK_FREE_BLUETOPIA_MEDIUM, - prv_task_get_stack_free(PebbleTask_BTController), AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_STACK_FREE_BLUETOPIA_SMALL, - prv_task_get_stack_free(PebbleTask_BTHCI), AnalyticsClient_System); - - analytics_set(ANALYTICS_DEVICE_METRIC_STACK_FREE_NEWTIMERS, - prv_task_get_stack_free(PebbleTask_NewTimers), AnalyticsClient_System); +void pbl_analytics_external_collect_stack_free(void) { + PBL_ANALYTICS_SET_UNSIGNED(stack_free_kernel_main_bytes, prv_task_get_stack_free(PebbleTask_KernelMain)); + PBL_ANALYTICS_SET_UNSIGNED(stack_free_kernel_background_bytes, prv_task_get_stack_free(PebbleTask_KernelBackground)); + PBL_ANALYTICS_SET_UNSIGNED(stack_free_newtimers_bytes, prv_task_get_stack_free(PebbleTask_NewTimers)); + PBL_ANALYTICS_SET_UNSIGNED(stack_free_app_syscall_bytes, syscall_app_stack_free_bytes()); + PBL_ANALYTICS_SET_UNSIGNED(stack_free_worker_syscall_bytes, syscall_worker_stack_free_bytes()); +} + +static const enum pbl_analytics_key s_task_cpu_pct_keys[NumPebbleTask] = { + [PebbleTask_KernelMain] = PBL_ANALYTICS_KEY(task_cpu_kernel_main_pct), + [PebbleTask_KernelBackground] = PBL_ANALYTICS_KEY(task_cpu_kernel_background_pct), + [PebbleTask_Worker] = PBL_ANALYTICS_KEY(task_cpu_worker_pct), + [PebbleTask_App] = PBL_ANALYTICS_KEY(task_cpu_app_pct), + [PebbleTask_BTHost] = PBL_ANALYTICS_KEY(task_cpu_bt_host_pct), + [PebbleTask_BTController] = PBL_ANALYTICS_KEY(task_cpu_bt_controller_pct), + [PebbleTask_BTHCI] = PBL_ANALYTICS_KEY(task_cpu_bt_hci_pct), + [PebbleTask_NewTimers] = PBL_ANALYTICS_KEY(task_cpu_new_timers_pct), + [PebbleTask_PULSE] = PBL_ANALYTICS_KEY(task_cpu_pulse_pct), +}; + +void pbl_analytics_external_collect_task_cpu_stats(void) { + static uint32_t s_prev_total_task_cycles[NumPebbleTask]; + static uint32_t s_prev_idle_run_time; + static uint32_t s_prev_total_run_time; + + // Snapshot dead-task cycles before walking the live task list. Combined with + // the (clear-handle, then update-accumulator) ordering in + // pebble_task_unregister(), this guarantees that cycles from a task dying + // mid-collection are never double-counted; in the worst case they show up + // one heartbeat late. + uint32_t dead_cycles[NumPebbleTask]; + for (int task = 0; task < NumPebbleTask; task++) { + dead_cycles[task] = s_dead_task_cycles[task]; + } + + UBaseType_t num_tasks = uxTaskGetNumberOfTasks(); + TaskStatus_t *statuses = kernel_malloc(num_tasks * sizeof(TaskStatus_t)); + if (!statuses) { + return; + } + + uint32_t total_run_time; + UBaseType_t count = uxTaskGetSystemState(statuses, num_tasks, &total_run_time); + + uint32_t delta_total = total_run_time - s_prev_total_run_time; + s_prev_total_run_time = total_run_time; + + TaskHandle_t idle_handle = xTaskGetIdleTaskHandle(); + uint32_t curr_task_run_time[NumPebbleTask] = {0}; + uint32_t curr_idle_run_time = 0; + for (UBaseType_t i = 0; i < count; i++) { + if (statuses[i].xHandle == idle_handle) { + curr_idle_run_time = statuses[i].ulRunTimeCounter; + continue; + } + PebbleTask task = pebble_task_get_task_for_handle(statuses[i].xHandle); + if (task < NumPebbleTask) { + curr_task_run_time[task] = statuses[i].ulRunTimeCounter; + } + } + + kernel_free(statuses); + + for (int task = 0; task < NumPebbleTask; task++) { + uint32_t total = dead_cycles[task] + curr_task_run_time[task]; + uint32_t delta = total - s_prev_total_task_cycles[task]; + s_prev_total_task_cycles[task] = total; + uint32_t pct = delta_total ? (uint32_t)(((uint64_t)delta * 10000U) / delta_total) : 0; + sys_pbl_analytics_set_unsigned(s_task_cpu_pct_keys[task], pct); + } + + uint32_t idle_delta = curr_idle_run_time - s_prev_idle_run_time; + s_prev_idle_run_time = curr_idle_run_time; + uint32_t idle_pct = + delta_total ? (uint32_t)(((uint64_t)idle_delta * 10000U) / delta_total) : 0; + PBL_ANALYTICS_SET_UNSIGNED(task_cpu_idle_pct, idle_pct); } QueueHandle_t pebble_task_get_to_queue(PebbleTask task) { @@ -187,6 +268,14 @@ void pebble_task_create(PebbleTask pebble_task, TaskParameters_t *task_params, } const MpuRegion *stack_guard_region = NULL; +#ifndef CONFIG_MPU_TYPE_ARMV8M + // Per-task stack overflow detection: on ARMv7-M we plant a no-access + // MPU region at the bottom of each task's stack. On ARMv8-M, the + // FreeRTOS CM33 port sets PSPLIM to the same address via portSetupTCB(), + // so the hardware stack-pointer-limit check catches overflows directly + // -- and the ARMv8-M MPU AP encoding can't actually express "no access" + // anyway (the closest is priv-RO, which is half a guard at best). + // Skip programming the redundant MPU region and reclaim the slot. switch (pebble_task) { case PebbleTask_App: stack_guard_region = memory_layout_get_app_stack_guard_region(); @@ -209,15 +298,13 @@ void pebble_task_create(PebbleTask pebble_task, TaskParameters_t *task_params, default: WTF; } +#endif const MpuRegion *region_ptrs[portNUM_CONFIGURABLE_REGIONS] = { - // FIXME(SF32LB52): Not supported on ARMv8 MPU yet -#ifndef MICRO_FAMILY_SF32LB52 &app_region, &worker_region, stack_guard_region, NULL -#endif }; mpu_set_task_configurable_regions(task_params->xRegions, region_ptrs); diff --git a/src/fw/kernel/pebble_tasks.h b/src/fw/kernel/pebble_tasks.h index 49c4838d54..ac59f8c6b0 100644 --- a/src/fw/kernel/pebble_tasks.h +++ b/src/fw/kernel/pebble_tasks.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/pulse_logging.c b/src/fw/kernel/pulse_logging.c index 93cf16480f..45ea21210f 100644 --- a/src/fw/kernel/pulse_logging.c +++ b/src/fw/kernel/pulse_logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pulse_logging.h" @@ -74,10 +61,14 @@ static size_t prv_serialize_log_header(MessageContents *contents, // Log the log level and the current task+privilege level contents->log_level_char = pbl_log_get_level_char(log_level); +#ifdef CONFIG_LOG_TASK_PREFIX contents->task_char = pebble_task_get_char(task); if (mcu_state_is_privileged()) { contents->task_char = toupper(contents->task_char); } +#else + contents->task_char = '-'; +#endif contents->time_ms = timestamp_ms; diff --git a/src/fw/kernel/pulse_logging.h b/src/fw/kernel/pulse_logging.h index 9964fe4231..88e8672392 100644 --- a/src/fw/kernel/pulse_logging.h +++ b/src/fw/kernel/pulse_logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/reset.c b/src/fw/kernel/reset.c index 0b0db95f18..3b23d5631d 100644 --- a/src/fw/kernel/reset.c +++ b/src/fw/kernel/reset.c @@ -1,25 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system/reset.h" -#define CMSIS_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - #include "board/board.h" #include "drivers/pmic.h" #include "system/bootbits.h" @@ -30,11 +13,17 @@ #include "drivers/flash.h" #include "system/reboot_reason.h" +#include + +#ifdef CONFIG_SOC_SF32LB52 +#include +#endif + #include "FreeRTOS.h" #include "task.h" -void system_reset_prepare(bool unsafe_reset) { - fw_prepare_for_reset(unsafe_reset); +void system_reset_prepare(void) { + fw_prepare_for_reset(); flash_stop(); } @@ -51,16 +40,16 @@ NORETURN system_reset(void) { // if we're in a critical section, interrupt or if the scheduler has been suspended if (!already_failed && !mcu_state_is_isr() && !portIN_CRITICAL() && (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)) { - system_reset_prepare(failure_occurred /* skip BT teardown if failure occured */); + system_reset_prepare(); reboot_reason_set_restarted_safely(); } // If a software failure occcured, do a core dump before resetting if (failure_occurred) { core_dump_reset(false /* don't force overwrite */); + } else { + system_hard_reset(); } - - system_hard_reset(); } void system_reset_callback(void *data) { @@ -72,7 +61,7 @@ NORETURN system_hard_reset(void) { // Don't do anything fancy here. We may be in a context where nothing works, not even // interrupts. Just reset us. -#if MICRO_FAMILY_SF32LB52 +#ifdef CONFIG_SOC_SF32LB52 HAL_PMU_Reboot(); #else NVIC_SystemReset(); diff --git a/src/fw/kernel/system_message.c b/src/fw/kernel/system_message.c index d8f3673439..904b497f2a 100644 --- a/src/fw/kernel/system_message.c +++ b/src/fw/kernel/system_message.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/system_message.h" @@ -20,13 +7,13 @@ #include "kernel/events.h" #include "kernel/util/sleep.h" #include "process_management/worker_manager.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" -#include "services/common/firmware_update.h" -#include "services/common/i18n/i18n.h" -#include "services/common/put_bytes/put_bytes.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/put_bytes/put_bytes.h" +#include "pbl/services/system_task.h" +#include "pbl/services/runlevel.h" #include "system/bootbits.h" #include "system/logging.h" #include "system/passert.h" @@ -48,11 +35,11 @@ void system_message_send(SystemMessageType type) { uint8_t buffer[2] = { 0x00, type }; CommSession *system_session = comm_session_get_system_session(); comm_session_send_data(system_session, ENDPOINT_ID, buffer, sizeof(buffer), COMM_SESSION_DEFAULT_TIMEOUT); - PBL_LOG(LOG_LEVEL_DEBUG, "Sending sysmsg: %u", type); + PBL_LOG_DBG("Sending sysmsg: %u", type); } static void prv_reset_kernel_bg_cb(void *unused) { - PBL_LOG(LOG_LEVEL_ALWAYS, "Rebooting to install firmware..."); + PBL_LOG_ALWAYS("Rebooting to install firmware..."); RebootReason reason = { RebootReasonCode_SoftwareUpdate, 0 }; reboot_reason_set(&reason); system_reset(); @@ -66,7 +53,7 @@ static void prv_handle_firmware_complete_msg(void) { uint32_t timeout = 3000; // Wait 3 seconds before rebooting so there is time to show the update complete screen - PBL_LOG(LOG_LEVEL_ALWAYS, "Delaying reset by 3s so the UI can update..."); + PBL_LOG_ALWAYS("Delaying reset by 3s so the UI can update..."); TimerID timer = new_timer_create(); // Don't bother cleaning up this timer, we're going to reset PBL_ASSERTN(timer != TIMER_INVALID_ID); new_timer_start(timer, timeout, prv_ui_update_reset_delay_timer_callback, NULL, 0); @@ -99,7 +86,7 @@ static void prv_handle_firmware_status_request(CommSession *session) { fw_status_resp.resource_crc = status.crc_of_bytes; } - PBL_LOG(LOG_LEVEL_INFO, "FW Status Resp: res %"PRIu32" : 0x%x fw %"PRIu32" : 0x%x", + PBL_LOG_INFO("FW Status Resp: res %"PRIu32" : 0x%x fw %"PRIu32" : 0x%x", fw_status_resp.resource_bytes_written, (int)fw_status_resp.resource_crc, fw_status_resp.firmware_bytes_written, (int)fw_status_resp.firmware_crc); @@ -112,11 +99,11 @@ void sys_msg_protocol_msg_callback(CommSession *session, const uint8_t* data, si SystemMessageType t = data[1]; - PBL_LOG(LOG_LEVEL_DEBUG, "Received sysmsg: %u", t); + PBL_LOG_DBG("Received sysmsg: %u", t); switch (t) { case SysMsgFirmwareAvailable_Deprecated: { - PBL_LOG(LOG_LEVEL_DEBUG, "Deprecated available message received."); + PBL_LOG_DBG("Deprecated available message received."); break; } @@ -134,7 +121,7 @@ void sys_msg_protocol_msg_callback(CommSession *session, const uint8_t* data, si SysMsgSmoothFirmwareStartPayload *payload = (SysMsgSmoothFirmwareStartPayload *)data; bytes_transferred = payload->bytes_already_transferred; total_size = bytes_transferred + payload->bytes_to_transfer; - PBL_LOG(LOG_LEVEL_INFO, "Starting FW update, %"PRIu32" of %"PRIu32" bytes already " + PBL_LOG_INFO("Starting FW update, %"PRIu32" of %"PRIu32" bytes already " "transferred", bytes_transferred, total_size); } @@ -187,7 +174,7 @@ void sys_msg_protocol_msg_callback(CommSession *session, const uint8_t* data, si } default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received, type is %u", data[1]); + PBL_LOG_ERR("Invalid message received, type is %u", data[1]); break; } } diff --git a/src/fw/kernel/system_message.h b/src/fw/kernel/system_message.h index 6905270e60..18ecbdd369 100644 --- a/src/fw/kernel/system_message.h +++ b/src/fw/kernel/system_message.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#include "services/common/firmware_update.h" +#include "pbl/services/firmware_update.h" #include diff --git a/src/fw/kernel/system_versions.c b/src/fw/kernel/system_versions.c index e3a364bff7..451dd5c6e9 100644 --- a/src/fw/kernel/system_versions.c +++ b/src/fw/kernel/system_versions.c @@ -1,34 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/prompt.h" -#include "drivers/fpc_pinstrap.h" #include "drivers/mcu.h" #include "drivers/pmic.h" #include "mfg/mfg_info.h" #include "mfg/mfg_serials.h" #include "resource/resource.h" #include "resource/system_resource.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/local_id.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/insights_settings.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/insights_settings.h" #include "shell/system_app_ids.auto.h" #include "system/bootbits.h" #include "system/logging.h" @@ -82,7 +68,7 @@ static void prv_fixup_firmware_metadata(FirmwareMetadata *fw_metadata) { static void prv_fixup_running_firmware_metadata(FirmwareMetadata *fw_metadata) { prv_fixup_firmware_metadata(fw_metadata); -#ifdef MANUFACTURING_FW +#ifdef CONFIG_MFG // Lie to the phone and force this to say we're not a MFG firmware. If we tell the phone app // that we're a MFG firmware it will get mad at us and try to update us out of this mode. We // want to stay in this mode to collect logs and core dumps at the factory. @@ -121,7 +107,7 @@ static void prv_send_watch_versions(CommSession *session) { strncpy(versions_msg.iso_locale, i18n_get_locale(), ISO_LOCALE_LENGTH - 1); versions_msg.iso_locale[ISO_LOCALE_LENGTH - 1] = '\0'; versions_msg.lang_version = htons(i18n_get_version()); - PBL_LOG(LOG_LEVEL_DEBUG, "Sending lang version: %d", versions_msg.lang_version); + PBL_LOG_DBG("Sending lang version: %d", versions_msg.lang_version); // Set the capabilities as zero, effectively saying that we don't support anything. versions_msg.capabilities.flags = 0; @@ -132,9 +118,7 @@ static void prv_send_watch_versions(CommSession *session) { versions_msg.capabilities.extended_notification_service = 1; versions_msg.capabilities.lang_pack_support = 1; versions_msg.capabilities.app_message_8k_support = 1; -#if CAPABILITY_HAS_HEALTH_TRACKING versions_msg.capabilities.activity_insights_support = 1; -#endif versions_msg.capabilities.voice_api_support = 1; versions_msg.capabilities.unread_coredump_support = 1; // FIXME: PBL-31627 In PRF, APP_ID_SEND_TEXT isn't defined - requiring the #ifdef and ternary op. @@ -151,20 +135,18 @@ static void prv_send_watch_versions(CommSession *session) { #endif #ifdef APP_ID_WORKOUT versions_msg.capabilities.workout_app_support = (APP_ID_WORKOUT != INSTALL_ID_INVALID) ? 1 : 0; -#endif -#if CAPABILITY_HAS_JAVASCRIPT - versions_msg.capabilities.javascript_bytecode_version_appended = 0x1; - versions_msg.javascript_bytecode_version = hton16(CAPABILITY_JAVASCRIPT_BYTECODE_VERSION); #endif versions_msg.capabilities.continue_fw_install_across_disconnect_support = 1; versions_msg.capabilities.smooth_fw_install_progress_support = 1; + versions_msg.capabilities.custom_vibe_pattern_support = 1; + versions_msg.capabilities.blob_db_version_support = 1; bt_local_id_copy_address(&versions_msg.device_address); versions_msg.system_resources_version = resource_get_system_version(); resource_version_to_network_endian(&versions_msg.system_resources_version); versions_msg.is_unfaithful = bt_persistent_storage_is_unfaithful(); -#if CAPABILITY_HAS_HEALTH_TRACKING && !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) versions_msg.activity_insights_version = hton16(activity_insights_settings_get_version()); #endif @@ -179,13 +161,13 @@ void system_version_protocol_msg_callback(CommSession *session, const uint8_t* d break; } default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received. First byte is %u", data[0]); + PBL_LOG_ERR("Invalid message received. First byte is %u", data[0]); break; } } void command_version_info(void) { -#ifdef MANUFACTURING_FW +#ifdef CONFIG_MFG prompt_send_response("MANUFACTURING FW"); #endif @@ -203,6 +185,14 @@ void command_version_info(void) { "%s FW:\n ts:%"PRIu32"\n tag:%s\n short:%s\n recov:%u\n platform:%u", label[i], fw_metadata.version_timestamp, fw_metadata.version_tag, fw_metadata.version_short, fw_metadata.is_recovery_firmware, fw_metadata.hw_platform); + + if ((i == 0) && fw_metadata.is_dual_slot) { + prompt_send_response_fmt(buffer, sizeof(buffer), " dual slot"); + if (!fw_metadata.is_recovery_firmware) { + prompt_send_response_fmt(buffer, sizeof(buffer), " current slot:%s", + fw_metadata.is_slot_0 ? "0" : "1"); + } + } } else { prompt_send_response_fmt(buffer, sizeof(buffer), "%s FW: no version info or lookup failed", label[i]); @@ -231,30 +221,11 @@ void command_version_info(void) { prompt_send_response_fmt(buffer, sizeof(buffer), "MCU Serial: %s", serial_str); } - prompt_send_response_fmt(buffer, sizeof(buffer), "Boot:%"PRIu32"\nHW:%s\nSN:%s", + prompt_send_response_fmt(buffer, sizeof(buffer), "Boot:0x%08"PRIx32"\nHW:%s\nSN:%s", boot_version_read(), hw_version, serial_number); ResourceVersion system_resources_version = resource_get_system_version(); prompt_send_response_fmt(buffer, sizeof(buffer), "System Resources:\n CRC:0x%"PRIx32"\n Valid:%s", system_resources_version.crc, bool_to_str(system_resource_is_valid())); - -#if CAPABILITY_HAS_PMIC - uint8_t chip_id; - uint8_t chip_revision; - uint8_t buck1_vset; - pmic_read_chip_info(&chip_id, &chip_revision, &buck1_vset); - prompt_send_response_fmt(buffer, - sizeof(buffer), - "PMIC Chip Id: 0x%"PRIx8" Chip Rev: 0x%"PRIx8" Buck1 VSET: 0x%"PRIx8, - chip_id, chip_revision, buck1_vset); -#endif // CAPABILITY_HAS_PMIC - -#ifdef PLATFORM_SNOWY - const uint8_t fpc_pinstrap = fpc_pinstrap_get_value(); - if (fpc_pinstrap != FPC_PINSTRAP_NOT_AVAILABLE) { - // + 1 since variants are documented as being between 1-9 instead of 0-based - prompt_send_response_fmt(buffer, sizeof(buffer), "FPC Variant: %"PRIu8, fpc_pinstrap + 1); - } -#endif } diff --git a/src/fw/kernel/task_timer.c b/src/fw/kernel/task_timer.c index 05195d62f6..bcbf362e91 100644 --- a/src/fw/kernel/task_timer.c +++ b/src/fw/kernel/task_timer.c @@ -1,24 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "task_timer.h" #include "task_timer_manager.h" #include "kernel/pebble_tasks.h" -#include "kernel/pbl_malloc.h" #include "os/mutex.h" #include "os/tick.h" #include "system/logging.h" @@ -59,6 +45,44 @@ typedef struct TaskTimer { bool defer_delete:1; } TaskTimer; +// TaskTimer slab pool. Avoids fragmenting the kernel heap with heavy +// create/delete churn. Exhaustion is fatal — size CONFIG_TASK_TIMER_POOL_SIZE +// above the observed peak. +static TaskTimer s_task_timer_pool[CONFIG_TASK_TIMER_POOL_SIZE]; +static TaskTimer *s_task_timer_pool_free_head; +static PebbleMutex *s_task_timer_pool_mutex; + +static void prv_pool_init(void) { + if (s_task_timer_pool_mutex) { + return; + } + for (size_t i = 0; i < CONFIG_TASK_TIMER_POOL_SIZE - 1; i++) { + s_task_timer_pool[i].list_node.next = &s_task_timer_pool[i + 1].list_node; + } + s_task_timer_pool[CONFIG_TASK_TIMER_POOL_SIZE - 1].list_node.next = NULL; + s_task_timer_pool_free_head = &s_task_timer_pool[0]; + s_task_timer_pool_mutex = mutex_create(); +} + +static TaskTimer *prv_timer_alloc(void) { + TaskTimer *timer = NULL; + mutex_lock(s_task_timer_pool_mutex); + if (s_task_timer_pool_free_head) { + timer = s_task_timer_pool_free_head; + s_task_timer_pool_free_head = (TaskTimer *)timer->list_node.next; + } + mutex_unlock(s_task_timer_pool_mutex); + PBL_ASSERTN(timer); + return timer; +} + +static void prv_timer_free(TaskTimer *timer) { + mutex_lock(s_task_timer_pool_mutex); + timer->list_node.next = (ListNode *)s_task_timer_pool_free_head; + s_task_timer_pool_free_head = timer; + mutex_unlock(s_task_timer_pool_mutex); +} + // ------------------------------------------------------------------------------------ // Comparator function for list_sorted_add // Returns the order in which (a, b) occurs @@ -100,10 +124,7 @@ static TaskTimer* prv_find_timer(TaskTimerManager *manager, TaskTimerID timer_id // --------------------------------------------------------------------------------------- // Create a new timer TaskTimerID task_timer_create(TaskTimerManager *manager) { - TaskTimer *timer = kernel_malloc(sizeof(TaskTimer)); - if (!timer) { - return TASK_TIMER_INVALID_ID; - } + TaskTimer *timer = prv_timer_alloc(); // Grab lock on timer structures, create a unique ID for this timer and put it into our idle // timers list @@ -264,12 +285,13 @@ void task_timer_delete(TaskTimerManager *manager, TaskTimerID timer_id) { PBL_ASSERTN(list_contains(manager->idle_timers, &timer->list_node)); list_remove(&timer->list_node, &manager->idle_timers /* &head */, NULL /* &tail */); mutex_unlock(manager->mutex); - kernel_free(timer); + prv_timer_free(timer); } } void task_timer_manager_init(TaskTimerManager *manager, SemaphoreHandle_t semaphore) { + prv_pool_init(); *manager = (TaskTimerManager) { .mutex = mutex_create(), // Initialize next id to be a number that's theoretically unique per-task @@ -313,7 +335,7 @@ TickType_t task_timer_manager_execute_expired_timers(TaskTimerManager *manager) if (next_timer->repeating && (int64_t)next_expiry_time < (int64_t)(current_time - next_timer->period_ticks - 5 * RTC_TICKS_HZ)) { - PBL_LOG(LOG_LEVEL_WARNING, "NT: Skipping some callbacks for %p because we fell behind", + PBL_LOG_WRN("NT: Skipping some callbacks for %p because we fell behind", next_timer->cb); } } else { @@ -355,7 +377,7 @@ TickType_t task_timer_manager_execute_expired_timers(TaskTimerManager *manager) list_remove(&next_timer->list_node, &manager->idle_timers /* &head */, NULL /* &tail */); mutex_unlock(manager->mutex); - kernel_free(next_timer); + prv_timer_free(next_timer); } else { mutex_unlock(manager->mutex); diff --git a/src/fw/kernel/task_timer.h b/src/fw/kernel/task_timer.h index 58e929ee2d..3a78355dc8 100644 --- a/src/fw/kernel/task_timer.h +++ b/src/fw/kernel/task_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/task_timer_manager.h b/src/fw/kernel/task_timer_manager.h index c50400802b..89bc634e1f 100644 --- a/src/fw/kernel/task_timer_manager.h +++ b/src/fw/kernel/task_timer_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/ui/kernel_ui.c b/src/fw/kernel/ui/kernel_ui.c index edafe74eb4..c689b04b5f 100644 --- a/src/fw/kernel/ui/kernel_ui.c +++ b/src/fw/kernel/ui/kernel_ui.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel_ui.h" #include "kernel/kernel_applib_state.h" #include "kernel/pebble_tasks.h" #include "process_state/app_state/app_state.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" #include "system/passert.h" #include "applib/graphics/graphics.h" diff --git a/src/fw/kernel/ui/kernel_ui.h b/src/fw/kernel/ui/kernel_ui.h index d034f999e5..30b2c721c1 100644 --- a/src/fw/kernel/ui/kernel_ui.h +++ b/src/fw/kernel/ui/kernel_ui.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" #include "applib/ui/content_indicator_private.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/timeline/timeline_actions.h" void kernel_ui_init(void); diff --git a/src/fw/kernel/ui/modals/modal_manager.c b/src/fw/kernel/ui/modals/modal_manager.c index f78c72ada9..f6c5f1b29b 100644 --- a/src/fw/kernel/ui/modals/modal_manager.c +++ b/src/fw/kernel/ui/modals/modal_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "modal_manager.h" @@ -31,7 +18,9 @@ #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_transitions.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/powermode_service.h" #include "shell/normal/app_idle_timeout.h" #include "shell/normal/watchface.h" #include "system/passert.h" @@ -68,6 +57,13 @@ static ModalProperty s_current_modal_properties = ModalPropertyDefault; // Used to decide the compositor transition after a modal is already removed from the stack static ModalPriority s_last_highest_modal_priority = ModalPriorityInvalid; +#if !defined(CONFIG_RECOVERY_FW) +static bool s_powermode_hp_requested; +static TimerID s_powermode_release_timer; + +#define POWERMODE_MODAL_RELEASE_DELAY_MS 5000 +#endif + // Private API //////////////////// static bool prv_has_visible_window(ModalContext *context, void *unused) { @@ -120,6 +116,10 @@ void modal_manager_init(void) { // honour that setting. click_manager_init(&s_modal_window_click_manager); + +#if !defined(CONFIG_RECOVERY_FW) + s_powermode_release_timer = new_timer_create(); +#endif } void modal_manager_set_min_priority(ModalPriority priority) { @@ -194,6 +194,35 @@ static const CompositorTransition *prv_get_compositor_transition(bool modal_is_d return is_top_discreet ? NULL : compositor_modal_transition_to_modal_get(modal_is_destination); } +#if !defined(CONFIG_RECOVERY_FW) +static void prv_powermode_release_cb(void *data) { + (void)data; + if (s_powermode_hp_requested) { + powermode_service_release_hp(); + s_powermode_hp_requested = false; + } +} +#endif + +static void prv_handle_app_to_modal_transition_powermode(void) { +#if !defined(CONFIG_RECOVERY_FW) + new_timer_stop(s_powermode_release_timer); + if (!s_powermode_hp_requested) { + powermode_service_request_hp(); + s_powermode_hp_requested = true; + } +#endif +} + +static void prv_handle_modal_to_app_transition_powermode(void) { +#if !defined(CONFIG_RECOVERY_FW) + if (s_powermode_hp_requested) { + new_timer_start(s_powermode_release_timer, POWERMODE_MODAL_RELEASE_DELAY_MS, + prv_powermode_release_cb, NULL, 0); + } +#endif +} + static void prv_handle_app_to_modal_transition_visible(void) { // The last event resulted in a modal window being pushed where we didn't have any before. // Start the animation! @@ -205,19 +234,19 @@ static void prv_handle_modal_to_app_transition_visible(void) { } static void prv_handle_app_to_modal_transition_hidden_and_unfocused(void) { -#if !RECOVERY_FW && !SHELL_SDK +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_SHELL_SDK) app_idle_timeout_pause(); #endif } static void prv_handle_modal_to_app_transition_hidden_and_unfocused(void) { -#if !RECOVERY_FW && !SHELL_SDK +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_SHELL_SDK) app_idle_timeout_resume(); #endif } static void prv_handle_app_to_modal_transition_focus(void) { -#if !RECOVERY_FW && (!SHELL_SDK || CAPABILITY_HAS_SDK_SHELL4) +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_SHELL_SDK) watchface_reset_click_manager(); #endif @@ -248,9 +277,11 @@ void modal_manager_event_loop_upkeep(void) { if (!was_modal_transitionable && is_modal_transitionable) { // We now have a window visible when we didn't have one before, start the transition. prv_handle_app_to_modal_transition_visible(); + prv_handle_app_to_modal_transition_powermode(); } else if (was_modal_transitionable && !is_modal_transitionable) { // This event resulted in our last visible modal window being popped, let's transition away. prv_handle_modal_to_app_transition_visible(); + prv_handle_modal_to_app_transition_powermode(); } const bool is_modal_unfocused = (update.properties & ModalProperty_Unfocused); diff --git a/src/fw/kernel/ui/modals/modal_manager.h b/src/fw/kernel/ui/modals/modal_manager.h index 01071e7f88..1f09fe2fac 100644 --- a/src/fw/kernel/ui/modals/modal_manager.h +++ b/src/fw/kernel/ui/modals/modal_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/ui/modals/modal_manager_private.h b/src/fw/kernel/ui/modals/modal_manager_private.h index e143239475..3d2c02c466 100644 --- a/src/fw/kernel/ui/modals/modal_manager_private.h +++ b/src/fw/kernel/ui/modals/modal_manager_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/ui/system_icons.h b/src/fw/kernel/ui/system_icons.h index c0572cbf01..bffd1e9fcd 100644 --- a/src/fw/kernel/ui/system_icons.h +++ b/src/fw/kernel/ui/system_icons.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/delay.c b/src/fw/kernel/util/delay.c index 505ee94c79..3b7992b16e 100644 --- a/src/fw/kernel/util/delay.c +++ b/src/fw/kernel/util/delay.c @@ -1,37 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "delay.h" #include "util/attributes.h" #include "util/units.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - -#include - -#if MICRO_FAMILY_NRF5 - +#ifdef CONFIG_SOC_NRF52 #include #include +#elif defined(CONFIG_SOC_SF32LB52) +#include +#endif + +#include +#ifdef CONFIG_SOC_NRF52 void NOINLINE delay_us(uint32_t us) { nrfx_coredep_delay_us(us); } @@ -39,8 +22,7 @@ void NOINLINE delay_us(uint32_t us) { void delay_init(void) { } -#elif MICRO_FAMILY_SF32LB52 - +#elif defined(CONFIG_SOC_SF32LB52) void NOINLINE delay_us(uint32_t us) { HAL_Delay_us(us); } @@ -48,68 +30,20 @@ void NOINLINE delay_us(uint32_t us) { void delay_init(void) { } -#else - -#if MICRO_FAMILY_STM32F7 -# define INSTRUCTIONS_PER_LOOP (1) -#elif MICRO_FAMILY_STM32F2 || MICRO_FAMILY_STM32F4 -# define INSTRUCTIONS_PER_LOOP (3) -#else -# error "Unexpected micro family" -#endif - -static uint32_t s_loops_per_us = 0; +#elif defined(CONFIG_QEMU) +#include void NOINLINE delay_us(uint32_t us) { - - uint32_t delay_loops = us * s_loops_per_us; - - __asm volatile ( - "spinloop: \n" - " subs %[delay_loops], #1 \n" - " bne spinloop \n" - : [delay_loops] "+r" (delay_loops) // read-write operand - : - : "cc" - ); + // Use DWT cycle counter for accurate delays + uint32_t cycles = us * (SystemCoreClock / 1000000); + uint32_t start = DWT->CYCCNT; + while ((DWT->CYCCNT - start) < cycles) {} } void delay_init(void) { - // The loop above consists of 2 instructions (output of arm-none-eabi-objdump -d - // delay.X.o): - // - // subs r0, #1 - // bne.w 4 - // - // Subtract consumes 1 cycle & the conditional branch consumes 1 + P (pipeline fill delay, - // 1-3 cycles) if the branch is taken, or 1 if not taken. For this situation, it appears that P=1 - // on the STM32F2/F4, so the loop takes 3 and 2 cycles respectively. The Cortex-M7 (STM32F7) has a - // superscalar dual-issue architecture which allows for 1-cycle loops (including the subtract). - // - // @ 64MHz 1 instructions is ~15.6ns which translates to the previously measured 47ns for one loop - // Thus we can derive that to get a duration of 1µs from an arbitrary clock frequency the count - // value needs to be: - // count = 1e-6 / (1/F * 3) where F is the core clock frequency - // - // An additional note is that delay_us is always executed from flash. The - // instruction cache in the cortex M3 & M4 cores is pretty good at saving - // instructions with simple branches which means we don't stall on flash - // reads after the first loop. Counterintuitively, executing from SRAM - // actually adds a extra delay cycle on instruction fetches and can be - // stalled if peripherals are doing DMAs. (See PBL-22265 for more details) - - RCC_ClocksTypeDef clocks; - RCC_GetClocksFreq(&clocks); - // Get the frequency in MHz so we don't overflow a uint32_t. - const uint32_t frequency_mhz = clocks.HCLK_Frequency / MHZ_TO_HZ(1); - const uint32_t clock_period_ps = PS_PER_US / frequency_mhz; - s_loops_per_us = PS_PER_US / (clock_period_ps * INSTRUCTIONS_PER_LOOP); - - // we always want to delay for more than the time specified so round up - // if the numbers don't divide evenly - if ((PS_PER_US % (clock_period_ps * INSTRUCTIONS_PER_LOOP)) != 0) { - s_loops_per_us += 1; - } + // Enable DWT cycle counter + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CYCCNT = 0; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; } - #endif diff --git a/src/fw/kernel/util/delay.h b/src/fw/kernel/util/delay.h index 71d0d373e6..5c324e18e7 100644 --- a/src/fw/kernel/util/delay.h +++ b/src/fw/kernel/util/delay.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/factory_reset.c b/src/fw/kernel/util/factory_reset.c index f01c525717..2508a33943 100644 --- a/src/fw/kernel/util/factory_reset.c +++ b/src/fw/kernel/util/factory_reset.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/util/factory_reset.h" @@ -22,28 +9,29 @@ #include "kernel/event_loop.h" #include "kernel/util/standby.h" #include "process_management/worker_manager.h" -#include "services/common/event_service.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" +#include "pbl/services/event_service.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/system_task.h" +#include "pbl/services/runlevel.h" #include "shell/normal/app_idle_timeout.h" #include "system/bootbits.h" +#include "system/firmware_storage.h" #include "system/logging.h" #include "system/reboot_reason.h" #include "system/reset.h" #include "kernel/util/sleep.h" -#if !RECOVERY_FW -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/timeline/event.h" +#if !defined(CONFIG_RECOVERY_FW) +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/timeline/event.h" #endif static bool s_in_factory_reset = false; static void prv_factory_reset_non_pfs_data() { - PBL_LOG_SYNC(LOG_LEVEL_INFO, "Factory resetting..."); + PBL_LOG_SYNC_INFO("Factory resetting..."); // This function can block the system task for a long time. // Prevent callbacks being added to the system task so it doesn't overflow. @@ -85,18 +73,26 @@ void factory_reset(bool should_shutdown) { prv_factory_reset_non_pfs_data(); - // TODO: wipe the registry on tintin? - filesystem_regions_erase_all(); +#if !defined(CONFIG_RECOVERY_FW) + // pfs_format() holds the PFS mutex across the erase, blocking concurrent + // writes from the App task that would otherwise survive into a freshly-erased region. + pfs_format(false /* write_erase_headers */); -#if !defined(RECOVERY_FW) // "First use" is part of the PRF image for Snowy boot_bit_set(BOOT_BIT_FORCE_PRF); +#ifdef CONFIG_PBLBOOT + // Invalidate both firmware slots so the bootloader doesn't boot into them + firmware_storage_invalidate_firmware_slot(0); + firmware_storage_invalidate_firmware_slot(1); +#endif +#else + filesystem_regions_erase_all(); #endif prv_factory_reset_post(should_shutdown); } -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) void close_db_files() { // Deinit the databases and any clients timeline_event_deinit(); @@ -118,7 +114,7 @@ void factory_reset_fast(void *unused) { prv_factory_reset_post(false /* should_shutdown */); } -#endif // !RECOVERY_FW +#endif // !defined(CONFIG_RECOVERY_FW) //! Used by the mfg flow to kick us out the MFG firmware and into the conumer PRF that's stored //! on the external flash. diff --git a/src/fw/kernel/util/factory_reset.h b/src/fw/kernel/util/factory_reset.h index 27b65d3edf..3d1fd4e157 100644 --- a/src/fw/kernel/util/factory_reset.h +++ b/src/fw/kernel/util/factory_reset.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/fw_reset.c b/src/fw/kernel/util/fw_reset.c index 2c5d7fd2ab..61dbb6bd51 100644 --- a/src/fw/kernel/util/fw_reset.c +++ b/src/fw/kernel/util/fw_reset.c @@ -1,27 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/util/fw_reset.h" +#include "apps/core/progress_ui.h" #include "console/pulse_internal.h" #include "kernel/core_dump.h" +#include "kernel/event_loop.h" +#include "kernel/ui/modals/modal_manager.h" #include "kernel/util/factory_reset.h" -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/runlevel.h" +#include "pbl/services/system_task.h" +#include "process_management/app_manager.h" #include "system/bootbits.h" #include "system/logging.h" #include "system/passert.h" @@ -48,6 +39,24 @@ typedef enum { ResetCmdIntoRecovery = 0xff, } ResetCmd; +static void prv_launch_factory_reset_app(void *unused) { + // factory_reset() holds the PFS mutex for the entire erase (~3 min via + // pfs_format()). launcher_block_popups() in prv_factory_reset_non_pfs_data + // only blocks new popups -- it does not dismiss ones already on screen. + // Pop them here so the user can't, say, press Select on an open notification + // and block KernelMain in pin_db_get -> pfs_seek on the held PFS mutex. + modal_manager_pop_all(); + + static const ProgressUIAppArgs s_factory_reset_args = { + .progress_source = PROGRESS_UI_SOURCE_FACTORY_RESET, + }; + app_manager_launch_new_app(&(AppLaunchConfig) { + .md = progress_ui_app_get_info(), + .common.args = &s_factory_reset_args, + .restart = true, + }); +} + void reset_protocol_msg_callback(CommSession *session, const uint8_t* data, unsigned int length) { PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground); @@ -55,39 +64,36 @@ void reset_protocol_msg_callback(CommSession *session, const uint8_t* data, unsi switch (cmd) { case ResetCmdNormal: - PBL_LOG(LOG_LEVEL_WARNING, "Rebooting"); + PBL_LOG_WRN("Rebooting"); system_reset(); break; case ResetCmdCoreDump: - PBL_LOG(LOG_LEVEL_INFO, "Core dump + Reboot triggered"); + PBL_LOG_INFO("Core dump + Reboot triggered"); core_dump_reset(true /* force overwrite any existing core dump */); break; case ResetCmdIntoRecovery: - PBL_LOG(LOG_LEVEL_WARNING, "Rebooting into PRF"); + PBL_LOG_WRN("Rebooting into PRF"); prv_reset_into_prf(); break; case ResetCmdFactoryReset: + launcher_task_add_callback(prv_launch_factory_reset_app, NULL); factory_reset(false /* should_shutdown */); break; default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid reset msg, data[0] %u", data[0]); + PBL_LOG_ERR("Invalid reset msg, data[0] %u", data[0]); break; } } -void fw_prepare_for_reset(bool unsafe_reset) { - if (!unsafe_reset) { - // Tear down Bluetooth, to avoid confusing the phone: - services_set_runlevel(RunLevel_BareMinimum); -#if PULSE_EVERYWHERE - pulse_end(); +void fw_prepare_for_reset(void) { + // Tear down Bluetooth, to avoid confusing the phone: + services_set_runlevel(RunLevel_BareMinimum); +#ifdef CONFIG_PULSE_EVERYWHERE + pulse_end(); #endif - } else { - pulse_prepare_to_crash(); - } } diff --git a/src/fw/kernel/util/fw_reset.h b/src/fw/kernel/util/fw_reset.h index 556a1a4664..cd8e3c91f5 100644 --- a/src/fw/kernel/util/fw_reset.h +++ b/src/fw/kernel/util/fw_reset.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -void fw_prepare_for_reset(bool skip_bt_teardown); +void fw_prepare_for_reset(void); void fw_reset_into_prf(void); diff --git a/src/fw/kernel/util/interval_timer.c b/src/fw/kernel/util/interval_timer.c index 498829c28b..0d18f13891 100644 --- a/src/fw/kernel/util/interval_timer.c +++ b/src/fw/kernel/util/interval_timer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "interval_timer.h" diff --git a/src/fw/kernel/util/interval_timer.h b/src/fw/kernel/util/interval_timer.h index 8e8d4c0664..201933d5c0 100644 --- a/src/fw/kernel/util/interval_timer.h +++ b/src/fw/kernel/util/interval_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/segment.c b/src/fw/kernel/util/segment.c index f6703db285..6dd5a7d5a0 100644 --- a/src/fw/kernel/util/segment.c +++ b/src/fw/kernel/util/segment.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "segment.h" diff --git a/src/fw/kernel/util/segment.h b/src/fw/kernel/util/segment.h index 8ba6fc1e70..3abd80f90e 100644 --- a/src/fw/kernel/util/segment.h +++ b/src/fw/kernel/util/segment.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Memory Segments //! diff --git a/src/fw/kernel/util/sleep.c b/src/fw/kernel/util/sleep.c index eec4aaf1b8..3b22b69125 100644 --- a/src/fw/kernel/util/sleep.c +++ b/src/fw/kernel/util/sleep.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "syscall/syscall.h" diff --git a/src/fw/kernel/util/sleep.h b/src/fw/kernel/util/sleep.h index 9069f9c4e3..c0c59ee07c 100644 --- a/src/fw/kernel/util/sleep.h +++ b/src/fw/kernel/util/sleep.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/stack_info.c b/src/fw/kernel/util/stack_info.c index 1ca5799a8b..61fca0e8d6 100644 --- a/src/fw/kernel/util/stack_info.c +++ b/src/fw/kernel/util/stack_info.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "FreeRTOS.h" #include "task.h" diff --git a/src/fw/kernel/util/stack_info.h b/src/fw/kernel/util/stack_info.h index 98c9e211d5..31250bb302 100644 --- a/src/fw/kernel/util/stack_info.h +++ b/src/fw/kernel/util/stack_info.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/standby.c b/src/fw/kernel/util/standby.c index 5776568264..e73656a55a 100644 --- a/src/fw/kernel/util/standby.c +++ b/src/fw/kernel/util/standby.c @@ -1,116 +1,41 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/util/standby.h" -#include "drivers/imu.h" -#include "drivers/rtc.h" -#include "drivers/flash.h" +#include "drivers/display/display.h" #include "drivers/pmic.h" #include "drivers/pwr.h" #include "drivers/periph_config.h" #include "system/bootbits.h" #include "system/logging.h" #include "system/reset.h" +#include "system/passert.h" -#include "drivers/display/display.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - -#include "FreeRTOS.h" -#include "task.h" - -//! If we don't have a PMIC entering standby is a little more complicated. -//! See platform/tintin/boot/src/standby.c. -//! We set a bootbit and reboot, and then the bootloader is responsible for really winding us down. -//! This is necessary because: -//! - When entering standby, a system reset is the only way to disable the IWDG -//! - When shutting down, it simplifies waiting on the charger to be removed, -//! and allows for us to handle other boot bits (eg. Force PRF) before powering down. -//! -//! @param boot_bit Boot bit to set. It should cause the bootloader to shut down or enter standby. -static NORETURN prv_enter_standby_non_pmic(BootBitValue boot_bit) { - // The I2C bus is not initialized in the bootloader. - // Put the Accelerometer into low power mode before resetting - imu_power_down(); - - boot_bit_set(boot_bit); - - PBL_LOG(LOG_LEVEL_ALWAYS, "Rebooting to enter Standby mode."); - reboot_reason_set_restarted_safely(); +#ifdef CONFIG_PMIC +static NORETURN prv_enter_standby(void) { + pmic_power_off(); - system_hard_reset(); + PBL_CROAK("We were not shut down!"); } - -#if CAPABILITY_HAS_PMIC -static NORETURN prv_enter_standby_pmic(void) { - reboot_reason_set_restarted_safely(); - -#if defined(TARGET_QEMU) -#if MICRO_FAMILY_STM32F7 - WTF; // Unsupported #else - // QEMU does not implement i2c devices, like the PMIC, yet. Let's turn off instead - // by going into standby mode using the power control of the STM32. We can't use - // prv_enter_standby_non_pmic() because PMIC based boards don't support that feature in their - // bootloader. - periph_config_enable(PWR, RCC_APB1Periph_PWR); - pwr_enable_wakeup(true); - PWR_EnterSTANDBYMode(); -#endif -#endif - - PBL_LOG(LOG_LEVEL_ALWAYS, "Using the PMIC to enter standby mode."); - pmic_power_off(); - PBL_CROAK("PMIC didn't shut us down!"); +static NORETURN prv_enter_standby(void) { + boot_bit_set(BOOT_BIT_STANDBY_MODE_REQUESTED); + system_hard_reset(); } #endif NORETURN enter_standby(RebootReasonCode reason) { - PBL_LOG(LOG_LEVEL_ALWAYS, "Preparing to enter standby mode."); + PBL_LOG_ALWAYS("Preparing to enter standby mode."); RebootReason reboot_reason = { reason, 0 }; reboot_reason_set(&reboot_reason); - // Wipe display display_clear(); display_set_enabled(false); - /* skip BT teardown if BT isn't working */ - system_reset_prepare(reason == RebootReasonCode_DialogBootFault); + system_reset_prepare(); + reboot_reason_set_restarted_safely(); -#if PLATFORM_SILK && RECOVERY_FW - // For Silk PRF & MFG firmwares, fully shutdown the watch using the bootloader. - // Always entering full shutdown in these two situations will guarantee a much - // better shelf-life, and ensure that watches are shipped in full shutdown mode. - // - // Request the bootloader to completely power down as the last thing it does, - // rather than jumping into the fw. The bootloader may spin on the charger - // connection status, as we cannot shutdown while the charger is plugged. - // Luckily, we never try to power down the watch while plugged when in PRF. - prv_enter_standby_non_pmic(BOOT_BIT_SHUTDOWN_REQUESTED); -#elif CAPABILITY_HAS_PMIC - prv_enter_standby_pmic(); -#else - // Request the bootloader to enter standby mode immediately after the system is reset. - prv_enter_standby_non_pmic(BOOT_BIT_STANDBY_MODE_REQUESTED); -#endif + prv_enter_standby(); } diff --git a/src/fw/kernel/util/standby.h b/src/fw/kernel/util/standby.h index ff71ca3c22..55eab3ad8f 100644 --- a/src/fw/kernel/util/standby.h +++ b/src/fw/kernel/util/standby.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/stop.c b/src/fw/kernel/util/stop.c index bccfbc6cea..b69044fb8f 100644 --- a/src/fw/kernel/util/stop.c +++ b/src/fw/kernel/util/stop.c @@ -1,47 +1,28 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/dbgserial.h" #include "console/dbgserial_input.h" #include "drivers/flash.h" #include "drivers/periph_config.h" #include "drivers/rtc.h" -#include "drivers/lptim_systick.h" #include "drivers/task_watchdog.h" #include "os/tick.h" #include "kernel/util/stop.h" #include "kernel/util/wfi.h" #include "mcu/interrupts.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" #include "system/passert.h" #include "console/dbgserial_input.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include +#include #include #include static int s_num_items_disallowing_stop_mode = 0; -#ifdef PBL_NOSLEEP +#ifdef CONFIG_NOSLEEP static bool s_sleep_mode_allowed = false; #else static bool s_sleep_mode_allowed = true; @@ -57,7 +38,7 @@ typedef struct { // they are read and modified by multiple threads static InhibitorTickProfile s_inhibitor_profile[InhibitorNumItems]; -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 void enter_stop_mode(void) { dbgserial_enable_rx_exti(); dbgserial_disable_rx_dma_before_stop(); @@ -76,163 +57,8 @@ void enter_stop_mode(void) { dbgserial_enable_rx_dma_after_stop(); } -#elif MICRO_FAMILY_SF32LB52 -static uint32_t s_iser_bak[16]; -static void clear_interrupt_setting(void) -{ - uint32_t i; - for (i = 0; i < 16; i++) - { - s_iser_bak[i] = NVIC->ISER[i]; - NVIC->ICER[i] = 0xFFFFFFFF; - __DSB(); - __ISB(); - } -} - -static void restore_interrupt_setting(void) -{ - uint32_t i; - for (i = 0; i < 16; i++) - { - __COMPILER_BARRIER(); - NVIC->ISER[i] = s_iser_bak[i]; - __COMPILER_BARRIER(); - } -} - -// Refer from sdk sifli_deep_handler function +#elif defined(CONFIG_QEMU) void enter_stop_mode(void) { - uint32_t dll1_freq; - uint32_t dll2_freq; - int clk_src; - - lptim_systick_pause(); - - clear_interrupt_setting(); - - /* Wait flash cache idle */ - HAL_Delay_us(100); - - NVIC_EnableIRQ(AON_IRQn); - - // Switch system clock to HRC48, backup current clock source - clk_src = HAL_RCC_HCPU_GetClockSrc(RCC_CLK_MOD_SYS); - HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_SYS, RCC_SYSCLK_HRC48); - dll1_freq = HAL_RCC_HCPU_GetDLL1Freq(); - dll2_freq = HAL_RCC_HCPU_GetDLL2Freq(); - - HAL_RCC_HCPU_DisableDLL1(); - HAL_RCC_HCPU_DisableDLL2(); - - HAL_HPAON_CLEAR_HP_ACTIVE(); - HAL_HPAON_SET_POWER_MODE(AON_PMR_DEEP_SLEEP); - - __WFI(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - __NOP(); - - HAL_HPAON_SET_HP_ACTIVE(); - HAL_HPAON_CLEAR_POWER_MODE(); - - // Wait for HXT48 to be ready - if (dll1_freq != 0) { - while (0 == (hwp_hpsys_aon->ACR & HPSYS_AON_ACR_HXT48_RDY)) { - __NOP(); - } - } - - // Switch back to original clock source - HAL_RCC_HCPU_EnableDLL1(dll1_freq); - HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_SYS, clk_src); - HAL_RCC_HCPU_EnableDLL2(dll2_freq); - HAL_Delay_us(0); - - restore_interrupt_setting(); - - lptim_systick_resume(); -} -#else /* STM32 */ -void enter_stop_mode(void) { - // enable the interrupt on the debug RX line so that we can use the serial - // console even when we are in stop mode. - dbgserial_enable_rx_exti(); - - flash_power_down_for_stop_mode(); - - // Turn on the power control peripheral so that we can put the regulator into low-power mode - periph_config_enable(PWR, RCC_APB1Periph_PWR); - - if (mcu_state_are_interrupts_enabled()) { - // If INTs aren't disabled here, we would wind up servicing INTs - // immediately after the WFI (while running at the wrong clock speed) which - // can confuse peripherals in subtle ways - WTF; - } - - // Enter stop mode. - //PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); - // We don't use ^^ the above function because of a silicon bug which - // causes the processor to skip some instructions upon wake from STOP - // in certain sitations. See the STM32F20x and STM32F21x Errata sheet - // section 2.1.3 "Debugging Stop mode with WFE entry", or the erratum - // of the same name in section 2.1.2 of the STM32F42x and STM32F43x - // Errata sheet, for (misleading) details. - // http://www.st.com/web/en/resource/technical/document/errata_sheet/DM00027213.pdf - // http://www.st.com/web/en/resource/technical/document/errata_sheet/DM00068628.pdf - - // Configure the PWR peripheral to put us in low-power STOP mode when - // the processor enters deepsleep. -#if defined(MICRO_FAMILY_STM32F7) - uint32_t temp = PWR->CR1; - temp &= ~PWR_CR1_PDDS; - temp |= PWR_CR1_LPDS; - PWR->CR1 = temp; -#else - uint32_t temp = PWR->CR; - temp &= ~PWR_CR_PDDS; - temp |= PWR_CR_LPDS; -#if STM32F412xG - // STM32F412xG suports a new "low-power regulator low voltage in deep sleep" mode. - temp |= PWR_CR_LPLVDS; -#endif - PWR->CR = temp; -#endif - - // Configure the processor core to enter deepsleep mode when we - // execute a WFI or WFE instruction. - SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; - - // Go stop now. - __DSB(); // Drain any pending memory writes before entering sleep. - do_wfi(); // Wait for Interrupt (enter sleep mode). Work around F2/F4 errata. - __ISB(); // Let the pipeline catch up (force the WFI to activate before moving on). - - // Tell the processor not to emter deepsleep mode for future WFIs. - SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; - - // Stop mode will change our system clock to the HSI. Move it back to the PLL. - - // Enable the PLL and wait until it's ready - RCC_PLLCmd(ENABLE); - while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {} - - // Select PLL as system clock source and wait until it's being used - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); - while (RCC_GetSYSCLKSource() != 0x08) {} - - // No longer need the power control peripheral - periph_config_disable(PWR, RCC_APB1Periph_PWR); - - flash_power_up_after_stop_mode(); } #endif @@ -264,7 +90,7 @@ void stop_mode_enable( StopModeInhibitor inhibitor ) { } bool stop_mode_is_allowed(void) { -#if PBL_NOSTOP +#ifdef CONFIG_NOSTOP return false; #else return s_num_items_disallowing_stop_mode == 0; @@ -276,44 +102,12 @@ void sleep_mode_enable(bool enable) { } bool sleep_mode_is_allowed(void) { -#ifdef PBL_NOSLEEP +#ifdef CONFIG_NOSLEEP return false; #endif return s_sleep_mode_allowed; } -static RtcTicks prv_get_nostop_ticks(StopModeInhibitor inhibitor, RtcTicks now_ticks) { - RtcTicks total_ticks = s_inhibitor_profile[inhibitor].total_ticks_while_disabled; - if (s_inhibitor_profile[inhibitor].active_count != 0) { - total_ticks += (now_ticks - s_inhibitor_profile[inhibitor].ticks_when_stop_mode_disabled); - } - return total_ticks; -} - -static void prv_collect(AnalyticsMetric metric, StopModeInhibitor inhibitor, RtcTicks now_ticks) { - // operating on 64 bit values so the load/stores will _not_ be atomic - portENTER_CRITICAL(); - RtcTicks ticks = prv_get_nostop_ticks(inhibitor, now_ticks); - s_inhibitor_profile[inhibitor].total_ticks_while_disabled = 0; - portEXIT_CRITICAL(); - analytics_set(metric, ticks_to_milliseconds(ticks), AnalyticsClient_System); -} - -void analytics_external_collect_stop_inhibitor_stats(RtcTicks now_ticks) { - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_MAIN_TIME, InhibitorMain, now_ticks); - // We don't care about the serial console nostop time, it should always - // be zero on watches in the field anyway. (InhibitorDbgSerial skipped) - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BUTTON_TIME, InhibitorButton, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BLUETOOTH_TIME, InhibitorBluetooth, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_DISPLAY_TIME, InhibitorDisplay, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BACKLIGHT_TIME, InhibitorBacklight, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_COMM_TIME, InhibitorCommMode, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_FLASH_TIME, InhibitorFlash, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_I2C1_TIME, InhibitorI2C1, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_ACCESSORY, InhibitorAccessory, now_ticks); - prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_MIC, InhibitorMic, now_ticks); - // TODO PBL-37941: Add analytics for InhibitorDMA -} void command_scheduler_force_active(void) { sleep_mode_enable(false); diff --git a/src/fw/kernel/util/stop.h b/src/fw/kernel/util/stop.h index 65a3112960..3ce4706350 100644 --- a/src/fw/kernel/util/stop.h +++ b/src/fw/kernel/util/stop.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -30,13 +17,14 @@ typedef enum { InhibitorI2C1, InhibitorI2C2, InhibitorMic, - InhibitorAccessory, InhibitorVibes, InhibitorCompositor, InhibitorI2C3, InhibitorI2C4, InhibitorBluetoothWatchdog, InhibitorPWM, + InhibitorAudio, + InhibitorUARTRX, InhibitorNumItems } StopModeInhibitor; diff --git a/src/fw/kernel/util/task_init.c b/src/fw/kernel/util/task_init.c index b0860d86cc..11a96e3189 100644 --- a/src/fw/kernel/util/task_init.c +++ b/src/fw/kernel/util/task_init.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "task_init.h" diff --git a/src/fw/kernel/util/task_init.h b/src/fw/kernel/util/task_init.h index c7800a1d89..ddf690f511 100644 --- a/src/fw/kernel/util/task_init.h +++ b/src/fw/kernel/util/task_init.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/kernel/util/task_telemetry.c b/src/fw/kernel/util/task_telemetry.c index 3fe26bfbc8..f8d84d4268 100644 --- a/src/fw/kernel/util/task_telemetry.c +++ b/src/fw/kernel/util/task_telemetry.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/prompt.h" #include "kernel/pbl_malloc.h" diff --git a/src/fw/kernel/util/wfi.c b/src/fw/kernel/util/wfi.c index f94acff659..e4d684d677 100644 --- a/src/fw/kernel/util/wfi.c +++ b/src/fw/kernel/util/wfi.c @@ -1,24 +1,19 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/attributes.h" #include "wfi.h" void NOINLINE NAKED_FUNC do_wfi(void) { +#ifdef CONFIG_QEMU + // QEMU Cortex-M33 doesn't properly wake from WFI when PRIMASK=1. + // Use WFE which yields to QEMU's scheduler and wakes on events. + __asm volatile ( + "wfe \n" + "bx lr \n" + ); +#else // Work around a very strange bug in the STM32F where, upon waking from // STOP or SLEEP mode, the processor begins acting strangely depending on the // contents of the bytes following the "bx lr" instruction. @@ -33,4 +28,5 @@ void NOINLINE NAKED_FUNC do_wfi(void) { "nop \n" "nop \n" ); +#endif } diff --git a/src/fw/kernel/util/wfi.h b/src/fw/kernel/util/wfi.h index 6f4b818aaa..bbb8c502c9 100644 --- a/src/fw/kernel/util/wfi.h +++ b/src/fw/kernel/util/wfi.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/libgcc_override.c b/src/fw/libgcc_override.c index f59f6321fa..a41d0c2db3 100644 --- a/src/fw/libgcc_override.c +++ b/src/fw/libgcc_override.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/attributes.h" diff --git a/src/fw/libos_platform.c b/src/fw/libos_platform.c index 9ddebdeaef..2d20ef8260 100644 --- a/src/fw/libos_platform.c +++ b/src/fw/libos_platform.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/pbl_malloc.h" #include "system/logging.h" diff --git a/src/fw/main.c b/src/fw/main.c index 3594d9c4ca..3d196046a2 100644 --- a/src/fw/main.c +++ b/src/fw/main.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include @@ -31,32 +18,32 @@ #include "drivers/flash.h" #include "drivers/debounced_button.h" -#include "drivers/accessory.h" +#include "drivers/accel.h" #include "drivers/ambient_light.h" #include "drivers/backlight.h" #include "drivers/battery.h" #include "drivers/display/display.h" #include "drivers/gpio.h" #include "drivers/hrm.h" -#include "drivers/imu.h" -#include "drivers/led_controller.h" +#include "drivers/mag.h" #include "drivers/mic.h" #include "drivers/otp.h" #include "drivers/pmic.h" +#include "drivers/pressure.h" #include "drivers/pwr.h" #include "drivers/spi.h" -#include "drivers/system_flash.h" #include "drivers/task_watchdog.h" #include "drivers/temperature.h" #include "drivers/touch/touch_sensor.h" #include "drivers/vibe.h" #include "drivers/voltage_monitor.h" #include "drivers/watchdog.h" -#include "drivers/lptim_systick.h" +#include "drivers/sf32lb52/rc10k.h" #include "resource/resource.h" #include "resource/system_resource.h" +#include "kernel/coredump_extra_regions.h" #include "kernel/util/stop.h" #include "kernel/util/task_init.h" #include "kernel/util/sleep.h" @@ -66,19 +53,21 @@ #include "kernel/memory_layout.h" #include "kernel/panic.h" #include "kernel/pulse_logging.h" -#include "services/services.h" -#include "services/common/clock.h" -#include "services/common/compositor/compositor.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" -#include "services/common/new_timer/new_timer_service.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/analytics/analytics.h" -#include "services/common/prf_update.h" +#include "pbl/services/services.h" +#include "pbl/services/boot_splash.h" +#include "pbl/services/clock.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/new_timer/new_timer_service.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/prf_update.h" #include "kernel/ui/kernel_ui.h" #include "kernel/kernel_applib_state.h" #include "kernel/util/delay.h" #include "util/mbuf.h" +#include "system/firmware_storage.h" #include "system/version.h" #include "kernel/event_loop.h" @@ -98,14 +87,6 @@ #include "syscall/syscall_internal.h" #include "debug/debug.h" -#include "debug/setup.h" - -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include #include "FreeRTOS.h" #include "task.h" @@ -113,33 +94,38 @@ #include "mfg/mfg_info.h" #include "mfg/mfg_serials.h" -#include "pebble_errors.h" - #include #include +void soc_early_init(void); + /* here is as good as anywhere else ... */ const int __attribute__((used)) uxTopUsedPriority = configMAX_PRIORITIES - 1; static TimerID s_lowpower_timer = TIMER_INVALID_ID; +#ifndef CONFIG_MFG static TimerID s_uptime_timer = TIMER_INVALID_ID; - +#endif static void main_task(void *parameter); static void print_splash_screen(void) { -#if defined(MANUFACTURING_FW) - PBL_LOG(LOG_LEVEL_ALWAYS, "PebbleOS - MANUFACTURING MODE"); -#elif defined(RECOVERY_FW) - PBL_LOG(LOG_LEVEL_ALWAYS, "PebbleOS - RECOVERY MODE"); +#if defined(CONFIG_MFG) + PBL_LOG_ALWAYS("PebbleOS - MANUFACTURING MODE"); +#elif defined(CONFIG_RECOVERY_FW) + PBL_LOG_ALWAYS("PebbleOS - RECOVERY MODE"); #else - PBL_LOG(LOG_LEVEL_ALWAYS, "PebbleOS"); + PBL_LOG_ALWAYS("PebbleOS"); #endif - PBL_LOG(LOG_LEVEL_ALWAYS, "%s", TINTIN_METADATA.version_tag); - PBL_LOG(LOG_LEVEL_ALWAYS, "(c) 2013-2025 The PebbleOS contributors"); - PBL_LOG(LOG_LEVEL_ALWAYS, " "); + PBL_LOG_ALWAYS("%s%s", + TINTIN_METADATA.version_tag, + (TINTIN_METADATA.is_dual_slot && !TINTIN_METADATA.is_recovery_firmware) ? + (TINTIN_METADATA.is_slot_0 ? " (slot0)" : " (slot1)") : + ""); + PBL_LOG_ALWAYS("(c) 2013-2025 The PebbleOS contributors"); + PBL_LOG_ALWAYS(" "); } #ifdef DUMP_GPIO_CFG_STATE @@ -174,25 +160,7 @@ static void dump_gpio_configuration_state(void) { #endif /* DUMP_GPIO_CFG_STATE */ int main(void) { -#if defined(MICRO_FAMILY_SF32LB52) - board_early_init(); - // FIXME(SF32LB52): remove once we have bootloader - boot_bit_init(); -#endif - - gpio_init_all(); - -#if defined(MICRO_FAMILY_STM32F4) && !defined(LOW_POWER_DEBUG) - // If we're on a snowy board using the stm32f4, we experience random hardfaults after leaving a - // wfi instruction if we have mcu debugging enabled. For now, just turn off mcu debugging - // entirely unless we explicitly want it. See PBL-10174 - disable_mcu_debugging(); -#else - // Turn on MCU debugging at boot. This consumes some power so we'll turn it off after a short - // time has passed (see prv_low_power_debug_config_callback) to allow us to connect after a - // reset but not passively consume power after we've been running for a bit. - enable_mcu_debugging(); -#endif + soc_early_init(); extern void * __ISR_VECTOR_TABLE__; // Defined in linker script SCB->VTOR = (uint32_t)&__ISR_VECTOR_TABLE__; @@ -205,36 +173,13 @@ int main(void) { mbuf_init(); delay_init(); - periph_config_init(); dbgserial_init(); pulse_early_init(); print_splash_screen(); rtc_init(); -#ifdef MICRO_FAMILY_SF32LB52 - lptim_systick_init(); -#endif - -#if BOOTLOADER_TEST_STAGE2 -#define BLTEST_LOG(x...) pbl_log(LOG_LEVEL_ALWAYS, __FILE__, __LINE__, x) - BLTEST_LOG("BOOTLOADER TEST STAGE 2"); - boot_bit_set(BOOT_BIT_FW_STABLE); - BLTEST_LOG("STAGE 2 -- Checking test boot bits"); - if (boot_bit_test(BOOT_BIT_BOOTLOADER_TEST_A) && !boot_bit_test(BOOT_BIT_BOOTLOADER_TEST_B)) { - BLTEST_LOG("ALL BOOTLOADER TESTS PASSED"); - } else { - BLTEST_LOG("STAGE 2 -- Boot bits incorrect!"); - BLTEST_LOG("BOOTLOADER TEST FAILED"); - } - boot_bit_clear(BOOT_BIT_BOOTLOADER_TEST_A | BOOT_BIT_BOOTLOADER_TEST_B); - psleep(10000); - system_hard_reset(); - for (;;) {} - // won't get here, rest gets optimized out -#endif - -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS); #endif @@ -260,13 +205,7 @@ int main(void) { stop_mode_disable(InhibitorMain); // Turn off power to internal flash when in stop mode -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - periph_config_enable(PWR, RCC_APB1Periph_PWR); -#endif pwr_flash_power_down_stop_mode(true /* power_down */); -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - periph_config_disable(PWR, RCC_APB1Periph_PWR); -#endif vTaskStartScheduler(); for(;;); @@ -276,18 +215,9 @@ static void watchdog_timer_callback(void* data) { task_watchdog_bit_set(PebbleTask_NewTimers); } -static void vcom_timer_callback(void* data) { - display_pulse_vcom(); -} - static void register_system_timers(void) { static RegularTimerInfo watchdog_timer = { .list_node = { 0, 0 }, .cb = watchdog_timer_callback }; regular_timer_add_seconds_callback(&watchdog_timer); - - if (BOARD_CONFIG.lcd_com.gpio != 0) { - static RegularTimerInfo vcom_timer = { .list_node = { 0, 0 }, .cb = vcom_timer_callback }; - regular_timer_add_seconds_callback(&vcom_timer); - } } static void init_drivers(void) { @@ -306,35 +236,40 @@ static void init_drivers(void) { battery_init(); vibe_init(); -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - accessory_init(); -#endif - -#if CAPABILITY_HAS_PMIC +#ifdef CONFIG_PMIC pmic_init(); -#endif // CAPABILITY_HAS_PMIC +#endif flash_init(); flash_sleep_when_idle(true); flash_enable_write_protection(); flash_prf_set_protection(true); -#if CAPABILITY_HAS_MICROPHONE + uint8_t vibe_cali = mfg_info_get_vibe_cali(); + if (vibe_cali != MFG_INFO_VIBE_CALI_INVALID) { + vibe_apply_calibration(vibe_cali); + } + +#ifdef CONFIG_MIC mic_init(MIC); #endif -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH touch_sensor_init(); #endif - imu_init(); + accel_init(); +#ifdef CONFIG_MAG + mag_init(); +#endif +#ifdef CONFIG_PRESSURE + pressure_init(); +#endif backlight_init(); ambient_light_init(); -#if CAPABILITY_HAS_TEMPERATURE temperature_init(); -#endif rtc_init_timers(); rtc_alarm_init(); @@ -348,56 +283,17 @@ static void clear_reset_loop_detection_bits(void) { boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE); } +#ifndef CONFIG_MFG static void uptime_callback(void* data) { PBL_LOG_VERBOSE("Uptime reached 15 minutes, set stable bit."); new_timer_delete(s_uptime_timer); boot_bit_set(BOOT_BIT_FW_STABLE); } +#endif static void prv_low_power_debug_config_callback(void* data) { new_timer_delete(s_lowpower_timer); - - // Turn off sleep and stop mode debugging if it is not explicitly enabled - // 4 cases: - // STM32F2/STM32F7, low power debug off: we need to disable it after the first 10 seconds - // STM32F2/STM32F7, low power debug on: we want to leave it on - // STM32F4, low power debug off: we never turned it on in the first place - // STM32F4, low power debug on: we want to leave it on -#if (defined(MICRO_FAMILY_STM32F2) || defined(MICRO_FAMILY_STM32F7)) && !defined(LOW_POWER_DEBUG) - disable_mcu_debugging(); -#endif -} - -#ifdef TEST_SJLJ -static jmp_buf s_sjlj_jmpbuf; -static volatile int s_sjlj_num; -static void prv_sjlj_second(int r) { - PBL_ASSERT(s_sjlj_num == 1, "SJLJ TRACK INCORRECT @ SECOND"); - s_sjlj_num++; - longjmp(s_sjlj_jmpbuf, 0); -} -static void prv_sjlj_first(int r) { - PBL_ASSERT(s_sjlj_num == 0, "SJLJ TRACK INCORRECT @ FIRST"); - s_sjlj_num++; - prv_sjlj_second(r); - PBL_ASSERT(1, "SJLJ IS BROKEN (longjmp didn't occur)"); -} -static void prv_sjlj_main(int r) { - PBL_ASSERT(s_sjlj_num == 2, "SJLJ TRACK INCORRECT @ MAIN"); - s_sjlj_num++; - PBL_ASSERT(r == 1, "SETJMP IS BROKEN (longjmp value wasn't correct)"); -} -static void prv_test_sjlj(void) { - int r; - s_sjlj_num = 0; - if (!(r = setjmp(s_sjlj_jmpbuf))) - prv_sjlj_first(r); - else - prv_sjlj_main(r); - PBL_ASSERT(s_sjlj_num == 3, "SJLJ TRACK INCORRECT @ END"); - PBL_LOG(LOG_LEVEL_ALWAYS, "sjlj works \\o/"); } -#endif static NOINLINE void prv_main_task_init(void) { // The Snowy bootloader does not clear the watchdog flag itself. Clear the @@ -406,7 +302,7 @@ static NOINLINE void prv_main_task_init(void) { static McuRebootReason s_mcu_reboot_reason; s_mcu_reboot_reason = watchdog_clear_reset_flag(); -#if PULSE_EVERYWHERE +#ifdef CONFIG_PULSE_EVERYWHERE pulse_init(); pulse_logging_init(); #endif @@ -417,11 +313,9 @@ static NOINLINE void prv_main_task_init(void) { memory_layout_setup_mpu(); -#if !defined(MICRO_FAMILY_SF32LB52) board_early_init(); -#endif - display_show_splash_screen(); + boot_splash_start(); kernel_applib_init(); @@ -431,20 +325,32 @@ static NOINLINE void prv_main_task_init(void) { new_timer_service_init(); regular_timer_init(); - clock_init(); + + // Initialize the task watchdog and immediately pause it for 30 seconds to + // give us time to initialize everything without worrying about task watchdog + // from firing if we block other tasks. task_watchdog_init(); - analytics_init(); + task_watchdog_pause(30); + + // Wire up the coredump extra-regions registry before pbl_analytics_init, + // which triggers Memfault coredump reconstruction. Reconstruction reads the + // registry to decide what beyond-the-defaults RAM to forward to the cloud. + coredump_extra_regions_init(); + + pbl_analytics_init(); register_system_timers(); system_task_timer_init(); init_drivers(); -#if defined(IS_BIGBOARD) + clock_init(); + +#if defined(CONFIG_IS_BIGBOARD) // Program a random S/N into the Bigboard in case it's not been done yet: mfg_write_bigboard_serial_number(); #endif -#if defined(MANUFACTURING_FW) +#if defined(CONFIG_MFG) mfg_info_update_constant_data(); #endif @@ -457,16 +363,20 @@ static NOINLINE void prv_main_task_init(void) { // Do this early before things can screw ith it. check_prf_update(); +#if defined(CONFIG_PBLBOOT) && defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_MFG) + // Invalidate slot0/1 when booting PRF, so we force main firmware re-install + firmware_storage_invalidate_firmware_slot(0); + firmware_storage_invalidate_firmware_slot(1); +#endif + // When there are new system resources waiting to be installed, this call // will actually install them: resource_init(); system_resource_init(); -#if CAPABILITY_HAS_BUILTIN_HRM - if (mfg_info_is_hrm_present()) { - hrm_init(HRM); - } +#ifdef CONFIG_HRM + hrm_init(HRM); #endif // The display has to be initialized before bluetooth because on Snowy the @@ -475,12 +385,8 @@ static NOINLINE void prv_main_task_init(void) { // clock from reaching the bluetooth module and initialization fails. display_init(); - // Use the MFG calibrated display offset to adjust the display - GPoint mfg_offset = mfg_info_get_disp_offsets(); - display_set_offset(mfg_offset); - // Log display offsets for use in contact support logs - PBL_LOG(LOG_LEVEL_INFO, "MFG Display Offsets (%"PRIi16",%"PRIi16").", mfg_offset.x, mfg_offset.y); - + // Stop boot splash before initializing compositor + boot_splash_stop(); // Can't use the compositor framebuffer until the compositor is initialized compositor_init(); kernel_ui_init(); @@ -491,6 +397,9 @@ static NOINLINE void prv_main_task_init(void) { // The RTC needs be calibrated after the mfg registry service has been initialized so we can // load the measured frequency. +#if defined(CONFIG_SOC_SF32LB52) && !defined(SF32LB52_USE_LXT) + rc10k_init(); +#endif rtc_calibrate_frequency(mfg_info_get_rtc_freq()); clear_reset_loop_detection_bits(); @@ -506,8 +415,12 @@ static NOINLINE void prv_main_task_init(void) { new_timer_start(s_lowpower_timer, 10 * 1000, prv_low_power_debug_config_callback, NULL, 0 /*flags*/); +#ifndef CONFIG_MFG s_uptime_timer = new_timer_create(); new_timer_start(s_uptime_timer, 15 * 60 * 1000, uptime_callback, NULL, 0 /*flags*/); +#else + boot_bit_set(BOOT_BIT_FW_STABLE); +#endif // Initialize button driver at the last moment to prevent "system on" button press from // entering the kernel event queue. @@ -518,10 +431,7 @@ static NOINLINE void prv_main_task_init(void) { dump_gpio_configuration_state(); #endif -#ifdef TEST_SJLJ - // Test setjmp/longjmp - prv_test_sjlj(); -#endif + task_watchdog_resume(); } static void main_task(void *parameter) { diff --git a/src/fw/mcu.h b/src/fw/mcu.h deleted file mode 100644 index 330d6560b1..0000000000 --- a/src/fw/mcu.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! \file -//! MCU wrapper header -//! -//! Include this header in firmware sources instead of including the header for -//! a specific MCU family. -//! -//! Before including this header, a compatibility matrix must be declared by -//! `#define`-ing the appropriate `_COMPATIBLE` macros. -//! -//! Define `CMSIS_COMPATIBLE` if the source file only requires symbols and -//! macros which are defined by CMSIS. -//! -//! Define `STM32F2_COMPATIBLE` and/or `STM32F4_COMPATIBLE` if the source file -//! is compatible with the STM32F2 or STM32F4 microcontroller families, -//! respectively, and requires the corresponding family's peripheral -//! definitions or library functions. - -// Multiple inclusion of this header is allowed. - -#if defined(MICRO_FAMILY_STM32F2) -# if !defined(STM32F2_COMPATIBLE) && !defined(CMSIS_COMPATIBLE) -# error "Source is incompatible with the target MCU" -# endif -# include -#elif defined(MICRO_FAMILY_STM32F4) -# if !defined(STM32F4_COMPATIBLE) && !defined(CMSIS_COMPATIBLE) -# error "Source is incompatible with the target MCU" -# endif -# include -#elif defined(MICRO_FAMILY_STM32F7) -# if !defined(STM32F7_COMPATIBLE) && !defined(CMSIS_COMPATIBLE) -# error "Source is incompatible with the target MCU" -# endif -# include -#elif defined(MICRO_FAMILY_NRF52840) -# if !defined(NRF52840_COMPATIBLE) && !defined(CMSIS_COMPATIBLE) && !defined(NRF5_COMPATIBLE) -# error "Source is incompatible with the target MCU" -# endif -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-declarations" -# include -# pragma GCC diagnostic pop -#elif defined(MICRO_FAMILY_SF32LB52) -# if !defined(SF32LB52_COMPATIBLE) && !defined(CMSIS_COMPATIBLE) -# error "Source is incompatible with the target MCU" -# endif -# include -#elif !defined(SDK) && !defined(UNITTEST) -# error "Unknown or missing MICRO_FAMILY_* define" -#endif - -#undef CMSIS_COMPATIBLE -#undef STM32F2_COMPATIBLE -#undef STM32F4_COMPATIBLE -#undef STM32F7_COMPATIBLE -#undef NRF52840_COMPATIBLE -#undef NRF5_COMPATIBLE -#undef SF32LB52_COMPATIBLE diff --git a/src/fw/mfg/asterix/mfg_info.c b/src/fw/mfg/asterix/mfg_info.c index bd3ada40a2..aaa963ba6a 100644 --- a/src/fw/mfg/asterix/mfg_info.c +++ b/src/fw/mfg/asterix/mfg_info.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mfg/mfg_info.h" #include "drivers/flash.h" @@ -25,6 +12,7 @@ typedef struct { uint32_t color; char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string + uint8_t vibe_cali; //!< Vibration motor trim, MFG_INFO_VIBE_CALI_INVALID if unset } MfgData; static void prv_update_struct(const MfgData *data) { @@ -40,9 +28,10 @@ static MfgData prv_fetch_struct(void) { // Fallback data if not available if (result.data_version != CURRENT_DATA_VERSION) { result.data_version = CURRENT_DATA_VERSION; - result.color = WATCH_INFO_COLOR_COREDEVICES_C2D_BLACK; + result.color = WATCH_INFO_COLOR_COREDEVICES_P2D_BLACK; strncpy(result.model, "asterix", sizeof(result.model)); result.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; + result.vibe_cali = MFG_INFO_VIBE_CALI_INVALID; } return result; @@ -70,21 +59,22 @@ void mfg_info_set_model(const char* model) { prv_update_struct(&data); } -uint32_t mfg_info_get_rtc_freq(void) { - // Not implemented. - return 0U; +uint8_t mfg_info_get_vibe_cali(void) { + return prv_fetch_struct().vibe_cali; } -void mfg_info_set_rtc_freq(uint32_t rtc_freq) { - // Not implemented. +void mfg_info_set_vibe_cali(uint8_t cali) { + MfgData data = prv_fetch_struct(); + data.vibe_cali = cali; + prv_update_struct(&data); } -GPoint mfg_info_get_disp_offsets(void) { - // Not implemented. Can just assume no offset - return (GPoint) {}; +uint32_t mfg_info_get_rtc_freq(void) { + // Not implemented. + return 0U; } -void mfg_info_set_disp_offsets(GPoint p) { +void mfg_info_set_rtc_freq(uint32_t rtc_freq) { // Not implemented. } diff --git a/src/fw/mfg/getafix/mfg_info.c b/src/fw/mfg/getafix/mfg_info.c new file mode 100644 index 0000000000..647cb85957 --- /dev/null +++ b/src/fw/mfg/getafix/mfg_info.c @@ -0,0 +1,83 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg/mfg_info.h" +#include "drivers/flash.h" +#include "flash_region/flash_region.h" + +#define CURRENT_DATA_VERSION 0 + +typedef struct { + uint32_t data_version; + + uint32_t color; + char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string + uint8_t vibe_cali; //!< Vibration motor trim, MFG_INFO_VIBE_CALI_INVALID if unset +} MfgData; + +static void prv_update_struct(const MfgData *data) { + flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); + flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); +} + +static MfgData prv_fetch_struct(void) { + MfgData result; + + flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); + + // Fallback data if not available + if (result.data_version != CURRENT_DATA_VERSION) { + result.data_version = CURRENT_DATA_VERSION; + result.color = WATCH_INFO_COLOR_COREDEVICES_PR2_BLACK_20; + strncpy(result.model, "getafix", sizeof(result.model)); + result.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; + result.vibe_cali = MFG_INFO_VIBE_CALI_INVALID; + } + + return result; +} + +WatchInfoColor mfg_info_get_watch_color(void) { + return prv_fetch_struct().color; +} + +void mfg_info_set_watch_color(WatchInfoColor color) { + MfgData data = prv_fetch_struct(); + data.color = color; + prv_update_struct(&data); +} + +void mfg_info_get_model(char* buffer) { + MfgData data = prv_fetch_struct(); + strcpy(buffer, data.model); +} + +void mfg_info_set_model(const char* model) { + MfgData data = prv_fetch_struct(); + strncpy(data.model, model, sizeof(data.model)); + data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; + prv_update_struct(&data); +} + +uint8_t mfg_info_get_vibe_cali(void) { + return prv_fetch_struct().vibe_cali; +} + +void mfg_info_set_vibe_cali(uint8_t cali) { + MfgData data = prv_fetch_struct(); + data.vibe_cali = cali; + prv_update_struct(&data); +} + +uint32_t mfg_info_get_rtc_freq(void) { + // Not implemented. + return 0U; +} + +void mfg_info_set_rtc_freq(uint32_t rtc_freq) { + // Not implemented. +} + +void mfg_info_update_constant_data(void) { + // Not implemented +} diff --git a/src/fw/mfg/mfg_apps/mfg_app_registry.h b/src/fw/mfg/mfg_apps/mfg_app_registry.h deleted file mode 100644 index 805ed06a27..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_app_registry.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "process_management/pebble_process_md.h" - -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "mfg/mfg_apps/mfg_display_burnin.h" -#include "mfg/mfg_apps/mfg_button_app.h" -#include "mfg/mfg_apps/mfg_bt_test_app.h" -#include "mfg/mfg_apps/mfg_display_app.h" -#include "mfg/mfg_apps/mfg_runin_app.h" -#include "mfg/mfg_apps/mfg_func_test.h" - -typedef const struct PebbleProcessMd* (*MfgInitFuncType)(void); - -static const MfgInitFuncType INIT_MFG_FUNCTIONS[] = { - &mfg_app_button_test_get_info, - &mfg_app_runin_get_info, - &mfg_display_burnin_get_app_info, - &mfg_app_bt_test_get_info, - &mfg_app_lcd_test_black_get_info, - &mfg_app_lcd_test_white_get_info, - &mfg_app_lcd_test_black_white_border_get_info, - &mfg_app_lcd_test_cycle_get_info, - &mfg_func_test_get_app_info, -}; diff --git a/src/fw/mfg/mfg_apps/mfg_bt_test_app.c b/src/fw/mfg/mfg_apps/mfg_bt_test_app.c deleted file mode 100644 index 019a043015..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_bt_test_app.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_bt_test_app.h" - -#include "applib/app.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "applib/ui/window_private.h" -#include "process_state/app_state/app_state.h" -#include "process_management/process_manager.h" - -#include "kernel/pbl_malloc.h" -#include "kernel/util/sleep.h" -#include "services/common/bluetooth/bt_compliance_tests.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "system/passert.h" -#include "system/reboot_reason.h" -#include "system/reset.h" - -EventServiceInfo bt_state_change_event_info; - -typedef enum { - BtTestStateInit = 0, - BtTestStateStopped, - BtTestStateStarting, - BtTestStateStopping, - BtTestStateStarted, - BtTestStateFailed, - BtTestStateResetting, - - BtTestStateNumStates, -} BtTestState; - -static const char* status_text[] = { - [BtTestStateInit] = "Initializing", - [BtTestStateStopped] = "Stopped", - [BtTestStateStarting] = "Starting", - [BtTestStateStopping] = "Stopping", - [BtTestStateStarted] = "Started", - [BtTestStateFailed] = "Failed", - [BtTestStateResetting] = "Resetting", -}; - -typedef struct { - Window window; - TextLayer title; - TextLayer status; - BtTestState test_state; - TimerID reset_timer; -} AppData; - -static void update_text_layers_callback(void *data) { - AppData *app_data = data; - TextLayer *status = &app_data->status; - text_layer_set_text(status, status_text[app_data->test_state]); -} - -static void prv_bt_event_handler(PebbleEvent *e, void* data) { - AppData *app_data = (AppData*)data; - - switch (app_data->test_state) { - case BtTestStateStarting: { - PBL_ASSERTN(!bt_ctl_is_bluetooth_active()); - if (bt_test_bt_sig_rf_test_mode()) { - app_data->test_state = BtTestStateStarted; - } else { - app_data->test_state = BtTestStateFailed; - } - break; - } - case BtTestStateStopping: { - PBL_ASSERTN(bt_ctl_is_bluetooth_active()); - app_data->test_state = BtTestStateStopped; - break; - } - case BtTestStateStopped: - case BtTestStateStarted: - break; - default: - WTF; - } - - process_manager_send_callback_event_to_process(PebbleTask_App, update_text_layers_callback, (void*)data); -} - -static void select_single_click_handler(ClickRecognizerRef recognizer, Window *window) { - AppData *data = app_state_get_user_data(); - BtTestState new_state = data->test_state; - - PBL_ASSERTN(data->test_state < BtTestStateNumStates); - - switch (data->test_state) { - case BtTestStateStopped: { - new_state = BtTestStateStarting; - bt_ctl_set_override_mode(BtCtlModeOverrideStop); - break; - } - case BtTestStateStarted: { - new_state = BtTestStateStopping; - bt_ctl_set_override_mode(BtCtlModeOverrideRun); - break; - } - case BtTestStateStarting: - case BtTestStateStopping: - default: - break; - } - - data->test_state = new_state; - update_text_layers_callback(data); -} - -static void bt_test_reset_callback(void *timer_data) { - RebootReason reason = { RebootReasonCode_MfgShutdown, 0 }; - reboot_reason_set(&reason); - system_reset(); -} - -static void back_single_click_handler(ClickRecognizerRef recognizer, Window *window) { - AppData *app_data = app_state_get_user_data(); - - app_data->test_state = BtTestStateResetting; - text_layer_set_text(&app_data->status, status_text[app_data->test_state]); - - if (app_data->reset_timer == TIMER_INVALID_ID) { - bool success = false; - app_data->reset_timer = new_timer_create(); - if (app_data->reset_timer == TIMER_INVALID_ID) { - success = new_timer_start(app_data->reset_timer, 500, bt_test_reset_callback, app_data, 0 /*flags*/); - } - - if (app_data->reset_timer == TIMER_INVALID_ID || !success) { - bt_test_reset_callback(app_data); - } - } -} - -static void config_provider(Window *window) { - // single click / repeat-on-hold config: - window_single_click_subscribe(BUTTON_ID_SELECT, (ClickHandler) select_single_click_handler); - window_single_click_subscribe(BUTTON_ID_BACK, (ClickHandler) back_single_click_handler); -} - -static void handle_init() { - AppData *data = task_malloc_check(sizeof(AppData)); - - app_state_set_user_data(data); - - *data = (AppData) { - .test_state = BtTestStateInit, - .reset_timer = TIMER_INVALID_ID, - }; - - Window *window = &data->window; - window_init(window, "BT Test"); - - // want to indicate resetting. - window_set_overrides_back_button(window, true); - - TextLayer *title = &data->title; - text_layer_init(title, &window->layer.bounds); - text_layer_set_font(title, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - text_layer_set_text(title, "BT Test Mode"); - layer_add_child(&window->layer, &title->layer); - - TextLayer *status = &data->status; - text_layer_init(status, - &GRect(0, 50, window->layer.bounds.size.w, window->layer.bounds.size.h - 30)); - text_layer_set_font(status, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text(status, status_text[data->test_state]); - layer_add_child(&window->layer, &status->layer); - - window_set_click_config_provider(window, (ClickConfigProvider) config_provider); - - app_window_stack_push(window, true /* Animated */); - - bt_state_change_event_info = (EventServiceInfo) { - .type = PEBBLE_BT_STATE_EVENT, - .handler = prv_bt_event_handler, - .context = data, - }; - - event_service_client_subscribe(&bt_state_change_event_info); - bt_ctl_set_override_mode(BtCtlModeOverrideRun); - bt_ctl_reset_bluetooth(); -} - -static void handle_deinit() { - AppData *data = app_state_get_user_data(); - bt_ctl_set_override_mode(BtCtlModeOverrideNone); - event_service_client_unsubscribe(&bt_state_change_event_info); - task_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -static const PebbleProcessMdSystem s_mfg_bt_test_info = { - .common.main_func = &s_main, - .name = "BT Test" -}; - -const PebbleProcessMd* mfg_app_bt_test_get_info() { - return (const PebbleProcessMd*) &s_mfg_bt_test_info; -} diff --git a/src/fw/mfg/mfg_apps/mfg_bt_test_app.h b/src/fw/mfg/mfg_apps/mfg_bt_test_app.h deleted file mode 100644 index fbaa253330..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_bt_test_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" - -const PebbleProcessMd* mfg_app_bt_test_get_info(); diff --git a/src/fw/mfg/mfg_apps/mfg_display_burnin.c b/src/fw/mfg/mfg_apps/mfg_display_burnin.c deleted file mode 100644 index 48c3b59a92..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_display_burnin.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg_display_burnin.h" - -#include "applib/app.h" -#include "applib/app_timer.h" -#include "applib/ui/app_window_stack.h" -#include "process_state/app_state/app_state.h" - -#include "drivers/display/display.h" -#include "kernel/pbl_malloc.h" -#include "util/units.h" - -#include -#include - -typedef struct { - Window window; - Layer background; - InverterLayer inverter_layer; - uint32_t old_display_hz; -} MfgDisplayBurninAppData; - -static void draw_checkerboard(Layer* background, GContext* c) { - const int height = background->bounds.size.h; - const int width = background->bounds.size.w; - for(int y = 0; y < height; y += 4) { - for(int x = 0; x < width; x += 4) { - graphics_context_set_stroke_color(c, GColorBlack); - graphics_draw_pixel(c, GPoint(x,y)); - graphics_draw_pixel(c, GPoint(x+1,y)); - graphics_draw_pixel(c, GPoint(x,y+1)); - graphics_draw_pixel(c, GPoint(x+1,y+1)); - graphics_draw_pixel(c, GPoint(x+2,y+2)); - graphics_draw_pixel(c, GPoint(x+3,y+2)); - graphics_draw_pixel(c, GPoint(x+2,y+3)); - graphics_draw_pixel(c, GPoint(x+3,y+3)); - } - } -} - - -static void handle_timer(void *timer_data) { - MfgDisplayBurninAppData *data = app_state_get_user_data(); - - layer_set_hidden((Layer*)&data->inverter_layer, - !layer_get_hidden((Layer*)&data->inverter_layer)); - app_timer_register(100 /* milliseconds */, handle_timer, NULL); -} - -static void handle_init(void) { - MfgDisplayBurninAppData *data = task_malloc_check(sizeof(MfgDisplayBurninAppData)); - - app_state_set_user_data(data); - - // Overclock the display to 4MHz make the artifacts issue more likely to happen - data->old_display_hz = display_baud_rate_change(MHZ_TO_HZ(4)); - - window_init(&data->window, "Display Burnin"); - window_set_fullscreen(&data->window, true); - app_window_stack_push(&data->window, true /* Animated */); - - layer_init(&data->background, &data->window.layer.bounds); - data->background.update_proc = (LayerUpdateProc) draw_checkerboard; - layer_add_child(&data->window.layer, (Layer*) &data->background); - - inverter_layer_init(&data->inverter_layer, &data->window.layer.bounds); - layer_add_child(&data->window.layer, (Layer*) &data->inverter_layer); - - app_timer_register(100 /* milliseconds */, handle_timer, NULL); -} - -static void handle_deinit(void) { - MfgDisplayBurninAppData *data = app_state_get_user_data(); - - display_baud_rate_change(data->old_display_hz); - - task_free(data); -} - -static void s_main(void) { - handle_init(); - - app_event_loop(); - - handle_deinit(); -} - -static const PebbleProcessMdSystem s_mfg_func_test = { - .common = { - // UUID: 1bef4e93-5ec4-4af8-9eff-196eaf25b92b - .uuid = {0x1b, 0xef, 0x4e, 0x93, 0x5e, 0xc4, 0x4a, 0xf8, 0x9e, 0xff, 0x19, 0x6e, 0xaf, 0x25, 0xb9, 0x2b}, - .main_func = s_main - }, - .name = "Display Burn-in" -}; - -const Uuid* mfg_display_burnin_get_uuid() { - return &s_mfg_func_test.common.uuid; -} - -const PebbleProcessMd* mfg_display_burnin_get_app_info() { - return (const PebbleProcessMd*) &s_mfg_func_test; -} diff --git a/src/fw/mfg/mfg_apps/mfg_display_burnin.h b/src/fw/mfg/mfg_apps/mfg_display_burnin.h deleted file mode 100644 index 3d1e398553..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_display_burnin.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_md.h" -#include "applib/ui/ui.h" - -#include - -const PebbleProcessMd* mfg_display_burnin_get_app_info(); - -const Uuid* mfg_display_burnin_get_uuid(); diff --git a/src/fw/mfg/mfg_apps/mfg_flash_test.c b/src/fw/mfg/mfg_apps/mfg_flash_test.c deleted file mode 100644 index 8a1a2a875d..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_flash_test.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -// This app only makes sense on Snowy, as it uses addresses and sector sizes that only make sense -// on our parallel flash hardware -#if CAPABILITY_USE_PARALLEL_FLASH - -#include - -#include "mfg_flash_test.h" - -#include "drivers/flash.h" -#include "drivers/task_watchdog.h" -#include "flash_region/flash_region.h" -#include "system/logging.h" -#include "kernel/pbl_malloc.h" - - -static const uint8_t data_pattern = 0xAA; -static const uint8_t test_pattern = 0x55; -static volatile bool enable_flash_test = false; - -static FlashTestErrorType prv_read_verify_byte(uint32_t read_addr, - uint8_t expected_val, - FlashTestErrorType err_code, - uint8_t bitpos, - bool disp_logs) { - uint8_t read_buffer = 0; - flash_read_bytes((uint8_t *)&read_buffer, read_addr, sizeof(read_buffer)); - - if (disp_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx8, - read_addr, read_buffer); - } - - if (read_buffer != expected_val) { - switch (err_code) { - case FLASH_TEST_ERR_ERASE: - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Did not successfully erase the sector"); - break; - case FLASH_TEST_ERR_STUCK_AT_HIGH: - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Address bit %d stuck at high", bitpos); - break; - case FLASH_TEST_ERR_STUCK_AT_LOW: - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Address bit %d stuck at low or shorted", bitpos); - break; - default: - break; - } - return err_code; - } - - return FLASH_TEST_SUCCESS; -} - -static FlashTestErrorType prv_read_verify_halfword(uint32_t read_addr, - uint16_t expected_val, - FlashTestErrorType err_code, - bool disp_logs) { - uint16_t read_buffer = 0; - flash_read_bytes((uint8_t *)&read_buffer, read_addr, sizeof(read_buffer)); - - if (disp_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx16, - read_addr, read_buffer); - } - - if (read_buffer != expected_val) { - switch (err_code) { - case FLASH_TEST_ERR_ERASE: - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Did not successfully erase the sector"); - break; - case FLASH_TEST_ERR_DATA_WRITE: - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Did not successfully write the data"); - break; - default: - break; - } - return err_code; - } - - return FLASH_TEST_SUCCESS; -} - -static FlashTestErrorType prv_write_read_verify_byte(uint32_t write_addr, - uint8_t write_val, - uint8_t expected_val, - bool disp_logs) { - uint8_t write_buffer = write_val; - uint8_t read_buffer = 0; - // Write test pattern - if (disp_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Writing Addr 0x%"PRIx32" to value 0x%"PRIx8, - write_addr, write_val); - } - flash_write_bytes((uint8_t *)&write_buffer, write_addr, sizeof(write_buffer)); - - // Confirm write took place - read_buffer = 0; - flash_read_bytes((uint8_t*) &read_buffer, write_addr, sizeof(read_buffer)); - - if (disp_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx8, - write_addr, read_buffer); - } - - if (read_buffer != expected_val) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Did not successfully write the data"); - return FLASH_TEST_ERR_DATA_WRITE; - } - - return FLASH_TEST_SUCCESS; -} - -static FlashTestErrorType prv_write_read_verify_halfword(uint32_t write_addr, - uint16_t write_val, - uint16_t expected_val, - bool disp_logs) { - uint16_t write_buffer = write_val; - uint16_t read_buffer = 0; - - // Write test pattern - if (disp_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Writing Addr 0x%"PRIx32" to value 0x%"PRIx16, - write_addr, write_val); - } - - flash_write_bytes((uint8_t *)&write_buffer, write_addr, sizeof(write_buffer)); - - // Confirm write took place - read_buffer = 0; - flash_read_bytes((uint8_t*) &read_buffer, write_addr, sizeof(read_buffer)); - - if (disp_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx16, - write_addr, read_buffer); - } - - if (read_buffer != expected_val) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Did not successfully write the data"); - return FLASH_TEST_ERR_DATA_WRITE; - } - - return FLASH_TEST_SUCCESS; -} - -#define VERIFY_TEST_STATUS(status) \ - do { \ - if (status != FLASH_TEST_SUCCESS) { return status; } \ - } while(0) - -/***********************************************************/ -/******************* DATA Test Functions *******************/ -/***********************************************************/ -static FlashTestErrorType prv_run_data_test(void) { - FlashTestErrorType status = FLASH_TEST_SUCCESS; - - // Test each data bit using walking 1's method by toggling each data line - PBL_LOG(LOG_LEVEL_DEBUG, ">START - DATA TEST 1: Data bus test"); - - // Initialize region that is to be written - uint16_t data_buffer = 0x0; - uint8_t bitpos = 0; - - uint16_t read_buffer = 0x0; - // Ensure within test data region and aligned to sector boundary - uint32_t addr_region = (FLASH_TEST_ADDR_START + SECTOR_SIZE_BYTES) & SECTOR_ADDR_MASK; - - // Loop on each data bit - erase the sector, then write the next data value and verify that data - // was written - for (data_buffer = 1; data_buffer != 0; data_buffer <<= 1) { - read_buffer = 0; - flash_read_bytes((uint8_t*) &read_buffer, addr_region, sizeof(read_buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx16, - addr_region, read_buffer); - - if (read_buffer != 0xFFFF) { - // Erase sector only if necessary - flash_erase_sector_blocking(addr_region); - flash_read_bytes((uint8_t*) &read_buffer, addr_region, sizeof(read_buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx16, - addr_region, read_buffer); - } - - // All data should be set to 0xFFFF upon erase - if (read_buffer != 0xFFFF) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Did not successfully erase the sector"); - return FLASH_TEST_ERR_ERASE; - } - - // Read and compare data that was written - status = prv_write_read_verify_halfword(addr_region, data_buffer, data_buffer, true); - if (status != 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Data bit %d not returning correct data value", bitpos); - return status; - } - - bitpos++; - addr_region += 4; // increment to the next address to avoid extra erases - } - - PBL_LOG(LOG_LEVEL_DEBUG, ">PASS - DATA TEST 1: Data bus test"); - return status; -} - -/***********************************************************/ -/******************* Addr Test Functions *******************/ -/***********************************************************/ -// Write the test byte 0xAA at each power-of-two offset within the flash test range. If necessary, -// erase the sector that the byte resides in first. -// The base address always gets erased. If skip_base_addr is true, we leave it at 0xFF (erased) -// otherwise we write 0xAA to that address as well. -static FlashTestErrorType write_initial_pattern(bool display_logs, bool skip_base_addr, - uint32_t* erase_addr) { - FlashTestErrorType status = FLASH_TEST_SUCCESS; - uint32_t base_addr = FLASH_TEST_ADDR_START; - uint32_t test_addr = base_addr; - uint32_t addr_mask = FLASH_TEST_ADDR_MSK; - uint8_t read_buffer = 0; - uint32_t bit_offset; - - if (display_logs) { PBL_LOG(LOG_LEVEL_DEBUG, ">>> Initializing data patterns..."); } - if (display_logs) { PBL_LOG(LOG_LEVEL_DEBUG, ">>> Erasing sectors..."); } - if (erase_addr) { - // only erase the specific erase address - if (display_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Erasing Addr 0x%"PRIx32, *erase_addr); - } - flash_erase_sector_blocking(*erase_addr); - } else { - // Erase the addresses within test range that we will touch. These are addresses with - // power-of-two offsets - for (bit_offset = 0; (bit_offset == 0) || (bit_offset & addr_mask); bit_offset <<= 1) { - - if (bit_offset > base_addr) { - test_addr = bit_offset; - } else { - test_addr = base_addr + bit_offset; - } - if (test_addr >= FLASH_TEST_ADDR_END) { - break; - } - - // skip erasing of unnecessary overlapping sectors - if ((test_addr >= base_addr + SECTOR_SIZE_BYTES) || - (test_addr == base_addr + 1) || (test_addr == base_addr)) { - // check if byte location is already 0xFF or default data pattern, then skip erase - // Always erase base address - - flash_read_bytes((uint8_t*)&read_buffer, test_addr, sizeof(read_buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Testing Addr 0x%"PRIx32", value:0x%x", test_addr, read_buffer); - - if (((read_buffer != 0xFF) && (read_buffer != 0xAA)) || (test_addr == base_addr)) { - if (display_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Erasing Addr 0x%"PRIx32, test_addr); - } - flash_erase_sector_blocking(test_addr); - - // Verify data was erased - status = prv_read_verify_byte(test_addr, 0xFF, FLASH_TEST_ERR_ERASE, 0, display_logs); - VERIFY_TEST_STATUS(status); - } - } - - // After the base address, go up by power of 2's - if (bit_offset == 0) { - bit_offset = 1; - } - } - } - - if (display_logs) { PBL_LOG(LOG_LEVEL_DEBUG, ">>> Erasing sectors...complete"); } - - // Write default data pattern to each power-of-two offset within the test region - for (bit_offset = 1; (bit_offset & addr_mask) != 0; bit_offset <<= 1) { - if (bit_offset > base_addr) { - test_addr = bit_offset; - } else { - test_addr = base_addr + bit_offset; - } - if (test_addr >= FLASH_TEST_ADDR_END) { - break; - } - - // Write default data pattern to address if necessary - status = prv_read_verify_byte(test_addr, data_pattern, FLASH_TEST_ERR_SKIP, 0, display_logs); - if (status != FLASH_TEST_SUCCESS) { - // Write default data pattern to address - status = prv_write_read_verify_byte(test_addr, data_pattern, data_pattern, display_logs); - VERIFY_TEST_STATUS(status); - } - } - - if (!skip_base_addr) { - test_addr = base_addr; - - // Read initial value - read_buffer = 0; - flash_read_bytes((uint8_t*) &read_buffer, test_addr, sizeof(read_buffer)); - if (display_logs) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx8, - test_addr, read_buffer); - } - - // Write data pattern - status = prv_write_read_verify_byte(test_addr, data_pattern, data_pattern, display_logs); - VERIFY_TEST_STATUS(status); - } - - if (display_logs) { PBL_LOG(LOG_LEVEL_DEBUG, ">>> Initializing data patterns...complete"); } - - return FLASH_TEST_SUCCESS; -} - -static FlashTestErrorType prv_run_addr_test (void) { - uint32_t base_addr = FLASH_TEST_ADDR_START; - uint32_t test_addr = base_addr; - uint32_t addr_mask = FLASH_TEST_ADDR_MSK; - uint8_t read_buffer = 0; - uint32_t bit_offset; - uint32_t test_offset = 0; - FlashTestErrorType status = FLASH_TEST_SUCCESS; - - /////////////////////////////////////////////////// - /// Test 1: Check for address bits stuck at high - /////////////////////////////////////////////////// - PBL_LOG(LOG_LEVEL_DEBUG, ">START - ADDR TEST 1: Check for address bits stuck at high"); - - // Write data pattern (0xAA) to each power-of-2 offset within the flash - status = write_initial_pattern(true /*display_logs*/, false /*skip_base_addr*/, - NULL /*erase_addr*/); - VERIFY_TEST_STATUS(status); - - // offset of 0 - test_addr = base_addr + test_offset; - - // Read initial value - read_buffer = 0; - flash_read_bytes((uint8_t*) &read_buffer, test_addr, sizeof(read_buffer)); - PBL_LOG(LOG_LEVEL_DEBUG, ">> Reading Addr 0x%"PRIx32" value is 0x%"PRIx8, - test_addr, read_buffer); - - // Write test pattern to address 0 - // After writing test pattern, data should be 0x00 since initial value was 0xAA and 0x55 was written - status = prv_write_read_verify_byte(test_addr, test_pattern, 0x00, true /*display_logs*/); - VERIFY_TEST_STATUS(status); - - - // Check if any of the address bits are stuck at high. If they are, then the previous write to - // address 0 would have trashed the data at one of the other addresses - uint8_t base_addr_pos = 0; - uint8_t bitpos; - bool stuck_at_high = false; - for (bit_offset = 1, bitpos = 0; bit_offset & addr_mask; bit_offset <<= 1, bitpos++) { - if (bit_offset > base_addr) { - test_addr = bit_offset; - } else if (bit_offset == base_addr) { - base_addr_pos = bitpos; - // Skip base address check - that is done later - PBL_LOG(LOG_LEVEL_DEBUG, "Skip base address bit position %d", bitpos); - bitpos++; - continue; - } else { - test_addr = base_addr + bit_offset; - } - - if (test_addr >= FLASH_TEST_ADDR_END) { - PBL_LOG(LOG_LEVEL_DEBUG, "Skipping test address 0x%"PRIx32" which is out of range", - test_addr); - break; - } - - - // If test_pattern was written over the data_pattern, then return data should be 0 since - // data cannot transition from 0 to 1 without an erase; else it will be initial data_pattern - status = prv_read_verify_byte(test_addr, data_pattern, FLASH_TEST_ERR_STUCK_AT_HIGH, bitpos, - true); - if (status != FLASH_TEST_SUCCESS) { - stuck_at_high = true; - } - } - - // Special case - test bit for base address - // - Use an address between FLASH_TEST_ADDR_START and base_addr - PBL_LOG(LOG_LEVEL_DEBUG, ">> Testing special case for base address bit %d", base_addr_pos); - // Read initial value - test_addr = FLASH_REGION_FILESYSTEM_BEGIN; - uint32_t special_case_addr = test_addr | base_addr; - if ((test_addr >= base_addr) || (special_case_addr > FLASH_TEST_ADDR_END)) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Cannot test address bit for base_addr"); - return FLASH_TEST_ERR_ADDR_RANGE; - } - - // erase (base_addr | test_addr) and start of test space - flash_erase_sector_blocking(test_addr); - flash_erase_sector_blocking(special_case_addr); - - // Verify erase took place - status = prv_read_verify_byte(test_addr, 0xFF, FLASH_TEST_ERR_ERASE, 0, true); - VERIFY_TEST_STATUS(status); - - // Verify erase took place - status = prv_read_verify_byte(special_case_addr, 0xFF, FLASH_TEST_ERR_ERASE, 0, true); - VERIFY_TEST_STATUS(status); - - // Write test pattern to the test address - // Data should be set to test_pattern since existing data should be 0xFF and we are writing - // test_pattern - status = prv_write_read_verify_byte(test_addr, test_pattern, test_pattern, true); - VERIFY_TEST_STATUS(status); - - // Confirm write into base_addr did not take place - // If test_pattern was written over the data_pattern, then return data should be 0 since - // data cannot transition from 0 to 1 without an erase - status = prv_read_verify_byte(special_case_addr, 0xFF, FLASH_TEST_ERR_STUCK_AT_HIGH, - base_addr_pos, true); - if (status != FLASH_TEST_SUCCESS) { - stuck_at_high = true; - } - - // If any bits are stuck at high, return error - if (stuck_at_high) { - return FLASH_TEST_ERR_STUCK_AT_HIGH; - } - - PBL_LOG(LOG_LEVEL_DEBUG, ">PASS - ADDR TEST 1: Check for address bits stuck at high"); - - - - ///////////////////////////////////////////////////////////// - /// Test 2: Check for address bits stuck at low or shorted - ///////////////////////////////////////////////////////////// - PBL_LOG(LOG_LEVEL_DEBUG, ">START - ADDR TEST 2: Check for address bits stuck at low or shorted"); - - // NOTE that the previous test only modified the data at base_addr and left all other - // power-of-2 addresses with the data pattern in them. The write_initial_pattern() method - // will skip erasing a sector if all of the power of 2 addresses within it still have the - // data pattern, so only the first sector will end up being re-erased. - status = write_initial_pattern(true /*display_logs*/, false /*skip_base_addr*/, - NULL /*erase_addr*/); - VERIFY_TEST_STATUS(status); - - bool stuck_at_low = false; - for (test_offset = 1, bitpos=0; test_offset & addr_mask; test_offset <<= 1, bitpos++) { - - if (test_offset >= base_addr) { - test_addr = test_offset; - } else { - test_addr = base_addr + test_offset; - } - if (test_addr >= FLASH_TEST_ADDR_END) { - break; - } - - // Skip base address - if (test_addr == base_addr) { - continue; - } - PBL_LOG(LOG_LEVEL_DEBUG, ">> Testing Stuck at Low at Addr 0x%"PRIx32, test_addr); - - // After we write test_pattern, data should be set to 0x00 since existing data should be 0xAA - // and we are writing 0x55 - status = prv_write_read_verify_byte(test_addr, test_pattern, 0x00, false); - VERIFY_TEST_STATUS(status); - - // read base address to insure that it wasn't modified due to a stuck at zero in an address - // bit - status = prv_read_verify_byte(base_addr, data_pattern, FLASH_TEST_ERR_STUCK_AT_LOW, bitpos, - false); - if (status != FLASH_TEST_SUCCESS) { - stuck_at_low = true; - } - - // Check if any other address bits are shorted with our test bit. If shorted, then we would - // read 0 from the address bit which is shorted with the test one. - // We only have to check shorts with higher address bits, since we've already checked for - // shorts from the lower address bits to this one. - uint8_t bitpos2 = bitpos+1; - for (bit_offset = test_offset << 1; bit_offset & addr_mask; bit_offset <<= 1, bitpos2++) { - // skip same offset - if (bit_offset == test_offset) { - continue; - } - - uint32_t test_addr2; - if (bit_offset >= base_addr) { - test_addr2 = bit_offset; - } else { - test_addr2 = base_addr + bit_offset; - } - if (test_addr2 >= FLASH_TEST_ADDR_END) { - break; - } - - status = prv_read_verify_byte(test_addr2, data_pattern, FLASH_TEST_ERR_STUCK_AT_LOW, bitpos2, - false /*display_logs*/); - if (status != FLASH_TEST_SUCCESS) { - stuck_at_low = true; - } - } - - if (stuck_at_low) { - // Restore data back to original if stuck at low occurred - status = write_initial_pattern(false /*display_logs*/, false /*skip_base_addr*/, - NULL /*base_addr*/); - } - - VERIFY_TEST_STATUS(status); - } - - if (stuck_at_low) { - return FLASH_TEST_ERR_STUCK_AT_LOW; - } - - PBL_LOG(LOG_LEVEL_DEBUG, ">PASS - ADDR TEST 2: Check for address bits stuck at low or shorted"); - - return FLASH_TEST_SUCCESS; -} - -/***********************************************************/ -/******************* Stress Test Functions *****************/ -/***********************************************************/ -#define FLASH_TEST_STRESS_ADDR1 0x00A5A5A5 -#define FLASH_TEST_STRESS_DATA1 0x5A5A -#define FLASH_TEST_STRESS_ADDR2 0x00CA5A5A -#define FLASH_TEST_STRESS_DATA2 0xA5A5 -static FlashTestErrorType setup_stress_addr_test(void) { - FlashTestErrorType status = FLASH_TEST_SUCCESS; - - // Read/Write from address 1 - uint32_t stress_addr1 = FLASH_TEST_STRESS_ADDR1; - uint16_t stress_data1 = FLASH_TEST_STRESS_DATA1; - - // Read/Write from address 2 - uint32_t stress_addr2 = FLASH_TEST_STRESS_ADDR2; - uint16_t stress_data2 = FLASH_TEST_STRESS_DATA2; - - if ((stress_addr1 < FLASH_REGION_FILESYSTEM_BEGIN) || - (stress_addr1 >= FLASH_REGION_FILESYSTEM_END)) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Invalid range"); - return FLASH_TEST_ERR_ADDR_RANGE; - } - - if ((stress_addr2 < FLASH_REGION_FILESYSTEM_BEGIN) || - (stress_addr2 >= FLASH_REGION_FILESYSTEM_END)) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Invalid range"); - return FLASH_TEST_ERR_ADDR_RANGE; - } - - // Erase sectors - flash_erase_sector_blocking(stress_addr1); - status = prv_read_verify_halfword(stress_addr1, 0xFFFF, FLASH_TEST_ERR_ERASE, false); - VERIFY_TEST_STATUS(status); - flash_erase_sector_blocking(stress_addr2); - status = prv_read_verify_halfword(stress_addr2, 0xFFFF, FLASH_TEST_ERR_ERASE, false); - VERIFY_TEST_STATUS(status); - - // Write data to stress address locations - PBL_LOG(LOG_LEVEL_DEBUG, ">> Writing Addr 0x%"PRIx32" to value 0x%"PRIx16, - stress_addr1, stress_data1); - flash_write_bytes((uint8_t *)&stress_data1, stress_addr1, sizeof(stress_data1)); - - PBL_LOG(LOG_LEVEL_DEBUG, ">> Writing Addr 0x%"PRIx32" to value 0x%"PRIx16, - stress_addr2, stress_data2); - flash_write_bytes((uint8_t *)&stress_data2, stress_addr2, sizeof(stress_data2)); - - return FLASH_TEST_SUCCESS; -} - -// Run address read/write stess test - if iterations is 0, then stop only when button is pushed; -// else go until iterations hit -static FlashTestErrorType prv_run_stress_addr_test(uint32_t iterations) { - PBL_LOG(LOG_LEVEL_DEBUG, ">START - STRESS TEST 1"); - - uint16_t halfwordcount = 0; - unsigned int iteration_count = 0; - - // Read/Write from address 1 - uint32_t stress_addr1 = FLASH_TEST_STRESS_ADDR1; - uint16_t stress_data1 = FLASH_TEST_STRESS_DATA1; - - // Read/Write from adress 2 - uint32_t stress_addr2 = FLASH_TEST_STRESS_ADDR2; - uint16_t stress_data2 = FLASH_TEST_STRESS_DATA2; - - FlashTestErrorType status = setup_stress_addr_test(); - if (status != FLASH_TEST_SUCCESS) { - return status; - } - - // Keep going until DOWN button is pushed or iterations reached - while(((iterations == 0) && enable_flash_test) || - ((iterations > 0) && (iteration_count < iterations))) { - // Confirm write took place - data should now be set to stress_data1 - status = prv_read_verify_halfword(stress_addr1, stress_data1, FLASH_TEST_ERR_DATA_WRITE, false); - VERIFY_TEST_STATUS(status); - halfwordcount++; - - // Confirm write took place - data should now be set to stress_data2 - status = prv_read_verify_halfword(stress_addr2, stress_data2, FLASH_TEST_ERR_DATA_WRITE, false); - VERIFY_TEST_STATUS(status); - halfwordcount++; - - if (halfwordcount*2 % (256*1024) == 0) { - // Reading flash words (which are 16 bits) hence double - if (iterations) { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Read 256KB, iteration: %d of %"PRId32, - iteration_count, iterations); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, ">> Read 256KB, iteration: %d", iteration_count); - } - } - - iteration_count++; - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Ran %d iterations", iteration_count); - PBL_LOG(LOG_LEVEL_DEBUG, ">PASS - STRESS TEST 1"); - - return FLASH_TEST_SUCCESS; -} - -/***********************************************************/ -/******************* Perf Data Test Functions **************/ -/***********************************************************/ - -#define COUNTER_START \ - uint32_t _start = *((volatile uint32_t *)0xE0001004);\ - uint32_t _tot = 0 -#define COUNTER_STOP \ - uint32_t _end = *((volatile uint32_t *)0xE0001004) -#define COUNTER_PRINT(x) \ - do { \ - if (_end > _start) { \ - _tot += (_end - _start); \ - } else { \ - _tot += (UINT32_MAX - _start) + _end; \ - } \ - PBL_LOG(LOG_LEVEL_DEBUG, "Read %lu bytes %lu ticks %lu us", x, _tot, _tot / 64); \ -} while (0) - -#define SWAP(a, b) \ - do { \ - uint32_t temp = a; \ - a = b; \ - b = temp; \ - } while (0) - -// Run performance test to measure data access times -#define DWT_CTRL_ADDR 0xE0001000 -#define DWT_CYCCNT_ADDR 0xE0001004 -#define MAX_READ_BUFF_SIZE 4096 // 4KB -static FlashTestErrorType prv_run_perf_data_test(void) { - uint8_t *read_buffer = (uint8_t *) app_malloc(MAX_READ_BUFF_SIZE); - if (!read_buffer) { - PBL_LOG(LOG_LEVEL_DEBUG, "ERROR: Not enough memory to run test"); - return FLASH_TEST_ERR_OOM; - } - - uint32_t addr = FLASH_TEST_ADDR_START; - volatile uint32_t *ptr = (uint32_t *) DWT_CTRL_ADDR; - for (uint32_t num_bytes = 1; num_bytes <= MAX_READ_BUFF_SIZE; num_bytes<<=1) { - // Run test three times and print out the median throughput - uint32_t ticks[3] = {0, 0, 0}; - for (uint8_t repeat = 0; repeat < 3; repeat++) { - *ptr = *ptr & 0xFFFFFFFE; - *((volatile uint32_t *)DWT_CYCCNT_ADDR) = 0; - *ptr = *ptr | 0x1; - COUNTER_START; - flash_read_bytes((uint8_t *)&read_buffer[0], addr, num_bytes); - COUNTER_STOP; - COUNTER_PRINT(num_bytes); - ticks[repeat] = _tot; - } - - // Do a simple sort - if (ticks[0] > ticks[1]) { - SWAP(ticks[0], ticks[1]); - } - if (ticks[1] > ticks[2]) { - SWAP(ticks[1], ticks[2]); - if (ticks[0] > ticks[1]) { - SWAP(ticks[0], ticks[1]); - } - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Read %lu bytes, median throughput %lu KBps", num_bytes, (num_bytes * 1000 * 64 / ticks[1])); - } - - app_free(read_buffer); - return FLASH_TEST_SUCCESS; -} - -/***********************************************************/ -/******************* Wrapper Functions *********************/ -/***********************************************************/ -void stop_flash_test_case( void ) { - enable_flash_test = false; -} - -FlashTestErrorType run_flash_test_case(FlashTestCaseType test_case_num, uint32_t iterations) { - FlashTestErrorType status = FLASH_TEST_SUCCESS; - - // Disable watchdog if enabled - bool previous_task_watchdog_state = task_watchdog_mask_get(pebble_task_get_current()); - if (previous_task_watchdog_state) { - task_watchdog_mask_clear(pebble_task_get_current()); - } - - enable_flash_test = true; - - // Schedule test to run - switch (test_case_num) { - case FLASH_TEST_CASE_RUN_DATA_TEST: - status = prv_run_data_test(); - break; - case FLASH_TEST_CASE_RUN_ADDR_TEST: - status = prv_run_addr_test(); - break; - case FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST: - status = prv_run_stress_addr_test(iterations); - break; - case FLASH_TEST_CASE_RUN_PERF_DATA_TEST: - status = prv_run_perf_data_test(); - break; - case FLASH_TEST_CASE_RUN_SWITCH_MODE_ASYNC: - case FLASH_TEST_CASE_RUN_SWITCH_MODE_SYNC_BURST: - flash_switch_mode(test_case_num - FLASH_TEST_CASE_RUN_SWITCH_MODE_ASYNC); - status = FLASH_TEST_SUCCESS; - break; - default: - status = FLASH_TEST_ERR_UNSUPPORTED; - break; - } - - enable_flash_test = false; - - if (status == FLASH_TEST_SUCCESS) { - PBL_LOG(LOG_LEVEL_DEBUG, ">>>>>PASS FLASH TEST CASE %d<<<<<", test_case_num); - } - else { - PBL_LOG(LOG_LEVEL_DEBUG, ">>>>>FAIL FLASH TEST CASE %d, Status: %d<<<<<", test_case_num, status); - } - - // Re-enable watchdog state if previously enabled - if (previous_task_watchdog_state) { - task_watchdog_bit_set(pebble_task_get_current()); - task_watchdog_mask_set(pebble_task_get_current()); - } - - return status; -} - -#endif // CAPABILITY_USE_PARALLEL_FLASH diff --git a/src/fw/mfg/mfg_apps/mfg_flash_test.h b/src/fw/mfg/mfg_apps/mfg_flash_test.h deleted file mode 100644 index c791a77dfa..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_flash_test.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// To add new tests, update the following: -// 1. FlashTestCaseType enum -// 2. Update run_flash_test_case to include test -// 3. Update test_window_load in flash_test.c with test case name -// 4. Update flash_test_window_load in flash_test.c with test case menu item -// 5. Update prompt_commands.c if necessary -typedef enum { - FLASH_TEST_CASE_RUN_DATA_TEST = 0, - FLASH_TEST_CASE_RUN_ADDR_TEST = 1, - FLASH_TEST_CASE_RUN_STRESS_ADDR_TEST = 2, - FLASH_TEST_CASE_RUN_PERF_DATA_TEST = 3, - FLASH_TEST_CASE_RUN_SWITCH_MODE_ASYNC = 4, - FLASH_TEST_CASE_RUN_SWITCH_MODE_SYNC_BURST = 5, - // Add new test cases above this line - FLASH_TEST_CASE_NUM_MENU_ITEMS -} FlashTestCaseType; - -typedef enum { -FLASH_TEST_SUCCESS = 0, -FLASH_TEST_ERR_OTHER = -1, -FLASH_TEST_ERR_ERASE = -2, -FLASH_TEST_ERR_DATA_WRITE = -3, -FLASH_TEST_ERR_ADDR_RANGE = -4, -FLASH_TEST_ERR_STUCK_AT_HIGH = -5, -FLASH_TEST_ERR_STUCK_AT_LOW = -6, -FLASH_TEST_ERR_OOM = -7, -FLASH_TEST_ERR_UNSUPPORTED = -8, -FLASH_TEST_ERR_SKIP = -9, -} FlashTestErrorType; - -// This function explicitly stop a test case if it is currently running. Currently this only affects -// the stress test. -extern void stop_flash_test_case( void ); - -extern FlashTestErrorType run_flash_test_case(FlashTestCaseType test_case_num, uint32_t iterations); diff --git a/src/fw/mfg/mfg_apps/mfg_func_test_battery.h b/src/fw/mfg/mfg_apps/mfg_func_test_battery.h deleted file mode 100644 index 7ac6d02bad..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_func_test_battery.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// FIXME: Wha? We don't use this file directly. -#include "mfg_func_test_buttons.h" - -#include "kernel/events.h" -#include "applib/fonts/fonts.h" -#include "applib/ui/ui.h" -#include "drivers/battery.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/new_timer/new_timer.h" - -#include - -typedef struct { - MfgFuncTestData *app_data; - char text_top[32]; - TextLayer text_layer_top; - TextLayer text_layer_center; - char text_volt[16]; - TextLayer text_layer_volt; - PathLayer bolt; - TimerID poll_timer_id; -} BatteryTestData; - -static void battery_polling_callback(void *timer_data) { - BatteryTestData *data = (BatteryTestData*)timer_data; - - sprintf(data->text_volt, "%i mV", battery_get_millivolts()); - text_layer_set_text(&data->text_layer_volt, data->text_volt); - - const bool is_discharging = !battery_get_charge_state().is_charging; - layer_set_hidden(&data->bolt.layer, is_discharging); - layer_set_hidden(&data->text_layer_center.layer, !is_discharging); -} - -static void stop_battery_polling(BatteryTestData *data) { - if (data == NULL || data->poll_timer_id == TIMER_INVALID_ID) { - return; - } - new_timer_delete(data->poll_timer_id); - data->poll_timer_id = TIMER_INVALID_ID; -} - -static void start_battery_polling(BatteryTestData *data) { - if (data == NULL || data->poll_timer_id != TIMER_INVALID_ID) { - stop_battery_polling(data); - } - const uint32_t poll_interval_ms = 300; - if (data->poll_timer_id == TIMER_INVALID_ID) { - data->poll_timer_id = new_timer_create(); - } - new_timer_start(data->poll_timer_id, poll_interval_ms, battery_polling_callback, data, TIMER_START_FLAG_REPEATING); -} - -static void battery_window_button_up(ClickRecognizerRef recognizer, void *context) { - BatteryTestData *data = window_get_user_data(context); - if (data->app_data->charge_test_done) { - const bool animated = false; - window_stack_pop(animated); - } else { - if (battery_get_charge_state().is_charging) { - mfg_func_test_append_bits(MfgFuncTestBitChargeTestPassed); - data->app_data->charge_test_done = true; - stop_battery_polling(data); - layer_set_hidden(&data->text_layer_volt.layer, true); - layer_set_hidden(&data->bolt.layer, true); - text_layer_set_text(&data->text_layer_center, "QC OK!"); - layer_set_hidden(&data->text_layer_center.layer, false); - } - } -} - -static void battery_window_click_config_provider(void *context) { - for (ButtonId button_id = BUTTON_ID_BACK; button_id < NUM_BUTTONS; ++button_id) { - window_raw_click_subscribe(button_id, NULL, (ClickHandler) battery_window_button_up, context); - } -} - -static void battery_window_load(Window *window) { - BatteryTestData *data = window_get_user_data(window); - - // Layout Battery Test Window: - Layer *root = &window->layer; - - char addr_hex_str[BT_ADDR_FMT_BUFFER_SIZE_BYTES]; - bt_local_id_copy_address_hex_string(addr_hex_str); - sprintf(data->text_top, "Quality Test\nMAC: %s", addr_hex_str); - - TextLayer *text_layer_top = &data->text_layer_top; - text_layer_init(text_layer_top, &GRect(0, 0, 144, 168)); - text_layer_set_background_color(text_layer_top, GColorWhite); - text_layer_set_text_color(text_layer_top, GColorBlack); - text_layer_set_text(text_layer_top, data->text_top); - text_layer_set_font(text_layer_top, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); - layer_add_child(root, &text_layer_top->layer); - - TextLayer *text_layer_center = &data->text_layer_center; - text_layer_init(text_layer_center, &GRect(0, 60, 144, 40)); - text_layer_set_background_color(text_layer_center, GColorClear); - text_layer_set_text_color(text_layer_center, GColorBlack); - text_layer_set_text(text_layer_center, "Plug Charger"); - text_layer_set_font(text_layer_center, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - layer_add_child(root, &text_layer_center->layer); - - TextLayer *text_layer_volt = &data->text_layer_volt; - text_layer_init(text_layer_volt, &GRect(0, 128, 144, 40)); - text_layer_set_background_color(text_layer_volt, GColorBlack); - text_layer_set_text_color(text_layer_volt, GColorWhite); - text_layer_set_font(text_layer_volt, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - layer_add_child(root, &text_layer_volt->layer); - - PathLayer *bolt = &data->bolt; - path_layer_init(bolt, &BOLT_PATH_INFO); - path_layer_set_fill_color(bolt, GColorBlack); - path_layer_set_stroke_color(bolt, GColorClear); - layer_set_frame(&bolt->layer, &GRect(58, 48, 28, 60)); - layer_set_hidden(&bolt->layer, true); - layer_add_child(root, &bolt->layer); -} - -static void battery_window_appear(Window *window) { - BatteryTestData *data = window_get_user_data(window); - start_battery_polling(data); -} - -static void battery_window_disappear(Window *window) { - BatteryTestData *data = window_get_user_data(window); - stop_battery_polling(data); -} - -static void push_battery_test_window(MfgFuncTestData *app_data) { - static BatteryTestData s_battery_test_data; - s_battery_test_data = (BatteryTestData) { - .app_data = app_data, - .poll_timer_id = TIMER_INVALID_ID, - }; - - // Battery Charge test window: - Window *battery_window = &app_data->battery_window; - window_init(battery_window, WINDOW_NAME("Mfg Func Test Battery")); - window_set_overrides_back_button(battery_window, true); - window_set_user_data(battery_window, app_data); - window_set_click_config_provider_with_context(battery_window, - (ClickConfigProvider) battery_window_click_config_provider, battery_window); - window_set_window_handlers(battery_window, &(WindowHandlers) { - .load = battery_window_load, - .appear = battery_window_appear, - .disappear = battery_window_disappear - }); - window_set_user_data(battery_window, &s_battery_test_data); - window_set_fullscreen(battery_window, true); - - const bool animated = false; - window_stack_push(battery_window, animated); -} - diff --git a/src/fw/mfg/mfg_apps/mfg_func_test_black.h b/src/fw/mfg/mfg_apps/mfg_func_test_black.h deleted file mode 100644 index 3c0510333a..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_func_test_black.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "mfg_func_test_buttons.h" -#include "applib/ui/ui.h" - -static void black_window_button_up(ClickRecognizerRef recognizer, Window *window) { - MfgFuncTestData *app_data = (MfgFuncTestData*)window_get_user_data(window); - - mfg_func_test_append_bits(MfgFuncTestBitBlackTestPassed); - app_data->black_test_done = true; - vibes_short_pulse(); - - const bool animated = false; - window_stack_pop(animated); -} - -static void black_window_click_config_provider(void *context) { - for (ButtonId button_id = BUTTON_ID_BACK; button_id < NUM_BUTTONS; ++button_id) { - window_raw_click_subscribe(button_id, NULL, (ClickHandler) black_window_button_up, context); - } -} - -static void push_black_test_window(MfgFuncTestData *app_data) { - Window *black_window = &app_data->black_window; - window_init(black_window, WINDOW_NAME("Mfg Func Test Black")); - window_set_background_color(black_window, GColorBlack); - window_set_click_config_provider_with_context(black_window, - (ClickConfigProvider) black_window_click_config_provider, black_window); - window_set_user_data(black_window, app_data); - window_set_fullscreen(black_window, true); - - const bool animated = false; - window_stack_push(black_window, animated); -} diff --git a/src/fw/mfg/mfg_apps/mfg_func_test_buttons.h b/src/fw/mfg/mfg_apps/mfg_func_test_buttons.h deleted file mode 100644 index 061ecc9e5d..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_func_test_buttons.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/button.h" -#include "drivers/backlight.h" - -#include "util/trig.h" - -#include "applib/ui/ui.h" -#include "applib/ui/window_private.h" - -#include "applib/fonts/fonts.h" - -typedef struct { - MfgFuncTestData *app_data; - ButtonId button_id; - TextLayer label; - PathLayer arrow; -} ButtonTestData; - -static const GPathInfo ARROW_PATH_INFO = { - .num_points = 7, - .points = (GPoint []) {{0, 14}, {29, 14}, {29, 0}, {54, 25}, {29, 50}, {29, 36}, {0, 36}} -}; - -static const GPathInfo BOLT_PATH_INFO = { - .num_points = 6, - .points = (GPoint []) {{21, 0}, {14, 26}, {28, 26}, {7, 60}, {14, 34}, {0, 34}} -}; - -static void move_arrow_to_button(ButtonTestData *data, ButtonId id) { -#define ARROW_SIZE {54, 50} - static const GRect ARROW_RECTS[] = { - {{2, 30}, ARROW_SIZE}, // BACK - {{88, 2}, ARROW_SIZE}, // UP - {{88, 59}, ARROW_SIZE}, // SELECT - {{88, 116}, ARROW_SIZE}, // DOWN - }; - layer_set_frame(&data->arrow.layer, &ARROW_RECTS[id]); - gpath_rotate_to(&data->arrow.path, id == BUTTON_ID_BACK ? (TRIG_MAX_ANGLE / 2) : 0); - gpath_move_to(&data->arrow.path, id == BUTTON_ID_BACK ? GPoint(54, 50) : GPoint(0, 0)); -} - -static void button_window_button_up(ClickRecognizerRef recognizer, Window *window) { - ButtonTestData *data = window_get_user_data(window); - ButtonId button_id = click_recognizer_get_button_id(recognizer); - - if (data->button_id == button_id) { - ++(data->button_id); - if (data->button_id > BUTTON_ID_DOWN) { - mfg_func_test_append_bits(MfgFuncTestBitButtonTestPassed); - data->app_data->button_test_done = true; - data->button_id = BUTTON_ID_BACK; - const bool animated = false; - window_stack_pop(animated); - } else { - move_arrow_to_button(data, data->button_id); - } - backlight_set_brightness(0); - } -} - -static void button_window_button_down(ClickRecognizerRef recognizer, Window *window) { - ButtonTestData *data = window_get_user_data(window); - if (data->button_id == click_recognizer_get_button_id(recognizer)) { - backlight_set_brightness(0xffff); - } -} - -static void button_window_click_config_provider(void *context) { - for (ButtonId button_id = BUTTON_ID_BACK; button_id < NUM_BUTTONS; ++button_id) { - window_raw_click_subscribe(button_id, - (ClickHandler) button_window_button_down, (ClickHandler) button_window_button_up, context); - } -} - -static void button_window_load(Window *window) { - ButtonTestData *data = window_get_user_data(window); - Layer *root = &window->layer; - - TextLayer *label = &data->label; - text_layer_init(label, GRect(0, 0, 144, 40)); - text_layer_set_background_color(label, GColorClear); - text_layer_set_text_color(label, GColorBlack); - text_layer_set_text(label, "Press Button"); - text_layer_set_font(label, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - layer_add_child(root, &label->layer); - - PathLayer *arrow = &data->arrow; - path_layer_init(arrow, &ARROW_PATH_INFO); - path_layer_set_fill_color(arrow, GColorBlack); - path_layer_set_stroke_color(arrow, GColorClear); - layer_add_child(root, &arrow->layer); - - move_arrow_to_button(data, data->button_id); -} - -static void push_button_test_window(MfgFuncTestData *app_data) { - static ButtonTestData s_button_test_data = { - .button_id = BUTTON_ID_BACK, - }; - - s_button_test_data.app_data = app_data; - - Window *button_window = &app_data->button_window; - window_init(button_window, WINDOW_NAME("Mfg Func Test Buttons")); - window_set_overrides_back_button(button_window, true); - window_set_click_config_provider_with_context(button_window, - (ClickConfigProvider) button_window_click_config_provider, button_window); - window_set_window_handlers(button_window, &(WindowHandlers) { - .load = button_window_load - }); - window_set_user_data(button_window, &s_button_test_data); - window_set_fullscreen(button_window, true); - const bool animated = false; - window_stack_push(button_window, animated); -} diff --git a/src/fw/mfg/mfg_apps/mfg_func_test_version.h b/src/fw/mfg/mfg_apps/mfg_func_test_version.h deleted file mode 100644 index e83e1b5a1d..0000000000 --- a/src/fw/mfg/mfg_apps/mfg_func_test_version.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/backlight.h" -#include "drivers/button.h" -#include "applib/fonts/fonts.h" -#include "git_version.auto.h" -#include "applib/graphics/utf8.h" -#include "resource/resource.h" -#include "resource/resource_ids.auto.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "applib/ui/ui.h" -#include "applib/ui/window_private.h" -#include "system/version.h" - -#include -#include -#include - -typedef struct { - MfgFuncTestData *app_data; - TextLayer label; -} VersionData; - -static int s_click_count; -static char* s_version_str; - -static void version_window_button_up(ClickRecognizerRef recognizer, Window *window) { - // The button-up event from launching the QC app will propagate to - // this window, so instead we wait for two button-up events before - // proceeding - if (s_click_count++ > 0) { - const bool animated = false; - window_stack_pop(animated); - } -} - -static void version_window_click_config_provider(void *context) { - for (ButtonId button_id = BUTTON_ID_BACK; button_id < NUM_BUTTONS; ++button_id) { - window_raw_click_subscribe(button_id, NULL, (ClickHandler) version_window_button_up, context); - } -} - - -static void version_get_version_str(char* buf, int size) { - FirmwareMetadata normal_fw; - FirmwareMetadata recovery_fw; - - version_copy_running_fw_metadata(&normal_fw); - version_copy_recovery_fw_metadata(&recovery_fw); - - ResourceVersion system_res = resource_get_system_version(); - - uint32_t bootloader_version = boot_version_read(); - uint32_t system_res_version = system_res.crc; - - char* normal_version = (char*) normal_fw.version_short; - if (!utf8_is_valid_string(normal_version)) { - normal_version = "???"; - } - - char* recovery_version = (char*) recovery_fw.version_short; - if (!utf8_is_valid_string(recovery_version)) { - recovery_version = "???"; - } - - sniprintf(buf, size, - "n:%s\nr:%s\nb:0x%"PRIx32"\ns:0x%"PRIx32, - normal_version, - recovery_version, - bootloader_version, - system_res_version); -} - -static void version_window_load(Window *window) { - VersionData *data = window_get_user_data(window); - Layer *root = &window->layer; - - s_version_str = (char*) malloc(64); - version_get_version_str(s_version_str, 64); - - TextLayer *label = &data->label; - text_layer_init(label, GRect(2, 2, 142, 164)); - text_layer_set_background_color(label, GColorClear); - text_layer_set_text_color(label, GColorBlack); - text_layer_set_text(label, s_version_str); - text_layer_set_font(label, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - layer_add_child(root, &label->layer); - - s_click_count = 0; -} - -static void version_window_unload(Window* window) { - free(s_version_str); - (void)window; -} - -static void push_version_window(MfgFuncTestData *app_data) { - static VersionData s_version_data; - s_version_data.app_data = app_data; - - Window* version_window = &app_data->version_window; - window_init(version_window, WINDOW_NAME("Mfg Func Test Version")); - window_set_overrides_back_button(version_window, true); - window_set_click_config_provider_with_context(version_window, - (ClickConfigProvider) version_window_click_config_provider, version_window); - window_set_window_handlers(version_window, &(WindowHandlers) { - .load = version_window_load, - .unload = version_window_unload - }); - window_set_user_data(version_window, &s_version_data); - window_set_fullscreen(version_window, true); - - const bool animated = false; - window_stack_push(version_window, animated); -} - diff --git a/src/fw/mfg/mfg_command.c b/src/fw/mfg/mfg_command.c index e94aef22f8..1c8ac15ee1 100644 --- a/src/fw/mfg/mfg_command.c +++ b/src/fw/mfg/mfg_command.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mfg_command.h" @@ -52,30 +39,6 @@ void command_color_write(const char* color_num) { } } -void command_disp_offset_read(void) { - char buffer[16]; - prompt_send_response_fmt(buffer, sizeof(buffer), "X: %"PRId16" Y: %"PRId16, - mfg_info_get_disp_offsets().x, - mfg_info_get_disp_offsets().y); -} - -void command_disp_offset_write(const char* offset_x_str, const char* offset_y_str) { - char *nonnumeric_x, *nonnumeric_y; - int8_t offset_x = strtol(offset_x_str, &nonnumeric_x, 10); - if (*nonnumeric_x) { - prompt_send_response("Invalid x offset"); - } - - int8_t offset_y = strtol(offset_y_str, &nonnumeric_y, 10); - if (*nonnumeric_y) { - prompt_send_response("Invalid y offset"); - } - - if (!*nonnumeric_x && !*nonnumeric_y) { - mfg_info_set_disp_offsets((GPoint) {offset_x, offset_y}); - } -} - void command_rtcfreq_read(void) { char buffer[10]; prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRIu32, mfg_info_get_rtc_freq()); @@ -113,82 +76,3 @@ void command_model_write(const char* model) { } } -#if BOOTLOADER_TEST_STAGE1 -#include "bootloader_test_bin.auto.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "system/firmware_storage.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/reset.h" -#include "util/crc32.h" -#include "util/legacy_checksum.h" -static void prv_bootloader_test_copy(uint32_t flash_addr, uint32_t flash_end) { - const uint8_t *bin; - size_t size; - - bin = s_bootloader_test_stage2; - size = sizeof(s_bootloader_test_stage2); - flash_region_erase_optimal_range(flash_addr, - flash_addr, - flash_addr+size+sizeof(FirmwareDescription), - flash_end); - - FirmwareDescription fw_desc = { - .description_length = sizeof(FirmwareDescription), - .firmware_length = size, - .checksum = 0, - }; -#if CAPABILITY_HAS_DEFECTIVE_FW_CRC - fw_desc.checksum = legacy_defective_checksum_memory(bin, size); -#else - fw_desc.checksum = crc32(CRC32_INIT, bin, size); -#endif - - flash_write_bytes((uint8_t *)&fw_desc, flash_addr, sizeof(FirmwareDescription)); - flash_write_bytes(bin, flash_addr+sizeof(FirmwareDescription), size); -} - -#define BLTEST_LOG(x...) pbl_log(LOG_LEVEL_ALWAYS, __FILE__, __LINE__, x) - -void command_bootloader_test(const char *dest_type) { - prompt_command_finish(); - - BLTEST_LOG("BOOTLOADER TEST STAGE 1"); - boot_bit_set(BOOT_BIT_FW_STABLE); - - BLTEST_LOG("STAGE 1 -- Setting test boot bits"); - boot_bit_clear(BOOT_BIT_BOOTLOADER_TEST_A | BOOT_BIT_BOOTLOADER_TEST_B); - boot_bit_set(BOOT_BIT_BOOTLOADER_TEST_A); - - bool as_fw = true; - if (strcmp(dest_type, "prf") == 0) { - as_fw = false; - } - if (as_fw) { - BLTEST_LOG("STAGE 1 -- Copying STAGE 2 to scratch"); - prv_bootloader_test_copy(FLASH_REGION_FIRMWARE_DEST_BEGIN, - FLASH_REGION_FIRMWARE_DEST_END); - - BLTEST_LOG("STAGE 1 -- Marking new FW boot bit"); - boot_bit_set(BOOT_BIT_NEW_FW_AVAILABLE); - } else { - BLTEST_LOG("STAGE 1 -- Copying STAGE 2 to PRF"); - flash_prf_set_protection(false); - prv_bootloader_test_copy(FLASH_REGION_SAFE_FIRMWARE_BEGIN, FLASH_REGION_SAFE_FIRMWARE_END); - - BLTEST_LOG("STAGE 1 -- Marking PRF boot bit"); - boot_bit_set(BOOT_BIT_FORCE_PRF); - } - - BLTEST_LOG("STAGE 1 -- Rebooting"); - RebootReason reason = { RebootReasonCode_PrfReset, 0 }; - reboot_reason_set(&reason); - system_hard_reset(); -} -#else -void command_bootloader_test(void) { - prompt_send_response("Not configured for bootloader test"); -} -#endif diff --git a/src/fw/mfg/mfg_command.h b/src/fw/mfg/mfg_command.h index 8661b4bbca..a1f879e803 100644 --- a/src/fw/mfg/mfg_command.h +++ b/src/fw/mfg/mfg_command.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/mfg/mfg_info.c b/src/fw/mfg/mfg_info.c index 68f6371a07..be703f6824 100644 --- a/src/fw/mfg/mfg_info.c +++ b/src/fw/mfg/mfg_info.c @@ -1,26 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mfg/mfg_serials.h" //! @file mfg_info.c //! -//! This file implements mfg_info functions where the storage is the same for both tintin and -//! snowy, mostly for things that are stored in OTP. See the tintin/mfg_info.c and -//! snowy/mfg_info.c for the board specific implementations. +//! This file implements mfg_info functions where the storage is in OTP. See the per-board +//! mfg_info.c files for the board specific implementations. void mfg_info_get_serialnumber(char *serial_number, size_t serial_number_size) { strncpy(serial_number, mfg_get_serial_number(), serial_number_size); diff --git a/src/fw/mfg/mfg_info.h b/src/fw/mfg/mfg_info.h index e492ef8af2..89af0af73e 100644 --- a/src/fw/mfg/mfg_info.h +++ b/src/fw/mfg/mfg_info.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -54,11 +41,6 @@ uint32_t mfg_info_get_rtc_freq(void); void mfg_info_set_rtc_freq(uint32_t rtc_freq); -// x offset +/- for display -GPoint mfg_info_get_disp_offsets(void); - -void mfg_info_set_disp_offsets(GPoint p); - //! The number of bytes in our model name, including the null-terminator. #define MFG_INFO_MODEL_STRING_LENGTH 16 @@ -75,9 +57,6 @@ void mfg_info_set_model(const char* model); //! time but which is not customized to the individual unit. void mfg_info_update_constant_data(void); -//! @internal -bool mfg_info_is_hrm_present(void); - typedef enum { MfgTest_Vibe, MfgTest_Display, @@ -96,3 +75,14 @@ bool mfg_info_get_test_result(MfgTest test); void mfg_info_write_als_result(uint32_t reading); uint32_t mfg_info_get_als_result(void); + +//! Value returned by mfg_info_get_vibe_cali() when the unit has not been +//! calibrated. +#define MFG_INFO_VIBE_CALI_INVALID 0xFF + +//! Get the stored vibration motor calibration (driver-specific trim value). +//! @return the stored value, or MFG_INFO_VIBE_CALI_INVALID if not calibrated. +uint8_t mfg_info_get_vibe_cali(void); + +//! Store the vibration motor calibration (driver-specific trim value). +void mfg_info_set_vibe_cali(uint8_t cali); diff --git a/src/fw/mfg/mfg_mode/mfg_factory_mode.c b/src/fw/mfg/mfg_mode/mfg_factory_mode.c index a81ef2069b..eb039999c4 100644 --- a/src/fw/mfg/mfg_mode/mfg_factory_mode.c +++ b/src/fw/mfg/mfg_mode/mfg_factory_mode.c @@ -1,28 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mfg_factory_mode.h" -#include "apps/prf_apps/mfg_menu_app.h" +#include "apps/prf/mfg_menu.h" #include "board/board.h" #include "kernel/event_loop.h" #include "kernel/low_power.h" #include "process_management/app_manager.h" -#include "services/prf/accessory/accessory_manager.h" -#include "services/prf/idle_watchdog.h" static bool s_mfg_mode = false; @@ -38,12 +23,6 @@ void mfg_enter_mfg_mode(void) { if (!s_mfg_mode) { s_mfg_mode = true; -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - accessory_manager_set_state(AccessoryInputStateMfg); -#endif - - prf_idle_watchdog_stop(); - low_power_exit(); } } diff --git a/src/fw/mfg/mfg_mode/mfg_factory_mode.h b/src/fw/mfg/mfg_mode/mfg_factory_mode.h index c9d32eabd1..3a4c381e20 100644 --- a/src/fw/mfg/mfg_mode/mfg_factory_mode.h +++ b/src/fw/mfg/mfg_mode/mfg_factory_mode.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/mfg/mfg_mode/mfg_selftest.c b/src/fw/mfg/mfg_mode/mfg_selftest.c deleted file mode 100644 index c7e1205c46..0000000000 --- a/src/fw/mfg/mfg_mode/mfg_selftest.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bluetooth/bt_test.h" -#include "console/prompt.h" -#include "drivers/button.h" -#include "drivers/flash.h" -#include "drivers/i2c.h" -#include "drivers/imu.h" -#include "drivers/imu/bmi160/bmi160.h" -#include "util/bitset.h" -#include "util/size.h" - -#include -#include -#include - -struct SelfTestCase { - char name[16]; - bool (*func)(void); -}; - -// TODO: PBL-34018 This file is a mess. We should clean it up to better chose the right functions -// and list of test cases depending on the capabilities of the platform. - -// Here's a clever trick: selftest functions which may or may not be -// linked into the firmware depending on the ./waf configure settings -// (read: IMU) are redeclared as weak so that it is not a linker error -// to have missing definitions for these functions. They simply link as -// zero (null pointer). This works out perfectly as the selftest code -// considers a null function pointer to mean not-implemented, which is -// exactly the outcome we want! -bool bmi160_query_whoami(void) WEAK; -bool bma255_query_whoami(void) WEAK; -bool flash_check_whoami(void) WEAK; -bool accel_manager_run_selftest(void) WEAK; -bool gyro_manager_run_selftest(void) WEAK; -bool mag3110_check_whoami(void) WEAK; -bool snowy_mag3110_query_whoami(void) WEAK; -bool mic_selftest(void) WEAK; - -// NULL function pointer means test is not implemented -static const struct SelfTestCase s_test_cases[] = { -#if PLATFORM_SILK - { "Accel Comm", bma255_query_whoami }, -#else - { "IMU Comm", bmi160_query_whoami }, -#endif - { "Accel ST", accel_manager_run_selftest }, -#if !PLATFORM_SILK - { "Gyro ST", gyro_manager_run_selftest }, - { "MAG3110 Comm", mag3110_check_whoami }, -#endif -#if CAPABILITY_HAS_APPLE_MFI - { "Apple ACP I2C", bt_driver_test_mfi_chip_selftest }, -#endif - { "BT Module", bt_driver_test_selftest }, - { "Flash Comm", flash_check_whoami }, - { "Mic", mic_selftest }, - { "Buttons", button_selftest }, -}; - -static char* bool_to_pass_fail(bool b) { - if (b) { - return "PASS"; - } else { - return "FAIL"; - } -} - -//! Runs all the test cases -//! @return a bitset of tests that passed or failed -uint32_t mfg_selftest(void) { - uint32_t result = 0; - for (uint32_t i = 0; i < ARRAY_LENGTH(s_test_cases); i++) { - const struct SelfTestCase* test = s_test_cases + i; - bool test_passed = false; - if (test->func != 0) { - test_passed = test->func(); - } - bitset32_update(&result, i, test_passed); - } - return result; -} - -void command_selftest(void) { - char buffer[32]; - uint32_t result = mfg_selftest(); - for (uint32_t i = 0; i < ARRAY_LENGTH(s_test_cases); i++) { - const struct SelfTestCase* test = s_test_cases + i; - char *pass_fail; - if (test->func != 0) { // Test is implemented - pass_fail = bool_to_pass_fail(bitset32_get(&result, i)); - } else { - pass_fail = "NYI"; - } - prompt_send_response_fmt(buffer, 32, "%15s: %s", test->name, pass_fail); - } -} diff --git a/src/fw/mfg/mfg_serials.c b/src/fw/mfg/mfg_serials.c index 33c3f6c7cd..70b0bc236c 100644 --- a/src/fw/mfg/mfg_serials.c +++ b/src/fw/mfg/mfg_serials.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -21,7 +8,6 @@ #include "console/prompt.h" #include "util/size.h" -#if PLATFORM_ASTERIX || PLATFORM_OBELIX static const uint8_t OTP_SERIAL_SLOT_INDICES[] = { OTP_SERIAL, }; @@ -31,21 +17,6 @@ static const uint8_t OTP_PCBA_SLOT_INDICES[] = { static const uint8_t OTP_HWVER_SLOT_INDICES[] = { OTP_HWVER }; -#else -static const uint8_t OTP_SERIAL_SLOT_INDICES[] = { - OTP_SERIAL1, OTP_SERIAL2, OTP_SERIAL3, OTP_SERIAL4, OTP_SERIAL5 -}; -static const uint8_t OTP_PCBA_SLOT_INDICES[] = { - OTP_PCBA_SERIAL1, OTP_PCBA_SERIAL2, OTP_PCBA_SERIAL3 -}; -#if PLATFORM_SILK || PLATFORM_CALCULUS || PLATFORM_ROBERT -static const uint8_t OTP_HWVER_SLOT_INDICES[] = { - OTP_HWVER1, OTP_HWVER2, OTP_HWVER3, OTP_HWVER4, OTP_HWVER5 -}; -#else -static const uint8_t OTP_HWVER_SLOT_INDICES[] = {OTP_HWVER1}; -#endif -#endif static const char DUMMY_SERIAL[MFG_SERIAL_NUMBER_SIZE + 1] = "XXXXXXXXXXXX"; // FIXME: shouldn't the dummy HWVER be 9 X's? @@ -219,13 +190,13 @@ static void mfg_print_feedback(const MfgSerialsResult result, const uint8_t inde } } -#if defined(IS_BIGBOARD) +#if defined(CONFIG_IS_BIGBOARD) #include #include "drivers/rtc.h" #include "system/logging.h" -#if !MICRO_FAMILY_NRF5 +#ifndef CONFIG_SOC_NRF52 static void prv_get_not_so_unique_serial(char *serial_number) { // Contains 96 bits (12 bytes) that uniquely identify the STM32F2/F4 MCUs: const uint8_t *DEVICE_ID_REGISTER = (const uint8_t *) 0x1FFF7A10; @@ -254,7 +225,7 @@ void mfg_write_bigboard_serial_number(void) { serial_number[2] = 0; // Check whether the previous not-so-unique SN or the no SN ("XXXXXXXXXXXX") has been written: -#if !MICRO_FAMILY_NRF5 +#ifndef CONFIG_SOC_NRF52 prv_get_not_so_unique_serial(serial_number); #endif const char *current_serial_number = mfg_get_serial_number(); diff --git a/src/fw/mfg/mfg_serials.h b/src/fw/mfg/mfg_serials.h index 3380160c06..9e0ee52a12 100644 --- a/src/fw/mfg/mfg_serials.h +++ b/src/fw/mfg/mfg_serials.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -55,7 +42,7 @@ typedef enum MfgSerialsResult { //! MfgSerialsResultFailIncorrectLength if the serial_size was not 13. MfgSerialsResult mfg_write_serial_number(const char* serial, size_t serial_size, uint8_t *out_index); -#if defined(IS_BIGBOARD) +#if defined(CONFIG_IS_BIGBOARD) //! Writes a fake serial number based on the unique identifier of the MCU void mfg_write_bigboard_serial_number(void); #endif diff --git a/src/fw/mfg/obelix/mfg_info.c b/src/fw/mfg/obelix/mfg_info.c index 62ef3fa153..9fd8d046dc 100644 --- a/src/fw/mfg/obelix/mfg_info.c +++ b/src/fw/mfg/obelix/mfg_info.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mfg/mfg_info.h" #include "drivers/flash.h" @@ -25,6 +12,7 @@ typedef struct { uint32_t color; char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string + uint8_t vibe_cali; //!< Vibration motor trim, MFG_INFO_VIBE_CALI_INVALID if unset } MfgData; static void prv_update_struct(const MfgData *data) { @@ -40,9 +28,10 @@ static MfgData prv_fetch_struct(void) { // Fallback data if not available if (result.data_version != CURRENT_DATA_VERSION) { result.data_version = CURRENT_DATA_VERSION; - result.color = WATCH_INFO_COLOR_COREDEVICES_CT2_BLACK; - strncpy(result.model, "CT2-BK", sizeof(result.model)); + result.color = WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_GREY; + strncpy(result.model, "obelix", sizeof(result.model)); result.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; + result.vibe_cali = MFG_INFO_VIBE_CALI_INVALID; } return result; @@ -70,8 +59,14 @@ void mfg_info_set_model(const char* model) { prv_update_struct(&data); } -bool mfg_info_is_hrm_present(void) { - return true; +uint8_t mfg_info_get_vibe_cali(void) { + return prv_fetch_struct().vibe_cali; +} + +void mfg_info_set_vibe_cali(uint8_t cali) { + MfgData data = prv_fetch_struct(); + data.vibe_cali = cali; + prv_update_struct(&data); } uint32_t mfg_info_get_rtc_freq(void) { @@ -83,15 +78,6 @@ void mfg_info_set_rtc_freq(uint32_t rtc_freq) { // Not implemented. } -GPoint mfg_info_get_disp_offsets(void) { - // Not implemented. Can just assume no offset - return (GPoint) {}; -} - -void mfg_info_set_disp_offsets(GPoint p) { - // Not implemented. -} - void mfg_info_update_constant_data(void) { // Not implemented } diff --git a/src/fw/mfg/qemu/mfg_info.c b/src/fw/mfg/qemu/mfg_info.c new file mode 100644 index 0000000000..eb611c5153 --- /dev/null +++ b/src/fw/mfg/qemu/mfg_info.c @@ -0,0 +1,81 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "mfg/mfg_info.h" +#include "drivers/flash.h" +#include "flash_region/flash_region.h" + +#define CURRENT_DATA_VERSION 0 + +typedef struct { + uint32_t data_version; + + uint32_t color; + char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string + uint8_t vibe_cali; //!< Vibration motor trim, MFG_INFO_VIBE_CALI_INVALID if unset +} MfgData; + +static void prv_update_struct(const MfgData *data) { + flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); + flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); +} + +static MfgData prv_fetch_struct(void) { + MfgData result; + + flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); + + // Fallback data if not available + if (result.data_version != CURRENT_DATA_VERSION) { + result.data_version = CURRENT_DATA_VERSION; + result.color = WATCH_INFO_COLOR_COREDEVICES_PT2_BLACK_GREY; + strncpy(result.model, "qemu", sizeof(result.model)); + result.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; + result.vibe_cali = MFG_INFO_VIBE_CALI_INVALID; + } + + return result; +} + +WatchInfoColor mfg_info_get_watch_color(void) { + return prv_fetch_struct().color; +} + +void mfg_info_set_watch_color(WatchInfoColor color) { + MfgData data = prv_fetch_struct(); + data.color = color; + prv_update_struct(&data); +} + +void mfg_info_get_model(char* buffer) { + MfgData data = prv_fetch_struct(); + strcpy(buffer, data.model); +} + +void mfg_info_set_model(const char* model) { + MfgData data = prv_fetch_struct(); + strncpy(data.model, model, sizeof(data.model)); + data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; + prv_update_struct(&data); +} + +uint8_t mfg_info_get_vibe_cali(void) { + return prv_fetch_struct().vibe_cali; +} + +void mfg_info_set_vibe_cali(uint8_t cali) { + MfgData data = prv_fetch_struct(); + data.vibe_cali = cali; + prv_update_struct(&data); +} + +uint32_t mfg_info_get_rtc_freq(void) { + return 0U; +} + +void mfg_info_set_rtc_freq(uint32_t rtc_freq) { + (void)rtc_freq; +} + +void mfg_info_update_constant_data(void) { +} diff --git a/src/fw/mfg/results_ui.c b/src/fw/mfg/results_ui.c deleted file mode 100644 index f3c01dc394..0000000000 --- a/src/fw/mfg/results_ui.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "results_ui.h" - -#include "applib/ui/app_window_stack.h" - -static void prv_record_and_exit(MfgResultsUI *results_ui, bool result) { - mfg_info_write_test_result(results_ui->test, result); - - if (results_ui->results_cb) { - results_ui->results_cb(); - } - - app_window_stack_pop(true); -} - -static void prv_up_click_handler(ClickRecognizerRef recognizer, void *data) { - prv_record_and_exit(data, true); -} - -static void prv_down_click_handler(ClickRecognizerRef recognizer, void *data) { - prv_record_and_exit(data, false); -} - -static void prv_click_config_provider(void *data) { - window_single_click_subscribe(BUTTON_ID_UP, prv_up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_down_click_handler); -} - -void mfg_results_ui_init(MfgResultsUI *results_ui, MfgTest test, Window *window) { - GRect bounds = window->layer.bounds; - bounds.size.w -= 5; - bounds.size.h = 40; - bounds.origin.y += 5; - - TextLayer *pass = &results_ui->pass_text_layer; - text_layer_init(pass, &bounds); - text_layer_set_font(pass, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(pass, GTextAlignmentRight); - text_layer_set_text(pass, "Pass"); - layer_add_child(&window->layer, &pass->layer); - - bounds.origin.y = 120; - TextLayer *fail = &results_ui->fail_text_layer; - text_layer_init(fail, &bounds); - text_layer_set_font(fail, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(fail, GTextAlignmentRight); - text_layer_set_text(fail, "Fail"); - layer_add_child(&window->layer, &fail->layer); - - results_ui->test = test; - - window_set_click_config_provider_with_context(window, prv_click_config_provider, results_ui); -} - -void mfg_results_ui_set_callback(MfgResultsUI *ui, MfgResultsCallback cb) { - ui->results_cb = cb; -} diff --git a/src/fw/mfg/results_ui.h b/src/fw/mfg/results_ui.h deleted file mode 100644 index bcb2ac81a1..0000000000 --- a/src/fw/mfg/results_ui.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/window.h" -#include "applib/ui/text_layer.h" -#include "mfg/mfg_info.h" - -typedef void (*MfgResultsCallback)(void); - -typedef struct { - MfgTest test; - - TextLayer pass_text_layer; - TextLayer fail_text_layer; - - MfgResultsCallback results_cb; -} MfgResultsUI; - -void mfg_results_ui_init(MfgResultsUI *results_ui, MfgTest test, Window *window); -void mfg_results_ui_set_callback(MfgResultsUI *ui, MfgResultsCallback cb); diff --git a/src/fw/mfg/robert/mfg_info.c b/src/fw/mfg/robert/mfg_info.c deleted file mode 100644 index 9aa11e8163..0000000000 --- a/src/fw/mfg/robert/mfg_info.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/mfg_info.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "mfg/mfg_serials.h" -#include "system/logging.h" - -//! Used to version this struct if we have to add additional fields in the future. -#define CURRENT_DATA_VERSION 2 - -typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string -} MfgData; - -static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); -} - -static MfgData prv_fetch_struct(void) { - MfgData result; - - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); - - switch (result.data_version) { - case CURRENT_DATA_VERSION: - // Our data is valid. Fall through. - break; - case 1: - // Our data is out of date. We need to do a conversion to populate the new model field. - result.data_version = CURRENT_DATA_VERSION; - result.model[0] = '\0'; - break; - default: - // No data present, just return an initialized struct with default values. - return (MfgData) { .data_version = CURRENT_DATA_VERSION }; - } - - return result; -} - -WatchInfoColor mfg_info_get_watch_color(void) { - return prv_fetch_struct().color; -} - -void mfg_info_set_watch_color(WatchInfoColor color) { - MfgData data = prv_fetch_struct(); - data.color = color; - prv_update_struct(&data); -} - -uint32_t mfg_info_get_rtc_freq(void) { - return prv_fetch_struct().rtc_freq; -} - -#if MANUFACTURING_FW -void mfg_info_set_rtc_freq(uint32_t rtc_freq) { - MfgData data = prv_fetch_struct(); - data.rtc_freq = rtc_freq; - prv_update_struct(&data); -} -#endif - -void mfg_info_get_model(char* buffer) { - MfgData data = prv_fetch_struct(); - strncpy(buffer, data.model, sizeof(data.model)); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; // Just in case -} - -void mfg_info_set_model(const char* model) { - MfgData data = prv_fetch_struct(); - strncpy(data.model, model, sizeof(data.model)); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; - prv_update_struct(&data); -} - -GPoint mfg_info_get_disp_offsets(void) { - // Not implemented. Can just assume no offset - return (GPoint) {}; -} - -void mfg_info_set_disp_offsets(GPoint p) { - // Not implemented. -} - -void mfg_info_update_constant_data(void) { - // No constant data required for Robert. -} - -bool mfg_info_is_hrm_present(void) { - return true; -} diff --git a/src/fw/mfg/silk/mfg_info.c b/src/fw/mfg/silk/mfg_info.c deleted file mode 100644 index 70b677c267..0000000000 --- a/src/fw/mfg/silk/mfg_info.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/mfg_info.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "mfg/mfg_serials.h" -#include "mfg/snowy/mfg_private.h" -#include "system/logging.h" - -//! Used to version this struct if we have to add additional fields in the future. -#define CURRENT_DATA_VERSION 3 - -typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string - - bool test_results[MfgTestCount]; //!< UI Test Results - uint32_t als_result; //!< Result for ALS reading -} MfgData; - -static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); -} - -static MfgData prv_fetch_struct(void) { - MfgData result; - - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); - - switch (result.data_version) { - case CURRENT_DATA_VERSION: - // Our data is valid. Fall through. - break; - case 1: - // Our data is out of date. We need to do a conversion to populate the new model field. - result.data_version = CURRENT_DATA_VERSION; - result.model[0] = '\0'; - break; - case 2: - result.data_version = CURRENT_DATA_VERSION; - memset(result.test_results, 0, sizeof(result.test_results)); - result.als_result = 0; - break; - default: - // No data present, just return an initialized struct with default values. - return (MfgData) { .data_version = CURRENT_DATA_VERSION }; - } - - return result; -} - -WatchInfoColor mfg_info_get_watch_color(void) { - return prv_fetch_struct().color; -} - -void mfg_info_set_watch_color(WatchInfoColor color) { - MfgData data = prv_fetch_struct(); - data.color = color; - prv_update_struct(&data); -} - -uint32_t mfg_info_get_rtc_freq(void) { - return prv_fetch_struct().rtc_freq; -} - -void mfg_info_set_rtc_freq(uint32_t rtc_freq) { - MfgData data = prv_fetch_struct(); - data.rtc_freq = rtc_freq; - prv_update_struct(&data); -} - -void mfg_info_get_model(char* buffer) { - MfgData data = prv_fetch_struct(); - strncpy(buffer, data.model, sizeof(data.model) + 0); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; // Just in case -} - -void mfg_info_set_model(const char* model) { - MfgData data = prv_fetch_struct(); - strncpy(data.model, model, sizeof(data.model)); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; - prv_update_struct(&data); -} - -GPoint mfg_info_get_disp_offsets(void) { - // Not implemented. Can just assume no offset - return (GPoint) {}; -} - -void mfg_info_set_disp_offsets(GPoint p) { - // Not implemented. -} - -void mfg_info_update_constant_data(void) { - // Not implemented -} - -#if CAPABILITY_HAS_BUILTIN_HRM -bool mfg_info_is_hrm_present(void) { -#if defined(TARGET_QEMU) || defined(IS_BIGBOARD) - return true; -#else - char model[MFG_INFO_MODEL_STRING_LENGTH]; - mfg_info_get_model(model); - if (!strcmp(model, "1002")) { // SilkHR - return true; - } - return false; -#endif -} -#endif - -#if MFG_INFO_RECORDS_TEST_RESULTS -void mfg_info_write_test_result(MfgTest test, bool pass) { - MfgData data = prv_fetch_struct(); - data.test_results[test] = pass; - prv_update_struct(&data); -} - -bool mfg_info_get_test_result(MfgTest test) { - MfgData data = prv_fetch_struct(); - return data.test_results[test]; -} - -#include "console/prompt.h" -#define TEST_RESULT_TO_STR(test) ((data.test_results[(test)]) ? "PASS" : "FAIL") -void command_mfg_info_test_results(void) { - MfgData data = prv_fetch_struct(); - char buf[32]; - prompt_send_response_fmt(buf, 32, "Vibe: %s", TEST_RESULT_TO_STR(MfgTest_Vibe)); - prompt_send_response_fmt(buf, 32, "LCM: %s", TEST_RESULT_TO_STR(MfgTest_Display)); - prompt_send_response_fmt(buf, 32, "ALS: %s", TEST_RESULT_TO_STR(MfgTest_ALS)); - prompt_send_response_fmt(buf, 32, "Buttons: %s", TEST_RESULT_TO_STR(MfgTest_Buttons)); - - prompt_send_response_fmt(buf, 32, "ALS Reading: %"PRIu32, data.als_result); -} - -void mfg_info_write_als_result(uint32_t reading) { - MfgData data = prv_fetch_struct(); - data.als_result = reading; - prv_update_struct(&data); -} - -uint32_t mfg_info_get_als_result(void) { - MfgData data = prv_fetch_struct(); - return data.als_result; -} -#endif diff --git a/src/fw/mfg/snowy/boot_fpga.c b/src/fw/mfg/snowy/boot_fpga.c deleted file mode 100644 index 336f88a908..0000000000 --- a/src/fw/mfg/snowy/boot_fpga.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/snowy/mfg_private.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "mfg/snowy/snowy_boot.fpga.auto.h" -#include "util/attributes.h" - -#define BOOT_FPGA_FLASH_ADDR (FLASH_REGION_MFG_INFO_BEGIN + 0x10000) - -typedef struct PACKED { - uint16_t fpga_len; - uint16_t fpga_len_complemented; -} BootFPGAHeader; - -bool mfg_info_is_boot_fpga_bitstream_written(void) { - BootFPGAHeader header; - flash_read_bytes((void *)&header, BOOT_FPGA_FLASH_ADDR, sizeof header); - return (header.fpga_len != 0xffff && - header.fpga_len_complemented != 0xffff); -} - -void mfg_info_write_boot_fpga_bitstream(void) { - // Store the bootloader FPGA in the MFG info flash region so that the - // bootloader can find it. - _Static_assert(sizeof s_boot_fpga < 1<<16, "FPGA bitstream too big"); - BootFPGAHeader fpga_header = { (uint16_t)sizeof s_boot_fpga, - (uint16_t)~sizeof s_boot_fpga }; - - _Static_assert( - (BOOT_FPGA_FLASH_ADDR + sizeof fpga_header + sizeof s_boot_fpga) - < FLASH_REGION_MFG_INFO_END, - "FPGA bitstream will overflow FLASH_REGION_MFG_INFO!"); - flash_write_bytes((const uint8_t *)&fpga_header, - BOOT_FPGA_FLASH_ADDR, sizeof fpga_header); - flash_write_bytes((const uint8_t *)s_boot_fpga, - BOOT_FPGA_FLASH_ADDR + sizeof fpga_header, - sizeof s_boot_fpga); -} diff --git a/src/fw/mfg/snowy/mfg_info.c b/src/fw/mfg/snowy/mfg_info.c deleted file mode 100644 index 5efd6ef0fe..0000000000 --- a/src/fw/mfg/snowy/mfg_info.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/mfg_info.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "mfg/mfg_serials.h" -#include "mfg/snowy/mfg_private.h" -#include "system/logging.h" - -//! Used to version this struct if we have to add additional fields in the future. -#define CURRENT_DATA_VERSION 2 - -typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string -} MfgData; - -static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); - mfg_info_write_boot_fpga_bitstream(); -} - -static MfgData prv_fetch_struct(void) { - MfgData result; - - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); - - switch (result.data_version) { - case CURRENT_DATA_VERSION: - // Our data is valid. Fall through. - break; - case 1: - // Our data is out of date. We need to do a conversion to populate the new model field. - result.data_version = CURRENT_DATA_VERSION; - result.model[0] = '\0'; - break; - default: - // No data present, just return an initialized struct with default values. - return (MfgData) { .data_version = CURRENT_DATA_VERSION }; - } - - return result; -} - -WatchInfoColor mfg_info_get_watch_color(void) { - return prv_fetch_struct().color; -} - -void mfg_info_set_watch_color(WatchInfoColor color) { - MfgData data = prv_fetch_struct(); - data.color = color; - prv_update_struct(&data); -} - -uint32_t mfg_info_get_rtc_freq(void) { - return prv_fetch_struct().rtc_freq; -} - -void mfg_info_set_rtc_freq(uint32_t rtc_freq) { - MfgData data = prv_fetch_struct(); - data.rtc_freq = rtc_freq; - prv_update_struct(&data); -} - -void mfg_info_get_model(char* buffer) { - MfgData data = prv_fetch_struct(); - strncpy(buffer, data.model, sizeof(data.model) + 0); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; // Just in case -} - -void mfg_info_set_model(const char* model) { - MfgData data = prv_fetch_struct(); - strncpy(data.model, model, sizeof(data.model)); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; - prv_update_struct(&data); -} - -GPoint mfg_info_get_disp_offsets(void) { - // Not implemented. Can just assume no offset - return (GPoint) {}; -} - -void mfg_info_set_disp_offsets(GPoint p) { - // Not implemented. -} - -void mfg_info_update_constant_data(void) { - if (mfg_info_is_boot_fpga_bitstream_written()) { - PBL_LOG(LOG_LEVEL_INFO, "Boot FPGA bitstream already in flash."); - } else { - PBL_LOG(LOG_LEVEL_INFO, "Writing boot FPGA bitstream to flash..."); - mfg_info_write_boot_fpga_bitstream(); - } -} diff --git a/src/fw/mfg/snowy/mfg_private.h b/src/fw/mfg/snowy/mfg_private.h deleted file mode 100644 index 563ab7b2bd..0000000000 --- a/src/fw/mfg/snowy/mfg_private.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void mfg_info_write_boot_fpga_bitstream(void); -bool mfg_info_is_boot_fpga_bitstream_written(void); diff --git a/src/fw/mfg/spalding/boot_fpga.c b/src/fw/mfg/spalding/boot_fpga.c deleted file mode 100644 index 2cd10bc712..0000000000 --- a/src/fw/mfg/spalding/boot_fpga.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/spalding/mfg_private.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "mfg/spalding/spalding_boot.fpga.auto.h" -#include "system/logging.h" -#include "util/attributes.h" -#include "util/crc32.h" - -#include - -const uintptr_t BOOT_FPGA_FLASH_ADDR = FLASH_REGION_MFG_INFO_BEGIN + 0x10000; - -typedef struct PACKED { - uint16_t fpga_len; - uint16_t fpga_len_complemented; -} BootFPGAHeader; - -bool mfg_info_is_boot_fpga_bitstream_written(void) { - BootFPGAHeader expected_fpga_header = { (uint16_t)sizeof s_boot_fpga, - (uint16_t)~sizeof s_boot_fpga }; - - BootFPGAHeader header; - flash_read_bytes((void *)&header, BOOT_FPGA_FLASH_ADDR, sizeof header); - if (header.fpga_len != expected_fpga_header.fpga_len || - header.fpga_len_complemented != expected_fpga_header.fpga_len_complemented) { - - PBL_LOG(LOG_LEVEL_DEBUG, "Boot FPGA length invalid, needs a rewrite"); - - // The length doesn't even match, we definitely need to update. - return false; - } - - // Just because the length is the same that doesn't mean we don't need to update the FPGA - // image. Compare CRCs to see if the new FPGA image is different. - uint32_t expected_crc = crc32(CRC32_INIT, s_boot_fpga, sizeof(s_boot_fpga)); - uint32_t stored_crc = flash_crc32( - BOOT_FPGA_FLASH_ADDR + sizeof(BootFPGAHeader), sizeof(s_boot_fpga)); - - PBL_LOG(LOG_LEVEL_DEBUG, "Comparing boot FPGA CRCs, expected 0x%"PRIx32" found 0x%"PRIx32, - expected_crc, stored_crc); - - return expected_crc == stored_crc; -} - -void mfg_info_write_boot_fpga_bitstream(void) { - // Store the bootloader FPGA in the MFG info flash region so that the - // bootloader can find it. - _Static_assert(sizeof s_boot_fpga < 1<<16, "FPGA bitstream too big"); - BootFPGAHeader fpga_header = { (uint16_t)sizeof s_boot_fpga, - (uint16_t)~sizeof s_boot_fpga }; - - // I have no idea why but clang really hates this assert - // - // ../../src/fw/mfg/spalding/boot_fpga.c:50:7: error: static_assert expression is not an - // integral constant expression - // (BOOT_FPGA_FLASH_ADDR + sizeof(BootFPGAHeader) + sizeof(s_boot_fpga)) - // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -#if !__clang__ - _Static_assert( - (BOOT_FPGA_FLASH_ADDR + sizeof(BootFPGAHeader) + sizeof(s_boot_fpga)) - < FLASH_REGION_MFG_INFO_END, - "FPGA bitstream will overflow FLASH_REGION_MFG_INFO!"); -#endif - - flash_write_bytes((const uint8_t *)&fpga_header, - BOOT_FPGA_FLASH_ADDR, sizeof fpga_header); - flash_write_bytes((const uint8_t *)s_boot_fpga, - BOOT_FPGA_FLASH_ADDR + sizeof fpga_header, - sizeof s_boot_fpga); -} diff --git a/src/fw/mfg/spalding/mfg_info.c b/src/fw/mfg/spalding/mfg_info.c deleted file mode 100644 index 6cd4d9444e..0000000000 --- a/src/fw/mfg/spalding/mfg_info.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/mfg_info.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "mfg/mfg_serials.h" -#include "mfg/spalding/mfg_private.h" -#include "system/logging.h" - -//! Used to version this struct if we have to add additional fields in the future. -#define CURRENT_DATA_VERSION 3 - -typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - char model[MFG_INFO_MODEL_STRING_LENGTH]; //!< Null terminated model string - int8_t disp_offset_x; - int8_t disp_offset_y; -} MfgData; - -static void prv_update_struct(const MfgData *data) { - flash_erase_subsector_blocking(FLASH_REGION_MFG_INFO_BEGIN); - flash_write_bytes((const uint8_t*) data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(*data)); - mfg_info_write_boot_fpga_bitstream(); -} - -static MfgData prv_fetch_struct(void) { - MfgData result; - - flash_read_bytes((uint8_t*) &result, FLASH_REGION_MFG_INFO_BEGIN, sizeof(result)); - - switch (result.data_version) { - case CURRENT_DATA_VERSION: - // Our data is valid. Fall through. - break; - case 2: - result.data_version = CURRENT_DATA_VERSION; - result.disp_offset_x = 0; - result.disp_offset_y = 0; - break; - case 1: - // Our data is out of date. We need to do a conversion to populate the new model field. - result.data_version = CURRENT_DATA_VERSION; - result.model[0] = '\0'; - result.disp_offset_x = 0; - result.disp_offset_y = 0; - break; - default: - // No data present, just return an initialized struct with default values. - return (MfgData) { .data_version = CURRENT_DATA_VERSION }; - } - - return result; -} - -WatchInfoColor mfg_info_get_watch_color(void) { - return prv_fetch_struct().color; -} - -void mfg_info_set_watch_color(WatchInfoColor color) { - MfgData data = prv_fetch_struct(); - data.color = color; - prv_update_struct(&data); -} - -GPoint mfg_info_get_disp_offsets(void) { - return (GPoint) { - .x = prv_fetch_struct().disp_offset_x, - .y = prv_fetch_struct().disp_offset_y - }; -} - -void mfg_info_set_disp_offsets(GPoint p) { - MfgData data = prv_fetch_struct(); - data.disp_offset_x = p.x; - data.disp_offset_y = p.y; - prv_update_struct(&data); -} - -uint32_t mfg_info_get_rtc_freq(void) { - return prv_fetch_struct().rtc_freq; -} - -void mfg_info_set_rtc_freq(uint32_t rtc_freq) { - MfgData data = prv_fetch_struct(); - data.rtc_freq = rtc_freq; - prv_update_struct(&data); -} - -void mfg_info_get_model(char* buffer) { - MfgData data = prv_fetch_struct(); - strncpy(buffer, data.model, sizeof(data.model) + 0); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; // Just in case -} - -void mfg_info_set_model(const char* model) { - MfgData data = prv_fetch_struct(); - strncpy(data.model, model, sizeof(data.model)); - data.model[MFG_INFO_MODEL_STRING_LENGTH - 1] = '\0'; - prv_update_struct(&data); -} - -void mfg_info_update_constant_data(void) { - if (mfg_info_is_boot_fpga_bitstream_written()) { - PBL_LOG(LOG_LEVEL_INFO, "Boot FPGA bitstream already in flash."); - } else { - PBL_LOG(LOG_LEVEL_INFO, "Writing boot FPGA bitstream to flash..."); - - // Read the mfg data and write it back again. The prv_update_struct function write in a fresh - // copy of the FPGA image as a side effect. - MfgData data = prv_fetch_struct(); - prv_update_struct(&data); - } -} diff --git a/src/fw/mfg/spalding/mfg_private.h b/src/fw/mfg/spalding/mfg_private.h deleted file mode 100644 index 563ab7b2bd..0000000000 --- a/src/fw/mfg/spalding/mfg_private.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void mfg_info_write_boot_fpga_bitstream(void); -bool mfg_info_is_boot_fpga_bitstream_written(void); diff --git a/src/fw/mfg/tintin/mfg_info.c b/src/fw/mfg/tintin/mfg_info.c deleted file mode 100644 index bbf699b2d4..0000000000 --- a/src/fw/mfg/tintin/mfg_info.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mfg/mfg_info.h" - -#include "console/dbgserial.h" - -#include "services/common/legacy/factory_registry.h" -#include "util/net.h" - -#define COLOR_KEY "mfg_color" -#define RTC_FREQ_KEY "mfg_rtcfreq" - -static uint32_t prv_get_uint32(const char* key) { - Record *rec = factory_registry_get(key, strlen(key), REGISTRY_SYSTEM_UUID); - if (rec && (rec->value_length == sizeof(uint32_t))) { - uint32_t *value = (uint32_t*)&rec->value; - return ntohl(*value); - } else { - return 0; - } -} - -static void prv_set_uint32(const char* key, uint32_t value) { - // We store everything in the factory registry in network order, I'm not sure why. - value = htonl(value); - - int error = factory_registry_add(key, strlen(key), REGISTRY_SYSTEM_UUID, 0, - (uint8_t*)&value, sizeof(value)); - - if (error) { - dbgserial_putstr("Error"); - return; - } - - factory_registry_write_to_flash(); -} - -WatchInfoColor mfg_info_get_watch_color(void) { - return prv_get_uint32(COLOR_KEY); -} - -void mfg_info_set_watch_color(WatchInfoColor color) { - prv_set_uint32(COLOR_KEY, color); -} - -uint32_t mfg_info_get_rtc_freq(void) { - return prv_get_uint32(RTC_FREQ_KEY); -} - -void mfg_info_set_rtc_freq(uint32_t rtc_freq) { - prv_set_uint32(RTC_FREQ_KEY, rtc_freq); -} - -void mfg_info_get_model(char* buffer) { - // Not implemented, tintin/bianca's won't have this programmed. - // FIXME: We could approximate this based on the watch color value. -} - -void mfg_info_set_model(const char* model) { - // Not implemented, we won't be using this firmware for manufacturing tintin/biancas. -} - -GPoint mfg_info_get_disp_offsets(void) { - // Not implemented. Can just assume no offset - return (GPoint) {}; -} - -void mfg_info_set_disp_offsets(GPoint p) { - // Not implemented. -} - -void mfg_info_update_constant_data(void) { - // No constant data required for tintin/bianca. -} diff --git a/src/fw/newlib_canary.c b/src/fw/newlib_canary.c index 2be40aa243..e31e0aa775 100644 --- a/src/fw/newlib_canary.c +++ b/src/fw/newlib_canary.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include <_ansi.h> diff --git a/src/fw/pebble_errors.h b/src/fw/pebble_errors.h deleted file mode 100644 index 73e47c16ba..0000000000 --- a/src/fw/pebble_errors.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -static const uint32_t ERROR_NO_ACTIVE_ERROR = 0; - -// FW Errors -static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501; -static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502; -static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503; -static const uint32_t ERROR_RESET_LOOP = 0xfe504504; -static const uint32_t ERROR_BAD_RESOURCES = 0xfe504505; - -// BT Errors -static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510; -static const uint32_t ERROR_CANT_START_LSE = 0xfe504511; -static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512; - -static const uint32_t ERROR_LOW_BATTERY = 0xfe504520; - diff --git a/src/fw/popups/alarm_popup.c b/src/fw/popups/alarm_popup.c index e7d28ca2f3..4bd3034d23 100644 --- a/src/fw/popups/alarm_popup.c +++ b/src/fw/popups/alarm_popup.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "alarm_popup.h" @@ -26,22 +13,28 @@ #include "kernel/pbl_malloc.h" #include "kernel/ui/modals/modal_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "services/common/new_timer/new_timer.h" -#include "services/normal/alarms/alarm.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/alarms/alarm.h" #include "util/time/time.h" #include #include -#if !PLATFORM_TINTIN -#include "services/normal/vibes/vibe_client.h" -#include "services/normal/vibes/vibe_score.h" +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_score.h" + +#ifdef CONFIG_SPEAKER +#include "applib/event_service_client.h" +#include "kernel/events.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/speaker/speaker_finish_reason.h" +#include "pbl/services/speaker/speaker_service.h" +#include "services/alarms/alarm_tones.h" #endif -#if !TINTIN_FORCE_FIT #define DIALOG_TIMEOUT_SNOOZE 2000 #define DIALOG_TIMEOUT_DISMISS DIALOG_TIMEOUT_SNOOZE @@ -96,8 +89,19 @@ typedef struct { TimerID vibe_timer; int max_vibes; int vibe_count; -#if CAPABILITY_HAS_VIBE_SCORES VibeScore *vibe_score; + +#ifdef CONFIG_SPEAKER + // Speaker playback state. sound_active is the single source of truth: it must + // be cleared *before* speaker_service_stop() so any in-flight finish event + // bails out instead of re-triggering playback. When sound_driven_by_vibe_timer + // is set, replays are issued from the vibe outer timer instead of the speaker + // Done event, so audio and vibe stay anchored to the same drift-free clock. + bool sound_active; + bool sound_driven_by_vibe_timer; + const SpeakerNote *sound_notes; + uint32_t sound_count; + EventServiceInfo speaker_event_info; #endif } AlarmPopupData; @@ -116,16 +120,94 @@ static void prv_stop_vibes(void) { new_timer_stop(s_alarm_popup_data->vibe_timer); new_timer_delete(s_alarm_popup_data->vibe_timer); s_alarm_popup_data->vibe_timer = TIMER_INVALID_ID; -#if CAPABILITY_HAS_VIBE_SCORES if (s_alarm_popup_data->vibe_score) { vibe_score_destroy(s_alarm_popup_data->vibe_score); s_alarm_popup_data->vibe_score = NULL; } -#endif } vibes_cancel(); } +#ifdef CONFIG_SPEAKER +// Volume 60/100 is a moderate first cut; tunable, and a per-user volume +// preference can be added in a follow-up. +#define ALARM_SPEAKER_VOLUME 60 + +static void prv_play_sound_loop_iteration(void) { + speaker_service_play_note_seq(s_alarm_popup_data->sound_notes, + s_alarm_popup_data->sound_count, + SpeakerPriorityCritical, ALARM_SPEAKER_VOLUME); +} + +static void prv_handle_speaker_event(PebbleEvent *e, void *context) { + if (!s_alarm_popup_data || !s_alarm_popup_data->sound_active) { + return; + } + if (e->speaker.type != SpeakerEventFinished) { + return; + } + if (s_alarm_popup_data->sound_driven_by_vibe_timer) { + // Replays are issued from the vibe timer; ignore Done/Preempted here. + return; + } + if ((SpeakerFinishReason)e->speaker.finish_reason == SpeakerFinishReasonDone) { + // Sound-only mode: alarm rings continuously until dismiss/snooze/timeout. + prv_play_sound_loop_iteration(); + } +} + +static void prv_start_sound(AlarmId id, bool vibe_driving_loop) { + if (low_power_is_active()) { + return; // Skip sound in LPM to conserve battery; vibration still runs. + } + + AlarmInfo info; + if (!alarm_get_info(id, &info) || !info.sound_enabled) { + return; + } + + alarm_tones_get(info.tone, &s_alarm_popup_data->sound_notes, &s_alarm_popup_data->sound_count); + if (!s_alarm_popup_data->sound_notes || s_alarm_popup_data->sound_count == 0) { + return; + } + + s_alarm_popup_data->speaker_event_info = (EventServiceInfo) { + .type = PEBBLE_SPEAKER_EVENT, + .handler = prv_handle_speaker_event, + }; + event_service_client_subscribe(&s_alarm_popup_data->speaker_event_info); + speaker_service_register_finish(PebbleTask_KernelMain); + + // Only piggyback on the vibe outer timer when the selected tone has been + // laid out beat-for-beat against the user's selected alarm vibe (see the + // paired_vibe column in s_tones[]). For any other combination the cycle + // lengths and beats won't line up, so each side loops on its own cadence. + const VibeScoreId paired = alarm_tones_get_paired_vibe(info.tone); + const bool tones_match = vibe_driving_loop && + paired != VibeScoreId_Invalid && + paired == alerts_preferences_get_vibe_score_for_client(VibeClient_Alarms); + s_alarm_popup_data->sound_active = true; + s_alarm_popup_data->sound_driven_by_vibe_timer = tones_match; + if (!tones_match) { + prv_play_sound_loop_iteration(); + } + // When the vibe timer is driving the loop, the first audio play happens + // inside the vibe timer's callback so both subsystems are anchored to the + // same clock. +} + +static void prv_stop_sound(void) { + if (!s_alarm_popup_data->sound_active) { + return; + } + // Order matters: clear the flag first so any in-flight finish event bails + // out before stop() generates one. + s_alarm_popup_data->sound_active = false; + speaker_service_stop(); + event_service_client_unsubscribe(&s_alarm_popup_data->speaker_event_info); +} +#endif // CONFIG_SPEAKER + // ---------------------------------------------------------------------------------------------- //! Vibe Timer #define TINTIN_VIBE_REPEAT_INTERVAL_MS (1000) @@ -136,24 +218,23 @@ static void prv_vibe_kernel_main_cb(void *callback_context) { if (s_alarm_popup_data) { if (s_alarm_popup_data->vibe_count < s_alarm_popup_data->max_vibes) { s_alarm_popup_data->vibe_count++; -#if CAPABILITY_HAS_VIBE_SCORES + vibes_cancel(); vibe_score_do_vibe(s_alarm_popup_data->vibe_score); -#else - if (low_power_is_active()) { - // Only vibe 10 seconds every minute in low_power_mode - _Static_assert(TINTIN_VIBE_REPEAT_INTERVAL_MS == MS_PER_SECOND, - "LPM Vibes timing incorrect"); - if (s_alarm_popup_data->vibe_count % SECONDS_PER_MINUTE < TINTIN_LPM_VIBES_PER_MINUTE) { - vibes_long_pulse(); - } - } else { - vibes_long_pulse(); +#ifdef CONFIG_SPEAKER + if (s_alarm_popup_data->sound_active && + s_alarm_popup_data->sound_driven_by_vibe_timer) { + prv_play_sound_loop_iteration(); } #endif } else { prv_stop_vibes(); launcher_task_add_callback(prv_stop_animation_kernel_main_cb, NULL); + // Auto-dismiss the alarm after the vibration period ends + alarm_dismiss_alarm(); + if (s_alarm_popup_data && s_alarm_popup_data->alarm_popup) { + actionable_dialog_pop(s_alarm_popup_data->alarm_popup); + } } } } @@ -165,7 +246,6 @@ static void prv_vibe(void *unused) { static void prv_start_vibes(void) { s_alarm_popup_data->vibe_count = 0; unsigned int vibe_repeat_interval_ms = TINTIN_VIBE_REPEAT_INTERVAL_MS; -#if CAPABILITY_HAS_VIBE_SCORES if (low_power_is_active()) { s_alarm_popup_data->vibe_score = vibe_client_get_score(VibeClient_AlarmsLPM); } else { @@ -177,9 +257,6 @@ static void prv_start_vibes(void) { vibe_repeat_interval_ms = vibe_score_get_duration_ms(s_alarm_popup_data->vibe_score) + vibe_score_get_repeat_delay_ms(s_alarm_popup_data->vibe_score); s_alarm_popup_data->max_vibes = DIVIDE_CEIL(VIBE_DURATION, vibe_repeat_interval_ms); -#else - s_alarm_popup_data->max_vibes = TINTIN_MAX_VIBES; -#endif s_alarm_popup_data->vibe_timer = new_timer_create(); prv_vibe(NULL); new_timer_start(s_alarm_popup_data->vibe_timer, vibe_repeat_interval_ms, prv_vibe, @@ -190,14 +267,14 @@ static void prv_start_vibes(void) { //! Click Handler static void prv_dismiss_click_handler(ClickRecognizerRef recognizer, void *data) { alarm_dismiss_alarm(); - prv_show_dismiss_confirm_dialog(); actionable_dialog_pop(s_alarm_popup_data->alarm_popup); + prv_show_dismiss_confirm_dialog(); } static void prv_snooze_click_handler(ClickRecognizerRef recognizer, void *data) { alarm_set_snooze_alarm(); - prv_show_snooze_confirm_dialog(); actionable_dialog_pop(s_alarm_popup_data->alarm_popup); + prv_show_snooze_confirm_dialog(); } static void prv_click_provider(void *context) { @@ -225,6 +302,9 @@ static void prv_setup_action_bar(void) { static void prv_cleanup_alarm_popup(void *callback_context) { if (s_alarm_popup_data) { prv_stop_vibes(); +#ifdef CONFIG_SPEAKER + prv_stop_sound(); +#endif gbitmap_destroy(s_alarm_popup_data->bitmap); gbitmap_destroy(s_alarm_popup_data->action_bar_snooze); gbitmap_destroy(s_alarm_popup_data->action_bar_dismiss); @@ -235,9 +315,7 @@ static void prv_cleanup_alarm_popup(void *callback_context) { // ---------------------------------------------------------------------------------------------- //! API -#endif void alarm_popup_push_window(PebbleAlarmClockEvent *event) { -#if !TINTIN_FORCE_FIT if (s_alarm_popup_data) { // The window is already visible, don't show another one return; @@ -271,10 +349,23 @@ void alarm_popup_push_window(PebbleAlarmClockEvent *event) { dialog_set_callbacks(dialog, &callback, NULL); actionable_dialog_push(s_alarm_popup_data->alarm_popup, prv_get_window_stack()); - prv_start_vibes(); + // The alarm id isn't carried in the event (PebbleEvent must stay <= 12 bytes + // — see _Static_assert in events.c). Fall back to the most-recently-fired + // alarm tracked in the alarm service. Synthetic events (trigger_alarm demo) + // get ALARM_INVALID_ID and skip the per-alarm config lookup. + const AlarmId alarm_id = alarm_get_most_recent_id(); + AlarmInfo info; + const bool have_info = (alarm_id != ALARM_INVALID_ID) && + alarm_get_info(alarm_id, &info); + const bool vibe_on = !have_info || info.vibrate_enabled; + if (vibe_on) { + prv_start_vibes(); + } +#ifdef CONFIG_SPEAKER + if (have_info) { + prv_start_sound(alarm_id, vibe_on); + } +#endif light_enable_interaction(); -#else - return; -#endif } diff --git a/src/fw/popups/alarm_popup.h b/src/fw/popups/alarm_popup.h index 1d392445e3..b75a0bc632 100644 --- a/src/fw/popups/alarm_popup.h +++ b/src/fw/popups/alarm_popup.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.c b/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.c index a35300bdbf..fff2d9cab8 100644 --- a/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.c +++ b/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ble_hrm_reminder_popup.h" #include "drivers/rtc.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/notifications/notifications.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/notifications/notifications.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_resources.h" #include diff --git a/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.h b/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.h index e72c6ebcc8..a740114b65 100644 --- a/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.h +++ b/src/fw/popups/ble_hrm/ble_hrm_reminder_popup.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.c b/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.c index 9e21ea67a2..a5ed6a68c5 100644 --- a/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.c +++ b/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/bluetooth/ble_hrm.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/ble_hrm.h" #include "applib/graphics/gcolor_definitions.h" #include "applib/ui/dialogs/actionable_dialog.h" @@ -23,7 +10,7 @@ #include "kernel/ui/modals/modal_manager.h" #include "applib/ui/vibes.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include diff --git a/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.h b/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.h index a3e3cbd574..0fa6d6810a 100644 --- a/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.h +++ b/src/fw/popups/ble_hrm/ble_hrm_sharing_popup.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM typedef struct BLEHRMSharingRequest BLEHRMSharingRequest; diff --git a/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.c b/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.c index dc0d2755c9..c57d485868 100644 --- a/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.c +++ b/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ble_hrm_stop_sharing_popup.h" #include "applib/ui/dialogs/simple_dialog.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "util/time/time.h" #define BLE_HRM_CONFIRMATION_TIMEOUT_MS (2 * MS_PER_SECOND) diff --git a/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.h b/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.h index 8e0549dff0..ccfde38a79 100644 --- a/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.h +++ b/src/fw/popups/ble_hrm/ble_hrm_stop_sharing_popup.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/popups/bluetooth_pairing_ui.c b/src/fw/popups/bluetooth_pairing_ui.c index 753823aaa4..e9de1a78a9 100644 --- a/src/fw/popups/bluetooth_pairing_ui.c +++ b/src/fw/popups/bluetooth_pairing_ui.c @@ -1,20 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bluetooth_pairing_ui.h" @@ -31,10 +16,10 @@ #include "kernel/ui/modals/modal_manager.h" #include "kernel/ui/system_icons.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" @@ -69,7 +54,7 @@ typedef struct BTPairingUIData { // The info text layers store show the text that prompts the user to pair TextLayer info_text_layer; char info_text_layer_buffer[MAX_PAIR_STR_LEN]; -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW GRect pair_text_area; GRect above_pair_text_area; TextLayer info_text_layer2; @@ -92,7 +77,7 @@ static BTPairingUIData *s_data_ptr = NULL; static void prv_handle_pairing_complete(bool success); -#ifdef RECOVERY_FW // PRF -- animate through a few hard-coded text strings for "Pair?" +#ifdef CONFIG_RECOVERY_FW // PRF -- animate through a few hard-coded text strings for "Pair?" static void prv_animate_info_text(BTPairingUIData *data); @@ -130,13 +115,9 @@ static void prv_update_text_layer_with_translation(TextLayer *text_layer, } static void prv_update_prf_info_text_layers_text(BTPairingUIData *data) { -#if PLATFORM_ROBERT || PLATFORM_CALCULUS +#if PBL_DISPLAY_HEIGHT >= 200 const char *font_key_default = FONT_KEY_GOTHIC_28_BOLD; const char *font_key_japanese = FONT_KEY_MINCHO_24_PAIR; -#elif PLATFORM_OBELIX - const char *font_key_default = FONT_KEY_GOTHIC_28_BOLD; - // FIXME(OBELIX): We need MINCHO_24, but not available right now - const char *font_key_japanese = FONT_KEY_MINCHO_20_PAIR; #else const char *font_key_default = FONT_KEY_GOTHIC_24_BOLD; const char *font_key_japanese = FONT_KEY_MINCHO_20_PAIR; @@ -279,7 +260,7 @@ static void prv_adjust_background_frame_for_state(BTPairingUIData *data) { switch (data->ui_state) { case BTPairingUIStateAwaitingUserConfirmation: alignment = GAlignTopLeft; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 x_offset = 39; y_offset = 85; #else @@ -290,7 +271,7 @@ static void prv_adjust_background_frame_for_state(BTPairingUIData *data) { break; case BTPairingUIStateAwaitingResult: alignment = GAlignLeft; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 x_offset = 76; y_offset = 30; #else @@ -302,7 +283,7 @@ static void prv_adjust_background_frame_for_state(BTPairingUIData *data) { case BTPairingUIStateFailed: case BTPairingUIStateSuccess: alignment = GAlignTop; -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 x_offset = 0; y_offset = 59; #else @@ -390,7 +371,7 @@ static void prv_window_load(Window *window) { const int32_t width_of_action_bar_with_padding = ACTION_BAR_WIDTH + PBL_IF_RECT_ELSE(2, -4); const int32_t width = window->layer.bounds.size.w - width_of_action_bar_with_padding; const int32_t x_offset = PBL_IF_RECT_ELSE(0, 22); -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 const int32_t info_text_y_offset = 36; #else const int32_t info_text_y_offset = PBL_IF_RECT_ELSE(10, 12); @@ -400,13 +381,13 @@ static void prv_window_load(Window *window) { kino_layer_init(kino_layer, &window->layer.bounds); layer_add_child(&window->layer, &kino_layer->layer); -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 GRect pair_text_area = GRect(0, -2, width, 44); #else GRect pair_text_area = GRect(0, -2, width, 30); #endif -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 layer_set_frame(&data->info_text_mask_layer, &GRect(x_offset, info_text_y_offset, width, 30)); #else layer_set_frame(&data->info_text_mask_layer, &GRect(x_offset, info_text_y_offset, width, 26)); @@ -418,7 +399,7 @@ static void prv_window_load(Window *window) { text_layer_init_with_parameters(info_text_layer, &pair_text_area, data->info_text_layer_buffer, -#if PLATFORM_ROBERT || PLATFORM_CALCULUS || PLATFORM_OBELIX +#if PBL_DISPLAY_HEIGHT >= 200 fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), #else fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), @@ -440,7 +421,11 @@ static void prv_window_load(Window *window) { prv_add_prf_layers(pair_text_area, data); +#if PBL_DISPLAY_HEIGHT >= 200 + const int16_t y_offset = 55; +#else const int16_t y_offset = PBL_IF_RECT_ELSE(0, 2); +#endif // Device name: if (prv_has_device_name(data)) { TextLayer *device_name_layer = &data->device_name_text_layer; @@ -497,7 +482,7 @@ static void prv_show_failure_kernel_main_cb(void *unused) { } static void prv_pairing_timeout_timer_callback(void *unused) { - PBL_LOG(LOG_LEVEL_WARNING, "SSP timeout fired!"); + PBL_LOG_WRN("SSP timeout fired!"); launcher_task_add_callback(prv_show_failure_kernel_main_cb, NULL); } @@ -579,7 +564,7 @@ static void prv_handle_confirmation_request(const PairingUserConfirmationCtx *ct static void prv_handle_pairing_complete(bool success) { if (!s_data_ptr) { - PBL_LOG(LOG_LEVEL_WARNING, "Dialog was not present, but got complete (%u) event", success); + PBL_LOG_WRN("Dialog was not present, but got complete (%u) event", success); return; } @@ -587,19 +572,18 @@ static void prv_handle_pairing_complete(bool success) { if (data->ui_state == BTPairingUIStateAwaitingUserConfirmation) { prv_exit_awaiting_user_confirmation(data); } else if (data->ui_state != BTPairingUIStateAwaitingResult) { - PBL_LOG(LOG_LEVEL_WARNING, - "Got completion (%u) but not right state", success); + PBL_LOG_WRN("Got completion (%u) but not right state", success); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Got Completion! %u", success); + PBL_LOG_DBG("Got Completion! %u", success); data->ui_state = success ? BTPairingUIStateSuccess : BTPairingUIStateFailed; prv_adjust_background_frame_for_state(data); if (!new_timer_stop(data->timer)) { // Timer was already executing... if (success) { - PBL_LOG(LOG_LEVEL_WARNING, "Timeout cb executing while received successful completion event"); + PBL_LOG_WRN("Timeout cb executing while received successful completion event"); } } @@ -624,8 +608,8 @@ void bluetooth_pairing_ui_handle_event(PebbleBluetoothPairEvent *event) { if (s_data_ptr && s_data_ptr->ctx == event->ctx) { prv_handle_pairing_complete(event->success); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Got complete event for unknown process %p vs %p", - event->ctx, s_data_ptr ? s_data_ptr->ctx : NULL); + PBL_LOG_ERR("Got complete event for unknown process %p vs %p", + (void *)event->ctx, s_data_ptr ? (void *)s_data_ptr->ctx : NULL); } break; diff --git a/src/fw/popups/bluetooth_pairing_ui.h b/src/fw/popups/bluetooth_pairing_ui.h index 5da4c55412..0a98e6e1a8 100644 --- a/src/fw/popups/bluetooth_pairing_ui.h +++ b/src/fw/popups/bluetooth_pairing_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" diff --git a/src/fw/popups/crashed_ui.c b/src/fw/popups/crashed_ui.c index e22f57df3f..ac04df3691 100644 --- a/src/fw/popups/crashed_ui.c +++ b/src/fw/popups/crashed_ui.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "crashed_ui.h" -#include "services/common/light.h" +#include "pbl/services/light.h" #include "applib/ui/dialogs/dialog.h" #include "applib/ui/dialogs/actionable_dialog.h" @@ -29,7 +16,7 @@ #include "process_management/app_manager.h" #include "process_management/worker_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include @@ -168,14 +155,6 @@ void crashed_ui_show_worker_crash(const AppInstallId install_id) { i18n_noop("A bug report has been captured. " \ "Please finish uploading the bug report using the Pebble phone app.") -#define YOUR_PEBBLE_RESET \ - i18n_noop("Your Pebble just reset. " \ - "Please report this using the 'Support' link in the Pebble phone app.") - -#define PHONE_BT_CONTROLLER_WEDGED \ - i18n_noop("Bluetooth on your phone is in a high power state. " \ - "Please report this using 'Support' and reboot your phone.") - //! Display a dialog for watch reset or bluetooth being stuck. static void prv_push_reset_dialog(void *context) { const char *crash_reason = context; @@ -191,19 +170,3 @@ static void prv_push_reset_dialog(void *context) { void crashed_ui_show_forced_core_dump(void) { launcher_task_add_callback(prv_push_reset_dialog, CORE_DUMP_COMPLETE); } - -#if (defined(SHOW_BAD_BT_STATE_ALERT) || defined(SHOW_PEBBLE_JUST_RESET_ALERT)) - -//! Restrict only to the two defines above -//! Show the "Your pebble has just reset" -void crashed_ui_show_pebble_reset(void) { - launcher_task_add_callback(prv_push_reset_dialog, YOUR_PEBBLE_RESET); -} - -//! Restrict only to the two defines above -//! Show the "Your Bluetooth is ..." -void crashed_ui_show_bluetooth_stuck(void) { - launcher_task_add_callback(prv_push_reset_dialog, PHONE_BT_CONTROLLER_WEDGED); -} - -#endif diff --git a/src/fw/popups/crashed_ui.h b/src/fw/popups/crashed_ui.h index a154636f8a..cb67865c17 100644 --- a/src/fw/popups/crashed_ui.h +++ b/src/fw/popups/crashed_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,10 +8,4 @@ //! Show the "Bug report captured" modal void crashed_ui_show_forced_core_dump(void); -//! Show the "Your Pebble just reset" modal -void crashed_ui_show_pebble_reset(void); - -//! Show the "Bluetooth stuck in ..." modal -void crashed_ui_show_bluetooth_stuck(void); - void crashed_ui_show_worker_crash(const AppInstallId install_id); diff --git a/src/fw/popups/health_tracking_ui.c b/src/fw/popups/health_tracking_ui.c index 9a0c071b1e..860550d2de 100644 --- a/src/fw/popups/health_tracking_ui.c +++ b/src/fw/popups/health_tracking_ui.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "health_tracking_ui.h" @@ -23,14 +10,11 @@ #include "kernel/ui/modals/modal_manager.h" #include "process_management/app_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" #include - -#if CAPABILITY_HAS_HEALTH_TRACKING - typedef struct HealthTrackingUIData { uint32_t res_id; const char *text; @@ -88,23 +72,6 @@ void health_tracking_ui_show_message(uint32_t res_id, const char *text, bool sho launcher_task_add_callback(prv_push_enable_in_mobile_dialog, data); } -// --------------------------------------------------------------------------- -void health_tracking_ui_app_show_disabled(void) { - // Show at most once per app launch - AppInstallId app_id = app_manager_get_current_app_id(); - if (app_id == s_last_app_id) { - return; - } - s_last_app_id = app_id; - - /// Health disabled dialog - static const char *msg = - i18n_noop("This app requires Pebble Health to work. Enable Health in the Pebble" - " mobile app to continue."); - - health_tracking_ui_show_message(RESOURCE_ID_GENERIC_WARNING_TINY, msg, false); -} - // --------------------------------------------------------------------------- void health_tracking_ui_feature_show_disabled(void) { /// Feature requires health dialog @@ -119,6 +86,3 @@ void health_tracking_ui_feature_show_disabled(void) { void health_tracking_ui_register_app_launch(AppInstallId app_id) { s_last_app_id = 0; } - - -#endif // CAPABILITY_HAS_HEALTH_TRACKING diff --git a/src/fw/popups/health_tracking_ui.h b/src/fw/popups/health_tracking_ui.h index caddd5d0c7..a5f2363271 100644 --- a/src/fw/popups/health_tracking_ui.h +++ b/src/fw/popups/health_tracking_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,10 +8,6 @@ //! Show a modal with a message and text with an optional action bar void health_tracking_ui_show_message(uint32_t res_id, const char *text, bool show_action_bar); -//! Show the modal that tells the user that health tracking is disabled -//! and a given app will not work as expected -void health_tracking_ui_app_show_disabled(void); - //! Show the modal that tells the user that health tracking is disabled //! and a given feature will not work as expected void health_tracking_ui_feature_show_disabled(void); diff --git a/src/fw/popups/notifications/notification_window.c b/src/fw/popups/notifications/notification_window.c index 60108aa1a2..6a75076637 100644 --- a/src/fw/popups/notifications/notification_window.c +++ b/src/fw/popups/notifications/notification_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "notification_window.h" @@ -30,38 +17,38 @@ #include "applib/ui/window.h" #include "applib/ui/window_manager.h" #include "applib/ui/window_stack.h" -#include "apps/system_apps/timeline/peek_layer.h" +#include "apps/system/timeline/peek_layer.h" #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "kernel/ui/modals/modal_manager.h" #include "os/mutex.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/session.h" -#include "services/common/evented_timer.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "services/common/regular_timer.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/notifications/alerts.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/notifications/ancs/ancs_filtering.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/notifications/notification_types.h" -#include "services/normal/notifications/notifications.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/notification_layout.h" -#include "services/normal/timeline/swap_layer.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_actions.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/notifications/ancs/ancs_filtering.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/notifications/notification_types.h" +#include "pbl/services/notifications/notifications.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/notification_layout.h" +#include "pbl/services/timeline/swap_layer.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_actions.h" +#include "pbl/services/timeline/timeline_resources.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" @@ -71,10 +58,8 @@ #include #include -#if CAPABILITY_HAS_VIBE_SCORES -#include "services/normal/vibes/vibe_client.h" -#include "services/normal/vibes/vibe_score.h" -#endif +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_score.h" #define NOTIFICATION_PRIORITY (ModalPriorityNotification) @@ -87,7 +72,7 @@ static const unsigned int QUICK_DND_HOLD_MS = 800; T_STATIC NotificationWindowData s_notification_window_data; -static bool s_in_use = false; +T_STATIC bool s_in_use = false; PebbleMutex *s_notification_window_mutex; static bool prv_should_provide_action_menu_for_item(NotificationWindowData *data, @@ -97,6 +82,8 @@ static void prv_handle_notification_removed_common(Uuid *, NotificationType); static bool prv_should_pop_due_to_inactivity(void); +static void prv_do_notification_vibe(NotificationWindowData *data, Uuid *id); + ///////////////////// // Helpers ///////////////////// @@ -261,7 +248,7 @@ static void prv_dismiss_all(void *data, ActionMenu *action_menu) { notif_list[i].type = notifications_presented_list_get_type(id); } - PBL_LOG(LOG_LEVEL_DEBUG, "Dismissing %d notifications", num_notifications); + PBL_LOG_DBG("Dismissing %d notifications", num_notifications); window_data->window_frozen = true; timeline_actions_dismiss_all(notif_list, num_notifications, @@ -312,14 +299,46 @@ static void prv_peek_anim_stopped(Animation *animation, bool finished, void *con TimelineItem *item = prv_get_current_notification(data); layer_set_hidden((Layer *)&data->action_button_layer, !prv_should_provide_action_menu_for_item(data, item)); + + // Fallback: if a pending vibe/backlight wasn't triggered in prv_hide_peek_layer (e.g., a new + // notification arrived after prv_hide_peek_layer ran but before this callback), + // trigger it now + if (data->pending_vibe) { + data->pending_vibe = false; + prv_do_notification_vibe(data, &data->pending_vibe_id); + } + if (data->pending_backlight) { + data->pending_backlight = false; + if (!data->color_preempted) { + data->color_preempted = true; + light_system_color_request(); + } + light_enable_interaction(); + } } static void prv_hide_peek_layer(void *context) { NotificationWindowData *data = context; + // Execute pending vibration and backlight if delayed vibe was requested - do it as the + // notification starts sliding up to reveal the text + if (data->pending_vibe) { + data->pending_vibe = false; + prv_do_notification_vibe(data, &data->pending_vibe_id); + } + if (data->pending_backlight) { + data->pending_backlight = false; + if (!data->color_preempted) { + data->color_preempted = true; + light_system_color_request(); + } + light_enable_interaction(); + } + // get the frame of the swap_layer and set its destination const GRect *swap_frame = &data->swap_layer.layer.frame; - int16_t swap_frame_animation_dy = STATUS_BAR_LAYER_HEIGHT - swap_frame->origin.y; + const int16_t status_bar_height = data->status_layer.layer.frame.size.h; + int16_t swap_frame_animation_dy = status_bar_height - swap_frame->origin.y; // duration of animation of both peek layer and swap layer moving up to the top int16_t peek_frame_animation_dy = swap_frame_animation_dy; @@ -344,7 +363,7 @@ static void prv_hide_peek_layer(void *context) { PeekLayer *peek_layer = data->peek_layer; GRect frame = peek_layer->layer.frame; frame.origin.x = (frame.size.w / 2) - (NOTIFICATION_TINY_RESOURCE_SIZE.w / 2); - frame.origin.y = CARD_ICON_UPPER_PADDING + STATUS_BAR_LAYER_HEIGHT; + frame.origin.y = CARD_ICON_UPPER_PADDING + status_bar_height; frame.size = NOTIFICATION_TINY_RESOURCE_SIZE; const bool align_in_frame = true; @@ -491,14 +510,14 @@ T_STATIC LayoutLayer *prv_get_layout_handler(SwapLayer *swap_layer, int8_t rel_p if (type == NotificationMobile) { if (!notification_storage_get(id, item)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read notification"); + PBL_LOG_ERR("Failed to read notification"); goto cleanup; } } else if (type == NotificationReminder) { // validate reminder int rv = reminder_db_read_item(item, id); if (rv != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read reminder"); + PBL_LOG_ERR("Failed to read reminder"); goto cleanup; } } @@ -616,7 +635,7 @@ static void prv_clear_if_stale_reminder(Uuid *id, NotificationType type, void *c const time_t now = rtc_get_time(); if (stale_time <= now && window_data->is_modal) { - PBL_LOG(LOG_LEVEL_INFO, "Removing stale reminder from notification popup window"); + PBL_LOG_INFO("Removing stale reminder from notification popup window"); prv_remove_notification(window_data, id, true /* close am */); } } @@ -744,7 +763,7 @@ static void prv_mute_notification(const ActionMenuItem *action_menu_item, const char *app_id = attribute_get_string(&item->attr_list, AttributeIdiOSAppIdentifier, ""); if (!*app_id) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not mute notification. Unknown app_id"); + PBL_LOG_ERR("Could not mute notification. Unknown app_id"); return; } @@ -760,12 +779,11 @@ static void prv_mute_notification(const ActionMenuItem *action_menu_item, timeline_invoke_action(item, dismiss, NULL); } prv_push_muted_dialog(); - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_MUTED_COUNT, AnalyticsClient_System); } else { // This is a very unlikely case. We store some default prefs which includes the mute // attribute when we receive the notification so either someone deleted the entry // in the DB or the mute attribute (neither of which should happen) - PBL_LOG(LOG_LEVEL_WARNING, "Could not mute notification. No prefs or mute attribute"); + PBL_LOG_WRN("Could not mute notification. No prefs or mute attribute"); } ios_notif_pref_db_free_prefs(notif_prefs); @@ -789,6 +807,55 @@ static void prv_mute_notification_weekends(ActionMenu *action_menu, prv_mute_notification(action_menu_item, MuteBitfield_Weekends); } +static void prv_mute_notification_timed(const ActionMenuItem *action_menu_item, int duration_seconds) { + NotificationWindowData *window_data = action_menu_item->action_data; + TimelineItem *item = prv_get_current_notification(window_data); + + const char *app_id = attribute_get_string(&item->attr_list, AttributeIdiOSAppIdentifier, ""); + if (!app_id || !*app_id) { + PBL_LOG_ERR("Could not mute notification. Unknown app_id"); + return; + } + + const int app_id_len = strlen(app_id); + iOSNotifPrefs *notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)app_id, app_id_len); + Attribute *expiration_attr = notif_prefs ? + attribute_find(¬if_prefs->attr_list, AttributeIdMuteExpiration) : NULL; + if (notif_prefs && expiration_attr) { + const uint32_t expiration_time = rtc_get_time() + duration_seconds; + expiration_attr->uint32 = expiration_time; + + ios_notif_pref_db_store_prefs((uint8_t *)app_id, app_id_len, + ¬if_prefs->attr_list, ¬if_prefs->action_group); + + TimelineItemAction *dismiss = timeline_item_find_dismiss_action(item); + if (dismiss) { + timeline_invoke_action(item, dismiss, NULL); + } + prv_push_muted_dialog(); + } else { + PBL_LOG_WRN("Could not mute notification. No prefs or mute expiration attribute"); + } + + ios_notif_pref_db_free_prefs(notif_prefs); +} + +static void prv_mute_notification_1_hour(ActionMenu *action_menu, + const ActionMenuItem *action_menu_item, + void *context) { + prv_mute_notification_timed(action_menu_item, 3600); +} + +static void prv_mute_notification_today(ActionMenu *action_menu, + const ActionMenuItem *action_menu_item, + void *context) { + time_t now = rtc_get_time(); + struct tm now_tm; + localtime_r(&now, &now_tm); + const int seconds_until_midnight = (24 * 3600) - (now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec); + prv_mute_notification_timed(action_menu_item, seconds_until_midnight); +} + static bool prv_has_mute_action(TimelineItem *item) { PebbleProtocolCapabilities capabilities; bt_persistent_storage_get_cached_system_capabilities(&capabilities); @@ -890,13 +957,23 @@ static ActionMenuLevel *prv_create_action_menu_for_item(TimelineItem *item, prv_mute_notification_always, window_data); } else { - const uint8_t number_mute_actions = 3; + const uint8_t number_mute_actions = 5; ActionMenuLevel *mute_level = action_menu_level_create(number_mute_actions); action_menu_level_add_child(root_level, mute_level, mute_label_buf); + action_menu_level_add_action(mute_level, + i18n_get("Mute 1 Hour", root_level), + prv_mute_notification_1_hour, + window_data); + + action_menu_level_add_action(mute_level, + i18n_get("Mute Today", root_level), + prv_mute_notification_today, + window_data); + action_menu_level_add_action(mute_level, i18n_get("Mute Always", root_level), prv_mute_notification_always, @@ -962,7 +1039,7 @@ static void prv_select_single_click_handler(ClickRecognizerRef recognizer, void config.root_level = prv_create_action_menu_for_item(item, window_data, source); if (config.root_level == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Couldn't create notification action menu"); + PBL_LOG_ERR("Couldn't create notification action menu"); return; } @@ -976,9 +1053,6 @@ static void prv_select_long_click_handler(ClickRecognizerRef recognizer, void *d static void prv_back_button_single_click_handler(ClickRecognizerRef recognizer, void *data) { NotificationWindowData *window_data = data; - if (window_data->is_modal) { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_CLOSED_COUNT, AnalyticsClient_System); - } prv_pop_notification_window(window_data); } @@ -1043,7 +1117,14 @@ static void prv_window_unload(Window *window) { return; } + PBL_LOG_INFO("Notification vibe: window_unload, cancelling vibes (pending_vibe=%d)", + data->pending_vibe); vibes_cancel(); + data->pending_vibe = false; + if (data->color_preempted) { + data->color_preempted = false; + light_system_color_release(); + } prv_cleanup_timers(data); // clean up peek layer @@ -1083,6 +1164,7 @@ static void prv_layout_did_appear_handler(SwapLayer *swap_layer, LayoutLayer *la kino_layer_play(&((NotificationLayout *)layout)->icon_layer); } +#if PBL_COLOR static void prv_update_colors_handler(SwapLayer *swap_layer, GColor bg_color, bool status_bar_filled, void *context) { NotificationWindowData *data = context; @@ -1091,6 +1173,7 @@ static void prv_update_colors_handler(SwapLayer *swap_layer, GColor bg_color, status_bar_layer_set_colors(&data->status_layer, PBL_IF_ROUND_ELSE(GColorClear, status_color), gcolor_legible_over(status_color)); } +#endif // PBL_COLOR static void prv_interaction_handler(SwapLayer *swap_layer, void *context) { NotificationWindowData *data = context; @@ -1120,7 +1203,9 @@ static void prv_set_dnd_icon_visible(bool is_visible) { layer_set_frame(&data->status_layer.layer, &new_status_frame); #endif const uint16_t icon_layer_x_offset = PBL_IF_ROUND_ELSE(new_icon_layer_x_offset, 6); - const uint16_t icon_layer_y_offset = PBL_IF_ROUND_ELSE(10, 2); + const GRect status_frame = data->status_layer.layer.frame; + const int16_t icon_layer_y_offset = status_frame.origin.y + + MAX((status_frame.size.h - icon_rect.size.h) / 2, 0); const GRect dnd_frame = (GRect) { .origin = GPoint(icon_layer_x_offset, icon_layer_y_offset), .size = icon_rect.size, @@ -1140,6 +1225,18 @@ static void prv_dnd_status_changed(bool dnd_is_active) { // Notification Window API /////////////////////////// +static StatusBarLayerMode prv_status_bar_mode_for_style(NotificationStatusBarStyle style) { + switch (style) { + case NotificationStatusBarStyle_Bold: + return StatusBarLayerModeClockBold; + case NotificationStatusBarStyle_LargeBold: + return StatusBarLayerModeClockLargeBold; + case NotificationStatusBarStyle_Default: + default: + return StatusBarLayerModeClock; + } +} + static void prv_init_notification_window(bool is_modal) { NotificationWindowData *data = &s_notification_window_data; @@ -1165,6 +1262,7 @@ static void prv_init_notification_window(bool is_modal) { }; data->action_menu = NULL; data->dnd_icon_visible = false; + data->pending_vibe = false; Window *window = &data->window; window_init(window, "Notification Window"); @@ -1179,8 +1277,21 @@ static void prv_init_notification_window(bool is_modal) { Layer *root_layer = window_get_root_layer(window); const GRect *window_frame = &root_layer->frame; + // Configure the status bar first so its (possibly taller) height drives the + // layout below it. The selected notification status-bar style maps to a + // StatusBarLayerMode; status_bar_layer_set_mode resizes the bar to match. + StatusBarLayer *status_layer = &data->status_layer; + status_bar_layer_init(status_layer); + status_bar_layer_set_colors(status_layer, PBL_IF_RECT_ELSE(GColorBlack, GColorClear), + PBL_IF_RECT_ELSE(GColorWhite, GColorBlack)); + status_bar_layer_set_separator_mode(status_layer, StatusBarLayerSeparatorModeNone); + status_bar_layer_set_mode( + status_layer, + prv_status_bar_mode_for_style(alerts_preferences_get_notification_status_bar_style())); + const int16_t status_bar_height = status_layer->layer.frame.size.h; + // prepare the swap layer frame using notification_layout values including the status bar - const GRect swap_frame = GRect(0, STATUS_BAR_LAYER_HEIGHT, window_frame->size.w, + const GRect swap_frame = GRect(0, status_bar_height, window_frame->size.w, LAYOUT_HEIGHT + LAYOUT_ARROW_HEIGHT); SwapLayer *swap_layer = &data->swap_layer; @@ -1198,11 +1309,7 @@ static void prv_init_notification_window(bool is_modal) { swap_layer_set_click_config_onto_window(swap_layer, window); layer_add_child(root_layer, swap_layer_get_layer(swap_layer)); - StatusBarLayer *status_layer = &data->status_layer; - status_bar_layer_init(status_layer); - status_bar_layer_set_colors(status_layer, PBL_IF_RECT_ELSE(GColorBlack, GColorClear), - PBL_IF_RECT_ELSE(GColorWhite, GColorBlack)); - status_bar_layer_set_separator_mode(status_layer, StatusBarLayerSeparatorModeNone); + // Add the status bar after the swap layer so it renders on top. layer_add_child(root_layer, (Layer *)status_layer); // bubble on right for action button @@ -1335,60 +1442,117 @@ static void prv_handle_notification_acted_upon(Uuid *id) { } } +static void prv_do_notification_vibe(NotificationWindowData *data, Uuid *id) { + PBL_LOG_INFO("Notification vibe: do_vibe called"); + TimelineItem *item = prv_get_current_notification(data); + // Check if the current notification is the one we want to vibe for - if not then reload to make + // sure it is, before reading the attributes. + if (!item || !uuid_equal(&item->header.id, id)) { + prv_reload_swap_layer(data); + item = prv_get_current_notification(data); + } + if (!item) { + return; + } + bool did_vibrate = false; + Uint32List *vibeDurations = attribute_get_uint32_list(&item->attr_list, AttributeIdVibrationPattern); + if (vibeDurations && vibeDurations->num_values > 0) { + VibePattern patt; + + PBL_LOG_INFO("Notification vibe: using CUSTOM pattern from phone, %" PRIu16 " segments", + vibeDurations->num_values); + + patt.durations = vibeDurations->values; + patt.num_segments = vibeDurations->num_values; + vibes_enqueue_custom_pattern(patt); + did_vibrate = true; + } else { + VibeScore *score = vibe_client_get_score(VibeClient_Notifications); + if (score) { + VibeScoreId id = alerts_preferences_get_vibe_score_for_client(VibeClient_Notifications); + PBL_LOG_INFO("Notification vibe: using alerts preferences (%d, %s)", + (int)id, vibe_score_info_get_name(id)); + + vibe_score_do_vibe(score); + vibe_score_destroy(score); + did_vibrate = true; + } + } + // Timestamp set after call to vibrate since if something fails, + // its better to have no vibe blocking then vibe blocking and no vibrations. + if (did_vibrate) { + alerts_set_notification_vibe_timestamp(); + } +} + static void prv_handle_notification_added_common(Uuid *id, NotificationType type) { + NotificationWindowData *data = &s_notification_window_data; + if (!alerts_should_notify_for_type(prv_alert_type_for_notification_type(type))) { return; } - NotificationWindowData *data = &s_notification_window_data; + alerts_incoming_alert_analytics(); + + if (do_not_disturb_is_active() && + alerts_preferences_dnd_get_show_notifications() == DndNotificationModeHide) { + return; + } // will fail and return early if already init'ed. prv_init_notification_window(true /*is_modal*/); - if (data->is_modal) { - WindowStack *window_stack = modal_manager_get_window_stack(NOTIFICATION_PRIORITY); - bool is_new = !window_stack_contains_window(window_stack, &data->window); - bool in_view = window_is_on_screen(&data->window); + if (!data->is_modal) { + return; + } - prv_notification_window_add_notification(id, type); + WindowStack *window_stack = modal_manager_get_window_stack(NOTIFICATION_PRIORITY); + bool is_new = !window_stack_contains_window(window_stack, &data->window); + bool in_view = window_is_on_screen(&data->window); - if (is_new) { - data->first_notif_loaded = false; - prv_show_peek_for_notification(data, id, true /* is_first_notification */); - modal_window_push(&data->window, NOTIFICATION_PRIORITY, true /* animated */); - } else if (in_view) { - // Only focus the new notification if it becomes the new front of the list. - // In DND mode notifications can get inserted into the middle of the list and we don't - // want to change focus in this use case - if (notifications_presented_list_current() != notifications_presented_list_first()) { - const bool should_animate = !do_not_disturb_is_active(); - notification_window_focus_notification(id, should_animate); - } else { - // If we are inserting into the middle of this list, just reaload the swap layer so the - // number of notifications displayed is correct - prv_reload_swap_layer(data); - } + prv_notification_window_add_notification(id, type); + + if (is_new) { + data->first_notif_loaded = false; + prv_show_peek_for_notification(data, id, true /* is_first_notification */); + modal_window_push(&data->window, NOTIFICATION_PRIORITY, true /* animated */); + } else if (in_view) { + // Only focus the new notification if it becomes the new front of the list. + // In DND mode notifications can get inserted into the middle of the list and we don't + // want to change focus in this use case + if (notifications_presented_list_current() != notifications_presented_list_first()) { + const bool should_animate = !do_not_disturb_is_active(); + notification_window_focus_notification(id, should_animate); + } else { + // If we are inserting into the middle of this list, just reaload the swap layer so the + // number of notifications displayed is correct + prv_reload_swap_layer(data); } } - alerts_incoming_alert_analytics(); if (alerts_should_vibrate_for_type(prv_alert_type_for_notification_type(type))) { -#if CAPABILITY_HAS_VIBE_SCORES - VibeScore *score = vibe_client_get_score(VibeClient_Notifications); - if (score) { - vibe_score_do_vibe(score); - vibe_score_destroy(score); + // Check if we should delay the vibration until the animation completes + if (alerts_preferences_get_notification_vibe_delay() && data->peek_layer) { + // Delay vibration until peek animation finishes + data->pending_vibe = true; + data->pending_vibe_id = *id; + } else { + // Vibe immediately + prv_do_notification_vibe(data, id); } -#else - vibes_short_pulse(); -#endif - // Timestamp set after call to vibrate since if something fails, - // its better to have no vibe blocking then vibe blocking and no vibrations. - alerts_set_notification_vibe_timestamp(); } if (alerts_should_enable_backlight_for_type(prv_alert_type_for_notification_type(type))) { - light_enable_interaction(); + // Check if we should delay the backlight until the animation completes (same as vibe delay) + if (alerts_preferences_get_notification_vibe_delay() && data->peek_layer) { + data->pending_backlight = true; + } else { + if (!data->color_preempted) { + data->color_preempted = true; + light_system_color_request(); + } + light_enable_interaction(); + } } prv_refresh_pop_timer(data); diff --git a/src/fw/popups/notifications/notification_window.h b/src/fw/popups/notifications/notification_window.h index ddd1af6084..2ca90095d7 100644 --- a/src/fw/popups/notifications/notification_window.h +++ b/src/fw/popups/notifications/notification_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include #include "applib/fonts/fonts.h" -#include "services/normal/notifications/notifications.h" +#include "pbl/services/notifications/notifications.h" #include "kernel/events.h" void notification_window_service_init(void); diff --git a/src/fw/popups/notifications/notification_window_private.h b/src/fw/popups/notifications/notification_window_private.h index 2853be3d52..c43400b4af 100644 --- a/src/fw/popups/notifications/notification_window_private.h +++ b/src/fw/popups/notifications/notification_window_private.h @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/ui/action_menu_window.h" #include "applib/ui/status_bar_layer.h" -#include "apps/system_apps/timeline/peek_layer.h" -#include "services/common/evented_timer.h" -#include "services/normal/timeline/swap_layer.h" +#include "apps/system/timeline/peek_layer.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/timeline/swap_layer.h" typedef struct NotificationWindowData { Window window; @@ -61,4 +48,13 @@ typedef struct NotificationWindowData { Layer dnd_icon_layer; GBitmap dnd_icon; bool dnd_icon_visible; + + // Delayed vibration support - vibe at end of animation instead of immediately + bool pending_vibe; + Uuid pending_vibe_id; + bool pending_backlight; + + // Set once we've pushed a modal color preempt for this notification's + // backlight pulse, so window_unload knows to balance it with a pop. + bool color_preempted; } NotificationWindowData; diff --git a/src/fw/popups/notifications/notifications_presented_list.c b/src/fw/popups/notifications/notifications_presented_list.c index 3deb5000d5..4c9fad981f 100644 --- a/src/fw/popups/notifications/notifications_presented_list.c +++ b/src/fw/popups/notifications/notifications_presented_list.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "notifications_presented_list.h" diff --git a/src/fw/popups/notifications/notifications_presented_list.h b/src/fw/popups/notifications/notifications_presented_list.h index d21a3c8867..c27c630563 100644 --- a/src/fw/popups/notifications/notifications_presented_list.h +++ b/src/fw/popups/notifications/notifications_presented_list.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "util/uuid.h" -#include "services/normal/notifications/notification_types.h" +#include "pbl/services/notifications/notification_types.h" #include diff --git a/src/fw/popups/phone_formatting.c b/src/fw/popups/phone_formatting.c index ee0f0c157a..3848698d03 100644 --- a/src/fw/popups/phone_formatting.c +++ b/src/fw/popups/phone_formatting.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "phone_formatting.h" diff --git a/src/fw/popups/phone_formatting.h b/src/fw/popups/phone_formatting.h index 5e13cd6ecc..b2f74277b6 100644 --- a/src/fw/popups/phone_formatting.h +++ b/src/fw/popups/phone_formatting.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/popups/phone_ui.c b/src/fw/popups/phone_ui.c index 8955270302..28c46462ca 100644 --- a/src/fw/popups/phone_ui.c +++ b/src/fw/popups/phone_ui.c @@ -1,23 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "phone_ui.h" #include "phone_formatting.h" #include "applib/fonts/fonts.h" +#include "util/math.h" #include "util/trig.h" #include "applib/ui/action_bar_layer.h" #include "applib/ui/kino/kino_layer.h" @@ -36,18 +24,18 @@ #include "kernel/pbl_malloc.h" #include "popups/notifications/notifications_presented_list.h" #include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/i18n/i18n.h" -#include "services/common/evented_timer.h" -#include "services/common/regular_timer.h" -#include "services/common/light.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/notifications/alerts.h" -#include "services/normal/notifications/notification_constants.h" -#include "services/normal/phone_call.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_actions.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/light.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/notification_constants.h" +#include "pbl/services/phone_call.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_actions.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "system/logging.h" #include "system/passert.h" @@ -57,10 +45,8 @@ #include #include -#if CAPABILITY_HAS_VIBE_SCORES -#include "services/normal/vibes/vibe_client.h" -#include "services/normal/vibes/vibe_score.h" -#endif +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_score.h" #define DECLINE_DELAY_MS 2000 #define SMS_REPLY_DELAY_MS 1200 @@ -69,6 +55,9 @@ #define CALL_END_DELAY_MS 5000 #define OUTGOING_CALL_DELAY_MS 5000 #define MISSED_CALL_DELAY_MS 180000 +// Phones stop ringing well within this window (carriers give up after at most +// ~60s). Past it, assume we missed the call-ended event and stop vibing. +#define MAX_RING_DURATION_S 90 #define NAME_BUFFER_LENGTH 32 #define CALL_STATUS_BUFFER_LENGTH 32 @@ -82,8 +71,10 @@ #define TEXT_RIGHTSIDE_PADDING \ PBL_IF_RECT_ELSE(ACTION_BAR_WIDTH, ACTION_BAR_WIDTH + RIGHTSIDE_PADDING - TEXT_MARGIN_WIDTH) #define ICON_WIDTH 80 +// Center icon horizontally in content area (display width minus action bar) #define ICON_POSITION_X \ - PBL_IF_RECT_ELSE(18, DISP_ROWS - (ACTION_BAR_WIDTH + RIGHTSIDE_PADDING) - ICON_WIDTH) + PBL_IF_RECT_ELSE(((DISP_COLS - ACTION_BAR_WIDTH - ICON_WIDTH) / 2), \ + DISP_ROWS - (ACTION_BAR_WIDTH + RIGHTSIDE_PADDING) - ICON_WIDTH) #if PBL_ROUND #define ICON_POSITION_CENTERED_X (DISP_ROWS / 2 - ICON_WIDTH / 2) @@ -129,12 +120,12 @@ typedef struct { typedef enum { ACCEPTED, DECLINED, - DISCONNECTED + DISCONNECTED, + ENDED } CallStatus; typedef struct { Window window; -#if !PLATFORM_TINTIN struct { GColor left; GColor right; @@ -144,7 +135,6 @@ typedef struct { Animation *action_bar_animation; Animation *bg_color_animation; Animation *call_status_animation; -#endif ActionBarLayer action_bar; Layer core_ui_container; @@ -174,10 +164,9 @@ typedef struct { EventedTimerID call_duration_timer; EventedTimerID window_pop_timer; time_t call_start_time; -#if CAPABILITY_HAS_VIBE_SCORES VibeScore *vibe_score; -#endif RegularTimerInfo ring_timer; + time_t ring_start_time; bool show_ongoing_call_ui; // Incoming call reply data @@ -187,21 +176,39 @@ typedef struct { void *action_handle; } PhoneUIData; +// Dynamic layout calculation for rectangular displays based on display height +// Content area starts after status bar (16px) and we need to fit: +// - Icon (80x80) +// - Caller ID text area +// - Status text area +// Extra vertical space on larger displays is distributed to improve spacing +#define PHONE_EXTRA_HEIGHT PBL_IF_RECT_ELSE((DISP_ROWS - 168), 0) + +// Default style: icon near top, text below +#define PHONE_ICON_POS_Y_DEFAULT PBL_IF_RECT_ELSE((25 + PHONE_EXTRA_HEIGHT / 4), 22) +#define PHONE_CALLER_ID_POS_Y_DEFAULT PBL_IF_RECT_ELSE((102 + PHONE_EXTRA_HEIGHT / 2), 93) +#define PHONE_STATUS_POS_Y_DEFAULT PBL_IF_RECT_ELSE((142 + PHONE_EXTRA_HEIGHT * 3 / 4), 144) + +// Large style: more compact icon placement, larger text area +#define PHONE_ICON_POS_Y_LARGE PBL_IF_RECT_ELSE((11 + PHONE_EXTRA_HEIGHT / 4), 22) +#define PHONE_CALLER_ID_POS_Y_LARGE PBL_IF_RECT_ELSE((80 + PHONE_EXTRA_HEIGHT / 2), 88) +#define PHONE_STATUS_POS_Y_LARGE PBL_IF_RECT_ELSE((138 + PHONE_EXTRA_HEIGHT * 3 / 4), 144) + static const PhoneStyle s_phone_style_default = { .icon_size = TimelineResourceSizeLarge, - .icon_pos = { ICON_POSITION_X, PBL_IF_RECT_ELSE(25, 22) }, - .caller_id_pos_y = PBL_IF_RECT_ELSE(102, 93), + .icon_pos = { ICON_POSITION_X, PHONE_ICON_POS_Y_DEFAULT }, + .caller_id_pos_y = PHONE_CALLER_ID_POS_Y_DEFAULT, .caller_id_height = 50, - .status_pos_y = PBL_IF_RECT_ELSE(142, 144), + .status_pos_y = PHONE_STATUS_POS_Y_DEFAULT, .status_height = 20, }; static const PhoneStyle s_phone_style_large = { - .icon_size = PBL_IF_RECT_ELSE(TimelineResourceSizeSmall, TimelineResourceSizeLarge), - .icon_pos = { ICON_POSITION_X, PBL_IF_RECT_ELSE(11, 22) }, - .caller_id_pos_y = PBL_IF_RECT_ELSE(80, 88), + .icon_size = TimelineResourceSizeLarge, + .icon_pos = { ICON_POSITION_X, PHONE_ICON_POS_Y_LARGE }, + .caller_id_pos_y = PHONE_CALLER_ID_POS_Y_LARGE, .caller_id_height = 60, - .status_pos_y = PBL_IF_RECT_ELSE(138, 144), + .status_pos_y = PHONE_STATUS_POS_Y_LARGE, .status_height = 20, .large_caller_id = true, }; @@ -243,7 +250,6 @@ static void prv_set_icon_resource(TimelineResourceId timeline_res_id) { } KinoReel *new_image = kino_reel_create_with_resource(resource); -#if !PLATFORM_TINTIN KinoReel *old_image = s_phone_ui_data->current_icon; kino_layer_pause(&s_phone_ui_data->icon_layer); @@ -255,11 +261,6 @@ static void prv_set_icon_resource(TimelineResourceId timeline_res_id) { kino_layer_play(&s_phone_ui_data->icon_layer); s_phone_ui_data->current_icon = new_image; s_phone_ui_data->current_icon_id = resource; -#else - kino_layer_set_reel(&s_phone_ui_data->icon_layer, new_image, true); - s_phone_ui_data->current_icon = new_image; - s_phone_ui_data->current_icon_id = resource; -#endif } // This will do the wrong thing if called after the action bar is removed, due to the absolute @@ -273,7 +274,6 @@ static void prv_unfold_icon_resource(TimelineResourceId timeline_res_id) { const ResourceId resource = icon_res_info.res_id; KinoReel *image = kino_reel_create_with_resource(resource); -#if !PLATFORM_TINTIN GRect layer_frame = s_phone_ui_data->icon_layer.layer.frame; GSize size = kino_reel_get_size(image); GRect icon_from = { @@ -297,14 +297,8 @@ static void prv_unfold_icon_resource(TimelineResourceId timeline_res_id) { kino_layer_play(&s_phone_ui_data->icon_layer); s_phone_ui_data->current_icon = image; s_phone_ui_data->current_icon_id = resource; -#else - kino_layer_set_reel(&s_phone_ui_data->icon_layer, image, true); - s_phone_ui_data->current_icon = image; - s_phone_ui_data->current_icon_id = resource; -#endif } -#if !PLATFORM_TINTIN static void prv_update_color_boundary(void *subject, int16_t boundary) { s_phone_ui_data->bg_color.boundary = boundary; layer_mark_dirty(&s_phone_ui_data->window.layer); @@ -323,10 +317,8 @@ static const PropertyAnimationImplementation s_color_slide_animation_impl = { .setter = { .int16 = (const Int16Setter)prv_update_color_boundary, }, }, }; -#endif static void prv_set_window_color(GColor color, bool left_to_right) { -#if !PLATFORM_TINTIN Animation *color_animation; int16_t width = s_phone_ui_data->window.layer.bounds.size.w; int16_t zero = 0; @@ -355,9 +347,6 @@ static void prv_set_window_color(GColor color, bool left_to_right) { animation_set_duration(color_animation, COLOUR_ANIMATION_FRAMES * ANIMATION_FRAME_MS); animation_set_curve(color_animation, AnimationCurveEaseIn); animation_schedule(color_animation); -#else - layer_mark_dirty(&s_phone_ui_data->window.layer); -#endif } // Names can sometimes actually be phone numbers. We're assuming that phone numbers will always @@ -432,41 +421,44 @@ static void prv_set_caller_id_text(PebblePhoneCaller *caller) { } else { s_phone_ui_data->caller_id_text_layer.layer.bounds.origin.y = DOUBLE_LINE_BOUND_OFFSET; } - s_phone_ui_data->caller_id_text_layer.layer.bounds.size.h = - lines * fonts_get_font_height(caller_id_font); + const int16_t content_height = lines * fonts_get_font_height(caller_id_font); + s_phone_ui_data->caller_id_text_layer.layer.bounds.size.h = content_height; + s_phone_ui_data->caller_id_text_layer.layer.frame.size.h = + MAX(s_phone_ui_data->style->caller_id_height, content_height); text_layer_set_text(&s_phone_ui_data->caller_id_text_layer, s_phone_ui_data->caller_id_text_buf); } //! Window background rendering static void prv_window_update_proc(Layer *layer, GContext *ctx) { -#if !PLATFORM_TINTIN graphics_context_set_fill_color(ctx, s_phone_ui_data->bg_color.left); graphics_fill_rect(ctx, &GRect(0, 0, s_phone_ui_data->bg_color.boundary, layer->bounds.size.h)); graphics_context_set_fill_color(ctx, s_phone_ui_data->bg_color.right); graphics_fill_rect(ctx, &GRect(s_phone_ui_data->bg_color.boundary, 0, layer->bounds.size.w, layer->bounds.size.h)); -#else - graphics_context_set_fill_color(ctx, DEFAULT_COLOR); - graphics_fill_rect(ctx, &layer->bounds); -#endif } //! Ring functionality static void prv_ring(void *unused) { - PBL_LOG(LOG_LEVEL_DEBUG, "RING"); + PBL_LOG_DBG("RING"); + if (!s_phone_ui_data) { + // There is a mutex-related issue that can appear where the timer callback will execute after + // phone_ui cancels the timer and frees the vibe_score / s_phone_ui_data. Thus, bail early + // if we detect this bad state. + // See PBL-35548 + return; + } + if ((rtc_get_time() - s_phone_ui_data->ring_start_time) > MAX_RING_DURATION_S) { + // The call-ended event never arrived (e.g. an ANCS removal lost across a + // re-subscription); don't vibe until the user dismisses the window. + PBL_LOG_WRN("Still ringing after %ds; stopping the vibe loop", MAX_RING_DURATION_S); + regular_timer_remove_callback(&s_phone_ui_data->ring_timer); + return; + } if (alerts_should_vibrate_for_type(AlertPhoneCall)) { -#if CAPABILITY_HAS_VIBE_SCORES - if (!s_phone_ui_data || !s_phone_ui_data->vibe_score) { - // There is a mutex-related issue that can appear where the timer callback will execute after - // phone_ui cancels the timer and frees the vibe_score / s_phone_ui_data. Thus, bail early - // if we detect this bad state. - // See PBL-35548 + if (!s_phone_ui_data->vibe_score) { return; } vibe_score_do_vibe(s_phone_ui_data->vibe_score); -#else - vibes_long_pulse(); -#endif } if (alerts_should_enable_backlight_for_type(AlertPhoneCall)) { light_enable_interaction(); @@ -478,8 +470,8 @@ static void prv_start_ringing(void) { s_phone_ui_data->ring_timer = (const RegularTimerInfo) { .cb = prv_ring, }; + s_phone_ui_data->ring_start_time = rtc_get_time(); unsigned int vibe_repeat_interval_sec; -#if CAPABILITY_HAS_VIBE_SCORES s_phone_ui_data->vibe_score = vibe_client_get_score(VibeClient_PhoneCalls); if (!s_phone_ui_data->vibe_score) { return; @@ -487,21 +479,16 @@ static void prv_start_ringing(void) { unsigned int vibe_interval_ms = vibe_score_get_duration_ms(s_phone_ui_data->vibe_score) + vibe_score_get_repeat_delay_ms(s_phone_ui_data->vibe_score); vibe_repeat_interval_sec = DIVIDE_CEIL(vibe_interval_ms, MS_PER_SECOND); -#else - vibe_repeat_interval_sec = 2; -#endif prv_ring(NULL); regular_timer_add_multisecond_callback(&s_phone_ui_data->ring_timer, vibe_repeat_interval_sec); } static void prv_stop_ringing(void) { regular_timer_remove_callback(&s_phone_ui_data->ring_timer); -#if CAPABILITY_HAS_VIBE_SCORES if (s_phone_ui_data->vibe_score) { vibe_score_destroy(s_phone_ui_data->vibe_score); s_phone_ui_data->vibe_score = NULL; } -#endif vibes_cancel(); } @@ -509,7 +496,6 @@ static void prv_stop_ringing(void) { //! Call duration related functions static void prv_show_call_status(void) { layer_set_hidden(&s_phone_ui_data->call_status_text_layer.layer, false); -#if !PLATFORM_TINTIN s_phone_ui_data->call_status_text_layer.layer.bounds.origin.y = DURATION_ANIMATION_START_OFFSET; Animation *upward = property_animation_get_animation( property_animation_create_bounds_origin(&s_phone_ui_data->call_status_text_layer.layer, @@ -528,15 +514,15 @@ static void prv_show_call_status(void) { Animation *animation = animation_sequence_create(upward, bounceback, NULL); s_phone_ui_data->call_status_animation = animation; animation_schedule(animation); -#else - s_phone_ui_data->call_status_text_layer.layer.bounds.origin = GPointZero; -#endif } static void prv_update_call_time(void *unused) { if (s_phone_ui_data == NULL) { return; } + if (s_phone_ui_data->call_start_time == 0) { + return; + } if (layer_get_hidden(&s_phone_ui_data->call_status_text_layer.layer)) { prv_show_call_status(); @@ -572,6 +558,7 @@ static void prv_start_call_duration_timer(void) { static void prv_stop_call_duration_timer(void) { evented_timer_cancel(s_phone_ui_data->call_duration_timer); s_phone_ui_data->call_duration_timer = EVENTED_TIMER_INVALID_ID; + s_phone_ui_data->call_start_time = 0; } static void prv_set_status_text(CallStatus status) { @@ -581,14 +568,12 @@ static void prv_set_status_text(CallStatus status) { } else if (status == DISCONNECTED) { i18n_get_with_buffer("Disconnected", s_phone_ui_data->call_status_text_buf, CALL_STATUS_BUFFER_LENGTH); + } else if (status == ENDED) { + i18n_get_with_buffer("Call Ended", s_phone_ui_data->call_status_text_buf, + CALL_STATUS_BUFFER_LENGTH); } else { - if (s_phone_ui_data->call_start_time) { - i18n_get_with_buffer("Call Ended", s_phone_ui_data->call_status_text_buf, - CALL_STATUS_BUFFER_LENGTH); - } else { - i18n_get_with_buffer("Call Declined", s_phone_ui_data->call_status_text_buf, - CALL_STATUS_BUFFER_LENGTH); - } + i18n_get_with_buffer("Call Declined", s_phone_ui_data->call_status_text_buf, + CALL_STATUS_BUFFER_LENGTH); } text_layer_set_text(&s_phone_ui_data->call_status_text_layer, @@ -711,7 +696,6 @@ static void prv_sms_reply_click_handler(ClickRecognizerRef recognizer, void *unu } static void prv_pop_click_handler(ClickRecognizerRef recognizer, void *unused) { - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_POP_COUNT, AnalyticsClient_System); prv_stop_ringing(); prv_window_pop(); } @@ -723,7 +707,6 @@ static void prv_hide_action_bar(void) { } s_phone_ui_data->hid_action_bar = true; -#if !PLATFORM_TINTIN const GRect window_bounds = s_phone_ui_data->window.layer.bounds; GRect offscreen = GRect(window_bounds.size.w, 0, PBL_IF_RECT_ELSE(ACTION_BAR_WIDTH, 0), window_bounds.size.h); @@ -757,14 +740,6 @@ static void prv_hide_action_bar(void) { // Center the kino icon s_phone_ui_data->icon_layer.layer.frame.origin.x = ICON_POSITION_CENTERED_X; #endif - -#else - const GRect container_bounds = s_phone_ui_data->core_ui_container.bounds; - const GRect onscreen = GRect(ACTION_BAR_WIDTH / 2, 0, - container_bounds.size.w, container_bounds.size.h); - layer_set_hidden(&s_phone_ui_data->action_bar.layer, true /* hide */); - layer_set_bounds(&s_phone_ui_data->core_ui_container, &onscreen); -#endif } //! Action bar setup functions @@ -919,16 +894,13 @@ static void prv_phone_ui_deinit(void) { kino_layer_pause(&s_phone_ui_data->icon_layer); kino_layer_deinit(&s_phone_ui_data->icon_layer); -#if !PLATFORM_TINTIN // The reels will destroy intermediate images, but not the one currently on screen - // clean it up here. Note that we don't have to do this on Tintin/Bianca as we - // do not create an intermediary reel for animating. + // clean it up here. kino_reel_destroy(s_phone_ui_data->current_icon); animation_unschedule(s_phone_ui_data->bg_color_animation); animation_unschedule(s_phone_ui_data->action_bar_animation); animation_unschedule(s_phone_ui_data->call_status_animation); -#endif s_phone_ui_data->current_icon = NULL; s_phone_ui_data->current_icon_id = 0; @@ -1022,11 +994,9 @@ static void prv_phone_ui_init(void) { .unload = prv_handle_window_unload, }); window_set_overrides_back_button(window, true); -#if !PLATFORM_TINTIN s_phone_ui_data->bg_color.left = DEFAULT_COLOR; s_phone_ui_data->bg_color.right = DEFAULT_COLOR; s_phone_ui_data->bg_color.boundary = 0; -#endif const int16_t width = window->layer.bounds.size.w - (TEXT_MARGIN_WIDTH * 2); @@ -1089,7 +1059,7 @@ static void prv_phone_ui_init(void) { static bool prv_check_popups_are_blocked(void) { if (launcher_popups_are_blocked()) { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring call event. Popups are blocked"); + PBL_LOG_INFO("Ignoring call event. Popups are blocked"); return true; } return false; @@ -1172,7 +1142,7 @@ void phone_ui_handle_missed_call(void) { void phone_ui_handle_call_start(bool can_decline) { if (!s_phone_ui_data) { - PBL_LOG(LOG_LEVEL_ERROR, "Can't handle call start, UI isn't setup"); + PBL_LOG_ERR("Can't handle call start, UI isn't setup"); return; } @@ -1196,7 +1166,7 @@ void phone_ui_handle_call_start(bool can_decline) { void phone_ui_handle_call_end(bool call_accepted, bool disconnected) { if (!s_phone_ui_data) { - PBL_LOG(LOG_LEVEL_ERROR, "Can't handle call end, UI isn't setup"); + PBL_LOG_ERR("Can't handle call end, UI isn't setup"); return; } @@ -1210,7 +1180,7 @@ void phone_ui_handle_call_end(bool call_accepted, bool disconnected) { if (call_accepted) { prv_set_icon_resource(TIMELINE_RESOURCE_DURING_PHONE_CALL); prv_set_window_color(ACCEPT_COLOR, true); - prv_set_status_text(ACCEPTED); + prv_set_status_text(ENDED); } else { prv_set_icon_resource(TIMELINE_RESOURCE_DISMISSED_PHONE_CALL); prv_set_window_color(DECLINE_COLOR, true); @@ -1225,13 +1195,12 @@ void phone_ui_handle_call_end(bool call_accepted, bool disconnected) { } void phone_ui_handle_call_hide(void) { - // Just pop the window - it'll handle all the cleanup prv_window_pop(); } void phone_ui_handle_caller_id(PebblePhoneCaller *caller) { if (!s_phone_ui_data) { - PBL_LOG(LOG_LEVEL_ERROR, "Can't update caller id, UI isn't setup"); + PBL_LOG_ERR("Can't update caller id, UI isn't setup"); return; } diff --git a/src/fw/popups/phone_ui.h b/src/fw/popups/phone_ui.h index 0075b49e69..6a72f79559 100644 --- a/src/fw/popups/phone_ui.h +++ b/src/fw/popups/phone_ui.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" -#include "services/normal/phone_call_util.h" +#include "pbl/services/phone_call_util.h" #include diff --git a/src/fw/popups/switch_worker_ui.c b/src/fw/popups/switch_worker_ui.c index b40e6fe89b..9b279a9324 100644 --- a/src/fw/popups/switch_worker_ui.c +++ b/src/fw/popups/switch_worker_ui.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "switch_worker_ui.h" @@ -27,8 +14,8 @@ #include "process_management/app_install_manager.h" #include "process_management/process_manager.h" #include "process_management/worker_manager.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/app_cache.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/app_cache.h" typedef struct { AppInstallId new_worker_id; diff --git a/src/fw/popups/switch_worker_ui.h b/src/fw/popups/switch_worker_ui.h index 5cda83905d..c16ed2c60f 100644 --- a/src/fw/popups/switch_worker_ui.h +++ b/src/fw/popups/switch_worker_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/popups/timeline/peek.c b/src/fw/popups/timeline/peek.c index 51dd66777a..1131e382d7 100644 --- a/src/fw/popups/timeline/peek.c +++ b/src/fw/popups/timeline/peek.c @@ -1,25 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "peek_private.h" #include "applib/ui/property_animation.h" +#include "process_management/app_manager.h" #include "applib/ui/window_stack.h" #include "applib/unobstructed_area_service.h" -#include "apps/system_apps/timeline/timeline_common.h" +#include "apps/system/timeline/common.h" #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "kernel/ui/kernel_ui.h" @@ -171,12 +159,45 @@ static void prv_unschedule_animation(TimelinePeek *peek) { peek->animation = NULL; } -static bool prv_should_use_unobstructed_area() { +//! Returns true if we're running an upscaled legacy app in scaling mode. +static bool prv_is_upscaled_app(void) { GSize app_framebuffer_size; app_manager_get_framebuffer_size(&app_framebuffer_size); + if (app_framebuffer_size.h == DISP_ROWS) { + return false; // Native app, not upscaled + } +#if defined(CONFIG_APP_SCALING) && !defined(CONFIG_RECOVERY_FW) + // On platforms that support scaling mode, check the preference + return shell_prefs_get_legacy_app_render_mode() >= LegacyAppRenderMode_ScalingNearest; +#else + return false; // Other platforms use bezel mode only +#endif +} + +static bool prv_should_use_unobstructed_area(void) { + GSize app_framebuffer_size; + app_manager_get_framebuffer_size(&app_framebuffer_size); + // For upscaled apps in scaling mode, the peek always covers part of the scaled content, + // so we need to notify the app. + // For bezel mode or native apps, check if the gap is less than peek height. + if (prv_is_upscaled_app()) { + return true; + } return (DISP_ROWS - app_framebuffer_size.h) < TIMELINE_PEEK_HEIGHT; } +//! Scales a display Y coordinate to framebuffer coordinates for upscaled apps. +//! For native apps or bezel mode, returns the coordinate unchanged. +static int16_t prv_scale_y_to_framebuffer(int16_t display_y) { + if (!prv_is_upscaled_app()) { + return display_y; + } + GSize app_framebuffer_size; + app_manager_get_framebuffer_size(&app_framebuffer_size); + // Scale from display coordinates to framebuffer coordinates + return (display_y * app_framebuffer_size.h) / DISP_ROWS; +} + static void prv_peek_frame_setup(Animation *animation) { PropertyAnimation *prop_anim = (PropertyAnimation *)animation; TimelinePeek *peek; @@ -186,7 +207,8 @@ static void prv_peek_frame_setup(Animation *animation) { GRect to_frame; property_animation_get_to_grect(prop_anim, &to_frame); if (prv_should_use_unobstructed_area()) { - unobstructed_area_service_will_change(from_frame.origin.y, to_frame.origin.y); + unobstructed_area_service_will_change(prv_scale_y_to_framebuffer(from_frame.origin.y), + prv_scale_y_to_framebuffer(to_frame.origin.y)); } } @@ -198,8 +220,10 @@ static void prv_peek_frame_update(Animation *animation, AnimationProgress progre GRect to_frame; property_animation_get_to_grect(prop_anim, &to_frame); if (prv_should_use_unobstructed_area()) { - unobstructed_area_service_change(peek->layout_layer.frame.origin.y, to_frame.origin.y, - progress); + unobstructed_area_service_change( + prv_scale_y_to_framebuffer(peek->layout_layer.frame.origin.y), + prv_scale_y_to_framebuffer(to_frame.origin.y), + progress); } } @@ -208,7 +232,7 @@ static void prv_peek_frame_teardown(Animation *animation) { GRect to_frame; property_animation_get_to_grect(prop_anim, &to_frame); if (prv_should_use_unobstructed_area()) { - unobstructed_area_service_did_change(to_frame.origin.y); + unobstructed_area_service_did_change(prv_scale_y_to_framebuffer(to_frame.origin.y)); } } @@ -389,7 +413,7 @@ static void prv_push_timeline_peek(void *unused) { void timeline_peek_init(void) { TimelinePeek *peek = &s_peek; *peek = (TimelinePeek) { -#if CAPABILITY_HAS_TIMELINE_PEEK && !SHELL_SDK +#ifndef CONFIG_SHELL_SDK .enabled = timeline_peek_prefs_get_enabled(), #endif }; @@ -400,16 +424,13 @@ void timeline_peek_init(void) { layer_init(&peek->layout_layer, &TIMELINE_PEEK_FRAME_HIDDEN); layer_add_child(&peek->window.layer, &peek->layout_layer); -#if CAPABILITY_HAS_TIMELINE_PEEK timeline_peek_set_show_before_time(timeline_peek_prefs_get_before_time() * SECONDS_PER_MINUTE); -#endif // Wait one event loop to show the timeline peek launcher_task_add_callback(prv_push_timeline_peek, NULL); } static void prv_set_visible(bool visible, bool animated) { -#if CAPABILITY_HAS_TIMELINE_PEEK TimelinePeek *peek = &s_peek; if (!peek->started && visible) { cron_job_schedule(&s_timeline_peek_job); @@ -417,7 +438,6 @@ static void prv_set_visible(bool visible, bool animated) { cron_job_unschedule(&s_timeline_peek_job); } prv_transition_frame(peek, visible, animated); -#endif } static bool prv_can_animate(void) { @@ -426,7 +446,7 @@ static bool prv_can_animate(void) { void timeline_peek_set_visible(bool visible, bool animated) { TimelinePeek *peek = &s_peek; -#if !SHELL_SDK +#ifndef CONFIG_SHELL_SDK if (!peek->exists) { visible = false; } @@ -471,7 +491,7 @@ void timeline_peek_dismiss(void) { } else { char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(&item->header.id, uuid_buffer); - PBL_LOG(LOG_LEVEL_WARNING, "Failed to dismiss Timeline Peek event %s (status: %"PRIi32")", + PBL_LOG_WRN("Failed to dismiss Timeline Peek event %s (status: %"PRIi32")", uuid_buffer, rv); } } @@ -482,7 +502,13 @@ int16_t timeline_peek_get_origin_y(void) { } int16_t timeline_peek_get_obstruction_origin_y(void) { - return prv_should_use_unobstructed_area() ? timeline_peek_get_origin_y() : DISP_ROWS; + if (prv_should_use_unobstructed_area()) { + return prv_scale_y_to_framebuffer(timeline_peek_get_origin_y()); + } + // Not using unobstructed area - return framebuffer height to indicate no obstruction + GSize app_framebuffer_size; + app_manager_get_framebuffer_size(&app_framebuffer_size); + return app_framebuffer_size.h; } void timeline_peek_get_item_id(TimelineItemId *item_id_out) { @@ -518,7 +544,6 @@ void timeline_peek_set_enabled(bool enabled) { } void timeline_peek_handle_peek_event(PebbleTimelinePeekEvent *event) { -#if CAPABILITY_HAS_TIMELINE_PEEK TimelinePeek *peek = &s_peek; peek->future_empty = event->is_future_empty; bool show = false; @@ -553,19 +578,14 @@ void timeline_peek_handle_peek_event(PebbleTimelinePeekEvent *event) { false /* is_first_event */, true /* animated */); } timeline_item_free_allocated_buffer(&item); -#endif } void timeline_peek_handle_process_start(void) { -#if CAPABILITY_HAS_TIMELINE_PEEK timeline_peek_set_visible(true, false /* animated */); -#endif } void timeline_peek_handle_process_kill(void) { -#if CAPABILITY_HAS_TIMELINE_PEEK timeline_peek_set_visible(false, false /* animated */); -#endif } #if UNITTEST diff --git a/src/fw/popups/timeline/peek.h b/src/fw/popups/timeline/peek.h index 2152b1e796..fa5fc831a0 100644 --- a/src/fw/popups/timeline/peek.h +++ b/src/fw/popups/timeline/peek.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "applib/ui/animation.h" #include "applib/ui/window.h" #include "kernel/events.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/timeline/timeline.h" #define TIMELINE_PEEK_HEIGHT \ PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, \ @@ -37,13 +24,25 @@ /* This is the same as Medium until Small is designed */ \ /* small */ PBL_IF_RECT_ELSE(30, 51), \ /* medium */ PBL_IF_RECT_ELSE(30, 51), \ - /* large */ 34, \ + /* large */ PBL_IF_RECT_ELSE(34, 51), \ /* This is the same as Large until ExtraLarge is designed */ \ - /* x-large */ 34 \ + /* x-large */ PBL_IF_RECT_ELSE(34, 51) \ ) #define TIMELINE_PEEK_MARGIN (5) -#define TIMELINE_PEEK_ORIGIN_Y_VISIBLE PBL_IF_RECT_ELSE(DISP_ROWS - TIMELINE_PEEK_HEIGHT, 112) + +// On round displays the peek is drawn at full width and the round display +// mask clips the corners that fall outside the circle. Spalding's original +// origin of 112 was chosen for an 180-tall display; scale it to the current +// display height so taller round panels (e.g. 260-tall getafix) sit in the +// equivalent lower portion of the circle rather than bottom-flush, which +// would put the peek deep in the narrow bottom curve of the circle. +#if PBL_ROUND +#define TIMELINE_PEEK_ORIGIN_Y_VISIBLE ((DISP_ROWS * 112) / 180) +#else +#define TIMELINE_PEEK_ORIGIN_Y_VISIBLE (DISP_ROWS - TIMELINE_PEEK_HEIGHT) +#endif + #define TIMELINE_PEEK_FRAME_VISIBLE GRect(0, TIMELINE_PEEK_ORIGIN_Y_VISIBLE, DISP_COLS, \ TIMELINE_PEEK_HEIGHT) diff --git a/src/fw/popups/timeline/peek_animations.c b/src/fw/popups/timeline/peek_animations.c index 5714b3f86a..d80b8e83ba 100644 --- a/src/fw/popups/timeline/peek_animations.c +++ b/src/fw/popups/timeline/peek_animations.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "peek_animations.h" diff --git a/src/fw/popups/timeline/peek_animations.h b/src/fw/popups/timeline/peek_animations.h index 41f82998b0..59fd9db29e 100644 --- a/src/fw/popups/timeline/peek_animations.h +++ b/src/fw/popups/timeline/peek_animations.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/popups/timeline/peek_private.h b/src/fw/popups/timeline/peek_private.h index 06ef34d61a..c78bd13f87 100644 --- a/src/fw/popups/timeline/peek_private.h +++ b/src/fw/popups/timeline/peek_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "applib/ui/animation.h" #include "applib/ui/window.h" -#include "services/normal/timeline/timeline_layout.h" +#include "pbl/services/timeline/timeline_layout.h" typedef struct PeekLayout { TimelineLayoutInfo info; diff --git a/src/fw/popups/timeline/timeline_item_layer.c b/src/fw/popups/timeline/timeline_item_layer.c index e03b56cfc0..fb0a1b0283 100644 --- a/src/fw/popups/timeline/timeline_item_layer.c +++ b/src/fw/popups/timeline/timeline_item_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "timeline_item_layer.h" @@ -20,16 +7,16 @@ #include "applib/ui/action_menu_window.h" #include "applib/ui/window.h" #include "applib/ui/window_manager.h" -#include "apps/system_apps/timeline/timeline.h" +#include "apps/system/timeline/timeline.h" #include "kernel/pbl_malloc.h" #include "kernel/pebble_tasks.h" #include "kernel/ui/kernel_ui.h" #include "kernel/ui/modals/modal_manager.h" #include "process_state/app_state/app_state.h" -#include "services/normal/timeline/actions_endpoint.h" -#include "services/normal/timeline/layout_layer.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/timeline/actions_endpoint.h" +#include "pbl/services/timeline/layout_layer.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_actions.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" diff --git a/src/fw/popups/timeline/timeline_item_layer.h b/src/fw/popups/timeline/timeline_item_layer.h index b1e7ef734a..a1cc7edfed 100644 --- a/src/fw/popups/timeline/timeline_item_layer.h +++ b/src/fw/popups/timeline/timeline_item_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,8 +8,8 @@ #include "applib/ui/layer.h" #include "applib/ui/property_animation_private.h" #include "kernel/events.h" -#include "services/normal/timeline/item.h" -#include "services/normal/timeline/timeline_layout.h" +#include "pbl/services/timeline/item.h" +#include "pbl/services/timeline/timeline_layout.h" //! The timeline item layer is a mock UI used to display timeline items //! until actual layouts are implemented. It is somewhat related to the diff --git a/src/fw/popups/wakeup_ui.c b/src/fw/popups/wakeup_ui.c index 825037cb5e..866a39295e 100644 --- a/src/fw/popups/wakeup_ui.c +++ b/src/fw/popups/wakeup_ui.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "wakeup_ui.h" @@ -27,7 +14,7 @@ #include "process_management/app_install_manager.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" typedef void (*EachCb)(AppInstallEntry *entry, void *data); diff --git a/src/fw/popups/wakeup_ui.h b/src/fw/popups/wakeup_ui.h index d81f93b2e2..65838533ea 100644 --- a/src/fw/popups/wakeup_ui.h +++ b/src/fw/popups/wakeup_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/prj.conf b/src/fw/prj.conf new file mode 100644 index 0000000000..411370163a --- /dev/null +++ b/src/fw/prj.conf @@ -0,0 +1,35 @@ +# PebbleOS application-level Kconfig overrides. +# Values here are applied on top of the board defconfig and below variant- +# specific prj_.conf overrides. + +# Common services. +CONFIG_SERVICE_ACCEL_MANAGER=y +CONFIG_SERVICE_ANALYTICS=y +CONFIG_SERVICE_ANIMATION_SERVICE=y +CONFIG_SERVICE_BATTERY=y +CONFIG_SERVICE_BLUETOOTH=y +CONFIG_SERVICE_BOOT_SPLASH=y +CONFIG_SERVICE_CLOCK=y +CONFIG_SERVICE_COMM_SESSION=y +CONFIG_SERVICE_COMPOSITOR=y +CONFIG_SERVICE_CRON=y +CONFIG_SERVICE_DEBOUNCED_CONNECTION_SERVICE=y +CONFIG_SERVICE_EVENT_SERVICE=y +CONFIG_SERVICE_EVENTED_TIMER=y +CONFIG_SERVICE_FIRMWARE_UPDATE=y +CONFIG_SERVICE_GET_BYTES=y +CONFIG_SERVICE_I18N=y +CONFIG_SERVICE_LIGHT=y +CONFIG_SERVICE_NEW_TIMER=y +CONFIG_SERVICE_PHONE_PP=y +CONFIG_SERVICE_PING=y +CONFIG_SERVICE_POLL_REMOTE=y +CONFIG_SERVICE_PRF_UPDATE=y +CONFIG_SERVICE_PUT_BYTES=y +CONFIG_SERVICE_REGISTRY_ENDPOINT=y +CONFIG_SERVICE_REGULAR_TIMER=y +CONFIG_SERVICE_SERVICES_COMMON=y +CONFIG_SERVICE_SHARED_PRF_STORAGE=y +CONFIG_SERVICE_SYSTEM_TASK=y +CONFIG_SERVICE_TICK_TIMER=y +CONFIG_SERVICE_VIBE_PATTERN=y diff --git a/src/fw/prj_normal.conf b/src/fw/prj_normal.conf new file mode 100644 index 0000000000..7e21680b8d --- /dev/null +++ b/src/fw/prj_normal.conf @@ -0,0 +1,39 @@ +# Normal (non-recovery) firmware Kconfig overrides. +# Applied on top of prj.conf when building with --variant=normal. + +# Normal services. +CONFIG_SERVICE_ACTIVITY=y +CONFIG_SERVICE_ALARMS=y +CONFIG_SERVICE_APP_CACHE=y +CONFIG_SERVICE_APP_FETCH_ENDPOINT=y +CONFIG_SERVICE_APP_GLANCES=y +CONFIG_SERVICE_APP_INBOX_SERVICE=y +CONFIG_SERVICE_APP_MESSAGE=y +CONFIG_SERVICE_APP_ORDER_ENDPOINT=y +CONFIG_SERVICE_APP_OUTBOX_SERVICE=y +CONFIG_SERVICE_AUDIO_ENDPOINT=y +CONFIG_SERVICE_BLOB_DB=y +CONFIG_SERVICE_CONTACTS=y +CONFIG_SERVICE_DATA_LOGGING=y +CONFIG_SERVICE_FILESYSTEM=y +CONFIG_SERVICE_HEALTH_SYNC_ENDPOINT=y +CONFIG_SERVICE_LEGACY=y +CONFIG_SERVICE_MUSIC=y +CONFIG_SERVICE_NOTIFICATIONS=y +CONFIG_SERVICE_ORIENTATION_MANAGER=y +CONFIG_SERVICE_PERSIST=y +CONFIG_SERVICE_PHONE_CALL=y +CONFIG_SERVICE_POWERMODE_SERVICE=y +CONFIG_SERVICE_PROCESS_MANAGEMENT=y +CONFIG_SERVICE_PROTOBUF_LOG=y +CONFIG_SERVICE_SEND_TEXT_SERVICE=y +CONFIG_SERVICE_SERVICES_NORMAL=y +CONFIG_SERVICE_SETTINGS=y +CONFIG_SERVICE_SPEAKER=y +CONFIG_SERVICE_STATIONARY=y +CONFIG_SERVICE_TIMELINE=y +CONFIG_SERVICE_TIMEZONE_DATABASE=y +CONFIG_SERVICE_VIBES=y +CONFIG_SERVICE_VOICE_ENDPOINT=y +CONFIG_SERVICE_WAKEUP=y +CONFIG_SERVICE_WEATHER=y diff --git a/src/fw/prj_prf.conf b/src/fw/prj_prf.conf new file mode 100644 index 0000000000..83673cfedc --- /dev/null +++ b/src/fw/prj_prf.conf @@ -0,0 +1,12 @@ +# Recovery (PRF) firmware Kconfig overrides. +# Applied on top of prj.conf when building with --variant=prf. + +# PRF firmware never reports to Memfault — the recovery image must stay +# minimal and is the fallback used to recover from coredumps. +CONFIG_MEMFAULT=n + +# PRF services. +CONFIG_SERVICE_IDLE_WATCHDOG=y + +# Recovery firmware identity. +CONFIG_RECOVERY_FW=y diff --git a/src/fw/process_management/app_custom_icon.c b/src/fw/process_management/app_custom_icon.c index ca041a21cd..5abf28bc07 100644 --- a/src/fw/process_management/app_custom_icon.c +++ b/src/fw/process_management/app_custom_icon.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_custom_icon.h" @@ -21,8 +8,8 @@ #include "apps/system_app_ids.h" #include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" #include "util/attributes.h" #include "util/math.h" diff --git a/src/fw/process_management/app_custom_icon.h b/src/fw/process_management/app_custom_icon.h index 24aa5b21a2..d01ed2b7ba 100644 --- a/src/fw/process_management/app_custom_icon.h +++ b/src/fw/process_management/app_custom_icon.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_management/app_install_manager.c b/src/fw/process_management/app_install_manager.c index cb36735e2a..c2dc7ccae2 100644 --- a/src/fw/process_management/app_install_manager.c +++ b/src/fw/process_management/app_install_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_install_manager.h" #include "app_install_manager_private.h" @@ -31,13 +18,13 @@ #include "kernel/util/sleep.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/common/comm_session/app_session_capabilities.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/app_db.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/persist.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/comm_session/app_session_capabilities.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/app_db.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/persist.h" +#include "pbl/services/process_management/app_storage.h" #include "system/logging.h" #include "system/passert.h" #include "util/circular_cache.h" @@ -77,7 +64,7 @@ static AppInstallId s_pending_app_deletion; static AppInstallId s_pending_worker_deletion; // PBL-31769: This should be moved to send_text.c -#if !PLATFORM_TINTIN && defined(APP_ID_SEND_TEXT) +#if defined(APP_ID_SEND_TEXT) static EventServiceInfo s_capabilities_event_info; #endif @@ -334,7 +321,7 @@ static void prv_app_install_delete(AppInstallId id, Uuid *uuid, bool app_upgrade if (delete_cache) { // only log when we actually delete the cache entry. This is so we don't print out 100 logs // during an app cache clear - PBL_LOG(LOG_LEVEL_INFO, "Deleting app with id %"PRId32"", id); + PBL_LOG_INFO("Deleting app with id %"PRId32"", id); app_cache_remove_entry(id); } } @@ -350,7 +337,7 @@ static void prv_delete_pending_id(AppInstallId *app_id) { static void prv_process_pending_deletions(void) { prv_delete_pending_id(&s_pending_app_deletion); prv_delete_pending_id(&s_pending_worker_deletion); - PBL_LOG(LOG_LEVEL_DEBUG, "Finished processing pending deletions"); + PBL_LOG_DBG("Finished processing pending deletions"); } typedef struct InstallCallbackData { @@ -399,7 +386,7 @@ static void app_install_launcher_task_callback(void *context) { if (prv_ids_equal(cur_app_id, to_kill) || ((s_install_callback_data.install_type == APP_DB_CLEARED) && (app_install_id_from_app_db(cur_app_id)))) { - PBL_LOG(LOG_LEVEL_DEBUG, "close and delay callbacks for app closing"); + PBL_LOG_DBG("close and delay callbacks for app closing"); s_install_callback_data.callback_paused_for_app = true; s_pending_app_deletion = cur_app_id; @@ -414,7 +401,7 @@ static void app_install_launcher_task_callback(void *context) { if (prv_ids_equal(cur_worker_id, to_kill) || ((s_install_callback_data.install_type == APP_DB_CLEARED) && (app_install_id_from_app_db(cur_worker_id)))) { - PBL_LOG(LOG_LEVEL_DEBUG, "close and delay callbacks for worker closing"); + PBL_LOG_DBG("close and delay callbacks for worker closing"); s_install_callback_data.callback_paused_for_worker = true; s_pending_worker_deletion = cur_worker_id; @@ -431,7 +418,7 @@ static void app_install_launcher_task_callback(void *context) { } } - PBL_LOG(LOG_LEVEL_DEBUG, "app_install_invoke_callbacks"); + PBL_LOG_DBG("app_install_invoke_callbacks"); app_install_invoke_callbacks(s_install_callback_data.install_type, s_install_callback_data.install_id); @@ -448,7 +435,9 @@ static void app_install_launcher_task_callback(void *context) { // app, not during an AppDB clear. if (!app_upgrade) { persist_service_delete_file(s_install_callback_data.uuid); +#if !defined(CONFIG_RECOVERY_FW) comm_session_app_session_capabilities_evict(s_install_callback_data.uuid); +#endif } break; case APP_DB_CLEARED: @@ -474,7 +463,7 @@ static void app_install_launcher_task_callback(void *context) { bool app_install_do_callbacks(InstallEventType event_type, AppInstallId install_id, Uuid *uuid, InstallCallbackDoneCallback done_callback, void* callback_data) { if (s_install_callback_data.callback_in_progress) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to do app callbacks, already in progress"); + PBL_LOG_ERR("Failed to do app callbacks, already in progress"); return false; } @@ -559,7 +548,7 @@ static int prv_cmp_recent_apps(void *a, void *b) { } // PBL-31769: This should be moved to send_text.c -#if !PLATFORM_TINTIN && defined(APP_ID_SEND_TEXT) +#if defined(APP_ID_SEND_TEXT) static void prv_capabilities_changed_event_handler(PebbleEvent *event, void *context) { // We only care if send text support changed right now if (!event->capabilities.flags_diff.send_text_support) { @@ -579,7 +568,7 @@ void app_install_manager_init(void) { s_recent_apps.mutex = mutex_create_recursive(); // PBL-31769: This should be moved to send_text.c -#if !PLATFORM_TINTIN && defined(APP_ID_SEND_TEXT) +#if defined(APP_ID_SEND_TEXT) s_capabilities_event_info = (EventServiceInfo) { .type = PEBBLE_CAPABILITIES_CHANGED_EVENT, .handler = prv_capabilities_changed_event_handler, @@ -599,7 +588,7 @@ bool app_install_id_from_app_db(AppInstallId id) { static GColor prv_hard_coded_color_for_3rd_party_apps(Uuid *uuid) { // Remove this from Recovery FW for code size savings. -#if !defined(RECOVERY_FW) && !defined(PLATFORM_TINTIN) +#if !defined(CONFIG_RECOVERY_FW) // this is a temporary solution to enable custom colors for 3rd-party apps // please replace this, once PBL-19673 landed @@ -625,7 +614,7 @@ static GColor prv_hard_coded_color_for_3rd_party_apps(Uuid *uuid) { static GColor prv_valid_color_from_uuid(GColor color, Uuid *uuid) { -#if PLATFORM_TINTIN || PLATFORM_SILK || PLATFORM_ASTERIX +#ifdef CONFIG_BOARD_FAMILY_ASTERIX return GColorClear; #endif @@ -677,7 +666,7 @@ static bool prv_app_install_entry_from_resource_registry_entry(const AppRegistry if (resource_load_byte_range_system(SYSTEM_APP, reg_entry->bin_resource_id, 0, (uint8_t *)app_header, sizeof(*app_header)) != sizeof(*app_header)) { - PBL_LOG(LOG_LEVEL_WARNING, "Stored app with resource id %d not found in resources", + PBL_LOG_WRN("Stored app with resource id %d not found in resources", reg_entry->bin_resource_id); goto done; } @@ -746,7 +735,7 @@ bool app_install_get_entry_for_install_id(AppInstallId install_id, AppInstallEnt break; case AppInstallStorageInvalid: case AppInstallStorageFlash: - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid app registry type %d", reg_entry->type); + PBL_LOG_DBG("Invalid app registry type %d", reg_entry->type); break; } if (rv) { @@ -763,7 +752,7 @@ bool app_install_get_entry_for_install_id(AppInstallId install_id, AppInstallEnt return rv; } - PBL_LOG(LOG_LEVEL_ERROR, "Failed to get entry for id %"PRId32, install_id); + PBL_LOG_ERR("Failed to get entry for id %"PRId32, install_id); return false; } @@ -797,7 +786,7 @@ static const PebbleProcessMd *prv_get_md_for_reg_entry(const AppRegistryEntry *r PebbleProcessInfo app_header; if (resource_load_byte_range_system(SYSTEM_APP, reg_entry->bin_resource_id, 0, (uint8_t *)&app_header, sizeof(app_header)) != sizeof(app_header)) { - PBL_LOG(LOG_LEVEL_WARNING, "Stored app with resource id %d not found in resources", + PBL_LOG_WRN("Stored app with resource id %d not found in resources", reg_entry->bin_resource_id); return NULL; } @@ -817,7 +806,7 @@ static const PebbleProcessMd *prv_get_md_for_reg_entry(const AppRegistryEntry *r } static const PebbleProcessMd *prv_get_md_for_flash_id(AppInstallId id, bool worker) { -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW return NULL; #endif @@ -826,7 +815,7 @@ static const PebbleProcessMd *prv_get_md_for_flash_id(AppInstallId id, bool work const PebbleTask task = worker ? PebbleTask_Worker : PebbleTask_App; if (GET_APP_INFO_SUCCESS != app_storage_get_process_info(&app_header, build_id_buffer, id, task)) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to get app from flash with id %"PRIu32, id); + PBL_LOG_WRN("Failed to get app from flash with id %"PRIu32, id); return NULL; } @@ -848,7 +837,7 @@ const PebbleProcessMd *app_install_get_md(AppInstallId id, bool worker) { } // Not a registered app, fail. - PBL_LOG(LOG_LEVEL_ERROR, "Can't get PebbleProcessMd for app id %"PRId32, id); + PBL_LOG_ERR("Can't get PebbleProcessMd for app id %"PRId32, id); return NULL; } diff --git a/src/fw/process_management/app_install_manager.h b/src/fw/process_management/app_install_manager.h index a1c312fb16..1f1d98bfb0 100644 --- a/src/fw/process_management/app_install_manager.h +++ b/src/fw/process_management/app_install_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_management/app_install_manager_known_apps.h b/src/fw/process_management/app_install_manager_known_apps.h index 93b4aea456..3d3e1be63e 100644 --- a/src/fw/process_management/app_install_manager_known_apps.h +++ b/src/fw/process_management/app_install_manager_known_apps.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // @nolint // please don't change these values manually, they are derived from the spreadsheet diff --git a/src/fw/process_management/app_install_manager_private.h b/src/fw/process_management/app_install_manager_private.h index 422212ab6a..d26576ccbe 100644 --- a/src/fw/process_management/app_install_manager_private.h +++ b/src/fw/process_management/app_install_manager_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "app_install_manager.h" diff --git a/src/fw/process_management/app_install_types.h b/src/fw/process_management/app_install_types.h index 8e1b114930..b3e44b2282 100644 --- a/src/fw/process_management/app_install_types.h +++ b/src/fw/process_management/app_install_types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_management/app_manager.c b/src/fw/process_management/app_manager.c index e4fb456e4c..f48f964070 100644 --- a/src/fw/process_management/app_manager.c +++ b/src/fw/process_management/app_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_manager.h" #include "worker_manager.h" @@ -45,14 +32,20 @@ #include "process_state/app_state/app_state.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "resource/resource_mapped.h" -#include "services/common/analytics/analytics.h" -#include "services/common/compositor/compositor_transitions.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" -#include "services/normal/app_cache.h" -#include "services/normal/app_inbox_service.h" -#include "services/normal/app_outbox_service.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/compositor/compositor_transitions.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/new_timer/new_timer.h" +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/powermode_service.h" +#endif +#include "pbl/services/app_inbox_service.h" +#include "pbl/services/app_outbox_service.h" +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/speaker/speaker_service.h" +#endif #include "shell/normal/app_idle_timeout.h" #include "shell/normal/watchface.h" #include "shell/shell.h" @@ -108,15 +101,25 @@ typedef struct { } AppCrashInfo; static NextApp s_next_app; +#ifndef CONFIG_RECOVERY_FW +static bool s_powermode_hp_requested; +static TimerID s_powermode_release_timer; -static void prv_handle_app_start_analytics(const PebbleProcessMd *app_md, - const AppLaunchReason launch_reason); +#define POWERMODE_WATCHFACE_RELEASE_DELAY_MS 5000 +#endif // --------------------------------------------------------------------------------------------- void app_manager_init(void) { s_to_app_event_queue = xQueueCreate(MAX_TO_APP_EVENTS, sizeof(PebbleEvent)); s_app_task_context = (ProcessContext) { 0 }; + +#ifndef CONFIG_RECOVERY_FW + // Start in high-performance mode; released when a watchface is loaded + powermode_service_request_hp(); + s_powermode_hp_requested = true; + s_powermode_release_timer = new_timer_create(); +#endif } // --------------------------------------------------------------------------------------------- @@ -146,11 +149,7 @@ static void prv_app_task_main(void *entry_point) { // Enter unprivileged mode! const bool is_unprivileged = s_app_task_context.app_md->is_unprivileged; - // There are currently no Rocky.js APIs that need to be called while in privileged mode, so run - // in unprivileged mode for the built-in Rocky.js apps (Tictoc) as well: - const bool is_rocky_app = s_app_task_context.app_md->is_rocky_app; - - if (is_unprivileged || is_rocky_app) { + if (is_unprivileged) { mcu_state_set_thread_privilege(false); } @@ -162,7 +161,7 @@ static void prv_app_task_main(void *entry_point) { // be cleaned up, make it so the kernel can do it on the apps behalf and put // the call at the bottom of prv_app_cleanup. app_state_deinit(); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW app_message_close(); #endif @@ -196,13 +195,13 @@ void prv_dump_start_app_info(const PebbleProcessMd *app_md) { char *const sdk_platform = platform_type_get_name(process_metadata_get_app_sdk_platform(app_md)); - PBL_LOG(LOG_LEVEL_DEBUG, "Starting %s app <%s>", app_type, process_metadata_get_name(app_md)); + PBL_LOG_DBG("Starting %s app <%s>", app_type, process_metadata_get_name(app_md)); // new logging only allows for 2 %s per format string... - PBL_LOG(LOG_LEVEL_DEBUG, "Starting app with sdk platform %s", sdk_platform); + PBL_LOG_DBG("Starting app with sdk platform %s", sdk_platform); } -#define APP_STACK_ROCKY_SIZE (8 * 1024) -#define APP_STACK_NORMAL_SIZE (2 * 1024) +#define APP_STACK_JS_SIZE (8 * 1024) +#define APP_STACK_NORMAL_SIZE (4 * 1024) static size_t prv_get_app_segment_size(const PebbleProcessMd *app_md) { switch (process_metadata_get_app_sdk_type(app_md)) { @@ -211,16 +210,9 @@ static size_t prv_get_app_segment_size(const PebbleProcessMd *app_md) { case ProcessAppSDKType_Legacy3x: return APP_RAM_3X_SIZE; case ProcessAppSDKType_4x: -#if CAPABILITY_HAS_JAVASCRIPT - if (app_md->is_rocky_app) { - // on Spalding, we didn't have enough applib padding to guarantee both, - // 4.x native app heap + JerryScript statis + increased stack for Rocky. - // For now, we just decrease the amount of available heap as we don't use it. - // In the future, we will move the JS stack to the heap PBL-35783, - // make byte code swappable PBL-37937,and remove JerryScript's static PBL-40400. - // All of the above will work to our advantage so it's safe to make this simple - // change now. - return APP_RAM_4X_SIZE - (APP_STACK_ROCKY_SIZE - APP_STACK_NORMAL_SIZE); +#ifdef CONFIG_MODDABLE_XS + if (app_md->is_moddable_app) { + return APP_RAM_4X_SIZE - (APP_STACK_JS_SIZE - APP_STACK_NORMAL_SIZE); } #endif return APP_RAM_4X_SIZE; @@ -232,9 +224,9 @@ static size_t prv_get_app_segment_size(const PebbleProcessMd *app_md) { } static size_t prv_get_app_stack_size(const PebbleProcessMd *app_md) { -#if CAPABILITY_HAS_JAVASCRIPT - if (app_md->is_rocky_app) { - return APP_STACK_ROCKY_SIZE; +#ifdef CONFIG_MODDABLE_XS + if (app_md->is_moddable_app) { + return APP_STACK_JS_SIZE; } #endif return APP_STACK_NORMAL_SIZE; @@ -248,6 +240,17 @@ T_STATIC size_t prv_get_stack_guard_size(void) { return (uintptr_t)__stack_guard_size__; } +#ifndef CONFIG_RECOVERY_FW +// --------------------------------------------------------------------------------------------- +static void prv_powermode_release_cb(void *data) { + (void)data; + if (s_powermode_hp_requested) { + powermode_service_release_hp(); + s_powermode_hp_requested = false; + } +} +#endif + // --------------------------------------------------------------------------------------------- //! @return True on success, False if: //! - We fail to start the app. No app is running and the caller is responsible for starting @@ -275,27 +278,6 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, MemorySegment app_ram = prv_get_app_ram_segment(); -#if !UNITTEST - if (app_md->is_rocky_app) { - /* PBL-40376: Temp hack: put .rocky_bss at end of APP_RAM: - Interim solution until all statics are removed from applib & jerry. - These statics are only used for rocky apps, so it's OK that this overlaps/overlays with the - app heap for non-rocky apps. - */ - extern char __ROCKY_BSS_size__[]; - extern char __ROCKY_BSS__[]; - memset(__ROCKY_BSS__, 0, (size_t)__ROCKY_BSS_size__); - - // ROCKY_BSS is inside APP_RAM to make the syscall buffer checks pass. - // However, we want to avoid overlapping with any splits we're about to make: - app_ram.end = __ROCKY_BSS__; - - // Reduce the size available for the code + app heap, on Spalding the "padding" we had left - // isn't enough to fit Rocky + Jerry's .bss: - app_segment_size -= 1400; - } -#endif - memset((char *)app_ram.start + stack_guard_size, 0, memory_segment_get_size(&app_ram) - stack_guard_size); @@ -315,7 +297,7 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, void *entry_point = process_loader_load(app_md, PebbleTask_App, &app_segment); s_app_task_context.load_end = app_segment.start; if (!entry_point) { - PBL_LOG(LOG_LEVEL_WARNING, "Tried to launch an invalid app in bank %u!", + PBL_LOG_WRN("Tried to launch an invalid app in bank %u!", process_metadata_get_code_bank_num(app_md)); return false; } @@ -323,13 +305,10 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, const ResAppNum res_bank_num = process_metadata_get_res_bank_num(app_md); if (res_bank_num != SYSTEM_APP) { const ResourceVersion res_version = process_metadata_get_res_version(app_md); - // for RockyJS apps, we initialize without checking the for a match between - // binary's copy of the resource CRC and the actual CRC as it could be outdated - const ResourceVersion *const res_version_ptr = app_md->is_rocky_app ? NULL : &res_version; - if (!resource_init_app(res_bank_num, res_version_ptr)) { + if (!resource_init_app(res_bank_num, &res_version)) { // The resources are busted! Abort starting this app. APP_LOG(APP_LOG_LEVEL_ERROR, - "Checksum for resources differs or insufficient meta data for JavaScript app."); + "Checksum for resources differs."); return false; } } @@ -341,7 +320,7 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, // The rest of app_ram is available for app_state to use as it sees fit. if (!app_state_configure(&app_ram, sdk_type, timeline_peek_get_obstruction_origin_y())) { - PBL_LOG(LOG_LEVEL_ERROR, "App state configuration failed"); + PBL_LOG_ERR("App state configuration failed"); return false; } // The remaining space in app_segment is assigned to the app's heap. @@ -351,7 +330,7 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, // Don't fuzz 3rd party app heaps because likely many of them rely on accessing free'd memory bool enable_heap_fuzzing = (sdk_type == ProcessAppSDKType_System); Heap *app_heap = app_state_get_heap(); - PBL_LOG(LOG_LEVEL_DEBUG, "App heap init %p %p", + PBL_LOG_DBG("App heap init %p %p", app_segment.start, app_segment.end); heap_init(app_heap, app_segment.start, app_segment.end, enable_heap_fuzzing); heap_set_lock_impl(app_heap, (HeapLockImpl) { @@ -367,7 +346,7 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, AppInstallEntry entry; if (!app_install_get_entry_for_install_id(s_app_task_context.install_id, &entry)) { // cant retrieve app install entry for id - PBL_LOG(LOG_LEVEL_ERROR, "Failed to get entry for id %"PRId32, s_app_task_context.install_id); + PBL_LOG_ERR("Failed to get entry for id %"PRId32, s_app_task_context.install_id); return false; } if (app_install_entry_is_watchface(&entry) && !app_install_entry_is_hidden(&entry)) { @@ -395,7 +374,7 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, .puxStackBuffer = stack, }; - PBL_LOG(LOG_LEVEL_DEBUG, "Starting %s", task_name); + PBL_LOG_DBG("Starting %s", task_name); // Store slot of launched app for reboot support (flash apps only) reboot_set_slot_of_last_launched_app( @@ -409,12 +388,36 @@ static bool prv_app_start(const PebbleProcessMd *app_md, const void *args, system_app_state_machine_register_app_launch(s_app_task_context.install_id); - prv_handle_app_start_analytics(app_md, launch_reason); + // Track per-watchface usage metrics +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_SHELL_SDK) + if (app_md->process_type == ProcessTypeWatchface) { + PBL_ANALYTICS_TIMER_START(watchface_time_ms); + PBL_ANALYTICS_SET_STRING(watchface_name, process_metadata_get_name(app_md)); + char uuid_str[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&app_md->uuid, uuid_str); + PBL_ANALYTICS_SET_STRING(watchface_uuid, uuid_str); + } +#endif -#if CAPABILITY_HAS_HEALTH_TRACKING && !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) health_tracking_ui_register_app_launch(s_app_task_context.install_id); #endif +#ifndef CONFIG_RECOVERY_FW + if (app_md->process_type == ProcessTypeWatchface) { + if (s_powermode_hp_requested) { + new_timer_start(s_powermode_release_timer, POWERMODE_WATCHFACE_RELEASE_DELAY_MS, + prv_powermode_release_cb, NULL, 0); + } + } else { + new_timer_stop(s_powermode_release_timer); + if (!s_powermode_hp_requested) { + powermode_service_request_hp(); + s_powermode_hp_requested = true; + } + } +#endif + return true; } @@ -436,18 +439,18 @@ static void prv_app_cleanup(void) { // Perform app specific cleanup app_idle_timeout_stop(); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW app_inbox_service_unregister_all(); app_outbox_service_cleanup_all_pending_messages(); #endif light_reset_user_controlled(); + light_set_system_color(); sys_vibe_history_stop_collecting(); -#if !defined(PLATFORM_TINTIN) - ble_app_cleanup(); -#endif -#if CAPABILITY_HAS_MAPPABLE_FLASH - resource_mapped_release_all(PebbleTask_App); + sys_vibe_pattern_clear(); +#ifndef CONFIG_RECOVERY_FW + speaker_service_stop_for_task(PebbleTask_App); #endif + ble_app_cleanup(); app_comm_set_sniff_interval(SNIFF_INTERVAL_NORMAL); @@ -472,7 +475,7 @@ static void prv_app_show_crash_ui(AppInstallId install_id) { return; } -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) static AppCrashInfo crash_info = { 0 }; // If the same watchface crashes twice in one minute, then we show a dialog informing // the user that the watchface has crashed. Any button press will dismiss @@ -480,6 +483,8 @@ static void prv_app_show_crash_ui(AppInstallId install_id) { PBL_ASSERTN(install_id != INSTALL_ID_INVALID); if (crash_info.install_id != install_id || (crash_info.crash_ticks + RETURN_CRASH_TIMEOUT_TICKS) < rtc_get_ticks()) { + PBL_LOG_ERR("Watchface crashed (id=%"PRId32"); relaunching", install_id); + PBL_ANALYTICS_ADD(watchface_crash_count, 1); crash_info = (AppCrashInfo) { .install_id = install_id, .crash_ticks = rtc_get_ticks() @@ -527,7 +532,13 @@ static void prv_app_show_crash_ui(AppInstallId install_id) { i18n_free_all(crash_dialog); - PBL_LOG(LOG_LEVEL_DEBUG, "Watchface crashed, launching default."); + const uint32_t elapsed_ms = + (rtc_get_ticks() - crash_info.crash_ticks) * 1000 / RTC_TICKS_HZ; + PBL_LOG_WRN("Watchface crashed twice in %"PRIu32"ms (id=%"PRId32"); " + "reverting to default", + elapsed_ms, install_id); + PBL_ANALYTICS_ADD(watchface_crash_count, 1); + PBL_ANALYTICS_ADD(watchface_crash_revert_count, 1); crash_info = (AppCrashInfo) { 0 }; @@ -552,7 +563,7 @@ static void prv_app_show_crash_ui(AppInstallId install_id) { static bool prv_app_switch(bool gracefully) { ProcessContext *app_task_ctx = &s_app_task_context; - PBL_LOG(LOG_LEVEL_DEBUG, "Switching from '%s' to '%s', graceful=%d...", + PBL_LOG_DBG("Switching from '%s' to '%s', graceful=%d...", process_metadata_get_name(app_task_ctx->app_md), process_metadata_get_name(s_next_app.md), (int)gracefully); @@ -599,7 +610,7 @@ static bool prv_app_switch(bool gracefully) { if (s_next_app.md->process_storage != ProcessStorageFlash) { PBL_CROAK("Failed to start system app <%s>!", process_metadata_get_name(s_next_app.md)); } - PBL_LOG(LOG_LEVEL_WARNING, "Failed to start app <%s>! Restarting launcher", + PBL_LOG_WRN("Failed to start app <%s>! Restarting launcher", process_metadata_get_name(s_next_app.md)); prv_app_start(system_app_state_machine_system_start(), NULL, APP_LAUNCH_SYSTEM); @@ -622,7 +633,27 @@ static bool prv_app_switch(bool gracefully) { // --------------------------------------------------------------------------------------------- void app_manager_start_first_app(void) { const PebbleProcessMd* app_md = system_app_state_machine_system_start(); +#ifdef CONFIG_SHELL_SDK + // SDK shell's system_start returns the default watchface (a flash app) when one is set. + // If an install crashed partway through PutBytes, BlobDB has the entry and + // watchface_default_install_id already points at it, but the on-flash PBL_APP is + // truncated — process_loader_load returns NULL, prv_app_start fails, and the + // unconditional PBL_ASSERTN below bootloops the device. Clear the default and retry + // with sdk_app (builtin) so the emulator boots into something runnable. + // Normal shell can't hit this: its system_start always returns a builtin. + if (!prv_app_start(app_md, 0, APP_LAUNCH_SYSTEM)) { + PBL_LOG_ERR("Failed to start first app <%s>, clearing default watchface", + process_metadata_get_name(app_md)); + if (app_md->process_storage == ProcessStorageFlash) { + watchface_set_default_install_id(INSTALL_ID_INVALID); + app_install_release_md(app_md); + } + const PebbleProcessMd *fallback_md = system_app_state_machine_system_start(); + PBL_ASSERTN(prv_app_start(fallback_md, 0, APP_LAUNCH_SYSTEM)); + } +#else PBL_ASSERTN(prv_app_start(app_md, 0, APP_LAUNCH_SYSTEM)); +#endif s_first_app_launched = true; compositor_transition(NULL); } @@ -663,7 +694,7 @@ bool app_manager_launch_new_app(const AppLaunchConfig *config) { const AppInstallId new_app_id = app_install_get_id_for_uuid(&app_md->uuid); if (!config->restart && uuid_equal(&(app_md->uuid), &(s_app_task_context.app_md->uuid))) { - PBL_LOG(LOG_LEVEL_WARNING, "Ignoring launch for app <%s>, app is already running", + PBL_LOG_WRN("Ignoring launch for app <%s>, app is already running", process_metadata_get_name(app_md)); app_install_release_md(app_md); @@ -671,8 +702,7 @@ bool app_manager_launch_new_app(const AppLaunchConfig *config) { } if (process_metadata_get_run_level(app_md) < s_minimum_run_level) { - PBL_LOG(LOG_LEVEL_WARNING, - "Ignoring launch for app <%s>, minimum run level %d, app run level %d", + PBL_LOG_WRN("Ignoring launch for app <%s>, minimum run level %d, app run level %d", process_metadata_get_name(app_md), s_minimum_run_level, process_metadata_get_run_level(app_md)); @@ -714,14 +744,13 @@ void app_manager_handle_app_fetch_request_event(const PebbleAppFetchRequestEvent } // ----------------------------------------------------------------------------------------- -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) static AppInstallId prv_get_app_exit_reason_destination_install_id_override(void) { switch (s_app_task_context.exit_reason) { case APP_EXIT_NOT_SPECIFIED: return INSTALL_ID_INVALID; case APP_EXIT_ACTION_PERFORMED_SUCCESSFULLY: - PBL_LOG(LOG_LEVEL_INFO, - "Next app overridden with watchface because action was performed successfully"); + PBL_LOG_INFO("Next app overridden with watchface because action was performed successfully"); return watchface_get_default_install_id(); // Handling this case specifically instead of providing a default case ensures that the addition // of future exit reason values will cause compilation to fail until the new case is handled @@ -741,7 +770,7 @@ void app_manager_close_current_app(bool gracefully) { const AppInstallId current_app_id = s_app_task_context.install_id; AppInstallId destination_app_id = INSTALL_ID_INVALID; -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) destination_app_id = prv_get_app_exit_reason_destination_install_id_override(); #endif @@ -846,6 +875,8 @@ void app_manager_get_framebuffer_size(GSize *size) { return; case PlatformTypeDiorite: case PlatformTypeEmery: + case PlatformTypeFlint: + case PlatformTypeGabbro: *size = GSize(DISP_COLS, DISP_ROWS); return; } @@ -880,41 +911,6 @@ void command_get_active_app_metadata(void) { } -// Analytics -////////////////////////////////////////////////////////////// - -static void prv_handle_app_start_analytics(const PebbleProcessMd *app_md, - const AppLaunchReason launch_reason) { - analytics_event_app_launch(&app_md->uuid); - analytics_inc(ANALYTICS_APP_METRIC_LAUNCH_COUNT, AnalyticsClient_App); - analytics_stopwatch_start(ANALYTICS_APP_METRIC_FRONT_MOST_TIME, AnalyticsClient_App); - - Version app_sdk_version = process_metadata_get_sdk_version(app_md); - analytics_set(ANALYTICS_APP_METRIC_SDK_MAJOR_VERSION, app_sdk_version.major, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_SDK_MINOR_VERSION, app_sdk_version.minor, AnalyticsClient_App); - - Version app_version = process_metadata_get_process_version(app_md); - analytics_set(ANALYTICS_APP_METRIC_APP_MAJOR_VERSION, app_version.major, AnalyticsClient_App); - analytics_set(ANALYTICS_APP_METRIC_APP_MINOR_VERSION, app_version.minor, AnalyticsClient_App); - - ResourceVersion resource_version = process_metadata_get_res_version(app_md); - analytics_set(ANALYTICS_APP_METRIC_RESOURCE_TIMESTAMP, resource_version.timestamp, AnalyticsClient_App); - - if (app_md->is_rocky_app) { - analytics_inc(ANALYTICS_DEVICE_METRIC_APP_ROCKY_LAUNCH_COUNT, AnalyticsClient_System); - analytics_inc(ANALYTICS_APP_METRIC_ROCKY_LAUNCH_COUNT, AnalyticsClient_App); - } - - if (launch_reason == APP_LAUNCH_QUICK_LAUNCH) { - analytics_inc(ANALYTICS_DEVICE_METRIC_APP_QUICK_LAUNCH_COUNT, AnalyticsClient_System); - analytics_inc(ANALYTICS_APP_METRIC_QUICK_LAUNCH_COUNT, AnalyticsClient_App); - } else if (launch_reason == APP_LAUNCH_USER) { - analytics_inc(ANALYTICS_DEVICE_METRIC_APP_USER_LAUNCH_COUNT, AnalyticsClient_System); - analytics_inc(ANALYTICS_APP_METRIC_USER_LAUNCH_COUNT, AnalyticsClient_App); - } -} - - // ------------------------------------------------------------------------------------------- /*! @brief User mode access to its UUID. @@ -936,10 +932,6 @@ DEFINE_SYSCALL(bool, sys_get_current_app_is_js_allowed, void) { return (app_manager_get_current_app_md()->allow_js); } -DEFINE_SYSCALL(bool, sys_get_current_app_is_rocky_app, void) { - return (app_manager_get_current_app_md()->is_rocky_app); -} - DEFINE_SYSCALL(PlatformType, sys_get_current_app_sdk_platform, void) { return process_metadata_get_app_sdk_platform(app_manager_get_current_app_md()); } diff --git a/src/fw/process_management/app_manager.h b/src/fw/process_management/app_manager.h index 1c259c0c6b..c57387eecb 100644 --- a/src/fw/process_management/app_manager.h +++ b/src/fw/process_management/app_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -22,7 +9,7 @@ #include "kernel/events.h" #include "kernel/pebble_tasks.h" #include "resource/resource.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" #include diff --git a/src/fw/process_management/app_menu_data_source.c b/src/fw/process_management/app_menu_data_source.c index b187522aef..fdfd89291e 100644 --- a/src/fw/process_management/app_menu_data_source.c +++ b/src/fw/process_management/app_menu_data_source.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_menu_data_source.h" @@ -22,12 +9,12 @@ #include "kernel/pbl_malloc.h" #include "process_management/app_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/process_management/app_order_storage.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/process_management/app_order_storage.h" #include "system/passert.h" #include "util/size.h" -#include "apps/system_apps/activity_demo_app.h" +#include "apps/demo/activity_demo/activity_demo.h" #include "shell/prefs.h" #include diff --git a/src/fw/process_management/app_menu_data_source.h b/src/fw/process_management/app_menu_data_source.h index e0944e4314..6dff948437 100644 --- a/src/fw/process_management/app_menu_data_source.h +++ b/src/fw/process_management/app_menu_data_source.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -28,7 +15,7 @@ #include "process_management/app_install_manager.h" #include "process_management/pebble_process_info.h" #include "process_management/pebble_process_md.h" -#include "services/normal/process_management/app_order_storage.h" +#include "pbl/services/process_management/app_order_storage.h" #include "util/attributes.h" #include "util/list.h" diff --git a/src/fw/process_management/app_run_state.c b/src/fw/process_management/app_run_state.c index 199387b95a..4c03ee5869 100644 --- a/src/fw/process_management/app_run_state.c +++ b/src/fw/process_management/app_run_state.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_run_state.h" #include "launcher_app_message.h" @@ -22,7 +9,7 @@ #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" #include "process_management/process_manager.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "util/attributes.h" @@ -46,10 +33,10 @@ static void prv_send_response(void *data) { COMM_SESSION_DEFAULT_TIMEOUT); uuid_to_string(&app_run_state->uuid, uuid_buffer); - PBL_LOG(LOG_LEVEL_DEBUG, "AppRunState(0x34) %s sending status: %s - %u", + PBL_LOG_DBG("AppRunState(0x34) %s sending status: %s - %u", (success ? "success" : "failed"), uuid_buffer, app_run_state->state); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Using deprecated launcher_app_message"); + PBL_LOG_DBG("Using deprecated launcher_app_message"); const bool is_running = ((app_run_state->state == RUNNING) ? true : false); launcher_app_message_send_app_state_deprecated(&app_run_state->uuid, is_running); } @@ -67,7 +54,7 @@ void app_run_state_command(CommSession *session, AppRunStateCommand cmd, const U if (install_id == INSTALL_ID_INVALID && cmd != APP_RUN_STATE_STATUS_COMMAND) { char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(uuid, uuid_buffer); - PBL_LOG(LOG_LEVEL_DEBUG, "No app found with uuid %s", uuid_buffer); + PBL_LOG_DBG("No app found with uuid %s", uuid_buffer); return; } @@ -102,7 +89,7 @@ void app_run_state_command(CommSession *session, AppRunStateCommand cmd, const U } break; default: - PBL_LOG(LOG_LEVEL_ERROR, "Unknown command: %d", cmd); + PBL_LOG_ERR("Unknown command: %d", cmd); } } @@ -116,7 +103,7 @@ void app_run_state_protocol_msg_callback(CommSession *session, const uint8_t *da if (msg->command != APP_RUN_STATE_STATUS_COMMAND) { if (length < sizeof(AppStateMessage)) { - PBL_LOG(LOG_LEVEL_ERROR, "length mismatch, expected %"PRIu32" byte(s), got %"PRIu32" bytes", + PBL_LOG_ERR("length mismatch, expected %"PRIu32" byte(s), got %"PRIu32" bytes", (uint32_t) sizeof(AppStateMessage), (uint32_t) length); return; } @@ -125,7 +112,7 @@ void app_run_state_protocol_msg_callback(CommSession *session, const uint8_t *da } void app_run_state_send_update(const Uuid *uuid, AppState app_state) { -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW // FIXME: Need to actually factor out this so it's totally removed from PRF, but for now just // pull it all. We can't use this here because we don't initialize app message at all. return; diff --git a/src/fw/process_management/app_run_state.h b/src/fw/process_management/app_run_state.h index 5128e2a8b1..21bb8df841 100644 --- a/src/fw/process_management/app_run_state.h +++ b/src/fw/process_management/app_run_state.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" #include "util/uuid.h" typedef enum { diff --git a/src/fw/process_management/launch_config.h b/src/fw/process_management/launch_config.h index 43d959d502..b1dd8f690d 100644 --- a/src/fw/process_management/launch_config.h +++ b/src/fw/process_management/launch_config.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/app_launch_button.h" #include "applib/app_launch_reason.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" typedef struct LaunchConfigCommon { AppLaunchReason reason; diff --git a/src/fw/process_management/launcher_app_message.c b/src/fw/process_management/launcher_app_message.c index 7e99b3bc62..0353e982a6 100644 --- a/src/fw/process_management/launcher_app_message.c +++ b/src/fw/process_management/launcher_app_message.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "launcher_app_message.h" @@ -75,7 +62,7 @@ void launcher_app_message_send_app_state_deprecated(const Uuid *uuid, bool runni static bool prv_has_invalid_length(size_t expected, size_t actual) { if (actual < expected) { - PBL_LOG(LOG_LEVEL_ERROR, "Too short"); + PBL_LOG_ERR("Too short"); return true; } return false; diff --git a/src/fw/process_management/launcher_app_message.h b/src/fw/process_management/launcher_app_message.h index 08741d8d8c..72c16b329f 100644 --- a/src/fw/process_management/launcher_app_message.h +++ b/src/fw/process_management/launcher_app_message.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_management/pebble_process_info.c b/src/fw/process_management/pebble_process_info.c index d1bcaf6212..2a12c4b526 100644 --- a/src/fw/process_management/pebble_process_info.c +++ b/src/fw/process_management/pebble_process_info.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pebble_process_info.h" diff --git a/src/fw/process_management/pebble_process_info.h b/src/fw/process_management/pebble_process_info.h index 18f586afac..5637d41ef6 100644 --- a/src/fw/process_management/pebble_process_info.h +++ b/src/fw/process_management/pebble_process_info.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -42,11 +29,14 @@ typedef enum { PROCESS_INFO_ALLOW_JS = 1 << 3, //! Use to indicate the process should have a worker.bin installed as well. PROCESS_INFO_HAS_WORKER = 1 << 4, - //! True, if process uses RockyJS APIs + //! Deprecated: was RockyJS, kept for ABI compatibility (bit 5) PROCESS_INFO_ROCKY_APP = 1 << 5, //! Bitmask, to store compile time platform PROCESS_INFO_PLATFORM_MASK = 0xf << 6, + + //! True, if process uses Moddable XS APIs + PROCESS_INFO_MODDABLE_APP = 1 << 10, //! SDK older than 4.2 doesn't store any value PROCESS_INFO_PLATFORM_UNKNOWN = 0x0 << 6, PROCESS_INFO_PLATFORM_APLITE = 0x1 << 6, @@ -55,6 +45,9 @@ typedef enum { PROCESS_INFO_PLATFORM_CHALK = 0x3 << 6, PROCESS_INFO_PLATFORM_DIORITE = 0x4 << 6, PROCESS_INFO_PLATFORM_EMERY = 0x5 << 6, + //! Values that are actually added by SDK 4.9+ + PROCESS_INFO_PLATFORM_FLINT = 0x6 << 6, + PROCESS_INFO_PLATFORM_GABBRO = 0x7 << 6, } PebbleProcessInfoFlags; //! @} // group App @@ -159,9 +152,23 @@ typedef enum { // sdk.major:0x5 .minor:0x54 -- Add PlatformType enum and defines (rev 87) // sdk.major:0x5 .minor:0x55 -- Preferred Content Size (rev 88) // sdk.major:0x5 .minor:0x56 -- Add PlatformType enum and defines (rev 89) +// sdk.major:0x5 .minor:0x57 -- Add moddable_createMachine (rev 90) +// sdk.major:0x5 .minor:0x58 -- Add size 60 LECO font (rev 91) +// sdk.major:0x5 .minor:0x59 -- Add flags to ModdableCreationRecord (rev 92) +// sdk.major:0x5 .minor:0x5a -- Add rot_bitmap_layer_get_layer() and AppGlanceSliceLayout (rev 93) +// sdk.major:0x5 .minor:0x5b -- Add app_light_set_color() and app_light_set_system_color() (rev 94) +// sdk.major:0x5 .minor:0x5c -- Add light_is_on() and expose touch_service_subscribe/unsubscribe to apps (rev 95) +// sdk.major:0x5 .minor:0x5d -- Add app_light_set_color_rgb888() for 8-bit-per-channel backlight tint (rev 96) +// sdk.major:0x5 .minor:0x5e -- Add Speaker API (rev 97) +// sdk.major:0x5 .minor:0x5f -- speaker_play_tone() now plays the exact frequency (rev 98) +// sdk.major:0x5 .minor:0x60 -- Add persist_get_max_size() for runtime persist storage capacity (rev 99) +// sdk.major:0x5 .minor:0x61 -- Add speaker_is_muted() for system-wide speaker mute query (rev 100) +// sdk.major:0x5 .minor:0x62 -- Add backlight_service_subscribe/unsubscribe for backlight on/off events (rev 101) +// sdk.major:0x5 .minor:0x63 -- Export launch_button() (rev 102) +// sdk.major:0x5 .minor:0x64 -- Add kModdableCreationFlagDebug (rev 103) #define PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR 0x5 -#define PROCESS_INFO_CURRENT_SDK_VERSION_MINOR 0x56 +#define PROCESS_INFO_CURRENT_SDK_VERSION_MINOR 0x64 // The first SDK to ship with 2.x APIs #define PROCESS_INFO_FIRST_2X_SDK_VERSION_MAJOR 0x4 @@ -202,7 +209,7 @@ typedef struct __attribute__((__packed__)) { char header[8]; //!< Sentinal value, should always be 'PBLAPP' Version struct_version; //!< version of this structure's format Version sdk_version; //!< version of the SDK used to build this process - Version process_version; //!< version of the process + Version process_version; //!< version of the process. Note this omits any semver "patch" version. uint16_t load_size; //!< size of the binary in flash, including this metadata but not the reloc table uint32_t offset; //!< The entry point of this executable uint32_t crc; //!< CRC of the data only, ie, not including this struct or the reloc table at the end diff --git a/src/fw/process_management/pebble_process_md.c b/src/fw/process_management/pebble_process_md.c index dc51537ea1..f646113d80 100644 --- a/src/fw/process_management/pebble_process_md.c +++ b/src/fw/process_management/pebble_process_md.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "pebble_process_md.h" @@ -108,6 +95,7 @@ static void prv_init_from_info_common(PebbleProcessMd *common, const PebbleProce common->visibility = process_metadata_flags_visibility(info->flags); common->allow_js = process_metadata_flags_allow_js(info->flags); common->has_worker = process_metadata_flags_has_worker(info->flags); + common->is_moddable_app = process_metadata_flags_moddable_app(info->flags); common->is_rocky_app = process_metadata_flags_rocky_app(info->flags); common->stored_sdk_platform = process_metadata_flags_stored_sdk_platform(info->flags); common->is_unprivileged = true; @@ -201,6 +189,10 @@ bool process_metadata_flags_has_worker(PebbleProcessInfoFlags flags) { return ((flags & PROCESS_INFO_HAS_WORKER) != 0); } +bool process_metadata_flags_moddable_app(PebbleProcessInfoFlags flags) { + return ((flags & PROCESS_INFO_MODDABLE_APP) != 0); +} + bool process_metadata_flags_rocky_app(PebbleProcessInfoFlags flags) { return ((flags & PROCESS_INFO_ROCKY_APP) != 0); } @@ -242,7 +234,9 @@ PlatformType process_metadata_get_app_sdk_platform(const PebbleProcessMd *md) { /* basalt */ PlatformTypeBasalt, /* chalk */ PlatformTypeChalk, /* diorite */ PlatformTypeAplite, // there's was no Diorite SDK prior to 4.0 - /* emery */ PlatformTypeBasalt); + /* emery */ PlatformTypeBasalt, + /* flint */ PlatformTypeAplite, + /* gabbro */ PlatformTypeChalk); } // 4.0 <= SDK < 4.2 if (version_compare(app_sdk_version, first_4_2_version) < 0) { @@ -251,7 +245,9 @@ PlatformType process_metadata_get_app_sdk_platform(const PebbleProcessMd *md) { /* basalt */ PlatformTypeBasalt, /* chalk */ PlatformTypeChalk, /* diorite */ PlatformTypeDiorite, // there's was no Aplite SDK after 4.0 - /* emery */ PlatformTypeBasalt); + /* emery */ PlatformTypeBasalt, + /* flint */ PlatformTypeDiorite, + /* gabbro */ PlatformTypeChalk); } // 4.2 <= SDK --> the flags should be filled correctly. @@ -267,6 +263,10 @@ PlatformType process_metadata_get_app_sdk_platform(const PebbleProcessMd *md) { return PlatformTypeDiorite; case PROCESS_INFO_PLATFORM_EMERY: return PlatformTypeEmery; + case PROCESS_INFO_PLATFORM_FLINT: + return PlatformTypeFlint; + case PROCESS_INFO_PLATFORM_GABBRO: + return PlatformTypeGabbro; default: { // If we encounter an unknown platform, we assume that it's meant for the current platform // (as it's most-likely a system-app). This is not a security risk as developers could always diff --git a/src/fw/process_management/pebble_process_md.h b/src/fw/process_management/pebble_process_md.h index a6b094d1b8..70fc7d6b1f 100644 --- a/src/fw/process_management/pebble_process_md.h +++ b/src/fw/process_management/pebble_process_md.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -90,7 +77,10 @@ typedef struct PebbleProcessMd { //! This process has a sister worker process in flash. bool has_worker; - //! Process is allowed to call RockyJS APIs + //! Process is allowed to call Moddable APIs + bool is_moddable_app; + + //! Deprecated: Process was built as a RockyJS app (no longer supported) bool is_rocky_app; //! Bits of the sdk_platform as they were stored in the binary, or 0 if undefined @@ -184,6 +174,8 @@ bool process_metadata_flags_allow_js(PebbleProcessInfoFlags flags); bool process_metadata_flags_has_worker(PebbleProcessInfoFlags flags); +bool process_metadata_flags_moddable_app(PebbleProcessInfoFlags flags); + bool process_metadata_flags_rocky_app(PebbleProcessInfoFlags flags); uint16_t process_metadata_flags_stored_sdk_platform(PebbleProcessInfoFlags flags); diff --git a/src/fw/process_management/process_heap.c b/src/fw/process_management/process_heap.c index c51289e662..1d028c644b 100644 --- a/src/fw/process_management/process_heap.c +++ b/src/fw/process_management/process_heap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_heap.h" diff --git a/src/fw/process_management/process_heap.h b/src/fw/process_management/process_heap.h index f5d4a32c99..013e6e6d94 100644 --- a/src/fw/process_management/process_heap.h +++ b/src/fw/process_management/process_heap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_management/process_loader.h b/src/fw/process_management/process_loader.h index 4734dd124f..9b48d4ea3c 100644 --- a/src/fw/process_management/process_loader.h +++ b/src/fw/process_management/process_loader.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_management/process_manager.c b/src/fw/process_management/process_manager.c index ee36188795..a2eb1c4de5 100644 --- a/src/fw/process_management/process_manager.c +++ b/src/fw/process_management/process_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_manager.h" #include "app_install_manager.h" @@ -22,7 +9,6 @@ #include "applib/app_logging.h" #include "applib/accel_service_private.h" #include "applib/platform.h" -#include "applib/rockyjs/rocky_res.h" #include "applib/ui/dialogs/dialog.h" #include "applib/ui/dialogs/expandable_dialog.h" @@ -33,18 +19,17 @@ #include "kernel/pebble_tasks.h" #include "os/tick.h" #include "resource/resource_ids.auto.h" -#include "services/common/animation_service.h" -#include "services/common/analytics/analytics.h" -#include "services/common/evented_timer.h" -#include "services/common/event_service.h" -#include "services/common/hrm/hrm_manager.h" -#include "services/normal/filesystem/pfs.h" -#include "services/common/system_task.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "services/normal/app_cache.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/persist.h" -#include "services/normal/voice/voice.h" +#include "pbl/services/animation_service.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/event_service.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/system_task.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/persist.h" +#include "pbl/services/voice/voice.h" #include "shell/normal/watchface.h" #include "syscall/syscall.h" @@ -57,7 +42,7 @@ #include "syscall/syscall_internal.h" -#include "apps/system_apps/app_fetch_ui.h" +#include "apps/system/app_fetch_ui.h" #include "FreeRTOS.h" #include "task.h" @@ -88,7 +73,7 @@ static ProcessContext *prv_get_context(void) { // This timer callback gets called if the process doesn't finish it's deinit within the required timeout (currently // 3 seconds). static void prv_graceful_close_timer_callback(void* data) { - PBL_LOG(LOG_LEVEL_DEBUG, "deinit timeout expired, killing app forcefully"); + PBL_LOG_DBG("deinit timeout expired, killing app forcefully"); PebbleTask task = (PebbleTask)data; process_manager_put_kill_process_event(task, false /*gracefully*/); @@ -129,7 +114,7 @@ EXTERNALLY_VISIBLE void process_manager_handle_syscall_exit(void) { ProcessContext *context = prv_get_context_for_task(task); if (context->closing_state == ProcessRunState_ForceClosing) { - PBL_LOG(LOG_LEVEL_DEBUG, "Hit syscall exit trap!"); + PBL_LOG_DBG("Hit syscall exit trap!"); context->safe_to_kill = true; process_manager_put_kill_process_event(task, false); @@ -200,7 +185,7 @@ void process_manager_init_context(ProcessContext* context, context->exit_reason = APP_EXIT_NOT_SPECIFIED; } -#if !defined(RECOVERY_FW) +#if !defined(CONFIG_RECOVERY_FW) bool process_manager_check_SDK_compatible(const AppInstallId id) { AppInstallEntry entry; if (!app_install_get_entry_for_install_id(id, &entry)) { @@ -211,7 +196,7 @@ bool process_manager_check_SDK_compatible(const AppInstallId id) { return true; } - PBL_LOG(LOG_LEVEL_WARNING, "App requires support for SDK version (%"PRIu8".%"PRIu8"), " + PBL_LOG_WRN("App requires support for SDK version (%"PRIu8".%"PRIu8"), " "we only support version (%"PRIu8".%"PRIu8").", entry.sdk_version.major, entry.sdk_version.minor, (uint8_t) PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, @@ -238,18 +223,12 @@ static bool prv_needs_fetch(AppInstallId id, const PebbleProcessMd **md, bool is PBL_ASSERTN(md); if (!app_cache_entry_exists(id)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Cache entry did not exist on launch attempt"); + PBL_LOG_DBG("Cache entry did not exist on launch attempt"); return true; } *md = app_install_get_md(id, is_worker); - if (!is_worker && rocky_app_validate_resources(*md) == RockyResourceValidation_Invalid) { - PBL_LOG(LOG_LEVEL_DEBUG, "App has incompatible JavaScript bytecode"); - // TODO: do we need to purge the app cache here? - return true; - } - return false; } @@ -261,12 +240,12 @@ void process_manager_launch_process(const ProcessLaunchConfig *config) { const bool is_worker = config->worker; if (id == INSTALL_ID_INVALID) { - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid ID"); + PBL_LOG_DBG("Invalid ID"); return; } const PebbleProcessMd *md = NULL; -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) if (app_install_id_from_app_db(id)) { if (!process_manager_check_SDK_compatible(id)) { return; @@ -274,7 +253,7 @@ void process_manager_launch_process(const ProcessLaunchConfig *config) { // This is a third party flash 3.0 app install if (prv_needs_fetch(id, &md, is_worker)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Cache entry did not exist on launch attempt"); + PBL_LOG_DBG("Cache entry did not exist on launch attempt"); // Freed in app_fetch_ui.c AppFetchUIArgs *fetch_args = kernel_malloc_check(sizeof(AppFetchUIArgs)); @@ -312,19 +291,19 @@ void process_manager_launch_process(const ProcessLaunchConfig *config) { } if (!md) { - PBL_LOG(LOG_LEVEL_ERROR, "Tried to launch non-existant app!"); + PBL_LOG_ERR("Tried to launch non-existant app!"); return; } -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) if (!is_worker) { // Check if the app ram size is valid in order to determine if its SDK version is supported. if (!app_manager_is_app_supported(md)) { - PBL_LOG(LOG_LEVEL_WARNING, "Tried to launch an app with an unsupported SDK version."); + PBL_LOG_WRN("Tried to launch an app with an unsupported SDK version."); AppInstallEntry entry; if (!app_install_get_entry_for_install_id(id, &entry)) { // can't retrieve app install entry for id - PBL_LOG(LOG_LEVEL_ERROR, "Failed to get entry for id %"PRId32, id); + PBL_LOG_ERR("Failed to get entry for id %"PRId32, id); } else if (app_install_entry_is_watchface(&entry)) { // If the watchface is for an unsupported SDK version, we need to switch the default // watchface back to tictoc. Otherwise, we will be stuck in the launcher forever. @@ -337,6 +316,32 @@ void process_manager_launch_process(const ProcessLaunchConfig *config) { return; } + + // RockyJS is no longer supported. Show a dialog to the user and, if it's a watchface, + // revert to the default watchface so the user isn't stuck in the launcher. + if (md->is_rocky_app) { + PBL_LOG_WRN("Tried to launch an unsupported RockyJS app."); + AppInstallEntry entry; + if (app_install_get_entry_for_install_id(id, &entry) && + app_install_entry_is_watchface(&entry)) { + watchface_set_default_install_id(INSTALL_ID_INVALID); + watchface_launch_default(NULL); + } + + ExpandableDialog *expandable_dialog = expandable_dialog_create("Unsupported App"); + Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog); + const char *error_text = + i18n_noop("This app uses RockyJS which is no longer supported."); + dialog_set_text(dialog, i18n_get(error_text, expandable_dialog)); + dialog_set_icon(dialog, RESOURCE_ID_GENERIC_WARNING_SMALL); + i18n_free(error_text, expandable_dialog); + + WindowStack *window_stack = modal_manager_get_window_stack(ModalPriorityAlert); + expandable_dialog_push(expandable_dialog, window_stack); + + app_install_release_md(md); + return; + } } #endif @@ -351,33 +356,6 @@ void process_manager_launch_process(const ProcessLaunchConfig *config) { } } -// --------------------------------------------------------------------------------------------- -extern void analytics_external_collect_app_cpu_stats(void); -extern void analytics_external_collect_app_flash_read_stats(void); -static void prv_handle_app_stop_analytics(const ProcessContext *const context, - PebbleTask task, bool gracefully) { - if (!gracefully) { - if (task == PebbleTask_App) { - if (context->app_md->is_rocky_app) { - analytics_inc(ANALYTICS_APP_METRIC_ROCKY_CRASHED_COUNT, AnalyticsClient_App); - } - analytics_inc(ANALYTICS_APP_METRIC_CRASHED_COUNT, AnalyticsClient_App); - } else if (task == PebbleTask_Worker) { - analytics_inc(ANALYTICS_APP_METRIC_BG_CRASHED_COUNT, AnalyticsClient_Worker); - } - if (context->app_md->is_rocky_app) { - analytics_inc(ANALYTICS_DEVICE_METRIC_APP_ROCKY_CRASHED_COUNT, AnalyticsClient_System); - } - analytics_inc(ANALYTICS_DEVICE_METRIC_APP_CRASHED_COUNT, AnalyticsClient_System); - } - if (task == PebbleTask_App) { - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_FRONT_MOST_TIME); - } - analytics_external_collect_app_cpu_stats(); - analytics_external_collect_app_flash_read_stats(); -} - - // --------------------------------------------------------------------------------------------- //! This method returns true if the process is safe to kill (it has exited out of it's main function). If the //! the process is not already safe to kill, it will "prod" it to exit, set a timer, and return false. @@ -405,18 +383,24 @@ bool process_manager_make_process_safe_to_kill(PebbleTask task, bool gracefully) // If already safe to kill, we're done if (context->safe_to_kill) { - prv_handle_app_stop_analytics(context, task, gracefully); +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_SHELL_SDK) + // Stop per-watchface time tracking if this was a watchface + if (context->app_md->process_type == ProcessTypeWatchface) { + PBL_ANALYTICS_TIMER_STOP(watchface_time_ms); + } +#endif + return true; } - PBL_LOG(LOG_LEVEL_DEBUG, "make %s process safe to kill: state %u", pebble_task_get_name(task), + PBL_LOG_DBG("make %s process safe to kill: state %u", pebble_task_get_name(task), context->closing_state); if (gracefully) { if (context->closing_state == ProcessRunState_Running) { context->closing_state = ProcessRunState_GracefullyClosing; - PBL_LOG(LOG_LEVEL_DEBUG, "Attempting to gracefully deinit %s", pebble_task_get_name(task)); + PBL_LOG_DBG("Attempting to gracefully deinit %s", pebble_task_get_name(task)); // Send deinit event to app: PebbleEvent deinit_event = { @@ -434,11 +418,18 @@ bool process_manager_make_process_safe_to_kill(PebbleTask task, bool gracefully) // Else we're already in the gracefully closing state, just let the timer run out or the // app to mark itself as safe_to_kill. } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Check if we can force stop the %s task", pebble_task_get_name(task)); + PBL_LOG_DBG("Check if we can force stop the %s task", pebble_task_get_name(task)); if (prv_force_stop_task_if_unprivileged(context)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Got it"); - prv_handle_app_stop_analytics(context, task, gracefully); + PBL_LOG_DBG("Got it"); + +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_SHELL_SDK) + // Stop per-watchface time tracking if this was a watchface + if (context->app_md->process_type == ProcessTypeWatchface) { + PBL_ANALYTICS_TIMER_STOP(watchface_time_ms); + } +#endif + return true; } @@ -452,7 +443,18 @@ bool process_manager_make_process_safe_to_kill(PebbleTask task, bool gracefully) // the process safe to kill if its state is set to ForceClosing. // All we have to do is set the state and wait. context->closing_state = ProcessRunState_ForceClosing; - PBL_LOG(LOG_LEVEL_DEBUG, "task is privileged, setting the syscall exit trap"); + PBL_LOG_DBG("task is privileged, setting the syscall exit trap"); + + // Send a DEINIT event to wake up the app if it's blocked waiting for events + // (e.g., in sys_get_pebble_event). This allows the syscall to return and + // trigger process_manager_handle_syscall_exit() which will mark the process + // as safe to kill. + PBL_LOG_DBG("Sending DEINIT event to wake %s from syscall", + pebble_task_get_name(task)); + PebbleEvent deinit_event = { + .type = PEBBLE_PROCESS_DEINIT_EVENT, + }; + process_manager_send_event_to_process(task, &deinit_event); bool success = new_timer_start(s_deinit_timer_id, 3 * 1000, prv_force_close_timer_callback, (void*)task, 0 /*flags*/); @@ -528,7 +530,7 @@ void process_manager_process_cleanup(PebbleTask task) { ProcessContext *context = prv_get_context_for_task(task); PBL_ASSERTN(context->safe_to_kill); - PBL_LOG(LOG_LEVEL_DEBUG, "%s is getting cleaned up", pebble_task_get_name(task)); + PBL_LOG_DBG("%s is getting cleaned up", pebble_task_get_name(task)); // Shutdown services that may be running. Do this before we destory the task and clear the queue // just in case other services are still in flight. @@ -539,22 +541,19 @@ void process_manager_process_cleanup(PebbleTask task) { evented_timer_clear_process_timers(task); event_service_clear_process_subscriptions(task); -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM hrm_manager_process_cleanup(task, context->install_id); #endif -#ifndef RECOVERY_FW -#if CAPABILITY_HAS_MICROPHONE +#ifndef CONFIG_RECOVERY_FW +#ifdef CONFIG_MIC voice_kill_app_session(task); #endif dls_inactivate_sessions(task); if (task == PebbleTask_App) { -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - smartstrap_attribute_unregister_all(); -#endif } -#endif // RECOVERY_FW +#endif // CONFIG_RECOVERY_FW // Unregister the task pebble_task_unregister(task); @@ -575,7 +574,7 @@ void process_manager_process_cleanup(PebbleTask task) { if (context->to_process_event_queue && pdFAIL == event_queue_cleanup_and_reset(context->to_process_event_queue)) { - PBL_LOG(LOG_LEVEL_ERROR, "The to processs queue could not be reset!"); + PBL_LOG_ERR("The to processs queue could not be reset!"); } context->to_process_event_queue = NULL; } @@ -602,13 +601,13 @@ bool process_manager_send_event_to_process(PebbleTask task, PebbleEvent* e) { ProcessContext *context = prv_get_context_for_task(task); if (context->to_process_event_queue == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Dropped app event! Type: %u", e->type); + PBL_LOG_WRN("Dropped app event! Type: %u", e->type); return false; } // Put on app's own queue: if (!xQueueSend(context->to_process_event_queue, e, milliseconds_to_ticks(1000))) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to send event %u to app! Closing it!", e->type); + PBL_LOG_ERR("Failed to send event %u to app! Closing it!", e->type); // We could be called from a timer task callback, so post a kill event rather than call // process_manager_close_process directly. process_manager_put_kill_process_event(task, false); @@ -624,7 +623,7 @@ uint32_t process_manager_process_events_waiting(PebbleTask task) { ProcessContext *context = prv_get_context_for_task(task); if (context->to_process_event_queue == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "no event queue"); + PBL_LOG_WRN("no event queue"); return 0; } @@ -712,6 +711,14 @@ DEFINE_SYSCALL(uint32_t, sys_process_get_launch_args, void) { } } +// ------------------------------------------------------------------------------------------- +DEFINE_SYSCALL(AppQuickLaunchAction, sys_process_get_quick_launch_action, void) { + if (sys_process_get_launch_reason() != APP_LAUNCH_QUICK_LAUNCH) { + return APP_QUICK_LAUNCH_ACTION_NONE; + } + return (AppQuickLaunchAction)(uintptr_t) process_manager_get_current_process_args(); +} + // ------------------------------------------------------------------------------------------- DEFINE_SYSCALL(AppExitReason, sys_process_get_exit_reason, void) { return prv_get_context()->exit_reason; diff --git a/src/fw/process_management/process_manager.h b/src/fw/process_management/process_manager.h index 92eb4a83b5..a340fdc71a 100644 --- a/src/fw/process_management/process_manager.h +++ b/src/fw/process_management/process_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,8 +10,8 @@ #include "kernel/events.h" #include "process_management/app_install_types.h" #include "process_management/pebble_process_md.h" -#include "services/common/accel_manager.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/accel_manager.h" +#include "pbl/services/compositor/compositor.h" #include diff --git a/src/fw/process_management/sdk_memory_limits.template.h b/src/fw/process_management/sdk_memory_limits.template.h index 4324a68b27..ba1000a9d7 100644 --- a/src/fw/process_management/sdk_memory_limits.template.h +++ b/src/fw/process_management/sdk_memory_limits.template.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Memory limits for SDK applications //! diff --git a/src/fw/process_management/sdk_shims.c b/src/fw/process_management/sdk_shims.c index fcd17d1e20..eea1690a1b 100644 --- a/src/fw/process_management/sdk_shims.c +++ b/src/fw/process_management/sdk_shims.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_state/app_state/app_state.h" diff --git a/src/fw/process_management/sdk_shims.h b/src/fw/process_management/sdk_shims.h index 698bc9db86..b4dfc6c4af 100644 --- a/src/fw/process_management/sdk_shims.h +++ b/src/fw/process_management/sdk_shims.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ struct GContext; typedef struct GContext GContext; diff --git a/src/fw/process_management/sdk_version.c b/src/fw/process_management/sdk_version.c index 18988a3048..840cdd32ec 100644 --- a/src/fw/process_management/sdk_version.c +++ b/src/fw/process_management/sdk_version.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "sdk_version.h" diff --git a/src/fw/process_management/sdk_version.h b/src/fw/process_management/sdk_version.h index 824c2420e8..aa6dc6347e 100644 --- a/src/fw/process_management/sdk_version.h +++ b/src/fw/process_management/sdk_version.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "pebble_process_info.h" diff --git a/src/fw/process_management/worker_manager.c b/src/fw/process_management/worker_manager.c index f4db86f621..295a914828 100644 --- a/src/fw/process_management/worker_manager.c +++ b/src/fw/process_management/worker_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_manager.h" #include "worker_manager.h" @@ -132,13 +119,13 @@ bool worker_manager_launch_new_worker_with_args(const PebbleProcessMd *app_md, c PBL_ASSERT_TASK(PebbleTask_KernelMain); // Don't launch workers in recovery mode to reduce the chance of crashes -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW return false; #endif // If workers are disabled, don't launch if (!s_workers_enabled) { - PBL_LOG(LOG_LEVEL_WARNING, "Workers disabled"); + PBL_LOG_WRN("Workers disabled"); return false; } @@ -161,7 +148,7 @@ bool worker_manager_launch_new_worker_with_args(const PebbleProcessMd *app_md, c // Error if a worker already launched if (pebble_task_get_handle_for_task(PebbleTask_Worker) != NULL) { - PBL_LOG(LOG_LEVEL_WARNING, "Worker already launched"); + PBL_LOG_WRN("Worker already launched"); return false; } @@ -199,21 +186,21 @@ bool worker_manager_launch_new_worker_with_args(const PebbleProcessMd *app_md, c &worker_segment); s_worker_task_context.load_end = worker_segment.start; if (!entry_point) { - PBL_LOG(LOG_LEVEL_WARNING, "Tried to launch an invalid worker in bank %u!", + PBL_LOG_WRN("Tried to launch an invalid worker in bank %u!", process_metadata_get_code_bank_num(app_md)); return false; } // The rest of worker_ram is available for worker state to use as it sees fit. if (!worker_state_configure(&worker_ram)) { - PBL_LOG(LOG_LEVEL_ERROR, "Worker state configuration failed"); + PBL_LOG_ERR("Worker state configuration failed"); return false; } // The remaining space in worker_segment is assigned to the worker's // heap. Worker state needs to be configured before initializing the // heap as the WorkerState struct holds the worker heap's Heap object. Heap *worker_heap = worker_state_get_heap(); - PBL_LOG(LOG_LEVEL_DEBUG, "Worker heap init %p %p", + PBL_LOG_DBG("Worker heap init %p %p", worker_segment.start, worker_segment.end); heap_init(worker_heap, worker_segment.start, worker_segment.end, /* enable_heap_fuzzing */ false); @@ -237,7 +224,7 @@ bool worker_manager_launch_new_worker_with_args(const PebbleProcessMd *app_md, c .puxStackBuffer = stack, }; - PBL_LOG(LOG_LEVEL_DEBUG, "Starting %s", task_name); + PBL_LOG_DBG("Starting %s", task_name); pebble_task_create(PebbleTask_Worker, &task_params, &s_worker_task_context.task_handle); @@ -295,7 +282,7 @@ void worker_manager_close_current_worker(bool gracefully) { // to post another KILL event in a few seconds, thus giving the process a chance to clean up. if (!process_manager_make_process_safe_to_kill(PebbleTask_Worker, gracefully)) { // Maybe next time... - PBL_LOG(LOG_LEVEL_DEBUG, "Worker not ready to exit"); + PBL_LOG_DBG("Worker not ready to exit"); return; } diff --git a/src/fw/process_management/worker_manager.h b/src/fw/process_management/worker_manager.h index 7b5a05b8c5..fcce0055e8 100644 --- a/src/fw/process_management/worker_manager.h +++ b/src/fw/process_management/worker_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/process_state/app_state/app_state.c b/src/fw/process_state/app_state/app_state.c index 9751e67583..176066d651 100644 --- a/src/fw/process_state/app_state/app_state.c +++ b/src/fw/process_state/app_state/app_state.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_state/app_state/app_state.h" @@ -29,8 +16,8 @@ #include "kernel/util/segment.h" #include "process_management/process_loader.h" #include "process_management/process_manager.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/persist.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/persist.h" #include "syscall/syscall_internal.h" #include "system/passert.h" #include "util/attributes.h" @@ -70,9 +57,7 @@ typedef struct { EventServiceInfo event_service_state; -#if !defined(PLATFORM_TINTIN) BLEAppState ble_app_state; -#endif AccelServiceState accel_state; @@ -86,20 +71,18 @@ typedef struct { BatteryStateServiceState battery_state_service_state; + BacklightServiceState backlight_service_state; + TickTimerServiceState tick_timer_service_state; + TouchServiceState touch_service_state; + ConnectionServiceState connection_service_state; -#if CAPABILITY_HAS_HEALTH_TRACKING HealthServiceState health_service_state; -#endif LocaleInfo locale_info; -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - SmartstrapConnectionState smartstrap_state; -#endif - ContentIndicatorsBuffer content_indicators_buffer; bool app_framebuffer_render_pending; @@ -114,20 +97,20 @@ typedef struct { EventServiceInfo wakeup_event_info; -#if CAPABILITY_HAS_TOUCHSCREEN + SpeakerFinishedCallback speaker_finish_handler; + void *speaker_finish_ctx; + EventServiceInfo speaker_finish_event_info; + +#ifdef CONFIG_TOUCH RecognizerList recognizer_list; #endif - //! Potentially un-aligned w.r.t. JerryScript's requirements: - uint8_t *rocky_runtime_context_buffer; - //! Aligned w.r.t. JerryScript's requirements, pointing into rocky_runtime_context_buffer. - RockyRuntimeContext *rocky_runtime_context; + uint8_t *js_runtime_context_buffer; + JsRuntimeContext *js_runtime_context; - RockyMemoryAPIContext *rocky_memory_api_context; + JsMemoryAPIContext *js_memory_api_context; -#if CAPABILITY_HAS_APP_GLANCES AppGlance glance; -#endif TextRenderState text_render_state; @@ -211,9 +194,7 @@ NOINLINE void app_state_init(void) { &s_app_state_ptr->framebuffer, init_mode); -#if !defined(PLATFORM_TINTIN) ble_init_app_state(); -#endif accel_service_state_init(app_state_get_accel_state()); @@ -221,13 +202,15 @@ NOINLINE void app_state_init(void) { battery_state_service_state_init(app_state_get_battery_state_service_state()); + backlight_service_state_init(app_state_get_backlight_service_state()); + connection_service_state_init(app_state_get_connection_service_state()); tick_timer_service_state_init(app_state_get_tick_timer_service_state()); -#if CAPABILITY_HAS_HEALTH_TRACKING + touch_service_state_init(app_state_get_touch_service_state()); + health_service_state_init(app_state_get_health_service_state()); -#endif locale_init_app_locale(app_state_get_locale_info()); @@ -236,7 +219,7 @@ NOINLINE void app_state_init(void) { unobstructed_area_service_init(app_state_get_unobstructed_area_state(), s_app_state_ptr->initial_obstruction_origin_y); -#if CAPABILITY_HAS_APP_GLANCES && !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) app_glance_service_init_glance(app_state_get_glance()); #endif @@ -245,12 +228,7 @@ NOINLINE void app_state_init(void) { NOINLINE void app_state_deinit(void) { animation_private_state_deinit(&s_app_state_ptr->animation_state); -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR - app_smartstrap_cleanup(); -#endif -#if CAPABILITY_HAS_HEALTH_TRACKING health_service_state_deinit(app_state_get_health_service_state()); -#endif unobstructed_area_service_deinit(app_state_get_unobstructed_area_state()); } @@ -287,11 +265,9 @@ AppMessageCtx *app_state_get_app_message_ctx(void) { return &s_app_state_ptr->app_message_ctx; } -#if !defined(PLATFORM_TINTIN) BLEAppState* app_state_get_ble_app_state(void) { return &s_app_state_ptr->ble_app_state; } -#endif ClickManager* app_state_get_click_manager() { return &s_app_state_ptr->click_manager; @@ -341,25 +317,25 @@ BatteryStateServiceState *app_state_get_battery_state_service_state(void) { return &s_app_state_ptr->battery_state_service_state; } +BacklightServiceState *app_state_get_backlight_service_state(void) { + return &s_app_state_ptr->backlight_service_state; +} + TickTimerServiceState *app_state_get_tick_timer_service_state(void) { return &s_app_state_ptr->tick_timer_service_state; } +TouchServiceState *app_state_get_touch_service_state(void) { + return &s_app_state_ptr->touch_service_state; +} + ConnectionServiceState *app_state_get_connection_service_state(void) { return &s_app_state_ptr->connection_service_state; } -#if CAPABILITY_HAS_HEALTH_TRACKING HealthServiceState *app_state_get_health_service_state(void) { return &s_app_state_ptr->health_service_state; } -#endif - -#if CAPABILITY_HAS_ACCESSORY_CONNECTOR -SmartstrapConnectionState *app_state_get_smartstrap_state(void) { - return &s_app_state_ptr->smartstrap_state; -} -#endif ContentIndicatorsBuffer *app_state_get_content_indicators_buffer(void) { return &s_app_state_ptr->content_indicators_buffer; @@ -385,11 +361,9 @@ UnobstructedAreaState *app_state_get_unobstructed_area_state(void) { return &s_app_state_ptr->unobstructed_area_service_state; } -#if CAPABILITY_HAS_APP_GLANCES AppGlance *app_state_get_glance(void) { return &s_app_state_ptr->glance; } -#endif WakeupHandler app_state_get_wakeup_handler(void) { return s_app_state_ptr->wakeup_handler; @@ -403,42 +377,57 @@ EventServiceInfo *app_state_get_wakeup_event_info(void) { return &s_app_state_ptr->wakeup_event_info; } -GBitmap* app_state_legacy2_get_2bit_framebuffer(void) { -#ifdef PLATFORM_TINTIN - // Tintin platforms have a native framebuffer type of 2bit, they shouldn't be calling this. - WTF; -#endif +SpeakerFinishedCallback app_state_get_speaker_finish_handler(void) { + return s_app_state_ptr->speaker_finish_handler; +} +void app_state_set_speaker_finish_handler(SpeakerFinishedCallback handler) { + s_app_state_ptr->speaker_finish_handler = handler; +} + +void *app_state_get_speaker_finish_ctx(void) { + return s_app_state_ptr->speaker_finish_ctx; +} + +void app_state_set_speaker_finish_ctx(void *ctx) { + s_app_state_ptr->speaker_finish_ctx = ctx; +} + +EventServiceInfo *app_state_get_speaker_finish_event_info(void) { + return &s_app_state_ptr->speaker_finish_event_info; +} + +GBitmap* app_state_legacy2_get_2bit_framebuffer(void) { PBL_ASSERTN(s_app_state_ptr->legacy2_framebuffer); return s_app_state_ptr->legacy2_framebuffer; } -#if CAPABILITY_HAS_TOUCHSCREEN +#ifdef CONFIG_TOUCH RecognizerList *app_state_get_recognizer_list(void) { return &s_app_state_ptr->recognizer_list; } #endif -RockyRuntimeContext *app_state_get_rocky_runtime_context(void) { - return s_app_state_ptr->rocky_runtime_context; +JsRuntimeContext *app_state_get_js_runtime_context(void) { + return s_app_state_ptr->js_runtime_context; } -uint8_t *app_state_get_rocky_runtime_context_buffer(void) { - return s_app_state_ptr->rocky_runtime_context_buffer; +uint8_t *app_state_get_js_runtime_context_buffer(void) { + return s_app_state_ptr->js_runtime_context_buffer; } -void app_state_set_rocky_runtime_context(uint8_t *unaligned_buffer, - RockyRuntimeContext *rocky_runtime_context) { - s_app_state_ptr->rocky_runtime_context_buffer = unaligned_buffer; - s_app_state_ptr->rocky_runtime_context = rocky_runtime_context; +void app_state_set_js_runtime_context(uint8_t *unaligned_buffer, + JsRuntimeContext *js_runtime_context) { + s_app_state_ptr->js_runtime_context_buffer = unaligned_buffer; + s_app_state_ptr->js_runtime_context = js_runtime_context; } -RockyMemoryAPIContext *app_state_get_rocky_memory_api_context(void) { - return s_app_state_ptr->rocky_memory_api_context; +JsMemoryAPIContext *app_state_get_js_memory_api_context(void) { + return s_app_state_ptr->js_memory_api_context; } -void app_state_set_rocky_memory_api_context(RockyMemoryAPIContext *context) { - s_app_state_ptr->rocky_memory_api_context = context; +void app_state_set_js_memory_api_context(JsMemoryAPIContext *context) { + s_app_state_ptr->js_memory_api_context = context; } ApplibInternalEventsInfo *app_state_get_applib_internal_events_info(void) { @@ -467,7 +456,7 @@ void app_state_set_current_timeline_item_action_source(TimelineItemActionSource // Serial Commands /////////////////////////////////////////////////////////// -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION void command_dump_malloc_app(void) { heap_dump_malloc_instrumentation_to_dbgserial(app_state_get_heap()); } diff --git a/src/fw/process_state/app_state/app_state.h b/src/fw/process_state/app_state/app_state.h index 6756fe6744..d55a8683a0 100644 --- a/src/fw/process_state/app_state/app_state.h +++ b/src/fw/process_state/app_state/app_state.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,8 +7,9 @@ #include "applib/app_focus_service.h" #include "applib/app_inbox.h" #include "applib/app_message/app_message_internal.h" -#include "applib/app_smartstrap_private.h" #include "applib/app_wakeup.h" +#include "applib/backlight_service.h" +#include "applib/backlight_service_private.h" #include "applib/battery_state_service.h" #include "applib/battery_state_service_private.h" #include "applib/bluetooth/ble_app_support.h" @@ -36,15 +24,17 @@ #include "applib/plugin_service_private.h" #include "applib/tick_timer_service.h" #include "applib/tick_timer_service_private.h" +#include "applib/touch_service_private.h" #include "applib/ui/animation_private.h" #include "applib/ui/click_internal.h" #include "applib/ui/content_indicator_private.h" #include "applib/ui/recognizer/recognizer.h" +#include "applib/ui/speaker.h" #include "applib/ui/window_stack_private.h" #include "applib/unobstructed_area_service_private.h" #include "kernel/logging_private.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "pbl/services/timeline/timeline_actions.h" #include "util/heap.h" #include "util/list.h" @@ -78,8 +68,8 @@ typedef struct AppStateInitParams { typedef struct MemorySegment MemorySegment; -typedef struct RockyRuntimeContext RockyRuntimeContext; -typedef struct RockyMemoryAPIContext RockyMemoryAPIContext; +typedef struct JsRuntimeContext JsRuntimeContext; +typedef struct JsMemoryAPIContext JsMemoryAPIContext; //! Allocate memory in the process' address space for AppState data and //! perform initial configuration. @@ -127,30 +117,32 @@ LogState *app_state_get_log_state(void); BatteryStateServiceState *app_state_get_battery_state_service_state(void); +BacklightServiceState *app_state_get_backlight_service_state(void); + TickTimerServiceState *app_state_get_tick_timer_service_state(void); +TouchServiceState *app_state_get_touch_service_state(void); + ConnectionServiceState *app_state_get_connection_service_state(void); LocaleInfo *app_state_get_locale_info(void); -SmartstrapConnectionState *app_state_get_smartstrap_state(void); - ContentIndicatorsBuffer *app_state_get_content_indicators_buffer(void); HealthServiceState *app_state_get_health_service_state(void); RecognizerList *app_state_get_recognizer_list(void); -RockyRuntimeContext *app_state_get_rocky_runtime_context(void); +JsRuntimeContext *app_state_get_js_runtime_context(void); -uint8_t *app_state_get_rocky_runtime_context_buffer(void); +uint8_t *app_state_get_js_runtime_context_buffer(void); -void app_state_set_rocky_runtime_context(uint8_t *unaligned_buffer, - RockyRuntimeContext *rocky_runtime_context); +void app_state_set_js_runtime_context(uint8_t *unaligned_buffer, + JsRuntimeContext *js_runtime_context); -RockyMemoryAPIContext *app_state_get_rocky_memory_api_context(void); +JsMemoryAPIContext *app_state_get_js_memory_api_context(void); -void app_state_set_rocky_memory_api_context(RockyMemoryAPIContext *context); +void app_state_set_js_memory_api_context(JsMemoryAPIContext *context); bool *app_state_get_framebuffer_render_pending(); @@ -173,6 +165,12 @@ void app_state_set_wakeup_handler(WakeupHandler handler); EventServiceInfo *app_state_get_wakeup_event_info(void); +SpeakerFinishedCallback app_state_get_speaker_finish_handler(void); +void app_state_set_speaker_finish_handler(SpeakerFinishedCallback handler); +void *app_state_get_speaker_finish_ctx(void); +void app_state_set_speaker_finish_ctx(void *ctx); +EventServiceInfo *app_state_get_speaker_finish_event_info(void); + //! Retrieve a preallocated full screen 2bit framebuffer for use with 2.x apps that want to use the //! capture_frame_buffer API. Note this memory is only valid when used with 2.x apps. GBitmap* app_state_legacy2_get_2bit_framebuffer(void); diff --git a/src/fw/process_state/worker_state/worker_state.c b/src/fw/process_state/worker_state/worker_state.c index 31da3c9849..654ece1202 100644 --- a/src/fw/process_state/worker_state/worker_state.c +++ b/src/fw/process_state/worker_state/worker_state.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -44,13 +31,13 @@ typedef struct { BatteryStateServiceState battery_state_service_state; + BacklightServiceState backlight_service_state; + TickTimerServiceState tick_timer_service_state; ConnectionServiceState connection_service_state; -#if CAPABILITY_HAS_HEALTH_TRACKING HealthServiceState health_service_state; -#endif } WorkerState; KERNEL_READONLY_DATA static WorkerState *s_worker_state_ptr; @@ -70,19 +57,17 @@ void worker_state_init(void) { battery_state_service_state_init(worker_state_get_battery_state_service_state()); + backlight_service_state_init(worker_state_get_backlight_service_state()); + connection_service_state_init(worker_state_get_connection_service_state()); tick_timer_service_state_init(worker_state_get_tick_timer_service_state()); -#if CAPABILITY_HAS_HEALTH_TRACKING health_service_state_init(worker_state_get_health_service_state()); -#endif } void worker_state_deinit(void) { -#if CAPABILITY_HAS_HEALTH_TRACKING health_service_state_deinit(worker_state_get_health_service_state()); -#endif } Heap *worker_state_get_heap(void) { @@ -126,6 +111,10 @@ BatteryStateServiceState *worker_state_get_battery_state_service_state(void) { return &s_worker_state_ptr->battery_state_service_state; } +BacklightServiceState *worker_state_get_backlight_service_state(void) { + return &s_worker_state_ptr->backlight_service_state; +} + TickTimerServiceState *worker_state_get_tick_timer_service_state(void) { return &s_worker_state_ptr->tick_timer_service_state; } @@ -134,17 +123,15 @@ ConnectionServiceState *worker_state_get_connection_service_state(void) { return &s_worker_state_ptr->connection_service_state; } -#if CAPABILITY_HAS_HEALTH_TRACKING HealthServiceState *worker_state_get_health_service_state(void) { return &s_worker_state_ptr->health_service_state; } -#endif // =================================================================================================== // Serial Commands -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION void command_dump_malloc_worker(void) { heap_dump_malloc_instrumentation_to_dbgserial(worker_state_get_heap()); } diff --git a/src/fw/process_state/worker_state/worker_state.h b/src/fw/process_state/worker_state/worker_state.h index b4c627af24..31559c6ea9 100644 --- a/src/fw/process_state/worker_state/worker_state.h +++ b/src/fw/process_state/worker_state/worker_state.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,6 +8,8 @@ #include "applib/accel_service_private.h" #include "applib/compass_service_private.h" #include "applib/plugin_service_private.h" +#include "applib/backlight_service.h" +#include "applib/backlight_service_private.h" #include "applib/battery_state_service.h" #include "applib/battery_state_service_private.h" #include "applib/connection_service.h" @@ -60,6 +49,8 @@ LogState *worker_state_get_log_state(void); BatteryStateServiceState *worker_state_get_battery_state_service_state(void); +BacklightServiceState *worker_state_get_backlight_service_state(void); + TickTimerServiceState *worker_state_get_tick_timer_service_state(void); ConnectionServiceState *worker_state_get_connection_service_state(void); diff --git a/src/fw/qemu_flash_fw.ld.template b/src/fw/qemu_flash_fw.ld.template new file mode 100644 index 0000000000..25bfe4f8f2 --- /dev/null +++ b/src/fw/qemu_flash_fw.ld.template @@ -0,0 +1,24 @@ +MEMORY +{ + KERNEL_RAM (xrw) : ORIGIN = @KERNEL_RAM_ADDR@, LENGTH = @KERNEL_RAM_SIZE@ + WORKER_RAM (xrw) : ORIGIN = @WORKER_RAM_ADDR@, LENGTH = @WORKER_RAM_SIZE@ + APP_RAM (xrw) : ORIGIN = @APP_RAM_ADDR@, LENGTH = @APP_RAM_SIZE@ + + FLASH (rx) : ORIGIN = @FW_FLASH_ORIGIN@, LENGTH = @FW_FLASH_LENGTH@ +} + +__FLASH_start__ = @FLASH_ORIGIN@; +__FLASH_size__ = @FLASH_SIZE@; + +REGION_ALIAS("REGION_ISR_STACK", KERNEL_RAM); +REGION_ALIAS("REGION_KERNEL_STACKS", KERNEL_RAM); +REGION_ALIAS("REGION_KERNEL_HEAP", KERNEL_RAM); + +SECTIONS +{ + .isr_vector : { + ASSERT(. == ORIGIN(FLASH), "Error: Vector table is offset from the start of the FLASH region"); + PROVIDE(__ISR_VECTOR_TABLE__ = .); + KEEP(*(.isr_vector)) + } >FLASH +} diff --git a/src/fw/resource/font_resource_table.h b/src/fw/resource/font_resource_table.h index 49e71436c3..048fbf9513 100644 --- a/src/fw/resource/font_resource_table.h +++ b/src/fw/resource/font_resource_table.h @@ -1,16 +1,3 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/src/fw/resource/resource.c b/src/fw/resource/resource.c index 00189ce0fc..be09007a7c 100644 --- a/src/fw/resource/resource.c +++ b/src/fw/resource/resource.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource.h" #include "resource_storage.h" @@ -24,7 +11,7 @@ #include "flash_region/flash_region.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/process_management/app_storage.h" #include "system/logging.h" #include "system/passert.h" @@ -40,6 +27,16 @@ PebbleRecursiveMutex *s_resource_mutex = NULL; static CachedResource *s_resource_list = NULL; +// Last-resolved app resource; invalidated in resource_init_app(). +typedef struct { + bool valid; + ResAppNum app_num; + uint32_t id; + ResourceStoreEntry entry; +} AppResourceCache; + +static AppResourceCache s_app_resource_cache; + static bool prv_resource_filter(ListNode *found_node, void *data) { CachedResource *resource = (CachedResource *)found_node; uint32_t resource_id = (uint32_t)data; @@ -63,7 +60,24 @@ static void prv_get_resource(ResAppNum app_num, uint32_t id, ResourceStoreEntry return; } + // App resource resolution re-reads the PFS manifest + table every call (costly + // on the glyph hot path). Cache the last one; it holds offsets, so it survives + // GC. + if (app_num != SYSTEM_APP && s_app_resource_cache.valid && + s_app_resource_cache.app_num == app_num && s_app_resource_cache.id == id) { + *entry = s_app_resource_cache.entry; + mutex_unlock_recursive(s_resource_mutex); + return; + } + resource_storage_get_resource(app_num, id, entry); + + if (app_num != SYSTEM_APP && entry->id >= 1) { + s_app_resource_cache = (AppResourceCache){ + .valid = true, .app_num = app_num, .id = id, .entry = *entry, + }; + } + mutex_unlock_recursive(s_resource_mutex); } @@ -71,6 +85,8 @@ static void prv_get_resource(ResAppNum app_num, uint32_t id, ResourceStoreEntry bool resource_init_app(ResAppNum app_num, const ResourceVersion *expected_version) { // resource_id is ignored in this case, so we set it to 0 mutex_lock_recursive(s_resource_mutex); + // Drop the cache: a reused app slot must not serve a stale entry. + s_app_resource_cache.valid = false; bool rv = resource_storage_check(app_num, 0, expected_version); mutex_unlock_recursive(s_resource_mutex); return rv; @@ -135,7 +151,7 @@ size_t resource_load_byte_range_system(ResAppNum app_num, uint32_t resource_id, // We want to stop the FW from doing this, so we added an assert // but in the name of backwards compatibility, we let the app misbehave num_bytes = resource.length - offset; - PBL_LOG(LOG_LEVEL_DEBUG, "Tried to read past end of resource, reading %d bytes", + PBL_LOG_DBG("Tried to read past end of resource, reading %d bytes", (int)num_bytes); } diff --git a/src/fw/resource/resource.h b/src/fw/resource/resource.h index 8c1dc3b73a..816523545e 100644 --- a/src/fw/resource/resource.h +++ b/src/fw/resource/resource.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/resource/resource_impl.def b/src/fw/resource/resource_impl.def index 8d62cda9ae..3a7cc7b1ef 100644 --- a/src/fw/resource/resource_impl.def +++ b/src/fw/resource/resource_impl.def @@ -1,7 +1,7 @@ // This order is significant. // The first implementation that matches the resource/store query gets it. -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW RESOURCE_IMPL(g_app_file_impl) RESOURCE_IMPL(g_builtin_impl) RESOURCE_IMPL(g_file_impl) diff --git a/src/fw/resource/resource_mapped.c b/src/fw/resource/resource_mapped.c deleted file mode 100644 index 9ddb990f20..0000000000 --- a/src/fw/resource/resource_mapped.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "resource_mapped.h" - -#include "drivers/flash.h" -#include "system/passert.h" - -#if CAPABILITY_HAS_MAPPABLE_FLASH -static uint32_t s_mapped_refcount_for_task[NumPebbleTask]; - -void resource_mapped_use(PebbleTask task) { - // We keep track of the refcount per task so we can cleanup all resources for a task - s_mapped_refcount_for_task[task]++; - flash_use(); -} - -void resource_mapped_release(PebbleTask task) { - PBL_ASSERTN(s_mapped_refcount_for_task[task] != 0); - s_mapped_refcount_for_task[task]--; - flash_release_many(1); -} - -void resource_mapped_release_all(PebbleTask task) { - flash_release_many(s_mapped_refcount_for_task[task]); - s_mapped_refcount_for_task[task] = 0; -} -#endif diff --git a/src/fw/resource/resource_mapped.h b/src/fw/resource/resource_mapped.h deleted file mode 100644 index 84ad4f6aaa..0000000000 --- a/src/fw/resource/resource_mapped.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/pebble_tasks.h" - - -void resource_mapped_use(PebbleTask task); - -void resource_mapped_release(PebbleTask task); - -void resource_mapped_release_all(PebbleTask task); diff --git a/src/fw/resource/resource_storage.c b/src/fw/resource/resource_storage.c index 3636132e65..31ba2268ec 100644 --- a/src/fw/resource/resource_storage.c +++ b/src/fw/resource/resource_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource_storage.h" #include "resource_storage_impl.h" @@ -20,7 +7,7 @@ #include #include -#include "services/normal/filesystem/app_file.h" +#include "pbl/services/filesystem/app_file.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/passert.h" @@ -46,10 +33,10 @@ static uint32_t prv_check_resource_bounds(ResourceStoreEntry *entry, uint32_t st uint32_t resource_offset = store_offset - entry->offset; if (entry->length < resource_offset) { - PBL_LOG(LOG_LEVEL_ERROR, "Resource offset past its own ending."); + PBL_LOG_ERR("Resource offset past its own ending."); return 0; } else if ((resource_offset + num_bytes) > entry->length) { - PBL_LOG(LOG_LEVEL_ERROR, "offset + length > resource size, truncated."); + PBL_LOG_ERR("offset + length > resource size, truncated."); return entry->length - resource_offset; } else { return num_bytes; @@ -105,7 +92,7 @@ T_STATIC uint32_t prv_get_store_length(ResourceStoreEntry *entry, ResourceManife // Catch an overflow if the store is enormous (unlikely unless corrupted) if (store_length < resource_end_offset) { // overflow - PBL_LOG(LOG_LEVEL_ERROR, "Overflow while validating resource"); + PBL_LOG_ERR("Overflow while validating resource"); return 0; } @@ -123,21 +110,21 @@ static bool prv_validate_store(ResourceManifest *manifest, ResourceStoreEntry *e uint32_t num_bytes; if ((num_bytes = prv_get_store_length(entry, manifest)) == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Resource table check failed. Table or manifest may be corrupted"); + PBL_LOG_WRN("Resource table check failed. Table or manifest may be corrupted"); return false; } uint32_t calculated_crc = prv_get_crc(entry, num_bytes, 0); if (calculated_crc != manifest->version.crc) { - PBL_LOG(LOG_LEVEL_WARNING, "Resource crc mismatch for app %"PRIu32".", app_num); - PBL_LOG(LOG_LEVEL_WARNING, "0x%"PRIx32" != 0x%"PRIx32, calculated_crc, manifest->version.crc); + PBL_LOG_WRN("Resource crc mismatch for app %"PRIu32".", app_num); + PBL_LOG_WRN("0x%"PRIx32" != 0x%"PRIx32, calculated_crc, manifest->version.crc); - PBL_LOG(LOG_LEVEL_WARNING, "PBL-28517: If you see this please let Brad know"); + PBL_LOG_WRN("PBL-28517: If you see this please let Brad know"); const uint32_t calculated_crc_again = prv_get_crc(entry, num_bytes, 0); - PBL_LOG(LOG_LEVEL_WARNING, "Num bytes is %"PRIu32, num_bytes); - PBL_LOG(LOG_LEVEL_WARNING, "Calculated the CRC again, got 0x%"PRIx32, calculated_crc_again); + PBL_LOG_WRN("Num bytes is %"PRIu32, num_bytes); + PBL_LOG_WRN("Calculated the CRC again, got 0x%"PRIx32, calculated_crc_again); return calculated_crc_again == manifest->version.crc; } @@ -158,8 +145,7 @@ static void prv_get_store_entry(ResAppNum app_num, uint32_t resource_id, return; } } - PBL_LOG(LOG_LEVEL_WARNING, - "get_store_entry(%"PRIu32",%"PRIu32") failed to find appropriate store", + PBL_LOG_WRN("get_store_entry(%"PRIu32",%"PRIu32") failed to find appropriate store", app_num, resource_id); entry->impl = NULL; } @@ -167,7 +153,7 @@ static void prv_get_store_entry(ResAppNum app_num, uint32_t resource_id, static bool prv_validate_entry(ResourceStoreEntry *entry, ResourceManifest *manifest, uint32_t resource_id) { if (entry->id > manifest->num_resources) { - PBL_LOG(LOG_LEVEL_DEBUG, "Out of bound resource %"PRId32" vs %"PRId32, + PBL_LOG_DBG("Out of bound resource %"PRId32" vs %"PRId32, entry->id, manifest->num_resources); return false; } @@ -178,14 +164,14 @@ static bool prv_validate_entry(ResourceStoreEntry *entry, ResourceManifest *mani } if (entry->id != table_entry.resource_id) { - PBL_LOG(LOG_LEVEL_ERROR, "Resource table entry for %" PRIx32 " is corrupt!" + PBL_LOG_ERR("Resource table entry for %" PRIx32 " is corrupt!" "(%"PRIx32" != %"PRIx32")", resource_id, entry->id, table_entry.resource_id); return false; } const uint32_t resource_crc = prv_get_crc(entry, table_entry.length, table_entry.offset); if (resource_crc != table_entry.crc) { - PBL_LOG(LOG_LEVEL_DEBUG, "Bad resource CRC for %" PRIx32 ", %" PRIx32 + PBL_LOG_DBG("Bad resource CRC for %" PRIx32 ", %" PRIx32 " vs %" PRIx32, resource_id, resource_crc, table_entry.crc); return false; } @@ -283,7 +269,7 @@ ResourceCallbackHandle resource_watch(ResAppNum app_num, uint32_t resource_id, } void resource_unwatch(ResourceCallbackHandle cb_handle) { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW // TODO: Support unwatching not-files. g_file_impl.unwatch(cb_handle); #endif @@ -307,9 +293,9 @@ bool resource_storage_generic_check(ResAppNum app_num, uint32_t resource_id, ResourceManifest manifest; prv_get_manifest(entry, &manifest); if (expected_version && !resource_version_matches(&manifest.version, expected_version)) { - PBL_LOG(LOG_LEVEL_WARNING, "expected version <%#010"PRIx32", %"PRIu32">,", + PBL_LOG_WRN("expected version <%#010"PRIx32", %"PRIu32">,", expected_version->crc, expected_version->timestamp); - PBL_LOG(LOG_LEVEL_WARNING, "got <%#010"PRIx32", %"PRIu32">,", + PBL_LOG_WRN("got <%#010"PRIx32", %"PRIu32">,", manifest.version.crc, manifest.version.timestamp); return false; } @@ -322,7 +308,7 @@ bool resource_storage_generic_check(ResAppNum app_num, uint32_t resource_id, if (resource_id == 0) { return (prv_validate_store(&manifest, entry, app_num)); } else if (!prv_validate_entry(entry, &manifest, resource_id)) { - PBL_LOG(LOG_LEVEL_WARNING, "Resource %"PRId32" check for App %"PRIu32" failed", + PBL_LOG_WRN("Resource %"PRId32" check for App %"PRIu32" failed", resource_id, app_num); return false; } @@ -372,7 +358,7 @@ uint32_t resource_storage_generic_write(ResourceStoreEntry *entry, uint32_t offs ResourceCallbackHandle resource_storage_generic_watch(ResourceStoreEntry *entry, ResourceChangedCallback callback, void* data) { - PBL_LOG(LOG_LEVEL_WARNING, "resource_watch not supported for resource type %d.", + PBL_LOG_WRN("resource_watch not supported for resource type %d.", entry->impl->type); return NULL; } diff --git a/src/fw/resource/resource_storage.h b/src/fw/resource/resource_storage.h index 4abd1f54ab..c23cdc0c07 100644 --- a/src/fw/resource/resource_storage.h +++ b/src/fw/resource/resource_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/resource/resource_storage_builtin.c b/src/fw/resource/resource_storage_builtin.c index 6dc627078a..01f4c43238 100644 --- a/src/fw/resource/resource_storage_builtin.c +++ b/src/fw/resource/resource_storage_builtin.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource_storage_builtin.h" #include "resource_storage_impl.h" #include "kernel/memory_layout.h" #include "kernel/pbl_malloc.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/process_management/app_storage.h" #include @@ -65,7 +52,7 @@ static bool resource_storage_builtin_find_resource(ResourceStoreEntry *entry, Re // In the future, we need to change this hideously gross behavior. // BUTTTTTTT, PRF only has builtin, so we _need_ to say yes on PRF! if (resource_id == 0) { -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW return true; #else return false; diff --git a/src/fw/resource/resource_storage_builtin.h b/src/fw/resource/resource_storage_builtin.h index 2b8b4d87b0..6f095c974b 100644 --- a/src/fw/resource/resource_storage_builtin.h +++ b/src/fw/resource/resource_storage_builtin.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/resource/resource_storage_file.c b/src/fw/resource/resource_storage_file.c index 20f9f6f5b6..7eb0553bc5 100644 --- a/src/fw/resource/resource_storage_file.c +++ b/src/fw/resource/resource_storage_file.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource_storage_impl.h" #include "resource_storage_file.h" #include "kernel/util/sleep.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "system/logging.h" #include @@ -77,7 +64,7 @@ static int prv_file_open_by_name(const char *name, uint8_t op_flags) { int fd = pfs_open(name, op_flags, FILE_TYPE_STATIC, 0); if ((fd < 0) && (fd != E_DOES_NOT_EXIST)) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not open resource pfs file <%s>, fd: %d", name, fd); + PBL_LOG_WRN("Could not open resource pfs file <%s>, fd: %d", name, fd); } return fd; @@ -156,14 +143,14 @@ static void resource_storage_file_init(void) { const int fd = prv_file_open_by_name(name, op_flags); const uint32_t file_length = prv_file_common_get_length_and_close(fd); - PBL_LOG(LOG_LEVEL_INFO, "File %s has length %"PRIu32, name, file_length); + PBL_LOG_INFO("File %s has length %"PRIu32, name, file_length); // Now check each entry in the file for (uint32_t resource_id = g_file_resource_stores[i].first_resource_id; resource_id <= g_file_resource_stores[i].last_resource_id; resource_id++) { // TODO PBL-21402 if (!resource_storage_check(SYSTEM_APP, resource_id, NULL)) { - PBL_LOG(LOG_LEVEL_ERROR, "System resource file %"PRIu32" corrupt!!!", resource_id); + PBL_LOG_ERR("System resource file %"PRIu32" corrupt!!!", resource_id); } const uint32_t large_file_size_threshold = 200 * 1024; @@ -211,7 +198,7 @@ static int prv_app_file_open(ResourceStoreEntry *entry, uint8_t op_flags) { int fd = pfs_open(filename, op_flags, FILE_TYPE_STATIC, 0); if ((fd < 0) && (fd != E_DOES_NOT_EXIST)) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not open resource pfs file <%s>, fd: %d", filename, fd); + PBL_LOG_WRN("Could not open resource pfs file <%s>, fd: %d", filename, fd); } return fd; diff --git a/src/fw/resource/resource_storage_file.h b/src/fw/resource/resource_storage_file.h index 48df1e9f72..bf27348712 100644 --- a/src/fw/resource/resource_storage_file.h +++ b/src/fw/resource/resource_storage_file.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/resource/resource_storage_flash.c b/src/fw/resource/resource_storage_flash.c index 1a8de9226c..a10055a02b 100644 --- a/src/fw/resource/resource_storage_flash.c +++ b/src/fw/resource/resource_storage_flash.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource_storage_flash.h" #include "resource_storage_impl.h" @@ -20,7 +7,7 @@ #include "drivers/flash.h" #include "kernel/pbl_malloc.h" #include "resource/resource_version.auto.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/process_management/app_storage.h" #include "system/bootbits.h" #include "system/logging.h" #include "util/size.h" @@ -60,9 +47,9 @@ static void resource_storage_system_bank_init(void) { // Increment s_active_bank and call resource_storage_generic_check for each value to find // a bank that's valid. for (s_active_bank = 0; s_active_bank < ARRAY_LENGTH(s_resource_banks); ++s_active_bank) { - PBL_LOG(LOG_LEVEL_INFO, "Checking bank %u for system resources", s_active_bank); + PBL_LOG_INFO("Checking bank %u for system resources", s_active_bank); if (resource_storage_generic_check(SYSTEM_APP, 0, &entry, &SYSTEM_RESOURCE_VERSION)) { - PBL_LOG(LOG_LEVEL_INFO, "Valid system resources found!"); + PBL_LOG_INFO("Valid system resources found!"); s_valid_resources_found = true; return; } @@ -99,29 +86,8 @@ static uint32_t resource_storage_system_bank_metadata_size(ResourceStoreEntry *e return SYSTEM_STORE_METADATA_BYTES; } -// PBL-28517 investigation -extern uint8_t pbl_28517_flash_impl_get_status_register(uint32_t sector_addr); - static uint32_t resource_storage_system_bank_get_crc(ResourceStoreEntry *entry, uint32_t num_bytes, uint32_t entry_offset) { -#if (PLATFORM_SNOWY || PLATFORM_SPALDING) && !RELEASE && !UNITTEST - // PBL-28517 investigation - if (entry_offset == 0) { - // We're calculating the CRC of the whole bank. Before we do this, let's save the status - // register for each sector so we can see if the flash is in a funny state. - - for (int i = 0; (i * SECTOR_SIZE_BYTES) < (int) num_bytes; ++i) { - const uint32_t addr = (BANK.begin + (i * SECTOR_SIZE_BYTES)) & SECTOR_ADDR_MASK; - - uint8_t status_reg = pbl_28517_flash_impl_get_status_register(addr); - uint32_t crc = flash_calculate_legacy_defective_checksum(addr, SECTOR_SIZE_BYTES); - - PBL_LOG(LOG_LEVEL_DEBUG, "PBL-28517 Sector 0x%"PRIx32" Status 0x%"PRIx8" CRC 0x%"PRIx32, - addr, status_reg, crc); - } - } -#endif - uint32_t start_offset = resource_store_get_metadata_size(entry) + entry_offset; return flash_calculate_legacy_defective_checksum( BANK.begin + start_offset, num_bytes); @@ -133,33 +99,33 @@ static uint32_t resource_storage_system_bank_read(ResourceStoreEntry *entry, uin return num_bytes; } -#if CAPABILITY_HAS_MAPPABLE_FLASH bool resource_storage_flash_bytes_are_readonly(const void *bytes) { - return (bytes > (void *)FLASH_MEMORY_MAPPABLE_ADDRESS) && - (bytes < (void *)(FLASH_MEMORY_MAPPABLE_ADDRESS + FLASH_MEMORY_MAPPABLE_SIZE)); +#ifdef CONFIG_MMAP_RESOURCES + // Pointers inside the banks are in the always-mapped XIP window. + for (unsigned int i = 0; i < ARRAY_LENGTH(s_resource_banks); i++) { + if ((bytes >= (const void *)(uintptr_t)s_resource_banks[i].begin) && + (bytes < (const void *)(uintptr_t)s_resource_banks[i].end)) { + return true; + } + } +#endif + return false; } static const uint8_t *resource_storage_system_bank_readonly_bytes(ResourceStoreEntry *entry, bool has_privileged_access) { +#ifdef CONFIG_MMAP_RESOURCES + // Bank is XIP-mapped, so data is addressable at BANK.begin + offset. Only + // privileged callers get the raw pointer; unprivileged apps use the copy path. if (!has_privileged_access) { return NULL; } - return (uint8_t *)(uintptr_t)(FLASH_MEMORY_MAPPABLE_ADDRESS + BANK.begin + entry->offset); -} - + return (const uint8_t *)(uintptr_t)(BANK.begin + entry->offset); #else - -bool resource_storage_flash_bytes_are_readonly(const void *bytes) { - return false; -} - -static const uint8_t *resource_storage_system_bank_readonly_bytes(ResourceStoreEntry *entry, - bool has_privileged_access) { return NULL; +#endif } -#endif // CAPABILITY_HAS_MAPPABLE_FLASH - static void resource_storage_system_bank_clear(ResourceStoreEntry *entry) { uint8_t buffer[MANIFEST_SIZE] = {0}; flash_write_bytes(buffer, BANK.begin, MANIFEST_SIZE); diff --git a/src/fw/resource/resource_storage_flash.h b/src/fw/resource/resource_storage_flash.h index 2f486bd974..cf6402ba12 100644 --- a/src/fw/resource/resource_storage_flash.h +++ b/src/fw/resource/resource_storage_flash.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/resource/resource_storage_impl.h b/src/fw/resource/resource_storage_impl.h index 60706d1665..0bca76028b 100644 --- a/src/fw/resource/resource_storage_impl.h +++ b/src/fw/resource/resource_storage_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -57,7 +44,7 @@ typedef struct PACKED { } ResTableEntry; #define MAX_RESOURCES_PER_STORE 256 -#define MAX_RESOURCES_FOR_SYSTEM_STORE 512 +#define MAX_RESOURCES_FOR_SYSTEM_STORE 768 #define MANIFEST_SIZE (sizeof(ResourceManifest)) #define TABLE_ENTRY_SIZE (sizeof(ResTableEntry)) #define RESOURCE_STORE_METADATA_BYTES \ diff --git a/src/fw/resource/resource_syscalls.c b/src/fw/resource/resource_syscalls.c index 4dc7eeefdd..ec4f52be5e 100644 --- a/src/fw/resource/resource_syscalls.c +++ b/src/fw/resource/resource_syscalls.c @@ -1,21 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource.h" -#include "resource_mapped.h" #include "process_management/app_manager.h" #include "kernel/memory_layout.h" @@ -81,13 +67,3 @@ DEFINE_SYSCALL(bool, sys_resource_is_valid, ResAppNum app_num, uint32_t resource DEFINE_SYSCALL(uint32_t, sys_resource_get_and_cache, ResAppNum app_num, uint32_t resource_id) { return resource_get_and_cache(app_num, resource_id); } - -DEFINE_SYSCALL(void, sys_resource_mapped_use) { - PebbleTask task = pebble_task_get_current(); - resource_mapped_use(task); -} - -DEFINE_SYSCALL(void, sys_resource_mapped_release) { - PebbleTask task = pebble_task_get_current(); - resource_mapped_release(task); -} diff --git a/src/fw/resource/system_resource.c b/src/fw/resource/system_resource.c index 2e4e992ab0..632682a5ee 100644 --- a/src/fw/resource/system_resource.c +++ b/src/fw/resource/system_resource.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system_resource.h" @@ -23,7 +10,6 @@ #include "kernel/panic.h" #include "kernel/pbl_malloc.h" #include "kernel/util/fw_reset.h" -#include "pebble_errors.h" #include "resource/resource.h" #include "resource/resource_storage.h" #include "syscall/syscall_internal.h" @@ -39,12 +25,13 @@ void system_resource_init(void) { if (!resource_init_app(SYSTEM_APP, &SYSTEM_RESOURCE_VERSION)) { // System resources are missing! -#if defined(IS_BIGBOARD) +#if defined(CONFIG_IS_BIGBOARD) + static const uint32_t ERROR_BAD_RESOURCES = 0xfe504505; pbl_log(LOG_LEVEL_ERROR, __FILE_NAME__, __LINE__, "System resources are missing or corrupt, time to sad watch"); launcher_panic(ERROR_BAD_RESOURCES); #else - PBL_LOG(LOG_LEVEL_ERROR, "System resources are missing or corrupt! Going to PRF"); + PBL_LOG_ERR("System resources are missing or corrupt! Going to PRF"); fw_reset_into_prf(); #endif } @@ -61,7 +48,7 @@ FontInfo s_system_fonts_info_table[NUM_SYSTEM_FONTS + 1] KERNEL_READONLY_DATA; static GFont prv_load_system_font(const char *font_key) { if (font_key == NULL) { - PBL_LOG(LOG_LEVEL_DEBUG, "GETTING FALLBACK FONT"); + PBL_LOG_DBG("GETTING FALLBACK FONT"); // load fallback font if (!s_system_fonts_info_table[NUM_SYSTEM_FONTS].loaded) { PBL_ASSERTN(text_resources_init_font(SYSTEM_APP, RESOURCE_ID_FONT_FALLBACK_INTERNAL, 0, @@ -102,7 +89,7 @@ DEFINE_SYSCALL(GFont, sys_font_get_system_font, const char *font_key) { if (font_key && PRIVILEGE_WAS_ELEVATED) { if (!memory_layout_is_cstring_in_region(memory_layout_get_app_region(), font_key, 100) && !memory_layout_is_cstring_in_region(memory_layout_get_microflash_region(), font_key, 100)) { - PBL_LOG(LOG_LEVEL_ERROR, "Pointer %p not in app or microflash region", font_key); + PBL_LOG_ERR("Pointer %p not in app or microflash region", font_key); syscall_failed(); } } diff --git a/src/fw/resource/system_resource.h b/src/fw/resource/system_resource.h index 366a162ac1..7b5d83df5c 100644 --- a/src/fw/resource/system_resource.h +++ b/src/fw/resource/system_resource.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/services/Kconfig b/src/fw/services/Kconfig new file mode 100644 index 0000000000..d85868ea89 --- /dev/null +++ b/src/fw/services/Kconfig @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menu "Services" + +rsource "accel_manager/Kconfig" +rsource "activity/Kconfig" +rsource "alarms/Kconfig" +rsource "analytics/Kconfig" +rsource "animation_service/Kconfig" +rsource "app_cache/Kconfig" +rsource "app_fetch_endpoint/Kconfig" +rsource "app_glances/Kconfig" +rsource "app_inbox_service/Kconfig" +rsource "app_message/Kconfig" +rsource "app_order_endpoint/Kconfig" +rsource "app_outbox_service/Kconfig" +rsource "audio_endpoint/Kconfig" +rsource "battery/Kconfig" +rsource "blob_db/Kconfig" +rsource "bluetooth/Kconfig" +rsource "boot_splash/Kconfig" +rsource "clock/Kconfig" +rsource "comm_session/Kconfig" +rsource "compositor/Kconfig" +rsource "contacts/Kconfig" +rsource "cron/Kconfig" +rsource "data_logging/Kconfig" +rsource "debounced_connection_service/Kconfig" +rsource "ecompass/Kconfig" +rsource "event_service/Kconfig" +rsource "evented_timer/Kconfig" +rsource "filesystem/Kconfig" +rsource "firmware_update/Kconfig" +rsource "get_bytes/Kconfig" +rsource "health_sync_endpoint/Kconfig" +rsource "hrm/Kconfig" +rsource "i18n/Kconfig" +rsource "idle_watchdog/Kconfig" +rsource "legacy/Kconfig" +rsource "light/Kconfig" +rsource "music/Kconfig" +rsource "new_timer/Kconfig" +rsource "notifications/Kconfig" +rsource "orientation_manager/Kconfig" +rsource "persist/Kconfig" +rsource "phone_call/Kconfig" +rsource "phone_pp/Kconfig" +rsource "ping/Kconfig" +rsource "poll_remote/Kconfig" +rsource "powermode_service/Kconfig" +rsource "prf_update/Kconfig" +rsource "process_management/Kconfig" +rsource "protobuf_log/Kconfig" +rsource "put_bytes/Kconfig" +rsource "registry_endpoint/Kconfig" +rsource "regular_timer/Kconfig" +rsource "send_text_service/Kconfig" +rsource "services_common/Kconfig" +rsource "services_normal/Kconfig" +rsource "settings/Kconfig" +rsource "shared_prf_storage/Kconfig" +rsource "speaker/Kconfig" +rsource "stationary/Kconfig" +rsource "system_task/Kconfig" +rsource "tick_timer/Kconfig" +rsource "timeline/Kconfig" +rsource "timezone_database/Kconfig" +rsource "touch/Kconfig" +rsource "vibe_pattern/Kconfig" +rsource "vibes/Kconfig" +rsource "voice/Kconfig" +rsource "voice_endpoint/Kconfig" +rsource "wakeup/Kconfig" +rsource "weather/Kconfig" + +endmenu diff --git a/src/fw/services/accel_manager/Kconfig b/src/fw/services/accel_manager/Kconfig new file mode 100644 index 0000000000..75bac935e4 --- /dev/null +++ b/src/fw/services/accel_manager/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ACCEL_MANAGER + bool "Accelerometer manager" + help + Accelerometer data subscription manager. + +if SERVICE_ACCEL_MANAGER + +module = SERVICE_ACCEL_MANAGER +module-str = Accelerometer manager +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/accel_manager/service.c b/src/fw/services/accel_manager/service.c new file mode 100644 index 0000000000..dece4c4544 --- /dev/null +++ b/src/fw/services/accel_manager/service.c @@ -0,0 +1,898 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/accel_manager.h" + +#include "console/prompt.h" +#include "drivers/accel.h" +#include "drivers/vibe.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "mcu/interrupts.h" +#include "os/mutex.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/event_service.h" +#include "pbl/services/system_task.h" +#include "pbl/services/imu/units.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/math.h" +#include "util/shared_circular_buffer.h" + +#include "FreeRTOS.h" +#include "queue.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_accel_manager, CONFIG_SERVICE_ACCEL_MANAGER_LOG_LEVEL); + +// We use this as an argument to indicate a lookup of the current task +#define PEBBLE_TASK_CURRENT PebbleTask_Unknown + +#define US_PER_SECOND (1000 * 1000) + +typedef void (*ProcessDataHandler)(CallbackEventCallback *cb, void *data); + +// We create one of these for each data service subscriber +typedef struct AccelManagerState { + ListNode list_node; // Entry into the s_data_subscribers linked list + + //! Client pointing into s_buffer + SubsampledSharedCircularBufferClient buffer_client; + //! The sampling interval we've promised to this client after subsampling. + uint32_t sampling_interval_us; + //! The requested number of samples needed before calling data_cb_handler + uint16_t samples_per_update; + + //! Which task we should call the data_cb_handler on + PebbleTask task; + CallbackEventCallback data_cb_handler; + void* data_cb_context; + + uint64_t timestamp_ms; // timestamp of first item in the buffer + AccelRawData *raw_buffer; // raw buffer allocated by subscriber + uint8_t num_samples; // number of samples in raw_buffer + bool event_posted; // True if we've posted a "data ready" callback event +} AccelManagerState; + +typedef struct { + AccelRawData rawdata; + // The exact time the sample was collected can be recovered by: + // time_sample_collected = s_last_empty_timestamp_ms + timestamp_delta_ms + uint16_t timestamp_delta_ms; +} AccelManagerBufferData; +_Static_assert(offsetof(AccelManagerBufferData, rawdata) == 0, + "AccelRawData must be first entry in AccelManagerBufferData struct"); + +// Statics +//! List of all registered consumers of accel data. Points to AccelManagerState objects. +static ListNode *s_data_subscribers = NULL; +//! Mutex locking all accel_manager state +static PebbleRecursiveMutex *s_accel_manager_mutex; + +//! Reference count of how many shake subscribers we have. Used to turn off the feature when not +//! in use. +static uint8_t s_shake_subscribers_count = 0; +//! Reference count of how many double tap subscribers we have. Used to turn off the feature when +//! not in use. +static uint8_t s_double_tap_subscribers_count = 0; + +//! Circular buffer that raw accel data is written into before being subsampled for each client +static SharedCircularBuffer s_buffer; +//! Storage for s_buffer +//! 1600 bytes (~4s of data at 50Hz) +static uint8_t s_buffer_storage[200 * sizeof(AccelManagerBufferData)]; + +static uint64_t s_last_empty_timestamp_ms = 0; + +//! Whether the accel manager is enabled. When disabled (e.g. during factory reset via +//! RunLevel_BareMinimum), the accelerometer hardware is powered down and callbacks are ignored. +static bool s_enabled = true; + +//! Whether the kernel is subscribed to shake events for the motion backlight feature. +static bool s_motion_backlight_subscribed = false; + +// Accel Idle +#define ACCEL_MAX_IDLE_DELTA 100 +static AccelData s_last_analytics_position; +static AccelData s_last_accel_data; + +static void prv_setup_subsampling(uint32_t sampling_interval); + +static void prv_shake_add_subscriber_cb(PebbleTask task) { + mutex_lock_recursive(s_accel_manager_mutex); + { + if (++s_shake_subscribers_count == 1) { + PBL_LOG_DBG("Starting accel shake service"); + accel_enable_shake_detection(true); + prv_setup_subsampling(accel_get_sampling_interval()); + } + } + mutex_unlock_recursive(s_accel_manager_mutex); +} + +static void prv_shake_remove_subscriber_cb(PebbleTask task) { + mutex_lock_recursive(s_accel_manager_mutex); + { + PBL_ASSERTN(s_shake_subscribers_count > 0); + if (--s_shake_subscribers_count == 0) { + PBL_LOG_DBG("Stopping accel shake service"); + accel_enable_shake_detection(false); + prv_setup_subsampling(accel_get_sampling_interval()); + } + } + mutex_unlock_recursive(s_accel_manager_mutex); +} + +static void prv_double_tap_add_subscriber_cb(PebbleTask task) { + mutex_lock_recursive(s_accel_manager_mutex); + + if (++s_double_tap_subscribers_count == 1) { + PBL_LOG_DBG("Starting accel double tap service"); + accel_enable_double_tap_detection(true); + prv_setup_subsampling(accel_get_sampling_interval()); + } + + mutex_unlock_recursive(s_accel_manager_mutex); +} + +static void prv_double_tap_remove_subscriber_cb(PebbleTask task) { + mutex_lock_recursive(s_accel_manager_mutex); + + PBL_ASSERTN(s_double_tap_subscribers_count > 0); + if (--s_double_tap_subscribers_count == 0) { + PBL_LOG_DBG("Stopping accel double tap service"); + accel_enable_double_tap_detection(false); + prv_setup_subsampling(accel_get_sampling_interval()); + } + + mutex_unlock_recursive(s_accel_manager_mutex); +} + + +//! Out of all accel subscribers, figures out: +//! @param[out] lowest_interval_us - the lowest sampling interval requested (in microseconds) +//! @param[out] max_n_samples - the max number of samples requested for batching +//! @return The longest amount of samples which can be batched assuming we are +//! running at the lowest_sampling_interval +//! +//! @note currently the longest interval we can batch samples for is computed +//! as the minimum of (samples to batch / sample rate) out of all the active +//! subscribers. This means that if we have two subscribers, subscriber A at +//! 200ms, and subscriber B at 250ms, new samples will become available every +//! 200ms, so subscriber B's data buffer would not fill until 400ms, resulting +//! in a 150ms latency. This is how the legacy implementation worked as well +//! but is potentionally something we could improve in the future if it becomes +//! a problem. +static uint32_t prv_get_sample_interval_info(uint32_t *lowest_interval_us, + uint32_t *max_n_samples) { + *lowest_interval_us = (US_PER_SECOND / ACCEL_SAMPLING_10HZ); + *max_n_samples = 0; + // Tracks which subscriber wants data most frequently. Note this is different than just + // lowest_interval_us * max_n_samples as those values can come from 2 different subscribers + // where we want to know which one subscriber wants the highest update frequency. + uint32_t lowest_us_per_update = UINT32_MAX; + + AccelManagerState *state = (AccelManagerState *)s_data_subscribers; + while (state) { + *lowest_interval_us = MIN(state->sampling_interval_us, *lowest_interval_us); + *max_n_samples = MAX(state->samples_per_update, *max_n_samples); + + if (state->samples_per_update > 0) { + uint32_t us_per_update = state->samples_per_update * state->sampling_interval_us; + lowest_us_per_update = MIN(lowest_us_per_update, us_per_update); + } + state = (AccelManagerState *)state->list_node.next; + } + + if (lowest_us_per_update == UINT32_MAX) { + // No one subscribing or no one who wants updates + return 0; + } + + uint32_t num_samples = lowest_us_per_update / (*lowest_interval_us); + num_samples = MIN(num_samples, ACCEL_MAX_SAMPLES_PER_UPDATE); + + return num_samples; +} + +static void prv_setup_subsampling(uint32_t sampling_interval) { + // Setup the subsampling numerator and denominators + AccelManagerState *state = (AccelManagerState *)s_data_subscribers; + while (state) { + uint32_t interval_gcd = gcd(sampling_interval, + state->sampling_interval_us); + + // Protect against divide-by-zero if gcd returns 0 (when either input is 0) + // This can happen if the accelerometer driver is not initialized properly + if (interval_gcd == 0) { + PBL_LOG_ERR("Invalid sampling interval (sampling_interval=%" PRIu32 ", state->sampling_interval_us=%" PRIu32 "), skipping session %p", + sampling_interval, state->sampling_interval_us, state); + state = (AccelManagerState *)state->list_node.next; + continue; + } + + uint32_t numerator = sampling_interval / interval_gcd; + uint32_t denominator = state->sampling_interval_us / interval_gcd; + + PBL_LOG_DBG("set subsampling for session %p to %" PRIu32 "/%" PRIu32, + state, numerator, denominator); + subsampled_shared_circular_buffer_client_set_ratio( + &state->buffer_client, numerator, denominator); + state = (AccelManagerState *)state->list_node.next; + } +} + +//! Should be called after any change to a subscriber. Handles re-configuring +//! the accel driver to satisfy the requirements of all consumers (i.e setting +//! sampling rate and max number of samples which can be batched). If there are no +//! subscribers, chooses the lowest power configuration settings +static void prv_update_driver_config(void) { + // TODO: Add low power support + uint32_t lowest_interval_us; + uint32_t max_n_samples; + uint32_t max_batch = prv_get_sample_interval_info(&lowest_interval_us, &max_n_samples); + + // Configure the driver sampling interval and get the actual interval that the driver is going + // to use. + uint32_t interval_us = accel_set_sampling_interval(lowest_interval_us); + + prv_setup_subsampling(interval_us); + + PBL_LOG_DBG("setting accel rate:%"PRIu32", num_samples:%"PRIu32, + US_PER_SECOND / interval_us, max_batch); + + accel_set_num_samples(max_batch); +} + +static bool prv_call_data_callback(AccelManagerState *state) { + switch (state->task) { + case PebbleTask_App: + case PebbleTask_Worker: + case PebbleTask_KernelMain: { + PebbleEvent event = { + .type = PEBBLE_CALLBACK_EVENT, + .callback = { + .callback = state->data_cb_handler, + .data = state->data_cb_context, + }, + }; + + QueueHandle_t queue = pebble_task_get_to_queue(state->task); + // Note: This call may fail if the queue is full but when a new sample + // becomes available from the driver, we will retry anyway + return xQueueSendToBack(queue, &event, 0); + } + case PebbleTask_KernelBackground: + return system_task_add_callback(state->data_cb_handler, state->data_cb_context); + case PebbleTask_NewTimers: + return new_timer_add_work_callback(state->data_cb_handler, state->data_cb_context); + default: + WTF; // Unsupported task for the accel manager + } +} + +//! This is called every time new samples arrive from the accel driver & every +//! time data has been drained by the accel service. Its responsibility is +//! populating subscriber storage with new samples (at the requested sample +//! frequency) and generating a callback event on the subscriber's queue when +//! the requested number of samples have been batched +static void prv_dispatch_data(bool post_event) { + mutex_lock_recursive(s_accel_manager_mutex); + + AccelManagerState * state = (AccelManagerState *)s_data_subscribers; + while (state) { + if (!state->raw_buffer) { + state = (AccelManagerState *)state->list_node.next; + continue; + } + + // if subscribed but not looking for any samples then just drop the data + if (state->samples_per_update == 0) { + uint16_t len = shared_circular_buffer_get_read_space_remaining( + &s_buffer, &state->buffer_client.buffer_client); + shared_circular_buffer_consume( + &s_buffer, &state->buffer_client.buffer_client, len); + state = (AccelManagerState *)state->list_node.next; + continue; + } + + // If buffer has room, read more data + uint32_t samples_drained = 0; + while (state->num_samples < state->samples_per_update) { + // Read available data. + AccelManagerBufferData data; + if (!shared_circular_buffer_read_subsampled( + &s_buffer, &state->buffer_client, sizeof(data), &data, 1)) { + // we have drained all available samples + break; + } + + // Note: the accel_service currently only buffers AccelRawData (i.e it + // does not track the timestamp explicitly.) The accel service drains a + // buffers worth of data at a time and asks for the starting time + // (state->timestamp_ms) of the first sample in that buffer when it + // does. Therefore, we provide the real time for the first sample. In + // the future, we could phase out legacy accel code and provide the + // exact timestamp with every sample + if (state->num_samples == 0) { + state->timestamp_ms = s_last_empty_timestamp_ms + data.timestamp_delta_ms; + } + + memcpy(state->raw_buffer + state->num_samples, &data, + sizeof(AccelRawData)); + state->num_samples++; + samples_drained++; + } + + // If buffer is full, notify subscriber to process it + if (post_event && !state->event_posted && + state->num_samples >= state->samples_per_update) { + // Notify the subscriber that data is available + state->event_posted = prv_call_data_callback(state); + + PBL_LOG_VERBOSE("full set of %d samples for session %p", state->num_samples, state); + + if (!state->event_posted) { + PBL_LOG_INFO("Failed to post accel event to task: 0x%x", (int) state->task); + } + } + state = (AccelManagerState *)state->list_node.next; + } + + mutex_unlock_recursive(s_accel_manager_mutex); +} + +#ifdef TEST_KERNEL_SUBSCRIPTION +static void prv_kernel_data_subscription_handler(AccelData *accel_data, + uint32_t num_samples) { + PBL_LOG_INFO("Received %" PRIu32 " accel samples for KernelMain.", num_samples); +} + +static void prv_kernel_tap_subscription_handler(AccelAxisType axis, + int32_t direction) { + PBL_LOG_INFO("Received a tap event for KernelMain, axis: %d, " + "direction: %" PRId32, axis, direction); +} +#endif + +// Compute and return the device's delta position to help determine movement as idle. +static uint32_t prv_compute_delta_pos(AccelData *cur_pos, AccelData *last_pos) { + return (abs(last_pos->x - cur_pos->x) + abs(last_pos->y - cur_pos->y) + + abs(last_pos->z - cur_pos->z)); +} + +/* + * Exported APIs + */ + +void accel_manager_set_motion_backlight_enabled(bool enabled) { + mutex_lock_recursive(s_accel_manager_mutex); + if (enabled && !s_motion_backlight_subscribed) { + prv_shake_add_subscriber_cb(PebbleTask_KernelMain); + s_motion_backlight_subscribed = true; + } else if (!enabled && s_motion_backlight_subscribed) { + prv_shake_remove_subscriber_cb(PebbleTask_KernelMain); + s_motion_backlight_subscribed = false; + } + mutex_unlock_recursive(s_accel_manager_mutex); +} + +// Update the motion sensitivity based on user preference (0-100%) +// This is called by the preferences system when the user changes the setting +void accel_manager_update_sensitivity(uint8_t sensitivity_percent) { + // Sensitivity mapping: + // - sensitivity_percent: 0-100 where higher = more sensitive + // - For those that support it, this maps to the wake-up threshold + // - Lower threshold = more sensitive (triggers on smaller movements) + // - Higher threshold = less sensitive (requires larger movements to trigger) + // + // We'll map the user's percentage to a threshold multiplier: + // - 100% (most sensitive) = use Low threshold + // - 50% (medium) = use mid-range + // - 0% (least sensitive) = use High threshold + + mutex_lock_recursive(s_accel_manager_mutex); + accel_set_shake_sensitivity_percent(sensitivity_percent); + mutex_unlock_recursive(s_accel_manager_mutex); + + PBL_LOG_INFO("Motion sensitivity updated to %u percent", sensitivity_percent); +} + +void accel_manager_init(void) { + s_accel_manager_mutex = mutex_create_recursive(); + + shared_circular_buffer_init(&s_buffer, s_buffer_storage, + sizeof(s_buffer_storage)); + + event_service_init(PEBBLE_ACCEL_SHAKE_EVENT, &prv_shake_add_subscriber_cb, + &prv_shake_remove_subscriber_cb); + + event_service_init(PEBBLE_ACCEL_DOUBLE_TAP_EVENT, &prv_double_tap_add_subscriber_cb, + &prv_double_tap_remove_subscriber_cb); + + // Apply saved motion sensitivity preference for Asterix/Obelix + // Only available in normal shell (not PRF) + #if defined(CONFIG_ACCEL_SENSITIVITY) && !defined(CONFIG_RECOVERY_FW) + extern uint8_t shell_prefs_get_motion_sensitivity(void); + uint8_t saved_sensitivity = shell_prefs_get_motion_sensitivity(); + accel_manager_update_sensitivity(saved_sensitivity); + PBL_LOG_INFO("Initialized motion sensitivity to %u percent", saved_sensitivity); + #endif +} + +static void prv_copy_accel_sample_to_accel_data(AccelDriverSample const *accel_sample, + AccelData *accel_data) { + *accel_data = (AccelData) { + .x = accel_sample->x, + .y = accel_sample->y, + .z = accel_sample->z, + .timestamp /* ms */ = (accel_sample->timestamp_us / 1000), + .did_vibrate = (sys_vibe_get_vibe_strength() != VIBE_STRENGTH_OFF) + }; +} + +static void prv_update_last_accel_data(AccelDriverSample const *data) { + prv_copy_accel_sample_to_accel_data(data, &s_last_accel_data); +} + +DEFINE_SYSCALL(int, sys_accel_manager_peek, AccelData *accel_data) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(accel_data, sizeof(*accel_data)); + } + + PBL_ANALYTICS_ADD(accel_peek_count, 1); + + mutex_lock_recursive(s_accel_manager_mutex); + + AccelDriverSample data; + int result = accel_peek(&data); + if (result == 0 /* success */) { + prv_copy_accel_sample_to_accel_data(&data, accel_data); + prv_update_last_accel_data(&data); + } + + mutex_unlock_recursive(s_accel_manager_mutex); + + return result; +} + +DEFINE_SYSCALL(AccelManagerState*, sys_accel_manager_data_subscribe, + AccelSamplingRate rate, AccelDataReadyCallback data_cb, void* context, + PebbleTask handler_task) { + AccelManagerState *state; + + // `handler_task` decides where prv_call_data_callback() dispatches the + // user-supplied data_cb. For KernelMain/KernelBackground/NewTimers values + // the callback ends up invoked directly in kernel mode (either via the + // kernel main event loop's PEBBLE_CALLBACK_EVENT handler, system_task_add_callback, + // or new_timer_add_work_callback) — handing an unprivileged app arbitrary + // kernel-mode code execution. Force unprivileged callers onto their own + // task so the dispatch lands in their app/worker event loop instead. + if (PRIVILEGE_WAS_ELEVATED) { + handler_task = pebble_task_get_current(); + if (handler_task != PebbleTask_App && handler_task != PebbleTask_Worker) { + syscall_failed(); + } + } + + mutex_lock_recursive(s_accel_manager_mutex); + { + state = kernel_malloc_check(sizeof(AccelManagerState)); + *state = (AccelManagerState) { + .task = handler_task, + .data_cb_handler = data_cb, + .data_cb_context = context, + .sampling_interval_us = (US_PER_SECOND / rate), + .samples_per_update = ACCEL_MAX_SAMPLES_PER_UPDATE, + }; + + bool no_subscribers_before = (s_data_subscribers == NULL); + s_data_subscribers = list_insert_before(s_data_subscribers, &state->list_node); + if (no_subscribers_before) { + sys_vibe_history_start_collecting(); + } + + // Add as a consumer to the accel buffer + shared_circular_buffer_add_subsampled_client( + &s_buffer, &state->buffer_client, 1, 1); + + // Update the sampling rate and num samples of the driver considering the new + // subscriber's request + prv_update_driver_config(); + } + mutex_unlock_recursive(s_accel_manager_mutex); + + return state; +} + +// Several syscalls in this file take an AccelManagerState pointer that came from +// userspace. Without validation, an app can fabricate one and use the embedded +// fields (timestamp_ms, raw_buffer, list_node, ...) as kernel read/write primitives. +// Walk the authoritative kernel-side subscriber list to confirm the pointer really +// is one we handed out. The list is short (one entry per active subscriber) so the +// walk is cheap. Caller must hold s_accel_manager_mutex. +static bool prv_state_is_valid_subscriber(const AccelManagerState *state) { + if (state == NULL) { + return false; + } + for (ListNode *node = s_data_subscribers; node != NULL; node = node->next) { + if ((const AccelManagerState *)node == state) { + return true; + } + } + return false; +} + +static void prv_assert_state_from_user(const AccelManagerState *state) { + if (!PRIVILEGE_WAS_ELEVATED) { + return; + } + mutex_lock_recursive(s_accel_manager_mutex); + bool valid = prv_state_is_valid_subscriber(state); + mutex_unlock_recursive(s_accel_manager_mutex); + if (!valid) { + PBL_LOG_ERR("Rejecting unknown AccelManagerState %p from unprivileged caller", state); + syscall_failed(); + } +} + +DEFINE_SYSCALL(bool, sys_accel_manager_data_unsubscribe, AccelManagerState *state) { + prv_assert_state_from_user(state); + bool event_outstanding; + mutex_lock_recursive(s_accel_manager_mutex); + { + event_outstanding = state->event_posted; + // Remove this subscriber and free up its state variables + shared_circular_buffer_remove_subsampled_client( + &s_buffer, &state->buffer_client); + list_remove(&state->list_node, &s_data_subscribers /* &head */, NULL /* &tail */); + kernel_free(state); + + if (!s_data_subscribers) { + // If no one left using the data subscription, disable it + sys_vibe_history_stop_collecting(); + } + + // reconfig for the common subset of requirements among remaining subscribers + prv_update_driver_config(); + } + mutex_unlock_recursive(s_accel_manager_mutex); + return event_outstanding; +} + +DEFINE_SYSCALL(int, sys_accel_manager_set_sampling_rate, + AccelManagerState *state, AccelSamplingRate rate) { + prv_assert_state_from_user(state); + + // Make sure the rate is one of our externally supported fixed rates + switch (rate) { + case ACCEL_SAMPLING_10HZ: + case ACCEL_SAMPLING_25HZ: + case ACCEL_SAMPLING_50HZ: + case ACCEL_SAMPLING_100HZ: + break; + default: + return -1; + } + + mutex_lock_recursive(s_accel_manager_mutex); + + state->sampling_interval_us = (US_PER_SECOND / rate); + prv_update_driver_config(); + + mutex_unlock_recursive(s_accel_manager_mutex); + + // TODO: doesn't look like our API specifies what this routine should return. + return 0; +} + +uint32_t accel_manager_set_jitterfree_sampling_rate(AccelManagerState *state, + uint32_t min_rate_mHz) { + // HACK + // We're dumb and don't support anything other than 12.5hz for jitter-free sampling. We chose + // this rate because it divides evenly into all the native rates we support right now. + // Supporting a wider range of jitter-free rates is harder due to dealing with all the potential + // combinations of different subscribers asking for different rates. + const uint32_t ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ = 12500; + PBL_ASSERTN(min_rate_mHz <= ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ); + + mutex_lock_recursive(s_accel_manager_mutex); + + state->sampling_interval_us = (US_PER_SECOND * 1000) / ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ; + prv_update_driver_config(); + + mutex_unlock_recursive(s_accel_manager_mutex); + + return ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ; +} + +DEFINE_SYSCALL(int, sys_accel_manager_set_sample_buffer, + AccelManagerState *state, AccelRawData *buffer, uint32_t samples_per_update) { + prv_assert_state_from_user(state); + if (samples_per_update > ACCEL_MAX_SAMPLES_PER_UPDATE) { + return -1; + } + + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(buffer, samples_per_update * sizeof(AccelRawData)); + } + + mutex_lock_recursive(s_accel_manager_mutex); + { + state->raw_buffer = buffer; + state->samples_per_update = samples_per_update; + state->num_samples = 0; + prv_update_driver_config(); + } + mutex_unlock_recursive(s_accel_manager_mutex); + + return 0; +} + +DEFINE_SYSCALL(uint32_t, sys_accel_manager_get_num_samples, + AccelManagerState *state, uint64_t *timestamp_ms) { + prv_assert_state_from_user(state); + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(timestamp_ms, sizeof(*timestamp_ms)); + } + + mutex_lock_recursive(s_accel_manager_mutex); + + uint32_t result = state->num_samples; + *timestamp_ms = state->timestamp_ms; + + mutex_unlock_recursive(s_accel_manager_mutex); + return result; +} + +DEFINE_SYSCALL(bool, sys_accel_manager_consume_samples, + AccelManagerState *state, uint32_t samples) { + prv_assert_state_from_user(state); + bool success = true; + mutex_lock_recursive(s_accel_manager_mutex); + + if (samples > state->num_samples) { + PBL_LOG_ERR("Consuming more samples than exist %d vs %d!", + (int)samples, (int)state->num_samples); + success = false; + } else if (samples != state->num_samples) { + PBL_LOG_DBG("Dropping %d accel samples", (int)(state->num_samples - samples)); + success = false; + } + + state->event_posted = false; + state->num_samples = 0; + // Fill it again from circular buffer + prv_dispatch_data(state->task != pebble_task_get_current() /* post_event */); + + mutex_unlock_recursive(s_accel_manager_mutex); + return success; +} + + +/* + * TODO: APIs that still need to be implemented + */ + +void accel_manager_enable(bool on) { + mutex_lock_recursive(s_accel_manager_mutex); + bool prev = s_enabled; + s_enabled = on; + if (on && !prev) { + prv_update_driver_config(); + if (s_shake_subscribers_count > 0) { + accel_enable_shake_detection(true); + } + if (s_double_tap_subscribers_count > 0) { + accel_enable_double_tap_detection(true); + } + } else if (!on && prev) { + accel_set_num_samples(0); + accel_set_sampling_interval(0); + accel_enable_shake_detection(false); + accel_enable_double_tap_detection(false); + } + mutex_unlock_recursive(s_accel_manager_mutex); +} + +void accel_manager_exit_low_power_mode(void) { } + +// Return true if we are "idle", defined as seeing no movement in the last hour. +bool accel_is_idle(void) { + // It was idle recently, see if it's still idle. Note we avoid reading the accel hardware + // again here to keep this call as lightweight as possible. Instead we are just comparing the last + // read value with the value last captured by analytics (which does so on an hourly heartbeat). + return (prv_compute_delta_pos(&s_last_accel_data, &s_last_analytics_position) + < ACCEL_MAX_IDLE_DELTA); +} + +// The accelerometer should issue a shake/tap event with any slight movements when stationary. +// This will allow the watch to immediately return to normal mode, and attempt to reconnect to +// the phone. +void accel_enable_high_sensitivity(bool high_sensitivity) { + mutex_lock_recursive(s_accel_manager_mutex); + accel_set_shake_sensitivity_high(high_sensitivity); + mutex_unlock_recursive(s_accel_manager_mutex); +} + +/* + * Driver Callbacks - See accel.h header for more context + */ + +static bool prv_shared_buffer_empty(void) { + bool empty = true; + mutex_lock_recursive(s_accel_manager_mutex); + { + AccelManagerState *state = (AccelManagerState *)s_data_subscribers; + while (state) { + int left = shared_circular_buffer_get_read_space_remaining( + &s_buffer, &state->buffer_client.buffer_client); + if (left != 0) { + empty = false; + break; + } + state = (AccelManagerState *)state->list_node.next; + } + } + mutex_unlock_recursive(s_accel_manager_mutex); + return empty; +} + +void accel_cb_new_sample(AccelDriverSample const *data) { + if (!s_enabled) { + return; + } + + prv_update_last_accel_data(data); + + PBL_ANALYTICS_ADD(accel_sample_count, 1); + + if (!s_buffer.clients) { + return; // no clients so don't buffer any data + } + + AccelManagerBufferData accel_buffer_data; + accel_buffer_data.rawdata.x = data->x; + accel_buffer_data.rawdata.y = data->y; + accel_buffer_data.rawdata.z = data->z; + + if (prv_shared_buffer_empty()) { + s_last_empty_timestamp_ms = data->timestamp_us / 1000; + } + + // Note: the delta value overflows if the s_buffer is not drained for ~65s, + // but there should be more than enough time for it to drain in that window + accel_buffer_data.timestamp_delta_ms = ((data->timestamp_us / 1000) - + s_last_empty_timestamp_ms); + + // if we have one or more clients who fell behind reading out of the buffer, + // we will advance them until there is enough space available for the new data + bool rv = shared_circular_buffer_write(&s_buffer, (uint8_t *)&accel_buffer_data, + sizeof(accel_buffer_data), false /*advance_slackers*/); + if (!rv) { + PBL_LOG_WRN("Accel subscriber fell behind, truncating data"); + rv = shared_circular_buffer_write(&s_buffer, (uint8_t *)&accel_buffer_data, + sizeof(accel_buffer_data), true /*advance_slackers*/); + } + + PBL_ASSERTN(rv); + + prv_dispatch_data(true /* post_event */); +} + +void accel_cb_shake_detected(IMUCoordinateAxis axis, int32_t direction) { + if (!s_enabled) { + return; + } + +#if !defined(CONFIG_RECOVERY_FW) + extern bool shell_prefs_get_accel_shake_log_info_enabled(void); + if (shell_prefs_get_accel_shake_log_info_enabled()) { + PBL_LOG_INFO("Shake detected; axis=%d, direction=%" PRId32, axis, direction); + } else { + PBL_LOG_DBG("Shake detected; axis=%d, direction=%" PRId32, axis, direction); + } +#else + PBL_LOG_DBG("Shake detected; axis=%d, direction=%" PRId32, axis, direction); +#endif + + PebbleEvent e = { + .type = PEBBLE_ACCEL_SHAKE_EVENT, + .accel_tap = { + .axis = axis, + .direction = direction, + }, + }; + + event_put(&e); + + PBL_ANALYTICS_ADD(accel_shake_count, 1); +} + +void accel_cb_double_tap_detected(IMUCoordinateAxis axis, int32_t direction) { + if (!s_enabled) { + return; + } + + PebbleEvent e = { + .type = PEBBLE_ACCEL_DOUBLE_TAP_EVENT, + .accel_tap = { + .axis = axis, + .direction = direction, + }, + }; + + event_put(&e); + + PBL_ANALYTICS_ADD(accel_double_tap_count, 1); +} + +static void prv_handle_accel_driver_work_cb(void *data) { + // The accel manager is responsible for handling locking + mutex_lock_recursive(s_accel_manager_mutex); + AccelOffloadCallback cb = data; + cb(); + mutex_unlock_recursive(s_accel_manager_mutex); +} + +void accel_offload_work_from_isr(AccelOffloadCallback cb, bool *should_context_switch) { + PBL_ASSERTN(mcu_state_is_isr()); + + *should_context_switch = + new_timer_add_work_callback_from_isr(prv_handle_accel_driver_work_cb, cb); +} + +void accel_offload_work(AccelOffloadCallback cb) { + new_timer_add_work_callback(prv_handle_accel_driver_work_cb, cb); +} + +void command_accel_peek(void) { + AccelData data; + + int result = sys_accel_manager_peek(&data); + PBL_LOG_DBG("result: %d", result); + + char buffer[20]; + prompt_send_response_fmt(buffer, sizeof(buffer), "X: %"PRId16, data.x); + prompt_send_response_fmt(buffer, sizeof(buffer), "Y: %"PRId16, data.y); + prompt_send_response_fmt(buffer, sizeof(buffer), "Z: %"PRId16, data.z); +} + +void command_accel_num_samples(char *num_samples) { + int num = atoi(num_samples); + mutex_lock_recursive(s_accel_manager_mutex); + accel_set_num_samples(num); + mutex_unlock_recursive(s_accel_manager_mutex); +} + +#if UNITTEST +/* + * Helper routines strictly for unit tests + */ + +void test_accel_manager_get_subsample_info(AccelManagerState *state, uint16_t *num, uint16_t *den, + uint16_t *samps_per_update) { + *num = state->buffer_client.numerator; + *den = state->buffer_client.denominator; + *samps_per_update = state->samples_per_update; +} + +void test_accel_manager_reset(void) { + s_buffer = (SharedCircularBuffer){}; + AccelManagerState *state = (AccelManagerState *)s_data_subscribers; + while (state) { + AccelManagerState *free_state = state; + state = (AccelManagerState *)state->list_node.next; + kernel_free(free_state); + } + s_data_subscribers = NULL; + s_shake_subscribers_count = 0; + s_double_tap_subscribers_count = 0; + s_motion_backlight_subscribed = false; + s_enabled = true; +} + +#endif diff --git a/src/fw/services/accel_manager/wscript_build b/src/fw/services/accel_manager/wscript_build new file mode 100644 index 0000000000..57a0c0cb17 --- /dev/null +++ b/src/fw/services/accel_manager/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c'], + target='services_accel_manager', +) +bld.env.SERVICES.append('services_accel_manager') diff --git a/src/fw/services/activity/Kconfig b/src/fw/services/activity/Kconfig new file mode 100644 index 0000000000..69329d8992 --- /dev/null +++ b/src/fw/services/activity/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ACTIVITY + bool "Activity" + help + Activity (step/sleep/workout) tracking service. + +if SERVICE_ACTIVITY + +module = SERVICE_ACTIVITY +module-str = Activity +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/activity/activity.c b/src/fw/services/activity/activity.c similarity index 89% rename from src/fw/services/normal/activity/activity.c rename to src/fw/services/activity/activity.c index 86722d80bc..99bbd22436 100644 --- a/src/fw/services/normal/activity/activity.c +++ b/src/fw/services/activity/activity.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/data_logging.h" #include "applib/health_service.h" @@ -26,16 +13,15 @@ #include "popups/health_tracking_ui.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/battery/battery_state.h" -#include "services/common/hrm/hrm_manager_private.h" -#include "services/common/system_task.h" -#include "services/common/vibe_pattern.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/blob_db/health_db.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/protobuf_log/protobuf_log.h" -#include "services/normal/protobuf_log/protobuf_log_hr.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/hrm/hrm_manager_private.h" +#include "pbl/services/system_task.h" +#include "pbl/services/vibe_pattern.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/blob_db/health_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log_hr.h" #include "shell/prefs.h" #include "syscall/syscall.h" #include "syscall/syscall_internal.h" @@ -52,15 +38,16 @@ #include "FreeRTOS.h" #include "semphr.h" -#include "activity.h" -#include "activity_algorithm.h" -#include "activity_calculators.h" -#include "activity_insights.h" -#include "activity_private.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_calculators.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/activity_private.h" + +PBL_LOG_MODULE_DEFINE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); // Our globals static ActivityState s_activity_state; -static bool s_hrm_present = false; static bool s_activity_initialized = false; // ------------------------------------------------------------------------------------------------ @@ -74,10 +61,14 @@ ActivityState *activity_private_state(void) { // ------------------------------------------------------------------------------------------------ bool activity_is_hrm_present(void) { +#ifdef CONFIG_HRM if (!s_activity_initialized) { return false; } - return s_hrm_present; + return true; +#else + return false; +#endif } static bool prv_activity_allowed_to_be_enabled(void) { @@ -85,12 +76,37 @@ static bool prv_activity_allowed_to_be_enabled(void) { } +// ------------------------------------------------------------------------------------------------ +#ifdef CONFIG_HRM +// Get the HRM measurement period in seconds based on the user's setting +static uint32_t prv_get_hrm_period_sec(void) { + switch (activity_prefs_get_hrm_measurement_interval()) { + case HRMonitoringInterval_30Min: + return 30 * SECONDS_PER_MINUTE; + case HRMonitoringInterval_1Hour: + return SECONDS_PER_HOUR; + case HRMonitoringInterval_10Min: + default: + return 10 * SECONDS_PER_MINUTE; + } +} +#endif + // ------------------------------------------------------------------------------------------------ // If necessary, change the sampling period of our heart rate subscription // @param[in] now_ts number of seconds the system has been running (from time_get_uptime_seconds()) static void prv_heart_rate_subscription_update(uint32_t now_ts) { -#if CAPABILITY_HAS_BUILTIN_HRM - if (!s_hrm_present) { +#ifdef CONFIG_HRM + // If measurement interval is disabled, ensure we're not sampling and skip + if (activity_prefs_get_hrm_measurement_interval() == HRMonitoringInterval_Disabled) { + if (s_activity_state.hr.currently_sampling) { + bool success = sys_hrm_manager_set_update_interval(s_activity_state.hr.hrm_session, + ACTIVITY_HRM_SUBSCRIPTION_OFF_PERIOD_SEC, + 0 /*expire_sec*/); + PBL_ASSERTN(success); + s_activity_state.hr.currently_sampling = false; + s_activity_state.hr.toggled_sampling_at_ts = now_ts; + } return; } @@ -100,18 +116,19 @@ static void prv_heart_rate_subscription_update(uint32_t now_ts) { if (s_activity_state.hr.currently_sampling) { // If we are currently sampling, turn off when: // - We reach the end of our maximum time on, ACTIVITY_DEFAULT_HR_ON_TIME_SEC - // - We get ACTIVITY_MIN_NUM_SAMPLES_SHORT_CIRCUIT samples before the time runs out - // - e.g. We get X samples >= ACTIVITY_MIN_HR_QUALITY_THRESH in our current minute, - // go ahead and turn off the sensor + // - We get ACTIVITY_MIN_NUM_GOOD_SAMPLES_SHORT_CIRCUIT good quality samples + // - We get ACTIVITY_MIN_NUM_EXCELLENT_SAMPLES_SHORT_CIRCUIT excellent quality samples const uint32_t turn_off_at = last_toggled_ts + ACTIVITY_DEFAULT_HR_ON_TIME_SEC; - const bool samples_req_met = - (s_activity_state.hr.num_quality_samples >= ACTIVITY_MIN_NUM_SAMPLES_SHORT_CIRCUIT); - if ((turn_off_at <= now_ts) || samples_req_met) { + const bool good_samples_req_met = + (s_activity_state.hr.num_good_quality_samples >= ACTIVITY_MIN_NUM_GOOD_SAMPLES_SHORT_CIRCUIT); + const bool excellent_samples_req_met = + (s_activity_state.hr.num_excellent_samples >= ACTIVITY_MIN_NUM_EXCELLENT_SAMPLES_SHORT_CIRCUIT); + if ((turn_off_at <= now_ts) || good_samples_req_met || excellent_samples_req_met) { should_toggle = true; } } else { - // If we are not currently sampling, turn on after ACTIVITY_DEFAULT_HR_PERIOD_SEC - const uint32_t turn_on_at = last_toggled_ts + ACTIVITY_DEFAULT_HR_PERIOD_SEC; + // If we are not currently sampling, turn on after the configured period + const uint32_t turn_on_at = last_toggled_ts + prv_get_hrm_period_sec(); if (turn_on_at <= now_ts) { should_toggle = true; } @@ -125,7 +142,7 @@ static void prv_heart_rate_subscription_update(uint32_t now_ts) { const bool should_be_sampling = !s_activity_state.hr.currently_sampling && !watch_is_flat; if (!s_activity_state.hr.currently_sampling && watch_is_flat) { - PBL_LOG(LOG_LEVEL_INFO, "Not subscribing to HRM: watch is flat(ish)"); + PBL_LOG_INFO("Not subscribing to HRM: watch is flat(ish)"); } // Pick the subscription rate (essentially ON and OFF) @@ -139,20 +156,16 @@ static void prv_heart_rate_subscription_update(uint32_t now_ts) { // Update history s_activity_state.hr.currently_sampling = should_be_sampling; s_activity_state.hr.toggled_sampling_at_ts = now_ts; - PBL_LOG(LOG_LEVEL_DEBUG, "Changed HR sampling period to %"PRIu32" sec", desired_interval_sec); + PBL_LOG_DBG("Changed HR sampling period to %"PRIu32" sec", desired_interval_sec); } -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM } // ------------------------------------------------------------------------------------------------ // Kernel BG callback called by the Heart Rate Manager when new data arrives -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM T_STATIC void prv_hrm_subscription_cb(PebbleHRMEvent *hrm_event, void *context) { - if (!s_hrm_present) { - return; - } - ACTIVITY_LOG_DEBUG("Got HR event: %d", (int) hrm_event->event_type); if (hrm_event->event_type == HRMEvent_BPM) { ACTIVITY_LOG_DEBUG("HR bpm: %"PRIu8", qual: %"PRId8" ", hrm_event->bpm.bpm, @@ -168,9 +181,15 @@ T_STATIC void prv_hrm_subscription_cb(PebbleHRMEvent *hrm_event, void *context) } uint32_t now_uptime_ts = time_get_uptime_seconds(); + time_t now_utc = rtc_get_time(); + + // Cache the worn-status from this event so sleep tracking can use it as a strong off-wrist + // signal (PPG off-wrist detection is far more reliable than the accel-only heuristics). + activity_metrics_prv_set_hrm_worn_status( + now_utc, hrm_event->bpm.quality == HRMQuality_OffWrist); + if (valid_hr_reading) { // Update the heart rate metrics - time_t now_utc = rtc_get_time(); activity_metrics_prv_add_median_hr_sample(hrm_event, now_utc, now_uptime_ts); // Log it to the mobile @@ -206,18 +225,13 @@ T_STATIC void prv_hrm_subscription_cb(PebbleHRMEvent *hrm_event, void *context) prv_heart_rate_subscription_update(now_uptime_ts); } } -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM // --------------------------------------------------------------------------------------- // Init heart rate support static void prv_heart_rate_init(void) { -#if CAPABILITY_HAS_BUILTIN_HRM - s_hrm_present = mfg_info_is_hrm_present(); - if (!s_hrm_present) { - return; - } - +#ifdef CONFIG_HRM // Subscribe to HRM data s_activity_state.hr.currently_sampling = false; s_activity_state.hr.toggled_sampling_at_ts = time_get_uptime_seconds(); @@ -228,22 +242,18 @@ static void prv_heart_rate_init(void) { s_activity_state.hr.log_session = protobuf_log_hr_create(NULL); PBL_ASSERTN(s_activity_state.hr.log_session != NULL); -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM } // --------------------------------------------------------------------------------------- // De-init heart rate support static void prv_heart_rate_deinit(void) { -#if CAPABILITY_HAS_BUILTIN_HRM - if (!s_hrm_present) { - return; - } - +#ifdef CONFIG_HRM sys_hrm_manager_unsubscribe(s_activity_state.hr.hrm_session); protobuf_log_session_delete(s_activity_state.hr.log_session); activity_metrics_prv_reset_hr_stats(); -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM } @@ -255,7 +265,7 @@ SettingsFile *activity_private_settings_open(void) { if (settings_file_open(file, ACTIVITY_SETTINGS_FILE_NAME, ACTIVITY_SETTINGS_FILE_LEN) != S_SUCCESS) { kernel_free(file); - PBL_LOG(LOG_LEVEL_ERROR, "No settings file"); + PBL_LOG_ERR("No settings file"); return NULL; } return file; @@ -276,7 +286,7 @@ void activity_private_settings_close(SettingsFile *file) { static void prv_settings_rewrite_cb(SettingsFile *old_file, SettingsFile *new_file, SettingsRecordInfo *info, void *context) { if (info->key_len != sizeof(ActivitySettingsKey)) { - PBL_LOG(LOG_LEVEL_WARNING, "Unexpected key len: %"PRIu32" ", (uint32_t)info->key_len); + PBL_LOG_WRN("Unexpected key len: %"PRIu32" ", (uint32_t)info->key_len); return; } @@ -317,7 +327,7 @@ static SettingsFile *prv_settings_migrate(SettingsFile *file, uint16_t *written_ return file; } - PBL_LOG(LOG_LEVEL_INFO, "Performing settings file migration from verison %"PRIu16"", version); + PBL_LOG_INFO("Performing settings file migration from verison %"PRIu16"", version); // Perform migration if (version == 1) { @@ -325,11 +335,11 @@ static SettingsFile *prv_settings_migrate(SettingsFile *file, uint16_t *written_ // size is different. We need to re-create it using the new, bigger size. result = settings_file_rewrite(file, prv_settings_rewrite_cb, NULL); if (result != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Failure %"PRIi32" while re-writing setting file", (int32_t)result); + PBL_LOG_ERR("Failure %"PRIi32" while re-writing setting file", (int32_t)result); } } else { // If the version is totally unexpected, remove the file and create a new one - PBL_LOG(LOG_LEVEL_ERROR, "Unknown settings file verison %"PRIu16"", version); + PBL_LOG_ERR("Unknown settings file verison %"PRIu16"", version); } if (result != S_SUCCESS) { @@ -447,7 +457,7 @@ static void NOINLINE prv_process_minute_data_tail(time_t utc_sec) { // Send the history update event now if history has changed if (need_history_update_event) { - PBL_LOG(LOG_LEVEL_DEBUG, "Sending history update event"); + PBL_LOG_DBG("Sending history update event"); PebbleEvent e = { .type = PEBBLE_HEALTH_SERVICE_EVENT, .health_event = { @@ -545,7 +555,7 @@ static void prv_collect_raw_samples(AccelRawData *accel_data, uint32_t num_sampl DlsSystemTagActivityAccelSamples, DATA_LOGGING_BYTE_ARRAY, sizeof(ActivityRawSamplesRecord), true /*buffered*/, false /*resume*/, &system_uuid); if (data->dls_session == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Unable to create DLS session"); + PBL_LOG_ERR("Unable to create DLS session"); return; } } @@ -620,7 +630,7 @@ static void prv_collect_raw_samples(AccelRawData *accel_data, uint32_t num_sampl } DataLoggingResult result = dls_log(data->dls_session, &data->record, 1); if (result != DATA_LOGGING_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "Error %"PRIi32" while logging raw sample data", + PBL_LOG_WRN("Error %"PRIi32" while logging raw sample data", (int32_t)result); } @@ -742,7 +752,7 @@ static void prv_stop_tracking_early(void) { s_activity_state.update_settings_counter = -1; prv_update_storage(rtc_get_time()); - PBL_LOG(LOG_LEVEL_DEBUG, "Updated and persisted sessions before stopping activity tracking"); + PBL_LOG_DBG("Updated and persisted sessions before stopping activity tracking"); } @@ -760,7 +770,7 @@ static void prv_start_tracking_cb(void *context) { AccelSamplingRate sampling_rate; if (activity_algorithm_init(&sampling_rate)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting activity tracking..."); + PBL_LOG_DBG("Starting activity tracking..."); // Subscribe to the accelerometer from KernelBG s_activity_state.test_mode = test_mode; @@ -789,7 +799,7 @@ static void prv_start_tracking_cb(void *context) { // Register our minutes callback cron_job_schedule(&s_activity_job); s_activity_state.started = true; - PBL_LOG(LOG_LEVEL_INFO, "Activity tracking started"); + PBL_LOG_INFO("Activity tracking started"); PebbleEvent event = { .type = PEBBLE_ACTIVITY_EVENT, @@ -822,7 +832,7 @@ static void prv_stop_tracking_cb(void *context) { PBL_ASSERTN(activity_algorithm_deinit()); s_activity_state.started = false; - PBL_LOG(LOG_LEVEL_INFO, "activity tracking stopped"); + PBL_LOG_INFO("activity tracking stopped"); PebbleEvent event = { .type = PEBBLE_ACTIVITY_EVENT, @@ -869,15 +879,20 @@ static void prv_set_enable_cb(void *context) { } static void prv_handle_activity_enabled_change(void) { - if (activity_tracking_on() && !prv_activity_allowed_to_be_enabled()) { + // Hold the activity mutex across the started/allowed check and prv_stop_tracking_early so we + // can't race with prv_set_enable_cb on KernelBG, which can call activity_algorithm_deinit() and + // free s_alg_state out from under an in-flight activity_algorithm_early_deinit(). + mutex_lock_recursive(s_activity_state.mutex); + if (s_activity_state.started && !prv_activity_allowed_to_be_enabled()) { prv_stop_tracking_early(); } + mutex_unlock_recursive(s_activity_state.mutex); system_task_add_callback(prv_set_enable_cb, NULL); } static void prv_charger_event_cb(PebbleEvent *e, void *context) { -#if !IS_BIGBOARD +#ifndef CONFIG_IS_BIGBOARD // Since bigboards are usually plugged in, don't react to a battery connection event const PebbleBatteryStateChangeEvent *evt = &e->battery_state; mutex_lock_recursive(s_activity_state.mutex); @@ -995,7 +1010,7 @@ bool activity_init(void) { .handler = prv_charger_event_cb, }; event_service_client_subscribe(&s_activity_state.charger_subscription); -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD s_activity_state.enabled_charging_state = true; #else s_activity_state.enabled_charging_state = !battery_is_usb_connected(); @@ -1089,12 +1104,27 @@ bool activity_get_sessions(uint32_t *session_entries, ActivitySession *sessions) DEFINE_SYSCALL(bool, sys_activity_get_sessions, uint32_t *session_entries, ActivitySession *sessions) { if (PRIVILEGE_WAS_ELEVATED) { - if (session_entries) { - syscall_assert_userspace_buffer(session_entries, sizeof(*session_entries)); + if (!session_entries) { + // The implementation dereferences session_entries unconditionally. + return false; + } + syscall_assert_userspace_buffer(session_entries, sizeof(*session_entries)); + // Same pattern as sys_activity_get_minute_history: snapshot *session_entries + // so the inner activity_get_sessions can't race us into a larger memcpy than + // we validated, and reject sizes whose `requested * sizeof(*sessions)` would + // wrap and let a much-smaller validated buffer gate a much-larger write. + const uint32_t requested = *session_entries; + if (requested > SIZE_MAX / sizeof(*sessions)) { + PBL_LOG_ERR("session_entries=%" PRIu32 " would overflow size", requested); + syscall_failed(); } if (sessions) { - syscall_assert_userspace_buffer(sessions, sizeof(*sessions) * (*session_entries)); + syscall_assert_userspace_buffer(sessions, requested * sizeof(*sessions)); } + uint32_t entries_inout = requested; + bool result = activity_get_sessions(&entries_inout, sessions); + *session_entries = entries_inout; + return result; } return activity_get_sessions(session_entries, sessions); @@ -1163,7 +1193,24 @@ DEFINE_SYSCALL(bool, sys_activity_get_minute_history, HealthMinuteData *minute_d if (PRIVILEGE_WAS_ELEVATED) { syscall_assert_userspace_buffer(utc_start, sizeof(*utc_start)); syscall_assert_userspace_buffer(num_records, sizeof(*num_records)); - syscall_assert_userspace_buffer(minute_data, *num_records * sizeof(HealthMinuteData)); + // Snapshot the requested count to a kernel-stack local so that: + // - the size computation below can't be tricked by an app racing the + // KernelBG callback to swap *num_records after we validated it (TOCTOU); + // - we can bound the multiplication against SIZE_MAX before performing it + // (otherwise `*num_records * sizeof(HealthMinuteData)` wraps in uint32_t + // and a tiny validated size would gate a much larger kernel write). + const uint32_t requested = *num_records; + if (requested > SIZE_MAX / sizeof(HealthMinuteData)) { + PBL_LOG_ERR("num_records=%" PRIu32 " would overflow size", requested); + syscall_failed(); + } + syscall_assert_userspace_buffer(minute_data, requested * sizeof(HealthMinuteData)); + uint32_t records_inout = requested; + time_t start_inout = *utc_start; + bool result = activity_get_minute_history(minute_data, &records_inout, &start_inout); + *num_records = records_inout; + *utc_start = start_inout; + return result; } return activity_get_minute_history(minute_data, num_records, utc_start); @@ -1298,7 +1345,7 @@ bool activity_test_feed_samples(AccelRawData *data, uint32_t num_samples) { return false; } if (!s_activity_state.test_mode) { - PBL_LOG(LOG_LEVEL_ERROR, "not in test mode"); + PBL_LOG_ERR("not in test mode"); return false; } @@ -1315,7 +1362,7 @@ bool activity_test_feed_samples(AccelRawData *data, uint32_t num_samples) { uint16_t req_size = sizeof(ActivityFeedSamples) + chunk_size * sizeof(AccelRawData); ActivityFeedSamples *context = kernel_malloc(req_size); if (!context) { - PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory"); + PBL_LOG_ERR("Not enough memory"); return false; } diff --git a/src/fw/services/normal/activity/activity_calculators.c b/src/fw/services/activity/activity_calculators.c similarity index 90% rename from src/fw/services/normal/activity/activity_calculators.c rename to src/fw/services/activity/activity_calculators.c index 38a51ab238..7857d61e66 100644 --- a/src/fw/services/normal/activity/activity_calculators.c +++ b/src/fw/services/activity/activity_calculators.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "activity_calculators.h" - -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/activity/activity_calculators.h" + +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_private.h" #include "util/units.h" #include diff --git a/src/fw/services/normal/activity/activity_insights.c b/src/fw/services/activity/activity_insights.c similarity index 88% rename from src/fw/services/normal/activity/activity_insights.c rename to src/fw/services/activity/activity_insights.c index 66dfd51a59..98e0a717cc 100644 --- a/src/fw/services/normal/activity/activity_insights.c +++ b/src/fw/services/activity/activity_insights.c @@ -1,42 +1,28 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "activity_insights.h" - -#include "activity.h" -#include "activity_private.h" -#include "insights_settings.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/activity/activity_insights.h" + +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/insights_settings.h" #include "applib/event_service_client.h" #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "resource/resource_ids.auto.h" #include "resource/timeline_resource_ids.auto.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/common/system_task.h" -#include "services/normal/activity/health_util.h" -#include "services/normal/activity/hr_util.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/timeline/health_layout.h" -#include "services/normal/timeline/metricgroup.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/weather_layout.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/system_task.h" +#include "pbl/services/activity/health_util.h" +#include "pbl/services/activity/hr_util.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/timeline/health_layout.h" +#include "pbl/services/timeline/metricgroup.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/weather_layout.h" #include "shell/prefs.h" #include "shell/system_app_ids.auto.h" #include "system/logging.h" @@ -51,8 +37,10 @@ #include +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); + #define INSIGHTS_LOG_DEBUG(fmt, args...) \ - PBL_LOG_D(LOG_DOMAIN_ACTIVITY_INSIGHTS, LOG_LEVEL_DEBUG, fmt, ## args) + PBL_LOG_D_DBG(LOG_DOMAIN_ACTIVITY_INSIGHTS, fmt, ## args) #define SUBTITLE_BUFFER_LENGTH 18 #define TIME_BUFFER_LENGTH 9 @@ -60,16 +48,6 @@ #define NUM_COPY_VARIANTS 5 #define VARIANT_RANDOM (-1) -typedef TimelineItem *(*ActivationDelayInsightTrigger)(time_t notif_time); - -typedef struct { - int day_lag; - int hour; - int minute; - ActivationDelayInsightTrigger trigger; - ActivityInsightType insight_type; -} ActivationDelayInsight; - typedef struct NotificationConfig { time_t notif_time; ActivitySession *session; @@ -652,7 +630,6 @@ static bool prv_push_summary_pin(time_t pin_time_utc, time_t now_utc, Uuid *exis static void prv_push_reward(time_t now_utc, const RewardNotifConfig *notif_config) { prv_push_reward_notification(now_utc, notif_config); notif_config->state->last_triggered_utc = time_util_get_midnight_of(now_utc); - analytics_event_health_insight_created(now_utc, notif_config->insight_type, 0); // Save out the trigger time prv_save_state(notif_config->settings_key, @@ -720,7 +697,7 @@ static bool prv_validate_history_stats(const ActivityInsightMetricHistoryStats * // We want to look at the x days before today (which is always index 0), so add 1 uint32_t history_len = insight_settings->reward.target_qualifying_days + 1; if (history_len > ACTIVITY_HISTORY_DAYS) { - PBL_LOG(LOG_LEVEL_ERROR, "Insight qualifying history length is too long: %"PRIu32, + PBL_LOG_ERR("Insight qualifying history length is too long: %"PRIu32, history_len); return false; } @@ -744,231 +721,7 @@ static bool prv_validate_history_stats(const ActivityInsightMetricHistoryStats * return true; } -// ------------------------------------------------------------------------------------------------ -static TimelineItem *prv_create_day_1_insight(time_t notif_time) { - if (activity_prefs_get_health_app_opened_version() != 0) { - // The user already knows about the Health app - return NULL; - } - - AttributeList notif_attr_list = {0}; - const char *body = i18n_get("Wanna know more about you? " - "Track your activity and sleep with Pebble Health.", - ¬if_attr_list); - prv_build_notification_attr_list(¬if_attr_list, body, TIMELINE_RESOURCE_ACTIVITY, - ActivityInsightType_Day1, ActivitySessionType_None); - - AttributeList dismiss_action_attr_list = {0}; - attribute_list_add_cstring(&dismiss_action_attr_list, AttributeIdTitle, - i18n_get("Dismiss", ¬if_attr_list)); - - AttributeList open_app_action_attr_list = {0}; - prv_set_open_app_action(&open_app_action_attr_list, HealthCardType_Activity, ¬if_attr_list); - - const int num_actions = 2; - TimelineItemActionGroup action_group = { - .num_actions = num_actions, - .actions = (TimelineItemAction[]) { - { - .id = 0, - .type = TimelineItemActionTypeDismiss, - .attr_list = dismiss_action_attr_list, - }, - { - .id = 1, - .type = TimelineItemActionTypeOpenWatchApp, - .attr_list = open_app_action_attr_list, - } - }, - }; - - // Note: it's fine if this returns null, since the parent functions will check for a null pointer - TimelineItem *item = timeline_item_create_with_attributes(notif_time, 0, - TimelineItemTypeNotification, - LayoutIdNotification, ¬if_attr_list, - &action_group); - i18n_free_all(¬if_attr_list); - attribute_list_destroy_list(¬if_attr_list); - attribute_list_destroy_list(&dismiss_action_attr_list); - attribute_list_destroy_list(&open_app_action_attr_list); - return item; -} - -// ------------------------------------------------------------------------------------------------ -static TimelineItem *prv_create_day_4_insight(time_t notif_time) { - if (activity_prefs_get_health_app_opened_version() == 0) { - // The user has not enabled pebble health - return NULL; - } - - AttributeList notif_attr_list = {0}; - const char *body = i18n_get("You like sleep, don't you? " - "Get fun stats on your sleep and start waking " - "up less groggy with Pebble Health.", - ¬if_attr_list); - prv_build_notification_attr_list(¬if_attr_list, body, TIMELINE_RESOURCE_ACTIVITY, - ActivityInsightType_Day4, ActivitySessionType_None); - - AttributeList dismiss_action_attr_list = {0}; - attribute_list_add_cstring(&dismiss_action_attr_list, AttributeIdTitle, - i18n_get("Dismiss", ¬if_attr_list)); - - AttributeList open_app_action_attr_list = {0}; - prv_set_open_app_action(&open_app_action_attr_list, - HealthCardType_Sleep, - ¬if_attr_list); - - const int num_actions = 2; - TimelineItemActionGroup action_group = { - .num_actions = num_actions, - .actions = (TimelineItemAction[]) { - { - .id = 0, - .type = TimelineItemActionTypeDismiss, - .attr_list = dismiss_action_attr_list, - }, - { - .id = 1, - .type = TimelineItemActionTypeOpenWatchApp, - .attr_list = open_app_action_attr_list, - } - }, - }; - - // Note: it's fine if this returns null, since the parent functions will check for a null pointer - TimelineItem *item = timeline_item_create_with_attributes(notif_time, 0, - TimelineItemTypeNotification, - LayoutIdNotification, ¬if_attr_list, - &action_group); - i18n_free_all(¬if_attr_list); - attribute_list_destroy_list(¬if_attr_list); - attribute_list_destroy_list(&dismiss_action_attr_list); - attribute_list_destroy_list(&open_app_action_attr_list); - - return item; -} - -// ------------------------------------------------------------------------------------------------ -static TimelineItem *prv_create_day_10_insight(time_t notif_time) { - AttributeList notif_attr_list = {0}; - const char *body = i18n_get("Wish you could get daily updates on your " - "activity and sleep progress? You can! " - "Check out your stats with Pebble Health.", - ¬if_attr_list); - prv_build_notification_attr_list(¬if_attr_list, body, TIMELINE_RESOURCE_ACTIVITY, - ActivityInsightType_Day10, ActivitySessionType_None); - - AttributeList dismiss_action_attr_list = {0}; - attribute_list_add_cstring(&dismiss_action_attr_list, AttributeIdTitle, - i18n_get("Dismiss", ¬if_attr_list)); - - AttributeList open_app_action_attr_list = {0}; - prv_set_open_app_action(&open_app_action_attr_list, - HealthCardType_Activity, - ¬if_attr_list); - - const int num_actions = 2; - TimelineItemActionGroup action_group = { - .num_actions = num_actions, - .actions = (TimelineItemAction[]) { - { - .id = 0, - .type = TimelineItemActionTypeDismiss, - .attr_list = dismiss_action_attr_list, - }, - { - .id = 1, - .type = TimelineItemActionTypeOpenWatchApp, - .attr_list = open_app_action_attr_list, - } - }, - }; - - // Note: it's fine if this returns null, since the parent functions will check for a null pointer - TimelineItem *item = timeline_item_create_with_attributes(notif_time, 0, - TimelineItemTypeNotification, - LayoutIdNotification, ¬if_attr_list, - &action_group); - i18n_free_all(¬if_attr_list); - attribute_list_destroy_list(¬if_attr_list); - attribute_list_destroy_list(&dismiss_action_attr_list); - attribute_list_destroy_list(&open_app_action_attr_list); - - return item; -} - -// ------------------------------------------------------------------------------------------------ -static const ActivationDelayInsight ACTIVATION_DELAY_INSIGHTS[ActivationDelayInsightTypeCount] = { - [ActivationDelayInsightType_Day1] = { - // https://pebbletechnology.atlassian.net/browse/PBL-28580 - .day_lag = 1, - .hour = 18, - .minute = 0, - .trigger = prv_create_day_1_insight, - .insight_type = ActivityInsightType_Day1, - }, - [ActivationDelayInsightType_Day4] = { - // https://pebbletechnology.atlassian.net/browse/PBL-28581 - .day_lag = 4, - .hour = 20, - .minute = 30, - .trigger = prv_create_day_4_insight, - .insight_type = ActivityInsightType_Day4, - }, - [ActivationDelayInsightType_Day10] = { - // https://pebbletechnology.atlassian.net/browse/PBL-28582 - .day_lag = 10, - .hour = 20, - .minute = 30, - .trigger = prv_create_day_10_insight, - .insight_type = ActivityInsightType_Day10, - }, -}; - -// ------------------------------------------------------------------------------------------------ -static void prv_trigger_activation_delay_insight(time_t now_utc, - const ActivationDelayInsight *insight) { - TimelineItem *item = insight->trigger(now_utc); - - if (item) { - analytics_event_health_insight_created(now_utc, insight->insight_type, 0); - prv_push_notification(item, NULL); - } -} - -// ------------------------------------------------------------------------------------------------ -static NOINLINE void prv_do_activation_delay_insights(time_t now_utc) { - // Only needs to be checked every 15 minutes - struct tm tm_now; - gmtime_r(&now_utc, &tm_now); - if ((tm_now.tm_min % 15) != 0) { - return; - } - - time_t activation_time_utc = activity_prefs_get_activation_time(); - if (activation_time_utc == 0) { - return; - } - - time_t activation_time = time_utc_to_local(activation_time_utc); - time_t now = time_utc_to_local(now_utc); - - for (int i = 0; i < ActivationDelayInsightTypeCount; i++) { - time_t trigger_time_t = time_util_get_midnight_of(activation_time); - trigger_time_t += (ACTIVATION_DELAY_INSIGHTS[i].day_lag * SECONDS_PER_DAY); - struct tm trigger_time; - gmtime_r(&trigger_time_t, &trigger_time); - trigger_time_t += time_util_get_seconds_until_daily_time(&trigger_time, - ACTIVATION_DELAY_INSIGHTS[i].hour, - ACTIVATION_DELAY_INSIGHTS[i].minute); - - if (!activity_prefs_has_activation_delay_insight_fired(i) && (now >= trigger_time_t)) { - prv_trigger_activation_delay_insight(now_utc, &ACTIVATION_DELAY_INSIGHTS[i]); - activity_prefs_set_activation_delay_insight_fired(i); - } - } -} // ------------------------------------------------------------------------------------------------ // This is called during init and midnight rollover in order to update our stats for the sleep @@ -1606,7 +1359,6 @@ static void prv_push_activity_summary_notification( .health_card_type = HealthCardType_Activity, }, }; - analytics_event_health_insight_created(notif_time, ActivityInsightType_ActivitySummary, tier); prv_create_and_push_notification(&config); kernel_free(body); } @@ -1710,7 +1462,6 @@ static void prv_push_sleep_summary_notification(time_t notif_time, int32_t sleep }, }; - analytics_event_health_insight_created(notif_time, ActivityInsightType_SleepSummary, tier); prv_create_and_push_notification(&config); kernel_free(body); } @@ -1849,7 +1600,7 @@ static const char* prv_get_intro_str_for_activity(ActivitySession *session) { // ------------------------------------------------------------------------------------------------ static const char *prv_get_distance_unit(void *i18n_owner) { - return health_util_get_distance_string(i18n_get("MI", i18n_owner), i18n_get("KM", i18n_owner)); + return health_util_get_distance_string(i18n_get("mi", i18n_owner), i18n_get("km", i18n_owner)); } static void prv_add_metric_duration_info(StringList *headings, int headings_buf_size, @@ -2049,7 +1800,6 @@ void activity_insights_push_activity_session_notification(time_t notif_time, }, }; - analytics_event_health_insight_created(notif_time, type, 0); prv_create_and_push_notification(&config); cleanup: @@ -2163,7 +1913,6 @@ void NOINLINE activity_insights_process_minute_data(time_t now_utc) { } prv_do_activity_summary(now_utc); - prv_do_activation_delay_insights(now_utc); prv_process_activity_sessions(now_utc); } @@ -2358,13 +2107,6 @@ static void prv_test_push_rewards(void *unused) { } } -static void prv_test_push_day_insights(void *unused) { - time_t now_utc = rtc_get_time(); - for (int i = 0; i < ActivationDelayInsightTypeCount; i++) { - prv_trigger_activation_delay_insight(now_utc, &ACTIVATION_DELAY_INSIGHTS[i]); - } -} - static void prv_test_push_walk_run_session(void *unused) { const time_t now_utc = rtc_get_time(); ActivitySession walk_session = { @@ -2441,10 +2183,6 @@ void activity_insights_test_push_rewards(void) { system_task_add_callback(prv_test_push_rewards, NULL); } -void activity_insights_test_push_day_insights(void) { - system_task_add_callback(prv_test_push_day_insights, NULL); -} - void activity_insights_test_push_walk_run_sessions(void) { system_task_add_callback(prv_test_push_walk_run_session, NULL); } diff --git a/src/fw/services/normal/activity/activity_metrics.c b/src/fw/services/activity/activity_metrics.c similarity index 92% rename from src/fw/services/normal/activity/activity_metrics.c rename to src/fw/services/activity/activity_metrics.c index f26b5d4c24..4fc28ef26f 100644 --- a/src/fw/services/normal/activity/activity_metrics.c +++ b/src/fw/services/activity/activity_metrics.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/data_logging.h" #include "applib/health_service.h" @@ -20,9 +7,7 @@ #include "kernel/pbl_malloc.h" #include "os/mutex.h" #include "os/tick.h" -#include "popups/health_tracking_ui.h" -#include "services/common/analytics/analytics_event.h" -#include "services/normal/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log.h" #include "syscall/syscall.h" #include "syscall/syscall_internal.h" #include "system/hexdump.h" @@ -34,11 +19,13 @@ #include "util/stats.h" #include "util/units.h" -#include "activity.h" -#include "activity_algorithm.h" -#include "activity_calculators.h" -#include "activity_insights.h" -#include "activity_private.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_calculators.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/activity_private.h" + +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); // --------------------------------------------------------------------------------------- @@ -254,7 +241,7 @@ void activity_metrics_prv_set_metric(ActivityMetric metric, DayInWeek wday, int3 // We use NOINLINE to reduce the stack requirements during the minute handler (see PBL-38130) static void NOINLINE prv_shift_history(time_t utc_now) { ActivityState *state = activity_private_state(); - PBL_LOG(LOG_LEVEL_INFO, "resetting metrics for new day"); + PBL_LOG_INFO("resetting metrics for new day"); mutex_lock_recursive(state->mutex); { SettingsFile *file = activity_private_settings_open(); @@ -544,9 +531,7 @@ static uint8_t prv_get_hr_quality_weight(HRMQuality quality) { HRMQuality quality; uint8_t weight_x100; } s_hr_quality_weights_x100[] = { - {HRMQuality_NoAccel, 0 }, {HRMQuality_OffWrist, 0 }, - {HRMQuality_NoSignal, 0 }, {HRMQuality_Worst, 1 }, {HRMQuality_Poor, 1 }, {HRMQuality_Acceptable, 60 }, @@ -588,7 +573,8 @@ void activity_metrics_prv_reset_hr_stats(void) { mutex_lock_recursive(state->mutex); { state->hr.num_samples = 0; - state->hr.num_quality_samples = 0; + state->hr.num_good_quality_samples = 0; + state->hr.num_excellent_samples = 0; memset(state->hr.samples, 0, sizeof(state->hr.samples)); memset(state->hr.weights, 0, sizeof(state->hr.weights)); @@ -598,6 +584,33 @@ void activity_metrics_prv_reset_hr_stats(void) { mutex_unlock_recursive(state->mutex); } +// -------------------------------------------------------------------------------------------- +void activity_metrics_prv_set_hrm_worn_status(time_t now_utc, bool is_offwrist) { + ActivityState *state = activity_private_state(); + mutex_lock_recursive(state->mutex); + { + state->hr.last_quality_event_utc = now_utc; + state->hr.last_quality_was_offwrist = is_offwrist; + } + mutex_unlock_recursive(state->mutex); +} + +// -------------------------------------------------------------------------------------------- +bool activity_metrics_prv_is_hrm_offwrist(time_t now_utc) { + ActivityState *state = activity_private_state(); + bool offwrist = false; + mutex_lock_recursive(state->mutex); + { + if (state->hr.last_quality_event_utc != 0 && + state->hr.last_quality_was_offwrist && + (now_utc - state->hr.last_quality_event_utc) <= ACTIVITY_HRM_OFFWRIST_STALE_SEC) { + offwrist = true; + } + } + mutex_unlock_recursive(state->mutex); + return offwrist; +} + // -------------------------------------------------------------------------------------------- void activity_metrics_prv_add_median_hr_sample(PebbleHRMEvent *hrm_event, time_t now_utc, time_t now_uptime) { @@ -612,8 +625,11 @@ void activity_metrics_prv_add_median_hr_sample(PebbleHRMEvent *hrm_event, time_t state->hr.samples[state->hr.num_samples] = hrm_event->bpm.bpm; state->hr.weights[state->hr.num_samples] = prv_get_hr_quality_weight(hrm_event->bpm.quality); - if (hrm_event->bpm.quality >= ACTIVITY_MIN_HR_QUALITY_THRESH) { - state->hr.num_quality_samples++; + if (hrm_event->bpm.quality >= HRMQuality_Good) { + state->hr.num_good_quality_samples++; + } + if (hrm_event->bpm.quality >= HRMQuality_Excellent) { + state->hr.num_excellent_samples++; } state->hr.num_samples++; @@ -715,10 +731,6 @@ bool activity_get_metric(ActivityMetric metric, uint32_t history_len, int32_t *h mutex_lock_recursive(state->mutex); { - if (!activity_prefs_tracking_is_enabled() && pebble_task_get_current() == PebbleTask_App) { - health_tracking_ui_app_show_disabled(); - } - // Update derived metrics prv_update_real_time_derived_metrics(); @@ -750,7 +762,7 @@ bool activity_get_metric(ActivityMetric metric, uint32_t history_len, int32_t *h ActivitySettingsValueHistory setting_history = {}; SettingsFile *file = activity_private_settings_open(); if (!file) { - PBL_LOG(LOG_LEVEL_ERROR, "Settings file DNE. No need to continue getting metric"); + PBL_LOG_ERR("Settings file DNE. No need to continue getting metric"); success = false; goto unlock; } @@ -774,6 +786,17 @@ bool activity_get_metric(ActivityMetric metric, uint32_t history_len, int32_t *h DEFINE_SYSCALL(bool, sys_activity_get_metric, ActivityMetric metric, uint32_t history_len, int32_t *history) { if (PRIVILEGE_WAS_ELEVATED) { + // activity_get_metric() unconditionally writes history_len int32_t entries + // to `history` before later clamping to ACTIVITY_HISTORY_DAYS. A huge + // history_len makes `history_len * sizeof(*history)` wrap to a tiny size + // that passes validation while the kernel keeps writing -1 past the end + // (effectively an arbitrary-address kernel write). + // + // We never read more than ACTIVITY_HISTORY_DAYS entries anyway, so clamp + // the count here and validate against the clamped value. + if (history_len > ACTIVITY_HISTORY_DAYS) { + history_len = ACTIVITY_HISTORY_DAYS; + } if (history) { syscall_assert_userspace_buffer(history, history_len * sizeof(*history)); } diff --git a/src/fw/services/normal/activity/activity_sessions.c b/src/fw/services/activity/activity_sessions.c similarity index 93% rename from src/fw/services/normal/activity/activity_sessions.c rename to src/fw/services/activity/activity_sessions.c index e3e856b176..529d7902dd 100644 --- a/src/fw/services/normal/activity/activity_sessions.c +++ b/src/fw/services/activity/activity_sessions.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/data_logging.h" #include "applib/health_service.h" @@ -20,19 +7,21 @@ #include "kernel/pbl_malloc.h" #include "os/mutex.h" #include "os/tick.h" -#include "services/common/analytics/analytics_event.h" -#include "services/normal/alarms/alarm.h" +#include "pbl/services/alarms/alarm.h" #include "syscall/syscall.h" #include "syscall/syscall_internal.h" +#include "system/logging.h" #include "system/passert.h" #include "util/size.h" #include -#include "activity.h" -#include "activity_algorithm.h" -#include "activity_insights.h" -#include "activity_private.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/activity_private.h" + +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); // ------------------------------------------------------------------------------------ @@ -127,19 +116,19 @@ static bool prv_is_valid_activity_session(ActivitySession *session) { break; case ActivitySessionType_None: case ActivitySessionTypeCount: - PBL_LOG(LOG_LEVEL_WARNING, "Invalid activity type: %d", (int)session->type); + PBL_LOG_WRN("Invalid activity type: %d", (int)session->type); return false; } // The length must be reasonable if (session->length_min > ACTIVITY_SESSION_MAX_LENGTH_MIN) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid duration: %"PRIu16" ", session->length_min); + PBL_LOG_WRN("Invalid duration: %"PRIu16" ", session->length_min); return false; } // The flags must be valid if (session->reserved != 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid flags: %d", (int)session->reserved); + PBL_LOG_WRN("Invalid flags: %d", (int)session->reserved); return false; } @@ -197,12 +186,12 @@ void activity_sessions_prv_add_activity_session(ActivitySession *session) { // If no more room, fail if (state->activity_sessions_count >= ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT) { - PBL_LOG(LOG_LEVEL_WARNING, "No more room for additional activities"); + PBL_LOG_WRN("No more room for additional activities"); goto unlock; } // Add this activity in - PBL_LOG(LOG_LEVEL_INFO, "Adding activity session %d, start_time: %"PRIu32, + PBL_LOG_INFO("Adding activity session %d, start_time: %"PRIu32, (int)session->type, (uint32_t)session->start_utc); state->activity_sessions[state->activity_sessions_count++] = *session; } @@ -230,7 +219,7 @@ void activity_sessions_prv_delete_activity_session(ActivitySession *session) { // If session not found, do nothing if (found_session_idx < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Session to delete not found"); + PBL_LOG_WRN("Session to delete not found"); goto unlock; } @@ -282,7 +271,7 @@ void activity_sessions_prv_send_activity_session_to_data_logging(ActivitySession DlsSystemTagActivitySession, DATA_LOGGING_BYTE_ARRAY, sizeof(dls_record), buffered, resume, &system_uuid); if (!state->activity_dls_session) { - PBL_LOG(LOG_LEVEL_WARNING, "Error creating activity DLS session"); + PBL_LOG_WRN("Error creating activity DLS session"); return; } } @@ -290,9 +279,9 @@ void activity_sessions_prv_send_activity_session_to_data_logging(ActivitySession // Log the record DataLoggingResult result = dls_log(state->activity_dls_session, &dls_record, 1); if (result != DATA_LOGGING_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "Error %"PRIi32" while logging activity to DLS", (int32_t)result); + PBL_LOG_WRN("Error %"PRIi32" while logging activity to DLS", (int32_t)result); } - PBL_LOG(LOG_LEVEL_INFO, "Logging activity event %d, start_time: %"PRIu32", " + PBL_LOG_INFO("Logging activity event %d, start_time: %"PRIu32", " "elapsed_min: %"PRIu16", end_time: %"PRIu32" ", (int)session->type, (uint32_t)session->start_utc, session->length_min, (uint32_t)session->start_utc + (session->length_min * SECONDS_PER_MINUTE)); @@ -590,7 +579,7 @@ static void prv_log_activities(time_t now_utc) { status_t result = settings_file_set(file, ¶ms->key, sizeof(params->key), params->exit_utc, sizeof(*params->exit_utc)); if (result != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Error saving last event time"); + PBL_LOG_ERR("Error saving last event time"); } } activity_private_settings_close(file); @@ -610,7 +599,7 @@ void activity_sessions_prv_init(SettingsFile *file, time_t utc_now) { // for less than the value size int stored_len = settings_file_get_len(file, &key, sizeof(key)); if (stored_len != sizeof(state->activity_sessions)) { - PBL_LOG(LOG_LEVEL_WARNING, "Stored activities not found or incompatible"); + PBL_LOG_WRN("Stored activities not found or incompatible"); return; } @@ -635,7 +624,7 @@ void activity_sessions_prv_init(SettingsFile *file, time_t utc_now) { // flash got corrupted, as in PBL-37848 PBL_HEXDUMP(LOG_LEVEL_INFO, (void *)state->activity_sessions, sizeof(state->activity_sessions)); - PBL_LOG(LOG_LEVEL_ERROR, "Invalid activity session detected - could be flash corrruption"); + PBL_LOG_ERR("Invalid activity session detected - could be flash corrruption"); // Zero out flash so that we don't get into a reboot loop memset(state->activity_sessions, 0, sizeof(state->activity_sessions)); @@ -649,7 +638,7 @@ void activity_sessions_prv_init(SettingsFile *file, time_t utc_now) { // Remove any activities that don't belong to "today" or that are ongoing activity_sessions_prv_remove_out_of_range_activity_sessions(utc_now, true /*remove_ongoing*/); - PBL_LOG(LOG_LEVEL_INFO, "Restored %"PRIu16" activities from storage", + PBL_LOG_INFO("Restored %"PRIu16" activities from storage", state->activity_sessions_count); } diff --git a/src/fw/services/normal/activity/docs/fft_arm_swing.png b/src/fw/services/activity/docs/fft_arm_swing.png similarity index 100% rename from src/fw/services/normal/activity/docs/fft_arm_swing.png rename to src/fw/services/activity/docs/fft_arm_swing.png diff --git a/src/fw/services/normal/activity/docs/fft_driving.png b/src/fw/services/activity/docs/fft_driving.png similarity index 100% rename from src/fw/services/normal/activity/docs/fft_driving.png rename to src/fw/services/activity/docs/fft_driving.png diff --git a/src/fw/services/normal/activity/docs/fft_non_walk.png b/src/fw/services/activity/docs/fft_non_walk.png similarity index 100% rename from src/fw/services/normal/activity/docs/fft_non_walk.png rename to src/fw/services/activity/docs/fft_non_walk.png diff --git a/src/fw/services/normal/activity/docs/fft_walking.png b/src/fw/services/activity/docs/fft_walking.png similarity index 100% rename from src/fw/services/normal/activity/docs/fft_walking.png rename to src/fw/services/activity/docs/fft_walking.png diff --git a/src/fw/services/normal/activity/docs/index.md b/src/fw/services/activity/docs/index.md similarity index 99% rename from src/fw/services/normal/activity/docs/index.md rename to src/fw/services/activity/docs/index.md index 5c10f424e2..673990c45b 100644 --- a/src/fw/services/normal/activity/docs/index.md +++ b/src/fw/services/activity/docs/index.md @@ -191,11 +191,11 @@ The following sections discuss how the step and sleep tracking algorithms are in ### Code organization -The core of the Health support logic is implemented in the activity service, which is in the `src/fw/services/normal/activity` directory. The 3rd party API, which calls into the activity service, is implemented in `src/fw/applib/health_service.c.` +The core of the Health support logic is implemented in the activity service, which is in the `src/fw/services/activity` directory. The 3rd party API, which calls into the activity service, is implemented in `src/fw/applib/health_service.c.` The activity service implements the step and sleep algorithms and all of the supporting logic required to integrate the algorithms into the system. It has the following directory structure: - src/fw/services/normal/activity + src/fw/services/activity activity.c activity_insights.c kraepelin/ diff --git a/src/fw/services/normal/activity/docs/raw_accel_5s.png b/src/fw/services/activity/docs/raw_accel_5s.png similarity index 100% rename from src/fw/services/normal/activity/docs/raw_accel_5s.png rename to src/fw/services/activity/docs/raw_accel_5s.png diff --git a/src/fw/services/normal/activity/docs/spectial_density.png b/src/fw/services/activity/docs/spectial_density.png similarity index 100% rename from src/fw/services/normal/activity/docs/spectial_density.png rename to src/fw/services/activity/docs/spectial_density.png diff --git a/src/fw/services/normal/activity/docs/vmc_formula.png b/src/fw/services/activity/docs/vmc_formula.png similarity index 100% rename from src/fw/services/normal/activity/docs/vmc_formula.png rename to src/fw/services/activity/docs/vmc_formula.png diff --git a/src/fw/services/normal/activity/health_util.c b/src/fw/services/activity/health_util.c similarity index 89% rename from src/fw/services/normal/activity/health_util.c rename to src/fw/services/activity/health_util.c index 91cb6dd3a3..97efe9f64f 100644 --- a/src/fw/services/normal/activity/health_util.c +++ b/src/fw/services/activity/health_util.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_util.h" - -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/activity/health_util.h" + +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity.h" #include "shell/prefs.h" #include "util/time/time.h" #include "util/units.h" @@ -108,7 +95,7 @@ void health_util_duration_to_hours_and_minutes_text_node(int duration_s, void *i i18n_get("%d", i18n_owner), hours); GTextNodeText *hours_units_text_node = health_util_create_text_node_with_text( - i18n_get("H", i18n_owner), units_font, color, container); + i18n_get("h", i18n_owner), units_font, color, container); hours_units_text_node->node.offset.y = units_offset_y; } @@ -124,7 +111,7 @@ void health_util_duration_to_hours_and_minutes_text_node(int duration_s, void *i i18n_get("%d", i18n_owner), minutes); GTextNodeText *minutes_units_text_node = health_util_create_text_node_with_text( - i18n_get("M", i18n_owner), units_font, color, container); + i18n_get("min", i18n_owner), units_font, color, container); minutes_units_text_node->node.offset.y = units_offset_y; } } diff --git a/src/fw/services/activity/hr_util.c b/src/fw/services/activity/hr_util.c new file mode 100644 index 0000000000..ea48cf9549 --- /dev/null +++ b/src/fw/services/activity/hr_util.c @@ -0,0 +1,27 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/activity/hr_util.h" + +#include "pbl/services/activity/activity.h" + +// ------------------------------------------------------------------------------------------------ +HRZone hr_util_get_hr_zone(int bpm) { + const int zone_thresholds[HRZone_Max] = { + activity_prefs_heart_get_zone1_threshold(), + activity_prefs_heart_get_zone2_threshold(), + activity_prefs_heart_get_zone3_threshold(), + }; + + HRZone zone; + for (zone = HRZone_Zone0; zone < HRZone_Max; zone++) { + if (bpm < zone_thresholds[zone]) { + break; + } + } + return zone; +} + +bool hr_util_is_elevated(int bpm) { + return bpm >= activity_prefs_heart_get_elevated_hr(); +} diff --git a/src/fw/services/normal/activity/insights_settings.c b/src/fw/services/activity/insights_settings.c similarity index 85% rename from src/fw/services/normal/activity/insights_settings.c rename to src/fw/services/activity/insights_settings.c index 606dd1800d..373b9adcb2 100644 --- a/src/fw/services/normal/activity/insights_settings.c +++ b/src/fw/services/activity/insights_settings.c @@ -1,30 +1,19 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include -#include "activity.h" -#include "insights_settings.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/insights_settings.h" #include "os/mutex.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" #include "system/logging.h" #include "util/size.h" +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); + #define ACTIVITY_INSIGHTS_SETTINGS_FILENAME "insights" #define ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_FILE_SIZE 4096 @@ -59,8 +48,8 @@ static PebbleMutex *s_insight_settings_mutex; .sleep = { \ .max_fail_minutes = 7 * MINUTES_PER_HOUR, \ .trigger_notif_seconds = 30 * SECONDS_PER_MINUTE, \ - .trigger_notif_activity = 20, \ - .trigger_notif_active_minutes = 5 \ + .trigger_notif_activity = 10, \ + .trigger_notif_active_minutes = 2 \ } \ } \ } @@ -183,7 +172,7 @@ void activity_insights_settings_init(void) { return; } - PBL_LOG(LOG_LEVEL_ERROR, "Failed to create activity insights settings file"); + PBL_LOG_ERR("Failed to create activity insights settings file"); } uint16_t activity_insights_settings_get_version(void) { @@ -216,13 +205,13 @@ bool activity_insights_settings_read(const char *insight_name, if (settings_file_get(&file, insight_name, strlen(insight_name), settings_out, sizeof(*settings_out)) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_DEBUG, "Didn't find insight with key %s", insight_name); + PBL_LOG_DBG("Didn't find insight with key %s", insight_name); goto close; } if (settings_out->version != ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION) { // versions don't match, bail out! - PBL_LOG(LOG_LEVEL_WARNING, "activity insights struct version mismatch"); + PBL_LOG_WRN("activity insights struct version mismatch"); goto close; } @@ -235,7 +224,7 @@ bool activity_insights_settings_read(const char *insight_name, // Use default value if we didn't find anything else for (unsigned i = 0; i < ARRAY_LENGTH(AIS_DEFAULTS); ++i) { if (strcmp(insight_name, AIS_DEFAULTS[i].key) == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "Using default for insight %s", insight_name); + PBL_LOG_DBG("Using default for insight %s", insight_name); *settings_out = AIS_DEFAULTS[i].default_val; rv = true; } @@ -256,7 +245,7 @@ bool activity_insights_settings_write(const char *insight_name, if (settings_file_set(&file, insight_name, strlen(insight_name), settings, sizeof(*settings)) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "Unable to save insight setting with key %s", insight_name); + PBL_LOG_WRN("Unable to save insight setting with key %s", insight_name); } else { rv = true; } diff --git a/src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.c b/src/fw/services/activity/kraepelin/activity_algorithm_kraepelin.c similarity index 94% rename from src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.c rename to src/fw/services/activity/kraepelin/activity_algorithm_kraepelin.c index 55ceab4a0b..a9ab591154 100644 --- a/src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.c +++ b/src/fw/services/activity/kraepelin/activity_algorithm_kraepelin.c @@ -1,31 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/accel_service.h" #include "applib/data_logging.h" #include "util/uuid.h" #include "drivers/ambient_light.h" #include "kernel/pbl_malloc.h" -#include "services/common/battery/battery_state.h" -#include "services/common/system_task.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/system_task.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" #include "syscall/syscall.h" #include "system/logging.h" #include "system/passert.h" @@ -36,8 +23,10 @@ #include "util/time/time.h" #include "util/units.h" -#include "activity_algorithm_kraepelin.h" -#include "kraepelin_algorithm.h" +#include "pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h" +#include "pbl/services/activity/kraepelin/kraepelin_algorithm.h" + +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); // NOTE: This file is called "activity_sleep" for legacy reasons. A better name now would be // something like "activity_minute_data", but we want to maintain compatibility with prior @@ -98,8 +87,8 @@ static AlgState *s_alg_state = NULL; // ---------------------------------------------------------------------------------------------- static bool prv_lock(void) { if (!s_alg_state) { -#if RELEASE - PBL_LOG(LOG_LEVEL_ERROR, "Trying to use the activity algorithm but it hasn't been initialized"); +#ifdef CONFIG_RELEASE + PBL_LOG_ERR("Trying to use the activity algorithm but it hasn't been initialized"); return false; #else WTF; @@ -119,7 +108,7 @@ static void prv_unlock(void) { static NOINLINE SettingsFile *prv_minute_data_file_open(void) { SettingsFile *file = kernel_malloc_check(sizeof(SettingsFile)); if (settings_file_open(file, ALG_MINUTE_DATA_FILE_NAME, ALG_MINUTE_DATA_FILE_LEN) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "No minute data file"); + PBL_LOG_ERR("No minute data file"); return NULL; } return file; @@ -384,19 +373,19 @@ static SettingsFile *prv_validate_and_trim_minute_file(SettingsFile *file, uint1 }; // Rewrite the settings file, keeping only the keys we need - PBL_LOG(LOG_LEVEL_DEBUG, "Compacting minute file down to %"PRIu16" records", max_records); + PBL_LOG_DBG("Compacting minute file down to %"PRIu16" records", max_records); status_t status = settings_file_rewrite_filtered(file, prv_minute_file_rewrite_cb, &context); // Error re-writing? if (status != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Encountered error %"PRIi32" rewriting settings file", + PBL_LOG_ERR("Encountered error %"PRIi32" rewriting settings file", (int32_t)status); nuke_file = true; } else { s_alg_state->num_minute_records = context.num_keys_kept; } - PBL_LOG(LOG_LEVEL_DEBUG, "Compaction done, ended up with %"PRIu16" records", + PBL_LOG_DBG("Compaction done, ended up with %"PRIu16" records", s_alg_state->num_minute_records); exit: @@ -406,7 +395,7 @@ static SettingsFile *prv_validate_and_trim_minute_file(SettingsFile *file, uint1 } if (nuke_file) { - PBL_LOG(LOG_LEVEL_WARNING, "Detected invalid minute data file, deleting it"); + PBL_LOG_WRN("Detected invalid minute data file, deleting it"); pfs_remove(ALG_MINUTE_DATA_FILE_NAME); } return file; @@ -457,7 +446,7 @@ static bool prv_write_minute_file_record(AlgMinuteFileRecord *file_record) { SettingsFile *minute_file = prv_minute_data_file_open(); if (!minute_file) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not open minute file for saving minute stats"); + PBL_LOG_ERR("Could not open minute file for saving minute stats"); goto exit; } @@ -466,7 +455,7 @@ static bool prv_write_minute_file_record(AlgMinuteFileRecord *file_record) { sizeof(*file_record)); if (status == E_OUT_OF_STORAGE) { uint16_t max_records = s_alg_state->num_minute_records / 2; - PBL_LOG(LOG_LEVEL_INFO, "Compacting minute file from %"PRIu16" records to %"PRIu16"", + PBL_LOG_INFO("Compacting minute file from %"PRIu16" records to %"PRIu16"", s_alg_state->num_minute_records, max_records); minute_file = prv_validate_and_trim_minute_file(minute_file, max_records); if (!minute_file) { @@ -478,7 +467,7 @@ static bool prv_write_minute_file_record(AlgMinuteFileRecord *file_record) { // Was there an error writing the value out? if (status != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Error %"PRIi32" writing out minute data to minute file", + PBL_LOG_ERR("Error %"PRIi32" writing out minute data to minute file", (int32_t)status); } else { s_alg_state->num_minute_records++; @@ -509,7 +498,7 @@ static DataLoggingSession *prv_get_dls_minute_session(void) { // This can happen when you are not connected to the phone and have rebooted a number of // times because each time you reboot, you get new sessions created and reach the limit // of the max # of sessions allowed. - PBL_LOG(LOG_LEVEL_WARNING, "Error creating activity logging session"); + PBL_LOG_WRN("Error creating activity logging session"); return NULL; } } @@ -533,7 +522,7 @@ static void NOINLINE prv_set_dls_minute_record_entry(AlgMinuteDLSRecord *dls_rec if (was_sleeping && (dls_record->samples[sample_idx].base.steps != 0)) { // Subtract from our total steps since we decided we were definitely sleeping during // this minute - PBL_LOG(LOG_LEVEL_DEBUG, "Subtracting %"PRIu8" steps that occurred during sleep", + PBL_LOG_DBG("Subtracting %"PRIu8" steps that occurred during sleep", dls_record->samples[sample_idx].base.steps); s_alg_state->steps -= dls_record->samples[sample_idx].base.steps; s_alg_state->steps = MAX(0, s_alg_state->steps); @@ -643,10 +632,10 @@ static void prv_send_minute_data(uint16_t uncertain_m, time_t sleep_start_utc, // Handle writing the record out to data logging DataLoggingResult result = dls_log(dls_session, dls_record, 1); // PBL-43622: Will revert later - PBL_LOG(LOG_LEVEL_DEBUG, "Logging %"PRIu8" MLD Records, First UTC: %"PRIu32, + PBL_LOG_DBG("Logging %"PRIu8" MLD Records, First UTC: %"PRIu32, dls_record->hdr.num_samples, dls_record->hdr.time_utc); if (result != DATA_LOGGING_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "Error %"PRIi32" while logging activity data", (int32_t) result); + PBL_LOG_WRN("Error %"PRIi32" while logging activity data", (int32_t) result); return; } } @@ -666,7 +655,7 @@ static void prv_log_minute_data(time_t utc_now, AlgMinuteRecord *minute_rec) { // Although unlikely, we could get buffer overruns if we failed to open up the data logging // session after a number of retries. In that case, we will start dropping the oldest // minute data from data logging. - PBL_LOG(LOG_LEVEL_ERROR, "Circular buffer overrun"); + PBL_LOG_ERR("Circular buffer overrun"); success = shared_circular_buffer_write(&s_alg_state->minute_data_cbuf, (uint8_t *)minute_rec, sizeof(*minute_rec), true /*advance_slackers*/); } @@ -684,7 +673,7 @@ static void prv_log_minute_data(time_t utc_now, AlgMinuteRecord *minute_rec) { uncertain_m = (utc_now - sleep_stats.uncertain_start_utc) / SECONDS_PER_MINUTE; } if (uncertain_m > KALG_MAX_UNCERTAIN_SLEEP_M) { - PBL_LOG(LOG_LEVEL_ERROR, "Unexpectedly large number of uncertain minutes"); + PBL_LOG_ERR("Unexpectedly large number of uncertain minutes"); uncertain_m = KALG_MAX_UNCERTAIN_SLEEP_M; } @@ -797,12 +786,12 @@ void activity_algorithm_post_process_sleep_sessions(uint16_t num_input_sessions, // Label it as a nap if (session->type == ActivitySessionType_Sleep) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found nap - start_utc: %d, start_min: %u, len: %"PRIu16" ", + PBL_LOG_DBG("Found nap - start_utc: %d, start_min: %u, len: %"PRIu16" ", (int)session->start_utc, start_minute, session->length_min); session->type = ActivitySessionType_Nap; most_recent_nap_session = session; } else if (session->type == ActivitySessionType_RestfulSleep) { - PBL_LOG(LOG_LEVEL_DEBUG, "Found restful nap - start_utc: %d, start_min: %u, len: %"PRIu16" ", + PBL_LOG_DBG("Found restful nap - start_utc: %d, start_min: %u, len: %"PRIu16" ", (int)session->start_utc, start_minute, session->length_min); session->type = ActivitySessionType_RestfulNap; } @@ -820,7 +809,7 @@ bool activity_algorithm_init(AccelSamplingRate *sampling_rate) { s_alg_state = kernel_zalloc(sizeof(AlgState)); } if (!s_alg_state) { - PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory"); + PBL_LOG_ERR("Not enough memory"); kernel_free(k_state); return false; } @@ -848,7 +837,7 @@ bool activity_algorithm_init(AccelSamplingRate *sampling_rate) { activity_algorithm_minute_file_info(false /*compact_first*/, &num_records, &data_bytes, &minutes); s_alg_state->num_minute_records = num_records; - PBL_LOG(LOG_LEVEL_DEBUG, "Found %"PRIu16" records in minute file", + PBL_LOG_DBG("Found %"PRIu16" records in minute file", s_alg_state->num_minute_records); // Reset all metrics @@ -983,14 +972,22 @@ static void prv_activity_update_states(time_t utc_sec, AlgMinuteRecord *record_o prv_reset_state_minute_handler(m_rec); + // Treat a recent HRM off-wrist reading as a definite not-worn signal for sleep tracking. + // The PPG-based off-wrist detection is far more reliable than the accel-only heuristic in + // kraepelin, which can take 30+ minutes to fire and is easily defeated by ambient vibration + // (see FIRM-1901, FIRM-1804, FIRM-1533). plugged_in keeps its original "charging" meaning + // in the logged record. + const bool hrm_offwrist = activity_metrics_prv_is_hrm_offwrist(utc_sec); + const bool not_worn = m_rec->base.plugged_in || hrm_offwrist; + ACTIVITY_LOG_DEBUG("minute handler: steps: %"PRIu8", orientation: 0x%"PRIx8", vmc: %"PRIu16", " - "light: %"PRIu8", plugged_in: %d", + "light: %"PRIu8", plugged_in: %d, hrm_offwrist: %d", m_rec->base.steps, m_rec->base.orientation, m_rec->base.vmc, - m_rec->base.light, (int) m_rec->base.plugged_in); + m_rec->base.light, (int) m_rec->base.plugged_in, (int) hrm_offwrist); // Pass the minute data onto the activity detection logic kalg_activities_update(s_alg_state->k_state, utc_sec, m_rec->base.steps, m_rec->base.vmc, - m_rec->base.orientation, m_rec->base.plugged_in, m_rec->resting_calories, + m_rec->base.orientation, not_worn, m_rec->resting_calories, m_rec->active_calories, minute_distance_mm, shutting_down, prv_create_activity_session_cb, NULL); } @@ -1358,7 +1355,7 @@ bool activity_algorithm_test_fill_minute_file(void) { uint32_t secs_per_record = ALG_MINUTES_PER_FILE_RECORD * SECONDS_PER_MINUTE; time_t start_utc = utc_sec - ALG_MINUTE_FILE_MAX_ENTRIES * secs_per_record; - PBL_LOG(LOG_LEVEL_DEBUG, "Writing %"PRIu32" records", (uint32_t) ALG_MINUTE_FILE_MAX_ENTRIES); + PBL_LOG_DBG("Writing %"PRIu32" records", (uint32_t) ALG_MINUTE_FILE_MAX_ENTRIES); // Fill up the minute file to capacity, starting from back in time uint8_t heart_rate = 50; @@ -1385,11 +1382,11 @@ bool activity_algorithm_test_fill_minute_file(void) { } system_task_watchdog_feed(); if ((i % 25) == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "wrote %"PRIu32" records...", i); + PBL_LOG_DBG("wrote %"PRIu32" records...", i); } } - PBL_LOG(LOG_LEVEL_DEBUG, "Done. End # of records: %"PRIu16, s_alg_state->num_minute_records); + PBL_LOG_DBG("Done. End # of records: %"PRIu16, s_alg_state->num_minute_records); return success; } diff --git a/src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.c b/src/fw/services/activity/kraepelin/kraepelin_algorithm.c similarity index 97% rename from src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.c rename to src/fw/services/activity/kraepelin/kraepelin_algorithm.c index babaa7cc49..58d0b0b3d2 100644 --- a/src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.c +++ b/src/fw/services/activity/kraepelin/kraepelin_algorithm.c @@ -32,17 +32,20 @@ Pebble App roject. #include "applib/accel_service.h" #include "util/trig.h" -#include "services/common/hrm/hrm_manager_private.h" +#include "pbl/services/hrm/hrm_manager_private.h" +#include "pbl/services/activity/activity.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" #include "util/math_fixed.h" #include "util/size.h" -#include "kraepelin_algorithm.h" +#include "pbl/services/activity/kraepelin/kraepelin_algorithm.h" + +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); #define KALG_LOG_DEBUG(fmt, args...) \ - PBL_LOG_D(LOG_DOMAIN_ACTIVITY, LOG_LEVEL_DEBUG, fmt, ## args) + PBL_LOG_D_DBG(LOG_DOMAIN_ACTIVITY, fmt, ## args) // Set this to 1 to get text graphs of the overall FFT magnitudes #define KALG_LOG_OVERALL_MAGNITUDES 0 @@ -125,7 +128,7 @@ typedef struct { typedef struct { uint16_t vmc; uint8_t orientation; - bool plugged_in; + bool definitely_not_worn; } KAlgSleepMinute; // State information for sleep detection @@ -191,7 +194,7 @@ typedef struct { // Set the sleep parameters -#if defined(PLATFORM_ASTERIX) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) static const KAlgSleepParams KALG_SLEEP_PARAMS = { .max_sleep_minute_score = 500, // Increased significantly for asterix - much stricter .force_wake_minute_score = 8000, @@ -306,11 +309,11 @@ typedef struct { // Set the not-worn parameters -#if defined(PLATFORM_ASTERIX) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) static const KAlgNotWornParams KALG_NOT_WORN_PARAMS = { .max_non_worn_vmc = 2500, .min_worn_vmc = 15, // Increased significantly for asterix - much higher baseline noise - .max_low_vmc_run_m = 30, // Reduced to catch desk time within 30 minutes + .max_low_vmc_run_m = 120, // Allow longer stillness during deep sleep while still catching desk time }; #else static const KAlgNotWornParams KALG_NOT_WORN_PARAMS = { @@ -381,7 +384,7 @@ static const char* prv_log_time(KAlgState *alg_state, time_t utc) { // ---------------------------------------------------------------------------------------- static void prv_reset_step_activity_state(KAlgStepActivityState *state) { -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM if (state->hrm_session != HRM_INVALID_SESSION_REF) { sys_hrm_manager_unsubscribe(state->hrm_session); } @@ -391,8 +394,14 @@ static void prv_reset_step_activity_state(KAlgStepActivityState *state) { // ---------------------------------------------------------------------------------------- static void prv_reset_state(KAlgState *state) { - prv_reset_step_activity_state(&state->walk_state); - prv_reset_step_activity_state(&state->run_state); + // Only reset step activity states if they're not currently in progress to avoid + // race conditions with the minute handler + if (state->walk_state.start_time == KALG_START_TIME_NONE) { + prv_reset_step_activity_state(&state->walk_state); + } + if (state->run_state.start_time == KALG_START_TIME_NONE) { + prv_reset_step_activity_state(&state->run_state); + } state->sleep_state = (KAlgSleepActivityState){}; state->deep_sleep_state = (KAlgDeepSleepActivityState){}; state->not_worn_state = (KAlgNotWornState){}; @@ -1371,7 +1380,7 @@ uint32_t kalg_analyze_finish_epoch(KAlgState *state) { // Update the not-worn detection state machine. This state machine gets called on every minute // update. It returns true if it determines the watch was not worn static bool prv_not_worn_update(KAlgState *alg_state, time_t utc_now, uint16_t vmc, - uint8_t orientation, bool plugged_in) { + uint8_t orientation, bool definitely_not_worn) { // Handy access to some variables const KAlgNotWornParams *params = &KALG_NOT_WORN_PARAMS; KAlgNotWornState *state = &alg_state->not_worn_state; @@ -1394,7 +1403,7 @@ static bool prv_not_worn_update(KAlgState *alg_state, time_t utc_now, uint16_t v } // Look for specific VMC values here that indicate definite worn or not-worn status - bool definite_not_worn = plugged_in; + bool definite_not_worn = definitely_not_worn; // Update stats if (maybe_not_worn || definite_not_worn) { @@ -1620,7 +1629,7 @@ static void prv_deep_sleep_update(KAlgState *alg_state, time_t sample_time, uint // We reached the end of it last_deep_run_size minutes ago end_time = sample_time - (last_deep_run_size * SECONDS_PER_MINUTE); uint16_t len_m = MAX((end_time - start_time) / SECONDS_PER_MINUTE, 0); - PBL_LOG(LOG_LEVEL_DEBUG, "Detected deep sleep of %" + PBL_LOG_DBG("Detected deep sleep of %" PRIu16 " minutes starting at %s ", len_m, prv_log_time(alg_state, start_time)); @@ -1632,7 +1641,7 @@ static void prv_deep_sleep_update(KAlgState *alg_state, time_t sample_time, uint state->len_m[state->num_sessions] = len_m; state->num_sessions++; } else { - PBL_LOG(LOG_LEVEL_WARNING, "No more room for another deep sleep session"); + PBL_LOG_WRN("No more room for another deep sleep session"); } // Wait for another session state->deep_start_time = KALG_START_TIME_NONE; @@ -1652,7 +1661,7 @@ static void prv_deep_sleep_update(KAlgState *alg_state, time_t sample_time, uint // called at the beginning of prv_sleep_activity_update(). // @return true if we have enough data to compute the score for this minute static bool prv_sleep_activity_update_stats(KAlgState *alg_state, time_t utc_now, uint16_t vmc, - uint8_t orientation, bool plugged_in, + uint8_t orientation, bool definitely_not_worn, uint32_t *score_ret, time_t *sample_utc_ret, bool *is_sleep_minute_ret) { // Handy access to some variables @@ -1669,11 +1678,11 @@ static bool prv_sleep_activity_update_stats(KAlgState *alg_state, time_t utc_now state->minute_history[state->num_history_entries++] = (KAlgSleepMinute) { .vmc = vmc, .orientation = orientation, - .plugged_in = plugged_in, + .definitely_not_worn = definitely_not_worn, }; // Get the not-worn status - bool not_worn = prv_not_worn_update(alg_state, utc_now, vmc, orientation, plugged_in); + bool not_worn = prv_not_worn_update(alg_state, utc_now, vmc, orientation, definitely_not_worn); // We have to have at least a filter's worth of data if (state->num_history_entries < history_capacity) { @@ -1822,7 +1831,8 @@ static void prv_sleep_activity_update_session_state( // ------------------------------------------------------------------------------------------ // Process the minute data for sleep detection static void prv_sleep_activity_update(KAlgState *alg_state, time_t utc_now, uint16_t vmc, - uint8_t orientation, bool plugged_in, bool shutting_down, + uint8_t orientation, bool definitely_not_worn, + bool shutting_down, KAlgActivitySessionCallback sessions_cb, void *context) { // Handy access to some variables const KAlgSleepParams *params = &KALG_SLEEP_PARAMS; @@ -1838,8 +1848,9 @@ static void prv_sleep_activity_update(KAlgState *alg_state, time_t utc_now, uint // because the sleep algorithm can only be run when we accumulated enough minutes. We // essentially run it with old data, but with the added constraint that we are shutting down. sample_utc = alg_state->sleep_state.last_sample_utc; - } else if (!prv_sleep_activity_update_stats(alg_state, utc_now, vmc, orientation, plugged_in, - &score, &sample_utc, &is_sleep_minute)) { + } else if (!prv_sleep_activity_update_stats(alg_state, utc_now, vmc, orientation, + definitely_not_worn, &score, &sample_utc, + &is_sleep_minute)) { return; } @@ -1885,7 +1896,7 @@ static void prv_sleep_activity_update(KAlgState *alg_state, time_t utc_now, uint // If we got a valid sleep cycle, add it to the totals if (!reject_session) { - PBL_LOG(LOG_LEVEL_DEBUG, "Detected valid sleep cycle of len %d, starting at %s", + PBL_LOG_DBG("Detected valid sleep cycle of len %d, starting at %s", session_len_m, prv_log_time(alg_state, state->current_stats.start_time)); sessions_cb(context, KAlgActivityType_Sleep, state->current_stats.start_time, @@ -1963,7 +1974,7 @@ static const KAlgActivityAttributes *prv_get_step_activity_attributes(KAlgActivi return &k_attributes[activity]; } -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM // ------------------------------------------------------------------------------------------ static void prv_hrm_subscription_cb(PebbleHRMEvent *hrm_event, void *context) { // The algorithm doesn't care about these events. It only subscribed so the activity service @@ -2009,10 +2020,11 @@ static void prv_step_activity_update(KAlgState *alg_state, KAlgStepActivityState PBL_ASSERTN(state->start_time < utc_now); uint32_t duration_secs = utc_now - state->start_time; -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM // Make sure we have a couple active minutes in a row before enabling the HRM to save battery const unsigned min_duration_for_hrm = 3 * SECONDS_PER_MINUTE; - if (duration_secs >= min_duration_for_hrm && state->hrm_session == HRM_INVALID_SESSION_REF) { + if (duration_secs >= min_duration_for_hrm && state->hrm_session == HRM_INVALID_SESSION_REF && + activity_prefs_hrm_activity_tracking_is_enabled()) { state->hrm_session = hrm_manager_subscribe_with_callback(INSTALL_ID_INVALID, 1 /* update interval */, 0 /*expire_s*/, HRMFeature_BPM, prv_hrm_subscription_cb, NULL); } @@ -2074,7 +2086,8 @@ static void prv_step_activity_update(KAlgState *alg_state, KAlgStepActivityState // Feed new minute data into the activity detection state machine. This logic looks for non-sleep // activities, like walks, runs, etc. void kalg_activities_update(KAlgState *state, time_t utc_now, uint16_t steps, uint16_t vmc, - uint8_t orientation, bool plugged_in, uint32_t resting_calories, + uint8_t orientation, bool definitely_not_worn, + uint32_t resting_calories, uint32_t active_calories, uint32_t distance_mm, bool shutting_down, KAlgActivitySessionCallback sessions_cb, void *context) { // If we've encountered a significant change in UTC time (connecting to a new phone, factory @@ -2082,7 +2095,7 @@ void kalg_activities_update(KAlgState *state, time_t utc_now, uint16_t steps, ui // state if ((utc_now < state->last_activity_update_utc) || (utc_now > (state->last_activity_update_utc + (5 * SECONDS_PER_MINUTE)))) { - PBL_LOG(LOG_LEVEL_WARNING, "Resetting state due to time travel"); + PBL_LOG_WRN("Resetting state due to time travel"); prv_reset_state(state); }; state->last_activity_update_utc = utc_now; @@ -2099,8 +2112,8 @@ void kalg_activities_update(KAlgState *state, time_t utc_now, uint16_t steps, ui KAlgActivityType_Run); // Pass onto the sleep detector - prv_sleep_activity_update(state, utc_now, vmc, orientation, plugged_in, shutting_down, - sessions_cb, context); + prv_sleep_activity_update(state, utc_now, vmc, orientation, definitely_not_worn, + shutting_down, sessions_cb, context); } } diff --git a/src/fw/services/normal/activity/workout_service.c b/src/fw/services/activity/workout_service.c similarity index 80% rename from src/fw/services/normal/activity/workout_service.c rename to src/fw/services/activity/workout_service.c index 6ba59cee46..03373f9a29 100644 --- a/src/fw/services/normal/activity/workout_service.c +++ b/src/fw/services/activity/workout_service.c @@ -1,40 +1,30 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "workout_service.h" - -#include "activity_algorithm.h" -#include "activity_calculators.h" -#include "activity_insights.h" -#include "activity_private.h" -#include "hr_util.h" - -#include "apps/system_apps/workout/workout_utils.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/activity/workout_service.h" + +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_calculators.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/hr_util.h" + +#include "apps/system/workout/utils.h" #include "applib/app.h" #include "applib/health_service.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/evented_timer.h" -#include "services/common/regular_timer.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/regular_timer.h" +#include "system/logging.h" #include "system/passert.h" #include "util/time/time.h" #include "util/units.h" #include +PBL_LOG_MODULE_DECLARE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL); + #define WORKOUT_HR_READING_TS_EXPIRE (SECONDS_PER_MINUTE) #define WORKOUT_ENDED_HR_SUBSCRIPTION_TS_EXPIRE (10 * SECONDS_PER_MINUTE) #define WORKOUT_ACTIVE_HR_SUBSCRIPTION_TS_EXPIRE (SECONDS_PER_HOUR) @@ -146,7 +136,7 @@ static void prv_handle_movement_update(HealthEventMovementUpdateData *event) { const int32_t new_event_steps = event->steps; const time_t now_ts = time_get_uptime_seconds(); if (new_event_steps < wrkt_data->last_event_step_count) { - PBL_LOG(LOG_LEVEL_WARNING, "Working out through midnight, resetting last_event_step_count"); + PBL_LOG_WRN("Working out through midnight, resetting last_event_step_count"); wrkt_data->last_event_step_count = 0; } @@ -223,21 +213,22 @@ T_STATIC void prv_abandoned_notification_timer_callback(void *unused) { // --------------------------------------------------------------------------------------- T_STATIC void prv_workout_timer_cb(void *unused) { - if (!workout_service_is_workout_ongoing()) { + // This runs on the NewTimer task, which has a watchdog. The workout mutex + // can be held by paths that block on flash (e.g. stop_workout pushing the + // session notification), so a blocking lock here can trip the watchdog and + // reboot the watch. The cb is purely advisory (duration tick / HR aging), + // so if the lock is contended just skip this tick — the next one catches up. + if (!mutex_lock_recursive_with_timeout(s_workout_data.s_workout_mutex, 0)) { return; } - prv_lock(); - { - // Update the duration + if (s_workout_data.current_workout) { prv_update_duration(); - // Check to make sure our HR sample is still valid const time_t now_ts = time_get_uptime_seconds(); const time_t age_hr_s = now_ts - s_workout_data.current_workout->current_bpm_timestamp_ts; if (s_workout_data.current_workout->current_bpm != 0 && age_hr_s >= WORKOUT_HR_READING_TS_EXPIRE) { - // Reset HR reading. It has expired prv_reset_hr_data(); } } @@ -246,27 +237,25 @@ T_STATIC void prv_workout_timer_cb(void *unused) { // --------------------------------------------------------------------------------------- void workout_service_health_event_handler(PebbleHealthEvent *event) { - if (!workout_service_is_workout_ongoing()) { - return; - } - prv_lock(); { + if (!s_workout_data.current_workout) { + goto unlock; + } if (event->type == HealthEventMovementUpdate) { prv_handle_movement_update(&event->data.movement_update); } else if (event->type == HealthEventHeartRateUpdate) { prv_handle_heart_rate_update(&event->data.heart_rate_update); } } +unlock: prv_unlock(); } // --------------------------------------------------------------------------------------- void workout_service_activity_event_handler(PebbleActivityEvent *event) { - if (!workout_service_is_workout_ongoing()) { - return; - } - + // workout_service_pause_workout is a no-op when no workout is ongoing, + // so let it do the check under the lock rather than racing it here. if (event->type == PebbleActivityEvent_TrackingStopped) { workout_service_pause_workout(true); } @@ -274,18 +263,22 @@ void workout_service_activity_event_handler(PebbleActivityEvent *event) { // --------------------------------------------------------------------------------------- void workout_service_workout_event_handler(PebbleWorkoutEvent *event) { - if (!workout_service_is_workout_ongoing()) { - return; - } - - // Handling this with an event because the timer needs to be called from KernelMain - if (event->type == PebbleWorkoutEvent_FrontendOpened) { - evented_timer_cancel(s_workout_data.current_workout->workout_abandoned_timer); - } else if (event->type == PebbleWorkoutEvent_FrontendClosed) { - s_workout_data.current_workout->workout_abandoned_timer = - evented_timer_register(WORKOUT_ABANDONED_NOTIFICATION_TIMEOUT_MS, false, - prv_abandoned_notification_timer_callback, NULL); + prv_lock(); + { + if (!s_workout_data.current_workout) { + goto unlock; + } + // Handling this with an event because the timer needs to be called from KernelMain + if (event->type == PebbleWorkoutEvent_FrontendOpened) { + evented_timer_cancel(s_workout_data.current_workout->workout_abandoned_timer); + } else if (event->type == PebbleWorkoutEvent_FrontendClosed) { + s_workout_data.current_workout->workout_abandoned_timer = + evented_timer_register(WORKOUT_ABANDONED_NOTIFICATION_TIMEOUT_MS, false, + prv_abandoned_notification_timer_callback, NULL); + } } +unlock: + prv_unlock(); } // --------------------------------------------------------------------------------------- @@ -299,10 +292,10 @@ void workout_service_frontend_opened(void) { PBL_ASSERT_TASK(PebbleTask_App); prv_lock(); { -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM s_workout_data.hrm_session = sys_hrm_manager_app_subscribe(app_get_app_id(), 1, 0, HRMFeature_BPM); -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM s_workout_data.frontend_last_opened_ts = time_get_uptime_seconds(); prv_put_event(PebbleWorkoutEvent_FrontendOpened); } @@ -316,7 +309,7 @@ void workout_service_frontend_closed(void) { PBL_ASSERT_TASK(PebbleTask_App); prv_lock(); { -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM int32_t hr_time_left; if (workout_service_is_workout_ongoing()) { @@ -346,7 +339,7 @@ void workout_service_frontend_closed(void) { // No time left. Kill the subscription sys_hrm_manager_unsubscribe(s_workout_data.hrm_session); } -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM prv_put_event(PebbleWorkoutEvent_FrontendClosed); } @@ -365,7 +358,7 @@ bool workout_service_start_workout(ActivitySessionType type) { } if (workout_service_is_workout_ongoing()) { - PBL_LOG(LOG_LEVEL_WARNING, "Only 1 workout at a time is supported"); + PBL_LOG_WRN("Only 1 workout at a time is supported"); rv = false; goto unlock; } @@ -404,7 +397,7 @@ bool workout_service_start_workout(ActivitySessionType type) { // Finally tell our algorithm it should stop automatically tracking activities activity_algorithm_enable_activity_tracking(false /* disable */); - PBL_LOG(LOG_LEVEL_INFO, "Starting a workout with type: %d", type); + PBL_LOG_INFO("Starting a workout with type: %d", type); prv_put_event(PebbleWorkoutEvent_Started); } unlock: @@ -414,21 +407,23 @@ bool workout_service_start_workout(ActivitySessionType type) { // --------------------------------------------------------------------------------------- bool workout_service_pause_workout(bool should_be_paused) { - if (workout_service_is_paused() == should_be_paused) { - // If no change in state, return early and successful - return true; - } - - if (!workout_service_is_workout_ongoing()) { - PBL_LOG(LOG_LEVEL_WARNING, "Workout (un)pause requested but no workout in progress"); - return false; - } - + bool rv = false; prv_lock(); { + if (!s_workout_data.current_workout) { + PBL_LOG_WRN("Workout (un)pause requested but no workout in progress"); + goto unlock; + } + CurrentWorkoutData *wrkt_data = s_workout_data.current_workout; - if (workout_service_is_paused()) { + if (wrkt_data->paused == should_be_paused) { + // If no change in state, return early and successful + rv = true; + goto unlock; + } + + if (wrkt_data->paused) { // We are paused and want to unpause. Add the in progress pause time to the total wrkt_data->duration_completed_pauses_s += (rtc_get_time() - wrkt_data->last_paused_utc); } else { @@ -436,53 +431,59 @@ bool workout_service_pause_workout(bool should_be_paused) { wrkt_data->last_paused_utc = rtc_get_time(); } - s_workout_data.current_workout->paused = should_be_paused; + wrkt_data->paused = should_be_paused; // Update the global duration since we have changed the pause state prv_update_duration(); - PBL_LOG(LOG_LEVEL_INFO, "Paused a workout with type: %d", wrkt_data->type); + PBL_LOG_INFO("Paused a workout with type: %d", wrkt_data->type); prv_put_event(PebbleWorkoutEvent_Paused); + rv = true; } +unlock: prv_unlock(); - return true; + return rv; } // --------------------------------------------------------------------------------------- bool workout_service_stop_workout(void) { - bool rv = true; + bool save_session = false; + ActivitySession session_to_save; + int32_t avg_hr_to_save = 0; + int32_t hr_zone_time_s_to_save[HRZoneCount]; + prv_lock(); { if (!workout_service_is_workout_ongoing()) { - PBL_LOG(LOG_LEVEL_WARNING, "No workout in progress"); - rv = false; - goto unlock; + PBL_LOG_WRN("No workout in progress"); + prv_unlock(); + return false; } - // Create an activity session for this workout if it was long enough - if (s_workout_data.current_workout->duration_s >= SECONDS_PER_MINUTE) { - const time_t len_min = - MIN(ACTIVITY_SESSION_MAX_LENGTH_MIN, - s_workout_data.current_workout->duration_s / SECONDS_PER_MINUTE); - - ActivitySession session = { - .type = s_workout_data.current_workout->type, - .start_utc = s_workout_data.current_workout->start_utc, + CurrentWorkoutData *wrkt = s_workout_data.current_workout; + + // Snapshot the session data so we can persist it after dropping the + // workout mutex. activity_insights_push_activity_session_notification + // creates a notification (blob_db flash write) that can take long enough + if (wrkt->duration_s >= SECONDS_PER_MINUTE) { + const time_t len_min = MIN(ACTIVITY_SESSION_MAX_LENGTH_MIN, + wrkt->duration_s / SECONDS_PER_MINUTE); + session_to_save = (ActivitySession) { + .type = wrkt->type, + .start_utc = wrkt->start_utc, .length_min = len_min, .ongoing = false, .manual = true, - .step_data.steps = s_workout_data.current_workout->steps, - .step_data.distance_meters = s_workout_data.current_workout->distance_m, - .step_data.active_kcalories = ROUND(s_workout_data.current_workout->active_calories, + .step_data.steps = wrkt->steps, + .step_data.distance_meters = wrkt->distance_m, + .step_data.active_kcalories = ROUND(wrkt->active_calories, ACTIVITY_CALORIES_PER_KCAL), .step_data.resting_kcalories = ROUND(activity_private_compute_resting_calories(len_min), ACTIVITY_CALORIES_PER_KCAL), }; - activity_sessions_prv_add_activity_session(&session); - - activity_insights_push_activity_session_notification(rtc_get_time(), &session, - prv_get_avg_hr(), s_workout_data.current_workout->hr_zone_time_s); - + avg_hr_to_save = prv_get_avg_hr(); + memcpy(hr_zone_time_s_to_save, wrkt->hr_zone_time_s, sizeof(hr_zone_time_s_to_save)); s_workout_data.last_workout_end_ts = time_get_uptime_seconds(); + save_session = true; } regular_timer_remove_callback(&s_workout_data.second_timer); @@ -490,16 +491,29 @@ bool workout_service_stop_workout(void) { // Re-enable automatic activity tracking activity_algorithm_enable_activity_tracking(true /* enable */); - PBL_LOG(LOG_LEVEL_INFO, "Stopping a workout with type: %d", - s_workout_data.current_workout->type); +#ifdef CONFIG_HRM + // The workout app holds an unbounded 1-second HR subscription while open. Now that the workout + // has ended, start the post-workout recovery countdown immediately so HR sampling drops back to + // the user's preferred rate within a bounded window, regardless of when the app actually exits. + sys_hrm_manager_set_update_interval(s_workout_data.hrm_session, 1, + WORKOUT_ENDED_HR_SUBSCRIPTION_TS_EXPIRE); +#endif // CONFIG_HRM + + PBL_LOG_INFO("Stopping a workout with type: %d", wrkt->type); prv_put_event(PebbleWorkoutEvent_Stopped); kernel_free(s_workout_data.current_workout); s_workout_data.current_workout = NULL; } -unlock: prv_unlock(); - return rv; + + if (save_session) { + activity_sessions_prv_add_activity_session(&session_to_save); + activity_insights_push_activity_session_notification(rtc_get_time(), &session_to_save, + avg_hr_to_save, hr_zone_time_s_to_save); + } + + return true; } // --------------------------------------------------------------------------------------- diff --git a/src/fw/services/activity/wscript_build b/src/fw/services/activity/wscript_build new file mode 100644 index 0000000000..bd403c456f --- /dev/null +++ b/src/fw/services/activity/wscript_build @@ -0,0 +1,28 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'activity.c', + 'activity_calculators.c', + 'activity_insights.c', + 'activity_metrics.c', + 'activity_sessions.c', + 'health_util.c', + 'hr_util.c', + 'insights_settings.c', + 'kraepelin/activity_algorithm_kraepelin.c', + 'kraepelin/kraepelin_algorithm.c', + 'workout_service.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_activity', +) + +bld.env.SERVICES.append('services_activity') + +target = bld.path.find_or_declare('services_activity.pot') +bld.gettext(source=sources, target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/alarms/Kconfig b/src/fw/services/alarms/Kconfig new file mode 100644 index 0000000000..25f928c443 --- /dev/null +++ b/src/fw/services/alarms/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ALARMS + bool "Alarms" + help + Alarms service. + +if SERVICE_ALARMS + +module = SERVICE_ALARMS +module-str = Alarms +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/alarms/alarm.c b/src/fw/services/alarms/alarm.c similarity index 84% rename from src/fw/services/normal/alarms/alarm.c rename to src/fw/services/alarms/alarm.c index 0576d57750..4d943d9239 100644 --- a/src/fw/services/normal/alarms/alarm.c +++ b/src/fw/services/alarms/alarm.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "alarm.h" -#include "alarm_pin.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/alarms/alarm_pin.h" #include "apps/system_app_ids.h" #include "drivers/rtc.h" @@ -23,15 +10,15 @@ #include "kernel/pbl_malloc.h" #include "os/mutex.h" #include "process_management/app_install_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/activity/activity.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/timeline/event.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/timeline/event.h" +#include "pbl/services/timeline/timeline.h" #include "system/logging.h" #include "system/passert.h" #include "util/attributes.h" @@ -43,6 +30,8 @@ #include +PBL_LOG_MODULE_DEFINE(service_alarms, CONFIG_SERVICE_ALARMS_LOG_LEVEL); + #define DEFAULT_SNOOZE_DELAY_M (10) #define MAX_CONFIGURED_ALARMS (10) @@ -91,6 +80,15 @@ typedef struct PACKED { //! Whether the alarm is a smart alarm or not. Smart alarms attempt to wake the user the //! first moment the user is not in deep sleep in the time range T-30min to T every 5 min. bool is_smart:1; + //! Whether the alarm should play a tone on speaker hardware. Default 0 (off) so legacy + //! alarms loaded from older firmware stay silent. + bool sound_enabled:1; + //! Whether vibration is *disabled* for this alarm. Default 0 (vibration on) so legacy + //! alarms loaded from older firmware keep vibrating. + bool vibrate_disabled:1; + //! Selected tone (AlarmTone enum value). Default 0 (Reveille). Irrelevant when + //! sound_enabled is 0. + uint8_t tone:3; }; uint8_t flags; }; @@ -110,7 +108,6 @@ static bool prv_alarm_get_config(SettingsFile *file, AlarmId id, AlarmConfig* co static void prv_alarm_set_config(SettingsFile *file, AlarmId id, const AlarmConfig* config); static void prv_cron_callback(CronJob *job, void* data); static void prv_snooze_alarm(int snooze_delay_s); -static bool prv_analytics_op(AlarmId id, AlarmConfig *config, void *context); static bool prv_set_alarm_kind_op(AlarmId id, AlarmConfig *config, void *context); static bool prv_set_alarm_custom_op(AlarmId id, AlarmConfig *config, void *context); @@ -156,24 +153,16 @@ static void prv_file_close_and_unlock(SettingsFile *file) { // ---------------------------------------------------------------------------------------------- static ActivitySleepState prv_get_sleep_state(void) { -#if CAPABILITY_HAS_HEALTH_TRACKING int32_t sleep_state; const bool rv = activity_get_metric(ActivityMetricSleepState, 1, &sleep_state); return rv ? sleep_state : ActivitySleepStateUnknown; -#else - return ActivitySleepStateUnknown; -#endif } // ---------------------------------------------------------------------------------------------- static int32_t prv_get_vmc(void) { -#if CAPABILITY_HAS_HEALTH_TRACKING int32_t vmc; const bool rv = activity_get_metric(ActivityMetricLastVMC, 1, &vmc); return rv ? vmc : 0; -#else - return 0; -#endif } // ---------------------------------------------------------------------------------------------- @@ -271,9 +260,7 @@ static void prv_timeline_add_alarm(SettingsFile *file, const Alarm *alarm, prv_add_pin(alarm->id, &alarm->config, alarm_time, pinid); if (updated) { - analytics_event_pin_updated(alarm_time, &entry->uuid); } else { - analytics_event_pin_created(alarm_time, &entry->uuid); } } } @@ -320,7 +307,7 @@ static void prv_assign_alarm(Alarm *alarm, CronJob *cron) { s_next_alarm_cron.cb_data = (void*)(intptr_t)alarm->id; s_next_alarm = *alarm; s_next_alarm_time = cron_job_schedule(&s_next_alarm_cron); - PBL_LOG(LOG_LEVEL_INFO, "Scheduling alarm %u to go off at %d:%d (%ld) (smart:%d)", + PBL_LOG_INFO("Scheduling alarm %u to go off at %d:%d (%ld) (smart:%d)", alarm->id, alarm->config.hour, alarm->config.minute, s_next_alarm_time, alarm->config.is_smart); } @@ -380,8 +367,7 @@ static void prv_put_alarm_event(void) { AlarmConfig *config = s_most_recent_alarm_id != ALARM_INVALID_ID ? &s_most_recent_alarm_config : NULL; - const bool is_smart = (config && config->is_smart && CAPABILITY_HAS_HEALTH_TRACKING && - activity_tracking_on()); + const bool is_smart = (config && config->is_smart && activity_tracking_on()); PebbleEvent e = (PebbleEvent) { .type = PEBBLE_ALARM_CLOCK_EVENT, .alarm_clock = { @@ -398,8 +384,6 @@ static bool prv_record_alarm_op(AlarmId id, AlarmConfig *config, void *context) // Add a pin to the timeline (will show up in the past) prv_add_pin(id, config, rtc_get_time(), NULL); - // Send one triggered event for the alarm - prv_analytics_op(id, config, (void *)(uintptr_t)AnalyticsEvent_AlarmTriggered); return false; } @@ -444,7 +428,7 @@ static void prv_snooze_kernel_bg_callback(void *unused) { // ---------------------------------------------------------------------------------------------- static void prv_snooze_timer_callback(void *unused) { - PBL_LOG(LOG_LEVEL_INFO, "Snooze timeout"); + PBL_LOG_INFO("Snooze timeout"); system_task_add_callback(prv_snooze_kernel_bg_callback, NULL); } @@ -465,9 +449,7 @@ T_STATIC void prv_timer_kernel_bg_callback(void *data) { rv = prv_alarm_get_config(file, id, config); -#if CAPABILITY_HAS_HEALTH_TRACKING s_smart_snooze_counter = 0; -#endif // If this is a just once alarm, then disable it. if (rv && config->kind == ALARM_KIND_JUST_ONCE) { @@ -482,7 +464,7 @@ T_STATIC void prv_timer_kernel_bg_callback(void *data) { kernel_free(file); - PBL_LOG(LOG_LEVEL_INFO, "Alarm %u timeout", id); + PBL_LOG_INFO("Alarm %u timeout", id); s_most_recent_alarm_recorded = false; s_most_recent_alarm_id = rv ? id : ALARM_INVALID_ID; prv_clear_snooze_timer(); @@ -504,6 +486,9 @@ static void prv_persist_alarm(SettingsFile *fd, Alarm *alarm) { .hour = alarm->config.hour, .minute = alarm->config.minute, .is_smart = alarm->config.is_smart, + .sound_enabled = alarm->config.sound_enabled, + .vibrate_disabled = alarm->config.vibrate_disabled, + .tone = alarm->config.tone, }; memcpy(&config.scheduled_days, alarm->config.scheduled_days, sizeof(config.scheduled_days)); settings_file_set(fd, &key, sizeof(key), &config, sizeof(config)); @@ -527,7 +512,7 @@ static bool prv_alarm_get_config(SettingsFile *file, AlarmId id, AlarmConfig* co const int load_size = MIN(size, (int)sizeof(config)); if (settings_file_get(file, &key, sizeof(key), &config, load_size) == S_SUCCESS) { if (config.hour > 23 || config.minute > 59) { - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid config for id %u! Blowing it out! " + PBL_LOG_DBG("Invalid config for id %u! Blowing it out! " "Hours %u Minutes %u Kind %u", id, config.hour, config.minute, config.kind); settings_file_delete(file, &key, sizeof(key)); @@ -540,6 +525,9 @@ static bool prv_alarm_get_config(SettingsFile *file, AlarmId id, AlarmConfig* co .is_disabled = config.is_disabled, .kind = config.kind, .is_smart = config.is_smart, + .sound_enabled = config.sound_enabled, + .vibrate_disabled = config.vibrate_disabled, + .tone = config.tone, }; memcpy(&config_out->scheduled_days, config.scheduled_days, sizeof(config.scheduled_days)); return true; @@ -629,6 +617,9 @@ AlarmId alarm_create(const AlarmInfo *info) { .kind = info->kind, .is_disabled = false, .is_smart = info->is_smart, + .sound_enabled = info->sound_enabled, + .vibrate_disabled = !info->vibrate_enabled, + .tone = info->tone, }; if (info->kind == ALARM_KIND_CUSTOM && info->scheduled_days) { prv_set_alarm_custom_op(id, &config, (void *)info->scheduled_days); @@ -640,7 +631,6 @@ AlarmId alarm_create(const AlarmInfo *info) { prv_add_and_schedule_alarm(&file, &alarm); prv_file_close_and_unlock(&file); - analytics_event_alarm(AnalyticsEvent_AlarmCreated, info); return id; } @@ -703,6 +693,36 @@ void alarm_set_smart(AlarmId id, bool smart) { prv_alarm_operation(id, prv_set_alarm_smart_op, (void *)(uintptr_t)smart); } +// ---------------------------------------------------------------------------------------------- +static bool prv_set_sound_enabled_op(AlarmId id, AlarmConfig *config, void *context) { + config->sound_enabled = (uintptr_t)context; + return true; +} + +void alarm_set_sound_enabled(AlarmId id, bool enabled) { + prv_alarm_operation(id, prv_set_sound_enabled_op, (void *)(uintptr_t)enabled); +} + +// ---------------------------------------------------------------------------------------------- +static bool prv_set_vibrate_enabled_op(AlarmId id, AlarmConfig *config, void *context) { + config->vibrate_disabled = !(uintptr_t)context; + return true; +} + +void alarm_set_vibrate_enabled(AlarmId id, bool enabled) { + prv_alarm_operation(id, prv_set_vibrate_enabled_op, (void *)(uintptr_t)enabled); +} + +// ---------------------------------------------------------------------------------------------- +static bool prv_set_tone_op(AlarmId id, AlarmConfig *config, void *context) { + config->tone = (uintptr_t)context; + return true; +} + +void alarm_set_tone(AlarmId id, AlarmTone tone) { + prv_alarm_operation(id, prv_set_tone_op, (void *)(uintptr_t)tone); +} + // ---------------------------------------------------------------------------------------------- static bool prv_set_alarm_kind_op(AlarmId id, AlarmConfig *config, void *context) { AlarmKind type = (uintptr_t)context; @@ -783,9 +803,11 @@ void alarm_set_enabled(AlarmId id, bool enable) { } if (id == s_most_recent_alarm_id && !enable) { - PBL_LOG(LOG_LEVEL_DEBUG, "Canceling snooze timer because alarm was disabled"); + PBL_LOG_DBG("Canceling snooze timer because alarm was disabled"); // Harmless if the alarm is not currently snoozing - the snooze timer still exists to be stopped prv_clear_snooze_timer(); + s_most_recent_alarm_id = ALARM_INVALID_ID; + s_smart_snooze_counter = 0; } prv_enable_alarm_config(&config, enable); @@ -803,8 +825,10 @@ void alarm_delete(AlarmId id) { } if (id == s_most_recent_alarm_id) { - PBL_LOG(LOG_LEVEL_DEBUG, "Canceling snooze timer on delete"); + PBL_LOG_DBG("Canceling snooze timer on delete"); prv_clear_snooze_timer(); + s_most_recent_alarm_id = ALARM_INVALID_ID; + s_smart_snooze_counter = 0; } AlarmStorageKey key = { .id = id, .type = ALARM_DATA_CONFIG }; @@ -939,9 +963,48 @@ bool alarm_get_kind(AlarmId id, AlarmKind *kind_out) { return rv; } +// ---------------------------------------------------------------------------------------------- +AlarmId alarm_get_most_recent_id(void) { + return s_most_recent_alarm_id; +} + +// ---------------------------------------------------------------------------------------------- +bool alarm_get_info(AlarmId id, AlarmInfo *info_out) { + if (!info_out) { + return false; + } + + SettingsFile file; + if (!prv_file_open_and_lock(&file)) { + return false; + } + + AlarmConfig config; + bool rv = prv_alarm_get_config(&file, id, &config); + if (!rv) { + goto cleanup; + } + + *info_out = (AlarmInfo) { + .hour = config.hour, + .minute = config.minute, + .kind = config.kind, + .enabled = !config.is_disabled, + .is_smart = config.is_smart, + .sound_enabled = config.sound_enabled, + .vibrate_enabled = !config.vibrate_disabled, + .tone = config.tone, + .scheduled_days = NULL, + }; + +cleanup: + prv_file_close_and_unlock(&file); + return rv; +} + static void prv_snooze_alarm(int snooze_delay_s) { prv_clear_snooze_timer(); - PBL_LOG(LOG_LEVEL_INFO, "Snoozing for %d minutes", snooze_delay_s / SECONDS_PER_MINUTE); + PBL_LOG_INFO("Snoozing for %d minutes", snooze_delay_s / SECONDS_PER_MINUTE); bool success = new_timer_start(s_snooze_timer_id, snooze_delay_s * MS_PER_SECOND, prv_snooze_timer_callback, NULL, 0 /* flags*/); PBL_ASSERTN(success); @@ -973,26 +1036,10 @@ void alarm_set_snooze_delay(uint16_t delay_m) { prv_file_close_and_unlock(&file); } -// ---------------------------------------------------------------------------------------------- -static bool prv_analytics_op(AlarmId id, AlarmConfig *config, void *context) { - const AlarmInfo info = { - .hour = config->hour, - .minute = config->minute, - .kind = config->kind, - .is_smart = config->is_smart, - .scheduled_days = &config->scheduled_days, - }; - - analytics_event_alarm((intptr_t)context, &info); - return false; -} void alarm_dismiss_alarm(void) { prv_clear_snooze_timer(); - // Read from flash since the in-memory cache can be modified - prv_alarm_operation(s_most_recent_alarm_id, prv_analytics_op, - (void *)(intptr_t)AnalyticsEvent_AlarmDismissed); } // ---------------------------------------------------------------------------------------------- @@ -1028,6 +1075,9 @@ static bool alarm_for_each_itr(SettingsFile *file, SettingsRecordInfo *info, voi .kind = config.kind, .enabled = !config.is_disabled, .is_smart = config.is_smart, + .sound_enabled = config.sound_enabled, + .vibrate_enabled = !config.vibrate_disabled, + .tone = config.tone, .scheduled_days = &config.scheduled_days, }; itr_data->cb(key.id, &alarm_info, itr_data->cb_data); @@ -1076,6 +1126,60 @@ void alarm_handle_clock_change(void) { return; } + // If there's an active alarm (e.g., currently snoozing), we need to handle it carefully. + // For smart alarms near their deadline, we should trigger them before clearing state. + if (s_most_recent_alarm_id != ALARM_INVALID_ID) { + bool should_force_trigger = false; + + // Only consider force-triggering for smart alarms that are in their smart snooze window + if (s_most_recent_alarm_config.is_smart && s_smart_snooze_counter > 0) { + // Check if we've used up most of our smart snooze attempts (within last 2 minutes) + if (s_smart_snooze_counter >= (SMART_ALARM_MAX_SMART_SNOOZE - 2)) { + should_force_trigger = true; + PBL_LOG_INFO("Smart alarm %u at counter %d near deadline, forcing trigger", + s_most_recent_alarm_id, s_smart_snooze_counter); + } else { + // Also check if current time is within the smart alarm window past the deadline + // This handles cases where counter < 28 but we're legitimately past the alarm time + time_t now = rtc_get_time(); + struct tm now_tm; + localtime_r(&now, &now_tm); + int current_minutes = now_tm.tm_hour * 60 + now_tm.tm_min; + int alarm_minutes = s_most_recent_alarm_config.hour * 60 + s_most_recent_alarm_config.minute; + int time_diff_minutes = current_minutes - alarm_minutes; + + // Only trigger if we're 0-30 minutes past the alarm time (the smart window) + // This prevents false triggers when time >= alarm but we're not in the window + // (e.g., alarm at 5:35 AM, current time 11 PM would give time_diff = 1045 min) + if (time_diff_minutes >= 0 && time_diff_minutes <= (SMART_ALARM_RANGE_S / 60)) { + should_force_trigger = true; + PBL_LOG_INFO("Smart alarm %u in window, %d min past deadline, forcing trigger", + s_most_recent_alarm_id, time_diff_minutes); + } + } + } + + if (should_force_trigger) { + // Trigger the alarm and let it follow its normal lifecycle (user can snooze/dismiss) + prv_put_alarm_event(); + if (!s_most_recent_alarm_recorded) { + s_most_recent_alarm_recorded = true; + prv_alarm_operation(s_most_recent_alarm_id, prv_record_alarm_op, NULL); + } + PBL_LOG_INFO("Clock change during alarm %u, triggered alarm", s_most_recent_alarm_id); + } else { + PBL_LOG_INFO("Clock change during alarm %u, clearing snooze state", + s_most_recent_alarm_id); + } + + // In either case, stop the smart snooze timer and reset the counter. + // We leave s_most_recent_alarm_id set because: + // - If we triggered: user needs it to snooze/dismiss + // - If we didn't trigger: it's harmless (timer stopped, will be overwritten by next alarm) + prv_clear_snooze_timer(); + s_smart_snooze_counter = 0; + } + // Update the day for any just once alarms for (int i = 0; i < MAX_CONFIGURED_ALARMS; ++i) { AlarmConfig config; diff --git a/src/fw/services/normal/alarms/alarm_pin.c b/src/fw/services/alarms/alarm_pin.c similarity index 77% rename from src/fw/services/normal/alarms/alarm_pin.c rename to src/fw/services/alarms/alarm_pin.c index b8567e166a..f7ab88516b 100644 --- a/src/fw/services/normal/alarms/alarm_pin.c +++ b/src/fw/services/alarms/alarm_pin.c @@ -1,28 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "alarm_pin.h" +#include "pbl/services/alarms/alarm_pin.h" #include "apps/system_app_ids.h" #include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_resources.h" // ---------------------------------------------------------------------------------------------- diff --git a/src/fw/services/alarms/alarm_tones.c b/src/fw/services/alarms/alarm_tones.c new file mode 100644 index 0000000000..b5ca8e1293 --- /dev/null +++ b/src/fw/services/alarms/alarm_tones.c @@ -0,0 +1,167 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "alarm_tones.h" + +#include "pbl/services/i18n/i18n.h" + +#define ALARM_TONE_COUNT 4 + +#define NOTE(midi, wave, ms) \ + { .midi_note = (midi), .waveform = (wave), .duration_ms = (ms), .velocity = 0, .reserved = 0 } + +// MIDI: C4=60, D4=62, E4=64, F4=65, G4=67, A4=69, B4=71, +// C5=72, D5=74, E5=76, F5=77, G5=79, A5=81, C6=84. + +// Reveille — beat-for-beat with the Reveille vibe score +static const SpeakerNote s_reveille[] = { + // note_1, _ + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6, _, note_10, note_6, note_1, _, note_10, _ + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // (repeat) + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6, _, note_10, note_6, note_1, _, note_6, _ + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_10_ (held), _, _, note_6, _, note_1, _ + NOTE(76, SpeakerWaveformSquare, 240), NOTE(0, SpeakerWaveformSquare, 240), + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6, _, note_10, note_6, note_1, _, note_10, _ + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // (repeat) + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6, _, note_10, note_6, note_1, _, note_1, _ + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6___ (long held), _, _, note_10, _ + NOTE(72, SpeakerWaveformSquare, 480), NOTE(0, SpeakerWaveformSquare, 240), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_10 × 4 + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_13_ (apex G5), _, _, note_10, _, note_10, _ + NOTE(79, SpeakerWaveformSquare, 240), NOTE(0, SpeakerWaveformSquare, 240), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_10, _, note_6, _, note_10, _, note_6, _ + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_10___ (long E5), _, _, note_10, _ + NOTE(76, SpeakerWaveformSquare, 480), NOTE(0, SpeakerWaveformSquare, 240), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_10 × 4 + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_13_ (apex G5), _, _, note_10, _, note_10, _ + NOTE(79, SpeakerWaveformSquare, 240), NOTE(0, SpeakerWaveformSquare, 240), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6, _, note_10, note_6, note_1, _, note_1, _ + NOTE(72, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(76, SpeakerWaveformSquare, 120), NOTE(72, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + NOTE(67, SpeakerWaveformSquare, 120), NOTE(0, SpeakerWaveformSquare, 120), + // note_6___ (final C5), then the 480ms of trailing rests inside the score. + NOTE(72, SpeakerWaveformSquare, 480), NOTE(0, SpeakerWaveformSquare, 480), +}; + +// Beacon — alternating C5/G5 squares, six cycles. Insistent and unambiguous. +static const SpeakerNote s_beacon[] = { + NOTE(72, SpeakerWaveformSquare, 200), NOTE(79, SpeakerWaveformSquare, 200), + NOTE(72, SpeakerWaveformSquare, 200), NOTE(79, SpeakerWaveformSquare, 200), + NOTE(72, SpeakerWaveformSquare, 200), NOTE(79, SpeakerWaveformSquare, 200), + NOTE(72, SpeakerWaveformSquare, 200), NOTE(79, SpeakerWaveformSquare, 200), + NOTE(72, SpeakerWaveformSquare, 200), NOTE(79, SpeakerWaveformSquare, 200), + NOTE(72, SpeakerWaveformSquare, 200), NOTE(79, SpeakerWaveformSquare, 200), +}; + +// Bell — sine wave descending, gentler than Reveille. +static const SpeakerNote s_bell[] = { + NOTE(81, SpeakerWaveformSine, 500), // A5 + NOTE(77, SpeakerWaveformSine, 500), // F5 + NOTE(74, SpeakerWaveformSine, 500), // D5 + NOTE(69, SpeakerWaveformSine, 700), // A4 +}; + +// Chime — triangle ascending with brief rests, light and pleasant. +static const SpeakerNote s_chime[] = { + NOTE(72, SpeakerWaveformTriangle, 250), // C5 + NOTE(0, SpeakerWaveformTriangle, 50), + NOTE(76, SpeakerWaveformTriangle, 250), // E5 + NOTE(0, SpeakerWaveformTriangle, 50), + NOTE(79, SpeakerWaveformTriangle, 250), // G5 + NOTE(0, SpeakerWaveformTriangle, 50), + NOTE(84, SpeakerWaveformTriangle, 500), // C6 +}; + +#undef NOTE + +// paired_vibe names the vibe score whose beat grid this tone is laid out +// against. When the user's selected alarm vibe matches paired_vibe, the alarm +// popup drives both subsystems off the same outer timer to keep them in +// phase. Set to VibeScoreId_Invalid for tones with no rhythmically aligned +// vibe — those loop on their own natural cadence. +static const struct { + const SpeakerNote *notes; + uint32_t count; + const char *name; + VibeScoreId paired_vibe; +} s_tones[ALARM_TONE_COUNT] = { + [AlarmTone_Reveille] = { s_reveille, sizeof(s_reveille) / sizeof(s_reveille[0]), + i18n_noop("Reveille"), VibeScoreId_Reveille }, + [AlarmTone_Beacon] = { s_beacon, sizeof(s_beacon) / sizeof(s_beacon[0]), + i18n_noop("Beacon"), VibeScoreId_Invalid }, + [AlarmTone_Bell] = { s_bell, sizeof(s_bell) / sizeof(s_bell[0]), + i18n_noop("Bell"), VibeScoreId_Invalid }, + [AlarmTone_Chime] = { s_chime, sizeof(s_chime) / sizeof(s_chime[0]), + i18n_noop("Chime"), VibeScoreId_Invalid }, +}; + +_Static_assert(AlarmTone_Chime + 1 == ALARM_TONE_COUNT, + "alarm_tones table must cover every AlarmTone enum value"); + +void alarm_tones_get(AlarmTone tone, const SpeakerNote **notes_out, uint32_t *count_out) { + if ((unsigned)tone >= ALARM_TONE_COUNT) { + tone = AlarmTone_Reveille; + } + *notes_out = s_tones[tone].notes; + *count_out = s_tones[tone].count; +} + +const char *alarm_tones_get_name(AlarmTone tone) { + if ((unsigned)tone >= ALARM_TONE_COUNT) { + tone = AlarmTone_Reveille; + } + return s_tones[tone].name; +} + +VibeScoreId alarm_tones_get_paired_vibe(AlarmTone tone) { + if ((unsigned)tone >= ALARM_TONE_COUNT) { + return VibeScoreId_Invalid; + } + return s_tones[tone].paired_vibe; +} diff --git a/src/fw/services/alarms/alarm_tones.h b/src/fw/services/alarms/alarm_tones.h new file mode 100644 index 0000000000..0a1332a3a0 --- /dev/null +++ b/src/fw/services/alarms/alarm_tones.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/vibes/vibe_score_info.h" + +//! Look up the SpeakerNote sequence for a given alarm tone. +//! @param tone The tone to look up. +//! @param notes_out Receives a pointer to the static note array. +//! @param count_out Receives the number of notes in the array. +//! Falls back to AlarmTone_Reveille for out-of-range values. +void alarm_tones_get(AlarmTone tone, const SpeakerNote **notes_out, uint32_t *count_out); + +//! Get the i18n_noop()'d display name for a tone. Caller wraps with i18n_get() at display time. +//! Falls back to the Reveille name for out-of-range values. +const char *alarm_tones_get_name(AlarmTone tone); + +//! Get the vibe score this tone is rhythmically aligned with, or +//! VibeScoreId_Invalid if the tone has no paired vibe. The alarm popup uses +//! this to decide whether to drive sound playback off the vibe outer timer so +//! both subsystems stay anchored to the same clock. +VibeScoreId alarm_tones_get_paired_vibe(AlarmTone tone); diff --git a/src/fw/services/alarms/wscript_build b/src/fw/services/alarms/wscript_build new file mode 100644 index 0000000000..5e352140af --- /dev/null +++ b/src/fw/services/alarms/wscript_build @@ -0,0 +1,20 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'alarm.c', + 'alarm_pin.c', + 'alarm_tones.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_alarms', +) + +bld.env.SERVICES.append('services_alarms') + +target = bld.path.find_or_declare('services_alarms.pot') +bld.gettext(source=sources, target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/analytics/Kconfig b/src/fw/services/analytics/Kconfig new file mode 100644 index 0000000000..73f17fcf06 --- /dev/null +++ b/src/fw/services/analytics/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ANALYTICS + bool "Analytics" + help + Firmware analytics and telemetry. + +if SERVICE_ANALYTICS + +module = SERVICE_ANALYTICS +module-str = Analytics +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/analytics/analytics.c b/src/fw/services/analytics/analytics.c new file mode 100644 index 0000000000..6c337eda57 --- /dev/null +++ b/src/fw/services/analytics/analytics.c @@ -0,0 +1,205 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/analytics/backend.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "kernel/memory_layout.h" +#include "syscall/syscall_internal.h" +#include "system/reboot_reason.h" +#include "system/version.h" +#include "util/size.h" +#include "util/time/time.h" + +#define HEARTBEAT_PERIOD_SEC 3600 +#define ANALYTICS_STRING_MAX_LEN 64 + +extern void pbl_analytics_external_collect_battery(void); +extern void pbl_analytics_external_collect_cpu_stats(void); +extern void pbl_analytics_external_collect_task_cpu_stats(void); +extern void pbl_analytics_external_collect_stack_free(void); +extern void pbl_analytics_external_collect_pfs_stats(void); +extern void pbl_analytics_external_collect_kernel_heap_stats(void); +extern void pbl_analytics_external_collect_backlight_stats(void); +extern void pbl_analytics_external_collect_vibe_stats(void); +extern void pbl_analytics_external_collect_speaker_stats(void); +extern void pbl_analytics_external_collect_settings(void); + +#if defined(ANALYTICS_NATIVE) || defined(ANALYTICS_MEMFAULT) + +#ifdef ANALYTICS_NATIVE +extern void pbl_analytics__native_init(void); +extern void pbl_analytics__native_heartbeat(void); + +extern const struct pbl_analytics_backend_ops pbl_analytics__native_ops; +#endif + +#ifdef ANALYTICS_MEMFAULT +extern void pbl_analytics__memfault_init(void); +extern void pbl_analytics__memfault_heartbeat(void); + +extern const struct pbl_analytics_backend_ops pbl_analytics__memfault_ops; +#endif + +static TimerID s_heartbeat_timer; + +static void (*const s_init[])(void) = { +#ifdef ANALYTICS_NATIVE + pbl_analytics__native_init, +#endif +#ifdef ANALYTICS_MEMFAULT + pbl_analytics__memfault_init, +#endif +}; + +static void (*const s_heartbeat[])(void) = { +#ifdef ANALYTICS_NATIVE + pbl_analytics__native_heartbeat, +#endif +#ifdef ANALYTICS_MEMFAULT + pbl_analytics__memfault_heartbeat, +#endif +}; + +static const struct pbl_analytics_backend_ops *s_backend_ops[] = { +#ifdef ANALYTICS_NATIVE + &pbl_analytics__native_ops, +#endif +#ifdef ANALYTICS_MEMFAULT + &pbl_analytics__memfault_ops, +#endif +}; + +static void prv_heartbeat_system_task_cb(void *data) { + PBL_ANALYTICS_SET_STRING(fw_version, TINTIN_METADATA.version_tag); + PBL_ANALYTICS_SET_UNSIGNED(last_reboot_reason, reboot_reason_get_last_reboot_reason()); + PBL_ANALYTICS_SET_UNSIGNED(uptime_s, time_get_uptime_seconds()); + + pbl_analytics_external_collect_battery(); + pbl_analytics_external_collect_cpu_stats(); + pbl_analytics_external_collect_task_cpu_stats(); + pbl_analytics_external_collect_stack_free(); + pbl_analytics_external_collect_pfs_stats(); + pbl_analytics_external_collect_kernel_heap_stats(); + pbl_analytics_external_collect_backlight_stats(); + pbl_analytics_external_collect_vibe_stats(); + pbl_analytics_external_collect_speaker_stats(); + pbl_analytics_external_collect_settings(); + + for (size_t i = 0U; i < ARRAY_LENGTH(s_heartbeat); i++) { + s_heartbeat[i](); + } +} + +static void prv_heartbeat_timer_cb(void *data) { + system_task_add_callback(prv_heartbeat_system_task_cb, NULL); +} + +void pbl_analytics_init(void) { + for (size_t i = 0U; i < ARRAY_LENGTH(s_init); i++) { + s_init[i](); + } + + s_heartbeat_timer = new_timer_create(); + + new_timer_start(s_heartbeat_timer, HEARTBEAT_PERIOD_SEC * 1000, prv_heartbeat_timer_cb, NULL, + TIMER_START_FLAG_REPEATING); +} + +// Apps pass `key` straight through to backends that use it as an unchecked +// index into fixed-size kernel tables (`s_key_to_integer[]`, `s_pbl_to_memfault[]`, +// `s_string_ptrs[]`, `s_string_lens[]`, ...). An out-of-range key reads +// arbitrary kernel bytes; in the set_string path those bytes become a kernel +// pointer + length that strncpy() then writes the (validated) app string into, +// turning analytics into an arbitrary kernel write primitive. Bound the key +// here once and let the backends keep their direct indexing. +static bool prv_analytics_key_in_range(enum pbl_analytics_key key) { + return ((unsigned)key < PBL_ANALYTICS_KEY_COUNT); +} + +DEFINE_SYSCALL(void, sys_pbl_analytics_set_signed, enum pbl_analytics_key key, + int32_t signed_value) { + if (!prv_analytics_key_in_range(key)) { + return; + } + for (size_t i = 0U; i < ARRAY_LENGTH(s_backend_ops); i++) { + s_backend_ops[i]->set_signed(key, signed_value); + } +} + +DEFINE_SYSCALL(void, sys_pbl_analytics_set_unsigned, enum pbl_analytics_key key, + uint32_t unsigned_value) { + if (!prv_analytics_key_in_range(key)) { + return; + } + for (size_t i = 0U; i < ARRAY_LENGTH(s_backend_ops); i++) { + s_backend_ops[i]->set_unsigned(key, unsigned_value); + } +} + +DEFINE_SYSCALL(void, sys_pbl_analytics_set_string, enum pbl_analytics_key key, + const char *value) { + if (!prv_analytics_key_in_range(key)) { + return; + } + if (PRIVILEGE_WAS_ELEVATED) { + if (!memory_layout_is_cstring_in_region( + memory_layout_get_app_region(), value, ANALYTICS_STRING_MAX_LEN)) { + syscall_failed(); + } + } + + for (size_t i = 0U; i < ARRAY_LENGTH(s_backend_ops); i++) { + s_backend_ops[i]->set_string(key, value); + } +} + +DEFINE_SYSCALL(void, sys_pbl_analytics_timer_start, enum pbl_analytics_key key) { + if (!prv_analytics_key_in_range(key)) { + return; + } + for (size_t i = 0U; i < ARRAY_LENGTH(s_backend_ops); i++) { + s_backend_ops[i]->timer_start(key); + } +} + +DEFINE_SYSCALL(void, sys_pbl_analytics_timer_stop, enum pbl_analytics_key key) { + if (!prv_analytics_key_in_range(key)) { + return; + } + for (size_t i = 0U; i < ARRAY_LENGTH(s_backend_ops); i++) { + s_backend_ops[i]->timer_stop(key); + } +} + +DEFINE_SYSCALL(void, sys_pbl_analytics_add, enum pbl_analytics_key key, int32_t amount) { + if (!prv_analytics_key_in_range(key)) { + return; + } + for (size_t i = 0U; i < ARRAY_LENGTH(s_backend_ops); i++) { + s_backend_ops[i]->add(key, amount); + } +} + +void command_analytics_heartbeat(void) { + system_task_add_callback(prv_heartbeat_system_task_cb, NULL); +} + +#else // No analytics backend: provide no-op stubs. + +void pbl_analytics_init(void) {} +DEFINE_SYSCALL(void, sys_pbl_analytics_set_signed, enum pbl_analytics_key key, + int32_t signed_value) {} +DEFINE_SYSCALL(void, sys_pbl_analytics_set_unsigned, enum pbl_analytics_key key, + uint32_t unsigned_value) {} +DEFINE_SYSCALL(void, sys_pbl_analytics_set_string, enum pbl_analytics_key key, + const char *value) {} +DEFINE_SYSCALL(void, sys_pbl_analytics_timer_start, enum pbl_analytics_key key) {} +DEFINE_SYSCALL(void, sys_pbl_analytics_timer_stop, enum pbl_analytics_key key) {} +DEFINE_SYSCALL(void, sys_pbl_analytics_add, enum pbl_analytics_key key, int32_t amount) {} +void command_analytics_heartbeat(void) {} + +#endif \ No newline at end of file diff --git a/src/fw/services/analytics/memfault.c b/src/fw/services/analytics/memfault.c new file mode 100644 index 0000000000..3033a75a5d --- /dev/null +++ b/src/fw/services/analytics/memfault.c @@ -0,0 +1,62 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/analytics/backend.h" + +#include + +extern void memfault_platform_boot_early(void); +extern void memfault_platform_heartbeat(void); + +static const MemfaultMetricId s_pbl_to_memfault[] = { +#define _PBL_TO_MEMFAULT(key) [PBL_ANALYTICS_KEY(key)] = MEMFAULT_METRICS_KEY(key), + +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) _PBL_TO_MEMFAULT(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) _PBL_TO_MEMFAULT(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) _PBL_TO_MEMFAULT(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) _PBL_TO_MEMFAULT(key) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) _PBL_TO_MEMFAULT(key) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) _PBL_TO_MEMFAULT(key) + #include "pbl/services/analytics/analytics.def" +}; + +void pbl_analytics__memfault_init(void) { + memfault_platform_boot_early(); +} + +void pbl_analytics__memfault_heartbeat(void) { + memfault_platform_heartbeat(); +} + +static void prv_set_signed(enum pbl_analytics_key key, int32_t signed_value) { + memfault_metrics_heartbeat_set_signed(s_pbl_to_memfault[key], signed_value); +} + +static void prv_set_unsigned(enum pbl_analytics_key key, uint32_t unsigned_value) { + memfault_metrics_heartbeat_set_unsigned(s_pbl_to_memfault[key], unsigned_value); +} + +static void prv_set_string(enum pbl_analytics_key key, const char *value) { + memfault_metrics_heartbeat_set_string(s_pbl_to_memfault[key], value); +} + +static void prv_timer_start(enum pbl_analytics_key key) { + memfault_metrics_heartbeat_timer_start(s_pbl_to_memfault[key]); +} + +static void prv_timer_stop(enum pbl_analytics_key key) { + memfault_metrics_heartbeat_timer_stop(s_pbl_to_memfault[key]); +} + +static void prv_add(enum pbl_analytics_key key, int32_t amount) { + memfault_metrics_heartbeat_add(s_pbl_to_memfault[key], amount); +} + +const struct pbl_analytics_backend_ops pbl_analytics__memfault_ops = { + .set_signed = prv_set_signed, + .set_unsigned = prv_set_unsigned, + .set_string = prv_set_string, + .timer_start = prv_timer_start, + .timer_stop = prv_timer_stop, + .add = prv_add, +}; \ No newline at end of file diff --git a/src/fw/services/analytics/native.c b/src/fw/services/analytics/native.c new file mode 100644 index 0000000000..8e06a0f02f --- /dev/null +++ b/src/fw/services/analytics/native.c @@ -0,0 +1,428 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "console/prompt.h" +#include "drivers/rtc.h" +#include "os/mutex.h" +#include "pbl/services/analytics/backend.h" +#include "pbl/services/system_task.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/build_id.h" +#include "util/math.h" +#include "util/size.h" +#include "util/uuid.h" + +PBL_LOG_MODULE_DEFINE(service_analytics, CONFIG_SERVICE_ANALYTICS_LOG_LEVEL); + +#define NATIVE_HEARTBEAT_RECORD_VERSION 1 + +/* Heartbeat record logged to DLS */ +struct PACKED native_heartbeat_record { + uint8_t version; + uint64_t timestamp; + uint8_t build_id[BUILD_ID_EXPECTED_LEN]; +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) uint32_t metric_##key; +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) int32_t metric_##key; +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) \ + uint32_t metric_##key; \ + uint16_t metric_##key##_scale; +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) \ + int32_t metric_##key; \ + uint16_t metric_##key##_scale; +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) uint32_t metric_##key; +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) char metric_##key[(len) + 1]; +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +}; + +/* The record is logged as a raw byte blob, so it must have no padding. */ +_Static_assert( + sizeof(struct native_heartbeat_record) == + sizeof(uint8_t) + sizeof(uint64_t) + BUILD_ID_EXPECTED_LEN +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) +sizeof(uint32_t) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) +sizeof(int32_t) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) +sizeof(uint32_t) + sizeof(uint16_t) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) +sizeof(int32_t) + sizeof(uint16_t) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) +sizeof(uint32_t) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) +((len) + 1) +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + , + "native_heartbeat_record must be packed (no padding)"); + +/* Type-specific internal index enums (dense, no gaps) */ + +enum native_integer_index { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + NATIVE_INTEGER_COUNT, +}; + +enum native_timer_index { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) NATIVE_TIMER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + NATIVE_TIMER_COUNT, +}; + +enum native_string_index { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) NATIVE_STRING_IDX_##key, +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + NATIVE_STRING_COUNT, +}; + +/* Mapping tables: global key enum -> type-specific index (-1 if N/A) */ + +static const int8_t s_key_to_integer[] = { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) NATIVE_INTEGER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) -1, +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +}; + +static const int8_t s_key_to_timer[] = { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) NATIVE_TIMER_IDX_##key, +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) -1, +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +}; + +static const int8_t s_key_to_string[] = { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) -1, +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) NATIVE_STRING_IDX_##key, +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +}; + +/* Type-specific storage */ + +static int32_t s_integer_values[NATIVE_INTEGER_COUNT]; + +static struct { + uint32_t value_ms; + bool running; + RtcTicks start_ticks; +} s_timers[NATIVE_TIMER_COUNT]; + +/* Per-string dedicated buffers (sized to declared length) */ +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) static char s_string_##key[(len) + 1]; +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + +/* String pointer and length lookup tables */ + +static char *const s_string_ptrs[] = { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) s_string_##key, +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +}; + +static const uint8_t s_string_lens[] = { +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) (len), +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +}; + +static PebbleMutex *s_mutex; +static DataLoggingSession *s_dls_session; + +extern const ElfExternalNote TINTIN_BUILD_ID; + +static void prv_record_metrics(struct native_heartbeat_record *record, bool reset) { + uint32_t timer_value_ms[NATIVE_TIMER_COUNT]; + + memset(record, 0, sizeof(*record)); + + record->version = NATIVE_HEARTBEAT_RECORD_VERSION; + record->timestamp = rtc_get_time(); + memcpy(record->build_id, &TINTIN_BUILD_ID.data[TINTIN_BUILD_ID.name_length], + MIN(BUILD_ID_EXPECTED_LEN, TINTIN_BUILD_ID.data_length)); + + RtcTicks now = rtc_get_ticks(); + for (size_t i = 0; i < NATIVE_TIMER_COUNT; i++) { + timer_value_ms[i] = s_timers[i].value_ms; + if (s_timers[i].running) { + RtcTicks elapsed = now - s_timers[i].start_ticks; + timer_value_ms[i] += (elapsed * 1000) / RTC_TICKS_HZ; + if (reset) { + s_timers[i].value_ms = timer_value_ms[i]; + s_timers[i].start_ticks = now; + } + } + } + + /* Build heartbeat record from type-specific storage */ +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) \ + record->metric_##key = (uint32_t)s_integer_values[NATIVE_INTEGER_IDX_##key]; +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) \ + record->metric_##key = s_integer_values[NATIVE_INTEGER_IDX_##key]; +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) \ + record->metric_##key = (uint32_t)s_integer_values[NATIVE_INTEGER_IDX_##key]; \ + record->metric_##key##_scale = (scale); +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) \ + record->metric_##key = s_integer_values[NATIVE_INTEGER_IDX_##key]; \ + record->metric_##key##_scale = (scale); +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) \ + record->metric_##key = timer_value_ms[NATIVE_TIMER_IDX_##key]; +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) \ + strncpy(record->metric_##key, s_string_##key, (len)); \ + record->metric_##key[(len)] = '\0'; +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING + + if (reset) { + /* Reset storage for next heartbeat period, keeping running timers active */ + memset(s_integer_values, 0, sizeof(s_integer_values)); + for (size_t i = 0; i < NATIVE_TIMER_COUNT; i++) { + s_timers[i].value_ms = 0; + } + for (size_t i = 0; i < NATIVE_STRING_COUNT; i++) { + s_string_ptrs[i][0] = '\0'; + } + } +} + +void pbl_analytics__native_init(void) { + s_mutex = mutex_create(); + PBL_ASSERTN(s_mutex != NULL); +} + +void pbl_analytics__native_heartbeat(void) { + struct native_heartbeat_record record; + + mutex_lock(s_mutex); + prv_record_metrics(&record, true); + mutex_unlock(s_mutex); + + if (s_dls_session == NULL) { + Uuid system_uuid = UUID_SYSTEM; + + s_dls_session = dls_create(DlsSystemTagAnalyticsNativeHeartbeat, DATA_LOGGING_BYTE_ARRAY, + sizeof(struct native_heartbeat_record), false, false, &system_uuid); + PBL_ASSERTN(s_dls_session != NULL); + } + + DataLoggingResult result = dls_log(s_dls_session, &record, 1); + if (result != DATA_LOGGING_SUCCESS) { + PBL_LOG_ERR("Native analytics DLS log failed: %d", result); + } +} + +static void prv_set_signed(enum pbl_analytics_key key, int32_t signed_value) { + int8_t idx = s_key_to_integer[key]; + if (idx < 0) { + return; + } + mutex_lock(s_mutex); + s_integer_values[idx] = signed_value; + mutex_unlock(s_mutex); +} + +static void prv_set_unsigned(enum pbl_analytics_key key, uint32_t unsigned_value) { + int8_t idx = s_key_to_integer[key]; + if (idx < 0) { + return; + } + mutex_lock(s_mutex); + s_integer_values[idx] = (int32_t)unsigned_value; + mutex_unlock(s_mutex); +} + +static void prv_set_string(enum pbl_analytics_key key, const char *value) { + int8_t idx = s_key_to_string[key]; + if (idx < 0) { + return; + } + mutex_lock(s_mutex); + strncpy(s_string_ptrs[idx], value, s_string_lens[idx]); + s_string_ptrs[idx][s_string_lens[idx]] = '\0'; + mutex_unlock(s_mutex); +} + +static void prv_timer_start(enum pbl_analytics_key key) { + int8_t idx = s_key_to_timer[key]; + if (idx < 0) { + return; + } + mutex_lock(s_mutex); + if (!s_timers[idx].running) { + s_timers[idx].running = true; + s_timers[idx].start_ticks = rtc_get_ticks(); + } + mutex_unlock(s_mutex); +} + +static void prv_timer_stop(enum pbl_analytics_key key) { + int8_t idx = s_key_to_timer[key]; + if (idx < 0) { + return; + } + mutex_lock(s_mutex); + if (s_timers[idx].running) { + RtcTicks elapsed = rtc_get_ticks() - s_timers[idx].start_ticks; + s_timers[idx].value_ms += (int32_t)((elapsed * 1000) / RTC_TICKS_HZ); + s_timers[idx].running = false; + } + mutex_unlock(s_mutex); +} + +static void prv_add(enum pbl_analytics_key key, int32_t amount) { + int8_t idx = s_key_to_integer[key]; + if (idx < 0) { + return; + } + mutex_lock(s_mutex); + s_integer_values[idx] += amount; + mutex_unlock(s_mutex); +} + +const struct pbl_analytics_backend_ops pbl_analytics__native_ops = { + .set_signed = prv_set_signed, + .set_unsigned = prv_set_unsigned, + .set_string = prv_set_string, + .timer_start = prv_timer_start, + .timer_stop = prv_timer_stop, + .add = prv_add, +}; + +void command_analytics_native_metrics_dump(void) { + struct native_heartbeat_record record; + char buffer[64]; + + prv_record_metrics(&record, false); + +#define PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED(key) \ + prompt_send_response_fmt(buffer, sizeof(buffer), STRINGIFY(key) "=%" PRIu32, record.metric_##key); +#define PBL_ANALYTICS_METRIC_DEFINE_SIGNED(key) \ + prompt_send_response_fmt(buffer, sizeof(buffer), STRINGIFY(key) "=%" PRId32, record.metric_##key); +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED(key, scale) \ + prompt_send_response_fmt(buffer, sizeof(buffer), STRINGIFY(key) "=%" PRIu32 ".%" PRIu32, \ + record.metric_##key / (scale), \ + record.metric_##key - (record.metric_##key / (scale)) * (scale)); +#define PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED(key, scale) \ + prompt_send_response_fmt( \ + buffer, sizeof(buffer), STRINGIFY(key) "=%" PRId32 ".%" PRIu32, \ + record.metric_##key / (scale), \ + (uint32_t)(record.metric_##key - (record.metric_##key / (scale)) * (scale))); +#define PBL_ANALYTICS_METRIC_DEFINE_TIMER(key) \ + prompt_send_response_fmt(buffer, sizeof(buffer), STRINGIFY(key) "=%" PRId32 " ms", \ + record.metric_##key); +#define PBL_ANALYTICS_METRIC_DEFINE_STRING(key, len) \ + prompt_send_response_fmt(buffer, sizeof(buffer), STRINGIFY(key) "=%s", record.metric_##key); +#include "pbl/services/analytics/analytics.def" +#undef PBL_ANALYTICS_METRIC_DEFINE_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_UNSIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_SCALED_SIGNED +#undef PBL_ANALYTICS_METRIC_DEFINE_TIMER +#undef PBL_ANALYTICS_METRIC_DEFINE_STRING +} \ No newline at end of file diff --git a/src/fw/services/analytics/wscript_build b/src/fw/services/analytics/wscript_build new file mode 100644 index 0000000000..7ac16b87e0 --- /dev/null +++ b/src/fw/services/analytics/wscript_build @@ -0,0 +1,22 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +use = ['fw_includes'] +sources = ['analytics.c'] + +if bld.env.VARIANT != 'prf': + sources.append('native.c') + bld.env.DEFINES.append('ANALYTICS_NATIVE') + +if bld.env.memfault: + use.append('memfault_includes') + sources.append('memfault.c') + bld.env.DEFINES.append('ANALYTICS_MEMFAULT') + +bld.stlib( + source=sources, + target='services_analytics', + use=use +) + +bld.env.SERVICES.append('services_analytics') \ No newline at end of file diff --git a/src/fw/services/animation_service/Kconfig b/src/fw/services/animation_service/Kconfig new file mode 100644 index 0000000000..66624de07e --- /dev/null +++ b/src/fw/services/animation_service/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ANIMATION_SERVICE + bool "Animation" + help + Animation scheduling service. diff --git a/src/fw/services/animation_service/service.c b/src/fw/services/animation_service/service.c new file mode 100644 index 0000000000..b17f04d83c --- /dev/null +++ b/src/fw/services/animation_service/service.c @@ -0,0 +1,133 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/ui/animation_private.h" +#include "applib/app_logging.h" + +#include "kernel/events.h" +#include "kernel/kernel_applib_state.h" + +#include "process_management/process_manager.h" +#include "process_state/app_state/app_state.h" + +#include "pbl/services/new_timer/new_timer.h" + +#include "system/passert.h" + +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" + + +// The timer ID used for each task that we support +static TimerID s_kernel_main_timer_id = TIMER_INVALID_ID; +static TimerID s_app_timer_id = TIMER_INVALID_ID; + +static bool s_kernel_main_event_pending; +static bool s_app_event_pending; + +// ------------------------------------------------------------------------------------------ +void animation_service_cleanup(PebbleTask task) { + PBL_ASSERT_TASK(PebbleTask_KernelMain); + + if (task == PebbleTask_KernelMain) { + if (s_kernel_main_timer_id != TIMER_INVALID_ID) { + new_timer_delete(s_kernel_main_timer_id); + s_kernel_main_timer_id = TIMER_INVALID_ID; + } + s_kernel_main_event_pending = false; + } else if (task == PebbleTask_App) { + if (s_app_timer_id != TIMER_INVALID_ID) { + new_timer_delete(s_app_timer_id); + s_app_timer_id = TIMER_INVALID_ID; + } + s_app_event_pending = false; + } +} + + +// ------------------------------------------------------------------------------------------ +static void prv_timer_callback(void * context) { + PebbleTask task = (PebbleTask)context; + + PebbleEvent e = { + .type = PEBBLE_CALLBACK_EVENT, + .callback = { + .callback = animation_private_timer_callback + } + }; + + switch (task) { + case PebbleTask_KernelMain: + if (!s_kernel_main_event_pending) { + s_kernel_main_event_pending = true; + e.callback.data = (void *)kernel_applib_get_animation_state(); + event_put(&e); + } + break; + case PebbleTask_App: + if (!s_app_event_pending) { + e.callback.data = (void *)app_state_get_animation_state(); + s_app_event_pending = process_manager_send_event_to_process(task, &e); + } + break; + default: + PBL_CROAK("Invalid task %s", pebble_task_get_name(pebble_task_get_current())); + } +} + + +// ------------------------------------------------------------------------------------------ +DEFINE_SYSCALL(void, animation_service_timer_event_received, void) { + PebbleTask task = pebble_task_get_current(); + + if (task == PebbleTask_KernelMain) { + s_kernel_main_event_pending = false; + } else if (task == PebbleTask_App) { + s_app_event_pending = false; + } else { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_failed(); + } + return; + } +} + + +// ------------------------------------------------------------------------------------------ +DEFINE_SYSCALL(void, animation_service_timer_schedule, uint32_t ms) { + PebbleTask task = pebble_task_get_current(); + TimerID *timer_id; + + if (task == PebbleTask_KernelMain) { + timer_id = &s_kernel_main_timer_id; + } else if (task == PebbleTask_App) { + timer_id = &s_app_timer_id; + } else { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_failed(); + } + return; + } + + // Need to create the timer? + bool success = false; + if (*timer_id == TIMER_INVALID_ID) { + *timer_id = new_timer_create(); + } + + // Schedule/reschedule it + if (*timer_id != TIMER_INVALID_ID) { + success = new_timer_start(*timer_id, ms, prv_timer_callback, (void *)(uintptr_t)task, + 0 /*flags */); + } + if (!success) { + APP_LOG(APP_LOG_LEVEL_ERROR, "Error scheduling timer"); + } +} + + +// --------------------------------------------------------------------------- +// Used for unit tests only +TimerID animation_service_test_get_timer_id(void) { + return s_kernel_main_timer_id; +} diff --git a/src/fw/services/animation_service/wscript_build b/src/fw/services/animation_service/wscript_build new file mode 100644 index 0000000000..da04cb150a --- /dev/null +++ b/src/fw/services/animation_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_animation_service', +) +bld.env.SERVICES.append('services_animation_service') diff --git a/src/fw/services/app_cache/Kconfig b/src/fw/services/app_cache/Kconfig new file mode 100644 index 0000000000..608d281fb8 --- /dev/null +++ b/src/fw/services/app_cache/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_CACHE + bool "App cache" + help + On-flash app cache. + +if SERVICE_APP_CACHE + +module = SERVICE_APP_CACHE +module-str = App cache +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/app_cache/service.c b/src/fw/services/app_cache/service.c new file mode 100644 index 0000000000..8ce8b951cb --- /dev/null +++ b/src/fw/services/app_cache/service.c @@ -0,0 +1,592 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/app_cache.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "kernel/pebble_tasks.h" +#include "process_management/app_install_manager.h" +#include "pbl/services/process_management/app_storage.h" +#include "pbl/services/system_task.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/filesystem/app_file.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/settings/settings_file.h" +#include "shell/normal/quick_launch.h" +#include "shell/normal/watchface.h" +#include "shell/prefs.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/list.h" +#include "util/math.h" +#include "util/time/time.h" +#include "util/units.h" + +PBL_LOG_MODULE_DEFINE(service_app_cache, CONFIG_SERVICE_APP_CACHE_LOG_LEVEL); + +//! @file app_cache.c +//! App Cache + +//! The App Cache keeps track of the install date, last launch, launch count, and size of an +//! application. +//! +//! A priority can also be calculated for each entry. It is calculated by a simple last used +//! algorithm (TODO Improve: PBL-13209) which will help determine which application +//! needs to be evicted in order to free up more space for other application binaries. +//! +//! When an entry is added into the app cache, it means the binaries now reside on the watch. On +//! this function call, a callback is initiated to check if we need to free space for a possible +//! future application. If so, the applications with the lowest priority that add up to or are +//! greater than the space needed will be removed. +//! +//! It is assumed that there will ALWAYS be space for a single application of maximum size based +//! on the platform. The only time when this isn't true is the time between "add_entry" and the +//! callback to clean up the cache. + +#define APP_CACHE_FILE_NAME "appcache" + +//! each cache entry is ~16 bytes, 4000 / 16 = 250 apps +#define APP_CACHE_MAX_SIZE 4000 + +//! Keep enough room for the maximum sized application based on platform, plus a little more room. +//! Source: https://pebbletechnology.atlassian.net/wiki/display/DEV/PBW+3.0 +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(UNITTEST) +#define APP_SPACE_BUFFER KiBYTES(300) +#else +#define APP_SPACE_BUFFER MiBYTES(4) +#endif + +#define MAX_PRIORITY ((uint32_t)~0) + +// 4 quick launch apps, 1 default watchface, 1 default worker +#define DO_NOT_EVICT_LIST_SIZE (NUM_BUTTONS + 2) + +static PebbleRecursiveMutex *s_app_cache_mutex = NULL; + +//! Actual data structure stored in flash about an app cache entry +typedef struct PACKED { + time_t install_date; + time_t last_launch; + uint32_t total_size; + uint16_t launch_count; +} AppCacheEntry; + +typedef struct { + ListNode node; + AppInstallId id; + uint32_t size; + uint32_t priority; +} EvictListNode; + +typedef struct { + EvictListNode *list; + uint32_t bytes_needed; + uint32_t bytes_in_list; + const AppInstallId do_not_evict[DO_NOT_EVICT_LIST_SIZE]; +} EachEvictData; + +//! Takes the information given in entry and calculates a new priority for the app. +//! +//! Policy rules: +//! 1. App that has least recently launched or been installed app is evicted. +static uint32_t prv_calculate_priority(AppCacheEntry *entry) { + return (uint32_t) MAX(entry->last_launch, entry->install_date); +} + +//! Comparator for EvictListNode +static int evict_node_comparator(void *a, void *b) { + EvictListNode *a_node = (EvictListNode *)a; + EvictListNode *b_node = (EvictListNode *)b; + + if (b_node->priority > a_node->priority) { + return 1; + } else if (b_node->priority < a_node->priority) { + return -1; + } else { + // bigger applications to have a lower priority + if (b_node->size < a_node->size) { + return 1; + } else if (b_node->size > a_node->size) { + return -1; + } else { + return 0; + } + } +} + +//! Trim the applications with highest priority while still keeping (bytes_in_list > bytes_needed) +static void prv_trim_top_priorities(EvictListNode **list_node, uint32_t *bytes_in_list, + uint32_t bytes_needed) { + EvictListNode *node = *list_node; + while (node) { + EvictListNode *temp = node; + if (node->size <= (*bytes_in_list - bytes_needed)) { + *bytes_in_list -= node->size; + node = (EvictListNode *)list_pop_head((ListNode *)node); + kernel_free(temp); + } else { + break; + } + } + *list_node = node; +} + +//! Check if we need to free up some space in the cache. If so, do it. +static void prv_cleanup_app_cache_if_needed(void *data) { + uint32_t pfs_space = get_available_pfs_space(); + + if (pfs_space < APP_SPACE_BUFFER) { + const uint32_t to_free = (APP_SPACE_BUFFER - pfs_space); + PBL_LOG_DBG("Cache OOS: Need to free %"PRIu32" bytes, PFS avail space: %"PRIu32"", + to_free, pfs_space); + app_cache_free_up_space(to_free); + } +} + +static void prv_delete_cache_callback(void *data) { + app_cache_flush(); +} + +static void prv_delete_cached_files(void) { + pfs_remove_files(is_app_file_name); +} + +static bool prv_is_in_list(AppInstallId id, const AppInstallId list[], uint8_t len) { + for (unsigned int i = 0; i < len; i++) { + if (list[i] == id) { + return true; + } + } + return false; +} + +////////////////////// +// Settings Helpers +////////////////////// + +//! Settings iterator function that finds the entry with the lowest calculated priority +static bool prv_each_free_up_space(SettingsFile *file, SettingsRecordInfo *info, void *context) { + // check entry is valid + if ((info->key_len != sizeof(AppInstallId)) || (info->val_len != sizeof(AppCacheEntry))) { + PBL_LOG_WRN("Invalid cache entry with key_len: %u and val_len: %u, flushing", + info->key_len, info->val_len); + system_task_add_callback(prv_delete_cache_callback, NULL); + return false; // stop iterating, delete the file and binaries + } + + EachEvictData *data = (EachEvictData *)context; + + AppInstallId id; + AppCacheEntry entry; + + info->get_key(file, (uint8_t *)&id, info->key_len); + info->get_val(file, (uint8_t *)&entry, info->val_len); + + // create node + EvictListNode *node = kernel_malloc_check(sizeof(EvictListNode)); + list_init((ListNode *)node); + + // give them an extremely high priority so that we only remove them if we really NEED to + // This list contains defaults that we shouldn't be removing. + uint32_t priority = 0; + if (prv_is_in_list(id, data->do_not_evict, DO_NOT_EVICT_LIST_SIZE)) { + priority = MAX_PRIORITY; + } + + *node = (EvictListNode) { + .id = id, + .size = entry.total_size, + .priority = MAX(priority, prv_calculate_priority(&entry)), + }; + + data->list = (EvictListNode *)list_sorted_add((ListNode *)data->list, (ListNode *)node, + evict_node_comparator, false); + data->bytes_in_list += node->size; + + if (data->bytes_in_list > data->bytes_needed) { + prv_trim_top_priorities(&data->list, &data->bytes_in_list, data->bytes_needed); + } + + return true; // continue iterating +} + +////////////////////////// +// AppCache API's +////////////////////////// + +//! Updates metadata within the cache entry for the given AppInstallId. Will update such fields as +//! launch count, last launch, and priority +status_t app_cache_app_launched(AppInstallId app_id) { + status_t rv; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + AppCacheEntry entry = { 0 }; + rv = settings_file_get(&file, (uint8_t *)&app_id, sizeof(AppInstallId), + (uint8_t *)&entry, sizeof(AppCacheEntry)); + + if (rv == S_SUCCESS) { + entry.last_launch = rtc_get_time(); + entry.launch_count += 1; + + rv = settings_file_set(&file, (uint8_t *)&app_id, sizeof(AppInstallId), + (uint8_t *)&entry, sizeof(AppCacheEntry)); + } else { + app_storage_delete_app(app_id); + settings_file_delete(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); + } + + settings_file_close(&file); + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return rv; +} + +//! Asks the app cache to remove 'bytes_needed' bytes of application binaries to free up space +//! for other things. +status_t app_cache_free_up_space(uint32_t bytes_needed) { + if (bytes_needed == 0) { + return E_INVALID_ARGUMENT; + } + + status_t rv; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + // we don't want to remove any default apps or quick launch apps, so keep them in a list. + EachEvictData evict_data = (EachEvictData) { + .bytes_needed = bytes_needed, + .do_not_evict = { +#ifndef CONFIG_SHELL_SDK + quick_launch_get_app(BUTTON_ID_UP), + quick_launch_get_app(BUTTON_ID_SELECT), + quick_launch_get_app(BUTTON_ID_DOWN), + quick_launch_get_app(BUTTON_ID_BACK), +#endif + watchface_get_default_install_id(), + worker_preferences_get_default_worker(), + }, + }; + + settings_file_each(&file, prv_each_free_up_space, &evict_data); + settings_file_close(&file); + + // remove all nodes found + EvictListNode *node = evict_data.list; + while (node) { + EvictListNode *temp = node; + PBL_LOG_DBG("Deleting application binaries for app id: %"PRIu32", size: %"PRIu32, + node->id, node->size); + app_cache_remove_entry(node->id); + node = (EvictListNode *)list_pop_head((ListNode *)node); + kernel_free(temp); + } + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return rv; +} + +////////////////////// +// AppCache Helpers +////////////////////// + +// Remove the filename entry in the PFSFileList (via context) that corresponds to the +// app install id passed in via info +static bool prv_remove_matching_resource_file_callback(SettingsFile *file, + SettingsRecordInfo *info, + void *context) { + AppInstallId id; + // examine the SettingsRecordInfo and extract the AppInstallId from it + info->get_key(file, (uint8_t *)&id, info->key_len); + // the context passed in is really a pointer to the resource_list + PFSFileListEntry **resource_list = context; + PFSFileListEntry *iter = *resource_list; + while (iter) { + // grab the next entry right now since we may delete the node we're looking at + PFSFileListEntry *next = (PFSFileListEntry *)iter->list_node.next; + if (app_file_parse_app_id(iter->name) == id) { + // the AppInstallId of the file matches the one in the cache so we can remove this + // entry from the resource_list (since we don't want to delete it) + // note: resource_list may be updated if we happen to remove the first entry in the list + list_remove(&(iter->list_node), (ListNode**)resource_list, NULL); + kernel_free(iter); // free up the memory for the node we just removed + break; // we can quit now that we've found a match for this id + } + iter = next; + } + return true; +} + +// Delete files from resource_list that don't correspond to entries in the app cache +static void prv_app_cache_find_and_delete_orphans(PFSFileListEntry **resource_list) { + mutex_lock_recursive(s_app_cache_mutex); + + SettingsFile file; + status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + mutex_unlock_recursive(s_app_cache_mutex); + return; + } + // resource_list contains all of the resource files we found. We only + // want to delete orphans so we can remove any entries from the list that correspond + // to items in the app cache... + // prv_remove_matching_resource_file_callback scans resource_list and removes the entry + // corresponding to the passed-in application's id + settings_file_each(&file, prv_remove_matching_resource_file_callback, resource_list); + settings_file_close(&file); + + mutex_unlock_recursive(s_app_cache_mutex); + + // resource_list now only contains filenames of resource files that don't have corresponding + // entries in the app cache. We can safely delete these files. + PFSFileListEntry *iter = *resource_list; + while (iter) { + PBL_LOG_INFO("Orphaned resource file removed: %s", iter->name); + pfs_remove(iter->name); + iter = (PFSFileListEntry *)iter->list_node.next; + } +} + +// The bug addressed in PBL-34010 caused resource files to remain in the filesystem even +// after the associated application had been deleted. This function attempts to find such +// orphaned files and remove them. Note: further to the bug in PBL-34010, this function will +// remove any resource files that are not related to apps currently in the cache. +static void prv_purge_orphaned_resource_files(void) { + // create a list of all app resource files in the filesystem + PFSFileListEntry *resource_files = pfs_create_file_list(is_app_resource_file_name); + // delete app resource files that don't correspond to entries in the app cache + prv_app_cache_find_and_delete_orphans(&resource_files); + pfs_delete_file_list(resource_files); +} + +////////////////////////// +// AppCache Settings API's +////////////////////////// + +//! Set up the app cache +void app_cache_init(void) { + s_app_cache_mutex = mutex_create_recursive(); + + mutex_lock_recursive(s_app_cache_mutex); + { + // if no cache file exists, then we should go ahead and clean up any files that are left over + int fd = pfs_open(APP_CACHE_FILE_NAME, OP_FLAG_READ, FILE_TYPE_STATIC, 0); + if (fd < 0) { + prv_delete_cached_files(); + goto unlock; + } + pfs_close(fd); + } + +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + + prv_purge_orphaned_resource_files(); +} + +//! Adds an entry with the given AppInstallId to the cache +status_t app_cache_add_entry(AppInstallId app_id, uint32_t total_size) { + status_t rv; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + AppCacheEntry entry = { + .install_date = rtc_get_time(), + .last_launch = 0, + .launch_count = 0, + .total_size = total_size, + }; + + rv = settings_file_set(&file, (uint8_t *)&app_id, sizeof(AppInstallId), + (uint8_t *)&entry, sizeof(AppCacheEntry)); + + settings_file_close(&file); + + // cleanup the cache if we need to + system_task_add_callback(prv_cleanup_app_cache_if_needed, NULL); + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return rv; +} + +//! Tests if an entry with the given AppInstallId is in the cache +bool app_cache_entry_exists(AppInstallId app_id) { + bool exists = false; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + exists = settings_file_exists(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); + + if (exists && !app_storage_app_exists(app_id)) { + settings_file_delete(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); + exists = false; + } + + settings_file_close(&file); + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return exists; +} + +//! Removes an entry with the given AppInstallId from the cache +status_t app_cache_remove_entry(AppInstallId app_id) { + status_t rv; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + rv = settings_file_delete(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); + if (rv == S_SUCCESS) { + // Will delete an app from the filesystem. + app_storage_delete_app(app_id); + } + + settings_file_close(&file); + } + + if (rv == S_SUCCESS) { + PebbleEvent e = { + .type = PEBBLE_APP_CACHE_EVENT, + .app_cache_event = { + .cache_event_type = PebbleAppCacheEvent_Removed, + .install_id = app_id, + }, + }; + event_put(&e); + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return rv; +} + +void app_cache_flush(void) { + PBL_ASSERT_TASK(PebbleTask_KernelBackground); + + mutex_lock_recursive(s_app_cache_mutex); + { + pfs_remove(APP_CACHE_FILE_NAME); + prv_delete_cached_files(); + } + mutex_unlock_recursive(s_app_cache_mutex); +} + +//////////////////////////////// +// Testing only +//////////////////////////////// + +static bool prv_each_get_size(SettingsFile *file, SettingsRecordInfo *info, void *context) { + if ((info->key_len != sizeof(AppInstallId)) || (info->val_len != sizeof(AppCacheEntry))) { + return true; // continue iterating + } + + uint32_t *cache_size = (uint32_t *)context; + AppCacheEntry entry; + info->get_val(file, (uint8_t *)&entry, info->val_len); + *cache_size += entry.total_size; + + return true; // continue iterating +} + +uint32_t app_cache_get_size(void) { + uint32_t cache_size = 0; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + settings_file_each(&file, prv_each_get_size, &cache_size); + settings_file_close(&file); + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return cache_size; +} + +typedef struct { + AppInstallId id; + uint32_t priority; +} AppCacheEachData; + +//! Settings iterator function that finds the entry with the lowest calculated priority +static bool prv_each_min_priority(SettingsFile *file, SettingsRecordInfo *info, void *context) { + // check entry is valid + if ((info->key_len != sizeof(AppInstallId)) || (info->val_len != sizeof(AppCacheEntry))) { + return true; // continue iterating + } + + AppCacheEachData *to_evict = (AppCacheEachData *)context; + + AppInstallId id; + AppCacheEntry entry; + + info->get_key(file, (uint8_t *)&id, info->key_len); + info->get_val(file, (uint8_t *)&entry, info->val_len); + + uint32_t entry_priority = prv_calculate_priority(&entry); + if (entry_priority < to_evict->priority) { + to_evict->id = id; + to_evict->priority = entry_priority; + } + + return true; // continue iterating +} + +//! Find the entry in the app cache with the lowest calculated priority +AppInstallId app_cache_get_next_eviction(void) { + AppInstallId ret_value = INSTALL_ID_INVALID; + mutex_lock_recursive(s_app_cache_mutex); + { + SettingsFile file; + status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); + if (rv != S_SUCCESS) { + goto unlock; + } + + // set max so that any application will have a lower priority. + AppCacheEachData to_evict = { + .id = INSTALL_ID_INVALID, + .priority = MAX_PRIORITY, + }; + settings_file_each(&file, prv_each_min_priority, (void *)&to_evict); + + settings_file_close(&file); + ret_value = to_evict.id; + } +unlock: + mutex_unlock_recursive(s_app_cache_mutex); + return ret_value; +} diff --git a/src/fw/services/app_cache/wscript_build b/src/fw/services/app_cache/wscript_build new file mode 100644 index 0000000000..3daa0bd0a0 --- /dev/null +++ b/src/fw/services/app_cache/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_app_cache', +) +bld.env.SERVICES.append('services_app_cache') diff --git a/src/fw/services/app_fetch_endpoint/Kconfig b/src/fw/services/app_fetch_endpoint/Kconfig new file mode 100644 index 0000000000..6a87d5d79a --- /dev/null +++ b/src/fw/services/app_fetch_endpoint/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_FETCH_ENDPOINT + bool "App fetch endpoint" + help + Endpoint used by the phone to fetch apps on demand. + +if SERVICE_APP_FETCH_ENDPOINT + +module = SERVICE_APP_FETCH_ENDPOINT +module-str = App fetch endpoint +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/app_fetch_endpoint/service.c b/src/fw/services/app_fetch_endpoint/service.c new file mode 100644 index 0000000000..28ba90328b --- /dev/null +++ b/src/fw/services/app_fetch_endpoint/service.c @@ -0,0 +1,423 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/app_fetch_endpoint.h" + +#include +#include + +#include "kernel/pbl_malloc.h" +#include "process_management/pebble_process_info.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/put_bytes/put_bytes.h" +#include "pbl/services/system_task.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/app_db.h" +#include "pbl/services/process_management/app_storage.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/math.h" +#include "util/uuid.h" + +PBL_LOG_MODULE_DEFINE(service_app_fetch_endpoint, CONFIG_SERVICE_APP_FETCH_ENDPOINT_LOG_LEVEL); + +//! Used for keeping track of binaries that are loaded through put_bytes +typedef struct { + AppInstallId app_id; + uint32_t total_size; + AppFetchResult prev_error; + bool cancelling; + bool in_progress; + bool app; + bool worker; + bool resources; +} AppFetchState; + +//! Command type +enum { + APP_FETCH_INSTALL_COMMAND = 0x01, +} AppFetchCommand; + +//! Possible results that come back from the INSTALL_COMMAND +enum { + APP_FETCH_INSTALL_RESPONSE = 0x01, +} AppFetchResponse; + +//! Possible results that come back from the INSTALL_COMMAND +enum { + APP_FETCH_RESPONSE_STARTING = 0x01, + APP_FETCH_RESPONSE_BUSY = 0x02, + APP_FETCH_RESPONSE_UUID_INVALID = 0x03, + APP_FETCH_RESPONSE_NO_DATA = 0x04, +} AppFetchInstallResult; + +//! Data sent to mobile phone for an INSTALL_COMMAND +typedef struct PACKED { + uint8_t command; + Uuid uuid; + AppInstallId app_id; +} AppFetchInstallRequest; + +//! Timeout used to determine how long we should wait before the phone starts sending the app +//! we requested (by issuing a put_bytes request). +#define FETCH_TIMEOUT_MS 15000 + +//! State for the app fetch flow +static AppFetchState s_fetch_state; + +//! Endpoint ID +static const uint16_t APP_FETCH_ENDPOINT_ID = 6001; + +//////////////////////////// +// Internal Helper Functions +//////////////////////////// + +//! Puts an error event with the given error code +static void prv_put_event_error(uint8_t error_code) { + s_fetch_state.prev_error = error_code; + PebbleEvent event = { + .type = PEBBLE_APP_FETCH_EVENT, + .app_fetch = { + .type = AppFetchEventTypeError, + .id = s_fetch_state.app_id, + .error_code = error_code, + } + }; + event_put(&event); +} + +//! Puts an event with the given progress +static void prv_put_event_progress(uint8_t percent) { + PebbleEvent event = { + .type = PEBBLE_APP_FETCH_EVENT, + .app_fetch = { + .type = AppFetchEventTypeProgress, + .id = s_fetch_state.app_id, + .progress_percent = percent, + } + }; + event_put(&event); +} + +//! Simply posts the type of event given. +static void prv_put_event_simple(AppFetchEventType type) { + PebbleEvent event = { + .type = PEBBLE_APP_FETCH_EVENT, + .app_fetch = { + .type = type, + .id = s_fetch_state.app_id, + } + }; + event_put(&event); +} + + +//! Recomputes and saves the progress percent for the current application fetch session +static uint8_t prv_compute_progress_percent(PutBytesObjectType type, unsigned int type_percent) { + // Add 33(34) percent for each piece that has finished (or is unneeded) + uint8_t percent = 0; + if (s_fetch_state.app) { + percent += 30; + } + if (s_fetch_state.worker) { + percent += 10; + } + if (s_fetch_state.resources) { + percent += 60; + } + + // add in the progress for the currently transferring piece. + percent += (type_percent / 3); + + // store value + return MIN(100, percent); +} + +//! Cleans up the state of the app fetch endpoint. Always called from the system task +static void prv_cleanup(AppFetchResult result) { + if (result != AppFetchResultSuccess) { + put_bytes_cancel(); + app_cache_remove_entry(s_fetch_state.app_id); + prv_put_event_error(result); + } + + s_fetch_state.in_progress = false; + + PBL_LOG_INFO("App fetch cleanup with result %d", result); +} + +//! System task callback triggered by app_fetch_put_bytes_event_handler() when we are receiving +//! put_bytes messages in reponse to a fetch request to the phone. +void prv_put_bytes_event_system_task_cb(void *data) { + PebblePutBytesEvent *pb_event = (PebblePutBytesEvent *)data; + + if (!s_fetch_state.in_progress) { + return; + } + + // If put_bytes has failed, let's just say fail and stop everything. + if (pb_event->failed == true) { + AppFetchResult error; + if (s_fetch_state.cancelling) { + PBL_LOG_WRN("Put bytes cancelled by user"); + error = AppFetchResultUserCancelled; + } else { + PBL_LOG_ERR("Put bytes failure"); + error = AppFetchResultPutBytesFailure; + } + + prv_cleanup(error); + goto finally; + } + + if (pb_event->type == PebblePutBytesEventTypeInitTimeout) { + PBL_LOG_WRN("Timed out waiting for putbytes request from phone"); + prv_cleanup(AppFetchResultTimeoutError); + } + + // If this is an object that doesn't have a cookie, then we won't care about it. + if (pb_event->has_cookie == false) { + PBL_LOG_DBG("Ignoring non cookie put_bytes event"); + goto finally; + } + + // Check for the different types of PutBytes events. + if (pb_event->type == PebblePutBytesEventTypeProgress) { + // compute and save the new progress, then show it on the progress bar + uint8_t percent = + prv_compute_progress_percent(pb_event->object_type, pb_event->progress_percent); + prv_put_event_progress(percent); + } else if (pb_event->type == PebblePutBytesEventTypeCleanup) { + // Mark off each finishing put_bytes transaction in our progress struct + + switch (pb_event->object_type) { + case ObjectWatchApp: + s_fetch_state.app = true; + break; + case ObjectWatchWorker: + s_fetch_state.worker = true; + break; + case ObjectAppResources: + s_fetch_state.resources = true; + break; + default: + PBL_LOG_ERR("Got a PutBytes Object that we shouldn't have gotten"); + prv_cleanup(AppFetchResultGeneralFailure); + goto finally; + } + + // add the size of the finished PutBytes transaction to the total size. + s_fetch_state.total_size += pb_event->total_size; + } + + if (s_fetch_state.app && s_fetch_state.worker && s_fetch_state.resources) { + // if everything has finished being transferred + PBL_LOG_DBG("All pieces (%"PRIu32" bytes) have been sent over put_bytes", + s_fetch_state.total_size); + + // signify in the app cache that the app binaries are now loaded + status_t added = app_cache_add_entry(s_fetch_state.app_id, s_fetch_state.total_size); + if (added == S_SUCCESS) { + // Set prev_error as a Success. + s_fetch_state.prev_error = AppFetchResultSuccess; + prv_put_event_simple(AppFetchEventTypeFinish); + } else { + PBL_LOG_ERR("Failed to insert into app cache: %"PRId32, added); + prv_put_event_error(AppFetchResultGeneralFailure); + } + prv_cleanup(AppFetchResultSuccess); + + } else if (pb_event->type == PebblePutBytesEventTypeCleanup) { + // Start the timeout watchdog again so we can tell if things get hung up + // before the phone starts sending the next putbytes object. + // This will only trigger if we've completed a piece and are still + // waiting for another one. + put_bytes_expect_init(FETCH_TIMEOUT_MS); + } + +finally: + kernel_free(pb_event); +} + +//! Put Bytes handler. Used for keeping track of progress and cleanup events. This is called +//! from KernelMain's event handler when it receives a PEBBLE_PUT_BYTES_EVENT event. put_bytes +//! posts these events to inform clients of progress. +void app_fetch_put_bytes_event_handler(PebblePutBytesEvent *pb_event) { + // If an app fetch isn't in progress, ignore it. + if (!s_fetch_state.in_progress) { + return; + } + + PebblePutBytesEvent *pb_event_copy = kernel_malloc_check(sizeof(PebblePutBytesEvent)); + memcpy(pb_event_copy, pb_event, sizeof(PebblePutBytesEvent)); + system_task_add_callback(prv_put_bytes_event_system_task_cb, pb_event_copy); +} + +//! Callback for the system task to fire off the fetch request. Triggered by a call to +//! app_fetch_binaries(). +static void prv_app_fetch_binaries_system_task_cb(void *data) { + AppFetchInstallRequest *request = (AppFetchInstallRequest *)data; + + // check if Bluetooth is active. If so, this will send. + bool successful = comm_session_send_data(comm_session_get_system_session(), APP_FETCH_ENDPOINT_ID, + (uint8_t*)request, sizeof(AppFetchInstallRequest), COMM_SESSION_DEFAULT_TIMEOUT); + + // log it + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&request->uuid, uuid_buffer); + PBL_LOG_INFO("%s request for app with uuid: %s and app_id: %"PRIu32"", + successful ? "Sent" : "Failed to send", uuid_buffer, request->app_id); + + // free before error checking + kernel_free(request); + + // If Bluetooth wasn't active, then post the error and cleanup. + if (!successful) { + prv_cleanup(AppFetchResultNoBluetooth); + return; + } + + // We next expect app_fetch_put_bytes_event_handler() to be called when the phone + // gets our fetch request and issues a putbytes request. + // Start the timeout watchdog to catch us in case the phone never issues the putbytes request. + put_bytes_expect_init(FETCH_TIMEOUT_MS); +} + +//! Called from the system task. Translates an Endpoint error to an event error and sends +//! off the appropriate event. +void prv_handle_app_fetch_install_response(uint8_t result_code) { + switch (result_code) { + case APP_FETCH_RESPONSE_STARTING: + PBL_LOG_INFO("Phone confirmed it will start sending data"); + prv_put_event_simple(AppFetchEventTypeStart); + put_bytes_expect_init(FETCH_TIMEOUT_MS); + break; + case APP_FETCH_RESPONSE_BUSY: + PBL_LOG_WRN("Error: Phone is currently busy"); + prv_cleanup(AppFetchResultPhoneBusy); + break; + case APP_FETCH_RESPONSE_UUID_INVALID: + PBL_LOG_WRN("Error: UUID Invalid"); + prv_cleanup(AppFetchResultUUIDInvalid); + break; + case APP_FETCH_RESPONSE_NO_DATA: + PBL_LOG_WRN("Error: No data on phone"); + prv_cleanup(AppFetchResultNoData); + break; + } +} + +///////////////////////// +// Exported App Fetch API +///////////////////////// + +//! Called by the system that triggers an app fetch install request +void app_fetch_binaries(const Uuid *uuid, AppInstallId app_id, bool has_worker) { + if (s_fetch_state.in_progress) { + PBL_LOG_WRN("Already an app fetch in progress. Ignoring request"); + return; + } + + AppFetchInstallRequest *request = kernel_malloc_check(sizeof(AppFetchInstallRequest)); + + // reset all state + s_fetch_state = (AppFetchState){}; + + // Mark whether the worker needs to be sent over. + s_fetch_state.worker = !has_worker; + s_fetch_state.app_id = app_id; + s_fetch_state.in_progress = true; + + // populate fields + request->command = APP_FETCH_INSTALL_COMMAND; + request->uuid = *uuid; + request->app_id = app_id; + + // Start "warming up" the connection, this will cause the low-latency period to start ~1s sooner. + // Put bytes will extend the low-latency period after this: + comm_session_set_responsiveness(comm_session_get_system_session(), BtConsumerPpAppFetch, + ResponseTimeMin, MIN_LATENCY_MODE_TIMEOUT_APP_FETCH_SECS); + + system_task_add_callback(prv_app_fetch_binaries_system_task_cb, request); +} + +AppFetchError app_fetch_get_previous_error(void) { + AppFetchError error = { + .error = s_fetch_state.prev_error, + .id = s_fetch_state.app_id, + }; + + return error; +} + +static void prv_cancel_fetch_from_system_task(void *data) { + AppInstallId app_id = (AppInstallId)data; + + if ((!s_fetch_state.in_progress) || + ((s_fetch_state.app_id != app_id) && (app_id != INSTALL_ID_INVALID))) { + PBL_LOG_DBG("Attempted to cancel an app that is currently not being" + " fetched: %"PRId32, app_id); + return; + } + + PBL_LOG_DBG("Cancelling app fetch from system task"); + s_fetch_state.cancelling = true; + put_bytes_cancel(); +} + +void app_fetch_cancel_from_system_task(AppInstallId app_id) { + PBL_ASSERT_TASK(PebbleTask_KernelBackground); + + prv_cancel_fetch_from_system_task((void *)(uintptr_t)app_id); +} + +void app_fetch_cancel(AppInstallId app_id) { + // Everything within app fetch happens on the background task + system_task_add_callback(prv_cancel_fetch_from_system_task, (void *)(uintptr_t)app_id); +} + +bool app_fetch_in_progress(void) { + return s_fetch_state.in_progress; +} + +//////////////////////////// +// Exported Callbacks +//////////////////////////// + +typedef struct __attribute__((__packed__)) { + uint8_t command; + uint8_t result_code; +} AppFetchResponseData; + +//! System task callback triggered by app_fetch_protocol_msg_callback(). +static void prv_app_fetch_protocol_handle_msg(AppFetchResponseData *response_data) { + + switch (response_data->command) { + case APP_FETCH_INSTALL_RESPONSE: + prv_handle_app_fetch_install_response(response_data->result_code); + break; + default: + PBL_LOG_ERR("Invalid message received, command: %u result: %u", + response_data->command, response_data->result_code); + prv_cleanup(AppFetchResultGeneralFailure); + break; + } +} + +//! Callback that is placed in the endpoints table. As of now, only responses will come through this +//! callback as all commands are originally sent to the phone. +void app_fetch_protocol_msg_callback(CommSession *session, const uint8_t *data, size_t length) { + if (length < sizeof(AppFetchResponseData)) { + PBL_LOG_ERR("Invalid message length %"PRIu32"", (uint32_t)length); + prv_cleanup(AppFetchResultGeneralFailure); + return; + } + + if (!s_fetch_state.in_progress) { + PBL_LOG_WRN("Got a message but app fetch not in progress. Ignoring"); + return; + } + + prv_app_fetch_protocol_handle_msg((AppFetchResponseData *)data); +} diff --git a/src/fw/services/app_fetch_endpoint/wscript_build b/src/fw/services/app_fetch_endpoint/wscript_build new file mode 100644 index 0000000000..9532e4f29f --- /dev/null +++ b/src/fw/services/app_fetch_endpoint/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_app_fetch_endpoint', +) +bld.env.SERVICES.append('services_app_fetch_endpoint') diff --git a/src/fw/services/app_glances/Kconfig b/src/fw/services/app_glances/Kconfig new file mode 100644 index 0000000000..6755da0533 --- /dev/null +++ b/src/fw/services/app_glances/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_GLANCES + bool "App glances" + help + App-glances (launcher tile) service. diff --git a/src/fw/services/app_glances/app_glance_service.c b/src/fw/services/app_glances/app_glance_service.c new file mode 100644 index 0000000000..a96a13ba74 --- /dev/null +++ b/src/fw/services/app_glances/app_glance_service.c @@ -0,0 +1,199 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/app_glances/app_glance_service.h" + +#include "applib/app_glance.h" +#include "applib/event_service_client.h" +#include "drivers/rtc.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "os/mutex.h" +#include "process_management/app_install_manager.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/app_glance_db.h" +#include "syscall/syscall_internal.h" +#include "system/passert.h" +#include "system/status_codes.h" +#include "util/math.h" + +//! Return true to continue iteration and false to stop it. +typedef bool (*SliceForEachCb)(AppGlanceSliceInternal *slice, void *context); + +static void prv_slice_for_each(AppGlance *glance, SliceForEachCb cb, void *context) { + if (!glance || !cb) { + return; + } + + for (unsigned int slice_index = 0; + slice_index < MIN(glance->num_slices, APP_GLANCE_DB_MAX_SLICES_PER_GLANCE); slice_index++) { + AppGlanceSliceInternal *current_slice = &glance->slices[slice_index]; + // Stop iterating if the client's callback function returns false + if (!cb(current_slice, context)) { + break; + } + } +} + +typedef struct FindCurrentSliceData { + time_t current_time; + AppGlanceSliceInternal *current_slice; +} FindCurrentSliceData; + +//! The "current" slice is the slice with an expiration_time closest to the current time while +//! still being after the current time +static bool prv_find_current_glance(AppGlanceSliceInternal *slice, void *context) { + FindCurrentSliceData *data = context; + PBL_ASSERTN(data); + + // First check if this slice never expires; the zero value of APP_GLANCE_SLICE_NO_EXPIRATION + // won't work with the comparisons we perform below + if (slice->expiration_time == APP_GLANCE_SLICE_NO_EXPIRATION) { + // We'll only use a never-expiring slice if we haven't set a slice yet + if (!data->current_slice) { + data->current_slice = slice; + } + // Continue iterating through the slices + return true; + } + + const int time_until_slice_expires = slice->expiration_time - data->current_time; + + // Continue iterating through the slices if this slice expires in the past + if (time_until_slice_expires <= 0) { + return true; + } + + // If we don't have a current slice or the current slice we have is a never-expiring slice, we can + // go ahead and use this slice now but continue iterating to try to find an earlier expiring slice + // in the list + if (!data->current_slice || + (data->current_slice->expiration_time == APP_GLANCE_SLICE_NO_EXPIRATION)) { + data->current_slice = slice; + return true; + } + + const int time_until_current_slice_expires = + data->current_slice->expiration_time - data->current_time; + + // If this slice expires earlier than our current slice, use this slice as the new current slice + if (time_until_slice_expires < time_until_current_slice_expires) { + data->current_slice = slice; + } + + // Continue iterating to try to find an earlier slice + return true; +} + +static void prv_glance_event_put(const Uuid *app_uuid) { + Uuid *app_uuid_copy = kernel_zalloc_check(sizeof(Uuid)); + *app_uuid_copy = *app_uuid; + + PebbleEvent e = (PebbleEvent) { + .type = PEBBLE_APP_GLANCE_EVENT, + .app_glance = (PebbleAppGlanceEvent) { + .app_uuid = app_uuid_copy, + }, + }; + + event_put(&e); +} + +////////////////////// +// Event handlers +// NOTE: These events are handled on KernelMain (app_glance_service_init called from +// services_normal_init) +////////////////////// + +static void prv_blob_db_event_handler(PebbleEvent *event, void *context) { + const PebbleBlobDBEvent *blob_db_event = &event->blob_db; + const BlobDBId blob_db_id = blob_db_event->db_id; + + if (blob_db_id != BlobDBIdAppGlance) { + // We only care about app glance changes + return; + } + + prv_glance_event_put((Uuid *)blob_db_event->key); +} + +static void prv_handle_app_cache_event(PebbleEvent *e, void *context) { + if (e->app_cache_event.cache_event_type == PebbleAppCacheEvent_Removed) { + Uuid app_uuid; + app_install_get_uuid_for_install_id(e->app_cache_event.install_id, &app_uuid); + app_glance_db_delete_glance(&app_uuid); + } +} + +////////////////////// +// Public API +////////////////////// + +void app_glance_service_init_glance(AppGlance *glance) { + if (!glance) { + return; + } + *glance = (AppGlance) {}; +} + +void app_glance_service_init(void) { + + static EventServiceInfo s_blob_db_event_info = { + .type = PEBBLE_BLOBDB_EVENT, + .handler = prv_blob_db_event_handler, + }; + event_service_client_subscribe(&s_blob_db_event_info); + + static EventServiceInfo s_app_cache_event_info = { + .type = PEBBLE_APP_CACHE_EVENT, + .handler = prv_handle_app_cache_event, + }; + event_service_client_subscribe(&s_app_cache_event_info); +} + +bool app_glance_service_get_current_slice(const Uuid *app_uuid, AppGlanceSliceInternal *slice_out) { + if (!slice_out) { + return false; + } + + bool success; + // Try to read the app's glance, first checking the cache + AppGlance *app_glance = kernel_zalloc_check(sizeof(*app_glance)); + const status_t rv = app_glance_db_read_glance(app_uuid, app_glance); + if (rv != S_SUCCESS) { + success = false; + goto cleanup; + } + + // Iterate over the slices to find the current slice (which might be NULL if there aren't any + // slices or if all of the slices have expired) + FindCurrentSliceData find_current_slice_data = (FindCurrentSliceData) { + .current_time = rtc_get_time(), + }; + prv_slice_for_each(app_glance, prv_find_current_glance, &find_current_slice_data); + if (!find_current_slice_data.current_slice) { + success = false; + goto cleanup; + } + + // Copy the current slice data to slice_out + *slice_out = *find_current_slice_data.current_slice; + success = true; + +cleanup: + kernel_free(app_glance); + + return success; +} + +DEFINE_SYSCALL(bool, sys_app_glance_update, const Uuid *uuid, const AppGlance *glance) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(uuid, sizeof(*uuid)); + syscall_assert_userspace_buffer(glance, sizeof(*glance)); + } + const bool success = (app_glance_db_insert_glance(uuid, glance) == S_SUCCESS); + if (success) { + prv_glance_event_put(uuid); + } + return success; +} diff --git a/src/fw/services/app_glances/wscript_build b/src/fw/services/app_glances/wscript_build new file mode 100644 index 0000000000..1f1ca152ff --- /dev/null +++ b/src/fw/services/app_glances/wscript_build @@ -0,0 +1,10 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['app_glance_service.c'], + target='services_app_glances', +) + +bld.env.SERVICES.append('services_app_glances') diff --git a/src/fw/services/app_inbox_service/Kconfig b/src/fw/services/app_inbox_service/Kconfig new file mode 100644 index 0000000000..b865288611 --- /dev/null +++ b/src/fw/services/app_inbox_service/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_INBOX_SERVICE + bool "App inbox" + help + App inbox messaging service. + +if SERVICE_APP_INBOX_SERVICE + +module = SERVICE_APP_INBOX_SERVICE +module-str = App inbox +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/app_inbox_service/service.c b/src/fw/services/app_inbox_service/service.c new file mode 100644 index 0000000000..9d8ccb87d5 --- /dev/null +++ b/src/fw/services/app_inbox_service/service.c @@ -0,0 +1,606 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/app_inbox_service.h" + +#include "kernel/pbl_malloc.h" +#include "kernel/pebble_tasks.h" +#include "process_management/process_manager.h" +#include "os/mutex.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/buffer.h" +#include "util/list.h" + +PBL_LOG_MODULE_DEFINE(service_app_inbox_service, CONFIG_SERVICE_APP_INBOX_SERVICE_LOG_LEVEL); + +typedef struct AppInboxNode { + ListNode node; + AppInboxServiceTag tag; + AppInboxMessageHandler message_handler; + AppInboxDroppedHandler dropped_handler; + PebbleTask event_handler_task; + + //! Indicates whether there is a writer. + //! The writer can set it to anything they want, mostly for debugging purposes. + void *writer; + bool write_failed; + bool has_pending_event; + + uint32_t num_failed; + uint32_t num_success; + + struct { + //! The size of `storage`. + size_t size; + + //! The positive offset relative relative to write_index, up until which the current + //! (incomplete) message has been written. + size_t current_offset; + + //! Index after which the current message should get written. + //! If this index is non-zero, there are completed message(s) in the buffer. + size_t write_index; + + ///! Pointer to the beginning of the storage. + uint8_t *storage; + } buffer; +} AppInboxNode; + +typedef struct AppInboxConsumerInfo { + AppInboxServiceTag tag; + AppInboxMessageHandler message_handler; + AppInboxDroppedHandler dropped_handler; + uint32_t num_failed; + uint32_t num_success; + uint8_t *it; + uint8_t *end; +} AppInboxConsumerInfo; + + +_Static_assert(sizeof(AppInboxServiceTag) <= sizeof(void *), + "AppInboxServiceTag should fit inside a void *"); + +static AppInboxNode *s_app_inbox_head; + +static PebbleRecursiveMutex *s_app_inbox_mutex; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Declarations of permitted handlers: + +extern void app_message_receiver_message_handler(const uint8_t *data, size_t length, + AppInboxConsumerInfo *consumer_info); +extern void app_message_receiver_dropped_handler(uint32_t num_dropped_messages); + +#ifdef UNITTEST +extern void test_message_handler(const uint8_t *data, size_t length, + AppInboxConsumerInfo *consumer_info); +extern void test_dropped_handler(uint32_t num_dropped_messages); +extern void test_alt_message_handler(const uint8_t *data, size_t length, + AppInboxConsumerInfo *consumer_info); +extern void test_alt_dropped_handler(uint32_t num_dropped_messages); +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Syscalls + +static AppInboxServiceTag prv_tag_for_event_handlers(const AppInboxMessageHandler message_handler, + const AppInboxDroppedHandler dropped_handler) { + static const struct { + AppInboxMessageHandler message_handler; + AppInboxDroppedHandler dropped_handler; + } s_event_handler_map[] = { + [AppInboxServiceTagAppMessageReceiver] = { + .message_handler = app_message_receiver_message_handler, + .dropped_handler = app_message_receiver_dropped_handler, + }, +#ifdef UNITTEST + [AppInboxServiceTagUnitTest] = { + .message_handler = test_message_handler, + .dropped_handler = test_dropped_handler, + }, + [AppInboxServiceTagUnitTestAlt] = { + .message_handler = test_alt_message_handler, + .dropped_handler = test_alt_dropped_handler, + } +#endif + }; + for (AppInboxServiceTag tag = 0; tag < NumAppInboxServiceTag; ++tag) { + if (s_event_handler_map[tag].message_handler == message_handler && + s_event_handler_map[tag].dropped_handler == dropped_handler) { + return tag; + } + } + return AppInboxServiceTagInvalid; +} + +DEFINE_SYSCALL(bool, sys_app_inbox_service_register, uint8_t *storage, size_t storage_size, + AppInboxMessageHandler message_handler, AppInboxDroppedHandler dropped_handler) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(storage, storage_size); + } + const AppInboxServiceTag service_tag = prv_tag_for_event_handlers(message_handler, + dropped_handler); + if (AppInboxServiceTagInvalid == service_tag) { + PBL_LOG_ERR("AppInbox event handlers not allowed <0x%"PRIx32", 0x%"PRIx32">", + // Ugh.. no more format signature slots free for %p %p... + (uint32_t)(uintptr_t)message_handler, (uint32_t)(uintptr_t)dropped_handler); + syscall_failed(); + } + + return app_inbox_service_register(storage, storage_size, + message_handler, dropped_handler, service_tag); +} + +DEFINE_SYSCALL(uint32_t, sys_app_inbox_service_unregister, uint8_t *storage) { + // No check is needed on the value of `storage `, we're not going to derefence it. + return app_inbox_service_unregister_by_storage(storage); +} + +static bool prv_get_consumer_info(AppInboxServiceTag tag, AppInboxConsumerInfo *info_in_out); + +DEFINE_SYSCALL(bool, sys_app_inbox_service_get_consumer_info, + AppInboxServiceTag tag, AppInboxConsumerInfo *info_out) { + if (PRIVILEGE_WAS_ELEVATED) { + if (info_out) { + syscall_assert_userspace_buffer(info_out, sizeof(*info_out)); + } + } + return prv_get_consumer_info(tag, info_out); +} + +static void prv_consume(AppInboxConsumerInfo *consumer_info); + +DEFINE_SYSCALL(void, sys_app_inbox_service_consume, AppInboxConsumerInfo *consumer_info) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(consumer_info, sizeof(*consumer_info)); + } + prv_consume(consumer_info); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +static void prv_lock(void) { + // Using one "global" lock for all app inboxes. + // If needed, we could easily give each app inbox its own mutex, but it seems overkill right now. + mutex_lock_recursive(s_app_inbox_mutex); +} + +static void prv_unlock(void) { + mutex_unlock_recursive(s_app_inbox_mutex); +} + +static bool prv_list_filter_by_storage(ListNode *found_node, void *data) { + return ((AppInboxNode *)found_node)->buffer.storage == (uint8_t *)data; +} + +static AppInboxNode *prv_find_inbox_by_storage(uint8_t *storage) { + return (AppInboxNode *) list_find((ListNode *)s_app_inbox_head, + prv_list_filter_by_storage, storage); +} + +static bool prv_list_filter_by_tag(ListNode *found_node, void *data) { + return ((AppInboxNode *)found_node)->tag == (AppInboxServiceTag)(uintptr_t)data; +} + +static AppInboxNode *prv_find_inbox_by_tag(AppInboxServiceTag tag) { + return (AppInboxNode *) list_find((ListNode *)s_app_inbox_head, + prv_list_filter_by_tag, (void *)(uintptr_t)tag); +} + +static AppInboxNode *prv_find_inbox_by_tag_and_log_if_not_found(AppInboxServiceTag tag) { + AppInboxNode *inbox = prv_find_inbox_by_tag(tag); + if (!inbox) { + PBL_LOG_ERR("No AppInbox for tag <%d>", tag); + } + return inbox; +} + +//! We don't report "number of messages consumed", because that would force the system to parse +//! the contents of the (app space) buffer, which might have been corrupted by the app. +//! Note that it's in theory possible for a misbehaving app to pass in a consumed_up_to_ptr that is +//! mid-way in a message. If it does so, it won't crash the kernel, but it will result in delivery +//! of broken messages to the app, but it won't be our fault... +static void prv_consume(AppInboxConsumerInfo *consumer_info) { + prv_lock(); + { + AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(consumer_info->tag); + if (!inbox) { + goto unlock; + } + uint8_t *const consumed_up_to_ptr = consumer_info->it; + uint8_t * const completed_messages_end = (inbox->buffer.storage + inbox->buffer.write_index); + if (consumed_up_to_ptr < inbox->buffer.storage || + consumed_up_to_ptr > completed_messages_end) { + PBL_LOG_ERR("Out of bounds"); + goto unlock; + } + const size_t bytes_consumed = (consumed_up_to_ptr - inbox->buffer.storage); + if (0 == bytes_consumed) { + goto unlock; + } + uint8_t * const partial_message_end = completed_messages_end + inbox->buffer.current_offset; + const size_t remaining_size = partial_message_end - consumed_up_to_ptr; + consumer_info->it = inbox->buffer.storage; + consumer_info->end = inbox->buffer.storage + remaining_size; + if (remaining_size) { + // New data has been written in the mean-time, move it all to the front of the buffer: + memmove(inbox->buffer.storage, consumed_up_to_ptr, remaining_size); + } + inbox->buffer.write_index -= bytes_consumed; + } +unlock: + prv_unlock(); +} + +static bool prv_get_consumer_info(AppInboxServiceTag tag, AppInboxConsumerInfo *info_out) { + if (!info_out) { + return false; + } + bool success = false; + prv_lock(); + { + AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); + if (!inbox) { + goto unlock; + } + + *info_out = (const AppInboxConsumerInfo) { + .tag = tag, + .message_handler = inbox->message_handler, + .dropped_handler = inbox->dropped_handler, + .num_failed = inbox->num_failed, + .num_success = inbox->num_success, + .it = inbox->buffer.storage, + .end = inbox->buffer.storage + inbox->buffer.write_index, + }; + + // Also mark that there is no event pending any more: + inbox->has_pending_event = false; + + // Reset counters because the info is communicated to app and it's about to consume the data. + inbox->num_failed = 0; + inbox->num_success = 0; + + success = true; + } +unlock: + prv_unlock(); + return success; +} + +//! @note Executes on app task, therefore we need to go through syscalls to access AppInbox! +static void prv_callback_event_handler(void *ctx) { + AppInboxServiceTag tag = (AppInboxServiceTag)(uintptr_t)ctx; + AppInboxConsumerInfo info = {}; + size_t num_message_consumed = 0; + if (!sys_app_inbox_service_get_consumer_info(tag, &info)) { + // Inbox wasn't there any more + return; + } + if (!info.message_handler) { + // Shouldn't ever happen, but better not PBL_ASSERTN on app task + PBL_LOG_ERR("No AppInbox message handler!"); + return; + } + if (!info.num_success && !info.num_failed) { + // Shouldn't ever happen, but better not PBL_ASSERTN on app task + PBL_LOG_ERR("Got callback, but zero messages!?"); + // fall-through + } + + // These conditions are redundant, just for safety: + while ((num_message_consumed < info.num_success) && (info.it < info.end)) { + AppInboxMessageHeader *msg = (AppInboxMessageHeader *)info.it; + + // Increment now so that if the message_handler calls into sys_app_inbox_service_consume(), + // it will be pointing *after* the message that is just handled: + info.it += (sizeof(AppInboxMessageHeader) + msg->length); + + // Check for safety, just in case the app has corrupted the buffer in the mean time: + if (msg->data + msg->length <= info.end) { + info.message_handler(msg->data, msg->length, &info); + } else { + PBL_LOG_ERR("Corrupted AppInbox message!"); + } + ++num_message_consumed; + } + + if (info.num_failed) { + if (info.dropped_handler) { + info.dropped_handler(info.num_failed); + } else { + PBL_LOG_ERR("Dropped %"PRIu32" messages but no dropped_handler", + info.num_failed); + } + } + + // Report back up to which byte we've consumed the data. + sys_app_inbox_service_consume(&info); +} + +bool app_inbox_service_register(uint8_t *storage, size_t storage_size, + AppInboxMessageHandler message_handler, + AppInboxDroppedHandler dropped_handler, AppInboxServiceTag tag) { + AppInboxNode *new_node = (AppInboxNode *)kernel_zalloc(sizeof(AppInboxNode)); + if (!new_node) { + PBL_LOG_ERR("Not enough memory to allocate AppInboxNode"); + return false; + } + + prv_lock(); + { + bool has_error = false; + + if (prv_find_inbox_by_storage(storage)) { + PBL_LOG_ERR("AppInbox already registered for storage <%p>", storage); + has_error = true; + } + + // This check effectively caps the kernel RAM impact of this service, + // so it's not possible to abuse the syscall and cause kernel OOM. + if (prv_find_inbox_by_tag(tag)) { + PBL_LOG_ERR("AppInbox already registered for tag <%d>", tag); + has_error = true; + } + + if (has_error) { + kernel_free(new_node); + new_node = NULL; + } else { + new_node->tag = tag; + new_node->message_handler = message_handler; + new_node->dropped_handler = dropped_handler; + new_node->event_handler_task = pebble_task_get_current(); + new_node->buffer.storage = storage; + new_node->buffer.size = storage_size; + s_app_inbox_head = (AppInboxNode *)list_prepend((ListNode *)s_app_inbox_head, + (ListNode *)new_node); + } + } + prv_unlock(); + + return (new_node != NULL); +} + +uint32_t app_inbox_service_unregister_by_storage(uint8_t *storage) { + uint32_t num_messages_lost = 0; + prv_lock(); + { + AppInboxNode *node = prv_find_inbox_by_storage(storage); + if (node) { + list_remove((ListNode *)node, (ListNode **)&s_app_inbox_head, NULL); + num_messages_lost = node->num_failed + node->num_success + (node->writer ? 1 : 0); + kernel_free(node); + } + } + prv_unlock(); + return num_messages_lost; +} + +void app_inbox_service_unregister_all(void) { + prv_lock(); + { + AppInboxNode *node = s_app_inbox_head; + while (node) { + AppInboxNode *next = (AppInboxNode *) node->node.next; + kernel_free(node); + node = next; + } + s_app_inbox_head = NULL; + } + prv_unlock(); +} + +static bool prv_is_inbox_being_written(AppInboxNode *inbox) { + return (inbox->writer != NULL); +} + +static size_t prv_get_space_remaining(AppInboxNode *inbox) { + return (inbox->buffer.size - inbox->buffer.write_index - inbox->buffer.current_offset); +} + +bool prv_check_space_remaining(AppInboxNode *inbox, size_t required_free_length) { + const size_t space_remaining = prv_get_space_remaining(inbox); + if (required_free_length > space_remaining) { + PBL_LOG_ERR("Dropping data, not enough space %"PRIu32" vs %"PRIu32, + (uint32_t)required_free_length, (uint32_t)space_remaining); + return false; + } + return true; +} + +static void prv_send_event_if_needed(AppInboxNode *inbox) { + if (!inbox || inbox->has_pending_event) { + return; + } + PebbleEvent event = { + .type = PEBBLE_CALLBACK_EVENT, + .callback = { + .callback = prv_callback_event_handler, + .data = (void *)(uintptr_t) inbox->tag, + }, + }; + const bool is_event_enqueued = process_manager_send_event_to_process(inbox->event_handler_task, + &event); + if (!is_event_enqueued) { + PBL_LOG_ERR("Event queue full"); + } + inbox->has_pending_event = is_event_enqueued; +} + +static void prv_mark_failed_if_no_writer(AppInboxNode *inbox) { + if (!inbox->writer) { + // See PBL-41464 + // App message has been reset (closed and opened again) while a message was being received. + // Fail it because our state got lost. + inbox->write_failed = true; + } +} + +bool app_inbox_service_begin(AppInboxServiceTag tag, size_t required_free_length, void *writer) { + if (!writer) { + return false; + } + bool success = false; + prv_lock(); + { + AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); + if (!inbox) { + goto unlock; + } + if (prv_is_inbox_being_written(inbox)) { + ++inbox->num_failed; + PBL_LOG_ERR("Dropping data, already written by <%p>", inbox->writer); + // Don't send event here, when the current write finishes, the drop(s) will be reported too. + goto unlock; + } + if (!prv_check_space_remaining(inbox, required_free_length + sizeof(AppInboxMessageHeader))) { + ++inbox->num_failed; + // If it doesn't fit, send event immediately, we don't know when the next write will happen. + prv_send_event_if_needed(inbox); + goto unlock; + } + + inbox->writer = writer; + inbox->write_failed = false; + // Leave space at the beginning for the header, which we'll write in the end + inbox->buffer.current_offset = sizeof(AppInboxMessageHeader); + success = true; + } +unlock: + prv_unlock(); + return success; +} + +bool app_inbox_service_write(AppInboxServiceTag tag, const uint8_t *data, size_t length) { + bool success = false; + prv_lock(); + { + AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); + if (!inbox) { + goto unlock; + } + prv_mark_failed_if_no_writer(inbox); + if (inbox->write_failed) { + goto unlock; + } + if (!prv_check_space_remaining(inbox, length)) { + inbox->write_failed = true; + goto unlock; + } + memcpy(inbox->buffer.storage + inbox->buffer.write_index + inbox->buffer.current_offset, + data, length); + inbox->buffer.current_offset += length; + success = true; + } +unlock: + prv_unlock(); + return success; +} + +static void prv_finish(AppInboxNode *inbox) { + inbox->writer = NULL; + inbox->buffer.current_offset = 0; +} + +void app_inbox_service_init(void) { + s_app_inbox_mutex = mutex_create_recursive(); +} + +bool app_inbox_service_end(AppInboxServiceTag tag) { + bool success = false; + prv_lock(); + { + AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); + if (!inbox) { + goto unlock; + } + prv_mark_failed_if_no_writer(inbox); + if (inbox->write_failed) { + ++inbox->num_failed; + } else { + const AppInboxMessageHeader header = (const AppInboxMessageHeader) { + .length = inbox->buffer.current_offset - sizeof(AppInboxMessageHeader), + // Fill with something that might aid debugging one day: + .padding = { 0xaa, 0xaa, 0xaa, 0xaa }, + }; + memcpy(inbox->buffer.storage + inbox->buffer.write_index, &header, sizeof(header)); + inbox->buffer.write_index += inbox->buffer.current_offset; + ++inbox->num_success; + success = true; + } + prv_finish(inbox); + + prv_send_event_if_needed(inbox); + } +unlock: + prv_unlock(); + return success; +} + +void app_inbox_service_cancel(AppInboxServiceTag tag) { + prv_lock(); + { + AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); + if (!inbox) { + goto unlock; + } + prv_finish(inbox); + } +unlock: + prv_unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Unit Test Interfaces + +bool app_inbox_service_has_inbox_for_tag(AppInboxServiceTag tag) { + bool has_inbox; + prv_lock(); + has_inbox = (prv_find_inbox_by_tag(tag) != NULL); + prv_unlock(); + return has_inbox; +} + +bool app_inbox_service_has_inbox_for_storage(uint8_t *storage) { + bool has_inbox; + prv_lock(); + has_inbox = (prv_find_inbox_by_storage(storage) != NULL); + prv_unlock(); + return has_inbox; +} + +bool app_inbox_service_is_being_written_for_tag(AppInboxServiceTag tag) { + bool is_written = false; + prv_lock(); + AppInboxNode *inbox = prv_find_inbox_by_tag(tag); + if (inbox) { + is_written = (inbox->writer != NULL); + } + prv_unlock(); + return is_written; +} + +uint32_t app_inbox_service_num_failed_for_tag(AppInboxServiceTag tag) { + uint32_t num_failed = 0; + prv_lock(); + AppInboxNode *inbox = prv_find_inbox_by_tag(tag); + if (inbox) { + num_failed = inbox->num_failed; + } + prv_unlock(); + return num_failed; +} + +uint32_t app_inbox_service_num_success_for_tag(AppInboxServiceTag tag) { + uint32_t num_success = 0; + prv_lock(); + AppInboxNode *inbox = prv_find_inbox_by_tag(tag); + if (inbox) { + num_success = inbox->num_success; + } + prv_unlock(); + return num_success; +} diff --git a/src/fw/services/app_inbox_service/wscript_build b/src/fw/services/app_inbox_service/wscript_build new file mode 100644 index 0000000000..0b5a7671fa --- /dev/null +++ b/src/fw/services/app_inbox_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_app_inbox_service', +) +bld.env.SERVICES.append('services_app_inbox_service') diff --git a/src/fw/services/app_message/Kconfig b/src/fw/services/app_message/Kconfig new file mode 100644 index 0000000000..ba6b3610a3 --- /dev/null +++ b/src/fw/services/app_message/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_MESSAGE + bool "App message" + help + AppMessage (app <-> phone) transport. + +if SERVICE_APP_MESSAGE + +module = SERVICE_APP_MESSAGE +module-str = App message +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/app_message/app_message_receiver.c b/src/fw/services/app_message/app_message_receiver.c similarity index 86% rename from src/fw/services/normal/app_message/app_message_receiver.c rename to src/fw/services/app_message/app_message_receiver.c index 8736ffbc2f..6cd63c2d6a 100644 --- a/src/fw/services/normal/app_message/app_message_receiver.c +++ b/src/fw/services/app_message/app_message_receiver.c @@ -1,32 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_message/app_message_internal.h" #include "kernel/pbl_malloc.h" #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_receive_router.h" -#include "services/normal/app_inbox_service.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_receive_router.h" +#include "pbl/services/app_inbox_service.h" #include "system/logging.h" #include "util/math.h" #include +PBL_LOG_MODULE_DECLARE(service_app_message, CONFIG_SERVICE_APP_MESSAGE_LOG_LEVEL); + extern const ReceiverImplementation g_default_kernel_receiver_implementation; extern const ReceiverImplementation g_app_message_receiver_implementation; @@ -70,7 +59,7 @@ static bool prv_fwd_prepare(AppMessageReceiver *rcv, CommSession *session, &kernel_nack_endpoint, header_bytes_remaining); if (!kernel_receiver) { - PBL_LOG(LOG_LEVEL_ERROR, "System receiver wasn't able to prepare"); + PBL_LOG_ERR("System receiver wasn't able to prepare"); return false; } rcv->kernel_receiver = kernel_receiver; @@ -84,8 +73,6 @@ static void prv_write(const uint8_t *data, size_t length) { static Receiver *prv_app_message_receiver_prepare(CommSession *session, const PebbleProtocolEndpoint *endpoint, size_t total_payload_size) { - analytics_inc(ANALYTICS_APP_METRIC_MSG_IN_COUNT, AnalyticsClient_App); - // FIXME: Find a better solution for this. // https://pebbletechnology.atlassian.net/browse/PBL-21538 if (total_payload_size > 500) { @@ -140,8 +127,6 @@ static void prv_app_message_receiver_write(Receiver *receiver, const uint8_t *da comm_session_set_responsiveness(rcv->session, BtConsumerPpAppMessage, ResponseTimeMin, MIN_LATENCY_MODE_TIMEOUT_APP_MESSAGE_SECS); - analytics_add(ANALYTICS_APP_METRIC_MSG_BYTE_IN_COUNT, length, AnalyticsClient_App); - if (rcv->header_bytes_remaining > 0) { const size_t header_bytes_to_write = MIN(rcv->header_bytes_remaining, length); g_default_kernel_receiver_implementation.write(rcv->kernel_receiver, @@ -170,8 +155,7 @@ static void prv_app_message_receiver_finish(Receiver *receiver) { if (app_inbox_service_end(AppInboxServiceTagAppMessageReceiver)) { // The write was successful, cancel processing the header for nacking: kernel_receiver_finally_cb = g_default_kernel_receiver_implementation.cleanup; - } else { - analytics_inc(ANALYTICS_APP_METRIC_MSG_DROP_COUNT, AnalyticsClient_App); + PBL_ANALYTICS_ADD(app_message_received_count, 1); } } diff --git a/src/fw/services/normal/app_message/app_message_sender.c b/src/fw/services/app_message/app_message_sender.c similarity index 90% rename from src/fw/services/normal/app_message/app_message_sender.c rename to src/fw/services/app_message/app_message_sender.c index fc3a3c73f7..1ae606c60d 100644 --- a/src/fw/services/normal/app_message/app_message_sender.c +++ b/src/fw/services/app_message/app_message_sender.c @@ -1,29 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_message/app_message_internal.h" #include "applib/app_outbox.h" #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/normal/app_message/app_message_sender.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/app_message/app_message_sender.h" #include "system/logging.h" #include "util/math.h" #include "util/net.h" +PBL_LOG_MODULE_DEFINE(service_app_message, CONFIG_SERVICE_APP_MESSAGE_LOG_LEVEL); + // ------------------------------------------------------------------------------------------------- // Misc helpers: @@ -134,8 +123,6 @@ static size_t prv_send_job_impl_get_read_pointer(const SessionSendQueueJob *send static void prv_send_job_impl_consume(const SessionSendQueueJob *send_job, size_t length) { AppMessageSendJob *app_message_send_job = (AppMessageSendJob *)send_job; app_message_send_job->consumed_length += length; - - analytics_add(ANALYTICS_APP_METRIC_MSG_BYTE_OUT_COUNT, length, AnalyticsClient_App); } static void prv_send_job_impl_free(SessionSendQueueJob *send_job) { @@ -145,8 +132,7 @@ static void prv_send_job_impl_free(SessionSendQueueJob *send_job) { if (is_completed) { const AppInstallId app_id = app_manager_get_current_app_id(); app_install_mark_prioritized(app_id, true /* can_expire */); - - analytics_inc(ANALYTICS_APP_METRIC_MSG_OUT_COUNT, AnalyticsClient_App); + PBL_ANALYTICS_ADD(app_message_sent_count, 1); } // The outbox_message is owned by app_outbox_service, calling consume will free it as well: const AppOutboxStatus status = @@ -204,7 +190,7 @@ static void prv_handle_outbox_message(AppOutboxMessage *message) { const AppMessageSenderError err = prv_sanity_check_msg_and_fill_header(message); if (AppMessageSenderErrorSuccess != err) { - PBL_LOG(LOG_LEVEL_ERROR, "Outbound app message corrupted %u", err); + PBL_LOG_ERR("Outbound app message corrupted %u", err); app_outbox_service_consume_message(message, (AppOutboxStatus)err); return; } diff --git a/src/fw/services/app_message/wscript_build b/src/fw/services/app_message/wscript_build new file mode 100644 index 0000000000..729fd5fe79 --- /dev/null +++ b/src/fw/services/app_message/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'app_message_receiver.c', + 'app_message_sender.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_app_message', +) + +bld.env.SERVICES.append('services_app_message') diff --git a/src/fw/services/app_order_endpoint/Kconfig b/src/fw/services/app_order_endpoint/Kconfig new file mode 100644 index 0000000000..6626c84697 --- /dev/null +++ b/src/fw/services/app_order_endpoint/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_ORDER_ENDPOINT + bool "App order endpoint" + help + Endpoint used to order installed apps in the launcher. + +if SERVICE_APP_ORDER_ENDPOINT + +module = SERVICE_APP_ORDER_ENDPOINT +module-str = App order endpoint +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/app_order_endpoint/service.c b/src/fw/services/app_order_endpoint/service.c new file mode 100644 index 0000000000..8abb7cab30 --- /dev/null +++ b/src/fw/services/app_order_endpoint/service.c @@ -0,0 +1,90 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "process_management/app_install_manager_private.h" +#include "pbl/services/process_management/app_order_storage.h" +#include "pbl/services/comm_session/session.h" +#include "system/hexdump.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/status_codes.h" +#include "util/uuid.h" + +#include +#include + +PBL_LOG_MODULE_DEFINE(service_app_order_endpoint, CONFIG_SERVICE_APP_ORDER_ENDPOINT_LOG_LEVEL); + +//! @file app_order_endpoint.c +//! App Order Endpoint +//! +//! There is only 1 way to use this endpoint + +//! \code{.c} +//! 0x01 +//! <16-byte UUID_1> +//! ... +//! <16-byte UUID_N> +//! \endcode + +//! AppOrder Endpoint ID +static const uint16_t APP_ORDER_ENDPOINT_ID = 0xabcd; + +typedef enum { + APP_ORDER_CMD = 0x01, +} AppOrderCommand; + +typedef enum { + APP_ORDER_RES_SUCCESS = 0x01, + APP_ORDER_RES_FAILURE = 0x02, + APP_ORDER_RES_INVALID = 0x03, + APP_ORDER_RES_RETRY_LATER = 0x04, +} AppOrderResponse; + +typedef struct { + CommSession *session; + uint8_t result; +} ResponseInfo; + +static void prv_send_result(CommSession *session, uint8_t result) { + PBL_LOG_DBG("Sending result of %d", result); + comm_session_send_data(session, APP_ORDER_ENDPOINT_ID, (uint8_t*)&result, sizeof(result), + COMM_SESSION_DEFAULT_TIMEOUT); +} + +static void prv_handle_app_order_msg(CommSession *session, const uint8_t *data, uint32_t length) { + // call write order function, then fire an event for app install manager to tell the launcher + // to throw everything away or at least don't overwrite the data please. + uint8_t num_uuids = data[0]; + + if (num_uuids != (length / UUID_SIZE)) { + PBL_LOG_DBG("invalid length, num_uuids does not match with the length of message"); + prv_send_result(session, APP_ORDER_RES_INVALID); + } + + write_uuid_list_to_file((const Uuid *)&data[1], num_uuids); + prv_send_result(session, APP_ORDER_RES_SUCCESS); +} + +void app_order_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { + // header includes APP_ORDER_CMD and a num_uuids uint8_t + const uint8_t header_len = sizeof(AppOrderCommand) + sizeof(uint8_t); + + // Ensure it is a valid message. There is a list of UUID's after the header. + if ((length % UUID_SIZE) != header_len) { + PBL_LOG_DBG("invalid length, (length - header_len) not multiple of 16"); + prv_send_result(session, APP_ORDER_RES_INVALID); + return; + } + + switch (data[0]) { + case APP_ORDER_CMD: + PBL_LOG_DBG("Got APP_ORDER message"); + prv_handle_app_order_msg(session, &data[1], length - 1); + break; + default: + PBL_LOG_ERR("Invalid message received, first byte is %u", data[0]); + prv_send_result(session, APP_ORDER_RES_FAILURE); + break; + } +} diff --git a/src/fw/services/app_order_endpoint/wscript_build b/src/fw/services/app_order_endpoint/wscript_build new file mode 100644 index 0000000000..4774311cad --- /dev/null +++ b/src/fw/services/app_order_endpoint/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_app_order_endpoint', +) +bld.env.SERVICES.append('services_app_order_endpoint') diff --git a/src/fw/services/app_outbox_service/Kconfig b/src/fw/services/app_outbox_service/Kconfig new file mode 100644 index 0000000000..7614eacd9c --- /dev/null +++ b/src/fw/services/app_outbox_service/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_APP_OUTBOX_SERVICE + bool "App outbox" + help + App outbox messaging service. + +if SERVICE_APP_OUTBOX_SERVICE + +module = SERVICE_APP_OUTBOX_SERVICE +module-str = App outbox +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/app_outbox_service/service.c b/src/fw/services/app_outbox_service/service.c new file mode 100644 index 0000000000..6ad634042c --- /dev/null +++ b/src/fw/services/app_outbox_service/service.c @@ -0,0 +1,327 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/app_message/app_message_internal.h" +#include "kernel/pbl_malloc.h" +#include "os/mutex.h" +#include "process_management/process_manager.h" +#include "pbl/services/app_message/app_message_sender.h" +#include "pbl/services/app_outbox_service.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/list.h" + +PBL_LOG_MODULE_DEFINE(service_app_outbox_service, CONFIG_SERVICE_APP_OUTBOX_SERVICE_LOG_LEVEL); + +static PebbleRecursiveMutex *s_app_outbox_mutex; + +typedef struct { + AppOutboxMessage *head; + AppOutboxMessageHandler message_handler; + size_t consumer_data_length; + PebbleTask consumer_task; +} AppOutboxConsumer; + +//! Array with the consuming kernel services that have been registered at run-time: +static AppOutboxConsumer s_app_outbox_consumer[NumAppOutboxServiceTag]; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Declarations of permitted senders: + +typedef struct { + AppOutboxSentHandler sent_handler; + size_t max_length; + uint32_t max_pending_messages; +} AppOutboxSenderDef; + +extern void app_message_outbox_handle_app_outbox_message_sent(AppOutboxStatus status, void *cb_ctx); + +#ifdef UNITTEST +extern void test_app_outbox_sent_handler(AppOutboxStatus status, void *cb_ctx); +#endif + +//! Constant array defining the allowed handlers and their restrictions: +static const AppOutboxSenderDef s_app_outbox_sender_defs[] = { + [AppOutboxServiceTagAppMessageSender] = { + .sent_handler = app_message_outbox_handle_app_outbox_message_sent, + .max_length = (sizeof(AppMessageAppOutboxData) + APP_MSG_HDR_OVRHD_SIZE + APP_MSG_8K_DICT_SIZE), + .max_pending_messages = 1, + }, +#ifdef UNITTEST + [AppOutboxServiceTagUnitTest] = { + .sent_handler = test_app_outbox_sent_handler, + .max_length = 1, + .max_pending_messages = 2, + }, +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Syscalls + +static const AppOutboxSenderDef *prv_find_def_and_tag_by_handler(AppOutboxSentHandler sent_handler, + AppOutboxServiceTag *tag_out) { + for (AppOutboxServiceTag tag = 0; tag < NumAppOutboxServiceTag; ++tag) { + if (s_app_outbox_sender_defs[tag].sent_handler == sent_handler) { + if (tag_out) { + *tag_out = tag; + } + return &s_app_outbox_sender_defs[tag]; + } + } + if (tag_out) { + *tag_out = AppOutboxServiceTagInvalid; + } + return NULL; +} + +static void app_outbox_service_send(const uint8_t *data, size_t length, + AppOutboxSentHandler sent_handler, void *cb_ctx); + +DEFINE_SYSCALL(void, sys_app_outbox_send, const uint8_t *data, size_t length, + AppOutboxSentHandler sent_handler, void *cb_ctx) { + if (PRIVILEGE_WAS_ELEVATED) { + // Check that data is in app space: + syscall_assert_userspace_buffer(data, length); + } + + const AppOutboxSenderDef *def = prv_find_def_and_tag_by_handler(sent_handler, NULL); + if (!def) { + PBL_LOG_ERR("AppOutbox sent_handler not allowed <%p>", sent_handler); + syscall_failed(); + } + + const size_t max_length = def->max_length; + if (length > max_length) { + PBL_LOG_ERR("AppOutbox max_length exceeded %"PRIu32" vs %"PRIu32, + (uint32_t)length, (uint32_t)max_length); + syscall_failed(); + } + app_outbox_service_send(data, length, sent_handler, cb_ctx); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Helpers + +static void prv_lock(void) { + // Using one "global" lock for all app outboxes. + // If needed, we could easily give each app outbox its own mutex, but it seems overkill right now. + mutex_lock_recursive(s_app_outbox_mutex); +} + +static void prv_unlock(void) { + mutex_unlock_recursive(s_app_outbox_mutex); +} + +static AppOutboxConsumer *prv_consumer_for_tag(AppOutboxServiceTag tag) { + if (tag == AppOutboxServiceTagInvalid) { + return NULL; + } + AppOutboxConsumer *consumer = &s_app_outbox_consumer[tag]; + if (consumer->message_handler == NULL) { + return NULL; + } + return consumer; +} + +static void prv_schedule_sent_handler(AppOutboxSentHandler sent_handler, + void *cb_ctx, AppOutboxStatus status) { + if (!sent_handler) { + return; + } + PebbleEvent event = { + .type = PEBBLE_APP_OUTBOX_SENT_EVENT, + .app_outbox_sent = { + .sent_handler = sent_handler, + .cb_ctx = cb_ctx, + .status = status, + }, + }; + process_manager_send_event_to_process(PebbleTask_App, &event); +} + +//! @note This executes on App Task +static void prv_schedule_consumer_message_handler(AppOutboxConsumer *consumer, + AppOutboxMessage *message) { + void (*callback)(void *) = (__typeof__(callback))consumer->message_handler; + PebbleEvent event = { + .type = PEBBLE_APP_OUTBOX_MSG_EVENT, + .app_outbox_msg = { + .callback = callback, + .data = message, + }, + }; + sys_send_pebble_event_to_kernel(&event); +} + +static uint32_t prv_num_pending_messages(const AppOutboxConsumer *consumer) { + return list_count((ListNode *)consumer->head); +} + +static AppOutboxConsumer *prv_find_consumer_with_message(const AppOutboxMessage *message) { + AppOutboxMessage *head = (AppOutboxMessage *)list_get_head((ListNode *)message); + for (AppOutboxServiceTag tag = 0; tag < NumAppOutboxServiceTag; ++tag) { + if (s_app_outbox_consumer[tag].head == head) { + return &s_app_outbox_consumer[tag]; + } + } + return NULL; +} + +void prv_cleanup_pending_messages(AppOutboxConsumer *consumer, bool should_call_sent_handler) { + AppOutboxMessage *message = consumer->head; + consumer->head = NULL; + while (message) { + if (should_call_sent_handler) { + prv_schedule_sent_handler(message->sent_handler, message->cb_ctx, + AppOutboxStatusConsumerDoesNotExist); + } + + AppOutboxMessage *next = (AppOutboxMessage *)message->node.next; + message->node = (ListNode) {}; + // Don't free it, it's the responsibility of the consumer to eventually call + // app_outbox_service_consume_message(), which will free the message! + message = next; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Exported functions + +void app_outbox_service_register(AppOutboxServiceTag tag, + AppOutboxMessageHandler message_handler, + PebbleTask consumer_task, + size_t consumer_data_length) { + prv_lock(); + { + PBL_ASSERTN(!prv_consumer_for_tag(tag)); + AppOutboxConsumer *consumer = &s_app_outbox_consumer[tag]; + consumer->message_handler = message_handler; + consumer->consumer_data_length = consumer_data_length; + consumer->consumer_task = consumer_task; + } + prv_unlock(); +} + +void app_outbox_service_unregister(AppOutboxServiceTag service_tag) { + prv_lock(); + { + prv_cleanup_pending_messages(&s_app_outbox_consumer[service_tag], + true /* should_call_sent_handler */); + s_app_outbox_consumer[service_tag].message_handler = NULL; + } + prv_unlock(); +} + +//! @note This executes on App Task +//! Should only get called through the syscall, sys_app_outbox_send +static void app_outbox_service_send(const uint8_t *data, size_t length, + AppOutboxSentHandler sent_handler, void *cb_ctx) { + AppOutboxStatus status = AppOutboxStatusSuccess; + prv_lock(); + { + AppOutboxServiceTag tag; + const AppOutboxSenderDef *def = prv_find_def_and_tag_by_handler(sent_handler, &tag); + AppOutboxConsumer *consumer = prv_consumer_for_tag(tag); + if (!consumer) { + status = AppOutboxStatusConsumerDoesNotExist; + goto finally; + } + + if (prv_num_pending_messages(consumer) >= def->max_pending_messages) { + status = AppOutboxStatusOutOfResources; + goto finally; + } + + const size_t consumer_data_length = consumer->consumer_data_length; + AppOutboxMessage *message = + (AppOutboxMessage *)kernel_zalloc(sizeof(AppOutboxMessage) + consumer_data_length); + if (!message) { + status = AppOutboxStatusOutOfMemory; + goto finally; + } + + *message = (AppOutboxMessage) { + .data = data, + .length = length, + .sent_handler = sent_handler, + .cb_ctx = cb_ctx, + }; + + consumer->head = (AppOutboxMessage *)list_prepend((ListNode *)consumer->head, + (ListNode *)message); + + prv_schedule_consumer_message_handler(consumer, message); + } +finally: + if (AppOutboxStatusSuccess != status) { + prv_schedule_sent_handler(sent_handler, cb_ctx, status); + } + prv_unlock(); +} + +bool app_outbox_service_is_message_cancelled(AppOutboxMessage *message) { + prv_lock(); + bool cancelled = !prv_find_consumer_with_message(message); + prv_unlock(); + return cancelled; +} + +void app_outbox_service_consume_message(AppOutboxMessage *message, AppOutboxStatus status) { + prv_lock(); + { + if (app_outbox_service_is_message_cancelled(message)) { + // Don't call the sent_handler + goto finally; + } + AppOutboxConsumer *consumer = prv_find_consumer_with_message(message); + PBL_ASSERTN(consumer); + list_remove(&message->node, (ListNode **)&consumer->head, NULL); + prv_schedule_sent_handler(message->sent_handler, message->cb_ctx, status); + } +finally: + kernel_free(message); + prv_unlock(); +} + +void app_outbox_service_cleanup_all_pending_messages(void) { + prv_lock(); + for (AppOutboxServiceTag tag = 0; tag < NumAppOutboxServiceTag; ++tag) { + AppOutboxConsumer *consumer = &s_app_outbox_consumer[tag]; + prv_cleanup_pending_messages(consumer, false /* should_call_sent_handler */); + } + prv_unlock(); +} + +void app_outbox_service_cleanup_event(PebbleEvent *event) { + if (event->type != PEBBLE_APP_OUTBOX_MSG_EVENT) { + return; + } + // Call consume directly to clean up the message, it's not valid anyway: + app_outbox_service_consume_message((AppOutboxMessage *)event->app_outbox_msg.data, + AppOutboxStatusSuccess /* ignored */); +} + +void app_outbox_service_init(void) { + s_app_outbox_mutex = mutex_create_recursive(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Unit Test Interfaces + +void app_outbox_service_deinit(void) { + app_outbox_service_cleanup_all_pending_messages(); + memset(&s_app_outbox_consumer, 0, sizeof(s_app_outbox_consumer)); + mutex_destroy((PebbleMutex *)s_app_outbox_mutex); + s_app_outbox_mutex = NULL; +} + +uint32_t app_outbox_service_max_pending_messages(AppOutboxServiceTag tag) { + return s_app_outbox_sender_defs[tag].max_pending_messages; +} + +uint32_t app_outbox_service_max_message_length(AppOutboxServiceTag tag) { + return s_app_outbox_sender_defs[tag].max_length; +} diff --git a/src/fw/services/app_outbox_service/wscript_build b/src/fw/services/app_outbox_service/wscript_build new file mode 100644 index 0000000000..b4dd938e45 --- /dev/null +++ b/src/fw/services/app_outbox_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_app_outbox_service', +) +bld.env.SERVICES.append('services_app_outbox_service') diff --git a/src/fw/services/audio_endpoint/Kconfig b/src/fw/services/audio_endpoint/Kconfig new file mode 100644 index 0000000000..946ce9df9c --- /dev/null +++ b/src/fw/services/audio_endpoint/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_AUDIO_ENDPOINT + bool "Audio endpoint" + help + Audio streaming endpoint. + +if SERVICE_AUDIO_ENDPOINT + +module = SERVICE_AUDIO_ENDPOINT +module-str = Audio endpoint +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/audio_endpoint/service.c b/src/fw/services/audio_endpoint/service.c new file mode 100644 index 0000000000..c15c0a8541 --- /dev/null +++ b/src/fw/services/audio_endpoint/service.c @@ -0,0 +1,160 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/audio_endpoint_private.h" + +#include "comm/bt_lock.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/circular_buffer.h" + +PBL_LOG_MODULE_DEFINE(service_audio_endpoint, CONFIG_SERVICE_AUDIO_ENDPOINT_LOG_LEVEL); + +#define AUDIO_ENDPOINT (10000) + +#define ACTIVE_MODE_TIMEOUT (10000) +#define ACTIVE_MODE_START_BUFFER (100) + +_Static_assert(ACTIVE_MODE_TIMEOUT > ACTIVE_MODE_START_BUFFER, + "ACTIVE_MODE_TIMEOUT must be greater than ACTIVE_MODE_START_BUFFER"); + +typedef struct { + AudioEndpointSessionId id; + AudioEndpointStopTransferCallback stop_transfer; + TimerID active_mode_trigger; +} AudioEndpointSession; + +static AudioEndpointSessionId s_session_id = AUDIO_ENDPOINT_SESSION_INVALID_ID; +static AudioEndpointSession s_session; +static uint32_t s_dropped_frames; + +static void prv_session_deinit(bool call_stop_handler) { + bt_lock(); + if (call_stop_handler && s_session.stop_transfer) { + s_session.stop_transfer(s_session.id); + } + + if (s_session.active_mode_trigger != TIMER_INVALID_ID) { + new_timer_delete(s_session.active_mode_trigger); + s_session.active_mode_trigger = TIMER_INVALID_ID; + CommSession *comm_session = comm_session_get_system_session(); + comm_session_set_responsiveness( + comm_session, BtConsumerPpAudioEndpoint, ResponseTimeMax, 0); + } + + s_session.id = AUDIO_ENDPOINT_SESSION_INVALID_ID; + s_session.stop_transfer = NULL; + bt_unlock(); + + if (s_dropped_frames > 0) { + PBL_LOG_INFO("Dropped %"PRIu32" frames during audio transfer", s_dropped_frames); + } +} + +void audio_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { + MsgId msg_id = data[0]; + if (size >= sizeof(StopTransferMsg) && msg_id == MsgIdStopTransfer) { + StopTransferMsg *msg = (StopTransferMsg *)data; + + if (msg->session_id == s_session.id) { + prv_session_deinit(true /* call_stop_handler */); + } else { + PBL_LOG_WRN("Received mismatching session id: %u vs %u", + msg->session_id, s_session.id); + } + } +} + +static void prv_start_active_mode(void *data) { + CommSession *comm_session = comm_session_get_system_session(); + comm_session_set_responsiveness_ext(comm_session, BtConsumerPpAudioEndpoint, ResponseTimeMin, + MIN_LATENCY_MODE_TIMEOUT_AUDIO_SECS, + NULL /* granted_handler */); +} + +AudioEndpointSessionId audio_endpoint_setup_transfer(AudioEndpointStopTransferCallback stop_transfer) { + + if (s_session.id != AUDIO_ENDPOINT_SESSION_INVALID_ID) { + return AUDIO_ENDPOINT_SESSION_INVALID_ID; + } + + bt_lock(); + + s_session.id = ++s_session_id; + s_session.stop_transfer = stop_transfer; + s_session.active_mode_trigger = new_timer_create(); + s_dropped_frames = 0; + + // restart active mode before it expires, this way it will never be off during the transfer + new_timer_start(s_session.active_mode_trigger, ACTIVE_MODE_TIMEOUT - ACTIVE_MODE_START_BUFFER, + prv_start_active_mode, NULL, TIMER_START_FLAG_REPEATING); + + bt_unlock(); + + prv_start_active_mode(NULL); + + return s_session.id; +} + +void audio_endpoint_add_frame(AudioEndpointSessionId session_id, uint8_t *frame, + uint8_t frame_size) { + PBL_ASSERTN(session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); + + if (s_session.id != session_id) { + return; + } + + CommSession *comm_session = comm_session_get_system_session(); + SendBuffer *sb = comm_session_send_buffer_begin_write(comm_session, AUDIO_ENDPOINT, + sizeof(DataTransferMsg) + frame_size + 1, + 0 /* timeout_ms, never block */); + if (!sb) { + s_dropped_frames++; + PBL_LOG_DBG("Dropping a frame..."); + return; + } + + uint8_t header[sizeof(DataTransferMsg) + sizeof(uint8_t) /* frame_size */]; + DataTransferMsg *msg = (DataTransferMsg *) header; + *msg = (const DataTransferMsg) { + .msg_id = MsgIdDataTransfer, + .session_id = session_id, + .frame_count = 1, + }; + msg->frames[0] = frame_size; + + comm_session_send_buffer_write(sb, header, sizeof(header)); + comm_session_send_buffer_write(sb, frame, frame_size); + comm_session_send_buffer_end_write(sb); +} + +void audio_endpoint_cancel_transfer(AudioEndpointSessionId session_id) { + PBL_ASSERTN(session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); + + if (s_session.id != session_id) { + return; + } + + prv_session_deinit(false /* call_stop_handler */); +} + +void audio_endpoint_stop_transfer(AudioEndpointSessionId session_id) { + PBL_ASSERTN(session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); + + if (s_session.id != session_id) { + return; + } + + StopTransferMsg msg = (const StopTransferMsg) { + .msg_id = MsgIdStopTransfer, + .session_id = session_id, + }; + + prv_session_deinit(false /* call_stop_handler */); + + comm_session_send_data(comm_session_get_system_session(), AUDIO_ENDPOINT, (const uint8_t *) &msg, + sizeof(msg), COMM_SESSION_DEFAULT_TIMEOUT); +} diff --git a/src/fw/services/audio_endpoint/wscript_build b/src/fw/services/audio_endpoint/wscript_build new file mode 100644 index 0000000000..f3d94f9207 --- /dev/null +++ b/src/fw/services/audio_endpoint/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_audio_endpoint', +) +bld.env.SERVICES.append('services_audio_endpoint') diff --git a/src/fw/services/battery/Kconfig b/src/fw/services/battery/Kconfig new file mode 100644 index 0000000000..f70f87f9c0 --- /dev/null +++ b/src/fw/services/battery/Kconfig @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_BATTERY + bool "Battery" + help + Battery monitor and state-of-charge estimation. + +config NRF_FUEL_GAUGE + bool "Nordic nRF Fuel Gauge" + depends on PMIC_NPM1300 + default y if PMIC_NPM1300 + help + Use the Nordic nRF Fuel Gauge library for state-of-charge + estimation. Requires the nPM1300 PMIC; selects both the prebuilt + nonfree fuel-gauge lib and the matching battery service impl. + +if SERVICE_BATTERY + +module = SERVICE_BATTERY +module-str = Battery +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/common/battery/battery_monitor.c b/src/fw/services/battery/battery_monitor.c similarity index 79% rename from src/fw/services/common/battery/battery_monitor.c rename to src/fw/services/battery/battery_monitor.c index e49f7cd17d..ba9c2cf2e6 100644 --- a/src/fw/services/common/battery/battery_monitor.c +++ b/src/fw/services/battery/battery_monitor.c @@ -1,33 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/battery/battery_monitor.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/battery/battery_monitor.h" #include "board/board.h" #include "kernel/low_power.h" #include "kernel/util/standby.h" -#include "services/common/firmware_update.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "util/ratio.h" #include -#define BATT_LOG_COLOR LOG_COLOR_YELLOW +PBL_LOG_MODULE_DEFINE(service_battery, CONFIG_SERVICE_BATTERY_LOG_LEVEL); // State machine stuff @@ -42,6 +29,7 @@ typedef enum { PowerStateGood, PowerStateLowPower, PowerStateCritical, + PowerStatePluggedIn, PowerStateStandby } PowerStateID; @@ -55,6 +43,7 @@ static const PowerState power_states[] = { [PowerStateGood] = { 0 }, [PowerStateLowPower] = { .enter = prv_enter_lpm, .exit = prv_exit_lpm }, [PowerStateCritical] = { .enter = prv_begin_standby_timer, .exit = prv_exit_critical }, + [PowerStatePluggedIn] = { 0 }, [PowerStateStandby] = { .enter = prv_enter_standby } }; @@ -80,25 +69,27 @@ static void prv_transition(PowerStateID next_state) { } static void prv_enter_lpm(void) { -#ifndef BATTERY_DEBUG if (!firmware_update_is_in_progress()) { low_power_enter(); } -#endif - PBL_LOG_COLOR(LOG_LEVEL_INFO, BATT_LOG_COLOR, "Battery low: enter low power mode"); + PBL_LOG_INFO("Battery low: enter low power mode"); } static void prv_resume_normal_operation(void) { low_power_exit(); - PBL_LOG_COLOR(LOG_LEVEL_INFO, BATT_LOG_COLOR, "Battery good: resume normal operation"); + PBL_LOG_INFO("Battery good: resume normal operation"); } static void prv_exit_critical(void) { + // Cancel the standby timer so we don't enter standby if we're no longer critical + // (e.g. charger was plugged in before the timer expired). + new_timer_stop(s_standby_timer_id); + // Checking the state here is a bit of a hack because the state machine does not have proper // transition actions, only entry/exit actions. // We check that the state is PowerStateGood because the state machine does not transition through // all states in between the new and old states in a transition. - if (s_power_state == PowerStateGood) { + if (s_power_state == PowerStateGood || s_power_state == PowerStatePluggedIn) { prv_resume_normal_operation(); } } @@ -106,7 +97,7 @@ static void prv_exit_critical(void) { static void prv_exit_lpm(void) { // Checking the state here is a bit of a hack because the state machine does not have proper // transition actions, only entry/exit actions - if (s_power_state == PowerStateGood) { + if (s_power_state == PowerStateGood || s_power_state == PowerStatePluggedIn) { prv_resume_normal_operation(); } } @@ -123,7 +114,7 @@ static void prv_standby_timer_callback(void* data) { } static void prv_begin_standby_timer(void) { - PBL_LOG_COLOR(LOG_LEVEL_INFO, BATT_LOG_COLOR, "Battery critical: begin standby timer"); + PBL_LOG_INFO("Battery critical: begin standby timer"); // If the watch was already running, give them 30s, otherwise just 2s. uint32_t standby_timeout = (s_first_run) ? 2000: 30000; new_timer_start(s_standby_timer_id, standby_timeout, @@ -131,7 +122,7 @@ static void prv_begin_standby_timer(void) { } static void system_task_handle_battery_critical(void* data) { - PBL_LOG_COLOR(LOG_LEVEL_INFO, BATT_LOG_COLOR, "Battery critical: go to standby mode"); + PBL_LOG_INFO("Battery critical: go to standby mode"); if (low_power_is_active()) { low_power_standby(); } else { @@ -167,8 +158,6 @@ static void prv_log_battery_state(PreciseBatteryChargeState state) { (s_prev_batt_state.all != new_batt_state.all) || s_first_run) { s_prev_batt_state.all = new_batt_state.all; - PBL_LOG_COLOR(LOG_LEVEL_INFO, BATT_LOG_COLOR, "Percent: %d Charging: %d Plugged: %d", - percent, state.is_charging, state.is_plugged); } } @@ -183,24 +172,39 @@ void battery_monitor_handle_state_change_event(PreciseBatteryChargeState state) // Similarly, if the battery voltage has rebounded when the timer expires, the shutdown // will not occur. +#ifdef CONFIG_QEMU + // QEMU has no real battery, so never enter LPM or standby — otherwise + // `pebble emu-battery --percent 1` (and any low value) shuts the emulator + // down or locks it into the low-power UI. + bool critical = false; + bool low_power = false; + s_low_on_first_run = false; +#else bool critical = (state.charge_percent == 0) && !state.is_charging; -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW const uint32_t LOW_POWER_PERCENT = ratio32_from_percent(BOARD_CONFIG_POWER.low_power_threshold); bool low_power = !state.is_charging && (state.charge_percent <= LOW_POWER_PERCENT); - s_low_on_first_run = s_low_on_first_run || (low_power && s_first_run); + if (low_power && s_first_run && !state.is_plugged) { + s_low_on_first_run = true; + } else if (!low_power) { + s_low_on_first_run = false; + } #else const uint32_t PRF_LOW_POWER_THRESHOLD_PERCENT = ratio32_from_percent(5); // We want to keep the LPM UI up until we've hit 10% regardless of charging bool low_power = state.charge_percent < PRF_LOW_POWER_THRESHOLD_PERCENT; s_low_on_first_run = false; +#endif #endif PowerStateID new_state; - if (critical || s_low_on_first_run) { + if (state.is_plugged) { + new_state = PowerStatePluggedIn; + } else if (critical || s_low_on_first_run) { new_state = PowerStateCritical; } else if (low_power) { new_state = PowerStateLowPower; diff --git a/src/fw/services/battery/nrf_fuel_gauge/battery_curve.c b/src/fw/services/battery/nrf_fuel_gauge/battery_curve.c new file mode 100644 index 0000000000..5864b05f47 --- /dev/null +++ b/src/fw/services/battery/nrf_fuel_gauge/battery_curve.c @@ -0,0 +1,33 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "board/board.h" +#include "pbl/services/battery/battery_curve.h" + +uint32_t battery_curve_get_percent_remaining(uint32_t hours) { + return ((hours * 100) / BOARD_CONFIG_POWER.battery_capacity_hours) + + BOARD_CONFIG_POWER.low_power_threshold; +} + +// TODO: nRF Fuel gauge lib provides TTE estimation +// First we need to evaluate if it's good enough for our use case, then, if +// it is, we need to refactor APIs and battery FSM a bit to use TTE value +// directly (i.e. not from % remaining). +uint32_t battery_curve_get_hours_remaining(uint32_t percent_remaining) { + if (percent_remaining <= BOARD_CONFIG_POWER.low_power_threshold) { + return 0; + } + percent_remaining -= BOARD_CONFIG_POWER.low_power_threshold; + return ((BOARD_CONFIG_POWER.battery_capacity_hours * percent_remaining) / 100); +} + + +// Stubs for tests (need fixing/test adjustments) +int32_t battery_curve_lookup_percent_with_scaling_factor( + int battery_mv, bool is_charging, uint32_t scaling_factor) { + return 0U; +} + +uint32_t battery_curve_lookup_voltage_by_percent(uint32_t percent, bool is_charging) { + return 0U; +} \ No newline at end of file diff --git a/src/fw/services/battery/nrf_fuel_gauge/battery_state.c b/src/fw/services/battery/nrf_fuel_gauge/battery_state.c new file mode 100644 index 0000000000..f60468e644 --- /dev/null +++ b/src/fw/services/battery/nrf_fuel_gauge/battery_state.c @@ -0,0 +1,620 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "board/board.h" +#include "drivers/battery.h" +#include "drivers/pmic.h" +#include "drivers/rtc.h" +#include "kernel/events.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/ratio.h" + +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/settings/settings_file.h" +#endif + +#ifdef CONFIG_MFG +#include "drivers/flash.h" +#include "flash_region/flash_region.h" +#endif + +#include "nrf_fuel_gauge.h" + +PBL_LOG_MODULE_DECLARE(service_battery, CONFIG_SERVICE_BATTERY_LOG_LEVEL); + +#if !defined(CONFIG_RECOVERY_FW) || defined(CONFIG_MFG) +#define FUEL_GAUGE_STATEFUL 1 +#else +#define FUEL_GAUGE_STATEFUL 0 +#endif + +#define ALWAYS_UPDATE_PCT 10.0f +#define RECONNECTION_DELAY_MS (1 * 1000) +// TODO: Adjust sample rate based on activity periods once we have good +// power consumption profiles +#define BATTERY_SAMPLE_RATE_S 1 + +#define LOG_MIN_SEC 30 + +// Minimum valid battery voltage for battery swap detection (mV) +#define BATTERY_MIN_VALID_VOLTAGE_MV 3300 + +static const struct battery_model prv_battery_model = { +#ifdef CONFIG_BOARD_FAMILY_ASTERIX +#include "battery_asterix.inc" +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) +#include "battery_obelix.inc" +#elif defined(CONFIG_BOARD_FAMILY_GETAFIX) +#include "battery_getafix.inc" +#else +#error "Battery model not defined for this platform" +#endif +}; + +static PreciseBatteryChargeState s_last_battery_charge_state; +static TimerID s_periodic_timer_id = TIMER_INVALID_ID; + +static volatile bool s_update_pending = false; +static volatile bool s_pending_force_update = false; + +static BatteryChargeStatus s_last_chg_status; +static uint64_t prv_ref_time; +static int32_t s_last_voltage_mv; +static int32_t s_last_temp_mc; +static uint32_t s_last_soc_cpct; +static int32_t s_analytics_last_voltage_mv; +static uint32_t s_analytics_last_cpct; +static uint32_t s_last_tte; +static uint32_t s_last_ttf; +static RtcTicks s_last_log; +static bool s_charger_enabled; + +#if FUEL_GAUGE_STATEFUL +#define FUEL_GAUGE_SAVE_INTERVAL_S 300 + +static uint32_t s_save_counter; + +#ifdef CONFIG_MFG +// In manufacturing firmware, use dedicated MFG_BATTERY_STATE flash region +static void prv_erase_state(void) { + flash_erase_subsector_blocking(FLASH_REGION_MFG_BATTERY_STATE_BEGIN); + PBL_LOG_DBG("Fuel gauge state erased"); +} + +static bool prv_load_state(void *state, size_t size) { + if (size > (FLASH_REGION_MFG_BATTERY_STATE_END - FLASH_REGION_MFG_BATTERY_STATE_BEGIN)) { + return false; + } + + flash_read_bytes(state, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, size); + + // Check if the flash region contains valid data (not all 0xFF) + uint8_t *bytes = (uint8_t *)state; + bool all_erased = true; + for (size_t i = 0; i < size; i++) { + if (bytes[i] != 0xFF) { + all_erased = false; + break; + } + } + + if (all_erased) { + return false; + } + + PBL_LOG_DBG("Fuel gauge state loaded"); + + return true; +} + +static void prv_save_state(void) { + uint8_t buf[nrf_fuel_gauge_state_size]; + + if (nrf_fuel_gauge_state_get(buf, sizeof(buf)) != 0) { + PBL_LOG_ERR("Failed to get fuel gauge state"); + return; + } + + if (sizeof(buf) > (FLASH_REGION_MFG_BATTERY_STATE_END - FLASH_REGION_MFG_BATTERY_STATE_BEGIN)) { + PBL_LOG_ERR("Fuel gauge state too large for MFG_BATTERY_STATE region"); + return; + } + + flash_erase_subsector_blocking(FLASH_REGION_MFG_BATTERY_STATE_BEGIN); + flash_write_bytes(buf, FLASH_REGION_MFG_BATTERY_STATE_BEGIN, sizeof(buf)); + + PBL_LOG_DBG("Fuel gauge state saved"); +} +#else +// In normal firmware, use settings file +#define FUEL_GAUGE_SETTINGS_FILE_NAME "fgs" +#define FUEL_GAUGE_SETTINGS_MAX_SIZE 2048 + +static const uint32_t FUEL_GAUGE_STATE_KEY = 1; + +static void prv_erase_state(void) { + SettingsFile file; + status_t ret; + + ret = settings_file_open(&file, FUEL_GAUGE_SETTINGS_FILE_NAME, + FUEL_GAUGE_SETTINGS_MAX_SIZE); + if (ret != S_SUCCESS) { + return; + } + + settings_file_delete(&file, &FUEL_GAUGE_STATE_KEY, sizeof(FUEL_GAUGE_STATE_KEY)); + settings_file_close(&file); + + PBL_LOG_DBG("Fuel gauge state erased"); +} + +static bool prv_load_state(void *state, size_t size) { + SettingsFile file; + status_t ret; + + ret = settings_file_open(&file, FUEL_GAUGE_SETTINGS_FILE_NAME, + FUEL_GAUGE_SETTINGS_MAX_SIZE); + if (ret != S_SUCCESS) { + return false; + } + + int len = settings_file_get_len(&file, &FUEL_GAUGE_STATE_KEY, + sizeof(FUEL_GAUGE_STATE_KEY)); + if (len != (int)size) { + settings_file_close(&file); + return false; + } + + ret = settings_file_get(&file, &FUEL_GAUGE_STATE_KEY, + sizeof(FUEL_GAUGE_STATE_KEY), state, size); + settings_file_close(&file); + + if (ret != S_SUCCESS) { + return false; + } + + PBL_LOG_DBG("Fuel gauge state loaded"); + + return true; +} + +static void prv_save_state(void) { + uint8_t buf[nrf_fuel_gauge_state_size]; + SettingsFile file; + status_t ret; + + if (nrf_fuel_gauge_state_get(buf, sizeof(buf)) != 0) { + PBL_LOG_ERR("Failed to get fuel gauge state"); + return; + } + + ret = settings_file_open(&file, FUEL_GAUGE_SETTINGS_FILE_NAME, + FUEL_GAUGE_SETTINGS_MAX_SIZE); + if (ret != S_SUCCESS) { + PBL_LOG_ERR("Failed to open fuel gauge settings file"); + return; + } + + ret = settings_file_set(&file, &FUEL_GAUGE_STATE_KEY, + sizeof(FUEL_GAUGE_STATE_KEY), buf, sizeof(buf)); + settings_file_close(&file); + + if (ret != S_SUCCESS) { + PBL_LOG_ERR("Failed to save fuel gauge state"); + } else { + PBL_LOG_DBG("Fuel gauge state saved"); + } +} +#endif // CONFIG_MFG +#endif // FUEL_GAUGE_STATEFUL + +static void prv_schedule_update(uint32_t delay, bool force_update); + +static int prv_fuel_gauge_init_common(const BatteryConstants *constants, bool load_state) { + struct nrf_fuel_gauge_init_parameters parameters = {0}; + int ret; + + parameters.model = &prv_battery_model; + parameters.v0 = (float)constants->v_mv / 1000.0f; + parameters.i0 = (float)constants->i_ua / 1000000.0f; + parameters.t0 = (float)constants->t_mc / 1000.0f; + +#if FUEL_GAUGE_STATEFUL + uint8_t saved_state[nrf_fuel_gauge_state_size]; + + if (load_state) { + if (prv_load_state(saved_state, sizeof(saved_state))) { + parameters.state = saved_state; + } + } +#endif + + ret = nrf_fuel_gauge_init(¶meters, NULL); +#if FUEL_GAUGE_STATEFUL + if (ret != 0 && parameters.state != NULL) { + PBL_LOG_WRN("Failed to initialize fuel gauge with saved state, erasing"); + + prv_erase_state(); + + parameters.state = NULL; + ret = nrf_fuel_gauge_init(¶meters, NULL); + } +#endif + + return ret; +} + +static void prv_charge_status_inform(BatteryChargeStatus chg_status) { + union nrf_fuel_gauge_ext_state_info_data state_info; + int ret; + + switch (chg_status) { + case BatteryChargeStatusComplete: + state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_COMPLETE; + break; + case BatteryChargeStatusTrickle: + state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_TRICKLE; + break; + case BatteryChargeStatusCC: + state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_CC; + break; + case BatteryChargeStatusCV: + state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_CV; + break; + default: + state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_IDLE; + break; + } + + ret = nrf_fuel_gauge_ext_state_update(NRF_FUEL_GAUGE_EXT_STATE_INFO_CHARGE_STATE_CHANGE, + &state_info); + PBL_ASSERTN(ret == 0); +} + +static void prv_battery_state_put_change_event(PreciseBatteryChargeState state) { + PebbleEvent e = { + .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, + .battery_state = + { + .new_state = state, + }, + }; + event_put(&e); +} + +static void prv_update_state(void *force_update) { + BatteryChargeStatus chg_status; + BatteryConstants constants; + RtcTicks now, delta; + uint8_t pct_int; + bool is_plugged; + bool is_charging; + bool update; + float pct; + int ret; + + s_update_pending = false; + update = (force_update != NULL) || s_pending_force_update; + s_pending_force_update = false; + + ret = battery_get_constants(&constants); + if (ret < 0) { + PBL_LOG_ERR("Could not obtain constants, skipping update (%d)", ret); + return; + } + + is_plugged = battery_is_usb_connected_impl(); + if (is_plugged != s_last_battery_charge_state.is_plugged) { + ret = nrf_fuel_gauge_ext_state_update(is_plugged + ? NRF_FUEL_GAUGE_EXT_STATE_INFO_VBUS_CONNECTED + : NRF_FUEL_GAUGE_EXT_STATE_INFO_VBUS_DISCONNECTED, + NULL); + PBL_ASSERTN(ret == 0); + s_last_battery_charge_state.is_plugged = is_plugged; + if (is_plugged) { + PBL_ANALYTICS_TIMER_STOP(battery_discharge_duration_ms); + } else { + PBL_ANALYTICS_TIMER_START(battery_discharge_duration_ms); + } + update = true; + } + + ret = battery_charge_status_get(&chg_status); + if (ret < 0) { + PBL_LOG_ERR("Could not obtain charge status, skipping update (%d)", ret); + return; + } + + if (chg_status != s_last_chg_status) { + s_last_chg_status = chg_status; + prv_charge_status_inform(chg_status); + } + + is_charging = is_plugged && !(chg_status == BatteryChargeStatusComplete || + chg_status == BatteryChargeStatusUnknown); + if (is_charging != s_last_battery_charge_state.is_charging) { + s_last_battery_charge_state.is_charging = is_charging; + if (is_charging) { + PBL_ANALYTICS_TIMER_START(battery_charge_time_ms); + } else { + PBL_ANALYTICS_TIMER_STOP(battery_charge_time_ms); + } + update = true; + } + + s_last_voltage_mv = constants.v_mv; + s_last_temp_mc = constants.t_mc; + + now = rtc_get_ticks(); + delta = (now - prv_ref_time) / RTC_TICKS_HZ; + prv_ref_time = now; + + pct = nrf_fuel_gauge_process((float)constants.v_mv / 1000.0f, (float)constants.i_ua / 1000000.0f, + (float)constants.t_mc / 1000.0f, (float)delta, NULL); + + pct_int = (uint8_t)ceilf(pct); + s_last_soc_cpct = (uint32_t)(pct * 100.0f); + if (pct_int != s_last_battery_charge_state.pct) { + s_last_battery_charge_state.pct = pct_int; + s_last_battery_charge_state.charge_percent = (uint32_t)(pct * RATIO32_MAX) / 100U; + update = true; + } + + if (s_last_battery_charge_state.is_charging) { + float ttf; + + ttf = nrf_fuel_gauge_ttf_get(); + if (!isnanf(ttf)) { + s_last_ttf = (uint32_t)ttf; + } + + s_last_tte = 0U; + } else { + float tte; + + tte = nrf_fuel_gauge_tte_get(); + if (!isnanf(tte)) { + s_last_tte = (uint32_t)tte; + } + + s_last_ttf = 0U; + } + +#if FUEL_GAUGE_STATEFUL + if (update || (++s_save_counter >= FUEL_GAUGE_SAVE_INTERVAL_S)) { + s_save_counter = 0; + prv_save_state(); + } +#endif + + PBL_LOG_VERBOSE("Battery state: v_mv: %ld, i_ua: %ld, t_mc: %ld, td: %lu, soc: %u, tte: %lu, ttf: %lu", + constants.v_mv, constants.i_ua, constants.t_mc, (uint32_t)delta, + s_last_battery_charge_state.pct, s_last_tte, s_last_ttf); + + if (update || (((now - s_last_log) / RTC_TICKS_HZ > LOG_MIN_SEC) && + (s_last_battery_charge_state.is_charging || (pct < ALWAYS_UPDATE_PCT)))) { + PBL_LOG_INFO("Percent: %" PRIu8 ", V: %" PRId32 " mV, I: %" PRId32 " uA, " + "T: %" PRId32 " mC, charging: %s, plugged: %s", + s_last_battery_charge_state.pct, constants.v_mv, constants.i_ua, constants.t_mc, + s_last_battery_charge_state.is_charging ? "yes" : "no", + s_last_battery_charge_state.is_plugged ? "yes" : "no"); + prv_battery_state_put_change_event(s_last_battery_charge_state); + s_last_log = now; + } + + // Enable battery charging after fuel gauge state has been updated for the first time + if (!s_charger_enabled) { + s_charger_enabled = true; + pmic_set_charger_state(true); + } +} + +static void prv_enqueue_update(bool force) { + if (force) { + s_pending_force_update = true; + } + if (s_update_pending) { + return; + } + if (system_task_add_callback(prv_update_state, NULL)) { + s_update_pending = true; + } +} + +static void prv_update_callback(void *data) { + new_timer_stop(s_periodic_timer_id); + prv_enqueue_update(data != NULL); +} + +static void prv_callback_from_regular_timer(void *data) { + // no need to stop the new_timer here, since this came from the regular_timer + prv_enqueue_update(false); +} + +static void prv_schedule_update(uint32_t delay, bool force_update) { + bool success = new_timer_start(s_periodic_timer_id, delay, prv_update_callback, + (void *)force_update, 0 /*flags*/); + PBL_ASSERTN(success); +} + +void battery_state_force_update(void) { prv_schedule_update(0, true); } + +void battery_state_init(void) { + int ret; + struct nrf_fuel_gauge_runtime_parameters runtime_parameters = {0}; + BatteryConstants constants; + + ret = battery_get_constants(&constants); + PBL_ASSERTN(ret == 0); + + s_last_voltage_mv = constants.v_mv; + + ret = prv_fuel_gauge_init_common(&constants, true); + PBL_ASSERTN(ret == 0); + + ret = nrf_fuel_gauge_ext_state_update( + NRF_FUEL_GAUGE_EXT_STATE_INFO_CHARGE_CURRENT_LIMIT, + &(union nrf_fuel_gauge_ext_state_info_data){ + .charge_current_limit = (float)NPM1300_CONFIG.chg_current_ma / 1000.0f}); + PBL_ASSERTN(ret == 0); + + ret = nrf_fuel_gauge_ext_state_update( + NRF_FUEL_GAUGE_EXT_STATE_INFO_TERM_CURRENT, + &(union nrf_fuel_gauge_ext_state_info_data){ + .charge_term_current = + (float)(NPM1300_CONFIG.chg_current_ma * NPM1300_CONFIG.term_current_pct / 100U) / + 1000.0f}); + PBL_ASSERTN(ret == 0); + + runtime_parameters.a = NAN_F; + runtime_parameters.b = NAN_F; + runtime_parameters.c = NAN_F; + runtime_parameters.d = NAN_F; + runtime_parameters.discard_positive_deltaz = true; + + nrf_fuel_gauge_param_adjust(&runtime_parameters); + + ret = battery_charge_status_get(&s_last_chg_status); + PBL_ASSERTN(ret == 0); + + prv_charge_status_inform(s_last_chg_status); + + s_last_battery_charge_state.is_plugged = battery_is_usb_connected_impl(); + s_last_battery_charge_state.is_charging = s_last_battery_charge_state.is_plugged && + !(s_last_chg_status == BatteryChargeStatusComplete || + s_last_chg_status == BatteryChargeStatusUnknown); + + float pct = nrf_fuel_gauge_process((float)constants.v_mv / 1000.0f, + (float)constants.i_ua / 1000000.0f, + (float)constants.t_mc / 1000.0f, 0.0f, NULL); +#if FUEL_GAUGE_STATEFUL + if ((uint8_t)ceilf(pct) == 0 && constants.v_mv >= BATTERY_MIN_VALID_VOLTAGE_MV) { + PBL_LOG_WRN("Invalid state detected, reloading without state"); + prv_erase_state(); + ret = prv_fuel_gauge_init_common(&constants, false); + PBL_ASSERTN(ret == 0); + pct = nrf_fuel_gauge_process((float)constants.v_mv / 1000.0f, + (float)constants.i_ua / 1000000.0f, + (float)constants.t_mc / 1000.0f, 0.0f, NULL); + } +#endif + + prv_ref_time = rtc_get_ticks(); + + s_last_soc_cpct = (uint32_t)(pct * 100.0f); + s_last_battery_charge_state.pct = (uint8_t)ceilf(pct); + s_last_battery_charge_state.charge_percent = (uint32_t)(pct * RATIO32_MAX) / 100U; + + if (s_last_battery_charge_state.is_charging) { + PBL_ANALYTICS_TIMER_START(battery_charge_time_ms); + } else if (!s_last_battery_charge_state.is_plugged) { + PBL_ANALYTICS_TIMER_START(battery_discharge_duration_ms); + } + + s_periodic_timer_id = new_timer_create(); + + battery_state_force_update(); + + static RegularTimerInfo battery_regular_timer = { + .cb = prv_callback_from_regular_timer + }; + regular_timer_add_multisecond_callback(&battery_regular_timer, BATTERY_SAMPLE_RATE_S); + + s_analytics_last_voltage_mv = s_last_voltage_mv; + s_analytics_last_cpct = s_last_soc_cpct; +} + +void battery_state_handle_connection_event(bool is_connected) { + prv_schedule_update(RECONNECTION_DELAY_MS, true); +} + +DEFINE_SYSCALL(BatteryChargeState, sys_battery_get_charge_state, void) { + return battery_get_charge_state(); +} + +BatteryChargeState battery_get_charge_state(void) { + BatteryChargeState state; + + state.charge_percent = s_last_battery_charge_state.pct; + state.is_charging = s_last_battery_charge_state.is_charging; + state.is_plugged = s_last_battery_charge_state.is_plugged; + + return state; +} + +// For unit tests +TimerID battery_state_get_periodic_timer_id(void) { return s_periodic_timer_id; } + +uint16_t battery_state_get_voltage(void) { return (uint16_t)s_last_voltage_mv; } + +int32_t battery_state_get_temperature(void) { return s_last_temp_mc; } + +#include "console/prompt.h" +void command_print_battery_status(void) { + char buffer[32]; + + prompt_send_response_fmt(buffer, 32, "%" PRId32 " mV", s_last_voltage_mv); + prompt_send_response_fmt(buffer, 32, "soc: %" PRIu8 "%% (%" PRIu32 ")", + s_last_battery_charge_state.pct, + s_last_battery_charge_state.charge_percent); + if (s_last_tte == 0U) { + prompt_send_response_fmt(buffer, 32, "tte: N/A"); + } else { + prompt_send_response_fmt(buffer, 32, "tte: %" PRIu32 "s", s_last_tte); + } + if (s_last_ttf == 0U) { + prompt_send_response_fmt(buffer, 32, "ttf: N/A"); + } else { + prompt_send_response_fmt(buffer, 32, "ttf: %" PRIu32 "s", s_last_ttf); + } + prompt_send_response_fmt(buffer, 32, "plugged: %s", + s_last_battery_charge_state.is_plugged ? "YES" : "NO"); + prompt_send_response_fmt(buffer, 32, "charging: %s", + s_last_battery_charge_state.is_charging ? "YES" : "NO"); +} + +///////////////// +// Analytics + +// Note that this is run on a different thread than battery_state! +void pbl_analytics_external_collect_battery(void) { + int32_t battery_mv = s_last_voltage_mv; + uint32_t battery_soc_cpct = s_last_soc_cpct; + int32_t d_mv; + uint32_t d_soc_cpct; + + d_mv = battery_mv - s_analytics_last_voltage_mv; + PBL_ANALYTICS_SET_UNSIGNED(battery_voltage, battery_mv); + PBL_ANALYTICS_SET_SIGNED(battery_voltage_delta, d_mv); + s_analytics_last_voltage_mv = battery_mv; + + d_soc_cpct = MAX((int32_t)s_analytics_last_cpct - (int32_t)battery_soc_cpct, 0); + PBL_ANALYTICS_SET_UNSIGNED(battery_soc_pct, battery_soc_cpct); + PBL_ANALYTICS_SET_UNSIGNED(battery_soc_pct_drop, d_soc_cpct); + s_analytics_last_cpct = battery_soc_cpct; + + PBL_ANALYTICS_SET_UNSIGNED(battery_tte_s, s_last_tte); +} + +static void prv_set_forced_charge_state(bool is_charging) { + battery_force_charge_enable(is_charging); + + // Trigger an immediate update to the state machine: may trigger an event + battery_state_force_update(); +} + +void command_battery_charge_option(const char *option) { + if (!strcmp("disable", option)) { + prv_set_forced_charge_state(false); + } else if (!strcmp("enable", option)) { + prv_set_forced_charge_state(true); + } +} diff --git a/src/fw/services/battery/voltage/battery_curve.c b/src/fw/services/battery/voltage/battery_curve.c new file mode 100644 index 0000000000..366d68e092 --- /dev/null +++ b/src/fw/services/battery/voltage/battery_curve.c @@ -0,0 +1,195 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/battery/battery_curve.h" + +#include "board/board.h" +#include "system/logging.h" +#include "util/math.h" +#include "util/ratio.h" +#include "util/size.h" + +typedef struct VoltagePoint { + uint8_t percent; + uint16_t voltage; +} VoltagePoint; + +// TODO: Move these curves somewhere else. Related: PBL-21049 + +// TODO(ASTERIX,OBELIX): Needs customization for Asterix/Obelix +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_QEMU_EMERY) || defined(CONFIG_BOARD_QEMU_FLINT) || defined(CONFIG_BOARD_QEMU_GABBRO) +// When the voltage drops below these (mV), the watch will start heading for standby (after delay) +#define BATTERY_CRITICAL_VOLTAGE_CHARGING 3550 +#define BATTERY_CRITICAL_VOLTAGE_DISCHARGING 3300 +// Battery Tables for Silk +static VoltagePoint discharge_curve[] = { + {0, BATTERY_CRITICAL_VOLTAGE_DISCHARGING}, + {2, 3490}, + {5, 3615}, + {10, 3655}, + {20, 3700}, + {30, 3735}, + {40, 3760}, + {50, 3800}, + {60, 3855}, + {70, 3935}, + {80, 4025}, + {90, 4120}, + {100, 4230} +}; + +static const VoltagePoint charge_curve[] = { + {0, BATTERY_CRITICAL_VOLTAGE_CHARGING}, + {2, 3570}, + {5, 3600}, + {10, 3645}, + {20, 3730}, + {30, 3800}, + {40, 3860}, + {50, 3915}, + {60, 3970}, + {70, 4030}, + {80, 4095}, + {90, 4175}, + {100, 4260} +}; + +#else +#error "No battery curve for platform!" +#endif + +static const uint8_t NUM_DISCHARGE_POINTS = ARRAY_LENGTH(discharge_curve); +static const uint8_t NUM_CHARGE_POINTS = ARRAY_LENGTH(charge_curve); + +static int s_battery_compensation_values[BATTERY_CURVE_COMPENSATE_COUNT]; + +// The 100% discharge voltage at boot, captured lazily before the first runtime +// adjustment so tests can restore the curve. 0 means "not yet captured". +static uint16_t s_discharge_full_voltage_default; + +// Shifts the 100% reference on the discharge curve, as long as it +// doesn't drop below the next highest point. +void battery_curve_set_full_voltage(uint16_t voltage) { + if (s_discharge_full_voltage_default == 0) { + s_discharge_full_voltage_default = discharge_curve[NUM_DISCHARGE_POINTS-1].voltage; + } + voltage = MAX(voltage, discharge_curve[NUM_DISCHARGE_POINTS-2].voltage + 1); + discharge_curve[NUM_DISCHARGE_POINTS-1].voltage = voltage; +} + +#if UNITTEST +// Restores curve state mutated by battery_curve_set_full_voltage() so tests +// run in isolation. Not built into production firmware (tests only). +void battery_curve_reset_for_tests(void) { + if (s_discharge_full_voltage_default != 0) { + discharge_curve[NUM_DISCHARGE_POINTS-1].voltage = s_discharge_full_voltage_default; + } +} +#endif + +static uint32_t prv_lookup_percent_by_voltage( + int battery_mv, bool is_charging, uint32_t scaling_factor) { + VoltagePoint const * const battery_curve = (is_charging) ? charge_curve : discharge_curve; + uint8_t const num_curve_points = (is_charging) ? NUM_CHARGE_POINTS : NUM_DISCHARGE_POINTS; + + // Constrain the voltage between the min and max points of the curve + if (battery_mv <= battery_curve[0].voltage) { + return battery_curve[0].percent * scaling_factor; + } else if (battery_mv >= battery_curve[num_curve_points-1].voltage) { + return battery_curve[num_curve_points-1].percent * scaling_factor; + } + + // search through the curves for the next charge level... + uint32_t charge_index; + for (charge_index = num_curve_points-2; + (charge_index > 0) && (battery_mv < battery_curve[charge_index].voltage); + --charge_index) { + } + + // linearly interpolate between battery_curve[charge_index] and battery_curve[charge_index+1] + uint32_t delta_mv = (battery_mv - battery_curve[charge_index].voltage); + uint32_t delta_next_mv = (battery_curve[charge_index + 1].voltage - + battery_curve[charge_index].voltage); + uint32_t delta_next_percent = (battery_curve[charge_index + 1].percent - + battery_curve[charge_index].percent); + + uint32_t start_percent = battery_curve[charge_index].percent * scaling_factor; + delta_next_percent *= scaling_factor; + + uint32_t charge_percent = + (((uint64_t)delta_next_percent * delta_mv) / delta_next_mv) + start_percent; + return charge_percent; +} + +static uint32_t prv_sample_scaled_charge_percent( + uint32_t battery_mv, bool is_charging, uint32_t scaling_factor) { + int compensate = 0; // We compensate 5% during rounding, so don't do here + + for (int i = 0; i < BATTERY_CURVE_COMPENSATE_COUNT; ++i) { + compensate += s_battery_compensation_values[i]; + } + + return prv_lookup_percent_by_voltage(battery_mv + compensate, is_charging, scaling_factor); +} + +uint32_t battery_curve_sample_ratio32_charge_percent(uint32_t battery_mv, bool is_charging) { + const uint32_t scaling_factor = ratio32_from_percent(100) / 100 + 1; + return prv_sample_scaled_charge_percent(battery_mv, is_charging, scaling_factor); +} + +void battery_curve_set_compensation(BatteryCurveVoltageCompensationKey key, int mv) { + s_battery_compensation_values[key] = mv; +} + +// This is used by unit tests and QEMU +uint32_t battery_curve_lookup_voltage_by_percent(uint32_t percent, bool is_charging) { + VoltagePoint const * const battery_curve = (is_charging) ? charge_curve : discharge_curve; + uint32_t const num_curve_points = (is_charging) ? NUM_CHARGE_POINTS : NUM_DISCHARGE_POINTS; + + // Clip if above curve upper bound + if (percent > battery_curve[num_curve_points-1].percent) { + return battery_curve[num_curve_points-1].voltage; + } + + // search through the curves for the next charge level... + uint32_t charge_index; + for (charge_index = num_curve_points-2; + (charge_index > 0) && (percent < battery_curve[charge_index].percent); + --charge_index) { + } + + // linearly interpolate between battery_curve[charge_index] and battery_curve[charge_index+1] + uint32_t delta_next_mv = (battery_curve[charge_index+1].voltage - + battery_curve[charge_index].voltage); + uint32_t delta_next_percent = (battery_curve[charge_index+1].percent - + battery_curve[charge_index].percent); + uint32_t battery_mv = battery_curve[charge_index].voltage + + ((percent - battery_curve[charge_index].percent) * delta_next_mv) / + delta_next_percent; + return battery_mv; +} + +//! This call is used internally with PreciseBatteryChargeState +//! So must remove low_power_threshold to get correct remaining hours +//! before low_power_mode is triggered +uint32_t battery_curve_get_hours_remaining(uint32_t percent_remaining) { + if (percent_remaining <= BOARD_CONFIG_POWER.low_power_threshold) { + return 0; + } + percent_remaining -= BOARD_CONFIG_POWER.low_power_threshold; + return ((BOARD_CONFIG_POWER.battery_capacity_hours * percent_remaining) / 100); +} + +//! This call is used internally with PreciseBatteryChargeState +//! So must add low_power_threshold to get percentage in terms of +//! PreciseBatteryChargeState (which includes low_power_mode) +uint32_t battery_curve_get_percent_remaining(uint32_t hours) { + return ((hours * 100) / BOARD_CONFIG_POWER.battery_capacity_hours) + + BOARD_CONFIG_POWER.low_power_threshold; +} + +// for unit tests and analytics +int32_t battery_curve_lookup_percent_with_scaling_factor( + int battery_mv, bool is_charging, uint32_t scaling_factor) { + return (prv_lookup_percent_by_voltage(battery_mv, is_charging, scaling_factor)); +} diff --git a/src/fw/services/battery/voltage/battery_state.c b/src/fw/services/battery/voltage/battery_state.c new file mode 100644 index 0000000000..2f6ec63742 --- /dev/null +++ b/src/fw/services/battery/voltage/battery_state.c @@ -0,0 +1,379 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/battery/battery_state.h" + +#ifdef CONFIG_QEMU +#include "drivers/qemu/qemu_battery.h" +#endif + +#include "board/board.h" +#include "debug/power_tracking.h" +#include "drivers/battery.h" +#include "kernel/events.h" +#include "kernel/util/stop.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/battery/battery_curve.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/math.h" +#include "util/ratio.h" + +PBL_LOG_MODULE_DECLARE(service_battery, CONFIG_SERVICE_BATTERY_LOG_LEVEL); + +#ifdef DEBUG_BATTERY_STATE +#define BATTERY_SAMPLE_RATE_MS 1000 +#else +#define BATTERY_SAMPLE_RATE_MS (60 * 1000) +#endif + +typedef void (*EntryFunc)(void); + +typedef enum { + ConnectionStateInvalid, + ConnectionStateChargingPlugged, + ConnectionStateDischargingPlugged, + ConnectionStateDischargingUnplugged +} ConnectionStateID; + +typedef struct ConnectionState { + EntryFunc enter; +} ConnectionState; + +static void prv_update_plugged_change(void); +static void prv_update_done_charging(void); + +static const ConnectionState s_transitions[] = { + [ConnectionStateChargingPlugged] = { .enter = prv_update_plugged_change }, + [ConnectionStateDischargingPlugged] = { .enter = prv_update_done_charging }, + [ConnectionStateDischargingUnplugged] = { .enter = prv_update_plugged_change } +}; + +typedef struct BatteryState { + uint64_t init_time; + uint32_t percent; + uint16_t voltage; + uint8_t skip_count; + ConnectionStateID connection; +} BatteryState; + +static BatteryState s_last_battery_state; +static TimerID s_periodic_timer_id = TIMER_INVALID_ID; +static int s_analytics_previous_mv = 0; +static uint32_t s_analytics_last_cpct = 0; + +static void prv_schedule_update(uint32_t delay, bool force_update); +PreciseBatteryChargeState prv_get_precise_charge_state(const BatteryState *state); + +static void prv_transition(BatteryState *state, ConnectionStateID next_state) { + state->connection = next_state; + s_transitions[state->connection].enter(); +} + +static void prv_update_plugged_change(void) { + // If the connection state changed or we finished charging, reset the filter since we're + // probably switching to a new curve. + battery_state_reset_filter(); + + bool is_charging = battery_charge_controller_thinks_we_are_charging(); + + if (is_charging) { + PBL_ANALYTICS_TIMER_START(battery_charge_time_ms); + PBL_ANALYTICS_TIMER_STOP(battery_discharge_duration_ms); + } else { + bool is_plugged = battery_is_usb_connected(); + + PBL_ANALYTICS_TIMER_STOP(battery_charge_time_ms); + + if (!is_plugged) { + PBL_ANALYTICS_TIMER_START(battery_discharge_duration_ms); + } else { + PBL_ANALYTICS_TIMER_STOP(battery_discharge_duration_ms); + } + } +} + +static void prv_update_done_charging(void) { + prv_update_plugged_change(); + + // Amount in mV to drop the "Full" voltage by to briefly stay at 100% once unplugged + const uint16_t BATTERY_FULL_FUDGE_AMOUNT = 10; + PBL_LOG_DBG("Done charging - Updating curve"); + battery_curve_set_full_voltage(s_last_battery_state.voltage - BATTERY_FULL_FUDGE_AMOUNT); +} + +static void battery_state_put_change_event(PreciseBatteryChargeState state) { + PebbleEvent e = { + .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, + .battery_state = { + .new_state = state, + }, + }; + event_put(&e); +} + +void battery_state_reset_filter(void) { + s_last_battery_state.voltage = battery_get_millivolts(); + // Reset the stablization timer in case we encountered a current spike during the reset + s_last_battery_state.init_time = rtc_get_ticks(); +} + +static uint32_t prv_filter_voltage(uint32_t avg_mv, uint32_t battery_mv) { + // Basic low-pass filter - See PBL-23637 + const uint8_t VOLTAGE_FILTER_BETA = 2; + uint32_t avg = (avg_mv << VOLTAGE_FILTER_BETA); + avg -= avg_mv; + avg += battery_mv; + return avg >> VOLTAGE_FILTER_BETA; +} + +static bool prv_is_stable(const BatteryState *state) { + // After a reboot, we typically source a lot of current which can drastically impact + // our mV readings due to the internal resistance of the battery. We use the + // system_likely_stabilized flag as an indicator of how trustworthy our readings are + const uint64_t STABLE_TICKS = 3 * 60 * RTC_TICKS_HZ; + uint64_t elapsed_ticks = rtc_get_ticks() - state->init_time; + return elapsed_ticks > STABLE_TICKS; +} + +static ConnectionStateID prv_get_connection_state(void) { + const bool charging = battery_charge_controller_thinks_we_are_charging(); + const bool plugged_in = battery_is_usb_connected(); + + if (plugged_in) { + if (charging) { + return ConnectionStateChargingPlugged; + } else { + return ConnectionStateDischargingPlugged; + } + } else { + if (charging) { + // Since we can't be charging and disconnected, + // just log a warning and pretend we aren't charging. + PBL_LOG_WRN("PMIC reported charging while unplugged - ignoring"); + } + return ConnectionStateDischargingUnplugged; + } +} + +static void prv_update_state(void *force_update) { + const uint8_t MAX_SAMPLE_SKIPS = 5; + bool forced = (bool)force_update; + // Large current draws will cause the voltage supplied by the battery to + // droop. We try to only sample the battery when there is minimal + // activity. We look to see if stop mode is allowed because this is a good + // indicator that no peripherals are in use (i.e vibe, backlight, etc) + if ((s_last_battery_state.skip_count < MAX_SAMPLE_SKIPS) && + !forced && !stop_mode_is_allowed()) { + s_last_battery_state.skip_count++; + return; + } + + if (s_last_battery_state.skip_count == MAX_SAMPLE_SKIPS) { + } + s_last_battery_state.skip_count = 0; + + // Driver communication + + bool state_changed = false; + ConnectionStateID next_state = prv_get_connection_state(); + if (s_last_battery_state.connection != next_state) { + // Do not allow DischargingPlugged -> ChargingPlugged state + if ((s_last_battery_state.connection != ConnectionStateDischargingPlugged) || + (next_state != ConnectionStateChargingPlugged)) { + prv_transition(&s_last_battery_state, next_state); + state_changed = true; + } + } + + s_last_battery_state.voltage = prv_filter_voltage(s_last_battery_state.voltage, + battery_get_millivolts()); + bool charging = (s_last_battery_state.connection == ConnectionStateChargingPlugged); + + // Update Percent & Filtering + + const uint32_t ALWAYS_UPDATE_THRESHOLD = ratio32_from_percent(10); + bool likely_stable = prv_is_stable(&s_last_battery_state); + + uint32_t new_charge_percent = + battery_curve_sample_ratio32_charge_percent(s_last_battery_state.voltage, charging); + // Allow updates iff: + // - We are charging + // - We are discharging and: + // - The readings have stabilized and the battery percent did not go up + // - The readings have not yet stablized + // TL;DR: Allow updates unless we're stable and discharging but the % went up. + if (!charging && likely_stable && + new_charge_percent > s_last_battery_state.percent) { + // It's okay to return early since any connection/plugged changes will reset the filter, + // so we won't catch those. + return; + } + + s_last_battery_state.percent = new_charge_percent; + + PBL_LOG_DBG("mV Raw: %"PRIu16" Ratio: %"PRIu32" Percent: %"PRIu32, + s_last_battery_state.voltage, s_last_battery_state.percent, + ratio32_to_percent(s_last_battery_state.percent)); + + PWR_TRACK_BATT(charging ? "CHARGING" : "DISCHARGING", s_last_battery_state.voltage); + + if (forced || likely_stable || s_last_battery_state.percent <= ALWAYS_UPDATE_THRESHOLD || + charging || state_changed) { + battery_state_put_change_event(prv_get_precise_charge_state(&s_last_battery_state)); + } +} + +static void prv_update_callback(void *data) { + // Running the battery monitor on the timer task is not a good idea because + // we could be sampling right in the middle of a flash erase, etc. Therefore, + // dispatch to a lower priority task + system_task_add_callback(prv_update_state, data); + + // Reschedule ourselves again so we create a loop + prv_schedule_update(BATTERY_SAMPLE_RATE_MS, false); +} + +static void prv_schedule_update(uint32_t delay, bool force_update) { + bool success = new_timer_start(s_periodic_timer_id, delay, prv_update_callback, + (void *)force_update, 0 /*flags*/); + PBL_ASSERTN(success); +} + +void battery_state_force_update(void) { + // Fire off our periodic timer. Note that we rely on the callback to reschedule the timer + // for 1 minute intervals rather than create it as a repeating timer. This is because + // we occasionally want the callback to get triggered immediately + // (in response to the charging cable being plugged in). In these instances, we reschedule it + // from the main task. + prv_schedule_update(0, true); +} + +void battery_state_init(void) { + s_periodic_timer_id = new_timer_create(); + + s_last_battery_state = (BatteryState) { .connection = ConnectionStateDischargingUnplugged }; + battery_state_reset_filter(); + battery_state_force_update(); + + s_analytics_previous_mv = s_last_battery_state.voltage; + s_analytics_last_cpct = (s_last_battery_state.percent * 10000U) / RATIO32_MAX; +} + +void battery_state_handle_connection_event(bool is_connected) { + static const uint32_t RECONNECTION_DELAY_MS = 1000; + + PBL_LOG_VERBOSE("USB Connected:%d", is_connected); + + // Trigger a reset update to the state machine. Delay the update to allow the battery voltage to + // settle and to debounce reconnection events + prv_schedule_update(RECONNECTION_DELAY_MS, true); +} + +PreciseBatteryChargeState prv_get_precise_charge_state(const BatteryState *state) { + PreciseBatteryChargeState event_state = { + .charge_percent = state->percent, + .pct = ratio32_to_percent(state->percent), + .is_charging = (s_last_battery_state.connection == ConnectionStateChargingPlugged), + .is_plugged = (s_last_battery_state.connection != ConnectionStateDischargingUnplugged) + }; + return event_state; +} + +DEFINE_SYSCALL(BatteryChargeState, sys_battery_get_charge_state, void) { + return battery_get_charge_state(); +} + +BatteryChargeState battery_get_charge_state(void) { + bool is_plugged = (s_last_battery_state.connection != ConnectionStateDischargingUnplugged); + +#ifdef CONFIG_QEMU + // Read the exact percent the host set via `pebble emu-battery --percent N`, + // skipping the lossy voltage-curve roundtrip and the low-power-reserve remap + // so the watch displays exactly what was requested. + int32_t percent = MIN((int32_t)qemu_battery_get_percent(), 100); + int32_t percent_normalized = percent; + uint8_t charge_percent = (uint8_t)percent; +#else + int32_t percent = ratio32_to_percent(s_last_battery_state.percent); + + // subtract low power reserve, so developer will see 0% when we're approaching low power mode + int32_t percent_normalized = MAX((percent - BOARD_CONFIG_POWER.low_power_threshold + + percent / (100 / BOARD_CONFIG_POWER.low_power_threshold)), 0); + + // massage rounding factor so that between 100% to 50% charge the SOC reported is biased to a + // higher charge percent bin. + int32_t rounding_factor = 5 + MAX(((percent - 50) / 10), 0); + uint8_t charge_percent = MIN(10 * ((percent_normalized + rounding_factor) / 10), 100); +#endif + BatteryChargeState state = { + .charge_percent = charge_percent, + .is_charging = is_plugged && percent_normalized < 100, + .is_plugged = is_plugged, + }; + return state; +} + +// For unit tests +TimerID battery_state_get_periodic_timer_id(void) { + return s_periodic_timer_id; +} + +uint16_t battery_state_get_voltage(void) { + return s_last_battery_state.voltage; +} + +int32_t battery_state_get_temp(void) { + return 0; +} + +#include "console/prompt.h" +void command_print_battery_status(void) { + char buffer[32]; + PreciseBatteryChargeState state = prv_get_precise_charge_state(&s_last_battery_state); + prompt_send_response_fmt(buffer, 32, "%"PRIu16" mV", s_last_battery_state.voltage); + prompt_send_response_fmt(buffer, 32, + "batt_percent: %"PRIu32"%%", ratio32_to_percent(state.charge_percent)); + prompt_send_response_fmt(buffer, 32, "plugged: %s", state.is_plugged ? "YES" : "NO"); + prompt_send_response_fmt(buffer, 32, "charging: %s", state.is_charging ? "YES" : "NO"); +} + +///////////////// +// Analytics + +// Note that this is run on a different thread than battery_state! +void pbl_analytics_external_collect_battery(void) { + int32_t battery_mv = s_last_battery_state.voltage; + uint32_t battery_soc_cpct = (s_last_battery_state.percent * 10000U) / RATIO32_MAX; + int32_t d_mv; + uint32_t d_soc_cpct; + + d_mv = battery_mv - s_analytics_previous_mv; + PBL_ANALYTICS_SET_UNSIGNED(battery_voltage, battery_mv); + PBL_ANALYTICS_SET_SIGNED(battery_voltage_delta, d_mv); + s_analytics_previous_mv = battery_mv; + + d_soc_cpct = MAX((int32_t)s_analytics_last_cpct - (int32_t)battery_soc_cpct, 0); + PBL_ANALYTICS_SET_UNSIGNED(battery_soc_pct, battery_soc_cpct); + PBL_ANALYTICS_SET_UNSIGNED(battery_soc_pct_drop, d_soc_cpct); + s_analytics_last_cpct = battery_soc_cpct; +} + +static void prv_set_forced_charge_state(bool is_charging) { + battery_force_charge_enable(is_charging); + + // Trigger an immediate update to the state machine: may trigger an event + battery_state_force_update(); +} + +void command_battery_charge_option(const char* option) { + if (!strcmp("disable", option)) { + prv_set_forced_charge_state(false); + } else if (!strcmp("enable", option)) { + prv_set_forced_charge_state(true); + } +} diff --git a/src/fw/services/battery/wscript_build b/src/fw/services/battery/wscript_build new file mode 100644 index 0000000000..220ec43cac --- /dev/null +++ b/src/fw/services/battery/wscript_build @@ -0,0 +1,25 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +use = ['fw_includes', 'services_analytics'] +sources = ['battery_monitor.c'] + +if bld.env.CONFIG_NRF_FUEL_GAUGE: + use.append('nrf_fuel_gauge') + sources.extend([ + 'nrf_fuel_gauge/battery_curve.c', + 'nrf_fuel_gauge/battery_state.c', + ]) +else: + sources.extend([ + 'voltage/battery_curve.c', + 'voltage/battery_state.c', + ]) + +bld.stlib( + source=sources, + target='services_battery', + use=use +) + +bld.env.SERVICES.append('services_battery') diff --git a/src/fw/services/blob_db/Kconfig b/src/fw/services/blob_db/Kconfig new file mode 100644 index 0000000000..3418e3616a --- /dev/null +++ b/src/fw/services/blob_db/Kconfig @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_BLOB_DB + bool "Blob DB" + help + BlobDB (persistent key/value stores used for timeline, notifications, + apps, contacts, ...). + +if SERVICE_BLOB_DB + +module = SERVICE_BLOB_DB +module-str = Blob DB +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/blob_db/api.c b/src/fw/services/blob_db/api.c new file mode 100644 index 0000000000..e3fefa4ca9 --- /dev/null +++ b/src/fw/services/blob_db/api.c @@ -0,0 +1,346 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/api.h" + +#include +#include + +#include "pbl/services/blob_db/app_db.h" +#include "pbl/services/blob_db/app_glance_db.h" +#include "pbl/services/blob_db/contacts_db.h" +#include "pbl/services/blob_db/health_db.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/blob_db/notif_db.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/prefs_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/blob_db/settings_blob_db.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" +#include "pbl/services/blob_db/weather_db.h" + +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + +typedef struct { + BlobDBInitImpl init; + BlobDBInsertImpl insert; + BlobDBGetLenImpl get_len; + BlobDBReadImpl read; + BlobDBDeleteImpl del; + BlobDBFlushImpl flush; + BlobDBIsDirtyImpl is_dirty; + BlobDBGetDirtyListImpl get_dirty_list; + BlobDBMarkSyncedImpl mark_synced; + BlobDBCompactImpl compact; + const char *name; + bool disabled; +} BlobDB; + +static const BlobDB s_blob_dbs[NumBlobDBs] = { + [BlobDBIdPins] = { + .init = pin_db_init, + .insert = pin_db_insert, + .get_len = pin_db_get_len, + .read = pin_db_read, + .del = pin_db_delete, + .flush = pin_db_flush, + .is_dirty = pin_db_is_dirty, + .get_dirty_list = pin_db_get_dirty_list, + .mark_synced = pin_db_mark_synced, + .compact = pin_db_compact, + .name = "pin_db", + }, + [BlobDBIdApps] = { + .init = app_db_init, + .insert = app_db_insert, + .get_len = app_db_get_len, + .read = app_db_read, + .del = app_db_delete, + .flush = app_db_flush, + .compact = app_db_compact, + .name = "app_db", + }, + [BlobDBIdReminders] = { + .init = reminder_db_init, + .insert = reminder_db_insert, + .get_len = reminder_db_get_len, + .read = reminder_db_read, + .del = reminder_db_delete, + .flush = reminder_db_flush, + .is_dirty = reminder_db_is_dirty, + .get_dirty_list = reminder_db_get_dirty_list, + .mark_synced = reminder_db_mark_synced, + .compact = reminder_db_compact, + .name = "reminder_db", + }, + [BlobDBIdNotifs] = { + .init = notif_db_init, + .insert = notif_db_insert, + .get_len = notif_db_get_len, + .read = notif_db_read, + .del = notif_db_delete, + .flush = notif_db_flush, + .name = "notif_db", + }, + [BlobDBIdWeather] = { + .init = weather_db_init, + .insert = weather_db_insert, + .get_len = weather_db_get_len, + .read = weather_db_read, + .del = weather_db_delete, + .flush = weather_db_flush, + .compact = weather_db_compact, + .name = "weather_db", + }, + [BlobDBIdiOSNotifPref] = { + .init = ios_notif_pref_db_init, + .insert = ios_notif_pref_db_insert, + .get_len = ios_notif_pref_db_get_len, + .read = ios_notif_pref_db_read, + .del = ios_notif_pref_db_delete, + .flush = ios_notif_pref_db_flush, + .is_dirty = ios_notif_pref_db_is_dirty, + .get_dirty_list = ios_notif_pref_db_get_dirty_list, + .mark_synced = ios_notif_pref_db_mark_synced, + .compact = ios_notif_pref_db_compact, + .name = "ios_notif_pref_db", + }, + [BlobDBIdPrefs] = { + .init = prefs_db_init, + .insert = prefs_db_insert, + .get_len = prefs_db_get_len, + .read = prefs_db_read, + .del = prefs_db_delete, + .flush = prefs_db_flush, + .name = "prefs_db", + }, + [BlobDBIdContacts] = { + .init = contacts_db_init, + .insert = contacts_db_insert, + .get_len = contacts_db_get_len, + .read = contacts_db_read, + .del = contacts_db_delete, + .flush = contacts_db_flush, + .compact = contacts_db_compact, + .name = "contacts_db", + }, + [BlobDBIdWatchAppPrefs] = { + .init = watch_app_prefs_db_init, + .insert = watch_app_prefs_db_insert, + .get_len = watch_app_prefs_db_get_len, + .read = watch_app_prefs_db_read, + .del = watch_app_prefs_db_delete, + .flush = watch_app_prefs_db_flush, + .compact = watch_app_prefs_db_compact, + .name = "watch_app_prefs_db", + }, + [BlobDBIdHealth] = { + .init = health_db_init, + .insert = health_db_insert, + .get_len = health_db_get_len, + .read = health_db_read, + .del = health_db_delete, + .flush = health_db_flush, + .compact = health_db_compact, + .name = "health_db", + }, + [BlobDBIdAppGlance] = { + .init = app_glance_db_init, + .insert = app_glance_db_insert, + .get_len = app_glance_db_get_len, + .read = app_glance_db_read, + .del = app_glance_db_delete, + .flush = app_glance_db_flush, + .compact = app_glance_db_compact, + .name = "app_glance_db", + }, + [BlobDBIdSettings] = { + .init = settings_blob_db_init, + .insert = settings_blob_db_insert, + .get_len = settings_blob_db_get_len, + .read = settings_blob_db_read, + .del = settings_blob_db_delete, + .flush = settings_blob_db_flush, + .is_dirty = settings_blob_db_is_dirty, + .get_dirty_list = settings_blob_db_get_dirty_list, + .mark_synced = settings_blob_db_mark_synced, + .name = "settings_blob_db", + }, +}; + +static bool prv_db_valid(BlobDBId db_id) { + return (db_id < NumBlobDBs) && (!s_blob_dbs[db_id].disabled); + +} + +void blob_db_event_put(BlobDBEventType type, BlobDBId db_id, const uint8_t *key, int key_len) { + // copy key for event + uint8_t *key_bytes = NULL; + if (key_len > 0) { + key_bytes = kernel_malloc(key_len); + memcpy(key_bytes, key, key_len); + } + + PebbleEvent e = { + .type = PEBBLE_BLOBDB_EVENT, + .blob_db = { + .db_id = db_id, + .type = type, + .key = key_bytes, + .key_len = (uint8_t)key_len, + } + }; + event_put(&e); +} + +void blob_db_init_dbs(void) { + const BlobDB *db = s_blob_dbs; + for (int i = 0; i < NumBlobDBs; ++i, ++db) { + if (db->init) { + db->init(); + } + } +} + +void blob_db_compact_growable_dbs(void) { + PBL_LOG_INFO("blob_db_compact_growable_dbs: start"); + const BlobDB *db = s_blob_dbs; + for (int i = 0; i < NumBlobDBs; ++i, ++db) { + if (!db->compact) { + continue; + } + const status_t rv = db->compact(); + PBL_LOG_INFO("blob_db_compact_growable_dbs: %s -> %"PRIi32, + db->name ? db->name : "?", rv); + } + PBL_LOG_INFO("blob_db_compact_growable_dbs: done"); +} + +void blob_db_get_dirty_dbs(uint8_t *ids, uint8_t *num_ids) { + const BlobDB *db = s_blob_dbs; + *num_ids = 0; + for (uint8_t i = 0; i < NumBlobDBs; ++i, ++db) { + bool is_dirty = false; + if (db->is_dirty && (db->is_dirty(&is_dirty) == S_SUCCESS) && is_dirty) { + ids[*num_ids] = i; + *num_ids += 1; + } + } +} + +status_t blob_db_insert(BlobDBId db_id, + const uint8_t *key, int key_len, const uint8_t *val, int val_len) { + if (!prv_db_valid(db_id)) { + return E_RANGE; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->insert) { + status_t rv = db->insert(key, key_len, val, val_len); + if (rv == S_SUCCESS) { + blob_db_event_put(BlobDBEventTypeInsert, db_id, key, key_len); + } + + return rv; + } + + return E_INVALID_OPERATION; +} + +int blob_db_get_len(BlobDBId db_id, + const uint8_t *key, int key_len) { + if (!prv_db_valid(db_id)) { + return E_RANGE; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->get_len) { + return db->get_len(key, key_len); + } + + return E_INVALID_OPERATION; +} + +status_t blob_db_read(BlobDBId db_id, + const uint8_t *key, int key_len, uint8_t *val_out, int val_len) { + if (!prv_db_valid(db_id)) { + return E_RANGE; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->read) { + return db->read(key, key_len, val_out, val_len); + } + + return E_INVALID_OPERATION; +} + +status_t blob_db_delete(BlobDBId db_id, + const uint8_t *key, int key_len) { + if (!prv_db_valid(db_id)) { + return E_RANGE; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->del) { + status_t rv = db->del(key, key_len); + if (rv == S_SUCCESS) { + blob_db_event_put(BlobDBEventTypeDelete, db_id, key, key_len); + } + return rv; + } + + return E_INVALID_OPERATION; +} + +status_t blob_db_flush(BlobDBId db_id) { + if (!prv_db_valid(db_id)) { + return E_RANGE; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->flush) { + status_t rv = db->flush(); + if (rv == S_SUCCESS) { + PBL_LOG_INFO("Flushing BlobDB with Id %d", db_id); + blob_db_event_put(BlobDBEventTypeFlush, db_id, NULL, 0); + } + return rv; + } + + return E_INVALID_OPERATION; +} + +BlobDBDirtyItem *blob_db_get_dirty_list(BlobDBId db_id) { + if (!prv_db_valid(db_id)) { + return NULL; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->get_dirty_list) { + return db->get_dirty_list(); + } + + return NULL; +} + +status_t blob_db_mark_synced(BlobDBId db_id, uint8_t *key, int key_len) { + if (!prv_db_valid(db_id)) { + return E_RANGE; + } + + const BlobDB *db = &s_blob_dbs[db_id]; + if (db->mark_synced) { + status_t rv = db->mark_synced(key, key_len); + // TODO event? + return rv; + } + + return E_INVALID_OPERATION; +} diff --git a/src/fw/services/normal/blob_db/app_db.c b/src/fw/services/blob_db/app_db.c similarity index 89% rename from src/fw/services/normal/blob_db/app_db.c rename to src/fw/services/blob_db/app_db.c index 148ac4ab7e..607315c844 100644 --- a/src/fw/services/normal/blob_db/app_db.c +++ b/src/fw/services/blob_db/app_db.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_db.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/app_db.h" #include "util/uuid.h" #include "kernel/pbl_malloc.h" #include "process_management/app_install_manager_private.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/app_fetch_endpoint.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/app_fetch_endpoint.h" #include "os/mutex.h" #include "system/logging.h" #include "system/passert.h" @@ -29,6 +16,8 @@ #include "util/math.h" #include "util/units.h" +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define SETTINGS_FILE_NAME "appdb" // Holds about ~150 app metadata blobs #define SETTINGS_FILE_SIZE KiBYTES(20) @@ -53,9 +42,10 @@ struct AppDBInitData { static status_t prv_lock_mutex_and_open_file(void) { mutex_lock(s_app_db.mutex); - status_t rv = settings_file_open(&s_app_db.settings_file, - SETTINGS_FILE_NAME, - SETTINGS_FILE_SIZE); + status_t rv = settings_file_open_growable(&s_app_db.settings_file, + SETTINGS_FILE_NAME, + SETTINGS_FILE_SIZE, + KiBYTES(4)); if (rv != S_SUCCESS) { mutex_unlock(s_app_db.mutex); } @@ -263,7 +253,7 @@ void app_db_init(void) { s_next_unique_flash_app_id = (data.max_id + 1); } - PBL_LOG(LOG_LEVEL_INFO, "Found %"PRIu32" apps. Next ID: %"PRIu32" ", data.num_apps, + PBL_LOG_INFO("Found %"PRIu32" apps. Next ID: %"PRIu32" ", data.num_apps, s_next_unique_flash_app_id); prv_close_file_and_unlock_mutex(); @@ -289,7 +279,7 @@ status_t app_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int new_install = true; app_id = s_next_unique_flash_app_id++; } else if (app_fetch_in_progress()) { - PBL_LOG(LOG_LEVEL_WARNING, "Got an insert for an app that is currently being fetched, %"PRId32, + PBL_LOG_WRN("Got an insert for an app that is currently being fetched, %"PRId32, app_id); rv = prv_cancel_app_fetch(app_id); } @@ -367,7 +357,7 @@ status_t app_db_delete(const uint8_t *key, int key_len) { if (app_id == INSTALL_ID_INVALID) { rv = E_DOES_NOT_EXIST; } else if (app_fetch_in_progress()) { - PBL_LOG(LOG_LEVEL_WARNING, "Tried to delete an app that is currently being fetched, %"PRId32, + PBL_LOG_WRN("Tried to delete an app that is currently being fetched, %"PRId32, app_id); rv = prv_cancel_app_fetch(app_id); } @@ -390,7 +380,7 @@ status_t app_db_delete(const uint8_t *key, int key_len) { } status_t app_db_flush(void) { - PBL_LOG(LOG_LEVEL_WARNING, "AppDB Flush initiated"); + PBL_LOG_WRN("AppDB Flush initiated"); if (app_fetch_in_progress()) { // cancels any app fetch @@ -410,10 +400,20 @@ status_t app_db_flush(void) { pfs_remove(SETTINGS_FILE_NAME); mutex_unlock(s_app_db.mutex); - PBL_LOG(LOG_LEVEL_WARNING, "AppDB Flush finished"); + PBL_LOG_WRN("AppDB Flush finished"); return S_SUCCESS; } +status_t app_db_compact(void) { + status_t rv = prv_lock_mutex_and_open_file(); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&s_app_db.settings_file); + prv_close_file_and_unlock_mutex(); + return rv; +} + ////////////////////// // Test functions ////////////////////// diff --git a/src/fw/services/normal/blob_db/app_glance_db.c b/src/fw/services/blob_db/app_glance_db.c similarity index 92% rename from src/fw/services/normal/blob_db/app_glance_db.c rename to src/fw/services/blob_db/app_glance_db.c index d8cedf9e51..b77ba28bfa 100644 --- a/src/fw/services/normal/blob_db/app_glance_db.c +++ b/src/fw/services/blob_db/app_glance_db.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_glance_db.h" - -#include "app_glance_db_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/app_glance_db.h" + +#include "pbl/services/blob_db/app_glance_db_private.h" #include "applib/app_glance.h" #include "drivers/rtc.h" @@ -25,14 +12,16 @@ #include "os/mutex.h" #include "resource/resource_ids.auto.h" #include "process_management/app_install_manager.h" -#include "services/normal/app_cache.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" #include "util/units.h" +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define SETTINGS_FILE_NAME "appglancedb" //! The defines below calculate `APP_GLANCE_DB_MAX_USED_SIZE` which is the actual minimum space we //! need to guarantee all of the apps's glances on the watch can have the same number of slices, @@ -222,8 +211,7 @@ static bool prv_deserialize_attribute_list(const SerializedAppGlanceSliceHeader attribute_get_buffer_size_for_serialized_attributes(num_attributes, &buffer_size_cursor, serialized_attr_list_end); if (buffer_size < 0) { - PBL_LOG(LOG_LEVEL_WARNING, - "Failed to measure the buffer size required for deserializing an AttributeList from a " + PBL_LOG_WRN("Failed to measure the buffer size required for deserializing an AttributeList from a " "serialized slice"); return false; } @@ -232,8 +220,7 @@ static bool prv_deserialize_attribute_list(const SerializedAppGlanceSliceHeader // Allocate buffer for the data attached to the attributes *attr_list_data_buffer_out = kernel_zalloc(buffer_size); if (!*attr_list_data_buffer_out) { - PBL_LOG(LOG_LEVEL_ERROR, - "Failed to alloc memory for the Attributes' data buffer while deserializing an " + PBL_LOG_ERR("Failed to alloc memory for the Attributes' data buffer while deserializing an " "AttributeList from a serialized slice"); return false; } @@ -248,8 +235,7 @@ static bool prv_deserialize_attribute_list(const SerializedAppGlanceSliceHeader // client calling `attribute_list_destroy_list()` on `attr_list_out` Attribute *attribute_buffer = kernel_zalloc(num_attributes * sizeof(*attribute_buffer)); if (!attribute_buffer) { - PBL_LOG(LOG_LEVEL_ERROR, - "Failed to alloc memory for the buffer of Attribute's while deserializing an " + PBL_LOG_ERR("Failed to alloc memory for the buffer of Attribute's while deserializing an " "AttributeList from a serialized slice"); // Free the `*attr_list_data_buffer_out` we might have allocated above kernel_free(*attr_list_data_buffer_out); @@ -390,8 +376,7 @@ static status_t prv_serialize_glance(const AppGlance *glance, const AppGlanceSliceInternal *current_slice = &glance->slices[slice_index]; // Check the slice's type, fail the entire serialization if it's invalid if (!prv_is_slice_type_valid(current_slice->type)) { - PBL_LOG(LOG_LEVEL_WARNING, - "Tried to serialize a glance containing a slice with invalid type: %d", + PBL_LOG_WRN("Tried to serialize a glance containing a slice with invalid type: %d", current_slice->type); rv = E_INVALID_ARGUMENT; goto cleanup; @@ -493,8 +478,7 @@ static bool prv_is_serialized_slice_valid(const SerializedAppGlanceSliceHeader * AttributeList attr_list = {}; char *attr_list_data_buffer = NULL; if (!prv_deserialize_attribute_list(serialized_slice, &attr_list, &attr_list_data_buffer)) { - PBL_LOG(LOG_LEVEL_WARNING, - "Failed to deserialize an AttributeList from a serialized slice"); + PBL_LOG_WRN("Failed to deserialize an AttributeList from a serialized slice"); return false; } @@ -502,7 +486,7 @@ static bool prv_is_serialized_slice_valid(const SerializedAppGlanceSliceHeader * const bool is_attr_list_valid = prv_is_slice_attribute_list_valid(serialized_slice->type, &attr_list); if (!is_attr_list_valid) { - PBL_LOG(LOG_LEVEL_WARNING, "Serialized slice AttributeList is invalid"); + PBL_LOG_WRN("Serialized slice AttributeList is invalid"); } attribute_list_destroy_list(&attr_list); @@ -620,8 +604,9 @@ status_t app_glance_db_delete_glance(const Uuid *uuid) { static status_t prv_lock_mutex_and_open_file(void) { mutex_lock(s_app_glance_db.mutex); - const status_t rv = settings_file_open(&s_app_glance_db.settings_file, SETTINGS_FILE_NAME, - SETTINGS_FILE_SIZE); + const status_t rv = settings_file_open_growable(&s_app_glance_db.settings_file, + SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE, + KiBYTES(4)); if (rv != S_SUCCESS) { mutex_unlock(s_app_glance_db.mutex); } @@ -649,13 +634,22 @@ status_t app_glance_db_flush(void) { return S_SUCCESS; } +status_t app_glance_db_compact(void) { + status_t rv = prv_lock_mutex_and_open_file(); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&s_app_glance_db.settings_file); + prv_close_file_and_unlock_mutex(); + return rv; +} + static status_t prv_validate_glance(const Uuid *app_uuid, const SerializedAppGlanceHeader *serialized_glance, size_t *len) { // Change this block if we support multiple app glance versions in the future // For now report an error if the glance's version isn't the current database version if (serialized_glance->version != APP_GLANCE_DB_CURRENT_VERSION) { - PBL_LOG(LOG_LEVEL_WARNING, - "Tried to insert AppGlanceDB entry with invalid version!" + PBL_LOG_WRN("Tried to insert AppGlanceDB entry with invalid version!" " Entry version: %"PRIu8", AppGlanceDB version: %u", serialized_glance->version, APP_GLANCE_DB_CURRENT_VERSION); return E_INVALID_ARGUMENT; @@ -666,8 +660,7 @@ static status_t prv_validate_glance(const Uuid *app_uuid, status_t rv = app_glance_db_read((uint8_t *)app_uuid, UUID_SIZE, (uint8_t *)&existing_glance, sizeof(existing_glance)); if ((rv == S_SUCCESS) && (serialized_glance->creation_time <= existing_glance.creation_time)) { - PBL_LOG(LOG_LEVEL_WARNING, - "Tried to insert AppGlanceDB entry with older creation_time (%"PRIu32")" + PBL_LOG_WRN("Tried to insert AppGlanceDB entry with older creation_time (%"PRIu32")" " than existing entry (%"PRIu32")", serialized_glance->creation_time, existing_glance.creation_time); return E_INVALID_ARGUMENT; @@ -683,12 +676,10 @@ static status_t prv_validate_glance(const Uuid *app_uuid, prv_slice_for_each((SerializedAppGlanceHeader *)serialized_glance, *len, prv_validate_slice, &validation_context); if (!iteration_succeeded) { - PBL_LOG(LOG_LEVEL_WARNING, - "Tried to insert AppGlanceDB entry but failed to iterate over the serialized slices"); + PBL_LOG_WRN("Tried to insert AppGlanceDB entry but failed to iterate over the serialized slices"); return E_INVALID_ARGUMENT; } else if (validation_context.is_at_least_one_slice_invalid) { - PBL_LOG(LOG_LEVEL_WARNING, - "Tried to insert AppGlanceDB entry with at least one invalid slice"); + PBL_LOG_WRN("Tried to insert AppGlanceDB entry with at least one invalid slice"); return E_INVALID_ARGUMENT; } @@ -699,8 +690,7 @@ static status_t prv_validate_glance(const Uuid *app_uuid, // slices as they can fit in a BlobDB packet. We just take as many slices as we support and trim // the excess. if (validation_context.validated_size < *len) { - PBL_LOG(LOG_LEVEL_WARNING, - "Trimming AppGlanceDB entry of excess slices before insertion"); + PBL_LOG_WRN("Trimming AppGlanceDB entry of excess slices before insertion"); *len = validation_context.validated_size; } return S_SUCCESS; @@ -745,8 +735,7 @@ status_t app_glance_db_insert(const uint8_t *key, int key_len, const uint8_t *va // String initialized on the heap to reduce stack usage char *app_uuid_string = kernel_malloc_check(UUID_STRING_BUFFER_LENGTH); uuid_to_string(app_uuid, app_uuid_string); - PBL_LOG(LOG_LEVEL_WARNING, - "Attempted app glance insert for an app that's not installed. UUID: %s", + PBL_LOG_WRN("Attempted app glance insert for an app that's not installed. UUID: %s", app_uuid_string); kernel_free(app_uuid_string); return E_DOES_NOT_EXIST; @@ -800,7 +789,7 @@ status_t app_glance_db_read(const uint8_t *key, int key_len, uint8_t *val_out, i // Change this block if we support multiple app glance versions in the future if (serialized_app_glance->version != APP_GLANCE_DB_CURRENT_VERSION) { // Clear out the stale entry - PBL_LOG(LOG_LEVEL_WARNING, "Read a AppGlanceDB entry with an outdated version; deleting it"); + PBL_LOG_WRN("Read a AppGlanceDB entry with an outdated version; deleting it"); settings_file_delete(&s_app_glance_db.settings_file, key, (size_t)key_len); rv = E_DOES_NOT_EXIST; } diff --git a/src/fw/services/normal/blob_db/contacts_db.c b/src/fw/services/blob_db/contacts_db.c similarity index 78% rename from src/fw/services/normal/blob_db/contacts_db.c rename to src/fw/services/blob_db/contacts_db.c index e0616550a4..31db8c7506 100644 --- a/src/fw/services/normal/blob_db/contacts_db.c +++ b/src/fw/services/blob_db/contacts_db.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "contacts_db.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/contacts_db.h" #include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/contacts/attributes_address.h" -#include "services/normal/contacts/contacts.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/contacts/attributes_address.h" +#include "pbl/services/contacts/contacts.h" #include "os/mutex.h" #include "system/passert.h" #include "system/status_codes.h" @@ -41,9 +28,10 @@ static struct { static status_t prv_lock_mutex_and_open_file(void) { mutex_lock(s_contacts_db.mutex); - status_t rv = settings_file_open(&s_contacts_db.settings_file, - SETTINGS_FILE_NAME, - SETTINGS_FILE_SIZE); + status_t rv = settings_file_open_growable(&s_contacts_db.settings_file, + SETTINGS_FILE_NAME, + SETTINGS_FILE_SIZE, + KiBYTES(4)); if (rv != S_SUCCESS) { mutex_unlock(s_contacts_db.mutex); } @@ -174,3 +162,13 @@ status_t contacts_db_flush(void) { mutex_unlock(s_contacts_db.mutex); return rv; } + +status_t contacts_db_compact(void) { + status_t rv = prv_lock_mutex_and_open_file(); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&s_contacts_db.settings_file); + prv_close_file_and_unlock_mutex(); + return rv; +} diff --git a/src/fw/services/blob_db/endpoint.c b/src/fw/services/blob_db/endpoint.c new file mode 100644 index 0000000000..8f4bfa6ab6 --- /dev/null +++ b/src/fw/services/blob_db/endpoint.c @@ -0,0 +1,360 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/endpoint_private.h" +#include "pbl/services/blob_db/settings_blob_db.h" + +#include "kernel/pebble_tasks.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/analytics/analytics.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/status_codes.h" +#include "system/hexdump.h" +#include "util/attributes.h" +#include "util/net.h" + +#include +#include +#include + +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + +//! @file endpoint.c +//! BlobDB Endpoint +//! +//! There are 3 commands implemented in this endpoint: INSERT, DELETE, and CLEAR +//! +//! INSERT: This command will insert a key and value into the database specified. +//! +//! \code{.c} +//! 0x01 +//! key_bytes> +//! value_bytes> +//! \endcode +//! +//! DELETE: This command will delete an entry with the key in the database specified. +//! +//! \code{.c} +//! 0x04 +//! key_bytes> +//! \endcode +//! +//! CLEAR: This command will clear all entries in the database specified. +//! +//! \code{.c} +//! 0x05 +//! \endcode +//! +//! INSERT_WITH_TIMESTAMP: Insert with timestamp for conflict resolution. +//! If watch data is newer, returns BLOB_DB_DATA_STALE and phone should resync. +//! +//! \code{.c} +//! 0x0D +//! +//! +//! \endcode + +//! BlobDB Endpoint ID +static const uint16_t BLOB_DB_ENDPOINT_ID = 0xb1db; + +static const uint8_t KEY_DATA_LENGTH __attribute__((unused)) = (sizeof(uint8_t) + sizeof(uint8_t)); +static const uint8_t VALUE_DATA_LENGTH = (sizeof(uint16_t) + sizeof(uint8_t)); + +//! Message Length Constants +static const uint8_t MIN_INSERT_LENGTH = 8; +static const uint8_t MIN_INSERT_WITH_TIMESTAMP_LENGTH = 12; // + 4 bytes for timestamp +static const uint8_t MIN_DELETE_LENGTH = 6; +static const uint8_t MIN_CLEAR_LENGTH = 3; + +static bool s_bdb_accepting_messages; + +static void prv_send_response(CommSession *session, BlobDBToken token, BlobDBResponse result) { + struct PACKED BlobDBResponseMsg { + BlobDBToken token; + BlobDBResponse result; + } response = { + .token = token, + .result = result, + }; + + comm_session_send_data(session, BLOB_DB_ENDPOINT_ID, (uint8_t*)&response, sizeof(response), + COMM_SESSION_DEFAULT_TIMEOUT); +} + +static BlobDBResponse prv_interpret_db_ret_val(status_t ret_val) { + switch (ret_val) { + case S_SUCCESS: + return BLOB_DB_SUCCESS; + case E_DOES_NOT_EXIST: + return BLOB_DB_KEY_DOES_NOT_EXIST; + case E_RANGE: + return BLOB_DB_INVALID_DATABASE_ID; + case E_INVALID_ARGUMENT: + return BLOB_DB_INVALID_DATA; + case E_OUT_OF_STORAGE: + return BLOB_DB_DATABASE_FULL; + case E_INVALID_OPERATION: + return BLOB_DB_DATA_STALE; + default: + PBL_LOG_WRN("BlobDB return value caught by default case"); + return BLOB_DB_GENERAL_FAILURE; + } +} + +static const uint8_t *prv_read_ptr(const uint8_t *iter, const uint8_t *iter_end, + const uint8_t **out_buf, uint16_t buf_len) { + + // >= because we will be reading more bytes after this point + if ((buf_len == 0) || ((iter + buf_len) > iter_end)) { + PBL_LOG_WRN("BlobDB: read invalid length"); + return NULL; + } + + // grab pointer to start of buf + *out_buf = (uint8_t*)iter; + iter += buf_len; + + return iter; +} + +static const uint8_t *prv_read_key_size(const uint8_t *iter, const uint8_t *iter_end, + uint8_t *out_int) { + + // copy length from iter to out_len, then save a local copy of the length + *out_int = *iter++; + return iter; +} + +static const uint8_t *prv_read_value_size(const uint8_t *iter, const uint8_t *iter_end, + uint16_t *out_int) { + + // copy length from iter to out_len, then save a local copy of the length + *out_int = *(uint16_t*)iter; + iter += sizeof(uint16_t); + return iter; +} + +static BlobDBToken prv_try_read_token(const uint8_t *data, uint32_t length) { + if (length < sizeof(BlobDBToken)) { + return 0; + } + + return *(BlobDBToken*)data; +} + +static void prv_handle_database_insert(CommSession *session, const uint8_t *data, uint32_t length) { + if (length < MIN_INSERT_LENGTH) { + prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); + return; + } + + const uint8_t *iter = data; + BlobDBToken token; + BlobDBId db_id; + + // Read token and db_id + iter = endpoint_private_read_token_db_id(iter, &token, &db_id); + + // read key length and key bytes ptr + uint8_t key_size; + const uint8_t *key_bytes = NULL; + iter = prv_read_key_size(iter, data + length, &key_size); + iter = prv_read_ptr(iter, data + length, &key_bytes, key_size); + + // If read past end or there is not enough data left in buffer for a value size and data to exist + if (!iter || (iter > (data + length - VALUE_DATA_LENGTH))) { + prv_send_response(session, token, BLOB_DB_INVALID_DATA); + return; + } + + // read value length and value bytes ptr + uint16_t value_size; + const uint8_t *value_bytes = NULL; + iter = prv_read_value_size(iter, data + length, &value_size); + iter = prv_read_ptr(iter, data + length, &value_bytes, value_size); + + // If we read too many bytes or didn't read all the bytes (2nd test) + if (!iter || (iter != (data + length))) { + prv_send_response(session, token, BLOB_DB_INVALID_DATA); + return; + } + + // perform action on database and return result + status_t ret = blob_db_insert(db_id, key_bytes, key_size, value_bytes, value_size); + prv_send_response(session, token, prv_interpret_db_ret_val(ret)); +} + + +static void prv_handle_database_insert_with_timestamp(CommSession *session, const uint8_t *data, + uint32_t length) { + if (length < MIN_INSERT_WITH_TIMESTAMP_LENGTH) { + prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); + return; + } + + const uint8_t *iter = data; + BlobDBToken token; + BlobDBId db_id; + + // Read token and db_id + iter = endpoint_private_read_token_db_id(iter, &token, &db_id); + + // Read timestamp (4 bytes, little-endian) + uint32_t timestamp = *(uint32_t *)iter; + iter += sizeof(uint32_t); + + // read key length and key bytes ptr + uint8_t key_size; + const uint8_t *key_bytes = NULL; + iter = prv_read_key_size(iter, data + length, &key_size); + iter = prv_read_ptr(iter, data + length, &key_bytes, key_size); + + // If read past end or there is not enough data left in buffer for a value size and data to exist + if (!iter || (iter > (data + length - VALUE_DATA_LENGTH))) { + prv_send_response(session, token, BLOB_DB_INVALID_DATA); + return; + } + + // read value length and value bytes ptr + uint16_t value_size; + const uint8_t *value_bytes = NULL; + iter = prv_read_value_size(iter, data + length, &value_size); + iter = prv_read_ptr(iter, data + length, &value_bytes, value_size); + + // If we read too many bytes or didn't read all the bytes (2nd test) + if (!iter || (iter != (data + length))) { + prv_send_response(session, token, BLOB_DB_INVALID_DATA); + return; + } + + // Only Settings BlobDB supports timestamped insert + if (db_id != BlobDBIdSettings) { + // Fall back to regular insert for other databases + status_t ret = blob_db_insert(db_id, key_bytes, key_size, value_bytes, value_size); + prv_send_response(session, token, prv_interpret_db_ret_val(ret)); + return; + } + + // Use timestamped insert for Settings - will reject if watch data is newer + status_t ret = settings_blob_db_insert_with_timestamp(key_bytes, key_size, + value_bytes, value_size, + (time_t)timestamp); + prv_send_response(session, token, prv_interpret_db_ret_val(ret)); +} + + +static void prv_handle_database_delete(CommSession *session, const uint8_t *data, uint32_t length) { + if (length < MIN_DELETE_LENGTH) { + prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); + return; + } + + const uint8_t *iter = data; + BlobDBToken token; + BlobDBId db_id; + + // Read token and db_id + iter = endpoint_private_read_token_db_id(iter, &token, &db_id); + + // Read key length and key bytes + uint8_t key_size; + const uint8_t *key_bytes = NULL; + iter = prv_read_key_size(iter, data + length, &key_size); + iter = prv_read_ptr(iter, data + length, &key_bytes, key_size); + + // If we read too many bytes or key_size is 0 or didn't read all the bytes + if (!iter || (iter != (data + length))) { + prv_send_response(session, token, BLOB_DB_INVALID_DATA); + return; + } + + // perform action on database and return result + status_t ret = blob_db_delete(db_id, key_bytes, key_size); + prv_send_response(session, token, prv_interpret_db_ret_val(ret)); +} + + +static void prv_handle_database_clear(CommSession *session, const uint8_t *data, uint32_t length) { + if (length < MIN_CLEAR_LENGTH) { + prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); + return; + } + + BlobDBToken token; + BlobDBId db_id; + + // Read token and db_id + endpoint_private_read_token_db_id(data, &token, &db_id); + + // perform action on database and return result + status_t ret = blob_db_flush(db_id); + prv_send_response(session, token, prv_interpret_db_ret_val(ret)); + + // Mark the device as faithful after successfully flushing + if (ret == S_SUCCESS) { + bt_persistent_storage_set_unfaithful(false /* We are now faithful */); + } +} + +static void prv_blob_db_msg_decode_and_handle( + CommSession *session, BlobDBCommand cmd, const uint8_t *data, size_t data_length) { + switch (cmd) { + case BLOB_DB_COMMAND_INSERT: + PBL_LOG_DBG("Got INSERT"); + prv_handle_database_insert(session, data, data_length); + break; + case BLOB_DB_COMMAND_DELETE: + PBL_LOG_DBG("Got DELETE"); + prv_handle_database_delete(session, data, data_length); + break; + case BLOB_DB_COMMAND_CLEAR: + PBL_LOG_DBG("Got CLEAR"); + prv_handle_database_clear(session, data, data_length); + break; + case BLOB_DB_COMMAND_INSERT_WITH_TIMESTAMP: + PBL_LOG_DBG("Got INSERT_WITH_TIMESTAMP"); + prv_handle_database_insert_with_timestamp(session, data, data_length); + break; + // Commands not implemented. + case BLOB_DB_COMMAND_READ: + case BLOB_DB_COMMAND_UPDATE: + PBL_LOG_ERR("BlobDB Command not implemented"); + // Fallthrough + default: + PBL_LOG_ERR("Invalid BlobDB message received, cmd is %u", cmd); + prv_send_response(session, prv_try_read_token(data, data_length), BLOB_DB_INVALID_OPERATION); + break; + } +} + +void blob_db_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { + PBL_ASSERT_TASK(PebbleTask_KernelBackground); + + PBL_HEXDUMP_D(LOG_DOMAIN_BLOBDB, LOG_LEVEL_DEBUG, data, length); + + // Each BlobDB message is required to have at least a Command and a Token + static const uint8_t MIN_RAW_DATA_LEN = sizeof(BlobDBCommand) + sizeof(BlobDBToken); + if (length < MIN_RAW_DATA_LEN) { + PBL_LOG_ERR("Got a blob_db message that was too short, len: %zu", length); + prv_send_response(session, 0, BLOB_DB_INVALID_DATA); + return; + } + + const BlobDBCommand cmd = *data; + data += sizeof(BlobDBCommand); // fwd to message contents + const size_t data_length = length - sizeof(BlobDBCommand); + + if (!s_bdb_accepting_messages) { + prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_TRY_LATER); + return; + } + + prv_blob_db_msg_decode_and_handle(session, cmd, data, data_length); +} + +void blob_db_set_accepting_messages(bool enabled) { + s_bdb_accepting_messages = enabled; +} diff --git a/src/fw/services/blob_db/endpoint2.c b/src/fw/services/blob_db/endpoint2.c new file mode 100644 index 0000000000..8f135ea7dd --- /dev/null +++ b/src/fw/services/blob_db/endpoint2.c @@ -0,0 +1,413 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/endpoint_private.h" +#include "pbl/services/blob_db/settings_blob_db.h" + +#include "kernel/pebble_tasks.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/analytics/analytics.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/status_codes.h" +#include "system/hexdump.h" +#include "util/attributes.h" +#include "util/net.h" + +#include +#include +#include + +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + +//! BlobDB Endpoint ID +static const uint16_t BLOB_DB2_ENDPOINT_ID = 0xb2db; + +//! BlobDB Protocol Version - increment when adding new features +static const uint8_t BLOB_DB_PROTOCOL_VERSION = 1; + +//! Message Length Constants +static const uint8_t DIRTY_DATABASES_LENGTH = 2; +static const uint8_t START_SYNC_LENGTH = 3; +static const uint8_t WRITE_RESPONSE_LENGTH = 3; +static const uint8_t WRITEBACK_RESPONSE_LENGTH = 3; +static const uint8_t SYNC_DONE_RESPONSE_LENGTH = 3; + +static bool s_b2db_accepting_messages; + +T_STATIC BlobDBToken prv_new_token(void) { + static BlobDBToken next_token = 1; // 0 token should be avoided + return next_token++; +} + +static const uint8_t *prv_read_token_and_response(const uint8_t *iter, BlobDBToken *out_token, + BlobDBResponse *out_response) { + *out_token = *(BlobDBToken *)iter; + iter += sizeof(BlobDBToken); + *out_response = *(BlobDBResponse *)iter; + iter += sizeof(BlobDBResponse); + + return iter; +} + +T_STATIC void prv_send_response(CommSession *session, uint8_t *response, + uint8_t response_length) { + comm_session_send_data(session, BLOB_DB2_ENDPOINT_ID, response, response_length, + COMM_SESSION_DEFAULT_TIMEOUT); +} + +static void prv_handle_get_dirty_databases(CommSession *session, + const uint8_t *data, + uint32_t length) { + if (length < DIRTY_DATABASES_LENGTH) { + PBL_LOG_ERR("Got a dirty databases with an invalid length: %"PRIu32"", length); + return; + } + + struct PACKED DirtyDatabasesResponseMsg { + BlobDBCommand cmd; + BlobDBToken token; + BlobDBResponse result; + uint8_t num_ids; + BlobDBId db_ids[NumBlobDBs]; + } response = { + .cmd = BLOB_DB_COMMAND_DIRTY_DBS_RESPONSE, + .token = *(BlobDBToken *)data, + .result = BLOB_DB_SUCCESS, + }; + + + blob_db_get_dirty_dbs(response.db_ids, &response.num_ids); + // we don't want to send the extra bytes in response.db_ids + int num_empty_ids = NumBlobDBs - response.num_ids; + + prv_send_response(session, (uint8_t *) &response, sizeof(response) - num_empty_ids); +} + +static void prv_handle_start_sync(CommSession *session, + const uint8_t *data, + uint32_t length) { + if (length < START_SYNC_LENGTH) { + PBL_LOG_ERR("Got a start sync with an invalid length: %"PRIu32"", length); + return; + } + + struct PACKED StartSyncResponseMsg { + BlobDBCommand cmd; + BlobDBToken token; + BlobDBResponse result; + } response = { + .cmd = BLOB_DB_COMMAND_START_SYNC_RESPONSE, + }; + + BlobDBId db_id; + endpoint_private_read_token_db_id(data, &response.token, &db_id); + + status_t rv = blob_db_sync_db(db_id); + switch (rv) { + case S_SUCCESS: + case S_NO_ACTION_REQUIRED: + response.result = BLOB_DB_SUCCESS; + break; + case E_INVALID_ARGUMENT: + response.result = BLOB_DB_INVALID_DATABASE_ID; + break; + case E_BUSY: + response.result = BLOB_DB_TRY_LATER; + break; + default: + response.result = BLOB_DB_GENERAL_FAILURE; + break; + } + + prv_send_response(session, (uint8_t *)&response, sizeof(response)); +} + +static void prv_handle_wb_write_response(const uint8_t *data, + uint32_t length) { + // read token and response code + BlobDBToken token; + BlobDBResponse response_code; + prv_read_token_and_response(data, &token, &response_code); + + BlobDBSyncSession *sync_session = blob_db_sync_get_session_for_token(token); + if (sync_session) { + if (response_code != BLOB_DB_SUCCESS) { + // Log rejected items but still mark synced to avoid spamming phone on every sync + BlobDBDirtyItem *dirty_item = sync_session->dirty_list; + char key_str[32]; + int copy_len = (dirty_item->key_len < (int)sizeof(key_str) - 1) ? + dirty_item->key_len : (int)sizeof(key_str) - 1; + memcpy(key_str, dirty_item->key, copy_len); + key_str[copy_len] = '\0'; + PBL_LOG_WRN("Writeback rejected: key=%s response=%d", key_str, response_code); + } + blob_db_sync_next(sync_session); + } else { + // No session + PBL_LOG_WRN("received blob db wb response with an invalid token: %d", token); + } +} + +static void prv_handle_write_response(CommSession *session, + const uint8_t *data, + uint32_t length) { + if (length < WRITE_RESPONSE_LENGTH) { + PBL_LOG_ERR("Got a write response with an invalid length: %"PRIu32"", length); + return; + } + + prv_handle_wb_write_response(data, length); +} + +static void prv_handle_wb_response(CommSession *session, + const uint8_t *data, + uint32_t length) { + if (length < WRITEBACK_RESPONSE_LENGTH) { + PBL_LOG_ERR("Got a writeback response with an invalid length: %"PRIu32"", length); + return; + } + + prv_handle_wb_write_response(data, length); +} + +static void prv_handle_sync_done_response(CommSession *session, + const uint8_t *data, + uint32_t length) { + if (length < SYNC_DONE_RESPONSE_LENGTH) { + PBL_LOG_ERR("Got a sync done response with an invalid length: %"PRIu32"", length); + return; + } + + // read token and response code + BlobDBToken token; + BlobDBResponse response_code; + prv_read_token_and_response(data, &token, &response_code); + + if (response_code != BLOB_DB_SUCCESS) { + PBL_LOG_ERR("Sync Done response error: %d", response_code); + } +} + +static void prv_handle_version(CommSession *session, const uint8_t *data, uint32_t length) { + BlobDBToken token = *(BlobDBToken *)data; + + struct PACKED BlobDBVersionResponseMsg { + BlobDBCommand command; + BlobDBToken token; + BlobDBResponse result; + uint8_t version; + } response = { + .command = BLOB_DB_COMMAND_VERSION_RESPONSE, + .token = token, + .result = BLOB_DB_SUCCESS, + .version = BLOB_DB_PROTOCOL_VERSION, + }; + + prv_send_response(session, (uint8_t *)&response, sizeof(response)); +} + + +static void prv_handle_dirty_all(CommSession *session, const uint8_t *data, uint32_t length) { + // Message format: [token (2 bytes)] [db_id (1 byte)] + static const uint8_t MIN_DIRTY_ALL_LENGTH = sizeof(BlobDBToken) + sizeof(BlobDBId); + if (length < MIN_DIRTY_ALL_LENGTH) { + PBL_LOG_ERR("Got a dirty_all with an invalid length: %"PRIu32"", length); + return; + } + + BlobDBToken token; + BlobDBId db_id; + endpoint_private_read_token_db_id(data, &token, &db_id); + + struct PACKED DirtyAllResponseMsg { + BlobDBCommand cmd; + BlobDBToken token; + BlobDBResponse result; + } response = { + .cmd = BLOB_DB_COMMAND_DIRTY_ALL | RESPONSE_MASK, + .token = token, + }; + + // Currently only Settings BlobDB supports mark_all_dirty + if (db_id != BlobDBIdSettings) { + response.result = BLOB_DB_DB_NOT_SUPPORTED; + prv_send_response(session, (uint8_t *)&response, sizeof(response)); + return; + } + + status_t ret = settings_blob_db_mark_all_dirty(); + response.result = (ret == S_SUCCESS) ? BLOB_DB_SUCCESS : BLOB_DB_GENERAL_FAILURE; + prv_send_response(session, (uint8_t *)&response, sizeof(response)); +} + + +static void prv_send_error_response(CommSession *session, + BlobDBCommand cmd, + const uint8_t *data, + BlobDBResponse response_code) { + struct PACKED ErrorResponseMsg { + BlobDBCommand cmd; + BlobDBToken token; + BlobDBResponse result; + } response = { + .cmd = cmd | RESPONSE_MASK, + .token = *(BlobDBToken *)data, + .result = response_code, + }; + + prv_send_response(session, (uint8_t *)&response, sizeof(response)); +} + +static void prv_blob_db_msg_decode_and_handle( + CommSession *session, BlobDBCommand cmd, const uint8_t *data, size_t data_length) { + switch (cmd) { + case BLOB_DB_COMMAND_DIRTY_DBS: + PBL_LOG_DBG("Got DIRTY DBs"); + prv_handle_get_dirty_databases(session, data, data_length); + break; + case BLOB_DB_COMMAND_START_SYNC: + PBL_LOG_DBG("Got SYNC"); + prv_handle_start_sync(session, data, data_length); + break; + case BLOB_DB_COMMAND_WRITE_RESPONSE: + PBL_LOG_DBG("WRITE Response"); + prv_handle_write_response(session, data, data_length); + break; + case BLOB_DB_COMMAND_WRITEBACK_RESPONSE: + PBL_LOG_DBG("WRITEBACK Response"); + prv_handle_wb_response(session, data, data_length); + break; + case BLOB_DB_COMMAND_SYNC_DONE_RESPONSE: + PBL_LOG_DBG("SYNC DONE Response"); + prv_handle_sync_done_response(session, data, data_length); + break; + case BLOB_DB_COMMAND_VERSION: + PBL_LOG_DBG("Got VERSION"); + prv_handle_version(session, data, data_length); + break; + case BLOB_DB_COMMAND_DIRTY_ALL: + PBL_LOG_DBG("Got DIRTY_ALL"); + prv_handle_dirty_all(session, data, data_length); + break; + default: + PBL_LOG_ERR("Invalid BlobDB2 message received, cmd is %u", cmd); + prv_send_error_response(session, cmd, data, BLOB_DB_INVALID_OPERATION); + break; + } +} + +static uint16_t prv_send_write_writeback(BlobDBCommand cmd, + BlobDBId db_id, + time_t last_updated, + const uint8_t *key, + int key_len, + const uint8_t *val, + int val_len) { + struct PACKED WritebackMetadata { + BlobDBCommand cmd; + BlobDBToken token; + BlobDBId db_id; + uint32_t last_updated; + } writeback_metadata = { + .cmd = cmd, + .token = prv_new_token(), + .db_id = db_id, + .last_updated = last_updated, + }; + + size_t writeback_length = sizeof(writeback_metadata) + + sizeof(uint8_t) /* key length size*/ + + key_len + + sizeof(uint16_t) /* val length size */ + + val_len; + + SendBuffer *sb = comm_session_send_buffer_begin_write(comm_session_get_system_session(), + BLOB_DB2_ENDPOINT_ID, + writeback_length, + COMM_SESSION_DEFAULT_TIMEOUT); + if (sb) { + comm_session_send_buffer_write(sb, (uint8_t *)&writeback_metadata, sizeof(writeback_metadata)); + comm_session_send_buffer_write(sb, (uint8_t *)&key_len, sizeof(uint8_t)); + comm_session_send_buffer_write(sb, key, key_len); + comm_session_send_buffer_write(sb, (uint8_t *)&val_len, sizeof(uint16_t)); + comm_session_send_buffer_write(sb, val, val_len); + comm_session_send_buffer_end_write(sb); + } + + return writeback_metadata.token; +} + +BlobDBToken blob_db_endpoint_send_write(BlobDBId db_id, + time_t last_updated, + const void *key, + int key_len, + const void *val, + int val_len) { + BlobDBToken token = prv_send_write_writeback(BLOB_DB_COMMAND_WRITE, db_id, last_updated, + key, key_len, val, val_len); + + return token; +} + +BlobDBToken blob_db_endpoint_send_writeback(BlobDBId db_id, + time_t last_updated, + const void *key, + int key_len, + const void *val, + int val_len) { + BlobDBToken token = prv_send_write_writeback(BLOB_DB_COMMAND_WRITEBACK, db_id, last_updated, + key, key_len, val, val_len); + + return token; +} + +void blob_db_endpoint_send_sync_done(BlobDBId db_id) { + struct PACKED SyncDoneMsg { + BlobDBCommand cmd; + BlobDBToken token; + BlobDBId db_id; + } msg = { + .cmd = BLOB_DB_COMMAND_SYNC_DONE, + .token = prv_new_token(), + .db_id = db_id, + }; + + PBL_LOG_DBG("Sending sync done for db: %d", db_id); + + comm_session_send_data(comm_session_get_system_session(), + BLOB_DB2_ENDPOINT_ID, + (uint8_t *)&msg, + sizeof(msg), + COMM_SESSION_DEFAULT_TIMEOUT); +} + +void blob_db2_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { + PBL_ASSERT_TASK(PebbleTask_KernelBackground); + + // Each BlobDB message is required to have at least a Command and a Token + static const uint8_t MIN_RAW_DATA_LEN = sizeof(BlobDBCommand) + sizeof(BlobDBToken); + if (length < MIN_RAW_DATA_LEN) { + // We don't send failure responses for too short messages in endpoint2 + PBL_LOG_ERR("Got a blob_db2 message that was too short, len: %zu", length); + return; + } + + const BlobDBCommand cmd = *data; + data += sizeof(BlobDBCommand); // fwd to message contents + const size_t data_length = length - sizeof(BlobDBCommand); + + if (!s_b2db_accepting_messages) { + prv_send_error_response(session, cmd, data, BLOB_DB_TRY_LATER); + return; + } + + prv_blob_db_msg_decode_and_handle(session, cmd, data, data_length); +} + +void blob_db2_set_accepting_messages(bool enabled) { + s_b2db_accepting_messages = enabled; +} diff --git a/src/fw/services/blob_db/endpoint_private.c b/src/fw/services/blob_db/endpoint_private.c new file mode 100644 index 0000000000..cb563b4c35 --- /dev/null +++ b/src/fw/services/blob_db/endpoint_private.c @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/endpoint_private.h" + +#include + +extern void blob_db_set_accepting_messages(bool enabled); +extern void blob_db2_set_accepting_messages(bool enabled); + +void blob_db_enabled(bool enabled) { + blob_db_set_accepting_messages(enabled); + blob_db2_set_accepting_messages(enabled); +} + +const uint8_t *endpoint_private_read_token_db_id(const uint8_t *iter, BlobDBToken *out_token, + BlobDBId *out_db_id) { + // read token + *out_token = *(BlobDBToken*)iter; + iter += sizeof(BlobDBToken); + // read database id + *out_db_id = *iter; + iter += sizeof(BlobDBId); + + return iter; +} diff --git a/src/fw/services/normal/blob_db/health_db.c b/src/fw/services/blob_db/health_db.c similarity index 88% rename from src/fw/services/normal/blob_db/health_db.c rename to src/fw/services/blob_db/health_db.c index 898f6a19da..970e746d1c 100644 --- a/src/fw/services/normal/blob_db/health_db.c +++ b/src/fw/services/blob_db/health_db.c @@ -1,29 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_db.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/health_db.h" #include "console/prompt.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/hr_util.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/hr_util.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/passert.h" @@ -33,6 +20,8 @@ #include #include +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define HEALTH_DB_DEBUG 0 #define HEALTH_DB_MAX_KEY_LEN 30 @@ -104,9 +93,10 @@ _Static_assert(sizeof(HeartRateZoneData) % sizeof(uint32_t) == 0, static status_t prv_file_open_and_lock(SettingsFile *file) { mutex_lock(s_mutex); - status_t rv = settings_file_open(file, HEALTH_DB_FILE_NAME, HEALTH_DB_MAX_SIZE); + status_t rv = settings_file_open_growable(file, HEALTH_DB_FILE_NAME, HEALTH_DB_MAX_SIZE, + KiBYTES(8)); if (rv != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to open settings file"); + PBL_LOG_ERR("Failed to open settings file"); mutex_unlock(s_mutex); } @@ -163,7 +153,7 @@ static void prv_notify_health_listeners(const char *key, if (!prv_is_last_processed_timestamp_valid(data->last_processed_timestamp)) { return; } - PBL_LOG(LOG_LEVEL_INFO, "Got MovementData for wday: %d, cur_wday: %d, steps: %"PRIu32"", + PBL_LOG_INFO("Got MovementData for wday: %d, cur_wday: %d, steps: %"PRIu32"", wday, cur_wday, data->steps); activity_metrics_prv_set_metric(ActivityMetricStepCount, wday, data->steps); activity_metrics_prv_set_metric(ActivityMetricActiveSeconds, wday, data->active_seconds); @@ -175,7 +165,7 @@ static void prv_notify_health_listeners(const char *key, if (!prv_is_last_processed_timestamp_valid(data->last_processed_timestamp)) { return; } - PBL_LOG(LOG_LEVEL_INFO, "Got SleepData for wday: %d, cur_wday: %d, sleep: %"PRIu32"", + PBL_LOG_INFO("Got SleepData for wday: %d, cur_wday: %d, sleep: %"PRIu32"", wday, cur_wday, data->sleep_duration); activity_metrics_prv_set_metric(ActivityMetricSleepTotalSeconds, wday, data->sleep_duration); activity_metrics_prv_set_metric(ActivityMetricSleepRestfulSeconds, wday, @@ -191,7 +181,7 @@ static void prv_notify_health_listeners(const char *key, if (data->num_zones != HRZone_Max) { return; } - PBL_LOG(LOG_LEVEL_INFO, "Got HeartRateZoneData for wday: %d, cur_wday: %d, zone1: %"PRIu32"", + PBL_LOG_INFO("Got HeartRateZoneData for wday: %d, cur_wday: %d, zone1: %"PRIu32"", wday, cur_wday, data->minutes_in_zone[0]); activity_metrics_prv_set_metric(ActivityMetricHeartRateZone1Minutes, wday, data->minutes_in_zone[0]); @@ -260,7 +250,7 @@ bool health_db_get_typical_value(ActivityMetric metric, case ActivityMetricHeartRateZone1Minutes: case ActivityMetricHeartRateZone2Minutes: case ActivityMetricHeartRateZone3Minutes: - PBL_LOG(LOG_LEVEL_WARNING, "Health DB doesn't know about typical metric %d", metric); + PBL_LOG_WRN("Health DB doesn't know about typical metric %d", metric); return false; } @@ -270,7 +260,7 @@ bool health_db_get_typical_value(ActivityMetric metric, bool health_db_get_monthly_average_value(ActivityMetric metric, int32_t *value_out) { if (metric != ActivityMetricStepCount && metric != ActivityMetricSleepTotalSeconds) { - PBL_LOG(LOG_LEVEL_WARNING, "Health DB doesn't store an average for metric %d", metric); + PBL_LOG_WRN("Health DB doesn't store an average for metric %d", metric); return false; } @@ -338,18 +328,18 @@ void health_db_init(void) { status_t health_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { if (!prv_key_is_valid(key, key_len)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid health db key"); + PBL_LOG_ERR("Invalid health db key"); PBL_HEXDUMP(LOG_LEVEL_ERROR, key, key_len); return E_INVALID_ARGUMENT; } else if (!prv_value_is_valid(key, key_len, val, val_len)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid health db value. Length %d", val_len); + PBL_LOG_ERR("Invalid health db value. Length %d", val_len); return E_INVALID_ARGUMENT; } #if HEALTH_DB_DEBUG - PBL_LOG(LOG_LEVEL_DEBUG, "New health db entry key:"); + PBL_LOG_DBG("New health db entry key:"); PBL_HEXDUMP(LOG_LEVEL_DEBUG, key, key_len); - PBL_LOG(LOG_LEVEL_DEBUG, "val: "); + PBL_LOG_DBG("val: "); PBL_HEXDUMP(LOG_LEVEL_DEBUG, val, val_len); #endif @@ -435,3 +425,14 @@ status_t health_db_flush(void) { return rv; } +status_t health_db_compact(void) { + SettingsFile file; + status_t rv = prv_file_open_and_lock(&file); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&file); + prv_file_close_and_unlock(&file); + return rv; +} + diff --git a/src/fw/services/normal/blob_db/ios_notif_pref_db.c b/src/fw/services/blob_db/ios_notif_pref_db.c similarity index 89% rename from src/fw/services/normal/blob_db/ios_notif_pref_db.c rename to src/fw/services/blob_db/ios_notif_pref_db.c index ed744548d9..c17d6158b5 100644 --- a/src/fw/services/normal/blob_db/ios_notif_pref_db.c +++ b/src/fw/services/blob_db/ios_notif_pref_db.c @@ -1,30 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ios_notif_pref_db.h" - -#include "sync.h" -#include "sync_util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/ios_notif_pref_db.h" + +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/sync_util.h" #include "console/prompt.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/timeline/attributes_actions.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/timeline/attributes_actions.h" #include "system/logging.h" #include "system/passert.h" #include "util/attributes.h" @@ -32,8 +19,10 @@ #include +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + T_STATIC const char *iOS_NOTIF_PREF_DB_FILE_NAME = "iosnotifprefdb"; -T_STATIC const int iOS_NOTIF_PREF_MAX_SIZE = KiBYTES(10); +T_STATIC const int iOS_NOTIF_PREF_MAX_SIZE = KiBYTES(32); typedef struct PACKED { @@ -48,7 +37,8 @@ static PebbleMutex *s_mutex; static status_t prv_file_open_and_lock(SettingsFile *file) { mutex_lock(s_mutex); - status_t rv = settings_file_open(file, iOS_NOTIF_PREF_DB_FILE_NAME, iOS_NOTIF_PREF_MAX_SIZE); + status_t rv = settings_file_open_growable(file, iOS_NOTIF_PREF_DB_FILE_NAME, + iOS_NOTIF_PREF_MAX_SIZE, KiBYTES(4)); if (rv != S_SUCCESS) { mutex_unlock(s_mutex); } @@ -120,7 +110,7 @@ iOSNotifPrefs* ios_notif_pref_db_get_prefs(const uint8_t *app_id, int key_len) { char buffer[key_len + 1]; strncpy(buffer, (const char *)app_id, key_len); buffer[key_len] = '\0'; - PBL_LOG(LOG_LEVEL_DEBUG, "No prefs found for <%s>", buffer); + PBL_LOG_DBG("No prefs found for <%s>", buffer); prv_file_close_and_unlock(&file); return NULL; } @@ -142,7 +132,7 @@ iOSNotifPrefs* ios_notif_pref_db_get_prefs(const uint8_t *app_id, int key_len) { char buffer[key_len + 1]; strncpy(buffer, (const char *)app_id, key_len); buffer[key_len] = '\0'; - PBL_LOG(LOG_LEVEL_ERROR, "Could not parse serial data for <%s>", buffer); + PBL_LOG_ERR("Could not parse serial data for <%s>", buffer); prv_free_serialzed_prefs(serialized_prefs); return NULL; } @@ -171,7 +161,7 @@ iOSNotifPrefs* ios_notif_pref_db_get_prefs(const uint8_t *app_id, int key_len) { char buffer[key_len + 1]; strncpy(buffer, (const char *)app_id, key_len); buffer[key_len] = '\0'; - PBL_LOG(LOG_LEVEL_ERROR, "Could not deserialize data for <%s>", buffer); + PBL_LOG_ERR("Could not deserialize data for <%s>", buffer); prv_free_serialzed_prefs(serialized_prefs); kernel_free(notif_prefs); return NULL; @@ -210,7 +200,7 @@ status_t ios_notif_pref_db_store_prefs(const uint8_t *app_id, int length, Attrib char buffer[length + 1]; strncpy(buffer, (const char *)app_id, length); buffer[length] = '\0'; - PBL_LOG(LOG_LEVEL_INFO, "Added <%s> to the notif pref db", buffer); + PBL_LOG_INFO("Added <%s> to the notif pref db", buffer); blob_db_sync_record(BlobDBIdiOSNotifPref, app_id, length, rtc_get_time()); } @@ -240,7 +230,7 @@ status_t ios_notif_pref_db_insert(const uint8_t *key, int key_len, char buffer[key_len + 1]; strncpy(buffer, (const char *)key, key_len); buffer[key_len] = '\0'; - PBL_LOG(LOG_LEVEL_INFO, "iOS notif pref insert <%s>", buffer); + PBL_LOG_INFO("iOS notif pref insert <%s>", buffer); // All records inserted from the phone are not dirty (the phone is the source of truth) rv = settings_file_mark_synced(&file, key, key_len); @@ -309,6 +299,17 @@ status_t ios_notif_pref_db_flush(void) { return rv; } +status_t ios_notif_pref_db_compact(void) { + SettingsFile file; + status_t rv = prv_file_open_and_lock(&file); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&file); + prv_file_close_and_unlock(&file); + return rv; +} + status_t ios_notif_pref_db_is_dirty(bool *is_dirty_out) { SettingsFile file; status_t rv = prv_file_open_and_lock(&file); diff --git a/src/fw/services/blob_db/notif_db.c b/src/fw/services/blob_db/notif_db.c new file mode 100644 index 0000000000..333047aad0 --- /dev/null +++ b/src/fw/services/blob_db/notif_db.c @@ -0,0 +1,82 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/notif_db.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/notifications/notification_storage.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + +void notif_db_init(void) { +} + +status_t notif_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { + if (key_len != UUID_SIZE || + val_len < (int)sizeof(SerializedTimelineItemHeader)) { + return E_INVALID_ARGUMENT; + } + + // [FBO] this is a little bit silly: we deserialize the item to then re-serialize it in + // notification_storage_store. It has the advantage that it validates the payload + // and works with the existing storage + SerializedTimelineItemHeader *hdr = (SerializedTimelineItemHeader *)val; + const uint8_t *payload = val + sizeof(SerializedTimelineItemHeader); + + const bool has_status_bits = (hdr->common.status != 0); + + TimelineItem notification = {}; + if (!timeline_item_deserialize_item(¬ification, hdr, payload)) { + return E_INTERNAL; + } + + Uuid *id = kernel_malloc_check(sizeof(Uuid)); + *id = notification.header.id; + + char uuid_string[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(id, uuid_string); + + // If the notification already exists, only update the status flags + if (notification_storage_notification_exists(¬ification.header.id)) { + notification_storage_set_status(¬ification.header.id, notification.header.status); + PBL_LOG_INFO("Notification modified: %s", uuid_string); + notifications_handle_notification_acted_upon(id); + } else if (!has_status_bits) { + notification_storage_store(¬ification); + PBL_LOG_INFO("Notification added: %s", uuid_string); + notifications_handle_notification_added(id); + } + + timeline_item_free_allocated_buffer(¬ification); + return S_SUCCESS; +} + +int notif_db_get_len(const uint8_t *key, int key_len) { + if (key_len < UUID_SIZE) { + return 0; + } + + return notification_storage_get_len((Uuid *)key); +} + +status_t notif_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) { + // NYI + return S_SUCCESS; +} + +status_t notif_db_delete(const uint8_t *key, int key_len) { + if (key_len != UUID_SIZE) { + return E_INVALID_ARGUMENT; + } + + notification_storage_remove((Uuid *)key); + notifications_handle_notification_removed((Uuid *)key); + + return S_SUCCESS; +} + +status_t notif_db_flush(void) { + notification_storage_reset_and_init(); + return S_SUCCESS; +} diff --git a/src/fw/services/normal/blob_db/pin_db.c b/src/fw/services/blob_db/pin_db.c similarity index 89% rename from src/fw/services/normal/blob_db/pin_db.c rename to src/fw/services/blob_db/pin_db.c index a93955e6f0..431e1d656b 100644 --- a/src/fw/services/normal/blob_db/pin_db.c +++ b/src/fw/services/blob_db/pin_db.c @@ -1,39 +1,28 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "api.h" -#include "pin_db.h" -#include "reminder_db.h" -#include "sync.h" -#include "sync_util.h" -#include "timeline_item_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/sync_util.h" +#include "pbl/services/blob_db/timeline_item_storage.h" #include #include "kernel/events.h" #include "kernel/pbl_malloc.h" #include "process_management/app_install_manager.h" -#include "services/normal/app_cache.h" -#include "services/normal/timeline/calendar.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/timeline/calendar.h" +#include "pbl/services/timeline/timeline.h" #include "system/logging.h" #include "system/passert.h" #include "util/units.h" #include "util/uuid.h" +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define PIN_DB_MAX_AGE (3 * SECONDS_PER_DAY) // so we get at two full past days in there #define PIN_DB_FILE_NAME "pindb" #define PIN_DB_MAX_SIZE KiBYTES(40) // TODO [FBO] variable size / reasonable value @@ -76,8 +65,7 @@ static status_t prv_insert_serialized_item(const uint8_t *key, int key_len, cons // String initialized on the heap to reduce stack usage char *parent_id_string = kernel_malloc_check(UUID_STRING_BUFFER_LENGTH); uuid_to_string(&parent_id, parent_id_string); - PBL_LOG(LOG_LEVEL_ERROR, - "Pin insert for a pin with no app installed, parent id: %s", + PBL_LOG_ERR("Pin insert for a pin with no app installed, parent id: %s", parent_id_string); kernel_free(parent_id_string); goto done; @@ -227,6 +215,10 @@ void pin_db_deinit(void) { timeline_item_storage_deinit(&s_pin_db_storage); } +status_t pin_db_compact(void) { + return timeline_item_storage_compact(&s_pin_db_storage); +} + bool pin_db_has_entry_expired(time_t pin_end_timestamp) { return (pin_end_timestamp < (rtc_get_time() - PIN_DB_MAX_AGE)); } diff --git a/src/fw/services/blob_db/prefs_db.c b/src/fw/services/blob_db/prefs_db.c new file mode 100644 index 0000000000..c0591f3388 --- /dev/null +++ b/src/fw/services/blob_db/prefs_db.c @@ -0,0 +1,47 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/prefs_db.h" + +#include "process_management/app_install_manager.h" +#include "shell/prefs_private.h" + +// BlobDB APIs +//////////////////////////////////////////////////////////////////////////////// + +void prefs_db_init(void) { +} + +status_t prefs_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { + bool success = prefs_private_write_backing(key, key_len, val, val_len); + if (success) { + return S_SUCCESS; + } else { + return E_INVALID_ARGUMENT; + } +} + +int prefs_db_get_len(const uint8_t *key, int key_len) { + size_t len = prefs_private_get_backing_len(key, key_len); + if (len == 0) { + return E_INVALID_ARGUMENT; + } + return len; +} + +status_t prefs_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) { + bool success = prefs_private_read_backing(key, key_len, val_out, val_out_len); + if (success) { + return S_SUCCESS; + } else { + return E_INVALID_ARGUMENT; + } +} + +status_t prefs_db_delete(const uint8_t *key, int key_len) { + return E_INVALID_OPERATION; +} + +status_t prefs_db_flush(void) { + return E_INVALID_OPERATION; +} diff --git a/src/fw/services/normal/blob_db/reminder_db.c b/src/fw/services/blob_db/reminder_db.c similarity index 88% rename from src/fw/services/normal/blob_db/reminder_db.c rename to src/fw/services/blob_db/reminder_db.c index a39bc42bd4..18359ab6c2 100644 --- a/src/fw/services/normal/blob_db/reminder_db.c +++ b/src/fw/services/blob_db/reminder_db.c @@ -1,32 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "reminder_db.h" -#include "sync.h" -#include "sync_util.h" -#include "timeline_item_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/sync_util.h" +#include "pbl/services/blob_db/timeline_item_storage.h" #include "util/uuid.h" #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics.h" -#include "services/normal/timeline/reminders.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/timeline/reminders.h" #include "system/passert.h" #include "system/logging.h" #include "util/units.h" +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define REMINDER_DB_FILE_NAME "reminderdb" #define REMINDER_DB_MAX_SIZE KiBYTES(40) #define MAX_REMINDER_SIZE SETTINGS_VAL_MAX_LEN @@ -91,7 +80,7 @@ static bool prv_reminder_filter(SerializedTimelineItemHeader *hdr, void *context } status_t reminder_db_next_item_header(TimelineItem *next_item_out) { - PBL_LOG(LOG_LEVEL_DEBUG, "Finding next item in queue."); + PBL_LOG_DBG("Finding next item in queue."); TimelineItemId id; status_t rv = timeline_item_storage_next_item(&s_storage, &id, prv_reminder_filter); if (rv) { @@ -164,7 +153,7 @@ static status_t prv_insert_reminder(const uint8_t *key, int key_len, char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; uuid_to_string((Uuid *)key, uuid_buffer); - PBL_LOG(LOG_LEVEL_INFO, "Reminder added: %s", uuid_buffer); + PBL_LOG_INFO("Reminder added: %s", uuid_buffer); if (rv == S_SUCCESS) { if (has_reminded) { @@ -239,9 +228,11 @@ void reminder_db_deinit(void) { timeline_item_storage_deinit(&s_storage); } -status_t reminder_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { - analytics_inc(ANALYTICS_DEVICE_METRIC_REMINDER_RECEIVED_COUNT, AnalyticsClient_System); +status_t reminder_db_compact(void) { + return timeline_item_storage_compact(&s_storage); +} +status_t reminder_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { // Records inserted from the phone are synced const bool mark_synced = true; return prv_insert_reminder(key, key_len, val, val_len, mark_synced); @@ -279,6 +270,6 @@ BlobDBDirtyItem* reminder_db_get_dirty_list(void) { } status_t reminder_db_mark_synced(const uint8_t *key, int key_len) { - PBL_LOG(LOG_LEVEL_DEBUG, "reminder_db_mark_synced"); + PBL_LOG_DBG("reminder_db_mark_synced"); return timeline_item_storage_mark_synced(&s_storage, key, key_len); } diff --git a/src/fw/services/blob_db/settings_blob_db.c b/src/fw/services/blob_db/settings_blob_db.c new file mode 100644 index 0000000000..fdf017bf3d --- /dev/null +++ b/src/fw/services/blob_db/settings_blob_db.c @@ -0,0 +1,785 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/settings_blob_db.h" +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/api.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/settings/settings_file.h" +#include "shell/prefs_private.h" +#include "system/logging.h" +#include "system/passert.h" +#include "pbl/services/system_task.h" +#include "util/list.h" +#include "util/size.h" + +#include + +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + +//! Notification preferences file name and size +#define NOTIF_PREFS_FILE_NAME "notifpref" +#define NOTIF_PREFS_FILE_LEN (1024) + +//! Flag to suppress change callback during phone-originated INSERTs. +//! This prevents queuing unnecessary sync callbacks that would flood the system task queue. +static bool s_suppress_change_callback = false; + +//! Flag to track if a sync callback is already pending. +//! This coalesces multiple setting changes into a single sync callback. +static bool s_sync_callback_pending = false; + +//! Settings Whitelist +//! +//! Only these settings will be synced via BlobDB. +//! This prevents sensitive data (Bluetooth pairing, debug flags, etc.) from syncing. +static const char *s_syncable_settings[] = { + // Clock preferences + "clock24h", + "timezoneSource", + "automaticTimezoneID", + + // Display preferences + "unitsDistance", + "textStyle", + "stationaryMode", +#ifdef CONFIG_ORIENTATION_MANAGER + "displayOrientationLeftHanded", +#endif + + // Motion preferences +#ifdef CONFIG_ACCEL_SENSITIVITY + "motionSensitivity", +#endif + + // Backlight preferences + "lightEnabled", + "lightAmbientSensorEnabled", + "lightTimeoutMs", + "lightIntensity", + "lightMotion", + "lightTouch", + "lightAmbientThreshold", +#ifdef CONFIG_DYNAMIC_BACKLIGHT + "lightDynamicIntensity", + "dynBacklightMinThreshold", +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + "lightColor", +#endif + + // Language preferences + "langEnglish", + + // App preferences + "qlUp", + "qlDown", + "qlSelect", + "qlBack", + "qlSetupOpened", + "qlSingleClickUp", + "qlSingleClickDown", + "qlComboBackUp", + "qlComboUpDown", + + // UI theming + "settingsMenuHighlightColor", + "appsMenuHighlightColor", + + // Timeline preferences + "timelineQuickViewEnabled", + "timelineQuickViewBeforeTimeMin", + "timelineQuickViewWatchfaceFit", + "timelineSettingsOpened", + + // Activity preferences + "activityPreferences", + "activityHealthAppOpened", + "activityWorkoutAppOpened", + "alarmsAppOpened", + "hrmPreferences", + "heartRatePreferences", + + // Menu wrap around preferences + "menuScrollWrapAround", + "menuScrollVibeBehavior", + + // Worker preferences + "workerId", + + // Music preferences + "musicShowVolumeControls", + "musicShowProgressBar", +}; + +static const size_t s_num_syncable_settings = ARRAY_LENGTH(s_syncable_settings); + +//! Notification preferences from notifpref file that should be synced +static const char *s_syncable_notif_prefs[] = { + "mask", + "dndInterruptionsMask", + "dndShowNotifications", + "vibeIntensity", + "vibeScoreNotifications", + "vibeScoreIncomingCalls", + "vibeScoreAlarms", + "dndManuallyEnabled", + "dndSmartEnabled", + "dndWeekdaySchedule", + "dndWeekdayScheduleEnabled", + "dndWeekendSchedule", + "dndWeekendScheduleEnabled", + "notifWindowTimeout", + "notifDesignStyle", + "notifVibeDelay", + "notifBacklight", + "dndMotionBacklight", + "dndTouchBacklight", +}; + +static const size_t s_num_syncable_notif_prefs = ARRAY_LENGTH(s_syncable_notif_prefs); + +static bool s_initialized = false; + +//! Check if the connected phone supports Settings BlobDB sync +//! Returns true if the phone advertises settings_sync_support capability +bool settings_blob_db_phone_supports_sync(void) { + PebbleProtocolCapabilities capabilities; + bt_persistent_storage_get_cached_system_capabilities(&capabilities); + return capabilities.settings_sync_support; +} + +//! Check if a key matches an entry in a given list +//! Handles both key_len with null terminator (strlen+1) and without (strlen) +static bool prv_is_key_in_list(const uint8_t *key, int key_len, + const char **list, size_t list_len) { + for (size_t i = 0; i < list_len; i++) { + const char *list_key = list[i]; + size_t list_key_strlen = strlen(list_key); + // Accept key_len matching strlen (no null) or strlen+1 (with null) + if ((key_len == (int)list_key_strlen || key_len == (int)(list_key_strlen + 1)) && + memcmp(key, list_key, list_key_strlen) == 0) { + return true; + } + } + return false; +} + +//! Check if a setting key is in the shell/prefs sync whitelist +static bool prv_is_shell_pref(const uint8_t *key, int key_len) { + return prv_is_key_in_list(key, key_len, s_syncable_settings, s_num_syncable_settings); +} + +//! Check if a setting key is in the notifpref sync whitelist +static bool prv_is_notif_pref(const uint8_t *key, int key_len) { + return prv_is_key_in_list(key, key_len, s_syncable_notif_prefs, s_num_syncable_notif_prefs); +} + +//! Check if a setting key is syncable (either shell pref or notif pref) +static bool prv_is_syncable(const uint8_t *key, int key_len) { + return prv_is_shell_pref(key, key_len) || prv_is_notif_pref(key, key_len); +} + +//! Normalize key_len for notif-pref writes by stripping a trailing null. +//! +//! alerts_preferences (the local owner of the notif-prefs file) uses strlen() +//! without a trailing null when reading and writing. The phone-side BlobDB +//! protocol historically sends keys with a trailing null (key_len = strlen+1). +//! Settings file records are matched on exact key_len, so a phone write +//! creates a record that alerts_preferences_init's RESTORE_PREF cannot find, +//! causing it to fall back to the static default at boot. Normalizing the +//! incoming key_len makes phone-originated writes land at the same canonical +//! key length the local readers use. +static int prv_canonical_notif_key_len(const uint8_t *key, int key_len) { + if (key_len > 0 && key[key_len - 1] == '\0') { + return key_len - 1; + } + return key_len; +} + +//! Lock the appropriate mutex for file access +static void prv_lock_for_file(bool is_notif) { + if (is_notif) { + alerts_preferences_lock(); + } else { + prefs_private_lock(); + } +} + +//! Unlock the appropriate mutex for file access +static void prv_unlock_for_file(bool is_notif) { + if (is_notif) { + alerts_preferences_unlock(); + } else { + prefs_private_unlock(); + } +} + +//! Kernel background callback to sync all dirty settings. +//! This is coalesced - only one instance runs at a time. +static void prv_deferred_sync_callback(void *data) { + s_sync_callback_pending = false; + + // Only sync if we have an active connection to the phone + if (!comm_session_get_system_session()) { + PBL_LOG_DBG("No connection to phone, skipping settings sync"); + return; + } + + // Only sync if the phone supports settings sync + if (!settings_blob_db_phone_supports_sync()) { + return; + } + + // Sync all dirty settings using the existing dirty list mechanism + blob_db_sync_db(BlobDBIdSettings); +} + +//! Callback for settings changes - defers sync to avoid re-entrancy +//! The callback is invoked while the settings file is still open, so we can't +//! immediately sync (which would try to open the file again). Instead, we defer +//! the sync to run after the file operations complete. +//! +//! This callback is coalesced - multiple setting changes result in at most one +//! queued callback, preventing system task queue overflow. +static void prv_settings_change_callback(SettingsFile *file, const void *key, int key_len, + time_t last_modified) { + // Skip callback if suppressed (during phone-originated INSERTs) + if (s_suppress_change_callback) { + return; + } + + // Only sync whitelisted settings + if (!prv_is_syncable((const uint8_t *)key, key_len)) { + return; + } + + // Coalesce multiple changes into a single callback to avoid queue overflow + if (s_sync_callback_pending) { + return; + } + + s_sync_callback_pending = true; + system_task_add_callback(prv_deferred_sync_callback, NULL); +} + +// BlobDB Interface Implementation + +void settings_blob_db_init(void) { + if (s_initialized) { + return; + } + + // Register callback to sync settings immediately when they change + settings_file_set_change_callback(prv_settings_change_callback); + + s_initialized = true; + PBL_LOG_INFO("Settings BlobDB initialized (%u whitelisted settings)", + (unsigned int) s_num_syncable_settings); +} + +status_t settings_blob_db_insert(const uint8_t *key, int key_len, + const uint8_t *val, int val_len) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Determine which file to use based on key type + const char *file_name; + int file_len; + bool is_notif_pref = prv_is_notif_pref(key, key_len); + + if (is_notif_pref) { + file_name = NOTIF_PREFS_FILE_NAME; + file_len = NOTIF_PREFS_FILE_LEN; + key_len = prv_canonical_notif_key_len(key, key_len); + } else if (prv_is_shell_pref(key, key_len)) { + file_name = SHELL_PREFS_FILE_NAME; + file_len = SHELL_PREFS_FILE_LEN; + } else { + char key_str[128]; + size_t copy_len = (key_len > 0 && (size_t)key_len < sizeof(key_str)) ? + (size_t)key_len : sizeof(key_str) - 1; + memcpy(key_str, key, copy_len); + key_str[copy_len] = '\0'; + PBL_LOG_WRN("Rejecting non-whitelisted setting: %s", key_str); + return E_INVALID_OPERATION; + } + + prv_lock_for_file(is_notif_pref); + + SettingsFile file; + status_t status = settings_file_open(&file, file_name, file_len); + if (FAILED(status)) { + prv_unlock_for_file(is_notif_pref); + return status; + } + + // Suppress change callback - we don't want to sync back to phone for phone-originated INSERTs. + // Keep suppressed through prefs_private_handle_blob_db_event() because validation failures + // can trigger prefs_private_write_backing() which would fire the callback again. + s_suppress_change_callback = true; + status = settings_file_set(&file, key, key_len, val, val_len); + + if (PASSED(status)) { + // Mark as synced - the record came from the phone so it's already in sync + settings_file_mark_synced(&file, key, key_len); + } + settings_file_close(&file); + + // Unlock before calling event handlers - they acquire the same mutex internally + prv_unlock_for_file(is_notif_pref); + + // Update the in-memory prefs state after successful write + if (PASSED(status)) { + PebbleBlobDBEvent event = { + .db_id = BlobDBIdSettings, + .type = BlobDBEventTypeInsert, + .key = (uint8_t *)key, + .key_len = key_len, + }; + if (is_notif_pref) { + alerts_preferences_handle_blob_db_event(&event); + } else { + prefs_private_handle_blob_db_event(&event); + } + } + + s_suppress_change_callback = false; + return status; +} + +int settings_blob_db_get_len(const uint8_t *key, int key_len) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Determine which file to use based on key type + const char *file_name; + int file_len; + bool is_notif = prv_is_notif_pref(key, key_len); + if (is_notif) { + file_name = NOTIF_PREFS_FILE_NAME; + file_len = NOTIF_PREFS_FILE_LEN; + key_len = prv_canonical_notif_key_len(key, key_len); + } else { + file_name = SHELL_PREFS_FILE_NAME; + file_len = SHELL_PREFS_FILE_LEN; + } + + prv_lock_for_file(is_notif); + + SettingsFile file; + status_t status = settings_file_open(&file, file_name, file_len); + if (FAILED(status)) { + prv_unlock_for_file(is_notif); + return status; + } + + int len = settings_file_get_len(&file, key, key_len); + settings_file_close(&file); + + prv_unlock_for_file(is_notif); + return len; +} + +status_t settings_blob_db_read(const uint8_t *key, int key_len, + uint8_t *val_out, int val_len) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Determine which file to use based on key type + const char *file_name; + int file_len; + bool is_notif = prv_is_notif_pref(key, key_len); + if (is_notif) { + file_name = NOTIF_PREFS_FILE_NAME; + file_len = NOTIF_PREFS_FILE_LEN; + key_len = prv_canonical_notif_key_len(key, key_len); + } else { + file_name = SHELL_PREFS_FILE_NAME; + file_len = SHELL_PREFS_FILE_LEN; + } + + prv_lock_for_file(is_notif); + + SettingsFile file; + status_t status = settings_file_open(&file, file_name, file_len); + if (FAILED(status)) { + prv_unlock_for_file(is_notif); + return status; + } + + status = settings_file_get(&file, key, key_len, val_out, val_len); + settings_file_close(&file); + + prv_unlock_for_file(is_notif); + return status; +} + +status_t settings_blob_db_delete(const uint8_t *key, int key_len) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Determine which file to use based on key type + const char *file_name; + int file_len; + bool is_notif = prv_is_notif_pref(key, key_len); + if (is_notif) { + file_name = NOTIF_PREFS_FILE_NAME; + file_len = NOTIF_PREFS_FILE_LEN; + key_len = prv_canonical_notif_key_len(key, key_len); + } else if (prv_is_shell_pref(key, key_len)) { + file_name = SHELL_PREFS_FILE_NAME; + file_len = SHELL_PREFS_FILE_LEN; + } else { + // Only allow whitelisted settings to be deleted + return E_INVALID_OPERATION; + } + + prv_lock_for_file(is_notif); + + SettingsFile file; + status_t status = settings_file_open(&file, file_name, file_len); + if (FAILED(status)) { + prv_unlock_for_file(is_notif); + return status; + } + + status = settings_file_delete(&file, key, key_len); + settings_file_close(&file); + + prv_unlock_for_file(is_notif); + return status; +} + +// Dirty list management + +typedef struct { + BlobDBDirtyItem *dirty_list; + BlobDBDirtyItem *dirty_list_tail; +} BuildDirtyListContext; + +static bool prv_build_dirty_list_callback(SettingsFile *file, + SettingsRecordInfo *info, + void *context) { + BuildDirtyListContext *ctx = (BuildDirtyListContext *)context; + + // Skip settings that are already synced + if (!info->dirty) { + return true; + } + + // Read the key to check whitelist + uint8_t key_buf[SETTINGS_KEY_MAX_LEN]; + info->get_key(file, key_buf, info->key_len); + + // Only include whitelisted settings + if (!prv_is_syncable(key_buf, info->key_len)) { + return true; // Skip, continue iteration + } + + // Allocate dirty item + BlobDBDirtyItem *item = kernel_malloc_check(sizeof(BlobDBDirtyItem) + info->key_len); + list_init((ListNode *)item); + item->last_updated = (time_t)info->last_modified; + item->key_len = info->key_len; + memcpy(item->key, key_buf, info->key_len); + + // Add to list + if (ctx->dirty_list == NULL) { + ctx->dirty_list = item; + ctx->dirty_list_tail = item; + } else { + ctx->dirty_list_tail = (BlobDBDirtyItem *)list_append( + (ListNode *)ctx->dirty_list_tail, (ListNode *)item); + } + + return true; // Continue iteration +} + +BlobDBDirtyItem *settings_blob_db_get_dirty_list(void) { + if (!s_initialized) { + return NULL; + } + + BuildDirtyListContext ctx = { .dirty_list = NULL, .dirty_list_tail = NULL }; + + // Iterate shell prefs file + prefs_private_lock(); + SettingsFile file; + status_t status = settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN); + if (PASSED(status)) { + settings_file_each(&file, prv_build_dirty_list_callback, &ctx); + settings_file_close(&file); + } + prefs_private_unlock(); + + // Iterate notif prefs file + alerts_preferences_lock(); + status = settings_file_open(&file, NOTIF_PREFS_FILE_NAME, NOTIF_PREFS_FILE_LEN); + if (PASSED(status)) { + settings_file_each(&file, prv_build_dirty_list_callback, &ctx); + settings_file_close(&file); + } + alerts_preferences_unlock(); + + return ctx.dirty_list; +} + +status_t settings_blob_db_mark_synced(const uint8_t *key, int key_len) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Determine which file to use based on key type + const char *file_name; + int file_len; + bool is_notif = prv_is_notif_pref(key, key_len); + if (is_notif) { + file_name = NOTIF_PREFS_FILE_NAME; + file_len = NOTIF_PREFS_FILE_LEN; + } else { + file_name = SHELL_PREFS_FILE_NAME; + file_len = SHELL_PREFS_FILE_LEN; + } + + prv_lock_for_file(is_notif); + + SettingsFile file; + status_t status = settings_file_open(&file, file_name, file_len); + if (FAILED(status)) { + prv_unlock_for_file(is_notif); + return status; + } + + status = settings_file_mark_synced(&file, key, key_len); + settings_file_close(&file); + + prv_unlock_for_file(is_notif); + return status; +} + +status_t settings_blob_db_is_dirty(bool *is_dirty_out) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Quick check: iterate and return true on first dirty whitelisted setting + typedef struct { + bool found_dirty; + } IsDirtyContext; + + bool is_dirty_callback(SettingsFile *file, SettingsRecordInfo *info, void *context) { + IsDirtyContext *ctx = (IsDirtyContext *)context; + + if (!info->dirty) { + return true; // Continue + } + + // Check if whitelisted + uint8_t key_buf[SETTINGS_KEY_MAX_LEN]; + info->get_key(file, key_buf, info->key_len); + + if (prv_is_syncable(key_buf, info->key_len)) { + ctx->found_dirty = true; + return false; // Stop iteration + } + + return true; // Continue + } + + IsDirtyContext ctx = { .found_dirty = false }; + + // Check shell prefs file + prefs_private_lock(); + SettingsFile file; + status_t status = settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN); + if (PASSED(status)) { + settings_file_each(&file, is_dirty_callback, &ctx); + settings_file_close(&file); + } + prefs_private_unlock(); + + // If already found dirty, no need to check notif prefs + if (!ctx.found_dirty) { + alerts_preferences_lock(); + status = settings_file_open(&file, NOTIF_PREFS_FILE_NAME, NOTIF_PREFS_FILE_LEN); + if (PASSED(status)) { + settings_file_each(&file, is_dirty_callback, &ctx); + settings_file_close(&file); + } + alerts_preferences_unlock(); + } + + *is_dirty_out = ctx.found_dirty; + return S_SUCCESS; +} + +status_t settings_blob_db_flush(void) { + if (!s_initialized) { + return E_INTERNAL; + } + + // SettingsFile writes are already atomic, no explicit flush needed + PBL_LOG_DBG("Settings BlobDB flush (no-op for SettingsFile)"); + return S_SUCCESS; +} + +status_t settings_blob_db_mark_all_dirty(void) { + if (!s_initialized) { + return E_INTERNAL; + } + + PBL_LOG_INFO("Marking all settings as dirty for full sync"); + + status_t result = S_SUCCESS; + + // Mark shell prefs file dirty + prefs_private_lock(); + SettingsFile file; + status_t status = settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN); + if (PASSED(status)) { + status = settings_file_mark_all_dirty(&file); + settings_file_close(&file); + if (FAILED(status)) { + result = status; + } + } else { + result = status; + } + prefs_private_unlock(); + + // Mark notif prefs file dirty + alerts_preferences_lock(); + status = settings_file_open(&file, NOTIF_PREFS_FILE_NAME, NOTIF_PREFS_FILE_LEN); + if (PASSED(status)) { + status = settings_file_mark_all_dirty(&file); + settings_file_close(&file); + if (FAILED(status) && PASSED(result)) { + result = status; + } + } + alerts_preferences_unlock(); + + return result; +} + +// Context for finding last_modified timestamp +typedef struct { + const uint8_t *search_key; + int search_key_len; + time_t last_modified; + bool found; +} GetTimestampContext; + +static bool prv_get_timestamp_callback(SettingsFile *file, SettingsRecordInfo *info, + void *context) { + GetTimestampContext *ctx = (GetTimestampContext *)context; + + if (info->key_len != ctx->search_key_len) { + return true; // Continue + } + + uint8_t key_buf[SETTINGS_KEY_MAX_LEN]; + info->get_key(file, key_buf, info->key_len); + + if (memcmp(key_buf, ctx->search_key, info->key_len) == 0) { + ctx->last_modified = (time_t)info->last_modified; + ctx->found = true; + return false; // Stop iteration + } + + return true; // Continue +} + +status_t settings_blob_db_insert_with_timestamp(const uint8_t *key, int key_len, + const uint8_t *val, int val_len, + time_t timestamp) { + if (!s_initialized) { + return E_INTERNAL; + } + + // Determine which file to use based on key type + const char *file_name; + int file_len; + bool is_notif_pref = prv_is_notif_pref(key, key_len); + + if (is_notif_pref) { + file_name = NOTIF_PREFS_FILE_NAME; + file_len = NOTIF_PREFS_FILE_LEN; + key_len = prv_canonical_notif_key_len(key, key_len); + } else if (prv_is_shell_pref(key, key_len)) { + file_name = SHELL_PREFS_FILE_NAME; + file_len = SHELL_PREFS_FILE_LEN; + } else { + return E_INVALID_OPERATION; + } + + prv_lock_for_file(is_notif_pref); + + SettingsFile file; + status_t status = settings_file_open(&file, file_name, file_len); + if (FAILED(status)) { + prv_unlock_for_file(is_notif_pref); + return status; + } + + // Check if existing value has a newer timestamp + GetTimestampContext ctx = { + .search_key = key, + .search_key_len = key_len, + .last_modified = 0, + .found = false, + }; + settings_file_each(&file, prv_get_timestamp_callback, &ctx); + + if (ctx.found && ctx.last_modified > timestamp) { + // Watch data is newer - reject the insert + settings_file_close(&file); + prv_unlock_for_file(is_notif_pref); + PBL_LOG_DBG("Rejecting stale data: watch=%lu phone=%lu", + (unsigned long)ctx.last_modified, (unsigned long)timestamp); + return E_INVALID_OPERATION; + } + + // Phone data is newer or equal, or key doesn't exist - do the insert + // Suppress change callback - we don't want to sync back to phone for phone-originated INSERTs. + // Keep suppressed through prefs_private_handle_blob_db_event() because validation failures + // can trigger prefs_private_write_backing() which would fire the callback again. + s_suppress_change_callback = true; + status = settings_file_set(&file, key, key_len, val, val_len); + + if (PASSED(status)) { + // Mark as synced - the record came from the phone so it's already in sync + settings_file_mark_synced(&file, key, key_len); + } + settings_file_close(&file); + + // Unlock before calling event handlers - they acquire the same mutex internally + prv_unlock_for_file(is_notif_pref); + + // Update the in-memory prefs state after successful write + if (PASSED(status)) { + PebbleBlobDBEvent event = { + .db_id = BlobDBIdSettings, + .type = BlobDBEventTypeInsert, + .key = (uint8_t *)key, + .key_len = key_len, + }; + if (is_notif_pref) { + alerts_preferences_handle_blob_db_event(&event); + } else { + prefs_private_handle_blob_db_event(&event); + } + } + + s_suppress_change_callback = false; + return status; +} diff --git a/src/fw/services/normal/blob_db/sync.c b/src/fw/services/blob_db/sync.c similarity index 77% rename from src/fw/services/normal/blob_db/sync.c rename to src/fw/services/blob_db/sync.c index e0be7ffea1..a145857907 100644 --- a/src/fw/services/normal/blob_db/sync.c +++ b/src/fw/services/blob_db/sync.c @@ -1,36 +1,28 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sync.h" -#include "endpoint.h" -#include "util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/blob_db/endpoint.h" +#include "pbl/services/blob_db/util.h" #include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "util/list.h" #include +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define SYNC_TIMEOUT_SECONDS 30 +#define SYNC_ABANDON_TIMEOUT_SECONDS (5 * 60) // 5 minutes to fully abandon static BlobDBSyncSession *s_sync_sessions = NULL; +static void prv_send_writeback(BlobDBSyncSession *session); + static bool prv_session_id_filter_callback(ListNode *node, void *data) { BlobDBId db_id = (BlobDBId)data; BlobDBSyncSession *session = (BlobDBSyncSession *)node; @@ -46,12 +38,30 @@ static bool prv_session_token_filter_callback(ListNode *node, void *data) { return session->current_token == token; } -static void prv_timeout_kernelbg_callback(void *data) { - PBL_LOG(LOG_LEVEL_INFO, "Blob DB Sync timeout"); +static void prv_abandon_kernelbg_callback(void *data) { + PBL_LOG_INFO("Blob DB Sync abandoned after extended timeout"); BlobDBSyncSession *session = data; blob_db_sync_cancel(session); } +static void prv_abandon_timer_callback(void *data) { + system_task_add_callback(prv_abandon_kernelbg_callback, data); +} + +static void prv_timeout_kernelbg_callback(void *data) { + BlobDBSyncSession *session = data; + + // Start the abandon timer if not already running + if (!regular_timer_is_scheduled(&session->abandon_timer)) { + regular_timer_add_multisecond_callback(&session->abandon_timer, SYNC_ABANDON_TIMEOUT_SECONDS); + } + + // Retry sending the current item + PBL_LOG_INFO("Blob DB Sync timeout, retrying (db %d)", session->db_id); + session->state = BlobDBSyncSessionStateIdle; + prv_send_writeback(session); +} + static void prv_timeout_timer_callback(void *data) { system_task_add_callback(prv_timeout_kernelbg_callback, data); } @@ -67,7 +77,7 @@ static void prv_send_writeback(BlobDBSyncSession *session) { } if (!comm_session_get_system_session()) { - PBL_LOG(LOG_LEVEL_INFO, "Cancelling sync: No route to phone"); + PBL_LOG_INFO("Cancelling sync: No route to phone"); blob_db_sync_cancel(session); return; } @@ -85,7 +95,7 @@ static void prv_send_writeback(BlobDBSyncSession *session) { blob_db_sync_next(session); } else { // something went terribly wrong - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read blob DB during sync. Error code: 0x%"PRIx32, status); + PBL_LOG_ERR("Failed to read blob DB during sync. Error code: 0x%"PRIx32, status); blob_db_sync_cancel(session); } @@ -123,6 +133,10 @@ BlobDBSyncSession* prv_create_sync_session(BlobDBId db_id, BlobDBDirtyItem *dirt .cb = prv_timeout_timer_callback, .cb_data = session, }; + session->abandon_timer = (const RegularTimerInfo) { + .cb = prv_abandon_timer_callback, + .cb_data = session, + }; s_sync_sessions = (BlobDBSyncSession *)list_prepend((ListNode *)s_sync_sessions, (ListNode *)session); @@ -146,7 +160,7 @@ status_t blob_db_sync_db(BlobDBId db_id) { if (db_id >= NumBlobDBs) { return E_INVALID_ARGUMENT; } - PBL_LOG(LOG_LEVEL_INFO, "Starting BlobDB db sync: %d", db_id); + PBL_LOG_INFO("Starting BlobDB db sync: %d", db_id); BlobDBDirtyItem *dirty_list = blob_db_get_dirty_list(db_id); if (!dirty_list) { @@ -181,7 +195,7 @@ status_t blob_db_sync_record(BlobDBId db_id, const void *key, int key_len, time_ char buffer[key_len + 1]; strncpy(buffer, (const char *)key, key_len); buffer[key_len] = '\0'; - PBL_LOG(LOG_LEVEL_INFO, "Starting BlobDB record sync: <%s>", buffer); + PBL_LOG_INFO("Starting BlobDB record sync: <%s>", buffer); BlobDBDirtyItem *dirty_list = kernel_zalloc_check(sizeof(BlobDBDirtyItem) + key_len); list_init((ListNode *)dirty_list); @@ -197,17 +211,26 @@ status_t blob_db_sync_record(BlobDBId db_id, const void *key, int key_len, time_ } void blob_db_sync_cancel(BlobDBSyncSession *session) { - PBL_LOG(LOG_LEVEL_DEBUG, "Cancelling session %d sync", session->db_id); + PBL_LOG_DBG("Cancelling session %d sync", session->db_id); if (regular_timer_is_scheduled(&session->timeout_timer)) { regular_timer_remove_callback(&session->timeout_timer); } + if (regular_timer_is_scheduled(&session->abandon_timer)) { + regular_timer_remove_callback(&session->abandon_timer); + } blob_db_util_free_dirty_list(session->dirty_list); list_remove((ListNode *)session, (ListNode **)&s_sync_sessions, NULL); kernel_free(session); } void blob_db_sync_next(BlobDBSyncSession *session) { - PBL_LOG(LOG_LEVEL_DEBUG, "blob_db_sync_next"); + PBL_LOG_DBG("blob_db_sync_next"); + + // Cancel abandon timer - we got a successful response, so connection is working + if (regular_timer_is_scheduled(&session->abandon_timer)) { + regular_timer_remove_callback(&session->abandon_timer); + } + BlobDBDirtyItem *dirty_item = session->dirty_list; blob_db_mark_synced(session->db_id, dirty_item->key, dirty_item->key_len); @@ -225,11 +248,14 @@ void blob_db_sync_next(BlobDBSyncSession *session) { if (session->dirty_list) { prv_send_writeback(session); } else { - PBL_LOG(LOG_LEVEL_INFO, "Finished syncing db %d, session type: %d", session->db_id, + PBL_LOG_INFO("Finished syncing db %d, session type: %d", session->db_id, session->session_type); if (regular_timer_is_scheduled(&session->timeout_timer)) { regular_timer_remove_callback(&session->timeout_timer); } + if (regular_timer_is_scheduled(&session->abandon_timer)) { + regular_timer_remove_callback(&session->abandon_timer); + } if (session->session_type == BlobDBSyncSessionTypeDB) { // Only send the sync done when syncing an entire db blob_db_endpoint_send_sync_done(session->db_id); @@ -239,3 +265,4 @@ void blob_db_sync_next(BlobDBSyncSession *session) { } } } + diff --git a/src/fw/services/blob_db/sync_util.c b/src/fw/services/blob_db/sync_util.c new file mode 100644 index 0000000000..05989c8a08 --- /dev/null +++ b/src/fw/services/blob_db/sync_util.c @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/sync_util.h" + +#include "kernel/pbl_malloc.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + +// Caution: CommonTimelineItemHeader .flags & .status are stored inverted and not auto-restored +// by the underlying db API. If .flags or .status is used from a CommonTimelineItemHeader below, +// be very careful + + +bool sync_util_is_dirty_cb(SettingsFile *file, SettingsRecordInfo *info, void *context) { + // If there is a single dirty record, update the out bool to dirty and stop iterating + if (info->dirty) { + *((bool *)context) = true; + return false; + } + + return true; +} + +bool sync_util_build_dirty_list_cb(SettingsFile *file, SettingsRecordInfo *info, void *context) { + if (info->dirty) { + BlobDBDirtyItem *dirty_list = *(BlobDBDirtyItem **)context; + + BlobDBDirtyItem *new_node = kernel_zalloc(sizeof(BlobDBDirtyItem) + info->key_len); + if (!new_node) { + PBL_LOG_WRN("Ran out of memory while building a dirty list"); + return false; + } + + new_node->last_updated = info->last_modified; + new_node->key_len = info->key_len; + info->get_key(file, new_node->key, new_node->key_len); + + *(BlobDBDirtyItem **)context = + (BlobDBDirtyItem *)list_prepend((ListNode *) dirty_list, (ListNode *)new_node); + } + + return true; +} diff --git a/src/fw/services/normal/blob_db/timeline_item_storage.c b/src/fw/services/blob_db/timeline_item_storage.c similarity index 75% rename from src/fw/services/normal/blob_db/timeline_item_storage.c rename to src/fw/services/blob_db/timeline_item_storage.c index c4ab2544b1..259bb69197 100644 --- a/src/fw/services/normal/blob_db/timeline_item_storage.c +++ b/src/fw/services/blob_db/timeline_item_storage.c @@ -1,28 +1,48 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_item_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "pbl/services/blob_db/timeline_item_storage.h" + +#include "drivers/rtc.h" #include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_raw_iter.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_raw_iter.h" #include "system/logging.h" +#include "util/units.h" + +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); #define MAX_CHILDREN_PER_PIN 3 +// FIRM-1649: temporary instrumentation to catch long mutex hold/wait on the +// timeline item storage mutex. Suspected root cause of multi-second KernelMain +// stalls during QuickView/alarm-save flows is settings_file compaction running +// under this mutex on KernelBackground. +#define FIRM_1649_MUTEX_WARN_MS 100 + +static RtcTicks prv_storage_lock(TimelineItemStorage *storage, const char *op) { + RtcTicks before = rtc_get_ticks(); + mutex_lock(storage->mutex); + RtcTicks after = rtc_get_ticks(); + uint32_t wait_ms = (uint32_t)(((after - before) * 1000) / RTC_TICKS_HZ); + if (wait_ms >= FIRM_1649_MUTEX_WARN_MS) { + PBL_LOG_WRN("FIRM-1649: %s waited %"PRIu32"ms for %s mutex", + op, wait_ms, storage->name); + } + return after; +} + +static void prv_storage_unlock(TimelineItemStorage *storage, RtcTicks lock_ticks, + const char *op) { + uint32_t hold_ms = + (uint32_t)(((rtc_get_ticks() - lock_ticks) * 1000) / RTC_TICKS_HZ); + mutex_unlock(storage->mutex); + if (hold_ms >= FIRM_1649_MUTEX_WARN_MS) { + PBL_LOG_WRN("FIRM-1649: %s held %s mutex for %"PRIu32"ms", + op, storage->name, hold_ms); + } +} + typedef struct { Uuid parent_id; Uuid children_ids[MAX_CHILDREN_PER_PIN]; @@ -53,7 +73,7 @@ static bool prv_each_first_item(SettingsFile *file, SettingsRecordInfo *info, if (info->val_len < (int)sizeof(SerializedTimelineItemHeader) || info->key_len != UUID_SIZE) { // deleted or malformed values if (info->key_len != UUID_SIZE) { - PBL_LOG(LOG_LEVEL_WARNING, "Found reminder with invalid key size %d; ignoring.", + PBL_LOG_WRN("Found reminder with invalid key size %d; ignoring.", info->key_len); } return true; @@ -104,7 +124,7 @@ static bool prv_each_find_children(SettingsFile *file, SettingsRecordInfo *info, info->key_len != UUID_SIZE) { // malformed values; deleted values have their lengths set to 0 if (info->key_len != UUID_SIZE) { - PBL_LOG(LOG_LEVEL_WARNING, "Found malformed item with invalid key/val sizes; ignoring."); + PBL_LOG_WRN("Found malformed item with invalid key/val sizes; ignoring."); } return true; } @@ -129,7 +149,7 @@ static bool prv_each_find_children(SettingsFile *file, SettingsRecordInfo *info, bool timeline_item_storage_is_empty(TimelineItemStorage *storage) { bool rv = true; - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); AnyInfo any_info = { .empty = true }; status_t status = settings_file_each(&storage->file, prv_each_any_item, &any_info); @@ -140,13 +160,13 @@ bool timeline_item_storage_is_empty(TimelineItemStorage *storage) { rv = any_info.empty; cleanup: - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } status_t timeline_item_storage_next_item(TimelineItemStorage *storage, Uuid *id_out, TimelineItemStorageFilterCallback filter_cb) { - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); NextInfo next_info = {0}; next_info.current = rtc_get_time(); @@ -167,13 +187,13 @@ status_t timeline_item_storage_next_item(TimelineItemStorage *storage, Uuid *id_ rv = S_SUCCESS; cleanup: - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } bool timeline_item_storage_exists_with_parent(TimelineItemStorage *storage, const Uuid *parent_id) { - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); FindChildrenInfo info = { .parent_id = *parent_id, @@ -192,7 +212,7 @@ bool timeline_item_storage_exists_with_parent(TimelineItemStorage *storage, cons } cleanup: - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv == S_SUCCESS; } @@ -200,7 +220,7 @@ status_t timeline_item_storage_delete_with_parent( TimelineItemStorage *storage, const Uuid *parent_id, TimelineItemStorageChildDeleteCallback child_delete_cb) { - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); FindChildrenInfo info = { .parent_id = *parent_id, @@ -226,16 +246,16 @@ status_t timeline_item_storage_delete_with_parent( } cleanup: - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } //! Caution: CommonTimelineItemHeader .flags & .status are stored inverted and not auto-restored status_t timeline_item_storage_each(TimelineItemStorage *storage, TimelineItemStorageEachCallback each, void *data) { - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_each(&storage->file, each, data); - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } @@ -247,9 +267,10 @@ void timeline_item_storage_init(TimelineItemStorage *storage, .max_item_age = max_age, .mutex = mutex_create(), }; - status_t rv = settings_file_open(&storage->file, storage->name, storage->max_size); + status_t rv = settings_file_open_growable(&storage->file, storage->name, + storage->max_size, KiBYTES(8)); if (FAILED(rv)) { - PBL_LOG(LOG_LEVEL_ERROR, "Unable to create settings file %s, rv = %"PRId32 "!", + PBL_LOG_ERR("Unable to create settings file %s, rv = %"PRId32 "!", filename, rv); } } @@ -258,6 +279,13 @@ void timeline_item_storage_deinit(TimelineItemStorage *storage) { settings_file_close(&storage->file); } +status_t timeline_item_storage_compact(TimelineItemStorage *storage) { + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); + status_t rv = settings_file_compact(&storage->file); + prv_storage_unlock(storage, lock_ticks, __func__); + return rv; +} + status_t timeline_item_storage_insert(TimelineItemStorage *storage, const uint8_t *key, int key_len, const uint8_t *val, int val_len, bool mark_as_synced) { if (key_len != UUID_SIZE || @@ -268,7 +296,7 @@ status_t timeline_item_storage_insert(TimelineItemStorage *storage, // Check that the layout has the correct items if (!timeline_item_verify_layout_serialized(val, val_len)) { - PBL_LOG(LOG_LEVEL_WARNING, "Timeline item does not have the correct attributes"); + PBL_LOG_WRN("Timeline item does not have the correct attributes"); return E_INVALID_ARGUMENT; } @@ -278,7 +306,7 @@ status_t timeline_item_storage_insert(TimelineItemStorage *storage, time_t timestamp = timeline_item_get_tz_timestamp(&hdr->common); time_t end_timestamp = timestamp + hdr->common.duration * SECONDS_PER_MINUTE; if (end_timestamp < (int)(now - storage->max_item_age)) { - PBL_LOG(LOG_LEVEL_WARNING, "Rejecting stale timeline item %ld seconds old", + PBL_LOG_WRN("Rejecting stale timeline item %ld seconds old", now - timestamp); return E_INVALID_OPERATION; } @@ -288,7 +316,7 @@ status_t timeline_item_storage_insert(TimelineItemStorage *storage, hdr->common.flags = ~hdr->common.flags; hdr->common.status = ~hdr->common.status; - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_set(&storage->file, key, key_len, val, val_len); // Restore flags & status @@ -299,17 +327,17 @@ status_t timeline_item_storage_insert(TimelineItemStorage *storage, settings_file_mark_synced(&storage->file, key, key_len); } - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } int timeline_item_storage_get_len(TimelineItemStorage *storage, const uint8_t *key, int key_len) { - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_get_len(&storage->file, key, key_len); - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } @@ -319,7 +347,7 @@ status_t timeline_item_storage_read(TimelineItemStorage *storage, return E_INVALID_ARGUMENT; } - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_get(&storage->file, key, key_len, val_out, val_len); @@ -328,7 +356,7 @@ status_t timeline_item_storage_read(TimelineItemStorage *storage, hdr->common.flags = ~hdr->common.flags; hdr->common.status = ~hdr->common.status; - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } @@ -355,14 +383,14 @@ status_t timeline_item_storage_set_status_bits(TimelineItemStorage *storage, return E_INVALID_ARGUMENT; } - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); int offset = offsetof(SerializedTimelineItemHeader, common.status); // Invert status to store on flash status = ~status; status_t rv = settings_file_set_byte(&storage->file, key, key_len, offset, status); - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } @@ -372,11 +400,11 @@ status_t timeline_item_storage_delete(TimelineItemStorage *storage, return E_INVALID_ARGUMENT; } - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_delete(&storage->file, key, key_len); - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } @@ -386,11 +414,11 @@ status_t timeline_item_storage_mark_synced(TimelineItemStorage *storage, return E_INVALID_ARGUMENT; } - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_mark_synced(&storage->file, key, key_len); - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } @@ -428,8 +456,8 @@ static void prv_flush_rewrite_cb(SettingsFile *old, } status_t timeline_item_storage_flush(TimelineItemStorage *storage) { - mutex_lock(storage->mutex); + RtcTicks lock_ticks = prv_storage_lock(storage, __func__); status_t rv = settings_file_rewrite(&storage->file, prv_flush_rewrite_cb, NULL); - mutex_unlock(storage->mutex); + prv_storage_unlock(storage, lock_ticks, __func__); return rv; } diff --git a/src/fw/services/blob_db/util.c b/src/fw/services/blob_db/util.c new file mode 100644 index 0000000000..176a678e03 --- /dev/null +++ b/src/fw/services/blob_db/util.c @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/util.h" + +#include "kernel/pbl_malloc.h" + +#include + +void blob_db_util_free_dirty_list(BlobDBDirtyItem *dirty_list) { + ListNode *head = &dirty_list->node; + ListNode *cur; + while (head) { + cur = head; + list_remove(cur, &head, NULL); + kernel_free(cur); + } +} diff --git a/src/fw/services/normal/blob_db/watch_app_prefs_db.c b/src/fw/services/blob_db/watch_app_prefs_db.c similarity index 85% rename from src/fw/services/normal/blob_db/watch_app_prefs_db.c rename to src/fw/services/blob_db/watch_app_prefs_db.c index f349799c8d..ac1ed19256 100644 --- a/src/fw/services/normal/blob_db/watch_app_prefs_db.c +++ b/src/fw/services/blob_db/watch_app_prefs_db.c @@ -1,31 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "watch_app_prefs_db.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/watch_app_prefs_db.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/weather/weather_service_private.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/weather/weather_service_private.h" #include "system/logging.h" #include "system/status_codes.h" #include "util/units.h" #include "util/uuid.h" +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + static struct { SettingsFile settings_file; PebbleRecursiveMutex *mutex; @@ -45,9 +34,10 @@ T_STATIC const char *PREF_KEY_SEND_TEXT_APP = "sendTextApp"; static status_t prv_lock_mutex_and_open_file(void) { mutex_lock_recursive(s_watch_app_prefs_db.mutex); - status_t rv = settings_file_open(&s_watch_app_prefs_db.settings_file, - SETTINGS_FILE_NAME, - SETTINGS_FILE_SIZE); + status_t rv = settings_file_open_growable(&s_watch_app_prefs_db.settings_file, + SETTINGS_FILE_NAME, + SETTINGS_FILE_SIZE, + KiBYTES(4)); if (rv != S_SUCCESS) { mutex_unlock_recursive(s_watch_app_prefs_db.mutex); } @@ -148,7 +138,7 @@ status_t watch_app_prefs_db_insert(const uint8_t *key, int key_len, const uint8_ const bool is_valid_reminder_key = prv_is_key_valid(key, key_len, PREF_KEY_REMINDER_APP); if (!is_valid_send_text_key && !is_valid_weather_key && !is_valid_reminder_key) { - PBL_LOG(LOG_LEVEL_ERROR, "Error inserting app_prefs: invalid key"); + PBL_LOG_ERR("Error inserting app_prefs: invalid key"); return E_INVALID_ARGUMENT; } @@ -156,7 +146,7 @@ status_t watch_app_prefs_db_insert(const uint8_t *key, int key_len, const uint8_ !prv_validate_received_pref(val_len, sizeof(SerializedSendTextPrefs), ((SerializedSendTextPrefs *)val)->num_contacts, sizeof(SerializedSendTextContact))) { - PBL_LOG(LOG_LEVEL_ERROR, "Error inserting app_prefs: invalid send text contact list"); + PBL_LOG_ERR("Error inserting app_prefs: invalid send text contact list"); return E_INVALID_ARGUMENT; } @@ -164,7 +154,7 @@ status_t watch_app_prefs_db_insert(const uint8_t *key, int key_len, const uint8_ !prv_validate_received_pref(val_len, sizeof(SerializedWeatherAppPrefs), ((SerializedWeatherAppPrefs *)val)->num_locations, sizeof(Uuid))) { - PBL_LOG(LOG_LEVEL_ERROR, "Error inserting app_prefs: invalid weather list"); + PBL_LOG_ERR("Error inserting app_prefs: invalid weather list"); return E_INVALID_ARGUMENT; } @@ -228,3 +218,13 @@ status_t watch_app_prefs_db_flush(void) { mutex_unlock_recursive(s_watch_app_prefs_db.mutex); return rv; } + +status_t watch_app_prefs_db_compact(void) { + status_t rv = prv_lock_mutex_and_open_file(); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&s_watch_app_prefs_db.settings_file); + prv_close_file_and_unlock_mutex(); + return rv; +} diff --git a/src/fw/services/normal/blob_db/weather_db.c b/src/fw/services/blob_db/weather_db.c similarity index 83% rename from src/fw/services/normal/blob_db/weather_db.c rename to src/fw/services/blob_db/weather_db.c index 7b69d4453f..4c7f603eb3 100644 --- a/src/fw/services/normal/blob_db/weather_db.c +++ b/src/fw/services/blob_db/weather_db.c @@ -1,30 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_db.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/weather_db.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/weather/weather_service.h" -#include "services/normal/weather/weather_types.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_types.h" +#include "system/logging.h" #include "system/passert.h" #include "util/units.h" +PBL_LOG_MODULE_DECLARE(service_blob_db, CONFIG_SERVICE_BLOB_DB_LOG_LEVEL); + #define SETTINGS_FILE_NAME "weatherdb" #define SETTINGS_FILE_SIZE (KiBYTES(30)) @@ -45,9 +35,10 @@ typedef struct WeatherDBIteratorData { static status_t prv_lock_mutex_and_open_file(void) { mutex_lock(s_weather_db.mutex); - status_t rv = settings_file_open(&s_weather_db.settings_file, - SETTINGS_FILE_NAME, - SETTINGS_FILE_SIZE); + status_t rv = settings_file_open_growable(&s_weather_db.settings_file, + SETTINGS_FILE_NAME, + SETTINGS_FILE_SIZE, + KiBYTES(4)); if (rv != S_SUCCESS) { mutex_unlock(s_weather_db.mutex); } @@ -71,7 +62,7 @@ static bool prv_weather_db_for_each_cb(SettingsFile *file, SettingsRecordInfo *i WeatherDBEntry *entry = task_zalloc_check(info->val_len); info->get_val(file, entry, info->val_len); if (entry->version != WEATHER_DB_CURRENT_VERSION) { - PBL_LOG(LOG_LEVEL_WARNING, "Version mismatch! Entry version: %" PRIu8 ", WeatherDB version: %u", + PBL_LOG_WRN("Version mismatch! Entry version: %" PRIu8 ", WeatherDB version: %u", entry->version, WEATHER_DB_CURRENT_VERSION); goto cleanup; } @@ -126,6 +117,16 @@ status_t weather_db_flush(void) { return S_SUCCESS; } +status_t weather_db_compact(void) { + status_t rv = prv_lock_mutex_and_open_file(); + if (rv != S_SUCCESS) { + return rv; + } + rv = settings_file_compact(&s_weather_db.settings_file); + prv_close_file_and_unlock_mutex(); + return rv; +} + status_t weather_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { if (!weather_service_supported_by_phone()) { return E_RANGE; @@ -138,8 +139,7 @@ status_t weather_db_insert(const uint8_t *key, int key_len, const uint8_t *val, const WeatherDBEntry *entry = (WeatherDBEntry *)val; if (entry->version != WEATHER_DB_CURRENT_VERSION) { - PBL_LOG(LOG_LEVEL_WARNING, - "Version mismatch on insert! Entry version: %" PRIu8 ", WeatherDB version: %u", + PBL_LOG_WRN("Version mismatch on insert! Entry version: %" PRIu8 ", WeatherDB version: %u", entry->version, WEATHER_DB_CURRENT_VERSION); return E_INVALID_ARGUMENT; } @@ -180,7 +180,7 @@ status_t weather_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int rv = settings_file_get(&s_weather_db.settings_file, key, key_len, val_out, val_out_len); if (((WeatherDBEntry*)val_out)->version != WEATHER_DB_CURRENT_VERSION) { // We might as well clear out the stale entry - PBL_LOG(LOG_LEVEL_WARNING, "Read an old weather DB entry"); + PBL_LOG_WRN("Read an old weather DB entry"); settings_file_delete(&s_weather_db.settings_file, key, key_len); rv = E_DOES_NOT_EXIST; } diff --git a/src/fw/services/blob_db/wscript_build b/src/fw/services/blob_db/wscript_build new file mode 100644 index 0000000000..c099c0d8f5 --- /dev/null +++ b/src/fw/services/blob_db/wscript_build @@ -0,0 +1,33 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'api.c', + 'app_db.c', + 'app_glance_db.c', + 'contacts_db.c', + 'endpoint.c', + 'endpoint2.c', + 'endpoint_private.c', + 'health_db.c', + 'ios_notif_pref_db.c', + 'notif_db.c', + 'pin_db.c', + 'prefs_db.c', + 'reminder_db.c', + 'settings_blob_db.c', + 'sync.c', + 'sync_util.c', + 'timeline_item_storage.c', + 'util.c', + 'watch_app_prefs_db.c', + 'weather_db.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_blob_db', +) + +bld.env.SERVICES.append('services_blob_db') diff --git a/src/fw/services/bluetooth/Kconfig b/src/fw/services/bluetooth/Kconfig new file mode 100644 index 0000000000..2f27c7bf81 --- /dev/null +++ b/src/fw/services/bluetooth/Kconfig @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_BLUETOOTH + bool "Bluetooth" + help + Core Bluetooth service (pairing, bonding, local id, ...). + +config BLE_GATT_NOTIF_WRITE_TIMEOUT_MS + int "GATT notification handoff timeout (ms)" + default 5 if SOC_SF32LB52 + default 100 + help + Max time the BT host task blocks for the client to drain the per-client + GATT subscription buffer before dropping a notification. Must stay below + the HCI transport's tolerance for a stalled host. + +config BLE_GATT_SUBSCRIPTION_DEPTH + int "GATT subscription buffer depth (notifications)" + default 8 if SOC_SF32LB52 + default 4 + help + Per-client GATT subscription buffer depth, in max-size notifications. + Deeper absorbs notification bursts (fewer drops) at the cost of RAM. + +if SERVICE_BLUETOOTH + +module = SERVICE_BLUETOOTH +module-str = Bluetooth +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/bluetooth/ble_bas.c b/src/fw/services/bluetooth/ble_bas.c new file mode 100644 index 0000000000..29627142ff --- /dev/null +++ b/src/fw/services/bluetooth/ble_bas.c @@ -0,0 +1,50 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/event_service_client.h" +#include "bluetooth/bas.h" +#include "kernel/event_loop.h" +#include "kernel/pebble_tasks.h" +#include "syscall/syscall.h" + +static EventServiceInfo s_bas_evt; + +static void prv_execute_on_kernel_main(CallbackEventCallback cb) { + if (pebble_task_get_current() != PebbleTask_KernelMain) { + launcher_task_add_callback(cb, NULL); + } else { + cb(NULL); + } +} + +static void prv_ble_bas_handle_event(PebbleEvent *e, void *context) { + const PebbleBatteryStateChangeEvent *const battery_state_event = &e->battery_state; + + bt_driver_bas_handle_update(battery_state_event->new_state.pct); +} + +static void prv_start_ble_bas_kernel_main(void *unused) { + BatteryChargeState battery_state; + + battery_state = sys_battery_get_charge_state(); + bt_driver_bas_handle_update(battery_state.charge_percent); + + s_bas_evt = (EventServiceInfo) { + .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, + .handler = prv_ble_bas_handle_event, + }; + + event_service_client_subscribe(&s_bas_evt); +} + +static void prv_stop_ble_bas_kernel_main(void *unused) { + event_service_client_unsubscribe(&s_bas_evt); +} + +void ble_bas_init(void) { + prv_execute_on_kernel_main(prv_start_ble_bas_kernel_main); +} + +void ble_bas_deinit(void) { + prv_execute_on_kernel_main(prv_stop_ble_bas_kernel_main); +} diff --git a/src/fw/services/normal/bluetooth/ble_hrm.c b/src/fw/services/bluetooth/ble_hrm.c similarity index 86% rename from src/fw/services/normal/bluetooth/ble_hrm.c rename to src/fw/services/bluetooth/ble_hrm.c index 2f0dbcda56..5da50f4552 100644 --- a/src/fw/services/normal/bluetooth/ble_hrm.c +++ b/src/fw/services/bluetooth/ble_hrm.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ble_hrm.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/ble_hrm.h" #include "applib/event_service_client.h" #include "comm/ble/gap_le_connection.h" @@ -26,11 +13,10 @@ #include "popups/ble_hrm/ble_hrm_reminder_popup.h" #include "popups/ble_hrm/ble_hrm_sharing_popup.h" #include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/hrm/hrm_manager_private.h" -#include "services/common/regular_timer.h" -#include "services/normal/activity/activity.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/hrm/hrm_manager_private.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/activity/activity.h" #include "shell/system_app_ids.auto.h" #include "system/logging.h" #include "system/passert.h" @@ -41,7 +27,9 @@ #include #include -#if CAPABILITY_HAS_BUILTIN_HRM +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +#ifdef CONFIG_HRM #define BLE_HRM_UPDATE_INTERVAL_SEC (1) @@ -130,7 +118,7 @@ void ble_hrm_handle_activity_prefs_heart_rate_is_enabled(bool is_enabled) { if (!prv_hw_and_sw_supports_hrm()) { return; } - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing prefs updated: is_enabled=%u", is_enabled); + PBL_LOG_INFO("BLE HRM sharing prefs updated: is_enabled=%u", is_enabled); if (!is_enabled) { prv_reset_subscriptions(); @@ -196,7 +184,7 @@ static void prv_ble_hrm_handle_hrm_data(PebbleEvent *e, void *context) { } const BleHrmServiceMeasurement measurement = { .bpm = hrm_event->bpm.bpm, - .is_on_wrist = (hrm_event->bpm.quality > HRMQuality_NoSignal), + .is_on_wrist = (hrm_event->bpm.quality >= HRMQuality_Worst), }; BTDeviceInternal sharing_to_devices[4]; @@ -206,7 +194,7 @@ static void prv_ble_hrm_handle_hrm_data(PebbleEvent *e, void *context) { } static void prv_start_hrm_kernel_main(void *unused) { - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing started"); + PBL_LOG_INFO("BLE HRM sharing started"); s_ble_hrm_session.service_info = (EventServiceInfo) { .type = PEBBLE_HRM_EVENT, .handler = prv_ble_hrm_handle_hrm_data, @@ -216,15 +204,13 @@ static void prv_start_hrm_kernel_main(void *unused) { hrm_manager_subscribe_with_callback(INSTALL_ID_INVALID, 1 /*update_interval_s*/, 0 /*expire_s*/, HRMFeature_BPM, NULL, NULL); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BLE_HRM_SHARING_TIME, AnalyticsClient_System); } static void prv_stop_hrm_kernel_main(void *unused) { - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing stopped"); + PBL_LOG_INFO("BLE HRM sharing stopped"); sys_hrm_manager_unsubscribe(s_ble_hrm_session.manager_session); event_service_client_unsubscribe(&s_ble_hrm_session.service_info); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_HRM_SHARING_TIME); } static void prv_execute_on_kernel_main(CallbackEventCallback cb) { @@ -240,7 +226,7 @@ static void prv_push_sharing_request_window_kernel_main_cb(void *ctx) { } static void prv_request_sharing_permission(GAPLEConnection *const connection) { - PBL_LOG(LOG_LEVEL_INFO, "Requesting BLE HRM sharing permission"); + PBL_LOG_INFO("Requesting BLE HRM sharing permission"); BLEHRMSharingRequest *const sharing_request = kernel_zalloc_check(sizeof(*sharing_request)); sharing_request->connection = connection; launcher_task_add_callback(prv_push_sharing_request_window_kernel_main_cb, sharing_request); @@ -275,8 +261,7 @@ static void prv_push_reminder_popup_kernel_main_cb(void *unused) { ble_hrm_push_reminder_popup(); - analytics_event_ble_hrm(BleHrmEventSubtype_SharingTimeoutPopupPresented); - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing timeout fired!"); + PBL_LOG_INFO("BLE HRM sharing timeout fired!"); } //! @note executes on timer task @@ -345,7 +330,7 @@ static void prv_disconnect_to_kill_subscription(GAPLEConnection *connection) { } void ble_hrm_revoke_sharing_permission_for_connection(GAPLEConnection *connection) { - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing: revoked for conn %p", connection); + PBL_LOG_INFO("BLE HRM sharing: revoked for conn %p", connection); bt_lock(); if (gap_le_connection_is_valid(connection)) { prv_update_permission(connection, HrmSharingPermission_Declined); @@ -353,7 +338,6 @@ void ble_hrm_revoke_sharing_permission_for_connection(GAPLEConnection *connectio } bt_unlock(); - analytics_event_ble_hrm(BleHrmEventSubtype_SharingRevoked); } static void prv_revoke_gap_le_connection_for_each_cb(GAPLEConnection *connection, void *unused) { @@ -367,8 +351,7 @@ void ble_hrm_revoke_all(void) { bt_unlock(); // Counting as one -- it's one user action. - analytics_event_ble_hrm(BleHrmEventSubtype_SharingRevoked); - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing: all revoked"); + PBL_LOG_INFO("BLE HRM sharing: all revoked"); } static void prv_update_subscription(GAPLEConnection *connection, bool is_subscribed) { @@ -376,7 +359,7 @@ static void prv_update_subscription(GAPLEConnection *connection, bool is_subscri if (connection->hrm_service_is_subscribed == is_subscribed) { return; } - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing: conn <%p> is_subscribed=%u", connection, is_subscribed); + PBL_LOG_INFO("BLE HRM sharing: conn <%p> is_subscribed=%u", connection, is_subscribed); const bool prev_is_sharing = prv_is_sharing(connection); connection->hrm_service_is_subscribed = is_subscribed; @@ -415,7 +398,7 @@ static void prv_reset_subscriptions(void) { void ble_hrm_handle_sharing_request_response(bool is_granted, BLEHRMSharingRequest *sharing_request) { - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing permission is_granted=%u", is_granted); + PBL_LOG_INFO("BLE HRM sharing permission is_granted=%u", is_granted); bt_lock(); GAPLEConnection *connection = sharing_request->connection; @@ -428,8 +411,6 @@ void ble_hrm_handle_sharing_request_response(bool is_granted, kernel_free(sharing_request); - analytics_event_ble_hrm(is_granted ? - BleHrmEventSubtype_SharingAccepted : BleHrmEventSubtype_SharingDeclined); } void bt_driver_cb_hrm_service_update_subscription(const BTDeviceInternal *device, @@ -440,7 +421,7 @@ void bt_driver_cb_hrm_service_update_subscription(const BTDeviceInternal *device } GAPLEConnection *connection = gap_le_connection_by_device(device); if (!connection) { - PBL_LOG(LOG_LEVEL_ERROR, "Subscription update but no connection?"); + PBL_LOG_ERR("Subscription update but no connection?"); goto unlock; } prv_update_subscription(connection, is_subscribed); diff --git a/src/fw/services/bluetooth/bluetooth_ctl.c b/src/fw/services/bluetooth/bluetooth_ctl.c new file mode 100644 index 0000000000..79ab5042e8 --- /dev/null +++ b/src/fw/services/bluetooth/bluetooth_ctl.c @@ -0,0 +1,273 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/bluetooth_ctl.h" + +#include +#include + +#include "comm/ble/gap_le.h" +#include "comm/ble/gatt_client_subscriptions.h" +#include "console/dbgserial.h" +#include "drivers/clocksource.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/stop.h" +#include "os/mutex.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/ble_bas.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/dis.h" +#include "pbl/services/bluetooth/local_addr.h" +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/bluetooth/ble_hrm.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DEFINE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +static bool s_comm_initialized = false; +static bool s_comm_airplane_mode_on = false; +static bool s_comm_enabled = false; +static bool s_comm_is_running = false; +static bool s_comm_state_change_eval_is_scheduled; +static BtCtlModeOverride s_comm_override = BtCtlModeOverrideNone; +static PebbleMutex *s_comm_state_change_mutex; + +bool bt_ctl_is_airplane_mode_on(void) { return s_comm_airplane_mode_on; } + +bool bt_ctl_is_bluetooth_active(void) { + if (s_comm_enabled) { + if (s_comm_override == BtCtlModeOverrideRun) { + return true; + } else if (s_comm_override == BtCtlModeOverrideNone && !s_comm_airplane_mode_on) { + return true; + } + } + return false; +} + +bool bt_ctl_is_bluetooth_running(void) { return s_comm_is_running; } + +static void prv_put_disconnection_event(void) { + PebbleEvent event = (PebbleEvent){.type = PEBBLE_BT_CONNECTION_EVENT, + .bluetooth.connection = { + .is_ble = true, + .state = PebbleBluetoothConnectionEventStateDisconnected, + }}; + PBL_LOG_DBG("New BT Conn change event, We are now disconnected"); + event_put(&event); +} + +static void prv_comm_start(void) { + if (s_comm_is_running) { + return; + } + stop_mode_disable(InhibitorCommMode); + // Heap allocated to reduce stack usage + BTDriverConfig *config = kernel_zalloc_check(sizeof(BTDriverConfig)); + dis_get_info(&config->dis_info); +#if defined(CONFIG_HRM) && !defined(CONFIG_RECOVERY_FW) + config->is_hrm_supported_and_enabled = ble_hrm_is_supported_and_enabled(); + PBL_LOG_INFO("BLE HRM sharing prefs: is_enabled=%u", + config->is_hrm_supported_and_enabled); +#endif + // Register existing bondings before bringing the connection up: NimBLE + // restores them before the link is established. The other backends use + // no-op bonding handlers, so doing it early is harmless for them too. + bt_persistent_storage_register_existing_ble_bondings(); + + s_comm_is_running = bt_driver_start(config); + kernel_free(config); + + if (s_comm_is_running) { + bt_local_addr_init(); + gap_le_init(); + bt_local_id_configure_driver(); +#if defined(CONFIG_HRM) && !defined(CONFIG_RECOVERY_FW) + ble_hrm_init(); +#endif + ble_bas_init(); + bt_pairability_init(); + } else { + PBL_LOG_ERR("BT driver failed to start!"); + // FIXME: PBL-36163 -- handle this better + } + + stop_mode_enable(InhibitorCommMode); +} + +static void prv_comm_stop(void) { + if (!s_comm_is_running) { + return; + } + stop_mode_disable(InhibitorCommMode); + ble_bas_deinit(); +#if defined(CONFIG_HRM) && !defined(CONFIG_RECOVERY_FW) + ble_hrm_deinit(); +#endif + gap_le_deinit(); + + // Should be the last thing to happen that touches the Bluetooth controller directly + bt_driver_stop(); + stop_mode_enable(InhibitorCommMode); + s_comm_is_running = false; + + // This is a legacy event used to update the Settings app. + prv_put_disconnection_event(); +} + +static void prv_send_state_change_event(void) { + PBL_LOG_DBG("----> Sending a BT state event"); + PebbleEvent event = { + .type = PEBBLE_BT_STATE_EVENT, + .bluetooth = + { + .state = + { + .airplane = s_comm_airplane_mode_on, + .enabled = s_comm_enabled, + .override = s_comm_override, + }, + }, + }; + event_put(&event); + if (s_comm_airplane_mode_on) { + PBL_ANALYTICS_TIMER_STOP(connectivity_connected_time_ms); + PBL_ANALYTICS_TIMER_STOP(connectivity_expected_time_ms); + } else { + PBL_ANALYTICS_TIMER_START(connectivity_expected_time_ms); + } +} + +static void prv_comm_state_change(void *context) { + static bool s_first_run = true; + mutex_lock(s_comm_state_change_mutex); + s_comm_state_change_eval_is_scheduled = false; + bool is_active_mode = bt_ctl_is_bluetooth_active(); + if (is_active_mode != s_comm_is_running) { + if (is_active_mode) { + prv_comm_start(); + } else { + prv_comm_stop(); + } + // Only send event if state changed successfully: + if (is_active_mode == s_comm_is_running) { + prv_send_state_change_event(); + } + } else if (!s_comm_is_running && s_first_run) { + PBL_LOG_DBG("Shutting down the BT stack on boot"); + bt_driver_power_down_controller_on_boot(); + } + + s_first_run = false; + mutex_unlock(s_comm_state_change_mutex); +} + +void bt_ctl_set_enabled(bool enabled) { + if (!s_comm_initialized) { + PBL_LOG_ERR("Error: Bluetooth isn't initialized yet"); + return; + } + mutex_lock(s_comm_state_change_mutex); + s_comm_enabled = enabled; + mutex_unlock(s_comm_state_change_mutex); + prv_comm_state_change(NULL); +} + +void bt_ctl_set_override_mode(BtCtlModeOverride override) { + if (!s_comm_initialized) { + PBL_LOG_ERR("Error: Bluetooth isn't initialized yet"); + return; + } + mutex_lock(s_comm_state_change_mutex); + s_comm_override = override; + mutex_unlock(s_comm_state_change_mutex); + prv_comm_state_change(NULL); +} + +static void prv_track_quick_airplane_mode_toggles(bool is_airplane_mode_currently_on) { + // Track when coming out of airplane mode and we've gone into airplane mode less than 30 secs ago: + static RtcTicks s_airplane_mode_last_toggle_ticks; + const RtcTicks now_ticks = rtc_get_ticks(); + const uint64_t max_interval_secs = 30; + if (((now_ticks - s_airplane_mode_last_toggle_ticks) < (max_interval_secs * RTC_TICKS_HZ)) && + is_airplane_mode_currently_on) { + PBL_LOG_INFO("Quick airplane mode toggle detected!"); + } + s_airplane_mode_last_toggle_ticks = now_ticks; +} + +void bt_ctl_set_airplane_mode_async(bool enabled) { + if (!s_comm_initialized) { + PBL_LOG_ERR("Error: Bluetooth isn't initialized yet"); + return; + } + mutex_lock(s_comm_state_change_mutex); + prv_track_quick_airplane_mode_toggles(!enabled); + bt_persistent_storage_set_airplane_mode_enabled(enabled); + s_comm_airplane_mode_on = enabled; + bool should_schedule_eval = false; + if (!s_comm_state_change_eval_is_scheduled) { + should_schedule_eval = true; + s_comm_state_change_eval_is_scheduled = true; + } + mutex_unlock(s_comm_state_change_mutex); + if (should_schedule_eval) { + system_task_add_callback(prv_comm_state_change, NULL); + } +} + +void bt_ctl_init(void) { + s_comm_state_change_mutex = mutex_create(); + + s_comm_airplane_mode_on = bt_persistent_storage_get_airplane_mode_enabled(); + s_comm_initialized = true; + + gatt_client_subscription_boot(); +} + +static void prv_bt_ctl_reset_bluetooth_callback(void *context) { + PBL_LOG_DBG("Resetting Bluetooth"); + mutex_lock(s_comm_state_change_mutex); + + bool was_already_running = s_comm_is_running; + + prv_comm_stop(); + prv_comm_start(); + + // It's possible a reset was triggered because the stack failed to boot up + // correctly in which case we have never generated an event about the stack + // booting up. Don't bother sending events if we are just returning the stack + // to the state it is already in + if (!was_already_running && s_comm_is_running) { + prv_send_state_change_event(); + } + mutex_unlock(s_comm_state_change_mutex); +} + +void bt_ctl_reset_bluetooth(void) { + if (bt_ctl_is_bluetooth_active()) { + system_task_add_callback(prv_bt_ctl_reset_bluetooth_callback, NULL); + } else { + PBL_LOG_DBG("Bluetooth is disabled, reset aborted"); + } +} + +void command_bt_airplane_mode(const char *new_mode) { + // as tests run using command_bt_airplane_mode, will retain nomenclature + // but work as override mode change + BtCtlModeOverride override = BtCtlModeOverrideStop; + if (strcmp(new_mode, "exit") == 0) { + override = BtCtlModeOverrideNone; + } + bt_ctl_set_override_mode(override); + bool new_state = bt_ctl_is_bluetooth_active(); + if (!new_state) { + dbgserial_putstr("Entered airplane mode"); + } else { + dbgserial_putstr("Left airplane mode"); + } +} diff --git a/src/fw/services/common/bluetooth/bluetooth_persistent_storage_debug.c b/src/fw/services/bluetooth/bluetooth_persistent_storage_debug.c similarity index 81% rename from src/fw/services/common/bluetooth/bluetooth_persistent_storage_debug.c rename to src/fw/services/bluetooth/bluetooth_persistent_storage_debug.c index 133c936b35..df1456cf54 100644 --- a/src/fw/services/common/bluetooth/bluetooth_persistent_storage_debug.c +++ b/src/fw/services/bluetooth/bluetooth_persistent_storage_debug.c @@ -1,29 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bluetooth_persistent_storage_debug.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/bluetooth_persistent_storage_debug.h" #include "console/prompt.h" -#include "services/common/shared_prf_storage/shared_prf_storage_debug.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage_debug.h" #include "system/hexdump.h" #include "system/logging.h" #include "util/string.h" #include -#include #include #include #include @@ -105,7 +91,7 @@ void bluetooth_persistent_storage_debug_dump_root_keys(SM128BitKey *irk, SM128Bi extern void bluetooth_persistent_storage_dump_contents(void); void command_gapdb_dump(void) { -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) bluetooth_persistent_storage_dump_contents(); #endif shared_prf_storage_dump_contents(); diff --git a/src/fw/services/bluetooth/bluetooth_persistent_storage_normal.c b/src/fw/services/bluetooth/bluetooth_persistent_storage_normal.c new file mode 100644 index 0000000000..bc60f517d0 --- /dev/null +++ b/src/fw/services/bluetooth/bluetooth_persistent_storage_normal.c @@ -0,0 +1,1660 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage_debug.h" + +#include "comm/ble/gap_le_connect.h" +#include "comm/ble/gap_le_connection.h" +#include "comm/ble/gap_le_slave_reconnect.h" +#include "comm/ble/kernel_le_client/kernel_le_client.h" +#include "comm/bt_lock.h" +#include "console/prompt.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "os/mutex.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/bluetooth/local_addr.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/system_task.h" +#include "pbl/services/settings/settings_file.h" +#include "system/hexdump.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/math.h" +#include "util/string.h" + +#include +#include +#include + +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +#ifdef UNITTEST +// Let the unittest define this using a header override: +# include "pbl/services/bluetooth/bluetooth_persistent_storage_unittest_impl.h" +#else +# include "pbl/services/bluetooth/bluetooth_persistent_storage_v2_impl.h" +#endif + +//! The BtPersistBonding*Data structs can never shrink, only grow + +//! Stores data about a remote BT classic device +typedef struct PACKED { + BTDeviceAddress addr; + SM128BitKey link_key; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + // These are the lowest bits of Remote.platform_bitfield_cache, which contain the OS type + uint8_t platform_bits; +} BtPersistBondingBTClassicData; + +//! Stores data about a remote BLE device +typedef struct PACKED { + bool supports_ancs:1; + bool is_gateway:1; + bool requires_address_pinning:1; + uint8_t flags:5; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + BtPersistLEPairingInfo pairing_info; +} BtPersistBondingBLEData; + +typedef struct PACKED { + BtPersistBondingType type:8; + + union PACKED { + BtPersistBondingBTClassicData bt_classic_data; + BtPersistBondingBLEData ble_data; + }; +} BtPersistBondingData; + +typedef struct PACKED { + BTDeviceInternal peer; + uint16_t chr_val_handle; + uint16_t flags; + unsigned value_changed:1; +} BtPersistCCCDData; + +typedef struct PACKED { + BTCCCDID id; + BtPersistCCCDData data; +} BtPersistCCCD; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Settings File + +#define BT_PERSISTENT_STORAGE_FILE_NAME "gap_bonding_db" +#define BT_PERSISTENT_STORAGE_FILE_SIZE (4096) + +//! All of the actual pairings use a BTBondingID as a key. This is because with BLE pairings an +//! address is not alwaywas available, and it made it easier to have BT Classic and BLE pairings +//! use the same type of key. When adding pairings there is no BTBondingID so a free key has to +//! be found by iterating over all possible keys. + +//! All of the local device attributes can be accessed directly with the following keys: + +//! This key is used to access the BTBondingID of the current active gateway +static const char ACTIVE_GATEWAY_KEY[] = "ACTIVE_GATEWAY"; +//! This key is used to access a bool which stores if we have recently changed active gateways +static const char IS_UNFAITHFUL_KEY[] = "IS_UNFAITHFUL"; +//! This key is used to access an array of two SM128BitKey values +static const char ROOT_KEYS_KEY[] = "ROOT_KEYS"; +//! This key is used to access a char array which holds the device name +static const char DEVICE_NAME_KEY[] = "DEVICE_NAME"; +//! This key is used to access a bool which stores the current airplane mode state +static const char AIRPLANE_MODE_KEY[] = "AIRPLANE_MODE"; +//! This key is used to access a uint64_t which stores the most recent system session capabilities +static const char SYSTEM_CAPABILITIES_KEY[] = "SYSTEM_CAPABILITIES"; +//! This key is used to access the BLE address that can be used for address pinning. +static const char BLE_PINNED_ADDRESS_KEY[] = "BLE_PINNED_ADDRESS"; + +static uint8_t s_bt_persistent_storage_updates = 0; + +static PebbleMutex *s_db_mutex = NULL; + +//! Cache of the last connected system session capabilities. Updated in flash when we get new flags +//! @note prv_lock() must be held when accessing this variable. +static PebbleProtocolCapabilities s_cached_system_capabilities; + +static void prv_lock(void) { + mutex_lock(s_db_mutex); +} + +static void prv_unlock(void) { + mutex_unlock(s_db_mutex); +} + +static bool prv_bt_persistent_storage_get_ble_smpairinginfo_by_id( + BTBondingID bonding, SMPairingInfo *info_out, char *name_out, bool *requires_address_pinning, + uint8_t *flags); + +static void prv_update_bondings(BTBondingID id, BtPersistBondingType type) { + if (id == BT_BONDING_ID_INVALID) { + return; + } + + if (type == BtPersistBondingTypeBLE) { + SMPairingInfo pairing_info; + char ble_name[BT_DEVICE_NAME_BUFFER_SIZE] = { }; + bool requires_address_pinning = false; + uint8_t flags = 0; + if (prv_bt_persistent_storage_get_ble_smpairinginfo_by_id( + id, &pairing_info, ble_name, &requires_address_pinning, + &flags)) { + // only send the ble_name if we have a name to send! + char *ble_name_ptr = (strlen(ble_name) == 0) ? NULL : &ble_name[0]; + shared_prf_storage_store_ble_pairing_data( + &pairing_info, ble_name_ptr, requires_address_pinning, flags); + } + } +} + +//! Returns the size of the data read. If the buffer provided is too small then 0 is returned +static int prv_file_get(const void *key, size_t key_len, void *data_out, size_t buf_len) { + unsigned int data_len = 0; + prv_lock(); + { + SettingsFile fd; + status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, + BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv != S_SUCCESS) { + goto cleanup; + } + + data_len = settings_file_get_len(&fd, key, key_len); + // If a big enough buffer wasn't passed in, then the data can't be read. + if (data_len > buf_len || + settings_file_get(&fd, key, key_len, data_out, buf_len) != S_SUCCESS) { + data_len = 0; + } + + settings_file_close(&fd); + } +cleanup: + prv_unlock(); + return data_len; +} + +//! @return the value that was read at that key or `default_value` if the key does not exist, +//! or if the stored data has been corrupted. +static bool prv_file_get_bool(const void *key, size_t key_len, bool default_value) { + uint8_t bool_data; + int read_size = prv_file_get(key, key_len, (void*)&bool_data, sizeof(bool_data)); + if (!read_size || + ((bool_data != (uint8_t)true) && (bool_data != (uint8_t)false))) { + return default_value; + } + // Default to false in the case of data corruption (anything other than 0x1 or 0x0). + return bool_data; +} + +//! Returns true if the set was successful +typedef enum { + GapBondingFileSetFail = 0, + GapBondingFileSetUpdated, + GapBondingFileSetNoUpdateNeeded, +} GapBondingFileSetStatus; + +static GapBondingFileSetStatus prv_file_set( + const void *key, size_t key_len, const void *data_in, size_t data_len) { + status_t rv; + bool do_perform_update = true; + prv_lock(); + { + SettingsFile fd; + rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv != S_SUCCESS) { + goto cleanup; + } + + // Only store data if data_in is a valid pointer, otherwise, clear the entry + if (data_in) { + if (settings_file_get_len(&fd, key, key_len) == (int)data_len) { + uint8_t curr_val[data_len]; + + settings_file_get(&fd, key, key_len, &curr_val[0], data_len); + + // Don't bother rewriting the exact same info. Pairing info is precious, + // we want to minimize cases where we could mess it up + if (memcmp(&curr_val, data_in, data_len) == 0) { + do_perform_update = false; + } + } + + if (do_perform_update) { + s_bt_persistent_storage_updates++; + PBL_LOG_D_DBG(LOG_DOMAIN_BT_PAIRING_INFO, "Updating GAP Bonding DB Value !"); + PBL_HEXDUMP_D(LOG_DOMAIN_BT_PAIRING_INFO, LOG_LEVEL_DEBUG, (uint8_t *)key, key_len); + PBL_HEXDUMP_D(LOG_DOMAIN_BT_PAIRING_INFO, LOG_LEVEL_DEBUG, (uint8_t *)data_in, data_len); + rv = settings_file_set(&fd, key, key_len, (uint8_t*) data_in, data_len); + } + } else { + rv = settings_file_delete(&fd, key, key_len); + } + settings_file_close(&fd); + } +cleanup: + prv_unlock(); + if (rv != S_SUCCESS) { + PBL_LOG_ERR("Failed to update gap bonding db, rv = %"PRId32, rv); + return GapBondingFileSetFail; + } + + return (do_perform_update ? GapBondingFileSetUpdated : GapBondingFileSetNoUpdateNeeded); +} + +//! Returns true if things were successful +static bool prv_file_each(SettingsFileEachCallback itr_cb, void *itr_data) { + status_t rv; + prv_lock(); + { + SettingsFile fd; + rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv) { + goto cleanup; + } + + settings_file_each(&fd, itr_cb, itr_data); + settings_file_close(&fd); + } +cleanup: + prv_unlock(); + return (rv == S_SUCCESS); +} + + +//! Get the next available BondingID +//! This function re-uses bonding ids as they are freed. This could be a problem with 3rd party +//! apps. https://pebbletechnology.atlassian.net/browse/PBL-8391 +static BTBondingID prv_get_free_key() { + BTBondingID free_key = BT_BONDING_ID_INVALID; + + prv_lock(); + { + SettingsFile fd; + status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, + BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv) { + goto cleanup; + } + + for (BTBondingID id = 0; id < BT_BONDING_ID_INVALID; id++) { + if (!settings_file_exists(&fd, &id, sizeof(id))) { + free_key = id; + break; + } + } + + settings_file_close(&fd); + + } +cleanup: + prv_unlock(); + return free_key; +} + +//! Get the next available CCCDID +static BTCCCDID prv_get_free_cccd() { + BTCCCDID free_cccd = BT_CCCD_ID_INVALID; + + prv_lock(); + { + SettingsFile fd; + status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, + BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv) { + goto cleanup; + } + + for (BTCCCDID id = 0U; id < BT_CCCD_ID_INVALID; id++) { + if (!settings_file_exists(&fd, &id, sizeof(id))) { + free_cccd = id; + break; + } + } + + settings_file_close(&fd); + + } +cleanup: + prv_unlock(); + return free_cccd; +} + +static bool prv_any_pinned_ble_pairings_itr(SettingsFile *file, + SettingsRecordInfo *info, + void *context) { + if (info->key_len != sizeof(BTBondingID)) { + return true; + } + if (info->val_len == 0) { + return true; + } + + BTBondingID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistBondingData data; + info->get_val(file, &data, sizeof(data)); + if (data.ble_data.requires_address_pinning) { + bool *has_pinned_ble_pairings = context; + *has_pinned_ble_pairings = true; + return false; + } + + return true; +} + +bool bt_persistent_storage_has_pinned_ble_pairings(void) { + bool has_pinned_ble_pairings = false; + prv_file_each(prv_any_pinned_ble_pairings_itr, &has_pinned_ble_pairings); + return has_pinned_ble_pairings; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Shared PRF Storage + +static void prv_load_pinned_address_from_prf(void) { + BTDeviceAddress pinned_address; + + if (shared_prf_storage_get_ble_pinned_address(&pinned_address)) { + bt_persistent_storage_set_ble_pinned_address(&pinned_address); + } + + // if we get here there is no pinned address in PRF, let's load the address fw has been + // using. This shouldn't ever really happen unless we reboot while saving new information to + // shared PRF + if (bt_persistent_storage_get_ble_pinned_address(&pinned_address)) { + shared_prf_storage_set_ble_pinned_address(&pinned_address); + } +} + +static void prv_load_local_data_from_prf(void) { + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + if (shared_prf_storage_get_local_device_name(name, BT_DEVICE_NAME_BUFFER_SIZE)) { + bt_persistent_storage_set_local_device_name(name, BT_DEVICE_NAME_BUFFER_SIZE); + } + + SM128BitKey keys[SMRootKeyTypeNum]; + if (shared_prf_storage_get_root_key(SMRootKeyTypeEncryption, &keys[SMRootKeyTypeEncryption]) && + shared_prf_storage_get_root_key(SMRootKeyTypeIdentity, &keys[SMRootKeyTypeIdentity])) { +#if !defined(CONFIG_RELEASE) + PBL_LOG_INFO("Loading Root Keys from PRF storage:"); + PBL_HEXDUMP(LOG_LEVEL_INFO, (const uint8_t *) keys, sizeof(keys)); +#endif + bt_persistent_storage_set_root_keys(keys); + return; + } + + // if we get here there are no root keys in prf storage, let's load the root + // keys normal fw has been using. This shouldn't ever really happen unless we + // reboot while saving new information to shared PRF + if (bt_persistent_storage_get_root_key(SMRootKeyTypeEncryption, &keys[SMRootKeyTypeEncryption]) && + bt_persistent_storage_get_root_key(SMRootKeyTypeIdentity, &keys[SMRootKeyTypeIdentity])) { + PBL_LOG_ERR("Storing Root Keys to PRF storage"); + shared_prf_storage_set_root_keys(keys); + } +} + +static void prv_push_ble_persist_to_shared_prf(void) { + BTBondingID bonding_id = bt_persistent_storage_get_ble_ancs_bonding(); + + if (bonding_id != BT_BONDING_ID_INVALID) { + prv_update_bondings(bonding_id, BtPersistBondingTypeBLE); + } +} + +static void prv_load_ble_pairing_from_prf(void) { + SMPairingInfo prf_pairing_info; + char device_name[BT_DEVICE_NAME_BUFFER_SIZE]; + bool requires_address_pinning; + uint8_t flags; + if (!shared_prf_storage_get_ble_pairing_data(&prf_pairing_info, device_name, + &requires_address_pinning, + &flags)) { + // No pairing available, check to see if we have a pairing in the gapDB + prv_push_ble_persist_to_shared_prf(); + return; + } + + // PRF pairing storage has only one pairing slot. Assume is_gateway: + bt_persistent_storage_store_ble_pairing(&prf_pairing_info, true /* is_gateway */, device_name, + requires_address_pinning, flags); +} + +static void prv_load_data_from_prf(void) { + prv_load_local_data_from_prf(); + prv_load_pinned_address_from_prf(); + prv_load_ble_pairing_from_prf(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Common Helper Functions + +BtPersistBondingType prv_get_type_for_id(BTBondingID id) { + BtPersistBondingData data; + prv_file_get(&id, sizeof(id), &data, sizeof(data)); + + return data.type; +} + +bool prv_delete_pairing_with_type_by_id(BTBondingID bonding, BtPersistBondingType type, + BtPersistBondingData *data_out) { + if (!prv_file_get(&bonding, sizeof(bonding), data_out, sizeof(*data_out))) { + return false; + } + + if (data_out->type != type) { + PBL_LOG_ERR("Type mismatch: not deleting pairing. Is the bonding db corrupted?"); + return false; + } + + if (prv_file_set(&bonding, sizeof(bonding), NULL, 0) == GapBondingFileSetFail) { + return false; + } + + return true; +} + +bool prv_has_active_gateway_by_type(BtPersistBondingType desired_type) { + BTBondingID bonding; + BtPersistBondingType type; + + if (!bt_persistent_storage_get_active_gateway(&bonding, &type)) { + return false; + } + + if (bonding == BT_BONDING_ID_INVALID || type != desired_type) { + return false; + } + + return true; +} + +static void prv_update_active_gateway_if_needed(BTBondingID bonding, BtPersistBondingOp op) { + // Invalidate the active gateway if it is getting deleted + if (op == BtPersistBondingOpWillDelete) { + BTBondingID current_active_gateway; + bt_persistent_storage_get_active_gateway(¤t_active_gateway, NULL); + if (current_active_gateway == bonding) { + bt_persistent_storage_set_active_gateway(BT_BONDING_ID_INVALID); + } + } +} + +static void prv_call_common_bonding_change_handlers(BTBondingID bonding, BtPersistBondingOp op) { + bt_pairability_update_due_to_bonding_change(); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! BLE Pairing Info +void gap_le_connection_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op); +typedef struct { + BTBondingID bonding; + BtPersistBondingOp op; +} BleBondingChangeContext; + +static void prv_call_ble_bonding_change_handlers_impl(BTBondingID bonding, + BtPersistBondingOp op) { + prv_update_active_gateway_if_needed(bonding, op); + + if (!bt_ctl_is_bluetooth_running()) { + return; + } + bt_local_addr_handle_bonding_change(bonding, op); + gap_le_connection_handle_bonding_change(bonding, op); + gap_le_connect_handle_bonding_change(bonding, op); + kernel_le_client_handle_bonding_change(bonding, op); + prv_call_common_bonding_change_handlers(bonding, op); +} + +static void prv_call_ble_bonding_change_handlers_cb(void *data) { + BleBondingChangeContext *context = data; + prv_call_ble_bonding_change_handlers_impl(context->bonding, context->op); + kernel_free(context); +} + +static void prv_call_ble_bonding_change_handlers(BTBondingID bonding, + BtPersistBondingOp op) { + if (launcher_task_is_current_task()) { + prv_call_ble_bonding_change_handlers_impl(bonding, op); + return; + } + + BleBondingChangeContext *context = kernel_malloc_check(sizeof(*context)); + + *context = (BleBondingChangeContext) { + .bonding = bonding, + .op = op, + }; + launcher_task_add_callback(prv_call_ble_bonding_change_handlers_cb, context); +} + +typedef struct { + SMPairingInfo pairing_info; + BTBondingID key_out; +} KeyForSMPairingItrData; + +static bool prv_is_pairing_info_equal_identity(const BtPersistLEPairingInfo *a, + const SMPairingInfo *b) { + return (a->is_remote_identity_info_valid && + b->is_remote_identity_info_valid && + bt_device_equal(&a->identity.opaque, &b->identity.opaque) && + memcmp(&a->irk, &b->irk, sizeof(SMIdentityResolvingKey)) == 0); +} + +static bool prv_get_key_for_sm_pairing_info_itr(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // check entry is valid + if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { + return true; // continue iterating + } + + KeyForSMPairingItrData *itr_data = (KeyForSMPairingItrData*) context; + + BTBondingID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistBondingData stored_data; + info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + + if (stored_data.type == BtPersistBondingTypeBLE && + prv_is_pairing_info_equal_identity(&stored_data.ble_data.pairing_info, + &itr_data->pairing_info)) { + itr_data->key_out = key; + return false; // stop iterating + } + + return true; +} + +static BTBondingID prv_get_key_for_sm_pairing_info(const SMPairingInfo *pairing_info) { + KeyForSMPairingItrData itr_data = { + .pairing_info = *pairing_info, + .key_out = BT_BONDING_ID_INVALID, + }; + prv_file_each(prv_get_key_for_sm_pairing_info_itr, &itr_data); + + return itr_data.key_out; +} + +static bool prv_delete_ble_pairing_by_id(BTBondingID bonding); + +//! Only a single BLE pairing is supported at a time. The buffer below is sized generously to absorb +//! any legacy state where the bonding DB ended up with multiple entries (e.g. after a PRF pairing +//! was merged on top of an existing normal-FW pairing). +#define BT_BONDING_PRUNE_MAX 8 + +typedef struct { + BTBondingID keep_id; + BTBondingID ids[BT_BONDING_PRUNE_MAX]; + uint8_t count; +} CollectOtherBleItrData; + +static bool prv_collect_other_ble_bondings_itr(SettingsFile *file, SettingsRecordInfo *info, + void *context) { + if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { + return true; + } + + CollectOtherBleItrData *itr_data = context; + + BTBondingID key; + info->get_key(file, (uint8_t *)&key, info->key_len); + if (key == itr_data->keep_id) { + return true; + } + + BtPersistBondingData stored_data; + info->get_val(file, (uint8_t *)&stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + if (stored_data.type != BtPersistBondingTypeBLE) { + return true; + } + + if (itr_data->count < BT_BONDING_PRUNE_MAX) { + itr_data->ids[itr_data->count++] = key; + } + return true; +} + +//! Delete every BLE bonding except `keep_id`. We only ever support one BLE pairing at a time, so +//! any other BLE bonding present is stale and must be removed (e.g. when a new phone pairs and +//! replaces the previous one). +//! +//! Uses the internal delete helper that does not erase shared PRF pairing data, since the kept +//! entry is the one that should remain reflected in PRF storage. +static void prv_delete_other_ble_bondings(BTBondingID keep_id) { + CollectOtherBleItrData itr_data = { + .keep_id = keep_id, + .count = 0, + }; + prv_file_each(prv_collect_other_ble_bondings_itr, &itr_data); + + for (uint8_t i = 0; i < itr_data.count; i++) { + PBL_LOG_INFO("Removing stale BLE bonding %d (kept %d)", itr_data.ids[i], keep_id); + prv_delete_ble_pairing_by_id(itr_data.ids[i]); + } +} + +typedef struct { + BTBondingID key_out; + uint32_t last_modified_out; + uint8_t ble_count; +} MostRecentBleItrData; + +static bool prv_find_most_recent_ble_bonding_itr(SettingsFile *file, SettingsRecordInfo *info, + void *context) { + if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { + return true; + } + + MostRecentBleItrData *itr_data = context; + + BtPersistBondingData stored_data; + info->get_val(file, (uint8_t *)&stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + if (stored_data.type != BtPersistBondingTypeBLE) { + return true; + } + + BTBondingID key; + info->get_key(file, (uint8_t *)&key, info->key_len); + + itr_data->ble_count++; + if (itr_data->key_out == BT_BONDING_ID_INVALID || + info->last_modified > itr_data->last_modified_out) { + itr_data->key_out = key; + itr_data->last_modified_out = info->last_modified; + } + return true; +} + +//! If the bonding DB contains multiple BLE pairings (e.g. left over from an older firmware that +//! allowed more than one, or from a PRF pairing merged on top of an existing one), keep the most +//! recently modified entry and drop the rest. +static void prv_prune_stale_ble_bondings(void) { + MostRecentBleItrData itr_data = { + .key_out = BT_BONDING_ID_INVALID, + .last_modified_out = 0, + .ble_count = 0, + }; + prv_file_each(prv_find_most_recent_ble_bonding_itr, &itr_data); + + if (itr_data.ble_count <= 1 || itr_data.key_out == BT_BONDING_ID_INVALID) { + return; + } + + PBL_LOG_INFO("Found %u BLE bondings at boot, keeping most recent (id %d)", + itr_data.ble_count, itr_data.key_out); + prv_delete_other_ble_bondings(itr_data.key_out); +} + +//! For unit testing +int bt_persistent_storage_get_raw_data(const void *key, size_t key_len, + void *data_out, size_t buf_len) { + return prv_file_get(key, key_len, data_out, buf_len); +} + +bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *addr) { + GapBondingFileSetStatus rv = prv_file_set(&BLE_PINNED_ADDRESS_KEY, sizeof(BLE_PINNED_ADDRESS_KEY), + addr, addr ? sizeof(*addr) : 0); + bool success = (rv != GapBondingFileSetFail); + if (!success) { + PBL_LOG_ERR("Failed to store pinned address"); + } else if (rv == GapBondingFileSetUpdated) { + shared_prf_storage_set_ble_pinned_address(addr); + } + return success; +} + +BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *new_pairing_info, + bool is_gateway, const char *device_name, + bool requires_address_pinning, + uint8_t flags) { + if (!new_pairing_info || sm_is_pairing_info_empty(new_pairing_info)) { + return BT_BONDING_ID_INVALID; + } + + // Check if this is an update + BtPersistBondingOp op = BtPersistBondingOpDidChange; + BTBondingID key = prv_get_key_for_sm_pairing_info(new_pairing_info); + + if (key == BT_BONDING_ID_INVALID) { + // This is an add, not an update + op = BtPersistBondingOpDidAdd; + key = prv_get_free_key(); + if (key == BT_BONDING_ID_INVALID) { + // We are out of keys.... + return BT_BONDING_ID_INVALID; + } + } else { + // If we add any optional fields a load will have to happen here so they don't get overwritten + } + + BtPersistBondingData new_data; + new_data = (BtPersistBondingData) { + .type = BtPersistBondingTypeBLE, + .ble_data.is_gateway = is_gateway, + .ble_data.flags = flags, + // This is defaulting to "is_gateway" for now because it is currently being used as the flag + // for the pairing that we want to reconnect/connect to. If this isn't set then + // we don't register an intent for the device and thus don't connect. + // Currently only 1 ble pairing is really supported so this works for now + // FIXME: https://pebbletechnology.atlassian.net/browse/PBL-15277 + .ble_data.supports_ancs = is_gateway, + .ble_data.requires_address_pinning = requires_address_pinning, + }; + bt_persistent_storage_assign_persist_pairing_info(&new_data.ble_data.pairing_info, + new_pairing_info); + + if (device_name) { + strncpy(new_data.ble_data.name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); + new_data.ble_data.name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; + } + + GapBondingFileSetStatus status; + status = prv_file_set(&key, sizeof(key), &new_data, sizeof(new_data)); + if (status == GapBondingFileSetFail) { + return BT_BONDING_ID_INVALID; + } + + if (is_gateway && status == GapBondingFileSetUpdated) { + prv_update_bondings(key, BtPersistBondingTypeBLE); + } + + // In practice we only support a single BLE pairing at a time, so if we add a new one, + // set ourselves as unfaithful. + if (op == BtPersistBondingOpDidAdd) { + bt_persistent_storage_set_unfaithful(true); + } + + prv_call_ble_bonding_change_handlers(key, op); + + // We only support a single BLE pairing at a time. Drop any previous BLE bonding so that + // re-pairing with a different phone (or merging a PRF pairing) replaces the old one instead of + // leaving it behind and forcing the user to forget it manually. + prv_delete_other_ble_bondings(key); + + return key; +} + +bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name) { + BtPersistBondingData data; + if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { + return false; + } + + if (data.type != BtPersistBondingTypeBLE) { + PBL_LOG_ERR("Not getting BLE id %d. Type mismatch", bonding); + return false; + } + + strncpy(data.ble_data.name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); + data.ble_data.name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; + + GapBondingFileSetStatus status; + status = prv_file_set(&bonding, sizeof(bonding), &data, sizeof(data)); + + // If this is the gateway, update SPRF so our pairing info betwen PRF and normal + // FW is in sync + if (data.ble_data.is_gateway && (status == GapBondingFileSetUpdated)) { + prv_update_bondings(bonding, BtPersistBondingTypeBLE); + } + + return (status != GapBondingFileSetFail); +} + +static void prv_init_and_assign_ble_bonding(BleBonding *bonding, + const BtPersistBondingData *stored_data) { + *bonding = (BleBonding){}; + bt_persistent_storage_assign_sm_pairing_info(&bonding->pairing_info, + &stored_data->ble_data.pairing_info); + bonding->is_gateway = stored_data->ble_data.is_gateway; +} + +static void prv_remove_ble_bonding_from_bt_driver(const BtPersistBondingData *deleted_data) { + if (!bt_ctl_is_bluetooth_running()) { + return; + } + BleBonding bonding; + prv_init_and_assign_ble_bonding(&bonding, deleted_data); + bt_driver_handle_host_removed_bonding(&bonding); +} + +status_t prv_delete_all_cccd_for_addr(const BTDeviceInternal *dev) { + status_t rv; + + prv_lock(); + { + SettingsFile fd; + rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, + BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv) { + goto cleanup; + } + + for (BTCCCDID id = 0U; id < BT_CCCD_ID_INVALID; id++) { + if (settings_file_exists(&fd, &id, sizeof(id))) { + BtPersistCCCDData stored_data; + + rv = settings_file_get(&fd, &id, sizeof(id), &stored_data, sizeof(stored_data)); + if (rv) { + goto cleanup; + } + + if (bt_device_internal_equal(dev, &stored_data.peer)) { + BleCCCD cccd_to_delete = { + .peer = *dev, + .chr_val_handle = stored_data.chr_val_handle, + .flags = stored_data.flags, + .value_changed = stored_data.value_changed, + }; + + bt_driver_handle_host_removed_cccd(&cccd_to_delete); + + rv = settings_file_delete(&fd, &id, sizeof(id)); + if (rv) { + goto cleanup; + } + } + } + } + + settings_file_close(&fd); + + } +cleanup: + prv_unlock(); + return rv; +} + +static bool prv_delete_ble_pairing_by_id(BTBondingID bonding) { + BtPersistBondingData deleted_data; + if (!prv_delete_pairing_with_type_by_id(bonding, BtPersistBondingTypeBLE, &deleted_data)) { + return false; + } + + status_t rv; + rv = prv_delete_all_cccd_for_addr(&deleted_data.ble_data.pairing_info.identity); + PBL_ASSERTN(rv == S_SUCCESS); + + prv_remove_ble_bonding_from_bt_driver(&deleted_data); + + prv_call_ble_bonding_change_handlers(bonding, BtPersistBondingOpWillDelete); + return true; +} + +void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID bonding) { + if (!prv_delete_ble_pairing_by_id(bonding)) { + return; + } + // TODO: Make sure this matches what we have stored + shared_prf_storage_erase_ble_pairing_data(); +} + +typedef struct { + BTDeviceInternal device; + SMIdentityResolvingKey irk_out; + char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; + BTBondingID id_out; + bool found; +} FindByAddrItrData; + +static bool prv_find_by_addr_itr(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // check entry is valid + if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { + return true; // continue iterating + } + + FindByAddrItrData *itr_data = (FindByAddrItrData *) context; + + BTBondingID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistBondingData stored_data; + info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + + if (stored_data.type == BtPersistBondingTypeBLE && + bt_device_equal(&itr_data->device.opaque, + &stored_data.ble_data.pairing_info.identity.opaque)) { + itr_data->irk_out = stored_data.ble_data.pairing_info.irk; + strncpy(itr_data->name_out, stored_data.ble_data.name, BT_DEVICE_NAME_BUFFER_SIZE); + itr_data->id_out = key; + itr_data->found = true; + return false; // stop iterating + } + + return true; // continue iterating +} + +void bt_persistent_storage_delete_ble_pairing_by_addr(const BTDeviceInternal *device) { + FindByAddrItrData itr_data = { + .device = *device, + .found = false, + }; + prv_file_each(prv_find_by_addr_itr, &itr_data); + + if (!itr_data.found) { + return; + } + + bt_persistent_storage_delete_ble_pairing_by_id(itr_data.id_out); +} + +static void prv_fill_ble_data(SMIdentityResolvingKey *irk_in, + BTDeviceInternal *device_in, + char *name_in, + SMIdentityResolvingKey *irk_out, + BTDeviceInternal *device_out, + char *name_out) { + if (irk_out && irk_in) { + *irk_out = *irk_in; + } + + if (device_out && device_in) { + *device_out = *device_in; + } + if (name_out && name_in) { + strncpy(name_out, name_in, BT_DEVICE_NAME_BUFFER_SIZE); + name_out[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; + } +} + +bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding, + SMIdentityResolvingKey *irk_out, + BTDeviceInternal *device_out, + char *name_out) { + BtPersistBondingData data; + if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { + return false; + } + + if (data.type != BtPersistBondingTypeBLE) { + PBL_LOG_ERR("Not getting BT Classic id %d. Type mismatch", bonding); + return false; + } + + prv_fill_ble_data(&data.ble_data.pairing_info.irk, &data.ble_data.pairing_info.identity, + data.ble_data.name, irk_out, device_out, name_out); + + return true; +} + +static bool prv_bt_persistent_storage_get_ble_smpairinginfo_by_id( + BTBondingID bonding, SMPairingInfo *info_out, char *name_out, bool *requires_address_pinning, + uint8_t *flags) { + BtPersistBondingData data; + if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { + return false; + } + + if (data.type != BtPersistBondingTypeBLE) { + PBL_LOG_ERR("Not getting BLE id %d. Type mismatch", bonding); + return false; + } + + if (info_out) { + bt_persistent_storage_assign_sm_pairing_info(info_out, &data.ble_data.pairing_info); + } + + *requires_address_pinning = data.ble_data.requires_address_pinning; + *flags = data.ble_data.flags; + + prv_fill_ble_data( + NULL, NULL, data.ble_data.name, NULL, NULL, name_out); + return true; +} + +bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device, + SMIdentityResolvingKey *irk_out, + char name_out[BT_DEVICE_NAME_BUFFER_SIZE]) { + FindByAddrItrData itr_data = { + .device = *device, + .found = false, + }; + prv_file_each(prv_find_by_addr_itr, &itr_data); + + if (!itr_data.found) { + return false; + } + + prv_fill_ble_data(&itr_data.irk_out, NULL, itr_data.name_out, + irk_out, NULL, name_out); + + return true; +} + +bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { + BTDeviceAddress address; + int read_size = prv_file_get(&BLE_PINNED_ADDRESS_KEY, sizeof(BLE_PINNED_ADDRESS_KEY), + &address, sizeof(address)); + if (!read_size) { + return false; + } + if (address_out) { + *address_out = address; + } + return true; +} + +static bool prv_get_first_ancs_bonding_itr(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // check entry is valid + if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { + return true; // continue iterating + } + + BTBondingID *first_ancs_supported_bonding_found = (BTBondingID *) context; + + BTBondingID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistBondingData stored_data; + info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + + if (stored_data.type == BtPersistBondingTypeBLE && stored_data.ble_data.supports_ancs) { + *first_ancs_supported_bonding_found = key; + return false; // stop iterating + } + + return true; +} + +BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void) { + BTBondingID first_ancs_supported_bonding_found = BT_BONDING_ID_INVALID; + prv_file_each(prv_get_first_ancs_bonding_itr, &first_ancs_supported_bonding_found); + + return first_ancs_supported_bonding_found; +} + +bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding) { + BtPersistBondingData data; + prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data)); + + if (data.type == BtPersistBondingTypeBLE) { + return data.ble_data.supports_ancs; + } + return false; +} + +bool bt_persistent_storage_has_ble_ancs_bonding(void) { + return bt_persistent_storage_get_ble_ancs_bonding() != BT_BONDING_ID_INVALID; +} + +bool bt_persistent_storage_has_active_ble_gateway_bonding(void) { + return prv_has_active_gateway_by_type(BtPersistBondingTypeBLE); +} + +typedef void (*BtPersistBondingDBEachBLEInternal)(BTBondingID key, + BtPersistBondingData *stored_data, void *ctx); + +typedef struct { + BtPersistBondingDBEachBLEInternal cb; + void *cb_data; +} ForEachBLEPairingInternalData; + +typedef void (*BtPersistCCCDDBEachBLEInternal)(BTCCCDID key, + BtPersistCCCDData *stored_data, void *ctx); + +typedef struct { + BtPersistCCCDDBEachBLEInternal cb; + void *cb_data; +} ForEachBLECCCDInternalData; + +typedef struct { + BtPersistBondingDBEachBLE cb; + void *cb_data; +} ForEachBLEPairingData; + +static void prv_public_for_each_ble_cb(BTBondingID key, + BtPersistBondingData *stored_data, void *context) { + ForEachBLEPairingData *itr_data = (ForEachBLEPairingData *)context; + itr_data->cb(&stored_data->ble_data.pairing_info.identity, + &stored_data->ble_data.pairing_info.irk, + stored_data->ble_data.name, &key, itr_data->cb_data); +} + +static bool prv_ble_pairing_internal_for_each_itr(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // check entry is valid + if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { + return true; // continue iterating + } + + ForEachBLEPairingInternalData *internal_itr_data = (ForEachBLEPairingInternalData*) context; + + BTBondingID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistBondingData stored_data; + info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + + if (stored_data.type == BtPersistBondingTypeBLE) { + internal_itr_data->cb(key, &stored_data, internal_itr_data->cb_data); + } + + return true; +} + +static bool prv_ble_cccd_internal_for_each_itr(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // check entry is valid + if (info->val_len == 0 || info->key_len != sizeof(BTCCCDID)) { + return true; // continue iterating + } + + ForEachBLECCCDInternalData *internal_itr_data = (ForEachBLECCCDInternalData*) context; + + BTCCCDID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistCCCDData stored_data; + info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + + internal_itr_data->cb(key, &stored_data, internal_itr_data->cb_data); + + return true; +} + +void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context) { + ForEachBLEPairingData itr_data = { + .cb = cb, + .cb_data = context, + }; + ForEachBLEPairingInternalData internal_itr_data = { + .cb = prv_public_for_each_ble_cb, + .cb_data = &itr_data, + }; + prv_file_each(prv_ble_pairing_internal_for_each_itr, &internal_itr_data); +} + +static void prv_register_bondings_for_each_ble_cb(BTBondingID key, + BtPersistBondingData *stored_data, + void *context) { + BleBonding bonding; + prv_init_and_assign_ble_bonding(&bonding, stored_data); + bonding.is_gateway = stored_data->ble_data.is_gateway; + bonding.flags = stored_data->ble_data.flags; + bt_driver_handle_host_added_bonding(&bonding); +} + +static void prv_register_cccd_for_each_ble_cb(BTCCCDID key, + BtPersistCCCDData *stored_data, + void *context) { + BleCCCD cccd = { + .peer = stored_data->peer, + .chr_val_handle = stored_data->chr_val_handle, + .flags = stored_data->flags, + .value_changed = stored_data->value_changed, + }; + + bt_driver_handle_host_added_cccd(&cccd); +} + +void bt_persistent_storage_register_existing_ble_bondings(void) { + ForEachBLEPairingInternalData internal_itr_data_bonding = { + .cb = prv_register_bondings_for_each_ble_cb, + }; + prv_file_each(prv_ble_pairing_internal_for_each_itr, &internal_itr_data_bonding); + + ForEachBLECCCDInternalData internal_itr_data_cccd = { + .cb = prv_register_cccd_for_each_ble_cb, + }; + prv_file_each(prv_ble_cccd_internal_for_each_itr, &internal_itr_data_cccd); +} + +typedef struct { + const BTDeviceInternal *peer; + uint16_t chr_val_handle; + BTCCCDID id; +} FindCCCDItrData; + +static bool prv_find_cccd_itr(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + if (info->val_len == 0 || info->key_len != sizeof(BTCCCDID)) { + return true; // continue iterating + } + + FindCCCDItrData *itr_data = (FindCCCDItrData *) context; + + BTCCCDID key; + info->get_key(file, (uint8_t*) &key, info->key_len); + + BtPersistCCCDData stored_data; + info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); + + if (bt_device_internal_equal(itr_data->peer, &stored_data.peer) && + stored_data.chr_val_handle == itr_data->chr_val_handle) { + itr_data->id = key; + return false; // stop iterating + } + + return true; +} + +BTCCCDID bt_persistent_storage_store_cccd(const BleCCCD *cccd) { + PBL_ASSERTN(cccd != NULL); + + FindCCCDItrData itr_data = { + .peer = &cccd->peer, + .chr_val_handle = cccd->chr_val_handle, + .id = BT_CCCD_ID_INVALID, + }; + prv_file_each(prv_find_cccd_itr, &itr_data); + + BTCCCDID cccd_id = itr_data.id; + if (cccd_id == BT_CCCD_ID_INVALID) { + cccd_id = prv_get_free_cccd(); + if (cccd_id == BT_CCCD_ID_INVALID) { + return BT_CCCD_ID_INVALID; + } + } + + BtPersistCCCDData stored_data = { + .peer = cccd->peer, + .chr_val_handle = cccd->chr_val_handle, + .flags = cccd->flags, + .value_changed = cccd->value_changed, + }; + + BtPersistCCCD stored_cccd = { + .id = cccd_id, + .data = stored_data, + }; + + GapBondingFileSetStatus status = prv_file_set(&stored_cccd.id, sizeof(stored_cccd.id), + &stored_cccd.data, sizeof(stored_cccd.data)); + if (status == GapBondingFileSetFail) { + return BT_CCCD_ID_INVALID; + } + + return cccd_id; +} + +bool bt_persistent_storage_delete_cccd(const BTDeviceInternal *peer, uint16_t chr_val_handle) { + BTCCCDID cccd_id; + + FindCCCDItrData itr_data = { + .peer = peer, + .chr_val_handle = chr_val_handle, + .id = BT_CCCD_ID_INVALID + }; + prv_file_each(prv_find_cccd_itr, &itr_data); + + if (itr_data.id == BT_CCCD_ID_INVALID) { + return false; + } + + if (prv_file_set(&cccd_id, sizeof(cccd_id), NULL, 0) == GapBondingFileSetFail) { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Local Device Info + +void bt_persistent_storage_set_active_gateway(BTBondingID bonding) { + BTBondingID old_active_gateway; + int read_size = prv_file_get(&ACTIVE_GATEWAY_KEY, sizeof(ACTIVE_GATEWAY_KEY), + &old_active_gateway, sizeof(old_active_gateway)); + + if (!read_size || old_active_gateway != bonding) { + prv_file_set(&ACTIVE_GATEWAY_KEY, sizeof(ACTIVE_GATEWAY_KEY), &bonding, sizeof(bonding)); + bt_persistent_storage_set_unfaithful(true); + bt_persistent_storage_set_cached_system_capabilities(NULL); + } +} + +bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out, + BtPersistBondingType *type_out) { + BTBondingID active_gateway; + int read_size = prv_file_get(&ACTIVE_GATEWAY_KEY, sizeof(ACTIVE_GATEWAY_KEY), + &active_gateway, sizeof(active_gateway)); + + if (!read_size || active_gateway == BT_BONDING_ID_INVALID) { + return false; + } + + if (bonding_out) { + *bonding_out = active_gateway; + } + if (type_out) { + *type_out = prv_get_type_for_id(active_gateway); + } + + return true; +} + +bool bt_persistent_storage_is_unfaithful(void) { + return prv_file_get_bool(&IS_UNFAITHFUL_KEY, sizeof(IS_UNFAITHFUL_KEY), true /* default */); +} + +void bt_persistent_storage_set_unfaithful(bool is_unfaithful) { + PBL_LOG_INFO("Marking the watch as %s", is_unfaithful ? "unfaithful" : "faithful"); + prv_file_set(&IS_UNFAITHFUL_KEY, sizeof(IS_UNFAITHFUL_KEY), + &is_unfaithful, sizeof(is_unfaithful)); +} + +bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { + SM128BitKey keys[SMRootKeyTypeNum]; + int read_size = prv_file_get(&ROOT_KEYS_KEY, sizeof(ROOT_KEYS_KEY), + &keys, sizeof(keys)); + if (!read_size) { + return false; + } + SM128BitKey nil_key = {}; + if (0 == memcmp(&nil_key, &keys[key_type], sizeof(nil_key))) { + return false; + } + + if (key_out) { + memcpy(key_out, &keys[key_type], sizeof(keys[key_type])); + } + + return true; +} + +void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in) { + if (!keys_in) { + return; + } + shared_prf_storage_set_root_keys(keys_in); + + prv_file_set(&ROOT_KEYS_KEY, sizeof(ROOT_KEYS_KEY), + keys_in, SMRootKeyTypeNum * sizeof(SM128BitKey)); +} + +bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { + int read_size = prv_file_get(&DEVICE_NAME_KEY, sizeof(DEVICE_NAME_KEY), + local_device_name_out, max_size); + if (!read_size) { + return false; + } + return true; +} + +void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t size) { + if (!local_device_name) { + return; + } + shared_prf_storage_set_local_device_name(local_device_name); + + prv_file_set(&DEVICE_NAME_KEY, sizeof(DEVICE_NAME_KEY), + local_device_name, size); +} + +bool bt_persistent_storage_get_airplane_mode_enabled(void) { + return prv_file_get_bool(&AIRPLANE_MODE_KEY, sizeof(AIRPLANE_MODE_KEY), false /* default */); +} + +void bt_persistent_storage_set_airplane_mode_enabled(bool new_state) { + prv_file_set(&AIRPLANE_MODE_KEY, sizeof(AIRPLANE_MODE_KEY), + &new_state, sizeof(bool)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Remote Device Info + +static void prv_load_cached_system_capabilities(PebbleProtocolCapabilities *capabilities_out) { + if (!capabilities_out) { + return; + } + + const int read_size = prv_file_get(&SYSTEM_CAPABILITIES_KEY, sizeof(SYSTEM_CAPABILITIES_KEY), + capabilities_out, sizeof(PebbleProtocolCapabilities)); + // Default to zero capabilities if no entry found + if (!read_size) { + *capabilities_out = (PebbleProtocolCapabilities) {}; + } +} + +void bt_persistent_storage_get_cached_system_capabilities( + PebbleProtocolCapabilities *capabilities_out) { + if (!capabilities_out) { + return; + } + + prv_lock(); + { + *capabilities_out = s_cached_system_capabilities; + } + prv_unlock(); +} + +void bt_persistent_storage_set_cached_system_capabilities( + const PebbleProtocolCapabilities *capabilities) { + PebbleProtocolCapabilities diff = {}; + + prv_lock(); + { + // If we were passed a null pointer, we'll just clear the cached capability bits + if (capabilities) { + diff.flags = s_cached_system_capabilities.flags ^ capabilities->flags; + s_cached_system_capabilities = *capabilities; + } else { + diff.flags = s_cached_system_capabilities.flags; + s_cached_system_capabilities = (PebbleProtocolCapabilities) {}; + } + } + prv_unlock(); + + // Only update the cache if the capability flags changed + if (diff.flags) { + prv_file_set(&SYSTEM_CAPABILITIES_KEY, sizeof(SYSTEM_CAPABILITIES_KEY), + capabilities, sizeof(PebbleProtocolCapabilities)); + + PebbleEvent event = { + .type = PEBBLE_CAPABILITIES_CHANGED_EVENT, + .capabilities.flags_diff = diff, + }; + event_put(&event); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Common + +void bt_persistent_storage_init(void) { + // Note: this gets called well before the BT stack is initialized, make sure there is no code + // that tries to use the BT stack in this path. + s_db_mutex = mutex_create(); + + prv_load_data_from_prf(); + + // Clean up any leftover state where the bonding DB ended up with more than one BLE pairing + // (e.g. inherited from an older firmware build, or a PRF pairing layered on an existing one). + prv_prune_stale_ble_bondings(); + + // Load cached capability bits from flash + prv_load_cached_system_capabilities(&s_cached_system_capabilities); +} + +static void prv_delete_all_pairings_itr(SettingsFile *old_file, SettingsFile *new_file, + SettingsRecordInfo *info, void *context) { + if (info->key_len == sizeof(BTBondingID)) { + // Skip pairing entries + return; + } + + // Re-write non-pairing entries + void *key = kernel_zalloc_check(info->key_len); + info->get_key(old_file, key, info->key_len); + + void *data = kernel_malloc_check(info->val_len); + info->get_val(old_file, data, info->val_len); + + settings_file_set(new_file, key, info->key_len, &data, info->val_len); + + kernel_free(key); + kernel_free(data); +} + +void bt_persistent_storage_delete_all_pairings(void) { + prv_lock(); + { + SettingsFile fd; + status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, + BT_PERSISTENT_STORAGE_FILE_SIZE); + if (rv) { + return; + } + + settings_file_rewrite(&fd, prv_delete_all_pairings_itr, NULL); + settings_file_close(&fd); + } + prv_unlock(); + + shared_prf_storage_erase_ble_pairing_data(); +} + +static void prv_dump_bonding_db_data(char display_buf[DISPLAY_BUF_LEN], + BTBondingID bond_id, BtPersistBondingData *data) { + bool matches_prf; + + if (data->type == BtPersistBondingTypeBTClassic) { + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "Classic Key %d (legacy)", + (int)bond_id); + } else if (data->type == BtPersistBondingTypeBLE) { + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "LE Key %d", + (int)bond_id); + + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " ANCS: %d Gateway: %d Req Pin: %d", + (int)data->ble_data.supports_ancs, + (int)data->ble_data.is_gateway, + (int)data->ble_data.requires_address_pinning); + + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Name: %s", + data->ble_data.name); + + SMPairingInfo info = {}; + bt_persistent_storage_assign_sm_pairing_info(&info, &data->ble_data.pairing_info); + bluetooth_persistent_storage_debug_dump_ble_pairing_info(&display_buf[0], &info); + + // does this info match the key stored in shared resources + SMPairingInfo sprf_info = {}; + bool requires_address_pinning; + uint8_t flags; + shared_prf_storage_get_ble_pairing_data(&sprf_info, NULL, &requires_address_pinning, + &flags); + matches_prf = (memcmp(&sprf_info, &info, sizeof(sprf_info)) == 0); + matches_prf &= (requires_address_pinning == data->ble_data.requires_address_pinning); + matches_prf &= (flags == data->ble_data.flags); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, + " SMPairingInfo matches Shared PRF: %s", + bool_to_str(matches_prf)); + } else { + prompt_send_response("Unhandled type of GapBondingDB Data!"); + PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)&data, sizeof(*data)); + } +} + +static void prv_dump_cccd_db_data(char display_buf[DISPLAY_BUF_LEN], + BTCCCDID cccd_id, BtPersistCCCDData *data) { + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "CCCD Key %d", (int)cccd_id); + + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Peer Address: "BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE_PTR(&data->peer.address)); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Handle: 0x%" PRIx16, + data->chr_val_handle); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Flags: 0x%" PRIx16, data->flags); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Value Changed: %s", + bool_to_str(data->value_changed)); +} + +static bool prv_dump_bt_persistent_storage_contents( + SettingsFile *file, SettingsRecordInfo *info, void *context) { + if (info->key_len == 0 || info->val_len == 0) { + prompt_send_response("key or val of 0 length"); + return true; + } + char *display_buf = kernel_malloc_check(DISPLAY_BUF_LEN); + + // get the key + uint8_t key[info->key_len]; + memset(key, 0x0, info->key_len); + info->get_key(file, &key[0], info->key_len); + // prompt_send_response("Raw dump Key"); + // PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)&key, info->key_len); + + uint8_t val[info->val_len]; + memset(val, 0x0, info->val_len); + info->get_val(file, &val[0], info->val_len); + // prompt_send_response("Raw dump Value:"); + // PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)&val, info->val_len); + + if (memcmp(key, ACTIVE_GATEWAY_KEY, info->key_len) == 0) { + PBL_ASSERTN(info->val_len == sizeof(BTBondingID)); + BTBondingID id; + memcpy(&id, val, sizeof(BTBondingID)); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "%s : %d", + ACTIVE_GATEWAY_KEY, (int)id); + + } else if (memcmp(key, IS_UNFAITHFUL_KEY, info->key_len) == 0) { + PBL_ASSERTN(info->val_len == sizeof(bool)); + bool is_unfaithful; + memcpy(&is_unfaithful, val, sizeof(bool)); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "%s : %d", + IS_UNFAITHFUL_KEY, (int)is_unfaithful); + + } else if (memcmp(key, ROOT_KEYS_KEY, info->key_len) == 0) { + SM128BitKey root_keys[SMRootKeyTypeNum], sprf_root_keys[SMRootKeyTypeNum]; + PBL_ASSERTN(info->val_len == sizeof(root_keys)); + memcpy(&root_keys, val, sizeof(root_keys)); + + bluetooth_persistent_storage_debug_dump_root_keys(&root_keys[SMRootKeyTypeEncryption], + &root_keys[SMRootKeyTypeIdentity]); + + if (shared_prf_storage_get_root_key( + SMRootKeyTypeEncryption, &sprf_root_keys[SMRootKeyTypeEncryption]) && + shared_prf_storage_get_root_key( + SMRootKeyTypeIdentity, &sprf_root_keys[SMRootKeyTypeIdentity])) { + bool root_keys_match = + memcmp(&root_keys, &sprf_root_keys, sizeof(SM128BitKey) * SMRootKeyTypeNum) == 0; + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, + " Root keys match shared prf: %s", + bool_to_str(root_keys_match)); + } + } else if (memcmp(key, DEVICE_NAME_KEY, info->key_len) == 0) { + char dev_name[info->val_len + 1]; + memcpy(&dev_name, val, info->val_len); + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "Device Name: %s", + dev_name); + } else if (memcmp(key, BLE_PINNED_ADDRESS_KEY, info->key_len) == 0) { + if (info->val_len == sizeof(BTDeviceAddress)) { + const BTDeviceAddress *address = (const BTDeviceAddress *)val; + prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, + "Pinned address: "BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE_PTR(address)); + } + } else if (info->key_len == sizeof(BTBondingID)) { + BTBondingID bonding_id; + + memcpy(&bonding_id, key, sizeof(BTBondingID)); + PBL_ASSERTN(sizeof(BtPersistBondingData) == info->val_len); + prv_dump_bonding_db_data(display_buf, bonding_id, (BtPersistBondingData *)&val); + } else if (info->key_len == sizeof(BTCCCDID)) { + BTCCCDID cccd_id; + + memcpy(&cccd_id, key, sizeof(BTCCCDID)); + PBL_ASSERTN(sizeof(BtPersistCCCDData) == info->val_len); + prv_dump_cccd_db_data(display_buf, cccd_id, (BtPersistCCCDData *)&val); + } else { + prompt_send_response("Something new be in the bonding DB!"); + PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, &key[0], info->key_len); + PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, &val[0], info->val_len); + } + + prompt_send_response(""); + + kernel_free(display_buf); + return true; +} + +void bluetooth_persistent_storage_dump_contents(void) { + prv_file_each(prv_dump_bt_persistent_storage_contents, NULL); +} diff --git a/src/fw/services/bluetooth/bluetooth_persistent_storage_prf.c b/src/fw/services/bluetooth/bluetooth_persistent_storage_prf.c new file mode 100644 index 0000000000..bce6e3e392 --- /dev/null +++ b/src/fw/services/bluetooth/bluetooth_persistent_storage_prf.c @@ -0,0 +1,278 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" + +#include "comm/ble/gap_le_connect.h" +#include "comm/ble/gap_le_slave_reconnect.h" + +#include "comm/bt_lock.h" + +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" + +#include "comm/ble/kernel_le_client/kernel_le_client.h" + +#include "system/logging.h" + +#include +#include +#include +#include + +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + + +//! This is just an interface for the shared PRF storage + + +//! These don't matter at all +#define BLE_BONDING_ID (0) + +#define BT_CCCD_ID_MIN 0x80U + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! BLE Pairing Info + +static void prv_call_ble_bonding_change_handlers(BTBondingID bonding, BtPersistBondingOp op) { + gap_le_connect_handle_bonding_change(bonding, op); + kernel_le_client_handle_bonding_change(bonding, op); + bt_pairability_update_due_to_bonding_change(); +} + +static BTBondingID prv_bt_persistent_storage_store_ble_pairing( + const SMPairingInfo *new_pairing_info, bool is_gateway, bool requires_address_pinning, + uint8_t flags, const char *device_name, BtPersistBondingOp op) { + if (new_pairing_info && is_gateway) { + shared_prf_storage_store_ble_pairing_data(new_pairing_info, device_name, + requires_address_pinning, + flags); + prv_call_ble_bonding_change_handlers(BLE_BONDING_ID, op); + return BLE_BONDING_ID; + } + + return BT_BONDING_ID_INVALID; +} + +bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *addr) { + shared_prf_storage_set_ble_pinned_address(addr); + return true; +} + +bool bt_persistent_storage_has_pinned_ble_pairings(void) { + bool requires_address_pinning_out = false; + shared_prf_storage_get_ble_pairing_data(NULL, NULL, &requires_address_pinning_out, NULL); + return requires_address_pinning_out; +} + +bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { + return shared_prf_storage_get_ble_pinned_address(address_out); +} + +BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *new_pairing_info, + bool is_gateway, const char *device_name, + bool requires_address_pinning, + uint8_t flags) { + // We only have one slot in PRF and all pairing info (except the device + // name) will arrive in one-shot so anytime this routine gets called it + // means we have 'added' a new pairing + + bool is_updating_existing = false; + SMPairingInfo existing_pairing_info; + if (shared_prf_storage_get_ble_pairing_data(&existing_pairing_info, NULL, NULL, NULL)) { + if (sm_is_pairing_info_equal_identity(new_pairing_info, &existing_pairing_info)) { + // Treat re-pairing an existing device as an "update" instead of deletion+addition, + // because there is only one bonding ID that gets re-used, a deletion would otherwise cause a + // disconnection to happen. See PBL-24737. + PBL_LOG_INFO("Re-pairing previously paired LE device"); + is_updating_existing = true; + } else { + // Since we only have one slot, this means we are about to delete what was + // already there so handle the deletion if a valid pairing was stored + prv_call_ble_bonding_change_handlers(BLE_BONDING_ID, BtPersistBondingOpWillDelete); + } + } + + BtPersistBondingOp pairing_op = + is_updating_existing ? BtPersistBondingOpDidChange : BtPersistBondingOpDidAdd; + return (prv_bt_persistent_storage_store_ble_pairing(new_pairing_info, is_gateway, + requires_address_pinning, + flags, device_name, pairing_op)); +} + +bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name) { + // A device name has come in, update the name of our currently paired device + SMPairingInfo data = {}; + bool requires_address_pinning = false; + uint8_t flags = 0; + if (!shared_prf_storage_get_ble_pairing_data(&data, NULL, &requires_address_pinning, &flags)) { + PBL_LOG_ERR("Tried to store device name, but pairing no longer around."); + return false; + } + // In PRF, only the gateway should get paired, so default to "true": + return (BT_BONDING_ID_INVALID != + prv_bt_persistent_storage_store_ble_pairing(&data, true /* is_gateway */, + requires_address_pinning, flags, + device_name, BtPersistBondingOpDidChange)); +} + +static void prv_remove_ble_bonding_from_bt_driver(void) { + if (!bt_ctl_is_bluetooth_running()) { + return; + } + BleBonding bonding = { + .is_gateway = true, + }; + if (!shared_prf_storage_get_ble_pairing_data(&bonding.pairing_info, NULL, NULL, NULL)) { + return; + } + bt_driver_handle_host_removed_bonding(&bonding); +} + +void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID bonding) { + prv_remove_ble_bonding_from_bt_driver(); + shared_prf_storage_erase_ble_pairing_data(); + prv_call_ble_bonding_change_handlers(bonding, BtPersistBondingOpWillDelete); +} + +void bt_persistent_storage_delete_ble_pairing_by_addr(const BTDeviceInternal *device) { + bt_persistent_storage_delete_ble_pairing_by_id(BLE_BONDING_ID); +} + +bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding, + SMIdentityResolvingKey *IRK_out, + BTDeviceInternal *device_out, + char *name_out) { + SMPairingInfo data; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + if (!shared_prf_storage_get_ble_pairing_data(&data, name, NULL, NULL)) { + return false; + } + + if (IRK_out) { + *IRK_out = data.irk; + } + if (device_out) { + *device_out = data.identity; + } + if (name_out) { + strncpy(name_out, name, BT_DEVICE_NAME_BUFFER_SIZE); + name_out[BT_DEVICE_NAME_BUFFER_SIZE - 1] = 0; + } + + return true; +} + +bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device, + SMIdentityResolvingKey *IRK_out, + char name[BT_DEVICE_NAME_BUFFER_SIZE]) { + BTDeviceInternal device_out = {}; + bool rv = bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, IRK_out, &device_out, name); + return (rv && bt_device_equal(&device->opaque, &device_out.opaque)); +} + +void bt_persistent_storage_set_active_ble_gateway(BTBondingID bonding) { +} + +BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void) { + return BLE_BONDING_ID; +} + +bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding) { + return bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, NULL, NULL, NULL); +} + +bool bt_persistent_storage_has_ble_ancs_bonding(void) { + return bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, NULL, NULL, NULL); +} + +bool bt_persistent_storage_has_active_ble_gateway_bonding(void) { + return bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, NULL, NULL, NULL); +} + +void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context) { + return; +} + +void bt_persistent_storage_register_existing_ble_bondings(void) { + BleBonding bonding = {}; + uint8_t flags; + if (!shared_prf_storage_get_ble_pairing_data(&bonding.pairing_info, NULL, NULL, &flags)) { + return; + } + bonding.is_gateway = true; + bonding.flags = flags; + bt_driver_handle_host_added_bonding(&bonding); +} + +// PRF does not support persistent CCCD storage, these are just stubs + +BTCCCDID bt_persistent_storage_store_cccd(const BleCCCD *cccd) { + return BT_CCCD_ID_MIN; +} + +bool bt_persistent_storage_delete_cccd(const BTDeviceInternal *peer, uint16_t chr_val_handle) { + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Local Device Info + +void bt_persistent_storage_set_active_gateway(BTBondingID bonding) { + return; +} + +bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out, + BtPersistBondingType *type_out) { + return false; +} + +bool bt_persistent_storage_is_unfaithful(void) { + return true; +} + +void bt_persistent_storage_set_unfaithful(bool is_unfaithful) { + return; +} + +bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { + return shared_prf_storage_get_root_key(key_type, key_out); +} + +void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in) { + shared_prf_storage_set_root_keys(keys_in); +} + +bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { + return shared_prf_storage_get_local_device_name(local_device_name_out, max_size); +} + +void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t size) { + shared_prf_storage_set_local_device_name(local_device_name); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Remote Device Info + +void bt_persistent_storage_get_cached_system_capabilities( + PebbleProtocolCapabilities *capabilities_out) { +} + +void bt_persistent_storage_set_cached_system_capabilities( + const PebbleProtocolCapabilities *capabilities) { +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Common + +void bt_persistent_storage_init(void) { +} + +void bt_persistent_storage_delete_all(void) { +} + +void bt_persistent_storage_delete_all_pairings(void) { + bt_persistent_storage_delete_ble_pairing_by_id(BLE_BONDING_ID); +} diff --git a/src/fw/services/bluetooth/bluetooth_prompt.c b/src/fw/services/bluetooth/bluetooth_prompt.c new file mode 100644 index 0000000000..ffa8b18be5 --- /dev/null +++ b/src/fw/services/bluetooth/bluetooth_prompt.c @@ -0,0 +1,76 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "comm/ble/gap_le_connection.h" +#include "comm/bt_lock.h" + +#include "console/prompt.h" +#include "pbl/services/bluetooth/bluetooth_ctl.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/local_id.h" +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "util/string.h" + +#include +#include + +#include + +void command_bt_print_mac(void) { + char addr_hex_str[BT_ADDR_FMT_BUFFER_SIZE_BYTES]; + bt_local_id_copy_address_hex_string(addr_hex_str); + prompt_send_response(addr_hex_str); +} + + +//! @param bt_name A custom Bluetooth device name. +void command_bt_set_name(const char *bt_name) { + bt_local_id_set_device_name(bt_name); +} + +void command_bt_prefs_wipe(void) { + bt_persistent_storage_delete_all_pairings(); +} + +void command_bt_sprf_nuke(void) { + shared_prf_storage_wipe_all(); +#ifdef CONFIG_RECOVERY_FW + // Reset system to get caches (in s_intents, s_connections and controller-side caches) in sync. + extern void factory_reset_set_reason_and_reset(void); + factory_reset_set_reason_and_reset(); +#endif +} + +#ifdef CONFIG_RECOVERY_FW +void command_bt_status(void) { + char buffer[64]; + + prompt_send_response_fmt(buffer, sizeof(buffer), "Alive: %s", + bt_ctl_is_bluetooth_running() ? "yes" : "no"); + + const char *prefix = "BT Chip Info: "; + size_t prefix_length = strlen(prefix); + strncpy(buffer, prefix, sizeof(buffer)); + bt_driver_id_copy_chip_info_string(buffer + prefix_length, + sizeof(buffer) - prefix_length); + prompt_send_response(buffer); + + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + bt_lock(); + bool connected = false; + GAPLEConnection *connection = gap_le_connection_any(); + if (connection) { + const char *device_name = connection->device_name ?: ""; + strncpy(name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); + name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; + connected = true; + } + bt_unlock(); + + prompt_send_response_fmt(buffer, sizeof(buffer), "Connected: %s", connected ? "yes" : "no"); + if (connected) { + prompt_send_response_fmt(buffer, sizeof(buffer), "Device: %s", name); + } +} +#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/services/bluetooth/bonding.c b/src/fw/services/bluetooth/bonding.c new file mode 100644 index 0000000000..38fdc868c8 --- /dev/null +++ b/src/fw/services/bluetooth/bonding.c @@ -0,0 +1,85 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "comm/ble/gap_le_connection.h" +#include "comm/ble/gap_le_device_name.h" +#include "comm/bt_lock.h" + +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/local_addr.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" + +#include +#include +#include + +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +typedef struct { + BTBondingID bonding_id; + BTDeviceAddress addr; + bool is_gateway; +} CreateBondingContext; + +static void prv_finalize_create_bonding(BTBondingID bonding_id, + const BTDeviceAddress *addr, + bool is_gateway) { + bt_lock(); + GAPLEConnection *connection = gap_le_connection_by_addr(addr); + if (connection) { + connection->bonding_id = bonding_id; + connection->is_gateway = is_gateway; + + if (!connection->is_gateway) { + PBL_LOG_DBG("New bonding is not gateway?"); + } + + gap_le_device_name_request(&connection->device); + } else { + PBL_LOG_ERR("Couldn't find connection for bonding!"); + } + bt_unlock(); +} + +static void prv_finalize_create_bonding_cb(void *data) { + CreateBondingContext *context = data; + prv_finalize_create_bonding(context->bonding_id, &context->addr, context->is_gateway); + kernel_free(context); +} + +void bt_driver_cb_handle_create_bonding(const BleBonding *bonding, + const BTDeviceAddress *addr) { + PBL_LOG_INFO("Creating new bonding for "BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE(bonding->pairing_info.identity.address)); + const bool should_pin_address = bonding->should_pin_address; + if (should_pin_address) { + bt_local_addr_pin(&bonding->pinned_address); + } + const uint8_t flags = bonding->flags; + if (flags) { + PBL_LOG_INFO("flags: 0x02%x", flags); + } + BTBondingID bonding_id = bt_persistent_storage_store_ble_pairing(&bonding->pairing_info, + bonding->is_gateway, NULL, + should_pin_address, + flags); + if (bonding_id == BT_BONDING_ID_INVALID) { + return; + } + + if (launcher_task_is_current_task()) { + prv_finalize_create_bonding(bonding_id, addr, bonding->is_gateway); + return; + } + + CreateBondingContext *context = kernel_malloc_check(sizeof(*context)); + + *context = (CreateBondingContext) { + .bonding_id = bonding_id, + .addr = *addr, + .is_gateway = bonding->is_gateway, + }; + launcher_task_add_callback(prv_finalize_create_bonding_cb, context); +} diff --git a/src/fw/services/bluetooth/dis.c b/src/fw/services/bluetooth/dis.c new file mode 100644 index 0000000000..2e8b6a5914 --- /dev/null +++ b/src/fw/services/bluetooth/dis.c @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include +#include +#include +#include + +#include "board/board.h" +#include "process_management/pebble_process_info.h" +#include "mfg/mfg_info.h" +#include "mfg/mfg_serials.h" +#include "system/version.h" + +_Static_assert(MODEL_NUMBER_LEN >= MFG_HW_VERSION_SIZE + 1, "Size mismatch"); +_Static_assert(MANUFACTURER_LEN >= sizeof(BT_VENDOR_NAME), "Size mismatch"); +_Static_assert(SERIAL_NUMBER_LEN >= MFG_SERIAL_NUMBER_SIZE + 1, "Size mismatch"); +_Static_assert(FW_REVISION_LEN >= sizeof(TINTIN_METADATA.version_tag), "Size mismatch"); + +static void prv_set_model_number(DisInfo *info) { + mfg_info_get_hw_version(info->model_number, MODEL_NUMBER_LEN); +} + +static void prv_set_manufacturer_name(DisInfo *info) { + strncpy(info->manufacturer, BT_VENDOR_NAME, MANUFACTURER_LEN); +} + +static void prv_set_serial_number(DisInfo *info) { + mfg_info_get_serialnumber(info->serial_number, SERIAL_NUMBER_LEN); +} + +static void prv_set_firmware_revision(DisInfo *info) { + strncpy(info->fw_revision, (char*)TINTIN_METADATA.version_tag, FW_REVISION_LEN); +} + +static void prv_set_software_revision(DisInfo *info) { + // Fmt: xx.xx\0 + char sdk_version[SW_REVISION_LEN]; + sniprintf(sdk_version, SW_REVISION_LEN, "%2u.%02u", + PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, + PROCESS_INFO_CURRENT_SDK_VERSION_MINOR); + strncpy(info->sw_revision, sdk_version, SW_REVISION_LEN); +} + +void dis_get_info(DisInfo *info) { + prv_set_model_number(info); + prv_set_manufacturer_name(info); + prv_set_serial_number(info); + prv_set_firmware_revision(info); + prv_set_software_revision(info); +} diff --git a/src/fw/services/bluetooth/local_addr.c b/src/fw/services/bluetooth/local_addr.c new file mode 100644 index 0000000000..ee3378fb92 --- /dev/null +++ b/src/fw/services/bluetooth/local_addr.c @@ -0,0 +1,115 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/local_addr.h" + +#include "comm/bt_lock.h" +#include "system/logging.h" +#include "system/passert.h" + +#include +#include +#include + +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +static uint32_t s_pra_cycling_pause_count; +static BTDeviceAddress s_pinned_addr; +static bool s_cycling_paused_due_to_dependent_bondings; + +static void prv_allow_cycling(bool allow_cycling) { + bt_driver_set_local_address(allow_cycling, allow_cycling ? NULL : &s_pinned_addr); +} + +void bt_local_addr_pause_cycling(void) { + bt_lock(); + { + if (s_pra_cycling_pause_count == 0) { + PBL_LOG_INFO("Pausing address cycling (pinned_addr="BT_DEVICE_ADDRESS_FMT")", + BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr)); + prv_allow_cycling(false); + } + ++s_pra_cycling_pause_count; + } + bt_unlock(); +} + +void bt_local_addr_resume_cycling(void) { + bt_lock(); + { + PBL_ASSERTN(s_pra_cycling_pause_count); + --s_pra_cycling_pause_count; + if (s_pra_cycling_pause_count == 0) { + PBL_LOG_INFO("Resuming address cycling (pinned_addr="BT_DEVICE_ADDRESS_FMT")", + BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr)); + prv_allow_cycling(true); + } + } + bt_unlock(); +} + +void bt_local_addr_pin(const BTDeviceAddress *addr) { + // In a previous version of the code, the main FW would not know yet what address would be used + // for pinning until the BT driver would give the address to pin when a pairing was added. + // A single, persistent pinned address is now generated up front in bt_local_addr_init(). + // Getting the address back in this call from the BT driver currently only serves as a + // consistency check. + // It is possible that the addresses do not match in the following scenario: + // 1. No bondings that require pinning present. Cycling address 'C' is used. + // 2. Device A is connected. + // 3. Become discoverable: cycling is requested to be paused at address 'P' but can't be granted + // yet because device A is still connected. + // 4. Device B connects (using 'C' as connection address) + // 5. Device B requests pin + pairs => the remote bonding is stored with 'C' as key instead of 'P' + // 6. We'll print here there's a mismatch. + // 7. Once Device A & B disconnect, device B won't be able to recognize us because 'P' is used... + + bt_lock(); + bool addresses_match = bt_device_address_equal(addr, &s_pinned_addr); + bt_unlock(); + + PBL_LOG_INFO("Requested to pin address to "BT_DEVICE_ADDRESS_FMT " match=%u", + BT_DEVICE_ADDRESS_XPLODE_PTR(addr), addresses_match); +} + +void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) { + bool has_pinned_ble_pairings = bt_persistent_storage_has_pinned_ble_pairings(); + if (has_pinned_ble_pairings != s_cycling_paused_due_to_dependent_bondings) { + if (has_pinned_ble_pairings) { + bt_local_addr_pause_cycling(); + } else { + bt_local_addr_resume_cycling(); + } + s_cycling_paused_due_to_dependent_bondings = has_pinned_ble_pairings; + } +} + +void bt_local_addr_init(void) { + s_pra_cycling_pause_count = 0; + s_cycling_paused_due_to_dependent_bondings = false; + + // Load pinned address from settings file or generate one if it hasn't happened before: + if (!bt_persistent_storage_get_ble_pinned_address(&s_pinned_addr)) { + if (bt_driver_id_generate_private_resolvable_address(&s_pinned_addr)) { + bt_persistent_storage_set_ble_pinned_address(&s_pinned_addr); + } else { + PBL_LOG_ERR("Failed to generate PRA... :("); + } + } + PBL_LOG_INFO("Pinned address: " BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr)); + + if (bt_persistent_storage_has_pinned_ble_pairings()) { + PBL_LOG_INFO("Bonding that requires address pinning exists, applying pinned addr!"); + bt_local_addr_pause_cycling(); + s_cycling_paused_due_to_dependent_bondings = true; + } else { +#ifdef CONFIG_RECOVERY_FW + PBL_LOG_INFO("Pausing address cycling because PRF!"); + bt_local_addr_pause_cycling(); +#else + PBL_LOG_INFO("No bondings found that require address pinning!"); + bt_driver_set_local_address(true /* allow_cycling */, NULL); +#endif + } +} diff --git a/src/fw/services/common/bluetooth/local_id.c b/src/fw/services/bluetooth/local_id.c similarity index 80% rename from src/fw/services/common/bluetooth/local_id.c rename to src/fw/services/bluetooth/local_id.c index 02656c2c90..935b9f960f 100644 --- a/src/fw/services/common/bluetooth/local_id.c +++ b/src/fw/services/bluetooth/local_id.c @@ -1,30 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "local_id.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/local_id.h" #include "mfg/mfg_serials.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" #include "util/attributes.h" #include "util/hash.h" #include "util/size.h" #include "util/string.h" -#include - #include #include #include @@ -41,13 +26,8 @@ static void prv_populate_name(char name[BT_DEVICE_NAME_BUFFER_SIZE], const char } static void prv_set_default_device_name(void) { -#if (PLATFORM_SNOWY || PLATFORM_SPALDING || PLATFORM_ROBERT) - const char *s_local_default_device_name_format = "Pebble Time %02X%02X"; - const char *s_local_default_le_device_name_format = "Pebble Time LE %02X%02X"; -#else const char *s_local_default_device_name_format = "Pebble %02X%02X"; const char *s_local_default_le_device_name_format = "Pebble-LE %02X%02X"; -#endif // Pebble + hex last 2 bytes of the device address: prv_populate_name(s_local_device_name, s_local_default_device_name_format); @@ -83,9 +63,7 @@ void bt_local_id_set_device_name(const char *device_name) { } void bt_local_id_copy_device_name(char name_out[BT_DEVICE_NAME_BUFFER_SIZE], bool is_le) { - char *name = (is_le && bt_driver_supports_bt_classic()) ? s_local_le_device_name : - s_local_device_name; - strncpy(name_out, name, BT_DEVICE_NAME_BUFFER_SIZE); + strncpy(name_out, s_local_device_name, BT_DEVICE_NAME_BUFFER_SIZE); } void bt_local_id_copy_address(BTDeviceAddress *addr_out) { diff --git a/src/fw/services/bluetooth/pairability.c b/src/fw/services/bluetooth/pairability.c new file mode 100644 index 0000000000..167f5b9bfa --- /dev/null +++ b/src/fw/services/bluetooth/pairability.c @@ -0,0 +1,122 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "system/logging.h" +#include "system/passert.h" + +#include "comm/ble/gap_le_slave_discovery.h" +#include "kernel/pebble_tasks.h" +#include "pbl/services/bluetooth/bluetooth_ctl.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/local_addr.h" +#include "pbl/services/bluetooth/pairability.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" + +#include + +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +static void prv_pairability_timer_cb(void *unused); + +static int s_allow_ble_pairing_refcount = 0; +static bool s_last_ble_discoverable_state = false; + +static RegularTimerInfo s_pairability_timer_info = { + .cb = prv_pairability_timer_cb, +}; + +static void evaluate_pairing_refcount(void *data) { + PBL_ASSERT_TASK(PebbleTask_KernelBackground); + + if (!bt_ctl_is_bluetooth_running()) { + return; + } + + PBL_LOG_DBG("Pairabilty state: LE=%u", s_allow_ble_pairing_refcount); + + bool is_ble_pairable_and_discoverable = (s_allow_ble_pairing_refcount > 0); + + bt_driver_le_pairability_set_enabled(is_ble_pairable_and_discoverable); + if (s_last_ble_discoverable_state != is_ble_pairable_and_discoverable) { + if (is_ble_pairable_and_discoverable) { + bt_local_addr_pause_cycling(); + } else { + bt_local_addr_resume_cycling(); + } + gap_le_slave_set_discoverable(is_ble_pairable_and_discoverable); + s_last_ble_discoverable_state = is_ble_pairable_and_discoverable; + } +} + +static void prv_schedule_evaluation(void) { + // We used to sparingly schedule the evaluation and had a bug because of this: + // https://pebbletechnology.atlassian.net/browse/PBL-22884 + // Because this pretty much only happens in response to user input, don't bother limiting this, + // and always evaluate, even though the state might not have changed: + system_task_add_callback(evaluate_pairing_refcount, NULL); +} + +void bt_pairability_use(void) { + ++s_allow_ble_pairing_refcount; + prv_schedule_evaluation(); +} + +void bt_pairability_use_ble(void) { + ++s_allow_ble_pairing_refcount; + prv_schedule_evaluation(); +} + +static void prv_pairability_timer_cb(void *unused) { + regular_timer_remove_callback(&s_pairability_timer_info); + bt_pairability_release_ble(); +} + +void bt_pairability_use_ble_for_period(uint16_t duration_secs) { + if (!regular_timer_is_scheduled(&s_pairability_timer_info)) { + // If this function is called multiple times before the timer is unscheduled, limit to calling + // "use" only once: + bt_pairability_use_ble(); + } + // Always reschedule, even if the duration is shorter than the one that might already be + // scheduled: + regular_timer_add_multisecond_callback(&s_pairability_timer_info, duration_secs); +} + +void bt_pairability_release(void) { + PBL_ASSERT(s_allow_ble_pairing_refcount != 0, ""); + --s_allow_ble_pairing_refcount; + prv_schedule_evaluation(); +} + +void bt_pairability_release_ble(void) { + PBL_ASSERT(s_allow_ble_pairing_refcount != 0, ""); + --s_allow_ble_pairing_refcount; + prv_schedule_evaluation(); +} + +//! Call this whenever we modify the number of saved bondings we have. +void bt_pairability_update_due_to_bonding_change(void) { + static bool s_pairable_due_to_no_gateway_bondings = false; + + if (!bt_persistent_storage_has_active_ble_gateway_bonding() && + !bt_persistent_storage_has_ble_ancs_bonding()) { + if (!s_pairable_due_to_no_gateway_bondings) { + bt_pairability_use(); + s_pairable_due_to_no_gateway_bondings = true; + } + } else { + if (s_pairable_due_to_no_gateway_bondings) { + bt_pairability_release(); + s_pairable_due_to_no_gateway_bondings = false; + } + } +} + +void bt_pairability_init(void) { + // Reset cached discoverable state: the advertising infrastructure was torn down + // before this init, so we must re-drive gap_le_slave_set_discoverable() if needed. + s_last_ble_discoverable_state = false; + bt_pairability_update_due_to_bonding_change(); + prv_schedule_evaluation(); +} diff --git a/src/fw/services/common/bluetooth/pebble_pairing_service.c b/src/fw/services/bluetooth/pebble_pairing_service.c similarity index 79% rename from src/fw/services/common/bluetooth/pebble_pairing_service.c rename to src/fw/services/bluetooth/pebble_pairing_service.c index 1f99999e95..a09809bda5 100644 --- a/src/fw/services/common/bluetooth/pebble_pairing_service.c +++ b/src/fw/services/bluetooth/pebble_pairing_service.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -24,6 +11,8 @@ #include "kernel/pbl_malloc.h" #include "system/logging.h" +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + extern void gap_le_connect_params_re_evaluate(GAPLEConnection *connection); static void prv_convert_pps_request_params(const PebblePairingServiceConnParamSet *pps_params_in, @@ -32,9 +21,9 @@ static void prv_convert_pps_request_params(const PebblePairingServiceConnParamSe params_out->connection_interval_min_1_25ms = min_1_25ms; params_out->connection_interval_max_1_25ms = min_1_25ms + pps_params_in->interval_max_delta_1_25ms; -#if RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW if (pps_params_in->slave_latency_events != 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "Overriding requested slave latency with 0 because PRF"); + PBL_LOG_DBG("Overriding requested slave latency with 0 because PRF"); } params_out->slave_latency_events = 0; #else @@ -49,7 +38,7 @@ static void prv_handle_set_remote_param_mgmt_settings(GAPLEConnection *connectio settings->is_remote_device_managing_connection_parameters; connection->is_remote_device_managing_connection_parameters = is_remote_device_managing_connection_parameters; - PBL_LOG(LOG_LEVEL_INFO, "PPS: is_remote_mgmt=%u", + PBL_LOG_INFO("PPS: is_remote_mgmt=%u", is_remote_device_managing_connection_parameters); if (settings_length >= PEBBLE_PAIRING_SERVICE_REMOTE_PARAM_MGTM_SETTINGS_SIZE_WITH_PARAM_SETS) { @@ -63,8 +52,7 @@ static void prv_handle_set_remote_param_mgmt_settings(GAPLEConnection *connectio &settings->connection_parameter_sets[s]; GAPLEConnectRequestParams *params = &connection->connection_parameter_sets[s]; prv_convert_pps_request_params(pps_params, params); - PBL_LOG(LOG_LEVEL_INFO, - "PPS: Updated param set %u: %u-%u, slave lat: %u, supervision timeout: %u", + PBL_LOG_INFO("PPS: Updated param set %u: %u-%u, slave lat: %u, supervision timeout: %u", s, params->connection_interval_min_1_25ms, params->connection_interval_max_1_25ms, params->slave_latency_events, params->supervision_timeout_10ms); } @@ -77,7 +65,7 @@ static void prv_handle_set_remote_param_mgmt_settings(GAPLEConnection *connectio static void prv_handle_set_remote_desired_state(GAPLEConnection *connection, const PebblePairingServiceRemoteDesiredState *desired_state) { const ResponseTimeState remote_desired_state = (ResponseTimeState)desired_state->state; - PBL_LOG(LOG_LEVEL_INFO, "PPS: desired_state=%u", remote_desired_state); + PBL_LOG_INFO("PPS: desired_state=%u", remote_desired_state); // "As a safety measure, the watch will reset it back to ResponseTimeMax after 5 minutes." const uint16_t max_period_secs = 5 * 60; @@ -107,13 +95,13 @@ void bt_driver_cb_pebble_pairing_service_handle_connection_parameter_write( prv_handle_set_remote_desired_state(connection, &conn_params->remote_desired_state); break; case PebblePairingServiceConnParamsWriteCmd_EnablePacketLengthExtension: - PBL_LOG(LOG_LEVEL_INFO, "Enabling BLE Packet Length Extension"); + PBL_LOG_INFO("Enabling BLE Packet Length Extension"); break; case PebblePairingServiceConnParamsWriteCmd_InhibitBLESleep: - PBL_LOG(LOG_LEVEL_INFO, "BLE Sleep Mode inhibited!"); + PBL_LOG_INFO("BLE Sleep Mode inhibited!"); break; default: - PBL_LOG(LOG_LEVEL_ERROR, "Unknown write_cmd %d", conn_params->cmd); + PBL_LOG_ERR("Unknown write_cmd %d", conn_params->cmd); break; } } diff --git a/src/fw/services/bluetooth/pp_ble_control.c b/src/fw/services/bluetooth/pp_ble_control.c new file mode 100644 index 0000000000..90926bd2f8 --- /dev/null +++ b/src/fw/services/bluetooth/pp_ble_control.c @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/pp_ble_control.h" +#include "pbl/services/bluetooth/pairability.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" + +PBL_LOG_MODULE_DECLARE(service_bluetooth, CONFIG_SERVICE_BLUETOOTH_LOG_LEVEL); + +typedef enum { + // Values 0 - 3 are deprecated, do not use. + BLEControlCommandTypeSetDiscoverablePairable = 4, +} BLEControlCommandType; + +typedef struct PACKED { + uint8_t opcode; + bool discoverable_pairable; + uint16_t duration; +} BLEControlCommandSetDiscoverablePairable; + +// ----------------------------------------------------------------------------- +//! Handler for the "Set Discoverable & Pairable" command +static void prv_handle_set_discoverable_pairable( + BLEControlCommandSetDiscoverablePairable *cmd_data) { + bt_pairability_use_ble_for_period(cmd_data->duration); + PBL_LOG_INFO("Set Discoverable Pairable: %u, %u", + cmd_data->discoverable_pairable, cmd_data->duration); +} + +// ----------------------------------------------------------------------------- +//! Pebble protocol handler for the BLE control endpoint +void pp_ble_control_protocol_msg_callback( + CommSession* session, const uint8_t *data, unsigned int length) { + PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground); + + if (length < sizeof(BLEControlCommandSetDiscoverablePairable)) { + PBL_LOG_WRN("Invalid pp_ble_control_protocol_msg_callback message: %d", length); + return; + } + + const uint8_t opcode = *(const uint8_t *) data; + switch (opcode) { + case 0 ... 3: + PBL_LOG_INFO("Deprecated & unsupported opcode: %u", opcode); + break; + + case BLEControlCommandTypeSetDiscoverablePairable: { + BLEControlCommandSetDiscoverablePairable *cmd_data = + (BLEControlCommandSetDiscoverablePairable *)data; + prv_handle_set_discoverable_pairable(cmd_data); + break; + } + default: + PBL_LOG_DBG("Unknown opcode %u", opcode); + break; + } +} diff --git a/src/fw/services/bluetooth/wscript_build b/src/fw/services/bluetooth/wscript_build new file mode 100644 index 0000000000..d95fc8865b --- /dev/null +++ b/src/fw/services/bluetooth/wscript_build @@ -0,0 +1,31 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'ble_bas.c', + 'bluetooth_ctl.c', + 'bluetooth_persistent_storage_debug.c', + 'bluetooth_prompt.c', + 'bonding.c', + 'dis.c', + 'local_addr.c', + 'local_id.c', + 'pairability.c', + 'pebble_pairing_service.c', + 'pp_ble_control.c', +] + +if bld.env.VARIANT != 'prf': + sources.append('bluetooth_persistent_storage_normal.c') + if bld.env.CONFIG_HRM: + sources.append('ble_hrm.c') +else: + sources.append('bluetooth_persistent_storage_prf.c') + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_bluetooth', +) + +bld.env.SERVICES.append('services_bluetooth') diff --git a/src/fw/services/boot_splash/Kconfig b/src/fw/services/boot_splash/Kconfig new file mode 100644 index 0000000000..71d86b325e --- /dev/null +++ b/src/fw/services/boot_splash/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_BOOT_SPLASH + bool "Boot splash" + help + Boot splash screen service. diff --git a/src/fw/services/boot_splash/service.c b/src/fw/services/boot_splash/service.c new file mode 100644 index 0000000000..1951aa9bdc --- /dev/null +++ b/src/fw/services/boot_splash/service.c @@ -0,0 +1,214 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/boot_splash.h" + +#if defined(CONFIG_PBLBOOT) || defined(CONFIG_QEMU) + +#include "board/display.h" +#include "board/splash.h" +#include "drivers/display/display.h" +#include "kernel/pbl_malloc.h" +#include "kernel/util/sleep.h" + +#include "FreeRTOS.h" +#include "task.h" + +#include + +// Platform-specific colors: PebbleOS uses ARGB2222 (GColor8), not raw RGB332 +#ifdef CONFIG_QEMU +// QEMU display natively renders ARGB2222 +#define SPLASH_COLOR_WHITE 0xFF // A=3, R=3, G=3, B=3 +#define SPLASH_COLOR_BLACK 0xC0 // A=3, R=0, G=0, B=0 +#define SPLASH_COLOR_LGRAY 0xEA // A=3, R=2, G=2, B=2 +// QEMU boot splash always uses 8bpp framebuffer regardless of platform bit depth +#define BOOT_SPLASH_FB_BYTES (PBL_DISPLAY_WIDTH * PBL_DISPLAY_HEIGHT) +#else +// Hardware displays: the compositor handles GColor8 → native conversion, +// but boot_splash writes raw pixels before the compositor is initialized. +// On obelix/getafix the LCDC accepts RGB332 directly. +#define SPLASH_COLOR_WHITE 0xFF +#define SPLASH_COLOR_BLACK 0x00 +#define SPLASH_COLOR_LGRAY 0xB6 +#define BOOT_SPLASH_FB_BYTES DISPLAY_FRAMEBUFFER_BYTES +#endif + +// Progress bar configuration +#define PROGRESS_BAR_WIDTH 80 +#define PROGRESS_BAR_HEIGHT 4 +#define PROGRESS_INDICATOR_WIDTH 20 +#define PROGRESS_FRAME_DELAY_MS 100 +#define PROGRESS_TOTAL_FRAMES 20 +#define BOOT_SPLASH_TASK_STACK_SIZE 512 +#define BOOT_SPLASH_TASK_PRIORITY (configMAX_PRIORITIES - 2) + +// Boot splash state +static TaskHandle_t s_boot_splash_task; +static volatile bool s_boot_splash_running; +static uint8_t *s_boot_splash_fb; + +// Draw a filled rectangle +static void prv_draw_filled_rect(uint8_t *fb, int16_t x0, int16_t y0, + int16_t width, int16_t height, uint8_t color) { + for (int16_t y = y0; y < y0 + height; y++) { + for (int16_t x = x0; x < x0 + width; x++) { + if (x >= 0 && x < PBL_DISPLAY_WIDTH && y >= 0 && y < PBL_DISPLAY_HEIGHT) { + fb[(uint32_t)y * PBL_DISPLAY_WIDTH + x] = color; + } + } + } +} + +// Draw progress bar with animated indicator +static void prv_draw_progress_bar(uint8_t *fb, int16_t center_x, int16_t center_y, + uint16_t frame) { + const uint8_t COLOR_TRACK = SPLASH_COLOR_LGRAY; + const uint8_t COLOR_INDICATOR = SPLASH_COLOR_BLACK; + + int16_t bar_x0 = center_x - PROGRESS_BAR_WIDTH / 2; + int16_t bar_y0 = center_y - PROGRESS_BAR_HEIGHT / 2; + + // Draw track (background) + prv_draw_filled_rect(fb, bar_x0, bar_y0, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT, COLOR_TRACK); + + // Calculate indicator position (ping-pong animation) + int32_t max_travel = PROGRESS_BAR_WIDTH - PROGRESS_INDICATOR_WIDTH; + int32_t half_frames = PROGRESS_TOTAL_FRAMES / 2; + int32_t cycle_frame = frame % PROGRESS_TOTAL_FRAMES; + int32_t indicator_offset; + + if (cycle_frame < half_frames) { + // Moving right + indicator_offset = (cycle_frame * max_travel) / half_frames; + } else { + // Moving left + indicator_offset = max_travel - ((cycle_frame - half_frames) * max_travel) / half_frames; + } + + // Draw indicator + prv_draw_filled_rect(fb, bar_x0 + indicator_offset, bar_y0, + PROGRESS_INDICATOR_WIDTH, PROGRESS_BAR_HEIGHT, COLOR_INDICATOR); +} + +// Boot splash task - runs until stopped +static void prv_boot_splash_task(void *param) { + uint8_t *fb = s_boot_splash_fb; + uint16_t frame = 0; + + // Get splash logo dimensions + const uint16_t logo_width = splash_width; + const uint16_t logo_height = splash_height; + const uint8_t *logo_data = splash_bits; + + // Calculate logo position (centered) + uint16_t logo_x0 = (PBL_DISPLAY_WIDTH - logo_width) / 2; + uint16_t logo_y0 = (PBL_DISPLAY_HEIGHT - logo_height) / 2; + + // Calculate progress bar center position (below centered logo) + int16_t progress_cx = PBL_DISPLAY_WIDTH / 2; + int16_t progress_cy = logo_y0 + logo_height + 20; + + // Animation loop - runs until s_boot_splash_running is cleared + while (s_boot_splash_running) { + // Clear framebuffer to white + memset(fb, SPLASH_COLOR_WHITE, BOOT_SPLASH_FB_BYTES); + + // Draw logo + for (uint16_t y = 0U; y < logo_height; y++) { + for (uint16_t x = 0U; x < logo_width; x++) { + if (logo_data[y * (logo_width / 8) + x / 8] & (0x1U << (x & 7))) { + fb[(y + logo_y0) * PBL_DISPLAY_WIDTH + (x + logo_x0)] = SPLASH_COLOR_BLACK; + } + } + } + + // Draw progress bar + prv_draw_progress_bar(fb, progress_cx, progress_cy, frame); + + // Send frame to display + display_update_boot_frame(fb); + + frame++; + + // Wait for next frame using short delays to allow quick stop + for (uint8_t i = 0; i < (PROGRESS_FRAME_DELAY_MS / 10) && s_boot_splash_running; i++) { + vTaskDelay(pdMS_TO_TICKS(10)); + } + } + + // Cleanup + kernel_free(fb); + s_boot_splash_fb = NULL; + s_boot_splash_task = NULL; + vTaskDelete(NULL); +} + +void boot_splash_start(void) { + // Initialize the display + display_init(); + + // Allocate framebuffer for boot splash + s_boot_splash_fb = kernel_malloc(BOOT_SPLASH_FB_BYTES); + if (!s_boot_splash_fb) { + return; + } + + // Start the boot splash task + s_boot_splash_running = true; + xTaskCreate(prv_boot_splash_task, + "BootSplash", + BOOT_SPLASH_TASK_STACK_SIZE, + NULL, + BOOT_SPLASH_TASK_PRIORITY, + &s_boot_splash_task); +} + +void boot_splash_stop(void) { + if (s_boot_splash_running && s_boot_splash_task != NULL) { + s_boot_splash_running = false; + // Wait for the splash task to finish (max ~10ms delay due to short sleep intervals) + while (s_boot_splash_task != NULL) { + psleep(10); + } + + // Draw final frame with logo only (no progress bar) + uint8_t *fb = kernel_malloc(BOOT_SPLASH_FB_BYTES); + if (fb) { + memset(fb, SPLASH_COLOR_WHITE, BOOT_SPLASH_FB_BYTES); + + // Get splash logo dimensions + const uint16_t logo_width = splash_width; + const uint16_t logo_height = splash_height; + const uint8_t *logo_data = splash_bits; + + // Calculate logo position (centered, same as animation) + uint16_t logo_x0 = (PBL_DISPLAY_WIDTH - logo_width) / 2; + uint16_t logo_y0 = (PBL_DISPLAY_HEIGHT - logo_height) / 2; + + // Draw logo + for (uint16_t y = 0U; y < logo_height; y++) { + for (uint16_t x = 0U; x < logo_width; x++) { + if (logo_data[y * (logo_width / 8) + x / 8] & (0x1U << (x & 7))) { + fb[(y + logo_y0) * PBL_DISPLAY_WIDTH + (x + logo_x0)] = SPLASH_COLOR_BLACK; + } + } + } + + display_update_boot_frame(fb); + kernel_free(fb); + } + } +} + +#else // Everything else + +void boot_splash_start(void) { + // No-op on platforms where the bootloader handles the splash +} + +void boot_splash_stop(void) { + // No-op on platforms where the bootloader handles the splash +} + +#endif diff --git a/src/fw/services/boot_splash/wscript_build b/src/fw/services/boot_splash/wscript_build new file mode 100644 index 0000000000..0062513d61 --- /dev/null +++ b/src/fw/services/boot_splash/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_boot_splash', +) +bld.env.SERVICES.append('services_boot_splash') diff --git a/src/fw/services/clock/Kconfig b/src/fw/services/clock/Kconfig new file mode 100644 index 0000000000..361a9292ab --- /dev/null +++ b/src/fw/services/clock/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_CLOCK + bool "Clock" + help + Wall-clock time formatting and events. + +if SERVICE_CLOCK + +module = SERVICE_CLOCK +module-str = Clock +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/clock/service.c b/src/fw/services/clock/service.c new file mode 100644 index 0000000000..660740f15e --- /dev/null +++ b/src/fw/services/clock/service.c @@ -0,0 +1,1066 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/clock.h" + +#include "console/prompt.h" +#include "drivers/rtc.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/timezone_database.h" +#include "pbl/services/wakeup.h" +#include "shell/prefs.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "util/attributes.h" +#include "util/math.h" +#include "util/net.h" +#include "util/size.h" +#include "util/string.h" +#include "pbl/services/analytics/analytics.h" + +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_score.h" +#endif + +#include + +PBL_LOG_MODULE_DEFINE(service_clock, CONFIG_SERVICE_CLOCK_LOG_LEVEL); + +// NOTE: There are CONFIG_RECOVERY_FW ifdefs in this file because PRF does not have +// timezone support + +#define UNKNOWN_TIMEZONE_ID (-1) + +static const uint16_t protocol_time_endpoint_id = 11; + +static RegularTimerInfo s_dst_checker; + +#ifndef CONFIG_RECOVERY_FW +// Armed on the first timer tick, after init has settled. +static bool s_hourly_chime_armed; +#endif + +static time_t prv_migrate_local_time_to_UTC(time_t local_time) { + return time_local_to_utc(local_time); +} + +// Should only called by prv_update_time_info_and_generate_event()! +static void prv_handle_timezone_set(TimezoneInfo *tz_info) { + time_util_update_timezone(tz_info); + + // Update the RTC registers with the latest timezone info + rtc_set_timezone(tz_info); + +} + +typedef struct PACKED { +// This struct is packed because it mirrors the endpoint definition: +// https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=491698#PebbleProtocol(BluetoothSerial)-0xb(11)-Time/Clock(bigendian) + time_t utc_time; // UTC timestamp + int16_t utc_offset_min; // local timestamp - UTC timestamp in mins + int8_t region_name_len; // timezone name length + char region_name[TIMEZONE_NAME_LENGTH]; // timezone name string +} TimezoneCBData; + +#ifndef UNITTEST +_Static_assert(sizeof(time_t) == 4, "Sizeof time_t does not match endpoint definition"); +#endif + +#if !defined(CONFIG_RECOVERY_FW) +static time_t prv_clock_dstrule_to_timestamp( + bool is_end, const TimezoneInfo *tz_info, const TimezoneDSTRule *rule, int year) { + + struct tm time_tm = { + .tm_min = rule->minute, + .tm_hour = rule->hour, + .tm_mday = rule->mday, + .tm_mon = rule->month, + .tm_year = year, + .tm_gmtoff = 0, + .tm_isdst = 0, + }; + // A few countries actually have their DST rule on the midnight AFTER a day + // This is subtly different from the midnight OF a day. + if (rule->hour >= HOURS_PER_DAY) { + time_tm.tm_hour %= HOURS_PER_DAY; + } + // Brazil delays DST end by one week every 3 years for elections + if (tz_info->dst_id == DSTID_BRAZIL && (((TM_YEAR_ORIGIN + year) % 3) == 2) && is_end) { + time_tm.tm_mday += DAYS_PER_WEEK; + } + time_t uxtime = mktime(&time_tm); + gmtime_r(&uxtime, &time_tm); + + for (int i = 0; i < DAYS_PER_WEEK; i++) { // max is DAYS_PER_WEEK to find a day_of_week + // we also have to check month here, as leap-year case puts us 1 day past feb +#define DSTRULE_WDAY_ANY (255) + if ((time_tm.tm_wday == rule->wday || rule->wday == DSTRULE_WDAY_ANY) && + time_tm.tm_mon == rule->month) { + break; + } + time_tm.tm_mday += (rule->flag & TIMEZONE_FLAG_DAY_DECREMENT) ? -1 : 1; + uxtime = mktime(&time_tm); + gmtime_r(&uxtime, &time_tm); + } + + if (rule->hour >= HOURS_PER_DAY) { + time_tm.tm_mday += rule->hour / HOURS_PER_DAY; + uxtime = mktime(&time_tm); + gmtime_r(&uxtime, &time_tm); + } + + if (rule->flag & TIMEZONE_FLAG_STANDARD_TIME) { // Standard time (not wall time) + time_tm.tm_gmtoff = tz_info->tm_gmtoff; + time_tm.tm_isdst = 0; + } else if (rule->flag & TIMEZONE_FLAG_UTC_TIME) { // UTC + time_tm.tm_gmtoff = 0; + time_tm.tm_isdst = 0; + } else { // Wall time + time_tm.tm_gmtoff = tz_info->tm_gmtoff; + time_tm.tm_isdst = is_end; + } + // Lord Howe Island has a half-hour DST + if (tz_info->dst_id == DSTID_LORDHOWE) { + uxtime -= time_tm.tm_isdst ? SECONDS_PER_HOUR / 2 : 0; + } else { + uxtime -= time_tm.tm_isdst ? SECONDS_PER_HOUR : 0; + } + uxtime -= time_tm.tm_gmtoff; + return uxtime; +} +#endif // CONFIG_RECOVERY_FW + +T_STATIC void prv_update_dstrule_timestamps_by_dstzone_id(TimezoneInfo *tz_info, time_t utc_time) { + if (tz_info->dst_id == 0) { + tz_info->dst_start = 0; + tz_info->dst_end = 0; + return; + } + +#if defined(CONFIG_RECOVERY_FW) + return; +#else + + // Load the pair of TimezoneDSTRule objects from the timezone database + + TimezoneDSTRule dst_rule_begin; + TimezoneDSTRule dst_rule_end; + + if (!timezone_database_load_dst_rule(tz_info->dst_id, &dst_rule_begin, &dst_rule_end)) { + // No DST rule or invalid DST ID. Either way just clear the DST information. + tz_info->dst_start = 0; + tz_info->dst_end = 0; + return; + } + + struct tm current_tm; + gmtime_r(&utc_time, ¤t_tm); + + // Calculate the timestamps of the start and ends of DST for the previous year, the current + // year, and the next year. + +#define DST_YEARS_RANGE 3 +#define DST_YEARS_OFFSET 1 + time_t dst_start_stamps[DST_YEARS_RANGE]; + time_t dst_end_stamps[DST_YEARS_RANGE]; + for (int i = 0; i < DST_YEARS_RANGE; i++) { + const int year = current_tm.tm_year + (i - DST_YEARS_OFFSET); + + dst_start_stamps[i] = + prv_clock_dstrule_to_timestamp(false, tz_info, &dst_rule_begin, year); + dst_end_stamps[i] = + prv_clock_dstrule_to_timestamp(true, tz_info, &dst_rule_end, year); + } + + // Figure out which timestamps are relevant to us + + int start_idx = DST_YEARS_OFFSET; + int end_idx = DST_YEARS_OFFSET; + + if (dst_start_stamps[start_idx] > dst_end_stamps[end_idx]) { + start_idx--; + } + + if (dst_start_stamps[start_idx] < utc_time && dst_end_stamps[end_idx] < utc_time) { + start_idx++; + end_idx++; + } + + tz_info->dst_start = dst_start_stamps[start_idx]; + tz_info->dst_end = dst_end_stamps[end_idx]; + +#endif // CONFIG_RECOVERY_FW +} + +static void prv_clock_get_timezone_info_from_region_id( + int16_t region_id, time_t utc_time, TimezoneInfo *tz_info) { + +#ifdef CONFIG_RECOVERY_FW + *tz_info = (TimezoneInfo) { .dst_id = 0 }; +#else + timezone_database_load_region_info(region_id, tz_info); + prv_update_dstrule_timestamps_by_dstzone_id(tz_info, utc_time); +#endif +} + +static TimezoneInfo prv_get_timezone_info_from_data(TimezoneCBData *tz_data) { + int region_id = -1; + + if (tz_data->region_name_len) { + region_id = timezone_database_find_region_by_name(tz_data->region_name, + tz_data->region_name_len); + } + + if (region_id != -1) { + // We have a valid region! + TimezoneInfo tz_info; + prv_clock_get_timezone_info_from_region_id(region_id, tz_data->utc_time, &tz_info); + return tz_info; + } + + // Else, we couldn't find find the specified timezone. +#ifndef CONFIG_RECOVERY_FW + TimezoneInfo tz_info = { + .dst_id = 0, + .timezone_id = UNKNOWN_TIMEZONE_ID, + .tm_gmtoff = tz_data->utc_offset_min * SECONDS_PER_MINUTE, + .dst_start = 0, + .dst_end = 0, + }; + + // I was hoping to fill the name with something like UTC-10 or UTC+4.25 but we only get 5 chars + strncpy(tz_info.tm_zone, "N/A", TZ_LEN - 1); + return tz_info; +#else + return (TimezoneInfo) {}; +#endif +} + +// This routine is solely responsible for setting the time and/or timezone for +// the system RTC. After the time is changed, it generates an event for +// consumers interested in time changes +T_STATIC void prv_update_time_info_and_generate_event(time_t *t, TimezoneInfo *tz_info) { + int orig_gmt_offset = time_get_gmtoffset(); + time_t orig_utc_time = rtc_get_time(); + TimezoneInfo tz_adjust_info = {{0}}; + + if (clock_is_timezone_set()) { // We'll need to update timezone stamps. + time_t tz_adjust_time; + // Get the time that we need to adjust for. + if (t) { + tz_adjust_time = *t; + } else { + tz_adjust_time = orig_utc_time; + } + if (tz_info) { // Adjust the DST rule timestamps of the provided tz_info + prv_update_dstrule_timestamps_by_dstzone_id(tz_info, tz_adjust_time); + } else if (clock_get_timezone_region_id() != UNKNOWN_TIMEZONE_ID) { + // If we have a timezone _actually_ set, update our own. + int region_id = clock_get_timezone_region_id(); + prv_clock_get_timezone_info_from_region_id(region_id, tz_adjust_time, &tz_adjust_info); + tz_info = &tz_adjust_info; // We need to set timezone info so point to the new info. + } + } + + // Note: update the timezone before setting the utc time. (If we set the utc + // time first we could wind up accidentally applying the timezone correction + // to that value in the case where no timezone data previously existed + // ... such as a migration from legacy firmware or after the RTC backup + // domain had completely powered down) + if (tz_info) { + prv_handle_timezone_set(tz_info); + } + + if (t) { + rtc_set_time(*t); + } + + int new_gmt_offset = time_get_gmtoffset(); + int new_utc_time = rtc_get_time(); + + PBL_ANALYTICS_SET_SIGNED(utc_offset_s, new_gmt_offset); + + PebbleEvent e = { + .type = PEBBLE_SET_TIME_EVENT, + .set_time_info = { + .utc_time_delta = new_utc_time - orig_utc_time, + .gmt_offset_delta = new_gmt_offset - orig_gmt_offset, + .dst_changed = false, + } + }; + event_put(&e); +} + +static void prv_handle_set_utc_and_timezone_msg(TimezoneCBData *tz_data) { + tz_data->utc_time = ntohl(tz_data->utc_time); + tz_data->utc_offset_min = ntohs(tz_data->utc_offset_min); + + const char *region_name = tz_data->region_name; + if (tz_data->region_name_len == 0) { + region_name = "[N/A]"; + } + PBL_LOG_INFO("set_timezone utc_time: %u offset: %d region_name: %s", + (int) tz_data->utc_time, (int) tz_data->utc_offset_min, region_name); + + TimezoneInfo tz_info = prv_get_timezone_info_from_data(tz_data); + shell_prefs_set_automatic_timezone_id(tz_info.timezone_id); + if (clock_time_source_is_manual()) { + // Manual time mode: ignore time set from phone entirely + return; + } + if (clock_timezone_source_is_manual()) { + prv_update_time_info_and_generate_event(&tz_data->utc_time, NULL); + } else { + prv_update_time_info_and_generate_event(&tz_data->utc_time, &tz_info); + } +} + +static void prv_handle_set_time_msg(time_t new_time) { + PBL_LOG_WRN("Mobile app calling deprecated API, time = %d", + (int)new_time); + if (clock_time_source_is_manual()) { + // Manual time mode: ignore time set from phone + return; + } + if (clock_is_timezone_set()) { + new_time = prv_migrate_local_time_to_UTC(new_time); + } + + prv_update_time_info_and_generate_event(&new_time, NULL); +} + +void clock_protocol_msg_callback(CommSession *session, const uint8_t* data, unsigned int length) { + char sub_command = *data++; + + switch (sub_command) { + // Get time request: + case 0x00: { + time_t t = rtc_get_time(); + + // Send Get time response (0x01): + const unsigned int response_buffer_length = 1 + 4; + uint8_t response_buffer[response_buffer_length]; + + response_buffer[0] = 0x01; + + *(uint32_t*)(response_buffer + 1) = htonl(t); + + comm_session_send_data(session, protocol_time_endpoint_id, response_buffer, + response_buffer_length, COMM_SESSION_DEFAULT_TIMEOUT); + PBL_LOG_VERBOSE("protocol_time_callback called, responding with current time: %"PRIu32, + (uint32_t)t); + break; + } + // Set time: + case 0x02: { + time_t new_time = ntohl(*(uint32_t*)data); + prv_handle_set_time_msg(new_time); + break; + } + // Set timezone: + case 0x03: { + // Verify that the message length is correct + const size_t header_size = offsetof(TimezoneCBData, region_name); + const uint8_t *timezone_length_ptr = data + offsetof(TimezoneCBData, region_name_len); + if (length != (sizeof(uint8_t) + header_size + *timezone_length_ptr)) { + PBL_LOG_WRN("Set timezone message invalid length"); + return; + } + + TimezoneCBData *timezone_data = (TimezoneCBData *)data; + prv_handle_set_utc_and_timezone_msg(timezone_data); + break; + } + // Request time: sent by the watch to ask the phone for its current time. + // The phone should respond with a 0x03 (set UTC + timezone) message. + // If the phone sends this to the watch, just ignore it. + case 0x04: + break; + default: + PBL_LOG_WRN("Invalid message received. First byte is %u", data[0]); + break; + } +} + +// TODO: Using a regular timer is pretty gross... +T_STATIC void prv_watch_dst(void* user) { + const bool was_dst = (bool)user; + const bool is_dst = time_get_isdst(rtc_get_time()); + +#ifndef CONFIG_RECOVERY_FW + if (!s_hourly_chime_armed) { + s_hourly_chime_armed = true; + } else if (alerts_should_vibrate_for_type(AlertOther) && + (time_utc_to_local(rtc_get_time()) % SECONDS_PER_HOUR == 0)) { + uint32_t vibe_id = vibe_score_info_get_resource_id( + alerts_preferences_get_vibe_score_for_client(VibeClient_Hourly)); + VibeScore *score = vibe_score_create_with_resource_system(0, vibe_id); + if (score) { + vibe_score_do_vibe(score); + vibe_score_destroy(score); + } + } +#endif + + if (is_dst != was_dst) { + PebbleEvent e = { + .type = PEBBLE_SET_TIME_EVENT, + .set_time_info = { + .utc_time_delta = 0, + .gmt_offset_delta = 0, + .dst_changed = true, + } + }; + event_put(&e); + s_dst_checker.cb_data = (void*)is_dst; + } +} + +// Minimum valid time: January 1, 2010 00:00:00 UTC +// On RTC loss, Asterix boots to 1970 and Obelix to 2000 +// Snap forward so time isnt *that* off +#define MIN_VALID_BOOT_TIMESTAMP 1262304000 + +void clock_init(void) { + if (rtc_get_time() < MIN_VALID_BOOT_TIMESTAMP) { + rtc_set_time(MIN_VALID_BOOT_TIMESTAMP); + } + + if (clock_is_timezone_set()) { + TimezoneInfo tz_info; + rtc_get_timezone(&tz_info); + time_util_update_timezone(&tz_info); + } + // TODO: Using a regular timer is pretty gross... + s_dst_checker = (RegularTimerInfo) { + .cb = prv_watch_dst, + .cb_data = (void*)time_get_isdst(rtc_get_time()), + }; +#ifndef CONFIG_RECOVERY_FW + s_hourly_chime_armed = false; +#endif + regular_timer_add_seconds_callback(&s_dst_checker); +} + +void clock_get_time_tm(struct tm* time_tm) { + rtc_get_time_tm(time_tm); +} + +size_t clock_format_time(char *buffer, uint8_t size, int16_t hours, int16_t minutes, + bool add_space) { + if (size == 0 || buffer == NULL) { + return 0; + } + + bool is24h = clock_is_24h_style(); + const char *format; + + // [INTL] you want to have layout resources that specify time formatting, + // and be able to set a default one for each locale. + if (is24h) { + format = "%u:%02u"; + } else { + if (hours < 12) { + format = add_space ? "%u:%02u AM" : "%u:%02uAM"; + } else { + format = add_space ? "%u:%02u PM" : "%u:%02uPM"; + } + } + return sniprintf(buffer, size, format, time_util_get_num_hours(hours, is24h), minutes); +} + +size_t clock_copy_time_string_timestamp(char *buffer, uint8_t size, time_t timestamp) { + struct tm time; + sys_localtime_r(×tamp, &time); + return clock_format_time(buffer, size, time.tm_hour, time.tm_min, true); +} + +void clock_copy_time_string(char *buffer, uint8_t size) { + time_t t = sys_get_time(); + clock_copy_time_string_timestamp(buffer, size, t); +} + +static size_t prv_format_time_tm(char *buffer, int buf_size, const char *format, + const struct tm *time_tm) { + const size_t ret_val = strftime(buffer, buf_size, i18n_get(format, buffer), time_tm); + i18n_free(format, buffer); + return ret_val; +} + +static size_t prv_format_time(char *buffer, int buf_size, const char *format, time_t timestamp) { + struct tm time_tm; + localtime_r(×tamp, &time_tm); + return prv_format_time_tm(buffer, buf_size, format, &time_tm); +} + +size_t clock_get_time_number(char *number_buffer, size_t number_buffer_size, time_t timestamp) { + const size_t written = + prv_format_time(number_buffer, number_buffer_size, + (clock_is_24h_style() ? i18n_noop("%R") : i18n_noop("%l:%M")), timestamp); + const char *number_buffer_ptr = string_strip_leading_whitespace(number_buffer); + memmove(number_buffer, + number_buffer_ptr, + number_buffer_size - (number_buffer_ptr - number_buffer)); + return written - (number_buffer_ptr - number_buffer); +} + +size_t clock_get_time_word(char *buffer, size_t buffer_size, time_t timestamp) { + if (clock_is_24h_style()) { + buffer[0] = '\0'; + return 0; + } else { + return prv_format_time(buffer, buffer_size, i18n_noop("%p"), timestamp); + } +} + +static void prv_copy_time_string_timestamp(char *number_buffer, uint8_t number_buffer_size, + char *word_buffer, uint8_t word_buffer_size, time_t timestamp) { + clock_get_time_number(number_buffer, number_buffer_size, timestamp); + clock_get_time_word(word_buffer, word_buffer_size, timestamp); +} + +static void prv_get_relative_all_day_string(char *buffer, int buffer_size, time_t timestamp) { + time_t today = time_util_get_midnight_of(rtc_get_time()); + if (time_util_get_midnight_of(timestamp) == today) { + i18n_get_with_buffer("Today", buffer, buffer_size); + } else { + i18n_get_with_buffer("All day", buffer, buffer_size); + } +} + +static void prv_copy_relative_time_string(char *number_buffer, uint8_t number_buffer_size, + char *word_buffer, uint8_t word_buffer_size, time_t timestamp, time_t end_time) { + time_t now = rtc_get_time(); + // average without overflows since time_t might be signed and now ~1.4 billion, so 2*now > INT_MAX + time_t midtime = timestamp / 2 + end_time / 2; + if (midtime > now) { // future + time_t difference = timestamp - now; + if (timestamp < now || difference < SECONDS_PER_MINUTE) { + i18n_get_with_buffer("Now", word_buffer, word_buffer_size); + strncpy(number_buffer, "", number_buffer_size); + } else if (difference <= SECONDS_PER_HOUR) { + snprintf(number_buffer, number_buffer_size, "%ld", difference / SECONDS_PER_MINUTE); + i18n_get_with_buffer(" MIN. TO", word_buffer, word_buffer_size); + } else { + prv_copy_time_string_timestamp(number_buffer, number_buffer_size, word_buffer, + word_buffer_size, timestamp); + } + } else { // past + time_t difference = now - timestamp; + if (now < timestamp || difference < SECONDS_PER_MINUTE) { + i18n_get_with_buffer("Now", word_buffer, word_buffer_size); + strncpy(number_buffer, "", number_buffer_size); + } else { + prv_copy_time_string_timestamp(number_buffer, number_buffer_size, word_buffer, + word_buffer_size, timestamp); + } + } +} + +// number: 10 +// word: min to +void clock_get_event_relative_time_string(char *number_buffer, int number_buffer_size, + char *word_buffer, int word_buffer_size, time_t timestamp, uint16_t duration, + time_t current_day, bool all_day) { + time_t end_time = timestamp + duration * SECONDS_PER_MINUTE; + if (all_day) { + // all day event, multiday or single day + prv_get_relative_all_day_string(word_buffer, word_buffer_size, current_day); + strncpy(number_buffer, "", number_buffer_size); + } else if (time_util_get_midnight_of(timestamp) == current_day) { + // first day of multiday event or only day + prv_copy_relative_time_string(number_buffer, number_buffer_size, word_buffer, + word_buffer_size, timestamp, end_time); + } else if (time_util_get_midnight_of(end_time) == current_day) { + // last day of multiday event + prv_copy_relative_time_string(number_buffer, number_buffer_size, word_buffer, + word_buffer_size, end_time, end_time); + } else { + // middle day of non-all day multiday event + prv_get_relative_all_day_string(word_buffer, word_buffer_size, current_day); + strncpy(number_buffer, "", number_buffer_size); + } +} + +DEFINE_SYSCALL(bool, clock_is_24h_style, void) { + return shell_prefs_get_clock_24h_style(); +} + +void clock_set_24h_style(bool is_24h_style) { + shell_prefs_set_clock_24h_style(is_24h_style); +} + +DEFINE_SYSCALL(bool, clock_is_timezone_set, void) { + return rtc_is_timezone_set(); // If timezone abbr isn't set +} + +bool clock_timezone_source_is_manual(void) { + return shell_prefs_is_timezone_source_manual(); +} + +void clock_set_manual_timezone_source(bool manual) { + shell_prefs_set_timezone_source_manual(manual); +} + +bool clock_time_source_is_manual(void) { + return shell_prefs_is_time_source_manual(); +} + +void clock_set_manual_time_source(bool manual) { + shell_prefs_set_time_source_manual(manual); +} + +void clock_request_time_from_phone(void) { + CommSession *session = comm_session_get_system_session(); + if (!session) { + PBL_LOG_VERBOSE("No system session, cannot request time from phone"); + return; + } + + // Send sub-command 0x04 (request time) to the phone. + // The phone should respond with a 0x03 (set UTC + timezone) message. + const uint8_t request[] = { 0x04 }; + comm_session_send_data(session, protocol_time_endpoint_id, request, + sizeof(request), COMM_SESSION_DEFAULT_TIMEOUT); +} + +DEFINE_SYSCALL(time_t, clock_to_timestamp, WeekDay day, int hour, int minute) { + time_t t = sys_get_time(); + struct tm cal; + sys_localtime_r(&t, &cal); + + if (day != TODAY) { + // If difference between WeekDay and current day, always in the future + day -= 1; // cal_wday is 0-6 + int day_offset = (day > cal.tm_wday) ? (day - cal.tm_wday) : (day - cal.tm_wday + 7); + cal.tm_mday += day_offset; // normalized by mktime + } else if ((hour < cal.tm_hour) || (hour == cal.tm_hour && minute <= cal.tm_min)){ + // Always return a future timestamp, so if day was today, and + // minutes and hours already occurred, just make it tomorrow + cal.tm_mday++; // normalized by mktime + } + + cal.tm_hour = hour; + cal.tm_min = minute; + cal.tm_sec = 0; + cal.tm_gmtoff = time_get_gmtoffset(); + cal.tm_isdst = 0; + + t = mktime(&cal); + if (time_get_isdst(t)) { + t -= time_get_dstoffset(); + if (!time_get_isdst(t)) { + t = time_get_dst_start(); + } + } + + return t; +} + +void command_timezone_clear(void) { + rtc_timezone_clear(); +} + +void command_get_time(void) { + char buffer[80]; + char time_buffer[26]; + prompt_send_response_fmt(buffer, 80, "Time is now <%s>", rtc_get_time_string(time_buffer)); +} + +void command_set_time(const char *arg) { + time_t t = atoi(arg); + if (t == 0) { + prompt_send_response("Invalid length"); + return; + } + + prv_update_time_info_and_generate_event(&t, NULL); + + char buffer[80]; + char time_buffer[26]; + prompt_send_response_fmt(buffer, 80, "Time is now <%s>", rtc_get_time_string(time_buffer)); +} + +void clock_get_timezone_region(char* region_name, const size_t buffer_size) { + if (!region_name) { + return; + } + + if (clock_is_timezone_set()) { + int region_id = clock_get_timezone_region_id(); + if (region_id != UNKNOWN_TIMEZONE_ID) { + timezone_database_load_region_name(region_id, region_name); + } else { + // Show something like UTC-4 or UTC-10.25 + // This will typically happen in the emulator when we know the UTC offset, but not + // the timezone (fallback case). + int gmt_offset_m = time_get_gmtoffset() / SECONDS_PER_MINUTE; + int hour_offset = gmt_offset_m / MINUTES_PER_HOUR; + + char min_buf[4] = {0}; + int min_offset_percent = ((ABS(gmt_offset_m) % MINUTES_PER_HOUR) * 100) / MINUTES_PER_HOUR; + if (min_offset_percent) { + snprintf(min_buf, sizeof(min_buf), ".%d", min_offset_percent); + } + snprintf(region_name, buffer_size, "UTC%+d%s", hour_offset, min_buf); + } + } else { + strncpy(region_name, "---", buffer_size); + } +} + +int16_t clock_get_timezone_region_id(void) { + return rtc_get_timezone_id(); +} + +void clock_set_timezone_by_region_id(uint16_t region_id) { + TimezoneInfo tz_info; + prv_clock_get_timezone_info_from_region_id(region_id, rtc_get_time(), &tz_info); + PBL_LOG_INFO("Set timezone by region id (%u)", region_id); + prv_update_time_info_and_generate_event(NULL, &tz_info); +} + +void clock_set_time(time_t utc_time) { + prv_update_time_info_and_generate_event(&utc_time, NULL); +} + +void clock_get_friendly_date(char *buffer, int buf_size, time_t timestamp) { + const time_t now = rtc_get_time(); + + const time_t midnight = time_util_get_midnight_of(timestamp); + const time_t today_midnight = time_util_get_midnight_of(now); + + if (midnight == today_midnight) { + i18n_get_with_buffer("Today", buffer, buf_size); + } else if (midnight == (today_midnight - SECONDS_PER_DAY)) { + i18n_get_with_buffer("Yesterday", buffer, buf_size); + } else if (midnight == (today_midnight + SECONDS_PER_DAY)) { + i18n_get_with_buffer("Tomorrow", buffer, buf_size); + } else if (midnight <= (today_midnight + (5 * SECONDS_PER_DAY))) { + // Use weekday name up to 5 days in the future, aka "Sunday" + prv_format_time(buffer, buf_size, i18n_noop("%A"), timestamp); + } else { + // Otherwise use "Month Day", aka "June 21" + prv_format_time(buffer, buf_size, i18n_noop("%B %d"), timestamp); + } +} + +enum { + RoundTypeHalfUp, + RoundTypeHalfDown, + RoundTypeAlwaysUp, + RoundTypeAlwaysDown, +}; + +static time_t prv_round(time_t round_me, time_t multiple, int round_type) { + switch (round_type) { + case RoundTypeHalfDown: + return ((round_me + multiple / 2 - 1) / multiple) * multiple; + case RoundTypeAlwaysUp: + return ((round_me + multiple - 1) / multiple) * multiple; + case RoundTypeAlwaysDown: + return (round_me / multiple) * multiple; + case RoundTypeHalfUp: + default: + return ((round_me + multiple / 2) / multiple) * multiple; + } +} + +enum { + FullStyleLower12h, + FullStyleCapital12h, + FullStyleLower24h, + FullStyleCapital24h, +}; + +static void prv_clock_get_full_relative_time(char *buffer, int buf_size, time_t timestamp, + bool capitalized, bool with_fulltime) { + time_t today_midnight = time_util_get_midnight_of(rtc_get_time()); + time_t timestamp_midnight = time_util_get_midnight_of(timestamp); + time_t yesterday_midnight = time_util_get_midnight_of(rtc_get_time() - SECONDS_PER_DAY); + time_t last_week_midnight = time_util_get_midnight_of(rtc_get_time() - SECONDS_PER_WEEK); + time_t next_week_midnight = time_util_get_midnight_of(rtc_get_time() + SECONDS_PER_WEEK); + + const char *time_fmt = NULL; + int style; + if (clock_is_24h_style()) { + if (capitalized) { + style = FullStyleCapital24h; + } else { + style = FullStyleLower24h; + } + } else { + if (capitalized) { + style = FullStyleCapital12h; + } else { + style = FullStyleLower12h; + } + } + + if (timestamp_midnight == today_midnight) { + switch (style) { + case FullStyleLower12h: + case FullStyleCapital12h: + time_fmt = i18n_noop("%l:%M %p"); + break; + case FullStyleLower24h: + case FullStyleCapital24h: + time_fmt = i18n_noop("%R"); + break; + } + } else if (timestamp_midnight == yesterday_midnight) { + switch (style) { + case FullStyleLower12h: + case FullStyleCapital12h: + if (with_fulltime) { + time_fmt = i18n_noop("Yesterday, %l:%M %p"); + } else { + time_fmt = i18n_noop("Yesterday"); + } + break; + case FullStyleLower24h: + case FullStyleCapital24h: + if (with_fulltime) { + time_fmt = i18n_noop("Yesterday, %R"); + } else { + time_fmt = i18n_noop("Yesterday"); + } + break; + } + } else if (timestamp_midnight <= last_week_midnight || timestamp_midnight >= next_week_midnight) { + switch (style) { + case FullStyleLower12h: + case FullStyleCapital12h: + if (with_fulltime) { + time_fmt = i18n_noop("%b %e, %l:%M %p"); + } else { + time_fmt = i18n_noop("%B %e"); + } + break; + case FullStyleLower24h: + case FullStyleCapital24h: + if (with_fulltime) { + time_fmt = i18n_noop("%b %e, %R"); + } else { + time_fmt = i18n_noop("%B %e"); + } + break; + } + } else { + switch (style) { + case FullStyleLower12h: + case FullStyleCapital12h: + if (with_fulltime) { + time_fmt = i18n_noop("%a, %l:%M %p"); + } else { + time_fmt = i18n_noop("%A"); + } + break; + case FullStyleLower24h: + case FullStyleCapital24h: + if (with_fulltime) { + time_fmt = i18n_noop("%a, %R"); + } else { + time_fmt = i18n_noop("%A"); + } + break; + } + } + prv_format_time(buffer, buf_size, time_fmt, timestamp); +} + +static void prv_clock_get_relative_time_string(char *buffer, int buf_size, time_t timestamp, + bool capitalized, int max_relative_hrs, + bool with_fulltime) { + time_t difference = rtc_get_time() - timestamp; + + time_t today_midnight = time_util_get_midnight_of(rtc_get_time()); + time_t timestamp_midnight = time_util_get_midnight_of(timestamp); + + if (today_midnight != timestamp_midnight) { + prv_clock_get_full_relative_time(buffer, buf_size, timestamp, capitalized, with_fulltime); + + } else if (difference >= (SECONDS_PER_HOUR * max_relative_hrs)) { + prv_clock_get_full_relative_time(buffer, buf_size, timestamp, capitalized, with_fulltime); + + } else if (difference >= SECONDS_PER_HOUR) { + const int num_hrs = + prv_round(difference, SECONDS_PER_HOUR, RoundTypeHalfUp) / SECONDS_PER_HOUR; + + const char *str_fmt; + if (capitalized) { + str_fmt = i18n_noop("%lu H AGO"); + } else if (num_hrs == 1) { + str_fmt = i18n_noop("An hour ago"); + } else { + str_fmt = i18n_noop("%lu hours ago"); + } + snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_hrs); + + } else if (difference >= SECONDS_PER_MINUTE) { + const int num_minutes = + prv_round(difference, SECONDS_PER_MINUTE, RoundTypeAlwaysDown) / SECONDS_PER_MINUTE; + + const char *str_fmt; + if (capitalized) { + str_fmt = i18n_noop("%lu MIN AGO"); + } else if (num_minutes == 1) { + str_fmt = i18n_noop("%lu minute ago"); + } else { + str_fmt = i18n_noop("%lu minutes ago"); + } + snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_minutes); + + } else if (difference >= 0) { + strncpy(buffer, capitalized ? i18n_get("NOW", buffer) : i18n_get("Now", buffer), buf_size); + + } else if (difference >= -(SECONDS_PER_HOUR - SECONDS_PER_MINUTE)) { + const int num_minutes = + prv_round(-difference, SECONDS_PER_MINUTE, RoundTypeAlwaysUp) / SECONDS_PER_MINUTE; + + const char *str_fmt; + if (capitalized) { + str_fmt = i18n_noop("IN %lu MIN"); + } else if (num_minutes == 1) { + str_fmt = i18n_noop("In %lu minute"); + } else { + str_fmt = i18n_noop("In %lu minutes"); + } + snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_minutes); + + } else if (difference >= -(SECONDS_PER_HOUR * max_relative_hrs)) { + const int num_hrs = + prv_round(-difference, SECONDS_PER_HOUR, RoundTypeHalfDown) / SECONDS_PER_HOUR; + + const char *str_fmt; + if (capitalized) { + str_fmt = i18n_noop("IN %lu H"); + } else if (num_hrs == 1) { + str_fmt = i18n_noop("In %lu hour"); + } else { + str_fmt = i18n_noop("In %lu hours"); + } + snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_hrs); + + } else { + prv_clock_get_full_relative_time(buffer, buf_size, timestamp, capitalized, with_fulltime); + } + i18n_free_all(buffer); +} + +size_t clock_get_date(char *buffer, int buf_size, time_t timestamp) { + return prv_format_time(buffer, buf_size, i18n_noop("%m/%d"), timestamp); +} + +size_t clock_get_date_tm(char *buffer, int buf_size, const struct tm *time_tm) { + return prv_format_time_tm(buffer, buf_size, i18n_noop("%m/%d"), time_tm); +} + +size_t clock_get_day_date(char *buffer, int buf_size, time_t timestamp) { + return prv_format_time(buffer, buf_size, i18n_noop("%d"), timestamp); +} + +static size_t prv_clock_get_month_named_date(char *buffer, size_t buffer_size, time_t timestamp, + bool abbrev) { + const char *format = abbrev ? i18n_noop("%b ") : i18n_noop("%B "); + const size_t month_size = prv_format_time(buffer, buffer_size, format, timestamp); + char *day_buffer = buffer + month_size; + const size_t day_buffer_size = buffer_size - month_size; + size_t day_size = prv_format_time(day_buffer, day_buffer_size, i18n_noop("%e"), timestamp); + const char *day_buffer_ptr = string_strip_leading_whitespace(day_buffer); + memmove(day_buffer, day_buffer_ptr, day_buffer_size - (day_buffer_ptr - day_buffer)); + return month_size + day_size; +} + +size_t clock_get_month_named_date(char *buffer, size_t buffer_size, time_t timestamp) { + const bool abbrev = false; + return prv_clock_get_month_named_date(buffer, buffer_size, timestamp, abbrev); +} + +size_t clock_get_month_named_abbrev_date(char *buffer, size_t buffer_size, time_t timestamp) { + const bool abbrev = true; + return prv_clock_get_month_named_date(buffer, buffer_size, timestamp, abbrev); +} + +void clock_get_since_time(char *buffer, int buf_size, time_t timestamp) { + const time_t now = rtc_get_time(); + const time_t clamped_timestamp = MIN(now, timestamp); + prv_clock_get_relative_time_string(buffer, buf_size, clamped_timestamp, false, HOURS_PER_DAY, + true); +} + +void clock_get_until_time(char *buffer, int buf_size, time_t timestamp, + int max_relative_hrs) { + prv_clock_get_relative_time_string(buffer, buf_size, timestamp, false, max_relative_hrs, true); +} + +void clock_get_until_time_capitalized(char *buffer, int buf_size, time_t timestamp, + int max_relative_hrs) { + prv_clock_get_relative_time_string(buffer, buf_size, timestamp, true, max_relative_hrs, true); +} + +void clock_get_until_time_without_fulltime(char *buffer, int buf_size, time_t timestamp, + int max_relative_hrs) { + prv_clock_get_relative_time_string(buffer, buf_size, timestamp, true, max_relative_hrs, false); +} + +DEFINE_SYSCALL(void, sys_clock_get_timezone, char *timezone, const size_t buffer_size) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(timezone, TIMEZONE_NAME_LENGTH); + } + clock_get_timezone_region(timezone, buffer_size); +} + +typedef struct daypart_message { + const uint32_t hour_offset; // hours from 12am of current day + const char* const message; // text containing daypart +} daypart_message; + +static const daypart_message daypart_messages[] = { + {0, i18n_noop("this morning")}, // anything before 12pm of the current day + {12, i18n_noop("this afternoon")}, // 12pm today + {18, i18n_noop("this evening")}, // 6pm today + {21, i18n_noop("tonight")}, // 9pm today + {33, i18n_noop("tomorrow morning")}, // 9am tomorrow + {36, i18n_noop("tomorrow afternoon")}, // 12pm tomorrow + {42, i18n_noop("tomorrow evening")}, // 6pm tomorrow + {45, i18n_noop("tomorrow night")}, // 9pm tomorrow + {57, i18n_noop("the day after tomorrow")}, // starting 9am 2 days from now + {72, i18n_noop("the day after tomorrow")}, // ends midnight 2 days from now + {73, i18n_noop("the foreseeable future")}, // Catchall for beyond 3 days +}; + +//! Daypart string is used internally for battery popups +//! and is a minimum threshold, ie. "Powered 'til at least"... +const char *clock_get_relative_daypart_string(time_t current_timestamp, + uint32_t hours_in_the_future) { + struct tm current_tm; + const char *message = NULL; + localtime_r(¤t_timestamp, ¤t_tm); + + // Look for the furthest time in the future that we are "above" + for (int i = ARRAY_LENGTH(daypart_messages) - 1; i >= 0; i--) { + if ((current_tm.tm_hour + hours_in_the_future) >= daypart_messages[i].hour_offset) { + message = daypart_messages[i].message; + break; + } + } + return message; +} + +void clock_hour_and_minute_add(int *hour, int *minute, int delta_minutes) { + const int new_minutes = positive_modulo(*hour * MINUTES_PER_HOUR + *minute + delta_minutes, + MINUTES_PER_DAY); + *hour = new_minutes / MINUTES_PER_HOUR; + *minute = new_minutes % MINUTES_PER_HOUR; +} diff --git a/src/fw/services/clock/wscript_build b/src/fw/services/clock/wscript_build new file mode 100644 index 0000000000..3099972845 --- /dev/null +++ b/src/fw/services/clock/wscript_build @@ -0,0 +1,13 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c'], + target='services_clock', +) +bld.env.SERVICES.append('services_clock') + +target = bld.path.find_or_declare('services_clock.pot') +bld.gettext(source=['service.c'], target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/comm_session/Kconfig b/src/fw/services/comm_session/Kconfig new file mode 100644 index 0000000000..3d999ea9de --- /dev/null +++ b/src/fw/services/comm_session/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_COMM_SESSION + bool "Comm session" + help + Pebble Protocol communication session management. + +if SERVICE_COMM_SESSION + +module = SERVICE_COMM_SESSION +module-str = Comm session +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/comm_session/app_session_capabilities.c b/src/fw/services/comm_session/app_session_capabilities.c new file mode 100644 index 0000000000..7d724c7881 --- /dev/null +++ b/src/fw/services/comm_session/app_session_capabilities.c @@ -0,0 +1,105 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "process_management/app_install_types.h" +#include "process_management/app_manager.h" +#include "pbl/services/comm_session/app_session_capabilities.h" +#include "pbl/services/settings/settings_file.h" +#include "os/mutex.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/units.h" + +#define APP_SESSION_CAPABILITIES_CACHE_FILENAME "app_comm" + +#define APP_SESSION_CAPABILITIES_CACHE_FILE_MAX_USED_SPACE (KiBYTES(2)) + +static PebbleMutex *s_mutex; + +static status_t prv_open(SettingsFile *settings_file) { + return settings_file_open(settings_file, APP_SESSION_CAPABILITIES_CACHE_FILENAME, + APP_SESSION_CAPABILITIES_CACHE_FILE_MAX_USED_SPACE); +} + +static status_t prv_open_locked(SettingsFile *settings_file) { + mutex_lock(s_mutex); + status_t status = prv_open(settings_file); + if (FAILED(status)) { + mutex_unlock(s_mutex); + } + return status; +} + +static void prv_close_and_unlock(SettingsFile *settings_file) { + settings_file_close(settings_file); + mutex_unlock(s_mutex); +} + +bool comm_session_current_app_session_cache_has_capability(CommSessionCapability capability) { + CommSession *app_session = comm_session_get_current_app_session(); + + const Uuid app_uuid = app_manager_get_current_app_md()->uuid; + + SettingsFile settings_file; + status_t open_status = prv_open_locked(&settings_file); + bool file_open = PASSED(open_status); + + uint64_t cached_capabilities = 0; + if (file_open) { + settings_file_get(&settings_file, + &app_uuid, sizeof(app_uuid), + &cached_capabilities, sizeof(cached_capabilities)); + } + + uint64_t new_capabilities = cached_capabilities; + if (app_session) { + // Connected, grab fresh capabilities data: + new_capabilities = comm_session_get_capabilities(app_session); + + if (file_open && (new_capabilities != cached_capabilities)) { + settings_file_set(&settings_file, + &app_uuid, sizeof(app_uuid), + &new_capabilities, sizeof(new_capabilities)); + } + } + + if (file_open) { + prv_close_and_unlock(&settings_file); + } + + return ((new_capabilities & capability) != 0); +} + +static void prv_rewrite_cb(SettingsFile *old_file, + SettingsFile *new_file, + SettingsRecordInfo *info, + void *context) { + if (!info->val_len) { + return; // Cache for this app has been deleted, don't rewrite it + } + Uuid key; + uint64_t val; + info->get_key(old_file, &key, sizeof(key)); + info->get_val(old_file, &val, sizeof(val)); + settings_file_set(new_file, &key, sizeof(key), &val, sizeof(val)); +} + +void comm_session_app_session_capabilities_evict(const Uuid *app_uuid) { + SettingsFile settings_file; + if (PASSED(prv_open_locked(&settings_file))) { + settings_file_delete(&settings_file, app_uuid, sizeof(*app_uuid)); + prv_close_and_unlock(&settings_file); + } +} + +void comm_session_app_session_capabilities_init(void) { + s_mutex = mutex_create(); + + PBL_ASSERTN(s_mutex != NULL); + + SettingsFile settings_file; + if (PASSED(prv_open_locked(&settings_file))) { + settings_file_rewrite(&settings_file, prv_rewrite_cb, NULL); + prv_close_and_unlock(&settings_file); + } +} diff --git a/src/fw/services/comm_session/default_kernel_receiver.c b/src/fw/services/comm_session/default_kernel_receiver.c new file mode 100644 index 0000000000..a95fe9a857 --- /dev/null +++ b/src/fw/services/comm_session/default_kernel_receiver.c @@ -0,0 +1,184 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/session_receive_router.h" + +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/slist.h" + +#include + +PBL_LOG_MODULE_DECLARE(service_comm_session, CONFIG_SERVICE_COMM_SESSION_LOG_LEVEL); + +//! Default option for the kernel receiver, execute the endpoint handler on KernelBG. +const PebbleTask g_default_kernel_receiver_opt_bg = PebbleTask_KernelBackground; + +//! If the endpoint handler puts events onto the KernelMain queue *and* it is important that +//! PEBBLE_COMM_SESSION_EVENT and your endpoint's events are handled in order, use this +//! receiver option in the protocol_endpoints_table.json: +const PebbleTask g_default_kernel_receiver_opt_main = PebbleTask_KernelMain; + +// A common pattern for endpoint handlers it to: +// 1) Kernel malloc a buffer & copy Pebble Protocol payload to it +// 2) Schedule a callback on KernelBG/Main to run the code that decodes the payload +// (because the handler runs from BT02, a high priority thread +// 3) Free malloc'ed buffer +// Leaving this up to each individual endpoint wastes code and creates more +// opportunity for memory leaks. This file contains an implementation that +// different endpoints can use to achieve this pattern. +// +// Note: Since the buffer is malloc'ed on the kernel heap, the expected consumer +// for this receiver is the system (not an app). However, it might be +// receiving messages *from* a PebbleKit app that the system is supposed +// to handle. For example, app run state commands (i.e. "app launch") are +// sent by PebbleKit apps, but get handled by the system. + +typedef struct { + SingleListNode node; + CommSession *session; + const PebbleProtocolEndpoint *endpoint; + size_t total_payload_size; + int curr_pos; + bool handler_scheduled; + bool should_use_kernel_main; + uint8_t payload[]; +} DefaultReceiverImpl; + +//! Pending receiver lists: completed messages are appended here instead of each +//! getting their own system_task/launcher_task callback. A single callback is +//! scheduled per list and drains all entries when it fires. This prevents the +//! system task queue from overflowing when many Pebble Protocol messages arrive +//! in rapid succession (e.g. after BT reconnect). +static SingleListNode *s_pending_bg_head; +static bool s_bg_callback_pending; + +static SingleListNode *s_pending_main_head; +static bool s_main_callback_pending; + +static Receiver *prv_default_kernel_receiver_prepare( + CommSession *session, const PebbleProtocolEndpoint *endpoint, + size_t total_payload_size) { + if (total_payload_size == 0) { + return NULL; // Ignore zero-length messages + } + + size_t size_needed = sizeof(DefaultReceiverImpl) + total_payload_size; + DefaultReceiverImpl *receiver = kernel_zalloc(size_needed); + + if (!receiver) { + PBL_LOG_WRN("Could not allocate receiver, handler:%p size:%d", + endpoint->handler, (int)size_needed); + return NULL; + } + + const bool should_use_kernel_main = + (endpoint->receiver_opt == &g_default_kernel_receiver_opt_main); + *receiver = (DefaultReceiverImpl) { + .session = session, + .endpoint = endpoint, + .total_payload_size = total_payload_size, + .should_use_kernel_main = should_use_kernel_main, + .curr_pos = 0 + }; + + return (Receiver *)receiver; +} + +static void prv_default_kernel_receiver_write( + Receiver *receiver, const uint8_t *data, size_t length) { + DefaultReceiverImpl *impl = (DefaultReceiverImpl *)receiver; + + PBL_ASSERTN((impl->curr_pos + length) <= impl->total_payload_size); + memcpy(impl->payload + impl->curr_pos, data, length); + + impl->curr_pos += length; +} + +static void prv_wipe_receiver_data(DefaultReceiverImpl *receiver) { + *receiver = (DefaultReceiverImpl) { }; +} + +static void prv_append_to_pending_list(DefaultReceiverImpl *impl, + SingleListNode **head) { + slist_init(&impl->node); + if (*head) { + slist_append(*head, &impl->node); + } else { + *head = &impl->node; + } +} + +static void prv_drain_pending_list(SingleListNode **head, + bool *callback_pending) { + // Snapshot and detach the list so new items that arrive while we are + // processing do not extend the current batch indefinitely. + SingleListNode *list = *head; + *head = NULL; + *callback_pending = false; + + while (list) { + DefaultReceiverImpl *impl = (DefaultReceiverImpl *)list; + list = slist_get_next(list); + PBL_ASSERTN(impl->handler_scheduled && impl->session); + impl->endpoint->handler(impl->session, impl->payload, impl->total_payload_size); + prv_wipe_receiver_data(impl); + kernel_free(impl); + } +} + +static void prv_default_kernel_receiver_bg_cb(void *data) { + prv_drain_pending_list(&s_pending_bg_head, &s_bg_callback_pending); +} + +static void prv_default_kernel_receiver_main_cb(void *data) { + prv_drain_pending_list(&s_pending_main_head, &s_main_callback_pending); +} + +static void prv_default_kernel_receiver_finish(Receiver *receiver) { + DefaultReceiverImpl *impl = (DefaultReceiverImpl *)receiver; + impl->handler_scheduled = true; + + if ((int)impl->total_payload_size != impl->curr_pos) { + PBL_LOG_WRN("Got fewer bytes than expected for handler %p", + impl->endpoint->handler); + } + + // Coalesce callbacks: append to the pending list and only schedule a new + // system_task / launcher_task callback if one isn't already in flight. + // This avoids overflowing the system task queue when many Pebble Protocol + // messages arrive in rapid succession (e.g. after BT reconnect). + if (impl->should_use_kernel_main) { + prv_append_to_pending_list(impl, &s_pending_main_head); + if (!s_main_callback_pending) { + s_main_callback_pending = true; + launcher_task_add_callback(prv_default_kernel_receiver_main_cb, NULL); + } + } else { + prv_append_to_pending_list(impl, &s_pending_bg_head); + if (!s_bg_callback_pending) { + s_bg_callback_pending = true; + system_task_add_callback(prv_default_kernel_receiver_bg_cb, NULL); + } + } +} + +static void prv_default_kernel_receiver_cleanup(Receiver *receiver) { + DefaultReceiverImpl *impl = (DefaultReceiverImpl *)receiver; + if (impl->handler_scheduled) { + return; // the kernel BG/main callback will free the data + } + prv_wipe_receiver_data(impl); + kernel_free(impl); +} + +const ReceiverImplementation g_default_kernel_receiver_implementation = { + .prepare = prv_default_kernel_receiver_prepare, + .write = prv_default_kernel_receiver_write, + .finish = prv_default_kernel_receiver_finish, + .cleanup = prv_default_kernel_receiver_cleanup, +}; diff --git a/src/fw/services/common/comm_session/default_kernel_sender.c b/src/fw/services/comm_session/default_kernel_sender.c similarity index 90% rename from src/fw/services/common/comm_session/default_kernel_sender.c rename to src/fw/services/comm_session/default_kernel_sender.c index 909507d17c..2f849db863 100644 --- a/src/fw/services/common/comm_session/default_kernel_sender.c +++ b/src/fw/services/comm_session/default_kernel_sender.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/bt_lock.h" #include "drivers/rtc.h" #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/comm_session/session_send_queue.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/comm_session/session_send_queue.h" #include "system/logging.h" #include "util/attributes.h" #include "util/likely.h" @@ -30,6 +17,8 @@ #include "FreeRTOS.h" #include "semphr.h" +PBL_LOG_MODULE_DECLARE(service_comm_session, CONFIG_SERVICE_COMM_SESSION_LOG_LEVEL); + typedef struct SendBuffer { //! Save some memory by making this a union. //! @note This is the first field, so that we can just cast between @@ -215,8 +204,7 @@ SendBuffer * comm_session_send_buffer_begin_write(CommSession *session, uint16_t return NULL; } if (required_payload_length > DEFAULT_KERNEL_SENDER_MAX_PAYLOAD_SIZE) { - PBL_LOG(LOG_LEVEL_WARNING, - "Message for endpoint_id %u exceeds maximum length (length=%"PRIu32")", + PBL_LOG_WRN("Message for endpoint_id %u exceeds maximum length (length=%"PRIu32")", endpoint_id, (uint32_t)required_payload_length); return NULL; } @@ -261,11 +249,7 @@ SendBuffer * comm_session_send_buffer_begin_write(CommSession *session, uint16_t } if (is_timeout) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BT_COMM_SESSION_SEND_DATA_FAIL_COUNT, - AnalyticsClient_System); - - PBL_LOG(LOG_LEVEL_WARNING, - "Failed to get send buffer (bytes=%"PRIu32", endpoint_id=%"PRIu16", to=%"PRIu32")", + PBL_LOG_WRN("Failed to get send buffer (bytes=%"PRIu32", endpoint_id=%"PRIu16", to=%"PRIu32")", (uint32_t)required_payload_length, endpoint_id, (uint32_t)is_timeout); return NULL; } diff --git a/src/fw/services/comm_session/meta_endpoint.c b/src/fw/services/comm_session/meta_endpoint.c new file mode 100644 index 0000000000..d6d1d6b69e --- /dev/null +++ b/src/fw/services/comm_session/meta_endpoint.c @@ -0,0 +1,47 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/meta_endpoint.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "util/net.h" + +PBL_LOG_MODULE_DECLARE(service_comm_session, CONFIG_SERVICE_COMM_SESSION_LOG_LEVEL); + +static const uint16_t META_ENDPOINT_ID = 0; + +static void prv_send_meta_response_kernelbg_cb(void *data) { + MetaResponseInfo *meta_response_info_heap_copy = data; + + // Swap endpoint_id bytes to be Big-Endian: + meta_response_info_heap_copy->payload.endpoint_id = + htons(meta_response_info_heap_copy->payload.endpoint_id); + + uint16_t payload_size; + if (meta_response_info_heap_copy->payload.error_code == MetaResponseCodeCorruptedMessage) { + payload_size = sizeof(meta_response_info_heap_copy->payload.error_code); + } else { + payload_size = sizeof(meta_response_info_heap_copy->payload); + } + + comm_session_send_data(meta_response_info_heap_copy->session, META_ENDPOINT_ID, + (const uint8_t *)&meta_response_info_heap_copy->payload, + payload_size, COMM_SESSION_DEFAULT_TIMEOUT); + + kernel_free(meta_response_info_heap_copy); +} + +void meta_endpoint_send_response_async(const MetaResponseInfo *meta_response_info) { + PBL_LOG_ERR("Meta protocol error: 0x%x (endpoint=%u)", + meta_response_info->payload.error_code, meta_response_info->payload.endpoint_id); + + MetaResponseInfo *meta_response_info_heap_copy = kernel_zalloc_check(sizeof(*meta_response_info)); + memcpy(meta_response_info_heap_copy, meta_response_info, sizeof(*meta_response_info)); + system_task_add_callback(prv_send_meta_response_kernelbg_cb, meta_response_info_heap_copy); +} + +void meta_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { + PBL_LOG_INFO("Meta endpoint callback called"); +} diff --git a/src/fw/services/common/comm_session/protocol_endpoints_table.json b/src/fw/services/comm_session/protocol_endpoints_table.json similarity index 97% rename from src/fw/services/common/comm_session/protocol_endpoints_table.json rename to src/fw/services/comm_session/protocol_endpoints_table.json index 4a1b329714..0398df07c2 100644 --- a/src/fw/services/common/comm_session/protocol_endpoints_table.json +++ b/src/fw/services/comm_session/protocol_endpoints_table.json @@ -50,6 +50,7 @@ [ 43981, "0xabcd", "private", "app_order_protocol_msg_callback", null, null ], [ 45531, "0xb1db", "private", "blob_db_protocol_msg_callback", null, null ], [ 45787, "0xb2db", "private", "blob_db2_protocol_msg_callback", null, null ], - [ 51966, "0xcafe", "any", "comm_poll_remote_protocol_msg_callback", null, null ] + [ 51966, "0xcafe", "any", "comm_poll_remote_protocol_msg_callback", null, null ], + [ 51967, "0xcaff", "any", "xsbug_protocol_msg_callback", null, null ] ] } diff --git a/src/fw/services/common/comm_session/session.c b/src/fw/services/comm_session/session.c similarity index 91% rename from src/fw/services/common/comm_session/session.c rename to src/fw/services/comm_session/session.c index bd52b3e894..fe5711176d 100644 --- a/src/fw/services/common/comm_session/session.c +++ b/src/fw/services/comm_session/session.c @@ -1,41 +1,28 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include -#include "services/common/comm_session/session.h" -#include "session_analytics.h" -#include "session_internal.h" -#include "session_transport.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_analytics.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_transport.h" #include "applib/app_comm.h" #include "comm/ble/kernel_le_client/app_launch/app_launch.h" #include "comm/bt_lock.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/common/comm_session/session_send_buffer.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/comm_session/session_send_buffer.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" #include "process_management/app_manager.h" -#include "services/common/system_task.h" -#include "services/normal/data_logging/dls_private.h" +#include "pbl/services/system_task.h" +#include "pbl/services/data_logging/dls_private.h" #include "system/logging.h" #include "system/passert.h" @@ -44,6 +31,8 @@ #include #include +PBL_LOG_MODULE_DEFINE(service_comm_session, CONFIG_SERVICE_COMM_SESSION_LOG_LEVEL); + // ------------------------------------------------------------------------------------------------- // Static variables @@ -132,7 +121,7 @@ void comm_session_reset(CommSession *session) { bt_lock(); { if (!comm_session_is_valid(session)) { - PBL_LOG(LOG_LEVEL_WARNING, "Already closed!"); + PBL_LOG_WRN("Already closed!"); goto unlock; } session->transport_imp->reset(session->transport); @@ -166,7 +155,7 @@ static const char *prv_string_for_destination(TransportDestination destination) static void prv_log_session_event(CommSession *session, bool is_open) { char uuid_string[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(prv_get_uuid(session), uuid_string); - PBL_LOG(LOG_LEVEL_INFO, "Session event: is_open=%d, destination=%s, app_uuid=%s", + PBL_LOG_INFO("Session event: is_open=%d, destination=%s, app_uuid=%s", is_open, prv_string_for_destination(session->destination), uuid_string); } @@ -194,7 +183,7 @@ CommSession * comm_session_open(Transport *transport, const TransportImplementat && !prv_is_transport_type(transport, implementation, CommSessionTransportType_PULSE)) { if (!existing_system_session->transport_imp->close) { // iAP sessions cannot be closed from the watch' side :( - PBL_LOG(LOG_LEVEL_ERROR, "System session already exists and cannot be closed"); + PBL_LOG_ERR("System session already exists and cannot be closed"); return NULL; } // Last system session to connect wins: @@ -203,7 +192,7 @@ CommSession * comm_session_open(Transport *transport, const TransportImplementat // is running over PPoGATT. If the app launches again, it will have no state of what was the // previously used transport was, prior to getting killed. Often, iAP ends up winning. // However, to the firmware, PPoGATT still appears connected, so we'd end up here. - PBL_LOG(LOG_LEVEL_INFO, "System session already exists, closing it now"); + PBL_LOG_INFO("System session already exists, closing it now"); existing_system_session->transport_imp->close(existing_system_session->transport); } } @@ -211,7 +200,7 @@ CommSession * comm_session_open(Transport *transport, const TransportImplementat CommSession *session = kernel_malloc(sizeof(CommSession)); if (!session) { - PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory for new CommSession"); + PBL_LOG_ERR("Not enough memory for new CommSession"); return NULL; } *session = (const CommSession) { @@ -257,7 +246,7 @@ void comm_session_close(CommSession *session, CommSessionCloseReason reason) { app_launch_trigger(); // TODO: PBL-1771: find a more graceful way to handle this -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW system_task_add_callback(dls_private_handle_disconnect, NULL); #endif } @@ -345,7 +334,7 @@ void comm_session_send_next(CommSession *session) { if (schedule_func(session)) { session->is_send_next_call_pending = true; } else { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to schedule comm_session_send_next callback"); + PBL_LOG_ERR("Failed to schedule comm_session_send_next callback"); } } @@ -371,7 +360,7 @@ bool comm_session_send_data(CommSession *session, uint16_t endpoint_id, } SendBuffer *sb = comm_session_send_buffer_begin_write(session, endpoint_id, length, timeout_ms); if (!sb) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not acquire send buffer for %x", endpoint_id); + PBL_LOG_WRN("Could not acquire send buffer for %x", endpoint_id); return false; } comm_session_send_buffer_write(sb, data, length); @@ -405,7 +394,7 @@ static bool prv_find_session_by_app_uuid_comparator(ListNode *found_node, void * // On iOS + iAP, we can expect at most one App session, so we assume that the found session is // the app one. if (ctx->fallback_session) { - PBL_LOG(LOG_LEVEL_ERROR, "Fallback session already set!?"); + PBL_LOG_ERR("Fallback session already set!?"); } ctx->fallback_session = session; } @@ -572,7 +561,7 @@ DEFINE_SYSCALL(void, sys_app_comm_set_responsiveness, SniffInterval interval) { ResponseTimeMax, 0); return; } - PBL_LOG(LOG_LEVEL_WARNING, "Invalid sniff interval"); + PBL_LOG_WRN("Invalid sniff interval"); syscall_failed(); } diff --git a/src/fw/services/comm_session/session_analytics.c b/src/fw/services/comm_session/session_analytics.c new file mode 100644 index 0000000000..1d059f4f03 --- /dev/null +++ b/src/fw/services/comm_session/session_analytics.c @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/session_analytics.h" + +#include "drivers/rtc.h" + +#if MEMFAULT +#include "memfault_chunk_collector.h" +#endif +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/ping.h" +#include "util/time/time.h" + +CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session) { + return session->transport_imp->get_type(session->transport); +} + +void comm_session_analytics_open_session(CommSession *session) { + const bool is_system = (session->destination != TransportDestinationApp); + if (is_system) { + PBL_ANALYTICS_TIMER_START(connectivity_expected_time_ms); + PBL_ANALYTICS_TIMER_START(connectivity_connected_time_ms); +#if MEMFAULT + // Trigger a delayed Memfault chunk collection so any pending coredump data + // gets pushed into datalogging shortly after the phone connects, rather than + // waiting for the 15-minute periodic timer. The delay gives the phone time + // to complete the datalogging Report handshake. + memfault_chunk_collect_after_delay(); +#endif + } + session->open_ticks = rtc_get_ticks(); +} + +void comm_session_analytics_close_session(CommSession *session, CommSessionCloseReason reason) { + const bool is_system = (session->destination != TransportDestinationApp); + if (is_system) { + PBL_ANALYTICS_TIMER_START(connectivity_expected_time_ms); + PBL_ANALYTICS_TIMER_STOP(connectivity_connected_time_ms); + } + +} \ No newline at end of file diff --git a/src/fw/services/common/comm_session/session_receive_router.c b/src/fw/services/comm_session/session_receive_router.c similarity index 86% rename from src/fw/services/common/comm_session/session_receive_router.c rename to src/fw/services/comm_session/session_receive_router.c index bbc81c11b1..659cbc9360 100644 --- a/src/fw/services/common/comm_session/session_receive_router.c +++ b/src/fw/services/comm_session/session_receive_router.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "session_receive_router.h" - -#include "services/common/comm_session/meta_endpoint.h" -#include "services/common/comm_session/session_analytics.h" -#include "services/common/comm_session/session_internal.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/session_receive_router.h" + +#include "pbl/services/comm_session/meta_endpoint.h" +#include "pbl/services/comm_session/session_analytics.h" +#include "pbl/services/comm_session/session_internal.h" #include "system/hexdump.h" #include "system/logging.h" #include "util/math.h" @@ -26,7 +13,9 @@ #include "util/size.h" // Generated table of endpoint handler (s_protocol_endpoints): -#include "services/common/comm_session/protocol_endpoints_table.auto.h" +#include "services/comm_session/protocol_endpoints_table.auto.h" + +PBL_LOG_MODULE_DECLARE(service_comm_session, CONFIG_SERVICE_COMM_SESSION_LOG_LEVEL); //////////////////////////////////////////////////////////////////////////////////////////////////// // Static helper functions @@ -138,7 +127,7 @@ static bool prv_prepare_receiver(const uint32_t payload_length, // - Look into SPP flow control // - With PPoGATT: drop packet and rely on automatic retransmission? - PBL_LOG(LOG_LEVEL_ERROR, "No receiver for endpoint=%"PRIu16" len=%"PRIu32, + PBL_LOG_ERR("No receiver for endpoint=%"PRIu16" len=%"PRIu32, endpoint_id, payload_length); prv_skip_message(rtr, payload_length); return true; @@ -168,8 +157,6 @@ static void prv_write_payload_to_receiver(ReceiveRouter *rtr, size_t *data_size_ void comm_session_receive_router_write(CommSession *session, const uint8_t *data, size_t data_size) { - comm_session_analytics_inc_bytes_received(session, data_size); - PBL_LOG_D_VERBOSE(LOG_DOMAIN_COMM, "Received packet from BT"); PBL_HEXDUMP_D(LOG_DOMAIN_COMM, LOG_LEVEL_DEBUG_VERBOSE, data, data_size); @@ -198,8 +185,7 @@ void comm_session_receive_router_write(CommSession *session, continue; // while (data_size) } - PBL_LOG_D(LOG_DOMAIN_COMM, LOG_LEVEL_DEBUG, - "Receiving message: endpoint_id 0x%"PRIx16" (%"PRIu16"), payload_length %"PRIu32, + PBL_LOG_D_DBG(LOG_DOMAIN_COMM, "Receiving message: endpoint_id 0x%"PRIx16" (%"PRIu16"), payload_length %"PRIu32, endpoint_id, endpoint_id, payload_length); if (prv_prepare_receiver(payload_length, endpoint, endpoint_id, session, rtr)) { diff --git a/src/fw/services/common/comm_session/session_remote_version.c b/src/fw/services/comm_session/session_remote_version.c similarity index 81% rename from src/fw/services/common/comm_session/session_remote_version.c rename to src/fw/services/comm_session/session_remote_version.c index 813739224a..d4a7eafb79 100644 --- a/src/fw/services/common/comm_session/session_remote_version.c +++ b/src/fw/services/comm_session/session_remote_version.c @@ -1,29 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "session_remote_version.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/session_remote_version.h" #include "comm/bt_lock.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/session_internal.h" -#include "services/common/comm_session/session_remote_os.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_remote_os.h" #include "kernel/event_loop.h" #include "util/attributes.h" @@ -31,7 +18,8 @@ #include "system/logging.h" #include "system/hexdump.h" -#include +PBL_LOG_MODULE_DECLARE(service_comm_session, CONFIG_SERVICE_COMM_SESSION_LOG_LEVEL); + extern bool comm_session_is_valid(const CommSession *session); @@ -115,7 +103,7 @@ static void prv_handle_phone_versions_response(CommSession *session, } else if (length >= sizeof(struct VersionsPhoneResponseV1)) { request_version = 1; } else { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid version request"); + PBL_LOG_ERR("Invalid version request"); return; } @@ -130,7 +118,7 @@ static void prv_handle_phone_versions_response(CommSession *session, // response_version field. That is why we only accept when this field is exactly 2, otherwise we // treat it as a V1 response. if (request_version >= 2) { - PBL_LOG(LOG_LEVEL_DEBUG, "Connected to Mobile App %"PRIu8 ".%"PRIu8 "-%"PRIu8, + PBL_LOG_DBG("Connected to Mobile App %"PRIu8 ".%"PRIu8 "-%"PRIu8, response->major_version, response->minor_version, response->bugfix_version); // For 3.X mobile applications, they will return additional bits in their response to correspond @@ -143,11 +131,9 @@ static void prv_handle_phone_versions_response(CommSession *session, comm_session_set_capabilities(session, capability_flags); const uint32_t platform_bits = ntohl(response->platform_bitfield); - bt_driver_reconnect_notify_platform_bitfield(platform_bits); const bool is_system = comm_session_is_system(session); - PBL_LOG(LOG_LEVEL_INFO, - "Phone app: is_system=%u, plf=0x%"PRIx32", capabilities=0x%"PRIx32, + PBL_LOG_INFO("Phone app: is_system=%u, plf=0x%"PRIx32", capabilities=0x%"PRIx32, is_system, platform_bits, (uint32_t)capability_flags); // Only emit for the Pebble app, not 3rd party companion apps: @@ -171,8 +157,7 @@ void session_remote_version_protocol_msg_callback(CommSession *session_ref, } default: - PBL_LOG_D(LOG_DOMAIN_COMM, LOG_LEVEL_ERROR, - "Invalid message received. First byte is %u", data[0]); + PBL_LOG_D_ERR(LOG_DOMAIN_COMM, "Invalid message received. First byte is %u", data[0]); break; } } diff --git a/src/fw/services/common/comm_session/session_send_queue.c b/src/fw/services/comm_session/session_send_queue.c similarity index 81% rename from src/fw/services/common/comm_session/session_send_queue.c rename to src/fw/services/comm_session/session_send_queue.c index 7c0903bac6..617d9b8304 100644 --- a/src/fw/services/common/comm_session/session_send_queue.c +++ b/src/fw/services/comm_session/session_send_queue.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/bt_lock.h" -#include "services/common/comm_session/session_analytics.h" -#include "services/common/comm_session/session_internal.h" -#include "services/common/comm_session/session_send_queue.h" +#include "pbl/services/comm_session/session_analytics.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_send_queue.h" #include "system/passert.h" #include "util/math.h" @@ -108,8 +95,6 @@ size_t comm_session_send_queue_get_read_pointer(const CommSession *session, void comm_session_send_queue_consume(CommSession *session, size_t remaining_length) { // The data has sucessfully been sent out at this point - comm_session_analytics_inc_bytes_sent(session, remaining_length); - PBL_ASSERTN(session->send_queue_head); SessionSendQueueJob *job = session->send_queue_head; while (job && remaining_length) { diff --git a/src/fw/services/comm_session/wscript_build b/src/fw/services/comm_session/wscript_build new file mode 100644 index 0000000000..e748621b10 --- /dev/null +++ b/src/fw/services/comm_session/wscript_build @@ -0,0 +1,113 @@ +import json + +in_node = bld.path.find_node('protocol_endpoints_table.json') +out_node = bld.path.get_bld().make_node('protocol_endpoints_table.auto.h') + +def generate_endpoints_table(task): + in_node = task.inputs[0] + out_node = task.outputs[0] + endpoints = [] + definition = {} + with open(in_node.abspath(), 'r') as f_in: + definition.update(json.load(f_in)) + + endpoints.extend(definition['prf_and_normal_fw']) + if bld.env.VARIANT != 'prf': + endpoints.extend(definition['normal_fw_only']) + endpoints.sort() + + def get_access_enum(access_str): + if access_str == "private": + return "PebbleProtocolAccessPrivate" + elif access_str == "any": + return "PebbleProtocolAccessAny" + elif access_str == "any": + return "PebbleProtocolAccessPublic" + else: + raise ValueError("Unknown value: %s" % access_str) + + with open(out_node.abspath(), 'w') as f_out: + f_out.write("// GENERATED -- DO NOT EDIT\n\n") + f_out.write("#include \"kernel/pebble_tasks.h\"\n\n") + + DEFAULT_SYSTEM_RECV_IMPL = \ + "g_default_kernel_receiver_implementation" + recv_imp_set = set([DEFAULT_SYSTEM_RECV_IMPL]) + + DEFAULT_SYSTEM_RECV_OPT = \ + "g_default_kernel_receiver_opt_bg" + recv_opt_set = set([DEFAULT_SYSTEM_RECV_OPT]) + + for (eid, eid_str, access_str, cb_str, + recv_imp, recv_opt) in endpoints: + if recv_imp: + recv_imp_set.add(recv_imp) + if recv_opt: + recv_opt_set.add(recv_opt) + if cb_str: + f_out.write("extern void {cb_str}(CommSession *session, " + "const uint8_t* data, " + "size_t length);\n".format(cb_str=cb_str)) + + f_out.write("\n\n") + for recv_imp in recv_imp_set: + fmt = "extern ReceiverImplementation {recv_imp};\n" + f_out.write(fmt.format(recv_imp=recv_imp)) + + f_out.write("\n\n") + for recv_opt in recv_opt_set: + fmt = "extern const PebbleTask {recv_opt};\n" + f_out.write(fmt.format(recv_opt=recv_opt)) + + f_out.write("\n\nstatic const PebbleProtocolEndpoint " + "s_protocol_endpoints[] = {\n") + for (eid, eid_str, access_str, cb_str, + recv_imp, recv_opt) in endpoints: + if int(eid_str, base=16) != eid: + raise ValueError("Endpoint IDs need to match: %i vs %s" % + (eid, eid_str)) + if not cb_str: + cb_str = "NULL" + if not recv_imp: + recv_imp = DEFAULT_SYSTEM_RECV_IMPL + if not recv_opt: + recv_opt = DEFAULT_SYSTEM_RECV_OPT + if not recv_opt: + recv_opt = "NULL" + else: + recv_opt = "&" + recv_opt + fmt = (" {{ {eid}, {cb_str}, {access_enum}, &{recv_imp}," + " {recv_opt} }},\n") + f_out.write(fmt.format(eid=eid, + cb_str=cb_str, + access_enum=get_access_enum(access_str), + recv_imp=recv_imp, + recv_opt=recv_opt, + )) + f_out.write("};\n\n") + +bld(rule=generate_endpoints_table, + source=[in_node], + target=out_node) + +sources = [ + 'default_kernel_receiver.c', + 'default_kernel_sender.c', + 'meta_endpoint.c', + 'session.c', + 'session_analytics.c', + 'session_receive_router.c', + 'session_remote_version.c', + 'session_send_queue.c', +] + +if bld.env.VARIANT != 'prf': + sources.append('app_session_capabilities.c') + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_comm_session', +) + +bld.env.SERVICES.append('services_comm_session') diff --git a/src/fw/services/common/accel_manager.c b/src/fw/services/common/accel_manager.c deleted file mode 100644 index c30dd13515..0000000000 --- a/src/fw/services/common/accel_manager.c +++ /dev/null @@ -1,803 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "accel_manager.h" - -#include "console/prompt.h" -#include "drivers/accel.h" -#include "drivers/vibe.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "mcu/interrupts.h" -#include "os/mutex.h" -#include "services/common/analytics/analytics.h" -#include "services/common/event_service.h" -#include "services/common/system_task.h" -#include "services/imu/units.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/shared_circular_buffer.h" - -#include "FreeRTOS.h" -#include "queue.h" - -#include - -// We use this as an argument to indicate a lookup of the current task -#define PEBBLE_TASK_CURRENT PebbleTask_Unknown - -#define US_PER_SECOND (1000 * 1000) - -typedef void (*ProcessDataHandler)(CallbackEventCallback *cb, void *data); - -// We create one of these for each data service subscriber -typedef struct AccelManagerState { - ListNode list_node; // Entry into the s_data_subscribers linked list - - //! Client pointing into s_buffer - SubsampledSharedCircularBufferClient buffer_client; - //! The sampling interval we've promised to this client after subsampling. - uint32_t sampling_interval_us; - //! The requested number of samples needed before calling data_cb_handler - uint16_t samples_per_update; - - //! Which task we should call the data_cb_handler on - PebbleTask task; - CallbackEventCallback data_cb_handler; - void* data_cb_context; - - uint64_t timestamp_ms; // timestamp of first item in the buffer - AccelRawData *raw_buffer; // raw buffer allocated by subscriber - uint8_t num_samples; // number of samples in raw_buffer - bool event_posted; // True if we've posted a "data ready" callback event -} AccelManagerState; - -typedef struct { - AccelRawData rawdata; - // The exact time the sample was collected can be recovered by: - // time_sample_collected = s_last_empty_timestamp_ms + timestamp_delta_ms - uint16_t timestamp_delta_ms; -} AccelManagerBufferData; -_Static_assert(offsetof(AccelManagerBufferData, rawdata) == 0, - "AccelRawData must be first entry in AccelManagerBufferData struct"); - -// Statics -//! List of all registered consumers of accel data. Points to AccelManagerState objects. -static ListNode *s_data_subscribers = NULL; -//! Mutex locking all accel_manager state -static PebbleRecursiveMutex *s_accel_manager_mutex; - -//! Reference count of how many shake subscribers we have. Used to turn off the feature when not -//! in use. -static uint8_t s_shake_subscribers_count = 0; -//! Reference count of how many double tap subscribers we have. Used to turn off the feature when -//! not in use. -static uint8_t s_double_tap_subscribers_count = 0; - -//! Circular buffer that raw accel data is written into before being subsampled for each client -static SharedCircularBuffer s_buffer; -//! Storage for s_buffer -//! 1600 bytes (~4s of data at 50Hz) -static uint8_t s_buffer_storage[200 * sizeof(AccelManagerBufferData)]; - -static uint64_t s_last_empty_timestamp_ms = 0; - -static uint32_t s_accel_samples_collected_count = 0; - -// Accel Idle -#define ACCEL_MAX_IDLE_DELTA 100 -static bool s_is_idle = false; -static AccelData s_last_analytics_position; -static AccelData s_last_accel_data; - -static void prv_setup_subsampling(uint32_t sampling_interval); - -static void prv_shake_add_subscriber_cb(PebbleTask task) { - mutex_lock_recursive(s_accel_manager_mutex); - { - if (++s_shake_subscribers_count == 1) { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting accel shake service"); - accel_enable_shake_detection(true); - prv_setup_subsampling(accel_get_sampling_interval()); - } - } - mutex_unlock_recursive(s_accel_manager_mutex); -} - -static void prv_shake_remove_subscriber_cb(PebbleTask task) { - mutex_lock_recursive(s_accel_manager_mutex); - { - PBL_ASSERTN(s_shake_subscribers_count > 0); - if (--s_shake_subscribers_count == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "Stopping accel shake service"); - accel_enable_shake_detection(false); - prv_setup_subsampling(accel_get_sampling_interval()); - } - } - mutex_unlock_recursive(s_accel_manager_mutex); -} - -static void prv_double_tap_add_subscriber_cb(PebbleTask task) { - mutex_lock_recursive(s_accel_manager_mutex); - - if (++s_double_tap_subscribers_count == 1) { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting accel double tap service"); - accel_enable_double_tap_detection(true); - prv_setup_subsampling(accel_get_sampling_interval()); - } - - mutex_unlock_recursive(s_accel_manager_mutex); -} - -static void prv_double_tap_remove_subscriber_cb(PebbleTask task) { - mutex_lock_recursive(s_accel_manager_mutex); - - PBL_ASSERTN(s_double_tap_subscribers_count > 0); - if (--s_double_tap_subscribers_count == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "Stopping accel double tap service"); - accel_enable_double_tap_detection(false); - prv_setup_subsampling(accel_get_sampling_interval()); - } - - mutex_unlock_recursive(s_accel_manager_mutex); -} - - -//! Out of all accel subscribers, figures out: -//! @param[out] lowest_interval_us - the lowest sampling interval requested (in microseconds) -//! @param[out] max_n_samples - the max number of samples requested for batching -//! @return The longest amount of samples which can be batched assuming we are -//! running at the lowest_sampling_interval -//! -//! @note currently the longest interval we can batch samples for is computed -//! as the minimum of (samples to batch / sample rate) out of all the active -//! subscribers. This means that if we have two subscribers, subscriber A at -//! 200ms, and subscriber B at 250ms, new samples will become available every -//! 200ms, so subscriber B's data buffer would not fill until 400ms, resulting -//! in a 150ms latency. This is how the legacy implementation worked as well -//! but is potentionally something we could improve in the future if it becomes -//! a problem. -static uint32_t prv_get_sample_interval_info(uint32_t *lowest_interval_us, - uint32_t *max_n_samples) { - *lowest_interval_us = (US_PER_SECOND / ACCEL_SAMPLING_10HZ); - *max_n_samples = 0; - // Tracks which subscriber wants data most frequently. Note this is different than just - // lowest_interval_us * max_n_samples as those values can come from 2 different subscribers - // where we want to know which one subscriber wants the highest update frequency. - uint32_t lowest_us_per_update = UINT32_MAX; - - AccelManagerState *state = (AccelManagerState *)s_data_subscribers; - while (state) { - *lowest_interval_us = MIN(state->sampling_interval_us, *lowest_interval_us); - *max_n_samples = MAX(state->samples_per_update, *max_n_samples); - - if (state->samples_per_update > 0) { - uint32_t us_per_update = state->samples_per_update * state->sampling_interval_us; - lowest_us_per_update = MIN(lowest_us_per_update, us_per_update); - } - state = (AccelManagerState *)state->list_node.next; - } - - if (lowest_us_per_update == UINT32_MAX) { - // No one subscribing or no one who wants updates - return 0; - } - - uint32_t num_samples = lowest_us_per_update / (*lowest_interval_us); - num_samples = MIN(num_samples, ACCEL_MAX_SAMPLES_PER_UPDATE); - - return num_samples; -} - -static void prv_setup_subsampling(uint32_t sampling_interval) { - // Setup the subsampling numerator and denominators - AccelManagerState *state = (AccelManagerState *)s_data_subscribers; - while (state) { - uint32_t interval_gcd = gcd(sampling_interval, - state->sampling_interval_us); - uint32_t numerator = sampling_interval / interval_gcd; - uint32_t denominator = state->sampling_interval_us / interval_gcd; - - PBL_LOG(LOG_LEVEL_DEBUG, - "set subsampling for session %p to %" PRIu32 "/%" PRIu32, - state, numerator, denominator); - subsampled_shared_circular_buffer_client_set_ratio( - &state->buffer_client, numerator, denominator); - state = (AccelManagerState *)state->list_node.next; - } -} - -//! Should be called after any change to a subscriber. Handles re-configuring -//! the accel driver to satisfy the requirements of all consumers (i.e setting -//! sampling rate and max number of samples which can be batched). If there are no -//! subscribers, chooses the lowest power configuration settings -static void prv_update_driver_config(void) { - // TODO: Add low power support - uint32_t lowest_interval_us; - uint32_t max_n_samples; - uint32_t max_batch = prv_get_sample_interval_info(&lowest_interval_us, &max_n_samples); - - // Configure the driver sampling interval and get the actual interval that the driver is going - // to use. - uint32_t interval_us = accel_set_sampling_interval(lowest_interval_us); - - prv_setup_subsampling(interval_us); - - PBL_LOG(LOG_LEVEL_DEBUG, "setting accel rate:%"PRIu32", num_samples:%"PRIu32, - US_PER_SECOND / interval_us, max_batch); - - accel_set_num_samples(max_batch); -} - -static bool prv_call_data_callback(AccelManagerState *state) { - switch (state->task) { - case PebbleTask_App: - case PebbleTask_Worker: - case PebbleTask_KernelMain: { - PebbleEvent event = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = state->data_cb_handler, - .data = state->data_cb_context, - }, - }; - - QueueHandle_t queue = pebble_task_get_to_queue(state->task); - // Note: This call may fail if the queue is full but when a new sample - // becomes available from the driver, we will retry anyway - return xQueueSendToBack(queue, &event, 0); - } - case PebbleTask_KernelBackground: - return system_task_add_callback(state->data_cb_handler, state->data_cb_context); - case PebbleTask_NewTimers: - return new_timer_add_work_callback(state->data_cb_handler, state->data_cb_context); - default: - WTF; // Unsupported task for the accel manager - } -} - -//! This is called every time new samples arrive from the accel driver & every -//! time data has been drained by the accel service. Its responsibility is -//! populating subscriber storage with new samples (at the requested sample -//! frequency) and generating a callback event on the subscriber's queue when -//! the requested number of samples have been batched -static void prv_dispatch_data(bool post_event) { - mutex_lock_recursive(s_accel_manager_mutex); - - AccelManagerState * state = (AccelManagerState *)s_data_subscribers; - while (state) { - if (!state->raw_buffer) { - state = (AccelManagerState *)state->list_node.next; - continue; - } - - // if subscribed but not looking for any samples then just drop the data - if (state->samples_per_update == 0) { - uint16_t len = shared_circular_buffer_get_read_space_remaining( - &s_buffer, &state->buffer_client.buffer_client); - shared_circular_buffer_consume( - &s_buffer, &state->buffer_client.buffer_client, len); - state = (AccelManagerState *)state->list_node.next; - continue; - } - - // If buffer has room, read more data - uint32_t samples_drained = 0; - while (state->num_samples < state->samples_per_update) { - // Read available data. - AccelManagerBufferData data; - if (!shared_circular_buffer_read_subsampled( - &s_buffer, &state->buffer_client, sizeof(data), &data, 1)) { - // we have drained all available samples - break; - } - - // Note: the accel_service currently only buffers AccelRawData (i.e it - // does not track the timestamp explicitly.) The accel service drains a - // buffers worth of data at a time and asks for the starting time - // (state->timestamp_ms) of the first sample in that buffer when it - // does. Therefore, we provide the real time for the first sample. In - // the future, we could phase out legacy accel code and provide the - // exact timestamp with every sample - if (state->num_samples == 0) { - state->timestamp_ms = s_last_empty_timestamp_ms + data.timestamp_delta_ms; - } - - memcpy(state->raw_buffer + state->num_samples, &data, - sizeof(AccelRawData)); - state->num_samples++; - samples_drained++; - } - - // If buffer is full, notify subscriber to process it - if (post_event && !state->event_posted && - state->num_samples >= state->samples_per_update) { - // Notify the subscriber that data is available - state->event_posted = prv_call_data_callback(state); - - ACCEL_LOG_DEBUG("full set of %d samples for session %p", state->num_samples, state); - - if (!state->event_posted) { - PBL_LOG(LOG_LEVEL_INFO, "Failed to post accel event to task: 0x%x", (int) state->task); - } - } - state = (AccelManagerState *)state->list_node.next; - } - - mutex_unlock_recursive(s_accel_manager_mutex); -} - -#ifdef TEST_KERNEL_SUBSCRIPTION -static void prv_kernel_data_subscription_handler(AccelData *accel_data, - uint32_t num_samples) { - PBL_LOG(LOG_LEVEL_INFO, "Received %" PRIu32 " accel samples for KernelMain.", num_samples); -} - -static void prv_kernel_tap_subscription_handler(AccelAxisType axis, - int32_t direction) { - PBL_LOG(LOG_LEVEL_INFO, "Received a tap event for KernelMain, axis: %d, " - "direction: %" PRId32, axis, direction); -} -#endif - -// Compute and return the device's delta position to help determine movement as idle. -static uint32_t prv_compute_delta_pos(AccelData *cur_pos, AccelData *last_pos) { - return (abs(last_pos->x - cur_pos->x) + abs(last_pos->y - cur_pos->y) + - abs(last_pos->z - cur_pos->z)); -} - -/* - * Exported APIs - */ - -// we expect this to get called once by accel_manager_init() so we have a default -// starting position. -void analytics_external_collect_accel_xyz_delta(void) { - AccelData accel_data; - - if (sys_accel_manager_peek(&accel_data) == 0) { - uint32_t delta = prv_compute_delta_pos(&accel_data, &s_last_analytics_position); - s_is_idle = (delta < ACCEL_MAX_IDLE_DELTA); - s_last_analytics_position = accel_data; - analytics_set(ANALYTICS_DEVICE_METRIC_ACCEL_XYZ_DELTA, delta, AnalyticsClient_System); - } -} - -void analytics_external_collect_accel_samples_received(void) { - mutex_lock_recursive(s_accel_manager_mutex); - uint32_t samps_collected = s_accel_samples_collected_count; - s_accel_samples_collected_count = 0; - mutex_unlock_recursive(s_accel_manager_mutex); - - analytics_set(ANALYTICS_DEVICE_METRIC_ACCEL_SAMPLE_COUNT, samps_collected, - AnalyticsClient_System); -} - -void accel_manager_init(void) { - s_accel_manager_mutex = mutex_create_recursive(); - - shared_circular_buffer_init(&s_buffer, s_buffer_storage, - sizeof(s_buffer_storage)); - - event_service_init(PEBBLE_ACCEL_SHAKE_EVENT, &prv_shake_add_subscriber_cb, - &prv_shake_remove_subscriber_cb); - - event_service_init(PEBBLE_ACCEL_DOUBLE_TAP_EVENT, &prv_double_tap_add_subscriber_cb, - &prv_double_tap_remove_subscriber_cb); - - // we always listen for motion events to decide whether or not to enable the backlight - // TODO: KernelMain could probably subscribe to the motion service to accomplish this? - prv_shake_add_subscriber_cb(PebbleTask_KernelMain); - - analytics_external_collect_accel_xyz_delta(); -} - -static void prv_copy_accel_sample_to_accel_data(AccelDriverSample const *accel_sample, - AccelData *accel_data) { - *accel_data = (AccelData) { - .x = accel_sample->x, - .y = accel_sample->y, - .z = accel_sample->z, - .timestamp /* ms */ = (accel_sample->timestamp_us / 1000), - .did_vibrate = (sys_vibe_get_vibe_strength() != VIBE_STRENGTH_OFF) - }; -} - -static void prv_update_last_accel_data(AccelDriverSample const *data) { - prv_copy_accel_sample_to_accel_data(data, &s_last_accel_data); -} - -DEFINE_SYSCALL(int, sys_accel_manager_peek, AccelData *accel_data) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(accel_data, sizeof(*accel_data)); - } - - // bump peek analytics - analytics_inc(ANALYTICS_DEVICE_METRIC_ACCEL_PEEK_COUNT, AnalyticsClient_System); - PebbleTask task = pebble_task_get_current(); - if (task == PebbleTask_Worker || task == PebbleTask_App) { - analytics_inc(ANALYTICS_APP_METRIC_ACCEL_PEEK_COUNT, AnalyticsClient_CurrentTask); - } - - mutex_lock_recursive(s_accel_manager_mutex); - - AccelDriverSample data; - int result = accel_peek(&data); - if (result == 0 /* success */) { - prv_copy_accel_sample_to_accel_data(&data, accel_data); - prv_update_last_accel_data(&data); - } - - mutex_unlock_recursive(s_accel_manager_mutex); - - return result; -} - -DEFINE_SYSCALL(AccelManagerState*, sys_accel_manager_data_subscribe, - AccelSamplingRate rate, AccelDataReadyCallback data_cb, void* context, - PebbleTask handler_task) { - AccelManagerState *state; - - mutex_lock_recursive(s_accel_manager_mutex); - { - state = kernel_malloc_check(sizeof(AccelManagerState)); - *state = (AccelManagerState) { - .task = handler_task, - .data_cb_handler = data_cb, - .data_cb_context = context, - .sampling_interval_us = (US_PER_SECOND / rate), - .samples_per_update = ACCEL_MAX_SAMPLES_PER_UPDATE, - }; - - bool no_subscribers_before = (s_data_subscribers == NULL); - s_data_subscribers = list_insert_before(s_data_subscribers, &state->list_node); - if (no_subscribers_before) { - sys_vibe_history_start_collecting(); - } - - // Add as a consumer to the accel buffer - shared_circular_buffer_add_subsampled_client( - &s_buffer, &state->buffer_client, 1, 1); - - // Update the sampling rate and num samples of the driver considering the new - // subscriber's request - prv_update_driver_config(); - } - mutex_unlock_recursive(s_accel_manager_mutex); - - return state; -} - -DEFINE_SYSCALL(bool, sys_accel_manager_data_unsubscribe, AccelManagerState *state) { - bool event_outstanding; - mutex_lock_recursive(s_accel_manager_mutex); - { - event_outstanding = state->event_posted; - // Remove this subscriber and free up its state variables - shared_circular_buffer_remove_subsampled_client( - &s_buffer, &state->buffer_client); - list_remove(&state->list_node, &s_data_subscribers /* &head */, NULL /* &tail */); - kernel_free(state); - - if (!s_data_subscribers) { - // If no one left using the data subscription, disable it - sys_vibe_history_stop_collecting(); - } - - // reconfig for the common subset of requirements among remaining subscribers - prv_update_driver_config(); - } - mutex_unlock_recursive(s_accel_manager_mutex); - return event_outstanding; -} - -DEFINE_SYSCALL(int, sys_accel_manager_set_sampling_rate, - AccelManagerState *state, AccelSamplingRate rate) { - - // Make sure the rate is one of our externally supported fixed rates - switch (rate) { - case ACCEL_SAMPLING_10HZ: - case ACCEL_SAMPLING_25HZ: - case ACCEL_SAMPLING_50HZ: - case ACCEL_SAMPLING_100HZ: - break; - default: - return -1; - } - - mutex_lock_recursive(s_accel_manager_mutex); - - state->sampling_interval_us = (US_PER_SECOND / rate); - prv_update_driver_config(); - - mutex_unlock_recursive(s_accel_manager_mutex); - - // TODO: doesn't look like our API specifies what this routine should return. - return 0; -} - -uint32_t accel_manager_set_jitterfree_sampling_rate(AccelManagerState *state, - uint32_t min_rate_mHz) { - // HACK - // We're dumb and don't support anything other than 12.5hz for jitter-free sampling. We chose - // this rate because it divides evenly into all the native rates we support right now. - // Supporting a wider range of jitter-free rates is harder due to dealing with all the potential - // combinations of different subscribers asking for different rates. - const uint32_t ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ = 12500; - PBL_ASSERTN(min_rate_mHz <= ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ); - - mutex_lock_recursive(s_accel_manager_mutex); - - state->sampling_interval_us = (US_PER_SECOND * 1000) / ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ; - prv_update_driver_config(); - - mutex_unlock_recursive(s_accel_manager_mutex); - - return ONLY_SUPPORTED_JITTERFREE_RATE_MILLIHZ; -} - -DEFINE_SYSCALL(int, sys_accel_manager_set_sample_buffer, - AccelManagerState *state, AccelRawData *buffer, uint32_t samples_per_update) { - if (samples_per_update > ACCEL_MAX_SAMPLES_PER_UPDATE) { - return -1; - } - - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(buffer, samples_per_update * sizeof(AccelRawData)); - } - - mutex_lock_recursive(s_accel_manager_mutex); - { - state->raw_buffer = buffer; - state->samples_per_update = samples_per_update; - state->num_samples = 0; - prv_update_driver_config(); - } - mutex_unlock_recursive(s_accel_manager_mutex); - - return 0; -} - -DEFINE_SYSCALL(uint32_t, sys_accel_manager_get_num_samples, - AccelManagerState *state, uint64_t *timestamp_ms) { - - mutex_lock_recursive(s_accel_manager_mutex); - - uint32_t result = state->num_samples; - *timestamp_ms = state->timestamp_ms; - - mutex_unlock_recursive(s_accel_manager_mutex); - return result; -} - -DEFINE_SYSCALL(bool, sys_accel_manager_consume_samples, - AccelManagerState *state, uint32_t samples) { - bool success = true; - mutex_lock_recursive(s_accel_manager_mutex); - - if (samples > state->num_samples) { - PBL_LOG(LOG_LEVEL_ERROR, "Consuming more samples than exist %d vs %d!", - (int)samples, (int)state->num_samples); - success = false; - } else if (samples != state->num_samples) { - PBL_LOG(LOG_LEVEL_DEBUG, "Dropping %d accel samples", (int)(state->num_samples - samples)); - success = false; - } - - state->event_posted = false; - state->num_samples = 0; - // Fill it again from circular buffer - prv_dispatch_data(state->task != pebble_task_get_current() /* post_event */); - - mutex_unlock_recursive(s_accel_manager_mutex); - return success; -} - - -/* - * TODO: APIs that still need to be implemented - */ - -void accel_manager_enable(bool on) { } - -void accel_manager_exit_low_power_mode(void) { } - -// Return true if we are "idle", defined as seeing no movement in the last hour. -bool accel_is_idle(void) { - // It was idle recently, see if it's still idle. Note we avoid reading the accel hardware - // again here to keep this call as lightweight as possible. Instead we are just comparing the last - // read value with the value last captured by analytics (which does so on an hourly heartbeat). - return (prv_compute_delta_pos(&s_last_accel_data, &s_last_analytics_position) - < ACCEL_MAX_IDLE_DELTA); -} - -// The accelerometer should issue a shake/tap event with any slight movements when stationary. -// This will allow the watch to immediately return to normal mode, and attempt to reconnect to -// the phone. -void accel_enable_high_sensitivity(bool high_sensitivity) { - mutex_lock_recursive(s_accel_manager_mutex); - accel_set_shake_sensitivity_high(high_sensitivity); - mutex_unlock_recursive(s_accel_manager_mutex); -} - -/* - * Driver Callbacks - See accel.h header for more context - */ - -static bool prv_shared_buffer_empty(void) { - bool empty = true; - mutex_lock_recursive(s_accel_manager_mutex); - { - AccelManagerState *state = (AccelManagerState *)s_data_subscribers; - while (state) { - int left = shared_circular_buffer_get_read_space_remaining( - &s_buffer, &state->buffer_client.buffer_client); - if (left != 0) { - empty = false; - break; - } - state = (AccelManagerState *)state->list_node.next; - } - } - mutex_unlock_recursive(s_accel_manager_mutex); - return empty; -} - -void accel_cb_new_sample(AccelDriverSample const *data) { - prv_update_last_accel_data(data); - - s_accel_samples_collected_count++; - - if (!s_buffer.clients) { - return; // no clients so don't buffer any data - } - - AccelManagerBufferData accel_buffer_data; - accel_buffer_data.rawdata.x = data->x; - accel_buffer_data.rawdata.y = data->y; - accel_buffer_data.rawdata.z = data->z; - - if (prv_shared_buffer_empty()) { - s_last_empty_timestamp_ms = data->timestamp_us / 1000; - } - - // Note: the delta value overflows if the s_buffer is not drained for ~65s, - // but there should be more than enough time for it to drain in that window - accel_buffer_data.timestamp_delta_ms = ((data->timestamp_us / 1000) - - s_last_empty_timestamp_ms); - - // if we have one or more clients who fell behind reading out of the buffer, - // we will advance them until there is enough space available for the new data - bool rv = shared_circular_buffer_write(&s_buffer, (uint8_t *)&accel_buffer_data, - sizeof(accel_buffer_data), false /*advance_slackers*/); - if (!rv) { - PBL_LOG(LOG_LEVEL_WARNING, "Accel subscriber fell behind, truncating data"); - rv = shared_circular_buffer_write(&s_buffer, (uint8_t *)&accel_buffer_data, - sizeof(accel_buffer_data), true /*advance_slackers*/); - } - - PBL_ASSERTN(rv); - - prv_dispatch_data(true /* post_event */); -} - -void accel_cb_shake_detected(IMUCoordinateAxis axis, int32_t direction) { - PebbleEvent e = { - .type = PEBBLE_ACCEL_SHAKE_EVENT, - .accel_tap = { - .axis = axis, - .direction = direction, - }, - }; - - event_put(&e); -} - -void accel_cb_double_tap_detected(IMUCoordinateAxis axis, int32_t direction) { - PebbleEvent e = { - .type = PEBBLE_ACCEL_DOUBLE_TAP_EVENT, - .accel_tap = { - .axis = axis, - .direction = direction, - }, - }; - - event_put(&e); -} - -static void prv_handle_accel_driver_work_cb(void *data) { - // The accel manager is responsible for handling locking - mutex_lock_recursive(s_accel_manager_mutex); - AccelOffloadCallback cb = data; - cb(); - mutex_unlock_recursive(s_accel_manager_mutex); -} - -void accel_offload_work_from_isr(AccelOffloadCallback cb, bool *should_context_switch) { - PBL_ASSERTN(mcu_state_is_isr()); - - *should_context_switch = - new_timer_add_work_callback_from_isr(prv_handle_accel_driver_work_cb, cb); -} - -bool accel_manager_run_selftest(void) { - mutex_lock_recursive(s_accel_manager_mutex); - bool rv = accel_run_selftest(); - mutex_unlock_recursive(s_accel_manager_mutex); - return rv; -} - -#if !defined(PLATFORM_SILK) && !defined(PLATFORM_ASTERIX) && !defined(PLATFORM_OBELIX) -// Note: This selftest is only used for MFG today. When we start to build out a -// gyro API, we will need to come up with a more generic way to handle locking -// for a gyro only part vs gyro + accel part -extern bool gyro_run_selftest(void); -bool gyro_manager_run_selftest(void) { - mutex_lock_recursive(s_accel_manager_mutex); - bool rv = gyro_run_selftest(); - mutex_unlock_recursive(s_accel_manager_mutex); - return rv; -} -#endif - -void command_accel_peek(void) { - AccelData data; - - int result = sys_accel_manager_peek(&data); - PBL_LOG(LOG_LEVEL_DEBUG, "result: %d", result); - - char buffer[20]; - prompt_send_response_fmt(buffer, sizeof(buffer), "X: %"PRId16, data.x); - prompt_send_response_fmt(buffer, sizeof(buffer), "Y: %"PRId16, data.y); - prompt_send_response_fmt(buffer, sizeof(buffer), "Z: %"PRId16, data.z); -} - -void command_accel_num_samples(char *num_samples) { - int num = atoi(num_samples); - mutex_lock_recursive(s_accel_manager_mutex); - accel_set_num_samples(num); - mutex_unlock_recursive(s_accel_manager_mutex); -} - -#if UNITTEST -/* - * Helper routines strictly for unit tests - */ - -void test_accel_manager_get_subsample_info(AccelManagerState *state, uint16_t *num, uint16_t *den, - uint16_t *samps_per_update) { - *num = state->buffer_client.numerator; - *den = state->buffer_client.denominator; - *samps_per_update = state->samples_per_update; -} - -void test_accel_manager_reset(void) { - s_buffer = (SharedCircularBuffer){}; - AccelManagerState *state = (AccelManagerState *)s_data_subscribers; - while (state) { - AccelManagerState *free_state = state; - state = (AccelManagerState *)state->list_node.next; - kernel_free(free_state); - } - s_data_subscribers = NULL; - s_shake_subscribers_count = 0; - s_double_tap_subscribers_count = 0; -} - -#endif diff --git a/src/fw/services/common/accel_manager_types.h b/src/fw/services/common/accel_manager_types.h deleted file mode 100644 index ffeef87834..0000000000 --- a/src/fw/services/common/accel_manager_types.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Valid accelerometer sampling rates, in Hz -typedef enum { - //! 10 HZ sampling rate - ACCEL_SAMPLING_10HZ = 10, - //! 25 HZ sampling rate [Default] - ACCEL_SAMPLING_25HZ = 25, - //! 50 HZ sampling rate - ACCEL_SAMPLING_50HZ = 50, - //! 100 HZ sampling rate - ACCEL_SAMPLING_100HZ = 100, -} AccelSamplingRate; - -//! A single accelerometer sample for all three axes -typedef struct __attribute__((__packed__)) { - //! acceleration along the x axis - int16_t x; - //! acceleration along the y axis - int16_t y; - //! acceleration along the z axis - int16_t z; -} AccelRawData; - -//! A single accelerometer sample for all three axes including timestamp and -//! vibration rumble status. -typedef struct __attribute__((__packed__)) AccelData { - //! acceleration along the x axis - int16_t x; - //! acceleration along the y axis - int16_t y; - //! acceleration along the z axis - int16_t z; - - //! true if the watch vibrated when this sample was collected - bool did_vibrate; - - //! timestamp, in milliseconds - uint64_t timestamp; -} AccelData; diff --git a/src/fw/services/common/analytics/analytics.h b/src/fw/services/common/analytics/analytics.h deleted file mode 100644 index 846760908f..0000000000 --- a/src/fw/services/common/analytics/analytics.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include - -#include "kernel/pebble_tasks.h" -#include "util/uuid.h" -#include "analytics_metric_table.h" -#include "analytics_event.h" - - -#define ANALYTICS_LOG_DEBUG(fmt, args...) \ - PBL_LOG_D(LOG_DOMAIN_ANALYTICS, LOG_LEVEL_DEBUG, fmt, ## args) - -//! Possible values for the client argument when setting/updating a metric. This tells the -//! analytics code under which "blob" to put the metric. For device metrics, the client argument -//! is ignored, but passing in AnalyticsClient_System is basically good documentation. -//! For app metrics, the client can be AnalyticsClient_App, AnalyticsClient_Worker or -//! AnalyticsClient_CurrentTask -typedef enum AnalyticsClient { - AnalyticsClient_System, //! Put in the "device" blob. Illegal if the metric is an app metric. - AnalyticsClient_App, //! Put in the "app" blob with the UUID of the current foreground app - AnalyticsClient_Worker, //! Put in the "app" blob with the UUID of the current background - //! worker - AnalyticsClient_CurrentTask, //! Put in the "app" blob with the UUID of the current task (either - //! app or worker) - AnalyticsClient_Ignore, //! For internal use by the analytics module only -} AnalyticsClient; - - -void analytics_init(void); - -//! Set a scalar metric -//! @param metric The metric to set -//! @param val The new value -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -void analytics_set(AnalyticsMetric metric, int64_t val, AnalyticsClient client); - -//! Keeps val if it's larger than the previous measurement -//! @param metric The metric to set -//! @param val The value of the new measurement -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -void analytics_max(AnalyticsMetric metric, int64_t val, AnalyticsClient client); - -//! Set a scalar metric for an app blob by UUID -//! @param metric The metric to set. This should be an app metric -//! @param val The new value -//! @param uuid The uuid of the app blob -void analytics_set_for_uuid(AnalyticsMetric metric, int64_t val, const Uuid *uuid); - -//! Set an array metric -//! @param metric The metric to set -//! @param data The new data array -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -// TODO: Remove this, and add analytics_append_array or something. See PBL-5333 -void analytics_set_entire_array(AnalyticsMetric metric, const void *data, AnalyticsClient client); - -//! Increment a metric by 1 -//! @param metric The metric to increment -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client); - -//! Increment an app metric for an app with the given UUID by 1 -//! @param metric The metric to increment. This should be an app metric -//! @param uuid The uuid of the app blob -void analytics_inc_for_uuid(AnalyticsMetric metric, const Uuid *uuid); - -//! Increment a metric -//! @param metric The metric to increment -//! @param amount The amount to increment by -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -void analytics_add(AnalyticsMetric metric, int64_t amount, AnalyticsClient client); - -//! Increment an app metric for an app with the given UUID -//! @param metric The metric to increment. This should be an app metric -//! @param amount The amount to increment by -//! @param uuid The uuid of the app blob -void analytics_add_for_uuid(AnalyticsMetric metric, int64_t amount, const Uuid *uuid); - -//! Starts a stopwatch that integrates a "rate of things" over time. -//! @param metric The metric of the stopwatch to start -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -void analytics_stopwatch_start(AnalyticsMetric metric, AnalyticsClient client); - -//! Starts a stopwatch that integrates a "rate of things" over time. -//! @param metric The metric for which to start the stopwatch. -//! @param count_per_second The rate in number of things per second to count. -//! @param client If the metric is an app metric, this logs it to the app blob using the UUID of the given client. -//! If the metric is a device metric, client must be AnalyticsClient_System -//! For example, if you want to measure "bytes transferred" over time and know the transfer speed is 1024 bytes per -//! second, then you would pass in 1024 as count_per_second. -void analytics_stopwatch_start_at_rate(AnalyticsMetric metric, uint32_t count_per_second, AnalyticsClient client); - -//! Stops a stopwatch -//! @param metric The metric of the stopwatch -void analytics_stopwatch_stop(AnalyticsMetric metric); diff --git a/src/fw/services/common/analytics/analytics_event.h b/src/fw/services/common/analytics/analytics_event.h deleted file mode 100644 index a6d5621167..0000000000 --- a/src/fw/services/common/analytics/analytics_event.h +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/pebble_process_info.h" -#include "services/common/comm_session/session_analytics.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/timeline/item.h" -#include "util/attributes.h" -#include "util/build_id.h" -#include "util/time/time.h" -#include "util/uuid.h" - -#if !PLATFORM_TINTIN -#include "services/normal/vibes/vibe_score_info.h" -#endif - -// Every analytics blob we send out (device blob, app blob, or event blob) starts out with -// an 8-bit AnalyticsBlobKind followed by a 16-bit version. Here we define the format of the -// event blob. The device and app blobs are defined in analytics_metric_table.h. -// The ANALYTICS_EVENT_BLOB_VERSION value defined here will need to bumped whenever the format of -// the AnalyticsEventBlob structure changes - this includes if ANY of the unions inside of it -// change or a new AnalyticsEvent enum is added. -// Please do not cherrypick any change here into a release branch without first checking -// with Katharine, or something is very likely to break. -#define ANALYTICS_EVENT_BLOB_VERSION 32 - - -//! Types of events that can be logged outside of a heartbeat using analytics_logging_log_event() -typedef enum { - AnalyticsEvent_AppLaunch, - AnalyticsEvent_PinOpen, - AnalyticsEvent_PinAction, - AnalyticsEvent_CannedReponseSent, - AnalyticsEvent_CannedReponseFailed, - AnalyticsEvent_VoiceTranscriptionAccepted, - AnalyticsEvent_VoiceTranscriptionRejected, - AnalyticsEvent_PinAppLaunch, - AnalyticsEvent_BtClassicDisconnect, - AnalyticsEvent_BtLeDisconnect, - AnalyticsEvent_Crash, - AnalyticsEvent_LocalBtDisconnect, - AnalyticsEvent_BtLockupError, - AnalyticsEvent_BtClassicConnectionComplete, - AnalyticsEvent_BtLeConnectionComplete, - AnalyticsEvent_PinCreated, - AnalyticsEvent_PinUpdated, - AnalyticsEvent_BtLeAMS, - AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted, - AnalyticsEvent_StationaryModeSwitch, - AnalyticsEvent_HealthLegacySleep, - AnalyticsEvent_HealthLegacyActivity, - AnalyticsEvent_PutByteTime, - AnalyticsEvent_HealthInsightCreated, - AnalyticsEvent_HealthInsightResponse, - AnalyticsEvent_AppCrash, - AnalyticsEvent_VibeAccess, - AnalyticsEvent_HealthActivitySession, // Deprecated - AnalyticsEvent_BtAppLaunchError, - AnalyticsEvent_BtLePairingError, - AnalyticsEvent_BtClassicPairingError, - AnalyticsEvent_PebbleProtocolSystemSessionEnd, - AnalyticsEvent_PebbleProtocolAppSessionEnd, - AnalyticsEvent_AlarmCreated, - AnalyticsEvent_AlarmTriggered, - AnalyticsEvent_AlarmDismissed, - AnalyticsEvent_PPoGATTDisconnect, - AnalyticsEvent_BtChipBoot, - AnalyticsEvent_GetBytesStats, - AnalyticsEvent_RockyAppCrash, - AnalyticsEvent_AppOOMNative, - AnalyticsEvent_AppOOMRocky, - AnalyticsEvent_BtLeMicError, - AnalyticsEvent_BleHrmEvent, -} AnalyticsEvent; - -// AnalyticsEvent_BleHrmEvent -typedef enum { - BleHrmEventSubtype_SharingAccepted, - BleHrmEventSubtype_SharingDeclined, - BleHrmEventSubtype_SharingRevoked, - BleHrmEventSubtype_SharingTimeoutPopupPresented, -} BleHrmEventSubtype; - -typedef struct PACKED { - BleHrmEventSubtype subtype:8; -} AnalyticsEventBleHrmEvent; - -// AnalyticsEvent_AppLaunch event -typedef struct PACKED { - Uuid uuid; -} AnalyticsEventAppLaunch; - -// AnalyticsEvent_PinOpen/Create/Update events -typedef struct PACKED { - uint32_t time_utc; // pin utc time - Uuid parent_id; // owner app UUID -} AnalyticsEventPinOpenCreateUpdate; - -// AnalyticsEvent_PinAction events -typedef struct PACKED { - uint32_t time_utc; // pin utc time - Uuid parent_id; // owner app UUID - uint8_t type; // action type -} AnalyticsEventPinAction; - -// AnalyticsEvent_PinAppLaunch event -typedef struct PACKED { - uint32_t time_utc; // pins utc time - Uuid parent_id; // owner app UUID -} AnalyticsEventPinAppLaunch; - -// AnalyticsEvent_PinCreated events -typedef struct PACKED { - uint32_t time_utc; // pin utc time - Uuid parent_id; // owner app UUID -} AnalyticsEventPinCreated; - -// AnalyticsEvent_PinUpdated events -typedef struct PACKED { - uint32_t time_utc; // pin utc time - Uuid parent_id; // owner app UUID -} AnalyticsEventPinUpdated; - -// AnalyticsEvent_CannedResponse.* events. -typedef struct PACKED { - uint8_t response_size_bytes; -} AnalyticsEventCannedResponse; - -// AnalyticsEvent_VoiceResponse.* events. -typedef struct PACKED { - uint8_t num_sessions; - uint8_t error_count; - uint16_t response_size_bytes; - uint16_t response_len_chars; - uint32_t response_len_ms; - Uuid app_uuid; -} AnalyticsEventVoiceResponse; - -typedef struct PACKED { - uint8_t reason; // the connection status / reason we disconnected -} AnalyticsEventBtConnectionDisconnection; - -typedef struct PACKED { - uint8_t reason; // The reason we disconnected - uint8_t remote_bt_version; - uint16_t remote_bt_company_id; - uint16_t remote_bt_subversion_number; - uint16_t remote_features_supported; // placeholder for supported features -} AnalyticsEventBleDisconnection; - -typedef struct CommSession CommSession; - -typedef struct PACKED { - CommSessionCloseReason close_reason:8; - uint16_t duration_minutes; -} AnalyticsEventPebbleProtocolCommonSessionClose; - -typedef struct PACKED { - AnalyticsEventPebbleProtocolCommonSessionClose common; -} AnalyticsEventPebbleProtocolSystemSessionClose; - -typedef struct PACKED { - AnalyticsEventPebbleProtocolCommonSessionClose common; - Uuid app_uuid; -} AnalyticsEventPebbleProtocolAppSessionClose; - -typedef struct PACKED { - uint32_t error_code; -} AnalyticsEventBtError; - -typedef struct PACKED { - uint8_t crash_code; - uint32_t link_register; -} AnalyticsEventCrash; - -typedef struct PACKED { - uint32_t lr; - uint16_t conn_handle; -} AnalyticsEvent_LocalBTDisconnect; - -typedef struct PACKED { - uint8_t type; - int32_t aux_info; -} AnalyticsEvent_AMSData; - -typedef struct PACKED { - time_t timestamp; - uint8_t state_change; -} AnalyticsEvent_StationaryStateChangeData; - -typedef struct PACKED { - uint16_t start_minute; // minute of day when sleep started (midnight is minute 0) - uint16_t wake_minute; // minute of day when sleep ended - uint16_t total_minutes; // total minutes of sleep - uint16_t deep_minutes; // deep minutes of sleep -} AnalyticsEvent_HealthLegacySleepData; - -typedef struct PACKED { - uint16_t duration_minutes; // duration in minutes - uint16_t steps; // # of steps -} AnalyticsEvent_HealthLegacyActivityData; - -typedef struct PACKED { - uint8_t insight_type; // numerical id of insight - uint8_t activity_type; // activity type, one of ActivitySessionType - uint8_t response_id; // numerical id of response - uint32_t time_utc; // insight utc time, activity start UTC if activity type is not none -} AnalyticsEvent_HealthInsightResponseData; - -typedef struct PACKED { - uint8_t insight_type; // numerical id of insight - uint32_t time_utc; // insight utc time - uint8_t percent_tier; // above average / below average -} AnalyticsEvent_HealthInsightCreatedData; - -typedef struct PACKED { - bool ppogatt; // true if transport is PPOGATT, else SPP - uint8_t conn_intvl_1_25ms; // if ppogatt, the connection interval at end of FW update - bool crc_good; // true if calculated CRC matches expected CRC - uint8_t type; // see PutBytesObjectType - uint32_t bytes_transferred; - uint32_t elapsed_time_ms; - uint32_t conn_events; - uint16_t sync_errors; - uint16_t skip_errors; - uint16_t other_errors; -} AnalyticsEvent_PutByteTimeData; - -//! Used for both AnalyticsEvent_AppCrash and AnalyticsEvent_RockyAppCrash event types! -typedef struct PACKED { - Uuid uuid; - uint32_t pc; - uint32_t lr; - uint8_t build_id_slice[4]; -} AnalyticsEvent_AppCrashData; - -typedef enum VibePatternFeature { - VibePatternFeature_Notifications = 1 << 0, - VibePatternFeature_PhoneCalls = 1 << 1, - VibePatternFeature_Alarms = 1 << 2, -} VibePatternFeature; - -typedef struct PACKED { - uint8_t feature; - uint8_t vibe_pattern_id; -} AnalyticsEvent_VibeAcessData; - -typedef struct PACKED { - uint16_t activity_type; // activity type, one of ActivitySessionType - uint32_t start_utc; // start time of activity, in UTC seconds - uint32_t elapsed_sec; // length of activity in seconds. -} AnalyticsEvent_HealthActivitySessionData; // Deprecated - -typedef struct PACKED { - uint8_t hour; - uint8_t minute; - bool is_smart; - uint8_t kind; - uint8_t scheduled_days[DAYS_PER_WEEK]; -} AnalyticsEvent_AlarmData; - -typedef struct PACKED AnalyticsEvent_BtChipBootData { - uint8_t build_id[BUILD_ID_EXPECTED_LEN]; - uint32_t crash_lr; - uint32_t reboot_reason; -} AnalyticsEvent_BtChipBootData; - -typedef struct PACKED AnalyticsEvent_PPoGATTDisconnectData { - bool successful_reconnect; - uint32_t time_utc; // utc time -} AnalyticsEvent_PPoGATTDisconnectData; - -typedef struct PACKED AnalyticsEvent_GetBytesStatsData { - bool ppogatt; // true if transport is PPOGATT, else SPP - uint8_t conn_intvl_1_25ms; // if ppogatt, the connection interval at end of FW update - uint8_t type; // see GetBytesObjectType - uint32_t bytes_transferred; - uint32_t elapsed_time_ms; - uint32_t conn_events; - uint16_t sync_errors; - uint16_t skip_errors; - uint16_t other_errors; -} AnalyticsEvent_GetBytesStatsData; - -typedef struct PACKED AnalyticsEvent_AppOomData { - Uuid app_uuid; - uint32_t requested_size; - uint32_t total_size; - uint16_t total_free; - uint16_t largest_free_block; -} AnalyticsEvent_AppOomData; - -typedef struct PACKED { - uint8_t kind; // set to ANALYTICS_BLOB_KIND_EVENT - uint16_t version; // set to ANALYTICS_EVENT_BLOB_VERSION - AnalyticsEvent event:8; // type of event - uint32_t timestamp; - union PACKED { - AnalyticsEventBtError bt_error; - AnalyticsEventAppLaunch app_launch; - AnalyticsEventPinOpenCreateUpdate pin_open_create_update; - AnalyticsEventPinAction pin_action; - AnalyticsEventPinAppLaunch pin_app_launch; - AnalyticsEventCannedResponse canned_response; - AnalyticsEventVoiceResponse voice_response; - AnalyticsEventBtConnectionDisconnection bt_connection_disconnection; - AnalyticsEventBleDisconnection ble_disconnection; - AnalyticsEventCrash crash_report; - AnalyticsEvent_LocalBTDisconnect local_bt_disconnect; - AnalyticsEvent_AMSData ams; - AnalyticsEvent_StationaryStateChangeData sd; - AnalyticsEvent_HealthLegacySleepData health_sleep; - AnalyticsEvent_HealthLegacyActivityData health_activity; - AnalyticsEvent_PutByteTimeData pb_time; - AnalyticsEvent_HealthInsightCreatedData health_insight_created; - AnalyticsEvent_HealthInsightResponseData health_insight_response; - AnalyticsEvent_AppCrashData app_crash_report; - AnalyticsEvent_VibeAcessData vibe_access_data; - AnalyticsEvent_HealthActivitySessionData health_activity_session; - AnalyticsEventPebbleProtocolCommonSessionClose pp_common_session_close; - AnalyticsEventPebbleProtocolSystemSessionClose pp_system_session_close; - AnalyticsEventPebbleProtocolAppSessionClose pp_app_session_close; - AnalyticsEvent_AlarmData alarm; - AnalyticsEvent_BtChipBootData bt_chip_boot; - AnalyticsEvent_PPoGATTDisconnectData ppogatt_disconnect; - AnalyticsEvent_GetBytesStatsData get_bytes_stats; - AnalyticsEvent_AppOomData app_oom; - AnalyticsEventBleHrmEvent ble_hrm; - }; -} AnalyticsEventBlob; - -//! @param type AnalyticsEvent_AppOOMNative or AnalyticsEvent_AppOOMRocky -//! @param total_free Sum of free bytes -//! @param largest_free_block The largest, contiguous, free block of memory. -//! @note Intended to be called from the app/worker task (calls sys_analytics_logging_log_event). -void analytics_event_app_oom(AnalyticsEvent type, - uint32_t requested_size, uint32_t total_size, - uint32_t total_free, uint32_t largest_free_block); - -//! Log an app launch event to analytics -//! @param uuid app's UUID -void analytics_event_app_launch(const Uuid *uuid); - -//! Log a pin open event to analytics -//! @param timestamp the UTC timestamp of the pin -//! @param parent_id UUID of the owner of the pin -void analytics_event_pin_open(time_t timestamp, const Uuid *parent_id); - -//! Log a generic pin action event (i.e. not an app launch) to analytics -//! @param timestamp the UTC timestamp of the pin -//! @param parent_id UUID of the owner of the pin -//! @param action_title the title of the action -void analytics_event_pin_action(time_t timestamp, const Uuid *parent_id, - TimelineItemActionType action_type); - -//! Log a pin launch app event to analytics -//! @param timestamp the UTC timestamp of the pin -//! @param parent_id UUID of the owner of the pin -void analytics_event_pin_app_launch(time_t timestamp, const Uuid *parent_id); - -//! Log a pin created event to analytics -//! @param timestamp the UTC timestamp of the pin -//! @param parent_id UUID of the owner of the pin -void analytics_event_pin_created(time_t timestamp, const Uuid *parent_id); - -//! Log a pin updated event to analytics -//! @param timestamp the UTC timestamp of the pin -//! @param parent_id UUID of the owner of the pin -void analytics_event_pin_updated(time_t timestamp, const Uuid *parent_id); - -//! Log a canned response event -//! @param response pointer to response text -//! @param successfully_sent true if successfully sent, false if a failure occurred -void analytics_event_canned_response(const char *response, bool successfully_sent); - -//! Log voice transcription event -//! @param event_type event type - must be one of \ref AnalyticsEvent_VoiceTranscriptionAccepted, -//! \ref AnalyticsEvent_VoiceTranscriptionRejected, or -//! \ref AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted -//! @param response_size_bytes accepted response size in number of bytes -//! @param response_len_chars accepted response length in number of unicode characters -//! @param response_len_ms accepted response time in ms -//! @param error_count number of errors that occurred -//! @param num_sessions number of transcription sessions initiated to get the accepted user response -//! or before the user exited the UI -//! @param app_uuid pointer to app or system UUID -void analytics_event_voice_response(AnalyticsEvent event_type, uint16_t response_size_bytes, - uint16_t response_len_chars, uint32_t response_len_ms, - uint8_t error_count, uint8_t num_sessions, Uuid *app_uuid); - -//! Log BLE HRM event -void analytics_event_ble_hrm(BleHrmEventSubtype subtype); - -//! Log bluetooth disconnection event -//! @param type - AnalyticsEvent_BtLeConnectionComplete, AnalyticsEvent_BtClassicDisconnect, etc -//! @param reason - The HCI Error code representing the disconnect reason. (See -//! "OVERVIEW OF ERROR CODES" in BT Core Specification or the HCI_ERROR_CODEs in HCITypes.h -void analytics_event_bt_connection_or_disconnection(AnalyticsEvent type, uint8_t reason); - -//! Log bluetooth le disconnection event -//! @param reason - The HCI Error code associated with the disconnect -//! remote_bt_version, remote_bt_company_id & remote_bt_subversion come from the version -//! response received from the remote device -void analytics_event_bt_le_disconnection(uint8_t reason, uint8_t remote_bt_version, - uint16_t remote_bt_company_id, - uint16_t remote_bt_subversion); - -//! Log bluetooth error -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error); - -//! Log when the CC2564x BT chip becomes unresponsive -void analytics_event_bt_cc2564x_lockup_error(void); - -//! Log when app_launch trigger failed. -void analytics_event_bt_app_launch_error(uint8_t gatt_error); - -//! Log when a Pebble Protocol session is closed. -void analytics_event_session_close(bool is_system_session, const Uuid *optional_app_uuid, - CommSessionCloseReason reason, uint16_t session_duration_mins); - -//! Log crash event to analytics -//! @param crash_code Reboot reason (see RebootReasonCode in reboot_reason.h) -//! @param link_register Last running function before crash -void analytics_event_crash(uint8_t crash_code, uint32_t link_register); - -//! Log the reason we disconnect locally as an event. (Mainly interested in -//! seeing how often Bluetopia kills us due to unexpected L2CAP errors) -//! @param handle - the handle being disconnected -//! @param lr - the LR of the function which invoked the disconnect -void analytics_event_local_bt_disconnect(uint16_t conn_handle, uint32_t lr); - -//! Log an Apple Media Service event. -//! @param type See AMSAnalyticsEvent -//! @param aux_info Additional information specific to the type of event -void analytics_event_ams(uint8_t type, int32_t aux_info); - -void analytics_event_stationary_state_change(time_t timestamp, uint8_t state_change_reason); - -//! Log a health insight created event -//! @param timestamp the UTC timestamp of the insight -//! @param insight_type numerical id (from \ref ActivityInsightType enum) of the insight -//! @param pct_tier numerical id (from \ref PercentageTier enum) of the percent tier from -//! average for the metric the insight is about. -void analytics_event_health_insight_created(time_t timestamp, - ActivityInsightType insight_type, - PercentTier pct_tier); - -//! Log a health insight response event -//! @param timestamp the UTC timestamp of the insight -//! @param insight_type numerical id (from \ref ActivityInsightType enum) of the insight -//! @param activity_type type of activity, one of ActivitySessionType -//! @param response_id numerical id (from \ref ActivityInsightResponseType enum) of response -void analytics_event_health_insight_response(time_t timestamp, ActivityInsightType insight_type, - ActivitySessionType activity_type, - ActivityInsightResponseType response_id); - -//! Tracks duration of time it takes to recieve byte transfers over putbytes -//! and statistics on the type of transfer and whether the data stored was valid -//! @param session the session used to transfer the data -//! @param crc_good whether or not the CRC for the blob transferred is valid -//! @param type the PutBytesObjectType that was transferred -//! @param bytes_transferred the number of bytes transferred -//! @param elapsed_time_ms the amount of time spent transmitting the bytes -//! @param conn_events, sync_errors, other_errors LE connection event statistics (if available) -void analytics_event_put_byte_stats( - CommSession *session, bool crc_good, uint8_t type, - uint32_t bytes_transferred, uint32_t elapsed_time_ms, - uint32_t conn_events, uint32_t sync_errors, uint32_t skip_errors, uint32_t other_errors); - -//! Log an App Crash event to analytics -//! @param uuid app's UUID -//! @param pc Current running function before crash -//! @param lr Last running function before crash -//! @param build_id Pointer to the build_id buffer of the application (can be NULL) -void analytics_event_app_crash(const Uuid *uuid, uint32_t pc, uint32_t lr, const uint8_t *build_id, - bool is_rocky_app); - -#if !PLATFORM_TINTIN -//! Log the user's vibration pattern -//! @param VibePatternFeature Notifications, Phone Calls, or Alarms -void analytics_event_vibe_access(VibePatternFeature vibe_feature, VibeScoreId pattern_id); -#endif - -typedef struct AlarmInfo AlarmInfo; - -//! Sends an analytic event about an alarm event -//! @param even_type The type of alarm analytic event that occurred -//! @param info Information about the alarm -void analytics_event_alarm(AnalyticsEvent event_type, const AlarmInfo *info); - -//! Sends an analytic event about an alarm event -//! @param even_type The type of alarm analytic event that occurred -//! @param info Information about the alarm -void analytics_event_bt_chip_boot(uint8_t build_id[BUILD_ID_EXPECTED_LEN], - uint32_t crash_lr, uint32_t reboot_reason_code); - -//! Log forced PPoGATT disconnection caused by too many resets -void analytics_event_PPoGATT_disconnect(time_t timestamp, bool successful_reconnect); - -//! Log out to analytics stats about a GetBytes transfer -void analytics_event_get_bytes_stats( - CommSession *session, uint8_t type, uint32_t bytes_transferred, uint32_t elapsed_time_ms, - uint32_t conn_events, uint32_t sync_errors, uint32_t skip_errors, uint32_t other_errors); diff --git a/src/fw/services/common/analytics/analytics_external.h b/src/fw/services/common/analytics/analytics_external.h deleted file mode 100644 index 79058fb98d..0000000000 --- a/src/fw/services/common/analytics/analytics_external.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "drivers/rtc.h" - -//! Request other modules to update their analytics fields -void analytics_external_update(void); - -extern void analytics_external_collect_battery(void); -extern void analytics_external_collect_accel_xyz_delta(void); -extern void analytics_external_collect_app_cpu_stats(void); -extern void analytics_external_collect_app_flash_read_stats(void); -extern void analytics_external_collect_cpu_stats(void); -extern void analytics_external_collect_stop_inhibitor_stats(RtcTicks now_ticks); -extern void analytics_external_collect_bt_parameters(void); -extern void analytics_external_collect_bt_pairing_info(void); -extern void analytics_external_collect_ble_parameters(void); -extern void analytics_external_collect_ble_pairing_info(void); -extern void analytics_external_collect_backlight_settings(void); -extern void analytics_external_collect_notification_settings(void); -extern void analytics_external_collect_system_theme_settings(void); -extern void analytics_external_collect_ancs_info(void); -extern void analytics_external_collect_dls_stats(void); -extern void analytics_external_collect_i2c_stats(void); -extern void analytics_external_collect_system_flash_statistics(void); -extern void analytics_external_collect_stack_free(void); -extern void analytics_external_collect_alerts_preferences(void); -extern void analytics_external_collect_timeline_pin_stats(void); -extern void analytics_external_collect_display_offset(void); -extern void analytics_external_collect_pfs_stats(void); -extern void analytics_external_collect_chip_specific_parameters(void); -extern void analytics_external_collect_bt_chip_heartbeat(void); -extern void analytics_external_collect_kernel_heap_stats(void); -extern void analytics_external_collect_accel_samples_received(void); diff --git a/src/fw/services/common/analytics/analytics_heartbeat.h b/src/fw/services/common/analytics/analytics_heartbeat.h deleted file mode 100644 index 66daac3959..0000000000 --- a/src/fw/services/common/analytics/analytics_heartbeat.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "util/uuid.h" - -#include "analytics_metric_table.h" - -typedef enum { - ANALYTICS_HEARTBEAT_KIND_DEVICE = 0, - ANALYTICS_HEARTBEAT_KIND_APP = 1, -} AnalyticsHeartbeatKind; - -typedef struct { - // Note that the first byte of data[] is also the kind of the heartbeat. - // We could merge these into one, but I'm not sure what kind of code gcc - // will generate when so many fields are unaligned, and I don't really want - // to risk the codesize. - AnalyticsHeartbeatKind kind; - uint8_t data[0]; -} AnalyticsHeartbeat; - -uint32_t analytics_heartbeat_kind_data_size(AnalyticsHeartbeatKind kind); - -void analytics_heartbeat_set(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, int64_t val); -void analytics_heartbeat_set_array(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, uint32_t index, int64_t val); -void analytics_heartbeat_set_entire_array(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, const void* data); - -int64_t analytics_heartbeat_get(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric); -int64_t analytics_heartbeat_get_array(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, uint32_t index); -const Uuid *analytics_heartbeat_get_uuid(AnalyticsHeartbeat *heartbeat); - -AnalyticsHeartbeat *analytics_heartbeat_device_create(); -AnalyticsHeartbeat *analytics_heartbeat_app_create(const Uuid *uuid); -void analytics_heartbeat_clear(AnalyticsHeartbeat *heartbeat); - -// Turning this on is helpful when debugging analytics subsystems. It changes the heartbeat -// to run once every 10 seconds instead of once every hour and also prints out the value of -// each metric. Also helpful is to change LOG_DOMAIN_ANALYTICS from 0 to 1 to enable extra -// logging messages (found in core/system/logging.h). -// Another useful debugging tip is that doing a long-select on any item in the launcher menu -// will trigger data logging to do an immediate flush of logged data to the phone. -// #define ANALYTICS_DEBUG - -void analytics_heartbeat_print(AnalyticsHeartbeat *heartbeat); diff --git a/src/fw/services/common/analytics/analytics_logging.h b/src/fw/services/common/analytics/analytics_logging.h deleted file mode 100644 index 4b65951d77..0000000000 --- a/src/fw/services/common/analytics/analytics_logging.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "analytics_event.h" - -typedef enum { - ANALYTICS_BLOB_KIND_DEVICE_HEARTBEAT, - ANALYTICS_BLOB_KIND_APP_HEARTBEAT, - ANALYTICS_BLOB_KIND_EVENT -} AnalyticsBlobKind; - -void analytics_logging_init(void); - -//! Used internally to log raw analytics events -void analytics_logging_log_event(AnalyticsEventBlob *event_blob); - -//! Exposed for unit testing only -void analytics_logging_system_task_cb(void *ignored); diff --git a/src/fw/services/common/analytics/analytics_metric.h b/src/fw/services/common/analytics/analytics_metric.h deleted file mode 100644 index 5a4d41b452..0000000000 --- a/src/fw/services/common/analytics/analytics_metric.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "analytics_metric_table.h" - -typedef enum { - ANALYTICS_METRIC_KIND_DEVICE, - ANALYTICS_METRIC_KIND_APP, - ANALYTICS_METRIC_KIND_MARKER, - ANALYTICS_METRIC_KIND_UNKNOWN, -} AnalyticsMetricKind; - -typedef enum { - ANALYTICS_METRIC_ELEMENT_TYPE_NIL, - ANALYTICS_METRIC_ELEMENT_TYPE_UINT8, - ANALYTICS_METRIC_ELEMENT_TYPE_UINT16, - ANALYTICS_METRIC_ELEMENT_TYPE_UINT32, - ANALYTICS_METRIC_ELEMENT_TYPE_INT8, - ANALYTICS_METRIC_ELEMENT_TYPE_INT16, - ANALYTICS_METRIC_ELEMENT_TYPE_INT32, -} AnalyticsMetricElementType; - -void analytics_metric_init(void); - -AnalyticsMetricElementType analytics_metric_element_type(AnalyticsMetric metric); - -uint32_t analytics_metric_num_elements(AnalyticsMetric metric); -uint32_t analytics_metric_element_size(AnalyticsMetric metric); -uint32_t analytics_metric_size(AnalyticsMetric metric); - -bool analytics_metric_is_array(AnalyticsMetric metric); -bool analytics_metric_is_unsigned(AnalyticsMetric metric); - -uint32_t analytics_metric_offset(AnalyticsMetric metric); - -AnalyticsMetricKind analytics_metric_kind(AnalyticsMetric metric); diff --git a/src/fw/services/common/analytics/analytics_metric_table.h b/src/fw/services/common/analytics/analytics_metric_table.h deleted file mode 100644 index 980f4dbe90..0000000000 --- a/src/fw/services/common/analytics/analytics_metric_table.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Increment when adding fields or changing the types of fields & tell Katharine what -// you changed so the new definition is in sync with the server -// Please do not cherrypick any change here into a release branch without first checking -// with Katharine, or something is very likely to break. - -#define ANALYTICS_APP_HEARTBEAT_BLOB_VERSION 11 -#define ANALYTICS_DEVICE_HEARTBEAT_BLOB_VERSION 69 - - -// Note that every analytics blob we send out (device blob, app blob, or event blob) starts out with -// an 8-bit AnalyticsBlobKind followed by a 16-bit version. This header defines the format of the -// device and app blobs. The format of an event blob is defined in analytics_logging.c - -// The device and application heartbeats. Defining them like this allows -// us to also use one table for defining the enum, creating the enum<->type -// table and to generate names for debugging (when enabled). -// DO NOT MOVE ELEMENTS AROUND, THIS WILL CHANGE THE BINARY FORMAT! -#define ANALYTICS_METRIC_TABLE(MARKER, DEVICE, APP, UINT8, UINT16, UINT32, INT8, INT16, INT32) \ - MARKER(ANALYTICS_METRIC_INVALID) \ - MARKER(ANALYTICS_METRIC_START) \ - MARKER(ANALYTICS_DEVICE_METRIC_START) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLOB_KIND, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLOB_VERSION, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMESTAMP, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIME_INTERVAL, UINT32) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_DEVICE_UP_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_CODE, UINT32) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE_DELTA, INT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT_DELTA, INT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_PLUGGED_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BATTERY_SAMPLE_SKIP_COUNT_EXCEEDED, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_STATIONARY_TIME_MINUTES, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_LR, UINT32) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_RUNNING_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_STOP_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_SLEEP_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_MAIN_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BUTTON_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BLUETOOTH_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_DISPLAY_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BACKLIGHT_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_COMM_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_FLASH_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_I2C1_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_ACCESSORY, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_MIC, UINT32) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_APP_INFO_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_INCOMING_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_OUTGOING_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_ANSWER_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_DECLINE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_POP_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_START_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_END_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PHONE_CALL_TIME, UINT32) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_AIRPLANE_MODE_QUICK_TOGGLE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PAIRING_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PAIRING_RECORDS_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PAIRING_FORGET_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_CONNECT_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_CONNECT_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_ACTIVE_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PRIVATE_BYTE_IN_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PRIVATE_BYTE_OUT_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PUBLIC_BYTE_IN_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PUBLIC_BYTE_OUT_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PEBBLE_SPP_APP_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PEBBLE_PPOGATT_APP_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_PEBBLE_APP_LAUNCH_SUCCESS_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_SYSTEM_SESSION_OPEN_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_IOS_IBEACON_WAKEUP_TIMEOUT_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_SNIFF_INTERVAL, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_AFH_CHAN_USE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_ROLE, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_MODE_CHANGE_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_SNIFF_ENTER_REQ, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_SNIFF_EXIT_REQ, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_HCILL_CNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_NUM_SDP_REQ, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_UART_BYTES_IN, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_UART_BYTES_OUT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_DISCONNECT_NOT_PAIRABLE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_DISCONNECT_MFI_FAILURE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_DISCONNECT_IAP_PACKET_FAILURE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_DISCONNECT_IAP_WATCHDOG_FAILURE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_ZERO_ACL_CREDITS_MAX_DURATION_TICKS, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BT_COMM_SESSION_SEND_DATA_FAIL_COUNT, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_ENCRYPTED_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_NO_INTENT_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_GATT_DROPPED_NOTIFICATIONS_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_GATT_STALLED_NOTIFICATIONS_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_GATT_UNHANDLED1_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_GATT_UNHANDLED2_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_GATT_UNHANDLED3_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_ESTIMATED_BYTES_ADVERTISED_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CHAN_USE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONN_PARAMS, UINT16, 2) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONN_PARAM_UPDATE_FAILED_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_CONN_EVENT_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_RECORDS_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_FORGET_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_LINK_QUALITY_SUM, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_RSSI_SUM, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_BT_PERSISTENT_STORAGE_UPDATES, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_MFI_RESET_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_MFI_ON_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_I2C_MAX_TRANSFER_DURATION_TICKS, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_I2C_ERROR_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BUTTON_PRESSED_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_SHAKE_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_DOUBLE_TAP_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_PEEK_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_SAMPLE_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_XYZ_DELTA, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_FIFO_OVERRUN_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACCEL_RESET_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ALARM_SOUNDED_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_FPGA_REPROGRAM_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_DND_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISSED_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISS_ALL_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_CLOSED_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_BYTE_IN_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_JUMBOJI_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DS_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_PARSE_ERROR_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DISCOVERED_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DS_SUBSCRIBE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_SUBSCRIBE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_DS_SUBSCRIBE_FAIL_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_SUBSCRIBE_FAIL_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_CONNECT_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_NS_FLAGS_BITSET, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_SMS_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_GROUP_SMS_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_MUTED_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_FILTERED_BECAUSE_MUTED_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_REMINDER_RECEIVED_COUNT, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_ROCKY_LAUNCH_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_ROCKY_CRASHED_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_USER_LAUNCH_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_CRASHED_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_THROTTLED_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_NOTIFIED_DISCONNECTED_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_FLUSH_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_REALLOC_COUNT, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_MAX_SPOOLED_BYTES, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_ENDPOINT_SENDS, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_PING_SENT_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_PONG_RECEIVED_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_APP_QUICK_LAUNCH_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_SETTING_SHAKE_TO_LIGHT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_INTENSITY_PCT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_TIMEOUT_SEC, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_SETTING_VIBRATION_STRENGTH, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_ALERTS_MASK, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_ALERTS_DND_ACTIVE_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ALERTS_DND_PREFS_BITMASK, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_SYSTEM_THEME_TEXT_STYLE, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_WATCH_ONLY_TIME, UINT32) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_PAST_LAUNCH_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_FUTURE_LAUNCH_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_PAST_NAVIGATION_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_FUTURE_NAVIGATION_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_VISIBLE_CALENDAR_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_VISIBLE_OTHER_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_HOURLY_CALENDAR_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_HOURLY_OTHER_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLOB_DB_EVENT_COUNT, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_FLASH_READ_BYTES_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_FLASH_WRITE_BYTES_COUNT, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_FLASH_ERASE_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_KERNEL_HEAP_MIN_HEADROOM_BYTES, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_MAIN, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_BACKGROUND, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_STACK_FREE_BLUETOPIA_BIG, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_STACK_FREE_BLUETOPIA_MEDIUM, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_STACK_FREE_BLUETOPIA_SMALL, UINT16) \ - DEVICE(ANALYTICS_DEVICE_METRIC_STACK_FREE_NEWTIMERS, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACTION_INVOKED_FROM_TIMELINE_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACTION_INVOKED_FROM_MODAL_NOTIFICATION_COUNT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_ACTION_INVOKED_FROM_NOTIFICATION_APP_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_HEALTH_CURRENT_STEP_COUNT__DEPRECATED, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_DISPLAY_UPDATES_PER_HOUR, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_DISPLAY_OFFSET_X, INT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_DISPLAY_OFFSET_Y, INT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_DISPLAY_OFFSET_MODIFIED_COUNT, UINT8) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_PFS_SPACE_FREE_KB, UINT16) \ - \ - DEVICE(ANALYTICS_DEVICE_METRIC_HRM_ACCEL_DATA_MISSING, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_HRM_ON_TIME, UINT32) \ - DEVICE(ANALYTICS_DEVICE_METRIC_HRM_WATCHDOG_TIMEOUT, UINT8) \ - DEVICE(ANALYTICS_DEVICE_METRIC_BLE_HRM_SHARING_TIME, UINT32) \ - \ - MARKER(ANALYTICS_DEVICE_METRIC_END) \ - \ - \ - MARKER(ANALYTICS_APP_METRIC_START) \ - \ - APP(ANALYTICS_APP_METRIC_BLOB_KIND, UINT8) \ - APP(ANALYTICS_APP_METRIC_BLOB_VERSION, UINT16) \ - APP(ANALYTICS_APP_METRIC_TIMESTAMP, UINT32) \ - \ - APP(ANALYTICS_APP_METRIC_TIME_INTERVAL, UINT32) \ - APP(ANALYTICS_APP_METRIC_UUID, UINT8, 16) \ - APP(ANALYTICS_APP_METRIC_SDK_MAJOR_VERSION, UINT8) \ - APP(ANALYTICS_APP_METRIC_SDK_MINOR_VERSION, UINT8) \ - APP(ANALYTICS_APP_METRIC_APP_MAJOR_VERSION, UINT8) \ - APP(ANALYTICS_APP_METRIC_APP_MINOR_VERSION, UINT8) \ - APP(ANALYTICS_APP_METRIC_RESOURCE_TIMESTAMP, UINT32) \ - \ - APP(ANALYTICS_APP_METRIC_LAUNCH_COUNT, UINT8) \ - APP(ANALYTICS_APP_METRIC_USER_LAUNCH_COUNT, UINT8) \ - APP(ANALYTICS_APP_METRIC_QUICK_LAUNCH_COUNT, UINT8) \ - APP(ANALYTICS_APP_METRIC_FRONT_MOST_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_CRASHED_COUNT, UINT8) \ - APP(ANALYTICS_APP_METRIC_ROCKY_LAUNCH_COUNT, UINT8) \ - APP(ANALYTICS_APP_METRIC_ROCKY_CRASHED_COUNT, UINT8) \ - \ - APP(ANALYTICS_APP_METRIC_CPU_RUNNING_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_CPU_SLEEP_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_CPU_STOP_TIME, UINT32) \ - \ - APP(ANALYTICS_APP_METRIC_MEM_NATIVE_HEAP_SIZE, UINT32) \ - APP(ANALYTICS_APP_METRIC_MEM_NATIVE_HEAP_PEAK, UINT32) \ - APP(ANALYTICS_APP_METRIC_MEM_ROCKY_HEAP_PEAK, UINT32) \ - APP(ANALYTICS_APP_METRIC_MEM_ROCKY_HEAP_WASTE, UINT32) \ - APP(ANALYTICS_APP_METRIC_MEM_ROCKY_RECURSIVE_MEMORYPRESSURE_EVENT_COUNT, UINT8) \ - \ - APP(ANALYTICS_APP_METRIC_BG_CPU_RUNNING_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_BG_CPU_SLEEP_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_BG_CPU_STOP_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_BG_CRASHED_COUNT, UINT8) \ - \ - APP(ANALYTICS_APP_METRIC_BUTTONS_PRESSED_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_ACCEL_SHAKE_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_ACCEL_PEEK_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_ACCEL_SAMPLE_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_BACKLIGHT_ON_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_BACKLIGHT_ON_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_VIBRATOR_ON_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_VIBRATOR_ON_TIME, UINT32) \ - APP(ANALYTICS_APP_METRIC_DISPLAY_WRITE_TIME, UINT32) \ - \ - APP(ANALYTICS_APP_METRIC_MSG_IN_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_MSG_BYTE_IN_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_MSG_OUT_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_MSG_BYTE_OUT_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_MSG_DROP_COUNT, UINT16) \ - \ - APP(ANALYTICS_APP_METRIC_LOG_OUT_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_LOG_BYTE_OUT_COUNT, UINT32) \ - \ - APP(ANALYTICS_APP_METRIC_FLASH_READ_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_FLASH_READ_BYTES_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_FLASH_WRITE_BYTES_COUNT, UINT32) \ - APP(ANALYTICS_APP_METRIC_FLASH_SUBSECTOR_ERASE_COUNT, UINT32) \ - \ - MARKER(ANALYTICS_APP_METRIC_END) \ - MARKER(ANALYTICS_METRIC_END) - - -#define ENUM(name, ...) name, -typedef enum { - ANALYTICS_METRIC_TABLE(ENUM, ENUM, ENUM,,,,,,) -} AnalyticsMetric; -#undef ENUM diff --git a/src/fw/services/common/analytics/analytics_storage.h b/src/fw/services/common/analytics/analytics_storage.h deleted file mode 100644 index 2add0e2f9c..0000000000 --- a/src/fw/services/common/analytics/analytics_storage.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "analytics.h" -#include "kernel/pebble_tasks.h" -#include "analytics_heartbeat.h" - -typedef struct { - ListNode node; - AnalyticsHeartbeat *heartbeat; -} AnalyticsHeartbeatList; - -typedef void (*AnalyticsHeartbeatCallback)(AnalyticsHeartbeat *heartbeat, void *data); - -void analytics_storage_init(void); - -void analytics_storage_take_lock(void); -bool analytics_storage_has_lock(void); -void analytics_storage_give_lock(void); - -// Must hold the lock before using any of the functions below this marker. -AnalyticsHeartbeat *analytics_storage_hijack_device_heartbeat(); -AnalyticsHeartbeatList *analytics_storage_hijack_app_heartbeats(); - -AnalyticsHeartbeat *analytics_storage_find(AnalyticsMetric metric, const Uuid *uuid, - AnalyticsClient client); - -const Uuid *analytics_uuid_for_client(AnalyticsClient client); diff --git a/src/fw/services/common/animation_service.c b/src/fw/services/common/animation_service.c deleted file mode 100644 index fcf47d02d2..0000000000 --- a/src/fw/services/common/animation_service.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/ui/animation_private.h" -#include "applib/app_logging.h" - -#include "kernel/events.h" -#include "kernel/kernel_applib_state.h" - -#include "process_management/process_manager.h" -#include "process_state/app_state/app_state.h" - -#include "services/common/new_timer/new_timer.h" - -#include "system/passert.h" - -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" - - -// The timer ID used for each task that we support -static TimerID s_kernel_main_timer_id = TIMER_INVALID_ID; -static TimerID s_app_timer_id = TIMER_INVALID_ID; - -static bool s_kernel_main_event_pending; -static bool s_app_event_pending; - -// ------------------------------------------------------------------------------------------ -void animation_service_cleanup(PebbleTask task) { - PBL_ASSERT_TASK(PebbleTask_KernelMain); - - if (task == PebbleTask_KernelMain) { - if (s_kernel_main_timer_id != TIMER_INVALID_ID) { - new_timer_delete(s_kernel_main_timer_id); - s_kernel_main_timer_id = TIMER_INVALID_ID; - } - s_kernel_main_event_pending = false; - } else if (task == PebbleTask_App) { - if (s_app_timer_id != TIMER_INVALID_ID) { - new_timer_delete(s_app_timer_id); - s_app_timer_id = TIMER_INVALID_ID; - } - s_app_event_pending = false; - } -} - - -// ------------------------------------------------------------------------------------------ -static void prv_timer_callback(void * context) { - PebbleTask task = (PebbleTask)context; - - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = animation_private_timer_callback - } - }; - - switch (task) { - case PebbleTask_KernelMain: - if (!s_kernel_main_event_pending) { - s_kernel_main_event_pending = true; - e.callback.data = (void *)kernel_applib_get_animation_state(); - event_put(&e); - } - break; - case PebbleTask_App: - if (!s_app_event_pending) { - e.callback.data = (void *)app_state_get_animation_state(); - s_app_event_pending = process_manager_send_event_to_process(task, &e); - } - break; - default: - PBL_CROAK("Invalid task %s", pebble_task_get_name(pebble_task_get_current())); - } -} - - -// ------------------------------------------------------------------------------------------ -DEFINE_SYSCALL(void, animation_service_timer_event_received, void) { - PebbleTask task = pebble_task_get_current(); - - if (task == PebbleTask_KernelMain) { - s_kernel_main_event_pending = false; - } else if (task == PebbleTask_App) { - s_app_event_pending = false; - } else { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_failed(); - } - return; - } -} - - -// ------------------------------------------------------------------------------------------ -DEFINE_SYSCALL(void, animation_service_timer_schedule, uint32_t ms) { - PebbleTask task = pebble_task_get_current(); - TimerID *timer_id; - - if (task == PebbleTask_KernelMain) { - timer_id = &s_kernel_main_timer_id; - } else if (task == PebbleTask_App) { - timer_id = &s_app_timer_id; - } else { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_failed(); - } - return; - } - - // Need to create the timer? - bool success = false; - if (*timer_id == TIMER_INVALID_ID) { - *timer_id = new_timer_create(); - } - - // Schedule/reschedule it - if (*timer_id != TIMER_INVALID_ID) { - success = new_timer_start(*timer_id, ms, prv_timer_callback, (void *)(uintptr_t)task, - 0 /*flags */); - } - if (!success) { - APP_LOG(APP_LOG_LEVEL_ERROR, "Error scheduling timer"); - } -} - - -// --------------------------------------------------------------------------- -// Used for unit tests only -TimerID animation_service_test_get_timer_id(void) { - return s_kernel_main_timer_id; -} diff --git a/src/fw/services/common/animation_service.h b/src/fw/services/common/animation_service.h deleted file mode 100644 index 6dfab1f09e..0000000000 --- a/src/fw/services/common/animation_service.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "kernel/pebble_tasks.h" - -//! @file animation_service.h -//! Manage the system resources used by the applib/animation module. - -//! Register the timer to fire in N ms. When it fires, the animation_private_timer_callback() -//! will be called and passed the AnimationState for that task. -void animation_service_timer_schedule(uint32_t ms); - -//! Acknowledge that we received an event sent by the animation timer -void animation_service_timer_event_received(void); - -//! Destroy the animation resoures used by the given task. Called by the process_manager when a -// process exits -void animation_service_cleanup(PebbleTask task); diff --git a/src/fw/services/common/battery/battery_curve.h b/src/fw/services/common/battery/battery_curve.h deleted file mode 100644 index d48b2d98ac..0000000000 --- a/src/fw/services/common/battery/battery_curve.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include -#include - -// Handles battery mV <-> % conversion - -typedef enum { - BATTERY_CURVE_COMPENSATE_STATUS_LED, - BATTERY_CURVE_COMPENSATE_COUNT -} BatteryCurveVoltageCompensationKey; - -//! Set compensation value to be applied to battery voltage when calculating percentage charge. -//! For example, if an LED is constantly on, the voltage being measured is going to drop due to the -//! internal resistance of the battery. -void battery_curve_set_compensation(BatteryCurveVoltageCompensationKey key, int mv); - -void battery_curve_set_full_voltage(uint16_t voltage); - -//! Returns the corresponding battery percentage as a ratio32. -uint32_t battery_curve_sample_ratio32_charge_percent(uint32_t battery_mv, bool is_charging); - -uint32_t battery_curve_lookup_percent_by_voltage(uint32_t battery_mv, bool is_charging); - -int32_t battery_curve_lookup_percent_with_scaling_factor( - int battery_mv, bool is_charging, uint32_t scaling_factor); - -uint32_t battery_curve_get_hours_remaining(uint32_t percent_remaining); - -uint32_t battery_curve_get_percent_remaining(uint32_t hours); - -// This is used by unit tests and QEMU -uint32_t battery_curve_lookup_voltage_by_percent(uint32_t percent, bool is_charging); diff --git a/src/fw/services/common/battery/battery_monitor.h b/src/fw/services/common/battery/battery_monitor.h deleted file mode 100644 index bcffd5e2ba..0000000000 --- a/src/fw/services/common/battery/battery_monitor.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "services/common/battery/battery_state.h" -#include "services/common/new_timer/new_timer.h" -#include - -// The battery monitor handles power state and associated service control, in response to battery -// state changes. This includes low power and critical modes. - -void battery_monitor_init(void); -void battery_monitor_handle_state_change_event(PreciseBatteryChargeState state); - -// Use the battery state to determine if UI elements should be locked out -// because the battery is too low -bool battery_monitor_critical_lockout(void); - -// For unit tests -TimerID battery_monitor_get_standby_timer_id(void); diff --git a/src/fw/services/common/battery/nrf_fuel_gauge/battery_curve.c b/src/fw/services/common/battery/nrf_fuel_gauge/battery_curve.c deleted file mode 100644 index a7a6c761ac..0000000000 --- a/src/fw/services/common/battery/nrf_fuel_gauge/battery_curve.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "services/common/battery/battery_curve.h" - -uint32_t battery_curve_get_percent_remaining(uint32_t hours) { - return ((hours * 100) / BOARD_CONFIG_POWER.battery_capacity_hours) + - BOARD_CONFIG_POWER.low_power_threshold; -} - -// TODO: nRF Fuel gauge lib provides TTE estimation -// First we need to evaluate if it's good enough for our use case, then, if -// it is, we need to refactor APIs and battery FSM a bit to use TTE value -// directly (i.e. not from % remaining). -uint32_t battery_curve_get_hours_remaining(uint32_t percent_remaining) { - if (percent_remaining <= BOARD_CONFIG_POWER.low_power_threshold) { - return 0; - } - percent_remaining -= BOARD_CONFIG_POWER.low_power_threshold; - return ((BOARD_CONFIG_POWER.battery_capacity_hours * percent_remaining) / 100); -} - -#if CAPABILITY_HAS_LED -// TODO: Probably can go away -void battery_curve_set_compensation(BatteryCurveVoltageCompensationKey key, int mv) { -} -#endif - -// Stubs for tests (need fixing/test adjustments) -int32_t battery_curve_lookup_percent_with_scaling_factor( - int battery_mv, bool is_charging, uint32_t scaling_factor) { - return 0U; -} - -uint32_t battery_curve_lookup_voltage_by_percent(uint32_t percent, bool is_charging) { - return 0U; -} \ No newline at end of file diff --git a/src/fw/services/common/battery/nrf_fuel_gauge/battery_state.c b/src/fw/services/common/battery/nrf_fuel_gauge/battery_state.c deleted file mode 100644 index 2ea0580c09..0000000000 --- a/src/fw/services/common/battery/nrf_fuel_gauge/battery_state.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "board/board.h" -#include "drivers/battery.h" -#include "drivers/rtc.h" -#include "kernel/events.h" -#include "services/common/analytics/analytics.h" -#include "services/common/battery/battery_state.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/ratio.h" - -#include "nrf_fuel_gauge.h" - -#define ALWAYS_UPDATE_PCT 10.0f -#define RECONNECTION_DELAY_MS (1 * 1000) -// TODO: Adjust sample rate based on activity periods once we have good -// power consumption profiles -#define BATTERY_SAMPLE_RATE_S 1 - -#define LOG_MIN_SEC 30 - -static const struct battery_model prv_battery_model = { -#if PLATFORM_ASTERIX -#include "battery_asterix.inc" -#elif PLATFORM_OBELIX -// FIXME(OBELIX): Add battery model once ready! -#include "battery_asterix.inc" -#else -#error "Battery model not defined for this platform" -#endif -}; - -static PreciseBatteryChargeState s_last_battery_charge_state; -static TimerID s_periodic_timer_id = TIMER_INVALID_ID; - -static BatteryChargeStatus s_last_chg_status; -static uint64_t prv_ref_time; -static int32_t s_last_voltage_mv; -static int32_t s_analytics_last_voltage_mv; -static uint8_t s_analytics_last_pct; -static uint32_t s_last_tte; -static uint32_t s_last_ttf; -static RtcTicks s_last_log; - -static void prv_schedule_update(uint32_t delay, bool force_update); - -static void prv_charge_status_inform(BatteryChargeStatus chg_status) { - union nrf_fuel_gauge_ext_state_info_data state_info; - int ret; - - switch (chg_status) { - case BatteryChargeStatusComplete: - state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_COMPLETE; - break; - case BatteryChargeStatusTrickle: - state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_TRICKLE; - break; - case BatteryChargeStatusCC: - state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_CC; - break; - case BatteryChargeStatusCV: - state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_CV; - break; - default: - state_info.charge_state = NRF_FUEL_GAUGE_CHARGE_STATE_IDLE; - break; - } - - ret = nrf_fuel_gauge_ext_state_update(NRF_FUEL_GAUGE_EXT_STATE_INFO_CHARGE_STATE_CHANGE, - &state_info); - PBL_ASSERTN(ret == 0); -} - -static void prv_battery_state_put_change_event(PreciseBatteryChargeState state) { - PebbleEvent e = { - .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, - .battery_state = - { - .new_state = state, - }, - }; - event_put(&e); -} - -static void prv_update_state(void *force_update) { - BatteryChargeStatus chg_status; - BatteryConstants constants; - RtcTicks now, delta; - uint8_t pct_int; - bool is_plugged; - bool is_charging; - bool update; - float pct; - int ret; - - update = force_update != NULL; - - is_plugged = battery_is_usb_connected_impl(); - if (is_plugged != s_last_battery_charge_state.is_plugged) { - ret = nrf_fuel_gauge_ext_state_update(is_plugged - ? NRF_FUEL_GAUGE_EXT_STATE_INFO_VBUS_CONNECTED - : NRF_FUEL_GAUGE_EXT_STATE_INFO_VBUS_DISCONNECTED, - NULL); - PBL_ASSERTN(ret == 0); - s_last_battery_charge_state.is_plugged = is_plugged; - update = true; - } - - ret = battery_charge_status_get(&chg_status); - PBL_ASSERTN(ret == 0); - - if (chg_status != s_last_chg_status) { - s_last_chg_status = chg_status; - prv_charge_status_inform(chg_status); - } - - is_charging = is_plugged && !(chg_status == BatteryChargeStatusComplete || - chg_status == BatteryChargeStatusUnknown); - if (is_charging != s_last_battery_charge_state.is_charging) { - s_last_battery_charge_state.is_charging = is_charging; - update = true; - } - - ret = battery_get_constants(&constants); - PBL_ASSERTN(ret == 0); - - s_last_voltage_mv = constants.v_mv; - - now = rtc_get_ticks(); - delta = (now - prv_ref_time) / RTC_TICKS_HZ; - prv_ref_time = now; - - pct = nrf_fuel_gauge_process((float)constants.v_mv / 1000.0f, (float)constants.i_ua / 1000000.0f, - (float)constants.t_mc / 1000.0f, (float)delta, NULL); - - pct_int = (uint8_t)ceilf(pct); - if (pct_int != s_last_battery_charge_state.pct) { - s_last_battery_charge_state.pct = pct_int; - s_last_battery_charge_state.charge_percent = (uint32_t)(pct * RATIO32_MAX) / 100U; - update = true; - } - - if (s_last_battery_charge_state.is_charging) { - float ttf; - - ttf = nrf_fuel_gauge_ttf_get(); - if (!isnanf(ttf)) { - s_last_ttf = (uint32_t)ttf; - } - - s_last_tte = 0U; - } else { - float tte; - - tte = nrf_fuel_gauge_tte_get(); - if (!isnanf(tte)) { - s_last_tte = (uint32_t)tte; - } - - s_last_ttf = 0U; - } - - PBL_LOG(LOG_LEVEL_DEBUG_VERBOSE, - "Battery state: v_mv: %ld, i_ua: %ld, t_mc: %ld, td: %lu, soc: %u, tte: %lu, ttf: %lu", - constants.v_mv, constants.i_ua, constants.t_mc, (uint32_t)delta, - s_last_battery_charge_state.pct, s_last_tte, s_last_ttf); - - if (update || (((now - s_last_log) / RTC_TICKS_HZ > LOG_MIN_SEC) && - (s_last_battery_charge_state.is_charging || (pct < ALWAYS_UPDATE_PCT)))) { - PBL_LOG(LOG_LEVEL_INFO, "Percent: %" PRIu8 ", V: %" PRId32 " mV, I: %" PRId32 " uA, " - "T: %" PRId32 " mC, charging: %s, plugged: %s", - s_last_battery_charge_state.pct, constants.v_mv, constants.i_ua, constants.t_mc, - s_last_battery_charge_state.is_charging ? "yes" : "no", - s_last_battery_charge_state.is_plugged ? "yes" : "no"); - prv_battery_state_put_change_event(s_last_battery_charge_state); - s_last_log = now; - } -} - -static void prv_update_callback(void *data) { - new_timer_stop(s_periodic_timer_id); - system_task_add_callback(prv_update_state, data); -} - -static void prv_callback_from_regular_timer(void *data) { - // no need to stop the new_timer here, since this came from the regular_timer - system_task_add_callback(prv_update_state, data); -} - -static void prv_schedule_update(uint32_t delay, bool force_update) { - bool success = new_timer_start(s_periodic_timer_id, delay, prv_update_callback, - (void *)force_update, 0 /*flags*/); - PBL_ASSERTN(success); -} - -void battery_state_force_update(void) { prv_schedule_update(0, true); } - -void battery_state_init(void) { - int ret; - struct nrf_fuel_gauge_init_parameters parameters = {0}; - struct nrf_fuel_gauge_runtime_parameters runtime_parameters = {0}; - BatteryConstants constants; - - parameters.model = &prv_battery_model; - - ret = battery_get_constants(&constants); - PBL_ASSERTN(ret == 0); - - parameters.v0 = (float)constants.v_mv / 1000.0f; - parameters.i0 = (float)constants.i_ua / 1000000.0f; - parameters.t0 = (float)constants.t_mc / 1000.0f; - - s_last_voltage_mv = constants.v_mv; - - prv_ref_time = rtc_get_ticks(); - - ret = nrf_fuel_gauge_init(¶meters, NULL); - PBL_ASSERTN(ret == 0); - - ret = nrf_fuel_gauge_ext_state_update( - NRF_FUEL_GAUGE_EXT_STATE_INFO_CHARGE_CURRENT_LIMIT, - &(union nrf_fuel_gauge_ext_state_info_data){ - .charge_current_limit = (float)NPM1300_CONFIG.chg_current_ma / 1000.0f}); - PBL_ASSERTN(ret == 0); - - ret = nrf_fuel_gauge_ext_state_update( - NRF_FUEL_GAUGE_EXT_STATE_INFO_TERM_CURRENT, - &(union nrf_fuel_gauge_ext_state_info_data){ - .charge_term_current = - (float)(NPM1300_CONFIG.chg_current_ma * NPM1300_CONFIG.term_current_pct / 100U) / - 1000.0f}); - PBL_ASSERTN(ret == 0); - - runtime_parameters.a = NAN_F; - runtime_parameters.b = NAN_F; - runtime_parameters.c = NAN_F; - runtime_parameters.d = NAN_F; - runtime_parameters.discard_positive_deltaz = true; - - nrf_fuel_gauge_param_adjust(&runtime_parameters); - - ret = battery_charge_status_get(&s_last_chg_status); - PBL_ASSERTN(ret == 0); - - prv_charge_status_inform(s_last_chg_status); - - s_last_battery_charge_state.is_plugged = battery_is_usb_connected_impl(); - s_last_battery_charge_state.is_charging = s_last_battery_charge_state.is_plugged && - !(s_last_chg_status == BatteryChargeStatusComplete || - s_last_chg_status == BatteryChargeStatusUnknown); - - s_periodic_timer_id = new_timer_create(); - - battery_state_force_update(); - - static RegularTimerInfo battery_regular_timer = { - .cb = prv_callback_from_regular_timer - }; - regular_timer_add_multisecond_callback(&battery_regular_timer, BATTERY_SAMPLE_RATE_S); -} - -void battery_state_handle_connection_event(bool is_connected) { - prv_schedule_update(RECONNECTION_DELAY_MS, true); -} - -DEFINE_SYSCALL(BatteryChargeState, sys_battery_get_charge_state, void) { - return battery_get_charge_state(); -} - -BatteryChargeState battery_get_charge_state(void) { - BatteryChargeState state; - - state.charge_percent = s_last_battery_charge_state.pct; - state.is_charging = s_last_battery_charge_state.is_charging; - state.is_plugged = s_last_battery_charge_state.is_plugged; - - return state; -} - -// For unit tests -TimerID battery_state_get_periodic_timer_id(void) { return s_periodic_timer_id; } - -uint16_t battery_state_get_voltage(void) { return (uint16_t)s_last_voltage_mv; } - -#include "console/prompt.h" -void command_print_battery_status(void) { - char buffer[32]; - - prompt_send_response_fmt(buffer, 32, "%" PRId32 " mV", s_last_voltage_mv); - prompt_send_response_fmt(buffer, 32, "soc: %" PRIu8 "%% (%" PRIu32 ")", - s_last_battery_charge_state.pct, - s_last_battery_charge_state.charge_percent); - if (s_last_tte == 0U) { - prompt_send_response_fmt(buffer, 32, "tte: N/A"); - } else { - prompt_send_response_fmt(buffer, 32, "tte: %" PRIu32 "s", s_last_tte); - } - if (s_last_ttf == 0U) { - prompt_send_response_fmt(buffer, 32, "ttf: N/A"); - } else { - prompt_send_response_fmt(buffer, 32, "ttf: %" PRIu32 "s", s_last_ttf); - } - prompt_send_response_fmt(buffer, 32, "plugged: %s", - s_last_battery_charge_state.is_plugged ? "YES" : "NO"); - prompt_send_response_fmt(buffer, 32, "charging: %s", - s_last_battery_charge_state.is_charging ? "YES" : "NO"); -} - -///////////////// -// Analytics - -// Note that this is run on a different thread than battery_state! -void analytics_external_collect_battery(void) { - // This should not be called for an hour after bootup - int32_t d_mv; - uint8_t d_pct; - - d_mv = s_last_voltage_mv - s_analytics_last_voltage_mv; - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE, s_last_voltage_mv, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE_DELTA, d_mv, AnalyticsClient_System); - s_analytics_last_voltage_mv = s_last_voltage_mv; - - d_pct = s_last_battery_charge_state.pct - s_analytics_last_pct; - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT_DELTA, d_pct, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT, s_last_battery_charge_state.pct, - AnalyticsClient_System); - s_analytics_last_pct = s_last_battery_charge_state.pct; -} - -static void prv_set_forced_charge_state(bool is_charging) { - battery_force_charge_enable(is_charging); - - // Trigger an immediate update to the state machine: may trigger an event - battery_state_force_update(); -} - -void command_battery_charge_option(const char *option) { - if (!strcmp("disable", option)) { - prv_set_forced_charge_state(false); - } else if (!strcmp("enable", option)) { - prv_set_forced_charge_state(true); - } -} diff --git a/src/fw/services/common/battery/voltage/battery_curve.c b/src/fw/services/common/battery/voltage/battery_curve.c deleted file mode 100644 index 17b65e8b7d..0000000000 --- a/src/fw/services/common/battery/voltage/battery_curve.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/battery/battery_curve.h" - -#include "board/board.h" -#include "system/logging.h" -#include "util/math.h" -#include "util/ratio.h" -#include "util/size.h" - -typedef struct VoltagePoint { - uint8_t percent; - uint16_t voltage; -} VoltagePoint; - -// TODO: Move these curves somewhere else. Related: PBL-21049 - -#ifdef PLATFORM_TINTIN -// When the voltage drops below these (mV), the watch will start heading for standby (after delay) -#define BATTERY_CRITICAL_VOLTAGE_CHARGING 3200 -#define BATTERY_CRITICAL_VOLTAGE_DISCHARGING 3100 -// Battery Tables for non-Snowy -static VoltagePoint discharge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_DISCHARGING}, - {2, 3410}, - {5, 3600}, - {10, 3670}, - {20, 3710}, - {30, 3745}, - {40, 3775}, - {50, 3810}, - {60, 3860}, - {70, 3925}, - {80, 4000}, - {90, 4080}, - {100, 4120}, -}; - -static const VoltagePoint charge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_CHARGING}, - {5, 3725}, - {10, 3750}, - {20, 3790}, - {30, 3830}, - {40, 3845}, - {50, 3870}, - {60, 3905}, - {70, 3970}, - {80, 4025}, - {90, 4090}, - {100, 4130}, -}; -#elif BOARD_SNOWY_S3 || PLATFORM_ROBERT -// When the voltage drops below these (mV), the watch will start heading for standby (after delay) -#define BATTERY_CRITICAL_VOLTAGE_CHARGING 3700 -#define BATTERY_CRITICAL_VOLTAGE_DISCHARGING 3300 -// Battery Tables for Bobby Smiles -static VoltagePoint discharge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_DISCHARGING}, - {2, 3465}, - {5, 3615}, - {10, 3685}, - {20, 3725}, - {30, 3760}, - {40, 3795}, - {50, 3830}, - {60, 3885}, - {70, 3955}, - {80, 4065}, - {90, 4160}, - {100, 4250}, -}; - -static const VoltagePoint charge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_CHARGING}, - {2, 3850}, - {5, 3935}, - {10, 4000}, - {20, 4040}, - {30, 4090}, - {40, 4145}, - {50, 4175}, - {60, 4225}, - {70, 4250}, -}; -#elif PLATFORM_SNOWY || PLATFORM_CALCULUS -// When the voltage drops below these (mV), the watch will start heading for standby (after delay) -#define BATTERY_CRITICAL_VOLTAGE_CHARGING 3500 -#define BATTERY_CRITICAL_VOLTAGE_DISCHARGING 3300 -// Battery Tables for Snowy -static VoltagePoint discharge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_DISCHARGING}, - {2, 3500}, - {5, 3600}, - {10, 3640}, - {20, 3690}, - {30, 3730}, - {40, 3750}, - {50, 3790}, - {60, 3840}, - {70, 3910}, - {80, 4000}, - {90, 4120}, - {100, 4250}, -}; - -static const VoltagePoint charge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_CHARGING}, - {10, 3970}, - {20, 4020}, - {30, 4060}, - {40, 4090}, - {50, 4130}, - {60, 4190}, - {70, 4250}, -}; -#elif PLATFORM_SPALDING -// When the voltage drops below these (mV), the watch will start heading for standby (after delay) -#define BATTERY_CRITICAL_VOLTAGE_CHARGING 3700 -#define BATTERY_CRITICAL_VOLTAGE_DISCHARGING 3300 -// Battery Tables for Spalding -static VoltagePoint discharge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_DISCHARGING}, - {2, 3470}, - {5, 3600}, - {10, 3680}, - {20, 3720}, - {30, 3760}, - {40, 3790}, - {50, 3830}, - {60, 3875}, - {70, 3950}, - {80, 4050}, - {90, 4130}, - {100, 4250}, -}; - -static const VoltagePoint charge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_CHARGING}, - {10, 3950}, - {20, 3990}, - {30, 4030}, - {40, 4090}, - {50, 4180}, - {60, 4230}, - {70, 4250}, -}; - -// TODO(ASTERIX,OBELIX): Needs customization for Asterix/Obelix -#elif PLATFORM_SILK || PLATFORM_ASTERIX || PLATFORM_OBELIX -// When the voltage drops below these (mV), the watch will start heading for standby (after delay) -#define BATTERY_CRITICAL_VOLTAGE_CHARGING 3550 -#define BATTERY_CRITICAL_VOLTAGE_DISCHARGING 3300 -// Battery Tables for Silk -static VoltagePoint discharge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_DISCHARGING}, - {2, 3490}, - {5, 3615}, - {10, 3655}, - {20, 3700}, - {30, 3735}, - {40, 3760}, - {50, 3800}, - {60, 3855}, - {70, 3935}, - {80, 4025}, - {90, 4120}, - {100, 4230} -}; - -static const VoltagePoint charge_curve[] = { - {0, BATTERY_CRITICAL_VOLTAGE_CHARGING}, - {2, 3570}, - {5, 3600}, - {10, 3645}, - {20, 3730}, - {30, 3800}, - {40, 3860}, - {50, 3915}, - {60, 3970}, - {70, 4030}, - {80, 4095}, - {90, 4175}, - {100, 4260} -}; - -#else -#error "No battery curve for platform!" -#endif - -static const uint8_t NUM_DISCHARGE_POINTS = ARRAY_LENGTH(discharge_curve); -static const uint8_t NUM_CHARGE_POINTS = ARRAY_LENGTH(charge_curve); - -static int s_battery_compensation_values[BATTERY_CURVE_COMPENSATE_COUNT]; - -// Shifts the 100% reference on the discharge curve, as long as it -// doesn't drop below the next highest point. -void battery_curve_set_full_voltage(uint16_t voltage) { - voltage = MAX(voltage, discharge_curve[NUM_DISCHARGE_POINTS-2].voltage + 1); - discharge_curve[NUM_DISCHARGE_POINTS-1].voltage = voltage; -} - -static uint32_t prv_lookup_percent_by_voltage( - int battery_mv, bool is_charging, uint32_t scaling_factor) { - VoltagePoint const * const battery_curve = (is_charging) ? charge_curve : discharge_curve; - uint8_t const num_curve_points = (is_charging) ? NUM_CHARGE_POINTS : NUM_DISCHARGE_POINTS; - - // Constrain the voltage between the min and max points of the curve - if (battery_mv <= battery_curve[0].voltage) { - return battery_curve[0].percent * scaling_factor; - } else if (battery_mv >= battery_curve[num_curve_points-1].voltage) { - return battery_curve[num_curve_points-1].percent * scaling_factor; - } - - // search through the curves for the next charge level... - uint32_t charge_index; - for (charge_index = num_curve_points-2; - (charge_index > 0) && (battery_mv < battery_curve[charge_index].voltage); - --charge_index) { - } - - // linearly interpolate between battery_curve[charge_index] and battery_curve[charge_index+1] - uint32_t delta_mv = (battery_mv - battery_curve[charge_index].voltage); - uint32_t delta_next_mv = (battery_curve[charge_index + 1].voltage - - battery_curve[charge_index].voltage); - uint32_t delta_next_percent = (battery_curve[charge_index + 1].percent - - battery_curve[charge_index].percent); - - uint32_t start_percent = battery_curve[charge_index].percent * scaling_factor; - delta_next_percent *= scaling_factor; - - uint32_t charge_percent = - (((uint64_t)delta_next_percent * delta_mv) / delta_next_mv) + start_percent; - return charge_percent; -} - -static uint32_t prv_sample_scaled_charge_percent( - uint32_t battery_mv, bool is_charging, uint32_t scaling_factor) { - int compensate = 0; // We compensate 5% during rounding, so don't do here - - for (int i = 0; i < BATTERY_CURVE_COMPENSATE_COUNT; ++i) { - compensate += s_battery_compensation_values[i]; - } - - return prv_lookup_percent_by_voltage(battery_mv + compensate, is_charging, scaling_factor); -} - -uint32_t battery_curve_sample_ratio32_charge_percent(uint32_t battery_mv, bool is_charging) { - const uint32_t scaling_factor = ratio32_from_percent(100) / 100 + 1; - return prv_sample_scaled_charge_percent(battery_mv, is_charging, scaling_factor); -} - -void battery_curve_set_compensation(BatteryCurveVoltageCompensationKey key, int mv) { - s_battery_compensation_values[key] = mv; -} - -// This is used by unit tests and QEMU -uint32_t battery_curve_lookup_voltage_by_percent(uint32_t percent, bool is_charging) { - VoltagePoint const * const battery_curve = (is_charging) ? charge_curve : discharge_curve; - uint32_t const num_curve_points = (is_charging) ? NUM_CHARGE_POINTS : NUM_DISCHARGE_POINTS; - - // Clip if above curve upper bound - if (percent > battery_curve[num_curve_points-1].percent) { - return battery_curve[num_curve_points-1].voltage; - } - - // search through the curves for the next charge level... - uint32_t charge_index; - for (charge_index = num_curve_points-2; - (charge_index > 0) && (percent < battery_curve[charge_index].percent); - --charge_index) { - } - - // linearly interpolate between battery_curve[charge_index] and battery_curve[charge_index+1] - uint32_t delta_next_mv = (battery_curve[charge_index+1].voltage - - battery_curve[charge_index].voltage); - uint32_t delta_next_percent = (battery_curve[charge_index+1].percent - - battery_curve[charge_index].percent); - uint32_t battery_mv = battery_curve[charge_index].voltage + - ((percent - battery_curve[charge_index].percent) * delta_next_mv) / - delta_next_percent; - return battery_mv; -} - -//! This call is used internally with PreciseBatteryChargeState -//! So must remove low_power_threshold to get correct remaining hours -//! before low_power_mode is triggered -uint32_t battery_curve_get_hours_remaining(uint32_t percent_remaining) { - if (percent_remaining <= BOARD_CONFIG_POWER.low_power_threshold) { - return 0; - } - percent_remaining -= BOARD_CONFIG_POWER.low_power_threshold; - return ((BOARD_CONFIG_POWER.battery_capacity_hours * percent_remaining) / 100); -} - -//! This call is used internally with PreciseBatteryChargeState -//! So must add low_power_threshold to get percentage in terms of -//! PreciseBatteryChargeState (which includes low_power_mode) -uint32_t battery_curve_get_percent_remaining(uint32_t hours) { - return ((hours * 100) / BOARD_CONFIG_POWER.battery_capacity_hours) + - BOARD_CONFIG_POWER.low_power_threshold; -} - -// for unit tests and analytics -int32_t battery_curve_lookup_percent_with_scaling_factor( - int battery_mv, bool is_charging, uint32_t scaling_factor) { - return (prv_lookup_percent_by_voltage(battery_mv, is_charging, scaling_factor)); -} diff --git a/src/fw/services/common/battery/voltage/battery_state.c b/src/fw/services/common/battery/voltage/battery_state.c deleted file mode 100644 index 8bce8913d0..0000000000 --- a/src/fw/services/common/battery/voltage/battery_state.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/battery/battery_state.h" - -#include "board/board.h" -#include "debug/power_tracking.h" -#include "drivers/battery.h" -#include "kernel/events.h" -#include "kernel/util/stop.h" -#include "services/common/analytics/analytics.h" -#include "services/common/battery/battery_curve.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" -#include "util/ratio.h" - -#ifdef DEBUG_BATTERY_STATE -#define BATTERY_SAMPLE_RATE_MS 1000 -#else -#define BATTERY_SAMPLE_RATE_MS (60 * 1000) -#endif - -typedef void (*EntryFunc)(void); - -typedef enum { - ConnectionStateInvalid, - ConnectionStateChargingPlugged, - ConnectionStateDischargingPlugged, - ConnectionStateDischargingUnplugged -} ConnectionStateID; - -typedef struct ConnectionState { - EntryFunc enter; -} ConnectionState; - -static void prv_update_plugged_change(void); -static void prv_update_done_charging(void); - -static const ConnectionState s_transitions[] = { - [ConnectionStateChargingPlugged] = { .enter = prv_update_plugged_change }, - [ConnectionStateDischargingPlugged] = { .enter = prv_update_done_charging }, - [ConnectionStateDischargingUnplugged] = { .enter = prv_update_plugged_change } -}; - -typedef struct BatteryState { - uint64_t init_time; - uint32_t percent; - uint16_t voltage; - uint8_t skip_count; - ConnectionStateID connection; -} BatteryState; - -static BatteryState s_last_battery_state; -static TimerID s_periodic_timer_id = TIMER_INVALID_ID; -static int s_analytics_previous_mv = 0; - -static void prv_schedule_update(uint32_t delay, bool force_update); -PreciseBatteryChargeState prv_get_precise_charge_state(const BatteryState *state); - -static void prv_transition(BatteryState *state, ConnectionStateID next_state) { - state->connection = next_state; - s_transitions[state->connection].enter(); -} - -static void prv_update_plugged_change(void) { - // If the connection state changed or we finished charging, reset the filter since we're - // probably switching to a new curve. - battery_state_reset_filter(); - - - bool is_charging = battery_charge_controller_thinks_we_are_charging(); - if (is_charging) { - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME, AnalyticsClient_System); - } else { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME); - } - - bool is_plugged = battery_is_usb_connected(); - if (is_plugged) { - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BATTERY_PLUGGED_TIME, AnalyticsClient_System); - } else { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BATTERY_PLUGGED_TIME); - } -} - -static void prv_update_done_charging(void) { - prv_update_plugged_change(); - - // Amount in mV to drop the "Full" voltage by to briefly stay at 100% once unplugged - const uint16_t BATTERY_FULL_FUDGE_AMOUNT = 10; - PBL_LOG(LOG_LEVEL_DEBUG, "Done charging - Updating curve"); - battery_curve_set_full_voltage(s_last_battery_state.voltage - BATTERY_FULL_FUDGE_AMOUNT); -} - -static void battery_state_put_change_event(PreciseBatteryChargeState state) { - PebbleEvent e = { - .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, - .battery_state = { - .new_state = state, - }, - }; - event_put(&e); -} - -void battery_state_reset_filter(void) { - s_last_battery_state.voltage = battery_get_millivolts(); - // Reset the stablization timer in case we encountered a current spike during the reset - s_last_battery_state.init_time = rtc_get_ticks(); -} - -static uint32_t prv_filter_voltage(uint32_t avg_mv, uint32_t battery_mv) { - // Basic low-pass filter - See PBL-23637 - const uint8_t VOLTAGE_FILTER_BETA = 2; - uint32_t avg = (avg_mv << VOLTAGE_FILTER_BETA); - avg -= avg_mv; - avg += battery_mv; - return avg >> VOLTAGE_FILTER_BETA; -} - -static bool prv_is_stable(const BatteryState *state) { - // After a reboot, we typically source a lot of current which can drastically impact - // our mV readings due to the internal resistance of the battery. We use the - // system_likely_stabilized flag as an indicator of how trustworthy our readings are - const uint64_t STABLE_TICKS = 3 * 60 * RTC_TICKS_HZ; - uint64_t elapsed_ticks = rtc_get_ticks() - state->init_time; - return elapsed_ticks > STABLE_TICKS; -} - -static ConnectionStateID prv_get_connection_state(void) { - const bool charging = battery_charge_controller_thinks_we_are_charging(); - const bool plugged_in = battery_is_usb_connected(); - - if (plugged_in) { - if (charging) { - return ConnectionStateChargingPlugged; - } else { - return ConnectionStateDischargingPlugged; - } - } else { - if (charging) { - // Since we can't be charging and disconnected, - // just log a warning and pretend we aren't charging. - PBL_LOG(LOG_LEVEL_WARNING, "PMIC reported charging while unplugged - ignoring"); - } - return ConnectionStateDischargingUnplugged; - } -} - -static void prv_update_state(void *force_update) { - const uint8_t MAX_SAMPLE_SKIPS = 5; - bool forced = (bool)force_update; - // Large current draws will cause the voltage supplied by the battery to - // droop. We try to only sample the battery when there is minimal - // activity. We look to see if stop mode is allowed because this is a good - // indicator that no peripherals are in use (i.e vibe, backlight, etc) - if ((s_last_battery_state.skip_count < MAX_SAMPLE_SKIPS) && - !forced && !stop_mode_is_allowed()) { - s_last_battery_state.skip_count++; - return; - } - - if (s_last_battery_state.skip_count == MAX_SAMPLE_SKIPS) { - analytics_inc(ANALYTICS_DEVICE_METRIC_BATTERY_SAMPLE_SKIP_COUNT_EXCEEDED, - AnalyticsClient_System); - } - s_last_battery_state.skip_count = 0; - - // Driver communication - - bool state_changed = false; - ConnectionStateID next_state = prv_get_connection_state(); - if (s_last_battery_state.connection != next_state) { - // Do not allow DischargingPlugged -> ChargingPlugged state - if ((s_last_battery_state.connection != ConnectionStateDischargingPlugged) || - (next_state != ConnectionStateChargingPlugged)) { - prv_transition(&s_last_battery_state, next_state); - state_changed = true; - } - } - - s_last_battery_state.voltage = prv_filter_voltage(s_last_battery_state.voltage, - battery_get_millivolts()); - bool charging = (s_last_battery_state.connection == ConnectionStateChargingPlugged); - - // Update Percent & Filtering - - const uint32_t ALWAYS_UPDATE_THRESHOLD = ratio32_from_percent(10); - bool likely_stable = prv_is_stable(&s_last_battery_state); - - uint32_t new_charge_percent = - battery_curve_sample_ratio32_charge_percent(s_last_battery_state.voltage, charging); -#ifndef TARGET_QEMU - // If QEMU, allow updates to always occur for ease of testing otherwise - // Allow updates iff: - // - We are charging - // - We are discharging and: - // - The readings have stabilized and the battery percent did not go up - // - The readings have not yet stablized - // TL;DR: Allow updates unless we're stable and discharging but the % went up. - if (!charging && likely_stable && - new_charge_percent > s_last_battery_state.percent) { - // It's okay to return early since any connection/plugged changes will reset the filter, - // so we won't catch those. - return; - } -#endif - - s_last_battery_state.percent = new_charge_percent; - - PBL_LOG(LOG_LEVEL_DEBUG, "mV Raw: %"PRIu16" Ratio: %"PRIu32" Percent: %"PRIu32, - s_last_battery_state.voltage, s_last_battery_state.percent, - ratio32_to_percent(s_last_battery_state.percent)); - - PWR_TRACK_BATT(charging ? "CHARGING" : "DISCHARGING", s_last_battery_state.voltage); - - if (forced || likely_stable || s_last_battery_state.percent <= ALWAYS_UPDATE_THRESHOLD || - charging || state_changed) { - battery_state_put_change_event(prv_get_precise_charge_state(&s_last_battery_state)); - } -} - -static void prv_update_callback(void *data) { - // Running the battery monitor on the timer task is not a good idea because - // we could be sampling right in the middle of a flash erase, etc. Therefore, - // dispatch to a lower priority task - system_task_add_callback(prv_update_state, data); - - // Reschedule ourselves again so we create a loop - prv_schedule_update(BATTERY_SAMPLE_RATE_MS, false); -} - -static void prv_schedule_update(uint32_t delay, bool force_update) { - bool success = new_timer_start(s_periodic_timer_id, delay, prv_update_callback, - (void *)force_update, 0 /*flags*/); - PBL_ASSERTN(success); -} - -void battery_state_force_update(void) { - // Fire off our periodic timer. Note that we rely on the callback to reschedule the timer - // for 1 minute intervals rather than create it as a repeating timer. This is because - // we occasionally want the callback to get triggered immediately - // (in response to the charging cable being plugged in). In these instances, we reschedule it - // from the main task. - prv_schedule_update(0, true); -} - -void battery_state_init(void) { - s_periodic_timer_id = new_timer_create(); - - s_last_battery_state = (BatteryState) { .connection = ConnectionStateDischargingUnplugged }; - battery_state_reset_filter(); - battery_state_force_update(); - - s_analytics_previous_mv = s_last_battery_state.voltage; -} - -void battery_state_handle_connection_event(bool is_connected) { - static const uint32_t RECONNECTION_DELAY_MS = 1000; - - PBL_LOG_VERBOSE("USB Connected:%d", is_connected); - - // Trigger a reset update to the state machine. Delay the update to allow the battery voltage to - // settle and to debounce reconnection events - prv_schedule_update(RECONNECTION_DELAY_MS, true); -} - -PreciseBatteryChargeState prv_get_precise_charge_state(const BatteryState *state) { - PreciseBatteryChargeState event_state = { - .charge_percent = state->percent, - .pct = ratio32_to_percent(state->percent), - .is_charging = (s_last_battery_state.connection == ConnectionStateChargingPlugged), - .is_plugged = (s_last_battery_state.connection != ConnectionStateDischargingUnplugged) - }; - return event_state; -} - -DEFINE_SYSCALL(BatteryChargeState, sys_battery_get_charge_state, void) { - return battery_get_charge_state(); -} - -BatteryChargeState battery_get_charge_state(void) { - bool is_plugged = (s_last_battery_state.connection != ConnectionStateDischargingUnplugged); - - int32_t percent = ratio32_to_percent(s_last_battery_state.percent); - - // subtract low power reserve, so developer will see 0% when we're approaching low power mode - int32_t percent_normalized = MAX((percent - BOARD_CONFIG_POWER.low_power_threshold - + percent / (100 / BOARD_CONFIG_POWER.low_power_threshold)), 0); - - // massage rounding factor so that between 100% to 50% charge the SOC reported is biased to a - // higher charge percent bin. - int32_t rounding_factor = 5 + MAX(((percent - 50) / 10), 0); - BatteryChargeState state = { - .charge_percent = MIN(10 * ((percent_normalized + rounding_factor) / 10), 100), - .is_charging = is_plugged && percent_normalized < 100, - .is_plugged = is_plugged, - }; - return state; -} - -// For unit tests -TimerID battery_state_get_periodic_timer_id(void) { - return s_periodic_timer_id; -} - -uint16_t battery_state_get_voltage(void) { - return s_last_battery_state.voltage; -} - - -#include "console/prompt.h" -void command_print_battery_status(void) { - char buffer[32]; - PreciseBatteryChargeState state = prv_get_precise_charge_state(&s_last_battery_state); - prompt_send_response_fmt(buffer, 32, "%"PRIu16" mV", s_last_battery_state.voltage); - prompt_send_response_fmt(buffer, 32, - "batt_percent: %"PRIu32"%%", ratio32_to_percent(state.charge_percent)); - prompt_send_response_fmt(buffer, 32, "plugged: %s", state.is_plugged ? "YES" : "NO"); - prompt_send_response_fmt(buffer, 32, "charging: %s", state.is_charging ? "YES" : "NO"); -} - -///////////////// -// Analytics - -// Note that this is run on a different thread than battery_state! -void analytics_external_collect_battery(void) { - // This should not be called for an hour after bootup - - int battery_mv = s_last_battery_state.voltage; - - int d_mv = battery_mv - s_analytics_previous_mv; - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE, battery_mv, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE_DELTA, d_mv, AnalyticsClient_System); - - int scaling_factor = INT32_MAX / 100; // we want to cover -100 to 100 percent - // Note: we assume that the watch was not charging during the hour. - int32_t start_percent = battery_curve_lookup_percent_with_scaling_factor(s_analytics_previous_mv, - false, scaling_factor); - int32_t curr_percent = battery_curve_lookup_percent_with_scaling_factor(battery_mv, false, - scaling_factor); - int32_t d_percent = curr_percent - start_percent; - - s_analytics_previous_mv = battery_mv; - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT_DELTA, d_percent, AnalyticsClient_System); - - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT, - ratio32_to_percent(s_last_battery_state.percent), - AnalyticsClient_System); -} - -static void prv_set_forced_charge_state(bool is_charging) { - battery_force_charge_enable(is_charging); - - // Trigger an immediate update to the state machine: may trigger an event - battery_state_force_update(); -} - -void command_battery_charge_option(const char* option) { - if (!strcmp("disable", option)) { - prv_set_forced_charge_state(false); - } else if (!strcmp("enable", option)) { - prv_set_forced_charge_state(true); - } -} diff --git a/src/fw/services/common/bluetooth/ble_bas.c b/src/fw/services/common/bluetooth/ble_bas.c deleted file mode 100644 index b58d514d4e..0000000000 --- a/src/fw/services/common/bluetooth/ble_bas.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/event_service_client.h" -#include "bluetooth/bas.h" -#include "kernel/event_loop.h" -#include "kernel/pebble_tasks.h" -#include "syscall/syscall.h" - -static EventServiceInfo s_bas_evt; - -static void prv_execute_on_kernel_main(CallbackEventCallback cb) { - if (pebble_task_get_current() != PebbleTask_KernelMain) { - launcher_task_add_callback(cb, NULL); - } else { - cb(NULL); - } -} - -static void prv_ble_bas_handle_event(PebbleEvent *e, void *context) { - const PebbleBatteryStateChangeEvent *const battery_state_event = &e->battery_state; - - bt_driver_bas_handle_update(battery_state_event->new_state.pct); -} - -static void prv_start_ble_bas_kernel_main(void *unused) { - BatteryChargeState battery_state; - - battery_state = sys_battery_get_charge_state(); - bt_driver_bas_handle_update(battery_state.charge_percent); - - s_bas_evt = (EventServiceInfo) { - .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, - .handler = prv_ble_bas_handle_event, - }; - - event_service_client_subscribe(&s_bas_evt); -} - -static void prv_stop_ble_bas_kernel_main(void *unused) { - event_service_client_unsubscribe(&s_bas_evt); -} - -void ble_bas_init(void) { - prv_execute_on_kernel_main(prv_start_ble_bas_kernel_main); -} - -void ble_bas_deinit(void) { - prv_execute_on_kernel_main(prv_stop_ble_bas_kernel_main); -} diff --git a/src/fw/services/common/bluetooth/ble_bas.h b/src/fw/services/common/bluetooth/ble_bas.h deleted file mode 100644 index fd7beb9438..0000000000 --- a/src/fw/services/common/bluetooth/ble_bas.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void ble_bas_init(void); - -void ble_bas_deinit(void); \ No newline at end of file diff --git a/src/fw/services/common/bluetooth/bluetooth_ctl.c b/src/fw/services/common/bluetooth/bluetooth_ctl.c deleted file mode 100644 index 4c289c9d10..0000000000 --- a/src/fw/services/common/bluetooth/bluetooth_ctl.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE -#include "services/common/bluetooth/bluetooth_ctl.h" - -#include -#include - -#include "comm/ble/gap_le.h" -#include "comm/ble/gatt_client_subscriptions.h" -#include "console/dbgserial.h" -#include "drivers/clocksource.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "kernel/util/stop.h" -#if MEMFAULT -#include "memfault/metrics/connectivity.h" -#endif -#include "os/mutex.h" -#include "pebble_errors.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/ble_bas.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/dis.h" -#include "services/common/bluetooth/local_addr.h" -#include "services/common/bluetooth/local_id.h" -#include "services/common/bluetooth/pairability.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" -#include "services/normal/bluetooth/ble_hrm.h" -#include "system/logging.h" - -static bool s_comm_initialized = false; -static bool s_comm_airplane_mode_on = false; -static bool s_comm_enabled = false; -static bool s_comm_is_running = false; -static bool s_comm_state_change_eval_is_scheduled; -static BtCtlModeOverride s_comm_override = BtCtlModeOverrideNone; -static PebbleMutex *s_comm_state_change_mutex; - -bool bt_ctl_is_airplane_mode_on(void) { return s_comm_airplane_mode_on; } - -bool bt_ctl_is_bluetooth_active(void) { - if (s_comm_enabled) { - if (s_comm_override == BtCtlModeOverrideRun) { - return true; - } else if (s_comm_override == BtCtlModeOverrideNone && !s_comm_airplane_mode_on) { - return true; - } - } - return false; -} - -bool bt_ctl_is_bluetooth_running(void) { return s_comm_is_running; } - -static void prv_put_disconnection_event(void) { - PebbleEvent event = (PebbleEvent){.type = PEBBLE_BT_CONNECTION_EVENT, - .bluetooth.connection = { - .is_ble = true, - .state = PebbleBluetoothConnectionEventStateDisconnected, - }}; - PBL_LOG(LOG_LEVEL_DEBUG, "New BT Conn change event, We are now disconnected"); - event_put(&event); -} - -static void prv_comm_start(void) { - if (s_comm_is_running) { - return; - } - stop_mode_disable(InhibitorCommMode); - // Heap allocated to reduce stack usage - BTDriverConfig *config = kernel_zalloc_check(sizeof(BTDriverConfig)); - dis_get_info(&config->dis_info); -#if CAPABILITY_HAS_BUILTIN_HRM - config->is_hrm_supported_and_enabled = ble_hrm_is_supported_and_enabled(); - PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing prefs: is_enabled=%u", - config->is_hrm_supported_and_enabled); -#endif -#ifdef BT_REQUIRE_EARLY_BONDINGS - bt_persistent_storage_register_existing_ble_bondings(); -#endif - - s_comm_is_running = bt_driver_start(config); - kernel_free(config); - - if (s_comm_is_running) { - bt_local_addr_init(); -#ifndef BT_REQUIRE_EARLY_BONDINGS - bt_persistent_storage_register_existing_ble_bondings(); -#endif - gap_le_init(); - bt_local_id_configure_driver(); -#if CAPABILITY_HAS_BUILTIN_HRM - ble_hrm_init(); -#endif - ble_bas_init(); - bt_pairability_init(); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "BT driver failed to start!"); - // FIXME: PBL-36163 -- handle this better - } - - stop_mode_enable(InhibitorCommMode); -} - -static void prv_comm_stop(void) { - if (!s_comm_is_running) { - return; - } - stop_mode_disable(InhibitorCommMode); - ble_bas_deinit(); -#if CAPABILITY_HAS_BUILTIN_HRM - ble_hrm_deinit(); -#endif - gap_le_deinit(); - - // Should be the last thing to happen that touches the Bluetooth controller directly - bt_driver_stop(); - stop_mode_enable(InhibitorCommMode); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME, AnalyticsClient_System); - s_comm_is_running = false; - - // This is a legacy event used to update the Settings app. - prv_put_disconnection_event(); -} - -static void prv_send_state_change_event(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "----> Sending a BT state event"); - PebbleEvent event = { - .type = PEBBLE_BT_STATE_EVENT, - .bluetooth = - { - .state = - { - .airplane = s_comm_airplane_mode_on, - .enabled = s_comm_enabled, - .override = s_comm_override, - }, - }, - }; - event_put(&event); -#if MEMFAULT - if (s_comm_airplane_mode_on) { - memfault_metrics_connectivity_connected_state_change( - kMemfaultMetricsConnectivityState_Stopped); - } else { - memfault_metrics_connectivity_connected_state_change( - kMemfaultMetricsConnectivityState_Started); - } -#endif -} - -static void prv_comm_state_change(void *context) { - static bool s_first_run = true; - mutex_lock(s_comm_state_change_mutex); - s_comm_state_change_eval_is_scheduled = false; - bool is_active_mode = bt_ctl_is_bluetooth_active(); - if (is_active_mode != s_comm_is_running) { - if (is_active_mode) { - prv_comm_start(); - } else { - prv_comm_stop(); - } - // Only send event if state changed successfully: - if (is_active_mode == s_comm_is_running) { - prv_send_state_change_event(); - } - } else if (!s_comm_is_running && s_first_run) { - PBL_LOG(LOG_LEVEL_DEBUG, "Shutting down the BT stack on boot"); - bt_driver_power_down_controller_on_boot(); - } - - s_first_run = false; - mutex_unlock(s_comm_state_change_mutex); -} - -void bt_ctl_set_enabled(bool enabled) { - if (!s_comm_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Error: Bluetooth isn't initialized yet"); - return; - } - mutex_lock(s_comm_state_change_mutex); - s_comm_enabled = enabled; - mutex_unlock(s_comm_state_change_mutex); - prv_comm_state_change(NULL); -} - -void bt_ctl_set_override_mode(BtCtlModeOverride override) { - if (!s_comm_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Error: Bluetooth isn't initialized yet"); - return; - } - mutex_lock(s_comm_state_change_mutex); - s_comm_override = override; - mutex_unlock(s_comm_state_change_mutex); - prv_comm_state_change(NULL); -} - -static void prv_track_quick_airplane_mode_toggles(bool is_airplane_mode_currently_on) { - // Track when coming out of airplane mode and we've gone into airplane mode less than 30 secs ago: - static RtcTicks s_airplane_mode_last_toggle_ticks; - const RtcTicks now_ticks = rtc_get_ticks(); - const uint64_t max_interval_secs = 30; - if (((now_ticks - s_airplane_mode_last_toggle_ticks) < (max_interval_secs * RTC_TICKS_HZ)) && - is_airplane_mode_currently_on) { - PBL_LOG(LOG_LEVEL_INFO, "Quick airplane mode toggle detected!"); - analytics_inc(ANALYTICS_DEVICE_METRIC_BT_AIRPLANE_MODE_QUICK_TOGGLE_COUNT, - AnalyticsClient_System); - } - s_airplane_mode_last_toggle_ticks = now_ticks; -} - -void bt_ctl_set_airplane_mode_async(bool enabled) { - if (!s_comm_initialized) { - PBL_LOG(LOG_LEVEL_ERROR, "Error: Bluetooth isn't initialized yet"); - return; - } - mutex_lock(s_comm_state_change_mutex); - prv_track_quick_airplane_mode_toggles(!enabled); - bt_persistent_storage_set_airplane_mode_enabled(enabled); - s_comm_airplane_mode_on = enabled; - bool should_schedule_eval = false; - if (!s_comm_state_change_eval_is_scheduled) { - should_schedule_eval = true; - s_comm_state_change_eval_is_scheduled = true; - } - mutex_unlock(s_comm_state_change_mutex); - if (should_schedule_eval) { - system_task_add_callback(prv_comm_state_change, NULL); - } -} - -void bt_ctl_init(void) { - s_comm_state_change_mutex = mutex_create(); - - s_comm_airplane_mode_on = bt_persistent_storage_get_airplane_mode_enabled(); - s_comm_initialized = true; - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME, AnalyticsClient_System); - - gatt_client_subscription_boot(); -} - -static void prv_bt_ctl_reset_bluetooth_callback(void *context) { - PBL_LOG(LOG_LEVEL_DEBUG, "Resetting Bluetooth"); - mutex_lock(s_comm_state_change_mutex); - - bool was_already_running = s_comm_is_running; - - prv_comm_stop(); - prv_comm_start(); - - // It's possible a reset was triggered because the stack failed to boot up - // correctly in which case we have never generated an event about the stack - // booting up. Don't bother sending events if we are just returning the stack - // to the state it is already in - if (!was_already_running && s_comm_is_running) { - prv_send_state_change_event(); - } - mutex_unlock(s_comm_state_change_mutex); -} - -void bt_ctl_reset_bluetooth(void) { - if (bt_ctl_is_bluetooth_active()) { - system_task_add_callback(prv_bt_ctl_reset_bluetooth_callback, NULL); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Bluetooth is disabled, reset aborted"); - } -} - -void command_bt_airplane_mode(const char *new_mode) { - // as tests run using command_bt_airplane_mode, will retain nomenclature - // but work as override mode change - BtCtlModeOverride override = BtCtlModeOverrideStop; - if (strcmp(new_mode, "exit") == 0) { - override = BtCtlModeOverrideNone; - } - bt_ctl_set_override_mode(override); - bool new_state = bt_ctl_is_bluetooth_active(); - if (!new_state) { - dbgserial_putstr("Entered airplane mode"); - } else { - dbgserial_putstr("Left airplane mode"); - } -} diff --git a/src/fw/services/common/bluetooth/bluetooth_ctl.h b/src/fw/services/common/bluetooth/bluetooth_ctl.h deleted file mode 100644 index eb2a041bc0..0000000000 --- a/src/fw/services/common/bluetooth/bluetooth_ctl.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum { - BtCtlModeOverrideNone, - BtCtlModeOverrideStop, - BtCtlModeOverrideRun -} BtCtlModeOverride; - -void bt_ctl_init(void); - -//! returns the airplane mode state -bool bt_ctl_is_airplane_mode_on(void); - -//! Returns whether the bluetooth stack is supposed to be up and running (but might not because it's -//! still starting or in the middle of resetting). -bool bt_ctl_is_bluetooth_active(void); - -//! Returns whether the bluetooth stack is up and running or not. -bool bt_ctl_is_bluetooth_running(void); - -// The following three functions are used for setting the flags that define the state of -// the bluetooth stack. - -//! Sets the airplane mode flag. The flag is persisted across reboots -void bt_ctl_set_airplane_mode_async(bool enabled); - -//! Sets enable flag (used by the runlevel system). -void bt_ctl_set_enabled(bool enabled); - -//! Sets the override mode used to stop and start the bluetooth independent of the airplane mode. -void bt_ctl_set_override_mode(BtCtlModeOverride override); - -//! Reset bluetoosh using sequential calls to comm_stop() and comm_start() -void bt_ctl_reset_bluetooth(void); diff --git a/src/fw/services/common/bluetooth/bluetooth_persistent_storage.h b/src/fw/services/common/bluetooth/bluetooth_persistent_storage.h deleted file mode 100644 index 914c6578bc..0000000000 --- a/src/fw/services/common/bluetooth/bluetooth_persistent_storage.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/comm_session/session_remote_version.h" - -#include -#include -#include -#include - -//! @file bluetooth_persistent_storage.h -//! Future file-based database for Bluetooth related credentials, cached data, etc. -//! The idea is to replace the deprecated, registry-based "remote_prefs.c" and "known_device_list.c" -//! For now this module contains temporary implementations that use the legacy registry. - -typedef enum { - BtPersistBondingOpInvalid = -1, - BtPersistBondingOpDidAdd, - BtPersistBondingOpDidChange, - BtPersistBondingOpWillDelete, -} BtPersistBondingOp; - -typedef enum { - BtPersistBondingTypeBTClassic, - BtPersistBondingTypeBLE, - BtPersistBondingNumTypes -} BtPersistBondingType; - - -//! Signature of function that handles changes in the pairing database -typedef void (*BtPersistBondingChangeHandler)(BTBondingID affected_bonding, - BtPersistBondingOp operation); - -typedef void (*BtPersistBondingDBEachBLE)(BTDeviceInternal *device, SMIdentityResolvingKey *irk, - const char *name, BTBondingID *id, void *context); - -typedef void (*BtPersistBondingDBEachBTClassic)(BTDeviceAddress *addr, SM128BitKey *link_key, - const char *name, uint8_t *platform_bits, void *context); -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BLE Pairing Info - -bool bt_persistent_storage_has_pinned_ble_pairings(void); - -bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *address); - -bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out); - -BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *pairing_info, - bool is_gateway, const char *device_name, - bool requires_address_pinning, - uint8_t flags); - -bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name); - -void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID); - -void bt_persistent_storage_delete_ble_pairing_by_addr(const BTDeviceInternal *device); - -bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding, - SMIdentityResolvingKey *IRK_out, - BTDeviceInternal *device_out, - char *name_out); - -bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device, - SMIdentityResolvingKey *IRK_out, - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]); - -//! Returns the first ANCS supported bonding that is found -//! The case of having multiple supported ANCS bondings isn't handled well yet. -//! When this happens this could easily be changed to a for_each_ancs_supported_bonding(cb) -BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void); - -//! Returns true if the bondings is BLE and supports ANCS -bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding); - -//! Returns true if there exists a BLE bonding which supports ANCS -bool bt_persistent_storage_has_ble_ancs_bonding(void); - -//! Returns true if the active gateway uses BLE -//! [PG]: This will currently always return false until PPoGATT is supported -bool bt_persistent_storage_has_active_ble_gateway_bonding(void); - -//! Runs the callback for each BLE pairing -//! The callback is NOT allowed to aquire the bt_lock() (or we could deadlock). -void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context); - -//! Registers all the existing BLE bondings with the BT driver lib. -void bt_persistent_storage_register_existing_ble_bondings(void); - -BTCCCDID bt_persistent_storage_store_cccd(const BleCCCD *cccd); - -bool bt_persistent_storage_delete_cccd(const BTDeviceInternal *peer, uint16_t chr_val_handle); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BT Classic Pairing Info - -BTBondingID bt_persistent_storage_store_bt_classic_pairing(BTDeviceAddress *address, - SM128BitKey *key, char *name, - uint8_t *platform_bits); - -void bt_persistent_storage_delete_bt_classic_pairing_by_id(BTBondingID bonding); - -void bt_persistent_storage_delete_bt_classic_pairing_by_addr(const BTDeviceAddress *bd_addr); - -bool bt_persistent_storage_get_bt_classic_pairing_by_id(BTBondingID bonding, - BTDeviceAddress *address_out, - SM128BitKey *link_key_out, - char *name_out, - uint8_t *platform_bits_out); - -BTBondingID bt_persistent_storage_get_bt_classic_pairing_by_addr(BTDeviceAddress *addr_in, - SM128BitKey *link_key_out, - char *name_out, - uint8_t *platform_bits_out); - -//! Returns true if the active gateway uses BT Classic -bool bt_persistent_storage_has_active_bt_classic_gateway_bonding(void); - -//! Runs the callback for each BT Classic pairing -//! The callback is NOT allowed to aquire the bt_lock() (or we could deadlock). -void bt_persistent_storage_for_each_bt_classic_pairing(BtPersistBondingDBEachBTClassic cb, - void *context); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Local Device Info - -//! Updates the active gateway (the gateway which implements PP) -//! This bonding is used for BT Classic reconnection as well -//! @param bonding The desired active gateway -void bt_persistent_storage_set_active_gateway(BTBondingID bonding); - -//! Returns false if no active gateway exists, true if one does exist -//! bonding_out and type_out are only valid when this function returns true; -bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out, - BtPersistBondingType *type_out); - -//! Returns true when the active gateway is changed until a sync happens -bool bt_persistent_storage_is_unfaithful(void); - -//! Marks the device as being unfaithful -void bt_persistent_storage_set_unfaithful(bool is_unfaithful); - -//! Copies the BLE Encryption Root (ER) or Identity Root (IR) keys out of storage -//! @param key_out Storage into which ER or IR should be copied. -//! @param key_type The type of key to copy -//! @return true if ER and IR are copied, false if there are no keys have been found to copy. -bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out); - -//! Stores new BLE Encryption Root (ER) and Identity Root (IR) keys -void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in); - -//! @param local_device_name_out Storage for the local device name. -//! @param max_size Size of the local_device_name_out buffer -//! @return true if there is a valid local device name stored, otherwise false (a zero-length string -bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size); - -//! Stores the customized local device name -//! @param local_device_name The device name to store -//! @param size The size of the string -void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t max_size); - -//! Retrieve the airplane mode setting -//! @return the stored airplane mode flag -bool bt_persistent_storage_get_airplane_mode_enabled(void); - -//! Store the airplane mode setting -//! @param the airplane mode state to be saved -void bt_persistent_storage_set_airplane_mode_enabled(bool enable); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Remote Device Info - -//! Retrieve the most recent system session capabilities -//! @param capabilities_out Storage for system session capabilities -//! @note It's preferable to use \ref comm_session_get_capabilities when possible -void bt_persistent_storage_get_cached_system_capabilities( - PebbleProtocolCapabilities *capabilities_out); - -//! Store the most recent system session capabilities -//! @param capabilities The capability flags to be saved (cache will be cleared if NULL) -void bt_persistent_storage_set_cached_system_capabilities( - const PebbleProtocolCapabilities *capabilities); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Common - -void bt_persistent_storage_init(void); - -//! This will not delete the local device info, only pairings -void bt_persistent_storage_delete_all_pairings(void); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Unit testing -int bt_persistent_storage_get_raw_data(const void *key, size_t key_len, - void *data_out, size_t buf_len); diff --git a/src/fw/services/common/bluetooth/bluetooth_persistent_storage_debug.h b/src/fw/services/common/bluetooth/bluetooth_persistent_storage_debug.h deleted file mode 100644 index 92525b5077..0000000000 --- a/src/fw/services/common/bluetooth/bluetooth_persistent_storage_debug.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define DISPLAY_BUF_LEN 160 - -void bluetooth_persistent_storage_debug_dump_ble_pairing_info( - char *display_buf, const SMPairingInfo *info); - -void bluetooth_persistent_storage_debug_dump_classic_pairing_info( - char *display_buf, BTDeviceAddress *addr, char *device_name, SM128BitKey *link_key, - uint8_t platform_bits); - -void bluetooth_persistent_storage_debug_dump_root_keys(SM128BitKey *irk, SM128BitKey *erk); diff --git a/src/fw/services/common/bluetooth/bluetooth_prompt.c b/src/fw/services/common/bluetooth/bluetooth_prompt.c deleted file mode 100644 index f69a901176..0000000000 --- a/src/fw/services/common/bluetooth/bluetooth_prompt.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "comm/ble/gap_le_connection.h" -#include "comm/bt_lock.h" - -#include "console/prompt.h" -#include "services/common/bluetooth/bluetooth_ctl.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/bt_compliance_tests.h" -#include "services/common/bluetooth/local_id.h" -#include "services/common/bluetooth/pairability.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "util/string.h" - -#include -#include -#include -#include - -#include - -void command_bt_print_mac(void) { - char addr_hex_str[BT_ADDR_FMT_BUFFER_SIZE_BYTES]; - bt_local_id_copy_address_hex_string(addr_hex_str); - prompt_send_response(addr_hex_str); -} - -//! Overrides the BD ADDR of the Bluetooth controller for test automation purposes -//! @param bd_addr String of 12 hex characters (6 bytes) of the Bluetooth device address -//! @note To undo the change, call this with all zeroes. -//! @note The change will take effect when the Bluetooth is (re)enabled. -void command_bt_set_addr(const char *bd_addr_str) { - char buffer[32]; - BTDeviceAddress bd_addr; - if (convert_bt_addr_hex_str_to_bd_addr(bd_addr_str, (uint8_t *) &bd_addr, sizeof(bd_addr))) { - bt_driver_test_set_spoof_address(&bd_addr); - prompt_send_response_fmt(buffer, 32, BT_DEVICE_ADDRESS_FMT, BT_DEVICE_ADDRESS_XPLODE(bd_addr)); - } else { - prompt_send_response("?"); - } -} - -//! @param bt_name A custom Bluetooth device name. -void command_bt_set_name(const char *bt_name) { - bt_local_id_set_device_name(bt_name); -} - -// BT FCC tests -void command_bt_test_start(void) { - // take down the BT stack and put the OS in a mode where it will not - // interfere with the BT testing. - bt_test_start(); -} - -void command_bt_test_stop(void) { - // restore the watch to normal operation - bt_test_stop(); -} - -void command_bt_test_hci_passthrough(void) { - bt_test_enter_hci_passthrough(); -} - -void command_bt_test_bt_sig_rf_mode(void) { - if (bt_test_bt_sig_rf_test_mode()) { - prompt_send_response("BT SIG RF Test Mode Enabled"); - } else { - prompt_send_response("Failed to enter BT SIG RF Test Mode"); - } -} - -void command_bt_prefs_wipe(void) { - bt_driver_classic_disconnect(NULL); - bt_persistent_storage_delete_all_pairings(); -} - -void command_bt_sprf_nuke(void) { - shared_prf_storage_wipe_all(); -#if RECOVERY_FW - // Reset system to get caches (in s_intents, s_connections and controller-side caches) in sync. - extern void factory_reset_set_reason_and_reset(void); - factory_reset_set_reason_and_reset(); -#endif -} - -#ifdef RECOVERY_FW -void command_bt_status(void) { - char buffer[64]; - - prompt_send_response_fmt(buffer, sizeof(buffer), "Alive: %s", - bt_ctl_is_bluetooth_running() ? "yes" : "no"); - - const char *prefix = "BT Chip Info: "; - size_t prefix_length = strlen(prefix); - strncpy(buffer, prefix, sizeof(buffer)); - bt_driver_id_copy_chip_info_string(buffer + prefix_length, - sizeof(buffer) - prefix_length); - prompt_send_response(buffer); - - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - bt_lock(); - bool connected = bt_driver_classic_copy_connected_device_name(name); - if (!connected) { - // Try LE: - GAPLEConnection *connection = gap_le_connection_any(); - if (connection) { - const char *device_name = connection->device_name ?: ""; - strncpy(name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); - name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; - connected = true; - } - } - bt_unlock(); - - prompt_send_response_fmt(buffer, sizeof(buffer), "Connected: %s", connected ? "yes" : "no"); - if (connected) { - prompt_send_response_fmt(buffer, sizeof(buffer), "Device: %s", name); - } -} -#endif // RECOVERY_FW diff --git a/src/fw/services/common/bluetooth/bonding.c b/src/fw/services/common/bluetooth/bonding.c deleted file mode 100644 index f27b481e50..0000000000 --- a/src/fw/services/common/bluetooth/bonding.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "comm/ble/gap_le_connection.h" -#include "comm/ble/gap_le_device_name.h" -#include "comm/bt_lock.h" - -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/local_addr.h" -#include "system/logging.h" - -#include -#include -#include - -void bt_driver_cb_handle_create_bonding(const BleBonding *bonding, - const BTDeviceAddress *addr) { -#if !defined(PLATFORM_TINTIN) - PBL_LOG(LOG_LEVEL_INFO, "Creating new bonding for "BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE(bonding->pairing_info.identity.address)); -#endif - const bool should_pin_address = bonding->should_pin_address; - if (should_pin_address) { - bt_local_addr_pin(&bonding->pinned_address); - } - const uint8_t flags = bonding->flags; - if (flags) { - PBL_LOG(LOG_LEVEL_INFO, "flags: 0x02%x", flags); - } - BTBondingID bonding_id = bt_persistent_storage_store_ble_pairing(&bonding->pairing_info, - bonding->is_gateway, NULL, - should_pin_address, - flags); - bt_lock(); - { - GAPLEConnection *connection = gap_le_connection_by_addr(addr); - if (connection) { - // Associate the connection with the bonding: - connection->bonding_id = bonding_id; - connection->is_gateway = bonding->is_gateway; - - if (!connection->is_gateway) { - PBL_LOG(LOG_LEVEL_DEBUG, "New bonding is not gateway?"); - } - - // Request device name. iOS returns an "anonymized" device name before encryption, like - // "iPhone" and only returns the real name i.e. "Martijn's iPhone" after encryption is set up. - gap_le_device_name_request(&connection->device); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Couldn't find connection for bonding!"); - } - } - bt_unlock(); -} diff --git a/src/fw/services/common/bluetooth/bt_compliance_tests.c b/src/fw/services/common/bluetooth/bt_compliance_tests.c deleted file mode 100644 index feee853dbd..0000000000 --- a/src/fw/services/common/bluetooth/bt_compliance_tests.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "console/console_internal.h" -#include "console/prompt.h" - -#include "services/common/bluetooth/bluetooth_ctl.h" -#include "services/common/bluetooth/bt_compliance_tests.h" - -#include "kernel/util/stop.h" - -#include - -static bool s_test_mode_enabled = false; - -void bt_test_start(void) { - if (s_test_mode_enabled) { - prompt_send_response("Invalid operation: Run 'bt test stop' first"); - return; - } - s_test_mode_enabled = true; - bt_ctl_set_override_mode(BtCtlModeOverrideStop); - stop_mode_disable(InhibitorBluetooth); - - bt_driver_test_start(); -} - -bool bt_test_bt_sig_rf_test_mode(void) { - return bt_driver_test_enter_rf_test_mode(); -} - -void bt_test_enter_hci_passthrough(void) { - // redirect all communications to the BT module - serial_console_set_state(SERIAL_CONSOLE_STATE_HCI_PASSTHROUGH); - - bt_driver_test_enter_hci_passthrough(); -} - -void bt_test_stop(void) { - if (!s_test_mode_enabled) { - prompt_send_response("Invalid operation: Run 'bt test start' first"); - return; - } - - bt_driver_test_stop(); - stop_mode_enable(InhibitorBluetooth); - - // Bring the normal BT stack back up - airplane mode makes this simple - bt_ctl_set_override_mode(BtCtlModeOverrideNone); - s_test_mode_enabled = false; -} diff --git a/src/fw/services/common/bluetooth/bt_compliance_tests.h b/src/fw/services/common/bluetooth/bt_compliance_tests.h deleted file mode 100644 index cb7f514c5c..0000000000 --- a/src/fw/services/common/bluetooth/bt_compliance_tests.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void bt_test_start(void); -void bt_test_stop(void); - -bool bt_test_bt_sig_rf_test_mode(void); -void bt_test_enter_hci_passthrough(void); diff --git a/src/fw/services/common/bluetooth/dis.c b/src/fw/services/common/bluetooth/dis.c deleted file mode 100644 index 12d39680a4..0000000000 --- a/src/fw/services/common/bluetooth/dis.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include - -#include "board/board.h" -#include "process_management/pebble_process_info.h" -#include "mfg/mfg_info.h" -#include "mfg/mfg_serials.h" -#include "system/version.h" - -_Static_assert(MODEL_NUMBER_LEN >= MFG_HW_VERSION_SIZE + 1, "Size mismatch"); -_Static_assert(MANUFACTURER_LEN >= sizeof(BT_VENDOR_NAME), "Size mismatch"); -_Static_assert(SERIAL_NUMBER_LEN >= MFG_SERIAL_NUMBER_SIZE + 1, "Size mismatch"); -_Static_assert(FW_REVISION_LEN >= sizeof(TINTIN_METADATA.version_tag), "Size mismatch"); - -static void prv_set_model_number(DisInfo *info) { - mfg_info_get_hw_version(info->model_number, MODEL_NUMBER_LEN); -} - -static void prv_set_manufacturer_name(DisInfo *info) { - strncpy(info->manufacturer, BT_VENDOR_NAME, MANUFACTURER_LEN); -} - -static void prv_set_serial_number(DisInfo *info) { - mfg_info_get_serialnumber(info->serial_number, SERIAL_NUMBER_LEN); -} - -static void prv_set_firmware_revision(DisInfo *info) { - strncpy(info->fw_revision, (char*)TINTIN_METADATA.version_tag, FW_REVISION_LEN); -} - -static void prv_set_software_revision(DisInfo *info) { - // Fmt: xx.xx\0 - char sdk_version[SW_REVISION_LEN]; - sniprintf(sdk_version, SW_REVISION_LEN, "%2u.%02u", - PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, - PROCESS_INFO_CURRENT_SDK_VERSION_MINOR); - strncpy(info->sw_revision, sdk_version, SW_REVISION_LEN); -} - -void dis_get_info(DisInfo *info) { - prv_set_model_number(info); - prv_set_manufacturer_name(info); - prv_set_serial_number(info); - prv_set_firmware_revision(info); - prv_set_software_revision(info); -} diff --git a/src/fw/services/common/bluetooth/dis.h b/src/fw/services/common/bluetooth/dis.h deleted file mode 100644 index e75953deea..0000000000 --- a/src/fw/services/common/bluetooth/dis.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef struct DisInfo DisInfo; - -void dis_get_info(DisInfo *info); diff --git a/src/fw/services/common/bluetooth/local_addr.c b/src/fw/services/common/bluetooth/local_addr.c deleted file mode 100644 index ec0f490511..0000000000 --- a/src/fw/services/common/bluetooth/local_addr.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "local_addr.h" - -#include "comm/bt_lock.h" -#include "system/logging.h" -#include "system/passert.h" - -#include -#include -#include - -static uint32_t s_pra_cycling_pause_count; -static BTDeviceAddress s_pinned_addr; -static bool s_cycling_paused_due_to_dependent_bondings; - -static void prv_allow_cycling(bool allow_cycling) { - bt_driver_set_local_address(allow_cycling, allow_cycling ? NULL : &s_pinned_addr); -} - -void bt_local_addr_pause_cycling(void) { - bt_lock(); - { - if (s_pra_cycling_pause_count == 0) { - PBL_LOG(LOG_LEVEL_INFO, "Pausing address cycling (pinned_addr="BT_DEVICE_ADDRESS_FMT")", - BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr)); - prv_allow_cycling(false); - } - ++s_pra_cycling_pause_count; - } - bt_unlock(); -} - -void bt_local_addr_resume_cycling(void) { - bt_lock(); - { - PBL_ASSERTN(s_pra_cycling_pause_count); - --s_pra_cycling_pause_count; - if (s_pra_cycling_pause_count == 0) { - PBL_LOG(LOG_LEVEL_INFO, "Resuming address cycling (pinned_addr="BT_DEVICE_ADDRESS_FMT")", - BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr)); - prv_allow_cycling(true); - } - } - bt_unlock(); -} - -void bt_local_addr_pin(const BTDeviceAddress *addr) { - // In a previous version of the code, the main FW would not know yet what address would be used - // for pinning until the BT driver would give the address to pin when a pairing was added. - // A single, persistent pinned address is now generated up front in bt_local_addr_init(). - // Getting the address back in this call from the BT driver currently only serves as a - // consistency check. - // It is possible that the addresses do not match in the following scenario: - // 1. No bondings that require pinning present. Cycling address 'C' is used. - // 2. Device A is connected. - // 3. Become discoverable: cycling is requested to be paused at address 'P' but can't be granted - // yet because device A is still connected. - // 4. Device B connects (using 'C' as connection address) - // 5. Device B requests pin + pairs => the remote bonding is stored with 'C' as key instead of 'P' - // 6. We'll print here there's a mismatch. - // 7. Once Device A & B disconnect, device B won't be able to recognize us because 'P' is used... - - bt_lock(); - bool addresses_match = bt_device_address_equal(addr, &s_pinned_addr); - bt_unlock(); - - PBL_LOG(LOG_LEVEL_INFO, - "Requested to pin address to "BT_DEVICE_ADDRESS_FMT " match=%u", - BT_DEVICE_ADDRESS_XPLODE_PTR(addr), addresses_match); -} - -void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) { - bool has_pinned_ble_pairings = bt_persistent_storage_has_pinned_ble_pairings(); - if (has_pinned_ble_pairings != s_cycling_paused_due_to_dependent_bondings) { - if (has_pinned_ble_pairings) { - bt_local_addr_pause_cycling(); - } else { - bt_local_addr_resume_cycling(); - } - s_cycling_paused_due_to_dependent_bondings = has_pinned_ble_pairings; - } -} - -void bt_local_addr_init(void) { - s_pra_cycling_pause_count = 0; - s_cycling_paused_due_to_dependent_bondings = false; - - // Load pinned address from settings file or generate one if it hasn't happened before: - if (!bt_persistent_storage_get_ble_pinned_address(&s_pinned_addr)) { - if (bt_driver_id_generate_private_resolvable_address(&s_pinned_addr)) { - bt_persistent_storage_set_ble_pinned_address(&s_pinned_addr); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to generate PRA... :("); - } - } - PBL_LOG(LOG_LEVEL_INFO, "Pinned address: " BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr)); - - if (bt_persistent_storage_has_pinned_ble_pairings()) { - PBL_LOG(LOG_LEVEL_INFO, "Bonding that requires address pinning exists, applying pinned addr!"); - bt_local_addr_pause_cycling(); - s_cycling_paused_due_to_dependent_bondings = true; - } else { -#if RECOVERY_FW - PBL_LOG(LOG_LEVEL_INFO, "Pausing address cycling because PRF!"); - bt_local_addr_pause_cycling(); -#else - PBL_LOG(LOG_LEVEL_INFO, "No bondings found that require address pinning!"); - bt_driver_set_local_address(true /* allow_cycling */, NULL); -#endif - } -} diff --git a/src/fw/services/common/bluetooth/local_addr.h b/src/fw/services/common/bluetooth/local_addr.h deleted file mode 100644 index 091e1b494a..0000000000 --- a/src/fw/services/common/bluetooth/local_addr.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/bluetooth/bluetooth_persistent_storage.h" - -typedef struct BTDeviceAddress BTDeviceAddress; - -//! Pauses cycling of local Private Resolvable Address (ref counted). -//! As long as the cycling is paused, the address that is used "on air" will be stable for the -//! duration that the BT stack is up (so the address can be expected to have changed after rebooting -//! or resetting the stack). -//! In case the local address is currently pinned, this function will be a no-op. -void bt_local_addr_pause_cycling(void); - -//! Resumes cycling of local Private Resolvable Address (ref counted). -//! In case the local address is currently pinned, this function will be a no-op. -void bt_local_addr_resume_cycling(void); - -//! Called by BT driver to indicate what the local address was that was used during the pairing -//! and pinning was requested. See comment in the implementation for more details. -void bt_local_addr_pin(const BTDeviceAddress *addr); - -//! Handler for bonding changes (deletions primarily). -void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op); - -//! Called during the BT stack initialization. -void bt_local_addr_init(void); diff --git a/src/fw/services/common/bluetooth/local_id.h b/src/fw/services/common/bluetooth/local_id.h deleted file mode 100644 index 4cc4df344b..0000000000 --- a/src/fw/services/common/bluetooth/local_id.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -//! Called by bl_ctl right after the stack starts, to configure the local device name and address. -void bt_local_id_configure_driver(void); - -//! Sets a new device name, overriding the existing (default) one. -//! The name will be truncated to BT_DEVICE_NAME_BUFFER_SIZE - 1 characters. -void bt_local_id_set_device_name(const char *device_name); - -//! Copies the name of the local device into the given buffer. -//! @param is_le Only consumed if the device used is dual mode. If so, -// this changes the name returned -void bt_local_id_copy_device_name(char name_out[BT_DEVICE_NAME_BUFFER_SIZE], bool is_le); - -//! Copies the address of the local device. -void bt_local_id_copy_address(BTDeviceAddress *addr_out); - -//! Copies a hex-formatted string representation ("0x000000000000") of the device address into the -//! given buffer. The buffer should be at least BT_ADDR_FMT_BUFFER_SIZE_BYTES bytes in size to fit -//! the address string. -//! If there is no local address known, the string "Unknown" will be copied into the buffer. -void bt_local_id_copy_address_hex_string(char addr_hex_str_out[BT_ADDR_FMT_BUFFER_SIZE_BYTES]); - -//! Copies a MAC-formatted string representation ("00:00:00:00:00:00") of the device address into -//! the given buffer. The buffer should be at least BT_ADDR_FMT_BUFFER_SIZE_BYTES bytes in size to -//! fit the address string. -void bt_local_id_copy_address_mac_string(char addr_mac_str_out[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]); - -//! Generates a BTDeviceAddress from the serial number of the watch -void bt_local_id_generate_address_from_serial(BTDeviceAddress *addr_out); diff --git a/src/fw/services/common/bluetooth/pairability.c b/src/fw/services/common/bluetooth/pairability.c deleted file mode 100644 index 81750791ba..0000000000 --- a/src/fw/services/common/bluetooth/pairability.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE -#include "system/logging.h" -#include "system/passert.h" - -#include "comm/ble/gap_le_slave_discovery.h" -#include "kernel/pebble_tasks.h" -#include "services/common/bluetooth/bluetooth_ctl.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/local_addr.h" -#include "services/common/bluetooth/pairability.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" - -#include -#include -#include - -static void prv_pairability_timer_cb(void *unused); - -static int s_allow_bt_pairing_refcount = 0; -static int s_allow_ble_pairing_refcount = 0; - -static RegularTimerInfo s_pairability_timer_info = { - .cb = prv_pairability_timer_cb, -}; - -static void evaluate_pairing_refcount(void *data) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - - if (!bt_ctl_is_bluetooth_running()) { - return; - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Pairabilty state: LE=%u, Classic=%u", - s_allow_ble_pairing_refcount, s_allow_bt_pairing_refcount); - - bool is_ble_pairable_and_discoverable = (s_allow_ble_pairing_refcount > 0); - - bt_driver_le_pairability_set_enabled(is_ble_pairable_and_discoverable); - if (gap_le_slave_is_discoverable() != is_ble_pairable_and_discoverable) { - if (is_ble_pairable_and_discoverable) { - bt_local_addr_pause_cycling(); - } else { - bt_local_addr_resume_cycling(); - } - gap_le_slave_set_discoverable(is_ble_pairable_and_discoverable); - } - - if (bt_driver_supports_bt_classic()) { - bt_driver_classic_pairability_set_enabled((s_allow_bt_pairing_refcount > 0)); - bt_driver_classic_update_connectability(); - } -} - -static void prv_schedule_evaluation(void) { - // We used to sparingly schedule the evaluation and had a bug because of this: - // https://pebbletechnology.atlassian.net/browse/PBL-22884 - // Because this pretty much only happens in response to user input, don't bother limiting this, - // and always evaluate, even though the state might not have changed: - system_task_add_callback(evaluate_pairing_refcount, NULL); -} - -void bt_pairability_use(void) { - ++s_allow_bt_pairing_refcount; - ++s_allow_ble_pairing_refcount; - prv_schedule_evaluation(); -} - -void bt_pairability_use_bt(void) { - ++s_allow_bt_pairing_refcount; - prv_schedule_evaluation(); -} - -void bt_pairability_use_ble(void) { - ++s_allow_ble_pairing_refcount; - prv_schedule_evaluation(); -} - -static void prv_pairability_timer_cb(void *unused) { - regular_timer_remove_callback(&s_pairability_timer_info); - bt_pairability_release_ble(); -} - -void bt_pairability_use_ble_for_period(uint16_t duration_secs) { - if (!regular_timer_is_scheduled(&s_pairability_timer_info)) { - // If this function is called multiple times before the timer is unscheduled, limit to calling - // "use" only once: - bt_pairability_use_ble(); - } - // Always reschedule, even if the duration is shorter than the one that might already be - // scheduled: - regular_timer_add_multisecond_callback(&s_pairability_timer_info, duration_secs); -} - -void bt_pairability_release(void) { - PBL_ASSERT(s_allow_bt_pairing_refcount != 0 && s_allow_ble_pairing_refcount != 0, ""); - --s_allow_bt_pairing_refcount; - --s_allow_ble_pairing_refcount; - prv_schedule_evaluation(); -} - -void bt_pairability_release_bt(void) { - PBL_ASSERT(s_allow_bt_pairing_refcount != 0, ""); - --s_allow_bt_pairing_refcount; - prv_schedule_evaluation(); -} - -void bt_pairability_release_ble(void) { - PBL_ASSERT(s_allow_ble_pairing_refcount != 0, ""); - --s_allow_ble_pairing_refcount; - prv_schedule_evaluation(); -} - -//! Call this whenever we modify the number of saved bondings we have. -void bt_pairability_update_due_to_bonding_change(void) { - static bool s_pairable_due_to_no_gateway_bondings = false; - const bool has_classic_bonding = - (bt_driver_supports_bt_classic() && - bt_persistent_storage_has_active_bt_classic_gateway_bonding()); - - if (!has_classic_bonding && - !bt_persistent_storage_has_active_ble_gateway_bonding() && - !bt_persistent_storage_has_ble_ancs_bonding()) { - if (!s_pairable_due_to_no_gateway_bondings) { - bt_pairability_use(); - s_pairable_due_to_no_gateway_bondings = true; - } - } else { - if (s_pairable_due_to_no_gateway_bondings) { - bt_pairability_release(); - s_pairable_due_to_no_gateway_bondings = false; - } - } -} - -void bt_pairability_init(void) { - bt_pairability_update_due_to_bonding_change(); - prv_schedule_evaluation(); -} diff --git a/src/fw/services/common/bluetooth/pairability.h b/src/fw/services/common/bluetooth/pairability.h deleted file mode 100644 index ec55a2423d..0000000000 --- a/src/fw/services/common/bluetooth/pairability.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Reference counted request to allow us to be discovered and paired with over BT Classic & LE. -void bt_pairability_use(void); - -//! Reference counted request to allow us to be discovered and paired with over BT Classic. -void bt_pairability_use_bt(void); - -//! Reference counted request to allow us to be discovered and paired with over BLE. -void bt_pairability_use_ble(void); - -//! Reference counted request to allow us to be discovered and paired with over BLE for a specific -//! period, after which bt_pairability_release_ble will be called automatically. -void bt_pairability_use_ble_for_period(uint16_t duration_secs); - -//! Reference counted request to disallow us to be discovered and paired with over BT Classic & LE. -void bt_pairability_release(void); - -//! Reference counted request to disallow us to be discovered and paired with over BT Classic. -void bt_pairability_release_bt(void); - -//! Reference counted request to disallow us to be discovered and paired with over BLE. -void bt_pairability_release_ble(void); - -//! Evaluates whether there are any bondings to gateways. If there are none, make the system -//! discoverable and pairable. -void bt_pairability_update_due_to_bonding_change(void); - -void bt_pairability_init(void); diff --git a/src/fw/services/common/bluetooth/pp_ble_control.c b/src/fw/services/common/bluetooth/pp_ble_control.c deleted file mode 100644 index 44caa551fa..0000000000 --- a/src/fw/services/common/bluetooth/pp_ble_control.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE - -#include "pp_ble_control.h" -#include "services/common/bluetooth/pairability.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" - -typedef enum { - // Values 0 - 3 are deprecated, do not use. - BLEControlCommandTypeSetDiscoverablePairable = 4, -} BLEControlCommandType; - -typedef struct PACKED { - uint8_t opcode; - bool discoverable_pairable; - uint16_t duration; -} BLEControlCommandSetDiscoverablePairable; - -// ----------------------------------------------------------------------------- -//! Handler for the "Set Discoverable & Pairable" command -static void prv_handle_set_discoverable_pairable( - BLEControlCommandSetDiscoverablePairable *cmd_data) { - bt_pairability_use_ble_for_period(cmd_data->duration); - PBL_LOG(LOG_LEVEL_INFO, "Set Discoverable Pairable: %u, %u", - cmd_data->discoverable_pairable, cmd_data->duration); -} - -// ----------------------------------------------------------------------------- -//! Pebble protocol handler for the BLE control endpoint -void pp_ble_control_protocol_msg_callback( - CommSession* session, const uint8_t *data, unsigned int length) { - PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground); - - if (length < sizeof(BLEControlCommandSetDiscoverablePairable)) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid pp_ble_control_protocol_msg_callback message: %d", length); - return; - } - - const uint8_t opcode = *(const uint8_t *) data; - switch (opcode) { - case 0 ... 3: - PBL_LOG(LOG_LEVEL_INFO, "Deprecated & unsupported opcode: %u", opcode); - break; - - case BLEControlCommandTypeSetDiscoverablePairable: { - BLEControlCommandSetDiscoverablePairable *cmd_data = - (BLEControlCommandSetDiscoverablePairable *)data; - prv_handle_set_discoverable_pairable(cmd_data); - break; - } - default: - PBL_LOG(LOG_LEVEL_DEBUG, "Unknown opcode %u", opcode); - break; - } -} diff --git a/src/fw/services/common/bluetooth/pp_ble_control.h b/src/fw/services/common/bluetooth/pp_ble_control.h deleted file mode 100644 index 7c034e9d54..0000000000 --- a/src/fw/services/common/bluetooth/pp_ble_control.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/comm_session/session.h" - -void pp_ble_control_protocol_msg_callback(CommSession* session, - const uint8_t *data, - unsigned int length); diff --git a/src/fw/services/common/clock.c b/src/fw/services/common/clock.c deleted file mode 100644 index 4241aafc62..0000000000 --- a/src/fw/services/common/clock.c +++ /dev/null @@ -1,994 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/clock.h" - -#include "console/prompt.h" -#include "drivers/rtc.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session.h" -#include "services/common/i18n/i18n.h" -#include "services/common/regular_timer.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/timezone_database.h" -#include "services/normal/wakeup.h" -#include "shell/prefs.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "util/attributes.h" -#include "util/math.h" -#include "util/net.h" -#include "util/size.h" -#include "util/string.h" - -#include - -// NOTE: There are RECOVERY_FW ifdefs in this file because PRF does not have -// timezone support - -#define UNKNOWN_TIMEZONE_ID (-1) - -static const uint16_t protocol_time_endpoint_id = 11; - -static RegularTimerInfo s_dst_checker; - -//! Migrations for services that use timezone info -static void prv_migrate_timezone_info(int utc_diff) { -#ifndef RECOVERY_FW - // Since all migrations are to UTC time, we are passed the relative offset from UTC - notifications_migrate_timezone(utc_diff); - wakeup_migrate_timezone(utc_diff); -#endif -} - -static time_t prv_migrate_local_time_to_UTC(time_t local_time) { - return time_local_to_utc(local_time); -} - -// Should only called by prv_update_time_info_and_generate_event()! -static void prv_handle_timezone_set(TimezoneInfo *tz_info) { - // Check if the timezone is set before setting it. This ensures that this - // will only be false once as needed for us to migrate. - bool timezone_migration_needed = !clock_is_timezone_set(); - - time_util_update_timezone(tz_info); - - // Update the RTC registers with the latest timezone info - rtc_set_timezone(tz_info); - - // We are pivoting to UTC from localtime for the first time - if (timezone_migration_needed) { - time_t t = rtc_get_time(); - t = prv_migrate_local_time_to_UTC(t); - rtc_sanitize_time_t(&t); - rtc_set_time(t); // Pivot RTC from localtime to UTC - prv_migrate_timezone_info(tz_info->tm_gmtoff); - } -} - -typedef struct PACKED { -// This struct is packed because it mirrors the endpoint definition: -// https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=491698#PebbleProtocol(BluetoothSerial)-0xb(11)-Time/Clock(bigendian) - time_t utc_time; // UTC timestamp - int16_t utc_offset_min; // local timestamp - UTC timestamp in mins - int8_t region_name_len; // timezone name length - char region_name[TIMEZONE_NAME_LENGTH]; // timezone name string -} TimezoneCBData; - -#ifndef UNITTEST -_Static_assert(sizeof(time_t) == 4, "Sizeof time_t does not match endpoint definition"); -#endif - -#if !defined(RECOVERY_FW) -static time_t prv_clock_dstrule_to_timestamp( - bool is_end, const TimezoneInfo *tz_info, const TimezoneDSTRule *rule, int year) { - - struct tm time_tm = { - .tm_min = rule->minute, - .tm_hour = rule->hour, - .tm_mday = rule->mday, - .tm_mon = rule->month, - .tm_year = year, - .tm_gmtoff = 0, - .tm_isdst = 0, - }; - // A few countries actually have their DST rule on the midnight AFTER a day - // This is subtly different from the midnight OF a day. - if (rule->hour >= HOURS_PER_DAY) { - time_tm.tm_hour %= HOURS_PER_DAY; - } - // Brazil delays DST end by one week every 3 years for elections - if (tz_info->dst_id == DSTID_BRAZIL && (((TM_YEAR_ORIGIN + year) % 3) == 2) && is_end) { - time_tm.tm_mday += DAYS_PER_WEEK; - } - time_t uxtime = mktime(&time_tm); - gmtime_r(&uxtime, &time_tm); - - for (int i = 0; i < DAYS_PER_WEEK; i++) { // max is DAYS_PER_WEEK to find a day_of_week - // we also have to check month here, as leap-year case puts us 1 day past feb -#define DSTRULE_WDAY_ANY (255) - if ((time_tm.tm_wday == rule->wday || rule->wday == DSTRULE_WDAY_ANY) && - time_tm.tm_mon == rule->month) { - break; - } - time_tm.tm_mday += (rule->flag & TIMEZONE_FLAG_DAY_DECREMENT) ? -1 : 1; - uxtime = mktime(&time_tm); - gmtime_r(&uxtime, &time_tm); - } - - if (rule->hour >= HOURS_PER_DAY) { - time_tm.tm_mday += rule->hour / HOURS_PER_DAY; - uxtime = mktime(&time_tm); - gmtime_r(&uxtime, &time_tm); - } - - if (rule->flag & TIMEZONE_FLAG_STANDARD_TIME) { // Standard time (not wall time) - time_tm.tm_gmtoff = tz_info->tm_gmtoff; - time_tm.tm_isdst = 0; - } else if (rule->flag & TIMEZONE_FLAG_UTC_TIME) { // UTC - time_tm.tm_gmtoff = 0; - time_tm.tm_isdst = 0; - } else { // Wall time - time_tm.tm_gmtoff = tz_info->tm_gmtoff; - time_tm.tm_isdst = is_end; - } - // Lord Howe Island has a half-hour DST - if (tz_info->dst_id == DSTID_LORDHOWE) { - uxtime -= time_tm.tm_isdst ? SECONDS_PER_HOUR / 2 : 0; - } else { - uxtime -= time_tm.tm_isdst ? SECONDS_PER_HOUR : 0; - } - uxtime -= time_tm.tm_gmtoff; - return uxtime; -} -#endif // RECOVERY_FW - -T_STATIC void prv_update_dstrule_timestamps_by_dstzone_id(TimezoneInfo *tz_info, time_t utc_time) { - if (tz_info->dst_id == 0) { - tz_info->dst_start = 0; - tz_info->dst_end = 0; - return; - } - -#if defined(RECOVERY_FW) - return; -#else - - // Load the pair of TimezoneDSTRule objects from the timezone database - - TimezoneDSTRule dst_rule_begin; - TimezoneDSTRule dst_rule_end; - - if (!timezone_database_load_dst_rule(tz_info->dst_id, &dst_rule_begin, &dst_rule_end)) { - // No DST rule or invalid DST ID. Either way just clear the DST information. - tz_info->dst_start = 0; - tz_info->dst_end = 0; - return; - } - - struct tm current_tm; - gmtime_r(&utc_time, ¤t_tm); - - // Calculate the timestamps of the start and ends of DST for the previous year, the current - // year, and the next year. - -#define DST_YEARS_RANGE 3 -#define DST_YEARS_OFFSET 1 - time_t dst_start_stamps[DST_YEARS_RANGE]; - time_t dst_end_stamps[DST_YEARS_RANGE]; - for (int i = 0; i < DST_YEARS_RANGE; i++) { - const int year = current_tm.tm_year + (i - DST_YEARS_OFFSET); - - dst_start_stamps[i] = - prv_clock_dstrule_to_timestamp(false, tz_info, &dst_rule_begin, year); - dst_end_stamps[i] = - prv_clock_dstrule_to_timestamp(true, tz_info, &dst_rule_end, year); - } - - // Figure out which timestamps are relevant to us - - int start_idx = DST_YEARS_OFFSET; - int end_idx = DST_YEARS_OFFSET; - - if (dst_start_stamps[start_idx] > dst_end_stamps[end_idx]) { - start_idx--; - } - - if (dst_start_stamps[start_idx] < utc_time && dst_end_stamps[end_idx] < utc_time) { - start_idx++; - end_idx++; - } - - tz_info->dst_start = dst_start_stamps[start_idx]; - tz_info->dst_end = dst_end_stamps[end_idx]; - -#endif // RECOVERY_FW -} - -static void prv_clock_get_timezone_info_from_region_id( - int16_t region_id, time_t utc_time, TimezoneInfo *tz_info) { - -#ifdef RECOVERY_FW - *tz_info = (TimezoneInfo) { .dst_id = 0 }; -#else - timezone_database_load_region_info(region_id, tz_info); - prv_update_dstrule_timestamps_by_dstzone_id(tz_info, utc_time); -#endif -} - -static TimezoneInfo prv_get_timezone_info_from_data(TimezoneCBData *tz_data) { - int region_id = -1; - - if (tz_data->region_name_len) { - region_id = timezone_database_find_region_by_name(tz_data->region_name, - tz_data->region_name_len); - } - - if (region_id != -1) { - // We have a valid region! - TimezoneInfo tz_info; - prv_clock_get_timezone_info_from_region_id(region_id, tz_data->utc_time, &tz_info); - return tz_info; - } - - // Else, we couldn't find find the specified timezone. -#ifndef RECOVERY_FW - TimezoneInfo tz_info = { - .dst_id = 0, - .timezone_id = UNKNOWN_TIMEZONE_ID, - .tm_gmtoff = tz_data->utc_offset_min * SECONDS_PER_MINUTE, - .dst_start = 0, - .dst_end = 0, - }; - - // I was hoping to fill the name with something like UTC-10 or UTC+4.25 but we only get 5 chars - strncpy(tz_info.tm_zone, "N/A", TZ_LEN - 1); - return tz_info; -#else - return (TimezoneInfo) {}; -#endif -} - -// This routine is solely responsible for setting the time and/or timezone for -// the system RTC. After the time is changed, it generates an event for -// consumers interested in time changes -T_STATIC void prv_update_time_info_and_generate_event(time_t *t, TimezoneInfo *tz_info) { - int orig_gmt_offset = time_get_gmtoffset(); - time_t orig_utc_time = rtc_get_time(); - TimezoneInfo tz_adjust_info = {{0}}; - - if (clock_is_timezone_set()) { // We'll need to update timezone stamps. - time_t tz_adjust_time; - // Get the time that we need to adjust for. - if (t) { - tz_adjust_time = *t; - } else { - tz_adjust_time = orig_utc_time; - } - if (tz_info) { // Adjust the DST rule timestamps of the provided tz_info - prv_update_dstrule_timestamps_by_dstzone_id(tz_info, tz_adjust_time); - } else if (clock_get_timezone_region_id() != UNKNOWN_TIMEZONE_ID) { - // If we have a timezone _actually_ set, update our own. - int region_id = clock_get_timezone_region_id(); - prv_clock_get_timezone_info_from_region_id(region_id, tz_adjust_time, &tz_adjust_info); - tz_info = &tz_adjust_info; // We need to set timezone info so point to the new info. - } - } - - // Note: update the timezone before setting the utc time. (If we set the utc - // time first we could wind up accidentally applying the timezone correction - // to that value in the case where no timezone data previously existed - // ... such as a migration from legacy firmware or after the RTC backup - // domain had completely powered down) - if (tz_info) { - prv_handle_timezone_set(tz_info); - } - - if (t) { - rtc_set_time(*t); - } - - int new_gmt_offset = time_get_gmtoffset(); - int new_utc_time = rtc_get_time(); - - PebbleEvent e = { - .type = PEBBLE_SET_TIME_EVENT, - .set_time_info = { - .utc_time_delta = new_utc_time - orig_utc_time, - .gmt_offset_delta = new_gmt_offset - orig_gmt_offset, - .dst_changed = false, - } - }; - event_put(&e); -} - -static void prv_handle_set_utc_and_timezone_msg(TimezoneCBData *tz_data) { - tz_data->utc_time = ntohl(tz_data->utc_time); - tz_data->utc_offset_min = ntohs(tz_data->utc_offset_min); - - const char *region_name = tz_data->region_name; - if (tz_data->region_name_len == 0) { - region_name = "[N/A]"; - } - PBL_LOG(LOG_LEVEL_DEBUG, "set_timezone utc_time: %u offset: %d region_name: %s", - (int) tz_data->utc_time, (int) tz_data->utc_offset_min, region_name); - - TimezoneInfo tz_info = prv_get_timezone_info_from_data(tz_data); - shell_prefs_set_automatic_timezone_id(tz_info.timezone_id); - if (clock_timezone_source_is_manual()) { - prv_update_time_info_and_generate_event(&tz_data->utc_time, NULL); - } else { - prv_update_time_info_and_generate_event(&tz_data->utc_time, &tz_info); - } -} - -static void prv_handle_set_time_msg(time_t new_time) { - PBL_LOG(LOG_LEVEL_WARNING, "Mobile app calling deprecated API, time = %d", - (int)new_time); - if (clock_is_timezone_set()) { - new_time = prv_migrate_local_time_to_UTC(new_time); - } - - prv_update_time_info_and_generate_event(&new_time, NULL); -} - -void clock_protocol_msg_callback(CommSession *session, const uint8_t* data, unsigned int length) { - char sub_command = *data++; - - switch (sub_command) { - // Get time request: - case 0x00: { - time_t t = rtc_get_time(); - - // Send Get time response (0x01): - const unsigned int response_buffer_length = 1 + 4; - uint8_t response_buffer[response_buffer_length]; - - response_buffer[0] = 0x01; - - *(uint32_t*)(response_buffer + 1) = htonl(t); - - comm_session_send_data(session, protocol_time_endpoint_id, response_buffer, - response_buffer_length, COMM_SESSION_DEFAULT_TIMEOUT); - PBL_LOG_VERBOSE("protocol_time_callback called, responding with current time: %"PRIu32, - (uint32_t)t); - break; - } - // Set time: - case 0x02: { - time_t new_time = ntohl(*(uint32_t*)data); - prv_handle_set_time_msg(new_time); - break; - } - // Set timezone: - case 0x03: { - // Verify that the message length is correct - const size_t header_size = offsetof(TimezoneCBData, region_name); - const uint8_t *timezone_length_ptr = data + offsetof(TimezoneCBData, region_name_len); - if (length != (sizeof(uint8_t) + header_size + *timezone_length_ptr)) { - PBL_LOG(LOG_LEVEL_WARNING, "Set timezone message invalid length"); - return; - } - - TimezoneCBData *timezone_data = (TimezoneCBData *)data; - prv_handle_set_utc_and_timezone_msg(timezone_data); - break; - } - default: - PBL_LOG(LOG_LEVEL_WARNING, "Invalid message received. First byte is %u", data[0]); - break; - } -} - -// TODO: Using a regular timer is pretty gross... -static void prv_watch_dst(void* user) { - const bool was_dst = (bool)user; - const bool is_dst = time_get_isdst(rtc_get_time()); - if (is_dst != was_dst) { - PebbleEvent e = { - .type = PEBBLE_SET_TIME_EVENT, - .set_time_info = { - .utc_time_delta = 0, - .gmt_offset_delta = 0, - .dst_changed = true, - } - }; - event_put(&e); - s_dst_checker.cb_data = (void*)is_dst; - } -} - -void clock_init(void) { - if (clock_is_timezone_set()) { - TimezoneInfo tz_info; - rtc_get_timezone(&tz_info); - time_util_update_timezone(&tz_info); - } - // TODO: Using a regular timer is pretty gross... - s_dst_checker = (RegularTimerInfo) { - .cb = prv_watch_dst, - .cb_data = (void*)time_get_isdst(rtc_get_time()), - }; - regular_timer_add_seconds_callback(&s_dst_checker); -} - -void clock_get_time_tm(struct tm* time_tm) { - rtc_get_time_tm(time_tm); -} - -size_t clock_format_time(char *buffer, uint8_t size, int16_t hours, int16_t minutes, - bool add_space) { - if (size == 0 || buffer == NULL) { - return 0; - } - - bool is24h = clock_is_24h_style(); - const char *format; - - // [INTL] you want to have layout resources that specify time formatting, - // and be able to set a default one for each locale. - if (is24h) { - format = "%u:%02u"; - } else { - if (hours < 12) { - format = add_space ? "%u:%02u AM" : "%u:%02uAM"; - } else { - format = add_space ? "%u:%02u PM" : "%u:%02uPM"; - } - } - return sniprintf(buffer, size, format, time_util_get_num_hours(hours, is24h), minutes); -} - -size_t clock_copy_time_string_timestamp(char *buffer, uint8_t size, time_t timestamp) { - struct tm time; - sys_localtime_r(×tamp, &time); - return clock_format_time(buffer, size, time.tm_hour, time.tm_min, true); -} - -void clock_copy_time_string(char *buffer, uint8_t size) { - time_t t = sys_get_time(); - clock_copy_time_string_timestamp(buffer, size, t); -} - -static size_t prv_format_time(char *buffer, int buf_size, const char *format, time_t timestamp) { - struct tm time_tm; - localtime_r(×tamp, &time_tm); - const size_t ret_val = strftime(buffer, buf_size, i18n_get(format, buffer), &time_tm); - i18n_free(format, buffer); - return ret_val; -} - -size_t clock_get_time_number(char *number_buffer, size_t number_buffer_size, time_t timestamp) { - const size_t written = - prv_format_time(number_buffer, number_buffer_size, - (clock_is_24h_style() ? i18n_noop("%R") : i18n_noop("%l:%M")), timestamp); - const char *number_buffer_ptr = string_strip_leading_whitespace(number_buffer); - memmove(number_buffer, - number_buffer_ptr, - number_buffer_size - (number_buffer_ptr - number_buffer)); - return written - (number_buffer_ptr - number_buffer); -} - -size_t clock_get_time_word(char *buffer, size_t buffer_size, time_t timestamp) { - if (clock_is_24h_style()) { - buffer[0] = '\0'; - return 0; - } else { - return prv_format_time(buffer, buffer_size, i18n_noop("%p"), timestamp); - } -} - -static void prv_copy_time_string_timestamp(char *number_buffer, uint8_t number_buffer_size, - char *word_buffer, uint8_t word_buffer_size, time_t timestamp) { - clock_get_time_number(number_buffer, number_buffer_size, timestamp); - clock_get_time_word(word_buffer, word_buffer_size, timestamp); -} - -static void prv_get_relative_all_day_string(char *buffer, int buffer_size, time_t timestamp) { - time_t today = time_util_get_midnight_of(rtc_get_time()); - if (time_util_get_midnight_of(timestamp) == today) { - i18n_get_with_buffer("Today", buffer, buffer_size); - } else { - i18n_get_with_buffer("All day", buffer, buffer_size); - } -} - -static void prv_copy_relative_time_string(char *number_buffer, uint8_t number_buffer_size, - char *word_buffer, uint8_t word_buffer_size, time_t timestamp, time_t end_time) { - time_t now = rtc_get_time(); - // average without overflows since time_t might be signed and now ~1.4 billion, so 2*now > INT_MAX - time_t midtime = timestamp / 2 + end_time / 2; - if (midtime > now) { // future - time_t difference = timestamp - now; - if (timestamp < now || difference < SECONDS_PER_MINUTE) { - i18n_get_with_buffer("Now", word_buffer, word_buffer_size); - strncpy(number_buffer, "", number_buffer_size); - } else if (difference <= SECONDS_PER_HOUR) { - snprintf(number_buffer, number_buffer_size, "%ld", difference / SECONDS_PER_MINUTE); - i18n_get_with_buffer(" MIN. TO", word_buffer, word_buffer_size); - } else { - prv_copy_time_string_timestamp(number_buffer, number_buffer_size, word_buffer, - word_buffer_size, timestamp); - } - } else { // past - time_t difference = now - timestamp; - if (now < timestamp || difference < SECONDS_PER_MINUTE) { - i18n_get_with_buffer("Now", word_buffer, word_buffer_size); - strncpy(number_buffer, "", number_buffer_size); - } else { - prv_copy_time_string_timestamp(number_buffer, number_buffer_size, word_buffer, - word_buffer_size, timestamp); - } - } -} - -// number: 10 -// word: min to -void clock_get_event_relative_time_string(char *number_buffer, int number_buffer_size, - char *word_buffer, int word_buffer_size, time_t timestamp, uint16_t duration, - time_t current_day, bool all_day) { - time_t end_time = timestamp + duration * SECONDS_PER_MINUTE; - if (all_day) { - // all day event, multiday or single day - prv_get_relative_all_day_string(word_buffer, word_buffer_size, current_day); - strncpy(number_buffer, "", number_buffer_size); - } else if (time_util_get_midnight_of(timestamp) == current_day) { - // first day of multiday event or only day - prv_copy_relative_time_string(number_buffer, number_buffer_size, word_buffer, - word_buffer_size, timestamp, end_time); - } else if (time_util_get_midnight_of(end_time) == current_day) { - // last day of multiday event - prv_copy_relative_time_string(number_buffer, number_buffer_size, word_buffer, - word_buffer_size, end_time, end_time); - } else { - // middle day of non-all day multiday event - prv_get_relative_all_day_string(word_buffer, word_buffer_size, current_day); - strncpy(number_buffer, "", number_buffer_size); - } -} - -DEFINE_SYSCALL(bool, clock_is_24h_style, void) { - return shell_prefs_get_clock_24h_style(); -} - -void clock_set_24h_style(bool is_24h_style) { - shell_prefs_set_clock_24h_style(is_24h_style); -} - -DEFINE_SYSCALL(bool, clock_is_timezone_set, void) { - return rtc_is_timezone_set(); // If timezone abbr isn't set -} - -bool clock_timezone_source_is_manual(void) { - return shell_prefs_is_timezone_source_manual(); -} - -void clock_set_manual_timezone_source(bool manual) { - shell_prefs_set_timezone_source_manual(manual); -} - -time_t clock_to_timestamp(WeekDay day, int hour, int minute) { - time_t t = sys_get_time(); - struct tm cal; - sys_localtime_r(&t, &cal); - - if (day != TODAY) { - // If difference between WeekDay and current day, always in the future - day -= 1; // cal_wday is 0-6 - int day_offset = (day > cal.tm_wday) ? (day - cal.tm_wday) : (day - cal.tm_wday + 7); - cal.tm_mday += day_offset; // normalized by mktime - } else if ((hour < cal.tm_hour) || (hour == cal.tm_hour && minute <= cal.tm_min)){ - // Always return a future timestamp, so if day was today, and - // minutes and hours already occurred, just make it tomorrow - cal.tm_mday++; // normalized by mktime - } - - cal.tm_hour = hour; - cal.tm_min = minute; - - return mktime(&cal); -} - -void command_timezone_clear(void) { - rtc_timezone_clear(); -} - -void command_get_time(void) { - char buffer[80]; - char time_buffer[26]; - prompt_send_response_fmt(buffer, 80, "Time is now <%s>", rtc_get_time_string(time_buffer)); -} - -void command_set_time(const char *arg) { - time_t t = atoi(arg); - if (t == 0) { - prompt_send_response("Invalid length"); - return; - } - - prv_update_time_info_and_generate_event(&t, NULL); - - char buffer[80]; - char time_buffer[26]; - prompt_send_response_fmt(buffer, 80, "Time is now <%s>", rtc_get_time_string(time_buffer)); -} - -void clock_get_timezone_region(char* region_name, const size_t buffer_size) { - if (!region_name) { - return; - } - - if (clock_is_timezone_set()) { - int region_id = clock_get_timezone_region_id(); - if (region_id != UNKNOWN_TIMEZONE_ID) { - timezone_database_load_region_name(region_id, region_name); - } else { - // Show something like UTC-4 or UTC-10.25 - // This will typically happen in the emulator when we know the UTC offset, but not - // the timezone (fallback case). - int gmt_offset_m = time_get_gmtoffset() / SECONDS_PER_MINUTE; - int hour_offset = gmt_offset_m / MINUTES_PER_HOUR; - - char min_buf[4] = {0}; - int min_offset_percent = ((ABS(gmt_offset_m) % MINUTES_PER_HOUR) * 100) / MINUTES_PER_HOUR; - if (min_offset_percent) { - snprintf(min_buf, sizeof(min_buf), ".%d", min_offset_percent); - } - snprintf(region_name, buffer_size, "UTC%+d%s", hour_offset, min_buf); - } - } else { - strncpy(region_name, "---", buffer_size); - } -} - -int16_t clock_get_timezone_region_id(void) { - return rtc_get_timezone_id(); -} - -void clock_set_timezone_by_region_id(uint16_t region_id) { - TimezoneInfo tz_info; - prv_clock_get_timezone_info_from_region_id(region_id, rtc_get_time(), &tz_info); - prv_update_time_info_and_generate_event(NULL, &tz_info); -} - -void clock_get_friendly_date(char *buffer, int buf_size, time_t timestamp) { - const time_t now = rtc_get_time(); - - const time_t midnight = time_util_get_midnight_of(timestamp); - const time_t today_midnight = time_util_get_midnight_of(now); - - if (midnight == today_midnight) { - i18n_get_with_buffer("Today", buffer, buf_size); - } else if (midnight == (today_midnight - SECONDS_PER_DAY)) { - i18n_get_with_buffer("Yesterday", buffer, buf_size); - } else if (midnight == (today_midnight + SECONDS_PER_DAY)) { - i18n_get_with_buffer("Tomorrow", buffer, buf_size); - } else if (midnight <= (today_midnight + (5 * SECONDS_PER_DAY))) { - // Use weekday name up to 5 days in the future, aka "Sunday" - prv_format_time(buffer, buf_size, i18n_noop("%A"), timestamp); - } else { - // Otherwise use "Month Day", aka "June 21" - prv_format_time(buffer, buf_size, i18n_noop("%B %d"), timestamp); - } -} - -enum { - RoundTypeHalfUp, - RoundTypeHalfDown, - RoundTypeAlwaysUp, - RoundTypeAlwaysDown, -}; - -static time_t prv_round(time_t round_me, time_t multiple, int round_type) { - switch (round_type) { - case RoundTypeHalfDown: - return ((round_me + multiple / 2 - 1) / multiple) * multiple; - case RoundTypeAlwaysUp: - return ((round_me + multiple - 1) / multiple) * multiple; - case RoundTypeAlwaysDown: - return (round_me / multiple) * multiple; - case RoundTypeHalfUp: - default: - return ((round_me + multiple / 2) / multiple) * multiple; - } -} - -enum { - FullStyleLower12h, - FullStyleCapital12h, - FullStyleLower24h, - FullStyleCapital24h, -}; - -static void prv_clock_get_full_relative_time(char *buffer, int buf_size, time_t timestamp, - bool capitalized, bool with_fulltime) { - time_t today_midnight = time_util_get_midnight_of(rtc_get_time()); - time_t timestamp_midnight = time_util_get_midnight_of(timestamp); - time_t yesterday_midnight = time_util_get_midnight_of(rtc_get_time() - SECONDS_PER_DAY); - time_t last_week_midnight = time_util_get_midnight_of(rtc_get_time() - SECONDS_PER_WEEK); - time_t next_week_midnight = time_util_get_midnight_of(rtc_get_time() + SECONDS_PER_WEEK); - - const char *time_fmt = NULL; - int style; - if (clock_is_24h_style()) { - if (capitalized) { - style = FullStyleCapital24h; - } else { - style = FullStyleLower24h; - } - } else { - if (capitalized) { - style = FullStyleCapital12h; - } else { - style = FullStyleLower12h; - } - } - - if (timestamp_midnight == today_midnight) { - switch (style) { - case FullStyleLower12h: - case FullStyleCapital12h: - time_fmt = i18n_noop("%l:%M %p"); - break; - case FullStyleLower24h: - case FullStyleCapital24h: - time_fmt = i18n_noop("%R"); - break; - } - } else if (timestamp_midnight == yesterday_midnight) { - switch (style) { - case FullStyleLower12h: - case FullStyleCapital12h: - if (with_fulltime) { - time_fmt = i18n_noop("Yesterday, %l:%M %p"); - } else { - time_fmt = i18n_noop("Yesterday"); - } - break; - case FullStyleLower24h: - case FullStyleCapital24h: - if (with_fulltime) { - time_fmt = i18n_noop("Yesterday, %R"); - } else { - time_fmt = i18n_noop("Yesterday"); - } - break; - } - } else if (timestamp_midnight <= last_week_midnight || timestamp_midnight >= next_week_midnight) { - switch (style) { - case FullStyleLower12h: - case FullStyleCapital12h: - if (with_fulltime) { - time_fmt = i18n_noop("%b %e, %l:%M %p"); - } else { - time_fmt = i18n_noop("%B %e"); - } - break; - case FullStyleLower24h: - case FullStyleCapital24h: - if (with_fulltime) { - time_fmt = i18n_noop("%b %e, %R"); - } else { - time_fmt = i18n_noop("%B %e"); - } - break; - } - } else { - switch (style) { - case FullStyleLower12h: - case FullStyleCapital12h: - if (with_fulltime) { - time_fmt = i18n_noop("%a, %l:%M %p"); - } else { - time_fmt = i18n_noop("%A"); - } - break; - case FullStyleLower24h: - case FullStyleCapital24h: - if (with_fulltime) { - time_fmt = i18n_noop("%a, %R"); - } else { - time_fmt = i18n_noop("%A"); - } - break; - } - } - prv_format_time(buffer, buf_size, time_fmt, timestamp); -} - -static void prv_clock_get_relative_time_string(char *buffer, int buf_size, time_t timestamp, - bool capitalized, int max_relative_hrs, - bool with_fulltime) { - time_t difference = rtc_get_time() - timestamp; - - time_t today_midnight = time_util_get_midnight_of(rtc_get_time()); - time_t timestamp_midnight = time_util_get_midnight_of(timestamp); - - if (today_midnight != timestamp_midnight) { - prv_clock_get_full_relative_time(buffer, buf_size, timestamp, capitalized, with_fulltime); - - } else if (difference >= (SECONDS_PER_HOUR * max_relative_hrs)) { - prv_clock_get_full_relative_time(buffer, buf_size, timestamp, capitalized, with_fulltime); - - } else if (difference >= SECONDS_PER_HOUR) { - const int num_hrs = - prv_round(difference, SECONDS_PER_HOUR, RoundTypeHalfUp) / SECONDS_PER_HOUR; - - const char *str_fmt; - if (capitalized) { - str_fmt = i18n_noop("%lu H AGO"); - } else if (num_hrs == 1) { - str_fmt = i18n_noop("An hour ago"); - } else { - str_fmt = i18n_noop("%lu hours ago"); - } - snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_hrs); - - } else if (difference >= SECONDS_PER_MINUTE) { - const int num_minutes = - prv_round(difference, SECONDS_PER_MINUTE, RoundTypeAlwaysDown) / SECONDS_PER_MINUTE; - - const char *str_fmt; - if (capitalized) { - str_fmt = i18n_noop("%lu MIN AGO"); - } else if (num_minutes == 1) { - str_fmt = i18n_noop("%lu minute ago"); - } else { - str_fmt = i18n_noop("%lu minutes ago"); - } - snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_minutes); - - } else if (difference >= 0) { - strncpy(buffer, capitalized ? i18n_get("NOW", buffer) : i18n_get("Now", buffer), buf_size); - - } else if (difference >= -(SECONDS_PER_HOUR - SECONDS_PER_MINUTE)) { - const int num_minutes = - prv_round(-difference, SECONDS_PER_MINUTE, RoundTypeAlwaysUp) / SECONDS_PER_MINUTE; - - const char *str_fmt; - if (capitalized) { - str_fmt = i18n_noop("IN %lu MIN"); - } else if (num_minutes == 1) { - str_fmt = i18n_noop("In %lu minute"); - } else { - str_fmt = i18n_noop("In %lu minutes"); - } - snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_minutes); - - } else if (difference >= -(SECONDS_PER_HOUR * max_relative_hrs)) { - const int num_hrs = - prv_round(-difference, SECONDS_PER_HOUR, RoundTypeHalfDown) / SECONDS_PER_HOUR; - - const char *str_fmt; - if (capitalized) { - str_fmt = i18n_noop("IN %lu H"); - } else if (num_hrs == 1) { - str_fmt = i18n_noop("In %lu hour"); - } else { - str_fmt = i18n_noop("In %lu hours"); - } - snprintf(buffer, buf_size, i18n_get(str_fmt, buffer), num_hrs); - - } else { - prv_clock_get_full_relative_time(buffer, buf_size, timestamp, capitalized, with_fulltime); - } - i18n_free_all(buffer); -} - -size_t clock_get_date(char *buffer, int buf_size, time_t timestamp) { - return prv_format_time(buffer, buf_size, i18n_noop("%m/%d"), timestamp); -} - -size_t clock_get_day_date(char *buffer, int buf_size, time_t timestamp) { - return prv_format_time(buffer, buf_size, i18n_noop("%d"), timestamp); -} - -static size_t prv_clock_get_month_named_date(char *buffer, size_t buffer_size, time_t timestamp, - bool abbrev) { - const char *format = abbrev ? i18n_noop("%b ") : i18n_noop("%B "); - const size_t month_size = prv_format_time(buffer, buffer_size, format, timestamp); - char *day_buffer = buffer + month_size; - const size_t day_buffer_size = buffer_size - month_size; - size_t day_size = prv_format_time(day_buffer, day_buffer_size, i18n_noop("%e"), timestamp); - const char *day_buffer_ptr = string_strip_leading_whitespace(day_buffer); - memmove(day_buffer, day_buffer_ptr, day_buffer_size - (day_buffer_ptr - day_buffer)); - return month_size + day_size; -} - -size_t clock_get_month_named_date(char *buffer, size_t buffer_size, time_t timestamp) { - const bool abbrev = false; - return prv_clock_get_month_named_date(buffer, buffer_size, timestamp, abbrev); -} - -size_t clock_get_month_named_abbrev_date(char *buffer, size_t buffer_size, time_t timestamp) { - const bool abbrev = true; - return prv_clock_get_month_named_date(buffer, buffer_size, timestamp, abbrev); -} - -void clock_get_since_time(char *buffer, int buf_size, time_t timestamp) { - const time_t now = rtc_get_time(); - const time_t clamped_timestamp = MIN(now, timestamp); - prv_clock_get_relative_time_string(buffer, buf_size, clamped_timestamp, false, HOURS_PER_DAY, - true); -} - -void clock_get_until_time(char *buffer, int buf_size, time_t timestamp, - int max_relative_hrs) { - prv_clock_get_relative_time_string(buffer, buf_size, timestamp, false, max_relative_hrs, true); -} - -void clock_get_until_time_capitalized(char *buffer, int buf_size, time_t timestamp, - int max_relative_hrs) { - prv_clock_get_relative_time_string(buffer, buf_size, timestamp, true, max_relative_hrs, true); -} - -void clock_get_until_time_without_fulltime(char *buffer, int buf_size, time_t timestamp, - int max_relative_hrs) { - prv_clock_get_relative_time_string(buffer, buf_size, timestamp, true, max_relative_hrs, false); -} - -DEFINE_SYSCALL(void, sys_clock_get_timezone, char *timezone, const size_t buffer_size) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(timezone, TIMEZONE_NAME_LENGTH); - } - clock_get_timezone_region(timezone, buffer_size); -} - -typedef struct daypart_message { - const uint32_t hour_offset; // hours from 12am of current day - const char* const message; // text containing daypart -} daypart_message; - -static const daypart_message daypart_messages[] = { - {0, i18n_noop("this morning")}, // anything before 12pm of the current day - {12, i18n_noop("this afternoon")}, // 12pm today - {18, i18n_noop("this evening")}, // 6pm today - {21, i18n_noop("tonight")}, // 9pm today - {33, i18n_noop("tomorrow morning")}, // 9am tomorrow - {36, i18n_noop("tomorrow afternoon")}, // 12pm tomorrow - {42, i18n_noop("tomorrow evening")}, // 6pm tomorrow - {45, i18n_noop("tomorrow night")}, // 9pm tomorrow - {57, i18n_noop("the day after tomorrow")}, // starting 9am 2 days from now - {72, i18n_noop("the day after tomorrow")}, // ends midnight 2 days from now - {73, i18n_noop("the foreseeable future")}, // Catchall for beyond 3 days -}; - -//! Daypart string is used internally for battery popups -//! and is a minimum threshold, ie. "Powered 'til at least"... -const char *clock_get_relative_daypart_string(time_t current_timestamp, - uint32_t hours_in_the_future) { - struct tm current_tm; - const char *message = NULL; - localtime_r(¤t_timestamp, ¤t_tm); - - // Look for the furthest time in the future that we are "above" - for (int i = ARRAY_LENGTH(daypart_messages) - 1; i >= 0; i--) { - if ((current_tm.tm_hour + hours_in_the_future) >= daypart_messages[i].hour_offset) { - message = daypart_messages[i].message; - break; - } - } - return message; -} - -void clock_hour_and_minute_add(int *hour, int *minute, int delta_minutes) { - const int new_minutes = positive_modulo(*hour * MINUTES_PER_HOUR + *minute + delta_minutes, - MINUTES_PER_DAY); - *hour = new_minutes / MINUTES_PER_HOUR; - *minute = new_minutes % MINUTES_PER_HOUR; -} diff --git a/src/fw/services/common/comm_session/app_session_capabilities.h b/src/fw/services/common/comm_session/app_session_capabilities.h deleted file mode 100644 index 89ac40f762..0000000000 --- a/src/fw/services/common/comm_session/app_session_capabilities.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/comm_session/session.h" -#include "util/uuid.h" - -//! @param capability The capability to check for. -//! @returns True if the session for the current application supports the capability of interest. -//! If the session is currently not connected, it will use cached data. If no cache exists -//! and the session is not connected, false will be returned. -bool comm_session_current_app_session_cache_has_capability(CommSessionCapability capability); - -//! Removes the cached app session capabilities for app with specified uuid. -void comm_session_app_session_capabilities_evict(const Uuid *app_uuid); - -void comm_session_app_session_capabilities_init(void); diff --git a/src/fw/services/common/comm_session/default_kernel_receiver.c b/src/fw/services/common/comm_session/default_kernel_receiver.c deleted file mode 100644 index 983e3cd6e4..0000000000 --- a/src/fw/services/common/comm_session/default_kernel_receiver.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "session_receive_router.h" - -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" - -#include - -//! Default option for the kernel receiver, execute the endpoint handler on KernelBG. -const PebbleTask g_default_kernel_receiver_opt_bg = PebbleTask_KernelBackground; - -//! If the endpoint handler puts events onto the KernelMain queue *and* it is important that -//! PEBBLE_COMM_SESSION_EVENT and your endpoint's events are handled in order, use this -//! receiver option in the protocol_endpoints_table.json: -const PebbleTask g_default_kernel_receiver_opt_main = PebbleTask_KernelMain; - -// A common pattern for endpoint handlers it to: -// 1) Kernel malloc a buffer & copy Pebble Protocol payload to it -// 2) Schedule a callback on KernelBG/Main to run the code that decodes the payload -// (because the handler runs from BT02, a high priority thread -// 3) Free malloc'ed buffer -// Leaving this up to each individual endpoint wastes code and creates more -// opportunity for memory leaks. This file contains an implementation that -// different endpoints can use to achieve this pattern. -// -// Note: Since the buffer is malloc'ed on the kernel heap, the expected consumer -// for this receiver is the system (not an app). However, it might be -// receiving messages *from* a PebbleKit app that the system is supposed -// to handle. For example, app run state commands (i.e. "app launch") are -// sent by PebbleKit apps, but get handled by the system. - -typedef struct { - CommSession *session; - const PebbleProtocolEndpoint *endpoint; - size_t total_payload_size; - int curr_pos; - bool handler_scheduled; - bool should_use_kernel_main; - uint8_t payload[]; -} DefaultReceiverImpl; - -static Receiver *prv_default_kernel_receiver_prepare( - CommSession *session, const PebbleProtocolEndpoint *endpoint, - size_t total_payload_size) { - if (total_payload_size == 0) { - return NULL; // Ignore zero-length messages - } - - size_t size_needed = sizeof(DefaultReceiverImpl) + total_payload_size; - DefaultReceiverImpl *receiver = kernel_zalloc(size_needed); - - if (!receiver) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not allocate receiver, handler:%p size:%d", - endpoint->handler, (int)size_needed); - return NULL; - } - - const bool should_use_kernel_main = - (endpoint->receiver_opt == &g_default_kernel_receiver_opt_main); - *receiver = (DefaultReceiverImpl) { - .session = session, - .endpoint = endpoint, - .total_payload_size = total_payload_size, - .should_use_kernel_main = should_use_kernel_main, - .curr_pos = 0 - }; - - return (Receiver *)receiver; -} - -static void prv_default_kernel_receiver_write( - Receiver *receiver, const uint8_t *data, size_t length) { - DefaultReceiverImpl *impl = (DefaultReceiverImpl *)receiver; - - PBL_ASSERTN((impl->curr_pos + length) <= impl->total_payload_size); - memcpy(impl->payload + impl->curr_pos, data, length); - - impl->curr_pos += length; -} - -static void prv_wipe_receiver_data(DefaultReceiverImpl *receiver) { - *receiver = (DefaultReceiverImpl) { }; -} - -static void prv_default_kernel_receiver_cb(void *data) { - DefaultReceiverImpl *impl = (DefaultReceiverImpl *)data; - PBL_ASSERTN(impl && impl->handler_scheduled && impl->session); - - impl->endpoint->handler(impl->session, impl->payload, impl->total_payload_size); - - prv_wipe_receiver_data(impl); - kernel_free(impl); -} - -static void prv_default_kernel_receiver_finish(Receiver *receiver) { - DefaultReceiverImpl *impl = (DefaultReceiverImpl *)receiver; - impl->handler_scheduled = true; - - if ((int)impl->total_payload_size != impl->curr_pos) { - PBL_LOG(LOG_LEVEL_WARNING, "Got fewer bytes than expected for handler %p", - impl->endpoint->handler); - } - - // Note: At the moment we unconditionally spawn a new callback upon - // completion of each individual payload. If we are getting a flood of - // events, this may generate too many CBs and overflow the queue. We could keep a list - // of pending receiver events and only schedule the CB if there isn't one - // already pending - if (impl->should_use_kernel_main) { - launcher_task_add_callback(prv_default_kernel_receiver_cb, receiver); - } else { - system_task_add_callback(prv_default_kernel_receiver_cb, receiver); - } -} - -static void prv_default_kernel_receiver_cleanup(Receiver *receiver) { - DefaultReceiverImpl *impl = (DefaultReceiverImpl *)receiver; - if (impl->handler_scheduled) { - return; // the kernel BG/main callback will free the data - } - prv_wipe_receiver_data(impl); - kernel_free(impl); -} - -const ReceiverImplementation g_default_kernel_receiver_implementation = { - .prepare = prv_default_kernel_receiver_prepare, - .write = prv_default_kernel_receiver_write, - .finish = prv_default_kernel_receiver_finish, - .cleanup = prv_default_kernel_receiver_cleanup, -}; diff --git a/src/fw/services/common/comm_session/default_kernel_sender.h b/src/fw/services/common/comm_session/default_kernel_sender.h deleted file mode 100644 index 0669f61309..0000000000 --- a/src/fw/services/common/comm_session/default_kernel_sender.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void comm_default_kernel_sender_init(void); diff --git a/src/fw/services/common/comm_session/meta_endpoint.c b/src/fw/services/common/comm_session/meta_endpoint.c deleted file mode 100644 index ff11937314..0000000000 --- a/src/fw/services/common/comm_session/meta_endpoint.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "meta_endpoint.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "util/net.h" - -static const uint16_t META_ENDPOINT_ID = 0; - -static void prv_send_meta_response_kernelbg_cb(void *data) { - MetaResponseInfo *meta_response_info_heap_copy = data; - - // Swap endpoint_id bytes to be Big-Endian: - meta_response_info_heap_copy->payload.endpoint_id = - htons(meta_response_info_heap_copy->payload.endpoint_id); - - uint16_t payload_size; - if (meta_response_info_heap_copy->payload.error_code == MetaResponseCodeCorruptedMessage) { - payload_size = sizeof(meta_response_info_heap_copy->payload.error_code); - } else { - payload_size = sizeof(meta_response_info_heap_copy->payload); - } - - comm_session_send_data(meta_response_info_heap_copy->session, META_ENDPOINT_ID, - (const uint8_t *)&meta_response_info_heap_copy->payload, - payload_size, COMM_SESSION_DEFAULT_TIMEOUT); - - kernel_free(meta_response_info_heap_copy); -} - -void meta_endpoint_send_response_async(const MetaResponseInfo *meta_response_info) { - PBL_LOG(LOG_LEVEL_ERROR, "Meta protocol error: 0x%x (endpoint=%u)", - meta_response_info->payload.error_code, meta_response_info->payload.endpoint_id); - - MetaResponseInfo *meta_response_info_heap_copy = kernel_zalloc_check(sizeof(*meta_response_info)); - memcpy(meta_response_info_heap_copy, meta_response_info, sizeof(*meta_response_info)); - system_task_add_callback(prv_send_meta_response_kernelbg_cb, meta_response_info_heap_copy); -} - -void meta_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { - PBL_LOG(LOG_LEVEL_INFO, "Meta endpoint callback called"); -} diff --git a/src/fw/services/common/comm_session/meta_endpoint.h b/src/fw/services/common/comm_session/meta_endpoint.h deleted file mode 100644 index 15d2f49e7a..0000000000 --- a/src/fw/services/common/comm_session/meta_endpoint.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/comm_session/session.h" -#include "util/attributes.h" - -#include - -typedef enum { - MetaResponseCodeNoError = 0x0, - MetaResponseCodeCorruptedMessage = 0xd0, - MetaResponseCodeDisallowed = 0xdd, - MetaResponseCodeUnhandled = 0xdc, -} MetaResponseCode; - -typedef struct MetaResponseInfo { - CommSession *session; - struct PACKED { - //! @see MetaResponseCode - uint8_t error_code; - uint16_t endpoint_id; - } payload; -} MetaResponseInfo; - -//! Sends out a response for the "meta" endpoint, asynchronously on KernelBG. -//! @note The endpoint_id must be set in Little Endian byte order. This function will take care -//! of swapping it to the correct endianness. -void meta_endpoint_send_response_async(const MetaResponseInfo *meta_response_info); diff --git a/src/fw/services/common/comm_session/protocol.h b/src/fw/services/common/comm_session/protocol.h deleted file mode 100644 index b5af211f7b..0000000000 --- a/src/fw/services/common/comm_session/protocol.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef struct PACKED { - uint16_t length; - uint16_t endpoint_id; -} PebbleProtocolHeader; - -#define COMM_PRIVATE_MAX_INBOUND_PAYLOAD_SIZE 2044 -#define COMM_PUBLIC_MAX_INBOUND_PAYLOAD_SIZE 144 -// TODO: If we have memory to spare, let's crank this up to improve data spooling -#define COMM_MAX_OUTBOUND_PAYLOAD_SIZE 656 diff --git a/src/fw/services/common/comm_session/session_analytics.c b/src/fw/services/common/comm_session/session_analytics.c deleted file mode 100644 index 0bc6354fc3..0000000000 --- a/src/fw/services/common/comm_session/session_analytics.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "session_analytics.h" - -#include "drivers/rtc.h" - -#if MEMFAULT -#include "memfault/metrics/connectivity.h" -#endif -#include "services/common/comm_session/session_internal.h" -#include "services/common/analytics/analytics.h" -#include "services/common/ping.h" -#include "util/time/time.h" - -//! returns the analytic timer id we want to use -static int prv_get_analytic_id_for_session(CommSession *session) { - if (comm_session_analytics_get_transport_type(session) == CommSessionTransportType_PPoGATT) { - return ANALYTICS_DEVICE_METRIC_BT_PEBBLE_PPOGATT_APP_TIME; - } else { - return ANALYTICS_DEVICE_METRIC_BT_PEBBLE_SPP_APP_TIME; - } -} - -CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session) { - return session->transport_imp->get_type(session->transport); -} - -void comm_session_analytics_open_session(CommSession *session) { - const bool is_system = (session->destination != TransportDestinationApp); - if (is_system) { - int analytic_id = prv_get_analytic_id_for_session(session); - analytics_stopwatch_start(analytic_id, AnalyticsClient_System); - analytics_inc(ANALYTICS_DEVICE_METRIC_BT_SYSTEM_SESSION_OPEN_COUNT, AnalyticsClient_System); -#if MEMFAULT - memfault_metrics_connectivity_connected_state_change( - kMemfaultMetricsConnectivityState_Connected); -#endif - } - session->open_ticks = rtc_get_ticks(); -} - -void comm_session_analytics_close_session(CommSession *session, CommSessionCloseReason reason) { - const bool is_system = (session->destination != TransportDestinationApp); - if (is_system) { - int analytic_id = prv_get_analytic_id_for_session(session); - analytics_stopwatch_stop(analytic_id); -#if MEMFAULT - memfault_metrics_connectivity_connected_state_change( - kMemfaultMetricsConnectivityState_ConnectionLost); -#endif - } - - const RtcTicks duration_ticks = (rtc_get_ticks() - session->open_ticks); - const uint16_t duration_mins = ((duration_ticks / RTC_TICKS_HZ) / SECONDS_PER_MINUTE); - const Uuid *optional_app_uuid = comm_session_get_uuid(session); - - analytics_event_session_close(is_system, optional_app_uuid, reason, duration_mins); -} - -//! Increment "bytes sent" counter and perform app ping if due -void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length) { - CommSessionType type = comm_session_get_type(session); - AnalyticsMetric metric; - switch (type) { - case CommSessionTypeSystem: - metric = ANALYTICS_DEVICE_METRIC_BT_PRIVATE_BYTE_OUT_COUNT; - - // We know that bluetooth is already active. If we just sent a message to the Pebble mobile - // app, this is a good time to see if we should send our ping out to it as well. - ping_send_if_due(); - break; - - case CommSessionTypeApp: - metric = ANALYTICS_DEVICE_METRIC_BT_PUBLIC_BYTE_OUT_COUNT; - break; - - case CommSessionTypeInvalid: - default: - return; - } - analytics_add(metric, length, AnalyticsClient_System); -} - -void comm_session_analytics_inc_bytes_received(CommSession *session, uint16_t length) { - const AnalyticsMetric metric = (comm_session_get_type(session) == CommSessionTypeSystem) ? - ANALYTICS_DEVICE_METRIC_BT_PRIVATE_BYTE_IN_COUNT : - ANALYTICS_DEVICE_METRIC_BT_PUBLIC_BYTE_IN_COUNT; - analytics_add(metric, length, AnalyticsClient_System); -} diff --git a/src/fw/services/common/comm_session/session_analytics.h b/src/fw/services/common/comm_session/session_analytics.h deleted file mode 100644 index f79be41d2c..0000000000 --- a/src/fw/services/common/comm_session/session_analytics.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef struct CommSession CommSession; - -typedef enum { - CommSessionCloseReason_UnderlyingDisconnection = 0, - CommSessionCloseReason_ClosedRemotely = 1, - CommSessionCloseReason_ClosedLocally = 2, - CommSessionCloseReason_TransportSpecificBegin = 100, - CommSessionCloseReason_TransportSpecificEnd = 255, -} CommSessionCloseReason; - -typedef enum { - CommSessionTransportType_PlainSPP = 0, - CommSessionTransportType_iAP = 1, - CommSessionTransportType_PPoGATT = 2, - CommSessionTransportType_QEMU = 3, - CommSessionTransportType_PULSE = 4, -} CommSessionTransportType; - -//! Assumes bt_lock() is held by the caller. -CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session); - -void comm_session_analytics_open_session(CommSession *session); - -void comm_session_analytics_close_session(CommSession *session, CommSessionCloseReason reason); - -void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length); - -void comm_session_analytics_inc_bytes_received(CommSession *session, uint16_t length); diff --git a/src/fw/services/common/comm_session/session_internal.h b/src/fw/services/common/comm_session/session_internal.h deleted file mode 100644 index 49edf0287e..0000000000 --- a/src/fw/services/common/comm_session/session_internal.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "drivers/rtc.h" - -#include "session_receive_router.h" -#include "session_transport.h" - -#include "services/common/regular_timer.h" -#include "util/list.h" - -#include - -typedef struct SessionSendQueueJob SessionSendQueueJob; - -//! Data structure representing a Pebble Protocol communication session. -//! There can be multiple. For example, with the iAP transport, the Pebble app has a session and -//! 3rd party apps share another separate session as well. With PPoGATT, the Pebble app has its own -//! session, but each 3rd party app has its own session as well. -typedef struct CommSession { - ListNode node; - - //! The underlying transport responsible for actually sending and receiving the Pebble Protocol - //! data. This can be SPP, iAP (see ispp.c), PPoGATT (see ppogatt.c) or QEMU (qemu_transport.c). - Transport *transport; - - //! Set of function pointers that the session uses to call back to the transport. - const TransportImplementation *transport_imp; - - //! True if a Kernel BG callback has been scheduled to call transport_imp->send_next() - bool is_send_next_call_pending; - - //! True if the session is a system session (connected to the Pebble mobile app). - TransportDestination destination; - - // Extensions supported by the mobile endpoint, see - // https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=491698 - CommSessionCapability protocol_capabilities; - - //! The send queue of this session. See session_send_queue.c - SessionSendQueueJob *send_queue_head; - - ReceiveRouter recv_router; - - //! Absolute number of ticks since session opened. - RtcTicks open_ticks; -} CommSession; diff --git a/src/fw/services/common/comm_session/session_remote_os.h b/src/fw/services/common/comm_session/session_remote_os.h deleted file mode 100644 index 07d31cf0e4..0000000000 --- a/src/fw/services/common/comm_session/session_remote_os.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef enum { - RemoteBitmaskOS = 0x7, // bits 0 - 2 -} RemoteBitmask; - -typedef enum { - RemoteOSUnknown = 0, - RemoteOSiOS = 1, - RemoteOSAndroid = 2, - RemoteOSX = 3, - RemoteOSLinux = 4, - RemoteOSWindows = 5, -} RemoteOS; diff --git a/src/fw/services/common/comm_session/session_remote_version.h b/src/fw/services/common/comm_session/session_remote_version.h deleted file mode 100644 index c5ad7c8b4d..0000000000 --- a/src/fw/services/common/comm_session/session_remote_version.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" -#include -#include - -typedef struct CommSession CommSession; - -// Capabilities are a bitfield set by passing the capabilities character array in -// system_versions.c. The corresponding mobile applications return an integer -// field indicating which endpoints it has support for over the deprecated ones. -typedef struct PACKED { - union { - struct PACKED { - bool run_state_support:1; - bool infinite_log_dumping_support:1; - bool extended_music_service:1; - bool extended_notification_service:1; - bool lang_pack_support:1; - bool app_message_8k_support:1; - bool activity_insights_support:1; - bool voice_api_support:1; - bool send_text_support:1; - bool notification_filtering_support:1; - bool unread_coredump_support:1; - bool weather_app_support:1; - bool reminders_app_support:1; - bool workout_app_support:1; - bool smooth_fw_install_progress_support:1; - // Workaround the fact that we inadvertently injected some padding when we added a 5 bit - // field (PBL-37933) Since the padded bits are all getting 0'ed out today, we are free to use - // them in the future! - uint8_t padded_bits:1; - uint8_t javascript_bytecode_version_appended: 1; - uint8_t more_padded_bits:4; - bool continue_fw_install_across_disconnect_support: 1; - }; - uint64_t flags; - }; -} PebbleProtocolCapabilities; - -void session_remote_version_start_requests(CommSession *session); diff --git a/src/fw/services/common/comm_session/wscript b/src/fw/services/common/comm_session/wscript deleted file mode 100644 index 3b5ff26d00..0000000000 --- a/src/fw/services/common/comm_session/wscript +++ /dev/null @@ -1,97 +0,0 @@ -import json - - -def configure(conf): - pass - - -def build(bld): - in_node = bld.path.find_node('protocol_endpoints_table.json') - out_node = bld.path.get_bld().make_node('protocol_endpoints_table.auto.h') - - def generate_endpoints_table(task): - in_node = task.inputs[0] - out_node = task.outputs[0] - endpoints = [] - definition = {} - with open(in_node.abspath(), 'r') as f_in: - definition.update(json.load(f_in)) - - endpoints.extend(definition['prf_and_normal_fw']) - if bld.variant != 'prf': - endpoints.extend(definition['normal_fw_only']) - endpoints.sort() - - def get_access_enum(access_str): - if access_str == "private": - return "PebbleProtocolAccessPrivate" - elif access_str == "any": - return "PebbleProtocolAccessAny" - elif access_str == "any": - return "PebbleProtocolAccessPublic" - else: - raise ValueError("Unknown value: %s" % access_str) - - with open(out_node.abspath(), 'w') as f_out: - f_out.write("// GENERATED -- DO NOT EDIT\n\n") - f_out.write("#include \"kernel/pebble_tasks.h\"\n\n") - - DEFAULT_SYSTEM_RECV_IMPL = \ - "g_default_kernel_receiver_implementation" - recv_imp_set = set([DEFAULT_SYSTEM_RECV_IMPL]) - - DEFAULT_SYSTEM_RECV_OPT = \ - "g_default_kernel_receiver_opt_bg" - recv_opt_set = set([DEFAULT_SYSTEM_RECV_OPT]) - - for (eid, eid_str, access_str, cb_str, - recv_imp, recv_opt) in endpoints: - if recv_imp: - recv_imp_set.add(recv_imp) - if recv_opt: - recv_opt_set.add(recv_opt) - if cb_str: - f_out.write("extern void {cb_str}(CommSession *session, " - "const uint8_t* data, " - "size_t length);\n".format(cb_str=cb_str)) - - f_out.write("\n\n") - for recv_imp in recv_imp_set: - fmt = "extern ReceiverImplementation {recv_imp};\n" - f_out.write(fmt.format(recv_imp=recv_imp)) - - f_out.write("\n\n") - for recv_opt in recv_opt_set: - fmt = "extern const PebbleTask {recv_opt};\n" - f_out.write(fmt.format(recv_opt=recv_opt)) - - f_out.write("\n\nstatic const PebbleProtocolEndpoint " - "s_protocol_endpoints[] = {\n") - for (eid, eid_str, access_str, cb_str, - recv_imp, recv_opt) in endpoints: - if int(eid_str, base=16) != eid: - raise ValueError("Endpoint IDs need to match: %i vs %s" % - (eid, eid_str)) - if not cb_str: - cb_str = "NULL" - if not recv_imp: - recv_imp = DEFAULT_SYSTEM_RECV_IMPL - if not recv_opt: - recv_opt = DEFAULT_SYSTEM_RECV_OPT - if not recv_opt: - recv_opt = "NULL" - else: - recv_opt = "&" + recv_opt - fmt = (" {{ {eid}, {cb_str}, {access_enum}, &{recv_imp}," - " {recv_opt} }},\n") - f_out.write(fmt.format(eid=eid, - cb_str=cb_str, - access_enum=get_access_enum(access_str), - recv_imp=recv_imp, - recv_opt=recv_opt, - )) - f_out.write("};\n\n") - - bld(rule=generate_endpoints_table, - source=[in_node], - target=out_node) diff --git a/src/fw/services/common/compositor/compositor.c b/src/fw/services/common/compositor/compositor.c deleted file mode 100644 index 16b7de0b26..0000000000 --- a/src/fw/services/common/compositor/compositor.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor.h" -#include "compositor_dma.h" -#include "compositor_display.h" - -#include "applib/graphics/bitblt.h" -#include "applib/graphics/framebuffer.h" -#include "applib/graphics/gcontext.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/animation.h" -#include "applib/ui/animation_private.h" -#include "drivers/display/display.h" -#include "kernel/event_loop.h" -#include "kernel/kernel_applib_state.h" -#include "kernel/ui/kernel_ui.h" -#include "kernel/ui/modals/modal_manager.h" -#include "mcu/cache.h" -#include "popups/timeline/peek.h" -#include "process_management/app_manager.h" -#include "process_management/process_manager.h" -#include "process_state/app_state/app_state.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/profiler.h" -#include "util/size.h" - -// The number of pixels for a given row which get set to black to round the corner. These numbers -// are for the top-left corner, but can easily be translated to the other corners. This is used by -// bezel mode to replicate the behavior of the FPGA. -#if PBL_COLOR -static const uint32_t s_rounded_corner_width[6] = { 6, 4, 3, 2, 1, 1 }; -#endif - -//! This is our root framebuffer that everything gets composited into. -static FrameBuffer DMA_BSS s_framebuffer; - -typedef enum { - //! Render the app with no transparent modals straight through - CompositorState_App, - //! Render the opaque modal straight through - CompositorState_Modal, - //! Render the app with transparent modals straight through - CompositorState_AppAndModal, - //!< Waiting for the app to render itself so we can start the transition - CompositorState_AppTransitionPending, - //!< Compositor is running a transition animation - CompositorState_Transitioning, -} CompositorState; - -//! Deferred render struct is used to handle a render event initiated while a display update is in -//! progress and the update is non-blocking on the platform (ie. snowy/bobby smiles). -typedef struct { - struct { - bool pending; - AnimationProgress progress; - } animation; - struct { - bool pending; - } transition_complete; - struct { - bool pending; - } app; - struct { - bool pending; - const CompositorTransition *compositor_animation; - } transition_start; -} DeferredRender; - -typedef struct { - Animation *animation; - const CompositorTransition *impl; - GPoint modal_offset; -} CompositorTransitionState; - -static CompositorState s_state; - -static DeferredRender s_deferred_render; - -static CompositorTransitionState s_animation_state; - -static bool s_framebuffer_frozen; - -//! Animation .update function for the AnimationImplementation we use to drive our transitions. -//! Wraps the .update function of the current CompositorTransition. -static void prv_animation_update(Animation *animation, const AnimationProgress distance_normalized); - -//! Call this function whenever a transition completes to change the state to one of the stable -//! states (CompositorState_App or CompositorState_Modal). -static void prv_finish_transition(void); - -void compositor_init(void) { -#if CAPABILITY_COMPOSITOR_USES_DMA && !TARGET_QEMU && !UNITTEST - compositor_dma_init(); -#endif - - const GSize fb_size = GSize(DISP_COLS, DISP_ROWS); - framebuffer_init(&s_framebuffer, &fb_size); - framebuffer_clear(&s_framebuffer); - - s_state = CompositorState_App; - - s_deferred_render = (DeferredRender) { - .animation.pending = false, - .app.pending = false - }; - - s_animation_state = (CompositorTransitionState) { 0 }; - - s_framebuffer_frozen = false; -} - -// Helper functions to make implementing transitions easier -/////////////////////////////////////////////////////////// - -void compositor_app_framebuffer_fill_callback(GContext *ctx, int16_t y, - Fixed_S16_3 x_range_begin, Fixed_S16_3 x_range_end, - Fixed_S16_3 delta_begin, Fixed_S16_3 delta_end, - void *user_data) { - const GPoint *offset = user_data ?: &GPointZero; // User data has left the building - - GBitmap src_bitmap = compositor_get_app_framebuffer_as_bitmap(); - src_bitmap.bounds = GRect(x_range_begin.integer - offset->x, y - offset->y, - x_range_end.integer - x_range_begin.integer, 1); - - GBitmap dest_bitmap = compositor_get_framebuffer_as_bitmap(); - - bitblt_bitmap_into_bitmap(&dest_bitmap, &src_bitmap, GPoint(x_range_begin.integer, y), - GCompOpAssign, GColorWhite); -} - -#if PBL_COLOR -static int prv_get_rounded_corner_width(int row_index, int num_rows) { - if (row_index >= num_rows) { - return 0; - } - if (row_index < (int)ARRAY_LENGTH(s_rounded_corner_width)) { - return s_rounded_corner_width[row_index]; - } else if (num_rows - row_index - 1 < (int)ARRAY_LENGTH(s_rounded_corner_width)) { - return s_rounded_corner_width[num_rows - row_index - 1]; - } - return 0; -} -#endif - -void compositor_set_modal_transition_offset(GPoint modal_offset) { - s_animation_state.modal_offset = modal_offset; -} - -void compositor_render_app(void) { - PBL_ASSERT_TASK(PebbleTask_KernelMain); - - PROFILER_NODE_START(compositor); - - // Don't trust the size field within the app framebuffer as the app could modify it. - GSize app_framebuffer_size; - app_manager_get_framebuffer_size(&app_framebuffer_size); - - const FrameBuffer *app_framebuffer = app_state_get_framebuffer(); - - if (gsize_equal(&app_framebuffer_size, &s_framebuffer.size)) { -#if CAPABILITY_COMPOSITOR_USES_DMA && !TARGET_QEMU && !UNITTEST - compositor_dma_run(s_framebuffer.buffer, app_framebuffer->buffer, FRAMEBUFFER_SIZE_BYTES); -#else - GBitmap src_bitmap = compositor_get_app_framebuffer_as_bitmap(); - GBitmap dest_bitmap = compositor_get_framebuffer_as_bitmap(); - - bitblt_bitmap_into_bitmap(&dest_bitmap, &src_bitmap, GPointZero, GCompOpAssign, GColorWhite); -#endif - } else { -#if PBL_COLOR - // On Robert, we support running older apps which have a smaller framebuffer in "bezel mode" - // where we center them and draw a black bezel around them. Using memset to set the bezel to - // black and using memcpy to copy the app framebuffer into the center is the fastest method - // (significantly faster than DMA even). We only support the app framebuffer being smaller than - // the system framebuffer and we assume the system framebuffer is always DISP_COLS x DISP_ROWS. - - const int16_t app_width = app_framebuffer_size.w; - const int16_t app_height = app_framebuffer_size.h; - const int16_t bezel_width = (DISP_COLS - app_width) / 2; - const int16_t bezel_height = (DISP_ROWS - app_height) / 2; - const int16_t app_peek_offset_y = timeline_peek_get_origin_y() - app_height; - const int16_t app_offset_y = CLIP(app_peek_offset_y, 0, bezel_height); - PBL_ASSERTN((bezel_width > 0) && (bezel_height > 0)); - - uint8_t *dst = (uint8_t *)s_framebuffer.buffer; - uint8_t *app_buffer = (uint8_t *)app_framebuffer->buffer; - - // Set all the black pixels from the start, which is the sum of the following: - // - app_offset_y * DISP_COLS - the top part of the bezel - // - bezel_width - the left bezel for the first row of the app - // - corner_pixels - the top-left corner for the first row - const int top_bezel_length = - app_offset_y * DISP_COLS + bezel_width + s_rounded_corner_width[0]; - memset(dst, GColorBlack.argb, top_bezel_length); - dst += top_bezel_length; - - // Starting from the origin for the app (bezel_width, bezel_height), copy one row of the app - // framebuffer and set two bezel_width's worth of pixels to black. This will set the right-most - // bezel pixels of the current row to black, and the left-most bezel pixels of the next row to - // black. - int corner_width = prv_get_rounded_corner_width(0, app_height); - for (int app_row = 0; app_row < app_height; ++app_row) { - const int row_width = app_width - corner_width * 2; - // Copy the row of the app framebuffer (advance past the corner pixels on the left) - const uint8_t *src = &app_buffer[app_row * app_width + corner_width]; - memcpy(dst, src, row_width); - dst += row_width; - - // Set the right-side corner and bezel of this row and left-size corner and bezel of the next. - const int next_corner_width = prv_get_rounded_corner_width(app_row + 1, app_height); - const int bezel_length = corner_width + bezel_width * 2 + next_corner_width; - memset(dst, GColorBlack.argb, bezel_length); - dst += bezel_length; - corner_width = next_corner_width; - } - - // Set the remaining pixels to black. - size_t framebuffer_size = framebuffer_get_size_bytes(&s_framebuffer); - const int bottom_bezel_length = (uintptr_t)&s_framebuffer.buffer[framebuffer_size] - - (uintptr_t)dst; - memset(dst, GColorBlack.argb, bottom_bezel_length); -#endif - } - - if (s_state == CompositorState_AppAndModal) { - compositor_render_modal(); - } - - PROFILER_NODE_STOP(compositor); - - framebuffer_dirty_all(&s_framebuffer); -} - -void compositor_render_modal(void) { - - GContext *ctx = kernel_ui_get_graphics_context(); - - // We make this GDrawState static to save stack space, thus the declaration and init must be - // performed on two separate lines because the initializer value is not constant - static GDrawState prev_state; - prev_state = ctx->draw_state; - - gpoint_add_eq(&ctx->draw_state.drawing_box.origin, s_animation_state.modal_offset); - - modal_manager_render(ctx); - - ctx->draw_state = prev_state; -} - -// Compositor implementation -/////////////////////////////////////////////////////////// - -T_STATIC void prv_handle_display_update_complete(void) { - if (s_deferred_render.transition_complete.pending) { - s_deferred_render.transition_complete.pending = false; - prv_finish_transition(); - } - if (s_deferred_render.animation.pending) { - s_deferred_render.animation.pending = false; - prv_animation_update(s_animation_state.animation, s_deferred_render.animation.progress); - } - if (s_deferred_render.app.pending) { - s_deferred_render.app.pending = false; - compositor_app_render_ready(); - } - if (s_deferred_render.transition_start.pending) { - s_deferred_render.transition_start.pending = false; - compositor_transition(s_deferred_render.transition_start.compositor_animation); - } -} - -static void prv_compositor_flush(void) { - PBL_ASSERT_TASK(PebbleTask_KernelMain); - - // Stop the framebuffer_prepare performance timer. This timer was started when the client - // first posted the render event to the system. - compositor_display_update(prv_handle_display_update_complete); -} - -static void prv_send_did_focus_event(bool in_focus) { - PebbleEvent event = { - .type = PEBBLE_APP_DID_CHANGE_FOCUS_EVENT, - .app_focus = { - .in_focus = in_focus, - }, - }; - event_put(&event); -} - -static bool prv_should_render(void) { - return !(compositor_display_update_in_progress() || s_framebuffer_frozen); -} - -static void prv_release_app_framebuffer(void) { - // Inform the app that the render is complete and it is safe to write into its framebuffer again. - PebbleEvent event = { - .type = PEBBLE_RENDER_FINISHED_EVENT, - }; - process_manager_send_event_to_process(PebbleTask_App, &event); -} - -void compositor_app_render_ready(void) { - if (!prv_should_render()) { - s_deferred_render.app.pending = true; - return; - } - - if (s_state == CompositorState_AppTransitionPending) { - // Huzzah, the app sent us the first frame! - if (s_animation_state.animation) { - // We have an animation to run, run it. - s_state = CompositorState_Transitioning; - animation_schedule(s_animation_state.animation); - - // Don't release the app framebuffer yet, we'll do this once the transition completes. This - // way the app won't update its frame buffer while we're transitioning to it. - return; - } else { - // No animation was used, immediately say that the app is now fully focused. - const ModalProperty properties = modal_manager_get_properties(); - s_state = ((properties & ModalProperty_Exists) && (properties & ModalProperty_Transparent)) ? - CompositorState_AppAndModal : CompositorState_App; - prv_send_did_focus_event(true); - } - } - - // Draw the app framebuffer if in the App state - if (s_state == CompositorState_App || s_state == CompositorState_AppAndModal) { - // compositor_render_app also renders modals if the CompositorState_AppAndModal as that state - // indicates that there are transparent modals that allow the app framebuffer to show through - compositor_render_app(); - prv_compositor_flush(); - } - - // Draw the modal if in the Modal state - if (s_state == CompositorState_Modal) { - compositor_render_modal(); - prv_compositor_flush(); - } - - prv_release_app_framebuffer(); -} - -static void prv_send_app_render_request(void) { - PebbleEvent event = { - .type = PEBBLE_RENDER_REQUEST_EVENT, - }; - process_manager_send_event_to_process(PebbleTask_App, &event); -} - -void compositor_modal_render_ready(void) { - if ((s_state == CompositorState_Transitioning) || !prv_should_render()) { - // Don't let the modal redraw itself when the redraw loop is being currently driven by an - // animation or if a display update is in progress. - return; - } - - if ((s_state == CompositorState_AppTransitionPending) && - (modal_manager_get_properties() & ModalProperty_Transparent)) { - // Don't render if modals are transparent while the app is not ready yet - return; - } - - if (s_state == CompositorState_Modal) { - compositor_render_modal(); - prv_compositor_flush(); - } else if (s_state == CompositorState_AppAndModal) { - prv_send_app_render_request(); - } -} - -void compositor_transition_render(CompositorTransitionUpdateFunc func, Animation *animation, - const AnimationProgress distance_normalized) { - if (!prv_should_render()) { - if (!s_deferred_render.transition_complete.pending) { - s_deferred_render.animation.pending = true; - s_deferred_render.animation.progress = distance_normalized; - } - return; - } - GContext *ctx = kernel_ui_get_graphics_context(); - - // Save the draw state in a static to save stack space - static GDrawState prev_state; - prev_state = ctx->draw_state; - - func(ctx, animation, distance_normalized); - - ctx->draw_state = prev_state; - - if (!s_animation_state.impl->skip_modal_render_after_update) { - compositor_render_modal(); - } - - prv_compositor_flush(); -} - -static void prv_animation_update(Animation *animation, - const AnimationProgress distance_normalized) { - PBL_ASSERT_TASK(PebbleTask_KernelMain); - // Since we might be running this animation update as part of a deferred render, we must - // update the kernel animation state's .current_animation to point to this animation; - // otherwise if the animation specified any custom spacial interpolation (e.g. moook), it would - // be ignored - AnimationPrivate *animation_private = animation_private_animation_find(animation); - AnimationState *kernel_animation_state = kernel_applib_get_animation_state(); - PBL_ASSERTN(animation_private && kernel_animation_state && kernel_animation_state->aux); - AnimationPrivate *saved_current_animation = kernel_animation_state->aux->current_animation; - - kernel_animation_state->aux->current_animation = animation_private; - compositor_transition_render(s_animation_state.impl->update, animation, distance_normalized); - kernel_animation_state->aux->current_animation = saved_current_animation; -} - -static void prv_finish_transition(void) { - const ModalProperty properties = modal_manager_get_properties(); - if (properties & ModalProperty_Exists) { - s_state = (properties & ModalProperty_Transparent) ? CompositorState_AppAndModal : - CompositorState_Modal; - compositor_modal_render_ready(); - - // Force the app framebuffer to be released. We hold it during transitions to keep the app - // framebuffer from changing while it's being animated but now that we're done we want to make - // sure it's always available to the app. This is only needed when we're finishing to a modal - // since compositor_app_render_ready will also release the framebuffer. - prv_release_app_framebuffer(); - } else { - s_state = CompositorState_App; - compositor_app_render_ready(); - } - - prv_send_did_focus_event(properties & ModalProperty_Unfocused); -} - -static void prv_animation_teardown(Animation *animation) { - if (s_animation_state.impl->teardown) { - s_animation_state.impl->teardown(animation); - } - s_animation_state = (CompositorTransitionState) { 0 }; - - s_deferred_render.animation.pending = false; - if (!prv_should_render()) { - s_deferred_render.transition_complete.pending = true; - return; - } - - prv_finish_transition(); -} - -void compositor_transition(const CompositorTransition *compositor_animation) { - if (s_animation_state.animation != NULL) { - PBL_LOG(LOG_LEVEL_DEBUG, "Animation <%u> in progress, cancelling", - (int) s_animation_state.animation); - - animation_destroy(s_animation_state.animation); - s_animation_state = (CompositorTransitionState) { 0 }; - - s_deferred_render.animation.pending = false; - s_deferred_render.transition_complete.pending = false; - } - - if (!prv_should_render() || s_deferred_render.animation.pending) { - if (s_deferred_render.app.pending) { - s_deferred_render.app.pending = false; - prv_release_app_framebuffer(); - } - - s_deferred_render.transition_start.pending = true; - s_deferred_render.transition_start.compositor_animation = compositor_animation; - return; - } - - if (compositor_animation) { - // Set up our animation state and schedule it - - s_animation_state = (CompositorTransitionState) { - .animation = animation_create(), - .impl = compositor_animation - }; - - static const AnimationImplementation s_compositor_animation_impl = { - .update = prv_animation_update, - .teardown = prv_animation_teardown, - }; - animation_set_implementation(s_animation_state.animation, &s_compositor_animation_impl); - - compositor_animation->init(s_animation_state.animation); - } - - const ModalProperty properties = modal_manager_get_properties(); - const bool is_modal_existing = (properties & ModalProperty_Exists); - const bool is_modal_transparent = (properties & ModalProperty_Transparent); - if (((s_state == CompositorState_Modal) && !is_modal_existing) || is_modal_transparent) { - // Modal to App or Any to Transparent Modal - - // We can't say for sure whether or not the app framebuffer is in a reasonable state, as the - // app could be redrawing itself right now. Since we can't query this, instead trigger the - // app to redraw itself. This way we will cause an PEBBLE_RENDER_READY_EVENT in the very near - // future, regardless of the app's state. - prv_send_app_render_request(); - - // Now wait for the ready event. - s_state = CompositorState_AppTransitionPending; - - } else if (is_modal_existing && !is_modal_transparent) { - // Modal to Modal or App to Modal - - // We can start animating immediately if we're going to a modal window. This is because - // modal window content is drawn on demand so it's always available. - if (compositor_animation) { - s_state = CompositorState_Transitioning; - animation_schedule(s_animation_state.animation); - } else { - prv_finish_transition(); - } - - } else { - // App to App - - // We have to wait for the app to populate its framebuffer - s_state = CompositorState_AppTransitionPending; - } -} - -FrameBuffer *compositor_get_framebuffer(void) { - return &s_framebuffer; -} - -GBitmap compositor_get_framebuffer_as_bitmap(void) { - return framebuffer_get_as_bitmap(&s_framebuffer, &s_framebuffer.size); -} - -GBitmap compositor_get_app_framebuffer_as_bitmap(void) { - // Get the app framebuffer state based on the size it should be to prevent a malicious app from - // changing it and causing issues. - GSize app_framebuffer_size; - app_manager_get_framebuffer_size(&app_framebuffer_size); - return framebuffer_get_as_bitmap(app_state_get_framebuffer(), &app_framebuffer_size); -} - -bool compositor_is_animating(void) { - return s_state == CompositorState_AppTransitionPending || - s_state == CompositorState_Transitioning; -} - -void compositor_transition_cancel(void) { - if (animation_is_scheduled(s_animation_state.animation)) { - animation_unschedule(s_animation_state.animation); - } -} - -void compositor_freeze(void) { - s_framebuffer_frozen = true; -} - -static void prv_compositor_unfreeze_cb(void *ignored) { - // Run deferred draws - prv_handle_display_update_complete(); -} - -void compositor_unfreeze(void) { - s_framebuffer_frozen = false; - - launcher_task_add_callback(prv_compositor_unfreeze_cb, NULL); -} diff --git a/src/fw/services/common/compositor/compositor_display.c b/src/fw/services/common/compositor/compositor_display.c deleted file mode 100644 index cec971f764..0000000000 --- a/src/fw/services/common/compositor/compositor_display.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor.h" - -#include "applib/graphics/framebuffer.h" -#include "applib/graphics/gtypes.h" -#include "util/bitset.h" -#include "util/math.h" -#include "util/size.h" - -#include - -//! This variable is used when we are flushing s_framebuffer out to the display driver. -//! It's set to the current row index that we are DMA'ing out to the display. -static uint8_t s_current_flush_line; - -static void (*s_update_complete_handler)(void); - -#if PLATFORM_SILK || PLATFORM_ASTERIX -static const uint8_t s_corner_shape[] = { 3, 1, 1 }; -static uint8_t s_line_buffer[FRAMEBUFFER_BYTES_PER_ROW]; -#endif - -//! display_update get next line callback -static bool prv_flush_get_next_line_cb(DisplayRow* row) { - FrameBuffer *fb = compositor_get_framebuffer(); - - s_current_flush_line = MAX(s_current_flush_line, fb->dirty_rect.origin.y); - const uint8_t y_end = fb->dirty_rect.origin.y + fb->dirty_rect.size.h; - if (s_current_flush_line < y_end) { - row->address = s_current_flush_line; - void *fb_line = framebuffer_get_line(fb, s_current_flush_line); -#if PLATFORM_SILK || PLATFORM_ASTERIX - // Draw rounded corners onto the screen without modifying the - // system framebuffer. - if (s_current_flush_line < ARRAY_LENGTH(s_corner_shape) || - s_current_flush_line >= DISP_ROWS - ARRAY_LENGTH(s_corner_shape)) { - memcpy(s_line_buffer, fb_line, FRAMEBUFFER_BYTES_PER_ROW); - uint8_t corner_idx = - (s_current_flush_line < ARRAY_LENGTH(s_corner_shape))? - s_current_flush_line : DISP_ROWS - s_current_flush_line - 1; - uint8_t corner_width = s_corner_shape[corner_idx]; - for (uint8_t pixel = 0; pixel < corner_width; ++pixel) { - bitset8_clear(s_line_buffer, pixel); - bitset8_clear(s_line_buffer, DISP_COLS - pixel - 1); - } - row->data = s_line_buffer; - } else { - row->data = fb_line; - } -#else - row->data = fb_line; -#endif - s_current_flush_line++; - return true; - } - - return false; -} - -//! display_update complete callback -static void prv_flush_complete_cb(void) { - s_current_flush_line = 0; - framebuffer_reset_dirty(compositor_get_framebuffer()); - - if (s_update_complete_handler) { - s_update_complete_handler(); - } -} - -void compositor_display_update(void (*handle_update_complete_cb)(void)) { - if (!framebuffer_is_dirty(compositor_get_framebuffer())) { - return; - } - s_update_complete_handler = handle_update_complete_cb; - s_current_flush_line = 0; - - display_update(&prv_flush_get_next_line_cb, &prv_flush_complete_cb); -} - -bool compositor_display_update_in_progress(void) { - return display_update_in_progress(); -} - diff --git a/src/fw/services/common/compositor/compositor_display.h b/src/fw/services/common/compositor/compositor_display.h deleted file mode 100644 index b60d1797f5..0000000000 --- a/src/fw/services/common/compositor/compositor_display.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! @file compositor_display.h -//! -//! This module handles copying the framebuffer content to the display driver. - -void compositor_display_update(void (*handle_update_complete_cb)(void)); - -bool compositor_display_update_in_progress(void); diff --git a/src/fw/services/common/compositor/compositor_dma.c b/src/fw/services/common/compositor/compositor_dma.c deleted file mode 100644 index a8a1a64a7a..0000000000 --- a/src/fw/services/common/compositor/compositor_dma.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "board/board.h" -#include "drivers/dma.h" -#include "kernel/util/stop.h" -#include "system/logging.h" - -#include "FreeRTOS.h" -#include "semphr.h" - -#if CAPABILITY_COMPOSITOR_USES_DMA && !TARGET_QEMU && !UNITTEST - -static SemaphoreHandle_t s_dma_in_progress; - -void compositor_dma_init(void) { - s_dma_in_progress = xSemaphoreCreateBinary(); - dma_request_init(COMPOSITOR_DMA); -} - -static bool prv_dma_complete_handler(DMARequest *transfer, void *context) { - signed portBASE_TYPE should_context_switch = false; - xSemaphoreGiveFromISR(s_dma_in_progress, &should_context_switch); - return should_context_switch != pdFALSE; -} - -void compositor_dma_run(void *to, const void *from, uint32_t size) { - stop_mode_disable(InhibitorCompositor); - dma_request_start_direct(COMPOSITOR_DMA, to, from, size, prv_dma_complete_handler, NULL); - - if (xSemaphoreTake(s_dma_in_progress, 10) != pdTRUE) { - PBL_LOG_SYNC(LOG_LEVEL_ERROR, "DMA Compositing never completed."); - // TODO: This should never be hit, but do we want to queue up a new render - // event so that there is no visible breakage in low-fps situations? - dma_request_stop(COMPOSITOR_DMA); - } - stop_mode_enable(InhibitorCompositor); -} - -#endif // CAPABILITY_COMPOSITOR_USES_DMA && !TARGET_QEMU && !UNITTEST diff --git a/src/fw/services/common/compositor/compositor_dma.h b/src/fw/services/common/compositor/compositor_dma.h deleted file mode 100644 index 99c8f20a59..0000000000 --- a/src/fw/services/common/compositor/compositor_dma.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void compositor_dma_init(void); -void compositor_dma_run(void *to, const void *from, uint32_t size); diff --git a/src/fw/services/common/compositor/compositor_private.h b/src/fw/services/common/compositor/compositor_private.h deleted file mode 100644 index fd005cfe56..0000000000 --- a/src/fw/services/common/compositor/compositor_private.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/graphics.h" - -//! @file compositor_private.h -//! -//! Useful helpful function to help out implementing compositor animations - -//! Trigger the app framebuffer to be copied to the system framebuffer -void compositor_render_app(void); - -//! Trigger the modal window to be rendered to the system framebuffer -void compositor_render_modal(void); - -//! A GPathDrawFilledCallback that can be used to fill pixels with the app's framebuffer -void compositor_app_framebuffer_fill_callback(GContext *ctx, int16_t y, - Fixed_S16_3 x_range_begin, Fixed_S16_3 x_range_end, - Fixed_S16_3 delta_begin, Fixed_S16_3 delta_end, - void *user_data); diff --git a/src/fw/services/common/compositor/default/compositor_dot_transitions.h b/src/fw/services/common/compositor/default/compositor_dot_transitions.h deleted file mode 100644 index ff71a85f7e..0000000000 --- a/src/fw/services/common/compositor/default/compositor_dot_transitions.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -// These numbers approximate the visuals shown in the videos from the design team -#define STATIC_DOT_ANIMATION_DURATION_MS 233 -#define DOT_ANIMATION_STROKE_WIDTH 12 - -void compositor_dot_transitions_collapsing_ring_animation_update(GContext *ctx, - uint32_t distance_normalized, - GColor outer_ring_color, - GColor inner_ring_color); - -const CompositorTransition* compositor_dot_transition_timeline_get(bool timeline_is_future, - bool timeline_is_destination); - -const CompositorTransition* compositor_dot_transition_app_fetch_get(void); diff --git a/src/fw/services/common/compositor/default/compositor_launcher_app_transitions.h b/src/fw/services/common/compositor/default/compositor_launcher_app_transitions.h deleted file mode 100644 index e3b0b448f5..0000000000 --- a/src/fw/services/common/compositor/default/compositor_launcher_app_transitions.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -#include "apps/system_apps/launcher/launcher_app.h" - -//! @file compositor_launcher_app_transitions.h -//! Allows a user to create and configure compositor transition animations between the launcher -//! and apps. - -//! @param app_is_destination Whether the animation should reflect the app as the destination -//! @return \ref CompositorTransition for the resulting animation -const CompositorTransition *compositor_launcher_app_transition_get(bool app_is_destination); diff --git a/src/fw/services/common/compositor/default/compositor_modal_transitions.h b/src/fw/services/common/compositor/default/compositor_modal_transitions.h deleted file mode 100644 index 4695a744c8..0000000000 --- a/src/fw/services/common/compositor/default/compositor_modal_transitions.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -//! @file compositor_modal_transitions.h -//! Allows a user to create and configure compositor transition animations for modals. - -//! @param modal_is_destination Whether the animation should animate to the modal or not -//! @return \ref CompositorTransition for the requested modal animation -const CompositorTransition* compositor_modal_transition_to_modal_get(bool modal_is_destination); diff --git a/src/fw/services/common/compositor/default/compositor_peek_transitions.c b/src/fw/services/common/compositor/default/compositor_peek_transitions.c deleted file mode 100644 index 07ad1dda15..0000000000 --- a/src/fw/services/common/compositor/default/compositor_peek_transitions.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_peek_transitions.h" - -#include "applib/graphics/bitblt.h" -#include "applib/graphics/framebuffer.h" -#include "applib/graphics/gtypes.h" -#include "applib/ui/animation_interpolate.h" -#include "apps/system_apps/timeline/timeline_common.h" -#include "popups/timeline/peek.h" -#include "popups/timeline/peek_animations.h" -#include "services/common/compositor/compositor_private.h" -#include "services/common/compositor/compositor_transitions.h" - -#define NUM_FRAMES (3) - -typedef struct CompositorPeekTransitionData { - int offset_y; -} CompositorPeekTransitionData; - -static CompositorPeekTransitionData s_data; - -static void prv_update_peek_transition_animation(GContext *ctx, Animation *animation, - uint32_t distance_normalized) { - const AnimationProgress progress = distance_normalized; - - GRect box = TIMELINE_PEEK_FRAME_VISIBLE; - const int16_t initial_offset_y = -4; - const int16_t final_offset_y = 7; - box.origin.y = interpolate_int16(progress, box.origin.y + initial_offset_y, final_offset_y); - - if (progress > (ANIMATION_NORMALIZED_MAX / NUM_FRAMES)) { - timeline_peek_draw_background(ctx, &DISP_FRAME, 0); - peek_animations_draw_compositor_background_speed_lines( - ctx, GPoint(PEEK_ANIMATIONS_SPEED_LINES_OFFSET_X, 0)); - } - const unsigned int num_concurrent = 3; - timeline_peek_draw_background(ctx, &box, num_concurrent); - - const unsigned int concurrent_height = timeline_peek_get_concurrent_height(num_concurrent); - const int16_t foreground_speed_line_offset_y = 2; - const GPoint offset = - gpoint_add(box.origin, GPoint(PEEK_ANIMATIONS_SPEED_LINES_OFFSET_X, - concurrent_height + foreground_speed_line_offset_y)); - graphics_context_set_fill_color(ctx, GColorBlack); - peek_animations_draw_compositor_foreground_speed_lines(ctx, offset); -} - -static void prv_init_peek_transition_animation(Animation *animation) { - animation_set_curve(animation, AnimationCurveLinear); - animation_set_duration(animation, NUM_FRAMES * ANIMATION_TARGET_FRAME_INTERVAL_MS); -} - -const CompositorTransition *compositor_peek_transition_timeline_get(void) { - s_data = (CompositorPeekTransitionData) {}; - static const CompositorTransition s_impl = { - .init = prv_init_peek_transition_animation, - .update = prv_update_peek_transition_animation, - }; - return &s_impl; -} diff --git a/src/fw/services/common/compositor/default/compositor_peek_transitions.h b/src/fw/services/common/compositor/default/compositor_peek_transitions.h deleted file mode 100644 index a62c59b522..0000000000 --- a/src/fw/services/common/compositor/default/compositor_peek_transitions.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor_transitions.h" - -const CompositorTransition *compositor_peek_transition_timeline_get(void); diff --git a/src/fw/services/common/compositor/default/compositor_port_hole_transitions.h b/src/fw/services/common/compositor/default/compositor_port_hole_transitions.h deleted file mode 100644 index 1f310d8aa8..0000000000 --- a/src/fw/services/common/compositor/default/compositor_port_hole_transitions.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -#define PORT_HOLE_TRANSITION_DURATION_MS (6 * ANIMATION_TARGET_FRAME_INTERVAL_MS) - -const CompositorTransition *compositor_port_hole_transition_app_get( - CompositorTransitionDirection direction); - -void compositor_port_hole_transition_draw_outer_ring(GContext *ctx, int16_t pixels, - GColor ring_color); diff --git a/src/fw/services/common/compositor/default/compositor_round_flip_transitions.h b/src/fw/services/common/compositor/default/compositor_round_flip_transitions.h deleted file mode 100644 index d70ce1effd..0000000000 --- a/src/fw/services/common/compositor/default/compositor_round_flip_transitions.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -// Animation in design video lasts this many frames -#define ROUND_FLIP_ANIMATION_DURATION_MS (6 * ANIMATION_TARGET_FRAME_INTERVAL_MS) - -void compositor_round_flip_transitions_flip_animation_update(GContext *ctx, - uint32_t distance_normalized, - CompositorTransitionDirection dir, - GColor flip_lid_color); - -const CompositorTransition *compositor_round_flip_transition_get(bool flip_to_the_right); diff --git a/src/fw/services/common/compositor/default/compositor_shutter_transitions.h b/src/fw/services/common/compositor/default/compositor_shutter_transitions.h deleted file mode 100644 index 05a4be8a16..0000000000 --- a/src/fw/services/common/compositor/default/compositor_shutter_transitions.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -// The length of first "section" of the animation, where the old app is moved off of the screen. -#define SHUTTER_TRANSITION_FIRST_DURATION_MS (2 * ANIMATION_TARGET_FRAME_INTERVAL_MS) -// The length of second "section" of the animation, where the new app is moved in. -#define SHUTTER_TRANSITION_SECOND_DURATION_MS (4 * ANIMATION_TARGET_FRAME_INTERVAL_MS) -// Total length of the animation. -#define SHUTTER_TRANSITION_DURATION_MS (SHUTTER_TRANSITION_FIRST_DURATION_MS + \ - SHUTTER_TRANSITION_SECOND_DURATION_MS) - -const CompositorTransition *compositor_shutter_transition_get( - CompositorTransitionDirection direction, GColor color); diff --git a/src/fw/services/common/compositor/default/compositor_slide_transitions.h b/src/fw/services/common/compositor/default/compositor_slide_transitions.h deleted file mode 100644 index 57d2507f16..0000000000 --- a/src/fw/services/common/compositor/default/compositor_slide_transitions.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -const CompositorTransition *compositor_slide_transition_timeline_get(bool timeline_is_future, - bool timeline_is_destination, - bool timeline_is_empty); diff --git a/src/fw/services/common/compositor/legacy/compositor_app_slide_transitions.h b/src/fw/services/common/compositor/legacy/compositor_app_slide_transitions.h deleted file mode 100644 index fa6f6e961b..0000000000 --- a/src/fw/services/common/compositor/legacy/compositor_app_slide_transitions.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -void compositor_app_slide_transitions_animation_update(GContext *ctx, - uint32_t distance_normalized, - CompositorTransitionDirection dir); - -const CompositorTransition *compositor_app_slide_transition_get(bool flip_to_the_right); diff --git a/src/fw/services/common/compositor/legacy/compositor_modal_slide_transitions.h b/src/fw/services/common/compositor/legacy/compositor_modal_slide_transitions.h deleted file mode 100644 index 4695a744c8..0000000000 --- a/src/fw/services/common/compositor/legacy/compositor_modal_slide_transitions.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/compositor/compositor.h" - -//! @file compositor_modal_transitions.h -//! Allows a user to create and configure compositor transition animations for modals. - -//! @param modal_is_destination Whether the animation should animate to the modal or not -//! @return \ref CompositorTransition for the requested modal animation -const CompositorTransition* compositor_modal_transition_to_modal_get(bool modal_is_destination); diff --git a/src/fw/services/common/compositor/screenshot_pp.h b/src/fw/services/common/compositor/screenshot_pp.h deleted file mode 100644 index 111a373350..0000000000 --- a/src/fw/services/common/compositor/screenshot_pp.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "services/common/comm_session/session.h" - -//! Callback for handling a screenshot request message from the client -void screenshot_protocol_msg_callback(CommSession *session, const uint8_t* data, unsigned int length); diff --git a/src/fw/services/common/cron.c b/src/fw/services/common/cron.c deleted file mode 100644 index 6e7a911aa6..0000000000 --- a/src/fw/services/common/cron.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/cron.h" -#include - -#include "os/mutex.h" -#include "system/passert.h" -#include "services/common/regular_timer.h" -#include "system/logging.h" -#include "util/math.h" - -//! Don't let users modify the list while callbacks are occurring. -static PebbleMutex *s_list_mutex = NULL; - -static void prv_timer_callback(void* data); -static RegularTimerInfo s_regular = { - .cb = prv_timer_callback, -}; - -// List of jobs sorted from soonest to farthest. -static ListNode *s_scheduled_jobs; - -// ------------------------------------------------------------------------------------------- -static bool prv_is_scheduled(CronJob *job) { - // Assumes mutex lock is already taken - return list_contains(s_scheduled_jobs, &job->list_node); -} - -static int prv_sort(void *a, void *b) { - CronJob *job_a = (CronJob*)a; - CronJob *job_b = (CronJob*)b; - return job_b->cached_execute_time - job_a->cached_execute_time; -} - -// ------------------------------------------------------------------------------------------- -static void prv_timer_callback(void* data) { - mutex_lock(s_list_mutex); - while (s_scheduled_jobs != NULL && - ((CronJob*)s_scheduled_jobs)->cached_execute_time <= rtc_get_time()) { - CronJob *job = (CronJob*)s_scheduled_jobs; - // Remove the job from the list, it's done. - s_scheduled_jobs = list_pop_head(s_scheduled_jobs); - - // Release the mutex while we execute the callback - mutex_unlock(s_list_mutex); - job->cb(job, job->cb_data); - mutex_lock(s_list_mutex); - } - mutex_unlock(s_list_mutex); -} - -// -------------------------------------------------------------------------------------------- -void cron_service_handle_clock_change(PebbleSetTimeEvent *set_time_info) { - mutex_lock(s_list_mutex); - - const bool must_recalc = set_time_info->gmt_offset_delta != 0 || set_time_info->dst_changed; - // Because it's ABS, it'll be unsigned. This makes the compiler behave. - const uint32_t change_diff = ABS(set_time_info->utc_time_delta); - // Need to re-build the list somewhere else - ListNode *newlist = NULL; - while (s_scheduled_jobs != NULL) { - CronJob* job = (CronJob*)s_scheduled_jobs; - s_scheduled_jobs = list_pop_head(s_scheduled_jobs); - // Re-calculate the execute time. - // See the notes in the API header on how this works. - if (must_recalc || change_diff >= job->clock_change_tolerance) { - job->cached_execute_time = cron_job_get_execute_time(job); - } - PBL_LOG(LOG_LEVEL_INFO, "Cron job rescheduled for %ld", job->cached_execute_time); - - newlist = list_sorted_add(newlist, &job->list_node, prv_sort, true); - } - // Then move it back to the static - s_scheduled_jobs = newlist; - - mutex_unlock(s_list_mutex); - - // We want to run any tasks we've skipped over. - prv_timer_callback(NULL); -} - -// -------------------------------------------------------------------------------------------- -void cron_service_init(void) { - PBL_ASSERTN(s_list_mutex == NULL); - - s_list_mutex = mutex_create(); - s_scheduled_jobs = NULL; - - regular_timer_add_seconds_callback(&s_regular); -} - -// ------------------------------------------------------------------------------------------- -time_t cron_job_schedule(CronJob *job) { - PBL_ASSERTN(s_list_mutex); - - mutex_lock(s_list_mutex); - - const time_t now = rtc_get_time(); - // Always update the execution time. - job->cached_execute_time = cron_job_get_execute_time_from_epoch(job, now); - // If not scheduled yet, schedule it. - if (!prv_is_scheduled(job)) { - s_scheduled_jobs = list_sorted_add(s_scheduled_jobs, &job->list_node, prv_sort, true); - } - PBL_LOG(LOG_LEVEL_DEBUG, "Cron job scheduled for %ld (%+ld)", job->cached_execute_time, - (job->cached_execute_time - now)); - - mutex_unlock(s_list_mutex); - - return job->cached_execute_time; -} - -// ------------------------------------------------------------------------------------------ -time_t cron_job_schedule_after(CronJob *job, CronJob *new_job) { - PBL_ASSERTN(s_list_mutex); - - mutex_lock(s_list_mutex); - - // can't schedule an already scheduled job - PBL_ASSERTN(!prv_is_scheduled(new_job)); - // can't schedule after an unscheduled job - PBL_ASSERTN(prv_is_scheduled(job)); - - // copy schedule info from existing job - CronJob temp_job = *job; - list_init(&temp_job.list_node); - temp_job.cb = new_job->cb; - temp_job.cb_data = new_job->cb_data; - *new_job = temp_job; - - // insert after in the list, which guarantees it gets executed after - list_insert_after(&job->list_node, &new_job->list_node); - PBL_LOG(LOG_LEVEL_DEBUG, "Cron job scheduled for %ld", job->cached_execute_time); - - mutex_unlock(s_list_mutex); - - return job->cached_execute_time; -} - -// ------------------------------------------------------------------------------------------ -bool cron_job_is_scheduled(CronJob *job) { - PBL_ASSERTN(s_list_mutex); - - mutex_lock(s_list_mutex); - bool rv = prv_is_scheduled(job); - mutex_unlock(s_list_mutex); - - return (rv); -} - -// ------------------------------------------------------------------------------------------ -bool cron_job_unschedule(CronJob *job) { - PBL_ASSERTN(s_list_mutex); - bool removed = false; - mutex_lock(s_list_mutex); - - if (prv_is_scheduled(job)) { - list_remove(&job->list_node, &s_scheduled_jobs, NULL); - removed = true; - } - - mutex_unlock(s_list_mutex); - return removed; -} - - -// --------------------------------------------------------------------------------------- -// For Testing: - -void cron_clear_all_jobs(void) { - mutex_lock(s_list_mutex); - - // Iterate over all the jobs to remove them all. - for (ListNode* iter = s_scheduled_jobs; iter != NULL; ) { - CronJob* job = (CronJob*)iter; - iter = list_get_next(iter); - // Remove the job from the list. - list_remove(&job->list_node, NULL, NULL); - } - s_scheduled_jobs = NULL; - - mutex_unlock(s_list_mutex); -} - -void cron_service_deinit(void) { - cron_clear_all_jobs(); - - mutex_destroy(s_list_mutex); - s_list_mutex = NULL; - - regular_timer_remove_callback(&s_regular); -} - -uint32_t cron_service_get_job_count(void) { - uint32_t count = 0; - mutex_lock(s_list_mutex); - count = list_count(s_scheduled_jobs); - mutex_unlock(s_list_mutex); - return count; -} - -void cron_service_wakeup(void) { - prv_timer_callback(NULL); -} - -// --------------------------------------------------------------------------------------- -// The brains. -typedef enum { - CronAssignMode_LocalEpoch, // 'any' uses local epoch's value - CronAssignMode_Zero, // 'any' uses 0 -} CronAssignMode; - -// Indices for the access arrays -#define CRON_INDEX_YEAR 0 -#define CRON_INDEX_MONTH 1 -#define CRON_INDEX_DAY 2 -#define CRON_INDEX_HOUR 3 -#define CRON_INDEX_MIN 4 -#define CRON_INDEX_SEC 5 -#define CRON_INDEX_COUNT 6 - -#define CRON_GENERIC_ANY (-1) -#define CRON_YEAR_ANY (-1) -#define CRON_SECOND_ANY (-1) - -// If the 'working' time is ahead of local epoch, we return 1. If behind, we return -1. -// Otherwise, return 0. -static int prv_future_past_direction(int **dest_arr, const int *curr_arr) { - // Iterate from highest order to lowest. - for (int i = 0; i < CRON_INDEX_COUNT; i++) { - if (*(dest_arr[i]) > curr_arr[i]) { - // In future - return 1; - } else if (*(dest_arr[i]) < curr_arr[i]) { - // In past - return -1; - } - } - return 0; -} - -// Increase the day in `cron_tm` to fit into the wday set in `cron`. -// This doesn't take mday into account because that's way too hard and we won't need it. -static bool prv_adjust_for_wday_spec(const CronJob *cron, struct tm *cron_tm) { - // If we're allowing any wday, we're not adjusting. - if (cron->wday == WDAY_ANY || cron->wday == 0) { - return false; - } - - // Keep track of whether we've adjusted or not. - bool adjusted = false; - - // We need to update cron_tm's tm_wday for proper checking. - cron_tm->tm_mday += 1; // Adjustment because struct tm has mday 1-indexed for whatever reason - mktime(cron_tm); - cron_tm->tm_mday -= 1; - // We have 1 week to find a fitting date - for (int l = 0; l < DAYS_PER_WEEK; l++) { - if (cron->wday & (1 << cron_tm->tm_wday)) { - break; - } - // Advance the day. - cron_tm->tm_mday++; - cron_tm->tm_wday = (cron_tm->tm_wday + 1) % DAYS_PER_WEEK; - adjusted = true; - } - return adjusted; -} - -static time_t prv_get_execute_time_from_epoch(const CronJob *job, time_t local_epoch) { - struct tm current_tm; - // We work off of each element, so we need a struct tm. - localtime_r(&local_epoch, ¤t_tm); - - // Adjust to be zero-indexed - current_tm.tm_mday -= 1; - - // If the job isn't allowed to fire instantly, we're going to force the current second to be - // 1, and the destination second to be 0. This works because it means we cannot use the current - // time as-is, but it will not influence the other fields more than necessary. - if (!job->may_be_instant) { - current_tm.tm_sec = 1; - } - // Cron tm is based on the current tm - struct tm cron_tm = current_tm; - // Don't listen to this stuff (yet) - cron_tm.tm_gmtoff = 0; - cron_tm.tm_isdst = 0; - - // Access everything as arrays because it's way easier that way. - int *dest_arr[CRON_INDEX_COUNT] = { - &cron_tm.tm_year, - &cron_tm.tm_mon, - &cron_tm.tm_mday, - &cron_tm.tm_hour, - &cron_tm.tm_min, - &cron_tm.tm_sec, - }; - const int curr_arr[CRON_INDEX_COUNT] = { - current_tm.tm_year, - current_tm.tm_mon, - current_tm.tm_mday, - current_tm.tm_hour, - current_tm.tm_min, - current_tm.tm_sec, - }; - const int spec_arr[CRON_INDEX_COUNT] = { - CRON_YEAR_ANY, // year should always default - job->month, - job->mday, - job->hour, - job->minute, - // If can be instant, second should default. - // If it can't, use 0 because it's less than 1. - job->may_be_instant ? CRON_SECOND_ANY : 0, - }; - -/* -This is where the actual date finding is done. Essentially, we start with setting the result to -the local epoch, and modify from there. - -We iterate over the fields from most significant to least significant. The reasoning for this is -that we will only know how to properly adjust a less significant field based on the value of the -more significant fields. - -When a field in the spec is marked as ANY (-1), we need to decide what to put in the result: - - If all values so far are still the same as the local epoch, we will use the local epoch's - value. - - Otherwise, the value stored will be 0, because the result is in the future, so a value of 0 - will definitely be the soonest time that matches. - -Now, if the result is behind the local epoch, we step through higher order fields for a field -that was not specified. When we find one, we increase the value by 1. Since this is a higher -order field, this is guaranteed to put the result ahead of the local epoch. -*/ - - // 'any' assignment defaults to using the local epoch's values. - CronAssignMode assign_mode = CronAssignMode_LocalEpoch; - // Iterate over all the fields - for (int i = CRON_INDEX_YEAR; i < CRON_INDEX_COUNT; i++) { - // If the spec had an 'any': - if (spec_arr[i] <= CRON_GENERIC_ANY) { - switch (assign_mode) { - case CronAssignMode_LocalEpoch: - // value will be the local epoch's value, we don't need to change anything - break; - case CronAssignMode_Zero: - // value will be set to 0 - *(dest_arr[i]) = 0; - break; - } - } else { // Otherwise, use the spec's value. - *(dest_arr[i]) = spec_arr[i]; - } - - if (assign_mode == CronAssignMode_LocalEpoch) { - // If we haven't started adjusting things yet, we need to do checking. - - const int direction = prv_future_past_direction(dest_arr, curr_arr); - if (direction < 0) { - // If the target is _behind_ the current time, we need to increase a higher order field. - - // Step from next highest all the way up to a year. We adjust the least significant field - // that is more significant than the current field, and is unspec'd. - for (int l = i - 1; l >= CRON_INDEX_YEAR; l--) { - // If the field isn't set in the spec, increasing it by 1 will put us back in the - // future. - if (spec_arr[l] <= CRON_GENERIC_ANY) { - *(dest_arr[l]) += 1; - break; - } - } - } - if (direction != 0) { - // The target is now ahead of the current time, the rest of the unspec'd fields - // should be 0. - assign_mode = CronAssignMode_Zero; - } - } - } - - // Increase the day until we fit into the `wday` spec. - if (prv_adjust_for_wday_spec(job, &cron_tm)) { - // If the day has been adjusted, we need to re-set hour+minute+second. - // Since we are definitely in the future on an adjustment, fields with 'any' should be - // set to 0, otherwise set to the spec value. - cron_tm.tm_hour = MAX(job->hour, 0); - cron_tm.tm_min = MAX(job->minute, 0); - // Second is always 0 when we're in the future. - cron_tm.tm_sec = 0; - } - - // Adjust back to 1-indexed - cron_tm.tm_mday += 1; - - // Decide the DSTny (Adjust for DST transitions) - cron_tm.tm_gmtoff = current_tm.tm_gmtoff; // We're using the current time's GMT offset - cron_tm.tm_isdst = 0; // We'll do the DST adjust ourselves - time_t t = mktime(&cron_tm); - - // Apply offset seconds - t += job->offset_seconds; - - if (time_get_isdst(t)) { - t -= time_get_dstoffset(); - if (!time_get_isdst(t)) { - // We're in the hole where DST starts. - // We want holed alarms to fire instantly, so set time to DST start time. - t = time_get_dst_start(); - } - } - // We could be in the overlap where DST ends, but we don't actually care about it. - // Why, you ask? This gives us the 'first' matching time if we ignore it. - // So 1:30 will give us the first 1:30, not the second one. - // Yes it's arbitrary. Yes it's confusing. But that's timekeeping and DST for you. - - return t; -} - -time_t cron_job_get_execute_time_from_epoch(const CronJob *job, time_t local_epoch) { - time_t t = prv_get_execute_time_from_epoch(job, local_epoch); - - if (job->offset_seconds != 0) { - time_t t_last = t; - time_t offset_epoch = local_epoch; - while (true) { - const time_t t_delta = (t - local_epoch) * ((job->offset_seconds > 0) ? 1 : -1); - if ((job->may_be_instant ? (t_delta <= 0) : (t_delta < 0))) { - break; - } - // Offset seconds is positive => Applying a positive offset seconds could result in a trigger - // time after the nearest trigger time, find and check the previous time. - // Offset seconds is negative => Applying a negative offset seconds resulted in a time before - // local_epoch, calculate the next time. - t_last = t; - offset_epoch -= job->offset_seconds; - const time_t rv = prv_get_execute_time_from_epoch(job, offset_epoch); - t = rv < local_epoch ? t : rv; - if (job->offset_seconds > 0 && t == t_last) { - break; - } - } - } - - return t; -} - - -time_t cron_job_get_execute_time(const CronJob *job) { - return cron_job_get_execute_time_from_epoch(job, rtc_get_time()); -} diff --git a/src/fw/services/common/cron.h b/src/fw/services/common/cron.h deleted file mode 100644 index 9054a78350..0000000000 --- a/src/fw/services/common/cron.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/events.h" - -//! @file cron.h -//! Wall-clock based timer system. Designed for use in things such as alarms, calendar events, etc. -//! Properly handles DST, etc. -//! This file is for controlling the service itself. The actual job API is in - -//! Initialize the cron service. -void cron_service_init(void); - -//! Adjust all cron jobs, as the wall clock has changed. -//! This means DST and/or time zone may have changed! -void cron_service_handle_clock_change(PebbleSetTimeEvent *set_time_info); - -#if UNITTEST -// ----------------------------------------------------------------------------- -// For testing: - -//! Remove all jobs. -void cron_clear_all_jobs(void); - -//! Clean up the cron service. -void cron_service_deinit(void); - -//! The number of registered cron jobs. -uint32_t cron_service_get_job_count(void); - -//! Run the cron timers if they've fired. -void cron_service_wakeup(void); -#endif diff --git a/src/fw/services/common/debounced_connection_service.c b/src/fw/services/common/debounced_connection_service.c deleted file mode 100644 index 029f3bf51f..0000000000 --- a/src/fw/services/common/debounced_connection_service.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "debounced_connection_service.h" - -#include "services/common/comm_session/session.h" -#include "services/common/regular_timer.h" -#include "syscall/syscall_internal.h" - -//! This module is responsible for propagating debounced connection events. -//! Connection events are passed through right away to subscribers but -//! disconnection events are only passed through if a re-connection did not -//! occur within a small window of time. This way, short disconnect periods -//! can go unnoticed to the end consumer resulting in a better perception of -//! connection reliability -//! -//! At the moment, the connections this module tracks are: -//! + Watch <-> Mobile App / PebbleKit JS -//! + Watch <-> third-party App using PebbleKit - -typedef enum { - MobileAppDebounce = 0, - PebbleKitDebounce, - NumConnectionsToDebounce, -} DebounceConnection; - -static RegularTimerInfo s_debounce_timers[NumConnectionsToDebounce]; -static bool s_debounced_state_is_connected[NumConnectionsToDebounce]; - -static void prv_put_debounced_connection_event(DebounceConnection conn_id) { - PebbleEvent event = { - .type = PEBBLE_BT_CONNECTION_DEBOUNCED_EVENT, - .bluetooth.comm_session_event.is_open = s_debounced_state_is_connected[conn_id], - .bluetooth.comm_session_event.is_system = (conn_id == MobileAppDebounce), - }; - event_put(&event); -} - -static void prv_handle_disconnection_debounced(void *data) { - DebounceConnection conn_id = (DebounceConnection)data; - s_debounced_state_is_connected[conn_id] = false; - prv_put_debounced_connection_event(conn_id); - regular_timer_remove_callback(&s_debounce_timers[conn_id]); -} - -void debounced_connection_service_init(void) { - for (int i = 0; i < NumConnectionsToDebounce; i++) { - s_debounce_timers[i].cb = prv_handle_disconnection_debounced; - s_debounce_timers[i].cb_data = (void *)(uintptr_t)i; - } - - // initial state of the connections - s_debounced_state_is_connected[MobileAppDebounce] = (comm_session_get_system_session() != NULL); - s_debounced_state_is_connected[PebbleKitDebounce] = - (comm_session_get_current_app_session() != NULL); -} - -DEFINE_SYSCALL(bool, sys_mobile_app_is_connected_debounced, void) { - return s_debounced_state_is_connected[MobileAppDebounce]; -} - -DEFINE_SYSCALL(bool, sys_pebblekit_is_connected_debounced, void) { - return s_debounced_state_is_connected[PebbleKitDebounce]; -} - -#define DISCONNECT_HIDE_DURATION_SECS 25 -void debounced_connection_service_handle_event(PebbleCommSessionEvent *e) { - DebounceConnection conn_id = e->is_system ? MobileAppDebounce : PebbleKitDebounce; - bool timer_stopped = false; - if (!e->is_open) { - // If we become disconnected don't update apps until we have had a chance - // to recover the connection. This will make our BT connection seem more - // reliable. - regular_timer_add_multisecond_callback( - &s_debounce_timers[conn_id], DISCONNECT_HIDE_DURATION_SECS); - return; - } - - if (regular_timer_is_scheduled(&s_debounce_timers[conn_id])) { - // we reconnected quickly so no need to notify the app about it - timer_stopped = regular_timer_remove_callback(&s_debounce_timers[conn_id]); - } - - if (!timer_stopped) { - // We've been disconnected long enough that we've already told the app that - // we disconnected so let the app know that we are connected again. - s_debounced_state_is_connected[conn_id] = true; - prv_put_debounced_connection_event(conn_id); - } -} diff --git a/src/fw/services/common/debounced_connection_service.h b/src/fw/services/common/debounced_connection_service.h deleted file mode 100644 index dbc13a806b..0000000000 --- a/src/fw/services/common/debounced_connection_service.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/events.h" -#include - -void debounced_connection_service_init(void); - -bool debounced_connection_service_is_connected(void); - -void debounced_connection_service_handle_event(PebbleCommSessionEvent *e); diff --git a/src/fw/services/common/ecompass.c b/src/fw/services/common/ecompass.c deleted file mode 100644 index 59fff40685..0000000000 --- a/src/fw/services/common/ecompass.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ecompass.h" - -#include "applib/accel_service.h" -#include "applib/compass_service.h" -#include "util/trig.h" -#include "console/prompt.h" -#include "drivers/mag.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/event_service.h" -#include "services/common/regular_timer.h" -#include "syscall/syscall_internal.h" -#include "syscall/syscall.h" -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/util/sleep.h" - -#include "system/rtc_registers.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include - -// Duration (in minutes) to run high-frequency sampling during compass calibration. -// Defaults to 2 minutes, but some platforms (e.g., Asterix) require longer. -#if PLATFORM_ASTERIX -#define ECOMPASS_CALIBRATION_FAST_MINUTES 6 -#else -#define ECOMPASS_CALIBRATION_FAST_MINUTES 2 -#endif - -#define VALID_CORR_MARKER 0x5644 -#define BITS_PER_CORRECTION_VAL 16 -#define CORRECTION_VAL_MASK ((1 << BITS_PER_CORRECTION_VAL) - 1) - - -static CompassStatus s_current_cal_status = CompassStatusDataInvalid; -static int16_t s_active_corr[3] = { 0 }; - -static bool s_service_init = false; -static bool s_saved_corr_present = false; -static int16_t s_saved_corr[3] = { 0 }; - -static int32_t s_last_heading = -1; // the last heading we found -#ifdef RECOVERY_FW -static MagData s_last_mag_sample = { 0 }; -#endif - -////////////////////////////////////////////////////////////////////////////////// -// Calibration state variables -static void prv_calibration_time_expired_cb(void* data); - -static bool s_high_freq_calib_active = false; -static bool s_calib_run = false; -static RegularTimerInfo s_cb_info = { .cb = prv_calibration_time_expired_cb }; - - -////////////////////////////////////////////////////////////////////////////////// -// Compass subscription state variables - -static uint8_t s_compass_subscribers_count = 0; -static bool s_compass_subscribers[NumPebbleTask] = { 0 }; - - -////////////////////////////////////////////////////////////////////////////////// -// Accel service state variables - -static AccelServiceState *s_accel_session = NULL; -static bool s_charger_plugged = false; -static AccelRawData s_accel_data = { 0 }; - - -////////////////////////////////////////////////////////////////////////////////// -// Private calibration handlers - -static void prv_reset_saved_sample(void) { -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - RTC_WriteBackupRegister(MAG_Z_CORRECTION_VAL, 0x0); -#endif - s_saved_corr_present = false; -} - -static void prv_save_calibration_values(int16_t *corr) { -#if !MICRO_FAMILY_NRF5 && !MICRO_FAMILY_SF32LB52 - // first zero out the valid marker - prv_reset_saved_sample(); - - uint32_t valxy = (((uint32_t)corr[1]) << BITS_PER_CORRECTION_VAL) | - (corr[0] & CORRECTION_VAL_MASK); - uint32_t valz = (((uint32_t)VALID_CORR_MARKER) << BITS_PER_CORRECTION_VAL) | - (corr[2] & CORRECTION_VAL_MASK); - - RTC_WriteBackupRegister(MAG_XY_CORRECTION_VALS, valxy); - RTC_WriteBackupRegister(MAG_Z_CORRECTION_VAL, valz); - - for (int i = 0; i < 3; i++) { - s_saved_corr[i] = corr[i]; - } - s_saved_corr_present = true; -#endif -} - -// Loads the calibration values. Returns true if loads successfully, -// otherwise false. -static bool prv_load_calibration_values(void) { -#if MICRO_FAMILY_NRF5 || MICRO_FAMILY_SF32LB52 - return false; -#else - uint32_t valxy = RTC_ReadBackupRegister(MAG_XY_CORRECTION_VALS); - uint32_t valz = RTC_ReadBackupRegister(MAG_Z_CORRECTION_VAL); - - bool is_valid = ((valz >> BITS_PER_CORRECTION_VAL) & CORRECTION_VAL_MASK) == - VALID_CORR_MARKER; - - if (is_valid) { - s_saved_corr[0] = valxy & CORRECTION_VAL_MASK; - s_saved_corr[1] = (valxy >> BITS_PER_CORRECTION_VAL) & CORRECTION_VAL_MASK; - s_saved_corr[2] = valz & CORRECTION_VAL_MASK; - } - - return (is_valid); -#endif -} - -static void prv_get_roll_and_pitch(AccelRawData *d, int32_t *rollp, - int32_t *pitchp) { - if ((d->x == 0) && (d->y == 0) && (d->z == 0)) { - *rollp = *pitchp = 0; - return; - } - - int32_t roll = atan2_lookup(d->y, d->z); - - int32_t act_ang = (roll * 360) / TRIG_MAX_ANGLE; - if (act_ang > 180) { - roll = roll - TRIG_MAX_ANGLE; - } - - int32_t pitch = atan2_lookup(-d->x, - (d->y * sin_lookup(roll) + d->z * cos_lookup(roll)) / TRIG_MAX_RATIO); - - // solution repeats every 180 degrees - if (pitch > (TRIG_MAX_ANGLE / 4)) { // > 90 degrees - if (pitch < ((270 * TRIG_MAX_ANGLE) / 360)) { - pitch -= (TRIG_MAX_ANGLE / 2); - } else { - pitch -= TRIG_MAX_ANGLE; - } - } - - *rollp = roll; - *pitchp = pitch; -} - -static int32_t prv_correct_for_roll_and_pitch(AccelRawData *accel_data, - MagData *mag_data, int32_t roll, int32_t pitch) { - int32_t mx = mag_data->x - s_active_corr[0]; - int32_t my = mag_data->y - s_active_corr[1]; - int32_t mz = mag_data->z - s_active_corr[2]; - - int32_t mx_rot, my_rot; - - // per freescale AN4249, roll is unstable close to verticle but pitch is ok - int32_t corr = 0; - if (TRIGANGLE_TO_DEG(pitch) > 82) { - pitch = TRIG_MAX_ANGLE / 4; - roll = 0; - } else if (accel_data->z < 0) { - // the watch has been flipped over. If someone is viewing the watch - // at a pitch > 90 degrees, this means the heading will rotate around - // on them (since technically, the 'front' of the watch is pointing - // at them.) Flip the heading back around in this case. - corr = TRIG_MAX_ANGLE / 2; - } - - mx_rot = (mx * cos_lookup(pitch)) / TRIG_MAX_RATIO; - mx_rot += (((my * sin_lookup(pitch)) / TRIG_MAX_RATIO) * sin_lookup(roll)) / - TRIG_MAX_RATIO; - mx_rot += (((mz * sin_lookup(pitch)) / TRIG_MAX_RATIO) * cos_lookup(roll)) / - TRIG_MAX_RATIO; - - my_rot = mz * sin_lookup(roll) - my * cos_lookup(roll); - my_rot /= TRIG_MAX_RATIO; - - int32_t heading = (atan2_lookup(-my_rot, mx_rot) + corr) % TRIG_MAX_ANGLE; - return (heading); -} - - -////////////////////////////////////////////////////////////////////////////////// -// Private handlers for compass service - -static void prv_calibration_time_expired_cb(void* data) { - PBL_LOG(LOG_LEVEL_DEBUG, "Calibration time expired, complete, or app exit, " - "dropping back to low frequency"); - - if (!mag_change_sample_rate(MagSampleRate5Hz)) { - PBL_LOG(LOG_LEVEL_WARNING, "Forcing reset to enter low freq mode"); - mag_release(); - mag_start_sampling(); - } - - s_high_freq_calib_active = false; - regular_timer_remove_callback(&s_cb_info); -} - -static void prv_accel_for_compass_handler(AccelRawData *d, uint32_t num_samples, - uint64_t timestamp) { - int32_t x = 0, y = 0, z = 0; - - // 1st order butterworth filter with a cutoff freq of 0.02Fs - static int32_t xp = 0, yp = 0, zp = 0; - static int32_t xr = 0, yr = 0, zr = 0; - - for (uint32_t i = 0; i < num_samples; i++) { - xr = (305 * d[i].x + 305 * xp + 9391 * xr) / 10000; - yr = (305 * d[i].y + 305 * yp + 9391 * yr) / 10000; - zr = (305 * d[i].z + 305 * zp + 9391 * zr) / 10000; - xp = d[i].x; yp = d[i].y; zp = d[i].z; - x += xr; - y += yr; - z += zr; - } - - x /= (int)num_samples; - y /= (int)num_samples; - z /= (int)num_samples; - - // TODO: could this ever be called in the middle of ecompass_service_handle? - // map accel data to NED coordinate system - s_accel_data.x = y; - s_accel_data.y = x; - s_accel_data.z = -z; -} - -static void prv_compass_data_service_stop(PebbleTask task) { - if (s_compass_subscribers[task]) { - s_compass_subscribers[task] = false; - - if (--s_compass_subscribers_count == 0) { - // If this was the last subscribed process, then stop the compass - // service - if (s_high_freq_calib_active) { - prv_calibration_time_expired_cb(NULL); - s_calib_run = false; - } - accel_session_data_unsubscribe(s_accel_session); - accel_session_delete(s_accel_session); - s_accel_session = NULL; - mag_release(); - } - } - PBL_LOG(LOG_LEVEL_DEBUG, "subscribers %"PRIu8, s_compass_subscribers_count); -} - -static void prv_compass_data_service_start(PebbleTask task) { - prv_compass_data_service_stop(task); - s_compass_subscribers[task] = true; - if (++s_compass_subscribers_count == 1) { - // If this is the first subscriber to the compass service, start sampling - PBL_ASSERTN(s_accel_session == NULL); - - s_accel_session = accel_session_create(); - accel_session_raw_data_subscribe(s_accel_session, ACCEL_SAMPLING_25HZ, 5, - prv_accel_for_compass_handler); - - mag_start_sampling(); - } - PBL_LOG(LOG_LEVEL_DEBUG, "subscribers %"PRIu8, s_compass_subscribers_count); -} - -////////////////////////////////////////////////////////////////////////////////// -// Public API - -void ecompass_handle_battery_state_change_event(PreciseBatteryChargeState new_state) { - if (new_state.is_plugged) { - s_charger_plugged = true; - s_current_cal_status = CompassStatusDataInvalid; - memset(s_active_corr, 0x00, sizeof(s_active_corr)); - prv_reset_saved_sample(); - } else if (s_charger_plugged) { - // we have unplugged the charger, initiate recalibration - s_charger_plugged = false; - s_calib_run = false; // trigger a rerun of fast compass calibration - PBL_LOG(LOG_LEVEL_DEBUG, "Restarting calibration after charge event"); - } -} - -void ecompass_service_init(void) { - if (!s_service_init) { - s_saved_corr_present = prv_load_calibration_values(); - if (s_saved_corr_present) { - s_current_cal_status = CompassStatusCalibrating; - for (int i = 0; i < 3; i++) { - s_active_corr[i] = s_saved_corr[i]; - } - } - s_service_init = true; - } - - event_service_init(PEBBLE_COMPASS_DATA_EVENT, &prv_compass_data_service_start, - &prv_compass_data_service_stop); -} - -void ecompass_service_handle(void) { - static int samples_collected = 0; - - // read magnetometer sample - MagData mag_data; - MagReadStatus rv = mag_read_data(&mag_data); - if (rv != MagReadSuccess) { - if (rv == MagReadCommunicationFail) { - // heavy hammer fix for now - // FIXME: move the restart logic to driver - PBL_LOG(LOG_LEVEL_WARNING, "Read after %d samples failed, " - "restarting compass", samples_collected); - mag_release(); - mag_start_sampling(); - } - return; - } - -#ifdef RECOVERY_FW - s_last_mag_sample = mag_data; -#endif - - // industry standard for heading coordinates uses NED convention (check out - // Freescale's AN4248 or ST's AN3192 as examples). Therefore, we map pebbles - // coordinate system (ENU) to NED in this service module - int16_t my_orig = mag_data.y; - mag_data.y = mag_data.x; - mag_data.x = my_orig; - mag_data.z = -mag_data.z; - - samples_collected++; - - // don't perform any calibration if the charger is plugged in - if (!s_charger_plugged && (s_current_cal_status != CompassStatusCalibrated)) { - // if we haven't tried to calibrate yet, run at a higher sampling rate - // for a short duration in order to finish calibration more quickly - if (!s_calib_run) { - ecomp_corr_reset(); - mag_change_sample_rate(MagSampleRate20Hz); - regular_timer_add_multiminute_callback(&s_cb_info, ECOMPASS_CALIBRATION_FAST_MINUTES); - s_calib_run = true; - s_high_freq_calib_active = true; - samples_collected = 0; - return; // if we are switching to high freq mode, don't use 1st sample - } - - if (samples_collected < 5) { - return; // wait a few samples for ramp up error to stabilize - } - - int16_t new_corr[3]; - MagCalStatus cal_status = ecomp_corr_add_raw_mag_sample((int16_t *)&mag_data, - (s_saved_corr_present) ? s_saved_corr : NULL, new_corr); - - if (cal_status != MagCalStatusNoSolution) { - PBL_LOG(LOG_LEVEL_INFO, "%s : %d %d %d (type = %d)", "Mag Corr", - (int)new_corr[0], (int)new_corr[1], (int)new_corr[2], (int)cal_status); - } - - bool locked_sol = (cal_status == MagCalStatusNewLockedSolutionAvail); - if (locked_sol || ((cal_status == MagCalStatusNewSolutionAvail) && - !s_saved_corr_present)) { - s_current_cal_status = CompassStatusCalibrating; - for (int i = 0; i < 3; i++) { - if ((s_active_corr[i] == 0) || locked_sol) { - s_active_corr[i] = new_corr[i]; - continue; - } - - // smooth out noise from solutions while we wait for a locked set - const int alpha = 30; // greater alpha leads to less smoothing - new_corr[i] = ((new_corr[i] - s_active_corr[i]) * alpha) / 100; - s_active_corr[i] += new_corr[i]; - } - } - - if (s_high_freq_calib_active && (cal_status == MagCalStatusNoSolution) && - ((samples_collected % 4) != 0)) { - return; // only bubble up every 4th sample to app land with high samp rate - } - - if ((cal_status == MagCalStatusNewLockedSolutionAvail) || - (cal_status == MagCalStatusSavedSampleMatch)) { - if (s_high_freq_calib_active) { - prv_calibration_time_expired_cb(NULL); - } - prv_save_calibration_values(s_active_corr); - s_current_cal_status = CompassStatusCalibrated; - } - } - - // get the most recent accel readings - AccelRawData accel_data = s_accel_data; - int32_t roll = 0, pitch = 0; - prv_get_roll_and_pitch(&accel_data, &roll, &pitch); - - PebbleEvent e = { - .type = PEBBLE_COMPASS_DATA_EVENT, - .compass_data = { - .magnetic_heading = prv_correct_for_roll_and_pitch(&accel_data, &mag_data, - roll, pitch), - .calib_status = s_current_cal_status - } - }; - - s_last_heading = e.compass_data.magnetic_heading; - event_put(&e); -} - -////////////////////////////////////////////////////////////////////////////////// -// System call handlers - -DEFINE_SYSCALL(bool, sys_ecompass_service_subscribed, void) { - PebbleTask task = pebble_task_get_current(); - return s_compass_subscribers[task]; -} - -DEFINE_SYSCALL(void, sys_ecompass_get_last_heading, CompassHeadingData *data) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(data, sizeof(*data)); - } - - *data = (CompassHeadingData) { - .magnetic_heading = s_last_heading, - .true_heading = s_last_heading, - .compass_status = s_current_cal_status, - .is_declination_valid = false - }; -} - -////////////////////////////////////////////////////////////////////////////////// -// Recovery firmware commands - -#ifdef RECOVERY_FW -static void prv_ecompass_start_callback(void *context) { - s_accel_session = accel_session_create(); - accel_session_raw_data_subscribe(s_accel_session, ACCEL_SAMPLING_25HZ, 5, - prv_accel_for_compass_handler); - mag_start_sampling(); -} - -static void prv_ecompass_stop_callback(void *context) { - accel_session_data_unsubscribe(s_accel_session); - accel_session_delete(s_accel_session); - mag_release(); -} - -//! Serial command for reading a single value from the compass. -void command_compass_peek(void) { - int32_t prev_heading = s_last_heading; - - launcher_task_add_callback(prv_ecompass_start_callback, NULL); - - // wait for last heading to be updated - int retries = 50; // 5 seconds should be ample time - while ((prev_heading == s_last_heading) && retries-- > 0) { - psleep(100); - } - - launcher_task_add_callback(prv_ecompass_stop_callback, NULL); - psleep(5); // give the compass some time to stop - - char buffer[40]; - prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRId32" degrees", - (s_last_heading * 360) / TRIG_MAX_ANGLE); - - prompt_send_response_fmt(buffer, sizeof(buffer), "Mx=%d, My=%d, Mz=%d", - s_last_mag_sample.x, s_last_mag_sample.y, s_last_mag_sample.z); -} -#endif // RECOVERY_FW diff --git a/src/fw/services/common/ecompass_correction.c b/src/fw/services/common/ecompass_correction.c deleted file mode 100644 index 88f5e79a8d..0000000000 --- a/src/fw/services/common/ecompass_correction.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ecompass.h" - -#include "util/trig.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" -#include "util/math.h" - -#include -#include - -#define N_SAMPS 4 // four points define a unique sphere -#define N_AXIS 3 -static int16_t s_samples[N_SAMPS][N_AXIS]; - -// -// Two basic equations of a sphere: -// a) (x - x0)^2 + (y - y0)^2 + (z - z0)^2 = r0^2 -// (x^2 - 2*x*x0 + x0^2) + (y^2 - 2*y*x0 + y0^2) + (z^2 - 2*z*z0 + z0^2) = r^2 -// -2*x*x0 - 2*y*y0 - 2*z*z0 + (x^2 + y^2 + z^2) + r0^2 = 0 -// b) A*(x^2 + y^2 + x^2) + B*x + C*y + D*z + E = 0 -// -// Ideally, we would be able to build a large cloud of points and apply a least -// squares or ellipsoid fit to that dataset. However, this quickly becomes -// expensive (from a code space perspective). Therefore, we focus on collecting -// four 'good' data points and fitting those point to a sphere -// -// The sphere fit entails solving a linear system of the form Ax = B -// -// | (x^2 + y^2 + z^2) x y z 1 | | A | | 0 | -// | (x0^2 + y0^2 + z0^2) x0 y0 z0 1 | | B | | 0 | -// | (x1^2 + y1^2 + z1^2) x1 y1 z1 1 | * | C | = | 0 | -// | (x2^2 + y2^2 + z2^2) x2 y2 z2 1 | | D | | 0 | -// | (x3^2 + y3^2 + z3^2) x3 y3 z3 1 | | E | | 0 | -// -// to solve we want to find where det(A) = 0 -// -// Using Laplace's formula for determinant expansion we can break this into a -// system of 4x4 determinants. Expanding along row 0 and using Cij to represent -// the cofactor which removes row i and column j: -// -// (x^2 + y^2 + z^2) * C_0,0 + x * C_0,1 + y * C_0,2 + z * C_0,3 + C_0,4 = 0 -// -// This solution can be re-written in a form similar to eq a) above as -// -// x * (C_0,1 / C_0,0) + y * (C_0,2 / C_0,0) + z * (C_0,3 / C_0,0) + -// (C_0,4 / C_0,0) + (x^2 + y^2 + z^2) = 0 -// -// This gives us our hard iron correction estimates (i.e location of the sphere -// origin, (xo, yo, zo)) as: -// -// (C_0,1 / C_0,0) = -2*x0 ==> x0 = (C_0,1 / C_0,0) / -2 -// (C_0,2 / C_0,0) = -2*y0 ==> y0 = (C_0,2 / C_0,0) / -2 -// (C_0,3 / C_0,0) = -2*z0 ==> z0 = (C_0,3 / C_0,0) / -2 -// - -static int32_t sphere_determinant4x4(int32_t **m, int32_t down_samp) { - // assume x, y, z value fits within 14 bits & m[x][3] == 1 - // when m[x][0] == x^2 + y^2 + z^2, this val < 30 bits - // so m[x][0] * temp is capped at 44bits leaving plenty of space - // within 64 bits - - int64_t res = 0; - // computes the determinant by decomposing the computation into 4 3x3 minor - // determinants formed by removal of row 'skip_row' & the third column - for (int skip_row = 0; skip_row < 4; skip_row++) { - int r0 = (skip_row != 0) ? 0 : 1; - int r1 = (skip_row < 2) ? 2 : 1; - int r2 = (skip_row < 3) ? 3 : 2; - - int64_t temp = (m[r1][1] * m[r2][2]) - (m[r1][2] * m[r2][1]); - int64_t det = temp * (int64_t)m[r0][0]; - - temp = (m[r1][0] * m[r2][2]) - (m[r1][2] * m[r2][0]); - det -= temp * (int64_t)m[r0][1]; - - temp = (m[r1][0] * m[r2][1]) - (m[r1][1] * m[r2][0]); - det += temp * (int64_t)m[r0][2]; - - if ((skip_row % 2) == 0) { - res += det; - } else { - res -=det; - } - } - - return (res / down_samp); -} - -static bool sphere_fit(int16_t *solution) { - bool rv = false; - - int32_t **matrix = kernel_malloc(sizeof(int32_t *) * N_SAMPS); - for (int i = 0; i < N_SAMPS; i++) { - matrix[i] = kernel_malloc(sizeof(int32_t) * N_AXIS); - } - - // determine average value of x, y, & z coordinates - // and shift by this factor to prevent overflow. - int32_t shift_factor[N_AXIS] = { 0 }; - for (int j = 0; j < N_AXIS; j++) { - for (int i = 0; i < N_SAMPS; i++) { - shift_factor[j] += s_samples[i][j]; - } - shift_factor[j] /= N_SAMPS; - } - - int16_t raw_data[N_SAMPS][N_AXIS]; - for (int i = 0; i < N_SAMPS; i++) { - for (int j = 0; j < 3; j++) { - raw_data[i][j] = (s_samples[i][j] - shift_factor[j]); - // TODO: stop here if we have a chance of overflowing - } - } - - int32_t r[N_SAMPS]; - for (int i = 0; i < N_SAMPS; i++) { - r[i] = raw_data[i][0] * raw_data[i][0] + - raw_data[i][1] * raw_data[i][1] + - raw_data[i][2] * raw_data[i][2]; - } - - // We now find the origin by solving the linear equation discussed above - - for (int i = 0; i < N_SAMPS; i++) { - matrix[i][0] = raw_data[i][0]; - matrix[i][1] = raw_data[i][1]; - matrix[i][2] = raw_data[i][2]; - } - int32_t c00 = sphere_determinant4x4(matrix, 1); - if (c00 == 0) { - goto cleanup; - } - - // compute cofactor01 to solve for x0 - for (int i = 0; i < N_SAMPS; i++) { - matrix[i][0] = r[i]; // m[i][1] = y, m[i][2] = z - } - solution[0] = shift_factor[0] + sphere_determinant4x4(matrix, c00 * 2); - - // compute cofactor02 to solve for y0 - for (int i = 0; i < N_SAMPS; i++) { - matrix[i][1] = raw_data[i][0]; // m[i][0] = r^2, m[i][2] = z - } - solution[1] = shift_factor[1] - sphere_determinant4x4(matrix, c00 * 2); - - // compute cofactor03 to solve for z0 - for (int i = 0; i < N_SAMPS; i++) { - matrix[i][2] = raw_data[i][1]; // m[i][0] = r^2, m[i][1] = x - } - solution[2] = shift_factor[2] + sphere_determinant4x4(matrix, c00 * 2); - - rv = true; -cleanup: - for (int i = 0; i < N_SAMPS; i++) { - kernel_free(matrix[i]); - } - kernel_free(matrix); - return (rv); -} - -// Earths magnetic field intensity ranges from 25uT (near the equator) to 65uT -// (near the earths poles). A majority of Europe, North America, & Asia ranges -// between 35-50uT. -// -// Assuming magnetometer readings are predominantly influenced by hard iron -// distortions, we seek to find four points and fit them to a sphere in -// order to determine the offset we need to shift the raw data by to correct -// for hard iron distortions. -// -// For points A, B, C, D and a distance threshold, t, we select four points by -// satisfying the following: -// distance_ptA_to_ptB > t -// distance_lineAB_to_ptC > t -// distance_planeABC_to_ptD > t. -// -// Conceptually, it makes sense that the farther points are from one another (> -// t), the less that errors due to noise, fixed point mathematics, & motion -// render bad solutions. (empirically, this seems to be the behavior as -// well). However, the greater the the threshold, the more orientations one -// must put their watch through in order to get solution sets -// -// For now, select a distance metric that should work out of the box for a -// majority of the middle of the world. However, if no solution sets are found -// after 45s, fall back to a less aggressive threshold that will work -// anywhere in the world. - -#define THRESH_MAX 370 /* 37 uT */ -#define THRESH_MIN 220 /* 22 uT */ - -// Note: All of the following helper routines operate on the s_samples array -// defined above and populated by add_raw_mag_sample - -static bool pt_to_pt_dist_under_thresh(int idx_a, int idx_b, int32_t thresh) { - int32_t v_ab[3] = { - s_samples[idx_b][0] - s_samples[idx_a][0], - s_samples[idx_b][1] - s_samples[idx_a][1], - s_samples[idx_b][2] - s_samples[idx_a][2] - }; - - int32_t dist_sq = v_ab[0] * v_ab[0] + v_ab[1] * v_ab[1] + - v_ab[2] * v_ab[2]; - - return (dist_sq > (thresh * thresh)); -} - -static bool pt_to_line_dist_under_thresh(int idx_line_a, int idx_line_b, - int idx_pt, int32_t thresh) { - - int32_t s[3] = { - s_samples[idx_line_b][0] - s_samples[idx_line_a][0], - s_samples[idx_line_b][1] - s_samples[idx_line_a][1], - s_samples[idx_line_b][2] - s_samples[idx_line_a][2] - }; - - int32_t m1[3] = { - s_samples[idx_line_a][0] - s_samples[idx_pt][0], - s_samples[idx_line_a][1] - s_samples[idx_pt][1], - s_samples[idx_line_a][2] - s_samples[idx_pt][2] - }; - - int32_t m1xs[3] = { - m1[1] * s[2] - m1[2] *s[1], - -(m1[0] * s[2] - m1[2] * s[0]), - m1[0]*s[1] - m1[1] * s[0] - }; - - int64_t dist_sq = ((int64_t)m1xs[0]*m1xs[0] + (int64_t)m1xs[1]*m1xs[1] + - (int64_t)m1xs[2]*m1xs[2]) / (s[0]*s[0] + s[1]*s[1] + s[2]*s[2]); - - return (dist_sq > (thresh * thresh)); -} - -static bool pt_to_plane_dist_under_thresh(int idx_pln_a, int idx_pln_b, - int idx_pln_c, int idx_pt, int32_t thresh) { - - int32_t v_ab[3]; - int32_t v_ac[3]; - for (int i = 0; i < 3; i++) { - v_ab[i] = s_samples[idx_pln_b][i] - s_samples[idx_pln_a][i]; - v_ac[i] = s_samples[idx_pln_c][i] - s_samples[idx_pln_a][i]; - } - - int64_t plane_eq[4]; - plane_eq[0] = v_ab[1] * v_ac[2] - v_ab[2] * v_ac[1]; - plane_eq[1] = -v_ab[0] * v_ac[2] + v_ab[2] * v_ac[0]; - plane_eq[2] = v_ab[0] * v_ac[1] - v_ab[1] * v_ac[0]; - - // solve for d - plane_eq[3] = -(plane_eq[0] * s_samples[0][0] + - plane_eq[1] * s_samples[0][1] + plane_eq[2] * s_samples[0][2]); - - // Distance^2 = (a * xo + b * yo + c * zo + d) / (a^2 + b^2 + c^2) - int64_t distance = ABS((plane_eq[0] * s_samples[idx_pt][0] + - plane_eq[1] * s_samples[idx_pt][1] + plane_eq[2] * s_samples[idx_pt][2] + - plane_eq[3])); - int32_t r = integer_sqrt(plane_eq[0] * plane_eq[0] + plane_eq[1] * plane_eq[1] - + plane_eq[2] * plane_eq[2]); - - - return ((distance / r) > thresh); -} - -static int min_max_diff(int16_t *vals, int n_vals) { - int min = vals[0]; - int max = vals[0]; - - for (int i = 0; i < n_vals; i++) { - if (vals[i] > max) { - max = vals[i]; - } else if (vals[i] < min) { - min = vals[i]; - } - } - return (max - min); -} - -#define N_COMP_SAMPS 3 -static MagCalStatus check_correction_value(int16_t *solution, - int16_t *saved_solution) { - const int max_delta_thresh = 50; - - // stash several of the most recent calibration results. The idea here is if - // we get multiple readings in a row close to one another than we have locked - // onto a good set of solutions - static int16_t calib_idx = 0; - static int16_t calib_val[N_AXIS][N_COMP_SAMPS]; - calib_val[0][calib_idx % 3] = solution[0]; - calib_val[1][calib_idx % 3] = solution[1]; - calib_val[2][calib_idx % 3] = solution[2]; - calib_idx++; - - static int saved_sample_match = 0; - int x_delta, y_delta, z_delta; - - // is the new solution close to what we already have saved? - if (saved_solution != NULL) { - x_delta = ABS(saved_solution[0] - solution[0]); - y_delta = ABS(saved_solution[1] - solution[1]); - z_delta = ABS(saved_solution[2] - solution[2]); - if ((x_delta < max_delta_thresh) && (y_delta < max_delta_thresh) && - (z_delta < max_delta_thresh)) { - saved_sample_match++; - if (saved_sample_match == 3) { - saved_sample_match = 0; - calib_idx = 0; - PBL_LOG(LOG_LEVEL_INFO, "Persisting previous values!"); - return (MagCalStatusSavedSampleMatch); // locked - } - } - } - - // do we have several solutions in a row that are close to one another - if (calib_idx >= N_COMP_SAMPS) { - x_delta = min_max_diff(calib_val[0], 3); - y_delta = min_max_diff(calib_val[1], 3); - z_delta = min_max_diff(calib_val[2], 3); - if ((x_delta < max_delta_thresh) && (y_delta < max_delta_thresh) && - (z_delta < max_delta_thresh)) { - int corrs[N_AXIS] = { 0 }; - for (int i = 0; i < N_AXIS; i++) { - for (int j = 0; j < N_COMP_SAMPS; j++) { - corrs[i] += calib_val[i][j]; - } - solution[i] = corrs[i] / N_COMP_SAMPS; - } - calib_idx = 0; - return (MagCalStatusNewLockedSolutionAvail); - } - } - - return (MagCalStatusNewSolutionAvail); -} - -static int s_sample_idx = 0; -static int s_no_fit_strikes = 0; -static int s_samples_collected_for_fit = 0; - -void ecomp_corr_reset(void) { - s_no_fit_strikes = 0; - s_samples_collected_for_fit = 0; - s_sample_idx = 0; -} - -MagCalStatus ecomp_corr_add_raw_mag_sample(int16_t *sample, - int16_t *saved_corr, int16_t *solution) { - static int thresh = THRESH_MAX; - s_samples_collected_for_fit++; - - // if we haven't gotten good samples points in 15s @ 20Hz (60s @ 5Hz) - if (s_samples_collected_for_fit > 300) { - if (s_sample_idx >= 2) { // there was some kind of motion - s_no_fit_strikes++; - } - if (s_no_fit_strikes == 2) { - PBL_LOG(LOG_LEVEL_INFO, "Lowering magnetometer distance threshold"); - thresh = THRESH_MIN; - } - - s_samples_collected_for_fit = 0; - s_sample_idx = 0; - } - - s_samples[s_sample_idx][0] = sample[0]; - s_samples[s_sample_idx][1] = sample[1]; - s_samples[s_sample_idx][2] = sample[2]; - - if (s_sample_idx == 1) { - if (!pt_to_pt_dist_under_thresh(0, 1, thresh)) { - return (MagCalStatusNoSolution); - } - } else if (s_sample_idx == 2) { - if (!pt_to_line_dist_under_thresh(0, 1, 2, thresh)) { - return (MagCalStatusNoSolution); - } - } else if (s_sample_idx == 3) { - if (!pt_to_plane_dist_under_thresh(0, 1, 2, 3, thresh)) { - return (MagCalStatusNoSolution); - } - } - - // the sample has passed its distance threshold check so add it - PBL_LOG(LOG_LEVEL_DEBUG, "---> [%d] Adding %d %d %d \n", - s_sample_idx, (int)sample[0], (int)sample[1], (int)sample[2]); - s_sample_idx++; - - if (s_sample_idx != 4) { - return (MagCalStatusNoSolution); // we need 4 pts before we can do a fit - } - - // reset vars for next potential sphere fit - s_samples_collected_for_fit = 0; - s_sample_idx = 0; - - if (!sphere_fit(&solution[0])) { - return (MagCalStatusNoSolution); - } - - return (check_correction_value(solution, saved_corr)); -} diff --git a/src/fw/services/common/event_service.c b/src/fw/services/common/event_service.c deleted file mode 100644 index 6311c7a1d5..0000000000 --- a/src/fw/services/common/event_service.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/event_service_client.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "process_management/app_manager.h" -#include "process_management/worker_manager.h" -#include "os/mutex.h" -#include "services/common/event_service.h" -#include "syscall/syscall_internal.h" -#include "syscall/syscall.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "FreeRTOS.h" -#include "queue.h" - -#include - -typedef struct { - int num_subscribers; - QueueHandle_t subscribers[NumPebbleTask]; - EventServiceAddSubscriberCallback add_subscriber_callback; - EventServiceRemoveSubscriberCallback remove_subscriber_callback; -} EventServiceEntry; - -typedef struct { - ListNode list_node; - void *ptr; - // There is an intent bit for each task + a special "claimed" bit - uint16_t intents_pending; -} EventServiceBuffer; - -static const uint16_t CLAIMED_BIT = (1 << NumPebbleTask); - -static EventServiceBuffer *s_event_service_buffers = NULL; - -// We dynamically allocate one of these for every service UUID that either a client subscribes to or a service -// publishes an event to. -typedef struct { - ListNode list_node; - uint16_t service_index; // index of the service - Uuid uuid; // UUID -} EventPluginUUIDEntry; - -static uint16_t s_next_service_index = 0; -static ListNode s_plugin_list; -// This mutex guards the s_plugin_list linked list -static PebbleMutex *s_plugin_list_mutex = NULL; - -// There's an event service for each event so that -// System apps can also use the service -static EventServiceEntry *s_event_services[PEBBLE_NUM_EVENTS]; - -static void prv_event_service_unsubscribe(PebbleSubscriptionEvent *subscription) { - EventServiceEntry *service = s_event_services[subscription->event_type]; - - if (s_event_services[subscription->event_type] == NULL) { - // service does not exist - PBL_LOG(LOG_LEVEL_WARNING, "Attempted to unsubscribe from %d, no service found", - subscription->event_type); - return; - } - - if (service->subscribers[subscription->task] == NULL) { - // not subscribed - PBL_LOG(LOG_LEVEL_WARNING, "Attempted to unsubscribe from %d, not subscribed", - subscription->event_type); - return; - } - - PBL_ASSERTN(service->num_subscribers > 0); - --service->num_subscribers; - service->subscribers[subscription->task] = NULL; - if (service->remove_subscriber_callback != NULL) { - service->remove_subscriber_callback(subscription->task); - } -} - - -static void prv_event_service_subscribe(PebbleSubscriptionEvent *subscription) { - EventServiceEntry *service = s_event_services[subscription->event_type]; - - if (service == NULL) { - // No event service for this event type, create one - event_service_init(subscription->event_type, NULL, NULL); - service = s_event_services[subscription->event_type]; - } - - if (service->subscribers[subscription->task]) { - // already subscribed ? - PBL_LOG(LOG_LEVEL_DEBUG, "already subscribed"); - return; - } - - if (service->add_subscriber_callback != NULL) { - service->add_subscriber_callback(subscription->task); - } - - service->subscribers[subscription->task] = subscription->event_queue; - ++service->num_subscribers; -} - -void event_service_subscribe_from_kernel_main(PebbleSubscriptionEvent *subscription) { - PBL_ASSERT_TASK(PebbleTask_KernelMain); - prv_event_service_subscribe(subscription); -} - -static bool prv_event_service_send_event(QueueHandle_t queue, PebbleEvent *e) { - PBL_ASSERTN(queue != NULL); - bool success = (xQueueSendToBack(queue, e, 0) == pdTRUE); - return success; -} - -void event_service_handle_subscription(PebbleSubscriptionEvent *subscription) { - if (subscription->subscribe) { - prv_event_service_subscribe(subscription); - } else { - prv_event_service_unsubscribe(subscription); - } -} - - -void event_service_clear_process_subscriptions(PebbleTask task) { - EventServiceEntry *service; - - for (int i = 0; i < PEBBLE_NUM_EVENTS; i++) { - if ((service = s_event_services[i]) == NULL) { - continue; - } - if (service->subscribers[task] == NULL) { - continue; - } - - PebbleSubscriptionEvent event = { - .subscribe = false, - .task = task, - .event_type = i, - }; - prv_event_service_unsubscribe(&event); - } -} - -void event_service_system_init(void) { - s_plugin_list_mutex = mutex_create(); -} - -void event_service_init(PebbleEventType type, EventServiceAddSubscriberCallback add_subscriber_callback, - EventServiceRemoveSubscriberCallback remove_subscriber_callback) { - if(s_event_services[type] != NULL) { - // an event service was already inited, free it - kernel_free(s_event_services[type]); - } - - s_event_services[type] = kernel_malloc(sizeof(EventServiceEntry)); - memset(s_event_services[type], 0, sizeof(*s_event_services[type])); - s_event_services[type]->add_subscriber_callback = add_subscriber_callback; - s_event_services[type]->remove_subscriber_callback = remove_subscriber_callback; -} - -bool event_service_is_running(PebbleEventType event_type) { - if (s_event_services[event_type] == NULL) { - return (false); - } - if (s_event_services[event_type]->num_subscribers > 0) { - return (true); - } - - return (false); -} - -static bool prv_task_is_masked_out(PebbleEvent *e, PebbleTask task) { - const PebbleTaskBitset task_bit = (1 << task); - return (e->task_mask & task_bit); -} - -static bool prv_steal_buffer(void *buf, EventServiceEntry *service, PebbleEvent *e) { - uint16_t intents_pending = 0; - - for (int i = 0; i < NumPebbleTask; i++) { - if (!prv_task_is_masked_out(e, i) && service->subscribers[i]) { - const PebbleTaskBitset task_bit = (1 << i); - intents_pending |= task_bit; - } - } - - if (intents_pending) { - EventServiceBuffer *esb = kernel_zalloc_check(sizeof(EventServiceBuffer)); - esb->ptr = buf; - esb->intents_pending = intents_pending; - list_init(&esb->list_node); - s_event_service_buffers = (EventServiceBuffer *)list_prepend( - (ListNode *)s_event_service_buffers, (ListNode *)esb); - return true; // we stole the buffer - } else { - return false; - } -} - -void event_service_handle_event(PebbleEvent *e) { - EventServiceEntry *service = s_event_services[e->type]; - if (service == NULL) { - return; - } - - bool stolen = false; - void **buf_ptr = event_get_buffer(e); - if (buf_ptr && *buf_ptr) { - stolen = prv_steal_buffer(*buf_ptr, service, e); // FIXME arguments? - } - - PebbleTask cur_task = pebble_task_get_current(); - for (unsigned i = 0; i < NumPebbleTask; i++) { - if (!prv_task_is_masked_out(e, i) && service->subscribers[i]) { - if (i == cur_task) { - // We will handle this inline, but that has to happen after we copy it into the queues - // because handling it inline could modify the event - continue; - } else { - if (!prv_event_service_send_event(service->subscribers[i], e)) { - PBL_LOG(LOG_LEVEL_INFO, "Queue full! %d not delivered to task %d!", - (int)e->type, (int)i); -#if !RELEASE - // For 3rd party apps, just close them. For a 1st party app or other task, reboot - // the watch - if (i == PebbleTask_App && app_manager_get_current_app_md()->is_unprivileged) { - app_manager_close_current_app(false); - } else if (i == PebbleTask_Worker && - worker_manager_get_current_worker_md()->is_unprivileged) { - worker_manager_close_current_worker(false); - } else { - PBL_ASSERTN(0); - } -#endif - } - } - } - } - - if (!prv_task_is_masked_out(e, cur_task) && service->subscribers[cur_task]) { - // we are on the current task, so we just tell the client to handle it inline - event_service_client_handle_event(e); - } - - if (stolen) { - // we stole the buffer from the event, NULL it out - *buf_ptr = NULL; - } -} - -// ------------------------------------------------------------------------------------------------- -static bool prv_buffer_find(ListNode *found_node, void *data) { - EventServiceBuffer *esb = (EventServiceBuffer *)found_node; - return (esb->ptr == data); -} - -static EventServiceBuffer* prv_get_esb_for_event(PebbleEvent *e) { - void **buf_ptr = event_get_buffer(e); - EventServiceBuffer *esb = NULL; - if (buf_ptr && *buf_ptr) { - esb = (EventServiceBuffer *)list_find((ListNode *)s_event_service_buffers, - prv_buffer_find, - *buf_ptr); - } - return esb; -} - -void* event_service_claim_buffer(PebbleEvent *e) { - EventServiceBuffer *esb = prv_get_esb_for_event(e); - if (esb) { - if (esb->intents_pending & CLAIMED_BIT) { - // For now only 1 claim at a time is needed, so lets keep things simple and just support that - PBL_LOG(LOG_LEVEL_WARNING, "Buffer already claimed"); - return NULL; - } else { - esb->intents_pending |= CLAIMED_BIT; - return esb; - } - } - return NULL; -} - -void event_service_free_claimed_buffer(void *ref) { - if (!ref) { - return; - } - - EventServiceBuffer *esb = ref; - - if (esb->intents_pending & CLAIMED_BIT) { - // If other events still need the buffer removing the claim marker will make things - // get cleaned up as usual. - uint16_t intents_pending = __sync_and_and_fetch(&esb->intents_pending, ~CLAIMED_BIT); - - if (!intents_pending) { - list_remove((ListNode *)esb, (ListNode **)&s_event_service_buffers, NULL); - if (esb->ptr) { - kernel_free(esb->ptr); - esb->ptr = NULL; - } - kernel_free(esb); - } - } -} - -// --------------------------------------------------------------------------------------------------------------- -static bool prv_service_filter(ListNode *node, void *tp) { - EventPluginUUIDEntry *info = (EventPluginUUIDEntry *)node; - return uuid_equal(&info->uuid, (Uuid *)tp); -} - -// --------------------------------------------------------------------------------------------------------------- -// TODO: We need to prune out entries from this list when they are no longer needed -// TODO: The applib should force a restriction on the number of plugin service UUIDs that an app can subscribe -// to at once. -static int16_t prv_get_plugin_index(const Uuid *uuid) { - int16_t result = -1; - - mutex_lock(s_plugin_list_mutex); - - // Look for this service UUID - ListNode *found; - ListNode *list = &s_plugin_list; - found = list_find(list, prv_service_filter, (void*)uuid); - if (found) { - result = ((EventPluginUUIDEntry *)found)->service_index; - goto unlock; - } - - EventPluginUUIDEntry *entry = kernel_zalloc_check(sizeof(EventPluginUUIDEntry)); - entry->service_index = ++s_next_service_index; - entry->uuid = *uuid; - list_append(list, &entry->list_node); - result = entry->service_index; - - char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(uuid, uuid_buffer); - PBL_LOG(LOG_LEVEL_DEBUG, "Registered plug-in service %s as index %d", uuid_buffer, result); - -unlock: - mutex_unlock(s_plugin_list_mutex); - return result; -} - - -//! @param uuid the UUID of the plugin service, or NULL to use uuid of the current process -//! @return non-negative service index, or -1 if error -DEFINE_SYSCALL(int16_t, sys_event_service_get_plugin_service_index, const Uuid * uuid) { - if (PRIVILEGE_WAS_ELEVATED && uuid != NULL) { - syscall_assert_userspace_buffer(uuid, sizeof(*uuid)); - } - if (uuid == NULL) { - uuid = &sys_process_manager_get_current_process_md()->uuid; - } - return prv_get_plugin_index(uuid); -} - -DEFINE_SYSCALL(void, sys_event_service_cleanup, PebbleEvent *e) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(e, sizeof(PebbleEvent)); - } - - EventServiceBuffer *esb = prv_get_esb_for_event(e); - - if (esb) { - uint16_t task_bit = 1 << pebble_task_get_current(); - uint16_t intents_pending = __sync_and_and_fetch(&esb->intents_pending, ~task_bit); - if (intents_pending) { - // zero out buf_ptr so it won't be freed by cleanup. Other tasks are still waiting to use it - void **buf_ptr = event_get_buffer(e); - *buf_ptr = NULL; - } else { - // free the EventServiceBuffer and free the data - list_remove((ListNode *)esb, (ListNode **)&s_event_service_buffers, NULL); - kernel_free(esb); - - event_deinit(e); - } - } -} diff --git a/src/fw/services/common/event_service.h b/src/fw/services/common/event_service.h deleted file mode 100644 index f4426de6ac..0000000000 --- a/src/fw/services/common/event_service.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/events.h" -#include "process_management/app_manager.h" - -typedef void (*EventServiceAddSubscriberCallback)(PebbleTask task); -typedef void (*EventServiceRemoveSubscriberCallback)(PebbleTask task); - -//! Call once during system startup -void event_service_system_init(void); - -//! Called from app task for each event type the app wants notifications for -void event_service_init(PebbleEventType type, EventServiceAddSubscriberCallback start_cb, - EventServiceRemoveSubscriberCallback stop_cb); -bool event_service_is_running(PebbleEventType event_type); -void event_service_handle_event(PebbleEvent *e); - -//! Subscribe to an event. This can only be called from the Kernel Main task -void event_service_subscribe_from_kernel_main(PebbleSubscriptionEvent *subscription); - -void event_service_handle_subscription(PebbleSubscriptionEvent *subscription); -void event_service_clear_process_subscriptions(PebbleTask task); - -//! Claim a buffer. This means it won't automatically get cleaned up -//! If you claim a buffer you must free it with event_service_free_claimed_buffer() -void* event_service_claim_buffer(PebbleEvent *e); -//! This function expects the pointer returned by event_service_claim_buffer -void event_service_free_claimed_buffer(void *ref); diff --git a/src/fw/services/common/evented_timer.c b/src/fw/services/common/evented_timer.c deleted file mode 100644 index d9e349d493..0000000000 --- a/src/fw/services/common/evented_timer.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "evented_timer.h" - -#include "kernel/events.h" -#include "os/tick.h" -#include "os/mutex.h" -#include "kernel/pbl_malloc.h" -#include "services/common/new_timer/new_timer.h" -#include "system/passert.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "process_management/app_manager.h" - -typedef struct EventedTimer { - ListNode list_node; - - //! The TimerID type used for sys_timers is a non-repeating integer that we also use as our key for finding - //! EventedTimers by id. - TimerID sys_timer_id; - - EventedTimerCallback callback; - void* callback_data; - - PebbleTask target_task; - - bool expired; // This gets set when the timer's callback runs - bool repeating; -} EventedTimer; - -//! The list of all the timers that have been created. -static ListNode* s_timer_list_head; - -static PebbleMutex * s_mutex; - -// ------------------------------------------------------------------------------------ -// Find timer by id -static bool prv_id_list_filter(ListNode* node, void* data) { - EventedTimer* timer = (EventedTimer*)node; - return timer->sys_timer_id == (TimerID)data; -} - -static EventedTimer* prv_find_timer(TimerID timer_id) -{ - if (timer_id == EVENTED_TIMER_INVALID_ID) { - return NULL; - } - - // Look for this timer in our linked list - ListNode* node = list_find(s_timer_list_head, prv_id_list_filter, (void*)(intptr_t)timer_id); - - return (EventedTimer *)node; -} - - -//! Retrieves details for a given timer handle and copies them out to user supplied memory. -//! This gets executed on the client's task and is called directly from the callback we put onto the -//! client task's event queue. -//! It accesses the privileged contents of the timer from the client's unprivileged task. -//! This call deletes the system timer and removes it from the timer list before returning unless -//! it is a repeating timer. -DEFINE_SYSCALL(void, sys_evented_timer_consume, TimerID timer_id, EventedTimerCallback* out_cb, - void** out_cb_data) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(out_cb, sizeof(*out_cb)); - syscall_assert_userspace_buffer(out_cb_data, sizeof(*out_cb_data)); - } - - mutex_lock(s_mutex); - - EventedTimer *timer = prv_find_timer(timer_id); - - // It's possible that the client made a call to delete the timer just - // after the timer executed (from the timer task) and posted the PEBBLE_CALLBACK_EVENT - // to the client's event queue. In this case, the timer will no - // longer be in our timers list by the time the event arrives and gets processed here. - if (!timer) { - mutex_unlock(s_mutex); - *out_cb = 0; - return; - } - - *out_cb = timer->callback; - *out_cb_data = timer->callback_data; - - if (timer->repeating) { - mutex_unlock(s_mutex); - } else { - list_remove(&timer->list_node, &s_timer_list_head, NULL); - mutex_unlock(s_mutex); - new_timer_delete(timer->sys_timer_id); - kernel_free(timer); - } -} - - -//! Wrapper for the user supplied callback. We installed this callback by posting a PEBBLE_CALLBACK_EVENT -//! to the client's event queue. This gets executed on the target task. -static void prv_evented_timer_event_callback(void* data) { - // Note this may be running on the app task, so we have to jump through hoops to read kernel memory. - - TimerID timer_id = (TimerID) data; - - EventedTimerCallback timer_cb; - void* timer_cb_data; - - // Get the user supplied callback pointer and data, remove the timer from our list, and delete it. - sys_evented_timer_consume(timer_id, &timer_cb, &timer_cb_data); - - if (!timer_cb) { - // We've already cancelled this timer, just abort. - return; - } - - timer_cb(timer_cb_data); -} - - -//! Called on the timer task. From here we need to generate a callback on the client's task. -static void prv_sys_timer_callback(void* cb_data) { - PBL_ASSERT_TASK(PebbleTask_NewTimers); - TimerID id = (TimerID)cb_data; - - mutex_lock(s_mutex); - - EventedTimer *timer = prv_find_timer(id); - if (!timer) { - // If there's no timer in the list, that means we've been cancelled already. - // When we cancel a timer, we immediately free the EventedTimer* and - // call new_time_delete() to delete the timer. It's possible however that this - // callback got entered after we grabbed the mutex but before we issued the - // new_timer_delete(), in which case we would have been blocked at the beginning - // of this method trying to grab the mutex while the timer was deleted. - // To handle this, we check here to see if the EventedTimer got deleted on us and - // return immediately if so. - mutex_unlock(s_mutex); - return; - } - - timer->expired = !timer->repeating; - - mutex_unlock(s_mutex); - - PebbleEvent e = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_evented_timer_event_callback, - .data = (void*)(intptr_t)id, - } - }; - - switch (timer->target_task) { - case PebbleTask_KernelMain: - event_put(&e); - break; - case PebbleTask_App: - case PebbleTask_Worker: - process_manager_send_event_to_process(timer->target_task, &e); - break; - default: - PBL_CROAK("Invalid task %s", pebble_task_get_name(pebble_task_get_current())); - } -} - - -// ======================================================================================================== -// External API - -void evented_timer_init(void) { - s_mutex = mutex_create(); -} - -void evented_timer_clear_process_timers(PebbleTask task) { - PBL_ASSERT_TASK(PebbleTask_KernelMain); - - mutex_lock(s_mutex); - - ListNode* iter = s_timer_list_head; - while (iter) { - EventedTimer* timer = (EventedTimer*) iter; - ListNode* next = list_get_next(iter); - - if (timer->target_task == task) { - list_remove(iter, &s_timer_list_head, NULL); - // The delete operation will stop it for us - new_timer_delete(timer->sys_timer_id); - kernel_free(timer); - } - - iter = next; - } - - mutex_unlock(s_mutex); -} - -EventedTimerID evented_timer_register_or_reschedule(EventedTimerID timer_id, uint32_t timeout_ms, - EventedTimerCallback callback, void *data) { - if (timer_id != EVENTED_TIMER_INVALID_ID && evented_timer_reschedule(timer_id, timeout_ms)) { - return timer_id; - } - return evented_timer_register(timeout_ms, false, callback, data); -} - -EventedTimerID evented_timer_register(uint32_t timeout_ms, - bool repeating, - EventedTimerCallback callback, - void* data) { - PebbleTask current_task = pebble_task_get_current(); - PBL_ASSERT(current_task == PebbleTask_KernelMain || current_task == PebbleTask_App - || current_task == PebbleTask_Worker, - "Invalid task: %s", pebble_task_get_name(current_task)); - - // Handle a lazy client. Timers are useful for handling things "not right now, but soon". - if (timeout_ms == 0) { - timeout_ms = 1; - } - - mutex_lock(s_mutex); - - EventedTimer* new_timer = kernel_malloc_check(sizeof(EventedTimer)); - - *new_timer = (EventedTimer) { - .list_node = { 0 }, - .sys_timer_id = TIMER_INVALID_ID, // We set this below - .callback = callback, - .callback_data = data, - .target_task = current_task, - .repeating = repeating, - .expired = false - }; - - new_timer->sys_timer_id = new_timer_create(); - PBL_ASSERTN(new_timer->sys_timer_id != TIMER_INVALID_ID); - - s_timer_list_head = list_prepend(s_timer_list_head, &new_timer->list_node); - - uint32_t flags = repeating ? TIMER_START_FLAG_REPEATING : 0; - bool success = new_timer_start(new_timer->sys_timer_id, timeout_ms, prv_sys_timer_callback, - (void*)(intptr_t)new_timer->sys_timer_id, flags); - PBL_ASSERTN(success); - - mutex_unlock(s_mutex); - return new_timer->sys_timer_id; -} - - -bool evented_timer_reschedule(EventedTimerID timer_id, uint32_t timeout_ms) { - if (timeout_ms == 0) { - timeout_ms = 1; - } - mutex_lock(s_mutex); - - // This will detect an invalid timer ID, or one that already ran on the client's task and got deleted - // already - EventedTimer* timer = prv_find_timer(timer_id); - if (!timer) { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempting to reschedule an invalid timer (id=%u)", - (unsigned)timer_id); - mutex_unlock(s_mutex); - return false; - } - - PBL_ASSERT(timer->target_task == pebble_task_get_current(), "%u vs %u", - timer->target_task, pebble_task_get_current()); - - // This will detect if the timer callback has already executed on the timer task. - // If the timer is still in our timer's list but is expired, - // it means the event posted by the timer task has not yet arrived at the - // client's task. - if (timer->expired) { - mutex_unlock(s_mutex); - return false; - } - - // At this point, we are assured that the timer callback either has not yet run - // or that the callback is currently blocked trying to grab s_mutex. - // new_timer_start() will reliably tell us if it was able to reschedule - // the timer before the callback got entered. - // If it returns false, it means the callback was entered before it was able to reschedule it. - uint32_t flags = timer->repeating ? TIMER_START_FLAG_REPEATING : - TIMER_START_FLAG_FAIL_IF_EXECUTING; - bool success = new_timer_start(timer->sys_timer_id, timeout_ms, prv_sys_timer_callback, - (void*)(intptr_t)timer->sys_timer_id, flags); - - mutex_unlock(s_mutex); - return success; -} - - -void evented_timer_cancel(EventedTimerID timer_id) { - if (timer_id == EVENTED_TIMER_INVALID_ID) { - return; - } - - mutex_lock(s_mutex); - - // Find this timer and validate it - EventedTimer* timer = prv_find_timer(timer_id); - if (!timer) { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempting to cancel an invalid timer (id=%u)", (unsigned)timer_id); - mutex_unlock(s_mutex); - return; - } - - new_timer_delete(timer->sys_timer_id); // This automatically stops the timer for us first - list_remove(&timer->list_node, &s_timer_list_head, NULL); - kernel_free(timer); - - mutex_unlock(s_mutex); -} - -bool evented_timer_exists(EventedTimerID timer_id){ - return prv_find_timer(timer_id) != NULL; -} - -bool evented_timer_is_current_task(EventedTimerID timer_id){ - EventedTimer* timer = prv_find_timer(timer_id); - PBL_ASSERTN(timer); - return timer->target_task == pebble_task_get_current(); -} - -void evented_timer_reset(void) { - s_timer_list_head = 0; -} - -void *evented_timer_get_data(EventedTimerID timer_id) { - EventedTimer* timer = prv_find_timer(timer_id); - if (timer) { - return timer->callback_data; - } else { - return NULL; - } -} diff --git a/src/fw/services/common/firmware_update.c b/src/fw/services/common/firmware_update.c deleted file mode 100644 index 1d7b0031f0..0000000000 --- a/src/fw/services/common/firmware_update.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/firmware_update.h" - -#include "apps/core_apps/progress_ui_app.h" -#include "flash_region/flash_region.h" -#include "kernel/event_loop.h" -#include "kernel/system_message.h" -#include "kernel/ui/modals/modal_manager.h" -#include "process_management/app_manager.h" -#include "process_management/app_manager.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" -#include "util/math.h" - -#include "FreeRTOS.h" -#include "semphr.h" - -#include -#include -#include - -// The legacy firmware UI breaks firmware and resources into 50% chunks. In reality since these -// parts are not of equal sizes, one of these '50%' blocks will take longer than the -// other. Additionally, iOS/Android and the watch are not in sync over what this should look like. -// -// The new UI allows the phone to give us more info up front about how much data will be -// transmitted and also cleanly drive a re-start of the UI to a non-0 percentage if the FW update -// is being resumed. Newer implementations should this! (See PBL-42130) - -static SemaphoreHandle_t s_firmware_update_semaphore; -static bool s_is_recovery_fw = false; -static FirmwareUpdateStatus s_update_status = FirmwareUpdateStopped; - -typedef struct LegacyFwUpdateCompletionStatus { - uint32_t recovery_percent_completion; - uint32_t resource_percent_completion; - uint32_t firmware_percent_completion; -} LegacyFwUpdateCompletionStatus; - -typedef struct FwUpdateCompletionStatus { - uint32_t bytes_transferred; - uint32_t total_size; -} FwUpdateCompletionStatus; - -typedef struct { - bool use_legacy_mode; - union { - FwUpdateCompletionStatus status; - LegacyFwUpdateCompletionStatus legacy_status; - }; -} FwUpdateCurrentCompletionStatus; - -static FwUpdateCurrentCompletionStatus s_current_completion_status = { 0 }; - -// -// Start handlers for legacy percentage status handling. Someday, we can hopefully -// remove them outright and it should just involve deleting these routines -// - -static bool prv_legacy_firmware_update_get_percent_progress(uint32_t *progress) { - if (!s_current_completion_status.use_legacy_mode) { - return false; - } - - LegacyFwUpdateCompletionStatus *status = &s_current_completion_status.legacy_status; - if (s_is_recovery_fw) { - *progress = MIN(100, status->recovery_percent_completion); - } else { - *progress = - MIN(100, (status->resource_percent_completion + status->firmware_percent_completion) / 2); - } - - return true; -} - -static bool prv_legacy_handle_progress(PebblePutBytesEvent *event) { - if (!s_current_completion_status.use_legacy_mode) { - return false; - } - - LegacyFwUpdateCompletionStatus *status = &s_current_completion_status.legacy_status; - switch (event->object_type) { - case ObjectFirmware: - status->firmware_percent_completion = event->progress_percent; - break; - - case ObjectSysResources: - status->resource_percent_completion = event->progress_percent; - break; - - case ObjectRecovery: - status->recovery_percent_completion = event->progress_percent; - break; - - default: - PBL_LOG(LOG_LEVEL_ERROR, "Unexpected Object type %u", event->object_type); - break; - } - - return true; -} - -static bool prv_legacy_completion_status_init(PebbleSystemMessageEvent *event) { - if (event->type != PebbleSystemMessageFirmwareUpdateStartLegacy) { - return false; - } - - s_current_completion_status.use_legacy_mode = true; - LegacyFwUpdateCompletionStatus *status = - &s_current_completion_status.legacy_status; - - *status = (LegacyFwUpdateCompletionStatus) { - .recovery_percent_completion = 0, - .resource_percent_completion = 0, - .firmware_percent_completion = 0 - }; - - return true; -} - -// End Legacy completion handlers - -bool firmware_update_is_in_progress(void) { - return s_update_status == FirmwareUpdateRunning; -} - -FirmwareUpdateStatus firmware_update_current_status(void) { - return s_update_status; -} - -void firmware_update_init(void) { - vSemaphoreCreateBinary(s_firmware_update_semaphore); - PBL_ASSERTN(s_firmware_update_semaphore != NULL); -} - -static void prv_initialize_completion_status(PebbleSystemMessageEvent *event) { - if (prv_legacy_completion_status_init(event)) { - return; - } - - s_current_completion_status.use_legacy_mode = false; - FwUpdateCompletionStatus *status = &s_current_completion_status.status; - *status = (FwUpdateCompletionStatus) { - .bytes_transferred = event->bytes_transferred, - .total_size = event->total_transfer_size - }; -} - -// Initialization for a firmware update could involve an erase of 8 flash -// sectors. Worst case timing for an erase is ~5s, so let's set our timeout to -// 40s to give us some headroom. -#define FIRMWARE_TIMEOUT_MS (1000 * 40) - -static FirmwareUpdateStatus prv_firmware_update_start(PebbleSystemMessageEvent *event) { - if (battery_monitor_critical_lockout()) { - return FirmwareUpdateCancelled; // Disable firmware updates on low power - } - - if (xSemaphoreTake(s_firmware_update_semaphore, 0) == pdFALSE) { - return FirmwareUpdateStopped; - } - - FirmwareUpdateStatus result = s_update_status; - if (result != FirmwareUpdateRunning) { - prv_initialize_completion_status(event); - - services_set_runlevel(RunLevel_FirmwareUpdate); - modal_manager_pop_all(); - - static const ProgressUIAppArgs s_update_args = { - .progress_source = PROGRESS_UI_SOURCE_FW_UPDATE, - }; - app_manager_launch_new_app(&(AppLaunchConfig) { - .md = progress_ui_app_get_info(), - .common.args = &s_update_args, - .restart = true, - }); - put_bytes_expect_init(FIRMWARE_TIMEOUT_MS); - result = FirmwareUpdateRunning; - } - - xSemaphoreGive(s_firmware_update_semaphore); - return result; -} - -static void prv_handle_firmware_update_start_msg(PebbleSystemMessageEvent *event) { - FirmwareUpdateStatus result = prv_firmware_update_start(event); - s_update_status = result; - PBL_ASSERTN((result == FirmwareUpdateRunning) || - (result == FirmwareUpdateStopped) || - (result == FirmwareUpdateCancelled)); - system_message_send_firmware_start_response(result); -} - -static void prv_firmware_update_finish(bool failed) { - if (xSemaphoreTake(s_firmware_update_semaphore, 0) == pdFALSE) { - return; - } - - if (failed) { - // If we failed, we can set it back to normal. If we succeeded, we'll reboot shortly. - // We don't know the runlevel that was set before, so we assume it was Normal. - services_set_runlevel(RunLevel_Normal); - } - - s_update_status = failed ? FirmwareUpdateFailed : FirmwareUpdateStopped; - - xSemaphoreGive(s_firmware_update_semaphore); -} - -unsigned int firmware_update_get_percent_progress(void) { - if (!firmware_update_is_in_progress()) { - return 0; - } - - uint32_t progress = 0; - if (prv_legacy_firmware_update_get_percent_progress(&progress)) { - return progress; - } - - FwUpdateCompletionStatus *status = &s_current_completion_status.status; - return (status->bytes_transferred * 100) / status->total_size; -} - -void firmware_update_event_handler(PebbleSystemMessageEvent* event) { - switch (event->type) { - case PebbleSystemMessageFirmwareUpdateStartLegacy: - case PebbleSystemMessageFirmwareUpdateStart: - prv_handle_firmware_update_start_msg(event); - break; - - case PebbleSystemMessageFirmwareUpdateFailed: - prv_firmware_update_finish(true /* failed */); - break; - - case PebbleSystemMessageFirmwareUpdateComplete: - prv_firmware_update_finish(false /* failed */); - break; - - default: - break; - } -} - -static void prv_handle_progress(PebblePutBytesEvent *event) { - if (prv_legacy_handle_progress(event)) { - return; - } - - if (event->type != PebblePutBytesEventTypeProgress) { - return; // Only progress events report bytes_transferred updates - } - - FwUpdateCompletionStatus *status = &s_current_completion_status.status; - status->bytes_transferred += event->bytes_transferred; -} - -void firmware_update_pb_event_handler(PebblePutBytesEvent *event) { - if (!firmware_update_is_in_progress()) { - return; // not my pb transfer - } - - switch (event->type) { - case PebblePutBytesEventTypeStart: - s_is_recovery_fw = (event->object_type == ObjectRecovery); - prv_handle_progress(event); - break; - - case PebblePutBytesEventTypeProgress: - prv_handle_progress(event); - break; - - case PebblePutBytesEventTypeCleanup: - if (event->failed) { - // exit now in case the phone is gone - prv_firmware_update_finish(true /* failed */); - } - break; - - case PebblePutBytesEventTypeInitTimeout: - PBL_LOG(LOG_LEVEL_WARNING, "Timed out waiting for putbytes request from phone"); - prv_firmware_update_finish(true /* failed */); - break; - - default: - break; - } -} diff --git a/src/fw/services/common/firmware_update.h b/src/fw/services/common/firmware_update.h deleted file mode 100644 index aae9ef9d48..0000000000 --- a/src/fw/services/common/firmware_update.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "kernel/events.h" - -void firmware_update_init(void); - -unsigned int firmware_update_get_percent_progress(void); - -void firmware_update_event_handler(PebbleSystemMessageEvent* event); -void firmware_update_pb_event_handler(PebblePutBytesEvent *event); - -typedef enum { - FirmwareUpdateStopped = 0, - FirmwareUpdateRunning = 1, - FirmwareUpdateCancelled = 2, - FirmwareUpdateFailed = 3, -} FirmwareUpdateStatus; - -FirmwareUpdateStatus firmware_update_current_status(void); - -bool firmware_update_is_in_progress(void); diff --git a/src/fw/services/common/get_bytes/get_bytes.h b/src/fw/services/common/get_bytes/get_bytes.h deleted file mode 100644 index 66202c0a7b..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Different types of objects that can be transferred over GetBytes -typedef enum { - GetBytesObjectUnknown = 0x00, - GetBytesObjectCoredump = 0x01, - GetBytesObjectFile = 0x02, - GetBytesObjectFlash = 0x03 -} GetBytesObjectType; - -// Possible values for GetBytesRspObjectInfo.error_code -typedef enum { - GET_BYTES_OK = 0, - GET_BYTES_MALFORMED_COMMAND = 1, - GET_BYTES_ALREADY_IN_PROGRESS = 2, - GET_BYTES_DOESNT_EXIST = 3, - GET_BYTES_CORRUPTED = 4, -} GetBytesInfoErrorCode; diff --git a/src/fw/services/common/get_bytes/get_bytes_storage.h b/src/fw/services/common/get_bytes/get_bytes_storage.h deleted file mode 100644 index 30fa40df9b..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes_storage.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "services/common/get_bytes/get_bytes.h" - -typedef enum { - GetBytesStorageTypeUnknown, - GetBytesStorageTypeCoredump, - GetBytesStorageTypeFile, - GetBytesStorageTypeFlash -} GetBytesStorageType; - -struct GetBytesStorageImplementation; -typedef struct GetBytesStorageImplementation GetBytesStorageImplementation; - -typedef struct { - // A struct full of function pointers that implements a storage API - const GetBytesStorageImplementation *impl; - - //! A void pointer that the GetBytesStorageImplementation is free to stash stuff into - void *impl_data; - - //! The offset into the storage we've initialized. Updated by pb_storage_append. pb_storage_init - //! may set this to a non-zero value. - uint32_t current_offset; -} GetBytesStorage; - -// info used by the setup routines depending on the implementation -typedef struct { - //! Used by GetBytesStorageTypeFile - char *filename; - //! Used by GetBytesStorageTypeFlash - uint32_t flash_start_addr; - uint32_t flash_len; - //! Used by GetBytesStorageTypeCoredump - bool only_get_new_coredump; -} GetBytesStorageInfo; - -//! Set up the storage. This may include allocating memory, open a file descriptor, etc. -bool gb_storage_setup(GetBytesStorage *storage, GetBytesObjectType object_type, - GetBytesStorageInfo *info); - -//! Retrieve the size of the object that is to be sent -GetBytesInfoErrorCode gb_storage_get_size(GetBytesStorage *storage, uint32_t *size); - -//! Read a chunk of the object into a buffer -bool gb_storage_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); - -//! Cleanup the storage. -void gb_storage_cleanup(GetBytesStorage *storage, bool successful); diff --git a/src/fw/services/common/get_bytes/get_bytes_storage_coredump.h b/src/fw/services/common/get_bytes/get_bytes_storage_coredump.h deleted file mode 100644 index 067a8647ea..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes_storage_coredump.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "get_bytes_storage.h" - -bool gb_storage_coredump_setup(GetBytesStorage *storage, GetBytesObjectType object_type, - GetBytesStorageInfo *info); - -GetBytesInfoErrorCode gb_storage_coredump_get_size(GetBytesStorage *storage, uint32_t *size); - -bool gb_storage_coredump_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); - -void gb_storage_coredump_cleanup(GetBytesStorage *storage, bool successful); diff --git a/src/fw/services/common/get_bytes/get_bytes_storage_file.c b/src/fw/services/common/get_bytes/get_bytes_storage_file.c deleted file mode 100644 index 4ce415c624..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes_storage_file.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "get_bytes_storage_file.h" - -#include "services/normal/filesystem/pfs.h" - -bool gb_storage_file_setup(GetBytesStorage *storage, GetBytesObjectType object_type, - GetBytesStorageInfo *info) { - const int fd = pfs_open(info->filename, OP_FLAG_READ, FILE_TYPE_STATIC, 0); - storage->impl_data = (void*)(uintptr_t) fd; - return fd >= 0; -} - -GetBytesInfoErrorCode gb_storage_file_get_size(GetBytesStorage *storage, uint32_t *size) { - const int fd = (int) storage->impl_data; - *size = pfs_get_file_size(fd); - return GET_BYTES_OK; -} - -bool gb_storage_file_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len) { - const int fd = (int) storage->impl_data; - storage->current_offset += len; - return (pfs_read(fd, buffer, len) > 0); -} - -void gb_storage_file_cleanup(GetBytesStorage *storage, bool successful) { - const int fd = (int) storage->impl_data; - pfs_close(fd); -} diff --git a/src/fw/services/common/get_bytes/get_bytes_storage_file.h b/src/fw/services/common/get_bytes/get_bytes_storage_file.h deleted file mode 100644 index b8f16610c9..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes_storage_file.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "get_bytes_storage.h" - -bool gb_storage_file_setup(GetBytesStorage *storage, GetBytesObjectType object_type, - GetBytesStorageInfo *info); - -GetBytesInfoErrorCode gb_storage_file_get_size(GetBytesStorage *storage, uint32_t *size); - -bool gb_storage_file_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); - -void gb_storage_file_cleanup(GetBytesStorage *storage, bool successful); diff --git a/src/fw/services/common/get_bytes/get_bytes_storage_flash.c b/src/fw/services/common/get_bytes/get_bytes_storage_flash.c deleted file mode 100644 index a8f10e794f..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes_storage_flash.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "get_bytes_storage.h" -#include "drivers/flash.h" -#include "kernel/pbl_malloc.h" -#include "flash_region/flash_region.h" - - -bool gb_storage_flash_setup( - GetBytesStorage *storage, GetBytesObjectType object_type, GetBytesStorageInfo *info) { - if (!info->flash_len || (info->flash_start_addr + info->flash_len) > BOARD_NOR_FLASH_SIZE) { - return false; - } - storage->impl_data = kernel_zalloc_check(sizeof(*info)); - memcpy(storage->impl_data, info, sizeof(*info)); - - return true; -} - -GetBytesInfoErrorCode gb_storage_flash_get_size(GetBytesStorage *storage, uint32_t *size) { - *size = ((GetBytesStorageInfo *)storage->impl_data)->flash_len; - return GET_BYTES_OK; -} - -bool gb_storage_flash_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len) { - uint32_t start_offset = ((GetBytesStorageInfo *)storage->impl_data)->flash_start_addr; - flash_read_bytes(buffer, storage->current_offset + start_offset, len); - storage->current_offset += len; - return true; -} - -void gb_storage_flash_cleanup(GetBytesStorage *storage, bool successful) { - kernel_free(storage->impl_data); -} diff --git a/src/fw/services/common/get_bytes/get_bytes_storage_flash.h b/src/fw/services/common/get_bytes/get_bytes_storage_flash.h deleted file mode 100644 index 5c63c41fc8..0000000000 --- a/src/fw/services/common/get_bytes/get_bytes_storage_flash.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "get_bytes_storage.h" - -bool gb_storage_flash_setup(GetBytesStorage *storage, GetBytesObjectType object_type, - GetBytesStorageInfo *info); - -GetBytesInfoErrorCode gb_storage_flash_get_size(GetBytesStorage *storage, uint32_t *size); - -bool gb_storage_flash_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len); - -void gb_storage_flash_cleanup(GetBytesStorage *storage, bool successful); diff --git a/src/fw/services/common/hrm/hrm_manager.c b/src/fw/services/common/hrm/hrm_manager.c deleted file mode 100644 index 291392cfc7..0000000000 --- a/src/fw/services/common/hrm/hrm_manager.c +++ /dev/null @@ -1,828 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "hrm_manager.h" -#include "hrm_manager_private.h" - -#include "console/prompt.h" -#include "drivers/hrm.h" -#include "kernel/pbl_malloc.h" -#include "mfg/mfg_info.h" -#include "os/tick.h" -#include "process_management/app_manager.h" -#include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/system_task.h" -#include "syscall/syscall_internal.h" -#include "system/hexdump.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/math.h" -#include "util/size.h" - -#include "FreeRTOS.h" -#include "queue.h" - -#include - -#define HRM_DEBUG 0 - -#if HRM_DEBUG -#define HRM_LOG(fmt, ...) \ - do { \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## __VA_ARGS__); \ - } while (0) -#define HRM_HEXDUMP(data, length) \ - do { \ - PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)data, length); \ - } while (0) -#else -#define HRM_LOG(fmt, ...) -#define HRM_HEXDUMP(data, length) -#endif - -static struct HRMManagerState s_manager_state; -static bool s_hrm_present = false; - -// Forward declarations -static void prv_update_enable_timer_cb(void *context); - - -static bool prv_match_session_ref(ListNode *found_node, void *data) { - const HRMSubscriberState *state = (HRMSubscriberState *)found_node; - return (state->session_ref == (HRMSessionRef)data); -} - -T_STATIC HRMSubscriberState * prv_get_subscriber_state_from_ref(HRMSessionRef session) { - ListNode *node = list_find(s_manager_state.subscribers, prv_match_session_ref, - (void *)(uintptr_t)session); - return (HRMSubscriberState *)node; -} - - -typedef struct { - AppInstallId app_id; - PebbleTask task; -} HRMAppIdAndTask; - -static bool prv_match_app_id(ListNode *found_node, void *data) { - HRMAppIdAndTask *context = (HRMAppIdAndTask *)data; - const HRMSubscriberState *state = (HRMSubscriberState *)found_node; - return ((state->app_id == context->app_id) && (state->task == context->task)); -} - -T_STATIC HRMSubscriberState * prv_get_subscriber_state_from_app_id(PebbleTask task, - AppInstallId app_id) { - HRMAppIdAndTask context = { - .app_id = app_id, - .task = task, - }; - - ListNode *node = list_find(s_manager_state.subscribers, prv_match_app_id, &context); - return (HRMSubscriberState *)node; -} - -// Return true if this subscriber needs to be sent a HRMEvent_SubscriptionExpiring event -static bool prv_needs_expiring_event(HRMSubscriberState *state, time_t utc_now) { - if (state->sent_expiration_event) { - return false; - } - return (state->expire_utc && (utc_now >= state->expire_utc - - MAX(HRM_SUBSCRIPTION_EXPIRING_WARNING_SEC, (int)state->update_interval_s))); -} - -T_STATIC void prv_read_event_from_buffer_and_consume(CircularBuffer *buffer, - PebbleHRMEvent *event) { - const uint16_t total_size = sizeof(*event); - uint16_t remaining = total_size; - uint8_t *out_buf = (uint8_t *)event; - while (remaining > 0) { - const uint8_t *data_out; - uint16_t length_out; - - const bool success = circular_buffer_read(buffer, remaining, &data_out, &length_out); - PBL_ASSERTN(success); - memcpy(out_buf, data_out, length_out); - - out_buf += length_out; - remaining -= length_out; - } - PBL_ASSERTN(remaining == 0); - - circular_buffer_consume(buffer, sizeof(*event)); -} - -static void prv_remove_and_free_subscription(HRMSubscriberState *state) { - list_remove((ListNode *)state, &s_manager_state.subscribers, NULL); - kernel_free(state); -} - -#if UNITTEST -// Used by unit tests -T_STATIC TimerID prv_get_timer_id(void) { - return s_manager_state.update_enable_timer_id; -} - -// Used by unit tests -T_STATIC uint32_t prv_num_system_task_events_queued(void) { - uint16_t avail_bytes = circular_buffer_get_read_space_remaining( - &s_manager_state.system_task_event_buffer); - return avail_bytes / sizeof(PebbleHRMEvent); -} -#endif - -static void prv_handle_accel_data(void * data) { - PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_NewTimers); - - uint64_t timestamp_ms; - uint32_t num_new_samples = sys_accel_manager_get_num_samples( - s_manager_state.accel_state, ×tamp_ms); - - mutex_lock(s_manager_state.accel_data_lock); - - // Only read as many as we have space to store - const size_t MAX_BUFFERED_SAMPLES = ARRAY_LENGTH(s_manager_state.accel_data.data); - if ((s_manager_state.accel_data.num_samples + num_new_samples) > MAX_BUFFERED_SAMPLES) { - analytics_inc(ANALYTICS_DEVICE_METRIC_HRM_ACCEL_DATA_MISSING, AnalyticsClient_System); - num_new_samples = MAX_BUFFERED_SAMPLES - s_manager_state.accel_data.num_samples; - } - - void *write_ptr = &s_manager_state.accel_data.data[s_manager_state.accel_data.num_samples]; - memcpy(write_ptr, s_manager_state.accel_manager_buffer, num_new_samples * sizeof(AccelRawData)); - - s_manager_state.accel_data.num_samples += num_new_samples; - - mutex_unlock(s_manager_state.accel_data_lock); - - sys_accel_manager_consume_samples(s_manager_state.accel_state, num_new_samples); -} - -// Return true if this is a stable BPM reading. This is called each time we power the sensor off -// or receive a new HRMData update from the sensor driver. It returns true if we should trust the -// BPMData hrm_bpm and hrm_quality fields or not. -// -// In the current rev of the sensor FW, we need to take the following approach to filter out -// good readings: -// 1.) After first turning on the sensor, wait until the quality is "Good" or better, but wait -// no more than HRM_SENSOR_SPIN_UP_SEC seconds. -// 2.) During sensor startup, the sensor will occasionally send erroneous "Excellent" readings. -// We can tell they are erroneous because the BPM will be 0. These erroneous readings need to -// be ignored. We ignore any reading where the BPM is below HRM_SENSOR_MIN_VALID_BPM_READING. -// 3.) Once the quality is "Good", we have to ignore all other quality readings (except off-wrist) -// because they don't mean anything in this version of the sensor FW. -// 4.) If we suddently go "off-wrist", wait for another "Good" or better. -// -// So, for the first 0 to HRM_SENSOR_SPIN_UP_SEC seconds after turning the sensor on or first -// contacting the wrist after being off-wrist, the readings can be unstable and this method will -// return false during that time. -// -// @param[in] data pointer to last received HRMdata or NULL if sensor powered off -// @return true if sensor is stable -static bool prv_is_sensor_stable(const HRMData *data) { - // Passing a NULL data pointer means reset our state - if (!data) { - s_manager_state.sensor_stable = false; - s_manager_state.sensor_start_ticks = 0; - return false; - } - - // Ignore the "no accel" quality reading samples. We seem to get these occasionally - // and don't want them to mess up our state. - if (data->hrm_quality == HRMQuality_NoAccel) { - return s_manager_state.sensor_stable; - } - - // If we were stable before, just make sure we are still stable - if (s_manager_state.sensor_stable) { - // If we just went on-wrist or off-wrist, reset the stable state - bool off_wrist_now = (data->hrm_quality == HRMQuality_OffWrist); - if (off_wrist_now != s_manager_state.off_wrist_when_stable) { - s_manager_state.sensor_stable = false; - s_manager_state.sensor_start_ticks = 0; - return false; - } - return true; - } - - // Start the tick counter if this is the first reading since power-on or off-wrist - if (s_manager_state.sensor_start_ticks == 0) { - s_manager_state.sensor_start_ticks = rtc_get_ticks(); - } - - // When first powering up, we can get "Excellent" quality readings the first few seconds, even - // though the BPM is 0. Let's fix the quality if the BPM is too low to be valid - HRMQuality quality = data->hrm_quality; - if ((data->hrm_bpm < HRM_SENSOR_MIN_VALID_BPM_READING) - && (data->hrm_quality > HRMQuality_NoSignal)) { - quality = HRMQuality_NoSignal; - } - - // Update our state - if (quality >= HRMQuality_Good) { - // Once we receive at least one good reading, we are stable - s_manager_state.sensor_stable = true; - s_manager_state.off_wrist_when_stable = false; - } else { - // We haven't yet received a good reading yet. Wait for a timeout... - RtcTicks elapased_ticks = rtc_get_ticks() - s_manager_state.sensor_start_ticks; - RtcTicks max_startup_time = milliseconds_to_ticks(HRM_SENSOR_SPIN_UP_SEC * MS_PER_SECOND); - if (elapased_ticks >= max_startup_time) { - // If it's been past the tolerable startup time, we have a valid reading - even though it - // may indicate off-wrist. - s_manager_state.sensor_stable = true; - s_manager_state.off_wrist_when_stable = (quality == HRMQuality_OffWrist); - } - } - - return s_manager_state.sensor_stable; -} - -T_STATIC bool prv_can_turn_sensor_on(void) { -#if IS_BIGBOARD || RECOVERY_FW - return true; -#endif - - return s_manager_state.enabled_run_level && - s_manager_state.enabled_charging_state && - activity_prefs_heart_rate_is_enabled(); -} - -// Figure out if we should enable the HR sensor or not based on all subscribers and their -// desired sampling periods. Must be called from the KernelBG task. -static void prv_update_hrm_enable_system_cb(void *unused) { - const time_t utc_now = rtc_get_time(); - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - mutex_lock_recursive(s_manager_state.lock); - { - bool turn_sensor_on = false; - // How many ms until we need the sensor on again. INT32_MAX means we don't need to turn it on - // again - int32_t remaining_ms = INT32_MAX; - - if (prv_can_turn_sensor_on()) { - RtcTicks cur_ticks = rtc_get_ticks(); - int32_t remaining_ticks = INT32_MAX; - const int32_t spin_up_ticks = (int32_t)milliseconds_to_ticks( - HRM_SENSOR_SPIN_UP_SEC * MS_PER_SECOND); - - // Loop through each of the subscribers and figure out when the next one needs an update - HRMSubscriberState *state = (HRMSubscriberState *) s_manager_state.subscribers; - for (; state != NULL; state = (HRMSubscriberState *) state->list_node.next) { - if (state->expire_utc && (utc_now >= state->expire_utc)) { - // Ignore expired subscriptions - continue; - } - int64_t subscriber_age_ticks; - if (state->last_valid_ticks) { - subscriber_age_ticks = cur_ticks - state->last_valid_ticks; - } else { - // Never got an update yet - subscriber_age_ticks = milliseconds_to_ticks(state->update_interval_s * MS_PER_SECOND); - } - int64_t subscriber_remaining_ticks = - (int64_t)milliseconds_to_ticks(state->update_interval_s * MS_PER_SECOND) - - subscriber_age_ticks - spin_up_ticks; - subscriber_remaining_ticks = MAX(0, subscriber_remaining_ticks); - - remaining_ticks = MIN(remaining_ticks, subscriber_remaining_ticks); - } - - // How many milliseconds till we need to send the next sensor reading - remaining_ms = ticks_to_milliseconds(remaining_ticks); - HRM_LOG("Need sensor on again in %"PRIu32" sec", remaining_ms / MS_PER_SECOND); - turn_sensor_on = (remaining_ms <= 0); - } - - if (turn_sensor_on && !hrm_is_enabled(HRM)) { - // Turn on the sensor now - HRM_LOG("Turning on HR sensor"); - - s_manager_state.accel_state = sys_accel_manager_data_subscribe( - ACCEL_SAMPLING_25HZ, prv_handle_accel_data, NULL, PebbleTask_NewTimers); - accel_manager_set_jitterfree_sampling_rate(s_manager_state.accel_state, - HRM_MANAGER_ACCEL_RATE_MILLIHZ); - sys_accel_manager_set_sample_buffer( - s_manager_state.accel_state, s_manager_state.accel_manager_buffer, - HRM_MANAGER_ACCEL_MANAGER_SAMPLES_PER_UPDATE); - - hrm_enable(HRM); - - // Don't need the re-enable timer to fire - new_timer_stop(s_manager_state.update_enable_timer_id); - - } else if (!turn_sensor_on && hrm_is_enabled(HRM)) { - // Turn off the sensor now - HRM_LOG("Turning off HR sensor"); - hrm_disable(HRM); - - sys_accel_manager_data_unsubscribe(s_manager_state.accel_state); - s_manager_state.accel_state = NULL; - - prv_is_sensor_stable(NULL); // inform state machine that sensor got powered off - - // If we need the sensor on again later, turn on a timer to re-enable the HRM in enough time - // to get a good reading for the next subscriber that needs one - if (remaining_ms < INT32_MAX) { - new_timer_start(s_manager_state.update_enable_timer_id, remaining_ms, - prv_update_enable_timer_cb, NULL /*context*/, 0 /*flags*/); - } else { - new_timer_stop(s_manager_state.update_enable_timer_id); - } - } - } - mutex_unlock_recursive(s_manager_state.lock); -} - -// Timer callback that we use to re-enable the HR sensor in case we turned it off for a while -static void prv_update_enable_timer_cb(void *context) { - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); -} - -//! The system task needs its own handler for HRM data since we can't queue up generic events. -static void prv_system_task_hrm_handler(void *context) { - time_t utc_now = rtc_get_time(); - - mutex_lock_recursive(s_manager_state.lock); - PebbleHRMEvent event; - prv_read_event_from_buffer_and_consume(&s_manager_state.system_task_event_buffer, &event); - - // Send event to all KernelBG subscribers that asked for this feature - HRMSubscriberState *state = (HRMSubscriberState *)s_manager_state.subscribers; - for (; state != NULL; state = (HRMSubscriberState *)state->list_node.next) { - if (!state->callback_handler) { - // Not a KernelBG subscriber - continue; - } - - // If this subscription is ready to expire, send an "expiring" event - if (prv_needs_expiring_event(state, utc_now)) { - PebbleHRMEvent expiring_event = (PebbleHRMEvent) { - .event_type = HRMEvent_SubscriptionExpiring, - .expiring.session_ref = state->session_ref, - }; - state->callback_handler(&expiring_event, state->callback_context); - state->sent_expiration_event = true; - } - - // See if this subscriber wants these types of events - switch (event.event_type) { - case HRMEvent_BPM: - if (!(state->features & HRMFeature_BPM)) { - continue; - } - break; - case HRMEvent_LEDCurrent: - if (!(state->features & HRMFeature_LEDCurrent)) { - continue; - } - break; - case HRMEvent_HRV: - if (!(state->features & HRMFeature_HRV)) { - continue; - } - break; - case HRMEvent_Diagnostics: - if (!(state->features & HRMFeature_Diagnostics)) { - continue; - } - break; - case HRMEvent_SubscriptionExpiring: - continue; - } - - // Send the event to the subscriber - state->callback_handler(&event, state->callback_context); - } - mutex_unlock_recursive(s_manager_state.lock); -} - -// Assumes that s_manager_state.lock is held -static void prv_queue_system_task_event(const PebbleHRMEvent *event) { - const uint16_t free_space = - circular_buffer_get_read_space_remaining(&s_manager_state.system_task_event_buffer); - if (free_space < sizeof(PebbleHRMEvent)) { - circular_buffer_consume(&s_manager_state.system_task_event_buffer, sizeof(PebbleHRMEvent)); - ++s_manager_state.dropped_events; - } - circular_buffer_write(&s_manager_state.system_task_event_buffer, - (const uint8_t *)event, sizeof(PebbleHRMEvent)); -} - -static void prv_populate_hrm_event(PebbleHRMEvent *event, HRMFeature feature, const HRMData *data) { - switch (feature) { - case HRMFeature_BPM: - *event = (PebbleHRMEvent) { - .event_type = HRMEvent_BPM, - .bpm = { - .bpm = data->hrm_bpm, - .quality = data->hrm_quality, - }, - }; - break; - case HRMFeature_HRV: - *event = (PebbleHRMEvent) { - .event_type = HRMEvent_HRV, - .hrv = { - .ppi_ms = data->hrv_ppi_ms, - .quality = data->hrv_quality, - }, - }; - break; - case HRMFeature_LEDCurrent: - *event = (PebbleHRMEvent) { - .event_type = HRMEvent_LEDCurrent, - .led = { - .current_ua = data->led_current_ua, - .tia = data->ppg_data.tia[0] - }, - }; - break; - case HRMFeature_Diagnostics: - { - HRMDiagnosticsData *debug = kernel_zalloc_check(sizeof(HRMDiagnosticsData)); - debug->ppg_data = data->ppg_data; - debug->accel_data = data->accel_data; - *event = (PebbleHRMEvent) { - .event_type = HRMEvent_Diagnostics, - .debug = debug, - }; - break; - } - default: - WTF; - } -} - -static bool prv_event_put(HRMSubscriberState *state, PebbleHRMEvent *event) { - bool success; - if (state->queue) { - PebbleEvent e = { - .type = PEBBLE_HRM_EVENT, - .hrm = *event, - }; - success = xQueueSendToBack(state->queue, &e, 0); - } else { - prv_queue_system_task_event(event); - success = system_task_add_callback(prv_system_task_hrm_handler, NULL); - } - return success; -} - -T_STATIC void prv_charger_event_cb(PebbleEvent *e, void *context) { - const PebbleBatteryStateChangeEvent *evt = &e->battery_state; - mutex_lock_recursive(s_manager_state.lock); - { - s_manager_state.enabled_charging_state = !evt->new_state.is_plugged; - } - mutex_unlock_recursive(s_manager_state.lock); - - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); -} - -// Accept new data from the HR device driver. -void hrm_manager_new_data_cb(const HRMData *data) { - mutex_lock_recursive(s_manager_state.lock); - if (!prv_can_turn_sensor_on() || s_manager_state.subscribers == NULL) { - // If the hrm manager should be disabled or we have no subscribers, this data is unwanted. - goto unlock; - } - // See if the sensor signal is stable or not - bool stable_sensor = prv_is_sensor_stable(data); - - HRM_LOG("HRM Data:"); - HRM_LOG("Status %"PRIx8, data->hrm_status); - HRM_LOG("HRM: %"PRIu8"bpm, Quality: %d, Stable: %d", data->hrm_bpm, data->hrm_quality, - (int)stable_sensor); - HRM_LOG("PPG samples: %d", data->ppg_data.num_samples); - HRM_HEXDUMP(data->ppg_data.ppg, data->ppg_data.num_samples * sizeof(uint16_t)); - HRM_LOG("TIA samples: %d", data->ppg_data.num_samples); - HRM_HEXDUMP(data->ppg_data.tia, data->ppg_data.num_samples * sizeof(uint16_t)); - HRM_LOG("Accel samples: %"PRIu32, data->accel_data.num_samples); - HRM_LOG("LED %"PRIu16"uA, TIA: %"PRIu16, data->led_current_ua, data->tia); - - time_t utc_now = rtc_get_time(); - RtcTicks cur_ticks = rtc_get_ticks(); - HRMFeature kernel_bg_features_sent = 0; - - HRMSubscriberState *state = (HRMSubscriberState *)s_manager_state.subscribers; - while (state) { - HRMSubscriberState *expired_state = NULL; - - // Update the time stamp for when this subscriber last received an update if the sensor - // is currently stable - if (stable_sensor) { - state->last_valid_ticks = cur_ticks; - } - - PebbleHRMEvent hrm_event; - for (uint8_t i = 0; i < HRMFeatureShiftMax; ++i) { - HRMFeature feature = (1 << i); - if (!(state->features & feature)) { - continue; - } - // Only send BPM and HRV events if the sensor is stable - if (!stable_sensor && (feature == HRMFeature_BPM || feature == HRMFeature_HRV)) { - continue; - } - if (state->callback_handler) { - // For kernel BG subscribers, we only queue one event of each type (which is then - // dispatched to all KernelBG subscribers from the KernelBG callback) so that we don't - // overfill our limited size circular buffer. - if (kernel_bg_features_sent & feature) { - continue; - } - kernel_bg_features_sent |= feature; - } - prv_populate_hrm_event(&hrm_event, feature, data); - PBL_ASSERTN(prv_event_put(state, &hrm_event)); - } - - // If this is an app subscription, see if we need to send an "expiring" event. We check - // KernelBG subscribers from the system callback function (prv_system_task_hrm_handler). - if (!state->callback_handler && prv_needs_expiring_event(state, utc_now)) { - hrm_event = (PebbleHRMEvent) { - .event_type = HRMEvent_SubscriptionExpiring, - .expiring.session_ref = state->session_ref, - }; - PBL_ASSERTN(prv_event_put(state, &hrm_event)); - state->sent_expiration_event = true; - } - - if (state->expire_utc && (utc_now >= state->expire_utc)) { - // This subscription has expired - expired_state = state; - } - state = (HRMSubscriberState *)state->list_node.next; - - // If the prior subscription expired, remove it now - if (expired_state) { - PBL_LOG(LOG_LEVEL_DEBUG, "Subscription %"PRIu32" expired", expired_state->session_ref); - prv_remove_and_free_subscription(expired_state); - } - } - - // Update the HRM enable state. If no subscribers need an update for a while, we can turn off the - // HR sensor and set a timer to turn it on again later. To avoid this overhead on every callback, - // we only check it once every HRM_CHECK_SENSOR_DISABLE_COUNT times - if (++s_manager_state.check_disable_counter >= HRM_CHECK_SENSOR_DISABLE_COUNT) { - s_manager_state.check_disable_counter = 0; - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); - } -unlock: - mutex_unlock_recursive(s_manager_state.lock); -} - -void hrm_manager_handle_prefs_changed(void) { - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); -} - -void hrm_manager_init(void) { - s_hrm_present = mfg_info_is_hrm_present(); - s_manager_state = (struct HRMManagerState) { - .lock = mutex_create_recursive(), - .accel_data_lock = mutex_create(), - .update_enable_timer_id = new_timer_create(), - .enabled_charging_state = !battery_is_usb_connected(), - .charger_subscription = (EventServiceInfo) { - .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, - .handler = prv_charger_event_cb, - }, - }; - circular_buffer_init(&s_manager_state.system_task_event_buffer, - s_manager_state.system_task_event_storage, - EVENT_STORAGE_SIZE); - event_service_client_subscribe(&s_manager_state.charger_subscription); -} - -HRMSessionRef hrm_manager_subscribe_with_callback(AppInstallId app_id, uint32_t update_interval_s, - uint16_t expire_s, HRMFeature features, - HRMSubscriberCallback callback, void *context) { - if (!s_hrm_present) { - return HRM_INVALID_SESSION_REF; - } - - const PebbleTask current_task = pebble_task_get_current(); - bool is_app_subscription = false; - if (current_task == PebbleTask_KernelBackground) { - // KernelBG must provide a callback - PBL_ASSERTN(callback != NULL); - } else if (current_task == PebbleTask_KernelMain) { - // KernelMain clients can either set a callback, or use the event_service interface. - } else { - PBL_ASSERTN(current_task == PebbleTask_App || current_task == PebbleTask_Worker); - is_app_subscription = true; - } - - mutex_lock_recursive(s_manager_state.lock); - HRMSessionRef session_ref = HRM_INVALID_SESSION_REF; - - // If there is already an existing subscription for this app, remove the old one before we - // add another subscription for this app. - if (is_app_subscription) { - HRMSubscriberState * state = prv_get_subscriber_state_from_app_id(current_task, app_id); - if (state != NULL) { - session_ref = state->session_ref; - PBL_LOG(LOG_LEVEL_DEBUG, "Removing existing subscription for this app"); - prv_remove_and_free_subscription(state); - } - } - - // Get the session ref to use - if (session_ref == HRM_INVALID_SESSION_REF) { - session_ref = ++s_manager_state.next_session_ref; - } - - HRMSubscriberState *state = kernel_malloc_check(sizeof(*state)); - *state = (HRMSubscriberState) { - .session_ref = session_ref, - .app_id = app_id, - .task = current_task, - .queue = pebble_task_get_to_queue(current_task), - .callback_handler = callback, - .callback_context = context, - .update_interval_s = update_interval_s, - .expire_utc = (expire_s != 0) ? (rtc_get_time() + expire_s) : 0, - .features = features, - }; - s_manager_state.subscribers = - list_insert_before(s_manager_state.subscribers, &state->list_node); - - // Update the HR enablement state - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); - - mutex_unlock_recursive(s_manager_state.lock); - return state->session_ref; -} - -DEFINE_SYSCALL(HRMSessionRef, sys_hrm_manager_app_subscribe, - AppInstallId app_id, uint32_t update_interval_s, uint16_t expire_sec, HRMFeature features) { - return hrm_manager_subscribe_with_callback(app_id, update_interval_s, expire_sec, features, NULL, - NULL); -} - - -DEFINE_SYSCALL(bool, sys_hrm_manager_unsubscribe, HRMSessionRef session) { - HRM_LOG("Unsubscribing"); - bool success = false; - mutex_lock_recursive(s_manager_state.lock); - - HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); - if (state) { - prv_remove_and_free_subscription(state); - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); - success = true; - } - - mutex_unlock_recursive(s_manager_state.lock); - return success; -} - -DEFINE_SYSCALL(HRMSessionRef, sys_hrm_manager_get_app_subscription, AppInstallId app_id) { - mutex_lock_recursive(s_manager_state.lock); - HRMSessionRef ref = HRM_INVALID_SESSION_REF; - HRMSubscriberState *state = prv_get_subscriber_state_from_app_id(pebble_task_get_current(), - app_id); - if (state) { - ref = state->session_ref; - } - mutex_unlock_recursive(s_manager_state.lock); - return ref; -} - - -DEFINE_SYSCALL(bool, sys_hrm_manager_get_subscription_info, HRMSessionRef session, - AppInstallId *app_id, uint32_t *update_interval_s, uint16_t *expire_s, - HRMFeature *features) { - mutex_lock_recursive(s_manager_state.lock); - HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); - if (state) { - if (app_id) { - *app_id = state->app_id; - } - if (update_interval_s) { - *update_interval_s = state->update_interval_s; - } - if (expire_s) { - int16_t expire_in_s = 0; - if (state->expire_utc != 0) { - expire_in_s = MAX(0, state->expire_utc - rtc_get_time()); - } - *expire_s = MAX(0, expire_in_s); - } - if (features) { - *features = state->features; - } - } - mutex_unlock_recursive(s_manager_state.lock); - return (state != NULL); -} - - -DEFINE_SYSCALL(bool, sys_hrm_manager_set_features, HRMSessionRef session, HRMFeature features) { - bool success = false; - mutex_lock_recursive(s_manager_state.lock); - HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); - if (state) { - state->features = features; - success = true; - } - mutex_unlock_recursive(s_manager_state.lock); - return success; -} - -DEFINE_SYSCALL(bool, sys_hrm_manager_set_update_interval, HRMSessionRef session, - uint32_t update_interval_s, uint16_t expire_s) { - bool success = false; - mutex_lock_recursive(s_manager_state.lock); - - HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); - if (state) { - state->update_interval_s = update_interval_s; - state->expire_utc = (expire_s != 0) ? (rtc_get_time() + expire_s) : 0; - state->sent_expiration_event = false; - success = true; - } - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); - mutex_unlock_recursive(s_manager_state.lock); - return success; -} - -DEFINE_SYSCALL(bool, sys_hrm_manager_is_hrm_present) { - return s_hrm_present; -} - -void hrm_manager_enable(bool on) { - mutex_lock_recursive(s_manager_state.lock); - s_manager_state.enabled_run_level = on; - system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); - mutex_unlock_recursive(s_manager_state.lock); -} - - -static HRMSessionRef s_console_session = HRM_INVALID_SESSION_REF; -static uint8_t s_tia_count = 0; -static void prv_console_unsubscribe_callback(void *data) { - sys_hrm_manager_unsubscribe(s_console_session); - s_console_session = HRM_INVALID_SESSION_REF; - prompt_command_finish(); -} - -static void prv_console_read_callback(PebbleHRMEvent *event, void *context) { - if (event->event_type == HRMEvent_LEDCurrent) { - if (s_tia_count++ == 5) { // Need to leave time for TIA to ramp up - system_task_add_callback(prv_console_unsubscribe_callback, NULL); - char buf[32]; - prompt_send_response_fmt(buf, 32, "TIA: %"PRIu16, event->led.tia); - prompt_send_response_fmt(buf, 32, "LED: %"PRIu16"uA", event->led.current_ua); - } - } -} - -void command_hrm_read(void) { - s_tia_count = 0; - sys_hrm_manager_unsubscribe(s_console_session); - s_console_session = hrm_manager_subscribe_with_callback( - INSTALL_ID_INVALID, 1 /*update_interval_s*/, 0 /*expire_s*/, HRMFeature_LEDCurrent, - prv_console_read_callback, NULL); - prompt_command_continues_after_returning(); -} - -HRMAccelData * hrm_manager_get_accel_data(void) { - mutex_lock(s_manager_state.accel_data_lock); - return &s_manager_state.accel_data; -} - -void hrm_manager_release_accel_data(void) { - s_manager_state.accel_data.num_samples = 0; // Reset buffer - mutex_unlock(s_manager_state.accel_data_lock); -} - -void hrm_manager_process_cleanup(PebbleTask task, AppInstallId app_id) { - if (task != PebbleTask_App && task != PebbleTask_Worker) { - return; - } - - // For apps and workers, if they have a subscription still active, make sure it expires - HRMSubscriberState *state = prv_get_subscriber_state_from_app_id(task, app_id); - if (state == NULL) { - return; - } - - // Set an expiration time now - PBL_LOG(LOG_LEVEL_DEBUG, "Setting expiration time on session for app_id %d", (int)app_id); - sys_hrm_manager_set_update_interval(state->session_ref, state->update_interval_s, - HRM_MANAGER_APP_EXIT_EXPIRATION_SEC); -} diff --git a/src/fw/services/common/i18n/i18n.h b/src/fw/services/common/i18n/i18n.h deleted file mode 100644 index 4887bf7716..0000000000 --- a/src/fw/services/common/i18n/i18n.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2000 Citrus Project, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#pragma once - -#include -#include -#include "util/list.h" - -#define ISO_LOCALE_LENGTH 6 -#define LOCALE_NAME_LENGTH 30 - -typedef struct { - ListNode node; //!< Linked list node - const void *owner; //!< pointer to owner object - uint32_t original_hash; //!< hashed original string - char *original_string; //!< original string. Stored following translated_string below - char translated_string[]; //!< i18n'ed string. Storage for original string comes after this -} I18nString; - -//! macro used to tag strings for extractions. Needed when we -//! can't call i18n_get directly (i.e constant initializers) -#define i18n_noop(string) (string) - -//! macro used to tag strings for extractions. Needed when we -//! can't call i18n_ctx_get directly (i.e constant initializers) -//! The resulting string should be used with i18n_get instead of i18n_ctx_get. -#define i18n_ctx_noop(ctx, string) (ctx "\4" string) - -//! Look up and return i18n'ed string (or original string if not found) -//! Tags it as owned by owner -//! NOTE: Currently, we don't do reference counting, so bad things will happen if the caller -//! calls i18n_get() on the same string more than once and assumes that any of those return -//! pointers will still be valid after i18n_free() is called on one of them. -const char *i18n_get(const char *string, const void *owner); - -#define i18n_ctx_get(ctx, string, owner) i18n_get(i18n_ctx_noop(ctx, string), owner) - -//! Look up an i18n'ed string and copy it into a provided buffer. -void i18n_get_with_buffer(const char *string, char *buffer, size_t length); - -#define i18n_ctx_get_with_buffer(ctx, string, buffer, length) \ - i18n_get_with_buffer(i18n_ctx_noop(ctx, string), buffer, length) - -//! Look up an i18n'ed string and return the length of it. -size_t i18n_get_length(const char *string); - -#define i18n_ctx_get_length(ctx, string) i18n_get_length(i18n_ctx_noop(ctx, string)) - -//! Free an i18n'ed string and it's associated metadata -void i18n_free(const char *string, const void *owner); - -#define i18n_ctx_free(ctx, string, owner) i18n_free(i18n_ctx_noop(ctx, string), owner) - -//! Free all i18n'ed strings associated with owner -void i18n_free_all(const void *owner); -void i18n_set_resource(uint32_t resource_id); - -//! return the ISO language string for the currently installed language -char *i18n_get_locale(void); - -//! return the version number for the currently installed language -uint16_t i18n_get_version(void); - -char *i18n_get_lang_name(void); - -void i18n_enable(bool enable); diff --git a/src/fw/services/common/i18n/syscalls.c b/src/fw/services/common/i18n/syscalls.c deleted file mode 100644 index f46650589b..0000000000 --- a/src/fw/services/common/i18n/syscalls.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "i18n.h" - -#include "kernel/memory_layout.h" -#include "kernel/pebble_tasks.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" - -DEFINE_SYSCALL(void, sys_i18n_get_locale, char *buf) { - if (PRIVILEGE_WAS_ELEVATED) { - if (pebble_task_get_current() == PebbleTask_Worker) { - // not allowed from workers - syscall_failed(); - } - } - - strncpy(buf, i18n_get_locale(), ISO_LOCALE_LENGTH); -} - -DEFINE_SYSCALL(void, sys_i18n_get_with_buffer, const char *string, - char *buffer, size_t length) { - if (PRIVILEGE_WAS_ELEVATED) { - if (pebble_task_get_current() == PebbleTask_Worker) { - // not allowed from workers - syscall_failed(); - } - if (!memory_layout_is_cstring_in_region(memory_layout_get_app_region(), string, 100) && - !memory_layout_is_cstring_in_region(memory_layout_get_microflash_region(), string, 100)) { - PBL_LOG(LOG_LEVEL_ERROR, "Pointer %p not in app or microflash region", string); - syscall_failed(); - } - syscall_assert_userspace_buffer(buffer, length); - } - - i18n_get_with_buffer(string, buffer, length); -} - -DEFINE_SYSCALL(size_t, sys_i18n_get_length, const char *string) { - if (PRIVILEGE_WAS_ELEVATED) { - if (pebble_task_get_current() == PebbleTask_Worker) { - // not allowed from workers - syscall_failed(); - } - if (!memory_layout_is_cstring_in_region(memory_layout_get_app_region(), string, 100) && - !memory_layout_is_cstring_in_region(memory_layout_get_microflash_region(), string, 100)) { - PBL_LOG(LOG_LEVEL_ERROR, "Pointer %p not in app or microflash region", string); - syscall_failed(); - } - } - - return i18n_get_length(string); -} diff --git a/src/fw/services/common/legacy/accel_manager.c b/src/fw/services/common/legacy/accel_manager.c deleted file mode 100644 index ca52ddad94..0000000000 --- a/src/fw/services/common/legacy/accel_manager.c +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/accel_manager.h" - -#include "applib/accel_service.h" -#include "applib/accel_service_private.h" -#include "console/prompt.h" -#include "drivers/imu.h" -#include "drivers/legacy/accel.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "os/mutex.h" -#include "os/tick.h" -#include "process_management/app_manager.h" -#include "process_management/worker_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/event_service.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" - -// Define this to install an accel subscription from the KernelMain task for testing -//#define TEST_KERNEL_SUBSCRIPTION 1 - -// Handy define -#define PEBBLE_TASK_CURRENT PebbleTask_Unknown - - -// ---------------------------------------------------------------------------------------------- -// We create one of these for each data service subscriber -typedef struct AccelSubscriberState { - ListNode list_node; // Entry into the s_data_subscribers linked list - AccelSessionRef session_ref; // client's session_ref. This is used to lookup - // the corresponding AccelSubscriberState - SharedCircularBufferClient buffer_client; - AccelSamplingRate sampling_rate; - uint16_t samples_per_update; - uint16_t subsample_numerator; - uint16_t subsample_denominator; - - //! Which task we should call the data_cb_handler on - PebbleTask task; - CallbackEventCallback data_cb_handler; - void* data_cb_context; - - uint64_t timestamp_ms; // timestamp of first item in the buffer - AccelRawData *raw_buffer; // raw buffer allocated by subscriber in it's heap - uint16_t raw_buffer_size; // size of buffer - uint16_t num_samples; // number of samples in raw_buffer - -} AccelSubscriberState; - - -// ---------------------------------------------------------------------------------------------- -// Globals -static ListNode *s_data_subscribers = 0; -static uint8_t s_tap_subscribers_count = 0; -AccelSamplingRate s_accel_sampling_rate; -static uint8_t s_accel_samples_per_update; -static TimerID s_timer_id = TIMER_INVALID_ID; -static bool s_temp_peek_mode = false; -static bool s_enabled = false; - - -// NOTE: All of our event service callbacks (add/remove subscriber, control) are guaranteed to only -// be called from the KernelMain task by the event_service, but sys_accel_consume_data() is called -// from the subscriber task, so we need to guard our globals with this mutex -static PebbleRecursiveMutex *s_mutex; - - -// ------------------------------------------------------------------------------------ -// Find AccelSubscriberState by AccelSessionRef -static bool prv_session_ref_list_filter(ListNode* node, void* data) { - AccelSubscriberState *state = (AccelSubscriberState *)node; - return state->session_ref == (AccelSessionRef)data; -} - - -// ------------------------------------------------------------------------------------------- -// Get the state variables for the given task -static AccelSubscriberState* prv_subscriber_state(AccelSessionRef session) { - if (session == NULL) { - return NULL; - } - - // Look for this session in our list of subscribers - ListNode* node = list_find(s_data_subscribers, prv_session_ref_list_filter, (void*)session); - return (AccelSubscriberState *)node; -} - - -// ----------------------------------------------------------------------------------------------- -// Get accel timestamp value in ms -static uint64_t prv_get_timestamp(void) { - time_t s; - uint16_t ms; - rtc_get_time_ms(&s, &ms); - return ((uint64_t)s) * 1000 + ms; -} - - -// --------------------------------------------------------------------------------------------- -// Update the driver configuration based on requested params from each subscriber -static void prv_update_driver_config(void) { - if (!s_enabled) { - //do not update config when in low power mode - return; - } - - AccelSamplingRate highest_rate = ACCEL_SAMPLING_10HZ; - uint32_t lowest_ms_per_update = ACCEL_MAX_SAMPLES_PER_UPDATE * 1000 / ACCEL_SAMPLING_10HZ; - uint32_t ms_per_update; - uint32_t num_fifo_subscribers = 0; - - // Cancel the peek restore config timer, if set - s_temp_peek_mode = false; - new_timer_stop(s_timer_id); - - AccelSubscriberState *state = (AccelSubscriberState *)s_data_subscribers; - while (state) { - if (state->sampling_rate > highest_rate) { - highest_rate = state->sampling_rate; - } - if (state->samples_per_update > 0) { - num_fifo_subscribers++; - ms_per_update = state->samples_per_update * 1000 / state->sampling_rate; - if (ms_per_update < lowest_ms_per_update) { - lowest_ms_per_update = ms_per_update; - } - } - state = (AccelSubscriberState *)state->list_node.next; - } - - // Setup the subsampling numerator and denominators - state = (AccelSubscriberState *)s_data_subscribers; - while (state) { - uint16_t new_num, new_den; - if ((highest_rate % state->sampling_rate) == 0) { - new_num = 1; - new_den = highest_rate / state->sampling_rate; - } else { - PBL_ASSERTN(highest_rate == ACCEL_SAMPLING_25HZ - && state->sampling_rate == ACCEL_SAMPLING_10HZ); - new_num = 2; - new_den = 5; - } - if (state->subsample_numerator != new_num || state->subsample_denominator != new_den) { - state->subsample_numerator = new_num; - state->subsample_denominator = new_den; - } - - PBL_LOG(LOG_LEVEL_DEBUG, "set subsampling for session %d to %d/%d", (int)state->session_ref, - state->subsample_numerator, state->subsample_denominator); - - state = (AccelSubscriberState *)state->list_node.next; - } - - // Configure the driver - accel_set_sampling_rate(highest_rate); - s_accel_sampling_rate = highest_rate; - - uint32_t num_samples = lowest_ms_per_update / (1000 / highest_rate); - if (num_fifo_subscribers == 0) { - num_samples = 0; - // This is used if all subscribers are peek mode only, no FIFO needed - } else if (num_samples == 0) { - // Min FIFO setting is 1 deep - num_samples = 1; - } else if (num_samples > ACCEL_MAX_SAMPLES_PER_UPDATE) { - num_samples = ACCEL_MAX_SAMPLES_PER_UPDATE; - } - accel_set_num_samples(num_samples); - s_accel_samples_per_update = num_samples; - - PBL_LOG(LOG_LEVEL_DEBUG, "setting accel rate:%d, num_samples:%"PRIu32, highest_rate, num_samples); -} - -// Switch accelerometer into and out of low power mode. This function is -// idempotent; calling it multiple times in a row with the same arguments must -// have the same result as calling it once. -void accel_manager_enable(bool on) { - mutex_lock_recursive(s_mutex); - bool prev = s_enabled; - s_enabled = on; - if (on && !prev) { - imu_power_up(); - prv_update_driver_config(); - } else if (!on && prev) { - imu_power_down(); - } - mutex_unlock_recursive(s_mutex); -} - - -// ---------------------------------------------------------------------------------------------- -static void prv_restore_fifo_mode_callback(void* data) { - mutex_lock_recursive(s_mutex); - { - ACCEL_LOG_DEBUG("Restoring FIFO settings after peek"); - prv_update_driver_config(); - } - mutex_unlock_recursive(s_mutex); -} - - -// ---------------------------------------------------------------------------------------------- -// Reset the FIFO mode restoration timer for another N seconds -static void prv_set_restore_fifo_mode_timer(void) { - s_temp_peek_mode = true; - bool success = new_timer_start(s_timer_id, 5 * 1000, prv_restore_fifo_mode_callback, NULL, 0 /*flags*/); - PBL_ASSERTN(success); -} - - - - -// ------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(int, sys_accel_manager_peek, AccelData *accel_data) { - int result; - - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(accel_data, sizeof(*accel_data)); - } - - analytics_inc(ANALYTICS_DEVICE_METRIC_ACCEL_PEEK_COUNT, AnalyticsClient_System); - PebbleTask task = pebble_task_get_current(); - if (task == PebbleTask_Worker || task == PebbleTask_App) { - analytics_inc(ANALYTICS_APP_METRIC_ACCEL_PEEK_COUNT, AnalyticsClient_CurrentTask); - } - - if (!accel_running()) { - return (-1); - } - - mutex_lock_recursive(s_mutex); - { - - if (s_accel_samples_per_update == 0) { - // If we are not in FIFO mode, can peek - result = (accel_peek(accel_data)); - accel_data->timestamp = prv_get_timestamp(); - - } else if (s_accel_samples_per_update == 1) { - // Else, if the FIFO is 1 deep, we can ask the accel driver for the cached reading from the - // last FIFO read - if (s_temp_peek_mode) { - prv_set_restore_fifo_mode_timer(); // Give us another N seconds - } - accel_get_latest_reading((AccelRawData *)accel_data); - accel_data->timestamp = accel_get_latest_timestamp(); - result = 0; - - } else { - uint64_t old_timestamp_ms = accel_get_latest_timestamp(); - - // Else, change the FIFO to 1 deep and wait for a reading - ACCEL_LOG_DEBUG("setting FIFO to 1 deep for peek"); - accel_set_num_samples(1); - s_accel_samples_per_update = 1; - - // Set a timer to restore settings after a while - prv_set_restore_fifo_mode_timer(); - - int max_loops = 12; - result = -3; - while (max_loops--) { - accel_data->timestamp = accel_get_latest_timestamp(); - if (accel_data->timestamp != old_timestamp_ms) { - accel_get_latest_reading((AccelRawData *)accel_data); - result = 0; - break; - } - vTaskDelay(milliseconds_to_ticks(10)); - } - } - } - mutex_unlock_recursive(s_mutex); - return result; -} - -static bool prv_call_data_callback(AccelSubscriberState *state) { - switch (state->task) { - case PebbleTask_App: - case PebbleTask_Worker: - case PebbleTask_KernelMain: { - PebbleEvent event = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = state->data_cb_handler, - .data = state->data_cb_context, - }, - }; - - QueueHandle_t queue = pebble_task_get_to_queue(state->task); - // Note: This call may fail if the queue is full but when a new sample - // becomes available from the driver, we will retry anyway - return xQueueSendToBack(queue, &event, 0); - } - case PebbleTask_KernelBackground: - return system_task_add_callback(state->data_cb_handler, state->data_cb_context); - case PebbleTask_NewTimers: - return new_timer_add_work_callback(state->data_cb_handler, state->data_cb_context); - default: - WTF; // Unsupported task for the accel manager - } -} - - -// --------------------------------------------------------------------------------------------- -// Called by accel driver after it has put more data into the circular buffer -void accel_manager_dispatch_data(void) { - ACCEL_LOG_DEBUG("entering accel_manager_dispatch_data"); - mutex_lock_recursive(s_mutex); - { - AccelSubscriberState* state; - - // Tell the accel driver it's OK to post another event if more data arrives. - accel_reset_pending_accel_event(); - - state = (AccelSubscriberState *)s_data_subscribers; - while (state) { - if (!state->raw_buffer || state->samples_per_update == 0) { - state = (AccelSubscriberState *)state->list_node.next; - continue; - } - - // If buffer has room, read more data - if (state->num_samples < state->samples_per_update) { - - // Read available data. We have to ask for a multiple of state->subsample_numerator - uint32_t num_samples; - uint32_t ask_for = state->samples_per_update - state->num_samples; - ask_for = MAX(ask_for, state->subsample_numerator); - ask_for = ask_for / state->subsample_numerator * state->subsample_numerator; - PBL_ASSERTN(state->num_samples + ask_for <= state->raw_buffer_size); - - num_samples = accel_consume_data(state->raw_buffer + state->num_samples, // buffer - &state->buffer_client, - ask_for, - state->subsample_numerator, - state->subsample_denominator); - - // Set the timestamp if we just put the first item in the buffer. If we emptied accel's - // buffer, we can resync the timestamp. Otherwise, we stick to the computed timestamp - // updated by sys_accel_manager_consume_samples(). - if (state->timestamp_ms == 0 || (state->num_samples == 0 && num_samples < ask_for)) { - state->timestamp_ms = accel_get_latest_timestamp() - (num_samples * 1000 / state->sampling_rate); - ACCEL_LOG_DEBUG("resyncing time"); - } - - state->num_samples += num_samples; - analytics_add(ANALYTICS_DEVICE_METRIC_ACCEL_SAMPLE_COUNT, num_samples, - AnalyticsClient_System); - PBL_ASSERTN(state->num_samples <= state->raw_buffer_size); - } - - // If buffer is full, notify subscriber to process it - if (state->num_samples >= state->samples_per_update) { - prv_call_data_callback(state); - - ACCEL_LOG_DEBUG("full set of %d samples for session %d", state->num_samples, - (int)state->session_ref); - } - state = (AccelSubscriberState *)state->list_node.next; - } - } - mutex_unlock_recursive(s_mutex); -} - - -// --------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(uint32_t, sys_accel_manager_get_num_samples, AccelSessionRef session, - uint64_t *timestamp_ms) { - uint32_t result = 0; - mutex_lock_recursive(s_mutex); - { - AccelSubscriberState* state = prv_subscriber_state(session); - if (!state) { - PBL_LOG(LOG_LEVEL_WARNING, "not subscribed"); - goto unlock; - } - result = state->num_samples; - *timestamp_ms = state->timestamp_ms; - } -unlock: - mutex_unlock_recursive(s_mutex); - return result; -} - - -// --------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(bool, sys_accel_manager_consume_samples, AccelSessionRef session, uint32_t samples) { - bool success = true; - mutex_lock_recursive(s_mutex); - { - AccelSubscriberState* state = prv_subscriber_state(session); - if (!state) { - PBL_LOG(LOG_LEVEL_WARNING, "not subscribed"); - goto unlock; - } - if (samples != state->num_samples) { - PBL_LOG(LOG_LEVEL_WARNING, "Wrong number of samples"); - success = false; - } else { - // Default timestamp for next chunk - state->timestamp_ms += samples * 1000 / state->sampling_rate; - state->num_samples = 0; - // Fill it again from accel circular buffer - accel_manager_dispatch_data(); - } - } -unlock: - mutex_unlock_recursive(s_mutex); - return success; -} - - -// ------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(int, sys_accel_manager_set_sampling_rate, AccelSessionRef session, - AccelSamplingRate rate) { - if (rate != ACCEL_SAMPLING_10HZ && rate != ACCEL_SAMPLING_25HZ && rate != ACCEL_SAMPLING_50HZ - && rate != ACCEL_SAMPLING_100HZ) { - return -1; - } - mutex_lock_recursive(s_mutex); - { - AccelSubscriberState* state = prv_subscriber_state(session); - if (!state) { - PBL_LOG(LOG_LEVEL_WARNING, "not subscribed"); - goto unlock; - } - state->sampling_rate = rate; - prv_update_driver_config(); - } -unlock: - mutex_unlock_recursive(s_mutex); - return 0; -} - - -// ------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(uint32_t, sys_accel_manager_get_buffer_size, AccelSessionRef session, - uint32_t samples_per_update) { - return samples_per_update + 1; -} - - -// ------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(int, sys_accel_manager_set_sample_buffer, AccelSessionRef session, - AccelRawData *buffer, uint32_t buffer_size, uint32_t samples_per_update) { - int result = 0; - if (samples_per_update > ACCEL_MAX_SAMPLES_PER_UPDATE) { - return -1; - } - - // The buffer must be big enough to hold at least 1 more item to support 2/5 subsampling - if (buffer_size < samples_per_update + 1) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid buffer size"); - return -1; - } - - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(buffer, samples_per_update * sizeof(AccelRawData)); - } - - mutex_lock_recursive(s_mutex); - { - AccelSubscriberState* state = prv_subscriber_state(session); - if (!state) { - PBL_LOG(LOG_LEVEL_WARNING, "not subscribed"); - result = -1; - goto unlock; - } - - state->raw_buffer = buffer; - state->raw_buffer_size = buffer_size; - state->samples_per_update = samples_per_update; - state->num_samples = 0; - prv_update_driver_config(); - } -unlock: - mutex_unlock_recursive(s_mutex); - return result; -} - - -// ------------------------------------------------------------------------------------------- -// NOTE: This is guaranteed to be only called from the KernelMain task by the event_service -static void prv_tap_add_subscriber_cb(PebbleTask task) { - mutex_lock_recursive(s_mutex); - { - if (++s_tap_subscribers_count == 1 && !s_data_subscribers) { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting accel service"); - accel_set_sampling_rate(ACCEL_SAMPLING_25HZ); - if (!accel_start()) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to start accel service"); - } - } - } - mutex_unlock_recursive(s_mutex); -} - - -// ------------------------------------------------------------------------------------------- -// NOTE: This is guaranteed to be only called from the KernelMain task by the event_service -static void prv_tap_remove_subscriber_cb(PebbleTask task) { - mutex_lock_recursive(s_mutex); - { - PBL_ASSERTN(s_tap_subscribers_count > 0); - if (--s_tap_subscribers_count == 0 && !s_data_subscribers) { - PBL_LOG(LOG_LEVEL_DEBUG, "Stopping accel service"); - if (accel_running()) { - accel_stop(); - } - } - } - mutex_unlock_recursive(s_mutex); -} - - -// ------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(void, sys_accel_manager_data_unsubscribe, AccelSessionRef session) { - mutex_lock_recursive(s_mutex); - { - AccelSubscriberState* state = prv_subscriber_state(session); - if (state != NULL) { - // Remove this subscriber and free up it's state variables - accel_remove_consumer(&state->buffer_client); - list_remove(&state->list_node, &s_data_subscribers /* &head */, NULL /* &tail */); - kernel_free(state); - - // All data subscribes are also tap subscribers - prv_tap_remove_subscriber_cb(pebble_task_get_current()); - - if (!s_data_subscribers) { - // If no one left using the data subscription, disable it - sys_vibe_history_stop_collecting(); - accel_set_num_samples(0); - } else { - // Else, reconfigure for the common subset of requirements among remaining subscribers - prv_update_driver_config(); - } - } - } - mutex_unlock_recursive(s_mutex); -} - - -// ------------------------------------------------------------------------------------------- -DEFINE_SYSCALL(void, sys_accel_manager_data_subscribe, AccelSessionRef session, - AccelSamplingRate rate, CallbackEventCallback data_cb, void* context, - PebbleTask handler_task) { - mutex_lock_recursive(s_mutex); - { - // Remove previous subscription for this task, if there is one - sys_accel_manager_data_unsubscribe(session); - AccelSubscriberState *state = prv_subscriber_state(session); - PBL_ASSERTN(state == NULL); - - state = kernel_malloc_check(sizeof(AccelSubscriberState)); - *state = (AccelSubscriberState) { - .session_ref = session, - .task = handler_task, - .data_cb_handler = data_cb, - .data_cb_context = context, - .sampling_rate = rate, - .samples_per_update = ACCEL_MAX_SAMPLES_PER_UPDATE, - }; - // All data subscribers are also tap subscribers - prv_tap_add_subscriber_cb(pebble_task_get_current()); - - bool no_subscribers_before = (s_data_subscribers == NULL); - s_data_subscribers = list_insert_before(s_data_subscribers, &state->list_node); - if (no_subscribers_before) { - sys_vibe_history_start_collecting(); - } - - // Add as a consumer to the accel buffer - accel_add_consumer(&state->buffer_client); - - // Update the sampling rate and num samples of the driver considering the new subscriber's - // request - prv_update_driver_config(); - } - mutex_unlock_recursive(s_mutex); -} - - -#ifdef TEST_KERNEL_SUBSCRIPTION -// ----------------------------------------------------------------------------------------------- -static void prv_kernel_data_subscription_handler(AccelData *accel_data, uint32_t num_samples) { - PBL_LOG(LOG_LEVEL_INFO, "Received %d accel samples for KernelMain.", num_samples); -} - - -// ----------------------------------------------------------------------------------------------- -static void prv_kernel_tap_subscription_handler(AccelAxisType axis, int32_t direction) { - PBL_LOG(LOG_LEVEL_INFO, "Received tap event for KernelMain, axis: %d, direction: %d", axis, - direction); -} -#endif - - -// ------------------------------------------------------------------------------------------- -void accel_manager_init(void) { - s_mutex = mutex_create_recursive(); - s_timer_id = new_timer_create(); - - event_service_init(PEBBLE_ACCEL_SHAKE_EVENT, &prv_tap_add_subscriber_cb, - &prv_tap_remove_subscriber_cb); - -#ifdef TEST_KERNEL_SUBSCRIPTION - accel_data_service_subscribe(5, prv_kernel_data_subscription_handler); - accel_tap_service_subscribe(prv_kernel_tap_subscription_handler); - accel_service_set_sampling_rate(ACCEL_SAMPLING_10HZ); -#endif - - imu_power_down(); -} - -#ifdef RECOVERY_FW -void command_accel_peek(void) { - const bool temporarily_start = !accel_running(); - if (temporarily_start) { - accel_start(); - } - - AccelData data; - int result = sys_accel_manager_peek(&data); - PBL_LOG(LOG_LEVEL_DEBUG, "result: %d", result); - - char buffer[20]; - prompt_send_response_fmt(buffer, sizeof(buffer), "X: %"PRId16, data.x); - prompt_send_response_fmt(buffer, sizeof(buffer), "Y: %"PRId16, data.y); - prompt_send_response_fmt(buffer, sizeof(buffer), "Z: %"PRId16, data.z); - - if (temporarily_start) { - accel_stop(); - } -} -#endif - -// The accelerometer should issue a shake/tap event with any slight movements when stationary. -// This will allow the watch to immediately return to normal mode, and attempt to reconnect to -// the phone. -void accel_enable_high_sensitivity(bool high_sensitivity) { - mutex_lock_recursive(s_mutex); - accel_set_shake_sensitivity_high(high_sensitivity); - mutex_unlock_recursive(s_mutex); -} - -void accel_manager_set_data_callback_task(AccelSessionRef session, PebbleTask task) { - mutex_lock_recursive(s_mutex); - - AccelSubscriberState* state = prv_subscriber_state(session); - state->task = task; - - mutex_unlock_recursive(s_mutex); -} diff --git a/src/fw/services/common/legacy/factory_registry.c b/src/fw/services/common/legacy/factory_registry.c deleted file mode 100644 index 1d5e75fde8..0000000000 --- a/src/fw/services/common/legacy/factory_registry.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/legacy/factory_registry.h" -#include "services/common/legacy/registry_private.h" - -#include "flash_region/flash_region.h" - -#define FACTORY_REGISTRY_NUM_OF_RECORDS 10 -static Record s_factory_records[FACTORY_REGISTRY_NUM_OF_RECORDS]; - -static RegistryCursor s_factory_flash_cursor = { - .address = FLASH_CURSOR_UNINITIALIZED, - .begin = FACTORY_REGISTRY_FLASH_BEGIN, - .end = FACTORY_REGISTRY_FLASH_END, -}; - -static Registry s_factory_registry = { - .is_different_from_flash = false, - .records = s_factory_records, - .num_records = FACTORY_REGISTRY_NUM_OF_RECORDS, - .registry_size_bytes = FACTORY_REGISTRY_NUM_OF_RECORDS * sizeof(Record), - .total_buffer_size_bytes = FACTORY_REGISTRY_NUM_OF_RECORDS * sizeof(Record) + - REGISTRY_HEADER_SIZE_BYTES, - .cursor = &s_factory_flash_cursor, -}; - -void factory_registry_init(void) { - registry_private_init(&s_factory_registry); - registry_private_read_from_flash(&s_factory_registry); -} - -int factory_registry_add(const char* key, const uint8_t key_length, const uint8_t* uuid, - const uint8_t description, const uint8_t* value, uint8_t value_length) { - return registry_private_add(key, key_length, uuid, description, value, value_length, - &s_factory_registry); -} - -Record* factory_registry_get(const char* key, const uint8_t key_length, const uint8_t* uuid) { - return registry_private_get(key, key_length, uuid, &s_factory_registry); -} - -int factory_registry_remove(const char* key, const uint8_t key_length, const uint8_t* uuid) { - return registry_private_remove(key, key_length, uuid, &s_factory_registry); -} - -void factory_registry_write_to_flash(void) { - registry_private_write_to_flash(&s_factory_registry); -} - -void factory_registry_remove_all(const uint8_t* uuid) { - registry_private_remove_all(uuid, &s_factory_registry); -} - - diff --git a/src/fw/services/common/legacy/factory_registry.h b/src/fw/services/common/legacy/factory_registry.h deleted file mode 100644 index 219f9c28d0..0000000000 --- a/src/fw/services/common/legacy/factory_registry.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/legacy/registry_common.h" - -#include - -//! Initialize the factory settings registry -//! -//! This must be called before using the factory registry. -void factory_registry_init(); - -//! Add a record to the factory settings registry -//! -//! @return 0 on success, -1 if factory settings registry is full or if key_length or value_length is -//! greater than MAX_KEY_SIZE_BYTES or MAX_VALUE_SIZE_BYTES, respectively -int factory_registry_add(const char* key, const uint8_t key_length, const uint8_t* uuid, const uint8_t description, - const uint8_t* value, uint8_t value_length); - -//! Get a record from the factory settings registry -//! -//! @return Record if found, NULL if not found -Record* factory_registry_get(const char* key, const uint8_t key_length, const uint8_t* uuid); - -//! Remove a record from the factory settings registry -//! -//! @return 0 on sucess, -1 if record not found -int factory_registry_remove(const char* key, const uint8_t key_length, const uint8_t* uuid); - -//! Write the factory settings registry to flash -void factory_registry_write_to_flash(void); - -//! Remove all records from the factory settings registry that match `uuid` -void factory_registry_remove_all(const uint8_t* uuid); - diff --git a/src/fw/services/common/legacy/registry_common.h b/src/fw/services/common/legacy/registry_common.h deleted file mode 100644 index a640092c68..0000000000 --- a/src/fw/services/common/legacy/registry_common.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "util/attributes.h" - -#define MAX_KEY_SIZE_BYTES 16 -#define MAX_VALUE_SIZE_BYTES 44 -#define UUID_SIZE_BYTES 5 - -typedef struct PACKED { - bool active; - uint8_t key_length; - uint8_t description; - uint8_t value_length; - uint8_t uuid[UUID_SIZE_BYTES]; - char key[MAX_KEY_SIZE_BYTES]; - uint8_t value[MAX_VALUE_SIZE_BYTES]; -} Record; - -static const uint8_t REGISTRY_SYSTEM_UUID[UUID_SIZE_BYTES] = { - 0x00, 0x00, 0x00, 0x00, 0x00 -}; diff --git a/src/fw/services/common/legacy/registry_private.c b/src/fw/services/common/legacy/registry_private.c deleted file mode 100644 index 671099c5b0..0000000000 --- a/src/fw/services/common/legacy/registry_private.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "services/common/legacy/registry_private.h" -#include "system/logging.h" -#include "system/passert.h" - -#include -#include - -// Header patterns must precede any registry written to flash. -// They are used to indicate if a registry is active. Only one registry should be -// active at a time -static const uint8_t s_active_header[] = {0xff, 0x00, 0xff}; -static const uint8_t s_inactive_header[] = {0x00, 0x00, 0x00}; - -//////////////////////////////////////////////////////////// -// Functions for manipulating the cursor in flash -//////////////////////////////////////////////////////////// -static int round_up_to_nearest_subsector(uint32_t addr) { - // - 1 because if we're currently on a subsector border we don't want to round up to the - // next one. - return (addr + SUBSECTOR_SIZE_BYTES - 1) & SUBSECTOR_ADDR_MASK; -} - -static bool is_cursor_at_active_registry(uint32_t cursor) { - uint8_t header[REGISTRY_HEADER_SIZE_BYTES]; - flash_read_bytes(header, cursor, REGISTRY_HEADER_SIZE_BYTES); - return memcmp(header, s_active_header, REGISTRY_HEADER_SIZE_BYTES) == 0; -} - -static uint32_t get_next_cursor_position(uint32_t old_address, Registry* registry) { - const uint32_t new_address = - round_up_to_nearest_subsector(old_address + registry->total_buffer_size_bytes); - - const bool is_space_for_registry = - new_address + registry->total_buffer_size_bytes < registry->cursor->end; - - if (is_space_for_registry) { - return new_address; - } - - return registry->cursor->begin; -} - -static void move_cursor_to_active_registry(Registry* registry) { - // Search for an active registry, starting at the start of the registry sector - // in flash (`REGISTRY_FLASH_BEGIN`). - // If cursor loops back to the start of the registry sector, then the - // entire sector has been scanned and no registry was found. In that case, leave - // the cursor in an invalid position (address 0xffff...) - uint32_t temp_address = registry->cursor->begin; - - registry->cursor->address = FLASH_CURSOR_UNINITIALIZED; - - do { - if (is_cursor_at_active_registry(temp_address)) { - registry->cursor->address = temp_address; - break; - } - temp_address = get_next_cursor_position(temp_address, registry); - } while (temp_address != registry->cursor->begin); -} - -//////////////////////////////////////////////////////////// -// Registry data structure -//////////////////////////////////////////////////////////// -static int registry_get_next_available_index(Registry* registry) { - for (int i = 0; i < registry->num_records; i++) { - Record* r = registry->records + i; - if (r->active == false) { - return i; - } - } - return -1; -} - -int registry_private_add(const char* key, const uint8_t key_length, const uint8_t* uuid, - uint8_t description, const uint8_t* value, uint8_t value_length, Registry* registry) { - if (value_length >= MAX_VALUE_SIZE_BYTES) { - PBL_LOG(LOG_LEVEL_WARNING, "Length of record value exceeds maximum length."); - return -1; - } - - if (key_length > MAX_KEY_SIZE_BYTES) { - PBL_LOG(LOG_LEVEL_WARNING, "Length of record key exceeds maximum length."); - return -1; - } - - Record* r = registry_private_get(key, key_length, uuid, registry); - - if (r) { - if (r->value_length == value_length && - r->description == description && - memcmp(r->value, value, value_length) == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "Key & value already exist."); - return 0; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Key already exists. Updating record."); - r->description = description; - memcpy(r->value, value, value_length); - r->value_length = value_length; - registry->is_different_from_flash = true; - return 0; - } - - int idx = registry_get_next_available_index(registry); - - if (idx < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Registry full."); - return -1; - } - - PBL_ASSERTN(idx >= 0 && idx < registry->num_records); - - r = registry->records + idx; - - r->active = true; - strncpy(r->key, key, key_length); - r->key[MAX_KEY_SIZE_BYTES - 1] = '\0'; - r->key_length = key_length; - memcpy(r->uuid, uuid, UUID_SIZE_BYTES); - r->description = description; - memcpy(r->value, value, value_length); - r->value_length = value_length; - registry->is_different_from_flash = true; - - PBL_LOG(LOG_LEVEL_DEBUG, "Writing new key: %s", r->key); - - return 0; -} - -void registry_record_print(Record* r) { - PBL_LOG_VERBOSE("Active:\n\t"); - if (r->active) { - PBL_LOG_VERBOSE("True"); - } else { - PBL_LOG_VERBOSE("False"); - } - PBL_LOG_VERBOSE("\n"); - - PBL_LOG_VERBOSE("Key is:\n\t"); - for (int i = 0; i < r->key_length; i++) { - PBL_LOG_VERBOSE("'%c'", r->key[i]); - } - PBL_LOG_VERBOSE("\n"); - - PBL_LOG_VERBOSE("UUID is:\n\t"); - for (int i = 0; i < UUID_SIZE_BYTES; i++) { - PBL_LOG_VERBOSE("%#.2hx, ",r->uuid[i]); - } - PBL_LOG_VERBOSE("\n"); - - PBL_LOG_VERBOSE("Description is:\n\t%#.2hx\n", r->description); - - PBL_LOG_VERBOSE("Value is:\n\t"); - for (int i = 0; i < r->value_length; i++) { - PBL_LOG_VERBOSE("%#.2hx, ",r->value[i]); - } - PBL_LOG_VERBOSE("\n"); -} - -static bool record_compare(Record* r, const char* key, const uint8_t key_length, const uint8_t* uuid) { - if ((r->key_length == key_length) && - (memcmp(r->uuid, uuid, UUID_SIZE_BYTES) == 0) && - (memcmp(r->key, key, r->key_length) == 0)) { - return true; - } - return false; -} - -static int record_get_index(const char* key, const uint8_t key_length, const uint8_t* uuid, Registry* registry) { - for (int i = 0; i < registry->num_records; i++) { - Record* r = registry->records + i; - - if (r->active) { - if (record_compare(r, key, key_length, uuid)) { - return i; - } - } - } - return -1; -} - -Record* registry_private_get(const char* key, const uint8_t key_length, - const uint8_t* uuid, Registry* registry) { - const int idx = record_get_index(key, key_length, uuid, registry); - - PBL_ASSERTN(idx < registry->num_records); - - if (idx >= 0) { - return registry->records + idx; - } - - return NULL; -} - -void registry_private_remove_all(const uint8_t* uuid, Registry* registry) { - for (int i = 0; i < registry->num_records; i++) { - Record* r = registry->records + i; - - if (r->active) { - bool uuid_match = memcmp(r->uuid, uuid, UUID_SIZE_BYTES) == 0; - if (uuid_match) { - r->active = false; - registry->is_different_from_flash = true; - } - } - } -} - -int registry_private_remove(const char* key, const uint8_t key_length, const uint8_t* uuid, - Registry* registry) { - const int idx = record_get_index(key, key_length, uuid, registry); - - PBL_ASSERTN(idx < registry->num_records); - - if (idx >= 0) { - Record* r = registry->records + idx; - r->active = false; - registry->is_different_from_flash = true; - return 0; - } - - return -1; -} - -//////////////////////////////////////////////////////////// -// Read and write from flash -//////////////////////////////////////////////////////////// -static void registry_set_header(uint32_t cursor, bool active) { - const uint8_t* header = s_inactive_header; - if (active) { - header = s_active_header; - } - flash_write_bytes(header, cursor, REGISTRY_HEADER_SIZE_BYTES); -} - -//! Verifies the flash cursor is at the active registry (as initialized by `registry_init()`. -//! Cursor must be subsector aligned and within the bounds of REGISTRY_FLASH_BEGIN and -//! REGISTRY_FLASH_END. -static void prv_assert_valid_cursor(const RegistryCursor *cursor) { - const bool is_addr_subsector_aligned = (cursor->address % (SUBSECTOR_SIZE_BYTES)) == 0; - - PBL_ASSERTN(is_addr_subsector_aligned && - cursor->address >= cursor->begin && - cursor->address < cursor->end); -} - -void registry_private_read_from_flash(Registry* registry) { - prv_assert_valid_cursor(registry->cursor); - - uint32_t registry_addr = registry->cursor->address + REGISTRY_HEADER_SIZE_BYTES; - flash_read_bytes((uint8_t*)registry->records, registry_addr, registry->registry_size_bytes); - registry->is_different_from_flash = false; -} - -static void prv_write_next_registry(Registry* registry) { - // Compute the addresses of the subsectors where the next registry will - // be stored in flash; erase those subsectors - const uint32_t next_start_address = get_next_cursor_position(registry->cursor->address, registry); - const uint32_t next_end_address = - round_up_to_nearest_subsector(next_start_address + registry->total_buffer_size_bytes); - - flash_region_erase_optimal_range(next_start_address, next_start_address, - next_end_address, next_end_address); - - // Write the next header + content - registry_set_header(next_start_address, true); - - const uint32_t data_start_address = next_start_address + REGISTRY_HEADER_SIZE_BYTES; - flash_write_bytes((uint8_t*)registry->records, data_start_address, - registry->registry_size_bytes); - - registry->cursor->address = next_start_address; -} - -void registry_private_write_to_flash(Registry* registry) { - // If the flash cursor is 0 (if no registry currently exists in flash), then set - // the flash cursor to REGISTRY_FLASH_BEGIN. - // - // Write a registry to flash by erasing the following subsectors, writing the - // registry to those subsectors. Then, set the current registry to be - // inactive and advance the flash cursor. - - // Cursor not initialized, no registry exists - if (registry->cursor->address == (unsigned)FLASH_CURSOR_UNINITIALIZED) { - registry->cursor->address = registry->cursor->begin; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Writing registry to flash..."); - - prv_assert_valid_cursor(registry->cursor); - - // Mark the previous registry as invalid - registry_set_header(registry->cursor->address, false); - - // Erase the spot for the next registry and write to it - prv_write_next_registry(registry); - - registry->is_different_from_flash = false; -} - -void registry_private_init(Registry* registry) { - // This fills the statically allocated registry with zeroes, tests that - // the registry is large enough to fit in memory using PBL_ASSERT and initializes - // the `flash cursor`---the flash address of the active registry's header. - // - // Registries are stored in flash with a preceeding three-byte header. This - // header is used to identify if the registry is active. The pattern 0xff, - // 0x00, 0xff indicates a registry is active, 0x00, 0x00, 0x00 indicates a - // registry is inactive. One registry should be active at any time. If no - // active registry can be found when `registry_init()` is called, an empty registry - // is written. - // - // The flash cursor starts at the begining of the registry's SPIFlash - // address (`REGISTRY_FLASH_BEGIN`), and is incremented to the next completely - // empty subsector every time the registry is written to flash. - memset(registry->records, 0, registry->registry_size_bytes); - - // Test that there is enough space in SPIFlash to write the registry - int flash_space_available_bytes = registry->cursor->end - registry->cursor->begin; - - // Registry is too large to store in SPIFlash: allocate more space - PBL_ASSERTN((signed)registry->registry_size_bytes < flash_space_available_bytes); - - move_cursor_to_active_registry(registry); - - // Write empty registry if one does not exist - if (registry->cursor->address == (unsigned)FLASH_CURSOR_UNINITIALIZED) { - registry_private_write_to_flash(registry); - } else { - registry_private_read_from_flash(registry); - } - - prv_assert_valid_cursor(registry->cursor); -} diff --git a/src/fw/services/common/legacy/registry_private.h b/src/fw/services/common/legacy/registry_private.h deleted file mode 100644 index 5c527b8a80..0000000000 --- a/src/fw/services/common/legacy/registry_private.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/legacy/registry_common.h" - -#include -#include - - -typedef struct { - unsigned int address; - unsigned int begin; - unsigned int end; -} RegistryCursor; - -#define FLASH_CURSOR_UNINITIALIZED (~0) -#define REGISTRY_HEADER_SIZE_BYTES 3 - -typedef struct { - bool is_different_from_flash; - Record* records; - int num_records; - unsigned int registry_size_bytes; - //! Size of the buffer in bytes when registry is written to flash - unsigned int total_buffer_size_bytes; - RegistryCursor* cursor; -} Registry; - - -void registry_private_init(Registry* registry); - -void registry_private_read_from_flash(Registry* registry); - -void registry_private_write_to_flash(Registry* registry); - -Record* registry_private_get(const char* key, const uint8_t key_length, const uint8_t* uuid, - Registry* registry); - -int registry_private_add(const char* key, const uint8_t key_length, const uint8_t* uuid, - uint8_t description, const uint8_t* value, uint8_t value_length, - Registry* registry); - -void registry_private_remove_all(const uint8_t* uuid, Registry* registry); - -int registry_private_remove(const char* key, const uint8_t key_length, const uint8_t* uuid, - Registry* registry); - diff --git a/src/fw/services/common/light.c b/src/fw/services/common/light.c deleted file mode 100644 index bf786fba10..0000000000 --- a/src/fw/services/common/light.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/light.h" - -#include "board/board.h" -#include "drivers/ambient_light.h" -#include "drivers/backlight.h" -#include "kernel/low_power.h" -#include "services/common/analytics/analytics.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/new_timer/new_timer.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" - -#include "FreeRTOS.h" -#include "semphr.h" - -#include - -typedef enum { - LIGHT_STATE_ON = 1, // backlight on, no timeouts - LIGHT_STATE_ON_TIMED = 2, // backlight on, will start fading after a period - LIGHT_STATE_ON_FADING = 3, // backlight in the process of fading out - LIGHT_STATE_OFF = 4, // backlight off; idle state -} BacklightState; - -// the time duration of the fade out -const uint32_t LIGHT_FADE_TIME_MS = 500; -// number of fade-out steps -const uint32_t LIGHT_FADE_STEPS = 20; - -/* - * ^ - * | - * LIGHT_ON | +---------------------------------+ - * | / \ - * | / \ - * | / \ - * | / \ - * | / \ - * LIGHT_ON/2 | /+ +\ - * | / | | \ - * | / | | \ - * | / | | \ - * | / | | \ - * | / | | \ - * |/ | | \ - * LIGHT_OFF +-------|-------------------------------------------|---------> - * | | - * |<----------------------------------------->| - * Integrate over this range for the mean - */ - -//! The current state of the backlight (example: ON/ON_TIMED/ON_FADING). -static BacklightState s_light_state; - -//! The brightness of the display in a range between BACKLIGHT_BRIGHTNESS_OFF and BACKLIGHT_BRIGHTNESS_ON -static int32_t s_current_brightness; - -//! Timer to count down from the LIGHT_STATE_ON_TIMED state. -static TimerID s_timer_id; - -//! Refcount of the number of buttons that are currently pushed -static int s_num_buttons_down; - -//! The current app is forcing the light on and off, don't muck with it. -static bool s_user_controlled_state; - -//! For temporary disabling backlight (ie: low power mode) -static bool s_backlight_allowed = false; - -//! Mutex to guard all the above state. We have a pattern of taking the lock in the public functions and assuming -//! it's already taken in the prv_ functions. -static PebbleMutex *s_mutex; - -static void prv_change_state(BacklightState new_state); - -static void light_timer_callback(void *data) { - mutex_lock(s_mutex); - prv_change_state(LIGHT_STATE_ON_FADING); - mutex_unlock(s_mutex); -} - -static uint16_t prv_backlight_get_intensity(void) { - // low_power_mode backlight intensity (25% of max brightness) - const uint16_t backlight_low_power_intensity = (BACKLIGHT_BRIGHTNESS_MAX * (uint32_t)25) / 100; - return low_power_is_active() ? backlight_low_power_intensity : backlight_get_intensity(); -} - -static void prv_change_brightness(int32_t new_brightness) { - const uint16_t HALF_BRIGHTNESS = (prv_backlight_get_intensity() - BACKLIGHT_BRIGHTNESS_OFF) / 2; - - // update the debug stats - if (new_brightness > HALF_BRIGHTNESS && s_current_brightness <= HALF_BRIGHTNESS) { - // getting brighter and have now transitioned past half brightness - analytics_stopwatch_start(ANALYTICS_APP_METRIC_BACKLIGHT_ON_TIME, AnalyticsClient_App); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_TIME, AnalyticsClient_System); - analytics_inc(ANALYTICS_APP_METRIC_BACKLIGHT_ON_COUNT, AnalyticsClient_App); - analytics_inc(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_COUNT, AnalyticsClient_System); - } - - if (new_brightness <= HALF_BRIGHTNESS && s_current_brightness > HALF_BRIGHTNESS) { - // getting dimmer and have now transitioned past half brightness - analytics_stopwatch_stop(ANALYTICS_APP_METRIC_BACKLIGHT_ON_TIME); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_TIME); - } - - backlight_set_brightness(new_brightness); - s_current_brightness = new_brightness; -} - -static void prv_change_state(BacklightState new_state) { - s_light_state = new_state; - - // Calculate the new brightness and reset any timers based on our state. - int32_t new_brightness = 0; - switch (new_state) { - case LIGHT_STATE_ON: - new_brightness = prv_backlight_get_intensity(); - new_timer_stop(s_timer_id); - break; - case LIGHT_STATE_ON_TIMED: - new_brightness = prv_backlight_get_intensity(); - - // Schedule the timer to move us from the ON_TIMED state to the ON_FADING state - new_timer_start(s_timer_id, backlight_get_timeout_ms(), - light_timer_callback, NULL, 0 /* flags */); - break; - case LIGHT_STATE_ON_FADING: - new_brightness = s_current_brightness - (prv_backlight_get_intensity() / LIGHT_FADE_STEPS); - - if (new_brightness <= BACKLIGHT_BRIGHTNESS_OFF) { - // Done fading! - new_brightness = BACKLIGHT_BRIGHTNESS_OFF; - s_light_state = LIGHT_STATE_OFF; - - // Don't need to cancel the timer, we can only get here from the just-expired timer. - } else { - // Reschedule the timer so we step down the brightness again. - new_timer_start(s_timer_id, LIGHT_FADE_TIME_MS / LIGHT_FADE_STEPS, light_timer_callback, NULL, 0 /* flags */); - } - break; - case LIGHT_STATE_OFF: - new_brightness = BACKLIGHT_BRIGHTNESS_OFF; - new_timer_stop(s_timer_id); - break; - } - - if (s_current_brightness != new_brightness) { - prv_change_brightness(new_brightness); - } -} - -static bool prv_light_allowed(void) { - if (!s_backlight_allowed) { - return false; - } - - if (backlight_is_enabled()) { - if (backlight_is_ambient_sensor_enabled()) { - // If the light is off and it's bright outside, don't allow the light to turn on - // (we don't need it!). Grab the mutex here so that the timer state machine doesn't change - // the light brightness while we're checking the ambient light levels. - bool allowed = !((s_current_brightness == 0) && ambient_light_is_light()); - return allowed; - } else { - return true; - } - } else { - return false; - } -} - -void light_init(void) { - s_light_state = LIGHT_STATE_OFF; - s_current_brightness = BACKLIGHT_BRIGHTNESS_OFF; - s_timer_id = new_timer_create(); - s_num_buttons_down = 0; - s_user_controlled_state = false; - s_mutex = mutex_create(); -} - -void light_button_pressed(void) { - mutex_lock(s_mutex); - - s_num_buttons_down++; - if (s_num_buttons_down > 4) { - PBL_LOG(LOG_LEVEL_ERROR, "More buttons were pressed than have been released."); - s_num_buttons_down = 0; - } - - // set the state to be on; releasing buttons will start the timer counting down - if (prv_light_allowed()) { - prv_change_state(LIGHT_STATE_ON); - } - - mutex_unlock(s_mutex); -} - -void light_button_released(void) { - mutex_lock(s_mutex); - - s_num_buttons_down--; - if (s_num_buttons_down < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "More buttons were released than have been pressed."); - s_num_buttons_down = 0; - } - - if (s_num_buttons_down == 0 && - s_light_state == LIGHT_STATE_ON && - !s_user_controlled_state) { - // no more buttons pressed: wait for a bit and then start the fade-out timer - prv_change_state(LIGHT_STATE_ON_TIMED); - } - - mutex_unlock(s_mutex); -} - -void light_enable_interaction(void) { - mutex_lock(s_mutex); - - //if some buttons are held or light_enable is asserted, do nothing - if (s_num_buttons_down > 0 || s_light_state == LIGHT_STATE_ON) { - mutex_unlock(s_mutex); - return; - } - - if (prv_light_allowed()) { - prv_change_state(LIGHT_STATE_ON_TIMED); - } - - mutex_unlock(s_mutex); -} - -void light_enable(bool enable) { - mutex_lock(s_mutex); - - // This function is a bit of a black sheep - it dives in and messes with the normal - // flow of the state machine. - // We don't actually use it, but it is now documented and used in the SDK, so - // I am reluctant to chop it out. - - s_user_controlled_state = enable; - - if (enable) { - prv_change_state(LIGHT_STATE_ON); - } else if (s_num_buttons_down == 0) { - // reset the state if someone calls light_enable(false); - // (unless there are buttons pressed, then leave the backlight on) - prv_change_state(LIGHT_STATE_OFF); - } - - mutex_unlock(s_mutex); -} - -void light_enable_respect_settings(bool enable) { - mutex_lock(s_mutex); - - s_user_controlled_state = enable; - - if (enable) { - if (prv_light_allowed()) { - prv_change_state(LIGHT_STATE_ON); - } - } else if (s_num_buttons_down == 0) { - prv_change_state(LIGHT_STATE_OFF); - } - - mutex_unlock(s_mutex); -} - -void light_reset_user_controlled(void) { - mutex_lock(s_mutex); - - // http://www.youtube.com/watch?v=6t_KgE6Yuqg - if (s_user_controlled_state) { - s_user_controlled_state = false; - - if (s_num_buttons_down == 0) { - prv_change_state(LIGHT_STATE_OFF); - } - } - - mutex_unlock(s_mutex); -} - -static void prv_light_reset_to_timed_mode(void) { - mutex_lock(s_mutex); - - if (s_user_controlled_state) { - s_user_controlled_state = false; - if (prv_light_allowed()) { - prv_change_state(LIGHT_STATE_ON_TIMED); - } - } - - mutex_unlock(s_mutex); -} - -void light_toggle_enabled(void) { - mutex_lock(s_mutex); - - backlight_set_enabled(!backlight_is_enabled()); - if (prv_light_allowed()) { - prv_change_state(LIGHT_STATE_ON_TIMED); - } else { - prv_change_state(LIGHT_STATE_OFF); - } - mutex_unlock(s_mutex); -} - -void light_toggle_ambient_sensor_enabled(void) { - mutex_lock(s_mutex); - backlight_set_ambient_sensor_enabled(!backlight_is_ambient_sensor_enabled()); - if (prv_light_allowed() && !ambient_light_is_light()) { - prv_change_state(LIGHT_STATE_ON_TIMED); - } else { - prv_change_state(LIGHT_STATE_OFF); - // FIXME: PBL-24793 There is an edge case of when the backlight has timed off - // or you're toggling it from no ambient (always light on buttons) to ambient, - // you will see it turn on and immediately off if its bright out - } - mutex_unlock(s_mutex); -} - -void light_allow(bool allowed) { - if (s_backlight_allowed && !allowed) { - prv_change_state(LIGHT_STATE_OFF); - } - s_backlight_allowed = allowed; -} - -DEFINE_SYSCALL(void, sys_light_enable_interaction, void) { - light_enable_interaction(); -} - -DEFINE_SYSCALL(void, sys_light_enable, bool enable) { - light_enable(enable); -} - -DEFINE_SYSCALL(void, sys_light_enable_respect_settings, bool enable) { - light_enable_respect_settings(enable); -} - -DEFINE_SYSCALL(void, sys_light_reset_to_timed_mode, void) { - prv_light_reset_to_timed_mode(); -} - -extern BacklightBehaviour backlight_get_behaviour(void); - -void analytics_external_collect_backlight_settings(void) { - BacklightBehaviour behaviour = backlight_get_behaviour(); - bool is_motion_enabled = backlight_is_motion_enabled(); - uint8_t backlight_intensity_pct = backlight_get_intensity_percent(); - uint8_t backlight_timeout_sec = backlight_get_timeout_ms() / 1000; - analytics_set(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT, behaviour, - AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_SETTING_SHAKE_TO_LIGHT, - is_motion_enabled, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_INTENSITY_PCT, - backlight_intensity_pct, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_TIMEOUT_SEC, - backlight_timeout_sec, AnalyticsClient_System); -} diff --git a/src/fw/services/common/light.h b/src/fw/services/common/light.h deleted file mode 100644 index cd47a5b206..0000000000 --- a/src/fw/services/common/light.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "shell/prefs.h" - -//! @file light.h -//! @addtogroup UI -//! @{ -//! @addtogroup Light Light -//! \brief Controlling Pebble's backlight -//! -//! The Light API provides you with functions to turn on Pebble’s backlight or -//! put it back into automatic control. You can trigger the backlight and schedule a timer -//! to automatically disable the backlight after a short delay, which is the preferred -//! method of interacting with the backlight. -//! @{ -//! -//! @internal -//! to be called when starting up to initialize variables correctly -void light_init(void); - -//! @internal -//! to be called by the launcher on a button down event -void light_button_pressed(void); - -//! @internal -//! to be called by the launcher on a button up event -void light_button_released(void); - -//! @copydoc app_light_enable -void light_enable(bool enable); - -//! @internal -//! light_enable that adheres to user's backlight setting. -void light_enable_respect_settings(bool enable); - -//! @copydoc app_light_enable_interaction -//! if light_enable was called (backlight was forced on), -//! then do nothing -void light_enable_interaction(void); - -//! Reset the state if an app overrode the usual state machine using light_enable() -void light_reset_user_controlled(void); - -//! @internal -void light_toggle_enabled(void); - -//! @internal -void light_toggle_ambient_sensor_enabled(void); - -//! Switches for temporary disabling backlight (ie: low power mode) -void light_allow(bool allowed); - -//! @} // group Light -//! @} // group UI diff --git a/src/fw/services/common/new_timer/new_timer_service.h b/src/fw/services/common/new_timer/new_timer_service.h deleted file mode 100644 index 9d451323c9..0000000000 --- a/src/fw/services/common/new_timer/new_timer_service.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void new_timer_service_init(void); - diff --git a/src/fw/services/common/phone_pp.c b/src/fw/services/common/phone_pp.c deleted file mode 100644 index 3668d8448b..0000000000 --- a/src/fw/services/common/phone_pp.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "phone_pp.h" - -#include "kernel/events.h" -#include "services/common/comm_session/session.h" -#include "services/normal/phone_call.h" -#include "services/normal/phone_call_util.h" -#include "system/hexdump.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#include -#include -#include -#include - -#define CALLER_BUFFER_LENGTH 32 - -static const uint16_t PHONE_CTRL_ENDPOINT = 0x21; - -static bool s_get_phone_state_enabled; - -typedef enum PhoneCallState { - CallStateIncoming, - CallStateOutgoing, - CallStateMissed, - NumCallStates -} PhoneCallState; - -enum PhoneCmd { - PhoneCmdAnswer = 0x01, - PhoneCmdHangup = 0x02, - PhoneCmdGetStateRequest = 0x03, - PhoneCmdGetStateResponse = 0x83, - PhoneCmdIncoming = 0x04, - PhoneCmdOutgoing = 0x05, - PhoneCmdMissed = 0x06, - PhoneCmdRing = 0x07, - PhoneCmdStart = 0x08, - PhoneCmdEnd = 0x09 -}; - -typedef struct { - uint32_t cookie; - char caller_number[CALLER_BUFFER_LENGTH]; - char caller_name[CALLER_BUFFER_LENGTH]; -} PebbleCallInfo; - - -static bool get_call_info_from_msg(const uint8_t* msg, unsigned int length, - PebbleCallInfo* info) { - unsigned int msg_length = 0; - info->cookie = *((uint32_t*) msg); - msg += 4; - msg_length += 4; - - uint8_t caller_number_size = *msg++; - memcpy(&info->caller_number, msg, - MIN(caller_number_size, sizeof(info->caller_number))); - msg += caller_number_size; - msg_length += caller_number_size + 1; - - uint8_t caller_name_size = *msg++; - memcpy(&info->caller_name, msg, - MIN(caller_name_size, sizeof(info->caller_name))); - msg_length += caller_name_size + 1; - - // Ensure that we haven't run off the end of our buffer - if (msg_length > length) { - return false; - } - - info->caller_number[CALLER_BUFFER_LENGTH - 1] = '\0'; - info->caller_name[CALLER_BUFFER_LENGTH - 1] = '\0'; - return true; -} - -// MJZ: This has been left here for future debugging -static void print_call_info(PebbleCallInfo* i) { - // PBL_LOG(LOG_LEVEL_DEBUG, "Call Cookie: 0x%"PRIx32, i->cookie); - // PBL_LOG(LOG_LEVEL_DEBUG, "Caller Number: %s", i->caller_number); - // PBL_LOG(LOG_LEVEL_DEBUG, "Caller Name: %s", i->caller_name); -} - -static void prv_put_call_disconnect_event(void) { - PebbleEvent e = { - .type = PEBBLE_PHONE_EVENT, - .phone = { - .type = PhoneEventType_Disconnect, - .source = PhoneCallSource_PP, - .call_identifier = 0, // Cookie is not yet implemented / used - } - }; - event_put(&e); -} - -static void prv_put_call_end_event(void) { - PebbleEvent e = { - .type = PEBBLE_PHONE_EVENT, - .phone = { - .type = PhoneEventType_End, - .source = PhoneCallSource_PP, - .call_identifier = 0, // Cookie is not yet implemented / used - } - }; - event_put(&e); -} - - -static void prv_send_phone_command_to_handset(uint8_t cmd, uint8_t *data, unsigned length) { - static uint8_t buffer[5]; - PBL_ASSERTN(length <= sizeof(buffer) - sizeof(cmd)); - - *buffer = cmd; - memcpy(buffer + sizeof(cmd), data, length); - - // PBL_LOG(LOG_LEVEL_DEBUG, "Sending PhoneCmd: %d", cmd); - CommSession *session = comm_session_get_system_session(); - if (!session) { - // Looks like we disconnected... - PBL_LOG(LOG_LEVEL_ERROR, "No CommSession for phone command, ending call"); - prv_put_call_disconnect_event(); - } else { - comm_session_send_data(session, PHONE_CTRL_ENDPOINT, buffer, - length + sizeof(cmd), COMM_SESSION_DEFAULT_TIMEOUT); - } -} - -void pp_answer_call(uint32_t cookie) { - prv_send_phone_command_to_handset(PhoneCmdAnswer, (uint8_t*)&cookie, sizeof(cookie)); -} - -void pp_decline_call(uint32_t cookie) { - prv_send_phone_command_to_handset(PhoneCmdHangup, (uint8_t*)&cookie, sizeof(cookie)); -} - -void pp_get_phone_state(void) { - prv_send_phone_command_to_handset(PhoneCmdGetStateRequest, NULL, 0); -} - -void pp_get_phone_state_set_enabled(bool enabled) { - s_get_phone_state_enabled = enabled; -} - -static bool prv_parse_msg_to_event(const uint8_t *iter, size_t length, - PebbleEvent *event_out, bool is_state_response) { - uint8_t msg_type = *iter++; - --length; - - bool did_parse = false; - PebbleCallInfo call_info; - call_info = (PebbleCallInfo){}; - - PebblePhoneCaller *caller = NULL; - PhoneEventType type = ~0; - - switch (msg_type) { - case PhoneCmdIncoming: { - // PBL-34640 Generating incoming call events for phone state responses just gives us a bad - // time. We can look at changing this later if iOS ever starts sending us cookies properly, - // but it's not really worth the effort since it only applies to iOS 8 - if (is_state_response) { - return false; - } - bool result = get_call_info_from_msg(iter, length, &call_info); - if (!result) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to read caller information from 'Incoming' phone event"); - return false; - } - -#ifndef RELEASE - print_call_info(&call_info); -#endif - - type = PhoneEventType_Incoming; - caller = phone_call_util_create_caller(call_info.caller_number, call_info.caller_name); - did_parse = true; - break; - } - - case PhoneCmdStart: { - type = PhoneEventType_Start; - call_info.cookie = *((uint32_t*) iter); - did_parse = true; - break; - } - - case PhoneCmdEnd: { - type = PhoneEventType_End; - call_info.cookie = *((uint32_t*) iter); - did_parse = true; - break; - } - - case PhoneCmdRing: { - // We generate rings internally - // Return here so we don't log / hexdump - return false; - } - - case PhoneCmdOutgoing: - // Return here so we don't log / hexdump - return false; - - case PhoneCmdMissed: - // Return here so we don't log / hexdump - return false; - } - - if (did_parse) { - *event_out = (const PebbleEvent) { - .type = PEBBLE_PHONE_EVENT, - .phone = { - .type = type, - .source = PhoneCallSource_PP, - .call_identifier = call_info.cookie, - .caller = caller, - } - }; - } else { - // Try to catch potentially malformed messages. - PBL_LOG(LOG_LEVEL_ERROR, "Error parsing phone msg"); - PBL_HEXDUMP(LOG_LEVEL_INFO, iter, length); - } - - return did_parse; -} - -static void prv_parse_msg_and_emit_event(const uint8_t *msg, size_t length, - bool is_state_response) { - PebbleEvent e; - if (prv_parse_msg_to_event(msg, length, &e, is_state_response)) { - event_put(&e); - } -} - -void phone_protocol_msg_callback(CommSession *session, const uint8_t* iter, size_t length) { - PBL_HEXDUMP(LOG_LEVEL_DEBUG, iter, length); - - // Get State Response is basically a list representing the state of current calls. It's - // conveniently formatted exactly the same as the event messages, so just loop over them and - // re-use the code that parses these messages: - if (iter[0] == PhoneCmdGetStateResponse) { - if (!s_get_phone_state_enabled) { - return; - } - // Eat the command byte: - --length; - ++iter; - uint8_t num_items = 0; - while (length) { - // First byte is the length of the item - uint8_t item_length = *iter++; - --length; - ++num_items; - if (length < item_length) { - PBL_LOG(LOG_LEVEL_ERROR, "Malformed message"); - break; - } - prv_parse_msg_and_emit_event(iter, item_length, true /* is_state_response */); - iter += item_length; - length -= item_length; - } - if (num_items == 0) { - // Generate fake call stop if there are no calls, to hide the phone UI in case it's showing - prv_put_call_end_event(); - } - } else { - prv_parse_msg_and_emit_event(iter, length, false /* is_state_response */); - } -} diff --git a/src/fw/services/common/phone_pp.h b/src/fw/services/common/phone_pp.h deleted file mode 100644 index 1f920727c9..0000000000 --- a/src/fw/services/common/phone_pp.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - - -void pp_answer_call(uint32_t cookie); - -void pp_decline_call(uint32_t cookie); - -void pp_get_phone_state(void); - -//! Enables or disables handling the Get Phone State responses. -//! This is part of a work-around to ignore for stray requests that can be in flight after the phone -//! call has been declined by the user from the Pebble. -void pp_get_phone_state_set_enabled(bool enabled); diff --git a/src/fw/services/common/ping.c b/src/fw/services/common/ping.c deleted file mode 100644 index cd3b2a5962..0000000000 --- a/src/fw/services/common/ping.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/ui/dialogs/dialog.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "console/prompt.h" -#include "drivers/battery.h" -#include "kernel/event_loop.h" -#include "kernel/ui/modals/modal_manager.h" -#include "services/common/accel_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "util/attributes.h" -#include "util/net.h" - -#include - -#define PING_ENDPOINT 2001 -#define PING_MIN_PERIOD_SECS (60 * 60) // 1 hour - -static time_t s_last_send_time; -static bool s_is_ping_kernel_bg_callback_scheduled; - - -// --------------------------------------------------------------------------------------------------------- -// Ping Pong structures -typedef struct PACKED { - uint8_t cmd; - uint32_t cookie; -} PingMsgHeader; - -typedef struct PACKED { - PingMsgHeader hdr; -} PingMsgV1; - -typedef struct PACKED { - PingMsgHeader hdr; - uint8_t idle; // Optional -} PingMsgV2; - -typedef struct PACKED { - PingMsgHeader hdr; -} PongMsg; - - -static void prv_send_ping_kernel_bg_cb(void *unused) { - CommSession *system_session = comm_session_get_system_session(); - if (system_session) { - // Are we idle? - bool idle = (battery_is_usb_connected() || accel_is_idle()); - - PingMsgV2 ping_msg = (PingMsgV2) { - .hdr = { - .cmd = 0, - .cookie = htonl(42) - }, - .idle = idle - }; - bool success = comm_session_send_data(system_session, PING_ENDPOINT, - (const uint8_t *) &ping_msg, sizeof(ping_msg), - COMM_SESSION_DEFAULT_TIMEOUT); - if (success) { - s_last_send_time = rtc_get_time(); - analytics_inc(ANALYTICS_DEVICE_METRIC_PING_SENT_COUNT, AnalyticsClient_System); - } - PBL_LOG(LOG_LEVEL_DEBUG, "Sent ping idle=%d, success=%d", (int)idle, (int)success); - } - - s_is_ping_kernel_bg_callback_scheduled = false; -} - -// ------------------------------------------------------------------------------------------------------------ -// If a ping is due to be sent, send it. -// bt_lock() is held by the caller -void ping_send_if_due(void) { - if (s_is_ping_kernel_bg_callback_scheduled) { - return; - } - - // Only send if we haven't sent within the last PING_MIN_PERIOD_SECS - time_t current_time = rtc_get_time(); - if (current_time < s_last_send_time + PING_MIN_PERIOD_SECS) { - return; - } - - // Offload to KernelBG, because we cannot use comm_session_send_data() with bt_lock held. - system_task_add_callback(prv_send_ping_kernel_bg_cb, NULL); - s_is_ping_kernel_bg_callback_scheduled = true; -} - -static void prv_push_window(void *data) { - SimpleDialog *s_dialog = simple_dialog_create("Ping"); - Dialog *dialog = simple_dialog_get_dialog(s_dialog); - - dialog_set_background_color(dialog, GColorCobaltBlue); - dialog_set_text_color(dialog, GColorWhite); - dialog_set_text(dialog, "Ping"); - - WindowStack *stack = modal_manager_get_window_stack(ModalPriorityGeneric); - simple_dialog_push(s_dialog, stack); -} - -void ping_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { - PingMsgV1 *ping = (PingMsgV1 *)data; - switch (ping->hdr.cmd) { - case 0: - { - if (length != sizeof(PingMsgV1) && length != sizeof(PingMsgV2) /* idle boolean is optional */) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid Ping, l=%u", length); - return; - } - - // Ping message - uint32_t cookie = ntohl(ping->hdr.cookie); - PBL_LOG(LOG_LEVEL_DEBUG, "Ping c=%"PRIu32"", cookie); - launcher_task_add_callback(prv_push_window, NULL); - - // Send the pong response - PongMsg pong = { - .hdr = { - .cmd = 1, - .cookie = htonl(cookie) - } - }; - comm_session_send_data(session, PING_ENDPOINT, (uint8_t *)&pong, sizeof(pong), COMM_SESSION_DEFAULT_TIMEOUT); - break; - } - - case 1: - if (length != sizeof(PongMsg)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid Pong, l=%u", length); - return; - } - - PongMsg *pong = (PongMsg *)data; - PBL_LOG(LOG_LEVEL_DEBUG, "Pong c=%"PRIu32, ntohl(pong->hdr.cookie)); - analytics_inc(ANALYTICS_DEVICE_METRIC_PONG_RECEIVED_COUNT, AnalyticsClient_System); - break; - - default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received. First byte is %u", ping->hdr.cmd); - break; - } -} - - -// Serial Commands -////////////////////////////////////////////////////////////////////// -void command_ping_send(void) { - // Override last send time - s_last_send_time = 0; - ping_send_if_due(); -} - diff --git a/src/fw/services/common/ping.h b/src/fw/services/common/ping.h deleted file mode 100644 index de28c4493b..0000000000 --- a/src/fw/services/common/ping.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// If a ping is due to be sent, send it. This should be called when we are already sending other -// data to the phone anyways in order to minimize the number of times we have to wake up the phone. -// It will return without doing anything if a minimum amount of time (currently 1 hour) -// has not elapsed since the last ping was sent out. -void ping_send_if_due(void); diff --git a/src/fw/services/common/poll_remote.c b/src/fw/services/common/poll_remote.c deleted file mode 100644 index 3f9a708a4f..0000000000 --- a/src/fw/services/common/poll_remote.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "poll_remote.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" - -#include - -/* - * Private - */ - -static const uint8_t MIN_INTERVAL_MINUTES = 1; -static const uint16_t ENDPOINT_ID = 0xcafe; -static bool s_running = false; - -typedef enum { - CMD_POLL = 0x0, // Formerly command poll mail - LEGACY_CMD_REQUEST_INTERVAL = 0x1, // for backwards compatibility - CMD_SET_INTERVAL = 0x2, - CMD_REQUEST_POLL = 0x3, -} PollRemoteCommand; - -// Deprecated -- used to set the mail poll interval -typedef struct PACKED { - uint8_t cmd; - uint8_t interval_minutes; -} PollLegacySetIntervalMessage; - -// Poll a service at a specific interval -typedef struct PACKED { - PollRemoteCommand cmd; - PollRemoteService service; - uint8_t interval_minutes; -} PollSetIntervalMessage; - -// Request to poll a service now -typedef struct PACKED { - PollRemoteCommand cmd; - PollRemoteService service; -} PollRequestMessage; - -typedef struct PACKED { - PollRemoteCommand cmd; - PollRemoteService service; -} PollRemoteMessage; - -//! Struct that holds all the state required for a remote polling subsystem. -typedef struct { - PollRemoteService service; - //! The minimum interval between two "poll services" requests. - //! Calls to poll_remote_send_request() will be no-ops if min_interval_minutes has not been reached. - uint8_t min_interval_minutes; - - //! The maximum interval between two "poll services" requests. - //! The automatic sending of poll requests will only occur when max_interval_minutes is reached. - uint8_t max_interval_minutes; - - uint8_t counted_minutes; //!< Number of minutes passed since the last request -} PollRemoteContext; - -static void poll_service_timer_callback(); - -static RegularTimerInfo s_poll_timer = { - .cb = poll_service_timer_callback, - .cb_data = NULL, -}; - -static PollRemoteContext s_poll_remote_contexts[NUM_POLL_REMOTE_SERVICES]; - -static void for_each_context(void (*poll_remote_function)(PollRemoteContext *ctx)) { - for (int i = 0; i < NUM_POLL_REMOTE_SERVICES; i++) { - poll_remote_function(&s_poll_remote_contexts[i]); - } -} - -static bool has_min_interval_passed(PollRemoteContext *ctx) { - return (ctx->counted_minutes >= ctx->min_interval_minutes); -} - -static bool has_max_interval_passed(PollRemoteContext *ctx) { - return (ctx->counted_minutes >= ctx->max_interval_minutes); -} - -// SystemTaskCallback -static void prv_send_request(PollRemoteContext *ctx) { - if (has_min_interval_passed(ctx) == false) { - return; - } - CommSession *session = comm_session_get_system_session(); - if (!session) { - return; - } - // [MT]: comm_session_send_data() doesn't make the link active, - // which is what we want here. If this this changes in the future - // we need to take measures here to make sure we don't pull the link active. - const PollRemoteMessage msg = { - .cmd = CMD_POLL, - .service = ctx->service - }; - comm_session_send_data(session, ENDPOINT_ID, (const uint8_t *)&msg, sizeof(PollRemoteMessage), - COMM_SESSION_DEFAULT_TIMEOUT); - ctx->counted_minutes = 0; -} - -static void context_interval_check(PollRemoteContext *ctx) { - if (ctx->max_interval_minutes == 0) { - return; - } - - ctx->counted_minutes++; - - if (has_max_interval_passed(ctx)) { - prv_send_request(ctx); - } -} - - -static void start(PollRemoteContext *ctx) { - ctx->counted_minutes = 0; -} - -static void set_intervals(PollRemoteContext *ctx, const uint8_t min_interval_minutes, const uint8_t max_interval_minutes) { - ctx->min_interval_minutes = min_interval_minutes; - ctx->max_interval_minutes = max_interval_minutes; -} - -void comm_poll_remote_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { - PollRemoteCommand cmd = data[0]; - - switch (cmd) { - case CMD_REQUEST_POLL: { - PollRequestMessage *msg = (PollRequestMessage *)data; - if (msg->service >= NUM_POLL_REMOTE_SERVICES) { return; } - prv_send_request(&s_poll_remote_contexts[msg->service]); - break; - } - case LEGACY_CMD_REQUEST_INTERVAL: { - PollLegacySetIntervalMessage *msg = (PollLegacySetIntervalMessage *)data; - poll_remote_set_intervals(POLL_REMOTE_SERVICE_MAIL, MIN_INTERVAL_MINUTES, msg->interval_minutes); - break; - } - case CMD_SET_INTERVAL: { - PollSetIntervalMessage *msg = (PollSetIntervalMessage *)data; - if (msg->service >= NUM_POLL_REMOTE_SERVICES) { return; } - poll_remote_set_intervals(msg->service, MIN_INTERVAL_MINUTES, msg->interval_minutes); - break; - } - default: { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid command."); - return; - } - } -} - -/* - * Public - */ - -static void poll_service_system_task_callback(void *data) { - PBL_ASSERTN(s_running); - for_each_context(context_interval_check); -} - -static void poll_service_timer_callback(void *data) { - system_task_add_callback(poll_service_system_task_callback, data); -} - -void poll_remote_init(void) { - for (int i = 0; i < NUM_POLL_REMOTE_SERVICES; i++) { - s_poll_remote_contexts[i].service = i; - } -} - -void poll_remote_send_request(PollRemoteService service) { - prv_send_request(&s_poll_remote_contexts[service]); -} - -void poll_remote_start(void) { - if (s_running) { - return; - } - - s_running = true; - for_each_context(start); - regular_timer_add_minutes_callback(&s_poll_timer); -} - -void poll_remote_stop(void) { - if (!s_running) { - return; - } - - s_running = false; - regular_timer_remove_callback(&s_poll_timer); -} - -void poll_remote_set_intervals(PollRemoteService service, const uint8_t min_interval_minutes, const uint8_t max_interval_minutes) { - set_intervals(&s_poll_remote_contexts[service], min_interval_minutes, max_interval_minutes); - (max_interval_minutes == 0) ? poll_remote_stop() : poll_remote_start(); -} diff --git a/src/fw/services/common/prf_update.c b/src/fw/services/common/prf_update.c deleted file mode 100644 index 01594caf28..0000000000 --- a/src/fw/services/common/prf_update.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "system/bootbits.h" -#include "system/firmware_storage.h" -#include "system/logging.h" -#include "util/math.h" - -// Don't allow PRF updating when we're in PRF -#ifndef RECOVERY_FW -static void prv_do_update(void) { - PBL_LOG(LOG_LEVEL_INFO, "Updating PRF!"); - flash_prf_set_protection(false); - - bool saved_sleep_when_idle = flash_get_sleep_when_idle(); - flash_sleep_when_idle(false); - -#if !CAPABILITY_HAS_PBLBOOT - FirmwareDescription description = - firmware_storage_read_firmware_description(FLASH_REGION_FIRMWARE_DEST_BEGIN); - - if (!firmware_storage_check_valid_firmware_description(FLASH_REGION_FIRMWARE_DEST_BEGIN, - &description)) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid recovery firmware CRC in SPI flash!"); - goto done; - } - - const uint32_t total_length = description.description_length + description.firmware_length; -#else - FirmwareHeader header = - firmware_storage_read_firmware_header(FLASH_REGION_FIRMWARE_DEST_BEGIN); - if (!firmware_storage_check_valid_firmware_header(FLASH_REGION_FIRMWARE_DEST_BEGIN, - &header)) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid recovery firmware CRC in SPI flash!"); - goto done; - } - - const uint32_t total_length = header.fw_start + header.fw_length; -#endif - - PBL_LOG(LOG_LEVEL_DEBUG, "Erasing previous PRF..."); - flash_region_erase_optimal_range(FLASH_REGION_SAFE_FIRMWARE_BEGIN, - FLASH_REGION_SAFE_FIRMWARE_BEGIN, - FLASH_REGION_SAFE_FIRMWARE_BEGIN + total_length, - FLASH_REGION_SAFE_FIRMWARE_END); - - PBL_LOG(LOG_LEVEL_DEBUG, "Copying PRF from scratch to the PRF slot"); - uint8_t buffer[512]; - uint32_t offset = 0; - while (offset < total_length) { - const uint32_t chunk_size = MIN(sizeof(buffer), (total_length - offset)); - - flash_read_bytes(buffer, FLASH_REGION_FIRMWARE_DEST_BEGIN + offset, chunk_size); - flash_write_bytes(buffer, FLASH_REGION_SAFE_FIRMWARE_BEGIN + offset, chunk_size); - - offset += chunk_size; - } - -done: - flash_prf_set_protection(true); - flash_sleep_when_idle(saved_sleep_when_idle); - PBL_LOG(LOG_LEVEL_DEBUG, "Done!"); -} -#endif - -void check_prf_update(void) { - if (!boot_bit_test(BOOT_BIT_NEW_PRF_AVAILABLE)) { - return; - } - - boot_bit_clear(BOOT_BIT_NEW_PRF_AVAILABLE); - -#ifndef RECOVERY_FW - prv_do_update(); -#endif -} diff --git a/src/fw/services/common/prf_update.h b/src/fw/services/common/prf_update.h deleted file mode 100644 index 2a220d07e2..0000000000 --- a/src/fw/services/common/prf_update.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void check_prf_update(void); diff --git a/src/fw/services/common/put_bytes/put_bytes.h b/src/fw/services/common/put_bytes/put_bytes.h deleted file mode 100644 index 9180f02f7c..0000000000 --- a/src/fw/services/common/put_bytes/put_bytes.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef struct PebbleCommSessionEvent PebbleCommSessionEvent; - -typedef enum { - ObjectUnknown = 0x00, - ObjectFirmware = 0x01, - ObjectRecovery = 0x02, - ObjectSysResources = 0x03, - ObjectAppResources = 0x04, - ObjectWatchApp = 0x05, - ObjectFile = 0x06, - ObjectWatchWorker = 0x07, - NumObjects -} PutBytesObjectType; - -typedef struct PbInstallStatus { - uint32_t num_bytes_written; - uint32_t crc_of_bytes; -} PbInstallStatus; - -void put_bytes_init(void); - -//! Tells put_bytes to clean up instantly. If put_bytes is already cleaned up, this is a no-op. -//! Any future messages sent by clients will be NACK'ed appropriately. -//! NOTE: Must be called from the KernelBackground task. -void put_bytes_cancel(void); - -//! Reset all put bytes state. Only useful for unit tests. -void put_bytes_deinit(void); - -//! Sets an initialization timeout for put_bytes. -//! If the phone doesn't send any data within the specified timeout, -//! put_bytes raises a timeout event. -void put_bytes_expect_init(uint32_t timeout_ms); - -//! Informs Put Bytes when the Pebble app disconnects to the Pebble, to make -//! it cancel any on-going transaction. -void put_bytes_handle_comm_session_event(const PebbleCommSessionEvent *app_event); diff --git a/src/fw/services/common/put_bytes/put_bytes_storage_file.c b/src/fw/services/common/put_bytes/put_bytes_storage_file.c deleted file mode 100644 index c665353f3d..0000000000 --- a/src/fw/services/common/put_bytes/put_bytes_storage_file.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "put_bytes_storage_file.h" - -#include "services/normal/filesystem/pfs.h" -#include "system/passert.h" - -bool pb_storage_file_init(PutBytesStorage *storage, PutBytesObjectType object_type, - uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset) { - // if a file exists with that name, remove it - pfs_remove(info->filename); - if (total_size == 0) { - // a file of size zero is valid at the moment - return true; - } - - int fd = pfs_open(info->filename, OP_FLAG_READ | OP_FLAG_WRITE, FILE_TYPE_STATIC, total_size); - storage->impl_data = (void*)(uintptr_t) fd; - - return fd >= 0; -} - -uint32_t pb_storage_file_get_max_size(PutBytesObjectType object_type) { - return get_available_pfs_space(); -} - -void pb_storage_file_write(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, - uint32_t length) { - // We don't support writing to arbitrary offsets in this implementation. - PBL_ASSERTN(offset == storage->current_offset); - - int fd = (int) storage->impl_data; - pfs_write(fd, buffer, length); -} - -uint32_t pb_storage_file_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type) { - PBL_ASSERTN(crc_type == PutBytesCrcType_Legacy); // PFS doesn't use new checksum at the moment - - int fd = (int) storage->impl_data; - return pfs_crc_calculate_file(fd, 0, storage->current_offset); -} - -void pb_storage_file_deinit(PutBytesStorage *storage, bool is_success) { - int fd = (int) storage->impl_data; - - if (!is_success) { - pfs_close_and_remove(fd); - } else { - pfs_close(fd); - } -} diff --git a/src/fw/services/common/put_bytes/put_bytes_storage_file.h b/src/fw/services/common/put_bytes/put_bytes_storage_file.h deleted file mode 100644 index 6a7ae74112..0000000000 --- a/src/fw/services/common/put_bytes/put_bytes_storage_file.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "put_bytes_storage.h" - -bool pb_storage_file_init(PutBytesStorage *storage, PutBytesObjectType object_type, - uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset); - -uint32_t pb_storage_file_get_max_size(PutBytesObjectType object_type); - -void pb_storage_file_write(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, - uint32_t length); - -uint32_t pb_storage_file_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type); - -void pb_storage_file_deinit(PutBytesStorage *storage, bool is_success); diff --git a/src/fw/services/common/put_bytes/put_bytes_storage_internal.h b/src/fw/services/common/put_bytes/put_bytes_storage_internal.h deleted file mode 100644 index 980635e4c1..0000000000 --- a/src/fw/services/common/put_bytes/put_bytes_storage_internal.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "put_bytes_storage.h" - -#include - -typedef struct PutBytesStorageImplementation { - bool (*init)(PutBytesStorage *storage, PutBytesObjectType object_type, - uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset); - - uint32_t (*get_max_size)(PutBytesObjectType object_type); - - void (*write)(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, uint32_t length); - - uint32_t (*calculate_crc)(PutBytesStorage *storage, PutBytesCrcType crc_type); - - void (*deinit)(PutBytesStorage *storage, bool is_success); -} PutBytesStorageImplementation; diff --git a/src/fw/services/common/put_bytes/put_bytes_storage_raw.h b/src/fw/services/common/put_bytes/put_bytes_storage_raw.h deleted file mode 100644 index 29c179997c..0000000000 --- a/src/fw/services/common/put_bytes/put_bytes_storage_raw.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "put_bytes_storage.h" - -#include - -bool pb_storage_raw_init(PutBytesStorage *storage, PutBytesObjectType object_type, - uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset); - -uint32_t pb_storage_raw_get_max_size(PutBytesObjectType object_type); - -void pb_storage_raw_write(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, - uint32_t length); - -uint32_t pb_storage_raw_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type); - - -void pb_storage_raw_deinit(PutBytesStorage *storage, bool is_success); - - -bool pb_storage_raw_get_status(PutBytesObjectType obj_type, PbInstallStatus *status); diff --git a/src/fw/services/common/registry_endpoint.c b/src/fw/services/common/registry_endpoint.c deleted file mode 100644 index e0a77e7886..0000000000 --- a/src/fw/services/common/registry_endpoint.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/comm_session/session.h" -#include "mfg/mfg_info.h" - -#include - -#define MFG_COLOR_KEY "mfg_color" - -#define FACTORY_REGISTRY_ENDPOINT 5001 - -static void prv_send_response(const uint8_t *data, unsigned int length) { - comm_session_send_data(comm_session_get_system_session(), FACTORY_REGISTRY_ENDPOINT, - data, length, COMM_SESSION_DEFAULT_TIMEOUT); -} - -static void prv_send_color_response(void) { - const uint8_t response[] = { 0x01, 0x04, 0x0, 0x0, 0x0, mfg_info_get_watch_color() }; - prv_send_response(response, sizeof(response)); -} - -static void prv_send_fail_response(void) { - const uint8_t error_response = 0xff; - prv_send_response(&error_response, sizeof(error_response)); -} - -void factory_registry_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length_bytes) { - // Expected message is 0x0 (Read), 0x09 (Length of key), followed by the string "mfg_color". - // All other messages just get an error response - - if (length_bytes == 1 + 1 + strlen(MFG_COLOR_KEY) && - data[0] == 0x0 && - data[1] == strlen(MFG_COLOR_KEY) && - memcmp(data + 2, MFG_COLOR_KEY, strlen(MFG_COLOR_KEY)) == 0) { - - prv_send_color_response(); - return; - } - - prv_send_fail_response(); -} - diff --git a/src/fw/services/common/registry_endpoint.h b/src/fw/services/common/registry_endpoint.h deleted file mode 100644 index 71200e4d11..0000000000 --- a/src/fw/services/common/registry_endpoint.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/comm_session/protocol.h" - -typedef enum { - RegistryEndpointIdSystem = 5000, - RegistryEndpointIdFactory = 5001, -} RegistryEndpointId; - -void registry_endpoint_callback(CommSession *session, const uint8_t* data, unsigned int length_bytes); - -void factory_registry_endpoint_callback(CommSession *session, const uint8_t* data, unsigned int length_bytes); diff --git a/src/fw/services/common/regular_timer.c b/src/fw/services/common/regular_timer.c deleted file mode 100644 index 37725e8712..0000000000 --- a/src/fw/services/common/regular_timer.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/regular_timer.h" - -#include "os/mutex.h" -#include "services/common/new_timer/new_timer.h" -#include "system/logging.h" -#include "system/passert.h" - -#include "FreeRTOS.h" -#include "portmacro.h" - -//! Don't let users modify the list while callbacks are occurring. -static PebbleMutex * s_callback_list_semaphore = 0; - -//! The timer we use -static TimerID s_timer_id = TIMER_INVALID_ID; - -static ListNode s_seconds_callbacks; -static ListNode s_minutes_callbacks; - -// Set to 90 seconds because we do eventually drift. Make it in the middle of a minute so we can -// be sure that it isn't due to drifting. -#define MISSING_MINUTE_CB_LOG_THRESHOLD_S 90 -static time_t s_last_minute_fire_ts; // uses - - - -// ------------------------------------------------------------------------------------------- -// Passed to list_find() to determine if a callback is already registered or not -static bool prv_callback_registered_filter(ListNode *found_node, void *data) { - return (found_node == (ListNode *)data); -} - -// ------------------------------------------------------------------------------------------- -static void do_callbacks(ListNode* list) { - mutex_lock(s_callback_list_semaphore); - - for (ListNode* iter = list_get_next(list); iter != 0; ) { - RegularTimerInfo* reg_timer = (RegularTimerInfo*) iter; - - if (--reg_timer->private_count == 0) { - reg_timer->private_count = reg_timer->private_reset_count; - - // Release the mutex while we execute the callback - reg_timer->is_executing = true; - mutex_unlock(s_callback_list_semaphore); - reg_timer->cb(reg_timer->cb_data); - mutex_lock(s_callback_list_semaphore); - reg_timer->is_executing = false; - - // Get the next one to execute before we possibly remove this one - iter = list_get_next(iter); - - // Did the caller want to remove this one? - // NOTE: We do not support callers that free the memory for the regular timer structure - // from their callback procedure! - if (reg_timer->pending_delete) { - list_remove(®_timer->list_node, NULL, NULL); - } - - } else { - iter = list_get_next(iter); - } - } - - mutex_unlock(s_callback_list_semaphore); -} - -// ------------------------------------------------------------------------------------------- -static void timer_callback(void* data) { - (void) data; - - do_callbacks(&s_seconds_callbacks); - - time_t t = rtc_get_time(); - struct tm time; - localtime_r(&t, &time); - if (time.tm_sec == 0) { - // If the phone sets the time, we may actually skip a regular_timer minute callback. Let's - // get an idea of how often this happens by logging - const time_t now_ts = rtc_get_ticks() / configTICK_RATE_HZ; - if ((now_ts - s_last_minute_fire_ts) > MISSING_MINUTE_CB_LOG_THRESHOLD_S) { - PBL_LOG(LOG_LEVEL_WARNING, - "Skipped a regular_timer_minute callback. Previous ts: %lu, Now ts: %lu", - s_last_minute_fire_ts, now_ts); - } - s_last_minute_fire_ts = now_ts; - - do_callbacks(&s_minutes_callbacks); - } -} - -// ------------------------------------------------------------------------------------------- -//! Used only once when we first start up. This should be really close to the 0ms point. -static void timer_callback_initializing(void* data) { - // FIXME: FreeRTOS timers are subject to skew if something else is running on the millisecond. - // We'll need to continously adjust our timer period in really annoying ways. - new_timer_start(s_timer_id, 1000, timer_callback, NULL, TIMER_START_FLAG_REPEATING); - - timer_callback(data); -} - -// -------------------------------------------------------------------------------------------- -void regular_timer_init(void) { - PBL_ASSERTN(s_callback_list_semaphore == 0); - - s_callback_list_semaphore = mutex_create(); - - time_t seconds; - uint16_t milliseconds; - rtc_get_time_ms(&seconds, &milliseconds); - s_timer_id = new_timer_create(); - bool success = new_timer_start(s_timer_id, 1000-milliseconds, timer_callback_initializing, NULL, 0 /*flags*/); - PBL_ASSERTN(success); -} - -// ------------------------------------------------------------------------------------------- -void regular_timer_add_multisecond_callback(RegularTimerInfo* cb, uint16_t seconds) { - PBL_ASSERTN(s_callback_list_semaphore); - - mutex_lock(s_callback_list_semaphore); - - cb->private_reset_count = seconds; - cb->private_count = seconds; - - // Only add to the list if not already registered - if (!list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node)) { - // better not be registered as a minute callback already - PBL_ASSERTN(!list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)); - cb->is_executing = false; - cb->pending_delete = false; - list_append(&s_seconds_callbacks, &cb->list_node); - } else { - // If it is marked for deletion, remove the deletion flag - cb->pending_delete = false; - } - - mutex_unlock(s_callback_list_semaphore); -} - -// -------------------------------------------------------------------------------------------- -void regular_timer_add_seconds_callback(RegularTimerInfo* cb) { - // special case for triggering each second - regular_timer_add_multisecond_callback(cb, 1); -} - -// -------------------------------------------------------------------------------------------- -void regular_timer_add_multiminute_callback(RegularTimerInfo* cb, uint16_t minutes) { - PBL_ASSERTN(s_callback_list_semaphore); - - mutex_lock(s_callback_list_semaphore); - - cb->private_reset_count = minutes; - cb->private_count = minutes; - - if (!list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)) { - // better not be registered as a minute callback already - PBL_ASSERTN(!list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node)); - cb->is_executing = false; - cb->pending_delete = false; - list_append(&s_minutes_callbacks, &cb->list_node); - } else { - // If it is marked for deletion, remove the deletion flag - cb->pending_delete = false; - } - - mutex_unlock(s_callback_list_semaphore); -} - -// ----------------------------------------------------------------------------------------- -void regular_timer_add_minutes_callback(RegularTimerInfo* cb) { - // special case for triggering each minute - regular_timer_add_multiminute_callback(cb, 1); -} - -// ------------------------------------------------------------------------------------------ -static bool prv_regular_timer_is_scheduled(RegularTimerInfo *cb) { - // Assumes mutex lock is already taken - return (list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node) || - list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)); -} - -// ------------------------------------------------------------------------------------------ -bool regular_timer_is_scheduled(RegularTimerInfo *cb) { - PBL_ASSERTN(s_callback_list_semaphore); - - mutex_lock(s_callback_list_semaphore); - bool rv = prv_regular_timer_is_scheduled(cb); - mutex_unlock(s_callback_list_semaphore); - - return (rv); -} - -bool regular_timer_pending_deletion(RegularTimerInfo *cb) { - return cb->pending_delete; -} - -// ------------------------------------------------------------------------------------------ -bool regular_timer_remove_callback(RegularTimerInfo* cb) { - PBL_ASSERTN(s_callback_list_semaphore); - bool timer_removed = false; - mutex_lock(s_callback_list_semaphore); - - if (!prv_regular_timer_is_scheduled(cb)) { - PBL_LOG(LOG_LEVEL_WARNING, "Timer not registered"); - } else { - // If currently executing, mark for deletion. do_callbacks will delete it for us once - // it completes. - if (cb->is_executing) { - cb->pending_delete = true; - } else { - list_remove(&cb->list_node, NULL, NULL); - timer_removed = true; - } - } - - mutex_unlock(s_callback_list_semaphore); - return timer_removed; -} - - -// --------------------------------------------------------------------------------------- -// For Testing: - -void regular_timer_deinit(void) { - mutex_destroy((PebbleMutex *) s_callback_list_semaphore); - s_callback_list_semaphore = NULL; - new_timer_delete(s_timer_id); - s_timer_id = TIMER_INVALID_ID; -} - -static void prv_fire_callbacks(ListNode *list, uint16_t mod) { - mutex_lock(s_callback_list_semaphore); - ListNode* iter = list_get_next(list); - while (iter) { - RegularTimerInfo* reg_timer = (RegularTimerInfo*) iter; - if (reg_timer->private_reset_count % mod == 0) { - // Last one. Will trigger callback when do_callbacks() is called: - reg_timer->private_count = 1; - } - iter = list_get_next(iter); - } - mutex_unlock(s_callback_list_semaphore); - - do_callbacks(list); -} - -void regular_timer_fire_seconds(uint8_t secs) { - prv_fire_callbacks(&s_seconds_callbacks, secs); -} - -void regular_timer_fire_minutes(uint8_t mins) { - prv_fire_callbacks(&s_minutes_callbacks, mins); -} - -static uint32_t prv_count(ListNode *list) { - uint32_t count = 0; - mutex_lock(s_callback_list_semaphore); - // -1, because s_..._callbacks is a ListNode too - count = list_count(list) - 1; - mutex_unlock(s_callback_list_semaphore); - return count; -} - -uint32_t regular_timer_seconds_count(void) { - return prv_count(&s_seconds_callbacks); -} - -uint32_t regular_timer_minutes_count(void) { - return prv_count(&s_minutes_callbacks); -} diff --git a/src/fw/services/common/services_common.c b/src/fw/services/common/services_common.c deleted file mode 100644 index 9dc063ebb9..0000000000 --- a/src/fw/services/common/services_common.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! @file services.c -//! -//! This file should control the initialization of the various services in the right order. -//! I'll slowly move initialization routines into here as we continue to refactor services. -//! For now this will just be woefully incomplete. - -#include "services_common.h" - -#include "mfg/mfg_info.h" - -#ifdef MICRO_FAMILY_STM32F2 -#include "services/common/legacy/factory_registry.h" -#endif -#include "services/common/accel_manager.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/app_session_capabilities.h" -#include "services/common/comm_session/default_kernel_sender.h" -#include "services/common/comm_session/session.h" -#include "services/common/cron.h" -#include "services/common/firmware_update.h" -#include "services/common/hrm/hrm_manager.h" -#include "services/common/light.h" -#include "services/common/poll_remote.h" -#include "services/common/put_bytes/put_bytes.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/common/touch/touch.h" -#include "services/common/vibe_pattern.h" -#include "services/runlevel_impl.h" -#include "util/size.h" - -void services_common_init(void) { - firmware_update_init(); - put_bytes_init(); - poll_remote_init(); - accel_manager_init(); - light_init(); - - cron_service_init(); - - shared_prf_storage_init(); - bt_persistent_storage_init(); - - comm_default_kernel_sender_init(); - comm_session_app_session_capabilities_init(); - comm_session_init(); - - bt_ctl_init(); - -#if CAPABILITY_HAS_TOUCHSCREEN - touch_init(); -#endif - -#if CAPABILITY_HAS_BUILTIN_HRM - hrm_manager_init(); -#endif - - // We only use the factory registry on tintins and biancas -#ifdef MICRO_FAMILY_STM32F2 - factory_registry_init(); -#endif -} - -static struct ServiceRunLevelSetting s_runlevel_settings[] = { - { - .set_enable_fn = accel_manager_enable, - .enable_mask = R_Stationary | R_FirmwareUpdate | R_Normal, - }, - { - .set_enable_fn = light_allow, - .enable_mask = R_LowPower | R_FirmwareUpdate | R_Normal, - }, - { - .set_enable_fn = vibe_service_set_enabled, - .enable_mask = R_LowPower | R_FirmwareUpdate | R_Normal - }, - { - .set_enable_fn = bt_ctl_set_enabled, - .enable_mask = R_FirmwareUpdate | R_Normal, - }, -#if CAPABILITY_HAS_BUILTIN_HRM - { - .set_enable_fn = hrm_manager_enable, - .enable_mask = R_Normal, - }, -#endif -}; - -void services_common_set_runlevel(RunLevel runlevel) { - for (size_t i = 0; i < ARRAY_LENGTH(s_runlevel_settings); ++i) { - struct ServiceRunLevelSetting *service = &s_runlevel_settings[i]; - service->set_enable_fn(((1 << runlevel) & service->enable_mask) != 0); - } -} - diff --git a/src/fw/services/common/services_common.h b/src/fw/services/common/services_common.h deleted file mode 100644 index 79de13ecc6..0000000000 --- a/src/fw/services/common/services_common.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/runlevel.h" - -void services_common_init(void); - -void services_common_set_runlevel(RunLevel runlevel); diff --git a/src/fw/services/common/shared_prf_storage/shared_prf_storage_debug.c b/src/fw/services/common/shared_prf_storage/shared_prf_storage_debug.c deleted file mode 100644 index 72af337fd0..0000000000 --- a/src/fw/services/common/shared_prf_storage/shared_prf_storage_debug.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "console/dbgserial.h" -#include "console/prompt.h" -#include "system/hexdump.h" -#include "system/logging.h" -#include "util/string.h" - -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/common/bluetooth/bluetooth_persistent_storage_debug.h" - - -#include -#include -#include -#include -#include - -void shared_prf_storage_dump_contents(void) { - prompt_send_response("---Shared PRF Contents---\n------------------------\n"); - - char buf[DISPLAY_BUF_LEN]; - SMPairingInfo pairing_info; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - bool requires_address_pinning; - uint8_t flags; - if (shared_prf_storage_get_ble_pairing_data(&pairing_info, name, &requires_address_pinning, - &flags)) { - bluetooth_persistent_storage_debug_dump_ble_pairing_info(&buf[0], &pairing_info); - prompt_send_response_fmt(buf, sizeof(buf), - "Req addr pin: %u, flags: %x, BLE Dev Name: %s", - requires_address_pinning, flags, name); - } else { - prompt_send_response("No BLE Data"); - } - - SM128BitKey keys[SMRootKeyTypeNum]; - if (shared_prf_storage_get_root_key(SMRootKeyTypeEncryption, &keys[SMRootKeyTypeEncryption]) && - shared_prf_storage_get_root_key(SMRootKeyTypeIdentity, &keys[SMRootKeyTypeIdentity])) { - bluetooth_persistent_storage_debug_dump_root_keys(&keys[SMRootKeyTypeIdentity], - &keys[SMRootKeyTypeEncryption]); - } else { - prompt_send_response("Missing IRK and/or ERK root key(s)!"); - } - - BTDeviceAddress addr; - - if (shared_prf_storage_get_ble_pinned_address(&addr)) { - prompt_send_response_fmt(buf, DISPLAY_BUF_LEN, "\nPinned address: "BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE_PTR(&addr)); - } - - SM128BitKey link_key; - uint8_t platform_bits; - - if (bt_driver_supports_bt_classic()) { - if (shared_prf_storage_get_bt_classic_pairing_data(&addr, &name[0], - &link_key, &platform_bits)) { - bluetooth_persistent_storage_debug_dump_classic_pairing_info( - &buf[0], &addr, &name[0], &link_key, platform_bits); - } else { - prompt_send_response("No BT classic data"); - } - } - - if (shared_prf_storage_get_local_device_name(name, BT_DEVICE_NAME_BUFFER_SIZE)) { - prompt_send_response_fmt(buf, BT_DEVICE_NAME_BUFFER_SIZE, "Local device name: %s", name); - } else { - prompt_send_response("No Device Name"); - } - - prompt_send_response_fmt(buf, BT_DEVICE_NAME_BUFFER_SIZE, "Started Complete: %s", - bool_to_str(shared_prf_storage_get_getting_started_complete())); -} diff --git a/src/fw/services/common/shared_prf_storage/shared_prf_storage_debug.h b/src/fw/services/common/shared_prf_storage/shared_prf_storage_debug.h deleted file mode 100644 index 1d55b5934a..0000000000 --- a/src/fw/services/common/shared_prf_storage/shared_prf_storage_debug.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void shared_prf_storage_dump_contents(void); diff --git a/src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage.c b/src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage.c deleted file mode 100644 index 5d9c2874b9..0000000000 --- a/src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "shared_prf_storage_private.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "kernel/pbl_malloc.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "util/size.h" - -#include -#include -#include - -static RegularTimerInfo s_shared_prf_writeback_timer; -typedef struct { - SharedPRFData pending_data; - bool has_bt_classic_pairing_pending; - bool has_ble_pairing_pending; -} SharedPRFPendingBondings; - -static SharedPRFPendingBondings s_pending_bondings = { }; - -static PebbleMutex *s_pending_data_mutex = NULL; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Private functions - -static void prv_lock_pending_bonding(void) { - mutex_lock(s_pending_data_mutex); -} - -static void prv_unlock_pending_bonding(void) { - mutex_unlock(s_pending_data_mutex); -} - -static void prv_shared_prf_reschedule_writeback_timer(void) { - if (regular_timer_is_scheduled(&s_shared_prf_writeback_timer)) { - regular_timer_remove_callback(&s_shared_prf_writeback_timer); - } - regular_timer_add_multiminute_callback(&s_shared_prf_writeback_timer, 5); -} - -// Helper to wipe memory to avoid leaking secrets stored in PRF shared storage through used stack -static void prv_cleanup_struct(SharedPRFData *data_out) { - *data_out = (SharedPRFData){}; -} - -static void prv_get_empty_struct(SharedPRFData *data_out) { - *data_out = (SharedPRFData) { .version = SHARED_PRF_STORAGE_VERSION }; -} - -static void prv_fetch_struct(SharedPRFData *data_out) { - flash_read_bytes((uint8_t*) data_out, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, sizeof(*data_out)); - - if (data_out->version != SHARED_PRF_STORAGE_VERSION) { - // No data present, just return an empty struct with the current version set. - prv_get_empty_struct(data_out); - } -} - -static void prv_perform_write(SharedPRFData *data, bool should_erase) { - if (should_erase) { - flash_erase_subsector_blocking(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN); - } - - flash_write_bytes((const uint8_t*) data, FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, sizeof(*data)); -} - -static void prv_apply_patch_to_struct(void *patch, size_t size, size_t offset, bool should_erase) { - SharedPRFData data; - prv_fetch_struct(&data); - - uint8_t *unmodified_data_patch_start = ((uint8_t *)&data) + offset; - // Struct is packed, so it's OK to use memcmp: - if (memcmp(unmodified_data_patch_start, patch, size) != 0) { - // There is new data present, so perform a write! - memcpy(unmodified_data_patch_start, patch, size); - prv_perform_write(&data, should_erase); - } - - prv_cleanup_struct(&data); -} - -typedef struct { - size_t size; - size_t offset; - uint8_t patch[]; -} WriteEraseCBData; - -static void prv_perform_flash_erase_write_cb(void *data_in) { - WriteEraseCBData *cb_data = (WriteEraseCBData*) data_in; - prv_apply_patch_to_struct(cb_data->patch, cb_data->size, cb_data->offset, true); - kernel_free(cb_data); -} - -// Does the flash write / erase on the background task -static void prv_update_and_cleanup_struct_async(const void *data, size_t size, size_t offset) { - WriteEraseCBData *callback_data = kernel_malloc_check(sizeof(*callback_data) + size); - *callback_data = (WriteEraseCBData) { - .size = size, - .offset = offset, - }; - memcpy(callback_data->patch, data, size); - system_task_add_callback(prv_perform_flash_erase_write_cb, callback_data); -} - -// Doesn't perform a flash erase. Should only be used to zero out parts / all of the struct -static void prv_update_and_cleanup_struct_no_erase(void *data, size_t size, size_t offset) { - prv_apply_patch_to_struct(data, size, offset, false); -} - -// Does the flash write / erase on the current task -static void prv_update_and_cleanup_struct(void *data, size_t size, size_t offset) { - prv_apply_patch_to_struct(data, size, offset, true); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Custom Local Device Name - -bool shared_prf_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { - SharedPRFData data; - prv_fetch_struct(&data); - - if (local_device_name_out) { - strncpy(local_device_name_out, data.local_device_name, max_size); - local_device_name_out[max_size - 1] = 0; - } - const bool result = (data.local_device_name[0] != 0); // Is not zero length? - prv_cleanup_struct(&data); - return result; -} - -void shared_prf_storage_set_local_device_name(const char *local_device_name) { - prv_update_and_cleanup_struct_async(local_device_name, - MEMBER_SIZE(SharedPRFData, local_device_name), - offsetof(SharedPRFData, local_device_name)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BLE Root Keys - -bool shared_prf_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { - SharedPRFData data; - prv_fetch_struct(&data); - bool result = false; - SM128BitKey nil_key = {}; - if (0 == memcmp(&nil_key, &data.root_keys[key_type], sizeof(nil_key))) { - goto done; - } - if (key_out) { - memcpy(key_out, &data.root_keys[key_type], sizeof(*key_out)); - } - result = true; -done: - prv_cleanup_struct(&data); - return result; -} - -void shared_prf_storage_set_root_keys(SM128BitKey *keys_in) { -#ifdef RECOVERY_FW - // This can't be async because after it is set, sm.c accesses this key right away and will assert - // if it isn't available yet. - prv_update_and_cleanup_struct(keys_in, MEMBER_SIZE(SharedPRFData, root_keys), - offsetof(SharedPRFData, root_keys)); -#else - // This can be async because the sm.c will read this key from bt_persistent_storage instead - prv_update_and_cleanup_struct_async(keys_in, MEMBER_SIZE(SharedPRFData, root_keys), - offsetof(SharedPRFData, root_keys)); -#endif -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BLE Pairing Data - -bool shared_prf_storage_get_ble_pairing_data(SMPairingInfo *pairing_info_out, - char *name_out, bool *requires_address_pinning_out, - uint8_t *flags) { - SharedPRFData data; - prv_fetch_struct(&data); - - const BLEPairingData *ble_data = &data.ble_data; - - bool result = false; - if (!ble_data->is_local_encryption_info_valid && - !ble_data->is_remote_encryption_info_valid && - !ble_data->is_remote_identity_info_valid && - !ble_data->is_remote_signing_info_valid) { - goto done; - } - - if (pairing_info_out) { - *pairing_info_out = (const SMPairingInfo) {}; - pairing_info_out->local_encryption_info.ediv = ble_data->local_ediv; - pairing_info_out->local_encryption_info.div = ble_data->local_div; - pairing_info_out->remote_encryption_info.ltk = ble_data->ltk; - pairing_info_out->remote_encryption_info.rand = ble_data->rand; - pairing_info_out->remote_encryption_info.ediv = ble_data->ediv; - pairing_info_out->irk = ble_data->irk; - pairing_info_out->identity = ble_data->identity; - pairing_info_out->csrk = ble_data->csrk; - pairing_info_out->is_local_encryption_info_valid = ble_data->is_local_encryption_info_valid; - pairing_info_out->is_remote_encryption_info_valid = ble_data->is_remote_encryption_info_valid; - pairing_info_out->is_remote_identity_info_valid = ble_data->is_remote_identity_info_valid; - pairing_info_out->is_remote_signing_info_valid = ble_data->is_remote_signing_info_valid; - } - if (name_out) { - strncpy(name_out, data.ble_data.name, BT_DEVICE_NAME_BUFFER_SIZE); - } - if (requires_address_pinning_out) { - // Not supported in v2 - *requires_address_pinning_out = false; - } - if (flags) { - // Not supported in v2 - *flags = 0; - } - result = true; -done: - prv_cleanup_struct(&data); - return result; -} - -static void prv_load_ble_pairing_data(BLEPairingData *data, - const SMPairingInfo *pairing_info, - const char *name) { - *data = (BLEPairingData) { - .local_ediv = pairing_info->local_encryption_info.ediv, - .local_div = pairing_info->local_encryption_info.div, - - .ltk = pairing_info->remote_encryption_info.ltk, - .rand = pairing_info->remote_encryption_info.rand, - .ediv = pairing_info->remote_encryption_info.ediv, - - .irk = pairing_info->irk, - .identity = pairing_info->identity, - - .csrk = pairing_info->csrk, - - .is_local_encryption_info_valid = pairing_info->is_local_encryption_info_valid, - .is_remote_encryption_info_valid = pairing_info->is_remote_encryption_info_valid, - .is_remote_identity_info_valid = pairing_info->is_remote_identity_info_valid, - .is_remote_signing_info_valid = pairing_info->is_remote_signing_info_valid, - }; - if (name) { - strncpy(data->name, name, BT_DEVICE_NAME_BUFFER_SIZE); - } -} - -static void prv_shared_prf_storage_store_ble_pairing_data(BLEPairingData *data) { -#ifdef RECOVERY_FW - // The callers of bt_persistent_storage expect this store to be synchronous. - // In PRF bt_persistent_storage is just a wrapper for this - prv_update_and_cleanup_struct(data, sizeof(*data), - offsetof(SharedPRFData, ble_data)); -#else - prv_update_and_cleanup_struct_async(data, sizeof(*data), - offsetof(SharedPRFData, ble_data)); -#endif -} - -void shared_prf_storage_store_ble_pairing_data( - const SMPairingInfo *pairing_info, const char *name, bool requires_address_pinning, - uint8_t flags) { - if (!pairing_info || sm_is_pairing_info_empty(pairing_info)) { - PBL_LOG(LOG_LEVEL_WARNING, "PRF Storage: Attempting to store an NULL or empty pairing info"); - return; - } - -#ifdef RECOVERY_FW - BLEPairingData data; - prv_load_ble_pairing_data(&data, pairing_info, name); - prv_shared_prf_storage_store_ble_pairing_data(&data); -#else - prv_lock_pending_bonding(); - { - shared_prf_storage_erase_ble_pairing_data(); - prv_load_ble_pairing_data(&s_pending_bondings.pending_data.ble_data, pairing_info, name); - s_pending_bondings.has_ble_pairing_pending = true; - prv_shared_prf_reschedule_writeback_timer(); - } - prv_unlock_pending_bonding(); -#endif -} - -void shared_prf_storage_erase_ble_pairing_data(void) { - BLEPairingData empty_data = {}; - - // Call the version that doesn't do a flash erase because we are only writing zeros. - prv_update_and_cleanup_struct_no_erase(&empty_data, sizeof(empty_data), - offsetof(SharedPRFData, ble_data)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BT Classic Pairing Data - -bool shared_prf_storage_get_bt_classic_pairing_data(BTDeviceAddress *addr_out, - char *device_name_out, - SM128BitKey *link_key_out, - uint8_t *platform_bits) { - SharedPRFData data; - prv_fetch_struct(&data); - - bool result = false; - const BTDeviceAddress invalid_address = (BTDeviceAddress) {}; - if (memcmp(&data.bt_classic_data.address, &invalid_address, sizeof(invalid_address)) == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid pairing stored"); - goto done; - } - - if (addr_out) { - *addr_out = data.bt_classic_data.address; - } - if (link_key_out) { - *link_key_out = data.bt_classic_data.link_key; - } - if (platform_bits) { - *platform_bits = data.bt_classic_data.platform_bits; - } - if (device_name_out) { - strcpy(device_name_out, data.bt_classic_data.name); - } - result = true; -done: - prv_cleanup_struct(&data); - return result; -} - -static void prv_load_bt_classic_pairing_data( - BTClassicPairingData *data, BTDeviceAddress *addr, const char *device_name, - SM128BitKey *link_key, uint8_t platform_bits) { - *data = (BTClassicPairingData) { - .address = *addr, - .link_key = *link_key, - .platform_bits = platform_bits - }; - strncpy(data->name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); -} - -static void prv_shared_prf_storage_store_bt_classic_pairing_data( - BTDeviceAddress *addr, const char *device_name, SM128BitKey *link_key, - uint8_t platform_bits) { - if (link_key) { - // New pairing - BTClassicPairingData data; - prv_load_bt_classic_pairing_data(&data, addr, device_name, link_key, platform_bits); - - prv_update_and_cleanup_struct_async(&data, - sizeof(BTClassicPairingData), - offsetof(SharedPRFData, bt_classic_data)); - } else { - // Just updating the name - prv_update_and_cleanup_struct_async(device_name, MEMBER_SIZE(BTClassicPairingData, name), - offsetof(SharedPRFData, bt_classic_data) + offsetof(BTClassicPairingData, name)); - } -} - -void shared_prf_storage_store_bt_classic_pairing_data( - BTDeviceAddress *addr, const char *device_name, SM128BitKey *link_key, uint8_t platform_bits) { - if (!addr || !device_name) { - PBL_LOG(LOG_LEVEL_WARNING, "PRF Storage: Can't store this BT classic pairing"); - return; - } - -#ifdef RECOVERY_FW - prv_shared_prf_storage_store_bt_classic_pairing_data(addr, device_name, link_key, platform_bits); -#else - prv_lock_pending_bonding(); - { - shared_prf_storage_erase_bt_classic_pairing_data(); - prv_load_bt_classic_pairing_data(&s_pending_bondings.pending_data.bt_classic_data, addr, - device_name, link_key, platform_bits); - s_pending_bondings.has_bt_classic_pairing_pending = true; - prv_shared_prf_reschedule_writeback_timer(); - } - prv_unlock_pending_bonding(); -#endif -} - -void shared_prf_storage_store_platform_bits(uint8_t platform_bits) { - prv_update_and_cleanup_struct_async(&platform_bits, - sizeof(platform_bits), - offsetof(SharedPRFData, bt_classic_data) + - offsetof(BTClassicPairingData, platform_bits)); -} - -void shared_prf_storage_erase_bt_classic_pairing_data(void) { - BTClassicPairingData empty_data = {}; - - // Call the version that doesn't do a flash erase because we are only writing zeros. - prv_update_and_cleanup_struct_no_erase(&empty_data, sizeof(empty_data), - offsetof(SharedPRFData, bt_classic_data)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Getting Started Is Complete - -bool shared_prf_storage_get_getting_started_complete(void) { - SharedPRFData data; - prv_fetch_struct(&data); - const bool is_complete = data.getting_started_is_complete; - prv_cleanup_struct(&data); - return is_complete; -} - -void shared_prf_storage_set_getting_started_complete(bool is_complete) { - prv_update_and_cleanup_struct_async(&is_complete, - sizeof(is_complete), - offsetof(SharedPRFData, getting_started_is_complete)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Factory Reset - -void shared_prf_storage_wipe_all(void) { - SharedPRFData data; - prv_get_empty_struct(&data); - prv_update_and_cleanup_struct(&data, sizeof(data), 0); -} - -static void prv_system_task_prf_update_cb(void *unused) { - PBL_LOG(LOG_LEVEL_DEBUG, "Syncing pairing information to SPRF"); - prv_lock_pending_bonding(); - { - bool le_bonding_update = s_pending_bondings.has_ble_pairing_pending; - bool classic_bonding_update = s_pending_bondings.has_bt_classic_pairing_pending; - - SharedPRFData *data = &s_pending_bondings.pending_data; - - if (le_bonding_update && classic_bonding_update) { - prv_update_and_cleanup_struct_async( - &data->ble_data, sizeof(BLEPairingData) + sizeof(BTClassicPairingData), - offsetof(SharedPRFData, ble_data)); - } else if (classic_bonding_update) { - prv_update_and_cleanup_struct_async(&data->bt_classic_data, sizeof(BTClassicPairingData), - offsetof(SharedPRFData, bt_classic_data)); - } else if (le_bonding_update) { - prv_shared_prf_storage_store_ble_pairing_data(&data->ble_data); - } - - memset(&s_pending_bondings, 0x00, sizeof(s_pending_bondings)); - } - prv_unlock_pending_bonding(); -} - -static void prv_async_shared_prf_update_timer_cb(void *data) { - system_task_add_callback(prv_system_task_prf_update_cb, NULL); - - prv_lock_pending_bonding(); - { - regular_timer_remove_callback(&s_shared_prf_writeback_timer); - } - prv_unlock_pending_bonding(); -} - -void shared_prf_storage_init(void) { - s_pending_data_mutex = mutex_create(); - - s_shared_prf_writeback_timer = (const RegularTimerInfo) { - .cb = prv_async_shared_prf_update_timer_cb, - }; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Pinned Address Stubs - -bool shared_prf_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { - return false; -} - -//! Stores the new BLE Pinned Address in the shared storage. -void shared_prf_storage_set_ble_pinned_address(const BTDeviceAddress *address) { -} - -void command_force_shared_prf_flush(void) { - if (regular_timer_is_scheduled(&s_shared_prf_writeback_timer)) { - regular_timer_remove_callback(&s_shared_prf_writeback_timer); - s_shared_prf_writeback_timer.cb(NULL); - } -} - -//! For unit tests -RegularTimerInfo *shared_prf_storage_get_writeback_timer(void) { - return &s_shared_prf_writeback_timer; -} diff --git a/src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage_private.h b/src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage_private.h deleted file mode 100644 index 8c95b21496..0000000000 --- a/src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage_private.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" - -#include -#include - -//! Used to version the struct if we have to add additional fields in the future. -//! 1: Added BLE and BT Classic pairing data -//! 2: Added getting started is complete bit -//! 3: Added remote Rand, remote EDIV, local DIV, local EDIV, is_..._valid flags, local device name -#define SHARED_PRF_STORAGE_VERSION 3 - -typedef struct PACKED { - // Remote device name - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - - // DIV / EDIV that was handed to the remote with our LTK (used when Pebble is Slave): - uint16_t local_ediv; - uint16_t local_div; - - // Remote encryption info (used when Pebble is Master): - SMLongTermKey ltk; - uint64_t rand; - uint16_t ediv; - - // Remote identity info (used when Pebble is Slave): - SMIdentityResolvingKey irk; - BTDeviceInternal identity; - - // Remote signature key: - SM128BitKey csrk; - - //! True if local_div and local_ediv are valid - bool is_local_encryption_info_valid:1; - - //! True if ltk, rand and ediv are valid - bool is_remote_encryption_info_valid:1; - - //! True if irk and identity are valid - bool is_remote_identity_info_valid:1; - - //! True if csrk is valid - //! @note Since iOS 9, CSRK is no longer exchanged. - bool is_remote_signing_info_valid:1; -} BLEPairingData; - -typedef struct PACKED { - BTDeviceAddress address; - SM128BitKey link_key; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits; -} BTClassicPairingData; - -typedef struct PACKED { - uint32_t version; - - // Customized local device name, or zero-length string if the default device name should be used - char local_device_name[BT_DEVICE_NAME_BUFFER_SIZE]; - - SM128BitKey root_keys[SMRootKeyTypeNum]; // ER and IR key - - // We rely on these two pieces of data being adjacent to each other - BLEPairingData ble_data; - BTClassicPairingData bt_classic_data; - - bool getting_started_is_complete; -} SharedPRFData; diff --git a/src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage.c b/src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage.c deleted file mode 100644 index 945474c581..0000000000 --- a/src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "shared_prf_storage_private.h" - -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/crc32.h" - -#include -#include - -#define SPRF_REGION_SIZE (FLASH_REGION_SHARED_PRF_STORAGE_END - \ - FLASH_REGION_SHARED_PRF_STORAGE_BEGIN) -#define SPRF_NUM_PAGES (SPRF_REGION_SIZE / sizeof(SharedPRFData)) - -#define SPRF_PAGE_FLASH_ADDR(idx) (FLASH_REGION_SHARED_PRF_STORAGE_BEGIN + \ - (idx * sizeof(SharedPRFData))) -// CRC Unwritten state and size -#define SPRF_UNWRITTEN_CRC ((uint32_t)0xFFFFFFFF) -#define SPRF_CRC_SIZE (sizeof(uint32_t)) - -// Accessors to a fields data and size. Basically skips over the CRC for the field. -#define SPRF_FIELD_DATA(f) (((uint8_t *)f) + SPRF_CRC_SIZE) -#define SPRF_FIELD_DATA_SIZE(sz) (sz - SPRF_CRC_SIZE) - -// Accessors for the CRC of a field -// A field always has the CRC as the first element, and the CRC is 4 bytes long -#define FIELD_CRC_FROM_FIELD(f) (*(uint32_t *)f) -// 'DATA', or 'd', is SharedPRFData struct. Offset, or 'off', is the offset of the field from the -// data struct. -#define FIELD_CRC_FROM_DATA(d, off) *((uint32_t *)(((uint8_t *)d) + off)) - -// Tests if a bit is set in the flags variable -#define SPRF_FLAG_IS_SET(flags, bit) (flags & bit) -// Returns a bit in the correct place -#define SPRF_FLAG_GET_BIT(b, bit) ((b) ? bit : 0) - -#define SPRF_CUR_VERSION 0x02 - -// Keeps track of the current page within the region that the valid (or empty) page is -static uint32_t s_valid_page_idx; -static PebbleMutex *s_sprf_mutex; - -// -// Helper functions -// - -static void prv_lock(void) { - mutex_lock(s_sprf_mutex); -} - -static void prv_unlock(void) { - mutex_unlock(s_sprf_mutex); -} - -static bool prv_buffer_empty(const uint8_t *buf, size_t num_bytes) { - for (uint32_t i = 0; i < num_bytes; i++) { - if (buf[i] != 0xFF) { - return false; - } - } - return true; -} - -static uint32_t prv_current_page_flash_addr(void) { - return SPRF_PAGE_FLASH_ADDR(s_valid_page_idx); -} - -static SprfMagic prv_get_magic_for_page(uint32_t page) { - SprfMagic magic; - flash_read_bytes((uint8_t *)&magic, SPRF_PAGE_FLASH_ADDR(page), sizeof(magic)); - return magic; -} - -// -// Struct validators -// - -//! Pass a field (starting at address of CRC) and a field size. Return whether the field is valid. -//! A valid struct means that: -// 1. The field has not been written to (both CRC and field data are blank) -// 2. The CRC and the written data match -static bool prv_field_valid(const uint8_t *field, size_t field_size) { - const bool empty = prv_buffer_empty(field, field_size); - if (empty) { - return true; - } - - const uint32_t field_crc = *(uint32_t*)field; - const bool valid_crc = (field_crc == - crc32(CRC32_INIT, SPRF_FIELD_DATA(field), SPRF_FIELD_DATA_SIZE(field_size))); - return valid_crc; -} - -//! Return whether the entire SharedPRFData given as an argument is valid. -//! It checks: -//! 1. That the struct header is either ValidEntry or UnpopulatedEntry. -//! 2. If the struct is an UnpopulatedEntry, it ensures the struct is entirely empty -//! 3. If the struct is *not* an UnpopulatedEntry, it ensures that each field is either empty -//! or written with a valid CRC. -static bool prv_valid_struct(SharedPRFData *data) { - if (data->magic == SprfMagic_UnpopulatedEntry && - prv_buffer_empty((uint8_t *)data, sizeof(*data))) { - return true; - } - - if (data->magic == SprfMagic_ValidEntry && - (prv_field_valid((uint8_t *)&data->root_keys, sizeof(data->root_keys)) && - prv_field_valid((uint8_t *)&data->ble_pairing_data, sizeof(data->ble_pairing_data)) && - prv_field_valid((uint8_t *)&data->ble_pairing_name, sizeof(data->ble_pairing_name)) && - prv_field_valid((uint8_t *)&data->pinned_address, sizeof(data->pinned_address)) && - prv_field_valid((uint8_t *)&data->local_name, sizeof(data->local_name)) && - prv_field_valid((uint8_t *)&data->getting_started, sizeof(data->getting_started)))) { - return true; - } - - return false; -} - -// Stored struct setters - -static void prv_write_to_current_page(SharedPRFData *data, bool write_metadata) { - if (data) { - if (write_metadata) { - data->magic = SprfMagic_ValidEntry; - data->version = SPRF_CUR_VERSION; - } - flash_write_bytes((uint8_t *)data, prv_current_page_flash_addr(), sizeof(*data)); - } -} - -static void prv_erase_region_and_save(SharedPRFData *data) { - flash_region_erase_optimal_range_no_watchdog(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, - FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, - FLASH_REGION_SHARED_PRF_STORAGE_END, - FLASH_REGION_SHARED_PRF_STORAGE_END); - s_valid_page_idx = 0; - prv_write_to_current_page(data, false); -} - -static void prv_invalidate_current_page(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "Invalidating current page: #%"PRIu32, s_valid_page_idx); - // First, check if the page is Unpopulated - SprfMagic magic = prv_get_magic_for_page(s_valid_page_idx); - if (magic == SprfMagic_UnpopulatedEntry) { - // This page is already Unpopulated. No need for invalidating - return; - } - - // Invalidate current page - SprfMagic new_magic = SprfMagic_InvalidatedEntry; - flash_write_bytes((uint8_t *)&new_magic, prv_current_page_flash_addr(), sizeof(SprfMagic)); - s_valid_page_idx++; - - // Sanity check to make sure that the page we are moving to is actually empty. - if ((s_valid_page_idx >= SPRF_NUM_PAGES) || - (prv_get_magic_for_page(s_valid_page_idx) != SprfMagic_UnpopulatedEntry)) { - PBL_LOG(LOG_LEVEL_WARNING, "Ran out of pages or found corrupted next page, erasing region"); - // NOTE: This should not happen often. On boot, we delete and rewrite the region if >75% of - // regions are filled. In the worst case, this will happen if the user pair/repairs - // NUM_REGIONS * .25 times without rebooting in between. (e.g. 16 pages. - // We boot up on sector 12. User pairs 4 times, we now want to access page 16, - // that is past our region, we need to clean up. - // - // We've run out of blank pages. Delete the entire region and roll around to the front. - // This will take some time. - prv_erase_region_and_save(NULL); - s_valid_page_idx = 0; - } -} - -// -// SharedPRFData allocators, deallocators, and getters -// - -static void prv_fetch_struct(SharedPRFData *data_out) { - flash_read_bytes((uint8_t *)data_out, prv_current_page_flash_addr(), sizeof(*data_out)); - - if (!prv_valid_struct(data_out)) { - PBL_LOG(LOG_LEVEL_WARNING, "Shared PRF Storage sector # %"PRIu32" is corrupted. Invalidating" - " and starting a new one", s_valid_page_idx); - prv_invalidate_current_page(); - memset(data_out, 0xFF, sizeof(*data_out)); - } -} - -static SharedPRFData *prv_alloc_and_fetch_struct(void) { - SharedPRFData *data = kernel_zalloc_check(sizeof(SharedPRFData)); - prv_fetch_struct(data); - return data; -} - -static void prv_dealloc_struct(SharedPRFData *data) { - kernel_free(data); -} - -static void prv_persist_field(uint8_t *field, size_t offset, size_t field_size, bool calc_crc) { - SharedPRFData *data = prv_alloc_and_fetch_struct(); - - const size_t field_data_size = SPRF_FIELD_DATA_SIZE(field_size); - const uint32_t old_crc = FIELD_CRC_FROM_DATA(data, offset); - const uint32_t new_crc = (calc_crc) ? crc32(CRC32_INIT, SPRF_FIELD_DATA(field), field_data_size) - : SPRF_UNWRITTEN_CRC; - const bool same_data = - (0 == memcmp(SPRF_FIELD_DATA(field), SPRF_FIELD_DATA(((uint8_t *)data) + offset), - field_data_size)); - - if (data->magic == SprfMagic_UnpopulatedEntry) { - // Struct that is written is currently unpopulated. Set it up and write - // the struct's Magic and Version - prv_write_to_current_page(data, true); - } else if ((old_crc == new_crc) && same_data) { - // We are trying to write the same data, ignore the write - goto cleanup; - } else if (old_crc != SPRF_UNWRITTEN_CRC) { - // We are writing different data. Clear the field, write the struct again, and later write - // the new data in the empty field - memset(((uint8_t *)data) + offset, 0xFF, field_size); - prv_invalidate_current_page(); - prv_write_to_current_page(data, true); - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Overwriting SPRF field at offset %d, size %d", - (int)offset, (int)field_size); - - // write the crc first so it's easier to detect a non empty field (we can just read if the CRC is - // not 0xFFFFFFFF instead of comparing all bytes. - - *(uint32_t *)field = new_crc; - flash_write_bytes(field, prv_current_page_flash_addr() + offset, field_size); - -cleanup: - prv_dealloc_struct(data); -} - -static void prv_erase_field(size_t offset, size_t field_size) { - SharedPRFData *data = prv_alloc_and_fetch_struct(); - uint8_t *field_ptr = ((uint8_t *)data) + offset; - memset(field_ptr, 0xFF, field_size); - prv_persist_field(field_ptr, offset, field_size, false); - prv_dealloc_struct(data); -} - -static bool prv_fetch_field(uint8_t *field_out, size_t offset, size_t field_size) { - flash_read_bytes(field_out, prv_current_page_flash_addr() + offset, field_size); - if (!prv_field_valid(field_out, field_size)) { - // If corrupted field, delete entire page - PBL_LOG(LOG_LEVEL_WARNING, "Shared PRF Storage sector # %"PRIu32" is corrupted. Invalidating" - " and starting a new one", s_valid_page_idx); - prv_invalidate_current_page(); - return false; - } - - uint32_t written_crc = FIELD_CRC_FROM_FIELD(field_out); - if (written_crc == SPRF_UNWRITTEN_CRC) { - // If empty field, return that we have invalid data too - return false; - } - - return true; -} - -#define SPRF_PERSIST_FIELD(data, name) \ - prv_persist_field((uint8_t *)&data, offsetof(SharedPRFData, name), sizeof(data), true) -#define SPRF_ERASE_FIELD(name) \ - prv_erase_field(offsetof(SharedPRFData, name), sizeof(((SharedPRFData *)0)->name)) -#define SPRF_FETCH_FIELD(data, name) \ - prv_fetch_field((uint8_t *)&data, offsetof(SharedPRFData, name), sizeof(data)) - -//! -//! SharedPRFStorage API -//! - -//! Scans through the shared PRF flash region and finds the valid entry. -//! (Should only ever be one!) If we are > 75% through the shared PRF region, -//! erase the sector and re-write the info at offset 0. We want to make the chance -//! of blocking on an erase ~0, by doing this prep on init. -void shared_prf_storage_init(void) { - s_sprf_mutex = mutex_create(); - - prv_lock(); - { - s_valid_page_idx = SPRF_PAGE_IDX_INVALID; - - SharedPRFData data = {}; - SprfMagic page_magic = 0; - - for (uint32_t i = 0; i < SPRF_NUM_PAGES; i++) { - page_magic = prv_get_magic_for_page(i); - // Check the magic to see if we need to investigate further and read the entire contents. - if (page_magic == SprfMagic_ValidEntry || page_magic == SprfMagic_UnpopulatedEntry) { - flash_read_bytes((uint8_t *) &data, SPRF_PAGE_FLASH_ADDR(i), sizeof(data)); - if (prv_valid_struct(&data)) { - s_valid_page_idx = i; - break; - } - } - } - - // Keep a write offset, this won't work when we try to roll over to the other 25% of the sectors - if (s_valid_page_idx == SPRF_PAGE_IDX_INVALID) { - prv_erase_region_and_save(NULL); - } else if (s_valid_page_idx > (SPRF_MAX_NUM_PAGES_MULT(SPRF_NUM_PAGES))) { - prv_erase_region_and_save(&data); - } - } - prv_unlock(); -} - -void shared_prf_storage_wipe_all(void) { - prv_lock(); - prv_invalidate_current_page(); - prv_unlock(); -} - -//! -//! Custom Local Device Name APIs -//! - -bool shared_prf_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { - bool rv; - prv_lock(); - { - SprfLocalName data; - rv = SPRF_FETCH_FIELD(data, local_name); - if (!rv) { - goto unlock; - } - - if (local_device_name_out) { - strncpy(local_device_name_out, data.name, max_size); - local_device_name_out[max_size - 1] = 0; - } - - // Is not zero length? - rv = (data.name[0] != 0); - } - -unlock: - prv_unlock(); - return rv; -} - -void shared_prf_storage_set_local_device_name(const char *local_device_name) { - prv_lock(); - { - SprfLocalName data = {}; - - if (local_device_name) { - strncpy(data.name, local_device_name, sizeof(data.name)); - SPRF_PERSIST_FIELD(data, local_name); - } else { - SPRF_ERASE_FIELD(local_name); - } - } - prv_unlock(); -} - -//! -//! BLE Root Key APIs -//! - -bool shared_prf_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { - bool rv; - prv_lock(); - { - SprfRootKeys data; - rv = SPRF_FETCH_FIELD(data, root_keys); - if (!rv) { - goto unlock; - } - - SM128BitKey nil_key = {}; - if (0 == memcmp(&nil_key, &data.keys[key_type], sizeof(nil_key))) { - rv = false; - goto unlock; - } - if (key_out) { - memcpy(key_out, &data.keys[key_type], sizeof(*key_out)); - } - - rv = true; - } - -unlock: - prv_unlock(); - return rv; -} - -void shared_prf_storage_set_root_keys(SM128BitKey *keys_in) { - prv_lock(); - { - SprfRootKeys data = {}; - if (keys_in) { - memcpy(&data.keys, keys_in, sizeof(data.keys)); - } - SPRF_PERSIST_FIELD(data, root_keys); - } - prv_unlock(); -} - -//! -//! BLE Pairing Data APIs -//! - -bool shared_prf_storage_get_ble_pairing_data(SMPairingInfo *pairing_info_out, char *name_out, - bool *requires_address_pinning_out, - uint8_t *flags) { - bool rv; - prv_lock(); - { - SprfBlePairingData data; - rv = SPRF_FETCH_FIELD(data, ble_pairing_data); - if (!rv) { - goto unlock; - } - - if (!data.fields) { - // The pairing stored is empty - rv = false; - goto unlock; - } - - if (pairing_info_out) { - *pairing_info_out = (SMPairingInfo) { - .local_encryption_info.ltk = data.l_ltk, - .local_encryption_info.ediv = data.l_ediv, - .local_encryption_info.rand = data.l_rand, - - .remote_encryption_info.ltk = data.r_ltk, - .remote_encryption_info.ediv = data.r_ediv, - .remote_encryption_info.rand = data.r_rand, - - .irk = data.irk, - .identity = data.identity, - .csrk = data.csrk, - - .is_mitm_protection_enabled = data.is_mitm_protection_enabled, - - .is_local_encryption_info_valid = - SPRF_FLAG_IS_SET(data.fields, SprfValidFields_LocalEncryptionInfoValid), - .is_remote_encryption_info_valid = - SPRF_FLAG_IS_SET(data.fields, SprfValidFields_RemoteEncryptionInfoValid), - .is_remote_identity_info_valid = - SPRF_FLAG_IS_SET(data.fields, SprfValidFields_RemoteIdentityInfoValid), - .is_remote_signing_info_valid = - SPRF_FLAG_IS_SET(data.fields, SprfValidFields_RemoteSigningInfoValid), - }; - } - - if (requires_address_pinning_out) { - *requires_address_pinning_out = data.requires_address_pinning; - } - if (flags) { - *flags = data.flags; - } - - if (name_out) { - SprfBlePairingName name_data = {}; - const bool name_rv = SPRF_FETCH_FIELD(name_data, ble_pairing_name); - // Should we return a failure on a failed name get? - if (name_rv) { - strncpy(name_out, name_data.name, BT_DEVICE_NAME_BUFFER_SIZE); - } else { - name_out[0] = '\0'; - } - } - rv = true; - } - -unlock: - prv_unlock(); - return rv; -} - -void shared_prf_storage_store_ble_pairing_data( - const SMPairingInfo *pairing_info, const char *name, bool requires_address_pinning, - uint8_t flags) { - if (!pairing_info || sm_is_pairing_info_empty(pairing_info)) { - PBL_LOG(LOG_LEVEL_WARNING, "PRF Storage: Attempting to store an NULL or empty pairing info"); - return; - } - - prv_lock(); - { - SprfBlePairingData data = (SprfBlePairingData) { - .l_ediv = pairing_info->local_encryption_info.ediv, - .l_ltk = pairing_info->local_encryption_info.ltk, - .l_rand = pairing_info->local_encryption_info.rand, - - .r_ediv = pairing_info->remote_encryption_info.ediv, - .r_ltk = pairing_info->remote_encryption_info.ltk, - .r_rand = pairing_info->remote_encryption_info.rand, - - .irk = pairing_info->irk, - .identity = pairing_info->identity, - - .csrk = pairing_info->csrk, - - .is_mitm_protection_enabled = pairing_info->is_mitm_protection_enabled, - - .requires_address_pinning = requires_address_pinning, - - .flags = flags, - - .fields = SPRF_FLAG_GET_BIT(pairing_info->is_local_encryption_info_valid, - SprfValidFields_LocalEncryptionInfoValid) | - SPRF_FLAG_GET_BIT(pairing_info->is_remote_encryption_info_valid, - SprfValidFields_RemoteEncryptionInfoValid) | - SPRF_FLAG_GET_BIT(pairing_info->is_remote_identity_info_valid, - SprfValidFields_RemoteIdentityInfoValid) | - SPRF_FLAG_GET_BIT(pairing_info->is_remote_signing_info_valid, - SprfValidFields_RemoteSigningInfoValid), - }; - - SPRF_PERSIST_FIELD(data, ble_pairing_data); - - if (name) { - // only persist name if one is actually included - SprfBlePairingName name_data = {}; - strncpy(name_data.name, name, sizeof(name_data.name)); - SPRF_PERSIST_FIELD(name_data, ble_pairing_name); - } - } - prv_unlock(); -} - -void shared_prf_storage_erase_ble_pairing_data(void) { - prv_lock(); - { - SPRF_ERASE_FIELD(ble_pairing_data); - SPRF_ERASE_FIELD(ble_pairing_name); - } - prv_unlock(); -} - -//! -//! Getting started bit -//! - -bool shared_prf_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { - bool rv; - prv_lock(); - { - SprfPinnedAddress data; - rv = SPRF_FETCH_FIELD(data, pinned_address); - if (!rv) { - goto unlock; - } - - if (address_out) { - *address_out = data.pinned_address; - } - - rv = true; - } - -unlock: - prv_unlock(); - return rv; -} - -//! Stores the new BLE Pinned Address in the shared storage. -void shared_prf_storage_set_ble_pinned_address(const BTDeviceAddress *address) { - prv_lock(); - { - if (address) { - SprfPinnedAddress data = { - .pinned_address = *address, - }; - SPRF_PERSIST_FIELD(data, pinned_address); - } else { - SPRF_ERASE_FIELD(pinned_address); - } - } - prv_unlock(); -} - -//! -//! Getting started bit -//! - -bool shared_prf_storage_get_getting_started_complete(void) { - bool rv; - prv_lock(); - { - SprfGettingStarted data; - rv = SPRF_FETCH_FIELD(data, getting_started); - if (!rv) { - goto unlock; - } - rv = data.is_complete; - } -unlock: - prv_unlock(); - return rv; -} - -void shared_prf_storage_set_getting_started_complete(bool set) { - prv_lock(); - { - SprfGettingStarted data = { - .is_complete = set - }; - SPRF_PERSIST_FIELD(data, getting_started); - } - prv_unlock(); -} - -//! -//! Legacy Stubs for BT Classic - Should never be called so assert if they are! -//! - -bool shared_prf_storage_get_bt_classic_pairing_data( - BTDeviceAddress *addr_out, char *device_name_out, SM128BitKey *link_key_out, - uint8_t *platform_bits) { - WTF; -} - -void shared_prf_storage_store_bt_classic_pairing_data( - BTDeviceAddress *addr, const char *device_name, SM128BitKey *link_key, - uint8_t platform_bits) { - WTF; -} - -void shared_prf_storage_store_platform_bits(uint8_t platform_bits) { - WTF; -} - -void shared_prf_storage_erase_bt_classic_pairing_data(void) { - WTF; -} - -void shared_prf_store_pairing_data( - SMPairingInfo *pairing_info, const char *device_name_ble, BTDeviceAddress *addr, - const char *device_name_classic, SM128BitKey *link_key, uint8_t platform_bits) { - WTF; -} - -void command_force_shared_prf_flush(void) { -} - - -//! -//! Unit test functions -//! - -uint16_t shared_prf_storage_get_valid_page_number(void) { - return s_valid_page_idx; -} - -void shared_prf_storage_set_valid_page_number(uint32_t page_num) { - s_valid_page_idx = page_num; -} diff --git a/src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage_private.h b/src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage_private.h deleted file mode 100644 index d9299cb438..0000000000 --- a/src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage_private.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/common/shared_prf_storage/shared_prf_storage.h" - -#include -#include - -#include - -#define SPRF_PAGE_IDX_INVALID ((uint16_t)~0) - -#define SPRF_MAX_NUM_PAGES_MULT(num) ((num) * 3 / 4) - -typedef enum { - SprfValidFields_LocalEncryptionInfoValid = (1 << 0), - SprfValidFields_RemoteEncryptionInfoValid = (1 << 1), - SprfValidFields_RemoteIdentityInfoValid = (1 << 2), - SprfValidFields_RemoteSigningInfoValid = (1 << 3), -} SprfValidFields; - -#ifndef __clang__ -_Static_assert(sizeof(SprfValidFields) == 1, "SprfValidFields unexpected size"); -#endif - - -typedef enum { - SprfMagic_ValidEntry = 0x46525053, - SprfMagic_UnpopulatedEntry = 0xFFFFFFFF, - SprfMagic_InvalidatedEntry = 0x0 -} SprfMagic; - -_Static_assert(sizeof(SprfMagic) == 4, "SprfMagic unexpected size"); - -//! This is the struct written out to the Shared PRF flash region -//! -//! It's composed of seven sub entries: -//! root_keys: Root keys (only identity since that is all Dialog needs) -//! ble_pairing_data: The pairing info for the device most recently paired to the watch -//! ble_pairing_name: The name of the device most recently paired to the watch -//! pinned_address: Pinned address of the device most recently paired to watch (may be empty) -//! getting_started: Captures whether or not we have gone through onboarding -//! local_name: Not used yet, but saved for future proofing -//! main_fw_scratch: A region for normal fw to stash info in the future if needed -//! -//! Each entry, or field, has its own crc which is written once the write of the field is complete. -//! @NOTE: The CRC _must_ be the first member of a field. There are static asserts to catch -//! this for current, please add a static assert for this if you create a new field -//! -//! A field is 'valid' iff a CRC of its contents matches the crc in flash -//! A field is 'unpopulated' if a memcmp of its contents is all 0xff. -//! A field is 'deleted' if its header has the value of SprfMagic_InvalidatedEntry -//! A field is 'corrupted' or 'partially written' if the content CRC does not match the field CRC -//! -//! On flash, there is a rolling list of entries. If a field above needs to be rewritten, -//! 'valid' entries must be copied from the current flash area to the next adjacent one. -//! -//! The shared PRF struct itself is defined as 256 bytes. Flash architectures have sectors -//! which are some 2^n multiple so this size pretty much guarantees that a divisible number -//! of structs can fit in the region allocated - -typedef struct PACKED SprfRootKeys { - uint32_t crc; - SM128BitKey keys[SMRootKeyTypeNum]; -} SprfRootKeys; -_Static_assert(offsetof(SprfRootKeys, crc) == 0, "crc must be the first field"); - -typedef struct PACKED SprfBlePairingData { - uint32_t crc; // CRC over the 'pairing_data' struct ('name' through 'fields') - - // local encryption data - SMLongTermKey l_ltk; // 16 byte key - uint64_t l_rand; - uint16_t l_ediv; - - // remote encryption data - uint16_t r_ediv; - SMLongTermKey r_ltk; - uint64_t r_rand; - - SMIdentityResolvingKey irk; // 16 byte key - SMConnectionSignatureResolvingKey csrk; // 16 byte key - BTDeviceInternal identity; - - SprfValidFields fields:8; - bool is_mitm_protection_enabled; - bool requires_address_pinning; - - //! Added in SPRF_CUR_VERSION 2. In SPRF_CUR_VERSION 1, this field is always 0x00. - uint8_t flags; -} SprfBlePairingData; -_Static_assert(offsetof(SprfBlePairingData, crc) == 0, "crc must be the first field"); - -typedef struct PACKED SprfBlePairingName { - uint32_t crc; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; -} SprfBlePairingName; -_Static_assert(offsetof(SprfBlePairingName, crc) == 0, "crc must be the first field"); - -typedef struct PACKED SprfPinnedAddress { - uint32_t crc; - BTDeviceAddress pinned_address; - uint8_t rsvd[2]; -} SprfPinnedAddress; -_Static_assert(offsetof(SprfPinnedAddress, crc) == 0, "crc must be the first field"); - -typedef struct PACKED SprfGettingStarted { - uint32_t crc; - bool is_complete; - uint8_t rsvd[3]; -} SprfGettingStarted; -_Static_assert(offsetof(SprfGettingStarted, crc) == 0, "crc must be the first field"); - -typedef struct PACKED SprfLocalName { - // Not used today, but in the future we could replace 'Pebble XXXX' with - // a user friendly name, 'Chris' Pebble' - uint32_t crc; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; -} SprfLocalName; -_Static_assert(offsetof(SprfLocalName, crc) == 0, "crc must be the first field"); - -typedef struct PACKED SharedPRFData { - SprfMagic magic; - uint8_t version; - uint8_t rsvd[3]; - - SprfRootKeys root_keys; - SprfBlePairingData ble_pairing_data; - SprfBlePairingName ble_pairing_name; - SprfPinnedAddress pinned_address; - SprfGettingStarted getting_started; - SprfLocalName local_name; - - // Occasions have arisen in the past where a region in sharedPRF that - // main FW can stash info related to a pairing. That is the intent of this region. - struct PACKED { - uint8_t rsvd[44]; - } main_fw_scratch; -} SharedPRFData; - -_Static_assert(BT_DEVICE_NAME_BUFFER_SIZE == 20, "Changing the length will break SharedPRF"); -_Static_assert(sizeof(SharedPRFData) == 256, "SharedPRFData does not match expected size"); diff --git a/src/fw/services/common/status_led.c b/src/fw/services/common/status_led.c deleted file mode 100644 index 3f241a687f..0000000000 --- a/src/fw/services/common/status_led.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/status_led.h" -#include "system/passert.h" -#include "services/common/battery/battery_curve.h" -#include "drivers/led_controller.h" -#include "board/board.h" -#include "system/passert.h" - -#if CAPABILITY_HAS_LED - -static uint32_t s_led_color = LED_BLACK; - -void status_led_set(StatusLedState state) { - PBL_ASSERTN(state < StatusLedStateCount); - - static const uint32_t STATE_COLOR_MAPPING[] = { - [StatusLedState_Off] = LED_BLACK, - [StatusLedState_Charging] = LED_DIM_ORANGE, - [StatusLedState_FullyCharged] = LED_DIM_GREEN - }; - - const uint32_t new_color = STATE_COLOR_MAPPING[state]; - - if (new_color == s_led_color) { - return; - } - - s_led_color = new_color; - - // Tell the battery curve service to account for the updated LED state. - int compenstation_mv = 0; - if (s_led_color != LED_BLACK) { - compenstation_mv = BOARD_CONFIG_POWER.charging_status_led_voltage_compensation; - } - battery_curve_set_compensation(BATTERY_CURVE_COMPENSATE_STATUS_LED, compenstation_mv); - - led_controller_rgb_set_color(s_led_color); -} - -#else - -void status_led_set(StatusLedState state) { - // No LED present, do nothing! -} - -#endif // CAPABILITY_HAS_LED diff --git a/src/fw/services/common/status_led.h b/src/fw/services/common/status_led.h deleted file mode 100644 index 4f048b6045..0000000000 --- a/src/fw/services/common/status_led.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Different states supported by the status LED. -typedef enum { - StatusLedState_Off, - StatusLedState_Charging, - StatusLedState_FullyCharged, - - StatusLedStateCount -} StatusLedState; - -//! Set the status LED to a new state. Note that this function is a no-op on boards that don't -//! have a status LED. -void status_led_set(StatusLedState state); diff --git a/src/fw/services/common/system_task.c b/src/fw/services/common/system_task.c deleted file mode 100644 index a428aa1915..0000000000 --- a/src/fw/services/common/system_task.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/system_task.h" - -#include "system/logging.h" - -#include "drivers/task_watchdog.h" -#include "kernel/pebble_tasks.h" -#include "kernel/util/task_init.h" -#include "mcu/fpu.h" -#include "os/tick.h" -#include "services/common/regular_timer.h" -#include "system/passert.h" - -#include "FreeRTOS.h" -#include "queue.h" -#include "task.h" - -#define SYSTEM_TASK_PRIORITY (tskIDLE_PRIORITY + 1) - -typedef struct { - SystemTaskEventCallback cb; - void *data; -} SystemTaskEvent; - -static QueueHandle_t s_system_task_queue; -static QueueHandle_t s_from_app_system_task_queue; - -static QueueSetHandle_t s_system_task_queue_set; - -static SystemTaskEventCallback s_current_cb; - -static bool s_system_task_idle = true; -static bool s_should_block_callbacks = false; - -static bool prv_is_accepting_callbacks() { - return s_system_task_queue != 0 && !s_should_block_callbacks; -} - -static void system_task_idle_timer_callback(void* data) { - if (s_system_task_idle && uxQueueMessagesWaiting(s_system_task_queue_set) == 0) { - system_task_watchdog_feed(); - } -} - -static void system_task_main(void* paramater) { - task_watchdog_mask_set(PebbleTask_KernelBackground); - task_init(); - - while (true) { - s_system_task_idle = true; - - SystemTaskEvent event; - - QueueSetMemberHandle_t activated_queue = xQueueSelectFromSet(s_system_task_queue_set, portMAX_DELAY); - - // Get event from the activated queue - portBASE_TYPE result = xQueueReceive(activated_queue, &event, 0); - - // I believe its possible that we just reset the queue and accidently - // pended an extra event to the queue set so handle that case gracefully - if (result) { - s_system_task_idle = false; - s_current_cb = event.cb; - event.cb(event.data); - mcu_fpu_cleanup(); - s_current_cb = NULL; - } - - // Refresh the watchdog immediately, just in case that cb() took awhile to run. - system_task_watchdog_feed(); - } -} - -void system_task_init(void) { - static const int SYSTEM_TASK_QUEUE_LENGTH = 30; - static const int FROM_APP_SYSTEM_TASK_QUEUE_LENGTH = 8; - - s_system_task_queue = xQueueCreate(SYSTEM_TASK_QUEUE_LENGTH, sizeof(SystemTaskEvent)); - s_from_app_system_task_queue = xQueueCreate(FROM_APP_SYSTEM_TASK_QUEUE_LENGTH, sizeof(SystemTaskEvent)); - - s_system_task_queue_set = xQueueCreateSet(SYSTEM_TASK_QUEUE_LENGTH + FROM_APP_SYSTEM_TASK_QUEUE_LENGTH); - xQueueAddToSet(s_system_task_queue, s_system_task_queue_set); - xQueueAddToSet(s_from_app_system_task_queue, s_system_task_queue_set); - - extern uint32_t __kernel_bg_stack_start__[]; - extern uint32_t __kernel_bg_stack_size__[]; - extern uint32_t __stack_guard_size__[]; - const uint32_t kernel_bg_stack_words = ( (uint32_t)__kernel_bg_stack_size__ - - (uint32_t)__stack_guard_size__) / sizeof(portSTACK_TYPE); - - TaskParameters_t task_params = { - .pvTaskCode = system_task_main, - .pcName = "KernelBG", - .usStackDepth = kernel_bg_stack_words, - .uxPriority = SYSTEM_TASK_PRIORITY | portPRIVILEGE_BIT, - .puxStackBuffer = (void*)(uintptr_t)((uint32_t)__kernel_bg_stack_start__ - + (uint32_t)__stack_guard_size__) - }; - - pebble_task_create(PebbleTask_KernelBackground, &task_params, NULL); -} - -void system_task_timer_init(void) { - // Register a regular timer to kick the watchdog while we're waiting for something - // to do. The other way to do this is to have the xQueueReceive in system_task_main timeout - // occasionally, but that isn't necessarily second aligned and will require the watch - // to wakeup from sleep just to kick the watchdog. This way it's kicked at the same time as - // all the other regular tasks. Note that the system_task_idle_timer_callback only kicks - // the watchdog if we're currently waiting for work to do on the system_task. If we're in the - // middle of something we won't kick it. - static RegularTimerInfo idle_watchdog_timer = { - .cb = system_task_idle_timer_callback - }; - regular_timer_add_seconds_callback(&idle_watchdog_timer); -} - -void system_task_watchdog_feed(void) { - task_watchdog_bit_set(PebbleTask_KernelBackground); -} - -static void handle_system_task_send_failure(SystemTaskEventCallback cb) { - register uintptr_t lr __asm("lr"); - uintptr_t saved_lr = lr; - - PBL_LOG(LOG_LEVEL_ERROR, "System task queue full. Dropped cb: %p, current cb: %p", cb, s_current_cb); - - RebootReason reason = { - .code = RebootReasonCode_EventQueueFull, - .event_queue = { - .push_lr = (uint32_t) saved_lr, - .current_event = (uint32_t) s_current_cb, - .dropped_event = (uint32_t) cb - } - }; - reboot_reason_set(&reason); - - reset_due_to_software_failure(); -} - -bool system_task_add_callback_from_isr(SystemTaskEventCallback cb, void *data, bool* should_context_switch) { - if (!prv_is_accepting_callbacks()) { - return false; - } - SystemTaskEvent event = { - .cb = cb, - .data = data, - }; - - signed portBASE_TYPE tmp; - bool success = (xQueueSendToBackFromISR(s_system_task_queue, &event, &tmp) == pdTRUE); - if (!success) { - handle_system_task_send_failure(cb); - } - - *should_context_switch = (tmp == pdTRUE); - - return success; -} - -bool system_task_add_callback(SystemTaskEventCallback cb, void *data) { - if (!prv_is_accepting_callbacks()) { - return false; - } - - SystemTaskEvent event = { - .cb = cb, - .data = data, - }; - - if (pebble_task_get_current() == PebbleTask_App) { - // If we're the app and we've filled up our system task, the app just gets to wait. - // FIXME: In the future when we want to bound the amount of time a syscall can take this will have to change. - xQueueSendToBack(s_from_app_system_task_queue, &event, portMAX_DELAY); - return true; - } else { - // Back ourselves up and wait a reasonable amount of time before failing. If the queue is really backed up - // we want to fall through to the handle_system_task_send_failure and not just get killed by the watchdog. - bool success = (xQueueSendToBack(s_system_task_queue, &event, milliseconds_to_ticks(3000)) == pdTRUE); - if (!success) { - handle_system_task_send_failure(cb); - } - } - - return true; -} - -void system_task_block_callbacks(bool block) { - s_should_block_callbacks = block; -} - -uint32_t system_task_get_available_space(void) { - const bool is_app = pebble_task_get_current() == PebbleTask_App; - return uxQueueSpacesAvailable(is_app ? s_from_app_system_task_queue : s_system_task_queue); -} - -void* system_task_get_current_callback(void) { - return s_current_cb; -} - -void system_task_enable_raised_priority(bool is_raised) { - const uint32_t raised_priority_level = tskIDLE_PRIORITY + 3; // Same as KernelMain / BT tasks - vTaskPrioritySet(pebble_task_get_handle_for_task(PebbleTask_KernelBackground), - (is_raised ? raised_priority_level : SYSTEM_TASK_PRIORITY) | portPRIVILEGE_BIT); -} - -bool system_task_is_ready_to_run(void) { - const eTaskState bg_task_state = - eTaskGetState(pebble_task_get_handle_for_task(PebbleTask_KernelBackground)); - // check if system task is ready to go (instead of e.g. waiting for a mutex) - return (bg_task_state == eReady); -} diff --git a/src/fw/services/common/tick_timer.c b/src/fw/services/common/tick_timer.c deleted file mode 100644 index b06e369117..0000000000 --- a/src/fw/services/common/tick_timer.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "tick_timer.h" - -#include "kernel/events.h" -#include "drivers/rtc.h" -#include "services/common/regular_timer.h" -#include "process_management/app_manager.h" -#include "system/logging.h" -#include "system/passert.h" - -static uint16_t s_num_subscribers; - -static void timer_tick_event_publisher(void* data) { - PebbleEvent e = { - .type = PEBBLE_TICK_EVENT, - .clock_tick.tick_time = rtc_get_time(), - }; - - event_put(&e); -} - -static RegularTimerInfo s_tick_timer_info = { - .cb = &timer_tick_event_publisher -}; - -void tick_timer_add_subscriber(PebbleTask task) { - ++s_num_subscribers; - if (s_num_subscribers == 1) { - PBL_LOG(LOG_LEVEL_DEBUG, "starting tick timer"); - regular_timer_add_seconds_callback(&s_tick_timer_info); - } -} - -void tick_timer_remove_subscriber(PebbleTask task) { - PBL_ASSERTN(s_num_subscribers > 0); - --s_num_subscribers; - if (s_num_subscribers == 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "stopping tick timer"); - regular_timer_remove_callback(&s_tick_timer_info); - } -} diff --git a/src/fw/services/common/tick_timer.h b/src/fw/services/common/tick_timer.h deleted file mode 100644 index 23135cd369..0000000000 --- a/src/fw/services/common/tick_timer.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/pebble_tasks.h" - -void tick_timer_add_subscriber(PebbleTask task); -void tick_timer_remove_subscriber(PebbleTask task); diff --git a/src/fw/services/common/touch/touch.c b/src/fw/services/common/touch/touch.c deleted file mode 100644 index 0521e3119e..0000000000 --- a/src/fw/services/common/touch/touch.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "touch.h" -#include "touch_event.h" -#include "touch_client.h" - -#include "kernel/events.h" -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" -#include "util/size.h" - -#define TOUCH_DEBUG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_TOUCH, LOG_LEVEL_DEBUG, fmt, ## args) - -#define TOUCH_QUEUE_LENGTH (2) - -typedef struct TouchContext { - TouchState state; - bool update_pending; - bool update_cancelled; - struct { - TouchEvent events[TOUCH_QUEUE_LENGTH]; - uint32_t count; - uint32_t read_idx; - } touch_queue; - GPoint start_pos; - TouchPressure start_pressure; - uint64_t start_time_ms; -} TouchContext; - -static TouchContext s_touch_ctx[MAX_NUM_TOUCHES]; - -static PebbleMutex *s_touch_mutex; - -void touch_init(void) { - s_touch_mutex = mutex_create(); -} - -// This should be called within a mutex protected section! -static void prv_touch_queue_reset(TouchContext *ctx) { - ctx->touch_queue.count = 0; - ctx->touch_queue.read_idx = 0; -} - -// This should be called within a mutex protected section! -static void prv_touch_context_reset(TouchContext *ctx) { - ctx->state = TouchState_FingerUp; - prv_touch_queue_reset(ctx); -} - -static bool prv_queue_is_full(TouchContext *ctx) { - return ctx->touch_queue.count == ARRAY_LENGTH(ctx->touch_queue.events); -} - -// Note: Do not call if the queue count is zero -static uint32_t prv_get_idx_last_item(TouchContext *ctx) { - return (ctx->touch_queue.read_idx + ctx->touch_queue.count - 1) % - ARRAY_LENGTH(ctx->touch_queue.events); -} - -static void prv_touch_queue_add(TouchContext *ctx, TouchIdx touch_idx, TouchEventType type, - const GPoint *pos, TouchPressure pressure, uint64_t time_ms) { - // Note: pos might be NULL for liftoff events - GPoint diff_pos = pos ? gpoint_sub(*pos, ctx->start_pos) : GPointZero; - int64_t diff_time = time_ms - ctx->start_time_ms; - TouchPressure diff_pressure = pressure - ctx->start_pressure; - uint32_t queue_idx; - if (!prv_queue_is_full(ctx)) { - queue_idx = (ctx->touch_queue.read_idx + ctx->touch_queue.count) % - ARRAY_LENGTH(ctx->touch_queue.events); - ctx->touch_queue.count++; - } else { - // overwrite the last event - queue_idx = prv_get_idx_last_item(ctx); - if (!pos) { - diff_pos = ctx->touch_queue.events[queue_idx].diff_pos; - } - } - TouchEvent *event = &ctx->touch_queue.events[queue_idx]; - *event = (TouchEvent) { - .index = touch_idx, - .type = type, - .start_pos = ctx->start_pos, - .start_time_ms = ctx->start_time_ms, - .start_pressure = ctx->start_pressure, - .diff_pos = (type == TouchEvent_Touchdown) ? GPointZero : diff_pos, - .diff_time_ms = (type == TouchEvent_Touchdown) ? 0 : diff_time, - .diff_pressure = (type == TouchEvent_Touchdown) ? 0 : diff_pressure - }; -} - -void touch_handle_update(TouchIdx touch_idx, TouchState touch_state, const GPoint *pos, - TouchPressure pressure, uint64_t time_ms) { - PBL_ASSERTN(touch_idx < MAX_NUM_TOUCHES); - - mutex_lock(s_touch_mutex); - - TouchContext *ctx = &s_touch_ctx[touch_idx]; - bool update = false; - if (ctx->state != touch_state) { - if (touch_state == TouchState_FingerDown) { - PBL_ASSERTN(pos); - - // Reset all state when a touchdown event occurs - prv_touch_context_reset(ctx); - ctx->start_pos = *pos; - ctx->start_time_ms = time_ms; - ctx->start_pressure = pressure; - prv_touch_queue_add(ctx, touch_idx, TouchEvent_Touchdown, pos, pressure, time_ms); - TOUCH_DEBUG("Touch %"PRIu8": Touchdown @ (%"PRId16", %"PRId16")", touch_idx, - pos->x, pos->y); - } else { - prv_touch_queue_add(ctx, touch_idx, TouchEvent_Liftoff, pos, 0, time_ms); - TOUCH_DEBUG("Touch %"PRIu8": Liftoff!", touch_idx); - } - update = true; - } else if (touch_state == TouchState_FingerDown) { - PBL_ASSERTN(pos); - if (ctx->touch_queue.count > 0) { - // don't update if the position hasn't changed - TouchEvent *last = &ctx->touch_queue.events[prv_get_idx_last_item(ctx)]; - GPoint last_pos = gpoint_add(last->start_pos, last->diff_pos); - update = !gpoint_equal(&last_pos, pos); - } else { - update = true; - } - if (update) { - TOUCH_DEBUG("Touch %"PRIu8": Position Update @ (%"PRId16", %"PRId16")", touch_idx, - pos->x, pos->y); - prv_touch_queue_add(ctx, touch_idx, TouchEvent_PositionUpdate, pos, pressure, time_ms); - } - } - ctx->state = touch_state; - - bool send_event = false; - if (update && !ctx->update_pending) { - ctx->update_pending = true; - send_event = true; - } - - mutex_unlock(s_touch_mutex); - - if (send_event) { - PebbleEvent e = { - .type = PEBBLE_TOUCH_EVENT, - .touch = { - .type = PebbleTouchEvent_TouchesAvailable, - .touch_idx = touch_idx - } - }; - event_put(&e); - } -} - -void touch_dispatch_touch_events(TouchIdx touch_idx, TouchEventHandler event_handler, - void *context) { - PBL_ASSERTN(event_handler); - PBL_ASSERTN(touch_idx < MAX_NUM_TOUCHES); - - TouchContext *ctx = &s_touch_ctx[touch_idx]; - mutex_lock(s_touch_mutex); - - if (ctx->update_cancelled) { - ctx->update_cancelled = false; - goto unlock; - } - if (!ctx->update_pending) { - goto unlock; - } - while (ctx->touch_queue.count > 0) { - // Copy touch event so that resetting the touch queue does not trash the event data - TouchEvent event = ctx->touch_queue.events[ctx->touch_queue.read_idx]; - - // Update the queue position before unlocking so if the state changes during the delivery - // callback, we don't overwrite the change - ctx->touch_queue.read_idx = (ctx->touch_queue.read_idx + 1) % - ARRAY_LENGTH(ctx->touch_queue.events); - ctx->touch_queue.count--; - - // unlock the mutex so that any calculations done in the callback do not block touch updates - // from the driver. - mutex_unlock(s_touch_mutex); - event_handler(&event, context); - mutex_lock(s_touch_mutex); - } - ctx->update_pending = false; - -unlock: - mutex_unlock(s_touch_mutex); -} - -void touch_handle_driver_event(TouchDriverEvent driver_event) { - PBL_ASSERTN(driver_event < TouchDriverEventCount); - mutex_lock(s_touch_mutex); - - for (uint32_t i = 0; i < ARRAY_LENGTH(s_touch_ctx); i++) { - TouchContext *ctx = &s_touch_ctx[i]; - prv_touch_context_reset(ctx); - - // If there is an event on the kernel queue, we need to set a flag to not dispatch touches in - // the queue when that event is called, because the TouchesCancelled event will arrive after - // the event on the queue. We do however want to be able to handle any touch events that happen - // afterwards, so we might have a touches available event (which would receive no touches), - // followed by a touches cancelled event, followed by another touches available event (which - // would receive touches). - if (ctx->update_pending) { - ctx->update_cancelled = true; - } - ctx->update_pending = false; - } - - mutex_unlock(s_touch_mutex); - - // Always send a touches cancelled event (all currently defined events cancel other touches) - PebbleEvent cancel_event = { - .type = PEBBLE_TOUCH_EVENT, - .touch = { - .type = PebbleTouchEvent_TouchesCancelled, - } - }; - event_put(&cancel_event); - - if (driver_event == TouchDriverEvent_PalmDetect) { - PebbleEvent palm_event = { - .type = PEBBLE_TOUCH_EVENT, - .touch = { - .type = PebbleTouchEvent_PalmDetected, - } - }; - event_put(&palm_event); - } -} - -void touch_reset(void) { - mutex_lock(s_touch_mutex); - - for (uint32_t i = 0; i < ARRAY_LENGTH(s_touch_ctx); i++) { - TouchContext *ctx = &s_touch_ctx[i]; - prv_touch_context_reset(ctx); - ctx->update_pending = false; - ctx->update_cancelled = false; - } - - mutex_unlock(s_touch_mutex); -} - -#if UNITTEST -TouchEvent *touch_event_queue_get_event(TouchIdx touch_idx, uint32_t queue_idx) { - if ((touch_idx > ARRAY_LENGTH(s_touch_ctx)) || - (queue_idx > ARRAY_LENGTH(s_touch_ctx[touch_idx].touch_queue.events)) || - (queue_idx >= s_touch_ctx[touch_idx].touch_queue.count)) { - return NULL; - } - queue_idx = (s_touch_ctx[touch_idx].touch_queue.read_idx + queue_idx) % - ARRAY_LENGTH(s_touch_ctx[touch_idx].touch_queue.events); - return &s_touch_ctx[touch_idx].touch_queue.events[queue_idx]; -} - -void touch_set_touch_state(TouchIdx touch_idx, TouchState touch_state, GPoint touch_down_pos, - uint64_t touch_down_time_ms, TouchPressure touch_down_pressure) { - if (touch_idx > ARRAY_LENGTH(s_touch_ctx)) { - return; - } - s_touch_ctx[touch_idx].start_pos = touch_down_pos; - s_touch_ctx[touch_idx].start_time_ms = touch_down_time_ms; - s_touch_ctx[touch_idx].start_pressure = touch_down_pressure; - s_touch_ctx[touch_idx].state = touch_state; -} -#endif diff --git a/src/fw/services/common/touch/touch.h b/src/fw/services/common/touch/touch.h deleted file mode 100644 index 98e4dc8529..0000000000 --- a/src/fw/services/common/touch/touch.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "touch_event.h" - -#include "applib/graphics/gtypes.h" - -#include -#include - -// TODO: PBL-29944 - move to board configuration -#define MAX_NUM_TOUCHES (2) - -typedef enum TouchState { - TouchState_FingerUp, - TouchState_FingerDown, -} TouchState; - -typedef enum TouchDriverEvent { - TouchDriverEvent_ControllerError, // an error occurred in the touch controller - TouchDriverEvent_PalmDetect, // a palm detection event occurred - - TouchDriverEventCount -} TouchDriverEvent; - -void touch_init(void); - -//! Pass a touch update to the service (called by the touch driver) -//! @param touch_idx zero-based index of concurrent touches (1st, 2nd concurrent touch etc.) -//! @param touch_state whether or not the screen is touched -//! @param pos position of touch -//! @param pressure pressure reading from touch -//! @param time_ms time (in milliseconds) that touch occurred. This value should be based on a -//! monotonically increasing clock (should not be affected by setting the system time). This service -//! assumes that touches will be passed in in the order that they occur. -void touch_handle_update(TouchIdx touch_idx, TouchState touch_state, const GPoint *pos, - TouchPressure pressure, uint64_t time_ms); - -//! Handle driver exceptional events, like palm detection, touch controller errors etc. -//! @param driver_event Driver event -void touch_handle_driver_event(TouchDriverEvent driver_event); - -//! Reset the touch service. Called when app context is switched to cancel context about current -//! touches -void touch_reset(void); diff --git a/src/fw/services/common/touch/touch_client.h b/src/fw/services/common/touch/touch_client.h deleted file mode 100644 index a3035c72bb..0000000000 --- a/src/fw/services/common/touch/touch_client.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "touch_event.h" - -#include -#include - -//! Touch event callback -//! @param event Touch event -//! @param context callback context -typedef void (*TouchEventHandler)(const TouchEvent *event, void *context); - -//! Dispatch touch events to specified handler -//! @param touch_idx index of touch for which to dispatch events -//! @param event_handler callback to dispatch touch events to -//! @param context callback context -void touch_dispatch_touch_events(TouchIdx touch_idx, TouchEventHandler event_handler, - void *context); diff --git a/src/fw/services/common/touch/touch_event.h b/src/fw/services/common/touch/touch_event.h deleted file mode 100644 index d817b5f10e..0000000000 --- a/src/fw/services/common/touch/touch_event.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" - -#include -#include - -typedef uint8_t TouchIdx; -typedef int16_t TouchPressure; - -//! Touch event type -typedef enum TouchEventType { - TouchEvent_Touchdown, - TouchEvent_Liftoff, - TouchEvent_PositionUpdate, - TouchEvent_PressureUpdate, -} TouchEventType; - -//! Touch event -typedef struct TouchEvent { - TouchEventType type; - TouchIdx index; - GPoint start_pos; - GPoint diff_pos; - int64_t start_time_ms; - int64_t diff_time_ms; - TouchPressure start_pressure; - TouchPressure diff_pressure; -} TouchEvent; diff --git a/src/fw/services/common/vibe_pattern.c b/src/fw/services/common/vibe_pattern.c deleted file mode 100644 index af015a9c93..0000000000 --- a/src/fw/services/common/vibe_pattern.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vibe_pattern.h" - -#include "drivers/vibe.h" -#include "drivers/battery.h" - -#include "util/list.h" -#include "util/math.h" - -#include "os/mutex.h" - -#include "services/common/accel_manager.h" -#include "services/common/new_timer/new_timer.h" -#include "kernel/events.h" - -#include "kernel/pbl_malloc.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" - -#include - -typedef struct { - ListNode list_node; - uint64_t time_start; - uint64_t time_end; -} VibeHistory; - -// The maximum history we need to keep is based on the maximum time between accel samples (the -// lowest sampling rate) in milliseconds and the maximum number of accel samples per update. -#define MAX_HISTORY_MS (ACCEL_MAX_SAMPLES_PER_UPDATE * 1000 / ACCEL_MINIMUM_SAMPLING_RATE) -#define END_NOT_SET 0 -#define HISTORY_CLEAR_ALL 0 - -static PebbleMutex *s_vibe_history_mutex = NULL; -static VibeHistory *s_vibe_history = NULL; -static bool s_vibe_history_enabled = false; -static bool s_vibe_service_enabled = true; - -DEFINE_SYSCALL(bool, sys_vibe_history_was_vibrating, uint64_t time_search) { - bool rc = false; - - PBL_ASSERTN(s_vibe_history_mutex); - mutex_lock(s_vibe_history_mutex); - VibeHistory *node = s_vibe_history; - while (node) { - if (node->time_end == END_NOT_SET && time_search >= node->time_start) { - rc = true; - break; - } - if (time_search >= node->time_start && time_search <= node->time_end) { - rc = true; - break; - } - node = (VibeHistory*)list_get_next((ListNode*)node); - } - mutex_unlock(s_vibe_history_mutex); - return rc; -} - -// @param cutoff The time to cut off the list at. 0 means to clear the list. -static void prv_vibe_history_clear(uint64_t cutoff) { - PBL_ASSERTN(s_vibe_history_mutex); - mutex_assert_held_by_curr_task(s_vibe_history_mutex, true); - while (s_vibe_history && s_vibe_history->time_end != END_NOT_SET) { - VibeHistory *vibe = s_vibe_history; - if (cutoff != HISTORY_CLEAR_ALL && vibe->time_end >= cutoff) { - break; - } - s_vibe_history = (VibeHistory*)list_get_next((ListNode*)vibe); - kernel_free(vibe); - } -} - -DEFINE_SYSCALL(void, sys_vibe_history_start_collecting, void) { - s_vibe_history_enabled = true; -} - -DEFINE_SYSCALL(void, sys_vibe_history_stop_collecting, void) { - s_vibe_history_enabled = false; - mutex_lock(s_vibe_history_mutex); - prv_vibe_history_clear(HISTORY_CLEAR_ALL); - mutex_unlock(s_vibe_history_mutex); -} - -static void prv_vibe_history_start_event(void) { - if (!s_vibe_history_enabled) { - return; - } - VibeHistory *vibe = kernel_malloc(sizeof(VibeHistory)); - if (vibe == NULL) { - s_vibe_history_enabled = false; - return; - } - list_init((ListNode*)vibe); - time_t s; - uint16_t ms; - rtc_get_time_ms(&s, &ms); - vibe->time_start = ((uint64_t)s) * 1000 + ms; - vibe->time_end = END_NOT_SET; - - PBL_ASSERTN(s_vibe_history_mutex); - mutex_lock(s_vibe_history_mutex); - if (s_vibe_history == NULL) { - s_vibe_history = vibe; - } else { - list_append((ListNode*)s_vibe_history, (ListNode*)vibe); - } - prv_vibe_history_clear(vibe->time_start - MAX_HISTORY_MS); - mutex_unlock(s_vibe_history_mutex); -} - -// Ends the last vibration event -static void prv_vibe_history_end_event(void) { - if (!s_vibe_history_enabled) { - return; - } - if (!s_vibe_history) { - // Possible that it was enabled while the watch was vibrating - return; - } - - time_t s; - uint16_t ms; - rtc_get_time_ms(&s, &ms); - - mutex_lock(s_vibe_history_mutex); - VibeHistory *vibe = (VibeHistory*)list_get_tail((ListNode*)s_vibe_history); - if (vibe->time_end == END_NOT_SET) { - vibe->time_end = ((uint64_t)s) * 1000 + ms; - } - mutex_unlock(s_vibe_history_mutex); -} - -typedef struct { - ListNode list_node; - uint32_t duration_ms; - int32_t strength; -} VibePatternStep; - -static const uint32_t MAX_VIBE_DURATION_MS = 10000; - -static int s_pattern_timer = TIMER_INVALID_ID; -static bool s_pattern_in_progress = false; -// s_vibe_strength is the current vibration strength setting of the motor -static int32_t s_vibe_strength = VIBE_STRENGTH_OFF; -// s_vibe_strength_default is the vibrations trength of the motor used when one is not specified -// explicitly, and can be changed in the notification vibration strength setting. -static int32_t s_vibe_strength_default = VIBE_STRENGTH_MAX; - -static PebbleMutex *s_vibe_pattern_mutex = NULL; -static VibePatternStep *s_vibe_queue_head = NULL; - -void vibes_init() { - s_vibe_history_mutex = mutex_create(); - s_vibe_pattern_mutex = mutex_create(); - s_pattern_in_progress = false; - s_pattern_timer = new_timer_create(); -} - -//! Turn the vibe motor on or off. -//! -//! This function should be used instead of vibe_ctl so that the vibe -//! history is kept in sync with the vibe state. -//! The caller must be holding s_vibe_pattern_mutex -static void prv_vibes_set_vibe_strength(int32_t new_strength) { - mutex_assert_held_by_curr_task(s_vibe_pattern_mutex, true); - if (!s_vibe_service_enabled) { - PBL_ASSERTN(s_vibe_strength == VIBE_STRENGTH_OFF); - return; - } - if (new_strength != VIBE_STRENGTH_OFF) { - vibe_set_strength(new_strength); - vibe_ctl(true /* on */); - if (s_vibe_strength == VIBE_STRENGTH_OFF) { - prv_vibe_history_start_event(); - } - } else { - vibe_ctl(false /* on */); - if (s_vibe_strength != VIBE_STRENGTH_OFF) { - prv_vibe_history_end_event(); - } - } - s_vibe_strength = new_strength; -} - -void vibe_service_set_enabled(bool enable) { - mutex_lock(s_vibe_pattern_mutex); - if (enable != s_vibe_service_enabled) { - // ensure that the vibe is off before disabling it. No op if enabling it - prv_vibes_set_vibe_strength(VIBE_STRENGTH_OFF); - s_vibe_service_enabled = enable; - } - mutex_unlock(s_vibe_pattern_mutex); -} - -static void prv_timer_callback(void* data) { - if (s_vibe_queue_head == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Tried to handle a vibe event with a null vibe queue"); - return; - } - - mutex_lock(s_vibe_pattern_mutex); - - // remove the event I've finished - VibePatternStep *removed_node = s_vibe_queue_head; - s_vibe_queue_head = (VibePatternStep*)list_pop_head((ListNode*)s_vibe_queue_head); - kernel_free(removed_node); - - if (s_vibe_queue_head != NULL) { - // move to the next step - prv_vibes_set_vibe_strength(s_vibe_queue_head->strength); - bool success = new_timer_start(s_pattern_timer, s_vibe_queue_head->duration_ms, - prv_timer_callback, NULL, 0 /*flags*/); - PBL_ASSERTN(success); - } else { - // I'm done with the active pattern - // make sure it's off - prv_vibes_set_vibe_strength(VIBE_STRENGTH_OFF); - s_pattern_in_progress = false; - } - - mutex_unlock(s_vibe_pattern_mutex); -} - -int32_t vibes_get_vibe_strength(void) { - return s_vibe_strength; -} - -int32_t vibes_get_default_vibe_strength(void) { - return s_vibe_strength_default; -} - -void vibes_set_default_vibe_strength(int32_t vibe_strength_default) { - s_vibe_strength_default = vibe_strength_default; -} - -DEFINE_SYSCALL(int32_t, sys_vibe_get_vibe_strength, void) { - return vibes_get_vibe_strength(); -} - -bool prv_vibe_pattern_enqueue_step_raw(uint32_t duration_ms, int32_t strength) { - mutex_lock(s_vibe_pattern_mutex); - - if (s_pattern_in_progress) { - PBL_LOG(LOG_LEVEL_DEBUG, "Pattern is in progress"); - mutex_unlock(s_vibe_pattern_mutex); - return false; - } - - VibePatternStep *step = kernel_malloc(sizeof(VibePatternStep)); - if (step == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Couldn't malloc for a vibe step"); - mutex_unlock(s_vibe_pattern_mutex); - return false; - } - - list_init((ListNode*)step); - step->duration_ms = MIN(duration_ms, MAX_VIBE_DURATION_MS); - step->strength = strength; - - if (s_vibe_queue_head == NULL) { - s_vibe_queue_head = step; - } else { - list_append((ListNode*)s_vibe_queue_head, (ListNode*)step); - } - - mutex_unlock(s_vibe_pattern_mutex); - - return true; -} - -DEFINE_SYSCALL(bool, sys_vibe_pattern_enqueue_step_raw, uint32_t duration_ms, int32_t strength) { - return prv_vibe_pattern_enqueue_step_raw(duration_ms, strength); -} - -DEFINE_SYSCALL(bool, sys_vibe_pattern_enqueue_step, uint32_t duration_ms, bool on) { - return prv_vibe_pattern_enqueue_step_raw(duration_ms, on ? s_vibe_strength_default - : VIBE_STRENGTH_OFF); -} - -DEFINE_SYSCALL(void, sys_vibe_pattern_trigger_start, void) { - mutex_lock(s_vibe_pattern_mutex); - if (s_vibe_queue_head == NULL || s_pattern_in_progress) { - // either no vibes queued or I've already started - mutex_unlock(s_vibe_pattern_mutex); - return; - } - - if (pebble_task_get_current() == PebbleTask_App) { - analytics_inc(ANALYTICS_APP_METRIC_VIBRATOR_ON_COUNT, AnalyticsClient_App); - } - - prv_vibes_set_vibe_strength(s_vibe_queue_head->strength); - s_pattern_in_progress = true; - bool success = new_timer_start(s_pattern_timer, s_vibe_queue_head->duration_ms, - prv_timer_callback, NULL, 0 /*flags*/); - PBL_ASSERTN(success); - mutex_unlock(s_vibe_pattern_mutex); -} - -DEFINE_SYSCALL(void, sys_vibe_pattern_clear, void) { - mutex_lock(s_vibe_pattern_mutex); - new_timer_stop(s_pattern_timer); - while (s_vibe_queue_head) { - VibePatternStep *removed_node = s_vibe_queue_head; - s_vibe_queue_head = (VibePatternStep*)list_pop_head((ListNode*)s_vibe_queue_head); - kernel_free(removed_node); - } - prv_vibes_set_vibe_strength(VIBE_STRENGTH_OFF); - s_pattern_in_progress = false; - mutex_unlock(s_vibe_pattern_mutex); -} diff --git a/src/fw/services/common/vibe_pattern.h b/src/fw/services/common/vibe_pattern.h deleted file mode 100644 index 4b3636329e..0000000000 --- a/src/fw/services/common/vibe_pattern.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -void vibes_init(); - -int32_t vibes_get_vibe_strength(void); -int32_t vibes_get_default_vibe_strength(void); -void vibes_set_default_vibe_strength(int32_t vibe_strength_default); - -void vibe_service_set_enabled(bool enable); diff --git a/src/fw/services/compositor/Kconfig b/src/fw/services/compositor/Kconfig new file mode 100644 index 0000000000..0cdb0cbfd1 --- /dev/null +++ b/src/fw/services/compositor/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_COMPOSITOR + bool "Compositor" + help + UI compositor and window transitions. + +if SERVICE_COMPOSITOR + +module = SERVICE_COMPOSITOR +module-str = Compositor +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/compositor/compositor.c b/src/fw/services/compositor/compositor.c new file mode 100644 index 0000000000..a0e56257bc --- /dev/null +++ b/src/fw/services/compositor/compositor.c @@ -0,0 +1,797 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/compositor/compositor_display.h" + +#include "applib/graphics/bitblt.h" +#include "applib/graphics/framebuffer.h" +#include "applib/graphics/gcontext.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/animation.h" +#include "applib/ui/animation_private.h" +#include "drivers/display/display.h" +#include "kernel/event_loop.h" +#include "kernel/kernel_applib_state.h" +#include "kernel/ui/kernel_ui.h" +#include "kernel/ui/modals/modal_manager.h" +#include "mcu/cache.h" +#include "popups/timeline/peek.h" +#include "process_management/app_manager.h" +#include "process_management/process_manager.h" +#include "process_state/app_state/app_state.h" +#include "shell/prefs.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/profiler.h" +#include "util/size.h" +#include "util/math.h" + +PBL_LOG_MODULE_DEFINE(service_compositor, CONFIG_SERVICE_COMPOSITOR_LOG_LEVEL); + +// The number of pixels for a given row which get set to black to round the corner. These numbers +// are for the top-left corner, but can easily be translated to the other corners. This is used by + +//! This is our root framebuffer that everything gets composited into. +static FrameBuffer s_framebuffer; + +typedef enum { + //! Render the app with no transparent modals straight through + CompositorState_App, + //! Render the opaque modal straight through + CompositorState_Modal, + //! Render the app with transparent modals straight through + CompositorState_AppAndModal, + //!< Waiting for the app to render itself so we can start the transition + CompositorState_AppTransitionPending, + //!< Compositor is running a transition animation + CompositorState_Transitioning, +} CompositorState; + +//! Deferred render struct is used to handle a render event initiated while a display update is in +//! progress and the update is non-blocking on the platform (ie. snowy/bobby smiles). +typedef struct { + struct { + bool pending; + AnimationProgress progress; + } animation; + struct { + bool pending; + } transition_complete; + struct { + bool pending; + } app; + struct { + bool pending; + const CompositorTransition *compositor_animation; + } transition_start; +} DeferredRender; + +typedef struct { + Animation *animation; + const CompositorTransition *impl; + GPoint modal_offset; +} CompositorTransitionState; + +static CompositorState s_state; + +static DeferredRender s_deferred_render; + +static CompositorTransitionState s_animation_state; + +static bool s_framebuffer_frozen; + +//! Animation .update function for the AnimationImplementation we use to drive our transitions. +//! Wraps the .update function of the current CompositorTransition. +static void prv_animation_update(Animation *animation, const AnimationProgress distance_normalized); + +//! Call this function whenever a transition completes to change the state to one of the stable +//! states (CompositorState_App or CompositorState_Modal). +static void prv_finish_transition(void); + +void compositor_init(void) { + const GSize fb_size = GSize(DISP_COLS, DISP_ROWS); + framebuffer_init(&s_framebuffer, &fb_size); + framebuffer_clear(&s_framebuffer); + + s_state = CompositorState_App; + + s_deferred_render = (DeferredRender) { + .animation.pending = false, + .app.pending = false + }; + + s_animation_state = (CompositorTransitionState) { 0 }; + + s_framebuffer_frozen = false; +} + +// Helper functions to make implementing transitions easier +/////////////////////////////////////////////////////////// + +void compositor_app_framebuffer_fill_callback(GContext *ctx, int16_t y, + Fixed_S16_3 x_range_begin, Fixed_S16_3 x_range_end, + Fixed_S16_3 delta_begin, Fixed_S16_3 delta_end, + void *user_data) { + const GPoint *offset = user_data ?: &GPointZero; // User data has left the building + GBitmap app_framebuffer = compositor_get_app_framebuffer_as_bitmap(); + const int16_t fb_width = app_framebuffer.bounds.size.w; + const int16_t fb_height = app_framebuffer.bounds.size.h; + + const int16_t x1 = CLIP(x_range_begin.integer - offset->x, 0, fb_width); + const int16_t clipped_y = CLIP(y - offset->y, 0, fb_height); + const int16_t x2 = CLIP(x_range_end.integer - offset->x, 0, fb_width); + + compositor_scaled_app_fb_copy( + GRect(x1, clipped_y, x2 - x1, 1), + true /* copy_relative_to_origin */ + ); +} + + +void compositor_set_modal_transition_offset(GPoint modal_offset) { + s_animation_state.modal_offset = modal_offset; +} + +void compositor_render_app(void) { + PBL_ASSERT_TASK(PebbleTask_KernelMain); + + PROFILER_NODE_START(compositor); + + // Don't trust the size field within the app framebuffer as the app could modify it. + GSize app_framebuffer_size; + app_manager_get_framebuffer_size(&app_framebuffer_size); + + + // Fill entire framebuffer with black first to avoid artifacts + GBitmap dest_bitmap = compositor_get_framebuffer_as_bitmap(); + memset(dest_bitmap.addr, GColorBlack.argb, framebuffer_get_size_bytes(&s_framebuffer)); + + compositor_scaled_app_fb_copy(GRect(0, 0, DISP_COLS, DISP_ROWS), false /* copy_relative_to_origin */); + + if (s_state == CompositorState_AppAndModal) { + compositor_render_modal(); + } + + PROFILER_NODE_STOP(compositor); + + framebuffer_dirty_all(&s_framebuffer); +} + +void compositor_render_modal(void) { + GContext *ctx = kernel_ui_get_graphics_context(); + + // We make this GDrawState static to save stack space, thus the declaration and init must be + // performed on two separate lines because the initializer value is not constant + static GDrawState prev_state; + prev_state = ctx->draw_state; + + gpoint_add_eq(&ctx->draw_state.drawing_box.origin, s_animation_state.modal_offset); + + modal_manager_render(ctx); + + ctx->draw_state = prev_state; +} + +// Compositor implementation +/////////////////////////////////////////////////////////// + +T_STATIC void prv_handle_display_update_complete(void) { + if (s_deferred_render.transition_complete.pending) { + s_deferred_render.transition_complete.pending = false; + prv_finish_transition(); + } + if (s_deferred_render.animation.pending) { + s_deferred_render.animation.pending = false; + prv_animation_update(s_animation_state.animation, s_deferred_render.animation.progress); + } + // Process transition_start before app so that the compositor state is set to + // AppTransitionPending before compositor_app_render_ready() is called. Otherwise, the app + // framebuffer may be rendered directly to the display before the transition animation starts. + if (s_deferred_render.transition_start.pending) { + s_deferred_render.transition_start.pending = false; + compositor_transition(s_deferred_render.transition_start.compositor_animation); + } + if (s_deferred_render.app.pending) { + s_deferred_render.app.pending = false; + compositor_app_render_ready(); + } +} + +static void prv_compositor_flush(void) { + PBL_ASSERT_TASK(PebbleTask_KernelMain); + + // Stop the framebuffer_prepare performance timer. This timer was started when the client + // first posted the render event to the system. + compositor_display_update(prv_handle_display_update_complete); +} + +static void prv_send_did_focus_event(bool in_focus) { + PebbleEvent event = { + .type = PEBBLE_APP_DID_CHANGE_FOCUS_EVENT, + .app_focus = { + .in_focus = in_focus, + }, + }; + event_put(&event); +} + +static bool prv_should_render(void) { + return !(compositor_display_update_in_progress() || s_framebuffer_frozen); +} + +static void prv_release_app_framebuffer(void) { + // Inform the app that the render is complete and it is safe to write into its framebuffer again. + PebbleEvent event = { + .type = PEBBLE_RENDER_FINISHED_EVENT, + }; + process_manager_send_event_to_process(PebbleTask_App, &event); +} + +void compositor_app_render_ready(void) { + if (!prv_should_render()) { + s_deferred_render.app.pending = true; + return; + } + + if (s_state == CompositorState_AppTransitionPending) { + // Huzzah, the app sent us the first frame! + if (s_animation_state.animation) { + // We have an animation to run, run it. + s_state = CompositorState_Transitioning; + animation_schedule(s_animation_state.animation); + + // Don't release the app framebuffer yet, we'll do this once the transition completes. This + // way the app won't update its frame buffer while we're transitioning to it. + return; + } else { + // No animation was used, immediately say that the app is now fully focused. + const ModalProperty properties = modal_manager_get_properties(); + s_state = ((properties & ModalProperty_Exists) && (properties & ModalProperty_Transparent)) ? + CompositorState_AppAndModal : CompositorState_App; + prv_send_did_focus_event(true); + } + } + + // Draw the app framebuffer if in the App state + if (s_state == CompositorState_App || s_state == CompositorState_AppAndModal) { + // compositor_render_app also renders modals if the CompositorState_AppAndModal as that state + // indicates that there are transparent modals that allow the app framebuffer to show through + compositor_render_app(); + prv_compositor_flush(); + } + + // Draw the modal if in the Modal state + if (s_state == CompositorState_Modal) { + compositor_render_modal(); + prv_compositor_flush(); + } + + prv_release_app_framebuffer(); +} + +static void prv_send_app_render_request(void) { + PebbleEvent event = { + .type = PEBBLE_RENDER_REQUEST_EVENT, + }; + process_manager_send_event_to_process(PebbleTask_App, &event); +} + +void compositor_modal_render_ready(void) { + if ((s_state == CompositorState_Transitioning) || !prv_should_render()) { + // Don't let the modal redraw itself when the redraw loop is being currently driven by an + // animation or if a display update is in progress. + return; + } + + if ((s_state == CompositorState_AppTransitionPending) && + (modal_manager_get_properties() & ModalProperty_Transparent)) { + // Don't render if modals are transparent while the app is not ready yet + return; + } + + if (s_state == CompositorState_Modal) { + compositor_render_modal(); + prv_compositor_flush(); + } else if (s_state == CompositorState_AppAndModal) { + prv_send_app_render_request(); + } +} + +void compositor_transition_render(CompositorTransitionUpdateFunc func, Animation *animation, + const AnimationProgress distance_normalized) { + if (!prv_should_render()) { + if (!s_deferred_render.transition_complete.pending) { + s_deferred_render.animation.pending = true; + s_deferred_render.animation.progress = distance_normalized; + } + return; + } + GContext *ctx = kernel_ui_get_graphics_context(); + + // Save the draw state in a static to save stack space + static GDrawState prev_state; + prev_state = ctx->draw_state; + + func(ctx, animation, distance_normalized); + + ctx->draw_state = prev_state; + + if (!s_animation_state.impl->skip_modal_render_after_update) { + compositor_render_modal(); + } + + prv_compositor_flush(); +} + +static void prv_animation_update(Animation *animation, + const AnimationProgress distance_normalized) { + PBL_ASSERT_TASK(PebbleTask_KernelMain); + // Since we might be running this animation update as part of a deferred render, we must + // update the kernel animation state's .current_animation to point to this animation; + // otherwise if the animation specified any custom spacial interpolation (e.g. moook), it would + // be ignored + AnimationPrivate *animation_private = animation_private_animation_find(animation); + AnimationState *kernel_animation_state = kernel_applib_get_animation_state(); + PBL_ASSERTN(animation_private && kernel_animation_state && kernel_animation_state->aux); + AnimationPrivate *saved_current_animation = kernel_animation_state->aux->current_animation; + + kernel_animation_state->aux->current_animation = animation_private; + compositor_transition_render(s_animation_state.impl->update, animation, distance_normalized); + kernel_animation_state->aux->current_animation = saved_current_animation; +} + +static void prv_finish_transition(void) { + const ModalProperty properties = modal_manager_get_properties(); + if (properties & ModalProperty_Exists) { + s_state = (properties & ModalProperty_Transparent) ? CompositorState_AppAndModal : + CompositorState_Modal; + compositor_modal_render_ready(); + + // Force the app framebuffer to be released. We hold it during transitions to keep the app + // framebuffer from changing while it's being animated but now that we're done we want to make + // sure it's always available to the app. This is only needed when we're finishing to a modal + // since compositor_app_render_ready will also release the framebuffer. + prv_release_app_framebuffer(); + } else { + s_state = CompositorState_App; + compositor_app_render_ready(); + } + + prv_send_did_focus_event(properties & ModalProperty_Unfocused); +} + +static void prv_animation_teardown(Animation *animation) { + if (s_animation_state.impl->teardown) { + s_animation_state.impl->teardown(animation); + } + s_animation_state = (CompositorTransitionState) { 0 }; + + s_deferred_render.animation.pending = false; + if (!prv_should_render()) { + s_deferred_render.transition_complete.pending = true; + return; + } + + prv_finish_transition(); +} + +void compositor_transition(const CompositorTransition *compositor_animation) { + if (s_animation_state.animation != NULL) { + PBL_LOG_DBG("Animation <%u> in progress, cancelling", + (int) s_animation_state.animation); + + animation_destroy(s_animation_state.animation); + s_animation_state = (CompositorTransitionState) { 0 }; + + s_deferred_render.animation.pending = false; + s_deferred_render.transition_complete.pending = false; + } + + if (!prv_should_render() || s_deferred_render.animation.pending) { + if (s_deferred_render.app.pending) { + s_deferred_render.app.pending = false; + prv_release_app_framebuffer(); + } + + s_deferred_render.transition_start.pending = true; + s_deferred_render.transition_start.compositor_animation = compositor_animation; + return; + } + + if (compositor_animation) { + // Set up our animation state and schedule it + + s_animation_state = (CompositorTransitionState) { + .animation = animation_create(), + .impl = compositor_animation + }; + + static const AnimationImplementation s_compositor_animation_impl = { + .update = prv_animation_update, + .teardown = prv_animation_teardown, + }; + animation_set_implementation(s_animation_state.animation, &s_compositor_animation_impl); + + compositor_animation->init(s_animation_state.animation); + } + + const ModalProperty properties = modal_manager_get_properties(); + const bool is_modal_existing = (properties & ModalProperty_Exists); + const bool is_modal_transparent = (properties & ModalProperty_Transparent); + if (((s_state == CompositorState_Modal) && !is_modal_existing) || is_modal_transparent) { + // Modal to App or Any to Transparent Modal + + // We can't say for sure whether or not the app framebuffer is in a reasonable state, as the + // app could be redrawing itself right now. Since we can't query this, instead trigger the + // app to redraw itself. This way we will cause an PEBBLE_RENDER_READY_EVENT in the very near + // future, regardless of the app's state. + prv_send_app_render_request(); + + // Now wait for the ready event. + s_state = CompositorState_AppTransitionPending; + + } else if (is_modal_existing && !is_modal_transparent) { + // Modal to Modal or App to Modal + + // We can start animating immediately if we're going to a modal window. This is because + // modal window content is drawn on demand so it's always available. + if (compositor_animation) { + s_state = CompositorState_Transitioning; + animation_schedule(s_animation_state.animation); + } else { + prv_finish_transition(); + } + + } else { + // App to App (also handles Transitioning->App when the previous animation was cancelled) + + // We can't say for sure whether or not the app framebuffer is in a reasonable state, as the + // app could be redrawing itself right now. Since we can't query this, instead trigger the + // app to redraw itself. This way we will cause an PEBBLE_RENDER_READY_EVENT in the very near + // future, regardless of the app's state. + prv_send_app_render_request(); + + // Now wait for the ready event. + s_state = CompositorState_AppTransitionPending; + } +} + +FrameBuffer *compositor_get_framebuffer(void) { + return &s_framebuffer; +} + +GBitmap compositor_get_framebuffer_as_bitmap(void) { + return framebuffer_get_as_bitmap(&s_framebuffer, &s_framebuffer.size); +} + +GBitmap compositor_get_app_framebuffer_as_bitmap(void) { + // Get the app framebuffer state based on the size it should be to prevent a malicious app from + // changing it and causing issues. + GSize app_framebuffer_size; + app_manager_get_framebuffer_size(&app_framebuffer_size); + return framebuffer_get_as_bitmap(app_state_get_framebuffer(), &app_framebuffer_size); +} + +bool compositor_is_animating(void) { + return s_state == CompositorState_AppTransitionPending || + s_state == CompositorState_Transitioning; +} + +void compositor_transition_cancel(void) { + if (animation_is_scheduled(s_animation_state.animation)) { + animation_unschedule(s_animation_state.animation); + } +} + +void compositor_freeze(void) { + s_framebuffer_frozen = true; +} + +static void prv_compositor_unfreeze_cb(void *ignored) { + // Run deferred draws + prv_handle_display_update_complete(); +} + +void compositor_unfreeze(void) { + s_framebuffer_frozen = false; + + launcher_task_add_callback(prv_compositor_unfreeze_cb, NULL); +} + +static bool prv_app_framebuffer_matches_display(void) { + GSize app_framebuffer_size; + app_manager_get_framebuffer_size(&app_framebuffer_size); + return gsize_equal(&app_framebuffer_size, &s_framebuffer.size); +} + +uint16_t prv_scale_coordinate(const uint32_t scale_factor, uint16_t val) { + const uint32_t val_fixed = (uint32_t)val * scale_factor; + return val_fixed >> 16; // Get integer part +} + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED && !defined(CONFIG_RECOVERY_FW) +static TimelinePeekUnsupportedFaceMode prv_get_unsupported_face_mode_for_timeline_peek(void) { + const TimelinePeekUnsupportedFaceMode mode = + timeline_peek_prefs_get_unsupported_face_mode(); + const PebbleProcessMd *app_md = app_manager_get_current_app_md(); + if (mode == TimelinePeekUnsupportedFaceMode_None || !app_md || + app_md->process_type != ProcessTypeWatchface) { + return TimelinePeekUnsupportedFaceMode_None; + } + + UnobstructedAreaState *unobstructed_state = app_state_get_unobstructed_area_state(); + if (unobstructed_area_service_is_subscribed(unobstructed_state) || + unobstructed_area_service_has_requested_area(unobstructed_state)) { + return TimelinePeekUnsupportedFaceMode_None; + } + + const int16_t obstruction_y = timeline_peek_get_origin_y(); + return ((obstruction_y > 0) && (obstruction_y < DISP_ROWS)) ? + mode : TimelinePeekUnsupportedFaceMode_None; +} +#endif + +void compositor_scaled_app_fb_copy(const GRect update_rect, bool copy_relative_to_origin) { + compositor_scaled_app_fb_copy_offset(update_rect, copy_relative_to_origin, 0 /* offset_y */); +} + +void compositor_scaled_app_fb_copy_offset(const GRect update_rect, bool copy_relative_to_origin, + int16_t offset_y) { + GBitmap src_bitmap = compositor_get_app_framebuffer_as_bitmap(); + GBitmap dst_bitmap = compositor_get_framebuffer_as_bitmap(); + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED && !defined(CONFIG_RECOVERY_FW) + const TimelinePeekUnsupportedFaceMode unsupported_face_mode = + prv_get_unsupported_face_mode_for_timeline_peek(); + const bool squish_watchface_for_peek = + (unsupported_face_mode == TimelinePeekUnsupportedFaceMode_SquishUp); + const bool shift_watchface_for_peek = + (unsupported_face_mode == TimelinePeekUnsupportedFaceMode_ShiftUp); +#else + const bool squish_watchface_for_peek = false; + const bool shift_watchface_for_peek = false; +#endif + + if (prv_app_framebuffer_matches_display() && !squish_watchface_for_peek && + !shift_watchface_for_peek) { + GBitmap sub_bitmap; + gbitmap_init_as_sub_bitmap(&sub_bitmap, &src_bitmap, update_rect); + bitblt_bitmap_into_bitmap(&dst_bitmap, &sub_bitmap, update_rect.origin, GCompOpAssign, + GColorWhite); + return; + } + +#if PBL_COLOR + const int16_t app_width = src_bitmap.bounds.size.w; + const int16_t app_height = src_bitmap.bounds.size.h; + +#if defined(CONFIG_APP_SCALING) && !defined(CONFIG_RECOVERY_FW) + const int16_t disp_width = dst_bitmap.bounds.size.w; + const int16_t disp_height = dst_bitmap.bounds.size.h; + // Check if we should use scaling mode for legacy apps + const LegacyAppRenderMode render_mode = shell_prefs_get_legacy_app_render_mode(); + const bool should_scale_app = + (render_mode >= LegacyAppRenderMode_ScalingNearest) || squish_watchface_for_peek || + (shift_watchface_for_peek && prv_app_framebuffer_matches_display()); + if (should_scale_app) { + const bool bilinear = (render_mode == LegacyAppRenderMode_ScalingBilinear); + const bool shift_scaled_watchface_for_peek = + shift_watchface_for_peek && !squish_watchface_for_peek; + const GRect scale_to = squish_watchface_for_peek + ? GRect(0, 0, disp_width, timeline_peek_get_origin_y()) + : GRect(0, 0, disp_width, disp_height); + + if (shift_scaled_watchface_for_peek) { + int16_t first_row = CLIP(update_rect.origin.y, 0, DISP_ROWS - 1); + int16_t last_row = CLIP(update_rect.origin.y + update_rect.size.h, first_row, DISP_ROWS); + for (int16_t y = first_row; y < last_row; y++) { + GBitmapDataRowInfo dst_row_info = gbitmap_get_data_row_info(&dst_bitmap, y); + const int16_t start_x = MAX(update_rect.origin.x, dst_row_info.min_x); + const int16_t end_x = MIN(update_rect.origin.x + update_rect.size.w, + dst_row_info.max_x + 1); + memset(&dst_row_info.data[start_x], GColorBlack.argb, end_x - start_x); + } + } + + // Calculate scaling factors using fixed-point arithmetic (16.16 format) + // This gives us sub-pixel precision for better scaling + const uint32_t scale_x = ((uint32_t)app_width << 16) / scale_to.size.w; + const uint32_t scale_y = ((uint32_t)app_height << 16) / scale_to.size.h; + const int16_t app_shift_y = timeline_peek_get_origin_y() - scale_to.size.h; + + for (int16_t dst_y = 0; dst_y < update_rect.size.h; dst_y++) { + const int16_t dst_y_offset = dst_y + update_rect.origin.y + offset_y; + if (dst_y_offset < 0 || dst_y_offset >= disp_height) continue; + if ((squish_watchface_for_peek && + (dst_y_offset < scale_to.origin.y || + dst_y_offset >= scale_to.origin.y + scale_to.size.h)) || + (shift_scaled_watchface_for_peek && + (dst_y_offset >= timeline_peek_get_origin_y()))) { + continue; + } + + uint16_t dst_y_coord; + if (squish_watchface_for_peek) { + dst_y_coord = dst_y_offset - scale_to.origin.y; + } else if (shift_scaled_watchface_for_peek) { + const int16_t shifted_y = dst_y_offset - app_shift_y; + if (shifted_y < 0 || shifted_y >= scale_to.size.h) { + continue; + } + dst_y_coord = shifted_y; + } else { + dst_y_coord = copy_relative_to_origin ? CLIP(dst_y_offset, 0, disp_height - 1) : dst_y; + } + const uint32_t src_y_fixed = (uint32_t)dst_y_coord * scale_y; + const int16_t src_y = src_y_fixed >> 16; + + if (src_y < 0 || src_y >= app_height) continue; + + GBitmapDataRowInfo dst_row_info = gbitmap_get_data_row_info(&dst_bitmap, dst_y_offset); + GBitmapDataRowInfo src_row_info = gbitmap_get_data_row_info(&src_bitmap, src_y); + + // For bilinear, also get the next row (clamped to bounds) + const int16_t src_y1 = MIN(src_y + 1, app_height - 1); + GBitmapDataRowInfo src_row_info_next; + if (bilinear && src_y1 != src_y) { + src_row_info_next = gbitmap_get_data_row_info(&src_bitmap, src_y1); + } else { + src_row_info_next = src_row_info; + } + + // Fractional Y weight for bilinear (0-16 range, using 4 bits from fixed-point) + const uint8_t fy = (src_y_fixed >> 12) & 0xF; + + uint8_t *dst_line = dst_row_info.data; + + for (int16_t dst_x = 0; dst_x < update_rect.size.w; dst_x++) { + const int16_t dst_x_offset = dst_x + update_rect.origin.x; + if (dst_x_offset < dst_row_info.min_x || dst_x_offset > dst_row_info.max_x) { + continue; + } + if (squish_watchface_for_peek && + (dst_x_offset < scale_to.origin.x || + dst_x_offset >= scale_to.origin.x + scale_to.size.w)) { + continue; + } + + const uint16_t dst_x_coord = squish_watchface_for_peek + ? (dst_x_offset - scale_to.origin.x) + : copy_relative_to_origin ? CLIP(dst_x_offset, 0, disp_width - 1) : dst_x; + const uint32_t src_x_fixed = (uint32_t)dst_x_coord * scale_x; + const int16_t src_x = src_x_fixed >> 16; + + if (src_x < src_row_info.min_x || src_x > src_row_info.max_x) { + continue; + } + + if (!bilinear) { + // Nearest-neighbor: copy pixel directly + dst_line[dst_x_offset] = src_row_info.data[src_x]; + } else { + // Bilinear interpolation on ARGB2222 pixels + const int16_t src_x1 = MIN(src_x + 1, app_width - 1); + + // Sample 2x2 neighborhood + const uint8_t p00 = src_row_info.data[src_x]; + const uint8_t p10 = (src_x1 <= src_row_info.max_x) ? + src_row_info.data[src_x1] : p00; + const uint8_t p01 = (src_y1 != src_y && src_x >= src_row_info_next.min_x && + src_x <= src_row_info_next.max_x) ? + src_row_info_next.data[src_x] : p00; + const uint8_t p11 = (src_y1 != src_y && src_x1 >= src_row_info_next.min_x && + src_x1 <= src_row_info_next.max_x) ? + src_row_info_next.data[src_x1] : p10; + + // Fractional X weight (0-16 range, using 4 bits from fixed-point) + const uint8_t fx = (src_x_fixed >> 12) & 0xF; + + // Interpolate each 2-bit channel (b, g, r) using 16-bit intermediates + // Channel layout: [b1:b0 g1:g0 r1:r0 a1:a0] + // Using 4-bit fractional weights so we can divide by shift (16*16=256) + uint8_t result = 0xC0; // Alpha = 3 (fully opaque) + for (int shift = 0; shift < 6; shift += 2) { + const uint16_t c00 = (p00 >> shift) & 0x3; + const uint16_t c10 = (p10 >> shift) & 0x3; + const uint16_t c01 = (p01 >> shift) & 0x3; + const uint16_t c11 = (p11 >> shift) & 0x3; + + // Bilinear: lerp in X for both rows, then lerp in Y + const uint16_t top = c00 * (16 - fx) + c10 * fx; // 0..48 + const uint16_t bot = c01 * (16 - fx) + c11 * fx; // 0..48 + const uint16_t val = top * (16 - fy) + bot * fy; // 0..768 + + // Scale back to 2-bit: divide by 256 with rounding + const uint8_t channel = (val + 128) >> 8; + result |= (channel & 0x3) << shift; + } + + dst_line[dst_x_offset] = result; + } + } + } + } else if (shift_watchface_for_peek) { + const int16_t app_offset_x = (disp_width - app_width) / 2; + const int16_t app_offset_y = timeline_peek_get_origin_y() - app_height; + const GRect shifted_region = GRect(app_offset_x, app_offset_y, app_width, app_height); + const GRect unobstructed_region = GRect(0, 0, disp_width, timeline_peek_get_origin_y()); + + int16_t first_row = CLIP(update_rect.origin.y, 0, DISP_ROWS - 1); + int16_t last_row = CLIP(update_rect.origin.y + update_rect.size.h, first_row, DISP_ROWS); + for (int16_t y = first_row; y < last_row; y++) { + GBitmapDataRowInfo dst_row_info = gbitmap_get_data_row_info(&dst_bitmap, y); + const int16_t start_x = MAX(update_rect.origin.x, dst_row_info.min_x); + const int16_t end_x = MIN(update_rect.origin.x + update_rect.size.w, dst_row_info.max_x + 1); + memset(&dst_row_info.data[start_x], GColorBlack.argb, end_x - start_x); + } + + GRect clipped_update_region = update_rect; + grect_clip(&clipped_update_region, &unobstructed_region); + grect_clip(&clipped_update_region, &shifted_region); + if (clipped_update_region.size.w > 0 && clipped_update_region.size.h > 0) { + GBitmap sub_bitmap; + const GRect src_rect = GRect( + clipped_update_region.origin.x - app_offset_x, + clipped_update_region.origin.y - app_offset_y + offset_y, + clipped_update_region.size.w, + clipped_update_region.size.h + ); + gbitmap_init_as_sub_bitmap(&sub_bitmap, &src_bitmap, src_rect); + bitblt_bitmap_into_bitmap(&dst_bitmap, &sub_bitmap, clipped_update_region.origin, + GCompOpAssign, GColorWhite); + } + } else +#endif + { + // Original bezel mode - center with black bezel + const int16_t bezel_width = (DISP_COLS - app_width) / 2; + const int16_t bezel_height = (DISP_ROWS - app_height) / 2; + const int16_t app_peek_offset_y = timeline_peek_get_origin_y() - app_height; + const int16_t app_offset_y = CLIP(app_peek_offset_y, 0, bezel_height); + PBL_ASSERTN((bezel_width > 0) && (bezel_height > 0)); + + // memset the entire region to be updated to black + int16_t first_row = CLIP(update_rect.origin.y, 0, DISP_ROWS - 1); + int16_t last_row = CLIP(update_rect.origin.y + update_rect.size.h, first_row, DISP_ROWS); + for (int16_t y = first_row; y < last_row; y++) { + GBitmapDataRowInfo dst_row_info = gbitmap_get_data_row_info(&dst_bitmap, y); + const int16_t start_x = MAX(update_rect.origin.x, dst_row_info.min_x); + const int16_t end_x = MIN(update_rect.origin.x + update_rect.size.w, dst_row_info.max_x + 1); + memset(&dst_row_info.data[start_x], GColorBlack.argb, end_x - start_x); + } + + // bitblt the region of the app framebuffer into the display framebuffer + GPoint dst_offset; + GRect src_rect; + + if (copy_relative_to_origin) { + GRect centered_region = GRect(bezel_width, app_offset_y, app_width, app_height); + GRect clipped_update_region = update_rect; + grect_clip(&clipped_update_region, ¢ered_region); + + src_rect = GRect( + clipped_update_region.origin.x - bezel_width, + clipped_update_region.origin.y - app_offset_y + offset_y, + clipped_update_region.size.w, + clipped_update_region.size.h + ); + dst_offset = clipped_update_region.origin; + } else { + src_rect = GRect( + 0, + offset_y, + update_rect.size.w - bezel_width, + update_rect.size.h - app_offset_y + ); + dst_offset = GPoint(bezel_width + update_rect.origin.x, app_offset_y + update_rect.origin.y); + } + + if (src_rect.size.w > 0 && src_rect.size.h > 0) { + GBitmap sub_bitmap; + gbitmap_init_as_sub_bitmap(&sub_bitmap, &src_bitmap, src_rect); + bitblt_bitmap_into_bitmap(&dst_bitmap, &sub_bitmap, dst_offset, GCompOpAssign, GColorWhite); + } + } +#endif +} diff --git a/src/fw/services/compositor/compositor_display.c b/src/fw/services/compositor/compositor_display.c new file mode 100644 index 0000000000..5bc4352a4d --- /dev/null +++ b/src/fw/services/compositor/compositor_display.c @@ -0,0 +1,162 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/compositor.h" + +#include "applib/graphics/framebuffer.h" +#include "applib/graphics/gcolor_definitions.h" +#include "applib/graphics/gtypes.h" +#include "util/bitset.h" +#include "util/math.h" +#include "util/size.h" + +#include + +//! This variable is used when we are flushing s_framebuffer out to the display driver. +//! It's set to the current row index that we are DMA'ing out to the display. +static uint16_t s_current_flush_line; + +static void (*s_update_complete_handler)(void); + +#ifdef CONFIG_BOARD_FAMILY_ASTERIX +static const uint8_t s_corner_shape[] = { 3, 1, 1 }; +static uint8_t s_line_buffer[FRAMEBUFFER_BYTES_PER_ROW]; +#endif + +#ifdef CONFIG_BOARD_FAMILY_OBELIX +// Rounded corner mask for Obelix — radius 3 quarter-circle. +// Each entry is the number of pixels to mask from the left/right edge. +// Shape follows a quarter-circle: at row y, mask pixels where x < sqrt(r² - y²) +static const uint8_t s_corner_shape[] = { 3, 3, 2, 1 }; + +// For Obelix, we modify the framebuffer directly because the display driver +// does in-place pixel format conversion and expects row.data to point into +// the compositor's framebuffer. We save original corner pixels here to restore later. +#define CORNER_SAVE_ROWS ARRAY_LENGTH(s_corner_shape) +#define CORNER_MAX_WIDTH 3 +static uint8_t s_saved_corners[CORNER_SAVE_ROWS * 2][CORNER_MAX_WIDTH * 2]; // [row][left+right pixels] +static uint8_t s_dirty_y0; +static uint8_t s_dirty_y1; +#endif + +//! display_update get next line callback +static bool prv_flush_get_next_line_cb(DisplayRow* row) { + FrameBuffer *fb = compositor_get_framebuffer(); + + s_current_flush_line = MAX(s_current_flush_line, fb->dirty_rect.origin.y); + const uint16_t y_end = fb->dirty_rect.origin.y + fb->dirty_rect.size.h; + if (s_current_flush_line < y_end) { + row->address = s_current_flush_line; + void *fb_line = framebuffer_get_line(fb, s_current_flush_line); +#ifdef CONFIG_BOARD_FAMILY_ASTERIX + // Draw rounded corners onto the screen without modifying the + // system framebuffer. + if (s_current_flush_line < ARRAY_LENGTH(s_corner_shape) || + s_current_flush_line >= DISP_ROWS - ARRAY_LENGTH(s_corner_shape)) { + memcpy(s_line_buffer, fb_line, FRAMEBUFFER_BYTES_PER_ROW); + uint8_t corner_idx = + (s_current_flush_line < ARRAY_LENGTH(s_corner_shape))? + s_current_flush_line : DISP_ROWS - s_current_flush_line - 1; + uint8_t corner_width = s_corner_shape[corner_idx]; + for (uint8_t pixel = 0; pixel < corner_width; ++pixel) { + bitset8_clear(s_line_buffer, pixel); + bitset8_clear(s_line_buffer, DISP_COLS - pixel - 1); + } + row->data = s_line_buffer; + } else { + row->data = fb_line; + } +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) + // Draw rounded corners by modifying the framebuffer directly. + // The display driver does in-place format conversion and expects row.data + // to point into the compositor's framebuffer. We save and restore corners. + if (s_current_flush_line < ARRAY_LENGTH(s_corner_shape) || + s_current_flush_line >= DISP_ROWS - ARRAY_LENGTH(s_corner_shape)) { + uint8_t corner_idx = + (s_current_flush_line < ARRAY_LENGTH(s_corner_shape))? + s_current_flush_line : DISP_ROWS - s_current_flush_line - 1; + uint8_t save_idx = + (s_current_flush_line < ARRAY_LENGTH(s_corner_shape))? + s_current_flush_line : CORNER_SAVE_ROWS + corner_idx; + uint8_t corner_width = s_corner_shape[corner_idx]; + uint8_t *line = fb_line; + // Save original corner pixels + for (uint8_t pixel = 0; pixel < corner_width; ++pixel) { + s_saved_corners[save_idx][pixel] = line[pixel]; + s_saved_corners[save_idx][CORNER_MAX_WIDTH + pixel] = line[DISP_COLS - pixel - 1]; + } + // Mask corner pixels to black (rounded corner effect) + for (uint8_t pixel = 0; pixel < corner_width; ++pixel) { + line[pixel] = GColorBlackARGB8; + line[DISP_COLS - pixel - 1] = GColorBlackARGB8; + } + } + row->data = fb_line; +#else + row->data = fb_line; +#endif + s_current_flush_line++; + return true; + } + + return false; +} + +//! display_update complete callback +static void prv_flush_complete_cb(void) { +#ifdef CONFIG_BOARD_FAMILY_OBELIX + // Restore original corner pixels that we modified before the display update + FrameBuffer *fb = compositor_get_framebuffer(); + for (uint8_t i = 0; i < CORNER_SAVE_ROWS; ++i) { + uint8_t corner_width = s_corner_shape[i]; + // Top corners (only if row was in dirty region) + if (i >= s_dirty_y0 && i <= s_dirty_y1) { + uint8_t *top_line = framebuffer_get_line(fb, i); + for (uint8_t pixel = 0; pixel < corner_width; ++pixel) { + top_line[pixel] = s_saved_corners[i][pixel]; + top_line[DISP_COLS - pixel - 1] = s_saved_corners[i][CORNER_MAX_WIDTH + pixel]; + } + } + // Bottom corners (only if row was in dirty region) + uint8_t bottom_row = DISP_ROWS - i - 1; + if (bottom_row >= s_dirty_y0 && bottom_row <= s_dirty_y1) { + uint8_t *bottom_line = framebuffer_get_line(fb, bottom_row); + for (uint8_t pixel = 0; pixel < corner_width; ++pixel) { + bottom_line[pixel] = s_saved_corners[CORNER_SAVE_ROWS + i][pixel]; + bottom_line[DISP_COLS - pixel - 1] = s_saved_corners[CORNER_SAVE_ROWS + i][CORNER_MAX_WIDTH + pixel]; + } + } + } +#endif + + s_current_flush_line = 0; + framebuffer_reset_dirty(compositor_get_framebuffer()); + + if (s_update_complete_handler) { + s_update_complete_handler(); + } +} + +void compositor_display_update(void (*handle_update_complete_cb)(void)) { + FrameBuffer *fb = compositor_get_framebuffer(); + if (!framebuffer_is_dirty(fb)) { + return; + } +#ifdef CONFIG_BOARD_FAMILY_GETAFIX + // Force full screen updates - partial ROI causes animation issues on getafix display + fb->dirty_rect = (GRect){ GPointZero, fb->size }; +#endif +#ifdef CONFIG_BOARD_FAMILY_OBELIX + // Capture dirty region bounds for corner restoration later + s_dirty_y0 = fb->dirty_rect.origin.y; + s_dirty_y1 = fb->dirty_rect.origin.y + fb->dirty_rect.size.h - 1; +#endif + s_update_complete_handler = handle_update_complete_cb; + s_current_flush_line = 0; + + display_update(&prv_flush_get_next_line_cb, &prv_flush_complete_cb); +} + +bool compositor_display_update_in_progress(void) { + return display_update_in_progress(); +} diff --git a/src/fw/services/common/compositor/compositor_transitions.c b/src/fw/services/compositor/compositor_transitions.c similarity index 84% rename from src/fw/services/common/compositor/compositor_transitions.c rename to src/fw/services/compositor/compositor_transitions.c index 381402c81d..502b06b5dc 100644 --- a/src/fw/services/common/compositor/compositor_transitions.c +++ b/src/fw/services/compositor/compositor_transitions.c @@ -1,27 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_transitions.h" -#include "compositor_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_private.h" #include "applib/graphics/framebuffer.h" #include "applib/graphics/gdraw_command_transforms.h" #include "applib/graphics/gpath.h" #include "applib/graphics/graphics_private_raw.h" -#include "applib/graphics/graphics_private_raw_mask.h" #include "applib/ui/animation_interpolate.h" #include "kernel/ui/modals/modal_manager.h" #include "system/passert.h" @@ -48,11 +34,11 @@ static void prv_compositor_replace_colors_processor(GDrawCommandProcessor *proce const GDrawCommandList* list, const GDrawCommand *command) { CompositorColorReplacementProcessor *p = (CompositorColorReplacementProcessor *)processor; + const GColor8 key_stroke_color = GColorRed; // fill with app framebuffer (only if a app_fb_key_color != clear was passed) if (!gcolor_is_invisible(p->app_fb_key_color) && gcolor_equal(gdraw_command_get_fill_color(processed_command), p->app_fb_key_color)) { - gdraw_command_set_hidden(processed_command, true); const uint16_t num_points = gdraw_command_get_num_points(processed_command); GPoint points[num_points]; if (sizeof(points) == gdraw_command_copy_points(processed_command, points, sizeof(points))) { @@ -64,9 +50,12 @@ static void prv_compositor_replace_colors_processor(GDrawCommandProcessor *proce compositor_app_framebuffer_fill_callback, &p->framebuffer_offset); } + + // Preserve any stroke by leaving the command visible and clearing only its fill. + gdraw_command_set_fill_color(processed_command, GColorClear); + gdraw_command_replace_color(processed_command, key_stroke_color, p->stroke_color); } else { // Original SVGs use Red for the stroke, replace it here - const GColor8 key_stroke_color = GColorRed; gdraw_command_replace_color(processed_command, key_stroke_color, p->stroke_color); // replace surrounding color gdraw_command_replace_color(processed_command, p->key_color, p->overdraw_color); @@ -124,12 +113,24 @@ void prv_app_fb_fill_assign_horizontal_line(GContext *ctx, int16_t y, Fixed_S16_ } GBitmap app_framebuffer = compositor_get_app_framebuffer_as_bitmap(); - // We only check the destination data rows above (and not also the source data rows) because we - // assume that both source and destination are framebuffers using the native bitmap format + // Both source and destination must use the same format (native bitmap format) PBL_ASSERTN(app_framebuffer.info.format == framebuffer->info.format); - PBL_ASSERTN(app_framebuffer.data_row_infos == framebuffer->data_row_infos); + + // Check if y is within the app framebuffer (e.g., legacy app vs notification) + if (y >= app_framebuffer.bounds.size.h) { + // Row is beyond app framebuffer, nothing to copy + return; + } const GBitmapDataRowInfo source_data_row_info = gbitmap_get_data_row_info(&app_framebuffer, y); + + // Clip to the intersection of source and destination bounds + x1.raw_value = MAX(x1.raw_value, source_data_row_info.min_x << FIXED_S16_3_PRECISION); + x2.raw_value = MIN(x2.raw_value, source_data_row_info.max_x << FIXED_S16_3_PRECISION); + if (x1.integer > x2.integer) { + return; + } + GColor8 *input = (GColor8 *)(source_data_row_info.data + x1.integer); GColor8 *output = (GColor8 *)(destination_data_row_info.data + x1.integer); @@ -147,18 +148,9 @@ void prv_app_fb_fill_assign_horizontal_line(GContext *ctx, int16_t y, Fixed_S16_ // Middle pixels const int16_t width = x2.integer - x1.integer + 1; if (width > 0) { -#if CAPABILITY_HAS_MASKING - const GDrawMask *mask = ctx->draw_state.draw_mask; - for (int x = x1.integer; x < x1.integer + width; x++) { - graphics_private_raw_mask_apply(output, mask, data_row_offset, x1.integer, 1, *input); - input++; - output++; - } -#else memcpy((uint8_t *)output, (uint8_t *)input, width); input += width; output += width; -#endif } // Last pixel with blending (don't render first AND last pixel if line length is 1) @@ -177,9 +169,20 @@ void prv_app_fb_fill_assign_vertical_line(GContext *ctx, int16_t x, Fixed_S16_3 PBL_ASSERTN(framebuffer->bounds.origin.x == 0 && framebuffer->bounds.origin.y == 0); GBitmap app_framebuffer = compositor_get_app_framebuffer_as_bitmap(); - // We assume that both source and destination are framebuffers using the native bitmap format + // Both source and destination must use the same format (native bitmap format) PBL_ASSERTN(app_framebuffer.info.format == framebuffer->info.format); - PBL_ASSERTN(app_framebuffer.data_row_infos == framebuffer->data_row_infos); + + // Clip to app framebuffer height (e.g., legacy app vs notification) + const int16_t app_fb_max_y = app_framebuffer.bounds.size.h - 1; + if (y1.integer > app_fb_max_y) { + // Line starts beyond app framebuffer, nothing to copy + return; + } + if (y2.integer > app_fb_max_y) { + // Clip end of line to app framebuffer bounds + y2.integer = app_fb_max_y; + y2.fraction = FIXED_S16_3_ONE.raw_value - 1; + } GBitmapDataRowInfo source_data_row_info = gbitmap_get_data_row_info(&app_framebuffer, y1.integer); GColor8 *input = (GColor8 *)(source_data_row_info.data + x); @@ -207,12 +210,7 @@ void prv_app_fb_fill_assign_vertical_line(GContext *ctx, int16_t x, Fixed_S16_3 while (y1.integer <= y2.integer) { // Only draw the pixel if its within the bitmap data row range if (WITHIN(x, destination_data_row_info.min_x, destination_data_row_info.max_x)) { -#if CAPABILITY_HAS_MASKING - const GDrawMask *mask = ctx->draw_state.draw_mask; - graphics_private_raw_mask_apply(output, mask, data_row_offset, x, 1, *input); -#else *output = *input; -#endif } y1.integer++; source_data_row_info = gbitmap_get_data_row_info(&app_framebuffer, y1.integer); diff --git a/src/fw/services/common/compositor/default/compositor_dot_transitions.c b/src/fw/services/compositor/default/compositor_dot_transitions.c similarity index 93% rename from src/fw/services/common/compositor/default/compositor_dot_transitions.c rename to src/fw/services/compositor/default/compositor_dot_transitions.c index b197cad801..d3c9b2a266 100644 --- a/src/fw/services/common/compositor/default/compositor_dot_transitions.c +++ b/src/fw/services/compositor/default/compositor_dot_transitions.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_dot_transitions.h" - -#include "services/common/compositor/compositor_private.h" -#include "services/common/compositor/compositor_transitions.h" - -#include "apps/system_apps/launcher/launcher_app.h" -#include "apps/system_apps/timeline/timeline_common.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_dot_transitions.h" + +#include "pbl/services/compositor/compositor_private.h" +#include "pbl/services/compositor/compositor_transitions.h" + +#include "apps/system/launcher/launcher.h" +#include "apps/system/timeline/common.h" #include "applib/ui/animation_interpolate.h" #include "applib/ui/animation_timing.h" #include "applib/graphics/bitblt.h" @@ -51,6 +38,7 @@ static CompositorTransitionDirection prv_flip_transition_direction( } } +#if PBL_RECT //! linear interpolation between two GPoints, supports delay and clamping //! @param delay value to postpone interpolation (in range 0..ANIMATION_NORMALIZED_MAX) static GPoint prv_gpoint_interpolate(int32_t delay, int32_t normalized, @@ -67,7 +55,6 @@ static GPoint prv_gpoint_interpolate(int32_t delay, int32_t normalized, static GPoint prv_gpoint_mid(const GPoint a, const GPoint b) { return GPoint((a.x + b.x) / 2, (a.y + b.y) / 2); } - //! Draw the "collapse" portion of the animation. This function can either work by drawing //! an outer ring using the fill_cb, or by drawing and expanding inner portion using the fill_cb. //! This behaviour is configured by the inner bool. @@ -152,6 +139,7 @@ static void prv_gpath_draw_filled_cb(GContext *ctx, int16_t y, x_range_end.integer - x_range_begin.integer - 1, 1); graphics_fill_rect(ctx, &fill_rect); } +#endif // PBL_RECT //! Draw a dumb dot at the supplied position with the supplied color static void prv_draw_dot(GContext *ctx, GPoint pos, GColor color) { @@ -181,6 +169,7 @@ typedef struct PACKED { _Static_assert(sizeof(DotTransitionAnimationConfiguration) == sizeof(void *), ""); +#if PBL_RECT static void prv_collapse_animation_update_rect(GContext *ctx, DotTransitionAnimationConfiguration config, uint32_t distance_normalized) { @@ -211,6 +200,7 @@ static void prv_collapse_animation_update_rect(GContext *ctx, prv_collapse_animation(ctx, distance_normalized, inner, draw_filled_cb); } +#endif void compositor_dot_transitions_collapsing_ring_animation_update(GContext *ctx, uint32_t distance_normalized, @@ -250,9 +240,7 @@ static void prv_collapse_animation_update_round(GContext *ctx, uint32_t distance_normalized) { // If we're expanding, blit the app framebuffer into the system framebuffer (so below the ring) if (!config.collapse_starting_animation) { - GBitmap src_bitmap = compositor_get_app_framebuffer_as_bitmap(); - GBitmap dest_bitmap = compositor_get_framebuffer_as_bitmap(); - bitblt_bitmap_into_bitmap(&dest_bitmap, &src_bitmap, GPointZero, GCompOpAssign, GColorWhite); + compositor_scaled_app_fb_copy(GRect(0, 0, DISP_COLS, DISP_ROWS), false /* copy_relative_to_origin */); } compositor_dot_transitions_collapsing_ring_animation_update(ctx, distance_normalized, diff --git a/src/fw/services/common/compositor/default/compositor_launcher_app_transitions.c b/src/fw/services/compositor/default/compositor_launcher_app_transitions.c similarity index 85% rename from src/fw/services/common/compositor/default/compositor_launcher_app_transitions.c rename to src/fw/services/compositor/default/compositor_launcher_app_transitions.c index 1bb3050a0c..972ce0acf7 100644 --- a/src/fw/services/common/compositor/default/compositor_launcher_app_transitions.c +++ b/src/fw/services/compositor/default/compositor_launcher_app_transitions.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_launcher_app_transitions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_launcher_app_transitions.h" #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics_private.h" -#include "apps/system_apps/launcher/default/launcher_app.h" -#include "services/common/compositor/compositor_transitions.h" +#include "apps/system/launcher/default/launcher.h" +#include "pbl/services/compositor/compositor_transitions.h" #include "system/passert.h" #include "util/attributes.h" #include "util/math.h" @@ -54,31 +41,32 @@ static void prv_move_region_of_bitmap_horizontally(GBitmap *bitmap, const GRect graphics_private_move_pixels_horizontally(®ion_sub_bitmap, delta_x, true /* patch_garbage */); } +static void prv_duplicate_framebuffer_cols(GBitmap *bitmap, int16_t start_col, int16_t end_col, int16_t dupe_col) { + const int16_t delta = start_col > end_col ? -1 : 1; + for (int16_t dest_col = start_col; dest_col != end_col; dest_col += delta) { + bitmap->bounds = (GRect) { + .origin.x = (dupe_col >= 0 ? dupe_col : dest_col), + .size = { 1, DISP_ROWS }, + }; + bitblt_bitmap_into_bitmap(bitmap, bitmap, GPoint(dest_col, 0), GCompOpAssign, GColorWhite); + } +} + static void prv_copy_app_fb_patching_garbage(int16_t dest_origin_x) { - const GBitmap src_bitmap = compositor_get_app_framebuffer_as_bitmap(); GBitmap dest_bitmap = compositor_get_framebuffer_as_bitmap(); + compositor_scaled_app_fb_copy(GRect(dest_origin_x, 0, DISP_COLS - dest_origin_x, DISP_ROWS), false /* copy_relative_to_origin */); + // Patch garbage pixels using the first/last column, if necessary if (dest_origin_x != 0) { const bool offscreen_left = (dest_origin_x < 0); const int16_t first_column_x = 0; const int16_t last_column_x = DISP_COLS - 1; - const GRect column_to_replicate = (GRect) { - .origin = GPoint((offscreen_left ? last_column_x : first_column_x), 0), - .size = GSize(1, DISP_ROWS), - }; - GBitmap column_to_replicate_sub_bitmap; - gbitmap_init_as_sub_bitmap(&column_to_replicate_sub_bitmap, &src_bitmap, column_to_replicate); + const int16_t column_to_replicate = (offscreen_left ? last_column_x : first_column_x); const int16_t from = (int16_t)(offscreen_left ? (dest_origin_x + DISP_COLS) : first_column_x); const int16_t to = (int16_t)(offscreen_left ? last_column_x : dest_origin_x - 1); - for (int16_t x = from; x <= to; x++) { - bitblt_bitmap_into_bitmap(&dest_bitmap, &column_to_replicate_sub_bitmap, - GPoint(x, 0), GCompOpAssign, GColorWhite); - } + prv_duplicate_framebuffer_cols(&dest_bitmap, from, to, column_to_replicate); } - - bitblt_bitmap_into_bitmap(&dest_bitmap, &src_bitmap, GPoint(dest_origin_x, 0), GCompOpAssign, - GColorWhite); } static void prv_manipulate_launcher_in_system_framebuffer(GContext *ctx, diff --git a/src/fw/services/common/compositor/default/compositor_modal_transitions.c b/src/fw/services/compositor/default/compositor_modal_transitions.c similarity index 91% rename from src/fw/services/common/compositor/default/compositor_modal_transitions.c rename to src/fw/services/compositor/default/compositor_modal_transitions.c index 29d50de48b..64a741bb7a 100644 --- a/src/fw/services/common/compositor/default/compositor_modal_transitions.c +++ b/src/fw/services/compositor/default/compositor_modal_transitions.c @@ -1,36 +1,23 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_modal_transitions.h" - -#include "services/common/compositor/compositor_transitions.h" -#include "services/common/compositor/compositor_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_modal_transitions.h" + +#include "pbl/services/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_private.h" #include "applib/graphics/framebuffer.h" #include "util/trig.h" #include "applib/ui/animation_interpolate.h" #include "applib/graphics/gdraw_command_sequence.h" -#include "apps/system_apps/timeline/timeline_common.h" +#include "apps/system/timeline/common.h" #include "kernel/ui/kernel_ui.h" #include "kernel/ui/modals/modal_manager.h" #include "resource/resource_ids.auto.h" // No animations will be shown on the following platforms -#if defined(RECOVERY_FW) +#if defined(CONFIG_RECOVERY_FW) #define MODAL_CONTRACT_TO_MODAL_ANIMATION (RESOURCE_ID_INVALID) #define MODAL_CONTRACT_FROM_MODAL_ANIMATION (RESOURCE_ID_INVALID) #define MODAL_EXPAND_TO_APP_ANIMATION (RESOURCE_ID_INVALID) diff --git a/src/fw/services/compositor/default/compositor_peek_transitions.c b/src/fw/services/compositor/default/compositor_peek_transitions.c new file mode 100644 index 0000000000..9a2f05aa9a --- /dev/null +++ b/src/fw/services/compositor/default/compositor_peek_transitions.c @@ -0,0 +1,62 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_peek_transitions.h" + +#include "applib/graphics/bitblt.h" +#include "applib/graphics/framebuffer.h" +#include "applib/graphics/gtypes.h" +#include "applib/ui/animation_interpolate.h" +#include "apps/system/timeline/common.h" +#include "popups/timeline/peek.h" +#include "popups/timeline/peek_animations.h" +#include "pbl/services/compositor/compositor_private.h" +#include "pbl/services/compositor/compositor_transitions.h" + +#define NUM_FRAMES (3) + +typedef struct CompositorPeekTransitionData { + int offset_y; +} CompositorPeekTransitionData; + +static CompositorPeekTransitionData s_data; + +static void prv_update_peek_transition_animation(GContext *ctx, Animation *animation, + uint32_t distance_normalized) { + const AnimationProgress progress = distance_normalized; + + GRect box = TIMELINE_PEEK_FRAME_VISIBLE; + const int16_t initial_offset_y = -4; + const int16_t final_offset_y = 7; + box.origin.y = interpolate_int16(progress, box.origin.y + initial_offset_y, final_offset_y); + + if (progress > (ANIMATION_NORMALIZED_MAX / NUM_FRAMES)) { + timeline_peek_draw_background(ctx, &DISP_FRAME, 0); + peek_animations_draw_compositor_background_speed_lines( + ctx, GPoint(PEEK_ANIMATIONS_SPEED_LINES_OFFSET_X, 0)); + } + const unsigned int num_concurrent = 3; + timeline_peek_draw_background(ctx, &box, num_concurrent); + + const unsigned int concurrent_height = timeline_peek_get_concurrent_height(num_concurrent); + const int16_t foreground_speed_line_offset_y = 2; + const GPoint offset = + gpoint_add(box.origin, GPoint(PEEK_ANIMATIONS_SPEED_LINES_OFFSET_X, + concurrent_height + foreground_speed_line_offset_y)); + graphics_context_set_fill_color(ctx, GColorBlack); + peek_animations_draw_compositor_foreground_speed_lines(ctx, offset); +} + +static void prv_init_peek_transition_animation(Animation *animation) { + animation_set_curve(animation, AnimationCurveLinear); + animation_set_duration(animation, NUM_FRAMES * ANIMATION_TARGET_FRAME_INTERVAL_MS); +} + +const CompositorTransition *compositor_peek_transition_timeline_get(void) { + s_data = (CompositorPeekTransitionData) {}; + static const CompositorTransition s_impl = { + .init = prv_init_peek_transition_animation, + .update = prv_update_peek_transition_animation, + }; + return &s_impl; +} diff --git a/src/fw/services/common/compositor/default/compositor_port_hole_transitions.c b/src/fw/services/compositor/default/compositor_port_hole_transitions.c similarity index 75% rename from src/fw/services/common/compositor/default/compositor_port_hole_transitions.c rename to src/fw/services/compositor/default/compositor_port_hole_transitions.c index c6550efee5..08e1cdb2c0 100644 --- a/src/fw/services/common/compositor/default/compositor_port_hole_transitions.c +++ b/src/fw/services/compositor/default/compositor_port_hole_transitions.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_port_hole_transitions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_port_hole_transitions.h" #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics_private.h" #include "util/trig.h" -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_transitions.h" #include "resource/resource_ids.auto.h" #include "system/logging.h" @@ -75,13 +62,10 @@ static void prv_port_hole_transition_animation_update(GContext *ctx, if (distance_normalized > transition_progress_threshold) { // Second half of the transition - const GBitmap app_bitmap = compositor_get_app_framebuffer_as_bitmap(); - GBitmap sys_bitmap = compositor_get_framebuffer_as_bitmap(); - const GPoint point = direction_vertical ? GPoint(0, -current_offset_px) : - GPoint(-current_offset_px, 0); - // the framebuffer is already wiped at the beginning, so we can use GColorWhite as a fill color - // without filling it ourselves - bitblt_bitmap_into_bitmap(&sys_bitmap, &app_bitmap, point, GCompOpAssign, GColorWhite); + const GRect rect = direction_vertical ? GRect(0, -current_offset_px, DISP_COLS, DISP_ROWS - current_offset_px) : + GRect(-current_offset_px, 0, DISP_COLS - current_offset_px, DISP_ROWS); + + compositor_scaled_app_fb_copy(rect, false /* copy_relative_to_origin */); } else { // First half of the transition const int16_t diff = s_data.animation_offset_px - current_offset_px; diff --git a/src/fw/services/common/compositor/default/compositor_round_flip_transitions.c b/src/fw/services/compositor/default/compositor_round_flip_transitions.c similarity index 89% rename from src/fw/services/common/compositor/default/compositor_round_flip_transitions.c rename to src/fw/services/compositor/default/compositor_round_flip_transitions.c index 2c0ec4cfca..814b6ff43f 100644 --- a/src/fw/services/common/compositor/default/compositor_round_flip_transitions.c +++ b/src/fw/services/compositor/default/compositor_round_flip_transitions.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_round_flip_transitions.h" - -#include "services/common/compositor/compositor_transitions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_round_flip_transitions.h" + +#include "pbl/services/compositor/compositor_transitions.h" #include "applib/graphics/graphics_private_raw.h" #include "applib/graphics/framebuffer.h" diff --git a/src/fw/services/common/compositor/default/compositor_shutter_transitions.c b/src/fw/services/compositor/default/compositor_shutter_transitions.c similarity index 92% rename from src/fw/services/common/compositor/default/compositor_shutter_transitions.c rename to src/fw/services/compositor/default/compositor_shutter_transitions.c index 2c954236b6..cdcee60f10 100644 --- a/src/fw/services/common/compositor/default/compositor_shutter_transitions.c +++ b/src/fw/services/compositor/default/compositor_shutter_transitions.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_shutter_transitions.h" - -#include "services/common/compositor/compositor_transitions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_shutter_transitions.h" + +#include "pbl/services/compositor/compositor_transitions.h" #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" @@ -189,8 +176,6 @@ static void prv_draw_in(GContext *ctx, int move_size, uint32_t distance, bool ve bool invert) { const int16_t current_offset_px = prv_interpolate_two_ways(distance, invert ? -move_size : move_size); - const GBitmap app_bitmap = compositor_get_app_framebuffer_as_bitmap(); - GBitmap sys_bitmap = compositor_get_framebuffer_as_bitmap(); const GPoint point = vertical ? GPoint(0, -current_offset_px) : GPoint(-current_offset_px, 0); @@ -198,7 +183,7 @@ static void prv_draw_in(GContext *ctx, int move_size, uint32_t distance, bool ve graphics_context_set_fill_color(ctx, s_data.sampled_color); graphics_fill_rect(ctx, &DISP_FRAME); - bitblt_bitmap_into_bitmap(&sys_bitmap, &app_bitmap, point, GCompOpAssign, GColorWhite); + compositor_scaled_app_fb_copy(GRect(point.x, point.y, DISP_COLS, DISP_ROWS), false /* copy_relative_to_origin */); const GPoint drawing_box_origin = ctx->draw_state.drawing_box.origin; gpoint_add_eq(&ctx->draw_state.drawing_box.origin, point); diff --git a/src/fw/services/common/compositor/default/compositor_slide_transitions.c b/src/fw/services/compositor/default/compositor_slide_transitions.c similarity index 84% rename from src/fw/services/common/compositor/default/compositor_slide_transitions.c rename to src/fw/services/compositor/default/compositor_slide_transitions.c index 11c85cb5d3..895723de25 100644 --- a/src/fw/services/common/compositor/default/compositor_slide_transitions.c +++ b/src/fw/services/compositor/default/compositor_slide_transitions.c @@ -1,30 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_slide_transitions.h" - -#include "services/common/compositor/compositor_private.h" -#include "services/common/compositor/compositor_transitions.h" - -#include "apps/system_apps/timeline/timeline_common.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/default/compositor_slide_transitions.h" + +#include "pbl/services/compositor/compositor_private.h" +#include "pbl/services/compositor/compositor_transitions.h" + +#include "apps/system/timeline/common.h" #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" #include "applib/graphics/gtypes.h" #include "applib/ui/animation_interpolate.h" #include "popups/timeline/peek.h" +#include "system/logging.h" // TODO: PBL-31388 Factor out vertical compositor slide animations // This does a similar transition to the legacy modal slide transition @@ -61,7 +49,11 @@ static void prv_shift_framebuffer_rows(GBitmap *dest_bitmap, int16_t start_row, static void prv_duplicate_framebuffer_row(GBitmap *dest_bitmap, int16_t start_row, int16_t end_row, GBitmap *src_bitmap, int16_t dupe_row) { - prv_copy_framebuffer_rows(dest_bitmap, src_bitmap, start_row, end_row, dupe_row, 0); + const int16_t delta = start_row > end_row ? -1 : 1; + for (int16_t dest_row = start_row; dest_row != end_row; dest_row += delta) { + int16_t offset_y = (dupe_row >= 0 ? dupe_row : dest_row) - dest_row; + compositor_scaled_app_fb_copy_offset(GRect(0, dest_row, DISP_COLS, 1), false /* copy_relative_to_origin */, offset_y); + } } static void prv_slide_transition_animation_update(GContext *ctx, Animation *animation, @@ -115,7 +107,6 @@ static void prv_slide_transition_animation_update(GContext *ctx, Animation *anim prv_shift_framebuffer_rows(&dest_bitmap, shift_start_row, shift_end_row, shift_amount); } if (s_data.timeline_is_destination) { -#if CAPABILITY_HAS_TIMELINE_PEEK if (!s_data.timeline_is_empty) { graphics_context_set_fill_color(ctx, GColorWhite); const int content_width = DISP_COLS - TIMELINE_PEEK_ICON_BOX_WIDTH; @@ -123,17 +114,14 @@ static void prv_slide_transition_animation_update(GContext *ctx, Animation *anim graphics_context_set_fill_color(ctx, s_data.fill_color); graphics_fill_rect(ctx, &GRect(content_width, fill_offset_y, TIMELINE_PEEK_ICON_BOX_WIDTH, fill_height)); - } else -#endif - { + } else { graphics_context_set_fill_color(ctx, s_data.fill_color); graphics_fill_rect(ctx, &GRect(0, fill_offset_y, DISP_COLS, fill_height)); } } else { - GBitmap app_bitmap = compositor_get_app_framebuffer_as_bitmap(); - bitblt_bitmap_into_bitmap(&dest_bitmap, &app_bitmap, GPoint(0, app_offset_y), - GCompOpAssign, GColorWhite); + compositor_scaled_app_fb_copy(GRect(0, app_offset_y, DISP_COLS, DISP_ROWS), false /* copy_relative_to_origin */); if (app_should_dupe) { + GBitmap app_bitmap = compositor_get_app_framebuffer_as_bitmap(); prv_duplicate_framebuffer_row(&dest_bitmap, app_dupe_row, app_offset_y + app_dupe_row, &app_bitmap, app_dupe_row); } diff --git a/src/fw/services/common/compositor/legacy/compositor_app_slide_transitions.c b/src/fw/services/compositor/legacy/compositor_app_slide_transitions.c similarity index 84% rename from src/fw/services/common/compositor/legacy/compositor_app_slide_transitions.c rename to src/fw/services/compositor/legacy/compositor_app_slide_transitions.c index c75573219a..281e91f379 100644 --- a/src/fw/services/common/compositor/legacy/compositor_app_slide_transitions.c +++ b/src/fw/services/compositor/legacy/compositor_app_slide_transitions.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_app_slide_transitions.h" - -#include "services/common/compositor/compositor_transitions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/legacy/compositor_app_slide_transitions.h" + +#include "pbl/services/compositor/compositor_transitions.h" #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" diff --git a/src/fw/services/common/compositor/legacy/compositor_modal_slide_transitions.c b/src/fw/services/compositor/legacy/compositor_modal_slide_transitions.c similarity index 87% rename from src/fw/services/common/compositor/legacy/compositor_modal_slide_transitions.c rename to src/fw/services/compositor/legacy/compositor_modal_slide_transitions.c index cf154b5962..1c4565dd21 100644 --- a/src/fw/services/common/compositor/legacy/compositor_modal_slide_transitions.c +++ b/src/fw/services/compositor/legacy/compositor_modal_slide_transitions.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compositor_modal_slide_transitions.h" - -#include "services/common/compositor/compositor_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/legacy/compositor_modal_slide_transitions.h" + +#include "pbl/services/compositor/compositor_private.h" #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" diff --git a/src/fw/services/common/compositor/screenshot_pp.c b/src/fw/services/compositor/screenshot_pp.c similarity index 83% rename from src/fw/services/common/compositor/screenshot_pp.c rename to src/fw/services/compositor/screenshot_pp.c index 97a50de1a7..8283dffd6d 100644 --- a/src/fw/services/common/compositor/screenshot_pp.c +++ b/src/fw/services/compositor/screenshot_pp.c @@ -1,32 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "screenshot_pp.h" - -#include "compositor.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/compositor/screenshot_pp.h" + +#include "pbl/services/compositor/compositor.h" #include "applib/graphics/framebuffer.h" #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/system_task.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "util/attributes.h" #include "util/net.h" +PBL_LOG_MODULE_DECLARE(service_compositor, CONFIG_SERVICE_COMPOSITOR_LOG_LEVEL); + static const uint16_t SCREENSHOT_ENDPOINT_ID = 8000; static bool s_screenshot_in_progress = false; @@ -94,7 +83,7 @@ static void prv_request_fast_connection(CommSession *session) { static uint32_t prv_framebuffer_next_chunk(FrameBufferState *restrict state, uint32_t max_chunk_bytes, uint8_t *output_buffer) { - const uint32_t bytes_per_row = SCREEN_COLOR_DEPTH_BITS * DISP_COLS / 8; + const uint32_t bytes_per_row = CONFIG_SCREEN_COLOR_DEPTH_BITS * DISP_COLS / 8; const uint32_t cols_per_byte = DISP_COLS / bytes_per_row; uint32_t remaining_chunk_bytes = max_chunk_bytes; @@ -111,8 +100,8 @@ static uint32_t prv_framebuffer_next_chunk(FrameBufferState *restrict state, remaining_framebuffer_row_bytes = remaining_chunk_bytes; } -#ifdef PLATFORM_SPALDING - const GBitmapDataRowInfoInternal *row_infos = g_gbitmap_spalding_data_row_infos; +#if PBL_ROUND + const GBitmapDataRowInfoInternal *row_infos = g_gbitmap_data_row_infos; const size_t framebuffer_row_min_pixel = row_infos[state->row].min_x; const size_t framebuffer_row_max_pixel = row_infos[state->row].max_x; for (uint32_t i = 0; i < remaining_framebuffer_row_bytes; i++) { @@ -154,9 +143,10 @@ void screenshot_send_next_chunk(void* raw_state) { void *buffer = kernel_zalloc(max_buf_len); if (!buffer) { - PBL_LOG(LOG_LEVEL_WARNING, "Screenshot aborted, OOM."); + PBL_LOG_WRN("Screenshot aborted, OOM."); prv_send_error_response(session, SCREENSHOT_OOM_ERROR); prv_finish(state); + return; } uint32_t len = prv_framebuffer_next_chunk(&state->framebuffer, max_buf_len, buffer); session_len += len; @@ -171,7 +161,7 @@ void screenshot_send_next_chunk(void* raw_state) { if (max_buf_len == 0 /* disconnected */ || !(sb = comm_session_send_buffer_begin_write(session, SCREENSHOT_ENDPOINT_ID, session_len, COMM_SESSION_DEFAULT_TIMEOUT))) { - PBL_LOG(LOG_LEVEL_WARNING, "Terminating screenshot send early: %"PRIu32, max_buf_len); + PBL_LOG_WRN("Terminating screenshot send early: %"PRIu32, max_buf_len); prv_finish(state); return; } @@ -181,12 +171,12 @@ void screenshot_send_next_chunk(void* raw_state) { } else { const ScreenshotHeader header = (const ScreenshotHeader) { .response_code = SCREENSHOT_OK, -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 .version = htonl(1), -#elif SCREEN_COLOR_DEPTH_BITS == 8 +#elif CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 .version = htonl(2), #else -#warning "Need SCREEN_COLOR_DEPTH_BITS for screenshot version." +#warning "Need CONFIG_SCREEN_COLOR_DEPTH_BITS for screenshot version." #endif .width = htonl(state->framebuffer.width), .height = htonl(state->framebuffer.height), @@ -209,13 +199,13 @@ void screenshot_send_next_chunk(void* raw_state) { void screenshot_protocol_msg_callback(CommSession *session, const uint8_t* msg_data, unsigned int msg_len) { uint8_t sub_command = msg_data[0]; if (sub_command != 0x00) { - PBL_LOG(LOG_LEVEL_ERROR, "first byte can't be %u", sub_command); + PBL_LOG_ERR("first byte can't be %u", sub_command); prv_send_error_response(session, SCREENSHOT_MALFORMED_COMMAND); return; } if (s_screenshot_in_progress) { - PBL_LOG(LOG_LEVEL_ERROR, "Screenshot already in progress."); + PBL_LOG_ERR("Screenshot already in progress."); // Use a low timeout, if we are already in screenshot_send_next_chunk with the send buffer locked, then this // would block for a long time, causing the comm_protocol_dispatch_message()'s 150ms max timeout to trip. prv_send_error_response(session, SCREENSHOT_ALREADY_IN_PROGRESS); diff --git a/src/fw/services/compositor/wscript_build b/src/fw/services/compositor/wscript_build new file mode 100644 index 0000000000..0e52118611 --- /dev/null +++ b/src/fw/services/compositor/wscript_build @@ -0,0 +1,30 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'compositor.c', + 'compositor_display.c', + 'compositor_transitions.c', + 'screenshot_pp.c', + 'default/compositor_dot_transitions.c', + 'default/compositor_launcher_app_transitions.c', + 'default/compositor_peek_transitions.c', + 'default/compositor_port_hole_transitions.c', + 'default/compositor_round_flip_transitions.c', + 'default/compositor_shutter_transitions.c', + 'default/compositor_slide_transitions.c', + 'legacy/compositor_app_slide_transitions.c', +] + +if bld.env.CONFIG_SCREEN_COLOR_DEPTH_BITS == 1: + sources.append('legacy/compositor_modal_slide_transitions.c') +else: + sources.append('default/compositor_modal_transitions.c') + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_compositor', +) + +bld.env.SERVICES.append('services_compositor') diff --git a/src/fw/services/contacts/Kconfig b/src/fw/services/contacts/Kconfig new file mode 100644 index 0000000000..5fc089b419 --- /dev/null +++ b/src/fw/services/contacts/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_CONTACTS + bool "Contacts" + help + Contacts service. diff --git a/src/fw/services/normal/contacts/attributes_address.c b/src/fw/services/contacts/attributes_address.c similarity index 84% rename from src/fw/services/normal/contacts/attributes_address.c rename to src/fw/services/contacts/attributes_address.c index 222d754ba6..e1017a755c 100644 --- a/src/fw/services/normal/contacts/attributes_address.c +++ b/src/fw/services/contacts/attributes_address.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "attributes_address.h" +#include "pbl/services/contacts/attributes_address.h" -#include "services/normal/timeline/attribute_group.h" +#include "pbl/services/timeline/attribute_group.h" #define GROUP_TYPE AttributeGroupType_Address diff --git a/src/fw/services/normal/contacts/contacts.c b/src/fw/services/contacts/contacts.c similarity index 79% rename from src/fw/services/normal/contacts/contacts.c rename to src/fw/services/contacts/contacts.c index c19dc17880..8e366b23a4 100644 --- a/src/fw/services/normal/contacts/contacts.c +++ b/src/fw/services/contacts/contacts.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "contacts.h" +#include "pbl/services/contacts/contacts.h" #include "kernel/pbl_malloc.h" -#include "services/normal/blob_db/contacts_db.h" -#include "services/normal/timeline/attributes_actions.h" +#include "pbl/services/blob_db/contacts_db.h" +#include "pbl/services/timeline/attributes_actions.h" #include "system/logging.h" static Contact* prv_deserialize_contact(SerializedContact *serialized_contact, diff --git a/src/fw/services/contacts/wscript_build b/src/fw/services/contacts/wscript_build new file mode 100644 index 0000000000..2d2267de29 --- /dev/null +++ b/src/fw/services/contacts/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'attributes_address.c', + 'contacts.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_contacts', +) + +bld.env.SERVICES.append('services_contacts') diff --git a/src/fw/services/cron/Kconfig b/src/fw/services/cron/Kconfig new file mode 100644 index 0000000000..9b3b33713d --- /dev/null +++ b/src/fw/services/cron/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_CRON + bool "Cron" + help + Cron-like scheduler for periodic jobs. + +if SERVICE_CRON + +module = SERVICE_CRON +module-str = Cron +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/cron/service.c b/src/fw/services/cron/service.c new file mode 100644 index 0000000000..c1468d6ae1 --- /dev/null +++ b/src/fw/services/cron/service.c @@ -0,0 +1,454 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/cron.h" +#include + +#include "os/mutex.h" +#include "system/passert.h" +#include "pbl/services/regular_timer.h" +#include "system/logging.h" +#include "util/math.h" + +PBL_LOG_MODULE_DEFINE(service_cron, CONFIG_SERVICE_CRON_LOG_LEVEL); + +//! Don't let users modify the list while callbacks are occurring. +static PebbleMutex *s_list_mutex = NULL; + +static void prv_timer_callback(void* data); +static RegularTimerInfo s_regular = { + .cb = prv_timer_callback, +}; + +// List of jobs sorted from soonest to farthest. +static ListNode *s_scheduled_jobs; + +// ------------------------------------------------------------------------------------------- +static bool prv_is_scheduled(CronJob *job) { + // Assumes mutex lock is already taken + return list_contains(s_scheduled_jobs, &job->list_node); +} + +static int prv_sort(void *a, void *b) { + CronJob *job_a = (CronJob*)a; + CronJob *job_b = (CronJob*)b; + return job_b->cached_execute_time - job_a->cached_execute_time; +} + +// ------------------------------------------------------------------------------------------- +static void prv_timer_callback(void* data) { + mutex_lock(s_list_mutex); + while (s_scheduled_jobs != NULL && + ((CronJob*)s_scheduled_jobs)->cached_execute_time <= rtc_get_time()) { + CronJob *job = (CronJob*)s_scheduled_jobs; + // Remove the job from the list, it's done. + s_scheduled_jobs = list_pop_head(s_scheduled_jobs); + + // Release the mutex while we execute the callback + mutex_unlock(s_list_mutex); + job->cb(job, job->cb_data); + mutex_lock(s_list_mutex); + } + mutex_unlock(s_list_mutex); +} + +// -------------------------------------------------------------------------------------------- +void cron_service_handle_clock_change(PebbleSetTimeEvent *set_time_info) { + mutex_lock(s_list_mutex); + + const bool must_recalc = set_time_info->gmt_offset_delta != 0 || set_time_info->dst_changed; + // Because it's ABS, it'll be unsigned. This makes the compiler behave. + const uint32_t change_diff = ABS(set_time_info->utc_time_delta); + // Need to re-build the list somewhere else + ListNode *newlist = NULL; + while (s_scheduled_jobs != NULL) { + CronJob* job = (CronJob*)s_scheduled_jobs; + s_scheduled_jobs = list_pop_head(s_scheduled_jobs); + // Re-calculate the execute time. + // See the notes in the API header on how this works. + if (must_recalc || change_diff >= job->clock_change_tolerance) { + job->cached_execute_time = cron_job_get_execute_time(job); + } + PBL_LOG_INFO("Cron job rescheduled for %ld", job->cached_execute_time); + + newlist = list_sorted_add(newlist, &job->list_node, prv_sort, true); + } + // Then move it back to the static + s_scheduled_jobs = newlist; + + mutex_unlock(s_list_mutex); + + // We want to run any tasks we've skipped over. + prv_timer_callback(NULL); +} + +// -------------------------------------------------------------------------------------------- +void cron_service_init(void) { + PBL_ASSERTN(s_list_mutex == NULL); + + s_list_mutex = mutex_create(); + s_scheduled_jobs = NULL; + + regular_timer_add_seconds_callback(&s_regular); +} + +// ------------------------------------------------------------------------------------------- +time_t cron_job_schedule(CronJob *job) { + PBL_ASSERTN(s_list_mutex); + + mutex_lock(s_list_mutex); + + const time_t now = rtc_get_time(); + // Always update the execution time. + job->cached_execute_time = cron_job_get_execute_time_from_epoch(job, now); + // If not scheduled yet, schedule it. + if (!prv_is_scheduled(job)) { + s_scheduled_jobs = list_sorted_add(s_scheduled_jobs, &job->list_node, prv_sort, true); + } + PBL_LOG_DBG("Cron job scheduled for %ld (%+ld)", job->cached_execute_time, + (job->cached_execute_time - now)); + + mutex_unlock(s_list_mutex); + + return job->cached_execute_time; +} + +// ------------------------------------------------------------------------------------------ +time_t cron_job_schedule_after(CronJob *job, CronJob *new_job) { + PBL_ASSERTN(s_list_mutex); + + mutex_lock(s_list_mutex); + + // can't schedule an already scheduled job + PBL_ASSERTN(!prv_is_scheduled(new_job)); + // can't schedule after an unscheduled job + PBL_ASSERTN(prv_is_scheduled(job)); + + // copy schedule info from existing job + CronJob temp_job = *job; + list_init(&temp_job.list_node); + temp_job.cb = new_job->cb; + temp_job.cb_data = new_job->cb_data; + *new_job = temp_job; + + // insert after in the list, which guarantees it gets executed after + list_insert_after(&job->list_node, &new_job->list_node); + PBL_LOG_DBG("Cron job scheduled for %ld", job->cached_execute_time); + + mutex_unlock(s_list_mutex); + + return job->cached_execute_time; +} + +// ------------------------------------------------------------------------------------------ +bool cron_job_is_scheduled(CronJob *job) { + PBL_ASSERTN(s_list_mutex); + + mutex_lock(s_list_mutex); + bool rv = prv_is_scheduled(job); + mutex_unlock(s_list_mutex); + + return (rv); +} + +// ------------------------------------------------------------------------------------------ +bool cron_job_unschedule(CronJob *job) { + PBL_ASSERTN(s_list_mutex); + bool removed = false; + mutex_lock(s_list_mutex); + + if (prv_is_scheduled(job)) { + list_remove(&job->list_node, &s_scheduled_jobs, NULL); + removed = true; + } + + mutex_unlock(s_list_mutex); + return removed; +} + + +// --------------------------------------------------------------------------------------- +// For Testing: + +void cron_clear_all_jobs(void) { + mutex_lock(s_list_mutex); + + // Iterate over all the jobs to remove them all. + for (ListNode* iter = s_scheduled_jobs; iter != NULL; ) { + CronJob* job = (CronJob*)iter; + iter = list_get_next(iter); + // Remove the job from the list. + list_remove(&job->list_node, NULL, NULL); + } + s_scheduled_jobs = NULL; + + mutex_unlock(s_list_mutex); +} + +void cron_service_deinit(void) { + cron_clear_all_jobs(); + + mutex_destroy(s_list_mutex); + s_list_mutex = NULL; + + regular_timer_remove_callback(&s_regular); +} + +uint32_t cron_service_get_job_count(void) { + uint32_t count = 0; + mutex_lock(s_list_mutex); + count = list_count(s_scheduled_jobs); + mutex_unlock(s_list_mutex); + return count; +} + +void cron_service_wakeup(void) { + prv_timer_callback(NULL); +} + +// --------------------------------------------------------------------------------------- +// The brains. +typedef enum { + CronAssignMode_LocalEpoch, // 'any' uses local epoch's value + CronAssignMode_Zero, // 'any' uses 0 +} CronAssignMode; + +// Indices for the access arrays +#define CRON_INDEX_YEAR 0 +#define CRON_INDEX_MONTH 1 +#define CRON_INDEX_DAY 2 +#define CRON_INDEX_HOUR 3 +#define CRON_INDEX_MIN 4 +#define CRON_INDEX_SEC 5 +#define CRON_INDEX_COUNT 6 + +#define CRON_GENERIC_ANY (-1) +#define CRON_YEAR_ANY (-1) +#define CRON_SECOND_ANY (-1) + +// If the 'working' time is ahead of local epoch, we return 1. If behind, we return -1. +// Otherwise, return 0. +static int prv_future_past_direction(int **dest_arr, const int *curr_arr) { + // Iterate from highest order to lowest. + for (int i = 0; i < CRON_INDEX_COUNT; i++) { + if (*(dest_arr[i]) > curr_arr[i]) { + // In future + return 1; + } else if (*(dest_arr[i]) < curr_arr[i]) { + // In past + return -1; + } + } + return 0; +} + +// Increase the day in `cron_tm` to fit into the wday set in `cron`. +// This doesn't take mday into account because that's way too hard and we won't need it. +static bool prv_adjust_for_wday_spec(const CronJob *cron, struct tm *cron_tm) { + // If we're allowing any wday, we're not adjusting. + if (cron->wday == WDAY_ANY || cron->wday == 0) { + return false; + } + + // Keep track of whether we've adjusted or not. + bool adjusted = false; + + // We need to update cron_tm's tm_wday for proper checking. + cron_tm->tm_mday += 1; // Adjustment because struct tm has mday 1-indexed for whatever reason + mktime(cron_tm); + cron_tm->tm_mday -= 1; + // We have 1 week to find a fitting date + for (int l = 0; l < DAYS_PER_WEEK; l++) { + if (cron->wday & (1 << cron_tm->tm_wday)) { + break; + } + // Advance the day. + cron_tm->tm_mday++; + cron_tm->tm_wday = (cron_tm->tm_wday + 1) % DAYS_PER_WEEK; + adjusted = true; + } + return adjusted; +} + +static time_t prv_get_execute_time_from_epoch(const CronJob *job, time_t local_epoch) { + struct tm current_tm; + // We work off of each element, so we need a struct tm. + localtime_r(&local_epoch, ¤t_tm); + + // Adjust to be zero-indexed + current_tm.tm_mday -= 1; + + // If the job isn't allowed to fire instantly, we're going to force the current second to be + // 1, and the destination second to be 0. This works because it means we cannot use the current + // time as-is, but it will not influence the other fields more than necessary. + if (!job->may_be_instant) { + current_tm.tm_sec = 1; + } + // Cron tm is based on the current tm + struct tm cron_tm = current_tm; + // Don't listen to this stuff (yet) + cron_tm.tm_gmtoff = 0; + cron_tm.tm_isdst = 0; + + // Access everything as arrays because it's way easier that way. + int *dest_arr[CRON_INDEX_COUNT] = { + &cron_tm.tm_year, + &cron_tm.tm_mon, + &cron_tm.tm_mday, + &cron_tm.tm_hour, + &cron_tm.tm_min, + &cron_tm.tm_sec, + }; + const int curr_arr[CRON_INDEX_COUNT] = { + current_tm.tm_year, + current_tm.tm_mon, + current_tm.tm_mday, + current_tm.tm_hour, + current_tm.tm_min, + current_tm.tm_sec, + }; + const int spec_arr[CRON_INDEX_COUNT] = { + CRON_YEAR_ANY, // year should always default + job->month, + job->mday, + job->hour, + job->minute, + // If can be instant, second should default. + // If it can't, use 0 because it's less than 1. + job->may_be_instant ? CRON_SECOND_ANY : 0, + }; + +/* +This is where the actual date finding is done. Essentially, we start with setting the result to +the local epoch, and modify from there. + +We iterate over the fields from most significant to least significant. The reasoning for this is +that we will only know how to properly adjust a less significant field based on the value of the +more significant fields. + +When a field in the spec is marked as ANY (-1), we need to decide what to put in the result: + - If all values so far are still the same as the local epoch, we will use the local epoch's + value. + - Otherwise, the value stored will be 0, because the result is in the future, so a value of 0 + will definitely be the soonest time that matches. + +Now, if the result is behind the local epoch, we step through higher order fields for a field +that was not specified. When we find one, we increase the value by 1. Since this is a higher +order field, this is guaranteed to put the result ahead of the local epoch. +*/ + + // 'any' assignment defaults to using the local epoch's values. + CronAssignMode assign_mode = CronAssignMode_LocalEpoch; + // Iterate over all the fields + for (int i = CRON_INDEX_YEAR; i < CRON_INDEX_COUNT; i++) { + // If the spec had an 'any': + if (spec_arr[i] <= CRON_GENERIC_ANY) { + switch (assign_mode) { + case CronAssignMode_LocalEpoch: + // value will be the local epoch's value, we don't need to change anything + break; + case CronAssignMode_Zero: + // value will be set to 0 + *(dest_arr[i]) = 0; + break; + } + } else { // Otherwise, use the spec's value. + *(dest_arr[i]) = spec_arr[i]; + } + + if (assign_mode == CronAssignMode_LocalEpoch) { + // If we haven't started adjusting things yet, we need to do checking. + + const int direction = prv_future_past_direction(dest_arr, curr_arr); + if (direction < 0) { + // If the target is _behind_ the current time, we need to increase a higher order field. + + // Step from next highest all the way up to a year. We adjust the least significant field + // that is more significant than the current field, and is unspec'd. + for (int l = i - 1; l >= CRON_INDEX_YEAR; l--) { + // If the field isn't set in the spec, increasing it by 1 will put us back in the + // future. + if (spec_arr[l] <= CRON_GENERIC_ANY) { + *(dest_arr[l]) += 1; + break; + } + } + } + if (direction != 0) { + // The target is now ahead of the current time, the rest of the unspec'd fields + // should be 0. + assign_mode = CronAssignMode_Zero; + } + } + } + + // Increase the day until we fit into the `wday` spec. + if (prv_adjust_for_wday_spec(job, &cron_tm)) { + // If the day has been adjusted, we need to re-set hour+minute+second. + // Since we are definitely in the future on an adjustment, fields with 'any' should be + // set to 0, otherwise set to the spec value. + cron_tm.tm_hour = MAX(job->hour, 0); + cron_tm.tm_min = MAX(job->minute, 0); + // Second is always 0 when we're in the future. + cron_tm.tm_sec = 0; + } + + // Adjust back to 1-indexed + cron_tm.tm_mday += 1; + + // Decide the DSTny (Adjust for DST transitions) + cron_tm.tm_gmtoff = time_get_gmtoffset(); + cron_tm.tm_isdst = 0; // We'll do the DST adjust ourselves + time_t t = mktime(&cron_tm); + + // Apply offset seconds + t += job->offset_seconds; + + if (time_get_isdst(t)) { + t -= time_get_dstoffset(); + if (!time_get_isdst(t)) { + // We're in the hole where DST starts. + // We want holed alarms to fire instantly, so set time to DST start time. + t = time_get_dst_start(); + } + } + // We could be in the overlap where DST ends, but we don't actually care about it. + // Why, you ask? This gives us the 'first' matching time if we ignore it. + // So 1:30 will give us the first 1:30, not the second one. + // Yes it's arbitrary. Yes it's confusing. But that's timekeeping and DST for you. + + return t; +} + +time_t cron_job_get_execute_time_from_epoch(const CronJob *job, time_t local_epoch) { + time_t t = prv_get_execute_time_from_epoch(job, local_epoch); + + if (job->offset_seconds != 0) { + time_t t_last = t; + time_t offset_epoch = local_epoch; + while (true) { + const time_t t_delta = (t - local_epoch) * ((job->offset_seconds > 0) ? 1 : -1); + if ((job->may_be_instant ? (t_delta <= 0) : (t_delta < 0))) { + break; + } + // Offset seconds is positive => Applying a positive offset seconds could result in a trigger + // time after the nearest trigger time, find and check the previous time. + // Offset seconds is negative => Applying a negative offset seconds resulted in a time before + // local_epoch, calculate the next time. + t_last = t; + offset_epoch -= job->offset_seconds; + const time_t rv = prv_get_execute_time_from_epoch(job, offset_epoch); + t = rv < local_epoch ? t : rv; + if (job->offset_seconds > 0 && t == t_last) { + break; + } + } + } + + return t; +} + + +time_t cron_job_get_execute_time(const CronJob *job) { + return cron_job_get_execute_time_from_epoch(job, rtc_get_time()); +} diff --git a/src/fw/services/cron/wscript_build b/src/fw/services/cron/wscript_build new file mode 100644 index 0000000000..cf934732c3 --- /dev/null +++ b/src/fw/services/cron/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_cron', +) +bld.env.SERVICES.append('services_cron') diff --git a/src/fw/services/data_logging/Kconfig b/src/fw/services/data_logging/Kconfig new file mode 100644 index 0000000000..48744b5739 --- /dev/null +++ b/src/fw/services/data_logging/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_DATA_LOGGING + bool "Data logging" + help + Data logging (DLS) service. + +if SERVICE_DATA_LOGGING + +module = SERVICE_DATA_LOGGING +module-str = Data logging +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/data_logging/dls_command.c b/src/fw/services/data_logging/dls_command.c similarity index 75% rename from src/fw/services/normal/data_logging/dls_command.c rename to src/fw/services/data_logging/dls_command.c index e17ba71f37..081a8025b0 100644 --- a/src/fw/services/normal/data_logging/dls_command.c +++ b/src/fw/services/data_logging/dls_command.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "data_logging_service.h" -#include "dls_list.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/dls_list.h" #include "console/prompt.h" diff --git a/src/fw/services/normal/data_logging/dls_endpoint.c b/src/fw/services/data_logging/dls_endpoint.c similarity index 82% rename from src/fw/services/normal/data_logging/dls_endpoint.c rename to src/fw/services/data_logging/dls_endpoint.c index aaaa7f9dc6..6b078ad496 100644 --- a/src/fw/services/normal/data_logging/dls_endpoint.c +++ b/src/fw/services/data_logging/dls_endpoint.c @@ -1,30 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dls_private.h" -#include "dls_endpoint.h" -#include "dls_list.h" -#include "dls_storage.h" - -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/system_task.h" -#include "services/common/new_timer/new_timer.h" -#include "services/normal/data_logging/data_logging_service.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/data_logging/dls_private.h" +#include "pbl/services/data_logging/dls_endpoint.h" +#include "pbl/services/data_logging/dls_list.h" +#include "pbl/services/data_logging/dls_storage.h" + +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/data_logging/data_logging_service.h" #include "kernel/pbl_malloc.h" #include "system/logging.h" #include "system/passert.h" @@ -37,6 +24,8 @@ #include "FreeRTOS.h" #include "timers.h" +PBL_LOG_MODULE_DECLARE(service_data_logging, CONFIG_SERVICE_DATA_LOGGING_LOG_LEVEL); + typedef struct { ListNode list_node; DataLoggingSession *session; @@ -76,6 +65,14 @@ static const uint16_t ENDPOINT_ID_DATA_LOGGING = 0x1a7a; static const uint8_t MAX_NACK_COUNT = 20; +// If we don't have a PRF -- or we are in PRF, or CoreApp is confused about +// whether we have a PRF or not -- then CoreApp might decide not to talk to +// us, and will NACK our DLS open requests. Fair enough, I guess, but if +// that's what's happening, then we shouldn't waste our battery continually +// trying to reopen the session. +static const uint8_t MAX_UNEXPECTED_NACK_COUNT = 20; +static uint8_t s_unexpected_nacks = 0; + static void reschedule_ack_timeout(void); static void update_session_state(DataLoggingSession *session, DataLoggingSessionCommState new_state, @@ -124,7 +121,7 @@ static bool check_ack_timeout_for_session(DataLoggingSession *session, void *dat RtcTicks *current_ticks = (RtcTicks*) data; if (session->comm.ack_timeout != 0 && session->comm.ack_timeout <= *current_ticks) { - PBL_LOG(LOG_LEVEL_DEBUG, "session %"PRIu8" timeout", session->comm.session_id); + PBL_LOG_DBG("session %"PRIu8" timeout", session->comm.session_id); // Send timeout msg from system task because it could take a while and also require // more stack space than provided by the timer task. @@ -202,25 +199,25 @@ static void dls_endpoint_print_message(uint8_t *message, int num_bytes) { case DataLoggingEndpointCmdClose: { DataLoggingCloseSessionMessage *msg = (DataLoggingCloseSessionMessage *)message; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Closing session %d", msg->session_id); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Closing session %d", msg->session_id); break; } case DataLoggingEndpointCmdOpen: { DataLoggingOpenSessionMessage *msg = (DataLoggingOpenSessionMessage *)message; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Opening session %u with tag %"PRIu32", type %u, size %hu", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Opening session %u with tag %"PRIu32", type %u, size %hu", msg->session_id, msg->logging_session_tag, msg->data_item_type, msg->data_item_size); break; } case DataLoggingEndpointCmdData: { DataLoggingSendDataMessage *msg = (DataLoggingSendDataMessage *)message; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Sending data with session_id %"PRIu8", items remaining %"PRIu32", crc 0x%"PRIx32", num_bytes %d", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Sending data with session_id %"PRIu8", items remaining %"PRIu32", crc 0x%"PRIx32", num_bytes %d", msg->session_id, msg->items_left_hereafter, msg->crc32, num_bytes); break; } default: - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Message type 0x%x not recognized", message[0]); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Message type 0x%x not recognized", message[0]); } } @@ -296,9 +293,6 @@ bool dls_endpoint_send_data(DataLoggingSession *logging_session, const uint8_t * return false; } - analytics_inc(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_ENDPOINT_SENDS, - AnalyticsClient_System); - const DataLoggingSendDataMessage header = (const DataLoggingSendDataMessage) { .command = DataLoggingEndpointCmdData, .session_id = logging_session->comm.session_id, @@ -318,32 +312,29 @@ bool dls_endpoint_send_data(DataLoggingSession *logging_session, const uint8_t * mutex_unlock(s_endpoint_data.mutex); - unsigned int data_buffer_length = sizeof(DataLoggingSendDataMessage) + num_bytes; - if (!uuid_is_system(&logging_session->app_uuid)) { - analytics_inc_for_uuid(ANALYTICS_APP_METRIC_LOG_OUT_COUNT, &logging_session->app_uuid); - analytics_add_for_uuid(ANALYTICS_APP_METRIC_LOG_BYTE_OUT_COUNT, data_buffer_length, &logging_session->app_uuid); - } return true; } static void prv_dls_endpoint_handle_ack(uint8_t session_id) { DataLoggingSession *session = dls_list_find_by_session_id(session_id); if (session == NULL) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_WARNING, "Received ack for non-existent session id: %"PRIu8, session_id); + PBL_LOG_D_WRN(LOG_DOMAIN_DATA_LOGGING, "Received ack for non-existent session id: %"PRIu8, session_id); return; } mutex_lock(s_endpoint_data.mutex); - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Received ACK for id: %"PRIu8" state: %u", session->comm.session_id, session->comm.state); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Received ACK for id: %"PRIu8" state: %u", session->comm.session_id, session->comm.state); switch (session->comm.state) { case DataLoggingSessionCommStateIdle: - PBL_LOG(LOG_LEVEL_ERROR, "Unexpected ACK"); + PBL_LOG_ERR("Unexpected ACK"); break; case DataLoggingSessionCommStateOpening: update_session_state(session, DataLoggingSessionCommStateIdle, true /*reschedule*/); - break; + mutex_unlock(s_endpoint_data.mutex); + dls_private_send_session(session, true); + return; case DataLoggingSessionCommStateSending: session->comm.nack_count = 0; update_session_state(session, DataLoggingSessionCommStateIdle, true /*reschedule*/); @@ -363,11 +354,11 @@ static void prv_dls_endpoint_handle_ack(uint8_t session_id) { } static void prv_dls_endpoint_handle_nack(uint8_t session_id) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Received NACK for id: %"PRIu8, session_id); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Received NACK for id: %"PRIu8, session_id); DataLoggingSession *logging_session = dls_list_find_by_session_id(session_id); if (!logging_session) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_WARNING, "Received nack for non-existent session id: %"PRIu8, session_id); + PBL_LOG_D_WRN(LOG_DOMAIN_DATA_LOGGING, "Received nack for non-existent session id: %"PRIu8, session_id); return; } @@ -376,15 +367,20 @@ static void prv_dls_endpoint_handle_nack(uint8_t session_id) { case DataLoggingSessionCommStateIdle: case DataLoggingSessionCommStateOpening: //Currently, these messages never get NACK'd - PBL_LOG(LOG_LEVEL_ERROR, "Unexpected NACK"); + PBL_LOG_ERR("Unexpected NACK"); + if (s_unexpected_nacks < MAX_UNEXPECTED_NACK_COUNT) { + s_unexpected_nacks++; + if (s_unexpected_nacks == MAX_UNEXPECTED_NACK_COUNT) { + PBL_LOG_ERR("I give up; I will not try to autonomously repair sessions anymore"); + } + } break; case DataLoggingSessionCommStateSending: //Maybe queue a resend logging_session->comm.num_bytes_pending = 0; if (++logging_session->comm.nack_count > MAX_NACK_COUNT) { - PBL_LOG(LOG_LEVEL_ERROR, "Too many nacks. Flushing..."); + PBL_LOG_ERR("Too many nacks. Flushing..."); dls_storage_consume(logging_session, logging_session->storage.num_bytes); - analytics_inc(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_FLUSH_COUNT, AnalyticsClient_System); logging_session->comm.nack_count = 0; } break; @@ -395,7 +391,9 @@ static void prv_dls_endpoint_handle_nack(uint8_t session_id) { mutex_unlock(s_endpoint_data.mutex); // reopen the session that was NACK'ed - dls_endpoint_open_session(logging_session); + if (s_unexpected_nacks < MAX_UNEXPECTED_NACK_COUNT) { + dls_endpoint_open_session(logging_session); + } } //! System task callback executed which reopens the next session in the list built up by report_cmd_system_task_cb @@ -413,7 +411,7 @@ static void prv_reopen_next_session_system_task_cb(void* data) { uuid_equal(&entry->app_uuid, &entry->session->app_uuid) && entry->timestamp == entry->session->session_created_timestamp && entry->tag == entry->session->tag) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Reopening session %d", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Reopening session %d", entry->session->comm.session_id); success = (dls_endpoint_open_session(entry->session) && dls_private_send_session(entry->session, false)); @@ -436,7 +434,7 @@ static void prv_reopen_next_session_system_task_cb(void* data) { } else { s_endpoint_data.report_in_progress = false; // If we failed, give up on the remaining ones - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Aborting all remaining open requests"); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Aborting all remaining open requests"); while (new_head) { DataLoggingReopenEntry *entry = new_head; new_head = (DataLoggingReopenEntry *)list_pop_head((ListNode *)new_head); @@ -456,7 +454,7 @@ static bool dls_endpoint_add_reopen_sessions_cb(DataLoggingSession *session, voi .timestamp = session->session_created_timestamp, .tag = session->tag, }; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "adding session %d to reopen list", session->comm.session_id); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "adding session %d to reopen list", session->comm.session_id); *head_ptr = (DataLoggingReopenEntry *)list_insert_before((ListNode *)(*head_ptr), &entry->list_node); return true; } @@ -467,7 +465,7 @@ static void prv_handle_report_cmd(const uint8_t *session_ids, size_t num_session DataLoggingSession *logging_session = dls_list_find_by_session_id(session_id); - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Phone reported session %u opened", session_id); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Phone reported session %u opened", session_id); // If the phone thinks we're open and we're not, send a close message. if (logging_session == NULL) { @@ -486,7 +484,7 @@ static void prv_handle_report_cmd(const uint8_t *session_ids, size_t num_session //! Empty a session by session id static void prv_empty_session(uint8_t session_id) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Phone requested empty of session %u", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Phone requested empty of session %u", session_id); DataLoggingSession *logging_session = dls_list_find_by_session_id(session_id); if (logging_session) { @@ -504,7 +502,7 @@ void data_logging_protocol_msg_callback(CommSession *session, const uint8_t *dat // All commands from the phone have their high bit set. if ((command & ~DLS_ENDPOINT_CMD_MASK) == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid data logging endpoint command 0x%x", command); + PBL_LOG_ERR("Invalid data logging endpoint command 0x%x", command); // TODO: send some error code back? return; } @@ -520,7 +518,7 @@ void data_logging_protocol_msg_callback(CommSession *session, const uint8_t *dat case (DataLoggingEndpointCmdReport): if (s_endpoint_data.report_in_progress) { - PBL_LOG(LOG_LEVEL_INFO, "Report already in progress"); + PBL_LOG_INFO("Report already in progress"); } else { s_endpoint_data.report_in_progress = true; prv_handle_report_cmd(&data[1], length); diff --git a/src/fw/services/normal/data_logging/dls_list.c b/src/fw/services/data_logging/dls_list.c similarity index 91% rename from src/fw/services/normal/data_logging/dls_list.c rename to src/fw/services/data_logging/dls_list.c index 2f0b24a43a..c35ef5c577 100644 --- a/src/fw/services/normal/data_logging/dls_list.c +++ b/src/fw/services/data_logging/dls_list.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dls_list.h" -#include "dls_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/data_logging/dls_list.h" +#include "pbl/services/data_logging/dls_storage.h" #include "drivers/flash.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" #include "process_management/process_manager.h" -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" #include "util/uuid.h" @@ -30,6 +17,8 @@ #include #include +PBL_LOG_MODULE_DECLARE(service_data_logging, CONFIG_SERVICE_DATA_LOGGING_LOG_LEVEL); + static DataLoggingSession *s_logging_sessions; static PebbleRecursiveMutex * s_list_mutex; @@ -75,7 +64,7 @@ bool dls_lock_session(DataLoggingSession *session) { // --------------------------------------------------------------------------------------- // Callback used to free a storage buffer from unprivileged mode. static void prv_free_storage_buffer_cb(void *p) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Freeing buffer storage ptr: %p", p); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Freeing buffer storage ptr: %p", p); task_free(p); } @@ -181,7 +170,7 @@ DataLoggingSession *dls_list_find_active_session(uint32_t tag, const Uuid *app_u void dls_list_remove_session(DataLoggingSession *logging_session) { if (uuid_is_system(&logging_session->app_uuid)) { - PBL_LOG(LOG_LEVEL_WARNING, "Deleting the system data logging session with tag %"PRIu32, + PBL_LOG_WRN("Deleting the system data logging session with tag %"PRIu32, logging_session->tag); } @@ -242,7 +231,7 @@ void dls_list_insert_session(DataLoggingSession *logging_session) { logging_session->next = *iter; *iter = logging_session; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Created session: %p id %"PRIu8 + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Created session: %p id %"PRIu8 " tag %"PRIu32, logging_session, logging_session->comm.session_id, logging_session->tag); mutex_unlock_recursive(s_list_mutex); @@ -287,7 +276,7 @@ DataLoggingSession *dls_list_create_session(uint32_t tag, DataLoggingItemType ty uint32_t num_sessions = prv_get_num_sessions(); if (num_sessions >= DLS_MAX_NUM_SESSIONS) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not allocate additional DataLoggingSession objects"); + PBL_LOG_WRN("Could not allocate additional DataLoggingSession objects"); return NULL; } diff --git a/src/fw/services/normal/data_logging/dls_main.c b/src/fw/services/data_logging/dls_main.c similarity index 78% rename from src/fw/services/normal/data_logging/dls_main.c rename to src/fw/services/data_logging/dls_main.c index 5680fc53b6..48a8f8e5dc 100644 --- a/src/fw/services/normal/data_logging/dls_main.c +++ b/src/fw/services/data_logging/dls_main.c @@ -1,26 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/data_logging.h" +#include "util/list.h" #include "util/uuid.h" -#include "data_logging_service.h" -#include "dls_endpoint.h" -#include "dls_list.h" -#include "dls_storage.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/dls_endpoint.h" +#include "pbl/services/data_logging/dls_list.h" +#include "pbl/services/data_logging/dls_storage.h" #include "comm/bt_lock.h" #include "drivers/flash.h" @@ -30,10 +18,10 @@ #include "kernel/pbl_malloc.h" #include "process_management/pebble_process_md.h" #include "process_management/process_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" #include "syscall/syscall.h" #include "system/logging.h" #include "os/mutex.h" @@ -44,6 +32,8 @@ #include #include +PBL_LOG_MODULE_DEFINE(service_data_logging, CONFIG_SERVICE_DATA_LOGGING_LOG_LEVEL); + static bool s_initialized = false; static bool s_sends_enabled_pp = true; static bool s_sends_enabled_run_level = true; @@ -55,17 +45,107 @@ static bool prv_sends_enabled(void) { return (s_sends_enabled_run_level && s_sends_enabled_pp); } -// ---------------------------------------------------------------------------------------- -//! Wrapper for data_logging_send_session that makes it usable in dls_list_for_each_session -//! @param empty_all_data A bool that indicates if the session should be force emptied -static bool prv_send_session(DataLoggingSession *logging_session, void *empty_all_data) { - dls_private_send_session(logging_session, (bool) empty_all_data); +// Snapshot the session list, then chain one send per system task callback. +// Iterating + sending inline under s_list_mutex can stall KernelBG for +// ~500ms per session (the comm_session_send_buffer_begin_write timeout), +// tripping the watchdog once the pool fills. +typedef struct { + ListNode list_node; + DataLoggingSession *session; + Uuid app_uuid; + time_t timestamp; + uint32_t tag; + bool empty; +} DataLoggingSendEntry; + +typedef struct { + DataLoggingSendEntry *head; + bool empty; +} DataLoggingSendBuildCtx; + +// Touched only on KernelBG (the system task), so no synchronization needed. +static bool s_flush_in_progress = false; + +static void prv_send_next_session_system_task_cb(void *data); + +static bool prv_add_send_entry_cb(DataLoggingSession *session, void *data) { + DataLoggingSendBuildCtx *ctx = (DataLoggingSendBuildCtx *)data; + DataLoggingSendEntry *entry = kernel_malloc(sizeof(DataLoggingSendEntry)); + if (!entry) { + // OOM: send what we already queued; the next 5-minute tick will retry. + PBL_LOG_WRN("OOM building DLS flush list; truncating pass"); + return false; + } + *entry = (DataLoggingSendEntry) { + .session = session, + .app_uuid = session->app_uuid, + .timestamp = session->session_created_timestamp, + .tag = session->tag, + .empty = ctx->empty, + }; + ctx->head = (DataLoggingSendEntry *)list_insert_before((ListNode *)ctx->head, + &entry->list_node); return true; } +static void prv_drain_send_list(DataLoggingSendEntry *head) { + while (head) { + DataLoggingSendEntry *entry = head; + head = (DataLoggingSendEntry *)list_pop_head((ListNode *)head); + kernel_free(entry); + } +} + +static void prv_send_next_session_system_task_cb(void *data) { + DataLoggingSendEntry *entry = (DataLoggingSendEntry *)data; + if (!entry) { + s_flush_in_progress = false; + return; + } + + DataLoggingSendEntry *new_head = + (DataLoggingSendEntry *)list_pop_head((ListNode *)entry); + + // The session may have been freed between snapshot and now; the identity + // tuple guards against the pointer being reused for a different session. + if (dls_list_is_session_valid(entry->session) && + uuid_equal(&entry->app_uuid, &entry->session->app_uuid) && + entry->timestamp == entry->session->session_created_timestamp && + entry->tag == entry->session->tag) { + dls_private_send_session(entry->session, entry->empty); + } + kernel_free(entry); + + if (!new_head) { + s_flush_in_progress = false; + return; + } + + if (!system_task_add_callback(prv_send_next_session_system_task_cb, new_head)) { + PBL_LOG_WRN("Failed to schedule next DLS flush callback; aborting pass"); + prv_drain_send_list(new_head); + s_flush_in_progress = false; + } +} + //! @param empty_all_data A bool that indicates if the session should be force emptied static void prv_send_all_sessions_system_task_cb(void *empty_all_data) { - dls_list_for_each_session(prv_send_session, (void*) empty_all_data); + if (s_flush_in_progress) { + return; + } + + DataLoggingSendBuildCtx ctx = { + .head = NULL, + .empty = (bool)(uintptr_t)empty_all_data, + }; + dls_list_for_each_session(prv_add_send_entry_cb, &ctx); + + if (!ctx.head) { + return; + } + + s_flush_in_progress = true; + prv_send_next_session_system_task_cb(ctx.head); } @@ -74,7 +154,7 @@ static void prv_send_all_sessions_system_task_cb(void *empty_all_data) { static void prv_check_all_sessions_timer_cb(void *data) { // If sends are not enabled, do nothing if (!prv_sends_enabled()) { - PBL_LOG(LOG_LEVEL_INFO, "Not sending sessions beause sending is disabled"); + PBL_LOG_INFO("Not sending sessions beause sending is disabled"); return; } @@ -84,7 +164,7 @@ static void prv_check_all_sessions_timer_cb(void *data) { // messages. However, occasionally we do want to flush everything out. static int check_counter = 0; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "send all sessions: empty %s connected %s counter %u", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "send all sessions: empty %s connected %s counter %u", bool_to_str(check_counter == 0), bool_to_str(comm_session_get_system_session() != NULL), check_counter); @@ -109,7 +189,7 @@ static RegularTimerInfo prv_check_all_sessions_timer_info = { static void prv_remove_logging_session(DataLoggingSession *data) { DataLoggingSession *logging_session = (DataLoggingSession *)data; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Removing session %d.", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Removing session %d.", logging_session->comm.session_id); dls_endpoint_close_session(logging_session->comm.session_id); @@ -125,7 +205,7 @@ bool dls_private_send_session(DataLoggingSession *logging_session, bool empty) { // If sends are not enabled, ignore if (!prv_sends_enabled()) { - PBL_LOG(LOG_LEVEL_INFO, "Not sending session beause sending is disabled"); + PBL_LOG_INFO("Not sending session beause sending is disabled"); return true; } @@ -137,8 +217,7 @@ bool dls_private_send_session(DataLoggingSession *logging_session, bool empty) { int32_t total_bytes = logging_session->storage.num_bytes; bool inactive = (dls_get_session_status(logging_session) == DataLoggingStatusInactive); - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, - "de-logging session %"PRIu8", tag %"PRIu32 + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "de-logging session %"PRIu8", tag %"PRIu32 " (inactive %s tot_bytes %"PRIu32" empty %s)", logging_session->comm.session_id, logging_session->tag, bool_to_str(inactive), total_bytes, bool_to_str(empty)); @@ -165,11 +244,10 @@ bool dls_private_send_session(DataLoggingSession *logging_session, bool empty) { unsigned int leftover_bytes = read_bytes % logging_session->item_size; if (leftover_bytes) { - PBL_LOG(LOG_LEVEL_ERROR, "leftover bytes in the session. Flushing..."); + PBL_LOG_ERR("leftover bytes in the session. Flushing..."); // remove the number of leftover bytes so we fall back on our feet read_bytes -= leftover_bytes; dls_storage_consume(logging_session, leftover_bytes); - analytics_inc(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_FLUSH_COUNT, AnalyticsClient_System); } success = dls_endpoint_send_data(logging_session, buffer, read_bytes); @@ -258,7 +336,7 @@ static bool prv_inactivate_sessions_each_cb(DataLoggingSession *session, void *d Uuid system_uuid = UUID_SYSTEM; if (!uuid_equal(&session->app_uuid, &system_uuid)) { if (task == session->task) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Inactivating session: %"PRIu8, + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Inactivating session: %"PRIu8, session->comm.session_id); // Free the buffer if it's in kernel heap. If not in kernel heap we are intentionally not @@ -288,7 +366,7 @@ static bool prv_inactivate_sessions_each_cb(DataLoggingSession *session, void *d void dls_send_all_sessions(void) { // If sends are not enabled, do nothing if (!prv_sends_enabled()) { - PBL_LOG(LOG_LEVEL_INFO, "Not sending sessions beause sending is disabled"); + PBL_LOG_INFO("Not sending sessions beause sending is disabled"); return; } system_task_add_callback(prv_send_all_sessions_system_task_cb, (void*) true); @@ -310,11 +388,11 @@ static DataLoggingSession *prv_dls_create(uint32_t tag, DataLoggingItemType item // validate size parameter if (item_size == 0 || (buffered && item_size > DLS_SESSION_MAX_BUFFERED_ITEM_SIZE) || (!buffered && item_size > DLS_ENDPOINT_MAX_PAYLOAD)) { - PBL_LOG(LOG_LEVEL_ERROR, "invalid logging_session item size, %d", item_size); + PBL_LOG_ERR("invalid logging_session item size, %d", item_size); return (NULL); } else if (item_type == DATA_LOGGING_UINT || item_type == DATA_LOGGING_INT) { if (item_size > 4 || item_size == 3) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid data width: integer types can be 1, 2, or 4 bytes"); + PBL_LOG_ERR("Invalid data width: integer types can be 1, 2, or 4 bytes"); return (NULL); } } @@ -388,12 +466,12 @@ DataLoggingSession* dls_create_current_process(uint32_t tag, DataLoggingItemType void dls_finish(DataLoggingSession *logging_session) { PBL_ASSERTN(logging_session != NULL); if (uuid_is_system(&logging_session->app_uuid)) { - PBL_LOG(LOG_LEVEL_WARNING, "Finishing the system data logging session at %p", logging_session); + PBL_LOG_WRN("Finishing the system data logging session at %p", logging_session); } bool is_active = dls_lock_session(logging_session); if (!is_active) { - PBL_LOG(LOG_LEVEL_WARNING, "Tried to close a non-active data logging session"); + PBL_LOG_WRN("Tried to close a non-active data logging session"); return; } @@ -418,7 +496,7 @@ void dls_finish(DataLoggingSession *logging_session) { } if (timeout <= 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Timed out waiting for logging_session to write"); + PBL_LOG_ERR("Timed out waiting for logging_session to write"); } dls_unlock_session(logging_session, true /*inactivate*/); exit: @@ -439,7 +517,7 @@ static void prv_write_all_sessions_to_flash(void *data) { // ---------------------------------------------------------------------------------------- DataLoggingResult dls_log(DataLoggingSession *session, const void* data, uint32_t num_items) { -#if !RELEASE +#ifndef CONFIG_RELEASE // TODO: We should be able to remove this requirement once PBL-23925 is fixed // // Some datalogging code holds the dls_list.c:s_list_mutex while taking the @@ -467,7 +545,7 @@ DataLoggingResult dls_log(DataLoggingSession *session, const void* data, uint32_ return (DATA_LOGGING_CLOSED); } - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "logging %d items of size %d to session %d", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "logging %d items of size %d to session %d", (int)num_items, (int)session->item_size, session->comm.session_id); if (!session->data->buffer_storage) { diff --git a/src/fw/services/normal/data_logging/dls_storage.c b/src/fw/services/data_logging/dls_storage.c similarity index 89% rename from src/fw/services/normal/data_logging/dls_storage.c rename to src/fw/services/data_logging/dls_storage.c index ba768824d9..07d0c72cd6 100644 --- a/src/fw/services/normal/data_logging/dls_storage.c +++ b/src/fw/services/data_logging/dls_storage.c @@ -1,29 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "data_logging_service.h" -#include "dls_storage.h" -#include "dls_list.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/dls_storage.h" +#include "pbl/services/data_logging/dls_list.h" #include "drivers/flash.h" #include "kernel/pbl_malloc.h" #include "kernel/pebble_tasks.h" #include "kernel/util/sleep.h" -#include "services/common/analytics/analytics.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/filesystem/pfs.h" #include "system/logging.h" #include "system/passert.h" #include "util/attributes.h" @@ -34,6 +21,8 @@ #include #include +PBL_LOG_MODULE_DECLARE(service_data_logging, CONFIG_SERVICE_DATA_LOGGING_LOG_LEVEL); + typedef enum { DLS_VERSION_0 = 0x20, @@ -106,10 +95,10 @@ static bool prv_pfs_read(int fd, void *buf, size_t size) { int bytes_read; bytes_read = pfs_read(fd, buf, size); if (bytes_read < S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Err %d while reading", (int)bytes_read); + PBL_LOG_ERR("Err %d while reading", (int)bytes_read); return false; } else if (bytes_read < (int)size) { - PBL_LOG(LOG_LEVEL_ERROR, "Read only %d bytes, expected %d", (int)bytes_read, (int)size); + PBL_LOG_ERR("Read only %d bytes, expected %d", (int)bytes_read, (int)size); return false; } return true; @@ -121,10 +110,10 @@ static bool prv_pfs_read(int fd, void *buf, size_t size) { static bool prv_pfs_write(int fd, void *buf, size_t size) { int bytes_wrote = pfs_write(fd, buf, size); if (bytes_wrote < S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Err %d while writing", (int)bytes_wrote); + PBL_LOG_ERR("Err %d while writing", (int)bytes_wrote); return false; } else if (bytes_wrote < (int)size) { - PBL_LOG(LOG_LEVEL_ERROR, "Wrote only %d bytes, expected %d", (int)bytes_wrote, (int)size); + PBL_LOG_ERR("Wrote only %d bytes, expected %d", (int)bytes_wrote, (int)size); return false; } return true; @@ -137,7 +126,7 @@ static bool prv_pfs_seek(int fd, int offset, FSeekType seek_type) { int result; result = pfs_seek(fd, offset, seek_type); if (result < S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Err %d while seeking", result); + PBL_LOG_ERR("Err %d while seeking", result); return false; } return true; @@ -149,7 +138,7 @@ static bool prv_pfs_seek(int fd, int offset, FSeekType seek_type) { static size_t prv_pfs_get_file_size(int fd) { size_t result = pfs_get_file_size(fd); if (result == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Err getting size"); + PBL_LOG_ERR("Err getting size"); } return result; } @@ -195,7 +184,7 @@ static bool prv_accumulate_size_cb(DataLoggingSession* session, void *data) { static uint32_t prv_get_total_file_system_bytes(void) { uint32_t size = 0; dls_list_for_each_session(prv_accumulate_size_cb, &size); - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Total used space: %d", (int)size); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Total used space: %d", (int)size); return size; } @@ -261,7 +250,7 @@ static bool prv_open_file(DataLoggingSessionStorage *storage, uint8_t op_flags, int fd = pfs_open(name, op_flags, FILE_TYPE_STATIC, size); if (fd < S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not open/create DLS file %s", name); + PBL_LOG_ERR("Could not open/create DLS file %s", name); return false; } @@ -293,7 +282,7 @@ static bool prv_open_file(DataLoggingSessionStorage *storage, uint8_t op_flags, .read_offset = sizeof(hdr) }; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, "Created session-storage: " + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Created session-storage: " "id %"PRIu8", filename: %s, fd: %d, size: %d", session->comm.session_id, name, fd, (int)size); return true; @@ -307,7 +296,7 @@ static void prv_release_session_file(DataLoggingSession *session) { status_t status = pfs_close(session->storage.fd); if (status != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Error %d closing file for session %d", (int)status, + PBL_LOG_ERR("Error %d closing file for session %d", (int)status, session->comm.session_id); } session->storage.fd = DLS_INVALID_FILE; @@ -464,15 +453,11 @@ static bool prv_realloc_storage(DataLoggingSession *session, uint32_t new_size) bool success; uint8_t *tmp_buf = NULL; - // Record in metrics - analytics_inc(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_REALLOC_COUNT, AnalyticsClient_System); - // Must be called with the file closed PBL_ASSERTN(session->storage.fd == DLS_INVALID_FILE); - PBL_LOG(LOG_LEVEL_INFO, "Compacting storage for session %d", session->comm.session_id); - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, - "Before compaction: num_bytes: %"PRIu32", write_offset:%"PRIu32, + PBL_LOG_INFO("Compacting storage for session %d", session->comm.session_id); + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Before compaction: num_bytes: %"PRIu32", write_offset:%"PRIu32, session->storage.num_bytes, session->storage.write_offset); // Init a storage struct and create a new file for the compacted data @@ -482,7 +467,7 @@ static bool prv_realloc_storage(DataLoggingSession *session, uint32_t new_size) success = prv_open_file(&new_storage, OP_FLAG_OVERWRITE | OP_FLAG_READ, new_size, session); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not create temporary file to migrate storage file"); + PBL_LOG_ERR("Could not create temporary file to migrate storage file"); goto exit; } @@ -501,7 +486,7 @@ static bool prv_realloc_storage(DataLoggingSession *session, uint32_t new_size) break; } if (max_chunk_size < 256) { - PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory for reallocation"); + PBL_LOG_ERR("Not enough memory for reallocation"); goto exit; } max_chunk_size /= 2; @@ -540,8 +525,7 @@ static bool prv_realloc_storage(DataLoggingSession *session, uint32_t new_size) // Plug in the new storage info into the session session->storage = new_storage; - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, - "After compaction: size: %d, num_bytes: %d, write_offset:%d", + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "After compaction: size: %d, num_bytes: %d, write_offset:%d", (int)new_size, (int)session->storage.num_bytes, (int)session->storage.write_offset); success = true; @@ -552,7 +536,7 @@ static bool prv_realloc_storage(DataLoggingSession *session, uint32_t new_size) } if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Migration failed of session file %d", session->comm.session_id); + PBL_LOG_ERR("Migration failed of session file %d", session->comm.session_id); dls_storage_delete_logging_storage(session); } return success; @@ -576,7 +560,7 @@ void dls_storage_delete_logging_storage(DataLoggingSession *session) { prv_get_filename(name, session); status_t status = pfs_remove(name); if (status != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Error %d removing file", (int) status); + PBL_LOG_ERR("Error %d removing file", (int) status); } // Clear out storage info @@ -606,7 +590,7 @@ bool dls_storage_write_data(DataLoggingSession *session, const void *data, uint3 } if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Nuking storage for session %d", session->comm.session_id); + PBL_LOG_ERR("Nuking storage for session %d", session->comm.session_id); dls_storage_delete_logging_storage(session); } return success; @@ -672,7 +656,7 @@ bool dls_storage_write_session(DataLoggingSession *session) { } if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Nuking storage for session %d", session->comm.session_id); + PBL_LOG_ERR("Nuking storage for session %d", session->comm.session_id); dls_storage_delete_logging_storage(session); } return success; @@ -765,7 +749,7 @@ int32_t dls_storage_read(DataLoggingSession *logging_session, uint8_t *buffer, i } if (last_whole_items_read_bytes < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Nuking storage for session %d", logging_session->comm.session_id); + PBL_LOG_ERR("Nuking storage for session %d", logging_session->comm.session_id); dls_storage_delete_logging_storage(logging_session); } @@ -826,7 +810,7 @@ int32_t dls_storage_consume(DataLoggingSession *logging_session, int32_t num_byt } if (chunk_hdr.num_bytes > num_bytes) { // Somehow the caller tried to consume less than they read? - PBL_LOG(LOG_LEVEL_WARNING, "Read/consume out of sync"); + PBL_LOG_WRN("Read/consume out of sync"); goto exit; } // Invalidate the chunk, now that we have consumed it @@ -841,7 +825,7 @@ int32_t dls_storage_consume(DataLoggingSession *logging_session, int32_t num_byt goto exit; } if (logging_session->storage.num_bytes < chunk_hdr.num_bytes) { - PBL_LOG(LOG_LEVEL_ERROR, "Inconsistent tracking of num_bytes"); + PBL_LOG_ERR("Inconsistent tracking of num_bytes"); consumed_bytes = -1; // error goto exit; } @@ -854,8 +838,7 @@ int32_t dls_storage_consume(DataLoggingSession *logging_session, int32_t num_byt exit: if (consumed_bytes > 0) { - PBL_LOG_D(LOG_DOMAIN_DATA_LOGGING, LOG_LEVEL_DEBUG, - "Consumed %d bytes from session %d", (int)consumed_bytes, + PBL_LOG_D_DBG(LOG_DOMAIN_DATA_LOGGING, "Consumed %d bytes from session %d", (int)consumed_bytes, logging_session->comm.session_id); } @@ -864,7 +847,7 @@ int32_t dls_storage_consume(DataLoggingSession *logging_session, int32_t num_byt } if (consumed_bytes < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Nuking storage for session %d", logging_session->comm.session_id); + PBL_LOG_ERR("Nuking storage for session %d", logging_session->comm.session_id); dls_storage_delete_logging_storage(logging_session); } return consumed_bytes; @@ -893,7 +876,7 @@ void dls_storage_rebuild(void) { int fd = pfs_open(head->name, OP_FLAG_READ | OP_FLAG_WRITE, FILE_TYPE_STATIC, DLS_FILE_INIT_SIZE_BYTES); if (fd < S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Error %d opening file %s", fd, head->name); + PBL_LOG_ERR("Error %d opening file %s", fd, head->name); pfs_remove(head->name); goto bad_session; } @@ -922,7 +905,7 @@ void dls_storage_rebuild(void) { // Make sure the filename is what we expect prv_get_filename(name, session); if (strncmp(name, head->name, DLS_FILE_NAME_MAX_LEN)) { - PBL_LOG(LOG_LEVEL_ERROR, "Expected name of %s, got %s", head->name, name); + PBL_LOG_ERR("Expected name of %s, got %s", head->name, name); pfs_remove(head->name); goto bad_session; } @@ -943,8 +926,7 @@ void dls_storage_rebuild(void) { goto bad_session; } - PBL_LOG(LOG_LEVEL_INFO, - "Restored session %"PRIu8 + PBL_LOG_INFO("Restored session %"PRIu8 " num_bytes:%"PRIu32", read_offset:%"PRIu32", write_offset:%"PRIu32, session->comm.session_id, session->storage.num_bytes, session->storage.read_offset, session->storage.write_offset); @@ -972,7 +954,7 @@ void dls_storage_rebuild(void) { head = (PFSFileListEntry *)head->list_node.next; } - PBL_LOG(LOG_LEVEL_INFO, "Restored %d sessions. Total %"PRIu32" bytes allocated", + PBL_LOG_INFO("Restored %d sessions. Total %"PRIu32" bytes allocated", num_sessions_restored, prv_get_total_file_system_bytes()); // Free the directory list @@ -981,19 +963,3 @@ void dls_storage_rebuild(void) { // No longer in initialization. From now on, only KernelBG can use the session storage calls. s_initializing_storage = false; } - - -// ------------------------------------------------------------------------------------------------- -// Analytics -static bool prv_max_numbytes_cb(DataLoggingSession* session, void *data) { - uint32_t *max_bytes = (uint32_t *)data; - *max_bytes = MAX(*max_bytes, session->storage.num_bytes); - return true; -} - -void analytics_external_collect_dls_stats(void) { - uint32_t max_bytes = 0; - dls_list_for_each_session(prv_max_numbytes_cb, &max_bytes); - analytics_set(ANALYTICS_DEVICE_METRIC_DATA_LOGGING_MAX_SPOOLED_BYTES, max_bytes, - AnalyticsClient_System); -} diff --git a/src/fw/services/data_logging/dls_syscalls.c b/src/fw/services/data_logging/dls_syscalls.c new file mode 100644 index 0000000000..f4bffc1976 --- /dev/null +++ b/src/fw/services/data_logging/dls_syscalls.c @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/dls_private.h" + +#include "kernel/memory_layout.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" + +#include + +PBL_LOG_MODULE_DECLARE(service_data_logging, CONFIG_SERVICE_DATA_LOGGING_LOG_LEVEL); + +DEFINE_SYSCALL(DataLoggingSessionRef, sys_data_logging_create, uint32_t tag, + DataLoggingItemType item_type, uint16_t item_size, + void *buffer, bool resume) { + // Apps allocate the circular buffer themselves in their own heap and hand the + // pointer to the kernel via this syscall. Without validation, a malicious app + // could point at kernel memory and turn dls_log into an arbitrary kernel write. + if (PRIVILEGE_WAS_ELEVATED && buffer != NULL) { + syscall_assert_userspace_buffer(buffer, DLS_SESSION_MIN_BUFFER_SIZE); + } + return dls_create_current_process(tag, item_type, item_size, buffer, resume); +} + +DEFINE_SYSCALL(void, sys_data_logging_finish, DataLoggingSessionRef session_ref) { + // dls_is_session_valid() (below) walks the kernel-owned s_logging_sessions + // list to confirm the session pointer is one we handed out, so a forged + // session_ref simply returns invalid and never reaches dls_finish(). + DataLoggingSession* session = (DataLoggingSession*)session_ref; + + if (!dls_is_session_valid(session)) { + PBL_LOG_WRN("finish: Invalid session %p", session); + return; // TODO: Return error code? + } + + dls_finish(session); +} + +DEFINE_SYSCALL(DataLoggingResult, sys_data_logging_log, + DataLoggingSessionRef session_ref, void* data, uint32_t num_items) { + DataLoggingSession* session = (DataLoggingSession*)session_ref; + + if (!dls_is_session_valid(session)) { + PBL_LOG_WRN("log: Invalid session %p", session); + return DATA_LOGGING_INVALID_PARAMS; + } + if (data == NULL) { + PBL_LOG_WRN("log: NULL data pointer"); + return DATA_LOGGING_INVALID_PARAMS; + } + + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(data, num_items * session->item_size); + } + + return dls_log(session, data, num_items); +} diff --git a/src/fw/services/data_logging/wscript_build b/src/fw/services/data_logging/wscript_build new file mode 100644 index 0000000000..7f22c4f22c --- /dev/null +++ b/src/fw/services/data_logging/wscript_build @@ -0,0 +1,19 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'dls_command.c', + 'dls_endpoint.c', + 'dls_list.c', + 'dls_main.c', + 'dls_storage.c', + 'dls_syscalls.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_data_logging', +) + +bld.env.SERVICES.append('services_data_logging') diff --git a/src/fw/services/debounced_connection_service/Kconfig b/src/fw/services/debounced_connection_service/Kconfig new file mode 100644 index 0000000000..1ecfda375a --- /dev/null +++ b/src/fw/services/debounced_connection_service/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_DEBOUNCED_CONNECTION_SERVICE + bool "Debounced connection service" + help + Debounced connection state events. diff --git a/src/fw/services/debounced_connection_service/service.c b/src/fw/services/debounced_connection_service/service.c new file mode 100644 index 0000000000..03d89be896 --- /dev/null +++ b/src/fw/services/debounced_connection_service/service.c @@ -0,0 +1,111 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/debounced_connection_service.h" + +#include "pbl/services/comm_session/session.h" +#include "pbl/services/regular_timer.h" +#include "syscall/syscall_internal.h" + +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/vibes/vibe_client.h" +#include "pbl/services/vibes/vibe_score.h" +#endif + +#include "system/logging.h" + +//! This module is responsible for propagating debounced connection events. +//! Connection events are passed through right away to subscribers but +//! disconnection events are only passed through if a re-connection did not +//! occur within a small window of time. This way, short disconnect periods +//! can go unnoticed to the end consumer resulting in a better perception of +//! connection reliability +//! +//! At the moment, the connections this module tracks are: +//! + Watch <-> Mobile App / PebbleKit JS +//! + Watch <-> third-party App using PebbleKit + +typedef enum { + MobileAppDebounce = 0, + PebbleKitDebounce, + NumConnectionsToDebounce, +} DebounceConnection; + +static RegularTimerInfo s_debounce_timers[NumConnectionsToDebounce]; +static bool s_debounced_state_is_connected[NumConnectionsToDebounce]; + +static void prv_put_debounced_connection_event(DebounceConnection conn_id) { + PebbleEvent event = { + .type = PEBBLE_BT_CONNECTION_DEBOUNCED_EVENT, + .bluetooth.comm_session_event.is_open = s_debounced_state_is_connected[conn_id], + .bluetooth.comm_session_event.is_system = (conn_id == MobileAppDebounce), + }; + event_put(&event); +} + +static void prv_handle_disconnection_debounced(void *data) { + DebounceConnection conn_id = (DebounceConnection)data; + s_debounced_state_is_connected[conn_id] = false; + prv_put_debounced_connection_event(conn_id); +#ifndef CONFIG_RECOVERY_FW + if (alerts_should_vibrate_for_type(AlertOther)) { + uint32_t vibe_id = vibe_score_info_get_resource_id( + alerts_preferences_get_vibe_score_for_client(VibeClient_OnDisconnect)); + VibeScore *score = vibe_score_create_with_resource_system(0, vibe_id); + if (score) { + vibe_score_do_vibe(score); + vibe_score_destroy(score); + } + } +#endif + regular_timer_remove_callback(&s_debounce_timers[conn_id]); +} + +void debounced_connection_service_init(void) { + for (int i = 0; i < NumConnectionsToDebounce; i++) { + s_debounce_timers[i].cb = prv_handle_disconnection_debounced; + s_debounce_timers[i].cb_data = (void *)(uintptr_t)i; + } + + // initial state of the connections + s_debounced_state_is_connected[MobileAppDebounce] = (comm_session_get_system_session() != NULL); + s_debounced_state_is_connected[PebbleKitDebounce] = + (comm_session_get_current_app_session() != NULL); +} + +DEFINE_SYSCALL(bool, sys_mobile_app_is_connected_debounced, void) { + return s_debounced_state_is_connected[MobileAppDebounce]; +} + +DEFINE_SYSCALL(bool, sys_pebblekit_is_connected_debounced, void) { + return s_debounced_state_is_connected[PebbleKitDebounce]; +} + +#define DISCONNECT_HIDE_DURATION_SECS 25 +void debounced_connection_service_handle_event(PebbleCommSessionEvent *e) { + DebounceConnection conn_id = e->is_system ? MobileAppDebounce : PebbleKitDebounce; + bool timer_stopped = false; + if (!e->is_open) { + // If we become disconnected don't update apps until we have had a chance + // to recover the connection. This will make our BT connection seem more + // reliable. + regular_timer_add_multisecond_callback( + &s_debounce_timers[conn_id], DISCONNECT_HIDE_DURATION_SECS); + return; + } + + if (regular_timer_is_scheduled(&s_debounce_timers[conn_id])) { + // we reconnected quickly so no need to notify the app about it + timer_stopped = regular_timer_remove_callback(&s_debounce_timers[conn_id]); + } + + if (!timer_stopped) { + // We've been disconnected long enough that we've already told the app that + // we disconnected so let the app know that we are connected again. + s_debounced_state_is_connected[conn_id] = true; + prv_put_debounced_connection_event(conn_id); + } +} diff --git a/src/fw/services/debounced_connection_service/wscript_build b/src/fw/services/debounced_connection_service/wscript_build new file mode 100644 index 0000000000..1aa4dd35a9 --- /dev/null +++ b/src/fw/services/debounced_connection_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_debounced_connection_service', +) +bld.env.SERVICES.append('services_debounced_connection_service') diff --git a/src/fw/services/ecompass/Kconfig b/src/fw/services/ecompass/Kconfig new file mode 100644 index 0000000000..a0cf8b8326 --- /dev/null +++ b/src/fw/services/ecompass/Kconfig @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ECOMPASS + bool "E-compass" + default y if MAG + help + Electronic compass service. Requires a magnetometer driver (CONFIG_MAG). + +if SERVICE_ECOMPASS + +module = SERVICE_ECOMPASS +module-str = E-compass +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/ecompass/correction.c b/src/fw/services/ecompass/correction.c new file mode 100644 index 0000000000..a176a2f2d4 --- /dev/null +++ b/src/fw/services/ecompass/correction.c @@ -0,0 +1,404 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/ecompass.h" + +#include "util/trig.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "util/math.h" + +#include +#include + +PBL_LOG_MODULE_DECLARE(service_ecompass, CONFIG_SERVICE_ECOMPASS_LOG_LEVEL); + +#define N_SAMPS 4 // four points define a unique sphere +#define N_AXIS 3 +static int16_t s_samples[N_SAMPS][N_AXIS]; + +// +// Two basic equations of a sphere: +// a) (x - x0)^2 + (y - y0)^2 + (z - z0)^2 = r0^2 +// (x^2 - 2*x*x0 + x0^2) + (y^2 - 2*y*x0 + y0^2) + (z^2 - 2*z*z0 + z0^2) = r^2 +// -2*x*x0 - 2*y*y0 - 2*z*z0 + (x^2 + y^2 + z^2) + r0^2 = 0 +// b) A*(x^2 + y^2 + x^2) + B*x + C*y + D*z + E = 0 +// +// Ideally, we would be able to build a large cloud of points and apply a least +// squares or ellipsoid fit to that dataset. However, this quickly becomes +// expensive (from a code space perspective). Therefore, we focus on collecting +// four 'good' data points and fitting those point to a sphere +// +// The sphere fit entails solving a linear system of the form Ax = B +// +// | (x^2 + y^2 + z^2) x y z 1 | | A | | 0 | +// | (x0^2 + y0^2 + z0^2) x0 y0 z0 1 | | B | | 0 | +// | (x1^2 + y1^2 + z1^2) x1 y1 z1 1 | * | C | = | 0 | +// | (x2^2 + y2^2 + z2^2) x2 y2 z2 1 | | D | | 0 | +// | (x3^2 + y3^2 + z3^2) x3 y3 z3 1 | | E | | 0 | +// +// to solve we want to find where det(A) = 0 +// +// Using Laplace's formula for determinant expansion we can break this into a +// system of 4x4 determinants. Expanding along row 0 and using Cij to represent +// the cofactor which removes row i and column j: +// +// (x^2 + y^2 + z^2) * C_0,0 + x * C_0,1 + y * C_0,2 + z * C_0,3 + C_0,4 = 0 +// +// This solution can be re-written in a form similar to eq a) above as +// +// x * (C_0,1 / C_0,0) + y * (C_0,2 / C_0,0) + z * (C_0,3 / C_0,0) + +// (C_0,4 / C_0,0) + (x^2 + y^2 + z^2) = 0 +// +// This gives us our hard iron correction estimates (i.e location of the sphere +// origin, (xo, yo, zo)) as: +// +// (C_0,1 / C_0,0) = -2*x0 ==> x0 = (C_0,1 / C_0,0) / -2 +// (C_0,2 / C_0,0) = -2*y0 ==> y0 = (C_0,2 / C_0,0) / -2 +// (C_0,3 / C_0,0) = -2*z0 ==> z0 = (C_0,3 / C_0,0) / -2 +// + +static int32_t sphere_determinant4x4(int32_t **m, int32_t down_samp) { + // assume x, y, z value fits within 14 bits & m[x][3] == 1 + // when m[x][0] == x^2 + y^2 + z^2, this val < 30 bits + // so m[x][0] * temp is capped at 44bits leaving plenty of space + // within 64 bits + + int64_t res = 0; + // computes the determinant by decomposing the computation into 4 3x3 minor + // determinants formed by removal of row 'skip_row' & the third column + for (int skip_row = 0; skip_row < 4; skip_row++) { + int r0 = (skip_row != 0) ? 0 : 1; + int r1 = (skip_row < 2) ? 2 : 1; + int r2 = (skip_row < 3) ? 3 : 2; + + int64_t temp = (m[r1][1] * m[r2][2]) - (m[r1][2] * m[r2][1]); + int64_t det = temp * (int64_t)m[r0][0]; + + temp = (m[r1][0] * m[r2][2]) - (m[r1][2] * m[r2][0]); + det -= temp * (int64_t)m[r0][1]; + + temp = (m[r1][0] * m[r2][1]) - (m[r1][1] * m[r2][0]); + det += temp * (int64_t)m[r0][2]; + + if ((skip_row % 2) == 0) { + res += det; + } else { + res -=det; + } + } + + return (res / down_samp); +} + +static bool sphere_fit(int16_t *solution) { + bool rv = false; + + int32_t **matrix = kernel_malloc(sizeof(int32_t *) * N_SAMPS); + for (int i = 0; i < N_SAMPS; i++) { + matrix[i] = kernel_malloc(sizeof(int32_t) * N_AXIS); + } + + // determine average value of x, y, & z coordinates + // and shift by this factor to prevent overflow. + int32_t shift_factor[N_AXIS] = { 0 }; + for (int j = 0; j < N_AXIS; j++) { + for (int i = 0; i < N_SAMPS; i++) { + shift_factor[j] += s_samples[i][j]; + } + shift_factor[j] /= N_SAMPS; + } + + int16_t raw_data[N_SAMPS][N_AXIS]; + for (int i = 0; i < N_SAMPS; i++) { + for (int j = 0; j < 3; j++) { + raw_data[i][j] = (s_samples[i][j] - shift_factor[j]); + // TODO: stop here if we have a chance of overflowing + } + } + + int32_t r[N_SAMPS]; + for (int i = 0; i < N_SAMPS; i++) { + r[i] = raw_data[i][0] * raw_data[i][0] + + raw_data[i][1] * raw_data[i][1] + + raw_data[i][2] * raw_data[i][2]; + } + + // We now find the origin by solving the linear equation discussed above + + for (int i = 0; i < N_SAMPS; i++) { + matrix[i][0] = raw_data[i][0]; + matrix[i][1] = raw_data[i][1]; + matrix[i][2] = raw_data[i][2]; + } + int32_t c00 = sphere_determinant4x4(matrix, 1); + if (c00 == 0) { + goto cleanup; + } + + // compute cofactor01 to solve for x0 + for (int i = 0; i < N_SAMPS; i++) { + matrix[i][0] = r[i]; // m[i][1] = y, m[i][2] = z + } + solution[0] = shift_factor[0] + sphere_determinant4x4(matrix, c00 * 2); + + // compute cofactor02 to solve for y0 + for (int i = 0; i < N_SAMPS; i++) { + matrix[i][1] = raw_data[i][0]; // m[i][0] = r^2, m[i][2] = z + } + solution[1] = shift_factor[1] - sphere_determinant4x4(matrix, c00 * 2); + + // compute cofactor03 to solve for z0 + for (int i = 0; i < N_SAMPS; i++) { + matrix[i][2] = raw_data[i][1]; // m[i][0] = r^2, m[i][1] = x + } + solution[2] = shift_factor[2] + sphere_determinant4x4(matrix, c00 * 2); + + rv = true; +cleanup: + for (int i = 0; i < N_SAMPS; i++) { + kernel_free(matrix[i]); + } + kernel_free(matrix); + return (rv); +} + +// Earths magnetic field intensity ranges from 25uT (near the equator) to 65uT +// (near the earths poles). A majority of Europe, North America, & Asia ranges +// between 35-50uT. +// +// Assuming magnetometer readings are predominantly influenced by hard iron +// distortions, we seek to find four points and fit them to a sphere in +// order to determine the offset we need to shift the raw data by to correct +// for hard iron distortions. +// +// For points A, B, C, D and a distance threshold, t, we select four points by +// satisfying the following: +// distance_ptA_to_ptB > t +// distance_lineAB_to_ptC > t +// distance_planeABC_to_ptD > t. +// +// Conceptually, it makes sense that the farther points are from one another (> +// t), the less that errors due to noise, fixed point mathematics, & motion +// render bad solutions. (empirically, this seems to be the behavior as +// well). However, the greater the the threshold, the more orientations one +// must put their watch through in order to get solution sets +// +// For now, select a distance metric that should work out of the box for a +// majority of the middle of the world. However, if no solution sets are found +// after 45s, fall back to a less aggressive threshold that will work +// anywhere in the world. + +#define THRESH_MAX 370 /* 37 uT */ +#define THRESH_MIN 220 /* 22 uT */ + +// Note: All of the following helper routines operate on the s_samples array +// defined above and populated by add_raw_mag_sample + +static bool pt_to_pt_dist_under_thresh(int idx_a, int idx_b, int32_t thresh) { + int32_t v_ab[3] = { + s_samples[idx_b][0] - s_samples[idx_a][0], + s_samples[idx_b][1] - s_samples[idx_a][1], + s_samples[idx_b][2] - s_samples[idx_a][2] + }; + + int32_t dist_sq = v_ab[0] * v_ab[0] + v_ab[1] * v_ab[1] + + v_ab[2] * v_ab[2]; + + return (dist_sq > (thresh * thresh)); +} + +static bool pt_to_line_dist_under_thresh(int idx_line_a, int idx_line_b, + int idx_pt, int32_t thresh) { + + int32_t s[3] = { + s_samples[idx_line_b][0] - s_samples[idx_line_a][0], + s_samples[idx_line_b][1] - s_samples[idx_line_a][1], + s_samples[idx_line_b][2] - s_samples[idx_line_a][2] + }; + + int32_t m1[3] = { + s_samples[idx_line_a][0] - s_samples[idx_pt][0], + s_samples[idx_line_a][1] - s_samples[idx_pt][1], + s_samples[idx_line_a][2] - s_samples[idx_pt][2] + }; + + int32_t m1xs[3] = { + m1[1] * s[2] - m1[2] *s[1], + -(m1[0] * s[2] - m1[2] * s[0]), + m1[0]*s[1] - m1[1] * s[0] + }; + + int64_t dist_sq = ((int64_t)m1xs[0]*m1xs[0] + (int64_t)m1xs[1]*m1xs[1] + + (int64_t)m1xs[2]*m1xs[2]) / (s[0]*s[0] + s[1]*s[1] + s[2]*s[2]); + + return (dist_sq > (thresh * thresh)); +} + +static bool pt_to_plane_dist_under_thresh(int idx_pln_a, int idx_pln_b, + int idx_pln_c, int idx_pt, int32_t thresh) { + + int32_t v_ab[3]; + int32_t v_ac[3]; + for (int i = 0; i < 3; i++) { + v_ab[i] = s_samples[idx_pln_b][i] - s_samples[idx_pln_a][i]; + v_ac[i] = s_samples[idx_pln_c][i] - s_samples[idx_pln_a][i]; + } + + int64_t plane_eq[4]; + plane_eq[0] = v_ab[1] * v_ac[2] - v_ab[2] * v_ac[1]; + plane_eq[1] = -v_ab[0] * v_ac[2] + v_ab[2] * v_ac[0]; + plane_eq[2] = v_ab[0] * v_ac[1] - v_ab[1] * v_ac[0]; + + // solve for d + plane_eq[3] = -(plane_eq[0] * s_samples[0][0] + + plane_eq[1] * s_samples[0][1] + plane_eq[2] * s_samples[0][2]); + + // Distance^2 = (a * xo + b * yo + c * zo + d) / (a^2 + b^2 + c^2) + int64_t distance = ABS((plane_eq[0] * s_samples[idx_pt][0] + + plane_eq[1] * s_samples[idx_pt][1] + plane_eq[2] * s_samples[idx_pt][2] + + plane_eq[3])); + int32_t r = integer_sqrt(plane_eq[0] * plane_eq[0] + plane_eq[1] * plane_eq[1] + + plane_eq[2] * plane_eq[2]); + + + return ((distance / r) > thresh); +} + +static int min_max_diff(int16_t *vals, int n_vals) { + int min = vals[0]; + int max = vals[0]; + + for (int i = 0; i < n_vals; i++) { + if (vals[i] > max) { + max = vals[i]; + } else if (vals[i] < min) { + min = vals[i]; + } + } + return (max - min); +} + +#define N_COMP_SAMPS 3 +static MagCalStatus check_correction_value(int16_t *solution, + int16_t *saved_solution) { + const int max_delta_thresh = 50; + + // stash several of the most recent calibration results. The idea here is if + // we get multiple readings in a row close to one another than we have locked + // onto a good set of solutions + static int16_t calib_idx = 0; + static int16_t calib_val[N_AXIS][N_COMP_SAMPS]; + calib_val[0][calib_idx % 3] = solution[0]; + calib_val[1][calib_idx % 3] = solution[1]; + calib_val[2][calib_idx % 3] = solution[2]; + calib_idx++; + + static int saved_sample_match = 0; + int x_delta, y_delta, z_delta; + + // is the new solution close to what we already have saved? + if (saved_solution != NULL) { + x_delta = ABS(saved_solution[0] - solution[0]); + y_delta = ABS(saved_solution[1] - solution[1]); + z_delta = ABS(saved_solution[2] - solution[2]); + if ((x_delta < max_delta_thresh) && (y_delta < max_delta_thresh) && + (z_delta < max_delta_thresh)) { + saved_sample_match++; + if (saved_sample_match == 3) { + saved_sample_match = 0; + calib_idx = 0; + PBL_LOG_INFO("Persisting previous values!"); + return (MagCalStatusSavedSampleMatch); // locked + } + } + } + + // do we have several solutions in a row that are close to one another + if (calib_idx >= N_COMP_SAMPS) { + x_delta = min_max_diff(calib_val[0], 3); + y_delta = min_max_diff(calib_val[1], 3); + z_delta = min_max_diff(calib_val[2], 3); + if ((x_delta < max_delta_thresh) && (y_delta < max_delta_thresh) && + (z_delta < max_delta_thresh)) { + int corrs[N_AXIS] = { 0 }; + for (int i = 0; i < N_AXIS; i++) { + for (int j = 0; j < N_COMP_SAMPS; j++) { + corrs[i] += calib_val[i][j]; + } + solution[i] = corrs[i] / N_COMP_SAMPS; + } + calib_idx = 0; + return (MagCalStatusNewLockedSolutionAvail); + } + } + + return (MagCalStatusNewSolutionAvail); +} + +static int s_sample_idx = 0; +static int s_no_fit_strikes = 0; +static int s_samples_collected_for_fit = 0; + +void ecomp_corr_reset(void) { + s_no_fit_strikes = 0; + s_samples_collected_for_fit = 0; + s_sample_idx = 0; +} + +MagCalStatus ecomp_corr_add_raw_mag_sample(int16_t *sample, + int16_t *saved_corr, int16_t *solution) { + static int thresh = THRESH_MAX; + s_samples_collected_for_fit++; + + // if we haven't gotten good samples points in 15s @ 20Hz (60s @ 5Hz) + if (s_samples_collected_for_fit > 300) { + if (s_sample_idx >= 2) { // there was some kind of motion + s_no_fit_strikes++; + } + if (s_no_fit_strikes == 2) { + PBL_LOG_INFO("Lowering magnetometer distance threshold"); + thresh = THRESH_MIN; + } + + s_samples_collected_for_fit = 0; + s_sample_idx = 0; + } + + s_samples[s_sample_idx][0] = sample[0]; + s_samples[s_sample_idx][1] = sample[1]; + s_samples[s_sample_idx][2] = sample[2]; + + if (s_sample_idx == 1) { + if (!pt_to_pt_dist_under_thresh(0, 1, thresh)) { + return (MagCalStatusNoSolution); + } + } else if (s_sample_idx == 2) { + if (!pt_to_line_dist_under_thresh(0, 1, 2, thresh)) { + return (MagCalStatusNoSolution); + } + } else if (s_sample_idx == 3) { + if (!pt_to_plane_dist_under_thresh(0, 1, 2, 3, thresh)) { + return (MagCalStatusNoSolution); + } + } + + // the sample has passed its distance threshold check so add it + PBL_LOG_DBG("---> [%d] Adding %d %d %d \n", + s_sample_idx, (int)sample[0], (int)sample[1], (int)sample[2]); + s_sample_idx++; + + if (s_sample_idx != 4) { + return (MagCalStatusNoSolution); // we need 4 pts before we can do a fit + } + + // reset vars for next potential sphere fit + s_samples_collected_for_fit = 0; + s_sample_idx = 0; + + if (!sphere_fit(&solution[0])) { + return (MagCalStatusNoSolution); + } + + return (check_correction_value(solution, saved_corr)); +} diff --git a/src/fw/services/ecompass/service.c b/src/fw/services/ecompass/service.c new file mode 100644 index 0000000000..adaff607bd --- /dev/null +++ b/src/fw/services/ecompass/service.c @@ -0,0 +1,421 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/ecompass.h" + +#include "applib/accel_service.h" +#include "applib/compass_service.h" +#include "util/trig.h" +#include "console/prompt.h" +#include "drivers/mag.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/event_service.h" +#include "pbl/services/regular_timer.h" +#include "syscall/syscall_internal.h" +#include "syscall/syscall.h" +#include "system/logging.h" +#include "system/passert.h" +#include "kernel/util/sleep.h" + +#include "system/rtc_registers.h" + +PBL_LOG_MODULE_DEFINE(service_ecompass, CONFIG_SERVICE_ECOMPASS_LOG_LEVEL); + +// Duration (in minutes) to run high-frequency sampling during compass calibration. +// Defaults to 2 minutes, but some platforms (e.g., Asterix) require longer. +#ifdef CONFIG_BOARD_FAMILY_ASTERIX +#define ECOMPASS_CALIBRATION_FAST_MINUTES 6 +#else +#define ECOMPASS_CALIBRATION_FAST_MINUTES 2 +#endif + +#define VALID_CORR_MARKER 0x5644 +#define BITS_PER_CORRECTION_VAL 16 +#define CORRECTION_VAL_MASK ((1 << BITS_PER_CORRECTION_VAL) - 1) + + +static CompassStatus s_current_cal_status = CompassStatusDataInvalid; +static int16_t s_active_corr[3] = { 0 }; + +static bool s_service_init = false; +static bool s_saved_corr_present = false; +static int16_t s_saved_corr[3] = { 0 }; + +static int32_t s_last_heading = -1; // the last heading we found +#ifdef CONFIG_RECOVERY_FW +static MagData s_last_mag_sample = { 0 }; +#endif + +////////////////////////////////////////////////////////////////////////////////// +// Calibration state variables +static void prv_calibration_time_expired_cb(void* data); + +static bool s_high_freq_calib_active = false; +static bool s_calib_run = false; +static RegularTimerInfo s_cb_info = { .cb = prv_calibration_time_expired_cb }; + + +////////////////////////////////////////////////////////////////////////////////// +// Compass subscription state variables + +static uint8_t s_compass_subscribers_count = 0; +static bool s_compass_subscribers[NumPebbleTask] = { 0 }; + + +////////////////////////////////////////////////////////////////////////////////// +// Accel service state variables + +static AccelServiceState *s_accel_session = NULL; +static bool s_charger_plugged = false; +static AccelRawData s_accel_data = { 0 }; + + +////////////////////////////////////////////////////////////////////////////////// +// Private calibration handlers + +static void prv_get_roll_and_pitch(AccelRawData *d, int32_t *rollp, + int32_t *pitchp) { + if ((d->x == 0) && (d->y == 0) && (d->z == 0)) { + *rollp = *pitchp = 0; + return; + } + + int32_t roll = atan2_lookup(d->y, d->z); + + int32_t act_ang = (roll * 360) / TRIG_MAX_ANGLE; + if (act_ang > 180) { + roll = roll - TRIG_MAX_ANGLE; + } + + int32_t pitch = atan2_lookup(-d->x, + (d->y * sin_lookup(roll) + d->z * cos_lookup(roll)) / TRIG_MAX_RATIO); + + // solution repeats every 180 degrees + if (pitch > (TRIG_MAX_ANGLE / 4)) { // > 90 degrees + if (pitch < ((270 * TRIG_MAX_ANGLE) / 360)) { + pitch -= (TRIG_MAX_ANGLE / 2); + } else { + pitch -= TRIG_MAX_ANGLE; + } + } + + *rollp = roll; + *pitchp = pitch; +} + +static int32_t prv_correct_for_roll_and_pitch(AccelRawData *accel_data, + MagData *mag_data, int32_t roll, int32_t pitch) { + int32_t mx = mag_data->x - s_active_corr[0]; + int32_t my = mag_data->y - s_active_corr[1]; + int32_t mz = mag_data->z - s_active_corr[2]; + + int32_t mx_rot, my_rot; + + // per freescale AN4249, roll is unstable close to verticle but pitch is ok + int32_t corr = 0; + if (TRIGANGLE_TO_DEG(pitch) > 82) { + pitch = TRIG_MAX_ANGLE / 4; + roll = 0; + } else if (accel_data->z < 0) { + // the watch has been flipped over. If someone is viewing the watch + // at a pitch > 90 degrees, this means the heading will rotate around + // on them (since technically, the 'front' of the watch is pointing + // at them.) Flip the heading back around in this case. + corr = TRIG_MAX_ANGLE / 2; + } + + mx_rot = (mx * cos_lookup(pitch)) / TRIG_MAX_RATIO; + mx_rot += (((my * sin_lookup(pitch)) / TRIG_MAX_RATIO) * sin_lookup(roll)) / + TRIG_MAX_RATIO; + mx_rot += (((mz * sin_lookup(pitch)) / TRIG_MAX_RATIO) * cos_lookup(roll)) / + TRIG_MAX_RATIO; + + my_rot = mz * sin_lookup(roll) - my * cos_lookup(roll); + my_rot /= TRIG_MAX_RATIO; + + int32_t heading = (atan2_lookup(-my_rot, mx_rot) + corr) % TRIG_MAX_ANGLE; + return (heading); +} + + +////////////////////////////////////////////////////////////////////////////////// +// Private handlers for compass service + +static void prv_calibration_time_expired_cb(void* data) { + PBL_LOG_DBG("Calibration time expired, complete, or app exit, " + "dropping back to low frequency"); + + if (!mag_change_sample_rate(MagSampleRate5Hz)) { + PBL_LOG_WRN("Forcing reset to enter low freq mode"); + mag_release(); + mag_start_sampling(); + } + + s_high_freq_calib_active = false; + regular_timer_remove_callback(&s_cb_info); +} + +static void prv_accel_for_compass_handler(AccelRawData *d, uint32_t num_samples, + uint64_t timestamp) { + int32_t x = 0, y = 0, z = 0; + + // 1st order butterworth filter with a cutoff freq of 0.02Fs + static int32_t xp = 0, yp = 0, zp = 0; + static int32_t xr = 0, yr = 0, zr = 0; + + for (uint32_t i = 0; i < num_samples; i++) { + xr = (305 * d[i].x + 305 * xp + 9391 * xr) / 10000; + yr = (305 * d[i].y + 305 * yp + 9391 * yr) / 10000; + zr = (305 * d[i].z + 305 * zp + 9391 * zr) / 10000; + xp = d[i].x; yp = d[i].y; zp = d[i].z; + x += xr; + y += yr; + z += zr; + } + + x /= (int)num_samples; + y /= (int)num_samples; + z /= (int)num_samples; + + // TODO: could this ever be called in the middle of ecompass_service_handle? + // map accel data to NED coordinate system + s_accel_data.x = y; + s_accel_data.y = x; + s_accel_data.z = -z; +} + +static void prv_compass_data_service_stop(PebbleTask task) { + if (s_compass_subscribers[task]) { + s_compass_subscribers[task] = false; + + if (--s_compass_subscribers_count == 0) { + // If this was the last subscribed process, then stop the compass + // service + if (s_high_freq_calib_active) { + prv_calibration_time_expired_cb(NULL); + s_calib_run = false; + } + accel_session_data_unsubscribe(s_accel_session); + accel_session_delete(s_accel_session); + s_accel_session = NULL; + mag_release(); + } + } + PBL_LOG_DBG("subscribers %"PRIu8, s_compass_subscribers_count); +} + +static void prv_compass_data_service_start(PebbleTask task) { + prv_compass_data_service_stop(task); + s_compass_subscribers[task] = true; + if (++s_compass_subscribers_count == 1) { + // If this is the first subscriber to the compass service, start sampling + PBL_ASSERTN(s_accel_session == NULL); + + s_accel_session = accel_session_create(); + accel_session_raw_data_subscribe(s_accel_session, ACCEL_SAMPLING_25HZ, 5, + prv_accel_for_compass_handler); + + mag_start_sampling(); + } + PBL_LOG_DBG("subscribers %"PRIu8, s_compass_subscribers_count); +} + +////////////////////////////////////////////////////////////////////////////////// +// Public API + +void ecompass_handle_battery_state_change_event(PreciseBatteryChargeState new_state) { + if (new_state.is_plugged) { + s_charger_plugged = true; + s_current_cal_status = CompassStatusDataInvalid; + memset(s_active_corr, 0x00, sizeof(s_active_corr)); + s_saved_corr_present = false; + } else if (s_charger_plugged) { + // we have unplugged the charger, initiate recalibration + s_charger_plugged = false; + s_calib_run = false; // trigger a rerun of fast compass calibration + PBL_LOG_DBG("Restarting calibration after charge event"); + } +} + +void ecompass_service_init(void) { + s_service_init = true; + + event_service_init(PEBBLE_COMPASS_DATA_EVENT, &prv_compass_data_service_start, + &prv_compass_data_service_stop); +} + +void ecompass_service_handle(void) { + static int samples_collected = 0; + + // read magnetometer sample + MagData mag_data; + MagReadStatus rv = mag_read_data(&mag_data); + if (rv != MagReadSuccess) { + if (rv == MagReadCommunicationFail) { + // heavy hammer fix for now + // FIXME: move the restart logic to driver + PBL_LOG_WRN("Read after %d samples failed, " + "restarting compass", samples_collected); + mag_release(); + mag_start_sampling(); + } + return; + } + +#ifdef CONFIG_RECOVERY_FW + s_last_mag_sample = mag_data; +#endif + + // industry standard for heading coordinates uses NED convention (check out + // Freescale's AN4248 or ST's AN3192 as examples). Therefore, we map pebbles + // coordinate system (ENU) to NED in this service module + int16_t my_orig = mag_data.y; + mag_data.y = mag_data.x; + mag_data.x = my_orig; + mag_data.z = -mag_data.z; + + samples_collected++; + + // don't perform any calibration if the charger is plugged in + if (!s_charger_plugged && (s_current_cal_status != CompassStatusCalibrated)) { + // if we haven't tried to calibrate yet, run at a higher sampling rate + // for a short duration in order to finish calibration more quickly + if (!s_calib_run) { + ecomp_corr_reset(); + mag_change_sample_rate(MagSampleRate20Hz); + regular_timer_add_multiminute_callback(&s_cb_info, ECOMPASS_CALIBRATION_FAST_MINUTES); + s_calib_run = true; + s_high_freq_calib_active = true; + samples_collected = 0; + return; // if we are switching to high freq mode, don't use 1st sample + } + + if (samples_collected < 5) { + return; // wait a few samples for ramp up error to stabilize + } + + int16_t new_corr[3]; + MagCalStatus cal_status = ecomp_corr_add_raw_mag_sample((int16_t *)&mag_data, + (s_saved_corr_present) ? s_saved_corr : NULL, new_corr); + + if (cal_status != MagCalStatusNoSolution) { + PBL_LOG_INFO("%s : %d %d %d (type = %d)", "Mag Corr", + (int)new_corr[0], (int)new_corr[1], (int)new_corr[2], (int)cal_status); + } + + bool locked_sol = (cal_status == MagCalStatusNewLockedSolutionAvail); + if (locked_sol || ((cal_status == MagCalStatusNewSolutionAvail) && + !s_saved_corr_present)) { + s_current_cal_status = CompassStatusCalibrating; + for (int i = 0; i < 3; i++) { + if ((s_active_corr[i] == 0) || locked_sol) { + s_active_corr[i] = new_corr[i]; + continue; + } + + // smooth out noise from solutions while we wait for a locked set + const int alpha = 30; // greater alpha leads to less smoothing + new_corr[i] = ((new_corr[i] - s_active_corr[i]) * alpha) / 100; + s_active_corr[i] += new_corr[i]; + } + } + + if (s_high_freq_calib_active && (cal_status == MagCalStatusNoSolution) && + ((samples_collected % 4) != 0)) { + return; // only bubble up every 4th sample to app land with high samp rate + } + + if ((cal_status == MagCalStatusNewLockedSolutionAvail) || + (cal_status == MagCalStatusSavedSampleMatch)) { + if (s_high_freq_calib_active) { + prv_calibration_time_expired_cb(NULL); + } + s_current_cal_status = CompassStatusCalibrated; + for (int i = 0; i < 3; i++) { + s_active_corr[i] = new_corr[i]; + } + s_saved_corr_present = true; + } + } + + // get the most recent accel readings + AccelRawData accel_data = s_accel_data; + int32_t roll = 0, pitch = 0; + prv_get_roll_and_pitch(&accel_data, &roll, &pitch); + + PebbleEvent e = { + .type = PEBBLE_COMPASS_DATA_EVENT, + .compass_data = { + .magnetic_heading = prv_correct_for_roll_and_pitch(&accel_data, &mag_data, + roll, pitch), + .calib_status = s_current_cal_status + } + }; + + s_last_heading = e.compass_data.magnetic_heading; + event_put(&e); +} + +////////////////////////////////////////////////////////////////////////////////// +// System call handlers + +DEFINE_SYSCALL(bool, sys_ecompass_service_subscribed, void) { + PebbleTask task = pebble_task_get_current(); + return s_compass_subscribers[task]; +} + +DEFINE_SYSCALL(void, sys_ecompass_get_last_heading, CompassHeadingData *data) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(data, sizeof(*data)); + } + + *data = (CompassHeadingData) { + .magnetic_heading = s_last_heading, + .true_heading = s_last_heading, + .compass_status = s_current_cal_status, + .is_declination_valid = false + }; +} + +////////////////////////////////////////////////////////////////////////////////// +// Recovery firmware commands + +#ifdef CONFIG_RECOVERY_FW +static void prv_ecompass_start_callback(void *context) { + s_accel_session = accel_session_create(); + accel_session_raw_data_subscribe(s_accel_session, ACCEL_SAMPLING_25HZ, 5, + prv_accel_for_compass_handler); + mag_start_sampling(); +} + +static void prv_ecompass_stop_callback(void *context) { + accel_session_data_unsubscribe(s_accel_session); + accel_session_delete(s_accel_session); + mag_release(); +} + +//! Serial command for reading a single value from the compass. +void command_compass_peek(void) { + int32_t prev_heading = s_last_heading; + + launcher_task_add_callback(prv_ecompass_start_callback, NULL); + + // wait for last heading to be updated + int retries = 50; // 5 seconds should be ample time + while ((prev_heading == s_last_heading) && retries-- > 0) { + psleep(100); + } + + launcher_task_add_callback(prv_ecompass_stop_callback, NULL); + psleep(5); // give the compass some time to stop + + char buffer[40]; + prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRId32" degrees", + (s_last_heading * 360) / TRIG_MAX_ANGLE); + + prompt_send_response_fmt(buffer, sizeof(buffer), "Mx=%d, My=%d, Mz=%d", + s_last_mag_sample.x, s_last_mag_sample.y, s_last_mag_sample.z); +} +#endif // CONFIG_RECOVERY_FW diff --git a/src/fw/services/ecompass/wscript_build b/src/fw/services/ecompass/wscript_build new file mode 100644 index 0000000000..d27df63c2e --- /dev/null +++ b/src/fw/services/ecompass/wscript_build @@ -0,0 +1,10 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +if bld.env.CONFIG_MAG: + bld.stlib( + use=['fw_includes'], + source=['service.c', 'correction.c'], + target='services_ecompass', + ) + bld.env.SERVICES.append('services_ecompass') diff --git a/src/fw/services/event_service/Kconfig b/src/fw/services/event_service/Kconfig new file mode 100644 index 0000000000..5095a1152c --- /dev/null +++ b/src/fw/services/event_service/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_EVENT_SERVICE + bool "Event service" + help + Generic event subscription/delivery service. + +if SERVICE_EVENT_SERVICE + +module = SERVICE_EVENT_SERVICE +module-str = Event service +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/event_service/service.c b/src/fw/services/event_service/service.c new file mode 100644 index 0000000000..d41248b357 --- /dev/null +++ b/src/fw/services/event_service/service.c @@ -0,0 +1,393 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/event_service_client.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "kernel/pebble_tasks.h" +#include "process_management/app_manager.h" +#include "process_management/worker_manager.h" +#include "os/mutex.h" +#include "pbl/services/event_service.h" +#include "syscall/syscall_internal.h" +#include "syscall/syscall.h" +#include "system/logging.h" +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "queue.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_event_service, CONFIG_SERVICE_EVENT_SERVICE_LOG_LEVEL); + +typedef struct { + int num_subscribers; + QueueHandle_t subscribers[NumPebbleTask]; + EventServiceAddSubscriberCallback add_subscriber_callback; + EventServiceRemoveSubscriberCallback remove_subscriber_callback; +} EventServiceEntry; + +typedef struct { + ListNode list_node; + void *ptr; + // There is an intent bit for each task + a special "claimed" bit + uint16_t intents_pending; +} EventServiceBuffer; + +static const uint16_t CLAIMED_BIT = (1 << NumPebbleTask); + +static EventServiceBuffer *s_event_service_buffers = NULL; + +// We dynamically allocate one of these for every service UUID that either a client subscribes to or a service +// publishes an event to. +typedef struct { + ListNode list_node; + uint16_t service_index; // index of the service + Uuid uuid; // UUID +} EventPluginUUIDEntry; + +static uint16_t s_next_service_index = 0; +static ListNode s_plugin_list; +// This mutex guards the s_plugin_list linked list +static PebbleMutex *s_plugin_list_mutex = NULL; + +// There's an event service for each event so that +// System apps can also use the service +static EventServiceEntry *s_event_services[PEBBLE_NUM_EVENTS]; + +static void prv_event_service_unsubscribe(PebbleSubscriptionEvent *subscription) { + EventServiceEntry *service = s_event_services[subscription->event_type]; + + if (s_event_services[subscription->event_type] == NULL) { + // service does not exist + PBL_LOG_WRN("Attempted to unsubscribe from %d, no service found", + subscription->event_type); + return; + } + + if (service->subscribers[subscription->task] == NULL) { + // not subscribed + PBL_LOG_WRN("Attempted to unsubscribe from %d, not subscribed", + subscription->event_type); + return; + } + + PBL_ASSERTN(service->num_subscribers > 0); + --service->num_subscribers; + service->subscribers[subscription->task] = NULL; + if (service->remove_subscriber_callback != NULL) { + service->remove_subscriber_callback(subscription->task); + } +} + + +static void prv_event_service_subscribe(PebbleSubscriptionEvent *subscription) { + EventServiceEntry *service = s_event_services[subscription->event_type]; + + if (service == NULL) { + // No event service for this event type, create one + event_service_init(subscription->event_type, NULL, NULL); + service = s_event_services[subscription->event_type]; + } + + if (service->subscribers[subscription->task]) { + // already subscribed ? + PBL_LOG_DBG("already subscribed"); + return; + } + + if (service->add_subscriber_callback != NULL) { + service->add_subscriber_callback(subscription->task); + } + + service->subscribers[subscription->task] = subscription->event_queue; + ++service->num_subscribers; +} + +void event_service_subscribe_from_kernel_main(PebbleSubscriptionEvent *subscription) { + PBL_ASSERT_TASK(PebbleTask_KernelMain); + prv_event_service_subscribe(subscription); +} + +static bool prv_event_service_send_event(QueueHandle_t queue, PebbleEvent *e) { + PBL_ASSERTN(queue != NULL); + bool success = (xQueueSendToBack(queue, e, 0) == pdTRUE); + return success; +} + +void event_service_handle_subscription(PebbleSubscriptionEvent *subscription) { + if (subscription->subscribe) { + prv_event_service_subscribe(subscription); + } else { + prv_event_service_unsubscribe(subscription); + } +} + + +void event_service_clear_process_subscriptions(PebbleTask task) { + EventServiceEntry *service; + + for (int i = 0; i < PEBBLE_NUM_EVENTS; i++) { + if ((service = s_event_services[i]) == NULL) { + continue; + } + if (service->subscribers[task] == NULL) { + continue; + } + + PebbleSubscriptionEvent event = { + .subscribe = false, + .task = task, + .event_type = i, + }; + prv_event_service_unsubscribe(&event); + } +} + +void event_service_system_init(void) { + s_plugin_list_mutex = mutex_create(); +} + +void event_service_init(PebbleEventType type, EventServiceAddSubscriberCallback add_subscriber_callback, + EventServiceRemoveSubscriberCallback remove_subscriber_callback) { + if(s_event_services[type] != NULL) { + // an event service was already inited, free it + kernel_free(s_event_services[type]); + } + + s_event_services[type] = kernel_malloc(sizeof(EventServiceEntry)); + memset(s_event_services[type], 0, sizeof(*s_event_services[type])); + s_event_services[type]->add_subscriber_callback = add_subscriber_callback; + s_event_services[type]->remove_subscriber_callback = remove_subscriber_callback; +} + +bool event_service_is_running(PebbleEventType event_type) { + if (s_event_services[event_type] == NULL) { + return (false); + } + if (s_event_services[event_type]->num_subscribers > 0) { + return (true); + } + + return (false); +} + +static bool prv_task_is_masked_out(PebbleEvent *e, PebbleTask task) { + const PebbleTaskBitset task_bit = (1 << task); + return (e->task_mask & task_bit); +} + +static bool prv_steal_buffer(void *buf, EventServiceEntry *service, PebbleEvent *e) { + uint16_t intents_pending = 0; + + for (int i = 0; i < NumPebbleTask; i++) { + if (!prv_task_is_masked_out(e, i) && service->subscribers[i]) { + const PebbleTaskBitset task_bit = (1 << i); + intents_pending |= task_bit; + } + } + + if (intents_pending) { + EventServiceBuffer *esb = kernel_zalloc_check(sizeof(EventServiceBuffer)); + esb->ptr = buf; + esb->intents_pending = intents_pending; + list_init(&esb->list_node); + s_event_service_buffers = (EventServiceBuffer *)list_prepend( + (ListNode *)s_event_service_buffers, (ListNode *)esb); + return true; // we stole the buffer + } else { + return false; + } +} + +void event_service_handle_event(PebbleEvent *e) { + EventServiceEntry *service = s_event_services[e->type]; + if (service == NULL) { + return; + } + + bool stolen = false; + void **buf_ptr = event_get_buffer(e); + if (buf_ptr && *buf_ptr) { + stolen = prv_steal_buffer(*buf_ptr, service, e); // FIXME arguments? + } + + PebbleTask cur_task = pebble_task_get_current(); + for (unsigned i = 0; i < NumPebbleTask; i++) { + if (!prv_task_is_masked_out(e, i) && service->subscribers[i]) { + if (i == cur_task) { + // We will handle this inline, but that has to happen after we copy it into the queues + // because handling it inline could modify the event + continue; + } else { + if (!prv_event_service_send_event(service->subscribers[i], e)) { + PBL_LOG_INFO("Queue full! %d not delivered to task %d!", + (int)e->type, (int)i); +#ifndef CONFIG_RELEASE + // For 3rd party apps, just close them. For a 1st party app or other task, reboot + // the watch + if (i == PebbleTask_App && app_manager_get_current_app_md()->is_unprivileged) { + app_manager_close_current_app(false); + } else if (i == PebbleTask_Worker && + worker_manager_get_current_worker_md()->is_unprivileged) { + worker_manager_close_current_worker(false); + } else { + PBL_ASSERTN(0); + } +#endif + } + } + } + } + + if (!prv_task_is_masked_out(e, cur_task) && service->subscribers[cur_task]) { + // we are on the current task, so we just tell the client to handle it inline + event_service_client_handle_event(e); + } + + if (stolen) { + // we stole the buffer from the event, NULL it out + *buf_ptr = NULL; + } +} + +// ------------------------------------------------------------------------------------------------- +static bool prv_buffer_find(ListNode *found_node, void *data) { + EventServiceBuffer *esb = (EventServiceBuffer *)found_node; + return (esb->ptr == data); +} + +bool event_service_is_known_buffer(const void *buf) { + if (buf == NULL) { + return false; + } + // Cast away const for list_find's callback signature; prv_buffer_find only compares. + return list_find((ListNode *)s_event_service_buffers, prv_buffer_find, + (void *)buf) != NULL; +} + +static EventServiceBuffer* prv_get_esb_for_event(PebbleEvent *e) { + void **buf_ptr = event_get_buffer(e); + EventServiceBuffer *esb = NULL; + if (buf_ptr && *buf_ptr) { + esb = (EventServiceBuffer *)list_find((ListNode *)s_event_service_buffers, + prv_buffer_find, + *buf_ptr); + } + return esb; +} + +void* event_service_claim_buffer(PebbleEvent *e) { + EventServiceBuffer *esb = prv_get_esb_for_event(e); + if (esb) { + if (esb->intents_pending & CLAIMED_BIT) { + // For now only 1 claim at a time is needed, so lets keep things simple and just support that + PBL_LOG_WRN("Buffer already claimed"); + return NULL; + } else { + esb->intents_pending |= CLAIMED_BIT; + return esb; + } + } + return NULL; +} + +void event_service_free_claimed_buffer(void *ref) { + if (!ref) { + return; + } + + EventServiceBuffer *esb = ref; + + if (esb->intents_pending & CLAIMED_BIT) { + // If other events still need the buffer removing the claim marker will make things + // get cleaned up as usual. + uint16_t intents_pending = __sync_and_and_fetch(&esb->intents_pending, ~CLAIMED_BIT); + + if (!intents_pending) { + list_remove((ListNode *)esb, (ListNode **)&s_event_service_buffers, NULL); + if (esb->ptr) { + kernel_free(esb->ptr); + esb->ptr = NULL; + } + kernel_free(esb); + } + } +} + +// --------------------------------------------------------------------------------------------------------------- +static bool prv_service_filter(ListNode *node, void *tp) { + EventPluginUUIDEntry *info = (EventPluginUUIDEntry *)node; + return uuid_equal(&info->uuid, (Uuid *)tp); +} + +// --------------------------------------------------------------------------------------------------------------- +// TODO: We need to prune out entries from this list when they are no longer needed +// TODO: The applib should force a restriction on the number of plugin service UUIDs that an app can subscribe +// to at once. +static int16_t prv_get_plugin_index(const Uuid *uuid) { + int16_t result = -1; + + mutex_lock(s_plugin_list_mutex); + + // Look for this service UUID + ListNode *found; + ListNode *list = &s_plugin_list; + found = list_find(list, prv_service_filter, (void*)uuid); + if (found) { + result = ((EventPluginUUIDEntry *)found)->service_index; + goto unlock; + } + + EventPluginUUIDEntry *entry = kernel_zalloc_check(sizeof(EventPluginUUIDEntry)); + entry->service_index = ++s_next_service_index; + entry->uuid = *uuid; + list_append(list, &entry->list_node); + result = entry->service_index; + + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(uuid, uuid_buffer); + PBL_LOG_DBG("Registered plug-in service %s as index %d", uuid_buffer, result); + +unlock: + mutex_unlock(s_plugin_list_mutex); + return result; +} + + +//! @param uuid the UUID of the plugin service, or NULL to use uuid of the current process +//! @return non-negative service index, or -1 if error +DEFINE_SYSCALL(int16_t, sys_event_service_get_plugin_service_index, const Uuid * uuid) { + if (PRIVILEGE_WAS_ELEVATED && uuid != NULL) { + syscall_assert_userspace_buffer(uuid, sizeof(*uuid)); + } + if (uuid == NULL) { + uuid = &sys_process_manager_get_current_process_md()->uuid; + } + return prv_get_plugin_index(uuid); +} + +DEFINE_SYSCALL(void, sys_event_service_cleanup, PebbleEvent *e) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(e, sizeof(PebbleEvent)); + } + + EventServiceBuffer *esb = prv_get_esb_for_event(e); + + if (esb) { + uint16_t task_bit = 1 << pebble_task_get_current(); + uint16_t intents_pending = __sync_and_and_fetch(&esb->intents_pending, ~task_bit); + if (intents_pending) { + // zero out buf_ptr so it won't be freed by cleanup. Other tasks are still waiting to use it + void **buf_ptr = event_get_buffer(e); + *buf_ptr = NULL; + } else { + // free the EventServiceBuffer and free the data + list_remove((ListNode *)esb, (ListNode **)&s_event_service_buffers, NULL); + kernel_free(esb); + + event_deinit(e); + } + } +} diff --git a/src/fw/services/event_service/wscript_build b/src/fw/services/event_service/wscript_build new file mode 100644 index 0000000000..6cb18b0e2b --- /dev/null +++ b/src/fw/services/event_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_event_service', +) +bld.env.SERVICES.append('services_event_service') diff --git a/src/fw/services/evented_timer/Kconfig b/src/fw/services/evented_timer/Kconfig new file mode 100644 index 0000000000..742d4f778d --- /dev/null +++ b/src/fw/services/evented_timer/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_EVENTED_TIMER + bool "Evented timer" + help + Event-loop timer service. + +if SERVICE_EVENTED_TIMER + +module = SERVICE_EVENTED_TIMER +module-str = Evented timer +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/evented_timer/service.c b/src/fw/services/evented_timer/service.c new file mode 100644 index 0000000000..06869659af --- /dev/null +++ b/src/fw/services/evented_timer/service.c @@ -0,0 +1,347 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/evented_timer.h" + +#include "kernel/events.h" +#include "os/tick.h" +#include "os/mutex.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/passert.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "process_management/app_manager.h" + +PBL_LOG_MODULE_DEFINE(service_evented_timer, CONFIG_SERVICE_EVENTED_TIMER_LOG_LEVEL); + +typedef struct EventedTimer { + ListNode list_node; + + //! The TimerID type used for sys_timers is a non-repeating integer that we also use as our key for finding + //! EventedTimers by id. + TimerID sys_timer_id; + + EventedTimerCallback callback; + void* callback_data; + + PebbleTask target_task; + + bool expired; // This gets set when the timer's callback runs + bool repeating; +} EventedTimer; + +//! The list of all the timers that have been created. +static ListNode* s_timer_list_head; + +static PebbleMutex * s_mutex; + +// ------------------------------------------------------------------------------------ +// Find timer by id +static bool prv_id_list_filter(ListNode* node, void* data) { + EventedTimer* timer = (EventedTimer*)node; + return timer->sys_timer_id == (TimerID)data; +} + +static EventedTimer* prv_find_timer(TimerID timer_id) +{ + if (timer_id == EVENTED_TIMER_INVALID_ID) { + return NULL; + } + + // Look for this timer in our linked list + ListNode* node = list_find(s_timer_list_head, prv_id_list_filter, (void*)(intptr_t)timer_id); + + return (EventedTimer *)node; +} + + +//! Retrieves details for a given timer handle and copies them out to user supplied memory. +//! This gets executed on the client's task and is called directly from the callback we put onto the +//! client task's event queue. +//! It accesses the privileged contents of the timer from the client's unprivileged task. +//! This call deletes the system timer and removes it from the timer list before returning unless +//! it is a repeating timer. +DEFINE_SYSCALL(void, sys_evented_timer_consume, TimerID timer_id, EventedTimerCallback* out_cb, + void** out_cb_data) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(out_cb, sizeof(*out_cb)); + syscall_assert_userspace_buffer(out_cb_data, sizeof(*out_cb_data)); + } + + mutex_lock(s_mutex); + + EventedTimer *timer = prv_find_timer(timer_id); + + // It's possible that the client made a call to delete the timer just + // after the timer executed (from the timer task) and posted the PEBBLE_CALLBACK_EVENT + // to the client's event queue. In this case, the timer will no + // longer be in our timers list by the time the event arrives and gets processed here. + if (!timer) { + mutex_unlock(s_mutex); + *out_cb = 0; + return; + } + + // Only allow consuming a timer that belongs to the calling task. Without this + // check an unprivileged app could probe TimerIDs to find a system timer and + // read back its kernel callback pointer + data (KASLR-defeating info leak), + // and additionally cancel that timer (DoS). + if (PRIVILEGE_WAS_ELEVATED && timer->target_task != pebble_task_get_current()) { + mutex_unlock(s_mutex); + *out_cb = 0; + return; + } + + *out_cb = timer->callback; + *out_cb_data = timer->callback_data; + + if (timer->repeating) { + mutex_unlock(s_mutex); + } else { + list_remove(&timer->list_node, &s_timer_list_head, NULL); + mutex_unlock(s_mutex); + new_timer_delete(timer->sys_timer_id); + kernel_free(timer); + } +} + + +//! Wrapper for the user supplied callback. We installed this callback by posting a PEBBLE_CALLBACK_EVENT +//! to the client's event queue. This gets executed on the target task. +static void prv_evented_timer_event_callback(void* data) { + // Note this may be running on the app task, so we have to jump through hoops to read kernel memory. + + TimerID timer_id = (TimerID) data; + + EventedTimerCallback timer_cb; + void* timer_cb_data; + + // Get the user supplied callback pointer and data, remove the timer from our list, and delete it. + sys_evented_timer_consume(timer_id, &timer_cb, &timer_cb_data); + + if (!timer_cb) { + // We've already cancelled this timer, just abort. + return; + } + + timer_cb(timer_cb_data); +} + + +//! Called on the timer task. From here we need to generate a callback on the client's task. +static void prv_sys_timer_callback(void* cb_data) { + PBL_ASSERT_TASK(PebbleTask_NewTimers); + TimerID id = (TimerID)cb_data; + + mutex_lock(s_mutex); + + EventedTimer *timer = prv_find_timer(id); + if (!timer) { + // If there's no timer in the list, that means we've been cancelled already. + // When we cancel a timer, we immediately free the EventedTimer* and + // call new_time_delete() to delete the timer. It's possible however that this + // callback got entered after we grabbed the mutex but before we issued the + // new_timer_delete(), in which case we would have been blocked at the beginning + // of this method trying to grab the mutex while the timer was deleted. + // To handle this, we check here to see if the EventedTimer got deleted on us and + // return immediately if so. + mutex_unlock(s_mutex); + return; + } + + timer->expired = !timer->repeating; + + mutex_unlock(s_mutex); + + PebbleEvent e = { + .type = PEBBLE_CALLBACK_EVENT, + .callback = { + .callback = prv_evented_timer_event_callback, + .data = (void*)(intptr_t)id, + } + }; + + switch (timer->target_task) { + case PebbleTask_KernelMain: + event_put(&e); + break; + case PebbleTask_App: + case PebbleTask_Worker: + process_manager_send_event_to_process(timer->target_task, &e); + break; + default: + PBL_CROAK("Invalid task %s", pebble_task_get_name(pebble_task_get_current())); + } +} + + +// ======================================================================================================== +// External API + +void evented_timer_init(void) { + s_mutex = mutex_create(); +} + +void evented_timer_clear_process_timers(PebbleTask task) { + PBL_ASSERT_TASK(PebbleTask_KernelMain); + + mutex_lock(s_mutex); + + ListNode* iter = s_timer_list_head; + while (iter) { + EventedTimer* timer = (EventedTimer*) iter; + ListNode* next = list_get_next(iter); + + if (timer->target_task == task) { + list_remove(iter, &s_timer_list_head, NULL); + // The delete operation will stop it for us + new_timer_delete(timer->sys_timer_id); + kernel_free(timer); + } + + iter = next; + } + + mutex_unlock(s_mutex); +} + +EventedTimerID evented_timer_register_or_reschedule(EventedTimerID timer_id, uint32_t timeout_ms, + EventedTimerCallback callback, void *data) { + if (timer_id != EVENTED_TIMER_INVALID_ID && evented_timer_reschedule(timer_id, timeout_ms)) { + return timer_id; + } + return evented_timer_register(timeout_ms, false, callback, data); +} + +EventedTimerID evented_timer_register(uint32_t timeout_ms, + bool repeating, + EventedTimerCallback callback, + void* data) { + PebbleTask current_task = pebble_task_get_current(); + PBL_ASSERT(current_task == PebbleTask_KernelMain || current_task == PebbleTask_App + || current_task == PebbleTask_Worker, + "Invalid task: %s", pebble_task_get_name(current_task)); + + // Handle a lazy client. Timers are useful for handling things "not right now, but soon". + if (timeout_ms == 0) { + timeout_ms = 1; + } + + mutex_lock(s_mutex); + + EventedTimer* new_timer = kernel_malloc_check(sizeof(EventedTimer)); + + *new_timer = (EventedTimer) { + .list_node = { 0 }, + .sys_timer_id = TIMER_INVALID_ID, // We set this below + .callback = callback, + .callback_data = data, + .target_task = current_task, + .repeating = repeating, + .expired = false + }; + + new_timer->sys_timer_id = new_timer_create(); + PBL_ASSERTN(new_timer->sys_timer_id != TIMER_INVALID_ID); + + s_timer_list_head = list_prepend(s_timer_list_head, &new_timer->list_node); + + uint32_t flags = repeating ? TIMER_START_FLAG_REPEATING : 0; + bool success = new_timer_start(new_timer->sys_timer_id, timeout_ms, prv_sys_timer_callback, + (void*)(intptr_t)new_timer->sys_timer_id, flags); + PBL_ASSERTN(success); + + mutex_unlock(s_mutex); + return new_timer->sys_timer_id; +} + + +bool evented_timer_reschedule(EventedTimerID timer_id, uint32_t timeout_ms) { + if (timeout_ms == 0) { + timeout_ms = 1; + } + mutex_lock(s_mutex); + + // This will detect an invalid timer ID, or one that already ran on the client's task and got deleted + // already + EventedTimer* timer = prv_find_timer(timer_id); + if (!timer) { + PBL_LOG_DBG("Attempting to reschedule an invalid timer (id=%u)", + (unsigned)timer_id); + mutex_unlock(s_mutex); + return false; + } + + PBL_ASSERT(timer->target_task == pebble_task_get_current(), "%u vs %u", + timer->target_task, pebble_task_get_current()); + + // This will detect if the timer callback has already executed on the timer task. + // If the timer is still in our timer's list but is expired, + // it means the event posted by the timer task has not yet arrived at the + // client's task. + if (timer->expired) { + mutex_unlock(s_mutex); + return false; + } + + // At this point, we are assured that the timer callback either has not yet run + // or that the callback is currently blocked trying to grab s_mutex. + // new_timer_start() will reliably tell us if it was able to reschedule + // the timer before the callback got entered. + // If it returns false, it means the callback was entered before it was able to reschedule it. + uint32_t flags = timer->repeating ? TIMER_START_FLAG_REPEATING : + TIMER_START_FLAG_FAIL_IF_EXECUTING; + bool success = new_timer_start(timer->sys_timer_id, timeout_ms, prv_sys_timer_callback, + (void*)(intptr_t)timer->sys_timer_id, flags); + + mutex_unlock(s_mutex); + return success; +} + + +void evented_timer_cancel(EventedTimerID timer_id) { + if (timer_id == EVENTED_TIMER_INVALID_ID) { + return; + } + + mutex_lock(s_mutex); + + // Find this timer and validate it + EventedTimer* timer = prv_find_timer(timer_id); + if (!timer) { + PBL_LOG_DBG("Attempting to cancel an invalid timer (id=%u)", (unsigned)timer_id); + mutex_unlock(s_mutex); + return; + } + + new_timer_delete(timer->sys_timer_id); // This automatically stops the timer for us first + list_remove(&timer->list_node, &s_timer_list_head, NULL); + kernel_free(timer); + + mutex_unlock(s_mutex); +} + +bool evented_timer_exists(EventedTimerID timer_id){ + return prv_find_timer(timer_id) != NULL; +} + +bool evented_timer_is_current_task(EventedTimerID timer_id){ + EventedTimer* timer = prv_find_timer(timer_id); + PBL_ASSERTN(timer); + return timer->target_task == pebble_task_get_current(); +} + +void evented_timer_reset(void) { + s_timer_list_head = 0; +} + +void *evented_timer_get_data(EventedTimerID timer_id) { + EventedTimer* timer = prv_find_timer(timer_id); + if (timer) { + return timer->callback_data; + } else { + return NULL; + } +} diff --git a/src/fw/services/evented_timer/wscript_build b/src/fw/services/evented_timer/wscript_build new file mode 100644 index 0000000000..9758fdc2ca --- /dev/null +++ b/src/fw/services/evented_timer/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_evented_timer', +) +bld.env.SERVICES.append('services_evented_timer') diff --git a/src/fw/services/filesystem/Kconfig b/src/fw/services/filesystem/Kconfig new file mode 100644 index 0000000000..698f0005b5 --- /dev/null +++ b/src/fw/services/filesystem/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_FILESYSTEM + bool "Filesystem" + help + Pebble filesystem (PFS) service. + +if SERVICE_FILESYSTEM + +module = SERVICE_FILESYSTEM +module-str = Filesystem +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/filesystem/app_file.c b/src/fw/services/filesystem/app_file.c similarity index 76% rename from src/fw/services/normal/filesystem/app_file.c rename to src/fw/services/filesystem/app_file.c index 0f7ae2cbba..220f686243 100644 --- a/src/fw/services/normal/filesystem/app_file.c +++ b/src/fw/services/filesystem/app_file.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "app_file.h" +#include "pbl/services/filesystem/app_file.h" #include diff --git a/src/fw/services/normal/filesystem/flash_translation.c b/src/fw/services/filesystem/flash_translation.c similarity index 85% rename from src/fw/services/normal/filesystem/flash_translation.c rename to src/fw/services/filesystem/flash_translation.c index dbe046ef52..b914d815ef 100644 --- a/src/fw/services/normal/filesystem/flash_translation.c +++ b/src/fw/services/filesystem/flash_translation.c @@ -1,31 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "flash_translation.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/filesystem/flash_translation.h" #include "drivers/flash.h" #include "drivers/task_watchdog.h" #include "flash_region/filesystem_regions.h" #include "flash_region/flash_region.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" #include "util/size.h" +PBL_LOG_MODULE_DECLARE(service_filesystem, CONFIG_SERVICE_FILESYSTEM_LOG_LEVEL); + //! Flash translation operation typedef enum { FTLRead, @@ -65,7 +54,7 @@ static void prv_layout_version_add_all_regions(bool revert) { s_ftl_size += prv_region_size(i); } - PBL_LOG(LOG_LEVEL_DEBUG, "Filesystem: Temporary size - %"PRId32" Kb", (s_ftl_size / 1024)); + PBL_LOG_DBG("Filesystem: Temporary size - %"PRId32" Kb", (s_ftl_size / 1024)); pfs_set_size(s_ftl_size, false /* don't erase regions */); } @@ -105,8 +94,7 @@ void ftl_add_region(uint32_t region_start, uint32_t region_end, bool erase_new_r s_next_region_idx++; // failure, should never happen } else { - PBL_LOG(LOG_LEVEL_WARNING, - "Filesystem: Uh oh, we somehow added regions in the wrong order, %"PRIu32" %"PRIu32, + PBL_LOG_WRN("Filesystem: Uh oh, we somehow added regions in the wrong order, %"PRIu32" %"PRIu32, region_start, region_end); return; } @@ -125,7 +113,7 @@ void ftl_add_region(uint32_t region_start, uint32_t region_end, bool erase_new_r void ftl_populate_region_list(void) { uint8_t flash_layout_version = prv_ftl_get_layout_version(); - PBL_LOG(LOG_LEVEL_INFO, "Filesystem: Old Flash Layout Version: %u", flash_layout_version); + PBL_LOG_INFO("Filesystem: Old Flash Layout Version: %u", flash_layout_version); for (unsigned int i = s_next_region_idx; i < flash_layout_version; i++) { ftl_add_region(s_region_list[i].start, s_region_list[i].end, false); @@ -139,7 +127,7 @@ void ftl_populate_region_list(void) { ftl_add_region(s_region_list[i].start, s_region_list[i].end, true); } - PBL_LOG(LOG_LEVEL_DEBUG, "Filesystem: New size - %"PRId32" Kb", (s_ftl_size / 1024)); + PBL_LOG_DBG("Filesystem: New size - %"PRId32" Kb", (s_ftl_size / 1024)); } uint32_t ftl_get_size(void) { diff --git a/src/fw/services/normal/filesystem/pfs.c b/src/fw/services/filesystem/pfs.c similarity index 96% rename from src/fw/services/normal/filesystem/pfs.c rename to src/fw/services/filesystem/pfs.c index e69ecf1193..30e90e2b3f 100644 --- a/src/fw/services/normal/filesystem/pfs.c +++ b/src/fw/services/filesystem/pfs.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "pfs.h" +#include "pbl/services/filesystem/pfs.h" #include #include @@ -24,6 +11,7 @@ #include "console/prompt.h" #include "drivers/flash.h" +#include "drivers/rtc.h" #include "drivers/task_watchdog.h" #include "flash_region/filesystem_regions.h" #include "flash_region/flash_region.h" @@ -31,8 +19,8 @@ #include "kernel/pebble_tasks.h" #include "kernel/util/sleep.h" #include "os/mutex.h" -#include "services/common/analytics/analytics.h" -#include "services/normal/filesystem/flash_translation.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/filesystem/flash_translation.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/passert.h" @@ -41,6 +29,8 @@ #include "util/legacy_checksum.h" #include "util/math.h" +PBL_LOG_MODULE_DEFINE(service_filesystem, CONFIG_SERVICE_FILESYSTEM_LOG_LEVEL); + static PebbleRecursiveMutex *s_pfs_mutex = NULL; #define IS_FILE_TYPE(file_type, type) ((file_type) == (type)) @@ -246,7 +236,7 @@ static void prv_flash_read(void *buffer, uint32_t size, uint32_t offset) { if ((offset + size) <= s_pfs_size) { ftl_read(buffer, size, offset); } else { - PBL_LOG(LOG_LEVEL_ERROR, "FS read out of bounds 0x%x", (int)offset); + PBL_LOG_ERR("FS read out of bounds 0x%x", (int)offset); } } @@ -277,7 +267,7 @@ static void prv_flash_write(const void *buffer, uint32_t size, uint32_t offset) ftl_write(buffer, size, offset); prv_invalidate_page_flags_cache(offset, size); } else { - PBL_LOG(LOG_LEVEL_ERROR, "FS write out of bounds 0x%x", (int)offset); + PBL_LOG_ERR("FS write out of bounds 0x%x", (int)offset); } } @@ -288,7 +278,7 @@ static void prv_flash_erase_sector(uint16_t start_page) { ftl_erase_sector(PFS_PAGE_SIZE * PFS_PAGES_PER_ERASE_SECTOR, offset); prv_invalidate_page_flags_cache(offset, PFS_PAGE_SIZE * PFS_PAGES_PER_ERASE_SECTOR); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Erase out of bounds, 0x%x", (int)start_page); + PBL_LOG_ERR("Erase out of bounds, 0x%x", (int)start_page); } } @@ -424,7 +414,7 @@ static ReadHeaderStatus read_header(uint16_t page, PageHeader *pg_hdr, } if (pg_hdr->version > PFS_CUR_VERSION) { - PBL_LOG(LOG_LEVEL_ERROR, "Unexpected Version Header, 0x%x", + PBL_LOG_ERR("Unexpected Version Header, 0x%x", (int)pg_hdr->version); return (HdrVersionCheckFail); // let caller handle } @@ -491,7 +481,7 @@ static status_t locate_flash_file(const char *name, uint16_t *page) { if ((memcmp(name, file_name, namelen) == 0) && (!is_tmp_file(pg))) { if (read_header(pg, &pg_hdr, &file_hdr) == HdrCrcCorrupt) { - PBL_LOG(LOG_LEVEL_WARNING, "%d: CRC corrupt", pg); + PBL_LOG_WRN("%d: CRC corrupt", pg); continue; } @@ -552,13 +542,13 @@ static void update_last_written_page(void) { prv_page_to_flash_offset(pg) + offsetof(PageHeader, last_written)); if (hdr.last_written == LAST_WRITTEN_TAG) { s_last_page_written = pg; - PBL_LOG(LOG_LEVEL_INFO, "Last written page %d", (int)s_last_page_written); + PBL_LOG_INFO("Last written page %d", (int)s_last_page_written); return; } } // should only happen after a filesystem format - PBL_LOG(LOG_LEVEL_WARNING, "Couldn't resolve last written pg"); + PBL_LOG_WRN("Couldn't resolve last written pg"); s_last_page_written = s_pfs_page_count - 1; #if UNITTEST if (s_test_last_page_written_override != -1) { @@ -1073,7 +1063,7 @@ int pfs_read(int fd, void *buf_ptr, size_t size) { } if ((file->offset + size) > file->file_size) { - PBL_LOG(LOG_LEVEL_DEBUG, "Out of bound read at %d", + PBL_LOG_DBG("Out of bound read at %d", (int)(file->offset + size)); res = E_RANGE; goto cleanup; @@ -1103,7 +1093,7 @@ int pfs_read(int fd, void *buf_ptr, size_t size) { pg_offset = 0; // first usable byte next page if (get_next_page(file->curr_page, &file->curr_page) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "R:Couldn't find next page for %d", + PBL_LOG_WRN("R:Couldn't find next page for %d", file->curr_page); res = E_INTERNAL; goto cleanup; @@ -1193,7 +1183,7 @@ int pfs_write(int fd, const void *buf_ptr, size_t size) { pg_offset = 0; // first usable byte next page if (get_next_page(file->curr_page, &file->curr_page) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "W:Couldn't find next page for %d", + PBL_LOG_WRN("W:Couldn't find next page for %d", file->curr_page); res = E_INTERNAL; goto cleanup; @@ -1244,7 +1234,7 @@ bool pfs_active_in_region(uint32_t start_address, uint32_t ending_address) { } if (hdr.version > PFS_CUR_VERSION) { - PBL_LOG(LOG_LEVEL_WARNING, "Incompatible version of PFS active, 0x%x", + PBL_LOG_WRN("Incompatible version of PFS active, 0x%x", hdr.version); // pfs filesystem is a newer version than we support @@ -1282,16 +1272,16 @@ void pfs_reboot_cleanup(void) { if (IS_PAGE_TYPE(page_flags, PAGE_FLAG_START_PAGE)) { if (!is_create_complete(curr_pg)) { // make sure file creation completed - PBL_LOG(LOG_LEVEL_WARNING, "File at %d creation did not complete ", + PBL_LOG_WRN("File at %d creation did not complete ", curr_pg); unlink_flash_file(curr_pg); } else if (is_tmp_file(curr_pg)) { // make sure this isn't a temp file - PBL_LOG(LOG_LEVEL_WARNING, "Removing temp file at %d", curr_pg); + PBL_LOG_WRN("Removing temp file at %d", curr_pg); unlink_flash_file(curr_pg); } } else if (page_type_bits_set(page_flags, DELETED_START_PAGE_MASK) && !is_delete_complete(curr_pg)) { - PBL_LOG(LOG_LEVEL_WARNING, "Delete of %d did not complete", curr_pg); + PBL_LOG_WRN("Delete of %d did not complete", curr_pg); unlink_flash_file(curr_pg); } } @@ -1341,7 +1331,7 @@ static bool prv_update_gc_reserved_region(void) { .block_writes = 0, .gc_start_page = free_region_start }; - PBL_LOG(LOG_LEVEL_DEBUG, "New Erase Region: %d", s_gc_block.gc_start_page); + PBL_LOG_DBG("New Erase Region: %d", s_gc_block.gc_start_page); return (true); } @@ -1426,7 +1416,17 @@ status_t pfs_close(int fd) { File *f = &PFS_FD(fd).file; if (f->is_tmp) { - // TODO: For safety, could disallow this op if user has orig file hdl open + // If the original file is still open, force-evict it before removing. + // This prevents pfs_remove() from CROAKing on the in-use original fd. + // Callers should close the original before the temp, but if they don't, + // we handle it gracefully here rather than crashing. + int orig_fd; + if (get_avail_fd(f->name, &orig_fd, false) == FDBusy) { + PBL_LOG_ERR("Original file %s still open when closing temp, force-evicting", + f->name); + PFS_FD(orig_fd).fd_status = FD_STATUS_UNREFERENCED; + PFS_FD(orig_fd).time_closed = time_closed_counter++; + } pfs_remove(f->name); // Note: if we reboot before updating the tmp state flag to done, the tmp & // original file will be deleted. This is an extremely small window, but @@ -1541,7 +1541,7 @@ PFSFileListEntry *pfs_create_file_list(PFSFilenameTestCallback callback) { // filename filter call because it requires more flash reads and is likely slower than the // filter call. if (read_header(pg, &pg_hdr, &file_hdr) != PageAndFileHdrValid) { - PBL_LOG(LOG_LEVEL_WARNING, "%d: Invalid page/file header", pg); + PBL_LOG_WRN("%d: Invalid page/file header", pg); continue; } @@ -1598,7 +1598,7 @@ void pfs_remove_files(PFSFilenameTestCallback callback) { // filename filter call because it requires more flash reads and is likely slower than the // filter call. if (read_header(pg, &pg_hdr, &file_hdr) != PageAndFileHdrValid) { - PBL_LOG(LOG_LEVEL_WARNING, "%d: Invalid page/file header", pg); + PBL_LOG_WRN("%d: Invalid page/file header", pg); continue; } @@ -1803,7 +1803,7 @@ static NOINLINE status_t pfs_open_handle_read_request(int fd, uint16_t page) { return (S_SUCCESS); } - PBL_LOG(LOG_LEVEL_WARNING, "Could not read header %d", hdr_rv); + PBL_LOG_WRN("Could not read header %d", hdr_rv); return (E_INTERNAL); } @@ -2077,7 +2077,7 @@ status_t pfs_init(bool run_filesystem_check) { if (run_filesystem_check) { if (!pfs_active()) { // either we have downgraded or there is no data on the flash - PBL_LOG(LOG_LEVEL_INFO, "PFS not active ... formatting"); + PBL_LOG_INFO("PFS not active ... formatting"); pfs_format(true /* write erase headers */); } } @@ -2088,13 +2088,13 @@ status_t pfs_init(bool run_filesystem_check) { int fd; if ((fd = pfs_open_gc_file(0, false)) >= S_SUCCESS) { // we rebooted while we were in the middle of a garbage collection - PBL_LOG(LOG_LEVEL_INFO, "Recovering flash region from GC file"); + PBL_LOG_INFO("Recovering flash region from GC file"); recover_region_from_file(fd); } // find a free region if (!prv_update_gc_reserved_region()) { - PBL_LOG(LOG_LEVEL_ERROR, "No free flash erase units!"); + PBL_LOG_ERR("No free flash erase units!"); // Note: It should not be possible for this to happen since start of day no // files will be written on then flash. We could also try to force apps to // be flushed out of the FS in an attempt to free up space since they are @@ -2106,7 +2106,7 @@ status_t pfs_init(bool run_filesystem_check) { // the filesystem. We do a lot of initialization from different threads early // during boot flow. This prevents those threads from blocking each other uint32_t bytes_to_free = ((s_pfs_page_count * PFS_PAGE_SIZE) * 4) / 100; - PBL_LOG(LOG_LEVEL_DEBUG, "Preparing %"PRIu32" bytes of flash for filesystem use", + PBL_LOG_DBG("Preparing %"PRIu32" bytes of flash for filesystem use", bytes_to_free); pfs_prepare_for_file_creation(bytes_to_free, 15 * RTC_TICKS_HZ); @@ -2115,7 +2115,7 @@ status_t pfs_init(bool run_filesystem_check) { } void pfs_format(bool write_erase_headers) { - PBL_LOG(LOG_LEVEL_INFO, "FS-Format Start"); + PBL_LOG_INFO("FS-Format Start"); mutex_lock_recursive(s_pfs_mutex); for (int i = FD_INDEX_OFFSET; i < FD_INDEX_OFFSET+PFS_FD_SET_SIZE; i++) { @@ -2131,7 +2131,7 @@ void pfs_format(bool write_erase_headers) { } mutex_unlock_recursive(s_pfs_mutex); - PBL_LOG(LOG_LEVEL_INFO, "FS-Format Done"); + PBL_LOG_INFO("FS-Format Done"); } @@ -2202,10 +2202,9 @@ uint32_t pfs_crc_calculate_file(int fd, uint32_t offset, uint32_t num_bytes) { return (crc); } -void analytics_external_collect_pfs_stats(void) { +void pbl_analytics_external_collect_pfs_stats(void) { uint16_t avail_kilobytes = (uint16_t)(get_available_pfs_space() / 1024); - analytics_set(ANALYTICS_DEVICE_METRIC_PFS_SPACE_FREE_KB, - avail_kilobytes, AnalyticsClient_System); + PBL_ANALYTICS_SET_UNSIGNED(pfs_space_free_kb, avail_kilobytes); } /* diff --git a/src/fw/services/filesystem/wscript_build b/src/fw/services/filesystem/wscript_build new file mode 100644 index 0000000000..3646866692 --- /dev/null +++ b/src/fw/services/filesystem/wscript_build @@ -0,0 +1,16 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'app_file.c', + 'flash_translation.c', + 'pfs.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_filesystem', +) + +bld.env.SERVICES.append('services_filesystem') diff --git a/src/fw/services/firmware_update/Kconfig b/src/fw/services/firmware_update/Kconfig new file mode 100644 index 0000000000..d8d8714a5b --- /dev/null +++ b/src/fw/services/firmware_update/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_FIRMWARE_UPDATE + bool "Firmware update" + help + Firmware update endpoint/state machine. + +if SERVICE_FIRMWARE_UPDATE + +module = SERVICE_FIRMWARE_UPDATE +module-str = Firmware update +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/firmware_update/service.c b/src/fw/services/firmware_update/service.c new file mode 100644 index 0000000000..018ce2a738 --- /dev/null +++ b/src/fw/services/firmware_update/service.c @@ -0,0 +1,306 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/firmware_update.h" + +#include "apps/core/progress_ui.h" +#include "flash_region/flash_region.h" +#include "kernel/event_loop.h" +#include "kernel/system_message.h" +#include "kernel/ui/modals/modal_manager.h" +#include "process_management/app_manager.h" +#include "process_management/app_manager.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/system_task.h" +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/powermode_service.h" +#endif +#include "pbl/services/runlevel.h" +#include "system/bootbits.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/reset.h" +#include "util/math.h" + +#include "FreeRTOS.h" +#include "semphr.h" + +#include +#include +#include + +PBL_LOG_MODULE_DEFINE(service_firmware_update, CONFIG_SERVICE_FIRMWARE_UPDATE_LOG_LEVEL); + +// The legacy firmware UI breaks firmware and resources into 50% chunks. In reality since these +// parts are not of equal sizes, one of these '50%' blocks will take longer than the +// other. Additionally, iOS/Android and the watch are not in sync over what this should look like. +// +// The new UI allows the phone to give us more info up front about how much data will be +// transmitted and also cleanly drive a re-start of the UI to a non-0 percentage if the FW update +// is being resumed. Newer implementations should this! (See PBL-42130) + +static SemaphoreHandle_t s_firmware_update_semaphore; +static bool s_is_recovery_fw = false; +static FirmwareUpdateStatus s_update_status = FirmwareUpdateStopped; + +typedef struct LegacyFwUpdateCompletionStatus { + uint32_t recovery_percent_completion; + uint32_t resource_percent_completion; + uint32_t firmware_percent_completion; +} LegacyFwUpdateCompletionStatus; + +typedef struct FwUpdateCompletionStatus { + uint32_t bytes_transferred; + uint32_t total_size; +} FwUpdateCompletionStatus; + +typedef struct { + bool use_legacy_mode; + union { + FwUpdateCompletionStatus status; + LegacyFwUpdateCompletionStatus legacy_status; + }; +} FwUpdateCurrentCompletionStatus; + +static FwUpdateCurrentCompletionStatus s_current_completion_status = { 0 }; + +// +// Start handlers for legacy percentage status handling. Someday, we can hopefully +// remove them outright and it should just involve deleting these routines +// + +static bool prv_legacy_firmware_update_get_percent_progress(uint32_t *progress) { + if (!s_current_completion_status.use_legacy_mode) { + return false; + } + + LegacyFwUpdateCompletionStatus *status = &s_current_completion_status.legacy_status; + if (s_is_recovery_fw) { + *progress = MIN(100, status->recovery_percent_completion); + } else { + *progress = + MIN(100, (status->resource_percent_completion + status->firmware_percent_completion) / 2); + } + + return true; +} + +static bool prv_legacy_handle_progress(PebblePutBytesEvent *event) { + if (!s_current_completion_status.use_legacy_mode) { + return false; + } + + LegacyFwUpdateCompletionStatus *status = &s_current_completion_status.legacy_status; + switch (event->object_type) { + case ObjectFirmware: + status->firmware_percent_completion = event->progress_percent; + break; + + case ObjectSysResources: + status->resource_percent_completion = event->progress_percent; + break; + + case ObjectRecovery: + status->recovery_percent_completion = event->progress_percent; + break; + + default: + PBL_LOG_ERR("Unexpected Object type %u", event->object_type); + break; + } + + return true; +} + +static bool prv_legacy_completion_status_init(PebbleSystemMessageEvent *event) { + if (event->type != PebbleSystemMessageFirmwareUpdateStartLegacy) { + return false; + } + + s_current_completion_status.use_legacy_mode = true; + LegacyFwUpdateCompletionStatus *status = + &s_current_completion_status.legacy_status; + + *status = (LegacyFwUpdateCompletionStatus) { + .recovery_percent_completion = 0, + .resource_percent_completion = 0, + .firmware_percent_completion = 0 + }; + + return true; +} + +// End Legacy completion handlers + +bool firmware_update_is_in_progress(void) { + return s_update_status == FirmwareUpdateRunning; +} + +FirmwareUpdateStatus firmware_update_current_status(void) { + return s_update_status; +} + +void firmware_update_init(void) { + vSemaphoreCreateBinary(s_firmware_update_semaphore); + PBL_ASSERTN(s_firmware_update_semaphore != NULL); +} + +static void prv_initialize_completion_status(PebbleSystemMessageEvent *event) { + if (prv_legacy_completion_status_init(event)) { + return; + } + + s_current_completion_status.use_legacy_mode = false; + FwUpdateCompletionStatus *status = &s_current_completion_status.status; + *status = (FwUpdateCompletionStatus) { + .bytes_transferred = event->bytes_transferred, + .total_size = event->total_transfer_size + }; +} + +// Initialization for a firmware update could involve an erase of 8 flash +// sectors. Worst case timing for an erase is ~5s, so let's set our timeout to +// 40s to give us some headroom. +#define FIRMWARE_TIMEOUT_MS (1000 * 40) + +static FirmwareUpdateStatus prv_firmware_update_start(PebbleSystemMessageEvent *event) { + if (battery_monitor_critical_lockout()) { + return FirmwareUpdateCancelled; // Disable firmware updates on low power + } + + if (xSemaphoreTake(s_firmware_update_semaphore, 0) == pdFALSE) { + return FirmwareUpdateStopped; + } + + FirmwareUpdateStatus result = s_update_status; + if (result != FirmwareUpdateRunning) { + prv_initialize_completion_status(event); + + services_set_runlevel(RunLevel_FirmwareUpdate); + modal_manager_pop_all(); + + static const ProgressUIAppArgs s_update_args = { + .progress_source = PROGRESS_UI_SOURCE_FW_UPDATE, + }; + app_manager_launch_new_app(&(AppLaunchConfig) { + .md = progress_ui_app_get_info(), + .common.args = &s_update_args, + .restart = true, + }); + put_bytes_expect_init(FIRMWARE_TIMEOUT_MS); +#ifndef CONFIG_RECOVERY_FW + powermode_service_request_hp(); +#endif + result = FirmwareUpdateRunning; + } + + xSemaphoreGive(s_firmware_update_semaphore); + return result; +} + +static void prv_handle_firmware_update_start_msg(PebbleSystemMessageEvent *event) { + FirmwareUpdateStatus result = prv_firmware_update_start(event); + s_update_status = result; + PBL_ASSERTN((result == FirmwareUpdateRunning) || + (result == FirmwareUpdateStopped) || + (result == FirmwareUpdateCancelled)); + system_message_send_firmware_start_response(result); +} + +static void prv_firmware_update_finish(bool failed) { + if (xSemaphoreTake(s_firmware_update_semaphore, 0) == pdFALSE) { + return; + } + + if (failed) { + // If we failed, we can set it back to normal. If we succeeded, we'll reboot shortly. + // We don't know the runlevel that was set before, so we assume it was Normal. + services_set_runlevel(RunLevel_Normal); + } + + s_update_status = failed ? FirmwareUpdateFailed : FirmwareUpdateStopped; +#ifndef CONFIG_RECOVERY_FW + powermode_service_release_hp(); +#endif + + xSemaphoreGive(s_firmware_update_semaphore); +} + +unsigned int firmware_update_get_percent_progress(void) { + if (!firmware_update_is_in_progress()) { + return 0; + } + + uint32_t progress = 0; + if (prv_legacy_firmware_update_get_percent_progress(&progress)) { + return progress; + } + + FwUpdateCompletionStatus *status = &s_current_completion_status.status; + return (status->bytes_transferred * 100) / status->total_size; +} + +void firmware_update_event_handler(PebbleSystemMessageEvent* event) { + switch (event->type) { + case PebbleSystemMessageFirmwareUpdateStartLegacy: + case PebbleSystemMessageFirmwareUpdateStart: + prv_handle_firmware_update_start_msg(event); + break; + + case PebbleSystemMessageFirmwareUpdateFailed: + prv_firmware_update_finish(true /* failed */); + break; + + case PebbleSystemMessageFirmwareUpdateComplete: + prv_firmware_update_finish(false /* failed */); + break; + + default: + break; + } +} + +static void prv_handle_progress(PebblePutBytesEvent *event) { + if (prv_legacy_handle_progress(event)) { + return; + } + + if (event->type != PebblePutBytesEventTypeProgress) { + return; // Only progress events report bytes_transferred updates + } + + FwUpdateCompletionStatus *status = &s_current_completion_status.status; + status->bytes_transferred += event->bytes_transferred; +} + +void firmware_update_pb_event_handler(PebblePutBytesEvent *event) { + if (!firmware_update_is_in_progress()) { + return; // not my pb transfer + } + + switch (event->type) { + case PebblePutBytesEventTypeStart: + s_is_recovery_fw = (event->object_type == ObjectRecovery); + prv_handle_progress(event); + break; + + case PebblePutBytesEventTypeProgress: + prv_handle_progress(event); + break; + + case PebblePutBytesEventTypeCleanup: + if (event->failed) { + // exit now in case the phone is gone + prv_firmware_update_finish(true /* failed */); + } + break; + + case PebblePutBytesEventTypeInitTimeout: + PBL_LOG_WRN("Timed out waiting for putbytes request from phone"); + prv_firmware_update_finish(true /* failed */); + break; + + default: + break; + } +} diff --git a/src/fw/services/firmware_update/wscript_build b/src/fw/services/firmware_update/wscript_build new file mode 100644 index 0000000000..aefeb9ea80 --- /dev/null +++ b/src/fw/services/firmware_update/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_firmware_update', +) +bld.env.SERVICES.append('services_firmware_update') diff --git a/src/fw/services/get_bytes/Kconfig b/src/fw/services/get_bytes/Kconfig new file mode 100644 index 0000000000..801afcdec5 --- /dev/null +++ b/src/fw/services/get_bytes/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_GET_BYTES + bool "Get bytes" + help + get_bytes endpoint (file/flash/coredump retrieval). + +if SERVICE_GET_BYTES + +module = SERVICE_GET_BYTES +module-str = Get bytes +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/common/get_bytes/get_bytes.c b/src/fw/services/get_bytes/get_bytes.c similarity index 85% rename from src/fw/services/common/get_bytes/get_bytes.c rename to src/fw/services/get_bytes/get_bytes.c index 1f9a196537..7c50f961a5 100644 --- a/src/fw/services/common/get_bytes/get_bytes.c +++ b/src/fw/services/get_bytes/get_bytes.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "get_bytes_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/get_bytes/get_bytes_private.h" #include "comm/bluetooth_analytics.h" #include "drivers/flash.h" @@ -22,10 +9,10 @@ #include "flash_region/flash_region.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/get_bytes/get_bytes_storage.h" -#include "services/common/system_task.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/get_bytes/get_bytes_storage.h" +#include "pbl/services/system_task.h" +#include "pbl/services/filesystem/pfs.h" #include "system/hexdump.h" #include "system/logging.h" #include "util/math.h" @@ -41,6 +28,8 @@ #include #include +PBL_LOG_MODULE_DEFINE(service_get_bytes, CONFIG_SERVICE_GET_BYTES_LOG_LEVEL); + // Internal state used by the protocol handler. typedef struct { CommSession *session; @@ -86,7 +75,7 @@ static bool prv_protocol_send_err_response(CommSession *session, int8_t transact sizeof(rsp), COMM_SESSION_DEFAULT_TIMEOUT); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "GET_BYTES: aborted"); + PBL_LOG_ERR("GET_BYTES: aborted"); s_get_bytes_in_progress = false; return false; } @@ -103,7 +92,7 @@ static bool prv_protocol_send_err_response(CommSession *session, int8_t transact static void prv_gather_and_record_stats(GetBytesState *state) { uint32_t elapsed_time_ms = ticks_to_milliseconds(rtc_get_ticks() - state->start_ticks); uint32_t bytes_per_sec = ((state->num_bytes * MS_PER_SECOND) / elapsed_time_ms); - PBL_LOG(LOG_LEVEL_DEBUG, "GET_BYTES: Done sending data. Pushed %"PRIu32" bytes/sec", + PBL_LOG_DBG("GET_BYTES: Done sending data. Pushed %"PRIu32" bytes/sec", bytes_per_sec); bluetooth_analytics_handle_get_bytes_stats( state->object_type, state->num_bytes, elapsed_time_ms, &state->conn_event_stats); @@ -124,7 +113,7 @@ static void prv_protocol_send_next_chunk(void* raw_state) { goto cleanup; } state->num_bytes = size; - PBL_LOG(LOG_LEVEL_DEBUG, "GET_BYTES: total bytes: %ld", state->num_bytes); + PBL_LOG_DBG("GET_BYTES: total bytes: %ld", state->num_bytes); } // ------------------------------------------------------------------------------------------- @@ -170,7 +159,7 @@ static void prv_protocol_send_next_chunk(void* raw_state) { comm_session_send_buffer_write(sb, (const uint8_t *) rsp, packet_len); kernel_free(rsp); - PBL_LOG(LOG_LEVEL_DEBUG, "GET_BYTES: sending next %d bytes. %d remaining", (int)data_len, + PBL_LOG_DBG("GET_BYTES: sending next %d bytes. %d remaining", (int)data_len, (int)(remaining_bytes-data_len)); } else { // Send image info response @@ -226,18 +215,18 @@ bool prv_setup_state_for_command(GetBytesCmd cmd, GetBytesState *state, return gb_storage_setup(&state->storage, state->object_type, &info); // if we are on a release build, don't allow the user to retrieve files or read the flash -#ifndef RELEASE +#ifndef CONFIG_RELEASE case GET_BYTES_CMD_GET_FILE: { state->object_type = GetBytesObjectFile; // copy the filename GetBytesFileHeader *hdr = (GetBytesFileHeader *)data; if ((hdr->filename_len + sizeof(GetBytesFileHeader) + 1) < len) { - PBL_LOG(LOG_LEVEL_ERROR, "Filename len does not match message length %d", + PBL_LOG_ERR("Filename len does not match message length %d", hdr->filename_len); return false; } if (hdr->filename[hdr->filename_len] != '\0') { - PBL_LOG(LOG_LEVEL_ERROR, "Non NULL terminated filename"); + PBL_LOG_ERR("Non NULL terminated filename"); return false; } info.filename = hdr->filename; @@ -249,7 +238,7 @@ bool prv_setup_state_for_command(GetBytesCmd cmd, GetBytesState *state, GetBytesFlashHeader *hdr = (GetBytesFlashHeader *)data; info.flash_start_addr = ntohl(hdr->start_addr); info.flash_len = ntohl(hdr->len); - PBL_LOG(LOG_LEVEL_DEBUG, "Fetching %d bytes starting at %d", (int)info.flash_len, + PBL_LOG_DBG("Fetching %d bytes starting at %d", (int)info.flash_len, (int)info.flash_start_addr); bool rv = gb_storage_setup(&state->storage, state->object_type, &info); return rv; @@ -265,7 +254,7 @@ void get_bytes_protocol_msg_callback(CommSession *session, const uint8_t* msg_da uint32_t msg_len) { // at least have a cmd and a transaction_id if (msg_len < 2) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length %"PRIu32, msg_len); + PBL_LOG_ERR("Invalid length %"PRIu32, msg_len); prv_protocol_send_err_response(session, 0 /*transaction_id*/, GET_BYTES_MALFORMED_COMMAND); return; @@ -279,14 +268,14 @@ void get_bytes_protocol_msg_callback(CommSession *session, const uint8_t* msg_da case GET_BYTES_CMD_GET_NEW_COREDUMP: break; default: - PBL_LOG(LOG_LEVEL_ERROR, "first byte can't be %u", hdr->cmd_id); + PBL_LOG_ERR("first byte can't be %u", hdr->cmd_id); prv_protocol_send_err_response(session, hdr->transaction_id, GET_BYTES_MALFORMED_COMMAND); return; } if (s_get_bytes_in_progress == true) { - PBL_LOG(LOG_LEVEL_ERROR, "already in progress."); + PBL_LOG_ERR("already in progress."); prv_protocol_send_err_response(session, hdr->transaction_id, GET_BYTES_ALREADY_IN_PROGRESS); return; @@ -308,7 +297,6 @@ void get_bytes_protocol_msg_callback(CommSession *session, const uint8_t* msg_da s_get_bytes_in_progress = true; prv_put_status_event(DebugInfoStateStarted); state->start_ticks = rtc_get_ticks(); - bt_driver_analytics_get_conn_event_stats(&state->conn_event_stats); prv_protocol_send_next_chunk(state); } diff --git a/src/fw/services/common/get_bytes/get_bytes_storage.c b/src/fw/services/get_bytes/get_bytes_storage.c similarity index 78% rename from src/fw/services/common/get_bytes/get_bytes_storage.c rename to src/fw/services/get_bytes/get_bytes_storage.c index 49aa560a31..fdea0b9486 100644 --- a/src/fw/services/common/get_bytes/get_bytes_storage.c +++ b/src/fw/services/get_bytes/get_bytes_storage.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "get_bytes_storage.h" +#include "pbl/services/get_bytes/get_bytes_storage.h" #include "kernel/pbl_malloc.h" #include "system/passert.h" @@ -33,9 +20,9 @@ typedef struct GetBytesStorageImplementation { //! List of storage implementations and their functions //! If one is not included in PRF, then it is ifdef'ed out. -#include "get_bytes_storage_coredump.h" -#include "get_bytes_storage_file.h" -#include "get_bytes_storage_flash.h" +#include "pbl/services/get_bytes/get_bytes_storage_coredump.h" +#include "pbl/services/get_bytes/get_bytes_storage_file.h" +#include "pbl/services/get_bytes/get_bytes_storage_flash.h" static const GetBytesStorageImplementation s_get_bytes_impls[] = { { @@ -46,7 +33,7 @@ static const GetBytesStorageImplementation s_get_bytes_impls[] = { .read_next_chunk = gb_storage_coredump_read_next_chunk, .cleanup = gb_storage_coredump_cleanup }, -#if !defined(RECOVERY_FW) && !defined(RELEASE) +#if !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_RELEASE) { // Filesystem File Storage .type = GetBytesStorageTypeFile, @@ -63,7 +50,7 @@ static const GetBytesStorageImplementation s_get_bytes_impls[] = { .read_next_chunk = gb_storage_flash_read_next_chunk, .cleanup = gb_storage_flash_cleanup }, -#endif /* !defined(RECOVERY_FW) && !defined(RELEASE) */ +#endif /* !defined(CONFIG_RECOVERY_FW) && !defined(CONFIG_RELEASE) */ }; //! Return the storage type for the object type. diff --git a/src/fw/services/common/get_bytes/get_bytes_storage_coredump.c b/src/fw/services/get_bytes/get_bytes_storage_coredump.c similarity index 83% rename from src/fw/services/common/get_bytes/get_bytes_storage_coredump.c rename to src/fw/services/get_bytes/get_bytes_storage_coredump.c index ffb47835c9..fc68af8695 100644 --- a/src/fw/services/common/get_bytes/get_bytes_storage_coredump.c +++ b/src/fw/services/get_bytes/get_bytes_storage_coredump.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "get_bytes_storage_file.h" -#include "get_bytes_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/get_bytes/get_bytes_storage_file.h" +#include "pbl/services/get_bytes/get_bytes_private.h" #include "drivers/flash.h" #include "flash_region/flash_region.h" @@ -24,6 +11,8 @@ #include "system/logging.h" #include "system/status_codes.h" +PBL_LOG_MODULE_DECLARE(service_get_bytes, CONFIG_SERVICE_GET_BYTES_LOG_LEVEL); + typedef struct { uint32_t core_dump_base; bool only_get_new_coredump; @@ -89,7 +78,7 @@ GetBytesInfoErrorCode gb_storage_coredump_get_size(GetBytesStorage *storage, uin // Get the base address in flash uint32_t flash_base = prv_coredump_flash_base(data->only_get_new_coredump); - PBL_LOG(LOG_LEVEL_DEBUG, "GET_BYTES: checking image %p", (void *)flash_base); + PBL_LOG_DBG("GET_BYTES: checking image %p", (void *)flash_base); if (flash_base != CORE_DUMP_FLASH_INVALID_ADDR) { flash_read_bytes((uint8_t *)&image_hdr, flash_base + sizeof(CoreDumpFlashRegionHeader), sizeof(image_hdr)); diff --git a/src/fw/services/get_bytes/get_bytes_storage_file.c b/src/fw/services/get_bytes/get_bytes_storage_file.c new file mode 100644 index 0000000000..7897cbe898 --- /dev/null +++ b/src/fw/services/get_bytes/get_bytes_storage_file.c @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/get_bytes/get_bytes_storage_file.h" + +#include "pbl/services/filesystem/pfs.h" + +bool gb_storage_file_setup(GetBytesStorage *storage, GetBytesObjectType object_type, + GetBytesStorageInfo *info) { + const int fd = pfs_open(info->filename, OP_FLAG_READ, FILE_TYPE_STATIC, 0); + storage->impl_data = (void*)(uintptr_t) fd; + return fd >= 0; +} + +GetBytesInfoErrorCode gb_storage_file_get_size(GetBytesStorage *storage, uint32_t *size) { + const int fd = (int) storage->impl_data; + *size = pfs_get_file_size(fd); + return GET_BYTES_OK; +} + +bool gb_storage_file_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len) { + const int fd = (int) storage->impl_data; + storage->current_offset += len; + return (pfs_read(fd, buffer, len) > 0); +} + +void gb_storage_file_cleanup(GetBytesStorage *storage, bool successful) { + const int fd = (int) storage->impl_data; + pfs_close(fd); +} diff --git a/src/fw/services/get_bytes/get_bytes_storage_flash.c b/src/fw/services/get_bytes/get_bytes_storage_flash.c new file mode 100644 index 0000000000..038d9c85a6 --- /dev/null +++ b/src/fw/services/get_bytes/get_bytes_storage_flash.c @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/get_bytes/get_bytes_storage.h" +#include "drivers/flash.h" +#include "kernel/pbl_malloc.h" +#include "flash_region/flash_region.h" + + +bool gb_storage_flash_setup( + GetBytesStorage *storage, GetBytesObjectType object_type, GetBytesStorageInfo *info) { + if (!info->flash_len || (info->flash_start_addr + info->flash_len) > BOARD_NOR_FLASH_SIZE) { + return false; + } + storage->impl_data = kernel_zalloc_check(sizeof(*info)); + memcpy(storage->impl_data, info, sizeof(*info)); + + return true; +} + +GetBytesInfoErrorCode gb_storage_flash_get_size(GetBytesStorage *storage, uint32_t *size) { + *size = ((GetBytesStorageInfo *)storage->impl_data)->flash_len; + return GET_BYTES_OK; +} + +bool gb_storage_flash_read_next_chunk(GetBytesStorage *storage, uint8_t *buffer, uint32_t len) { + uint32_t start_offset = ((GetBytesStorageInfo *)storage->impl_data)->flash_start_addr; + flash_read_bytes(buffer, storage->current_offset + start_offset, len); + storage->current_offset += len; + return true; +} + +void gb_storage_flash_cleanup(GetBytesStorage *storage, bool successful) { + kernel_free(storage->impl_data); +} diff --git a/src/fw/services/get_bytes/wscript_build b/src/fw/services/get_bytes/wscript_build new file mode 100644 index 0000000000..f938c9922a --- /dev/null +++ b/src/fw/services/get_bytes/wscript_build @@ -0,0 +1,18 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'get_bytes.c', + 'get_bytes_storage.c', + 'get_bytes_storage_coredump.c', + 'get_bytes_storage_file.c', + 'get_bytes_storage_flash.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_get_bytes', +) + +bld.env.SERVICES.append('services_get_bytes') diff --git a/src/fw/services/health_sync_endpoint/Kconfig b/src/fw/services/health_sync_endpoint/Kconfig new file mode 100644 index 0000000000..1a91d8fb8f --- /dev/null +++ b/src/fw/services/health_sync_endpoint/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_HEALTH_SYNC_ENDPOINT + bool "Health sync endpoint" + help + Health data sync endpoint. + +if SERVICE_HEALTH_SYNC_ENDPOINT + +module = SERVICE_HEALTH_SYNC_ENDPOINT +module-str = Health sync endpoint +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/health_sync_endpoint/service.c b/src/fw/services/health_sync_endpoint/service.c new file mode 100644 index 0000000000..dc23a7a726 --- /dev/null +++ b/src/fw/services/health_sync_endpoint/service.c @@ -0,0 +1,86 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/health_sync_endpoint.h" + +#include "pbl/services/comm_session/session.h" +#include "pbl/services/system_task.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "system/logging.h" +#include "util/attributes.h" + +PBL_LOG_MODULE_DEFINE(service_health_sync_endpoint, CONFIG_SERVICE_HEALTH_SYNC_ENDPOINT_LOG_LEVEL); + +#define HEALTH_SYNC_ENDPOINT_ID 911 +#define ACK 0x1 +#define NACK 0x2 + +typedef enum HealthSyncEndpointCmd { + HealthSyncEndpointCmd_Sync = 0x1, + HealthSyncEndpointCmd_Ack = 0x11, +} HealthSyncEndpointCmd; + +typedef struct PACKED HealthSyncEndpointSyncMsg { + HealthSyncEndpointCmd cmd : 8; + uint32_t seconds_since_sync; +} HealthSyncEndpointSyncMsg; + +typedef struct PACKED HealthSyncEndpointAckMsg { + HealthSyncEndpointCmd cmd : 8; + uint8_t ack_nack; +} HealthSyncEndpointAckMsg; + +static void prv_send_ack_nack(bool ok) { + const HealthSyncEndpointAckMsg msg = { + .cmd = HealthSyncEndpointCmd_Ack, + .ack_nack = ok ? ACK : NACK, + }; + + comm_session_send_data(comm_session_get_system_session(), + HEALTH_SYNC_ENDPOINT_ID, + (uint8_t*)&msg, + sizeof(HealthSyncEndpointAckMsg), + COMM_SESSION_DEFAULT_TIMEOUT); +} + +#include "pbl/services/activity/activity_algorithm.h" + +static void prv_sync_health_system_task_cb(void *unused) { + if (activity_tracking_on()) { + // tell the activity service to pool the minutes it's got so far + activity_algorithm_send_minutes(); + } + + // send all data logging data + dls_send_all_sessions(); + // ACK + prv_send_ack_nack(true /*ok*/); +} + +static void prv_handle_sync(const uint8_t *msg, size_t len) { + if (len < sizeof(HealthSyncEndpointSyncMsg)) { + PBL_LOG_ERR("Invalid SYNC msg received, length: %u", len); + return; + } + + PBL_LOG_DBG("Received health SYNC request"); + + system_task_add_callback(prv_sync_health_system_task_cb, NULL); +} + +void health_sync_protocol_msg_callback(CommSession *session, const uint8_t *msg, size_t len) { + if (len < 1) { + PBL_LOG_ERR("Invalid message received, length: %u", len); + } + + HealthSyncEndpointCmd cmd = *msg; + switch (cmd) { + case HealthSyncEndpointCmd_Sync: + prv_handle_sync(msg, len); + break; + + default: + PBL_LOG_WRN("Unexpected command received, 0x%x", cmd); + return; + } +} diff --git a/src/fw/services/health_sync_endpoint/wscript_build b/src/fw/services/health_sync_endpoint/wscript_build new file mode 100644 index 0000000000..e1c78e3a9f --- /dev/null +++ b/src/fw/services/health_sync_endpoint/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_health_sync_endpoint', +) +bld.env.SERVICES.append('services_health_sync_endpoint') diff --git a/src/fw/services/hrm/Kconfig b/src/fw/services/hrm/Kconfig new file mode 100644 index 0000000000..d89b4dd30f --- /dev/null +++ b/src/fw/services/hrm/Kconfig @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_HRM + bool "Heart rate monitor" + default y if HRM + help + Heart rate monitor manager service. Requires an HRM driver (CONFIG_HRM). + +if SERVICE_HRM + +module = SERVICE_HRM +module-str = Heart rate monitor +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/hrm/hrm_manager.c b/src/fw/services/hrm/hrm_manager.c new file mode 100644 index 0000000000..d9429d4030 --- /dev/null +++ b/src/fw/services/hrm/hrm_manager.c @@ -0,0 +1,812 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/hrm/hrm_manager_private.h" + +#include "console/prompt.h" +#include "drivers/hrm.h" +#include "kernel/pbl_malloc.h" +#include "mfg/mfg_info.h" +#include "os/tick.h" +#include "process_management/app_manager.h" +#include "process_management/worker_manager.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/system_task.h" +#include "pbl/services/activity/activity.h" +#include "syscall/syscall_internal.h" +#include "system/hexdump.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" +#include "util/math.h" +#include "util/size.h" + +#include "FreeRTOS.h" +#include "queue.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_hrm, CONFIG_SERVICE_HRM_LOG_LEVEL); + +#define HRM_DEBUG 0 + +#if HRM_DEBUG +#define HRM_LOG(fmt, ...) \ + do { \ + PBL_LOG_DBG(fmt, ## __VA_ARGS__); \ + } while (0) +#define HRM_HEXDUMP(data, length) \ + do { \ + PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)data, length); \ + } while (0) +#else +#define HRM_LOG(fmt, ...) +#define HRM_HEXDUMP(data, length) +#endif + +static struct HRMManagerState s_manager_state; + +// Forward declarations +static void prv_update_enable_timer_cb(void *context); + + +static bool prv_match_session_ref(ListNode *found_node, void *data) { + const HRMSubscriberState *state = (HRMSubscriberState *)found_node; + return (state->session_ref == (HRMSessionRef)data); +} + +T_STATIC HRMSubscriberState * prv_get_subscriber_state_from_ref(HRMSessionRef session) { + ListNode *node = list_find(s_manager_state.subscribers, prv_match_session_ref, + (void *)(uintptr_t)session); + return (HRMSubscriberState *)node; +} + + +typedef struct { + AppInstallId app_id; + PebbleTask task; +} HRMAppIdAndTask; + +static bool prv_match_app_id(ListNode *found_node, void *data) { + HRMAppIdAndTask *context = (HRMAppIdAndTask *)data; + const HRMSubscriberState *state = (HRMSubscriberState *)found_node; + return ((state->app_id == context->app_id) && (state->task == context->task)); +} + +T_STATIC HRMSubscriberState * prv_get_subscriber_state_from_app_id(PebbleTask task, + AppInstallId app_id) { + HRMAppIdAndTask context = { + .app_id = app_id, + .task = task, + }; + + ListNode *node = list_find(s_manager_state.subscribers, prv_match_app_id, &context); + return (HRMSubscriberState *)node; +} + +// Return true if this subscriber needs to be sent a HRMEvent_SubscriptionExpiring event +static bool prv_needs_expiring_event(HRMSubscriberState *state, time_t utc_now) { + if (state->sent_expiration_event) { + return false; + } + return (state->expire_utc && (utc_now >= state->expire_utc + - MAX(HRM_SUBSCRIPTION_EXPIRING_WARNING_SEC, (int)state->update_interval_s))); +} + +T_STATIC void prv_read_event_from_buffer_and_consume(CircularBuffer *buffer, + PebbleHRMEvent *event) { + const uint16_t total_size = sizeof(*event); + uint16_t remaining = total_size; + uint8_t *out_buf = (uint8_t *)event; + while (remaining > 0) { + const uint8_t *data_out; + uint16_t length_out; + + const bool success = circular_buffer_read(buffer, remaining, &data_out, &length_out); + PBL_ASSERTN(success); + memcpy(out_buf, data_out, length_out); + + out_buf += length_out; + remaining -= length_out; + } + PBL_ASSERTN(remaining == 0); + + circular_buffer_consume(buffer, sizeof(*event)); +} + +static void prv_remove_and_free_subscription(HRMSubscriberState *state) { + list_remove((ListNode *)state, &s_manager_state.subscribers, NULL); + kernel_free(state); +} + +#if UNITTEST +// Used by unit tests +T_STATIC TimerID prv_get_timer_id(void) { + return s_manager_state.update_enable_timer_id; +} + +// Used by unit tests +T_STATIC uint32_t prv_num_system_task_events_queued(void) { + uint16_t avail_bytes = circular_buffer_get_read_space_remaining( + &s_manager_state.system_task_event_buffer); + return avail_bytes / sizeof(PebbleHRMEvent); +} + +// Used by unit tests +T_STATIC uint32_t prv_get_dropped_events_count(void) { + return s_manager_state.dropped_events; +} +#endif + +static void prv_handle_accel_data(void * data) { + PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_NewTimers); + + uint64_t timestamp_ms; + uint32_t num_new_samples = sys_accel_manager_get_num_samples( + s_manager_state.accel_state, ×tamp_ms); + + mutex_lock(s_manager_state.accel_data_lock); + + // Only read as many as we have space to store + const size_t MAX_BUFFERED_SAMPLES = ARRAY_LENGTH(s_manager_state.accel_data.data); + uint32_t num_samples_to_copy = num_new_samples; + if ((s_manager_state.accel_data.num_samples + num_new_samples) > MAX_BUFFERED_SAMPLES) { + num_samples_to_copy = MAX_BUFFERED_SAMPLES - s_manager_state.accel_data.num_samples; + } + + void *write_ptr = &s_manager_state.accel_data.data[s_manager_state.accel_data.num_samples]; + memcpy(write_ptr, s_manager_state.accel_manager_buffer, num_samples_to_copy * sizeof(AccelRawData)); + + s_manager_state.accel_data.num_samples += num_samples_to_copy; + + mutex_unlock(s_manager_state.accel_data_lock); + + // Always consume all samples that were prepared, even if we couldn't store them all + sys_accel_manager_consume_samples(s_manager_state.accel_state, num_new_samples); +} + +T_STATIC bool prv_can_turn_sensor_on(void) { +#if defined(CONFIG_IS_BIGBOARD) || defined(CONFIG_RECOVERY_FW) + return true; +#endif + + return s_manager_state.enabled_run_level && + s_manager_state.enabled_charging_state && + activity_prefs_heart_rate_is_enabled(); +} + +// Figure out if we should enable the HR sensor or not based on all subscribers and their +// desired sampling periods. Must be called from the KernelBG task. +static void prv_update_hrm_enable_system_cb(void *unused) { + const time_t utc_now = rtc_get_time(); + PBL_ASSERT_TASK(PebbleTask_KernelBackground); + mutex_lock_recursive(s_manager_state.lock); + { + bool turn_sensor_on = false; + // How many ms until we need the sensor on again. INT32_MAX means we don't need to turn it on + // again + int32_t remaining_ms = INT32_MAX; + + if (prv_can_turn_sensor_on()) { + RtcTicks cur_ticks = rtc_get_ticks(); + int32_t remaining_ticks = INT32_MAX; + const int32_t spin_up_ticks = (int32_t)milliseconds_to_ticks( + HRM_SENSOR_SPIN_UP_SEC * MS_PER_SECOND); + + // Loop through each of the subscribers and figure out when the next one needs an update + HRMSubscriberState *state = (HRMSubscriberState *) s_manager_state.subscribers; + for (; state != NULL; state = (HRMSubscriberState *) state->list_node.next) { + if (state->expire_utc && (utc_now >= state->expire_utc)) { + // Ignore expired subscriptions + continue; + } + int64_t subscriber_age_ticks; + if (state->last_valid_bpm_ticks) { + subscriber_age_ticks = cur_ticks - state->last_valid_bpm_ticks; + } else { + // Never got an update yet + subscriber_age_ticks = milliseconds_to_ticks(state->update_interval_s * MS_PER_SECOND); + } + int64_t subscriber_remaining_ticks = + (int64_t)milliseconds_to_ticks(state->update_interval_s * MS_PER_SECOND) + - subscriber_age_ticks - spin_up_ticks; + subscriber_remaining_ticks = MAX(0, subscriber_remaining_ticks); + + remaining_ticks = MIN(remaining_ticks, subscriber_remaining_ticks); + } + + // How many milliseconds till we need to send the next sensor reading + remaining_ms = ticks_to_milliseconds(remaining_ticks); + HRM_LOG("Need sensor on again in %"PRIu32" sec", remaining_ms / MS_PER_SECOND); + turn_sensor_on = (remaining_ms <= 0); + } + + // Check if we've permanently failed to enable HRM + bool hrm_permanently_failed = (s_manager_state.enable_failure_count >= HRM_MAX_ENABLE_FAILURES); + + if (turn_sensor_on && !hrm_is_enabled(HRM) && !hrm_permanently_failed) { + // Turn on the sensor now + HRM_LOG("Turning on HR sensor"); + + // Only subscribe if not already subscribed (prevents leak if hrm_is_enabled is out of sync) + if (s_manager_state.accel_state) { + PBL_LOG_WRN("HRM: accel already subscribed, unsubscribing first"); + sys_accel_manager_data_unsubscribe(s_manager_state.accel_state); + s_manager_state.accel_state = NULL; + } + + s_manager_state.accel_state = sys_accel_manager_data_subscribe( + ACCEL_SAMPLING_25HZ, prv_handle_accel_data, NULL, PebbleTask_NewTimers); + + sys_accel_manager_set_sample_buffer( + s_manager_state.accel_state, s_manager_state.accel_manager_buffer, + HRM_MANAGER_ACCEL_MANAGER_SAMPLES_PER_UPDATE); + + if (!hrm_enable(HRM)) { + // HRM failed to enable, clean up the accel subscription + s_manager_state.enable_failure_count++; + if (s_manager_state.enable_failure_count >= HRM_MAX_ENABLE_FAILURES) { + PBL_LOG_ERR("HRM failed to enable %d times, giving up until reboot", + HRM_MAX_ENABLE_FAILURES); + } else { + PBL_LOG_ERR("HRM failed to enable (attempt %d/%d)", + s_manager_state.enable_failure_count, HRM_MAX_ENABLE_FAILURES); + } + sys_accel_manager_data_unsubscribe(s_manager_state.accel_state); + s_manager_state.accel_state = NULL; + } else { + // Success - reset failure counter + s_manager_state.enable_failure_count = 0; + // Don't need the re-enable timer to fire + new_timer_stop(s_manager_state.update_enable_timer_id); + // Track HRM on-time + PBL_ANALYTICS_TIMER_START(hrm_on_time_ms); + } + + } else if (!turn_sensor_on && hrm_is_enabled(HRM)) { + // Turn off the sensor now + HRM_LOG("Turning off HR sensor"); + hrm_disable(HRM); + // Stop tracking HRM on-time + PBL_ANALYTICS_TIMER_STOP(hrm_on_time_ms); + + sys_accel_manager_data_unsubscribe(s_manager_state.accel_state); + s_manager_state.accel_state = NULL; + + // If we need the sensor on again later, turn on a timer to re-enable the HRM in enough time + // to get a good reading for the next subscriber that needs one + if (remaining_ms < INT32_MAX) { + new_timer_start(s_manager_state.update_enable_timer_id, remaining_ms, + prv_update_enable_timer_cb, NULL /*context*/, 0 /*flags*/); + } else { + new_timer_stop(s_manager_state.update_enable_timer_id); + } + } + } + mutex_unlock_recursive(s_manager_state.lock); +} + +// Timer callback that we use to re-enable the HR sensor in case we turned it off for a while +static void prv_update_enable_timer_cb(void *context) { + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); +} + +//! The system task needs its own handler for HRM data since we can't queue up generic events. +static void prv_system_task_hrm_handler(void *context) { + time_t utc_now = rtc_get_time(); + + mutex_lock_recursive(s_manager_state.lock); + + // Check if there's data available in the circular buffer before attempting to read + const uint16_t available_bytes = + circular_buffer_get_read_space_remaining(&s_manager_state.system_task_event_buffer); + if (available_bytes < sizeof(PebbleHRMEvent)) { + // No event available to read - this can happen if system task callbacks are queued + // without corresponding events, or during concurrent access. + PBL_LOG_WRN("HRM: system task handler called with no event in buffer " + "(available=%u, needed=%u)", available_bytes, sizeof(PebbleHRMEvent)); + mutex_unlock_recursive(s_manager_state.lock); + return; + } + + PebbleHRMEvent event; + prv_read_event_from_buffer_and_consume(&s_manager_state.system_task_event_buffer, &event); + + // Send event to all KernelBG subscribers that asked for this feature + HRMSubscriberState *state = (HRMSubscriberState *)s_manager_state.subscribers; + for (; state != NULL; state = (HRMSubscriberState *)state->list_node.next) { + if (!state->callback_handler) { + // Not a KernelBG subscriber + continue; + } + + // If this subscription is ready to expire, send an "expiring" event + if (prv_needs_expiring_event(state, utc_now)) { + PebbleHRMEvent expiring_event = (PebbleHRMEvent) { + .event_type = HRMEvent_SubscriptionExpiring, + .expiring.session_ref = state->session_ref, + }; + state->callback_handler(&expiring_event, state->callback_context); + state->sent_expiration_event = true; + } + + // See if this subscriber wants these types of events + switch (event.event_type) { + case HRMEvent_BPM: + if (!(state->features & HRMFeature_BPM)) { + continue; + } + break; + case HRMEvent_HRV: + if (!(state->features & HRMFeature_HRV)) { + continue; + } + break; + case HRMEvent_SpO2: + if (!(state->features & HRMFeature_SpO2)) { + continue; + } + break; +#ifdef CONFIG_MFG + case HRMEvent_CTR: + if (!(state->features & HRMFeature_CTR)) { + continue; + } + break; + case HRMEvent_Leakage: + if (!(state->features & HRMFeature_Leakage)) { + continue; + } + break; +#endif + case HRMEvent_SubscriptionExpiring: + continue; + } + + // Send the event to the subscriber + state->callback_handler(&event, state->callback_context); + } + mutex_unlock_recursive(s_manager_state.lock); +} + +// Assumes that s_manager_state.lock is held +static void prv_queue_system_task_event(const PebbleHRMEvent *event) { + const uint16_t free_space = + circular_buffer_get_read_space_remaining(&s_manager_state.system_task_event_buffer); + if (free_space < sizeof(PebbleHRMEvent)) { + circular_buffer_consume(&s_manager_state.system_task_event_buffer, sizeof(PebbleHRMEvent)); + ++s_manager_state.dropped_events; + } + circular_buffer_write(&s_manager_state.system_task_event_buffer, + (const uint8_t *)event, sizeof(PebbleHRMEvent)); +} + +static void prv_populate_hrm_event(PebbleHRMEvent *event, HRMFeature feature, const HRMData *data) { + switch (feature) { + case HRMFeature_BPM: + *event = (PebbleHRMEvent) { + .event_type = HRMEvent_BPM, + .bpm = { + .bpm = data->hrm_bpm, + .quality = data->hrm_quality, + }, + }; + break; + case HRMFeature_HRV: + *event = (PebbleHRMEvent) { + .event_type = HRMEvent_HRV, + .hrv = { + .ppi_ms = data->hrv_ppi_ms, + .quality = data->hrv_quality, + }, + }; + break; + case HRMFeature_SpO2: + *event = (PebbleHRMEvent) { + .event_type = HRMEvent_SpO2, + .spo2 = { + .percent = data->spo2_percent, + .quality = data->spo2_quality, + }, + }; + break; +#ifdef CONFIG_MFG + case HRMFeature_CTR: + { + HRMCTRData *ctr_data = kernel_zalloc_check(sizeof(HRMCTRData)); + memcpy(ctr_data->ctr, data->ctr, sizeof(HRMCTRData)); + *event = (PebbleHRMEvent) { + .event_type = HRMEvent_CTR, + .ctr = ctr_data, + }; + break; + } + case HRMFeature_Leakage: + { + HRMLeakageData *leakage_data = kernel_zalloc_check(sizeof(HRMLeakageData)); + memcpy(leakage_data->leakage, data->leakage, sizeof(HRMLeakageData)); + *event = (PebbleHRMEvent) { + .event_type = HRMEvent_Leakage, + .leakage = leakage_data, + }; + break; + } +#endif + default: + WTF; + } +} + +static bool prv_event_put(HRMSubscriberState *state, PebbleHRMEvent *event) { + bool success; + if (state->queue) { + PebbleEvent e = { + .type = PEBBLE_HRM_EVENT, + .hrm = *event, + }; + success = xQueueSendToBack(state->queue, &e, 0); + } else { + prv_queue_system_task_event(event); + success = system_task_add_callback(prv_system_task_hrm_handler, NULL); + } + return success; +} + +T_STATIC void prv_charger_event_cb(PebbleEvent *e, void *context) { + const PebbleBatteryStateChangeEvent *evt = &e->battery_state; + mutex_lock_recursive(s_manager_state.lock); + { + s_manager_state.enabled_charging_state = !evt->new_state.is_plugged; + } + mutex_unlock_recursive(s_manager_state.lock); + + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); +} + +// Accept new data from the HR device driver. +void hrm_manager_new_data_cb(const HRMData *data) { + mutex_lock_recursive(s_manager_state.lock); + if (!prv_can_turn_sensor_on() || s_manager_state.subscribers == NULL) { + // If the hrm manager should be disabled or we have no subscribers, this data is unwanted. + goto unlock; + } + + HRM_LOG("HRM Data:"); + if (data->features & HRMFeature_BPM) { + HRM_LOG(" BPM: %"PRIu8", Quality: %d", data->hrm_bpm, data->hrm_quality); + } + if (data->features & HRMFeature_HRV) { + HRM_LOG(" HRV PPI: %"PRIu16"ms, Quality: %d", data->hrv_ppi_ms, data->hrv_quality); + } + if (data->features & HRMFeature_SpO2) { + HRM_LOG(" SpO2: %"PRIu8", Quality: %d", data->spo2_percent, data->spo2_quality); + } + + time_t utc_now = rtc_get_time(); + RtcTicks cur_ticks = rtc_get_ticks(); + HRMFeature kernel_bg_features_sent = 0; + + HRMSubscriberState *state = (HRMSubscriberState *)s_manager_state.subscribers; + while (state) { + HRMSubscriberState *expired_state = NULL; + + // Only count Good+ or OffWrist as "served" for sensor power cycling + if ((data->features & HRMFeature_BPM) && + (data->hrm_quality >= HRMQuality_Good || + data->hrm_quality == HRMQuality_OffWrist)) { + state->last_valid_bpm_ticks = cur_ticks; + } + + PebbleHRMEvent hrm_event; + for (uint8_t i = 0; i < HRMFeatureShiftMax; ++i) { + HRMFeature feature = (1 << i); + if (!(state->features & feature) || !(data->features & feature)) { + continue; + } + if (state->callback_handler) { + // For kernel BG subscribers, we only queue one event of each type (which is then + // dispatched to all KernelBG subscribers from the KernelBG callback) so that we don't + // overfill our limited size circular buffer. + if (kernel_bg_features_sent & feature) { + continue; + } + kernel_bg_features_sent |= feature; + } + prv_populate_hrm_event(&hrm_event, feature, data); + if (!prv_event_put(state, &hrm_event)) { + // Consumer queue full (e.g. app not draining events); drop instead of panicking. + ++s_manager_state.dropped_events; + } + } + + // If this is an app subscription, see if we need to send an "expiring" event. We check + // KernelBG subscribers from the system callback function (prv_system_task_hrm_handler). + if (!state->callback_handler && prv_needs_expiring_event(state, utc_now)) { + hrm_event = (PebbleHRMEvent) { + .event_type = HRMEvent_SubscriptionExpiring, + .expiring.session_ref = state->session_ref, + }; + if (prv_event_put(state, &hrm_event)) { + state->sent_expiration_event = true; + } else { + // Retry on the next sample rather than panicking. + ++s_manager_state.dropped_events; + } + } + + if (state->expire_utc && (utc_now >= state->expire_utc)) { + // This subscription has expired + expired_state = state; + } + state = (HRMSubscriberState *)state->list_node.next; + + // If the prior subscription expired, remove it now + if (expired_state) { + PBL_LOG_DBG("Subscription %"PRIu32" expired", expired_state->session_ref); + prv_remove_and_free_subscription(expired_state); + } + } + + // Update the HRM enable state. If no subscribers need an update for a while, we can turn off the + // HR sensor and set a timer to turn it on again later. To avoid this overhead on every callback, + // we only check it once every HRM_CHECK_SENSOR_DISABLE_COUNT times + if (++s_manager_state.check_disable_counter >= HRM_CHECK_SENSOR_DISABLE_COUNT) { + s_manager_state.check_disable_counter = 0; + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); + } +unlock: + mutex_unlock_recursive(s_manager_state.lock); +} + +void hrm_manager_handle_prefs_changed(void) { + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); +} + +void hrm_manager_init(void) { + s_manager_state = (struct HRMManagerState) { + .lock = mutex_create_recursive(), + .accel_data_lock = mutex_create(), + .update_enable_timer_id = new_timer_create(), + .enabled_charging_state = !battery_is_usb_connected(), + .charger_subscription = (EventServiceInfo) { + .type = PEBBLE_BATTERY_STATE_CHANGE_EVENT, + .handler = prv_charger_event_cb, + }, + }; + circular_buffer_init(&s_manager_state.system_task_event_buffer, + s_manager_state.system_task_event_storage, + EVENT_STORAGE_SIZE); + event_service_client_subscribe(&s_manager_state.charger_subscription); +} + +HRMSessionRef hrm_manager_subscribe_with_callback(AppInstallId app_id, uint32_t update_interval_s, + uint16_t expire_s, HRMFeature features, + HRMSubscriberCallback callback, void *context) { + const PebbleTask current_task = pebble_task_get_current(); + bool is_app_subscription = false; + if (current_task == PebbleTask_KernelBackground) { + // KernelBG must provide a callback + PBL_ASSERTN(callback != NULL); + } else if (current_task == PebbleTask_KernelMain) { + // KernelMain clients can either set a callback, or use the event_service interface. + } else { + PBL_ASSERTN(current_task == PebbleTask_App || current_task == PebbleTask_Worker); + is_app_subscription = true; + } + + mutex_lock_recursive(s_manager_state.lock); + HRMSessionRef session_ref = HRM_INVALID_SESSION_REF; + + // If there is already an existing subscription for this app, remove the old one before we + // add another subscription for this app. + if (is_app_subscription) { + HRMSubscriberState * state = prv_get_subscriber_state_from_app_id(current_task, app_id); + if (state != NULL) { + session_ref = state->session_ref; + PBL_LOG_DBG("Removing existing subscription for this app"); + prv_remove_and_free_subscription(state); + } + } + + // Get the session ref to use + if (session_ref == HRM_INVALID_SESSION_REF) { + session_ref = ++s_manager_state.next_session_ref; + } + + HRMSubscriberState *state = kernel_malloc_check(sizeof(*state)); + *state = (HRMSubscriberState) { + .session_ref = session_ref, + .app_id = app_id, + .task = current_task, + .queue = pebble_task_get_to_queue(current_task), + .callback_handler = callback, + .callback_context = context, + .update_interval_s = update_interval_s, + .expire_utc = (expire_s != 0) ? (rtc_get_time() + expire_s) : 0, + .features = features, + }; + s_manager_state.subscribers = + list_insert_before(s_manager_state.subscribers, &state->list_node); + + // Update the HR enablement state + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); + + mutex_unlock_recursive(s_manager_state.lock); + return state->session_ref; +} + +DEFINE_SYSCALL(HRMSessionRef, sys_hrm_manager_app_subscribe, + AppInstallId app_id, uint32_t update_interval_s, uint16_t expire_sec, HRMFeature features) { + return hrm_manager_subscribe_with_callback(app_id, update_interval_s, expire_sec, features, NULL, + NULL); +} + + +DEFINE_SYSCALL(bool, sys_hrm_manager_unsubscribe, HRMSessionRef session) { + HRM_LOG("Unsubscribing"); + bool success = false; + mutex_lock_recursive(s_manager_state.lock); + + HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); + if (state) { + prv_remove_and_free_subscription(state); + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); + success = true; + } + + mutex_unlock_recursive(s_manager_state.lock); + return success; +} + +DEFINE_SYSCALL(HRMSessionRef, sys_hrm_manager_get_app_subscription, AppInstallId app_id) { + mutex_lock_recursive(s_manager_state.lock); + HRMSessionRef ref = HRM_INVALID_SESSION_REF; + HRMSubscriberState *state = prv_get_subscriber_state_from_app_id(pebble_task_get_current(), + app_id); + if (state) { + ref = state->session_ref; + } + mutex_unlock_recursive(s_manager_state.lock); + return ref; +} + + +DEFINE_SYSCALL(bool, sys_hrm_manager_get_subscription_info, HRMSessionRef session, + AppInstallId *app_id, uint32_t *update_interval_s, uint16_t *expire_s, + HRMFeature *features) { + // Each of these out-params is optional, but if the caller supplied one it + // must point into its own app/worker RAM. The app can otherwise prime + // *update_interval_s (via set_update_interval) and then aim the pointer at + // any kernel address to land a controlled uint32_t write there. + if (PRIVILEGE_WAS_ELEVATED) { + if (app_id) { + syscall_assert_userspace_buffer(app_id, sizeof(*app_id)); + } + if (update_interval_s) { + syscall_assert_userspace_buffer(update_interval_s, sizeof(*update_interval_s)); + } + if (expire_s) { + syscall_assert_userspace_buffer(expire_s, sizeof(*expire_s)); + } + if (features) { + syscall_assert_userspace_buffer(features, sizeof(*features)); + } + } + mutex_lock_recursive(s_manager_state.lock); + HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); + if (state) { + if (app_id) { + *app_id = state->app_id; + } + if (update_interval_s) { + *update_interval_s = state->update_interval_s; + } + if (expire_s) { + int16_t expire_in_s = 0; + if (state->expire_utc != 0) { + expire_in_s = MAX(0, state->expire_utc - rtc_get_time()); + } + *expire_s = MAX(0, expire_in_s); + } + if (features) { + *features = state->features; + } + } + mutex_unlock_recursive(s_manager_state.lock); + return (state != NULL); +} + + +DEFINE_SYSCALL(bool, sys_hrm_manager_set_features, HRMSessionRef session, HRMFeature features) { + bool success = false; + mutex_lock_recursive(s_manager_state.lock); + HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); + if (state) { + state->features = features; + success = true; + } + mutex_unlock_recursive(s_manager_state.lock); + return success; +} + +DEFINE_SYSCALL(bool, sys_hrm_manager_set_update_interval, HRMSessionRef session, + uint32_t update_interval_s, uint16_t expire_s) { + bool success = false; + mutex_lock_recursive(s_manager_state.lock); + + HRMSubscriberState *state = prv_get_subscriber_state_from_ref(session); + if (state) { + state->update_interval_s = update_interval_s; + state->expire_utc = (expire_s != 0) ? (rtc_get_time() + expire_s) : 0; + state->sent_expiration_event = false; + success = true; + } + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); + mutex_unlock_recursive(s_manager_state.lock); + return success; +} + +void hrm_manager_enable(bool on) { + mutex_lock_recursive(s_manager_state.lock); + s_manager_state.enabled_run_level = on; + system_task_add_callback(prv_update_hrm_enable_system_cb, NULL); + mutex_unlock_recursive(s_manager_state.lock); +} + + +static HRMSessionRef s_console_session = HRM_INVALID_SESSION_REF; +static void prv_console_unsubscribe_callback(void *data) { + sys_hrm_manager_unsubscribe(s_console_session); + s_console_session = HRM_INVALID_SESSION_REF; + prompt_command_finish(); +} + +static void prv_console_read_callback(PebbleHRMEvent *event, void *context) { + if (event->event_type == HRMEvent_BPM) { + system_task_add_callback(prv_console_unsubscribe_callback, NULL); + char buf[32]; + prompt_send_response_fmt(buf, 32, "BPM: %"PRIu8 " quality: %"PRIu8, event->bpm.bpm, event->bpm.quality); + } +} + +void command_hrm_read(void) { + sys_hrm_manager_unsubscribe(s_console_session); + s_console_session = hrm_manager_subscribe_with_callback( + INSTALL_ID_INVALID, 1 /*update_interval_s*/, 0 /*expire_s*/, HRMFeature_BPM, + prv_console_read_callback, NULL); + prompt_command_continues_after_returning(); +} + +HRMAccelData * hrm_manager_get_accel_data(void) { + mutex_lock(s_manager_state.accel_data_lock); + return &s_manager_state.accel_data; +} + +void hrm_manager_release_accel_data(void) { + s_manager_state.accel_data.num_samples = 0; // Reset buffer + mutex_unlock(s_manager_state.accel_data_lock); +} + +void hrm_manager_process_cleanup(PebbleTask task, AppInstallId app_id) { + if (task != PebbleTask_App && task != PebbleTask_Worker) { + return; + } + + // For apps and workers, if they have a subscription still active, make sure it expires + HRMSubscriberState *state = prv_get_subscriber_state_from_app_id(task, app_id); + if (state == NULL) { + return; + } + + // Don't lengthen an expiration that's already shorter than ours — the app explicitly chose a + // shorter window (e.g. workout post-workout recovery), and overriding it would defeat that. + const time_t cleanup_expire_utc = rtc_get_time() + HRM_MANAGER_APP_EXIT_EXPIRATION_SEC; + if (state->expire_utc != 0 && state->expire_utc <= cleanup_expire_utc) { + return; + } + + PBL_LOG_DBG("Setting expiration time on session for app_id %d", (int)app_id); + sys_hrm_manager_set_update_interval(state->session_ref, state->update_interval_s, + HRM_MANAGER_APP_EXIT_EXPIRATION_SEC); +} diff --git a/src/fw/services/hrm/wscript_build b/src/fw/services/hrm/wscript_build new file mode 100644 index 0000000000..6c9981c99a --- /dev/null +++ b/src/fw/services/hrm/wscript_build @@ -0,0 +1,10 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['hrm_manager.c'], + target='services_hrm', +) + +bld.env.SERVICES.append('services_hrm') diff --git a/src/fw/services/i18n/Kconfig b/src/fw/services/i18n/Kconfig new file mode 100644 index 0000000000..c5382bba64 --- /dev/null +++ b/src/fw/services/i18n/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_I18N + bool "Internationalization" + help + Internationalization (gettext) runtime support. + +if SERVICE_I18N + +module = SERVICE_I18N +module-str = Internationalization +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/common/i18n/LICENSE b/src/fw/services/i18n/LICENSE similarity index 100% rename from src/fw/services/common/i18n/LICENSE rename to src/fw/services/i18n/LICENSE diff --git a/src/fw/services/common/i18n/i18n.c b/src/fw/services/i18n/i18n.c similarity index 95% rename from src/fw/services/common/i18n/i18n.c rename to src/fw/services/i18n/i18n.c index 9674036a5d..7330ca6751 100644 --- a/src/fw/services/common/i18n/i18n.c +++ b/src/fw/services/i18n/i18n.c @@ -29,18 +29,20 @@ #include #include -#include "i18n.h" -#include "mo.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/i18n/mo.h" #include "kernel/event_loop.h" #include "kernel/pbl_malloc.h" #include "resource/resource.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "shell/normal/language_ui.h" #include "shell/prefs.h" #include "system/logging.h" #include "system/passert.h" #include "util/list.h" +PBL_LOG_MODULE_DEFINE(service_i18n, CONFIG_SERVICE_I18N_LOG_LEVEL); + ////////////////////////////////////////////////////// // See mo.h for a description of the MO file format // ////////////////////////////////////////////////////// @@ -193,13 +195,13 @@ static bool prv_get_metadata(struct DomainBinding *db) { // all metadata is in the "" header entry prv_lookup("", db, &header_len, header, HEADER_BUFFER_SIZE); if (!header_len) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not find header in language pack"); + PBL_LOG_WRN("Could not find header in language pack"); goto cleanup; } // Isolate the language substring if (!prv_get_property(header, "Language: ", db->iso_locale, ISO_LOCALE_LENGTH)) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not parse a language from language pack"); + PBL_LOG_WRN("Could not parse a language from language pack"); goto cleanup; } @@ -211,18 +213,18 @@ static bool prv_get_metadata(struct DomainBinding *db) { // Isolate the version value char version_str[10] = {0}; if (!prv_get_property(header, "Project-Id-Version: ", version_str, 10)) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not parse a version from language pack"); + PBL_LOG_WRN("Could not parse a version from language pack"); goto cleanup; } char *version_end; db->lang_version = strtol(version_str, &version_end, 0); if (version_end == version_str) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not parse a version from language pack"); + PBL_LOG_WRN("Could not parse a version from language pack"); goto cleanup; } success = true; - PBL_LOG(LOG_LEVEL_INFO, "language: %s, version %d", db->iso_locale, db->lang_version); + PBL_LOG_INFO("language: %s, version %d", db->iso_locale, db->lang_version); cleanup: kernel_free(header); @@ -248,7 +250,7 @@ static bool prv_mapit(const uint32_t resource_id, struct DomainBinding *db) { return (db->version.crc != 0); } - PBL_LOG(LOG_LEVEL_DEBUG, "New language detected!"); + PBL_LOG_DBG("New language detected!"); db->need_reload = false; /* save version */ @@ -437,7 +439,7 @@ const char *i18n_get(const char *msgid, const void *owner) { size_t len = 0; prv_lookup(msgid, db, &len, translated, sizeof(translated)); if (len >= sizeof(translated)) { - PBL_LOG(LOG_LEVEL_WARNING, "Truncated string: <%s>", msgid); + PBL_LOG_WRN("Truncated string: <%s>", msgid); } if (len) { @@ -465,7 +467,7 @@ void i18n_get_with_buffer(const char *msgid, char *buffer, size_t length) { size_t len = 0; prv_lookup(msgid, db, &len, buffer, length); if (len >= length) { - PBL_LOG(LOG_LEVEL_WARNING, "Truncated string: <%s>", msgid); + PBL_LOG_WRN("Truncated string: <%s>", msgid); } if (len) { @@ -523,7 +525,7 @@ void i18n_free_all(const void *owner) { static void prv_resource_changed_handler(void *data) { struct DomainBinding *db = (struct DomainBinding *)data; // Mark as invalid - PBL_LOG(LOG_LEVEL_DEBUG, "lang resource file reloading"); + PBL_LOG_DBG("lang resource file reloading"); shell_prefs_set_language_english(false); db->need_reload = true; @@ -535,7 +537,7 @@ static void prv_resource_changed_handler(void *data) { static void prv_resource_changed_callback(void *data) { // We want to not actually handle the reload here, because the PFS lock is still held here. // So instead we throw in the reload as an event callback. - PBL_LOG(LOG_LEVEL_DEBUG, "lang resource file was modified"); + PBL_LOG_DBG("lang resource file was modified"); launcher_task_add_callback(prv_resource_changed_handler, data); } diff --git a/src/fw/services/i18n/syscalls.c b/src/fw/services/i18n/syscalls.c new file mode 100644 index 0000000000..29a0030526 --- /dev/null +++ b/src/fw/services/i18n/syscalls.c @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/i18n/i18n.h" + +#include "kernel/memory_layout.h" +#include "kernel/pebble_tasks.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DECLARE(service_i18n, CONFIG_SERVICE_I18N_LOG_LEVEL); + +DEFINE_SYSCALL(void, sys_i18n_get_locale, char *buf) { + if (PRIVILEGE_WAS_ELEVATED) { + if (pebble_task_get_current() == PebbleTask_Worker) { + // not allowed from workers + syscall_failed(); + } + // strncpy() below unconditionally writes ISO_LOCALE_LENGTH bytes via the + // caller-supplied pointer; without this check an app could hand us any + // kernel address and have us stamp the locale string into it. + syscall_assert_userspace_buffer(buf, ISO_LOCALE_LENGTH); + } + + strncpy(buf, i18n_get_locale(), ISO_LOCALE_LENGTH); +} + +DEFINE_SYSCALL(void, sys_i18n_get_with_buffer, const char *string, + char *buffer, size_t length) { + if (PRIVILEGE_WAS_ELEVATED) { + if (pebble_task_get_current() == PebbleTask_Worker) { + // not allowed from workers + syscall_failed(); + } + if (!memory_layout_is_cstring_in_region(memory_layout_get_app_region(), string, 100) && + !memory_layout_is_cstring_in_region(memory_layout_get_microflash_region(), string, 100)) { + PBL_LOG_ERR("Pointer %p not in app or microflash region", string); + syscall_failed(); + } + syscall_assert_userspace_buffer(buffer, length); + } + + i18n_get_with_buffer(string, buffer, length); +} + +DEFINE_SYSCALL(size_t, sys_i18n_get_length, const char *string) { + if (PRIVILEGE_WAS_ELEVATED) { + if (pebble_task_get_current() == PebbleTask_Worker) { + // not allowed from workers + syscall_failed(); + } + if (!memory_layout_is_cstring_in_region(memory_layout_get_app_region(), string, 100) && + !memory_layout_is_cstring_in_region(memory_layout_get_microflash_region(), string, 100)) { + PBL_LOG_ERR("Pointer %p not in app or microflash region", string); + syscall_failed(); + } + } + + return i18n_get_length(string); +} diff --git a/src/fw/services/i18n/wscript_build b/src/fw/services/i18n/wscript_build new file mode 100644 index 0000000000..5fcdf1940e --- /dev/null +++ b/src/fw/services/i18n/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'i18n.c', + 'syscalls.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_i18n', +) + +bld.env.SERVICES.append('services_i18n') diff --git a/src/fw/services/idle_watchdog/Kconfig b/src/fw/services/idle_watchdog/Kconfig new file mode 100644 index 0000000000..47c132a652 --- /dev/null +++ b/src/fw/services/idle_watchdog/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_IDLE_WATCHDOG + bool "Idle watchdog" + help + PRF (recovery) idle watchdog service. diff --git a/src/fw/services/idle_watchdog/service.c b/src/fw/services/idle_watchdog/service.c new file mode 100644 index 0000000000..42a331b816 --- /dev/null +++ b/src/fw/services/idle_watchdog/service.c @@ -0,0 +1,79 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/idle_watchdog.h" + +#include "applib/event_service_client.h" +#include "comm/ble/gap_le_connection.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" +#include "system/reboot_reason.h" +#include "kernel/util/standby.h" + +#define PRF_IDLE_TIMEOUT_MINUTES 10 +static RegularTimerInfo s_is_idle_timer; +static EventServiceInfo s_bt_event_info; +static EventServiceInfo s_battery_event_info; +static EventServiceInfo s_button_event_info; +static bool s_running; + +static void prv_handle_watchdog_timeout_cb(void *not_used) { + GAPLEConnection *le_connection = gap_le_connection_any(); + + if (le_connection) { + // We are still connected, don't shut down + return; + } + + BatteryChargeState current_state = battery_get_charge_state(); + if (current_state.is_plugged) { + // We are plugged in, don't shut down + return; + } + + enter_standby(RebootReasonCode_PrfIdle); +} + +static void prv_handle_watchdog_timeout(void *not_used) { + system_task_add_callback(prv_handle_watchdog_timeout_cb, NULL); +} + +static void prv_watchdog_feed(PebbleEvent *e, void *context) { + if (__atomic_load_n(&s_running, __ATOMIC_RELAXED)) { + regular_timer_add_multiminute_callback(&s_is_idle_timer, PRF_IDLE_TIMEOUT_MINUTES); + } +} + +void prf_idle_watchdog_init(void) { + s_bt_event_info = (EventServiceInfo) { + .type = PEBBLE_BT_CONNECTION_EVENT, + .handler = prv_watchdog_feed, + }; + event_service_client_subscribe(&s_bt_event_info); + + s_battery_event_info = (EventServiceInfo) { + .type = PEBBLE_BATTERY_CONNECTION_EVENT, + .handler = prv_watchdog_feed, + }; + event_service_client_subscribe(&s_battery_event_info); + + s_button_event_info = (EventServiceInfo) { + .type = PEBBLE_BUTTON_DOWN_EVENT, + .handler = prv_watchdog_feed, + }; + event_service_client_subscribe(&s_button_event_info); +} + +void prf_idle_watchdog_start(void) { + s_is_idle_timer = (RegularTimerInfo) { + .cb = prv_handle_watchdog_timeout, + }; + regular_timer_add_multiminute_callback(&s_is_idle_timer, PRF_IDLE_TIMEOUT_MINUTES); + __atomic_store_n(&s_running, true, __ATOMIC_RELAXED); +} + +void prf_idle_watchdog_stop(void) { + __atomic_store_n(&s_running, false, __ATOMIC_RELAXED); + regular_timer_remove_callback(&s_is_idle_timer); +} diff --git a/src/fw/services/idle_watchdog/wscript_build b/src/fw/services/idle_watchdog/wscript_build new file mode 100644 index 0000000000..09f0543f4d --- /dev/null +++ b/src/fw/services/idle_watchdog/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_idle_watchdog', +) +bld.env.SERVICES.append('services_idle_watchdog') diff --git a/src/fw/services/imu/units.h b/src/fw/services/imu/units.h deleted file mode 100644 index ad9106ac72..0000000000 --- a/src/fw/services/imu/units.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef enum { - //! The positive direction along the X axis goes toward the right - //! of the watch. - AXIS_X = 0, - //! The positive direction along the Y axis goes toward the top - //! of the watch. - AXIS_Y = 1, - //! The positive direction along the Z axis goes vertically out of - //! the watchface. - AXIS_Z = 2, -} IMUCoordinateAxis; diff --git a/src/fw/services/legacy/Kconfig b/src/fw/services/legacy/Kconfig new file mode 100644 index 0000000000..d1402eaf31 --- /dev/null +++ b/src/fw/services/legacy/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_LEGACY + bool "Legacy persist map" + help + Legacy persist-map support. + +if SERVICE_LEGACY + +module = SERVICE_LEGACY +module-str = Legacy persist map +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/legacy/persist_map.c b/src/fw/services/legacy/persist_map.c similarity index 84% rename from src/fw/services/normal/legacy/persist_map.c rename to src/fw/services/legacy/persist_map.c index e55efc6355..1b0a389093 100644 --- a/src/fw/services/normal/legacy/persist_map.c +++ b/src/fw/services/legacy/persist_map.c @@ -1,32 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "persist_map.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/legacy/persist_map.h" #include #include #include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/testinfra.h" #include "util/attributes.h" #include "util/math.h" +PBL_LOG_MODULE_DEFINE(service_legacy, CONFIG_SERVICE_LEGACY_LOG_LEVEL); + typedef struct PACKED { uint16_t version; } PersistMapHeader; @@ -47,7 +36,7 @@ static const uint16_t PERSIST_MAP_VERSION = 1; static const char *s_map_filename = "pmap"; -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD #include "system/hexdump.h" #include "system/testinfra.h" #define NUM_SUCCESSFUL_OPENS_TO_TRACK 2 @@ -58,7 +47,7 @@ static int s_next_pmap_fd_idx = 0; // TODO: Remove once we figure out PBL-20973 static void prv_pmap_grab_debug_fd_data(int fd) { -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD extern void pfs_collect_diagnostic_data(int fd, void *diagnostic_buf, size_t diagnostic_buf_len); pfs_collect_diagnostic_data(fd, &s_pmap_fd_diagnostic_data[s_next_pmap_fd_idx], @@ -72,7 +61,7 @@ static int prv_pmap_open_debug_wrapper(const char *name, uint8_t op_flags, uint8 size_t start_size) { int fd = pfs_open(name, op_flags, file_type, start_size); -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD if (fd < 0) { // pmap, where'd you go, we miss you so?! PBL_HEXDUMP_D(LOG_LEVEL_INFO, LOG_LEVEL_INFO, (uint8_t *)s_pmap_fd_diagnostic_data, sizeof(s_pmap_fd_diagnostic_data)); @@ -81,7 +70,7 @@ static int prv_pmap_open_debug_wrapper(const char *name, uint8_t op_flags, uint8 } else { prv_pmap_grab_debug_fd_data(fd); } -#endif /* IS_BIGBOARD */ +#endif /* CONFIG_IS_BIGBOARD */ return fd; } @@ -100,7 +89,7 @@ static int seek_map(int fd, SearchCallback callback, void *data) { if (PASSED(read_result) || read_result == E_RANGE) { return E_DOES_NOT_EXIST; } - PBL_LOG(LOG_LEVEL_WARNING, "seek_map failed: %d", read_result); + PBL_LOG_WRN("seek_map failed: %d", read_result); return read_result; } else if (field.id == EOF_PERSIST_ID_TAG) { break; @@ -116,7 +105,7 @@ static int seek_map(int fd, SearchCallback callback, void *data) { static int search_map(SearchCallback callback, void *data) { int fd = prv_pmap_open_debug_wrapper(s_map_filename, OP_FLAG_READ, 0, 0); if (fd < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "pmap search (open) failed: %d", fd); + PBL_LOG_WRN("pmap search (open) failed: %d", fd); return (fd); } @@ -147,11 +136,11 @@ static status_t enlarge_pmap_file(int *fd, size_t new_size) { if (buf == NULL) { return (E_OUT_OF_MEMORY); } - PBL_LOG(LOG_LEVEL_DEBUG, "Growing pmap to %d bytes", (int)new_size); + PBL_LOG_DBG("Growing pmap to %d bytes", (int)new_size); int new_fd = prv_pmap_open_debug_wrapper(s_map_filename, OP_FLAG_OVERWRITE, FILE_TYPE_STATIC, new_size); if (new_fd < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "pmap enlarge (overwrite) failed: %d", new_fd); + PBL_LOG_WRN("pmap enlarge (overwrite) failed: %d", new_fd); kernel_free(buf); return (new_fd); } @@ -177,7 +166,7 @@ static status_t enlarge_pmap_file(int *fd, size_t new_size) { *fd = prv_pmap_open_debug_wrapper(s_map_filename, OP_FLAG_READ | OP_FLAG_WRITE, 0, 0); if (*fd < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "pmap enlarge (re-open) failed: %d", *fd); + PBL_LOG_WRN("pmap enlarge (re-open) failed: %d", *fd); } kernel_free(buf); return (rv); @@ -189,7 +178,7 @@ int persist_map_add_uuid(const Uuid *uuid) { int fd = prv_pmap_open_debug_wrapper(s_map_filename, OP_FLAG_READ | OP_FLAG_WRITE, FILE_TYPE_STATIC, 0); if (fd < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "pmap add uuid (open) failed: %d", fd); + PBL_LOG_WRN("pmap add uuid (open) failed: %d", fd); return (fd); } @@ -208,7 +197,7 @@ int persist_map_add_uuid(const Uuid *uuid) { // user can take is to factory-reset if (file_sz > (PMAP_FILE_SIZE * 3)) { pfs_close(fd); - PBL_LOG(LOG_LEVEL_WARNING, "pmap file is larger than expected, 0x%x 0x%x", + PBL_LOG_WRN("pmap file is larger than expected, 0x%x 0x%x", (int)file_sz, (int)end_offset); return (E_INTERNAL); } @@ -221,7 +210,7 @@ int persist_map_add_uuid(const Uuid *uuid) { int seek_to; if ((seek_to = pfs_seek(fd, end_offset, FSeekSet)) != end_offset) { - PBL_LOG(LOG_LEVEL_WARNING, "Bad seek to %d, got %d", end_offset, seek_to); + PBL_LOG_WRN("Bad seek to %d, got %d", end_offset, seek_to); pfs_close(fd); return (seek_to); } @@ -294,13 +283,13 @@ static bool prv_dump_callback(PersistMapIdField *field, void *data) { char uuid_string[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(&field->uuid, uuid_string); - PBL_LOG(LOG_LEVEL_INFO, "%s -> %d", uuid_string, field->id); + PBL_LOG_INFO("%s -> %d", uuid_string, field->id); return false; } void persist_map_dump(void) { - PBL_LOG(LOG_LEVEL_INFO, "Dumping persist map:"); + PBL_LOG_INFO("Dumping persist map:"); search_map(prv_dump_callback, NULL); } @@ -313,7 +302,7 @@ status_t persist_map_init() { if (fd < 0) { if ((fd = prv_pmap_open_debug_wrapper(name, OP_FLAG_WRITE, FILE_TYPE_STATIC, PMAP_FILE_SIZE)) < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "pmap create failed: %d", fd); + PBL_LOG_WRN("pmap create failed: %d", fd); return (fd); } diff --git a/src/fw/services/legacy/wscript_build b/src/fw/services/legacy/wscript_build new file mode 100644 index 0000000000..6d7b160906 --- /dev/null +++ b/src/fw/services/legacy/wscript_build @@ -0,0 +1,10 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['persist_map.c'], + target='services_legacy', +) + +bld.env.SERVICES.append('services_legacy') diff --git a/src/fw/services/light/Kconfig b/src/fw/services/light/Kconfig new file mode 100644 index 0000000000..515d439c10 --- /dev/null +++ b/src/fw/services/light/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_LIGHT + bool "Light" + help + Backlight control service. + +if SERVICE_LIGHT + +module = SERVICE_LIGHT +module-str = Light +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/light/service.c b/src/fw/services/light/service.c new file mode 100644 index 0000000000..20b492edfc --- /dev/null +++ b/src/fw/services/light/service.c @@ -0,0 +1,616 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/light.h" + +#include "board/board.h" +#include "drivers/ambient_light.h" +#include "drivers/backlight.h" +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +#include "drivers/backlight.h" +#endif +#include "drivers/rtc.h" +#include "kernel/events.h" +#include "kernel/low_power.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/new_timer/new_timer.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "os/mutex.h" +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "semphr.h" +#include "task.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_light, CONFIG_SERVICE_LIGHT_LOG_LEVEL); + +typedef enum { + LIGHT_STATE_ON = 1, // backlight on, no timeouts + LIGHT_STATE_ON_TIMED = 2, // backlight on, will start fading after a period + LIGHT_STATE_ON_FADING = 3, // backlight in the process of fading out + LIGHT_STATE_OFF = 4, // backlight off; idle state +} BacklightState; + +// the time duration of the fade out +const uint32_t LIGHT_FADE_TIME_MS = 500; +// number of fade-out steps +const uint8_t LIGHT_FADE_STEPS = 20; + +/* + * ^ + * | + * LIGHT_ON | +---------------------------------+ + * | / \ + * | / \ + * | / \ + * | / \ + * | / \ + * LIGHT_ON/2 | /+ +\ + * | / | | \ + * | / | | \ + * | / | | \ + * | / | | \ + * | / | | \ + * |/ | | \ + * LIGHT_OFF +-------|-------------------------------------------|---------> + * | | + * |<----------------------------------------->| + * Integrate over this range for the mean + */ + +//! The current state of the backlight (example: ON/ON_TIMED/ON_FADING). +static BacklightState s_light_state; + +//! The brightness of the display in a range between 0 and 100 +static uint8_t s_current_brightness; + +//! Timer to count down from the LIGHT_STATE_ON_TIMED state. +static TimerID s_timer_id; + +//! Refcount of the number of buttons that are currently pushed +static int s_num_buttons_down; + +//! The current app is forcing the light on and off, don't muck with it. +static bool s_user_controlled_state; + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +//! The app's requested backlight tint. Valid only when s_app_rgb_override_valid +//! is true; otherwise the LED uses the user default (white). +static uint32_t s_app_rgb_override; +static bool s_app_rgb_override_valid; + +//! Count of active modal preempts (notifications, etc.) that temporarily +//! mask any app RGB override. The app's override stays stored; only while +//! this refcount is non-zero is the LED driven to the default color. +//! When the refcount returns to zero, the override (if any) is re-applied. +static uint8_t s_color_preempt_refcount; +#endif + +//! For temporary disabling backlight (ie: low power mode) +static bool s_backlight_allowed = false; + +//! Starting intensity for fade-out (captured when fade begins) +static uint8_t s_fade_start_intensity = 0; + +//! Fade step size (calculated once at start of fade to avoid rounding jitter) +static uint8_t s_fade_step_size = 0; + +//! Mutex to guard all the above state. We have a pattern of taking the lock in the public functions and assuming +//! it's already taken in the prv_ functions. +static PebbleMutex *s_mutex; + +//! Analytics: Track time-weighted average intensity +static uint64_t s_intensity_time_product_sum; // Sum of (intensity_pct × time_ms) +static RtcTicks s_last_intensity_sample_ticks; // Timestamp of last sample +static uint8_t s_last_sampled_intensity_pct; // Last intensity percentage sampled +static uint32_t s_total_on_time_ms; // Total backlight on time tracked internally + +//! Short-lived cache so back-to-back ALS consumers in the same wake path +//! (prv_light_allowed → prv_backlight_get_intensity, plus a button release +//! that follows the press within the TTL) skip the ~200 ms I2C poll. +static uint32_t s_als_cached_level; +static RtcTicks s_als_cached_ticks; // 0 = invalid +#define ALS_CACHE_TTL_TICKS (RTC_TICKS_HZ) // 1 second + +static void prv_change_state(BacklightState new_state); + +static uint32_t prv_get_als_level(void) { + RtcTicks now = rtc_get_ticks(); + const bool cache_valid = + s_als_cached_ticks != 0 && + (s_current_brightness > 0 || (now - s_als_cached_ticks) < ALS_CACHE_TTL_TICKS); + if (cache_valid) { + return s_als_cached_level; + } + s_als_cached_level = ambient_light_get_light_level(); + s_als_cached_ticks = now; + return s_als_cached_level; +} + +static bool prv_als_is_light(void) { + return prv_get_als_level() > ambient_light_get_dark_threshold(); +} + +static void light_timer_callback(void *data) { + mutex_lock(s_mutex); + prv_change_state(LIGHT_STATE_ON_FADING); + mutex_unlock(s_mutex); +} + +static uint8_t prv_backlight_get_intensity(void) { + // low_power_mode backlight intensity (25% of max brightness) + const uint8_t backlight_low_power_intensity = 25; + + if (low_power_is_active()) { + return backlight_low_power_intensity; + } + +#if defined(CONFIG_DYNAMIC_BACKLIGHT) && !defined(CONFIG_RECOVERY_FW) + // Dynamic backlight: dim in utter darkness, otherwise user max. The + // bright-outdoor case is already filtered upstream by prv_light_allowed() + // for ambient-sensor-enabled wakes; the few paths that bypass that gate + // (app-driven force-on, ambient-sensor pref off) sensibly land at max here. + if (backlight_is_dynamic_intensity_enabled()) { + const uint8_t dim_intensity = 10; + if (prv_get_als_level() <= backlight_get_dynamic_min_threshold()) { + return dim_intensity; + } + } +#endif + + return backlight_get_intensity(); +} + +static void prv_update_intensity_analytics(uint8_t new_intensity_pct) { + RtcTicks now_ticks = rtc_get_ticks(); + + // Calculate time delta in ms since last sample + if (s_last_intensity_sample_ticks > 0) { + uint32_t time_delta_ms = ((now_ticks - s_last_intensity_sample_ticks) * 1000) / RTC_TICKS_HZ; + + // Accumulate intensity × time for weighted average calculation + // Use last sampled intensity for the period that just elapsed + s_intensity_time_product_sum += (uint64_t)s_last_sampled_intensity_pct * time_delta_ms; + + // Track total on-time when intensity is above zero + if (s_last_sampled_intensity_pct > 0) { + s_total_on_time_ms += time_delta_ms; + } + } + + // Update tracking variables + s_last_intensity_sample_ticks = now_ticks; + s_last_sampled_intensity_pct = new_intensity_pct; +} + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +//! LED color to drive when no app has set an override. Backed by the +//! user's stored backlight-color preference, defaulting to BACKLIGHT_COLOR_WARM_WHITE. +static void prv_apply_rgb_color(void) { + const bool preempted = (s_color_preempt_refcount > 0); + const uint32_t color = (preempted || !s_app_rgb_override_valid) + ? backlight_get_default_color() + : s_app_rgb_override; + backlight_set_color(color); +} +#endif + +static void prv_change_brightness(uint8_t new_brightness) { + // Scale the 0-100% to the maximum value allowed in hardware + uint8_t scaled_brightness = (new_brightness * (uint16_t)BOARD_CONFIG.backlight_on_percent) / 100U; + + if (new_brightness == 0U) { + PBL_ANALYTICS_TIMER_STOP(backlight_on_time_ms); + } else { + PBL_ANALYTICS_TIMER_START(backlight_on_time_ms); + } + + prv_update_intensity_analytics(scaled_brightness); + + backlight_set_brightness(scaled_brightness); + s_current_brightness = new_brightness; + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + // backlight_set_brightness re-applies the last RGB color at the new + // intensity; follow up with the app override (or default white) so the + // LED reflects the current color request for this state change. + prv_apply_rgb_color(); +#endif +} + +static void prv_change_state(BacklightState new_state) { + BacklightState old_state = s_light_state; + s_light_state = new_state; + + // Calculate the new brightness and reset any timers based on our state. + uint8_t new_brightness = 0; + + switch (new_state) { + case LIGHT_STATE_ON: + new_brightness = prv_backlight_get_intensity(); + new_timer_stop(s_timer_id); + break; + case LIGHT_STATE_ON_TIMED: + new_brightness = prv_backlight_get_intensity(); + + // Schedule the timer to move us from the ON_TIMED state to the ON_FADING state + new_timer_start(s_timer_id, backlight_get_timeout_ms(), + light_timer_callback, NULL, 0 /* flags */); + break; + case LIGHT_STATE_ON_FADING: + // Capture the starting intensity only when we first enter fading state + if (old_state != LIGHT_STATE_ON_FADING) { + s_fade_start_intensity = s_current_brightness; + s_fade_step_size = s_fade_start_intensity / LIGHT_FADE_STEPS; + if (s_fade_step_size == 0) { + s_fade_step_size = 1; + } + } + + if (s_fade_step_size >= s_current_brightness) { + new_brightness = 0; + s_light_state = LIGHT_STATE_OFF; + } else { + new_brightness = s_current_brightness - s_fade_step_size; + + // Reschedule the timer so we step down the brightness again. + new_timer_start(s_timer_id, LIGHT_FADE_TIME_MS / LIGHT_FADE_STEPS, light_timer_callback, NULL, 0 /* flags */); + } + break; + case LIGHT_STATE_OFF: + new_brightness = 0; + new_timer_stop(s_timer_id); + break; + } + + if (s_current_brightness != new_brightness) { + prv_change_brightness(new_brightness); + } else if (new_state == LIGHT_STATE_ON || new_state == LIGHT_STATE_ON_TIMED) { + // A user-initiated wake with unchanged brightness would otherwise + // short-circuit. Re-apply the driver state so the hardware is brought back + // in sync if it has drifted from what the firmware believes. + backlight_refresh(); + } + + // Notify subscribers when the backlight transitions between on and off. + // Treat any non-OFF state as "on" so apps see a single edge per wake. + const bool was_on = (old_state != LIGHT_STATE_OFF); + const bool is_on = (s_light_state != LIGHT_STATE_OFF); + if (was_on != is_on) { + PebbleEvent event = { + .type = PEBBLE_BACKLIGHT_EVENT, + .backlight = { + .is_on = is_on, + }, + }; + event_put(&event); + } +} + +static bool prv_light_allowed(void) { + if (!s_backlight_allowed) { + return false; + } + + if (backlight_is_enabled()) { + if (backlight_is_ambient_sensor_enabled()) { + // If the light is off and it's bright outside, don't allow the light to turn on + // (we don't need it!). Grab the mutex here so that the timer state machine doesn't change + // the light brightness while we're checking the ambient light levels. + bool allowed = !((s_current_brightness == 0) && prv_als_is_light()); + return allowed; + } else { + return true; + } + } else { + return false; + } +} + +void light_init(void) { + s_light_state = LIGHT_STATE_OFF; + s_current_brightness = 0; + s_timer_id = new_timer_create(); + s_num_buttons_down = 0; + s_user_controlled_state = false; + s_fade_start_intensity = 0; + s_fade_step_size = 0; + s_mutex = mutex_create(); + + // Initialize intensity analytics tracking + s_intensity_time_product_sum = 0; + s_last_intensity_sample_ticks = 0; + s_last_sampled_intensity_pct = 0; + s_total_on_time_ms = 0; + + s_als_cached_level = 0; + s_als_cached_ticks = 0; +} + +void light_button_pressed(void) { + mutex_lock(s_mutex); + + s_num_buttons_down++; + if (s_num_buttons_down > 4) { + PBL_LOG_ERR("More buttons were pressed than have been released."); + s_num_buttons_down = 0; + } + + // set the state to be on; releasing buttons will start the timer counting down + if (prv_light_allowed()) { + prv_change_state(LIGHT_STATE_ON); + } + + mutex_unlock(s_mutex); +} + +void light_button_released(void) { + mutex_lock(s_mutex); + + s_num_buttons_down--; + if (s_num_buttons_down < 0) { + PBL_LOG_ERR("More buttons were released than have been pressed."); + s_num_buttons_down = 0; + } + + if (s_num_buttons_down == 0 && + s_light_state == LIGHT_STATE_ON && + !s_user_controlled_state) { + // no more buttons pressed: wait for a bit and then start the fade-out timer + prv_change_state(LIGHT_STATE_ON_TIMED); + } + + mutex_unlock(s_mutex); +} + +void light_enable_interaction(void) { + mutex_lock(s_mutex); + + //if some buttons are held or light_enable is asserted, do nothing + if (s_num_buttons_down > 0 || s_light_state == LIGHT_STATE_ON) { + mutex_unlock(s_mutex); + return; + } + + if (prv_light_allowed()) { + prv_change_state(LIGHT_STATE_ON_TIMED); + } else { + PBL_LOG_INFO("Backlight rejected: allowed=%d, brightness=%" PRIu8 ", is_light=%d", + s_backlight_allowed, s_current_brightness, prv_als_is_light()); + } + + mutex_unlock(s_mutex); +} + +void light_enable(bool enable) { + mutex_lock(s_mutex); + + // This function is a bit of a black sheep - it dives in and messes with the normal + // flow of the state machine. + // We don't actually use it, but it is now documented and used in the SDK, so + // I am reluctant to chop it out. + + s_user_controlled_state = enable; + + if (enable) { + prv_change_state(LIGHT_STATE_ON); + } else if (s_num_buttons_down == 0) { + // reset the state if someone calls light_enable(false); + // (unless there are buttons pressed, then leave the backlight on) + prv_change_state(LIGHT_STATE_OFF); + } + + mutex_unlock(s_mutex); +} + +void light_enable_respect_settings(bool enable) { + mutex_lock(s_mutex); + + s_user_controlled_state = enable; + + if (enable) { + if (prv_light_allowed()) { + prv_change_state(LIGHT_STATE_ON); + } + } else if (s_num_buttons_down == 0) { + prv_change_state(LIGHT_STATE_OFF); + } + + mutex_unlock(s_mutex); +} + +void light_reset_user_controlled(void) { + mutex_lock(s_mutex); + + // http://www.youtube.com/watch?v=6t_KgE6Yuqg + if (s_user_controlled_state) { + s_user_controlled_state = false; + + if (s_num_buttons_down == 0) { + prv_change_state(LIGHT_STATE_OFF); + } + } + + mutex_unlock(s_mutex); +} + +void light_set_color_rgb888(uint32_t rgb) { +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + mutex_lock(s_mutex); + s_app_rgb_override = rgb & 0x00FFFFFF; + s_app_rgb_override_valid = true; + if (s_light_state != LIGHT_STATE_OFF) { + prv_apply_rgb_color(); + } + mutex_unlock(s_mutex); +#else + (void)rgb; +#endif +} + +void light_set_system_color(void) { +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + mutex_lock(s_mutex); + if (s_app_rgb_override_valid) { + s_app_rgb_override_valid = false; + if (s_light_state != LIGHT_STATE_OFF) { + prv_apply_rgb_color(); + } + } + mutex_unlock(s_mutex); +#endif +} + +void light_system_color_request(void) { +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + mutex_lock(s_mutex); + if (s_color_preempt_refcount < UINT8_MAX) { + s_color_preempt_refcount++; + } + if (s_color_preempt_refcount == 1 && s_light_state != LIGHT_STATE_OFF) { + prv_apply_rgb_color(); + } + mutex_unlock(s_mutex); +#endif +} + +void light_system_color_release(void) { +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + mutex_lock(s_mutex); + if (s_color_preempt_refcount > 0) { + s_color_preempt_refcount--; + if (s_color_preempt_refcount == 0 && s_light_state != LIGHT_STATE_OFF) { + prv_apply_rgb_color(); + } + } + mutex_unlock(s_mutex); +#endif +} + +static void prv_light_reset_to_timed_mode(void) { + mutex_lock(s_mutex); + + if (s_user_controlled_state) { + s_user_controlled_state = false; + if (prv_light_allowed()) { + prv_change_state(LIGHT_STATE_ON_TIMED); + } + } + + mutex_unlock(s_mutex); +} + +void light_toggle_enabled(void) { + mutex_lock(s_mutex); + + // Toggling the setting is the user's only escape hatch if some path left + // s_user_controlled_state stuck on. Clear it here so a subsequent button + // release will actually start the auto-off timer. + s_user_controlled_state = false; + + backlight_set_enabled(!backlight_is_enabled()); + if (prv_light_allowed()) { + prv_change_state(LIGHT_STATE_ON_TIMED); + } else { + prv_change_state(LIGHT_STATE_OFF); + } + mutex_unlock(s_mutex); +} + +void light_toggle_ambient_sensor_enabled(void) { + mutex_lock(s_mutex); + s_user_controlled_state = false; + backlight_set_ambient_sensor_enabled(!backlight_is_ambient_sensor_enabled()); + if (prv_light_allowed() && !prv_als_is_light()) { + prv_change_state(LIGHT_STATE_ON_TIMED); + } else { + prv_change_state(LIGHT_STATE_OFF); + // FIXME: PBL-24793 There is an edge case of when the backlight has timed off + // or you're toggling it from no ambient (always light on buttons) to ambient, + // you will see it turn on and immediately off if its bright out + } + mutex_unlock(s_mutex); +} + +void light_toggle_dynamic_intensity_enabled(void) { +#ifdef CONFIG_DYNAMIC_BACKLIGHT + mutex_lock(s_mutex); + backlight_set_dynamic_intensity_enabled(!backlight_is_dynamic_intensity_enabled()); + if (prv_light_allowed()) { + prv_change_state(LIGHT_STATE_ON_TIMED); + } + mutex_unlock(s_mutex); +#endif +} + +void light_allow(bool allowed) { + if (s_backlight_allowed && !allowed) { + prv_change_state(LIGHT_STATE_OFF); + } + s_backlight_allowed = allowed; +} + +DEFINE_SYSCALL(bool, sys_light_is_on, void) { + return light_is_on(); +} + +DEFINE_SYSCALL(void, sys_light_enable_interaction, void) { + light_enable_interaction(); +} + +DEFINE_SYSCALL(void, sys_light_enable, bool enable) { + light_enable(enable); +} + +DEFINE_SYSCALL(void, sys_light_enable_respect_settings, bool enable) { + light_enable_respect_settings(enable); +} + +DEFINE_SYSCALL(void, sys_light_reset_to_timed_mode, void) { + prv_light_reset_to_timed_mode(); +} + +DEFINE_SYSCALL(void, sys_light_set_color_rgb888, uint32_t rgb) { + light_set_color_rgb888(rgb); +} + +DEFINE_SYSCALL(void, sys_light_set_system_color, void) { + light_set_system_color(); +} + +extern BacklightBehaviour backlight_get_behaviour(void); + +uint8_t light_get_current_brightness_percent(void) { + return s_current_brightness; +} + +bool light_is_on(void) { + return s_light_state != LIGHT_STATE_OFF; +} + +void pbl_analytics_external_collect_backlight_stats(void) { + mutex_lock(s_mutex); + + // Capture one final sample to account for time since last brightness change + prv_update_intensity_analytics(s_current_brightness); + + // Calculate time-weighted average intensity using internally tracked on-time + uint32_t avg_intensity_pct = 0; + if (s_total_on_time_ms > 0) { + // Calculate weighted average: sum(intensity × time) / total_time + avg_intensity_pct = s_intensity_time_product_sum / s_total_on_time_ms; + } + + PBL_ANALYTICS_SET_UNSIGNED(backlight_avg_intensity_pct, avg_intensity_pct); + + // Reset accumulators for next period + s_intensity_time_product_sum = 0; + s_total_on_time_ms = 0; + s_last_intensity_sample_ticks = rtc_get_ticks(); + + mutex_unlock(s_mutex); +} diff --git a/src/fw/services/light/wscript_build b/src/fw/services/light/wscript_build new file mode 100644 index 0000000000..da8ec09317 --- /dev/null +++ b/src/fw/services/light/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c'], + target='services_light', +) +bld.env.SERVICES.append('services_light') diff --git a/src/fw/services/music/Kconfig b/src/fw/services/music/Kconfig new file mode 100644 index 0000000000..5ddd914d8f --- /dev/null +++ b/src/fw/services/music/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_MUSIC + bool "Music" + help + Music control service. + +if SERVICE_MUSIC + +module = SERVICE_MUSIC +module-str = Music +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/music/endpoint.c b/src/fw/services/music/endpoint.c new file mode 100644 index 0000000000..861d8b8a29 --- /dev/null +++ b/src/fw/services/music/endpoint.c @@ -0,0 +1,308 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/music_endpoint.h" +#include "pbl/services/music_endpoint_types.h" + +#include "comm/ble/kernel_le_client/ams/ams.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_remote_os.h" +#include "pbl/services/music_internal.h" +#include "system/logging.h" +#include "util/math.h" + +PBL_LOG_MODULE_DECLARE(service_music, CONFIG_SERVICE_MUSIC_LOG_LEVEL); + +static const uint16_t MUSIC_CTRL_ENDPOINT = 0x20; + +static bool s_connected; +static bool s_progress_reporting_supported = true; + +static void prv_send_music_command_to_handset(MusicEndpointCmdID cmd) { + CommSession *session = comm_session_get_system_session(); + if (!session) { + PBL_LOG_ERR("No system session"); + return; + } + comm_session_send_data(session, MUSIC_CTRL_ENDPOINT, + (const uint8_t *)&cmd, 1, COMM_SESSION_DEFAULT_TIMEOUT); +} + +static const uint8_t* prv_read_ptr_and_length_from_buffer(const uint8_t *iter, + const uint8_t *iter_end, + const char** out_str, + size_t *out_length) { + if (!out_str || !out_length) { + return NULL; + } + + *out_length = *iter; + *out_str = (const char*) iter + 1; + + iter += 1 + *out_length; + if (iter > iter_end) { + PBL_LOG_WRN("Invalid music message"); + return NULL; + } + return iter; +} + +static void prv_update_now_playing_info(CommSession *session, const uint8_t* msg, size_t length) { + // Read all the lengths from the message so we know how to break it up. + const uint8_t* read_iter = msg; + const char* artist_ptr; + size_t artist_length; + + read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, + &artist_ptr, &artist_length); + if (!read_iter) { + return; + } + + const char* album_ptr; + size_t album_length; + read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, + &album_ptr, &album_length); + if (!read_iter) { + return; + } + + const char* title_ptr; + size_t title_length; + read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, + &title_ptr, &title_length); + if (!read_iter) { + return; + } + + music_update_now_playing(title_ptr, title_length, + artist_ptr, artist_length, + album_ptr, album_length); + + if (comm_session_has_capability(session, CommSessionExtendedMusicService)) { + if (read_iter + sizeof(uint32_t) <= msg + length) { + uint32_t track_duration_ms = *(uint32_t *)read_iter; + music_update_track_duration(track_duration_ms); + } + // TODO: Do something with this info + // read_iter += 4; + // if (read_iter + sizeof(uint16_t) <= msg + length) { + // uint16_t num_tracks = *(uint16_t *)read_iter; + // } + + // TODO: Do something with this info + // read_iter += 2; + // if (read_iter + sizeof(uint16_t) <= msg + length) { + // uint16_t idx_curr_track = *(uint16_t *)read_iter; + // } + } +} + +static void prv_update_play_state_info(CommSession *session, const uint8_t* msg, size_t length) { + if (length < sizeof(MusicEndpointPlayStateInfo)) { + return; + } + MusicEndpointPlayStateInfo *play_state_info = (MusicEndpointPlayStateInfo*) msg; + MusicPlayerStateUpdate player_state_update; + + switch (play_state_info->play_state) { + case MusicEndpointPlaybackStatePaused: + player_state_update.playback_state = MusicPlayStatePaused; + break; + case MusicEndpointPlaybackStatePlaying: + player_state_update.playback_state = MusicPlayStatePlaying; + break; + case MusicEndpointPlaybackStateRewinding: + player_state_update.playback_state = MusicPlayStateRewinding; + break; + case MusicEndpointPlaybackStateForwarding: + player_state_update.playback_state = MusicPlayStateForwarding; + break; + case MusicEndpointPlaybackStateUnknown: + player_state_update.playback_state = MusicPlayStateUnknown; + break; + default: + player_state_update.playback_state = MusicPlayStateInvalid; + } + player_state_update.playback_rate_percent = play_state_info->play_rate; + s_progress_reporting_supported = (play_state_info->track_pos_ms >= 0); + player_state_update.elapsed_time_ms = MAX(play_state_info->track_pos_ms, 0); + // TODO: Do something with this info + // play_state_info->play_shuffle_mode; + // play_state_info->play_repeat_mode; + + music_update_player_playback_state(&player_state_update); +} + +static void prv_update_volume_info(CommSession *session, const uint8_t* msg, size_t length) { + if (length < sizeof(uint8_t)) { + return; + } + music_update_player_volume_percent((uint8_t)*msg); +} + +static void prv_update_player_info(CommSession *session, const uint8_t* msg, size_t length) { + // Read all the lengths from the message so we know how to break it up. + const uint8_t* read_iter = msg; + + const char* player_package_ptr; + size_t player_package_length; + read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, + &player_package_ptr, &player_package_length); + if (!read_iter) { + return; + } + + const char* player_name_ptr; + size_t player_name_length; + read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, + &player_name_ptr, &player_name_length); + if (!read_iter) { + return; + } + // Not doing anything with player package name + music_update_player_name(player_name_ptr, player_name_length); +} + +void music_protocol_msg_callback(CommSession *session, const uint8_t* msg, size_t length) { + if (!s_connected) { + return; + } + --length; + + switch (*(msg++)) { + case MusicEndpointCmdIDNowPlayingInfoResponse: + prv_update_now_playing_info(session, msg, length); + break; + case MusicEndpointCmdIDPlayStateInfoResponse: + prv_update_play_state_info(session, msg, length); + break; + case MusicEndpointCmdIDVolumeInfoResponse: + prv_update_volume_info(session, msg, length); + break; + case MusicEndpointCmdIDPlayerInfoResponse: + prv_update_player_info(session, msg, length); + break; + default: + PBL_LOG_DBG("Invalid command 0x%"PRIx8, msg[0]); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// MusicServerImplementation + +static MusicEndpointCmdID prv_pp_command_for_music_command(MusicCommand command) { + switch (command) { + case MusicCommandPlay: + return MusicEndpointCmdIDPlay; + case MusicCommandPause: + return MusicEndpointCmdIDPause; + case MusicCommandTogglePlayPause: + return MusicEndpointCmdIDTogglePlayPause; + case MusicCommandNextTrack: + return MusicEndpointCmdIDNextTrack; + case MusicCommandPreviousTrack: + return MusicEndpointCmdIDPreviousTrack; + case MusicCommandVolumeUp: + return MusicEndpointCmdIDVolumeUp; + case MusicCommandVolumeDown: + return MusicEndpointCmdIDVolumeDown; + + case MusicCommandAdvanceRepeatMode: + case MusicCommandAdvanceShuffleMode: + case MusicCommandSkipForward: + case MusicCommandSkipBackward: + default: + return MusicEndpointCmdIDInvalid; + } +} + +static bool prv_music_is_command_supported(MusicCommand command) { + return (prv_pp_command_for_music_command(command) != MusicEndpointCmdIDInvalid); +} + +static void prv_music_command_send(MusicCommand command) { + const MusicEndpointCmdID pp_command = prv_pp_command_for_music_command(command); + if (pp_command == MusicEndpointCmdIDInvalid) { + return; + } + + prv_send_music_command_to_handset(pp_command); +} + +static MusicServerCapability prv_music_get_capability_bitset(void) { + if (comm_session_has_capability(comm_session_get_system_session(), + CommSessionExtendedMusicService)) { + if (s_progress_reporting_supported) { + return (MusicServerCapabilityPlaybackStateReporting | + MusicServerCapabilityProgressReporting | + MusicServerCapabilityVolumeReporting); + } else { + return (MusicServerCapabilityPlaybackStateReporting | MusicServerCapabilityVolumeReporting); + } + } else { + return MusicServerCapabilityNone; + } +} + +static bool prv_music_needs_user_to_start_playback_on_phone(void) { + return false; // On Android, we can initiate playback from Pebble. +} + +static void prv_music_request_reduced_latency(bool reduced_latency) { + const ResponseTimeState state = reduced_latency ? ResponseTimeMiddle : ResponseTimeMax; + comm_session_set_responsiveness(comm_session_get_system_session(), + BtConsumerMusicServiceIndefinite, state, + MAX_PERIOD_RUN_FOREVER); +} + +static void prv_music_request_low_latency_for_period(uint32_t period_ms) { + comm_session_set_responsiveness(comm_session_get_system_session(), + BtConsumerMusicServiceMomentary, + ResponseTimeMin, + period_ms / MS_PER_SECOND); +} + +static const MusicServerImplementation s_pp_music_implementation = { + .debug_name = "PP", + .is_command_supported = &prv_music_is_command_supported, + .command_send = &prv_music_command_send, + .needs_user_to_start_playback_on_phone = prv_music_needs_user_to_start_playback_on_phone, + .get_capability_bitset = prv_music_get_capability_bitset, + .request_reduced_latency = prv_music_request_reduced_latency, + .request_low_latency_for_period = prv_music_request_low_latency_for_period, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +static void prv_set_connected(bool connected) { + if (s_connected == connected) { + return; // Expected to happen because this is called with `false` for any OS + } + if (music_set_connected_server(&s_pp_music_implementation, connected)) { + s_connected = connected; + } else { + s_connected = false; + } + if (s_connected) { + // Request initial state: + prv_send_music_command_to_handset(MusicEndpointCmdIDGetAllInfo); + } +} + +void music_endpoint_handle_mobile_app_info_event(const PebbleRemoteAppInfoEvent *app_info_event) { + if (app_info_event->os != RemoteOSAndroid) { + // Only on Android we use Pebble Protocol for music metadata and control. + return; + } + + ams_music_disconnect(); + prv_set_connected(true); +} + +void music_endpoint_handle_mobile_app_event(const PebbleCommSessionEvent *app_event) { + if (!app_event->is_open && app_event->is_system) { + // Pebble mobile app went away, communicate the disconnection to the upper layers: + prv_set_connected(false); + } +} diff --git a/src/fw/services/music/service.c b/src/fw/services/music/service.c new file mode 100644 index 0000000000..5a902ab121 --- /dev/null +++ b/src/fw/services/music/service.c @@ -0,0 +1,450 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/music_internal.h" + +#include "apps/system/music.h" +#include "drivers/rtc.h" +#include "kernel/events.h" +#include "process_management/app_manager.h" +#include "shell/system_app_ids.auto.h" +#include "os/mutex.h" +#include "os/tick.h" +#include "system/logging.h" +#include "util/math.h" + +PBL_LOG_MODULE_DEFINE(service_music, CONFIG_SERVICE_MUSIC_LOG_LEVEL); + +//! @file This module implements the music service. It provides an abstraction layer on top of the +//! various underlying music metadata and control services: the Pebble Protocol +//! music endpoint (see music_endpoint.c) and Apple Media Service (see ams.c). +//! This module also caches the last known metadata and media player state. +//! @note Only one underlying backend is supported at a time. If a second backend tries to "connect" +//! it is ignored. + +#define MUSIC_NORMAL_PLAYBACK_RATE_PERCENT ((int32_t) 100) + +//! Cache of the most recently received now playing data. Note that this is read and written from +//! multiple threads, so access is protected by the mutex member. +struct MusicServiceContext { + PebbleRecursiveMutex *mutex; + + //! The connected server that provides media metadata and accepts control commands + const MusicServerImplementation *implementation; + + //! The volume setting of the current player + uint8_t player_volume_percent; + + char player_name[MUSIC_BUFFER_LENGTH]; + + char title[MUSIC_BUFFER_LENGTH]; + char artist[MUSIC_BUFFER_LENGTH]; + char album[MUSIC_BUFFER_LENGTH]; + + uint32_t track_length_ms; + + //! Position that was last communicated to Pebble by the server. + //! @note This is not necessarily the actual position. See music_get_pos() + uint32_t track_pos_ms; + + //! The time when track_pos_ms was last updated. + RtcTicks track_pos_updated_at; + + //! The current playback rate in percent units. + //! Example values: + //! 100 = normal playback rate + //! 0 = paused + //! 200 = 2x playback rate (Apple's Podcast app can vary the playback rate) + //! -100 = backwards at normal rate + int32_t playback_rate_percent; + + //! The current playback state + MusicPlayState playback_state; +} s_music_ctx; + +void music_init(void) { + s_music_ctx.mutex = mutex_create_recursive(); +} + +static void copy_and_truncate(char *dest, const char *src, size_t src_length) { + size_t cropped_length = MIN(MUSIC_BUFFER_LENGTH - 1, src_length); + if (src) { + memcpy(dest, src, cropped_length); + } + dest[cropped_length] = 0; +} + +static void prv_put_now_playing_changed_event(void) { + PebbleEvent e = { + .type = PEBBLE_MEDIA_EVENT, + .media.type = PebbleMediaEventTypeNowPlayingChanged + }; + event_put(&e); +} + +bool music_set_connected_server(const MusicServerImplementation *implementation, bool connected) { + enum { + Disconnected = -1, + None = 0, + Connected = 1, + } change_type = None; + + mutex_lock_recursive(s_music_ctx.mutex); + + if (connected) { + if (s_music_ctx.implementation == NULL) { + change_type = Connected; + s_music_ctx.implementation = implementation; + PBL_LOG_INFO("Music server connected: %s", implementation->debug_name); + } else { + PBL_LOG_ERR("Server <0x%p> connected, but another <0x%p> is already registered", + implementation, s_music_ctx.implementation); + } + + } else { + if (s_music_ctx.implementation == implementation) { + // Previously registered server got disconnected + change_type = Disconnected; + s_music_ctx.implementation = NULL; + PBL_LOG_INFO("Music server disconnected: %s", implementation->debug_name); + } else { + PBL_LOG_ERR("Unknown server <%p> disconnected", implementation); + } + } + + if (change_type != None) { + // Upon connect and disconnect, reset the cached data: + + music_update_player_volume_percent(0); + // Taking short-cut here, music_update_now_playing already puts NowPlayingChanged event, no + // need to put it again by calling music_update_player_name: + s_music_ctx.player_name[0] = 0; + music_update_now_playing(NULL, 0, NULL, 0, NULL, 0); + music_update_track_duration(0); + const MusicPlayerStateUpdate state = { + .playback_state = MusicPlayStateUnknown, + .playback_rate_percent = 0, + .elapsed_time_ms = 0, + }; + music_update_player_playback_state(&state); + + PebbleEvent event = { + .type = PEBBLE_MEDIA_EVENT, + .media = { + .type = (change_type == Connected) ? PebbleMediaEventTypeServerConnected : + PebbleMediaEventTypeServerDisconnected, + }, + }; + event_put(&event); + } + + mutex_unlock_recursive(s_music_ctx.mutex); + + return (change_type != None); +} + +const char * music_get_connected_server_debug_name(void) { + const char *debug_name = NULL; + mutex_lock_recursive(s_music_ctx.mutex); + if (s_music_ctx.implementation) { + debug_name = s_music_ctx.implementation->debug_name; + } + mutex_unlock_recursive(s_music_ctx.mutex); + return debug_name; +} + +void music_update_now_playing(const char *title, size_t title_length, + const char *artist, size_t artist_length, + const char *album, size_t album_length) { + mutex_lock_recursive(s_music_ctx.mutex); + + copy_and_truncate(s_music_ctx.title, title, title_length); + copy_and_truncate(s_music_ctx.artist, artist, artist_length); + copy_and_truncate(s_music_ctx.album, album, album_length); + + mutex_unlock_recursive(s_music_ctx.mutex); + + prv_put_now_playing_changed_event(); +} + +static void prv_update_string_and_put_event(const char *value, size_t value_length, off_t offset) { + mutex_lock_recursive(s_music_ctx.mutex); + char *buffer = ((char *) &s_music_ctx) + offset; + copy_and_truncate(buffer, value, value_length); + mutex_unlock_recursive(s_music_ctx.mutex); + prv_put_now_playing_changed_event(); +} + +void music_update_player_name(const char *player_name, size_t player_name_length) { + // TODO: actually do something with this + off_t o = offsetof(__typeof__(s_music_ctx), player_name); + prv_update_string_and_put_event(player_name, player_name_length, o); +} + +void music_update_track_title(const char *title, size_t title_length) { + off_t o = offsetof(__typeof__(s_music_ctx), title); + prv_update_string_and_put_event(title, title_length, o); +} + +void music_update_track_artist(const char *artist, size_t artist_length) { + off_t o = offsetof(__typeof__(s_music_ctx), artist); + prv_update_string_and_put_event(artist, artist_length, o); +} + +void music_update_track_album(const char *album, size_t album_length) { + off_t o = offsetof(__typeof__(s_music_ctx), album); + prv_update_string_and_put_event(album, album_length, o); +} + +static void prv_put_pos_changed_event(void) { + PebbleEvent e = { + .type = PEBBLE_MEDIA_EVENT, + .media.type = PebbleMediaEventTypeTrackPosChanged, + }; + event_put(&e); +} + +void music_update_track_position(uint32_t track_pos_ms) { + mutex_lock_recursive(s_music_ctx.mutex); + + s_music_ctx.track_pos_ms = track_pos_ms; + s_music_ctx.track_pos_updated_at = rtc_get_ticks(); + + mutex_unlock_recursive(s_music_ctx.mutex); + + prv_put_pos_changed_event(); +} + +void music_update_track_duration(uint32_t track_duration_ms) { + mutex_lock_recursive(s_music_ctx.mutex); + + s_music_ctx.track_length_ms = track_duration_ms; + + mutex_unlock_recursive(s_music_ctx.mutex); + + prv_put_pos_changed_event(); +} + +void music_get_now_playing(char *title, char *artist, char *album) { + mutex_lock_recursive(s_music_ctx.mutex); + + if (title) { + strcpy(title, s_music_ctx.title); + } + if (artist) { + strcpy(artist, s_music_ctx.artist); + } + if (album) { + strcpy(album, s_music_ctx.album); + } + + mutex_unlock_recursive(s_music_ctx.mutex); +} + +bool music_get_player_name(char *player_name_out) { + mutex_lock_recursive(s_music_ctx.mutex); + + const bool has_player_name = (s_music_ctx.player_name[0] != 0); + + if (player_name_out) { + strcpy(player_name_out, s_music_ctx.player_name); + } + + mutex_unlock_recursive(s_music_ctx.mutex); + + return has_player_name; +} + +bool music_has_now_playing(void) { + bool has_now_playing = false; + mutex_lock_recursive(s_music_ctx.mutex); + if (s_music_ctx.title[0] != 0 || + s_music_ctx.artist[0] != 0) { + has_now_playing = true; + } + mutex_unlock_recursive(s_music_ctx.mutex); + return has_now_playing; +} + +uint32_t music_get_ms_since_pos_last_updated(void) { + mutex_lock_recursive(s_music_ctx.mutex); + const RtcTicks time_elapsed_ticks = rtc_get_ticks() - s_music_ctx.track_pos_updated_at; + const uint32_t time_elapsed_ms = ticks_to_milliseconds(time_elapsed_ticks); + mutex_unlock_recursive(s_music_ctx.mutex); + return time_elapsed_ms; +} + +void music_get_pos(uint32_t *track_pos_ms, uint32_t *track_length_ms) { + mutex_lock_recursive(s_music_ctx.mutex); + + const int32_t time_elapsed_ms = music_get_ms_since_pos_last_updated(); + const int32_t track_time_elapsed = + (time_elapsed_ms * s_music_ctx.playback_rate_percent) / MUSIC_NORMAL_PLAYBACK_RATE_PERCENT; + const int32_t pos_ms = s_music_ctx.track_pos_ms + track_time_elapsed; + const int32_t length_ms = s_music_ctx.track_length_ms; + + *track_pos_ms = CLIP(pos_ms, 0, length_ms); + *track_length_ms = length_ms; + + mutex_unlock_recursive(s_music_ctx.mutex); +} + +int32_t music_get_playback_rate_percent(void) { + mutex_lock_recursive(s_music_ctx.mutex); + int32_t playback_rate_percent = s_music_ctx.playback_rate_percent; + mutex_unlock_recursive(s_music_ctx.mutex); + return playback_rate_percent; +} + +uint8_t music_get_volume_percent(void) { + mutex_lock_recursive(s_music_ctx.mutex); + int32_t player_volume_percent = s_music_ctx.player_volume_percent; + mutex_unlock_recursive(s_music_ctx.mutex); + return player_volume_percent; +} + +static void prv_put_state_changed_event(MusicPlayState playback_state) { + PebbleEvent event = { + .type = PEBBLE_MEDIA_EVENT, + .media = { + .type = PebbleMediaEventTypePlaybackStateChanged, + .playback_state = playback_state, + }, + }; + event_put(&event); +} + +void music_update_player_playback_state(const MusicPlayerStateUpdate *state) { + mutex_lock_recursive(s_music_ctx.mutex); + s_music_ctx.playback_state = state->playback_state; + s_music_ctx.playback_rate_percent = state->playback_rate_percent; + s_music_ctx.track_pos_ms = state->elapsed_time_ms; + s_music_ctx.track_pos_updated_at = rtc_get_ticks(); + mutex_unlock_recursive(s_music_ctx.mutex); + + prv_put_state_changed_event(state->playback_state); + prv_put_pos_changed_event(); +} + +void music_update_player_volume_percent(uint8_t volume_percent) { + mutex_lock_recursive(s_music_ctx.mutex); + s_music_ctx.player_volume_percent = volume_percent; + mutex_unlock_recursive(s_music_ctx.mutex); + + PebbleEvent event = { + .type = PEBBLE_MEDIA_EVENT, + .media = { + .type = PebbleMediaEventTypeVolumeChanged, + .volume_percent = volume_percent, + }, + }; + event_put(&event); +} + +MusicPlayState music_get_playback_state(void) { + if (!music_is_playback_state_reporting_supported()) { + return MusicPlayStateUnknown; + } + MusicPlayState result; + mutex_lock_recursive(s_music_ctx.mutex); + result = s_music_ctx.playback_state; + mutex_unlock_recursive(s_music_ctx.mutex); + return result; +} + +static void * prv_implementation_function_for_offset(off_t offset) { + typedef void (*FuncPtr)(void); + FuncPtr func_ptr = NULL; + mutex_lock_recursive(s_music_ctx.mutex); + if (s_music_ctx.implementation) { + func_ptr = *(FuncPtr *) (((const uint8_t *)s_music_ctx.implementation) + offset); + } + mutex_unlock_recursive(s_music_ctx.mutex); + return func_ptr; +} + +void music_command_send(MusicCommand command) { + const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), command_send); + void (*command_send)(MusicCommand) = prv_implementation_function_for_offset(o); + if (command_send) { + command_send(command); + } +} + +void music_request_reduced_latency(bool reduced_latency) { + const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), request_reduced_latency); + void (*request_reduced_latency)(bool) = prv_implementation_function_for_offset(o); + if (request_reduced_latency) { + request_reduced_latency(reduced_latency); + } +} + +void music_request_low_latency_for_period(uint32_t period_ms) { + const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), request_low_latency_for_period); + void (*request_low_latency_for_period)(uint32_t) = prv_implementation_function_for_offset(o); + if (request_low_latency_for_period) { + request_low_latency_for_period(period_ms); + } +} + +bool music_is_command_supported(MusicCommand command) { + const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), + is_command_supported); + bool (*func_ptr)(MusicCommand) = prv_implementation_function_for_offset(o); + if (!func_ptr) { + return false; + } + return func_ptr(command); +} + +static bool prv_call_implementation_bool_return_void_args(off_t offset) { + bool (*func_ptr)(void) = prv_implementation_function_for_offset(offset); + if (!func_ptr) { + return false; // defaults to false when there is no "connected" implementation + } + return func_ptr(); +} + +bool music_needs_user_to_start_playback_on_phone(void) { + const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), + needs_user_to_start_playback_on_phone); + return prv_call_implementation_bool_return_void_args(o); +} + +static bool prv_is_capability_supported(MusicServerCapability capability) { + const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), get_capability_bitset); + MusicServerCapability (*func_ptr)(void) = prv_implementation_function_for_offset(o); + if (!func_ptr) { + return false; + } + return (func_ptr() & capability); +} + +bool music_is_playback_state_reporting_supported(void) { + return prv_is_capability_supported(MusicServerCapabilityPlaybackStateReporting); +} + +bool music_is_progress_reporting_supported(void) { + // Check capability and that track length is greater than 0 + uint32_t track_length_ms; + mutex_lock_recursive(s_music_ctx.mutex); + track_length_ms = s_music_ctx.track_length_ms; + mutex_unlock_recursive(s_music_ctx.mutex); + return (prv_is_capability_supported(MusicServerCapabilityProgressReporting) && track_length_ms); +} + +bool music_is_volume_reporting_supported(void) { + return prv_is_capability_supported(MusicServerCapabilityVolumeReporting); +} + +void command_print_now_playing(void) { + char title[MUSIC_BUFFER_LENGTH]; + char artist[MUSIC_BUFFER_LENGTH]; + char album[MUSIC_BUFFER_LENGTH]; + + music_get_now_playing(title, artist, album); + + char buffer[128]; + dbgserial_putstr_fmt(buffer, 128, "title=%s; artist=%s; album=%s", title, artist, album); +} + diff --git a/src/fw/services/music/wscript_build b/src/fw/services/music/wscript_build new file mode 100644 index 0000000000..766df8d99c --- /dev/null +++ b/src/fw/services/music/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c', 'endpoint.c'], + target='services_music', +) +bld.env.SERVICES.append('services_music') diff --git a/src/fw/services/new_timer/Kconfig b/src/fw/services/new_timer/Kconfig new file mode 100644 index 0000000000..c04f6c1f95 --- /dev/null +++ b/src/fw/services/new_timer/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_NEW_TIMER + bool "New timer" + help + Legacy "new" timer service. + +if SERVICE_NEW_TIMER + +module = SERVICE_NEW_TIMER +module-str = New timer +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/common/new_timer/new_timer.c b/src/fw/services/new_timer/new_timer.c similarity index 88% rename from src/fw/services/common/new_timer/new_timer.c rename to src/fw/services/new_timer/new_timer.c index 40b8aee903..ab56ffa906 100644 --- a/src/fw/services/common/new_timer/new_timer.c +++ b/src/fw/services/new_timer/new_timer.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "new_timer.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/new_timer/new_timer.h" #include "kernel/pbl_malloc.h" #include "kernel/task_timer_manager.h" @@ -31,6 +18,8 @@ #include "semphr.h" #include "task.h" +PBL_LOG_MODULE_DEFINE(service_new_timer, CONFIG_SERVICE_NEW_TIMER_LOG_LEVEL); + typedef struct { NewTimerWorkCallback cb; @@ -123,7 +112,7 @@ void* new_timer_debug_get_current_callback(void) { //========================================================================================= // Initialize the timer service void new_timer_service_init(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "NT: Initializing"); + PBL_LOG_DBG("NT: Initializing"); vSemaphoreCreateBinary(s_wake_srv_loop); diff --git a/src/fw/services/new_timer/wscript_build b/src/fw/services/new_timer/wscript_build new file mode 100644 index 0000000000..c95a7faf8a --- /dev/null +++ b/src/fw/services/new_timer/wscript_build @@ -0,0 +1,14 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'new_timer.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_new_timer', +) + +bld.env.SERVICES.append('services_new_timer') diff --git a/src/fw/services/normal/accessory/accessory_manager.c b/src/fw/services/normal/accessory/accessory_manager.c deleted file mode 100644 index 0a90f16136..0000000000 --- a/src/fw/services/normal/accessory/accessory_manager.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/accessory.h" -#include "services/normal/accessory/accessory_manager.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_connection.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "system/logging.h" -#include "os/mutex.h" - -static AccessoryInputState s_input_state = AccessoryInputStateIdle; -static PebbleMutex *s_state_mutex; - -void accessory_manager_init(void) { - s_state_mutex = mutex_create(); - - // initialize consumers of the accessory port - smartstrap_attribute_init(); - smartstrap_comms_init(); - smartstrap_state_init(); - smartstrap_connection_init(); - smartstrap_profiles_init(); -} - -bool accessory_manager_handle_character_from_isr(char c) { - // NOTE: THIS IS RUN WITHIN AN ISR - switch (s_input_state) { - case AccessoryInputStateSmartstrap: - return smartstrap_handle_data_from_isr((uint8_t)c); - case AccessoryInputStateIdle: - case AccessoryInputStateMic: - // fallthrough - default: - break; - } - return false; -} - -bool accessory_manager_handle_break_from_isr(void) { - // NOTE: THIS IS RUN WITHIN AN ISR - switch (s_input_state) { - case AccessoryInputStateSmartstrap: - return smartstrap_handle_break_from_isr(); - case AccessoryInputStateIdle: - case AccessoryInputStateMic: - // fallthrough - default: - break; - } - return false; -} - -// The accessory state is used to differentiate between different consumers of the accessory port. -// Before a consumer uses the accessory port, it must set its state and return the state to idle -// once it has finished. No other consumer will be permitted to use the accessory port until the -// state is returned to idle. -bool accessory_manager_set_state(AccessoryInputState state) { - mutex_lock(s_state_mutex); - - // Setting the state is only allowed if we are currently in the Idle state or we are - // moving to the Idle state. - if (state != AccessoryInputStateIdle && s_input_state != AccessoryInputStateIdle) { - // the state is already set by somebody else - mutex_unlock(s_state_mutex); - return false; - } - - accessory_use_dma(false); - s_input_state = state; - switch (s_input_state) { - case AccessoryInputStateIdle: - // restore accessory to default state - accessory_enable_input(); - accessory_set_baudrate(AccessoryBaud115200); - accessory_set_power(false); - break; - case AccessoryInputStateSmartstrap: - case AccessoryInputStateMic: - // fallthrough - default: - break; - } - - mutex_unlock(s_state_mutex); - PBL_LOG(LOG_LEVEL_DEBUG, "Setting accessory state to %u", state); - return true; -} diff --git a/src/fw/services/normal/accessory/accessory_manager.h b/src/fw/services/normal/accessory/accessory_manager.h deleted file mode 100644 index 40f2829f43..0000000000 --- a/src/fw/services/normal/accessory/accessory_manager.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef enum { - AccessoryInputStateIdle, - AccessoryInputStateMic, - AccessoryInputStateSmartstrap -} AccessoryInputState; - -void accessory_manager_init(void); -bool accessory_manager_set_state(AccessoryInputState state); diff --git a/src/fw/services/normal/accessory/smartstrap_attribute.c b/src/fw/services/normal/accessory/smartstrap_attribute.c deleted file mode 100644 index de9d29d1db..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_attribute.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/applib_malloc.auto.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "process_management/process_manager.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_connection.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "os/mutex.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/list.h" -#include "util/mbuf.h" - -//! Currently, we only support attributes being created by the App task -#define CONSUMER_TASK PebbleTask_App -//! Gets the next attribute in the list -#define NEXT_ATTR(attr) ((SmartstrapAttributeInternal *)list_get_next(&attr->list_node)) -//! This macro allows easy iteration through attributes in the s_attr_head list which don't have the -//! 'deferred_delete' field set. Example usage: -//! SmartstrapAttributeInternal *attr; -//! FOREACH_VALID_ATTR(attr) { -//! // 'attr' will be the current item in the list -//! } -#define FOREACH_VALID_ATTR(item) \ - for (item = s_attr_head; item; item = NEXT_ATTR(item)) \ - if (!item->deferred_delete) - -// This file relies on the ServiceId/AttributeId being uint16_t as the protocol defines it as such. -_Static_assert(sizeof(SmartstrapServiceId) == sizeof(uint16_t), - "SmartstrapServiceId MUST be two bytes in length!"); -_Static_assert(sizeof(SmartstrapAttributeId) == sizeof(uint16_t), - "SmartstrapAttributeId MUST be two bytes in length!"); - -typedef struct { - ListNode list_node; - //! The ServiceId for this attribute - uint16_t service_id; - //! The AttributeId for this attribute - uint16_t attribute_id; - //! MBuf used for sending / receiving data for this attribute - MBuf mbuf; - //! The number of bytes to write from the buffer - uint32_t write_length; - //! The type of request which is currently pending - SmartstrapRequestType request_type:8; - //! The current state of this attribute - SmartstrapAttributeState state:8; - //! The timeout to use for the next request - uint16_t timeout_ms; - //! Whether or not writes are being blocked - bool write_blocked; - //! Whether or not this attribute has a deferred delete pending - bool deferred_delete; -} SmartstrapAttributeInternal; - -typedef struct { - uint16_t service_id; - uint16_t attribute_id; -} AttributeFilterContext; - -typedef enum { - AttributeTransactionRead, - AttributeTransactionBeginWrite, - AttributeTransactionEndWrite -} AttributeTransaction; - -static SmartstrapAttributeInternal *s_attr_head; -static bool s_deferred_delete_queued; -static PebbleMutex *s_attr_list_lock; - - -// Init -//////////////////////////////////////////////////////////////////////////////// - -void smartstrap_attribute_init(void) { - s_attr_list_lock = mutex_create(); -} - - -// Attribute state functions -//////////////////////////////////////////////////////////////////////////////// - -static bool prv_is_valid_fsm_transition(SmartstrapAttributeInternal *attr, - SmartstrapAttributeState new_state) { - SmartstrapAttributeState current_state = attr->state; - if ((current_state == SmartstrapAttributeStateIdle) && - (new_state == SmartstrapAttributeStateRequestPending)) { - return pebble_task_get_current() == CONSUMER_TASK; - } else if ((current_state == SmartstrapAttributeStateIdle) && - (new_state == SmartstrapAttributeStateWritePending)) { - return pebble_task_get_current() == CONSUMER_TASK; - } else if ((current_state == SmartstrapAttributeStateWritePending) && - (new_state == SmartstrapAttributeStateRequestPending)) { - return pebble_task_get_current() == CONSUMER_TASK; - } else if ((current_state == SmartstrapAttributeStateWritePending) && - (new_state == SmartstrapAttributeStateIdle)) { - return pebble_task_get_current() == CONSUMER_TASK; - } else if ((current_state == SmartstrapAttributeStateRequestPending) && - (new_state == SmartstrapAttributeStateIdle)) { - return pebble_task_get_current() == PebbleTask_KernelBackground; - } else if ((current_state == SmartstrapAttributeStateRequestPending) && - (new_state == SmartstrapAttributeStateRequestInProgress)) { - return pebble_task_get_current() == PebbleTask_KernelBackground; - } else if ((current_state == SmartstrapAttributeStateRequestInProgress) && - (new_state == SmartstrapAttributeStateIdle)) { - return pebble_task_get_current() == PebbleTask_KernelBackground; - } else { - return false; - } -} - -static void prv_set_attribute_state(SmartstrapAttributeInternal *attr, - SmartstrapAttributeState new_state) { - PBL_ASSERTN(prv_is_valid_fsm_transition(attr, new_state)); - attr->state = new_state; -} - -//! Note; This should only be called from the consumer task -static bool prv_start_transaction(SmartstrapAttributeInternal *attr, AttributeTransaction txn) { - PBL_ASSERT_TASK(CONSUMER_TASK); - if ((txn == AttributeTransactionRead) && (attr->state == SmartstrapAttributeStateIdle)) { - prv_set_attribute_state(attr, SmartstrapAttributeStateRequestPending); - return true; - } else if ((txn == AttributeTransactionBeginWrite) && - (attr->state == SmartstrapAttributeStateIdle)) { - prv_set_attribute_state(attr, SmartstrapAttributeStateWritePending); - return true; - } else if ((txn == AttributeTransactionEndWrite) && - (attr->state == SmartstrapAttributeStateWritePending)) { - prv_set_attribute_state(attr, SmartstrapAttributeStateRequestPending); - return true; - } - return false; -} - -//! Note; This should only be called from the consumer task -static void prv_cancel_transaction(SmartstrapAttributeInternal *attr) { - PBL_ASSERT_TASK(CONSUMER_TASK); - if (attr->state == SmartstrapAttributeStateWritePending) { - prv_set_attribute_state(attr, SmartstrapAttributeStateIdle); - } -} - - -// List searching functions -//////////////////////////////////////////////////////////////////////////////// - -//! NOTE: the caller must hold s_attr_list_lock -static SmartstrapAttributeInternal *prv_find_by_ids(uint16_t service_id, uint16_t attribute_id) { - mutex_assert_held_by_curr_task(s_attr_list_lock, true); - SmartstrapAttributeInternal *attr; - FOREACH_VALID_ATTR(attr) { - if ((attr->service_id == service_id) && (attr->attribute_id == attribute_id)) { - return attr; - } - } - return NULL; -} - -//! NOTE: the caller must hold s_attr_list_lock -static SmartstrapAttributeInternal *prv_find_by_buffer(uint8_t *buffer) { - mutex_assert_held_by_curr_task(s_attr_list_lock, true); - SmartstrapAttributeInternal *attr; - FOREACH_VALID_ATTR(attr) { - if (mbuf_get_data(&attr->mbuf) == buffer) { - return attr; - } - } - return NULL; -} - -//! NOTE: the caller must hold s_attr_list_lock -static SmartstrapAttributeInternal *prv_find_by_state(SmartstrapAttributeState state) { - mutex_assert_held_by_curr_task(s_attr_list_lock, true); - SmartstrapAttributeInternal *attr; - FOREACH_VALID_ATTR(attr) { - if (attr->state == state) { - return attr; - } - } - return NULL; -} - - -// Attribute processing / request functions -// NOTE: These all run on KernelBG which moves attributes from the RequestPending to the Idle state -//////////////////////////////////////////////////////////////////////////////// - -bool smartstrap_attribute_send_pending(void) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - mutex_lock(s_attr_list_lock); - if (prv_find_by_state(SmartstrapAttributeStateRequestInProgress)) { - // we already have a request in progress - mutex_unlock(s_attr_list_lock); - return false; - } - - // get the next attribute which has a pending request - SmartstrapAttributeInternal *attr = prv_find_by_state(SmartstrapAttributeStateRequestPending); - mutex_unlock(s_attr_list_lock); - if (!attr) { - return false; - } - - // prepare the request - PBL_ASSERTN(!mbuf_get_next(&attr->mbuf)); - MBuf write_mbuf = MBUF_EMPTY; - mbuf_set_data(&write_mbuf, mbuf_get_data(&attr->mbuf), attr->write_length); - SmartstrapRequest request = (SmartstrapRequest) { - .service_id = attr->service_id, - .attribute_id = attr->attribute_id, - .write_mbuf = (attr->request_type == SmartstrapRequestTypeRead) ? NULL : &write_mbuf, - .read_mbuf = (attr->request_type == SmartstrapRequestTypeWrite) ? NULL : &attr->mbuf, - .timeout_ms = attr->timeout_ms - }; - - // send the request - SmartstrapResult result = smartstrap_profiles_handle_request(&request); - if (result == SmartstrapResultBusy) { - // there was another request in progress so we'll try again later - return false; - } else if ((result == SmartstrapResultOk) && - (smartstrap_fsm_state_get() != SmartstrapStateReadReady)) { - prv_set_attribute_state(attr, SmartstrapAttributeStateRequestInProgress); - if (attr->request_type == SmartstrapRequestTypeWrite) { - // This is a generic service write, which will be ACK'd by the smartstrap so we shouldn't - // send the event yet. - return true; - } - } else { - // Either the request was not written successfully, or we are not waiting for a response for it. - prv_set_attribute_state(attr, SmartstrapAttributeStateIdle); - } - - // send an event now that we've completed the write - PebbleEvent event = { - .type = PEBBLE_SMARTSTRAP_EVENT, - .smartstrap = { - .type = SmartstrapDataSentEvent, - .result = result, - .attribute = mbuf_get_data(&attr->mbuf) - }, - }; - process_manager_send_event_to_process(CONSUMER_TASK, &event); - return true; -} - -void smartstrap_attribute_send_event(SmartstrapEventType type, SmartstrapProfile profile, - SmartstrapResult result, uint16_t service_id, - uint16_t attribute_id, uint16_t read_length) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - PebbleEvent event = { - .type = PEBBLE_SMARTSTRAP_EVENT, - .smartstrap = { - .type = type, - .profile = profile, - .result = result, - .read_length = read_length, - }, - }; - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr = prv_find_by_ids(service_id, attribute_id); - mutex_unlock(s_attr_list_lock); - if (!attr) { - // this attribute has likely since been destroyed - return; - } - event.smartstrap.attribute = mbuf_get_data(&attr->mbuf); - if (type == SmartstrapDataReceivedEvent) { - PBL_ASSERTN(attr->state == SmartstrapAttributeStateRequestInProgress); - prv_set_attribute_state(attr, SmartstrapAttributeStateIdle); - if (attr->request_type == SmartstrapRequestTypeWrite) { - // the data we got was the ACK of the write, so change the event type and don't block writes - event.smartstrap.type = SmartstrapDataSentEvent; - } else { - // prevent writing to the attribute until the app handles the event, at which point applib - // code will call sys_smartstrap_attribute_event_processed() to clear this flag - attr->write_blocked = true; - } - process_manager_send_event_to_process(CONSUMER_TASK, &event); - } else if (type == SmartstrapNotifyEvent) { - process_manager_send_event_to_process(CONSUMER_TASK, &event); - } -} - -static void prv_do_deferred_delete_cb(void *context) { - s_deferred_delete_queued = false; - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr = s_attr_head; - while (attr) { - SmartstrapAttributeInternal *next = NEXT_ATTR(attr); - if (attr->deferred_delete) { - list_remove(&attr->list_node, (ListNode **)&s_attr_head, NULL); - kernel_free(attr); - } - attr = next; - } - mutex_unlock(s_attr_list_lock); -} - - -// Syscalls -// NOTE: These all run on the consumer task which moves attributes from Idle to RequestPending -//////////////////////////////////////////////////////////////////////////////// - -DEFINE_SYSCALL(bool, sys_smartstrap_attribute_register, uint16_t service_id, - uint16_t attribute_id, uint8_t *buffer, size_t buffer_length) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(buffer, buffer_length); - } - if (buffer_length > SMARTSTRAP_ATTRIBUTE_LENGTH_MAXIMUM) { - PBL_LOG(LOG_LEVEL_ERROR, "Attribute length of %"PRIu32" is too long", (uint32_t)buffer_length); - return false; - } - - mutex_lock(s_attr_list_lock); - const bool exists = (prv_find_by_ids(service_id, attribute_id) || prv_find_by_buffer(buffer)); - mutex_unlock(s_attr_list_lock); - if (exists) { - PBL_LOG(LOG_LEVEL_ERROR, "Attribute already exists (0x%x,0x%x)", service_id, attribute_id); - return false; - } - - SmartstrapAttributeInternal *new_attr = kernel_zalloc(sizeof(SmartstrapAttributeInternal)); - if (!new_attr) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to allocate attribute"); - return false; - } - - *new_attr = (SmartstrapAttributeInternal) { - .service_id = service_id, - .attribute_id = attribute_id, - .mbuf = MBUF_EMPTY - }; - list_init(&new_attr->list_node); - mbuf_set_data(&new_attr->mbuf, buffer, buffer_length); - - // add the node to our list - mutex_lock(s_attr_list_lock); - s_attr_head = (SmartstrapAttributeInternal *)list_prepend(&s_attr_head->list_node, - &new_attr->list_node); - mutex_unlock(s_attr_list_lock); - return true; -} - -//! NOTE: the caller must hold s_attr_list_lock -static void prv_queue_deferred_delete(SmartstrapAttributeInternal *attr, bool do_free) { - mutex_assert_held_by_curr_task(s_attr_list_lock, true); - - if (attr->state != SmartstrapAttributeStateRequestInProgress) { - // stop the in-progress request - smartstrap_cancel_send(); - } - - void *buffer = mbuf_get_data(&attr->mbuf); - // clear out the mbuf just in-case - attr->mbuf = MBUF_EMPTY; - if (do_free) { - applib_free(buffer); - } - attr->deferred_delete = true; - - // queue the deferred delete callback on KernelBG - if (!s_deferred_delete_queued) { - system_task_add_callback(prv_do_deferred_delete_cb, NULL); - s_deferred_delete_queued = true; - } -} - -DEFINE_SYSCALL(void, sys_smartstrap_attribute_unregister, SmartstrapAttribute *app_attr) { - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr = prv_find_by_buffer((uint8_t *)app_attr); - if (!attr) { - mutex_unlock(s_attr_list_lock); - return; - } - prv_queue_deferred_delete(attr, true /* do_free */); - mutex_unlock(s_attr_list_lock); -} - -void smartstrap_attribute_unregister_all(void) { - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr; - FOREACH_VALID_ATTR(attr) { - // At this point, the app is closing so there's no point in freeing the buffers, and doing so - // will crash the watch if the app had crashed (and the heap has already been cleaned up). - prv_queue_deferred_delete(attr, false /* !do_free */); - } - mutex_unlock(s_attr_list_lock); -} - -DEFINE_SYSCALL(void, sys_smartstrap_attribute_get_info, SmartstrapAttribute *app_attr, - uint16_t *service_id, uint16_t *attribute_id, size_t *length) { - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr = prv_find_by_buffer((uint8_t *)app_attr); - mutex_unlock(s_attr_list_lock); - if (!attr) { - return; - } - if (PRIVILEGE_WAS_ELEVATED) { - if (service_id) { - syscall_assert_userspace_buffer(service_id, sizeof(uint16_t)); - } - if (attribute_id) { - syscall_assert_userspace_buffer(attribute_id, sizeof(uint16_t)); - } - if (length) { - syscall_assert_userspace_buffer(length, sizeof(size_t)); - } - } - if (service_id) { - *service_id = attr->service_id; - } - if (attribute_id) { - *attribute_id = attr->attribute_id; - } - if (length) { - *length = mbuf_get_length(&attr->mbuf); - } -} - -DEFINE_SYSCALL(SmartstrapResult, sys_smartstrap_attribute_do_request, SmartstrapAttribute *app_attr, - SmartstrapRequestType type, uint16_t timeout_ms, uint32_t write_length) { - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr = prv_find_by_buffer((uint8_t *)app_attr); - mutex_unlock(s_attr_list_lock); - if (!attr) { - return SmartstrapResultInvalidArgs; - } else if (!sys_smartstrap_is_service_connected(attr->service_id)) { - // go back to idle if we had begun a write - prv_cancel_transaction(attr); - return SmartstrapResultServiceUnavailable; - } - - if (type == SmartstrapRequestTypeBeginWrite) { - if (attr->write_blocked || !prv_start_transaction(attr, AttributeTransactionBeginWrite)) { - return SmartstrapResultBusy; - } - // clear the write buffer - memset(mbuf_get_data(&attr->mbuf), 0, mbuf_get_length(&attr->mbuf)); - return SmartstrapResultOk; - } else if (type == SmartstrapRequestTypeRead) { - // handle read request - if (!prv_start_transaction(attr, AttributeTransactionRead)) { - return SmartstrapResultBusy; - } - } else { - // handle write request - if (!write_length || (write_length > mbuf_get_length(&attr->mbuf))) { - prv_cancel_transaction(attr); - return SmartstrapResultInvalidArgs; - } else if (!prv_start_transaction(attr, AttributeTransactionEndWrite)) { - // they didn't call smartstrap_begin_write first - return SmartstrapResultInvalidArgs; - } - } - - attr->write_length = write_length; - attr->request_type = type; - attr->timeout_ms = timeout_ms; - smartstrap_connection_kick_monitor(); - return SmartstrapResultOk; -} - -DEFINE_SYSCALL(void, sys_smartstrap_attribute_event_processed, SmartstrapAttribute *app_attr) { - mutex_lock(s_attr_list_lock); - SmartstrapAttributeInternal *attr = prv_find_by_buffer((uint8_t *)app_attr); - mutex_unlock(s_attr_list_lock); - if (!attr) { - // the app might have destroyed the attribute - return; - } - // clear the write_blocked flag after the event has been processed for an attribute - attr->write_blocked = false; -} diff --git a/src/fw/services/normal/accessory/smartstrap_attribute.h b/src/fw/services/normal/accessory/smartstrap_attribute.h deleted file mode 100644 index 4167fa1c09..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_attribute.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/app_smartstrap.h" -#include "kernel/events.h" -#include "services/normal/accessory/smartstrap_profiles.h" - -#include -#include - -/* - * This module creates kernel-space structs to represent SmartstrapAttributes for the app. Because - * it is dealing with app buffers, the kernel-space structs are kept within smartstrap_attribute.c - * and they are referenced via the APIs using the user-space SmartstrapAttribute pointer. This - * SmartstrapAttribute pointer is actually just the user-space buffer pointer, but this fact is - * hidden from apps (and not particularly useful for them to know). - */ - -/* - * FSM state transitions: - * +-------------------+-------------------+----------+---------------------------+ - * | From State | To State | Task | Event | - * +-------------------+-------------------+----------+---------------------------+ - * | Idle | RequestPending | App | *_read() | - * | Idle | WritePending | App | *_begin_write() | - * | WritePending | RequestPending | App | *_end_write() | - * | WritePending | Idle | App | *_end_write() failed | - * | RequestPending | Idle | KernelBG | Failed to send request | - * | RequestPending | RequestInProgress | KernelBG | Request sent successfully | - * | RequestInProgress | Idle | KernelBG | Got response to request | - * +-------------------+-------------------+----------+---------------------------+ - * Notes: - * - Only the App task can move out of the Idle and WritePending states - * - Only KernelBG can move out of the RequestPending and RequestInProgress state - * - The lower-level smartstrap sending APIs are called only from KernelBG so we don't block the App - */ -typedef enum { - SmartstrapAttributeStateIdle = 0, - SmartstrapAttributeStateWritePending, - SmartstrapAttributeStateRequestPending, - SmartstrapAttributeStateRequestInProgress, - NumSmartstrapAttributeStates -} SmartstrapAttributeState; - -typedef enum { - SmartstrapRequestTypeRead, - SmartstrapRequestTypeBeginWrite, - SmartstrapRequestTypeWrite, - SmartstrapRequestTypeWriteRead -} SmartstrapRequestType; - - -//! Initializes the smartstrap attribute code -void smartstrap_attribute_init(void); - -//! Sends the next pending attribute request -bool smartstrap_attribute_send_pending(void); - -//! Called by one of the profiles to send an event for an attribute. -void smartstrap_attribute_send_event(SmartstrapEventType type, SmartstrapProfile profile, - SmartstrapResult result, uint16_t service_id, - uint16_t attribute_id, uint16_t read_length); - -//! Unregisters all attributes which the app has registered -void smartstrap_attribute_unregister_all(void); - -// syscalls - -//! Registers a new attribute by creating a kernel-space struct to represent it -bool sys_smartstrap_attribute_register(uint16_t service_id, uint16_t attribute_id, uint8_t *buffer, - size_t buffer_length); - -//! Unregisters an attribute -void sys_smartstrap_attribute_unregister(SmartstrapAttribute *app_attr); - -//! Gets information on the specified attribute which has previously been created -void sys_smartstrap_attribute_get_info(SmartstrapAttribute *app_attr, uint16_t *service_id, - uint16_t *attribute_id, size_t *length); - -//! Queues up a request for the specified attribute -SmartstrapResult sys_smartstrap_attribute_do_request(SmartstrapAttribute *app_attr, - SmartstrapRequestType type, - uint16_t timeout_ms, uint32_t write_length); - -//! Called by app_smartstrap.c after the app's event callback is called for an attribute -void sys_smartstrap_attribute_event_processed(SmartstrapAttribute *app_attr); diff --git a/src/fw/services/normal/accessory/smartstrap_comms.c b/src/fw/services/normal/accessory/smartstrap_comms.c deleted file mode 100644 index e718a1dec8..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_comms.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app_smartstrap.h" -#include "drivers/accessory.h" -#include "kernel/events.h" -#include "kernel/pebble_tasks.h" -#include "process_management/worker_manager.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/accessory/accessory_manager.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_connection.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "os/mutex.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/crc8.h" -#include "util/hdlc.h" -#include "util/math.h" -#include "util/mbuf_iterator.h" - -//! The timeout for receiving the context frame after the break characters in ms -static const uint32_t NOTIFY_TIMEOUT = 100; -static const uint16_t SMARTSTRAP_MAX_TIMEOUT = 1000; - -//! The header contains the version (1 byte), flags (4 bytes), and profile (2 bytes) fields -//! The footer contains the checksum (1 byte) field -#define FRAME_FOOTER_LENGTH 1 -#define FRAME_MIN_LENGTH (sizeof(FrameHeader) + FRAME_FOOTER_LENGTH) - -typedef struct PACKED { - uint8_t version; - union { - struct PACKED { - uint8_t is_read:1; - uint8_t is_master:1; - uint8_t is_notify:1; - uint32_t reserved:29; - }; - uint32_t raw; - } flags; - uint16_t profile; -} FrameHeader; - -typedef struct { - //! HDLC context - HdlcStreamingContext hdlc_ctx; - //! The total number of bytes we've read for this frame - uint32_t length; - //! A temporary buffer for storing the footer (checksum byte) - uint8_t footer_byte; - //! The checksum byte (comes after the payload in the frame) - uint8_t checksum; - //! Flag which is set if we find the frame is invalid - bool should_drop; -} ReadInfo; - -typedef struct { - //! The profile used for the request - SmartstrapProfile profile; - //! The MBufIterator to read data into - MBufIterator mbuf_iter; -} ReadConsumer; - -typedef union { - struct PACKED { - bool success; - bool is_notify; - }; - void *context_ptr; -} ReadCompleteContext; -_Static_assert(sizeof(ReadCompleteContext) == sizeof(void *), "ReadCompleteContext too big"); - -typedef struct { - MBufIterator mbuf_iter; - bool is_read; - bool sent_escape; - uint8_t escaped_byte; -} SendInfo; - -//! Info on the current frame being read -static ReadInfo s_read_info; -//! The consumer of the next frame which is read -static ReadConsumer s_read_consumer; -//! MBuf for storing the header when receiving -static MBuf s_header_mbuf; -static uint8_t s_header_data[sizeof(FrameHeader)]; -//! Info on the current frame being sent -static SendInfo s_send_info; - -//! Timer used to enforce read timeouts -static TimerID s_read_timer = TIMER_INVALID_ID; - - -// Init -//////////////////////////////////////////////////////////////////////////////// - -void smartstrap_comms_init(void) { - s_read_timer = new_timer_create(); - s_header_mbuf = MBUF_EMPTY; - mbuf_set_data(&s_header_mbuf, s_header_data, sizeof(s_header_data)); -} - - -// Helper functions for static variables -//////////////////////////////////////////////////////////////////////////////// - -static void prv_reset_read_info(void) { - s_read_info = (ReadInfo) { }; - hdlc_streaming_decode_reset(&s_read_info.hdlc_ctx); -} - -static void prv_reset_read_consumer(void) { - s_read_consumer = (ReadConsumer) { 0 }; - mbuf_iterator_init(&s_read_consumer.mbuf_iter, NULL); -} - -void smartstrap_comms_set_enabled(bool enabled) { - if (enabled) { - prv_reset_read_info(); - prv_reset_read_consumer(); - } else { - new_timer_stop(s_read_timer); - } -} - -// Receive functions -//////////////////////////////////////////////////////////////////////////////// - -static void prv_read_complete_system_task_cb(void *context_ptr) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - ReadCompleteContext context = { .context_ptr = context_ptr }; - - smartstrap_state_lock(); - if (smartstrap_fsm_state_get() != SmartstrapStateReadComplete) { - // We could not be in a ReadComplete state if we got disconnected or if we got a complete frame - // while the timeout was scheduled. - mbuf_clear_next(&s_header_mbuf); - smartstrap_state_unlock(); - return; - } - // All other tasks and ISRs will be blocked while we are in the ReadComplete state and while we - // hold the state lock, so we're free to access / modify static variables until we transition - // the state back to ReadReady. - - SmartstrapProfile read_profile = s_read_consumer.profile; - uint32_t read_length = 0; - if (context.success) { - if (context.is_notify) { - // get the profile from the frame - FrameHeader *header = mbuf_get_data(&s_header_mbuf); - read_profile = header->profile; - } - PBL_ASSERTN(s_read_info.length >= FRAME_MIN_LENGTH); - read_length = s_read_info.length - FRAME_MIN_LENGTH; - // don't care if the timeout is alreay queued as the FSM state will make it a noop - new_timer_stop(s_read_timer); - } - - accessory_use_dma(false); - mbuf_clear_next(&s_header_mbuf); - prv_reset_read_info(); - prv_reset_read_consumer(); - smartstrap_fsm_state_set(SmartstrapStateReadReady); - smartstrap_state_unlock(); - - if (context.is_notify) { - smartstrap_profiles_handle_notification(context.success, read_profile); - } else { - smartstrap_profiles_handle_read(context.success, read_profile, read_length); - } -} - -static void prv_read_timeout(void *context) { - if (smartstrap_fsm_state_test_and_set(SmartstrapStateReadInProgress, - SmartstrapStateReadComplete)) { - // we need to handle the timeout from KernelBG - ReadCompleteContext context = { - .success = false, - .is_notify = false - }; - system_task_add_callback(prv_read_complete_system_task_cb, context.context_ptr); - } -} - -static void prv_store_byte(const uint8_t data) { - // NOTE: THIS IS RUN WITHIN AN ISR - // The checksum byte is the last byte in the frame. This byte could be the last byte we receive - // (making it the checksum byte), so we always keep a 1 byte temporary buffer before storing the - // byte in the MBuf. This avoids us potentially overrunning a conservatively sized payload buffer; - if (s_read_info.length > 0) { - // copy the previous byte from the footer_byte field into the payload - if (!mbuf_iterator_write_byte(&s_read_consumer.mbuf_iter, - s_read_info.footer_byte)) { - // no room left to store this byte - s_read_info.should_drop = true; - } - } - // Store this byte in the footer_byte. Note that we will still calculate the checksum on this byte - // and verify that the checksum is 0 at the end, so if this byte is the actual footer byte (aka. - // the checksum), we will still include it in the checksum. - s_read_info.footer_byte = data; - - // increment the length and run the CRC calculation - s_read_info.length++; - crc8_calculate_bytes_streaming((uint8_t *)&data, sizeof(data), (uint8_t *)&s_read_info.checksum, - false /* !big_endian */); -} - -static void prv_handle_complete_frame(bool *should_context_switch) { - FrameHeader *header = mbuf_get_data(&s_header_mbuf); - bool is_notify = header->flags.is_notify; - if ((is_notify && (smartstrap_fsm_state_get() != SmartstrapStateNotifyInProgress)) || - (!is_notify && (s_read_consumer.profile != header->profile))) { - // We weither got a notify frame in response to a normal read, or we got a response for a - // different frame than we requested. - s_read_info.should_drop = true; - } - if ((s_read_info.should_drop == false) && - (header->version > 0) && - (header->version <= SMARTSTRAP_PROTOCOL_VERSION) && - !header->flags.is_read && - !header->flags.is_master && - !header->flags.reserved && - (header->profile > SmartstrapProfileInvalid) && - (header->profile < NumSmartstrapProfiles) && - (s_read_info.length >= FRAME_MIN_LENGTH) && - !s_read_info.checksum) { - // If this is a notification, we shouldn't have a read consumer set. - PBL_ASSERTN(!is_notify || (s_read_consumer.profile == SmartstrapProfileInvalid)); - // this frame is valid - transition the FSM and queue up processing of it - smartstrap_fsm_state_set(SmartstrapStateReadComplete); - ReadCompleteContext context = { - .success = true, - .is_notify = is_notify - }; - system_task_add_callback_from_isr(prv_read_complete_system_task_cb, context.context_ptr, - should_context_switch); - } else { - // Reset our context so we can try again to receive a frame in case we do happen to get a valid - // one before the timeout occurs. - prv_reset_read_info(); - mbuf_iterator_init(&s_read_consumer.mbuf_iter, &s_header_mbuf); - } -} - -bool smartstrap_handle_data_from_isr(uint8_t data) { - // NOTE: THIS IS RUN WITHIN AN ISR - if ((smartstrap_fsm_state_get() != SmartstrapStateReadInProgress) && - (smartstrap_fsm_state_get() != SmartstrapStateNotifyInProgress)) { - return false; - } - - bool should_context_switch = false; - bool hdlc_err; - bool should_store; - bool is_complete = hdlc_streaming_decode(&s_read_info.hdlc_ctx, &data, &should_store, &hdlc_err); - if (hdlc_err) { - // the rest of the frame is invalid - s_read_info.should_drop = true; - } else if (is_complete) { - prv_handle_complete_frame(&should_context_switch); - } else if (should_store && !s_read_info.should_drop) { - prv_store_byte(data); - } - - return should_context_switch; -} - -void prv_notify_timeout(void *context) { - if (smartstrap_fsm_state_test_and_set(SmartstrapStateNotifyInProgress, - SmartstrapStateReadComplete)) { - // we need to handle the timeout from KernelBG - ReadCompleteContext context = { - .success = false, - .is_notify = true - }; - system_task_add_callback(prv_read_complete_system_task_cb, context.context_ptr); - } -} - -void prv_schedule_notify_timeout(void *context) { - // make sure there's still a notification pending - if (smartstrap_fsm_state_get() == SmartstrapStateNotifyInProgress) { - PBL_ASSERTN(new_timer_start(s_read_timer, NOTIFY_TIMEOUT, prv_notify_timeout, NULL, 0)); - } -} - -bool smartstrap_handle_break_from_isr(void) { - // NOTE: THIS IS RUN WITHIN AN ISR - bool should_context_switch = false; - // we should only accept notifications if we're in the ReadReady state - if (smartstrap_fsm_state_test_and_set(SmartstrapStateReadReady, - SmartstrapStateNotifyInProgress)) { - // prepare to read notification context - PBL_ASSERTN(!mbuf_get_next(&s_header_mbuf)); - mbuf_iterator_init(&s_read_consumer.mbuf_iter, &s_header_mbuf); - system_task_add_callback_from_isr(prv_schedule_notify_timeout, NULL, &should_context_switch); - } - return should_context_switch; -} - - -// Sending functions -//////////////////////////////////////////////////////////////////////////////// - -static bool prv_send_byte_and_check(uint8_t data) { - // NOTE: THIS IS RUN WITHIN AN ISR - accessory_send_byte(data); - const bool bus_contention = accessory_bus_contention_detected(); - if (bus_contention) { - PBL_LOG(LOG_LEVEL_DEBUG, "Bus contention was detected!"); - } - return !bus_contention; -} - -static bool prv_send_byte(uint8_t data) { - // NOTE: THIS IS RUN WITHIN AN ISR - if (hdlc_encode(&data)) { - PBL_ASSERTN(!s_send_info.sent_escape); - s_send_info.sent_escape = true; - s_send_info.escaped_byte = data; - data = HDLC_ESCAPE; - } - return prv_send_byte_and_check(data); -} - -static bool prv_send_stream_callback(void *context) { - // NOTE: THIS IS RUN WITHIN AN ISR - if (smartstrap_fsm_state_get() != SmartstrapStateReadDisabled) { - // we should no longer be sending - return false; - } - - // handle escaped bytes first - if (s_send_info.sent_escape) { - s_send_info.sent_escape = false; - return prv_send_byte_and_check(s_send_info.escaped_byte); - } - - // send the next byte - bool result = true; - MBufIterator *iter = &s_send_info.mbuf_iter; - MBuf *mbuf = mbuf_iterator_get_current_mbuf(iter); - uint8_t read_data; - PBL_ASSERTN(mbuf_iterator_read_byte(iter, &read_data)); - if (mbuf_is_flag_set(mbuf, MBUF_FLAG_IS_FRAMING)) { - result = prv_send_byte_and_check(read_data); - } else { - result = prv_send_byte(read_data); - } - - if (mbuf_iterator_is_finished(iter)) { - // we just sent the last byte - if (s_send_info.is_read) { - // We just successfully sent a read request, so should move to ReadInProgress to prepare to - // read the response. We do this here to ensure we don't miss any bytes of the response due to - // KernelBG not getting scheduled quickly enough. - PBL_ASSERTN(!mbuf_get_next(&s_header_mbuf)); - mbuf_append(&s_header_mbuf, context); - mbuf_iterator_init(&s_read_consumer.mbuf_iter, &s_header_mbuf); - smartstrap_fsm_state_set(SmartstrapStateReadInProgress); - } - result = false; - } - - if (!result) { - accessory_enable_input(); - } - - return result; -} - -SmartstrapResult smartstrap_send(SmartstrapProfile profile, MBuf *write_mbuf, MBuf *read_mbuf, - uint16_t timeout_ms) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - smartstrap_state_assert_locked_by_current_task(); - // we expect the arguments to be valid - const bool is_read = (read_mbuf != NULL); - PBL_ASSERTN((profile > SmartstrapProfileInvalid) && (profile < NumSmartstrapProfiles)); - PBL_ASSERTN(!is_read || (mbuf_get_chain_length(read_mbuf) > 0)); - PBL_ASSERTN((!write_mbuf && !read_mbuf) || (write_mbuf != read_mbuf)); - timeout_ms = MIN(timeout_ms, SMARTSTRAP_MAX_TIMEOUT); - - // transition the FSM state - if (!smartstrap_fsm_state_test_and_set(SmartstrapStateReadReady, SmartstrapStateReadDisabled)) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to change smartstrap FSM state (%d)", - smartstrap_fsm_state_get()); - return SmartstrapResultBusy; - } - - // We are now be in a state which allows us to freely modify static variables as we can be sure - // that no ISR or other tasks will be allowed to access or modify them while we are in this - // state. - accessory_disable_input(); - // NOTE: Accessory input will be re-enabled by the stream callback after we finish sending - prv_reset_read_info(); - prv_reset_read_consumer(); - s_send_info = (SendInfo) { .is_read = is_read }; - - if (is_read) { - // populate the read consumer info - s_read_consumer.profile = profile; - } - - // Go through and build the frame: Start_Flag | Header | Payload | Checksum | End_Flag - - // Start_Flag - uint8_t flag_data = HDLC_FLAG; - MBuf start_flag_mbuf = MBUF_EMPTY; - mbuf_set_data(&start_flag_mbuf, &flag_data, sizeof(flag_data)); - mbuf_set_flag(&start_flag_mbuf, MBUF_FLAG_IS_FRAMING, true); - - // Header - FrameHeader header = (FrameHeader) { - .version = SMARTSTRAP_PROTOCOL_VERSION, - .flags = { - .is_read = is_read, - .is_master = true, - }, - .profile = profile - }; - MBuf header_mbuf = MBUF_EMPTY; - mbuf_set_data(&header_mbuf, &header, sizeof(header)); - mbuf_append(&start_flag_mbuf, &header_mbuf); - - // Payload - mbuf_append(&start_flag_mbuf, write_mbuf); - - // Checksum - uint8_t checksum = 0; - for (MBuf *m = &header_mbuf; m; m = mbuf_get_next(m)) { - if (!mbuf_is_flag_set(m, MBUF_FLAG_IS_FRAMING)) { - crc8_calculate_bytes_streaming(m->data, m->length, &checksum, false /* !big_endian */); - } - } - MBuf footer_mbuf = MBUF_EMPTY; - mbuf_set_data(&footer_mbuf, &checksum, sizeof(checksum)); - mbuf_append(&start_flag_mbuf, &footer_mbuf); - - // End_Flag - MBuf end_flag_mbuf = MBUF_EMPTY; - mbuf_set_data(&end_flag_mbuf, &flag_data, sizeof(flag_data)); - mbuf_set_flag(&end_flag_mbuf, MBUF_FLAG_IS_FRAMING, true); - mbuf_append(&start_flag_mbuf, &end_flag_mbuf); - - // send off the frame - mbuf_iterator_init(&s_send_info.mbuf_iter, &start_flag_mbuf); - accessory_use_dma(true); - if (!accessory_send_stream(prv_send_stream_callback, (void *)read_mbuf)) { - accessory_enable_input(); - } - - if (is_read) { - // If we sent the request successfully, the send ISR will have transitioned us out of - // ReadDisabled. - const bool was_successful = (smartstrap_fsm_state_get() != SmartstrapStateReadDisabled); - if (was_successful) { - // start the timer for the read timeout - PBL_ASSERTN(new_timer_start(s_read_timer, timeout_ms, prv_read_timeout, NULL, 0)); - } else { - // clean up and return an error - accessory_use_dma(false); - prv_reset_read_consumer(); - smartstrap_fsm_state_set(SmartstrapStateReadReady); - return SmartstrapResultBusy; - } - } else { - accessory_use_dma(false); - smartstrap_fsm_state_set(SmartstrapStateReadReady); - if (!mbuf_iterator_is_finished(&s_send_info.mbuf_iter)) { - // The write was not successful, so return an error - return SmartstrapResultBusy; - } - } - - return SmartstrapResultOk; -} - -void smartstrap_cancel_send(void) { - // Enter a critical region to prevent anybody else changing the state. - portENTER_CRITICAL(); - SmartstrapState state = smartstrap_fsm_state_get(); - if ((state != SmartstrapStateReadDisabled) && (state != SmartstrapStateReadInProgress) && - (state != SmartstrapStateReadComplete)) { - // we aren't in a state where something is in progress, so there's nothing to do - portEXIT_CRITICAL(); - return; - } - smartstrap_fsm_state_reset(); - new_timer_stop(s_read_timer); - smartstrap_profiles_handle_read_aborted(s_read_consumer.profile); - prv_reset_read_info(); - prv_reset_read_consumer(); - mbuf_clear_next(&s_header_mbuf); - portEXIT_CRITICAL(); - PBL_LOG(LOG_LEVEL_WARNING, "Canceled an in-progress request. Was in state: %d", state); -} diff --git a/src/fw/services/normal/accessory/smartstrap_comms.h b/src/fw/services/normal/accessory/smartstrap_comms.h deleted file mode 100644 index b18d18f8dd..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_comms.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/app_smartstrap.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "util/mbuf.h" - -#include -#include - -#define SMARTSTRAP_PROTOCOL_VERSION 1 - -//! Initialize the smartstrap manager -void smartstrap_comms_init(void); - -//! Called by accessory_manager when we receive a byte of data from the accessory port -bool smartstrap_handle_data_from_isr(uint8_t c); - -//! Called by accessory_manager when we receive a break character -bool smartstrap_handle_break_from_isr(void); - -//! Sends a message over the accessory port using the smartstrap protocol. The message will be sent -//! synchronously and the response will be read asynchronously with an event being put on the -//! calling task's queue when the response is read or a timeout occurs. A response will only be -//! expected if read_data is non-NULL. -//! @note The calling task must be subscribed first (@see sys_smartstrap_subscribe) -//! @param[in] profile The profile of the frame -//! @param[in] write_data The data to be written to the smartstrap -//! @param[in] write_length The length of write_data -//! @param[in] read_data The buffer to store the response in (asynchronously) -//! @param[in] read_length The length of the read_data buffer -//! @param[in] timeout_ms A timeout will occur if the response is not received after this amount of -//! time -//! @return The result of the send -SmartstrapResult smartstrap_send(SmartstrapProfile profile, MBuf *write_mbuf, MBuf *read_mbuf, - uint16_t timeout_ms); - - -//! Enables or disables the smartstrap communications -void smartstrap_comms_set_enabled(bool enabled); - -//! Cancels any send (write or read) which is in progress -void smartstrap_cancel_send(void); diff --git a/src/fw/services/normal/accessory/smartstrap_connection.c b/src/fw/services/normal/accessory/smartstrap_connection.c deleted file mode 100644 index fb2c342f82..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_connection.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/accessory.h" -#include "services/common/new_timer/new_timer.h" -#include "services/normal/accessory/accessory_manager.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_connection.h" -#include "services/normal/accessory/smartstrap_link_control.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/math.h" - -#include - -//! How long to wait after failing to acquire the accessory in ms before trying again -static const uint32_t ACCESSORY_ACQUIRE_INTERVAL = 5000; -//! The backoff before trying to detect a smartstrap again in ms -static const uint32_t DETECTION_BACKOFF = 200; -//! The maximum interval between detection attempts in ms -static const uint32_t DETECTION_MAX_INTERVAL = 10000; -//! When we expect something will kick us, we'll use this value as a timeout just in-case. -static const uint32_t KICK_TIMEOUT_INTERVAL = 2000; -//! If we hit bus contention during sending, we should wait this number of milliseconds. -static const uint32_t BUS_CONTENTION_INTERVAL = 100; - -//! Subscriber information -static int s_subscriber_count; -//! Timer used for monitoring the connection and sending pending requests -static TimerID s_monitor_timer = TIMER_INVALID_ID; -//! The last time we got valid data from the smartstrap -static time_t s_last_data_time = 0; - - -void smartstrap_connection_init(void) { - s_monitor_timer = new_timer_create(); -} - -static bool prv_acquire_accessory(void) { - if (accessory_manager_set_state(AccessoryInputStateSmartstrap)) { - // enable the accessory port - accessory_set_baudrate(AccessoryBaud9600); - accessory_set_power(true); - smartstrap_comms_set_enabled(true); - return true; - } else { - PBL_LOG(LOG_LEVEL_ERROR, "The accessory is already in use"); - return false; - } -} - -static void prv_release_accessory(void) { - PBL_ASSERTN(!s_subscriber_count); - - smartstrap_fsm_state_set(SmartstrapStateUnsubscribed); - PBL_LOG(LOG_LEVEL_DEBUG, "Disconnecting from smartstrap"); - smartstrap_link_control_disconnect(); - smartstrap_comms_set_enabled(false); - new_timer_stop(s_monitor_timer); - // stop any in-progress write - accessory_send_stream_stop(); - // release the accessory port - PBL_ASSERTN(accessory_manager_set_state(AccessoryInputStateIdle)); -} - -static void prv_monitor_timer_cb(void *context); - -static void prv_monitor_system_task_cb(void *context) { - static uint32_t s_monitor_interval = 0; - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - smartstrap_state_lock(); - if (s_subscriber_count == 0) { - prv_release_accessory(); - smartstrap_state_unlock(); - return; - } - - if (smartstrap_fsm_state_get() == SmartstrapStateUnsubscribed) { - if (prv_acquire_accessory()) { - // we will now start to attempt to connect to the smartstrap - smartstrap_fsm_state_set(SmartstrapStateReadReady); - s_monitor_interval = 0; - } else { - // try again in a little while to acquire the accessory - s_monitor_interval = ACCESSORY_ACQUIRE_INTERVAL; - } - } - - bool can_send = false; - if (smartstrap_fsm_state_get() == SmartstrapStateReadReady) { - if (accessory_is_present() || smartstrap_is_connected()) { - // If the accessory is present and we are connected then we can send data freely. If the - // accessory is present but we're not connected, we'll try to connected. If we are connected - // but the accessory is not present, we'll disconnect. - can_send = true; - } else { - // back off a bit and check again for an accessory to be present - s_monitor_interval = MIN(s_monitor_interval + DETECTION_BACKOFF, DETECTION_MAX_INTERVAL); - } - } else if (smartstrap_fsm_state_get() != SmartstrapStateUnsubscribed) { - // There is a request in progress. We'll get kicked when it's completed. - s_monitor_interval = KICK_TIMEOUT_INTERVAL; - } - - smartstrap_state_unlock(); - - if (can_send) { - // We should attempt to send control messages first, followed by pending attributes. - bool did_send = false; - if (smartstrap_profiles_send_control()) { - did_send = true; - } else if (smartstrap_attribute_send_pending()) { - did_send = true; - } - if (did_send && smartstrap_fsm_state_get() == SmartstrapStateReadReady) { - if (accessory_bus_contention_detected()) { - // There was bus contention during the send which caused it to fail. Set a short interval - // before trying to send to allow the bus contention to clear. - s_monitor_interval = BUS_CONTENTION_INTERVAL; - } else { - // We sent a write request, so are ready to send another request right away. - s_monitor_interval = 0; - } - } else { - // Either we are now waiting for a response, at which point we'll get kicked, or there was - // nothing to send, in which case we'll get kicked when there is. - s_monitor_interval = KICK_TIMEOUT_INTERVAL; - } - } - - if (s_monitor_interval) { - // run the monitor again after the set interval - new_timer_start(s_monitor_timer, s_monitor_interval, prv_monitor_timer_cb, NULL, 0); - } else { - // custom fast-path for 0ms timeout - prv_monitor_timer_cb(NULL); - } -} - -static void prv_monitor_timer_cb(void *context) { - // we need to run from KernelBG so schedule a callback - system_task_add_callback(prv_monitor_system_task_cb, NULL); -} - -void smartstrap_connection_kick_monitor(void) { - // queue up the system task immediately - prv_monitor_timer_cb(NULL); -} - -void smartstrap_connection_got_valid_data(void) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - s_last_data_time = rtc_get_time(); -} - -time_t smartstrap_connection_get_time_since_valid_data(void) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - return rtc_get_time() - s_last_data_time; -} - -#if !RELEASE -#include "console/prompt.h" - -void command_smartstrap_status(void) { - char buf[80]; - prompt_send_response_fmt(buf, sizeof(buf), "present=%d, connected=%d", accessory_is_present(), - smartstrap_is_connected()); -} -#endif - -// Subscription functions -//////////////////////////////////////////////////////////////////////////////// - -bool smartstrap_connection_has_subscriber(void) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - return s_subscriber_count > 0; -} - -DEFINE_SYSCALL(void, sys_smartstrap_subscribe, void) { - smartstrap_state_lock(); - s_subscriber_count++; - if (s_subscriber_count == 1) { - // kick the connection monitor - smartstrap_connection_kick_monitor(); - } - smartstrap_state_unlock(); -} - -DEFINE_SYSCALL(void, sys_smartstrap_unsubscribe, void) { - smartstrap_state_lock(); - s_subscriber_count--; - if (s_subscriber_count == 0) { - // Disconnect directly from here rather than waiting for the monitor in order to ensure it - // happens synchronously. - prv_release_accessory(); - } - PBL_ASSERTN(s_subscriber_count >= 0); - smartstrap_state_unlock(); -} diff --git a/src/fw/services/normal/accessory/smartstrap_connection.h b/src/fw/services/normal/accessory/smartstrap_connection.h deleted file mode 100644 index d84cc9e280..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_connection.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/pebble_tasks.h" - -#include -#include -#include - -void smartstrap_connection_init(void); - -//! Kicks the monitor which is responsible for detection of connected smartstraps and sending any -//! pending requests. -void smartstrap_connection_kick_monitor(void); - -//! Called to indicate that we got valid data from the smartstrap (@see smartstrap_got_valid_data()) -void smartstrap_connection_got_valid_data(void); - -//! Returns the number of milliseconds since smartstrap_got_valid_data() was last called. -time_t smartstrap_connection_get_time_since_valid_data(void); - -//! Returns whether or not we currently have any subscribers. -bool smartstrap_connection_has_subscriber(void); - -//! Subscribes to the smartstrap. When there is at least one subscriber, we will attempt to connect -//! to the smartstrap. -void sys_smartstrap_subscribe(void); - -//! Unsubscribes from the smartstrap. When nobody is subscribed, we will disconnect from the -//! smartstrap. -void sys_smartstrap_unsubscribe(void); diff --git a/src/fw/services/normal/accessory/smartstrap_generic_service.c b/src/fw/services/normal/accessory/smartstrap_generic_service.c deleted file mode 100644 index eec96e025d..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_generic_service.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app_smartstrap.h" -#include "drivers/accessory.h" -#include "kernel/events.h" -#include "kernel/pebble_tasks.h" -#include "os/mutex.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_link_control.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/mbuf.h" - -#define MAX_SERVICES 10 -#define MIN_SERVICE_ID 0x100 -#define GENERIC_SERVICE_VERSION 1 -#define TIMEOUT_MS 100 -//! The largest message for attributes which we support internally (the app has its own buffer) -#define BUFFER_LENGTH 20 -#define MIN_SERVICE_DISCOVERY_INTERVAL 1 - -typedef struct { - uint16_t service_id; - uint16_t attribute_id; -} ReadInfo; - -typedef struct PACKED { - uint8_t version; - uint16_t service_id; - uint16_t attribute_id; - uint8_t type; - uint8_t error; - uint16_t length; -} FrameInfo; - -typedef struct PACKED { - uint16_t service_id; - uint16_t attribute_id; -} NotificationInfoData; - -typedef enum { - GenericServiceResultOk = 0, - GenericServiceResultNotSupported = 1, - NumGenericServiceResults -} GenericServiceResult; - -typedef enum { - GenericServiceTypeRead = 0, - GenericServiceTypeWrite = 1, - GenericServiceTypeWriteRead = 2, - NumGenericServiceTypes -} GenericServiceType; - -typedef enum { - ReservedServiceManagement = 0x0101, - ReservedServiceControl = 0x0102, - ReservedServiceMax = 0x0fff -} ReservedService; - -typedef enum { - ManagementServiceAttributeServiceDiscovery = 0x0001, - ManagementServiceAttributeNotificationInfo = 0x0002 -} ManagementServiceAttribute; - -typedef enum { - ControlServiceAttributeLaunchApp = 0x0001, - ControlServiceAttributeButtonEvent = 0x0002 -} ControlServiceAttribute; - -static MBuf *s_reserved_read_mbuf; -static MBuf *s_read_header_mbuf; -static uint8_t s_read_header[sizeof(FrameInfo)]; -static ReadInfo s_read_info; -static PebbleMutex *s_read_lock; -static uint8_t s_read_buffer[BUFFER_LENGTH]; -static bool s_has_done_service_discovery; - - -static void prv_init(void) { - s_read_lock = mutex_create(); -} - -static SmartstrapResult prv_do_send(GenericServiceType type, uint16_t service_id, - uint16_t attribute_id, MBuf *write_mbuf, MBuf *read_mbuf, - uint16_t timeout_ms) { - if (s_read_header_mbuf) { - // already a read in progress - return SmartstrapResultBusy; - } - - mutex_lock(s_read_lock); - // populate the read info - s_read_info = (ReadInfo) { - .service_id = service_id, - .attribute_id = attribute_id, - }; - - // add the header - FrameInfo header = (FrameInfo) { - .version = GENERIC_SERVICE_VERSION, - .type = type, - .error = GenericServiceResultOk, - .service_id = service_id, - .attribute_id = attribute_id, - .length = mbuf_get_chain_length(write_mbuf) - }; - MBuf send_header_mbuf = MBUF_EMPTY; - mbuf_set_data(&send_header_mbuf, &header, sizeof(header)); - mbuf_append(&send_header_mbuf, write_mbuf); - - // setup the MBuf chain for reading - s_read_header_mbuf = mbuf_get(&s_read_header, sizeof(s_read_header), MBufPoolSmartstrap); - mbuf_append(s_read_header_mbuf, read_mbuf); - - SmartstrapResult result = smartstrap_send(SmartstrapProfileGenericService, &send_header_mbuf, - s_read_header_mbuf, timeout_ms); - if (result != SmartstrapResultOk) { - mbuf_free(s_read_header_mbuf); - s_read_header_mbuf = NULL; - } - mutex_unlock(s_read_lock); - return result; -} - -static void prv_send_service_discovery(void *context) { - const uint16_t service_id = ReservedServiceManagement; - const uint16_t attribute_id = ManagementServiceAttributeServiceDiscovery; - if (s_reserved_read_mbuf) { - // already a read in progress - return; - } - s_reserved_read_mbuf = mbuf_get(s_read_buffer, sizeof(s_read_buffer), MBufPoolSmartstrap); - SmartstrapResult result = prv_do_send(GenericServiceTypeRead, service_id, attribute_id, NULL, - s_reserved_read_mbuf, TIMEOUT_MS); - if (result != SmartstrapResultOk) { - mbuf_free(s_reserved_read_mbuf); - s_reserved_read_mbuf = NULL; - } - PBL_LOG(LOG_LEVEL_DEBUG, "Sent service discovery message (result=%d)", result); -} - -static void prv_set_connected(bool connected) { - s_has_done_service_discovery = false; -} - -static bool prv_handle_management_attribute_read(bool success, ManagementServiceAttribute attr, - void *data, uint32_t length) { - if (!success) { - PBL_LOG(LOG_LEVEL_DEBUG, "Read of management attribute was not successful (0x%x)", attr); - return false; - } - if (attr == ManagementServiceAttributeServiceDiscovery) { - if (length % sizeof(uint16_t)) { - PBL_LOG(LOG_LEVEL_WARNING, "Service discovery response is invalid length: %"PRIu32, length); - return false; - } - // validate and mark the services as connected - uint32_t i; - uint16_t *services = data; - bool has_valid_service = false; - for (i = 0; i < length / sizeof(uint16_t); i++) { - if ((services[i] > ReservedServiceMax) || (services[i] == ReservedServiceControl)) { - has_valid_service = true; - smartstrap_connection_state_set_by_service(services[i], true); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Skipping invalid service_id 0x%x", services[i]); - } - } - if (has_valid_service) { - s_has_done_service_discovery = true; - } else { - return false; - } - } else if (attr == ManagementServiceAttributeNotificationInfo) { - if (length != sizeof(NotificationInfoData)) { - PBL_LOG(LOG_LEVEL_WARNING, "Notification info response is invalid length: %"PRIu32, length); - return false; - } - const NotificationInfoData *notification_info = data; - if (notification_info->service_id <= ReservedServiceMax) { - // Currently we only support the control service and its launch app attribute - if (notification_info->service_id == ReservedServiceControl && - notification_info->attribute_id == ControlServiceAttributeLaunchApp) { - s_reserved_read_mbuf = mbuf_get(s_read_buffer, sizeof(s_read_buffer), MBufPoolSmartstrap); - SmartstrapResult result = prv_do_send(GenericServiceTypeRead, notification_info->service_id, - notification_info->attribute_id, NULL, - s_reserved_read_mbuf, TIMEOUT_MS); - if (result != SmartstrapResultOk) { - mbuf_free(s_reserved_read_mbuf); - s_reserved_read_mbuf = NULL; - return false; - } - return true; - } else { - // Unsupported reserved service and/or attribute - return false; - } - } else { - // Notification wasn't for a reserved service, send a notification event - smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, notification_info->service_id, - notification_info->attribute_id, 0); - } - } else { - WTF; - } - return true; -} - -static bool prv_handle_control_attribute_read(bool success, ControlServiceAttribute attr, - void *data, uint32_t length) { - if (!success) { - PBL_LOG(LOG_LEVEL_DEBUG, "Read of control attribute was not successful (0x%x)", attr); - return false; - } - if (attr == ControlServiceAttributeLaunchApp) { - if (length != UUID_SIZE) { - PBL_LOG(LOG_LEVEL_WARNING, "Launch app response is invalid length: %"PRIu32, length); - return false; - } - - Uuid *app_uuid = (Uuid *)data; - AppInstallId app_id = app_install_get_id_for_uuid(app_uuid); - if (app_id == INSTALL_ID_INVALID) { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempting to launch an invalid app"); - return false; - } - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = app_id, - .common.reason = APP_LAUNCH_SMARTSTRAP, - }); - } else if (attr == ControlServiceAttributeButtonEvent) { - // TODO: PBL-38311 - return false; - } else { - WTF; - } - return true; -} - -static bool prv_read_complete(bool success, uint32_t length) { - FrameInfo *header = mbuf_get_data(s_read_header_mbuf); - // get the length of the data buffer(s) which is the max length of data we could have received - const uint32_t data_length = mbuf_get_chain_length(mbuf_get_next(s_read_header_mbuf)); - mbuf_free(s_read_header_mbuf); - s_read_header_mbuf = NULL; - SmartstrapResult result = SmartstrapResultOk; - if (!success) { - result = SmartstrapResultTimeOut; - } else if ((length < sizeof(FrameInfo)) || - (header->length != length - sizeof(FrameInfo)) || - (header->length > data_length) || - (header->version != GENERIC_SERVICE_VERSION) || - (header->error >= NumGenericServiceResults) || - (header->service_id != s_read_info.service_id) || - (header->attribute_id != s_read_info.attribute_id)) { - success = false; - // TODO: We just got a bad frame, but time-out is the best error we have right now. Ideally, - // we could validate the generic service header and drop this frame before we stop the read - // timeout and then keep looking for a valid frame until the timeout is hit. - result = SmartstrapResultTimeOut; - } else if (success && (header->error != GenericServiceResultOk)) { - // The response was completely valid, but there was a non-Ok error returned - success = false; - // translate the error code to the appropraite SmartstrapResult - if (header->error == GenericServiceResultNotSupported) { - result = SmartstrapResultAttributeUnsupported; - } else { - WTF; - } - } - - const uint16_t service_id = s_read_info.service_id; - const uint16_t attribute_id = s_read_info.attribute_id; - if (success) { - length = header->length; - } else { - length = 0; - } - - if (service_id <= ReservedServiceMax) { - // This is a reserved service read which we should handle internally - void *data = mbuf_get_data(s_reserved_read_mbuf); - mbuf_free(s_reserved_read_mbuf); - s_reserved_read_mbuf = NULL; - if (service_id == ReservedServiceManagement) { - success = prv_handle_management_attribute_read(success, attribute_id, data, length); - } else if (service_id == ReservedServiceControl) { - success = prv_handle_control_attribute_read(success, attribute_id, data, length); - } else { - WTF; - } - } else { - smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, - result, service_id, attribute_id, length); - } - - return success; -} - -static void prv_handle_notification(void) { - // follow-up with a notification info frame - const uint16_t service_id = ReservedServiceManagement; - const uint16_t attribute_id = ManagementServiceAttributeNotificationInfo; - if (s_reserved_read_mbuf) { - // already a read in progress - return; - } - s_reserved_read_mbuf = mbuf_get(s_read_buffer, sizeof(s_read_buffer), MBufPoolSmartstrap); - SmartstrapResult result = prv_do_send(GenericServiceTypeRead, service_id, attribute_id, NULL, - s_reserved_read_mbuf, TIMEOUT_MS); - if (result != SmartstrapResultOk) { - mbuf_free(s_reserved_read_mbuf); - s_reserved_read_mbuf = NULL; - } -} - -static SmartstrapResult prv_send(const SmartstrapRequest *request) { - if (!s_has_done_service_discovery || !sys_smartstrap_is_service_connected(request->service_id)) { - return SmartstrapResultServiceUnavailable; - } - - GenericServiceType type; - if (request->write_mbuf && request->read_mbuf) { - type = GenericServiceTypeWriteRead; - } else if (request->write_mbuf) { - type = GenericServiceTypeWrite; - } else if (request->read_mbuf) { - type = GenericServiceTypeRead; - } else { - type = GenericServiceTypeRead; // stop lint from complaining - WTF; - } - return prv_do_send(type, request->service_id, request->attribute_id, request->write_mbuf, - request->read_mbuf, request->timeout_ms); -} - -static bool prv_send_control(void) { - // make sure we're not spamming the smartstrap with service discovery messages - static time_t s_last_service_discovery_time = 0; - const time_t current_time = rtc_get_time(); - if (!s_has_done_service_discovery && - (current_time > s_last_service_discovery_time + MIN_SERVICE_DISCOVERY_INTERVAL) && - smartstrap_link_control_is_profile_supported(SmartstrapProfileGenericService)) { - prv_send_service_discovery(NULL); - s_last_service_discovery_time = current_time; - return true; - } - return false; -} - -static void prv_read_aborted(void) { - mbuf_free(s_read_header_mbuf); - s_read_header_mbuf = NULL; - mbuf_free(s_reserved_read_mbuf); - s_reserved_read_mbuf = NULL; -} - -const SmartstrapProfileInfo *smartstrap_generic_service_get_info(void) { - static const SmartstrapProfileInfo s_generic_service_info = { - .profile = SmartstrapProfileGenericService, - .max_services = MAX_SERVICES, - .min_service_id = MIN_SERVICE_ID, - .init = prv_init, - .connected = prv_set_connected, - .send = prv_send, - .read_complete = prv_read_complete, - .notify = prv_handle_notification, - .control = prv_send_control, - .read_aborted = prv_read_aborted, - }; - return &s_generic_service_info; -} diff --git a/src/fw/services/normal/accessory/smartstrap_link_control.c b/src/fw/services/normal/accessory/smartstrap_link_control.c deleted file mode 100644 index 44d300c047..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_link_control.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/accessory.h" -#include "drivers/rtc.h" -#include "kernel/pebble_tasks.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_link_control.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#include -#include - -#define LINK_CONTROL_VERSION 1 -#define TIMEOUT_MS 100 -#define MAX_DATA_LENGTH 4 -#define MAX_FRAME_LENGTH (sizeof(FrameHeader) + MAX_DATA_LENGTH) -//! The number of consecutive invalid link control responses before we try to reconnect -#define MAX_STRIKES 3 -//! How often we'll go without some valid data from the smartstrap before sending a status message -//! and disconnecting if the smartstrap doesn't reply. This is in seconds. -#define STATUS_CHECK_INTERVAL 5 -//! The minimum number of seconds between connnection requests to avoid spamming the smartstrap. -#define MIN_CONNECTION_REQUEST_INTERVAL 1 -//! The minimum number of seconds between status requests to avoid spamming the smartstrap -#define MIN_STATUS_REQUEST_INTERVAL 5 - -typedef enum { - LinkControlTypeInvalid = 0, - LinkControlTypeStatus = 1, - LinkControlTypeProfiles = 2, - LinkControlTypeBaud = 3, - NumLinkControlTypes -} LinkControlType; - -typedef enum { - LinkControlStatusOk = 0, - LinkControlStatusBaudRate = 1, - LinkControlStatusDisconnect = 2 -} LinkControlStatus; - -static const AccessoryBaud BAUDS[] = { - AccessoryBaud9600, - AccessoryBaud14400, - AccessoryBaud19200, - AccessoryBaud28800, - AccessoryBaud38400, - AccessoryBaud57600, - AccessoryBaud62500, - AccessoryBaud115200, - AccessoryBaud125000, - AccessoryBaud230400, - AccessoryBaud250000, - AccessoryBaud460800 -}; -_Static_assert(ARRAY_LENGTH(BAUDS) - 1 == AccessoryBaud460800, "BAUDS doesn't match AccessoryBaud"); - -typedef struct { - uint8_t version; - LinkControlType type:8; - uint8_t data[]; -} FrameHeader; - -//! store supported profiles as a series of bits -static uint32_t s_profiles; -_Static_assert(sizeof(s_profiles) * 8 >= NumSmartstrapProfiles, "s_profiles is too small"); -//! MBuf used for receiving -static MBuf *s_read_mbuf; -static uint8_t s_read_data[MAX_FRAME_LENGTH]; -//! The type of the most recent link control message which was sent -static LinkControlType s_type; -//! Number of consecutive bad status message responses received from the smartstrap -static int s_strikes; - - -static void prv_do_send(LinkControlType type) { - FrameHeader header = (FrameHeader) { - .version = LINK_CONTROL_VERSION, - .type = type - }; - s_type = type; - MBuf send_mbuf = MBUF_EMPTY; - mbuf_set_data(&send_mbuf, &header, sizeof(header)); - PBL_ASSERTN(!s_read_mbuf); - s_read_mbuf = mbuf_get(s_read_data, MAX_FRAME_LENGTH, MBufPoolSmartstrap); - SmartstrapResult result = smartstrap_send(SmartstrapProfileLinkControl, &send_mbuf, s_read_mbuf, - TIMEOUT_MS); - if (result != SmartstrapResultOk) { - mbuf_free(s_read_mbuf); - s_read_mbuf = NULL; - PBL_LOG(LOG_LEVEL_WARNING, "Sending of link control message failed: result=%d, type=%d", result, - type); - smartstrap_link_control_disconnect(); - } -} - -static void prv_fatal_error_strike(void) { - s_strikes++; - PBL_LOG(LOG_LEVEL_WARNING, "Fatal error strike %d", s_strikes); - if (s_strikes >= MAX_STRIKES) { - // out of strikes - smartstrap_link_control_disconnect(); - } -} - -static bool prv_read_complete(bool success, uint32_t length) { - const FrameHeader *header = mbuf_get_data(s_read_mbuf); - mbuf_free(s_read_mbuf); - s_read_mbuf = NULL; - const uint32_t data_length = length - sizeof(FrameHeader); - if (!success || - (length < sizeof(FrameHeader)) || - (data_length > MAX_DATA_LENGTH) || - (header->type != s_type) || - (header->version != LINK_CONTROL_VERSION)) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid link control response (type=%d).", s_type); - if (s_type == LinkControlTypeStatus) { - prv_fatal_error_strike(); - } else if (!s_profiles) { - smartstrap_link_control_disconnect(); - } - return false; - } - s_strikes = 0; - - if (header->type == LinkControlTypeStatus) { - // status message - const LinkControlStatus status = header->data[0]; - if (status == LinkControlStatusOk) { - PBL_LOG(LOG_LEVEL_DEBUG, "Got link control status: Ok"); - smartstrap_connection_state_set(true); - } else if (status == LinkControlStatusBaudRate) { - PBL_LOG(LOG_LEVEL_DEBUG, "Got link control status: Baud rate"); - prv_do_send(LinkControlTypeBaud); - } else if (status == LinkControlStatusDisconnect) { - PBL_LOG(LOG_LEVEL_DEBUG, "Got link control status: Disconnect"); - smartstrap_link_control_disconnect(); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Got link control status: INVALID (%d)", status); - smartstrap_link_control_disconnect(); - success = false; - } - } else if (header->type == LinkControlTypeProfiles) { - // profiles message - if ((data_length % sizeof(uint16_t)) == 0) { - s_profiles = 0; - uint16_t *profiles = (uint16_t *)header->data; - const uint32_t num_profiles = data_length / sizeof(uint16_t); - for (uint32_t i = 0; i < num_profiles; i++) { - if ((profiles[i] > SmartstrapProfileInvalid) && - (profiles[i] < NumSmartstrapProfiles) && - (profiles[i] != SmartstrapProfileLinkControl)) { - // handle the valid profile - s_profiles |= (1 << profiles[i]); - } - } - if (s_profiles == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "No profiles specified"); - smartstrap_link_control_disconnect(); - success = false; - } else { - prv_do_send(LinkControlTypeStatus); - } - } else { - // length is invalid (should be an even multiple of the size of the profile value) - PBL_LOG(LOG_LEVEL_WARNING, "Got invalid profiles length (%"PRIu32")", data_length); - smartstrap_link_control_disconnect(); - success = false; - } - } else if (header->type == LinkControlTypeBaud) { - // new baud rate - const uint8_t requested_baud = header->data[0]; - if (requested_baud >= ARRAY_LENGTH(BAUDS)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid baud rate (%"PRIu8")", requested_baud); - smartstrap_link_control_disconnect(); - success = false; - } else { - accessory_set_baudrate(BAUDS[requested_baud]); - prv_do_send(LinkControlTypeStatus); - } - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid response type (%d)", header->type); - smartstrap_link_control_disconnect(); - success = false; - } - return success; -} - -void smartstrap_link_control_connect(void) { - prv_do_send(LinkControlTypeProfiles); - accessory_set_baudrate(AccessoryBaud9600); -} - -void smartstrap_link_control_disconnect(void) { - s_strikes = 0; - s_profiles = 0; - accessory_set_baudrate(AccessoryBaud9600); - smartstrap_connection_state_set(false); -} - -bool smartstrap_link_control_is_profile_supported(SmartstrapProfile profile) { - PBL_ASSERTN((profile > SmartstrapProfileInvalid) && (profile < NumSmartstrapProfiles)); - return !!(s_profiles & (1 << profile)); -} - -static bool prv_send_control(void) { - static time_t s_last_connection_request_time = 0; - static time_t s_last_status_check_time = 0; - const time_t current_time = rtc_get_time(); - if (!smartstrap_is_connected() && accessory_is_present() && - (smartstrap_fsm_state_get() == SmartstrapStateReadReady)) { - if (current_time > s_last_connection_request_time + MIN_CONNECTION_REQUEST_INTERVAL) { - PBL_LOG(LOG_LEVEL_DEBUG, "Smartstrap detected - attempting to connect."); - s_last_connection_request_time = current_time; - smartstrap_link_control_connect(); - } - return true; - } else if (smartstrap_connection_get_time_since_valid_data() > STATUS_CHECK_INTERVAL) { - if (current_time > s_last_status_check_time + MIN_STATUS_REQUEST_INTERVAL) { - // send a status message - prv_do_send(LinkControlTypeStatus); - if (accessory_bus_contention_detected()) { - PBL_LOG(LOG_LEVEL_WARNING, "Bus contention while sending status message"); - // Count bus contention as a strike as it could be that the accessory is disconnected - // or misbehaving. - prv_fatal_error_strike(); - } - s_last_status_check_time = current_time; - } - return true; - } - return false; -} - -static void prv_read_aborted(void) { - mbuf_free(s_read_mbuf); - s_read_mbuf = NULL; -} - -const SmartstrapProfileInfo *smartstrap_link_control_get_info(void) { - static const SmartstrapProfileInfo s_link_control_info = { - .profile = SmartstrapProfileLinkControl, - .read_complete = prv_read_complete, - .control = prv_send_control, - .read_aborted = prv_read_aborted, - }; - return &s_link_control_info; -} diff --git a/src/fw/services/normal/accessory/smartstrap_link_control.h b/src/fw/services/normal/accessory/smartstrap_link_control.h deleted file mode 100644 index 21b5e01528..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_link_control.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/app_smartstrap.h" -#include "services/normal/accessory/smartstrap_connection.h" -#include "services/normal/accessory/smartstrap_profiles.h" - -#include - -//! Sends a connection request to the smartstrap (should only be called from smartstrap_connection) -void smartstrap_link_control_connect(void); - -//! Disconnects from the smartstrap -void smartstrap_link_control_disconnect(void); - -//! Checks whether the specified profile is supported by the smartstrap which is connected. -bool smartstrap_link_control_is_profile_supported(SmartstrapProfile profile); diff --git a/src/fw/services/normal/accessory/smartstrap_profile_registry.def b/src/fw/services/normal/accessory/smartstrap_profile_registry.def deleted file mode 100644 index b890b257cc..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_profile_registry.def +++ /dev/null @@ -1,17 +0,0 @@ -// Registery for Smartstrap Profiles - -// Syntax: REGISTER_SMARTSTRAP_PROFILE(func) -// where func is a function of the following type: -// const SmartstrapProfileInfo *func(void); -// -// An extern declaration will be automatically generated in smartstrap_profiles.h and the info -// struct will be automatically inserted into the array in smartstrap_profiles.c. -// -// Note that the order is significant as it determines the order in which the profiles are given a -// chance to send control messages. - -REGISTER_SMARTSTRAP_PROFILE(smartstrap_link_control_get_info) -REGISTER_SMARTSTRAP_PROFILE(smartstrap_raw_data_get_info) -REGISTER_SMARTSTRAP_PROFILE(smartstrap_generic_service_get_info) - -// vim:filetype=c diff --git a/src/fw/services/normal/accessory/smartstrap_profiles.c b/src/fw/services/normal/accessory/smartstrap_profiles.c deleted file mode 100644 index d17b95e142..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_profiles.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "process_management/process_manager.h" -#include "services/normal/accessory/smartstrap_connection.h" -#include "services/normal/accessory/smartstrap_link_control.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#define NUM_PROFILES() ARRAY_LENGTH(s_profile_info_functions) -//! Iterates through the list of profiles and provides the info for each by calling the function -#define FOREACH_PROFILE_INFO(info) \ - for (unsigned int i = 0; i < NUM_PROFILES(); i++) \ - if ((info = s_profile_info_functions[i]()) != NULL) - -static const SmartstrapProfileGetInfoFunc s_profile_info_functions[] = { -#define REGISTER_SMARTSTRAP_PROFILE(f) f, -#include "services/normal/accessory/smartstrap_profile_registry.def" -#undef REGISTER_SMARTSTRAP_PROFILE -}; -// every profile except for SmartstrapProfileInvalid should be registered -_Static_assert(NUM_PROFILES() == (NumSmartstrapProfiles - 1), - "The number of registered profiles doesn't match the SmartstrapProfiles enum"); - - -static const SmartstrapProfileInfo *prv_get_info_by_profile(SmartstrapProfile profile) { - const SmartstrapProfileInfo *info; - FOREACH_PROFILE_INFO(info) { - if (info->profile == profile) { - return info; - } - } - return NULL; -} - -void smartstrap_profiles_init(void) { - // call the init handler for all the profiles - const SmartstrapProfileInfo *info; - FOREACH_PROFILE_INFO(info) { - if (info->init) { - info->init(); - } - } -} - -void smartstrap_profiles_handle_connection_event(bool connected) { - // send the event to the applicable profiles - PBL_LOG(LOG_LEVEL_DEBUG, "Dispatching smartstrap connection event (connected=%d)", connected); - const SmartstrapProfileInfo *info; - FOREACH_PROFILE_INFO(info) { - if (info->connected) { - info->connected(connected); - } - } - if (connected) { - smartstrap_connection_got_valid_data(); - } -} - -static const SmartstrapProfileInfo *prv_get_info_by_service_id(uint16_t service_id) { - // find the profile with the lowest minimum service_id which is <= the specified one - const SmartstrapProfileInfo *match_info = NULL; - const SmartstrapProfileInfo *info; - FOREACH_PROFILE_INFO(info) { - if ((info->max_services > 0) && (info->min_service_id <= service_id) && - (!match_info || (info->min_service_id > match_info->min_service_id))) { - // this is either the first match or a better match - match_info = info; - } - } - return match_info; -} - -SmartstrapResult smartstrap_profiles_handle_request(const SmartstrapRequest *request) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - // make sure this request is able to be fulfilled - smartstrap_state_lock(); - const SmartstrapProfileInfo *info = prv_get_info_by_service_id(request->service_id); - PBL_ASSERTN(info && info->send); - if (!smartstrap_connection_has_subscriber() || !smartstrap_is_connected() || - !smartstrap_link_control_is_profile_supported(info->profile)) { - smartstrap_state_unlock(); - return SmartstrapResultServiceUnavailable; - } - - SmartstrapResult result = info->send(request); - smartstrap_state_unlock(); - return result; -} - -void smartstrap_profiles_handle_read(bool success, SmartstrapProfile profile, uint32_t length) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - if (!success) { - // this is a timeout - PBL_LOG(LOG_LEVEL_WARNING, "Timed-out waiting for a response from the smartstrap"); - } - - // dispatch the read based on the profile - const SmartstrapProfileInfo *info = prv_get_info_by_profile(profile); - PBL_ASSERTN(info && info->read_complete); - smartstrap_state_lock(); - if (info->read_complete(success, length)) { - smartstrap_connection_got_valid_data(); - } - smartstrap_state_unlock(); - // If we are connected, kick the connection monitor right away. Otherwise, just let it wake up - // itself based on its own timer. This prevents us spamming the smartstrap with connection - // requests. - if (smartstrap_is_connected()) { - // send the next message - smartstrap_connection_kick_monitor(); - } -} - -void smartstrap_profiles_handle_read_aborted(SmartstrapProfile profile) { - PBL_ASSERTN(portIN_CRITICAL()); - // dispatch the aborted read based on the profile - const SmartstrapProfileInfo *info = prv_get_info_by_profile(profile); - if (info && info->read_aborted) { - info->read_aborted(); - } -} - -void smartstrap_profiles_handle_notification(bool success, SmartstrapProfile profile) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - if (!success) { - PBL_LOG(LOG_LEVEL_WARNING, "Dropped notification due to a timeout on the context frame."); - return; - } else if (!smartstrap_is_connected()) { - PBL_LOG(LOG_LEVEL_WARNING, "Dropped notification due to not being connected."); - return; - } - - // dispatch the notification based on the profile - const SmartstrapProfileInfo *info = prv_get_info_by_profile(profile); - if (info && info->notify) { - smartstrap_state_lock(); - info->notify(); - smartstrap_state_unlock(); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Dropped notification for unsupported profile: %d", profile); - } -} - -bool smartstrap_profiles_send_control(void) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - bool did_send = false; - smartstrap_state_lock(); - const SmartstrapProfileInfo *info; - FOREACH_PROFILE_INFO(info) { - if (info->control && info->control()) { - did_send = true; - break; - } - } - smartstrap_state_unlock(); - return did_send; -} - - -unsigned int smartstrap_profiles_get_max_services(void) { - unsigned int max = 0; - const SmartstrapProfileInfo *info; - FOREACH_PROFILE_INFO(info) { - max += info->max_services; - } - return max; -} diff --git a/src/fw/services/normal/accessory/smartstrap_profiles.h b/src/fw/services/normal/accessory/smartstrap_profiles.h deleted file mode 100644 index df96f91afd..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_profiles.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/pebble_tasks.h" -#include "util/mbuf.h" - -#include - -//! The currently-supported Smartstrap profiles -typedef enum { - SmartstrapProfileInvalid = 0, - SmartstrapProfileLinkControl = 1, - SmartstrapProfileRawData = 2, - SmartstrapProfileGenericService = 3, - NumSmartstrapProfiles, -} SmartstrapProfile; - -typedef struct { - uint16_t service_id; - uint16_t attribute_id; - MBuf *write_mbuf; - MBuf *read_mbuf; - uint16_t timeout_ms; -} SmartstrapRequest; - -typedef void (*SmartstrapProfileInitHandler)(void); -typedef void (*SmartstrapProfileConnectedHandler)(bool connected); -typedef SmartstrapResult (*SmartstrapProfileSendHandler)(const SmartstrapRequest *request); -typedef bool (*SmartstrapProfileReadCompleteHandler)(bool success, uint32_t length); -typedef void (*SmartstrapProfileReadAbortedHandler)(void); -typedef void (*SmartstrapProfileNotifyHandler)(void); -typedef bool (*SmartstrapProfileSendControlHandler)(void); - -typedef struct { - //! The profile this info applies to - SmartstrapProfile profile; - //! The maximum number of services which a smartstrap may support for this profile - uint8_t max_services; - //! The loweest service id which this profile supports - uint16_t min_service_id; - //! Optional handler for initialization - SmartstrapProfileInitHandler init; - //! Optional handler for connection changes - SmartstrapProfileConnectedHandler connected; - //! Required handler for sending requests - SmartstrapProfileSendHandler send; - //! Required handler for completed read requests - SmartstrapProfileReadCompleteHandler read_complete; - //! Optional handler for aborted requests (NOTE: called from a critical region) - SmartstrapProfileReadAbortedHandler read_aborted; - //! Optional handler for notifications - SmartstrapProfileNotifyHandler notify; - //! Optional handler to send any pending control messages - SmartstrapProfileSendControlHandler control; -} SmartstrapProfileInfo; - -typedef const SmartstrapProfileInfo *(*SmartstrapProfileGetInfoFunc)(void); - -// generate funciton prototypes for profile info functions -#define REGISTER_SMARTSTRAP_PROFILE(f) const SmartstrapProfileInfo *f(void); -#include "services/normal/accessory/smartstrap_profile_registry.def" -#undef REGISTER_SMARTSTRAP_PROFILE - -void smartstrap_profiles_init(void); - -//! Make a smartstrap request -SmartstrapResult smartstrap_profiles_handle_request(const SmartstrapRequest *request); - -//! Handle a smartstrap read (either complete frame or timeout) -void smartstrap_profiles_handle_read(bool success, SmartstrapProfile profile, uint32_t length); - -//! Handle an aborted (canceled) read request -void smartstrap_profiles_handle_read_aborted(SmartstrapProfile profile); - -//! Handle a smartstrap notification (either valid context frame or timeout) -void smartstrap_profiles_handle_notification(bool success, SmartstrapProfile profile); - -//! Handle a connection event -void smartstrap_profiles_handle_connection_event(bool connected); - -//! Goes through the profiles in order and allows them to send control messages. Returns true once -//! one of them sends something (or false if none of them send anything). -bool smartstrap_profiles_send_control(void); - -//! Returns the maximum number of services supported across all the profiles. -unsigned int smartstrap_profiles_get_max_services(void); diff --git a/src/fw/services/normal/accessory/smartstrap_raw_data.c b/src/fw/services/normal/accessory/smartstrap_raw_data.c deleted file mode 100644 index f2e2d62870..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_raw_data.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/events.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "services/normal/accessory/smartstrap_comms.h" -#include "services/normal/accessory/smartstrap_link_control.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "system/logging.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "util/mbuf.h" - -#define RAW_DATA_MAX_SERVICES 1 - - -static bool prv_read_complete(bool success, uint32_t length) { - // we don't allow reads of more than UINT16_MAX - if (length > UINT16_MAX) { - PBL_LOG(LOG_LEVEL_WARNING, - "Got read of length %"PRIu32" which is longer than UINT16_MAX", length); - success = false; - } - // send the read complete event directly to the app - SmartstrapResult result = success ? SmartstrapResultOk : SmartstrapResultTimeOut; - smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileRawData, result, - SMARTSTRAP_RAW_DATA_SERVICE_ID, SMARTSTRAP_RAW_DATA_ATTRIBUTE_ID, - length); - return success; -} - -static void prv_handle_notification(void) { - smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileRawData, - SmartstrapResultOk, SMARTSTRAP_RAW_DATA_SERVICE_ID, - SMARTSTRAP_RAW_DATA_ATTRIBUTE_ID, 0); -} - -static void prv_set_connected(bool connected) { - if (connected && smartstrap_link_control_is_profile_supported(SmartstrapProfileRawData)) { - smartstrap_connection_state_set_by_service(SMARTSTRAP_RAW_DATA_SERVICE_ID, true); - } -} - -static SmartstrapResult prv_send(const SmartstrapRequest *request) { - return smartstrap_send(SmartstrapProfileRawData, request->write_mbuf, request->read_mbuf, - request->timeout_ms); -} - - -const SmartstrapProfileInfo *smartstrap_raw_data_get_info(void) { - static const SmartstrapProfileInfo s_profile_info = { - .profile = SmartstrapProfileRawData, - .max_services = RAW_DATA_MAX_SERVICES, - .min_service_id = SMARTSTRAP_RAW_DATA_SERVICE_ID, - .connected = prv_set_connected, - .send = prv_send, - .read_complete = prv_read_complete, - .notify = prv_handle_notification, - }; - return &s_profile_info; -} diff --git a/src/fw/services/normal/accessory/smartstrap_state.c b/src/fw/services/normal/accessory/smartstrap_state.c deleted file mode 100644 index 5757100b62..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_state.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "mcu/interrupts.h" -#include "services/normal/accessory/smartstrap_profiles.h" -#include "services/normal/accessory/smartstrap_state.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "os/mutex.h" -#include "system/logging.h" -#include "system/passert.h" - -//! The current FSM state -static volatile SmartstrapState s_fsm_state = SmartstrapStateUnsubscribed; -//! Whether or not we're connected to a smartstrap -static bool s_is_connected = false; -//! The smartstrap state lock -static PebbleMutex *s_state_lock; -//! The maximum number of services we could have connected -static uint32_t s_max_services; -//! The services we are currently connected to -static uint16_t *s_connected_services; -//! The number of connected services -static uint32_t s_num_connected_services = 0; -static PebbleMutex *s_services_lock; - - -void smartstrap_state_init(void) { - s_state_lock = mutex_create(); - s_services_lock = mutex_create(); - s_max_services = smartstrap_profiles_get_max_services(); - s_connected_services = kernel_zalloc_check(s_max_services * sizeof(uint16_t)); -} - -static void prv_assert_valid_fsm_transition(SmartstrapState prev_state, SmartstrapState new_state) { - if (new_state == SmartstrapStateUnsubscribed) { - // we can go to SmartstrapStateUnsubscribed from any state - PBL_ASSERTN(!mcu_state_is_isr()); - } else if ((prev_state == SmartstrapStateUnsubscribed) && - (new_state == SmartstrapStateReadReady)) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - } else if ((prev_state == SmartstrapStateReadReady) && - (new_state == SmartstrapStateNotifyInProgress)) { - PBL_ASSERTN(mcu_state_is_isr()); - } else if ((prev_state == SmartstrapStateReadReady) && - (new_state == SmartstrapStateReadDisabled)) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - } else if ((prev_state == SmartstrapStateNotifyInProgress) && - (new_state == SmartstrapStateReadComplete)) { - PBL_ASSERTN(mcu_state_is_isr() || (pebble_task_get_current() == PebbleTask_NewTimers)); - } else if ((prev_state == SmartstrapStateReadDisabled) && - (new_state == SmartstrapStateReadInProgress)) { - PBL_ASSERTN(mcu_state_is_isr() || (pebble_task_get_current() == PebbleTask_KernelBackground)); - } else if ((prev_state == SmartstrapStateReadDisabled) && - (new_state == SmartstrapStateReadReady)) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - } else if ((prev_state == SmartstrapStateReadInProgress) && - (new_state == SmartstrapStateReadComplete)) { - PBL_ASSERTN(mcu_state_is_isr() || (pebble_task_get_current() == PebbleTask_NewTimers)); - } else if ((prev_state == SmartstrapStateReadComplete) && - (new_state == SmartstrapStateReadReady)) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - } else { - // all other transitions are invalid - WTF; - } -} - -bool smartstrap_fsm_state_test_and_set(SmartstrapState expected_state, SmartstrapState next_state) { - const bool did_set = __atomic_compare_exchange_n(&s_fsm_state, &expected_state, next_state, false, - __ATOMIC_RELAXED, __ATOMIC_RELAXED); - if (did_set) { - prv_assert_valid_fsm_transition(expected_state, next_state); - } - return did_set; -} - -void smartstrap_fsm_state_set(SmartstrapState next_state) { - prv_assert_valid_fsm_transition(s_fsm_state, next_state); - s_fsm_state = next_state; -} - -void smartstrap_fsm_state_reset(void) { - // we should only force an update to the FSM state in a critical region - PBL_ASSERTN(portIN_CRITICAL()); - s_fsm_state = SmartstrapStateReadReady; -} - -SmartstrapState smartstrap_fsm_state_get(void) { - return s_fsm_state; -} - -//! NOTE: the caller must hold s_services_lock -static int prv_find_connected_service(uint16_t service_id) { - mutex_assert_held_by_curr_task(s_services_lock, true); - for (uint32_t i = 0; i < s_num_connected_services; i++) { - if (s_connected_services[i] == service_id) { - return i; - } - } - return -1; -} - -//! NOTE: the caller must hold s_services_lock -static bool prv_remove_connected_service(uint16_t service_id) { - mutex_assert_held_by_curr_task(s_services_lock, true); - int index = prv_find_connected_service(service_id); - if (index == -1) { - return false; - } - PBL_ASSERTN(s_num_connected_services > 0); - // move the last entry into this slot to remove this entry from the array - s_num_connected_services--; - s_connected_services[index] = s_connected_services[s_num_connected_services]; - return true; -} - -//! NOTE: the caller must hold s_services_lock -static void prv_set_service_connected(uint16_t service_id, bool connected) { - mutex_assert_held_by_curr_task(s_services_lock, true); - if (connected) { - if (prv_find_connected_service(service_id) != -1) { - // already connected - return; - } - // insert the service_id - PBL_ASSERTN(s_num_connected_services < s_max_services); - s_connected_services[s_num_connected_services++] = service_id; - } else if (!prv_remove_connected_service(service_id)) { - // we weren't previously connected - return; - } - - PBL_LOG(LOG_LEVEL_INFO, "Connection state for service (0x%x) changed to %d", service_id, - connected); - PebbleEvent event = { - .type = PEBBLE_SMARTSTRAP_EVENT, - .smartstrap = { - .type = SmartstrapConnectionEvent, - .result = connected ? SmartstrapResultOk : SmartstrapResultServiceUnavailable, - .service_id = service_id - }, - }; - event_put(&event); -} - -void smartstrap_connection_state_set_by_service(uint16_t service_id, bool connected) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - mutex_lock(s_services_lock); - prv_set_service_connected(service_id, connected); - mutex_unlock(s_services_lock); -} - -void smartstrap_connection_state_set(bool connected) { - if (connected == s_is_connected) { - return; - } - // if we're disconnecting, disconnect the services first - if (s_is_connected) { - mutex_lock(s_services_lock); - while (s_num_connected_services) { - const uint16_t service_id = s_connected_services[s_num_connected_services - 1]; - prv_set_service_connected(service_id, false); - } - s_num_connected_services = 0; - mutex_unlock(s_services_lock); - } - s_is_connected = connected; - smartstrap_profiles_handle_connection_event(connected); -} - -DEFINE_SYSCALL(bool, sys_smartstrap_is_service_connected, uint16_t service_id) { - if (!smartstrap_is_connected()) { - return false; - } - mutex_lock(s_services_lock); - bool result = prv_find_connected_service(service_id) != -1; - mutex_unlock(s_services_lock); - return result; -} - -bool smartstrap_is_connected(void) { - return (s_fsm_state != SmartstrapStateUnsubscribed) && s_is_connected; -} - -void smartstrap_state_lock(void) { - mutex_lock(s_state_lock); -} - -void smartstrap_state_unlock(void) { - mutex_unlock(s_state_lock); -} - -void smartstrap_state_assert_locked_by_current_task(void) { - mutex_assert_held_by_curr_task(s_state_lock, true); -} diff --git a/src/fw/services/normal/accessory/smartstrap_state.h b/src/fw/services/normal/accessory/smartstrap_state.h deleted file mode 100644 index 4ae64f792f..0000000000 --- a/src/fw/services/normal/accessory/smartstrap_state.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -/* - * FSM state transitions: - * +------------------+------------------+--------------+---------------------------+ - * | From State | To State | Task | Event | - * +------------------+------------------+--------------+---------------------------+ - * | Unsubscribed | ReadReady | KernelBG | First subscriber | - * | ReadReady | NotifyInProgress | ISR | Break character received | - * | ReadReady | ReadDisabled | KernelBG | Send started | - * | NotifyInProgress | ReadComplete | ISR | Complete frame received | - * | NotifyInProgress | ReadComplete | NewTimer | Read timeout | - * | ReadDisabled | ReadInProgress | KernelBG,ISR | Send completed (is_read) | - * | ReadDisabled | ReadReady | KernelBG | Send completed (!is_read) | - * | ReadDisabled | ReadReady | KernelBG | Send failed | - * | ReadInProgress | ReadComplete | ISR | Complete frame received | - * | ReadInProgress | ReadComplete | NewTimer | Read timeout | - * | ReadComplete | ReadReady | KernelBG | Frame processed | - * | *ANY STATE* | Unsubscribed | KernelBG | No more subscribers | - * +------------------+------------------+--------------+---------------------------+ - * Notes: - * - Only KernelBG can send frames when s_is_connected == false - * - Transitions which can take place from "Any" task are not allowed from ISRs - * - Received data is ignored in any state except ReadInProgress or NotifyInProgress - * - Break characters are ignored in any state except ReadReady - * - We can only start sending data after a successful transition from ReadReady to ReadDisabled - */ -typedef enum { - SmartstrapStateUnsubscribed, - SmartstrapStateReadReady, - SmartstrapStateNotifyInProgress, - SmartstrapStateReadDisabled, - SmartstrapStateReadInProgress, - SmartstrapStateReadComplete -} SmartstrapState; - -//! Initialize the smartstrap state -void smartstrap_state_init(void); - -//! Attempt to transition from expected_state to next_state and returns whether or not we -//! transitioned successfully. This transition is done atomically. -bool smartstrap_fsm_state_test_and_set(SmartstrapState expected_state, SmartstrapState next_state); - -//! Sets the FSM state, regardless of what the current state is. -//! @note The caller must ensure that there can be no other task or an ISR trying to access or -//! change the state at the same time. If there is a posiblity for contention, the caller should use -//! prv_fsm_state_test_and_set instead or enter a critical region. -void smartstrap_fsm_state_set(SmartstrapState next_state); - -//! Change the FSM state to ReadReady without doing any assertions. Should only be used with great -//! care and from a critical region (such as within smartstrap_send_cancel). -void smartstrap_fsm_state_reset(void); - -//! Returns the current FSM state -SmartstrapState smartstrap_fsm_state_get(void); - -//! Returns whether or not we're connected to a smartstrap -bool smartstrap_is_connected(void); - -//! Acquires the smartstrap lock -void smartstrap_state_lock(void); - -//! Releases the smartstrap lock -void smartstrap_state_unlock(void); - -//! Asserts that the current task has acquired the state lock -void smartstrap_state_assert_locked_by_current_task(void); - -//! Set whether or not the specified service is currently connected -void smartstrap_connection_state_set_by_service(uint16_t service_id, bool connected); - -//! Set whether or not we are connected to a smartstrap -void smartstrap_connection_state_set(bool connected); - - -// syscalls - -//! Returns whether or not the specified service is available on a connected smartstrap -bool sys_smartstrap_is_service_connected(uint16_t service_id); diff --git a/src/fw/services/normal/activity/activity_calculators.h b/src/fw/services/normal/activity/activity_calculators.h deleted file mode 100644 index fa6d05d240..0000000000 --- a/src/fw/services/normal/activity/activity_calculators.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -// ------------------------------------------------------------------------------------------------ -// Compute distance (in millimeters) covered by the taking the given number of steps in the given -// amount of time. -uint32_t activity_private_compute_distance_mm(uint32_t steps, uint32_t ms); - - -// ------------------------------------------------------------------------------------------------ -// Compute active calories (in calories, not kcalories) covered by going the given distance in -// the given amount of time. -uint32_t activity_private_compute_active_calories(uint32_t distance_mm, uint32_t ms); - -// ------------------------------------------------------------------------------------------------ -// Compute resting calories (in calories, not kcalories) within the elapsed time given -uint32_t activity_private_compute_resting_calories(uint32_t elapsed_minutes); diff --git a/src/fw/services/normal/activity/hr_util.c b/src/fw/services/normal/activity/hr_util.c deleted file mode 100644 index 1dfa9e5fe3..0000000000 --- a/src/fw/services/normal/activity/hr_util.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "hr_util.h" - -#include "activity.h" - -// ------------------------------------------------------------------------------------------------ -HRZone hr_util_get_hr_zone(int bpm) { - const int zone_thresholds[HRZone_Max] = { - activity_prefs_heart_get_zone1_threshold(), - activity_prefs_heart_get_zone2_threshold(), - activity_prefs_heart_get_zone3_threshold(), - }; - - HRZone zone; - for (zone = HRZone_Zone0; zone < HRZone_Max; zone++) { - if (bpm < zone_thresholds[zone]) { - break; - } - } - return zone; -} - -bool hr_util_is_elevated(int bpm) { - return bpm >= activity_prefs_heart_get_elevated_hr(); -} diff --git a/src/fw/services/normal/activity/hr_util.h b/src/fw/services/normal/activity/hr_util.h deleted file mode 100644 index edcfddeaef..0000000000 --- a/src/fw/services/normal/activity/hr_util.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum HRZone { - HRZone_Zone0, - HRZone_Zone1, - HRZone_Zone2, - HRZone_Zone3, - - HRZoneCount, - HRZone_Max = HRZone_Zone3, -} HRZone; - -//! Returns the HR Zone for a given BPM -HRZone hr_util_get_hr_zone(int bpm); - -//! Returns whether the BPM should be considered elevated -bool hr_util_is_elevated(int bpm); diff --git a/src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.h b/src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.h deleted file mode 100644 index 96aef98479..0000000000 --- a/src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/time/time.h" -#include "kraepelin_algorithm.h" - -// We divide the raw light sensor reading by this factor before storing it into AlgDlsMinuteData -#define ALG_RAW_LIGHT_SENSOR_DIVIDE_BY 16 - -// Nap constraints, also used by unit tests -// A sleep session in this range is always considered "primary" (not nap) sleep -// ... if it ends after this minute in the evening -#define ALG_PRIMARY_EVENING_MINUTE (21 * MINUTES_PER_HOUR) // 9pm -// ... or starts before this minute in the morning -#define ALG_PRIMARY_MORNING_MINUTE (12 * MINUTES_PER_HOUR) // 12pm - -// A sleep session outside of the primary range is considered a nap if it is less than -// this duration, otherwise it is considered a primary sleep session -#define ALG_MAX_NAP_MINUTES (3 * MINUTES_PER_HOUR) - -// Max number of hours of past data we process to figure out sleep for "today". If a sleep -// cycle *ends* after midnight today, then we still count it as today's sleep. That means the -// start of the sleep cycle could have started more than 24 hours ago. -#define ALG_SLEEP_HISTORY_HOURS_FOR_TODAY 36 diff --git a/src/fw/services/normal/alarms/alarm_pin.h b/src/fw/services/normal/alarms/alarm_pin.h deleted file mode 100644 index 75d411b8c8..0000000000 --- a/src/fw/services/normal/alarms/alarm_pin.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/uuid.h" -#include "services/normal/alarms/alarm.h" - -void alarm_pin_add(time_t alarm_time, AlarmId id, AlarmType type, AlarmKind kind, Uuid *uuid_out); - -void alarm_pin_remove(Uuid *alarm_id); diff --git a/src/fw/services/normal/analytics/analytics.c b/src/fw/services/normal/analytics/analytics.c deleted file mode 100644 index 02534b486f..0000000000 --- a/src/fw/services/normal/analytics/analytics.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "apps/system_apps/launcher/launcher_app.h" -#include "kernel/pbl_malloc.h" -#include "os/tick.h" -#include "process_management/worker_manager.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/list.h" -#include "util/time/time.h" - -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_storage.h" -#include "services/common/analytics/analytics_metric.h" -#include "services/common/analytics/analytics_heartbeat.h" -#include "services/common/analytics/analytics_logging.h" -#include "services/common/analytics/analytics_external.h" - -// Stopwatches -typedef struct { - ListNode node; - AnalyticsMetric metric; - RtcTicks starting_ticks; - uint32_t count_per_sec; - AnalyticsClient client; -} AnalyticsStopwatchNode; - -static ListNode *s_stopwatch_list = NULL; -static bool prv_is_stopwatch_for_metric(ListNode *found_node, void *data); - -void analytics_init(void) { - analytics_metric_init(); - analytics_storage_init(); - analytics_logging_init(); -} - -void analytics_set(AnalyticsMetric metric, int64_t value, AnalyticsClient client) { - analytics_set_for_uuid(metric, value, analytics_uuid_for_client(client)); -} - -void analytics_max(AnalyticsMetric metric, int64_t val, AnalyticsClient client) { - const Uuid *uuid = analytics_uuid_for_client(client); - - analytics_storage_take_lock(); - - AnalyticsHeartbeat *heartbeat = analytics_storage_find(metric, uuid, AnalyticsClient_Ignore); - if (heartbeat) { - const int64_t prev_value = analytics_heartbeat_get(heartbeat, metric); - if (prev_value < val) { - analytics_heartbeat_set(heartbeat, metric, val); - } - } - - analytics_storage_give_lock(); -} - -void analytics_set_for_uuid(AnalyticsMetric metric, int64_t value, const Uuid *uuid) { - analytics_storage_take_lock(); - - AnalyticsHeartbeat *heartbeat = analytics_storage_find(metric, uuid, AnalyticsClient_Ignore); - if (heartbeat) { - // We allow only a limited number of app heartbeats to accumulate. A NULL means we reached the - // limit - analytics_heartbeat_set(heartbeat, metric, value); - } - - analytics_storage_give_lock(); -} - -void analytics_set_entire_array(AnalyticsMetric metric, const void *value, AnalyticsClient client) { - analytics_storage_take_lock(); - - AnalyticsHeartbeat *heartbeat = analytics_storage_find(metric, NULL, client); - if (heartbeat) { - // We allow only a limited number of app heartbeats to accumulate. A NULL means we reached the - // limite - analytics_heartbeat_set_entire_array(heartbeat, metric, value); - } - - analytics_storage_give_lock(); -} - - -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { - analytics_add(metric, 1, client); -} - -void analytics_inc_for_uuid(AnalyticsMetric metric, const Uuid *uuid) { - analytics_add_for_uuid(metric, 1, uuid); -} - -void analytics_add_for_uuid(AnalyticsMetric metric, int64_t amount, const Uuid *uuid) { - analytics_storage_take_lock(); - - // We don't currently allow incrementing signed integers, because the - // only intended use of this API call is to increment counters, and counters - // should always be unsigned. This restriction could be changed in the future, - // however. - PBL_ASSERTN(analytics_metric_is_unsigned(metric)); - - AnalyticsHeartbeat *heartbeat = analytics_storage_find(metric, uuid, AnalyticsClient_Ignore); - if (heartbeat) { - // We allow only a limited number of app heartbeats to accumulate. A NULL means we reached the - // limit - uint64_t val = analytics_heartbeat_get(heartbeat, metric); - analytics_heartbeat_set(heartbeat, metric, val + amount); - } - - analytics_storage_give_lock(); -} - -void analytics_add(AnalyticsMetric metric, int64_t amount, AnalyticsClient client) { - analytics_add_for_uuid(metric, amount, analytics_uuid_for_client(client)); -} - -/////////////////// -// Stopwatches -static bool prv_is_stopwatch_for_metric(ListNode *found_node, void *data) { - AnalyticsStopwatchNode* stopwatch_node = (AnalyticsStopwatchNode*)found_node; - return stopwatch_node->metric == (AnalyticsMetric)data; -} -AnalyticsStopwatchNode *prv_find_stopwatch(AnalyticsMetric metric) { - ListNode *node = list_find(s_stopwatch_list, prv_is_stopwatch_for_metric, (void*)metric); - return (AnalyticsStopwatchNode*)node; -} - -static uint32_t prv_stopwatch_elapsed_ms(AnalyticsStopwatchNode *stopwatch, uint64_t current_ticks) { - const uint64_t dt_ms = ticks_to_milliseconds(current_ticks - stopwatch->starting_ticks); - return (((uint64_t) stopwatch->count_per_sec) * dt_ms) / MS_PER_SECOND; -} - -void analytics_stopwatch_start(AnalyticsMetric metric, AnalyticsClient client) { - analytics_stopwatch_start_at_rate(metric, MS_PER_SECOND, client); -} - -void analytics_stopwatch_start_at_rate(AnalyticsMetric metric, uint32_t count_per_sec, AnalyticsClient client) { - analytics_storage_take_lock(); - - // Stopwatch metrics must be UINT32! - PBL_ASSERTN(analytics_metric_element_type(metric) == ANALYTICS_METRIC_ELEMENT_TYPE_UINT32); - - if (prv_find_stopwatch(metric)) { - // TODO: Increment this back up to LOG_LEVEL_WARNING when it doesn't happen - // on every bootup (PBL-5393) - PBL_LOG(LOG_LEVEL_DEBUG, "Analytics stopwatch for metric %d already started!", metric); - goto unlock; - } - - AnalyticsStopwatchNode *stopwatch = kernel_malloc_check(sizeof(*stopwatch)); - *stopwatch = (AnalyticsStopwatchNode) { - .metric = metric, - .starting_ticks = rtc_get_ticks(), - .count_per_sec = count_per_sec, - .client = client, - }; - - list_init(&stopwatch->node); - s_stopwatch_list = list_prepend(s_stopwatch_list, &stopwatch->node); - -unlock: - analytics_storage_give_lock(); -} - -void analytics_stopwatch_stop(AnalyticsMetric metric) { - analytics_storage_take_lock(); - - AnalyticsStopwatchNode *stopwatch = prv_find_stopwatch(metric); - if (!stopwatch) { - // TODO: Incerement this back up to LOG_LEVEL_WARNING when it doesn't happen - // on every bootup (PBL-5393) - PBL_LOG(LOG_LEVEL_DEBUG, "Analytics stopwatch for metric %d already stopped!", metric); - goto unlock; - } - - analytics_add(metric, prv_stopwatch_elapsed_ms(stopwatch, rtc_get_ticks()), stopwatch->client); - - list_remove(&stopwatch->node, &s_stopwatch_list, NULL); - kernel_free(stopwatch); - -unlock: - analytics_storage_give_lock(); -} - -void analytics_stopwatches_update(uint64_t current_ticks) { - PBL_ASSERTN(analytics_storage_has_lock()); - - ListNode *cur = s_stopwatch_list; - while (cur != NULL) { - AnalyticsStopwatchNode *node = (AnalyticsStopwatchNode*)cur; - analytics_add(node->metric, prv_stopwatch_elapsed_ms(node, current_ticks), node->client); - node->starting_ticks = current_ticks; - cur = cur->next; - } -} diff --git a/src/fw/services/normal/analytics/analytics_data_syscalls.c b/src/fw/services/normal/analytics/analytics_data_syscalls.c deleted file mode 100644 index 3dbde72bef..0000000000 --- a/src/fw/services/normal/analytics/analytics_data_syscalls.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/analytics/analytics_logging.h" -#include "syscall/syscall_internal.h" - -DEFINE_SYSCALL(void, sys_analytics_set, AnalyticsMetric metric, uint64_t value, - AnalyticsClient client) { - analytics_set(metric, value, client); -} - -DEFINE_SYSCALL(void, sys_analytics_set_entire_array, AnalyticsMetric metric, const void *value, - AnalyticsClient client) { - analytics_set_entire_array(metric, value, client); -} - -DEFINE_SYSCALL(void, sys_analytics_add, AnalyticsMetric metric, uint64_t increment, - AnalyticsClient client) { - analytics_add(metric, increment, client); -} - -DEFINE_SYSCALL(void, sys_analytics_inc, AnalyticsMetric metric, AnalyticsClient client) { - analytics_inc(metric, client); -} - -DEFINE_SYSCALL(void, sys_analytics_stopwatch_start, AnalyticsMetric metric, - AnalyticsClient client) { - analytics_stopwatch_start(metric, client); -} - -DEFINE_SYSCALL(void, sys_analytics_stopwatch_stop, AnalyticsMetric metric) { - analytics_stopwatch_stop(metric); -} - -static bool prv_is_event_allowed(const AnalyticsEventBlob *const event_blob) { - switch (event_blob->event) { - case AnalyticsEvent_AppOOMNative: - case AnalyticsEvent_AppOOMRocky: - return true; - - default: - // Don't allow any other event types: - return false; - } -} - -DEFINE_SYSCALL(void, sys_analytics_logging_log_event, AnalyticsEventBlob *event_blob) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(event_blob, sizeof(*event_blob)); - } - if (!prv_is_event_allowed(event_blob)) { - syscall_failed(); - } - analytics_logging_log_event(event_blob); -} - -DEFINE_SYSCALL(void, sys_analytics_max, AnalyticsMetric metric, int64_t val, - AnalyticsClient client) { - analytics_max(metric, val, client); -} diff --git a/src/fw/services/normal/analytics/analytics_event.c b/src/fw/services/normal/analytics/analytics_event.c deleted file mode 100644 index d5ce38e1dd..0000000000 --- a/src/fw/services/normal/analytics/analytics_event.c +++ /dev/null @@ -1,701 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/analytics/analytics_logging.h" -#include "services/common/analytics/analytics_storage.h" - -#include "apps/system_apps/launcher/launcher_app.h" -#include "comm/ble/gap_le_connection.h" -#include "comm/bt_lock.h" -#include "comm/ble/gap_le_connection.h" -#include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session_internal.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/timeline/timeline.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" -#include "util/time/time.h" - -_Static_assert(sizeof(AnalyticsEventBlob) == 36, - "When the blob format or size changes, be sure to bump up ANALYTICS_EVENT_BLOB_VERSION"); - -// ------------------------------------------------------------------------------------------ -// Log an app launch event -static bool prv_send_uuid(AnalyticsEvent event_enum, const Uuid *uuid) { - if (uuid_is_invalid(uuid) || uuid_is_system(uuid)) { - // No need to log apps with invalid uuids. This is typically built-in test apps like "Light - // config" that we don't bother to declare a UUID for - return false; - } - - // FIXME: The sdkshell doesn't have a launcher menu so this causes a linker error. Maybe the - // mapping of events to analytics should also be shell-specific? -#ifndef SHELL_SDK - // No need to log the launcher menu app - if (uuid_equal(uuid, &launcher_menu_app_get_app_info()->uuid)) { - return false; - } -#endif - - return true; -} - -// ------------------------------------------------------------------------------------------ -// Log an out-of-memory situation for an app. - -void analytics_event_app_oom(AnalyticsEvent type, - uint32_t requested_size, uint32_t total_size, uint32_t total_free, - uint32_t largest_free_block) { - PBL_ASSERTN(type == AnalyticsEvent_AppOOMNative || type == AnalyticsEvent_AppOOMRocky); - - AnalyticsEventBlob event_blob = { - .event = type, - .app_oom = { - .requested_size = requested_size, - .total_size = total_size, - .total_free = MIN(total_free, UINT16_MAX), - .largest_free_block = MIN(largest_free_block, UINT16_MAX), - }, - }; - if (!sys_process_manager_get_current_process_uuid(&event_blob.app_oom.app_uuid)) { - // Process has no UUID - return; - } - -#if LOG_DOMAIN_ANALYTICS - ANALYTICS_LOG_DEBUG("app oom: is_rocky=%u, req_sz=%"PRIu32" tot_sz=%"PRIu32" free=%"PRIu32 - " max_free=%"PRIu32, - (type == AnalyticsEvent_AppOOMRocky), - requested_size, total_size, total_free, largest_free_block); -#endif - - sys_analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log a generic app launch event -void analytics_event_app_launch(const Uuid *uuid) { - if (!prv_send_uuid(AnalyticsEvent_AppLaunch, uuid)) { - return; - } - - // Format the event specifc info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_AppLaunch, - .app_launch.uuid = *uuid, - }; - -#if LOG_DOMAIN_ANALYTICS - char uuid_string[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(uuid, uuid_string); - ANALYTICS_LOG_DEBUG("app launch event: uuid %s", uuid_string); -#endif - - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a pin open/create/update event. -static void prv_simple_pin_event(time_t timestamp, const Uuid *parent_id, - AnalyticsEvent event_enum, const char *verb) { - // Format the event specifc info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = event_enum, - .pin_open_create_update.time_utc = timestamp, - .pin_open_create_update.parent_id = *parent_id, - }; - -#if LOG_DOMAIN_ANALYTICS - char uuid_string[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&event_blob.pin_open_create_update.parent_id, uuid_string); - ANALYTICS_LOG_DEBUG("pin %s event: timestamp: %"PRIu32", uuid:%s", verb, - event_blob.pin_open_create_update.time_utc, uuid_string); -#endif - - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a pin open event. -void analytics_event_pin_open(time_t timestamp, const Uuid *parent_id) { - prv_simple_pin_event(timestamp, parent_id, AnalyticsEvent_PinOpen, "open"); -} - - -// ------------------------------------------------------------------------------------------ -// Log a pin created event. -void analytics_event_pin_created(time_t timestamp, const Uuid *parent_id) { - prv_simple_pin_event(timestamp, parent_id, AnalyticsEvent_PinCreated, "created"); -} - - -// ------------------------------------------------------------------------------------------ -// Log a pin updated event. -void analytics_event_pin_updated(time_t timestamp, const Uuid *parent_id) { - prv_simple_pin_event(timestamp, parent_id, AnalyticsEvent_PinUpdated, "updated"); -} - - -// ------------------------------------------------------------------------------------------ -// Log a pin action event. -void analytics_event_pin_action(time_t timestamp, const Uuid *parent_id, - TimelineItemActionType action_type) { - // Format the event specifc info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_PinAction, - .pin_action.time_utc = timestamp, - .pin_action.parent_id = *parent_id, - .pin_action.type = action_type, - }; - -#if LOG_DOMAIN_ANALYTICS - char uuid_string[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&event_blob.pin_action.parent_id, uuid_string); - ANALYTICS_LOG_DEBUG("pin action event: timestamp: %"PRIu32", uuid:%s, action:%"PRIu8, - event_blob.pin_action.time_utc, uuid_string, action_type); -#endif - - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a pin app launch event. -void analytics_event_pin_app_launch(time_t timestamp, const Uuid *parent_id) { - if (!prv_send_uuid(AnalyticsEvent_PinAppLaunch, parent_id)) { - return; - } - - // Format the event specifc info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_PinAppLaunch, - .pin_app_launch.time_utc = timestamp, - .pin_app_launch.parent_id = *parent_id, - }; - -#if LOG_DOMAIN_ANALYTICS - char uuid_string[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&event_blob.pin_app_launch.parent_id, uuid_string); - ANALYTICS_LOG_DEBUG("pin app launch event: timestamp: %"PRIu32", uuid:%s", - event_blob.pin_app_launch.time_utc, uuid_string); -#endif - - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a canned response event -void analytics_event_canned_response(const char *response, bool successfully_sent) { - // Format the event specific info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = successfully_sent ? AnalyticsEvent_CannedReponseSent - : AnalyticsEvent_CannedReponseFailed, - }; - - if (!response) { - event_blob.canned_response.response_size_bytes = 0; - } else { - event_blob.canned_response.response_size_bytes = strlen(response); - } - - if (successfully_sent) { - ANALYTICS_LOG_DEBUG("canned response sent event: response_size_bytes:%d", - event_blob.canned_response.response_size_bytes); - } else { - ANALYTICS_LOG_DEBUG("canned response failed event: response_size_bytes:%d", - event_blob.canned_response.response_size_bytes); - } - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a voice response event -void analytics_event_voice_response(AnalyticsEvent event_type, uint16_t response_size_bytes, - uint16_t response_len_chars, uint32_t response_len_ms, - uint8_t error_count, uint8_t num_sessions, Uuid *app_uuid) { - - PBL_ASSERTN((event_type >= AnalyticsEvent_VoiceTranscriptionAccepted) && - (event_type <= AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted)); - - // Format the event specific info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - - AnalyticsEventBlob event_blob = { - .event = event_type, - }; - - event_blob.voice_response = (AnalyticsEventVoiceResponse) { - .response_size_bytes = response_size_bytes, - .response_len_chars = response_len_chars, - .response_len_ms = response_len_ms, - .num_sessions = num_sessions, - .error_count = error_count, - .app_uuid = *app_uuid, - }; - - const char *msg = "Other"; - switch (event_type) { - case AnalyticsEvent_VoiceTranscriptionAccepted: - msg = "Accepted"; - break; - case AnalyticsEvent_VoiceTranscriptionRejected: - msg = "Rejected"; - break; - case AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted: - msg = "Automatically accepted"; - break; - default: - break; - } - - ANALYTICS_LOG_DEBUG("voice response %s event: size: %"PRIu16"; length (chars): %"PRIu16 - "; length (ms): %"PRIu32"; Errors: %"PRIu8"; Sessions: %"PRIu8, msg, - event_blob.voice_response.response_size_bytes, event_blob.voice_response.response_len_chars, - event_blob.voice_response.response_len_ms, event_blob.voice_response.error_count, - event_blob.voice_response.num_sessions); - // Use syscall because this is called by voice_window - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log a BLE HRM event -void analytics_event_ble_hrm(BleHrmEventSubtype subtype) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_BleHrmEvent, - .ble_hrm = { - .subtype = subtype, - }, - }; - - ANALYTICS_LOG_DEBUG("BLE HRM Event %u", subtype); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log a bluetooth disconnection event -void analytics_event_bt_connection_or_disconnection(AnalyticsEvent type, uint8_t reason) { - AnalyticsEventBlob event_blob = { - .event = type, - }; - - event_blob.bt_connection_disconnection.reason = reason; - - ANALYTICS_LOG_DEBUG("Event %d - BT (dis)connection: Reason: %"PRIu8, - event_blob.event, - event_blob.bt_connection_disconnection.reason); - - analytics_logging_log_event(&event_blob); -} - -void analytics_event_bt_le_disconnection(uint8_t reason, uint8_t remote_bt_version, - uint16_t remote_bt_company_id, - uint16_t remote_bt_subversion) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_BtLeDisconnect, - .ble_disconnection = { - .reason = reason, - .remote_bt_version = remote_bt_version, - .remote_bt_company_id = remote_bt_company_id, - .remote_bt_subversion_number = remote_bt_subversion, - } - }; - - ANALYTICS_LOG_DEBUG("Event %d - BT disconnection: Reason: %"PRIu8, event_blob.event, - event_blob.bt_connection_disconnection.reason); - - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a bluetooth error -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) { - AnalyticsEventBlob event_blob = {}; - event_blob.event = type, - event_blob.bt_error.error_code = error; - - ANALYTICS_LOG_DEBUG("bluetooth event %d - error: %"PRIu32, - event_blob.event, - event_blob.bt_error.error_code); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -//! Log when app_launch trigger failed. -void analytics_event_bt_app_launch_error(uint8_t gatt_error) { - analytics_event_bt_error(AnalyticsEvent_BtAppLaunchError, gatt_error); -} - -// ------------------------------------------------------------------------------------------ -//! Log when a Pebble Protocol session is closed. -void analytics_event_session_close(bool is_system_session, const Uuid *optional_app_uuid, - CommSessionCloseReason reason, uint16_t session_duration_mins) { - AnalyticsEventBlob event_blob = {}; - event_blob.event = (is_system_session ? AnalyticsEvent_PebbleProtocolSystemSessionEnd : - AnalyticsEvent_PebbleProtocolAppSessionEnd); - event_blob.pp_common_session_close.close_reason = reason; - event_blob.pp_common_session_close.duration_minutes = session_duration_mins; - - if (!is_system_session && optional_app_uuid) { - memcpy(&event_blob.pp_app_session_close.app_uuid, optional_app_uuid, sizeof(Uuid)); - } - -#if LOG_DOMAIN_ANALYTICS - char uuid_str[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(optional_app_uuid, uuid_str); - ANALYTICS_LOG_DEBUG("Session close event. is_system_session=%u, uuid=%s, " - "reason=%u, duration_mins=%"PRIu16, - is_system_session, uuid_str, reason, session_duration_mins); -#endif - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -//! Log when the CC2564x BT chip becomes unresponsive -void analytics_event_bt_cc2564x_lockup_error(void) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_BtLockupError, - }; - - ANALYTICS_LOG_DEBUG("CC2564x lockup event"); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log a crash event -void analytics_event_crash(uint8_t crash_code, uint32_t link_register) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_Crash, - .crash_report.crash_code = crash_code, - .crash_report.link_register = link_register - }; - - ANALYTICS_LOG_DEBUG("Crash occured: Code %"PRIu8" / LR: %"PRIu32, - event_blob.crash_report.crash_code, event_blob.crash_report.link_register); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log local bluetooth disconnection reason -void analytics_event_local_bt_disconnect(uint16_t conn_handle, uint32_t lr) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_LocalBtDisconnect, - }; - - event_blob.local_bt_disconnect.lr = lr; - event_blob.local_bt_disconnect.conn_handle = conn_handle; - - ANALYTICS_LOG_DEBUG("Event %d - BT Disconnect: Handle:%"PRIu16" LR: %"PRIu32, - event_blob.event, - conn_handle, - lr); - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log an Apple Media Service event. -void analytics_event_ams(uint8_t type, int32_t aux_info) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_BtLeAMS, - .ams = { - .type = type, - .aux_info = aux_info, - }, - }; - - ANALYTICS_LOG_DEBUG("Event %d - AMS: type:%d aux_info: %"PRId32, event_blob.event, type, aux_info); - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log stationary mode events -void analytics_event_stationary_state_change(time_t timestamp, uint8_t state_change) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_StationaryModeSwitch, - .sd = { - .timestamp = timestamp, - .state_change = state_change, - } - }; - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log a health insight created event. -void analytics_event_health_insight_created(time_t timestamp, - ActivityInsightType insight_type, - PercentTier pct_tier) { - // Format the event specifc info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_HealthInsightCreated, - .health_insight_created = { - .time_utc = timestamp, - .insight_type = insight_type, - .percent_tier = pct_tier, - } - }; - -#if LOG_DOMAIN_ANALYTICS - ANALYTICS_LOG_DEBUG("health insight created event: timestamp: %"PRIu32", type:%"PRIu8, - timestamp, insight_type); -#endif - - analytics_logging_log_event(&event_blob); -} - - -// ------------------------------------------------------------------------------------------ -// Log a health insight response event. -void analytics_event_health_insight_response(time_t timestamp, ActivityInsightType insight_type, - ActivitySessionType activity_type, - ActivityInsightResponseType response_id) { - // Format the event specifc info in the blob. The analytics_logging_log_event() method will fill - // in the common fields - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_HealthInsightResponse, - .health_insight_response = { - .time_utc = timestamp, - .insight_type = insight_type, - .activity_type = activity_type, - .response_id = response_id, - } - }; - -#if LOG_DOMAIN_ANALYTICS - ANALYTICS_LOG_DEBUG("health insight response event: timestamp: %"PRIu32", type:%"PRIu8 \ - ", response:%"PRIu8, timestamp, insight_type, response_id); -#endif - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -// Log an App Crash event -void analytics_event_app_crash(const Uuid *uuid, uint32_t pc, uint32_t lr, - const uint8_t *build_id, bool is_rocky_app) { - AnalyticsEventBlob event_blob = { - .event = (is_rocky_app ? AnalyticsEvent_RockyAppCrash : AnalyticsEvent_AppCrash), - .app_crash_report = { - .uuid = *uuid, - .pc = pc, - .lr = lr, - }, - }; - - if (build_id) { - memcpy(event_blob.app_crash_report.build_id_slice, build_id, - sizeof(event_blob.app_crash_report.build_id_slice)); - } - -#if LOG_DOMAIN_ANALYTICS - char uuid_string[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(uuid, uuid_string); - ANALYTICS_LOG_DEBUG("App Crash event: uuid:%s, pc: %p, lr: %p", - uuid_string, (void *)pc, (void *)lr); -#endif - - analytics_logging_log_event(&event_blob); -} - -extern bool comm_session_is_valid(const CommSession *session); - -// ------------------------------------------------------------------------------------------ -static bool prv_get_connection_details(CommSession *session, bool *is_ppogatt, - uint16_t *conn_interval) { - bt_lock(); - - if (!session || !comm_session_is_valid(session)) { - bt_unlock(); - return false; - } - - const bool tmp_is_ppogatt = - (comm_session_analytics_get_transport_type(session) == CommSessionTransportType_PPoGATT); - - uint16_t tmp_conn_interval = 0; - if (tmp_is_ppogatt) { - GAPLEConnection *conn = gap_le_connection_get_gateway(); - if (conn) { - tmp_conn_interval = conn->conn_params.conn_interval_1_25ms; - } - } - - bt_unlock(); - - if (is_ppogatt) { - *is_ppogatt = tmp_is_ppogatt; - } - if (conn_interval) { - *conn_interval = tmp_conn_interval; - } - return true; -} - -void analytics_event_put_byte_stats( - CommSession *session, bool crc_good, uint8_t type, - uint32_t bytes_transferred, uint32_t elapsed_time_ms, - uint32_t conn_events, uint32_t sync_errors, uint32_t skip_errors, uint32_t other_errors) { - - bool is_ppogatt = false; - uint16_t conn_interval = 0; - if (!prv_get_connection_details(session, &is_ppogatt, &conn_interval)) { - return; - } - - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_PutByteTime, - .pb_time = { - .ppogatt = is_ppogatt, - .conn_intvl_1_25ms = MIN(conn_interval, UINT8_MAX), - .crc_good = crc_good, - .type = type, - .bytes_transferred = bytes_transferred, - .elapsed_time_ms = elapsed_time_ms, - .conn_events = MIN(conn_events, UINT32_MAX), - .sync_errors = MIN(sync_errors, UINT16_MAX), - .skip_errors = MIN(skip_errors, UINT16_MAX), - .other_errors = MIN(other_errors, UINT16_MAX), - }, - }; - - ANALYTICS_LOG_DEBUG("PutBytes event: is_ppogatt: %d, bytes: %d, time ms: %d", - (int)event_blob.pb_time.ppogatt, - (int)event_blob.pb_time.bytes_transferred, - (int)event_blob.pb_time.elapsed_time_ms); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -#if !PLATFORM_TINTIN -void analytics_event_vibe_access(VibePatternFeature vibe_feature, VibeScoreId pattern_id) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_VibeAccess, - . vibe_access_data = { - .feature = (uint8_t) vibe_feature, - .vibe_pattern_id = (uint8_t) pattern_id - } - }; - - analytics_logging_log_event(&event_blob); -} -#endif - -// ------------------------------------------------------------------------------------------ -void analytics_event_alarm(AnalyticsEvent event_type, const AlarmInfo *info) { - AnalyticsEventBlob event_blob = { - .event = event_type, - .alarm = { - .hour = info->hour, - .minute = info->minute, - .is_smart = info->is_smart, - .kind = info->kind, - }, - }; - - memcpy(event_blob.alarm.scheduled_days, info->scheduled_days, - sizeof(event_blob.alarm.scheduled_days)); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -void analytics_event_bt_chip_boot(uint8_t build_id[BUILD_ID_EXPECTED_LEN], - uint32_t crash_lr, uint32_t reboot_reason_code) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_BtChipBoot, - .bt_chip_boot = { - .crash_lr = crash_lr, - .reboot_reason = reboot_reason_code, - }, - }; - - memcpy(event_blob.bt_chip_boot.build_id, build_id, sizeof(BUILD_ID_EXPECTED_LEN)); - - ANALYTICS_LOG_DEBUG("BtChipBoot event: crash_lr: 0x%x, reboot_reason: %"PRIu32, - (int)event_blob.bt_chip_boot.crash_lr, - event_blob.bt_chip_boot.reboot_reason); - - analytics_logging_log_event(&event_blob); -} - -// ------------------------------------------------------------------------------------------ -void analytics_event_PPoGATT_disconnect(time_t timestamp, bool successful_reconnect) { - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_PPoGATTDisconnect, - .ppogatt_disconnect = { - .successful_reconnect = successful_reconnect, - .time_utc = timestamp, - }, - }; - analytics_logging_log_event(&event_blob); -} - - -void analytics_event_get_bytes_stats(CommSession *session, uint8_t type, - uint32_t bytes_transferred, uint32_t elapsed_time_ms, - uint32_t conn_events, uint32_t sync_errors, - uint32_t skip_errors, uint32_t other_errors) { - bool is_ppogatt = false; - uint16_t conn_interval = 0; - if (!prv_get_connection_details(session, &is_ppogatt, &conn_interval)) { - return; - } - - AnalyticsEventBlob event_blob = { - .event = AnalyticsEvent_GetBytesStats, - .get_bytes_stats = { - .ppogatt = is_ppogatt, - .conn_intvl_1_25ms = MIN(conn_interval, UINT8_MAX), - .type = type, - .bytes_transferred = bytes_transferred, - .elapsed_time_ms = elapsed_time_ms, - .conn_events = conn_events, - .sync_errors = MIN(sync_errors, UINT16_MAX), - .skip_errors = MIN(skip_errors, UINT16_MAX), - .other_errors = MIN(other_errors, UINT16_MAX), - }, - }; - - ANALYTICS_LOG_DEBUG("GetBytesStats event: type: 0x%x, num_bytes: %"PRIu32", elapsed_ms: %"PRIu32, - type, bytes_transferred, elapsed_time_ms); - - analytics_logging_log_event(&event_blob); -} diff --git a/src/fw/services/normal/analytics/analytics_external.c b/src/fw/services/normal/analytics/analytics_external.c deleted file mode 100644 index 34c89bd103..0000000000 --- a/src/fw/services/normal/analytics/analytics_external.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bluetooth/analytics.h" -#include "services/common/analytics/analytics_external.h" - -void analytics_external_update(void) { - analytics_external_collect_battery(); - analytics_external_collect_accel_xyz_delta(); - analytics_external_collect_app_cpu_stats(); - analytics_external_collect_app_flash_read_stats(); - analytics_external_collect_cpu_stats(); - analytics_external_collect_stop_inhibitor_stats(rtc_get_ticks()); - analytics_external_collect_chip_specific_parameters(); - analytics_external_collect_bt_pairing_info(); - analytics_external_collect_ble_parameters(); - analytics_external_collect_ble_pairing_info(); - analytics_external_collect_system_flash_statistics(); - analytics_external_collect_backlight_settings(); - analytics_external_collect_notification_settings(); - analytics_external_collect_system_theme_settings(); - analytics_external_collect_ancs_info(); - analytics_external_collect_dls_stats(); - analytics_external_collect_i2c_stats(); - analytics_external_collect_stack_free(); - analytics_external_collect_alerts_preferences(); - analytics_external_collect_timeline_pin_stats(); -#if PLATFORM_SPALDING - analytics_external_collect_display_offset(); -#endif - analytics_external_collect_pfs_stats(); - analytics_external_collect_bt_chip_heartbeat(); - analytics_external_collect_kernel_heap_stats(); - analytics_external_collect_accel_samples_received(); -} diff --git a/src/fw/services/normal/analytics/analytics_heartbeat.c b/src/fw/services/normal/analytics/analytics_heartbeat.c deleted file mode 100644 index 69c0a7e25b..0000000000 --- a/src/fw/services/normal/analytics/analytics_heartbeat.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics_heartbeat.h" -#include "services/common/analytics/analytics_metric.h" -#include "services/common/analytics/analytics_logging.h" - -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/pbl_malloc.h" -#include "util/math.h" - -#include -#include - -uint32_t analytics_heartbeat_kind_data_size(AnalyticsHeartbeatKind kind) { - AnalyticsMetric last = ANALYTICS_METRIC_INVALID; - switch (kind) { - case ANALYTICS_HEARTBEAT_KIND_DEVICE: - last = ANALYTICS_DEVICE_METRIC_END - 1; - break; - case ANALYTICS_HEARTBEAT_KIND_APP: - last = ANALYTICS_APP_METRIC_END - 1; - break; - } - PBL_ASSERTN(last != ANALYTICS_METRIC_INVALID); - return analytics_metric_offset(last) + analytics_metric_size(last); -} - -///////////////////// -// Private -static bool prv_verify_kinds_match(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric) { - AnalyticsMetricKind metric_kind = analytics_metric_kind(metric); - if ((metric_kind == ANALYTICS_METRIC_KIND_DEVICE) && - (heartbeat->kind == ANALYTICS_HEARTBEAT_KIND_DEVICE)) { - return true; - } else if ((metric_kind == ANALYTICS_METRIC_KIND_APP) && - (heartbeat->kind == ANALYTICS_HEARTBEAT_KIND_APP)) { - return true; - } else { - PBL_CROAK("Metric kind does not match heartbeat kind! %d %d", metric_kind, heartbeat->kind); - } -} -static uint8_t *prv_heartbeat_get_location(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric) { - prv_verify_kinds_match(heartbeat, metric); - if (analytics_metric_is_array(metric)) { - PBL_CROAK("Attempt to use integer value for array metric."); - } - return heartbeat->data + analytics_metric_offset(metric); -} -static uint8_t *prv_heartbeat_get_array_location(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, - uint32_t index) { - prv_verify_kinds_match(heartbeat, metric); - if (!analytics_metric_is_array(metric)) { - PBL_CROAK("Attempt to use array value for integer metric."); - } - uint32_t len = analytics_metric_num_elements(metric); - uint32_t element_size = analytics_metric_element_size(metric); - if (index > len) { - PBL_CROAK("Attempt to use array value at invalid index %" PRId32 " (len %" PRId32 ")", - index, len); - } - return heartbeat->data + analytics_metric_offset(metric) + index*element_size; -} - -static void prv_location_set_value(uint8_t *location, int64_t val, AnalyticsMetricElementType type) { - switch (type) { - case ANALYTICS_METRIC_ELEMENT_TYPE_NIL: - WTF; - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT8: - { - *((uint8_t*)location) = (uint8_t)CLIP(val, 0, UINT8_MAX); - return; - } - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT16: - { - *((uint16_t*)location) = (uint16_t)CLIP(val, 0, UINT16_MAX); - return; - } - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT32: - { - *((uint32_t*)location) = (uint32_t)CLIP(val, 0, UINT32_MAX); - return; - } - case ANALYTICS_METRIC_ELEMENT_TYPE_INT8: - { - *((int8_t*)location) = (int8_t)CLIP(val, INT8_MIN, INT8_MAX); - return; - } - case ANALYTICS_METRIC_ELEMENT_TYPE_INT16: - { - *((int16_t*)location) = (int16_t)CLIP(val, INT16_MIN, INT16_MAX); - return; - } - case ANALYTICS_METRIC_ELEMENT_TYPE_INT32: - { - *((int32_t*)location) = (int32_t)CLIP(val, INT32_MIN, INT32_MAX); - return; - } - } - WTF; // Should not get here! -} -static int64_t prv_location_get_value(uint8_t *location, AnalyticsMetricElementType type) { - switch (type) { - case ANALYTICS_METRIC_ELEMENT_TYPE_NIL: - WTF; - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT8: - return *(uint8_t*)location; - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT16: - return *(uint16_t*)location; - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT32: - return *(uint32_t*)location; - case ANALYTICS_METRIC_ELEMENT_TYPE_INT8: - return *(int8_t*)location; - case ANALYTICS_METRIC_ELEMENT_TYPE_INT16: - return *(int16_t*)location; - case ANALYTICS_METRIC_ELEMENT_TYPE_INT32: - return *(int32_t*)location; - } - WTF; // Should not get here! -} - -////////// -// Set -void analytics_heartbeat_set(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, int64_t val) { - uint8_t *location = prv_heartbeat_get_location(heartbeat, metric); - prv_location_set_value(location, val, analytics_metric_element_type(metric)); -} - -void analytics_heartbeat_set_array(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, uint32_t index, int64_t val) { - uint8_t *location = prv_heartbeat_get_array_location(heartbeat, metric, index); - prv_location_set_value(location, val, analytics_metric_element_type(metric)); -} - -void analytics_heartbeat_set_entire_array(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, const void* data) { - uint8_t *location = prv_heartbeat_get_array_location(heartbeat, metric, 0); - uint32_t size = analytics_metric_size(metric); - memcpy(location, data, size); -} - -///////// -// Get -int64_t analytics_heartbeat_get(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric) { - uint8_t *location = prv_heartbeat_get_location(heartbeat, metric); - return prv_location_get_value(location, analytics_metric_element_type(metric)); -} - -int64_t analytics_heartbeat_get_array(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, uint32_t index) { - uint8_t *location = prv_heartbeat_get_array_location(heartbeat, metric, index); - return prv_location_get_value(location, analytics_metric_element_type(metric)); -} - -const Uuid *analytics_heartbeat_get_uuid(AnalyticsHeartbeat *heartbeat) { - return (const Uuid*)prv_heartbeat_get_array_location(heartbeat, ANALYTICS_APP_METRIC_UUID, 0); -} - -/////////////////// -// Create / Clear -AnalyticsHeartbeat *analytics_heartbeat_create(AnalyticsHeartbeatKind kind) { - uint32_t size = sizeof(AnalyticsHeartbeat) + analytics_heartbeat_kind_data_size(kind); - AnalyticsHeartbeat *heartbeat = kernel_malloc_check(size); - heartbeat->kind = kind; - analytics_heartbeat_clear(heartbeat); - return heartbeat; -} - -AnalyticsHeartbeat *analytics_heartbeat_device_create() { - AnalyticsHeartbeat *hb = analytics_heartbeat_create(ANALYTICS_HEARTBEAT_KIND_DEVICE); - analytics_heartbeat_set(hb, ANALYTICS_DEVICE_METRIC_BLOB_KIND, - ANALYTICS_BLOB_KIND_DEVICE_HEARTBEAT); - analytics_heartbeat_set(hb, ANALYTICS_DEVICE_METRIC_BLOB_VERSION, - ANALYTICS_DEVICE_HEARTBEAT_BLOB_VERSION); - return hb; -} - -AnalyticsHeartbeat *analytics_heartbeat_app_create(const Uuid *uuid) { - AnalyticsHeartbeat *hb = analytics_heartbeat_create(ANALYTICS_HEARTBEAT_KIND_APP); - analytics_heartbeat_set_entire_array(hb, ANALYTICS_APP_METRIC_UUID, uuid); - analytics_heartbeat_set(hb, ANALYTICS_APP_METRIC_BLOB_KIND, - ANALYTICS_BLOB_KIND_APP_HEARTBEAT); - analytics_heartbeat_set(hb, ANALYTICS_APP_METRIC_BLOB_VERSION, - ANALYTICS_APP_HEARTBEAT_BLOB_VERSION); - return hb; -} - -void analytics_heartbeat_clear(AnalyticsHeartbeat *heartbeat) { - AnalyticsHeartbeatKind kind = heartbeat->kind; - uint32_t size = sizeof(AnalyticsHeartbeat) + analytics_heartbeat_kind_data_size(kind); - memset(heartbeat, 0, size); - heartbeat->kind = kind; -} - -////////////////// -// Debug -#ifdef ANALYTICS_DEBUG -// Function to get the name of a macro given it's runtime value. (i.e. mapping -// (1: "ANALYTICS_DEVICE_METRIC_MSG_ID"), -// (2: "ANALYTICS_DEVICE_METRIC_VERSION"), -// ... -// ) -#define CASE(name, ...) case name: return #name; -static const char *prv_get_metric_name(AnalyticsMetric metric) { - switch (metric) { - ANALYTICS_METRIC_TABLE(CASE,CASE,CASE,,,,,,) - default: return ""; - } -} -#undef CASE - -static void prv_print_heartbeat(AnalyticsHeartbeat *heartbeat, AnalyticsMetric start, AnalyticsMetric end) { - for (AnalyticsMetric metric = start + 1; metric < end; metric++) { - const char *name = prv_get_metric_name(metric); - if (!analytics_metric_is_array(metric)) { - int64_t val = analytics_heartbeat_get(heartbeat, metric); - if (val >= 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "%3" PRIu32 ": %s: %" PRIu32 " (0x%" PRIx32")", - analytics_metric_offset(metric), name, (uint32_t)val, (uint32_t)val); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "%3" PRIu32 ": %s: %" PRId32 " (0x%" PRIx32")", - analytics_metric_offset(metric), name, (int32_t)val, (int32_t)val); - } - continue; - } - const size_t BUF_LENGTH = 256; - char buf[BUF_LENGTH]; - uint32_t written = 0; - for (uint32_t i = 0; i < analytics_metric_num_elements(metric); i++) { - if (written > BUF_LENGTH) { - PBL_LOG(LOG_LEVEL_DEBUG, "print buffer overflow by %lu bytes", - BUF_LENGTH - written); - continue; - } - int64_t val = analytics_heartbeat_get_array(heartbeat, metric, i); - const char *sep = (i == 0 ? "" : ", "); - if (val >= 0) { - written += snprintf(buf + written, BUF_LENGTH - written, - "%s%" PRIu32 " (0x%" PRIx32 ")", sep, (uint32_t)val, (uint32_t)val); - } else { - written += snprintf(buf + written, BUF_LENGTH - written, - "%s%" PRId32 " (0x%" PRIx32 ")", sep, (int32_t)val, (int32_t)val); - } - } - PBL_LOG(LOG_LEVEL_DEBUG, "%3" PRIu32 ": %s: %s", analytics_metric_offset(metric), name, buf); - } -} - -void analytics_heartbeat_print(AnalyticsHeartbeat *heartbeat) { - switch (heartbeat->kind) { - case ANALYTICS_HEARTBEAT_KIND_DEVICE: - PBL_LOG(LOG_LEVEL_DEBUG, "Device heartbeat:"); - prv_print_heartbeat(heartbeat, ANALYTICS_DEVICE_METRIC_START, ANALYTICS_DEVICE_METRIC_END); - break; - case ANALYTICS_HEARTBEAT_KIND_APP: { - const Uuid *uuid = analytics_heartbeat_get_uuid(heartbeat); - char uuid_buf[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(uuid, uuid_buf); - PBL_LOG(LOG_LEVEL_DEBUG, "App heartbeat for %s:", uuid_buf); - prv_print_heartbeat(heartbeat, ANALYTICS_APP_METRIC_START, ANALYTICS_APP_METRIC_END); - break; - } - default: - PBL_LOG(LOG_LEVEL_DEBUG, "Unable to print heartbeat: Unrecognized kind %d", heartbeat->kind); - } -} -#else -void analytics_heartbeat_print(AnalyticsHeartbeat *heartbeat) { - PBL_LOG(LOG_LEVEL_DEBUG, "Turn on ANALYTICS_DEBUG to get heartbeat printing support."); -} -#endif diff --git a/src/fw/services/normal/analytics/analytics_logging.c b/src/fw/services/normal/analytics/analytics_logging.c deleted file mode 100644 index 6dec7f9a35..0000000000 --- a/src/fw/services/normal/analytics/analytics_logging.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "applib/data_logging.h" -#include "comm/bt_lock.h" -#include "drivers/rtc.h" - -#include "os/tick.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" - -#include "services/normal/data_logging/data_logging_service.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" - -#include "services/common/analytics/analytics_event.h" -#include "services/common/analytics/analytics_external.h" -#include "services/common/analytics/analytics_heartbeat.h" -#include "services/common/analytics/analytics_logging.h" -#include "services/common/analytics/analytics_metric.h" -#include "services/common/analytics/analytics_storage.h" -#include "services/common/system_task.h" - -#include "system/logging.h" -#include "system/passert.h" - -enum { -#ifdef ANALYTICS_DEBUG - HEARTBEAT_INTERVAL = 10 * 1000, // 10 seconds -#else - HEARTBEAT_INTERVAL = 60 * 60 * 1000, // 1 hour -#endif -}; - -static int s_heartbeat_timer; -static uint32_t s_previous_send_ticks; - -DataLoggingSessionRef s_device_heartbeat_session = NULL; -DataLoggingSessionRef s_app_heartbeat_session = NULL; -DataLoggingSessionRef s_event_session = NULL; - -static void prv_schedule_retry(); -static void prv_create_event_session_cb(void *ignored); - -static void prv_reset_local_session_ptrs(void) { - s_device_heartbeat_session = NULL; - s_app_heartbeat_session = NULL; - s_event_session = NULL; -} - -static void prv_timer_callback(void *data) { - if (!dls_initialized()) { - // We need to wait until data logging is initialized before we can log heartbeats - prv_schedule_retry(); - return; - } - if (!s_event_session) { - // If the event session has not been created yet, create that. The only time we may have to do - // this here is if the first call to prv_create_event_session_cb() during boot failed to create - // the session. - launcher_task_add_callback(prv_create_event_session_cb, NULL); - } - system_task_add_callback(analytics_logging_system_task_cb, NULL); - new_timer_start(s_heartbeat_timer, HEARTBEAT_INTERVAL, prv_timer_callback, NULL, 0); -} - -static void prv_schedule_retry() { - new_timer_start(s_heartbeat_timer, 5000, prv_timer_callback, NULL, 0); -} - -static DataLoggingSessionRef prv_create_dls(AnalyticsBlobKind kind, uint32_t item_length) { - Uuid system_uuid = UUID_SYSTEM; - bool buffered = false; - const char *kind_str; - uint32_t tag; - - if (kind == ANALYTICS_BLOB_KIND_DEVICE_HEARTBEAT) { - kind_str = "Device"; - tag = DlsSystemTagAnalyticsDeviceHeartbeat; - } else if (kind == ANALYTICS_BLOB_KIND_APP_HEARTBEAT) { - kind_str = "App"; - tag = DlsSystemTagAnalyticsAppHeartbeat; - } else if (kind == ANALYTICS_BLOB_KIND_EVENT) { - kind_str = "Event"; - buffered = true; - tag = DlsSystemTagAnalyticsEvent; - } else { - WTF; - } - - // TODO: Use different tag ids for device_hb and app_hb sessions. - // https://pebbletechnology.atlassian.net/browse/PBL-5463 - const bool resume = false; - DataLoggingSessionRef dls_session = dls_create( - tag, DATA_LOGGING_BYTE_ARRAY, item_length, buffered, resume, &system_uuid); - - PBL_LOG(LOG_LEVEL_DEBUG, "%s HB Session: %p", kind_str, dls_session); - if (!dls_session) { - // Data logging full at boot. Reset it and try again 5s later - PBL_LOG(LOG_LEVEL_WARNING, "Data logging full at boot. Clearing..."); - // We reset all data logging here, including data logging for applications, - // because an inability to allocate a new session means all 200+ session - // IDs are exhausted, likely caused by a misbehaving app_hb. See discussion at: - // https://github.com/pebble/tintin/pull/1967#discussion-diff-11746345 - // And issue about removing/moving this at - // https://pebbletechnology.atlassian.net/browse/PBL-5473 - prv_reset_local_session_ptrs(); - dls_clear(); - prv_schedule_retry(); - return NULL; - } - return dls_session; -} - -static void prv_dls_log(AnalyticsHeartbeat *device_hb, AnalyticsHeartbeatList *app_hbs) { - dls_log(s_device_heartbeat_session, device_hb->data, 1); -#ifdef ANALYTICS_DEBUG - analytics_heartbeat_print(device_hb); -#endif - kernel_free(device_hb); - - AnalyticsHeartbeatList *app_hb_node = app_hbs; - while (app_hb_node) { - AnalyticsHeartbeat *app_hb = app_hb_node->heartbeat; -#ifdef ANALYTICS_DEBUG - analytics_heartbeat_print(app_hb); -#endif - dls_log(s_app_heartbeat_session, app_hb->data, 1); - - AnalyticsHeartbeatList *next = (AnalyticsHeartbeatList*)app_hb_node->node.next; - kernel_free(app_hb); - kernel_free(app_hb_node); - app_hb_node = next; - } -} - -// System task callback used to prepare and log the heartbeats using dls_log(). -void analytics_logging_system_task_cb(void *ignored) { - if (!s_device_heartbeat_session) { - uint32_t size = analytics_heartbeat_kind_data_size(ANALYTICS_HEARTBEAT_KIND_DEVICE); - s_device_heartbeat_session = prv_create_dls(ANALYTICS_BLOB_KIND_DEVICE_HEARTBEAT, size); - if (!s_device_heartbeat_session) return; - } - - // Tell the watchdog timer that we are still awake. dls_create() could take up to 4 seconds if - // we can't get the ispp send buffer - system_task_watchdog_feed(); - - if (!s_app_heartbeat_session) { - uint32_t size = analytics_heartbeat_kind_data_size(ANALYTICS_HEARTBEAT_KIND_APP); - s_app_heartbeat_session = prv_create_dls(ANALYTICS_BLOB_KIND_APP_HEARTBEAT, size); - if (!s_app_heartbeat_session) return; - } - - // Tell the watchdog timer that we are still awake. dls_create() could take up to 4 seconds if - // we can't get the ispp send buffer - system_task_watchdog_feed(); - - analytics_external_update(); - - // Tell the watchdog timer that we are still awake. Occasionally, analytics_external_update() - // could take a while to execute if it needs to wait for the bt_lock() for example - system_task_watchdog_feed(); - - // The phone and proxy server expect us to send local time. The phone will imbed the time zone - // offset into the blob and the proxy server will then use that to convert to UTC before it - // gets placed into the database - uint32_t timestamp = time_utc_to_local(rtc_get_time()); - uint64_t current_ticks = rtc_get_ticks(); - - AnalyticsHeartbeat *device_hb = NULL; - AnalyticsHeartbeatList *app_hbs = NULL; - - { - analytics_storage_take_lock(); - - extern void analytics_stopwatches_update(uint64_t current_ticks); - analytics_stopwatches_update(current_ticks); - - // Hijack the device_hb and app_hb heartbeats from analytics_storage. - // After this point, we own the memory, so analytics_storage will not - // modify it anymore. Thus, we do not need to hold the lock while - // logging. - device_hb = analytics_storage_hijack_device_heartbeat(); - app_hbs = analytics_storage_hijack_app_heartbeats(); - - analytics_storage_give_lock(); - } - - uint32_t dt_ticks = current_ticks - s_previous_send_ticks; - uint32_t dt_ms = ticks_to_milliseconds(dt_ticks); - s_previous_send_ticks = current_ticks; - - analytics_heartbeat_set(device_hb, ANALYTICS_DEVICE_METRIC_TIMESTAMP, timestamp); - analytics_heartbeat_set(device_hb, ANALYTICS_DEVICE_METRIC_DEVICE_UP_TIME, current_ticks); - analytics_heartbeat_set(device_hb, ANALYTICS_DEVICE_METRIC_TIME_INTERVAL, dt_ms); - - AnalyticsHeartbeatList *app_hb_node = app_hbs; - while (app_hb_node) { - AnalyticsHeartbeat *app_hb = app_hb_node->heartbeat; - analytics_heartbeat_set(app_hb, ANALYTICS_APP_METRIC_TIMESTAMP, timestamp); - analytics_heartbeat_set(app_hb, ANALYTICS_APP_METRIC_TIME_INTERVAL, dt_ms); - app_hb_node = (AnalyticsHeartbeatList*)app_hb_node->node.next; - } - - prv_dls_log(device_hb, app_hbs); -} - - -// Launcher task callback used to create our event logging session. -static void prv_create_event_session_cb(void *ignored) { - if (!s_event_session) { - s_event_session = prv_create_dls(ANALYTICS_BLOB_KIND_EVENT, sizeof(AnalyticsEventBlob)); - // If the above call fails, it will schedule our timer to try again in a few seconds - } -} - -static void prv_handle_log_event(AnalyticsEventBlob *event_blob) { - if (!s_event_session) { - PBL_LOG(LOG_LEVEL_INFO, "Event dropped because session not created yet"); - return; - } - - // Log it - dls_log(s_event_session, event_blob, 1); -} - -static void prv_handle_async_event_logging(void *data) { - AnalyticsEventBlob *event_blob = (AnalyticsEventBlob *)data; - prv_handle_log_event(event_blob); - kernel_free(event_blob); -} - -void analytics_logging_log_event(AnalyticsEventBlob *event_blob) { - // Fill in the meta info - event_blob->kind = ANALYTICS_BLOB_KIND_EVENT; - event_blob->version = ANALYTICS_EVENT_BLOB_VERSION; - event_blob->timestamp = time_utc_to_local(rtc_get_time()); - - // TODO: We should be able to remove this once PBL-23925 is fixed - if (bt_lock_is_held()) { - // We run the risk of deadlocking if we hold the bt_lock at this point. If - // it's the case then schedule a callback so the dls code runs while we no - // longer hold the lock - AnalyticsEventBlob *event_blob_copy = - kernel_malloc_check(sizeof(AnalyticsEventBlob)); - memcpy(event_blob_copy, event_blob, sizeof(*event_blob_copy)); - system_task_add_callback(prv_handle_async_event_logging, event_blob_copy); - } else { - prv_handle_log_event(event_blob); - } -} - -void analytics_logging_init(void) { - s_heartbeat_timer = new_timer_create(); - s_previous_send_ticks = rtc_get_ticks(); - new_timer_start(s_heartbeat_timer, HEARTBEAT_INTERVAL, prv_timer_callback, NULL, 0); - - // Create the event session on a launcher task callback because we have to wait for - // services (like data logging service) to be initialized) - launcher_task_add_callback(prv_create_event_session_cb, NULL); -} diff --git a/src/fw/services/normal/analytics/analytics_metric.c b/src/fw/services/normal/analytics/analytics_metric.c deleted file mode 100644 index 969938d90a..0000000000 --- a/src/fw/services/normal/analytics/analytics_metric.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics_metric.h" - -#include "system/passert.h" -#include "util/size.h" - -typedef struct { - AnalyticsMetricElementType element_type; - uint8_t num_elements; -} AnalyticsMetricDataType; - -// http://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments -#define GET_MACRO(_1, _2, _3, NAME, ...) NAME - -#define ENTRY3(name, element_type, num_elements) {element_type, num_elements}, -#define ENTRY2(name, element_type) {element_type, 1}, -#define ENTRY1(name) {ANALYTICS_METRIC_ELEMENT_TYPE_NIL, 0}, -#define ENTRY(...) GET_MACRO(__VA_ARGS__, ENTRY3, ENTRY2, ENTRY1)(__VA_ARGS__) - -// Mapping from type index to data type of metric. We waste some space here -// by including the marker metrics, but it makes the code a fair bit simpler -// since we don't need an index translation table. -static const AnalyticsMetricDataType s_heartbeat_template[] = { - ANALYTICS_METRIC_TABLE(ENTRY, ENTRY, ENTRY, - ANALYTICS_METRIC_ELEMENT_TYPE_UINT8, - ANALYTICS_METRIC_ELEMENT_TYPE_UINT16, - ANALYTICS_METRIC_ELEMENT_TYPE_UINT32, - ANALYTICS_METRIC_ELEMENT_TYPE_INT8, - ANALYTICS_METRIC_ELEMENT_TYPE_INT16, - ANALYTICS_METRIC_ELEMENT_TYPE_INT32) -}; - -#define NUM_METRICS ARRAY_LENGTH(s_heartbeat_template) - -static const AnalyticsMetricDataType *prv_get_metric_data_type(AnalyticsMetric metric) { - PBL_ASSERTN(analytics_metric_kind(metric) != ANALYTICS_METRIC_KIND_UNKNOWN); - return &s_heartbeat_template[metric]; -} - -AnalyticsMetricElementType analytics_metric_element_type(AnalyticsMetric metric) { - return prv_get_metric_data_type(metric)->element_type; -} - -uint32_t analytics_metric_num_elements(AnalyticsMetric metric) { - return prv_get_metric_data_type(metric)->num_elements; -} - -uint32_t analytics_metric_element_size(AnalyticsMetric metric) { - const AnalyticsMetricDataType *type = prv_get_metric_data_type(metric); - switch (type->element_type) { - case ANALYTICS_METRIC_ELEMENT_TYPE_NIL: - return 0; - case ANALYTICS_METRIC_ELEMENT_TYPE_INT8: - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT8: - return 1; - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT16: - case ANALYTICS_METRIC_ELEMENT_TYPE_INT16: - return 2; - case ANALYTICS_METRIC_ELEMENT_TYPE_UINT32: - case ANALYTICS_METRIC_ELEMENT_TYPE_INT32: - return 4; - } - PBL_CROAK("no such element_type %d", type->element_type); -} - -uint32_t analytics_metric_size(AnalyticsMetric metric) { - uint32_t num_elements = analytics_metric_num_elements(metric); - uint32_t element_size = analytics_metric_element_size(metric); - return num_elements * element_size; -} - -bool analytics_metric_is_array(AnalyticsMetric metric) { - const AnalyticsMetricDataType *type = prv_get_metric_data_type(metric); - return (type->num_elements > 1); -} - -bool analytics_metric_is_unsigned(AnalyticsMetric metric) { - const AnalyticsMetricDataType *type = prv_get_metric_data_type(metric); - return (type->element_type == ANALYTICS_METRIC_ELEMENT_TYPE_UINT32 || - type->element_type == ANALYTICS_METRIC_ELEMENT_TYPE_UINT16 || - type->element_type == ANALYTICS_METRIC_ELEMENT_TYPE_UINT8); -} - -static uint16_t s_metric_heartbeat_offset[NUM_METRICS]; - -void analytics_metric_init(void) { - uint32_t device_offset = 0; - uint32_t app_offset = 0; - const uint16_t INVALID_OFFSET = ~0; - for (AnalyticsMetric metric = ANALYTICS_METRIC_START; - metric < ANALYTICS_METRIC_END; metric++) { - switch (analytics_metric_kind(metric)) { - case ANALYTICS_METRIC_KIND_DEVICE: - s_metric_heartbeat_offset[metric] = device_offset; - device_offset += analytics_metric_size(metric); - PBL_ASSERTN(device_offset < INVALID_OFFSET); - break; - case ANALYTICS_METRIC_KIND_APP: - s_metric_heartbeat_offset[metric] = app_offset; - app_offset += analytics_metric_size(metric); - PBL_ASSERTN(app_offset < INVALID_OFFSET); - break; - case ANALYTICS_METRIC_KIND_MARKER: - // Marker metrics do not actually exist in either heartbeat, they are - // markers only. - s_metric_heartbeat_offset[metric] = INVALID_OFFSET; - break; - case ANALYTICS_METRIC_KIND_UNKNOWN: - WTF; - } - } -} - -uint32_t analytics_metric_offset(AnalyticsMetric metric) { - AnalyticsMetricKind kind = analytics_metric_kind(metric); - PBL_ASSERTN((kind == ANALYTICS_METRIC_KIND_DEVICE) || (kind == ANALYTICS_METRIC_KIND_APP)); - return s_metric_heartbeat_offset[metric]; -} - -AnalyticsMetricKind analytics_metric_kind(AnalyticsMetric metric) { - if ((metric > ANALYTICS_DEVICE_METRIC_START) - && (metric < ANALYTICS_DEVICE_METRIC_END)) { - return ANALYTICS_METRIC_KIND_DEVICE; - } else if ((metric > ANALYTICS_APP_METRIC_START) - && (metric < ANALYTICS_APP_METRIC_END)) { - return ANALYTICS_METRIC_KIND_APP; - } else if ((metric >= ANALYTICS_METRIC_START) - && (metric <= ANALYTICS_METRIC_END)) { - // "Marker" metrics are not actual real metrics, they are only used - // to easily find the position of other metrics. (i.e. ANALYTICS_METRIC_START - // is a "marker" metric). - return ANALYTICS_METRIC_KIND_MARKER; - } else { - return ANALYTICS_METRIC_KIND_UNKNOWN; - } -} diff --git a/src/fw/services/normal/analytics/analytics_storage.c b/src/fw/services/normal/analytics/analytics_storage.c deleted file mode 100644 index c500464c48..0000000000 --- a/src/fw/services/normal/analytics/analytics_storage.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "os/mutex.h" -#include "kernel/pbl_malloc.h" -#include "process_management/app_manager.h" -#include "process_management/worker_manager.h" - -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_metric.h" -#include "services/common/analytics/analytics_storage.h" -#include "system/logging.h" -#include "system/passert.h" - -static PebbleRecursiveMutex *s_analytics_storage_mutex = NULL; - -static AnalyticsHeartbeat *s_device_heartbeat = NULL; -static AnalyticsHeartbeatList *s_app_heartbeat_list = NULL; - -#define MAX_APP_HEARTBEATS 8 - -void analytics_storage_init(void) { - s_analytics_storage_mutex = mutex_create_recursive(); - PBL_ASSERTN(s_analytics_storage_mutex); - s_device_heartbeat = analytics_heartbeat_device_create(); - PBL_ASSERTN(s_device_heartbeat); -} - -////////// -// Lock -void analytics_storage_take_lock(void) { - mutex_lock_recursive(s_analytics_storage_mutex); -} - -bool analytics_storage_has_lock(void) { - bool has_lock = mutex_is_owned_recursive(s_analytics_storage_mutex); - if (!has_lock) { - PBL_LOG(LOG_LEVEL_ERROR, "Analytics lock is not held when it should be!"); - } - return has_lock; -} - -void analytics_storage_give_lock(void) { - mutex_unlock_recursive(s_analytics_storage_mutex); -} - -/////// -// Get -AnalyticsHeartbeat *analytics_storage_hijack_device_heartbeat() { - PBL_ASSERTN(analytics_storage_has_lock()); - - AnalyticsHeartbeat *device = s_device_heartbeat; - - s_device_heartbeat = analytics_heartbeat_device_create(); - PBL_ASSERTN(s_device_heartbeat); - - return device; -} - -AnalyticsHeartbeatList *analytics_storage_hijack_app_heartbeats() { - PBL_ASSERTN(analytics_storage_has_lock()); - - AnalyticsHeartbeatList *apps = s_app_heartbeat_list; - s_app_heartbeat_list = NULL; - return apps; -} - -/////////// -// Search -static bool prv_is_app_node_with_uuid(ListNode *found_node, void *data) { - const Uuid *searching_for_uuid = (const Uuid*)data; - AnalyticsHeartbeatList *app_node = (AnalyticsHeartbeatList*)found_node; - const Uuid *found_uuid = analytics_heartbeat_get_uuid(app_node->heartbeat); - return uuid_equal(searching_for_uuid, found_uuid); -} - -static AnalyticsHeartbeatList *prv_app_node_create(const Uuid *uuid) { - AnalyticsHeartbeatList *app_heartbeat_node = kernel_malloc_check(sizeof(AnalyticsHeartbeatList)); - - list_init(&app_heartbeat_node->node); - app_heartbeat_node->heartbeat = analytics_heartbeat_app_create(uuid); - - return app_heartbeat_node; -} - -const Uuid *analytics_uuid_for_client(AnalyticsClient client) { - const PebbleProcessMd *md; - if (client == AnalyticsClient_CurrentTask) { - PebbleTask task = pebble_task_get_current(); - if (task == PebbleTask_App) { - client = AnalyticsClient_App; - } else if (task == PebbleTask_Worker) { - client = AnalyticsClient_Worker; - } else { - return NULL; // System UUID - } - } - - if (client == AnalyticsClient_App) { - md = app_manager_get_current_app_md(); - } else if (client == AnalyticsClient_Worker) { - md = worker_manager_get_current_worker_md(); - } else if (client == AnalyticsClient_System) { - return NULL; - } else if (client == AnalyticsClient_Ignore) { - return NULL; - } else { - WTF; - } - - if (md != NULL) { - return &md->uuid; - } else { - return NULL; - } -} - - -AnalyticsHeartbeat *analytics_storage_find(AnalyticsMetric metric, const Uuid *uuid, - AnalyticsClient client) { - PBL_ASSERTN(analytics_storage_has_lock()); - - switch (analytics_metric_kind(metric)) { - case ANALYTICS_METRIC_KIND_DEVICE: - PBL_ASSERTN(client == AnalyticsClient_Ignore || client == AnalyticsClient_System); - return s_device_heartbeat; - case ANALYTICS_METRIC_KIND_APP: { - PBL_ASSERTN(client == AnalyticsClient_Ignore || client != AnalyticsClient_System); - const Uuid uuid_system = UUID_SYSTEM; - if (!uuid) { - uuid = analytics_uuid_for_client(client); - if (!uuid) { - // There is a brief period of time where no app is running, which we - // attribute to the system UUID. For now, this lets us track how - // much time we are missing, although we probably want to try and - // tighten this up as much as possible going forward. - uuid = &uuid_system; - } - } - ListNode *node = list_find((ListNode*)s_app_heartbeat_list, - prv_is_app_node_with_uuid, (void*)uuid); - AnalyticsHeartbeatList *app_node = (AnalyticsHeartbeatList*)node; - if (!app_node) { - if (list_count((ListNode *)s_app_heartbeat_list) >= MAX_APP_HEARTBEATS) { - ANALYTICS_LOG_DEBUG("No more app heartbeat sessions available"); - return NULL; - } - app_node = prv_app_node_create(uuid); - s_app_heartbeat_list = (AnalyticsHeartbeatList*)list_prepend( - (ListNode*)s_app_heartbeat_list, &app_node->node); - } - return app_node->heartbeat; - } - default: - WTF; - } -} diff --git a/src/fw/services/normal/app_cache.c b/src/fw/services/normal/app_cache.c deleted file mode 100644 index 3fad31bd60..0000000000 --- a/src/fw/services/normal/app_cache.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_cache.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_storage.h" -#include "services/common/system_task.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/filesystem/app_file.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/settings/settings_file.h" -#include "shell/normal/quick_launch.h" -#include "shell/normal/watchface.h" -#include "shell/prefs.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/list.h" -#include "util/math.h" -#include "util/time/time.h" -#include "util/units.h" - -//! @file app_cache.c -//! App Cache - -//! The App Cache keeps track of the install date, last launch, launch count, and size of an -//! application. -//! -//! A priority can also be calculated for each entry. It is calculated by a simple last used -//! algorithm (TODO Improve: PBL-13209) which will help determine which application -//! needs to be evicted in order to free up more space for other application binaries. -//! -//! When an entry is added into the app cache, it means the binaries now reside on the watch. On -//! this function call, a callback is initiated to check if we need to free space for a possible -//! future application. If so, the applications with the lowest priority that add up to or are -//! greater than the space needed will be removed. -//! -//! It is assumed that there will ALWAYS be space for a single application of maximum size based -//! on the platform. The only time when this isn't true is the time between "add_entry" and the -//! callback to clean up the cache. - -#define APP_CACHE_FILE_NAME "appcache" - -//! each cache entry is ~16 bytes, 4000 / 16 = 250 apps -#define APP_CACHE_MAX_SIZE 4000 - -//! Keep enough room for the maximum sized application based on platform, plus a little more room. -//! Source: https://pebbletechnology.atlassian.net/wiki/display/DEV/PBW+3.0 -#if PLATFORM_TINTIN || PLATFORM_SILK || PLATFORM_ASTERIX || UNITTEST -#define APP_SPACE_BUFFER KiBYTES(300) -#else -#define APP_SPACE_BUFFER MiBYTES(4) -#endif - -#define MAX_PRIORITY ((uint32_t)~0) - -// 4 quick launch apps, 1 default watchface, 1 default worker -#define DO_NOT_EVICT_LIST_SIZE (NUM_BUTTONS + 2) - -static PebbleRecursiveMutex *s_app_cache_mutex = NULL; - -//! Actual data structure stored in flash about an app cache entry -typedef struct PACKED { - time_t install_date; - time_t last_launch; - uint32_t total_size; - uint16_t launch_count; -} AppCacheEntry; - -typedef struct { - ListNode node; - AppInstallId id; - uint32_t size; - uint32_t priority; -} EvictListNode; - -typedef struct { - EvictListNode *list; - uint32_t bytes_needed; - uint32_t bytes_in_list; - const AppInstallId do_not_evict[DO_NOT_EVICT_LIST_SIZE]; -} EachEvictData; - -//! Takes the information given in entry and calculates a new priority for the app. -//! -//! Policy rules: -//! 1. App that has least recently launched or been installed app is evicted. -static uint32_t prv_calculate_priority(AppCacheEntry *entry) { - return (uint32_t) MAX(entry->last_launch, entry->install_date); -} - -//! Comparator for EvictListNode -static int evict_node_comparator(void *a, void *b) { - EvictListNode *a_node = (EvictListNode *)a; - EvictListNode *b_node = (EvictListNode *)b; - - if (b_node->priority > a_node->priority) { - return 1; - } else if (b_node->priority < a_node->priority) { - return -1; - } else { - // bigger applications to have a lower priority - if (b_node->size < a_node->size) { - return 1; - } else if (b_node->size > a_node->size) { - return -1; - } else { - return 0; - } - } -} - -//! Trim the applications with highest priority while still keeping (bytes_in_list > bytes_needed) -static void prv_trim_top_priorities(EvictListNode **list_node, uint32_t *bytes_in_list, - uint32_t bytes_needed) { - EvictListNode *node = *list_node; - while (node) { - EvictListNode *temp = node; - if (node->size <= (*bytes_in_list - bytes_needed)) { - *bytes_in_list -= node->size; - node = (EvictListNode *)list_pop_head((ListNode *)node); - kernel_free(temp); - } else { - break; - } - } - *list_node = node; -} - -//! Check if we need to free up some space in the cache. If so, do it. -static void prv_cleanup_app_cache_if_needed(void *data) { - uint32_t pfs_space = get_available_pfs_space(); - - if (pfs_space < APP_SPACE_BUFFER) { - const uint32_t to_free = (APP_SPACE_BUFFER - pfs_space); - PBL_LOG(LOG_LEVEL_DEBUG, "Cache OOS: Need to free %"PRIu32" bytes, PFS avail space: %"PRIu32"", - to_free, pfs_space); - app_cache_free_up_space(to_free); - } -} - -static void prv_delete_cache_callback(void *data) { - app_cache_flush(); -} - -static void prv_delete_cached_files(void) { - pfs_remove_files(is_app_file_name); -} - -static bool prv_is_in_list(AppInstallId id, const AppInstallId list[], uint8_t len) { - for (unsigned int i = 0; i < len; i++) { - if (list[i] == id) { - return true; - } - } - return false; -} - -////////////////////// -// Settings Helpers -////////////////////// - -//! Settings iterator function that finds the entry with the lowest calculated priority -static bool prv_each_free_up_space(SettingsFile *file, SettingsRecordInfo *info, void *context) { - // check entry is valid - if ((info->key_len != sizeof(AppInstallId)) || (info->val_len != sizeof(AppCacheEntry))) { - PBL_LOG(LOG_LEVEL_WARNING, - "Invalid cache entry with key_len: %u and val_len: %u, flushing", - info->key_len, info->val_len); - system_task_add_callback(prv_delete_cache_callback, NULL); - return false; // stop iterating, delete the file and binaries - } - - EachEvictData *data = (EachEvictData *)context; - - AppInstallId id; - AppCacheEntry entry; - - info->get_key(file, (uint8_t *)&id, info->key_len); - info->get_val(file, (uint8_t *)&entry, info->val_len); - - // create node - EvictListNode *node = kernel_malloc_check(sizeof(EvictListNode)); - list_init((ListNode *)node); - - // give them an extremely high priority so that we only remove them if we really NEED to - // This list contains defaults that we shouldn't be removing. - uint32_t priority = 0; - if (prv_is_in_list(id, data->do_not_evict, DO_NOT_EVICT_LIST_SIZE)) { - priority = MAX_PRIORITY; - } - - *node = (EvictListNode) { - .id = id, - .size = entry.total_size, - .priority = MAX(priority, prv_calculate_priority(&entry)), - }; - - data->list = (EvictListNode *)list_sorted_add((ListNode *)data->list, (ListNode *)node, - evict_node_comparator, false); - data->bytes_in_list += node->size; - - if (data->bytes_in_list > data->bytes_needed) { - prv_trim_top_priorities(&data->list, &data->bytes_in_list, data->bytes_needed); - } - - return true; // continue iterating -} - -////////////////////////// -// AppCache API's -////////////////////////// - -//! Updates metadata within the cache entry for the given AppInstallId. Will update such fields as -//! launch count, last launch, and priority -status_t app_cache_app_launched(AppInstallId app_id) { - status_t rv; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - AppCacheEntry entry = { 0 }; - rv = settings_file_get(&file, (uint8_t *)&app_id, sizeof(AppInstallId), - (uint8_t *)&entry, sizeof(AppCacheEntry)); - - if (rv == S_SUCCESS) { - entry.last_launch = rtc_get_time(); - entry.launch_count += 1; - - rv = settings_file_set(&file, (uint8_t *)&app_id, sizeof(AppInstallId), - (uint8_t *)&entry, sizeof(AppCacheEntry)); - } else { - app_storage_delete_app(app_id); - settings_file_delete(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); - } - - settings_file_close(&file); - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return rv; -} - -//! Asks the app cache to remove 'bytes_needed' bytes of application binaries to free up space -//! for other things. -status_t app_cache_free_up_space(uint32_t bytes_needed) { - if (bytes_needed == 0) { - return E_INVALID_ARGUMENT; - } - - status_t rv; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - // we don't want to remove any default apps or quick launch apps, so keep them in a list. - EachEvictData evict_data = (EachEvictData) { - .bytes_needed = bytes_needed, - .do_not_evict = { -#if !SHELL_SDK - quick_launch_get_app(BUTTON_ID_UP), - quick_launch_get_app(BUTTON_ID_SELECT), - quick_launch_get_app(BUTTON_ID_DOWN), - quick_launch_get_app(BUTTON_ID_BACK), -#endif - watchface_get_default_install_id(), - worker_preferences_get_default_worker(), - }, - }; - - settings_file_each(&file, prv_each_free_up_space, &evict_data); - settings_file_close(&file); - - // remove all nodes found - EvictListNode *node = evict_data.list; - while (node) { - EvictListNode *temp = node; - PBL_LOG(LOG_LEVEL_DEBUG, "Deleting application binaries for app id: %"PRIu32", size: %"PRIu32, - node->id, node->size); - app_cache_remove_entry(node->id); - node = (EvictListNode *)list_pop_head((ListNode *)node); - kernel_free(temp); - } - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return rv; -} - -////////////////////// -// AppCache Helpers -////////////////////// - -// Remove the filename entry in the PFSFileList (via context) that corresponds to the -// app install id passed in via info -static bool prv_remove_matching_resource_file_callback(SettingsFile *file, - SettingsRecordInfo *info, - void *context) { - AppInstallId id; - // examine the SettingsRecordInfo and extract the AppInstallId from it - info->get_key(file, (uint8_t *)&id, info->key_len); - // the context passed in is really a pointer to the resource_list - PFSFileListEntry **resource_list = context; - PFSFileListEntry *iter = *resource_list; - while (iter) { - // grab the next entry right now since we may delete the node we're looking at - PFSFileListEntry *next = (PFSFileListEntry *)iter->list_node.next; - if (app_file_parse_app_id(iter->name) == id) { - // the AppInstallId of the file matches the one in the cache so we can remove this - // entry from the resource_list (since we don't want to delete it) - // note: resource_list may be updated if we happen to remove the first entry in the list - list_remove(&(iter->list_node), (ListNode**)resource_list, NULL); - kernel_free(iter); // free up the memory for the node we just removed - break; // we can quit now that we've found a match for this id - } - iter = next; - } - return true; -} - -// Delete files from resource_list that don't correspond to entries in the app cache -static void prv_app_cache_find_and_delete_orphans(PFSFileListEntry **resource_list) { - mutex_lock_recursive(s_app_cache_mutex); - - SettingsFile file; - status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - mutex_unlock_recursive(s_app_cache_mutex); - return; - } - // resource_list contains all of the resource files we found. We only - // want to delete orphans so we can remove any entries from the list that correspond - // to items in the app cache... - // prv_remove_matching_resource_file_callback scans resource_list and removes the entry - // corresponding to the passed-in application's id - settings_file_each(&file, prv_remove_matching_resource_file_callback, resource_list); - settings_file_close(&file); - - mutex_unlock_recursive(s_app_cache_mutex); - - // resource_list now only contains filenames of resource files that don't have corresponding - // entries in the app cache. We can safely delete these files. - PFSFileListEntry *iter = *resource_list; - while (iter) { - PBL_LOG(LOG_LEVEL_INFO, "Orphaned resource file removed: %s", iter->name); - pfs_remove(iter->name); - iter = (PFSFileListEntry *)iter->list_node.next; - } -} - -// The bug addressed in PBL-34010 caused resource files to remain in the filesystem even -// after the associated application had been deleted. This function attempts to find such -// orphaned files and remove them. Note: further to the bug in PBL-34010, this function will -// remove any resource files that are not related to apps currently in the cache. -static void prv_purge_orphaned_resource_files(void) { - // create a list of all app resource files in the filesystem - PFSFileListEntry *resource_files = pfs_create_file_list(is_app_resource_file_name); - // delete app resource files that don't correspond to entries in the app cache - prv_app_cache_find_and_delete_orphans(&resource_files); - pfs_delete_file_list(resource_files); -} - -////////////////////////// -// AppCache Settings API's -////////////////////////// - -//! Set up the app cache -void app_cache_init(void) { - s_app_cache_mutex = mutex_create_recursive(); - - mutex_lock_recursive(s_app_cache_mutex); - { - // if no cache file exists, then we should go ahead and clean up any files that are left over - int fd = pfs_open(APP_CACHE_FILE_NAME, OP_FLAG_READ, FILE_TYPE_STATIC, 0); - if (fd < 0) { - prv_delete_cached_files(); - goto unlock; - } - pfs_close(fd); - } - -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - - prv_purge_orphaned_resource_files(); -} - -//! Adds an entry with the given AppInstallId to the cache -status_t app_cache_add_entry(AppInstallId app_id, uint32_t total_size) { - status_t rv; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - AppCacheEntry entry = { - .install_date = rtc_get_time(), - .last_launch = 0, - .launch_count = 0, - .total_size = total_size, - }; - - rv = settings_file_set(&file, (uint8_t *)&app_id, sizeof(AppInstallId), - (uint8_t *)&entry, sizeof(AppCacheEntry)); - - settings_file_close(&file); - - // cleanup the cache if we need to - system_task_add_callback(prv_cleanup_app_cache_if_needed, NULL); - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return rv; -} - -//! Tests if an entry with the given AppInstallId is in the cache -bool app_cache_entry_exists(AppInstallId app_id) { - bool exists = false; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - exists = settings_file_exists(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); - - if (exists && !app_storage_app_exists(app_id)) { - settings_file_delete(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); - exists = false; - } - - settings_file_close(&file); - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return exists; -} - -//! Removes an entry with the given AppInstallId from the cache -status_t app_cache_remove_entry(AppInstallId app_id) { - status_t rv; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - rv = settings_file_delete(&file, (uint8_t *)&app_id, sizeof(AppInstallId)); - if (rv == S_SUCCESS) { - // Will delete an app from the filesystem. - app_storage_delete_app(app_id); - } - - settings_file_close(&file); - } - - if (rv == S_SUCCESS) { - PebbleEvent e = { - .type = PEBBLE_APP_CACHE_EVENT, - .app_cache_event = { - .cache_event_type = PebbleAppCacheEvent_Removed, - .install_id = app_id, - }, - }; - event_put(&e); - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return rv; -} - -void app_cache_flush(void) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - - mutex_lock_recursive(s_app_cache_mutex); - { - pfs_remove(APP_CACHE_FILE_NAME); - prv_delete_cached_files(); - } - mutex_unlock_recursive(s_app_cache_mutex); -} - -//////////////////////////////// -// Testing only -//////////////////////////////// - -static bool prv_each_get_size(SettingsFile *file, SettingsRecordInfo *info, void *context) { - if ((info->key_len != sizeof(AppInstallId)) || (info->val_len != sizeof(AppCacheEntry))) { - return true; // continue iterating - } - - uint32_t *cache_size = (uint32_t *)context; - AppCacheEntry entry; - info->get_val(file, (uint8_t *)&entry, info->val_len); - *cache_size += entry.total_size; - - return true; // continue iterating -} - -uint32_t app_cache_get_size(void) { - uint32_t cache_size = 0; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - settings_file_each(&file, prv_each_get_size, &cache_size); - settings_file_close(&file); - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return cache_size; -} - -typedef struct { - AppInstallId id; - uint32_t priority; -} AppCacheEachData; - -//! Settings iterator function that finds the entry with the lowest calculated priority -static bool prv_each_min_priority(SettingsFile *file, SettingsRecordInfo *info, void *context) { - // check entry is valid - if ((info->key_len != sizeof(AppInstallId)) || (info->val_len != sizeof(AppCacheEntry))) { - return true; // continue iterating - } - - AppCacheEachData *to_evict = (AppCacheEachData *)context; - - AppInstallId id; - AppCacheEntry entry; - - info->get_key(file, (uint8_t *)&id, info->key_len); - info->get_val(file, (uint8_t *)&entry, info->val_len); - - uint32_t entry_priority = prv_calculate_priority(&entry); - if (entry_priority < to_evict->priority) { - to_evict->id = id; - to_evict->priority = entry_priority; - } - - return true; // continue iterating -} - -//! Find the entry in the app cache with the lowest calculated priority -AppInstallId app_cache_get_next_eviction(void) { - AppInstallId ret_value = INSTALL_ID_INVALID; - mutex_lock_recursive(s_app_cache_mutex); - { - SettingsFile file; - status_t rv = settings_file_open(&file, APP_CACHE_FILE_NAME, APP_CACHE_MAX_SIZE); - if (rv != S_SUCCESS) { - goto unlock; - } - - // set max so that any application will have a lower priority. - AppCacheEachData to_evict = { - .id = INSTALL_ID_INVALID, - .priority = MAX_PRIORITY, - }; - settings_file_each(&file, prv_each_min_priority, (void *)&to_evict); - - settings_file_close(&file); - ret_value = to_evict.id; - } -unlock: - mutex_unlock_recursive(s_app_cache_mutex); - return ret_value; -} diff --git a/src/fw/services/normal/app_cache.h b/src/fw/services/normal/app_cache.h deleted file mode 100644 index 7a7f7272fd..0000000000 --- a/src/fw/services/normal/app_cache.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/app_install_types.h" -#include "system/status_codes.h" - -#include -#include - -//! @file app_cache.c -//! AppCache -//! -//! The AppCache keeps track of a cache of the applications that have binaries that reside on the -//! watch. When an app's binaries are removed from the watch, the entry with the same AppInstallId -//! is removed from the AppCache. -//! -//! When the app storage space has run out, a call to the app cache will retrieve the entry that -//! needs to be removed. - -//! Initializes the AppCache -void app_cache_init(void); - -//! Adds a blank entry with the given AppInstallId and total size to the AppCache -status_t app_cache_add_entry(AppInstallId app_id, uint32_t total_size); - -//! Removes an entry with the given AppInstallId from the AppCache -status_t app_cache_remove_entry(AppInstallId app_id); - -//! Checks whether an entry with the given AppInstallId is in the AppCache. -bool app_cache_entry_exists(AppInstallId app_id); - -//! Increments data stored about an entry with the given AppInstallId in the AppCache -status_t app_cache_app_launched(AppInstallId app_id); - -//! Ask the app cache to free up n bytes in case other parts of the system need room in the -//! filesystem -status_t app_cache_free_up_space(uint32_t bytes_needed); - -//! Clears the entire AppCache -//! NOTE: Must be called from PebbleTask_KernelBackground -void app_cache_flush(void); diff --git a/src/fw/services/normal/app_fetch_endpoint.c b/src/fw/services/normal/app_fetch_endpoint.c deleted file mode 100644 index 83c25fa99d..0000000000 --- a/src/fw/services/normal/app_fetch_endpoint.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_fetch_endpoint.h" - -#include -#include - -#include "applib/rockyjs/rocky_res.h" -#include "kernel/pbl_malloc.h" -#include "process_management/pebble_process_info.h" -#include "services/common/comm_session/session.h" -#include "services/common/put_bytes/put_bytes.h" -#include "services/common/system_task.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/app_db.h" -#include "services/normal/process_management/app_storage.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/math.h" -#include "util/uuid.h" - -//! Used for keeping track of binaries that are loaded through put_bytes -typedef struct { - AppInstallId app_id; - uint32_t total_size; - AppFetchResult prev_error; - bool cancelling; - bool in_progress; - bool app; - bool worker; - bool resources; -} AppFetchState; - -//! Command type -enum { - APP_FETCH_INSTALL_COMMAND = 0x01, -} AppFetchCommand; - -//! Possible results that come back from the INSTALL_COMMAND -enum { - APP_FETCH_INSTALL_RESPONSE = 0x01, -} AppFetchResponse; - -//! Possible results that come back from the INSTALL_COMMAND -enum { - APP_FETCH_RESPONSE_STARTING = 0x01, - APP_FETCH_RESPONSE_BUSY = 0x02, - APP_FETCH_RESPONSE_UUID_INVALID = 0x03, - APP_FETCH_RESPONSE_NO_DATA = 0x04, -} AppFetchInstallResult; - -//! Data sent to mobile phone for an INSTALL_COMMAND -typedef struct PACKED { - uint8_t command; - Uuid uuid; - AppInstallId app_id; -} AppFetchInstallRequest; - -//! Timeout used to determine how long we should wait before the phone starts sending the app -//! we requested (by issuing a put_bytes request). -#define FETCH_TIMEOUT_MS 15000 - -//! State for the app fetch flow -static AppFetchState s_fetch_state; - -//! Endpoint ID -static const uint16_t APP_FETCH_ENDPOINT_ID = 6001; - -//////////////////////////// -// Internal Helper Functions -//////////////////////////// - -//! Puts an error event with the given error code -static void prv_put_event_error(uint8_t error_code) { - s_fetch_state.prev_error = error_code; - PebbleEvent event = { - .type = PEBBLE_APP_FETCH_EVENT, - .app_fetch = { - .type = AppFetchEventTypeError, - .id = s_fetch_state.app_id, - .error_code = error_code, - } - }; - event_put(&event); -} - -//! Puts an event with the given progress -static void prv_put_event_progress(uint8_t percent) { - PebbleEvent event = { - .type = PEBBLE_APP_FETCH_EVENT, - .app_fetch = { - .type = AppFetchEventTypeProgress, - .id = s_fetch_state.app_id, - .progress_percent = percent, - } - }; - event_put(&event); -} - -//! Simply posts the type of event given. -static void prv_put_event_simple(AppFetchEventType type) { - PebbleEvent event = { - .type = PEBBLE_APP_FETCH_EVENT, - .app_fetch = { - .type = type, - .id = s_fetch_state.app_id, - } - }; - event_put(&event); -} - - -//! Recomputes and saves the progress percent for the current application fetch session -static uint8_t prv_compute_progress_percent(PutBytesObjectType type, unsigned int type_percent) { - // Add 33(34) percent for each piece that has finished (or is unneeded) - uint8_t percent = 0; - if (s_fetch_state.app) { - percent += 30; - } - if (s_fetch_state.worker) { - percent += 10; - } - if (s_fetch_state.resources) { - percent += 60; - } - - // add in the progress for the currently transferring piece. - percent += (type_percent / 3); - - // store value - return MIN(100, percent); -} - -//! Cleans up the state of the app fetch endpoint. Always called from the system task -static void prv_cleanup(AppFetchResult result) { - if (result != AppFetchResultSuccess) { - put_bytes_cancel(); - app_cache_remove_entry(s_fetch_state.app_id); - prv_put_event_error(result); - } - - s_fetch_state.in_progress = false; - - PBL_LOG(LOG_LEVEL_INFO, "App fetch cleanup with result %d", result); -} - -//! System task callback triggered by app_fetch_put_bytes_event_handler() when we are receiving -//! put_bytes messages in reponse to a fetch request to the phone. -void prv_put_bytes_event_system_task_cb(void *data) { - PebblePutBytesEvent *pb_event = (PebblePutBytesEvent *)data; - - if (!s_fetch_state.in_progress) { - return; - } - - // If put_bytes has failed, let's just say fail and stop everything. - if (pb_event->failed == true) { - AppFetchResult error; - if (s_fetch_state.cancelling) { - PBL_LOG(LOG_LEVEL_WARNING, "Put bytes cancelled by user"); - error = AppFetchResultUserCancelled; - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Put bytes failure"); - error = AppFetchResultPutBytesFailure; - } - - prv_cleanup(error); - goto finally; - } - - if (pb_event->type == PebblePutBytesEventTypeInitTimeout) { - PBL_LOG(LOG_LEVEL_WARNING, "Timed out waiting for putbytes request from phone"); - prv_cleanup(AppFetchResultTimeoutError); - } - - // If this is an object that doesn't have a cookie, then we won't care about it. - if (pb_event->has_cookie == false) { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring non cookie put_bytes event"); - goto finally; - } - - // Check for the different types of PutBytes events. - if (pb_event->type == PebblePutBytesEventTypeProgress) { - // compute and save the new progress, then show it on the progress bar - uint8_t percent = - prv_compute_progress_percent(pb_event->object_type, pb_event->progress_percent); - prv_put_event_progress(percent); - } else if (pb_event->type == PebblePutBytesEventTypeCleanup) { - // Mark off each finishing put_bytes transaction in our progress struct - - switch (pb_event->object_type) { - case ObjectWatchApp: - s_fetch_state.app = true; - break; - case ObjectWatchWorker: - s_fetch_state.worker = true; - break; - case ObjectAppResources: - s_fetch_state.resources = true; - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Got a PutBytes Object that we shouldn't have gotten"); - prv_cleanup(AppFetchResultGeneralFailure); - goto finally; - } - - // add the size of the finished PutBytes transaction to the total size. - s_fetch_state.total_size += pb_event->total_size; - } - - if (s_fetch_state.app && s_fetch_state.worker && s_fetch_state.resources) { - // if everything has finished being transferred - PBL_LOG(LOG_LEVEL_DEBUG, "All pieces (%"PRIu32" bytes) have been sent over put_bytes", - s_fetch_state.total_size); - - // signify in the app cache that the app binaries are now loaded - status_t added = app_cache_add_entry(s_fetch_state.app_id, s_fetch_state.total_size); - if (added == S_SUCCESS) { - const PebbleProcessMd *md = app_install_get_md(s_fetch_state.app_id, false); - if (rocky_app_validate_resources(md) == RockyResourceValidation_Invalid) { - PBL_LOG(LOG_LEVEL_ERROR, "Received app contains invalid JS bytecode"); - prv_put_event_error(AppFetchResultIncompatibleJSFailure); - } else { - // Set prev_error as a Success. - s_fetch_state.prev_error = AppFetchResultSuccess; - prv_put_event_simple(AppFetchEventTypeFinish); - } - app_install_release_md(md); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to insert into app cache: %"PRId32, added); - prv_put_event_error(AppFetchResultGeneralFailure); - } - prv_cleanup(AppFetchResultSuccess); - - } else if (pb_event->type == PebblePutBytesEventTypeCleanup) { - // Start the timeout watchdog again so we can tell if things get hung up - // before the phone starts sending the next putbytes object. - // This will only trigger if we've completed a piece and are still - // waiting for another one. - put_bytes_expect_init(FETCH_TIMEOUT_MS); - } - -finally: - kernel_free(pb_event); -} - -//! Put Bytes handler. Used for keeping track of progress and cleanup events. This is called -//! from KernelMain's event handler when it receives a PEBBLE_PUT_BYTES_EVENT event. put_bytes -//! posts these events to inform clients of progress. -void app_fetch_put_bytes_event_handler(PebblePutBytesEvent *pb_event) { - // If an app fetch isn't in progress, ignore it. - if (!s_fetch_state.in_progress) { - return; - } - - PebblePutBytesEvent *pb_event_copy = kernel_malloc_check(sizeof(PebblePutBytesEvent)); - memcpy(pb_event_copy, pb_event, sizeof(PebblePutBytesEvent)); - system_task_add_callback(prv_put_bytes_event_system_task_cb, pb_event_copy); -} - -//! Callback for the system task to fire off the fetch request. Triggered by a call to -//! app_fetch_binaries(). -static void prv_app_fetch_binaries_system_task_cb(void *data) { - AppFetchInstallRequest *request = (AppFetchInstallRequest *)data; - - // check if Bluetooth is active. If so, this will send. - bool successful = comm_session_send_data(comm_session_get_system_session(), APP_FETCH_ENDPOINT_ID, - (uint8_t*)request, sizeof(AppFetchInstallRequest), COMM_SESSION_DEFAULT_TIMEOUT); - - // log it - char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&request->uuid, uuid_buffer); - PBL_LOG(LOG_LEVEL_INFO, "%s request for app with uuid: %s and app_id: %"PRIu32"", - successful ? "Sent" : "Failed to send", uuid_buffer, request->app_id); - - // free before error checking - kernel_free(request); - - // If Bluetooth wasn't active, then post the error and cleanup. - if (!successful) { - prv_cleanup(AppFetchResultNoBluetooth); - return; - } - - // We next expect app_fetch_put_bytes_event_handler() to be called when the phone - // gets our fetch request and issues a putbytes request. - // Start the timeout watchdog to catch us in case the phone never issues the putbytes request. - put_bytes_expect_init(FETCH_TIMEOUT_MS); -} - -//! Called from the system task. Translates an Endpoint error to an event error and sends -//! off the appropriate event. -void prv_handle_app_fetch_install_response(uint8_t result_code) { - switch (result_code) { - case APP_FETCH_RESPONSE_STARTING: - PBL_LOG(LOG_LEVEL_INFO, "Phone confirmed it will start sending data"); - prv_put_event_simple(AppFetchEventTypeStart); - put_bytes_expect_init(FETCH_TIMEOUT_MS); - break; - case APP_FETCH_RESPONSE_BUSY: - PBL_LOG(LOG_LEVEL_WARNING, "Error: Phone is currently busy"); - prv_cleanup(AppFetchResultPhoneBusy); - break; - case APP_FETCH_RESPONSE_UUID_INVALID: - PBL_LOG(LOG_LEVEL_WARNING, "Error: UUID Invalid"); - prv_cleanup(AppFetchResultUUIDInvalid); - break; - case APP_FETCH_RESPONSE_NO_DATA: - PBL_LOG(LOG_LEVEL_WARNING, "Error: No data on phone"); - prv_cleanup(AppFetchResultNoData); - break; - } -} - -///////////////////////// -// Exported App Fetch API -///////////////////////// - -//! Called by the system that triggers an app fetch install request -void app_fetch_binaries(const Uuid *uuid, AppInstallId app_id, bool has_worker) { - if (s_fetch_state.in_progress) { - PBL_LOG(LOG_LEVEL_WARNING, "Already an app fetch in progress. Ignoring request"); - return; - } - - AppFetchInstallRequest *request = kernel_malloc_check(sizeof(AppFetchInstallRequest)); - - // reset all state - s_fetch_state = (AppFetchState){}; - - // Mark whether the worker needs to be sent over. - s_fetch_state.worker = !has_worker; - s_fetch_state.app_id = app_id; - s_fetch_state.in_progress = true; - - // populate fields - request->command = APP_FETCH_INSTALL_COMMAND; - request->uuid = *uuid; - request->app_id = app_id; - - // Start "warming up" the connection, this will cause the low-latency period to start ~1s sooner. - // Put bytes will extend the low-latency period after this: - comm_session_set_responsiveness(comm_session_get_system_session(), BtConsumerPpAppFetch, - ResponseTimeMin, MIN_LATENCY_MODE_TIMEOUT_APP_FETCH_SECS); - - system_task_add_callback(prv_app_fetch_binaries_system_task_cb, request); -} - -AppFetchError app_fetch_get_previous_error(void) { - AppFetchError error = { - .error = s_fetch_state.prev_error, - .id = s_fetch_state.app_id, - }; - - return error; -} - -static void prv_cancel_fetch_from_system_task(void *data) { - AppInstallId app_id = (AppInstallId)data; - - if ((!s_fetch_state.in_progress) || - ((s_fetch_state.app_id != app_id) && (app_id != INSTALL_ID_INVALID))) { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempted to cancel an app that is currently not being" - " fetched: %"PRId32, app_id); - return; - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Cancelling app fetch from system task"); - s_fetch_state.cancelling = true; - put_bytes_cancel(); -} - -void app_fetch_cancel_from_system_task(AppInstallId app_id) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - - prv_cancel_fetch_from_system_task((void *)(uintptr_t)app_id); -} - -void app_fetch_cancel(AppInstallId app_id) { - // Everything within app fetch happens on the background task - system_task_add_callback(prv_cancel_fetch_from_system_task, (void *)(uintptr_t)app_id); -} - -bool app_fetch_in_progress(void) { - return s_fetch_state.in_progress; -} - -//////////////////////////// -// Exported Callbacks -//////////////////////////// - -typedef struct __attribute__((__packed__)) { - uint8_t command; - uint8_t result_code; -} AppFetchResponseData; - -//! System task callback triggered by app_fetch_protocol_msg_callback(). -static void prv_app_fetch_protocol_handle_msg(AppFetchResponseData *response_data) { - - switch (response_data->command) { - case APP_FETCH_INSTALL_RESPONSE: - prv_handle_app_fetch_install_response(response_data->result_code); - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received, command: %u result: %u", - response_data->command, response_data->result_code); - prv_cleanup(AppFetchResultGeneralFailure); - break; - } -} - -//! Callback that is placed in the endpoints table. As of now, only responses will come through this -//! callback as all commands are originally sent to the phone. -void app_fetch_protocol_msg_callback(CommSession *session, const uint8_t *data, size_t length) { - if (length < sizeof(AppFetchResponseData)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message length %"PRIu32"", (uint32_t)length); - prv_cleanup(AppFetchResultGeneralFailure); - return; - } - - if (!s_fetch_state.in_progress) { - PBL_LOG(LOG_LEVEL_WARNING, "Got a message but app fetch not in progress. Ignoring"); - return; - } - - prv_app_fetch_protocol_handle_msg((AppFetchResponseData *)data); -} diff --git a/src/fw/services/normal/app_fetch_endpoint.h b/src/fw/services/normal/app_fetch_endpoint.h deleted file mode 100644 index 06bea505a5..0000000000 --- a/src/fw/services/normal/app_fetch_endpoint.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/uuid.h" -#include "kernel/events.h" -#include "process_management/app_install_types.h" - -typedef enum { - AppFetchResultSuccess, - AppFetchResultTimeoutError, - AppFetchResultGeneralFailure, - AppFetchResultPhoneBusy, - AppFetchResultUUIDInvalid, - AppFetchResultNoBluetooth, - AppFetchResultPutBytesFailure, - AppFetchResultNoData, - AppFetchResultUserCancelled, - AppFetchResultIncompatibleJSFailure, -} AppFetchResult; - -typedef struct { - AppFetchResult error; - AppInstallId id; -} AppFetchError; - -void app_fetch_binaries(const Uuid *uuid, AppInstallId app_id, bool has_worker); - -//! @param app_id The AppInstallId of the fetch to be cancelled. -//! NOTE: If `app_id` is INSTALL_ID_INVALID, it will cancel the fetch regardless of AppInstallId -void app_fetch_cancel(AppInstallId app_id); - -//! @param app_id The AppInstallId of the fetch to be cancelled. -//! NOTE: If `app_id` is INSTALL_ID_INVALID, it will cancel the fetch regardless of AppInstallId -//! NOTE: Must be called from PebbleTask_KernelBackground -void app_fetch_cancel_from_system_task(AppInstallId app_id); - -bool app_fetch_in_progress(void); - -//! Put Bytes handler. Used for keeping track of progress and cleanup events -void app_fetch_put_bytes_event_handler(PebblePutBytesEvent *pb_event); - -AppFetchError app_fetch_get_previous_error(void); diff --git a/src/fw/services/normal/app_glances/app_glance_service.c b/src/fw/services/normal/app_glances/app_glance_service.c deleted file mode 100644 index e8427d60f2..0000000000 --- a/src/fw/services/normal/app_glances/app_glance_service.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_glance_service.h" - -#include "applib/app_glance.h" -#include "applib/event_service_client.h" -#include "drivers/rtc.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "os/mutex.h" -#include "process_management/app_install_manager.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/app_glance_db.h" -#include "syscall/syscall_internal.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "util/math.h" - -//! Return true to continue iteration and false to stop it. -typedef bool (*SliceForEachCb)(AppGlanceSliceInternal *slice, void *context); - -static void prv_slice_for_each(AppGlance *glance, SliceForEachCb cb, void *context) { - if (!glance || !cb) { - return; - } - - for (unsigned int slice_index = 0; - slice_index < MIN(glance->num_slices, APP_GLANCE_DB_MAX_SLICES_PER_GLANCE); slice_index++) { - AppGlanceSliceInternal *current_slice = &glance->slices[slice_index]; - // Stop iterating if the client's callback function returns false - if (!cb(current_slice, context)) { - break; - } - } -} - -typedef struct FindCurrentSliceData { - time_t current_time; - AppGlanceSliceInternal *current_slice; -} FindCurrentSliceData; - -//! The "current" slice is the slice with an expiration_time closest to the current time while -//! still being after the current time -static bool prv_find_current_glance(AppGlanceSliceInternal *slice, void *context) { - FindCurrentSliceData *data = context; - PBL_ASSERTN(data); - - // First check if this slice never expires; the zero value of APP_GLANCE_SLICE_NO_EXPIRATION - // won't work with the comparisons we perform below - if (slice->expiration_time == APP_GLANCE_SLICE_NO_EXPIRATION) { - // We'll only use a never-expiring slice if we haven't set a slice yet - if (!data->current_slice) { - data->current_slice = slice; - } - // Continue iterating through the slices - return true; - } - - const int time_until_slice_expires = slice->expiration_time - data->current_time; - - // Continue iterating through the slices if this slice expires in the past - if (time_until_slice_expires <= 0) { - return true; - } - - // If we don't have a current slice or the current slice we have is a never-expiring slice, we can - // go ahead and use this slice now but continue iterating to try to find an earlier expiring slice - // in the list - if (!data->current_slice || - (data->current_slice->expiration_time == APP_GLANCE_SLICE_NO_EXPIRATION)) { - data->current_slice = slice; - return true; - } - - const int time_until_current_slice_expires = - data->current_slice->expiration_time - data->current_time; - - // If this slice expires earlier than our current slice, use this slice as the new current slice - if (time_until_slice_expires < time_until_current_slice_expires) { - data->current_slice = slice; - } - - // Continue iterating to try to find an earlier slice - return true; -} - -static void prv_glance_event_put(const Uuid *app_uuid) { - Uuid *app_uuid_copy = kernel_zalloc_check(sizeof(Uuid)); - *app_uuid_copy = *app_uuid; - - PebbleEvent e = (PebbleEvent) { - .type = PEBBLE_APP_GLANCE_EVENT, - .app_glance = (PebbleAppGlanceEvent) { - .app_uuid = app_uuid_copy, - }, - }; - - event_put(&e); -} - -////////////////////// -// Event handlers -// NOTE: These events are handled on KernelMain (app_glance_service_init called from -// services_normal_init) -////////////////////// - -static void prv_blob_db_event_handler(PebbleEvent *event, void *context) { - const PebbleBlobDBEvent *blob_db_event = &event->blob_db; - const BlobDBId blob_db_id = blob_db_event->db_id; - - if (blob_db_id != BlobDBIdAppGlance) { - // We only care about app glance changes - return; - } - - prv_glance_event_put((Uuid *)blob_db_event->key); -} - -static void prv_handle_app_cache_event(PebbleEvent *e, void *context) { - if (e->app_cache_event.cache_event_type == PebbleAppCacheEvent_Removed) { - Uuid app_uuid; - app_install_get_uuid_for_install_id(e->app_cache_event.install_id, &app_uuid); - app_glance_db_delete_glance(&app_uuid); - } -} - -////////////////////// -// Public API -////////////////////// - -void app_glance_service_init_glance(AppGlance *glance) { - if (!glance) { - return; - } - *glance = (AppGlance) {}; -} - -void app_glance_service_init(void) { - - static EventServiceInfo s_blob_db_event_info = { - .type = PEBBLE_BLOBDB_EVENT, - .handler = prv_blob_db_event_handler, - }; - event_service_client_subscribe(&s_blob_db_event_info); - - static EventServiceInfo s_app_cache_event_info = { - .type = PEBBLE_APP_CACHE_EVENT, - .handler = prv_handle_app_cache_event, - }; - event_service_client_subscribe(&s_app_cache_event_info); -} - -bool app_glance_service_get_current_slice(const Uuid *app_uuid, AppGlanceSliceInternal *slice_out) { - if (!slice_out) { - return false; - } - - bool success; - // Try to read the app's glance, first checking the cache - AppGlance *app_glance = kernel_zalloc_check(sizeof(*app_glance)); - const status_t rv = app_glance_db_read_glance(app_uuid, app_glance); - if (rv != S_SUCCESS) { - success = false; - goto cleanup; - } - - // Iterate over the slices to find the current slice (which might be NULL if there aren't any - // slices or if all of the slices have expired) - FindCurrentSliceData find_current_slice_data = (FindCurrentSliceData) { - .current_time = rtc_get_time(), - }; - prv_slice_for_each(app_glance, prv_find_current_glance, &find_current_slice_data); - if (!find_current_slice_data.current_slice) { - success = false; - goto cleanup; - } - - // Copy the current slice data to slice_out - *slice_out = *find_current_slice_data.current_slice; - success = true; - -cleanup: - kernel_free(app_glance); - - return success; -} - -DEFINE_SYSCALL(bool, sys_app_glance_update, const Uuid *uuid, const AppGlance *glance) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(uuid, sizeof(*uuid)); - syscall_assert_userspace_buffer(glance, sizeof(*glance)); - } - const bool success = (app_glance_db_insert_glance(uuid, glance) == S_SUCCESS); - if (success) { - prv_glance_event_put(uuid); - } - return success; -} diff --git a/src/fw/services/normal/app_glances/app_glance_service.h b/src/fw/services/normal/app_glances/app_glance_service.h deleted file mode 100644 index 45b7f2e24f..0000000000 --- a/src/fw/services/normal/app_glances/app_glance_service.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/blob_db/app_glance_db_private.h" -#include "services/normal/timeline/attribute.h" -#include "util/attributes.h" -#include "util/time/time.h" -#include "util/uuid.h" - -typedef enum AppGlanceSliceType { - AppGlanceSliceType_IconAndSubtitle = 0, - - AppGlanceSliceTypeCount -} AppGlanceSliceType; - -//! We name this "internal" so it won't conflict with the AppGlanceSlice struct we export in the SDK -#if UNITTEST -// Memory comparisons in unit tests won't work unless we pack the struct -typedef struct PACKED AppGlanceSliceInternal { -#else -typedef struct AppGlanceSliceInternal { -#endif - AppGlanceSliceType type; - time_t expiration_time; - union { - //! Add more structs to this union as we introduce new app glance slice types - struct { - uint32_t icon_resource_id; - char template_string[ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1]; - } icon_and_subtitle; - }; -} AppGlanceSliceInternal; - -typedef struct AppGlance { - size_t num_slices; - AppGlanceSliceInternal slices[APP_GLANCE_DB_MAX_SLICES_PER_GLANCE]; -} AppGlance; - -//! Initializes an AppGlance. -void app_glance_service_init_glance(AppGlance *glance); - -//! Initializes the app glance service. -void app_glance_service_init(void); - -//! Returns true if the current slice was successfully copied to slice_out. -//! Returns false if all slices in the glance have expired or if an error occurred. -bool app_glance_service_get_current_slice(const Uuid *app_uuid, AppGlanceSliceInternal *slice_out); diff --git a/src/fw/services/normal/app_inbox_service.c b/src/fw/services/normal/app_inbox_service.c deleted file mode 100644 index e7e55b29fd..0000000000 --- a/src/fw/services/normal/app_inbox_service.c +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_inbox_service.h" - -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "process_management/process_manager.h" -#include "os/mutex.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/buffer.h" -#include "util/list.h" - -typedef struct AppInboxNode { - ListNode node; - AppInboxServiceTag tag; - AppInboxMessageHandler message_handler; - AppInboxDroppedHandler dropped_handler; - PebbleTask event_handler_task; - - //! Indicates whether there is a writer. - //! The writer can set it to anything they want, mostly for debugging purposes. - void *writer; - bool write_failed; - bool has_pending_event; - - uint32_t num_failed; - uint32_t num_success; - - struct { - //! The size of `storage`. - size_t size; - - //! The positive offset relative relative to write_index, up until which the current - //! (incomplete) message has been written. - size_t current_offset; - - //! Index after which the current message should get written. - //! If this index is non-zero, there are completed message(s) in the buffer. - size_t write_index; - - ///! Pointer to the beginning of the storage. - uint8_t *storage; - } buffer; -} AppInboxNode; - -typedef struct AppInboxConsumerInfo { - AppInboxServiceTag tag; - AppInboxMessageHandler message_handler; - AppInboxDroppedHandler dropped_handler; - uint32_t num_failed; - uint32_t num_success; - uint8_t *it; - uint8_t *end; -} AppInboxConsumerInfo; - - -_Static_assert(sizeof(AppInboxServiceTag) <= sizeof(void *), - "AppInboxServiceTag should fit inside a void *"); - -static AppInboxNode *s_app_inbox_head; - -static PebbleRecursiveMutex *s_app_inbox_mutex; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Declarations of permitted handlers: - -extern void app_message_receiver_message_handler(const uint8_t *data, size_t length, - AppInboxConsumerInfo *consumer_info); -extern void app_message_receiver_dropped_handler(uint32_t num_dropped_messages); - -#ifdef UNITTEST -extern void test_message_handler(const uint8_t *data, size_t length, - AppInboxConsumerInfo *consumer_info); -extern void test_dropped_handler(uint32_t num_dropped_messages); -extern void test_alt_message_handler(const uint8_t *data, size_t length, - AppInboxConsumerInfo *consumer_info); -extern void test_alt_dropped_handler(uint32_t num_dropped_messages); -#endif - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Syscalls - -static AppInboxServiceTag prv_tag_for_event_handlers(const AppInboxMessageHandler message_handler, - const AppInboxDroppedHandler dropped_handler) { - static const struct { - AppInboxMessageHandler message_handler; - AppInboxDroppedHandler dropped_handler; - } s_event_handler_map[] = { - [AppInboxServiceTagAppMessageReceiver] = { - .message_handler = app_message_receiver_message_handler, - .dropped_handler = app_message_receiver_dropped_handler, - }, -#ifdef UNITTEST - [AppInboxServiceTagUnitTest] = { - .message_handler = test_message_handler, - .dropped_handler = test_dropped_handler, - }, - [AppInboxServiceTagUnitTestAlt] = { - .message_handler = test_alt_message_handler, - .dropped_handler = test_alt_dropped_handler, - } -#endif - }; - for (AppInboxServiceTag tag = 0; tag < NumAppInboxServiceTag; ++tag) { - if (s_event_handler_map[tag].message_handler == message_handler && - s_event_handler_map[tag].dropped_handler == dropped_handler) { - return tag; - } - } - return AppInboxServiceTagInvalid; -} - -DEFINE_SYSCALL(bool, sys_app_inbox_service_register, uint8_t *storage, size_t storage_size, - AppInboxMessageHandler message_handler, AppInboxDroppedHandler dropped_handler) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(storage, storage_size); - } - const AppInboxServiceTag service_tag = prv_tag_for_event_handlers(message_handler, - dropped_handler); - if (AppInboxServiceTagInvalid == service_tag) { - PBL_LOG(LOG_LEVEL_ERROR, "AppInbox event handlers not allowed <0x%"PRIx32", 0x%"PRIx32">", - // Ugh.. no more format signature slots free for %p %p... - (uint32_t)(uintptr_t)message_handler, (uint32_t)(uintptr_t)dropped_handler); - syscall_failed(); - } - - return app_inbox_service_register(storage, storage_size, - message_handler, dropped_handler, service_tag); -} - -DEFINE_SYSCALL(uint32_t, sys_app_inbox_service_unregister, uint8_t *storage) { - // No check is needed on the value of `storage `, we're not going to derefence it. - return app_inbox_service_unregister_by_storage(storage); -} - -static bool prv_get_consumer_info(AppInboxServiceTag tag, AppInboxConsumerInfo *info_in_out); - -DEFINE_SYSCALL(bool, sys_app_inbox_service_get_consumer_info, - AppInboxServiceTag tag, AppInboxConsumerInfo *info_out) { - if (PRIVILEGE_WAS_ELEVATED) { - if (info_out) { - syscall_assert_userspace_buffer(info_out, sizeof(*info_out)); - } - } - return prv_get_consumer_info(tag, info_out); -} - -static void prv_consume(AppInboxConsumerInfo *consumer_info); - -DEFINE_SYSCALL(void, sys_app_inbox_service_consume, AppInboxConsumerInfo *consumer_info) { - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(consumer_info, sizeof(*consumer_info)); - } - prv_consume(consumer_info); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -static void prv_lock(void) { - // Using one "global" lock for all app inboxes. - // If needed, we could easily give each app inbox its own mutex, but it seems overkill right now. - mutex_lock_recursive(s_app_inbox_mutex); -} - -static void prv_unlock(void) { - mutex_unlock_recursive(s_app_inbox_mutex); -} - -static bool prv_list_filter_by_storage(ListNode *found_node, void *data) { - return ((AppInboxNode *)found_node)->buffer.storage == (uint8_t *)data; -} - -static AppInboxNode *prv_find_inbox_by_storage(uint8_t *storage) { - return (AppInboxNode *) list_find((ListNode *)s_app_inbox_head, - prv_list_filter_by_storage, storage); -} - -static bool prv_list_filter_by_tag(ListNode *found_node, void *data) { - return ((AppInboxNode *)found_node)->tag == (AppInboxServiceTag)(uintptr_t)data; -} - -static AppInboxNode *prv_find_inbox_by_tag(AppInboxServiceTag tag) { - return (AppInboxNode *) list_find((ListNode *)s_app_inbox_head, - prv_list_filter_by_tag, (void *)(uintptr_t)tag); -} - -static AppInboxNode *prv_find_inbox_by_tag_and_log_if_not_found(AppInboxServiceTag tag) { - AppInboxNode *inbox = prv_find_inbox_by_tag(tag); - if (!inbox) { - PBL_LOG(LOG_LEVEL_ERROR, "No AppInbox for tag <%d>", tag); - } - return inbox; -} - -//! We don't report "number of messages consumed", because that would force the system to parse -//! the contents of the (app space) buffer, which might have been corrupted by the app. -//! Note that it's in theory possible for a misbehaving app to pass in a consumed_up_to_ptr that is -//! mid-way in a message. If it does so, it won't crash the kernel, but it will result in delivery -//! of broken messages to the app, but it won't be our fault... -static void prv_consume(AppInboxConsumerInfo *consumer_info) { - prv_lock(); - { - AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(consumer_info->tag); - if (!inbox) { - goto unlock; - } - uint8_t *const consumed_up_to_ptr = consumer_info->it; - uint8_t * const completed_messages_end = (inbox->buffer.storage + inbox->buffer.write_index); - if (consumed_up_to_ptr < inbox->buffer.storage || - consumed_up_to_ptr > completed_messages_end) { - PBL_LOG(LOG_LEVEL_ERROR, "Out of bounds"); - goto unlock; - } - const size_t bytes_consumed = (consumed_up_to_ptr - inbox->buffer.storage); - if (0 == bytes_consumed) { - goto unlock; - } - uint8_t * const partial_message_end = completed_messages_end + inbox->buffer.current_offset; - const size_t remaining_size = partial_message_end - consumed_up_to_ptr; - consumer_info->it = inbox->buffer.storage; - consumer_info->end = inbox->buffer.storage + remaining_size; - if (remaining_size) { - // New data has been written in the mean-time, move it all to the front of the buffer: - memmove(inbox->buffer.storage, consumed_up_to_ptr, remaining_size); - } - inbox->buffer.write_index -= bytes_consumed; - } -unlock: - prv_unlock(); -} - -static bool prv_get_consumer_info(AppInboxServiceTag tag, AppInboxConsumerInfo *info_out) { - if (!info_out) { - return false; - } - bool success = false; - prv_lock(); - { - AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); - if (!inbox) { - goto unlock; - } - - *info_out = (const AppInboxConsumerInfo) { - .tag = tag, - .message_handler = inbox->message_handler, - .dropped_handler = inbox->dropped_handler, - .num_failed = inbox->num_failed, - .num_success = inbox->num_success, - .it = inbox->buffer.storage, - .end = inbox->buffer.storage + inbox->buffer.write_index, - }; - - // Also mark that there is no event pending any more: - inbox->has_pending_event = false; - - // Reset counters because the info is communicated to app and it's about to consume the data. - inbox->num_failed = 0; - inbox->num_success = 0; - - success = true; - } -unlock: - prv_unlock(); - return success; -} - -//! @note Executes on app task, therefore we need to go through syscalls to access AppInbox! -static void prv_callback_event_handler(void *ctx) { - AppInboxServiceTag tag = (AppInboxServiceTag)(uintptr_t)ctx; - AppInboxConsumerInfo info = {}; - size_t num_message_consumed = 0; - if (!sys_app_inbox_service_get_consumer_info(tag, &info)) { - // Inbox wasn't there any more - return; - } - if (!info.message_handler) { - // Shouldn't ever happen, but better not PBL_ASSERTN on app task - PBL_LOG(LOG_LEVEL_ERROR, "No AppInbox message handler!"); - return; - } - if (!info.num_success && !info.num_failed) { - // Shouldn't ever happen, but better not PBL_ASSERTN on app task - PBL_LOG(LOG_LEVEL_ERROR, "Got callback, but zero messages!?"); - // fall-through - } - - // These conditions are redundant, just for safety: - while ((num_message_consumed < info.num_success) && (info.it < info.end)) { - AppInboxMessageHeader *msg = (AppInboxMessageHeader *)info.it; - - // Increment now so that if the message_handler calls into sys_app_inbox_service_consume(), - // it will be pointing *after* the message that is just handled: - info.it += (sizeof(AppInboxMessageHeader) + msg->length); - - // Check for safety, just in case the app has corrupted the buffer in the mean time: - if (msg->data + msg->length <= info.end) { - info.message_handler(msg->data, msg->length, &info); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Corrupted AppInbox message!"); - } - ++num_message_consumed; - } - - if (info.num_failed) { - if (info.dropped_handler) { - info.dropped_handler(info.num_failed); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Dropped %"PRIu32" messages but no dropped_handler", - info.num_failed); - } - } - - // Report back up to which byte we've consumed the data. - sys_app_inbox_service_consume(&info); -} - -bool app_inbox_service_register(uint8_t *storage, size_t storage_size, - AppInboxMessageHandler message_handler, - AppInboxDroppedHandler dropped_handler, AppInboxServiceTag tag) { - AppInboxNode *new_node = (AppInboxNode *)kernel_zalloc(sizeof(AppInboxNode)); - if (!new_node) { - PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory to allocate AppInboxNode"); - return false; - } - - prv_lock(); - { - bool has_error = false; - - if (prv_find_inbox_by_storage(storage)) { - PBL_LOG(LOG_LEVEL_ERROR, "AppInbox already registered for storage <%p>", storage); - has_error = true; - } - - // This check effectively caps the kernel RAM impact of this service, - // so it's not possible to abuse the syscall and cause kernel OOM. - if (prv_find_inbox_by_tag(tag)) { - PBL_LOG(LOG_LEVEL_ERROR, "AppInbox already registered for tag <%d>", tag); - has_error = true; - } - - if (has_error) { - kernel_free(new_node); - new_node = NULL; - } else { - new_node->tag = tag; - new_node->message_handler = message_handler; - new_node->dropped_handler = dropped_handler; - new_node->event_handler_task = pebble_task_get_current(); - new_node->buffer.storage = storage; - new_node->buffer.size = storage_size; - s_app_inbox_head = (AppInboxNode *)list_prepend((ListNode *)s_app_inbox_head, - (ListNode *)new_node); - } - } - prv_unlock(); - - return (new_node != NULL); -} - -uint32_t app_inbox_service_unregister_by_storage(uint8_t *storage) { - uint32_t num_messages_lost = 0; - prv_lock(); - { - AppInboxNode *node = prv_find_inbox_by_storage(storage); - if (node) { - list_remove((ListNode *)node, (ListNode **)&s_app_inbox_head, NULL); - num_messages_lost = node->num_failed + node->num_success + (node->writer ? 1 : 0); - kernel_free(node); - } - } - prv_unlock(); - return num_messages_lost; -} - -void app_inbox_service_unregister_all(void) { - prv_lock(); - { - AppInboxNode *node = s_app_inbox_head; - while (node) { - AppInboxNode *next = (AppInboxNode *) node->node.next; - kernel_free(node); - node = next; - } - s_app_inbox_head = NULL; - } - prv_unlock(); -} - -static bool prv_is_inbox_being_written(AppInboxNode *inbox) { - return (inbox->writer != NULL); -} - -static size_t prv_get_space_remaining(AppInboxNode *inbox) { - return (inbox->buffer.size - inbox->buffer.write_index - inbox->buffer.current_offset); -} - -bool prv_check_space_remaining(AppInboxNode *inbox, size_t required_free_length) { - const size_t space_remaining = prv_get_space_remaining(inbox); - if (required_free_length > space_remaining) { - PBL_LOG(LOG_LEVEL_ERROR, "Dropping data, not enough space %"PRIu32" vs %"PRIu32, - (uint32_t)required_free_length, (uint32_t)space_remaining); - return false; - } - return true; -} - -static void prv_send_event_if_needed(AppInboxNode *inbox) { - if (!inbox || inbox->has_pending_event) { - return; - } - PebbleEvent event = { - .type = PEBBLE_CALLBACK_EVENT, - .callback = { - .callback = prv_callback_event_handler, - .data = (void *)(uintptr_t) inbox->tag, - }, - }; - const bool is_event_enqueued = process_manager_send_event_to_process(inbox->event_handler_task, - &event); - if (!is_event_enqueued) { - PBL_LOG(LOG_LEVEL_ERROR, "Event queue full"); - } - inbox->has_pending_event = is_event_enqueued; -} - -static void prv_mark_failed_if_no_writer(AppInboxNode *inbox) { - if (!inbox->writer) { - // See PBL-41464 - // App message has been reset (closed and opened again) while a message was being received. - // Fail it because our state got lost. - inbox->write_failed = true; - } -} - -bool app_inbox_service_begin(AppInboxServiceTag tag, size_t required_free_length, void *writer) { - if (!writer) { - return false; - } - bool success = false; - prv_lock(); - { - AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); - if (!inbox) { - goto unlock; - } - if (prv_is_inbox_being_written(inbox)) { - ++inbox->num_failed; - PBL_LOG(LOG_LEVEL_ERROR, "Dropping data, already written by <%p>", inbox->writer); - // Don't send event here, when the current write finishes, the drop(s) will be reported too. - goto unlock; - } - if (!prv_check_space_remaining(inbox, required_free_length + sizeof(AppInboxMessageHeader))) { - ++inbox->num_failed; - // If it doesn't fit, send event immediately, we don't know when the next write will happen. - prv_send_event_if_needed(inbox); - goto unlock; - } - - inbox->writer = writer; - inbox->write_failed = false; - // Leave space at the beginning for the header, which we'll write in the end - inbox->buffer.current_offset = sizeof(AppInboxMessageHeader); - success = true; - } -unlock: - prv_unlock(); - return success; -} - -bool app_inbox_service_write(AppInboxServiceTag tag, const uint8_t *data, size_t length) { - bool success = false; - prv_lock(); - { - AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); - if (!inbox) { - goto unlock; - } - prv_mark_failed_if_no_writer(inbox); - if (inbox->write_failed) { - goto unlock; - } - if (!prv_check_space_remaining(inbox, length)) { - inbox->write_failed = true; - goto unlock; - } - memcpy(inbox->buffer.storage + inbox->buffer.write_index + inbox->buffer.current_offset, - data, length); - inbox->buffer.current_offset += length; - success = true; - } -unlock: - prv_unlock(); - return success; -} - -static void prv_finish(AppInboxNode *inbox) { - inbox->writer = NULL; - inbox->buffer.current_offset = 0; -} - -void app_inbox_service_init(void) { - s_app_inbox_mutex = mutex_create_recursive(); -} - -bool app_inbox_service_end(AppInboxServiceTag tag) { - bool success = false; - prv_lock(); - { - AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); - if (!inbox) { - goto unlock; - } - prv_mark_failed_if_no_writer(inbox); - if (inbox->write_failed) { - ++inbox->num_failed; - } else { - const AppInboxMessageHeader header = (const AppInboxMessageHeader) { - .length = inbox->buffer.current_offset - sizeof(AppInboxMessageHeader), - // Fill with something that might aid debugging one day: - .padding = { 0xaa, 0xaa, 0xaa, 0xaa }, - }; - memcpy(inbox->buffer.storage + inbox->buffer.write_index, &header, sizeof(header)); - inbox->buffer.write_index += inbox->buffer.current_offset; - ++inbox->num_success; - success = true; - } - prv_finish(inbox); - - prv_send_event_if_needed(inbox); - } -unlock: - prv_unlock(); - return success; -} - -void app_inbox_service_cancel(AppInboxServiceTag tag) { - prv_lock(); - { - AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag); - if (!inbox) { - goto unlock; - } - prv_finish(inbox); - } -unlock: - prv_unlock(); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Unit Test Interfaces - -bool app_inbox_service_has_inbox_for_tag(AppInboxServiceTag tag) { - bool has_inbox; - prv_lock(); - has_inbox = (prv_find_inbox_by_tag(tag) != NULL); - prv_unlock(); - return has_inbox; -} - -bool app_inbox_service_has_inbox_for_storage(uint8_t *storage) { - bool has_inbox; - prv_lock(); - has_inbox = (prv_find_inbox_by_storage(storage) != NULL); - prv_unlock(); - return has_inbox; -} - -bool app_inbox_service_is_being_written_for_tag(AppInboxServiceTag tag) { - bool is_written = false; - prv_lock(); - AppInboxNode *inbox = prv_find_inbox_by_tag(tag); - if (inbox) { - is_written = (inbox->writer != NULL); - } - prv_unlock(); - return is_written; -} - -uint32_t app_inbox_service_num_failed_for_tag(AppInboxServiceTag tag) { - uint32_t num_failed = 0; - prv_lock(); - AppInboxNode *inbox = prv_find_inbox_by_tag(tag); - if (inbox) { - num_failed = inbox->num_failed; - } - prv_unlock(); - return num_failed; -} - -uint32_t app_inbox_service_num_success_for_tag(AppInboxServiceTag tag) { - uint32_t num_success = 0; - prv_lock(); - AppInboxNode *inbox = prv_find_inbox_by_tag(tag); - if (inbox) { - num_success = inbox->num_success; - } - prv_unlock(); - return num_success; -} diff --git a/src/fw/services/normal/app_message/app_message_sender.h b/src/fw/services/normal/app_message/app_message_sender.h deleted file mode 100644 index 4d1165ddd4..0000000000 --- a/src/fw/services/normal/app_message/app_message_sender.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/app_outbox_service.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_send_queue.h" - -#include - -//! This module uses AppOutbox to get Pebble Protocol outbound messages from the app. -//! It does not keep any static state inside this module, all the state is stored by the app outbox -//! service. It's really just a piece of glue code between app_outbox.c and session_send_queue.c - - -//! Enum that "inherits" from AppOutboxStatus and defines app-message-sender-specific status -//! values in the user range: -typedef enum { - AppMessageSenderErrorSuccess = AppOutboxStatusSuccess, - AppMessageSenderErrorDisconnected = AppOutboxStatusConsumerDoesNotExist, - AppMessageSenderErrorDataTooShort = AppOutboxStatusUserRangeStart, - AppMessageSenderErrorEndpointDisallowed, - - NumAppMessageSenderError, -} AppMessageSenderError; - -_Static_assert((NumAppMessageSenderError - 1) <= AppOutboxStatusUserRangeEnd, - "AppMessageSenderError value can't be bigger than AppOutboxStatusUserRangeEnd"); - -//! @note This is the data structure for the `consumer_data` of the AppOutboxMessage. -//! app_message_sender.c assumes this struct is always contained within the AppOutboxMessage -//! struct. -typedef struct { - SessionSendQueueJob send_queue_job; - - CommSession *session; - PebbleProtocolHeader header; - - size_t consumed_length; -} AppMessageSendJob; - -_Static_assert(offsetof(AppMessageSendJob, send_queue_job) == 0, - "send_queue_job must be first member, due to the way session_send_queue.c works"); - -//! Structure of `data` in outbox_message (in app's memory space) -//! @note None of these fields can be trusted / used as is, they need to be sanitized. -typedef struct { - //! Can be NULL to "auto select" the session based on the UUID of the running app. - CommSession *session; - - //! Padding for future use - uint8_t padding[6]; - - uint16_t endpoint_id; - uint8_t payload[]; -} AppMessageAppOutboxData; - -#if !UNITTEST -_Static_assert(sizeof(AppMessageAppOutboxData) <= 12, - "Can't grow AppMessageAppOutboxData beyond 12 bytes, can break apps!"); -#endif - -//! To be called once during boot. This registers this module with app_outbox_service. -void app_message_sender_init(void); diff --git a/src/fw/services/normal/app_order_endpoint.c b/src/fw/services/normal/app_order_endpoint.c deleted file mode 100644 index b3403cdb6f..0000000000 --- a/src/fw/services/normal/app_order_endpoint.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "process_management/app_install_manager_private.h" -#include "process_management/app_order_storage.h" -#include "services/common/comm_session/session.h" -#include "system/hexdump.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "util/uuid.h" - -#include -#include - -//! @file app_order_endpoint.c -//! App Order Endpoint -//! -//! There is only 1 way to use this endpoint - -//! \code{.c} -//! 0x01 -//! <16-byte UUID_1> -//! ... -//! <16-byte UUID_N> -//! \endcode - -//! AppOrder Endpoint ID -static const uint16_t APP_ORDER_ENDPOINT_ID = 0xabcd; - -typedef enum { - APP_ORDER_CMD = 0x01, -} AppOrderCommand; - -typedef enum { - APP_ORDER_RES_SUCCESS = 0x01, - APP_ORDER_RES_FAILURE = 0x02, - APP_ORDER_RES_INVALID = 0x03, - APP_ORDER_RES_RETRY_LATER = 0x04, -} AppOrderResponse; - -typedef struct { - CommSession *session; - uint8_t result; -} ResponseInfo; - -static void prv_send_result(CommSession *session, uint8_t result) { - PBL_LOG(LOG_LEVEL_DEBUG, "Sending result of %d", result); - comm_session_send_data(session, APP_ORDER_ENDPOINT_ID, (uint8_t*)&result, sizeof(result), - COMM_SESSION_DEFAULT_TIMEOUT); -} - -static void prv_handle_app_order_msg(CommSession *session, const uint8_t *data, uint32_t length) { - // call write order function, then fire an event for app install manager to tell the launcher - // to throw everything away or at least don't overwrite the data please. - uint8_t num_uuids = data[0]; - - if (num_uuids != (length / UUID_SIZE)) { - PBL_LOG(LOG_LEVEL_DEBUG, "invalid length, num_uuids does not match with the length of message"); - prv_send_result(session, APP_ORDER_RES_INVALID); - } - - write_uuid_list_to_file((const Uuid *)&data[1], num_uuids); - prv_send_result(session, APP_ORDER_RES_SUCCESS); -} - -void app_order_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { - // header includes APP_ORDER_CMD and a num_uuids uint8_t - const uint8_t header_len = sizeof(AppOrderCommand) + sizeof(uint8_t); - - // Ensure it is a valid message. There is a list of UUID's after the header. - if ((length % UUID_SIZE) != header_len) { - PBL_LOG(LOG_LEVEL_DEBUG, "invalid length, (length - header_len) not multiple of 16"); - prv_send_result(session, APP_ORDER_RES_INVALID); - return; - } - - switch (data[0]) { - case APP_ORDER_CMD: - PBL_LOG(LOG_LEVEL_DEBUG, "Got APP_ORDER message"); - prv_handle_app_order_msg(session, &data[1], length - 1); - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received, first byte is %u", data[0]); - prv_send_result(session, APP_ORDER_RES_FAILURE); - break; - } -} diff --git a/src/fw/services/normal/app_outbox_service.c b/src/fw/services/normal/app_outbox_service.c deleted file mode 100644 index 440068b1fa..0000000000 --- a/src/fw/services/normal/app_outbox_service.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/app_message/app_message_internal.h" -#include "kernel/pbl_malloc.h" -#include "os/mutex.h" -#include "process_management/process_manager.h" -#include "services/normal/app_message/app_message_sender.h" -#include "services/normal/app_outbox_service.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/list.h" - -static PebbleRecursiveMutex *s_app_outbox_mutex; - -typedef struct { - AppOutboxMessage *head; - AppOutboxMessageHandler message_handler; - size_t consumer_data_length; - PebbleTask consumer_task; -} AppOutboxConsumer; - -//! Array with the consuming kernel services that have been registered at run-time: -static AppOutboxConsumer s_app_outbox_consumer[NumAppOutboxServiceTag]; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Declarations of permitted senders: - -typedef struct { - AppOutboxSentHandler sent_handler; - size_t max_length; - uint32_t max_pending_messages; -} AppOutboxSenderDef; - -extern void app_message_outbox_handle_app_outbox_message_sent(AppOutboxStatus status, void *cb_ctx); - -#ifdef UNITTEST -extern void test_app_outbox_sent_handler(AppOutboxStatus status, void *cb_ctx); -#endif - -//! Constant array defining the allowed handlers and their restrictions: -static const AppOutboxSenderDef s_app_outbox_sender_defs[] = { - [AppOutboxServiceTagAppMessageSender] = { - .sent_handler = app_message_outbox_handle_app_outbox_message_sent, - .max_length = (sizeof(AppMessageAppOutboxData) + APP_MSG_HDR_OVRHD_SIZE + APP_MSG_8K_DICT_SIZE), - .max_pending_messages = 1, - }, -#ifdef UNITTEST - [AppOutboxServiceTagUnitTest] = { - .sent_handler = test_app_outbox_sent_handler, - .max_length = 1, - .max_pending_messages = 2, - }, -#endif -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Syscalls - -static const AppOutboxSenderDef *prv_find_def_and_tag_by_handler(AppOutboxSentHandler sent_handler, - AppOutboxServiceTag *tag_out) { - for (AppOutboxServiceTag tag = 0; tag < NumAppOutboxServiceTag; ++tag) { - if (s_app_outbox_sender_defs[tag].sent_handler == sent_handler) { - if (tag_out) { - *tag_out = tag; - } - return &s_app_outbox_sender_defs[tag]; - } - } - if (tag_out) { - *tag_out = AppOutboxServiceTagInvalid; - } - return NULL; -} - -static void app_outbox_service_send(const uint8_t *data, size_t length, - AppOutboxSentHandler sent_handler, void *cb_ctx); - -DEFINE_SYSCALL(void, sys_app_outbox_send, const uint8_t *data, size_t length, - AppOutboxSentHandler sent_handler, void *cb_ctx) { - if (PRIVILEGE_WAS_ELEVATED) { - // Check that data is in app space: - syscall_assert_userspace_buffer(data, length); - } - - const AppOutboxSenderDef *def = prv_find_def_and_tag_by_handler(sent_handler, NULL); - if (!def) { - PBL_LOG(LOG_LEVEL_ERROR, "AppOutbox sent_handler not allowed <%p>", sent_handler); - syscall_failed(); - } - - const size_t max_length = def->max_length; - if (length > max_length) { - PBL_LOG(LOG_LEVEL_ERROR, "AppOutbox max_length exceeded %"PRIu32" vs %"PRIu32, - (uint32_t)length, (uint32_t)max_length); - syscall_failed(); - } - app_outbox_service_send(data, length, sent_handler, cb_ctx); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Helpers - -static void prv_lock(void) { - // Using one "global" lock for all app outboxes. - // If needed, we could easily give each app outbox its own mutex, but it seems overkill right now. - mutex_lock_recursive(s_app_outbox_mutex); -} - -static void prv_unlock(void) { - mutex_unlock_recursive(s_app_outbox_mutex); -} - -static AppOutboxConsumer *prv_consumer_for_tag(AppOutboxServiceTag tag) { - if (tag == AppOutboxServiceTagInvalid) { - return NULL; - } - AppOutboxConsumer *consumer = &s_app_outbox_consumer[tag]; - if (consumer->message_handler == NULL) { - return NULL; - } - return consumer; -} - -static void prv_schedule_sent_handler(AppOutboxSentHandler sent_handler, - void *cb_ctx, AppOutboxStatus status) { - if (!sent_handler) { - return; - } - PebbleEvent event = { - .type = PEBBLE_APP_OUTBOX_SENT_EVENT, - .app_outbox_sent = { - .sent_handler = sent_handler, - .cb_ctx = cb_ctx, - .status = status, - }, - }; - process_manager_send_event_to_process(PebbleTask_App, &event); -} - -//! @note This executes on App Task -static void prv_schedule_consumer_message_handler(AppOutboxConsumer *consumer, - AppOutboxMessage *message) { - void (*callback)(void *) = (__typeof__(callback))consumer->message_handler; - PebbleEvent event = { - .type = PEBBLE_APP_OUTBOX_MSG_EVENT, - .app_outbox_msg = { - .callback = callback, - .data = message, - }, - }; - sys_send_pebble_event_to_kernel(&event); -} - -static uint32_t prv_num_pending_messages(const AppOutboxConsumer *consumer) { - return list_count((ListNode *)consumer->head); -} - -static AppOutboxConsumer *prv_find_consumer_with_message(const AppOutboxMessage *message) { - AppOutboxMessage *head = (AppOutboxMessage *)list_get_head((ListNode *)message); - for (AppOutboxServiceTag tag = 0; tag < NumAppOutboxServiceTag; ++tag) { - if (s_app_outbox_consumer[tag].head == head) { - return &s_app_outbox_consumer[tag]; - } - } - return NULL; -} - -void prv_cleanup_pending_messages(AppOutboxConsumer *consumer, bool should_call_sent_handler) { - AppOutboxMessage *message = consumer->head; - consumer->head = NULL; - while (message) { - if (should_call_sent_handler) { - prv_schedule_sent_handler(message->sent_handler, message->cb_ctx, - AppOutboxStatusConsumerDoesNotExist); - } - - AppOutboxMessage *next = (AppOutboxMessage *)message->node.next; - message->node = (ListNode) {}; - // Don't free it, it's the responsibility of the consumer to eventually call - // app_outbox_service_consume_message(), which will free the message! - message = next; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Exported functions - -void app_outbox_service_register(AppOutboxServiceTag tag, - AppOutboxMessageHandler message_handler, - PebbleTask consumer_task, - size_t consumer_data_length) { - prv_lock(); - { - PBL_ASSERTN(!prv_consumer_for_tag(tag)); - AppOutboxConsumer *consumer = &s_app_outbox_consumer[tag]; - consumer->message_handler = message_handler; - consumer->consumer_data_length = consumer_data_length; - consumer->consumer_task = consumer_task; - } - prv_unlock(); -} - -void app_outbox_service_unregister(AppOutboxServiceTag service_tag) { - prv_lock(); - { - prv_cleanup_pending_messages(&s_app_outbox_consumer[service_tag], - true /* should_call_sent_handler */); - s_app_outbox_consumer[service_tag].message_handler = NULL; - } - prv_unlock(); -} - -//! @note This executes on App Task -//! Should only get called through the syscall, sys_app_outbox_send -static void app_outbox_service_send(const uint8_t *data, size_t length, - AppOutboxSentHandler sent_handler, void *cb_ctx) { - AppOutboxStatus status = AppOutboxStatusSuccess; - prv_lock(); - { - AppOutboxServiceTag tag; - const AppOutboxSenderDef *def = prv_find_def_and_tag_by_handler(sent_handler, &tag); - AppOutboxConsumer *consumer = prv_consumer_for_tag(tag); - if (!consumer) { - status = AppOutboxStatusConsumerDoesNotExist; - goto finally; - } - - if (prv_num_pending_messages(consumer) >= def->max_pending_messages) { - status = AppOutboxStatusOutOfResources; - goto finally; - } - - const size_t consumer_data_length = consumer->consumer_data_length; - AppOutboxMessage *message = - (AppOutboxMessage *)kernel_zalloc(sizeof(AppOutboxMessage) + consumer_data_length); - if (!message) { - status = AppOutboxStatusOutOfMemory; - goto finally; - } - - *message = (AppOutboxMessage) { - .data = data, - .length = length, - .sent_handler = sent_handler, - .cb_ctx = cb_ctx, - }; - - consumer->head = (AppOutboxMessage *)list_prepend((ListNode *)consumer->head, - (ListNode *)message); - - prv_schedule_consumer_message_handler(consumer, message); - } -finally: - if (AppOutboxStatusSuccess != status) { - prv_schedule_sent_handler(sent_handler, cb_ctx, status); - } - prv_unlock(); -} - -bool app_outbox_service_is_message_cancelled(AppOutboxMessage *message) { - prv_lock(); - bool cancelled = !prv_find_consumer_with_message(message); - prv_unlock(); - return cancelled; -} - -void app_outbox_service_consume_message(AppOutboxMessage *message, AppOutboxStatus status) { - prv_lock(); - { - if (app_outbox_service_is_message_cancelled(message)) { - // Don't call the sent_handler - goto finally; - } - AppOutboxConsumer *consumer = prv_find_consumer_with_message(message); - PBL_ASSERTN(consumer); - list_remove(&message->node, (ListNode **)&consumer->head, NULL); - prv_schedule_sent_handler(message->sent_handler, message->cb_ctx, status); - } -finally: - kernel_free(message); - prv_unlock(); -} - -void app_outbox_service_cleanup_all_pending_messages(void) { - prv_lock(); - for (AppOutboxServiceTag tag = 0; tag < NumAppOutboxServiceTag; ++tag) { - AppOutboxConsumer *consumer = &s_app_outbox_consumer[tag]; - prv_cleanup_pending_messages(consumer, false /* should_call_sent_handler */); - } - prv_unlock(); -} - -void app_outbox_service_cleanup_event(PebbleEvent *event) { - if (event->type != PEBBLE_APP_OUTBOX_MSG_EVENT) { - return; - } - // Call consume directly to clean up the message, it's not valid anyway: - app_outbox_service_consume_message((AppOutboxMessage *)event->app_outbox_msg.data, - AppOutboxStatusSuccess /* ignored */); -} - -void app_outbox_service_init(void) { - s_app_outbox_mutex = mutex_create_recursive(); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Unit Test Interfaces - -void app_outbox_service_deinit(void) { - app_outbox_service_cleanup_all_pending_messages(); - memset(&s_app_outbox_consumer, 0, sizeof(s_app_outbox_consumer)); - mutex_destroy((PebbleMutex *)s_app_outbox_mutex); - s_app_outbox_mutex = NULL; -} - -uint32_t app_outbox_service_max_pending_messages(AppOutboxServiceTag tag) { - return s_app_outbox_sender_defs[tag].max_pending_messages; -} - -uint32_t app_outbox_service_max_message_length(AppOutboxServiceTag tag) { - return s_app_outbox_sender_defs[tag].max_length; -} diff --git a/src/fw/services/normal/audio_endpoint.c b/src/fw/services/normal/audio_endpoint.c deleted file mode 100644 index 60448c431d..0000000000 --- a/src/fw/services/normal/audio_endpoint.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "audio_endpoint.h" -#include "audio_endpoint_private.h" - -#include "comm/bt_lock.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/new_timer/new_timer.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/circular_buffer.h" - -#define AUDIO_ENDPOINT (10000) - -#define ACTIVE_MODE_TIMEOUT (10000) -#define ACTIVE_MODE_START_BUFFER (100) - -_Static_assert(ACTIVE_MODE_TIMEOUT > ACTIVE_MODE_START_BUFFER, - "ACTIVE_MODE_TIMEOUT must be greater than ACTIVE_MODE_START_BUFFER"); - -typedef struct { - AudioEndpointSessionId id; - AudioEndpointSetupCompleteCallback setup_completed; - AudioEndpointStopTransferCallback stop_transfer; - TimerID active_mode_trigger; -} AudioEndpointSession; - -static AudioEndpointSessionId s_session_id = AUDIO_ENDPOINT_SESSION_INVALID_ID; -static AudioEndpointSession s_session; -static uint32_t s_dropped_frames; - -static void prv_session_deinit(bool call_stop_handler) { - bt_lock(); - if (call_stop_handler && s_session.stop_transfer) { - s_session.stop_transfer(s_session.id); - } - - if (s_session.active_mode_trigger != TIMER_INVALID_ID) { - new_timer_delete(s_session.active_mode_trigger); - s_session.active_mode_trigger = TIMER_INVALID_ID; - CommSession *comm_session = comm_session_get_system_session(); - comm_session_set_responsiveness( - comm_session, BtConsumerPpAudioEndpoint, ResponseTimeMax, 0); - } - - s_session.id = AUDIO_ENDPOINT_SESSION_INVALID_ID; - s_session.setup_completed = NULL; - s_session.stop_transfer = NULL; - bt_unlock(); - - if (s_dropped_frames > 0) { - PBL_LOG(LOG_LEVEL_INFO, "Dropped %"PRIu32" frames during audio transfer", s_dropped_frames); - } -} - -#ifndef PLATFORM_TINTIN -void audio_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { - MsgId msg_id = data[0]; - if (size >= sizeof(StopTransferMsg) && msg_id == MsgIdStopTransfer) { - StopTransferMsg *msg = (StopTransferMsg *)data; - - if (msg->session_id == s_session.id) { - prv_session_deinit(true /* call_stop_handler */); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Received mismatching session id: %u vs %u", - msg->session_id, s_session.id); - } - } -} -#else -void audio_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { -} -#endif - -static void prv_responsiveness_granted_handler(void) { - if (s_session.id == AUDIO_ENDPOINT_SESSION_INVALID_ID) { - return; // Party's over - } - - AudioEndpointSetupCompleteCallback cb = NULL; - AudioEndpointSessionId id = AUDIO_ENDPOINT_SESSION_INVALID_ID; - - bt_lock(); - // We're repeatedly calling comm_session_set_responsiveness_ext, but we only need to call the - // completed handler the first time the requested responsiveness takes effect: - if (s_session.setup_completed) { - cb = s_session.setup_completed; - id = s_session.id; - s_session.setup_completed = NULL; - } - bt_unlock(); - - if (cb) { - cb(id); - } -} - -static void prv_start_active_mode(void *data) { - CommSession *comm_session = comm_session_get_system_session(); - comm_session_set_responsiveness_ext(comm_session, BtConsumerPpAudioEndpoint, ResponseTimeMin, - MIN_LATENCY_MODE_TIMEOUT_AUDIO_SECS, - prv_responsiveness_granted_handler); -} - -AudioEndpointSessionId audio_endpoint_setup_transfer( - AudioEndpointSetupCompleteCallback setup_completed, - AudioEndpointStopTransferCallback stop_transfer) { - - if (s_session.id != AUDIO_ENDPOINT_SESSION_INVALID_ID) { - return AUDIO_ENDPOINT_SESSION_INVALID_ID; - } - - bt_lock(); - - s_session.id = ++s_session_id; - s_session.setup_completed = setup_completed; - s_session.stop_transfer = stop_transfer; - s_session.active_mode_trigger = new_timer_create(); - s_dropped_frames = 0; - - // restart active mode before it expires, this way it will never be off during the transfer - new_timer_start(s_session.active_mode_trigger, ACTIVE_MODE_TIMEOUT - ACTIVE_MODE_START_BUFFER, - prv_start_active_mode, NULL, TIMER_START_FLAG_REPEATING); - - bt_unlock(); - - prv_start_active_mode(NULL); - - return s_session.id; -} - -void audio_endpoint_add_frame(AudioEndpointSessionId session_id, uint8_t *frame, - uint8_t frame_size) { - PBL_ASSERTN(session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); - - if (s_session.id != session_id) { - return; - } - - CommSession *comm_session = comm_session_get_system_session(); - SendBuffer *sb = comm_session_send_buffer_begin_write(comm_session, AUDIO_ENDPOINT, - sizeof(DataTransferMsg) + frame_size + 1, - 0 /* timeout_ms, never block */); - if (!sb) { - s_dropped_frames++; - PBL_LOG(LOG_LEVEL_DEBUG, "Dropping a frame..."); - return; - } - - uint8_t header[sizeof(DataTransferMsg) + sizeof(uint8_t) /* frame_size */]; - DataTransferMsg *msg = (DataTransferMsg *) header; - *msg = (const DataTransferMsg) { - .msg_id = MsgIdDataTransfer, - .session_id = session_id, - .frame_count = 1, - }; - msg->frames[0] = frame_size; - - comm_session_send_buffer_write(sb, header, sizeof(header)); - comm_session_send_buffer_write(sb, frame, frame_size); - comm_session_send_buffer_end_write(sb); -} - -void audio_endpoint_cancel_transfer(AudioEndpointSessionId session_id) { - PBL_ASSERTN(session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); - - if (s_session.id != session_id) { - return; - } - - prv_session_deinit(false /* call_stop_handler */); -} - -void audio_endpoint_stop_transfer(AudioEndpointSessionId session_id) { - PBL_ASSERTN(session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); - - if (s_session.id != session_id) { - return; - } - - StopTransferMsg msg = (const StopTransferMsg) { - .msg_id = MsgIdStopTransfer, - .session_id = session_id, - }; - - prv_session_deinit(false /* call_stop_handler */); - - comm_session_send_data(comm_session_get_system_session(), AUDIO_ENDPOINT, (const uint8_t *) &msg, - sizeof(msg), COMM_SESSION_DEFAULT_TIMEOUT); -} diff --git a/src/fw/services/normal/audio_endpoint.h b/src/fw/services/normal/audio_endpoint.h deleted file mode 100644 index bac0e0510a..0000000000 --- a/src/fw/services/normal/audio_endpoint.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Endpoint for transferring audio data between the watch and phone -//! https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=491698 - -//! Session identifier passed to endpoint functions -typedef uint16_t AudioEndpointSessionId; -#define AUDIO_ENDPOINT_SESSION_INVALID_ID (0) - -//! Function signature of the callback to handle stop transfer message received from phone -typedef void (*AudioEndpointStopTransferCallback)(AudioEndpointSessionId session_id); - -//! Function signature of the callback to handle the completion of the setup process. -//! After this point, the client may start adding audio frames using audio_endpoint_add_frame. -typedef void (*AudioEndpointSetupCompleteCallback)(AudioEndpointSessionId session_id); - -//! Create a session for transferring audio data from watch to phone -//! @param setup_completed Callback to handle the completion of the audio endpoint setup process. -//! @param stop_transfer Callback to handle stop transfer message received from phone. -//! @return Session identifier to pass to other endpoint functions -AudioEndpointSessionId audio_endpoint_setup_transfer( - AudioEndpointSetupCompleteCallback setup_completed, - AudioEndpointStopTransferCallback stop_transfer); - -//! Add a frame of audio data to session's internal buffer -//! @param session_id Session identifier returned by audio_endpoint_start_transfer -//! @param frame Pointer to frame of encoded audio data -//! @param frame_size Size of frame of encoded audio data in bytes -void audio_endpoint_add_frame(AudioEndpointSessionId session_id, uint8_t *frame, - uint8_t frame_size); - -//! Stop transferring audio data from watch to phone -//! @param session_id Session identifier returned by audio_endpoint_setup_transfer -void audio_endpoint_stop_transfer(AudioEndpointSessionId session_id); - -//! Cancel a transfer session without sending a stop transfer message -//! @param session_id Session identifier returned by audio_endpoint_setup_transfer -void audio_endpoint_cancel_transfer(AudioEndpointSessionId session_id); diff --git a/src/fw/services/normal/audio_endpoint_private.h b/src/fw/services/normal/audio_endpoint_private.h deleted file mode 100644 index 76adc94a19..0000000000 --- a/src/fw/services/normal/audio_endpoint_private.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" - -#include -#include - -typedef enum { - MsgIdDataTransfer = 0x02, - MsgIdStopTransfer = 0x03, -} MsgId; - -typedef struct PACKED { - MsgId msg_id; - AudioEndpointSessionId session_id; - uint8_t frame_count; - uint8_t frames[]; -} DataTransferMsg; - -typedef struct PACKED { - MsgId msg_id; - AudioEndpointSessionId session_id; -} StopTransferMsg; diff --git a/src/fw/services/normal/blob_db/api.c b/src/fw/services/normal/blob_db/api.c deleted file mode 100644 index d694856e09..0000000000 --- a/src/fw/services/normal/blob_db/api.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "api.h" - -#include -#include - -#include "app_db.h" -#include "app_glance_db.h" -#include "contacts_db.h" -#include "health_db.h" -#include "ios_notif_pref_db.h" -#include "notif_db.h" -#include "pin_db.h" -#include "prefs_db.h" -#include "reminder_db.h" -#include "watch_app_prefs_db.h" -#include "weather_db.h" - -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" - -typedef struct { - BlobDBInitImpl init; - BlobDBInsertImpl insert; - BlobDBGetLenImpl get_len; - BlobDBReadImpl read; - BlobDBDeleteImpl del; - BlobDBFlushImpl flush; - BlobDBIsDirtyImpl is_dirty; - BlobDBGetDirtyListImpl get_dirty_list; - BlobDBMarkSyncedImpl mark_synced; - bool disabled; -} BlobDB; - -static const BlobDB s_blob_dbs[NumBlobDBs] = { - [BlobDBIdPins] = { - .init = pin_db_init, - .insert = pin_db_insert, - .get_len = pin_db_get_len, - .read = pin_db_read, - .del = pin_db_delete, - .flush = pin_db_flush, - .is_dirty = pin_db_is_dirty, - .get_dirty_list = pin_db_get_dirty_list, - .mark_synced = pin_db_mark_synced, - }, - [BlobDBIdApps] = { - .init = app_db_init, - .insert = app_db_insert, - .get_len = app_db_get_len, - .read = app_db_read, - .del = app_db_delete, - .flush = app_db_flush, - }, - [BlobDBIdReminders] = { - .init = reminder_db_init, - .insert = reminder_db_insert, - .get_len = reminder_db_get_len, - .read = reminder_db_read, - .del = reminder_db_delete, - .flush = reminder_db_flush, - .is_dirty = reminder_db_is_dirty, - .get_dirty_list = reminder_db_get_dirty_list, - .mark_synced = reminder_db_mark_synced, - }, - [BlobDBIdNotifs] = { - .init = notif_db_init, - .insert = notif_db_insert, - .get_len = notif_db_get_len, - .read = notif_db_read, - .del = notif_db_delete, - .flush = notif_db_flush, - }, - [BlobDBIdWeather] = { -#if CAPABILITY_HAS_WEATHER - .init = weather_db_init, - .insert = weather_db_insert, - .get_len = weather_db_get_len, - .read = weather_db_read, - .del = weather_db_delete, - .flush = weather_db_flush, -#else - .disabled = true, -#endif - }, - [BlobDBIdiOSNotifPref] = { - .init = ios_notif_pref_db_init, - .insert = ios_notif_pref_db_insert, - .get_len = ios_notif_pref_db_get_len, - .read = ios_notif_pref_db_read, - .del = ios_notif_pref_db_delete, - .flush = ios_notif_pref_db_flush, - .is_dirty = ios_notif_pref_db_is_dirty, - .get_dirty_list = ios_notif_pref_db_get_dirty_list, - .mark_synced = ios_notif_pref_db_mark_synced, - }, - [BlobDBIdPrefs] = { - .init = prefs_db_init, - .insert = prefs_db_insert, - .get_len = prefs_db_get_len, - .read = prefs_db_read, - .del = prefs_db_delete, - .flush = prefs_db_flush, - }, - [BlobDBIdContacts] = { -#if !PLATFORM_TINTIN - .init = contacts_db_init, - .insert = contacts_db_insert, - .get_len = contacts_db_get_len, - .read = contacts_db_read, - .del = contacts_db_delete, - .flush = contacts_db_flush, -#else - // Disabled on tintin for code savings - .disabled = true, -#endif - }, - [BlobDBIdWatchAppPrefs] = { -#if !PLATFORM_TINTIN - .init = watch_app_prefs_db_init, - .insert = watch_app_prefs_db_insert, - .get_len = watch_app_prefs_db_get_len, - .read = watch_app_prefs_db_read, - .del = watch_app_prefs_db_delete, - .flush = watch_app_prefs_db_flush, -#else - // Disabled on tintin for code savings - .disabled = true, -#endif - }, - [BlobDBIdHealth] = { -#if CAPABILITY_HAS_HEALTH_TRACKING - .init = health_db_init, - .insert = health_db_insert, - .get_len = health_db_get_len, - .read = health_db_read, - .del = health_db_delete, - .flush = health_db_flush, -#else - .disabled = true, -#endif - }, - [BlobDBIdAppGlance] = { -#if CAPABILITY_HAS_APP_GLANCES - .init = app_glance_db_init, - .insert = app_glance_db_insert, - .get_len = app_glance_db_get_len, - .read = app_glance_db_read, - .del = app_glance_db_delete, - .flush = app_glance_db_flush, -#else - .disabled = true, -#endif - }, -}; - -static bool prv_db_valid(BlobDBId db_id) { - return (db_id < NumBlobDBs) && (!s_blob_dbs[db_id].disabled); - -} - -void blob_db_event_put(BlobDBEventType type, BlobDBId db_id, const uint8_t *key, int key_len) { - // copy key for event - uint8_t *key_bytes = NULL; - if (key_len > 0) { - key_bytes = kernel_malloc(key_len); - memcpy(key_bytes, key, key_len); - } - - PebbleEvent e = { - .type = PEBBLE_BLOBDB_EVENT, - .blob_db = { - .db_id = db_id, - .type = type, - .key = key_bytes, - .key_len = (uint8_t)key_len, - } - }; - event_put(&e); -} - -void blob_db_init_dbs(void) { - const BlobDB *db = s_blob_dbs; - for (int i = 0; i < NumBlobDBs; ++i, ++db) { - if (db->init) { - db->init(); - } - } -} - -void blob_db_get_dirty_dbs(uint8_t *ids, uint8_t *num_ids) { - const BlobDB *db = s_blob_dbs; - *num_ids = 0; - for (uint8_t i = 0; i < NumBlobDBs; ++i, ++db) { - bool is_dirty = false; - if (db->is_dirty && (db->is_dirty(&is_dirty) == S_SUCCESS) && is_dirty) { - ids[*num_ids] = i; - *num_ids += 1; - } - } -} - -status_t blob_db_insert(BlobDBId db_id, - const uint8_t *key, int key_len, const uint8_t *val, int val_len) { - if (!prv_db_valid(db_id)) { - return E_RANGE; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->insert) { - status_t rv = db->insert(key, key_len, val, val_len); - if (rv == S_SUCCESS) { - blob_db_event_put(BlobDBEventTypeInsert, db_id, key, key_len); - } - - return rv; - } - - return E_INVALID_OPERATION; -} - -int blob_db_get_len(BlobDBId db_id, - const uint8_t *key, int key_len) { - if (!prv_db_valid(db_id)) { - return E_RANGE; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->get_len) { - return db->get_len(key, key_len); - } - - return E_INVALID_OPERATION; -} - -status_t blob_db_read(BlobDBId db_id, - const uint8_t *key, int key_len, uint8_t *val_out, int val_len) { - if (!prv_db_valid(db_id)) { - return E_RANGE; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->read) { - return db->read(key, key_len, val_out, val_len); - } - - return E_INVALID_OPERATION; -} - -status_t blob_db_delete(BlobDBId db_id, - const uint8_t *key, int key_len) { - if (!prv_db_valid(db_id)) { - return E_RANGE; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->del) { - status_t rv = db->del(key, key_len); - if (rv == S_SUCCESS) { - blob_db_event_put(BlobDBEventTypeDelete, db_id, key, key_len); - } - return rv; - } - - return E_INVALID_OPERATION; -} - -status_t blob_db_flush(BlobDBId db_id) { - if (!prv_db_valid(db_id)) { - return E_RANGE; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->flush) { - status_t rv = db->flush(); - if (rv == S_SUCCESS) { - PBL_LOG(LOG_LEVEL_INFO, "Flushing BlobDB with Id %d", db_id); - blob_db_event_put(BlobDBEventTypeFlush, db_id, NULL, 0); - } - return rv; - } - - return E_INVALID_OPERATION; -} - -BlobDBDirtyItem *blob_db_get_dirty_list(BlobDBId db_id) { - if (!prv_db_valid(db_id)) { - return NULL; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->get_dirty_list) { - return db->get_dirty_list(); - } - - return NULL; -} - -status_t blob_db_mark_synced(BlobDBId db_id, uint8_t *key, int key_len) { - if (!prv_db_valid(db_id)) { - return E_RANGE; - } - - const BlobDB *db = &s_blob_dbs[db_id]; - if (db->mark_synced) { - status_t rv = db->mark_synced(key, key_len); - // TODO event? - return rv; - } - - return E_INVALID_OPERATION; -} diff --git a/src/fw/services/normal/blob_db/api_types.h b/src/fw/services/normal/blob_db/api_types.h deleted file mode 100644 index e1791d25fb..0000000000 --- a/src/fw/services/normal/blob_db/api_types.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef enum BlobDBEventType { - BlobDBEventTypeInsert, - BlobDBEventTypeDelete, - BlobDBEventTypeFlush, -} BlobDBEventType; diff --git a/src/fw/services/normal/blob_db/app_db.h b/src/fw/services/normal/blob_db/app_db.h deleted file mode 100644 index bc1b0959f7..0000000000 --- a/src/fw/services/normal/blob_db/app_db.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "util/uuid.h" -#include "process_management/app_install_manager.h" -#include "process_management/pebble_process_info.h" -#include "system/status_codes.h" -#include "util/attributes.h" -#include "util/list.h" - - -//! App database entry for BlobDB. First pass is very basic. The list will expand as more features -//! and requirements are implemented. -typedef struct PACKED { - Uuid uuid; - uint32_t info_flags; - uint32_t icon_resource_id; - Version app_version; - Version sdk_version; - GColor8 app_face_bg_color; - uint8_t template_id; - char name[APP_NAME_SIZE_BYTES]; -} AppDBEntry; - -//! Used in app_db_enumerate_entries -typedef void(*AppDBEnumerateCb)(AppInstallId install_id, AppDBEntry *entry, void *data); - -/* AppDB Functions */ - -int32_t app_db_get_next_unique_id(void); - -AppInstallId app_db_get_install_id_for_uuid(const Uuid *uuid); - -status_t app_db_get_app_entry_for_uuid(const Uuid *uuid, AppDBEntry *entry); - -status_t app_db_get_app_entry_for_install_id(AppInstallId app_id, AppDBEntry *entry); - -void app_db_enumerate_entries(AppDBEnumerateCb cb, void *data); - -/* AppDB AppInstallId Implementation */ - -bool app_db_exists_install_id(AppInstallId app_id); - -/* BlobDB Implementation */ - -void app_db_init(void); - -status_t app_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int app_db_get_len(const uint8_t *key, int key_len); - -status_t app_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t app_db_delete(const uint8_t *key, int key_len); - -status_t app_db_flush(void); - -/* TEST */ -AppInstallId app_db_check_next_unique_id(void); diff --git a/src/fw/services/normal/blob_db/app_glance_db.h b/src/fw/services/normal/blob_db/app_glance_db.h deleted file mode 100644 index 72d513c7e5..0000000000 --- a/src/fw/services/normal/blob_db/app_glance_db.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/app_glances/app_glance_service.h" -#include "system/status_codes.h" -#include "util/attributes.h" -#include "util/time/time.h" -#include "util/uuid.h" - -#include - -// ------------------------------------------------------------------------------------------------- -// AppGlanceDB Implementation - -status_t app_glance_db_insert_glance(const Uuid *uuid, const AppGlance *glance); - -status_t app_glance_db_read_glance(const Uuid *uuid, AppGlance *glance_out); - -status_t app_glance_db_read_creation_time(const Uuid *uuid, time_t *time_out); - -status_t app_glance_db_delete_glance(const Uuid *uuid); - -// ------------------------------------------------------------------------------------------------- -// BlobDB API Implementation - -void app_glance_db_init(void); - -status_t app_glance_db_flush(void); - -status_t app_glance_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int app_glance_db_get_len(const uint8_t *key, int key_len); - -status_t app_glance_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t app_glance_db_delete(const uint8_t *key, int key_len); diff --git a/src/fw/services/normal/blob_db/app_glance_db_private.h b/src/fw/services/normal/blob_db/app_glance_db_private.h deleted file mode 100644 index ff965901e0..0000000000 --- a/src/fw/services/normal/blob_db/app_glance_db_private.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/attribute_private.h" -#include "util/attributes.h" - -#define APP_GLANCE_DB_CURRENT_VERSION (1) - -//! This number is reduced for unit tests to avoid creating large glance payloads in the unit tests -#if UNITTEST -#define APP_GLANCE_DB_MAX_SLICES_PER_GLANCE (2) -#else -#define APP_GLANCE_DB_MAX_SLICES_PER_GLANCE (8) -#endif - -#define APP_GLANCE_DB_MAX_NUM_APP_GLANCES (50) - -typedef struct PACKED SerializedAppGlanceHeader { - uint8_t version; - uint32_t creation_time; - uint8_t data[]; // Serialized slices -} SerializedAppGlanceHeader; - -typedef struct PACKED SerializedAppGlanceSliceHeader { - uint16_t total_size; - uint8_t type; - uint8_t num_attributes; - uint8_t data[]; // Serialized attributes -} SerializedAppGlanceSliceHeader; - -//! The minimum size of an AppGlanceSliceType_IconAndSubtitle slice is the size of the header plus -//! the expiration_time because the icon and subtitle are optional -#define APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MIN_SIZE \ - (sizeof(SerializedAppGlanceSliceHeader) + sizeof(SerializedAttributeHeader) + sizeof(uint32_t)) -//! The maximum size of an AppGlanceSliceType_IconAndSubtitle slice is the size of the header plus -//! the expiration_time, icon resource ID, and subtitle string attributes (+1 added for null char) -#define APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MAX_SIZE \ - (sizeof(SerializedAppGlanceSliceHeader) + (sizeof(SerializedAttributeHeader) * 3) + \ - sizeof(uint32_t) + sizeof(uint32_t) + ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1) - -#define APP_GLANCE_DB_SLICE_MIN_SIZE (APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MIN_SIZE) -#define APP_GLANCE_DB_SLICE_MAX_SIZE (APP_GLANCE_DB_ICON_AND_SUBTITLE_SLICE_MAX_SIZE) diff --git a/src/fw/services/normal/blob_db/contacts_db.h b/src/fw/services/normal/blob_db/contacts_db.h deleted file mode 100644 index e6d9331d1a..0000000000 --- a/src/fw/services/normal/blob_db/contacts_db.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "system/status_codes.h" -#include "util/attributes.h" -#include "util/uuid.h" - -typedef struct PACKED { - Uuid uuid; - uint32_t flags; - uint8_t num_attributes; - uint8_t num_addresses; - uint8_t data[]; // Serialized attributes followed by serialized addresses -} SerializedContact; - -//! Given a contact's uuid, return the serialized data for that contact. This should probably only -//! be called by the contacts service. You probably want contacts_get_contact_by_uuid() instead -//! @param uuid The contact's uuid. -//! @param contact_out A pointer to the serialized contact data, NULL if the contact isn't found. -//! @return The length of the data[] field. -//! @note The caller must cleanup with contacts_db_free_serialized_contact(). -int contacts_db_get_serialized_contact(const Uuid *uuid, SerializedContact **contact_out); - -//! Frees the serialized contact data returned by contacts_db_get_serialized_contact(). -void contacts_db_free_serialized_contact(SerializedContact *contact); - - -/////////////////////////////////////////// -// BlobDB Boilerplate (see blob_db/api.h) -/////////////////////////////////////////// - -void contacts_db_init(void); - -status_t contacts_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int contacts_db_get_len(const uint8_t *key, int key_len); - -status_t contacts_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t contacts_db_delete(const uint8_t *key, int key_len); - -status_t contacts_db_flush(void); diff --git a/src/fw/services/normal/blob_db/endpoint.c b/src/fw/services/normal/blob_db/endpoint.c deleted file mode 100644 index 60abbcd7f0..0000000000 --- a/src/fw/services/normal/blob_db/endpoint.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sync.h" -#include "endpoint_private.h" - -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/session.h" -#include "services/common/analytics/analytics.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "system/hexdump.h" -#include "util/attributes.h" -#include "util/net.h" - -#include -#include -#include - -//! @file endpoint.c -//! BlobDB Endpoint -//! -//! There are 3 commands implemented in this endpoint: INSERT, DELETE, and CLEAR -//! -//! INSERT: This command will insert a key and value into the database specified. -//! -//! \code{.c} -//! 0x01 -//! key_bytes> -//! value_bytes> -//! \endcode -//! -//! DELETE: This command will delete an entry with the key in the database specified. -//! -//! \code{.c} -//! 0x04 -//! key_bytes> -//! \endcode -//! -//! CLEAR: This command will clear all entries in the database specified. -//! -//! \code{.c} -//! 0x05 -//! \endcode - -//! BlobDB Endpoint ID -static const uint16_t BLOB_DB_ENDPOINT_ID = 0xb1db; - -static const uint8_t KEY_DATA_LENGTH __attribute__((unused)) = (sizeof(uint8_t) + sizeof(uint8_t)); -static const uint8_t VALUE_DATA_LENGTH = (sizeof(uint16_t) + sizeof(uint8_t)); - -//! Message Length Constants -static const uint8_t MIN_INSERT_LENGTH = 8; -static const uint8_t MIN_DELETE_LENGTH = 6; -static const uint8_t MIN_CLEAR_LENGTH = 3; - -static bool s_bdb_accepting_messages; - -static void prv_send_response(CommSession *session, BlobDBToken token, BlobDBResponse result) { - struct PACKED BlobDBResponseMsg { - BlobDBToken token; - BlobDBResponse result; - } response = { - .token = token, - .result = result, - }; - - comm_session_send_data(session, BLOB_DB_ENDPOINT_ID, (uint8_t*)&response, sizeof(response), - COMM_SESSION_DEFAULT_TIMEOUT); -} - -static BlobDBResponse prv_interpret_db_ret_val(status_t ret_val) { - switch (ret_val) { - case S_SUCCESS: - return BLOB_DB_SUCCESS; - case E_DOES_NOT_EXIST: - return BLOB_DB_KEY_DOES_NOT_EXIST; - case E_RANGE: - return BLOB_DB_INVALID_DATABASE_ID; - case E_INVALID_ARGUMENT: - return BLOB_DB_INVALID_DATA; - case E_OUT_OF_STORAGE: - return BLOB_DB_DATABASE_FULL; - case E_INVALID_OPERATION: - return BLOB_DB_DATA_STALE; - default: - PBL_LOG(LOG_LEVEL_WARNING, "BlobDB return value caught by default case"); - return BLOB_DB_GENERAL_FAILURE; - } -} - -static const uint8_t *prv_read_ptr(const uint8_t *iter, const uint8_t *iter_end, - const uint8_t **out_buf, uint16_t buf_len) { - - // >= because we will be reading more bytes after this point - if ((buf_len == 0) || ((iter + buf_len) > iter_end)) { - PBL_LOG(LOG_LEVEL_WARNING, "BlobDB: read invalid length"); - return NULL; - } - - // grab pointer to start of buf - *out_buf = (uint8_t*)iter; - iter += buf_len; - - return iter; -} - -static const uint8_t *prv_read_key_size(const uint8_t *iter, const uint8_t *iter_end, - uint8_t *out_int) { - - // copy length from iter to out_len, then save a local copy of the length - *out_int = *iter++; - return iter; -} - -static const uint8_t *prv_read_value_size(const uint8_t *iter, const uint8_t *iter_end, - uint16_t *out_int) { - - // copy length from iter to out_len, then save a local copy of the length - *out_int = *(uint16_t*)iter; - iter += sizeof(uint16_t); - return iter; -} - -static BlobDBToken prv_try_read_token(const uint8_t *data, uint32_t length) { - if (length < sizeof(BlobDBToken)) { - return 0; - } - - return *(BlobDBToken*)data; -} - -static void prv_handle_database_insert(CommSession *session, const uint8_t *data, uint32_t length) { - if (length < MIN_INSERT_LENGTH) { - prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); - return; - } - - const uint8_t *iter = data; - BlobDBToken token; - BlobDBId db_id; - - // Read token and db_id - iter = endpoint_private_read_token_db_id(iter, &token, &db_id); - - // read key length and key bytes ptr - uint8_t key_size; - const uint8_t *key_bytes = NULL; - iter = prv_read_key_size(iter, data + length, &key_size); - iter = prv_read_ptr(iter, data + length, &key_bytes, key_size); - - // If read past end or there is not enough data left in buffer for a value size and data to exist - if (!iter || (iter > (data + length - VALUE_DATA_LENGTH))) { - prv_send_response(session, token, BLOB_DB_INVALID_DATA); - return; - } - - // read value length and value bytes ptr - uint16_t value_size; - const uint8_t *value_bytes = NULL; - iter = prv_read_value_size(iter, data + length, &value_size); - iter = prv_read_ptr(iter, data + length, &value_bytes, value_size); - - // If we read too many bytes or didn't read all the bytes (2nd test) - if (!iter || (iter != (data + length))) { - prv_send_response(session, token, BLOB_DB_INVALID_DATA); - return; - } - - // perform action on database and return result - status_t ret = blob_db_insert(db_id, key_bytes, key_size, value_bytes, value_size); - prv_send_response(session, token, prv_interpret_db_ret_val(ret)); -} - - -static void prv_handle_database_delete(CommSession *session, const uint8_t *data, uint32_t length) { - if (length < MIN_DELETE_LENGTH) { - prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); - return; - } - - const uint8_t *iter = data; - BlobDBToken token; - BlobDBId db_id; - - // Read token and db_id - iter = endpoint_private_read_token_db_id(iter, &token, &db_id); - - // Read key length and key bytes - uint8_t key_size; - const uint8_t *key_bytes = NULL; - iter = prv_read_key_size(iter, data + length, &key_size); - iter = prv_read_ptr(iter, data + length, &key_bytes, key_size); - - // If we read too many bytes or key_size is 0 or didn't read all the bytes - if (!iter || (iter != (data + length))) { - prv_send_response(session, token, BLOB_DB_INVALID_DATA); - return; - } - - // perform action on database and return result - status_t ret = blob_db_delete(db_id, key_bytes, key_size); - prv_send_response(session, token, prv_interpret_db_ret_val(ret)); -} - - -static void prv_handle_database_clear(CommSession *session, const uint8_t *data, uint32_t length) { - if (length < MIN_CLEAR_LENGTH) { - prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_INVALID_DATA); - return; - } - - BlobDBToken token; - BlobDBId db_id; - - // Read token and db_id - endpoint_private_read_token_db_id(data, &token, &db_id); - - // perform action on database and return result - status_t ret = blob_db_flush(db_id); - prv_send_response(session, token, prv_interpret_db_ret_val(ret)); - - // Mark the device as faithful after successfully flushing - if (ret == S_SUCCESS) { - bt_persistent_storage_set_unfaithful(false /* We are now faithful */); - } -} - -static void prv_blob_db_msg_decode_and_handle( - CommSession *session, BlobDBCommand cmd, const uint8_t *data, size_t data_length) { - switch (cmd) { - case BLOB_DB_COMMAND_INSERT: - PBL_LOG(LOG_LEVEL_DEBUG, "Got INSERT"); - prv_handle_database_insert(session, data, data_length); - break; - case BLOB_DB_COMMAND_DELETE: - PBL_LOG(LOG_LEVEL_DEBUG, "Got DELETE"); - prv_handle_database_delete(session, data, data_length); - break; - case BLOB_DB_COMMAND_CLEAR: - PBL_LOG(LOG_LEVEL_DEBUG, "Got CLEAR"); - prv_handle_database_clear(session, data, data_length); - break; - // Commands not implemented. - case BLOB_DB_COMMAND_READ: - case BLOB_DB_COMMAND_UPDATE: - PBL_LOG(LOG_LEVEL_ERROR, "BlobDB Command not implemented"); - // Fallthrough - default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid BlobDB message received, cmd is %u", cmd); - prv_send_response(session, prv_try_read_token(data, data_length), BLOB_DB_INVALID_OPERATION); - break; - } -} - -void blob_db_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - - analytics_inc(ANALYTICS_DEVICE_METRIC_BLOB_DB_EVENT_COUNT, AnalyticsClient_System); - - PBL_HEXDUMP_D(LOG_DOMAIN_BLOBDB, LOG_LEVEL_DEBUG, data, length); - - // Each BlobDB message is required to have at least a Command and a Token - static const uint8_t MIN_RAW_DATA_LEN = sizeof(BlobDBCommand) + sizeof(BlobDBToken); - if (length < MIN_RAW_DATA_LEN) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a blob_db message that was too short, len: %zu", length); - prv_send_response(session, 0, BLOB_DB_INVALID_DATA); - return; - } - - const BlobDBCommand cmd = *data; - data += sizeof(BlobDBCommand); // fwd to message contents - const size_t data_length = length - sizeof(BlobDBCommand); - - if (!s_bdb_accepting_messages) { - prv_send_response(session, prv_try_read_token(data, length), BLOB_DB_TRY_LATER); - return; - } - - prv_blob_db_msg_decode_and_handle(session, cmd, data, data_length); -} - -void blob_db_set_accepting_messages(bool enabled) { - s_bdb_accepting_messages = enabled; -} diff --git a/src/fw/services/normal/blob_db/endpoint.h b/src/fw/services/normal/blob_db/endpoint.h deleted file mode 100644 index 803532cfe1..0000000000 --- a/src/fw/services/normal/blob_db/endpoint.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "endpoint_private.h" - -//! Send a write message for the given blob db item. -//! @returns the blob db transaction token -BlobDBToken blob_db_endpoint_send_write(BlobDBId db_id, - time_t last_updated, - const void *key, - int key_len, - const void *val, - int val_len); - -//! Send a WB message for the given blob db item. -//! @returns the blob db transaction token -BlobDBToken blob_db_endpoint_send_writeback(BlobDBId db_id, - time_t last_updated, - const void *key, - int key_len, - const void *val, - int val_len); - -//! Indicate that blob db sync is done for a given db id -void blob_db_endpoint_send_sync_done(BlobDBId db_id); diff --git a/src/fw/services/normal/blob_db/endpoint2.c b/src/fw/services/normal/blob_db/endpoint2.c deleted file mode 100644 index 44b7ff0245..0000000000 --- a/src/fw/services/normal/blob_db/endpoint2.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "api.h" -#include "sync.h" -#include "endpoint_private.h" - -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/analytics/analytics.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/status_codes.h" -#include "system/hexdump.h" -#include "util/attributes.h" -#include "util/net.h" - -#include -#include -#include - -//! BlobDB Endpoint ID -static const uint16_t BLOB_DB2_ENDPOINT_ID = 0xb2db; - -//! Message Length Constants -static const uint8_t DIRTY_DATABASES_LENGTH = 2; -static const uint8_t START_SYNC_LENGTH = 3; -static const uint8_t WRITE_RESPONSE_LENGTH = 3; -static const uint8_t WRITEBACK_RESPONSE_LENGTH = 3; -static const uint8_t SYNC_DONE_RESPONSE_LENGTH = 3; - -static bool s_b2db_accepting_messages; - -T_STATIC BlobDBToken prv_new_token(void) { - static BlobDBToken next_token = 1; // 0 token should be avoided - return next_token++; -} - -static const uint8_t *prv_read_token_and_response(const uint8_t *iter, BlobDBToken *out_token, - BlobDBResponse *out_response) { - *out_token = *(BlobDBToken *)iter; - iter += sizeof(BlobDBToken); - *out_response = *(BlobDBResponse *)iter; - iter += sizeof(BlobDBResponse); - - return iter; -} - -T_STATIC void prv_send_response(CommSession *session, uint8_t *response, - uint8_t response_length) { - comm_session_send_data(session, BLOB_DB2_ENDPOINT_ID, response, response_length, - COMM_SESSION_DEFAULT_TIMEOUT); -} - -static void prv_handle_get_dirty_databases(CommSession *session, - const uint8_t *data, - uint32_t length) { - if (length < DIRTY_DATABASES_LENGTH) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a dirty databases with an invalid length: %"PRIu32"", length); - return; - } - - struct PACKED DirtyDatabasesResponseMsg { - BlobDBCommand cmd; - BlobDBToken token; - BlobDBResponse result; - uint8_t num_ids; - BlobDBId db_ids[NumBlobDBs]; - } response = { - .cmd = BLOB_DB_COMMAND_DIRTY_DBS_RESPONSE, - .token = *(BlobDBToken *)data, - .result = BLOB_DB_SUCCESS, - }; - - - blob_db_get_dirty_dbs(response.db_ids, &response.num_ids); - // we don't want to send the extra bytes in response.db_ids - int num_empty_ids = NumBlobDBs - response.num_ids; - - prv_send_response(session, (uint8_t *) &response, sizeof(response) - num_empty_ids); -} - -static void prv_handle_start_sync(CommSession *session, - const uint8_t *data, - uint32_t length) { - if (length < START_SYNC_LENGTH) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a start sync with an invalid length: %"PRIu32"", length); - return; - } - - struct PACKED StartSyncResponseMsg { - BlobDBCommand cmd; - BlobDBToken token; - BlobDBResponse result; - } response = { - .cmd = BLOB_DB_COMMAND_START_SYNC_RESPONSE, - }; - - BlobDBId db_id; - endpoint_private_read_token_db_id(data, &response.token, &db_id); - - status_t rv = blob_db_sync_db(db_id); - switch (rv) { - case S_SUCCESS: - case S_NO_ACTION_REQUIRED: - response.result = BLOB_DB_SUCCESS; - break; - case E_INVALID_ARGUMENT: - response.result = BLOB_DB_INVALID_DATABASE_ID; - break; - case E_BUSY: - response.result = BLOB_DB_TRY_LATER; - break; - default: - response.result = BLOB_DB_GENERAL_FAILURE; - break; - } - - prv_send_response(session, (uint8_t *)&response, sizeof(response)); -} - -static void prv_handle_wb_write_response(const uint8_t *data, - uint32_t length) { - // read token and response code - BlobDBToken token; - BlobDBResponse response_code; - prv_read_token_and_response(data, &token, &response_code); - - BlobDBSyncSession *sync_session = blob_db_sync_get_session_for_token(token); - if (sync_session) { - if (response_code == BLOB_DB_SUCCESS) { - blob_db_sync_next(sync_session); - } else { - blob_db_sync_cancel(sync_session); - } - } else { - // No session - PBL_LOG(LOG_LEVEL_WARNING, "received blob db wb response with an invalid token: %d", token); - } -} - -static void prv_handle_write_response(CommSession *session, - const uint8_t *data, - uint32_t length) { - if (length < WRITE_RESPONSE_LENGTH) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a write response with an invalid length: %"PRIu32"", length); - return; - } - - prv_handle_wb_write_response(data, length); -} - -static void prv_handle_wb_response(CommSession *session, - const uint8_t *data, - uint32_t length) { - if (length < WRITEBACK_RESPONSE_LENGTH) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a writeback response with an invalid length: %"PRIu32"", length); - return; - } - - prv_handle_wb_write_response(data, length); -} - -static void prv_handle_sync_done_response(CommSession *session, - const uint8_t *data, - uint32_t length) { - if (length < SYNC_DONE_RESPONSE_LENGTH) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a sync done response with an invalid length: %"PRIu32"", length); - return; - } - - // read token and response code - BlobDBToken token; - BlobDBResponse response_code; - prv_read_token_and_response(data, &token, &response_code); - - if (response_code != BLOB_DB_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Sync Done response error: %d", response_code); - } -} - -static void prv_send_error_response(CommSession *session, - BlobDBCommand cmd, - const uint8_t *data, - BlobDBResponse response_code) { - struct PACKED ErrorResponseMsg { - BlobDBCommand cmd; - BlobDBToken token; - BlobDBResponse result; - } response = { - .cmd = cmd | RESPONSE_MASK, - .token = *(BlobDBToken *)data, - .result = response_code, - }; - - prv_send_response(session, (uint8_t *)&response, sizeof(response)); -} - -static void prv_blob_db_msg_decode_and_handle( - CommSession *session, BlobDBCommand cmd, const uint8_t *data, size_t data_length) { - switch (cmd) { - case BLOB_DB_COMMAND_DIRTY_DBS: - PBL_LOG(LOG_LEVEL_DEBUG, "Got DIRTY DBs"); - prv_handle_get_dirty_databases(session, data, data_length); - break; - case BLOB_DB_COMMAND_START_SYNC: - PBL_LOG(LOG_LEVEL_DEBUG, "Got SYNC"); - prv_handle_start_sync(session, data, data_length); - break; - case BLOB_DB_COMMAND_WRITE_RESPONSE: - PBL_LOG(LOG_LEVEL_DEBUG, "WRITE Response"); - prv_handle_write_response(session, data, data_length); - break; - case BLOB_DB_COMMAND_WRITEBACK_RESPONSE: - PBL_LOG(LOG_LEVEL_DEBUG, "WRITEBACK Response"); - prv_handle_wb_response(session, data, data_length); - break; - case BLOB_DB_COMMAND_SYNC_DONE_RESPONSE: - PBL_LOG(LOG_LEVEL_DEBUG, "SYNC DONE Response"); - prv_handle_sync_done_response(session, data, data_length); - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Invalid BlobDB2 message received, cmd is %u", cmd); - prv_send_error_response(session, cmd, data, BLOB_DB_INVALID_OPERATION); - break; - } -} - -static uint16_t prv_send_write_writeback(BlobDBCommand cmd, - BlobDBId db_id, - time_t last_updated, - const uint8_t *key, - int key_len, - const uint8_t *val, - int val_len) { - struct PACKED WritebackMetadata { - BlobDBCommand cmd; - BlobDBToken token; - BlobDBId db_id; - uint32_t last_updated; - } writeback_metadata = { - .cmd = cmd, - .token = prv_new_token(), - .db_id = db_id, - .last_updated = last_updated, - }; - - size_t writeback_length = sizeof(writeback_metadata) + - sizeof(uint8_t) /* key length size*/ + - key_len + - sizeof(uint16_t) /* val length size */ + - val_len; - - SendBuffer *sb = comm_session_send_buffer_begin_write(comm_session_get_system_session(), - BLOB_DB2_ENDPOINT_ID, - writeback_length, - COMM_SESSION_DEFAULT_TIMEOUT); - if (sb) { - comm_session_send_buffer_write(sb, (uint8_t *)&writeback_metadata, sizeof(writeback_metadata)); - comm_session_send_buffer_write(sb, (uint8_t *)&key_len, sizeof(uint8_t)); - comm_session_send_buffer_write(sb, key, key_len); - comm_session_send_buffer_write(sb, (uint8_t *)&val_len, sizeof(uint16_t)); - comm_session_send_buffer_write(sb, val, val_len); - comm_session_send_buffer_end_write(sb); - } - - return writeback_metadata.token; -} - -BlobDBToken blob_db_endpoint_send_write(BlobDBId db_id, - time_t last_updated, - const void *key, - int key_len, - const void *val, - int val_len) { - BlobDBToken token = prv_send_write_writeback(BLOB_DB_COMMAND_WRITE, db_id, last_updated, - key, key_len, val, val_len); - - return token; -} - -BlobDBToken blob_db_endpoint_send_writeback(BlobDBId db_id, - time_t last_updated, - const void *key, - int key_len, - const void *val, - int val_len) { - BlobDBToken token = prv_send_write_writeback(BLOB_DB_COMMAND_WRITEBACK, db_id, last_updated, - key, key_len, val, val_len); - - return token; -} - -void blob_db_endpoint_send_sync_done(BlobDBId db_id) { - struct PACKED SyncDoneMsg { - BlobDBCommand cmd; - BlobDBToken token; - BlobDBId db_id; - } msg = { - .cmd = BLOB_DB_COMMAND_SYNC_DONE, - .token = prv_new_token(), - .db_id = db_id, - }; - - PBL_LOG(LOG_LEVEL_DEBUG, "Sending sync done for db: %d", db_id); - - comm_session_send_data(comm_session_get_system_session(), - BLOB_DB2_ENDPOINT_ID, - (uint8_t *)&msg, - sizeof(msg), - COMM_SESSION_DEFAULT_TIMEOUT); -} - -void blob_db2_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { - PBL_ASSERT_TASK(PebbleTask_KernelBackground); - - analytics_inc(ANALYTICS_DEVICE_METRIC_BLOB_DB_EVENT_COUNT, AnalyticsClient_System); - - // Each BlobDB message is required to have at least a Command and a Token - static const uint8_t MIN_RAW_DATA_LEN = sizeof(BlobDBCommand) + sizeof(BlobDBToken); - if (length < MIN_RAW_DATA_LEN) { - // We don't send failure responses for too short messages in endpoint2 - PBL_LOG(LOG_LEVEL_ERROR, "Got a blob_db2 message that was too short, len: %zu", length); - return; - } - - const BlobDBCommand cmd = *data; - data += sizeof(BlobDBCommand); // fwd to message contents - const size_t data_length = length - sizeof(BlobDBCommand); - - if (!s_b2db_accepting_messages) { - prv_send_error_response(session, cmd, data, BLOB_DB_TRY_LATER); - return; - } - - prv_blob_db_msg_decode_and_handle(session, cmd, data, data_length); -} - -void blob_db2_set_accepting_messages(bool enabled) { - s_b2db_accepting_messages = enabled; -} diff --git a/src/fw/services/normal/blob_db/endpoint_private.c b/src/fw/services/normal/blob_db/endpoint_private.c deleted file mode 100644 index 48b1dd28ba..0000000000 --- a/src/fw/services/normal/blob_db/endpoint_private.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "endpoint_private.h" - -#include - -extern void blob_db_set_accepting_messages(bool enabled); -extern void blob_db2_set_accepting_messages(bool enabled); - -void blob_db_enabled(bool enabled) { - blob_db_set_accepting_messages(enabled); - blob_db2_set_accepting_messages(enabled); -} - -const uint8_t *endpoint_private_read_token_db_id(const uint8_t *iter, BlobDBToken *out_token, - BlobDBId *out_db_id) { - // read token - *out_token = *(BlobDBToken*)iter; - iter += sizeof(BlobDBToken); - // read database id - *out_db_id = *iter; - iter += sizeof(BlobDBId); - - return iter; -} diff --git a/src/fw/services/normal/blob_db/health_db.h b/src/fw/services/normal/blob_db/health_db.h deleted file mode 100644 index 46412c26ea..0000000000 --- a/src/fw/services/normal/blob_db/health_db.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/activity/activity.h" -#include "system/status_codes.h" -#include "util/attributes.h" - - -//! Get the typical metric value for a given day. -//! If you want "typical steps" you probably want health_db_get_typical_step_averages -bool health_db_get_typical_value(ActivityMetric metric, - DayInWeek day, - int32_t *value_out); - -//! Get the average metric value over the last month -bool health_db_get_monthly_average_value(ActivityMetric metric, - int32_t *value_out); - -//! Often referred to as "typical steps" -bool health_db_get_typical_step_averages(DayInWeek day, - ActivityMetricAverages *averages); - - - -//! For test / debug purposes only -bool health_db_set_typical_values(ActivityMetric metric, - DayInWeek day, - uint16_t *values, - int num_values); - -/////////////////////////////////////////// -// BlobDB Boilerplate (see blob_db/api.h) -/////////////////////////////////////////// - -void health_db_init(void); - -status_t health_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int health_db_get_len(const uint8_t *key, int key_len); - -status_t health_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t health_db_delete(const uint8_t *key, int key_len); - -status_t health_db_flush(void); diff --git a/src/fw/services/normal/blob_db/notif_db.c b/src/fw/services/normal/blob_db/notif_db.c deleted file mode 100644 index 6b8919e52a..0000000000 --- a/src/fw/services/normal/blob_db/notif_db.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "notif_db.h" - -#include "kernel/pbl_malloc.h" -#include "services/normal/notifications/notification_storage.h" -#include "system/logging.h" - -void notif_db_init(void) { -} - -status_t notif_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { - if (key_len != UUID_SIZE || - val_len < (int)sizeof(SerializedTimelineItemHeader)) { - return E_INVALID_ARGUMENT; - } - - // [FBO] this is a little bit silly: we deserialize the item to then re-serialize it in - // notification_storage_store. It has the advantage that it validates the payload - // and works with the existing storage - SerializedTimelineItemHeader *hdr = (SerializedTimelineItemHeader *)val; - const uint8_t *payload = val + sizeof(SerializedTimelineItemHeader); - - const bool has_status_bits = (hdr->common.status != 0); - - TimelineItem notification = {}; - if (!timeline_item_deserialize_item(¬ification, hdr, payload)) { - return E_INTERNAL; - } - - Uuid *id = kernel_malloc_check(sizeof(Uuid)); - *id = notification.header.id; - - char uuid_string[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(id, uuid_string); - - // If the notification already exists, only update the status flags - if (notification_storage_notification_exists(¬ification.header.id)) { - notification_storage_set_status(¬ification.header.id, notification.header.status); - PBL_LOG(LOG_LEVEL_INFO, "Notification modified: %s", uuid_string); - notifications_handle_notification_acted_upon(id); - } else if (!has_status_bits) { - notification_storage_store(¬ification); - PBL_LOG(LOG_LEVEL_INFO, "Notification added: %s", uuid_string); - notifications_handle_notification_added(id); - } - - timeline_item_free_allocated_buffer(¬ification); - return S_SUCCESS; -} - -int notif_db_get_len(const uint8_t *key, int key_len) { - if (key_len < UUID_SIZE) { - return 0; - } - - return notification_storage_get_len((Uuid *)key); -} - -status_t notif_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) { - // NYI - return S_SUCCESS; -} - -status_t notif_db_delete(const uint8_t *key, int key_len) { - if (key_len != UUID_SIZE) { - return E_INVALID_ARGUMENT; - } - - notification_storage_remove((Uuid *)key); - notifications_handle_notification_removed((Uuid *)key); - - return S_SUCCESS; -} - -status_t notif_db_flush(void) { - notification_storage_reset_and_init(); - return S_SUCCESS; -} diff --git a/src/fw/services/normal/blob_db/notif_db.h b/src/fw/services/normal/blob_db/notif_db.h deleted file mode 100644 index 67d2bb4581..0000000000 --- a/src/fw/services/normal/blob_db/notif_db.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "system/status_codes.h" -#include "services/normal/timeline/item.h" - -/////////////////////////////////////////// -// BlobDB Boilerplate (see blob_db/api.h) -/////////////////////////////////////////// - -void notif_db_init(void); - -status_t notif_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int notif_db_get_len(const uint8_t *key, int key_len); - -status_t notif_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t notif_db_delete(const uint8_t *key, int key_len); - -status_t notif_db_flush(void); diff --git a/src/fw/services/normal/blob_db/prefs_db.c b/src/fw/services/normal/blob_db/prefs_db.c deleted file mode 100644 index cafc3c9d0e..0000000000 --- a/src/fw/services/normal/blob_db/prefs_db.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "prefs_db.h" - -#include "process_management/app_install_manager.h" -#include "shell/prefs_private.h" - -// BlobDB APIs -//////////////////////////////////////////////////////////////////////////////// - -void prefs_db_init(void) { -} - -status_t prefs_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { - bool success = prefs_private_write_backing(key, key_len, val, val_len); - if (success) { - return S_SUCCESS; - } else { - return E_INVALID_ARGUMENT; - } -} - -int prefs_db_get_len(const uint8_t *key, int key_len) { - size_t len = prefs_private_get_backing_len(key, key_len); - if (len == 0) { - return E_INVALID_ARGUMENT; - } - return len; -} - -status_t prefs_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) { - bool success = prefs_private_read_backing(key, key_len, val_out, val_out_len); - if (success) { - return S_SUCCESS; - } else { - return E_INVALID_ARGUMENT; - } -} - -status_t prefs_db_delete(const uint8_t *key, int key_len) { - return E_INVALID_OPERATION; -} - -status_t prefs_db_flush(void) { - return E_INVALID_OPERATION; -} diff --git a/src/fw/services/normal/blob_db/prefs_db.h b/src/fw/services/normal/blob_db/prefs_db.h deleted file mode 100644 index 7a50acede2..0000000000 --- a/src/fw/services/normal/blob_db/prefs_db.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "system/status_codes.h" - -/////////////////////////////////////////// -// BlobDB Boilerplate (see blob_db/api.h) -/////////////////////////////////////////// - -void prefs_db_init(void); - -status_t prefs_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int prefs_db_get_len(const uint8_t *key, int key_len); - -status_t prefs_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t prefs_db_delete(const uint8_t *key, int key_len); - -status_t prefs_db_flush(void); diff --git a/src/fw/services/normal/blob_db/sync.h b/src/fw/services/normal/blob_db/sync.h deleted file mode 100644 index 99e83f7b01..0000000000 --- a/src/fw/services/normal/blob_db/sync.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "api.h" -#include "endpoint.h" - -#include "services/common/regular_timer.h" - -typedef enum { - BlobDBSyncSessionStateIdle = 0, - BlobDBSyncSessionStateWaitingForAck = 1, -} BlobDBSyncSessionState; - -typedef enum { - BlobDBSyncSessionTypeDB, - BlobDBSyncSessionTypeRecord, -} BlobDBSyncSessionType; - -typedef struct { - ListNode node; - BlobDBSyncSessionState state; - BlobDBId db_id; - BlobDBDirtyItem *dirty_list; - RegularTimerInfo timeout_timer; - BlobDBToken current_token; - BlobDBSyncSessionType session_type; -} BlobDBSyncSession; - -//! Start sync-ing a blobdb. -//! @param db_id the BlobDBId of the database to sync -status_t blob_db_sync_db(BlobDBId db_id); - -//! Start sync-ing a key within a blobdb. -//! @param db_id the BlobDBId of the database to sync -//! @param key the key to sync -//! @param key_len the length of the key to sync -status_t blob_db_sync_record(BlobDBId db_id, const void *key, int key_len, time_t last_updated); - -//! Get the sync session for a given ID. Will NOT return sessions for individual records -//! returns NULL if no sync is in progress -BlobDBSyncSession *blob_db_sync_get_session_for_id(BlobDBId db_id); - -//! Get the sync session currently waiting for a response with the given token -//! return NULL if no sync is in progress -BlobDBSyncSession *blob_db_sync_get_session_for_token(BlobDBToken token); - -//! Mark current item as synced and sync the next one -void blob_db_sync_next(BlobDBSyncSession *session); - -//! Cancel the sync in progress. Pending items will be synced next time. -void blob_db_sync_cancel(BlobDBSyncSession *session); diff --git a/src/fw/services/normal/blob_db/sync_util.c b/src/fw/services/normal/blob_db/sync_util.c deleted file mode 100644 index 91f69693c2..0000000000 --- a/src/fw/services/normal/blob_db/sync_util.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "sync_util.h" - -#include "kernel/pbl_malloc.h" -#include "system/logging.h" - -// Caution: CommonTimelineItemHeader .flags & .status are stored inverted and not auto-restored -// by the underlying db API. If .flags or .status is used from a CommonTimelineItemHeader below, -// be very careful - - -bool sync_util_is_dirty_cb(SettingsFile *file, SettingsRecordInfo *info, void *context) { - // If there is a single dirty record, update the out bool to dirty and stop iterating - if (info->dirty) { - *((bool *)context) = true; - return false; - } - - return true; -} - -bool sync_util_build_dirty_list_cb(SettingsFile *file, SettingsRecordInfo *info, void *context) { - if (info->dirty) { - BlobDBDirtyItem *dirty_list = *(BlobDBDirtyItem **)context; - - BlobDBDirtyItem *new_node = kernel_zalloc(sizeof(BlobDBDirtyItem) + info->key_len); - if (!new_node) { - PBL_LOG(LOG_LEVEL_WARNING, "Ran out of memory while building a dirty list"); - return false; - } - - new_node->last_updated = info->last_modified; - new_node->key_len = info->key_len; - info->get_key(file, new_node->key, new_node->key_len); - - *(BlobDBDirtyItem **)context = - (BlobDBDirtyItem *)list_prepend((ListNode *) dirty_list, (ListNode *)new_node); - } - - return true; -} diff --git a/src/fw/services/normal/blob_db/sync_util.h b/src/fw/services/normal/blob_db/sync_util.h deleted file mode 100644 index 22a1bc2ba1..0000000000 --- a/src/fw/services/normal/blob_db/sync_util.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/blob_db/api.h" -#include "services/normal/settings/settings_file.h" - -// Caution: CommonTimelineItemHeader .flags & .status are stored inverted and not auto-restored -// by the underlying db API. If .flags or .status is used from a CommonTimelineItemHeader below, -// be very careful. - -//! A settings file each callback which checks if the there are dirty records in the file -//! @param context The address of a bool which will get set -bool sync_util_is_dirty_cb(SettingsFile *file, SettingsRecordInfo *info, void *context); - -//! A settings file each callback which builds a BlobDBDirtyItem list -//! @param context The address of an empty dirty list which will get built -bool sync_util_build_dirty_list_cb(SettingsFile *file, SettingsRecordInfo *info, void *context); diff --git a/src/fw/services/normal/blob_db/util.c b/src/fw/services/normal/blob_db/util.c deleted file mode 100644 index e0d0c24780..0000000000 --- a/src/fw/services/normal/blob_db/util.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util.h" - -#include "kernel/pbl_malloc.h" - -#include - -void blob_db_util_free_dirty_list(BlobDBDirtyItem *dirty_list) { - ListNode *head = &dirty_list->node; - ListNode *cur; - while (head) { - cur = head; - list_remove(cur, &head, NULL); - kernel_free(cur); - } -} diff --git a/src/fw/services/normal/blob_db/util.h b/src/fw/services/normal/blob_db/util.h deleted file mode 100644 index 32d3bd085f..0000000000 --- a/src/fw/services/normal/blob_db/util.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "api.h" - -void blob_db_util_free_dirty_list(BlobDBDirtyItem *dirty_list); diff --git a/src/fw/services/normal/blob_db/watch_app_prefs_db.h b/src/fw/services/normal/blob_db/watch_app_prefs_db.h deleted file mode 100644 index 934ddad6cf..0000000000 --- a/src/fw/services/normal/blob_db/watch_app_prefs_db.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "apps/system_apps/send_text/send_text_app_prefs.h" -#include "apps/system_apps/reminders/reminder_app_prefs.h" -#include "services/normal/weather/weather_service_private.h" -#include "system/status_codes.h" - -// Reads the Send Text app prefs from the db -// @return Pointer to new \ref SerializedSendTextPrefs, NULL on failure -// @note task_free() must be called on the pointer when done with the memory -SerializedSendTextPrefs *watch_app_prefs_get_send_text(void); - -// Reads the Weather app location ordering from the db -// @return pointer to new \ref SerializedWeatherAppPrefs, NULL on failure -// @note use weather_app_prefs_destroy_weather() to free memory allocated by this method -SerializedWeatherAppPrefs *watch_app_prefs_get_weather(void); - -// Reads the Reminder App prefs from the db -// @return pointer to new \ref SerializedRemindersAppPrefs, NULL on failure -// @note task_free() must be called on the pointer when done with the memory -SerializedReminderAppPrefs *watch_app_prefs_get_reminder(void); - -// Frees memory allocated from watch_app_prefs_get_weather() -void watch_app_prefs_destroy_weather(SerializedWeatherAppPrefs *prefs); - -/////////////////////////////////////////// -// BlobDB Boilerplate (see blob_db/api.h) -/////////////////////////////////////////// - -void watch_app_prefs_db_init(void); - -status_t watch_app_prefs_db_insert(const uint8_t *key, int key_len, const uint8_t *val, - int val_len); - -int watch_app_prefs_db_get_len(const uint8_t *key, int key_len); - -status_t watch_app_prefs_db_read(const uint8_t *key, int key_len, uint8_t *val_out, - int val_out_len); - -status_t watch_app_prefs_db_delete(const uint8_t *key, int key_len); - -status_t watch_app_prefs_db_flush(void); diff --git a/src/fw/services/normal/blob_db/weather_db.h b/src/fw/services/normal/blob_db/weather_db.h deleted file mode 100644 index bc0239e71d..0000000000 --- a/src/fw/services/normal/blob_db/weather_db.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/weather/weather_service.h" -#include "services/normal/weather/weather_types.h" -#include "system/status_codes.h" -#include "util/attributes.h" -#include "util/pstring.h" -#include "util/time/time.h" -#include "util/uuid.h" - -#include - -#define WEATHER_DB_CURRENT_VERSION (3) - -typedef Uuid WeatherDBKey; - -typedef struct PACKED { - uint8_t version; - int16_t current_temp; - WeatherType current_weather_type; - int16_t today_high_temp; - int16_t today_low_temp; - WeatherType tomorrow_weather_type; - int16_t tomorrow_high_temp; - int16_t tomorrow_low_temp; - time_t last_update_time_utc; - bool is_current_location; - SerializedArray pstring16s; -} WeatherDBEntry; - -typedef enum WeatherDbStringIndex { - WeatherDbStringIndex_LocationName, - WeatherDbStringIndex_ShortPhrase, - WeatherDbStringIndexCount, -} WeatherDbStringIndex; - -#define MIN_ENTRY_SIZE (sizeof(WeatherDBEntry)) -#define MAX_ENTRY_SIZE (MIN_ENTRY_SIZE + \ - WEATHER_SERVICE_MAX_WEATHER_LOCATION_BUFFER_SIZE + \ - WEATHER_SERVICE_MAX_SHORT_PHRASE_BUFFER_SIZE) - -// Memory ownership: pointer to key and entry must not be saved, as they become invalid after -// the callback finishes -typedef void (*WeatherDBIteratorCallback)(WeatherDBKey *key, WeatherDBEntry *entry, void *context); - -// ------------------------------------------------------------------------------------ -// WeatherDB functions -status_t weather_db_for_each(WeatherDBIteratorCallback cb, void *context); - -// ------------------------------------------------------------------------------------ -// BlobDB Implementation - -void weather_db_init(void); - -status_t weather_db_flush(void); - -status_t weather_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len); - -int weather_db_get_len(const uint8_t *key, int key_len); - -status_t weather_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len); - -status_t weather_db_delete(const uint8_t *key, int key_len); diff --git a/src/fw/services/normal/bluetooth/ble_hrm.h b/src/fw/services/normal/bluetooth/ble_hrm.h deleted file mode 100644 index 25eb147995..0000000000 --- a/src/fw/services/normal/bluetooth/ble_hrm.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "util/time/time.h" - -#define BLE_HRM_REMINDER_POPUP_DELAY_MINS (2 * MINUTES_PER_HOUR) - -typedef struct GAPLEConnection GAPLEConnection; - -typedef struct BLEHRMSharingRequest BLEHRMSharingRequest; - -//! Called by the ble_hrm_sharing_popup upon the user's action to grant or decline the sharing. -//! @note Also cleans up the sharing_request object. -void ble_hrm_handle_sharing_request_response(bool is_granted, - BLEHRMSharingRequest *sharing_request); - -bool ble_hrm_is_supported_and_enabled(void); - -bool ble_hrm_is_sharing_to_connection(const GAPLEConnection *connection); - -bool ble_hrm_is_sharing(void); - -void ble_hrm_revoke_sharing_permission_for_connection(GAPLEConnection *connection); - -void ble_hrm_revoke_all(void); - -void ble_hrm_handle_activity_prefs_heart_rate_is_enabled(bool is_enabled); - -void ble_hrm_handle_disconnection(GAPLEConnection *connection); - -void ble_hrm_init(void); - -void ble_hrm_deinit(void); diff --git a/src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c b/src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c deleted file mode 100644 index 1d3bd3caee..0000000000 --- a/src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c +++ /dev/null @@ -1,1875 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FILE_LOG_COLOR LOG_COLOR_BLUE - -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/bluetooth_persistent_storage_debug.h" - -#include "comm/ble/gap_le_connect.h" -#include "comm/ble/gap_le_connection.h" -#include "comm/ble/gap_le_slave_reconnect.h" -#include "comm/ble/kernel_le_client/kernel_le_client.h" -#include "comm/bt_lock.h" -#include "console/prompt.h" -#include "kernel/pbl_malloc.h" -#include "os/mutex.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/pairability.h" -#include "services/common/bluetooth/local_addr.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/common/system_task.h" -#include "services/normal/settings/settings_file.h" -#include "system/hexdump.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/attributes.h" -#include "util/math.h" -#include "util/string.h" - -#include -#include -#include -#include -#include - -#ifdef UNITTEST -// Let the unittest define this using a header override: -# include "services/normal/bluetooth/bluetooth_persistent_storage_unittest_impl.h" -#else -# include "services/normal/bluetooth/bluetooth_persistent_storage_v2_impl.h" -#endif - -//! The BtPersistBonding*Data structs can never shrink, only grow - -//! Stores data about a remote BT classic device -typedef struct PACKED { - BTDeviceAddress addr; - SM128BitKey link_key; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - // These are the lowest bits of Remote.platform_bitfield_cache, which contain the OS type - uint8_t platform_bits; -} BtPersistBondingBTClassicData; - -//! Stores data about a remote BLE device -typedef struct PACKED { - bool supports_ancs:1; - bool is_gateway:1; - bool requires_address_pinning:1; - uint8_t flags:5; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - BtPersistLEPairingInfo pairing_info; -} BtPersistBondingBLEData; - -typedef struct PACKED { - BtPersistBondingType type:8; - - union PACKED { - BtPersistBondingBTClassicData bt_classic_data; - BtPersistBondingBLEData ble_data; - }; -} BtPersistBondingData; - -typedef struct PACKED { - BTDeviceInternal peer; - uint16_t chr_val_handle; - uint16_t flags; - unsigned value_changed:1; -} BtPersistCCCDData; - -typedef struct PACKED { - BTCCCDID id; - BtPersistCCCDData data; -} BtPersistCCCD; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Settings File - -#define BT_PERSISTENT_STORAGE_FILE_NAME "gap_bonding_db" -#define BT_PERSISTENT_STORAGE_FILE_SIZE (4096) - -//! All of the actual pairings use a BTBondingID as a key. This is because with BLE pairings an -//! address is not alwaywas available, and it made it easier to have BT Classic and BLE pairings -//! use the same type of key. When adding pairings there is no BTBondingID so a free key has to -//! be found by iterating over all possible keys. - -//! All of the local device attributes can be accessed directly with the following keys: - -//! This key is used to access the BTBondingID of the current active gateway -static const char ACTIVE_GATEWAY_KEY[] = "ACTIVE_GATEWAY"; -//! This key is used to access a bool which stores if we have recently changed active gateways -static const char IS_UNFAITHFUL_KEY[] = "IS_UNFAITHFUL"; -//! This key is used to access an array of two SM128BitKey values -static const char ROOT_KEYS_KEY[] = "ROOT_KEYS"; -//! This key is used to access a char array which holds the device name -static const char DEVICE_NAME_KEY[] = "DEVICE_NAME"; -//! This key is used to access a bool which stores the current airplane mode state -static const char AIRPLANE_MODE_KEY[] = "AIRPLANE_MODE"; -//! This key is used to access a uint64_t which stores the most recent system session capabilities -static const char SYSTEM_CAPABILITIES_KEY[] = "SYSTEM_CAPABILITIES"; -//! This key is used to access the BLE address that can be used for address pinning. -static const char BLE_PINNED_ADDRESS_KEY[] = "BLE_PINNED_ADDRESS"; - -static uint8_t s_bt_persistent_storage_updates = 0; - -static PebbleMutex *s_db_mutex = NULL; - -//! Cache of the last connected system session capabilities. Updated in flash when we get new flags -//! @note prv_lock() must be held when accessing this variable. -static PebbleProtocolCapabilities s_cached_system_capabilities; - -static void prv_lock(void) { - mutex_lock(s_db_mutex); -} - -static void prv_unlock(void) { - mutex_unlock(s_db_mutex); -} - -static bool prv_bt_persistent_storage_get_ble_smpairinginfo_by_id( - BTBondingID bonding, SMPairingInfo *info_out, char *name_out, bool *requires_address_pinning, - uint8_t *flags); - -static void prv_update_bondings(BTBondingID id, BtPersistBondingType type) { - if (id == BT_BONDING_ID_INVALID) { - return; - } - - if (type == BtPersistBondingTypeBTClassic) { - BTDeviceAddress addr; - SM128BitKey key; - char classic_name[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits; - if (bt_persistent_storage_get_bt_classic_pairing_by_id( - id, &addr, &key, classic_name, &platform_bits)) { - shared_prf_storage_store_bt_classic_pairing_data( - &addr, classic_name, &key, platform_bits); - } - } else if (type == BtPersistBondingTypeBLE) { - SMPairingInfo pairing_info; - char ble_name[BT_DEVICE_NAME_BUFFER_SIZE] = { }; - bool requires_address_pinning = false; - uint8_t flags = 0; - if (prv_bt_persistent_storage_get_ble_smpairinginfo_by_id( - id, &pairing_info, ble_name, &requires_address_pinning, - &flags)) { - // only send the ble_name if we have a name to send! - char *ble_name_ptr = (strlen(ble_name) == 0) ? NULL : &ble_name[0]; - shared_prf_storage_store_ble_pairing_data( - &pairing_info, ble_name_ptr, requires_address_pinning, flags); - } - } -} - -//! Returns the size of the data read. If the buffer provided is too small then 0 is returned -static int prv_file_get(const void *key, size_t key_len, void *data_out, size_t buf_len) { - unsigned int data_len = 0; - prv_lock(); - { - SettingsFile fd; - status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, - BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv != S_SUCCESS) { - goto cleanup; - } - - data_len = settings_file_get_len(&fd, key, key_len); - // If a big enough buffer wasn't passed in, then the data can't be read. - if (data_len > buf_len || - settings_file_get(&fd, key, key_len, data_out, buf_len) != S_SUCCESS) { - data_len = 0; - } - - settings_file_close(&fd); - } -cleanup: - prv_unlock(); - return data_len; -} - -//! @return the value that was read at that key or `default_value` if the key does not exist, -//! or if the stored data has been corrupted. -static bool prv_file_get_bool(const void *key, size_t key_len, bool default_value) { - uint8_t bool_data; - int read_size = prv_file_get(key, key_len, (void*)&bool_data, sizeof(bool_data)); - if (!read_size || - ((bool_data != (uint8_t)true) && (bool_data != (uint8_t)false))) { - return default_value; - } - // Default to false in the case of data corruption (anything other than 0x1 or 0x0). - return bool_data; -} - -//! Returns true if the set was successful -typedef enum { - GapBondingFileSetFail = 0, - GapBondingFileSetUpdated, - GapBondingFileSetNoUpdateNeeded, -} GapBondingFileSetStatus; - -static GapBondingFileSetStatus prv_file_set( - const void *key, size_t key_len, const void *data_in, size_t data_len) { - status_t rv; - bool do_perform_update = true; - prv_lock(); - { - SettingsFile fd; - rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv != S_SUCCESS) { - goto cleanup; - } - - // Only store data if data_in is a valid pointer, otherwise, clear the entry - if (data_in) { - if (settings_file_get_len(&fd, key, key_len) == (int)data_len) { - uint8_t curr_val[data_len]; - - settings_file_get(&fd, key, key_len, &curr_val[0], data_len); - - // Don't bother rewriting the exact same info. Pairing info is precious, - // we want to minimize cases where we could mess it up - if (memcmp(&curr_val, data_in, data_len) == 0) { - do_perform_update = false; - } - } - - if (do_perform_update) { - s_bt_persistent_storage_updates++; - PBL_LOG_D(LOG_DOMAIN_BT_PAIRING_INFO, LOG_LEVEL_DEBUG, - "Updating GAP Bonding DB Value !"); - PBL_HEXDUMP_D(LOG_DOMAIN_BT_PAIRING_INFO, LOG_LEVEL_DEBUG, (uint8_t *)key, key_len); - PBL_HEXDUMP_D(LOG_DOMAIN_BT_PAIRING_INFO, LOG_LEVEL_DEBUG, (uint8_t *)data_in, data_len); - rv = settings_file_set(&fd, key, key_len, (uint8_t*) data_in, data_len); - } - } else { - rv = settings_file_delete(&fd, key, key_len); - } - settings_file_close(&fd); - } -cleanup: - prv_unlock(); - if (rv != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to update gap bonding db, rv = %"PRId32, rv); - return GapBondingFileSetFail; - } - - return (do_perform_update ? GapBondingFileSetUpdated : GapBondingFileSetNoUpdateNeeded); -} - -//! Returns true if things were successful -static bool prv_file_each(SettingsFileEachCallback itr_cb, void *itr_data) { - status_t rv; - prv_lock(); - { - SettingsFile fd; - rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv) { - goto cleanup; - } - - settings_file_each(&fd, itr_cb, itr_data); - settings_file_close(&fd); - } -cleanup: - prv_unlock(); - return (rv == S_SUCCESS); -} - - -//! Get the next available BondingID -//! This function re-uses bonding ids as they are freed. This could be a problem with 3rd party -//! apps. https://pebbletechnology.atlassian.net/browse/PBL-8391 -static BTBondingID prv_get_free_key() { - BTBondingID free_key = BT_BONDING_ID_INVALID; - - prv_lock(); - { - SettingsFile fd; - status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, - BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv) { - goto cleanup; - } - - for (BTBondingID id = 0; id < BT_BONDING_ID_INVALID; id++) { - if (!settings_file_exists(&fd, &id, sizeof(id))) { - free_key = id; - break; - } - } - - settings_file_close(&fd); - - } -cleanup: - prv_unlock(); - return free_key; -} - -//! Get the next available CCCDID -static BTCCCDID prv_get_free_cccd() { - BTCCCDID free_cccd = BT_CCCD_ID_INVALID; - - prv_lock(); - { - SettingsFile fd; - status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, - BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv) { - goto cleanup; - } - - for (BTCCCDID id = 0U; id < BT_CCCD_ID_INVALID; id++) { - if (!settings_file_exists(&fd, &id, sizeof(id))) { - free_cccd = id; - break; - } - } - - settings_file_close(&fd); - - } -cleanup: - prv_unlock(); - return free_cccd; -} - -static bool prv_any_pinned_ble_pairings_itr(SettingsFile *file, - SettingsRecordInfo *info, - void *context) { - if (info->key_len != sizeof(BTBondingID)) { - return true; - } - if (info->val_len == 0) { - return true; - } - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData data; - info->get_val(file, &data, sizeof(data)); - if (data.ble_data.requires_address_pinning) { - bool *has_pinned_ble_pairings = context; - *has_pinned_ble_pairings = true; - return false; - } - - return true; -} - -bool bt_persistent_storage_has_pinned_ble_pairings(void) { - bool has_pinned_ble_pairings = false; - prv_file_each(prv_any_pinned_ble_pairings_itr, &has_pinned_ble_pairings); - return has_pinned_ble_pairings; -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Shared PRF Storage - -static void prv_load_pinned_address_from_prf(void) { - BTDeviceAddress pinned_address; - - if (shared_prf_storage_get_ble_pinned_address(&pinned_address)) { - bt_persistent_storage_set_ble_pinned_address(&pinned_address); - } - - // if we get here there is no pinned address in PRF, let's load the address fw has been - // using. This shouldn't ever really happen unless we reboot while saving new information to - // shared PRF - if (bt_persistent_storage_get_ble_pinned_address(&pinned_address)) { - shared_prf_storage_set_ble_pinned_address(&pinned_address); - } -} - -static void prv_load_local_data_from_prf(void) { - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - if (shared_prf_storage_get_local_device_name(name, BT_DEVICE_NAME_BUFFER_SIZE)) { - bt_persistent_storage_set_local_device_name(name, BT_DEVICE_NAME_BUFFER_SIZE); - } - - SM128BitKey keys[SMRootKeyTypeNum]; - if (shared_prf_storage_get_root_key(SMRootKeyTypeEncryption, &keys[SMRootKeyTypeEncryption]) && - shared_prf_storage_get_root_key(SMRootKeyTypeIdentity, &keys[SMRootKeyTypeIdentity])) { -#if !defined(RELEASE) && !defined(PLATFORM_TINTIN) - PBL_LOG(LOG_LEVEL_INFO, "Loading Root Keys from PRF storage:"); - PBL_HEXDUMP(LOG_LEVEL_INFO, (const uint8_t *) keys, sizeof(keys)); -#endif - bt_persistent_storage_set_root_keys(keys); - return; - } - - // if we get here there are no root keys in prf storage, let's load the root - // keys normal fw has been using. This shouldn't ever really happen unless we - // reboot while saving new information to shared PRF - if (bt_persistent_storage_get_root_key(SMRootKeyTypeEncryption, &keys[SMRootKeyTypeEncryption]) && - bt_persistent_storage_get_root_key(SMRootKeyTypeIdentity, &keys[SMRootKeyTypeIdentity])) { - PBL_LOG(LOG_LEVEL_ERROR, "Storing Root Keys to PRF storage"); - shared_prf_storage_set_root_keys(keys); - } -} - -static void prv_push_bt_persist_to_shared_prf(BtPersistBondingType type) { - BTBondingID bonding_id = BT_BONDING_ID_INVALID; - BtPersistBondingType found_type; - - // At the moment, the "active gateway" concept is a bit broken. We're in a transition period - // with an amphibious iAP + LE mode, where the iOS app decides what connection to use for - // PP transport. To keep iAP reconnection going (for legacy PebbleKit iOS support and also to - // connect to the Pebble app), the BT Classic bonding is kept as the "active - // gateway" and any LE bonding currently never becomes the active gateway. - // Because of this, use bt_persistent_storage_get_ble_ancs_bonding() here. Once this transition - // period is over, we can change this to use bt_persistent_storage_get_active_gateway(). - // Also see https://pebbletechnology.atlassian.net/browse/PBL-25597 - if (type == BtPersistBondingTypeBLE) { - bonding_id = bt_persistent_storage_get_ble_ancs_bonding(); - } else if (!bt_persistent_storage_get_active_gateway(&bonding_id, &found_type) || - found_type != type) { - return; - } - - if (bonding_id != BT_BONDING_ID_INVALID) { - prv_update_bondings(bonding_id, type); - } -} - -static void prv_load_bt_classic_data_from_prf(void) { - BTDeviceAddress bd_addr; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - SM128BitKey link_key; - uint8_t platform_bits; - if (!shared_prf_storage_get_bt_classic_pairing_data(&bd_addr, name, &link_key, &platform_bits)) { - // No pairing available, check to see if we have a pairing in the gapDB - prv_push_bt_persist_to_shared_prf(BtPersistBondingTypeBTClassic); - return; - } - - bt_persistent_storage_store_bt_classic_pairing(&bd_addr, &link_key, name, &platform_bits); -} - -static void prv_load_ble_pairing_from_prf(void) { - SMPairingInfo prf_pairing_info; - char device_name[BT_DEVICE_NAME_BUFFER_SIZE]; - bool requires_address_pinning; - uint8_t flags; - if (!shared_prf_storage_get_ble_pairing_data(&prf_pairing_info, device_name, - &requires_address_pinning, - &flags)) { - // No pairing available, check to see if we have a pairing in the gapDB - prv_push_bt_persist_to_shared_prf(BtPersistBondingTypeBLE); - return; - } - - // PRF pairing storage has only one pairing slot. Assume is_gateway: - bt_persistent_storage_store_ble_pairing(&prf_pairing_info, true /* is_gateway */, device_name, - requires_address_pinning, flags); -} - -static void prv_load_data_from_prf(void) { - prv_load_local_data_from_prf(); - prv_load_pinned_address_from_prf(); - if (bt_driver_supports_bt_classic()) { - prv_load_bt_classic_data_from_prf(); - } - prv_load_ble_pairing_from_prf(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Common Helper Functions - -BtPersistBondingType prv_get_type_for_id(BTBondingID id) { - BtPersistBondingData data; - prv_file_get(&id, sizeof(id), &data, sizeof(data)); - - return data.type; -} - -bool prv_delete_pairing_with_type_by_id(BTBondingID bonding, BtPersistBondingType type, - BtPersistBondingData *data_out) { - if (!prv_file_get(&bonding, sizeof(bonding), data_out, sizeof(*data_out))) { - return false; - } - - if (data_out->type != type) { - PBL_LOG(LOG_LEVEL_ERROR, "Type mismatch: not deleting pairing. Is the bonding db corrupted?"); - return false; - } - - if (prv_file_set(&bonding, sizeof(bonding), NULL, 0) == GapBondingFileSetFail) { - return false; - } - - return true; -} - -bool prv_has_active_gateway_by_type(BtPersistBondingType desired_type) { - BTBondingID bonding; - BtPersistBondingType type; - - if (!bt_persistent_storage_get_active_gateway(&bonding, &type)) { - return false; - } - - if (bonding == BT_BONDING_ID_INVALID || type != desired_type) { - return false; - } - - return true; -} - -static void prv_update_active_gateway_if_needed(BTBondingID bonding, BtPersistBondingOp op) { - // Invalidate the active gateway if it is getting deleted - if (op == BtPersistBondingOpWillDelete) { - BTBondingID current_active_gateway; - bt_persistent_storage_get_active_gateway(¤t_active_gateway, NULL); - if (current_active_gateway == bonding) { - bt_persistent_storage_set_active_gateway(BT_BONDING_ID_INVALID); - } - } -} - -static void prv_call_common_bonding_change_handlers(BTBondingID bonding, BtPersistBondingOp op) { - bt_pairability_update_due_to_bonding_change(); -} - -typedef struct { - unsigned int count; - BtPersistBondingType type; -} PairingCountItrData; - -static bool prv_get_num_pairings_by_type_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - PairingCountItrData *itr_data = (PairingCountItrData *)context; - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == itr_data->type) { - itr_data->count++; - } - - return true; -} - -static unsigned int prv_get_num_pairings_by_type(BtPersistBondingType type) { - PairingCountItrData itr_data = { - .count = 0, - .type = type, - }; - - prv_file_each(prv_get_num_pairings_by_type_itr, &itr_data); - return itr_data.count; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BLE Pairing Info -void gap_le_connection_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op); -static void prv_call_ble_bonding_change_handlers(BTBondingID bonding, - BtPersistBondingOp op) { - prv_update_active_gateway_if_needed(bonding, op); - - if (!bt_ctl_is_bluetooth_running()) { - return; - } - bt_local_addr_handle_bonding_change(bonding, op); - gap_le_connection_handle_bonding_change(bonding, op); - gap_le_connect_handle_bonding_change(bonding, op); - kernel_le_client_handle_bonding_change(bonding, op); - prv_call_common_bonding_change_handlers(bonding, op); -} - -typedef struct { - SMPairingInfo pairing_info; - BTBondingID key_out; -} KeyForSMPairingItrData; - -static bool prv_is_pairing_info_equal_identity(const BtPersistLEPairingInfo *a, - const SMPairingInfo *b) { - return (a->is_remote_identity_info_valid && - b->is_remote_identity_info_valid && - bt_device_equal(&a->identity.opaque, &b->identity.opaque) && - memcmp(&a->irk, &b->irk, sizeof(SMIdentityResolvingKey)) == 0); -} - -static bool prv_get_key_for_sm_pairing_info_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - KeyForSMPairingItrData *itr_data = (KeyForSMPairingItrData*) context; - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == BtPersistBondingTypeBLE && - prv_is_pairing_info_equal_identity(&stored_data.ble_data.pairing_info, - &itr_data->pairing_info)) { - itr_data->key_out = key; - return false; // stop iterating - } - - return true; -} - -static BTBondingID prv_get_key_for_sm_pairing_info(const SMPairingInfo *pairing_info) { - KeyForSMPairingItrData itr_data = { - .pairing_info = *pairing_info, - .key_out = BT_BONDING_ID_INVALID, - }; - prv_file_each(prv_get_key_for_sm_pairing_info_itr, &itr_data); - - return itr_data.key_out; -} - -//! For unit testing -int bt_persistent_storage_get_raw_data(const void *key, size_t key_len, - void *data_out, size_t buf_len) { - return prv_file_get(key, key_len, data_out, buf_len); -} - -bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *addr) { - GapBondingFileSetStatus rv = prv_file_set(&BLE_PINNED_ADDRESS_KEY, sizeof(BLE_PINNED_ADDRESS_KEY), - addr, addr ? sizeof(*addr) : 0); - bool success = (rv != GapBondingFileSetFail); - if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to store pinned address"); - } else if (rv == GapBondingFileSetUpdated) { - shared_prf_storage_set_ble_pinned_address(addr); - } - return success; -} - -BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *new_pairing_info, - bool is_gateway, const char *device_name, - bool requires_address_pinning, - uint8_t flags) { - if (!new_pairing_info || sm_is_pairing_info_empty(new_pairing_info)) { - return BT_BONDING_ID_INVALID; - } - - // Check if this is an update - BtPersistBondingOp op = BtPersistBondingOpDidChange; - BTBondingID key = prv_get_key_for_sm_pairing_info(new_pairing_info); - - if (key == BT_BONDING_ID_INVALID) { - // This is an add, not an update - op = BtPersistBondingOpDidAdd; - key = prv_get_free_key(); - if (key == BT_BONDING_ID_INVALID) { - // We are out of keys.... - return BT_BONDING_ID_INVALID; - } - } else { - // If we add any optional fields a load will have to happen here so they don't get overwritten - } - - BtPersistBondingData new_data; - new_data = (BtPersistBondingData) { - .type = BtPersistBondingTypeBLE, - .ble_data.is_gateway = is_gateway, - .ble_data.flags = flags, - // This is defaulting to "is_gateway" for now because it is currently being used as the flag - // for the pairing that we want to reconnect/connect to. If this isn't set then - // we don't register an intent for the device and thus don't connect. - // Currently only 1 ble pairing is really supported so this works for now - // FIXME: https://pebbletechnology.atlassian.net/browse/PBL-15277 - .ble_data.supports_ancs = is_gateway, - .ble_data.requires_address_pinning = requires_address_pinning, - }; - bt_persistent_storage_assign_persist_pairing_info(&new_data.ble_data.pairing_info, - new_pairing_info); - - if (device_name) { - strncpy(new_data.ble_data.name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); - new_data.ble_data.name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; - } - - GapBondingFileSetStatus status; - status = prv_file_set(&key, sizeof(key), &new_data, sizeof(new_data)); - if (status == GapBondingFileSetFail) { - return BT_BONDING_ID_INVALID; - } - - if (is_gateway && status == GapBondingFileSetUpdated) { - prv_update_bondings(key, BtPersistBondingTypeBLE); - } - - // In practice we only support a single BLE pairing at a time, so if we add a new one, - // set ourselves as unfaithful. - if (op == BtPersistBondingOpDidAdd) { - bt_persistent_storage_set_unfaithful(true); - } - - prv_call_ble_bonding_change_handlers(key, op); - - return key; -} - -bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name) { - BtPersistBondingData data; - if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { - return false; - } - - if (data.type != BtPersistBondingTypeBLE) { - PBL_LOG(LOG_LEVEL_ERROR, "Not getting BLE id %d. Type mismatch", bonding); - return false; - } - - strncpy(data.ble_data.name, device_name, BT_DEVICE_NAME_BUFFER_SIZE); - data.ble_data.name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; - - GapBondingFileSetStatus status; - status = prv_file_set(&bonding, sizeof(bonding), &data, sizeof(data)); - - // If this is the gateway, update SPRF so our pairing info betwen PRF and normal - // FW is in sync - if (data.ble_data.is_gateway && (status == GapBondingFileSetUpdated)) { - prv_update_bondings(bonding, BtPersistBondingTypeBLE); - } - - return (status != GapBondingFileSetFail); -} - -static void prv_init_and_assign_ble_bonding(BleBonding *bonding, - const BtPersistBondingData *stored_data) { - *bonding = (BleBonding){}; - bt_persistent_storage_assign_sm_pairing_info(&bonding->pairing_info, - &stored_data->ble_data.pairing_info); - bonding->is_gateway = stored_data->ble_data.is_gateway; -} - -static void prv_remove_ble_bonding_from_bt_driver(const BtPersistBondingData *deleted_data) { - if (!bt_ctl_is_bluetooth_running()) { - return; - } - BleBonding bonding; - prv_init_and_assign_ble_bonding(&bonding, deleted_data); - bt_driver_handle_host_removed_bonding(&bonding); -} - -status_t prv_delete_all_cccd_for_addr(const BTDeviceInternal *dev) { - status_t rv; - - prv_lock(); - { - SettingsFile fd; - rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, - BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv) { - goto cleanup; - } - - for (BTCCCDID id = 0U; id < BT_CCCD_ID_INVALID; id++) { - if (settings_file_exists(&fd, &id, sizeof(id))) { - BtPersistCCCDData stored_data; - - rv = settings_file_get(&fd, &id, sizeof(id), &stored_data, sizeof(stored_data)); - if (rv) { - goto cleanup; - } - - if (bt_device_internal_equal(dev, &stored_data.peer)) { - BleCCCD cccd_to_delete = { - .peer = *dev, - .chr_val_handle = stored_data.chr_val_handle, - .flags = stored_data.flags, - .value_changed = stored_data.value_changed, - }; - - bt_driver_handle_host_removed_cccd(&cccd_to_delete); - - rv = settings_file_delete(&fd, &id, sizeof(id)); - if (rv) { - goto cleanup; - } - } - } - } - - settings_file_close(&fd); - - } -cleanup: - prv_unlock(); - return rv; -} - -void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID bonding) { - BtPersistBondingData deleted_data; - if (!prv_delete_pairing_with_type_by_id(bonding, BtPersistBondingTypeBLE, &deleted_data)) { - return; - } - - status_t rv; - rv = prv_delete_all_cccd_for_addr(&deleted_data.ble_data.pairing_info.identity); - PBL_ASSERTN(rv == S_SUCCESS); - - prv_remove_ble_bonding_from_bt_driver(&deleted_data); - - prv_call_ble_bonding_change_handlers(bonding, BtPersistBondingOpWillDelete); - // TODO: Make sure this matches what we have stored - shared_prf_storage_erase_ble_pairing_data(); -} - -typedef struct { - BTDeviceInternal device; - SMIdentityResolvingKey irk_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - BTBondingID id_out; - bool found; -} FindByAddrItrData; - -static bool prv_find_by_addr_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - FindByAddrItrData *itr_data = (FindByAddrItrData *) context; - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == BtPersistBondingTypeBLE && - bt_device_equal(&itr_data->device.opaque, - &stored_data.ble_data.pairing_info.identity.opaque)) { - itr_data->irk_out = stored_data.ble_data.pairing_info.irk; - strncpy(itr_data->name_out, stored_data.ble_data.name, BT_DEVICE_NAME_BUFFER_SIZE); - itr_data->id_out = key; - itr_data->found = true; - return false; // stop iterating - } - - return true; // continue iterating -} - -void bt_persistent_storage_delete_ble_pairing_by_addr(const BTDeviceInternal *device) { - FindByAddrItrData itr_data = { - .device = *device, - .found = false, - }; - prv_file_each(prv_find_by_addr_itr, &itr_data); - - if (!itr_data.found) { - return; - } - - bt_persistent_storage_delete_ble_pairing_by_id(itr_data.id_out); -} - -static void prv_fill_ble_data(SMIdentityResolvingKey *irk_in, - BTDeviceInternal *device_in, - char *name_in, - SMIdentityResolvingKey *irk_out, - BTDeviceInternal *device_out, - char *name_out) { - if (irk_out && irk_in) { - *irk_out = *irk_in; - } - - if (device_out && device_in) { - *device_out = *device_in; - } - if (name_out && name_in) { - strncpy(name_out, name_in, BT_DEVICE_NAME_BUFFER_SIZE); - name_out[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0'; - } -} - -bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding, - SMIdentityResolvingKey *irk_out, - BTDeviceInternal *device_out, - char *name_out) { - BtPersistBondingData data; - if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { - return false; - } - - if (data.type != BtPersistBondingTypeBLE) { - PBL_LOG(LOG_LEVEL_ERROR, "Not getting BT Classic id %d. Type mismatch", bonding); - return false; - } - - prv_fill_ble_data(&data.ble_data.pairing_info.irk, &data.ble_data.pairing_info.identity, - data.ble_data.name, irk_out, device_out, name_out); - - return true; -} - -static bool prv_bt_persistent_storage_get_ble_smpairinginfo_by_id( - BTBondingID bonding, SMPairingInfo *info_out, char *name_out, bool *requires_address_pinning, - uint8_t *flags) { - BtPersistBondingData data; - if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { - return false; - } - - if (data.type != BtPersistBondingTypeBLE) { - PBL_LOG(LOG_LEVEL_ERROR, "Not getting BLE id %d. Type mismatch", bonding); - return false; - } - - if (info_out) { - bt_persistent_storage_assign_sm_pairing_info(info_out, &data.ble_data.pairing_info); - } - - *requires_address_pinning = data.ble_data.requires_address_pinning; - *flags = data.ble_data.flags; - - prv_fill_ble_data( - NULL, NULL, data.ble_data.name, NULL, NULL, name_out); - return true; -} - -bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device, - SMIdentityResolvingKey *irk_out, - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]) { - FindByAddrItrData itr_data = { - .device = *device, - .found = false, - }; - prv_file_each(prv_find_by_addr_itr, &itr_data); - - if (!itr_data.found) { - return false; - } - - prv_fill_ble_data(&itr_data.irk_out, NULL, itr_data.name_out, - irk_out, NULL, name_out); - - return true; -} - -bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { - BTDeviceAddress address; - int read_size = prv_file_get(&BLE_PINNED_ADDRESS_KEY, sizeof(BLE_PINNED_ADDRESS_KEY), - &address, sizeof(address)); - if (!read_size) { - return false; - } - if (address_out) { - *address_out = address; - } - return true; -} - -static bool prv_get_first_ancs_bonding_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - BTBondingID *first_ancs_supported_bonding_found = (BTBondingID *) context; - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == BtPersistBondingTypeBLE && stored_data.ble_data.supports_ancs) { - *first_ancs_supported_bonding_found = key; - return false; // stop iterating - } - - return true; -} - -BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void) { - BTBondingID first_ancs_supported_bonding_found = BT_BONDING_ID_INVALID; - prv_file_each(prv_get_first_ancs_bonding_itr, &first_ancs_supported_bonding_found); - - return first_ancs_supported_bonding_found; -} - -bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding) { - BtPersistBondingData data; - prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data)); - - if (data.type == BtPersistBondingTypeBLE) { - return data.ble_data.supports_ancs; - } - return false; -} - -bool bt_persistent_storage_has_ble_ancs_bonding(void) { - return bt_persistent_storage_get_ble_ancs_bonding() != BT_BONDING_ID_INVALID; -} - -bool bt_persistent_storage_has_active_ble_gateway_bonding(void) { - return prv_has_active_gateway_by_type(BtPersistBondingTypeBLE); -} - -typedef void (*BtPersistBondingDBEachBLEInternal)(BTBondingID key, - BtPersistBondingData *stored_data, void *ctx); - -typedef struct { - BtPersistBondingDBEachBLEInternal cb; - void *cb_data; -} ForEachBLEPairingInternalData; - -typedef void (*BtPersistCCCDDBEachBLEInternal)(BTCCCDID key, - BtPersistCCCDData *stored_data, void *ctx); - -typedef struct { - BtPersistCCCDDBEachBLEInternal cb; - void *cb_data; -} ForEachBLECCCDInternalData; - -typedef struct { - BtPersistBondingDBEachBLE cb; - void *cb_data; -} ForEachBLEPairingData; - -static void prv_public_for_each_ble_cb(BTBondingID key, - BtPersistBondingData *stored_data, void *context) { - ForEachBLEPairingData *itr_data = (ForEachBLEPairingData *)context; - itr_data->cb(&stored_data->ble_data.pairing_info.identity, - &stored_data->ble_data.pairing_info.irk, - stored_data->ble_data.name, &key, itr_data->cb_data); -} - -static bool prv_ble_pairing_internal_for_each_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - ForEachBLEPairingInternalData *internal_itr_data = (ForEachBLEPairingInternalData*) context; - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == BtPersistBondingTypeBLE) { - internal_itr_data->cb(key, &stored_data, internal_itr_data->cb_data); - } - - return true; -} - -static bool prv_ble_cccd_internal_for_each_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTCCCDID)) { - return true; // continue iterating - } - - ForEachBLECCCDInternalData *internal_itr_data = (ForEachBLECCCDInternalData*) context; - - BTCCCDID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistCCCDData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - internal_itr_data->cb(key, &stored_data, internal_itr_data->cb_data); - - return true; -} - -void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context) { - ForEachBLEPairingData itr_data = { - .cb = cb, - .cb_data = context, - }; - ForEachBLEPairingInternalData internal_itr_data = { - .cb = prv_public_for_each_ble_cb, - .cb_data = &itr_data, - }; - prv_file_each(prv_ble_pairing_internal_for_each_itr, &internal_itr_data); -} - -static void prv_register_bondings_for_each_ble_cb(BTBondingID key, - BtPersistBondingData *stored_data, - void *context) { - BleBonding bonding; - prv_init_and_assign_ble_bonding(&bonding, stored_data); - bonding.is_gateway = stored_data->ble_data.is_gateway; - bonding.flags = stored_data->ble_data.flags; - bt_driver_handle_host_added_bonding(&bonding); -} - -static void prv_register_cccd_for_each_ble_cb(BTCCCDID key, - BtPersistCCCDData *stored_data, - void *context) { - BleCCCD cccd = { - .peer = stored_data->peer, - .chr_val_handle = stored_data->chr_val_handle, - .flags = stored_data->flags, - .value_changed = stored_data->value_changed, - }; - - bt_driver_handle_host_added_cccd(&cccd); -} - -void bt_persistent_storage_register_existing_ble_bondings(void) { - ForEachBLEPairingInternalData internal_itr_data_bonding = { - .cb = prv_register_bondings_for_each_ble_cb, - }; - prv_file_each(prv_ble_pairing_internal_for_each_itr, &internal_itr_data_bonding); - - ForEachBLECCCDInternalData internal_itr_data_cccd = { - .cb = prv_register_cccd_for_each_ble_cb, - }; - prv_file_each(prv_ble_cccd_internal_for_each_itr, &internal_itr_data_cccd); -} - -void analytics_external_collect_ble_pairing_info(void) { - unsigned int ble_pairings_count = prv_get_num_pairings_by_type(BtPersistBondingTypeBLE); - - analytics_set(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_RECORDS_COUNT, - ble_pairings_count, AnalyticsClient_System); - - analytics_set(ANALYTICS_DEVICE_BT_PERSISTENT_STORAGE_UPDATES, s_bt_persistent_storage_updates, - AnalyticsClient_System); - s_bt_persistent_storage_updates = 0; -} - -typedef struct { - const BTDeviceInternal *peer; - uint16_t chr_val_handle; - BTCCCDID id; -} FindCCCDItrData; - -static bool prv_find_cccd_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - if (info->val_len == 0 || info->key_len != sizeof(BTCCCDID)) { - return true; // continue iterating - } - - FindCCCDItrData *itr_data = (FindCCCDItrData *) context; - - BTCCCDID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistCCCDData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (bt_device_internal_equal(itr_data->peer, &stored_data.peer) && - stored_data.chr_val_handle == itr_data->chr_val_handle) { - itr_data->id = key; - return false; // stop iterating - } - - return true; -} - -BTCCCDID bt_persistent_storage_store_cccd(const BleCCCD *cccd) { - PBL_ASSERTN(cccd != NULL); - - FindCCCDItrData itr_data = { - .peer = &cccd->peer, - .chr_val_handle = cccd->chr_val_handle, - .id = BT_CCCD_ID_INVALID, - }; - prv_file_each(prv_find_cccd_itr, &itr_data); - - BTCCCDID cccd_id = itr_data.id; - if (cccd_id == BT_CCCD_ID_INVALID) { - cccd_id = prv_get_free_cccd(); - if (cccd_id == BT_CCCD_ID_INVALID) { - return BT_CCCD_ID_INVALID; - } - } - - BtPersistCCCDData stored_data = { - .peer = cccd->peer, - .chr_val_handle = cccd->chr_val_handle, - .flags = cccd->flags, - .value_changed = cccd->value_changed, - }; - - BtPersistCCCD stored_cccd = { - .id = cccd_id, - .data = stored_data, - }; - - GapBondingFileSetStatus status = prv_file_set(&stored_cccd.id, sizeof(stored_cccd.id), - &stored_cccd.data, sizeof(stored_cccd.data)); - if (status == GapBondingFileSetFail) { - return BT_CCCD_ID_INVALID; - } - - return cccd_id; -} - -bool bt_persistent_storage_delete_cccd(const BTDeviceInternal *peer, uint16_t chr_val_handle) { - BTCCCDID cccd_id; - - FindCCCDItrData itr_data = { - .peer = peer, - .chr_val_handle = chr_val_handle, - .id = BT_CCCD_ID_INVALID - }; - prv_file_each(prv_find_cccd_itr, &itr_data); - - if (itr_data.id == BT_CCCD_ID_INVALID) { - return false; - } - - if (prv_file_set(&cccd_id, sizeof(cccd_id), NULL, 0) == GapBondingFileSetFail) { - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BT Classic Pairing Info - -static void prv_call_bt_classic_bonding_change_handlers(BTBondingID bonding, - BtPersistBondingOp op) { - prv_update_active_gateway_if_needed(bonding, op); - - if (!bt_ctl_is_bluetooth_running()) { - return; - } - prv_call_common_bonding_change_handlers(bonding, op); -} - -typedef struct { - BTDeviceAddress address; - BTBondingID key_out; -} KeyForBTCAddrData; - -static bool prv_get_key_for_bt_classic_addr_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - KeyForBTCAddrData *itr_data = (KeyForBTCAddrData*) context; - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == BtPersistBondingTypeBTClassic && - !memcmp(&itr_data->address, &stored_data.bt_classic_data.addr, sizeof(itr_data->address))) { - itr_data->key_out = key; - return false; // stop iterating - } - - return true; -} - -static BTBondingID prv_get_key_for_bt_classic_addr(const BTDeviceAddress *address) { - KeyForBTCAddrData itr_data = { - .address = *address, - .key_out = BT_BONDING_ID_INVALID, - }; - prv_file_each(prv_get_key_for_bt_classic_addr_itr, &itr_data); - return itr_data.key_out; -} - - -BTBondingID bt_persistent_storage_store_bt_classic_pairing(BTDeviceAddress *address, - SM128BitKey *link_key, - char *name, uint8_t *platform_bits) { - if (!address) { - return BT_BONDING_ID_INVALID; - } - - BtPersistBondingData new_data = { - .type = BtPersistBondingTypeBTClassic, - .bt_classic_data.addr = *address, - }; - - // Check if this we already have a key for this addr - BTBondingID key = prv_get_key_for_bt_classic_addr(address); - if (key == BT_BONDING_ID_INVALID) { - key = prv_get_free_key(); - if (key == BT_BONDING_ID_INVALID) { - // We are out of keys.... - return BT_BONDING_ID_INVALID; - } - } else { - // Load the existing data so the optional fields don't get overwritten - bt_persistent_storage_get_bt_classic_pairing_by_addr(address, - &new_data.bt_classic_data.link_key, - new_data.bt_classic_data.name, - &new_data.bt_classic_data.platform_bits); - } - - BtPersistBondingOp op; - SM128BitKey nil_key = {}; - if (memcmp(&new_data.bt_classic_data.link_key, &nil_key, sizeof(SM128BitKey))) { - // We have a link key stored already, this is just an update - op = BtPersistBondingOpDidChange; - } else { - // No link key stored, this is an add - op = BtPersistBondingOpDidAdd; - } - - if (op == BtPersistBondingOpDidAdd && !link_key) { - // If this is an add, and there is no link key, then don't store anything. - // gap_pair typically sends just a name + addr combo before the link key comes in which should - // be ignored as we aren't fully paired until we have a link key - return BT_BONDING_ID_INVALID; - } - - if (name) { - strncpy(new_data.bt_classic_data.name, name, BT_DEVICE_NAME_BUFFER_SIZE); - } - if (link_key) { - new_data.bt_classic_data.link_key = *link_key; - } - if (platform_bits) { - new_data.bt_classic_data.platform_bits = *platform_bits; - } - - GapBondingFileSetStatus status = - prv_file_set(&key, sizeof(key), &new_data, sizeof(new_data)); - if (status == GapBondingFileSetFail) { - return BT_BONDING_ID_INVALID; - } - - if (status == GapBondingFileSetUpdated) { - // if we updated something, bring SPRF in sync - prv_update_bondings(key, BtPersistBondingTypeBTClassic); - } - - if (name && link_key) { - // For now make the active gateway the most recently added BT Classic pairing - if (op == BtPersistBondingOpDidAdd) { - bt_persistent_storage_set_active_gateway(key); - } - - prv_call_bt_classic_bonding_change_handlers(key, op); - } - return key; -} - -void bt_persistent_storage_delete_bt_classic_pairing_by_id(BTBondingID bonding) { - BtPersistBondingData deleted_data; - if (!prv_delete_pairing_with_type_by_id(bonding, BtPersistBondingTypeBTClassic, &deleted_data)) { - return; - } - - prv_call_bt_classic_bonding_change_handlers(bonding, BtPersistBondingOpWillDelete); - // TODO: Check that the address matches the one we have stored - shared_prf_storage_erase_bt_classic_pairing_data(); -} - - -void bt_persistent_storage_delete_bt_classic_pairing_by_addr(const BTDeviceAddress *bd_addr) { - if (!bd_addr) { - return; - } - - BTBondingID key = prv_get_key_for_bt_classic_addr(bd_addr); - bt_persistent_storage_delete_bt_classic_pairing_by_id(key); -} - -bool bt_persistent_storage_get_bt_classic_pairing_by_id(BTBondingID bonding, - BTDeviceAddress *address_out, - SM128BitKey *link_key_out, - char *name_out, - uint8_t *platform_bits_out) { - BtPersistBondingData data; - if (!prv_file_get(&bonding, sizeof(bonding), &data, sizeof(data))) { - return false; - } - - if (data.type != BtPersistBondingTypeBTClassic) { - PBL_LOG(LOG_LEVEL_ERROR, "Not getting BT Classic id %d. Type mismatch", bonding); - return false; - } - - if (address_out) { - *address_out = data.bt_classic_data.addr; - } - if (link_key_out) { - *link_key_out = data.bt_classic_data.link_key; - } - if (name_out) { - strncpy(name_out, data.bt_classic_data.name, BT_DEVICE_NAME_BUFFER_SIZE); - name_out[BT_DEVICE_NAME_BUFFER_SIZE - 1] = 0; - } - if (platform_bits_out) { - *platform_bits_out = data.bt_classic_data.platform_bits; - } - - return true; -} - -BTBondingID bt_persistent_storage_get_bt_classic_pairing_by_addr(BTDeviceAddress* addr_in, - SM128BitKey *link_key_out, - char *name_out, - uint8_t *platform_bits_out) { - BTBondingID key = prv_get_key_for_bt_classic_addr(addr_in); - if (!bt_persistent_storage_get_bt_classic_pairing_by_id(key, NULL, link_key_out, - name_out, platform_bits_out)) { - return BT_BONDING_ID_INVALID; - } - - return key; -} - -bool bt_persistent_storage_has_active_bt_classic_gateway_bonding(void) { - return prv_has_active_gateway_by_type(BtPersistBondingTypeBTClassic); -} - -typedef struct { - BtPersistBondingDBEachBTClassic cb; - void *cb_data; -} ForEachBTCPairingData; - -static bool bt_persistent_storage_bt_classic_pairing_for_each_itr(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->val_len == 0 || info->key_len != sizeof(BTBondingID)) { - return true; // continue iterating - } - - ForEachBTCPairingData *itr_data = (ForEachBTCPairingData*) context; - - BTBondingID key; - info->get_key(file, (uint8_t*) &key, info->key_len); - - BtPersistBondingData stored_data; - info->get_val(file, (uint8_t*) &stored_data, MIN((unsigned)info->val_len, sizeof(stored_data))); - - if (stored_data.type == BtPersistBondingTypeBTClassic) { - itr_data->cb(&stored_data.bt_classic_data.addr, &stored_data.bt_classic_data.link_key, - stored_data.bt_classic_data.name, &stored_data.bt_classic_data.platform_bits, - itr_data->cb_data); - } - - return true; -} - -void bt_persistent_storage_for_each_bt_classic_pairing(BtPersistBondingDBEachBTClassic cb, - void *context) { - ForEachBTCPairingData itr_data = { - .cb = cb, - .cb_data = context, - }; - prv_file_each(bt_persistent_storage_bt_classic_pairing_for_each_itr, &itr_data); -} - -void analytics_external_collect_bt_pairing_info(void) { - unsigned int ble_pairings_count = prv_get_num_pairings_by_type(BtPersistBondingTypeBTClassic); - - analytics_set(ANALYTICS_DEVICE_METRIC_BT_PAIRING_RECORDS_COUNT, - ble_pairings_count, AnalyticsClient_System); -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Local Device Info - -void bt_persistent_storage_set_active_gateway(BTBondingID bonding) { - BTBondingID old_active_gateway; - int read_size = prv_file_get(&ACTIVE_GATEWAY_KEY, sizeof(ACTIVE_GATEWAY_KEY), - &old_active_gateway, sizeof(old_active_gateway)); - - if (!read_size || old_active_gateway != bonding) { - prv_file_set(&ACTIVE_GATEWAY_KEY, sizeof(ACTIVE_GATEWAY_KEY), &bonding, sizeof(bonding)); - bt_persistent_storage_set_unfaithful(true); - if (bt_driver_supports_bt_classic()) { - bt_driver_classic_update_connectability(); - } - bt_persistent_storage_set_cached_system_capabilities(NULL); - } -} - -bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out, - BtPersistBondingType *type_out) { - BTBondingID active_gateway; - int read_size = prv_file_get(&ACTIVE_GATEWAY_KEY, sizeof(ACTIVE_GATEWAY_KEY), - &active_gateway, sizeof(active_gateway)); - - if (!read_size || active_gateway == BT_BONDING_ID_INVALID) { - return false; - } - - if (bonding_out) { - *bonding_out = active_gateway; - } - if (type_out) { - *type_out = prv_get_type_for_id(active_gateway); - } - - return true; -} - -bool bt_persistent_storage_is_unfaithful(void) { - return prv_file_get_bool(&IS_UNFAITHFUL_KEY, sizeof(IS_UNFAITHFUL_KEY), true /* default */); -} - -void bt_persistent_storage_set_unfaithful(bool is_unfaithful) { - PBL_LOG(LOG_LEVEL_INFO, "Marking the watch as %s", is_unfaithful ? "unfaithful" : "faithful"); - prv_file_set(&IS_UNFAITHFUL_KEY, sizeof(IS_UNFAITHFUL_KEY), - &is_unfaithful, sizeof(is_unfaithful)); -} - -bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { - SM128BitKey keys[SMRootKeyTypeNum]; - int read_size = prv_file_get(&ROOT_KEYS_KEY, sizeof(ROOT_KEYS_KEY), - &keys, sizeof(keys)); - if (!read_size) { - return false; - } - SM128BitKey nil_key = {}; - if (0 == memcmp(&nil_key, &keys[key_type], sizeof(nil_key))) { - return false; - } - - if (key_out) { - memcpy(key_out, &keys[key_type], sizeof(keys[key_type])); - } - - return true; -} - -void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in) { - if (!keys_in) { - return; - } - shared_prf_storage_set_root_keys(keys_in); - - prv_file_set(&ROOT_KEYS_KEY, sizeof(ROOT_KEYS_KEY), - keys_in, SMRootKeyTypeNum * sizeof(SM128BitKey)); -} - -bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { - int read_size = prv_file_get(&DEVICE_NAME_KEY, sizeof(DEVICE_NAME_KEY), - local_device_name_out, max_size); - if (!read_size) { - return false; - } - return true; -} - -void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t size) { - if (!local_device_name) { - return; - } - shared_prf_storage_set_local_device_name(local_device_name); - - prv_file_set(&DEVICE_NAME_KEY, sizeof(DEVICE_NAME_KEY), - local_device_name, size); -} - -bool bt_persistent_storage_get_airplane_mode_enabled(void) { - return prv_file_get_bool(&AIRPLANE_MODE_KEY, sizeof(AIRPLANE_MODE_KEY), false /* default */); -} - -void bt_persistent_storage_set_airplane_mode_enabled(bool new_state) { - prv_file_set(&AIRPLANE_MODE_KEY, sizeof(AIRPLANE_MODE_KEY), - &new_state, sizeof(bool)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Remote Device Info - -static void prv_load_cached_system_capabilities(PebbleProtocolCapabilities *capabilities_out) { - if (!capabilities_out) { - return; - } - - const int read_size = prv_file_get(&SYSTEM_CAPABILITIES_KEY, sizeof(SYSTEM_CAPABILITIES_KEY), - capabilities_out, sizeof(PebbleProtocolCapabilities)); - // Default to zero capabilities if no entry found - if (!read_size) { - *capabilities_out = (PebbleProtocolCapabilities) {}; - } -} - -void bt_persistent_storage_get_cached_system_capabilities( - PebbleProtocolCapabilities *capabilities_out) { - if (!capabilities_out) { - return; - } - - prv_lock(); - { - *capabilities_out = s_cached_system_capabilities; - } - prv_unlock(); -} - -void bt_persistent_storage_set_cached_system_capabilities( - const PebbleProtocolCapabilities *capabilities) { - PebbleProtocolCapabilities diff = {}; - - prv_lock(); - { - // If we were passed a null pointer, we'll just clear the cached capability bits - if (capabilities) { - diff.flags = s_cached_system_capabilities.flags ^ capabilities->flags; - s_cached_system_capabilities = *capabilities; - } else { - diff.flags = s_cached_system_capabilities.flags; - s_cached_system_capabilities = (PebbleProtocolCapabilities) {}; - } - } - prv_unlock(); - - // Only update the cache if the capability flags changed - if (diff.flags) { - prv_file_set(&SYSTEM_CAPABILITIES_KEY, sizeof(SYSTEM_CAPABILITIES_KEY), - capabilities, sizeof(PebbleProtocolCapabilities)); - - PebbleEvent event = { - .type = PEBBLE_CAPABILITIES_CHANGED_EVENT, - .capabilities.flags_diff = diff, - }; - event_put(&event); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Common - -void bt_persistent_storage_init(void) { - // Note: this gets called well before the BT stack is initialized, make sure there is no code - // that tries to use the BT stack in this path. - s_db_mutex = mutex_create(); - - prv_load_data_from_prf(); - - // Load cached capability bits from flash - prv_load_cached_system_capabilities(&s_cached_system_capabilities); -} - -static void prv_delete_all_pairings_itr(SettingsFile *old_file, SettingsFile *new_file, - SettingsRecordInfo *info, void *context) { - if (info->key_len == sizeof(BTBondingID)) { - // Skip pairing entries - return; - } - - // Re-write non-pairing entries - void *key = kernel_zalloc_check(info->key_len); - info->get_key(old_file, key, info->key_len); - - void *data = kernel_malloc_check(info->val_len); - info->get_val(old_file, data, info->val_len); - - settings_file_set(new_file, key, info->key_len, &data, info->val_len); - - kernel_free(key); - kernel_free(data); -} - -void bt_persistent_storage_delete_all_pairings(void) { - prv_lock(); - { - SettingsFile fd; - status_t rv = settings_file_open(&fd, BT_PERSISTENT_STORAGE_FILE_NAME, - BT_PERSISTENT_STORAGE_FILE_SIZE); - if (rv) { - return; - } - - settings_file_rewrite(&fd, prv_delete_all_pairings_itr, NULL); - settings_file_close(&fd); - } - prv_unlock(); - - shared_prf_storage_erase_ble_pairing_data(); - if (bt_driver_supports_bt_classic()) { - shared_prf_storage_erase_bt_classic_pairing_data(); - } -} - -static void prv_dump_bonding_db_data(char display_buf[DISPLAY_BUF_LEN], - BTBondingID bond_id, BtPersistBondingData *data) { - bool matches_prf; - - if (data->type == BtPersistBondingTypeBTClassic) { - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "Classic Key %d", - (int)bond_id); - - bluetooth_persistent_storage_debug_dump_classic_pairing_info( - display_buf, &data->bt_classic_data.addr, &data->bt_classic_data.name[0], - &data->bt_classic_data.link_key, data->bt_classic_data.platform_bits); - - BtPersistBondingBTClassicData sprf_bt_data = {}; - shared_prf_storage_get_bt_classic_pairing_data( - &sprf_bt_data.addr, &sprf_bt_data.name[0], &sprf_bt_data.link_key, - &sprf_bt_data.platform_bits); - matches_prf = - memcmp(&sprf_bt_data, &data->bt_classic_data, sizeof(sprf_bt_data)) == 0; - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, - " BT Pairing Data matches Shared PRF: %s", - bool_to_str(matches_prf)); - - } else if (data->type == BtPersistBondingTypeBLE) { - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "LE Key %d", - (int)bond_id); - - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " ANCS: %d Gateway: %d Req Pin: %d", - (int)data->ble_data.supports_ancs, - (int)data->ble_data.is_gateway, - (int)data->ble_data.requires_address_pinning); - - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Name: %s", - data->ble_data.name); - - SMPairingInfo info = {}; - bt_persistent_storage_assign_sm_pairing_info(&info, &data->ble_data.pairing_info); - bluetooth_persistent_storage_debug_dump_ble_pairing_info(&display_buf[0], &info); - - // does this info match the key stored in shared resources - SMPairingInfo sprf_info = {}; - bool requires_address_pinning; - uint8_t flags; - shared_prf_storage_get_ble_pairing_data(&sprf_info, NULL, &requires_address_pinning, - &flags); - matches_prf = (memcmp(&sprf_info, &info, sizeof(sprf_info)) == 0); - matches_prf &= (requires_address_pinning == data->ble_data.requires_address_pinning); - matches_prf &= (flags == data->ble_data.flags); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, - " SMPairingInfo matches Shared PRF: %s", - bool_to_str(matches_prf)); - } else { - prompt_send_response("Unhandled type of GapBondingDB Data!"); - PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)&data, sizeof(*data)); - } -} - -static void prv_dump_cccd_db_data(char display_buf[DISPLAY_BUF_LEN], - BTCCCDID cccd_id, BtPersistCCCDData *data) { - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "CCCD Key %d", (int)cccd_id); - - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Peer Address: "BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE_PTR(&data->peer.address)); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Handle: 0x%" PRIx16, - data->chr_val_handle); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Flags: 0x%" PRIx16, data->flags); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Value Changed: %s", - bool_to_str(data->value_changed)); -} - -static bool prv_dump_bt_persistent_storage_contents( - SettingsFile *file, SettingsRecordInfo *info, void *context) { - if (info->key_len == 0 || info->val_len == 0) { - prompt_send_response("key or val of 0 length"); - return true; - } - char *display_buf = kernel_malloc_check(DISPLAY_BUF_LEN); - - // get the key - uint8_t key[info->key_len]; - memset(key, 0x0, info->key_len); - info->get_key(file, &key[0], info->key_len); - // prompt_send_response("Raw dump Key"); - // PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)&key, info->key_len); - - uint8_t val[info->val_len]; - memset(val, 0x0, info->val_len); - info->get_val(file, &val[0], info->val_len); - // prompt_send_response("Raw dump Value:"); - // PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)&val, info->val_len); - - if (memcmp(key, ACTIVE_GATEWAY_KEY, info->key_len) == 0) { - PBL_ASSERTN(info->val_len == sizeof(BTBondingID)); - BTBondingID id; - memcpy(&id, val, sizeof(BTBondingID)); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "%s : %d", - ACTIVE_GATEWAY_KEY, (int)id); - - } else if (memcmp(key, IS_UNFAITHFUL_KEY, info->key_len) == 0) { - PBL_ASSERTN(info->val_len == sizeof(bool)); - bool is_unfaithful; - memcpy(&is_unfaithful, val, sizeof(bool)); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "%s : %d", - IS_UNFAITHFUL_KEY, (int)is_unfaithful); - - } else if (memcmp(key, ROOT_KEYS_KEY, info->key_len) == 0) { - SM128BitKey root_keys[SMRootKeyTypeNum], sprf_root_keys[SMRootKeyTypeNum]; - PBL_ASSERTN(info->val_len == sizeof(root_keys)); - memcpy(&root_keys, val, sizeof(root_keys)); - - bluetooth_persistent_storage_debug_dump_root_keys(&root_keys[SMRootKeyTypeEncryption], - &root_keys[SMRootKeyTypeIdentity]); - - if (shared_prf_storage_get_root_key( - SMRootKeyTypeEncryption, &sprf_root_keys[SMRootKeyTypeEncryption]) && - shared_prf_storage_get_root_key( - SMRootKeyTypeIdentity, &sprf_root_keys[SMRootKeyTypeIdentity])) { - bool root_keys_match = - memcmp(&root_keys, &sprf_root_keys, sizeof(SM128BitKey) * SMRootKeyTypeNum) == 0; - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, - " Root keys match shared prf: %s", - bool_to_str(root_keys_match)); - } - } else if (memcmp(key, DEVICE_NAME_KEY, info->key_len) == 0) { - char dev_name[info->val_len + 1]; - memcpy(&dev_name, val, info->val_len); - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, "Device Name: %s", - dev_name); - } else if (memcmp(key, BLE_PINNED_ADDRESS_KEY, info->key_len) == 0) { - if (info->val_len == sizeof(BTDeviceAddress)) { - const BTDeviceAddress *address = (const BTDeviceAddress *)val; - prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, - "Pinned address: "BT_DEVICE_ADDRESS_FMT, - BT_DEVICE_ADDRESS_XPLODE_PTR(address)); - } - } else if (info->key_len == sizeof(BTBondingID)) { - BTBondingID bonding_id; - - memcpy(&bonding_id, key, sizeof(BTBondingID)); - PBL_ASSERTN(sizeof(BtPersistBondingData) == info->val_len); - prv_dump_bonding_db_data(display_buf, bonding_id, (BtPersistBondingData *)&val); - } else if (info->key_len == sizeof(BTCCCDID)) { - BTCCCDID cccd_id; - - memcpy(&cccd_id, key, sizeof(BTCCCDID)); - PBL_ASSERTN(sizeof(BtPersistCCCDData) == info->val_len); - prv_dump_cccd_db_data(display_buf, cccd_id, (BtPersistCCCDData *)&val); - } else { - prompt_send_response("Something new be in the bonding DB!"); - PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, &key[0], info->key_len); - PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, &val[0], info->val_len); - } - - prompt_send_response(""); - - kernel_free(display_buf); - return true; -} - -void bluetooth_persistent_storage_dump_contents(void) { - prv_file_each(prv_dump_bt_persistent_storage_contents, NULL); -} diff --git a/src/fw/services/normal/comm_session/app_session_capabilities.c b/src/fw/services/normal/comm_session/app_session_capabilities.c deleted file mode 100644 index fd34d6bf86..0000000000 --- a/src/fw/services/normal/comm_session/app_session_capabilities.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "process_management/app_install_types.h" -#include "process_management/app_manager.h" -#include "services/common/comm_session/app_session_capabilities.h" -#include "services/normal/settings/settings_file.h" -#include "util/units.h" - -#define APP_SESSION_CAPABILITIES_CACHE_FILENAME "app_comm" - -#define APP_SESSION_CAPABILITIES_CACHE_FILE_MAX_USED_SPACE (KiBYTES(2)) - -static status_t prv_open(SettingsFile *settings_file) { - return settings_file_open(settings_file, APP_SESSION_CAPABILITIES_CACHE_FILENAME, - APP_SESSION_CAPABILITIES_CACHE_FILE_MAX_USED_SPACE); -} - -bool comm_session_current_app_session_cache_has_capability(CommSessionCapability capability) { - CommSession *app_session = comm_session_get_current_app_session(); - - const Uuid app_uuid = app_manager_get_current_app_md()->uuid; - - SettingsFile settings_file; - status_t open_status = prv_open(&settings_file); - - uint64_t cached_capabilities = 0; - if (PASSED(open_status)) { - settings_file_get(&settings_file, - &app_uuid, sizeof(app_uuid), - &cached_capabilities, sizeof(cached_capabilities)); - } - - uint64_t new_capabilities = cached_capabilities; - if (app_session) { - // Connected, grab fresh capabilities data: - new_capabilities = comm_session_get_capabilities(app_session); - - if (FAILED(open_status)) { - // File open failed, return live data without saving to cache - goto done; - } - - if (new_capabilities != cached_capabilities) { - settings_file_set(&settings_file, - &app_uuid, sizeof(app_uuid), - &new_capabilities, sizeof(new_capabilities)); - } - - } else { - // Not connected, use cached data. - - if (FAILED(open_status)) { - // File open failed, no cache available - goto done; - } - } - settings_file_close(&settings_file); - -done: - return ((new_capabilities & capability) != 0); -} - -static void prv_rewrite_cb(SettingsFile *old_file, - SettingsFile *new_file, - SettingsRecordInfo *info, - void *context) { - if (!info->val_len) { - return; // Cache for this app has been deleted, don't rewrite it - } - Uuid key; - uint64_t val; - info->get_key(old_file, &key, sizeof(key)); - info->get_val(old_file, &val, sizeof(val)); - settings_file_set(new_file, &key, sizeof(key), &val, sizeof(val)); -} - -void comm_session_app_session_capabilities_evict(const Uuid *app_uuid) { - SettingsFile settings_file; - if (PASSED(prv_open(&settings_file))) { - settings_file_delete(&settings_file, app_uuid, sizeof(*app_uuid)); - settings_file_close(&settings_file); - } -} - -void comm_session_app_session_capabilities_init(void) { - SettingsFile settings_file; - if (PASSED(prv_open(&settings_file))) { - settings_file_rewrite(&settings_file, prv_rewrite_cb, NULL); - settings_file_close(&settings_file); - } -} diff --git a/src/fw/services/normal/contacts/contacts.h b/src/fw/services/normal/contacts/contacts.h deleted file mode 100644 index 025f316fe0..0000000000 --- a/src/fw/services/normal/contacts/contacts.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "attributes_address.h" - -#include "util/attributes.h" -#include "util/uuid.h" - -typedef struct { - Uuid id; - uint32_t flags; - AttributeList attr_list; - AddressList addr_list; -} Contact; - -//! Lookup a contact given its uuid. Will return NULL if no contact is found. -//! The contact must be freed with contacts_free_contact(). -Contact* contacts_get_contact_by_uuid(const Uuid *uuid); - -//! Frees a contact -void contacts_free_contact(Contact *contact); diff --git a/src/fw/services/normal/data_logging/dls_endpoint.h b/src/fw/services/normal/data_logging/dls_endpoint.h deleted file mode 100644 index b6717654bd..0000000000 --- a/src/fw/services/normal/data_logging/dls_endpoint.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "dls_private.h" - -#include - -void dls_endpoint_init(void); - -void dls_endpoint_close_session(uint8_t session_id); - -bool dls_endpoint_send_data(DataLoggingSession *logging_session, const uint8_t *data, unsigned int num_bytes); - -bool dls_endpoint_open_session(DataLoggingSession *logging_session); - diff --git a/src/fw/services/normal/data_logging/dls_syscalls.c b/src/fw/services/normal/data_logging/dls_syscalls.c deleted file mode 100644 index 6bdf1f528a..0000000000 --- a/src/fw/services/normal/data_logging/dls_syscalls.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "data_logging_service.h" -#include "dls_private.h" - -#include "kernel/memory_layout.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" - -#include - -DEFINE_SYSCALL(DataLoggingSessionRef, sys_data_logging_create, uint32_t tag, - DataLoggingItemType item_type, uint16_t item_size, - void *buffer, bool resume) { - return dls_create_current_process(tag, item_type, item_size, buffer, resume); -} - -DEFINE_SYSCALL(void, sys_data_logging_finish, DataLoggingSessionRef session_ref) { - // TODO: It would be nice to verify the session itself, because they could be - // passing us any memory address (not necesarilly a valid DataLoggingSession). - // An evil developer could potentially use this to confuse the data_logging - // logic, and do evil things with kernel rights. However, it's pretty unlikely - // (especially since our executable code lives in microflash, and hence can't - // just be overwritten by a buffer overrun), so it's probably fine. - DataLoggingSession* session = (DataLoggingSession*)session_ref; - - if (!dls_is_session_valid(session)) { - PBL_LOG(LOG_LEVEL_WARNING, "finish: Invalid session %p", session); - return; // TODO: Return error code? - } - - dls_finish(session); -} - -DEFINE_SYSCALL(DataLoggingResult, sys_data_logging_log, - DataLoggingSessionRef session_ref, void* data, uint32_t num_items) { - DataLoggingSession* session = (DataLoggingSession*)session_ref; - - if (!dls_is_session_valid(session)) { - PBL_LOG(LOG_LEVEL_WARNING, "log: Invalid session %p", session); - return DATA_LOGGING_INVALID_PARAMS; - } - if (data == NULL) { - PBL_LOG(LOG_LEVEL_WARNING, "log: NULL data pointer"); - return DATA_LOGGING_INVALID_PARAMS; - } - - if (PRIVILEGE_WAS_ELEVATED) { - syscall_assert_userspace_buffer(data, num_items * session->item_size); - } - - return dls_log(session, data, num_items); -} diff --git a/src/fw/services/normal/filesystem/app_file.h b/src/fw/services/normal/filesystem/app_file.h deleted file mode 100644 index 40f7dc34ef..0000000000 --- a/src/fw/services/normal/filesystem/app_file.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! Consistent naming of per-app files. -//! -//! All files which are specific to an app are named with a consistent scheme -//! which identifies the files as belonging to the app. This is done by -//! prefixing the filename with a string based on the AppInstallId. Filenames -//! take the format printf("@%08x/%s", (uint32_t)app_id, suffix) to form a -//! pseudo-directory structure. -//! -//! The prefix is fixed-length to make it simple to generate, parse and -//! identify. -#pragma once - -#include -#include - -#include "process_management/app_install_types.h" - -// The suffix starts at offset 10 in the filename -// '@' + "XXXXXXXX" + '/' : (1 + 8 + 1 = 10) -#define APP_FILE_NAME_PREFIX_LENGTH (10) - -//! Make an app-file name from the given app_id and suffix string. -//! -//! @param suffix_len strlen(suffix) -//! -//! @note buffer_len must be > APP_FILE_NAME_PREFIX_LENGTH + suffix_len to fit -//! the full file name including NULL-terminator. -void app_file_name_make(char * restrict buffer, size_t buffer_len, - AppInstallId app_id, const char * restrict suffix, - size_t suffix_len); - -//! Checks whether the given filename is an app file. -bool is_app_file_name(const char *filename); - -//! Checks whether the given filename is an app resource file (suffix = "res") -bool is_app_resource_file_name(const char *filename); - -//! Parses an app-file name to get the AppInstallId. -//! Assumes the file is indeed an app-file -AppInstallId app_file_parse_app_id(const char *filename); - -//! Parses an app-file name to get the AppInstallId. -//! -//! @returns INSTALL_ID_INVALID if the filename is not an app-file. -AppInstallId app_file_get_app_id(const char *filename); diff --git a/src/fw/services/normal/health_sync_endpoint.c b/src/fw/services/normal/health_sync_endpoint.c deleted file mode 100644 index 99c542a000..0000000000 --- a/src/fw/services/normal/health_sync_endpoint.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_sync_endpoint.h" - -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "system/logging.h" -#include "util/attributes.h" - -#define HEALTH_SYNC_ENDPOINT_ID 911 -#define ACK 0x1 -#define NACK 0x2 - -typedef enum HealthSyncEndpointCmd { - HealthSyncEndpointCmd_Sync = 0x1, - HealthSyncEndpointCmd_Ack = 0x11, -} HealthSyncEndpointCmd; - -typedef struct PACKED HealthSyncEndpointSyncMsg { - HealthSyncEndpointCmd cmd : 8; - uint32_t seconds_since_sync; -} HealthSyncEndpointSyncMsg; - -typedef struct PACKED HealthSyncEndpointAckMsg { - HealthSyncEndpointCmd cmd : 8; - uint8_t ack_nack; -} HealthSyncEndpointAckMsg; - -static void prv_send_ack_nack(bool ok) { - const HealthSyncEndpointAckMsg msg = { - .cmd = HealthSyncEndpointCmd_Ack, - .ack_nack = ok ? ACK : NACK, - }; - - comm_session_send_data(comm_session_get_system_session(), - HEALTH_SYNC_ENDPOINT_ID, - (uint8_t*)&msg, - sizeof(HealthSyncEndpointAckMsg), - COMM_SESSION_DEFAULT_TIMEOUT); -} - -#if CAPABILITY_HAS_HEALTH_TRACKING -#include "services/normal/activity/activity_algorithm.h" - -static void prv_sync_health_system_task_cb(void *unused) { - if (activity_tracking_on()) { - // tell the activity service to pool the minutes it's got so far - activity_algorithm_send_minutes(); - } - - // send all data logging data - dls_send_all_sessions(); - // ACK - prv_send_ack_nack(true /*ok*/); -} - -static void prv_handle_sync(const uint8_t *msg, size_t len) { - if (len < sizeof(HealthSyncEndpointSyncMsg)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid SYNC msg received, length: %u", len); - return; - } - - PBL_LOG(LOG_LEVEL_DEBUG, "Received health SYNC request"); - - system_task_add_callback(prv_sync_health_system_task_cb, NULL); -} - -#endif - -void health_sync_protocol_msg_callback(CommSession *session, const uint8_t *msg, size_t len) { -#if CAPABILITY_HAS_HEALTH_TRACKING - if (len < 1) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid message received, length: %u", len); - } - - HealthSyncEndpointCmd cmd = *msg; - switch (cmd) { - case HealthSyncEndpointCmd_Sync: - prv_handle_sync(msg, len); - break; - - default: - PBL_LOG(LOG_LEVEL_WARNING, "Unexpected command received, 0x%x", cmd); - return; - } -#else - prv_send_ack_nack(false /*ok*/); -#endif -} diff --git a/src/fw/services/normal/health_sync_endpoint.h b/src/fw/services/normal/health_sync_endpoint.h deleted file mode 100644 index d22f7c8754..0000000000 --- a/src/fw/services/normal/health_sync_endpoint.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once diff --git a/src/fw/services/normal/legacy/persist_map.h b/src/fw/services/normal/legacy/persist_map.h deleted file mode 100644 index 4d1a1ae741..0000000000 --- a/src/fw/services/normal/legacy/persist_map.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/uuid.h" -#include "system/status_codes.h" - -/* persist_map is an intermediate mapping until app install ids are reimplemented. - * This is an id, uuid record based solution with the id as a positive native int. - */ - -int persist_map_get_size(); - -int persist_map_add_uuid(const Uuid *uuid); - -int persist_map_get_id(const Uuid *uuid); - -int persist_map_auto_id(const Uuid *uuid); - -int persist_map_get_uuid(int id, Uuid *uuid); - -status_t persist_map_init(); - -//! Dump the persist map to LOG_LEVEL_INFO -void persist_map_dump(void); diff --git a/src/fw/services/normal/music.c b/src/fw/services/normal/music.c deleted file mode 100644 index 135de08eae..0000000000 --- a/src/fw/services/normal/music.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "music_internal.h" - -#include "apps/system_apps/music_app.h" -#include "drivers/rtc.h" -#include "kernel/events.h" -#include "process_management/app_manager.h" -#include "shell/system_app_ids.auto.h" -#include "os/mutex.h" -#include "os/tick.h" -#include "system/logging.h" -#include "util/math.h" - -//! @file This module implements the music service. It provides an abstraction layer on top of the -//! various underlying music metadata and control services: the Pebble Protocol -//! music endpoint (see music_endpoint.c) and Apple Media Service (see ams.c). -//! This module also caches the last known metadata and media player state. -//! @note Only one underlying backend is supported at a time. If a second backend tries to "connect" -//! it is ignored. - -#define MUSIC_NORMAL_PLAYBACK_RATE_PERCENT ((int32_t) 100) - -//! Cache of the most recently received now playing data. Note that this is read and written from -//! multiple threads, so access is protected by the mutex member. -struct MusicServiceContext { - PebbleRecursiveMutex *mutex; - - //! The connected server that provides media metadata and accepts control commands - const MusicServerImplementation *implementation; - - //! The volume setting of the current player - uint8_t player_volume_percent; - - char player_name[MUSIC_BUFFER_LENGTH]; - - char title[MUSIC_BUFFER_LENGTH]; - char artist[MUSIC_BUFFER_LENGTH]; - char album[MUSIC_BUFFER_LENGTH]; - - uint32_t track_length_ms; - - //! Position that was last communicated to Pebble by the server. - //! @note This is not necessarily the actual position. See music_get_pos() - uint32_t track_pos_ms; - - //! The time when track_pos_ms was last updated. - RtcTicks track_pos_updated_at; - - //! The current playback rate in percent units. - //! Example values: - //! 100 = normal playback rate - //! 0 = paused - //! 200 = 2x playback rate (Apple's Podcast app can vary the playback rate) - //! -100 = backwards at normal rate - int32_t playback_rate_percent; - - //! The current playback state - MusicPlayState playback_state; -} s_music_ctx; - -void music_init(void) { - s_music_ctx.mutex = mutex_create_recursive(); -} - -static void copy_and_truncate(char *dest, const char *src, size_t src_length) { - size_t cropped_length = MIN(MUSIC_BUFFER_LENGTH - 1, src_length); - if (src) { - memcpy(dest, src, cropped_length); - } - dest[cropped_length] = 0; -} - -static void prv_put_now_playing_changed_event(void) { - PebbleEvent e = { - .type = PEBBLE_MEDIA_EVENT, - .media.type = PebbleMediaEventTypeNowPlayingChanged - }; - event_put(&e); -} - -bool music_set_connected_server(const MusicServerImplementation *implementation, bool connected) { - enum { - Disconnected = -1, - None = 0, - Connected = 1, - } change_type = None; - - mutex_lock_recursive(s_music_ctx.mutex); - - if (connected) { - if (s_music_ctx.implementation == NULL) { - change_type = Connected; - s_music_ctx.implementation = implementation; - PBL_LOG(LOG_LEVEL_INFO, "Music server connected: %s", implementation->debug_name); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Server <0x%p> connected, but another <0x%p> is already registered", - implementation, s_music_ctx.implementation); - } - - } else { - if (s_music_ctx.implementation == implementation) { - // Previously registered server got disconnected - change_type = Disconnected; - s_music_ctx.implementation = NULL; - PBL_LOG(LOG_LEVEL_INFO, "Music server disconnected: %s", implementation->debug_name); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Unknown server <%p> disconnected", implementation); - } - } - - if (change_type != None) { - // Upon connect and disconnect, reset the cached data: - - music_update_player_volume_percent(0); - // Taking short-cut here, music_update_now_playing already puts NowPlayingChanged event, no - // need to put it again by calling music_update_player_name: - s_music_ctx.player_name[0] = 0; - music_update_now_playing(NULL, 0, NULL, 0, NULL, 0); - music_update_track_duration(0); - const MusicPlayerStateUpdate state = { - .playback_state = MusicPlayStateUnknown, - .playback_rate_percent = 0, - .elapsed_time_ms = 0, - }; - music_update_player_playback_state(&state); - - PebbleEvent event = { - .type = PEBBLE_MEDIA_EVENT, - .media = { - .type = (change_type == Connected) ? PebbleMediaEventTypeServerConnected : - PebbleMediaEventTypeServerDisconnected, - }, - }; - event_put(&event); - } - - mutex_unlock_recursive(s_music_ctx.mutex); - - return (change_type != None); -} - -const char * music_get_connected_server_debug_name(void) { - const char *debug_name = NULL; - mutex_lock_recursive(s_music_ctx.mutex); - if (s_music_ctx.implementation) { - debug_name = s_music_ctx.implementation->debug_name; - } - mutex_unlock_recursive(s_music_ctx.mutex); - return debug_name; -} - -void music_update_now_playing(const char *title, size_t title_length, - const char *artist, size_t artist_length, - const char *album, size_t album_length) { - mutex_lock_recursive(s_music_ctx.mutex); - - copy_and_truncate(s_music_ctx.title, title, title_length); - copy_and_truncate(s_music_ctx.artist, artist, artist_length); - copy_and_truncate(s_music_ctx.album, album, album_length); - - mutex_unlock_recursive(s_music_ctx.mutex); - - prv_put_now_playing_changed_event(); -} - -static void prv_update_string_and_put_event(const char *value, size_t value_length, off_t offset) { - mutex_lock_recursive(s_music_ctx.mutex); - char *buffer = ((char *) &s_music_ctx) + offset; - copy_and_truncate(buffer, value, value_length); - mutex_unlock_recursive(s_music_ctx.mutex); - prv_put_now_playing_changed_event(); -} - -void music_update_player_name(const char *player_name, size_t player_name_length) { - // TODO: actually do something with this - off_t o = offsetof(__typeof__(s_music_ctx), player_name); - prv_update_string_and_put_event(player_name, player_name_length, o); -} - -void music_update_track_title(const char *title, size_t title_length) { - off_t o = offsetof(__typeof__(s_music_ctx), title); - prv_update_string_and_put_event(title, title_length, o); -} - -void music_update_track_artist(const char *artist, size_t artist_length) { - off_t o = offsetof(__typeof__(s_music_ctx), artist); - prv_update_string_and_put_event(artist, artist_length, o); -} - -void music_update_track_album(const char *album, size_t album_length) { - off_t o = offsetof(__typeof__(s_music_ctx), album); - prv_update_string_and_put_event(album, album_length, o); -} - -static void prv_put_pos_changed_event(void) { - PebbleEvent e = { - .type = PEBBLE_MEDIA_EVENT, - .media.type = PebbleMediaEventTypeTrackPosChanged, - }; - event_put(&e); -} - -void music_update_track_position(uint32_t track_pos_ms) { - mutex_lock_recursive(s_music_ctx.mutex); - - s_music_ctx.track_pos_ms = track_pos_ms; - s_music_ctx.track_pos_updated_at = rtc_get_ticks(); - - mutex_unlock_recursive(s_music_ctx.mutex); - - prv_put_pos_changed_event(); -} - -void music_update_track_duration(uint32_t track_duration_ms) { - mutex_lock_recursive(s_music_ctx.mutex); - - s_music_ctx.track_length_ms = track_duration_ms; - - mutex_unlock_recursive(s_music_ctx.mutex); - - prv_put_pos_changed_event(); -} - -void music_get_now_playing(char *title, char *artist, char *album) { - mutex_lock_recursive(s_music_ctx.mutex); - - if (title) { - strcpy(title, s_music_ctx.title); - } - if (artist) { - strcpy(artist, s_music_ctx.artist); - } - if (album) { - strcpy(album, s_music_ctx.album); - } - - mutex_unlock_recursive(s_music_ctx.mutex); -} - -bool music_get_player_name(char *player_name_out) { - mutex_lock_recursive(s_music_ctx.mutex); - - const bool has_player_name = (s_music_ctx.player_name[0] != 0); - - if (player_name_out) { - strcpy(player_name_out, s_music_ctx.player_name); - } - - mutex_unlock_recursive(s_music_ctx.mutex); - - return has_player_name; -} - -bool music_has_now_playing(void) { - bool has_now_playing = false; - mutex_lock_recursive(s_music_ctx.mutex); - if (s_music_ctx.title[0] != 0 || - s_music_ctx.artist[0] != 0) { - has_now_playing = true; - } - mutex_unlock_recursive(s_music_ctx.mutex); - return has_now_playing; -} - -uint32_t music_get_ms_since_pos_last_updated(void) { - mutex_lock_recursive(s_music_ctx.mutex); - const RtcTicks time_elapsed_ticks = rtc_get_ticks() - s_music_ctx.track_pos_updated_at; - const uint32_t time_elapsed_ms = ticks_to_milliseconds(time_elapsed_ticks); - mutex_unlock_recursive(s_music_ctx.mutex); - return time_elapsed_ms; -} - -void music_get_pos(uint32_t *track_pos_ms, uint32_t *track_length_ms) { - mutex_lock_recursive(s_music_ctx.mutex); - - const int32_t time_elapsed_ms = music_get_ms_since_pos_last_updated(); - const int32_t track_time_elapsed = - (time_elapsed_ms * s_music_ctx.playback_rate_percent) / MUSIC_NORMAL_PLAYBACK_RATE_PERCENT; - const int32_t pos_ms = s_music_ctx.track_pos_ms + track_time_elapsed; - const int32_t length_ms = s_music_ctx.track_length_ms; - - *track_pos_ms = CLIP(pos_ms, 0, length_ms); - *track_length_ms = length_ms; - - mutex_unlock_recursive(s_music_ctx.mutex); -} - -int32_t music_get_playback_rate_percent(void) { - mutex_lock_recursive(s_music_ctx.mutex); - int32_t playback_rate_percent = s_music_ctx.playback_rate_percent; - mutex_unlock_recursive(s_music_ctx.mutex); - return playback_rate_percent; -} - -uint8_t music_get_volume_percent(void) { - mutex_lock_recursive(s_music_ctx.mutex); - int32_t player_volume_percent = s_music_ctx.player_volume_percent; - mutex_unlock_recursive(s_music_ctx.mutex); - return player_volume_percent; -} - -static void prv_put_state_changed_event(MusicPlayState playback_state) { - PebbleEvent event = { - .type = PEBBLE_MEDIA_EVENT, - .media = { - .type = PebbleMediaEventTypePlaybackStateChanged, - .playback_state = playback_state, - }, - }; - event_put(&event); -} - -void music_update_player_playback_state(const MusicPlayerStateUpdate *state) { - mutex_lock_recursive(s_music_ctx.mutex); - s_music_ctx.playback_state = state->playback_state; - s_music_ctx.playback_rate_percent = state->playback_rate_percent; - s_music_ctx.track_pos_ms = state->elapsed_time_ms; - s_music_ctx.track_pos_updated_at = rtc_get_ticks(); - mutex_unlock_recursive(s_music_ctx.mutex); - - prv_put_state_changed_event(state->playback_state); - prv_put_pos_changed_event(); -} - -void music_update_player_volume_percent(uint8_t volume_percent) { - mutex_lock_recursive(s_music_ctx.mutex); - s_music_ctx.player_volume_percent = volume_percent; - mutex_unlock_recursive(s_music_ctx.mutex); - - PebbleEvent event = { - .type = PEBBLE_MEDIA_EVENT, - .media = { - .type = PebbleMediaEventTypeVolumeChanged, - .volume_percent = volume_percent, - }, - }; - event_put(&event); -} - -MusicPlayState music_get_playback_state(void) { - if (!music_is_playback_state_reporting_supported()) { - return MusicPlayStateUnknown; - } - MusicPlayState result; - mutex_lock_recursive(s_music_ctx.mutex); - result = s_music_ctx.playback_state; - mutex_unlock_recursive(s_music_ctx.mutex); - return result; -} - -static void * prv_implementation_function_for_offset(off_t offset) { - typedef void (*FuncPtr)(void); - FuncPtr func_ptr = NULL; - mutex_lock_recursive(s_music_ctx.mutex); - if (s_music_ctx.implementation) { - func_ptr = *(FuncPtr *) (((const uint8_t *)s_music_ctx.implementation) + offset); - } - mutex_unlock_recursive(s_music_ctx.mutex); - return func_ptr; -} - -void music_command_send(MusicCommand command) { - const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), command_send); - void (*command_send)(MusicCommand) = prv_implementation_function_for_offset(o); - if (command_send) { - command_send(command); - } -} - -void music_request_reduced_latency(bool reduced_latency) { - const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), request_reduced_latency); - void (*request_reduced_latency)(bool) = prv_implementation_function_for_offset(o); - if (request_reduced_latency) { - request_reduced_latency(reduced_latency); - } -} - -void music_request_low_latency_for_period(uint32_t period_ms) { - const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), request_low_latency_for_period); - void (*request_low_latency_for_period)(uint32_t) = prv_implementation_function_for_offset(o); - if (request_low_latency_for_period) { - request_low_latency_for_period(period_ms); - } -} - -bool music_is_command_supported(MusicCommand command) { - const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), - is_command_supported); - bool (*func_ptr)(MusicCommand) = prv_implementation_function_for_offset(o); - if (!func_ptr) { - return false; - } - return func_ptr(command); -} - -static bool prv_call_implementation_bool_return_void_args(off_t offset) { - bool (*func_ptr)(void) = prv_implementation_function_for_offset(offset); - if (!func_ptr) { - return false; // defaults to false when there is no "connected" implementation - } - return func_ptr(); -} - -bool music_needs_user_to_start_playback_on_phone(void) { - const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), - needs_user_to_start_playback_on_phone); - return prv_call_implementation_bool_return_void_args(o); -} - -static bool prv_is_capability_supported(MusicServerCapability capability) { - const off_t o = offsetof(__typeof__(*s_music_ctx.implementation), get_capability_bitset); - MusicServerCapability (*func_ptr)(void) = prv_implementation_function_for_offset(o); - if (!func_ptr) { - return false; - } - return (func_ptr() & capability); -} - -bool music_is_playback_state_reporting_supported(void) { - return prv_is_capability_supported(MusicServerCapabilityPlaybackStateReporting); -} - -bool music_is_progress_reporting_supported(void) { - // Check capability and that track length is greater than 0 - uint32_t track_length_ms; - mutex_lock_recursive(s_music_ctx.mutex); - track_length_ms = s_music_ctx.track_length_ms; - mutex_unlock_recursive(s_music_ctx.mutex); - return (prv_is_capability_supported(MusicServerCapabilityProgressReporting) && track_length_ms); -} - -bool music_is_volume_reporting_supported(void) { - return prv_is_capability_supported(MusicServerCapabilityVolumeReporting); -} - -void command_print_now_playing(void) { - char title[MUSIC_BUFFER_LENGTH]; - char artist[MUSIC_BUFFER_LENGTH]; - char album[MUSIC_BUFFER_LENGTH]; - - music_get_now_playing(title, artist, album); - - char buffer[128]; - dbgserial_putstr_fmt(buffer, 128, "title=%s; artist=%s; album=%s", title, artist, album); -} - diff --git a/src/fw/services/normal/music.h b/src/fw/services/normal/music.h deleted file mode 100644 index e8a372b68c..0000000000 --- a/src/fw/services/normal/music.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#define MUSIC_BUFFER_LENGTH 64 - -typedef enum { - MusicPlayStateUnknown, - MusicPlayStatePlaying, - MusicPlayStatePaused, - MusicPlayStateForwarding, - MusicPlayStateRewinding, - MusicPlayStateInvalid = 0xFF, -} MusicPlayState; - -typedef enum { - MusicCommandPlay, - MusicCommandPause, - MusicCommandTogglePlayPause, - MusicCommandNextTrack, - MusicCommandPreviousTrack, - MusicCommandVolumeUp, - MusicCommandVolumeDown, - MusicCommandAdvanceRepeatMode, - MusicCommandAdvanceShuffleMode, - MusicCommandSkipForward, - MusicCommandSkipBackward, - MusicCommandLike, - MusicCommandDislike, - MusicCommandBookmark, - - NumMusicCommand, -} MusicCommand; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface to Music app - -//! Copy out the current now playing fields into the parameters. We'll assume you've provided -//! buffers that are at least MUSIC_BUFFER_LENGTH in size. -void music_get_now_playing(char *title, char *artist, char *album); - -//! @return True if the music service has Now Playing metadata. -bool music_has_now_playing(void); - -//! Copy out the name of the current player. We'll assume you've provided -//! buffers that are at least MUSIC_BUFFER_LENGTH in size. -//! @return True if the name was copied successfully, or false if there was no name available. -bool music_get_player_name(char *player_name_out); - -//! @return The milliseconds since the track position was last updated. -uint32_t music_get_ms_since_pos_last_updated(void); - -//! Retrieve the position in the current track in the given pointers (which must not be null). -void music_get_pos(uint32_t *track_pos_ms, uint32_t *track_length_ms); - -//! @return The current playback rate percentage. -int32_t music_get_playback_rate_percent(void); - -//! @return The volume percentage. -uint8_t music_get_volume_percent(void); - -//! Retrieve the current playback state. -MusicPlayState music_get_playback_state(void); - -//! @return True if the service supports reporting of the player's playback state. -//! @see music_get_playback_state -bool music_is_playback_state_reporting_supported(void); - -//! @return True if the service support reporting of the playback progress. -//! @see music_get_pos -bool music_is_progress_reporting_supported(void); - -//! @return True if the service supports reporting of the current volume. -//! @see music_get_volume_percent -bool music_is_volume_reporting_supported(void); - -//! Sends the command to the server. Commands are "unreliable", they are sent at "best effort". -//! @param command The command to send. -//! @see music_is_command_supported -void music_command_send(MusicCommand command); - -//! @param The command to test. -//! @return True if the command is supported by the connected server. -bool music_is_command_supported(MusicCommand command); - -//! @return True if playback needs to be started manually by the user from the phone. -bool music_needs_user_to_start_playback_on_phone(void); - -//! Puts the underlying connection in a reduced latency mode, for better responsiveness. -void music_request_reduced_latency(bool reduced_latency); - -//! Puts the underlying connection in a low latency mode, for the best responsiveness. -void music_request_low_latency_for_period(uint32_t period_seconds); - -//! For testing purposes. -const char * music_get_connected_server_debug_name(void); diff --git a/src/fw/services/normal/music_endpoint.c b/src/fw/services/normal/music_endpoint.c deleted file mode 100644 index 79f8deb7aa..0000000000 --- a/src/fw/services/normal/music_endpoint.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "music_endpoint.h" -#include "music_endpoint_types.h" - -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_remote_os.h" -#include "services/normal/music_internal.h" -#include "system/logging.h" -#include "util/math.h" - -static const uint16_t MUSIC_CTRL_ENDPOINT = 0x20; - -static bool s_connected; -static bool s_progress_reporting_supported = true; - -static void prv_send_music_command_to_handset(MusicEndpointCmdID cmd) { - CommSession *session = comm_session_get_system_session(); - if (!session) { - PBL_LOG(LOG_LEVEL_ERROR, "No system session"); - return; - } - comm_session_send_data(session, MUSIC_CTRL_ENDPOINT, - (const uint8_t *)&cmd, 1, COMM_SESSION_DEFAULT_TIMEOUT); -} - -static const uint8_t* prv_read_ptr_and_length_from_buffer(const uint8_t *iter, - const uint8_t *iter_end, - const char** out_str, - size_t *out_length) { - if (!out_str || !out_length) { - return NULL; - } - - *out_length = *iter; - *out_str = (const char*) iter + 1; - - iter += 1 + *out_length; - if (iter > iter_end) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid music message"); - return NULL; - } - return iter; -} - -static void prv_update_now_playing_info(CommSession *session, const uint8_t* msg, size_t length) { - // Read all the lengths from the message so we know how to break it up. - const uint8_t* read_iter = msg; - const char* artist_ptr; - size_t artist_length; - - read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, - &artist_ptr, &artist_length); - if (!read_iter) { - return; - } - - const char* album_ptr; - size_t album_length; - read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, - &album_ptr, &album_length); - if (!read_iter) { - return; - } - - const char* title_ptr; - size_t title_length; - read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, - &title_ptr, &title_length); - if (!read_iter) { - return; - } - - music_update_now_playing(title_ptr, title_length, - artist_ptr, artist_length, - album_ptr, album_length); - - if (comm_session_has_capability(session, CommSessionExtendedMusicService)) { - if (read_iter + sizeof(uint32_t) <= msg + length) { - uint32_t track_duration_ms = *(uint32_t *)read_iter; - music_update_track_duration(track_duration_ms); - } - // TODO: Do something with this info - // read_iter += 4; - // if (read_iter + sizeof(uint16_t) <= msg + length) { - // uint16_t num_tracks = *(uint16_t *)read_iter; - // } - - // TODO: Do something with this info - // read_iter += 2; - // if (read_iter + sizeof(uint16_t) <= msg + length) { - // uint16_t idx_curr_track = *(uint16_t *)read_iter; - // } - } -} - -static void prv_update_play_state_info(CommSession *session, const uint8_t* msg, size_t length) { - if (length < sizeof(MusicEndpointPlayStateInfo)) { - return; - } - MusicEndpointPlayStateInfo *play_state_info = (MusicEndpointPlayStateInfo*) msg; - MusicPlayerStateUpdate player_state_update; - - switch (play_state_info->play_state) { - case MusicEndpointPlaybackStatePaused: - player_state_update.playback_state = MusicPlayStatePaused; - break; - case MusicEndpointPlaybackStatePlaying: - player_state_update.playback_state = MusicPlayStatePlaying; - break; - case MusicEndpointPlaybackStateRewinding: - player_state_update.playback_state = MusicPlayStateRewinding; - break; - case MusicEndpointPlaybackStateForwarding: - player_state_update.playback_state = MusicPlayStateForwarding; - break; - case MusicEndpointPlaybackStateUnknown: - player_state_update.playback_state = MusicPlayStateUnknown; - break; - default: - player_state_update.playback_state = MusicPlayStateInvalid; - } - player_state_update.playback_rate_percent = play_state_info->play_rate; - s_progress_reporting_supported = (play_state_info->track_pos_ms >= 0); - player_state_update.elapsed_time_ms = MAX(play_state_info->track_pos_ms, 0); - // TODO: Do something with this info - // play_state_info->play_shuffle_mode; - // play_state_info->play_repeat_mode; - - music_update_player_playback_state(&player_state_update); -} - -static void prv_update_volume_info(CommSession *session, const uint8_t* msg, size_t length) { - if (length < sizeof(uint8_t)) { - return; - } - music_update_player_volume_percent((uint8_t)*msg); -} - -static void prv_update_player_info(CommSession *session, const uint8_t* msg, size_t length) { - // Read all the lengths from the message so we know how to break it up. - const uint8_t* read_iter = msg; - - const char* player_package_ptr; - size_t player_package_length; - read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, - &player_package_ptr, &player_package_length); - if (!read_iter) { - return; - } - - const char* player_name_ptr; - size_t player_name_length; - read_iter = prv_read_ptr_and_length_from_buffer(read_iter, msg + length, - &player_name_ptr, &player_name_length); - if (!read_iter) { - return; - } - // Not doing anything with player package name - music_update_player_name(player_name_ptr, player_name_length); -} - -void music_protocol_msg_callback(CommSession *session, const uint8_t* msg, size_t length) { - if (!s_connected) { - return; - } - --length; - - switch (*(msg++)) { - case MusicEndpointCmdIDNowPlayingInfoResponse: - prv_update_now_playing_info(session, msg, length); - break; - case MusicEndpointCmdIDPlayStateInfoResponse: - prv_update_play_state_info(session, msg, length); - break; - case MusicEndpointCmdIDVolumeInfoResponse: - prv_update_volume_info(session, msg, length); - break; - case MusicEndpointCmdIDPlayerInfoResponse: - prv_update_player_info(session, msg, length); - break; - default: - PBL_LOG(LOG_LEVEL_DEBUG, "Invalid command 0x%"PRIx8, msg[0]); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// MusicServerImplementation - -static MusicEndpointCmdID prv_pp_command_for_music_command(MusicCommand command) { - switch (command) { - case MusicCommandPlay: - return MusicEndpointCmdIDPlay; - case MusicCommandPause: - return MusicEndpointCmdIDPause; - case MusicCommandTogglePlayPause: - return MusicEndpointCmdIDTogglePlayPause; - case MusicCommandNextTrack: - return MusicEndpointCmdIDNextTrack; - case MusicCommandPreviousTrack: - return MusicEndpointCmdIDPreviousTrack; - case MusicCommandVolumeUp: - return MusicEndpointCmdIDVolumeUp; - case MusicCommandVolumeDown: - return MusicEndpointCmdIDVolumeDown; - - case MusicCommandAdvanceRepeatMode: - case MusicCommandAdvanceShuffleMode: - case MusicCommandSkipForward: - case MusicCommandSkipBackward: - default: - return MusicEndpointCmdIDInvalid; - } -} - -static bool prv_music_is_command_supported(MusicCommand command) { - return (prv_pp_command_for_music_command(command) != MusicEndpointCmdIDInvalid); -} - -static void prv_music_command_send(MusicCommand command) { - const MusicEndpointCmdID pp_command = prv_pp_command_for_music_command(command); - if (pp_command == MusicEndpointCmdIDInvalid) { - return; - } - - prv_send_music_command_to_handset(pp_command); -} - -static MusicServerCapability prv_music_get_capability_bitset(void) { - if (comm_session_has_capability(comm_session_get_system_session(), - CommSessionExtendedMusicService)) { - if (s_progress_reporting_supported) { - return (MusicServerCapabilityPlaybackStateReporting | - MusicServerCapabilityProgressReporting | - MusicServerCapabilityVolumeReporting); - } else { - return (MusicServerCapabilityPlaybackStateReporting | MusicServerCapabilityVolumeReporting); - } - } else { - return MusicServerCapabilityNone; - } -} - -static bool prv_music_needs_user_to_start_playback_on_phone(void) { - return false; // On Android, we can initiate playback from Pebble. -} - -static void prv_music_request_reduced_latency(bool reduced_latency) { - const ResponseTimeState state = reduced_latency ? ResponseTimeMiddle : ResponseTimeMax; - comm_session_set_responsiveness(comm_session_get_system_session(), - BtConsumerMusicServiceIndefinite, state, - MAX_PERIOD_RUN_FOREVER); -} - -static void prv_music_request_low_latency_for_period(uint32_t period_ms) { - comm_session_set_responsiveness(comm_session_get_system_session(), - BtConsumerMusicServiceMomentary, - ResponseTimeMin, - period_ms / MS_PER_SECOND); -} - -static const MusicServerImplementation s_pp_music_implementation = { - .debug_name = "PP", - .is_command_supported = &prv_music_is_command_supported, - .command_send = &prv_music_command_send, - .needs_user_to_start_playback_on_phone = prv_music_needs_user_to_start_playback_on_phone, - .get_capability_bitset = prv_music_get_capability_bitset, - .request_reduced_latency = prv_music_request_reduced_latency, - .request_low_latency_for_period = prv_music_request_low_latency_for_period, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -static void prv_set_connected(bool connected) { - if (s_connected == connected) { - return; // Expected to happen because this is called with `false` for any OS - } - if (music_set_connected_server(&s_pp_music_implementation, connected)) { - s_connected = connected; - } else { - s_connected = false; - } - if (s_connected) { - // Request initial state: - prv_send_music_command_to_handset(MusicEndpointCmdIDGetAllInfo); - } -} - -void music_endpoint_handle_mobile_app_info_event(const PebbleRemoteAppInfoEvent *app_info_event) { - if (app_info_event->os != RemoteOSAndroid) { - // Only on Android we use Pebble Protocol for music metadata and control. - return; - } - prv_set_connected(true); -} - -void music_endpoint_handle_mobile_app_event(const PebbleCommSessionEvent *app_event) { - if (!app_event->is_open && app_event->is_system) { - // Pebble mobile app went away, communicate the disconnection to the upper layers: - prv_set_connected(false); - } -} diff --git a/src/fw/services/normal/music_endpoint.h b/src/fw/services/normal/music_endpoint.h deleted file mode 100644 index ca8723bdba..0000000000 --- a/src/fw/services/normal/music_endpoint.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/events.h" - -void music_endpoint_handle_mobile_app_info_event(const PebbleRemoteAppInfoEvent *app_info_event); - -void music_endpoint_handle_mobile_app_event(const PebbleCommSessionEvent *app_event); diff --git a/src/fw/services/normal/music_endpoint_types.h b/src/fw/services/normal/music_endpoint_types.h deleted file mode 100644 index 539ce8da5b..0000000000 --- a/src/fw/services/normal/music_endpoint_types.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "util/attributes.h" - -typedef enum { - // Watch -> Phone - MusicEndpointCmdIDTogglePlayPause = 0x1, - MusicEndpointCmdIDPause = 0x2, - MusicEndpointCmdIDPlay = 0x3, - MusicEndpointCmdIDNextTrack = 0x4, - MusicEndpointCmdIDPreviousTrack = 0x5, - MusicEndpointCmdIDVolumeUp = 0x6, - MusicEndpointCmdIDVolumeDown = 0x7, - MusicEndpointCmdIDGetAllInfo = 0x8, - - // Phone -> Watch - MusicEndpointCmdIDNowPlayingInfoResponse = 0x10, - MusicEndpointCmdIDPlayStateInfoResponse = 0x11, - MusicEndpointCmdIDVolumeInfoResponse = 0x12, - MusicEndpointCmdIDPlayerInfoResponse = 0x13, - - MusicEndpointCmdIDInvalid = 0xff, -} MusicEndpointCmdID; - -typedef enum { - MusicEndpointPlaybackStatePaused = 0, - MusicEndpointPlaybackStatePlaying = 1, - MusicEndpointPlaybackStateRewinding = 2, - MusicEndpointPlaybackStateForwarding = 3, - MusicEndpointPlaybackStateUnknown = 4, -} MusicEndpointPlaybackState; - -typedef enum { - MusicEndpointShuffleModeUnknown = 0, - MusicEndpointShuffleModeOff = 1, - MusicEndpointShuffleModeOn = 2, -} MusicEndpointShuffleMode; - -typedef enum { - MusicEndpointRepeatModeUnknown = 0, - MusicEndpointRepeatModeOff = 1, - MusicEndpointRepeatModeOne = 2, - MusicEndpointRepeatModeAll = 3, -} MusicEndpointRepeatMode; - -typedef struct PACKED { - uint8_t play_state; - int32_t track_pos_ms; - int32_t play_rate; - uint8_t play_shuffle_mode; - uint8_t play_repeat_mode; -} MusicEndpointPlayStateInfo; diff --git a/src/fw/services/normal/notifications/action_chaining_window.h b/src/fw/services/normal/notifications/action_chaining_window.h deleted file mode 100644 index 7c22066b73..0000000000 --- a/src/fw/services/normal/notifications/action_chaining_window.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/window_stack.h" -#include "services/normal/timeline/item.h" - -typedef void (*ActionChainingMenuSelectCb)(Window *chaining_window, - TimelineItemAction *action, void *context); -typedef void (*ActionChainingMenuClosedCb)(void *context); - -void action_chaining_window_push(WindowStack *window_stack, const char *title, - TimelineItemActionGroup *action_group, - ActionChainingMenuSelectCb select_cb, - void *select_cb_context, - ActionChainingMenuClosedCb closed_cb, - void *closed_cb_context); diff --git a/src/fw/services/normal/notifications/alerts.c b/src/fw/services/normal/notifications/alerts.c deleted file mode 100644 index fabe7378d8..0000000000 --- a/src/fw/services/normal/notifications/alerts.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "alerts.h" -#include "alerts_private.h" - -#include "drivers/battery.h" -#include "drivers/rtc.h" -#include "kernel/low_power.h" -#include "services/common/analytics/analytics.h" -#include "services/common/firmware_update.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/notifications/alerts_preferences_private.h" - -static const int NOTIFICATION_VIBE_HOLDOFF_MS = 3000; -static RtcTicks s_notification_vibe_tick_timestamp = 0; - -////////////////// -// Private Functions -////////////////// - -static int64_t prv_get_ms_since_last_notification_vibe(void) { - RtcTicks current_ticks = rtc_get_ticks(); - int64_t millis_since_last_vibe = - (current_ticks - s_notification_vibe_tick_timestamp) * 1000 / RTC_TICKS_HZ; // x1000 for ms - return millis_since_last_vibe; -} - -////////////////// -// Public Functions -////////////////// - -void alerts_incoming_alert_analytics() { - if (do_not_disturb_is_active()) { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_DND_COUNT, AnalyticsClient_System); - } -} - -bool alerts_should_notify_for_type(AlertType type) { - if (low_power_is_active()) { - return false; - } - - if (firmware_update_is_in_progress()) { - return false; - } - - return alerts_preferences_get_alert_mask() & type; -} - -bool alerts_should_enable_backlight_for_type(AlertType type) { - if (do_not_disturb_is_active() && !(alerts_preferences_dnd_get_mask() & type)) { - return false; - } - - return alerts_should_notify_for_type(type); -} - -bool alerts_should_vibrate_for_type(AlertType type) { - if (do_not_disturb_is_active() && !(alerts_preferences_dnd_get_mask() & type)) { - return false; - } - - if (!alerts_should_notify_for_type(type)) { - return false; - } - - if (battery_is_usb_connected()) { - return false; - } - - if (prv_get_ms_since_last_notification_vibe() < NOTIFICATION_VIBE_HOLDOFF_MS) { - return false; - } - - return alerts_preferences_get_vibrate(); -} - -bool alerts_get_vibrate(void) { - return alerts_preferences_get_vibrate(); -} - -AlertMask alerts_get_mask(void) { - return alerts_preferences_get_alert_mask(); -} - -AlertMask alerts_get_dnd_mask(void) { - return alerts_preferences_dnd_get_mask(); -} - -uint32_t alerts_get_notification_window_timeout_ms(void) { - return alerts_preferences_get_notification_window_timeout_ms(); -} - -void alerts_set_vibrate(bool enable) { - alerts_preferences_set_vibrate(enable); -} - -void alerts_set_mask(AlertMask mask) { - alerts_preferences_set_alert_mask(mask); -} - -void alerts_set_dnd_mask(AlertMask mask) { - alerts_preferences_dnd_set_mask(mask); -} - -void alerts_set_notification_vibe_timestamp() { - // if we do vibrate, update timestamp of last vibration - s_notification_vibe_tick_timestamp = rtc_get_ticks(); -} - -void alerts_set_notification_window_timeout_ms(uint32_t timeout_ms) { - alerts_preferences_set_notification_window_timeout_ms(timeout_ms); -} - -void alerts_init() { - alerts_preferences_init(); - do_not_disturb_init(); - vibe_intensity_init(); -} diff --git a/src/fw/services/normal/notifications/alerts.h b/src/fw/services/normal/notifications/alerts.h deleted file mode 100644 index 46d7e1568c..0000000000 --- a/src/fw/services/normal/notifications/alerts.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "services/normal/notifications/notification_types.h" - -typedef enum AlertType { - AlertInvalid = NotificationInvalid, - AlertMobile = NotificationMobile, - AlertPhoneCall = NotificationPhoneCall, - AlertOther = NotificationOther, - AlertReminder = NotificationReminder -} AlertType; - -// Service to determine how and if the user gets alerted on a call/notification - -//! Call this function before alerting the user in any notification/call for the alerts service -//! to handle analytics operations. -void alerts_incoming_alert_analytics(); - -bool alerts_should_notify_for_type(AlertType type); - -bool alerts_should_enable_backlight_for_type(AlertType type); - -bool alerts_should_vibrate_for_type(AlertType type); - -//! When vibrating for an incoming notification, call this function to prevent multiple vibes -//! within a short period of time. -void alerts_set_notification_vibe_timestamp(); diff --git a/src/fw/services/normal/notifications/alerts_preferences.c b/src/fw/services/normal/notifications/alerts_preferences.c deleted file mode 100644 index 633a1bfaab..0000000000 --- a/src/fw/services/normal/notifications/alerts_preferences.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/alerts_preferences_private.h" - -#include "drivers/rtc.h" -#include "popups/notifications/notification_window.h" -#include "services/common/analytics/analytics.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/vibes/vibe_intensity.h" -#include "system/passert.h" -#include "os/mutex.h" -#include "util/bitset.h" - -#include - -#define FILE_NAME "notifpref" -#define FILE_LEN (1024) - -static PebbleMutex *s_mutex; - -/////////////////////////////////// -//! Preference keys -/////////////////////////////////// - -#define PREF_KEY_MASK "mask" -static AlertMask s_mask = AlertMaskAllOn; - -#define PREF_KEY_DND_INTERRUPTIONS_MASK "dndInterruptionsMask" -static AlertMask s_dnd_interruptions_mask = AlertMaskAllOff; - -#define PREF_KEY_VIBE "vibe" -static bool s_vibe_on_notification = true; - -#define PREF_KEY_VIBE_INTENSITY "vibeIntensity" -static VibeIntensity s_vibe_intensity = DEFAULT_VIBE_INTENSITY; - -#if CAPABILITY_HAS_VIBE_SCORES -#define PREF_KEY_VIBE_SCORE_NOTIFICATIONS ("vibeScoreNotifications") -static VibeScoreId s_vibe_score_notifications = DEFAULT_VIBE_SCORE_NOTIFS; - -#define PREF_KEY_VIBE_SCORE_INCOMING_CALLS ("vibeScoreIncomingCalls") -static VibeScoreId s_vibe_score_incoming_calls = DEFAULT_VIBE_SCORE_INCOMING_CALLS; - -#define PREF_KEY_VIBE_SCORE_ALARMS ("vibeScoreAlarms") -static VibeScoreId s_vibe_score_alarms = DEFAULT_VIBE_SCORE_ALARMS; -#endif - -#define PREF_KEY_DND_MANUALLY_ENABLED "dndManuallyEnabled" -static bool s_do_not_disturb_manually_enabled = false; - -#define PREF_KEY_DND_SMART_ENABLED "dndSmartEnabled" -static bool s_do_not_disturb_smart_dnd_enabled = false; - -#define PREF_KEY_FIRST_USE_COMPLETE "firstUseComplete" -static uint32_t s_first_use_complete = 0; - -#define PREF_KEY_NOTIF_WINDOW_TIMEOUT "notifWindowTimeout" -static uint32_t s_notif_window_timeout_ms = NOTIF_WINDOW_TIMEOUT_DEFAULT; - -#define PREF_KEY_NOTIF_DESIGN_STYLE "notifDesignStyle" -static bool s_notification_alternative_design = false; // true = alternative (black banner), false = standard (default) - -/////////////////////////////////// -//! Legacy preference keys -/////////////////////////////////// - -#define PREF_KEY_LEGACY_DND_SCHEDULE "dndSchedule" -static DoNotDisturbSchedule s_legacy_dnd_schedule = { - .from_hour = 0, - .to_hour = 6, -}; - -#define PREF_KEY_LEGACY_DND_SCHEDULE_ENABLED "dndEnabled" -static bool s_legacy_dnd_schedule_enabled = false; - -#define PREF_KEY_LEGACY_DND_MANUAL_FIRST_USE "dndManualFirstUse" -#define PREF_KEY_LEGACY_DND_SMART_FIRST_USE "dndSmartFirstUse" - -/////////////////////////////////// -//! Variables -/////////////////////////////////// - -typedef struct DoNotDisturbScheduleConfig { - DoNotDisturbSchedule schedule; - bool enabled; -} DoNotDisturbScheduleConfig; - -typedef struct DoNotDisturbScheduleConfigKeys { - const char *schedule_pref_key; - const char *enabled_pref_key; -} DoNotDisturbScheduleConfigKeys; - -static DoNotDisturbScheduleConfig s_dnd_schedule[NumDNDSchedules]; - -static const DoNotDisturbScheduleConfigKeys s_dnd_schedule_keys[NumDNDSchedules] = { - [WeekdaySchedule] = { - .schedule_pref_key = "dndWeekdaySchedule", - .enabled_pref_key = "dndWeekdayScheduleEnabled", - }, - [WeekendSchedule] = { - .schedule_pref_key = "dndWeekendSchedule", - .enabled_pref_key = "dndWeekendScheduleEnabled", - } -}; - -static void prv_migrate_legacy_dnd_schedule(SettingsFile *file) { - // If Weekday schedule does not exist, assume that the other 3 settings files are missing as well - // Set the new schedules to the legacy schedule and delete the legacy schedule - if (!settings_file_exists(file, s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, - strlen(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key))) { -#define SET_PREF_ALREADY_OPEN(key, value) \ - settings_file_set(file, key, strlen(key), value, sizeof(value)); - - s_dnd_schedule[WeekdaySchedule].schedule = s_legacy_dnd_schedule; - SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, - &s_dnd_schedule[WeekdaySchedule].schedule); - s_dnd_schedule[WeekdaySchedule].enabled = s_legacy_dnd_schedule_enabled; - SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekdaySchedule].enabled_pref_key, - &s_dnd_schedule[WeekdaySchedule].enabled); - s_dnd_schedule[WeekendSchedule].schedule = s_legacy_dnd_schedule; - SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekendSchedule].schedule_pref_key, - &s_dnd_schedule[WeekendSchedule].schedule); - s_dnd_schedule[WeekendSchedule].enabled = s_legacy_dnd_schedule_enabled; - SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekendSchedule].enabled_pref_key, - &s_dnd_schedule[WeekendSchedule].enabled); -#undef SET_PREF_ALREADY_OPEN - -#define DELETE_PREF(key) \ - do { \ - if (settings_file_exists(file, key, strlen(key))) { \ - settings_file_delete(file, key, strlen(key)); \ - } \ - } while (0) - - DELETE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE); - DELETE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE_ENABLED); -#undef DELETE_PREF - } -} - -#if !PLATFORM_TINTIN -static void prv_migrate_legacy_first_use_settings(SettingsFile *file) { - // These don't need to be initialized since settings_file_get will clear them on error - uint8_t manual_dnd_first_use_complete; - bool smart_dnd_first_use_complete; - - // Migrate the old first use dialog prefs -#define RESTORE_AND_DELETE_PREF(key, var) \ - do { \ - if (settings_file_get(file, key, strlen(key), &var, sizeof(var)) == S_SUCCESS) { \ - settings_file_delete(file, key, strlen(key)); \ - } \ - } while (0) - - RESTORE_AND_DELETE_PREF(PREF_KEY_LEGACY_DND_MANUAL_FIRST_USE, manual_dnd_first_use_complete); - RESTORE_AND_DELETE_PREF(PREF_KEY_LEGACY_DND_SMART_FIRST_USE, smart_dnd_first_use_complete); - - s_first_use_complete |= manual_dnd_first_use_complete << FirstUseSourceManualDNDActionMenu; - s_first_use_complete |= smart_dnd_first_use_complete << FirstUseSourceSmartDND; - -#undef RESTORE_AND_DELETE_PREF -} -#endif - -#if CAPABILITY_HAS_VIBE_SCORES -static void prv_save_all_vibe_scores_to_file(SettingsFile *file) { -#define SET_PREF_ALREADY_OPEN(key, value) \ - settings_file_set(file, key, strlen(key), &value, sizeof(value)); - - SET_PREF_ALREADY_OPEN(PREF_KEY_VIBE_SCORE_NOTIFICATIONS, s_vibe_score_notifications); - SET_PREF_ALREADY_OPEN(PREF_KEY_VIBE_SCORE_INCOMING_CALLS, s_vibe_score_incoming_calls); - SET_PREF_ALREADY_OPEN(PREF_KEY_VIBE_SCORE_ALARMS, s_vibe_score_alarms); -#undef SET_PREF_ALREADY_OPEN -} - -static VibeScoreId prv_return_default_if_invalid(VibeScoreId id, VibeScoreId default_id) { - return vibe_score_info_is_valid(id) ? id : default_id; -} - -// Uses the default vibe pattern id if the given score isn't valid -static void prv_ensure_valid_vibe_scores(void) { - s_vibe_score_notifications = prv_return_default_if_invalid(s_vibe_score_notifications, - DEFAULT_VIBE_SCORE_NOTIFS); - s_vibe_score_incoming_calls = prv_return_default_if_invalid(s_vibe_score_incoming_calls, - DEFAULT_VIBE_SCORE_INCOMING_CALLS); - s_vibe_score_alarms = prv_return_default_if_invalid(s_vibe_score_alarms, - DEFAULT_VIBE_SCORE_ALARMS); -} - -static void prv_set_vibe_scores_based_on_legacy_intensity(VibeIntensity intensity) { - if (intensity == VibeIntensityHigh) { - s_vibe_score_notifications = VibeScoreId_StandardShortPulseHigh; - s_vibe_score_incoming_calls = VibeScoreId_StandardLongPulseHigh; - s_vibe_score_alarms = VibeScoreId_StandardLongPulseHigh; - } else { - s_vibe_score_notifications = VibeScoreId_StandardShortPulseLow; - s_vibe_score_incoming_calls = VibeScoreId_StandardLongPulseLow; - s_vibe_score_alarms = VibeScoreId_StandardLongPulseLow; - } -} - -static void prv_migrate_vibe_intensity_to_vibe_scores(SettingsFile *file) { - // We use the existence of the notifications vibe score pref as a shallow measurement of whether - // or not the user has migrated to vibe scores - const bool user_has_migrated_to_vibe_scores = - settings_file_exists(file, PREF_KEY_VIBE_SCORE_NOTIFICATIONS, - strlen(PREF_KEY_VIBE_SCORE_NOTIFICATIONS)); - - if (!user_has_migrated_to_vibe_scores) { - // If the user previously set a vibration intensity, set the vibe scores based on that intensity - if (settings_file_exists(file, PREF_KEY_VIBE_INTENSITY, strlen(PREF_KEY_VIBE_INTENSITY))) { - prv_set_vibe_scores_based_on_legacy_intensity(s_vibe_intensity); - } else if (rtc_is_timezone_set()) { - // Otherwise, if the timezone has been set, then we assume this is a user on 3.10 and lower - // that has not touched their vibe intensity preferences. - // rtc_is_timezone_set() was chosen because it is a setting that gets written when the user - // connects their watch to a phone - prv_set_vibe_scores_based_on_legacy_intensity(DEFAULT_VIBE_INTENSITY); - } - } - - // PREF_KEY_VIBE, which used to track whether the user enabled/disabled vibrations, has been - // deprecated in favor of the "disabled vibe score", VibeScoreId_Disabled, so switch to using it - // and delete PREF_KEY_VIBE from the settings file if PREF_KEY_VIBE exists in the settings file - if (settings_file_exists(file, PREF_KEY_VIBE, strlen(PREF_KEY_VIBE))) { - if (!s_vibe_on_notification) { - s_vibe_score_notifications = VibeScoreId_Disabled; - s_vibe_score_incoming_calls = VibeScoreId_Disabled; - } - settings_file_delete(file, PREF_KEY_VIBE, strlen(PREF_KEY_VIBE)); - } -} -#endif - -void alerts_preferences_init(void) { - s_mutex = mutex_create(); - - SettingsFile file = {{0}}; - if (settings_file_open(&file, FILE_NAME, FILE_LEN) != S_SUCCESS) { - return; - } - -#define RESTORE_PREF(key, var) \ - do { \ - __typeof__(var) _tmp; \ - if (settings_file_get( \ - &file, key, strlen(key), &_tmp, sizeof(_tmp)) == S_SUCCESS) { \ - var = _tmp; \ - } \ - } while (0) - - RESTORE_PREF(PREF_KEY_MASK, s_mask); - RESTORE_PREF(PREF_KEY_VIBE, s_vibe_on_notification); - RESTORE_PREF(PREF_KEY_VIBE_INTENSITY, s_vibe_intensity); -#if CAPABILITY_HAS_VIBE_SCORES - RESTORE_PREF(PREF_KEY_VIBE_SCORE_NOTIFICATIONS, s_vibe_score_notifications); - RESTORE_PREF(PREF_KEY_VIBE_SCORE_INCOMING_CALLS, s_vibe_score_incoming_calls); - RESTORE_PREF(PREF_KEY_VIBE_SCORE_ALARMS, s_vibe_score_alarms); -#endif - RESTORE_PREF(PREF_KEY_DND_MANUALLY_ENABLED, s_do_not_disturb_manually_enabled); - RESTORE_PREF(PREF_KEY_DND_SMART_ENABLED, s_do_not_disturb_smart_dnd_enabled); - RESTORE_PREF(PREF_KEY_DND_INTERRUPTIONS_MASK, s_dnd_interruptions_mask); - RESTORE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE, s_legacy_dnd_schedule); - RESTORE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE_ENABLED, s_legacy_dnd_schedule_enabled); - RESTORE_PREF(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, - s_dnd_schedule[WeekdaySchedule].schedule); - RESTORE_PREF(s_dnd_schedule_keys[WeekdaySchedule].enabled_pref_key, - s_dnd_schedule[WeekdaySchedule].enabled); - RESTORE_PREF(s_dnd_schedule_keys[WeekendSchedule].schedule_pref_key, - s_dnd_schedule[WeekendSchedule].schedule); - RESTORE_PREF(s_dnd_schedule_keys[WeekendSchedule].enabled_pref_key, - s_dnd_schedule[WeekendSchedule].enabled); - RESTORE_PREF(PREF_KEY_FIRST_USE_COMPLETE, s_first_use_complete); - RESTORE_PREF(PREF_KEY_NOTIF_WINDOW_TIMEOUT, s_notif_window_timeout_ms); - RESTORE_PREF(PREF_KEY_NOTIF_DESIGN_STYLE, s_notification_alternative_design); -#undef RESTORE_PREF - - prv_migrate_legacy_dnd_schedule(&file); - - // tintin watches don't have these prefs, so we can pull this out to save on codespace -#if !PLATFORM_TINTIN - prv_migrate_legacy_first_use_settings(&file); -#endif -#if CAPABILITY_HAS_VIBE_SCORES - prv_migrate_vibe_intensity_to_vibe_scores(&file); - prv_ensure_valid_vibe_scores(); - prv_save_all_vibe_scores_to_file(&file); -#endif - - settings_file_close(&file); -} - -// Convenience macro for setting a string key to a non-pointer value. -#define SET_PREF(key, value) \ - prv_set_pref(key, strlen(key), &value, sizeof(value)) -static void prv_set_pref(const void *key, size_t key_len, const void *value, - size_t value_len) { - mutex_lock(s_mutex); - SettingsFile file = {{0}}; - if (settings_file_open(&file, FILE_NAME, FILE_LEN) != S_SUCCESS) { - goto cleanup; - } - settings_file_set(&file, key, key_len, value, value_len); - settings_file_close(&file); -cleanup: - mutex_unlock(s_mutex); -} - -AlertMask alerts_preferences_get_alert_mask(void) { - if (s_mask == AlertMaskAllOnLegacy) { - // Migration for notification settings previously configured under - // old bit setup. - alerts_preferences_set_alert_mask(AlertMaskAllOn); - } - return s_mask; -} - -void alerts_preferences_set_alert_mask(AlertMask mask) { - s_mask = mask; - SET_PREF(PREF_KEY_MASK, s_mask); -} - -uint32_t alerts_preferences_get_notification_window_timeout_ms(void) { - return s_notif_window_timeout_ms; -} - -void alerts_preferences_set_notification_window_timeout_ms(uint32_t timeout_ms) { - s_notif_window_timeout_ms = timeout_ms; - SET_PREF(PREF_KEY_NOTIF_WINDOW_TIMEOUT, s_notif_window_timeout_ms); -} - -bool alerts_preferences_get_notification_alternative_design(void) { - return s_notification_alternative_design; -} - -void alerts_preferences_set_notification_alternative_design(bool alternative) { - s_notification_alternative_design = alternative; - SET_PREF(PREF_KEY_NOTIF_DESIGN_STYLE, s_notification_alternative_design); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Vibes - -bool alerts_preferences_get_vibrate(void) { - return s_vibe_on_notification; -} - -void alerts_preferences_set_vibrate(bool enable) { - s_vibe_on_notification = enable; - SET_PREF(PREF_KEY_VIBE, s_vibe_on_notification); -} - -VibeIntensity alerts_preferences_get_vibe_intensity(void) { - return s_vibe_intensity; -} - -void alerts_preferences_set_vibe_intensity(VibeIntensity intensity) { - s_vibe_intensity = intensity; - SET_PREF(PREF_KEY_VIBE_INTENSITY, s_vibe_intensity); -} - -#if CAPABILITY_HAS_VIBE_SCORES -VibeScoreId alerts_preferences_get_vibe_score_for_client(VibeClient client) { - switch (client) { - case VibeClient_Notifications: - return s_vibe_score_notifications; - case VibeClient_PhoneCalls: - return s_vibe_score_incoming_calls; - case VibeClient_Alarms: - return s_vibe_score_alarms; - default: - WTF; - } -} - -void alerts_preferences_set_vibe_score_for_client(VibeClient client, VibeScoreId id) { - const char *key = NULL; - switch (client) { - case VibeClient_Notifications: { - s_vibe_score_notifications = id; - key = PREF_KEY_VIBE_SCORE_NOTIFICATIONS; - break; - } - case VibeClient_PhoneCalls: { - s_vibe_score_incoming_calls = id; - key = PREF_KEY_VIBE_SCORE_INCOMING_CALLS; - break; - } - case VibeClient_Alarms: { - s_vibe_score_alarms = id; - key = PREF_KEY_VIBE_SCORE_ALARMS; - break; - } - default: { - WTF; - } - } - SET_PREF(key, id); -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! DND - -void alerts_preferences_dnd_set_mask(AlertMask mask) { - s_dnd_interruptions_mask = mask; - SET_PREF(PREF_KEY_DND_INTERRUPTIONS_MASK, s_dnd_interruptions_mask); -} - -AlertMask alerts_preferences_dnd_get_mask(void) { - return s_dnd_interruptions_mask; -} - -bool alerts_preferences_dnd_is_manually_enabled(void) { - return s_do_not_disturb_manually_enabled; -} - -void alerts_preferences_dnd_set_manually_enabled(bool enable) { - s_do_not_disturb_manually_enabled = enable; - SET_PREF(PREF_KEY_DND_MANUALLY_ENABLED, s_do_not_disturb_manually_enabled); -} - -void alerts_preferences_dnd_get_schedule(DoNotDisturbScheduleType type, - DoNotDisturbSchedule *schedule_out) { - *schedule_out = s_dnd_schedule[type].schedule; -}; - -void alerts_preferences_dnd_set_schedule(DoNotDisturbScheduleType type, - const DoNotDisturbSchedule *schedule) { - s_dnd_schedule[type].schedule = *schedule; - SET_PREF(s_dnd_schedule_keys[type].schedule_pref_key, s_dnd_schedule[type].schedule); -}; - -bool alerts_preferences_dnd_is_schedule_enabled(DoNotDisturbScheduleType type) { - return s_dnd_schedule[type].enabled; -} - -void alerts_preferences_dnd_set_schedule_enabled(DoNotDisturbScheduleType type, bool on) { - s_dnd_schedule[type].enabled = on; - SET_PREF(s_dnd_schedule_keys[type].enabled_pref_key, s_dnd_schedule[type].enabled); -} - -bool alerts_preferences_check_and_set_first_use_complete(FirstUseSource source) { - if (s_first_use_complete & (1 << source)) { - return true; - }; - - s_first_use_complete |= (1 << source); - SET_PREF(PREF_KEY_FIRST_USE_COMPLETE, s_first_use_complete); - return false; -} - -bool alerts_preferences_dnd_is_smart_enabled(void) { - return s_do_not_disturb_smart_dnd_enabled; -} - -void alerts_preferences_dnd_set_smart_enabled(bool enable) { - s_do_not_disturb_smart_dnd_enabled = enable; - SET_PREF(PREF_KEY_DND_SMART_ENABLED, s_do_not_disturb_smart_dnd_enabled); -} - -void analytics_external_collect_alerts_preferences(void) { - uint8_t alerts_dnd_prefs_bitmask = 0; - alerts_dnd_prefs_bitmask |= (alerts_preferences_dnd_is_manually_enabled() << 0); - alerts_dnd_prefs_bitmask |= (alerts_preferences_dnd_is_smart_enabled() << 1); - alerts_dnd_prefs_bitmask |= (alerts_preferences_dnd_is_schedule_enabled(WeekdaySchedule) << 2); - alerts_dnd_prefs_bitmask |= (alerts_preferences_dnd_is_schedule_enabled(WeekendSchedule) << 3); - analytics_set(ANALYTICS_DEVICE_METRIC_ALERTS_DND_PREFS_BITMASK, - alerts_dnd_prefs_bitmask, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_ALERTS_MASK, - (uint8_t) alerts_preferences_get_alert_mask(), AnalyticsClient_System); -} diff --git a/src/fw/services/normal/notifications/alerts_preferences.h b/src/fw/services/normal/notifications/alerts_preferences.h deleted file mode 100644 index 194b160fd1..0000000000 --- a/src/fw/services/normal/notifications/alerts_preferences.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum FirstUseSource { - FirstUseSourceManualDNDActionMenu = 0, - FirstUseSourceManualDNDSettingsMenu, - FirstUseSourceSmartDND, - FirstUseSourceDismiss -} FirstUseSource; - -typedef enum MuteBitfield { - MuteBitfield_None = 0b00000000, - MuteBitfield_Always = 0b01111111, - MuteBitfield_Weekdays = 0b00111110, - MuteBitfield_Weekends = 0b01000001, -} MuteBitfield; - -//! Checks whether a given "first use" dialog has been shown and sets it as complete -//! @param source The "first use" bit to check -//! @return true if the dialog has already been shown, false otherwise -bool alerts_preferences_check_and_set_first_use_complete(FirstUseSource source); diff --git a/src/fw/services/normal/notifications/alerts_preferences_private.h b/src/fw/services/normal/notifications/alerts_preferences_private.h deleted file mode 100644 index 73380573d2..0000000000 --- a/src/fw/services/normal/notifications/alerts_preferences_private.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/notifications/notification_types.h" -#include "services/normal/vibes/vibe_intensity.h" -#include "util/time/time.h" - -#if CAPABILITY_HAS_VIBE_SCORES -#include "services/normal/vibes/vibe_client.h" -#include "services/normal/vibes/vibe_score_info.h" -#endif - -#define NOTIF_WINDOW_TIMEOUT_INFINITE ((uint32_t)~0) -#define NOTIF_WINDOW_TIMEOUT_DEFAULT (3 * MS_PER_MINUTE) - -void alerts_preferences_init(void); - -AlertMask alerts_preferences_get_alert_mask(void); - -void alerts_preferences_set_alert_mask(AlertMask mask); - -AlertMask alerts_preferences_dnd_get_mask(void); - -void alerts_preferences_dnd_set_mask(AlertMask mask); - -uint32_t alerts_preferences_get_notification_window_timeout_ms(void); - -void alerts_preferences_set_notification_window_timeout_ms(uint32_t timeout_ms); - -bool alerts_preferences_get_notification_alternative_design(void); - -void alerts_preferences_set_notification_alternative_design(bool alternative); - -bool alerts_preferences_get_vibrate(void); - -void alerts_preferences_set_vibrate(bool enable); - -VibeIntensity alerts_preferences_get_vibe_intensity(void); - -void alerts_preferences_set_vibe_intensity(VibeIntensity intensity); - -#if CAPABILITY_HAS_VIBE_SCORES -VibeScoreId alerts_preferences_get_vibe_score_for_client(VibeClient client); - -void alerts_preferences_set_vibe_score_for_client(VibeClient client, VibeScoreId id); -#endif - -bool alerts_preferences_dnd_is_manually_enabled(void); - -void alerts_preferences_dnd_set_manually_enabled(bool enable); - -void alerts_preferences_dnd_get_schedule(DoNotDisturbScheduleType type, - DoNotDisturbSchedule *schedule_out); - -void alerts_preferences_dnd_set_schedule(DoNotDisturbScheduleType type, - const DoNotDisturbSchedule *schedule); - -bool alerts_preferences_dnd_is_schedule_enabled(DoNotDisturbScheduleType type); - -void alerts_preferences_dnd_set_schedule_enabled(DoNotDisturbScheduleType type, bool enable); - -bool alerts_preferences_dnd_is_smart_enabled(void); - -void alerts_preferences_dnd_set_smart_enabled(bool enable); - diff --git a/src/fw/services/normal/notifications/alerts_private.h b/src/fw/services/normal/notifications/alerts_private.h deleted file mode 100644 index 81263ee68f..0000000000 --- a/src/fw/services/normal/notifications/alerts_private.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/notifications/alerts.h" - -typedef enum AlertMask { - AlertMaskAllOff = 0, - AlertMaskPhoneCalls = NotificationPhoneCall, - AlertMaskOther = NotificationOther, - AlertMaskAllOnLegacy = - NotificationMobile | NotificationPhoneCall | NotificationOther, - AlertMaskAllOn = - NotificationMobile | NotificationPhoneCall | NotificationOther | NotificationReminder -} AlertMask; - -bool alerts_get_vibrate(void); - -AlertMask alerts_get_mask(void); - -AlertMask alerts_get_dnd_mask(void); - -uint32_t alerts_get_notification_window_timeout_ms(void); - -void alerts_set_vibrate(bool enable); - -void alerts_set_mask(AlertMask mask); - -void alerts_set_dnd_mask(AlertMask mask); - -void alerts_set_notification_window_timeout_ms(uint32_t timeout_ms); - -void alerts_init(void); diff --git a/src/fw/services/normal/notifications/ancs/ancs_filtering.c b/src/fw/services/normal/notifications/ancs/ancs_filtering.c deleted file mode 100644 index a28fad39a5..0000000000 --- a/src/fw/services/normal/notifications/ancs/ancs_filtering.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ancs_filtering.h" - -#include "drivers/rtc.h" -#include "kernel/pbl_malloc.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/timeline/attributes_actions.h" -#include "system/logging.h" -#include "util/pstring.h" - -void ancs_filtering_record_app(iOSNotifPrefs **notif_prefs, - const ANCSAttribute *app_id, - const ANCSAttribute *display_name, - const ANCSAttribute *title) { - // When we receive a notification, information about the app that sent us the notification - // is recorded in the notif_pref_db. We sync this DB with the phone which allows us to - // do things like add non ANCS actions, or filter notifications by app - - // The "default" attributes are merged with any existing attributes. This makes it easy to add - // new attributes in the future as well as support EMail / SMS apps which already have data - // stored. - - iOSNotifPrefs *app_notif_prefs = *notif_prefs; - const int num_existing_attribtues = app_notif_prefs ? app_notif_prefs->attr_list.num_attributes : - 0; - - AttributeList new_attr_list; - attribute_list_init_list(num_existing_attribtues, &new_attr_list); - bool list_dirty = false; - - // Copy over all the existing attributes to our new list - if (app_notif_prefs) { - for (int i = 0; i < num_existing_attribtues; i++) { - new_attr_list.attributes[i] = app_notif_prefs->attr_list.attributes[i]; - } - } - - // The app name should be the display name - // If there is no display name (Apple Pay) then fallback to the title - const ANCSAttribute *app_name_attr = NULL; - if (display_name && display_name->length > 0) { - app_name_attr = display_name; - } else if (title && title->length > 0) { - app_name_attr = title; - } - - char *app_name_buff = NULL; - if (app_name_attr) { - const char *existing_name = ""; - if (app_notif_prefs) { - existing_name = attribute_get_string(&app_notif_prefs->attr_list, AttributeIdAppName, ""); - } - - if (!pstring_equal_cstring(&app_name_attr->pstr, existing_name)) { - // If the existing name doesn't match our new name, update the name - app_name_buff = kernel_zalloc_check(app_name_attr->length + 1); - pstring_pstring16_to_string(&app_name_attr->pstr, app_name_buff); - attribute_list_add_cstring(&new_attr_list, AttributeIdAppName, app_name_buff); - list_dirty = true; - PBL_LOG(LOG_LEVEL_INFO, "Adding app name to app prefs: <%s>", app_name_buff); - } - } - - // Add the mute attribute if we don't have one already - // Default the app to not muted - const bool already_has_mute = - app_notif_prefs && attribute_find(&app_notif_prefs->attr_list, AttributeIdMuteDayOfWeek); - if (!already_has_mute) { - attribute_list_add_uint8(&new_attr_list, AttributeIdMuteDayOfWeek, MuteBitfield_None); - list_dirty = true; - } - - // Add / update the "last seen" timestamp - Attribute *last_updated = NULL; - if (app_notif_prefs) { - last_updated = attribute_find(&app_notif_prefs->attr_list, AttributeIdLastUpdated); - } - uint32_t now = rtc_get_time(); - // Only perform an update if there is no timestamp or the current timestamp is more than a day old - if (!last_updated || - (last_updated && now > (last_updated->uint32 + SECONDS_PER_DAY))) { - attribute_list_add_uint32(&new_attr_list, AttributeIdLastUpdated, now); - list_dirty = true; - PBL_LOG(LOG_LEVEL_INFO, "Updating / adding timestamp to app prefs"); - } - - if (list_dirty) { - // We don't change or add actions at this time - TimelineItemActionGroup *new_action_group = NULL; - if (app_notif_prefs) { - new_action_group = &app_notif_prefs->action_group; - } - - ios_notif_pref_db_store_prefs(app_id->value, app_id->length, - &new_attr_list, new_action_group); - - // Update our copy of the prefs with the new data - const size_t buf_size = attributes_actions_get_buffer_size(&new_attr_list, new_action_group); - *notif_prefs = kernel_zalloc_check(sizeof(iOSNotifPrefs) + buf_size); - uint8_t *buffer = (uint8_t*)*notif_prefs + sizeof(iOSNotifPrefs); - - attributes_actions_deep_copy(&new_attr_list, &(*notif_prefs)->attr_list, new_action_group, - &(*notif_prefs)->action_group, buffer, buffer + buf_size); - ios_notif_pref_db_free_prefs(app_notif_prefs); - } - - - kernel_free(app_name_buff); - attribute_list_destroy_list(&new_attr_list); -} - -uint8_t ancs_filtering_get_mute_type(const iOSNotifPrefs *app_notif_prefs) { - if (app_notif_prefs) { - return attribute_get_uint8(&app_notif_prefs->attr_list, - AttributeIdMuteDayOfWeek, - MuteBitfield_None); - } - - return MuteBitfield_None; -} - -bool ancs_filtering_is_muted(const iOSNotifPrefs *app_notif_prefs) { - uint8_t mute_type = ancs_filtering_get_mute_type(app_notif_prefs); - - struct tm now_tm; - time_t now = rtc_get_time(); - localtime_r(&now, &now_tm); - - return mute_type & (1 << now_tm.tm_wday); -} diff --git a/src/fw/services/normal/notifications/ancs/ancs_filtering.h b/src/fw/services/normal/notifications/ancs/ancs_filtering.h deleted file mode 100644 index 8915bf7579..0000000000 --- a/src/fw/services/normal/notifications/ancs/ancs_filtering.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" - -//! Updates the entry in the notif pref db for a given app -//! @param app_notif_prefs The existing prefs for the app we want to update. These prefs could be -//! updated in the process -//! @param app_id ID of the app we're recording -//! @param display_name Display name of the app we're recording -//! @param title Title attribute from the notification associated with the app we're recording -//! (fallback in the event that we don't have a display name such as in the case of Apple Pay) -void ancs_filtering_record_app(iOSNotifPrefs **app_notif_prefs, - const ANCSAttribute *app_id, - const ANCSAttribute *display_name, - const ANCSAttribute *title); - -//! Returns true if a given app is muted for the current day -//! @param app_notif_prefs Prefs for the given app loaded from the notif pref db -//! @return true if the given app is muted -bool ancs_filtering_is_muted(const iOSNotifPrefs *app_notif_prefs); - -//! Returns the mute type for an app -//! @param app_notif_prefs Prefs for the given app loaded from the notif pref db -//! @return MuteBitfield which is the mute type of the app -uint8_t ancs_filtering_get_mute_type(const iOSNotifPrefs *app_notif_prefs); diff --git a/src/fw/services/normal/notifications/ancs/ancs_item.h b/src/fw/services/normal/notifications/ancs/ancs_item.h deleted file mode 100644 index 95b1eef6ae..0000000000 --- a/src/fw/services/normal/notifications/ancs/ancs_item.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "ancs_notifications_util.h" - -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/timeline/timeline.h" - -//! Creates a new timeline item from ANCS data -//! @param notif_attributes ANCS Notification attributes -//! @param app_attributes ANCS App attributes (namely, the display name) -//! @param app_metadata The icon and color associated with the app -//! @param notif_prefs iOS notification prefs for this notification -//! @param timestamp Time the notification occured -//! @param properties Additional ANCS properties (category, flags, etc) -//! @return The newly created timeline item -TimelineItem *ancs_item_create_and_populate(ANCSAttribute *notif_attributes[], - ANCSAttribute *app_attributes[], - const ANCSAppMetadata *app_metadata, - iOSNotifPrefs *notif_prefs, - time_t timestamp, - ANCSProperty properties); - -//! Replaces the dismiss action of an existing timeline item with the ancs negative action -//! @param item The timeline item to update -//! @param uid The uid of the ANCS notification we're using the dismiss action from -//! @param attr_action_neg The negative action from the ANCS notification to use as the new -//! dismiss action -void ancs_item_update_dismiss_action(TimelineItem *item, uint32_t uid, - const ANCSAttribute *attr_action_neg); diff --git a/src/fw/services/normal/notifications/ancs/ancs_known_apps.h b/src/fw/services/normal/notifications/ancs/ancs_known_apps.h deleted file mode 100644 index b19ea6a6d5..0000000000 --- a/src/fw/services/normal/notifications/ancs/ancs_known_apps.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// @nolint -// please don't change these values manually, they are derived from the spreadsheet -// "Notification Colors" - -#if PLATFORM_TINTIN -// Tintin does not have the color arg in its App Metadata. Remove it. -#define APP(id, icon, color) { id, icon } -#else -#define APP(id, icon, color) { id, icon, color } -#endif - - APP(IOS_CALENDAR_APP_ID, TIMELINE_RESOURCE_TIMELINE_CALENDAR, GColorRedARGB8), - APP(IOS_FACETIME_APP_ID, TIMELINE_RESOURCE_NOTIFICATION_FACETIME, GColorIslamicGreenARGB8), - APP(IOS_MAIL_APP_ID, TIMELINE_RESOURCE_GENERIC_EMAIL, GColorVividCeruleanARGB8), - APP(IOS_PHONE_APP_ID, TIMELINE_RESOURCE_TIMELINE_MISSED_CALL, GColorPictonBlueARGB8), - APP(IOS_REMINDERS_APP_ID, TIMELINE_RESOURCE_NOTIFICATION_REMINDER, GColorFollyARGB8), - APP(IOS_SMS_APP_ID, TIMELINE_RESOURCE_GENERIC_SMS, GColorIslamicGreenARGB8), - APP("com.atebits.Tweetie2", TIMELINE_RESOURCE_NOTIFICATION_TWITTER, GColorVividCeruleanARGB8), - APP("com.burbn.instagram", TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM, GColorCobaltBlueARGB8), - APP("com.facebook.Facebook", TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK, GColorCobaltBlueARGB8), - APP("com.facebook.Messenger", TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER, GColorBlueMoonARGB8), - APP("com.getpebble.pebbletime", TIMELINE_RESOURCE_NOTIFICATION_FLAG, GColorOrangeARGB8), - APP("com.google.calendar", TIMELINE_RESOURCE_TIMELINE_CALENDAR, GColorVeryLightBlueARGB8), - APP("com.google.Gmail", TIMELINE_RESOURCE_NOTIFICATION_GMAIL, GColorRedARGB8), - APP("com.google.hangouts", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS, GColorJaegerGreenARGB8), - APP("com.google.inbox", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX, GColorBlueMoonARGB8), - APP("com.microsoft.Office.Outlook", TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK, GColorCobaltBlueARGB8), - APP("com.orchestra.v2", TIMELINE_RESOURCE_NOTIFICATION_MAILBOX, GColorVividCeruleanARGB8), - APP("com.skype.skype", TIMELINE_RESOURCE_NOTIFICATION_SKYPE, GColorVividCeruleanARGB8), - APP("com.tapbots.Tweetbot3", TIMELINE_RESOURCE_NOTIFICATION_TWITTER, GColorVividCeruleanARGB8), - APP("com.toyopagroup.picaboo", TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT, GColorIcterineARGB8), - APP("com.yahoo.Aerogram", TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL, GColorIndigoARGB8), - APP("jp.naver.line", TIMELINE_RESOURCE_NOTIFICATION_LINE, GColorIslamicGreenARGB8), - APP("net.whatsapp.WhatsApp", TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP, GColorIslamicGreenARGB8), - APP("ph.telegra.Telegraph", TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM, GColorVividCeruleanARGB8), -#if !PLATFORM_TINTIN - APP("com.blackberry.bbm1", TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER, GColorDarkGrayARGB8), - APP("com.getpebble.pebbletime.enterprise", TIMELINE_RESOURCE_NOTIFICATION_FLAG, GColorOrangeARGB8), - APP("com.google.GoogleMobile", TIMELINE_RESOURCE_NOTIFICATION_GENERIC, GColorBlueMoonARGB8), - APP("com.google.ios.youtube", TIMELINE_RESOURCE_NOTIFICATION_GENERIC, GColorClearARGB8), - APP("com.hipchat.ios", TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT, GColorCobaltBlueARGB8), - APP("com.iwilab.KakaoTalk", TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK, GColorYellowARGB8), - APP("com.kik.chat", TIMELINE_RESOURCE_NOTIFICATION_KIK, GColorIslamicGreenARGB8), - APP("com.tencent.xin", TIMELINE_RESOURCE_NOTIFICATION_WECHAT, GColorKellyGreenARGB8), - APP("com.viber", TIMELINE_RESOURCE_NOTIFICATION_VIBER, GColorVividVioletARGB8), - APP("com.amazon.Amazon", TIMELINE_RESOURCE_NOTIFICATION_AMAZON, GColorChromeYellowARGB8), - APP("com.google.Maps", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS, GColorBlueMoonARGB8), - APP("com.google.photos", TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS, GColorBlueMoonARGB8), - APP("com.apple.mobileslideshow", TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS, GColorBlueMoonARGB8), - APP("com.linkedin.LinkedIn", TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN, GColorCobaltBlueARGB8), - APP("com.tinyspeck.chatlyio", TIMELINE_RESOURCE_NOTIFICATION_SLACK, GColorFollyARGB8), -#endif - -#undef APP diff --git a/src/fw/services/normal/notifications/ancs/ancs_notifications.h b/src/fw/services/normal/notifications/ancs/ancs_notifications.h deleted file mode 100644 index 8b3b031505..0000000000 --- a/src/fw/services/normal/notifications/ancs/ancs_notifications.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "services/normal/timeline/item.h" - - -void ancs_notifications_handle_message(uint32_t uid, - ANCSProperty properties, - ANCSAttribute **notif_attributes, - ANCSAttribute **app_attributes); - -void ancs_notifications_handle_notification_removed(uint32_t ancs_uid, ANCSProperty properties); diff --git a/src/fw/services/normal/notifications/ancs/ancs_notifications_util.h b/src/fw/services/normal/notifications/ancs/ancs_notifications_util.h deleted file mode 100644 index 92d9be9036..0000000000 --- a/src/fw/services/normal/notifications/ancs/ancs_notifications_util.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "util/attributes.h" -#include "util/time/time.h" - -#define IOS_PHONE_APP_ID "com.apple.mobilephone" -#define IOS_CALENDAR_APP_ID "com.apple.mobilecal" -#define IOS_REMINDERS_APP_ID "com.apple.reminders" -#define IOS_MAIL_APP_ID "com.apple.mobilemail" -#define IOS_SMS_APP_ID "com.apple.MobileSMS" -#define IOS_FACETIME_APP_ID "com.apple.facetime" - -typedef struct PACKED ANCSAppMetadata { - const char *app_id; - uint32_t icon_id; -#if PBL_COLOR - uint8_t app_color; -#endif - bool is_blocked:1; //value, NEXMO_REAUTH_STRING)) { - PBL_LOG(LOG_LEVEL_INFO, "Got Nexmo Reauth SMS"); - return true; - } - } - return false; -} - -void nexmo_handle_reauth_sms(uint32_t uid, - const ANCSAttribute *app_id, - const ANCSAttribute *message, - iOSNotifPrefs *existing_notif_prefs) { - const int num_existing_attributes = existing_notif_prefs ? - existing_notif_prefs->attr_list.num_attributes : 0; - - AttributeList new_attr_list; - attribute_list_init_list(num_existing_attributes, &new_attr_list); - - // Copy over all the existing attributes to our new list - if (existing_notif_prefs) { - for (int i = 0; i < num_existing_attributes; i++) { - new_attr_list.attributes[i] = existing_notif_prefs->attr_list.attributes[i]; - } - } - - char msg_buffer[message->length + 1]; - memcpy(msg_buffer, message->value, message->length); - msg_buffer[message->length] = '\0'; - attribute_list_add_cstring(&new_attr_list, AttributeIdAuthCode, msg_buffer); - - // This will trigger a sync sending the auth code to the phone - ios_notif_pref_db_store_prefs(app_id->value, app_id->length, - &new_attr_list, &existing_notif_prefs->action_group); - - // Dismiss the notification so the user is oblivious to this process - ancs_perform_action(uid, ActionIDNegative); -} diff --git a/src/fw/services/normal/notifications/ancs/nexmo.h b/src/fw/services/normal/notifications/ancs/nexmo.h deleted file mode 100644 index cfed9db9b4..0000000000 --- a/src/fw/services/normal/notifications/ancs/nexmo.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" - -bool nexmo_is_reauth_sms(const ANCSAttribute *app_id, const ANCSAttribute *message); - -//! Adds the reauth msg to the notif prefs so the phone can start the reauth process -void nexmo_handle_reauth_sms(uint32_t uid, - const ANCSAttribute *app_id, - const ANCSAttribute *message, - iOSNotifPrefs *existing_notif_prefs); diff --git a/src/fw/services/normal/notifications/do_not_disturb_toggle.c b/src/fw/services/normal/notifications/do_not_disturb_toggle.c deleted file mode 100644 index 24e46377b3..0000000000 --- a/src/fw/services/normal/notifications/do_not_disturb_toggle.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "do_not_disturb.h" -#include "do_not_disturb_toggle.h" - -#include "applib/app_exit_reason.h" -#include "applib/ui/action_toggle.h" -#include "services/common/i18n/i18n.h" -#include "system/logging.h" - -static bool prv_get_state(void *context) { - // This toggle does not necessarily toggle Manual DND. It sets Manual DND to the opposite of DND - // active status which in turn overrides Smart and Scheduled DND. - return do_not_disturb_is_active(); -} - -static void prv_set_state(bool enabled, void *context) { - PBL_LOG(LOG_LEVEL_DEBUG, "Manual DND toggle: %s", enabled ? "enabled" : "disabled"); - do_not_disturb_set_manually_enabled(enabled); -} - -static const ActionToggleImpl s_dnd_action_toggle_impl = { - .window_name = "DNDManualToggle", - .prompt_icon = PBL_IF_RECT_ELSE(RESOURCE_ID_QUIET_TIME_MOUSE, - RESOURCE_ID_QUIET_TIME_MOUSE_RIGHT_ALIGNED), - .result_icon = RESOURCE_ID_QUIET_TIME_MOUSE, - .prompt_enable_message = i18n_noop("Start Quiet Time?"), - .prompt_disable_message = i18n_noop("End Quiet Time?"), - .result_enable_message = i18n_noop("Quiet Time\nStarted"), - .result_disable_message = i18n_noop("Quiet Time\nEnded"), - .callbacks = { - .get_state = prv_get_state, - .set_state = prv_set_state, - }, -}; - -void do_not_disturb_toggle_push(ActionTogglePrompt prompt, bool set_exit_reason) { - action_toggle_push(&(ActionToggleConfig) { - .impl = &s_dnd_action_toggle_impl, - .prompt = prompt, - .set_exit_reason = set_exit_reason, - }); -} diff --git a/src/fw/services/normal/notifications/do_not_disturb_toggle.h b/src/fw/services/normal/notifications/do_not_disturb_toggle.h deleted file mode 100644 index 764a81f26d..0000000000 --- a/src/fw/services/normal/notifications/do_not_disturb_toggle.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/ui/action_toggle.h" - -void do_not_disturb_toggle_push(ActionTogglePrompt prompt, bool set_exit_reason); diff --git a/src/fw/services/normal/notifications/notification_constants.h b/src/fw/services/normal/notifications/notification_constants.h deleted file mode 100644 index 73a9371f92..0000000000 --- a/src/fw/services/normal/notifications/notification_constants.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" - -#define SMS_REPLY_COLOR GColorIslamicGreen - -// Notif pref db key for send text -#define SEND_TEXT_NOTIF_PREF_KEY "com.pebble.sendText" - -// Notif pref db keys for incoming call reply -#define ANDROID_PHONE_KEY "com.pebble.android.phone" -#define IOS_PHONE_KEY "com.apple.mobilephone" diff --git a/src/fw/services/normal/notifications/notification_storage_private.h b/src/fw/services/normal/notifications/notification_storage_private.h deleted file mode 100644 index 567e767253..0000000000 --- a/src/fw/services/normal/notifications/notification_storage_private.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Notification storage file size -#define NOTIFICATION_STORAGE_FILE_SIZE (30 * 1024) - -//! Minimum increment of space to free up when compressing. -//! The higher the value, the less often we need to compress, -//! but we will lose more notifications -#define NOTIFICATION_STORAGE_MINIMUM_INCREMENT_SIZE (NOTIFICATION_STORAGE_FILE_SIZE / 4) - diff --git a/src/fw/services/normal/notifications/notification_types.h b/src/fw/services/normal/notifications/notification_types.h deleted file mode 100644 index 31e9a52eb5..0000000000 --- a/src/fw/services/normal/notifications/notification_types.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/uuid.h" - -//! This list is shared by notifications and reminders. -typedef enum { - NotificationInvalid = 0, - NotificationMobile = (1 << 0), - NotificationPhoneCall = (1 << 1), - NotificationOther = (1 << 2), - NotificationReminder = (1 << 3) -} NotificationType; - -//! Type and Id for the notification or reminder. -typedef struct { - NotificationType type; - Uuid id; -} NotificationInfo; diff --git a/src/fw/services/normal/notifications/notifications.c b/src/fw/services/normal/notifications/notifications.c deleted file mode 100644 index e603e176d4..0000000000 --- a/src/fw/services/normal/notifications/notifications.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "notifications.h" - -#include "notification_storage.h" -#include "do_not_disturb.h" - -#include "applib/ui/vibes.h" -#include "drivers/rtc.h" -#include "drivers/battery.h" -#include "resource/resource_ids.auto.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/bitset.h" - -#include "kernel/events.h" -#include "kernel/low_power.h" -#include "kernel/pbl_malloc.h" - -#include "services/common/analytics/analytics.h" -#include "services/common/evented_timer.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/phone_call.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/vibes/vibe_intensity.h" - -#include - -static void prv_notification_migration_iterator_callback(TimelineItem *notification, - SerializedTimelineItemHeader *header, void *data) { - header->common.timestamp -= *((int*)data); - notification->header.timestamp = header->common.timestamp; -} - -void notifications_handle_notification_action_result( - PebbleSysNotificationActionResult *action_result) { - PebbleEvent launcher_event = { - .type = PEBBLE_SYS_NOTIFICATION_EVENT, - .sys_notification = { - .type = NotificationActionResult, - .action_result = action_result, - } - }; - // event loop will free memory of action_result - event_put(&launcher_event); -} - -void notifications_handle_notification_removed(Uuid *notification_id) { - Uuid *removed_id = kernel_malloc_check(sizeof(Uuid)); - *removed_id = *notification_id; - PebbleEvent launcher_event = { - .type = PEBBLE_SYS_NOTIFICATION_EVENT, - .sys_notification = { - .type = NotificationRemoved, - .notification_id = removed_id, - } - }; - event_put(&launcher_event); -} - -void notifications_handle_notification_added(Uuid *notification_id) { - PebbleEvent launcher_event = { - .type = PEBBLE_SYS_NOTIFICATION_EVENT, - .sys_notification = { - .type = NotificationAdded, - .notification_id = notification_id - } - }; - event_put(&launcher_event); - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_COUNT, AnalyticsClient_System); -} - -void notifications_handle_notification_acted_upon(Uuid *notification_id) { - PebbleEvent launcher_event = { - .type = PEBBLE_SYS_NOTIFICATION_EVENT, - .sys_notification = { - .type = NotificationActedUpon, - .notification_id = notification_id - } - }; - event_put(&launcher_event); -} - -void notifications_migrate_timezone(const int tz_diff) { - notification_storage_rewrite(prv_notification_migration_iterator_callback, (void*)&tz_diff); -} - -void notification_storage_init(void); -void vibe_intensity_init(void); - -void notifications_init(void) { - notification_storage_init(); -} - -void notifications_add_notification(TimelineItem *notification) { - notification_storage_store(notification); - - Uuid *uuid = kernel_malloc_check(sizeof(Uuid)); - *uuid = notification->header.id; - notifications_handle_notification_added(uuid); -} diff --git a/src/fw/services/normal/notifications/notifications.h b/src/fw/services/normal/notifications/notifications.h deleted file mode 100644 index 9f03eb3e78..0000000000 --- a/src/fw/services/normal/notifications/notifications.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/timeline/item.h" - -#include -#include - -typedef enum { - ActionResultTypeSuccess, - ActionResultTypeFailure, - ActionResultTypeChaining, - ActionResultTypeDoResponse, - ActionResultTypeSuccessANCSDismiss, -} ActionResultType; - -typedef struct { - Uuid id; - ActionResultType type; - AttributeList attr_list; - TimelineItemActionGroup action_group; -} PebbleSysNotificationActionResult; - - -void notifications_init(void); - -//! Feedback for the result of an invoke action command -void notifications_handle_notification_action_result( - PebbleSysNotificationActionResult *action_result); - -//! Add a notification. -void notifications_handle_notification_added(Uuid *notification_id); - -//! Handle a notification getting acted upon on the phone -void notifications_handle_notification_acted_upon(Uuid *notification_id); - -//! Remove a notification -void notifications_handle_notification_removed(Uuid *notification_id); - -//! Notify of remove command from ANCS. Notification will be kept in history -void notifications_handle_ancs_notification_removed(uint32_t ancs_uid); - -//! Migration hook for notifications -//! Called with the GMT offset of the new timezone -void notifications_migrate_timezone(const int new_tz_offset); - -//! Inserts a new notification into notification storage and notifies the system of the new item -//! @param notification Pointer to the notification to add -void notifications_add_notification(TimelineItem *notification); diff --git a/src/fw/services/normal/persist.c b/src/fw/services/normal/persist.c deleted file mode 100644 index 23cac611f3..0000000000 --- a/src/fw/services/normal/persist.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "persist.h" - -#include -#include -#include - -#include "kernel/pbl_malloc.h" -#include "os/mutex.h" -#include "process_management/app_install_manager.h" -#include "services/normal/filesystem/app_file.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/legacy/persist_map.h" -#include "services/normal/settings/settings_file.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/list.h" -#include "util/units.h" - -#define PERSIST_STORAGE_MAX_SPACE KiBYTES(6) - -typedef struct PersistStore { - ListNode list_node; - Uuid uuid; - SettingsFile file; - bool file_open; - uint8_t usage_count; //!< How many clients are using this store -} PersistStore; - -// Each open client has a PersistStore structure linked into this list. If both -// a worker and foreground app of the same UUID are running, then they share the -// same store. -static ListNode *s_client_stores; -static PebbleMutex *s_mutex; - - -static bool prv_uuid_list_filter(ListNode* node, void* data) { - const Uuid *uuid = data; - PersistStore* store = (PersistStore*)node; - return uuid_equal(&store->uuid, uuid); -} - -static PersistStore * prv_find_open_store(const Uuid *uuid) { - return (PersistStore *)list_find(s_client_stores, prv_uuid_list_filter, - (void *)uuid); -} - -static ALWAYS_INLINE void prv_lock(void) { - mutex_lock_with_lr(s_mutex, (uint32_t)__builtin_return_address(0)); -} - -static inline void prv_unlock(void) { - mutex_unlock(s_mutex); -} - -#define PERSIST_FILE_NAME_MAX_LENGTH sizeof("ps000001") - -static status_t prv_get_file_name(char *name, size_t buf_len, const Uuid *uuid) { - // Firmware 2.x persist files are named "p%06d", the added "s" in the file - // name prefix indicates that it is in SettingsFile format. - int pid = persist_map_auto_id(uuid); - if (FAILED(pid)) { - // Attempting to debug persist map failure - PBL_LOG(LOG_LEVEL_WARNING, "Failed to get pid! %d", pid); - persist_map_dump(); - return pid; - } - return snprintf(name, buf_len, "ps%06d", pid); -} - -status_t persist_service_delete_file(const Uuid *uuid) { - char name[PERSIST_FILE_NAME_MAX_LENGTH]; - - status_t status = prv_get_file_name(name, sizeof(name), uuid); - if (FAILED(status)) { - return status; - } - return pfs_remove(name); -} - -static bool prv_bad_persist_file_filter(const char *filename) { - return is_app_file_name(filename) && - strcmp(filename + APP_FILE_NAME_PREFIX_LENGTH, "persist") == 0; -} - -// Designed to be called once during reset -void persist_service_init(void) { - persist_map_init(); - s_mutex = mutex_create(); - - // Find and delete any AppInstallId-indexed persist files. Due to PBL-16663 - // (affecting FW 3.0-dp5 thru -dp7), the AppInstallId in the file name may not - // correspond to the app that the persist file originally belonged to. Since - // we can't be sure that the persist files correspond to the current - // AppInstallId, the safest thing to do is to simply blow them away. - // TODO: remove this code before FW 3.0-golden. - PFSFileListEntry *bad_file_list = pfs_create_file_list( - prv_bad_persist_file_filter); - PFSFileListEntry *iter = bad_file_list; - while (iter) { - pfs_remove(iter->name); - iter = (PFSFileListEntry *)iter->list_node.next; - } - pfs_delete_file_list(bad_file_list); -} - -// Return a pointer to the store for the given UUID. Each task that uses persist -// must call persist_service_client_open() to create/open the store during its -// startup and persist_service_client_close() during its shutdown. -// -// The SettingsFile is opened/created lazily. A persist file will not be -// created for an app unless it calls a persist function. -// -// The persist service mutex is locked when this function is called. It will -// only be unlocked after a call to persist_service_unlock(). While the global -// persist service mutex is currently used, the API is designed such that a -// per-file mutex could be used without altering the callers. -SettingsFile * persist_service_lock_and_get_store(const Uuid *uuid) { - prv_lock(); - PersistStore *store = prv_find_open_store(uuid); - PBL_ASSERTN(store); - if (!store->file_open) { - char filename[PERSIST_FILE_NAME_MAX_LENGTH]; - PBL_ASSERTN(PASSED(prv_get_file_name(filename, sizeof(filename), uuid))); - PBL_ASSERTN(PASSED(settings_file_open(&store->file, filename, PERSIST_STORAGE_MAX_SPACE))); - store->file_open = true; - } - return &store->file; -} - -void persist_service_unlock_store(SettingsFile *store) { - prv_unlock(); -} - -// Create a store for a client of the given UUID it doesn't already exist. If it -// exists already (another client with the same UUID is running), then just -// increment its usage count. This is called by the process startup code -// (app_state_init() or worker_state_init()). -void persist_service_client_open(const Uuid *uuid) { - prv_lock(); - { - PersistStore *store = prv_find_open_store(uuid); - if (store) { - store->usage_count++; - } else { - store = kernel_malloc_check(sizeof(*store)); - *store = (PersistStore) { - .uuid = *uuid, - .usage_count = 1, - .file_open = false, - }; - s_client_stores = list_insert_before(s_client_stores, &store->list_node); - } - } - prv_unlock(); -} - -// Release the store for the given UUID. Called by ProcessManager to clean up -// after a task exists. If there are no other processes using the same store, it -// will be freed -void persist_service_client_close(const Uuid *uuid) { - prv_lock(); - { - PersistStore *store = prv_find_open_store(uuid); - PBL_ASSERTN(store && - list_contains(s_client_stores, &store->list_node) && - store->usage_count >= 1); - - if (--store->usage_count == 0) { - if (store->file_open) { - settings_file_close(&store->file); - } - list_remove(&store->list_node, - &s_client_stores /* &head */, NULL /* &tail */); - kernel_free(store); - } - } - prv_unlock(); -} diff --git a/src/fw/services/normal/persist.h b/src/fw/services/normal/persist.h deleted file mode 100644 index 3bc09693c4..0000000000 --- a/src/fw/services/normal/persist.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Persist service -//! -//! The persist service manages persistent app key-value stores. A persistent -//! store is simply a SettingsFile identified by the app's UUID. The service -//! manages the creation, opening and deletion of persist stores so that an app -//! and its worker can both access the same file through a single file handle -//! and SettingsFile state object. -//! -//! The persist service makes no attempt to make SettingsFile reentrant; it is -//! the caller's responsibility to enforce mutual exclusion and prevent -//! concurrent access to the SettingsFile. - -#include -#include - -#include "util/uuid.h" -#include "process_management/app_install_types.h" -#include "system/status_codes.h" - -typedef struct SettingsFile SettingsFile; - - -//! Initialize the persist service. -void persist_service_init(void); - -//! Lock and get the persist store for the given app. -SettingsFile *persist_service_lock_and_get_store(const Uuid *uuid); - -//! Unlock the given persist store. -void persist_service_unlock_store(SettingsFile *store); - -//! Call during each process's startup. -void persist_service_client_open(const Uuid *uuid); - -//! Call once after proces exits to clean it up. -void persist_service_client_close(const Uuid *uuid); - -//! Deletes the app's persist file. -status_t persist_service_delete_file(const Uuid *uuid); diff --git a/src/fw/services/normal/phone_call.c b/src/fw/services/normal/phone_call.c deleted file mode 100644 index b5622f9a4b..0000000000 --- a/src/fw/services/normal/phone_call.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "phone_call.h" - -#include "applib/event_service_client.h" -#include "applib/ui/vibes.h" -#include "comm/ble/kernel_le_client/ancs/ancs.h" -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "kernel/pbl_malloc.h" -#include "popups/phone_ui.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session.h" -#include "services/common/phone_pp.h" -#include "services/common/system_task.h" -#include "services/normal/notifications/alerts.h" -#include "services/normal/notifications/ancs/ancs_phone_call.h" -#include "system/logging.h" - -//! This service is a little confusing, but generally here is how the phone calls work: -//! On Android: -//! - The watch gets PP messages (parsed in phone_pp.c), which come in as events happen. -//! - The watch can decline / hangup the call by sending PP messages to the phone. -//! On iOS: -//! - The watch gets incomming calls from ANCS (parsed in ancs_notifications.c). -//! - After that the watch must poll the phone for its status if not iOS 9+ (using PP messages). -//! - On iOS 9, ANCS tells us when the phone stops ringing -//! - The watch can pickup / decline a call using ANCS actions -//! - We don't show the ongoing call UI because we must continue to poll so that we know when the -//! call ends, which consumes a lot of battery especially for longer calls. On iOS 9, we only -//! know when the phone stops ringing, we don't know what happens after the user accepts/rejects - - -static bool s_call_in_progress = false; -static PhoneCallSource s_call_source; - -// When using Android this is the cookie, when using ANCS this is the NotificationUUID -static uint32_t s_call_identifier; - -// If the mobile app is closed we won't receive PP messages and thus might miss a call end event -// which puts us in a bad state until BT disconnects -static bool s_mobile_app_is_connected; - -// We can't expect iOS to reliably send us phone call events, so we must poll for the current -// status of the phone call -static TimerID s_call_watchdog = TIMER_INVALID_ID; - - -static void prv_handle_call_end(bool disconnected); - -static bool prv_call_is_ancs(void) { - return (s_call_source == PhoneCallSource_ANCS_Legacy) || (s_call_source == PhoneCallSource_ANCS); -} - -static void prv_poll_phone_for_status(void *context) { - pp_get_phone_state(); -} - -static void prv_timer_callback(void *context) { - // Make sure we aren't overflowing / backing up the queue too much - if (system_task_get_available_space() > 10) { - system_task_add_callback(prv_poll_phone_for_status, context); - } -} - -static void prv_schedule_call_watchdog(int poll_interval_ms) { - // The Android app currently crashes if it recieves the get_state event. It currently doesn't - // respond either so don't bother sending messages we don't need to. We also don't need to poll - // iOS 9 since we can rely on ANCS to tell us when the phone stops ringing - if (s_call_source == PhoneCallSource_ANCS_Legacy) { - // Schedule/reschedule the watchdog - if (!new_timer_start(s_call_watchdog, poll_interval_ms, prv_timer_callback, - NULL, TIMER_START_FLAG_REPEATING)) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not start the phone call watchdog timer"); - prv_handle_call_end(true /* Treat this as a disconnection */); - } else { - PBL_LOG(LOG_LEVEL_INFO, "Phone call watchdog timer started"); - pp_get_phone_state_set_enabled(true); - } - } else { - PBL_LOG(LOG_LEVEL_INFO, "Not starting phone call watchdog, this isn't iOS 8: %d", - s_call_source); - } -} - -static void prv_cancel_call_watchdog(void) { - new_timer_stop(s_call_watchdog); - pp_get_phone_state_set_enabled(false); -} - -static bool prv_can_answer(void) { - // We can't answer calls with Android - return prv_call_is_ancs(); -} - -static bool prv_should_show_ongoing_call_ui(void) { - // We only want to show the ongoing call UI on Android - return (s_call_source == PhoneCallSource_PP); -} - -// hangup != decline. Decline == reject incomming call, Hangup == stop in progress call -static bool prv_can_hangup(void) { - // We can't hangup with iOS - return !prv_call_is_ancs(); -} - -// Handles the common things when we hide an incoming call -static void prv_call_end_common(void) { - s_call_in_progress = false; - prv_cancel_call_watchdog(); - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_PHONE_CALL_TIME); -} - -static void prv_handle_incoming_call(const PebblePhoneEvent *event) { - // Only 1 call at a time is supported - if (s_call_in_progress) { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring incoming call. A call is already in progress"); - return; - } - - // If we're not on iOS9+, we need to be connected to the mobile app since it tells us when - // the phone has stopped ringing - if ((event->source != PhoneCallSource_ANCS) && !s_mobile_app_is_connected) { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring incoming call. Mobile app is not connected. Call source: %d ", - event->source); - return; - } - - s_call_in_progress = true; - s_call_source = event->source; - s_call_identifier = event->call_identifier; - - prv_schedule_call_watchdog(600); - - phone_ui_handle_incoming_call(event->caller, prv_can_answer(), prv_should_show_ongoing_call_ui(), - s_call_source); - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_INCOMING_COUNT, AnalyticsClient_System); - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_PHONE_CALL_TIME, AnalyticsClient_System); -} - -static void prv_handle_outgoing_call(PebblePhoneEvent *event) { - // Only 1 call at a time is supported - if (!s_call_in_progress && s_mobile_app_is_connected) { - phone_ui_handle_outgoing_call(event->caller); - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_OUTGOING_COUNT, AnalyticsClient_System); - } else { - // PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring outgoing call. A call is already in progress: %d, " - // "the mobile app is connected: %d", s_call_in_progress, s_mobile_app_is_connected); - } -} - -static void prv_handle_missed_call(PebblePhoneEvent *event) { - if (s_call_in_progress) { - prv_call_end_common(); - phone_ui_handle_missed_call(); - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_INCOMING_COUNT, AnalyticsClient_System); - } else { - // PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring missed call. A call is not in progress"); - } -} - -static void prv_handle_call_start(void) { - if (s_call_in_progress) { - if (prv_call_is_ancs()) { - // We don't show an ongoing call UI on iOS, so from this service's point of view the call is - // now complete. - prv_call_end_common(); - phone_ui_handle_call_end(true /*call accepted*/, false /*disconnected*/); - } else { - phone_ui_handle_call_start(prv_can_hangup()); - } - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_START_COUNT, AnalyticsClient_System); - } else { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring start call. A call is not in progress"); - } -} - -static void prv_handle_call_hide(PebblePhoneEvent *event) { - if (!s_call_in_progress) { - return; - } - - // Make sure this wasn't caused due to an unrelated ANCS removal - if (prv_call_is_ancs() && (s_call_identifier != event->call_identifier)) { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring hide call. Call identifier %"PRIu32" doesn't match %"PRIu32, - s_call_identifier, event->call_identifier); - return; - } - - prv_call_end_common(); - phone_ui_handle_call_hide(); - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_END_COUNT, AnalyticsClient_System); -} - -static void prv_handle_call_end(bool disconnected) { - if (!disconnected) { - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_END_COUNT, AnalyticsClient_System); - } - - if (s_call_in_progress) { - prv_call_end_common(); - phone_ui_handle_call_end(false /*call accepted*/, disconnected); - } else if (!disconnected) { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring end call. A call is not in progress"); - } -} - -static void prv_handle_caller_id(PebblePhoneEvent *event) { - if (s_call_in_progress) { - phone_ui_handle_caller_id(event->caller); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring caller id. A call is not in progress"); - } -} - -T_STATIC void prv_handle_phone_event(PebbleEvent *e, void *context) { - PebblePhoneEvent event = (PebblePhoneEvent) e->phone; - - if (!alerts_should_notify_for_type(AlertPhoneCall)) { - prv_handle_call_end(true /* disconnected */); - phone_call_util_destroy_caller(event.caller); - return; - } - - if (!(event.type == PhoneEventType_Incoming && new_timer_scheduled(s_call_watchdog, NULL))) { - // Be careful not to spam the logs with the new iOS polling implementation - PBL_LOG(LOG_LEVEL_INFO, "PebblePhoneEvent: %d, Call in progress: %s, Connected: %s", - event.type, s_call_in_progress ? "T": "F", s_mobile_app_is_connected ? "T": "F"); - } - - switch (event.type) { - case PhoneEventType_Incoming: - prv_handle_incoming_call(&event); - break; - case PhoneEventType_Outgoing: - prv_handle_outgoing_call(&event); - break; - case PhoneEventType_Missed: - prv_handle_missed_call(&event); - break; - case PhoneEventType_Ring: - // Just ignore these. We can ring on our own. - break; - case PhoneEventType_Start: - prv_handle_call_start(); - break; - case PhoneEventType_End: - prv_handle_call_end(false /* disconnected */); - break; - case PhoneEventType_CallerID: - prv_handle_caller_id(&event); - break; - case PhoneEventType_Disconnect: - prv_handle_call_end(true /* disconnected */); - break; - case PhoneEventType_Hide: - prv_handle_call_hide(&event); - break; - case PhoneEventType_Invalid: - break; - } - - phone_call_util_destroy_caller(event.caller); -} - -T_STATIC void prv_handle_mobile_app_event(PebbleEvent *e, void *context) { - if (!e->bluetooth.comm_session_event.is_system) { - return; - } - - s_mobile_app_is_connected = e->bluetooth.comm_session_event.is_open; - if (!s_mobile_app_is_connected && (s_call_source != PhoneCallSource_ANCS)) { - prv_handle_call_end(true /* disconnected */); - } -} - -T_STATIC void prv_handle_ancs_disconnected_event(PebbleEvent *e, void *context) { - if (s_call_source == PhoneCallSource_ANCS) { - prv_handle_call_end(true /* disconnected */); - } -} - -//! -//! Phone Call API -//! -void phone_call_service_init() { - static EventServiceInfo phone_event_info; - phone_event_info = (EventServiceInfo) { - .type = PEBBLE_PHONE_EVENT, - .handler = prv_handle_phone_event, - }; - event_service_client_subscribe(&phone_event_info); - - static EventServiceInfo mobile_app_event_info; - mobile_app_event_info = (EventServiceInfo) { - .type = PEBBLE_COMM_SESSION_EVENT, - .handler = prv_handle_mobile_app_event, - }; - event_service_client_subscribe(&mobile_app_event_info); - - static EventServiceInfo ancs_disconnected_event_info; - ancs_disconnected_event_info = (EventServiceInfo) { - .type = PEBBLE_ANCS_DISCONNECTED_EVENT, - .handler = prv_handle_ancs_disconnected_event, - }; - event_service_client_subscribe(&ancs_disconnected_event_info); - - s_mobile_app_is_connected = (comm_session_get_system_session() != NULL); - - s_call_watchdog = new_timer_create(); -} - -void phone_call_answer(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_ANSWER_COUNT, AnalyticsClient_System); - PBL_LOG(LOG_LEVEL_INFO, "Call accepted"); - - if (prv_call_is_ancs()) { - ancs_perform_action(s_call_identifier, ActionIDPositive); - - // We don't show an ongoing call UI on iOS, so from this service's point of view the call is - // now complete. - prv_call_end_common(); - } else { - pp_answer_call(s_call_identifier); - } -} - -void phone_call_decline(void) { - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_CALL_DECLINE_COUNT, AnalyticsClient_System); - PBL_LOG(LOG_LEVEL_INFO, "Call declined"); - - if (prv_call_is_ancs()) { - ancs_perform_action(s_call_identifier, ActionIDNegative); - ancs_phone_call_temporarily_block_missed_calls(); - prv_cancel_call_watchdog(); - } else { - pp_decline_call(s_call_identifier); - } - - if (s_call_in_progress) { - s_call_in_progress = false; - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_PHONE_CALL_TIME); - } -} diff --git a/src/fw/services/normal/phone_call.h b/src/fw/services/normal/phone_call.h deleted file mode 100644 index 2cdaeda44e..0000000000 --- a/src/fw/services/normal/phone_call.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "kernel/events.h" - -void phone_call_service_init(void); - -void phone_call_answer(void); - -void phone_call_decline(void); diff --git a/src/fw/services/normal/phone_call_util.c b/src/fw/services/normal/phone_call_util.c deleted file mode 100644 index 13e3d13a96..0000000000 --- a/src/fw/services/normal/phone_call_util.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "phone_call_util.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" - -#include - -PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name) { - PebblePhoneCaller *caller = kernel_zalloc(sizeof(PebblePhoneCaller)); - if (!caller) { - return NULL; - } - - if ((!name || !strlen(name)) && (!number || !strlen(number))) { - const char *i18n_name = i18n_get("Unknown", __FILE__); - - caller->name = kernel_malloc(strlen(i18n_name) + 1); - if (caller->name) { - strcpy(caller->name, i18n_name); - } - i18n_free(i18n_name, __FILE__); - - return caller; - } - - if (number) { - caller->number = kernel_malloc(strlen(number) + 1); - if (caller->number) { - strcpy(caller->number, number); - } - } - if (name) { - caller->name = kernel_malloc(strlen(name) + 1); - if (caller->name) { - strcpy(caller->name, name); - } - } - - return caller; -} - -void phone_call_util_destroy_caller(PebblePhoneCaller *caller) { - if (caller) { - kernel_free(caller->number); - kernel_free(caller->name); - kernel_free(caller); - } -} diff --git a/src/fw/services/normal/phone_call_util.h b/src/fw/services/normal/phone_call_util.h deleted file mode 100644 index f59d452fc1..0000000000 --- a/src/fw/services/normal/phone_call_util.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -typedef struct PebblePhoneCaller { - char *number; - char *name; -} PebblePhoneCaller; - -//! Creates a new caller to pass as part of a phone event -//! @param number The phone number for this caller -//! @param name The name of the caller -//! @return Pointer to new caller -PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name); - -//! Destroys a caller previously created with \ref phone_call_util_create_caller -//! @param caller The caller to free -void phone_call_util_destroy_caller(PebblePhoneCaller *caller); diff --git a/src/fw/services/normal/process_management/app_order_storage.h b/src/fw/services/normal/process_management/app_order_storage.h deleted file mode 100644 index 6d3b98ec01..0000000000 --- a/src/fw/services/normal/process_management/app_order_storage.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "process_management/app_install_manager.h" -#include "util/attributes.h" - -typedef struct PACKED AppMenuOrderStorage { - uint8_t list_length; - AppInstallId id_list[]; -} AppMenuOrderStorage; - -void app_order_storage_init(void); - -//! Returns an AppMenuOrderStorage struct on the kernel heap -AppMenuOrderStorage *app_order_read_order(void); - -//! Writes a list of UUID's to the order file -void write_uuid_list_to_file(const Uuid *uuid_list, uint8_t count); diff --git a/src/fw/services/normal/process_management/process_commands.c b/src/fw/services/normal/process_management/process_commands.c deleted file mode 100644 index d37bc96477..0000000000 --- a/src/fw/services/normal/process_management/process_commands.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "console/prompt.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/app_db.h" -#include "services/normal/filesystem/pfs.h" - -//! @file process_commands.c -//! -//! Serial commands for process management - -extern AppInstallId app_db_check_next_unique_id(void); - -void command_app_remove(const char *id_str) { - int32_t id = atoi(id_str); - if (id == 0) { - prompt_send_response("invalid app number"); - return; - } - - AppInstallEntry entry; - if (!app_install_get_entry_for_install_id(id, &entry)) { - prompt_send_response("failed to get entry"); - return; - } - - // should delete from blob db and fire off an event to AppInstallManager that does the rest - app_db_delete((uint8_t *)&entry.uuid, sizeof(Uuid)); - prompt_send_response("OK"); -} - -bool prv_print_app_info(AppInstallEntry *entry, void *data) { - if (app_install_id_from_system(entry->install_id)) { - return true; - } - - char buffer[120]; - - char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&entry->uuid, uuid_buffer); - - prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRIi32": %s %s", entry->install_id, - entry->name, uuid_buffer); - return true; -} - -void command_app_list(void) { - app_install_enumerate_entries(prv_print_app_info, NULL); -} - -void command_app_launch(const char *id_str) { - int32_t id = atoi(id_str); - if (id == 0) { - prompt_send_response("invalid app number"); - return; - } - - AppInstallEntry entry; - bool success = app_install_get_entry_for_install_id(id, &entry); - - if (success) { - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = id }); - prompt_send_response("OK"); - } else { - prompt_send_response("No app with id"); - } -} - -void command_worker_launch(const char *id_str) { - int32_t id = atoi(id_str); - if (id == 0) { - prompt_send_response("invalid app number"); - return; - } - - AppInstallEntry entry; - bool success = app_install_get_entry_for_install_id(id, &entry); - - if (success && app_install_entry_has_worker(&entry)) { - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = id }); - prompt_send_response("OK"); - } else { - prompt_send_response("No worker with id"); - } -} diff --git a/src/fw/services/normal/process_management/process_loader_flash.h b/src/fw/services/normal/process_management/process_loader_flash.h deleted file mode 100644 index 3fd28cb008..0000000000 --- a/src/fw/services/normal/process_management/process_loader_flash.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -bool process_loader_load_from_flash(PebbleTask task, const ProcessConfig *config, - const PebbleProcessMd *app_md) { -} diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.h b/src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.h deleted file mode 100644 index 0c72a6dad6..0000000000 --- a/src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "protobuf_log.h" - -#include "services/common/hrm/hrm_manager.h" -#include "services/normal/activity/activity.h" - -#include -#include - -ProtobufLogRef protobuf_log_activity_sessions_create(void); - -bool protobuf_log_activity_sessions_add(ProtobufLogRef ref, time_t sample_utc, - ActivitySession *session); - -bool protobuf_log_activity_sessions_decode(pebble_pipeline_Event *event_in, - ActivitySession *session_out); diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_hr.c b/src/fw/services/normal/protobuf_log/protobuf_log_hr.c deleted file mode 100644 index 1e8cd53963..0000000000 --- a/src/fw/services/normal/protobuf_log/protobuf_log_hr.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "protobuf_log_hr.h" -#include "protobuf_log.h" - -#include "services/common/hrm/hrm_manager.h" - -#include "nanopb/measurements.pb.h" -#include "system/passert.h" - -#include - -#include -#include - -// ----------------------------------------------------------------------------------------- -// Convert HRMQuality to the internal protobuf representation. -T_STATIC uint32_t prv_hr_quality_int(HRMQuality quality) { - switch (quality) { - case HRMQuality_NoAccel: - return pebble_pipeline_MeasurementSet_HeartRateQuality_NoAccel; - case HRMQuality_OffWrist: - return pebble_pipeline_MeasurementSet_HeartRateQuality_OffWrist; - case HRMQuality_NoSignal: - return pebble_pipeline_MeasurementSet_HeartRateQuality_NoSignal; - case HRMQuality_Worst: - return pebble_pipeline_MeasurementSet_HeartRateQuality_Worst; - case HRMQuality_Poor: - return pebble_pipeline_MeasurementSet_HeartRateQuality_Poor; - case HRMQuality_Acceptable: - return pebble_pipeline_MeasurementSet_HeartRateQuality_Acceptable; - case HRMQuality_Good: - return pebble_pipeline_MeasurementSet_HeartRateQuality_Good; - case HRMQuality_Excellent: - return pebble_pipeline_MeasurementSet_HeartRateQuality_Excellent; - } - WTF; // Should never get here - return 0; -} - -ProtobufLogRef protobuf_log_hr_create(ProtobufLogTransportCB transport) { - // Create a measure log session, which we use to send heart rate readings to the phone - ProtobufLogMeasurementType measure_types[] = { - ProtobufLogMeasurementType_BPM, - ProtobufLogMeasurementType_HRQuality, - }; - - ProtobufLogConfig log_config = { - .type = ProtobufLogType_Measurements, - .measurements = { - .types = measure_types, - .num_types = ARRAY_LENGTH(measure_types), - }, - }; - - return protobuf_log_create(&log_config, transport, 0 /*max_encoded_msg_size*/); -} - -bool protobuf_log_hr_add_sample(ProtobufLogRef ref, time_t sample_utc, uint8_t bpm, - HRMQuality quality) { - uint32_t values[] = {bpm, prv_hr_quality_int(quality)}; - return protobuf_log_session_add_measurements(ref, sample_utc, ARRAY_LENGTH(values), values); -} diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_hr.h b/src/fw/services/normal/protobuf_log/protobuf_log_hr.h deleted file mode 100644 index a139ec7412..0000000000 --- a/src/fw/services/normal/protobuf_log/protobuf_log_hr.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "protobuf_log.h" - -#include "services/common/hrm/hrm_manager.h" - -#include -#include -#include - -ProtobufLogRef protobuf_log_hr_create(ProtobufLogTransportCB transport); - -bool protobuf_log_hr_add_sample(ProtobufLogRef ref, time_t sample_utc, uint8_t bpm, - HRMQuality quality); diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_private.h b/src/fw/services/normal/protobuf_log/protobuf_log_private.h deleted file mode 100644 index ef837f6bfe..0000000000 --- a/src/fw/services/normal/protobuf_log/protobuf_log_private.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "pb.h" -#include "pb_decode.h" -#include "pb_encode.h" - -#include "protobuf_log.h" - -#include "util/attributes.h" - -#include - -// This fixed size header is placed at the beginning of the buffer, before the protobuf -// encoded message -typedef struct PACKED { - uint16_t msg_size; -} PLogMessageHdr; - -// Internal structure of a protobuf log session -typedef struct PLogSession { - // TODO Change comments from MeasurementSet to MeasurementSet/Events - - ProtobufLogConfig config; - uint8_t *msg_buffer; // allocated buffer for the final record: PLogMessageHdr + Payload - uint8_t *data_buffer; // allocated buffer for the encoded data blob. (e.g. MeasurementSet) - // We form the MeasurementSet first, and then after it's complete we - // copy it into msg_buffer inside of a Payload - size_t max_msg_size; // max # of bytes to use in the allocated buffer - size_t max_data_size; // max allowed size of the encoded data blob - pb_ostream_t data_stream; // output stream we are writing the data blob to - time_t start_utc; // UTC time when session was created - ProtobufLogTransportCB transport; -} PLogSession; diff --git a/src/fw/services/normal/send_text_service.c b/src/fw/services/normal/send_text_service.c deleted file mode 100644 index ca39a2be74..0000000000 --- a/src/fw/services/normal/send_text_service.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "send_text_service.h" - -#include "applib/event_service_client.h" -#include "kernel/events.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/notifications/notification_constants.h" - - -static bool s_has_send_text_reply_action = false; - -static bool prv_has_send_text_reply_action(void) { - iOSNotifPrefs *notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)SEND_TEXT_NOTIF_PREF_KEY, - strlen(SEND_TEXT_NOTIF_PREF_KEY)); - if (!notif_prefs) { - return false; - } - - bool has_reply_action = - (timeline_item_action_group_find_reply_action(¬if_prefs->action_group) != NULL); - - ios_notif_pref_db_free_prefs(notif_prefs); - return has_reply_action; -} - -static void prv_blobdb_event_handler(PebbleEvent *event, void *context) { - const PebbleBlobDBEvent *blobdb_event = &event->blob_db; - if (blobdb_event->db_id != BlobDBIdiOSNotifPref) { - return; - } - - if (blobdb_event->type != BlobDBEventTypeFlush && - memcmp(blobdb_event->key, (uint8_t *)SEND_TEXT_NOTIF_PREF_KEY, - strlen(SEND_TEXT_NOTIF_PREF_KEY))) { - return; - } - - s_has_send_text_reply_action = prv_has_send_text_reply_action(); -} - -void send_text_service_init(void) { - // Save the initial state - s_has_send_text_reply_action = prv_has_send_text_reply_action();; - - // Register for updates - static EventServiceInfo s_blobdb_event_info = { - .type = PEBBLE_BLOBDB_EVENT, - .handler = prv_blobdb_event_handler, - }; - event_service_client_subscribe(&s_blobdb_event_info); -} - -bool send_text_service_is_send_text_supported(void) { - PebbleProtocolCapabilities capabilities; - bt_persistent_storage_get_cached_system_capabilities(&capabilities); - - return (capabilities.send_text_support && s_has_send_text_reply_action); -} diff --git a/src/fw/services/normal/send_text_service.h b/src/fw/services/normal/send_text_service.h deleted file mode 100644 index 46c9c6183f..0000000000 --- a/src/fw/services/normal/send_text_service.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Currently this file serves as a cache for the existence of the SEND_TEXT_NOTIF_PREF_KEY, and -//! a reply action within that key. -//! This is required because a user can have a supported mobile app but not a supported carrier, -//! and in that case we don't want to show the app in the launcher. -//! We cache the existence of this key so that the launcher isn't slowed down by flash reads - -void send_text_service_init(void); - -bool send_text_service_is_send_text_supported(void); diff --git a/src/fw/services/normal/services_normal.c b/src/fw/services/normal/services_normal.c deleted file mode 100644 index db96309c1f..0000000000 --- a/src/fw/services/normal/services_normal.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services_normal.h" - -#include - -#include "applib/event_service_client.h" -#include "drivers/rtc.h" -#include "kernel/events.h" -#include "process_management/app_install_manager.h" // FIXME: This should really be in services/ -#include "process_management/launcher_app_message.h" // FIXME: This should really be in services/ -#include "services/normal/activity/activity.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/app_cache.h" -#include "services/normal/app_fetch_endpoint.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/blob_db/endpoint_private.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/protobuf_log/protobuf_log.h" -#include "services/normal/music_endpoint.h" -#include "services/normal/music_internal.h" -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/notifications/notifications.h" -#include "services/normal/persist.h" -#include "services/normal/phone_call.h" -#include "services/normal/process_management/app_order_storage.h" -#include "services/normal/send_text_service.h" -#include "services/normal/stationary.h" -#include "services/normal/timeline/event.h" -#include "services/normal/wakeup.h" -#include "services/normal/weather/weather_service.h" -#include "services/runlevel_impl.h" - -#ifndef PLATFORM_TINTIN -#include "services/normal/activity/activity.h" -#include "services/normal/voice/voice.h" -#endif - -#include "util/size.h" - -// Minimum valid time: January 1, 2010 00:00:00 UTC (timestamp: 1262304000) -// This represents the minimum time we consider valid for activity initialization -#define MIN_VALID_TIME_TIMESTAMP 1262304000 - -// State for deferred activity initialization -static bool s_activity_init_deferred = false; -static EventServiceInfo s_time_event_info; - -static void prv_time_set_event_handler(PebbleEvent *e, void *context) { - // Check if time is now valid and activity init was deferred - if (s_activity_init_deferred && rtc_get_time() >= MIN_VALID_TIME_TIMESTAMP) { - // Time is now valid, initialize activity - s_activity_init_deferred = false; - - // Unsubscribe from time events - event_service_client_unsubscribe(&s_time_event_info); - - activity_init(); - // If the user had tracking enabled before init was deferred, start tracking now so we - // don't miss steps when initialization happens after boot. - if (activity_prefs_tracking_is_enabled()) { - // Ensure the runlevel-enabled flag is set for the activity service so the usual - // enable/disable machinery will attempt to start tracking. This covers the case - // where services_set_runlevel() already ran before activity was initialized. - activity_set_enabled(true); - activity_start_tracking(false /* test_mode */); - } - } -} - -static bool prv_is_time_valid_for_activity_init(void) { - return rtc_get_time() >= MIN_VALID_TIME_TIMESTAMP; -} - -void services_normal_early_init(void) { - pfs_init(true); -} - -void services_normal_init(void) { - persist_service_init(); - - app_install_manager_init(); - - blob_db_init_dbs(); - app_cache_init(); - phone_call_service_init(); - music_init(); - alarm_init(); - timeline_event_init(); - dls_init(); - - wakeup_init(); - - app_order_storage_init(); - -#if CAPABILITY_HAS_HEALTH_TRACKING - // Check if time is valid before initializing activity - if (prv_is_time_valid_for_activity_init()) { - activity_init(); - } else { - // Defer activity initialization until time is set properly - s_activity_init_deferred = true; - - // Subscribe to time set events - s_time_event_info = (EventServiceInfo) { - .type = PEBBLE_SET_TIME_EVENT, - .handler = prv_time_set_event_handler, - }; - event_service_client_subscribe(&s_time_event_info); - } -#endif - - notifications_init(); - alerts_init(); - send_text_service_init(); - protobuf_log_init(); - -#if CAPABILITY_HAS_WEATHER - weather_service_init(); -#endif - -#if CAPABILITY_HAS_MICROPHONE - voice_init(); -#endif - -#if CAPABILITY_HAS_APP_GLANCES - app_glance_service_init(); -#endif -} - -static struct ServiceRunLevelSetting s_runlevel_settings[] = { - { - .set_enable_fn = wakeup_enable, - .enable_mask = R_Stationary | R_Normal, - }, - { - .set_enable_fn = alarm_service_enable_alarms, - .enable_mask = R_LowPower | R_Stationary | R_Normal, - }, -#if CAPABILITY_HAS_HEALTH_TRACKING - { - .set_enable_fn = activity_set_enabled, - .enable_mask = R_Stationary | R_Normal, - }, -#endif - { - .set_enable_fn = stationary_run_level_enable, - .enable_mask = R_Stationary | R_Normal, - }, - { - .set_enable_fn = dls_set_send_enable_run_level, - .enable_mask = R_Normal, - }, - { - .set_enable_fn = blob_db_enabled, - .enable_mask = R_Normal, - } -}; - -void services_normal_set_runlevel(RunLevel runlevel) { - for (size_t i = 0; i < ARRAY_LENGTH(s_runlevel_settings); ++i) { - struct ServiceRunLevelSetting *service = &s_runlevel_settings[i]; - service->set_enable_fn(((1 << runlevel) & service->enable_mask) != 0); - } -} diff --git a/src/fw/services/normal/services_normal.h b/src/fw/services/normal/services_normal.h deleted file mode 100644 index 19c43751c2..0000000000 --- a/src/fw/services/normal/services_normal.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/runlevel.h" - -void services_normal_early_init(void); - -void services_normal_init(void); - -void services_normal_set_runlevel(RunLevel runlevel); diff --git a/src/fw/services/normal/settings/settings_file.c b/src/fw/services/normal/settings/settings_file.c deleted file mode 100644 index 4ad49a7aa0..0000000000 --- a/src/fw/services/normal/settings/settings_file.c +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_file.h" -#include "settings_raw_iter.h" - -#include "drivers/rtc.h" -#include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/crc8.h" - -#include -#include - -static status_t bootup_check(SettingsFile *file); -static void compute_stats(SettingsFile *file); - -static bool file_hdr_is_uninitialized(SettingsFileHeader *file_hdr) { - return (file_hdr->magic == 0xffffffff) && (file_hdr->version == 0xffff) - && (file_hdr->flags == 0xffff); -} - -static status_t prv_open(SettingsFile *file, const char *name, uint8_t flags, int max_used_space) { - // Making the max_space_total at least a little bit larger than the - // max_used_space allows us to avoid thrashing. Without it, if - // max_space_total == max_used_space, then if the file is full, changing a - // single value would force the whole file to be rewritten- every single - // time! It's probably worth it to "waste" a bit of flash space to avoid - // this pathalogical case. - int max_space_total = pfs_sector_optimal_size(max_used_space * 12 / 10, strlen(name)); - - // TODO: Dynamically sized files? - int fd = pfs_open(name, flags, FILE_TYPE_STATIC, max_space_total); - if (fd < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not open settings file '%s', %d", name, fd); - if (fd == E_BUSY) { - // This is very bad. Someone didn't use a mutex. There could already be - // silent corruption, so it's better to crash now rather than let things - // get even more scrambled. - PBL_CROAK("Settings file is already open!"); - } - return fd; - } - - *file = (SettingsFile) { - .name = kernel_strdup_check(name), - .max_used_space = max_used_space, - .max_space_total = max_space_total, - }; - - settings_raw_iter_init(&file->iter, fd, file->name); - - SettingsFileHeader file_hdr = file->iter.file_hdr; - if (file_hdr_is_uninitialized(&file_hdr)) { - // Newly created file, create & write out header. - memcpy(&file_hdr.magic, SETTINGS_FILE_MAGIC, sizeof(file_hdr.magic)); - file_hdr.version = SETTINGS_FILE_VERSION; - settings_raw_iter_write_file_header(&file->iter, &file_hdr); - } - - if (memcmp(&file_hdr.magic, SETTINGS_FILE_MAGIC, sizeof(file_hdr.magic)) != 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempted to open %s, not a settings file.", name); - pfs_close_and_remove(fd); - return E_INVALID_OPERATION; - } - - if (file_hdr.version > SETTINGS_FILE_VERSION) { - PBL_LOG(LOG_LEVEL_WARNING, - "Unrecognized version %d for file %s, removing...", - file_hdr.version, name); - pfs_close_and_remove(fd); - return prv_open(file, name, flags, max_used_space); - } - - status_t status = bootup_check(file); - if (status < 0) { - PBL_LOG(LOG_LEVEL_ERROR, - "Bootup check failed (%"PRId32"), not good. " - "Attempting to recover by deleting %s...", status, name); - pfs_close_and_remove(fd); - return prv_open(file, name, flags, max_used_space); - } - - // There's a chance that the caller increased the desired size of the settings file since - // the file was originally created (i.e. the file was created in an earlier version of the - // firmware). If we detect that situation, let's re-write the file to the new larger requested - // size. - int actual_size = pfs_get_file_size(file->iter.fd); - if (actual_size < max_space_total) { - PBL_LOG(LOG_LEVEL_INFO, "Re-writing settings file %s to increase its size from %d to %d.", - name, actual_size, max_space_total); - // The settings_file_rewrite_filtered call creates a new file based on file->max_used_space - // and copies the contents of the existing file into it. - status = settings_file_rewrite_filtered(file, NULL, NULL); - if (status < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not resize file %s (error %"PRId32"). Creating new one", - name, status); - return prv_open(file, name, flags, max_used_space); - } - } - - compute_stats(file); - - return S_SUCCESS; -} - -status_t settings_file_open(SettingsFile *file, const char *name, - int max_used_space) { - return prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, max_used_space); -} - -void settings_file_close(SettingsFile *file) { - settings_raw_iter_deinit(&file->iter); - kernel_free(file->name); - file->name = NULL; -} - -static int record_size(SettingsRecordHeader *hdr) { - return sizeof(*hdr) + hdr->key_len + hdr->val_len; -} - -// Flags are stored in flash the inverse of how you might normally expect- a -// zero denotes that the flag is set, a 1 means it is not. This is because our -// flash chip is NOR flash, and thus is all 1's by default. -// Once setting a flag, we cannot unset it. -static void set_flag(SettingsRecordHeader *hdr, uint8_t flags) { - hdr->flags &= ~flags; -} -static void clear_flag(SettingsRecordHeader *hdr, uint8_t flags) { - hdr->flags |= flags; -} -static bool flag_is_set(SettingsRecordHeader *hdr, uint8_t flags) { - return (hdr->flags & flags) == 0; -} - -// Records have 4 possible states: -// - EOF marker: Header is all 1s -// - partially_written: Some bits in the header have been changed to 0s, but -// the entire record has not been completely written yet. Records in this -// state are removed on bootup, since they are in an indeterminate state. -// - written: The typical state for a record. == !partially_written -// - partially_overwritten: This record has been superceeded by another, which -// we are currently in the process of writing out to flash. Records in -// this state are restored on bootup. -// - overwritten: This record has been superceeded by another, which has been -// completely written out to flash. We skip over and ignore overwritten -// records. -static bool partially_written(SettingsRecordHeader *hdr) { - return !flag_is_set(hdr, SETTINGS_FLAG_WRITE_COMPLETE); -} -static bool partially_overwritten(SettingsRecordHeader *hdr) { - return flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_STARTED) - && !flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); -} -static bool overwritten(SettingsRecordHeader *hdr) { - return flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_STARTED) - && flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); -} - -static uint32_t utc_time() { - return rtc_get_time(); -} - -static bool deleted_and_expired(SettingsRecordHeader *hdr) { - return (hdr->val_len == 0) - && (hdr->last_modified <= (utc_time() - DELETED_LIFETIME)); -} - -static void compute_stats(SettingsFile *file) { - file->dead_space = 0; - file->used_space = 0; - file->last_modified = 0; - file->used_space += sizeof(SettingsFileHeader); - file->used_space += sizeof(SettingsRecordHeader); // EOF Marker - for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); - settings_raw_iter_next(&file->iter)) { - if (overwritten(&file->iter.hdr) || deleted_and_expired(&file->iter.hdr)) { - file->dead_space += record_size(&file->iter.hdr); - } else { - file->used_space += record_size(&file->iter.hdr); - } - if (file->iter.hdr.last_modified > file->last_modified) { - file->last_modified = file->iter.hdr.last_modified; - } - } -} - -status_t settings_file_rewrite_filtered( - SettingsFile *file, SettingsFileRewriteFilterCallback filter_cb, void *context) { - SettingsFile new_file; - status_t status = prv_open(&new_file, file->name, OP_FLAG_OVERWRITE | OP_FLAG_READ, - file->max_used_space); - if (status < 0) { - PBL_LOG(LOG_LEVEL_ERROR, - "Could not open temporary file to compact settings file. Error %"PRIi32".", - status); - return status; - } - - settings_raw_iter_begin(&new_file.iter); - - for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); - settings_raw_iter_next(&file->iter)) { - SettingsRecordHeader *hdr = &file->iter.hdr; - if (partially_written(hdr)) { - // This should only happen if we reboot in the middle of writing a new - // record, and it should always be the most recently written record, so - // we shouldn't lose any data here (except for the partially written - // record, but we should ignore it's garbage data anyway). - break; - } - if (overwritten(hdr) || deleted_and_expired(hdr)) { - continue; - } - if (partially_overwritten(hdr)) { - // The only case where we should hit this is if we are compacting a file - // which has a record which was in the middle of being overwritten, but - // the write of the new record didn't finish by the time we rebooted. - // There should only ever be one such record, and we shouldn't ever hit - // this if writing the new record actually completed, since we check - // for this case in bootup_check(). - clear_flag(hdr, SETTINGS_FLAG_OVERWRITE_STARTED); - } - - // Get the old key and value - uint8_t *key = kernel_malloc(hdr->key_len); - settings_raw_iter_read_key(&file->iter, key); - uint8_t *val = kernel_malloc(hdr->val_len); - settings_raw_iter_read_val(&file->iter, val, hdr->val_len); - - // Include in re-written file if it passes the filter - if (!filter_cb || filter_cb(key, hdr->key_len, val, hdr->val_len, context)) { - settings_raw_iter_write_header(&new_file.iter, hdr); - settings_raw_iter_write_key(&new_file.iter, key); - settings_raw_iter_write_val(&new_file.iter, val); - settings_raw_iter_next(&new_file.iter); - } - kernel_free(key); - kernel_free(val); - } - settings_file_close(file); - // We have to close and reopen the new_file so that it's temp flag is cleared. - // Before the close succeeds, if we reboot, we will just end up reading the - // old file. After the close suceeds, we will end up reading the new - // (compacted) file. - char *name = kernel_strdup(new_file.name); - settings_file_close(&new_file); - status = prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, file->max_used_space); - kernel_free(name); - return status; -} - -T_STATIC status_t settings_file_compact(SettingsFile *file) { - return settings_file_rewrite_filtered(file, NULL, NULL); -} - -static bool key_matches(SettingsRawIter *iter, const uint8_t *key, int key_len) { - SettingsRecordHeader *hdr = &iter->hdr; - if (key_len != hdr->key_len) { - return false; - } - if (crc8_calculate_bytes(key, key_len, true /* big_endian */) != hdr->key_hash) { - return false; - } - uint8_t hdr_key[hdr->key_len]; - settings_raw_iter_read_key(iter, hdr_key); - if (memcmp(key, hdr_key, hdr->key_len) == 0) { - return true; - } - return false; -} - -static bool prv_is_desired_hdr(SettingsRawIter *iter, const uint8_t *key, int key_len) { - if (overwritten(&iter->hdr) || partially_written(&iter->hdr)) { - return false; - } - - return key_matches(iter, key, key_len); -} - -static bool search_forward(SettingsRawIter *iter, const uint8_t *key, int key_len) { - const int resumed_pos = settings_raw_iter_get_resumed_record_pos(iter); - - // Resume searching at the current record - for (; !settings_raw_iter_end(iter); settings_raw_iter_next(iter)) { - if (prv_is_desired_hdr(iter, key, key_len)) { - return true; - } - } - - // If we got here, we didn't find it between `resumed_pos` and the last record in the file. - // Wrap around to the beginning and search until we get to the `resumed_pos` - settings_raw_iter_begin(iter); - for (; settings_raw_iter_get_current_record_pos(iter) < resumed_pos; - settings_raw_iter_next(iter)) { - if (prv_is_desired_hdr(iter, key, key_len)) { - return true; - } - } - - // No record found - return false; -} - -static status_t cleanup_partial_transactions(SettingsFile *file) { - for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); - settings_raw_iter_next(&file->iter)) { - - if (partially_written(&file->iter.hdr)) { - // Compact will remove partially written records. We could be smarter, - // but this is something of an edge case. - return settings_file_compact(file); - } - - if (!partially_overwritten(&file->iter.hdr)) { - continue; - } - - int partially_overwritten_record_pos = - settings_raw_iter_get_current_record_pos(&file->iter); - uint8_t key[file->iter.hdr.key_len]; - settings_raw_iter_read_key(&file->iter, key); - settings_raw_iter_next(&file->iter); // Skip the current record - bool found_another = search_forward(&file->iter, key, file->iter.hdr.key_len); - - if (!found_another) { - // No other file->iter.hdr found, we must have rebooted in the middle of - // writing the new record. Compacting the file will copy over the - // previous record while clearing the overwrite bits for us, so that we - // can still find the previous record. We could be smarter here, but - // this should happen pretty rarely. - return settings_file_compact(file); - } - - // The overwrite completed, we just rebooted before getting a chance - // to flip the completion bit on the previous record. Flip it now so - // that we don't have to keep checking on every boot. - settings_raw_iter_set_current_record_pos(&file->iter, - partially_overwritten_record_pos); - set_flag(&file->iter.hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); - settings_raw_iter_write_header(&file->iter, &file->iter.hdr); - } - return S_SUCCESS; -} - -static status_t bootup_check(SettingsFile* file) { - return cleanup_partial_transactions(file); -} - -int settings_file_get_len(SettingsFile *file, const void *key, size_t key_len) { - settings_raw_iter_resume(&file->iter); - if (search_forward(&file->iter, key, key_len)) { - return file->iter.hdr.val_len; - } else { - return 0; - } -} - -bool settings_file_exists(SettingsFile *file, const void *key, size_t key_len) { - return (settings_file_get_len(file, key, key_len) > 0); -} - -status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len, - void *val_out, size_t val_out_len) { - settings_raw_iter_resume(&file->iter); - if (!search_forward(&file->iter, key, key_len)) { - memset(val_out, 0, val_out_len); - return E_DOES_NOT_EXIST; - } - if (deleted_and_expired(&file->iter.hdr)) { - memset(val_out, 0, val_out_len); - return E_DOES_NOT_EXIST; - } - size_t val_len = file->iter.hdr.val_len; - if (val_out_len > val_len) { - memset(val_out, 0, val_out_len); - return E_RANGE; - } - settings_raw_iter_read_val(&file->iter, val_out, val_out_len); - return S_SUCCESS; -} - -status_t settings_file_set_byte(SettingsFile *file, const void *key, - size_t key_len, size_t offset, uint8_t byte) { - if (key_len > SETTINGS_KEY_MAX_LEN) { - return E_RANGE; - } - - // Find the record - settings_raw_iter_resume(&file->iter); - if (!search_forward(&file->iter, key, key_len) || - file->iter.hdr.val_len == 0) { - return E_DOES_NOT_EXIST; - } - - PBL_ASSERTN(offset < file->iter.hdr.val_len); - settings_raw_iter_write_byte(&file->iter, offset, byte); - - return S_SUCCESS; -} - -// Note that this operation is designed to be atomic from the perspective of -// an outside observer. That is, either the new value will be completely -// written and returned for all future queries, or, if we reboot/loose power/ -// run into an error, then we will continue to return the previous value. -// We should never run into a case where neither value exists. -status_t settings_file_set(SettingsFile *file, const void *key, size_t key_len, - const void *val, size_t val_len) { - // Cannot set keys while iterating (Try settings_file_rewrite) - PBL_ASSERTN(file->cur_record_pos == 0); - if (key_len > SETTINGS_KEY_MAX_LEN) { - return E_RANGE; - } - if (val_len > SETTINGS_VAL_MAX_LEN) { - return E_RANGE; - } - const bool is_delete = (val_len == 0); - const int rec_size = sizeof(SettingsRecordHeader) + key_len + val_len; - if (!is_delete && file->used_space + rec_size > file->max_used_space) { - return E_OUT_OF_STORAGE; - } - if (file->used_space + file->dead_space + rec_size > file->max_space_total) { - status_t status = settings_file_compact(file); - if (status < 0) { - return status; - } - } - - int overwritten_record = -1; - // Find an existing record, if any, and mark it as overwrite-in-progress. - settings_raw_iter_resume(&file->iter); - if (search_forward(&file->iter, key, key_len)) { - set_flag(&file->iter.hdr, SETTINGS_FLAG_OVERWRITE_STARTED); - settings_raw_iter_write_header(&file->iter, &file->iter.hdr); - overwritten_record = settings_raw_iter_get_current_record_pos(&file->iter); - } - - while (!settings_raw_iter_end(&file->iter)) { - settings_raw_iter_next(&file->iter); - } - - // Create and write out a new record. Writing the header transitions us into - // the write-in-progress state, since at least once of the bits must be - // flipped from a 1 to a 0 in order for the header to be valid. - SettingsRecordHeader new_hdr; - memset(&new_hdr, 0xff, sizeof(new_hdr)); - new_hdr.last_modified = utc_time(); - new_hdr.key_hash = crc8_calculate_bytes(key, key_len, true /* big_endian */); - new_hdr.key_len = key_len; - new_hdr.val_len = val_len; - - settings_raw_iter_write_header(&file->iter, &new_hdr); - settings_raw_iter_write_key(&file->iter, key); - settings_raw_iter_write_val(&file->iter, val); - - // Mark the new record as write complete, now that we have completely written - // out the header, key, and value. - set_flag(&new_hdr, SETTINGS_FLAG_WRITE_COMPLETE); - settings_raw_iter_write_header(&file->iter, &new_hdr); - file->used_space += rec_size; - - // Finally, mark the existing record, if any, as overwritten. - if (overwritten_record >= 0) { - settings_raw_iter_set_current_record_pos(&file->iter, overwritten_record); - set_flag(&file->iter.hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); - settings_raw_iter_write_header(&file->iter, &file->iter.hdr); - file->dead_space += record_size(&file->iter.hdr); - file->used_space -= record_size(&file->iter.hdr); - } - - return S_SUCCESS; -} - -status_t settings_file_mark_synced(SettingsFile *file, const void *key, size_t key_len) { - // Cannot set keys while iterating (Try settings_file_rewrite) - PBL_ASSERTN(file->cur_record_pos == 0); - if (key_len > SETTINGS_KEY_MAX_LEN) { - return E_RANGE; - } - - // Find an existing record, if any, and mark it as synced - settings_raw_iter_resume(&file->iter); - if (search_forward(&file->iter, key, key_len)) { - set_flag(&file->iter.hdr, SETTINGS_FLAG_SYNCED); - settings_raw_iter_write_header(&file->iter, &file->iter.hdr); - return S_SUCCESS; - } - - return E_DOES_NOT_EXIST; -} - -status_t settings_file_delete(SettingsFile *file, - const void *key, size_t key_len) { - return settings_file_set(file, key, key_len, NULL, 0); -} - -static void prv_get_key(SettingsFile *file, void *key, size_t key_len) { - PBL_ASSERTN(key_len <= file->iter.hdr.key_len); - settings_raw_iter_set_current_record_pos(&file->iter, file->cur_record_pos); - settings_raw_iter_read_key(&file->iter, key); -} -static void prv_get_val(SettingsFile *file, void *val, size_t val_len) { - PBL_ASSERTN(val_len <= file->iter.hdr.val_len); - settings_raw_iter_set_current_record_pos(&file->iter, file->cur_record_pos); - settings_raw_iter_read_val(&file->iter, val, val_len); -} -status_t settings_file_each(SettingsFile *file, SettingsFileEachCallback cb, - void *context) { - // Cannot set keys while iterating - PBL_ASSERTN(file->cur_record_pos == 0); - SettingsRecordInfo info; - for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); - settings_raw_iter_next(&file->iter)) { - if (overwritten(&file->iter.hdr) || deleted_and_expired(&file->iter.hdr)) { - continue; - } - info = (SettingsRecordInfo) { - .last_modified = file->iter.hdr.last_modified, - .get_key = prv_get_key, - .key_len = file->iter.hdr.key_len, - .get_val = prv_get_val, - .val_len = file->iter.hdr.val_len, - .dirty = !flag_is_set(&file->iter.hdr, SETTINGS_FLAG_SYNCED), - }; - file->cur_record_pos = settings_raw_iter_get_current_record_pos(&file->iter); - // if the callback returns false, stop iterating. - if (!cb(file, &info, context)) { - break; - } - settings_raw_iter_set_current_record_pos(&file->iter, file->cur_record_pos); - } - - file->cur_record_pos = 0; - - return S_SUCCESS; -} - -typedef struct { - SettingsFileRewriteCallback cb; - SettingsFile *new_file; - void *user_context; -} RewriteCbContext; -static bool prv_rewrite_cb(SettingsFile *file, SettingsRecordInfo *info, - void *context) { - RewriteCbContext *cb_ctx = (RewriteCbContext*)context; - cb_ctx->cb(file, cb_ctx->new_file, info, cb_ctx->user_context); - return true; // continue iterating -} -status_t settings_file_rewrite(SettingsFile *file, - SettingsFileRewriteCallback cb, void *context) { - SettingsFile new_file; - status_t status = prv_open(&new_file, file->name, - OP_FLAG_OVERWRITE | OP_FLAG_READ, - file->max_used_space); - if (status < 0) { - return status; - } - RewriteCbContext cb_ctx = (RewriteCbContext) { - .cb = cb, - .new_file = &new_file, - .user_context = context, - }; - settings_file_each(file, prv_rewrite_cb, &cb_ctx); - settings_file_close(file); - // We have to close and reopen the new_file so that it's temp flag is cleared. - // Before the close succeeds, if we reboot, we will just end up reading the - // old file. After the close suceeds, we will end up reading the new - // (compacted) file. - char *name = kernel_strdup(new_file.name); - settings_file_close(&new_file); - status = prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, file->max_used_space); - kernel_free(name); - - return status; -} diff --git a/src/fw/services/normal/settings/settings_file.h b/src/fw/services/normal/settings/settings_file.h deleted file mode 100644 index 7d49ab66b9..0000000000 --- a/src/fw/services/normal/settings/settings_file.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "settings_raw_iter.h" - -// Deleted records have their key stick around for at least DELETED_LIFETIME -// before they can be garbage collected from the file in which they are -// contained, that way they have time to propegate to all devices we end up -// syncronizing with. For more information, refer to the sync protocol proposal: -// https://pebbletechnology.atlassian.net/wiki/pages/viewpage.action?pageId=26837564 -// -// FIXME: See PBL-18945 -#define DELETED_LIFETIME (0 * SECONDS_PER_DAY) - -//! A SettingsFile is just a simple binary key-value store. Keys can be strings, -//! uint32_ts, or arbitrary bytes. Values are similarilly flexible. All -//! operations are atomic, so a reboot in the middle of changing the value for a -//! key will always either complete, returning the new value upon reboot, or -//! will just return the old value. -//! It also supports bidirection syncronization between the phone & watch, -//! using timestamps to resolve conflicts. -//! Note that although all operations are atomic, they are not thread-safe. If -//! you will be accessing a SettingsFile from multiple threads, make sure you -//! use locks! - -// NOTE: These fields are internal, modify them at your own risk! -typedef struct SettingsFile { - SettingsRawIter iter; - char *name; - - //! Maximum total space which can be used by this settings_file before a - //! compaction will be forced. (Must be >= max_used_space) - int max_space_total; - - //! Maximum space that can be used by valid records within this settings_file. - //! Once this has been exceeded, attempting to add more keys or values will - //! fail. - int max_used_space; - - //! Amount of space in the settings_file that is currently dead, i.e. - //! has been written to with some data, but that data is no longer valid. - //! (overwritten records get added to this) - int dead_space; - - //! Amount of space in the settings_file that is currently used by valid - //! records. - int used_space; - - //! When this file as a whole was last_modified. - //! Defined as records.max(&:last_modified) - uint32_t last_modified; - - //! The position of the current record in the iteration (if any). Necessary - //! so that clients can read other records in the middle of iteration (i.e. - //! settings_file_each()/settings_file_rewrite()), without messing up the - //! state of the iteration. Set to 0 if not in use. - int cur_record_pos; -} SettingsFile; - - -//! max_used_space should be >= 5317 for persist files to make sure we can -//! always fit all of the records in the worst case (if the programmer stored -//! nothing but booleans). -//! Note: If the settings file already exists, the max_used_space parameter is -//! ignored. We could change this if the need arises. -status_t settings_file_open(SettingsFile *file, const char *name, - int max_used_space); -void settings_file_close(SettingsFile *file); - -bool settings_file_exists(SettingsFile *file, const void *key, size_t key_len); -status_t settings_file_delete(SettingsFile *file, - const void *key, size_t key_len); - -int settings_file_get_len(SettingsFile *file, const void *key, size_t key_len); -//! val_out_len must exactly match the length of the record on disk. -status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len, - void *val_out, size_t val_out_len); -status_t settings_file_set(SettingsFile *file, const void *key, size_t key_len, - const void *val, size_t val_len); - -//! Mark a record as synced. The flag will remain until the record is overwritten -//! @param file the settings_file that contains the record -//! @param key the key to the settings file. Note: keys can be up to 127 bytes -//! @param key_len the length of the key -status_t settings_file_mark_synced(SettingsFile *file, const void *key, size_t key_len); - -//! set a byte in a setting. This can only be used a byte at a time to guarantee -//! atomicity. Do not use to modify several bytes in a row! -//! Note that only the reset bits will be applied (it writes flash directly) -status_t settings_file_set_byte( - SettingsFile *file, const void *key, size_t key_len, - size_t offset, uint8_t byte); - - - - ////////////////// - // Each/rewrite // -////////////////// -typedef void (*SettingsFileGetter)(SettingsFile *file, - void *buf, size_t buf_len); - -typedef struct { - uint32_t last_modified; - SettingsFileGetter get_key; - int key_len; - SettingsFileGetter get_val; - int val_len; - bool dirty; // has the dirty flag set -} SettingsRecordInfo; - -//! Callback used for using settings_file_each. -//! The bool returned is used to control the iteration. -//! - If a callback returns true, the iteration continues -//! - If a callback returns false, the ieration stops. -typedef bool (*SettingsFileEachCallback)(SettingsFile *file, - SettingsRecordInfo *info, - void *context); -//! Calls cb for each and every entry within the given file. -//! Note that you cannot modify the settings file while iterating. If you want -//! to do this, try settings_file_rewrite instead. (you can read other entries -//! without fault). -status_t settings_file_each(SettingsFile *file, SettingsFileEachCallback cb, - void *context); - - -typedef void (*SettingsFileRewriteCallback)(SettingsFile *old_file, - SettingsFile *new_file, - SettingsRecordInfo *info, - void *context); -//! Opens a new SettingsFile with the same name as the original SettingsFile, -//! in overwrite mode. This new file is passed into the given -//! SettingsFileRewriteCallback, which is called for each entry within the -//! original file. If you desire to preserve a key/value pair, you must write -//! it to the new file. -status_t settings_file_rewrite(SettingsFile *file, - SettingsFileRewriteCallback cb, - void *context); - - -//! Callback used for using settings_file_rewrite_filtered. -//! The bool returned is used to control whether or not the record is included in the file -//! after compaction. This callback is not allowed to use any other settings_file calls. -//! - If callback returns true, the record is included -//! - If callback returns false, the record is not included -typedef bool (*SettingsFileRewriteFilterCallback)(void *key, size_t key_len, void *value, - size_t value_len, void *context); - -//! Opens a new SettingsFile with the same name as the original SettingsFile, -//! in overwrite mode. Any records from the old file which pass through the filter_cb with -//! a true result are included into the new file. This call is much faster than using -//! settings_file_rewrite if all you are doing is excluding specific records from the old file. -status_t settings_file_rewrite_filtered(SettingsFile *file, - SettingsFileRewriteFilterCallback filter_cb, void *context); diff --git a/src/fw/services/normal/stationary.c b/src/fw/services/normal/stationary.c deleted file mode 100644 index 31b4f9f7e6..0000000000 --- a/src/fw/services/normal/stationary.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stationary.h" - -#include "applib/accel_service_private.h" -#include "applib/battery_state_service.h" -#include "applib/ui/dialogs/dialog_private.h" -#include "applib/ui/dialogs/simple_dialog.h" -#include "comm/bt_lock.h" -#include "drivers/battery.h" -#include "kernel/event_loop.h" -#include "kernel/ui/modals/modal_manager.h" -#include "resource/resource_ids.auto.h" -#include "services/common/accel_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/i18n/i18n.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" -#include "services/runlevel.h" -#include "shell/prefs.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/size.h" - -#include - -#define STATIONARY_PEEKING_TIME_MINS 5 -#define STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS 30 -#define STATIONARY_ENABLED_DIALOG_TIMEOUT_MS 1800000 -#define STATIONARY_WELCOME_BACK_DIALOG_TIMEOUT_MS 2000 -#define ACCEL_MAX_IDLE_DELTA 100 - -#define DEBUG_STATIONARY 0 - -//! Used for describing the stationary event reason in analytics -typedef enum { - StationaryAnalyticsEnterNormally, - StationaryAnalyticsEnterFromPeek, - StationaryAnalyticsExitNormally, - StationaryAnalyticsExitToPeek, - StationaryAnalyticsEnterCharging, - StationaryAnalyticsExitCharging, - StationaryAnalyticsEnableStationaryMode, - StationaryAnalyticsDisableStationaryMode -} StationaryAnalytics; - -//! The possible states that the watch can be in regarding stationary mode -typedef enum { - StationaryStateAwake, - StationaryStateStationary, - StationaryStatePeeking, - StationaryStateDisabled, -} StationaryState; - -//! The actions we take upon state transitions -typedef enum { - StationaryActionGoToSleep, - StationaryActionWakeUp, - StationaryActionEnableStationary, - StationaryActionDisableStationary -} StationaryAction; - -static AccelData s_last_accel_data; -static AccelServiceState *s_accel_session; -static EventServiceInfo s_button_event_info; -static RegularTimerInfo s_accel_stationary_timer_info; -static StationaryState s_current_state = StationaryStateDisabled; -static uint8_t s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; -static bool s_stationary_mode_inhibit = true; - -static void prv_handle_action(StationaryAction action); - -// Compute and return the device's delta position to help determine movement as idle. -static uint32_t prv_compute_delta_pos(AccelData *cur_pos, AccelData *last_pos) { - return (abs(last_pos->x - cur_pos->x) + abs(last_pos->y - cur_pos->y) + - abs(last_pos->z - cur_pos->z)); -} - -//! The orientation of the accelerometer is checked every minute. If the orientation -//! has not changed by a significant amount, we consider it as stationary. -static bool prv_update_and_check_accel_is_stationary(void) { - AccelData last_accel_data = s_last_accel_data; - sys_accel_manager_peek(&s_last_accel_data); - return prv_compute_delta_pos(&last_accel_data, &s_last_accel_data) < ACCEL_MAX_IDLE_DELTA; -} - -static bool prv_is_allowed_to_run(void) { - return (stationary_get_enabled() && !s_stationary_mode_inhibit); -} - -static void prv_update_stationary_enabled(void *data) { - if (!battery_is_usb_connected() && prv_is_allowed_to_run()) { - prv_handle_action(StationaryActionEnableStationary); - } else { - prv_handle_action(StationaryActionDisableStationary); - } -} - -void stationary_handle_battery_connection_change_event(void) { - PBL_LOG_D(DEBUG_STATIONARY, LOG_LEVEL_DEBUG, - "Stationary mode battery state change event received"); - if (battery_is_usb_connected()) { - analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsEnterCharging); - } else { - analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsExitCharging); - } - prv_update_stationary_enabled(NULL); -} - -//! A movement of the watch will make the watch wake up. In this state, the watch -//! will have a low tap threshold, so it will be sensitive to motion -static void prv_accel_tap_handler(AccelAxisType axis, int32_t direction) { - prv_handle_action(StationaryActionWakeUp); -} - -static void prv_button_down_handler(PebbleEvent *event, void *data) { - prv_handle_action(StationaryActionWakeUp); -} - -//! If the watch is determined to be motionless for 30 minutes, it will go to sleep -static void prv_watch_is_motionless(void) { - // Check if we should enable stationary mode and disabled unneeded features - if (s_stationary_count_down > 0) { - PBL_LOG_D(DEBUG_STATIONARY, LOG_LEVEL_DEBUG, - "Countdown to stationary: %d", s_stationary_count_down); - s_stationary_count_down--; - } else { - analytics_inc(ANALYTICS_DEVICE_METRIC_STATIONARY_TIME_MINUTES, AnalyticsClient_System); - prv_handle_action(StationaryActionGoToSleep); - } -} - -//! The orientation of the accelerometer is checked every minute. If the orientation has -//! changed by a significant amount, we consider the watch as in motion, and restart the -//! stationary counter -static void prv_watch_is_in_motion(void) { - prv_handle_action(StationaryActionWakeUp); -} - -static void prv_stationary_check_launcher_task_cb(void *unused_data) { - if (prv_update_and_check_accel_is_stationary()) { - prv_watch_is_motionless(); - } else { - prv_watch_is_in_motion(); - } -} - -//! Called every minute to determine whether any motion has occured since the last time -//! the call was made. The current position is updated at this time -static void prv_stationary_check_timer_cb(void *unused_data) { - //! All stationary events need to be handled by kernel main - launcher_task_add_callback(prv_stationary_check_launcher_task_cb, NULL); -} - -bool stationary_get_enabled(void) { - return shell_prefs_get_stationary_enabled(); -} - -void stationary_set_enabled(bool enabled) { - if (enabled == stationary_get_enabled()) { - return; - } - shell_prefs_set_stationary_enabled(enabled); - - if (enabled) { - analytics_event_stationary_state_change(rtc_get_time(), - StationaryAnalyticsEnableStationaryMode); - } else { - analytics_event_stationary_state_change(rtc_get_time(), - StationaryAnalyticsDisableStationaryMode); - } - - launcher_task_add_callback(prv_update_stationary_enabled, NULL); -} - -void stationary_run_level_enable(bool enable) { -#if !STATIONARY_MODE - return; -#endif - - const bool inhibit = !enable; - if (inhibit == s_stationary_mode_inhibit) { - return; - } - s_stationary_mode_inhibit = inhibit; - launcher_task_add_callback(prv_update_stationary_enabled, NULL); -} - -void stationary_wake_up(void) { - if (!prv_is_allowed_to_run()) { - return; - } - prv_handle_action(StationaryActionWakeUp); -} - -static void prv_reset_stationary_counter(void) { - s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; -} - -static void prv_enter_awake_state(void) { - PBL_LOG(LOG_LEVEL_INFO, "Exiting stationary: Setting run level to normal"); - analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsExitNormally); - prv_reset_stationary_counter(); - s_current_state = StationaryStateAwake; -} - -//! The accelerometer tap threshold will be set very low, so a small motion will wake -//! the watch back up -static void prv_enter_stationary_state(void) { - PBL_LOG(LOG_LEVEL_INFO, "Entering stationary: Changing run level"); - if (s_current_state == StationaryStatePeeking) { - analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsEnterFromPeek); - } else if (s_current_state == StationaryStateAwake) { - analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsEnterNormally); - } - services_set_runlevel(RunLevel_Stationary); - accel_enable_high_sensitivity(true); - s_current_state = StationaryStateStationary; -} - -static void prv_exit_stationary(void) { - accel_enable_high_sensitivity(false); - services_set_runlevel(RunLevel_Normal); -} - -static void prv_enter_peek_state(void) { - analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsExitToPeek); - //! When exiting out of stationary, we aren't certain that this wasn't caused by noise yet - //! we set the counter to a small value in case there is no motion right after - s_stationary_count_down = STATIONARY_PEEKING_TIME_MINS; - prv_exit_stationary(); - s_current_state = StationaryStatePeeking; -} - -static void prv_enter_disabled_state(void) { - PBL_ASSERTN(s_accel_session); - accel_session_shake_unsubscribe(s_accel_session); - accel_session_delete(s_accel_session); - s_accel_session = NULL; - - event_service_client_unsubscribe(&s_button_event_info); - regular_timer_remove_callback(&s_accel_stationary_timer_info); - s_current_state = StationaryStateDisabled; -} - -static void prv_exit_disabled_state(void) { - s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; - PBL_ASSERTN(s_accel_session == NULL); - s_accel_session = accel_session_create(); - accel_session_shake_subscribe(s_accel_session, prv_accel_tap_handler); - event_service_client_subscribe(&s_button_event_info); - -#if DEBUG_STATIONARY - regular_timer_add_seconds_callback(&s_accel_stationary_timer_info); -#else - regular_timer_add_minutes_callback(&s_accel_stationary_timer_info); -#endif - s_current_state = StationaryStateAwake; -} - -static void prv_handle_awake_action(StationaryAction action) { - switch (action) { - case StationaryActionGoToSleep: - prv_enter_stationary_state(); - break; - case StationaryActionWakeUp: - prv_reset_stationary_counter(); - break; - case StationaryActionEnableStationary: - break; - case StationaryActionDisableStationary: - prv_enter_disabled_state(); - break; - default: - WTF; - } -} - -static void prv_handle_stationary_action(StationaryAction action) { - switch (action) { - case StationaryActionGoToSleep: - break; - case StationaryActionWakeUp: - prv_enter_peek_state(); - break; - case StationaryActionEnableStationary: - break; - case StationaryActionDisableStationary: - prv_exit_stationary(); - prv_enter_disabled_state(); - break; - default: - WTF; - } -} - -static void prv_handle_peeking_action(StationaryAction action) { - switch (action) { - case StationaryActionGoToSleep: - prv_enter_stationary_state(); - break; - case StationaryActionWakeUp: - prv_enter_awake_state(); - break; - case StationaryActionEnableStationary: - break; - case StationaryActionDisableStationary: - prv_enter_disabled_state(); - break; - default: - WTF; - } -} - -static void prv_handle_disabled_action(StationaryAction action) { - switch (action) { - case StationaryActionGoToSleep: - break; - case StationaryActionWakeUp: - // No-op here. Awake gives us the same runlevel as Disabled, so no harm in just staying in - // the disabled state. Could potentially be caused in races where we tap or press a button - // to wake us up from stationary while we're being disabled. - break; - case StationaryActionEnableStationary: - prv_exit_disabled_state(); - break; - case StationaryActionDisableStationary: - break; - default: - WTF; - } -} - -typedef void (*StationaryActionHandler)(StationaryAction action); - -static void prv_handle_action(StationaryAction action) { - // we need to be on kernel main so that we subscribe to event services - // for kernel main - PBL_ASSERT_TASK(PebbleTask_KernelMain); - PBL_LOG_D(DEBUG_STATIONARY, LOG_LEVEL_DEBUG, "Stationary: state %d action %d", - s_current_state, action); - - static StationaryActionHandler const prv_action_jump_table[] = { - [StationaryStateAwake] = prv_handle_awake_action, - [StationaryStateStationary] = prv_handle_stationary_action, - [StationaryStatePeeking] = prv_handle_peeking_action, - [StationaryStateDisabled] = prv_handle_disabled_action - }; - PBL_ASSERTN(s_current_state < ARRAY_LENGTH(prv_action_jump_table)); - prv_action_jump_table[s_current_state](action); -} - -static void prv_setup_callback_info(void) { - //! Timer callback to check whether the watch is stationary every minute - s_accel_stationary_timer_info = (RegularTimerInfo) { - .cb = prv_stationary_check_timer_cb - }; - - //! Button press events - s_button_event_info = (EventServiceInfo) { - .type = PEBBLE_BUTTON_DOWN_EVENT, - .handler = prv_button_down_handler, - }; -} - -void stationary_init(void) { - prv_setup_callback_info(); -} diff --git a/src/fw/services/normal/stationary.h b/src/fw/services/normal/stationary.h deleted file mode 100644 index 8b8d4240e6..0000000000 --- a/src/fw/services/normal/stationary.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "applib/accel_service.h" -#include "kernel/event_loop.h" - -//! Set up a timer that will check the position of the watch every minute to see -//! if any motion has occured -void stationary_init(void); - -//! Stationary mode should only be enabled when the user settings allow for it and when -//! the charger is not connected -bool stationary_get_enabled(void); - -//! Set whether the stationary module is enabled. When disabled, all operations will end, we ensure -//! that we are in a normal state, and the watch will not be able to enter stationary mode -void stationary_set_enabled(bool enabled); - -//! Set whether the stationary service is allowed to be enabled for the current runlevel -void stationary_run_level_enable(bool allow); - -//! If the stationary module is enabled and currently in stationary mode, then -//! we are put into a normal state. Call this if the system is about to do something that will -//! probably require user interaction, like an alarm going off. -void stationary_wake_up(void); - -//! Called by our event service system when there is a battery connection change -void stationary_handle_battery_connection_change_event(void); diff --git a/src/fw/services/normal/timeline/actions_endpoint.h b/src/fw/services/normal/timeline/actions_endpoint.h deleted file mode 100644 index 8129a6fc99..0000000000 --- a/src/fw/services/normal/timeline/actions_endpoint.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "attribute.h" -#include "item.h" - -#include "services/common/comm_session/session.h" -#include "util/uuid.h" - -#include -#include - -//! Sends a request to the phone asking it to invoke an action -//! @param id UUID of the pin/notification -//! @param type Type of the pin/notification -//! @param action_id The id of the action that is being invoked -//! @param attributes The list of attributes -//! @param do_async True = perform send on KernelBG, False = perform send on current task -void timeline_action_endpoint_invoke_action(const Uuid *id, TimelineItemActionType type, - uint8_t action_id, const AttributeList *attributes, - bool do_async); - -//! Handles messages from the phone sent to the timeline action endpoint -void timeline_action_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, - size_t length); diff --git a/src/fw/services/normal/timeline/alarm_layout.c b/src/fw/services/normal/timeline/alarm_layout.c deleted file mode 100644 index d217407e0a..0000000000 --- a/src/fw/services/normal/timeline/alarm_layout.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "alarm_layout.h" -#include "timeline_layout.h" - -#include "applib/fonts/fonts.h" -#include "applib/graphics/gtypes.h" -#include "applib/graphics/text.h" -#include "applib/ui/ui.h" -#include "drivers/rtc.h" -#include "font_resource_keys.auto.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/kernel_ui.h" -#include "process_state/app_state/app_state.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/alarms/alarm.h" -#include "system/logging.h" -#include "system/hexdump.h" -#include "util/size.h" -#include "util/string.h" - -#if !TINTIN_FORCE_FIT -////////////////////////////////////////// -// Card Mode -////////////////////////////////////////// - -#define CARD_MARGIN_TOP PBL_IF_RECT_ELSE(3, 10) - -static void prv_until_time_update(const LayoutLayer *layout_ref, - const LayoutNodeTextDynamicConfig *config, char *buffer, - bool render) { - const TimelineLayout *layout = (TimelineLayout *)layout_ref; - const int max_relative_hours = 24; // show up to "in 24 hours" - clock_get_until_time_without_fulltime(buffer, config->buffer_size, layout->info->timestamp, - max_relative_hours); -} - -T_STATIC void prv_get_subtitle_from_attributes(AttributeList *attributes, char *buffer, - size_t buffer_size, const void *i18n_owner) { - const char *subtitle_string = NULL; - // We only all-caps the subtitle in the card view on rectangular displays - bool all_caps_desired = PBL_IF_RECT_ELSE(true, false); - bool need_to_all_caps_string = false; - - // First, try to extract an AlarmKind from the pin to request a string with the desired - // capitalization - Attribute *alarm_kind_attribute = attribute_find(attributes, AttributeIdAlarmKind); - if (alarm_kind_attribute) { - const AlarmKind alarm_kind = (AlarmKind)alarm_kind_attribute->uint8; - subtitle_string = i18n_get(alarm_get_string_for_kind(alarm_kind, all_caps_desired), i18n_owner); - } else { - // Otherwise, fallback to just using the subtitle in the pin - subtitle_string = attribute_get_string(attributes, AttributeIdSubtitle, ""); - need_to_all_caps_string = all_caps_desired; - } - - strncpy(buffer, subtitle_string, buffer_size); - buffer[buffer_size - 1] = '\0'; - - if (need_to_all_caps_string) { - toupper_str(buffer); - } -} - -static void prv_subtitle_update(const LayoutLayer *layout, - const LayoutNodeTextDynamicConfig *config, char *buffer, - bool render) { - prv_get_subtitle_from_attributes(layout->attributes, buffer, config->buffer_size, layout); -} - -static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { - static const LayoutNodeTextDynamicConfig s_title_config = { - .text.extent.node.type = LayoutNodeType_TextDynamic, - .update = prv_until_time_update, - .buffer_size = TIME_STRING_REQUIRED_LENGTH, - .text.font_key = FONT_KEY_GOTHIC_18_BOLD, - .text.alignment = LayoutTextAlignment_Center, - .text.extent.margin.h = PBL_IF_RECT_ELSE(2, 0), // title margin height - }; - static const LayoutNodeTextDynamicConfig s_time_config = { - .text.extent.node.type = LayoutNodeType_TextDynamic, - .update = timeline_layout_time_text_update, - .buffer_size = TIME_STRING_REQUIRED_LENGTH, - .text.font_key = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM, - .text.alignment = LayoutTextAlignment_Center, - .text.extent.margin.h = PBL_IF_RECT_ELSE(9, 1), // time margin height - }; - static const LayoutNodeExtentConfig s_icon_config = { - .node.type = LayoutNodeType_TimelineIcon, - .margin.h = PBL_IF_RECT_ELSE(3, 1), // icon margin height - }; - static const LayoutNodeTextDynamicConfig s_subtitle_config = { - .text.extent.node.type = LayoutNodeType_TextDynamic, - .update = prv_subtitle_update, - .buffer_size = TIME_STRING_REQUIRED_LENGTH, - .text.font_key = FONT_KEY_GOTHIC_18_BOLD, - .text.alignment = LayoutTextAlignment_Center, - }; - static const LayoutNodeConfig * const s_vertical_config_nodes[] = { - PBL_IF_RECT_ELSE(&s_title_config.text.extent.node, &s_icon_config.node), - PBL_IF_RECT_ELSE(&s_time_config.text.extent.node, &s_title_config.text.extent.node), - PBL_IF_RECT_ELSE(&s_icon_config.node, &s_time_config.text.extent.node), - &s_subtitle_config.text.extent.node, - }; - static const LayoutNodeVerticalConfig s_vertical_config = { - .container.extent.node.type = LayoutNodeType_Vertical, - .container.num_nodes = ARRAY_LENGTH(s_vertical_config_nodes), - .container.nodes = (LayoutNodeConfig **)&s_vertical_config_nodes, - .container.extent.offset.y = CARD_MARGIN_TOP, - .container.extent.margin.h = CARD_MARGIN_TOP, - }; - - return layout_create_text_node_from_config(&timeline_layout->layout_layer, - &s_vertical_config.container.extent.node); -} - -////////////////////////////////////////// -// LayoutLayer API -////////////////////////////////////////// - -bool alarm_layout_verify(bool existing_attributes[]) { - return (existing_attributes[AttributeIdTitle] && existing_attributes[AttributeIdSubtitle]); -} - -LayoutLayer *alarm_layout_create(const LayoutLayerConfig *config) { - AlarmLayout *layout = task_zalloc_check(sizeof(AlarmLayout)); - - static const TimelineLayoutImpl s_timeline_layout_impl = { - .attributes = { AttributeIdTitle, AttributeIdSubtitle }, - .default_colors = { { .argb = GColorBlackARGB8 }, - { .argb = GColorClearARGB8 }, - { .argb = GColorJaegerGreenARGB8 } }, - .default_icon = TIMELINE_RESOURCE_ALARM_CLOCK, - .card_icon_align = GAlignCenter, - .card_icon_size = TimelineResourceSizeSmall, - .card_view_constructor = prv_card_view_constructor, - }; - - timeline_layout_init((TimelineLayout *)layout, config, &s_timeline_layout_impl); - - return (LayoutLayer *)layout; -} -#else -LayoutLayer *alarm_layout_create(const LayoutLayerConfig *config) { return NULL; } - -bool alarm_layout_verify(bool existing_attributes[]) { return false; } -#endif diff --git a/src/fw/services/normal/timeline/alarm_layout.h b/src/fw/services/normal/timeline/alarm_layout.h deleted file mode 100644 index 598e58d2d6..0000000000 --- a/src/fw/services/normal/timeline/alarm_layout.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "item.h" -#include "layout_layer.h" -#include "timeline_layout.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" - -typedef struct { - TimelineLayout timeline_layout; -} AlarmLayout; - -LayoutLayer *alarm_layout_create(const LayoutLayerConfig *config); - -bool alarm_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/attribute_private.h b/src/fw/services/normal/timeline/attribute_private.h deleted file mode 100644 index ab0f5ba7d8..0000000000 --- a/src/fw/services/normal/timeline/attribute_private.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" - -#include - -typedef struct PACKED { - uint8_t id; - uint16_t length; -} SerializedAttributeHeader; diff --git a/src/fw/services/normal/timeline/calendar.c b/src/fw/services/normal/timeline/calendar.c deleted file mode 100644 index fabf8cec2e..0000000000 --- a/src/fw/services/normal/timeline/calendar.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "calendar.h" - -#include "drivers/rtc.h" -#include "kernel/event_loop.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "services/common/system_task.h" -#include "services/normal/blob_db/pin_db.h" -#include "system/logging.h" -#include "system/status_codes.h" -#include "util/time/time.h" - -static bool s_event_ongoing = false; - -static void prv_put_calendar_event(void) { - PebbleEvent event = { - .type = PEBBLE_CALENDAR_EVENT, - .calendar = { - .is_event_ongoing = s_event_ongoing, - }, - }; - event_put(&event); -} - -static bool prv_calendar_filter(SerializedTimelineItemHeader *header, void **context) { - const time_t now = rtc_get_time(); - return ((header->common.layout == LayoutIdCalendar) && - !timeline_event_is_all_day(&header->common) && - (timeline_event_is_ongoing(now, header->common.timestamp, header->common.duration) || - timeline_event_starts_within(&header->common, now, 0, TIMELINE_EVENT_DELTA_INFINITE))); -} - -static uint32_t prv_calendar_update(TimelineItem *item, void **context) { - s_event_ongoing = item ? timeline_event_is_ongoing(rtc_get_time(), item->header.timestamp, - item->header.duration) - : false; - prv_put_calendar_event(); - return 0; -} - -const TimelineEventImpl *calendar_get_event_service(void) { - static const TimelineEventImpl s_event_impl = { - .filter = prv_calendar_filter, - .update = prv_calendar_update, - }; - return &s_event_impl; -} - -bool calendar_event_is_ongoing(void) { - return s_event_ongoing; -} diff --git a/src/fw/services/normal/timeline/calendar.h b/src/fw/services/normal/timeline/calendar.h deleted file mode 100644 index 85e9723000..0000000000 --- a/src/fw/services/normal/timeline/calendar.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "event.h" - -//! This module puts events that report the current state of calendar events. -//! The states are: -//! - "no calendar events ongoing" -//! - "one or more calendar events ongoing" -//! Not every calendar event start / stop produces an event, but every transition is guarenteed -//! to put an event. - - -const TimelineEventImpl *calendar_get_event_service(void); - -//! Used to determine if there is currently an event going on, used for Smart DND -bool calendar_event_is_ongoing(void); - -#if UNITTEST -#include "services/common/new_timer/new_timer.h" -TimerID get_calendar_timer_id(void); -void set_calendar_timer_id(TimerID id); -#endif diff --git a/src/fw/services/normal/timeline/calendar_layout.h b/src/fw/services/normal/timeline/calendar_layout.h deleted file mode 100644 index c2ef5ae00e..0000000000 --- a/src/fw/services/normal/timeline/calendar_layout.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "item.h" -#include "layout_layer.h" -#include "timeline_layout.h" - -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/ui/ui.h" - -typedef enum { - CalendarRecurringTypeNone = 0, - CalendarRecurringTypeRecurring, -} CalendarRecurringType; - -typedef struct { - TimelineLayout timeline_layout; - TextLayer date_layer; - char day_date_buffer[TIME_STRING_DAY_DATE_LENGTH]; -} CalendarLayout; - -LayoutLayer *calendar_layout_create(const LayoutLayerConfig *config); - -bool calendar_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/calendar_layout_resources.c b/src/fw/services/normal/timeline/calendar_layout_resources.c deleted file mode 100644 index 045cd9902e..0000000000 --- a/src/fw/services/normal/timeline/calendar_layout_resources.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "calendar_layout_resources.h" - -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/gdraw_command_private.h" -#include "util/size.h" - -// FIXME: PBL-28898 GPath algorithm requires strange coordinates for pixel perfection -// The paths here result in pixel perfect icons with the current gpath filled algorithm, -// but when gpath filled is fixed to correctly match its coordinates, these MUST be updated. - -CalendarStartIcon g_calendar_start_icon = { - .image = { - .version = 1, - .size = { 9, 9 }, - .command_list = { - .num_commands = 1, - }, - }, - .command = { - .type = GDrawCommandTypePath, - .fill_color = { .argb = GColorBlackARGB8 }, - .num_points = STATIC_ARRAY_LENGTH(GPoint, START_ICON_POINTS), - }, - .points = START_ICON_POINTS, -}; - -CalendarEndIcon g_calendar_end_icon = { - .image = { - .version = 1, - .size = { 9, 9 }, - .command_list = { - .num_commands = 1, - }, - }, - .command = { - .type = GDrawCommandTypePath, - .fill_color = { .argb = GColorBlackARGB8 }, - .num_points = STATIC_ARRAY_LENGTH(GPoint, END_ICON_POINTS), - }, - .points = END_ICON_POINTS, -}; diff --git a/src/fw/services/normal/timeline/calendar_layout_resources.h b/src/fw/services/normal/timeline/calendar_layout_resources.h deleted file mode 100644 index c3f677d9bf..0000000000 --- a/src/fw/services/normal/timeline/calendar_layout_resources.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gdraw_command_image.h" -#include "applib/graphics/gdraw_command_private.h" -#include "util/attributes.h" -#include "util/size.h" - -#define START_ICON_POINTS { { 0, -2 }, { 9, 4 }, { 0, 10 } } - -typedef struct PACKED { - struct { - GDrawCommandImage image; - }; - GDrawCommand command; - GPoint points[STATIC_ARRAY_LENGTH(GPoint, START_ICON_POINTS)]; -} CalendarStartIcon; - -extern CalendarStartIcon g_calendar_start_icon; - -#define END_ICON_POINTS { { 0, 0 }, { 10, 0 }, { 10, 8 }, { 0, 8 } } - -typedef struct PACKED { - struct { - GDrawCommandImage image; - }; - GDrawCommand command; - GPoint points[STATIC_ARRAY_LENGTH(GPoint, END_ICON_POINTS)]; -} CalendarEndIcon; - -extern CalendarEndIcon g_calendar_end_icon; diff --git a/src/fw/services/normal/timeline/generic_layout.h b/src/fw/services/normal/timeline/generic_layout.h deleted file mode 100644 index 68f6387c10..0000000000 --- a/src/fw/services/normal/timeline/generic_layout.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "item.h" -#include "layout_layer.h" -#include "timeline_layout.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/ui/bitmap_layer.h" - -typedef struct { - TimelineLayout timeline_layout; -} GenericLayout; - -LayoutLayer *generic_layout_create(const LayoutLayerConfig *config); - -bool generic_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/health_layout.c b/src/fw/services/normal/timeline/health_layout.c deleted file mode 100644 index e13759a49e..0000000000 --- a/src/fw/services/normal/timeline/health_layout.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "health_layout.h" -#include "timeline_layout.h" - -#include "kernel/pbl_malloc.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/activity/health_util.h" -#include "util/size.h" - -#include - -#if !PLATFORM_TINTIN -////////////////////////////////////////// -// Card Mode -////////////////////////////////////////// - -#define CARD_MARGIN_TOP PBL_IF_RECT_ELSE(0, 5) -#define CARD_MARGIN_BOTTOM PBL_IF_RECT_ELSE(11, 0) - -static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { - const LayoutNodeExtentConfig s_metrics_config = { - .node.type = LayoutNodeType_TimelineMetrics, - .offset.y = CARD_MARGIN_TOP, - .margin.h = CARD_MARGIN_TOP + CARD_MARGIN_BOTTOM, - }; - return layout_create_text_node_from_config(&timeline_layout->layout_layer, - &s_metrics_config.node); -} - -////////////////////////////////////////// -// LayoutLayer API -////////////////////////////////////////// - -bool health_layout_verify(bool existing_attributes[]) { - return existing_attributes[AttributeIdTitle]; -} - -LayoutLayer *health_layout_create(const LayoutLayerConfig *config) { - HealthLayout *layout = task_zalloc_check(sizeof(HealthLayout)); - - static const TimelineLayoutImpl s_timeline_layout_impl = { - .attributes = { AttributeIdTitle, AttributeIdSubtitle }, - .default_colors = { { .argb = GColorBlackARGB8 }, - { .argb = GColorWhiteARGB8 }, - { .argb = GColorSunsetOrangeARGB8 } }, - .default_icon = TIMELINE_RESOURCE_ACTIVITY, - .card_icon_align = PBL_IF_ROUND_ELSE(GAlignCenter, GAlignLeft), - .card_icon_size = TimelineResourceSizeTiny, - .card_view_constructor = prv_card_view_constructor, - }; - - timeline_layout_init((TimelineLayout *)layout, config, &s_timeline_layout_impl); - - return (LayoutLayer *)layout; -} -#endif diff --git a/src/fw/services/normal/timeline/health_layout.h b/src/fw/services/normal/timeline/health_layout.h deleted file mode 100644 index 77648d0220..0000000000 --- a/src/fw/services/normal/timeline/health_layout.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "item.h" -#include "layout_layer.h" -#include "timeline_layout.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "util/attributes.h" - -#define HEALTH_METRIC_BUFFER_LENGTH 128 - -//! The different types of health cards the app shows -typedef enum HealthCardType { - HealthCardType_Activity = 0, - HealthCardType_Sleep, - HealthCardTypeCount -} HealthCardType; - -//! Shared with insights to allow the app to launch into the appropriate card -typedef struct PACKED HealthLaunchArgs { - union { - struct { - HealthCardType card_type:8; //!< Tells us if we need to launch into an activity or sleep card - }; - uint32_t args; - }; -} HealthLaunchArgs; - -typedef enum { - ActivitySessionMetric_Duration = 0, - ActivitySessionMetric_Calories, - ActivitySessionMetric_Distance, - ActivitySessionMetric_Pace, - ActivitySessionMetricCount, -} ActivitySessionMetric; - -typedef struct { - KinoLayer *icon_layer; - const char *value_fmt; -} MetricData; - -typedef struct { - TimelineLayout timeline_layout; - MetricData metric_data[ActivitySessionMetricCount]; -} HealthLayout; - -LayoutLayer *health_layout_create(const LayoutLayerConfig *config); - -bool health_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/metricgroup.c b/src/fw/services/normal/timeline/metricgroup.c deleted file mode 100644 index 167a614fa7..0000000000 --- a/src/fw/services/normal/timeline/metricgroup.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "metricgroup.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/attribute.h" - -MetricGroup *metric_group_create(int max_num_items, size_t max_item_string_size) { - const size_t max_list_size = StringListSize(max_num_items, max_item_string_size); - MetricGroup *metric = task_zalloc_check(sizeof(MetricGroup)); - *metric = (MetricGroup) { - .names = task_zalloc_check(max_list_size), - .values = task_zalloc_check(max_list_size), - .icons = task_zalloc_check(Uint32ListSize(max_num_items)), - .max_num_items = max_num_items, - .max_item_string_size = max_item_string_size, - }; - return metric; -} - -void metric_group_destroy(MetricGroup *metric_group) { - if (!metric_group) { - return; - } - task_free(metric_group->names); - task_free(metric_group->values); - task_free(metric_group->icons); - task_free(metric_group); -} - -bool metric_group_add_item(MetricGroup *metric_group, const char *name_i18n, const char *value, - TimelineResourceId icon, void *i18n_owner) { - if (metric_group->num_items >= metric_group->max_num_items) { - return false; - } - const size_t max_list_size = StringListSize(metric_group->max_num_items, - metric_group->max_item_string_size); - string_list_add_string(metric_group->names, max_list_size, i18n_get(name_i18n, i18n_owner), - metric_group->max_item_string_size); - string_list_add_string(metric_group->values, max_list_size, value, - metric_group->max_item_string_size); - metric_group->icons->values[metric_group->num_items++] = icon; - metric_group->icons->num_values = metric_group->num_items; - return true; -} diff --git a/src/fw/services/normal/timeline/metricgroup.h b/src/fw/services/normal/timeline/metricgroup.h deleted file mode 100644 index 2f56f6a4af..0000000000 --- a/src/fw/services/normal/timeline/metricgroup.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "attribute.h" -#include "timeline_resources.h" - -typedef struct MetricGroup { - StringList *names; - StringList *values; - Uint32List *icons; - int num_items; - int max_num_items; - size_t max_item_string_size; -} MetricGroup; - -//! Create a metric group -//! @param max_num_items max number of items able to be added to the group -//! @param max_item_string_size max length of any string in the group, name and value -//! @return newly allocated MetricGroup -MetricGroup *metric_group_create(int max_num_items, size_t max_item_string_size); - -//! Destroy a metric group -//! @param metric_group MetricGroup to destroy -void metric_group_destroy(MetricGroup *metric_group); - -//! Adds an item to a metric group -//! @param metric_group MetricGroup to add an item to -//! @param name_i18n i18n key of the name string -//! @param value value field string -//! @param icon TimelineResourceId icon id -//! @param i18n_owner i18n owner to use -//! @return true if the item was added, false otherwise -bool metric_group_add_item(MetricGroup *metric_group, const char *name_i18n, const char *value, - TimelineResourceId icon, void *i18n_owner); diff --git a/src/fw/services/normal/timeline/notification_jumboji_table.h b/src/fw/services/normal/timeline/notification_jumboji_table.h deleted file mode 100644 index 9385ce00ae..0000000000 --- a/src/fw/services/normal/timeline/notification_jumboji_table.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/fonts/codepoint.h" -#include "resource/resource_ids.auto.h" - -#include - -typedef struct { - Codepoint codepoint; - ResourceId resource_id; -#if UNITTEST - const char *string; - const char *resource_name; -#endif -} EmojiEntry; - -#if UNITTEST -#define EMOJI_ENTRY(string, codepoint, resource_id) \ - { codepoint, resource_id, string, #resource_id } -#else -#define EMOJI_ENTRY(string, codepoint, resource_id) \ - { codepoint, resource_id } -#endif - -// Codepoint sorted table of supported Jumboji -#define JUMBOJI_TABLE(ENTRY) { \ - ENTRY("♥️", 0x02665, RESOURCE_ID_EMOJI_HEART_LARGE), \ - ENTRY("❤️", 0x02764, RESOURCE_ID_EMOJI_HEART_LARGE), \ - ENTRY("👍", 0x1f44d, RESOURCE_ID_EMOJI_THUMBS_UP_LARGE), \ - ENTRY("💙", 0x1f499, RESOURCE_ID_EMOJI_HEART_LARGE), \ - ENTRY("💚", 0x1f49a, RESOURCE_ID_EMOJI_HEART_LARGE), \ - ENTRY("💛", 0x1f49b, RESOURCE_ID_EMOJI_HEART_LARGE), \ - ENTRY("💜", 0x1f49c, RESOURCE_ID_EMOJI_HEART_LARGE), \ - ENTRY("😀", 0x1f600, RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE), \ - ENTRY("😁", 0x1f601, RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE), \ - ENTRY("😂", 0x1f602, RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE), \ - ENTRY("😃", 0x1f603, RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE), \ - ENTRY("😄", 0x1f604, RESOURCE_ID_EMOJI_BIG_SMILE_LARGE), \ - ENTRY("😉", 0x1f609, RESOURCE_ID_EMOJI_WINK_LARGE), \ - ENTRY("😊", 0x1f60a, RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE), \ - ENTRY("😍", 0x1f60d, RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE), \ - ENTRY("😘", 0x1f618, RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE), \ - ENTRY("😜", 0x1f61c, RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE), \ - ENTRY("😞", 0x1f61e, RESOURCE_ID_EMOJI_SAD_LARGE), \ - ENTRY("😟", 0x1f61f, RESOURCE_ID_EMOJI_SAD_LARGE), \ -} diff --git a/src/fw/services/normal/timeline/peek.h b/src/fw/services/normal/timeline/peek.h deleted file mode 100644 index 3a62fb6897..0000000000 --- a/src/fw/services/normal/timeline/peek.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "event.h" - -//! Default time at which the Timeline Peek will show an event before it starts. -//! This setting is user configurable. -#define TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S (10 * SECONDS_PER_MINUTE) - -//! Time at which the Timeline Peek will hide an event after it starts. -//! This settings is not user configurable. -#define TIMELINE_PEEK_HIDE_AFTER_TIME_S (10 * SECONDS_PER_MINUTE) - -//! TimelinePeek event subtypes which signify the relation between now and the event timestamp -typedef enum TimelinePeekTimeType { - TimelinePeekTimeType_None = 0, - //! The event is next, but not immediately, specifically > show_before_time_s - TimelinePeekTimeType_SomeTimeNext, - //! The event will start almost immediately, specifically <= show_before_time_s, and should be - //! presented to the user - TimelinePeekTimeType_ShowWillStart, - //! The event has started moments ago, specifically < TIMELINE_PEEK_HIDE_AFTER_TIME_S, - //! and should be presented to the user - TimelinePeekTimeType_ShowStarted, - //! The event is ongoing and will end and has already elapsed >= TIMELINE_PEEK_HIDE_AFTER_TIME_S - TimelinePeekTimeType_WillEnd, -} TimelinePeekTimeType; - -const TimelineEventImpl *timeline_peek_get_event_service(void); - -//! Sets the show before timing of timeline peek. -//! @param before_time_s The amount of time before event start the peek should be visible. -void timeline_peek_set_show_before_time(unsigned int before_time_s); diff --git a/src/fw/services/normal/timeline/reminders.c b/src/fw/services/normal/timeline/reminders.c deleted file mode 100644 index 0593c3827c..0000000000 --- a/src/fw/services/normal/timeline/reminders.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "reminders.h" - -#include "drivers/rtc.h" -#include "kernel/event_loop.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "kernel/pebble_tasks.h" -#include "services/common/system_task.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/timeline/item.h" -#include "system/logging.h" - -#include - -#define INVALID_SNOOZE_DELAY 0 -#define HALF_SNOOZE_END_MARK 30 // Seconds -#define CONSTANT_SNOOZE_DELAY (10 * SECONDS_PER_MINUTE) // Seconds -#define CONSTANT_SNOOZE_END_MARK (48 * MINUTES_PER_HOUR * SECONDS_PER_MINUTE) // Seconds - -static TimerID s_reminder_timer; - -// this reminder should stay here so the timer callback has something to refer to -static ReminderId s_next_reminder_id; - -bool reminders_mark_has_reminded(ReminderId *reminder_id); - -static void prv_put_reminder_event(ReminderId *reminder_id, ReminderEventType type) { - Uuid *removed_id = kernel_malloc(sizeof(Uuid)); - if (!removed_id) { - return; - } - - *removed_id = *reminder_id; - PebbleEvent event = { - .type = PEBBLE_REMINDER_EVENT, - .reminder = { - .type = type, - .reminder_id = removed_id, - } - }; - event_put(&event); -} - -void reminders_handle_reminder_updated(const Uuid *reminder_id) { - prv_put_reminder_event((ReminderId *)reminder_id, ReminderUpdated); -} - -void reminders_handle_reminder_removed(const Uuid *reminder_id) { - prv_put_reminder_event((ReminderId *)reminder_id, ReminderRemoved); -} - -static void prv_trigger_reminder_system_task_callback(void *data) { - ReminderId *item_id = (ReminderId *)data; - - // Mark that we are about to display the reminder - if (!reminders_mark_has_reminded(item_id)) { - return; - } - - prv_put_reminder_event(item_id, ReminderTriggered); - reminders_update_timer(); -} - -static void prv_new_timer_callback(void *data) { - system_task_add_callback(prv_trigger_reminder_system_task_callback, data); -} - -static status_t prv_set_timer(Reminder *item) { - time_t now = rtc_get_time(); - uint32_t timeout_ms; - if (item->header.timestamp <= now) { - timeout_ms = 0; - } else { - timeout_ms = 1000 * (item->header.timestamp - now); - } - s_next_reminder_id = item->header.id; - if (new_timer_start(s_reminder_timer, timeout_ms, prv_new_timer_callback, - &s_next_reminder_id, 0)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Set timer for %"PRIu32, timeout_ms); - return S_SUCCESS; - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Could not set timer."); - return E_ERROR; - } -} - -status_t reminders_update_timer(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempting to update timer."); - if (!new_timer_stop(s_reminder_timer)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Updated timer while callback running."); - return S_SUCCESS; - } - - TimelineItem item = {{{0}}}; - status_t rv = reminder_db_next_item_header(&item); - if (rv == S_NO_MORE_ITEMS) { - PBL_LOG(LOG_LEVEL_DEBUG, "No more reminders to add to queue."); - return S_SUCCESS; - } else if (rv) { - return rv; - } - - rv = prv_set_timer(&item); - if (rv) { - return E_ERROR; - } - return S_SUCCESS; -} - -status_t reminders_insert(Reminder *reminder) { - status_t rv = reminder_db_insert_item(reminder); - return rv; -} - -status_t reminders_init(void) { - if (s_reminder_timer) { - new_timer_delete(s_reminder_timer); - } - s_reminder_timer = new_timer_create(); - if (s_reminder_timer == TIMER_INVALID_ID) { - return E_ERROR; - } else { - return reminders_update_timer(); - } -} - -status_t reminders_delete(ReminderId *reminder_id) { - return reminder_db_delete_item(reminder_id, true /* send_event */); -} - -T_STATIC uint32_t prv_calculate_snooze_delay(TimelineItem *item) { - time_t current_time_utc = rtc_get_time(); - time_t reminder_time_utc = item->header.timestamp; - if (current_time_utc <= reminder_time_utc) { - return INVALID_SNOOZE_DELAY; - } - - uint32_t snooze_delay; - - // Get parent pin - const TimelineItemId *parent_id = &item->header.parent_id; - TimelineItem parent_item; - status_t status = pin_db_get(parent_id, &parent_item); - if (status != S_SUCCESS) { - return INVALID_SNOOZE_DELAY; - } - - // Snooze logic: - // If current_time is more than HALF_SNOOZE_END_MARK before event_time, snooze for half the - // remaining time until the event. - // If current_time is less than HALF_SNOOZE_END_MARK before event_time, and not more than - // CONSTANT_SNOOZE_END_MARK after event_time, snooze for CONSTANT_SNOOZE_DELAY. - // If current_time is more than CONSTANT_SNOOZE_END_MARK after event_time, don't snooze. - time_t event_time_utc = parent_item.header.timestamp; - if (event_time_utc > current_time_utc && - event_time_utc - current_time_utc > HALF_SNOOZE_END_MARK) { - // Half-time snooze - snooze_delay = (event_time_utc - reminder_time_utc) / 2; - } else if (current_time_utc > event_time_utc && - current_time_utc - event_time_utc > CONSTANT_SNOOZE_END_MARK) { - // Stop snoozing - snooze_delay = INVALID_SNOOZE_DELAY; - } else { - // Constant-time snooze - snooze_delay = CONSTANT_SNOOZE_DELAY; - } - - timeline_item_free_allocated_buffer(&parent_item); - return snooze_delay; -} - -bool reminders_can_snooze(Reminder *reminder) { - return (prv_calculate_snooze_delay((TimelineItem *)reminder) > 0); -} - -status_t reminders_snooze(Reminder *reminder) { - uint32_t snooze_delay = prv_calculate_snooze_delay((TimelineItem *)reminder); - if (snooze_delay == 0) { - return E_INVALID_OPERATION; - } - - // Modify reminder timestamp - TimelineItem *item = (TimelineItem*) reminder; - item->header.timestamp = rtc_get_time() + (time_t) snooze_delay; - - // Unset the reminded status - item->header.reminded = false; - - // Reinsert the reminder - return reminders_insert(reminder); -} - -// only used for tests -TimerID get_reminder_timer_id(void) { - return s_reminder_timer; -} - -bool reminders_mark_has_reminded(ReminderId *reminder_id) { - status_t rv = reminder_db_set_status_bits(reminder_id, TimelineItemStatusReminded); - return (rv == S_SUCCESS); -} diff --git a/src/fw/services/normal/timeline/reminders.h b/src/fw/services/normal/timeline/reminders.h deleted file mode 100644 index d357d0e433..0000000000 --- a/src/fw/services/normal/timeline/reminders.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "system/status_codes.h" -#include "services/normal/timeline/item.h" -#include "services/common/new_timer/new_timer.h" - -typedef TimelineItem Reminder; -typedef TimelineItemId ReminderId; - -//! Set the reminder timer to the next stored reminder chronologically -//! @return S_SUCCESS or appropriate error -status_t reminders_update_timer(void); - -//! Insert a reminder to be popped up at a certain time -//! @param reminder pointer to the reminder to be inserted -//! @return S_SUCCESS or appropriate error -status_t reminders_insert(Reminder *reminder); - -//! Initialize the reminders so they can be activated on the watch -//! @return S_SUCCESS or appropriate error -status_t reminders_init(void); - -//! Delete a reminder -//! @param reminder_id pointer to an Id of the reminder to be deleted -//! @return S_SUCCESS or appropriate error -status_t reminders_delete(ReminderId *reminder_id); - -//! @return True if the reminder can snooze for a non-zero amount of time, false otherwise. -bool reminders_can_snooze(Reminder *reminder); - -//! Snooze a reminder -//! @param reminder Pointer to the reminder to snooze -//! @return S_SUCCESS, E_INVALID_OPERATION if cannot snooze, or some other error otherwise -status_t reminders_snooze(Reminder *reminder); - -//! Creates an event to alert the system that a reminder has been removed -//! @param reminder_id Pointer to the uuid of the removed reminder -void reminders_handle_reminder_removed(const Uuid *reminder_id); - -//! Creates an event to alert the system that a triggered reminder has changed -//! @param reminder_id Pointer to the uuid of the updated reminder -void reminders_handle_reminder_updated(const Uuid *reminder_id); diff --git a/src/fw/services/normal/timeline/sports_layout.h b/src/fw/services/normal/timeline/sports_layout.h deleted file mode 100644 index 6938abd317..0000000000 --- a/src/fw/services/normal/timeline/sports_layout.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "item.h" -#include "layout_layer.h" -#include "timeline_layout.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "applib/ui/bitmap_layer.h" - -typedef enum { - GameStatePreGame = 0, - GameStateInGame, - GameStatePostGame -} GameState; - -typedef struct { - TimelineLayout timeline_layout; - GameState state; -} SportsLayout; - -LayoutLayer *sports_layout_create(const LayoutLayerConfig *config); - -bool sports_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timeline/timeline.c b/src/fw/services/normal/timeline/timeline.c deleted file mode 100644 index 7b7d164e81..0000000000 --- a/src/fw/services/normal/timeline/timeline.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline.h" - -#include "util/uuid.h" -#include "applib/ui/status_bar_layer.h" -#include "apps/system_app_ids.h" -#include "apps/system_apps/timeline/timeline.h" -#include "apps/system_apps/timeline/pin_window.h" -#include "comm/ble/kernel_le_client/ancs/ancs.h" -#include "comm/ble/kernel_le_client/ancs/ancs_types.h" -#include "drivers/rtc.h" -#include "kernel/event_loop.h" -#include "kernel/pbl_malloc.h" -#include "kernel/ui/modals/modal_manager.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/phone_call_util.h" -#include "services/normal/timeline/actions_endpoint.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/list.h" -#include "util/math.h" -#include "util/order.h" -#include "util/size.h" -#include "util/time/time.h" - -struct TimelineNode { - ListNode node; - int index; - Uuid id; - time_t timestamp; - uint16_t duration; - bool all_day; -}; - -static uint32_t i18n_key; - -#define TIMELINE_FUTURE_WINDOW (3 * SECONDS_PER_DAY) -#define TIMELINE_PAST_WINDOW (2 * SECONDS_PER_DAY) - -static bool s_bulk_action_mode = false; - -///////////////////////// -// Timeline Iterator -///////////////////////// - -// Order of events in timeline: -// * All day events appear first -// * (All day events should be timestamped at midnight, the first second in the day) -// * Order all other events by time -// * For concurrent events: order by duration (shortest to longest), then by (TODO) A-Z -// * Events that occur now appear both in timeline past and timeline future until event ends -static int prv_time_comparator(void *a, void *b) { - TimelineNode *node_a = (TimelineNode *)a; - TimelineNode *node_b = (TimelineNode *)b; - if (node_b->timestamp == node_a->timestamp) { - if (node_b->all_day) { - return -1; - } else if (node_a->all_day) { - return 1; - } else { - return (node_b->duration - node_a->duration); - } - } else { - return (node_b->timestamp - node_a->timestamp); - } -} - -static bool prv_filter(ListNode *found_node, void *data) { - TimelineNode *node = (TimelineNode *)found_node; - Uuid *uuid = (Uuid *)data; - return uuid_equal(&node->id, uuid); -} - -static TimelineNode *prv_find_by_uuid(TimelineNode *head, Uuid *uuid) { - TimelineNode *node = (TimelineNode *)list_find((ListNode *)head, prv_filter, uuid); - return node; -} - -static bool prv_is_in_window(time_t node_timestamp, uint16_t node_duration, time_t timestamp) { - time_t future_window = time_util_get_midnight_of(timestamp + TIMELINE_FUTURE_WINDOW); - time_t past_window = time_util_get_midnight_of(timestamp - TIMELINE_PAST_WINDOW); - time_t end_time = node_timestamp + (node_duration * SECONDS_PER_MINUTE); - time_t start_time = node_timestamp; - - return !(start_time >= future_window || end_time < past_window); -} - -static bool prv_show_event(TimelineNode *node, time_t timestamp, time_t midnight, - TimelineIterDirection direction, bool show_all_day_events) { - // hide events outside of the window - if (!prv_is_in_window(node->timestamp, node->duration, timestamp)) { - return false; - } - -#if CAPABILITY_HAS_CORE_NAVIGATION4 - // An event is in future until it ends - const time_t fudge_time = node->duration * SECONDS_PER_MINUTE; -#else - // An event is in future until either it ends or ten minutes passed, whichever happens first - const time_t fudge_time = MIN(node->duration, 10) * SECONDS_PER_MINUTE; -#endif - // deal with all day events - if (node->all_day && node->timestamp == midnight) { - return show_all_day_events; - } else if (direction == TimelineIterDirectionFuture) { - return (node->timestamp >= timestamp - fudge_time); - } else { // direction == TimelineIterDirectionPast - return (node->timestamp < timestamp - fudge_time); - } -} - -// All day events show up in future if no timed events have passed today, -// i.e. no events exist between midnight today and now -// iterate and figure out if we had a timed event pass today -static bool prv_should_show_all_day_events(TimelineNode *head, time_t now, time_t today_midnight, - TimelineIterDirection direction) { - TimelineNode *current = head; - // show in future / hide in past all day events unless we find a timed event - // between midnight and now - bool show = direction == TimelineIterDirectionFuture; - while (current) { - if (current->timestamp > now) { - break; - } - if (!current->all_day && (current->timestamp >= today_midnight)) { - show = !show; - break; - } - current = (TimelineNode *)current->node.next; - } - return show; -} - -static TimelineNode *prv_find_first_past(TimelineNode *head, time_t timestamp, - time_t today_midnight, bool show_all_day_events) { - TimelineNode *current = (TimelineNode *)list_get_tail((ListNode *)head); - while (current) { - if (prv_show_event(current, timestamp, today_midnight, TimelineIterDirectionPast, - show_all_day_events)) { - break; - } - current = (TimelineNode *)current->node.prev; - } - return current; -} - -static TimelineNode *prv_find_first_future(TimelineNode *head, time_t timestamp, - time_t today_midnight, bool show_all_day_events) { - TimelineNode *current = head; - while (current) { - if (prv_show_event(current, timestamp, today_midnight, TimelineIterDirectionFuture, - show_all_day_events)) { - break; - } - current = (TimelineNode *)current->node.next; - } - return current; -} - -static TimelineNode *prv_find_first(TimelineNode *head, TimelineIterDirection direction, - time_t timestamp, time_t today_midnight, bool show_all_day_events) { - if (direction == TimelineIterDirectionPast) { - return prv_find_first_past(head, timestamp, today_midnight, show_all_day_events); - } else { - return prv_find_first_future(head, timestamp, today_midnight, show_all_day_events); - } -} - -static void prv_remove_node(TimelineNode **head, TimelineNode *node) { - list_remove((ListNode *)node, (ListNode **)head, NULL); - task_free(node); -} - -static int prv_num_nodes_for_serialized_item(CommonTimelineItemHeader *header) { - int num_days; - if (header->all_day) { - num_days = header->duration ? (header->duration + MINUTES_PER_DAY - 1) / MINUTES_PER_DAY : 1; - } else { - // The span is the time between 0:00 on the first day of the event - // and 24:00 on the last day of the event - const time_t start_span = time_util_get_midnight_of(header->timestamp - SECONDS_PER_DAY + 1); - const time_t end_span = time_util_get_midnight_of(header->timestamp + header->duration * - SECONDS_PER_MINUTE - 1); - const time_t full_span = end_span - start_span; - num_days = full_span / SECONDS_PER_DAY; - } - return MAX(num_days, 1); -} - -static void prv_set_nodes(TimelineNode *nodes[], CommonTimelineItemHeader *header, int num_nodes) { - // Multiday events: - // first day: timestamp at beginning of event, duration for rest of the day - // middle days: all day events - // end days: event at end time - // - // single event: - // timestamp, duration are same as the original item - nodes[0]->timestamp = timeline_item_get_tz_timestamp(header); - const time_t midnight_first = time_util_get_midnight_of(nodes[0]->timestamp); - if (num_nodes == 1) { - nodes[0]->duration = header->duration; - } else { - // first item has correct timestamp, duration should make it last for the rest of the day - const time_t until_midnight = midnight_first + SECONDS_PER_DAY - header->timestamp; - nodes[0]->duration = until_midnight / SECONDS_PER_MINUTE; - - // last item at end of event, duration 0 - time_t endtime = header->timestamp + header->duration * SECONDS_PER_MINUTE; - nodes[num_nodes - 1]->timestamp = endtime; - nodes[num_nodes - 1]->duration = 0; - nodes[num_nodes - 1]->all_day = false; - } - nodes[0]->all_day = (nodes[0]->duration == MINUTES_PER_DAY && - nodes[0]->timestamp == midnight_first); - - // middle days are all day events - time_t midnight = time_util_get_midnight_of(header->timestamp); - for (int i = 1; i < num_nodes - 1; i++) { - midnight += SECONDS_PER_DAY; - nodes[i]->timestamp = midnight; - nodes[i]->duration = MINUTES_PER_DAY; - nodes[i]->all_day = true; - } -} - -static void prv_set_nodes_all_day(TimelineNode *nodes[], CommonTimelineItemHeader *header, - int num_nodes) { - // Multiday events: - // Each day is an all day event - - time_t midnight; - // iOS doesn't correctly send the timestamp at UTC midnight, rather it sends it in local time - if (header->timestamp % SECONDS_PER_DAY != 0) { - // NOT at UTC midnight, so presumably an iOS bug - midnight = time_util_get_midnight_of(header->timestamp); - } else { - midnight = timeline_item_get_tz_timestamp(header); - } - for (int i = 0; i < num_nodes; i++) { - nodes[i]->timestamp = midnight; - nodes[i]->duration = MINUTES_PER_DAY; - nodes[i]->all_day = true; - midnight += SECONDS_PER_DAY; - } -} - -static void prv_add_nodes_for_serialized_item(TimelineNode **list_head, - CommonTimelineItemHeader *header) { - int num_nodes = prv_num_nodes_for_serialized_item(header); - TimelineNode *nodes[num_nodes]; - - // copy UUID to all the nodes - for (int i = 0; i < num_nodes; i++) { - // each node requires its own malloc because each node must be individually free-able - nodes[i] = task_malloc_check(sizeof(TimelineNode)); - *(nodes[i]) = (TimelineNode){}; - nodes[i]->id = header->id; - } - - if (header->all_day) { - prv_set_nodes_all_day(nodes, header, num_nodes); - } else { - prv_set_nodes(nodes, header, num_nodes); - } - - for (int i = 0; i < num_nodes; i++) { - *list_head = (TimelineNode *)list_sorted_add((ListNode *)*list_head, (ListNode *)nodes[i], - prv_time_comparator, true); - } -} - -static bool prv_each(SettingsFile *file, SettingsRecordInfo *info, void *context) { - // check entry is valid - if (info->key_len != UUID_SIZE || info->val_len == 0) { - return true; // continue iteration - } - - TimelineNode **list_head = context; - - CommonTimelineItemHeader header; - // we don't care about the attributes here, so we don't allocate space for them - info->get_val(file, (uint8_t *)&header, sizeof(CommonTimelineItemHeader)); - // Flags & Status are stored inverted. - header.flags = ~header.flags; - header.status = ~header.status; - - prv_add_nodes_for_serialized_item(list_head, &header); - - return true; // continue iteration -} -static void prv_set_indices(TimelineNode *timeline) { - TimelineNode *node = timeline; - int index = 0; - while (node) { - node->index = index; - index++; - node = (TimelineNode *)list_get_next((ListNode *)node); - } -} - -static int prv_first_event_comparator(TimelineNode *new_node, TimelineNode *old_node, - TimelineIterDirection direction) { - const time_t now = rtc_get_time(); - const time_t midnight = time_util_get_midnight_of(now); - const bool show_all_day = false; - const bool show_new = prv_show_event(new_node, now, midnight, direction, show_all_day); - const bool show_old = prv_show_event(old_node, now, midnight, direction, show_all_day); - if (show_new != show_old) { - return (show_old ? 1 : 0) - (show_new ? 1 : 0); - } else { - return prv_time_comparator(old_node, new_node); - } -} - -static void prv_set_node_from_header(CommonTimelineItemHeader *header, TimelineNode *node_out) { - prv_set_nodes(&(TimelineNode *) { node_out }, header, 1 /* num_nodes */); -} - -int timeline_item_time_comparator(CommonTimelineItemHeader *new_common, - CommonTimelineItemHeader *old_common, - TimelineIterDirection direction) { - TimelineNode new_node; - TimelineNode old_node; - prv_set_node_from_header(new_common, &new_node); - prv_set_node_from_header(old_common, &old_node); - return prv_first_event_comparator(&new_node, &old_node, direction); -} - -bool timeline_item_should_show(CommonTimelineItemHeader *header, TimelineIterDirection direction) { - TimelineNode node; - prv_set_node_from_header(header, &node); - const time_t now = rtc_get_time(); - const time_t midnight = time_util_get_midnight_of(now); - return prv_show_event(&node, now, midnight, direction, false /* show_all_day */); -} - -#ifdef TIMELINE_SERVICE_DEBUG -static void prv_debug_print_pins(TimelineNode *node0) { - TimelineNode *node = (TimelineNode *)list_get_head((ListNode *)node0); - PBL_LOG(LOG_LEVEL_DEBUG, "= = = = = = = ="); - while (node) { - PBL_LOG(LOG_LEVEL_DEBUG, "======"); - PBL_LOG(LOG_LEVEL_DEBUG, "Node with id %x%x...", node->id.byte0, node->id.byte1); - PBL_LOG(LOG_LEVEL_DEBUG, "Index %d", node->index); - PBL_LOG(LOG_LEVEL_DEBUG, "Timestamp %ld", node->timestamp); - PBL_LOG(LOG_LEVEL_DEBUG, "Duration %hu", node->duration); - PBL_LOG(LOG_LEVEL_DEBUG, "All day? %s", node->all_day ? "True": "False"); - PBL_LOG(LOG_LEVEL_DEBUG, "Address %p", node); - node = (TimelineNode *)node->node.next; - } -} -#endif - -// dummy iterator that always returns false -// Useful for when there aren't any items in pindb -// but we don't want an invalid iterator. -static bool prv_iter_dummy(IteratorState state) { - return false; -} - -static bool prv_iter_next(IteratorState state) { - TimelineIterState *timeline_iter_state = (TimelineIterState *)state; - if (timeline_iter_state->node == NULL) { - return false; - } - // keep a copy of the original node in case we go to the end without finding a new valid node - TimelineNode *orig = timeline_iter_state->node; - do { - timeline_iter_state->node = (TimelineNode *)timeline_iter_state->node->node.next; - if (timeline_iter_state->node == NULL) { - timeline_iter_state->node = orig; - return false; - } - } while (!prv_show_event(timeline_iter_state->node, timeline_iter_state->start_time, - timeline_iter_state->midnight, timeline_iter_state->direction, - timeline_iter_state->show_all_day_events) || - !timeline_exists(&timeline_iter_state->node->id)); - - timeline_item_free_allocated_buffer(&timeline_iter_state->pin); - timeline_iter_state->pin = (TimelineItem){}; - - status_t rv = pin_db_get(&timeline_iter_state->node->id, &timeline_iter_state->pin); - timeline_iter_state->current_day = time_util_get_midnight_of( - timeline_iter_state->node->timestamp); - timeline_iter_state->index = timeline_iter_state->node->index; -#ifdef TIMELINE_SERVICE_DEBUG - prv_debug_print_pins(timeline_iter_state->node); -#endif - return (rv == S_SUCCESS); -} - -static bool prv_iter_prev(IteratorState state) { - TimelineIterState *timeline_iter_state = (TimelineIterState *)state; - // at the past-most item - if (timeline_iter_state->node == NULL) { - return false; - } - TimelineNode *orig = timeline_iter_state->node; - do { - timeline_iter_state->node = (TimelineNode *)timeline_iter_state->node->node.prev; - if (timeline_iter_state->node == NULL) { - timeline_iter_state->node = orig; - return false; - } - } while (!prv_show_event(timeline_iter_state->node, timeline_iter_state->start_time, - timeline_iter_state->midnight, timeline_iter_state->direction, - timeline_iter_state->show_all_day_events) || - !timeline_exists(&timeline_iter_state->node->id)); - - timeline_item_free_allocated_buffer(&timeline_iter_state->pin); - timeline_iter_state->pin = (TimelineItem){}; - - status_t rv = pin_db_get(&timeline_iter_state->node->id, &timeline_iter_state->pin); - timeline_iter_state->current_day = time_util_get_midnight_of( - timeline_iter_state->node->timestamp); - timeline_iter_state->index = timeline_iter_state->node->index; -#ifdef TIMELINE_SERVICE_DEBUG - prv_debug_print_pins(timeline_iter_state->node); -#endif - return (rv == S_SUCCESS); -} - -static void prv_prune_ordered_timeline_list(TimelineNode **head) { - TimelineNode *node = *head; - TimelineNode *next_node; - while (node) { - next_node = (TimelineNode *)list_get_next((ListNode *)node); - time_t end_time = node->timestamp + (node->duration * SECONDS_PER_MINUTE); - if (pin_db_has_entry_expired(end_time)) { - // remove the pin without emitting an event - pin_db_delete((uint8_t *)&node->id, sizeof(Uuid)); - - // remove the node from out list - timeline_iter_remove_node(head, node); - } else { - break; // the list is ordered so we are done - } - node = next_node; - } -} - -static void prv_put_outgoing_call_event(uint32_t call_identifier, const char *caller_id) { - PebbleEvent event = { - .type = PEBBLE_PHONE_EVENT, - .phone = { - .type = PhoneEventType_Outgoing, - .source = PhoneCallSource_ANCS_Legacy, - .call_identifier = call_identifier, - .caller = phone_call_util_create_caller(caller_id, NULL), - } - }; - - event_put(&event); -} - -/////////////////////////////////////////////////// -// Public functions -////////////////////////////////////////////////// - -status_t timeline_init(TimelineNode **timeline) { - PBL_LOG(LOG_LEVEL_DEBUG, "Starting to build list."); - status_t rv = pin_db_each(prv_each, timeline); - prv_prune_ordered_timeline_list(timeline); - prv_set_indices(*timeline); - PBL_LOG(LOG_LEVEL_DEBUG, "Finished building list."); -#ifdef TIMELINE_SERVICE_DEBUG - prv_debug_print_pins(*timeline); -#endif - return rv; -} - -bool timeline_add(TimelineItem *item) { - return (S_SUCCESS == pin_db_insert_item(item)); -} - -bool timeline_exists(Uuid *id) { - return (pin_db_get_len((uint8_t *)id, UUID_SIZE) > 0); -} - -bool timeline_remove(const Uuid *id) { - // Use BlobDB directly in order to emit the BlobDB delete event - return (S_SUCCESS == blob_db_delete(BlobDBIdPins, (uint8_t *)id, UUID_SIZE)); -} - -TimelineIterDirection timeline_direction_for_item(TimelineItem *item, - TimelineNode *timeline, time_t now) { - if (item->header.all_day) { - time_t today_midnight = time_util_get_midnight_of(now); - if (today_midnight > item->header.timestamp || - prv_should_show_all_day_events(timeline, now, today_midnight, TimelineIterDirectionPast)) { - return TimelineIterDirectionPast; - } else { - return TimelineIterDirectionFuture; - } - } else if (item->header.timestamp < now) { - return TimelineIterDirectionPast; - } else { - return TimelineIterDirectionFuture; - } -} - -bool timeline_nodes_equal(TimelineNode *a, TimelineNode *b) { - if (a == NULL || b == NULL) { - return (a == b); - } - return (uuid_equal(&a->id, &b->id) && (a->timestamp == b->timestamp)); -} - -bool timeline_get_originator_id(const TimelineItem *item, Uuid *uuid) { - TimelineItem pin = {}; - const TimelineItem *pin_p; - - switch (item->header.type) { - case TimelineItemTypeReminder: - // Follow the parent id to get to the owner pin - if (pin_db_get(&item->header.parent_id, &pin) != S_SUCCESS) { - *uuid = UUID_INVALID; - return false; - } - pin_p = &pin; - break; - case TimelineItemTypePin: - case TimelineItemTypeNotification: - // Some notifications have parent pins, some don't. If this one has a parent pin, follow it - if (pin_db_get(&item->header.parent_id, &pin) == S_SUCCESS) { - pin_p = &pin; - } else { - pin_p = item; - } - break; - default: - // Invalid item type - *uuid = UUID_INVALID; - return false; - } - - *uuid = pin_p->header.parent_id; - if (pin_p == &pin) { - timeline_item_free_allocated_buffer(&pin); - } - return true; -} - - -// -// Iter functions -// - -// return true if removed a node, false if non left -void timeline_iter_remove_node(TimelineNode **head, TimelineNode *node) { - PBL_ASSERTN(node); - prv_remove_node(head, node); -} - -// return true if removed a node, false if non left -bool timeline_iter_remove_node_with_id(TimelineNode **head, Uuid *key) { - // potentially more than one item with this UUID key since multiday events - TimelineNode *node = prv_find_by_uuid(*head, key); - if (node) { - timeline_iter_remove_node(head, node); - return true; - } else { - return false; - } -} - -status_t timeline_iter_init(Iterator *iter, TimelineIterState *iter_state, TimelineNode **head, - TimelineIterDirection direction, time_t timestamp) { - iter_state->direction = direction; - iter_state->start_time = timestamp; - iter_state->midnight = time_util_get_midnight_of(timestamp); - iter_state->current_day = iter_state->midnight; - iter_state->show_all_day_events = prv_should_show_all_day_events(*head, timestamp, - iter_state->midnight, direction); - TimelineNode *node = prv_find_first(*head, direction, timestamp, iter_state->midnight, - iter_state->show_all_day_events); - if (node == NULL) { - iter_init(iter, prv_iter_dummy, prv_iter_dummy, iter_state); - return S_NO_MORE_ITEMS; - } - - status_t rv = pin_db_get(&node->id, &iter_state->pin); - if (rv != S_SUCCESS) { - iter_state->pin = (TimelineItem){}; - iter_init(iter, prv_iter_dummy, prv_iter_dummy, iter_state); - return rv; - } - - iter_state->node = node; - iter_state->current_day = time_util_get_midnight_of(node->timestamp); - iter_state->index = iter_state->node->index; - if (direction == TimelineIterDirectionPast) { - iter_init(iter, prv_iter_prev, prv_iter_next, iter_state); - } else { // Future - iter_init(iter, prv_iter_next, prv_iter_prev, iter_state); - } - - return rv; -} - -void timeline_iter_copy_state(TimelineIterState *dst_state, TimelineIterState *src_state, - Iterator *dst_iter, Iterator *src_iter) { - PBL_ASSERTN(dst_state && src_state && dst_iter && src_iter); - - timeline_item_free_allocated_buffer(&dst_state->pin); - - *dst_state = *src_state; - dst_state->pin = (TimelineItem){}; - - *dst_iter = *src_iter; - dst_iter->state = dst_state; -} - -void timeline_iter_deinit(Iterator *iter, TimelineIterState *iter_state, TimelineNode **head) { - TimelineNode *node = *head; - while (node) { - TimelineNode *old = node; - node = (TimelineNode *)node->node.next; - prv_remove_node(head, old); - } - *head = NULL; - - // free the currently allocated item in the iterator - timeline_item_free_allocated_buffer(&iter_state->pin); - iter_init(iter, prv_iter_dummy, prv_iter_dummy, iter_state); -} - -void timeline_iter_refresh_pin(TimelineIterState *iter_state) { - // no-op if the item doesn't exist - if (timeline_exists(&iter_state->pin.header.id)) { - timeline_item_free_allocated_buffer(&iter_state->pin); - Uuid id = iter_state->pin.header.id; - iter_state->pin = (TimelineItem){}; - pin_db_get(&id, &iter_state->pin); - } -} - -bool timeline_add_missed_call_pin(TimelineItem *pin, uint32_t uid) { - uuid_generate(&pin->header.id); - pin->header.layout = LayoutIdGeneric; - pin->header.type = TimelineItemTypePin; - pin->header.from_watch = true; - pin->header.ancs_uid = uid; - - // patch the dismiss action to be a remove action - TimelineItemAction *remove_action = timeline_item_find_dismiss_action(pin); - - // TODO: PBL-23915 - // We leak this i18n'd string because not leaking it is really hard. - // We make sure we only ever allocate it once though, so it's not the end of the world. - remove_action->attr_list.attributes[1].cstring = (char*)i18n_get("Remove", &i18n_key); - remove_action->type = TimelineItemActionTypeRemove; - - return timeline_add(pin); -} - -// -// Actions functions -// - -static void prv_put_notification_action_result(const Uuid *id, const char *msg, - uint32_t timeline_res_id, ActionResultType type) { - // send action result event - PebbleSysNotificationActionResult *action_result = - kernel_malloc_check(sizeof(PebbleSysNotificationActionResult) + 2 * sizeof(Attribute)); - - AttributeList result_attributes = { - .num_attributes = 2, - .attributes = (Attribute *)(((uint8_t *)action_result) + sizeof(*action_result)), - }; - result_attributes.attributes[0].id = AttributeIdSubtitle; - result_attributes.attributes[0].cstring = (char *)msg; - result_attributes.attributes[1].id = AttributeIdIconLarge; - result_attributes.attributes[1].uint32 = timeline_res_id; - - *action_result = (PebbleSysNotificationActionResult) { - .id = *id, - .type = type, - .attr_list = result_attributes, - }; - notifications_handle_notification_action_result(action_result); -} - -static void prv_do_remote_action(const Uuid *id, TimelineItemActionType type, - uint8_t action_id, const AttributeList *attributes, - bool do_async) { - if (comm_session_get_system_session()) { - timeline_action_endpoint_invoke_action(id, type, action_id, attributes, do_async); - } else { - // We know we aren't connected, don't wait around for a response that won't come - prv_put_notification_action_result( - id, i18n_get("Can't connect. Relaunch Pebble Time app on phone.", &i18n_key), - TIMELINE_RESOURCE_GENERIC_WARNING, ActionResultTypeFailure); - } -} - -static void prv_remove_pin_action(const TimelineItem *item, - const TimelineItemAction *action, - const AttributeList *attributes) { - if (item->header.from_watch) { - // remove it via BlobDB - blob_db_delete(BlobDBIdPins, (uint8_t *)&item->header.id, UUID_SIZE); - - // TODO: PBL-23915 - // We leak this i18n'd string because not leaking it is really hard. - // We make sure we only ever allocate it once though, so it's not the end of the world. - prv_put_notification_action_result(&item->header.id, i18n_get("Removed", &i18n_key), - TIMELINE_RESOURCE_RESULT_DELETED, ActionResultTypeSuccess); - } else { - const bool do_async = true; - prv_do_remote_action(&item->header.id, action->type, - action->id, attributes, do_async); - } -} - -static void prv_dismiss_local_notification_action(const TimelineItem *item) { - // TODO: PBL-23915 - // We leak this i18n'd string because not leaking it is really hard. - // We make sure we only ever allocate it once though, so it's not the end of the world. - prv_put_notification_action_result(&item->header.id, i18n_get("Dismissed", &i18n_key), - TIMELINE_RESOURCE_RESULT_DISMISSED, ActionResultTypeSuccess); -} - -static void prv_perform_ancs_negative_action(const TimelineItem *item, - const TimelineItemAction *action) { - uint8_t action_id = attribute_get_uint8(&action->attr_list, AttributeIdAncsAction, - TIMELINE_INVALID_ACTION_ID); - - // Try to load the ancs id from attributes first in case the item's parent id points to another - // timeline item. If the attribute isn't found, we assume the ancs id is stored in the header - uint32_t ancs_uid = attribute_get_uint32(&action->attr_list, AttributeIdAncsId, - item->header.ancs_uid); - - PBL_LOG(LOG_LEVEL_INFO, "Perform ancs notification action (%"PRIu32", %"PRIu8")", ancs_uid, - action_id); - ancs_perform_action(ancs_uid, action_id); - - if (!timeline_is_bulk_ancs_action_mode_enabled()) { - // TODO: PBL-23915 - // We leak this i18n'd string because not leaking it is really hard. - // We make sure we only ever allocate it once though, so it's not the end of the world. - char *msg_i18n = i18n_noop("Dismissed"); - int res_id = TIMELINE_RESOURCE_RESULT_DISMISSED; - - if (action->type == TimelineItemActionTypeAncsDelete) { - msg_i18n = i18n_noop("Deleted"); - res_id = TIMELINE_RESOURCE_RESULT_DELETED; - } - prv_put_notification_action_result(&item->header.id, i18n_get(msg_i18n, &i18n_key), res_id, - ActionResultTypeSuccess); - } -} - -static void prv_get_pin_and_push_pin_window(void *data) { - Uuid *parent_id = data; - TimelineItem *pin = task_malloc(sizeof(TimelineItem)); // cleaned-up by the modal - if (pin && pin_db_get(parent_id, pin) == S_SUCCESS) { - timeline_pin_window_push_modal(pin); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to fetch parent pin"); - } - kernel_free(parent_id); -} - -static void prv_perform_health_response_action(const TimelineItem *item, - const TimelineItemAction *action) { - const uint8_t insight_type = attribute_get_uint8(&item->attr_list, AttributeIdHealthInsightType, - ActivityInsightType_Unknown); - const uint8_t activity_type = attribute_get_uint8(&item->attr_list, - AttributeIdHealthActivityType, - ActivitySessionType_None); - const time_t start_utc = attribute_get_uint32(&item->attr_list, AttributeIdTimestamp, 0); - analytics_event_health_insight_response(start_utc ?: item->header.timestamp, insight_type, - activity_type, action->id); - - // TODO: PBL-23915 - // We leak this i18n'd string because not leaking it is really hard. - // We make sure we only ever allocate it once though, so it's not the end of the world. - const char *message = attribute_get_string(&action->attr_list, AttributeIdBody, - (char *)i18n_get("Thanks!", &i18n_key)); - - const uint32_t timeline_res_id = attribute_get_uint32(&action->attr_list, AttributeIdIconLarge, - TIMELINE_RESOURCE_THUMBS_UP); - - prv_put_notification_action_result(&item->header.id, message, timeline_res_id, - ActionResultTypeSuccess); -} - -void timeline_enable_ancs_bulk_action_mode(bool enable) { - s_bulk_action_mode = enable; -} - -bool timeline_is_bulk_ancs_action_mode_enabled(void) { - return s_bulk_action_mode; -} - -typedef struct OpenAppContext { - EventServiceInfo event_info; - AppInstallId install_id; -} OpenAppContext; - -static void prv_app_render_ready(PebbleEvent *e, void *context) { - OpenAppContext *ctx = context; - - if (ctx->install_id == app_manager_get_current_app_id()) { - WindowStack *window_stack = modal_manager_get_window_stack(ModalPriorityNotification); - window_stack_pop_all(window_stack, true /* animated */); - } - - event_service_client_unsubscribe(&ctx->event_info); - kernel_free(ctx); -} - -void timeline_invoke_action(const TimelineItem *item, const TimelineItemAction *action, - const AttributeList *attributes) { - char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&item->header.parent_id, uuid_buffer); - - switch (action->type) { - case TimelineItemActionTypeOpenWatchApp: - { - // find parent app - AppInstallId install_id = app_install_get_id_for_uuid(&item->header.parent_id); - if (install_id == INSTALL_ID_INVALID) { - // This should never happen... but we're not quite there yet - PBL_LOG(LOG_LEVEL_ERROR, "Could not find parent app %s for pin", uuid_buffer); - return; - } - // fetch the relevant attribute - const uint32_t launch_code = - attribute_get_uint32(&action->attr_list, AttributeIdLaunchCode, 0); - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = install_id, - .common.args = (void *)(uintptr_t)launch_code, - .common.reason = APP_LAUNCH_TIMELINE_ACTION, - }); - PBL_LOG(LOG_LEVEL_INFO, "Opening watch app %s", uuid_buffer); - - // Wait for the app we just launched to have something to render before hiding all modals. - // If we don't we'll end up with flashing in a blank framebuffer. - OpenAppContext *ctx = kernel_malloc_check(sizeof(OpenAppContext)); - *ctx = (OpenAppContext) { - .event_info = { - .type = PEBBLE_RENDER_READY_EVENT, - .handler = prv_app_render_ready, - .context = ctx, - }, - .install_id = install_id, - }; - event_service_client_subscribe(&ctx->event_info); - break; - } - case TimelineItemActionTypeOpenPin: - { - Uuid *parent_id = kernel_malloc(sizeof(Uuid)); - if (parent_id) { - *parent_id = item->header.parent_id; - launcher_task_add_callback(prv_get_pin_and_push_pin_window, parent_id); - PBL_LOG(LOG_LEVEL_INFO, "Opening parent pin %s", uuid_buffer); - } - break; - } - case TimelineItemActionTypeAncsDial: - { - const char *caller_id = attribute_get_string(&item->attr_list, - AttributeIdTitle, "Unknown"); - prv_put_outgoing_call_event(item->header.ancs_uid, caller_id); - notifications_handle_notification_action_result(NULL); - ancs_perform_action(item->header.ancs_uid, ActionIDPositive); - break; - } - // FIXME PBL-18673 this is not necessarily dismiss - case TimelineItemActionTypeAncsPositive: - case TimelineItemActionTypeAncsNegative: - case TimelineItemActionTypeAncsDelete: - { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISSED_COUNT, AnalyticsClient_System); - prv_perform_ancs_negative_action(item, action); - notification_storage_set_status(&item->header.id, TimelineItemStatusDismissed); - break; - } - case TimelineItemActionTypeDismiss: - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISSED_COUNT, AnalyticsClient_System); - - // This is a notification that was sourced from timeline. The mobile phone does not care - // about dismissing it. We just confirm and dismiss locally. - if (item->header.from_watch || - (((item->header.type == TimelineItemTypeNotification) || - (item->header.type == TimelineItemTypeReminder)) && - !timeline_get_private_data_source((Uuid *)&item->header.parent_id))) { - prv_dismiss_local_notification_action(item); - return; - } - - // FALLTHROUGH - case TimelineItemActionTypeGeneric: - case TimelineItemActionTypeResponse: - case TimelineItemActionTypeAncsResponse: - case TimelineItemActionTypeAncsGeneric: - case TimelineItemActionTypeHttp: - case TimelineItemActionTypeComplete: - case TimelineItemActionTypePostpone: - case TimelineItemActionTypeRemoteRemove: - { - // remote action, send it to the phone - const bool do_async = false; - prv_do_remote_action(&item->header.id, action->type, - action->id, attributes, do_async); - break; - } - case TimelineItemActionTypeRemove: - prv_remove_pin_action(item, action, attributes); - break; - case TimelineItemActionTypeInsightResponse: - prv_perform_health_response_action(item, action); - break; - default: - PBL_LOG(LOG_LEVEL_ERROR, "Action type not implemented: %d", action->type); - break; - } -} - -/////////////////////////////////// -//! Timeline datasource functions -/////////////////////////////////// - -typedef struct { - Uuid id; - const char *name; -} PrivateDataSourceInfo; - -static const PrivateDataSourceInfo s_data_sources[] = { - { - .id = UUID_NOTIFICATIONS_DATA_SOURCE, - .name = i18n_noop("Notifications"), - }, - { - .id = UUID_CALENDAR_DATA_SOURCE, - .name = i18n_noop("Calendar"), - }, - { - .id = UUID_WEATHER_DATA_SOURCE, - .name = i18n_noop("Weather"), - }, - { - .id = UUID_REMINDERS_DATA_SOURCE, - .name = i18n_noop("Reminders"), - }, - { - .id = UUID_ALARMS_DATA_SOURCE, - .name = i18n_noop("Alarms"), - }, -#if CAPABILITY_HAS_HEALTH_TRACKING - { - .id = UUID_HEALTH_DATA_SOURCE, - .name = i18n_noop("Health"), - }, -#endif - { - .id = UUID_INTERCOM_DATA_SOURCE, - .name = i18n_noop("Intercom"), - }, -}; - -const char *timeline_get_private_data_source(Uuid *parent_id) { - for (int i = 0; i < (int) ARRAY_LENGTH(s_data_sources); i++) { - if (uuid_equal(parent_id, &s_data_sources[i].id)) { - return s_data_sources[i].name; - } - } - return NULL; -} - -///////////////// -// Analytics - -typedef struct { - unsigned int calendar; - unsigned int other; -} PinCount; - -typedef struct { - time_t timestamp; - PinCount visible_count; - PinCount hourly_count; -} PinAnalyticsInfo; - -static bool prv_count_each(SettingsFile *file, SettingsRecordInfo *info, void *context) { - static const Uuid uuid_calendar_data_source = UUID_CALENDAR_DATA_SOURCE; - - // check entry is valid - if (info->key_len != UUID_SIZE || info->val_len == 0) { - return true; // continue iteration - } - - CommonTimelineItemHeader header; - info->get_val(file, (uint8_t *)&header, sizeof(CommonTimelineItemHeader)); - // Flags & Status are stored inverted. - header.flags = ~header.flags; - header.status = ~header.status; - - // Count up the calendar pins and other (non-system) pins that are currently visible on timeline - PinAnalyticsInfo *analytics_info = context; - if (prv_is_in_window(header.timestamp, header.duration, analytics_info->timestamp)) { - bool within_hour = WITHIN(header.timestamp, analytics_info->timestamp, - analytics_info->timestamp + SECONDS_PER_HOUR); - if (uuid_equal(&header.parent_id, &uuid_calendar_data_source)) { - analytics_info->visible_count.calendar++; - analytics_info->hourly_count.calendar += within_hour; - } else if (!timeline_get_private_data_source(&header.parent_id) && - !uuid_is_system(&header.parent_id) && - app_get_install_id_for_uuid_from_registry(&header.parent_id) == INSTALL_ID_INVALID) { - analytics_info->visible_count.other++; - analytics_info->hourly_count.other += within_hour; - } - } - - return true; // continue iteration -} - -void analytics_external_collect_timeline_pin_stats(void) { - PinAnalyticsInfo analytics_info = { - .timestamp = rtc_get_time() - }; - pin_db_each(prv_count_each, &analytics_info); - - analytics_set(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_VISIBLE_CALENDAR_COUNT, - analytics_info.visible_count.calendar, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_VISIBLE_OTHER_COUNT, - analytics_info.visible_count.other, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_HOURLY_CALENDAR_COUNT, - analytics_info.hourly_count.calendar, AnalyticsClient_System); - analytics_set(ANALYTICS_DEVICE_METRIC_TIMELINE_PINS_HOURLY_OTHER_COUNT, - analytics_info.hourly_count.other, AnalyticsClient_System); -} diff --git a/src/fw/services/normal/timeline/timeline.h b/src/fw/services/normal/timeline/timeline.h deleted file mode 100644 index 971e7b4d6d..0000000000 --- a/src/fw/services/normal/timeline/timeline.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include "services/normal/timeline/item.h" -#include "system/status_codes.h" -#include "util/iterator.h" - -struct TimelineNode; -typedef struct TimelineNode TimelineNode; - -typedef enum { - TimelineIterDirectionPast, - TimelineIterDirectionFuture, -} TimelineIterDirection; - -typedef struct { - TimelineNode *node; - int index; - time_t start_time; - TimelineIterDirection direction; - TimelineItem pin; - bool show_all_day_events; - time_t midnight; // midnight at iter_init - time_t current_day; // midnight of the current pin -} TimelineIterState; - -//! initialize the timeline (builds the list of TimelineNodes) -status_t timeline_init(TimelineNode **timeline); - -//! Add a timeline pin we've created to the timeline. -//! Call \ref timeline_destroy_item after this in order to free up the memory used by the item. -//! @return true on success, false otherwise -bool timeline_add(TimelineItem *item); - -bool timeline_add_missed_call_pin(TimelineItem *pin, uint32_t uid); - -//! Remove a timeline pin we've added to the timeline -//! @return true on success, false otherwise -bool timeline_remove(const Uuid *id); - -//! Check whether a timeline pin exists -//! @return true if it does, false otherwise -bool timeline_exists(Uuid *id); - -//! Enables bulk action mode for ancs actions to avoid filling the event queue -void timeline_enable_ancs_bulk_action_mode(bool enable); - -//! Returns whether or not bulk actoin mode is enabled for ancs actions -bool timeline_is_bulk_ancs_action_mode_enabled(void); - -//! invokes a timelineitem's action. This can end up triggering a bluetooth message. -void timeline_invoke_action(const TimelineItem *item, const TimelineItemAction *action, - const AttributeList *attributes); - -TimelineIterDirection timeline_direction_for_item(TimelineItem *item, - TimelineNode *timeline, time_t now); - -bool timeline_nodes_equal(TimelineNode *a, TimelineNode *b); - -//! Get the UUID of the originator of a timeline item. For pins and notifications, this -//! returns the first parent_id of the item, which is the app's UUID (for pins) or source ID -//! (for notifications). For reminders, it will return the parent_id of the parent, which is the -//! app UUID of the pin that created the reminder. -//! @param [out] id pointer to storage for returned uuid. Set to UUID_INVALID when false -//! is returned -//! @return true if success, false on failure -bool timeline_get_originator_id(const TimelineItem *item, Uuid *id); - -//! Timeline item time comparator which sorts items as they would appear in Timeline with the -//! exception of all day events. -//! @param new_common The common that the resulting value is in reference to. -//! @param old_common The other common that is being compared against. -//! @param direction The Timeline direction. -//! @return < 0 if new_common should be before old_common, > 0 if new_common should be after -//! old_common, and 0 if equal in priority. -int timeline_item_time_comparator(CommonTimelineItemHeader *new_common, - CommonTimelineItemHeader *old_common, - TimelineIterDirection direction); - -//! Whether a Timeline item should show up in the Timeline direction with the exception of all -//! day events. -//! @param common The common header of the item to consider. -//! @param direction The Timeline direction. -//! @return true if the item would show up, false otherwise. -bool timeline_item_should_show(CommonTimelineItemHeader *header, TimelineIterDirection direction); - -/////////////////////////////////// -//! Timeline Iterator functions -/////////////////////////////////// - -status_t timeline_iter_init(Iterator *iter, TimelineIterState *iter_state, TimelineNode **timeline, - TimelineIterDirection direction, time_t timestamp); - -// Copy an iterator's contents into another one -void timeline_iter_copy_state(TimelineIterState *dst_state, TimelineIterState *src_state, - Iterator *dst_iter, Iterator *src_iter); - -void timeline_iter_deinit(Iterator *iter, TimelineIterState *iter_state, TimelineNode **head); - -//! refresh the pin at the current timeline iterator. Does a fairly naive refresh, i.e. does not -//! correctly place the pin in the timeline if the timestamp changes -void timeline_iter_refresh_pin(TimelineIterState *iter_state); - -//! Remove a timeline item from the iterator list -void timeline_iter_remove_node(TimelineNode **timeline, TimelineNode *node); - -//! Remove a timeline item from the iterator list -//! @return true if a node exists and was removed, false otherwise -bool timeline_iter_remove_node_with_id(TimelineNode **timeline, Uuid *key); - -/////////////////////////////////// -//! Timeline datasource functions -/////////////////////////////////// - -// ed429c16-f674-4220-95da-454f303f15e2 -#define UUID_NOTIFICATIONS_DATA_SOURCE {0xed, 0x42, 0x9c, 0x16, 0xf6, 0x74, 0x42, 0x20, 0x95, \ - 0xda, 0x45, 0x4f, 0x30, 0x3f, 0x15, 0xe2} - -// 6c6c6fc2-1912-4d25-8396-3547d1dfac5b -#define UUID_CALENDAR_DATA_SOURCE {0x6c, 0x6c, 0x6f, 0xc2, 0x19, 0x12, 0x4d, 0x25, 0x83, \ - 0x96, 0x35, 0x47, 0xd1, 0xdf, 0xac, 0x5b} - -// 61b22bc8-1e29-460d-a236-3fe409a439ff -#define UUID_WEATHER_DATA_SOURCE {0x61, 0xb2, 0x2b, 0xc8, 0x1e, 0x29, 0x46, 0xd, 0xa2, \ - 0x36, 0x3f, 0xe4, 0x9, 0xa4, 0x39, 0xff} - -// 42a07217-5491-4267-904a-d02a156752b6 -#define UUID_REMINDERS_DATA_SOURCE {0x42, 0xa0, 0x72, 0x17, 0x54, 0x91, 0x42, 0x67, \ - 0x90, 0x4a, 0xd0, 0x2a, 0x15, 0x67, 0x52, 0xb6} - -// UUID: 67a32d95-ef69-46d4-a0b9-854cc62f97f9 -#define UUID_ALARMS_DATA_SOURCE {0x67, 0xa3, 0x2d, 0x95, 0xef, 0x69, 0x46, 0xd4, \ - 0xa0, 0xb9, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9} - -// UUID: 36d8c6ed-4c83-4fa1-a9e2-8f12dc941f8c -#define UUID_HEALTH_DATA_SOURCE {0x36, 0xd8, 0xc6, 0xed, 0x4c, 0x83, 0x4f, 0xa1, \ - 0xa9, 0xe2, 0x8f, 0x12, 0xdc, 0x94, 0x1f, 0x8c} - -// UUID: fef82c82-7176-4e22-88de-35a3fc18d43f -#define UUID_WORKOUT_DATA_SOURCE {0xfe, 0xf8, 0x2c, 0x82, 0x71, 0x76, 0x4e, 0x22, \ - 0x88, 0xde, 0x35, 0xa3, 0xfc, 0x18, 0xd4, 0x3f} - -// UUID: 0863fc6a-66c5-4f62-ab8a-82ed00a98b5d -#define UUID_SEND_TEXT_DATA_SOURCE {0x08, 0x63, 0xfc, 0x6a, 0x66, 0xc5, 0x4f, 0x62, \ - 0xab, 0x8a, 0x82, 0xed, 0x00, 0xa9, 0x8b, 0x5d} - -// UUID: 0f71aaba-5814-4b5c-96e2-c9828c9734cb -// Special UUID that allows the watch to send SMS messages to a specific phone number -#define UUID_SEND_SMS {0x0f, 0x71, 0xaa, 0xba, 0x58, 0x14, 0x4b, 0x5c, \ - 0x96, 0xe2, 0xc9, 0x82, 0x8c, 0x97, 0x34, 0xcb} - -// UUID: 68010669-4b38-4751-ad04-067f1d8d2ab5 -#define UUID_INTERCOM_DATA_SOURCE {0x68, 0x01, 0x06, 0x69, 0x4b, 0x38, 0x47, 0x51, \ - 0xad, 0x04, 0x06, 0x7f, 0x1d, 0x8d, 0x2a, 0xb5} - -//! Get the name of a non-app, i.e. "private" datasource like Weather or Calendar -//! return NULL if parent_id is not a private data source, otherwise the name of the source -const char *timeline_get_private_data_source(Uuid *parent_id); diff --git a/src/fw/services/normal/timeline/timeline_layout_animations.h b/src/fw/services/normal/timeline/timeline_layout_animations.h deleted file mode 100644 index b29676d151..0000000000 --- a/src/fw/services/normal/timeline/timeline_layout_animations.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "timeline_layout.h" - -void timeline_layout_transition_pin_to_card(TimelineLayout *pin_timeline_layout, - TimelineLayout *card_timeline_layout); - -void timeline_layout_transition_card_to_pin(TimelineLayout *card_timeline_layout, - TimelineLayout *pin_timeline_layout); - -Animation *timeline_layout_create_up_down_animation( - TimelineLayout *layout, const GRect *from, const GRect *to, const GRect *icon_from, - const GRect *icon_to, uint32_t duration, InterpolateInt64Function interpolate); diff --git a/src/fw/services/normal/timeline/weather_layout.h b/src/fw/services/normal/timeline/weather_layout.h deleted file mode 100644 index 1879085810..0000000000 --- a/src/fw/services/normal/timeline/weather_layout.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "item.h" -#include "layout_layer.h" -#include "timeline_layout.h" - -#include "applib/graphics/graphics.h" -#include "applib/graphics/text.h" -#include "apps/system_apps/timeline/text_node.h" - -typedef enum { - WeatherTimeType_None = 0, - WeatherTimeType_Pin, -} WeatherTimeType; - -typedef struct { - TimelineLayout timeline_layout; -} WeatherLayout; - -LayoutLayer *weather_layout_create(const LayoutLayerConfig *config); - -bool weather_layout_verify(bool existing_attributes[]); diff --git a/src/fw/services/normal/timezone_database.c b/src/fw/services/normal/timezone_database.c deleted file mode 100644 index 6ca9995ac8..0000000000 --- a/src/fw/services/normal/timezone_database.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timezone_database.h" - -#include "resource/resource.h" -#include "resource/resource_ids.auto.h" -#include "services/common/clock.h" -#include "system/passert.h" - -#include -#include - -#include - -// The format of the database is as follows -// Header -// 2 bytes - Region count -// 2 bytes - DST Rule count -// 2 bytes - Link count -// Regions -// For each region (24 bytes): -// 1 byte - Continent index, @see CONTINENT_NAMES -// 15 bytes - City name -// 2 bytes - GMT offset in minutes as a int16_t -// 5 bytes - Timezone name abbreviation (aka tz_abbr) -// 1 byte - DST Rule ID -// DST Rules -// For each DST ID (16 bytes) -// For each rule in the pair, first the start rule followed by the end rule (8 bytes) -// @see TimezoneDSTRule for the structure -// Links -// For each link (35 bytes) -// 2 bytes - The region id this link maps to -// 33 bytes - The name of the link that should be treated as an alias to the linked region - -typedef struct PACKED { - uint16_t region_count; - uint16_t dst_rule_count; - uint16_t link_count; -} TimezoneDatabaseFlashHeader; -#define TZDATA_HEADER_BYTES (sizeof(TimezoneDatabaseFlashHeader)) - -#define TIMEZONE_CITY_LENGTH 15 // maximum length of the city name in timezone database -#define REGION_BYTES (1 + TIMEZONE_CITY_LENGTH + 2 + 5 + 1) - -#define DST_RULE_BYTES (sizeof(TimezoneDSTRule)) -#define DST_RULE_PAIR_BYTES (DST_RULE_BYTES * 2) - -#define LINK_REGION_LENGTH 2 -#define LINK_NAME_LENGTH 33 -#define LINK_BYTES (LINK_REGION_LENGTH + LINK_NAME_LENGTH) - - -//! Names for all the continents we support. The timezone database stores continents as indexes -//! into this constant array. -const char * const CONTINENT_NAMES[] = { "Africa", - "America", - "Antarctica", - "Asia", - "Atlantic", - "Australia", - "Europe", - "Indian", - "Pacific", - "Etc"}; - - -//! Helper function to curry out some common arguments to the resource reads in this file. -static bool prv_database_read(uint32_t offset, void *data, size_t num_bytes) { - return resource_load_byte_range_system(SYSTEM_APP, RESOURCE_ID_TIMEZONE_DATABASE, - offset, data, num_bytes) == num_bytes; -} - -//! Note! This count includes rule 0 which isn't actually stored in the database. -static int prv_get_dst_rule_count(void) { - uint16_t dst_rule_count; - prv_database_read(offsetof(TimezoneDatabaseFlashHeader, dst_rule_count), - &dst_rule_count, sizeof(dst_rule_count)); - return dst_rule_count; -} - -static int prv_get_link_count(void) { - uint16_t link_count; - prv_database_read(offsetof(TimezoneDatabaseFlashHeader, link_count), - &link_count, sizeof(link_count)); - return link_count; -} - -int timezone_database_get_region_count(void) { - uint16_t region_count; - prv_database_read(offsetof(TimezoneDatabaseFlashHeader, region_count), - ®ion_count, sizeof(region_count)); - return region_count; -} - -bool timezone_database_load_region_info(uint16_t region_id, TimezoneInfo *tz_info) { - const int region_offset = - // Skip over the region count - TZDATA_HEADER_BYTES + - // Skip over the regions list - (region_id * REGION_BYTES); - - //! Struct for reading data from a raw database of timezone information - struct PACKED { - int16_t gmt_offset_minutes; //!< timezone offset from UTC time (in minutes) - char tz_abbr[TZ_LEN - 1]; //!< timezone abbreviation (without terminating nul) - int8_t dst_id; //!< daylight savings time index identifier - } tz_data; - - // Load the timezone information for the region_id, excluding the country + city_name itself - if (!prv_database_read(region_offset + 1 + TIMEZONE_CITY_LENGTH, &tz_data, sizeof(tz_data))) { - *tz_info = (TimezoneInfo) { .dst_id = 0 }; - return false; - } - - *tz_info = (TimezoneInfo) { - .dst_id = tz_data.dst_id, - .timezone_id = region_id, - .tm_gmtoff = tz_data.gmt_offset_minutes * SECONDS_PER_MINUTE, - // Leave the dst_start and dst_end timestamps uninitialized - .dst_start = 0, - .dst_end = 0 - }; - memcpy(tz_info->tm_zone, tz_data.tz_abbr, sizeof(tz_data.tz_abbr)); - - return true; -} - -bool timezone_database_load_region_name(uint16_t region_id, char *region_name) { - if (region_id > timezone_database_get_region_count()) { - return false; - } - - const int region_offset = - // Skip over the region count - TZDATA_HEADER_BYTES + - // Skip over the regions list - (region_id * REGION_BYTES); - - // Read the continent index, which is the first byte - uint8_t continent_index = 0; - prv_database_read(region_offset, &continent_index, sizeof(continent_index)); - PBL_ASSERTN(continent_index < ARRAY_LENGTH(CONTINENT_NAMES)); - - // Copy the continent name into our buffer, followed by a slash. - const int continent_name_length = strlen(CONTINENT_NAMES[continent_index]); - memcpy(region_name, CONTINENT_NAMES[continent_index], continent_name_length); - region_name[continent_name_length] = '/'; - - char *city_name = region_name + continent_name_length + 1 /* slash */; - - // How many bytes left we have of our buffer to fill in the city information. - const int remaining_size = (region_name + TIMEZONE_NAME_LENGTH) - city_name; - - // Fill the rest of our buffer with city name. - // Our generation script will ensure that continent + slash + city name + null will always - // fit in our buffer with a null terminator to spare. - prv_database_read(region_offset + 1 /* continent index */, city_name, remaining_size); - - // FIXME: Perhaps we should refactor this to do one read instead of two? The information is - // right beside each other and it's pretty wasteful to do a 1 byte read followed by a 15 byte - // read as separate reads. - - return true; -} - -bool timezone_database_load_dst_rule(uint8_t dst_id, TimezoneDSTRule *start, TimezoneDSTRule *end) { - const int region_count = timezone_database_get_region_count(); - - const int dst_rule_pair_offset = - // Skip over the region count - TZDATA_HEADER_BYTES + - // Skip over the regions list - (region_count * REGION_BYTES) + - // Find the appropriate DST zone (DST ID is 1 indexed) - ((dst_id - 1) * DST_RULE_PAIR_BYTES); - - // First half of the DST rule pair - if (!prv_database_read(dst_rule_pair_offset, start, DST_RULE_BYTES) || - !prv_database_read(dst_rule_pair_offset + DST_RULE_BYTES, end, DST_RULE_BYTES)) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to load timezone for DST ID %"PRIu8, dst_id); - return false; - } - - if (start->ds_label == '\0' || end->ds_label == '\0') { - // Does not observe DST - return false; - } - - return true; -} - -static int prv_search_regions_by_name(const char *region_name, int region_name_length) { - const int region_count = timezone_database_get_region_count(); - - for (int i = 0; i < region_count; i++) { - char lookup_region_name[TIMEZONE_NAME_LENGTH]; - timezone_database_load_region_name(i, lookup_region_name); - if (strncmp(region_name, lookup_region_name, region_name_length) == 0) { - return i; - } - } - - return -1; -} - -static int prv_search_links_by_name(const char *region_name, int region_name_length) { - char name_asciz[256] = {0}; - memcpy(name_asciz, region_name, region_name_length); - - const int link_section_offset = - // Skip over the region count - TZDATA_HEADER_BYTES + - // Skip over the regions list - (timezone_database_get_region_count() * REGION_BYTES) + - // Skip over the DST list - ((prv_get_dst_rule_count() - 1) * DST_RULE_PAIR_BYTES); - - const uint16_t link_count = prv_get_link_count(); - - for (int i = 0; i < link_count; i++) { - const int link_offset = link_section_offset + (i * LINK_BYTES); - - char link_name[LINK_NAME_LENGTH + 1]; // + max length + null terminator - prv_database_read(link_offset + LINK_REGION_LENGTH, link_name, LINK_NAME_LENGTH); - link_name[sizeof(link_name) - 1] = '\0'; - - if (strncmp(name_asciz, link_name, LINK_NAME_LENGTH) == 0) { - // Found it! - uint16_t linked_region_id; - prv_database_read(link_offset, &linked_region_id, sizeof(linked_region_id)); - return linked_region_id; - } - } - - return -1; -} - -int timezone_database_find_region_by_name(const char *region_name, int region_name_length) { - int region_id = prv_search_regions_by_name(region_name, region_name_length); - - if (region_id == -1) { - // Might be a Link, let's check. - // To explain: iOS, when not synchronized from the internet, uses _ancient_ IANA region names. - // For example, when in California, iOS will send "US/Pacific" which hasn't been the name of - // that timezone since 1993. So we need to support linked timezones sent from the phone. - region_id = prv_search_links_by_name(region_name, region_name_length); - } - - return region_id; -} diff --git a/src/fw/services/normal/vibes/vibe_client.c b/src/fw/services/normal/vibes/vibe_client.c deleted file mode 100644 index f67ee7f6e6..0000000000 --- a/src/fw/services/normal/vibes/vibe_client.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vibe_client.h" - -#include "applib/ui/vibes.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/vibes/vibe_score.h" -#include "services/normal/vibes/vibe_score_info.h" -#include "system/logging.h" - -static VibeScoreId prv_get_resource_for_client(VibeClient client) { - if (client == VibeClient_AlarmsLPM) { - return VibeScoreId_AlarmsLPM; - } - return alerts_preferences_get_vibe_score_for_client(client); -} - -VibeScore *vibe_client_get_score(VibeClient client) { - VibeScoreId id = prv_get_resource_for_client(client); - if (id == VibeScoreId_Disabled) { - return NULL; - } - VibeScore *score = vibe_score_create_with_resource(vibe_score_info_get_resource_id(id)); - if (!score) { - PBL_LOG(LOG_LEVEL_ERROR, "Got a null VibeScore resource!"); - } - return score; -} diff --git a/src/fw/services/normal/vibes/vibe_client.h b/src/fw/services/normal/vibes/vibe_client.h deleted file mode 100644 index 9817e35835..0000000000 --- a/src/fw/services/normal/vibes/vibe_client.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "vibe_score.h" - -typedef enum VibeClient { - VibeClient_Notifications = 0, - VibeClient_PhoneCalls, - VibeClient_Alarms, - VibeClient_AlarmsLPM -} VibeClient; - -// Returns the appropriate vibe score for the client. -// This is determined from alert preferences. -VibeScore *vibe_client_get_score(VibeClient client); diff --git a/src/fw/services/normal/vibes/vibe_intensity.c b/src/fw/services/normal/vibes/vibe_intensity.c deleted file mode 100644 index f7a5f5638c..0000000000 --- a/src/fw/services/normal/vibes/vibe_intensity.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vibe_intensity.h" - -#include "services/common/i18n/i18n.h" -#include "services/common/vibe_pattern.h" -#include "services/normal/notifications/alerts_preferences_private.h" - -uint8_t get_strength_for_intensity(VibeIntensity intensity) { - switch (intensity) { - case VibeIntensityLow: - return 40; - case VibeIntensityMedium: - return 60; - case VibeIntensityHigh: - return 100; - default: - return 100; - } -} - -void vibe_intensity_init(void) { - vibe_intensity_set(vibe_intensity_get()); -} - -void vibe_intensity_set(VibeIntensity intensity) { - vibes_set_default_vibe_strength(get_strength_for_intensity(intensity)); -} - -VibeIntensity vibe_intensity_get(void) { - return alerts_preferences_get_vibe_intensity(); -} - -const char *vibe_intensity_get_string_for_intensity(VibeIntensity intensity) { - switch (intensity) { - case VibeIntensityLow: { - /// Standard vibration pattern option that has a low intensity - return i18n_noop("Standard - Low"); - } - case VibeIntensityMedium: { - /// Standard vibration pattern option that has a medium intensity - return i18n_noop("Standard - Medium"); - } - case VibeIntensityHigh: { - /// Standard vibration pattern option that has a high intensity - return i18n_noop("Standard - High"); - } - default: { - return NULL; - } - } -} - -VibeIntensity vibe_intensity_cycle_next(VibeIntensity intensity) { - return (intensity + 1) % VibeIntensityNum; -} diff --git a/src/fw/services/normal/vibes/vibe_intensity.h b/src/fw/services/normal/vibes/vibe_intensity.h deleted file mode 100644 index 01d0ae5e30..0000000000 --- a/src/fw/services/normal/vibes/vibe_intensity.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum VibeIntensity { - VibeIntensityLow, - VibeIntensityMedium, - VibeIntensityHigh, - VibeIntensityNum, -} VibeIntensity; - -#if PLATFORM_SPALDING -#define DEFAULT_VIBE_INTENSITY VibeIntensityMedium -#else -#define DEFAULT_VIBE_INTENSITY VibeIntensityHigh -#endif - -void vibe_intensity_init(void); - -//! Returns the corresponding strength for the given level of intensity. -//! The strength corresponds to a percentage of the max strength, ie in the range [0,100]. -uint8_t get_strength_for_intensity(VibeIntensity intensity); - -//! Sets the intensity of ALL vibrations (not just notifications) -void vibe_intensity_set(VibeIntensity intensity); - -//! Gets the current vibe intensity -VibeIntensity vibe_intensity_get(void); - -//! Returns a string representation of the provided vibe intensity. -//! @param intensity The intensity for which to get a string representation -//! @return A string representation of the provided intensity, or NULL if the intensity is invalid -const char *vibe_intensity_get_string_for_intensity(VibeIntensity intensity); - -//! Gets the next intensity in the vibe intensity cycle -//! @param intensity Input intensity for which to get the next intensity -//! @return The next vibe intensity in the cycle -VibeIntensity vibe_intensity_cycle_next(VibeIntensity intensity); diff --git a/src/fw/services/normal/vibes/vibe_score_info.h b/src/fw/services/normal/vibes/vibe_score_info.h deleted file mode 100644 index cb894f970d..0000000000 --- a/src/fw/services/normal/vibes/vibe_score_info.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/vibes/vibe_client.h" - -#include -#include - -#define VIBE_DEF(identifier, enum_name, name_str, alert_types_arg, res_id)\ - VibeScoreId_##enum_name = identifier, -typedef enum VibeScoreId { - VibeScoreId_Invalid = 0, - #include "vibes.def" -} VibeScoreId; -#undef VIBE_DEF - -#if PLATFORM_SPALDING -#define DEFAULT_VIBE_SCORE_NOTIFS (VibeScoreId_Pulse) -#define DEFAULT_VIBE_SCORE_INCOMING_CALLS (VibeScoreId_Pulse) -#define DEFAULT_VIBE_SCORE_ALARMS (VibeScoreId_Pulse) -#elif PLATFORM_ASTERIX -#define DEFAULT_VIBE_SCORE_NOTIFS (VibeScoreId_StandardShortPulseHigh) -#define DEFAULT_VIBE_SCORE_INCOMING_CALLS (VibeScoreId_Pulse) -#define DEFAULT_VIBE_SCORE_ALARMS (VibeScoreId_Reveille) -#else -#define DEFAULT_VIBE_SCORE_NOTIFS (VibeScoreId_NudgeNudge) -#define DEFAULT_VIBE_SCORE_INCOMING_CALLS (VibeScoreId_Pulse) -#define DEFAULT_VIBE_SCORE_ALARMS (VibeScoreId_Reveille) -#endif - -// Returns the ResourceId for the VibeScore represented by this id. -// If the id does not exist, the ResourceId of the first vibe in S_VIBE_MAP is returned -uint32_t vibe_score_info_get_resource_id(VibeScoreId id); - -// Returns the name of the VibeScore represented by this id -// If the id does not exist, the name of the first vibe in S_VIBE_MAP is returned -const char *vibe_score_info_get_name(VibeScoreId id); - -// Returns the next vibe score playable by the client from the array defined by vibes.def -// Wraps around and continues searching if the end of the array is reached -// Returns current_id if there is no next vibe score -VibeScoreId vibe_score_info_cycle_next(VibeClient client, VibeScoreId curr_id); - -// Checks if the vibe score id exists and if the associated VibeScoreInfo contains a valid -// resource_id -bool vibe_score_info_is_valid(VibeScoreId id); diff --git a/src/fw/services/normal/vibes/vibes.def b/src/fw/services/normal/vibes/vibes.def deleted file mode 100644 index b102323686..0000000000 --- a/src/fw/services/normal/vibes/vibes.def +++ /dev/null @@ -1,16 +0,0 @@ -// IDs must be monotonically increasing, i.e. they must never be reused -// ID 0 is reserved for "Invalid". Do not use! - -VIBE_DEF(1, Disabled, i18n_ctx_noop("NotifVibe", "Disabled"), (AlertType_Notifications | AlertType_Calls), RESOURCE_ID_INVALID) -VIBE_DEF(2, StandardShortPulseLow, i18n_noop("Standard - Low"), AlertType_Notifications, RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW) -VIBE_DEF(3, StandardLongPulseLow, i18n_noop("Standard - Low"), (AlertType_Calls | AlertType_Alarms), RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW) -VIBE_DEF(4, StandardShortPulseHigh, i18n_noop("Standard - High"), AlertType_Notifications, RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH) -VIBE_DEF(5, StandardLongPulseHigh, i18n_noop("Standard - High"), (AlertType_Calls | AlertType_Alarms), RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH) -VIBE_DEF(8, Pulse, i18n_noop("Pulse"), AlertType_All, RESOURCE_ID_VIBE_SCORE_PULSE) -VIBE_DEF(9, NudgeNudge, i18n_noop("Nudge Nudge"), AlertType_All, RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE) -#if !PLATFORM_SPALDING -VIBE_DEF(10, Jackhammer, i18n_noop("Jackhammer"), AlertType_All, RESOURCE_ID_VIBE_SCORE_JACKHAMMER) -VIBE_DEF(11, Reveille, "Reveille", AlertType_Alarms, RESOURCE_ID_VIBE_SCORE_REVEILLE) -VIBE_DEF(12, Mario, "Mario", AlertType_All, RESOURCE_ID_VIBE_SCORE_MARIO) -#endif -VIBE_DEF(13, AlarmsLPM, "ALARMS LPM", AlertType_AlarmsLPM, RESOURCE_ID_VIBE_SCORE_ALARM_LPM) diff --git a/src/fw/services/normal/voice/sample.pcm b/src/fw/services/normal/voice/sample.pcm deleted file mode 100644 index a4c18ac1cc..0000000000 Binary files a/src/fw/services/normal/voice/sample.pcm and /dev/null differ diff --git a/src/fw/services/normal/voice/voice.c b/src/fw/services/normal/voice/voice.c deleted file mode 100644 index 36bf703960..0000000000 --- a/src/fw/services/normal/voice/voice.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "voice.h" - -#include "bluetooth/responsiveness.h" -#include "board/board.h" -#include "drivers/mic.h" -#include "kernel/events.h" -#include "kernel/pbl_malloc.h" -#include "os/mutex.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" -#include "services/common/comm_session/session.h" -#include "services/common/new_timer/new_timer.h" -#include "services/normal/audio_endpoint.h" -#include "services/normal/voice/transcription.h" -#include "services/normal/voice/voice_speex.h" -#include "services/normal/voice_endpoint.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/profiler.h" -#include "util/likely.h" -#include "util/uuid.h" - -#include - -#define SPEEX_BITSTREAM_VERSION (4) - -#define TIMEOUT_SESSION_SETUP (8000) -#define TIMEOUT_SESSION_RESULT (15000) - -// Buffer size -#define MAX_ENCODED_FRAME_SIZE (200) - -#define VOICE_LOG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_VOICE, LOG_LEVEL_DEBUG, fmt, ## args) - -typedef enum { - SessionState_Idle = 0, - SessionState_StartSession, - SessionState_VoiceEndpointSetupReceived, - SessionState_AudioEndpointSetupReceived, - SessionState_Recording, - SessionState_WaitForSessionResult, -} SessionState; - -static SessionState s_state = SessionState_Idle; - -static PebbleMutex* s_lock = NULL; - -// Handle requests from apps -static bool s_from_app; -static Uuid s_app_uuid; - -static AudioEndpointSessionId s_session_id = AUDIO_ENDPOINT_SESSION_INVALID_ID; -static TimerID s_timeout = TIMER_INVALID_ID; - -// Session generation & teardown guards to mitigate race conditions between explicit cancel -// and timeout callbacks executing on the timer thread. -static uint32_t s_session_generation = 0; // Monotonic session counter -static uint32_t s_timeout_generation = 0; // Generation tied to currently scheduled timeout -static bool s_teardown_in_progress = false; // Debounce concurrent teardown paths -static bool s_delayed_speex_cleanup = false; // Flag to defer Speex cleanup during cancellation - -static void prv_send_event(VoiceEventType event_type, VoiceStatus status, - PebbleVoiceServiceEventData *data); -static void prv_session_result_timeout(void * data); -static void prv_delayed_cleanup_timeout(void *data); - -#if defined(VOICE_DEBUG) -// printf implemented here because the ADT Speex debug library calls printf for logging -int printf(const char *template, ...) { - va_list args; - va_start(args, template); - char s[100]; - vsnprintf(s, sizeof(s), template, args); - VOICE_LOG("%s", s); - - va_end(args); - return 0; -} -#endif - -static void prv_audio_data_handler(int16_t *samples, size_t sample_count, void *context) { - if (!voice_speex_is_initialized()) { - VOICE_LOG("Speex not initialized, dropping audio data"); - return; - } - - if (s_state != SessionState_Recording) { - VOICE_LOG("Not recording, dropping audio data"); - return; - } - - // Additional safety check - ensure we have a valid session - if (s_session_id == AUDIO_ENDPOINT_SESSION_INVALID_ID) { - return; - } - - // Ensure we have the right amount of data for a frame - size_t expected_samples = voice_speex_get_frame_size(); - if (sample_count != expected_samples) { - VOICE_LOG("Unexpected audio sample count: got %zu, expected %zu", sample_count, expected_samples); - return; - } - - // Encode the audio frame - uint8_t encoded_buffer[MAX_ENCODED_FRAME_SIZE]; // Max encoded frame size - int encoded_bytes = voice_speex_encode_frame(samples, encoded_buffer, sizeof(encoded_buffer)); - - if (encoded_bytes > 0) { - // Send encoded data to audio endpoint - audio_endpoint_add_frame(s_session_id, encoded_buffer, encoded_bytes); - } else { - VOICE_LOG("Failed to encode audio frame"); - } -} - -static void prv_teardown_session(void) { -#if !defined(TARGET_QEMU) - // Reset communication session responsiveness to default after voice session ends - CommSession *comm_session = comm_session_get_system_session(); - if (comm_session) { - comm_session_set_responsiveness(comm_session, BtConsumerPpVoiceEndpoint, ResponseTimeMax, 0); - } -#endif -} - -static void prv_stop_recording(void) { - VOICE_LOG("prv_stop_recording called - stopping mic and audio endpoint transfer"); - - // First, set state to non-recording to prevent any new audio processing - s_state = SessionState_WaitForSessionResult; - - // Stop audio endpoint transfer BEFORE stopping microphone - // This prevents new frames from being added while the endpoint shuts down - audio_endpoint_stop_transfer(s_session_id); - -#if !defined(TARGET_QEMU) - mic_stop(MIC); -#endif - - PBL_LOG(LOG_LEVEL_INFO, "Stop recording audio"); - prv_teardown_session(); - - // Speex cleanup will be handled by delayed cleanup to avoid race conditions -} - -static void prv_cancel_recording(void) { - VOICE_LOG("prv_cancel_recording called - cancelling mic and audio endpoint transfer"); -#if !defined(TARGET_QEMU) - mic_stop(MIC); -#endif - - audio_endpoint_cancel_transfer(s_session_id); - PBL_LOG(LOG_LEVEL_INFO, "Cancel audio recording"); - prv_teardown_session(); -} - -static void prv_cancel_early_session(void) { - // For early cancellation, only cancel the audio endpoint transfer - // Don't call mic_stop() since the microphone was never started - audio_endpoint_cancel_transfer(s_session_id); - PBL_LOG(LOG_LEVEL_INFO, "Cancel audio recording"); - prv_teardown_session(); -} - -static void prv_reset(void) { - // Clean up Speex codec safely after recording is completely done - // Skip immediate cleanup if delayed cleanup is scheduled to avoid race conditions - if (s_delayed_speex_cleanup) { - // Speex cleanup will be handled by delayed timer - } else { - voice_speex_deinit(); - } - - s_state = SessionState_Idle; - s_session_id = AUDIO_ENDPOINT_SESSION_INVALID_ID; - -} - -static void prv_cancel_session(void) { - prv_cancel_recording(); - prv_reset(); -} - -static void prv_start_result_timeout(void) { - new_timer_start(s_timeout, TIMEOUT_SESSION_RESULT, prv_session_result_timeout, NULL, 0); -} - -static void prv_audio_transfer_stopped_handler(AudioEndpointSessionId session_id) { - VOICE_LOG("prv_audio_transfer_stopped_handler called with session_id=%d (current=%d)", - session_id, s_session_id); - - if (s_session_id != session_id) { - PBL_LOG(LOG_LEVEL_WARNING, "Received audio transfer message when no session was in progress (" - "%d)", session_id); - return; - } - - if (s_state != SessionState_Recording) { - PBL_LOG(LOG_LEVEL_WARNING, "Received stop message from phone after audio session " - "stopped/cancelled"); - return; - } - - VOICE_LOG("Stopping recording due to phone request"); - // TODO: Handle this better: there is no feedback to the UI that we've stopped recording - s_state = SessionState_WaitForSessionResult; - prv_stop_recording(); - s_timeout_generation = s_session_generation; - prv_start_result_timeout(); -} - -static void prv_start_recording(void) { - VOICE_LOG("prv_start_recording called"); -#if !defined(TARGET_QEMU) - // Start microphone with Speex frame buffer - int16_t *frame_buffer = voice_speex_get_frame_buffer(); - size_t frame_size_samples = voice_speex_get_frame_size(); // Get frame size in samples - - VOICE_LOG("Got Speex frame buffer: %p, frame_size_samples: %zu", frame_buffer, frame_size_samples); - - if (frame_buffer && frame_size_samples > 0) { - VOICE_LOG("Starting microphone with frame buffer"); - PBL_ASSERTN(mic_start(MIC, &prv_audio_data_handler, NULL, frame_buffer, frame_size_samples)); - VOICE_LOG("Microphone started successfully"); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid Speex frame buffer"); - return; - } -#else - VOICE_LOG("Running on QEMU - skipping microphone start"); -#endif -} - -static void prv_send_event(VoiceEventType event_type, VoiceStatus status, - PebbleVoiceServiceEventData *data) { - PebbleEvent event = { - .type = PEBBLE_VOICE_SERVICE_EVENT, - .voice_service = { - .type = event_type, - .status = status, - .data = data, - } - }; - event_put(&event); -} - -//! Expects s_lock is held by caller -static void prv_handle_subsystem_started(SessionState transition_to_state) { - VOICE_LOG("prv_handle_subsystem_started called: transition_to_state=%d, current_state=%d", - transition_to_state, s_state); - - PBL_ASSERTN(transition_to_state == SessionState_VoiceEndpointSetupReceived || - transition_to_state == SessionState_AudioEndpointSetupReceived); - - if (s_state == SessionState_Idle) { // we error'ed out - VOICE_LOG("Session already errored out (state=Idle), ignoring subsystem start"); - return; - } - - if (s_state == SessionState_StartSession) { - // we are still waiting for one of the subsystems to be ready - VOICE_LOG("First subsystem ready, transitioning to state %d", transition_to_state); - s_state = transition_to_state; - } else { - PBL_ASSERTN((s_state == SessionState_VoiceEndpointSetupReceived || - s_state == SessionState_AudioEndpointSetupReceived) && - (transition_to_state != s_state)); - - VOICE_LOG("Both subsystems ready, transitioning to Recording state"); - s_state = SessionState_Recording; - - new_timer_stop(s_timeout); - - // Indicate to the UI that we have started recording - PBL_LOG(LOG_LEVEL_INFO, "Session setup successfully"); - prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusSuccess, NULL); - - VOICE_LOG("Starting recording now that both subsystems are ready"); - prv_start_recording(); - } -} - -static void prv_audio_transfer_setup_complete_handler(AudioEndpointSessionId session_id) { - VOICE_LOG("prv_audio_transfer_setup_complete_handler called with session_id=%d (current=%d)", - session_id, s_session_id); - - if (s_session_id != session_id) { - PBL_LOG(LOG_LEVEL_WARNING, "Received audio transfer message when no session was in progress (" - "%d)", session_id); - return; - } - - VOICE_LOG("Audio transfer setup complete, handling subsystem started"); - mutex_lock(s_lock); - prv_handle_subsystem_started(SessionState_AudioEndpointSetupReceived); - mutex_unlock(s_lock); -} - -static void prv_session_result_timeout(void * data) { - mutex_lock(s_lock); - - if (s_teardown_in_progress || (s_timeout_generation != s_session_generation)) { - VOICE_LOG("Ignoring stale session result timeout (t_gen=%"PRIu32" cur=%"PRIu32" teardown=%d)", - s_timeout_generation, s_session_generation, s_teardown_in_progress); - mutex_unlock(s_lock); - return; - } - - PBL_ASSERTN(s_state == SessionState_WaitForSessionResult); - - prv_reset(); - PBL_LOG(LOG_LEVEL_WARNING, "Timeout waiting for session result"); - - prv_send_event(VoiceEventTypeSessionResult, VoiceStatusTimeout, NULL); - - mutex_unlock(s_lock); -} - -static void prv_delayed_cleanup_timeout(void *data) { - mutex_lock(s_lock); - - voice_speex_deinit(); - s_delayed_speex_cleanup = false; - - mutex_unlock(s_lock); -} - -static void prv_session_setup_timeout(void * data) { - mutex_lock(s_lock); - if (s_teardown_in_progress || (s_timeout_generation != s_session_generation)) { - VOICE_LOG("Ignoring stale session setup timeout (t_gen=%"PRIu32" cur=%"PRIu32" teardown=%d)", - s_timeout_generation, s_session_generation, s_teardown_in_progress); - mutex_unlock(s_lock); - return; - } - - PBL_ASSERTN(s_state == SessionState_StartSession || - s_state == SessionState_VoiceEndpointSetupReceived || - s_state == SessionState_AudioEndpointSetupReceived); - - prv_cancel_session(); - PBL_LOG(LOG_LEVEL_WARNING, "Timeout waiting for session setup result "); - - prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusTimeout, NULL); - - mutex_unlock(s_lock); -} - -static VoiceStatus prv_get_status_from_result(VoiceEndpointResult result) { - VoiceStatus status; - switch (result) { - case VoiceEndpointResultFailServiceUnavailable: - status = VoiceStatusErrorConnectivity; - break; - case VoiceEndpointResultFailDisabled: - status = VoiceStatusErrorDisabled; - break; - case VoiceEndpointResultFailInvalidRecognizerResponse: - status = VoiceStatusRecognizerResponseError; - break; - case VoiceEndpointResultFailTimeout: - case VoiceEndpointResultFailRecognizerError: - case VoiceEndpointResultFailInvalidMessage: - default: - status = VoiceStatusErrorGeneric; - break; - } - return status; -} - -void voice_init(void) { - s_lock = mutex_create(); - // Speex encoder is now initialized lazily when a dictation session starts -} - -// This will kick off a dictation session. After the setup session message is sent via the -// voice control endpoint, we wait for a session ready response via the -// voice_handle_session_setup_result call or a session setup timeout occurs (timer callback -// prv_session_setup_timeout) -VoiceSessionId voice_start_dictation(VoiceEndpointSessionType session_type) { - VOICE_LOG("voice_start_dictation called with session_type: %d", session_type); - mutex_lock(s_lock); - - // Lazily initialize Speex encoder to avoid baseline memory usage when voice not used - if (!voice_speex_is_initialized()) { - if (!voice_speex_init()) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to initialize Speex encoder"); - mutex_unlock(s_lock); - return VOICE_SESSION_ID_INVALID; - } - } - - if (s_state != SessionState_Idle) { - VOICE_LOG("Voice service not idle (state: %d), returning invalid session", s_state); - mutex_unlock(s_lock); - return VOICE_SESSION_ID_INVALID; - } - - // Prevent new sessions while delayed cleanup is pending to avoid race conditions - if (s_delayed_speex_cleanup) { - mutex_unlock(s_lock); - return VOICE_SESSION_ID_INVALID; - } - // Start new session generation and clear teardown guard - s_session_generation++; - if (UNLIKELY(s_session_generation == 0)) { // handle wrap-around (very unlikely) - s_session_generation = 1; - } - s_teardown_in_progress = false; - VOICE_LOG("Setting state to StartSession"); - s_state = SessionState_StartSession; - - // check if we're being started from an app so we know to send the UUID when setting up a session - s_from_app = ((pebble_task_get_current() == PebbleTask_App) && - !app_install_id_from_system(app_manager_get_current_app_id())); - if (s_from_app) { - s_app_uuid = app_manager_get_current_app_md()->uuid; - char uuid_str[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(&s_app_uuid, uuid_str); - PBL_LOG(LOG_LEVEL_INFO, "Starting app-initiated voice dictation session for app %s", uuid_str); - } else { - VOICE_LOG("Starting system-initiated voice dictation session"); - } - -#if !defined(TARGET_QEMU) - // Set up communication session responsiveness for voice session - CommSession *comm_session = comm_session_get_system_session(); - if (comm_session) { - comm_session_set_responsiveness(comm_session, BtConsumerPpVoiceEndpoint, ResponseTimeMin, - MIN_LATENCY_MODE_TIMEOUT_VOICE_SECS); - } -#endif - - // Get Speex transfer info - AudioTransferInfoSpeex transfer_info; - voice_speex_get_transfer_info(&transfer_info); - VOICE_LOG("Got Speex transfer info: sample_rate=%"PRIu32", bit_rate=%"PRIu16", frame_size=%"PRIu16, - transfer_info.sample_rate, transfer_info.bit_rate, transfer_info.frame_size); - - VOICE_LOG("Setting up audio endpoint transfer"); - s_session_id = audio_endpoint_setup_transfer(prv_audio_transfer_setup_complete_handler, - prv_audio_transfer_stopped_handler); - PBL_ASSERTN(s_session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); - VOICE_LOG("Audio endpoint transfer setup complete with session_id=%d", s_session_id); - - - PBL_LOG(LOG_LEVEL_INFO, "Send session setup message. Session type: %d", session_type); - VOICE_LOG("Calling voice_endpoint_setup_session"); - voice_endpoint_setup_session(session_type, s_session_id, &transfer_info, - s_from_app ? &s_app_uuid : NULL); - - if (s_timeout == TIMER_INVALID_ID) { - s_timeout = new_timer_create(); - } - s_timeout_generation = s_session_generation; - new_timer_start(s_timeout, TIMEOUT_SESSION_SETUP, prv_session_setup_timeout, NULL, 0); - - mutex_unlock(s_lock); - return s_session_id; -} - -// Calling this will end the recording, disable the mic and and stop the audio transfer session. We -// expect voice_handle_dictation_result to be called next with a dictation response -void voice_stop_dictation(VoiceSessionId session_id) { - mutex_lock(s_lock); - if ((s_state == SessionState_Idle) || - (session_id != s_session_id) || - (session_id == VOICE_SESSION_ID_INVALID)) { - goto unlock; - } - - if (s_state != SessionState_Recording) { - mutex_unlock(s_lock); - voice_cancel_dictation(session_id); - return; - } - - s_state = SessionState_WaitForSessionResult; - prv_stop_recording(); - prv_start_result_timeout(); - -unlock: - mutex_unlock(s_lock); -} - -void voice_cancel_dictation(VoiceSessionId session_id) { - mutex_lock(s_lock); - if ((session_id != s_session_id) || - (session_id == VOICE_SESSION_ID_INVALID)) { - goto unlock; - } - - if (s_state != SessionState_Idle) { - bool stopped = new_timer_stop(s_timeout); - if (!stopped) { - // Timer callback in progress or already firing; invalidate generation so callback no-ops - s_timeout_generation = 0; - VOICE_LOG("Timeout stop race (cancel), invalidating timeout generation"); - } - s_teardown_in_progress = true; - if (s_state == SessionState_StartSession || - s_state == SessionState_VoiceEndpointSetupReceived || - s_state == SessionState_AudioEndpointSetupReceived) { - prv_cancel_early_session(); - // Use delayed cleanup for early cancellation to avoid race conditions with phone/endpoint - s_delayed_speex_cleanup = true; - new_timer_start(s_timeout, 100, prv_delayed_cleanup_timeout, NULL, 0); - prv_reset(); - } else if (s_state == SessionState_Recording) { - prv_stop_recording(); - // Use delayed cleanup to avoid race condition with microphone - s_delayed_speex_cleanup = true; - new_timer_start(s_timeout, 100, prv_delayed_cleanup_timeout, NULL, 0); - prv_reset(); - } else if (s_state == SessionState_WaitForSessionResult) { - // Recording is done but audio endpoint might still be processing - s_delayed_speex_cleanup = true; - new_timer_start(s_timeout, 100, prv_delayed_cleanup_timeout, NULL, 0); - prv_reset(); - } else { - // For any other state, safe to reset immediately - prv_reset(); - } - } - -unlock: - mutex_unlock(s_lock); -} - -// This will trigger an event to be sent to the main task indicating success or failure to set up -// a session. If the session setup result was success, the microphone will be enabled and we'll -// start sending Speex encoded data via the audio endpoint to the phone. voice_stop_dictation will -// end the recording -void voice_handle_session_setup_result(VoiceEndpointResult result, - VoiceEndpointSessionType session_type, bool app_initiated) { - VOICE_LOG("voice_handle_session_setup_result: result=%d, session_type=%d, app_initiated=%d", - result, session_type, app_initiated); - VOICE_LOG("Current state: %d", s_state); - - mutex_lock(s_lock); - - if (s_state == SessionState_Idle) { - VOICE_LOG("State is Idle, ignoring session setup result"); - goto unlock; - } - - bool has_error = true; - - if (s_state != SessionState_StartSession && - s_state != SessionState_AudioEndpointSetupReceived) { - PBL_LOG(LOG_LEVEL_WARNING, "Session setup result received when not expected, state=%d", - (int)s_state); - prv_cancel_session(); - VoiceEventType event_type = (s_state <= SessionState_StartSession) ? - VoiceEventTypeSessionSetup : VoiceEventTypeSessionResult; - prv_send_event(event_type, VoiceStatusErrorGeneric, NULL); - goto done; - } - - if (session_type >= VoiceEndpointSessionTypeCount) { - PBL_LOG(LOG_LEVEL_WARNING, "Session setup result for invalid session type received"); - goto done; - } - - if (result != VoiceEndpointResultSuccess) { - VOICE_LOG("ERROR: Session setup failed with result %d", result); - prv_cancel_session(); - VoiceStatus status = prv_get_status_from_result(result); - PBL_LOG(LOG_LEVEL_WARNING, "Error occurred setting up session: %d", result); - prv_send_event(VoiceEventTypeSessionSetup, status, NULL); - goto done; - } - - if (app_initiated != s_from_app) { - VOICE_LOG("ERROR: App initiated mismatch - received=%d, expected=%d", app_initiated, s_from_app); - prv_cancel_session(); - if (app_initiated) { - PBL_LOG(LOG_LEVEL_WARNING, "Received session setup result for app initiated session when it " - "was not expected"); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Received session setup result for non-app session when an app " - "session result was expected"); - } - prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusErrorGeneric, NULL); - goto done; - } - - VOICE_LOG("Session setup successful!"); - has_error = false; - -done: - if (has_error) { - VOICE_LOG("Session setup had errors, stopping timeout timer"); - new_timer_stop(s_timeout); - } else { - VOICE_LOG("Session setup complete, notifying voice endpoint subsystem started"); - prv_handle_subsystem_started(SessionState_VoiceEndpointSetupReceived); - } -unlock: - mutex_unlock(s_lock); -} - -static bool prv_get_string_size_cb(const TranscriptionWord *word, void *data) { - size_t *size = data; - *size += word->length + sizeof(char); // add 1 for space or null terminator - return true; -} - -static bool prv_build_string_cb(const TranscriptionWord *word, void *data) { - char *sentence = data; - - // if the current word is a punctuation mark strip out backspace (phone app inserts backspace - // before punctuation mark) and do not insert a space before the word - if (word->data[0] == '\x08') { - strncat(sentence, (char *) &word->data[1], word->length - 1); - } else { - // if this is not the beginning of the string, insert a space before the word - if (strlen(sentence) != 0) { - strcat(sentence, " "); - } - strncat(sentence, (char *) word->data, word->length); - } - - return true; -} - -static bool prv_handle_dictation_nlp_result_common(VoiceEndpointResult result, - AudioEndpointSessionId session_id, - bool app_initiated, Uuid *app_uuid) { - if (s_state == SessionState_Idle) { - return false; - } - - // stop timer before changing state variable - new_timer_stop(s_timeout); - - if (s_state != SessionState_WaitForSessionResult) { - // This handles erroneous replies from the phone app (sometimes the phone app sends a session - // result immediately after we start streaming - PBL_LOG(LOG_LEVEL_WARNING, "Session result when not expected (result: %d, " - "session_id: %d)", result, session_id); - if (s_state == SessionState_Recording) { - prv_stop_recording(); - } else { - prv_cancel_recording(); - } - VoiceEventType event_type = (s_state <= SessionState_StartSession) ? - VoiceEventTypeSessionSetup : VoiceEventTypeSessionResult; - prv_send_event(event_type, VoiceStatusErrorGeneric, NULL); - return false; - } - - if (s_session_id != session_id) { - PBL_LOG(LOG_LEVEL_WARNING, "Received session result for wrong session (Expected: " - "%"PRIu16"; Received: %"PRIu16, s_session_id, session_id); - prv_send_event(VoiceEventTypeSessionResult, VoiceStatusErrorGeneric, NULL); - return false; - } - - if (result != VoiceEndpointResultSuccess) { - VoiceStatus status = prv_get_status_from_result(result); - PBL_LOG(LOG_LEVEL_WARNING, "Error occurred processing result: %d", result); - prv_send_event(VoiceEventTypeSessionResult, status, NULL); - return false; - } - - // Make sure that if this is an app initiated session, we're expecting a response for an app - // initiated session and that if this is an app initiated session, the app UUID matches the - // expected UUID - if ((app_initiated != s_from_app) || (s_from_app && !uuid_equal(&s_app_uuid, app_uuid))) { - if (app_initiated) { - PBL_LOG(LOG_LEVEL_WARNING, "Received session result for app initiated session when a " - "non-app session result was expected"); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Received session result for non-app session when an app " - "session result was expected"); - } - prv_send_event(VoiceEventTypeSessionResult, VoiceStatusErrorGeneric, NULL); - return false; - } - - return true; -} - -// receiving this ends the session, sending an event to the main task with the result -void voice_handle_dictation_result(VoiceEndpointResult result, AudioEndpointSessionId session_id, - Transcription *transcription, bool app_initiated, - Uuid *app_uuid) { - mutex_lock(s_lock); - - if (!prv_handle_dictation_nlp_result_common(result, session_id, app_initiated, app_uuid)) { - goto unlock; - } - - // Calculate size of string - size_t sentence_size = 0; - transcription_iterate_words(transcription->sentences[0].words, - transcription->sentences[0].word_count, prv_get_string_size_cb, &sentence_size); - - const size_t event_size = sizeof(PebbleVoiceServiceEventData) + sentence_size; - PebbleVoiceServiceEventData *event_data = kernel_zalloc_check(event_size); - - // TODO: Final UI will probably demand a more sophisticated input, but this service will be - // updated to support additional features when the final UI is implemented - // Build string by concatenating each word in the first sentence - transcription_iterate_words(transcription->sentences[0].words, - transcription->sentences[0].word_count, prv_build_string_cb, event_data->sentence); - - if (app_initiated) { - char uuid_str[UUID_STRING_BUFFER_LENGTH]; - uuid_to_string(app_uuid, uuid_str); - PBL_LOG(LOG_LEVEL_INFO, "Transcription received (%"PRIu32" B) for app %s", - (uint32_t)sentence_size, uuid_str); - } else { - PBL_LOG(LOG_LEVEL_INFO, "Transcription received (%"PRIu32" B)", (uint32_t)sentence_size); - } - - prv_send_event(VoiceEventTypeSessionResult, VoiceStatusSuccess, event_data); - -unlock: - prv_reset(); - mutex_unlock(s_lock); -} - -// receiving this ends the session, sending an event to the main task with the result -void voice_handle_nlp_result(VoiceEndpointResult result, AudioEndpointSessionId session_id, - char *reminder, time_t timestamp) { - mutex_lock(s_lock); - - const bool app_initiated = false; - Uuid *app_uuid = NULL; - if (!prv_handle_dictation_nlp_result_common(result, session_id, app_initiated, app_uuid)) { - goto unlock; - } - - const size_t sentence_size = strlen(reminder) + 1; - const size_t event_size = sizeof(PebbleVoiceServiceEventData) + sentence_size; - PebbleVoiceServiceEventData *event_data = kernel_zalloc_check(event_size); - *event_data = (PebbleVoiceServiceEventData) { - .timestamp = timestamp, - }; - strncpy(event_data->sentence, reminder, sentence_size); - - prv_send_event(VoiceEventTypeSessionResult, VoiceStatusSuccess, event_data); - -unlock: - prv_reset(); - mutex_unlock(s_lock); -} - -DEFINE_SYSCALL(VoiceSessionId, sys_voice_start_dictation, VoiceEndpointSessionType session_type) { - if (session_type >= VoiceEndpointSessionTypeCount) { - return AUDIO_ENDPOINT_SESSION_INVALID_ID; - } - return voice_start_dictation(session_type); -} - -DEFINE_SYSCALL(void, sys_voice_stop_dictation, VoiceSessionId session_id) { - voice_stop_dictation(session_id); -} - -DEFINE_SYSCALL(void, sys_voice_cancel_dictation, VoiceSessionId session_id) { - voice_cancel_dictation(session_id); -} - -void voice_kill_app_session(PebbleTask task) { - if (task != PebbleTask_App) { - return; - } - mutex_lock(s_lock); - if (s_from_app && (s_session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID)) { - prv_cancel_session(); - } - mutex_unlock(s_lock); -} diff --git a/src/fw/services/normal/voice/voice_agc.c b/src/fw/services/normal/voice/voice_agc.c deleted file mode 100644 index d542edb6fa..0000000000 --- a/src/fw/services/normal/voice/voice_agc.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "voice_agc.h" - -#include "util/math.h" - -#include -#include -#include - -#define AGC_Q_BITS (12) -#define AGC_Q_UNIT (1 << AGC_Q_BITS) -#define AGC_TARGET_LEVEL (6000U) -#define AGC_SILENCE_LEVEL (200U) -#define AGC_MIN_GAIN_Q12 (AGC_Q_UNIT / 4) // 0.25x -#define AGC_MAX_GAIN_Q12 (AGC_Q_UNIT * 8) // 8x -#define AGC_DEFAULT_GAIN_Q12 (AGC_Q_UNIT) -#define AGC_LEVEL_SMOOTHING (8U) -#define AGC_ATTACK_FRAMES (4) -#define AGC_RELEASE_FRAMES (16) -#define AGC_SILENCE_HANG_FRAMES (12) - -static int32_t prv_agc_adjust_gain(int32_t current_gain_q12, int32_t desired_gain_q12) { - if (desired_gain_q12 > current_gain_q12) { - const int32_t delta = desired_gain_q12 - current_gain_q12; - int32_t step = MAX(1, (delta + (AGC_ATTACK_FRAMES - 1)) / AGC_ATTACK_FRAMES); - step = MIN(step, delta); - return current_gain_q12 + step; - } - - const int32_t delta = current_gain_q12 - desired_gain_q12; - int32_t step = MAX(1, (delta + (AGC_RELEASE_FRAMES - 1)) / AGC_RELEASE_FRAMES); - step = MIN(step, delta); - return current_gain_q12 - step; -} - -void voice_agc_init(VoiceAgcState *agc) { - PBL_ASSERTN(agc != NULL); - - memset(agc, 0, sizeof(*agc)); - agc->enabled = true; - agc->gain_q12 = AGC_DEFAULT_GAIN_Q12; - agc->last_applied_gain_q12 = AGC_DEFAULT_GAIN_Q12; - agc->smoothed_level = AGC_TARGET_LEVEL; - agc->silence_run = 0; -} - -void voice_agc_process_frame(VoiceAgcState *agc, int16_t *samples, size_t sample_count) { - PBL_ASSERTN(agc != NULL && agc->enabled && sample_count > 0 && samples != NULL); - - uint64_t sum_abs = 0; - int32_t peak = 0; - for (size_t i = 0; i < sample_count; i++) { - const int32_t sample = samples[i]; - const int32_t abs_sample = sample >= 0 ? sample : -sample; - sum_abs += (uint32_t)abs_sample; - peak = MAX(peak, abs_sample); - } - - const uint32_t instant_level = sample_count > 0 ? (uint32_t)(sum_abs / sample_count) : 0U; - const uint32_t bounded_level = MAX(instant_level, 1U); - - if (agc->smoothed_level == 0) { - agc->smoothed_level = bounded_level; - } else { - const uint32_t weight = AGC_LEVEL_SMOOTHING; - const uint64_t acc = (uint64_t)agc->smoothed_level * (weight - 1U) + bounded_level; - agc->smoothed_level = (uint32_t)((acc + (weight / 2U)) / weight); - } - - if (instant_level <= AGC_SILENCE_LEVEL) { - if (agc->silence_run < AGC_SILENCE_HANG_FRAMES) { - agc->silence_run++; - } - } else { - agc->silence_run = 0; - } - - int32_t desired_gain_q12 = AGC_DEFAULT_GAIN_Q12; - if (instant_level > AGC_SILENCE_LEVEL || agc->silence_run < AGC_SILENCE_HANG_FRAMES) { - const uint32_t effective_level = MAX(agc->smoothed_level, AGC_SILENCE_LEVEL); - const uint64_t numerator = (uint64_t)AGC_TARGET_LEVEL << AGC_Q_BITS; - desired_gain_q12 = (int32_t)(numerator / effective_level); - } - - desired_gain_q12 = CLIP(desired_gain_q12, AGC_MIN_GAIN_Q12, AGC_MAX_GAIN_Q12); - agc->gain_q12 = prv_agc_adjust_gain(agc->gain_q12, desired_gain_q12); - agc->gain_q12 = CLIP(agc->gain_q12, AGC_MIN_GAIN_Q12, AGC_MAX_GAIN_Q12); - - int32_t applied_gain_q12 = agc->gain_q12; - if (peak > 0) { - const int64_t max_gain_from_peak = ((int64_t)INT16_MAX << AGC_Q_BITS) / peak; - int32_t safe_gain_q12 = (int32_t)MIN(max_gain_from_peak, (int64_t)AGC_MAX_GAIN_Q12); - if (safe_gain_q12 < AGC_MIN_GAIN_Q12) { - safe_gain_q12 = AGC_MIN_GAIN_Q12; - } - applied_gain_q12 = MIN(applied_gain_q12, safe_gain_q12); - } - - applied_gain_q12 = CLIP(applied_gain_q12, 0, AGC_MAX_GAIN_Q12); - - for (size_t i = 0; i < sample_count; i++) { - int64_t scaled = (int64_t)samples[i] * (int64_t)applied_gain_q12; - scaled = (scaled + (int64_t)(1 << (AGC_Q_BITS - 1))) >> AGC_Q_BITS; - scaled = CLIP(scaled, (int64_t)INT16_MIN, (int64_t)INT16_MAX); - samples[i] = (int16_t)scaled; - } - - agc->last_applied_gain_q12 = applied_gain_q12; -} diff --git a/src/fw/services/normal/voice/voice_agc.h b/src/fw/services/normal/voice/voice_agc.h deleted file mode 100644 index 54b54f9619..0000000000 --- a/src/fw/services/normal/voice/voice_agc.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -typedef struct { - bool enabled; - int32_t gain_q12; - int32_t last_applied_gain_q12; - uint32_t smoothed_level; - uint16_t silence_run; -} VoiceAgcState; - -void voice_agc_init(VoiceAgcState *agc); -void voice_agc_process_frame(VoiceAgcState *agc, int16_t *samples, size_t sample_count); diff --git a/src/fw/services/normal/voice/voice_speex.c b/src/fw/services/normal/voice/voice_speex.c deleted file mode 100644 index 4294f89d66..0000000000 --- a/src/fw/services/normal/voice/voice_speex.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2025 Joshua Jun - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "voice_speex.h" -#include "voice_agc.h" - -#include "system/logging.h" -#include "system/passert.h" -#include "kernel/pbl_malloc.h" -#include "system/logging.h" -#include "services/normal/audio_endpoint.h" -#include "drivers/mic.h" - -#include "speex/speex.h" -#include "speex/speex_bits.h" -#include "speex/speex_header.h" - -#include -#include - -// External Speex mode declarations -extern const SpeexMode speex_wb_mode; - -#define VOICE_SPEEX_LOG(fmt, args...) PBL_LOG_D(LOG_DOMAIN_VOICE, LOG_LEVEL_DEBUG, fmt, ## args) - -// Speex bitstream version -#define SPEEX_BITSTREAM_VERSION 4 - -// Speex encoder state -typedef struct { - void *enc_state; - SpeexBits bits; - uint32_t frame_size; // Changed to uint32_t to match transfer info - uint32_t sample_rate; // Changed to uint32_t to match transfer info - uint16_t bit_rate; // Changed to uint16_t to match transfer info - uint8_t bitstream_version; // Changed to uint8_t to match transfer info - bool initialized; - uint8_t *frame_buffer; - size_t frame_buffer_size; - uint8_t *encoded_buffer; - size_t encoded_buffer_size; - VoiceAgcState agc; -} VoiceSpeexEncoder; - -static VoiceSpeexEncoder s_encoder = {0}; - -// Speex configuration -#define SPEEX_SAMPLE_RATE 16000 // 16 kHz wideband -#define SPEEX_BIT_RATE 8000 // 8 kbps -#define SPEEX_QUALITY 4 // Quality level (0-10) -#define SPEEX_COMPLEXITY 1 // Complexity (1-10, lower for embedded) -#define SPEEX_ENCODED_BUFFER_SIZE 200 // Max encoded frame size - -bool voice_speex_init(void) { - if (s_encoder.initialized) { - return true; - } - - memset(&s_encoder, 0, sizeof(s_encoder)); - voice_agc_init(&s_encoder.agc); - - // Initialize Speex encoder - use wideband mode for 16kHz sample rate - const SpeexMode *mode = &speex_wb_mode; - if (!mode) { - VOICE_SPEEX_LOG("ERROR: Failed to get Speex wideband mode"); - return false; - } - - s_encoder.enc_state = speex_encoder_init(mode); - if (!s_encoder.enc_state) { - VOICE_SPEEX_LOG("ERROR: Failed to initialize Speex encoder"); - return false; - } - - // Initialize bits structure - speex_bits_init(&s_encoder.bits); - - // Get frame size - speex_encoder_ctl(s_encoder.enc_state, SPEEX_GET_FRAME_SIZE, &s_encoder.frame_size); - VOICE_SPEEX_LOG("Initial frame size from Speex: %"PRIu32, s_encoder.frame_size); - - // Set encoder parameters - int tmp = SPEEX_QUALITY; - speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_QUALITY, &tmp); - - tmp = SPEEX_COMPLEXITY; - speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_COMPLEXITY, &tmp); - - tmp = SPEEX_SAMPLE_RATE; - speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_SAMPLING_RATE, &tmp); - VOICE_SPEEX_LOG("Set sample rate to: %d", tmp); - - tmp = SPEEX_BIT_RATE; - speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_BITRATE, &tmp); - VOICE_SPEEX_LOG("Set bit rate to: %d", tmp); - - // Get actual parameters - int actual_sample_rate, actual_bit_rate; - speex_encoder_ctl(s_encoder.enc_state, SPEEX_GET_SAMPLING_RATE, &actual_sample_rate); - speex_encoder_ctl(s_encoder.enc_state, SPEEX_GET_BITRATE, &actual_bit_rate); - - s_encoder.sample_rate = (uint32_t)actual_sample_rate; - s_encoder.bit_rate = (uint16_t)actual_bit_rate; - - s_encoder.bitstream_version = SPEEX_BITSTREAM_VERSION; - - // Allocate frame buffer (16-bit samples) - s_encoder.frame_buffer_size = s_encoder.frame_size * sizeof(int16_t); - s_encoder.frame_buffer = (uint8_t *)kernel_malloc_check(s_encoder.frame_buffer_size); - - // Allocate encoded buffer - s_encoder.encoded_buffer_size = SPEEX_ENCODED_BUFFER_SIZE; - s_encoder.encoded_buffer = kernel_malloc_check(s_encoder.encoded_buffer_size); - - s_encoder.initialized = true; - - VOICE_SPEEX_LOG("Speex encoder initialized: sample_rate=%"PRIu32", bit_rate=%"PRIu16", frame_size=%"PRIu32, - s_encoder.sample_rate, s_encoder.bit_rate, s_encoder.frame_size); - - // Verify sample rates match - if (s_encoder.sample_rate != MIC_SAMPLE_RATE) { - VOICE_SPEEX_LOG("WARNING: Speex sample rate (%"PRIu32") != Mic sample rate (%d)", - s_encoder.sample_rate, MIC_SAMPLE_RATE); - } - - return true; -} - -void voice_speex_deinit(void) { - if (!s_encoder.initialized) { - return; - } - - if (s_encoder.enc_state) { - speex_encoder_destroy(s_encoder.enc_state); - s_encoder.enc_state = NULL; - } - - speex_bits_destroy(&s_encoder.bits); - - - if (s_encoder.frame_buffer) { - kernel_free(s_encoder.frame_buffer); - s_encoder.frame_buffer = NULL; - } - - if (s_encoder.encoded_buffer) { - kernel_free(s_encoder.encoded_buffer); - s_encoder.encoded_buffer = NULL; - } - - memset(&s_encoder, 0, sizeof(s_encoder)); -} - -void voice_speex_get_transfer_info(AudioTransferInfoSpeex *info) { - PBL_ASSERTN(s_encoder.initialized); - PBL_ASSERTN(info); - - memset(info, 0, sizeof(AudioTransferInfoSpeex)); - strncpy(info->version, "1.2.1", sizeof(info->version) - 1); - info->sample_rate = s_encoder.sample_rate; - info->bit_rate = s_encoder.bit_rate; - info->frame_size = (uint16_t)s_encoder.frame_size; // Explicit cast to uint16_t - info->bitstream_version = s_encoder.bitstream_version; - - VOICE_SPEEX_LOG("Transfer info: sample_rate=%"PRIu32", bit_rate=%"PRIu16", frame_size=%"PRIu16", bitstream_version=%"PRIu8, - info->sample_rate, info->bit_rate, info->frame_size, info->bitstream_version); - - // Additional validation - if (info->sample_rate != 16000) { - VOICE_SPEEX_LOG("WARNING: Unexpected sample rate in transfer info: %"PRIu32, info->sample_rate); - } -} - -int voice_speex_get_frame_size(void) { - return s_encoder.initialized ? (int)s_encoder.frame_size : 0; -} - -int16_t *voice_speex_get_frame_buffer(void) { - return s_encoder.initialized ? (int16_t *)s_encoder.frame_buffer : NULL; -} - -size_t voice_speex_get_frame_buffer_size(void) { - return s_encoder.initialized ? s_encoder.frame_buffer_size : 0; -} - -int voice_speex_encode_frame(int16_t *samples, uint8_t *encoded_data, size_t max_encoded_size) { - if (!s_encoder.initialized) { - VOICE_SPEEX_LOG("ERROR: encode_frame called but Speex not initialized"); - return -1; - } - - if (!samples || !encoded_data) { - VOICE_SPEEX_LOG("ERROR: encode_frame called with invalid buffers"); - return -1; - } - - voice_agc_process_frame(&s_encoder.agc, samples, s_encoder.frame_size); - - // Reset bits structure - speex_bits_reset(&s_encoder.bits); - - // Encode frame (using 32-bit samples) - speex_encode_int(s_encoder.enc_state, (spx_int16_t *)samples, &s_encoder.bits); - - // Write encoded data to buffer - int encoded_bytes = speex_bits_write(&s_encoder.bits, (char *)encoded_data, max_encoded_size); - - if (encoded_bytes < 0) { - VOICE_SPEEX_LOG("ERROR: Failed to write Speex encoded data (returned %d)", encoded_bytes); - return -1; - } - - VOICE_SPEEX_LOG("Encoded frame: input_samples=%"PRIu32", output_bytes=%d, frame_size=%"PRIu32, - s_encoder.frame_size, encoded_bytes, s_encoder.frame_size); - - return encoded_bytes; -} - -bool voice_speex_is_initialized(void) { - return s_encoder.initialized; -} diff --git a/src/fw/services/normal/voice/voice_speex.h b/src/fw/services/normal/voice/voice_speex.h deleted file mode 100644 index 5f1a13a5cc..0000000000 --- a/src/fw/services/normal/voice/voice_speex.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2025 Joshua Jun - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "services/normal/voice_endpoint.h" - -/** - * @brief Initialize the Speex encoder - * @return true if successful, false otherwise - */ -bool voice_speex_init(void); - -/** - * @brief Deinitialize the Speex encoder - */ -void voice_speex_deinit(void); - -/** - * @brief Get transfer info for audio endpoint - * @param info Pointer to AudioTransferInfoSpeex structure to fill - */ -void voice_speex_get_transfer_info(AudioTransferInfoSpeex *info); - -/** - * @brief Get the frame size in samples - * @return frame size in samples, or 0 if not initialized - */ -int voice_speex_get_frame_size(void); - -/** - * @brief Get the frame buffer for audio input - * @return pointer to frame buffer, or NULL if not initialized - */ -int16_t *voice_speex_get_frame_buffer(void); - -/** - * @brief Get the frame buffer size in bytes - * @return frame buffer size in bytes, or 0 if not initialized - */ -size_t voice_speex_get_frame_buffer_size(void); - -/** - * @brief Encode a frame of audio samples - * @param samples Pointer to 16-bit audio samples (processed in-place) - * @param encoded_data Buffer to store encoded data - * @param max_encoded_size Maximum size of encoded data buffer - * @return number of encoded bytes, or -1 on error - */ -int voice_speex_encode_frame(int16_t *samples, uint8_t *encoded_data, size_t max_encoded_size); - -/** - * @brief Check if Speex encoder is initialized - * @return true if initialized, false otherwise - */ -bool voice_speex_is_initialized(void); diff --git a/src/fw/services/normal/voice_endpoint.c b/src/fw/services/normal/voice_endpoint.c deleted file mode 100644 index de08155391..0000000000 --- a/src/fw/services/normal/voice_endpoint.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "voice_endpoint.h" - -#include "kernel/pbl_malloc.h" -#include "services/common/comm_session/session.h" -#include "services/normal/audio_endpoint.h" -#include "services/normal/voice/voice.h" -#include "system/logging.h" -#include "system/passert.h" -#include "util/generic_attribute.h" -#include "util/uuid.h" - -#include - -#include "voice_endpoint_private.h" - -#define VOICE_CONTROL_ENDPOINT (11000) - -#if CAPABILITY_HAS_MICROPHONE -static bool prv_handle_result_common(VoiceEndpointResult result, - bool app_initiated, - AudioEndpointSessionId session_id, - GenericAttributeList *attr_list, - size_t attr_list_size, - Uuid **app_uuid_out) { - - GenericAttribute *uuid_attr = generic_attribute_find_attribute(attr_list, - VEAttributeIdAppUuid, - attr_list_size); - if (app_initiated && !uuid_attr) { - PBL_LOG(LOG_LEVEL_WARNING, "No app UUID found for dictation response from app-initiated " - "session"); - voice_handle_dictation_result(VoiceEndpointResultFailInvalidMessage, session_id, NULL, - app_initiated, NULL); - return false; - } - - Uuid *app_uuid = uuid_attr ? (Uuid *)uuid_attr->data : NULL; - - if (result != VoiceEndpointResultSuccess) { - voice_handle_dictation_result(result, session_id, NULL, app_initiated, app_uuid); - return false; - } - - if (attr_list->num_attributes == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "No attributes in message"); - voice_handle_dictation_result(VoiceEndpointResultFailInvalidMessage, session_id, NULL, - app_initiated, app_uuid); - return false; - } - - *app_uuid_out = app_uuid; - return true; -} - -static void prv_handle_dictation_result(VoiceSessionResultMsg *msg, size_t size) { - const size_t attr_list_size = size - sizeof(VoiceSessionResultMsg) + sizeof(GenericAttributeList); - const bool app_initiated = (msg->flags.app_initiated == 1); - Uuid *app_uuid = NULL; - - if (!prv_handle_result_common(msg->result, app_initiated, msg->session_id, - &msg->attr_list, attr_list_size, &app_uuid)) { - return; - } - - GenericAttribute *transcription_attr = generic_attribute_find_attribute(&msg->attr_list, - VEAttributeIdTranscription, attr_list_size); - - if (!transcription_attr || transcription_attr->length == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "No transcription attribute found"); - voice_handle_dictation_result(VoiceEndpointResultFailInvalidMessage, msg->session_id, NULL, - app_initiated, app_uuid); - return; - } - - Transcription *transcription = (Transcription *)transcription_attr->data; - bool valid = transcription_validate(transcription, transcription_attr->length); - - if (!valid) { - PBL_LOG(LOG_LEVEL_WARNING, "Unrecognized transcription format received"); - voice_handle_dictation_result(VoiceEndpointResultFailInvalidRecognizerResponse, - msg->session_id, NULL, app_initiated, app_uuid); - } - voice_handle_dictation_result(msg->result, msg->session_id, transcription, - app_initiated, app_uuid); -} - -static void prv_handle_nlp_result(VoiceSessionResultMsg *msg, size_t size) { - const size_t attr_list_size = size - sizeof(VoiceSessionResultMsg) + sizeof(GenericAttributeList); - const bool app_initiated = (msg->flags.app_initiated == 1); - Uuid *app_uuid = NULL; - - if (!prv_handle_result_common(msg->result, app_initiated, msg->session_id, - &msg->attr_list, attr_list_size, &app_uuid)) { - return; - } - if (app_uuid) { - PBL_LOG(LOG_LEVEL_WARNING, "Got an app UUID in a NLP result msg. Ignoring and continuing"); - } - - - // The timestamp attribute is optional - time_t timestamp = 0; - GenericAttribute *timestamp_attr = generic_attribute_find_attribute(&msg->attr_list, - VEAttributeIdTimestamp, attr_list_size); - if (timestamp_attr && timestamp_attr->length == sizeof(uint32_t)) { - uint32_t *timestamp_ptr = (uint32_t*)timestamp_attr->data; - timestamp = *timestamp_ptr; - } - - GenericAttribute *reminder_attr = generic_attribute_find_attribute(&msg->attr_list, - VEAttributeIdReminder, attr_list_size); - - if (!reminder_attr || reminder_attr->length == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "No reminder attribute found"); - voice_handle_nlp_result(VoiceEndpointResultFailInvalidMessage, msg->session_id, NULL, 0); - return; - } - char *reminder_str = kernel_zalloc_check(reminder_attr->length + 1); - memcpy(reminder_str, reminder_attr->data, reminder_attr->length); - reminder_str[reminder_attr->length] = '\0'; - - voice_handle_nlp_result(msg->result, msg->session_id, reminder_str, timestamp); - kernel_free(reminder_str); -} -#endif - -#if CAPABILITY_HAS_MICROPHONE -void voice_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { - MsgId msg_id = data[0]; - switch (msg_id) { - case MsgIdSessionSetup: { - if (size >= sizeof(SessionSetupResultMsg)) { - SessionSetupResultMsg *msg = (SessionSetupResultMsg *) data; - bool app_initiated = (msg->flags.app_initiated == 1); - voice_handle_session_setup_result(msg->result, msg->session_type, app_initiated); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid size for session setup result message"); - } - break; - } - case MsgIdDictationResult: { - if (size >= sizeof(VoiceSessionResultMsg)) { - VoiceSessionResultMsg *msg = (VoiceSessionResultMsg *) data; - prv_handle_dictation_result(msg, size); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid size for dictation result message %zu", size); - } - break; - } - case MsgIdNLPResult: { - if (size >= sizeof(VoiceSessionResultMsg)) { - VoiceSessionResultMsg *msg = (VoiceSessionResultMsg *) data; - prv_handle_nlp_result(msg, size); - } else { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid size for dictation result message %zu", size); - } - break; - } - default: - // Ignore invalid message ID - PBL_LOG(LOG_LEVEL_WARNING, "Invalid message ID"); - break; - } - -} -#else -void voice_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { -} -#endif - -void voice_endpoint_setup_session(VoiceEndpointSessionType session_type, - AudioEndpointSessionId session_id, AudioTransferInfoSpeex *info, Uuid *app_uuid) { - - CommSession *comm_session = comm_session_get_system_session(); - comm_session_set_responsiveness(comm_session, BtConsumerPpVoiceEndpoint, ResponseTimeMin, - MIN_LATENCY_MODE_TIMEOUT_VOICE_SECS); - - // We're only sending one attribute now: the speex audio transfer info packet - size_t size = sizeof(SessionSetupMsg) + sizeof(GenericAttribute) + - sizeof(AudioTransferInfoSpeex) + - (app_uuid ? (sizeof(Uuid) + sizeof(GenericAttribute)) : 0); - SessionSetupMsg *msg = kernel_malloc_check(size); - *msg = (SessionSetupMsg) { - .msg_id = MsgIdSessionSetup, - .session_type = session_type, - .session_id = session_id, - .attr_list.num_attributes = 1, - }; - - GenericAttribute *attr = msg->attr_list.attributes; - if (app_uuid) { - // set this after struct initialization because the rest of the fields in the bitfield are left - // uninitialized if just one is set. - msg->flags.app_initiated = 1; - - // we're also sending the app UUID - msg->attr_list.num_attributes += 1; - - // add app UUID attribute - attr = generic_attribute_add_attribute(attr, VEAttributeIdAppUuid, app_uuid, sizeof(Uuid)); - } - - attr = generic_attribute_add_attribute(attr, VEAttributeIdAudioTransferInfoSpeex, info, - sizeof(AudioTransferInfoSpeex)); - - size_t actual_size = (uint8_t *)attr - (uint8_t *)msg; - PBL_ASSERTN(actual_size == size); - - comm_session_send_data(comm_session, VOICE_CONTROL_ENDPOINT, (uint8_t *)msg, - size, COMM_SESSION_DEFAULT_TIMEOUT); - kernel_free(msg); -} diff --git a/src/fw/services/normal/voice_endpoint.h b/src/fw/services/normal/voice_endpoint.h deleted file mode 100644 index bf621e3a1e..0000000000 --- a/src/fw/services/normal/voice_endpoint.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/audio_endpoint.h" -#include "services/normal/voice/transcription.h" -#include "util/attributes.h" -#include "util/uuid.h" - -#include -#include - -typedef enum { - VoiceEndpointSessionTypeDictation = 0x01, - VoiceEndpointSessionTypeCommand = 0x02, // Not used yet - VoiceEndpointSessionTypeNLP = 0x03, - - VoiceEndpointSessionTypeCount, -} VoiceEndpointSessionType; - -typedef enum { - VoiceEndpointResultSuccess = 0x00, - VoiceEndpointResultFailServiceUnavailable = 0x01, - VoiceEndpointResultFailTimeout = 0x02, - VoiceEndpointResultFailRecognizerError = 0x03, - VoiceEndpointResultFailInvalidRecognizerResponse = 0x04, - VoiceEndpointResultFailDisabled = 0x05, - VoiceEndpointResultFailInvalidMessage = 0x06, -} VoiceEndpointResult; - -// Sent before Speex encoded data -typedef struct PACKED { - char version[20]; - uint32_t sample_rate; - uint16_t bit_rate; - uint8_t bitstream_version; - uint16_t frame_size; -} AudioTransferInfoSpeex; - -//! Called by the voice service to set up a dictation or command recognition session -void voice_endpoint_setup_session(VoiceEndpointSessionType session_type, - AudioEndpointSessionId session_id, AudioTransferInfoSpeex *info, Uuid *app_uuid); diff --git a/src/fw/services/normal/voice_endpoint_private.h b/src/fw/services/normal/voice_endpoint_private.h deleted file mode 100644 index c0d3f27961..0000000000 --- a/src/fw/services/normal/voice_endpoint_private.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/audio_endpoint.h" -#include "services/normal/voice_endpoint.h" -#include "util/attributes.h" -#include "util/generic_attribute.h" - -// Shared message definitions with unit test - -typedef enum { - MsgIdSessionSetup = 0x01, - MsgIdDictationResult = 0x02, - MsgIdNLPResult = 0x03, -} MsgId; - -// Attribute ID definitions -typedef enum { - VEAttributeIdInvalid = 0x00, - VEAttributeIdAudioTransferInfoSpeex = 0x01, - VEAttributeIdTranscription = 0x02, - VEAttributeIdAppUuid = 0x03, - VEAttributeIdReminder = 0x04, - VEAttributeIdTimestamp = 0x05, -} VEAttributeId; - -// Sent and received by watch. Result is only sent by phone. - -typedef union PACKED { - struct { - uint32_t app_initiated:1; - }; - uint32_t all; -} VEFlags; - -typedef struct PACKED { - MsgId msg_id:8; - VEFlags flags; - VoiceEndpointSessionType session_type:8; - AudioEndpointSessionId session_id; - GenericAttributeList attr_list; -} SessionSetupMsg; - -typedef struct PACKED { - MsgId msg_id:8; - VEFlags flags; - VoiceEndpointSessionType session_type:8; - VoiceEndpointResult result:8; -} SessionSetupResultMsg; - -typedef struct PACKED { - MsgId msg_id:8; - VEFlags flags; - AudioEndpointSessionId session_id; - VoiceEndpointResult result:8; - GenericAttributeList attr_list; -} VoiceSessionResultMsg; diff --git a/src/fw/services/normal/wakeup.c b/src/fw/services/normal/wakeup.c deleted file mode 100644 index f3e302c653..0000000000 --- a/src/fw/services/normal/wakeup.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "wakeup.h" - -#include "popups/wakeup_ui.h" - -#include "os/mutex.h" -#include "process_management/app_install_manager.h" -#include "process_management/app_manager.h" -#include "process_management/app_storage.h" -#include "services/common/clock.h" -#include "services/common/event_service.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/settings/settings_file.h" -#include "syscall/syscall.h" -#include "syscall/syscall_internal.h" -#include "system/logging.h" -#include "util/attributes.h" -#include "util/math.h" -#include "util/units.h" - -#include "kernel/pbl_malloc.h" - -#define SETTINGS_FILE_NAME "wakeup" -// settings file => 29 bytes * 30 apps * 8 wakeup events = ~7000 bytes -// This should be more than enough space to store all the wakeup events we will ever want. -#define SETTINGS_FILE_SIZE KiBYTES(8) -// This represents the size of the buffer that is allocated to pass into the wakeup_ui -// to show that an app's wakeup event had triggered while the watch was off. To reduce -// complexity, I have hard coded this buffer to a max size instead of going the linked_list -// route. 16 apps should be more than enough. -#define NUM_APPS_ALERT_ON_BOOT 16 - -static PebbleMutex *s_mutex; - -//! Settings entries == WakeupId are stored by timestamp, -//! duplicate timestamps not allowed (can't have 2 wakeup events at same time) -//! repeating and repeat_hours_offset were included for future use -//! and use in repeat support for alarms -typedef struct PACKED { - Uuid uuid; //!< UUID of app that scheduled the wakeup event - int32_t reason; //!< App provided value to differentiate wakeup event - bool repeating; //!< Enable event repetition - uint16_t repeat_hours_offset; //!< repeat hour interval - bool notify_if_missed; //!< Notify user if wakeup event has been missed - time_t timestamp; //!< The time at which this entry will wake up at - bool utc; //!< If timezone has been set, the this is UTC time -} WakeupEntry; - -typedef struct PACKED { - WakeupId current_wakeup_id; - WakeupId next_wakeup_id; - time_t timestamp; -} WakeupState; - -struct prv_missed_events_s { - uint8_t missed_apps_count; - AppInstallId *missed_app_ids; -}; - -struct prv_check_app_and_wakeup_event_s { - time_t wakeup_timestamp; //!< Timestamp of the WakupEntry - int wakeup_count; //!< wakeup event count for app, negative for error (StatusCode) -}; - -// Local prototypes -static WakeupEntry prv_wakeup_settings_get_entry(WakeupId wakeup_id); -static void prv_wakeup_settings_delete_entry(WakeupId wakeup_id); -static StatusCode prv_wakeup_settings_add_entry(WakeupId wakeup_id, WakeupEntry entry); -static void prv_wakeup_timer_next_pending(void); - -static bool s_wakeup_enabled = false; -static TimerID s_current_timer_id = TIMER_INVALID_ID; // single timer reused for each event -// single structure containing the global wakeup state -static WakeupState s_wakeup_state = { -1, -1, 0 }; -static bool s_catchup_enabled = false; // enables catching up with missed events - -void wakeup_dispatcher_system_task(void *data){ - WakeupId wakeup_id = (WakeupId)data; - WakeupEntry entry = prv_wakeup_settings_get_entry(wakeup_id); - - // Delete event from settings - prv_wakeup_settings_delete_entry(wakeup_id); - - AppInstallId app_id = app_install_get_id_for_uuid(&entry.uuid); - - // If specified app isn't currently running, launch - if (!(app_manager_get_current_app_id() == app_id)) { - // Lookup app, and if installed, launch - if (app_id != INSTALL_ID_INVALID) { - PebbleLaunchAppEventExtended* data = - kernel_malloc_check(sizeof(PebbleLaunchAppEventExtended)); - *data = (PebbleLaunchAppEventExtended) { - .common.reason = APP_LAUNCH_WAKEUP, - .wakeup.wakeup_id = wakeup_id, - .wakeup.wakeup_reason = entry.reason, - }; - data->common.args = &data->wakeup; - - PebbleEvent event = { - .type = PEBBLE_APP_LAUNCH_EVENT, - .launch_app = { - .id = app_id, - .data = data - } - }; - - event_put(&event); - } - } else { - // If app running, send event - PebbleEvent event = { - .type = PEBBLE_WAKEUP_EVENT, - .wakeup = { - .wakeup_info = { - .wakeup_id = wakeup_id, - .wakeup_reason = entry.reason - } - } - }; - event_put(&event); - } - - prv_wakeup_timer_next_pending(); -} - -// This callback is the system dispatcher that wakes up the application by AppInstallId -// OR queues a wakeup callback for that application -// and is triggered by NewTimer -static void prv_wakeup_dispatcher(void *data) { - // Place actual work on system_task to unload it from new_timer task - system_task_add_callback(wakeup_dispatcher_system_task, data); -} - -static bool prv_find_next_wakeup_id_callback(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // Check if valid entry - if (info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { - return true; // continue iterating - } - - WakeupId wakeup_id; - info->get_key(file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); - - WakeupEntry entry; - info->get_val(file, (uint8_t*)&entry, sizeof(WakeupEntry)); - - // If the wakeup_id is valid, and the timestamp of the entry is closer than - // the timestamp of our global wakeup state, then set the next close wakeup event - if (wakeup_id > 0 && (s_wakeup_state.current_wakeup_id == -1 || - entry.timestamp < s_wakeup_state.timestamp)) { - s_wakeup_state.timestamp = entry.timestamp; - s_wakeup_state.current_wakeup_id = wakeup_id; - } - - return true; // continue iterating -} - - -// Checks for the next pending wakeup event and sets up a timer for the event -static void prv_wakeup_timer_next_pending(void) { - if (!s_wakeup_enabled) { - return; - } - - // If there is already a wakeup timer scheduled, cancel it. There will be a - // new timer scheduled for the soonest wakeup that is registered. - if (new_timer_scheduled(s_current_timer_id, NULL)) { - new_timer_stop(s_current_timer_id); - } - - mutex_lock(s_mutex); - { - // Find the next event to occur - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - // Reset wakeup state to use for the search - s_wakeup_state.current_wakeup_id = -1; - s_wakeup_state.timestamp = 0; - settings_file_each(&wakeup_settings, prv_find_next_wakeup_id_callback, NULL); - settings_file_close(&wakeup_settings); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Error: could not open APP_WAKEUP settings"); - } - } - mutex_unlock(s_mutex); - - // Create a timer for the found wakeup id, given it's valid - if (s_wakeup_state.current_wakeup_id >= 0) { - int32_t timestamp = s_wakeup_state.timestamp; - time_t current_time = rtc_get_time(); - int32_t time_difference = timestamp - current_time; - - // If time_difference is negative, this was a missed past event due to set_time - // changing current time beyond the event - // Note: this catches the edge case that there are several wakeup events skipped - // and avoids clobbering these events with a WAKEUP_CATCHUP_WINDOW gap, - // including an event occurring after missed events - if (time_difference < 0 || s_catchup_enabled) { - // next catchup_enabled state before modifying time_difference - s_catchup_enabled = (time_difference < 0) ? true : false; - // Enforces the catchup gap to be at least WAKEUP_CATCHUP_WINDOW gap - time_difference = MAX(time_difference, WAKEUP_CATCHUP_WINDOW); - } - - // timers are in milliseconds, set main callback dispatch for wakeup - // WakeupId is used to save/restore/lookup wakeup events - new_timer_start(s_current_timer_id, (time_difference * 1000), prv_wakeup_dispatcher, - (void*)((intptr_t)s_wakeup_state.current_wakeup_id), 0); - } -} - - -static void prv_migrate_events_callback(SettingsFile *old_file, SettingsFile *new_file, - SettingsRecordInfo *info, void *utc_diff) { - if (!utc_diff || info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { - return; - } - - WakeupId wakeup_id; - info->get_val(old_file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); - - WakeupEntry entry; - info->get_val(old_file, (uint8_t*)&entry, sizeof(WakeupEntry)); - - // Migrate the entries to the new timezone - if (entry.utc == false) { - entry.timestamp -= *((int*)utc_diff); - entry.utc = true; - if (wakeup_id == s_wakeup_state.current_wakeup_id) { - s_wakeup_state.timestamp = entry.timestamp; - } - } - - // Write the new entry to the settings file. We always write as there's no - // chance of it being invalid. - settings_file_set(new_file, (uint8_t*)&wakeup_id, sizeof(WakeupId), - (uint8_t*)&entry, sizeof(WakeupEntry)); -} - -static bool prv_check_for_events(SettingsFile *file, SettingsRecordInfo *info, void *context) { - bool *event_found = (bool *)context; - *event_found = true; - return false; // stop iterating -} - -static void prv_update_events_callback(SettingsFile *old_file, SettingsFile *new_file, - SettingsRecordInfo *info, void *context) { - // Check if valid entry - if (!context || info->key_len != sizeof(WakeupId)) { - return; - } - - bool struct_size_mismatch = info->val_len != sizeof(WakeupEntry); - bool struct_migration = !clock_is_timezone_set() && struct_size_mismatch; - if (struct_size_mismatch && !struct_migration) { - return; - } - - struct prv_missed_events_s *missed_events = (struct prv_missed_events_s*)context; - - WakeupId wakeup_id; - info->get_key(old_file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); - - WakeupEntry entry; - info->get_val(old_file, (uint8_t*)&entry, info->val_len); - if (struct_migration) { - entry.timestamp = wakeup_id; // WakeupId (key) is a timestamp - entry.utc = false; // If we're migrating, this has not been utc - } - - int32_t timestamp = entry.timestamp; - time_t current_time = rtc_get_time(); - int32_t time_difference = timestamp - current_time; - - if (timestamp >= s_wakeup_state.next_wakeup_id) { - s_wakeup_state.next_wakeup_id = timestamp + 1; - } else if (wakeup_id >= s_wakeup_state.next_wakeup_id) { - s_wakeup_state.next_wakeup_id = wakeup_id + 1; - } - - // schedule non-expired events - if (time_difference > 0) { - // Using settings_file_rewrite, need to write to keep key/value - settings_file_set(new_file, (uint8_t*)&wakeup_id, sizeof(WakeupId), - (uint8_t*)&entry, sizeof(WakeupEntry)); - } else { - if (entry.notify_if_missed) { - if (missed_events->missed_app_ids == NULL) { - // This is allocated here, but free'd in the wakup_ui.h module - missed_events->missed_app_ids = - kernel_malloc(NUM_APPS_ALERT_ON_BOOT * sizeof(AppInstallId)); - } - if (missed_events->missed_apps_count > NUM_APPS_ALERT_ON_BOOT) { - // We have more than NUM_APPS_ALERT_ON_BOOT apps that had events fire while the watch - // was shut off. Very rare this will happen, but just down show that the apps > 16 missed - // an event. - return; - } - // Set the AppInstallId of the app that had an alert fired - missed_events->missed_app_ids[missed_events->missed_apps_count++] = - app_install_get_id_for_uuid(&entry.uuid); - } - // Deletes the entry automatically if not written - } -} - -void wakeup_init(void) { - struct prv_missed_events_s missed_events = { 0, NULL }; - - s_mutex = mutex_create(); - - event_service_init(PEBBLE_WAKEUP_EVENT, NULL, NULL); - - // Create single reusable timer for wakeup events - s_current_timer_id = new_timer_create(); - s_wakeup_state.next_wakeup_id = rtc_get_time(); - s_wakeup_state.timestamp = -1; - - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Error: could not open wakeup settings"); - return; - } - - // Want to check if there are any events first to prevent us from rewriting the file on boot if - // we don't need to. - bool event_found = false; - settings_file_each(&wakeup_settings, prv_check_for_events, &event_found); - if (event_found) { - PBL_LOG(LOG_LEVEL_DEBUG, "Rewriting wakeup file"); - // Update settings file removing expired events - settings_file_rewrite(&wakeup_settings, prv_update_events_callback, &missed_events); - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Not rewriting wakeup file because no entries were found"); - } - settings_file_close(&wakeup_settings); - - // If wakeup events were missed by apps requesting notify_if_missed - // popup a notification window displaying these apps - if (missed_events.missed_apps_count) { - wakeup_popup_window(missed_events.missed_apps_count, missed_events.missed_app_ids); - } -} - - -static bool prv_compiled_without_utc_support(void) { - static const Version first_utc_version = { - // See list of changes in pebble_process_info.h. Apps compiled prior to this version will - // get local time returned from the time() call. - 0x5, - 0x2f - }; - Version app_sdk_version = process_metadata_get_sdk_version( - sys_process_manager_get_current_process_md()); - - if (version_compare(app_sdk_version, first_utc_version) < 0) { - return true; - } - return false; -} - - -DEFINE_SYSCALL(WakeupId, sys_wakeup_schedule, time_t timestamp, int32_t reason, - bool notify_if_missed) { - - if (prv_compiled_without_utc_support()) { - // Legacy apps get local time returned from the time() call. - timestamp = time_local_to_utc(timestamp); - } - - time_t current_time = rtc_get_time(); - int32_t time_difference = timestamp - current_time; - WakeupId wakeup_id = s_wakeup_state.next_wakeup_id++; - - // Disallow scheduling past events - if (time_difference <= 0) { - return E_INVALID_ARGUMENT; - } - - Uuid uuid = app_manager_get_current_app_md()->uuid; - - WakeupEntry entry = { - .uuid = uuid, - .reason = reason, - .notify_if_missed = notify_if_missed, - .timestamp = timestamp, - .utc = clock_is_timezone_set() - }; - - // Add to settings file - StatusCode retval = prv_wakeup_settings_add_entry(wakeup_id, entry); - - // If there was an error adding the wakeup event, return the error - if (retval < S_SUCCESS) { - return retval; - } - - // If this new event is sooner than the currently scheduled timer, make this - // the current one - prv_wakeup_timer_next_pending(); - return wakeup_id; -} - - -static void prv_wakeup_settings_delete_entry(WakeupId wakeup_id) { - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - settings_file_delete(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId)); - settings_file_close(&wakeup_settings); - } - } - mutex_unlock(s_mutex); -} - -static WakeupEntry prv_wakeup_settings_get_entry(WakeupId wakeup_id) { - WakeupEntry entry = {{0}}; - - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - settings_file_get(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId), - (uint8_t*)&entry, sizeof(WakeupEntry)); - settings_file_close(&wakeup_settings); - } - } - mutex_unlock(s_mutex); - return entry; -} - -DEFINE_SYSCALL(void, sys_wakeup_delete, WakeupId wakeup_id) { - - WakeupEntry entry = prv_wakeup_settings_get_entry(wakeup_id); - - // Only allow owner to delete its own wakeup events - if (uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { - if (wakeup_id == s_wakeup_state.current_wakeup_id && - new_timer_scheduled(s_current_timer_id, NULL)) { - new_timer_stop(s_current_timer_id); - } - prv_wakeup_settings_delete_entry(wakeup_id); - prv_wakeup_timer_next_pending(); - } -} - -static bool prv_check_count_and_availability_callback(SettingsFile *file, - SettingsRecordInfo *info, void *context) { - // Check if valid entry - if (!context || info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { - return true; // continue iterating - } - - struct prv_check_app_and_wakeup_event_s *check = (struct prv_check_app_and_wakeup_event_s*)context; - - WakeupId wakeup_id; - info->get_key(file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); - - WakeupEntry entry; - info->get_val(file, (uint8_t*)&entry, sizeof(WakeupEntry)); - - //If we have already flagged an error, just skip the rest - if (check->wakeup_count < S_SUCCESS) { - return true; // continue iterating - } - - if (uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { - check->wakeup_count++; - } - // If the wakeup_id is with the same minute as another wakeup event - if ((entry.timestamp - WAKEUP_EVENT_WINDOW < check->wakeup_timestamp) && - (check->wakeup_timestamp < (entry.timestamp + WAKEUP_EVENT_WINDOW))) { - check->wakeup_count = E_RANGE; - } - - return true; // continue iterating -} - - -static StatusCode prv_wakeup_settings_add_entry(WakeupId wakeup_id, WakeupEntry entry) { - status_t status = S_SUCCESS; - - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - // Check if current app already has MAX_WAKEUP_EVENTS_PER_APP scheduled - // or if the minute event window is already occupied - struct prv_check_app_and_wakeup_event_s check = { - .wakeup_count = 0, - .wakeup_timestamp = entry.timestamp - }; - settings_file_each(&wakeup_settings, prv_check_count_and_availability_callback, &check); - - if (check.wakeup_count < S_SUCCESS) { - status = check.wakeup_count; - } else if (check.wakeup_count >= MAX_WAKEUP_EVENTS_PER_APP) { - status = E_OUT_OF_RESOURCES; - } else { - settings_file_set(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId), - (uint8_t*)&entry, sizeof(WakeupEntry)); - } - settings_file_close(&wakeup_settings); - } else { - status = E_INTERNAL; - } - } - mutex_unlock(s_mutex); - - return status; -} - -static void prv_delete_events_by_uuid_callback(SettingsFile *old_file, SettingsFile *new_file, - SettingsRecordInfo *info, void *context) { - // Check if valid entry - if (info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { - return; - } - - WakeupId wakeup_id; - info->get_key(old_file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); - - WakeupEntry entry; - info->get_val(old_file, (uint8_t*)&entry, sizeof(WakeupEntry)); - - // If the UUID is equal, delete the entry - if (uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { - // if this is the current timer event, cancel it - if (wakeup_id == s_wakeup_state.current_wakeup_id && - new_timer_scheduled(s_current_timer_id, NULL)) { - new_timer_stop(s_current_timer_id); - } - // Deletes the entry automatically if not written - } else { // Keep the entry - // Using settings_file_rewrite, need to write to keep key/value - settings_file_set(new_file, (uint8_t*)&wakeup_id, sizeof(WakeupId), - (uint8_t*)&entry, sizeof(WakeupEntry)); - } -} - - -DEFINE_SYSCALL(void, sys_wakeup_cancel_all_for_app, void) { - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - // Update settings file removing all events with UUID = uuid - settings_file_rewrite(&wakeup_settings, prv_delete_events_by_uuid_callback, NULL); - settings_file_close(&wakeup_settings); - } - } - mutex_unlock(s_mutex); - - // We may have cancelled the timer, next_pending will check - prv_wakeup_timer_next_pending(); -} - -DEFINE_SYSCALL(time_t, sys_wakeup_query, WakeupId wakeup_id) { - status_t status = E_DOES_NOT_EXIST; - WakeupEntry entry = {}; - - if (wakeup_id < 0) { - return status; - } - - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - // Check if the wakeup id is valid by seeing if it is in the wakeup settings_file - status = settings_file_get(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId), - (uint8_t*)&entry, sizeof(WakeupEntry)); - settings_file_close(&wakeup_settings); - } else { - status = E_INTERNAL; - } - } - mutex_unlock(s_mutex); - - if (status != S_SUCCESS) { - return status; - } - - // timer doesn't belong to this app - if (!uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { - return E_DOES_NOT_EXIST; - } - - time_t return_time = entry.timestamp; - if (prv_compiled_without_utc_support()) { - // Legacy apps expect everything in local time. - return_time = time_utc_to_local(return_time); - } - return return_time; -} - -void wakeup_enable(bool enable) { - bool was_enabled = s_wakeup_enabled; - s_wakeup_enabled = enable; - if (enable && !was_enabled) { - prv_wakeup_timer_next_pending(); - } else if (!enable && s_current_timer_id && - new_timer_scheduled(s_current_timer_id, NULL)) { - new_timer_stop(s_current_timer_id); - } -} - -TimerID wakeup_get_current(void) { - return s_current_timer_id; -} - -WakeupId wakeup_get_next_scheduled(void) { - return s_wakeup_state.current_wakeup_id; -} - -void wakeup_migrate_timezone(int utc_diff) { - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - settings_file_rewrite(&wakeup_settings, prv_migrate_events_callback, (void*)&utc_diff); - settings_file_close(&wakeup_settings); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Error: could not open wakeup settings"); - } - } - mutex_unlock(s_mutex); -} - -static void prv_wakeup_rewrite_kernel_bg_cb(void *data) { - // Update each wakeup entry via prv_update_events_callback and record any missed events - struct prv_missed_events_s missed_events = { 0, NULL }; - - mutex_lock(s_mutex); - { - SettingsFile wakeup_settings; - if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { - // Update each wakeup entry via prv_update_events_callback and record any missed events - settings_file_rewrite(&wakeup_settings, prv_update_events_callback, &missed_events); - settings_file_close(&wakeup_settings); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Error: could not open wakeup settings"); - } - } - mutex_unlock(s_mutex); - - // If any events were missed due to time change display the wakeup popup - if (missed_events.missed_apps_count) { - wakeup_popup_window(missed_events.missed_apps_count, missed_events.missed_app_ids); - } - - // Setup a timer for the next wakeup event; will return if wakeup is not enabled - prv_wakeup_timer_next_pending(); -} - -void wakeup_handle_clock_change(void) { - // Offload the rewrite of the wakeup file to KernelBG as it may take a while - // - // TODO: The flash burden of this routine could also be reduced by not doing - // rewrites and instead updating records in place - if (pebble_task_get_current() == PebbleTask_KernelBackground) { - prv_wakeup_rewrite_kernel_bg_cb(NULL); - } else { - system_task_add_callback(prv_wakeup_rewrite_kernel_bg_cb, NULL); - } -} diff --git a/src/fw/services/normal/wakeup.h b/src/fw/services/normal/wakeup.h deleted file mode 100644 index 1f68227981..0000000000 --- a/src/fw/services/normal/wakeup.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "services/common/new_timer/new_timer.h" - -//! @internal -//! Event window is (in seconds) a reserved amount of time each wakeup_event receives -//! in which other wakeup events cannot be scheduled -#define WAKEUP_EVENT_WINDOW 60 -//! @internal -//! Number of wakeup events allowed per application (UUID) -#define MAX_WAKEUP_EVENTS_PER_APP 8 -//! @internal -//! Reduced event window or gap for catching up on missed events due to a time change -//! or the service being disabled by the system (Power saving mode). -#define WAKEUP_CATCHUP_WINDOW (WAKEUP_EVENT_WINDOW / 2) - -//! WakeupId is an identifier for a wakeup event -typedef int32_t WakeupId; - -//! WakeupInfo is used to pass the wakeup event id and reason -//! to the application that requested the wakeup event -typedef struct { - WakeupId wakeup_id; //!< Identifier (Timestamp) of the wakeup event - int32_t wakeup_reason; //!< App provided reason for the wakeup event -} WakeupInfo; - -//! @internal -//! This function initializes the wakeup service. -//! Triggers a popup notification for any apps that missed a -//! wakeup_event while the Pebble was off and specified -//! notify_if_missed while scheduling the event. -//! Deletes all expired wakeup_events from "wakeup" settings_file and -//! schedules the next wakeup_event using a new_timer -void wakeup_init(void); - -//! @internal -//! This function enables and disables the wakeup service. -void wakeup_enable(bool enabled); - -//! @internal -//! This function enables unit testing of the current wakeup event -TimerID wakeup_get_current(void); - -//! @internal -//! This function is used for testing and gets the next scheduled wakeup id -WakeupId wakeup_get_next_scheduled(void); - -//! @internal -//! This function is used for migrating wakeup events after a timezone set -void wakeup_migrate_timezone(int utc_diff); - -//! @internal -////! This function is used for updating wakeup events after a time change -void wakeup_handle_clock_change(void); diff --git a/src/fw/services/normal/weather/weather_service_private.h b/src/fw/services/normal/weather/weather_service_private.h deleted file mode 100644 index ba3f7578a2..0000000000 --- a/src/fw/services/normal/weather/weather_service_private.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" -#include "util/uuid.h" - -#define PREF_KEY_WEATHER_APP "weatherApp" - -typedef struct PACKED SerializedWeatherAppPrefs { - uint8_t num_locations; - Uuid locations[]; -} SerializedWeatherAppPrefs; diff --git a/src/fw/services/normal/weather/weather_types.c b/src/fw/services/normal/weather/weather_types.c deleted file mode 100644 index 5e9d69cb88..0000000000 --- a/src/fw/services/normal/weather/weather_types.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "resource/timeline_resource_ids.auto.h" - -#include "applib/graphics/gtypes.h" -#include "applib/graphics/gcolor_definitions.h" -#include "services/normal/weather/weather_types.h" -#include "util/size.h" - -// Do NOT add entries to the following arrays. See weather_type_tuples.def -static const char *s_weather_type_names[] = { -#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) #id, -#include "services/normal/weather/weather_type_tuples.def" -}; - -#if PBL_COLOR -static uint8_t s_weather_type_bg_colors[] = { -#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) bg_color, -#include "services/normal/weather/weather_type_tuples.def" -}; -#endif - -static uint8_t s_weather_type_text_colors[] = { -#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) text_color, -#include "services/normal/weather/weather_type_tuples.def" -}; - -static TimelineResourceId s_weather_type_timeline_resource_ids[] = { -#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) \ - timeline_resource_id, -#include "services/normal/weather/weather_type_tuples.def" -}; - -static const size_t s_num_weather_types = ARRAY_LENGTH(s_weather_type_names); - -static size_t prv_get_array_index_of_type(WeatherType type) { - return (type == WeatherType_Unknown) ? (s_num_weather_types - 1) : type; -} - -const char *weather_type_get_name(WeatherType weather_type) { - return s_weather_type_names[prv_get_array_index_of_type(weather_type)]; -}; - -GColor weather_type_get_bg_color(WeatherType weather_type) { -#if PBL_COLOR - const size_t index = prv_get_array_index_of_type(weather_type); -#endif - return PBL_IF_COLOR_ELSE((GColor) {.argb = s_weather_type_bg_colors[index]}, GColorClear); -}; - -GColor weather_type_get_text_color(WeatherType weather_type) { - return (GColor) {.argb = s_weather_type_text_colors[prv_get_array_index_of_type(weather_type)]}; -}; - -TimelineResourceId weather_type_get_timeline_resource_id(WeatherType weather_type) { - return s_weather_type_timeline_resource_ids[prv_get_array_index_of_type(weather_type)]; -}; diff --git a/src/fw/services/normal/weather/weather_types.h b/src/fw/services/normal/weather/weather_types.h deleted file mode 100644 index e774e819dd..0000000000 --- a/src/fw/services/normal/weather/weather_types.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Weather Types -//! -//! This file contains all the types for Weather Locations and Weather Data. -//! The weather_timestamp_utcs of all hourly data is exactly on the hour. -//! The weather_timestamp_utcs of all daily data is at midnight of the day. - -#include "applib/graphics/gtypes.h" -#include "resource/timeline_resource_ids.auto.h" - -// Do NOT add entries here. See weather_type_tuples.def -// TODO (PBL-36438): use proper enum naming -typedef enum { -#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id)\ - WeatherType_##id = numeric_id, -#include "services/normal/weather/weather_type_tuples.def" -} WeatherType; - -// ------------------------------------------------------------------------------------ -const char *weather_type_get_name(WeatherType weather_type); - -GColor weather_type_get_bg_color(WeatherType weather_type); - -GColor weather_type_get_text_color(WeatherType weather_type); - -TimelineResourceId weather_type_get_timeline_resource_id(WeatherType weather_type); diff --git a/src/fw/services/notifications/Kconfig b/src/fw/services/notifications/Kconfig new file mode 100644 index 0000000000..ce8463b690 --- /dev/null +++ b/src/fw/services/notifications/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_NOTIFICATIONS + bool "Notifications" + help + Notifications, alerts and ANCS. + +if SERVICE_NOTIFICATIONS + +module = SERVICE_NOTIFICATIONS +module-str = Notifications +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/notifications/action_chaining_window.c b/src/fw/services/notifications/action_chaining_window.c similarity index 90% rename from src/fw/services/normal/notifications/action_chaining_window.c rename to src/fw/services/notifications/action_chaining_window.c index 990ab94ed4..d814f9d3c7 100644 --- a/src/fw/services/normal/notifications/action_chaining_window.c +++ b/src/fw/services/notifications/action_chaining_window.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "action_chaining_window.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/action_chaining_window.h" #include "applib/ui/ui.h" #include "kernel/pbl_malloc.h" diff --git a/src/fw/services/notifications/alerts.c b/src/fw/services/notifications/alerts.c new file mode 100644 index 0000000000..a753bd1282 --- /dev/null +++ b/src/fw/services/notifications/alerts.c @@ -0,0 +1,124 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/alerts_private.h" + +#include "drivers/battery.h" +#include "drivers/rtc.h" +#include "kernel/low_power.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/alerts_preferences_private.h" + +static const int NOTIFICATION_VIBE_HOLDOFF_MS = 3000; +static RtcTicks s_notification_vibe_tick_timestamp = 0; + +////////////////// +// Private Functions +////////////////// + +static int64_t prv_get_ms_since_last_notification_vibe(void) { + RtcTicks current_ticks = rtc_get_ticks(); + int64_t millis_since_last_vibe = + (current_ticks - s_notification_vibe_tick_timestamp) * 1000 / RTC_TICKS_HZ; // x1000 for ms + return millis_since_last_vibe; +} + +////////////////// +// Public Functions +////////////////// + +void alerts_incoming_alert_analytics() { + if (do_not_disturb_is_active()) { + PBL_ANALYTICS_ADD(notification_received_dnd_count, 1); + } +} + +bool alerts_should_notify_for_type(AlertType type) { + if (low_power_is_active()) { + return false; + } + + if (firmware_update_is_in_progress()) { + return false; + } + + return alerts_preferences_get_alert_mask() & type; +} + +bool alerts_should_enable_backlight_for_type(AlertType type) { + if (!alerts_preferences_get_notification_backlight()) { + return false; + } + + if (do_not_disturb_is_active() && !(alerts_preferences_dnd_get_mask() & type)) { + return false; + } + + return alerts_should_notify_for_type(type); +} + +bool alerts_should_vibrate_for_type(AlertType type) { + if (do_not_disturb_is_active() && !(alerts_preferences_dnd_get_mask() & type)) { + return false; + } + + if (!alerts_should_notify_for_type(type)) { + return false; + } + + if (battery_is_usb_connected()) { + return false; + } + + if (prv_get_ms_since_last_notification_vibe() < NOTIFICATION_VIBE_HOLDOFF_MS) { + return false; + } + + return alerts_preferences_get_vibrate(); +} + +bool alerts_get_vibrate(void) { + return alerts_preferences_get_vibrate(); +} + +AlertMask alerts_get_mask(void) { + return alerts_preferences_get_alert_mask(); +} + +AlertMask alerts_get_dnd_mask(void) { + return alerts_preferences_dnd_get_mask(); +} + +uint32_t alerts_get_notification_window_timeout_ms(void) { + return alerts_preferences_get_notification_window_timeout_ms(); +} + +void alerts_set_vibrate(bool enable) { + alerts_preferences_set_vibrate(enable); +} + +void alerts_set_mask(AlertMask mask) { + alerts_preferences_set_alert_mask(mask); +} + +void alerts_set_dnd_mask(AlertMask mask) { + alerts_preferences_dnd_set_mask(mask); +} + +void alerts_set_notification_vibe_timestamp() { + // if we do vibrate, update timestamp of last vibration + s_notification_vibe_tick_timestamp = rtc_get_ticks(); +} + +void alerts_set_notification_window_timeout_ms(uint32_t timeout_ms) { + alerts_preferences_set_notification_window_timeout_ms(timeout_ms); +} + +void alerts_init() { + alerts_preferences_init(); + do_not_disturb_init(); + vibe_intensity_init(); +} diff --git a/src/fw/services/notifications/alerts_preferences.c b/src/fw/services/notifications/alerts_preferences.c new file mode 100644 index 0000000000..c93ab222cf --- /dev/null +++ b/src/fw/services/notifications/alerts_preferences.c @@ -0,0 +1,720 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/alerts_preferences_private.h" + +#include "drivers/rtc.h" +#include "popups/notifications/notification_window.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/vibes/vibe_intensity.h" +#include "system/passert.h" +#include "os/mutex.h" +#include "util/bitset.h" + +#include + +#define FILE_NAME "notifpref" +#define FILE_LEN (1024) + +static PebbleMutex *s_mutex; + +/////////////////////////////////// +//! Preference keys +/////////////////////////////////// + +#define PREF_KEY_MASK "mask" +static AlertMask s_mask = AlertMaskAllOn; + +#define PREF_KEY_DND_INTERRUPTIONS_MASK "dndInterruptionsMask" +static AlertMask s_dnd_interruptions_mask = AlertMaskAllOff; + +#define PREF_KEY_DND_SHOW_NOTIFICATIONS "dndShowNotifications" +static DndNotificationMode s_dnd_show_notifications = DndNotificationModeShow; + +#define PREF_KEY_DND_MOTION_BACKLIGHT "dndMotionBacklight" +static bool s_dnd_motion_backlight = true; + +#define PREF_KEY_DND_TOUCH_BACKLIGHT "dndTouchBacklight" +static bool s_dnd_touch_backlight = true; + +#define PREF_KEY_DND_MUTE_SPEAKER "dndMuteSpeaker" +static bool s_dnd_mute_speaker = false; + +#define PREF_KEY_SPEAKER_MUTED "speakerMuted" +static bool s_speaker_muted = false; + +#define PREF_KEY_SPEAKER_VOLUME "speakerVolume" +static uint8_t s_speaker_volume = 100; + +#define PREF_KEY_VIBE "vibe" +static bool s_vibe_on_notification = true; + +#define PREF_KEY_VIBE_INTENSITY "vibeIntensity" +static VibeIntensity s_vibe_intensity = DEFAULT_VIBE_INTENSITY; + +#define PREF_KEY_VIBE_SCORE_NOTIFICATIONS ("vibeScoreNotifications") +static VibeScoreId s_vibe_score_notifications = DEFAULT_VIBE_SCORE_NOTIFS; + +#define PREF_KEY_VIBE_SCORE_INCOMING_CALLS ("vibeScoreIncomingCalls") +static VibeScoreId s_vibe_score_incoming_calls = DEFAULT_VIBE_SCORE_INCOMING_CALLS; + +#define PREF_KEY_VIBE_SCORE_ALARMS ("vibeScoreAlarms") +static VibeScoreId s_vibe_score_alarms = DEFAULT_VIBE_SCORE_ALARMS; + +#define PREF_KEY_VIBE_SCORE_HOURLY ("vibeScoreHourly") +static VibeScoreId s_vibe_score_hourly = DEFAULT_VIBE_SCORE_HOURLY; + +#define PREF_KEY_VIBE_SCORE_ON_DISCONNECT ("vibeScoreOnDisconnect") +static VibeScoreId s_vibe_score_on_disconnect = DEFAULT_VIBE_SCORE_ON_DISCONNECT; + +#define PREF_KEY_DND_MANUALLY_ENABLED "dndManuallyEnabled" +static bool s_do_not_disturb_manually_enabled = false; + +#define PREF_KEY_DND_SMART_ENABLED "dndSmartEnabled" +static bool s_do_not_disturb_smart_dnd_enabled = false; + +#define PREF_KEY_FIRST_USE_COMPLETE "firstUseComplete" +static uint32_t s_first_use_complete = 0; + +#define PREF_KEY_NOTIF_WINDOW_TIMEOUT "notifWindowTimeout" +static uint32_t s_notif_window_timeout_ms = NOTIF_WINDOW_TIMEOUT_DEFAULT; + +#define PREF_KEY_NOTIF_DESIGN_STYLE "notifDesignStyle" +static bool s_notification_alternative_design = false; // true = alternative (black banner), false = standard (default) + +#define PREF_KEY_NOTIF_VIBE_DELAY "notifVibeDelay" +static bool s_notification_vibe_delay = true; // true = vibe at end of animation (default), false = vibe immediately + +#define PREF_KEY_NOTIF_BACKLIGHT "notifBacklight" +static bool s_notification_backlight = true; // true = enable backlight (default), false = disable backlight + +#define PREF_KEY_NOTIF_STATUS_BAR_STYLE "notifStatusBarStyle" +static NotificationStatusBarStyle s_notification_status_bar_style = NotificationStatusBarStyle_Default; + +/////////////////////////////////// +//! Legacy preference keys +/////////////////////////////////// + +#define PREF_KEY_LEGACY_DND_SCHEDULE "dndSchedule" +static DoNotDisturbSchedule s_legacy_dnd_schedule = { + .from_hour = 0, + .to_hour = 6, +}; + +#define PREF_KEY_LEGACY_DND_SCHEDULE_ENABLED "dndEnabled" +static bool s_legacy_dnd_schedule_enabled = false; + +#define PREF_KEY_LEGACY_DND_MANUAL_FIRST_USE "dndManualFirstUse" +#define PREF_KEY_LEGACY_DND_SMART_FIRST_USE "dndSmartFirstUse" + +/////////////////////////////////// +//! Variables +/////////////////////////////////// + +typedef struct DoNotDisturbScheduleConfig { + DoNotDisturbSchedule schedule; + bool enabled; +} DoNotDisturbScheduleConfig; + +typedef struct DoNotDisturbScheduleConfigKeys { + const char *schedule_pref_key; + const char *enabled_pref_key; +} DoNotDisturbScheduleConfigKeys; + +static DoNotDisturbScheduleConfig s_dnd_schedule[NumDNDSchedules]; + +static const DoNotDisturbScheduleConfigKeys s_dnd_schedule_keys[NumDNDSchedules] = { + [WeekdaySchedule] = { + .schedule_pref_key = "dndWeekdaySchedule", + .enabled_pref_key = "dndWeekdayScheduleEnabled", + }, + [WeekendSchedule] = { + .schedule_pref_key = "dndWeekendSchedule", + .enabled_pref_key = "dndWeekendScheduleEnabled", + } +}; + +static void prv_migrate_legacy_dnd_schedule(SettingsFile *file) { + // If Weekday schedule does not exist, assume that the other 3 settings files are missing as well + // Set the new schedules to the legacy schedule and delete the legacy schedule + if (!settings_file_exists(file, s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, + strlen(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key))) { +#define SET_PREF_ALREADY_OPEN(key, value) \ + settings_file_set(file, key, strlen(key), value, sizeof(value)); + + s_dnd_schedule[WeekdaySchedule].schedule = s_legacy_dnd_schedule; + SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, + &s_dnd_schedule[WeekdaySchedule].schedule); + s_dnd_schedule[WeekdaySchedule].enabled = s_legacy_dnd_schedule_enabled; + SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekdaySchedule].enabled_pref_key, + &s_dnd_schedule[WeekdaySchedule].enabled); + s_dnd_schedule[WeekendSchedule].schedule = s_legacy_dnd_schedule; + SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekendSchedule].schedule_pref_key, + &s_dnd_schedule[WeekendSchedule].schedule); + s_dnd_schedule[WeekendSchedule].enabled = s_legacy_dnd_schedule_enabled; + SET_PREF_ALREADY_OPEN(s_dnd_schedule_keys[WeekendSchedule].enabled_pref_key, + &s_dnd_schedule[WeekendSchedule].enabled); +#undef SET_PREF_ALREADY_OPEN + +#define DELETE_PREF(key) \ + do { \ + if (settings_file_exists(file, key, strlen(key))) { \ + settings_file_delete(file, key, strlen(key)); \ + } \ + } while (0) + + DELETE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE); + DELETE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE_ENABLED); +#undef DELETE_PREF + } +} + +static void prv_migrate_legacy_first_use_settings(SettingsFile *file) { + // These don't need to be initialized since settings_file_get will clear them on error + uint8_t manual_dnd_first_use_complete; + bool smart_dnd_first_use_complete; + + // Migrate the old first use dialog prefs +#define RESTORE_AND_DELETE_PREF(key, var) \ + do { \ + if (settings_file_get(file, key, strlen(key), &var, sizeof(var)) == S_SUCCESS) { \ + settings_file_delete(file, key, strlen(key)); \ + } \ + } while (0) + + RESTORE_AND_DELETE_PREF(PREF_KEY_LEGACY_DND_MANUAL_FIRST_USE, manual_dnd_first_use_complete); + RESTORE_AND_DELETE_PREF(PREF_KEY_LEGACY_DND_SMART_FIRST_USE, smart_dnd_first_use_complete); + + s_first_use_complete |= manual_dnd_first_use_complete << FirstUseSourceManualDNDActionMenu; + s_first_use_complete |= smart_dnd_first_use_complete << FirstUseSourceSmartDND; + +#undef RESTORE_AND_DELETE_PREF +} + +// Persist any vibe score values that were modified by migration. Skipping +// unchanged keys avoids bumping their settings_file timestamps on every boot, +// which would otherwise make the watch win every sync conflict against the +// phone and leave settings_blob_db's INSERT_WITH_TIMESTAMP path permanently +// rejecting the user's chosen value. +static void prv_save_changed_vibe_scores_to_file(SettingsFile *file, + VibeScoreId orig_notifications, + VibeScoreId orig_incoming_calls, + VibeScoreId orig_alarms, + VibeScoreId orig_hourly, + VibeScoreId orig_on_disconnect) { +#define SET_PREF_IF_CHANGED(key, value, orig) \ + do { \ + if ((value) != (orig)) { \ + settings_file_set(file, key, strlen(key), &(value), sizeof(value)); \ + } \ + } while (0) + + SET_PREF_IF_CHANGED(PREF_KEY_VIBE_SCORE_NOTIFICATIONS, s_vibe_score_notifications, + orig_notifications); + SET_PREF_IF_CHANGED(PREF_KEY_VIBE_SCORE_INCOMING_CALLS, s_vibe_score_incoming_calls, + orig_incoming_calls); + SET_PREF_IF_CHANGED(PREF_KEY_VIBE_SCORE_ALARMS, s_vibe_score_alarms, orig_alarms); + SET_PREF_IF_CHANGED(PREF_KEY_VIBE_SCORE_HOURLY, s_vibe_score_hourly, orig_hourly); + SET_PREF_IF_CHANGED(PREF_KEY_VIBE_SCORE_ON_DISCONNECT, s_vibe_score_on_disconnect, + orig_on_disconnect); +#undef SET_PREF_IF_CHANGED +} + +static VibeScoreId prv_return_default_if_invalid(VibeScoreId id, VibeScoreId default_id) { + return vibe_score_info_is_valid(id) ? id : default_id; +} + +// Uses the default vibe pattern id if the given score isn't valid +static void prv_ensure_valid_vibe_scores(void) { + s_vibe_score_notifications = prv_return_default_if_invalid(s_vibe_score_notifications, + DEFAULT_VIBE_SCORE_NOTIFS); + s_vibe_score_incoming_calls = prv_return_default_if_invalid(s_vibe_score_incoming_calls, + DEFAULT_VIBE_SCORE_INCOMING_CALLS); + s_vibe_score_alarms = prv_return_default_if_invalid(s_vibe_score_alarms, + DEFAULT_VIBE_SCORE_ALARMS); + s_vibe_score_hourly = prv_return_default_if_invalid(s_vibe_score_hourly, + DEFAULT_VIBE_SCORE_HOURLY); + s_vibe_score_on_disconnect = prv_return_default_if_invalid(s_vibe_score_on_disconnect, + DEFAULT_VIBE_SCORE_ON_DISCONNECT); +} + +static void prv_set_vibe_scores_based_on_legacy_intensity(VibeIntensity intensity) { + if (intensity == VibeIntensityHigh) { + s_vibe_score_notifications = VibeScoreId_StandardShortPulseHigh; + s_vibe_score_incoming_calls = VibeScoreId_StandardLongPulseHigh; + s_vibe_score_alarms = VibeScoreId_StandardLongPulseHigh; + } else { + s_vibe_score_notifications = VibeScoreId_StandardShortPulseLow; + s_vibe_score_incoming_calls = VibeScoreId_StandardLongPulseLow; + s_vibe_score_alarms = VibeScoreId_StandardLongPulseLow; + } +} + +static void prv_migrate_vibe_intensity_to_vibe_scores(SettingsFile *file) { + // We use the existence of the notifications vibe score pref as a shallow measurement of whether + // or not the user has migrated to vibe scores + const bool user_has_migrated_to_vibe_scores = + settings_file_exists(file, PREF_KEY_VIBE_SCORE_NOTIFICATIONS, + strlen(PREF_KEY_VIBE_SCORE_NOTIFICATIONS)); + + if (!user_has_migrated_to_vibe_scores) { + // If the user previously set a vibration intensity, set the vibe scores based on that intensity + if (settings_file_exists(file, PREF_KEY_VIBE_INTENSITY, strlen(PREF_KEY_VIBE_INTENSITY))) { + prv_set_vibe_scores_based_on_legacy_intensity(s_vibe_intensity); + } else if (rtc_is_timezone_set()) { + // Otherwise, if the timezone has been set, then we assume this is a user on 3.10 and lower + // that has not touched their vibe intensity preferences. + // rtc_is_timezone_set() was chosen because it is a setting that gets written when the user + // connects their watch to a phone + prv_set_vibe_scores_based_on_legacy_intensity(DEFAULT_VIBE_INTENSITY); + } + } + + // PREF_KEY_VIBE, which used to track whether the user enabled/disabled vibrations, has been + // deprecated in favor of the "disabled vibe score", VibeScoreId_Disabled, so switch to using it + // and delete PREF_KEY_VIBE from the settings file if PREF_KEY_VIBE exists in the settings file + if (settings_file_exists(file, PREF_KEY_VIBE, strlen(PREF_KEY_VIBE))) { + if (!s_vibe_on_notification) { + s_vibe_score_notifications = VibeScoreId_Disabled; + s_vibe_score_incoming_calls = VibeScoreId_Disabled; + } + settings_file_delete(file, PREF_KEY_VIBE, strlen(PREF_KEY_VIBE)); + } +} + +void alerts_preferences_init(void) { + s_mutex = mutex_create(); + + SettingsFile file = {{0}}; + if (settings_file_open(&file, FILE_NAME, FILE_LEN) != S_SUCCESS) { + return; + } + + // The notif-prefs file may contain records keyed with strlen() (local writes) + // or strlen()+1 (legacy phone-pushed records from before settings_blob_db + // canonicalised the key length). Settings file lookups match key_len exactly, + // so probe both lengths to remain backwards compatible with records written + // by older firmware. +#define RESTORE_PREF(key, var) \ + do { \ + __typeof__(var) _tmp; \ + if (settings_file_get( \ + &file, key, strlen(key), &_tmp, sizeof(_tmp)) == S_SUCCESS || \ + settings_file_get( \ + &file, key, strlen(key) + 1, &_tmp, sizeof(_tmp)) == S_SUCCESS) { \ + var = _tmp; \ + } \ + } while (0) + + RESTORE_PREF(PREF_KEY_MASK, s_mask); + RESTORE_PREF(PREF_KEY_VIBE, s_vibe_on_notification); + RESTORE_PREF(PREF_KEY_VIBE_INTENSITY, s_vibe_intensity); + RESTORE_PREF(PREF_KEY_VIBE_SCORE_NOTIFICATIONS, s_vibe_score_notifications); + RESTORE_PREF(PREF_KEY_VIBE_SCORE_INCOMING_CALLS, s_vibe_score_incoming_calls); + RESTORE_PREF(PREF_KEY_VIBE_SCORE_ALARMS, s_vibe_score_alarms); + RESTORE_PREF(PREF_KEY_VIBE_SCORE_HOURLY, s_vibe_score_hourly); + RESTORE_PREF(PREF_KEY_VIBE_SCORE_ON_DISCONNECT, s_vibe_score_on_disconnect); + RESTORE_PREF(PREF_KEY_DND_MANUALLY_ENABLED, s_do_not_disturb_manually_enabled); + RESTORE_PREF(PREF_KEY_DND_SMART_ENABLED, s_do_not_disturb_smart_dnd_enabled); + RESTORE_PREF(PREF_KEY_DND_INTERRUPTIONS_MASK, s_dnd_interruptions_mask); + RESTORE_PREF(PREF_KEY_DND_SHOW_NOTIFICATIONS, s_dnd_show_notifications); + RESTORE_PREF(PREF_KEY_DND_MOTION_BACKLIGHT, s_dnd_motion_backlight); + RESTORE_PREF(PREF_KEY_DND_TOUCH_BACKLIGHT, s_dnd_touch_backlight); + RESTORE_PREF(PREF_KEY_DND_MUTE_SPEAKER, s_dnd_mute_speaker); + RESTORE_PREF(PREF_KEY_SPEAKER_MUTED, s_speaker_muted); + RESTORE_PREF(PREF_KEY_SPEAKER_VOLUME, s_speaker_volume); + RESTORE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE, s_legacy_dnd_schedule); + RESTORE_PREF(PREF_KEY_LEGACY_DND_SCHEDULE_ENABLED, s_legacy_dnd_schedule_enabled); + RESTORE_PREF(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, + s_dnd_schedule[WeekdaySchedule].schedule); + RESTORE_PREF(s_dnd_schedule_keys[WeekdaySchedule].enabled_pref_key, + s_dnd_schedule[WeekdaySchedule].enabled); + RESTORE_PREF(s_dnd_schedule_keys[WeekendSchedule].schedule_pref_key, + s_dnd_schedule[WeekendSchedule].schedule); + RESTORE_PREF(s_dnd_schedule_keys[WeekendSchedule].enabled_pref_key, + s_dnd_schedule[WeekendSchedule].enabled); + RESTORE_PREF(PREF_KEY_FIRST_USE_COMPLETE, s_first_use_complete); + RESTORE_PREF(PREF_KEY_NOTIF_WINDOW_TIMEOUT, s_notif_window_timeout_ms); + RESTORE_PREF(PREF_KEY_NOTIF_DESIGN_STYLE, s_notification_alternative_design); + RESTORE_PREF(PREF_KEY_NOTIF_VIBE_DELAY, s_notification_vibe_delay); + RESTORE_PREF(PREF_KEY_NOTIF_BACKLIGHT, s_notification_backlight); + RESTORE_PREF(PREF_KEY_NOTIF_STATUS_BAR_STYLE, s_notification_status_bar_style); +#undef RESTORE_PREF + + prv_migrate_legacy_dnd_schedule(&file); + + const VibeScoreId orig_vibe_score_notifications = s_vibe_score_notifications; + const VibeScoreId orig_vibe_score_incoming_calls = s_vibe_score_incoming_calls; + const VibeScoreId orig_vibe_score_alarms = s_vibe_score_alarms; + const VibeScoreId orig_vibe_score_hourly = s_vibe_score_hourly; + const VibeScoreId orig_vibe_score_on_disconnect = s_vibe_score_on_disconnect; + + prv_migrate_legacy_first_use_settings(&file); + prv_migrate_vibe_intensity_to_vibe_scores(&file); + prv_ensure_valid_vibe_scores(); + prv_save_changed_vibe_scores_to_file(&file, orig_vibe_score_notifications, + orig_vibe_score_incoming_calls, + orig_vibe_score_alarms, + orig_vibe_score_hourly, + orig_vibe_score_on_disconnect); + + settings_file_close(&file); +} + +// Convenience macro for setting a string key to a non-pointer value. +#define SET_PREF(key, value) \ + prv_set_pref(key, strlen(key), &value, sizeof(value)) +static void prv_set_pref(const void *key, size_t key_len, const void *value, + size_t value_len) { + mutex_lock(s_mutex); + SettingsFile file = {{0}}; + if (settings_file_open(&file, FILE_NAME, FILE_LEN) != S_SUCCESS) { + goto cleanup; + } + settings_file_set(&file, key, key_len, value, value_len); + settings_file_close(&file); +cleanup: + mutex_unlock(s_mutex); +} + +AlertMask alerts_preferences_get_alert_mask(void) { + if (s_mask == AlertMaskAllOnLegacy) { + // Migration for notification settings previously configured under + // old bit setup. + alerts_preferences_set_alert_mask(AlertMaskAllOn); + } + return s_mask; +} + +void alerts_preferences_set_alert_mask(AlertMask mask) { + s_mask = mask; + SET_PREF(PREF_KEY_MASK, s_mask); +} + +uint32_t alerts_preferences_get_notification_window_timeout_ms(void) { + return s_notif_window_timeout_ms; +} + +void alerts_preferences_set_notification_window_timeout_ms(uint32_t timeout_ms) { + s_notif_window_timeout_ms = timeout_ms; + SET_PREF(PREF_KEY_NOTIF_WINDOW_TIMEOUT, s_notif_window_timeout_ms); +} + +bool alerts_preferences_get_notification_alternative_design(void) { + return s_notification_alternative_design; +} + +void alerts_preferences_set_notification_alternative_design(bool alternative) { + s_notification_alternative_design = alternative; + SET_PREF(PREF_KEY_NOTIF_DESIGN_STYLE, s_notification_alternative_design); +} + +bool alerts_preferences_get_notification_vibe_delay(void) { + return s_notification_vibe_delay; +} + +void alerts_preferences_set_notification_vibe_delay(bool delay) { + s_notification_vibe_delay = delay; + SET_PREF(PREF_KEY_NOTIF_VIBE_DELAY, s_notification_vibe_delay); +} + +bool alerts_preferences_get_notification_backlight(void) { + return s_notification_backlight; +} + +void alerts_preferences_set_notification_backlight(bool enable) { + s_notification_backlight = enable; + SET_PREF(PREF_KEY_NOTIF_BACKLIGHT, s_notification_backlight); +} + +NotificationStatusBarStyle alerts_preferences_get_notification_status_bar_style(void) { + return s_notification_status_bar_style; +} + +void alerts_preferences_set_notification_status_bar_style(NotificationStatusBarStyle style) { + s_notification_status_bar_style = style; + SET_PREF(PREF_KEY_NOTIF_STATUS_BAR_STYLE, s_notification_status_bar_style); +} + +bool alerts_preferences_get_speaker_muted(void) { + return s_speaker_muted; +} + +void alerts_preferences_set_speaker_muted(bool muted) { + s_speaker_muted = muted; + SET_PREF(PREF_KEY_SPEAKER_MUTED, s_speaker_muted); +} + +uint8_t alerts_preferences_get_speaker_volume(void) { + return s_speaker_volume; +} + +void alerts_preferences_set_speaker_volume(uint8_t volume) { + if (volume > 100) { + volume = 100; + } + s_speaker_volume = volume; + SET_PREF(PREF_KEY_SPEAKER_VOLUME, s_speaker_volume); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! Vibes + +bool alerts_preferences_get_vibrate(void) { + return s_vibe_on_notification; +} + +void alerts_preferences_set_vibrate(bool enable) { + s_vibe_on_notification = enable; + SET_PREF(PREF_KEY_VIBE, s_vibe_on_notification); +} + +VibeIntensity alerts_preferences_get_vibe_intensity(void) { + return s_vibe_intensity; +} + +void alerts_preferences_set_vibe_intensity(VibeIntensity intensity) { + s_vibe_intensity = intensity; + SET_PREF(PREF_KEY_VIBE_INTENSITY, s_vibe_intensity); +} + +VibeScoreId alerts_preferences_get_vibe_score_for_client(VibeClient client) { + switch (client) { + case VibeClient_Notifications: + return s_vibe_score_notifications; + case VibeClient_PhoneCalls: + return s_vibe_score_incoming_calls; + case VibeClient_Alarms: + return s_vibe_score_alarms; + case VibeClient_Hourly: + return s_vibe_score_hourly; + case VibeClient_OnDisconnect: + return s_vibe_score_on_disconnect; + default: + WTF; + } +} + +void alerts_preferences_set_vibe_score_for_client(VibeClient client, VibeScoreId id) { + const char *key = NULL; + switch (client) { + case VibeClient_Notifications: { + s_vibe_score_notifications = id; + key = PREF_KEY_VIBE_SCORE_NOTIFICATIONS; + break; + } + case VibeClient_PhoneCalls: { + s_vibe_score_incoming_calls = id; + key = PREF_KEY_VIBE_SCORE_INCOMING_CALLS; + break; + } + case VibeClient_Alarms: { + s_vibe_score_alarms = id; + key = PREF_KEY_VIBE_SCORE_ALARMS; + break; + } + case VibeClient_Hourly: { + s_vibe_score_hourly = id; + key = PREF_KEY_VIBE_SCORE_HOURLY; + break; + } + case VibeClient_OnDisconnect: { + s_vibe_score_on_disconnect = id; + key = PREF_KEY_VIBE_SCORE_ON_DISCONNECT; + break; + } + default: { + WTF; + } + } + SET_PREF(key, id); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +//! DND + +void alerts_preferences_dnd_set_mask(AlertMask mask) { + s_dnd_interruptions_mask = mask; + SET_PREF(PREF_KEY_DND_INTERRUPTIONS_MASK, s_dnd_interruptions_mask); +} + +AlertMask alerts_preferences_dnd_get_mask(void) { + return s_dnd_interruptions_mask; +} + +void alerts_preferences_dnd_set_show_notifications(DndNotificationMode mode) { + s_dnd_show_notifications = mode; + SET_PREF(PREF_KEY_DND_SHOW_NOTIFICATIONS, s_dnd_show_notifications); +} + +DndNotificationMode alerts_preferences_dnd_get_show_notifications(void) { + return s_dnd_show_notifications; +} + +void alerts_preferences_dnd_set_motion_backlight(bool enable) { + s_dnd_motion_backlight = enable; + SET_PREF(PREF_KEY_DND_MOTION_BACKLIGHT, s_dnd_motion_backlight); +} + +bool alerts_preferences_dnd_get_motion_backlight(void) { + return s_dnd_motion_backlight; +} + +void alerts_preferences_dnd_set_touch_backlight(bool enable) { + s_dnd_touch_backlight = enable; + SET_PREF(PREF_KEY_DND_TOUCH_BACKLIGHT, s_dnd_touch_backlight); +} + +bool alerts_preferences_dnd_get_touch_backlight(void) { + return s_dnd_touch_backlight; +} + +void alerts_preferences_dnd_set_mute_speaker(bool enable) { + s_dnd_mute_speaker = enable; + SET_PREF(PREF_KEY_DND_MUTE_SPEAKER, s_dnd_mute_speaker); +} + +bool alerts_preferences_dnd_get_mute_speaker(void) { + return s_dnd_mute_speaker; +} + +bool alerts_preferences_dnd_is_manually_enabled(void) { + return s_do_not_disturb_manually_enabled; +} + +void alerts_preferences_dnd_set_manually_enabled(bool enable) { + s_do_not_disturb_manually_enabled = enable; + SET_PREF(PREF_KEY_DND_MANUALLY_ENABLED, s_do_not_disturb_manually_enabled); +} + +void alerts_preferences_dnd_get_schedule(DoNotDisturbScheduleType type, + DoNotDisturbSchedule *schedule_out) { + *schedule_out = s_dnd_schedule[type].schedule; +}; + +void alerts_preferences_dnd_set_schedule(DoNotDisturbScheduleType type, + const DoNotDisturbSchedule *schedule) { + s_dnd_schedule[type].schedule = *schedule; + SET_PREF(s_dnd_schedule_keys[type].schedule_pref_key, s_dnd_schedule[type].schedule); +}; + +bool alerts_preferences_dnd_is_schedule_enabled(DoNotDisturbScheduleType type) { + return s_dnd_schedule[type].enabled; +} + +void alerts_preferences_dnd_set_schedule_enabled(DoNotDisturbScheduleType type, bool on) { + s_dnd_schedule[type].enabled = on; + SET_PREF(s_dnd_schedule_keys[type].enabled_pref_key, s_dnd_schedule[type].enabled); +} + +bool alerts_preferences_check_and_set_first_use_complete(FirstUseSource source) { + if (s_first_use_complete & (1 << source)) { + return true; + }; + + s_first_use_complete |= (1 << source); + SET_PREF(PREF_KEY_FIRST_USE_COMPLETE, s_first_use_complete); + return false; +} + +bool alerts_preferences_dnd_is_smart_enabled(void) { + return s_do_not_disturb_smart_dnd_enabled; +} + +void alerts_preferences_dnd_set_smart_enabled(bool enable) { + s_do_not_disturb_smart_dnd_enabled = enable; + SET_PREF(PREF_KEY_DND_SMART_ENABLED, s_do_not_disturb_smart_dnd_enabled); +} + +void alerts_preferences_lock(void) { + mutex_lock(s_mutex); +} + +void alerts_preferences_unlock(void) { + mutex_unlock(s_mutex); +} + +void alerts_preferences_handle_blob_db_event(PebbleBlobDBEvent *event) { + if (event->type != BlobDBEventTypeInsert) { + return; + } + + const uint8_t *key = event->key; + int key_len = event->key_len; + const char *matched_key = NULL; + + mutex_lock(s_mutex); + + SettingsFile file = {{0}}; + if (settings_file_open(&file, FILE_NAME, FILE_LEN) != S_SUCCESS) { + mutex_unlock(s_mutex); + return; + } + + // Helper macro to reload a single preference from file if key matches + // key_len may or may not include the null terminator depending on BlobDB protocol + // IMPORTANT: settings_file_get uses exact key_len matching, so we must use the same + // key_len that was used when writing the record (i.e., key_len from the event) +#define RELOAD_IF_MATCH(pref_key, var) \ + do { \ + size_t _pref_strlen = strlen(pref_key); \ + if ((key_len == (int)_pref_strlen || key_len == (int)(_pref_strlen + 1)) && \ + memcmp(key, pref_key, _pref_strlen) == 0) { \ + __typeof__(var) _tmp; \ + if (settings_file_get(&file, key, key_len, &_tmp, sizeof(_tmp)) == S_SUCCESS) { \ + var = _tmp; \ + matched_key = pref_key; \ + } \ + goto done; \ + } \ + } while (0) + + RELOAD_IF_MATCH(PREF_KEY_MASK, s_mask); + RELOAD_IF_MATCH(PREF_KEY_DND_INTERRUPTIONS_MASK, s_dnd_interruptions_mask); + RELOAD_IF_MATCH(PREF_KEY_DND_SHOW_NOTIFICATIONS, s_dnd_show_notifications); + RELOAD_IF_MATCH(PREF_KEY_VIBE_INTENSITY, s_vibe_intensity); + RELOAD_IF_MATCH(PREF_KEY_VIBE_SCORE_NOTIFICATIONS, s_vibe_score_notifications); + RELOAD_IF_MATCH(PREF_KEY_VIBE_SCORE_INCOMING_CALLS, s_vibe_score_incoming_calls); + RELOAD_IF_MATCH(PREF_KEY_VIBE_SCORE_ALARMS, s_vibe_score_alarms); + RELOAD_IF_MATCH(PREF_KEY_DND_MANUALLY_ENABLED, s_do_not_disturb_manually_enabled); + RELOAD_IF_MATCH(PREF_KEY_DND_SMART_ENABLED, s_do_not_disturb_smart_dnd_enabled); + RELOAD_IF_MATCH(s_dnd_schedule_keys[WeekdaySchedule].schedule_pref_key, + s_dnd_schedule[WeekdaySchedule].schedule); + RELOAD_IF_MATCH(s_dnd_schedule_keys[WeekdaySchedule].enabled_pref_key, + s_dnd_schedule[WeekdaySchedule].enabled); + RELOAD_IF_MATCH(s_dnd_schedule_keys[WeekendSchedule].schedule_pref_key, + s_dnd_schedule[WeekendSchedule].schedule); + RELOAD_IF_MATCH(s_dnd_schedule_keys[WeekendSchedule].enabled_pref_key, + s_dnd_schedule[WeekendSchedule].enabled); + RELOAD_IF_MATCH(PREF_KEY_NOTIF_WINDOW_TIMEOUT, s_notif_window_timeout_ms); + RELOAD_IF_MATCH(PREF_KEY_NOTIF_DESIGN_STYLE, s_notification_alternative_design); + RELOAD_IF_MATCH(PREF_KEY_NOTIF_VIBE_DELAY, s_notification_vibe_delay); + RELOAD_IF_MATCH(PREF_KEY_NOTIF_BACKLIGHT, s_notification_backlight); + RELOAD_IF_MATCH(PREF_KEY_NOTIF_STATUS_BAR_STYLE, s_notification_status_bar_style); + RELOAD_IF_MATCH(PREF_KEY_DND_MOTION_BACKLIGHT, s_dnd_motion_backlight); + RELOAD_IF_MATCH(PREF_KEY_DND_TOUCH_BACKLIGHT, s_dnd_touch_backlight); + RELOAD_IF_MATCH(PREF_KEY_DND_MUTE_SPEAKER, s_dnd_mute_speaker); + RELOAD_IF_MATCH(PREF_KEY_SPEAKER_MUTED, s_speaker_muted); + RELOAD_IF_MATCH(PREF_KEY_SPEAKER_VOLUME, s_speaker_volume); + +#undef RELOAD_IF_MATCH + +done: + settings_file_close(&file); + mutex_unlock(s_mutex); + + // Notify UI that a preference changed so it can refresh + if (matched_key) { + PebbleEvent pref_event = { + .type = PEBBLE_PREF_CHANGE_EVENT, + .pref_change = { + .key = matched_key, + .key_len = strlen(matched_key) + 1, + }, + }; + event_put(&pref_event); + } +} \ No newline at end of file diff --git a/src/fw/services/notifications/ancs/ancs_filtering.c b/src/fw/services/notifications/ancs/ancs_filtering.c new file mode 100644 index 0000000000..7942e44617 --- /dev/null +++ b/src/fw/services/notifications/ancs/ancs_filtering.c @@ -0,0 +1,331 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/ancs/ancs_filtering.h" + +#include "drivers/rtc.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/timeline/attributes_actions.h" +#include "system/logging.h" +#include "util/pstring.h" + +#include + +PBL_LOG_MODULE_DECLARE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + +typedef enum { + FilteringMatchTypeText = 0, + FilteringMatchTypeRegex = 1, +} FilteringMatchType; + +typedef enum { + FilteringMatchFieldAny = 0, + FilteringMatchFieldTitle = 1, + FilteringMatchFieldBody = 2, +} FilteringMatchField; + +typedef struct PACKED { + uint8_t count; + uint8_t data[]; +} FilteringRulesSerialized; + +typedef struct PACKED { + uint8_t match_type; + uint8_t match_field; + uint8_t case_sensitive; + char pattern[]; +} FilteringRuleSerialized; + +static char prv_ascii_to_lower(char c) { + if ((c >= 'A') && (c <= 'Z')) { + return (char)(c + ('a' - 'A')); + } + return c; +} + +static bool prv_match_contains(const char *haystack, size_t haystack_len, const char *needle, + size_t needle_len, bool case_sensitive) { + if (needle_len == 0) { + return true; + } + if (haystack_len < needle_len) { + return false; + } + + for (size_t i = 0; i <= (haystack_len - needle_len); i++) { + bool matched = true; + for (size_t j = 0; j < needle_len; j++) { + char h = haystack[i + j]; + char n = needle[j]; + if (!case_sensitive) { + h = prv_ascii_to_lower(h); + n = prv_ascii_to_lower(n); + } + if (h != n) { + matched = false; + break; + } + } + if (matched) { + return true; + } + } + return false; +} + +static char *prv_attr_to_cstring(const ANCSAttribute *attr, size_t *len_out) { + if (!attr || (attr->length == 0)) { + *len_out = 0; + return NULL; + } + char *str = kernel_zalloc_check(attr->length + 1); + pstring_pstring16_to_string(&attr->pstr, str); + *len_out = strlen(str); + return str; +} + +static bool prv_match_rule(uint8_t match_type, uint8_t match_field, bool case_sensitive, + const char *pattern, size_t pattern_len, const char *title, + size_t title_len, const char *subtitle, size_t subtitle_len, + const char *body, size_t body_len) { + if (match_type == FilteringMatchTypeRegex) { + // Regex support is not available in firmware at the moment. + return false; + } + + const bool match_title = + ((match_field == FilteringMatchFieldTitle) || (match_field == FilteringMatchFieldAny)); + const bool match_body = + ((match_field == FilteringMatchFieldBody) || (match_field == FilteringMatchFieldAny)); + + // The subtitle is displayed as part of the title line, so title rules cover + // both attributes. + return ((match_title && + (prv_match_contains(title, title_len, pattern, pattern_len, case_sensitive) || + prv_match_contains(subtitle, subtitle_len, pattern, pattern_len, case_sensitive))) + || (match_body && prv_match_contains(body, body_len, pattern, pattern_len, case_sensitive))); +} + +bool ancs_filtering_matches_rules(const iOSNotifPrefs *app_notif_prefs, + const ANCSAttribute *title_attr, + const ANCSAttribute *subtitle_attr, + const ANCSAttribute *body_attr) { + if (!app_notif_prefs) { + return false; + } + + StringList *rules = attribute_get_string_list(&app_notif_prefs->attr_list, + AttributeIdNotificationFilteringRules); + if (!rules || (rules->serialized_byte_length == 0)) { + return false; + } + + size_t title_len = 0; + char *allocated_title = prv_attr_to_cstring(title_attr, &title_len); + const char *title = allocated_title ? allocated_title : ""; + + size_t subtitle_len = 0; + char *allocated_subtitle = prv_attr_to_cstring(subtitle_attr, &subtitle_len); + const char *subtitle = allocated_subtitle ? allocated_subtitle : ""; + + size_t body_len = 0; + char *allocated_body = prv_attr_to_cstring(body_attr, &body_len); + const char *body = allocated_body ? allocated_body : ""; + + const FilteringRulesSerialized *serialized_rules = (const FilteringRulesSerialized *)rules->data; + size_t remaining = rules->serialized_byte_length - 1; + const uint8_t *cursor = serialized_rules->data; + + bool matched = false; + for (uint8_t i = 0; i < serialized_rules->count; i++) { + if (remaining < sizeof(FilteringRuleSerialized)) { + break; + } + + const FilteringRuleSerialized *rule = (const FilteringRuleSerialized *)cursor; + cursor += sizeof(FilteringRuleSerialized); + remaining -= sizeof(FilteringRuleSerialized); + + const char *pattern = (const char *)cursor; + const char *terminator = memchr(cursor, '\0', remaining); + if (!terminator) { + break; + } + + const size_t pattern_len = (size_t)(terminator - pattern); + matched = prv_match_rule(rule->match_type, rule->match_field, (rule->case_sensitive != 0), + pattern, pattern_len, title, title_len, subtitle, subtitle_len, + body, body_len); + + cursor = (const uint8_t *)(terminator + 1); + remaining -= (pattern_len + 1); + + if (matched) { + break; + } + } + + kernel_free(allocated_body); + kernel_free(allocated_subtitle); + kernel_free(allocated_title); + return matched; +} + +void ancs_filtering_record_app(iOSNotifPrefs **notif_prefs, + const ANCSAttribute *app_id, + const ANCSAttribute *display_name, + const ANCSAttribute *title) { + // When we receive a notification, information about the app that sent us the notification + // is recorded in the notif_pref_db. We sync this DB with the phone which allows us to + // do things like add non ANCS actions, or filter notifications by app + + // The "default" attributes are merged with any existing attributes. This makes it easy to add + // new attributes in the future as well as support EMail / SMS apps which already have data + // stored. + + iOSNotifPrefs *app_notif_prefs = *notif_prefs; + const int num_existing_attribtues = app_notif_prefs ? app_notif_prefs->attr_list.num_attributes : + 0; + + AttributeList new_attr_list; + attribute_list_init_list(num_existing_attribtues, &new_attr_list); + bool list_dirty = false; + + // Copy over all the existing attributes to our new list + if (app_notif_prefs) { + for (int i = 0; i < num_existing_attribtues; i++) { + new_attr_list.attributes[i] = app_notif_prefs->attr_list.attributes[i]; + } + } + + // The app name should be the display name + // If there is no display name (Apple Pay) then fallback to the title + const ANCSAttribute *app_name_attr = NULL; + if (display_name && display_name->length > 0) { + app_name_attr = display_name; + } else if (title && title->length > 0) { + app_name_attr = title; + } + + char *app_name_buff = NULL; + if (app_name_attr) { + const char *existing_name = ""; + if (app_notif_prefs) { + existing_name = attribute_get_string(&app_notif_prefs->attr_list, AttributeIdAppName, ""); + } + + if (!pstring_equal_cstring(&app_name_attr->pstr, existing_name)) { + // If the existing name doesn't match our new name, update the name + app_name_buff = kernel_zalloc_check(app_name_attr->length + 1); + pstring_pstring16_to_string(&app_name_attr->pstr, app_name_buff); + attribute_list_add_cstring(&new_attr_list, AttributeIdAppName, app_name_buff); + list_dirty = true; + PBL_LOG_INFO("Adding app name to app prefs: <%s>", app_name_buff); + } + } + + // Add the mute attribute if we don't have one already + // Default the app to not muted + const bool already_has_mute = + app_notif_prefs && attribute_find(&app_notif_prefs->attr_list, AttributeIdMuteDayOfWeek); + if (!already_has_mute) { + attribute_list_add_uint8(&new_attr_list, AttributeIdMuteDayOfWeek, MuteBitfield_None); + list_dirty = true; + } + + // Add the mute expiration attribute if we don't have one already + // Default to no expiration (0 means not muted by time) + if (!attribute_find(&new_attr_list, AttributeIdMuteExpiration)) { + uint32_t expiration_value = 0; + if (app_notif_prefs) { + expiration_value = attribute_get_uint32(&app_notif_prefs->attr_list, AttributeIdMuteExpiration, 0); + } + attribute_list_add_uint32(&new_attr_list, AttributeIdMuteExpiration, expiration_value); + list_dirty = true; + } + + StringList *rules_attr = NULL; + if (app_notif_prefs) { + rules_attr = attribute_get_string_list(&app_notif_prefs->attr_list, + AttributeIdNotificationFilteringRules); + } + StringList *default_rules = NULL; + if (!rules_attr) { + default_rules = kernel_zalloc_check(sizeof(StringList) + sizeof(uint8_t)); + default_rules->serialized_byte_length = sizeof(uint8_t); + default_rules->data[0] = 0; // zero filtering rules by default + attribute_list_add_string_list(&new_attr_list, AttributeIdNotificationFilteringRules, + default_rules); + list_dirty = true; + } + + // Add / update the "last seen" timestamp + Attribute *last_updated = NULL; + if (app_notif_prefs) { + last_updated = attribute_find(&app_notif_prefs->attr_list, AttributeIdLastUpdated); + } + uint32_t now = rtc_get_time(); + // Only perform an update if there is no timestamp or the current timestamp is more than a day old + if (!last_updated || + (last_updated && now > (last_updated->uint32 + SECONDS_PER_DAY))) { + attribute_list_add_uint32(&new_attr_list, AttributeIdLastUpdated, now); + list_dirty = true; + PBL_LOG_INFO("Updating / adding timestamp to app prefs"); + } + + if (list_dirty) { + // We don't change or add actions at this time + TimelineItemActionGroup *new_action_group = NULL; + if (app_notif_prefs) { + new_action_group = &app_notif_prefs->action_group; + } + + ios_notif_pref_db_store_prefs(app_id->value, app_id->length, + &new_attr_list, new_action_group); + + // Update our copy of the prefs with the new data + const size_t buf_size = attributes_actions_get_buffer_size(&new_attr_list, new_action_group); + *notif_prefs = kernel_zalloc_check(sizeof(iOSNotifPrefs) + buf_size); + uint8_t *buffer = (uint8_t*)*notif_prefs + sizeof(iOSNotifPrefs); + + attributes_actions_deep_copy(&new_attr_list, &(*notif_prefs)->attr_list, new_action_group, + &(*notif_prefs)->action_group, buffer, buffer + buf_size); + ios_notif_pref_db_free_prefs(app_notif_prefs); + } + + + kernel_free(app_name_buff); + kernel_free(default_rules); + attribute_list_destroy_list(&new_attr_list); +} + +uint8_t ancs_filtering_get_mute_type(const iOSNotifPrefs *app_notif_prefs) { + if (app_notif_prefs) { + return attribute_get_uint8(&app_notif_prefs->attr_list, + AttributeIdMuteDayOfWeek, + MuteBitfield_None); + } + + return MuteBitfield_None; +} + +uint32_t ancs_filtering_get_mute_expiration(const iOSNotifPrefs *app_notif_prefs) { + if (app_notif_prefs) { + return attribute_get_uint32(&app_notif_prefs->attr_list, + AttributeIdMuteExpiration, 0); + } + + return 0; +} + +bool ancs_filtering_is_muted(const iOSNotifPrefs *app_notif_prefs) { + uint8_t mute_type = ancs_filtering_get_mute_type(app_notif_prefs); + uint32_t expiration_ts = ancs_filtering_get_mute_expiration(app_notif_prefs); + + struct tm now_tm; + time_t now = rtc_get_time(); + localtime_r(&now, &now_tm); + + return (mute_type & (1 << now_tm.tm_wday)) || (expiration_ts > (uint32_t)now); +} diff --git a/src/fw/services/normal/notifications/ancs/ancs_item.c b/src/fw/services/notifications/ancs/ancs_item.c similarity index 86% rename from src/fw/services/normal/notifications/ancs/ancs_item.c rename to src/fw/services/notifications/ancs/ancs_item.c index cca554c7b3..f586900c5c 100644 --- a/src/fw/services/normal/notifications/ancs/ancs_item.c +++ b/src/fw/services/notifications/ancs/ancs_item.c @@ -1,36 +1,31 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ancs_item.h" - -#include "ancs_notifications_util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/ancs/ancs_item.h" + +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" #include "applib/graphics/utf8.h" #include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" +#include "resource/resource_storage_impl.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" +#include "pbl/services/timeline/timeline_resources.h" #include "util/string.h" #include +PBL_LOG_MODULE_DECLARE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + //! Fits the maximum string "sent an attachment" and i18n translations, //! plus the emoji, newline and quotes when there is a text message in addition to media. #define MULTIMEDIA_INDICATOR_LENGTH 64 #define MULTIMEDIA_EMOJI "🎁" +//! This is derived from MAX_RESOURCES_PER_STORE * 2, representing the +//! boundary between system resource IDs and app resource IDs. +#define MAX_SYSTEM_RESOURCE_ID (MAX_RESOURCES_PER_STORE * 2) + // AttributeIdTitle + AttributeIdAncsAction. See prv_fill_native_ancs_action static const int NUM_NATIVE_ANCS_ACTION_ATTRS = 2; @@ -114,11 +109,7 @@ static uint8_t *prv_add_action_msg_to_attribute( } static int prv_set_multimedia_action_msg(char *buffer, size_t length) { -#if PLATFORM_TINTIN - const char *emoji_str = ""; -#else const char *emoji_str = PBL_IF_RECT_ELSE(" " MULTIMEDIA_EMOJI, "\n" MULTIMEDIA_EMOJI); -#endif return snprintf(buffer, length, "%s%s", i18n_get("sent an attachment", __FILE__), emoji_str); } @@ -196,6 +187,7 @@ static void prv_populate_attributes(TimelineItem *item, const ANCSAttribute *message, const ANCSAttribute *app_id, const ANCSAppMetadata *app_metadata, + const iOSNotifPrefs *notif_prefs, bool has_multimedia) { int attr_idx = 0; @@ -259,17 +251,53 @@ static void prv_populate_attributes(TimelineItem *item, attr_idx++; } - // add the icon attribute + uint32_t icon_id = app_metadata->icon_id; + if (notif_prefs) { + uint32_t pref_icon = attribute_get_uint32(¬if_prefs->attr_list, AttributeIdIcon, 0); + if (pref_icon != 0) { + if (pref_icon < MAX_SYSTEM_RESOURCE_ID) { + pref_icon |= SYSTEM_RESOURCE_FLAG; + } + icon_id = pref_icon; + } + } + item->attr_list.attributes[attr_idx].id = AttributeIdIconTiny; - item->attr_list.attributes[attr_idx].uint32 = app_metadata->icon_id; + item->attr_list.attributes[attr_idx].uint32 = icon_id; attr_idx++; #if PBL_COLOR - if (app_metadata->app_color != 0) { + uint8_t app_color = app_metadata->app_color; + uint8_t primary_color = 0; + if (notif_prefs) { + app_color = attribute_get_uint8(¬if_prefs->attr_list, AttributeIdBgColor, app_color); + primary_color = attribute_get_uint8(¬if_prefs->attr_list, AttributeIdPrimaryColor, 0); + } + + if (app_color != 0) { item->attr_list.attributes[attr_idx].id = AttributeIdBgColor; - item->attr_list.attributes[attr_idx].uint8 = app_metadata->app_color; + item->attr_list.attributes[attr_idx].uint8 = app_color; + attr_idx++; + } + if (primary_color != 0) { + item->attr_list.attributes[attr_idx].id = AttributeIdPrimaryColor; + item->attr_list.attributes[attr_idx].uint8 = primary_color; + attr_idx++; } #endif + + if (notif_prefs) { + const Attribute *vibe = attribute_find(¬if_prefs->attr_list, AttributeIdVibrationPattern); + if (vibe) { + item->attr_list.attributes[attr_idx] = *vibe; + Uint32List *src_list = vibe->uint32_list; + size_t list_size = Uint32ListSize(src_list->num_values); + item->attr_list.attributes[attr_idx].uint32_list = (Uint32List *)*buffer; + memcpy(*buffer, src_list, list_size); + *buffer += list_size; + attr_idx++; + } + } } static bool prv_should_hide_reply_because_group_sms(const TimelineItemAction *action, @@ -384,6 +412,13 @@ TimelineItem *ancs_item_create_and_populate(ANCSAttribute *notif_attributes[], required_space_for_strings += prv_max_ellipsified_cstring_size(title); } + if (notif_prefs) { + const Attribute *vibe = attribute_find(¬if_prefs->attr_list, AttributeIdVibrationPattern); + if (vibe) { + required_space_for_strings += Uint32ListSize(vibe->uint32_list->num_values); + } + } + int num_pebble_actions = 0; if (notif_prefs) { for (int i = 0; i < notif_prefs->action_group.num_actions; i++) { @@ -428,10 +463,27 @@ TimelineItem *ancs_item_create_and_populate(ANCSAttribute *notif_attributes[], ((app_id->length > 0) ? 1 : 0) + ((message->length > 0 || has_multimedia) ? 1 : 0) + (prv_should_add_sender_attr(app_id, title) ? 1 : 0) + + 1; // for icon + #if PBL_COLOR - ((app_metadata->app_color != 0) ? 1 : 0) + // for color + bool has_bg_color = (app_metadata->app_color != 0); + bool has_primary_color = false; + if (notif_prefs) { + if (attribute_find(¬if_prefs->attr_list, AttributeIdBgColor)) { + has_bg_color = true; + } + if (attribute_find(¬if_prefs->attr_list, AttributeIdPrimaryColor)) { + has_primary_color = true; + } + } + if (has_bg_color) num_attr++; + if (has_primary_color) num_attr++; +#else #endif - 1; // for icon + + if (notif_prefs && attribute_find(¬if_prefs->attr_list, AttributeIdVibrationPattern)) { + num_attr++; + } uint8_t *buffer; TimelineItem *item = timeline_item_create(num_attr, num_actions, attributes_per_action, @@ -439,14 +491,14 @@ TimelineItem *ancs_item_create_and_populate(ANCSAttribute *notif_attributes[], if (!item) { // Out of memory - we do not croak on out of memory for notifications (PBL-10521) - PBL_LOG(LOG_LEVEL_WARNING, "Ignoring ANCS notification (out of memory)"); + PBL_LOG_WRN("Ignoring ANCS notification (out of memory)"); return NULL; } item->header.timestamp = timestamp; prv_populate_attributes(item, &buffer, title, display_name, subtitle, message, app_id, - app_metadata, has_multimedia); + app_metadata, notif_prefs, has_multimedia); uint8_t *buf_end = buffer + required_space_for_strings; prv_populate_actions(item, &buffer, buf_end, positive_action, negative_action, subtitle, diff --git a/src/fw/services/normal/notifications/ancs/ancs_notifications.c b/src/fw/services/notifications/ancs/ancs_notifications.c similarity index 84% rename from src/fw/services/normal/notifications/ancs/ancs_notifications.c rename to src/fw/services/notifications/ancs/ancs_notifications.c index 62312fbada..4c84fcd1e4 100644 --- a/src/fw/services/normal/notifications/ancs/ancs_notifications.c +++ b/src/fw/services/notifications/ancs/ancs_notifications.c @@ -1,44 +1,33 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ancs_notifications.h" - -#include "ancs_filtering.h" -#include "ancs_item.h" -#include "ancs_phone_call.h" -#include "ancs_notifications_util.h" -#include "nexmo.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/ancs/ancs_notifications.h" + +#include "pbl/services/notifications/ancs/ancs_filtering.h" +#include "pbl/services/notifications/ancs/ancs_item.h" +#include "pbl/services/notifications/ancs/ancs_phone_call.h" +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" +#include "pbl/services/notifications/ancs/nexmo.h" #include "comm/ble/kernel_le_client/ancs/ancs_types.h" #include "drivers/rtc.h" #include "kernel/pbl_malloc.h" -#include "services/common/analytics/analytics.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/notifications/notifications.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/notifications/notifications.h" +#include "pbl/services/timeline/timeline.h" #include "system/logging.h" #include "system/passert.h" #include "util/pstring.h" #include +PBL_LOG_MODULE_DECLARE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + static const Uuid uuid_reminders_data_source = UUID_REMINDERS_DATA_SOURCE; static const Uuid uuid_calendar_data_source = UUID_CALENDAR_DATA_SOURCE; @@ -50,7 +39,7 @@ static void prv_dismiss_notification(const TimelineItem *notification) { } else { char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(¬ification->header.id, uuid_buffer); - PBL_LOG(LOG_LEVEL_ERROR, "Failed to load action for dismissal from %s", uuid_buffer); + PBL_LOG_ERR("Failed to load action for dismissal from %s", uuid_buffer); } } @@ -64,8 +53,7 @@ static void prv_handle_ancs_update(TimelineItem *notification, CommonTimelineItemHeader *existing_header) { if (existing_header->dismissed) { // this should be dismissed from iOS. Dismiss it again - PBL_LOG(LOG_LEVEL_DEBUG, - "ANCS notification already dismissed, dismissing again: %"PRIu32, + PBL_LOG_DBG("ANCS notification already dismissed, dismissing again: %"PRIu32, notification->header.ancs_uid); prv_dismiss_notification(notification); } @@ -93,7 +81,7 @@ static time_t prv_get_timestamp_from_ancs_date(const ANCSAttribute *date, // copy out app ID to a char buffer char app_id_buffer[app_id->length + 1]; pstring_pstring16_to_string(&app_id->pstr, app_id_buffer); - PBL_LOG(LOG_LEVEL_WARNING, "No valid date. Offending iOS app: %s", app_id_buffer); + PBL_LOG_WRN("No valid date. Offending iOS app: %s", app_id_buffer); } return timestamp; @@ -230,7 +218,7 @@ static bool prv_should_ignore_because_stale(time_t timestamp) { // PBL-12726: Added a check to see if the timstamp is coming from a location based reminder // This work-around is causing more trouble than the problem it was solving... if (timestamp < (now - MAXIMUM_NOTIFY_TIME) && timestamp != INVALID_TIME) { - PBL_LOG(LOG_LEVEL_INFO, "Not presenting stale notif (ts=%ld)", timestamp); + PBL_LOG_INFO("Not presenting stale notif (ts=%ld)", timestamp); return true; } @@ -248,6 +236,7 @@ static bool prv_should_ignore_notification(uint32_t uid, const ANCSAttribute *app_id = notif_attributes[FetchedNotifAttributeIndexAppID]; const ANCSAttribute *message = notif_attributes[FetchedNotifAttributeIndexMessage]; const ANCSAttribute *title = notif_attributes[FetchedNotifAttributeIndexTitle]; + const ANCSAttribute *subtitle = notif_attributes[FetchedNotifAttributeIndexSubtitle]; const ANCSAttribute *negative_action = notif_attributes[FetchedNotifAttributeIndexNegativeActionLabel]; @@ -255,15 +244,21 @@ static bool prv_should_ignore_notification(uint32_t uid, char app_id_buffer[app_id->length + 1]; pstring_pstring16_to_string(&app_id->pstr, app_id_buffer); - PBL_LOG(LOG_LEVEL_INFO, "Ignoring notification from <%s>: Muted", app_id_buffer); - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_FILTERED_BECAUSE_MUTED_COUNT, - AnalyticsClient_System); + PBL_LOG_INFO("Ignoring notification from <%s>: Muted", app_id_buffer); + return true; + } + + if (ancs_filtering_matches_rules(app_notif_prefs, title, subtitle, message)) { + char app_id_buffer[app_id->length + 1]; + pstring_pstring16_to_string(&app_id->pstr, app_id_buffer); + + PBL_LOG_INFO("Ignoring notification from <%s>: Matched filtering rule", app_id_buffer); return true; } // filter out mail buggy mail messages if (prv_should_ignore_because_apple_mail_dot_app_bug(app_id, message)) { - PBL_LOG(LOG_LEVEL_ERROR, "Ignoring ANCS notification because Mail.app bug"); + PBL_LOG_ERR("Ignoring ANCS notification because Mail.app bug"); return true; } @@ -273,14 +268,14 @@ static bool prv_should_ignore_notification(uint32_t uid, // filter out extraneous calendar messages if (prv_should_ignore_because_calendar_reminder(app_id, timestamp, title)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring ANCS calendar notification because reminders are set"); + PBL_LOG_DBG("Ignoring ANCS calendar notification because reminders are set"); return true; } // filter out time based reminder notifications if (prv_should_ignore_because_time_reminder(app_id, timestamp, title, uid, negative_action)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Ignoring ANCS reminders notification because existing " + PBL_LOG_DBG("Ignoring ANCS reminders notification because existing " "time-based reminder was found in db"); return true; } @@ -301,12 +296,11 @@ void ancs_notifications_handle_message(uint32_t uid, const ANCSAttribute *app_id = notif_attributes[FetchedNotifAttributeIndexAppID]; if (!app_id || app_id->length == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Can't handle notifications without an app id"); + PBL_LOG_ERR("Can't handle notifications without an app id"); return; } const ANCSAttribute *title = notif_attributes[FetchedNotifAttributeIndexTitle]; - const ANCSAttribute *subtitle = notif_attributes[FetchedNotifAttributeIndexSubtitle]; const ANCSAttribute *display_name = app_attributes[FetchedAppAttributeIndexDisplayName]; const ANCSAttribute *date = notif_attributes[FetchedNotifAttributeIndexDate]; const ANCSAttribute *message = notif_attributes[FetchedNotifAttributeIndexMessage]; @@ -340,21 +334,11 @@ void ancs_notifications_handle_message(uint32_t uid, // When declining a phone call from the Phone UI we still get a missed call notification // with a different UID. We don't want to show a missed call notification / pin in this case. if (has_missed_call_property && ancs_phone_call_should_ignore_missed_calls()) { - PBL_LOG(LOG_LEVEL_INFO, "Ignoring missed call"); + PBL_LOG_INFO("Ignoring missed call"); goto cleanup; } } - const bool is_sms = ancs_notifications_util_is_sms(app_id); - if (is_sms) { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_SMS_COUNT, - AnalyticsClient_System); - } - if (ancs_notifications_util_is_group_sms(app_id, subtitle)) { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_GROUP_SMS_COUNT, - AnalyticsClient_System); - } - // add a notification const ANCSAppMetadata* app_metadata = ancs_notifications_util_get_app_metadata(app_id); TimelineItem *notification = ancs_item_create_and_populate(notif_attributes, app_attributes, @@ -371,13 +355,15 @@ void ancs_notifications_handle_message(uint32_t uid, CommonTimelineItemHeader existing_header; if (prv_find_existing_notification(notification, &existing_header)) { if (prv_should_ignore_because_duplicate(notification, &existing_header)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Duplicate ANCS notification: %"PRIu32, uid); + PBL_LOG_DBG("Duplicate ANCS notification: %"PRIu32, uid); timeline_item_destroy(notification); notification_storage_unlock(); goto cleanup; } + PBL_LOG_INFO("Updating ANCS notification: %"PRIu32, uid); prv_handle_ancs_update(notification, &existing_header); } else { + PBL_LOG_INFO("New ANCS notification: %"PRIu32, uid); prv_handle_new_ancs_notif(notification); } @@ -408,7 +394,7 @@ void ancs_notifications_handle_notification_removed(uint32_t ancs_uid, ANCSPrope Uuid *notification_id = kernel_malloc_check(sizeof(Uuid)); if (notification_storage_find_ancs_notification_id(ancs_uid, notification_id)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Notification removed from notification centre: (UID: %"PRIu32")", + PBL_LOG_DBG("Notification removed from notification centre: (UID: %"PRIu32")", ancs_uid); notification_storage_set_status(notification_id, TimelineItemStatusDismissed); diff --git a/src/fw/services/normal/notifications/ancs/ancs_notifications_util.c b/src/fw/services/notifications/ancs/ancs_notifications_util.c similarity index 81% rename from src/fw/services/normal/notifications/ancs/ancs_notifications_util.c rename to src/fw/services/notifications/ancs/ancs_notifications_util.c index 2c1abfd607..63679afd52 100644 --- a/src/fw/services/normal/notifications/ancs/ancs_notifications_util.c +++ b/src/fw/services/notifications/ancs/ancs_notifications_util.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ancs_notifications_util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" #include "drivers/rtc.h" #include "resource/timeline_resource_ids.auto.h" @@ -34,7 +21,7 @@ const ANCSAppMetadata* ancs_notifications_util_get_app_metadata(const ANCSAttrib }; static const struct ANCSAppMetadata map[] = { -#include "ancs_known_apps.h" +#include "pbl/services/notifications/ancs/ancs_known_apps.h" }; for (unsigned int index = 0; index < ARRAY_LENGTH(map); ++index) { @@ -92,9 +79,9 @@ time_t ancs_notifications_util_parse_timestamp(const ANCSAttribute *timestamp_at // We have to assume that the timezone of the phone matches the timezone of the watch time_t sys_time = rtc_get_time(); - time_tm.tm_gmtoff = time_get_gmtoffset(); - time_get_timezone_abbr(time_tm.tm_zone, sys_time); time_tm.tm_isdst = time_get_isdst(sys_time); + time_tm.tm_gmtoff = time_get_gmtoffset() + (time_tm.tm_isdst ? time_get_dstoffset() : 0); + time_get_timezone_abbr(time_tm.tm_zone, sys_time); return mktime(&time_tm); } diff --git a/src/fw/services/normal/notifications/ancs/ancs_phone_call.c b/src/fw/services/notifications/ancs/ancs_phone_call.c similarity index 79% rename from src/fw/services/normal/notifications/ancs/ancs_phone_call.c rename to src/fw/services/notifications/ancs/ancs_phone_call.c index ad5907c687..531e47a6de 100644 --- a/src/fw/services/normal/notifications/ancs/ancs_phone_call.c +++ b/src/fw/services/notifications/ancs/ancs_phone_call.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ancs_phone_call.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/ancs/ancs_phone_call.h" #include "applib/graphics/utf8.h" #include "kernel/events.h" -#include "services/common/regular_timer.h" -#include "services/normal/phone_call_util.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/phone_call_util.h" #include #include diff --git a/src/fw/services/notifications/ancs/nexmo.c b/src/fw/services/notifications/ancs/nexmo.c new file mode 100644 index 0000000000..384a74dbc9 --- /dev/null +++ b/src/fw/services/notifications/ancs/nexmo.c @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/ancs/nexmo.h" +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" + +#include "comm/ble/kernel_le_client/ancs/ancs.h" +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" + +#include "system/logging.h" + +PBL_LOG_MODULE_DECLARE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + +T_STATIC char* NEXMO_REAUTH_STRING = "Pebble check-in code:"; + +bool nexmo_is_reauth_sms(const ANCSAttribute *app_id, const ANCSAttribute *message) { + if (ancs_notifications_util_is_sms(app_id)) { + if (strstr((const char *)message->value, NEXMO_REAUTH_STRING)) { + PBL_LOG_INFO("Got Nexmo Reauth SMS"); + return true; + } + } + return false; +} + +void nexmo_handle_reauth_sms(uint32_t uid, + const ANCSAttribute *app_id, + const ANCSAttribute *message, + iOSNotifPrefs *existing_notif_prefs) { + const int num_existing_attributes = existing_notif_prefs ? + existing_notif_prefs->attr_list.num_attributes : 0; + + AttributeList new_attr_list; + attribute_list_init_list(num_existing_attributes, &new_attr_list); + + // Copy over all the existing attributes to our new list + if (existing_notif_prefs) { + for (int i = 0; i < num_existing_attributes; i++) { + new_attr_list.attributes[i] = existing_notif_prefs->attr_list.attributes[i]; + } + } + + char msg_buffer[message->length + 1]; + memcpy(msg_buffer, message->value, message->length); + msg_buffer[message->length] = '\0'; + attribute_list_add_cstring(&new_attr_list, AttributeIdAuthCode, msg_buffer); + + // This will trigger a sync sending the auth code to the phone + ios_notif_pref_db_store_prefs(app_id->value, app_id->length, + &new_attr_list, &existing_notif_prefs->action_group); + + // Dismiss the notification so the user is oblivious to this process + ancs_perform_action(uid, ActionIDNegative); +} diff --git a/src/fw/services/normal/notifications/do_not_disturb.c b/src/fw/services/notifications/do_not_disturb.c similarity index 88% rename from src/fw/services/normal/notifications/do_not_disturb.c rename to src/fw/services/notifications/do_not_disturb.c index d7febd250b..190bbe45b5 100644 --- a/src/fw/services/normal/notifications/do_not_disturb.c +++ b/src/fw/services/notifications/do_not_disturb.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "do_not_disturb.h" -#include "do_not_disturb_toggle.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/do_not_disturb_toggle.h" #include "applib/ui/action_toggle.h" #include "applib/ui/app_window_stack.h" @@ -31,14 +18,14 @@ #include "kernel/ui/modals/modal_manager.h" #include "process_state/app_state/app_state.h" #include "resource/resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/i18n/i18n.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/activity/activity.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/timeline/calendar.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/timeline/calendar.h" #include "syscall/syscall_internal.h" #include "system/logging.h" #include "system/passert.h" @@ -49,6 +36,8 @@ #include #include +PBL_LOG_MODULE_DECLARE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + typedef struct DoNotDisturbData { TimerID update_timer_id; bool is_in_schedule_period; @@ -64,10 +53,7 @@ static void prv_set_schedule_mode_timer(); static void prv_update_active_time(bool is_active) { if (is_active) { - analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_ALERTS_DND_ACTIVE_TIME, - AnalyticsClient_System); } else { - analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_ALERTS_DND_ACTIVE_TIME); } } @@ -93,7 +79,7 @@ static void prv_do_update(void) { return; } s_data.was_active = is_active; - PBL_LOG(LOG_LEVEL_INFO, "Quiet Time: %s", prv_bool_to_string(is_active)); + PBL_LOG_INFO("Quiet Time: %s", prv_bool_to_string(is_active)); prv_update_active_time(is_active); prv_put_dnd_event(is_active); @@ -216,7 +202,7 @@ static void prv_set_schedule_mode_timer() { s_data.is_in_schedule_period = !is_enable_next; } - PBL_LOG(LOG_LEVEL_INFO, "%s scheduled period. %u seconds until update", + PBL_LOG_INFO("%s scheduled period. %u seconds until update", s_data.is_in_schedule_period ? "In" : "Out of", (unsigned int) seconds_until_update); bool success = new_timer_start(s_data.update_timer_id, seconds_until_update * 1000, diff --git a/src/fw/services/notifications/do_not_disturb_toggle.c b/src/fw/services/notifications/do_not_disturb_toggle.c new file mode 100644 index 0000000000..48f2269dc9 --- /dev/null +++ b/src/fw/services/notifications/do_not_disturb_toggle.c @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/do_not_disturb_toggle.h" + +#include "applib/app_exit_reason.h" +#include "applib/ui/action_toggle.h" +#include "pbl/services/i18n/i18n.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DECLARE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + +static bool prv_get_state(void *context) { + // This toggle does not necessarily toggle Manual DND. It sets Manual DND to the opposite of DND + // active status which in turn overrides Smart and Scheduled DND. + return do_not_disturb_is_active(); +} + +static void prv_set_state(bool enabled, void *context) { + PBL_LOG_DBG("Manual DND toggle: %s", enabled ? "enabled" : "disabled"); + do_not_disturb_set_manually_enabled(enabled); +} + +static const ActionToggleImpl s_dnd_action_toggle_impl = { + .window_name = "DNDManualToggle", + .prompt_icon = PBL_IF_RECT_ELSE(RESOURCE_ID_QUIET_TIME_MOUSE, + RESOURCE_ID_QUIET_TIME_MOUSE_RIGHT_ALIGNED), + .result_icon = RESOURCE_ID_QUIET_TIME_MOUSE, + .prompt_enable_message = i18n_noop("Start Quiet Time?"), + .prompt_disable_message = i18n_noop("End Quiet Time?"), + .result_enable_message = i18n_noop("Quiet Time\nStarted"), + .result_disable_message = i18n_noop("Quiet Time\nEnded"), + .callbacks = { + .get_state = prv_get_state, + .set_state = prv_set_state, + }, +}; + +void do_not_disturb_toggle_push(ActionTogglePrompt prompt, bool set_exit_reason) { + action_toggle_push(&(ActionToggleConfig) { + .impl = &s_dnd_action_toggle_impl, + .prompt = prompt, + .set_exit_reason = set_exit_reason, + }); +} diff --git a/src/fw/services/normal/notifications/notification_storage.c b/src/fw/services/notifications/notification_storage.c similarity index 89% rename from src/fw/services/normal/notifications/notification_storage.c rename to src/fw/services/notifications/notification_storage.c index b5a85443d9..525d09b0a5 100644 --- a/src/fw/services/normal/notifications/notification_storage.c +++ b/src/fw/services/notifications/notification_storage.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "notification_storage.h" -#include "notification_storage_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/notifications/notification_storage_private.h" #include "util/uuid.h" #include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" -#include "services/common/system_task.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/logging.h" #include "os/mutex.h" @@ -31,6 +18,8 @@ #include #include +PBL_LOG_MODULE_DEFINE(service_notifications, CONFIG_SERVICE_NOTIFICATIONS_LOG_LEVEL); + typedef struct NotificationIterState { SerializedTimelineItemHeader header; int fd; @@ -56,7 +45,7 @@ void notification_storage_init(void) { // Create a new file and close it (removes delay when receiving first notification after boot) int fd = pfs_open(FILENAME, OP_FLAG_WRITE, FILE_TYPE_STATIC, NOTIFICATION_STORAGE_FILE_SIZE); if (fd < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error opening file %d", fd); + PBL_LOG_ERR("Error opening file %d", fd); } else { pfs_close(fd); } @@ -81,7 +70,7 @@ static int prv_file_open(uint8_t op_flags) { bool read_only = ((op_flags & (OP_FLAG_WRITE | OP_FLAG_OVERWRITE | OP_FLAG_READ)) == OP_FLAG_READ); if ((!read_only) || (fd != E_DOES_NOT_EXIST)) { - PBL_LOG(LOG_LEVEL_ERROR, "Error opening file %d", fd); + PBL_LOG_ERR("Error opening file %d", fd); // Remove file so next open will create a new one (notification storage trashed) pfs_remove(FILENAME); } @@ -110,7 +99,7 @@ static int prv_write_notification(TimelineItem *notification, header->common.status = ~header->common.status; if (result < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error writing notification header %d", result); + PBL_LOG_ERR("Error writing notification header %d", result); return result; } bytes_written += result; @@ -124,7 +113,7 @@ static int prv_write_notification(TimelineItem *notification, timeline_item_serialize_payload(notification, write_buffer, header->payload_length); result = pfs_write(fd, write_buffer, header->payload_length); if (result < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error writing notification payload %d", result); + PBL_LOG_ERR("Error writing notification payload %d", result); kernel_free(write_buffer); return result; } @@ -150,6 +139,10 @@ static void prv_reclaim_space(size_t size_needed, int fd) { uint8_t status = iter_state.header.common.status; if (!(status & TimelineItemStatusDeleted)) { // Mark for deletion + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&iter_state.header.common.id, uuid_buffer); + PBL_LOG_WRN("Storage full: marking notification %s as deleted (ANCS UID: %"PRIu32")", + uuid_buffer, iter_state.header.common.ancs_uid); prv_set_header_status(&iter_state.header, TimelineItemStatusDeleted, fd); size_available += sizeof(SerializedTimelineItemHeader) + iter_state.header.payload_length; if (size_needed <= size_available) { @@ -195,7 +188,7 @@ static bool prv_compress(size_t size_needed, int *fd) { int new_fd = pfs_open(FILENAME, OP_FLAG_OVERWRITE, FILE_TYPE_STATIC, NOTIFICATION_STORAGE_FILE_SIZE); if (new_fd < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error opening new file for compression %d", new_fd); + PBL_LOG_ERR("Error opening new file for compression %d", new_fd); return false; } @@ -227,11 +220,17 @@ static bool prv_compress(size_t size_needed, int *fd) { TimelineItem notification; if (!prv_get_notification(¬ification, &iter_state.header, *fd)) { // Error occurred + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&iter_state.header.common.id, uuid_buffer); + PBL_LOG_ERR("Failed to read notification %s during compression. Resetting all notifications.", uuid_buffer); goto cleanup; } int result = prv_write_notification(¬ification, &iter_state.header, new_fd); if (result < 0) { // Error occurred + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&iter_state.header.common.id, uuid_buffer); + PBL_LOG_ERR("Failed to write notification %s during compression (error %d). Resetting all notifications.", uuid_buffer, result); kernel_free(notification.allocated_buffer); goto cleanup; } @@ -247,7 +246,7 @@ static bool prv_compress(size_t size_needed, int *fd) { *fd = pfs_open(FILENAME, OP_FLAG_READ | OP_FLAG_WRITE, FILE_TYPE_STATIC, NOTIFICATION_STORAGE_FILE_SIZE); if (*fd < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error re-opening after compression %d", new_fd); + PBL_LOG_ERR("Error re-opening after compression %d", new_fd); return false; } @@ -274,6 +273,7 @@ void notification_storage_store(TimelineItem* notification) { if (size_needed > (NOTIFICATION_STORAGE_FILE_SIZE - s_write_offset)) { if (!prv_compress(size_needed, &fd)) { // Notification storage compression failed. Clear notifications storage + PBL_LOG_ERR("Notification storage compression failed! Resetting all notifications."); goto reset_storage; } } @@ -283,6 +283,9 @@ void notification_storage_store(TimelineItem* notification) { int result = prv_write_notification(notification, &header, fd); if (result < 0) { // [AS] TODO: Write failure: reset storage, compression or reset watch? + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(¬ification->header.id, uuid_buffer); + PBL_LOG_ERR("Failed to write notification %s (error %d). Resetting all notifications.", uuid_buffer, result); goto reset_storage; } @@ -317,7 +320,7 @@ static bool prv_find_next_notification(SerializedTimelineItemHeader* header, (header->common.layout >= NumLayoutIds)) { pfs_close(fd); notification_storage_reset_and_init(); - PBL_LOG(LOG_LEVEL_ERROR, "Notification storage corrupt. Resetting..."); + PBL_LOG_ERR("Notification storage corrupt. Resetting..."); break; } @@ -396,7 +399,7 @@ size_t notification_storage_get_len(const Uuid *uuid) { if (prv_find_next_notification(&header, prv_uuid_equal_func, (void *) uuid, fd)) { size = header.payload_length + sizeof(SerializedTimelineItemHeader); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "notification not found"); + PBL_LOG_DBG("notification not found"); } prv_file_close(fd); @@ -417,12 +420,12 @@ bool notification_storage_get(const Uuid *id, TimelineItem *item_out) { char uuid_string[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(id, uuid_string); if (!prv_find_next_notification(&header, prv_uuid_equal_func, (void *) id, fd)) { - PBL_LOG(LOG_LEVEL_DEBUG, "notification not found, %s", uuid_string); + PBL_LOG_DBG("notification not found, %s", uuid_string); rv = false; } else { if (!prv_get_notification(item_out, &header, fd)) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not retrieve notification with id %s and size %u", + PBL_LOG_ERR("Could not retrieve notification with id %s and size %u", uuid_string, header.payload_length); rv = false; } @@ -446,7 +449,7 @@ static bool prv_iter_next(NotificationIterState *iter_state) { //End iteration if we have reached the end of the file or the header ID is invalid (erased flash) return false; } else if (result < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error reading notification header while iterating %d", result); + PBL_LOG_ERR("Error reading notification header while iterating %d", result); return false; } @@ -465,7 +468,7 @@ static bool prv_rewrite_iter_next(NotificationIterState *iter_state) { if ((result == E_RANGE) || (uuid_is_invalid(&iter_state->header.common.id))) { return false; } else if (result < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error reading notification header while iterating %d", result); + PBL_LOG_ERR("Error reading notification header while iterating %d", result); return false; } @@ -486,7 +489,7 @@ static void prv_set_header_status(SerializedTimelineItemHeader *header, uint8_t int result = pfs_write(fd, (uint8_t *)&status, sizeof(header->common.status)); if (result < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Error writing status to notification header %d", result); + PBL_LOG_ERR("Error writing status to notification header %d", result); } // Seek to the end of the header diff --git a/src/fw/services/notifications/notifications.c b/src/fw/services/notifications/notifications.c new file mode 100644 index 0000000000..4fc64036e5 --- /dev/null +++ b/src/fw/services/notifications/notifications.c @@ -0,0 +1,104 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/notifications.h" + +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/notifications/do_not_disturb.h" + +#include "applib/ui/vibes.h" +#include "drivers/rtc.h" +#include "drivers/battery.h" +#include "resource/resource_ids.auto.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/bitset.h" + +#include "kernel/events.h" +#include "kernel/low_power.h" +#include "kernel/pbl_malloc.h" + +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/phone_call.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/vibes/vibe_intensity.h" + +#include + +static void prv_notification_migration_iterator_callback(TimelineItem *notification, + SerializedTimelineItemHeader *header, void *data) { + header->common.timestamp -= *((int*)data); + notification->header.timestamp = header->common.timestamp; +} + +void notifications_handle_notification_action_result( + PebbleSysNotificationActionResult *action_result) { + PebbleEvent launcher_event = { + .type = PEBBLE_SYS_NOTIFICATION_EVENT, + .sys_notification = { + .type = NotificationActionResult, + .action_result = action_result, + } + }; + // event loop will free memory of action_result + event_put(&launcher_event); +} + +void notifications_handle_notification_removed(Uuid *notification_id) { + Uuid *removed_id = kernel_malloc_check(sizeof(Uuid)); + *removed_id = *notification_id; + PebbleEvent launcher_event = { + .type = PEBBLE_SYS_NOTIFICATION_EVENT, + .sys_notification = { + .type = NotificationRemoved, + .notification_id = removed_id, + } + }; + event_put(&launcher_event); +} + +void notifications_handle_notification_added(Uuid *notification_id) { + PebbleEvent launcher_event = { + .type = PEBBLE_SYS_NOTIFICATION_EVENT, + .sys_notification = { + .type = NotificationAdded, + .notification_id = notification_id + } + }; + event_put(&launcher_event); + PBL_ANALYTICS_ADD(notification_received_count, 1); +} + +void notifications_handle_notification_acted_upon(Uuid *notification_id) { + PebbleEvent launcher_event = { + .type = PEBBLE_SYS_NOTIFICATION_EVENT, + .sys_notification = { + .type = NotificationActedUpon, + .notification_id = notification_id + } + }; + event_put(&launcher_event); +} + +void notifications_migrate_timezone(const int tz_diff) { + notification_storage_rewrite(prv_notification_migration_iterator_callback, (void*)&tz_diff); +} + +void notification_storage_init(void); +void vibe_intensity_init(void); + +void notifications_init(void) { + notification_storage_init(); +} + +void notifications_add_notification(TimelineItem *notification) { + notification_storage_store(notification); + + Uuid *uuid = kernel_malloc_check(sizeof(Uuid)); + *uuid = notification->header.id; + notifications_handle_notification_added(uuid); +} diff --git a/src/fw/services/notifications/wscript_build b/src/fw/services/notifications/wscript_build new file mode 100644 index 0000000000..d69d86e156 --- /dev/null +++ b/src/fw/services/notifications/wscript_build @@ -0,0 +1,30 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'action_chaining_window.c', + 'alerts.c', + 'alerts_preferences.c', + 'ancs/ancs_filtering.c', + 'ancs/ancs_item.c', + 'ancs/ancs_notifications.c', + 'ancs/ancs_notifications_util.c', + 'ancs/ancs_phone_call.c', + 'ancs/nexmo.c', + 'do_not_disturb.c', + 'do_not_disturb_toggle.c', + 'notification_storage.c', + 'notifications.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_notifications', +) + +bld.env.SERVICES.append('services_notifications') + +target = bld.path.find_or_declare('services_notifications.pot') +bld.gettext(source=sources, target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/orientation_manager/Kconfig b/src/fw/services/orientation_manager/Kconfig new file mode 100644 index 0000000000..0f13721646 --- /dev/null +++ b/src/fw/services/orientation_manager/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_ORIENTATION_MANAGER + bool "Orientation manager" + help + Device orientation manager. diff --git a/src/fw/services/orientation_manager/service.c b/src/fw/services/orientation_manager/service.c new file mode 100644 index 0000000000..088c8fd47d --- /dev/null +++ b/src/fw/services/orientation_manager/service.c @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifdef CONFIG_ORIENTATION_MANAGER +#include "pbl/services/orientation_manager.h" +#include "system/passert.h" +#include "shell/prefs.h" +#include "drivers/display/display.h" +#include "drivers/accel.h" +#include "drivers/button.h" +#include "drivers/imu/mmc5603nj/mmc5603nj.h" +#include "kernel/events.h" +#include "process_management/process_manager.h" +#ifdef CONFIG_SERVICE_TOUCH +#include "pbl/services/touch/touch.h" +#endif + + +void prv_change_orientation(bool rotated) { + display_set_rotated(rotated); + button_set_rotated(rotated); + accel_set_rotated(rotated); + mag_set_rotated(rotated); +#ifdef CONFIG_SERVICE_TOUCH + touch_set_rotated(rotated); +#endif +} + +void orientation_handle_prefs_changed(void) { + prv_change_orientation(display_orientation_is_left()); + + // Force the running app to re-render so the display reflects the new orientation immediately. + // Without this, phone-originated orientation changes (via settings blob DB sync) would not + // be visible until the next natural redraw (e.g. button press or watchface tick). + PebbleEvent event = { + .type = PEBBLE_RENDER_REQUEST_EVENT, + }; + process_manager_send_event_to_process(PebbleTask_App, &event); +} + +void orientation_manager_enable(bool on) { + prv_change_orientation(on ? display_orientation_is_left() : false); +} +#endif \ No newline at end of file diff --git a/src/fw/services/orientation_manager/wscript_build b/src/fw/services/orientation_manager/wscript_build new file mode 100644 index 0000000000..0f72f5a5a9 --- /dev/null +++ b/src/fw/services/orientation_manager/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_orientation_manager', +) +bld.env.SERVICES.append('services_orientation_manager') diff --git a/src/fw/services/persist/Kconfig b/src/fw/services/persist/Kconfig new file mode 100644 index 0000000000..388d2d80c1 --- /dev/null +++ b/src/fw/services/persist/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PERSIST + bool "Persist" + help + App-persistent storage service. + +if SERVICE_PERSIST + +module = SERVICE_PERSIST +module-str = Persist +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/persist/service.c b/src/fw/services/persist/service.c new file mode 100644 index 0000000000..5c84b6b28c --- /dev/null +++ b/src/fw/services/persist/service.c @@ -0,0 +1,190 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/persist.h" + +#include +#include +#include + +#include "kernel/pbl_malloc.h" +#include "os/mutex.h" +#include "process_management/app_install_manager.h" +#include "pbl/services/filesystem/app_file.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/legacy/persist_map.h" +#include "pbl/services/settings/settings_file.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/list.h" +#include "util/units.h" + +PBL_LOG_MODULE_DEFINE(service_persist, CONFIG_SERVICE_PERSIST_LOG_LEVEL); + +#define PERSIST_STORAGE_MAX_SPACE MiBYTES(1) +#define PERSIST_STORAGE_INITIAL_ALLOC KiBYTES(4) + +typedef struct PersistStore { + ListNode list_node; + Uuid uuid; + SettingsFile file; + bool file_open; + uint8_t usage_count; //!< How many clients are using this store +} PersistStore; + +// Each open client has a PersistStore structure linked into this list. If both +// a worker and foreground app of the same UUID are running, then they share the +// same store. +static ListNode *s_client_stores; +static PebbleMutex *s_mutex; + + +static bool prv_uuid_list_filter(ListNode* node, void* data) { + const Uuid *uuid = data; + PersistStore* store = (PersistStore*)node; + return uuid_equal(&store->uuid, uuid); +} + +static PersistStore * prv_find_open_store(const Uuid *uuid) { + return (PersistStore *)list_find(s_client_stores, prv_uuid_list_filter, + (void *)uuid); +} + +static ALWAYS_INLINE void prv_lock(void) { + mutex_lock_with_lr(s_mutex, (uint32_t)__builtin_return_address(0)); +} + +static inline void prv_unlock(void) { + mutex_unlock(s_mutex); +} + +#define PERSIST_FILE_NAME_MAX_LENGTH sizeof("ps000001") + +static status_t prv_get_file_name(char *name, size_t buf_len, const Uuid *uuid) { + // Firmware 2.x persist files are named "p%06d", the added "s" in the file + // name prefix indicates that it is in SettingsFile format. + int pid = persist_map_auto_id(uuid); + if (FAILED(pid)) { + // Attempting to debug persist map failure + PBL_LOG_WRN("Failed to get pid! %d", pid); + persist_map_dump(); + return pid; + } + return snprintf(name, buf_len, "ps%06d", pid); +} + +status_t persist_service_delete_file(const Uuid *uuid) { + char name[PERSIST_FILE_NAME_MAX_LENGTH]; + + status_t status = prv_get_file_name(name, sizeof(name), uuid); + if (FAILED(status)) { + return status; + } + return pfs_remove(name); +} + +static bool prv_bad_persist_file_filter(const char *filename) { + return is_app_file_name(filename) && + strcmp(filename + APP_FILE_NAME_PREFIX_LENGTH, "persist") == 0; +} + +size_t persist_service_get_max_size(void) { + return PERSIST_STORAGE_MAX_SPACE; +} + +// Designed to be called once during reset +void persist_service_init(void) { + persist_map_init(); + s_mutex = mutex_create(); + + // Find and delete any AppInstallId-indexed persist files. Due to PBL-16663 + // (affecting FW 3.0-dp5 thru -dp7), the AppInstallId in the file name may not + // correspond to the app that the persist file originally belonged to. Since + // we can't be sure that the persist files correspond to the current + // AppInstallId, the safest thing to do is to simply blow them away. + // TODO: remove this code before FW 3.0-golden. + PFSFileListEntry *bad_file_list = pfs_create_file_list( + prv_bad_persist_file_filter); + PFSFileListEntry *iter = bad_file_list; + while (iter) { + pfs_remove(iter->name); + iter = (PFSFileListEntry *)iter->list_node.next; + } + pfs_delete_file_list(bad_file_list); +} + +// Return a pointer to the store for the given UUID. Each task that uses persist +// must call persist_service_client_open() to create/open the store during its +// startup and persist_service_client_close() during its shutdown. +// +// The SettingsFile is opened/created lazily. A persist file will not be +// created for an app unless it calls a persist function. +// +// The persist service mutex is locked when this function is called. It will +// only be unlocked after a call to persist_service_unlock(). While the global +// persist service mutex is currently used, the API is designed such that a +// per-file mutex could be used without altering the callers. +SettingsFile * persist_service_lock_and_get_store(const Uuid *uuid) { + prv_lock(); + PersistStore *store = prv_find_open_store(uuid); + PBL_ASSERTN(store); + if (!store->file_open) { + char filename[PERSIST_FILE_NAME_MAX_LENGTH]; + PBL_ASSERTN(PASSED(prv_get_file_name(filename, sizeof(filename), uuid))); + PBL_ASSERTN(PASSED(settings_file_open_growable(&store->file, filename, + PERSIST_STORAGE_MAX_SPACE, + PERSIST_STORAGE_INITIAL_ALLOC))); + store->file_open = true; + } + return &store->file; +} + +void persist_service_unlock_store(SettingsFile *store) { + prv_unlock(); +} + +// Create a store for a client of the given UUID it doesn't already exist. If it +// exists already (another client with the same UUID is running), then just +// increment its usage count. This is called by the process startup code +// (app_state_init() or worker_state_init()). +void persist_service_client_open(const Uuid *uuid) { + prv_lock(); + { + PersistStore *store = prv_find_open_store(uuid); + if (store) { + store->usage_count++; + } else { + store = kernel_malloc_check(sizeof(*store)); + *store = (PersistStore) { + .uuid = *uuid, + .usage_count = 1, + .file_open = false, + }; + s_client_stores = list_insert_before(s_client_stores, &store->list_node); + } + } + prv_unlock(); +} + +// Release the store for the given UUID. Called by ProcessManager to clean up +// after a task exists. If there are no other processes using the same store, it +// will be freed +void persist_service_client_close(const Uuid *uuid) { + prv_lock(); + { + PersistStore *store = prv_find_open_store(uuid); + PBL_ASSERTN(store && + list_contains(s_client_stores, &store->list_node) && + store->usage_count >= 1); + + if (--store->usage_count == 0) { + if (store->file_open) { + settings_file_close(&store->file); + } + list_remove(&store->list_node, + &s_client_stores /* &head */, NULL /* &tail */); + kernel_free(store); + } + } + prv_unlock(); +} diff --git a/src/fw/services/persist/wscript_build b/src/fw/services/persist/wscript_build new file mode 100644 index 0000000000..1e996935c8 --- /dev/null +++ b/src/fw/services/persist/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_persist', +) +bld.env.SERVICES.append('services_persist') diff --git a/src/fw/services/phone_call/Kconfig b/src/fw/services/phone_call/Kconfig new file mode 100644 index 0000000000..c1380dd090 --- /dev/null +++ b/src/fw/services/phone_call/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PHONE_CALL + bool "Phone call" + help + Phone call (incoming/outgoing) service. + +if SERVICE_PHONE_CALL + +module = SERVICE_PHONE_CALL +module-str = Phone call +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/phone_call/service.c b/src/fw/services/phone_call/service.c new file mode 100644 index 0000000000..07435795b1 --- /dev/null +++ b/src/fw/services/phone_call/service.c @@ -0,0 +1,337 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/phone_call.h" + +#include "applib/event_service_client.h" +#include "applib/ui/vibes.h" +#include "comm/ble/kernel_le_client/ancs/ancs.h" +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" +#include "kernel/pbl_malloc.h" +#include "popups/phone_ui.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/phone_pp.h" +#include "pbl/services/system_task.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/ancs/ancs_phone_call.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DEFINE(service_phone_call, CONFIG_SERVICE_PHONE_CALL_LOG_LEVEL); + +//! This service is a little confusing, but generally here is how the phone calls work: +//! On Android: +//! - The watch gets PP messages (parsed in phone_pp.c), which come in as events happen. +//! - The watch can decline / hangup the call by sending PP messages to the phone. +//! On iOS: +//! - The watch gets incomming calls from ANCS (parsed in ancs_notifications.c). +//! - After that the watch must poll the phone for its status if not iOS 9+ (using PP messages). +//! - On iOS 9, ANCS tells us when the phone stops ringing +//! - The watch can pickup / decline a call using ANCS actions +//! - We don't show the ongoing call UI because we must continue to poll so that we know when the +//! call ends, which consumes a lot of battery especially for longer calls. On iOS 9, we only +//! know when the phone stops ringing, we don't know what happens after the user accepts/rejects + + +static bool s_call_in_progress = false; +static PhoneCallSource s_call_source; + +// When using Android this is the cookie, when using ANCS this is the NotificationUUID +static uint32_t s_call_identifier; + +// If the mobile app is closed we won't receive PP messages and thus might miss a call end event +// which puts us in a bad state until BT disconnects +static bool s_mobile_app_is_connected; + +// We can't expect iOS to reliably send us phone call events, so we must poll for the current +// status of the phone call +static TimerID s_call_watchdog = TIMER_INVALID_ID; + + +static void prv_handle_call_end(bool disconnected); + +static bool prv_call_is_ancs(void) { + return (s_call_source == PhoneCallSource_ANCS_Legacy) || (s_call_source == PhoneCallSource_ANCS); +} + +static void prv_poll_phone_for_status(void *context) { + pp_get_phone_state(); +} + +static void prv_timer_callback(void *context) { + // Make sure we aren't overflowing / backing up the queue too much + if (system_task_get_available_space() > 10) { + system_task_add_callback(prv_poll_phone_for_status, context); + } +} + +static void prv_schedule_call_watchdog(int poll_interval_ms) { + // The Android app currently crashes if it recieves the get_state event. It currently doesn't + // respond either so don't bother sending messages we don't need to. We also don't need to poll + // iOS 9 since we can rely on ANCS to tell us when the phone stops ringing + if (s_call_source == PhoneCallSource_ANCS_Legacy) { + // Schedule/reschedule the watchdog + if (!new_timer_start(s_call_watchdog, poll_interval_ms, prv_timer_callback, + NULL, TIMER_START_FLAG_REPEATING)) { + PBL_LOG_ERR("Could not start the phone call watchdog timer"); + prv_handle_call_end(true /* Treat this as a disconnection */); + } else { + PBL_LOG_INFO("Phone call watchdog timer started"); + pp_get_phone_state_set_enabled(true); + } + } else { + PBL_LOG_INFO("Not starting phone call watchdog, this isn't iOS 8: %d", + s_call_source); + } +} + +static void prv_cancel_call_watchdog(void) { + new_timer_stop(s_call_watchdog); + pp_get_phone_state_set_enabled(false); +} + +static bool prv_can_answer(void) { + // We can't answer calls with Android + return prv_call_is_ancs(); +} + +static bool prv_should_show_ongoing_call_ui(void) { + // We only want to show the ongoing call UI on Android + return (s_call_source == PhoneCallSource_PP); +} + +// hangup != decline. Decline == reject incomming call, Hangup == stop in progress call +static bool prv_can_hangup(void) { + // We can't hangup with iOS + return !prv_call_is_ancs(); +} + +// Handles the common things when we hide an incoming call +static void prv_call_end_common(void) { + s_call_in_progress = false; + prv_cancel_call_watchdog(); + PBL_ANALYTICS_TIMER_STOP(phone_call_time_ms); +} + +static void prv_handle_incoming_call(const PebblePhoneEvent *event) { + // Only 1 call at a time is supported + if (s_call_in_progress) { + PBL_LOG_INFO("Ignoring incoming call. A call is already in progress"); + return; + } + + // If we're not on iOS9+, we need to be connected to the mobile app since it tells us when + // the phone has stopped ringing + if ((event->source != PhoneCallSource_ANCS) && !s_mobile_app_is_connected) { + PBL_LOG_INFO("Ignoring incoming call. Mobile app is not connected. Call source: %d ", + event->source); + return; + } + + s_call_in_progress = true; + s_call_source = event->source; + s_call_identifier = event->call_identifier; + + prv_schedule_call_watchdog(600); + + phone_ui_handle_incoming_call(event->caller, prv_can_answer(), prv_should_show_ongoing_call_ui(), + s_call_source); + PBL_ANALYTICS_ADD(phone_call_incoming_count, 1); + PBL_ANALYTICS_TIMER_START(phone_call_time_ms); +} + +static void prv_handle_outgoing_call(PebblePhoneEvent *event) { + // Only 1 call at a time is supported + if (!s_call_in_progress && s_mobile_app_is_connected) { + phone_ui_handle_outgoing_call(event->caller); + } else { + // PBL_LOG_DBG("Ignoring outgoing call. A call is already in progress: %d, " + // "the mobile app is connected: %d", s_call_in_progress, s_mobile_app_is_connected); + } +} + +static void prv_handle_missed_call(PebblePhoneEvent *event) { + if (s_call_in_progress) { + prv_call_end_common(); + phone_ui_handle_missed_call(); + PBL_ANALYTICS_ADD(phone_call_incoming_count, 1); + } else { + // PBL_LOG_DBG("Ignoring missed call. A call is not in progress"); + } +} + +static void prv_handle_call_start(void) { + if (s_call_in_progress) { + if (prv_call_is_ancs()) { + // We don't show an ongoing call UI on iOS, so from this service's point of view the call is + // now complete. + prv_call_end_common(); + phone_ui_handle_call_end(true /*call accepted*/, false /*disconnected*/); + } else { + phone_ui_handle_call_start(prv_can_hangup()); + } + } else { + PBL_LOG_INFO("Ignoring start call. A call is not in progress"); + } +} + +static void prv_handle_call_hide(PebblePhoneEvent *event) { + if (!s_call_in_progress) { + return; + } + + // Make sure this wasn't caused due to an unrelated ANCS removal + if (prv_call_is_ancs() && (s_call_identifier != event->call_identifier)) { + PBL_LOG_INFO("Ignoring hide call. Call identifier %"PRIu32" doesn't match %"PRIu32, + s_call_identifier, event->call_identifier); + return; + } + + prv_call_end_common(); + phone_ui_handle_call_hide(); +} + +static void prv_handle_call_end(bool disconnected) { + if (s_call_in_progress) { + prv_call_end_common(); + phone_ui_handle_call_end(false /*call accepted*/, disconnected); + } else if (!disconnected) { + PBL_LOG_INFO("Ignoring end call. A call is not in progress"); + } +} + +static void prv_handle_caller_id(PebblePhoneEvent *event) { + if (s_call_in_progress) { + phone_ui_handle_caller_id(event->caller); + } else { + PBL_LOG_DBG("Ignoring caller id. A call is not in progress"); + } +} + +T_STATIC void prv_handle_phone_event(PebbleEvent *e, void *context) { + PebblePhoneEvent event = (PebblePhoneEvent) e->phone; + + if (!alerts_should_notify_for_type(AlertPhoneCall)) { + prv_handle_call_end(true /* disconnected */); + phone_call_util_destroy_caller(event.caller); + return; + } + + if (!(event.type == PhoneEventType_Incoming && new_timer_scheduled(s_call_watchdog, NULL))) { + // Be careful not to spam the logs with the new iOS polling implementation + PBL_LOG_INFO("PebblePhoneEvent: %d, Call in progress: %s, Connected: %s", + event.type, s_call_in_progress ? "T": "F", s_mobile_app_is_connected ? "T": "F"); + } + + switch (event.type) { + case PhoneEventType_Incoming: + prv_handle_incoming_call(&event); + break; + case PhoneEventType_Outgoing: + prv_handle_outgoing_call(&event); + break; + case PhoneEventType_Missed: + prv_handle_missed_call(&event); + break; + case PhoneEventType_Ring: + // Just ignore these. We can ring on our own. + break; + case PhoneEventType_Start: + prv_handle_call_start(); + break; + case PhoneEventType_End: + prv_handle_call_end(false /* disconnected */); + break; + case PhoneEventType_CallerID: + prv_handle_caller_id(&event); + break; + case PhoneEventType_Disconnect: + prv_handle_call_end(true /* disconnected */); + break; + case PhoneEventType_Hide: + prv_handle_call_hide(&event); + break; + case PhoneEventType_Invalid: + break; + } + + phone_call_util_destroy_caller(event.caller); +} + +T_STATIC void prv_handle_mobile_app_event(PebbleEvent *e, void *context) { + if (!e->bluetooth.comm_session_event.is_system) { + return; + } + + s_mobile_app_is_connected = e->bluetooth.comm_session_event.is_open; + if (!s_mobile_app_is_connected && (s_call_source != PhoneCallSource_ANCS)) { + prv_handle_call_end(true /* disconnected */); + } +} + +T_STATIC void prv_handle_ancs_disconnected_event(PebbleEvent *e, void *context) { + if (s_call_source == PhoneCallSource_ANCS) { + prv_handle_call_end(true /* disconnected */); + } +} + +//! +//! Phone Call API +//! +void phone_call_service_init() { + static EventServiceInfo phone_event_info; + phone_event_info = (EventServiceInfo) { + .type = PEBBLE_PHONE_EVENT, + .handler = prv_handle_phone_event, + }; + event_service_client_subscribe(&phone_event_info); + + static EventServiceInfo mobile_app_event_info; + mobile_app_event_info = (EventServiceInfo) { + .type = PEBBLE_COMM_SESSION_EVENT, + .handler = prv_handle_mobile_app_event, + }; + event_service_client_subscribe(&mobile_app_event_info); + + static EventServiceInfo ancs_disconnected_event_info; + ancs_disconnected_event_info = (EventServiceInfo) { + .type = PEBBLE_ANCS_DISCONNECTED_EVENT, + .handler = prv_handle_ancs_disconnected_event, + }; + event_service_client_subscribe(&ancs_disconnected_event_info); + + s_mobile_app_is_connected = (comm_session_get_system_session() != NULL); + + s_call_watchdog = new_timer_create(); +} + +void phone_call_answer(void) { + PBL_LOG_INFO("Call accepted"); + + if (prv_call_is_ancs()) { + ancs_perform_action(s_call_identifier, ActionIDPositive); + + // We don't show an ongoing call UI on iOS, so from this service's point of view the call is + // now complete. + prv_call_end_common(); + } else { + pp_answer_call(s_call_identifier); + } +} + +void phone_call_decline(void) { + PBL_LOG_INFO("Call declined"); + + if (prv_call_is_ancs()) { + ancs_perform_action(s_call_identifier, ActionIDNegative); + ancs_phone_call_temporarily_block_missed_calls(); + prv_cancel_call_watchdog(); + } else { + pp_decline_call(s_call_identifier); + } + + if (s_call_in_progress) { + s_call_in_progress = false; + PBL_ANALYTICS_TIMER_STOP(phone_call_time_ms); + } +} diff --git a/src/fw/services/phone_call/util.c b/src/fw/services/phone_call/util.c new file mode 100644 index 0000000000..6690358ce2 --- /dev/null +++ b/src/fw/services/phone_call/util.c @@ -0,0 +1,51 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/phone_call_util.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" + +#include + +PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name) { + PebblePhoneCaller *caller = kernel_zalloc(sizeof(PebblePhoneCaller)); + if (!caller) { + return NULL; + } + + if ((!name || !strlen(name)) && (!number || !strlen(number))) { + const char *i18n_name = i18n_get("Unknown", __FILE__); + + caller->name = kernel_malloc(strlen(i18n_name) + 1); + if (caller->name) { + strcpy(caller->name, i18n_name); + } + i18n_free(i18n_name, __FILE__); + + return caller; + } + + if (number) { + caller->number = kernel_malloc(strlen(number) + 1); + if (caller->number) { + strcpy(caller->number, number); + } + } + if (name) { + caller->name = kernel_malloc(strlen(name) + 1); + if (caller->name) { + strcpy(caller->name, name); + } + } + + return caller; +} + +void phone_call_util_destroy_caller(PebblePhoneCaller *caller) { + if (caller) { + kernel_free(caller->number); + kernel_free(caller->name); + kernel_free(caller); + } +} diff --git a/src/fw/services/phone_call/wscript_build b/src/fw/services/phone_call/wscript_build new file mode 100644 index 0000000000..6d262016d6 --- /dev/null +++ b/src/fw/services/phone_call/wscript_build @@ -0,0 +1,13 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c', 'util.c'], + target='services_phone_call', +) +bld.env.SERVICES.append('services_phone_call') + +target = bld.path.find_or_declare('services_phone_call.pot') +bld.gettext(source=['service.c', 'util.c'], target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/phone_pp/Kconfig b/src/fw/services/phone_pp/Kconfig new file mode 100644 index 0000000000..f5abc3c822 --- /dev/null +++ b/src/fw/services/phone_pp/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PHONE_PP + bool "Phone (Pebble Protocol)" + help + Phone endpoint over Pebble Protocol. + +if SERVICE_PHONE_PP + +module = SERVICE_PHONE_PP +module-str = Phone (Pebble Protocol) +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/phone_pp/service.c b/src/fw/services/phone_pp/service.c new file mode 100644 index 0000000000..2e22627ce1 --- /dev/null +++ b/src/fw/services/phone_pp/service.c @@ -0,0 +1,265 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/phone_pp.h" + +#include "kernel/events.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/phone_call.h" +#include "pbl/services/phone_call_util.h" +#include "system/hexdump.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/math.h" + +#include +#include +#include +#include + +PBL_LOG_MODULE_DEFINE(service_phone_pp, CONFIG_SERVICE_PHONE_PP_LOG_LEVEL); + +#define CALLER_BUFFER_LENGTH 32 + +static const uint16_t PHONE_CTRL_ENDPOINT = 0x21; + +static bool s_get_phone_state_enabled; + +typedef enum PhoneCallState { + CallStateIncoming, + CallStateOutgoing, + CallStateMissed, + NumCallStates +} PhoneCallState; + +enum PhoneCmd { + PhoneCmdAnswer = 0x01, + PhoneCmdHangup = 0x02, + PhoneCmdGetStateRequest = 0x03, + PhoneCmdGetStateResponse = 0x83, + PhoneCmdIncoming = 0x04, + PhoneCmdOutgoing = 0x05, + PhoneCmdMissed = 0x06, + PhoneCmdRing = 0x07, + PhoneCmdStart = 0x08, + PhoneCmdEnd = 0x09 +}; + +typedef struct { + uint32_t cookie; + char caller_number[CALLER_BUFFER_LENGTH]; + char caller_name[CALLER_BUFFER_LENGTH]; +} PebbleCallInfo; + + +static bool get_call_info_from_msg(const uint8_t* msg, unsigned int length, + PebbleCallInfo* info) { + unsigned int msg_length = 0; + info->cookie = *((uint32_t*) msg); + msg += 4; + msg_length += 4; + + uint8_t caller_number_size = *msg++; + memcpy(&info->caller_number, msg, + MIN(caller_number_size, sizeof(info->caller_number))); + msg += caller_number_size; + msg_length += caller_number_size + 1; + + uint8_t caller_name_size = *msg++; + memcpy(&info->caller_name, msg, + MIN(caller_name_size, sizeof(info->caller_name))); + msg_length += caller_name_size + 1; + + // Ensure that we haven't run off the end of our buffer + if (msg_length > length) { + return false; + } + + info->caller_number[CALLER_BUFFER_LENGTH - 1] = '\0'; + info->caller_name[CALLER_BUFFER_LENGTH - 1] = '\0'; + return true; +} + +static void prv_put_call_disconnect_event(void) { + PebbleEvent e = { + .type = PEBBLE_PHONE_EVENT, + .phone = { + .type = PhoneEventType_Disconnect, + .source = PhoneCallSource_PP, + .call_identifier = 0, // Cookie is not yet implemented / used + } + }; + event_put(&e); +} + +static void prv_put_call_end_event(void) { + PebbleEvent e = { + .type = PEBBLE_PHONE_EVENT, + .phone = { + .type = PhoneEventType_End, + .source = PhoneCallSource_PP, + .call_identifier = 0, // Cookie is not yet implemented / used + } + }; + event_put(&e); +} + + +static void prv_send_phone_command_to_handset(uint8_t cmd, uint8_t *data, unsigned length) { + static uint8_t buffer[5]; + PBL_ASSERTN(length <= sizeof(buffer) - sizeof(cmd)); + + *buffer = cmd; + memcpy(buffer + sizeof(cmd), data, length); + + // PBL_LOG_DBG("Sending PhoneCmd: %d", cmd); + CommSession *session = comm_session_get_system_session(); + if (!session) { + // Looks like we disconnected... + PBL_LOG_ERR("No CommSession for phone command, ending call"); + prv_put_call_disconnect_event(); + } else { + comm_session_send_data(session, PHONE_CTRL_ENDPOINT, buffer, + length + sizeof(cmd), COMM_SESSION_DEFAULT_TIMEOUT); + } +} + +void pp_answer_call(uint32_t cookie) { + prv_send_phone_command_to_handset(PhoneCmdAnswer, (uint8_t*)&cookie, sizeof(cookie)); +} + +void pp_decline_call(uint32_t cookie) { + prv_send_phone_command_to_handset(PhoneCmdHangup, (uint8_t*)&cookie, sizeof(cookie)); +} + +void pp_get_phone_state(void) { + prv_send_phone_command_to_handset(PhoneCmdGetStateRequest, NULL, 0); +} + +void pp_get_phone_state_set_enabled(bool enabled) { + s_get_phone_state_enabled = enabled; +} + +static bool prv_parse_msg_to_event(const uint8_t *iter, size_t length, + PebbleEvent *event_out, bool is_state_response) { + uint8_t msg_type = *iter++; + --length; + + bool did_parse = false; + PebbleCallInfo call_info; + call_info = (PebbleCallInfo){}; + + PebblePhoneCaller *caller = NULL; + PhoneEventType type = ~0; + + switch (msg_type) { + case PhoneCmdIncoming: { + // PBL-34640 Generating incoming call events for phone state responses just gives us a bad + // time. We can look at changing this later if iOS ever starts sending us cookies properly, + // but it's not really worth the effort since it only applies to iOS 8 + if (is_state_response) { + return false; + } + bool result = get_call_info_from_msg(iter, length, &call_info); + if (!result) { + PBL_LOG_ERR("Failed to read caller information from 'Incoming' phone event"); + return false; + } + + type = PhoneEventType_Incoming; + caller = phone_call_util_create_caller(call_info.caller_number, call_info.caller_name); + did_parse = true; + break; + } + + case PhoneCmdStart: { + type = PhoneEventType_Start; + call_info.cookie = *((uint32_t*) iter); + did_parse = true; + break; + } + + case PhoneCmdEnd: { + type = PhoneEventType_End; + call_info.cookie = *((uint32_t*) iter); + did_parse = true; + break; + } + + case PhoneCmdRing: { + // We generate rings internally + // Return here so we don't log / hexdump + return false; + } + + case PhoneCmdOutgoing: + // Return here so we don't log / hexdump + return false; + + case PhoneCmdMissed: + // Return here so we don't log / hexdump + return false; + } + + if (did_parse) { + *event_out = (const PebbleEvent) { + .type = PEBBLE_PHONE_EVENT, + .phone = { + .type = type, + .source = PhoneCallSource_PP, + .call_identifier = call_info.cookie, + .caller = caller, + } + }; + } else { + // Try to catch potentially malformed messages. + PBL_LOG_ERR("Error parsing phone msg"); + PBL_HEXDUMP(LOG_LEVEL_INFO, iter, length); + } + + return did_parse; +} + +static void prv_parse_msg_and_emit_event(const uint8_t *msg, size_t length, + bool is_state_response) { + PebbleEvent e; + if (prv_parse_msg_to_event(msg, length, &e, is_state_response)) { + event_put(&e); + } +} + +void phone_protocol_msg_callback(CommSession *session, const uint8_t* iter, size_t length) { + PBL_HEXDUMP(LOG_LEVEL_DEBUG, iter, length); + + // Get State Response is basically a list representing the state of current calls. It's + // conveniently formatted exactly the same as the event messages, so just loop over them and + // re-use the code that parses these messages: + if (iter[0] == PhoneCmdGetStateResponse) { + if (!s_get_phone_state_enabled) { + return; + } + // Eat the command byte: + --length; + ++iter; + uint8_t num_items = 0; + while (length) { + // First byte is the length of the item + uint8_t item_length = *iter++; + --length; + ++num_items; + if (length < item_length) { + PBL_LOG_ERR("Malformed message"); + break; + } + prv_parse_msg_and_emit_event(iter, item_length, true /* is_state_response */); + iter += item_length; + length -= item_length; + } + if (num_items == 0) { + // Generate fake call stop if there are no calls, to hide the phone UI in case it's showing + prv_put_call_end_event(); + } + } else { + prv_parse_msg_and_emit_event(iter, length, false /* is_state_response */); + } +} diff --git a/src/fw/services/phone_pp/wscript_build b/src/fw/services/phone_pp/wscript_build new file mode 100644 index 0000000000..0228e0b558 --- /dev/null +++ b/src/fw/services/phone_pp/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_phone_pp', +) +bld.env.SERVICES.append('services_phone_pp') diff --git a/src/fw/services/ping/Kconfig b/src/fw/services/ping/Kconfig new file mode 100644 index 0000000000..e0909e0902 --- /dev/null +++ b/src/fw/services/ping/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PING + bool "Ping" + help + Ping endpoint. + +if SERVICE_PING + +module = SERVICE_PING +module-str = Ping +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/ping/service.c b/src/fw/services/ping/service.c new file mode 100644 index 0000000000..09e3b49349 --- /dev/null +++ b/src/fw/services/ping/service.c @@ -0,0 +1,156 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/ui/dialogs/dialog.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "console/prompt.h" +#include "drivers/battery.h" +#include "kernel/event_loop.h" +#include "kernel/ui/modals/modal_manager.h" +#include "pbl/services/accel_manager.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "util/attributes.h" +#include "util/net.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_ping, CONFIG_SERVICE_PING_LOG_LEVEL); + +#define PING_ENDPOINT 2001 +#define PING_MIN_PERIOD_SECS (60 * 60) // 1 hour + +static time_t s_last_send_time; +static bool s_is_ping_kernel_bg_callback_scheduled; + + +// --------------------------------------------------------------------------------------------------------- +// Ping Pong structures +typedef struct PACKED { + uint8_t cmd; + uint32_t cookie; +} PingMsgHeader; + +typedef struct PACKED { + PingMsgHeader hdr; +} PingMsgV1; + +typedef struct PACKED { + PingMsgHeader hdr; + uint8_t idle; // Optional +} PingMsgV2; + +typedef struct PACKED { + PingMsgHeader hdr; +} PongMsg; + + +static void prv_send_ping_kernel_bg_cb(void *unused) { + CommSession *system_session = comm_session_get_system_session(); + if (system_session) { + // Are we idle? + bool idle = (battery_is_usb_connected() || accel_is_idle()); + + PingMsgV2 ping_msg = (PingMsgV2) { + .hdr = { + .cmd = 0, + .cookie = htonl(42) + }, + .idle = idle + }; + bool success = comm_session_send_data(system_session, PING_ENDPOINT, + (const uint8_t *) &ping_msg, sizeof(ping_msg), + COMM_SESSION_DEFAULT_TIMEOUT); + if (success) { + s_last_send_time = rtc_get_time(); + } + PBL_LOG_DBG("Sent ping idle=%d, success=%d", (int)idle, (int)success); + } + + s_is_ping_kernel_bg_callback_scheduled = false; +} + +// ------------------------------------------------------------------------------------------------------------ +// If a ping is due to be sent, send it. +// bt_lock() is held by the caller +void ping_send_if_due(void) { + if (s_is_ping_kernel_bg_callback_scheduled) { + return; + } + + // Only send if we haven't sent within the last PING_MIN_PERIOD_SECS + time_t current_time = rtc_get_time(); + if (current_time < s_last_send_time + PING_MIN_PERIOD_SECS) { + return; + } + + // Offload to KernelBG, because we cannot use comm_session_send_data() with bt_lock held. + system_task_add_callback(prv_send_ping_kernel_bg_cb, NULL); + s_is_ping_kernel_bg_callback_scheduled = true; +} + +static void prv_push_window(void *data) { + SimpleDialog *s_dialog = simple_dialog_create("Ping"); + Dialog *dialog = simple_dialog_get_dialog(s_dialog); + + dialog_set_background_color(dialog, GColorCobaltBlue); + dialog_set_text_color(dialog, GColorWhite); + dialog_set_text(dialog, "Ping"); + + WindowStack *stack = modal_manager_get_window_stack(ModalPriorityGeneric); + simple_dialog_push(s_dialog, stack); +} + +void ping_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { + PingMsgV1 *ping = (PingMsgV1 *)data; + switch (ping->hdr.cmd) { + case 0: + { + if (length != sizeof(PingMsgV1) && length != sizeof(PingMsgV2) /* idle boolean is optional */) { + PBL_LOG_ERR("Invalid Ping, l=%u", length); + return; + } + + // Ping message + uint32_t cookie = ntohl(ping->hdr.cookie); + PBL_LOG_DBG("Ping c=%"PRIu32"", cookie); + launcher_task_add_callback(prv_push_window, NULL); + + // Send the pong response + PongMsg pong = { + .hdr = { + .cmd = 1, + .cookie = htonl(cookie) + } + }; + comm_session_send_data(session, PING_ENDPOINT, (uint8_t *)&pong, sizeof(pong), COMM_SESSION_DEFAULT_TIMEOUT); + break; + } + + case 1: + if (length != sizeof(PongMsg)) { + PBL_LOG_ERR("Invalid Pong, l=%u", length); + return; + } + + PongMsg *pong = (PongMsg *)data; + PBL_LOG_DBG("Pong c=%"PRIu32, ntohl(pong->hdr.cookie)); + break; + + default: + PBL_LOG_ERR("Invalid message received. First byte is %u", ping->hdr.cmd); + break; + } +} + + +// Serial Commands +////////////////////////////////////////////////////////////////////// +void command_ping_send(void) { + // Override last send time + s_last_send_time = 0; + ping_send_if_due(); +} + diff --git a/src/fw/services/ping/wscript_build b/src/fw/services/ping/wscript_build new file mode 100644 index 0000000000..6e09848e83 --- /dev/null +++ b/src/fw/services/ping/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c'], + target='services_ping', +) +bld.env.SERVICES.append('services_ping') diff --git a/src/fw/services/poll_remote/Kconfig b/src/fw/services/poll_remote/Kconfig new file mode 100644 index 0000000000..8613511575 --- /dev/null +++ b/src/fw/services/poll_remote/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_POLL_REMOTE + bool "Poll remote" + help + Poll-remote service. + +if SERVICE_POLL_REMOTE + +module = SERVICE_POLL_REMOTE +module-str = Poll remote +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/poll_remote/service.c b/src/fw/services/poll_remote/service.c new file mode 100644 index 0000000000..0c1277b8ad --- /dev/null +++ b/src/fw/services/poll_remote/service.c @@ -0,0 +1,208 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/poll_remote.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/attributes.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_poll_remote, CONFIG_SERVICE_POLL_REMOTE_LOG_LEVEL); + +/* + * Private + */ + +static const uint8_t MIN_INTERVAL_MINUTES = 1; +static const uint16_t ENDPOINT_ID = 0xcafe; +static bool s_running = false; + +typedef enum { + CMD_POLL = 0x0, // Formerly command poll mail + LEGACY_CMD_REQUEST_INTERVAL = 0x1, // for backwards compatibility + CMD_SET_INTERVAL = 0x2, + CMD_REQUEST_POLL = 0x3, +} PollRemoteCommand; + +// Deprecated -- used to set the mail poll interval +typedef struct PACKED { + uint8_t cmd; + uint8_t interval_minutes; +} PollLegacySetIntervalMessage; + +// Poll a service at a specific interval +typedef struct PACKED { + PollRemoteCommand cmd; + PollRemoteService service; + uint8_t interval_minutes; +} PollSetIntervalMessage; + +// Request to poll a service now +typedef struct PACKED { + PollRemoteCommand cmd; + PollRemoteService service; +} PollRequestMessage; + +typedef struct PACKED { + PollRemoteCommand cmd; + PollRemoteService service; +} PollRemoteMessage; + +//! Struct that holds all the state required for a remote polling subsystem. +typedef struct { + PollRemoteService service; + //! The minimum interval between two "poll services" requests. + //! Calls to poll_remote_send_request() will be no-ops if min_interval_minutes has not been reached. + uint8_t min_interval_minutes; + + //! The maximum interval between two "poll services" requests. + //! The automatic sending of poll requests will only occur when max_interval_minutes is reached. + uint8_t max_interval_minutes; + + uint8_t counted_minutes; //!< Number of minutes passed since the last request +} PollRemoteContext; + +static void poll_service_timer_callback(); + +static RegularTimerInfo s_poll_timer = { + .cb = poll_service_timer_callback, + .cb_data = NULL, +}; + +static PollRemoteContext s_poll_remote_contexts[NUM_POLL_REMOTE_SERVICES]; + +static void for_each_context(void (*poll_remote_function)(PollRemoteContext *ctx)) { + for (int i = 0; i < NUM_POLL_REMOTE_SERVICES; i++) { + poll_remote_function(&s_poll_remote_contexts[i]); + } +} + +static bool has_min_interval_passed(PollRemoteContext *ctx) { + return (ctx->counted_minutes >= ctx->min_interval_minutes); +} + +static bool has_max_interval_passed(PollRemoteContext *ctx) { + return (ctx->counted_minutes >= ctx->max_interval_minutes); +} + +// SystemTaskCallback +static void prv_send_request(PollRemoteContext *ctx) { + if (has_min_interval_passed(ctx) == false) { + return; + } + CommSession *session = comm_session_get_system_session(); + if (!session) { + return; + } + // [MT]: comm_session_send_data() doesn't make the link active, + // which is what we want here. If this this changes in the future + // we need to take measures here to make sure we don't pull the link active. + const PollRemoteMessage msg = { + .cmd = CMD_POLL, + .service = ctx->service + }; + comm_session_send_data(session, ENDPOINT_ID, (const uint8_t *)&msg, sizeof(PollRemoteMessage), + COMM_SESSION_DEFAULT_TIMEOUT); + ctx->counted_minutes = 0; +} + +static void context_interval_check(PollRemoteContext *ctx) { + if (ctx->max_interval_minutes == 0) { + return; + } + + ctx->counted_minutes++; + + if (has_max_interval_passed(ctx)) { + prv_send_request(ctx); + } +} + + +static void start(PollRemoteContext *ctx) { + ctx->counted_minutes = 0; +} + +static void set_intervals(PollRemoteContext *ctx, const uint8_t min_interval_minutes, const uint8_t max_interval_minutes) { + ctx->min_interval_minutes = min_interval_minutes; + ctx->max_interval_minutes = max_interval_minutes; +} + +void comm_poll_remote_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { + PollRemoteCommand cmd = data[0]; + + switch (cmd) { + case CMD_REQUEST_POLL: { + PollRequestMessage *msg = (PollRequestMessage *)data; + if (msg->service >= NUM_POLL_REMOTE_SERVICES) { return; } + prv_send_request(&s_poll_remote_contexts[msg->service]); + break; + } + case LEGACY_CMD_REQUEST_INTERVAL: { + PollLegacySetIntervalMessage *msg = (PollLegacySetIntervalMessage *)data; + poll_remote_set_intervals(POLL_REMOTE_SERVICE_MAIL, MIN_INTERVAL_MINUTES, msg->interval_minutes); + break; + } + case CMD_SET_INTERVAL: { + PollSetIntervalMessage *msg = (PollSetIntervalMessage *)data; + if (msg->service >= NUM_POLL_REMOTE_SERVICES) { return; } + poll_remote_set_intervals(msg->service, MIN_INTERVAL_MINUTES, msg->interval_minutes); + break; + } + default: { + PBL_LOG_ERR("Invalid command."); + return; + } + } +} + +/* + * Public + */ + +static void poll_service_system_task_callback(void *data) { + PBL_ASSERTN(s_running); + for_each_context(context_interval_check); +} + +static void poll_service_timer_callback(void *data) { + system_task_add_callback(poll_service_system_task_callback, data); +} + +void poll_remote_init(void) { + for (int i = 0; i < NUM_POLL_REMOTE_SERVICES; i++) { + s_poll_remote_contexts[i].service = i; + } +} + +void poll_remote_send_request(PollRemoteService service) { + prv_send_request(&s_poll_remote_contexts[service]); +} + +void poll_remote_start(void) { + if (s_running) { + return; + } + + s_running = true; + for_each_context(start); + regular_timer_add_minutes_callback(&s_poll_timer); +} + +void poll_remote_stop(void) { + if (!s_running) { + return; + } + + s_running = false; + regular_timer_remove_callback(&s_poll_timer); +} + +void poll_remote_set_intervals(PollRemoteService service, const uint8_t min_interval_minutes, const uint8_t max_interval_minutes) { + set_intervals(&s_poll_remote_contexts[service], min_interval_minutes, max_interval_minutes); + (max_interval_minutes == 0) ? poll_remote_stop() : poll_remote_start(); +} diff --git a/src/fw/services/poll_remote/wscript_build b/src/fw/services/poll_remote/wscript_build new file mode 100644 index 0000000000..2e006df310 --- /dev/null +++ b/src/fw/services/poll_remote/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_poll_remote', +) +bld.env.SERVICES.append('services_poll_remote') diff --git a/src/fw/services/powermode_service/Kconfig b/src/fw/services/powermode_service/Kconfig new file mode 100644 index 0000000000..a8a0814ed9 --- /dev/null +++ b/src/fw/services/powermode_service/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_POWERMODE_SERVICE + bool "Power mode" + help + Power-mode (low-power) service. diff --git a/src/fw/services/powermode_service/service.c b/src/fw/services/powermode_service/service.c new file mode 100644 index 0000000000..3b85a78496 --- /dev/null +++ b/src/fw/services/powermode_service/service.c @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/powermode_service.h" + +#include "drivers/cpumode.h" +#include "os/mutex.h" +#include "system/passert.h" + +#include + +static uint32_t s_refcount; +static PebbleMutex *s_mutex; +static bool s_enabled; + +void powermode_service_init(void) { + s_refcount = 0; + s_mutex = mutex_create(); +} + +void powermode_service_set_enabled(bool enabled) { + s_enabled = enabled; +} + +void powermode_service_request_hp(void) { + if (!s_enabled) { + return; + } + + mutex_lock(s_mutex); + + if (s_refcount == 0) { + cpumode_set(CPUMode_HighPerformance); + } + + s_refcount++; + + mutex_unlock(s_mutex); +} + +void powermode_service_release_hp(void) { + if (!s_enabled) { + return; + } + + mutex_lock(s_mutex); + + if (s_refcount == 0) { + mutex_unlock(s_mutex); + return; + } + + s_refcount--; + + if (s_refcount == 0) { + cpumode_set(CPUMode_LowPower); + } + + mutex_unlock(s_mutex); +} diff --git a/src/fw/services/powermode_service/wscript_build b/src/fw/services/powermode_service/wscript_build new file mode 100644 index 0000000000..078c63f880 --- /dev/null +++ b/src/fw/services/powermode_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_powermode_service', +) +bld.env.SERVICES.append('services_powermode_service') diff --git a/src/fw/services/prf/accessory/accessory_idle_mode.c b/src/fw/services/prf/accessory/accessory_idle_mode.c deleted file mode 100644 index e91cb6814e..0000000000 --- a/src/fw/services/prf/accessory/accessory_idle_mode.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "accessory_idle_mode.h" - -#include "drivers/accessory.h" -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "services/common/system_task.h" -#include "system/logging.h" - -#if PLATFORM_SNOWY || PLATFORM_SPALDING -static const char KNOCKING_CODE[] = "sn0wy"; -#elif PLATFORM_SILK -static const char KNOCKING_CODE[] = "s1lk"; -#elif PLATFORM_ASTERIX -static const char KNOCKING_CODE[] = "aster1x"; -#elif PLATFORM_OBELIX -static const char KNOCKING_CODE[] = "0belix"; -#elif PLATFORM_ROBERT -static const char KNOCKING_CODE[] = "r0bert"; -#elif PLATFORM_CALCULUS -static const char KNOCKING_CODE[] = "c@lculus"; -#else -#error "Unknown platform" -#endif - -static void prv_knocking_complete(void *data) { - mfg_enter_mfg_mode_and_launch_app(); -} - -bool accessory_idle_mode_handle_char(char c) { - // Note: You're in an interrupt here, be careful - - static int s_knocking_state = 0; - - bool should_context_switch = false; - - if (KNOCKING_CODE[s_knocking_state] == c) { - // This character matched! We're now looking for the next character. - ++s_knocking_state; - - PBL_LOG(LOG_LEVEL_DEBUG, "Idle: <%c> Match! State %u", c, s_knocking_state); - - // If we reach the null terminator, we're done! - if (KNOCKING_CODE[s_knocking_state] == 0) { - system_task_add_callback_from_isr( - prv_knocking_complete, NULL, &should_context_switch); - - s_knocking_state = 0; - } - } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Idle: <%c> Mismatch!", c); - - // Wrong character, reset - s_knocking_state = 0; - } - - return should_context_switch; -} - diff --git a/src/fw/services/prf/accessory/accessory_idle_mode.h b/src/fw/services/prf/accessory/accessory_idle_mode.h deleted file mode 100644 index 52b290de35..0000000000 --- a/src/fw/services/prf/accessory/accessory_idle_mode.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool accessory_idle_mode_handle_char(char c); - diff --git a/src/fw/services/prf/accessory/accessory_imaging.c b/src/fw/services/prf/accessory/accessory_imaging.c deleted file mode 100644 index f63e5f458f..0000000000 --- a/src/fw/services/prf/accessory/accessory_imaging.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "accessory_imaging.h" - -#include "console/prompt.h" -#include "drivers/accessory.h" -#include "drivers/flash.h" -#include "flash_region/flash_region.h" -#include "kernel/core_dump.h" -#include "kernel/core_dump_private.h" -#include "mfg/mfg_mode/mfg_factory_mode.h" -#include "resource/resource_storage_flash.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/prf/accessory/accessory_manager.h" -#include "system/bootbits.h" -#include "system/logging.h" -#include "system/passert.h" -#include "system/reset.h" -#include "util/crc32.h" -#include "util/hdlc.h" -#include "util/attributes.h" -#include "util/math.h" - -#include -#include -#include - -#define TIMEOUT_MS (3000) -#define VERSION (1) -#define NUM_RX_BUFFERS (3) -#define MAX_DATA_LENGTH (2048) -#define CHECKSUM_LENGTH (4) -#define MAX_FRAME_LENGTH (MAX_DATA_LENGTH + sizeof(ImagingHeader) + CHECKSUM_LENGTH) - -// flags -#define FLAG_IS_SERVER (1 << 0) -#define FLAG_VERSION (VERSION << 1) - -// opcodes -#define OPCODE_PING (0x01) -#define OPCODE_DISCONNECT (0x02) -#define OPCODE_RESET (0x03) -#define OPCODE_FLASH_GEOMETRY (0x11) -#define OPCODE_FLASH_ERASE (0x12) -#define OPCODE_FLASH_WRITE (0x13) -#define OPCODE_FLASH_CRC (0x14) -#define OPCODE_FLASH_FINALIZE (0x15) -#define OPCODE_FLASH_READ (0x16) - -// flash regions -#define REGION_PRF (0x01) -#define REGION_RESOURCES (0x02) -#define REGION_FW_SCRATCH (0x03) -#define REGION_PFS (0x04) -#define REGION_COREDUMP (0x05) - -// flash read flags -#define FLASH_READ_FLAG_ALL_SAME (1 << 0) - -typedef struct PACKED { - uint8_t flags; - uint8_t opcode; -} ImagingHeader; - -typedef struct PACKED { - uint8_t region; -} FlashGeometryRequest; - -typedef struct PACKED { - uint8_t region; - uint32_t address; - uint32_t length; -} FlashGeometryResponse; - -typedef struct PACKED { - uint32_t address; - uint32_t length; -} FlashEraseRequest; - -typedef struct PACKED { - uint32_t address; - uint32_t length; - uint8_t complete; -} FlashEraseResponse; - -typedef struct PACKED { - uint32_t address; - uint8_t data[]; -} FlashWriteRequest; - -typedef struct PACKED { - uint32_t address; - uint32_t length; -} FlashReadRequest; - -typedef struct PACKED { - uint32_t address; - uint32_t length; -} FlashCRCRequest; - -typedef struct PACKED { - uint32_t address; - uint32_t length; - uint32_t crc; -} FlashCRCResponse; - -typedef struct PACKED { - uint8_t region; -} FlashFinalizeRequest; - -typedef FlashFinalizeRequest FlashFinalizeResponse; // they are currently the same - -typedef struct { - bool is_free; - bool is_valid; - HdlcStreamingContext hdlc_ctx; - uint32_t index; - union { - struct PACKED { - ImagingHeader header; - uint8_t payload[MAX_DATA_LENGTH]; - uint32_t checksum; - } frame; - uint8_t data[MAX_FRAME_LENGTH]; - }; - uint32_t checksum; -} ReceiveBuffer; - -static bool s_enabled; -static TimerID s_timeout_timer; -static ReceiveBuffer s_buffers[NUM_RX_BUFFERS]; -static ReceiveBuffer *s_curr_buf; -static bool s_flash_erase_in_progress; -static int s_no_buffer_count; -static int s_dropped_char_count; - -static void prv_timeout_timer_cb(void *context); - - -// Helper functions -//////////////////////////////////////////////////////////////////// - -static void prv_reset_buffer(ReceiveBuffer *buffer) { - hdlc_streaming_decode_reset(&buffer->hdlc_ctx); - buffer->index = 0; - buffer->checksum = CRC32_INIT; - buffer->is_valid = true; - buffer->is_free = true; -} - - -// Start / stop -//////////////////////////////////////////////////////////////////// - -static void prv_start(void) { - s_curr_buf = NULL; - s_no_buffer_count = 0; - s_dropped_char_count = 0; - for (int i = 0; i < NUM_RX_BUFFERS; ++i) { - prv_reset_buffer(&s_buffers[i]); - } - accessory_manager_set_state(AccessoryInputStateImaging); - accessory_use_dma(true); - s_timeout_timer = new_timer_create(); - new_timer_start(s_timeout_timer, TIMEOUT_MS, prv_timeout_timer_cb, NULL, 0 /* flags */); - PBL_LOG(LOG_LEVEL_DEBUG, "Starting accessory imaging"); -} - -static void prv_stop(void *context) { - if (s_no_buffer_count > 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Ran out of buffers %d times and dropped %d bytes while imaging", - s_no_buffer_count, s_dropped_char_count); - } - flash_prf_set_protection(true); - accessory_use_dma(false); - accessory_manager_set_state(AccessoryInputStateMfg); - new_timer_delete(s_timeout_timer); - PBL_LOG(LOG_LEVEL_DEBUG, "Stopping accessory imaging"); -} - -static void prv_timeout_timer_cb(void *context) { - system_task_add_callback(prv_stop, NULL); -} - - -// Sending -//////////////////////////////////////////////////////////////////// - -static void prv_encode_and_send_data(const void *data, uint32_t length) { - const uint8_t *data_bytes = data; - for (uint32_t i = 0; i < length; i++) { - uint8_t byte = data_bytes[i]; - if (hdlc_encode(&byte)) { - accessory_send_byte(HDLC_ESCAPE); - } - accessory_send_byte(byte); - } -} - -static void prv_send_frame(uint8_t opcode, const void *payload, uint32_t length) { - accessory_disable_input(); - accessory_send_byte(HDLC_FLAG); - - // send the header - const ImagingHeader header = { - .flags = FLAG_IS_SERVER | FLAG_VERSION, - .opcode = opcode - }; - prv_encode_and_send_data(&header, sizeof(header)); - - // send the pyaload - prv_encode_and_send_data(payload, length); - - // send the checksum - uint32_t checksum = CRC32_INIT; - checksum = crc32(checksum, &header, sizeof(header)); - if (payload && length) { - checksum = crc32(checksum, payload, length); - } - prv_encode_and_send_data(&checksum, sizeof(checksum)); - - accessory_send_byte(HDLC_FLAG); - accessory_enable_input(); -} - - -// Request processing -//////////////////////////////////////////////////////////////////// - -static void prv_erase_complete(void *ignored, status_t result) { - s_flash_erase_in_progress = false; -} - -static bool prv_is_erased(uint32_t addr, uint32_t length) { - const uint32_t sectors_to_erase = (length + SECTOR_SIZE_BYTES - 1) / SECTOR_SIZE_BYTES; - for (uint32_t sector = 0; sector < sectors_to_erase; sector++) { - if (!flash_sector_is_erased(sector * SECTOR_SIZE_BYTES + addr)) { - return false; - } - } - return true; -} - -static void prv_handle_ping_request(const void *payload, uint32_t length) { - // echo it back - prv_send_frame(OPCODE_PING, payload, length); -} - -static void prv_handle_disconnect_request(const void *payload, uint32_t length) { - if (length) { - // should be 0 - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - prv_send_frame(OPCODE_DISCONNECT, NULL, 0); - prv_stop(NULL); -} - -static void prv_handle_reset_request(const void *payload, uint32_t length) { - if (length) { - // should be 0 - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - PBL_LOG(LOG_LEVEL_WARNING, "Got reset request"); - prv_send_frame(OPCODE_RESET, NULL, 0); - prv_stop(NULL); - system_reset(); -} - -static bool prv_coredump_flash_base(uint32_t *addr, uint32_t *size) { - CoreDumpFlashHeader flash_hdr; - CoreDumpFlashRegionHeader region_hdr; - uint32_t max_last_used = 0; - uint32_t base_address; - uint32_t last_used_idx = 0; - - // First, see if the flash header has been put in place - flash_read_bytes((uint8_t *)&flash_hdr, CORE_DUMP_FLASH_START, sizeof(flash_hdr)); - - if ((flash_hdr.magic != CORE_DUMP_FLASH_HDR_MAGIC) || - (flash_hdr.unformatted == CORE_DUMP_ALL_UNFORMATTED)) { - return false; - } - - // Find the region with the highest last_used count - for (unsigned int i = 0; i < CORE_DUMP_MAX_IMAGES; i++) { - if (flash_hdr.unformatted & (1 << i)) { - continue; - } - - base_address = core_dump_get_slot_address(i); - flash_read_bytes((uint8_t *)®ion_hdr, base_address, sizeof(region_hdr)); - - if (region_hdr.last_used > max_last_used) { - max_last_used = region_hdr.last_used; - last_used_idx = i; - } - } - - if (max_last_used == 0) { - return false; - } - - *addr = core_dump_get_slot_address(last_used_idx); - if (core_dump_size(*addr, size) != S_SUCCESS) { - return false; - } - *addr += sizeof(CoreDumpFlashRegionHeader); - return true; -} - -static void prv_handle_flash_geometry_request(const void *payload, uint32_t length) { - if (length != sizeof(FlashGeometryRequest)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - const FlashGeometryRequest *request = payload; - - FlashGeometryResponse response = { - .region = request->region - }; - if (request->region == REGION_PRF) { - // assume we're about to write to this region, so unlock it - flash_prf_set_protection(false); - response.address = FLASH_REGION_SAFE_FIRMWARE_BEGIN; - response.length = FLASH_REGION_SAFE_FIRMWARE_END - response.address; - } else if (request->region == REGION_RESOURCES) { - const SystemResourceBank *bank = resource_storage_flash_get_unused_bank(); - response.address = bank->begin; - response.length = bank->end - response.address; - } else if (request->region == REGION_FW_SCRATCH) { - response.address = FLASH_REGION_FIRMWARE_DEST_BEGIN; - response.length = FLASH_REGION_FIRMWARE_DEST_END - response.address; - } else if (request->region == REGION_PFS) { - response.address = FLASH_REGION_FILESYSTEM_BEGIN; - response.length = FLASH_REGION_FILESYSTEM_END - response.address; - } else if (request->region == REGION_COREDUMP) { - if (!prv_coredump_flash_base(&response.address, &response.length)) { - response.address = 0; - response.length = 0; - } - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid region (%"PRIu8")", request->region); - } - prv_send_frame(OPCODE_FLASH_GEOMETRY, &response, sizeof(response)); -} - -static void prv_handle_flash_erase_request(const void *payload, uint32_t length) { - if (length != sizeof(FlashEraseRequest)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - const FlashEraseRequest *request = payload; - - FlashEraseResponse response = { - .address = request->address, - .length = request->length - }; - bool start_erase = false; - if (s_flash_erase_in_progress) { - response.complete = 0; - } else if (prv_is_erased(request->address, request->length)) { - response.complete = 1; - } else { - response.complete = 0; - start_erase = true; - } - prv_send_frame(OPCODE_FLASH_ERASE, &response, sizeof(response)); - - // start the erase after sending the response - if (start_erase) { - uint32_t end_address = request->address + request->length; - s_flash_erase_in_progress = true; - flash_erase_optimal_range( - request->address, request->address, end_address, - (end_address + SECTOR_SIZE_BYTES - 1) & SECTOR_ADDR_MASK, - prv_erase_complete, NULL); - } -} - -static void prv_handle_flash_write_request(const void *payload, uint32_t length) { - if (length < sizeof(FlashWriteRequest)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - const FlashWriteRequest *request = payload; - length -= offsetof(FlashWriteRequest, data); - - flash_write_bytes(request->data, request->address, length); -} - -static void prv_handle_flash_read_request(const void *payload, uint32_t length) { - if (length < sizeof(FlashReadRequest)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - const FlashReadRequest *request = payload; - if (request->length > MAX_DATA_LENGTH) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid request length (%"PRIu32")", request->length); - } - - // leave 1 byte at the start for flags - static uint8_t buffer[1 + MAX_DATA_LENGTH]; - flash_read_bytes(&buffer[1], request->address, request->length); - bool is_all_same = true; - uint8_t same_byte = buffer[1]; - for (uint32_t i = 1; i < request->length; i++) { - if (buffer[i + 1] != same_byte) { - is_all_same = false; - break; - } - } - // As an optimization, if all the bytes are the same, we set a flag and just send a single byte. - buffer[0] = is_all_same ? FLASH_READ_FLAG_ALL_SAME : 0; // flags - const uint32_t frame_length = is_all_same ? 2 : request->length + 1; - prv_send_frame(OPCODE_FLASH_READ, buffer, frame_length); -} - -static void prv_handle_flash_crc_request(const void *payload, uint32_t length) { - // there can be 1 or more payloads - if (!length || (length % sizeof(FlashCRCRequest))) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - const FlashCRCRequest *request = payload; - const uint32_t num_entries = length / sizeof(FlashCRCRequest); - - // this is just static cause it's potentially too big to put on the stack - static FlashCRCResponse response[MAX_DATA_LENGTH / sizeof(FlashCRCResponse)]; - for (uint32_t i = 0; i < num_entries; i++) { - const FlashCRCRequest *entry = &request[i]; - response[i] = (FlashCRCResponse) { - .address = entry->address, - .length = entry->length, - .crc = flash_crc32(entry->address, entry->length) - }; - } - - prv_send_frame(OPCODE_FLASH_CRC, &response, sizeof(FlashCRCResponse) * num_entries); -} - -static void prv_handle_flash_finalize_request(const void *payload, uint32_t length) { - if (length != sizeof(FlashFinalizeRequest)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid length (%"PRIu32")", length); - return; - } - - const FlashFinalizeRequest *request = payload; - - FlashFinalizeResponse response = { - .region = request->region - }; - if (request->region == REGION_PRF) { - flash_prf_set_protection(true); - } else if (request->region == REGION_RESOURCES) { - boot_bit_set(BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE); - } else if (request->region == REGION_FW_SCRATCH) { - boot_bit_set(BOOT_BIT_NEW_FW_AVAILABLE); - } else if (request->region == REGION_PFS) { - // Do nothing! - } else if (request->region == REGION_COREDUMP) { - // Do nothing! - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid region (%"PRIu8")", request->region); - } - prv_send_frame(OPCODE_FLASH_FINALIZE, &response, sizeof(response)); -} - -static void prv_process_frame(void *context) { - ReceiveBuffer *buf = context; - const ImagingHeader *header = &buf->frame.header; - const void *payload = buf->frame.payload; - const uint32_t payload_length = buf->index - sizeof(ImagingHeader) - CHECKSUM_LENGTH; - PBL_ASSERTN(payload_length <= MAX_DATA_LENGTH); - - // sanity check - if (header->flags & FLAG_IS_SERVER) { - PBL_LOG(LOG_LEVEL_ERROR, "Got frame from server (loopback?)"); - prv_reset_buffer(buf); - return; - } - - // reset the timeout timer - new_timer_start(s_timeout_timer, TIMEOUT_MS, prv_timeout_timer_cb, NULL, 0 /* flags */); - - // look at the opcode and handle this message - if (header->opcode == OPCODE_PING) { - prv_handle_ping_request(payload, payload_length); - } else if (header->opcode == OPCODE_DISCONNECT) { - prv_handle_disconnect_request(payload, payload_length); - } else if (header->opcode == OPCODE_RESET) { - prv_handle_reset_request(payload, payload_length); - } else if (header->opcode == OPCODE_FLASH_GEOMETRY) { - prv_handle_flash_geometry_request(payload, payload_length); - } else if (header->opcode == OPCODE_FLASH_ERASE) { - prv_handle_flash_erase_request(payload, payload_length); - } else if (header->opcode == OPCODE_FLASH_WRITE) { - prv_handle_flash_write_request(payload, payload_length); - } else if (header->opcode == OPCODE_FLASH_READ) { - prv_handle_flash_read_request(payload, payload_length); - } else if (header->opcode == OPCODE_FLASH_CRC) { - prv_handle_flash_crc_request(payload, payload_length); - } else if (header->opcode == OPCODE_FLASH_FINALIZE) { - prv_handle_flash_finalize_request(payload, payload_length); - } else { - PBL_LOG(LOG_LEVEL_ERROR, "Got unexpected opcode (0x%x)", header->opcode); - } - - prv_reset_buffer(buf); -} - - -// Receiving (ISR-based) -//////////////////////////////////////////////////////////////////// - -static bool prv_handle_data(uint8_t data) { - bool should_context_switch = false; - bool hdlc_err; - bool should_store; - bool is_complete = hdlc_streaming_decode(&s_curr_buf->hdlc_ctx, &data, &should_store, &hdlc_err); - if (hdlc_err) { - s_curr_buf->is_valid = false; - } else if (is_complete) { - if (s_curr_buf->is_valid && s_curr_buf->checksum == CRC32_RESIDUE && s_curr_buf->index) { - // queue up processing of this frame and clear s_curr_buf so we'll switch to a new one - system_task_add_callback_from_isr(prv_process_frame, s_curr_buf, &should_context_switch); - } else { - prv_reset_buffer(s_curr_buf); - } - s_curr_buf = NULL; - } else if (should_store && s_curr_buf->is_valid) { - if (s_curr_buf->index < MAX_FRAME_LENGTH) { - // store this byte - s_curr_buf->data[(s_curr_buf->index)++] = data; - s_curr_buf->checksum = crc32(s_curr_buf->checksum, &data, 1); - } else { - // too long! - s_curr_buf->is_valid = false; - } - } - return should_context_switch; -} - -bool accessory_imaging_handle_char(char c) { - static bool has_no_buffer = false; - if (!s_curr_buf) { - // find a buffer to write into - for (int i = 0; i < NUM_RX_BUFFERS; ++i) { - if (s_buffers[i].is_free) { - s_buffers[i].is_free = false; - s_curr_buf = &s_buffers[i]; - break; - } - } - if (!s_curr_buf) { - // no available buffer :( - if (!has_no_buffer) { - s_no_buffer_count++; - } - has_no_buffer = true; - s_dropped_char_count++; - return false; - } - } - has_no_buffer = false; - - return prv_handle_data((uint8_t)c); -} - - -// Other exported functions -//////////////////////////////////////////////////////////////////// - -void accessory_imaging_enable(void) { - s_enabled = true; -} - -void command_accessory_imaging_start(void) { - if (!s_enabled) { - prompt_send_response("Command not available."); - } - prv_start(); -} diff --git a/src/fw/services/prf/accessory/accessory_imaging.h b/src/fw/services/prf/accessory/accessory_imaging.h deleted file mode 100644 index 2cb53b5f79..0000000000 --- a/src/fw/services/prf/accessory/accessory_imaging.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - - -bool accessory_imaging_handle_char(char c); -void accessory_imaging_enable(void); diff --git a/src/fw/services/prf/accessory/accessory_manager.c b/src/fw/services/prf/accessory/accessory_manager.c deleted file mode 100644 index d2eef89dc1..0000000000 --- a/src/fw/services/prf/accessory/accessory_manager.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "accessory_manager.h" - -#include "accessory_idle_mode.h" -#include "accessory_imaging.h" -#include "accessory_mfg_mode.h" - -#include "drivers/accessory.h" - -#include "system/logging.h" -#include "os/mutex.h" - -static AccessoryInputState s_input_state = AccessoryInputStateIdle; -static PebbleMutex *s_state_mutex; - -void accessory_manager_init(void) { - s_state_mutex = mutex_create(); -} - -bool accessory_manager_handle_character_from_isr(char c) { - // NOTE: THIS IS RUN WITHIN AN ISR - switch (s_input_state) { - case AccessoryInputStateMfg: - return accessory_mfg_mode_handle_char(c); - case AccessoryInputStateIdle: - return accessory_idle_mode_handle_char(c); - case AccessoryInputStateImaging: - return accessory_imaging_handle_char(c); - case AccessoryInputStateMic: - // fallthrough - default: - break; - } - return false; -} - -bool accessory_manager_handle_break_from_isr(void) { - // NOTE: THIS IS RUN WITHIN AN ISR - switch (s_input_state) { - case AccessoryInputStateIdle: - case AccessoryInputStateMic: - case AccessoryInputStateMfg: - case AccessoryInputStateImaging: - // fallthrough - default: - break; - } - return false; -} - -// Valid state transitions are: -// +-----+ -// | IMG | -// +-----+ -// ^ -// | -// v -// +------+ +-----+ +-----+ -// | Idle |<-->| MFG |<-->| MIC | -// +------+ +-----+ +-----+ -static bool prv_is_valid_state_transition(AccessoryInputState new_state) { - if (s_input_state == AccessoryInputStateIdle) { - return new_state == AccessoryInputStateMfg; - } else if (s_input_state == AccessoryInputStateMfg) { - return (new_state == AccessoryInputStateIdle) || - (new_state == AccessoryInputStateImaging) || - (new_state == AccessoryInputStateMic); - } else if (s_input_state == AccessoryInputStateImaging) { - return new_state == AccessoryInputStateMfg; - } else if (s_input_state == AccessoryInputStateMic) { - return new_state == AccessoryInputStateMfg; - } - return false; -} - -// The accessory state is used to differentiate between different consumers of the accessory port. -// Before a consumer uses the accessory port, it must set its state and return the state to idle -// once it has finished. No other consumer will be permitted to use the accessory port until the -// state is returned to idle. -bool accessory_manager_set_state(AccessoryInputState state) { - mutex_lock(s_state_mutex); - - if (!prv_is_valid_state_transition(state)) { - // the state is already set by somebody else - mutex_unlock(s_state_mutex); - return false; - } - - s_input_state = state; - switch (s_input_state) { - case AccessoryInputStateMfg: - accessory_enable_input(); - accessory_set_baudrate(AccessoryBaud115200); - accessory_set_power(false); - accessory_mfg_mode_start(); - break; - case AccessoryInputStateIdle: - // restore accessory to default state - accessory_enable_input(); - accessory_set_baudrate(AccessoryBaud115200); - accessory_set_power(false); - break; - case AccessoryInputStateImaging: - accessory_enable_input(); - accessory_set_baudrate(AccessoryBaud921600); - accessory_set_power(false); - break; - case AccessoryInputStateMic: - // fallthrough - default: - break; - } - - mutex_unlock(s_state_mutex); - PBL_LOG(LOG_LEVEL_DEBUG, "Setting accessory state to %u", state); - return true; -} diff --git a/src/fw/services/prf/accessory/accessory_manager.h b/src/fw/services/prf/accessory/accessory_manager.h deleted file mode 100644 index 8faacda21f..0000000000 --- a/src/fw/services/prf/accessory/accessory_manager.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum { - AccessoryInputStateIdle, - AccessoryInputStateMfg, - AccessoryInputStateMic, - AccessoryInputStateImaging, -} AccessoryInputState; - -void accessory_manager_init(void); -bool accessory_manager_set_state(AccessoryInputState state); diff --git a/src/fw/services/prf/accessory/accessory_mfg_mode.c b/src/fw/services/prf/accessory/accessory_mfg_mode.c deleted file mode 100644 index 4543f75dbe..0000000000 --- a/src/fw/services/prf/accessory/accessory_mfg_mode.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "console/prompt.h" -#include "drivers/accessory.h" -#include "services/common/system_task.h" -#include "system/logging.h" -#include "util/likely.h" -#include "util/math.h" - -#include -#include - -static void prv_command_response_callback(const char* response) { - accessory_send_data((const uint8_t*) response, strlen(response)); - accessory_send_data((const uint8_t*) "\r\n", sizeof(char) * 2); -} - -static void prv_display_prompt(void) { - accessory_send_data((const uint8_t*) ">", sizeof(char)); -} - -static PromptContext s_prompt_context = { - .response_callback = prv_command_response_callback, - .command_complete_callback = prv_display_prompt, -}; - -static void prv_execute_command(void *data) { - PromptContext *prompt_context = (PromptContext *)data; - - // Copy the command and append a NULL so we can print it for debugging purposes - char buffer[40]; - size_t cropped_length = MIN(sizeof(buffer) - 1, prompt_context->write_index); - memcpy(buffer, prompt_context->buffer, cropped_length); - buffer[cropped_length] = 0; - PBL_LOG(LOG_LEVEL_DEBUG, "Exec command <%s>", buffer); - - prompt_context_execute(prompt_context); -} - -void accessory_mfg_mode_start(void) { - #ifdef DISABLE_PROMPT - return; - #else - prv_display_prompt(); - #endif -} - -bool accessory_mfg_mode_handle_char(char c) { - // Note: You're in an interrupt here, be careful -#if DISABLE_PROMPT - return false; -#else - if (UNLIKELY(prompt_command_is_executing())) { - return false; - } - bool should_context_switch = false; - - if (LIKELY(c >= 0x20 && c < 127)) { - prompt_context_append_char(&s_prompt_context, c); - } else if (UNLIKELY(c == 0xd)) { // Enter key - system_task_add_callback_from_isr( - prv_execute_command, &s_prompt_context, &should_context_switch); - } else if (UNLIKELY(c == 0x3)) { // CTRL-C - // FIXME: Clean this up so this logic doesn't need to be duplicated here - s_prompt_context.write_index = 0; - prv_display_prompt(); - } - - return should_context_switch; -#endif -} - diff --git a/src/fw/services/prf/accessory/accessory_mfg_mode.h b/src/fw/services/prf/accessory/accessory_mfg_mode.h deleted file mode 100644 index df1dbf5b75..0000000000 --- a/src/fw/services/prf/accessory/accessory_mfg_mode.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Call this as you're just entering manufacturing mode to do initalial setup. -void accessory_mfg_mode_start(void); - -//! Called on an ISR to handle a character from the accessory connector. -bool accessory_mfg_mode_handle_char(char c); - diff --git a/src/fw/services/prf/analytics/analytics.c b/src/fw/services/prf/analytics/analytics.c deleted file mode 100644 index 332117a93b..0000000000 --- a/src/fw/services/prf/analytics/analytics.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics.h" - -//! Stub for PRF - -void analytics_init(void) { -} - -void analytics_set(AnalyticsMetric metric, int64_t value, AnalyticsClient client) { -} - -void analytics_max(AnalyticsMetric metric, int64_t val, AnalyticsClient client) { -} - -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { -} - -void analytics_add(AnalyticsMetric metric, int64_t amount, AnalyticsClient client) { -} - -void analytics_stopwatch_start(AnalyticsMetric metric, AnalyticsClient client) { -} - -void analytics_stopwatch_start_at_rate(AnalyticsMetric metric, - uint32_t count_per_sec, - AnalyticsClient client) { -} - -void analytics_stopwatch_stop(AnalyticsMetric metric) { -} - -void analytics_event_app_oom(AnalyticsEvent type, - uint32_t requested_size, uint32_t total_size, - uint32_t total_free, uint32_t largest_free_block) { -} - -void analytics_event_app_launch(const Uuid *uuid) { -} - -void analytics_event_bt_connection_or_disconnection(AnalyticsEvent type, uint8_t reason) { -} - -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) { -} - -void analytics_event_bt_cc2564x_lockup_error(void) { -} - -void analytics_event_bt_app_launch_error(uint8_t gatt_error) { -} - -void analytics_event_session_close(bool is_system_session, const Uuid *optional_app_uuid, - CommSessionCloseReason reason, uint16_t session_duration_mins) { -} - -void analytics_event_bt_le_disconnection(uint8_t reason, uint8_t remote_bt_version, - uint16_t remote_bt_company_id, - uint16_t remote_bt_subversion) { -} diff --git a/src/fw/services/prf/analytics/analytics_data_syscalls.c b/src/fw/services/prf/analytics/analytics_data_syscalls.c deleted file mode 100644 index 6ff1fe7175..0000000000 --- a/src/fw/services/prf/analytics/analytics_data_syscalls.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" - -//! Stub for PRF - -void sys_analytics_set(AnalyticsMetric metric, uint64_t value, AnalyticsClient client) { -} - -void sys_analytics_add(AnalyticsMetric metric, uint64_t increment, AnalyticsClient client) { -} - -void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { -} - -void sys_analytics_max(AnalyticsMetric metric, int64_t val, AnalyticsClient client) { -} - -void sys_analytics_logging_log_event(AnalyticsEventBlob *event_blob) { -} diff --git a/src/fw/services/prf/analytics/analytics_event.c b/src/fw/services/prf/analytics/analytics_event.c deleted file mode 100644 index f8afce6ee7..0000000000 --- a/src/fw/services/prf/analytics/analytics_event.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/uuid.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/comm_session/session_internal.h" - -//! Stub for PRF - -void analytics_event_crash(uint8_t crash_code, uint32_t link_register) { -} - -void analytics_event_local_bt_disconnect(uint16_t handle, uint32_t lr) { -} - -typedef struct CommSession CommSession; -void analytics_event_put_byte_stats( - CommSession *session, bool crc_good, uint8_t type, - uint32_t bytes_transferred, uint32_t elapsed_time_ms, - uint32_t conn_events, uint32_t sync_errors, uint32_t skip_errors, uint32_t other_errors) { -} - -void analytics_event_PPoGATT_disconnect(time_t timestamp, bool successful_reconnect) { -} - -void analytics_event_get_bytes_stats( - CommSession *session, uint8_t type, uint32_t bytes_transferred, uint32_t elapsed_time_ms, - uint32_t conn_events, uint32_t sync_errors, uint32_t skip_errors, uint32_t other_errors) { -} diff --git a/src/fw/services/prf/bluetooth/ble_hrm_stubs.c b/src/fw/services/prf/bluetooth/ble_hrm_stubs.c deleted file mode 100644 index 7430235e9a..0000000000 --- a/src/fw/services/prf/bluetooth/ble_hrm_stubs.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/bluetooth/ble_hrm.h" - -bool ble_hrm_is_supported_and_enabled(void) { - return false; -} - -void ble_hrm_handle_disconnection(GAPLEConnection *connection) { -} - -void ble_hrm_init(void) { -} - -void ble_hrm_deinit(void) { -} diff --git a/src/fw/services/prf/bluetooth/bluetooth_persistent_storage.c b/src/fw/services/prf/bluetooth/bluetooth_persistent_storage.c deleted file mode 100644 index 524c38175e..0000000000 --- a/src/fw/services/prf/bluetooth/bluetooth_persistent_storage.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/bluetooth/bluetooth_persistent_storage.h" - -#include "comm/ble/gap_le_connect.h" -#include "comm/ble/gap_le_slave_reconnect.h" - -#include "comm/bt_lock.h" - -#include "services/common/bluetooth/pairability.h" -#include "services/common/analytics/analytics.h" -#include "services/normal/settings/settings_file.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" - -#include "comm/ble/kernel_le_client/kernel_le_client.h" - -#include "system/logging.h" - -#include -#include -#include -#include -#include - - -//! This is just an interface for the shared PRF storage - - -//! These don't matter at all -#define BLE_BONDING_ID (0) -#define BT_CLASSIC_BONDING_ID (1) - -#define BT_CCCD_ID_MIN 0x80U - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BLE Pairing Info - -static void prv_call_ble_bonding_change_handlers(BTBondingID bonding, BtPersistBondingOp op) { - gap_le_connect_handle_bonding_change(bonding, op); - kernel_le_client_handle_bonding_change(bonding, op); - bt_pairability_update_due_to_bonding_change(); -} - -static BTBondingID prv_bt_persistent_storage_store_ble_pairing( - const SMPairingInfo *new_pairing_info, bool is_gateway, bool requires_address_pinning, - uint8_t flags, const char *device_name, BtPersistBondingOp op) { - if (new_pairing_info && is_gateway) { - shared_prf_storage_store_ble_pairing_data(new_pairing_info, device_name, - requires_address_pinning, - flags); - prv_call_ble_bonding_change_handlers(BLE_BONDING_ID, op); - return BLE_BONDING_ID; - } - - return BT_BONDING_ID_INVALID; -} - -bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *addr) { - shared_prf_storage_set_ble_pinned_address(addr); - return true; -} - -bool bt_persistent_storage_has_pinned_ble_pairings(void) { - bool requires_address_pinning_out = false; - shared_prf_storage_get_ble_pairing_data(NULL, NULL, &requires_address_pinning_out, NULL); - return requires_address_pinning_out; -} - -bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { - return shared_prf_storage_get_ble_pinned_address(address_out); -} - -BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *new_pairing_info, - bool is_gateway, const char *device_name, - bool requires_address_pinning, - uint8_t flags) { - // We only have one slot in PRF and all pairing info (except the device - // name) will arrive in one-shot so anytime this routine gets called it - // means we have 'added' a new pairing - - bool is_updating_existing = false; - SMPairingInfo existing_pairing_info; - if (shared_prf_storage_get_ble_pairing_data(&existing_pairing_info, NULL, NULL, NULL)) { - if (sm_is_pairing_info_equal_identity(new_pairing_info, &existing_pairing_info)) { - // Treat re-pairing an existing device as an "update" instead of deletion+addition, - // because there is only one bonding ID that gets re-used, a deletion would otherwise cause a - // disconnection to happen. See PBL-24737. - PBL_LOG(LOG_LEVEL_INFO, "Re-pairing previously paired LE device"); - is_updating_existing = true; - } else { - // Since we only have one slot, this means we are about to delete what was - // already there so handle the deletion if a valid pairing was stored - prv_call_ble_bonding_change_handlers(BLE_BONDING_ID, BtPersistBondingOpWillDelete); - } - } - - BtPersistBondingOp pairing_op = - is_updating_existing ? BtPersistBondingOpDidChange : BtPersistBondingOpDidAdd; - return (prv_bt_persistent_storage_store_ble_pairing(new_pairing_info, is_gateway, - requires_address_pinning, - flags, device_name, pairing_op)); -} - -bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name) { - // A device name has come in, update the name of our currently paired device - SMPairingInfo data = {}; - bool requires_address_pinning = false; - uint8_t flags = 0; - if (!shared_prf_storage_get_ble_pairing_data(&data, NULL, &requires_address_pinning, &flags)) { - PBL_LOG(LOG_LEVEL_ERROR, "Tried to store device name, but pairing no longer around."); - return false; - } - // In PRF, only the gateway should get paired, so default to "true": - return (BT_BONDING_ID_INVALID != - prv_bt_persistent_storage_store_ble_pairing(&data, true /* is_gateway */, - requires_address_pinning, flags, - device_name, BtPersistBondingOpDidChange)); -} - -static void prv_remove_ble_bonding_from_bt_driver(void) { - if (!bt_ctl_is_bluetooth_running()) { - return; - } - BleBonding bonding = { - .is_gateway = true, - }; - if (!shared_prf_storage_get_ble_pairing_data(&bonding.pairing_info, NULL, NULL, NULL)) { - return; - } - bt_driver_handle_host_removed_bonding(&bonding); -} - -void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID bonding) { - prv_remove_ble_bonding_from_bt_driver(); - shared_prf_storage_erase_ble_pairing_data(); - prv_call_ble_bonding_change_handlers(bonding, BtPersistBondingOpWillDelete); -} - -void bt_persistent_storage_delete_ble_pairing_by_addr(const BTDeviceInternal *device) { - bt_persistent_storage_delete_ble_pairing_by_id(BLE_BONDING_ID); -} - -bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding, - SMIdentityResolvingKey *IRK_out, - BTDeviceInternal *device_out, - char *name_out) { - SMPairingInfo data; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - if (!shared_prf_storage_get_ble_pairing_data(&data, name, NULL, NULL)) { - return false; - } - - if (IRK_out) { - *IRK_out = data.irk; - } - if (device_out) { - *device_out = data.identity; - } - if (name_out) { - strncpy(name_out, name, BT_DEVICE_NAME_BUFFER_SIZE); - name_out[BT_DEVICE_NAME_BUFFER_SIZE - 1] = 0; - } - - return true; -} - -bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device, - SMIdentityResolvingKey *IRK_out, - char name[BT_DEVICE_NAME_BUFFER_SIZE]) { - BTDeviceInternal device_out = {}; - bool rv = bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, IRK_out, &device_out, name); - return (rv && bt_device_equal(&device->opaque, &device_out.opaque)); -} - -void bt_persistent_storage_set_active_ble_gateway(BTBondingID bonding) { -} - -BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void) { - return BLE_BONDING_ID; -} - -bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding) { - return bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, NULL, NULL, NULL); -} - -bool bt_persistent_storage_has_ble_ancs_bonding(void) { - return bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, NULL, NULL, NULL); -} - -bool bt_persistent_storage_has_active_ble_gateway_bonding(void) { - return bt_persistent_storage_get_ble_pairing_by_id(BLE_BONDING_ID, NULL, NULL, NULL); -} - -void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context) { - return; -} - -void bt_persistent_storage_register_existing_ble_bondings(void) { - BleBonding bonding = {}; - uint8_t flags; - if (!shared_prf_storage_get_ble_pairing_data(&bonding.pairing_info, NULL, NULL, &flags)) { - return; - } - bonding.is_gateway = true; - bonding.flags = flags; - bt_driver_handle_host_added_bonding(&bonding); -} - -// PRF does not support persistent CCCD storage, these are just stubs - -BTCCCDID bt_persistent_storage_store_cccd(const BleCCCD *cccd) { - return BT_CCCD_ID_MIN; -} - -bool bt_persistent_storage_delete_cccd(const BTDeviceInternal *peer, uint16_t chr_val_handle) { - return true; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BT Classic Pairing Info - - -static void prv_call_bt_classic_bonding_change_handlers(BTBondingID bonding, - BtPersistBondingOp op) { - bt_pairability_update_due_to_bonding_change(); -} - -BTBondingID bt_persistent_storage_store_bt_classic_pairing(BTDeviceAddress *address, - SM128BitKey *key, - char *name, uint8_t *platform_bits) { - if (address) { - if (key) { - // We should really collect all of the classic info and store once its complete - // However, since platform bits are going to be the last piece collected its ok - // to 0 it out here - uint8_t platform_bits_val = platform_bits ? *platform_bits : 0x00; - shared_prf_storage_store_bt_classic_pairing_data(address, name, key, platform_bits_val); - } - if (platform_bits) { - shared_prf_storage_store_platform_bits(*platform_bits); - } - prv_call_bt_classic_bonding_change_handlers(BT_CLASSIC_BONDING_ID, BtPersistBondingOpDidChange); - return BT_CLASSIC_BONDING_ID; - } - - return BT_BONDING_ID_INVALID; -} - -void bt_persistent_storage_delete_bt_classic_pairing_by_id(BTBondingID bonding) { - shared_prf_storage_erase_bt_classic_pairing_data(); - prv_call_bt_classic_bonding_change_handlers(bonding, BtPersistBondingOpWillDelete); - bt_pairability_update_due_to_bonding_change(); -} - -void bt_persistent_storage_delete_bt_classic_pairing_by_addr(const BTDeviceAddress *bd_addr) { - if (!bd_addr) { - return; - } - - bt_persistent_storage_delete_bt_classic_pairing_by_id(BT_CLASSIC_BONDING_ID); -} - -bool bt_persistent_storage_get_bt_classic_pairing_by_id(BTBondingID bonding, - BTDeviceAddress *address_out, - SM128BitKey *link_key_out, - char *name_out, - uint8_t *platform_bits_out) { - BTDeviceAddress addr; - char name[BT_DEVICE_NAME_BUFFER_SIZE]; - SM128BitKey link_key; - uint8_t platform_bits; - if (!shared_prf_storage_get_bt_classic_pairing_data(&addr, name, &link_key, &platform_bits)) { - return false; - } - - if (address_out) { - *address_out = addr; - } - if (link_key_out) { - *link_key_out = link_key; - } - if (name_out) { - strncpy(name_out, name, BT_DEVICE_NAME_BUFFER_SIZE); - name_out[BT_DEVICE_NAME_BUFFER_SIZE - 1] = 0; - } - if (platform_bits_out) { - *platform_bits_out = platform_bits; - } - - return true; -} - -BTBondingID bt_persistent_storage_get_bt_classic_pairing_by_addr(BTDeviceAddress* addr_in, - SM128BitKey *link_key_out, - char *name_out, - uint8_t *platform_bits_out) { - if (bt_persistent_storage_get_bt_classic_pairing_by_id(BT_CLASSIC_BONDING_ID, NULL, link_key_out, - name_out, platform_bits_out)) { - return BT_CLASSIC_BONDING_ID; - } - - return BT_BONDING_ID_INVALID; -} - -bool bt_persistent_storage_has_active_bt_classic_gateway_bonding(void) { - return bt_persistent_storage_get_bt_classic_pairing_by_id(BT_CLASSIC_BONDING_ID, - NULL, NULL, NULL, NULL); -} - -void bt_persistent_storage_for_each_bt_classic_pairing(BtPersistBondingDBEachBTClassic cb, - void *context) { - return; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Local Device Info - -void bt_persistent_storage_set_active_gateway(BTBondingID bonding) { - return; -} - -bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out, - BtPersistBondingType *type_out) { - if (bt_persistent_storage_get_bt_classic_pairing_by_id(BT_CLASSIC_BONDING_ID, - NULL, NULL, NULL, NULL)) { - *bonding_out = BT_CLASSIC_BONDING_ID; - *type_out = BtPersistBondingTypeBTClassic; - return true; - } else { - return false; - } -} - -bool bt_persistent_storage_is_unfaithful(void) { - return true; -} - -void bt_persistent_storage_set_unfaithful(bool is_unfaithful) { - return; -} - -bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { - return shared_prf_storage_get_root_key(key_type, key_out); -} - -void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in) { - shared_prf_storage_set_root_keys(keys_in); -} - -bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { - return shared_prf_storage_get_local_device_name(local_device_name_out, max_size); -} - -void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t size) { - shared_prf_storage_set_local_device_name(local_device_name); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Remote Device Info - -void bt_persistent_storage_get_cached_system_capabilities( - PebbleProtocolCapabilities *capabilities_out) { -} - -void bt_persistent_storage_set_cached_system_capabilities( - const PebbleProtocolCapabilities *capabilities) { -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Common - -void bt_persistent_storage_init(void) { -} - -void bt_persistent_storage_delete_all(void) { -} - -void bt_persistent_storage_delete_all_pairings(void) { - bt_persistent_storage_delete_ble_pairing_by_id(BLE_BONDING_ID); - if (bt_driver_supports_bt_classic()) { - bt_persistent_storage_delete_bt_classic_pairing_by_id(BT_CLASSIC_BONDING_ID); - } -} diff --git a/src/fw/services/prf/comm_session/app_session_capabilities.c b/src/fw/services/prf/comm_session/app_session_capabilities.c deleted file mode 100644 index 8aedcfbed9..0000000000 --- a/src/fw/services/prf/comm_session/app_session_capabilities.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/uuid.h" - -void comm_session_app_session_capabilities_init(void) { -} -void comm_session_app_session_capabilities_evict(const Uuid *app_uuid) { -} diff --git a/src/fw/services/prf/idle_watchdog.c b/src/fw/services/prf/idle_watchdog.c deleted file mode 100644 index 49b68c5378..0000000000 --- a/src/fw/services/prf/idle_watchdog.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "idle_watchdog.h" - -#include "applib/event_service_client.h" -#include "comm/ble/gap_le_connection.h" -#include "services/common/battery/battery_monitor.h" -#include "services/common/regular_timer.h" -#include "services/common/system_task.h" -#include "system/reboot_reason.h" -#include "kernel/util/standby.h" - -#include - -#define PRF_IDLE_TIMEOUT_MINUTES 10 -static RegularTimerInfo s_is_idle_timer; - - -static void prv_handle_watchdog_timeout_cb(void *not_used) { - GAPLEConnection *le_connection = gap_le_connection_any(); - - if (le_connection || bt_driver_classic_is_connected()) { - // We are still connected, don't shut down - return; - } - - BatteryChargeState current_state = battery_get_charge_state(); - if (current_state.is_plugged) { - // We are plugged in, don't shut down - return; - } - - enter_standby(RebootReasonCode_PrfIdle); -} - -static void prv_handle_watchdog_timeout(void *not_used) { - system_task_add_callback(prv_handle_watchdog_timeout_cb, NULL); -} - -static void prv_start_watchdog(void) { - s_is_idle_timer = (const RegularTimerInfo) { - .cb = prv_handle_watchdog_timeout, - }; - - regular_timer_add_multiminute_callback(&s_is_idle_timer, - PRF_IDLE_TIMEOUT_MINUTES); -} - -void prv_watchdog_feed(PebbleEvent *e, void *context) { - if (regular_timer_is_scheduled(&s_is_idle_timer)) { - prv_start_watchdog(); - } -} - - -void prf_idle_watchdog_start(void) { - // Possible scenario: connect -> 9.9 minutes elapse -> disconnect - // Feeding the watchdog on bt events ensures we don't shutdown after being - // idle for only 0.1 minutes - static EventServiceInfo bt_event_info; - bt_event_info = (EventServiceInfo) { - .type = PEBBLE_BT_CONNECTION_EVENT, - .handler = prv_watchdog_feed, - }; - event_service_client_subscribe(&bt_event_info); - - // Possible scenario: plug in watch to charge -> 9.9 minutes elapse -> remove watch from charger - // Feeding the watchdog on usb events ensures we don't shutdown as the watch is about to be used - static EventServiceInfo battery_event_info; - battery_event_info = (EventServiceInfo) { - .type = PEBBLE_BATTERY_CONNECTION_EVENT, - .handler = prv_watchdog_feed, - }; - event_service_client_subscribe(&battery_event_info); - - // The watch is clearly being used if a button was pressed - static EventServiceInfo button_event_info; - button_event_info = (EventServiceInfo) { - .type = PEBBLE_BUTTON_DOWN_EVENT, - .handler = prv_watchdog_feed, - }; - event_service_client_subscribe(&button_event_info); - - prv_start_watchdog(); -} - -void prf_idle_watchdog_stop(void) { - if (regular_timer_is_scheduled(&s_is_idle_timer)) { - regular_timer_remove_callback(&s_is_idle_timer); - } -} diff --git a/src/fw/services/prf/idle_watchdog.h b/src/fw/services/prf/idle_watchdog.h deleted file mode 100644 index c3d59382e7..0000000000 --- a/src/fw/services/prf/idle_watchdog.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Auto-shutdown when idle in PRF to increase the changes of getting Pebbles shipped -//! that have some level of battery charge in them. - -//! Start listening for battery connection, bluetooth connection, and button events to feed a -//! watchdog. -void prf_idle_watchdog_start(void); - -//! Stop the watchdog. We will no longer reset if events don't occur frequently enough. -void prf_idle_watchdog_stop(void); diff --git a/src/fw/services/prf_update/Kconfig b/src/fw/services/prf_update/Kconfig new file mode 100644 index 0000000000..190c5f92f1 --- /dev/null +++ b/src/fw/services/prf_update/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PRF_UPDATE + bool "PRF update" + help + Recovery firmware update service. + +if SERVICE_PRF_UPDATE + +module = SERVICE_PRF_UPDATE +module-str = PRF update +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/prf_update/service.c b/src/fw/services/prf_update/service.c new file mode 100644 index 0000000000..91bde081c6 --- /dev/null +++ b/src/fw/services/prf_update/service.c @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/flash.h" +#include "flash_region/flash_region.h" +#include "system/bootbits.h" +#include "system/firmware_storage.h" +#include "system/logging.h" +#include "util/math.h" + +PBL_LOG_MODULE_DEFINE(service_prf_update, CONFIG_SERVICE_PRF_UPDATE_LOG_LEVEL); + +// Don't allow PRF updating when we're in PRF +#ifndef CONFIG_RECOVERY_FW +static void prv_do_update(void) { + PBL_LOG_INFO("Updating PRF!"); + flash_prf_set_protection(false); + + bool saved_sleep_when_idle = flash_get_sleep_when_idle(); + flash_sleep_when_idle(false); + +#ifndef CONFIG_PBLBOOT + FirmwareDescription description = + firmware_storage_read_firmware_description(FLASH_REGION_FIRMWARE_DEST_BEGIN); + + if (!firmware_storage_check_valid_firmware_description(FLASH_REGION_FIRMWARE_DEST_BEGIN, + &description)) { + PBL_LOG_WRN("Invalid recovery firmware CRC in SPI flash!"); + goto done; + } + + const uint32_t total_length = description.description_length + description.firmware_length; +#else + FirmwareHeader header = + firmware_storage_read_firmware_header(FLASH_REGION_FIRMWARE_DEST_BEGIN); + if (!firmware_storage_check_valid_firmware_header(FLASH_REGION_FIRMWARE_DEST_BEGIN, + &header)) { + PBL_LOG_WRN("Invalid recovery firmware CRC in SPI flash!"); + goto done; + } + + const uint32_t total_length = header.fw_start + header.fw_length; +#endif + + PBL_LOG_DBG("Erasing previous PRF..."); + flash_region_erase_optimal_range(FLASH_REGION_SAFE_FIRMWARE_BEGIN, + FLASH_REGION_SAFE_FIRMWARE_BEGIN, + FLASH_REGION_SAFE_FIRMWARE_BEGIN + total_length, + FLASH_REGION_SAFE_FIRMWARE_END); + + PBL_LOG_DBG("Copying PRF from scratch to the PRF slot"); + uint8_t buffer[512]; + uint32_t offset = 0; + while (offset < total_length) { + const uint32_t chunk_size = MIN(sizeof(buffer), (total_length - offset)); + + flash_read_bytes(buffer, FLASH_REGION_FIRMWARE_DEST_BEGIN + offset, chunk_size); + flash_write_bytes(buffer, FLASH_REGION_SAFE_FIRMWARE_BEGIN + offset, chunk_size); + + offset += chunk_size; + } + +done: + flash_prf_set_protection(true); + flash_sleep_when_idle(saved_sleep_when_idle); + PBL_LOG_DBG("Done!"); +} +#endif + +void check_prf_update(void) { + if (!boot_bit_test(BOOT_BIT_NEW_PRF_AVAILABLE)) { + return; + } + + boot_bit_clear(BOOT_BIT_NEW_PRF_AVAILABLE); + +#ifndef CONFIG_RECOVERY_FW + prv_do_update(); +#endif +} diff --git a/src/fw/services/prf_update/wscript_build b/src/fw/services/prf_update/wscript_build new file mode 100644 index 0000000000..caae1afe71 --- /dev/null +++ b/src/fw/services/prf_update/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_update', +) +bld.env.SERVICES.append('services_update') diff --git a/src/fw/services/process_management/Kconfig b/src/fw/services/process_management/Kconfig new file mode 100644 index 0000000000..1709b169f4 --- /dev/null +++ b/src/fw/services/process_management/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PROCESS_MANAGEMENT + bool "Process management" + help + App/process management and app storage. + +if SERVICE_PROCESS_MANAGEMENT + +module = SERVICE_PROCESS_MANAGEMENT +module-str = Process management +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/process_management/app_order_storage.c b/src/fw/services/process_management/app_order_storage.c similarity index 76% rename from src/fw/services/normal/process_management/app_order_storage.c rename to src/fw/services/process_management/app_order_storage.c index 9b5484509f..bbab95ae33 100644 --- a/src/fw/services/normal/process_management/app_order_storage.c +++ b/src/fw/services/process_management/app_order_storage.c @@ -1,32 +1,22 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_order_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/process_management/app_order_storage.h" #include "kernel/pbl_malloc.h" #include "process_management/app_install_manager.h" -#include "services/normal/filesystem/pfs.h" -#include "services/common/system_task.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/system_task.h" #include "system/logging.h" #include "system/passert.h" +PBL_LOG_MODULE_DEFINE(service_process_management, CONFIG_SERVICE_PROCESS_MANAGEMENT_LOG_LEVEL); + #define ORDER_FILE "lnc_ord" typedef struct { PebbleMutex *order_mutex; + bool file_known_missing; } AppOrderData; static AppOrderData s_data; @@ -35,6 +25,12 @@ void app_order_storage_init(void) { s_data.order_mutex = mutex_create(); } +#if UNITTEST +void app_order_storage_reset_for_tests(void) { + s_data.file_known_missing = false; +} +#endif + //! Must be called from the App Task AppMenuOrderStorage *app_order_read_order(void) { PBL_ASSERT_TASK(PebbleTask_App); @@ -43,16 +39,23 @@ AppMenuOrderStorage *app_order_read_order(void) { bool delete_file = false; mutex_lock(s_data.order_mutex); + // Early exit if we already know the file doesn't exist + if (s_data.file_known_missing) { + mutex_unlock(s_data.order_mutex); + return NULL; + } + int fd; if ((fd = pfs_open(ORDER_FILE, OP_FLAG_READ, 0, 0)) < 0) { - PBL_LOG(LOG_LEVEL_DEBUG, "App menu order file does not exist"); + PBL_LOG_DBG("App menu order file does not exist"); + s_data.file_known_missing = true; mutex_unlock(s_data.order_mutex); return NULL; } // Check if it is an valid file if ((pfs_get_file_size(fd) % sizeof(AppInstallId)) != sizeof(uint8_t)) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid order storage file"); + PBL_LOG_ERR("Invalid order storage file"); delete_file = true; goto cleanup; } @@ -60,7 +63,7 @@ AppMenuOrderStorage *app_order_read_order(void) { // Read the number of AppInstallId's listed in the file uint8_t list_length; if (pfs_read(fd, &list_length, sizeof(uint8_t)) != sizeof(uint8_t)) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not read app menu order file"); + PBL_LOG_ERR("Could not read app menu order file"); delete_file = true; goto cleanup; } @@ -68,7 +71,7 @@ AppMenuOrderStorage *app_order_read_order(void) { // Allocate room for the order list array. Free'd by the caller of the function. storage = app_malloc(sizeof(AppMenuOrderStorage) + list_length * sizeof(AppInstallId)); if (!storage) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to malloc stored order install_id list"); + PBL_LOG_ERR("Failed to malloc stored order install_id list"); goto cleanup; } @@ -76,7 +79,7 @@ AppMenuOrderStorage *app_order_read_order(void) { const int read_size = list_length * sizeof(AppInstallId); int rd_sz; if ((rd_sz = pfs_read(fd, (uint8_t *)storage->id_list, read_size)) != read_size) { - PBL_LOG(LOG_LEVEL_ERROR, "Corrupted ordered install_id list (Rd %d of %d bytes)", + PBL_LOG_ERR("Corrupted ordered install_id list (Rd %d of %d bytes)", rd_sz, read_size); app_free(storage); storage = NULL; @@ -110,7 +113,7 @@ static void prv_app_order_write_order(AppMenuOrderStorage *storage) { } if (fd < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not create app menu order file"); + PBL_LOG_ERR("Could not create app menu order file"); goto cleanup; } @@ -118,10 +121,14 @@ static void prv_app_order_write_order(AppMenuOrderStorage *storage) { int wrote_storage_bytes = pfs_write(fd, (uint8_t *)storage, storage_size); if (wrote_storage_bytes != storage_size) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to write all bytes of order list"); + PBL_LOG_ERR("Failed to write all bytes of order list"); } pfs_close(fd); + + // File now exists, clear the missing flag + s_data.file_known_missing = false; + cleanup: kernel_free(storage); mutex_unlock(s_data.order_mutex); diff --git a/src/fw/services/normal/process_management/app_storage.c b/src/fw/services/process_management/app_storage.c similarity index 82% rename from src/fw/services/normal/process_management/app_storage.c rename to src/fw/services/process_management/app_storage.c index c79c7d05de..7822cb66e7 100644 --- a/src/fw/services/normal/process_management/app_storage.c +++ b/src/fw/services/process_management/app_storage.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "app_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/process_management/app_storage.h" #include #include @@ -25,13 +12,15 @@ #include "flash_region/flash_region.h" #include "process_management/pebble_process_info.h" #include "resource/resource_storage.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/filesystem/app_file.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/filesystem/app_file.h" #include "system/logging.h" #include "system/passert.h" #include "system/hexdump.h" #include "util/build_id.h" +PBL_LOG_MODULE_DECLARE(service_process_management, CONFIG_SERVICE_PROCESS_MANAGEMENT_LOG_LEVEL); + // 64k. Note that both tintin and snowy apps have a maximum size of 64k enforced by the SDK, even // though there isn't enough memory for load more than 24k in practice on tintin. static const uint32_t APP_MAX_SIZE = 0x10000; @@ -78,7 +67,7 @@ AppStorageGetAppInfoResult app_storage_get_process_info(PebbleProcessInfo* app_i app_info->sdk_version.minor <= PROCESS_INFO_CURRENT_SDK_VERSION_MINOR); if (is_sdk_compatible == false) { - PBL_LOG(LOG_LEVEL_WARNING, "App requires support for SDK version (%u.%u), we only support version (%u.%u).", + PBL_LOG_WRN("App requires support for SDK version (%u.%u), we only support version (%u.%u).", app_info->sdk_version.major, app_info->sdk_version.minor, PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, PROCESS_INFO_CURRENT_SDK_VERSION_MINOR); @@ -87,7 +76,7 @@ AppStorageGetAppInfoResult app_storage_get_process_info(PebbleProcessInfo* app_i } if (app_info->virtual_size > APP_MAX_SIZE) { - PBL_LOG(LOG_LEVEL_WARNING, "App size (%u) larger than bank size; invalid app.", app_info->virtual_size); + PBL_LOG_WRN("App size (%u) larger than bank size; invalid app.", app_info->virtual_size); // The app's metadata indicates an app larger than the maximum bank size return GET_APP_INFO_APP_TOO_LARGE; } diff --git a/src/fw/services/process_management/process_commands.c b/src/fw/services/process_management/process_commands.c new file mode 100644 index 0000000000..ecde8d051c --- /dev/null +++ b/src/fw/services/process_management/process_commands.c @@ -0,0 +1,88 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "console/prompt.h" +#include "process_management/app_install_manager.h" +#include "process_management/app_manager.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/app_db.h" +#include "pbl/services/filesystem/pfs.h" + +//! @file process_commands.c +//! +//! Serial commands for process management + +extern AppInstallId app_db_check_next_unique_id(void); + +void command_app_remove(const char *id_str) { + int32_t id = atoi(id_str); + if (id == 0) { + prompt_send_response("invalid app number"); + return; + } + + AppInstallEntry entry; + if (!app_install_get_entry_for_install_id(id, &entry)) { + prompt_send_response("failed to get entry"); + return; + } + + // should delete from blob db and fire off an event to AppInstallManager that does the rest + app_db_delete((uint8_t *)&entry.uuid, sizeof(Uuid)); + prompt_send_response("OK"); +} + +bool prv_print_app_info(AppInstallEntry *entry, void *data) { + if (app_install_id_from_system(entry->install_id)) { + return true; + } + + char buffer[120]; + + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&entry->uuid, uuid_buffer); + + prompt_send_response_fmt(buffer, sizeof(buffer), "%"PRIi32": %s %s", entry->install_id, + entry->name, uuid_buffer); + return true; +} + +void command_app_list(void) { + app_install_enumerate_entries(prv_print_app_info, NULL); +} + +void command_app_launch(const char *id_str) { + int32_t id = atoi(id_str); + if (id == 0) { + prompt_send_response("invalid app number"); + return; + } + + AppInstallEntry entry; + bool success = app_install_get_entry_for_install_id(id, &entry); + + if (success) { + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = id }); + prompt_send_response("OK"); + } else { + prompt_send_response("No app with id"); + } +} + +void command_worker_launch(const char *id_str) { + int32_t id = atoi(id_str); + if (id == 0) { + prompt_send_response("invalid app number"); + return; + } + + AppInstallEntry entry; + bool success = app_install_get_entry_for_install_id(id, &entry); + + if (success && app_install_entry_has_worker(&entry)) { + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = id }); + prompt_send_response("OK"); + } else { + prompt_send_response("No worker with id"); + } +} diff --git a/src/fw/services/normal/process_management/process_loader_storage.c b/src/fw/services/process_management/process_loader_storage.c similarity index 83% rename from src/fw/services/normal/process_management/process_loader_storage.c rename to src/fw/services/process_management/process_loader_storage.c index b8b34b1d68..bf2341fe80 100644 --- a/src/fw/services/normal/process_management/process_loader_storage.c +++ b/src/fw/services/process_management/process_loader_storage.c @@ -1,32 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_management/process_loader.h" #include "drivers/flash.h" #include "kernel/util/segment.h" #include "process_management/pebble_process_md.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/process_management/app_storage.h" #include "system/logging.h" #include "system/passert.h" #include "util/legacy_checksum.h" #include +PBL_LOG_MODULE_DECLARE(service_process_management, CONFIG_SERVICE_PROCESS_MANAGEMENT_LOG_LEVEL); + //! This comes from the generated pebble.auto.c with all the exported functions in it. extern const void* const g_pbl_system_tbl[]; @@ -40,7 +29,7 @@ static bool prv_verify_checksum(const PebbleProcessInfo* app_info, const uint8_t app_size); if (app_info->crc != calculated_crc) { - PBL_LOG(LOG_LEVEL_WARNING, "Calculated App CRC is 0x%"PRIx32", expected 0x%"PRIx32"!", + PBL_LOG_WRN("Calculated App CRC is 0x%"PRIx32", expected 0x%"PRIx32"!", calculated_crc, app_info->crc); return false; } else { @@ -56,7 +45,7 @@ static void * prv_offset_to_address(MemorySegment *segment, size_t offset) { static bool prv_intialize_sdk_process(PebbleTask task, const PebbleProcessInfo *info, MemorySegment *destination) { if (!prv_verify_checksum(info, destination->start)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Calculated CRC does not match, aborting..."); + PBL_LOG_DBG("Calculated CRC does not match, aborting..."); return false; } @@ -105,8 +94,7 @@ static bool prv_load_from_flash(const PebbleProcessMd *app_md, PebbleTask task, const size_t load_size = app_storage_get_process_load_size(&info); if (load_size > memory_segment_get_size(destination)) { - PBL_LOG(LOG_LEVEL_ERROR, - "App/Worker exceeds available program space: %"PRIu16" + (%"PRIu32" * 4) = %zu", + PBL_LOG_ERR("App/Worker exceeds available program space: %"PRIu16" + (%"PRIu32" * 4) = %zu", info.load_size, info.num_reloc_entries, load_size); return false; } @@ -117,12 +105,12 @@ static bool prv_load_from_flash(const PebbleProcessMd *app_md, PebbleTask task, app_storage_get_file_name(process_name, sizeof(process_name), app_id, task); if ((fd = pfs_open(process_name, OP_FLAG_READ, 0, 0)) < S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Process open failed for process %s, fd = %d", process_name, fd); + PBL_LOG_ERR("Process open failed for process %s, fd = %d", process_name, fd); return (false); } if (pfs_read(fd, destination->start, load_size) != (int)load_size) { - PBL_LOG(LOG_LEVEL_ERROR, "Process read failed for process %s, fd = %d", process_name, fd); + PBL_LOG_ERR("Process read failed for process %s, fd = %d", process_name, fd); pfs_close(fd); return (false); } @@ -145,8 +133,7 @@ static bool prv_load_from_resource(const PebbleProcessMdResource *app_md, const size_t load_size = app_storage_get_process_load_size(&info); if (load_size > memory_segment_get_size(destination)) { - PBL_LOG(LOG_LEVEL_ERROR, - "App/Worker exceeds available program space: %"PRIu16" + (%"PRIu32" * 4) = %zu", + PBL_LOG_ERR("App/Worker exceeds available program space: %"PRIu16" + (%"PRIu32" * 4) = %zu", info.load_size, info.num_reloc_entries, load_size); return false; } diff --git a/src/fw/services/process_management/wscript_build b/src/fw/services/process_management/wscript_build new file mode 100644 index 0000000000..af740b84c8 --- /dev/null +++ b/src/fw/services/process_management/wscript_build @@ -0,0 +1,17 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'app_order_storage.c', + 'app_storage.c', + 'process_commands.c', + 'process_loader_storage.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_process_management', +) + +bld.env.SERVICES.append('services_process_management') diff --git a/src/fw/services/protobuf_log/Kconfig b/src/fw/services/protobuf_log/Kconfig new file mode 100644 index 0000000000..8d5a0ce89c --- /dev/null +++ b/src/fw/services/protobuf_log/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PROTOBUF_LOG + bool "Protobuf log" + help + Protobuf-encoded log/data upload service. + +if SERVICE_PROTOBUF_LOG + +module = SERVICE_PROTOBUF_LOG +module-str = Protobuf log +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/protobuf_log/protobuf_log.c b/src/fw/services/protobuf_log/protobuf_log.c similarity index 92% rename from src/fw/services/normal/protobuf_log/protobuf_log.c rename to src/fw/services/protobuf_log/protobuf_log.c index eec1980467..0584d3503f 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log.c +++ b/src/fw/services/protobuf_log/protobuf_log.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "protobuf_log.h" -#include "protobuf_log_private.h" -#include "protobuf_log_util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log_private.h" +#include "pbl/services/protobuf_log/protobuf_log_util.h" #include "applib/data_logging.h" #include "drivers/rtc.h" @@ -26,7 +13,7 @@ #include "pb.h" #include "pb_decode.h" #include "pb_encode.h" -#include "services/normal/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/data_logging_service.h" #include "system/logging.h" #include "system/passert.h" #include "system/version.h" @@ -44,8 +31,10 @@ #include "nanopb/measurements.pb.h" #include "nanopb/payload.pb.h" +PBL_LOG_MODULE_DEFINE(service_protobuf_log, CONFIG_SERVICE_PROTOBUF_LOG_LOG_LEVEL); + #define PROTOBUF_LOG_DEBUG(fmt, args...) \ - PBL_LOG_D(LOG_DOMAIN_PROTOBUF, LOG_LEVEL_DEBUG, fmt, ## args) + PBL_LOG_D_DBG(LOG_DOMAIN_PROTOBUF, fmt, ## args) #define MLOG_MAX_VARINT_ENCODED_SIZE 5 @@ -71,7 +60,7 @@ static DataLoggingSession *prv_get_dls_session(void) { // This can happen when you are not connected to the phone and have rebooted a number of // times because each time you reboot, you get new sessions created and reach the limit // of the max # of sessions allowed. - PBL_LOG(LOG_LEVEL_WARNING, "Error creating activity logging session"); + PBL_LOG_WRN("Error creating activity logging session"); return NULL; } } @@ -98,7 +87,7 @@ static bool prv_dls_transport(uint8_t *buffer, size_t buf_size) { if (result == DATA_LOGGING_SUCCESS) { success = true; } else { - PBL_LOG(LOG_LEVEL_ERROR, "Error %d while logging data", (int)result); + PBL_LOG_ERR("Error %d while logging data", (int)result); } } unlock: @@ -180,11 +169,11 @@ static bool prv_populate_payload(ProtobufLogConfig *config, size_t buffer_len, u bool success = pb_encode(stream, &pebble_pipeline_Payload_msg, &payload); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Error encoding payload"); + PBL_LOG_ERR("Error encoding payload"); } // PBL-43622: Will revert later - PBL_LOG(LOG_LEVEL_INFO, "Logged protobuf payload type: %d, utc:%"PRIu32, config->type, + PBL_LOG_INFO("Logged protobuf payload type: %d, utc:%"PRIu32, config->type, payload.send_time_utc); return success; } @@ -349,7 +338,7 @@ ProtobufLogRef protobuf_log_create(ProtobufLogConfig *config, // Start a new encoding const bool success = prv_session_encode_start(session); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Error encoding msg"); + PBL_LOG_ERR("Error encoding msg"); prv_session_free(session); session = NULL; } @@ -389,7 +378,7 @@ static bool prv_log_struct(PLogSession *session, uint32_t field_number, // Encode the struct into the message bool success = prv_encode_struct(&session->data_stream, field_number, fields, msg); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Error adding sample, resetting session"); + PBL_LOG_ERR("Error adding sample, resetting session"); return prv_session_encode_start(session); } @@ -474,7 +463,7 @@ bool protobuf_log_session_flush(ProtobufLogRef session_ref) { bool success = prv_populate_payload(&session->config, session->data_stream.bytes_written, session->data_buffer, &stream); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Error encoding payload"); + PBL_LOG_ERR("Error encoding payload"); goto exit; } @@ -488,7 +477,7 @@ bool protobuf_log_session_flush(ProtobufLogRef session_ref) { PROTOBUF_LOG_DEBUG("Session: 0x%x - Flushing %d bytes", (int)session_ref, hdr->msg_size); success = (session->transport)(session->msg_buffer, hdr->msg_size + sizeof(PLogMessageHdr)); if (!success) { - PBL_LOG(LOG_LEVEL_ERROR, "Failure when sending encoded message, resetting session"); + PBL_LOG_ERR("Failure when sending encoded message, resetting session"); } // TODO: Call a success callback so the clients know exactly which data has been sent. diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.c b/src/fw/services/protobuf_log/protobuf_log_activity_sessions.c similarity index 84% rename from src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.c rename to src/fw/services/protobuf_log/protobuf_log_activity_sessions.c index 91f1067956..130fb6c899 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.c +++ b/src/fw/services/protobuf_log/protobuf_log_activity_sessions.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "protobuf_log_activity_sessions.h" -#include "protobuf_log.h" -#include "protobuf_log_private.h" -#include "protobuf_log_util.h" - -#include "services/common/hrm/hrm_manager.h" -#include "services/normal/activity/activity.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/protobuf_log/protobuf_log_activity_sessions.h" +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log_private.h" +#include "pbl/services/protobuf_log/protobuf_log_util.h" + +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/activity/activity.h" #include "nanopb/event.pb.h" #include "system/passert.h" diff --git a/src/fw/services/protobuf_log/protobuf_log_hr.c b/src/fw/services/protobuf_log/protobuf_log_hr.c new file mode 100644 index 0000000000..9fc13dc0c3 --- /dev/null +++ b/src/fw/services/protobuf_log/protobuf_log_hr.c @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/protobuf_log/protobuf_log_hr.h" +#include "pbl/services/protobuf_log/protobuf_log.h" + +#include "pbl/services/hrm/hrm_manager.h" + +#include "nanopb/measurements.pb.h" +#include "system/passert.h" + +#include + +#include +#include + +// ----------------------------------------------------------------------------------------- +// Convert HRMQuality to the internal protobuf representation. +T_STATIC uint32_t prv_hr_quality_int(HRMQuality quality) { + switch (quality) { + case HRMQuality_OffWrist: + return pebble_pipeline_MeasurementSet_HeartRateQuality_OffWrist; + case HRMQuality_Worst: + return pebble_pipeline_MeasurementSet_HeartRateQuality_Worst; + case HRMQuality_Poor: + return pebble_pipeline_MeasurementSet_HeartRateQuality_Poor; + case HRMQuality_Acceptable: + return pebble_pipeline_MeasurementSet_HeartRateQuality_Acceptable; + case HRMQuality_Good: + return pebble_pipeline_MeasurementSet_HeartRateQuality_Good; + case HRMQuality_Excellent: + return pebble_pipeline_MeasurementSet_HeartRateQuality_Excellent; + } + WTF; // Should never get here + return 0; +} + +ProtobufLogRef protobuf_log_hr_create(ProtobufLogTransportCB transport) { + // Create a measure log session, which we use to send heart rate readings to the phone + ProtobufLogMeasurementType measure_types[] = { + ProtobufLogMeasurementType_BPM, + ProtobufLogMeasurementType_HRQuality, + }; + + ProtobufLogConfig log_config = { + .type = ProtobufLogType_Measurements, + .measurements = { + .types = measure_types, + .num_types = ARRAY_LENGTH(measure_types), + }, + }; + + return protobuf_log_create(&log_config, transport, 0 /*max_encoded_msg_size*/); +} + +bool protobuf_log_hr_add_sample(ProtobufLogRef ref, time_t sample_utc, uint8_t bpm, + HRMQuality quality) { + uint32_t values[] = {bpm, prv_hr_quality_int(quality)}; + return protobuf_log_session_add_measurements(ref, sample_utc, ARRAY_LENGTH(values), values); +} diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_test.c b/src/fw/services/protobuf_log/protobuf_log_test.c similarity index 94% rename from src/fw/services/normal/protobuf_log/protobuf_log_test.c rename to src/fw/services/protobuf_log/protobuf_log_test.c index 57b9a7fd91..53e62eb752 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log_test.c +++ b/src/fw/services/protobuf_log/protobuf_log_test.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "protobuf_log_test.h" - -#include "protobuf_log.h" -#include "protobuf_log_private.h" -#include "protobuf_log_activity_sessions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/protobuf_log/protobuf_log_test.h" + +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log_private.h" +#include "pbl/services/protobuf_log/protobuf_log_activity_sessions.h" #include "pb.h" #include "pb_encode.h" diff --git a/src/fw/services/normal/protobuf_log/protobuf_log_util.c b/src/fw/services/protobuf_log/protobuf_log_util.c similarity index 80% rename from src/fw/services/normal/protobuf_log/protobuf_log_util.c rename to src/fw/services/protobuf_log/protobuf_log_util.c index 94ca0f89c0..238c015811 100644 --- a/src/fw/services/normal/protobuf_log/protobuf_log_util.c +++ b/src/fw/services/protobuf_log/protobuf_log_util.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "protobuf_log.h" -#include "protobuf_log_util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log_util.h" #include "pb_encode.h" diff --git a/src/fw/services/protobuf_log/wscript_build b/src/fw/services/protobuf_log/wscript_build new file mode 100644 index 0000000000..4b3fdd0843 --- /dev/null +++ b/src/fw/services/protobuf_log/wscript_build @@ -0,0 +1,18 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'protobuf_log.c', + 'protobuf_log_activity_sessions.c', + 'protobuf_log_hr.c', + 'protobuf_log_test.c', + 'protobuf_log_util.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_protobuf_log', +) + +bld.env.SERVICES.append('services_protobuf_log') diff --git a/src/fw/services/put_bytes/Kconfig b/src/fw/services/put_bytes/Kconfig new file mode 100644 index 0000000000..51c84b2450 --- /dev/null +++ b/src/fw/services/put_bytes/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_PUT_BYTES + bool "Put bytes" + help + put_bytes endpoint (incoming binary transfer). + +if SERVICE_PUT_BYTES + +module = SERVICE_PUT_BYTES +module-str = Put bytes +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/common/put_bytes/put_bytes.c b/src/fw/services/put_bytes/put_bytes.c similarity index 88% rename from src/fw/services/common/put_bytes/put_bytes.c rename to src/fw/services/put_bytes/put_bytes.c index 1f6323abeb..9a647eef15 100644 --- a/src/fw/services/common/put_bytes/put_bytes.c +++ b/src/fw/services/put_bytes/put_bytes.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/put_bytes/put_bytes.h" -#include "services/common/put_bytes/put_bytes_storage.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/put_bytes/put_bytes.h" +#include "pbl/services/put_bytes/put_bytes_storage.h" #include "comm/bluetooth_analytics.h" #include "kernel/events.h" @@ -23,13 +10,12 @@ #include "kernel/system_message.h" #include "os/tick.h" #include "resource/resource_storage_file.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_receive_router.h" -#include "services/common/firmware_update.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_receive_router.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/process_management/app_storage.h" #include "system/bootbits.h" #include "system/firmware_storage.h" #include "system/logging.h" @@ -37,11 +23,6 @@ #include "util/attributes.h" #include "util/math.h" #include "util/net.h" -#if PLATFORM_TINTIN -#include "system/version.h" -#include "resource/resource_storage_flash.h" -#endif - #include #include "FreeRTOS.h" @@ -50,6 +31,8 @@ #include +PBL_LOG_MODULE_DEFINE(service_put_bytes, CONFIG_SERVICE_PUT_BYTES_LOG_LEVEL); + typedef enum { PutBytesIdle = 0x00, PutBytesInit = 0x01, @@ -123,11 +106,7 @@ typedef enum { NumResponseCodes } ResponseCode; -#if !CAPABILITY_HAS_PUTBYTES_PREACKING -#define MAX_BATCHED_PB_PUT_OPS 1 -#else #define MAX_BATCHED_PB_PUT_OPS 3 -#endif typedef struct { uint8_t *buffer; @@ -286,7 +265,7 @@ static void prv_pre_ack_if_space_in_put_job_queue(void) { if (pre_ack) { prv_send_response(ResponseAck, s_pb_state.token); } else if (put_jobs->enable_preack) { - PBL_LOG(LOG_LEVEL_DEBUG, "Not enough buffer room to pre-ack PB packet"); + PBL_LOG_DBG("Not enough buffer room to pre-ack PB packet"); } } @@ -306,11 +285,7 @@ static bool prv_init_put_job_queue_if_necessary(void) { return true; } -#if !CAPABILITY_HAS_PUTBYTES_PREACKING - put_jobs->enable_preack = false; -#else put_jobs->enable_preack = true; -#endif int i; for (i = 0; i < MAX_BATCHED_PB_PUT_OPS; i++) { @@ -319,11 +294,11 @@ static bool prv_init_put_job_queue_if_necessary(void) { uint8_t *buffer = (uint8_t *) kernel_zalloc(PUT_BYTES_PP_BUFFER_SIZE); if (!buffer) { if (i == 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory to service PB request, abort!"); + PBL_LOG_ERR("Not enough memory to service PB request, abort!"); prv_deinit_put_job_queue(); return false; } if (i == 1) { - PBL_LOG(LOG_LEVEL_INFO, "Not enough memory for PB pre-ack, falling back to legacy mode"); + PBL_LOG_INFO("Not enough memory for PB pre-ack, falling back to legacy mode"); put_jobs->enable_preack = false; break; } else { @@ -355,7 +330,7 @@ static void prv_add_nack_no_token_system_callback(void) { } static void prv_cleanup(void) { - PBL_LOG(LOG_LEVEL_INFO, "Put bytes cleanup. Tok: %"PRIu32, s_pb_state.token); + PBL_LOG_INFO("Put bytes cleanup. Tok: %"PRIu32, s_pb_state.token); prv_deinit_put_job_queue(); s_pb_state.receiver = (__typeof__(s_pb_state.receiver)) {}; @@ -413,19 +388,19 @@ static void prv_fail(uint32_t token) { } static void prv_timer_callback(void* data) { - PBL_LOG(LOG_LEVEL_WARNING, "Put bytes Tok: %"PRIu32" timed out after %"PRIu32"ms, cleaning up.", + PBL_LOG_WRN("Put bytes Tok: %"PRIu32" timed out after %"PRIu32"ms, cleaning up.", s_pb_state.token, PUT_TIMEOUT_MS); prv_cleanup_async(); } static bool prv_has_valid_fw_update_state_for_object_type(PutBytesObjectType type) { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW if (!firmware_update_is_in_progress()) { bool is_fw_update_object = (type == ObjectFirmware || type == ObjectRecovery || type == ObjectSysResources); if (is_fw_update_object) { - PBL_LOG(LOG_LEVEL_ERROR, "Cannot handle object type=<0x%x> when not in FW update mode", type); + PBL_LOG_ERR("Cannot handle object type=<0x%x> when not in FW update mode", type); return false; } } @@ -460,7 +435,7 @@ static void prv_send_response(ResponseCode code, uint32_t token) { bool success = comm_session_send_data(comm_session_get_system_session(), PB_ENDPOINT_ID, (uint8_t*) &msg, sizeof(msg), COMM_SESSION_DEFAULT_TIMEOUT); if (!success) { - PBL_LOG(LOG_LEVEL_WARNING, "PutBytes timeout sending response"); + PBL_LOG_WRN("PutBytes timeout sending response"); } } @@ -474,7 +449,7 @@ static void prv_cleanup_and_send_response(ResponseCode code) { } static void prv_commit_object(uint32_t crc) { -#if !CAPABILITY_HAS_PBLBOOT +#ifndef CONFIG_PBLBOOT if (s_pb_state.type == ObjectFirmware || s_pb_state.type == ObjectRecovery) { FirmwareDescription fw_descr = { .description_length = sizeof(FirmwareDescription), @@ -482,16 +457,7 @@ static void prv_commit_object(uint32_t crc) { .checksum = crc }; -#if !CAPABILITY_HAS_DEFECTIVE_FW_CRC - // Note: We are trying to move away from using the STM32 legacy defective - // checksum in our code. However, this implementation is baked into the - // mobile apps and it uses it to validate that the firmware image it has - // pulled from cohorts is correct. Thus, for now, we still use the legacy - // checksum in put_bytes() after pieces are transferred, but when we store - // the CRC for the bootloader to check, we use the real CRC32 - // implementation fw_descr.checksum = pb_storage_calculate_crc(&s_pb_state.storage, PutBytesCrcType_CRC32); -#endif pb_storage_write(&s_pb_state.storage, 0, (uint8_t *)&fw_descr, sizeof(FirmwareDescription)); } #endif @@ -507,7 +473,7 @@ static void prv_finish_fw_update_if_completed(void) { s_ready_to_install[ObjectSysResources - 1].type != ObjectSysResources) { return; // Haven't received both FW and System Resources yet } - PBL_LOG(LOG_LEVEL_DEBUG, "Got both FW bin and sys resources!"); + PBL_LOG_DBG("Got both FW bin and sys resources!"); s_ready_to_install[ObjectFirmware - 1].type = 0; s_ready_to_install[ObjectSysResources - 1].type = 0; @@ -531,12 +497,12 @@ static void prv_do_install(uint32_t token) { } if (token == 0 || o == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, "Token does not exist; got 0x%" PRIx32, token); + PBL_LOG_ERR("Token does not exist; got 0x%" PRIx32, token); prv_cleanup_and_send_response(ResponseNack); return; } - PBL_LOG(LOG_LEVEL_INFO, "PutBytes install CB. Tok: %"PRIu32", type: %d", token, o->type); + PBL_LOG_INFO("PutBytes install CB. Tok: %"PRIu32", type: %d", token, o->type); switch (o->type) { case ObjectFirmware: @@ -560,7 +526,7 @@ static void prv_do_install(uint32_t token) { } static void prv_do_abort(void) { - PBL_LOG(LOG_LEVEL_INFO, "PutBytes abort CB. Tok: %"PRIu32".", s_pb_state.token); + PBL_LOG_INFO("PutBytes abort CB. Tok: %"PRIu32".", s_pb_state.token); prv_mark_pb_jobs_complete(1); prv_cleanup_and_send_response(ResponseAck); } @@ -571,7 +537,7 @@ static bool prv_has_invalid_token(const PutBytesCommand command, uint32_t reques return false; } if (s_pb_state.token != request_token) { - PBL_LOG(LOG_LEVEL_ERROR, "%d: Token does not match; got 0x%" PRIx32 ", expected 0x%" PRIx32, + PBL_LOG_ERR("%d: Token does not match; got 0x%" PRIx32 ", expected 0x%" PRIx32, command, request_token, s_pb_state.token); return true; } @@ -599,21 +565,20 @@ static bool prv_has_invalid_request_length(const PutBytesCommand command, uint32 const size_t expected_length = prv_expected_minimum_length_by_command(command); const bool has_invalid_length = (actual_length < expected_length); if (has_invalid_length) { - PBL_LOG(LOG_LEVEL_ERROR, - "Invalid message length for command %"PRIu32"; expected=%"PRIu32", actual=%"PRIu32, + PBL_LOG_ERR("Invalid message length for command %"PRIu32"; expected=%"PRIu32", actual=%"PRIu32, (uint32_t)command, (uint32_t)expected_length, (uint32_t)actual_length); } return has_invalid_length; } static bool prv_is_object_allowed(PutBytesObjectType type) { -#ifdef RECOVERY_FW +#ifdef CONFIG_RECOVERY_FW switch (type) { case ObjectFirmware: case ObjectSysResources: return true; default: - PBL_LOG(LOG_LEVEL_WARNING, "Can't update Object Type %u from PRF!", type); + PBL_LOG_WRN("Can't update Object Type %u from PRF!", type); return false; } #else @@ -623,14 +588,14 @@ static bool prv_is_object_allowed(PutBytesObjectType type) { static bool prv_is_init_object_type_invalid(PutBytesObjectType type) { if (type == 0 || type >= NumObjects) { - PBL_LOG(LOG_LEVEL_ERROR, "Invalid object type, got 0x%x", type); + PBL_LOG_ERR("Invalid object type, got 0x%x", type); return true; } if (!prv_is_object_allowed(type)) { return true; } if (!prv_has_valid_fw_update_state_for_object_type(type)) { - PBL_LOG(LOG_LEVEL_ERROR, "Not in FW update state"); + PBL_LOG_ERR("Not in FW update state"); return true; } return false; @@ -646,7 +611,7 @@ static bool prv_parse_init_index(const InitRequest *init_request, uint32_t *inde } else { // legacy putbytes requests, bank numbers if (init_request->index >= MAX_APP_BANKS) { - PBL_LOG(LOG_LEVEL_ERROR, "Attempting to put byte in invalid bank #%d", init_request->index); + PBL_LOG_ERR("Attempting to put byte in invalid bank #%d", init_request->index); return false; } @@ -667,7 +632,7 @@ static bool prv_setup_storage_for_init_request(const InitRequest *request, uint3 PutBytesStorageInfo *storage_info = NULL; switch (request->type) { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW case ObjectFile: { storage_info = kernel_malloc_check(sizeof(PutBytesStorageInfo) + strlen(request->filename) + 1); @@ -741,7 +706,7 @@ static void prv_do_init(void) { const uint32_t append_offset_magic = 0xBE4354EF; if (ntohl(info->init_req_magic) == append_offset_magic) { append_offset = ntohl(info->append_offset); - PBL_LOG(LOG_LEVEL_INFO, "Restarting FW Update at offset %"PRIu32, append_offset); + PBL_LOG_INFO("Restarting FW Update at offset %"PRIu32, append_offset); } } @@ -760,18 +725,16 @@ static void prv_do_init(void) { const uint32_t r = rand(); s_pb_state.token = MAX(1, r); - PBL_LOG(LOG_LEVEL_INFO, - "PutBytes Init CB. Type: %d, Idx: %"PRIu32", Size: %"PRIu32" Tok: %"PRIu32, + PBL_LOG_INFO("PutBytes Init CB. Type: %d, Idx: %"PRIu32", Size: %"PRIu32" Tok: %"PRIu32, (int) s_pb_state.type, s_pb_state.index, s_pb_state.total_size, s_pb_state.token); success = prv_setup_storage_for_init_request(request, index); s_pb_state.start_ticks = rtc_get_ticks(); - bt_driver_analytics_get_conn_event_stats(&s_pb_state.conn_event_stats); if (!success) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to init storage"); + PBL_LOG_WRN("Failed to init storage"); goto exit; } @@ -818,7 +781,7 @@ static bool prv_do_put(const PutRequest *request, uint32_t request_size, uint32_ return false; } - PBL_LOG(LOG_LEVEL_DEBUG, "PutBytes put CB. type: %"PRIu32", length: %"PRIu32, + PBL_LOG_DBG("PutBytes put CB. type: %"PRIu32", length: %"PRIu32, (uint32_t)s_pb_state.type, data_length); pb_storage_append(&s_pb_state.storage, request->data, data_length); @@ -841,7 +804,7 @@ static void prv_do_commit(void) { if (elapsed_time_ms > 0) { int bytes_per_sec = (int)((s_pb_state.total_size * 1000) / elapsed_time_ms); - PBL_LOG(LOG_LEVEL_DEBUG, "PutBytes pushed %d bytes/sec", bytes_per_sec); + PBL_LOG_DBG("PutBytes pushed %d bytes/sec", bytes_per_sec); } bluetooth_analytics_handle_put_bytes_stats( @@ -850,11 +813,11 @@ static void prv_do_commit(void) { if (commit_succeeded) { s_pb_state.is_success = true; - PBL_LOG(LOG_LEVEL_DEBUG, "PutBytes commit CB. CRC matches! Calculated CRC is 0x%"PRIx32 + PBL_LOG_DBG("PutBytes commit CB. CRC matches! Calculated CRC is 0x%"PRIx32 " expected 0x%"PRIx32, calculated_crc, crc); prv_commit_object(crc); } else { - PBL_LOG(LOG_LEVEL_ERROR, "PutBytes commit CB. Calculated CRC is 0x%"PRIx32" expected 0x%"PRIx32, + PBL_LOG_ERR("PutBytes commit CB. Calculated CRC is 0x%"PRIx32" expected 0x%"PRIx32, calculated_crc, crc); } @@ -881,7 +844,7 @@ static bool prv_is_valid_command_for_current_state(PutBytesCommand command) { static bool prv_is_invalid_command_for_current_state(PutBytesCommand command) { if (!prv_is_valid_command_for_current_state(command)) { - PBL_LOG(LOG_LEVEL_ERROR, "PutBytes command 0x%x not permitted in current state 0x%x", + PBL_LOG_ERR("PutBytes command 0x%x not permitted in current state 0x%x", command, s_pb_state.current_command); return true; } @@ -909,7 +872,7 @@ static bool prv_check_putrequest_for_errors(const PutRequest *request_hdr, uint32_t req_size = tot_request_size - sizeof(PutRequest); uint32_t data_length = ntohl(request_hdr->length); if (data_length > req_size) { - PBL_LOG(LOG_LEVEL_ERROR, "Length value longer than buffer"); + PBL_LOG_ERR("Length value longer than buffer"); return true; } @@ -1003,7 +966,7 @@ static void prv_process_msg_system_task_callback(void *unused) { if (!s_pb_state.receiver.buffer || s_pb_state.receiver.length == 0) { - PBL_LOG(LOG_LEVEL_WARNING, "No message pending, PutBytes cancelled in the mean time?"); + PBL_LOG_WRN("No message pending, PutBytes cancelled in the mean time?"); prv_send_response(ResponseNack, s_pb_state.token); goto finally; } @@ -1057,20 +1020,20 @@ void put_bytes_cancel(void) { PBL_ASSERT_TASK(PebbleTask_KernelBackground); if (xSemaphoreTake(s_pb_semaphore, portMAX_DELAY) != pdTRUE) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to acquire the put-bytes semaphore"); + PBL_LOG_ERR("Failed to acquire the put-bytes semaphore"); return; } if (s_pb_state.current_command == PutBytesIdle) { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempted to cancel put_bytes while idle, %d", + PBL_LOG_DBG("Attempted to cancel put_bytes while idle, %d", s_pb_state.current_command); } else if (s_pb_state.type == ObjectWatchApp || s_pb_state.type == ObjectAppResources || s_pb_state.type == ObjectWatchWorker) { - PBL_LOG(LOG_LEVEL_INFO, "Forcefully cancelling put_bytes transfer of app binaries"); + PBL_LOG_INFO("Forcefully cancelling put_bytes transfer of app binaries"); prv_cleanup(); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Attempted to cancel put_bytes with a non desired type, %d", + PBL_LOG_DBG("Attempted to cancel put_bytes with a non desired type, %d", s_pb_state.type); } @@ -1119,7 +1082,7 @@ void put_bytes_expect_init(uint32_t timeout_ms) { xSemaphoreTake(s_pb_semaphore, portMAX_DELAY); if (s_pb_state.current_command != PutBytesIdle) { - PBL_LOG(LOG_LEVEL_ERROR, "Called put_bytes_expect while put_bytes is not idle"); + PBL_LOG_ERR("Called put_bytes_expect while put_bytes is not idle"); xSemaphoreGive(s_pb_semaphore); return; } @@ -1161,7 +1124,7 @@ static bool prv_take_lock_with_short_timeout(void) { // probably a Put Bytes session going on already anyway. const TickType_t SEMAPHORE_TIMEOUT_TICKS = milliseconds_to_ticks(25); if (xSemaphoreTake(s_pb_semaphore, SEMAPHORE_TIMEOUT_TICKS) != pdTRUE) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to acquire the put-bytes semaphore, retry"); + PBL_LOG_ERR("Failed to acquire the put-bytes semaphore, retry"); return false; } return true; @@ -1169,7 +1132,7 @@ static bool prv_take_lock_with_short_timeout(void) { static bool prv_prepare(size_t total_payload_length) { if (total_payload_length > PUT_BYTES_PP_BUFFER_SIZE) { - PBL_LOG(LOG_LEVEL_ERROR, "Put Bytes message too big"); + PBL_LOG_ERR("Put Bytes message too big"); return false; } @@ -1241,7 +1204,7 @@ void prv_receiver_write(Receiver *receiver, const uint8_t *data, size_t length) } if (!prv_is_message_pending_processing()) { // Could happen if put_bytes_cancel() was called after "prepare" - PBL_LOG(LOG_LEVEL_WARNING, "No message pending, PutBytes cancelled? Will NACK."); + PBL_LOG_WRN("No message pending, PutBytes cancelled? Will NACK."); s_pb_state.receiver.should_nack = true; goto finally; } @@ -1269,7 +1232,7 @@ void prv_receiver_cleanup(Receiver *receiver) { void prv_receiver_finish(Receiver *receiver) { if (s_pb_state.receiver.should_nack) { - PBL_LOG(LOG_LEVEL_WARNING, "NACK'ing from ..._finish"); + PBL_LOG_WRN("NACK'ing from ..._finish"); prv_add_nack_no_token_system_callback(); prv_receiver_reset(); return; diff --git a/src/fw/services/common/put_bytes/put_bytes_storage.c b/src/fw/services/put_bytes/put_bytes_storage.c similarity index 77% rename from src/fw/services/common/put_bytes/put_bytes_storage.c rename to src/fw/services/put_bytes/put_bytes_storage.c index 5c0e13cfad..26a1c0e12f 100644 --- a/src/fw/services/common/put_bytes/put_bytes_storage.c +++ b/src/fw/services/put_bytes/put_bytes_storage.c @@ -1,32 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "put_bytes_storage_internal.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/put_bytes/put_bytes_storage_internal.h" #include "kernel/pbl_malloc.h" #include "system/logging.h" #include "system/passert.h" #include "util/size.h" +PBL_LOG_MODULE_DECLARE(service_put_bytes, CONFIG_SERVICE_PUT_BYTES_LOG_LEVEL); + #ifdef UNITTEST extern const PutBytesStorageImplementation s_raw_implementation; extern const PutBytesStorageImplementation s_file_implementation; #else // #ifdef UNITTEST -#include "put_bytes_storage_raw.h" +#include "pbl/services/put_bytes/put_bytes_storage_raw.h" static const PutBytesStorageImplementation s_raw_implementation = { .init = pb_storage_raw_init, @@ -36,8 +25,8 @@ static const PutBytesStorageImplementation s_raw_implementation = { .deinit = pb_storage_raw_deinit }; -#ifndef RECOVERY_FW -#include "put_bytes_storage_file.h" +#ifndef CONFIG_RECOVERY_FW +#include "pbl/services/put_bytes/put_bytes_storage_file.h" static const PutBytesStorageImplementation s_file_implementation = { .init = pb_storage_file_init, @@ -46,7 +35,7 @@ static const PutBytesStorageImplementation s_file_implementation = { .calculate_crc = pb_storage_file_calculate_crc, .deinit = pb_storage_file_deinit }; -#endif // #ifndef RECOVERY_FW +#endif // #ifndef CONFIG_RECOVERY_FW #endif // #ifdef UNITTEST @@ -70,7 +59,7 @@ bool pb_storage_init(PutBytesStorage *storage, PutBytesObjectType object_type, [ObjectFirmware] = &s_raw_implementation, [ObjectRecovery] = &s_raw_implementation, [ObjectSysResources] = &s_raw_implementation, -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW [ObjectAppResources] = &s_file_implementation, [ObjectWatchApp] = &s_file_implementation, [ObjectFile] = &s_file_implementation, @@ -83,7 +72,7 @@ bool pb_storage_init(PutBytesStorage *storage, PutBytesObjectType object_type, if (object_type >= ARRAY_LENGTH(IMPL_FOR_OBJECT_TYPE) || IMPL_FOR_OBJECT_TYPE[object_type] == NULL) { - PBL_LOG(LOG_LEVEL_WARNING, "Unsupported PutBytesObjectType %u", object_type); + PBL_LOG_WRN("Unsupported PutBytesObjectType %u", object_type); return false; } @@ -91,7 +80,7 @@ bool pb_storage_init(PutBytesStorage *storage, PutBytesObjectType object_type, uint32_t max_size = impl->get_max_size(object_type); if (total_size > max_size) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid size for type %u, size: %"PRIu32", max_size: %"PRIu32, + PBL_LOG_WRN("Invalid size for type %u, size: %"PRIu32", max_size: %"PRIu32, object_type, total_size, max_size); return false; } diff --git a/src/fw/services/put_bytes/put_bytes_storage_file.c b/src/fw/services/put_bytes/put_bytes_storage_file.c new file mode 100644 index 0000000000..4a039ed443 --- /dev/null +++ b/src/fw/services/put_bytes/put_bytes_storage_file.c @@ -0,0 +1,52 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/put_bytes/put_bytes_storage_file.h" + +#include "pbl/services/filesystem/pfs.h" +#include "system/passert.h" + +bool pb_storage_file_init(PutBytesStorage *storage, PutBytesObjectType object_type, + uint32_t total_size, PutBytesStorageInfo *info, uint32_t append_offset) { + // if a file exists with that name, remove it + pfs_remove(info->filename); + if (total_size == 0) { + // a file of size zero is valid at the moment + return true; + } + + int fd = pfs_open(info->filename, OP_FLAG_READ | OP_FLAG_WRITE, FILE_TYPE_STATIC, total_size); + storage->impl_data = (void*)(uintptr_t) fd; + + return fd >= 0; +} + +uint32_t pb_storage_file_get_max_size(PutBytesObjectType object_type) { + return get_available_pfs_space(); +} + +void pb_storage_file_write(PutBytesStorage *storage, uint32_t offset, const uint8_t *buffer, + uint32_t length) { + // We don't support writing to arbitrary offsets in this implementation. + PBL_ASSERTN(offset == storage->current_offset); + + int fd = (int) storage->impl_data; + pfs_write(fd, buffer, length); +} + +uint32_t pb_storage_file_calculate_crc(PutBytesStorage *storage, PutBytesCrcType crc_type) { + PBL_ASSERTN(crc_type == PutBytesCrcType_Legacy); // PFS doesn't use new checksum at the moment + + int fd = (int) storage->impl_data; + return pfs_crc_calculate_file(fd, 0, storage->current_offset); +} + +void pb_storage_file_deinit(PutBytesStorage *storage, bool is_success) { + int fd = (int) storage->impl_data; + + if (!is_success) { + pfs_close_and_remove(fd); + } else { + pfs_close(fd); + } +} diff --git a/src/fw/services/common/put_bytes/put_bytes_storage_raw.c b/src/fw/services/put_bytes/put_bytes_storage_raw.c similarity index 85% rename from src/fw/services/common/put_bytes/put_bytes_storage_raw.c rename to src/fw/services/put_bytes/put_bytes_storage_raw.c index 87f5dccdcd..7d6488e814 100644 --- a/src/fw/services/common/put_bytes/put_bytes_storage_raw.c +++ b/src/fw/services/put_bytes/put_bytes_storage_raw.c @@ -1,26 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "put_bytes_storage_raw.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "pbl/services/put_bytes/put_bytes_storage_raw.h" + +#include "bluetooth/responsiveness.h" #include "drivers/flash.h" #include "drivers/task_watchdog.h" #include "flash_region/flash_region.h" #include "kernel/pbl_malloc.h" #include "resource/resource_storage_flash.h" +#include "pbl/services/comm_session/session.h" #include "system/firmware_storage.h" #include "util/math.h" @@ -38,7 +27,7 @@ typedef struct MemoryLayout { static const MemoryLayout* prv_get_layout_for_type(PutBytesObjectType object_type) { static const MemoryLayout layouts[] = { -#if !CAPABILITY_HAS_PBLBOOT +#ifndef CONFIG_PBLBOOT { FLASH_REGION_FIRMWARE_DEST_BEGIN, FLASH_REGION_FIRMWARE_DEST_END, sizeof(FirmwareDescription) }, { FLASH_REGION_FIRMWARE_DEST_BEGIN, @@ -133,6 +122,13 @@ bool pb_storage_raw_init(PutBytesStorage *storage, PutBytesObjectType object_typ storage->current_offset = layout->start_offset; + // Reduce BLE activity for the duration of raw flash operations to help prevent stack overflow + // in NimbleHost task. Flash operations block and concurrent BLE mbuf handling can cause deep + // call stacks. Using ResponseTimeMiddle reduces the frequency of BLE events while still + // maintaining reasonable transfer speeds. + comm_session_set_responsiveness(comm_session_get_system_session(), + BtConsumerPpPutBytes, ResponseTimeMiddle, 0); + // This erase operation will take awhile, so disable the task watchdog for the current task // while we're doing this. bool previous_system_task_watchdog_state = task_watchdog_mask_get(PebbleTask_KernelBackground); @@ -183,5 +179,8 @@ uint32_t pb_storage_raw_calculate_crc(PutBytesStorage *storage, PutBytesCrcType } void pb_storage_raw_deinit(PutBytesStorage *storage, bool is_success) { - // Nothing to do + // Restore normal BLE responsiveness that was reduced in pb_storage_raw_init + comm_session_set_responsiveness(comm_session_get_system_session(), + BtConsumerPpPutBytes, ResponseTimeMin, + MIN_LATENCY_MODE_TIMEOUT_PUT_BYTES_SECS); } diff --git a/src/fw/services/put_bytes/wscript_build b/src/fw/services/put_bytes/wscript_build new file mode 100644 index 0000000000..8858fd9222 --- /dev/null +++ b/src/fw/services/put_bytes/wscript_build @@ -0,0 +1,17 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'put_bytes.c', + 'put_bytes_storage.c', + 'put_bytes_storage_file.c', + 'put_bytes_storage_raw.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_put_bytes', +) + +bld.env.SERVICES.append('services_put_bytes') diff --git a/src/fw/services/registry_endpoint/Kconfig b/src/fw/services/registry_endpoint/Kconfig new file mode 100644 index 0000000000..5fbd9f8200 --- /dev/null +++ b/src/fw/services/registry_endpoint/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_REGISTRY_ENDPOINT + bool "Registry endpoint" + help + Registry endpoint for remote key/value access. diff --git a/src/fw/services/registry_endpoint/service.c b/src/fw/services/registry_endpoint/service.c new file mode 100644 index 0000000000..ed3132a89d --- /dev/null +++ b/src/fw/services/registry_endpoint/service.c @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/session.h" +#include "mfg/mfg_info.h" + +#include + +#define MFG_COLOR_KEY "mfg_color" + +#define FACTORY_REGISTRY_ENDPOINT 5001 + +static void prv_send_response(const uint8_t *data, unsigned int length) { + comm_session_send_data(comm_session_get_system_session(), FACTORY_REGISTRY_ENDPOINT, + data, length, COMM_SESSION_DEFAULT_TIMEOUT); +} + +static void prv_send_color_response(void) { + const uint8_t response[] = { 0x01, 0x04, 0x0, 0x0, 0x0, mfg_info_get_watch_color() }; + prv_send_response(response, sizeof(response)); +} + +static void prv_send_fail_response(void) { + const uint8_t error_response = 0xff; + prv_send_response(&error_response, sizeof(error_response)); +} + +void factory_registry_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length_bytes) { + // Expected message is 0x0 (Read), 0x09 (Length of key), followed by the string "mfg_color". + // All other messages just get an error response + + if (length_bytes == 1 + 1 + strlen(MFG_COLOR_KEY) && + data[0] == 0x0 && + data[1] == strlen(MFG_COLOR_KEY) && + memcmp(data + 2, MFG_COLOR_KEY, strlen(MFG_COLOR_KEY)) == 0) { + + prv_send_color_response(); + return; + } + + prv_send_fail_response(); +} + diff --git a/src/fw/services/registry_endpoint/wscript_build b/src/fw/services/registry_endpoint/wscript_build new file mode 100644 index 0000000000..fb1946c7b5 --- /dev/null +++ b/src/fw/services/registry_endpoint/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_registry_endpoint', +) +bld.env.SERVICES.append('services_registry_endpoint') diff --git a/src/fw/services/regular_timer/Kconfig b/src/fw/services/regular_timer/Kconfig new file mode 100644 index 0000000000..504eb8837a --- /dev/null +++ b/src/fw/services/regular_timer/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_REGULAR_TIMER + bool "Regular timer" + help + Periodic timer service (seconds/minutes ticks). + +if SERVICE_REGULAR_TIMER + +module = SERVICE_REGULAR_TIMER +module-str = Regular timer +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/regular_timer/service.c b/src/fw/services/regular_timer/service.c new file mode 100644 index 0000000000..e63e371abd --- /dev/null +++ b/src/fw/services/regular_timer/service.c @@ -0,0 +1,289 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/regular_timer.h" + +#include "os/mutex.h" +#include "pbl/services/new_timer/new_timer.h" +#include "system/logging.h" +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "portmacro.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_regular_timer, CONFIG_SERVICE_REGULAR_TIMER_LOG_LEVEL); + +//! Don't let users modify the list while callbacks are occurring. +static PebbleMutex * s_callback_list_semaphore = 0; + +//! The timer we use +static TimerID s_timer_id = TIMER_INVALID_ID; + +static ListNode s_seconds_callbacks; +static ListNode s_minutes_callbacks; + +// Set to 90 seconds because we do eventually drift. Make it in the middle of a minute so we can +// be sure that it isn't due to drifting. +#define MISSING_MINUTE_CB_LOG_THRESHOLD_S 90 +static time_t s_last_minute_fire_ts; // uses +static int s_last_minute_fired = -1; // Track which minute we last fired on + + + +// ------------------------------------------------------------------------------------------- +// Passed to list_find() to determine if a callback is already registered or not +static bool prv_callback_registered_filter(ListNode *found_node, void *data) { + return (found_node == (ListNode *)data); +} + +// ------------------------------------------------------------------------------------------- +static void do_callbacks(ListNode* list) { + mutex_lock(s_callback_list_semaphore); + + for (ListNode* iter = list_get_next(list); iter != 0; ) { + RegularTimerInfo* reg_timer = (RegularTimerInfo*) iter; + + if (--reg_timer->private_count == 0) { + reg_timer->private_count = reg_timer->private_reset_count; + + // Release the mutex while we execute the callback + reg_timer->is_executing = true; + mutex_unlock(s_callback_list_semaphore); + reg_timer->cb(reg_timer->cb_data); + mutex_lock(s_callback_list_semaphore); + reg_timer->is_executing = false; + + // Get the next one to execute before we possibly remove this one + iter = list_get_next(iter); + + // Did the caller want to remove this one? + // NOTE: We do not support callers that free the memory for the regular timer structure + // from their callback procedure! + if (reg_timer->pending_delete) { + list_remove(®_timer->list_node, NULL, NULL); + } + + } else { + iter = list_get_next(iter); + } + } + + mutex_unlock(s_callback_list_semaphore); +} + +// ------------------------------------------------------------------------------------------- +static void timer_callback(void* data) { + (void) data; + + do_callbacks(&s_seconds_callbacks); + + time_t t = rtc_get_time(); + struct tm time; + localtime_r(&t, &time); + + // Fire minute callbacks when the minute changes (not just when tm_sec == 0) + // This prevents missing callbacks when RTC adjusts + bool should_fire_minute = false; + if (s_last_minute_fired == -1) { + // First run - initialize but don't fire + s_last_minute_fired = time.tm_min; + } else if (s_last_minute_fired != time.tm_min) { + // Minute changed - fire callback + should_fire_minute = true; + s_last_minute_fired = time.tm_min; + } + + if (should_fire_minute) { + // Keep the logging to detect large time jumps (multiple minutes skipped) + const time_t now_ts = rtc_get_ticks() / configTICK_RATE_HZ; + if ((now_ts - s_last_minute_fire_ts) > MISSING_MINUTE_CB_LOG_THRESHOLD_S) { + PBL_LOG_WRN("Large time jump detected. Previous ts: %lu, Now ts: %lu", + s_last_minute_fire_ts, now_ts); + } + s_last_minute_fire_ts = now_ts; + + do_callbacks(&s_minutes_callbacks); + } +} + +// ------------------------------------------------------------------------------------------- +//! Used only once when we first start up. This should be really close to the 0ms point. +static void timer_callback_initializing(void* data) { + // FIXME: FreeRTOS timers are subject to skew if something else is running on the millisecond. + // We'll need to continously adjust our timer period in really annoying ways. + new_timer_start(s_timer_id, 1000, timer_callback, NULL, TIMER_START_FLAG_REPEATING); + + timer_callback(data); +} + +// -------------------------------------------------------------------------------------------- +void regular_timer_init(void) { + PBL_ASSERTN(s_callback_list_semaphore == 0); + + s_callback_list_semaphore = mutex_create(); + + time_t seconds; + uint16_t milliseconds; + rtc_get_time_ms(&seconds, &milliseconds); + s_timer_id = new_timer_create(); + bool success = new_timer_start(s_timer_id, 1000-milliseconds, timer_callback_initializing, NULL, 0 /*flags*/); + PBL_ASSERTN(success); +} + +// ------------------------------------------------------------------------------------------- +void regular_timer_add_multisecond_callback(RegularTimerInfo* cb, uint16_t seconds) { + PBL_ASSERTN(s_callback_list_semaphore); + + mutex_lock(s_callback_list_semaphore); + + cb->private_reset_count = seconds; + cb->private_count = seconds; + + // Only add to the list if not already registered + if (!list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node)) { + // better not be registered as a minute callback already + PBL_ASSERTN(!list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)); + cb->is_executing = false; + cb->pending_delete = false; + list_append(&s_seconds_callbacks, &cb->list_node); + } else { + // If it is marked for deletion, remove the deletion flag + cb->pending_delete = false; + } + + mutex_unlock(s_callback_list_semaphore); +} + +// -------------------------------------------------------------------------------------------- +void regular_timer_add_seconds_callback(RegularTimerInfo* cb) { + // special case for triggering each second + regular_timer_add_multisecond_callback(cb, 1); +} + +// -------------------------------------------------------------------------------------------- +void regular_timer_add_multiminute_callback(RegularTimerInfo* cb, uint16_t minutes) { + PBL_ASSERTN(s_callback_list_semaphore); + + mutex_lock(s_callback_list_semaphore); + + cb->private_reset_count = minutes; + cb->private_count = minutes; + + if (!list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)) { + // better not be registered as a minute callback already + PBL_ASSERTN(!list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node)); + cb->is_executing = false; + cb->pending_delete = false; + list_append(&s_minutes_callbacks, &cb->list_node); + } else { + // If it is marked for deletion, remove the deletion flag + cb->pending_delete = false; + } + + mutex_unlock(s_callback_list_semaphore); +} + +// ----------------------------------------------------------------------------------------- +void regular_timer_add_minutes_callback(RegularTimerInfo* cb) { + // special case for triggering each minute + regular_timer_add_multiminute_callback(cb, 1); +} + +// ------------------------------------------------------------------------------------------ +static bool prv_regular_timer_is_scheduled(RegularTimerInfo *cb) { + // Assumes mutex lock is already taken + return (list_find(&s_seconds_callbacks, prv_callback_registered_filter, &cb->list_node) || + list_find(&s_minutes_callbacks, prv_callback_registered_filter, &cb->list_node)); +} + +// ------------------------------------------------------------------------------------------ +bool regular_timer_is_scheduled(RegularTimerInfo *cb) { + PBL_ASSERTN(s_callback_list_semaphore); + + mutex_lock(s_callback_list_semaphore); + bool rv = prv_regular_timer_is_scheduled(cb); + mutex_unlock(s_callback_list_semaphore); + + return (rv); +} + +bool regular_timer_pending_deletion(RegularTimerInfo *cb) { + return cb->pending_delete; +} + +// ------------------------------------------------------------------------------------------ +bool regular_timer_remove_callback(RegularTimerInfo* cb) { + PBL_ASSERTN(s_callback_list_semaphore); + bool timer_removed = false; + mutex_lock(s_callback_list_semaphore); + + if (!prv_regular_timer_is_scheduled(cb)) { + PBL_LOG_WRN("Timer not registered"); + } else { + // If currently executing, mark for deletion. do_callbacks will delete it for us once + // it completes. + if (cb->is_executing) { + cb->pending_delete = true; + } else { + list_remove(&cb->list_node, NULL, NULL); + timer_removed = true; + } + } + + mutex_unlock(s_callback_list_semaphore); + return timer_removed; +} + + +// --------------------------------------------------------------------------------------- +// For Testing: + +void regular_timer_deinit(void) { + mutex_destroy((PebbleMutex *) s_callback_list_semaphore); + s_callback_list_semaphore = NULL; + new_timer_delete(s_timer_id); + s_timer_id = TIMER_INVALID_ID; +} + +static void prv_fire_callbacks(ListNode *list, uint16_t mod) { + mutex_lock(s_callback_list_semaphore); + ListNode* iter = list_get_next(list); + while (iter) { + RegularTimerInfo* reg_timer = (RegularTimerInfo*) iter; + if (reg_timer->private_reset_count % mod == 0) { + // Last one. Will trigger callback when do_callbacks() is called: + reg_timer->private_count = 1; + } + iter = list_get_next(iter); + } + mutex_unlock(s_callback_list_semaphore); + + do_callbacks(list); +} + +void regular_timer_fire_seconds(uint8_t secs) { + prv_fire_callbacks(&s_seconds_callbacks, secs); +} + +void regular_timer_fire_minutes(uint8_t mins) { + prv_fire_callbacks(&s_minutes_callbacks, mins); +} + +static uint32_t prv_count(ListNode *list) { + uint32_t count = 0; + mutex_lock(s_callback_list_semaphore); + // -1, because s_..._callbacks is a ListNode too + count = list_count(list) - 1; + mutex_unlock(s_callback_list_semaphore); + return count; +} + +uint32_t regular_timer_seconds_count(void) { + return prv_count(&s_seconds_callbacks); +} + +uint32_t regular_timer_minutes_count(void) { + return prv_count(&s_minutes_callbacks); +} diff --git a/src/fw/services/regular_timer/wscript_build b/src/fw/services/regular_timer/wscript_build new file mode 100644 index 0000000000..dcf1871428 --- /dev/null +++ b/src/fw/services/regular_timer/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_regular_timer', +) +bld.env.SERVICES.append('services_regular_timer') diff --git a/src/fw/services/runlevel_impl.h b/src/fw/services/runlevel_impl.h deleted file mode 100644 index 7ce3b01dfa..0000000000 --- a/src/fw/services/runlevel_impl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Definitions used to implement runlevels. -// -// The set of runlevels is defined in the runlevel.def X-Macro file. These -// definitions are used to construct two enums, RunLevel (in runlevel.h) and -// RunLevelBit (in this header). -// -// The set of runlevels for which a service should be enabled is defined by -// bitwise-OR-ing the RunLevelBit constants for every runlevel that the service -// should be enabled in to form an enable-mask. Testing whether a service should -// be enabled for a given runlevel is simply -// (enable_mask & (1 << runlevel) != 0). -// -// The RunLevelBit constants take the form R_ to minimize visual clutter -// when defining enable-masks. Since this header is only included in the source -// files for which enable-masks are defined, the potential for namespace -// pollution is minimized. - -#define RUNLEVEL(number, name) _Static_assert( \ - 0 <= number && number <= 31, \ - "The numeric value of runlevel " #name " (" #number ")" \ - " is out of range. Only runlevels in the range 0 <= level <= 31" \ - " are supported."); -#include "runlevel.def" -#undef RUNLEVEL - -typedef enum RunLevelBit { -#define RUNLEVEL(number, name) R_##name = (1 << number), -#include "runlevel.def" -#undef RUNLEVEL -} RunLevelBit; - -struct ServiceRunLevelSetting { - void (*set_enable_fn)(bool); - RunLevelBit enable_mask; -}; diff --git a/src/fw/services/send_text_service/Kconfig b/src/fw/services/send_text_service/Kconfig new file mode 100644 index 0000000000..f7d0c2bb49 --- /dev/null +++ b/src/fw/services/send_text_service/Kconfig @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SEND_TEXT_SERVICE + bool "Send text" + help + Send-text quick-reply service. diff --git a/src/fw/services/send_text_service/service.c b/src/fw/services/send_text_service/service.c new file mode 100644 index 0000000000..9c7595bb2c --- /dev/null +++ b/src/fw/services/send_text_service/service.c @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/send_text_service.h" + +#include "applib/event_service_client.h" +#include "kernel/events.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/notifications/notification_constants.h" + + +static bool s_has_send_text_reply_action = false; + +static bool prv_has_send_text_reply_action(void) { + iOSNotifPrefs *notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)SEND_TEXT_NOTIF_PREF_KEY, + strlen(SEND_TEXT_NOTIF_PREF_KEY)); + if (!notif_prefs) { + return false; + } + + bool has_reply_action = + (timeline_item_action_group_find_reply_action(¬if_prefs->action_group) != NULL); + + ios_notif_pref_db_free_prefs(notif_prefs); + return has_reply_action; +} + +static void prv_blobdb_event_handler(PebbleEvent *event, void *context) { + const PebbleBlobDBEvent *blobdb_event = &event->blob_db; + if (blobdb_event->db_id != BlobDBIdiOSNotifPref) { + return; + } + + if (blobdb_event->type != BlobDBEventTypeFlush && + memcmp(blobdb_event->key, (uint8_t *)SEND_TEXT_NOTIF_PREF_KEY, + strlen(SEND_TEXT_NOTIF_PREF_KEY))) { + return; + } + + s_has_send_text_reply_action = prv_has_send_text_reply_action(); +} + +void send_text_service_init(void) { + // Save the initial state + s_has_send_text_reply_action = prv_has_send_text_reply_action();; + + // Register for updates + static EventServiceInfo s_blobdb_event_info = { + .type = PEBBLE_BLOBDB_EVENT, + .handler = prv_blobdb_event_handler, + }; + event_service_client_subscribe(&s_blobdb_event_info); +} + +bool send_text_service_is_send_text_supported(void) { + PebbleProtocolCapabilities capabilities; + bt_persistent_storage_get_cached_system_capabilities(&capabilities); + + return (capabilities.send_text_support && s_has_send_text_reply_action); +} diff --git a/src/fw/services/send_text_service/wscript_build b/src/fw/services/send_text_service/wscript_build new file mode 100644 index 0000000000..3870878e09 --- /dev/null +++ b/src/fw/services/send_text_service/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_send_text_service', +) +bld.env.SERVICES.append('services_send_text_service') diff --git a/src/fw/services/services.c b/src/fw/services/services.c index acbe182f6b..b55dee683c 100644 --- a/src/fw/services/services.c +++ b/src/fw/services/services.c @@ -1,35 +1,22 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services.h" -#include "runlevel.h" +#include "pbl/services/services.h" +#include "pbl/services/runlevel.h" #include #include #include "console/prompt.h" -#include "services/common/services_common.h" -#include "services/normal/services_normal.h" +#include "pbl/services/services_common.h" +#include "pbl/services/services_normal.h" #include "system/logging.h" #include "system/passert.h" #include "util/size.h" #include "util/string.h" void services_early_init(void) { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW services_normal_early_init(); #endif } @@ -37,16 +24,16 @@ void services_early_init(void) { void services_init(void) { services_common_init(); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW services_normal_init(); #endif } void services_set_runlevel(RunLevel runlevel) { PBL_ASSERT(runlevel < RunLevel_COUNT, "Unknown runlevel %d", runlevel); - PBL_LOG(LOG_LEVEL_INFO, "Setting runlevel to %d", runlevel); + PBL_LOG_INFO("Setting runlevel to %d", runlevel); services_common_set_runlevel(runlevel); -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW services_normal_set_runlevel(runlevel); #endif } diff --git a/src/fw/services/services.h b/src/fw/services/services.h deleted file mode 100644 index 49afccf393..0000000000 --- a/src/fw/services/services.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Initialize services that our kernel depends on -void services_early_init(void); - -void services_init(void); diff --git a/src/fw/services/services_common/Kconfig b/src/fw/services/services_common/Kconfig new file mode 100644 index 0000000000..8e729df6cc --- /dev/null +++ b/src/fw/services/services_common/Kconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SERVICES_COMMON + bool "Common services glue" + help + Common services init/runlevel glue. Required when any other common + service is enabled. diff --git a/src/fw/services/services_common/service.c b/src/fw/services/services_common/service.c new file mode 100644 index 0000000000..9341efe93f --- /dev/null +++ b/src/fw/services/services_common/service.c @@ -0,0 +1,100 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! @file services.c +//! +//! This file should control the initialization of the various services in the right order. +//! I'll slowly move initialization routines into here as we continue to refactor services. +//! For now this will just be woefully incomplete. + +#include "pbl/services/services_common.h" + +#include "mfg/mfg_info.h" + +#include "pbl/services/accel_manager.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/app_session_capabilities.h" +#include "pbl/services/comm_session/default_kernel_sender.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/cron.h" +#include "pbl/services/firmware_update.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/light.h" +#include "pbl/services/poll_remote.h" +#include "pbl/services/put_bytes/put_bytes.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/touch/touch.h" +#include "drivers/touch/touch_sensor.h" +#include "pbl/services/vibe_pattern.h" +#include "pbl/services/runlevel_impl.h" +#include "util/size.h" + +void services_common_init(void) { + firmware_update_init(); + put_bytes_init(); + poll_remote_init(); + accel_manager_init(); + light_init(); + + cron_service_init(); + + shared_prf_storage_init(); + bt_persistent_storage_init(); + + comm_default_kernel_sender_init(); +#if !defined(CONFIG_RECOVERY_FW) + comm_session_app_session_capabilities_init(); +#endif + comm_session_init(); + + bt_ctl_init(); + +#ifdef CONFIG_TOUCH + touch_init(); +#endif + +#ifdef CONFIG_HRM + hrm_manager_init(); +#endif +} + +static struct ServiceRunLevelSetting s_runlevel_settings[] = { + { + .set_enable_fn = accel_manager_enable, + .enable_mask = R_Stationary | R_FirmwareUpdate | R_Normal, + }, + { + .set_enable_fn = light_allow, + .enable_mask = R_LowPower | R_FirmwareUpdate | R_Normal, + }, + { + .set_enable_fn = vibe_service_set_enabled, + .enable_mask = R_LowPower | R_FirmwareUpdate | R_Normal + }, + { + .set_enable_fn = bt_ctl_set_enabled, + .enable_mask = R_FirmwareUpdate | R_Normal, + }, +#if defined(CONFIG_TOUCH) && defined(CONFIG_RECOVERY_FW) + // Only keep touch enabled on recovery (and so manufacturing as well) + // Once supported in main firmware, this should be removed. + { + .set_enable_fn = touch_sensor_set_enabled, + .enable_mask = R_Normal, + }, +#endif +#ifdef CONFIG_HRM + { + .set_enable_fn = hrm_manager_enable, + .enable_mask = R_Normal, + }, +#endif +}; + +void services_common_set_runlevel(RunLevel runlevel) { + for (size_t i = 0; i < ARRAY_LENGTH(s_runlevel_settings); ++i) { + struct ServiceRunLevelSetting *service = &s_runlevel_settings[i]; + service->set_enable_fn(((1 << runlevel) & service->enable_mask) != 0); + } +} + diff --git a/src/fw/services/services_common/wscript_build b/src/fw/services/services_common/wscript_build new file mode 100644 index 0000000000..99fc19ae75 --- /dev/null +++ b/src/fw/services/services_common/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_common', +) +bld.env.SERVICES.append('services_common') diff --git a/src/fw/services/services_normal/Kconfig b/src/fw/services/services_normal/Kconfig new file mode 100644 index 0000000000..c22c03c587 --- /dev/null +++ b/src/fw/services/services_normal/Kconfig @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SERVICES_NORMAL + bool "Normal services glue" + help + Normal services init/runlevel glue. Required when any other normal + service is enabled. diff --git a/src/fw/services/services_normal/service.c b/src/fw/services/services_normal/service.c new file mode 100644 index 0000000000..99d71231f4 --- /dev/null +++ b/src/fw/services/services_normal/service.c @@ -0,0 +1,178 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/services_normal.h" + +#include + +#include "applib/event_service_client.h" +#include "drivers/rtc.h" +#include "kernel/events.h" +#include "process_management/app_install_manager.h" // FIXME: This should really be in services/ +#include "process_management/launcher_app_message.h" // FIXME: This should really be in services/ +#include "pbl/services/activity/activity.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/app_fetch_endpoint.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/endpoint_private.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/music_endpoint.h" +#include "pbl/services/music_internal.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/notifications/notifications.h" +#include "pbl/services/persist.h" +#include "pbl/services/phone_call.h" +#include "pbl/services/process_management/app_order_storage.h" +#include "pbl/services/powermode_service.h" +#include "pbl/services/send_text_service.h" +#include "shell/prefs.h" +#include "pbl/services/speaker/speaker_service.h" +#include "pbl/services/stationary.h" +#include "pbl/services/timeline/event.h" +#include "pbl/services/wakeup.h" +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/runlevel_impl.h" + +#ifdef CONFIG_ORIENTATION_MANAGER +#include "pbl/services/orientation_manager.h" +#endif + +#include "pbl/services/activity/activity.h" +#include "pbl/services/voice/voice.h" + +#include "util/size.h" + +// Minimum valid time: January 1, 2020 00:00:00 UTC (timestamp: 1577836800) +// This represents the minimum time we consider valid for activity initialization +#define MIN_VALID_TIME_TIMESTAMP 1577836800 + +// State for deferred activity initialization +static bool s_activity_init_deferred = false; +static EventServiceInfo s_time_event_info; + +static void prv_time_set_event_handler(PebbleEvent *e, void *context) { + // Check if time is now valid and activity init was deferred + if (s_activity_init_deferred && rtc_get_time() >= MIN_VALID_TIME_TIMESTAMP) { + // Time is now valid, initialize activity + s_activity_init_deferred = false; + + // Unsubscribe from time events + event_service_client_unsubscribe(&s_time_event_info); + + activity_init(); + // If the user had tracking enabled before init was deferred, start tracking now so we + // don't miss steps when initialization happens after boot. + if (activity_prefs_tracking_is_enabled()) { + // Ensure the runlevel-enabled flag is set for the activity service so the usual + // enable/disable machinery will attempt to start tracking. This covers the case + // where services_set_runlevel() already ran before activity was initialized. + activity_set_enabled(true); + activity_start_tracking(false /* test_mode */); + } + } +} + +static bool prv_is_time_valid_for_activity_init(void) { + return rtc_get_time() >= MIN_VALID_TIME_TIMESTAMP; +} + +void services_normal_early_init(void) { + pfs_init(true); +} + +void services_normal_init(void) { + persist_service_init(); + + app_install_manager_init(); + + blob_db_init_dbs(); + app_cache_init(); + phone_call_service_init(); + music_init(); + alarm_init(); + timeline_event_init(); + dls_init(); + + wakeup_init(); + + app_order_storage_init(); + + // Check if time is valid before initializing activity + if (prv_is_time_valid_for_activity_init()) { + activity_init(); + } else { + // Defer activity initialization until time is set properly + s_activity_init_deferred = true; + + // Subscribe to time set events + s_time_event_info = (EventServiceInfo) { + .type = PEBBLE_SET_TIME_EVENT, + .handler = prv_time_set_event_handler, + }; + event_service_client_subscribe(&s_time_event_info); + } + + notifications_init(); + alerts_init(); + send_text_service_init(); + protobuf_log_init(); + + weather_service_init(); + + speaker_service_init(); + +#ifdef CONFIG_MIC + voice_init(); +#endif + + app_glance_service_init(); + + powermode_service_init(); +#ifndef CONFIG_SHELL_SDK + powermode_service_set_enabled(shell_prefs_get_power_mode() == PowerMode_LowPower); +#endif +} + +static struct ServiceRunLevelSetting s_runlevel_settings[] = { + { + .set_enable_fn = wakeup_enable, + .enable_mask = R_Stationary | R_Normal, + }, + { + .set_enable_fn = alarm_service_enable_alarms, + .enable_mask = R_LowPower | R_Stationary | R_Normal, + }, + { + .set_enable_fn = activity_set_enabled, + .enable_mask = R_Stationary | R_Normal, + }, + { + .set_enable_fn = stationary_run_level_enable, + .enable_mask = R_Stationary | R_Normal, + }, + { + .set_enable_fn = dls_set_send_enable_run_level, + .enable_mask = R_Normal, + }, + { + .set_enable_fn = blob_db_enabled, + .enable_mask = R_Normal, + }, +#ifdef CONFIG_ORIENTATION_MANAGER + { + .set_enable_fn = orientation_manager_enable, + .enable_mask = R_LowPower | R_Stationary | R_Normal, + } +#endif +}; + +void services_normal_set_runlevel(RunLevel runlevel) { + for (size_t i = 0; i < ARRAY_LENGTH(s_runlevel_settings); ++i) { + struct ServiceRunLevelSetting *service = &s_runlevel_settings[i]; + service->set_enable_fn(((1 << runlevel) & service->enable_mask) != 0); + } +} diff --git a/src/fw/services/services_normal/wscript_build b/src/fw/services/services_normal/wscript_build new file mode 100644 index 0000000000..06d537258b --- /dev/null +++ b/src/fw/services/services_normal/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_normal', +) +bld.env.SERVICES.append('services_normal') diff --git a/src/fw/services/settings/Kconfig b/src/fw/services/settings/Kconfig new file mode 100644 index 0000000000..68492ab542 --- /dev/null +++ b/src/fw/services/settings/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SETTINGS + bool "Settings" + help + Settings-file storage service. + +if SERVICE_SETTINGS + +module = SERVICE_SETTINGS +module-str = Settings +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/settings/settings_file.c b/src/fw/services/settings/settings_file.c new file mode 100644 index 0000000000..99743ee250 --- /dev/null +++ b/src/fw/services/settings/settings_file.c @@ -0,0 +1,744 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/settings/settings_raw_iter.h" + +#include "drivers/rtc.h" +#include "drivers/task_watchdog.h" +#include "kernel/pbl_malloc.h" +#include "pbl/services/filesystem/pfs.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/crc8.h" + +#include +#include + +PBL_LOG_MODULE_DEFINE(service_settings, CONFIG_SERVICE_SETTINGS_LOG_LEVEL); + +// Callback for settings changes (used by settings_sync) +static SettingsFileChangeCallback s_change_callback = NULL; + +static status_t bootup_check(SettingsFile *file); +static void compute_stats(SettingsFile *file); + +static bool file_hdr_is_uninitialized(SettingsFileHeader *file_hdr) { + return (file_hdr->magic == 0xffffffff) && (file_hdr->version == 0xffff) + && (file_hdr->flags == 0xffff); +} + +static status_t prv_open(SettingsFile *file, const char *name, uint8_t flags, + int max_used_space, int alloc_used_space, + int min_alloc_used_space) { + // Making the max_space_total at least a little bit larger than the + // alloc_used_space allows us to avoid thrashing. Without it, if + // max_space_total == alloc_used_space, then if the file is full, changing a + // single value would force the whole file to be rewritten- every single + // time! It's probably worth it to "waste" a bit of flash space to avoid + // this pathalogical case. + int max_space_total = pfs_sector_optimal_size(alloc_used_space * 12 / 10, strlen(name)); + + int fd = pfs_open(name, flags, FILE_TYPE_STATIC, max_space_total); + if (fd < 0) { + PBL_LOG_ERR("Could not open settings file '%s', %d", name, fd); + if (fd == E_BUSY) { + // This is very bad. Someone didn't use a mutex. There could already be + // silent corruption, so it's better to crash now rather than let things + // get even more scrambled. + PBL_CROAK("Settings file is already open!"); + } + return fd; + } + + *file = (SettingsFile) { + .name = kernel_strdup_check(name), + .max_used_space = max_used_space, + .alloc_used_space = alloc_used_space, + .min_alloc_used_space = min_alloc_used_space, + .max_space_total = max_space_total, + }; + + settings_raw_iter_init(&file->iter, fd, file->name); + + SettingsFileHeader file_hdr = file->iter.file_hdr; + if (file_hdr_is_uninitialized(&file_hdr)) { + // Newly created file, create & write out header. + memcpy(&file_hdr.magic, SETTINGS_FILE_MAGIC, sizeof(file_hdr.magic)); + file_hdr.version = SETTINGS_FILE_VERSION; + settings_raw_iter_write_file_header(&file->iter, &file_hdr); + } + + if (memcmp(&file_hdr.magic, SETTINGS_FILE_MAGIC, sizeof(file_hdr.magic)) != 0) { + PBL_LOG_ERR("Attempted to open %s, not a settings file. Removing and recreating.", name); + status_t rv = pfs_close_and_remove(fd); + if (rv < 0) { + PBL_LOG_ERR("Could not remove corrupt settings file %s: %"PRId32, name, rv); + return rv; + } + return prv_open(file, name, flags, max_used_space, alloc_used_space, min_alloc_used_space); + } + + if (file_hdr.version > SETTINGS_FILE_VERSION) { + PBL_LOG_WRN("Unrecognized version %d for file %s, removing...", + file_hdr.version, name); + status_t rv = pfs_close_and_remove(fd); + if (rv < 0) { + PBL_LOG_ERR("Could not remove old-version settings file %s: %"PRId32, name, rv); + return rv; + } + return prv_open(file, name, flags, max_used_space, alloc_used_space, min_alloc_used_space); + } + + // For growable files, adopt the actual file size before bootup_check so that + // any compaction during recovery uses the correct (grown) allocation size. + int actual_size = pfs_get_file_size(file->iter.fd); + if (alloc_used_space < max_used_space && actual_size > max_space_total) { + file->alloc_used_space = actual_size * 10 / 12; + file->max_space_total = actual_size; + } + + status_t status = bootup_check(file); + if (status < 0) { + PBL_LOG_ERR("Bootup check failed (%"PRId32"), not good. " + "Attempting to recover by deleting %s...", status, name); + pfs_close_and_remove(fd); + return prv_open(file, name, flags, max_used_space, alloc_used_space, min_alloc_used_space); + } + + // There's a chance that the caller increased the desired size of the settings file since + // the file was originally created (i.e. the file was created in an earlier version of the + // firmware). If we detect that situation, let's re-write the file to the new larger requested + // size. + if (alloc_used_space >= max_used_space && actual_size < max_space_total) { + PBL_LOG_INFO("Re-writing settings file %s to increase its size from %d to %d.", + name, actual_size, max_space_total); + status = settings_file_rewrite_filtered(file, NULL, NULL); + if (status < 0) { + PBL_LOG_ERR("Could not resize file %s (error %"PRId32"). Creating new one", + name, status); + return prv_open(file, name, flags, max_used_space, alloc_used_space, min_alloc_used_space); + } + } + + compute_stats(file); + + return S_SUCCESS; +} + +status_t settings_file_open(SettingsFile *file, const char *name, + int max_used_space) { + return prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, + max_used_space, max_used_space, max_used_space); +} + +status_t settings_file_open_growable(SettingsFile *file, const char *name, + int max_used_space, int initial_alloc_size) { + // prv_grow doubles alloc_used_space; a zero seed would loop forever. + PBL_ASSERTN(initial_alloc_size > 0); + return prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, + max_used_space, initial_alloc_size, initial_alloc_size); +} + +void settings_file_close(SettingsFile *file) { + settings_raw_iter_deinit(&file->iter); + kernel_free(file->name); + file->name = NULL; +} + +static int record_size(SettingsRecordHeader *hdr) { + return sizeof(*hdr) + hdr->key_len + hdr->val_len; +} + +// Flags are stored in flash the inverse of how you might normally expect- a +// zero denotes that the flag is set, a 1 means it is not. This is because our +// flash chip is NOR flash, and thus is all 1's by default. +// Once setting a flag, we cannot unset it. +static void set_flag(SettingsRecordHeader *hdr, uint8_t flags) { + hdr->flags &= ~flags; +} +static void clear_flag(SettingsRecordHeader *hdr, uint8_t flags) { + hdr->flags |= flags; +} +static bool flag_is_set(SettingsRecordHeader *hdr, uint8_t flags) { + return (hdr->flags & flags) == 0; +} + +// Records have 4 possible states: +// - EOF marker: Header is all 1s +// - partially_written: Some bits in the header have been changed to 0s, but +// the entire record has not been completely written yet. Records in this +// state are removed on bootup, since they are in an indeterminate state. +// - written: The typical state for a record. == !partially_written +// - partially_overwritten: This record has been superceeded by another, which +// we are currently in the process of writing out to flash. Records in +// this state are restored on bootup. +// - overwritten: This record has been superceeded by another, which has been +// completely written out to flash. We skip over and ignore overwritten +// records. +static bool partially_written(SettingsRecordHeader *hdr) { + return !flag_is_set(hdr, SETTINGS_FLAG_WRITE_COMPLETE); +} +static bool partially_overwritten(SettingsRecordHeader *hdr) { + return flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_STARTED) + && !flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); +} +static bool overwritten(SettingsRecordHeader *hdr) { + return flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_STARTED) + && flag_is_set(hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); +} + +static uint32_t utc_time() { + return rtc_get_time(); +} + +static bool deleted_and_expired(SettingsRecordHeader *hdr) { + return (hdr->val_len == 0) + && (hdr->last_modified <= (utc_time() - DELETED_LIFETIME)); +} + +static void compute_stats(SettingsFile *file) { + file->dead_space = 0; + file->used_space = 0; + file->last_modified = 0; + file->used_space += sizeof(SettingsFileHeader); + file->used_space += sizeof(SettingsRecordHeader); // EOF Marker + for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); + settings_raw_iter_next(&file->iter)) { + if (overwritten(&file->iter.hdr) || deleted_and_expired(&file->iter.hdr)) { + file->dead_space += record_size(&file->iter.hdr); + } else { + file->used_space += record_size(&file->iter.hdr); + } + if (file->iter.hdr.last_modified > file->last_modified) { + file->last_modified = file->iter.hdr.last_modified; + } + } +} + +status_t settings_file_rewrite_filtered( + SettingsFile *file, SettingsFileRewriteFilterCallback filter_cb, void *context) { + // FIRM-1649: instrumentation. Compaction holds the per-storage mutex for the + // entire rewrite under flash erases that can take seconds; suspected cause of + // multi-second KernelMain stalls. Log start/end + elapsed. + const RtcTicks rewrite_start_ticks = rtc_get_ticks(); + PBL_LOG_INFO("FIRM-1649: settings_file_rewrite_filtered start file=%s", + file->name); + + // One reusable buffer for key+val per record; sized for the worst case. + // Avoids two malloc/free pairs per record over what can be thousands of + // records on a large persist file. + uint8_t *kv_buf = kernel_malloc(SETTINGS_KEY_MAX_LEN + SETTINGS_VAL_MAX_LEN); + char *name = kernel_strdup(file->name); + if (!kv_buf || !name) { + PBL_LOG_ERR("Could not allocate buffers to compact settings file %s", file->name); + kernel_free(kv_buf); + kernel_free(name); + return E_OUT_OF_MEMORY; + } + + // A 1 MiB grow can take many seconds of pure flash erase + write time. Pause + // the task watchdog rather than letting it trip and kick App Throttling. + task_watchdog_pause(60); + + SettingsFile new_file; + status_t status = prv_open(&new_file, file->name, OP_FLAG_OVERWRITE | OP_FLAG_READ, + file->max_used_space, file->alloc_used_space, + file->min_alloc_used_space); + if (status < 0) { + PBL_LOG_ERR("Could not open temporary file to compact settings file. Error %"PRIi32".", + status); + kernel_free(kv_buf); + kernel_free(name); + task_watchdog_resume(); + return status; + } + + settings_raw_iter_begin(&new_file.iter); + + for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); + settings_raw_iter_next(&file->iter)) { + SettingsRecordHeader *hdr = &file->iter.hdr; + if (partially_written(hdr)) { + // This should only happen if we reboot in the middle of writing a new + // record, and it should always be the most recently written record, so + // we shouldn't lose any data here (except for the partially written + // record, but we should ignore it's garbage data anyway). + break; + } + if (overwritten(hdr) || deleted_and_expired(hdr)) { + continue; + } + if (partially_overwritten(hdr)) { + // The only case where we should hit this is if we are compacting a file + // which has a record which was in the middle of being overwritten, but + // the write of the new record didn't finish by the time we rebooted. + // There should only ever be one such record, and we shouldn't ever hit + // this if writing the new record actually completed, since we check + // for this case in bootup_check(). + clear_flag(hdr, SETTINGS_FLAG_OVERWRITE_STARTED); + } + + // Read key+val in a single PFS call; key occupies the first key_len bytes. + settings_raw_iter_read_key_val(&file->iter, kv_buf); + uint8_t *key = kv_buf; + uint8_t *val = kv_buf + hdr->key_len; + + // Include in re-written file if it passes the filter + if (!filter_cb || filter_cb(key, hdr->key_len, val, hdr->val_len, context)) { + settings_raw_iter_write_header(&new_file.iter, hdr); + settings_raw_iter_write_key_val(&new_file.iter, kv_buf); + settings_raw_iter_next(&new_file.iter); + } + } + kernel_free(kv_buf); + settings_file_close(file); + // We have to close and reopen the new_file so that it's temp flag is cleared. + // Before the close succeeds, if we reboot, we will just end up reading the + // old file. After the close suceeds, we will end up reading the new + // (compacted) file. + int alloc_used_space = new_file.alloc_used_space; + int min_alloc_used_space = new_file.min_alloc_used_space; + settings_file_close(&new_file); + status = prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, + file->max_used_space, alloc_used_space, min_alloc_used_space); + kernel_free(name); + + task_watchdog_resume(); + + // FIRM-1649: instrumentation. See note at the top of this function. + const uint32_t rewrite_elapsed_ms = + (uint32_t)(((rtc_get_ticks() - rewrite_start_ticks) * 1000) / RTC_TICKS_HZ); + PBL_LOG_INFO( + "FIRM-1649: settings_file_rewrite_filtered end file=%s elapsed=%"PRIu32"ms status=%"PRIi32, + file->name, rewrite_elapsed_ms, status); + + return status; +} + +void settings_file_set_change_callback(SettingsFileChangeCallback callback) { + s_change_callback = callback; +} + +status_t settings_file_compact(SettingsFile *file) { + // For growable files, drop alloc_used_space toward the floor when live data + // is much smaller than the current allocation. Without this, a file that + // burst to e.g. 256 KiB and then idled would hold that flash forever even + // after the records were deleted, slowly bleeding free PFS space across + // device lifetime. Aim for the smallest doubling step that leaves ~2x + // headroom over used_space; the next grow cycle will re-expand if needed. + const int old_alloc = file->alloc_used_space; + if (file->alloc_used_space > file->min_alloc_used_space) { + int target = file->min_alloc_used_space; + while (target < file->used_space * 2 && target < file->alloc_used_space) { + target *= 2; + } + if (target < file->alloc_used_space) { + file->alloc_used_space = target; + } + } + status_t status = settings_file_rewrite_filtered(file, NULL, NULL); + if (status < 0) { + // rewrite_filtered fails before the swap if it fails at all; the on-disk + // file is still at old_alloc, so put the in-memory book-keeping back. + file->alloc_used_space = old_alloc; + } + return status; +} + +static bool key_matches(SettingsRawIter *iter, const uint8_t *key, int key_len) { + SettingsRecordHeader *hdr = &iter->hdr; + if (key_len != hdr->key_len) { + return false; + } + if (crc8_calculate_bytes(key, key_len, true /* big_endian */) != hdr->key_hash) { + return false; + } + uint8_t hdr_key[hdr->key_len]; + settings_raw_iter_read_key(iter, hdr_key); + if (memcmp(key, hdr_key, hdr->key_len) == 0) { + return true; + } + return false; +} + +static bool prv_is_desired_hdr(SettingsRawIter *iter, const uint8_t *key, int key_len) { + if (overwritten(&iter->hdr) || partially_written(&iter->hdr)) { + return false; + } + + return key_matches(iter, key, key_len); +} + +static bool search_forward(SettingsRawIter *iter, const uint8_t *key, int key_len) { + const int resumed_pos = settings_raw_iter_get_resumed_record_pos(iter); + + // Resume searching at the current record + for (; !settings_raw_iter_end(iter); settings_raw_iter_next(iter)) { + if (prv_is_desired_hdr(iter, key, key_len)) { + return true; + } + } + + // If we got here, we didn't find it between `resumed_pos` and the last record in the file. + // Wrap around to the beginning and search until we get to the `resumed_pos` + settings_raw_iter_begin(iter); + for (; settings_raw_iter_get_current_record_pos(iter) < resumed_pos; + settings_raw_iter_next(iter)) { + if (prv_is_desired_hdr(iter, key, key_len)) { + return true; + } + } + + // No record found + return false; +} + +static status_t cleanup_partial_transactions(SettingsFile *file) { + for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); + settings_raw_iter_next(&file->iter)) { + + if (partially_written(&file->iter.hdr)) { + // Compact will remove partially written records. We could be smarter, + // but this is something of an edge case. + return settings_file_compact(file); + } + + if (!partially_overwritten(&file->iter.hdr)) { + continue; + } + + int partially_overwritten_record_pos = + settings_raw_iter_get_current_record_pos(&file->iter); + uint8_t key[file->iter.hdr.key_len]; + settings_raw_iter_read_key(&file->iter, key); + settings_raw_iter_next(&file->iter); // Skip the current record + bool found_another = search_forward(&file->iter, key, file->iter.hdr.key_len); + + if (!found_another) { + // No other file->iter.hdr found, we must have rebooted in the middle of + // writing the new record. Compacting the file will copy over the + // previous record while clearing the overwrite bits for us, so that we + // can still find the previous record. We could be smarter here, but + // this should happen pretty rarely. + return settings_file_compact(file); + } + + // The overwrite completed, we just rebooted before getting a chance + // to flip the completion bit on the previous record. Flip it now so + // that we don't have to keep checking on every boot. + settings_raw_iter_set_current_record_pos(&file->iter, + partially_overwritten_record_pos); + set_flag(&file->iter.hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); + settings_raw_iter_write_header(&file->iter, &file->iter.hdr); + } + return S_SUCCESS; +} + +static status_t bootup_check(SettingsFile* file) { + return cleanup_partial_transactions(file); +} + +int settings_file_get_len(SettingsFile *file, const void *key, size_t key_len) { + settings_raw_iter_resume(&file->iter); + if (search_forward(&file->iter, key, key_len)) { + return file->iter.hdr.val_len; + } else { + return 0; + } +} + +bool settings_file_exists(SettingsFile *file, const void *key, size_t key_len) { + return (settings_file_get_len(file, key, key_len) > 0); +} + +status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len, + void *val_out, size_t val_out_len) { + settings_raw_iter_resume(&file->iter); + if (!search_forward(&file->iter, key, key_len)) { + memset(val_out, 0, val_out_len); + return E_DOES_NOT_EXIST; + } + if (deleted_and_expired(&file->iter.hdr)) { + memset(val_out, 0, val_out_len); + return E_DOES_NOT_EXIST; + } + size_t val_len = file->iter.hdr.val_len; + if (val_out_len > val_len) { + memset(val_out, 0, val_out_len); + return E_RANGE; + } + settings_raw_iter_read_val(&file->iter, val_out, val_out_len); + return S_SUCCESS; +} + +status_t settings_file_set_byte(SettingsFile *file, const void *key, + size_t key_len, size_t offset, uint8_t byte) { + if (key_len > SETTINGS_KEY_MAX_LEN) { + return E_RANGE; + } + + // Find the record + settings_raw_iter_resume(&file->iter); + if (!search_forward(&file->iter, key, key_len) || + file->iter.hdr.val_len == 0) { + return E_DOES_NOT_EXIST; + } + + PBL_ASSERTN(offset < file->iter.hdr.val_len); + settings_raw_iter_write_byte(&file->iter, offset, byte); + + return S_SUCCESS; +} + +static status_t prv_grow(SettingsFile *file, int needed_used_space) { + int new_alloc = file->alloc_used_space; + while (new_alloc < needed_used_space && new_alloc < file->max_used_space) { + new_alloc *= 2; + } + if (new_alloc > file->max_used_space) { + new_alloc = file->max_used_space; + } + if (new_alloc < needed_used_space) { + return E_OUT_OF_STORAGE; + } + + int old_alloc = file->alloc_used_space; + file->alloc_used_space = new_alloc; + status_t status = settings_file_rewrite_filtered(file, NULL, NULL); + if (status < 0) { + file->alloc_used_space = old_alloc; + } + return status; +} + +// Internal implementation that takes a timestamp parameter +// Note that this operation is designed to be atomic from the perspective of +// an outside observer. That is, either the new value will be completely +// written and returned for all future queries, or, if we reboot/loose power/ +// run into an error, then we will continue to return the previous value. +// We should never run into a case where neither value exists. +static status_t prv_settings_file_set_internal(SettingsFile *file, const void *key, size_t key_len, + const void *val, size_t val_len, + uint32_t timestamp) { + // Cannot set keys while iterating (Try settings_file_rewrite) + PBL_ASSERTN(file->cur_record_pos == 0); + if (key_len > SETTINGS_KEY_MAX_LEN) { + return E_RANGE; + } + if (val_len > SETTINGS_VAL_MAX_LEN) { + return E_RANGE; + } + const bool is_delete = (val_len == 0); + const int rec_size = sizeof(SettingsRecordHeader) + key_len + val_len; + if (!is_delete && file->used_space + rec_size > file->max_used_space) { + return E_OUT_OF_STORAGE; + } + if (file->used_space + file->dead_space + rec_size > file->max_space_total) { + bool needs_growth = (file->used_space + rec_size > file->max_space_total) && + (file->alloc_used_space < file->max_used_space); + status_t status; + if (needs_growth) { + status = prv_grow(file, file->used_space + rec_size); + } else { + status = settings_file_compact(file); + } + if (status < 0) { + return status; + } + } + + int overwritten_record = -1; + // Find an existing record, if any, and mark it as overwrite-in-progress. + settings_raw_iter_resume(&file->iter); + if (search_forward(&file->iter, key, key_len)) { + set_flag(&file->iter.hdr, SETTINGS_FLAG_OVERWRITE_STARTED); + settings_raw_iter_write_header(&file->iter, &file->iter.hdr); + overwritten_record = settings_raw_iter_get_current_record_pos(&file->iter); + } + + while (!settings_raw_iter_end(&file->iter)) { + settings_raw_iter_next(&file->iter); + } + + // Create and write out a new record. Writing the header transitions us into + // the write-in-progress state, since at least once of the bits must be + // flipped from a 1 to a 0 in order for the header to be valid. + SettingsRecordHeader new_hdr; + memset(&new_hdr, 0xff, sizeof(new_hdr)); + new_hdr.last_modified = timestamp; + new_hdr.key_hash = crc8_calculate_bytes(key, key_len, true /* big_endian */); + new_hdr.key_len = key_len; + new_hdr.val_len = val_len; + + settings_raw_iter_write_header(&file->iter, &new_hdr); + settings_raw_iter_write_key(&file->iter, key); + settings_raw_iter_write_val(&file->iter, val); + + // Mark the new record as write complete, now that we have completely written + // out the header, key, and value. + set_flag(&new_hdr, SETTINGS_FLAG_WRITE_COMPLETE); + settings_raw_iter_write_header(&file->iter, &new_hdr); + file->used_space += rec_size; + + // Finally, mark the existing record, if any, as overwritten. + if (overwritten_record >= 0) { + settings_raw_iter_set_current_record_pos(&file->iter, overwritten_record); + set_flag(&file->iter.hdr, SETTINGS_FLAG_OVERWRITE_COMPLETE); + settings_raw_iter_write_header(&file->iter, &file->iter.hdr); + file->dead_space += record_size(&file->iter.hdr); + file->used_space -= record_size(&file->iter.hdr); + } + + // Notify change callback if registered (for settings sync) + if (s_change_callback) { + s_change_callback(file, key, key_len, new_hdr.last_modified); + } + + return S_SUCCESS; +} + +status_t settings_file_set(SettingsFile *file, const void *key, size_t key_len, + const void *val, size_t val_len) { + return prv_settings_file_set_internal(file, key, key_len, val, val_len, utc_time()); +} + +status_t settings_file_set_with_timestamp(SettingsFile *file, const void *key, size_t key_len, + const void *val, size_t val_len, uint32_t timestamp) { + return prv_settings_file_set_internal(file, key, key_len, val, val_len, timestamp); +} + +status_t settings_file_mark_synced(SettingsFile *file, const void *key, size_t key_len) { + // Cannot set keys while iterating (Try settings_file_rewrite) + PBL_ASSERTN(file->cur_record_pos == 0); + if (key_len > SETTINGS_KEY_MAX_LEN) { + return E_RANGE; + } + + // Find an existing record, if any, and mark it as synced + settings_raw_iter_resume(&file->iter); + if (search_forward(&file->iter, key, key_len)) { + set_flag(&file->iter.hdr, SETTINGS_FLAG_SYNCED); + settings_raw_iter_write_header(&file->iter, &file->iter.hdr); + return S_SUCCESS; + } + + return E_DOES_NOT_EXIST; +} + +static void prv_mark_all_dirty_rewrite_cb(SettingsFile *old_file, SettingsFile *new_file, + SettingsRecordInfo *info, void *context) { + // Read key and value from old file + uint8_t key[SETTINGS_KEY_MAX_LEN]; + info->get_key(old_file, key, info->key_len); + + uint8_t *val = kernel_malloc(info->val_len); + if (!val) { + return; // Skip this record on OOM + } + info->get_val(old_file, val, info->val_len); + + // Write to new file with original timestamp - records are dirty by default (no SYNCED flag) + // Preserving the timestamp ensures sync conflict resolution works correctly + settings_file_set_with_timestamp(new_file, key, info->key_len, val, info->val_len, + info->last_modified); + kernel_free(val); +} + +status_t settings_file_mark_all_dirty(SettingsFile *file) { + return settings_file_rewrite(file, prv_mark_all_dirty_rewrite_cb, NULL); +} + +status_t settings_file_delete(SettingsFile *file, + const void *key, size_t key_len) { + return settings_file_set(file, key, key_len, NULL, 0); +} + +static void prv_get_key(SettingsFile *file, void *key, size_t key_len) { + PBL_ASSERTN(key_len <= file->iter.hdr.key_len); + settings_raw_iter_set_current_record_pos(&file->iter, file->cur_record_pos); + settings_raw_iter_read_key(&file->iter, key); +} +static void prv_get_val(SettingsFile *file, void *val, size_t val_len) { + PBL_ASSERTN(val_len <= file->iter.hdr.val_len); + settings_raw_iter_set_current_record_pos(&file->iter, file->cur_record_pos); + settings_raw_iter_read_val(&file->iter, val, val_len); +} +status_t settings_file_each(SettingsFile *file, SettingsFileEachCallback cb, + void *context) { + // Cannot set keys while iterating + PBL_ASSERTN(file->cur_record_pos == 0); + SettingsRecordInfo info; + for (settings_raw_iter_begin(&file->iter); !settings_raw_iter_end(&file->iter); + settings_raw_iter_next(&file->iter)) { + if (overwritten(&file->iter.hdr) || deleted_and_expired(&file->iter.hdr)) { + continue; + } + info = (SettingsRecordInfo) { + .last_modified = file->iter.hdr.last_modified, + .get_key = prv_get_key, + .key_len = file->iter.hdr.key_len, + .get_val = prv_get_val, + .val_len = file->iter.hdr.val_len, + .dirty = !flag_is_set(&file->iter.hdr, SETTINGS_FLAG_SYNCED), + }; + file->cur_record_pos = settings_raw_iter_get_current_record_pos(&file->iter); + // if the callback returns false, stop iterating. + if (!cb(file, &info, context)) { + break; + } + settings_raw_iter_set_current_record_pos(&file->iter, file->cur_record_pos); + } + + file->cur_record_pos = 0; + + return S_SUCCESS; +} + +typedef struct { + SettingsFileRewriteCallback cb; + SettingsFile *new_file; + void *user_context; +} RewriteCbContext; +static bool prv_rewrite_cb(SettingsFile *file, SettingsRecordInfo *info, + void *context) { + RewriteCbContext *cb_ctx = (RewriteCbContext*)context; + cb_ctx->cb(file, cb_ctx->new_file, info, cb_ctx->user_context); + return true; // continue iterating +} +status_t settings_file_rewrite(SettingsFile *file, + SettingsFileRewriteCallback cb, void *context) { + char *name = kernel_strdup(file->name); + if (!name) { + PBL_LOG_ERR("Could not allocate name to rewrite settings file %s", file->name); + return E_OUT_OF_MEMORY; + } + SettingsFile new_file; + status_t status = prv_open(&new_file, file->name, + OP_FLAG_OVERWRITE | OP_FLAG_READ, + file->max_used_space, file->alloc_used_space, + file->min_alloc_used_space); + if (status < 0) { + kernel_free(name); + return status; + } + RewriteCbContext cb_ctx = (RewriteCbContext) { + .cb = cb, + .new_file = &new_file, + .user_context = context, + }; + settings_file_each(file, prv_rewrite_cb, &cb_ctx); + settings_file_close(file); + // We have to close and reopen the new_file so that it's temp flag is cleared. + // Before the close succeeds, if we reboot, we will just end up reading the + // old file. After the close suceeds, we will end up reading the new + // (compacted) file. + int alloc_used_space = new_file.alloc_used_space; + int min_alloc_used_space = new_file.min_alloc_used_space; + settings_file_close(&new_file); + status = prv_open(file, name, OP_FLAG_READ | OP_FLAG_WRITE, + file->max_used_space, alloc_used_space, min_alloc_used_space); + kernel_free(name); + + return status; +} diff --git a/src/fw/services/normal/settings/settings_raw_iter.c b/src/fw/services/settings/settings_raw_iter.c similarity index 81% rename from src/fw/services/normal/settings/settings_raw_iter.c rename to src/fw/services/settings/settings_raw_iter.c index bab66e2461..4e376a7e51 100644 --- a/src/fw/services/normal/settings/settings_raw_iter.c +++ b/src/fw/services/settings/settings_raw_iter.c @@ -1,27 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "settings_raw_iter.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/settings/settings_raw_iter.h" #include "kernel/pbl_malloc.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" +PBL_LOG_MODULE_DECLARE(service_settings, CONFIG_SERVICE_SETTINGS_LOG_LEVEL); + /////////////////////////////////////////////////// // Helper functions for handling internal errors // /////////////////////////////////////////////////// @@ -45,8 +34,7 @@ static uint8_t *read_file_into_ram(SettingsRawIter *iter) { contents = kernel_calloc(1, read_size); } if (contents == NULL) { - PBL_LOG(LOG_LEVEL_ERROR, - "Could not allocate %d bytes for corrupt file of size %d.", + PBL_LOG_ERR("Could not allocate %d bytes for corrupt file of size %d.", read_size, file_size); return NULL; } @@ -57,27 +45,25 @@ static uint8_t *read_file_into_ram(SettingsRawIter *iter) { int start_offset = end_offset - read_size; int status = pfs_seek(iter->fd, start_offset, FSeekSet); if (status < 0) { - PBL_LOG(LOG_LEVEL_ERROR, "Debug seek failed: %d", status); + PBL_LOG_ERR("Debug seek failed: %d", status); kernel_free(contents); return NULL; } int actual_read_size = pfs_read(iter->fd, contents, read_size); - PBL_LOG(LOG_LEVEL_INFO, - "Read %d (expected %d) bytes of file %s (size %d), around offset %d.", + PBL_LOG_INFO("Read %d (expected %d) bytes of file %s (size %d), around offset %d.", actual_read_size, read_size, iter->file_name, file_size, pos); return contents; } static NORETURN fatal_logic_error(SettingsRawIter *iter) { - PBL_LOG(LOG_LEVEL_ERROR, - "settings_raw_iter logic error. " + PBL_LOG_ERR("settings_raw_iter logic error. " "Attempting to read affected file into RAM for easier debugging..."); uint8_t *contents = read_file_into_ram(iter); - PBL_LOG(LOG_LEVEL_INFO, "Removing affected file %s...", iter->file_name); + PBL_LOG_INFO("Removing affected file %s...", iter->file_name); // Remove the file that caused us to get into this state before we reboot, // that way we should be able to avoid getting into a reboot loop. pfs_close_and_remove(iter->fd); - PBL_LOG(LOG_LEVEL_INFO, "Data at address %p. Rebooting...", contents); + PBL_LOG_INFO("Data at address %p. Rebooting...", contents); PBL_CROAK("Internal logic error."); } @@ -88,7 +74,7 @@ static int sfs_seek(SettingsRawIter *iter, int amount, int whence) { } int pos = pfs_seek(iter->fd, 0, FSeekCur); - PBL_LOG(LOG_LEVEL_ERROR, "Could not seek by %d from whence %d at pos %d: %"PRId32, + PBL_LOG_ERR("Could not seek by %d from whence %d at pos %d: %"PRId32, amount, whence, pos, status); fatal_logic_error(iter); } @@ -104,7 +90,7 @@ static int sfs_read(SettingsRawIter *iter, uint8_t *data, int data_len) { } int pos = pfs_seek(iter->fd, 0, FSeekCur); - PBL_LOG(LOG_LEVEL_ERROR, "Could not read data to %p of length %d at pos %d: %"PRId32, + PBL_LOG_ERR("Could not read data to %p of length %d at pos %d: %"PRId32, data, data_len, pos, status); fatal_logic_error(iter); } @@ -116,7 +102,7 @@ static int sfs_write(SettingsRawIter *iter, const uint8_t *data, int data_len) { } int pos = pfs_seek(iter->fd, 0, FSeekCur); - PBL_LOG(LOG_LEVEL_ERROR, "Could not write from %p, %d bytes at pos %d: %"PRId32, + PBL_LOG_ERR("Could not write from %p, %d bytes at pos %d: %"PRId32, data, data_len, pos, status); fatal_logic_error(iter); } @@ -202,6 +188,13 @@ void settings_raw_iter_read_val(SettingsRawIter *iter, uint8_t *val_out, int val sfs_read(iter, val_out, val_len); } +void settings_raw_iter_read_key_val(SettingsRawIter *iter, uint8_t *key_val_out) { + const int kv_len = iter->hdr.key_len + iter->hdr.val_len; + if (kv_len == 0) return; + sfs_seek(iter, iter->hdr_pos + sizeof(SettingsRecordHeader), FSeekSet); + sfs_read(iter, key_val_out, kv_len); +} + void settings_raw_iter_write_header(SettingsRawIter *iter, SettingsRecordHeader *hdr) { PBL_ASSERTN(hdr->key_len <= SETTINGS_KEY_MAX_LEN); PBL_ASSERTN(hdr->val_len <= SETTINGS_VAL_MAX_LEN); @@ -219,6 +212,13 @@ void settings_raw_iter_write_val(SettingsRawIter *iter, const uint8_t *val) { sfs_seek(iter, iter->hdr_pos + sizeof(SettingsRecordHeader) + iter->hdr.key_len, FSeekSet); sfs_write(iter, val, iter->hdr.val_len); } + +void settings_raw_iter_write_key_val(SettingsRawIter *iter, const uint8_t *key_val) { + const int kv_len = iter->hdr.key_len + iter->hdr.val_len; + if (kv_len == 0) return; + sfs_seek(iter, iter->hdr_pos + sizeof(SettingsRecordHeader), FSeekSet); + sfs_write(iter, key_val, kv_len); +} void settings_raw_iter_write_byte(SettingsRawIter *iter, int offset, uint8_t byte) { sfs_seek(iter, iter->hdr_pos + sizeof(SettingsRecordHeader) + iter->hdr.key_len + offset, FSeekSet); @@ -228,7 +228,7 @@ void settings_raw_iter_write_byte(SettingsRawIter *iter, int offset, uint8_t byt void settings_raw_iter_deinit(SettingsRawIter *iter) { int status = pfs_close(iter->fd); if (status < 0) { - PBL_LOG(LOG_LEVEL_WARNING, "Could not close settings file"); + PBL_LOG_WRN("Could not close settings file"); } } diff --git a/src/fw/services/settings/wscript_build b/src/fw/services/settings/wscript_build new file mode 100644 index 0000000000..13dff4047d --- /dev/null +++ b/src/fw/services/settings/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'settings_file.c', + 'settings_raw_iter.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_settings', +) + +bld.env.SERVICES.append('services_settings') diff --git a/src/fw/services/shared_prf_storage/Kconfig b/src/fw/services/shared_prf_storage/Kconfig new file mode 100644 index 0000000000..2e223150ce --- /dev/null +++ b/src/fw/services/shared_prf_storage/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SHARED_PRF_STORAGE + bool "Shared PRF storage" + help + Shared storage used by the PRF (recovery) firmware. + +if SERVICE_SHARED_PRF_STORAGE + +module = SERVICE_SHARED_PRF_STORAGE +module-str = Shared PRF storage +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/shared_prf_storage/shared_prf_storage.c b/src/fw/services/shared_prf_storage/shared_prf_storage.c new file mode 100644 index 0000000000..f64437d422 --- /dev/null +++ b/src/fw/services/shared_prf_storage/shared_prf_storage.c @@ -0,0 +1,661 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/shared_prf_storage/v3_sprf/shared_prf_storage_private.h" + +#include "drivers/flash.h" +#include "flash_region/flash_region.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/crc32.h" + +#include +#include + +PBL_LOG_MODULE_DEFINE(service_shared_prf_storage, CONFIG_SERVICE_SHARED_PRF_STORAGE_LOG_LEVEL); + +#define SPRF_REGION_SIZE (FLASH_REGION_SHARED_PRF_STORAGE_END - \ + FLASH_REGION_SHARED_PRF_STORAGE_BEGIN) +#define SPRF_NUM_PAGES (SPRF_REGION_SIZE / sizeof(SharedPRFData)) + +#define SPRF_PAGE_FLASH_ADDR(idx) (FLASH_REGION_SHARED_PRF_STORAGE_BEGIN + \ + (idx * sizeof(SharedPRFData))) +// CRC Unwritten state and size +#define SPRF_UNWRITTEN_CRC ((uint32_t)0xFFFFFFFF) +#define SPRF_CRC_SIZE (sizeof(uint32_t)) + +// Accessors to a fields data and size. Basically skips over the CRC for the field. +#define SPRF_FIELD_DATA(f) (((uint8_t *)f) + SPRF_CRC_SIZE) +#define SPRF_FIELD_DATA_SIZE(sz) (sz - SPRF_CRC_SIZE) + +// Accessors for the CRC of a field +// A field always has the CRC as the first element, and the CRC is 4 bytes long +#define FIELD_CRC_FROM_FIELD(f) (*(uint32_t *)f) +// 'DATA', or 'd', is SharedPRFData struct. Offset, or 'off', is the offset of the field from the +// data struct. +#define FIELD_CRC_FROM_DATA(d, off) *((uint32_t *)(((uint8_t *)d) + off)) + +// Tests if a bit is set in the flags variable +#define SPRF_FLAG_IS_SET(flags, bit) (flags & bit) +// Returns a bit in the correct place +#define SPRF_FLAG_GET_BIT(b, bit) ((b) ? bit : 0) + +#define SPRF_CUR_VERSION 0x02 + +// Keeps track of the current page within the region that the valid (or empty) page is +static uint32_t s_valid_page_idx; +static PebbleMutex *s_sprf_mutex; + +// +// Helper functions +// + +static void prv_lock(void) { + mutex_lock(s_sprf_mutex); +} + +static void prv_unlock(void) { + mutex_unlock(s_sprf_mutex); +} + +static bool prv_buffer_empty(const uint8_t *buf, size_t num_bytes) { + for (uint32_t i = 0; i < num_bytes; i++) { + if (buf[i] != 0xFF) { + return false; + } + } + return true; +} + +static uint32_t prv_current_page_flash_addr(void) { + return SPRF_PAGE_FLASH_ADDR(s_valid_page_idx); +} + +static SprfMagic prv_get_magic_for_page(uint32_t page) { + SprfMagic magic; + flash_read_bytes((uint8_t *)&magic, SPRF_PAGE_FLASH_ADDR(page), sizeof(magic)); + return magic; +} + +// +// Struct validators +// + +//! Pass a field (starting at address of CRC) and a field size. Return whether the field is valid. +//! A valid struct means that: +// 1. The field has not been written to (both CRC and field data are blank) +// 2. The CRC and the written data match +static bool prv_field_valid(const uint8_t *field, size_t field_size) { + const bool empty = prv_buffer_empty(field, field_size); + if (empty) { + return true; + } + + const uint32_t field_crc = *(uint32_t*)field; + const bool valid_crc = (field_crc == + crc32(CRC32_INIT, SPRF_FIELD_DATA(field), SPRF_FIELD_DATA_SIZE(field_size))); + return valid_crc; +} + +//! Return whether the entire SharedPRFData given as an argument is valid. +//! It checks: +//! 1. That the struct header is either ValidEntry or UnpopulatedEntry. +//! 2. If the struct is an UnpopulatedEntry, it ensures the struct is entirely empty +//! 3. If the struct is *not* an UnpopulatedEntry, it ensures that each field is either empty +//! or written with a valid CRC. +static bool prv_valid_struct(SharedPRFData *data) { + if (data->magic == SprfMagic_UnpopulatedEntry && + prv_buffer_empty((uint8_t *)data, sizeof(*data))) { + return true; + } + + if (data->magic == SprfMagic_ValidEntry && + (prv_field_valid((uint8_t *)&data->root_keys, sizeof(data->root_keys)) && + prv_field_valid((uint8_t *)&data->ble_pairing_data, sizeof(data->ble_pairing_data)) && + prv_field_valid((uint8_t *)&data->ble_pairing_name, sizeof(data->ble_pairing_name)) && + prv_field_valid((uint8_t *)&data->pinned_address, sizeof(data->pinned_address)) && + prv_field_valid((uint8_t *)&data->local_name, sizeof(data->local_name)) && + prv_field_valid((uint8_t *)&data->getting_started, sizeof(data->getting_started)))) { + return true; + } + + return false; +} + +// Stored struct setters + +static void prv_write_to_current_page(SharedPRFData *data, bool write_metadata) { + if (data) { + if (write_metadata) { + data->magic = SprfMagic_ValidEntry; + data->version = SPRF_CUR_VERSION; + } + flash_write_bytes((uint8_t *)data, prv_current_page_flash_addr(), sizeof(*data)); + } +} + +static void prv_erase_region_and_save(SharedPRFData *data) { + flash_region_erase_optimal_range_no_watchdog(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, + FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, + FLASH_REGION_SHARED_PRF_STORAGE_END, + FLASH_REGION_SHARED_PRF_STORAGE_END); + s_valid_page_idx = 0; + prv_write_to_current_page(data, false); +} + +static void prv_invalidate_current_page(void) { + PBL_LOG_DBG("Invalidating current page: #%"PRIu32, s_valid_page_idx); + // First, check if the page is Unpopulated + SprfMagic magic = prv_get_magic_for_page(s_valid_page_idx); + if (magic == SprfMagic_UnpopulatedEntry) { + // This page is already Unpopulated. No need for invalidating + return; + } + + // Invalidate current page + SprfMagic new_magic = SprfMagic_InvalidatedEntry; + flash_write_bytes((uint8_t *)&new_magic, prv_current_page_flash_addr(), sizeof(SprfMagic)); + s_valid_page_idx++; + + // Sanity check to make sure that the page we are moving to is actually empty. + if ((s_valid_page_idx >= SPRF_NUM_PAGES) || + (prv_get_magic_for_page(s_valid_page_idx) != SprfMagic_UnpopulatedEntry)) { + PBL_LOG_WRN("Ran out of pages or found corrupted next page, erasing region"); + // NOTE: This should not happen often. On boot, we delete and rewrite the region if >75% of + // regions are filled. In the worst case, this will happen if the user pair/repairs + // NUM_REGIONS * .25 times without rebooting in between. (e.g. 16 pages. + // We boot up on sector 12. User pairs 4 times, we now want to access page 16, + // that is past our region, we need to clean up. + // + // We've run out of blank pages. Delete the entire region and roll around to the front. + // This will take some time. + prv_erase_region_and_save(NULL); + s_valid_page_idx = 0; + } +} + +// +// SharedPRFData allocators, deallocators, and getters +// + +static void prv_fetch_struct(SharedPRFData *data_out) { + flash_read_bytes((uint8_t *)data_out, prv_current_page_flash_addr(), sizeof(*data_out)); + + if (!prv_valid_struct(data_out)) { + PBL_LOG_WRN("Shared PRF Storage sector # %"PRIu32" is corrupted. Invalidating" + " and starting a new one", s_valid_page_idx); + prv_invalidate_current_page(); + memset(data_out, 0xFF, sizeof(*data_out)); + } +} + +static SharedPRFData *prv_alloc_and_fetch_struct(void) { + SharedPRFData *data = kernel_zalloc_check(sizeof(SharedPRFData)); + prv_fetch_struct(data); + return data; +} + +static void prv_dealloc_struct(SharedPRFData *data) { + kernel_free(data); +} + +static void prv_persist_field(uint8_t *field, size_t offset, size_t field_size, bool calc_crc) { + SharedPRFData *data = prv_alloc_and_fetch_struct(); + + const size_t field_data_size = SPRF_FIELD_DATA_SIZE(field_size); + const uint32_t old_crc = FIELD_CRC_FROM_DATA(data, offset); + const uint32_t new_crc = (calc_crc) ? crc32(CRC32_INIT, SPRF_FIELD_DATA(field), field_data_size) + : SPRF_UNWRITTEN_CRC; + const bool same_data = + (0 == memcmp(SPRF_FIELD_DATA(field), SPRF_FIELD_DATA(((uint8_t *)data) + offset), + field_data_size)); + + if (data->magic == SprfMagic_UnpopulatedEntry) { + // Struct that is written is currently unpopulated. Set it up and write + // the struct's Magic and Version + prv_write_to_current_page(data, true); + } else if ((old_crc == new_crc) && same_data) { + // We are trying to write the same data, ignore the write + goto cleanup; + } else if (old_crc != SPRF_UNWRITTEN_CRC) { + // We are writing different data. Clear the field, write the struct again, and later write + // the new data in the empty field + memset(((uint8_t *)data) + offset, 0xFF, field_size); + prv_invalidate_current_page(); + prv_write_to_current_page(data, true); + } + + PBL_LOG_DBG("Overwriting SPRF field at offset %d, size %d", + (int)offset, (int)field_size); + + // write the crc first so it's easier to detect a non empty field (we can just read if the CRC is + // not 0xFFFFFFFF instead of comparing all bytes. + + *(uint32_t *)field = new_crc; + flash_write_bytes(field, prv_current_page_flash_addr() + offset, field_size); + +cleanup: + prv_dealloc_struct(data); +} + +static void prv_erase_field(size_t offset, size_t field_size) { + SharedPRFData *data = prv_alloc_and_fetch_struct(); + uint8_t *field_ptr = ((uint8_t *)data) + offset; + memset(field_ptr, 0xFF, field_size); + prv_persist_field(field_ptr, offset, field_size, false); + prv_dealloc_struct(data); +} + +static bool prv_fetch_field(uint8_t *field_out, size_t offset, size_t field_size) { + flash_read_bytes(field_out, prv_current_page_flash_addr() + offset, field_size); + if (!prv_field_valid(field_out, field_size)) { + // If corrupted field, delete entire page + PBL_LOG_WRN("Shared PRF Storage sector # %"PRIu32" is corrupted. Invalidating" + " and starting a new one", s_valid_page_idx); + prv_invalidate_current_page(); + return false; + } + + uint32_t written_crc = FIELD_CRC_FROM_FIELD(field_out); + if (written_crc == SPRF_UNWRITTEN_CRC) { + // If empty field, return that we have invalid data too + return false; + } + + return true; +} + +#define SPRF_PERSIST_FIELD(data, name) \ + prv_persist_field((uint8_t *)&data, offsetof(SharedPRFData, name), sizeof(data), true) +#define SPRF_ERASE_FIELD(name) \ + prv_erase_field(offsetof(SharedPRFData, name), sizeof(((SharedPRFData *)0)->name)) +#define SPRF_FETCH_FIELD(data, name) \ + prv_fetch_field((uint8_t *)&data, offsetof(SharedPRFData, name), sizeof(data)) + +//! +//! SharedPRFStorage API +//! + +//! Scans through the shared PRF flash region and finds the valid entry. +//! (Should only ever be one!) If we are > 75% through the shared PRF region, +//! erase the sector and re-write the info at offset 0. We want to make the chance +//! of blocking on an erase ~0, by doing this prep on init. +void shared_prf_storage_init(void) { + s_sprf_mutex = mutex_create(); + + prv_lock(); + { + s_valid_page_idx = SPRF_PAGE_IDX_INVALID; + + SharedPRFData data = {}; + SprfMagic page_magic = 0; + + for (uint32_t i = 0; i < SPRF_NUM_PAGES; i++) { + page_magic = prv_get_magic_for_page(i); + // Check the magic to see if we need to investigate further and read the entire contents. + if (page_magic == SprfMagic_ValidEntry || page_magic == SprfMagic_UnpopulatedEntry) { + flash_read_bytes((uint8_t *) &data, SPRF_PAGE_FLASH_ADDR(i), sizeof(data)); + if (prv_valid_struct(&data)) { + s_valid_page_idx = i; + break; + } + } + } + + // Keep a write offset, this won't work when we try to roll over to the other 25% of the sectors + if (s_valid_page_idx == SPRF_PAGE_IDX_INVALID) { + prv_erase_region_and_save(NULL); + } else if (s_valid_page_idx > (SPRF_MAX_NUM_PAGES_MULT(SPRF_NUM_PAGES))) { + prv_erase_region_and_save(&data); + } + } + prv_unlock(); +} + +void shared_prf_storage_wipe_all(void) { + prv_lock(); + prv_invalidate_current_page(); + prv_unlock(); +} + +//! +//! Custom Local Device Name APIs +//! + +bool shared_prf_storage_get_local_device_name(char *local_device_name_out, size_t max_size) { + bool rv; + prv_lock(); + { + SprfLocalName data; + rv = SPRF_FETCH_FIELD(data, local_name); + if (!rv) { + goto unlock; + } + + if (local_device_name_out) { + strncpy(local_device_name_out, data.name, max_size); + local_device_name_out[max_size - 1] = 0; + } + + // Is not zero length? + rv = (data.name[0] != 0); + } + +unlock: + prv_unlock(); + return rv; +} + +void shared_prf_storage_set_local_device_name(const char *local_device_name) { + prv_lock(); + { + SprfLocalName data = {}; + + if (local_device_name) { + strncpy(data.name, local_device_name, sizeof(data.name)); + SPRF_PERSIST_FIELD(data, local_name); + } else { + SPRF_ERASE_FIELD(local_name); + } + } + prv_unlock(); +} + +//! +//! BLE Root Key APIs +//! + +bool shared_prf_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out) { + bool rv; + prv_lock(); + { + SprfRootKeys data; + rv = SPRF_FETCH_FIELD(data, root_keys); + if (!rv) { + goto unlock; + } + + SM128BitKey nil_key = {}; + if (0 == memcmp(&nil_key, &data.keys[key_type], sizeof(nil_key))) { + rv = false; + goto unlock; + } + if (key_out) { + memcpy(key_out, &data.keys[key_type], sizeof(*key_out)); + } + + rv = true; + } + +unlock: + prv_unlock(); + return rv; +} + +void shared_prf_storage_set_root_keys(SM128BitKey *keys_in) { + prv_lock(); + { + SprfRootKeys data = {}; + if (keys_in) { + memcpy(&data.keys, keys_in, sizeof(data.keys)); + } + SPRF_PERSIST_FIELD(data, root_keys); + } + prv_unlock(); +} + +//! +//! BLE Pairing Data APIs +//! + +bool shared_prf_storage_get_ble_pairing_data(SMPairingInfo *pairing_info_out, char *name_out, + bool *requires_address_pinning_out, + uint8_t *flags) { + bool rv; + prv_lock(); + { + SprfBlePairingData data; + rv = SPRF_FETCH_FIELD(data, ble_pairing_data); + if (!rv) { + goto unlock; + } + + if (!data.fields) { + // The pairing stored is empty + rv = false; + goto unlock; + } + + if (pairing_info_out) { + *pairing_info_out = (SMPairingInfo) { + .local_encryption_info.ltk = data.l_ltk, + .local_encryption_info.ediv = data.l_ediv, + .local_encryption_info.rand = data.l_rand, + + .remote_encryption_info.ltk = data.r_ltk, + .remote_encryption_info.ediv = data.r_ediv, + .remote_encryption_info.rand = data.r_rand, + + .irk = data.irk, + .identity = data.identity, + .csrk = data.csrk, + + .is_mitm_protection_enabled = data.is_mitm_protection_enabled, + + .is_local_encryption_info_valid = + SPRF_FLAG_IS_SET(data.fields, SprfValidFields_LocalEncryptionInfoValid), + .is_remote_encryption_info_valid = + SPRF_FLAG_IS_SET(data.fields, SprfValidFields_RemoteEncryptionInfoValid), + .is_remote_identity_info_valid = + SPRF_FLAG_IS_SET(data.fields, SprfValidFields_RemoteIdentityInfoValid), + .is_remote_signing_info_valid = + SPRF_FLAG_IS_SET(data.fields, SprfValidFields_RemoteSigningInfoValid), + }; + } + + if (requires_address_pinning_out) { + *requires_address_pinning_out = data.requires_address_pinning; + } + if (flags) { + *flags = data.flags; + } + + if (name_out) { + SprfBlePairingName name_data = {}; + const bool name_rv = SPRF_FETCH_FIELD(name_data, ble_pairing_name); + // Should we return a failure on a failed name get? + if (name_rv) { + strncpy(name_out, name_data.name, BT_DEVICE_NAME_BUFFER_SIZE); + } else { + name_out[0] = '\0'; + } + } + rv = true; + } + +unlock: + prv_unlock(); + return rv; +} + +void shared_prf_storage_store_ble_pairing_data( + const SMPairingInfo *pairing_info, const char *name, bool requires_address_pinning, + uint8_t flags) { + if (!pairing_info || sm_is_pairing_info_empty(pairing_info)) { + PBL_LOG_WRN("PRF Storage: Attempting to store an NULL or empty pairing info"); + return; + } + + prv_lock(); + { + SprfBlePairingData data = (SprfBlePairingData) { + .l_ediv = pairing_info->local_encryption_info.ediv, + .l_ltk = pairing_info->local_encryption_info.ltk, + .l_rand = pairing_info->local_encryption_info.rand, + + .r_ediv = pairing_info->remote_encryption_info.ediv, + .r_ltk = pairing_info->remote_encryption_info.ltk, + .r_rand = pairing_info->remote_encryption_info.rand, + + .irk = pairing_info->irk, + .identity = pairing_info->identity, + + .csrk = pairing_info->csrk, + + .is_mitm_protection_enabled = pairing_info->is_mitm_protection_enabled, + + .requires_address_pinning = requires_address_pinning, + + .flags = flags, + + .fields = SPRF_FLAG_GET_BIT(pairing_info->is_local_encryption_info_valid, + SprfValidFields_LocalEncryptionInfoValid) | + SPRF_FLAG_GET_BIT(pairing_info->is_remote_encryption_info_valid, + SprfValidFields_RemoteEncryptionInfoValid) | + SPRF_FLAG_GET_BIT(pairing_info->is_remote_identity_info_valid, + SprfValidFields_RemoteIdentityInfoValid) | + SPRF_FLAG_GET_BIT(pairing_info->is_remote_signing_info_valid, + SprfValidFields_RemoteSigningInfoValid), + }; + + SPRF_PERSIST_FIELD(data, ble_pairing_data); + + if (name) { + // only persist name if one is actually included + SprfBlePairingName name_data = {}; + strncpy(name_data.name, name, sizeof(name_data.name)); + SPRF_PERSIST_FIELD(name_data, ble_pairing_name); + } + } + prv_unlock(); +} + +void shared_prf_storage_erase_ble_pairing_data(void) { + prv_lock(); + { + SPRF_ERASE_FIELD(ble_pairing_data); + SPRF_ERASE_FIELD(ble_pairing_name); + } + prv_unlock(); +} + +//! +//! Getting started bit +//! + +bool shared_prf_storage_get_ble_pinned_address(BTDeviceAddress *address_out) { + bool rv; + prv_lock(); + { + SprfPinnedAddress data; + rv = SPRF_FETCH_FIELD(data, pinned_address); + if (!rv) { + goto unlock; + } + + if (address_out) { + *address_out = data.pinned_address; + } + + rv = true; + } + +unlock: + prv_unlock(); + return rv; +} + +//! Stores the new BLE Pinned Address in the shared storage. +void shared_prf_storage_set_ble_pinned_address(const BTDeviceAddress *address) { + prv_lock(); + { + if (address) { + SprfPinnedAddress data = { + .pinned_address = *address, + }; + SPRF_PERSIST_FIELD(data, pinned_address); + } else { + SPRF_ERASE_FIELD(pinned_address); + } + } + prv_unlock(); +} + +//! +//! Getting started bit +//! + +bool shared_prf_storage_get_getting_started_complete(void) { + bool rv; + prv_lock(); + { + SprfGettingStarted data; + rv = SPRF_FETCH_FIELD(data, getting_started); + if (!rv) { + goto unlock; + } + rv = data.is_complete; + } +unlock: + prv_unlock(); + return rv; +} + +void shared_prf_storage_set_getting_started_complete(bool set) { + prv_lock(); + { + SprfGettingStarted data = { + .is_complete = set + }; + SPRF_PERSIST_FIELD(data, getting_started); + } + prv_unlock(); +} + +//! +//! Legacy Stubs for BT Classic - Should never be called so assert if they are! +//! + +bool shared_prf_storage_get_bt_classic_pairing_data( + BTDeviceAddress *addr_out, char *device_name_out, SM128BitKey *link_key_out, + uint8_t *platform_bits) { + WTF; +} + +void shared_prf_storage_store_bt_classic_pairing_data( + BTDeviceAddress *addr, const char *device_name, SM128BitKey *link_key, + uint8_t platform_bits) { + WTF; +} + +void shared_prf_storage_store_platform_bits(uint8_t platform_bits) { + WTF; +} + +void shared_prf_storage_erase_bt_classic_pairing_data(void) { + WTF; +} + +void shared_prf_store_pairing_data( + SMPairingInfo *pairing_info, const char *device_name_ble, BTDeviceAddress *addr, + const char *device_name_classic, SM128BitKey *link_key, uint8_t platform_bits) { + WTF; +} + +void command_force_shared_prf_flush(void) { +} + + +//! +//! Unit test functions +//! + +uint16_t shared_prf_storage_get_valid_page_number(void) { + return s_valid_page_idx; +} + +void shared_prf_storage_set_valid_page_number(uint32_t page_num) { + s_valid_page_idx = page_num; +} diff --git a/src/fw/services/shared_prf_storage/shared_prf_storage_debug.c b/src/fw/services/shared_prf_storage/shared_prf_storage_debug.c new file mode 100644 index 0000000000..a9adfb42fd --- /dev/null +++ b/src/fw/services/shared_prf_storage/shared_prf_storage_debug.c @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "console/dbgserial.h" +#include "console/prompt.h" +#include "system/hexdump.h" +#include "system/logging.h" +#include "util/string.h" + +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage_debug.h" + + +#include +#include +#include +#include + +void shared_prf_storage_dump_contents(void) { + prompt_send_response("---Shared PRF Contents---\n------------------------\n"); + + char buf[DISPLAY_BUF_LEN]; + SMPairingInfo pairing_info; + char name[BT_DEVICE_NAME_BUFFER_SIZE]; + bool requires_address_pinning; + uint8_t flags; + if (shared_prf_storage_get_ble_pairing_data(&pairing_info, name, &requires_address_pinning, + &flags)) { + bluetooth_persistent_storage_debug_dump_ble_pairing_info(&buf[0], &pairing_info); + prompt_send_response_fmt(buf, sizeof(buf), + "Req addr pin: %u, flags: %x, BLE Dev Name: %s", + requires_address_pinning, flags, name); + } else { + prompt_send_response("No BLE Data"); + } + + SM128BitKey keys[SMRootKeyTypeNum]; + if (shared_prf_storage_get_root_key(SMRootKeyTypeEncryption, &keys[SMRootKeyTypeEncryption]) && + shared_prf_storage_get_root_key(SMRootKeyTypeIdentity, &keys[SMRootKeyTypeIdentity])) { + bluetooth_persistent_storage_debug_dump_root_keys(&keys[SMRootKeyTypeIdentity], + &keys[SMRootKeyTypeEncryption]); + } else { + prompt_send_response("Missing IRK and/or ERK root key(s)!"); + } + + BTDeviceAddress addr; + + if (shared_prf_storage_get_ble_pinned_address(&addr)) { + prompt_send_response_fmt(buf, DISPLAY_BUF_LEN, "\nPinned address: "BT_DEVICE_ADDRESS_FMT, + BT_DEVICE_ADDRESS_XPLODE_PTR(&addr)); + } + + if (shared_prf_storage_get_local_device_name(name, BT_DEVICE_NAME_BUFFER_SIZE)) { + prompt_send_response_fmt(buf, BT_DEVICE_NAME_BUFFER_SIZE, "Local device name: %s", name); + } else { + prompt_send_response("No Device Name"); + } + + prompt_send_response_fmt(buf, BT_DEVICE_NAME_BUFFER_SIZE, "Started Complete: %s", + bool_to_str(shared_prf_storage_get_getting_started_complete())); +} diff --git a/src/fw/services/shared_prf_storage/wscript_build b/src/fw/services/shared_prf_storage/wscript_build new file mode 100644 index 0000000000..4418591225 --- /dev/null +++ b/src/fw/services/shared_prf_storage/wscript_build @@ -0,0 +1,12 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = ['shared_prf_storage_debug.c', 'shared_prf_storage.c'] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_shared_prf_storage', +) + +bld.env.SERVICES.append('services_shared_prf_storage') diff --git a/src/fw/services/speaker/Kconfig b/src/fw/services/speaker/Kconfig new file mode 100644 index 0000000000..6d2a186e1d --- /dev/null +++ b/src/fw/services/speaker/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SPEAKER + bool "Speaker" + help + Speaker (PCM/note-sequence) playback service. + +if SERVICE_SPEAKER + +module = SERVICE_SPEAKER +module-str = Speaker +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/speaker/note_sequence.c b/src/fw/services/speaker/note_sequence.c new file mode 100644 index 0000000000..0dc05eaad5 --- /dev/null +++ b/src/fw/services/speaker/note_sequence.c @@ -0,0 +1,249 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/speaker/note_sequence.h" + +#include + +// 256-entry sine wave lookup table (one full cycle, 16-bit signed amplitude) +// Values represent sin(2*pi*i/256) * 32767 +static const int16_t s_sine_lut[256] = { + 0, 804, 1608, 2410, 3212, 4011, 4808, 5602, + 6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793, + 12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530, + 18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594, + 23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790, + 27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956, + 30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971, + 32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757, + 32767, 32757, 32728, 32678, 32609, 32521, 32412, 32285, + 32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571, + 30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683, + 27245, 26790, 26319, 25832, 25329, 24811, 24279, 23731, + 23170, 22594, 22005, 21403, 20787, 20159, 19519, 18868, + 18204, 17530, 16846, 16151, 15446, 14732, 14010, 13279, + 12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179, + 6393, 5602, 4808, 4011, 3212, 2410, 1608, 804, + 0, -804, -1608, -2410, -3212, -4011, -4808, -5602, + -6393, -7179, -7962, -8739, -9512, -10278, -11039, -11793, + -12539, -13279, -14010, -14732, -15446, -16151, -16846, -17530, + -18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594, + -23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790, + -27245, -27683, -28105, -28510, -28898, -29268, -29621, -29956, + -30273, -30571, -30852, -31113, -31356, -31580, -31785, -31971, + -32137, -32285, -32412, -32521, -32609, -32678, -32728, -32757, + -32767, -32757, -32728, -32678, -32609, -32521, -32412, -32285, + -32137, -31971, -31785, -31580, -31356, -31113, -30852, -30571, + -30273, -29956, -29621, -29268, -28898, -28510, -28105, -27683, + -27245, -26790, -26319, -25832, -25329, -24811, -24279, -23731, + -23170, -22594, -22005, -21403, -20787, -20159, -19519, -18868, + -18204, -17530, -16846, -16151, -15446, -14732, -14010, -13279, + -12539, -11793, -11039, -10278, -9512, -8739, -7962, -7179, + -6393, -5602, -4808, -4011, -3212, -2410, -1608, -804, +}; + +// MIDI note number to frequency (Hz * 256) lookup table for notes 0-127 +// Stored as 16.8 fixed-point: freq_hz_x256 = round(440 * 2^((note-69)/12) * 256) +// We use uint32_t to hold values for higher notes +static const uint32_t s_midi_freq_x256[128] = { + 2093, 2217, 2349, 2489, 2637, 2794, + 2960, 3136, 3322, 3520, 3729, 3951, + 4186, 4435, 4699, 4978, 5274, 5588, + 5920, 6272, 6645, 7040, 7459, 7902, + 8372, 8870, 9397, 9956, 10548, 11175, + 11840, 12544, 13290, 14080, 14917, 15804, + 16744, 17740, 18795, 19912, 21096, 22351, + 23680, 25088, 26580, 28160, 29834, 31609, + 33488, 35479, 37589, 39824, 42192, 44701, + 47359, 50175, 53159, 56320, 59669, 63217, + 66976, 70959, 75178, 79649, 84385, 89402, + 94719, 100351, 106318, 112640, 119338, 126434, + 133952, 141918, 150356, 159297, 168769, 178805, + 189437, 200702, 212636, 225280, 238676, 252868, + 267905, 283835, 300713, 318594, 337539, 357610, + 378874, 401403, 425272, 450560, 477352, 505737, + 535809, 567670, 601425, 637188, 675077, 715219, + 757749, 802807, 850544, 901120, 954703, 1011473, + 1071618, 1135340, 1202851, 1274376, 1350154, 1430439, + 1515497, 1605613, 1701088, 1802240, 1909407, 2022946, + 2143237, 2270680, 2405702, 2548752, 2700309, 2860878, + 3030994, 3211227, +}; + +static uint32_t s_sample_rate; + +uint32_t note_midi_freq_x256(uint8_t midi_note) { + if (midi_note >= 128) { + return 0; + } + return s_midi_freq_x256[midi_note]; +} + +uint32_t note_phase_inc(uint8_t midi_note, uint32_t sample_rate) { + if (midi_note == 0 || midi_note > 127 || sample_rate == 0) { + return 0; + } + // phase_inc = freq_x256 * 256 / sample_rate (16.16 per output sample) + return (s_midi_freq_x256[midi_note] << 8) / sample_rate; +} + +//! PolyBLEP correction value at phase t with per-sample phase increment dt. +//! Both are in 16.0 phase units where one cycle = 65536. Returns a Q15 value +//! (32768 = 1.0). Caller must ensure dt > 0 and dt < 32768. For phases more +//! than dt away from any discontinuity the correction is zero. +static int32_t prv_polyblep_q15(uint32_t t, uint32_t dt) { + if (t < dt) { + // Trailing side of the discontinuity: tp = t/dt in [0, 1) + // Correction = 2*tp - tp^2 - 1, in [-1, 0) + uint32_t tp = (t << 15) / dt; // Q15 in [0, 32768) + int32_t tp_s = (int32_t)tp; + int32_t tp_sq = (tp_s * tp_s) >> 15; + return (tp_s << 1) - tp_sq - 32768; + } + if (t + dt > 65536) { + // Leading side of the next discontinuity: u = (1 - t)/dt in (0, 1] + // Correction = (1 - u)^2, in [0, 1) + uint32_t u = ((65536u - t) << 15) / dt; // Q15 in (0, 32768] + int32_t one_minus_u = 32768 - (int32_t)u; + return (one_minus_u * one_minus_u) >> 15; + } + return 0; +} + +int16_t note_synth_sample(uint8_t waveform, uint32_t phase_acc, uint32_t phase_inc, + uint8_t velocity) { + uint8_t phase_idx = (phase_acc >> 8) & 0xFF; + int32_t sample = 0; + + switch (waveform) { + case SpeakerWaveformSine: + sample = s_sine_lut[phase_idx]; + break; + + case SpeakerWaveformSquare: { + // Naive square plus PolyBLEP corrections at the rising edge (t=0) and + // falling edge (t=0.5) to suppress the aliasing that otherwise mangles + // every frequency where the half-period isn't an integer number of + // output samples. + uint32_t t = phase_acc & 0xFFFFu; + uint32_t dt = phase_inc & 0xFFFFu; + sample = (t < 32768u) ? 32767 : -32767; + // Correction is meaningless at and above Nyquist; skip rather than + // generate nonsense when the caller asked for an out-of-range frequency. + if (dt > 0 && dt < 32768u) { + sample += prv_polyblep_q15(t, dt); + sample -= prv_polyblep_q15(t ^ 0x8000u, dt); + if (sample > 32767) sample = 32767; + else if (sample < -32768) sample = -32768; + } + break; + } + + case SpeakerWaveformTriangle: + if (phase_idx < 64) { + sample = (int32_t)phase_idx * 32767 / 64; + } else if (phase_idx < 192) { + sample = (128 - (int32_t)phase_idx) * 32767 / 64; + } else { + sample = ((int32_t)phase_idx - 256) * 32767 / 64; + } + break; + + case SpeakerWaveformSawtooth: + sample = ((int32_t)phase_idx - 128) * 32767 / 128; + break; + + default: + sample = 0; + break; + } + + // Equal-loudness normalization: scale each waveform to roughly the RMS + // that triangle and sawtooth naturally produce at peak ±full-scale, so + // swapping waveforms at the same velocity produces roughly the same + // perceived loudness. Q15 multipliers, 32768 = unity. + // Square loses 4.8 dB (1/sqrt(3)); sine loses 3 dB (sqrt(2/3)); + // triangle and sawtooth are unchanged. + static const uint16_t s_loudness_q15[SpeakerWaveformCount] = { + [SpeakerWaveformSine] = 26755, + [SpeakerWaveformSquare] = 18918, + [SpeakerWaveformTriangle] = 32768, + [SpeakerWaveformSawtooth] = 32768, + }; + if (waveform < SpeakerWaveformCount) { + sample = (sample * s_loudness_q15[waveform]) >> 15; + } + + if (velocity > 0 && velocity < 127) { + sample = (sample * velocity) / 127; + } + + return (int16_t)sample; +} + +static void prv_advance_to_note(NoteSequenceState *s) { + if (s->current_note >= s->num_notes) { + s->active = false; + return; + } + + const SpeakerNote *note = &s->notes[s->current_note]; + s->samples_remaining = ((uint32_t)note->duration_ms * s_sample_rate) / 1000; + s->current_waveform = note->waveform; + s->current_velocity = note->velocity; + s->phase_acc = 0; + s->phase_inc = note_phase_inc(note->midi_note, s_sample_rate); +} + +static int16_t prv_generate_sample(NoteSequenceState *s) { + if (s->phase_inc == 0) { + return 0; + } + int16_t sample = note_synth_sample(s->current_waveform, s->phase_acc, + s->phase_inc, s->current_velocity); + s->phase_acc += s->phase_inc; + return sample; +} + +void note_seq_init(NoteSequenceState *s, const SpeakerNote *notes, uint32_t count, + uint32_t sample_rate) { + memset(s, 0, sizeof(*s)); + s->notes = notes; + s->num_notes = count; + s->active = (count > 0); + s_sample_rate = sample_rate; + + if (s->active) { + prv_advance_to_note(s); + } +} + +uint32_t note_seq_fill(NoteSequenceState *s, int16_t *out, uint32_t max_samples) { + uint32_t written = 0; + + while (written < max_samples && s->active) { + if (s->samples_remaining == 0) { + s->current_note++; + prv_advance_to_note(s); + continue; + } + + uint32_t to_generate = max_samples - written; + if (to_generate > s->samples_remaining) { + to_generate = s->samples_remaining; + } + + for (uint32_t i = 0; i < to_generate; i++) { + out[written + i] = prv_generate_sample(s); + } + + written += to_generate; + s->samples_remaining -= to_generate; + } + + return written; +} + +void note_seq_deinit(NoteSequenceState *s) { + memset(s, 0, sizeof(*s)); +} diff --git a/src/fw/services/speaker/pcm_stream.c b/src/fw/services/speaker/pcm_stream.c new file mode 100644 index 0000000000..315ff1394f --- /dev/null +++ b/src/fw/services/speaker/pcm_stream.c @@ -0,0 +1,81 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pcm_stream.h" + +#include "kernel/pbl_malloc.h" + +#include + +bool pcm_stream_init(PcmStreamState *s, uint32_t size_bytes) { + memset(s, 0, sizeof(*s)); + s->buffer = kernel_malloc(size_bytes); + if (!s->buffer) { + return false; + } + s->size = size_bytes; + return true; +} + +uint32_t pcm_stream_write(PcmStreamState *s, const void *data, uint32_t n) { + if (!s->buffer || s->closing) { + return 0; + } + + uint32_t space = s->size - s->count; + if (n > space) { + n = space; + } + + const uint8_t *src = data; + for (uint32_t i = 0; i < n; i++) { + s->buffer[s->write_pos] = src[i]; + s->write_pos = (s->write_pos + 1) % s->size; + } + s->count += n; + + return n; +} + +uint32_t pcm_stream_read(PcmStreamState *s, void *out, uint32_t max) { + if (!s->buffer) { + return 0; + } + + uint32_t avail = s->count; + if (max > avail) { + max = avail; + } + + uint8_t *dst = out; + for (uint32_t i = 0; i < max; i++) { + dst[i] = s->buffer[s->read_pos]; + s->read_pos = (s->read_pos + 1) % s->size; + } + s->count -= max; + + return max; +} + +void pcm_stream_mark_closing(PcmStreamState *s) { + s->closing = true; +} + +bool pcm_stream_is_done(PcmStreamState *s) { + return s->closing && s->count == 0; +} + +void pcm_stream_deinit(PcmStreamState *s) { + if (s->buffer) { + kernel_free(s->buffer); + } + memset(s, 0, sizeof(*s)); +} + +uint32_t pcm_stream_available(const PcmStreamState *s) { + return s->count; +} + +uint32_t pcm_stream_free_space(const PcmStreamState *s) { + return s->size - s->count; +} diff --git a/src/fw/services/speaker/pcm_stream.h b/src/fw/services/speaker/pcm_stream.h new file mode 100644 index 0000000000..9f6d538b97 --- /dev/null +++ b/src/fw/services/speaker/pcm_stream.h @@ -0,0 +1,53 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include + +#define PCM_STREAM_DEFAULT_SIZE_BYTES 8192 // 8KB + +typedef struct { + uint8_t *buffer; + uint32_t size; // total capacity in bytes + uint32_t read_pos; + uint32_t write_pos; + uint32_t count; // number of bytes currently buffered + bool closing; // no more writes expected, drain remaining +} PcmStreamState; + +//! Initialize a PCM stream ring buffer. +//! @param s State to initialize +//! @param size_bytes Ring buffer capacity in bytes +//! @return true on success, false if allocation failed +bool pcm_stream_init(PcmStreamState *s, uint32_t size_bytes); + +//! Write data into the ring buffer. +//! @param s Stream state +//! @param data Source bytes +//! @param n Number of bytes to write +//! @return Number of bytes actually written (may be less if buffer is full) +uint32_t pcm_stream_write(PcmStreamState *s, const void *data, uint32_t n); + +//! Read data from the ring buffer. +//! @param s Stream state +//! @param out Destination buffer +//! @param max Maximum number of bytes to read +//! @return Number of bytes actually read +uint32_t pcm_stream_read(PcmStreamState *s, void *out, uint32_t max); + +//! Mark the stream as closing (no more writes). Playback continues until drained. +void pcm_stream_mark_closing(PcmStreamState *s); + +//! Check if the stream is done (closing and fully drained). +bool pcm_stream_is_done(PcmStreamState *s); + +//! Free resources associated with the stream. +void pcm_stream_deinit(PcmStreamState *s); + +//! Get the number of bytes available for reading. +uint32_t pcm_stream_available(const PcmStreamState *s); + +//! Get the number of bytes of free space for writing. +uint32_t pcm_stream_free_space(const PcmStreamState *s); diff --git a/src/fw/services/speaker/speaker_service.c b/src/fw/services/speaker/speaker_service.c new file mode 100644 index 0000000000..58cd3e6d45 --- /dev/null +++ b/src/fw/services/speaker/speaker_service.c @@ -0,0 +1,819 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/speaker/speaker_service.h" + +#ifdef CONFIG_SPEAKER + +#include "pbl/services/speaker/note_sequence.h" +#include "pcm_stream.h" +#include "track_player.h" + +#include "drivers/audio.h" +#include "drivers/rtc.h" +#include "board/board.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "os/mutex.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/system_task.h" +#include "system/logging.h" +#include "system/passert.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_speaker, CONFIG_SERVICE_SPEAKER_LOG_LEVEL); + +#define SPEAKER_SAMPLE_RATE 16000 +#define SPEAKER_REFILL_SAMPLES 512 + +typedef struct { + SpeakerState state; + SpeakerSourceType source_type; + SpeakerPriority priority; + PebbleTask owner_task; + uint8_t volume; + + // Note sequence source + NoteSequenceState note_seq; + SpeakerNote *note_buf; // kernel_malloc'd copy of notes + + // Single tone source (raw frequency, no MIDI quantization) + uint32_t tone_samples_remaining; + uint32_t tone_phase_acc; // 16.16 fixed-point + uint32_t tone_phase_inc; // per-sample phase increment + uint8_t tone_waveform; + uint8_t tone_velocity; + + // PCM stream source + PcmStreamState pcm_stream; + SpeakerPcmFormat pcm_format; + + // Previous decoded samples for cubic interpolation across chunk boundaries. + // [0] = second-to-last sample (s_{n-2}), [1] = last sample (s_{n-1}). + int16_t prev_samples[2]; + + // Temporary buffer for reading raw PCM data before format conversion + uint8_t raw_buf[1024]; + + // Refill buffer (avoids repeated small allocations) + int16_t refill_buf[SPEAKER_REFILL_SAMPLES]; + + // Polyphonic tracks source + TrackState tracks[SPEAKER_MAX_TRACKS]; + uint32_t num_tracks; + SpeakerNote *track_notes_buf[SPEAKER_MAX_TRACKS]; + SpeakerSample track_samples[SPEAKER_MAX_TRACKS]; + void *track_sample_data[SPEAKER_MAX_TRACKS]; + int32_t mix_buf[SPEAKER_REFILL_SAMPLES]; + int16_t track_scratch[SPEAKER_REFILL_SAMPLES]; + + // Finish-event delivery + bool finish_enabled; + PebbleTask finish_task; + + bool initialized; +} SpeakerServiceState; + +static SpeakerServiceState s_state; + +// Serializes public APIs against prv_refill_bg (system task). +static PebbleMutex *s_lock; + +//! Analytics: time-weighted average volume, reset on heartbeat. +static uint64_t s_volume_time_product_sum; // Sum of (volume_pct × time_ms) +static RtcTicks s_last_volume_sample_ticks; // Timestamp of last sample +static uint8_t s_last_sampled_volume_pct; // Last volume percentage sampled +static uint32_t s_total_speaker_on_time_ms; // Total speaker on-time tracked + +static void prv_stop_internal(SpeakerFinishReason reason); +static void prv_audio_trans_cb(uint32_t *free_size); +static void prv_refill_bg(void *data); + +static bool prv_is_speaker_muted(void) { + if (alerts_preferences_get_speaker_muted()) { + return true; + } + if (alerts_preferences_dnd_get_mute_speaker() && do_not_disturb_is_active()) { + return true; + } + return false; +} + +static uint8_t prv_effective_volume(uint8_t vol) { + if (prv_is_speaker_muted()) { + return 0; + } + const uint8_t cap = alerts_preferences_get_speaker_volume(); + return (uint32_t)vol * cap / 100; +} + +static void prv_update_volume_analytics(uint8_t new_volume_pct) { + RtcTicks now_ticks = rtc_get_ticks(); + + if (s_last_volume_sample_ticks > 0) { + uint32_t time_delta_ms = ((now_ticks - s_last_volume_sample_ticks) * 1000) / RTC_TICKS_HZ; + + s_volume_time_product_sum += (uint64_t)s_last_sampled_volume_pct * time_delta_ms; + + if (s_last_sampled_volume_pct > 0) { + s_total_speaker_on_time_ms += time_delta_ms; + } + } + + s_last_volume_sample_ticks = now_ticks; + s_last_sampled_volume_pct = new_volume_pct; +} + +void speaker_service_init(void) { + s_lock = mutex_create(); + + memset(&s_state, 0, sizeof(s_state)); + s_state.state = SpeakerStateIdle; + s_state.source_type = SpeakerSourceNone; + s_state.owner_task = PebbleTask_Unknown; + s_state.initialized = true; + + s_volume_time_product_sum = 0; + s_last_volume_sample_ticks = 0; + s_last_sampled_volume_pct = 0; + s_total_speaker_on_time_ms = 0; +} + +static void prv_start_audio(uint8_t vol) { + const uint8_t effective_vol = prv_effective_volume(vol); + + PBL_ANALYTICS_TIMER_START(speaker_on_time_ms); + PBL_ANALYTICS_ADD(speaker_play_count, 1); + prv_update_volume_analytics(effective_vol); + + audio_init((AudioDevice *)AUDIO); + audio_set_volume((AudioDevice *)AUDIO, effective_vol); + audio_start((AudioDevice *)AUDIO, prv_audio_trans_cb); +} + +static void prv_stop_audio(void) { + PBL_ANALYTICS_TIMER_STOP(speaker_on_time_ms); + prv_update_volume_analytics(0); + + audio_stop((AudioDevice *)AUDIO); +} + +static void prv_free_tracks(void) { + for (uint32_t i = 0; i < s_state.num_tracks; i++) { + track_deinit(&s_state.tracks[i]); + if (s_state.track_notes_buf[i]) { + kernel_free(s_state.track_notes_buf[i]); + s_state.track_notes_buf[i] = NULL; + } + if (s_state.track_sample_data[i]) { + kernel_free(s_state.track_sample_data[i]); + s_state.track_sample_data[i] = NULL; + } + } + s_state.num_tracks = 0; +} + +static void prv_post_finish_event(SpeakerFinishReason reason) { + if (!s_state.finish_enabled) { + return; + } + PebbleEvent e = { + .type = PEBBLE_SPEAKER_EVENT, + .speaker = { + .type = SpeakerEventFinished, + .finish_reason = (uint8_t)reason, + }, + }; + event_put(&e); +} + +//! Caller must hold s_lock. +static void prv_stop_internal(SpeakerFinishReason reason) { + if (s_state.state == SpeakerStateIdle) { + return; + } + const SpeakerSourceType source_type = s_state.source_type; + s_state.state = SpeakerStateIdle; + s_state.source_type = SpeakerSourceNone; + s_state.owner_task = PebbleTask_Unknown; + + if (reason == SpeakerFinishReasonPreempted) { + PBL_ANALYTICS_ADD(speaker_preempted_count, 1); + } + + prv_stop_audio(); + + if (source_type == SpeakerSourceNoteSeq) { + note_seq_deinit(&s_state.note_seq); + if (s_state.note_buf) { + kernel_free(s_state.note_buf); + s_state.note_buf = NULL; + } + } else if (source_type == SpeakerSourceStream) { + pcm_stream_deinit(&s_state.pcm_stream); + } else if (source_type == SpeakerSourceTracks) { + prv_free_tracks(); + } else if (source_type == SpeakerSourceTone) { + s_state.tone_samples_remaining = 0; + s_state.tone_phase_inc = 0; + } + + PBL_LOG_DBG("Speaker stopped (reason=%d)", reason); + + prv_post_finish_event(reason); +} + +static bool prv_can_preempt(SpeakerPriority new_pri) { + if (s_state.state == SpeakerStateIdle) { + return true; + } + return new_pri > s_state.priority; +} + +//! Called from system task context when the audio driver needs more data. +//! This is the DMA refill callback path: +//! DMA ISR -> system_task_add_callback_from_isr -> audio driver trans_cb -> here +static void prv_audio_trans_cb(uint32_t *free_size) { + // Schedule actual refill work on system task to keep ISR-context callback short + system_task_add_callback(prv_refill_bg, NULL); +} + +//! Convert a raw sample from the input buffer to 16-bit signed. +static int16_t prv_decode_sample(const uint8_t *raw, uint32_t index, bool is_16bit) { + if (is_16bit) { + return (int16_t)(raw[index * 2] | (raw[index * 2 + 1] << 8)); + } + // 8-bit signed -> 16-bit signed: scale [-128,127] to [-32768,32512] + return (int16_t)((int8_t)raw[index]) << 8; +} + +//! 4-tap cubic midpoint interpolation for 2x upsampling. +//! Uses the halfband polyphase filter: (-s0 + 9*s1 + 9*s2 - s3) / 16 +//! This produces a smooth curve through all original sample points with +//! continuous first derivatives, unlike linear interpolation. +static inline int16_t prv_cubic_midpoint(int16_t s0, int16_t s1, int16_t s2, int16_t s3) { + int32_t v = -(int32_t)s0 + 9 * (int32_t)s1 + 9 * (int32_t)s2 - (int32_t)s3; + // Clamp to int16_t range before dividing + v = (v + 8) >> 4; // divide by 16 with rounding + if (v > 32767) v = 32767; + if (v < -32768) v = -32768; + return (int16_t)v; +} + +//! Read raw PCM data from the stream and convert to 16kHz 16-bit output. +//! For 8kHz input, uses 4-tap cubic interpolation between consecutive samples +//! for smooth upsampling without staircase artifacts. Maintains state across +//! refill calls via prev_samples[] for seamless chunk boundaries. +//! @param out Output buffer for 16-bit samples +//! @param max_out_samples Maximum number of output samples to produce +//! @return Number of output samples written +static uint32_t prv_read_and_convert_pcm(int16_t *out, uint32_t max_out_samples) { + SpeakerPcmFormat fmt = s_state.pcm_format; + bool is_16khz = (fmt & 1); + bool is_16bit = (fmt & 2); + + // Calculate how many input bytes we need for max_out_samples output samples + // Output is always 16kHz 16-bit + uint32_t input_samples_needed = is_16khz ? max_out_samples : (max_out_samples / 2); + uint32_t bytes_per_sample = is_16bit ? 2 : 1; + uint32_t input_bytes_needed = input_samples_needed * bytes_per_sample; + + // Clamp to raw_buf size + if (input_bytes_needed > sizeof(s_state.raw_buf)) { + input_bytes_needed = sizeof(s_state.raw_buf); + input_samples_needed = input_bytes_needed / bytes_per_sample; + } + + uint32_t bytes_read = pcm_stream_read(&s_state.pcm_stream, s_state.raw_buf, + input_bytes_needed); + if (bytes_read == 0) { + return 0; + } + + uint32_t samples_read = bytes_read / bytes_per_sample; + uint32_t out_pos = 0; + + if (is_16khz) { + // No upsampling needed — just convert bit depth + for (uint32_t i = 0; i < samples_read; i++) { + out[out_pos++] = prv_decode_sample(s_state.raw_buf, i, is_16bit); + } + // Track last two samples for potential future use + if (samples_read >= 2) { + s_state.prev_samples[0] = out[out_pos - 2]; + s_state.prev_samples[1] = out[out_pos - 1]; + } else if (samples_read == 1) { + s_state.prev_samples[0] = s_state.prev_samples[1]; + s_state.prev_samples[1] = out[out_pos - 1]; + } + } else { + // 8kHz -> 16kHz: 4-tap cubic interpolation + // For each input sample, output the sample itself plus a cubic-interpolated + // midpoint using 4 surrounding points: s[i-1], s[i], s[i+1], s[i+2] + // prev_samples[] provides the history across chunk boundaries. + for (uint32_t i = 0; i < samples_read; i++) { + int16_t s_prev = (i >= 1) ? prv_decode_sample(s_state.raw_buf, i - 1, is_16bit) + : s_state.prev_samples[1]; + int16_t s_curr = prv_decode_sample(s_state.raw_buf, i, is_16bit); + int16_t s_next = (i + 1 < samples_read) + ? prv_decode_sample(s_state.raw_buf, i + 1, is_16bit) + : s_curr; + int16_t s_next2 = (i + 2 < samples_read) + ? prv_decode_sample(s_state.raw_buf, i + 2, is_16bit) + : s_next; + + out[out_pos++] = s_curr; + out[out_pos++] = prv_cubic_midpoint(s_prev, s_curr, s_next, s_next2); + } + + // Save last two decoded samples for next chunk's interpolation + if (samples_read >= 2) { + s_state.prev_samples[0] = prv_decode_sample(s_state.raw_buf, samples_read - 2, + is_16bit); + s_state.prev_samples[1] = prv_decode_sample(s_state.raw_buf, samples_read - 1, + is_16bit); + } else if (samples_read == 1) { + s_state.prev_samples[0] = s_state.prev_samples[1]; + s_state.prev_samples[1] = prv_decode_sample(s_state.raw_buf, 0, is_16bit); + } + } + + return out_pos; +} + +//! Caller must hold s_lock. +static void prv_refill_locked(void) { + if (s_state.state == SpeakerStateIdle) { + return; + } + + uint32_t samples_generated = 0; + + if (s_state.source_type == SpeakerSourceNoteSeq) { + samples_generated = note_seq_fill(&s_state.note_seq, s_state.refill_buf, + SPEAKER_REFILL_SAMPLES); + if (samples_generated == 0) { + prv_stop_internal(SpeakerFinishReasonDone); + return; + } + } else if (s_state.source_type == SpeakerSourceStream) { + samples_generated = prv_read_and_convert_pcm(s_state.refill_buf, + SPEAKER_REFILL_SAMPLES); + if (samples_generated == 0 && pcm_stream_is_done(&s_state.pcm_stream)) { + prv_stop_internal(SpeakerFinishReasonDone); + return; + } + // If no data but not done, write silence to keep DMA fed + if (samples_generated == 0) { + PBL_ANALYTICS_ADD(speaker_stream_underrun_count, 1); + memset(s_state.refill_buf, 0, SPEAKER_REFILL_SAMPLES * sizeof(int16_t)); + samples_generated = SPEAKER_REFILL_SAMPLES; + } + } else if (s_state.source_type == SpeakerSourceTone) { + uint32_t to_gen = s_state.tone_samples_remaining; + if (to_gen > SPEAKER_REFILL_SAMPLES) { + to_gen = SPEAKER_REFILL_SAMPLES; + } + if (to_gen == 0) { + prv_stop_internal(SpeakerFinishReasonDone); + return; + } + if (s_state.tone_phase_inc == 0) { + memset(s_state.refill_buf, 0, to_gen * sizeof(int16_t)); + } else { + for (uint32_t i = 0; i < to_gen; i++) { + s_state.refill_buf[i] = note_synth_sample(s_state.tone_waveform, + s_state.tone_phase_acc, + s_state.tone_phase_inc, + s_state.tone_velocity); + s_state.tone_phase_acc += s_state.tone_phase_inc; + } + } + s_state.tone_samples_remaining -= to_gen; + samples_generated = to_gen; + } else if (s_state.source_type == SpeakerSourceTracks) { + memset(s_state.mix_buf, 0, sizeof(int32_t) * SPEAKER_REFILL_SAMPLES); + uint32_t max_generated = 0; + for (uint32_t i = 0; i < s_state.num_tracks; i++) { + uint32_t n = track_fill(&s_state.tracks[i], s_state.track_scratch, + SPEAKER_REFILL_SAMPLES); + for (uint32_t j = 0; j < n; j++) { + s_state.mix_buf[j] += s_state.track_scratch[j]; + } + if (n > max_generated) { + max_generated = n; + } + } + if (max_generated == 0) { + prv_stop_internal(SpeakerFinishReasonDone); + return; + } + for (uint32_t j = 0; j < max_generated; j++) { + int32_t v = s_state.mix_buf[j]; + if (v > 32767) v = 32767; + else if (v < -32768) v = -32768; + s_state.refill_buf[j] = (int16_t)v; + } + samples_generated = max_generated; + } + + if (samples_generated > 0) { + audio_write((AudioDevice *)AUDIO, s_state.refill_buf, + samples_generated * sizeof(int16_t)); + } +} + +static void prv_refill_bg(void *data) { + mutex_lock(s_lock); + prv_refill_locked(); + mutex_unlock(s_lock); +} + +bool speaker_service_play_note_seq(const SpeakerNote *notes, uint32_t num_notes, + SpeakerPriority pri, uint8_t vol) { + mutex_lock(s_lock); + + if (!s_state.initialized || !notes || num_notes == 0) { + mutex_unlock(s_lock); + return false; + } + + if (!prv_can_preempt(pri)) { + mutex_unlock(s_lock); + return false; + } + + // Stop any existing playback + if (s_state.state != SpeakerStateIdle) { + prv_stop_internal(SpeakerFinishReasonPreempted); + } + + // Copy notes to kernel memory so they persist + size_t notes_size = num_notes * sizeof(SpeakerNote); + s_state.note_buf = kernel_malloc(notes_size); + if (!s_state.note_buf) { + PBL_LOG_ERR("Failed to allocate note buffer"); + mutex_unlock(s_lock); + return false; + } + memcpy(s_state.note_buf, notes, notes_size); + + note_seq_init(&s_state.note_seq, s_state.note_buf, num_notes, SPEAKER_SAMPLE_RATE); + + s_state.state = SpeakerStatePlaying; + s_state.source_type = SpeakerSourceNoteSeq; + s_state.priority = pri; + s_state.volume = vol; + + prv_start_audio(vol); + + // Prime the audio buffer with initial data + prv_refill_locked(); + + mutex_unlock(s_lock); + return true; +} + +bool speaker_service_play_tone(uint16_t freq_hz, uint16_t duration_ms, + uint8_t waveform, uint8_t velocity, + SpeakerPriority pri, uint8_t vol) { + mutex_lock(s_lock); + + if (!s_state.initialized || duration_ms == 0) { + mutex_unlock(s_lock); + return false; + } + + if (!prv_can_preempt(pri)) { + mutex_unlock(s_lock); + return false; + } + + if (s_state.state != SpeakerStateIdle) { + prv_stop_internal(SpeakerFinishReasonPreempted); + } + + s_state.tone_samples_remaining = + ((uint32_t)duration_ms * SPEAKER_SAMPLE_RATE) / 1000; + s_state.tone_phase_acc = 0; + // phase_inc = freq_hz * 65536 / sample_rate (16.16 fixed-point per sample) + s_state.tone_phase_inc = (freq_hz != 0) + ? ((uint32_t)freq_hz * 65536u) / SPEAKER_SAMPLE_RATE : 0; + s_state.tone_waveform = waveform; + s_state.tone_velocity = velocity; + + s_state.state = SpeakerStatePlaying; + s_state.source_type = SpeakerSourceTone; + s_state.priority = pri; + s_state.volume = vol; + + prv_start_audio(vol); + prv_refill_locked(); + + mutex_unlock(s_lock); + return true; +} + +bool speaker_service_play_tracks(const SpeakerTrack *tracks, uint32_t num_tracks, + SpeakerPriority pri, uint8_t vol) { + mutex_lock(s_lock); + + if (!s_state.initialized || !tracks || num_tracks == 0 || + num_tracks > SPEAKER_MAX_TRACKS) { + mutex_unlock(s_lock); + return false; + } + + uint32_t total_sample_bytes = 0; + for (uint32_t i = 0; i < num_tracks; i++) { + const SpeakerTrack *t = &tracks[i]; + if (!t->notes || t->num_notes == 0) { + mutex_unlock(s_lock); + return false; + } + if (t->sample) { + if (!t->sample->data || t->sample->num_bytes == 0) { + mutex_unlock(s_lock); + return false; + } + total_sample_bytes += t->sample->num_bytes; + if (total_sample_bytes > SPEAKER_MAX_SAMPLE_BYTES_TOTAL) { + mutex_unlock(s_lock); + return false; + } + } + } + + if (!prv_can_preempt(pri)) { + mutex_unlock(s_lock); + return false; + } + + if (s_state.state != SpeakerStateIdle) { + prv_stop_internal(SpeakerFinishReasonPreempted); + } + + // Kernel-copy each track's notes, sample struct, and sample data. Build a + // local array of SpeakerTrack pointing at the kernel copies. + SpeakerTrack kernel_tracks[SPEAKER_MAX_TRACKS]; + memset(kernel_tracks, 0, sizeof(kernel_tracks)); + + for (uint32_t i = 0; i < num_tracks; i++) { + const SpeakerTrack *t = &tracks[i]; + size_t notes_size = (size_t)t->num_notes * sizeof(SpeakerNote); + s_state.track_notes_buf[i] = kernel_malloc(notes_size); + if (!s_state.track_notes_buf[i]) { + PBL_LOG_ERR("Failed to allocate track notes"); + goto alloc_fail; + } + memcpy(s_state.track_notes_buf[i], t->notes, notes_size); + + kernel_tracks[i].notes = s_state.track_notes_buf[i]; + kernel_tracks[i].num_notes = t->num_notes; + + if (t->sample) { + s_state.track_sample_data[i] = kernel_malloc(t->sample->num_bytes); + if (!s_state.track_sample_data[i]) { + PBL_LOG_ERR("Failed to allocate sample data"); + goto alloc_fail; + } + memcpy(s_state.track_sample_data[i], t->sample->data, t->sample->num_bytes); + + s_state.track_samples[i] = *t->sample; + s_state.track_samples[i].data = s_state.track_sample_data[i]; + kernel_tracks[i].sample = &s_state.track_samples[i]; + } + } + + s_state.num_tracks = num_tracks; + for (uint32_t i = 0; i < num_tracks; i++) { + track_init(&s_state.tracks[i], &kernel_tracks[i], SPEAKER_SAMPLE_RATE); + } + + s_state.state = SpeakerStatePlaying; + s_state.source_type = SpeakerSourceTracks; + s_state.priority = pri; + s_state.volume = vol; + + prv_start_audio(vol); + prv_refill_locked(); + + mutex_unlock(s_lock); + return true; + +alloc_fail: + for (uint32_t i = 0; i < num_tracks; i++) { + if (s_state.track_notes_buf[i]) { + kernel_free(s_state.track_notes_buf[i]); + s_state.track_notes_buf[i] = NULL; + } + if (s_state.track_sample_data[i]) { + kernel_free(s_state.track_sample_data[i]); + s_state.track_sample_data[i] = NULL; + } + } + mutex_unlock(s_lock); + return false; +} + +bool speaker_service_stream_open(SpeakerPriority pri, uint8_t vol, SpeakerPcmFormat fmt) { + mutex_lock(s_lock); + + if (!s_state.initialized) { + mutex_unlock(s_lock); + return false; + } + + if (!prv_can_preempt(pri)) { + mutex_unlock(s_lock); + return false; + } + + // Stop any existing playback + if (s_state.state != SpeakerStateIdle) { + prv_stop_internal(SpeakerFinishReasonPreempted); + } + + if (!pcm_stream_init(&s_state.pcm_stream, PCM_STREAM_DEFAULT_SIZE_BYTES)) { + PBL_LOG_ERR("Failed to allocate PCM stream buffer"); + mutex_unlock(s_lock); + return false; + } + + s_state.state = SpeakerStatePlaying; + s_state.source_type = SpeakerSourceStream; + s_state.priority = pri; + s_state.volume = vol; + s_state.pcm_format = fmt; + s_state.prev_samples[0] = 0; + s_state.prev_samples[1] = 0; + + prv_start_audio(vol); + + mutex_unlock(s_lock); + return true; +} + +uint32_t speaker_service_stream_write(const void *data, uint32_t num_bytes) { + mutex_lock(s_lock); + + if (s_state.state == SpeakerStateIdle || + s_state.source_type != SpeakerSourceStream) { + mutex_unlock(s_lock); + return 0; + } + + uint32_t written = pcm_stream_write(&s_state.pcm_stream, data, num_bytes); + mutex_unlock(s_lock); + return written; +} + +void speaker_service_stream_close(void) { + mutex_lock(s_lock); + + if (s_state.source_type != SpeakerSourceStream) { + mutex_unlock(s_lock); + return; + } + + if (s_state.pcm_stream.count > 0) { + // Data remaining - enter draining state + pcm_stream_mark_closing(&s_state.pcm_stream); + s_state.state = SpeakerStateDraining; + } else { + prv_stop_internal(SpeakerFinishReasonDone); + } + + mutex_unlock(s_lock); +} + +void speaker_service_stop(void) { + mutex_lock(s_lock); + prv_stop_internal(SpeakerFinishReasonStopped); + mutex_unlock(s_lock); +} + +void speaker_service_set_volume(uint8_t vol) { + mutex_lock(s_lock); + s_state.volume = vol; + if (s_state.state != SpeakerStateIdle) { + const uint8_t effective_vol = prv_effective_volume(vol); + prv_update_volume_analytics(effective_vol); + audio_set_volume((AudioDevice *)AUDIO, effective_vol); + } + mutex_unlock(s_lock); +} + +bool speaker_service_is_muted(void) { + return prv_is_speaker_muted(); +} + +void speaker_service_handle_audio_prefs_changed(void) { + mutex_lock(s_lock); + if (s_state.state == SpeakerStateIdle) { + mutex_unlock(s_lock); + return; + } + const uint8_t effective_vol = prv_effective_volume(s_state.volume); + prv_update_volume_analytics(effective_vol); + audio_set_volume((AudioDevice *)AUDIO, effective_vol); + mutex_unlock(s_lock); +} + +SpeakerState speaker_service_get_state(void) { + return s_state.state; +} + +void speaker_service_stop_for_task(PebbleTask task) { + mutex_lock(s_lock); + if (s_state.state != SpeakerStateIdle && s_state.owner_task == task) { + // App is going away — no one to receive the finish event. + s_state.finish_enabled = false; + prv_stop_internal(SpeakerFinishReasonStopped); + } + if (s_state.finish_task == task) { + s_state.finish_enabled = false; + s_state.finish_task = PebbleTask_Unknown; + } + mutex_unlock(s_lock); +} + +void speaker_service_set_owner_task(PebbleTask task) { + s_state.owner_task = task; +} + +void speaker_service_register_finish(PebbleTask task) { + mutex_lock(s_lock); + s_state.finish_enabled = true; + s_state.finish_task = task; + mutex_unlock(s_lock); +} + +void pbl_analytics_external_collect_speaker_stats(void) { + mutex_lock(s_lock); + + // Capture one final sample to account for time since last volume change. + prv_update_volume_analytics(s_last_sampled_volume_pct); + + uint32_t avg_volume_pct = 0; + if (s_total_speaker_on_time_ms > 0) { + avg_volume_pct = s_volume_time_product_sum / s_total_speaker_on_time_ms; + } + + PBL_ANALYTICS_SET_UNSIGNED(speaker_avg_volume_pct, avg_volume_pct); + + s_volume_time_product_sum = 0; + s_total_speaker_on_time_ms = 0; + s_last_volume_sample_ticks = rtc_get_ticks(); + + mutex_unlock(s_lock); +} + +#else // !CONFIG_SPEAKER + +void speaker_service_init(void) {} + +bool speaker_service_play_note_seq(const SpeakerNote *notes, uint32_t num_notes, + SpeakerPriority pri, uint8_t vol) { + return false; +} + +bool speaker_service_play_tone(uint16_t freq_hz, uint16_t duration_ms, + uint8_t waveform, uint8_t velocity, + SpeakerPriority pri, uint8_t vol) { + return false; +} + +bool speaker_service_play_tracks(const SpeakerTrack *tracks, uint32_t num_tracks, + SpeakerPriority pri, uint8_t vol) { + return false; +} + +bool speaker_service_stream_open(SpeakerPriority pri, uint8_t vol, SpeakerPcmFormat fmt) { + return false; +} + +uint32_t speaker_service_stream_write(const void *data, uint32_t num_bytes) { + return 0; +} + +void speaker_service_stream_close(void) {} +void speaker_service_stop(void) {} +void speaker_service_set_volume(uint8_t vol) {} + +SpeakerState speaker_service_get_state(void) { + return SpeakerStateIdle; +} + +void speaker_service_stop_for_task(PebbleTask task) {} +void speaker_service_set_owner_task(PebbleTask task) {} +void speaker_service_register_finish(PebbleTask task) {} +void pbl_analytics_external_collect_speaker_stats(void) {} + +bool speaker_service_is_muted(void) { return false; } +void speaker_service_handle_audio_prefs_changed(void) {} + +#endif // CONFIG_SPEAKER diff --git a/src/fw/services/speaker/track_player.c b/src/fw/services/speaker/track_player.c new file mode 100644 index 0000000000..36ac646732 --- /dev/null +++ b/src/fw/services/speaker/track_player.c @@ -0,0 +1,159 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "track_player.h" + +#include + +static uint32_t s_sample_rate; + +static uint32_t prv_sample_input_rate(SpeakerPcmFormat fmt) { + return (fmt & 1) ? 16000 : 8000; +} + +static uint32_t prv_sample_bytes_per(SpeakerPcmFormat fmt) { + return (fmt & 2) ? 2 : 1; +} + +static int16_t prv_decode_sample_at(const uint8_t *raw, uint32_t idx, bool is_16bit) { + if (is_16bit) { + return (int16_t)(raw[idx * 2] | (raw[idx * 2 + 1] << 8)); + } + // 8-bit signed to 16-bit signed + return (int16_t)((int8_t)raw[idx]) << 8; +} + +static void prv_reset_sample_stride(TrackState *s, uint8_t target_note) { + const SpeakerSample *smp = s->sample; + uint32_t input_rate = prv_sample_input_rate(smp->format); + uint32_t tgt_x256 = note_midi_freq_x256(target_note); + uint32_t base_x256 = note_midi_freq_x256(smp->base_midi_note); + + s->sample_pos_q32 = 0; + s->sample_exhausted = false; + + if (tgt_x256 == 0 || base_x256 == 0 || s->sample_num_input == 0) { + // Rest or invalid base note: hold silence + s->sample_stride_q32 = 0; + s->sample_exhausted = true; + return; + } + + // stride = (input_rate / output_rate) * (freq_target / freq_base), 32.32 fixed. + // Compute in two 16.16 multiplies to stay within uint64. + uint64_t pitch_q16 = ((uint64_t)tgt_x256 << 16) / base_x256; + uint64_t rate_q16 = ((uint64_t)input_rate << 16) / s_sample_rate; + s->sample_stride_q32 = pitch_q16 * rate_q16; // 32.32 +} + +static void prv_advance_to_note(TrackState *s) { + if (s->current_note >= s->num_notes) { + s->active = false; + return; + } + + const SpeakerNote *note = &s->notes[s->current_note]; + s->samples_remaining = ((uint32_t)note->duration_ms * s_sample_rate) / 1000; + s->current_velocity = note->velocity; + s->phase_acc = 0; + + if (s->sample != NULL) { + prv_reset_sample_stride(s, note->midi_note); + } else { + s->current_waveform = note->waveform; + s->phase_inc = note_phase_inc(note->midi_note, s_sample_rate); + } +} + +static int16_t prv_gen_sample_mode(TrackState *s) { + if (s->sample_exhausted || s->sample_stride_q32 == 0) { + return 0; + } + + uint32_t idx = (uint32_t)(s->sample_pos_q32 >> 32); + if (idx >= s->sample_num_input) { + if (s->sample->loop) { + uint64_t span = (uint64_t)s->sample_num_input << 32; + s->sample_pos_q32 %= span; + idx = (uint32_t)(s->sample_pos_q32 >> 32); + } else { + s->sample_exhausted = true; + return 0; + } + } + + bool is_16bit = (s->sample->format & 2); + int16_t raw_sample = prv_decode_sample_at((const uint8_t *)s->sample->data, + idx, is_16bit); + + s->sample_pos_q32 += s->sample_stride_q32; + + int32_t out = raw_sample; + if (s->current_velocity > 0 && s->current_velocity < 127) { + out = (out * s->current_velocity) / 127; + } + return (int16_t)out; +} + +static int16_t prv_gen_waveform_mode(TrackState *s) { + if (s->phase_inc == 0) { + return 0; + } + int16_t v = note_synth_sample(s->current_waveform, s->phase_acc, s->phase_inc, + s->current_velocity); + s->phase_acc += s->phase_inc; + return v; +} + +void track_init(TrackState *s, const SpeakerTrack *track, uint32_t sample_rate) { + memset(s, 0, sizeof(*s)); + s->notes = track->notes; + s->num_notes = track->num_notes; + s->sample = track->sample; + s_sample_rate = sample_rate; + s->active = (track->num_notes > 0); + + if (s->sample != NULL) { + s->sample_num_input = s->sample->num_bytes / prv_sample_bytes_per(s->sample->format); + } + + if (s->active) { + prv_advance_to_note(s); + } +} + +uint32_t track_fill(TrackState *s, int16_t *out, uint32_t max_samples) { + uint32_t written = 0; + + while (written < max_samples && s->active) { + if (s->samples_remaining == 0) { + s->current_note++; + prv_advance_to_note(s); + continue; + } + + uint32_t to_generate = max_samples - written; + if (to_generate > s->samples_remaining) { + to_generate = s->samples_remaining; + } + + if (s->sample != NULL) { + for (uint32_t i = 0; i < to_generate; i++) { + out[written + i] = prv_gen_sample_mode(s); + } + } else { + for (uint32_t i = 0; i < to_generate; i++) { + out[written + i] = prv_gen_waveform_mode(s); + } + } + + written += to_generate; + s->samples_remaining -= to_generate; + } + + return written; +} + +void track_deinit(TrackState *s) { + memset(s, 0, sizeof(*s)); +} diff --git a/src/fw/services/speaker/track_player.h b/src/fw/services/speaker/track_player.h new file mode 100644 index 0000000000..84d5f72168 --- /dev/null +++ b/src/fw/services/speaker/track_player.h @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/speaker/track.h" + +#include +#include + +//! Per-voice state for a polyphonic track. Plays either waveform-synth notes +//! (when SpeakerTrack::sample is NULL) or pitch-shifted PCM samples. +typedef struct { + const SpeakerNote *notes; + uint32_t num_notes; + const SpeakerSample *sample; + + uint32_t current_note; + uint32_t samples_remaining; // at output sample rate + + // Waveform-mode state (used when sample == NULL) + uint32_t phase_acc; // 16.16 + uint32_t phase_inc; // 16.16 + + // Sample-mode state (used when sample != NULL) + uint64_t sample_pos_q32; // 32.32 position within the PCM source + uint64_t sample_stride_q32; // advance per output sample (32.32) + uint32_t sample_num_input; // total input samples in sample->data + bool sample_exhausted; // reached end and loop=false + + uint8_t current_waveform; + uint8_t current_velocity; + bool active; +} TrackState; + +//! Initialize a track voice. +//! @param s State to initialize +//! @param track Track configuration (notes, optional sample) +//! @param sample_rate Output sample rate in Hz +void track_init(TrackState *s, const SpeakerTrack *track, uint32_t sample_rate); + +//! Generate up to max_samples 16-bit signed output samples for this voice. +//! @return Number of samples actually produced. 0 means the voice has finished. +uint32_t track_fill(TrackState *s, int16_t *out, uint32_t max_samples); + +//! Clean up track state. +void track_deinit(TrackState *s); diff --git a/src/fw/services/speaker/wscript_build b/src/fw/services/speaker/wscript_build new file mode 100644 index 0000000000..52d5f2a66f --- /dev/null +++ b/src/fw/services/speaker/wscript_build @@ -0,0 +1,14 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=[ + 'note_sequence.c', + 'pcm_stream.c', + 'speaker_service.c', + 'track_player.c', + ], + target='services_speaker', +) +bld.env.SERVICES.append('services_speaker') diff --git a/src/fw/services/stationary/Kconfig b/src/fw/services/stationary/Kconfig new file mode 100644 index 0000000000..0303f79e73 --- /dev/null +++ b/src/fw/services/stationary/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_STATIONARY + bool "Stationary" + help + Stationary (idle-detection) service. + +if SERVICE_STATIONARY + +module = SERVICE_STATIONARY +module-str = Stationary +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/stationary/service.c b/src/fw/services/stationary/service.c new file mode 100644 index 0000000000..c7cf543b39 --- /dev/null +++ b/src/fw/services/stationary/service.c @@ -0,0 +1,360 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/stationary.h" + +#include "applib/accel_service_private.h" +#include "applib/battery_state_service.h" +#include "applib/ui/dialogs/dialog_private.h" +#include "applib/ui/dialogs/simple_dialog.h" +#include "comm/bt_lock.h" +#include "drivers/battery.h" +#include "kernel/event_loop.h" +#include "kernel/ui/modals/modal_manager.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/accel_manager.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/runlevel.h" +#include "shell/prefs.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/size.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_stationary, CONFIG_SERVICE_STATIONARY_LOG_LEVEL); + +#define STATIONARY_PEEKING_TIME_MINS 5 +#define STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS 30 +#define STATIONARY_ENABLED_DIALOG_TIMEOUT_MS 1800000 +#define STATIONARY_WELCOME_BACK_DIALOG_TIMEOUT_MS 2000 +#define ACCEL_MAX_IDLE_DELTA 50 + +#define DEBUG_STATIONARY 0 + +//! Used for describing the stationary event reason in analytics +typedef enum { + StationaryAnalyticsEnterNormally, + StationaryAnalyticsEnterFromPeek, + StationaryAnalyticsExitNormally, + StationaryAnalyticsExitToPeek, + StationaryAnalyticsEnterCharging, + StationaryAnalyticsExitCharging, + StationaryAnalyticsEnableStationaryMode, + StationaryAnalyticsDisableStationaryMode +} StationaryAnalytics; + +//! The possible states that the watch can be in regarding stationary mode +typedef enum { + StationaryStateAwake, + StationaryStateStationary, + StationaryStatePeeking, + StationaryStateDisabled, +} StationaryState; + +//! The actions we take upon state transitions +typedef enum { + StationaryActionGoToSleep, + StationaryActionWakeUp, + StationaryActionEnableStationary, + StationaryActionDisableStationary +} StationaryAction; + +static AccelData s_last_accel_data; +static AccelServiceState *s_accel_session; +static EventServiceInfo s_button_event_info; +static RegularTimerInfo s_accel_stationary_timer_info; +static StationaryState s_current_state = StationaryStateDisabled; +static uint8_t s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; +static bool s_stationary_mode_inhibit = true; + +static void prv_handle_action(StationaryAction action); + +// Compute and return the device's delta position to help determine movement as idle. +static uint32_t prv_compute_delta_pos(AccelData *cur_pos, AccelData *last_pos) { + return (abs(last_pos->x - cur_pos->x) + abs(last_pos->y - cur_pos->y) + + abs(last_pos->z - cur_pos->z)); +} + +//! The orientation of the accelerometer is checked every minute. If the orientation +//! has not changed by a significant amount, we consider it as stationary. +static bool prv_update_and_check_accel_is_stationary(void) { + AccelData last_accel_data = s_last_accel_data; + sys_accel_manager_peek(&s_last_accel_data); + return prv_compute_delta_pos(&last_accel_data, &s_last_accel_data) < ACCEL_MAX_IDLE_DELTA; +} + +static bool prv_is_allowed_to_run(void) { + return (stationary_get_enabled() && !s_stationary_mode_inhibit); +} + +static void prv_update_stationary_enabled(void *data) { + if (!battery_is_usb_connected() && prv_is_allowed_to_run()) { + prv_handle_action(StationaryActionEnableStationary); + } else { + prv_handle_action(StationaryActionDisableStationary); + } +} + +void stationary_handle_battery_connection_change_event(void) { + PBL_LOG_D_DBG(DEBUG_STATIONARY, "Stationary mode battery state change event received"); + if (battery_is_usb_connected()) { + } else { + } + prv_update_stationary_enabled(NULL); +} + +//! A movement of the watch will make the watch wake up. In this state, the watch +//! will have a low tap threshold, so it will be sensitive to motion +static void prv_accel_tap_handler(AccelAxisType axis, int32_t direction) { + prv_handle_action(StationaryActionWakeUp); +} + +static void prv_button_down_handler(PebbleEvent *event, void *data) { + prv_handle_action(StationaryActionWakeUp); +} + +//! If the watch is determined to be motionless for 30 minutes, it will go to sleep +static void prv_watch_is_motionless(void) { + // Check if we should enable stationary mode and disabled unneeded features + if (s_stationary_count_down > 0) { + PBL_LOG_D_DBG(DEBUG_STATIONARY, "Countdown to stationary: %d", s_stationary_count_down); + s_stationary_count_down--; + } else { + prv_handle_action(StationaryActionGoToSleep); + } +} + +//! The orientation of the accelerometer is checked every minute. If the orientation has +//! changed by a significant amount, we consider the watch as in motion, and restart the +//! stationary counter +static void prv_watch_is_in_motion(void) { + prv_handle_action(StationaryActionWakeUp); +} + +static void prv_stationary_check_launcher_task_cb(void *unused_data) { + if (prv_update_and_check_accel_is_stationary()) { + prv_watch_is_motionless(); + } else { + prv_watch_is_in_motion(); + } +} + +//! Called every minute to determine whether any motion has occured since the last time +//! the call was made. The current position is updated at this time +static void prv_stationary_check_timer_cb(void *unused_data) { + //! All stationary events need to be handled by kernel main + launcher_task_add_callback(prv_stationary_check_launcher_task_cb, NULL); +} + +bool stationary_get_enabled(void) { + return shell_prefs_get_stationary_enabled(); +} + +void stationary_set_enabled(bool enabled) { + if (enabled == stationary_get_enabled()) { + return; + } + shell_prefs_set_stationary_enabled(enabled); + + if (enabled) { + } else { + } + + launcher_task_add_callback(prv_update_stationary_enabled, NULL); +} + +void stationary_run_level_enable(bool enable) { +#if !STATIONARY_MODE + return; +#endif + + const bool inhibit = !enable; + if (inhibit == s_stationary_mode_inhibit) { + return; + } + s_stationary_mode_inhibit = inhibit; + launcher_task_add_callback(prv_update_stationary_enabled, NULL); +} + +void stationary_wake_up(void) { + if (!prv_is_allowed_to_run()) { + return; + } + prv_handle_action(StationaryActionWakeUp); +} + +static void prv_reset_stationary_counter(void) { + s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; +} + +static void prv_enter_awake_state(void) { + prv_reset_stationary_counter(); + s_current_state = StationaryStateAwake; +} + +//! The accelerometer tap threshold will be set very low, so a small motion will wake +//! the watch back up +static void prv_enter_stationary_state(void) { + PBL_ANALYTICS_TIMER_START(stationary_time_ms); + + PBL_LOG_INFO("Entering stationary"); + services_set_runlevel(RunLevel_Stationary); + + s_accel_session = accel_session_create(); + accel_session_shake_subscribe(s_accel_session, prv_accel_tap_handler); + + accel_enable_high_sensitivity(true); + + s_current_state = StationaryStateStationary; +} + +static void prv_exit_stationary(void) { + accel_enable_high_sensitivity(false); + + accel_session_shake_unsubscribe(s_accel_session); + accel_session_delete(s_accel_session); + + PBL_LOG_INFO("Exiting stationary"); + services_set_runlevel(RunLevel_Normal); + + PBL_ANALYTICS_TIMER_STOP(stationary_time_ms); +} + +static void prv_enter_peek_state(void) { + //! When exiting out of stationary, we aren't certain that this wasn't caused by noise yet + //! we set the counter to a small value in case there is no motion right after + s_stationary_count_down = STATIONARY_PEEKING_TIME_MINS; + prv_exit_stationary(); + s_current_state = StationaryStatePeeking; +} + +static void prv_enter_disabled_state(void) { + event_service_client_unsubscribe(&s_button_event_info); + regular_timer_remove_callback(&s_accel_stationary_timer_info); + s_current_state = StationaryStateDisabled; +} + +static void prv_exit_disabled_state(void) { + s_stationary_count_down = STATIONARY_WAIT_BEFORE_ENGAGING_TIME_MINS; + event_service_client_subscribe(&s_button_event_info); + +#if DEBUG_STATIONARY + regular_timer_add_seconds_callback(&s_accel_stationary_timer_info); +#else + regular_timer_add_minutes_callback(&s_accel_stationary_timer_info); +#endif + s_current_state = StationaryStateAwake; +} + +static void prv_handle_awake_action(StationaryAction action) { + switch (action) { + case StationaryActionGoToSleep: + prv_enter_stationary_state(); + break; + case StationaryActionWakeUp: + prv_reset_stationary_counter(); + break; + case StationaryActionEnableStationary: + break; + case StationaryActionDisableStationary: + prv_enter_disabled_state(); + break; + default: + WTF; + } +} + +static void prv_handle_stationary_action(StationaryAction action) { + switch (action) { + case StationaryActionGoToSleep: + break; + case StationaryActionWakeUp: + prv_enter_peek_state(); + break; + case StationaryActionEnableStationary: + break; + case StationaryActionDisableStationary: + prv_exit_stationary(); + prv_enter_disabled_state(); + break; + default: + WTF; + } +} + +static void prv_handle_peeking_action(StationaryAction action) { + switch (action) { + case StationaryActionGoToSleep: + prv_enter_stationary_state(); + break; + case StationaryActionWakeUp: + prv_enter_awake_state(); + break; + case StationaryActionEnableStationary: + break; + case StationaryActionDisableStationary: + prv_enter_disabled_state(); + break; + default: + WTF; + } +} + +static void prv_handle_disabled_action(StationaryAction action) { + switch (action) { + case StationaryActionGoToSleep: + break; + case StationaryActionWakeUp: + // No-op here. Awake gives us the same runlevel as Disabled, so no harm in just staying in + // the disabled state. Could potentially be caused in races where we tap or press a button + // to wake us up from stationary while we're being disabled. + break; + case StationaryActionEnableStationary: + prv_exit_disabled_state(); + break; + case StationaryActionDisableStationary: + break; + default: + WTF; + } +} + +typedef void (*StationaryActionHandler)(StationaryAction action); + +static void prv_handle_action(StationaryAction action) { + // we need to be on kernel main so that we subscribe to event services + // for kernel main + PBL_ASSERT_TASK(PebbleTask_KernelMain); + PBL_LOG_D_DBG(DEBUG_STATIONARY, "Stationary: state %d action %d", + s_current_state, action); + + static StationaryActionHandler const prv_action_jump_table[] = { + [StationaryStateAwake] = prv_handle_awake_action, + [StationaryStateStationary] = prv_handle_stationary_action, + [StationaryStatePeeking] = prv_handle_peeking_action, + [StationaryStateDisabled] = prv_handle_disabled_action + }; + PBL_ASSERTN(s_current_state < ARRAY_LENGTH(prv_action_jump_table)); + prv_action_jump_table[s_current_state](action); +} + +static void prv_setup_callback_info(void) { + //! Timer callback to check whether the watch is stationary every minute + s_accel_stationary_timer_info = (RegularTimerInfo) { + .cb = prv_stationary_check_timer_cb + }; + + //! Button press events + s_button_event_info = (EventServiceInfo) { + .type = PEBBLE_BUTTON_DOWN_EVENT, + .handler = prv_button_down_handler, + }; +} + +void stationary_init(void) { + prv_setup_callback_info(); +} diff --git a/src/fw/services/stationary/wscript_build b/src/fw/services/stationary/wscript_build new file mode 100644 index 0000000000..ee20c229ea --- /dev/null +++ b/src/fw/services/stationary/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c'], + target='services_stationary', +) +bld.env.SERVICES.append('services_stationary') diff --git a/src/fw/services/system_task/Kconfig b/src/fw/services/system_task/Kconfig new file mode 100644 index 0000000000..c0e0fb995c --- /dev/null +++ b/src/fw/services/system_task/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_SYSTEM_TASK + bool "System task" + help + Background system task runner. + +if SERVICE_SYSTEM_TASK + +module = SERVICE_SYSTEM_TASK +module-str = System task +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/system_task/service.c b/src/fw/services/system_task/service.c new file mode 100644 index 0000000000..87a96603dd --- /dev/null +++ b/src/fw/services/system_task/service.c @@ -0,0 +1,215 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/system_task.h" + +#include "system/logging.h" + +#include "drivers/task_watchdog.h" +#include "kernel/pebble_tasks.h" +#include "kernel/util/task_init.h" +#include "mcu/fpu.h" +#include "os/tick.h" +#include "pbl/services/regular_timer.h" +#include "system/passert.h" + +#include "FreeRTOS.h" +#include "queue.h" +#include "task.h" + +PBL_LOG_MODULE_DEFINE(service_system_task, CONFIG_SERVICE_SYSTEM_TASK_LOG_LEVEL); + +#define SYSTEM_TASK_PRIORITY (tskIDLE_PRIORITY + 1) + +typedef struct { + SystemTaskEventCallback cb; + void *data; +} SystemTaskEvent; + +static QueueHandle_t s_system_task_queue; +static QueueHandle_t s_from_app_system_task_queue; + +static QueueSetHandle_t s_system_task_queue_set; + +static SystemTaskEventCallback s_current_cb; + +static bool s_system_task_idle = true; +static bool s_should_block_callbacks = false; + +static bool prv_is_accepting_callbacks() { + return s_system_task_queue != 0 && !s_should_block_callbacks; +} + +static void system_task_idle_timer_callback(void* data) { + if (s_system_task_idle && uxQueueMessagesWaiting(s_system_task_queue_set) == 0) { + system_task_watchdog_feed(); + } +} + +static void system_task_main(void* paramater) { + task_watchdog_mask_set(PebbleTask_KernelBackground); + task_init(); + + while (true) { + s_system_task_idle = true; + + SystemTaskEvent event; + + QueueSetMemberHandle_t activated_queue = xQueueSelectFromSet(s_system_task_queue_set, portMAX_DELAY); + + // Get event from the activated queue + portBASE_TYPE result = xQueueReceive(activated_queue, &event, 0); + + // I believe its possible that we just reset the queue and accidently + // pended an extra event to the queue set so handle that case gracefully + if (result) { + s_system_task_idle = false; + s_current_cb = event.cb; + event.cb(event.data); + mcu_fpu_cleanup(); + s_current_cb = NULL; + } + + // Refresh the watchdog immediately, just in case that cb() took awhile to run. + system_task_watchdog_feed(); + } +} + +void system_task_init(void) { + static const int SYSTEM_TASK_QUEUE_LENGTH = 30; + static const int FROM_APP_SYSTEM_TASK_QUEUE_LENGTH = 8; + + s_system_task_queue = xQueueCreate(SYSTEM_TASK_QUEUE_LENGTH, sizeof(SystemTaskEvent)); + s_from_app_system_task_queue = xQueueCreate(FROM_APP_SYSTEM_TASK_QUEUE_LENGTH, sizeof(SystemTaskEvent)); + + s_system_task_queue_set = xQueueCreateSet(SYSTEM_TASK_QUEUE_LENGTH + FROM_APP_SYSTEM_TASK_QUEUE_LENGTH); + xQueueAddToSet(s_system_task_queue, s_system_task_queue_set); + xQueueAddToSet(s_from_app_system_task_queue, s_system_task_queue_set); + + extern uint32_t __kernel_bg_stack_start__[]; + extern uint32_t __kernel_bg_stack_size__[]; + extern uint32_t __stack_guard_size__[]; + const uint32_t kernel_bg_stack_words = ( (uint32_t)__kernel_bg_stack_size__ + - (uint32_t)__stack_guard_size__) / sizeof(portSTACK_TYPE); + + TaskParameters_t task_params = { + .pvTaskCode = system_task_main, + .pcName = "KernelBG", + .usStackDepth = kernel_bg_stack_words, + .uxPriority = SYSTEM_TASK_PRIORITY | portPRIVILEGE_BIT, + .puxStackBuffer = (void*)(uintptr_t)((uint32_t)__kernel_bg_stack_start__ + + (uint32_t)__stack_guard_size__) + }; + + pebble_task_create(PebbleTask_KernelBackground, &task_params, NULL); +} + +void system_task_timer_init(void) { + // Register a regular timer to kick the watchdog while we're waiting for something + // to do. The other way to do this is to have the xQueueReceive in system_task_main timeout + // occasionally, but that isn't necessarily second aligned and will require the watch + // to wakeup from sleep just to kick the watchdog. This way it's kicked at the same time as + // all the other regular tasks. Note that the system_task_idle_timer_callback only kicks + // the watchdog if we're currently waiting for work to do on the system_task. If we're in the + // middle of something we won't kick it. + static RegularTimerInfo idle_watchdog_timer = { + .cb = system_task_idle_timer_callback + }; + regular_timer_add_seconds_callback(&idle_watchdog_timer); +} + +void system_task_watchdog_feed(void) { + task_watchdog_bit_set(PebbleTask_KernelBackground); +} + +static void handle_system_task_send_failure(SystemTaskEventCallback cb, uintptr_t caller_lr) { + PBL_LOG_ERR("System task queue full. Dropped cb: %p, current cb: %p", cb, s_current_cb); + + RebootReason reason = { + .code = RebootReasonCode_EventQueueFull, + .event_queue = { + .push_lr = (uint32_t) caller_lr, + .current_event = (uint32_t) s_current_cb, + .dropped_event = (uint32_t) cb + } + }; + reboot_reason_set(&reason); + + reset_due_to_software_failure(); +} + +bool system_task_add_callback_from_isr(SystemTaskEventCallback cb, void *data, bool* should_context_switch) { + // Capture caller LR at entry; reading from a deeper helper is unreliable. + uintptr_t caller_lr = (uintptr_t)__builtin_return_address(0); + if (!prv_is_accepting_callbacks()) { + return false; + } + SystemTaskEvent event = { + .cb = cb, + .data = data, + }; + + signed portBASE_TYPE tmp; + bool success = (xQueueSendToBackFromISR(s_system_task_queue, &event, &tmp) == pdTRUE); + if (!success) { + handle_system_task_send_failure(cb, caller_lr); + } + + *should_context_switch = (tmp == pdTRUE); + + return success; +} + +bool system_task_add_callback(SystemTaskEventCallback cb, void *data) { + uintptr_t caller_lr = (uintptr_t)__builtin_return_address(0); + if (!prv_is_accepting_callbacks()) { + return false; + } + + SystemTaskEvent event = { + .cb = cb, + .data = data, + }; + + if (pebble_task_get_current() == PebbleTask_App) { + // If we're the app and we've filled up our system task, the app just gets to wait. + // FIXME: In the future when we want to bound the amount of time a syscall can take this will have to change. + xQueueSendToBack(s_from_app_system_task_queue, &event, portMAX_DELAY); + return true; + } else { + // Back ourselves up and wait a reasonable amount of time before failing. If the queue is really backed up + // we want to fall through to the handle_system_task_send_failure and not just get killed by the watchdog. + bool success = (xQueueSendToBack(s_system_task_queue, &event, milliseconds_to_ticks(3000)) == pdTRUE); + if (!success) { + handle_system_task_send_failure(cb, caller_lr); + } + } + + return true; +} + +void system_task_block_callbacks(bool block) { + s_should_block_callbacks = block; +} + +uint32_t system_task_get_available_space(void) { + const bool is_app = pebble_task_get_current() == PebbleTask_App; + return uxQueueSpacesAvailable(is_app ? s_from_app_system_task_queue : s_system_task_queue); +} + +void* system_task_get_current_callback(void) { + return s_current_cb; +} + +void system_task_enable_raised_priority(bool is_raised) { + const uint32_t raised_priority_level = tskIDLE_PRIORITY + 3; // Same as KernelMain / BT tasks + vTaskPrioritySet(pebble_task_get_handle_for_task(PebbleTask_KernelBackground), + (is_raised ? raised_priority_level : SYSTEM_TASK_PRIORITY) | portPRIVILEGE_BIT); +} + +bool system_task_is_ready_to_run(void) { + const eTaskState bg_task_state = + eTaskGetState(pebble_task_get_handle_for_task(PebbleTask_KernelBackground)); + // check if system task is ready to go (instead of e.g. waiting for a mutex) + return (bg_task_state == eReady); +} diff --git a/src/fw/services/system_task/wscript_build b/src/fw/services/system_task/wscript_build new file mode 100644 index 0000000000..ec3ca32c45 --- /dev/null +++ b/src/fw/services/system_task/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_system_task', +) +bld.env.SERVICES.append('services_system_task') diff --git a/src/fw/services/tick_timer/Kconfig b/src/fw/services/tick_timer/Kconfig new file mode 100644 index 0000000000..550dc44147 --- /dev/null +++ b/src/fw/services/tick_timer/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_TICK_TIMER + bool "Tick timer" + help + Tick-timer service (wall-clock tick events). + +if SERVICE_TICK_TIMER + +module = SERVICE_TICK_TIMER +module-str = Tick timer +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/tick_timer/service.c b/src/fw/services/tick_timer/service.c new file mode 100644 index 0000000000..5385c08220 --- /dev/null +++ b/src/fw/services/tick_timer/service.c @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/tick_timer.h" + +#include "kernel/events.h" +#include "drivers/rtc.h" +#include "pbl/services/regular_timer.h" +#include "process_management/app_manager.h" +#include "system/logging.h" +#include "system/passert.h" + +PBL_LOG_MODULE_DEFINE(service_tick_timer, CONFIG_SERVICE_TICK_TIMER_LOG_LEVEL); + +static uint16_t s_num_subscribers; + +static void timer_tick_event_publisher(void* data) { + PebbleEvent e = { + .type = PEBBLE_TICK_EVENT, + .clock_tick.tick_time = rtc_get_time(), + }; + + event_put(&e); +} + +static RegularTimerInfo s_tick_timer_info = { + .cb = &timer_tick_event_publisher +}; + +void tick_timer_add_subscriber(PebbleTask task) { + ++s_num_subscribers; + if (s_num_subscribers == 1) { + PBL_LOG_DBG("starting tick timer"); + regular_timer_add_seconds_callback(&s_tick_timer_info); + } +} + +void tick_timer_remove_subscriber(PebbleTask task) { + PBL_ASSERTN(s_num_subscribers > 0); + --s_num_subscribers; + if (s_num_subscribers == 0) { + PBL_LOG_DBG("stopping tick timer"); + regular_timer_remove_callback(&s_tick_timer_info); + } +} diff --git a/src/fw/services/tick_timer/wscript_build b/src/fw/services/tick_timer/wscript_build new file mode 100644 index 0000000000..9f1998f216 --- /dev/null +++ b/src/fw/services/tick_timer/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_tick_timer', +) +bld.env.SERVICES.append('services_tick_timer') diff --git a/src/fw/services/timeline/Kconfig b/src/fw/services/timeline/Kconfig new file mode 100644 index 0000000000..6e97c19a5a --- /dev/null +++ b/src/fw/services/timeline/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_TIMELINE + bool "Timeline" + help + Timeline service and layouts. + +if SERVICE_TIMELINE + +module = SERVICE_TIMELINE +module-str = Timeline +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/timeline/actions_endpoint.c b/src/fw/services/timeline/actions_endpoint.c similarity index 83% rename from src/fw/services/normal/timeline/actions_endpoint.c rename to src/fw/services/timeline/actions_endpoint.c index ac61801c6a..958d5555d9 100644 --- a/src/fw/services/normal/timeline/actions_endpoint.c +++ b/src/fw/services/timeline/actions_endpoint.c @@ -1,33 +1,22 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "actions_endpoint.h" -#include "attributes_actions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/actions_endpoint.h" +#include "pbl/services/timeline/attributes_actions.h" #include "comm/ble/kernel_le_client/ancs/ancs_types.h" #include "kernel/pbl_malloc.h" -#include "services/common/system_task.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/notifications/notifications.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/system_task.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/notifications/notifications.h" +#include "pbl/services/timeline/timeline_actions.h" #include "system/hexdump.h" #include "system/logging.h" #include "system/passert.h" #include "util/attributes.h" +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + typedef enum { CommandInvokeAction = 0x02, CommandInvokeActionANCSNotif = 0x03, @@ -127,7 +116,7 @@ static PebbleSysNotificationActionResult *prv_action_result_create_from_serial_d PebbleSysNotificationActionResult *action_result = kernel_zalloc(sizeof(PebbleSysNotificationActionResult) + alloc_size); if (!action_result) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to allocate memory for action result"); + PBL_LOG_WRN("Failed to allocate memory for action result"); return NULL; } @@ -178,7 +167,7 @@ void timeline_action_endpoint_invoke_action(const Uuid *id, TimelineItemActionTy char uuid_string[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(id, uuid_string); - PBL_LOG(LOG_LEVEL_INFO, "Send action to phone (Item ID: %s; Action ID: %d)", + PBL_LOG_INFO("Send action to phone (Item ID: %s; Action ID: %d)", uuid_string, action_id); PBL_HEXDUMP(LOG_LEVEL_DEBUG, (uint8_t *)&invoke_action_data->msg, invoke_action_data->length); @@ -196,22 +185,22 @@ void timeline_action_endpoint_invoke_action(const Uuid *id, TimelineItemActionTy void timeline_action_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) { if (length < sizeof(ResponseHeader)) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid phone response message length %d", (int) length); + PBL_LOG_WRN("Invalid phone response message length %d", (int) length); return; } ResponseHeader *header = (ResponseHeader *) data; if (header->command != CommandPhoneResponse && header->command != CommandPhoneActionResponse) { - PBL_LOG(LOG_LEVEL_WARNING, "Invalid command id"); + PBL_LOG_WRN("Invalid command id"); return; } if (uuid_is_system(&header->item_id)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Automatic SMS msg response: 0x%02X", header->response); + PBL_LOG_DBG("Automatic SMS msg response: 0x%02X", header->response); return; } - PBL_LOG(LOG_LEVEL_DEBUG, "Action Endpoint Response: 0x%02X", header->response); + PBL_LOG_DBG("Action Endpoint Response: 0x%02X", header->response); PebbleSysNotificationActionResult *action_result = NULL; diff --git a/src/fw/services/timeline/alarm_layout.c b/src/fw/services/timeline/alarm_layout.c new file mode 100644 index 0000000000..7a35f300c3 --- /dev/null +++ b/src/fw/services/timeline/alarm_layout.c @@ -0,0 +1,217 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/alarm_layout.h" +#include "pbl/services/timeline/timeline_layout.h" + +#include "applib/fonts/fonts.h" +#include "applib/graphics/gtypes.h" +#include "applib/graphics/text.h" +#include "applib/preferred_content_size.h" +#include "applib/ui/ui.h" +#include "drivers/rtc.h" +#include "font_resource_keys.auto.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/kernel_ui.h" +#include "process_state/app_state/app_state.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/alarms/alarm.h" +#include "system/logging.h" +#include "system/hexdump.h" +#include "util/size.h" +#include "util/string.h" + +////////////////////////////////////////// +// Card Mode +////////////////////////////////////////// + +#define CARD_MARGIN_TOP PBL_IF_RECT_ELSE(3, 10) + +static void prv_until_time_update(const LayoutLayer *layout_ref, + const LayoutNodeTextDynamicConfig *config, char *buffer, + bool render) { + const TimelineLayout *layout = (TimelineLayout *)layout_ref; + const int max_relative_hours = 24; // show up to "in 24 hours" + clock_get_until_time_without_fulltime(buffer, config->buffer_size, layout->info->timestamp, + max_relative_hours); +} + +T_STATIC void prv_get_subtitle_from_attributes(AttributeList *attributes, char *buffer, + size_t buffer_size, const void *i18n_owner) { + const char *subtitle_string = NULL; + // We only all-caps the subtitle in the card view on rectangular displays + bool all_caps_desired = PBL_IF_RECT_ELSE(true, false); + bool need_to_all_caps_string = false; + + // First, try to extract an AlarmKind from the pin to request a string with the desired + // capitalization + Attribute *alarm_kind_attribute = attribute_find(attributes, AttributeIdAlarmKind); + if (alarm_kind_attribute) { + const AlarmKind alarm_kind = (AlarmKind)alarm_kind_attribute->uint8; + subtitle_string = i18n_get(alarm_get_string_for_kind(alarm_kind, all_caps_desired), i18n_owner); + } else { + // Otherwise, fallback to just using the subtitle in the pin + subtitle_string = attribute_get_string(attributes, AttributeIdSubtitle, ""); + need_to_all_caps_string = all_caps_desired; + } + + strncpy(buffer, subtitle_string, buffer_size); + buffer[buffer_size - 1] = '\0'; + + if (need_to_all_caps_string) { + toupper_str(buffer); + } +} + +static void prv_subtitle_update(const LayoutLayer *layout, + const LayoutNodeTextDynamicConfig *config, char *buffer, + bool render) { + prv_get_subtitle_from_attributes(layout->attributes, buffer, config->buffer_size, layout); +} + +static void prv_time_digits_update(const LayoutLayer *layout_ref, + const LayoutNodeTextDynamicConfig *config, char *buffer, + bool render) { + const TimelineLayout *layout = (TimelineLayout *)layout_ref; + clock_get_time_number(buffer, config->buffer_size, layout->info->timestamp); +} + +static void prv_time_am_pm_update(const LayoutLayer *layout_ref, + const LayoutNodeTextDynamicConfig *config, char *buffer, + bool render) { + const TimelineLayout *layout = (TimelineLayout *)layout_ref; + clock_get_time_word(buffer, config->buffer_size, layout->info->timestamp); +} + +static GTextNode *prv_time_node_constructor( + const LayoutLayer *layout_ref, const LayoutNodeConstructorConfig *config) { + static const LayoutNodeTextDynamicConfig s_time_default_config = { + .text.extent.node.type = LayoutNodeType_TextDynamic, + .update = timeline_layout_time_text_update, + .buffer_size = TIME_STRING_REQUIRED_LENGTH, + .text.font_key = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM, + .text.alignment = LayoutTextAlignment_Center, + }; + static const LayoutNodeTextDynamicConfig s_time_digits_config = { + .text.extent.node.type = LayoutNodeType_TextDynamic, + .update = prv_time_digits_update, + .buffer_size = TIME_STRING_REQUIRED_LENGTH, + .text.font_key = FONT_KEY_LECO_42_NUMBERS, + .text.alignment = LayoutTextAlignment_Left, + .text.extent.margin.w = 4, + }; + static const LayoutNodeTextDynamicConfig s_time_am_pm_config = { + .text.extent.node.type = LayoutNodeType_TextDynamic, + .update = prv_time_am_pm_update, + .buffer_size = 4, + .text.font_key = FONT_KEY_GOTHIC_28_BOLD, + .text.alignment = LayoutTextAlignment_Left, + .text.extent.offset.y = 14, + }; + + const bool use_large_time = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ false, /* medium */ false, /* large */ true, /* extralarge */ true); + + if (!use_large_time) { + return layout_create_text_node_from_config(layout_ref, &s_time_default_config.text.extent.node); + } + + GTextNodeHorizontal *horizontal_node = graphics_text_node_create_horizontal(2); + horizontal_node->horizontal_alignment = GTextAlignmentCenter; + GTextNode *digits_node = layout_create_text_node_from_config( + layout_ref, &s_time_digits_config.text.extent.node); + GTextNode *am_pm_node = layout_create_text_node_from_config( + layout_ref, &s_time_am_pm_config.text.extent.node); + graphics_text_node_container_add_child(&horizontal_node->container, digits_node); + graphics_text_node_container_add_child(&horizontal_node->container, am_pm_node); + return &horizontal_node->container.node; +} + +static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { + static const LayoutNodeTextDynamicConfig s_title_config = { + .text.extent.node.type = LayoutNodeType_TextDynamic, + .update = prv_until_time_update, + .buffer_size = TIME_STRING_REQUIRED_LENGTH, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Header, + .text.alignment = LayoutTextAlignment_Center, + .text.extent.margin.h = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ PBL_IF_RECT_ELSE(2, 0), + /* medium */ PBL_IF_RECT_ELSE(2, 0), + /* large */ 2, + /* extralarge */ 2), // title margin height + }; + static const LayoutNodeConstructorConfig s_time_config = { + .extent.node.type = LayoutNodeType_Constructor, + .constructor = prv_time_node_constructor, + .extent.margin.h = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ PBL_IF_RECT_ELSE(9, 1), + /* medium */ PBL_IF_RECT_ELSE(9, 1), + /* large */ 5, + /* extralarge */ 5), // time margin height + }; + static const LayoutNodeExtentConfig s_icon_config = { + .node.type = LayoutNodeType_TimelineIcon, + .margin.h = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ PBL_IF_RECT_ELSE(3, 1), + /* medium */ PBL_IF_RECT_ELSE(3, 1), + /* large */ 0, + /* extralarge */ 0), // icon margin height + }; + static const LayoutNodeTextDynamicConfig s_subtitle_config = { + .text.extent.node.type = LayoutNodeType_TextDynamic, + .update = prv_subtitle_update, + .buffer_size = TIME_STRING_REQUIRED_LENGTH, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Header, + .text.alignment = LayoutTextAlignment_Center, + }; + static const LayoutNodeConfig * const s_vertical_config_nodes[] = { + PBL_IF_RECT_ELSE(&s_title_config.text.extent.node, &s_icon_config.node), + PBL_IF_RECT_ELSE(&s_time_config.extent.node, &s_title_config.text.extent.node), + PBL_IF_RECT_ELSE(&s_icon_config.node, &s_time_config.extent.node), + &s_subtitle_config.text.extent.node, + }; + static const LayoutNodeVerticalConfig s_vertical_config = { + .container.extent.node.type = LayoutNodeType_Vertical, + .container.num_nodes = ARRAY_LENGTH(s_vertical_config_nodes), + .container.nodes = (LayoutNodeConfig **)&s_vertical_config_nodes, + .container.extent.offset.y = CARD_MARGIN_TOP, + .container.extent.margin.h = CARD_MARGIN_TOP, + }; + + return layout_create_text_node_from_config(&timeline_layout->layout_layer, + &s_vertical_config.container.extent.node); +} + +////////////////////////////////////////// +// LayoutLayer API +////////////////////////////////////////// + +bool alarm_layout_verify(bool existing_attributes[]) { + return (existing_attributes[AttributeIdTitle] && existing_attributes[AttributeIdSubtitle]); +} + +LayoutLayer *alarm_layout_create(const LayoutLayerConfig *config) { + AlarmLayout *layout = task_zalloc_check(sizeof(AlarmLayout)); + + static const TimelineLayoutImpl s_timeline_layout_impl = { + .attributes = { AttributeIdTitle, AttributeIdSubtitle }, + .default_colors = { { .argb = GColorBlackARGB8 }, + { .argb = GColorClearARGB8 }, + { .argb = GColorJaegerGreenARGB8 } }, + .default_icon = TIMELINE_RESOURCE_ALARM_CLOCK, + .card_icon_align = GAlignCenter, + .card_icon_size = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ TimelineResourceSizeSmall, + /* medium */ TimelineResourceSizeSmall, + /* large */ TimelineResourceSizeLarge, + /* extralarge */ TimelineResourceSizeLarge), + .card_view_constructor = prv_card_view_constructor, + }; + + timeline_layout_init((TimelineLayout *)layout, config, &s_timeline_layout_impl); + + return (LayoutLayer *)layout; +} \ No newline at end of file diff --git a/src/fw/services/normal/timeline/attribute.c b/src/fw/services/timeline/attribute.c similarity index 94% rename from src/fw/services/normal/timeline/attribute.c rename to src/fw/services/timeline/attribute.c index c3325db528..ef1a6b6575 100644 --- a/src/fw/services/normal/timeline/attribute.c +++ b/src/fw/services/timeline/attribute.c @@ -1,28 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "attribute.h" - -#include "attribute_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/attribute.h" + +#include "pbl/services/timeline/attribute_private.h" #include "system/passert.h" #include "kernel/pbl_malloc.h" #include "system/logging.h" #include "util/math.h" +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + #define MAX_LENGTH_TITLE (64) #define MAX_LENGTH_SUBTITLE (64) #define MAX_LENGTH_BODY (512) @@ -93,14 +82,17 @@ static AttributeType prv_attribute_type(AttributeId id) { case AttributeIdLaunchCode: case AttributeIdAncsId: case AttributeIdTimestamp: + case AttributeIdMuteExpiration: return AttributeTypeUint32; case AttributeIdCannedResponses: case AttributeIdHeadings: case AttributeIdParagraphs: case AttributeIdMetricNames: case AttributeIdMetricValues: + case AttributeIdNotificationFilteringRules: return AttributeTypeStringList; case AttributeIdMetricIcons: + case AttributeIdVibrationPattern: return AttributeTypeUint32List; default: return AttributeTypeUnknown; @@ -325,7 +317,7 @@ bool attribute_deserialize_list(char **buffer, char *const buf_end, for (int i = 0; i < attr_list.num_attributes; i++) { if (!prv_deserialize_attribute(buffer, buf_end, cursor, payload_end, &attr_list.attributes[i])) { - PBL_LOG(LOG_LEVEL_WARNING, "Encountered unknown attribute"); + PBL_LOG_WRN("Encountered unknown attribute"); break; } } @@ -430,14 +422,14 @@ size_t attribute_list_get_string_buffer_size(const AttributeList *list) { void attribute_list_add_cstring(AttributeList *list, AttributeId id, const char *cstring) { if (prv_attribute_type(id) != AttributeTypeString) { - PBL_LOG(LOG_LEVEL_WARNING, "Adding attribute with type cstring for non-cstring attribute"); + PBL_LOG_WRN("Adding attribute with type cstring for non-cstring attribute"); } prv_add_attribute(list, id)->cstring = (char*) cstring; } void attribute_list_add_uint32(AttributeList *list, AttributeId id, uint32_t uint32) { if (prv_attribute_type(id) != AttributeTypeUint32) { - PBL_LOG(LOG_LEVEL_WARNING, "Adding attribute with type uint32 for non-uint32_t attribute"); + PBL_LOG_WRN("Adding attribute with type uint32 for non-uint32_t attribute"); } prv_add_attribute(list, id)->uint32 = uint32; } @@ -445,7 +437,7 @@ void attribute_list_add_uint32(AttributeList *list, AttributeId id, uint32_t uin void attribute_list_add_resource_id(AttributeList *list, AttributeId id, uint32_t resource_id) { if (prv_attribute_type(id) != AttributeTypeResourceId) { - PBL_LOG(LOG_LEVEL_WARNING, "Adding attribute with type ResourceId for non-ResourceId " \ + PBL_LOG_WRN("Adding attribute with type ResourceId for non-ResourceId " \ "attribute"); } prv_add_attribute(list, id)->uint32 = resource_id; @@ -453,7 +445,7 @@ void attribute_list_add_resource_id(AttributeList *list, AttributeId id, void attribute_list_add_uint8(AttributeList *list, AttributeId id, uint8_t uint8) { if (prv_attribute_type(id) != AttributeTypeUint8) { - PBL_LOG(LOG_LEVEL_WARNING, "Adding attribute with type uint8 for non-uint8_t attribute"); + PBL_LOG_WRN("Adding attribute with type uint8 for non-uint8_t attribute"); } prv_add_attribute(list, id)->uint8 = uint8; } diff --git a/src/fw/services/normal/timeline/attribute_group.c b/src/fw/services/timeline/attribute_group.c similarity index 95% rename from src/fw/services/normal/timeline/attribute_group.c rename to src/fw/services/timeline/attribute_group.c index 8f285f79dd..c39d951c65 100644 --- a/src/fw/services/normal/timeline/attribute_group.c +++ b/src/fw/services/timeline/attribute_group.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "attribute_group.h" - -#include "services/normal/contacts/attributes_address.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/attribute_group.h" + +#include "pbl/services/contacts/attributes_address.h" #include "system/passert.h" #include "util/attributes.h" @@ -45,9 +32,15 @@ static bool prv_parse_serial_group_type_data(AttributeGroupType type, int header_size; if (type == AttributeGroupType_Action) { header_size = sizeof(SerializedActionHeader); + if (data + header_size > end) { + return false; + } num_attributes = ((SerializedActionHeader *)data)->num_attributes; } else { header_size = sizeof(SerializedAddressHeader); + if (data + header_size > end) { + return false; + } num_attributes = ((SerializedAddressHeader *)data)->num_attributes; } @@ -196,6 +189,9 @@ static AttributeList *prv_deserialize_action(TimelineItemAction *action, const uint8_t *payload_end, const uint8_t *buffer, const uint8_t *buf_end) { + if (*cursor + sizeof(SerializedActionHeader) > payload_end) { + return NULL; + } SerializedActionHeader *serialized_action = (SerializedActionHeader *)*cursor; *cursor += sizeof(SerializedActionHeader); action->id = serialized_action->id; @@ -210,6 +206,9 @@ static AttributeList *prv_deserialize_address(Address *address, const uint8_t *payload_end, const uint8_t *buffer, const uint8_t *buf_end) { + if (*cursor + sizeof(SerializedAddressHeader) > payload_end) { + return NULL; + } SerializedAddressHeader *serialized_address = (SerializedAddressHeader *)*cursor; *cursor += sizeof(SerializedAddressHeader); address->id = serialized_address->uuid; @@ -246,6 +245,10 @@ static bool prv_deserialize_group_element(AttributeGroupType type, buffer, buf_end); } + if (!group_type_element_attribtue_list) { + return false; + } + if (!attribute_deserialize_list((char**)&buffer, (char *)buf_end, &cursor, payload_end, *group_type_element_attribtue_list)) { return false; diff --git a/src/fw/services/normal/timeline/attributes_actions.c b/src/fw/services/timeline/attributes_actions.c similarity index 88% rename from src/fw/services/normal/timeline/attributes_actions.c rename to src/fw/services/timeline/attributes_actions.c index 88b26eb178..66d96f49a5 100644 --- a/src/fw/services/normal/timeline/attributes_actions.c +++ b/src/fw/services/timeline/attributes_actions.c @@ -1,25 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "attributes_actions.h" - -#include "services/normal/timeline/attribute_group.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/attributes_actions.h" + +#include "pbl/services/timeline/attribute_group.h" #include "system/logging.h" +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + #define GROUP_TYPE AttributeGroupType_Action @@ -154,7 +143,7 @@ bool attributes_actions_deep_copy(AttributeList *src_attr_list, rv = attribute_list_copy(dest_attr_list, src_attr_list, buffer, MIN(buffer + attr_list_size, buf_end)); if (!rv) { - PBL_LOG(LOG_LEVEL_ERROR, "Error deep-copying pin attribute list"); + PBL_LOG_ERR("Error deep-copying pin attribute list"); return false; } } @@ -162,7 +151,7 @@ bool attributes_actions_deep_copy(AttributeList *src_attr_list, rv = prv_action_group_copy(dest_action_group, src_action_group, buffer + attr_list_size, buf_end); if (!rv) { - PBL_LOG(LOG_LEVEL_ERROR, "Error deep-copying pin action group"); + PBL_LOG_ERR("Error deep-copying pin action group"); return false; } } diff --git a/src/fw/services/timeline/calendar.c b/src/fw/services/timeline/calendar.c new file mode 100644 index 0000000000..e74b0e062f --- /dev/null +++ b/src/fw/services/timeline/calendar.c @@ -0,0 +1,55 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/calendar.h" + +#include "drivers/rtc.h" +#include "kernel/event_loop.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "kernel/pebble_tasks.h" +#include "pbl/services/system_task.h" +#include "pbl/services/blob_db/pin_db.h" +#include "system/logging.h" +#include "system/status_codes.h" +#include "util/time/time.h" + +static bool s_event_ongoing = false; + +static void prv_put_calendar_event(void) { + PebbleEvent event = { + .type = PEBBLE_CALENDAR_EVENT, + .calendar = { + .is_event_ongoing = s_event_ongoing, + }, + }; + event_put(&event); +} + +static bool prv_calendar_filter(SerializedTimelineItemHeader *header, void **context) { + const time_t now = rtc_get_time(); + return ((header->common.layout == LayoutIdCalendar) && + !timeline_event_is_all_day(&header->common) && + (timeline_event_is_ongoing(now, header->common.timestamp, header->common.duration) || + timeline_event_starts_within(&header->common, now, 0, TIMELINE_EVENT_DELTA_INFINITE))); +} + +static uint32_t prv_calendar_update(TimelineItem *item, void **context) { + s_event_ongoing = item ? timeline_event_is_ongoing(rtc_get_time(), item->header.timestamp, + item->header.duration) + : false; + prv_put_calendar_event(); + return 0; +} + +const TimelineEventImpl *calendar_get_event_service(void) { + static const TimelineEventImpl s_event_impl = { + .filter = prv_calendar_filter, + .update = prv_calendar_update, + }; + return &s_event_impl; +} + +bool calendar_event_is_ongoing(void) { + return s_event_ongoing; +} diff --git a/src/fw/services/normal/timeline/calendar_layout.c b/src/fw/services/timeline/calendar_layout.c similarity index 79% rename from src/fw/services/normal/timeline/calendar_layout.c rename to src/fw/services/timeline/calendar_layout.c index c9952093f8..75b400615e 100644 --- a/src/fw/services/normal/timeline/calendar_layout.c +++ b/src/fw/services/timeline/calendar_layout.c @@ -1,33 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "calendar_layout.h" -#include "calendar_layout_resources.h" -#include "timeline_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/calendar_layout.h" +#include "pbl/services/timeline/calendar_layout_resources.h" +#include "pbl/services/timeline/timeline_layout.h" #include "applib/fonts/fonts.h" #include "applib/graphics/gdraw_command_transforms.h" #include "applib/graphics/gtypes.h" #include "applib/graphics/text.h" +#include "applib/preferred_content_size.h" #include "applib/ui/ui.h" #include "board/display.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" #include "system/hexdump.h" #include "util/math.h" @@ -36,14 +24,22 @@ #include -#if !TINTIN_FORCE_FIT ////////////////////////////////////////// // Card Mode ////////////////////////////////////////// #if PBL_RECT -#define CARD_ICON_OFFSET { 0, 6 } -#define CARD_ICON_MARGIN { 3, 2 } +#define CARD_ICON_OFFSET_Y \ + PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, \ + /* small */ 6, /* medium */ 6, /* large */ -3, /* extralarge */ -3) +#define CARD_ICON_MARGIN_W \ + PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, \ + /* small */ 3, /* medium */ 3, /* large */ 5, /* extralarge */ 5) +#define CARD_ICON_OFFSET { 0, CARD_ICON_OFFSET_Y } +#define CARD_ICON_MARGIN_H \ + PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, \ + /* small */ 2, /* medium */ 2, /* large */ 0, /* extralarge */ 0) +#define CARD_ICON_MARGIN { CARD_ICON_MARGIN_W, CARD_ICON_MARGIN_H } #else #define CARD_ICON_OFFSET { 0, 9 } #define CARD_ICON_MARGIN { 0, 6 } @@ -52,7 +48,12 @@ //! This offset only applies for TIMELINE_RESOURCE_TIMELINE_CALENDAR and variants #define CARD_ICON_CALENDAR_OFFSET_X PBL_IF_RECT_ELSE(-5, 0) -#define CARD_MARGIN_TOP -1 +#define CARD_MARGIN_TOP \ + PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, \ + /* small */ -1, \ + /* medium */ -1, \ + /* large */ 6, \ + /* extralarge */ 6) #define CARD_MARGIN_BOTTOM PBL_IF_RECT_ELSE(7, 0) #define CARD_NUM_TIME_DATE_SPACES 2 #define CARD_LINE_DELTA -2 @@ -107,7 +108,10 @@ static void prv_day_node_callback(GContext *ctx, const GRect *box, GSize *size_out, void *user_data) { CalendarLayout *layout = user_data; const GRect *icon_frame = &layout->timeline_layout.icon_layer.layer.frame; - const GPoint date_offset = { 1, 16 }; + const GPoint date_offset = { + 1, PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ 16, /* medium */ 16, /* large */ 16, /* extralarge */ 16) + }; layer_set_frame((Layer *)&layout->date_layer, &(GRect) { gpoint_add(icon_frame->origin, date_offset), icon_frame->size }); clock_get_day_date(layout->day_date_buffer, sizeof(layout->day_date_buffer), @@ -124,7 +128,12 @@ static GTextNode *prv_day_node_constructor( } const LayoutColors *colors = &layout->timeline_layout.impl->default_colors; text_layer_init_with_parameters(&layout->date_layer, &GRectZero, layout->day_date_buffer, - fonts_get_system_font(FONT_KEY_LECO_20_BOLD_NUMBERS), + fonts_get_system_font(PREFERRED_CONTENT_SIZE_SWITCH( + PreferredContentSizeDefault, + /* small */ FONT_KEY_LECO_20_BOLD_NUMBERS, + /* medium */ FONT_KEY_LECO_20_BOLD_NUMBERS, + /* large */ FONT_KEY_LECO_32_BOLD_NUMBERS, + /* extralarge */ FONT_KEY_LECO_32_BOLD_NUMBERS)), colors->primary_color, GColorClear, GTextAlignmentCenter, GTextOverflowModeWordWrap); layer_add_child((Layer *)layout, (Layer *)&layout->date_layer); @@ -245,6 +254,23 @@ static GTextNode *prv_construct_if_recurring( return NULL; } +static void prv_not_recurring_spacer_callback(GContext *ctx, const GRect *box, + const GTextNodeDrawConfig *config, bool render, + GSize *size_out, void *user_data) { + if (size_out) { + *size_out = (GSize) { 0, PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ 0, /* medium */ 0, /* large */ 3, /* extralarge */ 3) }; + } +} + +static GTextNode *prv_construct_if_not_recurring( + const LayoutLayer *layout, const LayoutNodeConstructorConfig *config) { + if (prv_should_draw_recurring((const TimelineLayout *)layout)) { + return NULL; + } + return &graphics_text_node_create_custom(prv_not_recurring_spacer_callback, NULL)->node; +} + typedef struct { GDrawCommandImage *image; CalendarLayoutBufferCallback callback; @@ -261,7 +287,8 @@ static GTextNode *prv_create_icon_label_node_rect( const LayoutNodeTextBufferConfig time_config = { .text.extent.node.type = LayoutNodeType_TextBuffer, .str = buffer, - .text.font_key = FONT_KEY_GOTHIC_18_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Header, .text.extent.margin.h = time_margin_h, }; GTextNode *node = @@ -272,7 +299,8 @@ static GTextNode *prv_create_icon_label_node_rect( } GTextNodeHorizontal *horizontal_node = graphics_text_node_create_horizontal(2); GTextNodeCustom *image_node = prv_create_image_node(ctx->image); - image_node->node.offset.y = 8; + image_node->node.offset.y = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ 8, /* medium */ 8, /* large */ 11, /* extralarge */ 11); image_node->node.margin.w = 6; graphics_text_node_container_add_child(&horizontal_node->container, &image_node->node); graphics_text_node_container_add_child(&horizontal_node->container, node); @@ -286,7 +314,8 @@ static GTextNode *prv_construct_all_day_or_node( .text.extent.node.type = LayoutNodeType_TextBuffer, .str = i18n_noop("All Day"), .use_i18n = true, - .text.font_key = FONT_KEY_GOTHIC_18_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Header, }; return layout_create_text_node_from_config( layout_ref, layout->info->all_day ? &s_all_day_config.text.extent.node : config->context); @@ -327,7 +356,11 @@ static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { .text.extent.node.type = LayoutNodeType_TextBuffer, .str = i18n_noop("Recurring"), .use_i18n = true, - .text.font_key = PBL_IF_RECT_ELSE(FONT_KEY_GOTHIC_14, FONT_KEY_GOTHIC_14_BOLD), + .text.font_key = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ PBL_IF_RECT_ELSE(FONT_KEY_GOTHIC_14, FONT_KEY_GOTHIC_14_BOLD), + /* medium */ PBL_IF_RECT_ELSE(FONT_KEY_GOTHIC_14, FONT_KEY_GOTHIC_14_BOLD), + /* large */ FONT_KEY_GOTHIC_18_BOLD, + /* extralarge */ FONT_KEY_GOTHIC_18_BOLD), .text.extent.offset.y = PBL_IF_RECT_ELSE(4, 1), // recurring offset y .text.extent.margin.h = PBL_IF_RECT_ELSE(4, 1), // recurring margin height }; @@ -336,16 +369,22 @@ static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { .constructor = prv_construct_if_recurring, .context = (void *)&s_recurring_config, }; + PBL_UNUSED static const LayoutNodeConstructorConfig s_if_not_recurring_spacer_config = { + .extent.node.type = LayoutNodeType_Constructor, + .constructor = prv_construct_if_not_recurring, + }; static const LayoutNodeTextAttributeConfig s_glance_title_config = { .attr_id = AttributeIdTitle, - .text.font_key = FONT_KEY_GOTHIC_24_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Title, .text.fixed_lines = PBL_IF_RECT_ELSE(2, 1), // glance title fixed lines .text.line_spacing_delta = CARD_LINE_DELTA, .text.extent.margin.h = PBL_IF_RECT_ELSE(6, 4), // glance title margin height }; static const LayoutNodeTextAttributeConfig s_glance_location_config = { .attr_id = AttributeIdLocationName, - .text.font_key = FONT_KEY_GOTHIC_18_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Header, .text.fixed_lines = 1, // glance location fixed lines }; static const LayoutNodeConstructorConfig s_digit_config = { @@ -357,13 +396,15 @@ static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { }; static const LayoutNodeTextAttributeConfig s_title_config = { .attr_id = AttributeIdTitle, - .text.font_key = FONT_KEY_GOTHIC_24_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Title, .text.line_spacing_delta = CARD_LINE_DELTA, .text.extent.margin.h = 7, // title margin height }; static const LayoutNodeTextAttributeConfig s_location_config = { .attr_id = AttributeIdLocationName, - .text.font_key = FONT_KEY_GOTHIC_18_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Header, .text.extent.margin.h = 15, // location margin height }; static const IconLabelContext s_start_icon_label_context = { @@ -389,26 +430,33 @@ static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { }; static const LayoutNodeTextAttributeConfig s_body_config = { .attr_id = AttributeIdBody, - .text.font_key = FONT_KEY_GOTHIC_24_BOLD, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_Body, .text.line_spacing_delta = CARD_LINE_DELTA, .text.extent.margin.h = 17, // body margin height }; static const LayoutNodeTextAttributeConfig s_sender_config = { .attr_id = AttributeIdSender, - .text.font_key = FONT_KEY_GOTHIC_24, + .text.style = LayoutContentSizeDefault, + .text.style_font = TextStyleFont_PinSubtitle, .text.line_spacing_delta = CARD_LINE_DELTA, .text.extent.margin.h = 17, // sender margin height }; #if PBL_RECT static const LayoutNodeConfig * const s_metadata_config_nodes[] = { + &s_if_not_recurring_spacer_config.extent.node, &s_glance_start_time_or_all_day_config.extent.node, &s_glance_end_time_with_icon_config.extent.node, &s_if_recurring_config.extent.node, }; static const LayoutNodeVerticalConfig s_metadata_config = { .container.extent.node.type = LayoutNodeType_Vertical, - .vertical_alignment = LayoutVerticalAlignment_Center, + .vertical_alignment = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ LayoutVerticalAlignment_Center, + /* medium */ LayoutVerticalAlignment_Center, + /* large */ LayoutVerticalAlignment_Left, + /* extralarge */ LayoutVerticalAlignment_Left), .container.num_nodes = ARRAY_LENGTH(s_metadata_config_nodes), .container.nodes = (LayoutNodeConfig **)&s_metadata_config_nodes, }; @@ -480,7 +528,11 @@ LayoutLayer *calendar_layout_create(const LayoutLayerConfig *config) { { .argb = GColorSunsetOrangeARGB8 } }, .default_icon = TIMELINE_RESOURCE_TIMELINE_CALENDAR, .card_icon_align = PBL_IF_RECT_ELSE(GAlignLeft, GAlignCenter), - .card_icon_size = TimelineResourceSizeSmall, + .card_icon_size = PREFERRED_CONTENT_SIZE_SWITCH(PreferredContentSizeDefault, + /* small */ TimelineResourceSizeSmall, + /* medium */ TimelineResourceSizeSmall, + /* large */ TimelineResourceSizeLarge, + /* extralarge */ TimelineResourceSizeLarge), .card_view_constructor = prv_card_view_constructor, }; @@ -492,8 +544,3 @@ LayoutLayer *calendar_layout_create(const LayoutLayerConfig *config) { return (LayoutLayer *)layout; } -#else -LayoutLayer *calendar_layout_create(const LayoutLayerConfig *config) { return NULL; } - -bool calendar_layout_verify(bool existing_attributes[]) { return false; } -#endif diff --git a/src/fw/services/timeline/calendar_layout_resources.c b/src/fw/services/timeline/calendar_layout_resources.c new file mode 100644 index 0000000000..3a3ecf8f7b --- /dev/null +++ b/src/fw/services/timeline/calendar_layout_resources.c @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/calendar_layout_resources.h" + +#include "applib/graphics/gdraw_command_image.h" +#include "applib/graphics/gdraw_command_private.h" +#include "util/size.h" + +// FIXME: PBL-28898 GPath algorithm requires strange coordinates for pixel perfection +// The paths here result in pixel perfect icons with the current gpath filled algorithm, +// but when gpath filled is fixed to correctly match its coordinates, these MUST be updated. + +CalendarStartIcon g_calendar_start_icon = { + .image = { + .version = 1, + .size = { 9, 9 }, + .command_list = { + .num_commands = 1, + }, + }, + .command = { + .type = GDrawCommandTypePath, + .fill_color = { .argb = GColorBlackARGB8 }, + .num_points = STATIC_ARRAY_LENGTH(GPoint, START_ICON_POINTS), + }, + .points = START_ICON_POINTS, +}; + +CalendarEndIcon g_calendar_end_icon = { + .image = { + .version = 1, + .size = { 9, 9 }, + .command_list = { + .num_commands = 1, + }, + }, + .command = { + .type = GDrawCommandTypePath, + .fill_color = { .argb = GColorBlackARGB8 }, + .num_points = STATIC_ARRAY_LENGTH(GPoint, END_ICON_POINTS), + }, + .points = END_ICON_POINTS, +}; diff --git a/src/fw/services/normal/timeline/event.c b/src/fw/services/timeline/event.c similarity index 87% rename from src/fw/services/normal/timeline/event.c rename to src/fw/services/timeline/event.c index 8458721959..f869b2d4b6 100644 --- a/src/fw/services/normal/timeline/event.c +++ b/src/fw/services/timeline/event.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "calendar.h" -#include "event.h" -#include "peek.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/calendar.h" +#include "pbl/services/timeline/event.h" +#include "pbl/services/timeline/peek.h" #include "drivers/rtc.h" #include "kernel/event_loop.h" @@ -24,13 +11,15 @@ #include "kernel/pbl_malloc.h" #include "kernel/pebble_tasks.h" #include "os/mutex.h" -#include "services/common/system_task.h" -#include "services/normal/blob_db/pin_db.h" +#include "pbl/services/system_task.h" +#include "pbl/services/blob_db/pin_db.h" #include "system/logging.h" #include "system/passert.h" #include "system/status_codes.h" #include "util/time/time.h" +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + typedef struct TimelineEventState { const TimelineEventImpl *impl; SerializedTimelineItemHeader *filter_header; @@ -84,11 +73,11 @@ static uint32_t prv_calc_timeout(const TimelineItem *item) { static void prv_set_timer(unsigned int timeout_ms) { if (!timeout_ms) { - PBL_LOG(LOG_LEVEL_INFO, "Not setting timer."); + PBL_LOG_INFO("Not setting timer."); } else if (new_timer_start(s_timer, timeout_ms, prv_new_timer_callback, NULL, 0)) { - PBL_LOG(LOG_LEVEL_DEBUG, "Set timer for %u", timeout_ms); + PBL_LOG_DBG("Set timer for %u", timeout_ms); } else { - PBL_LOG(LOG_LEVEL_ERROR, "Could not start timer."); + PBL_LOG_ERR("Could not start timer."); } } @@ -143,7 +132,7 @@ static void prv_update_status(void) { uint32_t timeout_ms = 0; if ((rv != S_SUCCESS) && (rv != S_NO_MORE_ITEMS)) { // A failure occurred. Call the update functions with a NULL item - PBL_LOG(LOG_LEVEL_ERROR, "Failed to find next event."); + PBL_LOG_ERR("Failed to find next event."); } else if (rv != S_NO_MORE_ITEMS) { // Calculate the timeout before the item buffer is re-used timeout_ms = prv_calc_timeout(&item); diff --git a/src/fw/services/normal/timeline/generic_layout.c b/src/fw/services/timeline/generic_layout.c similarity index 91% rename from src/fw/services/normal/timeline/generic_layout.c rename to src/fw/services/timeline/generic_layout.c index 7316854c3d..e1f0f4f369 100644 --- a/src/fw/services/normal/timeline/generic_layout.c +++ b/src/fw/services/timeline/generic_layout.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "generic_layout.h" -#include "timeline_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/generic_layout.h" +#include "pbl/services/timeline/timeline_layout.h" #include "applib/fonts/fonts.h" #include "applib/graphics/gtypes.h" @@ -24,8 +11,8 @@ #include "font_resource_keys.auto.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" #include "system/hexdump.h" #include "util/size.h" @@ -46,6 +33,7 @@ #define CARD_MARGIN_BOTTOM PBL_IF_RECT_ELSE(7, 0) #define CARD_LINE_DELTA -2 +#if PBL_RECT static void prv_horizontal_rule_node_callback(GContext *ctx, const GRect *box, const GTextNodeDrawConfig *config, bool render, GSize *size_out, void *user_data) { @@ -83,6 +71,7 @@ static GTextNode *prv_horizontal_rule_constructor(const LayoutLayer *layout_ref, return NULL; } } +#endif // PBL_RECT static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { static const LayoutNodeExtentConfig s_icon_config = { @@ -102,10 +91,6 @@ static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { .text.alignment = PBL_IF_RECT_ELSE(LayoutTextAlignment_Right, LayoutTextAlignment_Center), .text.extent.margin.h = PBL_IF_RECT_ELSE(0, -2), // time margin height }; - static const LayoutNodeConstructorConfig s_horizontal_rule_config = { - .extent.node.type = LayoutNodeType_Constructor, - .constructor = prv_horizontal_rule_constructor, - }; static const LayoutNodeTextAttributeConfig s_title_config = { .attr_id = AttributeIdTitle, .text.style = LayoutContentSizeDefault, @@ -134,6 +119,10 @@ static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { }; #if PBL_RECT + static const LayoutNodeConstructorConfig s_horizontal_rule_config = { + .extent.node.type = LayoutNodeType_Constructor, + .constructor = prv_horizontal_rule_constructor, + }; static const LayoutNodeConfig * const s_icon_vertical_config_nodes[] = { &s_icon_config.node, }; diff --git a/src/fw/services/timeline/health_layout.c b/src/fw/services/timeline/health_layout.c new file mode 100644 index 0000000000..376ab76862 --- /dev/null +++ b/src/fw/services/timeline/health_layout.c @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/health_layout.h" +#include "pbl/services/timeline/timeline_layout.h" + +#include "kernel/pbl_malloc.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/health_util.h" +#include "util/size.h" + +#include + +////////////////////////////////////////// +// Card Mode +////////////////////////////////////////// + +#define CARD_MARGIN_TOP PBL_IF_RECT_ELSE(0, 5) +#define CARD_MARGIN_BOTTOM PBL_IF_RECT_ELSE(11, 0) + +static GTextNode *prv_card_view_constructor(TimelineLayout *timeline_layout) { + const LayoutNodeExtentConfig s_metrics_config = { + .node.type = LayoutNodeType_TimelineMetrics, + .offset.y = CARD_MARGIN_TOP, + .margin.h = CARD_MARGIN_TOP + CARD_MARGIN_BOTTOM, + }; + return layout_create_text_node_from_config(&timeline_layout->layout_layer, + &s_metrics_config.node); +} + +////////////////////////////////////////// +// LayoutLayer API +////////////////////////////////////////// + +bool health_layout_verify(bool existing_attributes[]) { + return existing_attributes[AttributeIdTitle]; +} + +LayoutLayer *health_layout_create(const LayoutLayerConfig *config) { + HealthLayout *layout = task_zalloc_check(sizeof(HealthLayout)); + + static const TimelineLayoutImpl s_timeline_layout_impl = { + .attributes = { AttributeIdTitle, AttributeIdSubtitle }, + .default_colors = { { .argb = GColorBlackARGB8 }, + { .argb = GColorWhiteARGB8 }, + { .argb = GColorSunsetOrangeARGB8 } }, + .default_icon = TIMELINE_RESOURCE_ACTIVITY, + .card_icon_align = PBL_IF_ROUND_ELSE(GAlignCenter, GAlignLeft), + .card_icon_size = TimelineResourceSizeTiny, + .card_view_constructor = prv_card_view_constructor, + }; + + timeline_layout_init((TimelineLayout *)layout, config, &s_timeline_layout_impl); + + return (LayoutLayer *)layout; +} diff --git a/src/fw/services/normal/timeline/item.c b/src/fw/services/timeline/item.c similarity index 92% rename from src/fw/services/normal/timeline/item.c rename to src/fw/services/timeline/item.c index 040fb96e2c..75b82b90a1 100644 --- a/src/fw/services/normal/timeline/item.c +++ b/src/fw/services/timeline/item.c @@ -1,27 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "item.h" -#include "attributes_actions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/item.h" +#include "pbl/services/timeline/attributes_actions.h" #include "drivers/rtc.h" #include "kernel/pbl_malloc.h" #include "system/logging.h" #include "system/passert.h" +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + static bool prv_is_valid_item(const TimelineItem *item) { return item && !uuid_is_invalid(&item->header.id); } @@ -176,7 +165,7 @@ bool timeline_item_deserialize_item(TimelineItem *item_out, header->payload_length, &string_alloc_size, (uint8_t **) &buffer)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to get timeline item"); + PBL_LOG_ERR("Failed to get timeline item"); goto cleanup; } @@ -187,7 +176,7 @@ bool timeline_item_deserialize_item(TimelineItem *item_out, string_alloc_size, payload, header->payload_length)) { - PBL_LOG(LOG_LEVEL_ERROR, "Failed to deserialize payload"); + PBL_LOG_ERR("Failed to deserialize payload"); goto cleanup; } @@ -278,12 +267,12 @@ bool timeline_item_verify_layout_serialized(const uint8_t *val, int val_len) { const uint8_t *cursor = val + sizeof(SerializedTimelineItemHeader); const uint8_t *val_end = val + val_len; if (!attribute_check_serialized_list(cursor, val_end, hdr->num_attributes, has_attribute)) { - PBL_LOG(LOG_LEVEL_ERROR, "Could not deserialize attributes to verify"); + PBL_LOG_ERR("Could not deserialize attributes to verify"); return false; } // verify that the layout of the item has the attribute it requires LayoutId layout = hdr->common.layout; - PBL_LOG(LOG_LEVEL_DEBUG, "Number of attributes: %d for layout: %d", hdr->num_attributes, layout); + PBL_LOG_DBG("Number of attributes: %d for layout: %d", hdr->num_attributes, layout); return layout_verify(has_attribute, layout); } diff --git a/src/fw/services/normal/timeline/layout_layer.c b/src/fw/services/timeline/layout_layer.c similarity index 78% rename from src/fw/services/normal/timeline/layout_layer.c rename to src/fw/services/timeline/layout_layer.c index 8f074103b2..d6a4a0b2e7 100644 --- a/src/fw/services/normal/timeline/layout_layer.c +++ b/src/fw/services/timeline/layout_layer.c @@ -1,31 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "layout_layer.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/layout_layer.h" // layout implementations -#include "alarm_layout.h" -#include "calendar_layout.h" -#include "generic_layout.h" -#include "health_layout.h" -#include "notification_layout.h" -#include "sports_layout.h" -#include "weather_layout.h" - -#include "services/normal/notifications/alerts_preferences_private.h" +#include "pbl/services/timeline/alarm_layout.h" +#include "pbl/services/timeline/calendar_layout.h" +#include "pbl/services/timeline/generic_layout.h" +#include "pbl/services/timeline/health_layout.h" +#include "pbl/services/timeline/notification_layout.h" +#include "pbl/services/timeline/sports_layout.h" +#include "pbl/services/timeline/weather_layout.h" + +#include "pbl/services/notifications/alerts_preferences_private.h" #include "system/passert.h" #include "applib/ui/status_bar_layer.h" @@ -57,6 +44,7 @@ static const LayoutColors s_default_colors = { .bg_color = { .argb = PBL_IF_COLOR_ELSE(GColorLightGrayARGB8, GColorWhiteARGB8) }, }; +#if !PBL_COLOR static const LayoutColors s_default_notification_colors_alternative = { .primary_color = {.argb = GColorWhiteARGB8}, .secondary_color = {.argb = GColorBlackARGB8}, @@ -68,6 +56,7 @@ static const LayoutColors s_default_notification_colors_standard = { .secondary_color = {.argb = GColorBlackARGB8}, .bg_color = {.argb = GColorLightGrayARGB8}, }; +#endif LayoutLayer *layout_create(LayoutId id, const LayoutLayerConfig *config) { // pretend tests are generics for testing diff --git a/src/fw/services/normal/timeline/layout_node.c b/src/fw/services/timeline/layout_node.c similarity index 96% rename from src/fw/services/normal/timeline/layout_node.c rename to src/fw/services/timeline/layout_node.c index 1e0073dcaf..a671f6d7aa 100644 --- a/src/fw/services/normal/timeline/layout_node.c +++ b/src/fw/services/timeline/layout_node.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "layout_node.h" - -#include "timeline_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/layout_node.h" + +#include "pbl/services/timeline/timeline_layout.h" #include "kernel/pbl_malloc.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "system/passert.h" #include "util/size.h" #include "util/string.h" @@ -502,21 +489,13 @@ GTextNode *layout_create_text_node_from_config(const LayoutLayer *layout, case LayoutNodeType_Constructor: return prv_create_node_from_constructor_config(layout, (LayoutNodeConstructorConfig *)config); case LayoutNodeType_Icon: -#if !PLATFORM_TINTIN return prv_create_icon_node_from_config(layout, (LayoutNodeIconConfig *)config); -#else - return NULL; -#endif case LayoutNodeType_TimelineIcon: return prv_create_timeline_icon_node_from_config(layout, (LayoutNodeExtentConfig *)config); case LayoutNodeType_TimelinePageBreak: return &timeline_layout_create_page_break_node((const TimelineLayout *)layout)->node; case LayoutNodeType_TimelineMetrics: -#if !PLATFORM_TINTIN return prv_create_metrics_node(layout, config); -#else - return NULL; -#endif case LayoutNodeType_HeadingsParagraphs: return prv_create_headings_paragraphs_node( layout, (LayoutNodeHeadingsParagraphsConfig *)config); diff --git a/src/fw/services/timeline/metricgroup.c b/src/fw/services/timeline/metricgroup.c new file mode 100644 index 0000000000..f5b713f96d --- /dev/null +++ b/src/fw/services/timeline/metricgroup.c @@ -0,0 +1,47 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/metricgroup.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/attribute.h" + +MetricGroup *metric_group_create(int max_num_items, size_t max_item_string_size) { + const size_t max_list_size = StringListSize(max_num_items, max_item_string_size); + MetricGroup *metric = task_zalloc_check(sizeof(MetricGroup)); + *metric = (MetricGroup) { + .names = task_zalloc_check(max_list_size), + .values = task_zalloc_check(max_list_size), + .icons = task_zalloc_check(Uint32ListSize(max_num_items)), + .max_num_items = max_num_items, + .max_item_string_size = max_item_string_size, + }; + return metric; +} + +void metric_group_destroy(MetricGroup *metric_group) { + if (!metric_group) { + return; + } + task_free(metric_group->names); + task_free(metric_group->values); + task_free(metric_group->icons); + task_free(metric_group); +} + +bool metric_group_add_item(MetricGroup *metric_group, const char *name_i18n, const char *value, + TimelineResourceId icon, void *i18n_owner) { + if (metric_group->num_items >= metric_group->max_num_items) { + return false; + } + const size_t max_list_size = StringListSize(metric_group->max_num_items, + metric_group->max_item_string_size); + string_list_add_string(metric_group->names, max_list_size, i18n_get(name_i18n, i18n_owner), + metric_group->max_item_string_size); + string_list_add_string(metric_group->values, max_list_size, value, + metric_group->max_item_string_size); + metric_group->icons->values[metric_group->num_items++] = icon; + metric_group->icons->num_values = metric_group->num_items; + return true; +} diff --git a/src/fw/services/normal/timeline/notification_layout.c b/src/fw/services/timeline/notification_layout.c similarity index 91% rename from src/fw/services/normal/timeline/notification_layout.c rename to src/fw/services/timeline/notification_layout.c index c89344ca21..c2017bad05 100644 --- a/src/fw/services/normal/timeline/notification_layout.c +++ b/src/fw/services/timeline/notification_layout.c @@ -1,39 +1,26 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "notification_jumboji_table.h" -#include "notification_layout.h" -#include "timeline_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/notification_jumboji_table.h" +#include "pbl/services/timeline/notification_layout.h" +#include "pbl/services/timeline/timeline_layout.h" #include "applib/fonts/fonts.h" #include "applib/graphics/gtypes.h" #include "applib/graphics/text.h" -#include "apps/system_apps/timeline/peek_layer.h" +#include "apps/system/timeline/peek_layer.h" #include "font_resource_keys.auto.h" #include "kernel/pbl_malloc.h" #include "kernel/ui/kernel_ui.h" #include "resource/resource_ids.auto.h" #include "resource/timeline_resource_ids.auto.h" -#include "services/common/analytics/analytics.h" -#include "services/common/clock.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/clock.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "system/hexdump.h" #include "system/logging.h" @@ -43,8 +30,6 @@ #include "util/string.h" #include "util/trig.h" -#if !TINTIN_FORCE_FIT - // NOTIFICATION // Title -> Sender/App // Subtitle -> Subject (Emails) @@ -61,8 +46,6 @@ #define CARD_MARGIN PBL_IF_ROUND_ELSE(12, 10) // All paddings relate to padding above the object unless othersize noted #define CARD_BOTTOM_PADDING 18 -// The y-position of a layout frame when its banner is peeking -#define BANNER_PEEK_STATIC_Y (DISP_ROWS - STATUS_BAR_LAYER_HEIGHT) #define BOTTOM_BANNER_CIRCLE_RADIUS 8 static void prv_card_render(NotificationLayout *layout, GContext *ctx, bool render); @@ -173,7 +156,6 @@ static void prv_notification_timestamp_update(const LayoutLayer *layout_ref, clock_get_since_time(buffer, config->buffer_size, layout->info.item->header.timestamp); } -#if !PLATFORM_TINTIN static const EmojiEntry s_emoji_table[] = JUMBOJI_TABLE(EMOJI_ENTRY); static bool prv_each_emoji_codepoint(int index, Codepoint codepoint, void *context) { @@ -225,7 +207,6 @@ static bool prv_should_enlarge_emoji(NotificationLayout *layout) { return (!layout->info.show_notification_timestamp && prv_get_emoji_icon(layout) != INVALID_RESOURCE); } -#endif //! Creates a GTextNode view node representing the inner content of the notification //! @param layout NotificationLayout of the notification @@ -274,7 +255,6 @@ static NOINLINE GTextNode *prv_create_view(NotificationLayout *layout, bool use_ .text.extent.offset.y = style->subtitle_upper_padding, .text.extent.margin.h = style->subtitle_upper_padding + style->subtitle_lower_padding, }; -#if !PLATFORM_TINTIN const LayoutNodeIconConfig body_icon_config = { .extent.node.type = LayoutNodeType_Icon, .res_info = &(AppResourceInfo) { @@ -286,7 +266,6 @@ static NOINLINE GTextNode *prv_create_view(NotificationLayout *layout, bool use_ .extent.offset.y = style->body_icon_offset, .extent.margin.h = style->body_icon_margin, }; -#endif const LayoutNodeTextAttributeConfig location_config = { .attr_id = AttributeIdLocationName, .text.style_font = TextStyleFont_Footer, @@ -319,11 +298,9 @@ static NOINLINE GTextNode *prv_create_view(NotificationLayout *layout, bool use_ notification_timestamp_node_config = ¬ification_timestamp_config.text.extent.node; header_node_config = &header_config.text.extent.node; } -#if !PLATFORM_TINTIN if (!layout->info.show_notification_timestamp && PBL_IF_RECT_ELSE(use_body_icon, true)) { notification_timestamp_node_config = NULL; } -#endif const LayoutNodeConfig * const vertical_config_nodes[] = { reminder_timestamp_node_config, #if PBL_ROUND @@ -333,12 +310,8 @@ static NOINLINE GTextNode *prv_create_view(NotificationLayout *layout, bool use_ &title_config.text.extent.node, &subtitle_config.text.extent.node, &location_config.text.extent.node, -#if PLATFORM_TINTIN - &body_config.text.extent.node, -#else use_body_icon ? &body_icon_config.extent.node : &body_config.text.extent.node, -#endif &headings_paragraphs_node.extent.node, #if PBL_RECT notification_timestamp_node_config, @@ -356,10 +329,8 @@ static NOINLINE GTextNode *prv_create_view(NotificationLayout *layout, bool use_ static void prv_destroy_view(NotificationLayout *layout) { graphics_text_node_destroy(layout->view_node); layout->view_node = NULL; -#if !PLATFORM_TINTIN kino_layer_destroy(layout->detail_icon_layer); layout->detail_icon_layer = NULL; -#endif } //! Do common init related tasks @@ -394,9 +365,6 @@ static void prv_card_init(NotificationLayout *layout, AttributeList *attributes, } static void NOINLINE prv_init_view(NotificationLayout *layout) { -#if PLATFORM_TINTIN - layout->view_node = prv_create_view(layout, false /* use_body_icon */); -#else const bool use_body_icon = prv_should_enlarge_emoji(layout); layout->view_node = prv_create_view(layout, use_body_icon); @@ -409,10 +377,8 @@ static void NOINLINE prv_init_view(NotificationLayout *layout) { prv_destroy_view(layout); layout->view_node = prv_create_view(layout, false /* use_body_icon */); } else { - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_JUMBOJI_COUNT, AnalyticsClient_System); } } -#endif } #if PBL_ROUND @@ -433,30 +399,43 @@ static CONST_FUNC int32_t prv_interpolate_linear(int32_t out_min, int32_t out_ma return out_min + (out_max - out_min) * (progress - in_min) / (in_max - in_min); } +// Effective notification status bar height: taller when the "Big & Bold" clock +// style is selected, otherwise the default. The content top is pushed down by +// this amount, so the banner clip math must match it. +static int16_t prv_notif_status_bar_height(void) { + return (alerts_preferences_get_notification_status_bar_style() == + NotificationStatusBarStyle_LargeBold) + ? STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT + : STATUS_BAR_LAYER_HEIGHT; +} + static void prv_draw_banner_round(NotificationLayout *notification_layout, GContext *ctx, const GRect *const notification_layout_frame, LayoutColors colors) { // We use DISP_ROWS and DISP_COLS instead of the layer's frame or bounds because the // notification layout's frame is not the same size as the display const int32_t half_screen_width = DISP_COLS / 2; + const int16_t status_bar_height = prv_notif_status_bar_height(); + // The y-position of a layout frame when its banner is peeking + const int32_t banner_peek_static_y = DISP_ROWS - status_bar_height; graphics_context_set_fill_color(ctx, colors.bg_color); const int32_t saved_clip_box_size_h = ctx->draw_state.clip_box.size.h; const int32_t saved_clip_box_origin_y = ctx->draw_state.clip_box.origin.y; ctx->draw_state.clip_box.origin.y = - MAX(ctx->draw_state.clip_box.origin.y - STATUS_BAR_LAYER_HEIGHT, 0); + MAX(ctx->draw_state.clip_box.origin.y - status_bar_height, 0); ctx->draw_state.clip_box.size.h = DISP_ROWS; grect_clip(&ctx->draw_state.clip_box, &DISP_FRAME); const int32_t banner_movement_raw_offset = - CLIP(BANNER_PEEK_STATIC_Y - notification_layout_frame->origin.y, - 0, BANNER_PEEK_STATIC_Y); + CLIP(banner_peek_static_y - notification_layout_frame->origin.y, + 0, banner_peek_static_y); const int32_t banner_radius = prv_interpolate_linear(BOTTOM_BANNER_CIRCLE_RADIUS, BANNER_CIRCLE_RADIUS, - 0, BANNER_PEEK_STATIC_Y, + 0, banner_peek_static_y, banner_movement_raw_offset); const int32_t banner_diameter = banner_radius * 2; const int32_t banner_center_y = prv_interpolate_linear(0, LAYOUT_TOP_BANNER_ORIGIN_Y, - 0, BANNER_PEEK_STATIC_Y, + 0, banner_peek_static_y, banner_movement_raw_offset); const GRect banner_frame = GRect(half_screen_width - banner_radius, banner_center_y - banner_radius, @@ -675,11 +654,6 @@ static void prv_layout_init(NotificationLayout *layout, const LayoutLayerConfig layer_mark_dirty(&(layout->layout.layer)); } -#else -LayoutLayer *notification_layout_create(const LayoutLayerConfig *config) { return NULL; } - -bool notification_layout_verify(bool existing_attributes[]) { return false; } -#endif TimelineResourceId notification_layout_get_fallback_icon_id(TimelineItemType item_type) { return (item_type == TimelineItemTypeNotification) ? NOTIF_FALLBACK_ICON : diff --git a/src/fw/services/normal/timeline/peek.c b/src/fw/services/timeline/peek.c similarity index 92% rename from src/fw/services/normal/timeline/peek.c rename to src/fw/services/timeline/peek.c index 847c654934..2f2ad06986 100644 --- a/src/fw/services/normal/timeline/peek.c +++ b/src/fw/services/timeline/peek.c @@ -1,29 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "peek.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/peek.h" #include "drivers/rtc.h" #include "kernel/event_loop.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" #include "kernel/pebble_tasks.h" -#include "services/common/system_task.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/system_task.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/timeline.h" #include "shell/prefs.h" #include "system/logging.h" #include "system/status_codes.h" diff --git a/src/fw/services/timeline/reminders.c b/src/fw/services/timeline/reminders.c new file mode 100644 index 0000000000..445cb3b195 --- /dev/null +++ b/src/fw/services/timeline/reminders.c @@ -0,0 +1,207 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/reminders.h" + +#include "drivers/rtc.h" +#include "kernel/event_loop.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "kernel/pebble_tasks.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/timeline/item.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + +#define INVALID_SNOOZE_DELAY 0 +#define HALF_SNOOZE_END_MARK 30 // Seconds +#define CONSTANT_SNOOZE_DELAY (10 * SECONDS_PER_MINUTE) // Seconds +#define CONSTANT_SNOOZE_END_MARK (48 * MINUTES_PER_HOUR * SECONDS_PER_MINUTE) // Seconds + +static RegularTimerInfo s_reminder_timer; +static bool s_reminder_armed; +static time_t s_next_reminder_timestamp; +static ReminderId s_next_reminder_id; + +bool reminders_mark_has_reminded(ReminderId *reminder_id); + +static void prv_put_reminder_event(ReminderId *reminder_id, ReminderEventType type) { + Uuid *removed_id = kernel_malloc(sizeof(Uuid)); + if (!removed_id) { + return; + } + + *removed_id = *reminder_id; + PebbleEvent event = { + .type = PEBBLE_REMINDER_EVENT, + .reminder = { + .type = type, + .reminder_id = removed_id, + } + }; + event_put(&event); +} + +void reminders_handle_reminder_updated(const Uuid *reminder_id) { + prv_put_reminder_event((ReminderId *)reminder_id, ReminderUpdated); +} + +void reminders_handle_reminder_removed(const Uuid *reminder_id) { + prv_put_reminder_event((ReminderId *)reminder_id, ReminderRemoved); +} + +static void prv_trigger_reminder_system_task_callback(void *data) { + ReminderId *item_id = (ReminderId *)data; + + // Mark that we are about to display the reminder + if (!reminders_mark_has_reminded(item_id)) { + return; + } + + prv_put_reminder_event(item_id, ReminderTriggered); + reminders_update_timer(); +} + +// Polls once per second against the RTC. Same pattern cron uses internally, +// avoids FreeRTOS-tick drift relative to wall-clock. +static void prv_timer_callback(void *data) { + if (!s_reminder_armed) { + return; + } + if (s_next_reminder_timestamp > rtc_get_time()) { + return; + } + if (system_task_add_callback(prv_trigger_reminder_system_task_callback, + &s_next_reminder_id)) { + s_reminder_armed = false; + } +} + +static status_t prv_set_timer(Reminder *item) { + s_next_reminder_id = item->header.id; + s_next_reminder_timestamp = item->header.timestamp; + s_reminder_armed = true; + PBL_LOG_DBG("Set reminder for %ld", s_next_reminder_timestamp); + return S_SUCCESS; +} + +status_t reminders_update_timer(void) { + PBL_LOG_DBG("Attempting to update timer."); + s_reminder_armed = false; + + TimelineItem item = {{{0}}}; + status_t rv = reminder_db_next_item_header(&item); + if (rv == S_NO_MORE_ITEMS) { + PBL_LOG_DBG("No more reminders to add to queue."); + return S_SUCCESS; + } else if (rv) { + return rv; + } + + return prv_set_timer(&item); +} + +status_t reminders_insert(Reminder *reminder) { + status_t rv = reminder_db_insert_item(reminder); + return rv; +} + +status_t reminders_init(void) { + if (s_reminder_timer.cb == NULL) { + s_reminder_timer.cb = prv_timer_callback; + regular_timer_add_seconds_callback(&s_reminder_timer); + } + return reminders_update_timer(); +} + +status_t reminders_delete(ReminderId *reminder_id) { + return reminder_db_delete_item(reminder_id, true /* send_event */); +} + +T_STATIC uint32_t prv_calculate_snooze_delay(TimelineItem *item) { + time_t current_time_utc = rtc_get_time(); + time_t reminder_time_utc = item->header.timestamp; + if (current_time_utc <= reminder_time_utc) { + return INVALID_SNOOZE_DELAY; + } + + uint32_t snooze_delay; + + // Get parent pin + const TimelineItemId *parent_id = &item->header.parent_id; + TimelineItem parent_item; + status_t status = pin_db_get(parent_id, &parent_item); + if (status != S_SUCCESS) { + return INVALID_SNOOZE_DELAY; + } + + // Snooze logic: + // If current_time is more than HALF_SNOOZE_END_MARK before event_time, snooze for half the + // remaining time until the event. + // If current_time is less than HALF_SNOOZE_END_MARK before event_time, and not more than + // CONSTANT_SNOOZE_END_MARK after event_time, snooze for CONSTANT_SNOOZE_DELAY. + // If current_time is more than CONSTANT_SNOOZE_END_MARK after event_time, don't snooze. + time_t event_time_utc = parent_item.header.timestamp; + if (event_time_utc > current_time_utc && + event_time_utc - current_time_utc > HALF_SNOOZE_END_MARK) { + // Half-time snooze + snooze_delay = (event_time_utc - reminder_time_utc) / 2; + } else if (current_time_utc > event_time_utc && + current_time_utc - event_time_utc > CONSTANT_SNOOZE_END_MARK) { + // Stop snoozing + snooze_delay = INVALID_SNOOZE_DELAY; + } else { + // Constant-time snooze + snooze_delay = CONSTANT_SNOOZE_DELAY; + } + + timeline_item_free_allocated_buffer(&parent_item); + return snooze_delay; +} + +bool reminders_can_snooze(Reminder *reminder) { + return (prv_calculate_snooze_delay((TimelineItem *)reminder) > 0); +} + +status_t reminders_snooze(Reminder *reminder) { + uint32_t snooze_delay = prv_calculate_snooze_delay((TimelineItem *)reminder); + if (snooze_delay == 0) { + return E_INVALID_OPERATION; + } + + // Modify reminder timestamp + TimelineItem *item = (TimelineItem*) reminder; + item->header.timestamp = rtc_get_time() + (time_t) snooze_delay; + + // Unset the reminded status + item->header.reminded = false; + + // Reinsert the reminder + return reminders_insert(reminder); +} + +// only used for tests +RegularTimerInfo *get_reminder_timer(void) { + return &s_reminder_timer; +} + +bool get_reminder_armed(void) { + return s_reminder_armed; +} + +time_t get_reminder_timestamp(void) { + return s_next_reminder_timestamp; +} + +ReminderId *get_reminder_id(void) { + return &s_next_reminder_id; +} + +bool reminders_mark_has_reminded(ReminderId *reminder_id) { + status_t rv = reminder_db_set_status_bits(reminder_id, TimelineItemStatusReminded); + return (rv == S_SUCCESS); +} diff --git a/src/fw/services/normal/timeline/sports_layout.c b/src/fw/services/timeline/sports_layout.c similarity index 92% rename from src/fw/services/normal/timeline/sports_layout.c rename to src/fw/services/timeline/sports_layout.c index 58cfc00527..667ca7805d 100644 --- a/src/fw/services/normal/timeline/sports_layout.c +++ b/src/fw/services/timeline/sports_layout.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "sports_layout.h" -#include "timeline_layout.h" +#include "pbl/services/timeline/sports_layout.h" +#include "pbl/services/timeline/timeline_layout.h" #include "applib/fonts/fonts.h" #include "applib/graphics/gtypes.h" @@ -25,8 +12,8 @@ #include "font_resource_keys.auto.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" #include "util/size.h" #include "util/string.h" @@ -41,7 +28,6 @@ _Static_assert(AttributeIdRecordAway + 1 == AttributeIdRecordHome, _Static_assert(AttributeIdScoreAway + 1 == AttributeIdScoreHome, "Sports layout requires that all Home attributes are directly after Away"); -#if !TINTIN_FORCE_FIT ////////////////////////////////////////// // Card Mode ////////////////////////////////////////// @@ -287,8 +273,3 @@ LayoutLayer *sports_layout_create(const LayoutLayerConfig *config) { return (LayoutLayer *)layout; } -#else -LayoutLayer *sports_layout_create(const LayoutLayerConfig *config) { return NULL; } - -bool sports_layout_verify(bool existing_attributes[]) { return false; } -#endif diff --git a/src/fw/services/normal/timeline/swap_layer.c b/src/fw/services/timeline/swap_layer.c similarity index 97% rename from src/fw/services/normal/timeline/swap_layer.c rename to src/fw/services/timeline/swap_layer.c index bfcae8f612..25b2686bd0 100644 --- a/src/fw/services/normal/timeline/swap_layer.c +++ b/src/fw/services/timeline/swap_layer.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "swap_layer.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/swap_layer.h" #include "applib/applib_malloc.auto.h" #include "applib/graphics/graphics.h" @@ -24,9 +11,9 @@ #include "applib/ui/shadows.h" #include "applib/ui/window.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/layout_layer.h" -#include "services/normal/timeline/notification_layout.h" -#include "services/normal/notifications/alerts_preferences_private.h" +#include "pbl/services/timeline/layout_layer.h" +#include "pbl/services/timeline/notification_layout.h" +#include "pbl/services/notifications/alerts_preferences_private.h" #include "kernel/ui/kernel_ui.h" #include "process_state/app_state/app_state.h" #include "system/logging.h" diff --git a/src/fw/services/timeline/timeline.c b/src/fw/services/timeline/timeline.c new file mode 100644 index 0000000000..af0b96c896 --- /dev/null +++ b/src/fw/services/timeline/timeline.c @@ -0,0 +1,965 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/timeline.h" + +#include "util/uuid.h" +#include "applib/ui/status_bar_layer.h" +#include "apps/system_app_ids.h" +#include "apps/system/timeline/timeline.h" +#include "apps/system/timeline/pin_window.h" +#include "comm/ble/kernel_le_client/ancs/ancs.h" +#include "comm/ble/kernel_le_client/ancs/ancs_types.h" +#include "drivers/rtc.h" +#include "kernel/event_loop.h" +#include "kernel/pbl_malloc.h" +#include "kernel/ui/modals/modal_manager.h" +#include "process_management/app_install_manager.h" +#include "process_management/app_manager.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/phone_call_util.h" +#include "pbl/services/timeline/actions_endpoint.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/list.h" +#include "util/math.h" +#include "util/order.h" +#include "util/size.h" +#include "util/time/time.h" + +PBL_LOG_MODULE_DEFINE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + +struct TimelineNode { + ListNode node; + int index; + Uuid id; + time_t timestamp; + uint16_t duration; + bool all_day; +}; + +static uint32_t i18n_key; + +#define TIMELINE_FUTURE_WINDOW (3 * SECONDS_PER_DAY) +#define TIMELINE_PAST_WINDOW (2 * SECONDS_PER_DAY) + +static bool s_bulk_action_mode = false; + +///////////////////////// +// Timeline Iterator +///////////////////////// + +// Order of events in timeline: +// * All day events appear first +// * (All day events should be timestamped at midnight, the first second in the day) +// * Order all other events by time +// * For concurrent events: order by duration (shortest to longest), then by (TODO) A-Z +// * Events that occur now appear both in timeline past and timeline future until event ends +static int prv_time_comparator(void *a, void *b) { + TimelineNode *node_a = (TimelineNode *)a; + TimelineNode *node_b = (TimelineNode *)b; + if (node_b->timestamp == node_a->timestamp) { + if (node_b->all_day) { + return -1; + } else if (node_a->all_day) { + return 1; + } else { + return (node_b->duration - node_a->duration); + } + } else { + return (node_b->timestamp - node_a->timestamp); + } +} + +static bool prv_filter(ListNode *found_node, void *data) { + TimelineNode *node = (TimelineNode *)found_node; + Uuid *uuid = (Uuid *)data; + return uuid_equal(&node->id, uuid); +} + +static TimelineNode *prv_find_by_uuid(TimelineNode *head, Uuid *uuid) { + TimelineNode *node = (TimelineNode *)list_find((ListNode *)head, prv_filter, uuid); + return node; +} + +static bool prv_is_in_window(time_t node_timestamp, uint16_t node_duration, time_t timestamp) { + time_t future_window = time_util_get_midnight_of(timestamp + TIMELINE_FUTURE_WINDOW); + time_t past_window = time_util_get_midnight_of(timestamp - TIMELINE_PAST_WINDOW); + time_t end_time = node_timestamp + (node_duration * SECONDS_PER_MINUTE); + time_t start_time = node_timestamp; + + return !(start_time >= future_window || end_time < past_window); +} + +static bool prv_show_event(TimelineNode *node, time_t timestamp, time_t midnight, + TimelineIterDirection direction, bool show_all_day_events) { + // hide events outside of the window + if (!prv_is_in_window(node->timestamp, node->duration, timestamp)) { + return false; + } + + // An event is in future until it ends + const time_t fudge_time = node->duration * SECONDS_PER_MINUTE; + // deal with all day events + if (node->all_day && node->timestamp == midnight) { + return show_all_day_events; + } else if (direction == TimelineIterDirectionFuture) { + return (node->timestamp >= timestamp - fudge_time); + } else { // direction == TimelineIterDirectionPast + return (node->timestamp < timestamp - fudge_time); + } +} + +// All day events show up in future if no timed events have passed today, +// i.e. no events exist between midnight today and now +// iterate and figure out if we had a timed event pass today +static bool prv_should_show_all_day_events(TimelineNode *head, time_t now, time_t today_midnight, + TimelineIterDirection direction) { + TimelineNode *current = head; + // show in future / hide in past all day events unless we find a timed event + // between midnight and now + bool show = direction == TimelineIterDirectionFuture; + while (current) { + if (current->timestamp > now) { + break; + } + if (!current->all_day && (current->timestamp >= today_midnight)) { + show = !show; + break; + } + current = (TimelineNode *)current->node.next; + } + return show; +} + +static TimelineNode *prv_find_first_past(TimelineNode *head, time_t timestamp, + time_t today_midnight, bool show_all_day_events) { + TimelineNode *current = (TimelineNode *)list_get_tail((ListNode *)head); + while (current) { + if (prv_show_event(current, timestamp, today_midnight, TimelineIterDirectionPast, + show_all_day_events)) { + break; + } + current = (TimelineNode *)current->node.prev; + } + return current; +} + +static TimelineNode *prv_find_first_future(TimelineNode *head, time_t timestamp, + time_t today_midnight, bool show_all_day_events) { + TimelineNode *current = head; + while (current) { + if (prv_show_event(current, timestamp, today_midnight, TimelineIterDirectionFuture, + show_all_day_events)) { + break; + } + current = (TimelineNode *)current->node.next; + } + return current; +} + +static TimelineNode *prv_find_first(TimelineNode *head, TimelineIterDirection direction, + time_t timestamp, time_t today_midnight, bool show_all_day_events) { + if (direction == TimelineIterDirectionPast) { + return prv_find_first_past(head, timestamp, today_midnight, show_all_day_events); + } else { + return prv_find_first_future(head, timestamp, today_midnight, show_all_day_events); + } +} + +static void prv_remove_node(TimelineNode **head, TimelineNode *node) { + list_remove((ListNode *)node, (ListNode **)head, NULL); + task_free(node); +} + +static int prv_num_nodes_for_serialized_item(CommonTimelineItemHeader *header) { + int num_days; + if (header->all_day) { + num_days = header->duration ? (header->duration + MINUTES_PER_DAY - 1) / MINUTES_PER_DAY : 1; + } else { + // The span is the time between 0:00 on the first day of the event + // and 24:00 on the last day of the event + const time_t start_span = time_util_get_midnight_of(header->timestamp - SECONDS_PER_DAY + 1); + const time_t end_span = time_util_get_midnight_of(header->timestamp + header->duration * + SECONDS_PER_MINUTE - 1); + const time_t full_span = end_span - start_span; + num_days = full_span / SECONDS_PER_DAY; + } + return MAX(num_days, 1); +} + +static void prv_set_nodes(TimelineNode *nodes[], CommonTimelineItemHeader *header, int num_nodes) { + // Multiday events: + // first day: timestamp at beginning of event, duration for rest of the day + // middle days: all day events + // end days: event at end time + // + // single event: + // timestamp, duration are same as the original item + nodes[0]->timestamp = timeline_item_get_tz_timestamp(header); + const time_t midnight_first = time_util_get_midnight_of(nodes[0]->timestamp); + if (num_nodes == 1) { + nodes[0]->duration = header->duration; + } else { + // first item has correct timestamp, duration should make it last for the rest of the day + const time_t until_midnight = midnight_first + SECONDS_PER_DAY - header->timestamp; + nodes[0]->duration = until_midnight / SECONDS_PER_MINUTE; + + // last item at end of event, duration 0 + time_t endtime = header->timestamp + header->duration * SECONDS_PER_MINUTE; + nodes[num_nodes - 1]->timestamp = endtime; + nodes[num_nodes - 1]->duration = 0; + nodes[num_nodes - 1]->all_day = false; + } + nodes[0]->all_day = (nodes[0]->duration == MINUTES_PER_DAY && + nodes[0]->timestamp == midnight_first); + + // middle days are all day events + time_t midnight = time_util_get_midnight_of(header->timestamp); + for (int i = 1; i < num_nodes - 1; i++) { + midnight += SECONDS_PER_DAY; + nodes[i]->timestamp = midnight; + nodes[i]->duration = MINUTES_PER_DAY; + nodes[i]->all_day = true; + } +} + +static void prv_set_nodes_all_day(TimelineNode *nodes[], CommonTimelineItemHeader *header, + int num_nodes) { + // Multiday events: + // Each day is an all day event + + time_t midnight; + // iOS doesn't correctly send the timestamp at UTC midnight, rather it sends it in local time + if (header->timestamp % SECONDS_PER_DAY != 0) { + // NOT at UTC midnight, so presumably an iOS bug + midnight = time_util_get_midnight_of(header->timestamp); + } else { + midnight = timeline_item_get_tz_timestamp(header); + } + for (int i = 0; i < num_nodes; i++) { + nodes[i]->timestamp = midnight; + nodes[i]->duration = MINUTES_PER_DAY; + nodes[i]->all_day = true; + midnight += SECONDS_PER_DAY; + } +} + +static void prv_add_nodes_for_serialized_item(TimelineNode **list_head, + CommonTimelineItemHeader *header) { + int num_nodes = prv_num_nodes_for_serialized_item(header); + TimelineNode *nodes[num_nodes]; + + // copy UUID to all the nodes + for (int i = 0; i < num_nodes; i++) { + // each node requires its own malloc because each node must be individually free-able + nodes[i] = task_malloc_check(sizeof(TimelineNode)); + *(nodes[i]) = (TimelineNode){}; + nodes[i]->id = header->id; + } + + if (header->all_day) { + prv_set_nodes_all_day(nodes, header, num_nodes); + } else { + prv_set_nodes(nodes, header, num_nodes); + } + + for (int i = 0; i < num_nodes; i++) { + *list_head = (TimelineNode *)list_sorted_add((ListNode *)*list_head, (ListNode *)nodes[i], + prv_time_comparator, true); + } +} + +static bool prv_each(SettingsFile *file, SettingsRecordInfo *info, void *context) { + // check entry is valid + if (info->key_len != UUID_SIZE || info->val_len == 0) { + return true; // continue iteration + } + + TimelineNode **list_head = context; + + CommonTimelineItemHeader header; + // we don't care about the attributes here, so we don't allocate space for them + info->get_val(file, (uint8_t *)&header, sizeof(CommonTimelineItemHeader)); + // Flags & Status are stored inverted. + header.flags = ~header.flags; + header.status = ~header.status; + + prv_add_nodes_for_serialized_item(list_head, &header); + + return true; // continue iteration +} +static void prv_set_indices(TimelineNode *timeline) { + TimelineNode *node = timeline; + int index = 0; + while (node) { + node->index = index; + index++; + node = (TimelineNode *)list_get_next((ListNode *)node); + } +} + +static int prv_first_event_comparator(TimelineNode *new_node, TimelineNode *old_node, + TimelineIterDirection direction) { + const time_t now = rtc_get_time(); + const time_t midnight = time_util_get_midnight_of(now); + const bool show_all_day = false; + const bool show_new = prv_show_event(new_node, now, midnight, direction, show_all_day); + const bool show_old = prv_show_event(old_node, now, midnight, direction, show_all_day); + if (show_new != show_old) { + return (show_old ? 1 : 0) - (show_new ? 1 : 0); + } else { + return prv_time_comparator(old_node, new_node); + } +} + +static void prv_set_node_from_header(CommonTimelineItemHeader *header, TimelineNode *node_out) { + prv_set_nodes(&(TimelineNode *) { node_out }, header, 1 /* num_nodes */); +} + +int timeline_item_time_comparator(CommonTimelineItemHeader *new_common, + CommonTimelineItemHeader *old_common, + TimelineIterDirection direction) { + TimelineNode new_node; + TimelineNode old_node; + prv_set_node_from_header(new_common, &new_node); + prv_set_node_from_header(old_common, &old_node); + return prv_first_event_comparator(&new_node, &old_node, direction); +} + +bool timeline_item_should_show(CommonTimelineItemHeader *header, TimelineIterDirection direction) { + TimelineNode node; + prv_set_node_from_header(header, &node); + const time_t now = rtc_get_time(); + const time_t midnight = time_util_get_midnight_of(now); + return prv_show_event(&node, now, midnight, direction, false /* show_all_day */); +} + +#ifdef TIMELINE_SERVICE_DEBUG +static void prv_debug_print_pins(TimelineNode *node0) { + TimelineNode *node = (TimelineNode *)list_get_head((ListNode *)node0); + PBL_LOG_DBG("= = = = = = = ="); + while (node) { + PBL_LOG_DBG("======"); + PBL_LOG_DBG("Node with id %x%x...", node->id.byte0, node->id.byte1); + PBL_LOG_DBG("Index %d", node->index); + PBL_LOG_DBG("Timestamp %ld", node->timestamp); + PBL_LOG_DBG("Duration %hu", node->duration); + PBL_LOG_DBG("All day? %s", node->all_day ? "True": "False"); + PBL_LOG_DBG("Address %p", node); + node = (TimelineNode *)node->node.next; + } +} +#endif + +// dummy iterator that always returns false +// Useful for when there aren't any items in pindb +// but we don't want an invalid iterator. +static bool prv_iter_dummy(IteratorState state) { + return false; +} + +static bool prv_iter_next(IteratorState state) { + TimelineIterState *timeline_iter_state = (TimelineIterState *)state; + if (timeline_iter_state->node == NULL) { + return false; + } + // keep a copy of the original node in case we go to the end without finding a new valid node + TimelineNode *orig = timeline_iter_state->node; + do { + timeline_iter_state->node = (TimelineNode *)timeline_iter_state->node->node.next; + if (timeline_iter_state->node == NULL) { + timeline_iter_state->node = orig; + return false; + } + } while (!prv_show_event(timeline_iter_state->node, timeline_iter_state->start_time, + timeline_iter_state->midnight, timeline_iter_state->direction, + timeline_iter_state->show_all_day_events) || + !timeline_exists(&timeline_iter_state->node->id)); + + timeline_item_free_allocated_buffer(&timeline_iter_state->pin); + timeline_iter_state->pin = (TimelineItem){}; + + status_t rv = pin_db_get(&timeline_iter_state->node->id, &timeline_iter_state->pin); + timeline_iter_state->current_day = time_util_get_midnight_of( + timeline_iter_state->node->timestamp); + timeline_iter_state->index = timeline_iter_state->node->index; +#ifdef TIMELINE_SERVICE_DEBUG + prv_debug_print_pins(timeline_iter_state->node); +#endif + return (rv == S_SUCCESS); +} + +static bool prv_iter_prev(IteratorState state) { + TimelineIterState *timeline_iter_state = (TimelineIterState *)state; + // at the past-most item + if (timeline_iter_state->node == NULL) { + return false; + } + TimelineNode *orig = timeline_iter_state->node; + do { + timeline_iter_state->node = (TimelineNode *)timeline_iter_state->node->node.prev; + if (timeline_iter_state->node == NULL) { + timeline_iter_state->node = orig; + return false; + } + } while (!prv_show_event(timeline_iter_state->node, timeline_iter_state->start_time, + timeline_iter_state->midnight, timeline_iter_state->direction, + timeline_iter_state->show_all_day_events) || + !timeline_exists(&timeline_iter_state->node->id)); + + timeline_item_free_allocated_buffer(&timeline_iter_state->pin); + timeline_iter_state->pin = (TimelineItem){}; + + status_t rv = pin_db_get(&timeline_iter_state->node->id, &timeline_iter_state->pin); + timeline_iter_state->current_day = time_util_get_midnight_of( + timeline_iter_state->node->timestamp); + timeline_iter_state->index = timeline_iter_state->node->index; +#ifdef TIMELINE_SERVICE_DEBUG + prv_debug_print_pins(timeline_iter_state->node); +#endif + return (rv == S_SUCCESS); +} + +static void prv_prune_ordered_timeline_list(TimelineNode **head) { + TimelineNode *node = *head; + TimelineNode *next_node; + while (node) { + next_node = (TimelineNode *)list_get_next((ListNode *)node); + time_t end_time = node->timestamp + (node->duration * SECONDS_PER_MINUTE); + if (pin_db_has_entry_expired(end_time)) { + // remove the pin without emitting an event + pin_db_delete((uint8_t *)&node->id, sizeof(Uuid)); + + // remove the node from out list + timeline_iter_remove_node(head, node); + } else { + break; // the list is ordered so we are done + } + node = next_node; + } +} + +static void prv_put_outgoing_call_event(uint32_t call_identifier, const char *caller_id) { + PebbleEvent event = { + .type = PEBBLE_PHONE_EVENT, + .phone = { + .type = PhoneEventType_Outgoing, + .source = PhoneCallSource_ANCS_Legacy, + .call_identifier = call_identifier, + .caller = phone_call_util_create_caller(caller_id, NULL), + } + }; + + event_put(&event); +} + +/////////////////////////////////////////////////// +// Public functions +////////////////////////////////////////////////// + +status_t timeline_init(TimelineNode **timeline) { + PBL_LOG_DBG("Starting to build list."); + status_t rv = pin_db_each(prv_each, timeline); + prv_prune_ordered_timeline_list(timeline); + prv_set_indices(*timeline); + PBL_LOG_DBG("Finished building list."); +#ifdef TIMELINE_SERVICE_DEBUG + prv_debug_print_pins(*timeline); +#endif + return rv; +} + +bool timeline_add(TimelineItem *item) { + return (S_SUCCESS == pin_db_insert_item(item)); +} + +bool timeline_exists(Uuid *id) { + return (pin_db_get_len((uint8_t *)id, UUID_SIZE) > 0); +} + +bool timeline_remove(const Uuid *id) { + // Use BlobDB directly in order to emit the BlobDB delete event + return (S_SUCCESS == blob_db_delete(BlobDBIdPins, (uint8_t *)id, UUID_SIZE)); +} + +TimelineIterDirection timeline_direction_for_item(TimelineItem *item, + TimelineNode *timeline, time_t now) { + if (item->header.all_day) { + time_t today_midnight = time_util_get_midnight_of(now); + if (today_midnight > item->header.timestamp || + prv_should_show_all_day_events(timeline, now, today_midnight, TimelineIterDirectionPast)) { + return TimelineIterDirectionPast; + } else { + return TimelineIterDirectionFuture; + } + } else if (item->header.timestamp < now) { + return TimelineIterDirectionPast; + } else { + return TimelineIterDirectionFuture; + } +} + +bool timeline_nodes_equal(TimelineNode *a, TimelineNode *b) { + if (a == NULL || b == NULL) { + return (a == b); + } + return (uuid_equal(&a->id, &b->id) && (a->timestamp == b->timestamp)); +} + +bool timeline_get_originator_id(const TimelineItem *item, Uuid *uuid) { + TimelineItem pin = {}; + const TimelineItem *pin_p; + + switch (item->header.type) { + case TimelineItemTypeReminder: + // Follow the parent id to get to the owner pin + if (pin_db_get(&item->header.parent_id, &pin) != S_SUCCESS) { + *uuid = UUID_INVALID; + return false; + } + pin_p = &pin; + break; + case TimelineItemTypePin: + case TimelineItemTypeNotification: + // Some notifications have parent pins, some don't. If this one has a parent pin, follow it + if (pin_db_get(&item->header.parent_id, &pin) == S_SUCCESS) { + pin_p = &pin; + } else { + pin_p = item; + } + break; + default: + // Invalid item type + *uuid = UUID_INVALID; + return false; + } + + *uuid = pin_p->header.parent_id; + if (pin_p == &pin) { + timeline_item_free_allocated_buffer(&pin); + } + return true; +} + + +// +// Iter functions +// + +// return true if removed a node, false if non left +void timeline_iter_remove_node(TimelineNode **head, TimelineNode *node) { + PBL_ASSERTN(node); + prv_remove_node(head, node); +} + +// return true if removed a node, false if non left +bool timeline_iter_remove_node_with_id(TimelineNode **head, Uuid *key) { + // potentially more than one item with this UUID key since multiday events + TimelineNode *node = prv_find_by_uuid(*head, key); + if (node) { + timeline_iter_remove_node(head, node); + return true; + } else { + return false; + } +} + +status_t timeline_iter_init(Iterator *iter, TimelineIterState *iter_state, TimelineNode **head, + TimelineIterDirection direction, time_t timestamp) { + iter_state->direction = direction; + iter_state->start_time = timestamp; + iter_state->midnight = time_util_get_midnight_of(timestamp); + iter_state->current_day = iter_state->midnight; + iter_state->show_all_day_events = prv_should_show_all_day_events(*head, timestamp, + iter_state->midnight, direction); + TimelineNode *node = prv_find_first(*head, direction, timestamp, iter_state->midnight, + iter_state->show_all_day_events); + if (node == NULL) { + iter_init(iter, prv_iter_dummy, prv_iter_dummy, iter_state); + return S_NO_MORE_ITEMS; + } + + status_t rv = pin_db_get(&node->id, &iter_state->pin); + if (rv != S_SUCCESS) { + iter_state->pin = (TimelineItem){}; + iter_init(iter, prv_iter_dummy, prv_iter_dummy, iter_state); + return rv; + } + + iter_state->node = node; + iter_state->current_day = time_util_get_midnight_of(node->timestamp); + iter_state->index = iter_state->node->index; + if (direction == TimelineIterDirectionPast) { + iter_init(iter, prv_iter_prev, prv_iter_next, iter_state); + } else { // Future + iter_init(iter, prv_iter_next, prv_iter_prev, iter_state); + } + + return rv; +} + +void timeline_iter_copy_state(TimelineIterState *dst_state, TimelineIterState *src_state, + Iterator *dst_iter, Iterator *src_iter) { + PBL_ASSERTN(dst_state && src_state && dst_iter && src_iter); + + timeline_item_free_allocated_buffer(&dst_state->pin); + + *dst_state = *src_state; + dst_state->pin = (TimelineItem){}; + + *dst_iter = *src_iter; + dst_iter->state = dst_state; +} + +void timeline_iter_deinit(Iterator *iter, TimelineIterState *iter_state, TimelineNode **head) { + TimelineNode *node = *head; + while (node) { + TimelineNode *old = node; + node = (TimelineNode *)node->node.next; + prv_remove_node(head, old); + } + *head = NULL; + + // free the currently allocated item in the iterator + timeline_item_free_allocated_buffer(&iter_state->pin); + iter_init(iter, prv_iter_dummy, prv_iter_dummy, iter_state); +} + +void timeline_iter_refresh_pin(TimelineIterState *iter_state) { + // no-op if the item doesn't exist + if (timeline_exists(&iter_state->pin.header.id)) { + timeline_item_free_allocated_buffer(&iter_state->pin); + Uuid id = iter_state->pin.header.id; + iter_state->pin = (TimelineItem){}; + pin_db_get(&id, &iter_state->pin); + } +} + +bool timeline_add_missed_call_pin(TimelineItem *pin, uint32_t uid) { + uuid_generate(&pin->header.id); + pin->header.layout = LayoutIdGeneric; + pin->header.type = TimelineItemTypePin; + pin->header.from_watch = true; + pin->header.ancs_uid = uid; + + // patch the dismiss action to be a remove action + TimelineItemAction *remove_action = timeline_item_find_dismiss_action(pin); + + // TODO: PBL-23915 + // We leak this i18n'd string because not leaking it is really hard. + // We make sure we only ever allocate it once though, so it's not the end of the world. + remove_action->attr_list.attributes[1].cstring = (char*)i18n_get("Remove", &i18n_key); + remove_action->type = TimelineItemActionTypeRemove; + + return timeline_add(pin); +} + +// +// Actions functions +// + +static void prv_put_notification_action_result(const Uuid *id, const char *msg, + uint32_t timeline_res_id, ActionResultType type) { + // send action result event + PebbleSysNotificationActionResult *action_result = + kernel_malloc_check(sizeof(PebbleSysNotificationActionResult) + 2 * sizeof(Attribute)); + + AttributeList result_attributes = { + .num_attributes = 2, + .attributes = (Attribute *)(((uint8_t *)action_result) + sizeof(*action_result)), + }; + result_attributes.attributes[0].id = AttributeIdSubtitle; + result_attributes.attributes[0].cstring = (char *)msg; + result_attributes.attributes[1].id = AttributeIdIconLarge; + result_attributes.attributes[1].uint32 = timeline_res_id; + + *action_result = (PebbleSysNotificationActionResult) { + .id = *id, + .type = type, + .attr_list = result_attributes, + }; + notifications_handle_notification_action_result(action_result); +} + +static void prv_do_remote_action(const Uuid *id, TimelineItemActionType type, + uint8_t action_id, const AttributeList *attributes, + bool do_async) { + if (comm_session_get_system_session()) { + timeline_action_endpoint_invoke_action(id, type, action_id, attributes, do_async); + } else { + // We know we aren't connected, don't wait around for a response that won't come + prv_put_notification_action_result( + id, i18n_get("Can't connect. Relaunch Pebble Time app on phone.", &i18n_key), + TIMELINE_RESOURCE_GENERIC_WARNING, ActionResultTypeFailure); + } +} + +static void prv_remove_pin_action(const TimelineItem *item, + const TimelineItemAction *action, + const AttributeList *attributes) { + if (item->header.from_watch) { + // remove it via BlobDB + blob_db_delete(BlobDBIdPins, (uint8_t *)&item->header.id, UUID_SIZE); + + // TODO: PBL-23915 + // We leak this i18n'd string because not leaking it is really hard. + // We make sure we only ever allocate it once though, so it's not the end of the world. + prv_put_notification_action_result(&item->header.id, i18n_get("Removed", &i18n_key), + TIMELINE_RESOURCE_RESULT_DELETED, ActionResultTypeSuccess); + } else { + const bool do_async = true; + prv_do_remote_action(&item->header.id, action->type, + action->id, attributes, do_async); + } +} + +static void prv_dismiss_local_notification_action(const TimelineItem *item) { + // TODO: PBL-23915 + // We leak this i18n'd string because not leaking it is really hard. + // We make sure we only ever allocate it once though, so it's not the end of the world. + prv_put_notification_action_result(&item->header.id, i18n_get("Dismissed", &i18n_key), + TIMELINE_RESOURCE_RESULT_DISMISSED, ActionResultTypeSuccess); +} + +static void prv_perform_ancs_negative_action(const TimelineItem *item, + const TimelineItemAction *action) { + uint8_t action_id = attribute_get_uint8(&action->attr_list, AttributeIdAncsAction, + TIMELINE_INVALID_ACTION_ID); + + // Try to load the ancs id from attributes first in case the item's parent id points to another + // timeline item. If the attribute isn't found, we assume the ancs id is stored in the header + uint32_t ancs_uid = attribute_get_uint32(&action->attr_list, AttributeIdAncsId, + item->header.ancs_uid); + + PBL_LOG_INFO("Perform ancs notification action (%"PRIu32", %"PRIu8")", ancs_uid, + action_id); + ancs_perform_action(ancs_uid, action_id); + + if (!timeline_is_bulk_ancs_action_mode_enabled()) { + // TODO: PBL-23915 + // We leak this i18n'd string because not leaking it is really hard. + // We make sure we only ever allocate it once though, so it's not the end of the world. + char *msg_i18n = i18n_noop("Dismissed"); + int res_id = TIMELINE_RESOURCE_RESULT_DISMISSED; + + if (action->type == TimelineItemActionTypeAncsDelete) { + msg_i18n = i18n_noop("Deleted"); + res_id = TIMELINE_RESOURCE_RESULT_DELETED; + } + prv_put_notification_action_result(&item->header.id, i18n_get(msg_i18n, &i18n_key), res_id, + ActionResultTypeSuccess); + } +} + +static void prv_get_pin_and_push_pin_window(void *data) { + Uuid *parent_id = data; + TimelineItem *pin = task_malloc(sizeof(TimelineItem)); // cleaned-up by the modal + if (pin && pin_db_get(parent_id, pin) == S_SUCCESS) { + timeline_pin_window_push_modal(pin); + } else { + PBL_LOG_ERR("Failed to fetch parent pin"); + } + kernel_free(parent_id); +} + +static void prv_perform_health_response_action(const TimelineItem *item, + const TimelineItemAction *action) { + // TODO: PBL-23915 + // We leak this i18n'd string because not leaking it is really hard. + // We make sure we only ever allocate it once though, so it's not the end of the world. + const char *message = attribute_get_string(&action->attr_list, AttributeIdBody, + (char *)i18n_get("Thanks!", &i18n_key)); + + const uint32_t timeline_res_id = attribute_get_uint32(&action->attr_list, AttributeIdIconLarge, + TIMELINE_RESOURCE_THUMBS_UP); + + prv_put_notification_action_result(&item->header.id, message, timeline_res_id, + ActionResultTypeSuccess); +} + +void timeline_enable_ancs_bulk_action_mode(bool enable) { + s_bulk_action_mode = enable; +} + +bool timeline_is_bulk_ancs_action_mode_enabled(void) { + return s_bulk_action_mode; +} + +typedef struct OpenAppContext { + EventServiceInfo event_info; + AppInstallId install_id; +} OpenAppContext; + +static void prv_app_render_ready(PebbleEvent *e, void *context) { + OpenAppContext *ctx = context; + + if (ctx->install_id == app_manager_get_current_app_id()) { + WindowStack *window_stack = modal_manager_get_window_stack(ModalPriorityNotification); + window_stack_pop_all(window_stack, true /* animated */); + } + + event_service_client_unsubscribe(&ctx->event_info); + kernel_free(ctx); +} + +void timeline_invoke_action(const TimelineItem *item, const TimelineItemAction *action, + const AttributeList *attributes) { + char uuid_buffer[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&item->header.parent_id, uuid_buffer); + + switch (action->type) { + case TimelineItemActionTypeOpenWatchApp: + { + // find parent app + AppInstallId install_id = app_install_get_id_for_uuid(&item->header.parent_id); + if (install_id == INSTALL_ID_INVALID) { + // This should never happen... but we're not quite there yet + PBL_LOG_ERR("Could not find parent app %s for pin", uuid_buffer); + return; + } + // fetch the relevant attribute + const uint32_t launch_code = + attribute_get_uint32(&action->attr_list, AttributeIdLaunchCode, 0); + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = install_id, + .common.args = (void *)(uintptr_t)launch_code, + .common.reason = APP_LAUNCH_TIMELINE_ACTION, + }); + PBL_LOG_INFO("Opening watch app %s", uuid_buffer); + + // Wait for the app we just launched to have something to render before hiding all modals. + // If we don't we'll end up with flashing in a blank framebuffer. + OpenAppContext *ctx = kernel_malloc_check(sizeof(OpenAppContext)); + *ctx = (OpenAppContext) { + .event_info = { + .type = PEBBLE_RENDER_READY_EVENT, + .handler = prv_app_render_ready, + .context = ctx, + }, + .install_id = install_id, + }; + event_service_client_subscribe(&ctx->event_info); + break; + } + case TimelineItemActionTypeOpenPin: + { + Uuid *parent_id = kernel_malloc(sizeof(Uuid)); + if (parent_id) { + *parent_id = item->header.parent_id; + launcher_task_add_callback(prv_get_pin_and_push_pin_window, parent_id); + PBL_LOG_INFO("Opening parent pin %s", uuid_buffer); + } + break; + } + case TimelineItemActionTypeAncsDial: + { + const char *caller_id = attribute_get_string(&item->attr_list, + AttributeIdTitle, "Unknown"); + prv_put_outgoing_call_event(item->header.ancs_uid, caller_id); + notifications_handle_notification_action_result(NULL); + ancs_perform_action(item->header.ancs_uid, ActionIDPositive); + break; + } + // FIXME PBL-18673 this is not necessarily dismiss + case TimelineItemActionTypeAncsPositive: + case TimelineItemActionTypeAncsNegative: + case TimelineItemActionTypeAncsDelete: + { + prv_perform_ancs_negative_action(item, action); + notification_storage_set_status(&item->header.id, TimelineItemStatusDismissed); + break; + } + case TimelineItemActionTypeDismiss: + // This is a notification that was sourced from timeline. The mobile phone does not care + // about dismissing it. We just confirm and dismiss locally. + if (item->header.from_watch || + (((item->header.type == TimelineItemTypeNotification) || + (item->header.type == TimelineItemTypeReminder)) && + !timeline_get_private_data_source((Uuid *)&item->header.parent_id))) { + prv_dismiss_local_notification_action(item); + return; + } + + // FALLTHROUGH + case TimelineItemActionTypeGeneric: + case TimelineItemActionTypeResponse: + case TimelineItemActionTypeAncsResponse: + case TimelineItemActionTypeAncsGeneric: + case TimelineItemActionTypeHttp: + case TimelineItemActionTypeComplete: + case TimelineItemActionTypePostpone: + case TimelineItemActionTypeRemoteRemove: + { + // remote action, send it to the phone + const bool do_async = false; + prv_do_remote_action(&item->header.id, action->type, + action->id, attributes, do_async); + break; + } + case TimelineItemActionTypeRemove: + prv_remove_pin_action(item, action, attributes); + break; + case TimelineItemActionTypeInsightResponse: + prv_perform_health_response_action(item, action); + break; + default: + PBL_LOG_ERR("Action type not implemented: %d", action->type); + break; + } +} + +/////////////////////////////////// +//! Timeline datasource functions +/////////////////////////////////// + +typedef struct { + Uuid id; + const char *name; +} PrivateDataSourceInfo; + +static const PrivateDataSourceInfo s_data_sources[] = { + { + .id = UUID_NOTIFICATIONS_DATA_SOURCE, + .name = i18n_noop("Notifications"), + }, + { + .id = UUID_CALENDAR_DATA_SOURCE, + .name = i18n_noop("Calendar"), + }, + { + .id = UUID_WEATHER_DATA_SOURCE, + .name = i18n_noop("Weather"), + }, + { + .id = UUID_REMINDERS_DATA_SOURCE, + .name = i18n_noop("Reminders"), + }, + { + .id = UUID_ALARMS_DATA_SOURCE, + .name = i18n_noop("Alarms"), + }, + { + .id = UUID_HEALTH_DATA_SOURCE, + .name = i18n_noop("Health"), + }, + { + .id = UUID_INTERCOM_DATA_SOURCE, + .name = i18n_noop("Intercom"), + }, +}; + +const char *timeline_get_private_data_source(Uuid *parent_id) { + for (int i = 0; i < (int) ARRAY_LENGTH(s_data_sources); i++) { + if (uuid_equal(parent_id, &s_data_sources[i].id)) { + return s_data_sources[i].name; + } + } + return NULL; +} \ No newline at end of file diff --git a/src/fw/services/normal/timeline/timeline_actions.c b/src/fw/services/timeline/timeline_actions.c similarity index 93% rename from src/fw/services/normal/timeline/timeline_actions.c rename to src/fw/services/timeline/timeline_actions.c index a5c1be0b46..877c3bb90d 100644 --- a/src/fw/services/normal/timeline/timeline_actions.c +++ b/src/fw/services/timeline/timeline_actions.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_actions.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/timeline_actions.h" #include "applib/applib_malloc.auto.h" #include "applib/event_service_client.h" @@ -38,30 +25,32 @@ #include "popups/ble_hrm/ble_hrm_stop_sharing_popup.h" #include "popups/notifications/notification_window.h" #include "process_state/app_state/app_state.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/session.h" -#include "services/common/event_service.h" -#include "services/common/evented_timer.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/blob_db/reminder_db.h" -#include "services/normal/bluetooth/ble_hrm.h" -#include "services/normal/notifications/action_chaining_window.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/ancs/ancs_notifications.h" -#include "services/normal/notifications/notification_constants.h" -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/event_service.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/bluetooth/ble_hrm.h" +#include "pbl/services/notifications/action_chaining_window.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/ancs/ancs_notifications.h" +#include "pbl/services/notifications/notification_constants.h" +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_resources.h" #include "system/logging.h" #include "system/passert.h" #include "util/size.h" #include "util/struct.h" +PBL_LOG_MODULE_DECLARE(service_timeline, CONFIG_SERVICE_TIMELINE_LOG_LEVEL); + typedef struct { void *action_data; void *context; ActionMenu *action_menu; -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC VoiceWindow *voice_window; #endif EventServiceInfo event_service_info; @@ -126,9 +115,11 @@ static WindowStack *prv_get_window_stack(ActionResultData *data) { static void prv_cleanup_voice_data(VoiceResponseData *data) { event_service_client_unsubscribe(&data->event_service_info); -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC voice_window_destroy(data->voice_window); #endif + // Free the copied TimelineItem that was created in prv_start_voice_reply() + timeline_item_destroy(data->context); applib_free(data); } @@ -146,7 +137,6 @@ static void prv_cleanup_action_result(ActionResultData *data, bool succeeded) { // report to analytics the result of the action if (data->response.attribute.id == AttributeIdTitle && data->response.attribute.cstring) { - analytics_event_canned_response(data->response.attribute.cstring, succeeded); } if (data->action_complete.callback) { @@ -277,7 +267,7 @@ static void prv_timeout_handler(void *context) { // we failed to perform action since we timed out. const char *msg = i18n_noop("Failed"); const bool succeeded = false; - PBL_LOG(LOG_LEVEL_INFO, "Timed out waiting for action result"); + PBL_LOG_INFO("Timed out waiting for action result"); prv_show_result_window_with_progress(data, TIMELINE_RESOURCE_GENERIC_WARNING, msg, succeeded); } @@ -363,7 +353,7 @@ static void prv_handle_chaining_response(ActionResultData *data, PebbleEvent *ev TimelineItem *item = timeline_item_copy(data->chaining_data.notif); if (!item) { - PBL_LOG(LOG_LEVEL_WARNING, "No notification in chaining data"); + PBL_LOG_WRN("No notification in chaining data"); prv_cleanup_action_result(data, false /* succeeded */); return; } @@ -401,7 +391,7 @@ static void prv_cleanup_do_response_menu(ActionMenu *action_menu, const ActionMe static void prv_handle_do_response_response(ActionResultData *data) { TimelineItem *item = timeline_item_copy(data->chaining_data.notif); if (!item) { - PBL_LOG(LOG_LEVEL_WARNING, "No notification in chaining data"); + PBL_LOG_WRN("No notification in chaining data"); prv_cleanup_action_result(data, false /* succeeded */); return; } @@ -446,7 +436,7 @@ static void prv_action_handle_response(PebbleEvent *e, void *context) { char uuid_string[UUID_STRING_BUFFER_LENGTH]; uuid_to_string(&action_result->id, uuid_string); - PBL_LOG(LOG_LEVEL_INFO, "Received action result: Item ID - %s; type - %" + PBL_LOG_INFO("Received action result: Item ID - %s; type - %" PRIu8, uuid_string, (uint8_t)action_result->type); // Each action result can only service one response event @@ -492,7 +482,7 @@ static void prv_action_handle_response(PebbleEvent *e, void *context) { break; default: prv_cleanup_action_result(data, false /* success */); - PBL_LOG(LOG_LEVEL_WARNING, "Unknown Action Response"); + PBL_LOG_WRN("Unknown Action Response"); break; } } @@ -632,25 +622,20 @@ static void prv_do_action_analytics(const TimelineItem *pin, const ActionMenuIte // Record action in the analytics if (action->type == TimelineItemActionTypeOpenWatchApp) { - analytics_event_pin_app_launch(pin->header.timestamp, &pin->header.parent_id); } else { Uuid app_uuid; timeline_get_originator_id(pin, &app_uuid); - analytics_event_pin_action(pin->header.timestamp, &app_uuid, action->type); } - AnalyticsMetric metric = ANALYTICS_DEVICE_METRIC_ACTION_INVOKED_FROM_TIMELINE_COUNT; const TimelineItemActionSource current_item_source = kernel_ui_get_current_timeline_item_action_source(); if (current_item_source == TimelineItemActionSourceModalNotification) { - metric = ANALYTICS_DEVICE_METRIC_ACTION_INVOKED_FROM_MODAL_NOTIFICATION_COUNT; } else if (current_item_source == TimelineItemActionSourceNotificationApp) { - metric = ANALYTICS_DEVICE_METRIC_ACTION_INVOKED_FROM_NOTIFICATION_APP_COUNT; + } else { } - analytics_inc(metric, AnalyticsClient_System); } -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM static void prv_invoke_ble_hrm_stop_sharing_action(ActionMenu *action_menu, const TimelineItem *item) { ble_hrm_revoke_all(); @@ -694,7 +679,7 @@ T_STATIC ActionResultData *prv_invoke_action(ActionMenu *action_menu, case TimelineItemActionTypeEmpty: case TimelineItemActionTypeUnknown: break; -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM case TimelineItemActionTypeBLEHRMStopSharing: prv_invoke_ble_hrm_stop_sharing_action(action_menu, pin); return NULL; @@ -704,7 +689,7 @@ T_STATIC ActionResultData *prv_invoke_action(ActionMenu *action_menu, #endif } - PBL_LOG(LOG_LEVEL_ERROR, "Unsupported action type %d", action->type); + PBL_LOG_ERR("Unsupported action type %d", action->type); if (action_menu) { action_menu_close(action_menu, true); } @@ -761,7 +746,7 @@ static void prv_action_menu_cb(ActionMenu *action_menu, const ActionMenuItem *it case TimelineItemTypeUnknown: case TimelineItemTypeOutOfRange: default: - PBL_LOG(LOG_LEVEL_ERROR, "Performing action on invalid TimelineItem with type: %d", + PBL_LOG_ERR("Performing action on invalid TimelineItem with type: %d", pin->header.type); action_menu_close(action_menu, true); return; @@ -776,7 +761,7 @@ static void prv_action_menu_cb(ActionMenu *action_menu, const ActionMenuItem *it prv_invoke_action(action_menu, action, pin, item->label); } -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC static void prv_invoke_voice_response(VoiceResponseData *voice_data, char *transcription) { // This is a bit of a hack, but we need all the behaviour of timeline_actions_invoke_action and // this allows voice responses to be used for other types of responses (i.e. ANCS in the future) @@ -861,7 +846,7 @@ static ActionMenuLevel *prv_create_emoji_level_from_action(ActionMenuLevel *pare return emoji_level; } -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC static void prv_handle_voice_transcription_result(PebbleEvent *e, void *context) { DictationSessionStatus status = e->dictation.result; char *transcription = e->dictation.text; @@ -877,17 +862,22 @@ static void prv_handle_voice_transcription_result(PebbleEvent *e, void *context) } #endif -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC static void prv_start_voice_reply(ActionMenu *action_menu, const ActionMenuItem *item, void *context) { TimelineActionMenu *timeline_action_menu = context; action_menu_freeze(action_menu); + // Make a copy of the TimelineItem to avoid use-after-free if the notification window's + // swap layer is reloaded while the voice window is active + TimelineItem *item_copy = timeline_item_copy(timeline_action_menu->item); + PBL_ASSERTN(item_copy); + VoiceResponseData *data = applib_malloc(sizeof(VoiceResponseData)); *data = (VoiceResponseData) { .action_data = item->action_data, - .context = timeline_action_menu->item, + .context = item_copy, .action_menu = action_menu, .voice_window = voice_window_create(NULL, 0, VoiceEndpointSessionTypeDictation), }; @@ -916,7 +906,7 @@ typedef enum ReplyOption { static bool prv_is_reply_option_supported(ReplyOption option, TimelineItemAction *action) { switch (option) { case ReplyOption_Voice: -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC return true; #else return false; @@ -927,7 +917,7 @@ static bool prv_is_reply_option_supported(ReplyOption option, TimelineItemAction // If this attribute isn't found, we want to support emoji by default return attribute_get_uint8(&action->attr_list, AttributeIdEmojiSupported, true); default: - PBL_LOG(LOG_LEVEL_WARNING, "Unknown reply option"); + PBL_LOG_WRN("Unknown reply option"); return false; } } @@ -950,7 +940,7 @@ static ActionMenuLevel *prv_create_responses_level(TimelineItemAction *action, } ActionMenuItem reply_options[ReplyOptionCount] = { -#if CAPABILITY_HAS_MICROPHONE +#ifdef CONFIG_MIC { .label = (reply_prefix ? i18n_get("Reply with Voice", root_level) : i18n_get("Voice", root_level)), @@ -1190,7 +1180,6 @@ void timeline_actions_dismiss_all(NotificationInfo *notif_list, int num_notifica } data->action_menu = action_menu; - analytics_inc(ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISS_ALL_COUNT, AnalyticsClient_System); data->action_complete.callback = dismiss_all_complete_callback; data->action_complete.callback_data = dismiss_all_cb_data; @@ -1216,12 +1205,12 @@ void timeline_actions_dismiss_all(NotificationInfo *notif_list, int num_notifica TimelineItem item; if (notif_list[i].type == NotificationReminder) { if (reminder_db_read_item(&item, ¬if_list[i].id) != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_ERROR, "Trying to dismiss all an invalid reminder"); + PBL_LOG_ERR("Trying to dismiss all an invalid reminder"); continue; } } else if (notif_list[i].type == NotificationMobile) { if (!notification_storage_get(¬if_list[i].id, &item)) { - PBL_LOG(LOG_LEVEL_ERROR, "Trying to dismiss all an invalid notification"); + PBL_LOG_ERR("Trying to dismiss all an invalid notification"); continue; } } @@ -1244,7 +1233,7 @@ void timeline_actions_dismiss_all(NotificationInfo *notif_list, int num_notifica timeline_enable_ancs_bulk_action_mode(false); if (!performed_actions) { - PBL_LOG(LOG_LEVEL_DEBUG, "Didn't take any actions, cleaning up"); + PBL_LOG_DBG("Didn't take any actions, cleaning up"); const bool success = false; prv_cleanup_action_result(data, success); } diff --git a/src/fw/services/normal/timeline/timeline_layout.c b/src/fw/services/timeline/timeline_layout.c similarity index 95% rename from src/fw/services/normal/timeline/timeline_layout.c rename to src/fw/services/timeline/timeline_layout.c index e5c08bc116..cc7bf54e60 100644 --- a/src/fw/services/normal/timeline/timeline_layout.c +++ b/src/fw/services/timeline/timeline_layout.c @@ -1,34 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/timeline_layout.h" #include "applib/graphics/graphics.h" #include "applib/preferred_content_size.h" #include "applib/ui/ui.h" -#include "apps/system_apps/timeline/text_node.h" -#include "apps/system_apps/timeline/timeline_layer.h" +#include "apps/system/timeline/text_node.h" +#include "apps/system/timeline/layer.h" #include "kernel/pbl_malloc.h" #include "kernel/ui/kernel_ui.h" #include "popups/timeline/peek.h" #include "process_management/app_install_manager.h" #include "process_state/app_state/app_state.h" #include "resource/timeline_resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "system/logging.h" #include "util/size.h" @@ -71,6 +58,10 @@ typedef struct TimelineLayoutStyle { int16_t primary_list_margin_h; int16_t primary_secondary_peek_margin_h; bool thin_can_have_secondary; + int16_t fat_future_title_offset_y; + int16_t fat_past_title_offset_y; + int16_t thin_future_title_offset_y; + int16_t thin_past_title_offset_y; } TimelineLayoutStyle; static const TimelineLayoutStyle s_style_medium = { @@ -78,6 +69,10 @@ static const TimelineLayoutStyle s_style_medium = { .thin_time_margin_h = -8, .primary_list_margin_h = 6, .primary_line_spacing_delta = -2, + .fat_future_title_offset_y = 0, + .fat_past_title_offset_y = 0, + .thin_future_title_offset_y = 0, + .thin_past_title_offset_y = 0, }; static const TimelineLayoutStyle s_style_large = { @@ -88,6 +83,10 @@ static const TimelineLayoutStyle s_style_large = { // depends on whether the remaining screen space after fat permits. .primary_secondary_peek_margin_h = -5, .thin_can_have_secondary = true, + .fat_future_title_offset_y = 25, + .fat_past_title_offset_y = -30, + .thin_future_title_offset_y = 45, + .thin_past_title_offset_y = 10, }; static const TimelineLayoutStyle * const s_styles[NumPreferredContentSizes] = { @@ -231,7 +230,12 @@ void timeline_layout_get_icon_frame(const GRect *bounds, TimelineScrollDirection const GSize size = timeline_resources_get_gsize(TimelineResourceSizeTiny); const bool is_future = (scroll_direction == TimelineScrollDirectionDown); PBL_UNUSED const int offset_y_rect = -5; - PBL_UNUSED const int offset_y_round = is_future ? 40 : 17; // Center the icon in the display + // Center the icon vertically at screen center (offsets differ by content size/style) + const bool use_large_style = (PreferredContentSizeDefault >= PreferredContentSizeLarge); + // s_style_large: future_top_margin=39, past layout origin=117, icon_offset_y=3 + // s_style_medium: future_top_margin=39, past layout origin=61, icon_offset_y=0 + PBL_UNUSED const int offset_y_round = use_large_style ? (is_future ? 76 : -2) + : (is_future ? 40 : 17); const GPoint origin = { .x = bounds->size.w - size.w + 2, .y = PBL_IF_RECT_ELSE(offset_y_rect, offset_y_round), @@ -581,12 +585,14 @@ static GTextNode *prv_create_pin_view_node(TimelineLayout *layout) { } } - if (PBL_IF_ROUND_ELSE(is_fat && !has_secondary, false)) { - const int single_offset = 2; - const GFont numbers_font = - system_theme_get_font_for_default_size(TextStyleFont_TimeHeaderNumbers); - vertical_node->container.node.offset.y = - ((is_future ? -1 : 1) * fonts_get_font_height(numbers_font)) + single_offset; + if (PBL_IF_ROUND_ELSE(!is_peek, false)) { + if (is_fat) { + vertical_node->container.node.offset.y = + is_future ? style->fat_future_title_offset_y : style->fat_past_title_offset_y; + } else { + vertical_node->container.node.offset.y = + is_future ? style->thin_future_title_offset_y : style->thin_past_title_offset_y; + } } if (is_peek) { diff --git a/src/fw/services/normal/timeline/timeline_layout_animations.c b/src/fw/services/timeline/timeline_layout_animations.c similarity index 93% rename from src/fw/services/normal/timeline/timeline_layout_animations.c rename to src/fw/services/timeline/timeline_layout_animations.c index 856a70afc1..60f90118e7 100644 --- a/src/fw/services/normal/timeline/timeline_layout_animations.c +++ b/src/fw/services/timeline/timeline_layout_animations.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_layout_animations.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/timeline_layout_animations.h" #include "applib/graphics/graphics.h" #include "applib/ui/animation_interpolate.h" @@ -23,11 +10,11 @@ #include "applib/ui/kino/kino_reel/transform.h" #include "applib/ui/kino/kino_reel_pdci.h" #include "applib/ui/ui.h" -#include "apps/system_apps/timeline/timeline_animations.h" +#include "apps/system/timeline/animations.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" #include "process_management/app_install_manager.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "system/logging.h" #define CARD_TRANSITION_ICON_EXPAND 5 diff --git a/src/fw/services/normal/timeline/timeline_resources.c b/src/fw/services/timeline/timeline_resources.c similarity index 91% rename from src/fw/services/normal/timeline/timeline_resources.c rename to src/fw/services/timeline/timeline_resources.c index 7af4a6a8fd..c026269883 100644 --- a/src/fw/services/normal/timeline/timeline_resources.c +++ b/src/fw/services/timeline/timeline_resources.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "timeline_resources.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/timeline_resources.h" #include "applib/graphics/gdraw_command_private.h" #include "applib/ui/kino/kino_reel.h" diff --git a/src/fw/services/normal/timeline/weather_layout.c b/src/fw/services/timeline/weather_layout.c similarity index 90% rename from src/fw/services/normal/timeline/weather_layout.c rename to src/fw/services/timeline/weather_layout.c index c24a2654c7..d8101ec47f 100644 --- a/src/fw/services/normal/timeline/weather_layout.c +++ b/src/fw/services/timeline/weather_layout.c @@ -1,34 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_layout.h" -#include "timeline_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/weather_layout.h" +#include "pbl/services/timeline/timeline_layout.h" #include "applib/fonts/fonts.h" #include "applib/graphics/gtypes.h" #include "applib/graphics/text.h" #include "applib/preferred_content_size.h" #include "applib/ui/ui.h" -#include "apps/system_apps/timeline/text_node.h" +#include "apps/system/timeline/text_node.h" #include "drivers/rtc.h" #include "font_resource_keys.auto.h" #include "kernel/pbl_malloc.h" #include "process_state/app_state/app_state.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" #include "system/logging.h" #include "system/hexdump.h" #include "util/size.h" @@ -38,7 +25,6 @@ #define WEATHER_CARD_TITLE_LENGTH 30 // We're limited to one line for this layout -#if !TINTIN_FORCE_FIT ////////////////////////////////////////// // Card Mode ////////////////////////////////////////// @@ -232,8 +218,3 @@ LayoutLayer *weather_layout_create(const LayoutLayerConfig *config) { return (LayoutLayer *)layout; } -#else -LayoutLayer *weather_layout_create(const LayoutLayerConfig *config) { return NULL; } - -bool weather_layout_verify(bool existing_attributes[]) { return false; } -#endif diff --git a/src/fw/services/timeline/wscript_build b/src/fw/services/timeline/wscript_build new file mode 100644 index 0000000000..ce62e3415b --- /dev/null +++ b/src/fw/services/timeline/wscript_build @@ -0,0 +1,43 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'actions_endpoint.c', + 'alarm_layout.c', + 'attribute.c', + 'attribute_group.c', + 'attributes_actions.c', + 'calendar.c', + 'calendar_layout.c', + 'calendar_layout_resources.c', + 'event.c', + 'generic_layout.c', + 'health_layout.c', + 'item.c', + 'layout_layer.c', + 'layout_node.c', + 'metricgroup.c', + 'notification_layout.c', + 'peek.c', + 'reminders.c', + 'sports_layout.c', + 'swap_layer.c', + 'timeline.c', + 'timeline_actions.c', + 'timeline_layout.c', + 'timeline_layout_animations.c', + 'timeline_resources.c', + 'weather_layout.c', +] + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=sources, + target='services_timeline', +) + +bld.env.SERVICES.append('services_timeline') + +target = bld.path.find_or_declare('services_timeline.pot') +bld.gettext(source=sources, target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/timezone_database/Kconfig b/src/fw/services/timezone_database/Kconfig new file mode 100644 index 0000000000..2f2a7e9e57 --- /dev/null +++ b/src/fw/services/timezone_database/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_TIMEZONE_DATABASE + bool "Timezone database" + help + Timezone database service. + +if SERVICE_TIMEZONE_DATABASE + +module = SERVICE_TIMEZONE_DATABASE +module-str = Timezone database +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/timezone_database/service.c b/src/fw/services/timezone_database/service.c new file mode 100644 index 0000000000..ff228e646e --- /dev/null +++ b/src/fw/services/timezone_database/service.c @@ -0,0 +1,255 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timezone_database.h" + +#include "resource/resource.h" +#include "resource/resource_ids.auto.h" +#include "pbl/services/clock.h" +#include "system/logging.h" +#include "system/passert.h" + +#include +#include + +#include + +PBL_LOG_MODULE_DEFINE(service_timezone_database, CONFIG_SERVICE_TIMEZONE_DATABASE_LOG_LEVEL); + +// The format of the database is as follows +// Header +// 2 bytes - Region count +// 2 bytes - DST Rule count +// 2 bytes - Link count +// Regions +// For each region (24 bytes): +// 1 byte - Continent index, @see CONTINENT_NAMES +// 15 bytes - City name +// 2 bytes - GMT offset in minutes as a int16_t +// 5 bytes - Timezone name abbreviation (aka tz_abbr) +// 1 byte - DST Rule ID +// DST Rules +// For each DST ID (16 bytes) +// For each rule in the pair, first the start rule followed by the end rule (8 bytes) +// @see TimezoneDSTRule for the structure +// Links +// For each link (35 bytes) +// 2 bytes - The region id this link maps to +// 33 bytes - The name of the link that should be treated as an alias to the linked region + +typedef struct PACKED { + uint16_t region_count; + uint16_t dst_rule_count; + uint16_t link_count; +} TimezoneDatabaseFlashHeader; +#define TZDATA_HEADER_BYTES (sizeof(TimezoneDatabaseFlashHeader)) + +#define TIMEZONE_CITY_LENGTH 15 // maximum length of the city name in timezone database +#define REGION_BYTES (1 + TIMEZONE_CITY_LENGTH + 2 + 5 + 1) + +#define DST_RULE_BYTES (sizeof(TimezoneDSTRule)) +#define DST_RULE_PAIR_BYTES (DST_RULE_BYTES * 2) + +#define LINK_REGION_LENGTH 2 +#define LINK_NAME_LENGTH 33 +#define LINK_BYTES (LINK_REGION_LENGTH + LINK_NAME_LENGTH) + + +//! Names for all the continents we support. The timezone database stores continents as indexes +//! into this constant array. +const char * const CONTINENT_NAMES[] = { "Africa", + "America", + "Antarctica", + "Asia", + "Atlantic", + "Australia", + "Europe", + "Indian", + "Pacific", + "Etc"}; + + +//! Helper function to curry out some common arguments to the resource reads in this file. +static bool prv_database_read(uint32_t offset, void *data, size_t num_bytes) { + return resource_load_byte_range_system(SYSTEM_APP, RESOURCE_ID_TIMEZONE_DATABASE, + offset, data, num_bytes) == num_bytes; +} + +//! Note! This count includes rule 0 which isn't actually stored in the database. +static int prv_get_dst_rule_count(void) { + uint16_t dst_rule_count; + prv_database_read(offsetof(TimezoneDatabaseFlashHeader, dst_rule_count), + &dst_rule_count, sizeof(dst_rule_count)); + return dst_rule_count; +} + +static int prv_get_link_count(void) { + uint16_t link_count; + prv_database_read(offsetof(TimezoneDatabaseFlashHeader, link_count), + &link_count, sizeof(link_count)); + return link_count; +} + +int timezone_database_get_region_count(void) { + uint16_t region_count; + prv_database_read(offsetof(TimezoneDatabaseFlashHeader, region_count), + ®ion_count, sizeof(region_count)); + return region_count; +} + +bool timezone_database_load_region_info(uint16_t region_id, TimezoneInfo *tz_info) { + const int region_offset = + // Skip over the region count + TZDATA_HEADER_BYTES + + // Skip over the regions list + (region_id * REGION_BYTES); + + //! Struct for reading data from a raw database of timezone information + struct PACKED { + int16_t gmt_offset_minutes; //!< timezone offset from UTC time (in minutes) + char tz_abbr[TZ_LEN - 1]; //!< timezone abbreviation (without terminating nul) + int8_t dst_id; //!< daylight savings time index identifier + } tz_data; + + // Load the timezone information for the region_id, excluding the country + city_name itself + if (!prv_database_read(region_offset + 1 + TIMEZONE_CITY_LENGTH, &tz_data, sizeof(tz_data))) { + *tz_info = (TimezoneInfo) { .dst_id = 0 }; + return false; + } + + *tz_info = (TimezoneInfo) { + .dst_id = tz_data.dst_id, + .timezone_id = region_id, + .tm_gmtoff = tz_data.gmt_offset_minutes * SECONDS_PER_MINUTE, + // Leave the dst_start and dst_end timestamps uninitialized + .dst_start = 0, + .dst_end = 0 + }; + memcpy(tz_info->tm_zone, tz_data.tz_abbr, sizeof(tz_data.tz_abbr)); + + return true; +} + +bool timezone_database_load_region_name(uint16_t region_id, char *region_name) { + if (region_id > timezone_database_get_region_count()) { + return false; + } + + const int region_offset = + // Skip over the region count + TZDATA_HEADER_BYTES + + // Skip over the regions list + (region_id * REGION_BYTES); + + // Read the continent index, which is the first byte + uint8_t continent_index = 0; + prv_database_read(region_offset, &continent_index, sizeof(continent_index)); + PBL_ASSERTN(continent_index < ARRAY_LENGTH(CONTINENT_NAMES)); + + // Copy the continent name into our buffer, followed by a slash. + const int continent_name_length = strlen(CONTINENT_NAMES[continent_index]); + memcpy(region_name, CONTINENT_NAMES[continent_index], continent_name_length); + region_name[continent_name_length] = '/'; + + char *city_name = region_name + continent_name_length + 1 /* slash */; + + // How many bytes left we have of our buffer to fill in the city information. + const int remaining_size = (region_name + TIMEZONE_NAME_LENGTH) - city_name; + + // Fill the rest of our buffer with city name. + // Our generation script will ensure that continent + slash + city name + null will always + // fit in our buffer with a null terminator to spare. + prv_database_read(region_offset + 1 /* continent index */, city_name, remaining_size); + + // FIXME: Perhaps we should refactor this to do one read instead of two? The information is + // right beside each other and it's pretty wasteful to do a 1 byte read followed by a 15 byte + // read as separate reads. + + return true; +} + +bool timezone_database_load_dst_rule(uint8_t dst_id, TimezoneDSTRule *start, TimezoneDSTRule *end) { + const int region_count = timezone_database_get_region_count(); + + const int dst_rule_pair_offset = + // Skip over the region count + TZDATA_HEADER_BYTES + + // Skip over the regions list + (region_count * REGION_BYTES) + + // Find the appropriate DST zone (DST ID is 1 indexed) + ((dst_id - 1) * DST_RULE_PAIR_BYTES); + + // First half of the DST rule pair + if (!prv_database_read(dst_rule_pair_offset, start, DST_RULE_BYTES) || + !prv_database_read(dst_rule_pair_offset + DST_RULE_BYTES, end, DST_RULE_BYTES)) { + PBL_LOG_WRN("Failed to load timezone for DST ID %"PRIu8, dst_id); + return false; + } + + if (start->ds_label == '\0' || end->ds_label == '\0') { + // Does not observe DST + return false; + } + + return true; +} + +static int prv_search_regions_by_name(const char *region_name, int region_name_length) { + const int region_count = timezone_database_get_region_count(); + + for (int i = 0; i < region_count; i++) { + char lookup_region_name[TIMEZONE_NAME_LENGTH]; + timezone_database_load_region_name(i, lookup_region_name); + if (strncmp(region_name, lookup_region_name, region_name_length) == 0) { + return i; + } + } + + return -1; +} + +static int prv_search_links_by_name(const char *region_name, int region_name_length) { + char name_asciz[256] = {0}; + memcpy(name_asciz, region_name, region_name_length); + + const int link_section_offset = + // Skip over the region count + TZDATA_HEADER_BYTES + + // Skip over the regions list + (timezone_database_get_region_count() * REGION_BYTES) + + // Skip over the DST list + ((prv_get_dst_rule_count() - 1) * DST_RULE_PAIR_BYTES); + + const uint16_t link_count = prv_get_link_count(); + + for (int i = 0; i < link_count; i++) { + const int link_offset = link_section_offset + (i * LINK_BYTES); + + char link_name[LINK_NAME_LENGTH + 1]; // + max length + null terminator + prv_database_read(link_offset + LINK_REGION_LENGTH, link_name, LINK_NAME_LENGTH); + link_name[sizeof(link_name) - 1] = '\0'; + + if (strncmp(name_asciz, link_name, LINK_NAME_LENGTH) == 0) { + // Found it! + uint16_t linked_region_id; + prv_database_read(link_offset, &linked_region_id, sizeof(linked_region_id)); + return linked_region_id; + } + } + + return -1; +} + +int timezone_database_find_region_by_name(const char *region_name, int region_name_length) { + int region_id = prv_search_regions_by_name(region_name, region_name_length); + + if (region_id == -1) { + // Might be a Link, let's check. + // To explain: iOS, when not synchronized from the internet, uses _ancient_ IANA region names. + // For example, when in California, iOS will send "US/Pacific" which hasn't been the name of + // that timezone since 1993. So we need to support linked timezones sent from the phone. + region_id = prv_search_links_by_name(region_name, region_name_length); + } + + return region_id; +} diff --git a/src/fw/services/timezone_database/wscript_build b/src/fw/services/timezone_database/wscript_build new file mode 100644 index 0000000000..b46e2a3300 --- /dev/null +++ b/src/fw/services/timezone_database/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_timezone_database', +) +bld.env.SERVICES.append('services_timezone_database') diff --git a/src/fw/services/touch/Kconfig b/src/fw/services/touch/Kconfig new file mode 100644 index 0000000000..63d5ef4c02 --- /dev/null +++ b/src/fw/services/touch/Kconfig @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_TOUCH + bool "Touch" + default y if TOUCH + help + Touch event service. Requires a touch driver (CONFIG_TOUCH). + +if SERVICE_TOUCH + +module = SERVICE_TOUCH +module-str = Touch +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/touch/touch.c b/src/fw/services/touch/touch.c new file mode 100644 index 0000000000..5c6b8f4c48 --- /dev/null +++ b/src/fw/services/touch/touch.c @@ -0,0 +1,233 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/touch/touch.h" +#include "pbl/services/touch/touch_event.h" + +#include "drivers/display/display.h" +#include "drivers/touch/touch_sensor.h" +#include "kernel/events.h" +#include "kernel/pebble_tasks.h" +#include "pbl/services/event_service.h" +#include "pbl/services/analytics/analytics.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "os/mutex.h" +#include "system/passert.h" + +PBL_LOG_MODULE_DEFINE(service_touch, CONFIG_SERVICE_TOUCH_LOG_LEVEL); + +static TouchState s_touch_state = TouchState_FingerUp; +static int16_t s_last_x; +static int16_t s_last_y; + +static PebbleMutex *s_touch_mutex; + +static uint8_t s_subscriber_count = 0; +static bool s_backlight_subscribed = false; +static bool s_globally_enabled = true; +static bool s_rotated = false; + +static void prv_apply_rotation(int16_t *x, int16_t *y) { + if (s_rotated) { + *x = (DISP_COLS - 1) - *x; + *y = (DISP_ROWS - 1) - *y; + } +} + +static void prv_add_subscriber_cb(PebbleTask task) { + mutex_lock(s_touch_mutex); + // Honor the global kill switch: when touch is globally disabled, track the + // subscriber count but don't power up the sensor. + if (++s_subscriber_count == 1 && s_globally_enabled) { + touch_sensor_set_enabled(true); + } + mutex_unlock(s_touch_mutex); +} + +static void prv_remove_subscriber_cb(PebbleTask task) { + mutex_lock(s_touch_mutex); + PBL_ASSERTN(s_subscriber_count > 0); + if (--s_subscriber_count == 0 && s_globally_enabled) { + touch_sensor_set_enabled(false); + } + mutex_unlock(s_touch_mutex); +} + +void touch_init(void) { + s_touch_mutex = mutex_create(); + + event_service_init(PEBBLE_TOUCH_EVENT, &prv_add_subscriber_cb, + &prv_remove_subscriber_cb); + event_service_init(PEBBLE_GESTURE_EVENT, &prv_add_subscriber_cb, + &prv_remove_subscriber_cb); +} + +bool touch_has_app_subscribers(void) { + mutex_lock(s_touch_mutex); + // The backlight gesture subscription is tracked in s_subscriber_count as + // well; exclude it so this only reflects real app subscribers. + const uint8_t backlight_count = s_backlight_subscribed ? 1 : 0; + const bool has_apps = s_subscriber_count > backlight_count; + mutex_unlock(s_touch_mutex); + return has_apps; +} + +void touch_service_set_globally_enabled(bool enabled) { + mutex_lock(s_touch_mutex); + if (s_globally_enabled == enabled) { + mutex_unlock(s_touch_mutex); + return; + } + s_globally_enabled = enabled; + const bool sensor_enabled = enabled && (s_subscriber_count > 0); + mutex_unlock(s_touch_mutex); + + touch_sensor_set_enabled(sensor_enabled); + if (!enabled) { + // Avoid delivering stale position on re-enable. + touch_reset(); + } +} + +bool touch_service_is_globally_enabled(void) { + mutex_lock(s_touch_mutex); + const bool enabled = s_globally_enabled; + mutex_unlock(s_touch_mutex); + return enabled; +} + +DEFINE_SYSCALL(bool, sys_touch_service_is_enabled, void) { + return touch_service_is_globally_enabled(); +} + +DEFINE_SYSCALL(void, sys_touch_reset, void) { + touch_reset(); +} + +void touch_set_backlight_enabled(bool enabled) { + mutex_lock(s_touch_mutex); + if (enabled && !s_backlight_subscribed) { + s_backlight_subscribed = true; + mutex_unlock(s_touch_mutex); + prv_add_subscriber_cb(PebbleTask_KernelMain); + return; + } else if (!enabled && s_backlight_subscribed) { + s_backlight_subscribed = false; + mutex_unlock(s_touch_mutex); + prv_remove_subscriber_cb(PebbleTask_KernelMain); + return; + } + mutex_unlock(s_touch_mutex); +} + +static void prv_put_touch_event(TouchEventType type, int16_t x, int16_t y) { + PebbleEvent e = { + .type = PEBBLE_TOUCH_EVENT, + .touch = { + .event = { + .type = type, + .x = x, + .y = y, + }, + }, + }; + event_put(&e); +} + +static void prv_put_gesture_event(GestureEventType gesture, int16_t x, int16_t y) { + PebbleEvent e = { + .type = PEBBLE_GESTURE_EVENT, + .gesture = { + .event = { + .type = gesture, + .x = x, + .y = y, + }, + }, + }; + event_put(&e); +} + +void touch_handle_update(TouchState touch_state, int16_t x, int16_t y) { + mutex_lock(s_touch_mutex); + + if (!s_globally_enabled) { + mutex_unlock(s_touch_mutex); + return; + } + + prv_apply_rotation(&x, &y); + + if (s_touch_state != touch_state) { + s_touch_state = touch_state; + s_last_x = x; + s_last_y = y; + mutex_unlock(s_touch_mutex); + + if (touch_state == TouchState_FingerDown) { + PBL_ANALYTICS_ADD(touch_event_count, 1); + PBL_LOG_DBG("Touch: Touchdown @ (%" PRId16 ", %" PRId16 ")", x, y); + prv_put_touch_event(TouchEvent_Touchdown, x, y); + } else { + PBL_LOG_DBG("Touch: Liftoff"); + prv_put_touch_event(TouchEvent_Liftoff, x, y); + } + return; + } + + if (touch_state == TouchState_FingerDown && (x != s_last_x || y != s_last_y)) { + s_last_x = x; + s_last_y = y; + mutex_unlock(s_touch_mutex); + + PBL_LOG_VERBOSE("Touch: Position Update @ (%" PRId16 ", %" PRId16 ")", x, y); + prv_put_touch_event(TouchEvent_PositionUpdate, x, y); + return; + } + + mutex_unlock(s_touch_mutex); +} + +void touch_handle_gesture(TouchGesture gesture, int16_t x, int16_t y) { + mutex_lock(s_touch_mutex); + + if (!s_globally_enabled) { + mutex_unlock(s_touch_mutex); + return; + } + + prv_apply_rotation(&x, &y); + + PBL_LOG_DBG("Gesture: %d @ (%" PRId16 ", %" PRId16 ")", gesture, x, y); + + switch (gesture) { + case TouchGesture_Tap: + PBL_ANALYTICS_ADD(gesture_tap_count, 1); + prv_put_gesture_event(GestureEvent_Tap, x, y); + break; + case TouchGesture_DoubleTap: + PBL_ANALYTICS_ADD(gesture_double_tap_count, 1); + prv_put_gesture_event(GestureEvent_DoubleTap, x, y); + break; + default: + break; + } + + mutex_unlock(s_touch_mutex); +} + +void touch_reset(void) { + mutex_lock(s_touch_mutex); + s_touch_state = TouchState_FingerUp; + s_last_x = 0; + s_last_y = 0; + mutex_unlock(s_touch_mutex); +} + +void touch_set_rotated(bool rotated) { + mutex_lock(s_touch_mutex); + s_rotated = rotated; + mutex_unlock(s_touch_mutex); +} diff --git a/src/fw/services/touch/wscript_build b/src/fw/services/touch/wscript_build new file mode 100644 index 0000000000..1b6286ae7b --- /dev/null +++ b/src/fw/services/touch/wscript_build @@ -0,0 +1,14 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'touch.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_touch', +) + +bld.env.SERVICES.append('services_touch') diff --git a/src/fw/services/vibe_pattern/Kconfig b/src/fw/services/vibe_pattern/Kconfig new file mode 100644 index 0000000000..73a2598d2b --- /dev/null +++ b/src/fw/services/vibe_pattern/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_VIBE_PATTERN + bool "Vibe pattern" + help + Vibration pattern playback service. + +if SERVICE_VIBE_PATTERN + +module = SERVICE_VIBE_PATTERN +module-str = Vibe pattern +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/vibe_pattern/service.c b/src/fw/services/vibe_pattern/service.c new file mode 100644 index 0000000000..34ce3f92db --- /dev/null +++ b/src/fw/services/vibe_pattern/service.c @@ -0,0 +1,438 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/vibe_pattern.h" + +#include "drivers/vibe.h" +#include "drivers/battery.h" +#include "drivers/rtc.h" + +#include "util/list.h" +#include "util/math.h" + +#include "os/mutex.h" + +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/accel_manager.h" +#include "pbl/services/new_timer/new_timer.h" +#include "kernel/events.h" + +#include "kernel/pbl_malloc.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" + +#include +#include + +PBL_LOG_MODULE_DEFINE(service_vibe_pattern, CONFIG_SERVICE_VIBE_PATTERN_LOG_LEVEL); + +typedef struct { + ListNode list_node; + uint64_t time_start; + uint64_t time_end; +} VibeHistory; + +// The maximum history we need to keep is based on the maximum time between accel samples (the +// lowest sampling rate) in milliseconds and the maximum number of accel samples per update. +#define MAX_HISTORY_MS (ACCEL_MAX_SAMPLES_PER_UPDATE * 1000 / ACCEL_MINIMUM_SAMPLING_RATE) +#define END_NOT_SET 0 +#define HISTORY_CLEAR_ALL 0 + +static PebbleMutex *s_vibe_history_mutex = NULL; +static VibeHistory *s_vibe_history = NULL; +static bool s_vibe_history_enabled = false; +static bool s_vibe_service_enabled = true; + +DEFINE_SYSCALL(bool, sys_vibe_history_was_vibrating, uint64_t time_search) { + bool rc = false; + + PBL_ASSERTN(s_vibe_history_mutex); + mutex_lock(s_vibe_history_mutex); + VibeHistory *node = s_vibe_history; + while (node) { + if (node->time_end == END_NOT_SET && time_search >= node->time_start) { + rc = true; + break; + } + if (time_search >= node->time_start && time_search <= node->time_end) { + rc = true; + break; + } + node = (VibeHistory*)list_get_next((ListNode*)node); + } + mutex_unlock(s_vibe_history_mutex); + return rc; +} + +// @param cutoff The time to cut off the list at. 0 means to clear the list. +static void prv_vibe_history_clear(uint64_t cutoff) { + PBL_ASSERTN(s_vibe_history_mutex); + mutex_assert_held_by_curr_task(s_vibe_history_mutex, true); + while (s_vibe_history && s_vibe_history->time_end != END_NOT_SET) { + VibeHistory *vibe = s_vibe_history; + if (cutoff != HISTORY_CLEAR_ALL && vibe->time_end >= cutoff) { + break; + } + s_vibe_history = (VibeHistory*)list_get_next((ListNode*)vibe); + kernel_free(vibe); + } +} + +DEFINE_SYSCALL(void, sys_vibe_history_start_collecting, void) { + s_vibe_history_enabled = true; +} + +DEFINE_SYSCALL(void, sys_vibe_history_stop_collecting, void) { + s_vibe_history_enabled = false; + mutex_lock(s_vibe_history_mutex); + prv_vibe_history_clear(HISTORY_CLEAR_ALL); + mutex_unlock(s_vibe_history_mutex); +} + +static void prv_vibe_history_start_event(void) { + if (!s_vibe_history_enabled) { + return; + } + VibeHistory *vibe = kernel_malloc(sizeof(VibeHistory)); + if (vibe == NULL) { + s_vibe_history_enabled = false; + return; + } + list_init((ListNode*)vibe); + time_t s; + uint16_t ms; + rtc_get_time_ms(&s, &ms); + vibe->time_start = ((uint64_t)s) * 1000 + ms; + vibe->time_end = END_NOT_SET; + + PBL_ASSERTN(s_vibe_history_mutex); + mutex_lock(s_vibe_history_mutex); + if (s_vibe_history == NULL) { + s_vibe_history = vibe; + } else { + list_append((ListNode*)s_vibe_history, (ListNode*)vibe); + } + prv_vibe_history_clear(vibe->time_start - MAX_HISTORY_MS); + mutex_unlock(s_vibe_history_mutex); +} + +// Ends the last vibration event +static void prv_vibe_history_end_event(void) { + if (!s_vibe_history_enabled) { + return; + } + if (!s_vibe_history) { + // Possible that it was enabled while the watch was vibrating + return; + } + + time_t s; + uint16_t ms; + rtc_get_time_ms(&s, &ms); + + mutex_lock(s_vibe_history_mutex); + VibeHistory *vibe = (VibeHistory*)list_get_tail((ListNode*)s_vibe_history); + if (vibe->time_end == END_NOT_SET) { + vibe->time_end = ((uint64_t)s) * 1000 + ms; + } + mutex_unlock(s_vibe_history_mutex); +} + +typedef struct { + ListNode list_node; + uint32_t duration_ms; + int32_t strength; +} VibePatternStep; + +static const uint32_t MAX_VIBE_DURATION_MS = 10000; + +static int s_pattern_timer = TIMER_INVALID_ID; +static bool s_pattern_in_progress = false; +// Reference points for drift-free step scheduling. The pattern's t=0 is anchored +// to s_pattern_start_ticks; s_pattern_deadline_ms is the (start-relative) +// completion time of the step the timer is currently waiting on. Each step's +// timeout is computed against an absolute deadline so per-callback scheduling +// jitter does not accumulate across long patterns (the Reveille score, for +// example, expands to ~180 chained steps). +static RtcTicks s_pattern_start_ticks = 0; +static uint64_t s_pattern_deadline_ms = 0; +// s_vibe_strength is the current vibration strength setting of the motor +static int32_t s_vibe_strength = VIBE_STRENGTH_OFF; +// s_vibe_strength_default is the vibrations trength of the motor used when one is not specified +// explicitly, and can be changed in the notification vibration strength setting. +static int32_t s_vibe_strength_default = VIBE_STRENGTH_MAX; + +static PebbleMutex *s_vibe_pattern_mutex = NULL; +static VibePatternStep *s_vibe_queue_head = NULL; + +//! Analytics: Track time-weighted average strength +static uint64_t s_strength_time_product_sum; // Sum of (strength_pct × time_ms) +static RtcTicks s_last_strength_sample_ticks; // Timestamp of last sample +static uint8_t s_last_sampled_strength_pct; // Last strength percentage sampled +static uint32_t s_total_vibe_on_time_ms; // Total vibe on time tracked internally + +void vibes_init() { + s_vibe_history_mutex = mutex_create(); + s_vibe_pattern_mutex = mutex_create(); + s_pattern_in_progress = false; + s_pattern_timer = new_timer_create(); + + // Initialize strength analytics tracking + s_strength_time_product_sum = 0; + s_last_strength_sample_ticks = 0; + s_last_sampled_strength_pct = 0; + s_total_vibe_on_time_ms = 0; +} + +static void prv_update_strength_analytics(uint8_t new_strength_pct) { + RtcTicks now_ticks = rtc_get_ticks(); + + // Calculate time delta in ms since last sample + if (s_last_strength_sample_ticks > 0) { + uint32_t time_delta_ms = ((now_ticks - s_last_strength_sample_ticks) * 1000) / RTC_TICKS_HZ; + + // Accumulate strength × time for weighted average calculation + // Use last sampled strength for the period that just elapsed + s_strength_time_product_sum += (uint64_t)s_last_sampled_strength_pct * time_delta_ms; + + // Track total on-time when strength is above zero + if (s_last_sampled_strength_pct > 0) { + s_total_vibe_on_time_ms += time_delta_ms; + } + } + + // Update tracking variables + s_last_strength_sample_ticks = now_ticks; + s_last_sampled_strength_pct = new_strength_pct; +} + +//! Turn the vibe motor on or off. +//! +//! This function should be used instead of vibe_ctl so that the vibe +//! history is kept in sync with the vibe state. +//! The caller must be holding s_vibe_pattern_mutex +static void prv_vibes_set_vibe_strength(int32_t new_strength) { + mutex_assert_held_by_curr_task(s_vibe_pattern_mutex, true); + if (!s_vibe_service_enabled) { + PBL_ASSERTN(s_vibe_strength == VIBE_STRENGTH_OFF); + return; + } + + // Track strength for analytics (convert to percentage: -100..100 -> 0..100%) + // Take absolute value since we care about vibration intensity, not direction + uint8_t new_strength_pct = (ABS(new_strength) * 100) / VIBE_STRENGTH_MAX; + prv_update_strength_analytics(new_strength_pct); + + if (new_strength != VIBE_STRENGTH_OFF) { + vibe_set_strength(new_strength); + vibe_ctl(true /* on */); + if (s_vibe_strength == VIBE_STRENGTH_OFF) { + // Transitioning from off to on + PBL_ANALYTICS_TIMER_START(vibrator_on_time_ms); + prv_vibe_history_start_event(); + } + } else { + vibe_ctl(false /* on */); + if (s_vibe_strength != VIBE_STRENGTH_OFF) { + // Transitioning from on to off + PBL_ANALYTICS_TIMER_STOP(vibrator_on_time_ms); + prv_vibe_history_end_event(); + } + } + s_vibe_strength = new_strength; +} + +void vibe_service_set_enabled(bool enable) { + mutex_lock(s_vibe_pattern_mutex); + if (enable != s_vibe_service_enabled) { + // ensure that the vibe is off before disabling it. No op if enabling it + prv_vibes_set_vibe_strength(VIBE_STRENGTH_OFF); + s_vibe_service_enabled = enable; + } + mutex_unlock(s_vibe_pattern_mutex); +} + +//! Extend the pattern's absolute deadline by step_duration_ms and return the +//! timer duration to schedule for the next step, compensating for any +//! callback-scheduling latency that has elapsed since the pattern started. +//! Caller must hold s_vibe_pattern_mutex. +static uint32_t prv_next_timeout_ms(uint32_t step_duration_ms) { + s_pattern_deadline_ms += step_duration_ms; + const RtcTicks elapsed_ticks = rtc_get_ticks() - s_pattern_start_ticks; + const uint64_t elapsed_ms = (elapsed_ticks * 1000) / RTC_TICKS_HZ; + if (s_pattern_deadline_ms <= elapsed_ms) { + // Already late — fire as soon as the timer service can. The deadline + // accumulator preserves the planned schedule so subsequent steps still + // line up against the original anchor. + return 1; + } + return (uint32_t)(s_pattern_deadline_ms - elapsed_ms); +} + +static void prv_timer_callback(void* data) { + if (s_vibe_queue_head == NULL) { + PBL_LOG_ERR("Tried to handle a vibe event with a null vibe queue"); + return; + } + + mutex_lock(s_vibe_pattern_mutex); + + // remove the event I've finished + VibePatternStep *removed_node = s_vibe_queue_head; + s_vibe_queue_head = (VibePatternStep*)list_pop_head((ListNode*)s_vibe_queue_head); + kernel_free(removed_node); + + if (s_vibe_queue_head != NULL) { + // move to the next step + prv_vibes_set_vibe_strength(s_vibe_queue_head->strength); + const uint32_t next_ms = prv_next_timeout_ms(s_vibe_queue_head->duration_ms); + bool success = new_timer_start(s_pattern_timer, next_ms, + prv_timer_callback, NULL, 0 /*flags*/); + PBL_ASSERTN(success); + } else { + // I'm done with the active pattern + // make sure it's off + PBL_LOG_INFO("vibe_pattern: pattern complete"); + prv_vibes_set_vibe_strength(VIBE_STRENGTH_OFF); + s_pattern_in_progress = false; + } + + mutex_unlock(s_vibe_pattern_mutex); +} + +int32_t vibes_get_vibe_strength(void) { + return s_vibe_strength; +} + +int32_t vibes_get_default_vibe_strength(void) { + return s_vibe_strength_default; +} + +void vibes_set_default_vibe_strength(int32_t vibe_strength_default) { + s_vibe_strength_default = vibe_strength_default; +} + +DEFINE_SYSCALL(int32_t, sys_vibe_get_vibe_strength, void) { + return vibes_get_vibe_strength(); +} + +bool prv_vibe_pattern_enqueue_step_raw(uint32_t duration_ms, int32_t strength) { + mutex_lock(s_vibe_pattern_mutex); + + if (s_pattern_in_progress) { + mutex_unlock(s_vibe_pattern_mutex); + return false; + } + + VibePatternStep *step = kernel_malloc(sizeof(VibePatternStep)); + if (step == NULL) { + PBL_LOG_ERR("Couldn't malloc for a vibe step"); + mutex_unlock(s_vibe_pattern_mutex); + return false; + } + + list_init((ListNode*)step); + step->duration_ms = MIN(duration_ms, MAX_VIBE_DURATION_MS); + step->strength = strength; + + if (s_vibe_queue_head == NULL) { + s_vibe_queue_head = step; + } else { + list_append((ListNode*)s_vibe_queue_head, (ListNode*)step); + } + + mutex_unlock(s_vibe_pattern_mutex); + + return true; +} + +DEFINE_SYSCALL(bool, sys_vibe_pattern_enqueue_step_raw, uint32_t duration_ms, int32_t strength) { + return prv_vibe_pattern_enqueue_step_raw(duration_ms, strength); +} + +DEFINE_SYSCALL(bool, sys_vibe_pattern_enqueue_step, uint32_t duration_ms, bool on) { + return prv_vibe_pattern_enqueue_step_raw(duration_ms, on ? s_vibe_strength_default + : VIBE_STRENGTH_OFF); +} + +DEFINE_SYSCALL(void, sys_vibe_pattern_trigger_start, void) { + mutex_lock(s_vibe_pattern_mutex); + if (s_vibe_queue_head == NULL || s_pattern_in_progress) { + // either no vibes queued or I've already started + mutex_unlock(s_vibe_pattern_mutex); + return; + } + +#if !defined(CONFIG_RECOVERY_FW) + { + unsigned int step_count = 0; + uint32_t total_duration_ms = 0; + VibePatternStep *step = s_vibe_queue_head; + while (step) { + step_count++; + total_duration_ms += step->duration_ms; + step = (VibePatternStep *)list_get_next((ListNode *)step); + } + extern bool shell_prefs_get_vibe_log_info_enabled(void); + if (shell_prefs_get_vibe_log_info_enabled()) { + PBL_LOG_INFO("vibe_pattern: trigger_start, %u steps, %" PRIu32 + "ms total, strength=%" PRId32, + step_count, total_duration_ms, s_vibe_queue_head->strength); + } else { + PBL_LOG_DBG("vibe_pattern: trigger_start, %u steps, %" PRIu32 + "ms total, strength=%" PRId32, + step_count, total_duration_ms, s_vibe_queue_head->strength); + } + } +#endif + + s_pattern_start_ticks = rtc_get_ticks(); + s_pattern_deadline_ms = 0; + prv_vibes_set_vibe_strength(s_vibe_queue_head->strength); + s_pattern_in_progress = true; + const uint32_t first_ms = prv_next_timeout_ms(s_vibe_queue_head->duration_ms); + bool success = new_timer_start(s_pattern_timer, first_ms, + prv_timer_callback, NULL, 0 /*flags*/); + PBL_ASSERTN(success); + mutex_unlock(s_vibe_pattern_mutex); +} + +DEFINE_SYSCALL(void, sys_vibe_pattern_clear, void) { + mutex_lock(s_vibe_pattern_mutex); + PBL_LOG_INFO("vibe_pattern: clear (was_in_progress=%d)", s_pattern_in_progress); + new_timer_stop(s_pattern_timer); + while (s_vibe_queue_head) { + VibePatternStep *removed_node = s_vibe_queue_head; + s_vibe_queue_head = (VibePatternStep*)list_pop_head((ListNode*)s_vibe_queue_head); + kernel_free(removed_node); + } + prv_vibes_set_vibe_strength(VIBE_STRENGTH_OFF); + s_pattern_in_progress = false; + mutex_unlock(s_vibe_pattern_mutex); +} + +void pbl_analytics_external_collect_vibe_stats(void) { + mutex_lock(s_vibe_pattern_mutex); + + // Capture one final sample to account for time since last strength change + uint8_t current_strength_pct = (ABS(s_vibe_strength) * 100) / VIBE_STRENGTH_MAX; + prv_update_strength_analytics(current_strength_pct); + + // Calculate time-weighted average strength using internally tracked on-time + uint32_t avg_strength_pct = 0; + if (s_total_vibe_on_time_ms > 0) { + // Calculate weighted average: sum(strength × time) / total_time + avg_strength_pct = s_strength_time_product_sum / s_total_vibe_on_time_ms; + } + + PBL_ANALYTICS_SET_UNSIGNED(vibrator_avg_strength_pct, avg_strength_pct); + + // Reset accumulators for next period + s_strength_time_product_sum = 0; + s_total_vibe_on_time_ms = 0; + s_last_strength_sample_ticks = rtc_get_ticks(); + + mutex_unlock(s_vibe_pattern_mutex); +} diff --git a/src/fw/services/vibe_pattern/wscript_build b/src/fw/services/vibe_pattern/wscript_build new file mode 100644 index 0000000000..da2e5c2520 --- /dev/null +++ b/src/fw/services/vibe_pattern/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes', 'services_analytics'], + source=['service.c'], + target='services_vibe_pattern', +) +bld.env.SERVICES.append('services_vibe_pattern') diff --git a/src/fw/services/vibes/Kconfig b/src/fw/services/vibes/Kconfig new file mode 100644 index 0000000000..84c6c160c1 --- /dev/null +++ b/src/fw/services/vibes/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_VIBES + bool "Vibes" + help + Vibe scores / intensity service. + +if SERVICE_VIBES + +module = SERVICE_VIBES +module-str = Vibes +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/vibes/vibe_client.c b/src/fw/services/vibes/vibe_client.c new file mode 100644 index 0000000000..e7383ec062 --- /dev/null +++ b/src/fw/services/vibes/vibe_client.c @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/vibes/vibe_client.h" + +#include "applib/ui/vibes.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/vibes/vibe_score.h" +#include "pbl/services/vibes/vibe_score_info.h" +#include "system/logging.h" + +PBL_LOG_MODULE_DEFINE(service_vibes, CONFIG_SERVICE_VIBES_LOG_LEVEL); + +static VibeScoreId prv_get_resource_for_client(VibeClient client) { + if (client == VibeClient_AlarmsLPM) { + return VibeScoreId_AlarmsLPM; + } + return alerts_preferences_get_vibe_score_for_client(client); +} + +VibeScore *vibe_client_get_score(VibeClient client) { + VibeScoreId id = prv_get_resource_for_client(client); + if (id == VibeScoreId_Disabled) { + return NULL; + } + VibeScore *score = vibe_score_create_with_resource(vibe_score_info_get_resource_id(id)); + if (!score) { + PBL_LOG_ERR("Got a null VibeScore resource!"); + } + return score; +} diff --git a/src/fw/services/vibes/vibe_intensity.c b/src/fw/services/vibes/vibe_intensity.c new file mode 100644 index 0000000000..8e247dbee4 --- /dev/null +++ b/src/fw/services/vibes/vibe_intensity.c @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/vibes/vibe_intensity.h" + +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/vibe_pattern.h" +#include "pbl/services/notifications/alerts_preferences_private.h" + +uint8_t get_strength_for_intensity(VibeIntensity intensity) { + switch (intensity) { + case VibeIntensityLow: + return 40; + case VibeIntensityMedium: + return 60; + case VibeIntensityHigh: + return 100; + default: + return 100; + } +} + +void vibe_intensity_init(void) { + vibe_intensity_set(vibe_intensity_get()); +} + +void vibe_intensity_set(VibeIntensity intensity) { + vibes_set_default_vibe_strength(get_strength_for_intensity(intensity)); +} + +VibeIntensity vibe_intensity_get(void) { + return alerts_preferences_get_vibe_intensity(); +} + +const char *vibe_intensity_get_string_for_intensity(VibeIntensity intensity) { + switch (intensity) { + case VibeIntensityLow: { + /// Standard vibration pattern option that has a low intensity + return i18n_noop("Standard - Low"); + } + case VibeIntensityMedium: { + /// Standard vibration pattern option that has a medium intensity + return i18n_noop("Standard - Medium"); + } + case VibeIntensityHigh: { + /// Standard vibration pattern option that has a high intensity + return i18n_noop("Standard - High"); + } + default: { + return NULL; + } + } +} + +VibeIntensity vibe_intensity_cycle_next(VibeIntensity intensity) { + return (intensity + 1) % VibeIntensityNum; +} diff --git a/src/fw/services/normal/vibes/vibe_score.c b/src/fw/services/vibes/vibe_score.c similarity index 92% rename from src/fw/services/normal/vibes/vibe_score.c rename to src/fw/services/vibes/vibe_score.c index 6125c97dd7..31190ed0cd 100644 --- a/src/fw/services/normal/vibes/vibe_score.c +++ b/src/fw/services/vibes/vibe_score.c @@ -1,29 +1,19 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vibe_score.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/vibes/vibe_score.h" #include "process_management/app_manager.h" #include "syscall/syscall.h" #include "system/passert.h" +#include "system/logging.h" #include "drivers/vibe.h" #include "applib/applib_malloc.auto.h" #include "util/net.h" +PBL_LOG_MODULE_DECLARE(service_vibes, CONFIG_SERVICE_VIBES_LOG_LEVEL); + #define VIBE_SCORE_MAX_REPEAT_DELAY_MS (10000) // matches MAX_VIBE_DURATION_MS in vibe_pattern static VibeNote *prv_vibe_score_get_note_list(GenericAttribute *notes_attribute) { @@ -211,8 +201,10 @@ void vibe_score_do_vibe(VibeScore *score) { VibeNoteIndex *pattern_list = prv_vibe_score_get_pattern_list(pattern_attribute); unsigned int pattern_length = prv_vibe_score_get_pattern_length(pattern_attribute); + unsigned int total_duration_ms = 0; for (unsigned int i = 0; i < pattern_length; i++) { VibeNote *note = ¬e_list[pattern_list[i]]; + total_duration_ms += note->vibe_duration_ms + note->brake_duration_ms; if (note->vibe_duration_ms > 0) { sys_vibe_pattern_enqueue_step_raw(note->vibe_duration_ms, note->strength); } @@ -220,6 +212,9 @@ void vibe_score_do_vibe(VibeScore *score) { sys_vibe_pattern_enqueue_step_raw(note->brake_duration_ms, vibe_get_braking_strength()); } } + unsigned int repeat_delay = vibe_score_get_repeat_delay_ms(score); + PBL_LOG_INFO("vibe_score: do_vibe, %u notes, %ums total, repeat_delay=%ums", + pattern_length, total_duration_ms, repeat_delay); sys_vibe_pattern_trigger_start(); } diff --git a/src/fw/services/normal/vibes/vibe_score_info.c b/src/fw/services/vibes/vibe_score_info.c similarity index 79% rename from src/fw/services/normal/vibes/vibe_score_info.c rename to src/fw/services/vibes/vibe_score_info.c index 5f9105f9a4..8520e56170 100644 --- a/src/fw/services/normal/vibes/vibe_score_info.c +++ b/src/fw/services/vibes/vibe_score_info.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "vibe_score_info.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/vibes/vibe_score_info.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "system/passert.h" #include "util/size.h" @@ -26,7 +13,9 @@ typedef enum AlertType { AlertType_Calls = 1 << 1, AlertType_Alarms = 1 << 2, AlertType_AlarmsLPM = 1 << 3, - AlertType_All = AlertType_Notifications | AlertType_Calls | AlertType_Alarms, + AlertType_Hourly = 1 << 4, + AlertType_OnDisconnect = 1 << 5, + AlertType_All = AlertType_Notifications | AlertType_Calls | AlertType_Alarms | AlertType_Hourly | AlertType_OnDisconnect, } AlertType; typedef struct { @@ -92,6 +81,14 @@ VibeScoreId vibe_score_info_cycle_next(VibeClient client, VibeScoreId curr_id) { alert_type = AlertType_Alarms; break; } + case VibeClient_Hourly: { + alert_type = AlertType_Hourly; + break; + } + case VibeClient_OnDisconnect: { + alert_type = AlertType_OnDisconnect; + break; + } default: { WTF; } diff --git a/src/fw/services/vibes/vibes.def b/src/fw/services/vibes/vibes.def new file mode 100644 index 0000000000..c491a0ab50 --- /dev/null +++ b/src/fw/services/vibes/vibes.def @@ -0,0 +1,15 @@ +// IDs must be monotonically increasing, i.e. they must never be reused +// ID 0 is reserved for "Invalid". Do not use! + +VIBE_DEF(1, Disabled, i18n_ctx_noop("NotifVibe", "Disabled"), (AlertType_Notifications | AlertType_Calls | AlertType_Hourly| AlertType_OnDisconnect), RESOURCE_ID_INVALID) +VIBE_DEF(2, StandardShortPulseLow, i18n_noop("Standard - Low"), (AlertType_Notifications | AlertType_Hourly | AlertType_OnDisconnect), RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW) +VIBE_DEF(3, StandardLongPulseLow, i18n_noop("Standard - Low"), (AlertType_Calls | AlertType_Alarms), RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW) +VIBE_DEF(4, StandardShortPulseHigh, i18n_noop("Standard - High"), (AlertType_Notifications | AlertType_Hourly | AlertType_OnDisconnect), RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH) +VIBE_DEF(5, StandardLongPulseHigh, i18n_noop("Standard - High"), (AlertType_Calls | AlertType_Alarms), RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH) +VIBE_DEF(8, Pulse, i18n_noop("Pulse"), AlertType_All, RESOURCE_ID_VIBE_SCORE_PULSE) +VIBE_DEF(9, NudgeNudge, i18n_noop("Nudge Nudge"), AlertType_All, RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE) +VIBE_DEF(10, Jackhammer, i18n_noop("Jackhammer"), AlertType_All, RESOURCE_ID_VIBE_SCORE_JACKHAMMER) +VIBE_DEF(11, Reveille, "Reveille", AlertType_Alarms, RESOURCE_ID_VIBE_SCORE_REVEILLE) +VIBE_DEF(12, Mario, "Mario", AlertType_All, RESOURCE_ID_VIBE_SCORE_MARIO) +VIBE_DEF(13, AlarmsLPM, "ALARMS LPM", AlertType_AlarmsLPM, RESOURCE_ID_VIBE_SCORE_ALARM_LPM) +VIBE_DEF(14, Gentle, i18n_noop("Gentle"), AlertType_Alarms, RESOURCE_ID_VIBE_SCORE_GENTLE) diff --git a/src/fw/services/vibes/wscript_build b/src/fw/services/vibes/wscript_build new file mode 100644 index 0000000000..bc03a354a9 --- /dev/null +++ b/src/fw/services/vibes/wscript_build @@ -0,0 +1,21 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'vibe_client.c', + 'vibe_intensity.c', + 'vibe_score.c', + 'vibe_score_info.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_vibes', +) + +bld.env.SERVICES.append('services_vibes') + +target = bld.path.find_or_declare('services_vibes.pot') +bld.gettext(source=sources, target=target) +bld.env.SERVICES_TEXT.append(target) diff --git a/src/fw/services/voice/Kconfig b/src/fw/services/voice/Kconfig new file mode 100644 index 0000000000..a20f518d9b --- /dev/null +++ b/src/fw/services/voice/Kconfig @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_VOICE + bool "Voice" + default y if MIC + help + Voice (dictation) service. Requires a microphone driver (CONFIG_MIC). + +if SERVICE_VOICE + +module = SERVICE_VOICE +module-str = Voice +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/voice/transcription.c b/src/fw/services/voice/transcription.c similarity index 79% rename from src/fw/services/normal/voice/transcription.c rename to src/fw/services/voice/transcription.c index 09b1f7318a..12dd5c2893 100644 --- a/src/fw/services/normal/voice/transcription.c +++ b/src/fw/services/voice/transcription.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "transcription.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/voice/transcription.h" #include "system/passert.h" diff --git a/src/fw/services/voice/voice.c b/src/fw/services/voice/voice.c new file mode 100644 index 0000000000..6b0769af29 --- /dev/null +++ b/src/fw/services/voice/voice.c @@ -0,0 +1,721 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/voice/voice.h" + +#include "bluetooth/responsiveness.h" +#include "board/board.h" +#include "drivers/mic.h" +#include "kernel/events.h" +#include "kernel/pbl_malloc.h" +#include "os/mutex.h" +#include "process_management/app_install_manager.h" +#include "process_management/app_manager.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/voice/transcription.h" +#include "pbl/services/voice/voice_speex.h" +#include "pbl/services/voice_endpoint.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "system/passert.h" +#include "system/profiler.h" +#include "util/likely.h" +#include "util/uuid.h" + +#include + +PBL_LOG_MODULE_DEFINE(service_voice, CONFIG_SERVICE_VOICE_LOG_LEVEL); + +#define SPEEX_BITSTREAM_VERSION (4) + +#define TIMEOUT_SESSION_SETUP (8000) +#define TIMEOUT_SESSION_RESULT (15000) + +// Buffer size +#define MAX_ENCODED_FRAME_SIZE (200) + +typedef enum { + SessionState_Idle = 0, + SessionState_StartSession, + SessionState_VoiceEndpointSetupReceived, + SessionState_AudioEndpointSetupReceived, + SessionState_Recording, + SessionState_WaitForSessionResult, +} SessionState; + +static SessionState s_state = SessionState_Idle; + +static PebbleMutex* s_lock = NULL; + +// Handle requests from apps +static bool s_from_app; +static Uuid s_app_uuid; + +static AudioEndpointSessionId s_session_id = AUDIO_ENDPOINT_SESSION_INVALID_ID; +static TimerID s_timeout = TIMER_INVALID_ID; + +// Session generation & teardown guards to mitigate race conditions between explicit cancel +// and timeout callbacks executing on the timer thread. +static uint32_t s_session_generation = 0; // Monotonic session counter +static uint32_t s_timeout_generation = 0; // Generation tied to currently scheduled timeout +static bool s_teardown_in_progress = false; // Debounce concurrent teardown paths + +static void prv_send_event(VoiceEventType event_type, VoiceStatus status, + PebbleVoiceServiceEventData *data); +static void prv_session_result_timeout(void * data); + +static void prv_audio_data_handler(int16_t *samples, size_t sample_count, void *context) { + if (!voice_speex_is_initialized()) { + PBL_LOG_DBG("Speex not initialized, dropping audio data"); + return; + } + + if (s_state != SessionState_Recording) { + PBL_LOG_DBG("Not recording, dropping audio data"); + return; + } + + // Additional safety check - ensure we have a valid session + if (s_session_id == AUDIO_ENDPOINT_SESSION_INVALID_ID) { + return; + } + + // Ensure we have the right amount of data for a frame + size_t expected_samples = voice_speex_get_frame_size(); + if (sample_count != expected_samples) { + PBL_LOG_DBG("Unexpected audio sample count: got %zu, expected %zu", sample_count, expected_samples); + return; + } + + // Encode the audio frame + uint8_t encoded_buffer[MAX_ENCODED_FRAME_SIZE]; // Max encoded frame size + int encoded_bytes = voice_speex_encode_frame(samples, encoded_buffer, sizeof(encoded_buffer)); + + if (encoded_bytes > 0) { + // Send encoded data to audio endpoint + audio_endpoint_add_frame(s_session_id, encoded_buffer, encoded_bytes); + } else { + PBL_LOG_DBG("Failed to encode audio frame"); + } +} + +static void prv_teardown_session(void) { + // Reset communication session responsiveness to default after voice session ends + CommSession *comm_session = comm_session_get_system_session(); + if (comm_session) { + comm_session_set_responsiveness(comm_session, BtConsumerPpVoiceEndpoint, ResponseTimeMax, 0); + } +} + +static void prv_stop_recording(void) { + PBL_LOG_DBG("prv_stop_recording called - stopping mic and audio endpoint transfer"); + + // First, set state to non-recording to prevent any new audio processing + s_state = SessionState_WaitForSessionResult; + + // Stop audio endpoint transfer BEFORE stopping microphone + // This prevents new frames from being added while the endpoint shuts down + audio_endpoint_stop_transfer(s_session_id); + + mic_stop(MIC); + + PBL_LOG_INFO("Stop recording audio"); + prv_teardown_session(); + + // Speex cleanup will be handled by delayed cleanup to avoid race conditions +} + +static void prv_cancel_recording(void) { + PBL_LOG_DBG("prv_cancel_recording called - cancelling mic and audio endpoint transfer"); + mic_stop(MIC); + + audio_endpoint_cancel_transfer(s_session_id); + PBL_LOG_INFO("Cancel audio recording"); + prv_teardown_session(); +} + +static void prv_cancel_early_session(void) { + // For early cancellation, only cancel the audio endpoint transfer + // Don't call mic_stop() since the microphone was never started + audio_endpoint_cancel_transfer(s_session_id); + PBL_LOG_INFO("Cancel audio recording"); + prv_teardown_session(); +} + +static void prv_reset(void) { + s_state = SessionState_Idle; + s_session_id = AUDIO_ENDPOINT_SESSION_INVALID_ID; +} + +static void prv_cancel_session(void) { + prv_cancel_recording(); + prv_reset(); +} + +static void prv_start_result_timeout(void) { + new_timer_start(s_timeout, TIMEOUT_SESSION_RESULT, prv_session_result_timeout, NULL, 0); +} + +static void prv_audio_transfer_stopped_handler(AudioEndpointSessionId session_id) { + mutex_lock(s_lock); + PBL_LOG_DBG("prv_audio_transfer_stopped_handler called with session_id=%d (current=%d)", + session_id, s_session_id); + + if (s_session_id != session_id) { + PBL_LOG_WRN("Received audio transfer message when no session was in progress (" + "%d)", session_id); + mutex_unlock(s_lock); + return; + } + + if (s_state != SessionState_Recording) { + PBL_LOG_WRN("Received stop message from phone after audio session " + "stopped/cancelled"); + mutex_unlock(s_lock); + return; + } + + PBL_LOG_DBG("Stopping recording due to phone request"); + // TODO: Handle this better: there is no feedback to the UI that we've stopped recording + s_state = SessionState_WaitForSessionResult; + prv_stop_recording(); + s_timeout_generation = s_session_generation; + prv_start_result_timeout(); + mutex_unlock(s_lock); +} + +static bool prv_start_recording(void) { + PBL_LOG_DBG("prv_start_recording called"); + // Start microphone with Speex frame buffer + int16_t *frame_buffer = voice_speex_get_frame_buffer(); + size_t frame_size_samples = voice_speex_get_frame_size(); // Get frame size in samples + + PBL_LOG_DBG("Got Speex frame buffer: %p, frame_size_samples: %zu", frame_buffer, frame_size_samples); + + if (frame_buffer && frame_size_samples > 0) { + PBL_LOG_DBG("Starting microphone with frame buffer"); + if (!mic_start(MIC, &prv_audio_data_handler, NULL, frame_buffer, frame_size_samples)) { + PBL_LOG_ERR("Failed to start microphone for voice session"); + return false; + } + PBL_LOG_DBG("Microphone started successfully"); + } else { + PBL_LOG_ERR("Invalid Speex frame buffer"); + return false; + } + + return true; +} + +static void prv_send_event(VoiceEventType event_type, VoiceStatus status, + PebbleVoiceServiceEventData *data) { + PebbleEvent event = { + .type = PEBBLE_VOICE_SERVICE_EVENT, + .voice_service = { + .type = event_type, + .status = status, + .data = data, + } + }; + event_put(&event); +} + +//! Expects s_lock is held by caller +static void prv_handle_subsystem_started(SessionState transition_to_state) { + PBL_LOG_DBG("prv_handle_subsystem_started called: transition_to_state=%d, current_state=%d", + transition_to_state, s_state); + + PBL_ASSERTN(transition_to_state == SessionState_VoiceEndpointSetupReceived || + transition_to_state == SessionState_AudioEndpointSetupReceived); + + if (s_state == SessionState_Idle) { // we error'ed out + PBL_LOG_DBG("Session already errored out (state=Idle), ignoring subsystem start"); + return; + } + + if (s_state == SessionState_StartSession) { + // we are still waiting for one of the subsystems to be ready + PBL_LOG_DBG("First subsystem ready, transitioning to state %d", transition_to_state); + s_state = transition_to_state; + } else { + PBL_ASSERTN((s_state == SessionState_VoiceEndpointSetupReceived || + s_state == SessionState_AudioEndpointSetupReceived) && + (transition_to_state != s_state)); + + PBL_LOG_DBG("Both subsystems ready, transitioning to Recording state"); + s_state = SessionState_Recording; + + new_timer_stop(s_timeout); + + PBL_LOG_DBG("Starting recording now that both subsystems are ready"); + if (!prv_start_recording()) { + PBL_LOG_ERR("Voice session setup failed while starting recording"); + prv_cancel_session(); + prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusErrorGeneric, NULL); + return; + } + + // Indicate to the UI that we have started recording + PBL_LOG_INFO("Session setup successfully"); + prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusSuccess, NULL); + } +} + +static void prv_session_result_timeout(void * data) { + mutex_lock(s_lock); + + if (s_teardown_in_progress || (s_timeout_generation != s_session_generation)) { + PBL_LOG_DBG("Ignoring stale session result timeout (t_gen=%"PRIu32" cur=%"PRIu32" teardown=%d)", + s_timeout_generation, s_session_generation, s_teardown_in_progress); + mutex_unlock(s_lock); + return; + } + + PBL_ASSERTN(s_state == SessionState_WaitForSessionResult); + + prv_reset(); + PBL_LOG_WRN("Timeout waiting for session result"); + + prv_send_event(VoiceEventTypeSessionResult, VoiceStatusTimeout, NULL); + + mutex_unlock(s_lock); +} + +static void prv_session_setup_timeout(void * data) { + mutex_lock(s_lock); + if (s_teardown_in_progress || (s_timeout_generation != s_session_generation)) { + PBL_LOG_DBG("Ignoring stale session setup timeout (t_gen=%"PRIu32" cur=%"PRIu32" teardown=%d)", + s_timeout_generation, s_session_generation, s_teardown_in_progress); + mutex_unlock(s_lock); + return; + } + + PBL_ASSERTN(s_state == SessionState_StartSession || + s_state == SessionState_VoiceEndpointSetupReceived || + s_state == SessionState_AudioEndpointSetupReceived); + + prv_cancel_session(); + PBL_LOG_WRN("Timeout waiting for session setup result "); + + prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusTimeout, NULL); + + mutex_unlock(s_lock); +} + +static VoiceStatus prv_get_status_from_result(VoiceEndpointResult result) { + VoiceStatus status; + switch (result) { + case VoiceEndpointResultFailServiceUnavailable: + status = VoiceStatusErrorConnectivity; + break; + case VoiceEndpointResultFailDisabled: + status = VoiceStatusErrorDisabled; + break; + case VoiceEndpointResultFailInvalidRecognizerResponse: + status = VoiceStatusRecognizerResponseError; + break; + case VoiceEndpointResultFailTimeout: + case VoiceEndpointResultFailRecognizerError: + case VoiceEndpointResultFailInvalidMessage: + default: + status = VoiceStatusErrorGeneric; + break; + } + return status; +} + +void voice_init(void) { + s_lock = mutex_create(); + // Speex encoder is now initialized lazily when a dictation session starts +} + +// This will kick off a dictation session. After the setup session message is sent via the +// voice control endpoint, we wait for a session ready response via the +// voice_handle_session_setup_result call or a session setup timeout occurs (timer callback +// prv_session_setup_timeout) +VoiceSessionId voice_start_dictation(VoiceEndpointSessionType session_type) { + PBL_LOG_DBG("voice_start_dictation called with session_type: %d", session_type); + mutex_lock(s_lock); + + // Lazily initialize Speex encoder to avoid baseline memory usage when voice not used + if (!voice_speex_is_initialized()) { + if (!voice_speex_init()) { + PBL_LOG_ERR("Failed to initialize Speex encoder"); + mutex_unlock(s_lock); + return VOICE_SESSION_ID_INVALID; + } + } + + if (s_state != SessionState_Idle) { + PBL_LOG_DBG("Voice service not idle (state: %d), returning invalid session", s_state); + mutex_unlock(s_lock); + return VOICE_SESSION_ID_INVALID; + } + + // Start new session generation and clear teardown guard + s_session_generation++; + if (UNLIKELY(s_session_generation == 0)) { // handle wrap-around (very unlikely) + s_session_generation = 1; + } + s_teardown_in_progress = false; + PBL_LOG_DBG("Setting state to StartSession"); + s_state = SessionState_StartSession; + + // check if we're being started from an app so we know to send the UUID when setting up a session + s_from_app = ((pebble_task_get_current() == PebbleTask_App) && + !app_install_id_from_system(app_manager_get_current_app_id())); + if (s_from_app) { + s_app_uuid = app_manager_get_current_app_md()->uuid; + char uuid_str[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(&s_app_uuid, uuid_str); + PBL_LOG_INFO("Starting app-initiated voice dictation session for app %s", uuid_str); + } else { + PBL_LOG_DBG("Starting system-initiated voice dictation session"); + } + + // Set up communication session responsiveness for voice session + CommSession *comm_session = comm_session_get_system_session(); + if (comm_session) { + comm_session_set_responsiveness(comm_session, BtConsumerPpVoiceEndpoint, ResponseTimeMin, + MIN_LATENCY_MODE_TIMEOUT_VOICE_SECS); + } + + // Get Speex transfer info + AudioTransferInfoSpeex transfer_info; + voice_speex_get_transfer_info(&transfer_info); + PBL_LOG_DBG("Got Speex transfer info: sample_rate=%"PRIu32", bit_rate=%"PRIu16", frame_size=%"PRIu16, + transfer_info.sample_rate, transfer_info.bit_rate, transfer_info.frame_size); + + PBL_LOG_DBG("Setting up audio endpoint transfer"); + s_session_id = audio_endpoint_setup_transfer(prv_audio_transfer_stopped_handler); + PBL_ASSERTN(s_session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); + PBL_LOG_DBG("Audio endpoint transfer setup complete with session_id=%d", s_session_id); + + + PBL_LOG_INFO("Send session setup message. Session type: %d", session_type); + PBL_LOG_DBG("Calling voice_endpoint_setup_session"); + voice_endpoint_setup_session(session_type, s_session_id, &transfer_info, + s_from_app ? &s_app_uuid : NULL); + + if (s_timeout == TIMER_INVALID_ID) { + s_timeout = new_timer_create(); + } + s_timeout_generation = s_session_generation; + new_timer_start(s_timeout, TIMEOUT_SESSION_SETUP, prv_session_setup_timeout, NULL, 0); + + PBL_LOG_DBG("Audio transfer setup complete, handling subsystem started"); + prv_handle_subsystem_started(SessionState_AudioEndpointSetupReceived); + + mutex_unlock(s_lock); + return s_session_id; +} + +// Calling this will end the recording, disable the mic and and stop the audio transfer session. We +// expect voice_handle_dictation_result to be called next with a dictation response +void voice_stop_dictation(VoiceSessionId session_id) { + mutex_lock(s_lock); + if ((s_state == SessionState_Idle) || + (session_id != s_session_id) || + (session_id == VOICE_SESSION_ID_INVALID)) { + goto unlock; + } + + if (s_state != SessionState_Recording) { + mutex_unlock(s_lock); + voice_cancel_dictation(session_id); + return; + } + + s_state = SessionState_WaitForSessionResult; + prv_stop_recording(); + prv_start_result_timeout(); + +unlock: + mutex_unlock(s_lock); +} + +void voice_cancel_dictation(VoiceSessionId session_id) { + mutex_lock(s_lock); + if ((session_id != s_session_id) || + (session_id == VOICE_SESSION_ID_INVALID)) { + goto unlock; + } + + if (s_state != SessionState_Idle) { + bool stopped = new_timer_stop(s_timeout); + if (!stopped) { + // Timer callback in progress or already firing; invalidate generation so callback no-ops + s_timeout_generation = 0; + PBL_LOG_DBG("Timeout stop race (cancel), invalidating timeout generation"); + } + s_teardown_in_progress = true; + if (s_state == SessionState_StartSession || + s_state == SessionState_VoiceEndpointSetupReceived || + s_state == SessionState_AudioEndpointSetupReceived) { + prv_cancel_early_session(); + } else if (s_state == SessionState_Recording) { + prv_stop_recording(); + } + prv_reset(); + } + +unlock: + mutex_unlock(s_lock); +} + +// This will trigger an event to be sent to the main task indicating success or failure to set up +// a session. If the session setup result was success, the microphone will be enabled and we'll +// start sending Speex encoded data via the audio endpoint to the phone. voice_stop_dictation will +// end the recording +void voice_handle_session_setup_result(VoiceEndpointResult result, + VoiceEndpointSessionType session_type, bool app_initiated) { + PBL_LOG_DBG("voice_handle_session_setup_result: result=%d, session_type=%d, app_initiated=%d", + result, session_type, app_initiated); + PBL_LOG_DBG("Current state: %d", s_state); + + mutex_lock(s_lock); + + if (s_state == SessionState_Idle) { + PBL_LOG_DBG("State is Idle, ignoring session setup result"); + goto unlock; + } + + bool has_error = true; + + if (s_state != SessionState_StartSession && + s_state != SessionState_AudioEndpointSetupReceived && + s_state != SessionState_VoiceEndpointSetupReceived) { + PBL_LOG_WRN("Session setup result received when not expected, state=%d", + (int)s_state); + prv_cancel_session(); + VoiceEventType event_type = (s_state <= SessionState_StartSession) ? + VoiceEventTypeSessionSetup : VoiceEventTypeSessionResult; + prv_send_event(event_type, VoiceStatusErrorGeneric, NULL); + goto done; + } + + // If we already received the voice endpoint setup, a duplicate is unexpected. + // Log a warning and ignore it rather than asserting in prv_handle_subsystem_started(). + if (s_state == SessionState_VoiceEndpointSetupReceived) { + PBL_LOG_WRN("Duplicate voice endpoint setup result received, ignoring"); + goto unlock; + } + + if (session_type >= VoiceEndpointSessionTypeCount) { + PBL_LOG_WRN("Session setup result for invalid session type received"); + goto done; + } + + if (result != VoiceEndpointResultSuccess) { + PBL_LOG_DBG("Session setup failed with result %d", result); + prv_cancel_session(); + VoiceStatus status = prv_get_status_from_result(result); + PBL_LOG_WRN("Error occurred setting up session: %d", result); + prv_send_event(VoiceEventTypeSessionSetup, status, NULL); + goto done; + } + + if (app_initiated != s_from_app) { + PBL_LOG_DBG("App initiated mismatch - received=%d, expected=%d", app_initiated, s_from_app); + prv_cancel_session(); + if (app_initiated) { + PBL_LOG_WRN("Received session setup result for app initiated session when it " + "was not expected"); + } else { + PBL_LOG_WRN("Received session setup result for non-app session when an app " + "session result was expected"); + } + prv_send_event(VoiceEventTypeSessionSetup, VoiceStatusErrorGeneric, NULL); + goto done; + } + + PBL_LOG_DBG("Session setup successful!"); + has_error = false; + +done: + if (has_error) { + PBL_LOG_DBG("Session setup had errors, stopping timeout timer"); + new_timer_stop(s_timeout); + } else { + PBL_LOG_DBG("Session setup complete, notifying voice endpoint subsystem started"); + prv_handle_subsystem_started(SessionState_VoiceEndpointSetupReceived); + } +unlock: + mutex_unlock(s_lock); +} + +static bool prv_get_string_size_cb(const TranscriptionWord *word, void *data) { + size_t *size = data; + *size += word->length + sizeof(char); // add 1 for space or null terminator + return true; +} + +static bool prv_build_string_cb(const TranscriptionWord *word, void *data) { + char *sentence = data; + + // if the current word is a punctuation mark strip out backspace (phone app inserts backspace + // before punctuation mark) and do not insert a space before the word + if (word->data[0] == '\x08') { + strncat(sentence, (char *) &word->data[1], word->length - 1); + } else { + // if this is not the beginning of the string, insert a space before the word + if (strlen(sentence) != 0) { + strcat(sentence, " "); + } + strncat(sentence, (char *) word->data, word->length); + } + + return true; +} + +static bool prv_handle_dictation_nlp_result_common(VoiceEndpointResult result, + AudioEndpointSessionId session_id, + bool app_initiated, Uuid *app_uuid) { + if (s_state == SessionState_Idle) { + return false; + } + + // stop timer before changing state variable + new_timer_stop(s_timeout); + + if (s_state != SessionState_WaitForSessionResult) { + // This handles erroneous replies from the phone app (sometimes the phone app sends a session + // result immediately after we start streaming + PBL_LOG_WRN("Session result when not expected (result: %d, " + "session_id: %d)", result, session_id); + if (s_state == SessionState_Recording) { + prv_stop_recording(); + } else { + prv_cancel_recording(); + } + VoiceEventType event_type = (s_state <= SessionState_StartSession) ? + VoiceEventTypeSessionSetup : VoiceEventTypeSessionResult; + prv_send_event(event_type, VoiceStatusErrorGeneric, NULL); + return false; + } + + if (s_session_id != session_id) { + PBL_LOG_WRN("Received session result for wrong session (Expected: " + "%"PRIu16"; Received: %"PRIu16, s_session_id, session_id); + prv_send_event(VoiceEventTypeSessionResult, VoiceStatusErrorGeneric, NULL); + return false; + } + + if (result != VoiceEndpointResultSuccess) { + VoiceStatus status = prv_get_status_from_result(result); + PBL_LOG_WRN("Error occurred processing result: %d", result); + prv_send_event(VoiceEventTypeSessionResult, status, NULL); + return false; + } + + // Make sure that if this is an app initiated session, we're expecting a response for an app + // initiated session and that if this is an app initiated session, the app UUID matches the + // expected UUID + if ((app_initiated != s_from_app) || (s_from_app && !uuid_equal(&s_app_uuid, app_uuid))) { + if (app_initiated) { + PBL_LOG_WRN("Received session result for app initiated session when a " + "non-app session result was expected"); + } else { + PBL_LOG_WRN("Received session result for non-app session when an app " + "session result was expected"); + } + prv_send_event(VoiceEventTypeSessionResult, VoiceStatusErrorGeneric, NULL); + return false; + } + + return true; +} + +// receiving this ends the session, sending an event to the main task with the result +void voice_handle_dictation_result(VoiceEndpointResult result, AudioEndpointSessionId session_id, + Transcription *transcription, bool app_initiated, + Uuid *app_uuid) { + mutex_lock(s_lock); + + if (!prv_handle_dictation_nlp_result_common(result, session_id, app_initiated, app_uuid)) { + goto unlock; + } + + // Calculate size of string + size_t sentence_size = 0; + transcription_iterate_words(transcription->sentences[0].words, + transcription->sentences[0].word_count, prv_get_string_size_cb, &sentence_size); + + const size_t event_size = sizeof(PebbleVoiceServiceEventData) + sentence_size; + PebbleVoiceServiceEventData *event_data = kernel_zalloc_check(event_size); + + // TODO: Final UI will probably demand a more sophisticated input, but this service will be + // updated to support additional features when the final UI is implemented + // Build string by concatenating each word in the first sentence + transcription_iterate_words(transcription->sentences[0].words, + transcription->sentences[0].word_count, prv_build_string_cb, event_data->sentence); + + if (app_initiated) { + char uuid_str[UUID_STRING_BUFFER_LENGTH]; + uuid_to_string(app_uuid, uuid_str); + PBL_LOG_INFO("Transcription received (%"PRIu32" B) for app %s", + (uint32_t)sentence_size, uuid_str); + } else { + PBL_LOG_INFO("Transcription received (%"PRIu32" B)", (uint32_t)sentence_size); + } + + prv_send_event(VoiceEventTypeSessionResult, VoiceStatusSuccess, event_data); + +unlock: + prv_reset(); + mutex_unlock(s_lock); +} + +// receiving this ends the session, sending an event to the main task with the result +void voice_handle_nlp_result(VoiceEndpointResult result, AudioEndpointSessionId session_id, + char *reminder, time_t timestamp) { + mutex_lock(s_lock); + + const bool app_initiated = false; + Uuid *app_uuid = NULL; + if (!prv_handle_dictation_nlp_result_common(result, session_id, app_initiated, app_uuid)) { + goto unlock; + } + + const size_t sentence_size = strlen(reminder) + 1; + const size_t event_size = sizeof(PebbleVoiceServiceEventData) + sentence_size; + PebbleVoiceServiceEventData *event_data = kernel_zalloc_check(event_size); + *event_data = (PebbleVoiceServiceEventData) { + .timestamp = timestamp, + }; + strncpy(event_data->sentence, reminder, sentence_size); + + prv_send_event(VoiceEventTypeSessionResult, VoiceStatusSuccess, event_data); + +unlock: + prv_reset(); + mutex_unlock(s_lock); +} + +DEFINE_SYSCALL(VoiceSessionId, sys_voice_start_dictation, VoiceEndpointSessionType session_type) { + if (session_type >= VoiceEndpointSessionTypeCount) { + return AUDIO_ENDPOINT_SESSION_INVALID_ID; + } + return voice_start_dictation(session_type); +} + +DEFINE_SYSCALL(void, sys_voice_stop_dictation, VoiceSessionId session_id) { + voice_stop_dictation(session_id); +} + +DEFINE_SYSCALL(void, sys_voice_cancel_dictation, VoiceSessionId session_id) { + voice_cancel_dictation(session_id); +} + +void voice_kill_app_session(PebbleTask task) { + if (task != PebbleTask_App) { + return; + } + mutex_lock(s_lock); + if (s_from_app && (s_session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID)) { + prv_cancel_session(); + } + mutex_unlock(s_lock); +} diff --git a/src/fw/services/voice/voice_speex.c b/src/fw/services/voice/voice_speex.c new file mode 100644 index 0000000000..4607812d2d --- /dev/null +++ b/src/fw/services/voice/voice_speex.c @@ -0,0 +1,251 @@ +/* SPDX-FileCopyrightText: 2025 Joshua Jun */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/voice/voice_speex.h" + +#include "board/board.h" +#include "system/logging.h" +#include "system/passert.h" +#include "kernel/pbl_malloc.h" +#include "system/logging.h" +#include "pbl/services/audio_endpoint.h" +#include "drivers/mic.h" + +#include "speex/speex.h" +#include "speex/speex_bits.h" +#include "speex/speex_header.h" +#include "speex/speex_stereo.h" + +#include +#include + +PBL_LOG_MODULE_DECLARE(service_voice, CONFIG_SERVICE_VOICE_LOG_LEVEL); + +// External Speex mode declarations +extern const SpeexMode speex_wb_mode; + +// Speex bitstream version +#define SPEEX_BITSTREAM_VERSION 4 + +// Speex encoder state +typedef struct { + void *enc_state; + SpeexBits bits; + SpeexStereoState stereo_state; + uint32_t frame_size; // Changed to uint32_t to match transfer info + uint32_t sample_rate; // Changed to uint32_t to match transfer info + uint16_t bit_rate; // Changed to uint16_t to match transfer info + uint8_t bitstream_version; // Changed to uint8_t to match transfer info + uint8_t channels; // 1 for mono, 2 for stereo + bool initialized; + uint8_t *frame_buffer; + size_t frame_buffer_size; + uint8_t *encoded_buffer; + size_t encoded_buffer_size; +} VoiceSpeexEncoder; + +static VoiceSpeexEncoder s_encoder = {0}; + +// Speex configuration +#define SPEEX_SAMPLE_RATE 16000 // 16 kHz wideband +#define SPEEX_BIT_RATE 9800 // 9.8 kbps +#define SPEEX_QUALITY 6 // Quality level (0-10) +#define SPEEX_COMPLEXITY 1 // Complexity (1-10, lower for embedded) +#define SPEEX_ENCODED_BUFFER_SIZE 320 // Max encoded frame size +#define SPEEX_AUDIO_GAIN 3 // Audio gain multiplier (3x) + +bool voice_speex_init(void) { + if (s_encoder.initialized) { + return true; + } + + memset(&s_encoder, 0, sizeof(s_encoder)); + + // Get channel count from mic device (default to mono if not specified) + s_encoder.channels = (uint8_t)mic_get_channels(MIC); + PBL_LOG_DBG("Mic channels: %"PRIu8, s_encoder.channels); + + // Initialize Speex encoder - use wideband mode for 16kHz sample rate + const SpeexMode *mode = &speex_wb_mode; + if (!mode) { + PBL_LOG_ERR("Failed to get Speex wideband mode"); + return false; + } + + s_encoder.enc_state = speex_encoder_init(mode); + if (!s_encoder.enc_state) { + PBL_LOG_ERR("Failed to initialize Speex encoder"); + return false; + } + + // Initialize bits structure + speex_bits_init(&s_encoder.bits); + + // Initialize stereo state if stereo + if (s_encoder.channels == 2) { + s_encoder.stereo_state = (SpeexStereoState)SPEEX_STEREO_STATE_INIT; + } + + // Get frame size + speex_encoder_ctl(s_encoder.enc_state, SPEEX_GET_FRAME_SIZE, &s_encoder.frame_size); + PBL_LOG_DBG("Initial frame size from Speex: %"PRIu32, s_encoder.frame_size); + + // Set encoder parameters + int tmp = SPEEX_QUALITY; + speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_QUALITY, &tmp); + + tmp = SPEEX_COMPLEXITY; + speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_COMPLEXITY, &tmp); + + tmp = SPEEX_SAMPLE_RATE; + speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_SAMPLING_RATE, &tmp); + PBL_LOG_DBG("Set sample rate to: %d", tmp); + + tmp = SPEEX_BIT_RATE; + speex_encoder_ctl(s_encoder.enc_state, SPEEX_SET_BITRATE, &tmp); + PBL_LOG_DBG("Set bit rate to: %d", tmp); + + // Get actual parameters + int actual_sample_rate, actual_bit_rate; + speex_encoder_ctl(s_encoder.enc_state, SPEEX_GET_SAMPLING_RATE, &actual_sample_rate); + speex_encoder_ctl(s_encoder.enc_state, SPEEX_GET_BITRATE, &actual_bit_rate); + + s_encoder.sample_rate = (uint32_t)actual_sample_rate; + s_encoder.bit_rate = (uint16_t)actual_bit_rate; + + s_encoder.bitstream_version = SPEEX_BITSTREAM_VERSION; + + // Allocate frame buffer (16-bit samples, multiplied by channel count for stereo) + s_encoder.frame_buffer_size = s_encoder.frame_size * sizeof(int16_t) * s_encoder.channels; + s_encoder.frame_buffer = (uint8_t *)kernel_malloc_check(s_encoder.frame_buffer_size); + + // Allocate encoded buffer + s_encoder.encoded_buffer_size = SPEEX_ENCODED_BUFFER_SIZE; + s_encoder.encoded_buffer = kernel_malloc_check(s_encoder.encoded_buffer_size); + + s_encoder.initialized = true; + + PBL_LOG_DBG("Speex encoder initialized: sample_rate=%"PRIu32", bit_rate=%"PRIu16", frame_size=%"PRIu32", channels=%"PRIu8, + s_encoder.sample_rate, s_encoder.bit_rate, s_encoder.frame_size, s_encoder.channels); + + // Verify sample rates match + if (s_encoder.sample_rate != MIC_SAMPLE_RATE) { + PBL_LOG_WRN("Speex sample rate (%"PRIu32") != Mic sample rate (%d)", + s_encoder.sample_rate, MIC_SAMPLE_RATE); + } + + return true; +} + +void voice_speex_deinit(void) { + if (!s_encoder.initialized) { + return; + } + + if (s_encoder.enc_state) { + speex_encoder_destroy(s_encoder.enc_state); + s_encoder.enc_state = NULL; + } + + speex_bits_destroy(&s_encoder.bits); + + + if (s_encoder.frame_buffer) { + kernel_free(s_encoder.frame_buffer); + s_encoder.frame_buffer = NULL; + } + + if (s_encoder.encoded_buffer) { + kernel_free(s_encoder.encoded_buffer); + s_encoder.encoded_buffer = NULL; + } + + memset(&s_encoder, 0, sizeof(s_encoder)); +} + +void voice_speex_get_transfer_info(AudioTransferInfoSpeex *info) { + PBL_ASSERTN(s_encoder.initialized); + PBL_ASSERTN(info); + + memset(info, 0, sizeof(AudioTransferInfoSpeex)); + strncpy(info->version, "1.2.1", sizeof(info->version) - 1); + info->sample_rate = s_encoder.sample_rate; + info->bit_rate = s_encoder.bit_rate; + info->frame_size = (uint16_t)s_encoder.frame_size; // Explicit cast to uint16_t + info->bitstream_version = s_encoder.bitstream_version; + + PBL_LOG_DBG("Transfer info: sample_rate=%"PRIu32", bit_rate=%"PRIu16", frame_size=%"PRIu16", bitstream_version=%"PRIu8, + info->sample_rate, info->bit_rate, info->frame_size, info->bitstream_version); + + // Additional validation + if (info->sample_rate != 16000) { + PBL_LOG_WRN("Unexpected sample rate in transfer info: %"PRIu32, info->sample_rate); + } +} + +int voice_speex_get_frame_size(void) { + // Return total samples per frame (frame_size * channels for stereo) + return s_encoder.initialized ? (int)(s_encoder.frame_size * s_encoder.channels) : 0; +} + +int16_t *voice_speex_get_frame_buffer(void) { + return s_encoder.initialized ? (int16_t *)s_encoder.frame_buffer : NULL; +} + +size_t voice_speex_get_frame_buffer_size(void) { + return s_encoder.initialized ? s_encoder.frame_buffer_size : 0; +} + +int voice_speex_encode_frame(int16_t *samples, uint8_t *encoded_data, size_t max_encoded_size) { + if (!s_encoder.initialized) { + PBL_LOG_ERR("encode_frame called but Speex not initialized"); + return -1; + } + + if (!samples || !encoded_data) { + PBL_LOG_ERR("encode_frame called with invalid buffers"); + return -1; + } + + uint32_t total_samples = s_encoder.frame_size * s_encoder.channels; + + // Apply gain boost to samples + for (uint32_t i = 0; i < total_samples; i++) { + int32_t boosted = (int32_t)samples[i] * SPEEX_AUDIO_GAIN; + // Clamp to int16_t range to prevent overflow + if (boosted > INT16_MAX) { + boosted = INT16_MAX; + } else if (boosted < INT16_MIN) { + boosted = INT16_MIN; + } + samples[i] = (int16_t)boosted; + } + + // Reset bits structure + speex_bits_reset(&s_encoder.bits); + + if (s_encoder.channels == 2) { + // For stereo: encode stereo info and convert to mono in-place + speex_encode_stereo_int(samples, s_encoder.frame_size, &s_encoder.bits); + } + + // Encode frame (for stereo, samples have been converted to mono in-place by speex_encode_stereo_int) + speex_encode_int(s_encoder.enc_state, (spx_int16_t *)samples, &s_encoder.bits); + + // Write encoded data to buffer + int encoded_bytes = speex_bits_write(&s_encoder.bits, (char *)encoded_data, max_encoded_size); + + if (encoded_bytes < 0) { + PBL_LOG_ERR("Failed to write Speex encoded data (returned %d)", encoded_bytes); + return -1; + } + + PBL_LOG_VERBOSE("Encoded frame: input_samples=%"PRIu32", output_bytes=%d, frame_size=%"PRIu32", channels=%"PRIu8, + total_samples, encoded_bytes, s_encoder.frame_size, s_encoder.channels); + + return encoded_bytes; +} + +bool voice_speex_is_initialized(void) { + return s_encoder.initialized; +} diff --git a/src/fw/services/voice/wscript_build b/src/fw/services/voice/wscript_build new file mode 100644 index 0000000000..500ebdbd36 --- /dev/null +++ b/src/fw/services/voice/wscript_build @@ -0,0 +1,16 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'transcription.c', + 'voice.c', + 'voice_speex.c', +] + +bld.stlib( + use=['fw_includes', 'speex_includes'], + source=sources, + target='services_voice', +) + +bld.env.SERVICES.append('services_voice') \ No newline at end of file diff --git a/src/fw/services/voice_endpoint/Kconfig b/src/fw/services/voice_endpoint/Kconfig new file mode 100644 index 0000000000..cd96210880 --- /dev/null +++ b/src/fw/services/voice_endpoint/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_VOICE_ENDPOINT + bool "Voice endpoint" + help + Voice endpoint (phone-side voice transport). + +if SERVICE_VOICE_ENDPOINT + +module = SERVICE_VOICE_ENDPOINT +module-str = Voice endpoint +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/voice_endpoint/service.c b/src/fw/services/voice_endpoint/service.c new file mode 100644 index 0000000000..d8dfd3c3b5 --- /dev/null +++ b/src/fw/services/voice_endpoint/service.c @@ -0,0 +1,226 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/voice_endpoint.h" + +#include "kernel/pbl_malloc.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/voice/voice.h" +#include "system/logging.h" +#include "system/passert.h" +#include "util/generic_attribute.h" +#include "util/uuid.h" + +#include + +#include "pbl/services/voice_endpoint_private.h" + +PBL_LOG_MODULE_DEFINE(service_voice_endpoint, CONFIG_SERVICE_VOICE_ENDPOINT_LOG_LEVEL); + +#define VOICE_CONTROL_ENDPOINT (11000) + +#ifdef CONFIG_MIC +static bool prv_handle_result_common(VoiceEndpointResult result, + bool app_initiated, + AudioEndpointSessionId session_id, + GenericAttributeList *attr_list, + size_t attr_list_size, + Uuid **app_uuid_out) { + + GenericAttribute *uuid_attr = generic_attribute_find_attribute(attr_list, + VEAttributeIdAppUuid, + attr_list_size); + if (app_initiated && !uuid_attr) { + PBL_LOG_WRN("No app UUID found for dictation response from app-initiated " + "session"); + voice_handle_dictation_result(VoiceEndpointResultFailInvalidMessage, session_id, NULL, + app_initiated, NULL); + return false; + } + + Uuid *app_uuid = uuid_attr ? (Uuid *)uuid_attr->data : NULL; + + if (result != VoiceEndpointResultSuccess) { + voice_handle_dictation_result(result, session_id, NULL, app_initiated, app_uuid); + return false; + } + + if (attr_list->num_attributes == 0) { + PBL_LOG_WRN("No attributes in message"); + voice_handle_dictation_result(VoiceEndpointResultFailInvalidMessage, session_id, NULL, + app_initiated, app_uuid); + return false; + } + + *app_uuid_out = app_uuid; + return true; +} + +static void prv_handle_dictation_result(VoiceSessionResultMsg *msg, size_t size) { + const size_t attr_list_size = size - sizeof(VoiceSessionResultMsg) + sizeof(GenericAttributeList); + const bool app_initiated = (msg->flags.app_initiated == 1); + Uuid *app_uuid = NULL; + + if (!prv_handle_result_common(msg->result, app_initiated, msg->session_id, + &msg->attr_list, attr_list_size, &app_uuid)) { + return; + } + + GenericAttribute *transcription_attr = generic_attribute_find_attribute(&msg->attr_list, + VEAttributeIdTranscription, attr_list_size); + + if (!transcription_attr || transcription_attr->length == 0) { + PBL_LOG_WRN("No transcription attribute found"); + voice_handle_dictation_result(VoiceEndpointResultFailInvalidMessage, msg->session_id, NULL, + app_initiated, app_uuid); + return; + } + + Transcription *transcription = (Transcription *)transcription_attr->data; + bool valid = transcription_validate(transcription, transcription_attr->length); + + if (!valid) { + PBL_LOG_WRN("Unrecognized transcription format received"); + voice_handle_dictation_result(VoiceEndpointResultFailInvalidRecognizerResponse, + msg->session_id, NULL, app_initiated, app_uuid); + } + voice_handle_dictation_result(msg->result, msg->session_id, transcription, + app_initiated, app_uuid); +} + +static void prv_handle_nlp_result(VoiceSessionResultMsg *msg, size_t size) { + const size_t attr_list_size = size - sizeof(VoiceSessionResultMsg) + sizeof(GenericAttributeList); + const bool app_initiated = (msg->flags.app_initiated == 1); + Uuid *app_uuid = NULL; + + if (!prv_handle_result_common(msg->result, app_initiated, msg->session_id, + &msg->attr_list, attr_list_size, &app_uuid)) { + return; + } + if (app_uuid) { + PBL_LOG_WRN("Got an app UUID in a NLP result msg. Ignoring and continuing"); + } + + + // The timestamp attribute is optional + time_t timestamp = 0; + GenericAttribute *timestamp_attr = generic_attribute_find_attribute(&msg->attr_list, + VEAttributeIdTimestamp, attr_list_size); + if (timestamp_attr && timestamp_attr->length == sizeof(uint32_t)) { + uint32_t *timestamp_ptr = (uint32_t*)timestamp_attr->data; + timestamp = *timestamp_ptr; + } + + GenericAttribute *reminder_attr = generic_attribute_find_attribute(&msg->attr_list, + VEAttributeIdReminder, attr_list_size); + + if (!reminder_attr || reminder_attr->length == 0) { + PBL_LOG_WRN("No reminder attribute found"); + voice_handle_nlp_result(VoiceEndpointResultFailInvalidMessage, msg->session_id, NULL, 0); + return; + } + char *reminder_str = kernel_zalloc_check(reminder_attr->length + 1); + memcpy(reminder_str, reminder_attr->data, reminder_attr->length); + reminder_str[reminder_attr->length] = '\0'; + + voice_handle_nlp_result(msg->result, msg->session_id, reminder_str, timestamp); + kernel_free(reminder_str); +} +#endif + +#ifdef CONFIG_MIC +void voice_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { + MsgId msg_id = data[0]; + switch (msg_id) { + case MsgIdSessionSetup: { + if (size >= sizeof(SessionSetupResultMsg)) { + SessionSetupResultMsg *msg = (SessionSetupResultMsg *) data; + + // Validate result enum value to prevent crashes from invalid values + VoiceEndpointResult result = msg->result; + if (result > VoiceEndpointResultFailInvalidMessage) { + PBL_LOG_ERR("Invalid VoiceEndpointResult value: %d, treating as invalid message", + (int)result); + result = VoiceEndpointResultFailInvalidMessage; + } + + bool app_initiated = (msg->flags.app_initiated == 1); + voice_handle_session_setup_result(result, msg->session_type, app_initiated); + } else { + PBL_LOG_WRN("Invalid size for session setup result message"); + } + break; + } + case MsgIdDictationResult: { + if (size >= sizeof(VoiceSessionResultMsg)) { + VoiceSessionResultMsg *msg = (VoiceSessionResultMsg *) data; + prv_handle_dictation_result(msg, size); + } else { + PBL_LOG_WRN("Invalid size for dictation result message %zu", size); + } + break; + } + case MsgIdNLPResult: { + if (size >= sizeof(VoiceSessionResultMsg)) { + VoiceSessionResultMsg *msg = (VoiceSessionResultMsg *) data; + prv_handle_nlp_result(msg, size); + } else { + PBL_LOG_WRN("Invalid size for dictation result message %zu", size); + } + break; + } + default: + // Ignore invalid message ID + PBL_LOG_WRN("Invalid message ID"); + break; + } + +} +#else +void voice_endpoint_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t size) { +} +#endif + +void voice_endpoint_setup_session(VoiceEndpointSessionType session_type, + AudioEndpointSessionId session_id, AudioTransferInfoSpeex *info, Uuid *app_uuid) { + + CommSession *comm_session = comm_session_get_system_session(); + comm_session_set_responsiveness(comm_session, BtConsumerPpVoiceEndpoint, ResponseTimeMin, + MIN_LATENCY_MODE_TIMEOUT_VOICE_SECS); + + // We're only sending one attribute now: the speex audio transfer info packet + size_t size = sizeof(SessionSetupMsg) + sizeof(GenericAttribute) + + sizeof(AudioTransferInfoSpeex) + + (app_uuid ? (sizeof(Uuid) + sizeof(GenericAttribute)) : 0); + SessionSetupMsg *msg = kernel_malloc_check(size); + *msg = (SessionSetupMsg) { + .msg_id = MsgIdSessionSetup, + .session_type = session_type, + .session_id = session_id, + .attr_list.num_attributes = 1, + }; + + GenericAttribute *attr = msg->attr_list.attributes; + if (app_uuid) { + // set this after struct initialization because the rest of the fields in the bitfield are left + // uninitialized if just one is set. + msg->flags.app_initiated = 1; + + // we're also sending the app UUID + msg->attr_list.num_attributes += 1; + + // add app UUID attribute + attr = generic_attribute_add_attribute(attr, VEAttributeIdAppUuid, app_uuid, sizeof(Uuid)); + } + + attr = generic_attribute_add_attribute(attr, VEAttributeIdAudioTransferInfoSpeex, info, + sizeof(AudioTransferInfoSpeex)); + + size_t actual_size = (uint8_t *)attr - (uint8_t *)msg; + PBL_ASSERTN(actual_size == size); + + comm_session_send_data(comm_session, VOICE_CONTROL_ENDPOINT, (uint8_t *)msg, + size, COMM_SESSION_DEFAULT_TIMEOUT); + kernel_free(msg); +} diff --git a/src/fw/services/voice_endpoint/wscript_build b/src/fw/services/voice_endpoint/wscript_build new file mode 100644 index 0000000000..75e810f4fa --- /dev/null +++ b/src/fw/services/voice_endpoint/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_voice_endpoint', +) +bld.env.SERVICES.append('services_voice_endpoint') diff --git a/src/fw/services/wakeup/Kconfig b/src/fw/services/wakeup/Kconfig new file mode 100644 index 0000000000..e03192b4c8 --- /dev/null +++ b/src/fw/services/wakeup/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_WAKEUP + bool "Wakeup" + help + App wakeup scheduling service. + +if SERVICE_WAKEUP + +module = SERVICE_WAKEUP +module-str = Wakeup +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/wakeup/service.c b/src/fw/services/wakeup/service.c new file mode 100644 index 0000000000..8ac7d4639b --- /dev/null +++ b/src/fw/services/wakeup/service.c @@ -0,0 +1,683 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/wakeup.h" + +#include "popups/wakeup_ui.h" + +#include "os/mutex.h" +#include "process_management/app_install_manager.h" +#include "process_management/app_manager.h" +#include "pbl/services/process_management/app_storage.h" +#include "pbl/services/clock.h" +#include "pbl/services/event_service.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/settings/settings_file.h" +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" +#include "system/logging.h" +#include "util/attributes.h" +#include "util/math.h" +#include "util/units.h" + +#include "kernel/pbl_malloc.h" + +PBL_LOG_MODULE_DEFINE(service_wakeup, CONFIG_SERVICE_WAKEUP_LOG_LEVEL); + +#define SETTINGS_FILE_NAME "wakeup" +// settings file => 29 bytes * 30 apps * 8 wakeup events = ~7000 bytes +// This should be more than enough space to store all the wakeup events we will ever want. +#define SETTINGS_FILE_SIZE KiBYTES(8) +// This represents the size of the buffer that is allocated to pass into the wakeup_ui +// to show that an app's wakeup event had triggered while the watch was off. To reduce +// complexity, I have hard coded this buffer to a max size instead of going the linked_list +// route. 16 apps should be more than enough. +#define NUM_APPS_ALERT_ON_BOOT 16 + +static PebbleMutex *s_mutex; + +//! Settings entries == WakeupId are stored by timestamp, +//! duplicate timestamps not allowed (can't have 2 wakeup events at same time) +//! repeating and repeat_hours_offset were included for future use +//! and use in repeat support for alarms +typedef struct PACKED { + Uuid uuid; //!< UUID of app that scheduled the wakeup event + int32_t reason; //!< App provided value to differentiate wakeup event + bool repeating; //!< Enable event repetition + uint16_t repeat_hours_offset; //!< repeat hour interval + bool notify_if_missed; //!< Notify user if wakeup event has been missed + time_t timestamp; //!< The time at which this entry will wake up at + bool utc; //!< If timezone has been set, the this is UTC time +} WakeupEntry; + +typedef struct PACKED { + WakeupId current_wakeup_id; + WakeupId next_wakeup_id; + time_t timestamp; +} WakeupState; + +struct prv_missed_events_s { + uint8_t missed_apps_count; + AppInstallId *missed_app_ids; +}; + +struct prv_check_app_and_wakeup_event_s { + time_t wakeup_timestamp; //!< Timestamp of the WakupEntry + int wakeup_count; //!< wakeup event count for app, negative for error (StatusCode) +}; + +// Local prototypes +static WakeupEntry prv_wakeup_settings_get_entry(WakeupId wakeup_id); +static void prv_wakeup_settings_delete_entry(WakeupId wakeup_id); +static StatusCode prv_wakeup_settings_add_entry(WakeupId wakeup_id, WakeupEntry entry); +static void prv_wakeup_timer_next_pending(void); + +static bool s_wakeup_enabled = false; +static TimerID s_current_timer_id = TIMER_INVALID_ID; // single timer reused for each event +// single structure containing the global wakeup state +static WakeupState s_wakeup_state = { -1, -1, 0 }; +static bool s_catchup_enabled = false; // enables catching up with missed events + +void wakeup_dispatcher_system_task(void *data){ + WakeupId wakeup_id = (WakeupId)data; + WakeupEntry entry = prv_wakeup_settings_get_entry(wakeup_id); + + // Delete event from settings + prv_wakeup_settings_delete_entry(wakeup_id); + + AppInstallId app_id = app_install_get_id_for_uuid(&entry.uuid); + + // If specified app isn't currently running, launch + if (!(app_manager_get_current_app_id() == app_id)) { + // Lookup app, and if installed, launch + if (app_id != INSTALL_ID_INVALID) { + PebbleLaunchAppEventExtended* data = + kernel_malloc_check(sizeof(PebbleLaunchAppEventExtended)); + *data = (PebbleLaunchAppEventExtended) { + .common.reason = APP_LAUNCH_WAKEUP, + .wakeup.wakeup_id = wakeup_id, + .wakeup.wakeup_reason = entry.reason, + }; + data->common.args = &data->wakeup; + + PebbleEvent event = { + .type = PEBBLE_APP_LAUNCH_EVENT, + .launch_app = { + .id = app_id, + .data = data + } + }; + + event_put(&event); + } + } else { + // If app running, send event + PebbleEvent event = { + .type = PEBBLE_WAKEUP_EVENT, + .wakeup = { + .wakeup_info = { + .wakeup_id = wakeup_id, + .wakeup_reason = entry.reason + } + } + }; + event_put(&event); + } + + prv_wakeup_timer_next_pending(); +} + +// This callback is the system dispatcher that wakes up the application by AppInstallId +// OR queues a wakeup callback for that application +// and is triggered by NewTimer +static void prv_wakeup_dispatcher(void *data) { + // Place actual work on system_task to unload it from new_timer task + system_task_add_callback(wakeup_dispatcher_system_task, data); +} + +static bool prv_find_next_wakeup_id_callback(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // Check if valid entry + if (info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { + return true; // continue iterating + } + + WakeupId wakeup_id; + info->get_key(file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); + + WakeupEntry entry; + info->get_val(file, (uint8_t*)&entry, sizeof(WakeupEntry)); + + // If the wakeup_id is valid, and the timestamp of the entry is closer than + // the timestamp of our global wakeup state, then set the next close wakeup event + if (wakeup_id > 0 && (s_wakeup_state.current_wakeup_id == -1 || + entry.timestamp < s_wakeup_state.timestamp)) { + s_wakeup_state.timestamp = entry.timestamp; + s_wakeup_state.current_wakeup_id = wakeup_id; + } + + return true; // continue iterating +} + + +// Checks for the next pending wakeup event and sets up a timer for the event +static void prv_wakeup_timer_next_pending(void) { + if (!s_wakeup_enabled) { + return; + } + + // If there is already a wakeup timer scheduled, cancel it. There will be a + // new timer scheduled for the soonest wakeup that is registered. + if (new_timer_scheduled(s_current_timer_id, NULL)) { + new_timer_stop(s_current_timer_id); + } + + mutex_lock(s_mutex); + { + // Find the next event to occur + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + // Reset wakeup state to use for the search + s_wakeup_state.current_wakeup_id = -1; + s_wakeup_state.timestamp = 0; + settings_file_each(&wakeup_settings, prv_find_next_wakeup_id_callback, NULL); + settings_file_close(&wakeup_settings); + } else { + PBL_LOG_ERR("Error: could not open APP_WAKEUP settings"); + } + } + mutex_unlock(s_mutex); + + // Create a timer for the found wakeup id, given it's valid + if (s_wakeup_state.current_wakeup_id >= 0) { + int32_t timestamp = s_wakeup_state.timestamp; + time_t current_time = rtc_get_time(); + int32_t time_difference = timestamp - current_time; + + // If time_difference is negative, this was a missed past event due to set_time + // changing current time beyond the event + // Note: this catches the edge case that there are several wakeup events skipped + // and avoids clobbering these events with a WAKEUP_CATCHUP_WINDOW gap, + // including an event occurring after missed events + if (time_difference < 0 || s_catchup_enabled) { + // next catchup_enabled state before modifying time_difference + s_catchup_enabled = (time_difference < 0) ? true : false; + // Enforces the catchup gap to be at least WAKEUP_CATCHUP_WINDOW gap + time_difference = MAX(time_difference, WAKEUP_CATCHUP_WINDOW); + } + + // timers are in milliseconds, set main callback dispatch for wakeup + // WakeupId is used to save/restore/lookup wakeup events + new_timer_start(s_current_timer_id, (time_difference * 1000), prv_wakeup_dispatcher, + (void*)((intptr_t)s_wakeup_state.current_wakeup_id), 0); + } +} + + +static void prv_migrate_events_callback(SettingsFile *old_file, SettingsFile *new_file, + SettingsRecordInfo *info, void *utc_diff) { + if (!utc_diff || info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { + return; + } + + WakeupId wakeup_id; + info->get_val(old_file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); + + WakeupEntry entry; + info->get_val(old_file, (uint8_t*)&entry, sizeof(WakeupEntry)); + + // Migrate the entries to the new timezone + if (entry.utc == false) { + entry.timestamp -= *((int*)utc_diff); + entry.utc = true; + if (wakeup_id == s_wakeup_state.current_wakeup_id) { + s_wakeup_state.timestamp = entry.timestamp; + } + } + + // Write the new entry to the settings file. We always write as there's no + // chance of it being invalid. + settings_file_set(new_file, (uint8_t*)&wakeup_id, sizeof(WakeupId), + (uint8_t*)&entry, sizeof(WakeupEntry)); +} + +static bool prv_check_for_events(SettingsFile *file, SettingsRecordInfo *info, void *context) { + bool *event_found = (bool *)context; + *event_found = true; + return false; // stop iterating +} + +static void prv_update_events_callback(SettingsFile *old_file, SettingsFile *new_file, + SettingsRecordInfo *info, void *context) { + // Check if valid entry + if (!context || info->key_len != sizeof(WakeupId)) { + return; + } + + bool struct_size_mismatch = info->val_len != sizeof(WakeupEntry); + bool struct_migration = !clock_is_timezone_set() && struct_size_mismatch; + if (struct_size_mismatch && !struct_migration) { + return; + } + + struct prv_missed_events_s *missed_events = (struct prv_missed_events_s*)context; + + WakeupId wakeup_id; + info->get_key(old_file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); + + WakeupEntry entry; + info->get_val(old_file, (uint8_t*)&entry, info->val_len); + if (struct_migration) { + entry.timestamp = wakeup_id; // WakeupId (key) is a timestamp + entry.utc = false; // If we're migrating, this has not been utc + } + + int32_t timestamp = entry.timestamp; + time_t current_time = rtc_get_time(); + int32_t time_difference = timestamp - current_time; + + if (timestamp >= s_wakeup_state.next_wakeup_id) { + s_wakeup_state.next_wakeup_id = timestamp + 1; + } else if (wakeup_id >= s_wakeup_state.next_wakeup_id) { + s_wakeup_state.next_wakeup_id = wakeup_id + 1; + } + + // schedule non-expired events + if (time_difference > 0) { + // Using settings_file_rewrite, need to write to keep key/value + settings_file_set(new_file, (uint8_t*)&wakeup_id, sizeof(WakeupId), + (uint8_t*)&entry, sizeof(WakeupEntry)); + } else { + if (entry.notify_if_missed) { + if (missed_events->missed_app_ids == NULL) { + // This is allocated here, but free'd in the wakup_ui.h module + missed_events->missed_app_ids = + kernel_malloc(NUM_APPS_ALERT_ON_BOOT * sizeof(AppInstallId)); + } + if (missed_events->missed_apps_count > NUM_APPS_ALERT_ON_BOOT) { + // We have more than NUM_APPS_ALERT_ON_BOOT apps that had events fire while the watch + // was shut off. Very rare this will happen, but just down show that the apps > 16 missed + // an event. + return; + } + // Set the AppInstallId of the app that had an alert fired + missed_events->missed_app_ids[missed_events->missed_apps_count++] = + app_install_get_id_for_uuid(&entry.uuid); + } + // Deletes the entry automatically if not written + } +} + +void wakeup_init(void) { + struct prv_missed_events_s missed_events = { 0, NULL }; + + s_mutex = mutex_create(); + + event_service_init(PEBBLE_WAKEUP_EVENT, NULL, NULL); + + // Create single reusable timer for wakeup events + s_current_timer_id = new_timer_create(); + s_wakeup_state.next_wakeup_id = rtc_get_time(); + s_wakeup_state.timestamp = -1; + + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) != S_SUCCESS) { + PBL_LOG_ERR("Error: could not open wakeup settings"); + return; + } + + // Want to check if there are any events first to prevent us from rewriting the file on boot if + // we don't need to. + bool event_found = false; + settings_file_each(&wakeup_settings, prv_check_for_events, &event_found); + if (event_found) { + PBL_LOG_DBG("Rewriting wakeup file"); + // Update settings file removing expired events + settings_file_rewrite(&wakeup_settings, prv_update_events_callback, &missed_events); + } else { + PBL_LOG_DBG("Not rewriting wakeup file because no entries were found"); + } + settings_file_close(&wakeup_settings); + + // If wakeup events were missed by apps requesting notify_if_missed + // popup a notification window displaying these apps + if (missed_events.missed_apps_count) { + wakeup_popup_window(missed_events.missed_apps_count, missed_events.missed_app_ids); + } +} + + +static bool prv_compiled_without_utc_support(void) { + static const Version first_utc_version = { + // See list of changes in pebble_process_info.h. Apps compiled prior to this version will + // get local time returned from the time() call. + 0x5, + 0x2f + }; + Version app_sdk_version = process_metadata_get_sdk_version( + sys_process_manager_get_current_process_md()); + + if (version_compare(app_sdk_version, first_utc_version) < 0) { + return true; + } + return false; +} + + +DEFINE_SYSCALL(WakeupId, sys_wakeup_schedule, time_t timestamp, int32_t reason, + bool notify_if_missed) { + + if (prv_compiled_without_utc_support()) { + // Legacy apps get local time returned from the time() call. + timestamp = time_local_to_utc(timestamp); + } + + time_t current_time = rtc_get_time(); + int32_t time_difference = timestamp - current_time; + WakeupId wakeup_id = s_wakeup_state.next_wakeup_id++; + + // Disallow scheduling past events + if (time_difference <= 0) { + return E_INVALID_ARGUMENT; + } + + Uuid uuid = app_manager_get_current_app_md()->uuid; + + WakeupEntry entry = { + .uuid = uuid, + .reason = reason, + .notify_if_missed = notify_if_missed, + .timestamp = timestamp, + .utc = clock_is_timezone_set() + }; + + // Add to settings file + StatusCode retval = prv_wakeup_settings_add_entry(wakeup_id, entry); + + // If there was an error adding the wakeup event, return the error + if (retval < S_SUCCESS) { + return retval; + } + + // If this new event is sooner than the currently scheduled timer, make this + // the current one + prv_wakeup_timer_next_pending(); + return wakeup_id; +} + + +static void prv_wakeup_settings_delete_entry(WakeupId wakeup_id) { + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + settings_file_delete(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId)); + settings_file_close(&wakeup_settings); + } + } + mutex_unlock(s_mutex); +} + +static WakeupEntry prv_wakeup_settings_get_entry(WakeupId wakeup_id) { + WakeupEntry entry = {{0}}; + + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + settings_file_get(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId), + (uint8_t*)&entry, sizeof(WakeupEntry)); + settings_file_close(&wakeup_settings); + } + } + mutex_unlock(s_mutex); + return entry; +} + +DEFINE_SYSCALL(void, sys_wakeup_delete, WakeupId wakeup_id) { + + WakeupEntry entry = prv_wakeup_settings_get_entry(wakeup_id); + + // Only allow owner to delete its own wakeup events + if (uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { + if (wakeup_id == s_wakeup_state.current_wakeup_id && + new_timer_scheduled(s_current_timer_id, NULL)) { + new_timer_stop(s_current_timer_id); + } + prv_wakeup_settings_delete_entry(wakeup_id); + prv_wakeup_timer_next_pending(); + } +} + +static bool prv_check_count_and_availability_callback(SettingsFile *file, + SettingsRecordInfo *info, void *context) { + // Check if valid entry + if (!context || info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { + return true; // continue iterating + } + + struct prv_check_app_and_wakeup_event_s *check = (struct prv_check_app_and_wakeup_event_s*)context; + + WakeupId wakeup_id; + info->get_key(file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); + + WakeupEntry entry; + info->get_val(file, (uint8_t*)&entry, sizeof(WakeupEntry)); + + //If we have already flagged an error, just skip the rest + if (check->wakeup_count < S_SUCCESS) { + return true; // continue iterating + } + + if (uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { + check->wakeup_count++; + } + // If the wakeup_id is with the same minute as another wakeup event + if ((entry.timestamp - WAKEUP_EVENT_WINDOW < check->wakeup_timestamp) && + (check->wakeup_timestamp < (entry.timestamp + WAKEUP_EVENT_WINDOW))) { + check->wakeup_count = E_RANGE; + } + + return true; // continue iterating +} + + +static StatusCode prv_wakeup_settings_add_entry(WakeupId wakeup_id, WakeupEntry entry) { + status_t status = S_SUCCESS; + + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + // Check if current app already has MAX_WAKEUP_EVENTS_PER_APP scheduled + // or if the minute event window is already occupied + struct prv_check_app_and_wakeup_event_s check = { + .wakeup_count = 0, + .wakeup_timestamp = entry.timestamp + }; + settings_file_each(&wakeup_settings, prv_check_count_and_availability_callback, &check); + + if (check.wakeup_count < S_SUCCESS) { + status = check.wakeup_count; + } else if (check.wakeup_count >= MAX_WAKEUP_EVENTS_PER_APP) { + status = E_OUT_OF_RESOURCES; + } else { + settings_file_set(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId), + (uint8_t*)&entry, sizeof(WakeupEntry)); + } + settings_file_close(&wakeup_settings); + } else { + status = E_INTERNAL; + } + } + mutex_unlock(s_mutex); + + return status; +} + +static void prv_delete_events_by_uuid_callback(SettingsFile *old_file, SettingsFile *new_file, + SettingsRecordInfo *info, void *context) { + // Check if valid entry + if (info->key_len != sizeof(WakeupId) || info->val_len != sizeof(WakeupEntry)) { + return; + } + + WakeupId wakeup_id; + info->get_key(old_file, (uint8_t*)&wakeup_id, sizeof(WakeupId)); + + WakeupEntry entry; + info->get_val(old_file, (uint8_t*)&entry, sizeof(WakeupEntry)); + + // If the UUID is equal, delete the entry + if (uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { + // if this is the current timer event, cancel it + if (wakeup_id == s_wakeup_state.current_wakeup_id && + new_timer_scheduled(s_current_timer_id, NULL)) { + new_timer_stop(s_current_timer_id); + } + // Deletes the entry automatically if not written + } else { // Keep the entry + // Using settings_file_rewrite, need to write to keep key/value + settings_file_set(new_file, (uint8_t*)&wakeup_id, sizeof(WakeupId), + (uint8_t*)&entry, sizeof(WakeupEntry)); + } +} + + +DEFINE_SYSCALL(void, sys_wakeup_cancel_all_for_app, void) { + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + // Update settings file removing all events with UUID = uuid + settings_file_rewrite(&wakeup_settings, prv_delete_events_by_uuid_callback, NULL); + settings_file_close(&wakeup_settings); + } + } + mutex_unlock(s_mutex); + + // We may have cancelled the timer, next_pending will check + prv_wakeup_timer_next_pending(); +} + +DEFINE_SYSCALL(time_t, sys_wakeup_query, WakeupId wakeup_id) { + status_t status = E_DOES_NOT_EXIST; + WakeupEntry entry = {}; + + if (wakeup_id < 0) { + return status; + } + + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + // Check if the wakeup id is valid by seeing if it is in the wakeup settings_file + status = settings_file_get(&wakeup_settings, (uint8_t*)&wakeup_id, sizeof(WakeupId), + (uint8_t*)&entry, sizeof(WakeupEntry)); + settings_file_close(&wakeup_settings); + } else { + status = E_INTERNAL; + } + } + mutex_unlock(s_mutex); + + if (status != S_SUCCESS) { + return status; + } + + // timer doesn't belong to this app + if (!uuid_equal(&app_manager_get_current_app_md()->uuid, &entry.uuid)) { + return E_DOES_NOT_EXIST; + } + + time_t return_time = entry.timestamp; + if (prv_compiled_without_utc_support()) { + // Legacy apps expect everything in local time. + return_time = time_utc_to_local(return_time); + } + return return_time; +} + +void wakeup_enable(bool enable) { + bool was_enabled = s_wakeup_enabled; + s_wakeup_enabled = enable; + if (enable && !was_enabled) { + prv_wakeup_timer_next_pending(); + } else if (!enable && s_current_timer_id && + new_timer_scheduled(s_current_timer_id, NULL)) { + new_timer_stop(s_current_timer_id); + } +} + +TimerID wakeup_get_current(void) { + return s_current_timer_id; +} + +WakeupId wakeup_get_next_scheduled(void) { + return s_wakeup_state.current_wakeup_id; +} + +void wakeup_migrate_timezone(int utc_diff) { + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + settings_file_rewrite(&wakeup_settings, prv_migrate_events_callback, (void*)&utc_diff); + settings_file_close(&wakeup_settings); + } else { + PBL_LOG_ERR("Error: could not open wakeup settings"); + } + } + mutex_unlock(s_mutex); +} + +static void prv_wakeup_rewrite_kernel_bg_cb(void *data) { + // Update each wakeup entry via prv_update_events_callback and record any missed events + struct prv_missed_events_s missed_events = { 0, NULL }; + + mutex_lock(s_mutex); + { + SettingsFile wakeup_settings; + if (settings_file_open(&wakeup_settings, SETTINGS_FILE_NAME, SETTINGS_FILE_SIZE) == S_SUCCESS) { + // Update each wakeup entry via prv_update_events_callback and record any missed events + settings_file_rewrite(&wakeup_settings, prv_update_events_callback, &missed_events); + settings_file_close(&wakeup_settings); + } else { + PBL_LOG_ERR("Error: could not open wakeup settings"); + } + } + mutex_unlock(s_mutex); + + // If any events were missed due to time change display the wakeup popup + if (missed_events.missed_apps_count) { + wakeup_popup_window(missed_events.missed_apps_count, missed_events.missed_app_ids); + } + + // Setup a timer for the next wakeup event; will return if wakeup is not enabled + prv_wakeup_timer_next_pending(); +} + +void wakeup_handle_significant_clock_change(void) { + // Handle significant time changes (>15s, timezone changes, DST changes). + // Performs a full rewrite of the wakeup file to: + // 1. Delete events that are now in the past + // 2. Show "missed event" popups for events with notify_if_missed=true + // 3. Reschedule the next wakeup timer + + if (pebble_task_get_current() == PebbleTask_KernelBackground) { + prv_wakeup_rewrite_kernel_bg_cb(NULL); + } else { + system_task_add_callback(prv_wakeup_rewrite_kernel_bg_cb, NULL); + } +} + +void wakeup_handle_clock_change(void) { + // Handle all time changes (including small RTC calibrations). + // Just reschedule the wakeup timer without deleting events or showing popups. + // Wakeup timers use tick-based scheduling, so even small time changes can cause drift. + // Events that become past-due will be caught up via the catchup logic in + // prv_wakeup_timer_next_pending() (schedules them with a small delay). + prv_wakeup_timer_next_pending(); +} diff --git a/src/fw/services/wakeup/wscript_build b/src/fw/services/wakeup/wscript_build new file mode 100644 index 0000000000..154a8cc74d --- /dev/null +++ b/src/fw/services/wakeup/wscript_build @@ -0,0 +1,9 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.stlib( + use=['fw_includes'], + source=['service.c'], + target='services_wakeup', +) +bld.env.SERVICES.append('services_wakeup') diff --git a/src/fw/services/weather/Kconfig b/src/fw/services/weather/Kconfig new file mode 100644 index 0000000000..c9b9552fff --- /dev/null +++ b/src/fw/services/weather/Kconfig @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +config SERVICE_WEATHER + bool "Weather" + help + Weather service. + +if SERVICE_WEATHER + +module = SERVICE_WEATHER +module-str = Weather +source "src/fw/Kconfig.template.log_level" + +endif diff --git a/src/fw/services/normal/weather/weather_service.c b/src/fw/services/weather/weather_service.c similarity index 87% rename from src/fw/services/normal/weather/weather_service.c rename to src/fw/services/weather/weather_service.c index ec3d49cd57..4191f9b26d 100644 --- a/src/fw/services/normal/weather/weather_service.c +++ b/src/fw/services/weather/weather_service.c @@ -1,34 +1,23 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "weather_service.h" -#include "weather_service_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_service_private.h" #include "applib/event_service_client.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" #include "os/mutex.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/normal/blob_db/watch_app_prefs_db.h" -#include "services/normal/blob_db/weather_db.h" -#include "services/normal/weather/weather_types.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" +#include "pbl/services/blob_db/weather_db.h" +#include "pbl/services/weather/weather_types.h" #include "system/logging.h" #include "system/passert.h" +PBL_LOG_MODULE_DEFINE(service_weather, CONFIG_SERVICE_WEATHER_LOG_LEVEL); + static int prv_weather_data_list_node_comparator(void *a, void *b) { return ((WeatherDataListNode *)b)->id - ((WeatherDataListNode *)a)->id; } @@ -62,14 +51,13 @@ static bool prv_fill_forecast_from_entry(WeatherDBEntry *entry, const uint16_t location_pstring_length = location_pstring->str_length; if (!is_valid_entry_update_time || (location_pstring_length == 0)) { - PBL_LOG(LOG_LEVEL_ERROR, - "Invalid entry. Valid UT: %u, location length: %"PRIu16, + PBL_LOG_ERR("Invalid entry. Valid UT: %u, location length: %"PRIu16, is_valid_entry_update_time, location_pstring_length); return false; } if (prv_entry_update_time_too_old_to_be_valid(entry->last_update_time_utc)) { - PBL_LOG(LOG_LEVEL_WARNING, "Weather entry too old to fill forecast"); + PBL_LOG_WRN("Weather entry too old to fill forecast"); return false; } @@ -119,7 +107,7 @@ static void prv_add_to_list_if_valid(WeatherDBKey *key, WeatherDBEntry *entry, if (!prv_get_location_index(key, prefs, &location_index)) { uuid_to_string(key, key_string_buffer); - PBL_LOG(LOG_LEVEL_WARNING, "Weather location %s has no known ordering! Skipping", + PBL_LOG_WRN("Weather location %s has no known ordering! Skipping", key_string_buffer); return; // location not found in ordering list, skip over } @@ -130,7 +118,7 @@ static void prv_add_to_list_if_valid(WeatherDBKey *key, WeatherDBEntry *entry, if (!prv_fill_forecast_from_entry(entry, forecast)) { uuid_to_string(key, key_string_buffer); - PBL_LOG(LOG_LEVEL_WARNING, "Could not create forecast from %s's entry", key_string_buffer); + PBL_LOG_WRN("Could not create forecast from %s's entry", key_string_buffer); task_free(node); return; } @@ -150,7 +138,7 @@ static void prv_add_to_list_if_valid(WeatherDBKey *key, WeatherDBEntry *entry, static bool prv_get_default_location_key(WeatherDBKey *key_out) { SerializedWeatherAppPrefs *prefs = watch_app_prefs_get_weather(); if (!prefs) { - PBL_LOG(LOG_LEVEL_ERROR, "No SerializedWeatherAppPrefs available!"); + PBL_LOG_ERR("No SerializedWeatherAppPrefs available!"); return false; } @@ -308,7 +296,7 @@ bool weather_service_supported_by_phone(void) { PebbleProtocolCapabilities capabilities; bt_persistent_storage_get_cached_system_capabilities(&capabilities); if (!capabilities.weather_app_support) { - PBL_LOG(LOG_LEVEL_WARNING, "No weather support on phone"); + PBL_LOG_WRN("No weather support on phone"); } return capabilities.weather_app_support; } diff --git a/src/fw/services/weather/weather_type_tuples.def b/src/fw/services/weather/weather_type_tuples.def new file mode 100644 index 0000000000..d5a11178ce --- /dev/null +++ b/src/fw/services/weather/weather_type_tuples.def @@ -0,0 +1,15 @@ +// Any modifications to entries or attributes to WeatherType must only be done here. + +// (id, numeric_id, bg_color, text_color, timeline_resource_id) +WEATHER_TYPE_TUPLE(PartlyCloudy, 0, GColorChromeYellowARGB8, GColorBlackARGB8, TIMELINE_RESOURCE_PARTLY_CLOUDY) +WEATHER_TYPE_TUPLE(CloudyDay, 1, GColorLightGrayARGB8, GColorBlackARGB8, TIMELINE_RESOURCE_CLOUDY_DAY) +WEATHER_TYPE_TUPLE(LightSnow, 2, GColorElectricBlueARGB8, GColorBlackARGB8, TIMELINE_RESOURCE_LIGHT_SNOW) +WEATHER_TYPE_TUPLE(LightRain, 3, GColorPictonBlueARGB8, GColorBlackARGB8, TIMELINE_RESOURCE_LIGHT_RAIN) +WEATHER_TYPE_TUPLE(HeavyRain, 4, GColorBlueMoonARGB8, GColorWhiteARGB8, TIMELINE_RESOURCE_HEAVY_RAIN) +WEATHER_TYPE_TUPLE(HeavySnow, 5, GColorTiffanyBlueARGB8, GColorBlackARGB8, TIMELINE_RESOURCE_HEAVY_SNOW) +WEATHER_TYPE_TUPLE(Generic, 6, GColorLightGrayARGB8, GColorBlackARGB8, TIMELINE_RESOURCE_TIMELINE_WEATHER) +WEATHER_TYPE_TUPLE(Sun, 7, GColorOrangeARGB8, GColorWhiteARGB8, TIMELINE_RESOURCE_TIMELINE_SUN) +WEATHER_TYPE_TUPLE(RainAndSnow, 8, GColorMidnightGreenARGB8, GColorWhiteARGB8, TIMELINE_RESOURCE_RAINING_AND_SNOWING) +WEATHER_TYPE_TUPLE(Unknown, 255, GColorLightGrayARGB8, GColorWhiteARGB8, TIMELINE_RESOURCE_TIMELINE_WEATHER) + +#undef WEATHER_TYPE_TUPLE diff --git a/src/fw/services/weather/weather_types.c b/src/fw/services/weather/weather_types.c new file mode 100644 index 0000000000..36b8629d43 --- /dev/null +++ b/src/fw/services/weather/weather_types.c @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "resource/timeline_resource_ids.auto.h" + +#include "applib/graphics/gtypes.h" +#include "applib/graphics/gcolor_definitions.h" +#include "pbl/services/weather/weather_types.h" +#include "util/size.h" + +// Do NOT add entries to the following arrays. See weather_type_tuples.def +static const char *s_weather_type_names[] = { +#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) #id, +#include "pbl/services/weather/weather_type_tuples.def" +}; + +#if PBL_COLOR +static uint8_t s_weather_type_bg_colors[] = { +#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) bg_color, +#include "pbl/services/weather/weather_type_tuples.def" +}; +#endif + +static uint8_t s_weather_type_text_colors[] = { +#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) text_color, +#include "pbl/services/weather/weather_type_tuples.def" +}; + +static TimelineResourceId s_weather_type_timeline_resource_ids[] = { +#define WEATHER_TYPE_TUPLE(id, numeric_id, bg_color, text_color, timeline_resource_id) \ + timeline_resource_id, +#include "pbl/services/weather/weather_type_tuples.def" +}; + +static const size_t s_num_weather_types = ARRAY_LENGTH(s_weather_type_names); + +static size_t prv_get_array_index_of_type(WeatherType type) { + return (type == WeatherType_Unknown) ? (s_num_weather_types - 1) : type; +} + +const char *weather_type_get_name(WeatherType weather_type) { + return s_weather_type_names[prv_get_array_index_of_type(weather_type)]; +}; + +GColor weather_type_get_bg_color(WeatherType weather_type) { +#if PBL_COLOR + const size_t index = prv_get_array_index_of_type(weather_type); +#endif + return PBL_IF_COLOR_ELSE((GColor) {.argb = s_weather_type_bg_colors[index]}, GColorClear); +}; + +GColor weather_type_get_text_color(WeatherType weather_type) { + return (GColor) {.argb = s_weather_type_text_colors[prv_get_array_index_of_type(weather_type)]}; +}; + +TimelineResourceId weather_type_get_timeline_resource_id(WeatherType weather_type) { + return s_weather_type_timeline_resource_ids[prv_get_array_index_of_type(weather_type)]; +}; diff --git a/src/fw/services/weather/wscript_build b/src/fw/services/weather/wscript_build new file mode 100644 index 0000000000..91b6c999cb --- /dev/null +++ b/src/fw/services/weather/wscript_build @@ -0,0 +1,15 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +sources = [ + 'weather_service.c', + 'weather_types.c', +] + +bld.stlib( + use=['fw_includes'], + source=sources, + target='services_weather', +) + +bld.env.SERVICES.append('services_weather') diff --git a/src/fw/services/wscript b/src/fw/services/wscript deleted file mode 100644 index 0581c68444..0000000000 --- a/src/fw/services/wscript +++ /dev/null @@ -1,116 +0,0 @@ -def configure(conf): - pass - -def build(bld): - # Generate Pebble Protocol endpoints table: - bld.recurse('common/comm_session') - - def _build_services(bld, sources): - bld.gettext(source=sources, target='services.pot') - use = [ - 'fw_includes', - 'freertos_includes', - 'bt_driver', - 'root_includes', - 'speex_includes', - ] - - if bld.is_asterix() or bld.is_obelix(): - use.append('nrf_fuel_gauge') - if bld.env.memfault: - use.append('memfault_includes') - bld.stlib(source=sources, - target='fw_services', - use=use) - - def _build_normal_recovery_services(bld): - def _get_excludes(bld): - excludes = [] - - # Platform based excludes - if bld.is_tintin(): - excludes.append('normal/activity/**') - excludes.append('normal/weather/**') - excludes.append('normal/voice/**') - excludes.append('normal/accessory/**') - excludes.append('prf/accessory/**') - excludes.append('common/compositor/default/**') - excludes.append('normal/vibes/vibe_client.c') - excludes.append('normal/vibes/vibe_score.c') - excludes.append('normal/vibes/vibe_score_info.c') - excludes.append('common/battery/nrf_fuel_gauge/**') - elif bld.is_asterix(): - excludes.append('common/legacy/*registry*.c') - excludes.append('common/compositor/default/compositor_modal_transitions.*') - excludes.append('common/battery/voltage/**') - elif bld.is_obelix(): - excludes.append('common/legacy/*registry*.c') - excludes.append('common/compositor/legacy/compositor_modal_slide_transitions.*') - excludes.append('common/battery/voltage/**') - else: - excludes.append('common/legacy/*registry*.c') - excludes.append('common/battery/nrf_fuel_gauge/**') - - if bld.is_silk(): - excludes.append('common/compositor/default/compositor_modal_transitions.*') - else: - excludes.append('common/compositor/legacy/compositor_modal_slide_transitions.*') - - # Capability based excludes - if bld.capability('HAS_SPRF_V3'): - excludes.append('common/shared_prf_storage/v2_sprf/**') - else: - excludes.append('common/shared_prf_storage/v3_sprf/**') - - if not bld.capability('HAS_BUILTIN_HRM'): - excludes.append('common/hrm/**') - excludes.append('normal/bluetooth/ble_hrm.c') - - if not bld.capability('HAS_MAGNETOMETER'): - excludes.append('common/ecompass.c') - - if not bld.capability('HAS_MICROPHONE'): - excludes.append('normal/voice/**') - - return excludes - - excludes = _get_excludes(bld) - - # get common services - sources = [] - sources.extend(bld.path.ant_glob('common/**/*.c', excl=excludes)) - - if bld.variant == 'prf': - # get recovery services - sources.extend(bld.path.ant_glob('prf/**/*.c', excl=excludes)) - else: - # get normal services - sources.extend(bld.path.ant_glob('normal/**/*.c', excl=excludes)) - - if bld.env.QEMU: - # get qemu services - sources.extend(bld.path.ant_glob('qemu/**/*.c', excl=excludes)) - - sources.extend(bld.path.ant_glob('*.c', excl=excludes)) - - # This could really be based on what accel driver is in use - if bld.is_tintin(): - accel_manager_to_remove = 'common/accel_manager.c' - else: - accel_manager_to_remove = 'legacy/accel_manager.c' - - sources = [x for x in sources if not x.abspath().endswith(accel_manager_to_remove)] - - _build_services(bld, sources) - - if bld.env.VOICE_CODEC_TESTS: - voice_sample_target = bld.path.get_bld().make_node('normal/voice/voice_test_sample.auto.h') - bld(features='binary_header', - source='normal/voice/sample.pcm', - target=voice_sample_target, - array_name='s_voice_test_sample') - - _build_normal_recovery_services(bld) - - -# vim:filetype=python diff --git a/src/fw/services/wscript_build b/src/fw/services/wscript_build new file mode 100644 index 0000000000..90ae86a62e --- /dev/null +++ b/src/fw/services/wscript_build @@ -0,0 +1,156 @@ +# SPDX-Copyright-Text: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.env.SERVICES = [] +bld.env.SERVICES_TEXT = [] + +if bld.env.CONFIG_SERVICE_ACCEL_MANAGER: + bld.recurse('accel_manager') +if bld.env.CONFIG_SERVICE_ACTIVITY: + bld.recurse('activity') +if bld.env.CONFIG_SERVICE_ALARMS: + bld.recurse('alarms') +if bld.env.CONFIG_SERVICE_ANALYTICS: + bld.recurse('analytics') +if bld.env.CONFIG_SERVICE_ANIMATION_SERVICE: + bld.recurse('animation_service') +if bld.env.CONFIG_SERVICE_APP_CACHE: + bld.recurse('app_cache') +if bld.env.CONFIG_SERVICE_APP_FETCH_ENDPOINT: + bld.recurse('app_fetch_endpoint') +if bld.env.CONFIG_SERVICE_APP_GLANCES: + bld.recurse('app_glances') +if bld.env.CONFIG_SERVICE_APP_INBOX_SERVICE: + bld.recurse('app_inbox_service') +if bld.env.CONFIG_SERVICE_APP_MESSAGE: + bld.recurse('app_message') +if bld.env.CONFIG_SERVICE_APP_ORDER_ENDPOINT: + bld.recurse('app_order_endpoint') +if bld.env.CONFIG_SERVICE_APP_OUTBOX_SERVICE: + bld.recurse('app_outbox_service') +if bld.env.CONFIG_SERVICE_AUDIO_ENDPOINT: + bld.recurse('audio_endpoint') +if bld.env.CONFIG_SERVICE_BATTERY: + bld.recurse('battery') +if bld.env.CONFIG_SERVICE_BLOB_DB: + bld.recurse('blob_db') +if bld.env.CONFIG_SERVICE_BLUETOOTH: + bld.recurse('bluetooth') +if bld.env.CONFIG_SERVICE_BOOT_SPLASH: + bld.recurse('boot_splash') +if bld.env.CONFIG_SERVICE_CLOCK: + bld.recurse('clock') +if bld.env.CONFIG_SERVICE_COMM_SESSION: + bld.recurse('comm_session') +if bld.env.CONFIG_SERVICE_COMPOSITOR: + bld.recurse('compositor') +if bld.env.CONFIG_SERVICE_CONTACTS: + bld.recurse('contacts') +if bld.env.CONFIG_SERVICE_CRON: + bld.recurse('cron') +if bld.env.CONFIG_SERVICE_DATA_LOGGING: + bld.recurse('data_logging') +if bld.env.CONFIG_SERVICE_DEBOUNCED_CONNECTION_SERVICE: + bld.recurse('debounced_connection_service') +if bld.env.CONFIG_SERVICE_ECOMPASS: + bld.recurse('ecompass') +if bld.env.CONFIG_SERVICE_EVENT_SERVICE: + bld.recurse('event_service') +if bld.env.CONFIG_SERVICE_EVENTED_TIMER: + bld.recurse('evented_timer') +if bld.env.CONFIG_SERVICE_FILESYSTEM: + bld.recurse('filesystem') +if bld.env.CONFIG_SERVICE_FIRMWARE_UPDATE: + bld.recurse('firmware_update') +if bld.env.CONFIG_SERVICE_GET_BYTES: + bld.recurse('get_bytes') +if bld.env.CONFIG_SERVICE_HEALTH_SYNC_ENDPOINT: + bld.recurse('health_sync_endpoint') +if bld.env.CONFIG_SERVICE_HRM: + bld.recurse('hrm') +if bld.env.CONFIG_SERVICE_I18N: + bld.recurse('i18n') +if bld.env.CONFIG_SERVICE_IDLE_WATCHDOG: + bld.recurse('idle_watchdog') +if bld.env.CONFIG_SERVICE_LEGACY: + bld.recurse('legacy') +if bld.env.CONFIG_SERVICE_LIGHT: + bld.recurse('light') +if bld.env.CONFIG_SERVICE_MUSIC: + bld.recurse('music') +if bld.env.CONFIG_SERVICE_NEW_TIMER: + bld.recurse('new_timer') +if bld.env.CONFIG_SERVICE_NOTIFICATIONS: + bld.recurse('notifications') +if bld.env.CONFIG_SERVICE_ORIENTATION_MANAGER: + bld.recurse('orientation_manager') +if bld.env.CONFIG_SERVICE_PERSIST: + bld.recurse('persist') +if bld.env.CONFIG_SERVICE_PHONE_CALL: + bld.recurse('phone_call') +if bld.env.CONFIG_SERVICE_PHONE_PP: + bld.recurse('phone_pp') +if bld.env.CONFIG_SERVICE_PING: + bld.recurse('ping') +if bld.env.CONFIG_SERVICE_POLL_REMOTE: + bld.recurse('poll_remote') +if bld.env.CONFIG_SERVICE_POWERMODE_SERVICE: + bld.recurse('powermode_service') +if bld.env.CONFIG_SERVICE_PRF_UPDATE: + bld.recurse('prf_update') +if bld.env.CONFIG_SERVICE_PROCESS_MANAGEMENT: + bld.recurse('process_management') +if bld.env.CONFIG_SERVICE_PROTOBUF_LOG: + bld.recurse('protobuf_log') +if bld.env.CONFIG_SERVICE_PUT_BYTES: + bld.recurse('put_bytes') +if bld.env.CONFIG_SERVICE_REGISTRY_ENDPOINT: + bld.recurse('registry_endpoint') +if bld.env.CONFIG_SERVICE_REGULAR_TIMER: + bld.recurse('regular_timer') +if bld.env.CONFIG_SERVICE_SEND_TEXT_SERVICE: + bld.recurse('send_text_service') +if bld.env.CONFIG_SERVICE_SERVICES_COMMON: + bld.recurse('services_common') +if bld.env.CONFIG_SERVICE_SERVICES_NORMAL: + bld.recurse('services_normal') +if bld.env.CONFIG_SERVICE_SETTINGS: + bld.recurse('settings') +if bld.env.CONFIG_SERVICE_SHARED_PRF_STORAGE: + bld.recurse('shared_prf_storage') +if bld.env.CONFIG_SERVICE_SPEAKER: + bld.recurse('speaker') +if bld.env.CONFIG_SERVICE_STATIONARY: + bld.recurse('stationary') +if bld.env.CONFIG_SERVICE_SYSTEM_TASK: + bld.recurse('system_task') +if bld.env.CONFIG_SERVICE_TICK_TIMER: + bld.recurse('tick_timer') +if bld.env.CONFIG_SERVICE_TIMELINE: + bld.recurse('timeline') +if bld.env.CONFIG_SERVICE_TIMEZONE_DATABASE: + bld.recurse('timezone_database') +if bld.env.CONFIG_SERVICE_TOUCH: + bld.recurse('touch') +if bld.env.CONFIG_SERVICE_VIBE_PATTERN: + bld.recurse('vibe_pattern') +if bld.env.CONFIG_SERVICE_VIBES: + bld.recurse('vibes') +if bld.env.CONFIG_SERVICE_VOICE: + bld.recurse('voice') +if bld.env.CONFIG_SERVICE_VOICE_ENDPOINT: + bld.recurse('voice_endpoint') +if bld.env.CONFIG_SERVICE_WAKEUP: + bld.recurse('wakeup') +if bld.env.CONFIG_SERVICE_WEATHER: + bld.recurse('weather') + +use = ['fw_includes'] +use.extend(bld.env.SERVICES) + +bld.stlib(source=['services.c'], target='fw_services', use=use) + +if bld.env.SERVICES_TEXT: + bld.msgcat(source=bld.env.SERVICES_TEXT, target='services.pot') + +# vim:filetype=python diff --git a/src/fw/sf32lb52_flash_fw.ld.template b/src/fw/sf32lb52_flash_fw.ld.template index 61908eead5..a9488753fc 100644 --- a/src/fw/sf32lb52_flash_fw.ld.template +++ b/src/fw/sf32lb52_flash_fw.ld.template @@ -22,9 +22,9 @@ SECTIONS KEEP(*(.isr_vector)) /* Startup code */ } >FLASH - .retm_ro : { + .ramfunc : { . = ALIGN(4); - __retm_ro_start = .; + __ramfunc_start = .; * (.*l1_ret_text_*) * (.*l1_ret_rodata_*) @@ -51,16 +51,11 @@ SECTIONS *bf0_pin_const.*.o (.text* .rodata*) *.o (.text.rt_memset) *rt_memclr*.*.o (.text*) - *memset*.*.o (.text*) - *memcpy*.*.o (.text*) - - *lptim_systick.*.o (.text* .rodata*) - *stop.*.o (.text* .rodata*) - *freertos_application.*.o (.text* .rodata*) + *freertos.*.o (.text* .rodata*) . = ALIGN(4); - __retm_ro_end = .; + __ramfunc_end = .; } >KERNEL_RAM AT>FLASH - __retm_ro_load_start = LOADADDR(.retm_ro); + __ramfunc_load_start = LOADADDR(.ramfunc); } diff --git a/src/fw/shell/normal/app_idle_timeout.c b/src/fw/shell/normal/app_idle_timeout.c index 439c41dad4..827a0edcd7 100644 --- a/src/fw/shell/normal/app_idle_timeout.c +++ b/src/fw/shell/normal/app_idle_timeout.c @@ -1,43 +1,30 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_idle_timeout.h" #include "kernel/event_loop.h" #include "os/tick.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "shell/normal/watchface.h" #include "shell/shell.h" #include "system/logging.h" #include "system/passert.h" -static const int WATCHFACE_TIMEOUT_MS = 30000; - TimerID s_timer; bool s_app_paused = false; bool s_app_started = false; -#ifndef NO_WATCH_TIMEOUT +#ifndef CONFIG_NO_WATCH_TIMEOUT +static const int WATCHFACE_TIMEOUT_MS = 30000; + static void prv_kernel_callback_watchface_launch(void* data) { watchface_launch_default(shell_get_watchface_compositor_animation(true /* watchface_is_dest */)); } static void prv_timeout_expired(void *cb_data) { - PBL_LOG(LOG_LEVEL_DEBUG, "App idle timeout hit! launching watchface"); + PBL_LOG_DBG("App idle timeout hit! launching watchface"); launcher_task_add_callback(prv_kernel_callback_watchface_launch, NULL); } @@ -58,7 +45,7 @@ void app_idle_timeout_start(void) { PBL_ASSERTN(s_timer == TIMER_INVALID_ID); s_app_started = true; -#ifndef NO_WATCH_TIMEOUT +#ifndef CONFIG_NO_WATCH_TIMEOUT prv_start_timer(true /* create a timer */); #endif } @@ -80,13 +67,13 @@ void app_idle_timeout_pause(void) { void app_idle_timeout_resume(void) { s_app_paused = false; -#ifndef NO_WATCH_TIMEOUT +#ifndef CONFIG_NO_WATCH_TIMEOUT prv_start_timer(false /* do not create a timer */); #endif } void app_idle_timeout_refresh(void) { -#ifndef NO_WATCH_TIMEOUT +#ifndef CONFIG_NO_WATCH_TIMEOUT prv_start_timer(false /* do not create a timer */); #endif } diff --git a/src/fw/shell/normal/app_idle_timeout.h b/src/fw/shell/normal/app_idle_timeout.h index f17ed725dd..03efef8872 100644 --- a/src/fw/shell/normal/app_idle_timeout.h +++ b/src/fw/shell/normal/app_idle_timeout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/shell/normal/battery_ui.c b/src/fw/shell/normal/battery_ui.c index b71c6ad2dc..04200603e4 100644 --- a/src/fw/shell/normal/battery_ui.c +++ b/src/fw/shell/normal/battery_ui.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "battery_ui.h" @@ -30,10 +17,10 @@ #include "kernel/ui/modals/modal_manager.h" #include "process_management/app_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/battery/battery_curve.h" -#include "services/common/clock.h" -#include "services/common/i18n/i18n.h" -#include "services/common/light.h" +#include "pbl/services/battery/battery_curve.h" +#include "pbl/services/clock.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/light.h" #include "system/logging.h" #include "util/time/time.h" diff --git a/src/fw/shell/normal/battery_ui.h b/src/fw/shell/normal/battery_ui.h index 9805d56678..a2fd282a3b 100644 --- a/src/fw/shell/normal/battery_ui.h +++ b/src/fw/shell/normal/battery_ui.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" -#include "services/common/battery/battery_monitor.h" +#include "pbl/services/battery/battery_monitor.h" typedef enum BatteryUIWarningLevel { BatteryUIWarningLevel_None = -1, diff --git a/src/fw/shell/normal/battery_ui_fsm.c b/src/fw/shell/normal/battery_ui_fsm.c index c8f822fb36..38a526ee02 100644 --- a/src/fw/shell/normal/battery_ui_fsm.c +++ b/src/fw/shell/normal/battery_ui_fsm.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "battery_ui.h" @@ -20,16 +7,15 @@ #include "applib/ui/vibes.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/battery_critical_app.h" +#include "apps/system/battery_critical.h" #include "kernel/low_power.h" #include "kernel/ui/modals/modal_manager.h" #include "kernel/util/standby.h" #include "process_management/app_manager.h" -#include "services/common/battery/battery_curve.h" -#include "services/common/status_led.h" -#include "services/common/vibe_pattern.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/vibes/vibe_intensity.h" +#include "pbl/services/battery/battery_curve.h" +#include "pbl/services/vibe_pattern.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/vibes/vibe_intensity.h" #include "shell/normal/watchface.h" #include "util/ratio.h" #include "util/size.h" @@ -71,7 +57,7 @@ static void prv_dismiss_plugged(void); static void prv_display_fully_charged(void *data); static void prv_dismiss_fully_charged(void); // TODO PBL-39883: Replace w/ QUIRK_RESET_ON_SHUTDOWN_WHILE_CHARGING once arbitrary prefixes land -#if PLATFORM_TINTIN || PLATFORM_SILK || PLATFORM_ASTERIX +#ifdef CONFIG_BOARD_FAMILY_ASTERIX static void prv_shutdown(void *ignored); #else static void prv_enter_shutdown_charging(void *ignored); @@ -99,7 +85,7 @@ static const BatteryUIState ui_states[] = { BatteryGood, BatteryWarning, BatteryLowPower, BatteryCritical, BatteryShutdownCharging }}, // TODO PBL-39883: Replace w/ QUIRK_RESET_ON_SHUTDOWN_WHILE_CHARGING once arbitrary prefixes land -#if PLATFORM_TINTIN || PLATFORM_SILK || PLATFORM_ASTERIX +#ifdef CONFIG_BOARD_FAMILY_ASTERIX [BatteryShutdownCharging] = { .enter = prv_shutdown } #else [BatteryShutdownCharging] = { .enter = prv_enter_shutdown_charging } @@ -109,13 +95,15 @@ static const BatteryUIState ui_states[] = { static BatteryUIStateID s_state = BatteryGood; static BatteryUIWarningLevel s_warning_points_index = -1; -#if PLATFORM_SPALDING -/* first warning for S4 is at 12 hours remaining, second at 6 hours remaining */ -static const uint8_t s_warning_points[] = { 12, 6 }; -#else /* first warning is at 18 hours remaining, second at 12 hours remaining */ static const uint8_t s_warning_points[] = { 18, 12 }; -#endif + +// Minimum hours of headroom above the next warning threshold required to show +// the current warning. If battery crosses a warning point already close to the +// next one (e.g. a fast drop or a level-estimate correction), skip the earlier +// warning so the user does not see contradictory daypart messaging like +// "Powered 'til tomorrow" followed shortly by "Powered 'til tonight". +#define BATTERY_WARNING_MIN_HOURS_HEADROOM 3 // State functions @@ -130,6 +118,14 @@ static void prv_display_warning(void *data) { new_warning = true; } + if (new_warning && s_warning_points_index < num_points) { + const uint32_t hours_remaining = battery_curve_get_hours_remaining(percent); + const uint32_t next_point_hours = s_warning_points[s_warning_points_index + 1]; + if (hours_remaining < next_point_hours + BATTERY_WARNING_MIN_HOURS_HEADROOM) { + new_warning = false; + } + } + if (new_warning) { if (!do_not_disturb_is_active()) { vibes_short_pulse(); @@ -144,7 +140,7 @@ static void prv_dismiss_warning(void) { } static void prv_enter_low_power(void *ignored) { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW watchface_start_low_power(); modal_manager_pop_all_below_priority(ModalPriorityAlarm); modal_manager_set_min_priority(ModalPriorityAlarm); @@ -158,7 +154,7 @@ static void prv_enter_low_power(void *ignored) { } static void prv_exit_low_power(void) { -#ifndef RECOVERY_FW +#ifndef CONFIG_RECOVERY_FW modal_manager_set_min_priority(ModalPriorityMin); watchface_launch_default(NULL); vibe_intensity_set(vibe_intensity_get()); @@ -189,29 +185,22 @@ static void prv_display_plugged(void *data) { vibes_short_pulse(); } battery_ui_display_plugged(); - - status_led_set(StatusLedState_Charging); } static void prv_dismiss_plugged(void) { battery_ui_dismiss_modal(); - - status_led_set(StatusLedState_Off); } static void prv_display_fully_charged(void *data) { battery_ui_display_fully_charged(); - status_led_set(StatusLedState_FullyCharged); } static void prv_dismiss_fully_charged(void) { battery_ui_dismiss_modal(); - - status_led_set(StatusLedState_Off); } // TODO PBL-39883: Replace w/ QUIRK_RESET_ON_SHUTDOWN_WHILE_CHARGING once arbitrary prefixes land -#if PLATFORM_TINTIN || PLATFORM_SILK || PLATFORM_ASTERIX +#ifdef CONFIG_BOARD_FAMILY_ASTERIX static void prv_shutdown(void *ignored) { battery_ui_handle_shut_down(); } diff --git a/src/fw/shell/normal/display_calibration_prompt.c b/src/fw/shell/normal/display_calibration_prompt.c deleted file mode 100644 index 19b007ecd9..0000000000 --- a/src/fw/shell/normal/display_calibration_prompt.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if PLATFORM_SPALDING - -#include "display_calibration_prompt.h" - -#include "applib/ui/dialogs/confirmation_dialog.h" -#include "apps/system_apps/settings/settings_display_calibration.h" -#include "kernel/event_loop.h" -#include "kernel/ui/modals/modal_manager.h" -#include "mfg/mfg_info.h" -#include "mfg/mfg_serials.h" -#include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" -#include "services/common/new_timer/new_timer.h" -#include "shell/prefs.h" -#include "util/size.h" - -// The calibration screen will be changing the screen offsets, so it's best that it remains on top -// of most other modals (generic, alerts, etc) to prevent confusion about the screen's alignment. -static const ModalPriority MODAL_PRIORITY = ModalPriorityCritical; - -static void prv_calibrate_confirm_pop(ClickRecognizerRef recognizer, void *context) { - i18n_free_all(context); - confirmation_dialog_pop((ConfirmationDialog *)context); -} - -static void prv_calibrate_confirm_cb(ClickRecognizerRef recognizer, void *context) { - settings_display_calibration_push(modal_manager_get_window_stack(MODAL_PRIORITY)); - prv_calibrate_confirm_pop(recognizer, context); -} - -static void prv_calibrate_click_config(void *context) { - window_single_click_subscribe(BUTTON_ID_UP, prv_calibrate_confirm_cb); - window_single_click_subscribe(BUTTON_ID_DOWN, prv_calibrate_confirm_pop); - window_single_click_subscribe(BUTTON_ID_BACK, prv_calibrate_confirm_pop); -} - -static TimerID s_timer = TIMER_INVALID_ID; - -static void prv_push_calibration_dialog(void *data) { - shell_prefs_set_should_prompt_display_calibration(false); - - ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Calibrate Prompt"); - Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog); - - dialog_set_text(dialog, i18n_get("Your screen may need calibration. Calibrate it now?", - confirmation_dialog)); - dialog_set_background_color(dialog, GColorMediumAquamarine); - dialog_set_icon(dialog, RESOURCE_ID_GENERIC_PIN_TINY); - confirmation_dialog_set_click_config_provider(confirmation_dialog, - prv_calibrate_click_config); - confirmation_dialog_push(confirmation_dialog, - modal_manager_get_window_stack(MODAL_PRIORITY)); -} - -static bool prv_display_has_user_offset(void) { - GPoint display_offset = shell_prefs_get_display_offset(); - GPoint mfg_display_offset = mfg_info_get_disp_offsets(); - return (!gpoint_equal(&display_offset, &mfg_display_offset)); -} - -static void prv_timer_callback(void *data) { - new_timer_delete(s_timer); - s_timer = TIMER_INVALID_ID; - - // last check: make sure we need to display the prompt in case something changed in the - // time that the timer was waiting. - if (!shell_prefs_should_prompt_display_calibration()) { - return; - } - - launcher_task_add_callback(prv_push_calibration_dialog, NULL); -} - -T_STATIC bool prv_is_known_misaligned_serial_number(const char *serial) { - // Filter watches known to be misaligned based on the serial number. This is possible because - // Serial numbers are represented as strings as described in: - // https://pebbletechnology.atlassian.net/wiki/display/DEV/Hardware+Serial+Numbering - // All watches of the same model produced from the same manufacturer on the same date, on the - // same manufacturing line, will share the same first 8 characters of the serial number. In this - // way, batches which are misaligned can be identified by a string comparison on these characters. - // - // NOTE: This also conveniently excludes test automation boards, so the dialog should not - // appear during integration tests. - const char *ranges[] = { "Q402445E" }; - for (size_t i = 0; i < ARRAY_LENGTH(ranges); i++) { - if (strncmp(serial, ranges[i], strlen(ranges[i])) == 0) { - return true; - } - } - return false; -} - -static bool prv_is_potentially_misaligned_watch() { - return !prv_display_has_user_offset() && - prv_is_known_misaligned_serial_number(mfg_get_serial_number()); -} - -void display_calibration_prompt_show_if_needed(void) { - if (!prv_is_potentially_misaligned_watch()) { - shell_prefs_set_should_prompt_display_calibration(false); - return; - } - - if (shell_prefs_should_prompt_display_calibration()) { - s_timer = new_timer_create(); - const uint32_t prompt_delay_time_ms = MS_PER_SECOND * SECONDS_PER_MINUTE; - new_timer_start(s_timer, prompt_delay_time_ms, prv_timer_callback, NULL, 0 /* flags */); - } -} - -#endif diff --git a/src/fw/shell/normal/display_calibration_prompt.h b/src/fw/shell/normal/display_calibration_prompt.h deleted file mode 100644 index c81ee0055b..0000000000 --- a/src/fw/shell/normal/display_calibration_prompt.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void display_calibration_prompt_show_if_needed(void); diff --git a/src/fw/shell/normal/language_ui.c b/src/fw/shell/normal/language_ui.c index 6dcda320b1..6d144c9e6a 100644 --- a/src/fw/shell/normal/language_ui.c +++ b/src/fw/shell/normal/language_ui.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "language_ui.h" @@ -22,7 +9,7 @@ #include "kernel/event_loop.h" #include "kernel/ui/modals/modal_manager.h" #include "resource/resource_ids.auto.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "shell/normal/watchface.h" static void prv_push_language_changed_dialog(void *data) { diff --git a/src/fw/shell/normal/language_ui.h b/src/fw/shell/normal/language_ui.h index 123d046d9e..8189ba17fd 100644 --- a/src/fw/shell/normal/language_ui.h +++ b/src/fw/shell/normal/language_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/shell/normal/prefs.c b/src/fw/shell/normal/prefs.c index deaad7b73c..7e6148d7d0 100644 --- a/src/fw/shell/normal/prefs.c +++ b/src/fw/shell/normal/prefs.c @@ -1,47 +1,48 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "quick_launch.h" #include "shell/normal/quick_launch.h" #include "shell/normal/watchface.h" +#include "shell/normal/prefs_sync.h" #include "shell/prefs.h" #include "shell/prefs_private.h" #include "shell/system_theme.h" -#include "apps/system_apps/toggle/quiet_time.h" +#include "apps/system/timeline/timeline.h" +#include "apps/system/toggle/quiet_time.h" #include "board/board.h" +#include "applib/graphics/gtypes.h" +#include "drivers/ambient_light.h" #include "drivers/backlight.h" #include "mfg/mfg_info.h" #include "os/mutex.h" #include "popups/timeline/peek.h" #include "process_management/app_install_manager.h" #include "process_management/process_manager.h" -#include "services/common/hrm/hrm_manager.h" -#include "services/common/i18n/i18n.h" -#include "services/normal/bluetooth/ble_hrm.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/timeline/peek.h" +#include "pbl/services/accel_manager.h" +#include "pbl/services/touch/touch.h" +#include "pbl/services/powermode_service.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/i18n/i18n.h" +#include "resource/resource_ids.auto.h" +#ifdef CONFIG_ORIENTATION_MANAGER +#include "pbl/services/orientation_manager.h" +#endif +#include "pbl/services/bluetooth/ble_hrm.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/timeline/peek.h" +#include "kernel/events.h" +#include "kernel/event_loop.h" #include "system/logging.h" #include "system/passert.h" #include "util/size.h" #include "util/uuid.h" -#if CAPABILITY_HAS_HEALTH_TRACKING -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_insights.h" -#endif +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_insights.h" + +#include "pbl/services/analytics/analytics.h" #include @@ -53,6 +54,9 @@ static bool s_clock_24h = false; #define PREF_KEY_CLOCK_TIMEZONE_SOURCE_IS_MANUAL "timezoneSource" static bool s_clock_timezone_source_is_manual = false; +#define PREF_KEY_CLOCK_TIME_SOURCE_IS_MANUAL "timeSource" +static bool s_clock_time_source_is_manual = false; + #define PREF_KEY_CLOCK_PHONE_TIMEZONE_ID "automaticTimezoneID" static int16_t s_clock_phone_timezone_id = -1; @@ -68,17 +72,43 @@ static bool s_backlight_ambient_sensor_enabled = true; #define PREF_KEY_BACKLIGHT_TIMEOUT_MS "lightTimeoutMs" static uint32_t s_backlight_timeout_ms = DEFAULT_BACKLIGHT_TIMEOUT_MS; #define PREF_KEY_BACKLIGHT_INTENSITY "lightIntensity" -static uint16_t s_backlight_intensity; // default pulled from BOARD_CONFIGs in shell_prefs_init() +static uint8_t s_backlight_intensity; // default pulled from BOARD_CONFIGs in shell_prefs_init() + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +#define PREF_KEY_BACKLIGHT_COLOR "lightColor" +static uint32_t s_backlight_color; // default pulled from BOARD_CONFIG in shell_prefs_init() +#endif #define PREF_KEY_BACKLIGHT_MOTION "lightMotion" static bool s_backlight_motion_enabled = true; +#define PREF_KEY_BACKLIGHT_TOUCH "lightTouch" +static uint8_t s_backlight_touch_wake = BacklightTouchWake_DoubleTap; + +#define PREF_KEY_TOUCH_ENABLED "touchEnabled" +static bool s_touch_enabled = true; + +#define PREF_KEY_MOTION_SENSITIVITY "motionSensitivity" +static uint8_t s_motion_sensitivity = 55; // Default to Medium + +#ifdef CONFIG_DYNAMIC_BACKLIGHT +#define PREF_KEY_BACKLIGHT_DYNAMIC_INTENSITY "lightDynamicIntensity" +static bool s_backlight_dynamic_intensity_enabled = true; + +#define PREF_KEY_DYNAMIC_BACKLIGHT_MIN_THRESHOLD "dynBacklightMinThreshold" +static uint32_t s_dynamic_backlight_min_threshold = 0; // default set from board config in shell_prefs_init() +#endif + +#ifdef CONFIG_ORIENTATION_MANAGER +#define PREF_KEY_DISPLAY_ORIENTATION_LEFT_HANDED "displayOrientationLeftHanded" +static bool s_display_orientation_left = false; +#endif + +#define PREF_KEY_BACKLIGHT_AMBIENT_THRESHOLD "lightAmbientThreshold" +static uint32_t s_backlight_ambient_threshold = 0; // default set from board config in shell_prefs_init() + #define PREF_KEY_STATIONARY "stationaryMode" -#if RELEASE && !PLATFORM_SPALDING -static bool s_stationary_mode_enabled = false; -#else static bool s_stationary_mode_enabled = true; -#endif #define PREF_KEY_DEFAULT_WORKER "workerId" static Uuid s_default_worker = UUID_INVALID_INIT; @@ -94,6 +124,9 @@ _Static_assert(sizeof(PreferredContentSize) == sizeof(s_text_style), #define PREF_KEY_LANG_ENGLISH "langEnglish" static bool s_language_english = false; +#define PREF_KEY_LANGUAGE "language" +static uint8_t s_language = ShellLanguageInstalledPack; + typedef struct QuickLaunchPreference { bool enabled; Uuid uuid; @@ -105,17 +138,17 @@ typedef struct QuickLaunchPreference { #define PREF_KEY_QUICK_LAUNCH_BACK "qlBack" static QuickLaunchPreference s_quick_launch_up = { - .enabled = true, + .enabled = false, .uuid = UUID_INVALID_INIT, }; static QuickLaunchPreference s_quick_launch_down = { - .enabled = true, + .enabled = false, .uuid = UUID_INVALID_INIT, }; static QuickLaunchPreference s_quick_launch_select = { - .enabled = true, + .enabled = false, .uuid = UUID_INVALID_INIT, }; @@ -124,6 +157,31 @@ static QuickLaunchPreference s_quick_launch_back = { .uuid = QUIET_TIME_TOGGLE_UUID, }; +#define PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_UP "qlSingleClickUp" +#define PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_DOWN "qlSingleClickDown" +#define PREF_KEY_QUICK_LAUNCH_COMBO_BACK_UP "qlComboBackUp" +#define PREF_KEY_QUICK_LAUNCH_COMBO_UP_DOWN "qlComboUpDown" + +static QuickLaunchPreference s_quick_launch_single_click_up = { + .enabled = true, + .uuid = UUID_HEALTH_DATA_SOURCE, +}; + +static QuickLaunchPreference s_quick_launch_single_click_down = { + .enabled = true, + .uuid = TIMELINE_UUID_INIT, +}; + +static QuickLaunchPreference s_quick_launch_combo_back_up = { + .enabled = false, + .uuid = UUID_INVALID_INIT, +}; + +static QuickLaunchPreference s_quick_launch_combo_up_down = { + .enabled = false, + .uuid = UUID_INVALID_INIT, +}; + #define PREF_KEY_QUICK_LAUNCH_SETUP_OPENED "qlSetupOpened" static uint8_t s_quick_launch_setup_opened = 0; @@ -133,7 +191,6 @@ static Uuid s_default_watchface = UUID_INVALID_INIT; #define PREF_KEY_WELCOME_VERSION "welcomeVersion" static uint8_t s_welcome_version = 0; -#if CAPABILITY_HAS_HEALTH_TRACKING #define PREF_KEY_ACTIVITY_PREFERENCES "activityPreferences" static ActivitySettings s_activity_preferences = ACTIVITY_DEFAULT_PREFERENCES; @@ -157,16 +214,7 @@ static ActivityHRMSettings s_activity_hrm_preferences = ACTIVITY_HRM_DEFAULT_PRE #define PREF_KEY_ACTIVITY_HEART_RATE_PREFERENCES "heartRatePreferences" static HeartRatePreferences s_activity_hr_preferences = ACTIVITY_HEART_RATE_DEFAULT_PREFERENCES; -#endif // CAPABILITY_HAS_HEALTH_TRACKING - -#if PLATFORM_SPALDING -#define PREF_KEY_DISPLAY_USER_OFFSET "displayUserOffset" -static GPoint s_display_user_offset = {0, 0}; -#define PREF_KEY_SHOULD_PROMPT_DISPLAY_CALIBRATION "promptDisplayCal" -static bool s_should_prompt_display_calibration = true; -#endif -#if CAPABILITY_HAS_TIMELINE_PEEK #define PREF_KEY_TIMELINE_SETTINGS_OPENED "timelineSettingsOpened" static uint8_t s_timeline_settings_opened = 0; @@ -176,10 +224,44 @@ static bool s_timeline_peek_enabled = true; #define PREF_KEY_TIMELINE_PEEK_BEFORE_TIME_M "timelineQuickViewBeforeTimeMin" static uint16_t s_timeline_peek_before_time_m = (TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S / SECONDS_PER_MINUTE); + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +#define PREF_KEY_TIMELINE_PEEK_WATCHFACE_FIT "timelineQuickViewWatchfaceFit" +static uint8_t s_timeline_peek_unsupported_face_mode = TimelinePeekUnsupportedFaceMode_None; #endif +#define PREF_KEY_POWER_MODE "powerMode" #define PREF_KEY_COREDUMP_ON_REQUEST "coredumpOnRequest" +#define PREF_KEY_ACCEL_SHAKE_LOG_INFO "accelShakeLogInfo" +#define PREF_KEY_VIBE_LOG_INFO "vibeLogInfo" +#define PREF_KEY_SETTINGS_DBS_COMPACTED_V1 "settingsDbsCompactedV1" +#ifdef CONFIG_APP_SCALING +#define PREF_KEY_LEGACY_APP_RENDER_MODE "legacyAppRenderMode" +#endif +static uint8_t s_power_mode = PowerMode_HighPerformance; static bool s_coredump_on_request_enabled = false; +static bool s_accel_shake_log_info_enabled = false; +static bool s_vibe_log_info_enabled = false; +static bool s_settings_dbs_compacted_v1 = false; +#ifdef CONFIG_APP_SCALING +static uint8_t s_legacy_app_render_mode = 1; // Default to scaled mode +#endif + +#ifdef CONFIG_THEMING +#define PREF_KEY_THEME_HIGHLIGHT_COLOR "themeHighlightColor" + +static GColor s_theme_highlight_color = GColorVividCerulean; +#endif + +#define PREF_KEY_MENU_SCROLL_WRAP_AROUND "menuScrollWrapAround" +#define PREF_KEY_MENU_SCROLL_VIBE_BEHAVIOR "menuScrollVibeBehavior" +#define PREF_KEY_MUSIC_SHOW_VOLUME_CONTROLS "musicShowVolumeControls" +#define PREF_KEY_MUSIC_SHOW_PROGRESS_BAR "musicShowProgressBar" + +static bool s_menu_scroll_wrap_around = false; +static MenuScrollVibeBehavior s_menu_scroll_vibe_behavior = MenuScrollNoVibe; +static bool s_music_show_volume_controls = true; +static bool s_music_show_progress_bar = true; // ============================================================================================ // Handlers for each pref that validate the new setting and store the new value in our globals. @@ -197,7 +279,17 @@ static bool s_coredump_on_request_enabled = false; // programmatically via the PREFS_MACRO macro defined below: // static bool prv_set_( new_value) static bool prv_set_s_clock_24h(bool *new_value) { - s_clock_24h = *new_value; + if (s_clock_24h != *new_value) { + s_clock_24h = *new_value; + // Fire a tick event so watchfaces re-render with the new time format + PebbleEvent e = { + .type = PEBBLE_TICK_EVENT, + .clock_tick.tick_time = rtc_get_time(), + }; + event_put(&e); + } else { + s_clock_24h = *new_value; + } return true; } @@ -206,6 +298,11 @@ static bool prv_set_s_clock_timezone_source_is_manual(bool *new_value) { return true; } +static bool prv_set_s_clock_time_source_is_manual(bool *new_value) { + s_clock_time_source_is_manual = *new_value; + return true; +} + static bool prv_set_s_clock_phone_timezone_id(int16_t *new_value) { s_clock_phone_timezone_id = *new_value; return true; @@ -239,23 +336,103 @@ static bool prv_set_s_backlight_timeout_ms(uint32_t *timeout_ms) { return false; } -static uint16_t prv_convert_backlight_percent_to_intensity(uint32_t percent_intensity); +static bool prv_set_s_backlight_intensity(uint8_t *intensity) { + s_backlight_intensity = *intensity; + return true; +} -static bool prv_set_s_backlight_intensity(uint16_t *intensity) { - if (*intensity > BACKLIGHT_BRIGHTNESS_OFF) { - s_backlight_intensity = *intensity; - return true; - } - s_backlight_intensity = - prv_convert_backlight_percent_to_intensity(BOARD_CONFIG.backlight_on_percent); - return false; +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +static bool prv_set_s_backlight_color(uint32_t *rgb_color) { + // Mask to packed 0x00RRGGBB; ignore any junk in the top byte. + s_backlight_color = *rgb_color & 0x00FFFFFFU; + return true; } +#endif static bool prv_set_s_backlight_motion_enabled(bool *enabled) { s_backlight_motion_enabled = *enabled; + accel_manager_set_motion_backlight_enabled(*enabled); return true; } +static bool prv_set_s_backlight_touch_wake(uint8_t *wake) { + if (*wake != BacklightTouchWake_Off && *wake != BacklightTouchWake_DoubleTap && + *wake != BacklightTouchWake_Tap) { + return false; + } + s_backlight_touch_wake = *wake; +#ifdef CONFIG_TOUCH + touch_set_backlight_enabled(*wake != BacklightTouchWake_Off); +#endif + return true; +} + +static bool prv_set_s_touch_enabled(bool *enabled) { + s_touch_enabled = *enabled; +#ifdef CONFIG_TOUCH + touch_service_set_globally_enabled(*enabled); +#endif + return true; +} + +#ifdef CONFIG_DYNAMIC_BACKLIGHT +static bool prv_set_s_backlight_dynamic_intensity_enabled(bool *enabled) { + s_backlight_dynamic_intensity_enabled = *enabled; + return true; +} + +static bool prv_set_s_dynamic_backlight_min_threshold(uint32_t *threshold) { + // Validate and constrain the threshold + if (*threshold > AMBIENT_LIGHT_LEVEL_MAX) { + s_dynamic_backlight_min_threshold = AMBIENT_LIGHT_LEVEL_MAX; + return false; + } + s_dynamic_backlight_min_threshold = *threshold; + return true; +} +#endif + +static bool prv_set_s_motion_sensitivity(uint8_t *sensitivity) { + // Clamp sensitivity to 0-100 range + if (*sensitivity > 100) { + s_motion_sensitivity = 100; // Reset to default if invalid + return false; + } + s_motion_sensitivity = *sensitivity; + + // Update accelerometer sensitivity in accel_manager + // This applies the setting to the hardware + #ifdef CONFIG_ACCEL_SENSITIVITY + accel_manager_update_sensitivity(*sensitivity); + #endif + + return true; +} + +static bool prv_set_s_backlight_ambient_threshold(uint32_t *threshold) { + // Validate and constrain the threshold + if (*threshold > AMBIENT_LIGHT_LEVEL_MAX) { + s_backlight_ambient_threshold = AMBIENT_LIGHT_LEVEL_MAX; + return false; + } + if (*threshold < 1) { + s_backlight_ambient_threshold = 1; + return false; + } + s_backlight_ambient_threshold = *threshold; + // Update the ambient light driver + ambient_light_set_dark_threshold(*threshold); + return true; +} + +#ifdef CONFIG_ORIENTATION_MANAGER +static bool prv_set_s_display_orientation_left(bool *left) { + s_display_orientation_left = *left; + orientation_handle_prefs_changed(); + return true; +} +#endif + static bool prv_set_s_stationary_mode_enabled(bool *enabled) { s_stationary_mode_enabled = *enabled; return true; @@ -277,26 +454,71 @@ static bool prv_set_s_language_english(bool *english) { return true; } +static bool prv_set_s_language(uint8_t *language) { + if (*language >= ShellLanguageCount) { + s_language = ShellLanguageInstalledPack; + return false; + } + + s_language = *language; + return true; +} + +// Normalize QuickLaunchPreference: enabled must be false if UUID is invalid +static void prv_normalize_quick_launch_pref(QuickLaunchPreference *pref) { + if (uuid_is_invalid(&pref->uuid)) { + pref->enabled = false; + } +} + static bool prv_set_s_quick_launch_up(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); s_quick_launch_up = *pref; return true; } static bool prv_set_s_quick_launch_down(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); s_quick_launch_down = *pref; return true; } static bool prv_set_s_quick_launch_select(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); s_quick_launch_select = *pref; return true; } static bool prv_set_s_quick_launch_back(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); s_quick_launch_back = *pref; return true; } +static bool prv_set_s_quick_launch_single_click_up(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); + s_quick_launch_single_click_up = *pref; + return true; +} + +static bool prv_set_s_quick_launch_single_click_down(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); + s_quick_launch_single_click_down = *pref; + return true; +} + +static bool prv_set_s_quick_launch_combo_back_up(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); + s_quick_launch_combo_back_up = *pref; + return true; +} + +static bool prv_set_s_quick_launch_combo_up_down(QuickLaunchPreference *pref) { + prv_normalize_quick_launch_pref(pref); + s_quick_launch_combo_up_down = *pref; + return true; +} + static bool prv_set_s_quick_launch_setup_opened(uint8_t *version) { s_quick_launch_setup_opened = *version; return true; @@ -312,7 +534,6 @@ static bool prv_set_s_welcome_version(uint8_t *version) { return true; } -#if CAPABILITY_HAS_HEALTH_TRACKING static bool prv_set_s_activity_preferences(ActivitySettings *new_settings) { bool invalid_data = false; @@ -338,6 +559,14 @@ static bool prv_set_s_activity_preferences(ActivitySettings *new_settings) { invalid_data = true; } + // Ensure activity is enabled before trying to start tracking. This is necessary because + // services_set_runlevel() may have already run before activity was initialized (e.g., when + // time is not valid at boot), which means the enabled_run_level flag was never set. + // Without this, activity_start_tracking() will fail to start tracking on first boot. + if (activity_is_initialized()) { + activity_set_enabled(true); + } + if (new_settings->tracking_enabled) { activity_start_tracking(false); } else { @@ -395,37 +624,31 @@ static bool prv_set_s_activity_hrm_preferences(ActivityHRMSettings *new_settings // for the setting s_activity_hrm_preferences = *new_settings; -#if CAPABILITY_HAS_BUILTIN_HRM +#ifdef CONFIG_HRM hrm_manager_handle_prefs_changed(); -#endif // CAPABILITY_HAS_BUILTIN_HRM +#endif // CONFIG_HRM #if BLE_HRM_SERVICE ble_hrm_handle_activity_prefs_heart_rate_is_enabled(new_settings->enabled); #endif // BLE_HRM_SERVICE return true; } -#endif /* CAPABILITY_HAS_HEALTH_TRACKING */ - -#if PLATFORM_SPALDING -static bool prv_set_s_display_user_offset(GPoint *offset) { - s_display_user_offset = *offset; - return true; -} -static bool prv_set_s_should_prompt_display_calibration(bool should_prompt) { - s_should_prompt_display_calibration = should_prompt; - return true; -} -#endif -#if CAPABILITY_HAS_TIMELINE_PEEK static uint8_t prv_set_s_timeline_settings_opened(uint8_t *version) { s_timeline_settings_opened = *version; return true; } +// Callback to run timeline_peek_set_enabled on KernelMain task. +// This is needed because animation scheduling requires running on KernelMain or App task, +// but settings sync runs from a different task context. +static void prv_timeline_peek_set_enabled_callback(void *data) { + timeline_peek_set_enabled(s_timeline_peek_enabled); +} + static bool prv_set_s_timeline_peek_enabled(bool *enabled) { s_timeline_peek_enabled = *enabled; - timeline_peek_set_enabled(*enabled); + launcher_task_add_callback(prv_timeline_peek_set_enabled_callback, NULL); return true; } @@ -434,13 +657,121 @@ static bool prv_set_s_timeline_peek_before_time_m(uint16_t *before_time_m) { timeline_peek_set_show_before_time(*before_time_m * SECONDS_PER_MINUTE); return true; } + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +static bool prv_set_s_timeline_peek_unsupported_face_mode(uint8_t *mode) { + if (*mode >= TimelinePeekUnsupportedFaceModeCount) { + return false; + } + s_timeline_peek_unsupported_face_mode = *mode; + return true; +} #endif +static bool prv_set_s_power_mode(uint8_t *mode) { + if (*mode >= PowerModeCount) { + return false; + } + s_power_mode = *mode; + powermode_service_set_enabled(*mode == PowerMode_LowPower); + return true; +} + static bool prv_set_s_coredump_on_request_enabled(bool *enabled) { s_coredump_on_request_enabled = *enabled; return true; } +static bool prv_set_s_accel_shake_log_info_enabled(bool *enabled) { + s_accel_shake_log_info_enabled = *enabled; + return true; +} + +static bool prv_set_s_vibe_log_info_enabled(bool *enabled) { + s_vibe_log_info_enabled = *enabled; + return true; +} + +static bool prv_set_s_settings_dbs_compacted_v1(bool *done) { + s_settings_dbs_compacted_v1 = *done; + return true; +} + +#ifdef CONFIG_APP_SCALING +static bool prv_set_s_legacy_app_render_mode(uint8_t *mode) { + if (*mode >= LegacyAppRenderModeCount) { + return false; // Invalid value + } + s_legacy_app_render_mode = *mode; + return true; +} +#endif + +#ifdef CONFIG_THEMING +// Valid theme colors (unified scheme) +// Must match s_color_definitions in settings_themes.h +static bool prv_is_valid_theme_color(GColor color) { + // GColorClear means "use default" + if (color.argb == GColorClear.argb) { + return true; + } + // Valid colors from settings_themes.h + static const uint8_t valid_colors[] = { + GColorSunsetOrangeARGB8, // Red + GColorChromeYellowARGB8, // Orange + GColorYellowARGB8, // Yellow + GColorGreenARGB8, // Green + GColorCyanARGB8, // Cyan + GColorVividCeruleanARGB8, // Light Blue + GColorVeryLightBlueARGB8, // Royal Blue + GColorLavenderIndigoARGB8, // Purple + GColorMagentaARGB8, // Magenta + GColorBrilliantRoseARGB8, // Pink + }; + for (size_t i = 0; i < ARRAY_LENGTH(valid_colors); i++) { + if (color.argb == valid_colors[i]) { + return true; + } + } + return false; +} + +static bool prv_set_s_theme_highlight_color(GColor *color) { + if (!prv_is_valid_theme_color(*color)) { + PBL_LOG_WRN("Invalid menu highlight color 0x%02x, using default", + color->argb); + s_theme_highlight_color = GColorVividCerulean; + return false; // Reject invalid value + } + s_theme_highlight_color = *color; + return true; +} +#endif + +static bool prv_set_s_menu_scroll_wrap_around(bool *enabled) { + s_menu_scroll_wrap_around = *enabled; + return true; +} + +static bool prv_set_s_menu_scroll_vibe_behavior(MenuScrollVibeBehavior *new_behavior) { + if (*new_behavior >= MenuScrollVibeBehaviorsCount) { + s_menu_scroll_vibe_behavior = MenuScrollNoVibe; + return false; + } + s_menu_scroll_vibe_behavior = *new_behavior; + return true; +} + +static bool prv_set_s_music_show_volume_controls(bool *enabled) { + s_music_show_volume_controls = *enabled; + return true; +} + +static bool prv_set_s_music_show_progress_bar(bool *enabled) { + s_music_show_progress_bar = *enabled; + return true; +} + // ------------------------------------------------------------------------------------ // Table of all prefs typedef bool (*PrefSetHandler)(const void *value, size_t val_len); @@ -468,18 +799,6 @@ static const PrefsTableEntry s_prefs_table[] = { }; #undef PREFS_MACRO - - -// ------------------------------------------------------------------------------------ -// FIXME PBL-22272. We back convert this value in -// settings_display.c:prv_get_scaled_brightness() We should really just store -// the percent intensity or a setting level name and leave it up to the light -// module to do the conversion -static uint16_t prv_convert_backlight_percent_to_intensity(uint32_t percent_intensity) { - return (BACKLIGHT_BRIGHTNESS_MAX * (uint32_t)percent_intensity) / 100; -} - - // ------------------------------------------------------------------------------------ static void prv_convert_deprecated_backlight_behaviour_key(SettingsFile *file) { // if present, convert deprecated BACKLIGHT_BEHAVIOUR key to the two new separate keys @@ -504,8 +823,22 @@ static void prv_convert_deprecated_backlight_behaviour_key(SettingsFile *file) { // ------------------------------------------------------------------------------------ void shell_prefs_init(void) { - s_backlight_intensity = - prv_convert_backlight_percent_to_intensity(BOARD_CONFIG.backlight_on_percent); +#ifdef CONFIG_QEMU + s_backlight_intensity = 100U; // Blinding +#else + s_backlight_intensity = 25U; // Medium +#endif + s_backlight_ambient_threshold = BOARD_CONFIG.ambient_light_dark_threshold; +#ifdef CONFIG_DYNAMIC_BACKLIGHT + s_dynamic_backlight_min_threshold = BOARD_CONFIG.dynamic_backlight_min_threshold; +#endif +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + s_backlight_color = BOARD_CONFIG.backlight_default_color; +#endif + // Use board-specific default motion sensitivity if provided (non-zero) + if (BOARD_CONFIG_ACCEL.accel_config.default_motion_sensitivity != 0) { + s_motion_sensitivity = BOARD_CONFIG_ACCEL.accel_config.default_motion_sensitivity; + } s_mutex = mutex_create(); SettingsFile file = {{0}}; @@ -527,6 +860,26 @@ void shell_prefs_init(void) { } settings_file_close(&file); + + // Update the ambient light driver with the loaded threshold value + ambient_light_set_dark_threshold(s_backlight_ambient_threshold); + + // Initialize prefs sync (must be after prefs are loaded) + prefs_sync_init(); + + // Update accelerometer sensitivity with the loaded value +#ifdef CONFIG_ACCEL_SENSITIVITY + accel_manager_update_sensitivity(s_motion_sensitivity); +#endif + + // Subscribe to shake events for motion backlight only if the setting is enabled + accel_manager_set_motion_backlight_enabled(s_backlight_motion_enabled); + + // Enable touch sensor for touch backlight only if the setting is not Off +#ifdef CONFIG_TOUCH + touch_set_backlight_enabled(s_backlight_touch_wake != BacklightTouchWake_Off); + touch_service_set_globally_enabled(s_touch_enabled); +#endif } @@ -540,7 +893,7 @@ static const PrefsTableEntry *prv_prefs_entry(const uint8_t *key, size_t key_len return entry; } } - PBL_LOG(LOG_LEVEL_WARNING, "Unrecognized key: %s", (const char *)key); + PBL_LOG_WRN("Unrecognized key: %s", (const char *)key); return NULL; } @@ -549,7 +902,7 @@ static const PrefsTableEntry *prv_prefs_entry(const uint8_t *key, size_t key_len // Set the backing store for a pref static bool prv_set_pref_backing(const PrefsTableEntry *entry, const void *value, int value_len) { if (value_len != entry->value_len) { - PBL_LOG(LOG_LEVEL_WARNING, "Attempt to set %s using invalid value_len of %"PRIu32"", + PBL_LOG_WRN("Attempt to set %s using invalid value_len of %"PRIu32"", entry->key, (uint32_t)value_len); return false; } @@ -562,7 +915,7 @@ static bool prv_set_pref_backing(const PrefsTableEntry *entry, const void *value // Keys in the backing store include the null terminator, so we add 1 to key_len rv = settings_file_set(&file, entry->key, strlen(entry->key) + 1, value, value_len); if (rv != S_SUCCESS) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to set pref '%s' (%"PRIi32")", entry->key, (int32_t)rv); + PBL_LOG_WRN("Failed to set pref '%s' (%"PRIi32")", entry->key, (int32_t)rv); } settings_file_close(&file); } @@ -629,7 +982,7 @@ bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, } if (value_len != entry->value_len) { - PBL_LOG(LOG_LEVEL_WARNING, "Attempt to read %s using invalid value_len of %"PRIu32"", + PBL_LOG_WRN("Attempt to read %s using invalid value_len of %"PRIu32"", entry->key, (uint32_t)value_len); return false; } @@ -639,8 +992,10 @@ bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, { SettingsFile file = {{0}}; if (settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN) == S_SUCCESS) { - // Keys in the backing store include the null terminator, so we add 1 to key_len - success = (settings_file_get(&file, entry->key, key_len + 1, value, value_len) + // Keys in the backing store include the null terminator + // Use strlen(entry->key) + 1 to match how it was written, since key_len from + // BlobDB may or may not include the null terminator + success = (settings_file_get(&file, entry->key, strlen(entry->key) + 1, value, value_len) == S_SUCCESS); settings_file_close(&file); } @@ -650,6 +1005,15 @@ bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, } +void prefs_private_lock(void) { + mutex_lock(s_mutex); +} + +void prefs_private_unlock(void) { + mutex_unlock(s_mutex); +} + + // ------------------------------------------------------------------------------------ // Called from KernelMain when we get a blob DB event. We take this opportunity to update the state // of the given pref @@ -674,6 +1038,16 @@ void prefs_private_handle_blob_db_event(PebbleBlobDBEvent *event) { // so we should write the new value to the backing prefs_private_write_backing(event->key, event->key_len, entry->value, entry->value_len); } + + // Notify UI that a preference changed so it can refresh + PebbleEvent pref_event = { + .type = PEBBLE_PREF_CHANGE_EVENT, + .pref_change = { + .key = entry->key, + .key_len = strlen(entry->key) + 1, + }, + }; + event_put(&pref_event); } } @@ -709,6 +1083,14 @@ void shell_prefs_set_timezone_source_manual(bool manual) { prv_pref_set(PREF_KEY_CLOCK_TIMEZONE_SOURCE_IS_MANUAL, &manual, sizeof(manual)); } +bool shell_prefs_is_time_source_manual(void) { + return s_clock_time_source_is_manual; +} + +void shell_prefs_set_time_source_manual(bool manual) { + prv_pref_set(PREF_KEY_CLOCK_TIME_SOURCE_IS_MANUAL, &manual, sizeof(manual)); +} + void shell_prefs_set_automatic_timezone_id(int16_t timezone_id) { prv_pref_set(PREF_KEY_CLOCK_PHONE_TIMEZONE_ID, &timezone_id, sizeof(timezone_id)); } @@ -740,9 +1122,6 @@ void backlight_set_enabled(bool enabled) { } bool backlight_is_ambient_sensor_enabled(void) { -#if INFINITE_BACKLIGHT - return false; -#endif return s_backlight_ambient_sensor_enabled; } @@ -751,9 +1130,6 @@ void backlight_set_ambient_sensor_enabled(bool enabled) { } uint32_t backlight_get_timeout_ms(void) { -#if INFINITE_BACKLIGHT - return UINT32_MAX; -#endif return s_backlight_timeout_ms; } @@ -761,21 +1137,27 @@ void backlight_set_timeout_ms(uint32_t timeout_ms) { prv_pref_set(PREF_KEY_BACKLIGHT_TIMEOUT_MS, &timeout_ms, sizeof(timeout_ms)); } -uint16_t backlight_get_intensity(void) { +uint8_t backlight_get_intensity(void) { return s_backlight_intensity; } -uint8_t backlight_get_intensity_percent(void) { - return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX; +void backlight_set_intensity(uint8_t percent_intensity) { + PBL_ASSERTN(percent_intensity > 0 && percent_intensity <= 100); + prv_pref_set(PREF_KEY_BACKLIGHT_INTENSITY, &percent_intensity, sizeof(percent_intensity)); } -void backlight_set_intensity_percent(uint8_t percent_intensity) { - PBL_ASSERTN(percent_intensity > 0 && percent_intensity <= 100); - uint16_t intensity = prv_convert_backlight_percent_to_intensity(percent_intensity); - PBL_ASSERTN(intensity > BACKLIGHT_BRIGHTNESS_OFF); - prv_pref_set(PREF_KEY_BACKLIGHT_INTENSITY, &intensity, sizeof(intensity)); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +uint32_t backlight_get_default_color(void) { + return s_backlight_color; } +void backlight_set_default_color(uint32_t rgb_color) { + // Clamp to 24-bit packed RGB; upper byte is unused. + rgb_color &= 0x00FFFFFFU; + prv_pref_set(PREF_KEY_BACKLIGHT_COLOR, &rgb_color, sizeof(rgb_color)); +} +#endif + bool backlight_is_motion_enabled(void) { return s_backlight_motion_enabled; } @@ -784,6 +1166,89 @@ void backlight_set_motion_enabled(bool enable) { prv_pref_set(PREF_KEY_BACKLIGHT_MOTION, &enable, sizeof(enable)); } +BacklightTouchWake backlight_get_touch_wake(void) { + return (BacklightTouchWake)s_backlight_touch_wake; +} + +void backlight_set_touch_wake(BacklightTouchWake wake) { + uint8_t value = (uint8_t)wake; + prv_pref_set(PREF_KEY_BACKLIGHT_TOUCH, &value, sizeof(value)); +} + +bool touch_is_globally_enabled(void) { + return s_touch_enabled; +} + +void touch_set_globally_enabled(bool enable) { + prv_pref_set(PREF_KEY_TOUCH_ENABLED, &enable, sizeof(enable)); +} + +#ifdef CONFIG_DYNAMIC_BACKLIGHT +bool backlight_is_dynamic_intensity_enabled(void) { + return s_backlight_dynamic_intensity_enabled; +} + +void backlight_set_dynamic_intensity_enabled(bool enable) { + prv_pref_set(PREF_KEY_BACKLIGHT_DYNAMIC_INTENSITY, &enable, sizeof(enable)); +} +#endif + +uint8_t shell_prefs_get_motion_sensitivity(void) { + return s_motion_sensitivity; +} + +void shell_prefs_set_motion_sensitivity(uint8_t sensitivity) { + // Clamp to valid range + if (sensitivity > 100) { + sensitivity = 50; + } + prv_pref_set(PREF_KEY_MOTION_SENSITIVITY, &sensitivity, sizeof(sensitivity)); +} + +uint32_t backlight_get_ambient_threshold(void) { + return s_backlight_ambient_threshold; +} + +void backlight_set_ambient_threshold(uint32_t threshold) { + // Validate threshold is within acceptable range + if (threshold > AMBIENT_LIGHT_LEVEL_MAX) { + threshold = AMBIENT_LIGHT_LEVEL_MAX; + } + if (threshold < 1) { + threshold = 1; + } + prv_pref_set(PREF_KEY_BACKLIGHT_AMBIENT_THRESHOLD, &threshold, sizeof(threshold)); + // Update the ambient light driver with the new threshold + ambient_light_set_dark_threshold(threshold); +} + +#ifdef CONFIG_DYNAMIC_BACKLIGHT +uint32_t backlight_get_dynamic_min_threshold(void) { + return s_dynamic_backlight_min_threshold; +} + +void backlight_set_dynamic_min_threshold(uint32_t threshold) { + // Validate threshold is within acceptable range + if (threshold > AMBIENT_LIGHT_LEVEL_MAX) { + threshold = AMBIENT_LIGHT_LEVEL_MAX; + } + // Note: Min threshold should be much smaller than ambient_light_dark_threshold (Zone 2 upper bound) + // Typically min_threshold is 0-5, while ambient_light_dark_threshold is ~150 + prv_pref_set(PREF_KEY_DYNAMIC_BACKLIGHT_MIN_THRESHOLD, &threshold, sizeof(threshold)); +} + +#endif + +#ifdef CONFIG_ORIENTATION_MANAGER +bool display_orientation_is_left(void) { + return s_display_orientation_left; +} + +void display_orientation_set_left(bool left) { + prv_pref_set(PREF_KEY_DISPLAY_ORIENTATION_LEFT_HANDED, &left, sizeof(left)); +} +#endif + bool shell_prefs_get_stationary_enabled(void) { return s_stationary_mode_enabled; } @@ -908,6 +1373,122 @@ uint8_t quick_launch_get_quick_launch_setup_opened(void) { return s_quick_launch_setup_opened; } +bool quick_launch_single_click_is_enabled(ButtonId button) { + switch (button) { + case BUTTON_ID_UP: + return s_quick_launch_single_click_up.enabled; + case BUTTON_ID_DOWN: + return s_quick_launch_single_click_down.enabled; + case BUTTON_ID_SELECT: + case BUTTON_ID_BACK: + case NUM_BUTTONS: + break; + } + return false; +} + +AppInstallId quick_launch_single_click_get_app(ButtonId button) { + Uuid *uuid = NULL; + switch (button) { + case BUTTON_ID_UP: + uuid = &s_quick_launch_single_click_up.uuid; + break; + case BUTTON_ID_DOWN: + uuid = &s_quick_launch_single_click_down.uuid; + break; + default: + PBL_ASSERTN(0); // Should not reach here: invalid button id + break; + } + return app_install_get_id_for_uuid(uuid); +} + +void quick_launch_single_click_set_app(ButtonId button, AppInstallId app_id) { + QuickLaunchPreference pref = (QuickLaunchPreference) { + .enabled = true, + }; + app_install_get_uuid_for_install_id(app_id, &pref.uuid); + + const char *key = NULL; + switch (button) { + case BUTTON_ID_UP: + key = PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_UP; + break; + case BUTTON_ID_DOWN: + key = PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_DOWN; + break; + default: + PBL_ASSERTN(0); // Should not reach here: invalid button id + break; + } + prv_pref_set(key, &pref, sizeof(pref)); +} + +void quick_launch_single_click_set_enabled(ButtonId button, bool enabled) { + QuickLaunchPreference pref; + + const char *key = NULL; + switch (button) { + case BUTTON_ID_UP: + pref = s_quick_launch_single_click_up; + key = PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_UP; + break; + case BUTTON_ID_DOWN: + pref = s_quick_launch_single_click_down; + key = PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_DOWN; + break; + default: + PBL_ASSERTN(0); // Should not reach here: invalid button id + break; + } + pref.enabled = enabled; + prv_pref_set(key, &pref, sizeof(pref)); +} + +bool quick_launch_combo_back_up_is_enabled(void) { + return s_quick_launch_combo_back_up.enabled; +} + +AppInstallId quick_launch_combo_back_up_get_app(void) { + return app_install_get_id_for_uuid(&s_quick_launch_combo_back_up.uuid); +} + +void quick_launch_combo_back_up_set_app(AppInstallId app_id) { + QuickLaunchPreference pref = (QuickLaunchPreference) { + .enabled = true, + }; + app_install_get_uuid_for_install_id(app_id, &pref.uuid); + prv_pref_set(PREF_KEY_QUICK_LAUNCH_COMBO_BACK_UP, &pref, sizeof(pref)); +} + +void quick_launch_combo_back_up_set_enabled(bool enabled) { + QuickLaunchPreference pref = s_quick_launch_combo_back_up; + pref.enabled = enabled; + prv_pref_set(PREF_KEY_QUICK_LAUNCH_COMBO_BACK_UP, &pref, sizeof(pref)); +} + +bool quick_launch_combo_up_down_is_enabled(void) { + return s_quick_launch_combo_up_down.enabled; +} + +AppInstallId quick_launch_combo_up_down_get_app(void) { + return app_install_get_id_for_uuid(&s_quick_launch_combo_up_down.uuid); +} + +void quick_launch_combo_up_down_set_app(AppInstallId app_id) { + QuickLaunchPreference pref = (QuickLaunchPreference) { + .enabled = true, + }; + app_install_get_uuid_for_install_id(app_id, &pref.uuid); + prv_pref_set(PREF_KEY_QUICK_LAUNCH_COMBO_UP_DOWN, &pref, sizeof(pref)); +} + +void quick_launch_combo_up_down_set_enabled(bool enabled) { + QuickLaunchPreference pref = s_quick_launch_combo_up_down; + pref.enabled = enabled; + prv_pref_set(PREF_KEY_QUICK_LAUNCH_COMBO_UP_DOWN, &pref, sizeof(pref)); +} + void watchface_set_default_install_id(AppInstallId app_id) { Uuid uuid; app_install_get_uuid_for_install_id(app_id, &uuid); @@ -950,7 +1531,7 @@ AppInstallId watchface_get_default_install_id(void) { void system_theme_set_content_size(PreferredContentSize content_size) { if (content_size >= NumPreferredContentSizes) { - PBL_LOG(LOG_LEVEL_WARNING, "Ignoring attempt to set content size to invalid size %d", + PBL_LOG_WRN("Ignoring attempt to set content size to invalid size %d", content_size); return; } @@ -975,7 +1556,51 @@ void shell_prefs_toggle_language_english(void) { shell_prefs_set_language_english(!shell_prefs_get_language_english()); } -#if CAPABILITY_HAS_HEALTH_TRACKING +ShellLanguage shell_prefs_get_language(void) { + if (s_language >= ShellLanguageCount) { + return ShellLanguageInstalledPack; + } + + return (ShellLanguage)s_language; +} + +uint32_t shell_prefs_get_language_resource_id(void) { + switch (shell_prefs_get_language()) { + case ShellLanguageCatalan: + return RESOURCE_ID_STRINGS_CA_ES; + case ShellLanguageGerman: + return RESOURCE_ID_STRINGS_DE_DE; + case ShellLanguageSpanish: + return RESOURCE_ID_STRINGS_ES_ES; + case ShellLanguageFrench: + return RESOURCE_ID_STRINGS_FR_FR; + case ShellLanguageItalian: + return RESOURCE_ID_STRINGS_IT_IT; + case ShellLanguageDutch: + return RESOURCE_ID_STRINGS_NL_NL; + case ShellLanguagePortuguese: + return RESOURCE_ID_STRINGS_PT_PT; + case ShellLanguageEnglish: + case ShellLanguageInstalledPack: + case ShellLanguageCount: + return RESOURCE_ID_STRINGS; + } + + return RESOURCE_ID_STRINGS; +} + +void shell_prefs_set_language(ShellLanguage language) { + uint8_t language_value = language; + if (language >= ShellLanguageCount) { + language_value = ShellLanguageInstalledPack; + } + + const bool english = (language_value == ShellLanguageEnglish); + prv_pref_set(PREF_KEY_LANGUAGE, &language_value, sizeof(language_value)); + shell_prefs_set_language_english(english); + i18n_set_resource(shell_prefs_get_language_resource_id()); +} + static void prv_activity_pref_set(void) { prv_pref_set(PREF_KEY_ACTIVITY_PREFERENCES, &s_activity_preferences, sizeof(s_activity_preferences)); @@ -1118,44 +1743,49 @@ bool activity_prefs_heart_rate_is_enabled(void) { return s_activity_hrm_preferences.enabled; } -void alarm_prefs_set_alarms_app_opened(uint8_t version) { - if (s_alarms_app_opened != version) { - s_alarms_app_opened = version; - prv_pref_set(PREF_KEY_ALARMS_APP_OPENED, &s_alarms_app_opened, sizeof(s_alarms_app_opened)); +#ifdef CONFIG_HRM +HRMonitoringInterval activity_prefs_get_hrm_measurement_interval(void) { + uint8_t interval = s_activity_hrm_preferences.measurement_interval; + if (interval >= HRMonitoringIntervalCount) { + return HRMonitoringInterval_10Min; } + return (HRMonitoringInterval)interval; } -uint8_t alarm_prefs_get_alarms_app_opened(void) { - return s_alarms_app_opened; -} -#endif /* CAPABILITY_HAS_HEALTH_TRACKING */ - -#if PLATFORM_SPALDING -void shell_prefs_set_display_offset(GPoint offset) { - const GPoint user_offset = gpoint_sub(offset, mfg_info_get_disp_offsets()); - prv_pref_set(PREF_KEY_DISPLAY_USER_OFFSET, &user_offset, sizeof(user_offset)); +void activity_prefs_set_hrm_measurement_interval(HRMonitoringInterval interval) { + if (s_activity_hrm_preferences.measurement_interval != (uint8_t)interval) { + s_activity_hrm_preferences.measurement_interval = (uint8_t)interval; + prv_pref_set(PREF_KEY_ACTIVITY_HRM_PREFERENCES, &s_activity_hrm_preferences, + sizeof(s_activity_hrm_preferences)); + hrm_manager_handle_prefs_changed(); + } } -GPoint shell_prefs_get_display_offset(void) { - return gpoint_add(s_display_user_offset, mfg_info_get_disp_offsets()); +bool activity_prefs_hrm_activity_tracking_is_enabled(void) { + return s_activity_hrm_preferences.activity_tracking_enabled; } -void shell_prefs_display_offset_init(void) { - display_set_offset(shell_prefs_get_display_offset()); +void activity_prefs_set_hrm_activity_tracking_enabled(bool enabled) { + if (s_activity_hrm_preferences.activity_tracking_enabled != enabled) { + s_activity_hrm_preferences.activity_tracking_enabled = enabled; + prv_pref_set(PREF_KEY_ACTIVITY_HRM_PREFERENCES, &s_activity_hrm_preferences, + sizeof(s_activity_hrm_preferences)); + hrm_manager_handle_prefs_changed(); + } } +#endif -bool shell_prefs_should_prompt_display_calibration(void) { - return s_should_prompt_display_calibration; +void alarm_prefs_set_alarms_app_opened(uint8_t version) { + if (s_alarms_app_opened != version) { + s_alarms_app_opened = version; + prv_pref_set(PREF_KEY_ALARMS_APP_OPENED, &s_alarms_app_opened, sizeof(s_alarms_app_opened)); + } } -void shell_prefs_set_should_prompt_display_calibration(bool should_prompt) { - s_should_prompt_display_calibration = should_prompt; - prv_pref_set(PREF_KEY_SHOULD_PROMPT_DISPLAY_CALIBRATION, &s_should_prompt_display_calibration, - sizeof(s_should_prompt_display_calibration)); +uint8_t alarm_prefs_get_alarms_app_opened(void) { + return s_alarms_app_opened; } -#endif -#if CAPABILITY_HAS_TIMELINE_PEEK void timeline_prefs_set_settings_opened(uint8_t version) { prv_pref_set(PREF_KEY_TIMELINE_SETTINGS_OPENED, &version, sizeof(version)); } @@ -1179,9 +1809,15 @@ void timeline_peek_prefs_set_before_time(uint16_t before_time_m) { uint16_t timeline_peek_prefs_get_before_time(void) { return s_timeline_peek_before_time_m; } -#else -uint16_t timeline_peek_prefs_get_before_time(void) { - return TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S; + +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +void timeline_peek_prefs_set_unsupported_face_mode(TimelinePeekUnsupportedFaceMode mode) { + uint8_t mode_value = mode; + prv_pref_set(PREF_KEY_TIMELINE_PEEK_WATCHFACE_FIT, &mode_value, sizeof(mode_value)); +} + +TimelinePeekUnsupportedFaceMode timeline_peek_prefs_get_unsupported_face_mode(void) { + return (TimelinePeekUnsupportedFaceMode)s_timeline_peek_unsupported_face_mode; } #endif @@ -1192,3 +1828,110 @@ bool shell_prefs_can_coredump_on_request(void) { void shell_prefs_set_coredump_on_request(bool enabled) { prv_pref_set(PREF_KEY_COREDUMP_ON_REQUEST, &enabled, sizeof(enabled)); } + +bool shell_prefs_get_accel_shake_log_info_enabled(void) { + return s_accel_shake_log_info_enabled; +} + +void shell_prefs_set_accel_shake_log_info_enabled(bool enabled) { + prv_pref_set(PREF_KEY_ACCEL_SHAKE_LOG_INFO, &enabled, sizeof(enabled)); +} + +bool shell_prefs_get_vibe_log_info_enabled(void) { + return s_vibe_log_info_enabled; +} + +void shell_prefs_set_vibe_log_info_enabled(bool enabled) { + prv_pref_set(PREF_KEY_VIBE_LOG_INFO, &enabled, sizeof(enabled)); +} + +bool shell_prefs_get_settings_dbs_compacted_v1(void) { + return s_settings_dbs_compacted_v1; +} + +void shell_prefs_set_settings_dbs_compacted_v1(bool done) { + prv_pref_set(PREF_KEY_SETTINGS_DBS_COMPACTED_V1, &done, sizeof(done)); +} + +#ifdef CONFIG_APP_SCALING +LegacyAppRenderMode shell_prefs_get_legacy_app_render_mode(void) { + return (LegacyAppRenderMode)s_legacy_app_render_mode; +} + +void shell_prefs_set_legacy_app_render_mode(LegacyAppRenderMode mode) { + uint8_t mode_value = (uint8_t)mode; + prv_pref_set(PREF_KEY_LEGACY_APP_RENDER_MODE, &mode_value, sizeof(mode_value)); +} +#endif + +GColor shell_prefs_get_theme_highlight_color(void) { +#ifdef CONFIG_THEMING + return s_theme_highlight_color; +#else + return PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorBlack); +#endif +} + +void shell_prefs_set_theme_highlight_color(GColor color) { +#ifdef CONFIG_THEMING + prv_pref_set(PREF_KEY_THEME_HIGHLIGHT_COLOR, &color, sizeof(GColor)); +#endif +} + +bool shell_prefs_get_menu_scroll_wrap_around_enable(void) { + return s_menu_scroll_wrap_around; +} + +void shell_prefs_set_menu_scroll_wrap_around_enable(bool enable) { + prv_pref_set(PREF_KEY_MENU_SCROLL_WRAP_AROUND, &enable, sizeof(bool)); +} + +MenuScrollVibeBehavior shell_prefs_get_menu_scroll_vibe_behavior(void) { + return s_menu_scroll_vibe_behavior; +} + +void shell_prefs_set_menu_scroll_vibe_behavior(MenuScrollVibeBehavior behavior) { + prv_pref_set(PREF_KEY_MENU_SCROLL_VIBE_BEHAVIOR, &behavior, sizeof(MenuScrollVibeBehavior)); +} + +PowerMode shell_prefs_get_power_mode(void) { + return (PowerMode)s_power_mode; +} + +void shell_prefs_set_power_mode(PowerMode mode) { + uint8_t val = (uint8_t)mode; + prv_pref_set(PREF_KEY_POWER_MODE, &val, sizeof(val)); +} + +void pbl_analytics_external_collect_settings(void) { + PBL_ANALYTICS_SET_UNSIGNED(settings_health_tracking_enabled, + activity_prefs_tracking_is_enabled()); +#ifdef CONFIG_HRM + PBL_ANALYTICS_SET_UNSIGNED(settings_health_hrm_enabled, + activity_prefs_heart_rate_is_enabled()); + PBL_ANALYTICS_SET_UNSIGNED(settings_health_hrm_measurement_interval, + activity_prefs_get_hrm_measurement_interval()); + PBL_ANALYTICS_SET_UNSIGNED(settings_health_hrm_activity_tracking_enabled, + activity_prefs_hrm_activity_tracking_is_enabled()); +#endif + PBL_ANALYTICS_SET_UNSIGNED(settings_power_mode, shell_prefs_get_power_mode()); + PBL_ANALYTICS_SET_UNSIGNED(settings_motion_sensitivity, shell_prefs_get_motion_sensitivity()); + PBL_ANALYTICS_SET_UNSIGNED(settings_backlight_intensity_pct, backlight_get_intensity()); + PBL_ANALYTICS_SET_UNSIGNED(settings_backlight_timeout_s, backlight_get_timeout_ms() / 1000); +} + +bool shell_prefs_get_music_show_volume_controls(void) { + return s_music_show_volume_controls; +} + +void shell_prefs_set_music_show_volume_controls(bool enable) { + prv_pref_set(PREF_KEY_MUSIC_SHOW_VOLUME_CONTROLS, &enable, sizeof(enable)); +} + +bool shell_prefs_get_music_show_progress_bar(void) { + return s_music_show_progress_bar; +} + +void shell_prefs_set_music_show_progress_bar(bool enable) { + prv_pref_set(PREF_KEY_MUSIC_SHOW_PROGRESS_BAR, &enable, sizeof(enable)); +} diff --git a/src/fw/shell/normal/prefs_sync.c b/src/fw/shell/normal/prefs_sync.c new file mode 100644 index 0000000000..76b37e1a2d --- /dev/null +++ b/src/fw/shell/normal/prefs_sync.c @@ -0,0 +1,152 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "prefs_sync.h" + +#include "applib/event_service_client.h" +#include "kernel/events.h" +#include "pbl/services/debounced_connection_service.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/settings_blob_db.h" +#include "pbl/services/blob_db/sync.h" +#include "system/logging.h" + +//! Prefs Sync using BlobDB +//! +//! Settings are now synced via BlobDB with database ID 0x0F (BlobDBIdSettings). +//! +//! The whitelist filtering and sync logic are implemented in: +//! services/blob_db/settings_blob_db.c +//! +//! This module simply triggers BlobDB sync when the phone connects. + +static bool s_sync_initialized = false; +static bool s_is_connected = false; +static EventServiceInfo s_connection_event_info; +static EventServiceInfo s_capabilities_event_info; + +//! Try to start settings sync if conditions are met +static void prv_try_start_sync(void) { + if (!s_is_connected) { + return; + } + + if (!settings_blob_db_phone_supports_sync()) { + PBL_LOG_DBG("Phone doesn't support settings sync"); + return; + } + + PBL_LOG_INFO("Starting settings sync via BlobDB"); + + status_t status = blob_db_sync_db(BlobDBIdSettings); + + if (status == S_SUCCESS) { + PBL_LOG_INFO("Settings sync started"); + } else if (status == S_NO_ACTION_REQUIRED) { + PBL_LOG_DBG("No settings need syncing"); + } else if (status == E_BUSY) { + PBL_LOG_DBG("Settings sync already in progress"); + } else { + PBL_LOG_ERR("Failed to start settings sync: 0x%"PRIx32"", status); + } +} + +//! Connection state change callback +static void prv_connection_handler(PebbleEvent *event, void *context) { + bool connected = event->bluetooth.comm_session_event.is_open; + s_is_connected = connected; + + if (connected) { + PBL_LOG_INFO("Phone connected, will sync when capabilities received"); + // Try to sync - capabilities may already be cached from previous connection + prv_try_start_sync(); + } else { + PBL_LOG_INFO("Phone disconnected"); + } +} + +//! Capabilities changed callback - triggers sync when phone reports settings_sync_support +static void prv_capabilities_handler(PebbleEvent *event, void *context) { + // Check if the settings_sync_support capability changed + if (event->capabilities.flags_diff.settings_sync_support) { + PBL_LOG_INFO("Settings sync capability changed, checking if we can sync"); + prv_try_start_sync(); + } +} + +// Public API + +void prefs_sync_init(void) { + if (s_sync_initialized) { + PBL_LOG_WRN("Prefs sync already initialized"); + return; + } + + // Subscribe to connection events + s_connection_event_info = (EventServiceInfo) { + .type = PEBBLE_BT_CONNECTION_DEBOUNCED_EVENT, + .handler = prv_connection_handler, + }; + event_service_client_subscribe(&s_connection_event_info); + + // Subscribe to capabilities changed events - this fires when phone sends version response + s_capabilities_event_info = (EventServiceInfo) { + .type = PEBBLE_CAPABILITIES_CHANGED_EVENT, + .handler = prv_capabilities_handler, + }; + event_service_client_subscribe(&s_capabilities_event_info); + + // Start with disconnected state - will be updated when we receive connection events + s_is_connected = false; + + s_sync_initialized = true; + + PBL_LOG_INFO("Prefs sync initialized (using BlobDB ID 0x0F)"); +} + +void prefs_sync_deinit(void) { + if (!s_sync_initialized) { + return; + } + + // Unsubscribe from events + event_service_client_unsubscribe(&s_connection_event_info); + event_service_client_unsubscribe(&s_capabilities_event_info); + + s_sync_initialized = false; + s_is_connected = false; + + PBL_LOG_INFO("Prefs sync deinitialized"); +} + +void prefs_sync_trigger(void) { + if (!s_sync_initialized) { + PBL_LOG_WRN("Prefs sync not initialized"); + return; + } + + if (!s_is_connected) { + PBL_LOG_WRN("Not connected to phone, cannot sync"); + return; + } + + // Check if the phone supports settings sync + if (!settings_blob_db_phone_supports_sync()) { + PBL_LOG_WRN("Phone doesn't support settings sync"); + return; + } + + PBL_LOG_INFO("Manually triggering settings sync via BlobDB"); + + status_t status = blob_db_sync_db(BlobDBIdSettings); + + if (status == S_SUCCESS) { + PBL_LOG_INFO("Settings sync started"); + } else if (status == S_NO_ACTION_REQUIRED) { + PBL_LOG_INFO("No settings need syncing"); + } else if (status == E_BUSY) { + PBL_LOG_WRN("Settings sync already in progress"); + } else { + PBL_LOG_ERR("Failed to start settings sync: 0x%"PRIx32"", status); + } +} diff --git a/src/fw/shell/normal/prefs_sync.h b/src/fw/shell/normal/prefs_sync.h new file mode 100644 index 0000000000..02c0c20c9d --- /dev/null +++ b/src/fw/shell/normal/prefs_sync.h @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +//! Prefs Sync Integration +//! +//! This module integrates settings sync with the shell prefs system. +//! It handles: +//! - Whitelisting of syncable preferences +//! - Automatic sync on connection to phone +//! - Debouncing for rapid preference changes + +//! Initialize prefs sync +//! Call this from shell_prefs_init() after prefs are loaded +void prefs_sync_init(void); + +//! Deinitialize prefs sync +void prefs_sync_deinit(void); + +//! Manually trigger a sync (e.g., for testing) +void prefs_sync_trigger(void); diff --git a/src/fw/shell/normal/prefs_values.h.inc b/src/fw/shell/normal/prefs_values.h.inc index e00651c19b..93517ef34a 100644 --- a/src/fw/shell/normal/prefs_values.h.inc +++ b/src/fw/shell/normal/prefs_values.h.inc @@ -1,25 +1,44 @@ // Included by prefs.c to declare the list of preferences and their keys PREFS_MACRO(PREF_KEY_CLOCK_24H, s_clock_24h) PREFS_MACRO(PREF_KEY_CLOCK_TIMEZONE_SOURCE_IS_MANUAL, s_clock_timezone_source_is_manual) + PREFS_MACRO(PREF_KEY_CLOCK_TIME_SOURCE_IS_MANUAL, s_clock_time_source_is_manual) PREFS_MACRO(PREF_KEY_CLOCK_PHONE_TIMEZONE_ID, s_clock_phone_timezone_id) PREFS_MACRO(PREF_KEY_UNITS_DISTANCE, s_units_distance) PREFS_MACRO(PREF_KEY_BACKLIGHT_ENABLED, s_backlight_enabled) PREFS_MACRO(PREF_KEY_BACKLIGHT_AMBIENT_SENSOR_ENABLED, s_backlight_ambient_sensor_enabled) PREFS_MACRO(PREF_KEY_BACKLIGHT_TIMEOUT_MS, s_backlight_timeout_ms) PREFS_MACRO(PREF_KEY_BACKLIGHT_INTENSITY, s_backlight_intensity) +#ifdef CONFIG_BACKLIGHT_HAS_COLOR + PREFS_MACRO(PREF_KEY_BACKLIGHT_COLOR, s_backlight_color) +#endif PREFS_MACRO(PREF_KEY_BACKLIGHT_MOTION, s_backlight_motion_enabled) + PREFS_MACRO(PREF_KEY_BACKLIGHT_TOUCH, s_backlight_touch_wake) + PREFS_MACRO(PREF_KEY_TOUCH_ENABLED, s_touch_enabled) + PREFS_MACRO(PREF_KEY_MOTION_SENSITIVITY, s_motion_sensitivity) +#ifdef CONFIG_DYNAMIC_BACKLIGHT + PREFS_MACRO(PREF_KEY_BACKLIGHT_DYNAMIC_INTENSITY, s_backlight_dynamic_intensity_enabled) + PREFS_MACRO(PREF_KEY_DYNAMIC_BACKLIGHT_MIN_THRESHOLD, s_dynamic_backlight_min_threshold) +#endif + PREFS_MACRO(PREF_KEY_BACKLIGHT_AMBIENT_THRESHOLD, s_backlight_ambient_threshold) PREFS_MACRO(PREF_KEY_STATIONARY, s_stationary_mode_enabled) PREFS_MACRO(PREF_KEY_DEFAULT_WORKER, s_default_worker) PREFS_MACRO(PREF_KEY_TEXT_STYLE, s_text_style) PREFS_MACRO(PREF_KEY_LANG_ENGLISH, s_language_english) + PREFS_MACRO(PREF_KEY_LANGUAGE, s_language) PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_UP, s_quick_launch_up) PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_DOWN, s_quick_launch_down) PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_SELECT, s_quick_launch_select) PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_BACK, s_quick_launch_back) + PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_UP, s_quick_launch_single_click_up) + PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_SINGLE_CLICK_DOWN, s_quick_launch_single_click_down) + PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_COMBO_BACK_UP, s_quick_launch_combo_back_up) + PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_COMBO_UP_DOWN, s_quick_launch_combo_up_down) PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_SETUP_OPENED, s_quick_launch_setup_opened) PREFS_MACRO(PREF_KEY_DEFAULT_WATCHFACE, s_default_watchface) PREFS_MACRO(PREF_KEY_WELCOME_VERSION, s_welcome_version) -#if CAPABILITY_HAS_HEALTH_TRACKING +#ifdef CONFIG_ORIENTATION_MANAGER + PREFS_MACRO(PREF_KEY_DISPLAY_ORIENTATION_LEFT_HANDED, s_display_orientation_left) +#endif PREFS_MACRO(PREF_KEY_ACTIVITY_PREFERENCES, s_activity_preferences) PREFS_MACRO(PREF_KEY_ACTIVITY_ACTIVATED_TIMESTAMP, s_activity_activation_timestamp) PREFS_MACRO(PREF_KEY_ACTIVITY_ACTIVATION_DELAY_INSIGHT, s_activity_activation_delay_insight) @@ -28,14 +47,26 @@ PREFS_MACRO(PREF_KEY_ALARMS_APP_OPENED, s_alarms_app_opened) PREFS_MACRO(PREF_KEY_ACTIVITY_HRM_PREFERENCES, s_activity_hrm_preferences) PREFS_MACRO(PREF_KEY_ACTIVITY_HEART_RATE_PREFERENCES, s_activity_hr_preferences) -#endif -#if PLATFORM_SPALDING - PREFS_MACRO(PREF_KEY_DISPLAY_USER_OFFSET, s_display_user_offset) - PREFS_MACRO(PREF_KEY_SHOULD_PROMPT_DISPLAY_CALIBRATION, s_should_prompt_display_calibration) -#endif -#if CAPABILITY_HAS_TIMELINE_PEEK PREFS_MACRO(PREF_KEY_TIMELINE_SETTINGS_OPENED, s_timeline_settings_opened) PREFS_MACRO(PREF_KEY_TIMELINE_PEEK_ENABLED, s_timeline_peek_enabled) PREFS_MACRO(PREF_KEY_TIMELINE_PEEK_BEFORE_TIME_M, s_timeline_peek_before_time_m) +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED + PREFS_MACRO(PREF_KEY_TIMELINE_PEEK_WATCHFACE_FIT, s_timeline_peek_unsupported_face_mode) #endif + PREFS_MACRO(PREF_KEY_POWER_MODE, s_power_mode) PREFS_MACRO(PREF_KEY_COREDUMP_ON_REQUEST, s_coredump_on_request_enabled) + PREFS_MACRO(PREF_KEY_ACCEL_SHAKE_LOG_INFO, s_accel_shake_log_info_enabled) + PREFS_MACRO(PREF_KEY_VIBE_LOG_INFO, s_vibe_log_info_enabled) + PREFS_MACRO(PREF_KEY_SETTINGS_DBS_COMPACTED_V1, s_settings_dbs_compacted_v1) +#ifdef CONFIG_APP_SCALING + PREFS_MACRO(PREF_KEY_LEGACY_APP_RENDER_MODE, s_legacy_app_render_mode) +#endif + +#ifdef CONFIG_THEMING + PREFS_MACRO(PREF_KEY_THEME_HIGHLIGHT_COLOR, s_theme_highlight_color) +#endif + + PREFS_MACRO(PREF_KEY_MENU_SCROLL_WRAP_AROUND, s_menu_scroll_wrap_around) + PREFS_MACRO(PREF_KEY_MENU_SCROLL_VIBE_BEHAVIOR, s_menu_scroll_vibe_behavior) + PREFS_MACRO(PREF_KEY_MUSIC_SHOW_VOLUME_CONTROLS, s_music_show_volume_controls) + PREFS_MACRO(PREF_KEY_MUSIC_SHOW_PROGRESS_BAR, s_music_show_progress_bar) diff --git a/src/fw/shell/normal/quick_launch.h b/src/fw/shell/normal/quick_launch.h index 95731a8ff7..879e64c83c 100644 --- a/src/fw/shell/normal/quick_launch.h +++ b/src/fw/shell/normal/quick_launch.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -26,3 +13,18 @@ void quick_launch_set_app(ButtonId button, AppInstallId app_id); void quick_launch_set_enabled(ButtonId button, bool enabled); void quick_launch_set_quick_launch_setup_opened(uint8_t version); uint8_t quick_launch_get_quick_launch_setup_opened(void); + +bool quick_launch_single_click_is_enabled(ButtonId button); +AppInstallId quick_launch_single_click_get_app(ButtonId button); +void quick_launch_single_click_set_app(ButtonId button, AppInstallId app_id); +void quick_launch_single_click_set_enabled(ButtonId button, bool enabled); + +bool quick_launch_combo_back_up_is_enabled(void); +AppInstallId quick_launch_combo_back_up_get_app(void); +void quick_launch_combo_back_up_set_app(AppInstallId app_id); +void quick_launch_combo_back_up_set_enabled(bool enabled); + +bool quick_launch_combo_up_down_is_enabled(void); +AppInstallId quick_launch_combo_up_down_get_app(void); +void quick_launch_combo_up_down_set_app(AppInstallId app_id); +void quick_launch_combo_up_down_set_enabled(bool enabled); \ No newline at end of file diff --git a/src/fw/shell/normal/shell.c b/src/fw/shell/normal/shell.c index 37b10f0a7b..d18c307bf4 100644 --- a/src/fw/shell/normal/shell.c +++ b/src/fw/shell/normal/shell.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/shell.h" @@ -21,7 +8,7 @@ #include "process_management/app_install_manager.h" #include "process_management/app_install_types.h" #include "process_management/app_manager.h" -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_transitions.h" #define WATCHFACE_SHUTTER_COLOR GColorWhite #define HEALTH_SHUTTER_COLOR PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite) @@ -54,9 +41,12 @@ const CompositorTransition *shell_get_watchface_compositor_animation( static const CompositorTransition *prv_app_launcher_transition_animation( CompositorTransitionDirection direction) { +#if PBL_RECT const bool app_is_destination = (direction == CompositorTransitionDirectionRight); - return PBL_IF_RECT_ELSE(compositor_launcher_app_transition_get(app_is_destination), - compositor_port_hole_transition_app_get(direction)); + return compositor_launcher_app_transition_get(app_is_destination); +#else + return compositor_port_hole_transition_app_get(direction); +#endif } const CompositorTransition *shell_get_close_compositor_animation(AppInstallId current_app_id, diff --git a/src/fw/shell/normal/shell_event_loop.c b/src/fw/shell/normal/shell_event_loop.c index 640b387021..4d0309d107 100644 --- a/src/fw/shell/normal/shell_event_loop.c +++ b/src/fw/shell/normal/shell_event_loop.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include "shell/shell_event_loop.h" #include "shell/prefs_private.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/app_fetch_ui.h" -#include "apps/system_apps/settings/settings_quick_launch.h" -#include "apps/system_apps/timeline/timeline.h" +#include "apps/system/app_fetch_ui.h" +#include "apps/system/settings/quick_launch.h" +#include "apps/system/timeline/timeline.h" #include "kernel/low_power.h" #include "kernel/pbl_malloc.h" #include "popups/alarm_popup.h" @@ -31,54 +18,64 @@ #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" #include "process_management/process_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/workout_service.h" -#include "services/normal/app_inbox_service.h" -#include "services/normal/app_outbox_service.h" -#include "services/normal/music.h" -#include "services/normal/music_endpoint.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/stationary.h" -#include "services/normal/timeline/event.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/workout_service.h" +#include "pbl/services/app_inbox_service.h" +#include "pbl/services/app_outbox_service.h" +#include "pbl/services/music.h" +#include "pbl/services/music_endpoint.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/stationary.h" +#include "pbl/services/system_task.h" +#include "pbl/services/timeline/event.h" #include "shell/normal/app_idle_timeout.h" #include "shell/normal/battery_ui.h" -#include "shell/normal/display_calibration_prompt.h" #include "shell/normal/quick_launch.h" #include "shell/normal/watchface.h" -#include "shell/normal/welcome.h" #include "shell/prefs.h" #include "system/logging.h" extern void shell_prefs_init(void); +// Force-compact every growable settings DB on first boot after upgrade. +// Pre-growable-files devices keep their original full-size settings file allocation forever otherwise, +// which on devices with many installed apps shows up as launcher scroll lag. +static void prv_settings_dbs_compaction_migration_cb(void *data) { + blob_db_compact_growable_dbs(); + shell_prefs_set_settings_dbs_compacted_v1(true); +} + +static void prv_maybe_run_settings_dbs_compaction_migration(void) { + if (shell_prefs_get_settings_dbs_compacted_v1()) { + return; + } + PBL_LOG_INFO("settings_dbs_compaction_migration: scheduling"); + system_task_add_callback(prv_settings_dbs_compaction_migration_cb, NULL); +} + void shell_event_loop_init(void) { shell_prefs_init(); -#if PLATFORM_SPALDING - shell_prefs_display_offset_init(); - display_calibration_prompt_show_if_needed(); -#endif + prv_maybe_run_settings_dbs_compaction_migration(); notification_window_service_init(); app_inbox_service_init(); app_outbox_service_init(); app_message_sender_init(); watchface_init(); timeline_peek_init(); -#if CAPABILITY_HAS_HEALTH_TRACKING // Start activity tracking if enabled if (activity_prefs_tracking_is_enabled()) { activity_start_tracking(false /*test_mode*/); } workout_service_init(); -#endif bool factory_reset_or_first_use = !shared_prf_storage_get_getting_started_complete(); // We are almost done booting, welcome the user if applicable. This _must_ occur before setting // the getting started completed below. - welcome_push_notification(factory_reset_or_first_use); if (factory_reset_or_first_use) { bt_persistent_storage_set_unfaithful(true); } @@ -95,8 +92,7 @@ void shell_event_loop_handle_event(PebbleEvent *e) { return; case PEBBLE_ALARM_CLOCK_EVENT: - analytics_inc(ANALYTICS_DEVICE_METRIC_ALARM_SOUNDED_COUNT, AnalyticsClient_System); - PBL_LOG(LOG_LEVEL_INFO, "Alarm event in the shell event loop"); + PBL_LOG_INFO("Alarm event in the shell event loop"); stationary_wake_up(); alarm_popup_push_window(&e->alarm_clock); return; @@ -158,7 +154,6 @@ void shell_event_loop_handle_event(PebbleEvent *e) { // Sent by the comm layer once we get a response from the mobile app to a phone version request case PEBBLE_REMOTE_APP_INFO_EVENT: music_endpoint_handle_mobile_app_info_event(&e->bluetooth.app_info_event); - analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_APP_INFO_COUNT, AnalyticsClient_System); return; case PEBBLE_MEDIA_EVENT: diff --git a/src/fw/shell/normal/shutdown_charging.c b/src/fw/shell/normal/shutdown_charging.c index 1040a0cd5b..ea6a67d5c1 100644 --- a/src/fw/shell/normal/shutdown_charging.c +++ b/src/fw/shell/normal/shutdown_charging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include @@ -30,8 +17,7 @@ #include "process_state/app_state/app_state.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/runlevel.h" -#include "services/common/status_led.h" +#include "pbl/services/runlevel.h" #include "system/logging.h" #include "system/passert.h" #include "system/reboot_reason.h" @@ -110,16 +96,6 @@ static void prv_battery_state_handler(BatteryChargeState charge) { } } - if (charge.is_plugged) { - if (charge.is_charging) { - status_led_set(StatusLedState_Charging); - } else { - status_led_set(StatusLedState_FullyCharged); - } - } else { - status_led_set(StatusLedState_Off); - } - data->was_plugged = charge.is_plugged; data->last_dialog_state = next_dialog_state; } diff --git a/src/fw/shell/normal/system_app_registry_list.json b/src/fw/shell/normal/system_app_registry_list.json index c2aa02fd3f..31659c3eb9 100644 --- a/src/fw/shell/normal/system_app_registry_list.json +++ b/src/fw/shell/normal/system_app_registry_list.json @@ -38,6 +38,7 @@ "silk", "asterix", "obelix", + "snowy_emery", "robert" ] }, @@ -86,11 +87,18 @@ "enum": "TIMELINE", "md_fn": "timeline_get_app_info" }, + { + "id": -96, + "enum": "TIMELINE_PAST", + "md_fn": "timeline_past_get_app_info" + }, { "id": -12, "enum": "BOUNCING_BOX_DEMO", "md_fn": "bouncing_box_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_BOUNCING_BOX_DEMO" + ], "target_platforms": [ "snowy", "robert" @@ -100,187 +108,249 @@ "id": -13, "enum": "PEBBLE_COLORS", "md_fn": "pebble_colors_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_PEBBLE_COLORS" + ] }, { "id": -14, "enum": "PEBBLE_SHAPES", "md_fn": "pebble_shapes_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_PEBBLE_SHAPES" + ] }, { "id": -17, "enum": "MOVABLE_LINE", "md_fn": "movable_line_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_MOVABLE_LINE" + ] }, { "id": -18, "enum": "GFX_TESTS", "md_fn": "gfx_tests_get_app_info", - "ifdefs": ["PERFORMANCE_TESTS"] + "ifdefs": [ + "CONFIG_DEMO_APP_GFX_TESTS" + ] }, { "id": -19, "enum": "TEST_ARGS_RECEIVER", "md_fn": "test_args_receiver_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEST_ARGS_RECEIVER" + ] }, { "id": -20, "enum": "TEST_ARGS_SENDER", "md_fn": "test_args_sender_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEST_ARGS_SENDER" + ] }, { "id": -21, "enum": "FLASH_DIAGNOSTIC", "md_fn": "flash_diagnostic_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_FLASH_DIAGNOSTIC" + ] }, { "id": -22, "enum": "FS_RESOURCES", "md_fn": "fs_resources_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_FS_RESOURCES" + ] }, { "id": -23, "enum": "EXIT", "md_fn": "exit_app_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_EXIT" + ] }, { "id": -25, "enum": "APP_HEAP_DEMO", "md_fn": "app_heap_demo_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_APP_HEAP_DEMO" + ] }, { "id": -26, "enum": "DATA_LOGGING_TEST", "md_fn": "data_logging_test_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_DATA_LOGGING_TEST" + ] }, { "id": -27, "enum": "KILL_BT", "md_fn": "kill_bt_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_KILL_BT" + ] }, { "id": -28, "enum": "TRIGGER_ALARM", "md_fn": "trigger_alarm_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TRIGGER_ALARM" + ] }, { "id": -29, "enum": "ANIMATED_DEMO", "md_fn": "animated_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] - }, - { - "id": -30, - "enum": "GRENADE_LAUNCHER", - "md_fn": "grenade_launcher_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_ANIMATED_DEMO" + ] }, { "id": -31, "enum": "TEXT_LAYOUT", "md_fn": "text_layout_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEXT_LAYOUT" + ] }, { "id": -33, "enum": "MENU", "md_fn": "menu_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_MENU" + ] }, { "id": -34, "enum": "SIMPLE_MENU", "md_fn": "simple_menu_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_SIMPLE_MENU" + ] }, { "id": -35, "enum": "SCROLL", "md_fn": "scroll_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_SCROLL" + ] }, { "id": -36, "enum": "CLICK", "md_fn": "click_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_CLICK" + ] }, { "id": -37, "enum": "PROGRESS", "md_fn": "progress_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_PROGRESS" + ] }, { "id": -38, "enum": "NUMBER_FIELD", "md_fn": "number_field_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_NUMBER_FIELD" + ] + }, + { + "id": -39, + "enum": "FW_UPDATE_PROGRESS_SIM", + "md_fn": "fw_update_progress_sim_app_get_info", + "ifdefs": [ + "CONFIG_DEMO_APP_FW_UPDATE_PROGRESS_SIM" + ] }, { "id": -41, "enum": "EVENT_SERVICE", "md_fn": "event_service_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_EVENT_SERVICE" + ] }, { "id": -43, "enum": "PERSIST", "md_fn": "persist_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_PERSIST" + ] }, { "id": -44, "enum": "TIMER", "md_fn": "timer_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TIMER" + ] }, { "id": -45, "enum": "TEST_SYS_TIMER", "md_fn": "test_sys_timer_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEST_SYS_TIMER" + ] }, { "id": -46, "enum": "TEST_CORE_DUMP", "md_fn": "test_core_dump_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEST_CORE_DUMP" + ] }, { "id": -47, "enum": "FLASH_PROF", "md_fn": "flash_prof_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_FLASH_PROF" + ] }, { "id": -48, "enum": "TEST_BLUETOOTH", "md_fn": "test_bluetooth_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEST_BLUETOOTH" + ] }, { "id": -50, "enum": "VIBE_AND_LOGS", "md_fn": "vibe_and_logs_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_VIBE_AND_LOGS" + ] }, { "id": -51, "enum": "MENU_OVERFLOW", "md_fn": "menu_overflow_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_MENU_OVERFLOW" + ] }, { "id": -54, @@ -291,19 +361,25 @@ "id": -55, "enum": "TEXT_CLIPPING", "md_fn": "text_clipping_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEXT_CLIPPING" + ] }, { "id": -56, "enum": "LIGHT_CONFIG", "md_fn": "light_config_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_LIGHT_CONFIG" + ] }, { "id": -57, "enum": "STROKE_WIDTH", "md_fn": "stroke_width_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_STROKE_WIDTH" + ], "target_platforms": [ "snowy" ] @@ -312,32 +388,21 @@ "id": -58, "enum": "AMB_LIGHT_READ", "md_fn": "ambient_light_reading_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_AMB_LIGHT_READ" + ] }, { "id": -59, "enum": "WEATHER", "md_fn": "weather_app_get_info", - "color_argb8": "GColorBlueMoonARGB8", - "target_platforms":[ - "snowy", - "spalding", - "silk", - "robert" - ] + "color_argb8": "GColorBlueMoonARGB8" }, { "id": -95, "enum": "WORKOUT", "md_fn": "workout_app_get_info", - "color_argb8": "GColorYellowARGB8", - "target_platforms": [ - "silk", - "snowy", - "spalding", - "asterix", - "obelix" - ] + "color_argb8": "GColorYellowARGB8" }, { "id": -60, @@ -353,7 +418,9 @@ "id": -61, "enum": "SWAP_LAYER", "md_fn": "swap_layer_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_SWAP_LAYER" + ] }, { "id": -62, @@ -364,85 +431,121 @@ "id": -63, "enum": "TEXT_SPACING", "md_fn": "text_spacing_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEXT_SPACING" + ] + }, + { + "id": -100, + "enum": "FONT_TEST", + "md_fn": "font_test_app_get_info", + "ifdefs": [ + "CONFIG_DEMO_APP_FONT_TEST" + ] }, { "id": -64, "enum": "KINO_LAYER", "md_fn": "kino_layer_demo_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_KINO_LAYER" + ] }, { "id": -65, "enum": "DIALOGS", "md_fn": "dialogs_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_DIALOGS" + ] }, { "id": -66, "enum": "STATUSBAR", "md_fn": "statusbar_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_STATUSBAR" + ] }, { "id": -68, "enum": "MORPH_SQUARE", "md_fn": "morph_square_demo_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_MORPH_SQUARE" + ] }, { "id": -70, "enum": "MENU_RIGHT_ICON", "md_fn": "menu_layer_right_icon_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_MENU_RIGHT_ICON" + ] }, { "id": -71, "enum": "VIBE_STRENGTH", "md_fn": "vibe_strength_demo_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_VIBE_STRENGTH" + ] }, { "id": -72, "enum": "ACTION_MENU", "md_fn": "action_menu_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_ACTION_MENU" + ] }, { "id": -73, "enum": "OPTION_MENU", "md_fn": "option_menu_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_OPTION_MENU" + ] }, { "id": -74, "enum": "PROFILE_MUTEXES", "md_fn": "profile_mutexes_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS", "DISABLED"] + "ifdefs": [ + "CONFIG_DEMO_APP_PROFILE_MUTEXES" + ] }, { "id": -75, "enum": "DEADLOCK", "md_fn": "deadlock_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_DEADLOCK" + ] }, { "id": -76, "enum": "TIMELINE_PINS", "md_fn": "timeline_pins_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TIMELINE_PINS" + ] }, { "id": -77, "enum": "TEXT_FLOW", "md_fn": "text_flow_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_TEXT_FLOW" + ] }, { "id": -78, "enum": "MENU_ROUND", "md_fn": "menu_round_app_get_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_MENU_ROUND" + ], "target_platforms": [ "spalding" ] @@ -451,19 +554,27 @@ "id": -79, "enum": "ACTIVITY_DEMO", "md_fn": "activity_demo_get_app_info", - "ifdefs": ["SHOW_ACTIVITY_DEMO"], + "ifdefs": [ + "CONFIG_DEMO_APP_ACTIVITY_DEMO" + ], "target_platforms": [ "snowy", "spalding", "silk", - "robert" + "robert", + "snowy_emery", + "obelix", + "getafix", + "spalding_gabbro" ] }, { "id": -80, "enum": "ACTIVITY_TEST", "md_fn": "activity_test_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_ACTIVITY_TEST" + ], "target_platforms": [ "snowy", "spalding", @@ -475,40 +586,28 @@ "id": -81, "enum": "DOUBLE_TAP_TEST", "md_fn": "double_tap_test_get_info", - "ifdefs": ["ENABLE_TEST_APPS"] + "ifdefs": [ + "CONFIG_DEMO_APP_DOUBLE_TAP_TEST" + ] }, { "id": -82, "enum": "HEALTH_APP", "md_fn": "health_app_get_info", - "color_argb8": "GColorSunsetOrangeARGB8", - "target_platforms": [ - "snowy", - "spalding", - "silk", - "robert", - "asterix", - "obelix" - ] + "color_argb8": "GColorSunsetOrangeARGB8" }, { "id": -83, "enum": "SEND_TEXT", - "md_fn": "send_text_app_get_info", - "target_platforms": [ - "snowy", - "spalding", - "silk", - "robert", - "asterix", - "obelix" - ] + "md_fn": "send_text_app_get_info" }, { "id": -84, "enum": "VIBE_SCORE", "md_fn": "vibe_score_demo_get_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_VIBE_SCORE" + ], "target_platforms": [ "snowy", "spalding" @@ -518,7 +617,9 @@ "id": -86, "enum": "IDL", "md_fn": "idl_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_IDL" + ], "target_platforms": [ "snowy", "spalding" @@ -528,7 +629,9 @@ "id": -87, "enum": "TEMPERATURE_DEMO", "md_fn": "temperature_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_TEMPERATURE_DEMO" + ], "target_platforms": [ "snowy" ] @@ -537,7 +640,9 @@ "id": -88, "enum": "GDRAWMASK_DEMO", "md_fn": "gdrawmask_demo_get_app_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_GDRAWMASK_DEMO" + ], "target_platforms": [ "snowy", "spalding" @@ -547,7 +652,10 @@ "id": -89, "enum": "HRM_DEMO", "md_fn": "hrm_demo_get_app_info", - "ifdefs": ["SHOW_ACTIVITY_DEMO", "CAPABILITY_HAS_BUILTIN_HRM=1"] + "ifdefs": [ + "CONFIG_DEMO_APP_HRM_DEMO", + "CONFIG_HRM" + ] }, { "id": -90, @@ -558,14 +666,21 @@ "snowy", "spalding", "silk", - "robert" + "robert", + "asterix", + "obelix", + "snowy_emery", + "getafix", + "spalding_gabbro" ] }, { "id": -91, "enum": "MPU_TEST", "md_fn": "test_mpu_cache_get_info", - "ifdefs": ["ENABLE_TEST_APPS"], + "ifdefs": [ + "CONFIG_DEMO_APP_MPU_TEST" + ], "target_platforms": [ "snowy", "spalding", @@ -573,62 +688,43 @@ "robert" ] }, + { + "id": -191, + "enum": "MPU_VIOLATION_TEST", + "md_fn": "test_mpu_violation_get_info", + "ifdefs": [ + "CONFIG_DEMO_APP_MPU_VIOLATION_TEST" + ] + }, { "id": -92, "enum": "QUIET_TIME_TOGGLE", - "md_fn": "quiet_time_toggle_get_app_info", - "target_platforms": [ - "snowy", - "spalding", - "silk", - "robert", - "asterix", - "obelix" - ] + "md_fn": "quiet_time_toggle_get_app_info" + }, + { + "id": -99, + "enum": "BACKLIGHT_STATE_TOGGLE_UUID", + "md_fn": "backlight_state_toggle_get_app_info" }, { "id": -94, "enum": "MOTION_BACKLIGHT_TOGGLE", - "md_fn": "motion_backlight_toggle_get_app_info", - "target_platforms": [ - "snowy", - "spalding", - "silk", - "robert", - "asterix", - "obelix" - ] + "md_fn": "motion_backlight_toggle_get_app_info" }, { "id": -93, "enum": "AIRPLANE_MODE_TOGGLE", - "md_fn": "airplane_mode_toggle_get_app_info", - "target_platforms": [ - "snowy", - "spalding", - "silk", - "robert", - "asterix", - "obelix" - ] - }, - { - "id": -96, - "enum": "TIMELINE_PAST", - "md_fn": "timeline_past_get_app_info", - "target_platforms": [ - "snowy", - "spalding", - "silk", - "robert", - "asterix", - "obelix" - ] + "md_fn": "airplane_mode_toggle_get_app_info" }, { "id": -97, "enum": "SPORTS", "md_fn": "sports_app_get_info" + }, + { + "id": -32, + "enum": "TIMELINE_FULL", + "md_fn": "timeline_full_get_app_info" } ], "resource_apps": [ diff --git a/src/fw/shell/normal/system_app_state_machine.c b/src/fw/shell/normal/system_app_state_machine.c index 7cd0e95d49..ead1b5f1f4 100644 --- a/src/fw/shell/normal/system_app_state_machine.c +++ b/src/fw/shell/normal/system_app_state_machine.c @@ -1,31 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/system_app_state_machine.h" -#include "apps/core_apps/panic_window_app.h" -#include "apps/system_apps/battery_critical_app.h" +#include "apps/core/panic_window.h" +#include "apps/system/battery_critical.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/launcher/launcher_app.h" -#include "apps/watch/low_power/low_power_face.h" +#include "apps/system/launcher/launcher.h" +#include "apps/watch/low_power/face.h" #include "shell/normal/watchface.h" #include "kernel/low_power.h" #include "kernel/panic.h" #include "resource/resource.h" -#include "services/common/battery/battery_monitor.h" +#include "pbl/services/battery/battery_monitor.h" #include "system/bootbits.h" #include "system/logging.h" #include "process_management/app_manager.h" diff --git a/src/fw/shell/normal/watchface.c b/src/fw/shell/normal/watchface.c index 38c7122793..51691dd37d 100644 --- a/src/fw/shell/normal/watchface.c +++ b/src/fw/shell/normal/watchface.c @@ -1,43 +1,46 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "watchface.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/launcher/launcher_app.h" -#include "apps/system_apps/settings/settings_quick_launch.h" -#include "apps/system_apps/settings/settings_quick_launch_app_menu.h" -#include "apps/system_apps/settings/settings_quick_launch_setup_menu.h" -#include "apps/system_apps/timeline/timeline.h" -#include "apps/watch/low_power/low_power_face.h" +#include "apps/system/launcher/launcher.h" +#include "apps/system/settings/quick_launch.h" +#include "apps/system/settings/quick_launch_app_menu.h" +#include "apps/system/settings/quick_launch_setup_menu.h" +#include "apps/system/timeline/timeline.h" +#include "apps/watch/low_power/face.h" #include "kernel/event_loop.h" #include "kernel/low_power.h" #include "kernel/ui/modals/modal_manager.h" #include "popups/timeline/peek.h" #include "process_management/app_manager.h" #include "process_management/pebble_process_md.h" -#include "services/common/analytics/analytics.h" -#include "services/common/compositor/compositor_transitions.h" -#include "services/normal/notifications/do_not_disturb.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/compositor/compositor_transitions.h" +#include "applib/app_timer.h" +#include "applib/app_launch_reason.h" +#include "applib/ui/click_internal.h" +#include "pbl/services/notifications/do_not_disturb.h" #include "system/logging.h" #include "system/passert.h" +#ifdef CONFIG_ORIENTATION_MANAGER +#include "shell/prefs.h" +#endif #define QUICK_LAUNCH_HOLD_MS (400) +#define BIT_SET (1) +#define BIT_CLEAR (0) +#define COMBO_BACK_UP_BUTTONS ((BIT_SET << BUTTON_ID_BACK) | (BIT_SET << BUTTON_ID_UP)) +#define COMBO_UP_DOWN_BUTTONS ((BIT_SET << BUTTON_ID_UP) | (BIT_SET << BUTTON_ID_DOWN)) +#ifdef CONFIG_ORIENTATION_MANAGER +#define COMBO_BACK_UP_FLIPPED_BUTTONS ((BIT_SET << BUTTON_ID_BACK) | (BIT_SET << BUTTON_ID_DOWN)) +#endif static ClickManager s_click_manager; +static uint8_t s_buttons_pressed = BIT_CLEAR; +static AppTimer *s_combo_back_hold_timer = NULL; +static uint8_t s_active_combo_buttons = BIT_CLEAR; static bool prv_should_ignore_button_click(void) { if (app_manager_get_task_context()->closing_state != ProcessRunState_Running) { @@ -57,39 +60,160 @@ static void prv_launch_app_via_button(AppLaunchEventConfig *config, app_manager_put_launch_app_event(config); } -static void prv_quick_launch_handler(ClickRecognizerRef recognizer, void *data) { - ButtonId button = click_recognizer_get_button_id(recognizer); - if (!quick_launch_is_enabled(button)) { +static bool prv_is_combo_pressed(uint8_t combo_buttons) { + return (s_buttons_pressed & combo_buttons) == combo_buttons; +} + +static bool prv_combo_is_enabled(uint8_t combo_buttons) { +#ifdef CONFIG_ORIENTATION_MANAGER + const bool orientation_flipped = display_orientation_is_left(); + if (orientation_flipped && combo_buttons == COMBO_BACK_UP_FLIPPED_BUTTONS) { + return quick_launch_combo_back_up_is_enabled(); + } else if (!orientation_flipped && combo_buttons == COMBO_BACK_UP_BUTTONS) { + return quick_launch_combo_back_up_is_enabled(); + } else if (combo_buttons == COMBO_UP_DOWN_BUTTONS) { + return quick_launch_combo_up_down_is_enabled(); + } +#else + if (combo_buttons == COMBO_BACK_UP_BUTTONS) { + return quick_launch_combo_back_up_is_enabled(); + } else if (combo_buttons == COMBO_UP_DOWN_BUTTONS) { + return quick_launch_combo_up_down_is_enabled(); + } +#endif + return false; +} + +static AppInstallId prv_combo_get_app(uint8_t combo_buttons) { +#ifdef CONFIG_ORIENTATION_MANAGER + if (combo_buttons == COMBO_BACK_UP_FLIPPED_BUTTONS) { + return quick_launch_combo_back_up_get_app(); + } else if (combo_buttons == COMBO_BACK_UP_BUTTONS) { + return quick_launch_combo_back_up_get_app(); + } else if (combo_buttons == COMBO_UP_DOWN_BUTTONS) { + return quick_launch_combo_up_down_get_app(); + } +#else + if (combo_buttons == COMBO_BACK_UP_BUTTONS) { + return quick_launch_combo_back_up_get_app(); + } else if (combo_buttons == COMBO_UP_DOWN_BUTTONS) { + return quick_launch_combo_up_down_get_app(); + } +#endif + return INSTALL_ID_INVALID; +} + +static bool prv_is_any_combo_active(void) { + return (s_combo_back_hold_timer != NULL) || + prv_is_combo_pressed(COMBO_BACK_UP_BUTTONS) || + prv_is_combo_pressed(COMBO_UP_DOWN_BUTTONS); +} + +static void prv_combo_back_timer_callback(void *data) { + s_combo_back_hold_timer = NULL; + if (!prv_is_combo_pressed(s_active_combo_buttons)) { + s_active_combo_buttons = BIT_CLEAR; return; } - AppInstallId app_id = quick_launch_get_app(button); - if (app_id == INSTALL_ID_INVALID) { - app_id = app_install_get_id_for_uuid(&quick_launch_setup_get_app_info()->uuid); + + if (!prv_combo_is_enabled(s_active_combo_buttons)) { + s_active_combo_buttons = BIT_CLEAR; + return; + } + + AppInstallId app_id = prv_combo_get_app(s_active_combo_buttons); + const ButtonId source_button = + (s_active_combo_buttons == COMBO_BACK_UP_BUTTONS) ? BUTTON_ID_BACK : BUTTON_ID_UP; + s_active_combo_buttons = BIT_CLEAR; + if (app_id != INSTALL_ID_INVALID) { + // Reset all button states before launching app to prevent state corruption. + s_buttons_pressed = BIT_CLEAR; + app_manager_put_launch_app_event(&(AppLaunchEventConfig) { + .id = app_id, + .common.reason = APP_LAUNCH_QUICK_LAUNCH, + .common.button = source_button, + .common.args = (void*)APP_QUICK_LAUNCH_ACTION_COMBO, + }); } - prv_launch_app_via_button(&(AppLaunchEventConfig) { - .id = app_id, - .common.reason = APP_LAUNCH_QUICK_LAUNCH, - }, recognizer); } -static void prv_launch_timeline(ClickRecognizerRef recognizer, void *data) { - static TimelineArgs s_timeline_args; - const bool is_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP); - if (is_up) { - PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in past mode."); - s_timeline_args.direction = TimelineIterDirectionPast; - analytics_inc(ANALYTICS_DEVICE_METRIC_TIMELINE_PAST_LAUNCH_COUNT, AnalyticsClient_System); +static void prv_check_combo_back_hold(void) { + uint8_t combo_buttons = BIT_CLEAR; + +#ifdef CONFIG_ORIENTATION_MANAGER + const bool orientation_flipped = display_orientation_is_left(); + if (orientation_flipped && prv_is_combo_pressed(COMBO_BACK_UP_FLIPPED_BUTTONS)) { + combo_buttons = COMBO_BACK_UP_FLIPPED_BUTTONS; + } else if (!orientation_flipped && prv_is_combo_pressed(COMBO_BACK_UP_BUTTONS)) { + combo_buttons = COMBO_BACK_UP_BUTTONS; + } else if (prv_is_combo_pressed(COMBO_UP_DOWN_BUTTONS)) { + combo_buttons = COMBO_UP_DOWN_BUTTONS; + } +#else + if (prv_is_combo_pressed(COMBO_BACK_UP_BUTTONS)) { + combo_buttons = COMBO_BACK_UP_BUTTONS; + } else if (prv_is_combo_pressed(COMBO_UP_DOWN_BUTTONS)) { + combo_buttons = COMBO_UP_DOWN_BUTTONS; + } +#endif + + if (combo_buttons != BIT_CLEAR) { + if (s_combo_back_hold_timer == NULL) { + s_active_combo_buttons = combo_buttons; + // Cancel individual button timers to prevent them from firing. + // This ensures only the combo executes, not individual hold handlers. +#ifdef CONFIG_ORIENTATION_MANAGER + if (combo_buttons == COMBO_BACK_UP_FLIPPED_BUTTONS) { + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_BACK]); + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_DOWN]); + } else if (combo_buttons == COMBO_BACK_UP_BUTTONS) { + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_BACK]); + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_UP]); + } else { + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_UP]); + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_DOWN]); + } +#else + if (combo_buttons == COMBO_BACK_UP_BUTTONS) { + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_BACK]); + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_UP]); + } else { + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_UP]); + click_recognizer_reset(&s_click_manager.recognizers[BUTTON_ID_DOWN]); + } +#endif + s_combo_back_hold_timer = + app_timer_register(QUICK_LAUNCH_HOLD_MS, prv_combo_back_timer_callback, NULL); + } } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in future mode."); - s_timeline_args.direction = TimelineIterDirectionFuture; - analytics_inc(ANALYTICS_DEVICE_METRIC_TIMELINE_FUTURE_LAUNCH_COUNT, AnalyticsClient_System); + if (s_combo_back_hold_timer != NULL) { + app_timer_cancel(s_combo_back_hold_timer); + s_combo_back_hold_timer = NULL; + s_active_combo_buttons = BIT_CLEAR; + } } +} + +static void prv_launch_timeline_app(AppInstallId app_id, ClickRecognizerRef recognizer, + AppLaunchReason reason) { + static TimelineArgs s_timeline_args; s_timeline_args.launch_into_pin = true; s_timeline_args.stay_in_list_view = true; timeline_peek_get_item_id(&s_timeline_args.pin_id); + const ButtonId button = click_recognizer_get_button_id(recognizer); const CompositorTransition *animation = NULL; - const bool is_future = (s_timeline_args.direction == TimelineIterDirectionFuture); + const bool is_up = (button == BUTTON_ID_UP); + const bool is_future = (app_id == APP_ID_TIMELINE) || (app_id == APP_ID_TIMELINE_FULL && !is_up); + + if (app_id == APP_ID_TIMELINE) { + s_timeline_args.direction = TimelineIterDirectionFuture; + } else if (app_id == APP_ID_TIMELINE_PAST) { + s_timeline_args.direction = TimelineIterDirectionPast; + } else { + s_timeline_args.direction = is_future ? TimelineIterDirectionFuture : TimelineIterDirectionPast; + } + const bool timeline_is_destination = true; #if PBL_ROUND animation = compositor_dot_transition_timeline_get(is_future, timeline_is_destination); @@ -100,12 +224,61 @@ static void prv_launch_timeline(ClickRecognizerRef recognizer, void *data) { timeline_peek_is_future_empty()); #endif prv_launch_app_via_button(&(AppLaunchEventConfig) { - .id = APP_ID_TIMELINE, + .id = app_id, + .common.reason = reason, .common.args = &s_timeline_args, .common.transition = animation, }, recognizer); } +static void prv_launch_quick_launch_app(AppInstallId app_id, ClickRecognizerRef recognizer, + AppLaunchReason timeline_reason, void *non_timeline_args) { + const bool is_timeline = (app_id == APP_ID_TIMELINE) || + (app_id == APP_ID_TIMELINE_PAST) || + (app_id == APP_ID_TIMELINE_FULL); + if (is_timeline) { + prv_launch_timeline_app(app_id, recognizer, timeline_reason); + } else { + prv_launch_app_via_button(&(AppLaunchEventConfig) { + .id = app_id, + .common.reason = APP_LAUNCH_QUICK_LAUNCH, + .common.args = non_timeline_args, + }, recognizer); + } +} + +static void prv_quick_launch_handler(ClickRecognizerRef recognizer, void *data) { + ButtonId button = click_recognizer_get_button_id(recognizer); + + if (prv_is_any_combo_active()) { + return; + } + + AppInstallId app_id = quick_launch_is_enabled(button) ? quick_launch_get_app(button) + : INSTALL_ID_INVALID; + if (app_id == INSTALL_ID_INVALID) { + app_id = app_install_get_id_for_uuid(&quick_launch_setup_get_app_info()->uuid); + } + s_buttons_pressed = BIT_CLEAR; // Reset our own tracking + + prv_launch_quick_launch_app(app_id, recognizer, APP_LAUNCH_QUICK_LAUNCH, + (void*)APP_QUICK_LAUNCH_ACTION_HOLD); +} + +static void prv_launch_up_down(ClickRecognizerRef recognizer, void *data) { + ButtonId button = click_recognizer_get_button_id(recognizer); + + if (prv_is_any_combo_active()) { + return; + } + + if (!quick_launch_single_click_is_enabled(button)) return; + const AppInstallId app_id = quick_launch_single_click_get_app(button); + + prv_launch_quick_launch_app(app_id, recognizer, APP_LAUNCH_SYSTEM, + (void*)APP_QUICK_LAUNCH_ACTION_TAP); +} + static void prv_configure_click_handler(ButtonId button_id, ClickHandler single_click_handler) { ClickConfig *cfg = &s_click_manager.recognizers[button_id].config; cfg->long_click.delay_ms = QUICK_LAUNCH_HOLD_MS; @@ -121,29 +294,16 @@ static void prv_launch_launcher_app(ClickRecognizerRef recognizer, void *data) { }, recognizer); } -#if CAPABILITY_HAS_CORE_NAVIGATION4 -static void prv_launch_health_app(ClickRecognizerRef recognizer, void *data) { - prv_launch_app_via_button(&(AppLaunchEventConfig) { - .id = APP_ID_HEALTH_APP, - }, recognizer); -} -#endif // CAPABILITY_HAS_CORE_NAVIGATION4 - -static ClickHandler prv_get_up_click_handler(void) { -#if CAPABILITY_HAS_CORE_NAVIGATION4 - return prv_launch_health_app; -#else - return prv_launch_timeline; -#endif // CAPABILITY_HAS_CORE_NAVIGATION4 -} - static void prv_dismiss_timeline_peek(ClickRecognizerRef recognizer, void *data) { + if (prv_is_any_combo_active()) { + return; + } timeline_peek_dismiss(); } static void prv_watchface_configure_click_handlers(void) { - prv_configure_click_handler(BUTTON_ID_UP, prv_get_up_click_handler()); - prv_configure_click_handler(BUTTON_ID_DOWN, prv_launch_timeline); + prv_configure_click_handler(BUTTON_ID_UP, prv_launch_up_down); + prv_configure_click_handler(BUTTON_ID_DOWN, prv_launch_up_down); prv_configure_click_handler(BUTTON_ID_SELECT, prv_launch_launcher_app); prv_configure_click_handler(BUTTON_ID_BACK, prv_dismiss_timeline_peek); } @@ -159,9 +319,13 @@ void watchface_handle_button_event(PebbleEvent *e) { } switch (e->type) { case PEBBLE_BUTTON_DOWN_EVENT: + s_buttons_pressed |= (BIT_SET << e->button.button_id); click_recognizer_handle_button_down(&s_click_manager.recognizers[e->button.button_id]); + prv_check_combo_back_hold(); break; case PEBBLE_BUTTON_UP_EVENT: + s_buttons_pressed &= ~(BIT_SET << e->button.button_id); + prv_check_combo_back_hold(); click_recognizer_handle_button_up(&s_click_manager.recognizers[e->button.button_id]); break; default: @@ -171,7 +335,7 @@ void watchface_handle_button_event(PebbleEvent *e) { } static void prv_watchface_launch_low_power(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "Switching default watchface to low_power_mode watchface"); + PBL_LOG_DBG("Switching default watchface to low_power_mode watchface"); app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = APP_ID_LOW_POWER_FACE, }); diff --git a/src/fw/shell/normal/watchface.h b/src/fw/shell/normal/watchface.h index 6282e19f14..c8fadc2f4b 100644 --- a/src/fw/shell/normal/watchface.h +++ b/src/fw/shell/normal/watchface.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" #include "process_management/app_install_manager.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" void watchface_init(void); diff --git a/src/fw/shell/normal/welcome.c b/src/fw/shell/normal/welcome.c deleted file mode 100644 index aaa878e9af..0000000000 --- a/src/fw/shell/normal/welcome.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "welcome.h" - -#include "kernel/event_loop.h" -#include "resource/timeline_resource_ids.auto.h" -#include "services/common/evented_timer.h" -#include "services/common/i18n/i18n.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "shell/prefs.h" -#include "system/logging.h" -#include "util/attributes.h" -#include "util/size.h" - -static void prv_push_welcome_notification(void *PBL_UNUSED data) { - AttributeList notif_attr_list = {}; - attribute_list_add_uint32(¬if_attr_list, AttributeIdIconTiny, - TIMELINE_RESOURCE_NOTIFICATION_FLAG); - attribute_list_add_cstring(¬if_attr_list, AttributeIdTitle, - /// Welcome title text welcoming a 3.x user to 4.x - i18n_get("Pebble Updated!", ¬if_attr_list)); - /// Welcome body text welcoming a 3.x user to 4.x. - const char *welcome_text = i18n_get( - "For activity and sleep tracking, press up from your watch face.\n\n" - "Press down for current and future events.\n\n" - "Read more at blog.pebble.com", - ¬if_attr_list); - attribute_list_add_cstring(¬if_attr_list, AttributeIdBody, welcome_text); - attribute_list_add_uint8(¬if_attr_list, AttributeIdBgColor, GColorOrangeARGB8); - - AttributeList dismiss_action_attr_list = {}; - attribute_list_add_cstring(&dismiss_action_attr_list, AttributeIdTitle, - i18n_get("Dismiss", ¬if_attr_list)); - - int action_id = 0; - TimelineItemAction actions[] = { - { - .id = action_id++, - .type = TimelineItemActionTypeDismiss, - .attr_list = dismiss_action_attr_list, - }, - }; - TimelineItemActionGroup action_group = { - .num_actions = ARRAY_LENGTH(actions), - .actions = actions, - }; - - const time_t now = rtc_get_time(); - TimelineItem *item = timeline_item_create_with_attributes( - now, 0, TimelineItemTypeNotification, LayoutIdNotification, ¬if_attr_list, &action_group); - i18n_free_all(¬if_attr_list); - attribute_list_destroy_list(¬if_attr_list); - attribute_list_destroy_list(&dismiss_action_attr_list); - - if (!item) { - PBL_LOG(LOG_LEVEL_WARNING, "Failed to welcome the user."); - return; - } - - item->header.from_watch = true; - notifications_add_notification(item); - timeline_item_destroy(item); - welcome_set_welcome_version(WelcomeVersionCurrent); -} - -void welcome_push_notification(bool factory_reset_or_first_use) { - const WelcomeVersion version = welcome_get_welcome_version(); - // This check only works if it is called before getting started complete is set - if (!factory_reset_or_first_use && (version < WelcomeVersion_4xNormalFirmware)) { - // This has completed getting started on a previous normal firmware, welcome them if the - // version is before 4.x - // We wait some time since notification storage takes time to initialize - launcher_task_add_callback(prv_push_welcome_notification, NULL); - } -} diff --git a/src/fw/shell/normal/welcome.h b/src/fw/shell/normal/welcome.h deleted file mode 100644 index 0b08460332..0000000000 --- a/src/fw/shell/normal/welcome.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Version of the welcoming of the user to the normal firmware -typedef enum WelcomeVersion { - //! Initial version or never launched normal firmware - WelcomeVersion_InitialVersion = 0, - //! 4.x Normal Firmware - WelcomeVersion_4xNormalFirmware = 1, - - WelcomeVersionCount, - //! WelcomeVersion is an increasing version number. WelcomeVersionCurrent must - //! not decrement. This should ensure that the current version is always the latest. - WelcomeVersionCurrent = WelcomeVersionCount - 1, -} WelcomeVersion; - -//! Welcomes the user to a newer normal firmware they have not used yet if they have used an older -//! normal firmware and the newer normal firmware warrants a notification. -//! @note This must be called before getting started completed is set in shared prf storage. -void welcome_push_notification(bool factory_reset_or_first_use); - -//! Set the welcome version. This is persisted in shell prefs. -void welcome_set_welcome_version(uint8_t version); - -//! Get the welcome version -uint8_t welcome_get_welcome_version(void); diff --git a/src/fw/shell/prefs.h b/src/fw/shell/prefs.h index ad61500df7..8abd6c11ac 100644 --- a/src/fw/shell/prefs.h +++ b/src/fw/shell/prefs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -32,17 +19,29 @@ #include "shell/system_theme.h" #include "util/uuid.h" +#if defined(CONFIG_APP_SCALING) && \ + (defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_QEMU_EMERY)) +#define TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED 1 +#else +#define TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED 0 +#endif -// The clock 12h/24h setting is required by services/common/clock.c. + +// The clock 12h/24h setting is required by services/clock.c. bool shell_prefs_get_clock_24h_style(void); void shell_prefs_set_clock_24h_style(bool is24h); -// The timezone source setting is required by services/common/clock.c. +// The timezone source setting is required by services/clock.c. // When the source is manual, we don't override our timezone with the phone's timezone info bool shell_prefs_is_timezone_source_manual(void); void shell_prefs_set_timezone_source_manual(bool manual); -// The timezone id setting is required by services/common/clock.c. +// The time source setting is required by services/clock.c. +// When the source is manual, we don't override our time with the phone's time info +bool shell_prefs_is_time_source_manual(void); +void shell_prefs_set_time_source_manual(bool manual); + +// The timezone id setting is required by services/clock.c. // The automatic timezone id is what we get from the phone int16_t shell_prefs_get_automatic_timezone_id(void); void shell_prefs_set_automatic_timezone_id(int16_t timezone_id); @@ -80,15 +79,57 @@ void backlight_set_ambient_sensor_enabled(bool enabled); uint32_t backlight_get_timeout_ms(void); void backlight_set_timeout_ms(uint32_t timeout_ms); -uint16_t backlight_get_intensity(void); +uint8_t backlight_get_intensity(void); + +void backlight_set_intensity(uint8_t intensity); -uint8_t backlight_get_intensity_percent(void); -void backlight_set_intensity_percent(uint8_t intensity_percent); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +// Packed 0x00RRGGBB. Matches the BACKLIGHT_COLOR_* constants in drivers/backlight.h. +uint32_t backlight_get_default_color(void); +void backlight_set_default_color(uint32_t rgb_color); +#endif // The backlight motion enabled setting is used by the kernel event loop. bool backlight_is_motion_enabled(void); void backlight_set_motion_enabled(bool enable); +// The backlight touch wake setting is used by the kernel event loop to decide +// whether touch gestures wake the backlight, and if so which gesture (single +// tap or double tap). +typedef enum BacklightTouchWake { + BacklightTouchWake_DoubleTap = 0, + BacklightTouchWake_Tap = 1, + BacklightTouchWake_Off = 2, +} BacklightTouchWake; + +BacklightTouchWake backlight_get_touch_wake(void); +void backlight_set_touch_wake(BacklightTouchWake wake); + +// Global touch input kill-switch. When false, the kernel touch service +// drops events at the source, powers the sensor down, and the applib +// touch_service_is_enabled() query returns false to apps. +bool touch_is_globally_enabled(void); +void touch_set_globally_enabled(bool enable); + +#ifdef CONFIG_DYNAMIC_BACKLIGHT +// Dynamic backlight intensity based on ambient light sensor +bool backlight_is_dynamic_intensity_enabled(void); +void backlight_set_dynamic_intensity_enabled(bool enable); + +// Dynamic backlight thresholds (for debug menu) +uint32_t backlight_get_dynamic_min_threshold(void); +void backlight_set_dynamic_min_threshold(uint32_t threshold); +#endif + +// Motion sensitivity for accelerometer shake detection (0-100, lower = less sensitive) +// Only available on platforms with LSM6DSO (Asterix, Obelix) +uint8_t shell_prefs_get_motion_sensitivity(void); +void shell_prefs_set_motion_sensitivity(uint8_t sensitivity); + +// The backlight ambient light threshold setting +uint32_t backlight_get_ambient_threshold(void); +void backlight_set_ambient_threshold(uint32_t threshold); + // Stationary mode will put the watch in a low power state. Disabling will // prevent the watch from turning off any features. bool shell_prefs_get_stationary_enabled(void); @@ -102,12 +143,22 @@ bool shell_prefs_get_language_english(void); void shell_prefs_set_language_english(bool english); void shell_prefs_toggle_language_english(void); -// Manage display offset -void shell_prefs_set_display_offset(GPoint offset); -GPoint shell_prefs_get_display_offset(void); -void shell_prefs_display_offset_init(void); -bool shell_prefs_should_prompt_display_calibration(void); -void shell_prefs_set_should_prompt_display_calibration(bool should_prompt); +typedef enum ShellLanguage { + ShellLanguageInstalledPack = 0, + ShellLanguageEnglish, + ShellLanguageCatalan, + ShellLanguageGerman, + ShellLanguageSpanish, + ShellLanguageFrench, + ShellLanguageItalian, + ShellLanguageDutch, + ShellLanguagePortuguese, + ShellLanguageCount, +} ShellLanguage; + +ShellLanguage shell_prefs_get_language(void); +uint32_t shell_prefs_get_language_resource_id(void); +void shell_prefs_set_language(ShellLanguage language); uint8_t timeline_prefs_get_settings_opened(void); void timeline_prefs_set_settings_opened(uint8_t version); @@ -115,6 +166,81 @@ void timeline_peek_prefs_set_enabled(bool enabled); bool timeline_peek_prefs_get_enabled(void); void timeline_peek_prefs_set_before_time(uint16_t before_time_m); uint16_t timeline_peek_prefs_get_before_time(void); +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +typedef enum TimelinePeekUnsupportedFaceMode { + TimelinePeekUnsupportedFaceMode_None = 0, + TimelinePeekUnsupportedFaceMode_SquishUp = 1, + TimelinePeekUnsupportedFaceMode_ShiftUp = 2, + TimelinePeekUnsupportedFaceModeCount, +} TimelinePeekUnsupportedFaceMode; + +void timeline_peek_prefs_set_unsupported_face_mode(TimelinePeekUnsupportedFaceMode mode); +TimelinePeekUnsupportedFaceMode timeline_peek_prefs_get_unsupported_face_mode(void); +#endif + +typedef enum PowerMode { + PowerMode_HighPerformance = 0, + PowerMode_LowPower = 1, + PowerModeCount +} PowerMode; + +PowerMode shell_prefs_get_power_mode(void); +void shell_prefs_set_power_mode(PowerMode mode); bool shell_prefs_can_coredump_on_request(void); void shell_prefs_set_coredump_on_request(bool enabled); + +// When enabled, accel shake detection logs are emitted at INFO level instead of DEBUG. +bool shell_prefs_get_accel_shake_log_info_enabled(void); +void shell_prefs_set_accel_shake_log_info_enabled(bool enabled); + +// When enabled, vibration pattern logs are emitted at INFO level instead of DEBUG. +bool shell_prefs_get_vibe_log_info_enabled(void); +void shell_prefs_set_vibe_log_info_enabled(bool enabled); + +// One-time migration flag: set to true once we have force-compacted every +// growable settings DB at boot. Devices that pre-date the growable-files +// change still have full-size settings files and only shrink to fit after +// this migration runs. +bool shell_prefs_get_settings_dbs_compacted_v1(void); +void shell_prefs_set_settings_dbs_compacted_v1(bool done); + +#ifdef CONFIG_APP_SCALING +// Legacy app rendering mode - whether to use bezel or scaling for legacy apps +typedef enum LegacyAppRenderMode { + LegacyAppRenderMode_Bezel = 0, // Center with black bezel (original behavior) + LegacyAppRenderMode_ScalingNearest = 1, // Scale to fill screen (nearest-neighbor) + LegacyAppRenderMode_ScalingBilinear = 2, // Scale to fill screen (bilinear) + LegacyAppRenderModeCount +} LegacyAppRenderMode; + +LegacyAppRenderMode shell_prefs_get_legacy_app_render_mode(void); +void shell_prefs_set_legacy_app_render_mode(LegacyAppRenderMode mode); +#endif + +#ifdef CONFIG_ORIENTATION_MANAGER +bool display_orientation_is_left(void); +void display_orientation_set_left(bool left); +#endif + +GColor shell_prefs_get_theme_highlight_color(void); +void shell_prefs_set_theme_highlight_color(GColor color); + +bool shell_prefs_get_menu_scroll_wrap_around_enable(void); +void shell_prefs_set_menu_scroll_wrap_around_enable(bool enable); + +typedef enum MenuScrollVibeBehavior { + MenuScrollNoVibe, + MenuScrollVibeOnWrapAround, + MenuScrollVibeOnLocked, + MenuScrollVibeBehaviorsCount, +} MenuScrollVibeBehavior; + +MenuScrollVibeBehavior shell_prefs_get_menu_scroll_vibe_behavior(void); +void shell_prefs_set_menu_scroll_vibe_behavior(MenuScrollVibeBehavior behavior); + +bool shell_prefs_get_music_show_volume_controls(void); +void shell_prefs_set_music_show_volume_controls(bool enable); + +bool shell_prefs_get_music_show_progress_bar(void); +void shell_prefs_set_music_show_progress_bar(bool enable); diff --git a/src/fw/shell/prefs_private.h b/src/fw/shell/prefs_private.h index 089813f6ff..7d4b55ff7b 100644 --- a/src/fw/shell/prefs_private.h +++ b/src/fw/shell/prefs_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -50,6 +37,12 @@ int prefs_private_get_backing_len(const uint8_t *key, size_t key_len); //! @return true on success, false if failure bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, int value_len); +//! Lock the prefs mutex. Must be paired with prefs_private_unlock(). +void prefs_private_lock(void); + +//! Unlock the prefs mutex. Must be paired with prefs_private_lock(). +void prefs_private_unlock(void); + //! Process a blobDB event issued for the prefs DB (BlobDBIdPrefs). For BlobDBEventTypeInsert //! events, this method will update the internal global copy of that preference based on the //! new value that was placed into the backing store. diff --git a/src/fw/shell/prefs_syscalls.c b/src/fw/shell/prefs_syscalls.c index 167cde0570..10310154f5 100644 --- a/src/fw/shell/prefs_syscalls.c +++ b/src/fw/shell/prefs_syscalls.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "prefs_syscalls.h" diff --git a/src/fw/shell/prefs_syscalls.h b/src/fw/shell/prefs_syscalls.h index 07deca2bc3..5de679118b 100644 --- a/src/fw/shell/prefs_syscalls.h +++ b/src/fw/shell/prefs_syscalls.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/shell/prf/shell.c b/src/fw/shell/prf/shell.c index a1df91bbe0..91dbee1f65 100644 --- a/src/fw/shell/prf/shell.c +++ b/src/fw/shell/prf/shell.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/shell.h" diff --git a/src/fw/shell/prf/shell_event_loop.c b/src/fw/shell/prf/shell_event_loop.c index 1ccbd0f41c..045662bb0a 100644 --- a/src/fw/shell/prf/shell_event_loop.c +++ b/src/fw/shell/prf/shell_event_loop.c @@ -1,71 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/shell_event_loop.h" -#include - #include "popups/bluetooth_pairing_ui.h" -#include "services/common/analytics/analytics.h" -#include "services/prf/idle_watchdog.h" - -static bool s_paused_reconnect_because_repairing = false; - -static void prv_pause_reconnect_if_needed(void) { - // See https://pebbletechnology.atlassian.net/browse/PBL-13231 - // iOS has a really annoying bug that causes it to automatically start pairing if it has no - // pairing yet, but it does not present the confirmation UI, unless the user is in Bluetooth - // Settings, OR, if the user has tapped the device from the EAAccessory device picker. - // However, chances are neither are the case... When this happens, a pairing UI will show up - // on Pebble, but nothing will show up on the iOS end. - // This situation will occur if the user got into PRF and forgets the pairing in iOS (or the - // other way around. Unfortunately, when PRF initiates the reconnection, there is no way to know - // whether iOS still has the pairing (the user might have removed it). When a pairing event is - // received, Pebble can also not know whether the confirmation UI is showing on iOS. However, it - // probably means the other side forgot the previous pairing, so make Pebble stop - // auto-reconnecting until reboot, so that the number of times the bug is hit is at least limited - // to one time... :(((( - if (!s_paused_reconnect_because_repairing) { - bt_driver_reconnect_pause(); - s_paused_reconnect_because_repairing = true; - } -} - -static void prv_resume_reconnect_if_needed(void) { -if (s_paused_reconnect_because_repairing) { - bt_driver_reconnect_resume(); - s_paused_reconnect_because_repairing = false; - } -} +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/idle_watchdog.h" void shell_event_loop_init(void) { -#ifndef MANUFACTURING_FW + prf_idle_watchdog_init(); prf_idle_watchdog_start(); -#endif } void shell_event_loop_handle_event(PebbleEvent *e) { switch(e->type) { case PEBBLE_BT_PAIRING_EVENT: - if (e->bluetooth.pair.type == PebbleBluetoothPairEventTypePairingComplete) { - prv_resume_reconnect_if_needed(); - } else { - prv_pause_reconnect_if_needed(); - } bluetooth_pairing_ui_handle_event(&e->bluetooth.pair); return; diff --git a/src/fw/shell/prf/stubs.c b/src/fw/shell/prf/stubs.c index 44e6a643b2..7b69b34e29 100644 --- a/src/fw/shell/prf/stubs.c +++ b/src/fw/shell/prf/stubs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file stubs.c //! @@ -21,18 +8,20 @@ //! use something that only exists in normal, but we're not quite there yet. #include "util/uuid.h" +#include "board/board.h" #include "drivers/backlight.h" #include "kernel/events.h" #include "popups/crashed_ui.h" #include "popups/notifications/notification_window.h" #include "process_management/app_install_manager.h" #include "process_management/pebble_process_md.h" +#include "resource/resource_ids.auto.h" #include "resource/resource_storage.h" #include "resource/resource_storage_file.h" -#include "services/common/light.h" -#include "services/normal/notifications/do_not_disturb.h" -#include "services/normal/notifications/alerts_private.h" -#include "services/normal/persist.h" +#include "pbl/services/light.h" +#include "pbl/services/notifications/do_not_disturb.h" +#include "pbl/services/notifications/alerts_private.h" +#include "pbl/services/persist.h" #include "shell/prefs.h" void app_fetch_binaries(const Uuid *uuid, AppInstallId app_id, bool has_worker) { @@ -48,12 +37,6 @@ void crashed_ui_show_worker_crash(AppInstallId install_id) { void crashed_ui_show_forced_core_dump(void) { } -void crashed_ui_show_pebble_reset(void) { -} - -void crashed_ui_show_bluetooth_stuck(void) { -} - void app_idle_timeout_stop(void) { } @@ -102,11 +85,11 @@ bool phone_call_is_using_ANCS(void) { return true; } -#include "services/normal/notifications/alerts.h" +#include "pbl/services/notifications/alerts.h" -#include "services/normal/blob_db/app_db.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/pin_db.h" +#include "pbl/services/blob_db/app_db.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/pin_db.h" status_t pin_db_delete_with_parent(const TimelineItemId *parent_id) { return E_INVALID_OPERATION; @@ -160,7 +143,7 @@ void * process_loader_load(const PebbleProcessMd *app_md, PebbleTask task, return app_md->main_func; } -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/process_management/app_storage.h" AppStorageGetAppInfoResult app_storage_get_process_info(PebbleProcessInfo* app_info, uint8_t *build_id_out, AppInstallId app_id, @@ -224,6 +207,20 @@ bool backlight_is_motion_enabled(void) { return false; } +BacklightTouchWake backlight_get_touch_wake(void) { + return BacklightTouchWake_Off; +} + +void backlight_set_touch_wake(BacklightTouchWake wake) { +} + +bool touch_is_globally_enabled(void) { + return true; +} + +void touch_set_globally_enabled(bool enable) { +} + bool bt_persistent_storage_get_airplane_mode_enabled(void) { return false; } @@ -235,13 +232,19 @@ uint32_t backlight_get_timeout_ms(void) { return DEFAULT_BACKLIGHT_TIMEOUT_MS; } -uint16_t backlight_get_intensity(void) { - return BACKLIGHT_BRIGHTNESS_MAX; +uint8_t backlight_get_intensity(void) { + return 100U; } -uint8_t backlight_get_intensity_percent(void) { - return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX; + +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +uint32_t backlight_get_default_color(void) { + return BOARD_CONFIG.backlight_default_color; } +void backlight_set_default_color(uint32_t rgb_color) { +} +#endif + bool shell_prefs_get_language_english(void) { return true; } @@ -249,17 +252,17 @@ void shell_prefs_set_language_english(bool english) { } void shell_prefs_toggle_language_english(void) { } - -FontInfo *fonts_get_system_emoji_font_for_size(unsigned int font_height) { - return NULL; +ShellLanguage shell_prefs_get_language(void) { + return ShellLanguageEnglish; } - -void analytics_event_app_crash(const Uuid *uuid, uint32_t pc, uint32_t lr, - const uint8_t *build_id, bool is_rocky_app) { +uint32_t shell_prefs_get_language_resource_id(void) { + return RESOURCE_ID_INVALID; +} +void shell_prefs_set_language(ShellLanguage language) { } -void analytics_event_bt_chip_boot(uint8_t build_id[BUILD_ID_EXPECTED_LEN], - uint32_t crash_lr, uint32_t reboot_reason_code) { +FontInfo *fonts_get_system_emoji_font_for_size(unsigned int font_height) { + return NULL; } int16_t timeline_peek_get_origin_y(void) { @@ -273,3 +276,9 @@ int16_t timeline_peek_get_obstruction_origin_y(void) { void timeline_peek_handle_process_start(void) { } void timeline_peek_handle_process_kill(void) { } + +void pbl_analytics_external_collect_pfs_stats(void) { +} + +void pbl_analytics_external_collect_settings(void) { +} diff --git a/src/fw/shell/prf/system_app_state_machine.c b/src/fw/shell/prf/system_app_state_machine.c index 07da1857de..5a35041cf7 100644 --- a/src/fw/shell/prf/system_app_state_machine.c +++ b/src/fw/shell/prf/system_app_state_machine.c @@ -1,37 +1,22 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/system_app_state_machine.h" #include "apps/system_app_ids.h" -#include "apps/core_apps/panic_window_app.h" -#include "apps/prf_apps/mfg_menu_app.h" -#include "apps/prf_apps/recovery_first_use_app/recovery_first_use_app.h" +#include "apps/core/panic_window.h" +#include "apps/prf/mfg_menu.h" +#include "apps/prf/recovery_first_use/recovery_first_use.h" #include "kernel/panic.h" #include "mfg/mfg_mode/mfg_factory_mode.h" #include "process_management/app_manager.h" -#include "services/prf/accessory/accessory_imaging.h" const PebbleProcessMd* system_app_state_machine_system_start(void) { if (launcher_panic_get_current_error() != 0) { return panic_app_get_app_info(); } -#ifdef MANUFACTURING_FW - accessory_imaging_enable(); +#ifdef CONFIG_MFG mfg_enter_mfg_mode(); return mfg_menu_app_get_info(); #endif diff --git a/src/fw/shell/sdk/prefs.c b/src/fw/shell/sdk/prefs.c index 6e9cc59aca..d78007d616 100644 --- a/src/fw/shell/sdk/prefs.c +++ b/src/fw/shell/sdk/prefs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/preferred_content_size.h" #include "apps/system_app_ids.h" @@ -20,10 +7,10 @@ #include "os/mutex.h" #include "process_management/app_install_manager.h" #include "process_management/process_manager.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/activity/insights_settings.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/insights_settings.h" +#include "pbl/services/settings/settings_file.h" #include "shell/prefs.h" #include "shell/prefs_private.h" @@ -42,6 +29,11 @@ _Static_assert(sizeof(PreferredContentSize) == sizeof(s_content_size), "sizeof(PreferredContentSize) grew, pref needs to be migrated!"); #endif +#ifdef CONFIG_APP_SCALING +#define PREF_KEY_LEGACY_APP_RENDER_MODE "legacyAppRenderMode" +static uint8_t s_legacy_app_render_mode = 1; // Default to scaled mode +#endif + void shell_prefs_init(void) { s_mutex = mutex_create(); mutex_lock(s_mutex); @@ -99,6 +91,14 @@ bool shell_prefs_is_timezone_source_manual(void) { void shell_prefs_set_timezone_source_manual(bool manual) { } +bool shell_prefs_is_time_source_manual(void) { + // Force things to automatic + return false; +} + +void shell_prefs_set_time_source_manual(bool manual) { +} + int16_t shell_prefs_get_automatic_timezone_id(void) { // Invalid return -1; @@ -108,6 +108,14 @@ void shell_prefs_set_automatic_timezone_id(int16_t timezone_id) { } +void prefs_private_lock(void) { + mutex_lock(s_mutex); +} + +void prefs_private_unlock(void) { + mutex_unlock(s_mutex); +} + // Exported function used by blob_db API to set the backing store for a specific key. // Not used by the SDK shell bool prefs_private_write_backing(const uint8_t *key, size_t key_len, const void *value, @@ -129,7 +137,6 @@ bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, return false; } -#if CAPABILITY_HAS_SDK_SHELL4 void watchface_set_default_install_id(AppInstallId app_id) { mutex_lock(s_mutex); Uuid uuid; @@ -161,20 +168,12 @@ AppInstallId watchface_get_default_install_id(void) { } return app_id; } -#else -AppInstallId watchface_get_default_install_id(void) { - return APP_ID_SDK; -} - -void watchface_set_default_install_id(AppInstallId id) { -} -#endif void system_theme_set_content_size(PreferredContentSize content_size) { mutex_lock(s_mutex); const uint8_t content_size_uint = content_size; if (content_size >= NumPreferredContentSizes) { - PBL_LOG(LOG_LEVEL_WARNING, "Ignoring attempt to set content size to invalid size %d", + PBL_LOG_WRN("Ignoring attempt to set content size to invalid size %d", content_size); } else if (prv_pref_set(PREF_KEY_CONTENT_SIZE, &content_size_uint, sizeof(content_size_uint))) { s_content_size = content_size; @@ -188,14 +187,9 @@ PreferredContentSize system_theme_get_content_size(void) { } bool activity_prefs_tracking_is_enabled(void) { -#if CAPABILITY_HAS_HEALTH_TRACKING return true; -#else - return false; -#endif } -#if CAPABILITY_HAS_HEALTH_TRACKING void activity_prefs_tracking_set_enabled(bool enable) { } @@ -249,6 +243,22 @@ bool activity_prefs_heart_rate_is_enabled(void) { return true; } +#ifdef CONFIG_HRM +HRMonitoringInterval activity_prefs_get_hrm_measurement_interval(void) { + return HRMonitoringInterval_10Min; +} + +void activity_prefs_set_hrm_measurement_interval(HRMonitoringInterval interval) { +} + +bool activity_prefs_hrm_activity_tracking_is_enabled(void) { + return true; +} + +void activity_prefs_set_hrm_activity_tracking_enabled(bool enabled) { +} +#endif + ActivityInsightSettings *activity_prefs_get_sleep_reward_settings(void) { static ActivityInsightSettings s_settings = { 0 }; return &s_settings; @@ -276,4 +286,59 @@ UnitsDistance shell_prefs_get_units_distance(void) { return UnitsDistance_Miles; } +GColor shell_prefs_get_theme_highlight_color(void) { + return PBL_IF_COLOR_ELSE(GColorVividCerulean, GColorBlack); +} + +void shell_prefs_set_theme_highlight_color(GColor color) { + // Not used in SDK shell +} + +#ifdef CONFIG_APP_SCALING +LegacyAppRenderMode shell_prefs_get_legacy_app_render_mode(void) { + return (LegacyAppRenderMode)s_legacy_app_render_mode; +} + +void shell_prefs_set_legacy_app_render_mode(LegacyAppRenderMode mode) { + uint8_t mode_value = (uint8_t)mode; + prv_pref_set(PREF_KEY_LEGACY_APP_RENDER_MODE, &mode_value, sizeof(mode_value)); +} #endif + +// Exported function used by blob_db API to handle settings events +// Not used by the SDK shell +void prefs_private_handle_blob_db_event(PebbleBlobDBEvent *event) { + +} + +bool shell_prefs_get_menu_scroll_wrap_around_enable(void) { + return false; +} + +void shell_prefs_set_menu_scroll_wrap_around_enable(bool enable) { + // Not used in SDK shell +} + +MenuScrollVibeBehavior shell_prefs_get_menu_scroll_vibe_behavior(void) { + return MenuScrollNoVibe; +} + +void shell_prefs_set_menu_scroll_vibe_behavior(MenuScrollVibeBehavior behavior) { + // Not used in SDK shell +} + +bool shell_prefs_get_vibe_log_info_enabled(void) { + return false; +} + +void shell_prefs_set_vibe_log_info_enabled(bool enabled) { + // Not used in SDK shell +} + +bool shell_prefs_get_settings_dbs_compacted_v1(void) { + return true; +} + +void shell_prefs_set_settings_dbs_compacted_v1(bool done) { + // Not used in SDK shell +} diff --git a/src/fw/shell/sdk/shell.c b/src/fw/shell/sdk/shell.c index bece29916f..fab119481e 100644 --- a/src/fw/shell/sdk/shell.c +++ b/src/fw/shell/sdk/shell.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell/shell.h" diff --git a/src/fw/shell/sdk/shell_event_loop.c b/src/fw/shell/sdk/shell_event_loop.c index 67394177af..41b113a40d 100644 --- a/src/fw/shell/sdk/shell_event_loop.c +++ b/src/fw/shell/sdk/shell_event_loop.c @@ -1,30 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "apps/system_app_ids.h" -#include "apps/system_apps/timeline/timeline.h" +#include "apps/system/timeline/timeline.h" #include "kernel/events.h" #include "kernel/pbl_malloc.h" #include "popups/notifications/notification_window.h" #include "popups/timeline/peek.h" #include "process_management/app_manager.h" -#include "services/common/compositor/compositor.h" -#include "services/normal/activity/activity.h" -#include "services/normal/app_inbox_service.h" -#include "services/normal/app_outbox_service.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/app_inbox_service.h" +#include "pbl/services/app_outbox_service.h" #include "shell/prefs.h" #include "shell/sdk/watchface.h" #include "shell/shell_event_loop.h" @@ -39,12 +26,10 @@ void shell_event_loop_init(void) { app_message_sender_init(); watchface_init(); timeline_peek_init(); -#if CAPABILITY_HAS_HEALTH_TRACKING // Start activity tracking if enabled if (activity_prefs_tracking_is_enabled()) { activity_start_tracking(false /*test_mode*/); } -#endif } void shell_event_loop_handle_event(PebbleEvent *e) { diff --git a/src/fw/shell/sdk/shell_sdk.c b/src/fw/shell/sdk/shell_sdk.c index 6937856c0e..5cf5fb8d6e 100644 --- a/src/fw/shell/sdk/shell_sdk.c +++ b/src/fw/shell/sdk/shell_sdk.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell_sdk.h" diff --git a/src/fw/shell/sdk/shell_sdk.h b/src/fw/shell/sdk/shell_sdk.h index 3cf995aba1..5bdd7b1b86 100644 --- a/src/fw/shell/sdk/shell_sdk.h +++ b/src/fw/shell/sdk/shell_sdk.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_management/app_install_types.h" diff --git a/src/fw/shell/sdk/stubs.c b/src/fw/shell/sdk/stubs.c index 5d09ceaaec..0b329b4a1d 100644 --- a/src/fw/shell/sdk/stubs.c +++ b/src/fw/shell/sdk/stubs.c @@ -1,23 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "board/board.h" #include "drivers/backlight.h" #include "process_management/pebble_process_md.h" -#include "services/normal/activity/activity.h" -#include "services/normal/timeline/peek.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/timeline/peek.h" +#include "resource/resource_ids.auto.h" #include "shell/prefs.h" #include "util/uuid.h" @@ -39,14 +28,18 @@ uint32_t backlight_get_timeout_ms(void) { return DEFAULT_BACKLIGHT_TIMEOUT_MS; } -#define SDKSHELL_BACKLIGHT_ON_PERCENT 25 // Same as snowy bb2 +uint8_t backlight_get_intensity(void) { + return 100U; +} -uint16_t backlight_get_intensity(void) { - return (BACKLIGHT_BRIGHTNESS_MAX * SDKSHELL_BACKLIGHT_ON_PERCENT / 100); +#ifdef CONFIG_BACKLIGHT_HAS_COLOR +uint32_t backlight_get_default_color(void) { + return BOARD_CONFIG.backlight_default_color; } -uint8_t backlight_get_intensity_percent(void) { - return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX; + +void backlight_set_default_color(uint32_t rgb_color) { } +#endif void backlight_set_timeout_ms(uint32_t timeout_ms) { } @@ -67,6 +60,20 @@ bool backlight_is_motion_enabled(void) { return false; } +BacklightTouchWake backlight_get_touch_wake(void) { + return BacklightTouchWake_Off; +} + +void backlight_set_touch_wake(BacklightTouchWake wake) { +} + +bool touch_is_globally_enabled(void) { + return true; +} + +void touch_set_globally_enabled(bool enable) { +} + #include "process_management/app_install_types.h" void worker_preferences_set_default_worker(AppInstallId id) { } @@ -75,15 +82,6 @@ AppInstallId worker_preferences_get_default_worker(void) { return INSTALL_ID_INVALID; } -#if !CAPABILITY_HAS_SDK_SHELL4 -const char *app_custom_get_title(AppInstallId id) { - return NULL; -} - -void customizable_app_protocol_msg_callback(void *session, const uint8_t* data, - size_t length) { -} -#endif // Used by the alarm service to add alarm pins to the timeline const PebbleProcessMd* alarms_app_get_info(void) { @@ -99,16 +97,6 @@ const PebbleProcessMd* alarms_app_get_info(void) { return (const PebbleProcessMd*) &s_alarms_app_info; } -// This stub isn't needed on tintin as it uses a different launcher -#if !PLATFORM_TINTIN && !CAPABILITY_HAS_SDK_SHELL4 -#include "apps/system_apps/launcher/launcher_app.h" -const LauncherDrawState *launcher_app_get_draw_state(void) { - return NULL; -} -#endif - -void analytics_external_update(void) { -} bool shell_prefs_get_stationary_enabled(void) { return false; @@ -121,6 +109,14 @@ void shell_prefs_set_language_english(bool english) { } void shell_prefs_toggle_language_english(void) { } +ShellLanguage shell_prefs_get_language(void) { + return ShellLanguageEnglish; +} +uint32_t shell_prefs_get_language_resource_id(void) { + return RESOURCE_ID_INVALID; +} +void shell_prefs_set_language(ShellLanguage language) { +} void language_ui_display_changed(const char *lang_name) { } @@ -133,6 +129,12 @@ void timeline_peek_prefs_set_before_time(uint16_t before_time_m) {} uint16_t timeline_peek_prefs_get_before_time(void) { return (TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S / SECONDS_PER_MINUTE); } +#if TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED +void timeline_peek_prefs_set_unsupported_face_mode(TimelinePeekUnsupportedFaceMode mode) {} +TimelinePeekUnsupportedFaceMode timeline_peek_prefs_get_unsupported_face_mode(void) { + return TimelinePeekUnsupportedFaceMode_None; +} +#endif bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out) { return false; @@ -161,3 +163,6 @@ uint8_t activity_prefs_heart_get_zone2_threshold(void) { uint8_t activity_prefs_heart_get_zone3_threshold(void) { return 172; } + +void pbl_analytics_external_collect_settings(void) { +} diff --git a/src/fw/shell/sdk/system_app_registry_list.json b/src/fw/shell/sdk/system_app_registry_list.json index a0da4e9653..5c1a99bb10 100644 --- a/src/fw/shell/sdk/system_app_registry_list.json +++ b/src/fw/shell/sdk/system_app_registry_list.json @@ -36,14 +36,12 @@ { "id": -3, "enum": "LAUNCHER_MENU", - "md_fn": "launcher_menu_app_get_app_info", - "ifdefs": ["CAPABILITY_HAS_SDK_SHELL4=1"] + "md_fn": "launcher_menu_app_get_app_info" }, { "id": -4, "enum": "WATCHFACES", "md_fn": "watchfaces_get_app_info", - "ifdefs": ["CAPABILITY_HAS_SDK_SHELL4=1"], "color_argb8": "GColorJazzberryJamARGB8" } ], diff --git a/src/fw/shell/sdk/system_app_state_machine.c b/src/fw/shell/sdk/system_app_state_machine.c index d40de0f159..2ec32c4b09 100644 --- a/src/fw/shell/sdk/system_app_state_machine.c +++ b/src/fw/shell/sdk/system_app_state_machine.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shell_sdk.h" -#include "apps/core_apps/panic_window_app.h" -#include "apps/sdk/sdk_app.h" +#include "apps/core/panic_window.h" +#include "apps/sdk/app.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/launcher/launcher_app.h" +#include "apps/system/launcher/launcher.h" #include "kernel/panic.h" #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" @@ -34,17 +21,14 @@ const PebbleProcessMd *system_app_state_machine_system_start(void) { return panic_app_get_app_info(); } -#if CAPABILITY_HAS_SDK_SHELL4 const AppInstallId watchface_app_id = watchface_get_default_install_id(); if (watchface_app_id != INSTALL_ID_INVALID) { return app_install_get_md(watchface_app_id, false /* worker */); } -#endif return sdk_app_get_info(); } -#if CAPABILITY_HAS_SDK_SHELL4 //! @return True if the currently running app is an installed watchface static bool prv_current_app_is_watchface(void) { return app_install_is_watchface(app_manager_get_current_app_id()); @@ -65,29 +49,17 @@ const PebbleProcessMd* system_app_state_machine_get_default_app(void) { return launcher_menu_app_get_app_info(); } -#else -AppInstallId system_app_state_machine_get_last_registered_app(void) { - return APP_ID_SDK; -} - -const PebbleProcessMd* system_app_state_machine_get_default_app(void) { - return sdk_app_get_info(); -} -#endif - void system_app_state_machine_register_app_launch(AppInstallId app_id) { if (app_install_id_from_app_db(app_id)) { shell_sdk_set_last_installed_app(app_id); } -#if CAPABILITY_HAS_SDK_SHELL4 if (app_id == APP_ID_LAUNCHER_MENU) { s_rooted_in_watchface = false; } else if (app_install_is_watchface(app_id)) { s_rooted_in_watchface = true; } // Other app launches don't modify our root so just ignore them. -#endif } void system_app_state_machine_panic(void) { diff --git a/src/fw/shell/sdk/watchface.c b/src/fw/shell/sdk/watchface.c index 992c4e6dea..94772ae3c2 100644 --- a/src/fw/shell/sdk/watchface.c +++ b/src/fw/shell/sdk/watchface.c @@ -1,32 +1,19 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "watchface.h" #include "applib/ui/dialogs/expandable_dialog.h" #include "apps/system_app_ids.h" -#include "apps/system_apps/launcher/launcher_app.h" -#include "apps/system_apps/timeline/timeline.h" +#include "apps/system/launcher/launcher.h" +#include "apps/system/timeline/timeline.h" #include "kernel/event_loop.h" #include "kernel/ui/modals/modal_manager.h" #include "popups/timeline/peek.h" #include "process_management/app_manager.h" #include "process_management/pebble_process_md.h" -#include "services/common/analytics/analytics.h" -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/compositor/compositor_transitions.h" #include "shell/sdk/shell_sdk.h" #include "shell/system_app_state_machine.h" #include "system/logging.h" @@ -53,7 +40,6 @@ static void prv_launch_app_via_button(AppLaunchEventConfig *config, app_manager_put_launch_app_event(config); } -#if CAPABILITY_HAS_SDK_SHELL4 static void prv_launch_launcher(ClickRecognizerRef recognizer, void *data) { static const LauncherMenuArgs s_launcher_args = { .reset_scroll = true }; prv_launch_app_via_button(&(AppLaunchEventConfig) { @@ -94,18 +80,14 @@ static void prv_watchface_configure_click_handlers(void) { prv_configure_click(BUTTON_ID_DOWN, prv_launch_timeline); prv_configure_click(BUTTON_ID_UP, prv_launch_timeline); } -#endif void watchface_init(void) { -#if CAPABILITY_HAS_SDK_SHELL4 WatchfaceData *data = &s_watchface_data; click_manager_init(&data->click_manager); prv_watchface_configure_click_handlers(); -#endif } void watchface_handle_button_event(PebbleEvent *e) { -#if CAPABILITY_HAS_SDK_SHELL4 // Only handle button press if app state indicates that the app is still running // which is not in the process of closing WatchfaceData *data = &s_watchface_data; @@ -123,15 +105,6 @@ void watchface_handle_button_event(PebbleEvent *e) { break; } } -#else - if ((e->button.button_id == BUTTON_ID_SELECT) && (e->type == PEBBLE_BUTTON_DOWN_EVENT)) { - app_manager_put_launch_app_event(&(AppLaunchEventConfig) { - .id = system_app_state_machine_get_last_registered_app(), - .common.reason = APP_LAUNCH_USER, - .common.button = e->button.button_id, - }); - } -#endif } void watchface_reset_click_manager(void) { diff --git a/src/fw/shell/sdk/watchface.h b/src/fw/shell/sdk/watchface.h index 6234d80bc6..5f8eaa2379 100644 --- a/src/fw/shell/sdk/watchface.h +++ b/src/fw/shell/sdk/watchface.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" #include "process_management/app_install_manager.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" void watchface_init(void); diff --git a/src/fw/shell/shell.h b/src/fw/shell/shell.h index 3004fb1b94..dd6f93ac38 100644 --- a/src/fw/shell/shell.h +++ b/src/fw/shell/shell.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/shell/shell_event_loop.h b/src/fw/shell/shell_event_loop.h index 348164bc64..7c92ad7bdb 100644 --- a/src/fw/shell/shell_event_loop.h +++ b/src/fw/shell/shell_event_loop.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/shell/system_app_state_machine.h b/src/fw/shell/system_app_state_machine.h index d73dfe38bc..e760892175 100644 --- a/src/fw/shell/system_app_state_machine.h +++ b/src/fw/shell/system_app_state_machine.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/shell/system_theme.c b/src/fw/shell/system_theme.c index 00091e9217..5cdc25d028 100644 --- a/src/fw/shell/system_theme.c +++ b/src/fw/shell/system_theme.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system_theme.h" #include "applib/fonts/fonts.h" -#include "apps/system_apps/settings/settings_notifications_private.h" +#include "apps/system/settings/notifications_private.h" #include "process_management/process_manager.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" #include "shell/prefs.h" #include "syscall/syscall_internal.h" #include "system/passert.h" @@ -38,7 +25,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [PreferredContentSizeSmall] = { .fonts = { [TextStyleFont_Header] = FONT_KEY_GOTHIC_18_BOLD, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) [TextStyleFont_Title] = FONT_KEY_GOTHIC_18_BOLD, [TextStyleFont_Body] = FONT_KEY_GOTHIC_18, #endif @@ -50,7 +37,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_18_BOLD, //! @note this is the same as Medium until Small is designed [TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_18, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) //! @note this is the same as Medium until Small is designed [TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_20_BOLD_NUMBERS, #endif @@ -65,7 +52,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [PreferredContentSizeMedium] = { .fonts = { [TextStyleFont_Header] = FONT_KEY_GOTHIC_18_BOLD, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) [TextStyleFont_Title] = FONT_KEY_GOTHIC_24_BOLD, [TextStyleFont_Body] = FONT_KEY_GOTHIC_24_BOLD, #endif @@ -74,7 +61,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [TextStyleFont_Footer] = FONT_KEY_GOTHIC_18, [TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_24_BOLD, [TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_18, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) [TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_20_BOLD_NUMBERS, #endif [TextStyleFont_TimeHeaderWords] = FONT_KEY_GOTHIC_14_BOLD, @@ -85,7 +72,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [PreferredContentSizeLarge] = { .fonts = { [TextStyleFont_Header] = FONT_KEY_GOTHIC_24_BOLD, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) [TextStyleFont_Title] = FONT_KEY_GOTHIC_28_BOLD, [TextStyleFont_Body] = FONT_KEY_GOTHIC_28, #endif @@ -94,7 +81,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [TextStyleFont_Footer] = FONT_KEY_GOTHIC_18, [TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_24_BOLD, [TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_24, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) [TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM, #endif [TextStyleFont_TimeHeaderWords] = FONT_KEY_GOTHIC_18_BOLD, @@ -105,7 +92,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [PreferredContentSizeExtraLarge] = { .fonts = { [TextStyleFont_Header] = FONT_KEY_GOTHIC_28_BOLD, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) [TextStyleFont_Title] = FONT_KEY_GOTHIC_36_BOLD, [TextStyleFont_Body] = FONT_KEY_GOTHIC_36, #endif @@ -117,7 +104,7 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { [TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_28, //! @note this is the same as Large until ExtraLarge is designed [TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_24_BOLD, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) //! @note this is the same as Large until ExtraLarge is designed [TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM, #endif @@ -135,10 +122,10 @@ static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = { static const char *prv_get_font_for_size(PreferredContentSize content_size, TextStyleFont font) { if (content_size >= NumPreferredContentSizes) { - PBL_LOG(LOG_LEVEL_ERROR, "Requested a content size that is out of bounds (%d)", content_size); + PBL_LOG_ERR("Requested a content size that is out of bounds (%d)", content_size); goto fail; } else if (font >= TextStyleFontCount) { - PBL_LOG(LOG_LEVEL_ERROR, "Requested a style font that is out of bounds (%d)", font); + PBL_LOG_ERR("Requested a style font that is out of bounds (%d)", font); goto fail; } return s_text_styles[content_size].fonts[font]; @@ -189,6 +176,8 @@ static const PreferredContentSize s_platform_default_content_sizes[] = { [PlatformTypeChalk] = PreferredContentSizeMedium, [PlatformTypeDiorite] = PreferredContentSizeMedium, [PlatformTypeEmery] = PreferredContentSizeLarge, + [PlatformTypeFlint] = PreferredContentSizeMedium, + [PlatformTypeGabbro] = PreferredContentSizeLarge, }; T_STATIC PreferredContentSize prv_convert_content_size_between_platforms(PreferredContentSize size, @@ -219,10 +208,3 @@ PreferredContentSize system_theme_convert_host_content_size_to_runtime_platform( return prv_convert_content_size_between_platforms(size, PBL_PLATFORM_TYPE_CURRENT, runtime_platform); } - -void analytics_external_collect_system_theme_settings(void) { - const SettingsContentSize content_size = - settings_content_size_from_preferred_size(system_theme_get_content_size()); - analytics_set(ANALYTICS_DEVICE_METRIC_SYSTEM_THEME_TEXT_STYLE, content_size, - AnalyticsClient_System); -} diff --git a/src/fw/shell/system_theme.h b/src/fw/shell/system_theme.h index abcb1fe1ba..b9990cd0cd 100644 --- a/src/fw/shell/system_theme.h +++ b/src/fw/shell/system_theme.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -30,7 +17,7 @@ typedef enum TextStyleFont { //! a notification belongs to, or who sent a message. It is smaller than the body copy when //! readable, but always bold. TextStyleFont_Header, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) //! Title is for prominent text that is usually the title or name of the content, giving context //! for the user such as the subject line of an email. It is comparable to the body, but //! always bold. @@ -52,7 +39,7 @@ typedef enum TextStyleFont { TextStyleFont_MenuCellTitle, //! For subtitles of menu cells that provide auxiliary information about an item of a list. TextStyleFont_MenuCellSubtitle, -#if !RECOVERY_FW +#if !defined(CONFIG_RECOVERY_FW) //! Time Header Numbers is used specially by Timeline to display content time in extra bold e.g. //! 4:06 AM. It is comparable to the title. TextStyleFont_TimeHeaderNumbers, diff --git a/src/fw/shell/wscript b/src/fw/shell/wscript deleted file mode 100644 index e8da977aea..0000000000 --- a/src/fw/shell/wscript +++ /dev/null @@ -1,209 +0,0 @@ -import json - -def configure(conf): - pass - -def generate_apps_table(task): - in_node = task.inputs[0] - registry = task.outputs[0] - app_enum = task.outputs[1] - # apps that are assumed to exist by source and must always have a define - default_app_enums = [ - 'ALARMS', - 'GOLF', - 'MUSIC', - 'SETTINGS', - 'SPORTS', - ] - system_apps = [] - resource_apps = [] - definition = {} - with open(in_node.abspath(), 'r') as f_in: - definition.update(json.load(f_in)) - - system_apps.extend(definition['system_apps']) - resource_apps.extend(definition['resource_apps']) - - def uuid_to_byte_hex_str(uuid): - uuid = uuid.replace("-", "") - - # split string into groups of twos - n = 2 - pieces = [uuid[i:i+n] for i in range(0, len(uuid), n)] - # prefix every group of two with 0x and add commas - hex_str = "0x" + ", 0x".join(pieces) - return hex_str - - def generate_registry(f_out): - # write generated header - f_out.write('// @'+'generated -- DO NOT EDIT\n\n') - - # write out includes - f_out.write('#include "system_app_ids.auto.h"\n') - f_out.write('#include "process_management/pebble_process_md.h"\n') - f_out.write('#include "resource/resource_ids.auto.h"\n\n') - - # zeroed app id's indicate disabled apps - enabled_system_apps = list(filter(lambda e: e['id'] != 0, system_apps)) - - # write out function declarations for system apps - for entry in enabled_system_apps: - f_out.write('extern const PebbleProcessMd *' - "{cb_str}(void);\n".format( - cb_str=entry['md_fn'])) - - # write variable name - f_out.write('\n\nstatic const AppRegistryEntry ' - 'APP_RECORDS[] = {\n') - - # comment for system apps - f_out.write('\n // System Apps\n') - - # write all entries for system apps - for entry in enabled_system_apps: - f_out.write( - ' {{\n' - ' .id = APP_ID_{enum},\n' - ' .type = AppInstallStorageFw,\n' - ' .md_fn = &{cb_str},\n' - ' .color.argb = {color_argb8},\n' - ' }},\n'.format( - enum=entry['enum'], - cb_str=entry['md_fn'], - color_argb8=entry.get('color_argb8', 'GColorClearARGB8'))) - - # comment for resource apps - f_out.write('\n // Resource (stored) Apps\n') - - # write all entries for resource apps - for entry in resource_apps: - f_out.write( - ' {{\n' - ' .id = APP_ID_{enum},\n' - ' .type = AppInstallStorageResources,\n' - ' .name = "{name}",\n' - ' .uuid = {{ {uuid} }},\n' - ' .bin_resource_id = {bin_id},\n' - ' .icon_resource_id = {icon_id},\n' - ' .color.argb = {color_argb8},\n' - ' }},\n'.format( - enum=entry['enum'], - name=entry['name'], - uuid=uuid_to_byte_hex_str(entry['uuid']), - bin_id=entry['bin_resource_id'], - icon_id=entry['icon_resource_id'], - color_argb8=entry.get('color_argb8', 'GColorClearARGB8'))) - - f_out.write('};\n') - - def generate_app_enum(f_out): - # write generated header - f_out.write('// @'+'generated -- DO NOT EDIT\n\n') - - # write out includes - f_out.write('#include "process_management/app_install_types.h"\n\n') - - # write all entries for resource apps - built_app_enums = [] - - def write_app_enum(enum, id): - f_out.write('#define APP_ID_{enum} ((AppInstallId) {id})\n'.format(enum=enum, id=id)) - - for entry in (system_apps + resource_apps): - built_app_enums.append(entry['enum']) - write_app_enum(enum=entry['enum'], id=entry['id']) - - # write remaining default apps that are not being built - for enum in [x for x in default_app_enums if x not in built_app_enums]: - write_app_enum(enum=enum, id=0) - - - def _entry_in_test_apps_list(entry): - return str(entry['id']) in task.env.test_apps_list - - # If the current build config allows the app with the given list of defines to be added. - # Disabled entries have no entry in the app registry. - def entry_enabled(entry): - - defines = entry.get("ifdefs") or [] - platforms = entry.get("target_platforms") or [] - - # Disabled overrides any define - if "DISABLED" in defines: - return False - - if (platforms) and (task.generator.bld.get_platform_name() not in platforms): - # If we have a platform list and the current platform isn't listed, return false - return False - - # keep around for legacy purposes - if "DEFAULT" in defines: - return True - - if defines: - # If we have a defines list, run through all of them and see if we should be including - # this application - - # if test apps are enabled and we have a list - test_apps_enabled = "ENABLE_TEST_APPS" in task.env.DEFINES - entry_is_test_app = "ENABLE_TEST_APPS" in defines - has_test_app_list = len(task.env.test_apps_list) > 0 - if test_apps_enabled and has_test_app_list: - # check if app is allowed. If not, return False - desired_test_app = _entry_in_test_apps_list(entry) - if entry_is_test_app and not desired_test_app: - return False - else: - if entry_is_test_app and not test_apps_enabled: - return False - - if all(define in task.env.DEFINES for define in defines): - return True - else: - # if there are no defines, it means we should include it by default - return True - - return False - - def _set_disabled_app_ids_to_zero(): - for entry in system_apps: - if not entry_enabled(entry): - entry['id'] = 0 - - def _move_desired_test_apps_to_top(): - for app_list in [system_apps, resource_apps]: - temp_list = [] - for entry in app_list: - if _entry_in_test_apps_list(entry): - temp_list.insert(0, entry) - else: - temp_list.append(entry) - app_list[:] = temp_list - - _set_disabled_app_ids_to_zero() - _move_desired_test_apps_to_top() - - with open(registry.abspath(), 'w') as f_registry: - generate_registry(f_registry) - - with open(app_enum.abspath(), 'w') as f_app_enum: - generate_app_enum(f_app_enum) - - -def build(bld): - if bld.variant == 'prf': - shell_name = 'prf' - else: - shell_name = bld.env.NORMAL_SHELL - - in_node = bld.path.find_node('%s/system_app_registry_list.json' % shell_name) - - registry = bld.path.get_bld().make_node('system_app_registry_list.auto.h') - app_enum = bld.path.get_bld().make_node('system_app_ids.auto.h') - - bld(rule=generate_apps_table, - source=[in_node], - target=[registry, app_enum], - vars=['DEFINES', 'test_apps_list']) - -# vim:filetype=python diff --git a/src/fw/shell/wscript_build b/src/fw/shell/wscript_build new file mode 100644 index 0000000000..e06c657c91 --- /dev/null +++ b/src/fw/shell/wscript_build @@ -0,0 +1,183 @@ +import json + +def generate_apps_table(task): + in_node = task.inputs[0] + registry = task.outputs[0] + app_enum = task.outputs[1] + # apps that are assumed to exist by source and must always have a define + default_app_enums = [ + 'ALARMS', + 'GOLF', + 'MUSIC', + 'SETTINGS', + 'SPORTS', + ] + system_apps = [] + resource_apps = [] + definition = {} + with open(in_node.abspath(), 'r') as f_in: + definition.update(json.load(f_in)) + + system_apps.extend(definition['system_apps']) + resource_apps.extend(definition['resource_apps']) + + def uuid_to_byte_hex_str(uuid): + uuid = uuid.replace("-", "") + + # split string into groups of twos + n = 2 + pieces = [uuid[i:i+n] for i in range(0, len(uuid), n)] + # prefix every group of two with 0x and add commas + hex_str = "0x" + ", 0x".join(pieces) + return hex_str + + def generate_registry(f_out): + # write generated header + f_out.write('// @'+'generated -- DO NOT EDIT\n\n') + + # write out includes + f_out.write('#include "system_app_ids.auto.h"\n') + f_out.write('#include "process_management/pebble_process_md.h"\n') + f_out.write('#include "resource/resource_ids.auto.h"\n\n') + + # zeroed app id's indicate disabled apps + enabled_system_apps = list(filter(lambda e: e['id'] != 0, system_apps)) + + # write out function declarations for system apps + for entry in enabled_system_apps: + f_out.write('extern const PebbleProcessMd *' + "{cb_str}(void);\n".format( + cb_str=entry['md_fn'])) + + # write variable name + f_out.write('\n\nstatic const AppRegistryEntry ' + 'APP_RECORDS[] = {\n') + + # comment for system apps + f_out.write('\n // System Apps\n') + + # write all entries for system apps + for entry in enabled_system_apps: + f_out.write( + ' {{\n' + ' .id = APP_ID_{enum},\n' + ' .type = AppInstallStorageFw,\n' + ' .md_fn = &{cb_str},\n' + ' .color.argb = {color_argb8},\n' + ' }},\n'.format( + enum=entry['enum'], + cb_str=entry['md_fn'], + color_argb8=entry.get('color_argb8', 'GColorClearARGB8'))) + + # comment for resource apps + f_out.write('\n // Resource (stored) Apps\n') + + # write all entries for resource apps + for entry in resource_apps: + f_out.write( + ' {{\n' + ' .id = APP_ID_{enum},\n' + ' .type = AppInstallStorageResources,\n' + ' .name = "{name}",\n' + ' .uuid = {{ {uuid} }},\n' + ' .bin_resource_id = {bin_id},\n' + ' .icon_resource_id = {icon_id},\n' + ' .color.argb = {color_argb8},\n' + ' }},\n'.format( + enum=entry['enum'], + name=entry['name'], + uuid=uuid_to_byte_hex_str(entry['uuid']), + bin_id=entry['bin_resource_id'], + icon_id=entry['icon_resource_id'], + color_argb8=entry.get('color_argb8', 'GColorClearARGB8'))) + + f_out.write('};\n') + + def generate_app_enum(f_out): + # write generated header + f_out.write('// @'+'generated -- DO NOT EDIT\n\n') + + # write out includes + f_out.write('#include "process_management/app_install_types.h"\n\n') + + # write all entries for resource apps + built_app_enums = [] + + def write_app_enum(enum, id): + f_out.write('#define APP_ID_{enum} ((AppInstallId) {id})\n'.format(enum=enum, id=id)) + + for entry in (system_apps + resource_apps): + built_app_enums.append(entry['enum']) + write_app_enum(enum=entry['enum'], id=entry['id']) + + # write remaining default apps that are not being built + for enum in [x for x in default_app_enums if x not in built_app_enums]: + write_app_enum(enum=enum, id=0) + + + def _ifdef_enabled(define): + if define in task.env.DEFINES: + return True + if define.startswith("CONFIG_") and define in task.env and task.env[define]: + return True + return False + + # If the current build config allows the app with the given list of defines to be added. + # Disabled entries have no entry in the app registry. + def entry_enabled(entry): + + defines = entry.get("ifdefs") or [] + platforms = entry.get("target_platforms") or [] + + # Disabled overrides any define + if "DISABLED" in defines: + return False + + if (platforms) and (task.generator.bld.env.CONFIG_BOARD_FAMILY_NAME not in platforms): + # If we have a platform list and the current platform isn't listed, return false + return False + + # keep around for legacy purposes + if "DEFAULT" in defines: + return True + + if defines: + # If we have a defines list, run through all of them and see if we should be including + # this application + if all(_ifdef_enabled(define) for define in defines): + return True + else: + # if there are no defines, it means we should include it by default + return True + + return False + + def _set_disabled_app_ids_to_zero(): + for entry in system_apps: + if not entry_enabled(entry): + entry['id'] = 0 + + _set_disabled_app_ids_to_zero() + + with open(registry.abspath(), 'w') as f_registry: + generate_registry(f_registry) + + with open(app_enum.abspath(), 'w') as f_app_enum: + generate_app_enum(f_app_enum) + +if bld.env.VARIANT == 'prf': + shell_name = 'prf' +else: + shell_name = bld.env.NORMAL_SHELL + +in_node = bld.path.find_node('%s/system_app_registry_list.json' % shell_name) + +registry = bld.path.get_bld().make_node('system_app_registry_list.auto.h') +app_enum = bld.path.get_bld().make_node('system_app_ids.auto.h') + +bld(rule=generate_apps_table, + source=[in_node], + target=[registry, app_enum], + vars=['DEFINES']) + +# vim:filetype=python diff --git a/src/fw/soc/Kconfig b/src/fw/soc/Kconfig new file mode 100644 index 0000000000..d2f4669536 --- /dev/null +++ b/src/fw/soc/Kconfig @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +choice + prompt "SoC" + help + SoC family. Real boards pick a vendor SoC; QEMU boards pick + SOC_QEMU and then choose a Cortex core explicitly. + +config SOC_NRF52 + bool "Nordic nRF52" + +config SOC_SF32LB52 + bool "SiFli SF32LB52" + +config SOC_QEMU + bool "QEMU emulated" + +endchoice + +config SOC + string + default "NRF52" if SOC_NRF52 + default "SF32LB52" if SOC_SF32LB52 + help + Stringified SoC name, for places that need the name as a token + (e.g. sftool -c). Empty on QEMU boards. + +config QEMU + bool + default y if SOC_QEMU + help + Derived alias for SOC_QEMU. Gates features that don't make + sense under emulation. + +choice + prompt "ARM Cortex core" + default CORTEX_M4 if SOC_NRF52 + default CORTEX_M33 if SOC_SF32LB52 + +config CORTEX_M4 + bool "Cortex-M4" + +config CORTEX_M33 + bool "Cortex-M33" + +endchoice + +config MPU_TYPE_ARMV8M + bool + default y if CORTEX_M33 + +config MPU_TYPE_ARMV7M + bool + default y if !MPU_TYPE_ARMV8M diff --git a/src/fw/soc/nrf/Kconfig.defconfig b/src/fw/soc/nrf/Kconfig.defconfig new file mode 100644 index 0000000000..9271b18f68 --- /dev/null +++ b/src/fw/soc/nrf/Kconfig.defconfig @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +if SOC_NRF52 + +choice BT_FW + default BT_FW_NIMBLE +endchoice + +endif # SOC_NRF52 diff --git a/src/fw/soc/nrf/nrf52/init.c b/src/fw/soc/nrf/nrf52/init.c new file mode 100644 index 0000000000..d2b66a1dee --- /dev/null +++ b/src/fw/soc/nrf/nrf52/init.c @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +void soc_early_init(void) { + +} diff --git a/src/fw/soc/nrf/nrf52/wscript_build b/src/fw/soc/nrf/nrf52/wscript_build new file mode 100644 index 0000000000..0f1e6130d9 --- /dev/null +++ b/src/fw/soc/nrf/nrf52/wscript_build @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.objects( + name='soc', + source=[ + 'init.c', + ], + use=[ + 'fw_includes', + ], +) diff --git a/src/fw/soc/nrf/wscript_build b/src/fw/soc/nrf/wscript_build new file mode 100644 index 0000000000..90366ea971 --- /dev/null +++ b/src/fw/soc/nrf/wscript_build @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 +if bld.env.CONFIG_SOC_NRF52: + bld.recurse("nrf52") \ No newline at end of file diff --git a/src/fw/soc/qemu/Kconfig.defconfig b/src/fw/soc/qemu/Kconfig.defconfig new file mode 100644 index 0000000000..555d3e4e3a --- /dev/null +++ b/src/fw/soc/qemu/Kconfig.defconfig @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +if SOC_QEMU + +choice BT_FW + default BT_FW_QEMU +endchoice + +endif # SOC_QEMU diff --git a/src/fw/soc/qemu/init.c b/src/fw/soc/qemu/init.c new file mode 100644 index 0000000000..5dd649dec9 --- /dev/null +++ b/src/fw/soc/qemu/init.c @@ -0,0 +1,11 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +// System clock frequency for QEMU (64 MHz) +uint32_t SystemCoreClock = 64000000; + +// SoC early init - nothing to do for QEMU +void soc_early_init(void) { +} diff --git a/src/fw/soc/qemu/wscript_build b/src/fw/soc/qemu/wscript_build new file mode 100644 index 0000000000..6f93e2ec2a --- /dev/null +++ b/src/fw/soc/qemu/wscript_build @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 +bld.objects( + name='soc', + source=['init.c'], + use=['fw_includes'], +) diff --git a/src/fw/soc/sf32lb/Kconfig.defconfig b/src/fw/soc/sf32lb/Kconfig.defconfig new file mode 100644 index 0000000000..312e01dbc6 --- /dev/null +++ b/src/fw/soc/sf32lb/Kconfig.defconfig @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +if SOC_SF32LB52 + +config MMAP_RESOURCES + default y + +choice BT_FW + default BT_FW_NIMBLE +endchoice + +endif # SOC_SF32LB52 diff --git a/src/fw/soc/sf32lb/sf32lb52x/freertos.c b/src/fw/soc/sf32lb/sf32lb52x/freertos.c new file mode 100644 index 0000000000..9b3edb5192 --- /dev/null +++ b/src/fw/soc/sf32lb/sf32lb52x/freertos.c @@ -0,0 +1,411 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +#include "board/board.h" +#include "console/prompt.h" +#include "drivers/flash.h" +#include "drivers/mcu.h" +#include "drivers/rtc.h" +#include "drivers/sf32lb52/rc10k.h" +#include "drivers/task_watchdog.h" +#include "kernel/util/stop.h" +#include "kernel/util/wfi.h" +#include "os/tick.h" +#include "pbl/services/analytics/analytics.h" +#include "util/math.h" + +#include + +#include + +#include "FreeRTOS.h" +#include "task.h" + +// HAL tick counter (milliseconds) - used by HAL timeout functions +extern __IO uint32_t uwTick; + +static LPTIM_HandleTypeDef s_lptim = { + .Instance = LPTIM1, +}; + +// CPU analytics tracking +typedef enum { + SleepTypeNone = 0, + SleepTypeWfi, + SleepTypeDeepWfi, +} SleepType; + +static volatile SleepType s_last_sleep_type; +static RtcTicks s_analytics_wfi_ticks; +static RtcTicks s_analytics_deepwfi_ticks; +static RtcTicks s_analytics_deepsleep_ticks; +static RtcTicks s_last_ticks; +static uint32_t s_analytics_ipc_not_idle_count; +static bool s_force_deepwfi; + +//! Early wake-up ticks (to avoid over-sleeping due to wake-up latency) +static const uint32_t EARLY_WAKEUP_TICKS = 4; +//! Minimum ticks to enter deep sleep +static const uint32_t MIN_DEEPSLEEP_TICKS = RTC_TICKS_HZ / 20; +//! Maximum LPTIM counter value (24-bit) +static const uint32_t MAX_LPTIM_CNT = 0xFFFFFFUL; + +static uint32_t s_iser_bak[16]; + +static void prv_wdt_feed(uint16_t elapsed_ticks) { + static uint32_t wdt_feed_ticks; + + wdt_feed_ticks += elapsed_ticks; + if (wdt_feed_ticks >= (RTC_TICKS_HZ / (1000 / TASK_WATCHDOG_FEED_PERIOD_MS))) { + wdt_feed_ticks = 0U; + task_watchdog_feed(); + } +} + +static void prv_save_iser(void) { + uint32_t i; + for (i = 0; i < 16; i++) { + s_iser_bak[i] = NVIC->ISER[i]; + NVIC->ICER[i] = 0xFFFFFFFF; + __DSB(); + __ISB(); + } +} + +static void prv_restore_iser(void) { + uint32_t i; + for (i = 0; i < 16; i++) { + __COMPILER_BARRIER(); + NVIC->ISER[i] = s_iser_bak[i]; + __COMPILER_BARRIER(); + } +} + +static inline void prv_enter_wfi(void) { + s_last_sleep_type = SleepTypeWfi; + __WFI(); +} + +static void prv_enter_deepwfi(void) { + s_last_sleep_type = SleepTypeDeepWfi; + + flash_power_down_for_stop_mode(); + + __DSB(); + __ISB(); + + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; + __WFI(); + SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; +} + +static void prv_enter_deepslep(void) { + QSPIPortState *flash_state; + uint32_t dll1_freq = 0UL; + int clk_src; + + flash_state = QSPI_FLASH->qspi->state; + + prv_save_iser(); + + HAL_FLASH_NOP_CMD(&flash_state->ctx.handle); + HAL_FLASH_DEEP_PWRDOWN(&flash_state->ctx.handle); + HAL_Delay_us(flash_state->t_enter_deep_us); + + NVIC_EnableIRQ(AON_IRQn); + + clk_src = HAL_RCC_HCPU_GetClockSrc(RCC_CLK_MOD_SYS); + if (clk_src == RCC_SYSCLK_DLL1) { + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_SYS, RCC_SYSCLK_HRC48); + dll1_freq = HAL_RCC_HCPU_GetDLL1Freq(); + HAL_RCC_HCPU_DisableDLL1(); + } + + HAL_HPAON_DISABLE_PAD(); + HAL_HPAON_DISABLE_VHP(); + + HAL_HPAON_CLEAR_HP_ACTIVE(); + HAL_HPAON_SET_POWER_MODE(AON_PMR_DEEP_SLEEP); + + __WFI(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + __NOP(); + + HAL_HPAON_ENABLE_PAD(); + HAL_HPAON_ENABLE_VHP(); + + HAL_HPAON_SET_HP_ACTIVE(); + HAL_HPAON_CLEAR_POWER_MODE(); + + // Wait for HXT48 to be ready + if (dll1_freq != 0UL) { + while (0 == (hwp_hpsys_aon->ACR & HPSYS_AON_ACR_HXT48_RDY)) { + __NOP(); + } + } + + if (clk_src == RCC_SYSCLK_DLL1) { + HAL_RCC_HCPU_EnableDLL1(dll1_freq); + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_SYS, RCC_SYSCLK_DLL1); + HAL_Delay_us(0); + } + + HAL_FLASH_RELEASE_DPD(&flash_state->ctx.handle); + HAL_Delay_us(flash_state->t_exit_deep_us); + + prv_restore_iser(); +} + +static uint32_t prv_calc_elapsed_ticks(uint32_t gtimer_cyc) { + static uint16_t s_err_milli_ticks; + uint32_t elapsed_milli_ticks; + uint32_t elapsed_ticks; + + elapsed_milli_ticks = rc10k_cyc_to_milli_ticks(gtimer_cyc); + elapsed_ticks = elapsed_milli_ticks / 1000U; + s_err_milli_ticks += (elapsed_milli_ticks % 1000U); + if (s_err_milli_ticks >= 1000U) { + elapsed_ticks++; + s_err_milli_ticks -= 1000U; + } + + return elapsed_ticks; +} + +void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) { + if (!sleep_mode_is_allowed()) { + return; + } + + if (!ipc_queue_check_idle()) { + s_analytics_ipc_not_idle_count++; + return; + } + + __disable_irq(); + + if (eTaskConfirmSleepModeStatus() != eAbortSleep) { + if (!stop_mode_is_allowed()) { + prv_enter_wfi(); + } else { + if (xExpectedIdleTime < MIN_DEEPSLEEP_TICKS || s_force_deepwfi) { + prv_enter_deepwfi(); + } else { + uint32_t gtimer_start; + uint32_t gtimer_stop; + uint32_t gtimer_delta; + uint32_t sleep_ticks; + uint32_t lptim_ticks; + uint32_t elapsed_ticks; + + // stop systick + SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk); + SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk; + + // configure LPTIM to wake us up after expected idle time + sleep_ticks = xExpectedIdleTime - EARLY_WAKEUP_TICKS; + lptim_ticks = MIN(sleep_ticks * rc10k_get_freq_hz() / RTC_TICKS_HZ, + MAX_LPTIM_CNT); + HAL_LPTIM_Counter_Start_IT(&s_lptim, lptim_ticks); + + gtimer_start = HAL_GTIMER_READ(); + + prv_enter_deepslep(); + + // NOTE: GTIMER needs at least 1 LP clock cycle to be updated after sleep, + // so spin until we see a change + do { + gtimer_stop = HAL_GTIMER_READ(); + } while (gtimer_stop == gtimer_start); + + if (gtimer_stop < gtimer_start) { + gtimer_delta = (UINT32_MAX - gtimer_start) + gtimer_stop + 1UL; + } else { + gtimer_delta = gtimer_stop - gtimer_start; + } + + elapsed_ticks = prv_calc_elapsed_ticks(gtimer_delta); + + vTaskStepTick(elapsed_ticks); + + // increment HAL tick counter by elapsed ticks + uwTick += elapsed_ticks; + + prv_wdt_feed(elapsed_ticks); + + // Force RTC synchronization of shadow registers + hwp_rtc->ISR &= RTC_RSF_MASK; + + // Track deep sleep time for analytics + s_analytics_deepsleep_ticks += elapsed_ticks; + + // stop LPTIM + HAL_LPTIM_Counter_Stop_IT(&s_lptim); + + // enable systick + SysTick->CTRL |= (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk); + } + } + } + + __enable_irq(); +} + +bool vPortEnableTimer() { + HAL_LPTIM_InitDefault(&s_lptim); + HAL_LPTIM_Init(&s_lptim); + + // configure SYSTICK + // - use HXT48 as TICK reference clock + // - divide TICK reference clock to 1.92MHz (48MHz / 25) (TICK_CLK) + // - enable TICK_CLK as SYSTICK clock source + // - configure SYSTICK to generate interrupt at RTC_TICKS_HZ rate + // + // HXT48│\ ┌────────────────┐ + // ────┼ \ │ SYSTICK│ + // HRC48│ │ ┌───────┐ TICK_CLK ││\ │ + // ────┼ ├──│TICKDIV├───────────┼┤ │ ┌──────┐ │ + // ...│ │ └───────┘. ┌┼┤ ├───│RELOAD├──┤ + // │ / │││/ └──────┘ │ + // │/ ││ │ + // HCLK ││ │ + // ─────────────────────┘└────────────────┘ + + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_HP_TICK, RCC_CLK_TICK_HXT48); + // delay to avoid systick config failure (undocumented silicon issue) + HAL_Delay_us(200); + MODIFY_REG(hwp_hpsys_rcc->CFGR, HPSYS_RCC_CFGR_TICKDIV_Msk, + MAKE_REG_VAL(25, HPSYS_RCC_CFGR_TICKDIV_Msk, HPSYS_RCC_CFGR_TICKDIV_Pos)); + HAL_SYSTICK_Config(1920000 / RTC_TICKS_HZ); + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); + + SysTick->CTRL |= (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk); + + // configure clock dividers for deep WFI: + // HCLK = 48MHz / 12 = 4MHz + // PCLK1 = 4MHz / 2^0 = 4MHz + // PCLK2 = 4MHz / 2^1 = 2MHz + HAL_RCC_HCPU_SetDeepWFIDiv(12, 0, 1); + + // TODO(SF32LB52): to use deep WFI when audio is ON, HCLK needs to remain + // at 48MHz (div=1). Also, clock needs to be forced ON during deep WFI + // (FORCE_HP bit in HPSYS_RCC_DBGR register) + + return true; +} + +void AON_IRQHandler(void) +{ + uint32_t status; + + NVIC_DisableIRQ(AON_IRQn); + HAL_HPAON_CLEAR_POWER_MODE(); + + status = HAL_HPAON_GET_WSR(); + status &= ~HPSYS_AON_WSR_PIN_ALL; + HAL_HPAON_CLEAR_WSR(status); +} + +void SysTick_Handler(void) { + extern void xPortSysTickHandler(void); + xPortSysTickHandler(); + + HAL_IncTick(); + + prv_wdt_feed(1U); + + if (s_last_sleep_type == SleepTypeWfi) { + s_analytics_wfi_ticks++; + } else if (s_last_sleep_type == SleepTypeDeepWfi) { + s_analytics_deepwfi_ticks++; + } + + s_last_sleep_type = SleepTypeNone; + + // TODO(SF32LB52): we may need to handle tick loss compensation when using + // SysTick due to flash erase times (runs with IRQs disabled to not interfere + // with XIP, and can easily span multiple ticks) +} + +void dump_current_runtime_stats(void) { + uint32_t wfi_ticks = s_analytics_wfi_ticks; + uint32_t deepwfi_ticks = s_analytics_deepwfi_ticks; + uint32_t deepsleep_ticks = s_analytics_deepsleep_ticks; + + RtcTicks now_ticks = rtc_get_ticks(); + uint32_t total_ticks = (uint32_t)(now_ticks - s_last_ticks); + uint32_t running_ticks = total_ticks - wfi_ticks - deepwfi_ticks - deepsleep_ticks; + + char buf[160]; + snprintf(buf, sizeof(buf), "Run: %"PRIu32" ticks (%"PRIu32" %%)", + running_ticks, (running_ticks * 100) / total_ticks); + prompt_send_response(buf); + snprintf(buf, sizeof(buf), "WFI: %"PRIu32" ticks (%"PRIu32" %%)", + wfi_ticks, (wfi_ticks * 100) / total_ticks); + prompt_send_response(buf); + snprintf(buf, sizeof(buf), "Deep WFI: %"PRIu32" ticks (%"PRIu32" %%)", + deepwfi_ticks, (deepwfi_ticks * 100) / total_ticks); + prompt_send_response(buf); + snprintf(buf, sizeof(buf), "Deepsleep: %"PRIu32" ticks (%"PRIu32" %%)", + deepsleep_ticks, (deepsleep_ticks * 100) / total_ticks); + prompt_send_response(buf); + snprintf(buf, sizeof(buf), "Tot: %"PRIu32" ticks", total_ticks); + prompt_send_response(buf); +} + +void command_force_deepwfi(const char *arg) { + if (arg[0] == '1') { + s_force_deepwfi = true; + prompt_send_response("Deep WFI forced ON (deep sleep disabled)"); + } else { + s_force_deepwfi = false; + prompt_send_response("Deep WFI forced OFF (deep sleep allowed)"); + } +} + +void pbl_analytics_external_collect_cpu_stats(void) { + uint32_t wfi_ticks = s_analytics_wfi_ticks; + uint32_t deepwfi_ticks = s_analytics_deepwfi_ticks; + uint32_t deepsleep_ticks = s_analytics_deepsleep_ticks; + + RtcTicks now_ticks = rtc_get_ticks(); + uint32_t total_ticks = (uint32_t)(now_ticks - s_last_ticks); + uint32_t running_ticks = total_ticks - wfi_ticks - deepwfi_ticks - deepsleep_ticks; + + // Calculate percentages + uint16_t running_pct = 0; + uint16_t wfi_pct = 0; + uint16_t deepwfi_pct = 0; + uint16_t deepsleep_pct = 0; + + if (total_ticks > 0) { + running_pct = (uint16_t)((running_ticks * 10000ULL) / total_ticks); + wfi_pct = (uint16_t)((wfi_ticks * 10000ULL) / total_ticks); + deepwfi_pct = (uint16_t)((deepwfi_ticks * 10000ULL) / total_ticks); + deepsleep_pct = (uint16_t)((deepsleep_ticks * 10000ULL) / total_ticks); + } + + // SF32LB52: sleep0 = WFI, sleep1 = Deep WFI, sleep2 = Deep sleep + PBL_ANALYTICS_SET_UNSIGNED(cpu_running_pct, running_pct); + PBL_ANALYTICS_SET_UNSIGNED(cpu_sleep0_pct, wfi_pct); + PBL_ANALYTICS_SET_UNSIGNED(cpu_sleep1_pct, deepwfi_pct); + PBL_ANALYTICS_SET_UNSIGNED(cpu_sleep2_pct, deepsleep_pct); + PBL_ANALYTICS_SET_UNSIGNED(sifli_ipc_not_idle_count, s_analytics_ipc_not_idle_count); + + s_last_ticks = now_ticks; + s_analytics_wfi_ticks = 0; + s_analytics_deepwfi_ticks = 0; + s_analytics_deepsleep_ticks = 0; + s_analytics_ipc_not_idle_count = 0; +} diff --git a/src/fw/soc/sf32lb/sf32lb52x/init.c b/src/fw/soc/sf32lb/sf32lb52x/init.c new file mode 100644 index 0000000000..e3a361b1fb --- /dev/null +++ b/src/fw/soc/sf32lb/sf32lb52x/init.c @@ -0,0 +1,108 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "drivers/watchdog.h" +#include "system/passert.h" + +#include + +#define HCPU_FREQ_MHZ 240 +#define PWRKEY_RESET_CNT (32000 * 15) + +void soc_early_init(void) { + HAL_StatusTypeDef ret; + uint32_t bootopt; + + // Adjust bootrom pull-up/down delays on PA21 (flash power control pin) so + // that the flash is properly power cycled on reset. A flash power cycle is + // needed if left in 4-byte addressing mode, as bootrom does not support it. + bootopt = HAL_Get_backup(RTC_BACKUP_BOOTOPT); + bootopt &= ~(RTC_BACKUP_BOOTOPT_PD_DELAY_Msk | RTC_BACKUP_BOOTOPT_PU_DELAY_Msk); + bootopt |= RTC_BACKUP_BOOTOPT_PD_DELAY_MS(100) | RTC_BACKUP_BOOTOPT_PU_DELAY_MS(10); + HAL_Set_backup(RTC_BACKUP_BOOTOPT, bootopt); + + // Disable default PA21 pull-down, causing leakage + HAL_PIN_Set(PAD_PA21, GPIO_A21, PIN_NOPULL, 1); + + if (HAL_RCC_HCPU_GetClockSrc(RCC_CLK_MOD_SYS) == RCC_SYSCLK_HRC48) { + HAL_HPAON_EnableXT48(); + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_SYS, RCC_SYSCLK_HXT48); + } + + HAL_RCC_HCPU_ClockSelect(RCC_CLK_MOD_HP_PERI, RCC_CLK_PERI_HXT48); + + // Halt LCPU first to avoid LCPU in running state + HAL_HPAON_WakeCore(CORE_ID_LCPU); + HAL_RCC_Reset_and_Halt_LCPU(1); + + // Load system configuration from EFUSE + BSP_System_Config(); + + HAL_HPAON_StartGTimer(); + + HAL_PMU_EnableRC32K(1); + + // Stop and restart WDT in case it was clocked by RC10K before + watchdog_stop(); + + HAL_PMU_LpCLockSelect(PMU_LPCLK_RC32); + +#ifndef CONFIG_NO_WATCHDOG + watchdog_init(); + watchdog_start(); +#endif + + HAL_PMU_EnableDLL(1); +#ifdef SF32LB52_USE_LXT + HAL_PMU_EnableXTAL32(); + ret = HAL_PMU_LXTReady(); + PBL_ASSERTN(ret == HAL_OK); + + HAL_RTC_ENABLE_LXT(); +#endif + + HAL_RCC_LCPU_ClockSelect(RCC_CLK_MOD_LP_PERI, RCC_CLK_PERI_HXT48); + + HAL_HPAON_CANCEL_LP_ACTIVE_REQUEST(); + + ret = HAL_RCC_CalibrateRC48(); + PBL_ASSERTN(ret == HAL_OK); + + HAL_RCC_HCPU_ConfigHCLK(HCPU_FREQ_MHZ); + + // Reset sysclk used by HAL_Delay_us + HAL_Delay_us(0); + + HAL_RCC_Init(); + HAL_PMU_Init(); + + __HAL_SYSCFG_CLEAR_SECURITY(); + HAL_EFUSE_Init(); + + //set Sifli chipset pwrkey reset time to 15s, so it always use PMIC cold reboot for long press + hwp_pmuc->PWRKEY_CNT = PWRKEY_RESET_CNT; + + // Disable 1V8 LDO (feeds PSRAM, we use VDD_SiP to power it) + hwp_pmuc->PERI_LDO &= ~(PMUC_PERI_LDO_EN_LDO18_Msk | PMUC_PERI_LDO_LDO18_PD_Msk); + hwp_pmuc->PERI_LDO |= PMUC_PERI_LDO_LDO18_PD_Msk; + + // Set all PSRAM pins as analog (low-power) + HAL_PIN_Set_Analog(PAD_SA00, 1); + HAL_PIN_Set_Analog(PAD_SA01, 1); + HAL_PIN_Set_Analog(PAD_SA02, 1); + HAL_PIN_Set_Analog(PAD_SA03, 1); + HAL_PIN_Set_Analog(PAD_SA04, 1); + HAL_PIN_Set_Analog(PAD_SA05, 1); + HAL_PIN_Set_Analog(PAD_SA06, 1); + HAL_PIN_Set_Analog(PAD_SA07, 1); + HAL_PIN_Set_Analog(PAD_SA08, 1); + HAL_PIN_Set_Analog(PAD_SA09, 1); + HAL_PIN_Set_Analog(PAD_SA10, 1); + HAL_PIN_Set_Analog(PAD_SA11, 1); + HAL_PIN_Set_Analog(PAD_SA12, 1); + + HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LP2HP_REQ, AON_PIN_MODE_HIGH); + HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LP2HP_IRQ, AON_PIN_MODE_HIGH); + HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LPTIM1, AON_PIN_MODE_HIGH); + HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_GPIO1, AON_PIN_MODE_HIGH); +} diff --git a/src/fw/soc/sf32lb/sf32lb52x/wscript_build b/src/fw/soc/sf32lb/sf32lb52x/wscript_build new file mode 100644 index 0000000000..4742fb386f --- /dev/null +++ b/src/fw/soc/sf32lb/sf32lb52x/wscript_build @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +bld.objects( + name='soc', + source=[ + 'init.c', + 'freertos.c', + ], + use=[ + 'fw_includes', + 'fw_services', + 'hal_sifli', + ], +) diff --git a/src/fw/soc/sf32lb/wscript_build b/src/fw/soc/sf32lb/wscript_build new file mode 100644 index 0000000000..1b5dff095e --- /dev/null +++ b/src/fw/soc/sf32lb/wscript_build @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 +if bld.env.CONFIG_SOC_SF32LB52: + bld.recurse("sf32lb52x") \ No newline at end of file diff --git a/src/fw/soc/wscript_build b/src/fw/soc/wscript_build new file mode 100644 index 0000000000..57fb7ccf5d --- /dev/null +++ b/src/fw/soc/wscript_build @@ -0,0 +1,6 @@ +if bld.env.CONFIG_QEMU: + bld.recurse("qemu") +elif bld.env.CONFIG_SOC_NRF52: + bld.recurse("nrf") +elif bld.env.CONFIG_SOC_SF32LB52: + bld.recurse("sf32lb") \ No newline at end of file diff --git a/src/fw/startup/irq_stm32f7.def b/src/fw/startup/irq_stm32f7.def deleted file mode 100644 index 7d2a831d4f..0000000000 --- a/src/fw/startup/irq_stm32f7.def +++ /dev/null @@ -1,109 +0,0 @@ -IRQ_DEF(WWDG_IRQHandler) // Window WatchDog -IRQ_DEF(PVD_IRQHandler) // PVD through EXTI Line detection -IRQ_DEF(TAMP_STAMP_IRQHandler) // Tamper and TimeStamps through the EXTI line -IRQ_DEF(RTC_WKUP_IRQHandler) // RTC Wakeup through the EXTI line -IRQ_DEF(FLASH_IRQHandler) // FLASH -IRQ_DEF(RCC_IRQHandler) // RCC -IRQ_DEF(EXTI0_IRQHandler) // EXTI Line0 -IRQ_DEF(EXTI1_IRQHandler) // EXTI Line1 -IRQ_DEF(EXTI2_IRQHandler) // EXTI Line2 -IRQ_DEF(EXTI3_IRQHandler) // EXTI Line3 -IRQ_DEF(EXTI4_IRQHandler) // EXTI Line4 -IRQ_DEF(DMA1_Stream0_IRQHandler) // DMA1 Stream 0 -IRQ_DEF(DMA1_Stream1_IRQHandler) // DMA1 Stream 1 -IRQ_DEF(DMA1_Stream2_IRQHandler) // DMA1 Stream 2 -IRQ_DEF(DMA1_Stream3_IRQHandler) // DMA1 Stream 3 -IRQ_DEF(DMA1_Stream4_IRQHandler) // DMA1 Stream 4 -IRQ_DEF(DMA1_Stream5_IRQHandler) // DMA1 Stream 5 -IRQ_DEF(DMA1_Stream6_IRQHandler) // DMA1 Stream 6 -IRQ_DEF(ADC_IRQHandler) // ADC1, ADC2 and ADC3s -IRQ_DEF(CAN1_TX_IRQHandler) // CAN1 TX -IRQ_DEF(CAN1_RX0_IRQHandler) // CAN1 RX0 -IRQ_DEF(CAN1_RX1_IRQHandler) // CAN1 RX1 -IRQ_DEF(CAN1_SCE_IRQHandler) // CAN1 SCE -IRQ_DEF(EXTI9_5_IRQHandler) // External Line[9:5]s -IRQ_DEF(TIM1_BRK_TIM9_IRQHandler) // TIM1 Break and TIM9 -IRQ_DEF(TIM1_UP_TIM10_IRQHandler) // TIM1 Update and TIM10 -IRQ_DEF(TIM1_TRG_COM_TIM11_IRQHandler) // TIM1 Trigger and Commutation and TIM11 -IRQ_DEF(TIM1_CC_IRQHandler) // TIM1 Capture Compare -IRQ_DEF(TIM2_IRQHandler) // TIM2 -IRQ_DEF(TIM3_IRQHandler) // TIM3 -IRQ_DEF(TIM4_IRQHandler) // TIM4 -IRQ_DEF(I2C1_EV_IRQHandler) // I2C1 Event -IRQ_DEF(I2C1_ER_IRQHandler) // I2C1 Error -IRQ_DEF(I2C2_EV_IRQHandler) // I2C2 Event -IRQ_DEF(I2C2_ER_IRQHandler) // I2C2 Error -IRQ_DEF(SPI1_IRQHandler) // SPI1 -IRQ_DEF(SPI2_IRQHandler) // SPI2 -IRQ_DEF(USART1_IRQHandler) // USART1 -IRQ_DEF(USART2_IRQHandler) // USART2 -IRQ_DEF(USART3_IRQHandler) // USART3 -IRQ_DEF(EXTI15_10_IRQHandler) // External Line[15:10]s -IRQ_DEF(RTC_Alarm_IRQHandler) // RTC Alarm (A and B) through EXTI Line -IRQ_DEF(OTG_FS_WKUP_IRQHandler) // USB OTG FS Wakeup through EXTI line -IRQ_DEF(TIM8_BRK_TIM12_IRQHandler) // TIM8 Break and TIM12 -IRQ_DEF(TIM8_UP_TIM13_IRQHandler) // TIM8 Update and TIM13 -IRQ_DEF(TIM8_TRG_COM_TIM14_IRQHandler) // TIM8 Trigger and Commutation and TIM14 -IRQ_DEF(TIM8_CC_IRQHandler) // TIM8 Capture Compare -IRQ_DEF(DMA1_Stream7_IRQHandler) // DMA1 Stream7 -IRQ_DEF(FMC_IRQHandler) // FMC -IRQ_DEF(SDMMC1_IRQHandler) // SDMMC1 -IRQ_DEF(TIM5_IRQHandler) // TIM5 -IRQ_DEF(SPI3_IRQHandler) // SPI3 -IRQ_DEF(UART4_IRQHandler) // UART4 -IRQ_DEF(UART5_IRQHandler) // UART5 -IRQ_DEF(TIM6_DAC_IRQHandler) // TIM6 and DAC1&2 underrun errors -IRQ_DEF(TIM7_IRQHandler) // TIM7 -IRQ_DEF(DMA2_Stream0_IRQHandler) // DMA2 Stream 0 -IRQ_DEF(DMA2_Stream1_IRQHandler) // DMA2 Stream 1 -IRQ_DEF(DMA2_Stream2_IRQHandler) // DMA2 Stream 2 -IRQ_DEF(DMA2_Stream3_IRQHandler) // DMA2 Stream 3 -IRQ_DEF(DMA2_Stream4_IRQHandler) // DMA2 Stream 4 -IRQ_DEF(ETH_IRQHandler) // Ethernet -IRQ_DEF(ETH_WKUP_IRQHandler) // Ethernet Wakeup through EXTI line -IRQ_DEF(CAN2_TX_IRQHandler) // CAN2 TX -IRQ_DEF(CAN2_RX0_IRQHandler) // CAN2 RX0 -IRQ_DEF(CAN2_RX1_IRQHandler) // CAN2 RX1 -IRQ_DEF(CAN2_SCE_IRQHandler) // CAN2 SCE -IRQ_DEF(OTG_FS_IRQHandler) // USB OTG FS -IRQ_DEF(DMA2_Stream5_IRQHandler) // DMA2 Stream 5 -IRQ_DEF(DMA2_Stream6_IRQHandler) // DMA2 Stream 6 -IRQ_DEF(DMA2_Stream7_IRQHandler) // DMA2 Stream 7 -IRQ_DEF(USART6_IRQHandler) // USART6 -IRQ_DEF(I2C3_EV_IRQHandler) // I2C3 event -IRQ_DEF(I2C3_ER_IRQHandler) // I2C3 error -IRQ_DEF(OTG_HS_EP1_OUT_IRQHandler) // USB OTG HS End Point 1 Out -IRQ_DEF(OTG_HS_EP1_IN_IRQHandler) // USB OTG HS End Point 1 In -IRQ_DEF(OTG_HS_WKUP_IRQHandler) // USB OTG HS Wakeup through EXTI -IRQ_DEF(OTG_HS_IRQHandler) // USB OTG HS -IRQ_DEF(DCMI_IRQHandler) // DCMI -IRQ_DEF(CRYP_IRQHandler) // CRYP crypto -IRQ_DEF(HASH_RNG_IRQHandler) // Hash and Rng -IRQ_DEF(FPU_IRQHandler) // FPU -IRQ_DEF(UART7_IRQHandler) // UART7 -IRQ_DEF(UART8_IRQHandler) // UART8 -IRQ_DEF(SPI4_IRQHandler) // SPI4 -IRQ_DEF(SPI5_IRQHandler) // SPI5 -IRQ_DEF(SPI6_IRQHandler) // SPI6 -IRQ_DEF(SAI1_IRQHandler) // SAI1 -IRQ_DEF(LTDC_IRQHandler) // LTDC_IRQHandler -IRQ_DEF(LTDC_ER_IRQHandler) // LTDC_ER_IRQHandler -IRQ_DEF(DMA2D_IRQHandler) // DMA2D -IRQ_DEF(SAI2_IRQHandler) // SAI2 -IRQ_DEF(QUADSPI_IRQHandler) // Quad SPI -IRQ_DEF(LPTIM1_IRQHandler) // LP TIM1 -IRQ_DEF(CEC_IRQHandler) // HDMI-CEC -IRQ_DEF(I2C4_EV_IRQHandler) // I2C4 Event -IRQ_DEF(I2C4_ER_IRQHandler) // I2C4 Error -IRQ_DEF(SPDIF_RX_IRQHandler) // SPDIF-RX -IRQ_DEF(DFSDM0_IRQHandler) // DFSDM Filter1 -IRQ_DEF(DFSDM1_IRQHandler) // DFSDM Filter2 -IRQ_DEF(DFSDM2_IRQHandler) // DFSDM Filter3 -IRQ_DEF(DFSDM3_IRQHandler) // DFSDM Filter4 -IRQ_DEF(SDMMC2_IRQHandler) // SDMMC2 -IRQ_DEF(CAN3_TX_IRQHandler) // CAN3 TX -IRQ_DEF(CAN3_RX0_IRQHandler) // CAN3 RX0 -IRQ_DEF(CAN3_RX1_IRQHandler) // CAN3 RX1 -IRQ_DEF(CAN3_SCE_IRQHandler) // CAN3 SCE -IRQ_DEF(JPEG_IRQHandler) // JPEG -IRQ_DEF(MDIOS_IRQHandler) // MDIO Slave diff --git a/src/fw/startup/startup_cortex_m.c b/src/fw/startup/startup_cortex_m.c new file mode 100644 index 0000000000..eca3d9c292 --- /dev/null +++ b/src/fw/startup/startup_cortex_m.c @@ -0,0 +1,50 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +//! Initial firmware startup, contains the vector table that the bootloader loads. +//! Based on "https://github.com/pfalcon/cortex-uni-startup/blob/master/startup.c" +//! by Paul Sokolovsky (public domain) + +#include +#include +#include +#include "mcu/cache.h" +#include "util/attributes.h" + +//! These symbols are defined in the linker script for use in initializing +//! the data sections. uint8_t since we do arithmetic with section lengths. +//! These are arrays to avoid the need for an & when dealing with linker symbols. +extern uint8_t __data_load_start[]; +extern uint8_t __data_start[]; +extern uint8_t __data_end[]; +extern uint8_t __bss_start[]; +extern uint8_t __bss_end[]; +extern uint8_t _estack[]; + +//! Firmware main function, ResetHandler calls this +extern int main(void); + +//! SoC-specific system initialization (e.g. nRF52 system_nrf52840.c) +extern void SystemInit(void); + +//! This function is what gets called when the processor first +//! starts execution following a reset event. The data and bss +//! sections are initialized, then we call the firmware's main +//! function +NORETURN Reset_Handler(void) { + // Copy data section from flash to RAM + memcpy(__data_start, __data_load_start, __data_end - __data_start); + + // Clear the bss section, assumes .bss goes directly after .data + memset(__bss_start, 0, __bss_end - __bss_start); + + SystemInit(); + + icache_enable(); + dcache_enable(); + + main(); + + // Main shouldn't return + while (true) {} +} diff --git a/src/fw/startup/startup_qemu.c b/src/fw/startup/startup_qemu.c new file mode 100644 index 0000000000..233b8b8335 --- /dev/null +++ b/src/fw/startup/startup_qemu.c @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +#include "util/attributes.h" + +//! These symbols are defined in the linker script for use in initializing +//! the data sections. +extern uint8_t __data_load_start[]; +extern uint8_t __data_start[]; +extern uint8_t __data_end[]; +extern uint8_t __bss_start[]; +extern uint8_t __bss_end[]; +extern uint8_t __isr_stack_start__[]; + +extern int main(void); + +NORETURN prv_startup(void) { + // Copy data section from flash to RAM + for (int i = 0; i < (__data_end - __data_start); i++) { + __data_start[i] = __data_load_start[i]; + } + + // Clear the bss section + memset(__bss_start, 0, __bss_end - __bss_start); + + main(); + + while (1) {} +} + +NAKED_FUNC NORETURN Reset_Handler(void) { + __asm volatile( + // Set MSPLIM to protect the ISR stack (Cortex-M33) + "ldr r0, =__isr_stack_start__ \n" + "msr msplim, r0 \n" + // Clear PSPLIM - set per-task by FreeRTOS + "mov r0, #0 \n" + "msr psplim, r0 \n" + // Jump to C startup + "b prv_startup \n" + ); +} diff --git a/src/fw/startup/startup_sf32lb52.c b/src/fw/startup/startup_sf32lb52.c index 4c4c0e53c7..de07eacaf1 100644 --- a/src/fw/startup/startup_sf32lb52.c +++ b/src/fw/startup/startup_sf32lb52.c @@ -1,26 +1,13 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include "system/passert.h" #include "util/attributes.h" -#define SF32LB52_COMPATIBLE -#include "mcu.h" +#include +#include //! These symbols are defined in the linker script for use in initializing //! the data sections. uint8_t since we do arithmetic with section lengths. @@ -31,18 +18,18 @@ extern uint8_t __data_end[]; extern uint8_t __bss_start[]; extern uint8_t __bss_end[]; -extern uint8_t __retm_ro_load_start[]; -extern uint8_t __retm_ro_start[]; -extern uint8_t __retm_ro_end[]; +extern uint8_t __ramfunc_load_start[]; +extern uint8_t __ramfunc_start[]; +extern uint8_t __ramfunc_end[]; + +extern uint8_t __isr_stack_start__[]; extern int main(void); NAKED_FUNC NORETURN Reset_Handler(void) { - SCB_EnableICache(); - SCB_EnableDCache(); - - // FIXME(SF32LB52): Set stack limits - __set_MSPLIM((uint32_t)(0)); + // Set MSPLIM to protect the ISR stack + __set_MSPLIM((uint32_t)__isr_stack_start__); + // PSPLIM is set per-task by FreeRTOS during context switches __set_PSPLIM((uint32_t)(0)); // Copy data section from flash to RAM @@ -50,8 +37,8 @@ NAKED_FUNC NORETURN Reset_Handler(void) { __data_start[i] = __data_load_start[i]; } - for (int i = 0; i < (__retm_ro_end - __retm_ro_start); i++) { - __retm_ro_start[i] = __retm_ro_load_start[i]; + for (int i = 0; i < (__ramfunc_end - __ramfunc_start); i++) { + __ramfunc_start[i] = __ramfunc_load_start[i]; } // Clear the bss section, assumes .bss goes directly after .data diff --git a/src/fw/startup/startup_stm32.c b/src/fw/startup/startup_stm32.c deleted file mode 100644 index 8e05d4b167..0000000000 --- a/src/fw/startup/startup_stm32.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! Initial firmware startup, contains the vector table that the bootloader loads. -//! Based on "https://github.com/pfalcon/cortex-uni-startup/blob/master/startup.c" -//! by Paul Sokolovsky (public domain) - -#include -#include -#include -#include "mcu/cache.h" -#include "util/attributes.h" - -//! These symbols are defined in the linker script for use in initializing -//! the data sections. uint8_t since we do arithmetic with section lengths. -//! These are arrays to avoid the need for an & when dealing with linker symbols. -extern uint8_t __data_load_start[]; -extern uint8_t __data_start[]; -extern uint8_t __data_end[]; -extern uint8_t __bss_start[]; -extern uint8_t __bss_end[]; -extern uint8_t _estack[]; - -#if MICRO_FAMILY_STM32F7 -extern uint8_t __dtcm_bss_start[]; -extern uint8_t __dtcm_bss_end[]; -#endif - -//! Firmware main function, ResetHandler calls this -extern int main(void); - -//! STM32 system initialization function, defined in the standard peripheral library -extern void SystemInit(void); - -//! This function is what gets called when the processor first -//! starts execution following a reset event. The data and bss -//! sections are initialized, then we call the firmware's main -//! function -NORETURN Reset_Handler(void) { - // Copy data section from flash to RAM - memcpy(__data_start, __data_load_start, __data_end - __data_start); - - // Clear the bss section, assumes .bss goes directly after .data - memset(__bss_start, 0, __bss_end - __bss_start); - -#if MICRO_FAMILY_STM32F7 - // Clear the DTCM bss section - memset(__dtcm_bss_start, 0, __dtcm_bss_end - __dtcm_bss_start); -#endif - - SystemInit(); - - icache_enable(); - dcache_enable(); - - main(); - - // Main shouldn't return - while (true) {} -} diff --git a/src/fw/startup/system_stm32f4xx.c b/src/fw/startup/system_stm32f4xx.c deleted file mode 100644 index f83157a3d3..0000000000 --- a/src/fw/startup/system_stm32f4xx.c +++ /dev/null @@ -1,405 +0,0 @@ -/** - ****************************************************************************** - * @file system_stm32f4xx.c - * @author MCD Application Team - * @version V1.8.1 - * @date 27-January-2022 - * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. - * This file contains the system clock configuration for STM32F4xx devices. - * - * 1. This file provides two functions and one global variable to be called from - * user application: - * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier - * and Divider factors, AHB/APBx prescalers and Flash settings), - * depending on the configuration made in the clock xls tool. - * This function is called at startup just after reset and - * before branch to main program. This call is made inside - * the "startup_stm32f4xx.s" file. - * - * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used - * by the user application to setup the SysTick - * timer or configure other parameters. - * - * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and mus - * be called whenever the core clock is changed - * during program execution. - * - * 2. After each device reset the HSI (16 MHz) is used as system clock source. - * Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to - * configure the system clock before to branch to main program. - * - * 3. If the system clock source selected by user fails to startup, the SystemInit() - * function will do nothing and HSI still used as system clock source. User can - * add some code to deal with this issue inside the SetSysClock() function. - * - * 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define - * in "stm32f4xx.h" file. When HSE is used as system clock source, directly or - * through PLL, and you are using different crystal you have to adapt the HSE - * value to your own configuration. - * - * 5. This file configures the system clock as follows: - *============================================================================= - *============================================================================= - * System Clock source | PLL (HSE) - *----------------------------------------------------------------------------- - * SYSCLK(Hz) | 100000000 - *----------------------------------------------------------------------------- - * HCLK(Hz) | 100000000 - *----------------------------------------------------------------------------- - * AHB Prescaler | 1 - *----------------------------------------------------------------------------- - * APB1 Prescaler | 4 - *----------------------------------------------------------------------------- - * APB2 Prescaler | 2 - *----------------------------------------------------------------------------- - * HSE Frequency(Hz) | 25000000 - *----------------------------------------------------------------------------- - * PLL_M | 16 - *----------------------------------------------------------------------------- - * PLL_N | 200 - *----------------------------------------------------------------------------- - * PLL_P | 2 - *----------------------------------------------------------------------------- - * PLL_Q | 15 - *----------------------------------------------------------------------------- - * PLLI2S_N | 192 - *----------------------------------------------------------------------------- - * PLLI2S_R | 5 - *----------------------------------------------------------------------------- - * I2S input clock(Hz) | 38400000 - * | - * To achieve the following I2S config: | - * - Master clock output (MCKO): OFF | - * - Frame wide : 16bit | - * - Audio sampling freq (KHz) : 32 | - * - Error % : 1.3158 | - * - Prescaler Odd factor (ODD): 0 | - * - Linear prescaler (DIV) : 2 | - *----------------------------------------------------------------------------- - * VDD(V) | 1.8 - *----------------------------------------------------------------------------- - * Main regulator output voltage | Scale1 mode - *----------------------------------------------------------------------------- - * Flash Latency(WS) | 4 - *----------------------------------------------------------------------------- - * Prefetch Buffer | OFF - *----------------------------------------------------------------------------- - * Instruction cache | ON - *----------------------------------------------------------------------------- - * Data cache | ON - *----------------------------------------------------------------------------- - * Require 48MHz for USB OTG FS, | Disabled - * SDIO and RNG clock | - *----------------------------------------------------------------------------- - *============================================================================= - ****************************************************************************** - * @attention - * - * Copyright (c) 2016 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ - -/** @addtogroup CMSIS - * @{ - */ - -/** @addtogroup stm32f4xx_system - * @{ - */ - -/** @addtogroup STM32F4xx_System_Private_Includes - * @{ - */ - -#define STM32F4_COMPATIBLE -#include - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_TypesDefinitions - * @{ - */ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Defines - * @{ - */ - -/*!< Uncomment the following line if you need to relocate your vector Table in - Internal SRAM. */ -/* #define VECT_TAB_SRAM */ -#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. - This value must be a multiple of 0x200. */ -/******************************************************************************/ - -/************************* PLL Parameters *************************************/ - -/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ -#define PLL_M 16 - -/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ -#define PLL_Q 15 - -#define PLL_N 200 -/* SYSCLK = PLL_VCO / PLL_P */ -#define PLL_P 2 - -/* PLLI2S_VCO = (HSE_VALUE Or HSI_VALUE / PLL_M) * PLLI2S_N - I2SCLK = PLLI2S_VCO / PLLI2S_R */ -#define PLLI2S_N 192 -#define PLLI2S_R 5 - -/******************************************************************************/ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Macros - * @{ - */ - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Variables - * @{ - */ - -uint32_t SystemCoreClock = 100000000; - -__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes - * @{ - */ - -static void SetSysClock(void); - -/** - * @} - */ - -/** @addtogroup STM32F4xx_System_Private_Functions - * @{ - */ - -/** - * @brief Setup the microcontroller system - * Initialize the Embedded Flash Interface, the PLL and update the - * SystemFrequency variable. - * @param None - * @retval None - */ -void SystemInit(void) -{ - /* Reset the RCC clock configuration to the default reset state ------------*/ - /* Set HSION bit */ - RCC->CR |= (uint32_t)0x00000001; - - /* Reset CFGR register */ - RCC->CFGR = 0x00000000; - - /* Reset HSEON, CSSON and PLLON bits */ - RCC->CR &= (uint32_t)0xFEF6FFFF; - - /* Reset PLLCFGR register */ - RCC->PLLCFGR = 0x24003010; - - /* Reset HSEBYP bit */ - RCC->CR &= (uint32_t)0xFFFBFFFF; - - /* Disable all interrupts */ - RCC->CIR = 0x00000000; - - /* Configure the System clock source, PLL Multiplier and Divider factors, - AHB/APBx prescalers and Flash settings ----------------------------------*/ - SetSysClock(); - - /* Configure the Vector Table location add offset address ------------------*/ -#ifdef VECT_TAB_SRAM - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ -#else - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ -#endif -} - -/** - * @brief Update SystemCoreClock variable according to Clock Register Values. - * The SystemCoreClock variable contains the core clock (HCLK), it can - * be used by the user application to setup the SysTick timer or configure - * other parameters. - * - * @note Each time the core clock (HCLK) changes, this function must be called - * to update SystemCoreClock variable value. Otherwise, any configuration - * based on this variable will be incorrect. - * - * @note - The system frequency computed by this function is not the real - * frequency in the chip. It is calculated based on the predefined - * constant and the selected clock source: - * - * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) - * - * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) - * - * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) - * or HSI_VALUE(*) multiplied/divided by the PLL factors. - * - * (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value - * 16 MHz) but the real value may vary depending on the variations - * in voltage and temperature. - * - * (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value - * 25 MHz), user has to ensure that HSE_VALUE is same as the real - * frequency of the crystal used. Otherwise, this function may - * have wrong result. - * - * - The result of this function could be not correct when using fractional - * value for HSE crystal. - * - * @param None - * @retval None - */ -void SystemCoreClockUpdate(void) -{ - uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; - - /* Get SYSCLK source -------------------------------------------------------*/ - tmp = RCC->CFGR & RCC_CFGR_SWS; - - switch (tmp) - { - case 0x00: /* HSI used as system clock source */ - SystemCoreClock = HSI_VALUE; - break; - case 0x04: /* HSE used as system clock source */ - SystemCoreClock = HSE_VALUE; - break; - case 0x08: /* PLL P used as system clock source */ - /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N - SYSCLK = PLL_VCO / PLL_P - */ - pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; - pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; - - if (pllsource != 0) - { - /* HSE used as PLL clock source */ - pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); - } - else - { - /* HSI used as PLL clock source */ - pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); - } - - pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; - SystemCoreClock = pllvco/pllp; - break; - default: - SystemCoreClock = HSI_VALUE; - break; - } - /* Compute HCLK frequency --------------------------------------------------*/ - /* Get HCLK prescaler */ - tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; - /* HCLK frequency */ - SystemCoreClock >>= tmp; -} - -/** - * @brief Configures the System clock source, PLL Multiplier and Divider factors, - * AHB/APBx prescalers and Flash settings - * @Note This function should be called only once the RCC clock configuration - * is reset to the default reset state (done in SystemInit() function). - * @param None - * @retval None - */ -static void SetSysClock(void) -{ -/******************************************************************************/ -/* PLL (clocked by HSI) used as System clock source */ -/******************************************************************************/ - - /* At this stage the HSI is already enabled and used as System clock source */ - - /* Select regulator voltage output Scale 1 mode */ - RCC->APB1ENR |= RCC_APB1ENR_PWREN; - PWR->CR |= PWR_CR_VOS; - - /* HCLK = SYSCLK / 1*/ - RCC->CFGR |= RCC_CFGR_HPRE_DIV1; - - /* PCLK2 = HCLK / 2*/ - RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; - - /* PCLK1 = HCLK / 4*/ - RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; - - /* Enable the main PLL */ - RCC->CR |= RCC_CR_PLLON; - - /* Wait till the main PLL is ready */ - while((RCC->CR & RCC_CR_PLLRDY) == 0) - { - } - - /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ - FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_4WS; - - /* Select the main PLL as system clock source */ - RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); - RCC->CFGR |= RCC_CFGR_SW_PLL; - - /* Wait till the main PLL is used as system clock source */ - while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL) - { - /* Do nothing. */ - } - -#if STM32F429_439xx // We only use the I2S clock for processors without a DFSDM peripheral - /******************************************************************************/ - /* I2S clock configuration */ - /******************************************************************************/ - /* PLLI2S clock used as I2S clock source */ - RCC->CFGR &= ~RCC_CFGR_I2SSRC; - - /* Configure PLLI2S */ - RCC->PLLI2SCFGR = (PLLI2S_N << 6) | (PLLI2S_R << 28); - - /* Enable PLLI2S */ - RCC->CR |= ((uint32_t)RCC_CR_PLLI2SON); - - /* Wait till PLLI2S is ready */ - while((RCC->CR & RCC_CR_PLLI2SRDY) == 0) { - } -#endif -} - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ diff --git a/src/fw/startup/wscript_build b/src/fw/startup/wscript_build index bb31305d0b..34a7160cc4 100644 --- a/src/fw/startup/wscript_build +++ b/src/fw/startup/wscript_build @@ -1,18 +1,11 @@ sources = [] -if bld.is_tintin(): - sources.append(bld.path.make_node("startup_stm32.c")) - sources.append(bld.path.make_node("system_stm32f2xx.c")) -elif bld.is_snowy_compatible() or bld.is_silk(): - sources.append(bld.path.make_node("startup_stm32.c")) - sources.append(bld.path.make_node("system_stm32f4xx.c")) -elif bld.is_cutts() or bld.is_robert(): - sources.append(bld.path.make_node("startup_stm32.c")) - sources.append(bld.path.make_node("system_stm32f7xx.c")) -elif bld.is_asterix(): +if bld.env.CONFIG_BOARD_FAMILY_ASTERIX: sources.append(bld.path.make_node("../../../third_party/hal_nordic/nrfx/mdk/system_nrf52840.c")) - sources.append(bld.path.make_node("startup_stm32.c")) # good enough -elif bld.is_obelix(): + sources.append(bld.path.make_node("startup_cortex_m.c")) +elif bld.env.CONFIG_BOARD_FAMILY_OBELIX or bld.env.CONFIG_BOARD_FAMILY_GETAFIX: sources.append(bld.path.make_node("startup_sf32lb52.c")) +elif bld.env.CONFIG_QEMU: + sources.append(bld.path.make_node("startup_qemu.c")) else: bld.fatal("No clock configuration file specified for this platform") diff --git a/src/fw/stm32f2xx_flash_fw.ld.template b/src/fw/stm32f2xx_flash_fw.ld.template deleted file mode 100644 index beb4d13825..0000000000 --- a/src/fw/stm32f2xx_flash_fw.ld.template +++ /dev/null @@ -1,27 +0,0 @@ -MEMORY -{ - KERNEL_RAM (xrw) : ORIGIN = @KERNEL_RAM_ADDR@, LENGTH = @KERNEL_RAM_SIZE@ - WORKER_RAM (xrw) : ORIGIN = @WORKER_RAM_ADDR@, LENGTH = @WORKER_RAM_SIZE@ - APP_RAM (xrw) : ORIGIN = @APP_RAM_ADDR@, LENGTH = @APP_RAM_SIZE@ - - /* Bootloader now ends at 0x8008000, once we're sure we won't break */ - /* current watches, we should take advantage of this free space. */ - FLASH (rx) : ORIGIN = @FW_FLASH_ORIGIN@, LENGTH = @FW_FLASH_LENGTH@ -} - -__FLASH_start__ = @FLASH_ORIGIN@; -__FLASH_size__ = @FLASH_SIZE@; - -REGION_ALIAS("REGION_ISR_STACK", KERNEL_RAM); -REGION_ALIAS("REGION_KERNEL_STACKS", KERNEL_RAM); -REGION_ALIAS("REGION_KERNEL_HEAP", KERNEL_RAM); - -@BOOTLOADER_SYMBOLS@ - -SECTIONS { - .isr_vector : { - ASSERT(. == ORIGIN(FLASH), "Error: Vector table is offset from the start of the FLASH region"); - PROVIDE(__ISR_VECTOR_TABLE__ = .); - KEEP(*(.isr_vector)) - } >FLASH -} diff --git a/src/fw/stm32f412_flash_fw.ld.template b/src/fw/stm32f412_flash_fw.ld.template deleted file mode 100644 index 17f2cbebc4..0000000000 --- a/src/fw/stm32f412_flash_fw.ld.template +++ /dev/null @@ -1,25 +0,0 @@ -MEMORY -{ - KERNEL_RAM (xrw) : ORIGIN = @KERNEL_RAM_ADDR@, LENGTH = @KERNEL_RAM_SIZE@ - WORKER_RAM (xrw) : ORIGIN = @WORKER_RAM_ADDR@, LENGTH = @WORKER_RAM_SIZE@ - APP_RAM (xrw) : ORIGIN = @APP_RAM_ADDR@, LENGTH = @APP_RAM_SIZE@ - - /* Bootloader now ends at 0x8008000, once we're sure we won't break */ - /* current watches, we should take advantage of this free space. */ - FLASH (rx) : ORIGIN = @FW_FLASH_ORIGIN@, LENGTH = @FW_FLASH_LENGTH@ -} - -__FLASH_start__ = @FLASH_ORIGIN@; -__FLASH_size__ = @FLASH_SIZE@; - -REGION_ALIAS("REGION_ISR_STACK", KERNEL_RAM); -REGION_ALIAS("REGION_KERNEL_STACKS", KERNEL_RAM); -REGION_ALIAS("REGION_KERNEL_HEAP", KERNEL_RAM); - -SECTIONS { - .isr_vector : { - ASSERT(. == ORIGIN(FLASH), "Error: Vector table is offset from the start of the FLASH region"); - PROVIDE(__ISR_VECTOR_TABLE__ = .); - KEEP(*(.isr_vector)) - } >FLASH -} diff --git a/src/fw/stm32f439_flash_fw.ld.template b/src/fw/stm32f439_flash_fw.ld.template deleted file mode 100644 index a5c997c9e9..0000000000 --- a/src/fw/stm32f439_flash_fw.ld.template +++ /dev/null @@ -1,30 +0,0 @@ -MEMORY -{ - KERNEL_RAM (xrw) : ORIGIN = @KERNEL_RAM_ADDR@, LENGTH = @KERNEL_RAM_SIZE@ - WORKER_RAM (xrw) : ORIGIN = @WORKER_RAM_ADDR@, LENGTH = @WORKER_RAM_SIZE@ - APP_RAM (xrw) : ORIGIN = @APP_RAM_ADDR@, LENGTH = @APP_RAM_SIZE@ - - CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64K - - /* Bootloader now ends at 0x8008000, once we're sure we won't break */ - /* current watches, we should take advantage of this free space. */ - FLASH (rx) : ORIGIN = @FW_FLASH_ORIGIN@, LENGTH = @FW_FLASH_LENGTH@ -} - -__FLASH_start__ = @FLASH_ORIGIN@; -__FLASH_size__ = @FLASH_SIZE@; - -__CCM_RAM_start__ = ORIGIN(CCM); -__CCM_RAM_size__ = LENGTH(CCM); - -REGION_ALIAS("REGION_ISR_STACK", KERNEL_RAM); -REGION_ALIAS("REGION_KERNEL_STACKS", CCM); -REGION_ALIAS("REGION_KERNEL_HEAP", CCM); - -SECTIONS { - .isr_vector : { - ASSERT(. == ORIGIN(FLASH), "Error: Vector table is offset from the start of the FLASH region"); - PROVIDE(__ISR_VECTOR_TABLE__ = .); - KEEP(*(.isr_vector)) - } >FLASH -} diff --git a/src/fw/stm32f7xx_flash_fw.ld.template b/src/fw/stm32f7xx_flash_fw.ld.template deleted file mode 100644 index cd5b5e3e6f..0000000000 --- a/src/fw/stm32f7xx_flash_fw.ld.template +++ /dev/null @@ -1,51 +0,0 @@ -MEMORY -{ - /* - * STM32F769 has 512kb total of system RAM - * DTCM-RAM - 128kb - TCM "for critical real-time data" - * 0x20000000 - 0x2001ffff - * SRAM1 - 368kb - AHB - * 0x20020000 - 0x2007bfff - * SRAM2 - 16kb - AHB - * 0x2007c000 - 0x2007ffff - * - * We put a bunch of static variables (tagged with DTCM_BSS) into DTCM and then use both SRAM - * regions as one continuous blob which gives us 384k of RAM to play with. - */ - DTCM (rw) : ORIGIN = 0x20000000, LENGTH = 128K - - KERNEL_RAM (xrw) : ORIGIN = @KERNEL_RAM_ADDR@, LENGTH = @KERNEL_RAM_SIZE@ - WORKER_RAM (xrw) : ORIGIN = @WORKER_RAM_ADDR@, LENGTH = @WORKER_RAM_SIZE@ - APP_RAM (xrw) : ORIGIN = @APP_RAM_ADDR@, LENGTH = @APP_RAM_SIZE@ - - FLASH (rx) : ORIGIN = @FW_FLASH_ORIGIN@, LENGTH = @FW_FLASH_LENGTH@ -} - -__FLASH_start__ = @FLASH_ORIGIN@; -__FLASH_size__ = @FLASH_SIZE@; - -__DTCM_RAM_size__ = LENGTH(DTCM); - -REGION_ALIAS("REGION_ISR_STACK", DTCM); -REGION_ALIAS("REGION_KERNEL_STACKS", DTCM); -REGION_ALIAS("REGION_KERNEL_HEAP", KERNEL_RAM); - -SECTIONS -{ - .isr_vector : { - ASSERT(. == ORIGIN(FLASH), "Error: Vector table is offset from the start of the FLASH region"); - PROVIDE(__ISR_VECTOR_TABLE__ = .); - KEEP(*(.isr_vector)) - } >FLASH - - .dtcm_bss (NOLOAD) : { - __dtcm_bss_start = .; - - *(.dtcm_bss) - *(.dtcm_bss.*) - - . = ALIGN(8); - __dtcm_bss_end = .; - } >DTCM -} - diff --git a/src/fw/syscall/app_pp_syscalls.c b/src/fw/syscall/app_pp_syscalls.c index f145a5690a..dde338eef8 100644 --- a/src/fw/syscall/app_pp_syscalls.c +++ b/src/fw/syscall/app_pp_syscalls.c @@ -1,32 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_message/app_message_internal.h" #include "process_management/app_install_manager.h" #include "process_management/app_manager.h" -#include "services/common/analytics/analytics.h" -#include "services/common/comm_session/app_session_capabilities.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/comm_session/app_session_capabilities.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" #include "syscall/syscall_internal.h" #include #include - static bool prv_is_endpoint_allowed(uint16_t endpoint_id) { return (endpoint_id == APP_MESSAGE_ENDPOINT_ID); } @@ -55,8 +41,6 @@ DEFINE_SYSCALL(bool, sys_app_pp_send_data, CommSession *session, uint16_t endpoi const AppInstallId app_id = app_manager_get_current_app_id(); app_install_mark_prioritized(app_id, true /* can_expire */); - analytics_add(ANALYTICS_APP_METRIC_MSG_BYTE_OUT_COUNT, length, AnalyticsClient_App); - // TODO: apply some heuristic to decide whether to put connection in fast mode or not: // https://pebbletechnology.atlassian.net/browse/PBL-21538 comm_session_set_responsiveness(session, BtConsumerPpAppMessage, ResponseTimeMin, @@ -69,8 +53,4 @@ DEFINE_SYSCALL(bool, sys_app_pp_send_data, CommSession *session, uint16_t endpoi DEFINE_SYSCALL(bool, sys_app_pp_has_capability, CommSessionCapability capability) { return comm_session_current_app_session_cache_has_capability(capability); -} - -DEFINE_SYSCALL(void, sys_app_pp_app_message_analytics_count_drop, void) { - analytics_inc(ANALYTICS_APP_METRIC_MSG_DROP_COUNT, AnalyticsClient_App); -} +} \ No newline at end of file diff --git a/src/fw/syscall/app_worker_syscalls.c b/src/fw/syscall/app_worker_syscalls.c index 235ebb0ceb..c7da5b7fd1 100644 --- a/src/fw/syscall/app_worker_syscalls.c +++ b/src/fw/syscall/app_worker_syscalls.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_worker.h" #include "kernel/event_loop.h" diff --git a/src/fw/syscall/ble_syscalls.c b/src/fw/syscall/ble_syscalls.c index 617460bb29..dcecf23422 100644 --- a/src/fw/syscall/ble_syscalls.c +++ b/src/fw/syscall/ble_syscalls.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "syscall/syscall_internal.h" @@ -126,14 +113,20 @@ DEFINE_SYSCALL(bool, sys_ble_client_get_notification_value_length, DEFINE_SYSCALL(void, sys_ble_client_consume_read, uintptr_t object_ref, uint8_t value_out[], uint16_t *value_length_in_out) { + // Snapshot the user-supplied length once so the buffer-size used for + // validation is the same one passed down to gatt_client_consume_read_response. + // Re-reading *value_length_in_out a second time would let a racing app thread + // grow it after we validated the buffer. + uint16_t value_length; if (PRIVILEGE_WAS_ELEVATED) { syscall_assert_userspace_buffer(value_length_in_out, sizeof(*value_length_in_out)); - syscall_assert_userspace_buffer(value_out, *value_length_in_out); + value_length = *value_length_in_out; + syscall_assert_userspace_buffer(value_out, value_length); + } else { + value_length = *value_length_in_out; } - gatt_client_consume_read_response(object_ref, - value_out, *value_length_in_out, - GAPLEClientApp); + gatt_client_consume_read_response(object_ref, value_out, value_length, GAPLEClientApp); } DEFINE_SYSCALL(bool, sys_ble_client_consume_notification, uintptr_t *object_ref_out, @@ -141,21 +134,38 @@ DEFINE_SYSCALL(bool, sys_ble_client_consume_notification, uintptr_t *object_ref_ uint16_t *value_length_in_out, bool *has_more_out) { + // Same snapshot pattern as sys_ble_client_consume_read: the inner function + // dereferences value_length_in_out multiple times and gates its memcpy() on + // it, so a racing app could let validation pass on a small value and the + // memcpy land past the validated buffer. Use a kernel-local copy, then write + // back the result the inner function would have stored through the pointer. + uint16_t value_length; if (PRIVILEGE_WAS_ELEVATED) { syscall_assert_userspace_buffer(object_ref_out, sizeof(*object_ref_out)); syscall_assert_userspace_buffer(value_length_in_out, sizeof(*value_length_in_out)); - syscall_assert_userspace_buffer(value_out, *value_length_in_out); + value_length = *value_length_in_out; + syscall_assert_userspace_buffer(value_out, value_length); syscall_assert_userspace_buffer(has_more_out, sizeof(*has_more_out)); + } else { + value_length = *value_length_in_out; } - return gatt_client_subscriptions_consume_notification(object_ref_out, - value_out, value_length_in_out, - GAPLEClientApp, has_more_out); + bool result = gatt_client_subscriptions_consume_notification( + object_ref_out, value_out, &value_length, GAPLEClientApp, has_more_out); + *value_length_in_out = value_length; + return result; } DEFINE_SYSCALL(BTErrno, sys_ble_client_write, BLECharacteristic characteristic, const uint8_t *value, size_t value_length) { + // gatt_client_op_write hands `value`/`value_length` straight to the BLE + // driver, which transmits those bytes to the peer. Without this check an + // app can point `value` at any kernel address and use the remote device as + // a sink for arbitrary kernel memory. + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(value, value_length); + } return gatt_client_op_write(characteristic, value, value_length, GAPLEClientApp); } @@ -163,6 +173,9 @@ DEFINE_SYSCALL(BTErrno, sys_ble_client_write_without_response, BLECharacteristic characteristic, const uint8_t *value, size_t value_length) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(value, value_length); + } return gatt_client_op_write_without_response(characteristic, value, value_length, GAPLEClientApp); } @@ -177,6 +190,9 @@ DEFINE_SYSCALL(BTErrno, sys_ble_client_subscribe, BLECharacteristic characterist DEFINE_SYSCALL(BTErrno, sys_ble_client_write_descriptor, BLEDescriptor descriptor, const uint8_t *value, size_t value_length) { + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(value, value_length); + } return gatt_client_op_write_descriptor(descriptor, value, value_length, GAPLEClientApp); } diff --git a/src/fw/syscall/event_syscalls.c b/src/fw/syscall/event_syscalls.c index b7337a5e2a..5a5f23d33b 100644 --- a/src/fw/syscall/event_syscalls.c +++ b/src/fw/syscall/event_syscalls.c @@ -1,40 +1,50 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "kernel/kernel_applib_state.h" #include "kernel/memory_layout.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" #include "process_state/app_state/app_state.h" -#include "services/common/compositor/compositor.h" -#include "services/common/event_service.h" +#include "process_state/worker_state/worker_state.h" +#include "pbl/services/compositor/compositor.h" +#include "pbl/services/event_service.h" #include "syscall/syscall_internal.h" #include "system/logging.h" #include "system/passert.h" static void prv_put_event_from_process(PebbleTask task, PebbleEvent *event) { if (!event_try_put_from_process(task, event)) { - PBL_LOG(LOG_LEVEL_WARNING, "%s: From app queue is full! Dropped %p! Killing App", + PBL_LOG_WRN("%s: From app queue is full! Dropped %p! Killing App", (task == PebbleTask_App ? "App" : "Worker"), event); syscall_failed(); } } +// Event types that an unprivileged app or worker is permitted to post directly via +// sys_send_pebble_event_to_kernel. Anything else (in particular events that carry a +// kernel-invoked callback pointer, like PEBBLE_CALLBACK_EVENT) is forbidden — the +// kernel would otherwise jump to an app-controlled address with kernel privileges. +static bool prv_event_type_allowed_from_user(PebbleEventType type) { + switch (type) { + case PEBBLE_PROCESS_KILL_EVENT: + case PEBBLE_RENDER_READY_EVENT: + case PEBBLE_DICTATION_EVENT: + case PEBBLE_HEALTH_SERVICE_EVENT: + case PEBBLE_PLUGIN_SERVICE_EVENT: + return true; + default: + return false; + } +} + DEFINE_SYSCALL(void, sys_send_pebble_event_to_kernel, PebbleEvent* event) { if (PRIVILEGE_WAS_ELEVATED) { syscall_assert_userspace_buffer(event, sizeof(*event)); + if (!prv_event_type_allowed_from_user(event->type)) { + PBL_LOG_ERR("Rejecting event type %u from unprivileged caller", event->type); + syscall_failed(); + } } PebbleTask task = pebble_task_get_current(); @@ -62,12 +72,38 @@ DEFINE_SYSCALL(void, sys_current_process_schedule_callback, } DEFINE_SYSCALL(uint32_t, sys_process_events_waiting, PebbleTask task) { + // process_manager_process_events_waiting -> prv_get_context_for_task + // PBL_ASSERTN()s for anything other than App or Worker. Without this guard + // an unprivileged app can pass any other PebbleTask value and panic the + // kernel from a syscall. Pin the caller to its own task instead. + if (PRIVILEGE_WAS_ELEVATED) { + task = pebble_task_get_current(); + if (task != PebbleTask_App && task != PebbleTask_Worker) { + syscall_failed(); + } + } return process_manager_process_events_waiting(task); } +// The handler structure is supplied by the app and its embedded ListNode pointers +// flow into kernel-mode list operations (notably list_remove on unsubscribe). Without +// validating the prev/next pointers, an app can craft them to point into kernel +// memory and use list_remove as a 4-byte arbitrary-write primitive (`*a = b; *b = a`). +static void prv_assert_list_node_in_userspace(const ListNode *node) { + if (node != NULL) { + syscall_assert_userspace_buffer(node, sizeof(*node)); + } +} + +static void prv_assert_handler_list_node_in_userspace(const EventServiceInfo *handler) { + prv_assert_list_node_in_userspace(handler->list_node.prev); + prv_assert_list_node_in_userspace(handler->list_node.next); +} + DEFINE_SYSCALL(void, sys_event_service_client_subscribe, EventServiceInfo *handler) { if (PRIVILEGE_WAS_ELEVATED) { syscall_assert_userspace_buffer(handler, sizeof(*handler)); + prv_assert_handler_list_node_in_userspace(handler); } PebbleTask task = pebble_task_get_current(); @@ -105,9 +141,21 @@ DEFINE_SYSCALL(void, sys_event_service_client_subscribe, EventServiceInfo *handl DEFINE_SYSCALL(void, sys_event_service_client_unsubscribe, EventServiceInfo *state, EventServiceInfo *handler) { + PebbleTask task = pebble_task_get_current(); + if (PRIVILEGE_WAS_ELEVATED) { syscall_assert_userspace_buffer(handler, sizeof(*handler)); - syscall_assert_userspace_buffer(state, sizeof(*state)); + prv_assert_handler_list_node_in_userspace(handler); + // The user-supplied `state` pointer flows into list_find as the list head; an app + // could craft it (and the chain it heads) to walk into kernel memory. Re-derive + // the head from authoritative per-process state instead of trusting the caller. + if (task == PebbleTask_App) { + state = app_state_get_event_service_state(); + } else if (task == PebbleTask_Worker) { + state = worker_state_get_event_service_state(); + } else { + WTF; + } } // Remove from handlers list @@ -117,8 +165,6 @@ DEFINE_SYSCALL(void, sys_event_service_client_unsubscribe, EventServiceInfo *sta // there are other handlers for this task, don't unsubscribe it return; } - // Get info - PebbleTask task = pebble_task_get_current(); // Unsubscribe from the service! PebbleEvent event = { .type = PEBBLE_SUBSCRIPTION_EVENT, diff --git a/src/fw/syscall/profiler_syscalls.c b/src/fw/syscall/profiler_syscalls.c index a00555e534..243e61351e 100644 --- a/src/fw/syscall/profiler_syscalls.c +++ b/src/fw/syscall/profiler_syscalls.c @@ -1,26 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include "syscall/syscall_internal.h" #include "system/profiler.h" -#define CMSIS_COMPATIBLE -#include +#include // ------------------------------------------------------------------------------------ diff --git a/src/fw/syscall/services_syscalls.c b/src/fw/syscall/services_syscalls.c new file mode 100644 index 0000000000..d4c63ddfbf --- /dev/null +++ b/src/fw/syscall/services_syscalls.c @@ -0,0 +1,12 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "syscall/syscall_internal.h" + +DEFINE_SYSCALL(bool, sys_hrm_manager_is_hrm_present) { +#ifdef CONFIG_SERVICE_HRM + return true; +#else + return false; +#endif +} \ No newline at end of file diff --git a/src/fw/syscall/stack_free_syscall.c b/src/fw/syscall/stack_free_syscall.c index a86a966434..c67cfcb045 100644 --- a/src/fw/syscall/stack_free_syscall.c +++ b/src/fw/syscall/stack_free_syscall.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/util/stack_info.h" #include "syscall/syscall_internal.h" diff --git a/src/fw/syscall/syscall.c b/src/fw/syscall/syscall.c index 2e569feecf..1002f9f0f9 100644 --- a/src/fw/syscall/syscall.c +++ b/src/fw/syscall/syscall.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "time.h" @@ -27,7 +14,8 @@ #include "os/tick.h" #include "process_management/app_manager.h" #include "process_management/worker_manager.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" +#include "kernel/logging_private.h" #include "system/logging.h" #include "system/passert.h" #include "util/string.h" @@ -39,7 +27,7 @@ DEFINE_SYSCALL(int, sys_test, int arg) { uint32_t ipsr; __asm volatile("mrs %0, ipsr" : "=r" (ipsr)); - PBL_LOG(LOG_LEVEL_DEBUG, "Inside test kernel function! Privileged? %s Arg %u IPSR: %"PRIu32, + PBL_LOG_DBG("Inside test kernel function! Privileged? %s Arg %u IPSR: %"PRIu32, bool_to_str(mcu_state_is_privileged()), arg, ipsr); return arg * 2; @@ -63,6 +51,17 @@ DEFINE_SYSCALL(RtcTicks, sys_get_ticks, void) { } DEFINE_SYSCALL(void, sys_pbl_log, LogBinaryMessage* log_message, bool async) { + // log_message points at a struct whose trailing message[] is sized by the + // embedded message_length byte. Without a check, an app can hand us any + // kernel address: log_level and message_length steer the formatting code, + // and kernel_pbl_log_flash() ends up writing `sizeof(*log_message) + + // message_length` bytes straight from that address to the flash log channel + // — a generic kernel-memory exfiltration primitive. + if (PRIVILEGE_WAS_ELEVATED) { + syscall_assert_userspace_buffer(log_message, sizeof(*log_message)); + syscall_assert_userspace_buffer(log_message, + sizeof(*log_message) + log_message->message_length); + } kernel_pbl_log(log_message, async); } diff --git a/src/fw/syscall/syscall.h b/src/fw/syscall/syscall.h index 1fb3022173..aeb91698a6 100644 --- a/src/fw/syscall/syscall.h +++ b/src/fw/syscall/syscall.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -37,13 +24,11 @@ #include "kernel/events.h" #include "kernel/logging_private.h" -#include "services/normal/wakeup.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_event.h" -#include "services/common/comm_session/session.h" -#include "services/common/evented_timer.h" -#include "services/normal/activity/activity.h" -#include "services/normal/app_glances/app_glance_service.h" +#include "pbl/services/wakeup.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/app_glances/app_glance_service.h" #include "process_management/pebble_process_info.h" @@ -78,16 +63,11 @@ NORETURN sys_app_fault(uint32_t stashed_lr); bool sys_resource_is_valid(ResAppNum app_num, uint32_t resource_id); size_t sys_resource_size(ResAppNum app_num, uint32_t handle); size_t sys_resource_load_range(ResAppNum app_num, uint32_t h, uint32_t start_bytes, uint8_t *buffer, size_t num_bytes); -void sys_resource_mapped_use(void); -void sys_resource_mapped_release(void); bool sys_resource_bytes_are_readonly(void *bytes); const uint8_t * sys_resource_read_only_bytes(ResAppNum app_num, uint32_t resource_id, size_t *num_bytes_out); -void sys_resource_mapped_use(void); -void sys_resource_mapped_release_many(uint32_t num); - uint32_t sys_resource_get_and_cache(ResAppNum app_num, uint32_t resource_id); NORETURN sys_exit(void); @@ -104,6 +84,24 @@ void sys_vibe_history_stop_collecting(void); bool sys_vibe_history_was_vibrating(uint64_t time_search); int32_t sys_vibe_get_vibe_strength(void); +// Speaker syscalls +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/speaker/track.h" +bool sys_speaker_play_note_seq(const SpeakerNote *notes, uint32_t num_notes, + uint8_t priority, uint8_t volume); +bool sys_speaker_play_tone(uint16_t freq_hz, uint16_t duration_ms, + uint8_t waveform, uint8_t velocity, + uint8_t priority, uint8_t volume); +bool sys_speaker_play_tracks(const SpeakerTrack *tracks, uint32_t num_tracks, + uint8_t priority, uint8_t volume); +bool sys_speaker_stream_open(uint8_t priority, uint8_t volume, uint8_t format); +uint32_t sys_speaker_stream_write(const void *data, uint32_t num_bytes); +void sys_speaker_stream_close(void); +void sys_speaker_stop(void); +void sys_speaker_set_volume(uint8_t volume); +uint8_t sys_speaker_get_state(void); +void sys_speaker_register_finish(void); +bool sys_speaker_is_muted(void); void sys_get_app_uuid(Uuid *uuid); bool sys_app_is_watchface(void); @@ -118,8 +116,6 @@ Version sys_get_current_app_sdk_version(void); PlatformType sys_get_current_app_sdk_platform(void); bool sys_get_current_app_is_js_allowed(void); -bool sys_get_current_app_is_rocky_app(void); - void sys_app_log(size_t length, void *log_buffer); void sys_event_service_client_subscribe(EventServiceInfo *handler); @@ -189,7 +185,7 @@ void sys_data_logging_finish(DataLoggingSessionRef logging_session); DataLoggingResult sys_data_logging_log(DataLoggingSessionRef logging_session, const void *data, uint32_t num_items); bool sys_clock_is_24h_style(void); -int sys_strftime(char* s, size_t maxsize, const char* format, const struct tm* tim_p, char *locale); +size_t sys_strftime(char* s, size_t maxsize, const char* format, const struct tm* tim_p, char *locale); BatteryChargeState sys_battery_get_charge_state(void); @@ -207,14 +203,20 @@ bool sys_activity_is_initialized(void); void sys_app_comm_set_responsiveness(SniffInterval interval); SniffInterval sys_app_comm_get_sniff_interval(void); +bool sys_light_is_on(void); void sys_light_enable_interaction(void); void sys_light_enable(bool enable); void sys_light_enable_respect_settings(bool enable); void sys_light_reset_to_timed_mode(void); +void sys_light_set_color_rgb888(uint32_t rgb); +void sys_light_set_system_color(void); bool sys_mobile_app_is_connected_debounced(void); bool sys_pebblekit_is_connected_debounced(void); +bool sys_touch_service_is_enabled(void); +void sys_touch_reset(void); + bool sys_app_inbox_service_register(uint8_t *storage, size_t storage_size, AppInboxMessageHandler message_handler, @@ -225,8 +227,6 @@ void sys_app_inbox_service_consume(AppInboxConsumerInfo *consumer_info); void sys_app_outbox_send(const uint8_t *data, size_t length, AppOutboxSentHandler sent_handler, void *cb_ctx); -void sys_app_pp_app_message_analytics_count_drop(void); - bool sys_app_pp_send_data(CommSession *session, uint16_t endpoint_id, const uint8_t* data, uint16_t length); @@ -242,15 +242,6 @@ bool sys_app_glance_update(const Uuid *uuid, const AppGlance *glance); //! @param millis The number of milliseconds to wait for void sys_psleep(int millis); -void sys_analytics_set(AnalyticsMetric metric, uint64_t value, AnalyticsClient client); -void sys_analytics_set_entire_array(AnalyticsMetric metric, const void *value, AnalyticsClient client); -void sys_analytics_add(AnalyticsMetric metric, uint64_t increment, AnalyticsClient client); -void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client); -void sys_analytics_max(AnalyticsMetric metric, int64_t val, AnalyticsClient client); -void sys_analytics_stopwatch_start(AnalyticsMetric metric, AnalyticsClient client); -void sys_analytics_stopwatch_stop(AnalyticsMetric metric); -void sys_analytics_logging_log_event(AnalyticsEventBlob *event_blob); - bool sys_app_worker_is_running(void); AppWorkerResult sys_app_worker_launch(void); AppWorkerResult sys_app_worker_kill(void); @@ -264,6 +255,7 @@ time_t sys_wakeup_query(WakeupId wakeup_id); AppLaunchReason sys_process_get_launch_reason(void); ButtonId sys_process_get_launch_button(void); uint32_t sys_process_get_launch_args(void); +AppQuickLaunchAction sys_process_get_quick_launch_action(void); AppExitReason sys_process_get_exit_reason(void); void sys_process_set_exit_reason(AppExitReason exit_reason); void sys_process_get_wakeup_info(WakeupInfo *info); diff --git a/src/fw/syscall/syscall_internal.c b/src/fw/syscall/syscall_internal.c index dd8cb40514..6ab0037dc4 100644 --- a/src/fw/syscall/syscall_internal.c +++ b/src/fw/syscall/syscall_internal.c @@ -1,24 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "syscall_internal.h" #include "applib/app_logging.h" #include "kernel/memory_layout.h" +#include "kernel/pebble_tasks.h" #include "mcu/privilege.h" +#include "process_management/app_manager.h" +#include "process_management/pebble_process_md.h" #include "process_management/process_loader.h" #include "process_management/process_manager.h" #include "syscall/syscall.h" @@ -28,9 +18,22 @@ #include "FreeRTOS.h" #include "task.h" +#include #include #include +// Run App/Worker syscalls on a dedicated privileged stack instead of the +// caller's small unprivileged one, so a task that exhausts its stack faults +// unprivileged (only that process dies) instead of rebooting the system. +// Enabled on ARMv8-M (needs PSPLIM); ARMv7-M keeps the old behaviour. +#if !defined(SYSCALL_PRIVILEGED_STACK) +# if defined(CONFIG_MPU_TYPE_ARMV8M) +# define SYSCALL_PRIVILEGED_STACK 1 +# else +# define SYSCALL_PRIVILEGED_STACK 0 +# endif +#endif + // Indices into FreeRTOS thread local storage #define TLS_SYSCALL_LR_IDX 0 #define TLS_SYSCALL_SP_IDX 1 @@ -52,13 +55,65 @@ static void prv_set_syscall_lr(uintptr_t new_lr) { vTaskSetThreadLocalStoragePointer(NULL, TLS_SYSCALL_LR_IDX, (void *)new_lr); } +//! Public setter used by mcu_call_unprivileged() to restore the TLS LR slot +//! after the nested SVC inside the trampoline clobbers it. +USED void syscall_set_outer_lr(uintptr_t new_lr) { + prv_set_syscall_lr(new_lr); +} + +//! Run @p fn unprivileged for the duration of one call, then return to the +//! caller still in privileged thread mode. +//! +//! Placed in .syscall_text so the SVC handler's xApplicationIsAllowed- +//! ToRaisePrivilege() check permits the inner `svc 2` to re-elevate after +//! @p fn returns. The inner SVC clobbers the per-task TLS LR slot that the +//! outer syscall's drop-privilege return path reads from, so we snapshot and +//! restore it across the bracket. +EXTERNALLY_VISIBLE NAKED_FUNC USED SECTION(".syscall_text.run_unprivileged") +void mcu_call_unprivileged(void (*fn)(void *), void *ctx) { + __asm volatile ( + " push {r4-r6, lr} \n" + " mov r4, r0 \n" // r4 = fn + " mov r5, r1 \n" // r5 = ctx + + // Snapshot outer syscall's TLS LR; the SVC below will overwrite it. + " bl get_syscall_lr \n" + " mov r6, r0 \n" // r6 = outer TLS LR + + // Drop privilege (CONTROL.nPRIV = 1). + " mov r0, #0 \n" + " bl mcu_state_set_thread_privilege \n" + + // Invoke fn(ctx) unprivileged. MPU-violating accesses fault here. + " mov r0, r5 \n" + " blx r4 \n" + + // Re-raise via svc 2. xApplicationIsAllowedToRaisePrivilege accepts this + // because we are in the .syscall_text region. + " svc 2 \n" + + // The SVC handler's vSetupSyscallRegisters() saved a return-into-this- + // function address into the TLS LR slot and stamped prv_drop_privilege + // onto our stacked LR. Restore the outer syscall's TLS LR so when *that* + // syscall eventually unwinds through prv_drop_privilege it returns to the + // right place. + " mov r0, r6 \n" + " bl syscall_set_outer_lr \n" + + // Pop the real caller's LR (overwriting the prv_drop_privilege value left + // in the register by the SVC handler) and return. + " pop {r4-r6, lr} \n" + " bx lr \n" + ); +} + NORETURN syscall_failed(void) { register uint32_t lr __asm("lr"); uint32_t saved_lr = lr; PBL_ASSERT(mcu_state_is_privileged(), "Insufficient Privileges!"); - PBL_LOG(LOG_LEVEL_WARNING, "Bad syscall!"); + PBL_LOG_WRN("Bad syscall!"); sys_app_fault(saved_lr); @@ -78,10 +133,110 @@ void syscall_assert_userspace_buffer(const void* buf, size_t num_bytes) { } APP_LOG(APP_LOG_LEVEL_ERROR, "syscall failure! %p..%p is not in app space.", buf, (char *)buf + num_bytes); - PBL_LOG(LOG_LEVEL_ERROR, "syscall failure! %p..%p is not in app space.", buf, (char *)buf + num_bytes); + PBL_LOG_ERR("syscall failure! %p..%p is not in app space.", buf, (char *)buf + num_bytes); syscall_failed(); } +#if SYSCALL_PRIVILEGED_STACK +// Dedicated privileged stacks for App/Worker syscalls. Plain .bss statics land +// in the privileged-only .kernel_bss output (KERNEL_RAM): unreadable by app +// code, zeroed at boot. (Not section(".kernel_bss") -- that would orphan them.) +#define SYSCALL_STACK_WORDS 512u // 2 KiB each; size against measured high-water. +static StackType_t s_app_syscall_stack[SYSCALL_STACK_WORDS] __attribute__((aligned(8))); +static StackType_t s_worker_syscall_stack[SYSCALL_STACK_WORDS] __attribute__((aligned(8))); + +// Port hook: top of the current task's dedicated syscall stack (base in +// *base_out), or NULL to keep it on the caller's stack. App + Worker only; +// moddable apps use mcu_call_unprivileged() and stay on their own stack. +uint32_t *xApplicationGetSyscallStack(uintptr_t *base_out) { + StackType_t *stack; + switch (pebble_task_get_current()) { + case PebbleTask_App: +#ifdef CONFIG_MODDABLE_XS + { + const PebbleProcessMd *md = app_manager_get_current_app_md(); + if (md != NULL && md->is_moddable_app) { + return NULL; + } + } +#endif + stack = s_app_syscall_stack; + break; + case PebbleTask_Worker: + stack = s_worker_syscall_stack; + break; + default: + return NULL; + } + *base_out = (uintptr_t)&stack[0]; + return (uint32_t *)&stack[SYSCALL_STACK_WORDS]; +} + +static bool prv_psp_in_syscall_stack(uintptr_t psp, const StackType_t *stack) { + return (psp > (uintptr_t)&stack[0]) && (psp <= (uintptr_t)&stack[SYSCALL_STACK_WORDS]); +} + +// Task SP/PSPLIM to restore if the finishing syscall ran on a dedicated stack, +// packed as (psplim << 32 | sp) to return in r0:r1; 0 = no switch needed. +USED uint64_t syscall_stack_restore_target(void) { + const uintptr_t psp = __get_PSP(); + if (prv_psp_in_syscall_stack(psp, s_app_syscall_stack) || + prv_psp_in_syscall_stack(psp, s_worker_syscall_stack)) { + const uint32_t sp = (uint32_t)prv_get_syscall_sp(); // slot1 = pre-syscall task SP + const uint32_t psplim = (uint32_t)ulTaskGetStackStart(xTaskGetCurrentTaskHandle()); + return ((uint64_t)psplim << 32) | sp; + } + return 0; +} + +// Peak unused bytes on a syscall stack: scan the zeroed .bss from the base to +// the first written word (the high-water). For sizing during validation. +static uint16_t prv_syscall_stack_free_bytes(const StackType_t *stack) { + uint32_t i = 0; + while (i < SYSCALL_STACK_WORDS && stack[i] == 0) { + i++; + } + return (uint16_t)(i * sizeof(StackType_t)); +} + +uint16_t syscall_app_stack_free_bytes(void) { + return prv_syscall_stack_free_bytes(s_app_syscall_stack); +} + +uint16_t syscall_worker_stack_free_bytes(void) { + return prv_syscall_stack_free_bytes(s_worker_syscall_stack); +} + +// Drop privilege and return to the task. If the syscall ran on a dedicated +// stack, restore PSP/PSPLIM to the task stack first (while still privileged). +EXTERNALLY_VISIBLE void NAKED_FUNC USED prv_drop_privilege(void) { + __asm volatile ( + " push {r0, r1} \n" // save syscall return value + " bl process_manager_handle_syscall_exit \n" + " bl get_syscall_lr \n" // r0 = real return address + " push {r0, r1} \n" // save real LR (r1 = pad; keeps 8-byte align) + " bl syscall_stack_restore_target \n" // r0 = restore SP (0 = none), r1 = restore PSPLIM + " mov r2, r0 \n" // r2 = restore SP + " mov r3, r1 \n" // r3 = restore PSPLIM + " pop {r0, r1} \n" // r0 = real LR + " mov r12, r0 \n" // r12 = real LR (caller-saved; no bl follows) + " pop {r0, r1} \n" // r0,r1 = syscall return value + " cbz r2, 1f \n" // skip stack switch if not relocated + " msr psp, r2 \n" // back to the app stack (higher addr; safe vs low limit) + " msr psplim, r3 \n" // restore the app stack limit + " isb \n" + "1: \n" + " mrs r2, control \n" // drop privilege: CONTROL.nPRIV = 1 + " orr r2, r2, #1 \n" + " msr control, r2 \n" + " isb \n" + " bx r12 \n" // return into the app + ); +} +#else +uint16_t syscall_app_stack_free_bytes(void) { return 0xFFFF; } +uint16_t syscall_worker_stack_free_bytes(void) { return 0xFFFF; } + // Drop privileges and return to the address stored in thread local storage // Has to preserve r0 and r1 so the syscall's return value is passed through EXTERNALLY_VISIBLE void NAKED_FUNC USED prv_drop_privilege(void) { @@ -100,6 +255,7 @@ EXTERNALLY_VISIBLE void NAKED_FUNC USED prv_drop_privilege(void) { " bx lr \n" // Leave the syscall ); } +#endif // SYSCALL_PRIVILEGED_STACK // Just jump straight into the drop privilege code EXTERNALLY_VISIBLE void NAKED_FUNC USED prv_drop_privilege_wrapper(void) { diff --git a/src/fw/syscall/syscall_internal.h b/src/fw/syscall/syscall_internal.h index 81d2401025..cf4094f0b2 100644 --- a/src/fw/syscall/syscall_internal.h +++ b/src/fw/syscall/syscall_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -55,6 +42,11 @@ void syscall_assert_userspace_buffer(const void* buf, size_t num_bytes); // Used to implement DEFINE_SYSCALL void syscall_internal_maybe_skip_privilege(void); +//! Unused bytes on the App/Worker syscall stacks (for sizing). Returns 0xFFFF +//! when SYSCALL_PRIVILEGED_STACK is disabled. +uint16_t syscall_app_stack_free_bytes(void); +uint16_t syscall_worker_stack_free_bytes(void); + // Test overrides. // TODO: really implement privilege escalation in unit tests. See PBL-9688 #if defined(UNITTEST) diff --git a/src/fw/syscall/syscall_speaker.c b/src/fw/syscall/syscall_speaker.c new file mode 100644 index 0000000000..18f46763bf --- /dev/null +++ b/src/fw/syscall/syscall_speaker.c @@ -0,0 +1,168 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "syscall/syscall.h" +#include "syscall/syscall_internal.h" + +#include "kernel/pebble_tasks.h" +#include "pbl/services/speaker/speaker_pcm_format.h" +#include "pbl/services/speaker/speaker_service.h" +#include "pbl/services/speaker/note_sequence.h" +#include "pbl/services/speaker/track.h" +#include "system/passert.h" + +#define SPEAKER_MAX_NOTES 256 +#define SPEAKER_MAX_STREAM_WRITE 8192 + +DEFINE_SYSCALL(bool, sys_speaker_play_note_seq, const SpeakerNote *notes, + uint32_t num_notes, uint8_t priority, uint8_t volume) { + if (PRIVILEGE_WAS_ELEVATED) { + if (num_notes > SPEAKER_MAX_NOTES) { + syscall_failed(); + } + syscall_assert_userspace_buffer(notes, num_notes * sizeof(SpeakerNote)); + } + + if (priority > SpeakerPriorityCritical) { + // Apps can only use App priority + priority = SpeakerPriorityApp; + } + + PebbleTask task = pebble_task_get_current(); + speaker_service_set_owner_task(task); + + return speaker_service_play_note_seq(notes, num_notes, + (SpeakerPriority)priority, volume); +} + +DEFINE_SYSCALL(bool, sys_speaker_play_tone, uint16_t freq_hz, + uint16_t duration_ms, uint8_t waveform, uint8_t velocity, + uint8_t priority, uint8_t volume) { + if (priority > SpeakerPriorityCritical) { + priority = SpeakerPriorityApp; + } + + if (waveform >= SpeakerWaveformCount) { + syscall_failed(); + } + + if (velocity > 127) { + syscall_failed(); + } + + PebbleTask task = pebble_task_get_current(); + speaker_service_set_owner_task(task); + + return speaker_service_play_tone(freq_hz, duration_ms, waveform, velocity, + (SpeakerPriority)priority, volume); +} + +DEFINE_SYSCALL(bool, sys_speaker_play_tracks, const SpeakerTrack *tracks, + uint32_t num_tracks, uint8_t priority, uint8_t volume) { + // We need to defend against TOCTOU: speaker_service_play_tracks() re-reads + // each track's num_notes and sample->num_bytes from user memory (e.g. as the + // size argument to kernel_malloc/memcpy that copy the user buffers into + // kernel buffers). A racing app could resize them after our validation and + // overflow either the kernel allocation or the validated source range. + // + // Take a kernel-stack copy of the track table (and embedded sample structs) + // up front and pass the kernel copy downstream. The notes/data pointers + // themselves still aim into user memory, but the sizes are now stable. + SpeakerTrack kernel_tracks[SPEAKER_MAX_TRACKS]; + SpeakerSample kernel_samples[SPEAKER_MAX_TRACKS]; + const SpeakerTrack *tracks_to_play = tracks; + + if (PRIVILEGE_WAS_ELEVATED) { + if (num_tracks == 0 || num_tracks > SPEAKER_MAX_TRACKS) { + syscall_failed(); + } + syscall_assert_userspace_buffer(tracks, num_tracks * sizeof(SpeakerTrack)); + + uint32_t total_sample_bytes = 0; + for (uint32_t i = 0; i < num_tracks; i++) { + kernel_tracks[i] = tracks[i]; + if (kernel_tracks[i].num_notes == 0 || kernel_tracks[i].num_notes > SPEAKER_MAX_NOTES) { + syscall_failed(); + } + syscall_assert_userspace_buffer(kernel_tracks[i].notes, + kernel_tracks[i].num_notes * sizeof(SpeakerNote)); + if (kernel_tracks[i].sample) { + syscall_assert_userspace_buffer(kernel_tracks[i].sample, sizeof(SpeakerSample)); + kernel_samples[i] = *kernel_tracks[i].sample; + if (kernel_samples[i].num_bytes == 0) { + syscall_failed(); + } + total_sample_bytes += kernel_samples[i].num_bytes; + if (total_sample_bytes > SPEAKER_MAX_SAMPLE_BYTES_TOTAL) { + syscall_failed(); + } + syscall_assert_userspace_buffer(kernel_samples[i].data, kernel_samples[i].num_bytes); + kernel_tracks[i].sample = &kernel_samples[i]; + } + } + tracks_to_play = kernel_tracks; + } + + if (priority > SpeakerPriorityCritical) { + priority = SpeakerPriorityApp; + } + + PebbleTask task = pebble_task_get_current(); + speaker_service_set_owner_task(task); + + return speaker_service_play_tracks(tracks_to_play, num_tracks, + (SpeakerPriority)priority, volume); +} + +DEFINE_SYSCALL(bool, sys_speaker_stream_open, uint8_t priority, uint8_t volume, + uint8_t format) { + if (priority > SpeakerPriorityCritical) { + priority = SpeakerPriorityApp; + } + + if (format >= SpeakerPcmFormatCount) { + format = SpeakerPcmFormat_16kHz_16bit; + } + + PebbleTask task = pebble_task_get_current(); + speaker_service_set_owner_task(task); + + return speaker_service_stream_open((SpeakerPriority)priority, volume, + (SpeakerPcmFormat)format); +} + +DEFINE_SYSCALL(uint32_t, sys_speaker_stream_write, const void *data, + uint32_t num_bytes) { + if (PRIVILEGE_WAS_ELEVATED) { + if (num_bytes > 8192) { + syscall_failed(); + } + syscall_assert_userspace_buffer(data, num_bytes); + } + + return speaker_service_stream_write(data, num_bytes); +} + +DEFINE_SYSCALL(void, sys_speaker_stream_close, void) { + speaker_service_stream_close(); +} + +DEFINE_SYSCALL(void, sys_speaker_stop, void) { + speaker_service_stop(); +} + +DEFINE_SYSCALL(void, sys_speaker_set_volume, uint8_t volume) { + speaker_service_set_volume(volume); +} + +DEFINE_SYSCALL(uint8_t, sys_speaker_get_state, void) { + return (uint8_t)speaker_service_get_state(); +} + +DEFINE_SYSCALL(void, sys_speaker_register_finish, void) { + speaker_service_register_finish(pebble_task_get_current()); +} + +DEFINE_SYSCALL(bool, sys_speaker_is_muted, void) { + return speaker_service_is_muted(); +} diff --git a/src/fw/system/bootbits.c b/src/fw/system/bootbits.c index 823c11613f..3ea56ec24d 100644 --- a/src/fw/system/bootbits.c +++ b/src/fw/system/bootbits.c @@ -1,38 +1,33 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system/bootbits.h" +#include "drivers/flash.h" #include "drivers/rtc.h" +#include "flash_region/flash_region.h" #include "system/logging.h" #include "system/version.h" #include "util/crc32.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include +#ifdef CONFIG_SOC_SF32LB52 +#include +#endif + +#if MICRO_FAMILY_STM32F4 +#include +#endif + +#ifdef CONFIG_QEMU +// Provided by the QEMU RTC driver +extern void RTC_WriteBackupRegister(uint32_t reg_id, uint32_t value); +extern uint32_t RTC_ReadBackupRegister(uint32_t reg_id); +#endif #include #include -#if MICRO_FAMILY_NRF5 - +#ifdef CONFIG_SOC_NRF52 static uint32_t __attribute__((section(".retained"))) retained[256 / 4]; void retained_write(uint8_t id, uint32_t value) { @@ -50,7 +45,7 @@ void boot_bit_init(void) { // in-memory value is probably scrambled and should be reset. uint32_t crc32_computed = crc32(0, retained, NRF_RETAINED_REGISTER_CRC * 4); if (crc32_computed != retained[NRF_RETAINED_REGISTER_CRC]) { - PBL_LOG(LOG_LEVEL_WARNING, "Retained register CRC failed: expected CRC %08lx, got CRC %08lx. Clearing bootbits!", crc32_computed, retained[NRF_RETAINED_REGISTER_CRC]); + PBL_LOG_WRN("Retained register CRC failed: expected CRC %08lx, got CRC %08lx. Clearing bootbits!", crc32_computed, retained[NRF_RETAINED_REGISTER_CRC]); memset(retained, 0, sizeof(retained)); } @@ -77,7 +72,7 @@ bool boot_bit_test(BootBitValue bit) { } void boot_bit_dump(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "0x%"PRIx32, retained_read(RTC_BKP_BOOTBIT_DR)); + PBL_LOG_DBG("0x%"PRIx32, retained_read(RTC_BKP_BOOTBIT_DR)); } uint32_t boot_bits_get(void) { @@ -89,19 +84,11 @@ void command_boot_bits_get(void) { dbgserial_putstr_fmt(buffer, sizeof(buffer), "bootbits: 0x%"PRIu32, boot_bits_get()); } -void boot_version_write(void) { - if (boot_version_read() == TINTIN_METADATA.version_timestamp) { - return; - } - retained_write(BOOTLOADER_VERSION_REGISTER, TINTIN_METADATA.version_timestamp); -} - uint32_t boot_version_read(void) { return retained_read(BOOTLOADER_VERSION_REGISTER); } -#elif MICRO_FAMILY_SF32LB52 - +#elif defined(CONFIG_SOC_SF32LB52) void boot_bit_init(void) { if (!boot_bit_test(BOOT_BIT_INITIALIZED)) { HAL_Set_backup(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED); @@ -126,7 +113,7 @@ bool boot_bit_test(BootBitValue bit) { } void boot_bit_dump(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "0x%"PRIx32, HAL_Get_backup(RTC_BKP_BOOTBIT_DR)); + PBL_LOG_DBG("0x%"PRIx32, HAL_Get_backup(RTC_BKP_BOOTBIT_DR)); } uint32_t boot_bits_get(void) { @@ -138,15 +125,36 @@ void command_boot_bits_get(void) { dbgserial_putstr_fmt(buffer, sizeof(buffer), "bootbits: 0x%"PRIu32, boot_bits_get()); } -void boot_version_write(void) { - if (boot_version_read() == TINTIN_METADATA.version_timestamp) { - return; - } - /* RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, TINTIN_METADATA.version_timestamp); */ -} +#define PB_VERSION_MAGIC 0x50425652UL + +struct pb_version { + uint32_t magic; + uint8_t major; + uint8_t minor; + uint8_t patch; + uint8_t tweak; +} __attribute__((packed)); + +_Static_assert(sizeof(struct pb_version) == 8, "pb_version struct must be 8 bytes"); uint32_t boot_version_read(void) { - return 0xABCD1234; + struct pb_version version_data; + uint32_t version; + + flash_read_bytes((uint8_t *)&version_data, + FLASH_REGION_BOOTLOADER_END - sizeof(struct pb_version), + sizeof(struct pb_version)); + + if (version_data.magic != PB_VERSION_MAGIC) { + return 0UL; + } + + version = ((uint32_t)version_data.major << 24) | + ((uint32_t)version_data.minor << 16) | + ((uint32_t)version_data.patch << 8) | + (uint32_t)version_data.tweak; + + return version; } #else @@ -177,7 +185,7 @@ bool boot_bit_test(BootBitValue bit) { } void boot_bit_dump(void) { - PBL_LOG(LOG_LEVEL_DEBUG, "0x%"PRIx32, RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR)); + PBL_LOG_DBG("0x%"PRIx32, RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR)); } uint32_t boot_bits_get(void) { @@ -189,13 +197,6 @@ void command_boot_bits_get(void) { dbgserial_putstr_fmt(buffer, sizeof(buffer), "bootbits: 0x%"PRIu32, boot_bits_get()); } -void boot_version_write(void) { - if (boot_version_read() == TINTIN_METADATA.version_timestamp) { - return; - } - RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, TINTIN_METADATA.version_timestamp); -} - uint32_t boot_version_read(void) { return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER); } diff --git a/src/fw/system/bootbits.h b/src/fw/system/bootbits.h index c4f0e6f87c..6a779923ba 100644 --- a/src/fw/system/bootbits.h +++ b/src/fw/system/bootbits.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -42,8 +29,6 @@ typedef enum BootBitValue { BOOT_BIT_FORCE_PRF = 0x1 << 17, BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18, BOOT_BIT_SHUTDOWN_REQUESTED = 0x1 << 19, //!< Bootloader hard power-off instead of jumping to fw. - BOOT_BIT_BOOTLOADER_TEST_A = 0x1 << 30, - BOOT_BIT_BOOTLOADER_TEST_B = 0x1 << 31, } BootBitValue; void boot_bit_init(); @@ -55,5 +40,4 @@ bool boot_bit_test(BootBitValue bit); void boot_bit_dump(void); uint32_t boot_bits_get(void); -void boot_version_write(void); uint32_t boot_version_read(void); diff --git a/src/fw/system/die.c b/src/fw/system/die.c index 6dca92c23e..914684b69d 100644 --- a/src/fw/system/die.c +++ b/src/fw/system/die.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/vibe.h" #include "kernel/core_dump.h" @@ -23,23 +10,28 @@ #include "system/reboot_reason.h" #include "system/reset.h" -#define CMSIS_COMPATIBLE -#include +#include -#if defined(NO_WATCHDOG) +#if defined(CONFIG_NO_WATCHDOG) #include "FreeRTOS.h" -#include "debug/setup.h" #endif -NORETURN reset_due_to_software_failure(void) { -#if PULSE_EVERYWHERE +void prepare_for_software_failure(void) { +#ifdef CONFIG_PULSE_EVERYWHERE pulse_logging_log_buffer_flush(); #endif -#if defined(NO_WATCHDOG) +#ifndef CONFIG_MFG + boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); +#endif +} + +NORETURN reset_due_to_software_failure(void) { + prepare_for_software_failure(); + +#if defined(CONFIG_NO_WATCHDOG) // Don't reset right away, leave it in a state we can inspect - enable_mcu_debugging(); __disable_irq(); while (1) { continue; @@ -47,6 +39,5 @@ NORETURN reset_due_to_software_failure(void) { #endif PBL_LOG_FROM_FAULT_HANDLER("Resetting!"); - boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED); system_reset(); } diff --git a/src/fw/system/die.h b/src/fw/system/die.h index 27105c0ca1..712334e3ca 100644 --- a/src/fw/system/die.h +++ b/src/fw/system/die.h @@ -1,23 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "util/attributes.h" +void prepare_for_software_failure(void); + //! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were //! able shut everything down nicely before rebooting. NORETURN reset_due_to_software_failure(void); diff --git a/src/fw/system/firmware_storage.c b/src/fw/system/firmware_storage.c index 58cb02eb55..7b0e4b0a2d 100644 --- a/src/fw/system/firmware_storage.c +++ b/src/fw/system/firmware_storage.c @@ -1,25 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "firmware_storage.h" #include "drivers/flash.h" +#include "flash_region/flash_region.h" #include "system/logging.h" +#include "util/math.h" -#if !CAPABILITY_HAS_PBLBOOT +#ifndef CONFIG_PBLBOOT FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) { FirmwareDescription firmware_description; flash_read_bytes((uint8_t*) &firmware_description, firmware_start_address, @@ -38,17 +27,12 @@ bool firmware_storage_check_valid_firmware_description( } // Log around this operation, as it can take some time (hundreds of ms) - PBL_LOG(LOG_LEVEL_DEBUG, "CRCing recovery..."); + PBL_LOG_DBG("CRCing recovery..."); start_address += sizeof(FirmwareDescription); -#if CAPABILITY_HAS_DEFECTIVE_FW_CRC - const uint32_t calculated_crc = flash_calculate_legacy_defective_checksum( - start_address, firmware_description->firmware_length); -#else const uint32_t calculated_crc = flash_crc32(start_address, firmware_description->firmware_length); -#endif - PBL_LOG(LOG_LEVEL_DEBUG, "CRCing recovery... done"); + PBL_LOG_DBG("CRCing recovery... done"); return calculated_crc == firmware_description->checksum; } @@ -69,13 +53,28 @@ bool firmware_storage_check_valid_firmware_header( } // Log around this operation, as it can take some time (hundreds of ms) - PBL_LOG(LOG_LEVEL_DEBUG, "CRCing recovery..."); + PBL_LOG_DBG("CRCing recovery..."); const uint32_t calculated_crc = flash_crc32(address + header->fw_start, header->fw_length); - PBL_LOG(LOG_LEVEL_DEBUG, "CRCing recovery... done"); + PBL_LOG_DBG("CRCing recovery... done"); return calculated_crc == header->fw_crc; } +void firmware_storage_invalidate_firmware_slot(uint8_t slot) { + uint32_t slot_start; + + if (slot == 0U) { + slot_start = FLASH_REGION_FIRMWARE_SLOT_0_BEGIN; + } else { + slot_start = FLASH_REGION_FIRMWARE_SLOT_1_BEGIN; + } + + flash_region_erase_optimal_range(slot_start, + slot_start, + slot_start + SUBSECTOR_SIZE_BYTES, + slot_start + SUBSECTOR_SIZE_BYTES); +} + #endif \ No newline at end of file diff --git a/src/fw/system/firmware_storage.h b/src/fw/system/firmware_storage.h index 241a181d4d..dc3228d466 100644 --- a/src/fw/system/firmware_storage.h +++ b/src/fw/system/firmware_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -24,7 +11,7 @@ #include #include -#if !CAPABILITY_HAS_PBLBOOT +#ifndef CONFIG_PBLBOOT typedef struct PACKED FirmwareDescription { uint32_t description_length; uint32_t firmware_length; @@ -51,4 +38,6 @@ typedef struct PACKED FirmwareHeader { FirmwareHeader firmware_storage_read_firmware_header(uint32_t address); bool firmware_storage_check_valid_firmware_header( uint32_t address, const FirmwareHeader* header); + +void firmware_storage_invalidate_firmware_slot(uint8_t slot); #endif \ No newline at end of file diff --git a/src/fw/system/hexdump.c b/src/fw/system/hexdump.c index bf68c7ae31..f55e5d045b 100644 --- a/src/fw/system/hexdump.c +++ b/src/fw/system/hexdump.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system/hexdump.h" #include "system/logging.h" diff --git a/src/fw/system/hexdump.h b/src/fw/system/hexdump.h index 92ffaba42e..0fb2c53818 100644 --- a/src/fw/system/hexdump.h +++ b/src/fw/system/hexdump.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -33,7 +20,7 @@ void hexdump_using_prompt(int level, const char *src_filename, int src_line_numb void hexdump_using_pbllog(int level, const char *src_filename, int src_line_number, const char *line_buffer); -#ifdef PBL_LOG_ENABLED +#ifdef CONFIG_LOG #define PBL_HEXDUMP_D_SERIAL(level, data, length) \ hexdump_log_src(__FILE_NAME__, __LINE__, level, data, length, hexdump_using_serial) #define PBL_HEXDUMP_D_PROMPT(level, data, length) \ diff --git a/src/fw/system/logging.h b/src/fw/system/logging.h index 60d11d4f88..fdb2d9a5eb 100644 --- a/src/fw/system/logging.h +++ b/src/fw/system/logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -28,14 +15,6 @@ #include #include -#ifndef __FILE_NAME__ -#ifdef __FILE_NAME_LEGACY__ -#define __FILE_NAME__ __FILE_NAME_LEGACY__ -#else -#define __FILE_NAME__ __FILE__ -#endif -#endif - #define SPLIT_64_BIT_ARG(x) (uint32_t)((x >> 32) & 0xFFFFFFFF), (uint32_t)(x & 0xFFFFFFFF) #define LOG_BUFFER_LENGTH 128 @@ -77,10 +56,64 @@ int pbl_log_get_bin_format(char* buffer, int buffer_len, const uint8_t log_level #define LOG_LEVEL_DEBUG 200 #define LOG_LEVEL_DEBUG_VERBOSE 255 -#ifdef PBL_LOGS_HASHED +#if defined(CONFIG_DEFAULT_LOG_LEVEL_ERROR) + #define DEFAULT_LOG_LEVEL LOG_LEVEL_ERROR +#elif defined(CONFIG_DEFAULT_LOG_LEVEL_WARNING) + #define DEFAULT_LOG_LEVEL LOG_LEVEL_WARNING +#elif defined(CONFIG_DEFAULT_LOG_LEVEL_INFO) + #define DEFAULT_LOG_LEVEL LOG_LEVEL_INFO +#elif defined(CONFIG_DEFAULT_LOG_LEVEL_DEBUG_VERBOSE) + #define DEFAULT_LOG_LEVEL LOG_LEVEL_DEBUG_VERBOSE +#else + #define DEFAULT_LOG_LEVEL LOG_LEVEL_DEBUG +#endif + +#if defined(CONFIG_FLASH_LOG_LEVEL_ERROR) + #define FLASH_LOG_LEVEL LOG_LEVEL_ERROR +#elif defined(CONFIG_FLASH_LOG_LEVEL_WARNING) + #define FLASH_LOG_LEVEL LOG_LEVEL_WARNING +#elif defined(CONFIG_FLASH_LOG_LEVEL_DEBUG) + #define FLASH_LOG_LEVEL LOG_LEVEL_DEBUG +#elif defined(CONFIG_FLASH_LOG_LEVEL_DEBUG_VERBOSE) + #define FLASH_LOG_LEVEL LOG_LEVEL_DEBUG_VERBOSE +#else + #define FLASH_LOG_LEVEL LOG_LEVEL_INFO +#endif + +#ifdef CONFIG_LOG_HASHED #include #endif +#if MEMFAULT && defined(CONFIG_LOG_HASHED) && __has_include("memfault/core/log.h") + #include "memfault/core/log.h" + #include "mcu/privilege.h" + + #define PBL_TO_MFLT_LOG_LEVEL(level) \ + ((level) <= LOG_LEVEL_ERROR ? kMemfaultPlatformLogLevel_Error : \ + (level) <= LOG_LEVEL_WARNING ? kMemfaultPlatformLogLevel_Warning : \ + (level) <= LOG_LEVEL_INFO ? kMemfaultPlatformLogLevel_Info : \ + kMemfaultPlatformLogLevel_Debug) + + // Memfault compact_log_helpers.h uses (arg)+0 in _Generic which triggers + // -Wpointer-arith when log arguments include function pointers. Suppress + // this warning around the SDK macro expansion. + // + // Memfault's RAM logger state lives in privileged BSS, so saving from + // unprivileged thread mode (apps, workers) faults the MPU. Skip the save + // in that case — kernel-side logs still reach Memfault. + #define PBL_MFLT_LOG_SAVE(level, fmt, ...) \ + do { \ + if (mcu_state_is_thread_privileged()) { \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpointer-arith\"") \ + MEMFAULT_COMPACT_LOG_SAVE(PBL_TO_MFLT_LOG_LEVEL(level), fmt, ## __VA_ARGS__); \ + _Pragma("GCC diagnostic pop") \ + } \ + } while (0) +#else + #define PBL_MFLT_LOG_SAVE(level, fmt, ...) ((void)0) +#endif + #define LOG_COLOR_BLACK "BLACK" // Not so useful in general #define LOG_COLOR_RED "RED" #define LOG_COLOR_GREEN "GREEN" @@ -99,36 +132,28 @@ int pbl_log_get_bin_format(char* buffer, int buffer_len, const uint8_t log_level #define LOG_COLOR_LIGHT_CYAN "LIGHT_CYAN" #define LOG_COLOR_WHITE "WHITE" -// Allow the default color for a src file to be set by defining FILE_LOG_COLOR -// Can be called directly with PBL_LOG_COLOR -#ifdef FILE_LOG_COLOR - #define DEFAULT_LOG_COLOR FILE_LOG_COLOR -#else - #define DEFAULT_LOG_COLOR LOG_COLOR_GREY -#endif +// Level-to-color mapping (fixed per level) +#define LOG_COLOR_FOR_ALWAYS LOG_COLOR_BLUE +#define LOG_COLOR_FOR_ERROR LOG_COLOR_RED +#define LOG_COLOR_FOR_WARNING LOG_COLOR_YELLOW +#define LOG_COLOR_FOR_INFO LOG_COLOR_GREEN +#define LOG_COLOR_FOR_DEBUG LOG_COLOR_GREY +#define LOG_COLOR_FOR_VERBOSE LOG_COLOR_GREY -#define LOG_DOMAIN_BT 1 #define LOG_DOMAIN_MISC 1 #define LOG_DOMAIN_FS 1 #define LOG_DOMAIN_COMM 1 -#define LOG_DOMAIN_ACCEL 0 #define LOG_DOMAIN_TEXT 0 -#define LOG_DOMAIN_QEMU_COMM 0 #define LOG_DOMAIN_ANIMATION 0 #define LOG_DOMAIN_ANALYTICS 0 #define LOG_DOMAIN_ACTIVITY 0 #define LOG_DOMAIN_ACTIVITY_INSIGHTS 0 #define LOG_DOMAIN_PROTOBUF 0 -#if defined(VOICE_DEBUG) -#define LOG_DOMAIN_VOICE 1 -#else -#define LOG_DOMAIN_VOICE 0 -#endif #define LOG_DOMAIN_BLOBDB 0 #ifndef LOG_DOMAIN_BT_PAIRING_INFO -#if !RELEASE +#ifndef CONFIG_RELEASE #define LOG_DOMAIN_BT_PAIRING_INFO 1 #else #define LOG_DOMAIN_BT_PAIRING_INFO 0 @@ -143,14 +168,6 @@ int pbl_log_get_bin_format(char* buffer, int buffer_len, const uint8_t log_level #define LOG_DOMAIN_DATA_LOGGING 0 #endif -#ifdef LOG_DOMAIN_ALL // Turn on all domains that are off by default - #define LOG_DOMAIN_BT -#endif - -#ifndef LOG_DOMAIN_TOUCH - #define LOG_DOMAIN_TOUCH 0 -#endif - #ifndef LOG_DOMAIN_I2C #define LOG_DOMAIN_I2C 0 #endif @@ -166,34 +183,49 @@ int pbl_log_get_bin_format(char* buffer, int buffer_len, const uint8_t log_level #define DEFAULT_LOG_DOMAIN LOG_DOMAIN_MISC #endif // DEFAULT_LOG_DOMAIN -#define PBL_SHOULD_LOG(level) ((level) <= DEFAULT_LOG_LEVEL) +// Per-module compile-time log level and name. PBL_LOG_MODULE_DEFINE / +// PBL_LOG_MODULE_DECLARE override these tentative definitions, e.g. +// PBL_LOG_MODULE_DEFINE(service_activity, CONFIG_SERVICE_ACTIVITY_LOG_LEVEL) (see +// Kconfig.template.log_level); level 0 selects DEFAULT_LOG_LEVEL. +__attribute__((unused)) static const uint8_t _pbl_log_module_level; +__attribute__((unused)) static const char *const _pbl_log_module_name; + +// Unit tests build with CONFIG_LOG but without the board Kconfig symbols, +// so module levels fall back to the default there. +#if defined(CONFIG_LOG) && !defined(UNITTEST) + #ifdef CONFIG_LOG_HASHED + // The MODULE map entry gives the loghash dict generator the + // file -> module mapping; the module name costs nothing at runtime. + #define PBL_LOG_MODULE_DEFINE(name, level) \ + __attribute__((unused)) static const uint8_t _pbl_log_module_level = (level); \ + __attribute__((unused)) static const char *const _pbl_log_module_name = #name; \ + __attribute__((used, nocommon, section(".log_strings"))) \ + static const char _pbl_log_module_map[] = "MODULE:" __FILE__ ":" #name + #else + #define PBL_LOG_MODULE_DEFINE(name, level) \ + __attribute__((unused)) static const uint8_t _pbl_log_module_level = (level); \ + __attribute__((unused)) static const char *const _pbl_log_module_name = #name + #endif +#else + #define PBL_LOG_MODULE_DEFINE(name, level) \ + __attribute__((unused)) static const uint8_t _pbl_log_module_level = 0 +#endif +#define PBL_LOG_MODULE_DECLARE(name, level) PBL_LOG_MODULE_DEFINE(name, level) -#ifdef PBL_LOG_ENABLED - #ifdef PBL_LOGS_HASHED - #define PBL_LOG_D(domain, level, fmt, ...) \ - do { \ - if (PBL_SHOULD_LOG(level)) { \ - if (domain) { \ - NEW_LOG_HASH(pbl_log_hashed_async, level, DEFAULT_LOG_COLOR, fmt, ## __VA_ARGS__); \ - } \ - } \ - } while (0) +#define PBL_SHOULD_LOG(level) \ + ((level) <= (_pbl_log_module_level != 0 ? _pbl_log_module_level : DEFAULT_LOG_LEVEL)) - #define PBL_LOG_D_SYNC(domain, level, fmt, ...) \ - do { \ - if (PBL_SHOULD_LOG(level)) { \ - if (domain) { \ - NEW_LOG_HASH(pbl_log_hashed_sync, level, DEFAULT_LOG_COLOR, fmt, ## __VA_ARGS__); \ - } \ - } \ - } while (0) +// Internal implementation macros (use level-named macros below instead) +#ifdef CONFIG_LOG + #ifdef CONFIG_LOG_HASHED #define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) \ do { \ if (PBL_SHOULD_LOG(level)) { \ if (domain) { \ NEW_LOG_HASH(pbl_log_hashed_async, level, color, fmt, ## __VA_ARGS__); \ + PBL_MFLT_LOG_SAVE(level, fmt, ## __VA_ARGS__); \ } \ } \ } while (0) @@ -207,151 +239,111 @@ int pbl_log_get_bin_format(char* buffer, int buffer_len, const uint8_t log_level } \ } while (0) #else - #define PBL_LOG_D(domain, level, fmt, ...) \ - do { \ - if (domain) { \ - pbl_log(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ - } \ - } while (0) - - #define PBL_LOG_D_SYNC(domain, level, fmt, ...) \ - do { \ - if (domain) { \ - pbl_log_sync(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ - } \ - } while (0) - #define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) \ do { \ - if (domain) { \ - pbl_log(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ + if (PBL_SHOULD_LOG(level)) { \ + if (domain) { \ + if (_pbl_log_module_name != NULL) { \ + pbl_log(level, __FILE__, __LINE__, "%s: " fmt, _pbl_log_module_name, \ + ## __VA_ARGS__); \ + } else { \ + pbl_log(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ + } \ + } \ } \ } while (0) #define PBL_LOG_COLOR_D_SYNC(domain, level, color, fmt, ...) \ do { \ - if (domain) { \ - pbl_log_sync(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ + if (PBL_SHOULD_LOG(level)) { \ + if (domain) { \ + if (_pbl_log_module_name != NULL) { \ + pbl_log_sync(level, __FILE__, __LINE__, "%s: " fmt, _pbl_log_module_name, \ + ## __VA_ARGS__); \ + } else { \ + pbl_log_sync(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ + } \ + } \ } \ } while (0) #endif - - #define PBL_LOG(level, fmt, ...) \ - PBL_LOG_D(DEFAULT_LOG_DOMAIN, level, fmt, ## __VA_ARGS__) - - #define PBL_LOG_COLOR(level, color, fmt, ...) \ - PBL_LOG_COLOR_D(DEFAULT_LOG_DOMAIN, level, color, fmt, ## __VA_ARGS__) - - #define PBL_LOG_SYNC(level, fmt, ...) \ - PBL_LOG_D_SYNC(DEFAULT_LOG_DOMAIN, level, fmt, ## __VA_ARGS__) - - #define PBL_LOG_COLOR_SYNC(level, color, fmt, ...) \ - PBL_LOG_COLOR_D_SYNC(DEFAULT_LOG_DOMAIN, level, color, fmt, ## __VA_ARGS__) - - #ifdef VERBOSE_LOGGING - - #define PBL_LOG_VERBOSE(fmt, ...) \ - PBL_LOG_D(DEFAULT_LOG_DOMAIN, LOG_LEVEL_DEBUG, fmt, ## __VA_ARGS__) - - #define PBL_LOG_D_VERBOSE(domain, fmt, ...) \ - PBL_LOG_D(domain, LOG_LEVEL_DEBUG, fmt, ## __VA_ARGS__) - - #define PBL_LOG_COLOR_D_VERBOSE(domain, color, fmt, ...) \ - PBL_LOG_COLOR_D(domain, LOG_LEVEL_DEBUG, color, fmt, ## __VA_ARGS__) - - #define PBL_LOG_COLOR_VERBOSE(color, fmt, ...) \ - PBL_LOG_COLOR_D(DEFAULT_LOG_DOMAIN, LOG_LEVEL_DEBUG, color, fmt, ## __VA_ARGS__) - - #define RETURN_STATUS_D(d, st) \ - do { \ - if (PASSED(st)) \ - PBL_LOG_D(d, LOG_LEVEL_INFO, "%d", (int)(st)); \ - else \ - PBL_LOG_D(d, LOG_LEVEL_WARNING, "%d", (int)(st)); \ - return st; \ - } while (0) - - #define RETURN_STATUS_COLOR_D(d, color, st) \ - do { \ - if (PASSED(st)) \ - PBL_LOG_COLOR_D(d, LOG_LEVEL_INFO, color, "%d", (int)(st)); \ - else \ - PBL_LOG_COLOR_D(d, LOG_LEVEL_WARNING, color, "%d", (int)(st)); \ - return st; \ - } while (0) - - #define RETURN_STATUS_UP_D(d, st) \ - do { \ - if ((st) == E_INVALID_ARGUMENT) { \ - PBL_LOG_D(d, LOG_LEVEL_ERROR, "%d", (int)(st)); \ - return E_INTERNAL; \ - } \ - else { \ - return (st); \ - } \ - } while (0) - - #define RETURN_STATUS_UP_COLOR_D(d, color, st) \ - do { \ - if ((st) == E_INVALID_ARGUMENT) { \ - PBL_LOG_COLOR_D(d, LOG_LEVEL_ERROR, color, "%d", (int)(st)); \ - return E_INTERNAL; \ - } \ - else { \ - return (st); \ - } \ - } while (0) - - - #else // VERBOSE_LOGGING - #define PBL_LOG_VERBOSE(fmt, ...) - #define PBL_LOG_COLOR_VERBOSE(color, fmt, ...) - #define PBL_LOG_D_VERBOSE(domain, fmt, ...) - #define PBL_LOG_COLOR_D_VERBOSE(domain, color, fmt, ...) - #define RETURN_STATUS_D(d, st) \ - do { \ - if (FAILED(st)) { \ - PBL_LOG_D(d, LOG_LEVEL_WARNING, "%d", (int)(st)); \ - } \ - return st; \ - } while (0) - - #define RETURN_STATUS_COLOR_D(d, color, st) \ - do { \ - if (FAILED(st)) { \ - PBL_LOG_COLOR_D(d, LOG_LEVEL_WARNING, color, "%d", (int)(st)); \ - } \ - return st; \ - } while (0) +#else // !CONFIG_LOG + #define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) + #define PBL_LOG_COLOR_D_SYNC(domain, level, color, fmt, ...) +#endif // CONFIG_LOG + +// Level-named domain macros (async) +#define PBL_LOG_D_ALWAYS(domain, fmt, ...) \ + PBL_LOG_COLOR_D(domain, LOG_LEVEL_ALWAYS, LOG_COLOR_FOR_ALWAYS, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_ERR(domain, fmt, ...) \ + PBL_LOG_COLOR_D(domain, LOG_LEVEL_ERROR, LOG_COLOR_FOR_ERROR, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_WRN(domain, fmt, ...) \ + PBL_LOG_COLOR_D(domain, LOG_LEVEL_WARNING, LOG_COLOR_FOR_WARNING, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_INFO(domain, fmt, ...) \ + PBL_LOG_COLOR_D(domain, LOG_LEVEL_INFO, LOG_COLOR_FOR_INFO, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_DBG(domain, fmt, ...) \ + PBL_LOG_COLOR_D(domain, LOG_LEVEL_DEBUG, LOG_COLOR_FOR_DEBUG, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_VERBOSE(domain, fmt, ...) \ + PBL_LOG_COLOR_D(domain, LOG_LEVEL_DEBUG_VERBOSE, LOG_COLOR_FOR_VERBOSE, fmt, ## __VA_ARGS__) + +// Level-named macros (default domain, async) +#define PBL_LOG_ALWAYS(fmt, ...) \ + PBL_LOG_D_ALWAYS(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_ERR(fmt, ...) \ + PBL_LOG_D_ERR(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_WRN(fmt, ...) \ + PBL_LOG_D_WRN(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_INFO(fmt, ...) \ + PBL_LOG_D_INFO(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_DBG(fmt, ...) \ + PBL_LOG_D_DBG(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_VERBOSE(fmt, ...) \ + PBL_LOG_D_VERBOSE(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) + +// Level-named domain sync macros +#define PBL_LOG_D_SYNC_ALWAYS(domain, fmt, ...) \ + PBL_LOG_COLOR_D_SYNC(domain, LOG_LEVEL_ALWAYS, LOG_COLOR_FOR_ALWAYS, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_SYNC_ERR(domain, fmt, ...) \ + PBL_LOG_COLOR_D_SYNC(domain, LOG_LEVEL_ERROR, LOG_COLOR_FOR_ERROR, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_SYNC_WRN(domain, fmt, ...) \ + PBL_LOG_COLOR_D_SYNC(domain, LOG_LEVEL_WARNING, LOG_COLOR_FOR_WARNING, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_SYNC_INFO(domain, fmt, ...) \ + PBL_LOG_COLOR_D_SYNC(domain, LOG_LEVEL_INFO, LOG_COLOR_FOR_INFO, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_SYNC_DBG(domain, fmt, ...) \ + PBL_LOG_COLOR_D_SYNC(domain, LOG_LEVEL_DEBUG, LOG_COLOR_FOR_DEBUG, fmt, ## __VA_ARGS__) +#define PBL_LOG_D_SYNC_VERBOSE(domain, fmt, ...) \ + PBL_LOG_COLOR_D_SYNC(domain, LOG_LEVEL_DEBUG_VERBOSE, LOG_COLOR_FOR_VERBOSE, fmt, ## __VA_ARGS__) + +// Level-named sync macros (default domain) +#define PBL_LOG_SYNC_ALWAYS(fmt, ...) \ + PBL_LOG_D_SYNC_ALWAYS(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_SYNC_ERR(fmt, ...) \ + PBL_LOG_D_SYNC_ERR(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_SYNC_WRN(fmt, ...) \ + PBL_LOG_D_SYNC_WRN(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_SYNC_INFO(fmt, ...) \ + PBL_LOG_D_SYNC_INFO(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_SYNC_DBG(fmt, ...) \ + PBL_LOG_D_SYNC_DBG(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) +#define PBL_LOG_SYNC_VERBOSE(fmt, ...) \ + PBL_LOG_D_SYNC_VERBOSE(DEFAULT_LOG_DOMAIN, fmt, ## __VA_ARGS__) + +#ifdef CONFIG_LOG + #define RETURN_STATUS_D(d, st) \ + do { \ + if (FAILED(st)) { \ + PBL_LOG_D_WRN(d, "%d", (int)(st)); \ + } \ + return st; \ + } while (0) #define RETURN_STATUS_UP_D(d, st) \ return ((st) != E_INVALID_ARGUMENT ? (st) : E_INTERNAL) - - #define RETURN_STATUS_UP_COLOR_D(d, color, st) \ - return ((st) != E_INVALID_ARGUMENT ? (st) : E_INTERNAL) - #endif // VERBOSE_LOGGING - -#else // PBL_LOG_ENABLED - #define PBL_LOG(level, fmt, ...) - #define PBL_LOG_COLOR(level, color, fmt, ...) - #define PBL_LOG_SYNC(level, fmt, ...) - #define PBL_LOG_COLOR_SYNC(level, color, fmt, ...) - #define PBL_LOG_D(domain, level, fmt, ...) - #define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) - #define PBL_LOG_D_SYNC(domain, level, fmt, ...) - #define PBL_LOG_COLOR_D_SYNC(domain, level, color, fmt, ...) - #define PBL_LOG_VERBOSE(fmt, ...) - #define PBL_LOG_COLOR_VERBOSE(color, fmt, ...) - #define PBL_LOG_D_VERBOSE(domain, fmt, ...) - #define PBL_LOG_COLOR_D_VERBOSE(domain, color, fmt, ...) +#else // CONFIG_LOG #define RETURN_STATUS_D(d, st) return (st) - #define RETURN_STATUS_COLOR_D(d, color, st) return (st) #define RETURN_STATUS_UP_D(d, st) \ return ((st) == E_INVALID_ARGUMENT ? E_INTERNAL : (st)) - #define RETURN_STATUS_UP_COLOR_D(d, color, st) \ - return ((st) == E_INVALID_ARGUMENT ? E_INTERNAL : (st)) -#endif // PBL_LOG_ENABLED +#endif // CONFIG_LOG #define RETURN_STATUS(s) RETURN_STATUS_D(DEFAULT_LOG_DOMAIN, s) #define RETURN_STATUS_UP(s) RETURN_STATUS_UP_D(DEFAULT_LOG_DOMAIN, s) - diff --git a/src/fw/system/passert.c b/src/fw/system/passert.c index d592a1bd9d..67f82946c7 100644 --- a/src/fw/system/passert.c +++ b/src/fw/system/passert.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "passert.h" @@ -23,6 +10,7 @@ #include "kernel/pebble_tasks.h" #include "syscall/syscall.h" #include "system/logging.h" +#include "util/heap.h" #include #include @@ -68,7 +56,7 @@ NORETURN passert_failed(const char* filename, int line_number, const char* messa NORETURN passert_failed_hashed(uint32_t packed_loghash, ...) { uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); - PBL_LOG(LOG_LEVEL_ALWAYS, "ASSERTION at LR 0x%x", saved_lr); + PBL_LOG_ALWAYS("ASSERTION at LR 0x%x", saved_lr); va_list fmt_args; va_start(fmt_args, packed_loghash); @@ -81,7 +69,7 @@ NORETURN passert_failed_hashed(uint32_t packed_loghash, ...) { } NORETURN passert_failed_hashed_with_lr(uint32_t lr, uint32_t packed_loghash, ...) { - PBL_LOG(LOG_LEVEL_ALWAYS, "ASSERTION at LR 0x%"PRIx32, lr); + PBL_LOG_ALWAYS("ASSERTION at LR 0x%"PRIx32, lr); va_list fmt_args; va_start(fmt_args, packed_loghash); @@ -94,7 +82,7 @@ NORETURN passert_failed_hashed_with_lr(uint32_t lr, uint32_t packed_loghash, ... } NORETURN passert_failed_hashed_no_message_with_lr(uint32_t lr) { - PBL_LOG(LOG_LEVEL_ALWAYS, "ASSERTION at LR 0x%"PRIx32, lr); + PBL_LOG_ALWAYS("ASSERTION at LR 0x%"PRIx32, lr); trigger_fault(RebootReasonCode_Assert, lr); } @@ -114,7 +102,7 @@ NORETURN passert_failed_no_message(const char* filename, int line_number) { NORETURN wtf(void) { uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); - PBL_LOG(LOG_LEVEL_ALWAYS, "*** WTF %p", (void *)saved_lr); + PBL_LOG_ALWAYS("*** WTF %p", (void *)saved_lr); trigger_fault(RebootReasonCode_Assert, saved_lr); } @@ -122,7 +110,7 @@ void passert_check_task(PebbleTask expected_task) { uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); if (pebble_task_get_current() != expected_task) { - PBL_LOG(LOG_LEVEL_ALWAYS, "LR: %p. Incorrect task! Expected <%s> got <%s>", + PBL_LOG_ALWAYS("LR: %p. Incorrect task! Expected <%s> got <%s>", (void*) saved_lr, pebble_task_get_name(expected_task), pebble_task_get_name(pebble_task_get_current())); trigger_fault(RebootReasonCode_Assert, saved_lr); @@ -133,7 +121,7 @@ void passert_check_not_task(PebbleTask unexpected_task) { uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0); if (pebble_task_get_current() == unexpected_task) { - PBL_LOG(LOG_LEVEL_ALWAYS, "LR: %p. Incorrect task! Can't be <%s>", + PBL_LOG_ALWAYS("LR: %p. Incorrect task! Can't be <%s>", (void*) saved_lr, pebble_task_get_name(unexpected_task)); trigger_fault(RebootReasonCode_Assert, saved_lr); } @@ -151,23 +139,27 @@ void assert_failed(uint8_t* file, uint32_t line) { extern void command_dump_malloc_kernel(void); NORETURN croak_oom(size_t bytes, int saved_lr, Heap *heap_ptr) { - PBL_LOG(LOG_LEVEL_ALWAYS, "CROAK OOM: Failed to alloc %d bytes at LR: 0x%x", - bytes, saved_lr); + unsigned int used = 0, free_bytes = 0, max_free = 0; + if (heap_ptr) { + heap_calc_totals(heap_ptr, &used, &free_bytes, &max_free); + } + PBL_LOG_ALWAYS("CROAK OOM: Failed to alloc %d bytes at LR: 0x%x (used %u, free %u, max_free %u)", + bytes, saved_lr, used, free_bytes, max_free); -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION command_dump_malloc_kernel(); #endif trigger_oom_fault(bytes, saved_lr, heap_ptr); } -#if MICRO_FAMILY_NRF52840 +#ifdef CONFIG_SOC_NRF52 NORETURN app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) { - PBL_LOG(LOG_LEVEL_ALWAYS, "nRF error %ld (pc %ld, info %ld)", id, pc, info); + PBL_LOG_ALWAYS("nRF error %ld (pc %ld, info %ld)", id, pc, info); trigger_fault(RebootReasonCode_Assert, pc); } NORETURN app_error_handler_bare(uint32_t error_code) { - app_error_fault_handler(error_code, 0, 0); + app_error_fault_handler(error_code, (uint32_t)__builtin_return_address(0), 0); } #endif diff --git a/src/fw/system/passert.h b/src/fw/system/passert.h index 67140bd0ad..b202634c10 100644 --- a/src/fw/system/passert.h +++ b/src/fw/system/passert.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -22,7 +9,7 @@ #include -#ifdef PBL_LOGS_HASHED +#ifdef CONFIG_LOG_HASHED #include NORETURN passert_failed_hashed(uint32_t packed_loghash, ...); @@ -123,7 +110,7 @@ void passert_check_not_task(enum PebbleTask unexpected_task); // extern void command_dump_malloc(void); -#ifdef PBL_LOGS_HASHED +#ifdef CONFIG_LOG_HASHED #define PBL_CROAK(msg, ...) \ do { \ @@ -131,12 +118,12 @@ void passert_check_not_task(enum PebbleTask unexpected_task); ## __VA_ARGS__); \ } while (0) -#else // PBL_LOGS_HASHED +#else // CONFIG_LOG_HASHED #define PBL_CROAK(fmt, args...) \ passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args) -#endif // PBL_LOGS_HASHED +#endif // CONFIG_LOG_HASHED typedef struct Heap Heap; diff --git a/src/fw/system/profiler.c b/src/fw/system/profiler.c index 4faf32b3a1..6309201b23 100644 --- a/src/fw/system/profiler.c +++ b/src/fw/system/profiler.c @@ -1,43 +1,32 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "profiler.h" #include "system/passert.h" #include "util/size.h" -#define CMSIS_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include +#include #include #include #include #include -#ifdef MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 #include #include #endif -#if PULSE_EVERYWHERE +#ifdef CONFIG_SOC_SF32LB52 +#include +#endif + +#ifdef CONFIG_PULSE_EVERYWHERE #define PROF_LOG(buf, sz, fmt, ...) \ do { \ snprintf(buf, sz, fmt, ## __VA_ARGS__); \ - PBL_LOG(LOG_LEVEL_DEBUG, "%s", buf); \ + PBL_LOG_DBG("%s", buf); \ } while (0) #else #define PROF_LOG(buf, sz, fmt, ...) dbgserial_putstr_fmt(buf, sz, fmt, ## __VA_ARGS__) @@ -49,9 +38,17 @@ Profiler g_profiler; #define PROFILER_NODE(name) ProfilerNode g_profiler_node_##name = {.module_name = #name}; #include "profiler_list.h" #undef PROFILER_NODE -#if PROFILE_INTERRUPTS +#ifdef CONFIG_PROFILE_INTERRUPTS #define IRQ_DEF(idx, irq) ProfilerNode g_profiler_node_##irq##_IRQ = {.module_name = #irq"_IRQ"}; -#include "irq_stm32.def" +#if defined(CONFIG_QEMU) +#include "irq_qemu.def" +#elif defined(CONFIG_SOC_NRF52) +#include "irq_nrf52.def" +#elif defined(CONFIG_SOC_SF32LB52) +#include "irq_sf32lb52.def" +#else +#error "No IRQ definition for this MICRO_FAMILY" +#endif #undef IRQ_DEF #endif @@ -59,9 +56,17 @@ static ProfilerNode *s_profiler_nodes[] = { #define PROFILER_NODE(name) &g_profiler_node_##name, #include "profiler_list.h" #undef PROFILER_NODE -#if PROFILE_INTERRUPTS +#ifdef CONFIG_PROFILE_INTERRUPTS #define IRQ_DEF(idx, irq) &g_profiler_node_##irq##_IRQ, -#include "irq_stm32.def" +#if defined(CONFIG_QEMU) +#include "irq_qemu.def" +#elif defined(CONFIG_SOC_NRF52) +#include "irq_nrf52.def" +#elif defined(CONFIG_SOC_SF32LB52) +#include "irq_sf32lb52.def" +#else +#error "No IRQ definition for this MICRO_FAMILY" +#endif #undef IRQ_DEF #endif }; @@ -94,9 +99,6 @@ void profiler_init(void) { void profiler_start(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; -#ifdef MICRO_FAMILY_STM32F7 - DWT->LAR = 0xC5ACCE55; -#endif DWT->CYCCNT = 0; DWT->CTRL |= 0x01; g_profiler.start = DWT->CYCCNT; @@ -124,10 +126,12 @@ void profiler_node_stop(ProfilerNode *node, uint32_t dwt_cyc_cnt) { } uint32_t profiler_cycles_to_us(uint32_t cycles) { -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) uint32_t mhz = NRFX_DELAY_CPU_FREQ_MHZ; -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) uint32_t mhz = HAL_RCC_GetHCLKFreq(CORE_ID_HCPU); +#elif defined(CONFIG_QEMU) + uint32_t mhz = SystemCoreClock / 1000000; #else RCC_ClocksTypeDef clocks; RCC_GetClocksFreq(&clocks); @@ -153,10 +157,12 @@ uint32_t profiler_get_total_duration(bool in_us) { } if (in_us) { -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) uint32_t mhz = NRFX_DELAY_CPU_FREQ_MHZ; -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) uint32_t mhz = HAL_RCC_GetHCLKFreq(CORE_ID_HCPU); +#elif defined(CONFIG_QEMU) + uint32_t mhz = SystemCoreClock / 1000000; #else RCC_ClocksTypeDef clocks; RCC_GetClocksFreq(&clocks); @@ -172,14 +178,18 @@ void profiler_print_stats(void) { PROFILER_STOP; // Make sure the profiler has been stopped. uint32_t total = profiler_get_total_duration(false); -#if defined(MICRO_FAMILY_NRF5) +#if defined(CONFIG_SOC_NRF52) uint32_t mhz = NRFX_DELAY_CPU_FREQ_MHZ; char buf[80]; PROF_LOG(buf, sizeof(buf), "CPU Frequency: %"PRIu32"MHz", mhz); -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) uint32_t mhz = HAL_RCC_GetHCLKFreq(CORE_ID_HCPU); char buf[80]; PROF_LOG(buf, sizeof(buf), "CPU Frequency: %"PRIu32"MHz", mhz); +#elif defined(CONFIG_QEMU) + uint32_t mhz = SystemCoreClock / 1000000; + char buf[80]; + PROF_LOG(buf, sizeof(buf), "CPU Frequency: %"PRIu32"MHz", mhz); #else RCC_ClocksTypeDef clocks; RCC_GetClocksFreq(&clocks); diff --git a/src/fw/system/profiler.h b/src/fw/system/profiler.h index b029754f9e..cfa79ead63 100644 --- a/src/fw/system/profiler.h +++ b/src/fw/system/profiler.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once /* Setting up a profiler node: * 1. Create a new profiler node by adding it to profiler_list.h. * 2. Place PROFILER_NODE_START() and PROFILER_NODE_STOP() as desired. - * 3. Make sure you are building with the "--profiler" configure option. + * 3. Make sure you are building with CONFIG_PROFILER=y. * * Starting the profiler: * The prompt commands "profiler start" and "profiler stop" can be used to toggle it from the @@ -34,9 +21,8 @@ #include "util/list.h" -#if PROFILER -#define CMSIS_COMPATIBLE -#include +#ifdef CONFIG_PROFILER +#include #endif typedef struct { @@ -56,7 +42,7 @@ typedef struct { extern Profiler g_profiler; -#if !defined(PROFILER) +#if !defined(CONFIG_PROFILER) #define PROFILER_NODE(name) #define PROFILER_INIT @@ -111,7 +97,7 @@ extern Profiler g_profiler; #define PROFILER_NODE_GET_COUNT(node) \ profiler_node_get_count(&g_profiler_node_##node) -#endif // PROFILER +#endif // CONFIG_PROFILER void profiler_init(void); void profiler_print_stats(void); diff --git a/src/fw/system/profiler_list.h b/src/fw/system/profiler_list.h index 41c08c28d5..a1fd5cd762 100644 --- a/src/fw/system/profiler_list.h +++ b/src/fw/system/profiler_list.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // List of specific node timers. These are started and stopped using PROFILER_NODE_START // and PROFILER_NODE_STOP diff --git a/src/fw/system/reboot_reason.c b/src/fw/system/reboot_reason.c index 81f385e578..959c444372 100644 --- a/src/fw/system/reboot_reason.c +++ b/src/fw/system/reboot_reason.c @@ -1,31 +1,21 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "reboot_reason.h" #include "mcu/interrupts.h" #include "os/tick.h" #include "system/logging.h" +#include "system/bootbits.h" -#define STM32F2_COMPATIBLE -#define STM32F4_COMPATIBLE -#define STM32F7_COMPATIBLE -#define NRF5_COMPATIBLE -#define SF32LB52_COMPATIBLE -#include +#ifdef CONFIG_SOC_SF32LB52 +#include +#endif + +#ifdef CONFIG_QEMU +extern void RTC_WriteBackupRegister(uint32_t reg_id, uint32_t value); +extern uint32_t RTC_ReadBackupRegister(uint32_t reg_id); +#endif #include @@ -35,14 +25,14 @@ _Static_assert(sizeof(RebootReason) == sizeof(uint32_t[4]), "RebootReason is a funny size"); void reboot_reason_set(RebootReason *reason) { -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 uint32_t *raw = (uint32_t*)reason; if (retained_read(REBOOT_REASON_REGISTER_1)) { // It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0) if (!mcu_state_is_isr() && __get_BASEPRI() == 0 && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { - PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set"); + PBL_LOG_WRN("Reboot reason is already set"); } return; } @@ -51,14 +41,14 @@ void reboot_reason_set(RebootReason *reason) { retained_write(REBOOT_REASON_STUCK_TASK_PC, raw[1]); retained_write(REBOOT_REASON_STUCK_TASK_LR, raw[2]); retained_write(REBOOT_REASON_STUCK_TASK_CALLBACK, raw[3]); -#elif defined MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) uint32_t *raw = (uint32_t*)reason; if (HAL_Get_backup(REBOOT_REASON_REGISTER_1)) { // It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0) if (!mcu_state_is_isr() && __get_BASEPRI() == 0 && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { - PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set"); + PBL_LOG_WRN("Reboot reason is already set"); } return; } @@ -67,14 +57,14 @@ void reboot_reason_set(RebootReason *reason) { HAL_Set_backup(REBOOT_REASON_STUCK_TASK_PC, raw[1]); HAL_Set_backup(REBOOT_REASON_STUCK_TASK_LR, raw[2]); HAL_Set_backup(REBOOT_REASON_STUCK_TASK_CALLBACK, raw[3]); -#else +#elif defined(CONFIG_QEMU) uint32_t *raw = (uint32_t*)reason; if (RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_1)) { // It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0) if (!mcu_state_is_isr() && __get_BASEPRI() == 0 && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { - PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set"); + PBL_LOG_WRN("Reboot reason is already set"); } return; } @@ -91,32 +81,32 @@ void reboot_reason_set_restarted_safely(void) { reboot_reason_get(&reason); reason.restarted_safely = true; -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 uint32_t* raw = (uint32_t *)&reason; retained_write(REBOOT_REASON_REGISTER_1, *raw); -#elif defined MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) uint32_t* raw = (uint32_t *)&reason; HAL_Set_backup(REBOOT_REASON_REGISTER_1, *raw); -#else +#elif defined(CONFIG_QEMU) uint32_t* raw = (uint32_t *)&reason; RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, *raw); #endif } void reboot_reason_get(RebootReason *reason) { -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 uint32_t *raw = (uint32_t *)reason; raw[0] = retained_read(REBOOT_REASON_REGISTER_1); raw[1] = retained_read(REBOOT_REASON_STUCK_TASK_PC); raw[2] = retained_read(REBOOT_REASON_STUCK_TASK_LR); raw[3] = retained_read(REBOOT_REASON_STUCK_TASK_CALLBACK); -#elif defined MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) uint32_t *raw = (uint32_t *)reason; raw[0] = HAL_Get_backup(REBOOT_REASON_REGISTER_1); raw[1] = HAL_Get_backup(REBOOT_REASON_STUCK_TASK_PC); raw[2] = HAL_Get_backup(REBOOT_REASON_STUCK_TASK_LR); raw[3] = HAL_Get_backup(REBOOT_REASON_STUCK_TASK_CALLBACK); -#else +#elif defined(CONFIG_QEMU) uint32_t *raw = (uint32_t *)reason; raw[0] = RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_1); raw[1] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_PC); @@ -126,17 +116,17 @@ void reboot_reason_get(RebootReason *reason) { } void reboot_reason_clear(void) { -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 retained_write(REBOOT_REASON_REGISTER_1, 0); retained_write(REBOOT_REASON_STUCK_TASK_PC, 0); retained_write(REBOOT_REASON_STUCK_TASK_LR, 0); retained_write(REBOOT_REASON_STUCK_TASK_CALLBACK, 0); -#elif defined MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) HAL_Set_backup(REBOOT_REASON_REGISTER_1, 0); HAL_Set_backup(REBOOT_REASON_STUCK_TASK_PC, 0); HAL_Set_backup(REBOOT_REASON_STUCK_TASK_LR, 0); HAL_Set_backup(REBOOT_REASON_STUCK_TASK_CALLBACK, 0); -#else +#elif defined(CONFIG_QEMU) RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, 0); RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_PC, 0); RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_LR, 0); @@ -145,21 +135,21 @@ void reboot_reason_clear(void) { } uint32_t reboot_get_slot_of_last_launched_app(void) { -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 return retained_read(SLOT_OF_LAST_LAUNCHED_APP); -#elif defined MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) return HAL_Get_backup(SLOT_OF_LAST_LAUNCHED_APP); -#else +#elif defined(CONFIG_QEMU) return RTC_ReadBackupRegister(SLOT_OF_LAST_LAUNCHED_APP); #endif } void reboot_set_slot_of_last_launched_app(uint32_t app_slot) { -#if MICRO_FAMILY_NRF5 +#ifdef CONFIG_SOC_NRF52 retained_write(SLOT_OF_LAST_LAUNCHED_APP, app_slot); -#elif defined MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) HAL_Set_backup(SLOT_OF_LAST_LAUNCHED_APP, app_slot); -#else +#elif defined(CONFIG_QEMU) RTC_WriteBackupRegister(SLOT_OF_LAST_LAUNCHED_APP, app_slot); #endif } diff --git a/src/fw/system/reboot_reason.h b/src/fw/system/reboot_reason.h index 0969aa4082..ae732823c8 100644 --- a/src/fw/system/reboot_reason.h +++ b/src/fw/system/reboot_reason.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -53,7 +40,6 @@ typedef enum { RebootReasonCode_EventQueueFull, RebootReasonCode_WorkerHardFault, // Off by default, compile in with WORKER_CRASH_CAUSES_RESET RebootReasonCode_OutOfMemory, - RebootReasonCode_DialogBootFault, RebootReasonCode_BtCoredump, RebootReasonCode_CoreDump, // Core dump initiated without a more specific reason set RebootReasonCode_CoreDumpEntryFailed, diff --git a/src/fw/system/reset.h b/src/fw/system/reset.h index 68d9158454..647891cf76 100644 --- a/src/fw/system/reset.h +++ b/src/fw/system/reset.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,7 +8,7 @@ #include //! Shut down system services but don't actually reset. -void system_reset_prepare(bool unsafe_reset); +void system_reset_prepare(void); //! Reset nicely after shutting down system services. Does not set the reboot_reason other than //! calling reboot_reason_set_restarted_safely just before the reset occurs. diff --git a/src/fw/system/rtc_registers.h b/src/fw/system/rtc_registers.h index 28ffb8769b..d6f836dce1 100644 --- a/src/fw/system/rtc_registers.h +++ b/src/fw/system/rtc_registers.h @@ -1,23 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#if MICRO_FAMILY_NRF5 - +#ifdef CONFIG_SOC_NRF52 #define RTC_BKP_BOOTBIT_DR 0 #define STUCK_BUTTON_REGISTER 1 #define BOOTLOADER_VERSION_REGISTER 2 @@ -46,7 +32,7 @@ extern void retained_write(uint8_t id, uint32_t value); extern uint32_t retained_read(uint8_t id); -#elif MICRO_FAMILY_SF32LB52 +#elif defined(CONFIG_SOC_SF32LB52) /* sf32lb52 rtc backup register 2/5/6/7/8/9 is freely usable */ #define RTC_BKP_BOOTBIT_DR 2 #define REBOOT_REASON_REGISTER_1 5 @@ -55,6 +41,20 @@ extern uint32_t retained_read(uint8_t id); #define REBOOT_REASON_STUCK_TASK_CALLBACK 8 #define SLOT_OF_LAST_LAUNCHED_APP 9 +#elif defined(CONFIG_QEMU) +/* QEMU backup registers mapped to MMIO backup region (indices 0..15) */ +#define RTC_BKP_BOOTBIT_DR 0 +#define STUCK_BUTTON_REGISTER 1 +#define BOOTLOADER_VERSION_REGISTER 2 +#define CURRENT_TIME_REGISTER 3 +#define CURRENT_INTERVAL_TICKS_REGISTER 4 +#define REBOOT_REASON_REGISTER_1 5 +#define REBOOT_REASON_STUCK_TASK_PC 6 +#define REBOOT_REASON_STUCK_TASK_LR 7 +#define REBOOT_REASON_STUCK_TASK_CALLBACK 8 +#define RTC_BKP_FLASH_ERASE_PROGRESS 9 +#define SLOT_OF_LAST_LAUNCHED_APP 10 + #else #define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0 #define STUCK_BUTTON_REGISTER RTC_BKP_DR1 diff --git a/src/fw/system/status_codes.h b/src/fw/system/status_codes.h index 883b6b689f..4516067b27 100644 --- a/src/fw/system/status_codes.h +++ b/src/fw/system/status_codes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /*! \file status_codes.h diff --git a/src/fw/system/syscalls.c b/src/fw/system/syscalls.c index 34b9fa88b2..efbde651dd 100644 --- a/src/fw/system/syscalls.c +++ b/src/fw/system/syscalls.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/src/fw/system/testinfra.c b/src/fw/system/testinfra.c index 610f5778d3..b059ced52e 100644 --- a/src/fw/system/testinfra.c +++ b/src/fw/system/testinfra.c @@ -1,45 +1,52 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "testinfra.h" #include "console/pulse_internal.h" #include "kernel/core_dump.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "system/bootbits.h" #include "system/logging.h" #include "system/passert.h" -void notify_system_ready_for_communication(void) { #if !UNITTEST +static void prv_emit_ready_log(void *unused) { pbl_log(LOG_LEVEL_DEBUG, __FILE_NAME__, __LINE__, "Ready for communication."); -#if PULSE_EVERYWHERE +} +#endif + +void notify_system_ready_for_communication(void) { +#if !UNITTEST +#ifdef CONFIG_PULSE_EVERYWHERE static bool s_pulse_started = false; if (!s_pulse_started) { pulse_start(); s_pulse_started = true; } #endif + // Defer the "Ready for communication" log briefly. pebble-tool's emulator + // wait loop polls for this string and starts the install immediately when + // it appears — but at this point the first app's task has only just been + // *created*; its main_func hasn't run yet and it hasn't pushed its first + // window. For third-party watchfaces (whose window names don't match + // pebble-tool's other wait tokens "" / "") this means + // install arrives before the watchface is event-loop-responsive, which + // wedges the install until the user manually toggles to the launcher. + // A short timer lets the first app run its main_func and push at least one + // window before pebble-tool starts pumping protocol messages at us. + static TimerID s_ready_log_timer = TIMER_INVALID_ID; + if (s_ready_log_timer == TIMER_INVALID_ID) { + s_ready_log_timer = new_timer_create(); + } + new_timer_start(s_ready_log_timer, 500, prv_emit_ready_log, NULL, 0); #endif } -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD NORETURN test_infra_quarantine_board(const char *quarantine_reason) { - PBL_LOG(LOG_LEVEL_INFO, "Quarantine Board: %s", quarantine_reason); + PBL_LOG_INFO("Quarantine Board: %s", quarantine_reason); boot_bit_set(BOOT_BIT_FORCE_PRF); core_dump_reset(true /* is_forced */); } -#endif /* IS_BIGBOARD */ +#endif /* CONFIG_IS_BIGBOARD */ diff --git a/src/fw/system/testinfra.h b/src/fw/system/testinfra.h index 965d91bd50..176dc6a8ca 100644 --- a/src/fw/system/testinfra.h +++ b/src/fw/system/testinfra.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -23,7 +10,7 @@ // handles that notification void notify_system_ready_for_communication(void); -#if IS_BIGBOARD +#ifdef CONFIG_IS_BIGBOARD // This sends a notification to infra that we have detected an issue which needs manual // intervention to debug. Infra should disable the board to give the team time to grab the board and // investigate. @@ -31,4 +18,4 @@ void notify_system_ready_for_communication(void); // Note: To preserve the current state, this routine sets the FORCE_PRF boot bit & then // forces a coredump NORETURN test_infra_quarantine_board(const char *quarantine_reason); -#endif /* IS_BIGBOARD */ +#endif /* CONFIG_IS_BIGBOARD */ diff --git a/src/fw/system/version.c b/src/fw/system/version.c index d76a912028..e6219c6d12 100644 --- a/src/fw/system/version.c +++ b/src/fw/system/version.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_watch_info.h" #include "drivers/flash.h" @@ -23,6 +10,7 @@ #include "util/build_id.h" #include "util/string.h" +#include #include #include #include @@ -33,7 +21,7 @@ #include "git_version.auto.h" //! This symbol and its contents are provided by the linker script, see the -//! .note.gnu.build-id section in src/fw/stm32f2xx_flash_fw.ld +//! .note.gnu.build-id section in src/fw/fw_common.ld extern const ElfExternalNote TINTIN_BUILD_ID; const FirmwareMetadata TINTIN_METADATA SECTION(".pbl_fw_version") = { @@ -43,12 +31,12 @@ const FirmwareMetadata TINTIN_METADATA SECTION(".pbl_fw_version") = { .is_recovery_firmware = FIRMWARE_METADATA_IS_RECOVERY_FIRMWARE, .is_ble_firmware = false, -#if PLATFORM_HAS_PBLBOOT +#ifdef CONFIG_PBLBOOT .is_dual_slot = true, #else .is_dual_slot = false, #endif -#if defined(FIRMWARE_SLOT_0) && !defined(RECOVERY_FW) +#if defined(FIRMWARE_SLOT_0) && !defined(CONFIG_RECOVERY_FW) .is_slot_0 = true, #else .is_slot_0 = false, @@ -70,7 +58,7 @@ bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata) { static bool prv_version_copy_flash_fw_metadata(FirmwareMetadata *out_metadata, uint32_t flash_address, bool check_crc) { -#if !CAPABILITY_HAS_PBLBOOT +#ifndef CONFIG_PBLBOOT FirmwareDescription firmware_description = firmware_storage_read_firmware_description(flash_address); @@ -126,7 +114,7 @@ bool version_copy_recovery_fw_version(char* dest, const int dest_len_bytes) { } bool version_is_prf_installed(void) { -#if !CAPABILITY_HAS_PBLBOOT +#ifndef CONFIG_PBLBOOT FirmwareDescription firmware_description = firmware_storage_read_firmware_description(FLASH_REGION_SAFE_FIRMWARE_BEGIN); @@ -167,3 +155,28 @@ void version_get_major_minor_patch(unsigned int *major, unsigned int *minor, *minor = GIT_MINOR_VERSION; *patch_ptr = GIT_PATCH_VERBOSE_STRING; } + +bool version_is_release_build(void) { + const char *tag = GIT_TAG; + + if (*tag++ != 'v') { + return false; + } + + // Expect: digits, '.', digits, '.', digits, '\0' + for (int i = 0; i < 3; i++) { + if (!isdigit((unsigned char)*tag)) { + return false; + } + while (isdigit((unsigned char)*tag)) { + tag++; + } + if (i < 2) { + if (*tag++ != '.') { + return false; + } + } + } + + return *tag == '\0'; +} diff --git a/src/fw/system/version.h b/src/fw/system/version.h index d97bfad0b8..42641b8527 100644 --- a/src/fw/system/version.h +++ b/src/fw/system/version.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -74,3 +61,7 @@ void version_copy_build_id_hex_string(char *buffer, size_t buffer_bytes_left, //! read-only void version_get_major_minor_patch(unsigned int *major, unsigned int *minor, char const **patch_ptr); + +//! Checks if the running firmware was built from a release tag (vX.Y.Z). +//! @return true if the version tag matches a release pattern, false otherwise. +bool version_is_release_build(void); diff --git a/src/fw/util/array.c b/src/fw/util/array.c index dd7dffb31f..9f40292b35 100644 --- a/src/fw/util/array.c +++ b/src/fw/util/array.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/array.h" diff --git a/src/fw/util/array.h b/src/fw/util/array.h index 5483908374..0ae6dc76cc 100644 --- a/src/fw/util/array.h +++ b/src/fw/util/array.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/base64.c b/src/fw/util/base64.c index 9fd273e1e3..1a84fc35da 100644 --- a/src/fw/util/base64.c +++ b/src/fw/util/base64.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "net.h" #include "system/passert.h" diff --git a/src/fw/util/base64.h b/src/fw/util/base64.h index c6be6660f1..2753020b45 100644 --- a/src/fw/util/base64.h +++ b/src/fw/util/base64.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/src/fw/util/bitset.c b/src/fw/util/bitset.c index 8cede09399..f724cda2ab 100644 --- a/src/fw/util/bitset.c +++ b/src/fw/util/bitset.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bitset.h" diff --git a/src/fw/util/bitset.h b/src/fw/util/bitset.h index adc934f123..a34f1d787c 100644 --- a/src/fw/util/bitset.h +++ b/src/fw/util/bitset.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file util/bitset.h //! diff --git a/src/fw/util/buffer.c b/src/fw/util/buffer.c index 74290496a2..05855e4dc2 100644 --- a/src/fw/util/buffer.c +++ b/src/fw/util/buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "buffer.h" diff --git a/src/fw/util/buffer.h b/src/fw/util/buffer.h index b1daa8c6e4..b9a4ef8d1b 100644 --- a/src/fw/util/buffer.h +++ b/src/fw/util/buffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/crc32_lut.py b/src/fw/util/crc32_lut.py index 5faabdd80a..b2b3ad8263 100755 --- a/src/fw/util/crc32_lut.py +++ b/src/fw/util/crc32_lut.py @@ -1,17 +1,6 @@ #!/usr/bin/env python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import print_function @@ -25,13 +14,14 @@ def crc_table(bits): rr = i * 16 for x in xrange(8): rr = (rr >> 1) ^ (-(rr & 1) & CRC_POLY) - lookup_table.append(rr & 0xffffffff) + lookup_table.append(rr & 0xFFFFFFFF) return lookup_table -table = ['0x{:08x},'.format(entry) for entry in crc_table(4)] -chunks = zip(*[iter(table)]*4) -print('static const uint32_t s_lookup_table[] = {') +table = ["0x{:08x},".format(entry) for entry in crc_table(4)] +chunks = zip(*[iter(table)] * 4) + +print("static const uint32_t s_lookup_table[] = {") for chunk in chunks: - print(' ' + ' '.join(chunk)) -print('};') + print(" " + " ".join(chunk)) +print("};") diff --git a/src/fw/util/crc8.c b/src/fw/util/crc8.c index aac4d0af0b..434b27980c 100644 --- a/src/fw/util/crc8.c +++ b/src/fw/util/crc8.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/crc8.h" diff --git a/src/fw/util/crc8.h b/src/fw/util/crc8.h index 62507b733a..cd7771adae 100644 --- a/src/fw/util/crc8.h +++ b/src/fw/util/crc8.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/date.c b/src/fw/util/date.c index acc8c476b9..9cd56994e0 100644 --- a/src/fw/util/date.c +++ b/src/fw/util/date.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "date.h" diff --git a/src/fw/util/date.h b/src/fw/util/date.h index 6d01e8d443..edf8c30fd0 100644 --- a/src/fw/util/date.h +++ b/src/fw/util/date.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/dict.c b/src/fw/util/dict.c index 201a03dca8..8b74741bee 100644 --- a/src/fw/util/dict.c +++ b/src/fw/util/dict.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "dict.h" diff --git a/src/fw/util/dict.h b/src/fw/util/dict.h index 7c1cf0768e..65b582d637 100644 --- a/src/fw/util/dict.h +++ b/src/fw/util/dict.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/generic_attribute.c b/src/fw/util/generic_attribute.c index 7fd1ca9190..7e9dc5229b 100644 --- a/src/fw/util/generic_attribute.c +++ b/src/fw/util/generic_attribute.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "generic_attribute.h" @@ -27,7 +14,7 @@ GenericAttribute *generic_attribute_find_attribute(GenericAttributeList *attr_li // Check that we do not read past the end of the buffer if ((cursor + sizeof(GenericAttribute) >= end) || (attribute->data + attribute->length > end)) { - PBL_LOG(LOG_LEVEL_WARNING, "Attribute list is invalid"); + PBL_LOG_WRN("Attribute list is invalid"); return NULL; } diff --git a/src/fw/util/generic_attribute.h b/src/fw/util/generic_attribute.h index 59d5970850..d9ae8f8c25 100644 --- a/src/fw/util/generic_attribute.h +++ b/src/fw/util/generic_attribute.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/graphics.h b/src/fw/util/graphics.h index ad85c5bfd6..4cbe1ec792 100644 --- a/src/fw/util/graphics.h +++ b/src/fw/util/graphics.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/hdlc.c b/src/fw/util/hdlc.c index afcd5f9659..176f4c6cad 100644 --- a/src/fw/util/hdlc.c +++ b/src/fw/util/hdlc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "hdlc.h" diff --git a/src/fw/util/hdlc.h b/src/fw/util/hdlc.h index 122c3c9788..1d2a970cf2 100644 --- a/src/fw/util/hdlc.h +++ b/src/fw/util/hdlc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/ihex.c b/src/fw/util/ihex.c index 3767fe4ba8..f1a2a915c8 100644 --- a/src/fw/util/ihex.c +++ b/src/fw/util/ihex.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/ihex.h" diff --git a/src/fw/util/ihex.h b/src/fw/util/ihex.h index a4ee0103fa..ef16b496c2 100644 --- a/src/fw/util/ihex.h +++ b/src/fw/util/ihex.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Intel HEX utilities diff --git a/src/fw/util/legacy_checksum.c b/src/fw/util/legacy_checksum.c index 33c5bcba00..fbba86e4de 100644 --- a/src/fw/util/legacy_checksum.c +++ b/src/fw/util/legacy_checksum.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/legacy_checksum.h" diff --git a/src/fw/util/legacy_checksum.h b/src/fw/util/legacy_checksum.h index 9bd2873714..674adcc177 100644 --- a/src/fw/util/legacy_checksum.h +++ b/src/fw/util/legacy_checksum.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/legacy_checksum_crc_table.py b/src/fw/util/legacy_checksum_crc_table.py index 5b9a0140d1..92bbdaea2e 100755 --- a/src/fw/util/legacy_checksum_crc_table.py +++ b/src/fw/util/legacy_checksum_crc_table.py @@ -1,17 +1,6 @@ #!/usr/bin/env python -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 from __future__ import print_function @@ -29,10 +18,11 @@ def precompute_table(bits): rr = (rr << 1) ^ CRC_POLY else: rr <<= 1 - lookup_table.append(rr & 0xffffffff) + lookup_table.append(rr & 0xFFFFFFFF) return lookup_table -print('static const uint32_t s_lookup_table[] = {') + +print("static const uint32_t s_lookup_table[] = {") for entry in precompute_table(4): - print(' 0x{:08x},'.format(entry)) -print('};') + print(" 0x{:08x},".format(entry)) +print("};") diff --git a/src/fw/util/lru_cache.c b/src/fw/util/lru_cache.c index 871c07e2f9..f00c4a64b3 100644 --- a/src/fw/util/lru_cache.c +++ b/src/fw/util/lru_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "lru_cache.h" diff --git a/src/fw/util/lru_cache.h b/src/fw/util/lru_cache.h index fe64e18336..1892654ff0 100644 --- a/src/fw/util/lru_cache.h +++ b/src/fw/util/lru_cache.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/macro.h b/src/fw/util/macro.h index d6eeb7ffe2..6fd3f09905 100644 --- a/src/fw/util/macro.h +++ b/src/fw/util/macro.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/mbuf.c b/src/fw/util/mbuf.c index 41b932297a..c13b40c621 100644 --- a/src/fw/util/mbuf.c +++ b/src/fw/util/mbuf.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mbuf.h" @@ -32,7 +19,6 @@ static PebbleMutex *s_free_list_lock; //! This array should be initialized with the maximum number of MBufs which may be allocated for //! each pool. static int s_mbuf_pool_space[] = { - [MBufPoolSmartstrap] = 2, #if UNITTEST [MBufPoolUnitTest] = 100, #endif @@ -66,6 +52,7 @@ static void prv_check_free_list(void) { } } +#if UNITTEST MBuf *mbuf_get(void *data, uint32_t length, MBufPool pool) { PBL_ASSERTN(pool < NumMBufPools); MBuf *m; @@ -91,6 +78,7 @@ MBuf *mbuf_get(void *data, uint32_t length, MBufPool pool) { mbuf_set_data(m, data, length); return m; } +#endif void mbuf_free(MBuf *m) { if (!m) { diff --git a/src/fw/util/mbuf.h b/src/fw/util/mbuf.h index e9e12f0837..c4db216d82 100644 --- a/src/fw/util/mbuf.h +++ b/src/fw/util/mbuf.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -50,7 +37,6 @@ //! Consumers of MBufs which use mbuf_get() should add an enum value and add the maximum number of //! MBufs which may be allocated for that pool to the MBUF_POOL_MAX_ALLOCATED array within mbuf.c. typedef enum { - MBufPoolSmartstrap, #if UNITTEST MBufPoolUnitTest, #endif diff --git a/src/fw/util/mbuf_iterator.c b/src/fw/util/mbuf_iterator.c index c882ab6f35..37e215ac35 100644 --- a/src/fw/util/mbuf_iterator.c +++ b/src/fw/util/mbuf_iterator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mbuf_iterator.h" diff --git a/src/fw/util/mbuf_iterator.h b/src/fw/util/mbuf_iterator.h index a300e68775..0e18f309f7 100644 --- a/src/fw/util/mbuf_iterator.h +++ b/src/fw/util/mbuf_iterator.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/net.h b/src/fw/util/net.h index 3d975cff3e..09ec7352af 100644 --- a/src/fw/util/net.h +++ b/src/fw/util/net.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/pack.h b/src/fw/util/pack.h index b58d21eaab..49b789938f 100644 --- a/src/fw/util/pack.h +++ b/src/fw/util/pack.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/packbits.c b/src/fw/util/packbits.c index 98a8ce9314..a1503f0949 100644 --- a/src/fw/util/packbits.c +++ b/src/fw/util/packbits.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "packbits.h" diff --git a/src/fw/util/packbits.h b/src/fw/util/packbits.h index 8d6ccd84cb..08dd8e0977 100644 --- a/src/fw/util/packbits.h +++ b/src/fw/util/packbits.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/pstring.c b/src/fw/util/pstring.c index 0cad1c49c1..e6b9441536 100644 --- a/src/fw/util/pstring.c +++ b/src/fw/util/pstring.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -177,16 +164,16 @@ PascalString16 *pstring_get_pstring16_from_list(PascalString16List *pstring16_li } void pstring_print_pstring(PascalString16 *pstring) { - PBL_LOG(LOG_LEVEL_DEBUG, "Length: %i ", pstring->str_length); + PBL_LOG_DBG("Length: %i ", pstring->str_length); char *buffer = task_malloc_check(sizeof(char) * (pstring->str_length + 1)); pstring_pstring16_to_string(pstring, buffer); - PBL_LOG(LOG_LEVEL_DEBUG, "%s", buffer); + PBL_LOG_DBG("%s", buffer); task_free(buffer); buffer = NULL; } void pstring_print_pstring16list(PascalString16List *list) { - PBL_LOG(LOG_LEVEL_DEBUG, "Data size: %i ", (list->pstrings)->data_size); + PBL_LOG_DBG("Data size: %i ", (list->pstrings)->data_size); PascalString16 *pstring; for (int i = 0; i < list->count; i++) { pstring = pstring_get_pstring16_from_list(list, i); diff --git a/src/fw/util/pstring.h b/src/fw/util/pstring.h index d890289f80..47e1bc1415 100644 --- a/src/fw/util/pstring.h +++ b/src/fw/util/pstring.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/rand.h b/src/fw/util/rand.h index 62b2b371fd..71d2821be2 100644 --- a/src/fw/util/rand.h +++ b/src/fw/util/rand.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/rand/rand.c b/src/fw/util/rand/rand.c index 0bc4d6619e..8c897fc5b5 100644 --- a/src/fw/util/rand/rand.c +++ b/src/fw/util/rand/rand.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/fw/util/ratio.h b/src/fw/util/ratio.h index 19cfb50d05..c11692efbf 100644 --- a/src/fw/util/ratio.h +++ b/src/fw/util/ratio.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/reverse.h b/src/fw/util/reverse.h index 29635ec9ed..508b05f0de 100644 --- a/src/fw/util/reverse.h +++ b/src/fw/util/reverse.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/shared_circular_buffer.c b/src/fw/util/shared_circular_buffer.c index 6c70f0584f..c8e1b240dc 100644 --- a/src/fw/util/shared_circular_buffer.c +++ b/src/fw/util/shared_circular_buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "shared_circular_buffer.h" @@ -99,7 +86,7 @@ bool shared_circular_buffer_write(SharedCircularBuffer* buffer, const uint8_t* d // If no clients, no need to write if (!buffer->clients) { - PBL_LOG(LOG_LEVEL_WARNING, "no readers"); + PBL_LOG_WRN("no readers"); return false; } diff --git a/src/fw/util/shared_circular_buffer.h b/src/fw/util/shared_circular_buffer.h index 40a41a3a18..ab1f69ed22 100644 --- a/src/fw/util/shared_circular_buffer.h +++ b/src/fw/util/shared_circular_buffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/sle.c b/src/fw/util/sle.c index da2f2b0b46..c077aafeac 100644 --- a/src/fw/util/sle.c +++ b/src/fw/util/sle.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "sle.h" diff --git a/src/fw/util/sle.h b/src/fw/util/sle.h index 807fb2ac16..f51248c3d0 100644 --- a/src/fw/util/sle.h +++ b/src/fw/util/sle.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/stats.c b/src/fw/util/stats.c index 2c4e5d5063..818ee40603 100644 --- a/src/fw/util/stats.c +++ b/src/fw/util/stats.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stats.h" diff --git a/src/fw/util/stats.h b/src/fw/util/stats.h index 613e52fdf8..be805c96db 100644 --- a/src/fw/util/stats.h +++ b/src/fw/util/stats.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/stringlist.c b/src/fw/util/stringlist.c index 36a8daf8fe..ffb55b658e 100644 --- a/src/fw/util/stringlist.c +++ b/src/fw/util/stringlist.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stringlist.h" diff --git a/src/fw/util/stringlist.h b/src/fw/util/stringlist.h index 7c9b6ebdeb..487d0c6f4a 100644 --- a/src/fw/util/stringlist.h +++ b/src/fw/util/stringlist.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/swap.h b/src/fw/util/swap.h index dd1ec942be..978108ef0f 100644 --- a/src/fw/util/swap.h +++ b/src/fw/util/swap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/test_base64.sh b/src/fw/util/test_base64.sh index 79c5d98a63..09559cd352 100755 --- a/src/fw/util/test_base64.sh +++ b/src/fw/util/test_base64.sh @@ -1,17 +1,6 @@ #!/bin/sh -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-FileCopyrightText: 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 gcc test_base64.c base64.c -I../ -g -std=c99 -DUNIT_TEST && ./a.out diff --git a/src/fw/util/time/mktime.c b/src/fw/util/time/mktime.c index 65402f8b29..4b5d59a7d9 100644 --- a/src/fw/util/time/mktime.c +++ b/src/fw/util/time/mktime.c @@ -169,7 +169,6 @@ time_t mktime(struct tm *tb) { */ tmptm1 -= tb->tm_gmtoff; - tmptm1 -= tb->tm_isdst * 60 * 60; /* * Convert this second count back into a time block structure. diff --git a/src/fw/util/time/time.c b/src/fw/util/time/time.c index 692f431bd4..7d2c276b78 100644 --- a/src/fw/util/time/time.c +++ b/src/fw/util/time/time.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/rtc.h" #include "syscall/syscall_internal.h" @@ -127,10 +114,10 @@ struct tm *time_to_tm(const time_t * tim_p, struct tm *res, bool utc_mode) { res->tm_zone[TZ_LEN - 1] = '\0'; local_time = utc_time; } else { - res->tm_gmtoff = time_get_gmtoffset(); res->tm_isdst = time_get_isdst(utc_time); + res->tm_gmtoff = time_get_gmtoffset() + (res->tm_isdst ? s_dst_adjust : 0); time_get_timezone_abbr(res->tm_zone, utc_time); - local_time = utc_time + res->tm_gmtoff + (res->tm_isdst ? s_dst_adjust : 0); + local_time = utc_time + res->tm_gmtoff; } int32_t days = local_time / SECONDS_PER_DAY; diff --git a/src/fw/util/time/time.h b/src/fw/util/time/time.h index cb05e99260..4729b1ddba 100644 --- a/src/fw/util/time/time.h +++ b/src/fw/util/time/time.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/units.h b/src/fw/util/units.h index 1eccb3f0b6..bc64e0ed2b 100644 --- a/src/fw/util/units.h +++ b/src/fw/util/units.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/fw/util/util_platform.c b/src/fw/util/util_platform.c index 6fd950aaa4..ee29b3cfe8 100644 --- a/src/fw/util/util_platform.c +++ b/src/fw/util/util_platform.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "console/dbgserial.h" #include "system/logging.h" diff --git a/src/fw/vector_table.c b/src/fw/vector_table.c index 7aa11f43de..2e8ddb2789 100644 --- a/src/fw/vector_table.c +++ b/src/fw/vector_table.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/attributes.h" @@ -45,17 +32,19 @@ ALIAS("Default_Handler") void SysTick_Handler(void); // External Interrupts #define IRQ_DEF(idx, irq) ALIAS("Default_Handler") void irq##_IRQHandler(void); -#if defined(MICRO_FAMILY_NRF52840) -# include "irq_nrf52840.def" -#elif defined(MICRO_FAMILY_SF32LB52) +#if defined(CONFIG_SOC_NRF52) +# include "irq_nrf52.def" +#elif defined(CONFIG_SOC_SF32LB52) # include "irq_sf32lb52.def" +#elif defined(CONFIG_QEMU) +# include "irq_qemu.def" #else -# include "irq_stm32.def" +# error "No IRQ definition for this MICRO_FAMILY" #endif #undef IRQ_DEF -#if PROFILE_INTERRUPTS +#ifdef CONFIG_PROFILE_INTERRUPTS #include "system/profiler.h" #define IRQ_DEF(idx, irq) static inline void irq##_IRQHandler_profiled(void) { \ extern ProfilerNode g_profiler_node_##irq##_IRQ; \ @@ -63,15 +52,17 @@ ALIAS("Default_Handler") void SysTick_Handler(void); irq##_IRQHandler();\ profiler_node_stop(&g_profiler_node_##irq##_IRQ, DWT->CYCCNT); \ } -#if defined(MICRO_FAMILY_NRF52840) +#if defined(CONFIG_SOC_NRF52) # include "irq_nrf52.def" -#elif defined(MICRO_FAMILY_SF32LB52) +#elif defined(CONFIG_SOC_SF32LB52) # include "irq_sf32lb52.def" +#elif defined(CONFIG_QEMU) +# include "irq_qemu.def" #else -# include "irq_stm32.def" +# error "No IRQ definition for this MICRO_FAMILY" #endif #undef IRQ_DEF -#endif // PROFILE_INTERRUPTS +#endif // CONFIG_PROFILE_INTERRUPTS EXTERNALLY_VISIBLE SECTION(".isr_vector") const void * const vector_table[] = { @@ -93,17 +84,19 @@ EXTERNALLY_VISIBLE SECTION(".isr_vector") const void * const vector_table[] = { SysTick_Handler, // External Interrupts -#if PROFILE_INTERRUPTS +#ifdef CONFIG_PROFILE_INTERRUPTS #define IRQ_DEF(idx, irq) [idx + 16] = irq##_IRQHandler_profiled, #else #define IRQ_DEF(idx, irq) [idx + 16] = irq##_IRQHandler, #endif -#if defined(MICRO_FAMILY_NRF52840) -# include "irq_nrf52840.def" -#elif defined(MICRO_FAMILY_SF32LB52) +#if defined(CONFIG_SOC_NRF52) +# include "irq_nrf52.def" +#elif defined(CONFIG_SOC_SF32LB52) # include "irq_sf32lb52.def" +#elif defined(CONFIG_QEMU) +# include "irq_qemu.def" #else -# include "irq_stm32.def" +# error "No IRQ definition for this MICRO_FAMILY" #endif #undef IRQ_DEF }; diff --git a/src/fw/wscript b/src/fw/wscript index 365c5756ef..42ee5b6c2a 100644 --- a/src/fw/wscript +++ b/src/fw/wscript @@ -8,8 +8,6 @@ from waflib.Configure import conf from waflib.TaskGen import before_method, feature from waflib.Tools.ccroot import link_task -import tools.mpu_calc -import waftools.asm import waftools.compress import waftools.generate_log_strings_json import waftools.generate_timezone_data @@ -18,6 +16,8 @@ import waftools.ldscript import waftools.objcopy import waftools.pblboot +from tools.pebble_sdk_platform import pebble_platforms + @feature("c") @before_method('apply_link') @@ -56,16 +56,6 @@ def get_tintin_fw_node(ctx, subdir=None): return ctx.path.get_bld().make_node(subpath) -@conf -def get_tintin_fw_node_prf(ctx): - return ctx.get_tintin_fw_node('prf') - - -@conf -def get_tintin_boot_node(ctx): - return ctx.path.get_bld().make_node('src/boot/tintin_boot.bin') - - def options(opt): opt.add_option( "--prf-as-firmware", @@ -85,21 +75,18 @@ def options(opt): def configure(conf): conf.load('binary_header') - conf.recurse('applib') - conf.recurse('services') - if conf.is_silk(): - conf.env.append_value('DEFINES', ['MFG_INFO_RECORDS_TEST_RESULTS']) + platform = pebble_platforms[conf.env.PLATFORM_NAME] + define = 'MAX_FONT_GLYPH_SIZE={}'.format(platform['MAX_FONT_GLYPH_SIZE']) + conf.env.append_value('DEFINES', [define]) - if conf.options.sdkshell: - conf.env.NORMAL_SHELL = 'sdk' - conf.env.append_value('DEFINES', ['SHELL_SDK']) - else: - conf.env.NORMAL_SHELL = 'normal' + conf.env.NORMAL_SHELL = 'sdk' if conf.env.CONFIG_SHELL_SDK else 'normal' conf.env.PRF_AS_FIRMWARE = conf.options.prf_as_firmware + if conf.options.prf_as_firmware: + conf.env.append_value('DEFINES', 'RECOVERY_FW_AS_FW') - if conf.capability('HAS_PBLBOOT'): + if conf.env.CONFIG_PBLBOOT: conf.env.SLOT = conf.options.slot conf.env.append_value('DEFINES', f'FIRMWARE_SLOT_{conf.options.slot}') else: @@ -107,104 +94,72 @@ def configure(conf): def _generate_memory_layout(bld): - if bld.is_cutts() or bld.is_robert(): - ldscript_template = bld.path.find_node('stm32f7xx_flash_fw.ld.template') - elif bld.is_silk(): - ldscript_template = bld.path.find_node('stm32f412_flash_fw.ld.template') - elif bld.is_snowy_compatible(): - ldscript_template = bld.path.find_node('stm32f439_flash_fw.ld.template') - elif bld.is_tintin(): - ldscript_template = bld.path.find_node('stm32f2xx_flash_fw.ld.template') - elif bld.is_asterix(): + if bld.env.CONFIG_QEMU: + ldscript_template = bld.path.find_node('qemu_flash_fw.ld.template') + elif bld.env.CONFIG_BOARD_FAMILY_ASTERIX: ldscript_template = bld.path.find_node('nrf52840_flash_fw.ld.template') - elif bld.is_obelix(): + elif bld.env.CONFIG_BOARD_FAMILY_OBELIX or bld.env.CONFIG_BOARD_FAMILY_GETAFIX: ldscript_template = bld.path.find_node('sf32lb52_flash_fw.ld.template') # Determine sizes so we can later calculate FLASH_LENGTH_* - if bld.env.MICRO_FAMILY == 'STM32F2': - # Bootloader - offset_size = '16K' - flash_size = '512K' + if bld.env.CONFIG_QEMU: + flash_size = 4 * 1024 * 1024 + offset_size = 0 + fw_max_size = flash_size - elif bld.env.MICRO_FAMILY == 'STM32F4': - if bld.env.BOARD in ('snowy_evt', 'snowy_evt2', 'spalding_evt'): - # Bootloader - offset_size = '64K' - else: - # Bootloader - offset_size = '16K' - if bld.is_silk() and bld.variant == 'prf': - # silk PRF is limited to 512k to save on SPI flash space - flash_size = '512K' - else: - flash_size = '1024K' - - elif bld.env.MICRO_FAMILY == 'STM32F7': - # Bootloader - offset_size = '32K' - flash_size = '2048K' - - elif bld.env.MICRO_FAMILY == 'SF32LB52': + elif bld.env.CONFIG_SOC_SF32LB52: + flash_size = 32 * 1024 * 1024 ptable_size = 64 * 1024 bootloader_size = 64 * 1024 slot_size = 3072 * 1024 resources_size = 2048 * 1024 - prf_size = 512 * 1024 - if bld.variant == 'prf' and not bld.env.PRF_AS_FIRMWARE: - offset_size = str( - ptable_size + bootloader_size + 2 * slot_size + 2 * resources_size - ) - flash_size = str(prf_size) + prf_size = 576 * 1024 + if bld.env.VARIANT == 'prf' and not (bld.env.CONFIG_MFG or bld.env.PRF_AS_FIRMWARE): + offset_size = ptable_size + bootloader_size + 2 * slot_size + 2 * resources_size + fw_max_size = prf_size else: offset_size = ptable_size + bootloader_size if bld.env.SLOT == 1: offset_size += slot_size - offset_size = str(offset_size) - flash_size = str(slot_size) + offset_size = offset_size + fw_max_size = slot_size - elif bld.env.MICRO_FAMILY == 'NRF52840': + elif bld.env.CONFIG_SOC_NRF52: # Bootloader - offset_size = '32K' - if bld.is_asterix() and bld.variant == 'prf' and not bld.env.IS_MFG: - flash_size = '512K' + offset_size = 32 * 1024 + flash_size = 1024 * 1024 + if bld.env.CONFIG_BOARD_FAMILY_ASTERIX and bld.env.VARIANT == 'prf' and not bld.env.CONFIG_MFG: + fw_max_size = flash_size // 2 else: - flash_size = '1024K' - - if bld.env.QEMU: - flash_size = '4M' - - if bld.env.FLASH_ITCM: - flash_origin = '0x00200000' - elif bld.env.MICRO_FAMILY == 'NRF52840': - flash_origin = '0x00000000' - elif bld.env.MICRO_FAMILY == 'SF32LB52': - flash_origin = '0x12000000' + fw_max_size = flash_size + + if bld.env.CONFIG_QEMU: + flash_size = 4 * 1024 * 1024 + fw_max_size = flash_size + + if bld.env.CONFIG_QEMU: + flash_origin = 0x00000000 + elif bld.env.FLASH_ITCM: + flash_origin = 0x00200000 + elif bld.env.CONFIG_SOC_NRF52: + flash_origin = 0x00000000 + elif bld.env.CONFIG_SOC_SF32LB52: + flash_origin = 0x12000000 else: - flash_origin = '0x08000000' + flash_origin = 0x08000000 firmware_offset = 0 - if bld.env.MICRO_FAMILY == 'SF32LB52': - firmware_offset = 512 + if bld.env.CONFIG_SOC_SF32LB52: + firmware_offset = 4096 bld.env.FIRMWARE_OFFSET = firmware_offset bld.env.append_value('DEFINES', [f'FIRMWARE_OFFSET={firmware_offset}']) # Determine FLASH_LENGTH_* - fw_flash_length = '%(flash_size)s - %(offset_size)s - %(firmware_offset)d' % locals() - fw_flash_origin = '%(flash_origin)s + %(offset_size)s + %(firmware_offset)d' % locals() - - if bld.env.MICRO_FAMILY == 'STM32F2' and bld.variant == '': - # If we're building a tintin normal firmware make sure to plug in all the symbols that we - # want to use from the bootloader. + fw_flash_length = '%(fw_max_size)d - %(firmware_offset)d' % locals() + fw_flash_origin = '0x%(flash_origin)08x + %(offset_size)d + %(firmware_offset)d' % locals() - with open(bld.path.find_node('bootloader_symbols.json').abspath(), 'r') as f: - bootloader_symbols_json = json.load(f) - - bootloader_symbols = bootloader_symbols_json['bootloader_symbols'].items() - bootloader_symbol_strings = ("%s = %s;" % (n, v) for n, v in bootloader_symbols) - bootloader_symbol_definitions = "\n ".join(bootloader_symbol_strings) - else: - bootloader_symbol_definitions = "" + bootloader_symbol_definitions = "" # Determine ram layout @@ -214,12 +169,14 @@ def _generate_memory_layout(bld): AppRamSize = collections.namedtuple('AppRamSize', 'app_segment runtime_reserved') APP_RAM_SIZES = { - 'aplite': AppRamSize(25952, 6820), - 'basalt': AppRamSize(66 * 1024, 30 * 1024), - 'chalk': AppRamSize(66 * 1024, 30 * 1024), + 'aplite': AppRamSize(28000, 6820), + 'basalt': AppRamSize(68 * 1024, 30 * 1024), + 'chalk': AppRamSize(68 * 1024, 30 * 1024), # FIXME: The runtime_reserved size could be reduced for diorite - 'diorite': AppRamSize(66 * 1024, 30 * 1024), - 'emery': AppRamSize(130 * 1024, 62 * 1024), + 'diorite': AppRamSize(68 * 1024, 30 * 1024), + 'emery': AppRamSize(132 * 1024, 62 * 1024), + 'flint': AppRamSize(68 * 1024, 30 * 1024), + 'gabbro': AppRamSize(132 * 1024, 94 * 1024), } APP_UNSUPPORTED = AppRamSize(0, 0) @@ -239,53 +196,24 @@ def _generate_memory_layout(bld): # hardware capabilities watch model itself - i.e. chalk vs basalt). We want to define # APP_RAM_*X_SIZE macros for each app execution environment supported by the model so we # don't need to hard-code these in the FW itself. - if bld.is_tintin(): - app_ram_size_2x = APP_RAM_SIZES['aplite'] - app_ram_size_3x = APP_RAM_SIZES['aplite'] - app_ram_size_4x = APP_UNSUPPORTED - # We have a 128k continuous block of RAM. - total_ram = (0x20000000, 128 * 1024) - elif bld.is_snowy(): - app_ram_size_2x = APP_RAM_SIZES['aplite'] - app_ram_size_3x = APP_RAM_SIZES['basalt'] - app_ram_size_4x = APP_RAM_SIZES['basalt'] - # We have a 192k continuous block of RAM, plus a separate 64k of CCM which we don't care - # about here. - total_ram = (0x20000000, 192 * 1024) - elif bld.is_spalding(): - app_ram_size_2x = APP_UNSUPPORTED - app_ram_size_3x = APP_RAM_SIZES['chalk'] - app_ram_size_4x = APP_RAM_SIZES['chalk'] - # We have a 192k continuous block of RAM, plus a separate 64k of CCM which we don't care - # about here. - total_ram = (0x20000000, 192 * 1024) - elif bld.is_silk(): - app_ram_size_2x = APP_RAM_SIZES['aplite'] - app_ram_size_3x = APP_RAM_SIZES['aplite'] - app_ram_size_4x = APP_RAM_SIZES['diorite'] - # We have a 256k continuous block of RAM. - total_ram = (0x20000000, 256 * 1024) - elif bld.is_cutts() or bld.is_robert(): + if bld.env.CONFIG_PLATFORM_EMERY: app_ram_size_2x = APP_RAM_SIZES['aplite'] app_ram_size_3x = APP_RAM_SIZES['basalt'] app_ram_size_4x = APP_RAM_SIZES['emery'] - # We block off the 128k of DTCM for variables marked as DTCM_BSS. We use the remaining 384k - # of SRAM as a continuous block of RAM. - total_ram = (0x20020000, 384 * 1024) - elif bld.is_asterix(): + # We have 512K of SRAM, last 1K reserved for LCPU IPC + total_ram = (0x20000000, (512 - 1) * 1024) + elif bld.env.CONFIG_PLATFORM_FLINT: app_ram_size_2x = APP_RAM_SIZES['aplite'] app_ram_size_3x = APP_RAM_SIZES['aplite'] - app_ram_size_4x = APP_RAM_SIZES['diorite'] + app_ram_size_4x = APP_RAM_SIZES['flint'] retained_size = 256 total_ram = (0x20000000 + retained_size, 256 * 1024 - retained_size) - elif bld.is_obelix(): - app_ram_size_2x = APP_RAM_SIZES['aplite'] - app_ram_size_3x = APP_RAM_SIZES['basalt'] - app_ram_size_4x = APP_RAM_SIZES['emery'] + elif bld.env.CONFIG_PLATFORM_GABBRO: + app_ram_size_2x = APP_UNSUPPORTED + app_ram_size_3x = APP_RAM_SIZES['chalk'] + app_ram_size_4x = APP_RAM_SIZES['gabbro'] # We have 512K of SRAM, last 1K reserved for LCPU IPC - # FIXME(SF32LB52): due to MPU find_subregions_for_region assuming - # ARMv7 subregion limitations, we need to reserve 32K! - total_ram = (0x20000000, (512 - 32) * 1024) + total_ram = (0x20000000, (512 - 1) * 1024) else: bld.fatal("No set of supported SDK platforms defined for this board") @@ -319,25 +247,12 @@ def _generate_memory_layout(bld): APP_RAM_SIZE=app_ram[1], WORKER_RAM_ADDR="0x{:x}".format(worker_ram[0]), WORKER_RAM_SIZE=worker_ram[1], - FLASH_ORIGIN=flash_origin, + FLASH_ORIGIN="0x{:x}".format(flash_origin), FW_FLASH_ORIGIN=fw_flash_origin, FW_FLASH_LENGTH=fw_flash_length, FLASH_SIZE=flash_size, BOOTLOADER_SYMBOLS=bootloader_symbol_definitions) - app_mpu_region = tools.mpu_calc.find_subregions_for_region(app_ram[0], app_ram[1]) - worker_mpu_region = tools.mpu_calc.find_subregions_for_region(worker_ram[0], worker_ram[1]) - - bld(features='subst', - source=bld.path.find_node('kernel/mpu_regions.template.h'), - target=bld.path.get_bld().make_node('kernel/mpu_regions.auto.h'), - APP_BASE_ADDRESS="0x{:x}".format(app_mpu_region.address), - APP_SIZE="0x{:x}".format(app_mpu_region.size), - APP_DISABLED_SUBREGIONS="0b{:08b}".format(app_mpu_region.disabled_subregion), - WORKER_BASE_ADDRESS="0x{:x}".format(worker_mpu_region.address), - WORKER_SIZE="0x{:x}".format(worker_mpu_region.size), - WORKER_DISABLED_SUBREGIONS=" 0b{:08b}".format(worker_mpu_region.disabled_subregion)) - bld(features='subst', source=bld.path.find_node('process_management/sdk_memory_limits.template.h'), target=bld.path.get_bld().make_node('process_management/sdk_memory_limits.auto.h'), @@ -350,8 +265,12 @@ def _generate_memory_layout(bld): def _link_firmware(bld, sources): - if bld.is_spalding(): - sources += ['board/displays/display_spalding.c'] + if bld.env.CONFIG_QEMU: + if bld.env.CONFIG_BOARD_QEMU_GABBRO: + # qemu_gabbro needs the getafix round display row info data + sources += ['board/displays/display_getafix.c'] + elif bld.env.CONFIG_BOARD_FAMILY_GETAFIX: + sources += ['board/displays/display_getafix.c'] fw_linkflags = ['-Wl,--cref', '-Wl,-Map=tintin_fw.map', @@ -378,25 +297,30 @@ def _link_firmware(bld, sources): 'fw_services', 'gcc', 'proto_schemas', - 'jerry_core', - 'jerry_libm', 'libbtutil', 'libos', 'libutil', 'nanopb', 'pblibc', - 'root_includes', + 'pbl_includes', + 'soc', 'speex', 'startup', 'tinymt32', 'upng'] + uses.extend(bld.env.FW_APPS) - if bld.env.memfault: + if bld.env.CONFIG_MEMFAULT: fw_linkflags.append('-Wl,--require-defined=g_memfault_build_id') uses.append('memfault') ldscript = _generate_memory_layout(bld) + ldscripts = [ldscript, 'fw_common.ld'] + if bld.env.CONFIG_MEMFAULT: + ldscripts.append(bld.srcnode.find_node( + 'third_party/memfault/port/memfault_compact_log.ld')) + if bld.env.NO_LINK: # Only build the object files bld.objects(source=sources, @@ -411,7 +335,7 @@ def _link_firmware(bld, sources): lib=['gcc'], target=elf_node, includes='fonts', - ldscript=[ldscript, 'fw_common.ld'], + ldscript=ldscripts, linkflags=fw_linkflags) x.env.append_value('LINKFLAGS', fw_linkflags) @@ -424,7 +348,7 @@ def _link_firmware(bld, sources): else: extra_args = '' - if bld.capability('HAS_PBLBOOT'): + if bld.env.CONFIG_PBLBOOT: nohdr_hex_node = elf_node.change_ext('.nohdr.hex') bld(rule=waftools.objcopy.objcopy_hex, source=elf_node, target=nohdr_hex_node, extra_args=extra_args) hex_node = elf_node.change_ext('.hex') @@ -440,7 +364,7 @@ def _link_firmware(bld, sources): bld(rule=waftools.objcopy.objcopy_bin, source=elf_node, target=bin_node) # Create the log_strings .elf and check the format specifier rules - if 'PBL_LOGS_HASHED' in bld.env.DEFINES: + if bld.env.CONFIG_LOG_HASHED: fw_loghash_node = bld.path.get_bld().make_node('tintin_fw_loghash_dict.json') bld(rule=waftools.generate_log_strings_json.wafrule, source=elf_node, target=fw_loghash_node) @@ -450,61 +374,25 @@ def _link_firmware(bld, sources): def _get_mfg_paths(bld): """Return a list of directories we want to build to support manufacturing on a platform""" - if bld.is_tintin(): - return ('mfg/tintin',) - elif bld.is_snowy(): - return ('mfg/snowy',) - elif bld.is_spalding(): - return ('mfg/spalding',) - elif bld.is_silk(): - return ('mfg/silk',) - elif bld.is_cutts(): - # This probably should end up as 'mfg/cutts' if this comes back - return ('mfg/robert',) - elif bld.is_robert(): - return ('mfg/robert',) - elif bld.is_asterix(): + if bld.env.CONFIG_QEMU: + return ('mfg/qemu',) + elif bld.env.CONFIG_BOARD_FAMILY_ASTERIX: return ('mfg/asterix',) - elif bld.is_obelix(): + elif bld.env.CONFIG_BOARD_FAMILY_OBELIX: return ('mfg/obelix',) + elif bld.env.CONFIG_BOARD_FAMILY_GETAFIX: + return ('mfg/getafix',) else: bld.fatal('No MFG configuration for board %s' % bld.env.BOARD) def _get_dbg_paths(bld): """Return a list of directories we want to build to support debug logging on a platform""" - if bld.is_tintin(): - return ('debug/legacy',) - else: - return ('debug/default',) - -def _gen_fpga_bitstream_header(bld): - if bld.is_snowy(): - if bld.env.BOARD in ('snowy_bb', 'snowy_evt'): - source_bitstream = '../../platform/snowy/snowy_framebuffer_evt.fpga' - else: - source_bitstream = '../../platform/snowy/Snowy_LP1K_framebuffer.fpga' - elif bld.is_spalding(): - if bld.env.BOARD in ('spalding_bb2',): - source_bitstream = '../../platform/snowy/Spalding_LP1K_framebuffer.fpga' - else: - source_bitstream = '../../platform/snowy/Spalding_UL1K_framebuffer.fpga' - elif bld.is_cutts(): - source_bitstream = '../../platform/robert/Cutts_UL1K_framebuffer_144x168_bitmap.fpga' - elif bld.is_robert(): - source_bitstream = '../../platform/robert/Robert_UL1K_framebuffer_bitmap.fpga' - else: - bld.fatal('No FPGA available for {}'.format(bld.env.BOARD)) - - bld(features='binary_header', - source=source_bitstream, - target='drivers/display/ice40lp/fpga_bitstream.auto.h', - array_name='s_fpga_bitstream', - compressed=True) + return ('debug/default',) def _get_comm_sources(bld, is_recovery): excl = [] - if bld.env.QEMU: + if bld.env.CONFIG_QEMU: excl.append('comm/internals/profiles/ispp.c') if is_recovery: @@ -517,8 +405,8 @@ def _get_comm_sources(bld, is_recovery): def _build_recovery(bld): - source_dirs = ['apps/core_apps/**', - 'apps/prf_apps/**', + source_dirs = ['apps/core/**', + 'apps/prf/**', 'process_management', 'process_state/**', 'console', @@ -543,18 +431,38 @@ def _build_recovery(bld): 'process_management/app_menu_data_source.c', 'resource/resource_storage_file.c'] - if 'MFG_INFO_RECORDS_TEST_RESULTS' not in bld.env.DEFINES: - excludes.extend('mfg/results_ui.c') + if not bld.env.CONFIG_BOARD_FAMILY_ASTERIX: + excludes.extend([ + 'apps/prf/mfg_speaker_asterix.c', + 'apps/prf/mfg_mic_asterix.c', + ]) + + if not bld.env.CONFIG_BOARD_FAMILY_OBELIX: + excludes.extend([ + 'apps/prf/mfg_speaker_obelix.c', + 'apps/prf/mfg_mic_obelix.c', + ]) - if not bld.is_asterix(): + if not bld.env.CONFIG_BOARD_FAMILY_OBELIX or not bld.env.CONFIG_MFG: excludes.extend([ - 'apps/prf_apps/mfg_speaker_app.c', - 'apps/prf_apps/mfg_mic_app.c', - 'apps/prf_apps/mfg_sine_wave.c', + 'apps/prf/mfg_hrm_ctr_leakage_obelix.c', ]) - if bld.is_silk(): - bld.env.append_value('DEFINES', ['QSPI_DMA_DISABLE=1']) + if not bld.env.CONFIG_BOARD_FAMILY_GETAFIX: + excludes.extend([ + 'apps/prf/mfg_mic_getafix.c', + ]) + + # Exclude magnetometer test for platforms without magnetometer + if not bld.env.CONFIG_MAG: + excludes.extend([ + 'apps/prf/mfg_mag.c', + ]) + + if not bld.env.CONFIG_TOUCH: + excludes.extend([ + 'apps/prf/mfg_touch.c', + ]) sources = sum([bld.path.ant_glob('%s/*.c' % d, excl=excludes) for d in source_dirs], []) sources.extend(_get_comm_sources(bld, True)) @@ -565,28 +473,10 @@ def _build_recovery(bld): sources.append(bld.path.get_bld().make_node('builtin_resources.auto.c')) - if bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert(): - _gen_fpga_bitstream_header(bld) - - if bld.is_snowy(): - bld(features='binary_header', - source='../../platform/snowy/snowy_boot.fpga', - target='mfg/snowy/snowy_boot.fpga.auto.h', - array_name='s_boot_fpga') - elif bld.is_spalding(): - bld(features='binary_header', - source='../../platform/snowy/spalding_boot.fpga', - target='mfg/spalding/spalding_boot.fpga.auto.h', - array_name='s_boot_fpga') - if 'BOOTLOADER_TEST_STAGE1=1' in bld.env.DEFINES: - bld(features='binary_header', - source=bld.path.make_node('../../bootloader_test_stage2.bin'), - target='bootloader_test_bin.auto.h', - array_name='s_bootloader_test_stage2') - if bld.env.DISABLE_PROMPT: + if not bld.env.CONFIG_PROMPT: sources = [ x for x in sources if not x.abspath().endswith('console/prompt.c') ] sources = [ x for x in sources if not x.abspath().endswith('console/prompt_commands.c') ] @@ -594,15 +484,12 @@ def _build_recovery(bld): def _get_launcher_globs_to_exclude(bld): - system_launchers_root_path = 'apps/system_apps/launcher/' + system_launchers_root_path = 'apps/system/launcher/' legacy_launcher_glob = system_launchers_root_path + 'legacy/**' default_launcher_glob = system_launchers_root_path + 'default/**' launcher_globs_to_exclude = [legacy_launcher_glob, default_launcher_glob] - if bld.is_tintin(): - launcher_glob_to_use = legacy_launcher_glob - else: - launcher_glob_to_use = default_launcher_glob + launcher_glob_to_use = default_launcher_glob launcher_globs_to_exclude.remove(launcher_glob_to_use) return launcher_globs_to_exclude @@ -620,7 +507,6 @@ def _build_normal(bld): source_dirs = ['process_management', 'process_state/**', - 'bt_test', 'console', 'debug', 'flash_region', @@ -636,59 +522,41 @@ def _build_normal(bld): 'shell/%s/**' % bld.env.NORMAL_SHELL, 'util/**'] - source_files = [] excludes = [] - source_dirs.append('apps/core_apps/**') + source_dirs.append('apps/core/**') if bld.env.NORMAL_SHELL == 'sdk': source_dirs.append('apps/sdk/**') - source_dirs.append('apps/system_apps/timeline') - if bld.capability('HAS_SDK_SHELL4'): - source_dirs.append('apps/system_apps/launcher/default/**') - else: - excludes.append('process_management/app_custom_icon.c') - excludes.append('process_management/app_menu_data_source.c') + source_dirs.append('apps/system/timeline') + source_dirs.append('apps/system/launcher/default/**') else: - source_dirs.extend(('apps/%s/**' % d for d in ('system_apps', 'watch'))) - if bld.is_spalding(): + source_dirs.extend(('apps/%s/**' % d for d in ('system', 'watch'))) + if bld.env.CONFIG_BOARD_FAMILY_GETAFIX: excludes.append('apps/watch/tictoc/default') + excludes.append('apps/watch/tictoc/bw') + elif bld.env.CONFIG_SCREEN_COLOR_DEPTH_BITS == 1: + excludes.append('apps/watch/tictoc/default') + excludes.append('apps/watch/tictoc/round') else: - excludes.append('apps/watch/tictoc/spalding') - - if bld.is_cutts(): - excludes.append('apps/watch/kickstart') + excludes.append('apps/watch/tictoc/bw') + excludes.append('apps/watch/tictoc/round') source_dirs.extend(_get_mfg_paths(bld)) source_dirs.extend(_get_dbg_paths(bld)) - if bld.env.BUILD_TEST_APPS: - source_dirs.append('apps/demo_apps/**') - if bld.is_tintin() or bld.is_silk(): - excludes.append('apps/demo_apps/vibe_score_demo.c') - elif bld.env.PERFORMANCE_TESTS: - source_dirs.append('apps/demo_apps/gfx_tests') - excludes.extend(_get_launcher_globs_to_exclude(bld)) - if not bld.capability('HAS_BUILTIN_HRM'): + if not bld.env.CONFIG_HRM: excludes.append('popups/ble_hrm/**') - if bld.is_tintin(): - excludes.append('apps/system_apps/health/**') - excludes.append('apps/system_apps/settings/settings_vibe_patterns.c') - excludes.append('apps/system_apps/weather/**') - excludes.append('apps/system_apps/workout/**') - sources = sum([bld.path.ant_glob('%s/*.c' % d, excl=excludes) for d in source_dirs], []) - sources.extend(bld.path.make_node(x) for x in source_files) sources.extend(_get_comm_sources(bld, False)) sources.extend(bld.path.ant_glob('*.c')) sources.extend(bld.path.ant_glob('*.[sS]')) if bld.env.NORMAL_SHELL == 'sdk': - sources.append('apps/system_apps/app_fetch_ui.c') - if bld.capability('HAS_SDK_SHELL4'): - sources.append('apps/system_apps/watchfaces.c') + sources.append('apps/system/app_fetch_ui.c') + sources.append('apps/system/watchfaces.c') gettexts = [] gettexts.extend(sources) @@ -700,49 +568,36 @@ def _build_normal(bld): source='fw.pot services/services.pot applib/applib.pot', target='tintin.pot') - if bld.is_snowy_compatible() or bld.is_cutts() or bld.is_robert(): - _gen_fpga_bitstream_header(bld) - - if bld.is_snowy(): - bld(features='binary_header', - source='../../platform/snowy/snowy_boot.fpga', - target='mfg/snowy/snowy_boot.fpga.auto.h', - array_name='s_boot_fpga') - elif bld.is_spalding(): - bld(features='binary_header', - source='../../platform/snowy/spalding_boot.fpga', - target='mfg/spalding/spalding_boot.fpga.auto.h', - array_name='s_boot_fpga') sources.append(bld.path.get_bld().make_node('pebble.auto.c')) sources.append(bld.path.get_bld().make_node('resource/pfs_resource_table.auto.c')) sources.append(bld.path.get_bld().make_node('resource/timeline_resource_table.auto.c')) sources.append(bld.path.get_bld().make_node('builtin_resources.auto.c')) - if bld.env.DISABLE_PROMPT: + if not bld.env.CONFIG_PROMPT: sources = [ x for x in sources if not x.abspath().endswith('console/prompt.c') ] sources = [ x for x in sources if not x.abspath().endswith('console/prompt_commands.c') ] _link_firmware(bld, sources) def build(bld): + bld.env.FW_APPS = [] + # FIXME create applib_includes or something like that fw_includes_use=['libbtutil_includes', 'libos_includes', 'libutil_includes', - 'root_includes', + 'pbl_includes', 'freertos_includes', 'idl_includes', 'nanopb_includes'] - if bld.options.memfault: + if bld.env.CONFIG_MEMFAULT: fw_includes_use.append('memfault_includes') - if bld.env.MICRO_FAMILY.startswith('STM32'): - fw_includes_use.append('stm32_stdlib') - elif bld.env.MICRO_FAMILY.startswith('NRF'): + if bld.env.CONFIG_SOC_NRF52: fw_includes_use.append('hal_nordic') - elif bld.env.MICRO_FAMILY.startswith('SF32LB'): + elif bld.env.CONFIG_SOC_SF32LB52: fw_includes_use.append('hal_sifli') bld(export_includes=['.', @@ -751,15 +606,6 @@ def build(bld): use=fw_includes_use, name='fw_includes') - if bld.variant in ('applib', 'test_rocky_emx'): - bld.set_env(bld.all_envs['local']) - bld.env.DEFINES.extend(['UNITTEST', 'SCREEN_COLOR_DEPTH_BITS=8', - 'DISP_COLS=144', 'DISP_ROWS=168', - 'DISPLAY_FRAMEBUFFER_BYTES=%d' % (144 * 168), - 'PBL_COLOR', 'PBL_RECT']) - bld.recurse('applib') - return - # Truncate the commit to fit in our versions struct. This may cause an ambiguous commit # hash, but it's better than killing the build because the commit doesn't fit. git_rev = waftools.gitinfo.get_git_revision(bld) @@ -780,14 +626,12 @@ def build(bld): bld.recurse('shell') bld.recurse('services') bld.recurse('applib') + bld.recurse('soc') - # We can't use DMA until we root cause PBL-37278 - if bld.is_silk(): - bld.env.append_value('DEFINES', ['QSPI_DMA_DISABLE=1']) - - if bld.variant == 'prf': + if bld.env.VARIANT == 'prf': _build_recovery(bld) else: + bld.recurse('apps') _build_normal(bld) # vim:filetype=python diff --git a/src/idl/nanopb/activity.proto b/src/idl/nanopb/activity.proto index 7ff62f8f3e..656f77b86d 100644 --- a/src/idl/nanopb/activity.proto +++ b/src/idl/nanopb/activity.proto @@ -1,16 +1,5 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: 2024 Google LLC +// SPDX-License-Identifier: Apache-2.0 /** These schemas define the structures to communicate activity/workout types diff --git a/src/idl/nanopb/common.proto b/src/idl/nanopb/common.proto index 968b9098d8..b962080e6d 100644 --- a/src/idl/nanopb/common.proto +++ b/src/idl/nanopb/common.proto @@ -1,16 +1,5 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: 2024 Google LLC +// SPDX-License-Identifier: Apache-2.0 /** These schemas define entities that are reused in multiple places in other diff --git a/src/idl/nanopb/event.proto b/src/idl/nanopb/event.proto index 291e311b7b..6a8fadca28 100644 --- a/src/idl/nanopb/event.proto +++ b/src/idl/nanopb/event.proto @@ -1,16 +1,5 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: 2024 Google LLC +// SPDX-License-Identifier: Apache-2.0 /** Events represent discrete moments in time or windows in time where something diff --git a/src/idl/nanopb/measurements.proto b/src/idl/nanopb/measurements.proto index 93ce0d8a09..c8f164f846 100644 --- a/src/idl/nanopb/measurements.proto +++ b/src/idl/nanopb/measurements.proto @@ -1,16 +1,5 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: 2024 Google LLC +// SPDX-License-Identifier: Apache-2.0 /** Measurements represent recordings from sensors at regular intervals, or diff --git a/src/idl/nanopb/payload.proto b/src/idl/nanopb/payload.proto index 9c78f107c6..6205696ca9 100644 --- a/src/idl/nanopb/payload.proto +++ b/src/idl/nanopb/payload.proto @@ -1,16 +1,5 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: 2024 Google LLC +// SPDX-License-Identifier: Apache-2.0 /** Payloads are how data should be sent and collected from devices. A simple diff --git a/src/idl/nanopb/simple.proto b/src/idl/nanopb/simple.proto index 632559ca37..3ba7fd90be 100644 --- a/src/idl/nanopb/simple.proto +++ b/src/idl/nanopb/simple.proto @@ -1,16 +1,5 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: 2024 Google LLC +// SPDX-License-Identifier: Apache-2.0 syntax = "proto2"; diff --git a/src/idl/nanopb/wscript b/src/idl/nanopb/wscript deleted file mode 100644 index 7db8094bfc..0000000000 --- a/src/idl/nanopb/wscript +++ /dev/null @@ -1,12 +0,0 @@ -def configure(conf): - conf.load('protoc') - - -def build(bld): - bld.stlib(features="c cstlib", - source=bld.path.ant_glob('**/*.proto'), - includes=bld.path.abspath(), - target='proto_schemas', - use='pblibc nanopb idl_includes') - -# vim:filetype=python diff --git a/src/idl/nanopb/wscript_build b/src/idl/nanopb/wscript_build new file mode 100644 index 0000000000..465cf6f6da --- /dev/null +++ b/src/idl/nanopb/wscript_build @@ -0,0 +1,7 @@ +bld.stlib(features="c cstlib", + source=bld.path.ant_glob('**/*.proto'), + includes=bld.path.abspath(), + target='proto_schemas', + use='pblibc nanopb idl_includes') + +# vim:filetype=python diff --git a/src/idl/wscript b/src/idl/wscript deleted file mode 100644 index 9ea2c3a719..0000000000 --- a/src/idl/wscript +++ /dev/null @@ -1,13 +0,0 @@ -def options(opt): - pass - -def configure(conf): - conf.recurse('nanopb') - - -def build(bld): - bld(export_includes=bld.path.get_bld().abspath(), name='idl_includes') - bld.recurse('nanopb') - - -# vim:filetype=python diff --git a/src/idl/wscript_build b/src/idl/wscript_build new file mode 100644 index 0000000000..9712f28300 --- /dev/null +++ b/src/idl/wscript_build @@ -0,0 +1,4 @@ +bld(export_includes=bld.path.get_bld().abspath(), name='idl_includes') +bld.recurse('nanopb') + +# vim:filetype=python diff --git a/src/include/bluetooth/analytics.h b/src/include/bluetooth/analytics.h deleted file mode 100644 index 57dbe654ed..0000000000 --- a/src/include/bluetooth/analytics.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "util/attributes.h" - -#include -#include - -#define NUM_LE_CHANNELS 37 - -typedef struct PACKED LEChannelMap { - uint8_t byte0; - uint8_t byte1; - uint8_t byte2; - uint8_t byte3; - uint8_t byte4; -} LEChannelMap; - -bool bt_driver_analytics_get_connection_quality(const BTDeviceInternal *address, - uint8_t *link_quality_out, int8_t *rssi_out); - -bool bt_driver_analytics_collect_ble_parameters(const BTDeviceInternal *addr, - LEChannelMap *le_chan_map_res); - -void bt_driver_analytics_external_collect_chip_specific_parameters(void); - -void bt_driver_analytics_external_collect_bt_chip_heartbeat(void); - -//! Returns true iff there are connection event stats to report -bool bt_driver_analytics_get_conn_event_stats(SlaveConnEventStats *stats); diff --git a/src/include/bluetooth/bas.h b/src/include/bluetooth/bas.h deleted file mode 100644 index 5dbaf76cf2..0000000000 --- a/src/include/bluetooth/bas.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -//! Sends the battery measurement to all subscribed & connected devices. -void bt_driver_bas_handle_update(uint8_t percent); diff --git a/src/include/bluetooth/bt_driver_advert.h b/src/include/bluetooth/bt_driver_advert.h deleted file mode 100644 index 2ec873488e..0000000000 --- a/src/include/bluetooth/bt_driver_advert.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -bool bt_driver_advert_advertising_enable(uint32_t min_interval_ms, uint32_t max_interval_ms); - -void bt_driver_advert_advertising_disable(void); - -bool bt_driver_advert_client_get_tx_power(int8_t *tx_power); - -void bt_driver_advert_set_advertising_data(const BLEAdData *ad_data); diff --git a/src/include/bluetooth/bt_driver_comm.h b/src/include/bluetooth/bt_driver_comm.h deleted file mode 100644 index 0ab78fd0d6..0000000000 --- a/src/include/bluetooth/bt_driver_comm.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef struct CommSession CommSession; - -//! Figures out the optimal thread to execute `bt_driver_run_send_next_job` on -//! and schedules a job to do so -bool bt_driver_comm_schedule_send_next_job(CommSession *session); - -//! @return The PebbleTask that is used with bt_driver_comm_schedule_send_next_job() to perform -//! the sending of pending data. -bool bt_driver_comm_is_current_task_send_next_task(void); - -extern void bt_driver_run_send_next_job(CommSession *session, bool is_callback); diff --git a/src/include/bluetooth/bt_test.h b/src/include/bluetooth/bt_test.h deleted file mode 100644 index f5ec905c72..0000000000 --- a/src/include/bluetooth/bt_test.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -void bt_driver_test_start(void); - -void bt_driver_test_enter_hci_passthrough(void); - -void bt_driver_test_handle_hci_passthrough_character(char c, bool *should_context_switch); - -bool bt_driver_test_enter_rf_test_mode(void); - -void bt_driver_test_set_spoof_address(const BTDeviceAddress *addr); - -void bt_driver_test_stop(void); - -bool bt_driver_test_selftest(void); - -bool bt_driver_test_mfi_chip_selftest(void); - -void bt_driver_le_transmitter_test( - uint8_t tx_channel, uint8_t tx_packet_length, uint8_t packet_payload); -void bt_driver_le_test_end(void); -void bt_driver_le_receiver_test(uint8_t rx_channel); - -typedef void (*BTDriverResponseCallback)(HciStatusCode status, const uint8_t *payload); -void bt_driver_register_response_callback(BTDriverResponseCallback callback); - -void bt_driver_start_unmodulated_tx(uint8_t tx_channel); -void bt_driver_stop_unmodulated_tx(void); - -typedef enum BtlePaConfig { - BtlePaConfig_Disable, - BtlePaConfig_Enable, - BtlePaConfig_Bypass, - BtlePaConfigCount -} BtlePaConfig; -void bt_driver_le_test_pa(BtlePaConfig option); - -typedef enum BtleCoreDump { - BtleCoreDump_UserRequest, - BtleCoreDump_ForceHardFault, - BtleCoreDump_Watchdog, - BtleCoreDumpCount -} BtleCoreDump; -void bt_driver_core_dump(BtleCoreDump type); - -void bt_driver_send_sleep_test_cmd(bool force_ble_sleep); diff --git a/src/include/bluetooth/classic_connect.h b/src/include/bluetooth/classic_connect.h deleted file mode 100644 index 4086a80134..0000000000 --- a/src/include/bluetooth/classic_connect.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include - -//! @param address Pass NULL to disconnect the "active" remote. -void bt_driver_classic_disconnect(const BTDeviceAddress* address); - -bool bt_driver_classic_is_connected(void); - -bool bt_driver_classic_copy_connected_address(BTDeviceAddress* address); - -bool bt_driver_classic_copy_connected_device_name(char name[BT_DEVICE_NAME_BUFFER_SIZE]); diff --git a/src/include/bluetooth/conn_event_stats.h b/src/include/bluetooth/conn_event_stats.h deleted file mode 100644 index 862a7c03e0..0000000000 --- a/src/include/bluetooth/conn_event_stats.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef struct SlaveConnEventStats { - uint32_t num_conn_events; // BLE Connection Events that have elapsed - uint32_t num_conn_events_skipped; // The number of events the controller never tried to listen for - uint32_t num_sync_errors; // Events where slave did not see a packet from Master - uint32_t num_type_errors; - uint32_t num_len_errors; - uint32_t num_crc_errors; // Events that ended due to a packet CRC error - uint32_t num_mic_errors; // Events that ended due to a packet MIC error -} SlaveConnEventStats; diff --git a/src/include/bluetooth/connectability.h b/src/include/bluetooth/connectability.h deleted file mode 100644 index 1ac632b670..0000000000 --- a/src/include/bluetooth/connectability.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void bt_driver_classic_update_connectability(void); diff --git a/src/include/bluetooth/dis.h b/src/include/bluetooth/dis.h deleted file mode 100644 index 89f0edffeb..0000000000 --- a/src/include/bluetooth/dis.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "util/attributes.h" - -// The reason the headers that define these lengths aren't included is because this header -// is included by the various number of bt_driver implementations. They don't know what "mfg" -// is, etc. -// NOTE: These sizes are asserted in a .c file to be in sync with the FW -#define MODEL_NUMBER_LEN (10) // MFG_HW_VERSION_SIZE + 1 -#define MANUFACTURER_LEN (18) // sizeof("Pebble Technology") -#define SERIAL_NUMBER_LEN (13) // MFG_SERIAL_NUMBER_SIZE + 1 -#define FW_REVISION_LEN (32) // FW_METADATA_VERSION_TAG_BYTES) -#define SW_REVISION_LEN (6) // Fmt: xx.xx\0 - -typedef struct PACKED DisInfo { - char model_number[MODEL_NUMBER_LEN]; - char manufacturer[MANUFACTURER_LEN]; - char serial_number[SERIAL_NUMBER_LEN]; - char fw_revision[FW_REVISION_LEN]; - char sw_revision[SW_REVISION_LEN]; -} DisInfo; diff --git a/src/include/bluetooth/features.h b/src/include/bluetooth/features.h deleted file mode 100644 index c1c1d3be48..0000000000 --- a/src/include/bluetooth/features.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -bool bt_driver_supports_bt_classic(void); diff --git a/src/include/bluetooth/gap_le_advert_workaround.h b/src/include/bluetooth/gap_le_advert_workaround.h deleted file mode 100644 index ae752b3fff..0000000000 --- a/src/include/bluetooth/gap_le_advert_workaround.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "comm/ble/gap_le_advert.h" - -//! @note The client should hold the `bt_lock` when calling these functions. -//! Also note that these functions are a workaround and should ideally not be used. -//! They are kept around to assist with a bug in the TI Bluetooth chips. - -extern GAPLEAdvertisingJobRef gap_le_advert_get_current_job(void); - -extern GAPLEAdvertisingJobRef gap_le_advert_get_jobs(void); - -extern GAPLEAdvertisingJobTag gap_le_advert_get_job_tag(GAPLEAdvertisingJobRef job); diff --git a/src/include/bluetooth/gap_le_device_name.h b/src/include/bluetooth/gap_le_device_name.h deleted file mode 100644 index 5be16287a5..0000000000 --- a/src/include/bluetooth/gap_le_device_name.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "comm/ble/gap_le_connection.h" - -//! Bluetooth LE GAP Device name APIs -void bt_driver_gap_le_device_name_request_all(void); -void bt_driver_gap_le_device_name_request(const BTDeviceInternal *address); - -//! The caller is expected to have implemented: -//! ctx will be kernel_free()'d -void bt_driver_store_device_name_kernelbg_cb(void *ctx); diff --git a/src/include/bluetooth/gap_le_scan.h b/src/include/bluetooth/gap_le_scan.h deleted file mode 100644 index b2061c0550..0000000000 --- a/src/include/bluetooth/gap_le_scan.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "comm/ble/gap_le_scan.h" - -//! Returns true on success, false on failure -bool bt_driver_start_le_scan(bool active_scan, bool use_white_list_filter, bool filter_dups, - uint16_t scan_interval_ms, uint16_t scan_window_ms); - -//! Returns true on success, false on failure -bool bt_driver_stop_le_scan(void); - -extern void bt_driver_cb_le_scan_handle_report(const GAPLERawAdReport *data, int length); diff --git a/src/include/bluetooth/gatt_discovery.h b/src/include/bluetooth/gatt_discovery.h deleted file mode 100644 index 6d46b8f0ac..0000000000 --- a/src/include/bluetooth/gatt_discovery.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -typedef struct GAPLEConnection GAPLEConnection; -typedef struct GATTService GATTService; -typedef struct GATTServiceNode GATTServiceNode; - -BTErrno bt_driver_gatt_start_discovery_range( - const GAPLEConnection *connection, const ATTHandleRange *data); -BTErrno bt_driver_gatt_stop_discovery(GAPLEConnection *connection); - -//! It's possible we are disconnected or the stack gets torn down while in the -//! middle of a discovery. This routine gets invoked if the connection gets -//! torn down or goes away so that the implementation can clean up any tracking -//! it has waiting for a discovery to complete -void bt_driver_gatt_handle_discovery_abandoned(void); - -//! gatt_service_discovery callbacks -//! cb returns true iff the driver completed, false if a discovery retry was initiated -extern bool bt_driver_cb_gatt_client_discovery_complete(GAPLEConnection *connection, BTErrno errno); -extern void bt_driver_cb_gatt_client_discovery_handle_indication( - GAPLEConnection *connection, GATTService *service_discovered, BTErrno error); diff --git a/src/include/bluetooth/hci_types.h b/src/include/bluetooth/hci_types.h deleted file mode 100644 index 244b84340c..0000000000 --- a/src/include/bluetooth/hci_types.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -typedef enum { - HciStatusCode_Success = 0x00, - HciStatusCode_UnknownConnectionIdentifier = 0x02, - HciStatusCode_VS_Base = 0x50, - HciStatusCode_Max = UINT16_MAX -} HciStatusCode; - -#ifndef __clang__ -_Static_assert(sizeof(HciStatusCode) == 2, "packed structs expect the status code to be 2 bytes!"); -#endif - -// disconnect reasons are just status codes -typedef HciStatusCode HciDisconnectReason; diff --git a/src/include/bluetooth/hrm_service.h b/src/include/bluetooth/hrm_service.h deleted file mode 100644 index 5e04d8d800..0000000000 --- a/src/include/bluetooth/hrm_service.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include - -typedef struct { - uint16_t bpm; - bool is_on_wrist; -} BleHrmServiceMeasurement; - -//! @return True if the BT driver lib supports exposing the GATT HRM service. -bool bt_driver_is_hrm_service_supported(void); - -//! Adds or removes the HRM service from the GATT database, notifying any connected devices -//! by sending a "Service Changed" indication for the mutated handle range. -void bt_driver_hrm_service_enable(bool enable); - -//! Sends the Heart Rate Measurement to all subscribed & connected devices. -void bt_driver_hrm_service_handle_measurement(const BleHrmServiceMeasurement *measurement, - const BTDeviceInternal *permitted_devices, - size_t num_permitted_devices); - -//! Called when a connected device (un)subscribes to the GATT HRM service's "Heart Rate Measurement" -//! characteristic. -extern void bt_driver_cb_hrm_service_update_subscription(const BTDeviceInternal *device, - bool is_subscribed); diff --git a/src/include/bluetooth/id.h b/src/include/bluetooth/id.h deleted file mode 100644 index 40a880d7dd..0000000000 --- a/src/include/bluetooth/id.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void bt_driver_id_set_local_device_name(const char device_name[BT_DEVICE_NAME_BUFFER_SIZE]); - -void bt_driver_id_copy_local_identity_address(BTDeviceAddress *addr_out); - -//! Configures the local address that the BT driver should use "on-air". -//! @note This address and the identity address are different things! -//! @note bt_lock() is held when this call is made. -//! @param allow_cycling True if the controller is allowed to cycle the address (implies address -//! pinning is *not* used!) -//! @param pinned_address The address to use, or NULL for "don't care". -void bt_driver_set_local_address(bool allow_cycling, - const BTDeviceAddress *pinned_address); - -//! Copies a human-readable string of freeform info that uniquely identifies the Bluetooth chip. -//! Used by MFG for part tracking purposes. -//! @param[out] dest Buffer into which to copy the info. -//! @param[in] dest_size Size of dest in bytes. -void bt_driver_id_copy_chip_info_string(char *dest, size_t dest_size); - -//! Generates a new private resolvable address using the current IRK (as passed with the -//! bt_driver_start() call when setting up the stack). -bool bt_driver_id_generate_private_resolvable_address(BTDeviceAddress *address_out); diff --git a/src/include/bluetooth/init.h b/src/include/bluetooth/init.h deleted file mode 100644 index f19279ca30..0000000000 --- a/src/include/bluetooth/init.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "util/attributes.h" - -#include -#include - -#include - -typedef struct PACKED BTDriverConfig { - SM128BitKey root_keys[SMRootKeyTypeNum]; - DisInfo dis_info; - BTDeviceAddress identity_addr; - bool is_hrm_supported_and_enabled; -} BTDriverConfig; - -//! Function that performs one-time initialization of the BT Driver. -//! The main FW is expected to call this once at boot. -void bt_driver_init(void); - -//! Starts the Bluetooth stack. -//! @return True if the stack started successfully. -bool bt_driver_start(BTDriverConfig *config); - -//! Stops the Bluetooth stack. -//! @return True if the stack stopped successfully. -void bt_driver_stop(void); - -//! Powers down the BT controller if has yet to be used -void bt_driver_power_down_controller_on_boot(void); diff --git a/src/include/bluetooth/mtu.h b/src/include/bluetooth/mtu.h deleted file mode 100644 index f7c752ab3e..0000000000 --- a/src/include/bluetooth/mtu.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// See NimBLE BLE_ATT_PREFERRED_MTU setting -#define ATT_MAX_SUPPORTED_MTU 256 \ No newline at end of file diff --git a/src/include/bluetooth/pairability.h b/src/include/bluetooth/pairability.h deleted file mode 100644 index 5d922ed81d..0000000000 --- a/src/include/bluetooth/pairability.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -void bt_driver_le_pairability_set_enabled(bool enabled); - -void bt_driver_classic_pairability_set_enabled(bool enabled); diff --git a/src/include/bluetooth/qemu_transport.h b/src/include/bluetooth/qemu_transport.h deleted file mode 100644 index 38b0acce76..0000000000 --- a/src/include/bluetooth/qemu_transport.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Called by the QEMU serial driver whenever Pebble Protocol data is received. -void qemu_transport_handle_received_data(const uint8_t *data, uint32_t length); - -//! Called by qemu version of comm_init() to tell ISPP that it is connected -void qemu_transport_set_connected(bool is_connected); - -bool qemu_transport_is_connected(void); diff --git a/src/include/bluetooth/reconnect.h b/src/include/bluetooth/reconnect.h deleted file mode 100644 index c306003302..0000000000 --- a/src/include/bluetooth/reconnect.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -//! Increments the reconnect-pause counter. When the counter is above -//! 0, reconnection attempts will not occur. -void bt_driver_reconnect_pause(void); - -//! Decrements the reconnect-pause counter. When the counter drops to -//! 0, reconnection attempts will be free to proceed. -void bt_driver_reconnect_resume(void); - -//! Attempt to reconnect to the last connected remote device and restore -//! connections to the Bluetooth Classic profile (iSPP). -//! This is an asynchonous operation. A call to this function returns quickly. -//! If the last connected remote device and services are already connected, or -//! if the device is not an iOS device, this function does not do much. -//! @param ignore_paused If true, this call will attempt to reconnect, -//! even if the reconnection manager is paused. If false, the call will not -//! attempt to reconnect if the manager is paused. -void bt_driver_reconnect_try_now(bool ignore_paused); - -//! Resets the reconnection manager's interval to the minimum interval, so -//! the rate of reconnection attempts is temporarily increased. This -//! should be called right after disconnecting or at any time that the remote -//! device might be suspected to be coming back in range. -void bt_driver_reconnect_reset_interval(void); - -//! Notifies the BT Driver of the platform bitfield we received from the -//! 'session remote version endpoint'. (Some drivers cache this information to -//! determine BT connection behavior (such as the reconnection algorithm for -//! bluetooth classic) -void bt_driver_reconnect_notify_platform_bitfield(uint32_t platform_bitfield); diff --git a/src/include/bluetooth/temp.h b/src/include/bluetooth/temp.h deleted file mode 100644 index 610c19ff3b..0000000000 --- a/src/include/bluetooth/temp.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -//! Should be called when a BT Classic disconnection occurs -void reconnect_android_update_disconnect_time(void); diff --git a/src/include/pebbleos/chip_id.h b/src/include/pebbleos/chip_id.h deleted file mode 100644 index 4048bcc502..0000000000 --- a/src/include/pebbleos/chip_id.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -/* - * chip_id.h - * - * This file specifies IDs for the different processors on our multi-processor devices. - * The IDs are used to differenetiate the source of system logs, core dumps, etc. - * - * The IDs must be unique within a platform and must fit in 2 bits. - * If we build a device with more than 4 log/core dump producing processors, this will need to be - * addressed. - */ - -#define CORE_ID_MAIN_MCU 0 -#define CORE_ID_BLE 1 diff --git a/src/include/pebbleos/core_dump_structs.h b/src/include/pebbleos/core_dump_structs.h deleted file mode 100644 index e548e6b1f5..0000000000 --- a/src/include/pebbleos/core_dump_structs.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -/* - * core_dump_structs.h - * - * This file specifies core_dump structures previously defined in fw/kernel/core_dump_private.h - * This is so the Dialog BLE core_dump code can use the same structures. - * - */ - -#include "portmacro.h" -#include "util/attributes.h" - -// Structure of thread info stored within a CORE_DUMP_CHUNK_KEY_THREAD chunk in the core dump -#define CORE_DUMP_THREAD_NAME_SIZE 16 -typedef struct PACKED { - int8_t name[CORE_DUMP_THREAD_NAME_SIZE]; // Name, includes null termination - uint32_t id; // thread id - uint8_t running; // true if this thread is running - uint32_t registers[portCANONICAL_REG_COUNT]; // registers [r0-r12, sp, lr, pc, xpsr] -} CoreDumpThreadInfo; - -// Structure of extra registers stored within a CORE_DUMP_CHUNK_KEY_EXTRA_REG chunk in the -// core dump -typedef struct PACKED { - uint32_t msp; - uint32_t psp; - uint8_t primask; - uint8_t basepri; - uint8_t faultmask; - uint8_t control; -} CoreDumpExtraRegInfo; - - - -// We save all the important registers on entry to core_dump_reset() in a structure of this type -// on the core_dump_reset() stack and save a pointer to it in the s_saved_registers global. -// IMPORTANT!: There is assembly code near the top of core_dump_reset() that makes assumptions -// about the order and packing of this structure. -typedef struct PACKED { - uint32_t core_reg[portCANONICAL_REG_COUNT]; - CoreDumpExtraRegInfo extra_reg; -} CoreDumpSavedRegisters; diff --git a/src/include/pebbleos/cron.h b/src/include/pebbleos/cron.h deleted file mode 100644 index f7aa4e0118..0000000000 --- a/src/include/pebbleos/cron.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "util/list.h" - -//! @file cron.h -//! Wall-clock based timer system. Designed for use in things such as alarms, calendar events, etc. -//! Properly handles DST, etc. - -typedef struct CronJob CronJob; - -typedef void (*CronJobCallback)(CronJob *job, void* data); - -//! Matches any possible value. -#define CRON_MINUTE_ANY (-1) -#define CRON_HOUR_ANY (-1) -#define CRON_MDAY_ANY (-1) -#define CRON_MONTH_ANY (-1) - -#define WDAY_SUNDAY (1 << 0) -#define WDAY_MONDAY (1 << 1) -#define WDAY_TUESDAY (1 << 2) -#define WDAY_WEDNESDAY (1 << 3) -#define WDAY_THURSDAY (1 << 4) -#define WDAY_FRIDAY (1 << 5) -#define WDAY_SATURDAY (1 << 6) - -#define WDAY_WEEKDAYS (WDAY_MONDAY | WDAY_TUESDAY | WDAY_WEDNESDAY | WDAY_THURSDAY | WDAY_FRIDAY) -#define WDAY_WEEKENDS (WDAY_SUNDAY | WDAY_SATURDAY) -#define WDAY_ANY (WDAY_WEEKENDS | WDAY_WEEKDAYS) - -struct CronJob { - //! internal, no touchy - ListNode list_node; - - //! Cached execution timestamp in UTC. - //! This is set by `cron_job_schedule`, and is required to never be changed once the job has been - //! added. - time_t cached_execute_time; - - //! Callback that is called when the job fires. - CronJobCallback cb; - void* cb_data; - - //! Occasionally, the system gets a clock change event for various reasons: - //! - User changed time-zones or a DST transition happened - //! - User changed the time - //! - Phone sent the current time and was different from ours, so we took theirs. - //! In the first case, the cron job's execute time will always be recalculated. - //! In the other two, we see if the time difference from the old time is >= this. - //! If it is, then we'll recalculate. Otherwise, we leave the calculated time alone. - //! In this way, 0 will always recalculate, and UINT32_MAX will never recalculate. - //! - //! Recalculating would essentially mean that a job that was "skipped over" will not fire until - //! the next match. If recalculation is not done, but the job was skipped over, it will fire - //! instantly. - //! - //! This value is specified in seconds. - uint32_t clock_change_tolerance; - - int8_t minute; //!< 0-59, or CRON_MINUTE_ANY - int8_t hour; //!< 0-23, or CRON_HOUR_ANY - int8_t mday; //!< 0-30, or CRON_MDAY_ANY - int8_t month; //!< 0-11, or CRON_MONTH_ANY - - //! Seconds to offset the cron execution time applied after regular cron job time calculation. - //! For example, a cron scheduled for Monday at 0:15 with an offset of negative 30min will fire - //! on Sunday at 23:45. - int32_t offset_seconds; - - union { - uint8_t flags; - - struct { - //! This should be any combination of WDAY_*. If zero, acts like WDAY_ANY. - uint8_t wday : 7; - - //! If this flag is set, the resulting execution time may be equal to the local epoch. - //! Having it set could be used for some event that must happen at the specified time even if - //! that time is right now. - bool may_be_instant : 1; - }; - }; -}; - -//! Add a cron job. This will make the service hold a reference to the specified job, so it must -//! not leave scope or be destroyed until it is unscheduled. -//! The job only gets scheduled once. For re-scheduling, you can call this on the job again. -//! @params job pointer to the CronJob struct to be scheduled. -//! @returns time_t for when the job is destined to go off. -time_t cron_job_schedule(CronJob *job); - -//! Schedule a cron job to run after another cron job. -//! This will make the service hold a reference to the new job, so it must -//! not leave scope or be destroyed until it is unscheduled. -//! @param job pointer to the CronJob after which we want our job to run. job must be scheduled. -//! @params new_job pointer to the CronJob struct to be scheduled. new_job must be unscheduled. -//! @returns time_t for when the job is destined to go off. -//! @note This API makes no guarantee that the two jobs will be scheduled back to back, -//! only that new_job will have the same scheduled time as job and that it will trigger -//! strictly after job. -time_t cron_job_schedule_after(CronJob *new_job, CronJob *job); - -//! Remove a scheduled cron job. -//! @params job pointer to the CronJob struct to be unscheduled. -//! @return true if the job was successfully removed (false may indicate no job was -//! scheduled at all or the cb is currently executing) -bool cron_job_unschedule(CronJob *job); - -//! Check if a cron job is scheduled. -//! @params job pointer to the CronJob struct to be checked for being scheduled. -//! @returns true if scheduled or pending deletion, false otherwise -bool cron_job_is_scheduled(CronJob *job); - -//! Calculate cron job's destined execution time, from the current time. -//! @params job pointer to the CronJob struct to get the execution time for. -//! @returns time_t for when the job is destined to go off. -time_t cron_job_get_execute_time(const CronJob *job); - -//! Calculate cron job's destined execution time if it were scheduled at the given time. -//! @params job pointer to the CronJob struct to get the execution time for. -//! @params local_epoch the epoch for getting the job's execution time. -//! @returns time_t for when the job is destined to go off. -time_t cron_job_get_execute_time_from_epoch(const CronJob *job, time_t local_epoch); diff --git a/src/include/pebbleos/firmware_metadata.h b/src/include/pebbleos/firmware_metadata.h deleted file mode 100644 index 921cdf0c5f..0000000000 --- a/src/include/pebbleos/firmware_metadata.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -/* - * firmware_metadata.h - * - * This file specifies the Firmware Metadata structure used in the .elf & .bin files to - * identify the build info, etc. - */ - -#include "util/attributes.h" - -#include -#include - - -#define FW_METADATA_CURRENT_STRUCT_VERSION 0x1 -#define FW_METADATA_VERSION_SHORT_BYTES 8 -#define FW_METADATA_VERSION_TAG_BYTES 32 - -// NOTE: When adding new platforms, if they use the legacy defective CRC, the list in -// tools/fw_binary_info.py needs to be updated with the platform value. -typedef enum FirmwareMetadataPlatform { - FirmwareMetadataPlatformUnknown = 0, - FirmwareMetadataPlatformPebbleOneEV1 = 1, - FirmwareMetadataPlatformPebbleOneEV2 = 2, - FirmwareMetadataPlatformPebbleOneEV2_3 = 3, - FirmwareMetadataPlatformPebbleOneEV2_4 = 4, - FirmwareMetadataPlatformPebbleOnePointFive = 5, - FirmwareMetadataPlatformPebbleTwoPointZero = 6, - FirmwareMetadataPlatformPebbleSnowyEVT2 = 7, - FirmwareMetadataPlatformPebbleSnowyDVT = 8, - FirmwareMetadataPlatformPebbleSpaldingEVT = 9, - FirmwareMetadataPlatformPebbleBobbyDVT = 10, - FirmwareMetadataPlatformPebbleSpalding = 11, - FirmwareMetadataPlatformPebbleSilkEVT = 12, - FirmwareMetadataPlatformPebbleRobertEVT = 13, - FirmwareMetadataPlatformPebbleSilk = 14, - FirmwareMetadataPlatformPebbleAsterix = 15, - FirmwareMetadataPlatformPebbleObelix = 16, - - FirmwareMetadataPlatformPebbleOneBigboard = 0xff, - FirmwareMetadataPlatformPebbleOneBigboard2 = 0xfe, - FirmwareMetadataPlatformPebbleSnowyBigboard = 0xfd, - FirmwareMetadataPlatformPebbleSnowyBigboard2 = 0xfc, - FirmwareMetadataPlatformPebbleSpaldingBigboard = 0xfb, - FirmwareMetadataPlatformPebbleSilkBigboard = 0xfa, - FirmwareMetadataPlatformPebbleRobertBigboard = 0xf9, - FirmwareMetadataPlatformPebbleSilkBigboard2 = 0xf8, - FirmwareMetadataPlatformPebbleRobertBigboard2 = 0xf7, -} FirmwareMetadataPlatform; - -// WARNING: changes in this struct must be reflected in: -// - iOS/PebblePrivateKit/PebblePrivateKit/PBBundle.m - -struct PACKED FirmwareMetadata { - uint32_t version_timestamp; - char version_tag[FW_METADATA_VERSION_TAG_BYTES]; - char version_short[FW_METADATA_VERSION_SHORT_BYTES]; - bool is_recovery_firmware:1; - bool is_ble_firmware:1; - bool is_dual_slot:1; - bool is_slot_0:1; - uint8_t reserved:4; - uint8_t hw_platform; - //! This should be the last field, since we put the meta data struct at the end of the fw binary. - uint8_t metadata_version; -}; -typedef struct FirmwareMetadata FirmwareMetadata; - -_Static_assert(sizeof(struct FirmwareMetadata) == (sizeof(uint32_t) + - FW_METADATA_VERSION_SHORT_BYTES + FW_METADATA_VERSION_TAG_BYTES + sizeof(uint8_t) + - sizeof(uint8_t) + sizeof(uint8_t)), - "FirmwareMetadata bitfields not packed correctly"); - - -// Shared defines. Let's not duplicate this everywhere. - -#ifdef RECOVERY_FW - #define FIRMWARE_METADATA_IS_RECOVERY_FIRMWARE (true) -#else - #define FIRMWARE_METADATA_IS_RECOVERY_FIRMWARE (false) -#endif - -#if BOARD_BIGBOARD - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleOneBigboard) -#elif BOARD_BB2 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleOneBigboard2) -#elif BOARD_SNOWY_BB - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSnowyBigboard) -#elif BOARD_SNOWY_BB2 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSnowyBigboard2) -#elif BOARD_SNOWY_EVT2 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSnowyEVT2) -#elif BOARD_SNOWY_DVT - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSnowyDVT) -#elif BOARD_SNOWY_S3 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleBobbyDVT) -#elif BOARD_V2_0 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleTwoPointZero) -#elif BOARD_V1_5 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleOnePointFive) -#elif BOARD_EV2_4 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleOneEV2_4) -#elif BOARD_SPALDING_BB2 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSpaldingBigboard) -#elif BOARD_SPALDING_EVT - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSpaldingEVT) -#elif BOARD_SPALDING - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSpalding) -#elif BOARD_SILK_EVT - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSilkEVT) -#elif BOARD_SILK_BB - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSilkBigboard) -#elif BOARD_SILK - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSilk) -#elif BOARD_SILK_BB2 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleSilkBigboard2) -#elif BOARD_ROBERT_BB - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleRobertBigboard) -#elif BOARD_ROBERT_BB2 - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleRobertBigboard2) -#elif BOARD_ROBERT_EVT - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleRobertEVT) -#elif BOARD_ASTERIX - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleAsterix) -#elif BOARD_OBELIX - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformPebbleObelix) -#else - #define FIRMWARE_METADATA_HW_PLATFORM (FirmwareMetadataPlatformUnknown) -#endif diff --git a/src/include/wscript b/src/include/wscript deleted file mode 100644 index 09f309bc44..0000000000 --- a/src/include/wscript +++ /dev/null @@ -1,6 +0,0 @@ -def build(bld): - bld(export_includes=['.'], - use=['libc_includes', 'libutil_includes'], - name='root_includes') - -# vim:filetype=python diff --git a/src/libbtutil/bt_device.c b/src/libbtutil/bt_device.c index ada76a616c..ef4d85d188 100644 --- a/src/libbtutil/bt_device.c +++ b/src/libbtutil/bt_device.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bt_device.h" #include diff --git a/src/libbtutil/bt_uuid.c b/src/libbtutil/bt_uuid.c index 95514b6e44..4395598437 100644 --- a/src/libbtutil/bt_uuid.c +++ b/src/libbtutil/bt_uuid.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bt_uuid.h" #include diff --git a/src/libbtutil/include/btutil/bt_device.h b/src/libbtutil/include/btutil/bt_device.h index 768bcc6734..41009a7813 100644 --- a/src/libbtutil/include/btutil/bt_device.h +++ b/src/libbtutil/include/btutil/bt_device.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libbtutil/include/btutil/bt_uuid.h b/src/libbtutil/include/btutil/bt_uuid.h index 0dadaa1de3..6134566b49 100644 --- a/src/libbtutil/include/btutil/bt_uuid.h +++ b/src/libbtutil/include/btutil/bt_uuid.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libbtutil/include/btutil/sm_util.h b/src/libbtutil/include/btutil/sm_util.h index 34f363f3b0..4ab3c0a2fa 100644 --- a/src/libbtutil/include/btutil/sm_util.h +++ b/src/libbtutil/include/btutil/sm_util.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/src/libbtutil/sm_util.c b/src/libbtutil/sm_util.c index c47a3ecb78..5d4362daf0 100644 --- a/src/libbtutil/sm_util.c +++ b/src/libbtutil/sm_util.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "sm_util.h" #include "bt_device.h" diff --git a/src/libbtutil/wscript b/src/libbtutil/wscript deleted file mode 100644 index f2a55c24fc..0000000000 --- a/src/libbtutil/wscript +++ /dev/null @@ -1,24 +0,0 @@ -import waftools - - -def build(bld): - sources = bld.path.ant_glob('**/*.c') - - def build_libutil(target, env): - # Build the libbtutil directory using firmware environment - bld.stlib(source=sources, - target=target, - includes=['.', 'include/btutil'], - use=['pblibc_includes', 'libutil_includes', 'root_includes'], - env=env.derive()) - - bld(export_includes=['include'], name='libbtutil_includes') - - if (bld.variant != 'test'): - build_libutil('libbtutil-cm0', bld.all_envs['cortex-m0']) - - build_libutil('libbtutil', bld.env) - build_libutil('libbtutil-32bit', bld.all_envs['32bit']) - - -# vim:filetype=python diff --git a/src/libbtutil/wscript_build b/src/libbtutil/wscript_build new file mode 100644 index 0000000000..d4ec8f8394 --- /dev/null +++ b/src/libbtutil/wscript_build @@ -0,0 +1,17 @@ +import waftools + +sources = bld.path.ant_glob('**/*.c') + +def build_libutil(target, env): + # Build the libbtutil directory using firmware environment + bld.stlib(source=sources, + target=target, + includes=['.', 'include/btutil'], + use=['pblibc_includes', 'libutil_includes', 'pbl_includes'], + env=env.derive()) + +bld(export_includes=['include'], name='libbtutil_includes') + +build_libutil('libbtutil', bld.env) + +# vim:filetype=python diff --git a/src/libc/alloca.c b/src/libc/alloca.c index c5c7ee1a6f..78b6a6cba7 100644 --- a/src/libc/alloca.c +++ b/src/libc/alloca.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/src/libc/ctype_ptr.c b/src/libc/ctype_ptr.c index f7dc7599bb..1d1809c63a 100644 --- a/src/libc/ctype_ptr.c +++ b/src/libc/ctype_ptr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: (sorta) diff --git a/src/libc/include/_ansi.h b/src/libc/include/_ansi.h index bc2964cd2c..9ccd124a6d 100644 --- a/src/libc/include/_ansi.h +++ b/src/libc/include/_ansi.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/alloca.h b/src/libc/include/alloca.h index 03e6968bad..17f297cbac 100644 --- a/src/libc/include/alloca.h +++ b/src/libc/include/alloca.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/ctype.h b/src/libc/include/ctype.h index 5e52974ef9..aa357c5377 100644 --- a/src/libc/include/ctype.h +++ b/src/libc/include/ctype.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/float.h b/src/libc/include/float.h index 4172b54431..0f126c300c 100644 --- a/src/libc/include/float.h +++ b/src/libc/include/float.h @@ -1,21 +1,36 @@ -/* - * Copyright 2025 Apache Software Foundation (ASF) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Apache Software Foundation (ASF) */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #ifndef FLT_EPSILON /* May be defined in toolchain header */ # define FLT_EPSILON 1.1920929e-07F /* 1E-5 */ +#endif + +#ifndef DBL_EPSILON /* May be defined in toolchain header */ +# define DBL_EPSILON 2.2204460492503131e-16 /* smallest double such that 1.0 + DBL_EPSILON != 1.0 */ +#endif + +#ifndef DBL_DIG /* May be defined in toolchain header */ +# define DBL_DIG 15 /* number of decimal digits of precision for double */ +#endif + +#ifndef DBL_MAX /* May be defined in toolchain header */ +# define DBL_MAX 1.7976931348623157e+308 /* max finite value of a double */ +#endif + +#ifndef DBL_MAX_10_EXP /* May be defined in toolchain header */ +# define DBL_MAX_10_EXP 308 /* max decimal exponent of a double */ +#endif + +#ifndef DBL_MAX_EXP /* May be defined in toolchain header */ +# define DBL_MAX_EXP 1024 /* max binary exponent of a double */ +#endif + +#ifndef FLT_RADIX /* May be defined in toolchain header */ +# define FLT_RADIX 2 /* radix of exponent representation */ +#endif + +#ifndef FLT_EVAL_METHOD /* May be defined in toolchain header */ +# define FLT_EVAL_METHOD 0 /* evaluate all operations just to the range and precision of the type */ #endif \ No newline at end of file diff --git a/src/libc/include/inttypes.h b/src/libc/include/inttypes.h index 18f2c01fc7..df4680eedb 100644 --- a/src/libc/include/inttypes.h +++ b/src/libc/include/inttypes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/locale.h b/src/libc/include/locale.h index 5fa569b840..aa887e575e 100644 --- a/src/libc/include/locale.h +++ b/src/libc/include/locale.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/math.h b/src/libc/include/math.h index d11a20e8fc..b87cfec965 100644 --- a/src/libc/include/math.h +++ b/src/libc/include/math.h @@ -1,19 +1,6 @@ -/* - * Copyright 2024 Google LLC - * Copyright 2025 Apache Software Foundation (ASF) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-FileCopyrightText: 2025 Apache Software Foundation (ASF) */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -22,36 +9,91 @@ #endif #define INFINITY ((double)(_HUGE_ENUF * _HUGE_ENUF)) +#define NAN ((double)(INFINITY * 0.0F)) #define INFINITY_F ((float)INFINITY) #define NAN_F ((float)(INFINITY * 0.0F)) #define M_E 2.7182818284590452353602874713526625 +#define M_LN2 0.69314718055994530941723212145817657 +#define M_LN10 2.3025850929940456840179914546843642 #define M_PI 3.1415926535897932384626433832795029 +#define M_PI_2 1.5707963267948966192313216916397514 typedef float float_t; typedef double double_t; +#define FP_NAN 0 +#define FP_INFINITE 1 +#define FP_ZERO 2 +#define FP_SUBNORMAL 3 +#define FP_NORMAL 4 + +#define fpclassify(x) __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, \ + FP_SUBNORMAL, FP_ZERO, (x)) + +#define isfinite(x) __builtin_isfinite(x) +#define isnormal(x) __builtin_isnormal(x) +#define isinf(x) (((x) == INFINITY) || ((x) == -INFINITY)) #define isinff(x) (((x) == INFINITY_F) || ((x) == -INFINITY_F)) +#define isnan(x) ((x) != (x)) #define isnanf(x) ((x) != (x)) +double asin(double x); + +double atan(double x); + +double ceil(double x); + float ceilf(float x); +double cos(double x); + +double copysign(double x, double y); + +double exp(double x); + float expf(float x); +double fabs(double x); + float fabsf(float x); +long double fabsl(long double x); + +double floor(double x); + float floorf(float x); float fmaxf(float x, float y); float fminf(float x, float y); +double fmod(double x, double div); + +double log(double x); + +double log2(double x); + float logf(float x); +float log10f(float x); + +double modf(double x, double *iptr); + float modff(float x, float *iptr); +double nearbyint(double x); + double round(double d); -float roundf(float x); \ No newline at end of file +float roundf(float x); + +double sin(double x); + +double sqrt(double x); + +float tanhf(float x); + +#define signbit(x) __builtin_signbit(x) \ No newline at end of file diff --git a/src/libc/include/setjmp.h b/src/libc/include/setjmp.h index ad7a6487cb..b87eceb1cd 100644 --- a/src/libc/include/setjmp.h +++ b/src/libc/include/setjmp.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Notes: @@ -37,7 +24,7 @@ struct __jmp_buf_struct { typedef struct __jmp_buf_struct jmp_buf[1]; int setjmp(jmp_buf env); -void longjmp(jmp_buf buf, int value); +void longjmp(jmp_buf buf, int value) __attribute__((noreturn)); #else // other implementations either use system setjmp or don't have it. # include_next diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h index 4ed474ac5f..671aae4f6f 100644 --- a/src/libc/include/stdio.h +++ b/src/libc/include/stdio.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -26,16 +13,22 @@ typedef struct { } FILE; +extern FILE *stderr; + // stdio.h isn't supposed to define va_list, so _need___va_list gives us __gnuc_va_list // Let's define that to something less compiler-specific #define __VA_LIST __gnuc_va_list +int fprintf(FILE *__restrict stream, const char *__restrict format, ...) + __attribute__((__format__(__printf__, 2, 3))); int printf(const char *__restrict format, ...) __attribute__((format (printf, 1, 2))); int sprintf(char * restrict str, const char * restrict format, ...) __attribute__((__format__(__printf__, 2, 3))); int snprintf(char * restrict str, size_t size, const char * restrict format, ...) __attribute__((__format__(__printf__, 3, 4))); +int vprintf(const char *__restrict format, __VA_LIST ap) + __attribute__((__format__(__printf__, 1, 0))); int vsprintf(char * restrict str, const char * restrict format, __VA_LIST ap) __attribute__((__format__(__printf__, 2, 0))); int vsnprintf(char * restrict str, size_t size, const char * restrict format, __VA_LIST ap) diff --git a/src/libc/include/stdlib.h b/src/libc/include/stdlib.h index 4eb89c90f7..779497d80f 100644 --- a/src/libc/include/stdlib.h +++ b/src/libc/include/stdlib.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/string.h b/src/libc/include/string.h index 0ca722c00b..970f46069e 100644 --- a/src/libc/include/string.h +++ b/src/libc/include/string.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -43,3 +30,5 @@ int atoi(const char *nptr) __attribute__((__pure__)); long int atol(const char *nptr) __attribute__((__pure__)); long int strtol(const char * restrict nptr, char ** restrict endptr, int base); unsigned long int strtoul(const char * restrict nptr, char ** restrict endptr, int base); + +char *itoa(int value, char *str, int base); diff --git a/src/libc/include/sys/cdefs.h b/src/libc/include/sys/cdefs.h index 4a21dd3033..436976e4b4 100644 --- a/src/libc/include/sys/cdefs.h +++ b/src/libc/include/sys/cdefs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/sys/stat.h b/src/libc/include/sys/stat.h index 34d604bc59..fa1749fa5e 100644 --- a/src/libc/include/sys/stat.h +++ b/src/libc/include/sys/stat.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/sys/types.h b/src/libc/include/sys/types.h index 87cdee4bd4..53cf796f4e 100644 --- a/src/libc/include/sys/types.h +++ b/src/libc/include/sys/types.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/unistd.h b/src/libc/include/unistd.h index 34d604bc59..fa1749fa5e 100644 --- a/src/libc/include/unistd.h +++ b/src/libc/include/unistd.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/wchar.h b/src/libc/include/wchar.h index 34d604bc59..fa1749fa5e 100644 --- a/src/libc/include/wchar.h +++ b/src/libc/include/wchar.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/include/wctype.h b/src/libc/include/wctype.h index 34d604bc59..fa1749fa5e 100644 --- a/src/libc/include/wctype.h +++ b/src/libc/include/wctype.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/math/floor.c b/src/libc/math/floor.c index ebdc427c39..4344363b1f 100644 --- a/src/libc/math/floor.c +++ b/src/libc/math/floor.c @@ -1,29 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: // double floor(double x); -#include - #include #include #include +#include + // TODO: PBL-36144 replace this naive implementation with __builtin_floor() #ifndef NAN diff --git a/src/libc/math/libc-nuttx/asin.c b/src/libc/math/libc-nuttx/asin.c new file mode 100644 index 0000000000..a76e4fed26 --- /dev/null +++ b/src/libc/math/libc-nuttx/asin.c @@ -0,0 +1,97 @@ +/**************************************************************************** + * libs/libm/libm/lib_asin.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2015-2016 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#undef DBL_EPSILON +#define DBL_EPSILON 1e-12 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* This lib uses Newton's method to approximate asin(x). Newton's Method + * converges very slowly for x close to 1. We can accelerate convergence + * with the following identy: asin(x)=Sign(x)*(Pi/2-asin(sqrt(1-x^2))) + */ + +static double asin_aux(double x) +{ + long double y; + double y_cos; + double y_sin; + + y = 0.0; + y_sin = 0.0; + + while (fabs(y_sin - x) > DBL_EPSILON) + { + y_cos = cos(y); + y -= ((long double)y_sin - (long double)x) / (long double)y_cos; + y_sin = sin(y); + } + + return y; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double asin(double x) +{ + double y; + + /* Verify that the input value is in the domain of the function */ + + if (x < -1.0 || x > 1.0 || isnan(x)) + { + return NAN; + } + + /* if x is > sqrt(2), use identity for faster convergence */ + + if (fabs(x) > 0.71) + { + y = M_PI_2 - asin_aux(sqrt(1.0 - x * x)); + y = copysign(y, x); + } + else + { + y = asin_aux(x); + } + + return y; +} diff --git a/src/libc/math/libc-nuttx/atan.c b/src/libc/math/libc-nuttx/atan.c new file mode 100644 index 0000000000..ed23221b7f --- /dev/null +++ b/src/libc/math/libc-nuttx/atan.c @@ -0,0 +1,42 @@ +/**************************************************************************** + * libs/libm/libm/lib_atan.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double atan(double x) +{ + return asin(x / sqrt(x * x + 1)); +} diff --git a/src/libc/math/libc-nuttx/ceil.c b/src/libc/math/libc-nuttx/ceil.c new file mode 100644 index 0000000000..cb3c512b92 --- /dev/null +++ b/src/libc/math/libc-nuttx/ceil.c @@ -0,0 +1,48 @@ +/**************************************************************************** + * libs/libm/libm/lib_ceil.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double ceil(double x) +{ + double x1 = x; + + modf(x, &x); + if (x1 > 0.0 && fabs(x1 - x) > 0.0) + { + x += 1.0; + } + + return x; +} diff --git a/src/libc/math/libc-nuttx/copysign.c b/src/libc/math/libc-nuttx/copysign.c new file mode 100644 index 0000000000..15dbf51359 --- /dev/null +++ b/src/libc/math/libc-nuttx/copysign.c @@ -0,0 +1,41 @@ +/**************************************************************************** + * libs/libm/libm/lib_copysign.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double copysign(double x, double y) +{ + if (signbit(y)) + { + return -fabs(x); + } + + return fabs(x); +} diff --git a/src/libc/math/libc-nuttx/cos.c b/src/libc/math/libc-nuttx/cos.c new file mode 100644 index 0000000000..5dee6c0b71 --- /dev/null +++ b/src/libc/math/libc-nuttx/cos.c @@ -0,0 +1,40 @@ +/**************************************************************************** + * libs/libm/libm/lib_cos.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double cos(double x) +{ + return sin(x + M_PI_2); +} diff --git a/src/libc/math/libc-nuttx/exp.c b/src/libc/math/libc-nuttx/exp.c new file mode 100644 index 0000000000..8a841e9b1d --- /dev/null +++ b/src/libc/math/libc-nuttx/exp.c @@ -0,0 +1,120 @@ +/**************************************************************************** + * libs/libm/libm/lib_exp.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +double lib_expi(size_t n); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static double _dbl_inv_fact[] = +{ + 1.0 / 1.0, /* 1 / 0! */ + 1.0 / 1.0, /* 1 / 1! */ + 1.0 / 2.0, /* 1 / 2! */ + 1.0 / 6.0, /* 1 / 3! */ + 1.0 / 24.0, /* 1 / 4! */ + 1.0 / 120.0, /* 1 / 5! */ + 1.0 / 720.0, /* 1 / 6! */ + 1.0 / 5040.0, /* 1 / 7! */ + 1.0 / 40320.0, /* 1 / 8! */ + 1.0 / 362880.0, /* 1 / 9! */ + 1.0 / 3628800.0, /* 1 / 10! */ + 1.0 / 39916800.0, /* 1 / 11! */ + 1.0 / 479001600.0, /* 1 / 12! */ + 1.0 / 6227020800.0, /* 1 / 13! */ + 1.0 / 87178291200.0, /* 1 / 14! */ + 1.0 / 1307674368000.0, /* 1 / 15! */ + 1.0 / 20922789888000.0, /* 1 / 16! */ + 1.0 / 355687428096000.0, /* 1 / 17! */ + 1.0 / 6402373705728000.0, /* 1 / 18! */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double exp(double x) +{ + size_t int_part; + bool invert; + double value; + double x0; + size_t i; + + if (x == 0) + { + return 1; + } + else if (x < 0) + { + invert = true; + x = -x; + } + else + { + invert = false; + } + + /* Extract integer component */ + + int_part = (size_t) x; + + /* Set x to fractional component */ + + x -= (double)int_part; + + /* Perform Taylor series approximation with nineteen terms */ + + value = 0.0; + x0 = 1.0; + for (i = 0; i < 19; i++) + { + value += x0 * _dbl_inv_fact[i]; + x0 *= x; + } + + /* Multiply by exp of the integer component */ + + value *= lib_expi(int_part); + + if (invert) + { + return (1.0 / value); + } + else + { + return value; + } +} diff --git a/src/libc/math/libc-nuttx/fabs.c b/src/libc/math/libc-nuttx/fabs.c new file mode 100644 index 0000000000..7da1745f37 --- /dev/null +++ b/src/libc/math/libc-nuttx/fabs.c @@ -0,0 +1,40 @@ +/**************************************************************************** + * libs/libm/libm/lib_fabs.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double fabs(double x) +{ + return ((x < 0) ? -x : x); +} diff --git a/src/libc/math/libc-nuttx/fabsl.c b/src/libc/math/libc-nuttx/fabsl.c new file mode 100644 index 0000000000..0c0e50dfbd --- /dev/null +++ b/src/libc/math/libc-nuttx/fabsl.c @@ -0,0 +1,40 @@ +/**************************************************************************** + * libs/libm/libm/lib_fabsl.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +long double fabsl(long double x) +{ + return ((x < 0) ? -x : x); +} diff --git a/src/libc/math/libc-nuttx/floor.c b/src/libc/math/libc-nuttx/floor.c new file mode 100644 index 0000000000..86aa3072a0 --- /dev/null +++ b/src/libc/math/libc-nuttx/floor.c @@ -0,0 +1,60 @@ +/**************************************************************************** + * libs/libm/libm/lib_floor.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double floor(double x) +{ + double modx; + + /* modf() will return the integer part of X. The return value of floor + * differs for non-integer, negative values. + * + * x modf floor + * ---- ----- ----- + * 2.0 2.0 2.0 + * 2.4 2.0 2.0 + * 2.9 2.0 2.0 + * -2.7 -2.0 -3.0 + * -2.0 -2.0 -2.0 + */ + + modf(x, &modx); + if (x < 0.0 && x < modx) + { + modx -= 1.0; + } + + return modx; +} diff --git a/src/libc/math/libc-nuttx/fmod.c b/src/libc/math/libc-nuttx/fmod.c new file mode 100644 index 0000000000..9f9f23cfdb --- /dev/null +++ b/src/libc/math/libc-nuttx/fmod.c @@ -0,0 +1,44 @@ +/**************************************************************************** + * libs/libm/libm/lib_fmod.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double fmod(double x, double div) +{ + double n0; + + modf(x / div, &n0); + + return x - n0 * div; +} diff --git a/src/libc/math/libc-nuttx/libexpi.c b/src/libc/math/libc-nuttx/libexpi.c new file mode 100644 index 0000000000..4613ce6135 --- /dev/null +++ b/src/libc/math/libc-nuttx/libexpi.c @@ -0,0 +1,98 @@ +/**************************************************************************** + * libs/libm/libm/lib_libexpi.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define M_E2 (M_E * M_E) +#define M_E4 (M_E2 * M_E2) +#define M_E8 (M_E4 * M_E4) +#define M_E16 (M_E8 * M_E8) +#define M_E32 (M_E16 * M_E16) +#define M_E64 (M_E32 * M_E32) +#define M_E128 (M_E64 * M_E64) +#define M_E256 (M_E128 * M_E128) +#define M_E512 (M_E256 * M_E256) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const double g_expi_square_tbl[] = +{ + M_E, /* e^1 */ + M_E2, /* e^2 */ + M_E4, /* e^4 */ + M_E8, /* e^8 */ + M_E16, /* e^16 */ + M_E32, /* e^32 */ + M_E64, /* e^64 */ + M_E128, /* e^128 */ + M_E256, /* e^256 */ + M_E512 /* e^512 */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double lib_expi(size_t n) +{ + size_t i; + double val; + + /* The largest calculable value for n is floor(ln(DBL_MAX)) */ + + if (n > 709) + { + return INFINITY; + } + + val = 1.0; + + for (i = 0; n != 0; i++) + { + if ((n & (1 << i)) != 0) + { + n &= ~(1 << i); + val *= g_expi_square_tbl[i]; + } + } + + return val; +} diff --git a/src/libc/math/libc-nuttx/log.c b/src/libc/math/libc-nuttx/log.c new file mode 100644 index 0000000000..a8d4484555 --- /dev/null +++ b/src/libc/math/libc-nuttx/log.c @@ -0,0 +1,109 @@ +/**************************************************************************** + * libs/libm/libm/lib_log.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DBL_MAX_EXP_X 700.0 + +/* To avoid looping forever in particular corner cases, every LOG_MAX_ITER + * the error criteria is relaxed by a factor LOG_RELAX_MULTIPLIER. + * todo: might need to adjust the double floating point version too. + */ + +#define LOG_MAX_ITER 400 +#define LOG_RELAX_MULTIPLIER 2.0 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: log + ****************************************************************************/ + + +double log(double x) +{ + double y; + double y_old; + double ey; + double epsilon; + double rf; /* epsilon relax factor */ + int iter; + + y = 0.0; + y_old = 1.0; + epsilon = DBL_EPSILON; + + iter = 0; + rf = 1.0; + + while (y > y_old + epsilon || y < y_old - epsilon) + { + y_old = y; + ey = exp(y); + y -= (ey - x) / ey; + + if (y > DBL_MAX_EXP_X) + { + y = DBL_MAX_EXP_X; + } + + if (y < -DBL_MAX_EXP_X) + { + y = -DBL_MAX_EXP_X; + } + + epsilon = ((fabs(y) > rf) ? fabs(y) : rf) * DBL_EPSILON; + + if (++iter >= LOG_MAX_ITER) + { + rf *= LOG_RELAX_MULTIPLIER; + iter = 0; + } + } + + if (y == DBL_MAX_EXP_X) + { + return INFINITY; + } + + if (y == -DBL_MAX_EXP_X) + { + return INFINITY; + } + + return y; +} diff --git a/src/libc/math/libc-nuttx/log10f.c b/src/libc/math/libc-nuttx/log10f.c new file mode 100644 index 0000000000..c799e3840b --- /dev/null +++ b/src/libc/math/libc-nuttx/log10f.c @@ -0,0 +1,40 @@ +/**************************************************************************** + * libs/libm/libm/lib_log10f.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +float log10f(float x) +{ + return (logf(x) / (float)M_LN10); +} diff --git a/src/libc/math/libc-nuttx/log2.c b/src/libc/math/libc-nuttx/log2.c new file mode 100644 index 0000000000..c128522762 --- /dev/null +++ b/src/libc/math/libc-nuttx/log2.c @@ -0,0 +1,40 @@ +/**************************************************************************** + * libs/libm/libm/lib_log2.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double log2(double x) +{ + return (log(x) / M_LN2); +} diff --git a/src/libc/math/libc-nuttx/modf.c b/src/libc/math/libc-nuttx/modf.c new file mode 100644 index 0000000000..8ef61e894a --- /dev/null +++ b/src/libc/math/libc-nuttx/modf.c @@ -0,0 +1,55 @@ +/**************************************************************************** + * libs/libm/libm/lib_modf.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double modf(double x, double *iptr) +{ + if (fabs(x) >= 4503599627370496.0) + { + *iptr = x; + return 0.0; + } + else if (fabs(x) < 1.0) + { + *iptr = 0.0; + return x; + } + else + { + *iptr = (double)(int64_t)x; + return (x - *iptr); + } +} diff --git a/src/libc/math/libc-nuttx/sin.c b/src/libc/math/libc-nuttx/sin.c new file mode 100644 index 0000000000..53fdde9e64 --- /dev/null +++ b/src/libc/math/libc-nuttx/sin.c @@ -0,0 +1,107 @@ +/**************************************************************************** + * libs/libm/libm/lib_sin.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static double _dbl_inv_fact[] = +{ + 1.0 / 1.0, /* 1 / 1! */ + 1.0 / 6.0, /* 1 / 3! */ + 1.0 / 120.0, /* 1 / 5! */ + 1.0 / 5040.0, /* 1 / 7! */ + 1.0 / 362880.0, /* 1 / 9! */ + 1.0 / 39916800.0, /* 1 / 11! */ + 1.0 / 6227020800.0, /* 1 / 13! */ + 1.0 / 1307674368000.0, /* 1 / 15! */ + 1.0 / 355687428096000.0, /* 1 / 17! */ + 1.0 / 121645100408832000.0, /* 1 / 19! */ +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double sin(double x) +{ + double x_squared; + double sin_x; + size_t i; + + /* Move x to [-pi, pi) */ + + x = fmod(x, 2 * M_PI); + if (x >= M_PI) + { + x -= 2 * M_PI; + } + + if (x < -M_PI) + { + x += 2 * M_PI; + } + + /* Move x to [-pi/2, pi/2) */ + + if (x >= M_PI_2) + { + x = M_PI - x; + } + + if (x < -M_PI_2) + { + x = -M_PI - x; + } + + x_squared = x * x; + sin_x = 0.0; + + /* Perform Taylor series approximation for sin(x) with ten terms */ + + for (i = 0; i < 10; i++) + { + if (i % 2 == 0) + { + sin_x += x * _dbl_inv_fact[i]; + } + else + { + sin_x -= x * _dbl_inv_fact[i]; + } + + x *= x_squared; + } + + return sin_x; +} diff --git a/src/libc/math/libc-nuttx/sqrt.c b/src/libc/math/libc-nuttx/sqrt.c new file mode 100644 index 0000000000..b40584fd46 --- /dev/null +++ b/src/libc/math/libc-nuttx/sqrt.c @@ -0,0 +1,92 @@ +/**************************************************************************** + * libs/libm/libm/lib_sqrt.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009-2011 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +float lib_sqrtapprox(float x); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +double sqrt(double x) +{ + long double y; + long double y1; + + if (x < 0.0) + { + return NAN; + } + + if (isnan(x)) + { + return NAN; + } + + if (isinf(x)) + { + return INFINITY; + } + + if (x == 0.0) + { + return 0.0; + } + + /* Guess square root (using bit manipulation) */ + + y = lib_sqrtapprox(x); + + /* Perform four iterations of approximation. This number (4) is + * definitely optimal + */ + + y = 0.5 * (y + x / y); + y = 0.5 * (y + x / y); + y = 0.5 * (y + x / y); + y = 0.5 * (y + x / y); + + /* If guess was terribe (out of range of float). Repeat approximation + * until convergence. + */ + + if (y * y < x - 1.0 || y * y > x + 1.0) + { + y1 = -1.0; + while (y != y1) + { + y1 = y; + y = 0.5 * (y + x / y); + } + } + + return y; +} diff --git a/src/libc/math/libc-nuttx/tanhf.c b/src/libc/math/libc-nuttx/tanhf.c new file mode 100644 index 0000000000..2c8585055c --- /dev/null +++ b/src/libc/math/libc-nuttx/tanhf.c @@ -0,0 +1,43 @@ +/**************************************************************************** + * libs/libm/libm/lib_tanhf.c + * + * SPDX-License-Identifier: ISC + * SPDX-FileCopyrightText: Copyright (C) 2012 Gregory Nutt. + * SPDX-FileContributor: Ported by: Darcy Gong + * + * It derives from the Rhombus OS math library by Nick Johnson which has + * a compatible, MIT-style license: + * + * Copyright (C) 2009, 2010 Nick Johnson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +float tanhf(float x) +{ + float x0 = expf(x); + float x1 = 1.0F / x0; + + return ((x0 - x1) / (x0 + x1)); +} diff --git a/src/libc/math/round.c b/src/libc/math/round.c index 38100a22e4..9ba31d23b7 100644 --- a/src/libc/math/round.c +++ b/src/libc/math/round.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // Naive implementation, probably room for optimization... double round(double d) { diff --git a/src/libc/pblibc_assembly.h b/src/libc/pblibc_assembly.h index b7bc4a9870..99b4a4d662 100644 --- a/src/libc/pblibc_assembly.h +++ b/src/libc/pblibc_assembly.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/pblibc_private.h b/src/libc/pblibc_private.h index 3847527082..d5b77dd116 100644 --- a/src/libc/pblibc_private.h +++ b/src/libc/pblibc_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libc/rand48.c b/src/libc/rand48.c index e1cbe69a40..96ccf89ae7 100644 --- a/src/libc/rand48.c +++ b/src/libc/rand48.c @@ -1,19 +1,6 @@ -/* - * Copyright 2025 Core Devices LLC - * Copyright 2025 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-FileCopyrightText: 2025 The Apache Software Foundation */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/src/libc/setjmp.c b/src/libc/setjmp.c index d13d8b5882..14b7b1329a 100644 --- a/src/libc/setjmp.c +++ b/src/libc/setjmp.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/atoi.c b/src/libc/string/atoi.c index 34ad1881de..cee49ffb8b 100644 --- a/src/libc/string/atoi.c +++ b/src/libc/string/atoi.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/itoa.c b/src/libc/string/itoa.c new file mode 100644 index 0000000000..2ce6c3a028 --- /dev/null +++ b/src/libc/string/itoa.c @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +static void reverse(char *str, int length) { + int start = 0; + int end = length - 1; + while (start < end) { + char temp = str[start]; + str[start] = str[end]; + str[end] = temp; + start++; + end--; + } +} + +char *itoa(int value, char *str, int base) { + int i = 0; + int is_negative = 0; + + // Handle base validation + if (base < 2 || base > 36) { + str[0] = '\0'; + return str; + } + + // Handle 0 explicitly + if (value == 0) { + str[i++] = '0'; + str[i] = '\0'; + return str; + } + + // Handle negative numbers for base 10 + if (value < 0 && base == 10) { + is_negative = 1; + value = -value; + } + + // Process individual digits + while (value != 0) { + int rem = value % base; + str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; + value = value / base; + } + + // Append negative sign for negative numbers + if (is_negative) { + str[i++] = '-'; + } + + str[i] = '\0'; + + // Reverse the string + reverse(str, i); + + return str; +} diff --git a/src/libc/string/memchr.c b/src/libc/string/memchr.c index af21991c6b..911b141788 100644 --- a/src/libc/string/memchr.c +++ b/src/libc/string/memchr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/memcmp.c b/src/libc/string/memcmp.c index 338fc3e542..5f3f1e51fd 100644 --- a/src/libc/string/memcmp.c +++ b/src/libc/string/memcmp.c @@ -1,29 +1,37 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: // int memcmp(const void *s1, const void *s2, size_t n); #include +#include #include int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *p1 = (const unsigned char*)s1; const unsigned char *p2 = (const unsigned char*)s2; + + // Fast path: word-by-word comparison if both pointers are word-aligned + if ((((uintptr_t)p1 | (uintptr_t)p2) & (sizeof(uint32_t) - 1)) == 0) { + const uint32_t *w1 = (const uint32_t*)p1; + const uint32_t *w2 = (const uint32_t*)p2; + + while (n >= sizeof(uint32_t)) { + if (*w1 != *w2) { + break; + } + w1++; + w2++; + n -= sizeof(uint32_t); + } + + p1 = (const unsigned char*)w1; + p2 = (const unsigned char*)w2; + } + + // Byte-by-byte comparison for remaining/unaligned bytes while (n--) { int diff = *p1 - *p2; if (diff) { diff --git a/src/libc/string/memcpy-armv7m.S b/src/libc/string/memcpy-armv7m.S index 6d7fe6e144..57ad59c853 100644 --- a/src/libc/string/memcpy-armv7m.S +++ b/src/libc/string/memcpy-armv7m.S @@ -1,30 +1,5 @@ -/* - * Copyright (c) 2013 ARM Ltd - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the company may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +/* SPDX-FileCopyrightText: 2013 ARM Ltd */ +/* SPDX-License-Identifier: BSD-3-Clause */ /* This memcpy routine is optimised for Cortex-M3/M4 cores with/without unaligned access. If compiled with GCC, this file should be enclosed within following diff --git a/src/libc/string/memcpy.c b/src/libc/string/memcpy.c index cc3ecc1baa..370a847e5d 100644 --- a/src/libc/string/memcpy.c +++ b/src/libc/string/memcpy.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/memset-thumb2.S b/src/libc/string/memset-thumb2.S index 9735dd9e13..7dba0ceae4 100644 --- a/src/libc/string/memset-thumb2.S +++ b/src/libc/string/memset-thumb2.S @@ -1,30 +1,5 @@ -/* - * Copyright (c) 2015 ARM Ltd - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the company may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +/* SPDX-FileCopyrightText: 2015 ARM Ltd */ +/* SPDX-License-Identifier: BSD-3-Clause */ /* modified 2016-06-22: * converted aeabi_memset to normal memset (save r0, different argument order) */ diff --git a/src/libc/string/memset.c b/src/libc/string/memset.c index a1717c4f16..1f6b7a063a 100644 --- a/src/libc/string/memset.c +++ b/src/libc/string/memset.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strcat.c b/src/libc/string/strcat.c index ff12cc2187..bccb594595 100644 --- a/src/libc/string/strcat.c +++ b/src/libc/string/strcat.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strchr.c b/src/libc/string/strchr.c index fec9c99850..8ab3419530 100644 --- a/src/libc/string/strchr.c +++ b/src/libc/string/strchr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strcmp.c b/src/libc/string/strcmp.c index b15cd181d3..4150ef9eb4 100644 --- a/src/libc/string/strcmp.c +++ b/src/libc/string/strcmp.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strcpy.c b/src/libc/string/strcpy.c index 3aae3581fd..ec7608ae61 100644 --- a/src/libc/string/strcpy.c +++ b/src/libc/string/strcpy.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strlen.c b/src/libc/string/strlen.c index 98d4f6bf98..02163f4454 100644 --- a/src/libc/string/strlen.c +++ b/src/libc/string/strlen.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strspn.c b/src/libc/string/strspn.c index 6f8f573c5d..f415bfa4a9 100644 --- a/src/libc/string/strspn.c +++ b/src/libc/string/strspn.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strstr.c b/src/libc/string/strstr.c index 61f320d35f..ebce79d013 100644 --- a/src/libc/string/strstr.c +++ b/src/libc/string/strstr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/string/strtol.c b/src/libc/string/strtol.c index 7725288158..1b2fb715b9 100644 --- a/src/libc/string/strtol.c +++ b/src/libc/string/strtol.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/stub/setlocale.c b/src/libc/stub/setlocale.c index e0e9dd72f4..9f41801604 100644 --- a/src/libc/stub/setlocale.c +++ b/src/libc/stub/setlocale.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ /////////////////////////////////////// // Implements: diff --git a/src/libc/wscript b/src/libc/wscript deleted file mode 100644 index 6b832cec85..0000000000 --- a/src/libc/wscript +++ /dev/null @@ -1,24 +0,0 @@ -import waftools.gitinfo - - -def build(bld): - # Collect all the files in the libc directory - source_dirs = ['string', 'math', 'stub'] - sources = sum([bld.path.ant_glob('%s/**/*.[csS]' % d) for d in source_dirs], []) - sources.extend(bld.path.ant_glob('*.[csS]')) - - def build_libc(target, env): - # Build the libc directory using firmware environment - bld.stlib(source=sources, - target=target, - includes=['include', '.'], - export_includes='include', - cflags=['-fno-lto', '-fno-builtin'], - use='', - env=env.derive()) - - bld(export_includes=['include'], name='pblibc_includes') - build_libc('pblibc', bld.env) - build_libc('pblibc-cm0', bld.all_envs['cortex-m0']) - -# vim:filetype=python diff --git a/src/libc/wscript_build b/src/libc/wscript_build new file mode 100644 index 0000000000..0ec5451efc --- /dev/null +++ b/src/libc/wscript_build @@ -0,0 +1,21 @@ +import waftools.gitinfo + +# Collect all the files in the libc directory +source_dirs = ['string', 'math', 'stub'] +sources = sum([bld.path.ant_glob('%s/**/*.[csS]' % d) for d in source_dirs], []) +sources.extend(bld.path.ant_glob('*.[csS]')) + +def build_libc(target, env): + # Build the libc directory using firmware environment + bld.stlib(source=sources, + target=target, + includes=['include', '.'], + export_includes='include', + cflags=['-fno-lto', '-fno-builtin'], + use='', + env=env.derive()) + +bld(export_includes=['include'], name='pblibc_includes') +build_libc('pblibc', bld.env) + +# vim:filetype=python diff --git a/src/libos/README.md b/src/libos/README.md index 0744f5c486..693b4c03d7 100644 --- a/src/libos/README.md +++ b/src/libos/README.md @@ -8,6 +8,4 @@ It is used by and built for the main FW and the Dialog Bluetooth FW. - libc - libutil - FreeRTOS -- Availability of an header file that includes the CMSIS headers (core_cmX.h, - core_cmFunc.h, etc.) - A handful of platform specific functions, see platform.c diff --git a/src/libos/include/mcu/cache.h b/src/libos/include/mcu/cache.h index adc42e615c..946180240f 100644 --- a/src/libos/include/mcu/cache.h +++ b/src/libos/include/mcu/cache.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/include/mcu/fpu.h b/src/libos/include/mcu/fpu.h index 82e5eba216..0e478d6d72 100644 --- a/src/libos/include/mcu/fpu.h +++ b/src/libos/include/mcu/fpu.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/include/mcu/interrupts.h b/src/libos/include/mcu/interrupts.h index e227b4af9d..343b529b15 100644 --- a/src/libos/include/mcu/interrupts.h +++ b/src/libos/include/mcu/interrupts.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/include/mcu/interrupts_arm.inl.h b/src/libos/include/mcu/interrupts_arm.inl.h index 47b529de19..f4527e3a12 100644 --- a/src/libos/include/mcu/interrupts_arm.inl.h +++ b/src/libos/include/mcu/interrupts_arm.inl.h @@ -1,21 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#define CMSIS_COMPATIBLE -#include +#include static inline bool mcu_state_is_isr(void) { return __get_IPSR() != 0; diff --git a/src/libos/include/mcu/interrupts_stubs.inl.h b/src/libos/include/mcu/interrupts_stubs.inl.h index c5de5d696a..bae0f2caf7 100644 --- a/src/libos/include/mcu/interrupts_stubs.inl.h +++ b/src/libos/include/mcu/interrupts_stubs.inl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ static inline bool mcu_state_is_isr(void) { return false; diff --git a/src/libos/include/mcu/privilege.h b/src/libos/include/mcu/privilege.h index b3807fb167..134cc7d803 100644 --- a/src/libos/include/mcu/privilege.h +++ b/src/libos/include/mcu/privilege.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -26,6 +13,19 @@ void mcu_state_set_thread_privilege(bool privilege); bool mcu_state_is_privileged(void); +//! Bracket a call to @p fn so its body runs in unprivileged thread mode while +//! the caller stays privileged. +//! +//! Must be called from privileged thread mode (asserts and behaves +//! unpredictably otherwise). The implementation lives in the .syscall_text +//! section so it is allowed to re-elevate via svc 2 once @p fn returns; it +//! also snapshots and restores the per-task TLS LR slot the inner SVC clobbers. +//! +//! Use this to invoke untrusted callbacks (e.g. JavaScript FFI dispatch) so +//! that any kernel-memory or peripheral access inside @p fn faults the MPU +//! instead of silently succeeding under the runtime's privileged context. +void mcu_call_unprivileged(void (*fn)(void *), void *ctx); + #ifdef __arm__ #include "mcu/privilege_arm.inl.h" #else diff --git a/src/libos/include/mcu/privilege_arm.inl.h b/src/libos/include/mcu/privilege_arm.inl.h index 22a7f1f1f2..0ee2bc7bc5 100644 --- a/src/libos/include/mcu/privilege_arm.inl.h +++ b/src/libos/include/mcu/privilege_arm.inl.h @@ -1,21 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#define CMSIS_COMPATIBLE -#include +#include //! @file privilege_arm.inl.h diff --git a/src/libos/include/mcu/privilege_stubs.inl.h b/src/libos/include/mcu/privilege_stubs.inl.h index 828d8e5fb4..15d65ec1af 100644 --- a/src/libos/include/mcu/privilege_stubs.inl.h +++ b/src/libos/include/mcu/privilege_stubs.inl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/attributes.h" diff --git a/src/libos/include/os/assert.h b/src/libos/include/os/assert.h index 45925e4cad..937b31218f 100644 --- a/src/libos/include/os/assert.h +++ b/src/libos/include/os/assert.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/include/os/malloc.h b/src/libos/include/os/malloc.h index 5f6256518c..49f7bbd5ae 100644 --- a/src/libos/include/os/malloc.h +++ b/src/libos/include/os/malloc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/include/os/mutex.h b/src/libos/include/os/mutex.h index e66a59ddd2..03128cd0ac 100644 --- a/src/libos/include/os/mutex.h +++ b/src/libos/include/os/mutex.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/include/os/tick.h b/src/libos/include/os/tick.h index 7cd933f453..776c000d3f 100644 --- a/src/libos/include/os/tick.h +++ b/src/libos/include/os/tick.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libos/mcu/cache_arm.c b/src/libos/mcu/cache_arm.c index 48ef5d8e70..67e8092f54 100644 --- a/src/libos/mcu/cache_arm.c +++ b/src/libos/mcu/cache_arm.c @@ -1,24 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mcu/cache.h" #include "util/attributes.h" -#define CMSIS_COMPATIBLE -#include +#include // I-Cache definition doesn't always exist #ifndef __ICACHE_PRESENT diff --git a/src/libos/mcu/fpu_arm.c b/src/libos/mcu/fpu_arm.c index dc31fe81c2..eda34244a9 100644 --- a/src/libos/mcu/fpu_arm.c +++ b/src/libos/mcu/fpu_arm.c @@ -1,23 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mcu/fpu.h" -#define CMSIS_COMPATIBLE -#include +#include #include diff --git a/src/libos/mcu/interrupts_arm.c b/src/libos/mcu/interrupts_arm.c index 1f8519a3f9..46fa33c1a3 100644 --- a/src/libos/mcu/interrupts_arm.c +++ b/src/libos/mcu/interrupts_arm.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mcu/interrupts.h" diff --git a/src/libos/mcu/privilege_arm.c b/src/libos/mcu/privilege_arm.c index 8c4cf5591e..a83f3b0fd5 100644 --- a/src/libos/mcu/privilege_arm.c +++ b/src/libos/mcu/privilege_arm.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mcu/privilege.h" diff --git a/src/libos/mutex_freertos.c b/src/libos/mutex_freertos.c index e75f342002..566a6d84bb 100644 --- a/src/libos/mutex_freertos.c +++ b/src/libos/mutex_freertos.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mcu/interrupts.h" #include "os/assert.h" diff --git a/src/libos/platform.c b/src/libos/platform.c index a683955f6c..1f4b0a5854 100644 --- a/src/libos/platform.c +++ b/src/libos/platform.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "os/malloc.h" #include "os/assert.h" diff --git a/src/libos/tick.c b/src/libos/tick.c index 2644597807..f7c54da63e 100644 --- a/src/libos/tick.c +++ b/src/libos/tick.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "FreeRTOS.h" #include "portmacro.h" diff --git a/src/libos/wscript b/src/libos/wscript deleted file mode 100644 index ead623d008..0000000000 --- a/src/libos/wscript +++ /dev/null @@ -1,25 +0,0 @@ -import waftools - - -def build(bld): - sources = bld.path.ant_glob('**/*.c') - - def build_lib(target, env, platform_uses): - # Build the directory using firmware environment - bld.stlib(source=sources, - target=target, - includes=['.', 'include'], - use=['pblibc_includes', - 'libutil_includes'] + platform_uses, - env=env.derive()) - - bld(export_includes=['include'], name='libos_includes') - - if bld.env in (bld.all_envs['local'], bld.all_envs['32bit']): - # Skip building sources for local builds, like unit tests. - return - - build_lib('libos', bld.env, ['fw_includes', 'freertos']) - - -# vim:filetype=python diff --git a/src/libos/wscript_build b/src/libos/wscript_build new file mode 100644 index 0000000000..21eb4c202e --- /dev/null +++ b/src/libos/wscript_build @@ -0,0 +1,22 @@ +import waftools + +sources = bld.path.ant_glob('**/*.c') + + +def build_lib(target, env, platform_uses): + # Build the directory using firmware environment + bld.stlib(source=sources, + target=target, + includes=['.', 'include'], + use=['pblibc_includes', + 'libutil_includes'] + platform_uses, + env=env.derive()) + + +bld(export_includes=['include'], name='libos_includes') + +# Skip building sources for local builds, like unit tests. +if bld.env not in (bld.all_envs['local'],): + build_lib('libos', bld.env, ['fw_includes', 'freertos']) + +# vim:filetype=python diff --git a/src/libutil/build_id.c b/src/libutil/build_id.c index c66134b19d..5b263464c4 100644 --- a/src/libutil/build_id.c +++ b/src/libutil/build_id.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/build_id.h" diff --git a/src/libutil/circular_buffer.c b/src/libutil/circular_buffer.c index f56ed7ed16..a25ea243dc 100644 --- a/src/libutil/circular_buffer.c +++ b/src/libutil/circular_buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/circular_buffer.h" diff --git a/src/libutil/circular_cache.c b/src/libutil/circular_cache.c index a0ccd90450..564dc78dce 100644 --- a/src/libutil/circular_cache.c +++ b/src/libutil/circular_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/circular_cache.h" diff --git a/src/libutil/crc32.c b/src/libutil/crc32.c index 987b8ad7fc..0e72106916 100644 --- a/src/libutil/crc32.c +++ b/src/libutil/crc32.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/crc32.h" diff --git a/src/libutil/hash.c b/src/libutil/hash.c index 0bc862e740..e9211252fa 100644 --- a/src/libutil/hash.c +++ b/src/libutil/hash.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/hash.h" diff --git a/src/libutil/heap.c b/src/libutil/heap.c index 73c7adff59..69ad624fc1 100644 --- a/src/libutil/heap.c +++ b/src/libutil/heap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/heap.h" @@ -71,7 +58,7 @@ typedef struct _tagHeapInfo_t //! Size of this segment, measured in units of ALIGNMENT_SIZE, including this size of this beginer uint16_t Size:15; -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION uintptr_t pc; //pc = client_pc; #endif @@ -311,7 +298,7 @@ void heap_free(Heap* const heap, void *ptr, uintptr_t client_pc) { /* This will make calculations in this block easier. */ heap_info_ptr->is_allocated = false; -#ifndef RELEASE +#ifndef CONFIG_RELEASE if (heap->fuzz_on_free) { memset(ptr, 0xBD, (heap_info_ptr->Size - HEAP_INFO_BLOCK_SIZE(0)) * ALIGNMENT_SIZE); @@ -319,7 +306,7 @@ void heap_free(Heap* const heap, void *ptr, uintptr_t client_pc) { #endif // Update metrics -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION heap_info_ptr->pc = client_pc; #endif heap->current_size -= heap_info_ptr->Size * ALIGNMENT_SIZE; @@ -530,7 +517,7 @@ static HeapInfo_t *allocate_block(Heap* const heap, unsigned long n_units, HeapI // A very naive realloc implementation void* heap_realloc(Heap* const heap, void *ptr, unsigned long nbytes, uintptr_t client_pc) { -#if !defined(MALLOC_INSTRUMENTATION) +#if !defined(CONFIG_MALLOC_INSTRUMENTATION) client_pc = 0; #endif // Get a pointer to the Heap Info. @@ -563,7 +550,7 @@ uint32_t heap_get_minimum_headroom(Heap *heap) { // Serial Commands /////////////////////////////////////////////////////////// -#ifdef MALLOC_INSTRUMENTATION +#ifdef CONFIG_MALLOC_INSTRUMENTATION void heap_dump_malloc_instrumentation_to_dbgserial(Heap *heap) { char buffer[80]; unsigned long num_free_blocks = 0; diff --git a/src/libutil/hexdump.c b/src/libutil/hexdump.c index 7095b53dad..f7063ad2d1 100644 --- a/src/libutil/hexdump.c +++ b/src/libutil/hexdump.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/hexdump.h" diff --git a/src/libutil/includes/util/assert.h b/src/libutil/includes/util/assert.h index ba87275b26..e1caa038b3 100644 --- a/src/libutil/includes/util/assert.h +++ b/src/libutil/includes/util/assert.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/attributes.h b/src/libutil/includes/util/attributes.h index abf07f1161..9c2561c8ea 100644 --- a/src/libutil/includes/util/attributes.h +++ b/src/libutil/includes/util/attributes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -57,23 +44,6 @@ # define SECTION(SEC) #endif -// Only present on STM32F7 -#define DTCM_BSS SECTION(".dtcm_bss") - -// DMA_BSS: Section attribute for DMA buffers -#if MICRO_FAMILY_STM32F7 -# define DMA_BSS DTCM_BSS -// There is an erratum present in STM32F7xx which causes DMA reads from DTCM -// (but not writes to DTCM) to be corrupted if the MCU enters sleep mode during -// the transfer. Source DMA buffers must be placed in SRAM on these platforms. -// The DMA driver enforces this. Also, alignment to the start of a cache line -// seems to be required, though it's unclear why. -# define DMA_READ_BSS ALIGN(32) -#else -# define DMA_BSS -# define DMA_READ_BSS -#endif - // Use this macro to allow overriding of private functions in order to test them within unit tests. #if !UNITTEST # define T_STATIC static diff --git a/src/libutil/includes/util/build_id.h b/src/libutil/includes/util/build_id.h index 4a451d125a..92883f5e76 100644 --- a/src/libutil/includes/util/build_id.h +++ b/src/libutil/includes/util/build_id.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/circular_buffer.h b/src/libutil/includes/util/circular_buffer.h index e2e5250c10..fecf6c4070 100644 --- a/src/libutil/includes/util/circular_buffer.h +++ b/src/libutil/includes/util/circular_buffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/circular_cache.h b/src/libutil/includes/util/circular_cache.h index 6f13d7e55b..bffde787bf 100644 --- a/src/libutil/includes/util/circular_cache.h +++ b/src/libutil/includes/util/circular_cache.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/crc32.h b/src/libutil/includes/util/crc32.h index 80ac71ebae..83f524fd82 100644 --- a/src/libutil/includes/util/crc32.h +++ b/src/libutil/includes/util/crc32.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/hash.h b/src/libutil/includes/util/hash.h index 7be628c44c..29284a3557 100644 --- a/src/libutil/includes/util/hash.h +++ b/src/libutil/includes/util/hash.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/heap.h b/src/libutil/includes/util/heap.h index 1127142a74..7efd01708d 100644 --- a/src/libutil/includes/util/heap.h +++ b/src/libutil/includes/util/heap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -85,7 +72,7 @@ void heap_set_corruption_handler(Heap *heap, CorruptionHandler corruption_handle //! @note heap_init() must be called prior to using heap_malloc(). //! @param nbytes Number of bytes to be allocated. Must be > 0. //! @param client_pc The PC register of the client who caused this malloc. Only used when -//! MALLOC_INSTRUMENTATION is defined. +//! CONFIG_MALLOC_INSTRUMENTATION is defined. //! @return A pointer to the start of the allocated memory void* heap_malloc(Heap* const heap, unsigned long nbytes, uintptr_t client_pc); @@ -104,7 +91,7 @@ void heap_free(Heap* const heap, void* ptr, uintptr_t client_pc); //! @param ptr Points to the memory region to re-allocate. //! @param nbytes The total number of bytes to allocate. //! @param client_pc The PC register of the client who caused this malloc. Only used when -//! MALLOC_INSTRUMENTATION is defined. +//! CONFIG_MALLOC_INSTRUMENTATION is defined. void* heap_realloc(Heap* const heap, void *ptr, unsigned long nbytes, uintptr_t client_pc); //! Allocate a buffer to hold anything. The initial contents of the buffer diff --git a/src/libutil/includes/util/hexdump.h b/src/libutil/includes/util/hexdump.h index a792d561cf..a214c4b876 100644 --- a/src/libutil/includes/util/hexdump.h +++ b/src/libutil/includes/util/hexdump.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/iterator.h b/src/libutil/includes/util/iterator.h index 2667abc5e9..6a933d7ad0 100644 --- a/src/libutil/includes/util/iterator.h +++ b/src/libutil/includes/util/iterator.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Simple utility for enforcing consistent use of the iterator pattern //! and facilitate unit testing. diff --git a/src/libutil/includes/util/keyed_circular_cache.h b/src/libutil/includes/util/keyed_circular_cache.h index b15a2363ed..325c4c2e1f 100644 --- a/src/libutil/includes/util/keyed_circular_cache.h +++ b/src/libutil/includes/util/keyed_circular_cache.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/likely.h b/src/libutil/includes/util/likely.h index 1d00762323..a5791e5a0a 100644 --- a/src/libutil/includes/util/likely.h +++ b/src/libutil/includes/util/likely.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/list.h b/src/libutil/includes/util/list.h index 6d1e874185..2dee61324f 100644 --- a/src/libutil/includes/util/list.h +++ b/src/libutil/includes/util/list.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/logging.h b/src/libutil/includes/util/logging.h index 1b7337b57f..80054b2e93 100644 --- a/src/libutil/includes/util/logging.h +++ b/src/libutil/includes/util/logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/math.h b/src/libutil/includes/util/math.h index 5fb3ec559e..7f71463f87 100644 --- a/src/libutil/includes/util/math.h +++ b/src/libutil/includes/util/math.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -41,10 +28,15 @@ // ex. val = 152 mod = 32 : returns 160 // val = -32 mod = 90 : returns -90 #define ROUND_TO_MOD_CEIL(val, mod) \ - ((val >= 0) ? \ + (((val) >= 0) ? \ ((((val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)) : \ -((((-val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod))) +// Round unsigned value up to the next increment of modulus +// ex. val = 152 mod = 32 : returns 160 +#define ROUND_TO_MOD_CEIL_U(val, mod) \ + ((((val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)) + int32_t sign_extend(uint32_t a, int bits); //! Calculates the distance (end - start), taking a roll-over into account as good as it can get. diff --git a/src/libutil/includes/util/math_fixed.h b/src/libutil/includes/util/math_fixed.h index 312a283051..e7ed2949fc 100644 --- a/src/libutil/includes/util/math_fixed.h +++ b/src/libutil/includes/util/math_fixed.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/misc.h b/src/libutil/includes/util/misc.h index 0c335ae5a3..c413517024 100644 --- a/src/libutil/includes/util/misc.h +++ b/src/libutil/includes/util/misc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/order.h b/src/libutil/includes/util/order.h index 39f058053c..18bd055982 100644 --- a/src/libutil/includes/util/order.h +++ b/src/libutil/includes/util/order.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/rand32.h b/src/libutil/includes/util/rand32.h index 9d64a3dbfb..7fce32e9b3 100644 --- a/src/libutil/includes/util/rand32.h +++ b/src/libutil/includes/util/rand32.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/size.h b/src/libutil/includes/util/size.h index 6bad013784..f9cc8f2025 100644 --- a/src/libutil/includes/util/size.h +++ b/src/libutil/includes/util/size.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/slist.h b/src/libutil/includes/util/slist.h new file mode 100644 index 0000000000..75c720fdc9 --- /dev/null +++ b/src/libutil/includes/util/slist.h @@ -0,0 +1,94 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once +#include +#include +#include "order.h" + +typedef struct SingleListNode { + struct SingleListNode *next; +} SingleListNode; + +typedef bool (*SingleListFilterCallback)(SingleListNode *found_node, void *data); + +//! - If a callback returns true, the iteration continues +//! - If a callback returns false, the ieration stops. +typedef bool (*SingleListForEachCallback)(SingleListNode *node, void *context); + +#define SINGLE_LIST_NODE_NULL { .next = NULL } + +//! Initializes the node. +void slist_init(SingleListNode *node); + +//! Inserts new_node after node in the list. +//! Always returns new_node. +SingleListNode* slist_insert_after(SingleListNode *node, SingleListNode *new_node); + +//! Prepends new_node to the head of the list. +//! @param head The current head of the list, can be NULL. +//! Always returns the new head of the list. +SingleListNode* slist_prepend(SingleListNode *head, SingleListNode *new_node); + +//! Appends new_node to the tail of the list that head is part of. +//! @param head Any node in the list, can be NULL (will result in a list containing only new_node). +//! Always returns the tail of the list. +SingleListNode* slist_append(SingleListNode *head, SingleListNode *new_node); + +//! Removes the head of the list and returns the new head. +SingleListNode* slist_pop_head(SingleListNode *head); + +//! Removes the node from the list. +//! @param node the SingleListNode to remove. +//! @param[in,out] *head will be updated if the removed node happens to be the head. +//! @note head must not be NULL. +void slist_remove(SingleListNode *node, SingleListNode **head); + +//! Gets the next node. +SingleListNode* slist_get_next(SingleListNode *node); + +//! Gets the last node in the list. +SingleListNode* slist_get_tail(SingleListNode *node); + +//! @return true if the passed in node is the tail of a list. +bool slist_is_tail(const SingleListNode *node); + +//! Counts the number of nodes from head to tail. +uint32_t slist_count(SingleListNode *head); + +//! @param[in] head The head of the list to search. +//! @param[in] node The node to search for. +//! @returns True if the list contains node. +bool slist_contains(const SingleListNode *head, const SingleListNode *node); + +//! Gets the first node that conforms to the given filter callback. +//! @param head The list node from which to start the search. +//! @param filter_callback A function returning true if the node matches the filter criteria. +//! @param data Optional callback data. +SingleListNode* slist_find(SingleListNode *head, SingleListFilterCallback filter_callback, + void *data); + +//! Adds a node to a list ordered by given comparator. +//! @param[in] head The head of the list that we want to add to. +//! @param[in] new_node The node being added. +//! @param[in] comparator The comparison function to use. +//! @param[in] ascending True to maintain the list ordered ascending from head to tail. +//! @returns The (new) head of the list. +//! @note This function will not sort existing nodes in the list. +SingleListNode* slist_sorted_add(SingleListNode *head, SingleListNode *new_node, + Comparator comparator, bool ascending); + +//! Concatenate two lists. +//! @param list_a list onto which to concatenate list_b. +//! @param list_b list to concatenate onto list_a. +//! @return head of the new list. +SingleListNode* slist_concatenate(SingleListNode *list_a, SingleListNode *list_b); + +//! Iterates over each node and passes it into callback given. +//! @param[in] head The head of the list that we want to iterate over. +//! @param[in] each_cb The callback function to pass each node into. +//! @param[in] context Optional callback data. +void slist_foreach(SingleListNode *head, SingleListForEachCallback each_cb, void *context); + +//! Dump a list to PBL_LOG. +void slist_debug_dump(SingleListNode *head); diff --git a/src/libutil/includes/util/sort.h b/src/libutil/includes/util/sort.h index f7a75bd61b..6d45d26b99 100644 --- a/src/libutil/includes/util/sort.h +++ b/src/libutil/includes/util/sort.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/string.h b/src/libutil/includes/util/string.h index 4e86deb3b3..646b4457b7 100644 --- a/src/libutil/includes/util/string.h +++ b/src/libutil/includes/util/string.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -35,7 +22,7 @@ void string_strip_trailing_whitespace(const char *string, char *string_out); //! Converts an unsigned integer value to a null-terminated hex-value string and stores the result //! in buffer. -void itoa(uint32_t num, char *buffer, int buffer_length); +void string_itoa(uint32_t num, char *buffer, int buffer_length); //! Converts a signed integer value to a null-terminated string and stores the result in buffer. //! NOTE: Buffer must be long enough to fit a string 12 bytes long. diff --git a/src/libutil/includes/util/struct.h b/src/libutil/includes/util/struct.h index 35a1189ad8..7beda2c33b 100644 --- a/src/libutil/includes/util/struct.h +++ b/src/libutil/includes/util/struct.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/includes/util/trig.h b/src/libutil/includes/util/trig.h index 14934042ff..256729b6af 100644 --- a/src/libutil/includes/util/trig.h +++ b/src/libutil/includes/util/trig.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/src/libutil/includes/util/uuid.h b/src/libutil/includes/util/uuid.h index 8ef5033cae..73707e04e5 100644 --- a/src/libutil/includes/util/uuid.h +++ b/src/libutil/includes/util/uuid.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/src/libutil/iterator.c b/src/libutil/iterator.c index 2c81504e84..fc82b80482 100644 --- a/src/libutil/iterator.c +++ b/src/libutil/iterator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/iterator.h" diff --git a/src/libutil/keyed_circular_cache.c b/src/libutil/keyed_circular_cache.c index 2ee215dbe7..b02476ea2b 100644 --- a/src/libutil/keyed_circular_cache.c +++ b/src/libutil/keyed_circular_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/keyed_circular_cache.h" diff --git a/src/libutil/list.c b/src/libutil/list.c index 152a9f0e02..bce62a6f4f 100644 --- a/src/libutil/list.c +++ b/src/libutil/list.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/list.h" #include "util/assert.h" diff --git a/src/libutil/math.c b/src/libutil/math.c index b6638f12a8..ceef4fcc43 100644 --- a/src/libutil/math.c +++ b/src/libutil/math.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/math.h" diff --git a/src/libutil/math_fixed.c b/src/libutil/math_fixed.c index a147873e93..baa86798c7 100644 --- a/src/libutil/math_fixed.c +++ b/src/libutil/math_fixed.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/assert.h" #include "util/math_fixed.h" diff --git a/src/libutil/order.c b/src/libutil/order.c index 01d2d09cc5..52d2b51c24 100644 --- a/src/libutil/order.c +++ b/src/libutil/order.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/order.h" diff --git a/src/libutil/platform.c b/src/libutil/platform.c index fb5d18c3ac..f24e902cff 100644 --- a/src/libutil/platform.c +++ b/src/libutil/platform.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/assert.h" #include "util/logging.h" @@ -45,5 +32,5 @@ WEAK NORETURN util_assertion_failed(const char *filename, int line) { } WEAK uint32_t rand32(void) { - return ((uint32_t)rand() << 1) + (uint32_t)rand; + return ((uint32_t)rand() << 1) + (uint32_t)rand(); } diff --git a/src/libutil/slist.c b/src/libutil/slist.c new file mode 100644 index 0000000000..b8d5548059 --- /dev/null +++ b/src/libutil/slist.c @@ -0,0 +1,207 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "util/slist.h" +#include "util/assert.h" +#include "util/logging.h" + +#include +#include + +void slist_init(SingleListNode *node) { + node->next = NULL; +} + +SingleListNode* slist_insert_after(SingleListNode *node, SingleListNode *new_node) { + if (node == NULL) { + return new_node; + } + new_node->next = node->next; + node->next = new_node; + + return new_node; +} + +SingleListNode* slist_prepend(SingleListNode *head, SingleListNode *new_node) { + if (new_node == NULL) { + return head; + } + new_node->next = head; + return new_node; +} + +SingleListNode* slist_append(SingleListNode *head, SingleListNode *new_node) { + return slist_insert_after(slist_get_tail(head), new_node); +} + +SingleListNode* slist_pop_head(SingleListNode *head) { + if (head == NULL) { + return NULL; + } + SingleListNode *new_head = head->next; + head->next = NULL; + return new_head; +} + +void slist_remove(SingleListNode *node, SingleListNode **head) { + if (node == NULL || head == NULL || *head == NULL) { + return; + } + + if (*head == node) { + *head = node->next; + node->next = NULL; + return; + } + + SingleListNode *prev = *head; + while (prev->next != NULL && prev->next != node) { + prev = prev->next; + } + + if (prev->next == node) { + prev->next = node->next; + node->next = NULL; + } +} + +SingleListNode* slist_get_next(SingleListNode *node) { + if (node == NULL) { + return NULL; + } + return node->next; +} + +SingleListNode* slist_get_tail(SingleListNode *node) { + if (node == NULL) { + return NULL; + } + while (node->next != NULL) { + node = node->next; + } + return node; +} + +bool slist_is_tail(const SingleListNode *node) { + if (!node) { + return false; + } + return !node->next; +} + +uint32_t slist_count(SingleListNode *head) { + if (head == NULL) { + return 0; + } + uint32_t count = 1; + while ((head = head->next) != NULL) { + ++count; + } + return count; +} + +bool slist_contains(const SingleListNode *head, const SingleListNode *node) { + if (head == NULL || node == NULL) { + return false; + } + while (head) { + if (head == node) { + return true; + } + head = head->next; + } + return false; +} + +SingleListNode* slist_find(SingleListNode *head, SingleListFilterCallback filter_callback, + void *data) { + if (head == NULL) { + return NULL; + } + SingleListNode *cursor = head; + do { + if (filter_callback(cursor, data)) { + return cursor; + } + } while ((cursor = cursor->next)); + return NULL; +} + +SingleListNode* slist_sorted_add(SingleListNode *head, SingleListNode *new_node, + Comparator comparator, bool ascending) { + if (head == NULL) { + return new_node; + } + if (new_node == NULL) { + return head; + } + + SingleListNode *prev = NULL; + SingleListNode *cursor = head; + for (;;) { + int order = comparator(cursor, new_node); + if (!ascending) { + order = -order; + } + + if (order < 0) { + // Insert before cursor + new_node->next = cursor; + if (prev == NULL) { + return new_node; + } else { + prev->next = new_node; + return head; + } + } + if (cursor->next == NULL) { + // Append after the last node + cursor->next = new_node; + new_node->next = NULL; + return head; + } + prev = cursor; + cursor = cursor->next; + } +} + +SingleListNode* slist_concatenate(SingleListNode *restrict list_a, + SingleListNode *restrict list_b) { + if (list_a == NULL) { + return list_b; + } + if (list_b == NULL) { + return list_a; + } + + SingleListNode *tail_a = slist_get_tail(list_a); + tail_a->next = list_b; + + return list_a; +} + +void slist_foreach(SingleListNode *head, SingleListForEachCallback each_cb, void *context) { + if (!each_cb) { + return; + } + + SingleListNode *iter = head; + while (iter) { + // Save off a pointer so the client can destroy the node in the callback + SingleListNode *next = iter->next; + if (!each_cb(iter, context)) { + return; + } + iter = next; + } +} + +void slist_debug_dump(SingleListNode *head) { + SingleListNode *iter = head; + char buffer[20]; + while (iter) { + snprintf(buffer, sizeof(buffer), "node %p (%p)", iter, iter->next); + UTIL_LOG(buffer); + iter = iter->next; + } +} diff --git a/src/libutil/sort.c b/src/libutil/sort.c index f79e054a44..1846c29137 100644 --- a/src/libutil/sort.c +++ b/src/libutil/sort.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/src/libutil/string.c b/src/libutil/string.c index 936faa4811..0b4c602512 100644 --- a/src/libutil/string.c +++ b/src/libutil/string.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/string.h" @@ -55,7 +42,7 @@ const char *bool_to_str(bool b) { } } -void itoa(uint32_t num, char *buffer, int buffer_length) { +void string_itoa(uint32_t num, char *buffer, int buffer_length) { if (buffer_length < 11) { return; } diff --git a/src/libutil/trig.c b/src/libutil/trig.c index 0f026f161c..50a1190d46 100644 --- a/src/libutil/trig.c +++ b/src/libutil/trig.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/trig.h" diff --git a/src/libutil/uuid.c b/src/libutil/uuid.c index e66b681f50..a460afa78b 100644 --- a/src/libutil/uuid.c +++ b/src/libutil/uuid.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/uuid.h" #include "util/rand32.h" diff --git a/src/libutil/wscript b/src/libutil/wscript deleted file mode 100644 index 1b3c111aba..0000000000 --- a/src/libutil/wscript +++ /dev/null @@ -1,24 +0,0 @@ -import waftools - - -def build(bld): - sources = bld.path.ant_glob('**/*.c') - - def build_libutil(target, env): - # Build the libutil directory using firmware environment - bld.stlib(source=sources, - target=target, - includes=['.', 'includes'], - use='pblibc_includes', - env=env.derive()) - - bld(export_includes=['includes'], name='libutil_includes') - - if (bld.variant not in ('test', 'test_rocky_emx')): - build_libutil('libutil-cm0', bld.all_envs['cortex-m0']) - - build_libutil('libutil', bld.env) - build_libutil('libutil-32bit', bld.all_envs['32bit']) - - -# vim:filetype=python diff --git a/src/libutil/wscript_build b/src/libutil/wscript_build new file mode 100644 index 0000000000..e0fb911b10 --- /dev/null +++ b/src/libutil/wscript_build @@ -0,0 +1,17 @@ +import waftools + +sources = bld.path.ant_glob('**/*.c') + +def build_libutil(target, env): + # Build the libutil directory using firmware environment + bld.stlib(source=sources, + target=target, + includes=['.', 'includes'], + use='pblibc_includes', + env=env.derive()) + +bld(export_includes=['includes'], name='libutil_includes') + +build_libutil('libutil', bld.env) + +# vim:filetype=python diff --git a/stored_apps/golf/src/golf.c b/stored_apps/golf/src/golf.c index e61687897c..51ad6eb784 100644 --- a/stored_apps/golf/src/golf.c +++ b/stored_apps/golf/src/golf.c @@ -1,22 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! The app is driven by pebble protocol app_messages, used indirectly through app_sync. -#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" -#include #include #include "golf_resources.h" diff --git a/stored_apps/golf/src/golf_resources.h b/stored_apps/golf/src/golf_resources.h index f742d002b0..972f80d34b 100644 --- a/stored_apps/golf/src/golf_resources.h +++ b/stored_apps/golf/src/golf_resources.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/stored_apps/wscript b/stored_apps/wscript deleted file mode 100644 index 746745a64e..0000000000 --- a/stored_apps/wscript +++ /dev/null @@ -1,135 +0,0 @@ -import os -import time - -import waflib -import waflib.Tools -import waftools.objcopy as objcopy -import waftools.pebble_sdk_gcc as pebble_sdk_gcc - -from resources.types.resource_definition import ResourceDefinition -from resources.types.resource_object import ResourceObject - -from pebble_sdk_platform import pebble_platforms, maybe_import_internal -from pebble_sdk_version import set_env_sdk_version - -import generate_appinfo - -# -# Make each of the apps within the stored_apps directory -# - - -# ----------------------------------------------------------------------------------- -def configure(conf): - process_info = conf.path.parent.find_node('src/fw/process_management/pebble_process_info.h') - set_env_sdk_version(conf, process_info) - pebble_sdk_gcc.configure(conf) - conf.env.append_value('DEFINES', 'RELEASE') - - -# ----------------------------------------------------------------------------------- -def build_app(bld, app_name): - sdk_folder = bld.path.parent.get_bld().make_node('sdk').make_node(bld.env.PLATFORM_NAME) - - # ----------------------------------------------------------------------------------- - # Generate the appinfo.auto.c file - appinfo_json_node = bld.path.get_src().find_node('%s/appinfo.json' % (app_name)) - if appinfo_json_node is None: - bld.fatal('Could not find appinfo.json') - appinfo_c_node = bld.path.get_bld().make_node('%s/appinfo.auto.c' % (app_name)) - - resource_ids_auto_node = bld.path.get_bld().make_node( - '%s/src/resource_ids.auto.h' % (app_name)) - bld(rule='echo "#define DEFAULT_MENU_ICON 0" >> ${TGT}', target=resource_ids_auto_node) - - message_keys_auto_node = bld.path.get_bld().make_node(app_name).make_node("message_keys.auto.h") - bld(rule='touch ${TGT}', target=message_keys_auto_node) - - def _generate_appinfo_c_file_rule(task): - generate_appinfo.generate_appinfo(task.inputs[0].abspath(), task.outputs[0].abspath()) - - bld(rule=_generate_appinfo_c_file_rule, - source=appinfo_json_node, - target=appinfo_c_node) - - - # ----------------------------------------------------------------------------------- - # Generate the rule to compile and link the sources into an ELF - includes = [sdk_folder.make_node('include'), app_name, '%s/src' % (app_name)] - link_flags = ['-mcpu=cortex-m3','-mthumb','-fPIE', '-Wl,--emit-relocs'] - source = bld.path.ant_glob('%s/src/**/*.c' % (app_name)) + [appinfo_c_node] - ld_script = bld.path.make_node('pebble_app.ld').path_from(bld.path) - app_elf_file = bld.path.get_bld().make_node('%s/pebble-app.elf' % (app_name)) - - stored_apps_env = bld.all_envs['stored_apps'] - - maybe_import_internal(bld.env) - - # Fetch platform-specific defines and add them to CFLAGS - platform_name = stored_apps_env['PLATFORM_NAME'] - platform_info = pebble_platforms.get(platform_name, None) - if platform_info is None: - bld.fatal("Unsupported platform: %s" % platform_name) - - platform_defines = platform_info['DEFINES'] - c_flags = ['-fPIE'] + ['-D%s' % define for define in platform_defines] - - gen = bld.program(env=stored_apps_env, - source=source, - target=app_elf_file, - includes=includes, - cflags=c_flags, - ldscript=ld_script, - linkflags=link_flags, - stlibpath=[sdk_folder.make_node('lib').abspath()], - stlib=['pebble']) - - - # ----------------------------------------------------------------------------------- - # Create the bin file and inject the metadata - app_raw_bin_file = bld.path.get_bld().make_node('%s/pebble-app.raw.bin' % (app_name)) - bld(rule=objcopy.objcopy_bin, source=app_elf_file, target=app_raw_bin_file) - - app_bin_file = bld.path.get_bld().make_node('%s/pebble-app.bin' % (app_name)) - # Use a dummy timestamp for the metadata in stored_apps. This timestamp is only used - # to describe the resource version, and stored_apps have no resources. If we use the - # real timestamp that will cause the CRC of the app to change from build to build, - # which causes the pbpack's CRC to change from build to build, which means we have to - # keep using image_resources in development every time we rebuild, even though the - # content didn't change. - pebble_sdk_gcc.gen_inject_metadata_rule(bld, src_bin_file=app_raw_bin_file, - dst_bin_file=app_bin_file, elf_file=app_elf_file, - resource_file=None, timestamp=0, has_pkjs=False, - has_worker=False) - - # ----------------------------------------------------------------------------------- - # Copy into the resources directory as a .reso file - def _make_reso(task): - app_bin_data = task.inputs[0].read(flags='rb') - - reso = ResourceObject( - ResourceDefinition('raw', 'STORED_APP_{}'.format(app_name.upper()), None), - app_bin_data) - reso.dump(task.outputs[0]) - - resources_bld_node = bld.bldnode.make_node('resources/') - app_resource_node = resources_bld_node.make_node('normal/base/raw/app_%s.bin.reso' % (app_name)) - bld(rule=_make_reso, source=app_bin_file, target=app_resource_node, name='stored_app_reso') - - bld.DYNAMIC_RESOURCES.append(app_resource_node) - - -# ----------------------------------------------------------------------------------- -def build(bld): - # When you add a new stored app, you must also do the following to include it into the - # system resources and register it with the launcher: - # 1.) Add a raw resource entry with a name of "STORED_APP_" to - # resources/normal/resource_map.json. - # The "file" field should be set to normal/raw/app_.bin - # 2.) Add a new entry to the INIT_STORED_APPS array in system_app_registry.h - apps = bld.path.ant_glob('*', dir=True, src=False) - for app in apps: - build_app(bld, app.name) - - -# vim:filetype=python diff --git a/stored_apps/wscript_build b/stored_apps/wscript_build new file mode 100644 index 0000000000..4aadd89c32 --- /dev/null +++ b/stored_apps/wscript_build @@ -0,0 +1,117 @@ +import waftools.objcopy as objcopy +import waftools.pebble_sdk_gcc as pebble_sdk_gcc + +from resources.types.resource_definition import ResourceDefinition +from resources.types.resource_object import ResourceObject + +from pebble_sdk_platform import pebble_platforms + +import generate_appinfo + +# +# Make each of the apps within the stored_apps directory +# + + +# ----------------------------------------------------------------------------------- +def build_app(bld, app_name): + sdk_folder = bld.path.parent.get_bld().make_node('sdk').make_node(bld.env.PLATFORM_NAME) + + # ----------------------------------------------------------------------------------- + # Generate the appinfo.auto.c file + appinfo_json_node = bld.path.get_src().find_node('%s/appinfo.json' % (app_name)) + if appinfo_json_node is None: + bld.fatal('Could not find appinfo.json') + appinfo_c_node = bld.path.get_bld().make_node('%s/appinfo.auto.c' % (app_name)) + + resource_ids_auto_node = bld.path.get_bld().make_node( + '%s/src/resource_ids.auto.h' % (app_name)) + bld(rule='echo "#define DEFAULT_MENU_ICON 0" >> ${TGT}', target=resource_ids_auto_node) + + message_keys_auto_node = bld.path.get_bld().make_node(app_name).make_node("message_keys.auto.h") + bld(rule='touch ${TGT}', target=message_keys_auto_node) + + def _generate_appinfo_c_file_rule(task): + generate_appinfo.generate_appinfo(task.inputs[0].abspath(), task.outputs[0].abspath()) + + bld(rule=_generate_appinfo_c_file_rule, + source=appinfo_json_node, + target=appinfo_c_node) + + + # ----------------------------------------------------------------------------------- + # Generate the rule to compile and link the sources into an ELF + includes = [sdk_folder.make_node('include'), app_name, '%s/src' % (app_name)] + link_flags = ['-mcpu=cortex-m3','-mthumb','-fPIE', '-Wl,--emit-relocs'] + source = bld.path.ant_glob('%s/src/**/*.c' % (app_name)) + [appinfo_c_node] + ld_script = bld.path.make_node('pebble_app.ld').path_from(bld.path) + app_elf_file = bld.path.get_bld().make_node('%s/pebble-app.elf' % (app_name)) + + stored_apps_env = bld.all_envs['stored_apps'] + + # Fetch platform-specific defines and add them to CFLAGS + platform_name = stored_apps_env['PLATFORM_NAME'] + platform_info = pebble_platforms.get(platform_name, None) + if platform_info is None: + bld.fatal("Unsupported platform: %s" % platform_name) + + platform_defines = platform_info['DEFINES'] + c_flags = ['-fPIE'] + ['-D%s' % define for define in platform_defines] + + gen = bld.program(env=stored_apps_env, + source=source, + target=app_elf_file, + includes=includes, + cflags=c_flags, + ldscript=ld_script, + linkflags=link_flags, + stlibpath=[sdk_folder.make_node('lib').abspath()], + stlib=['pebble']) + + + # ----------------------------------------------------------------------------------- + # Create the bin file and inject the metadata + app_raw_bin_file = bld.path.get_bld().make_node('%s/pebble-app.raw.bin' % (app_name)) + bld(rule=objcopy.objcopy_bin, source=app_elf_file, target=app_raw_bin_file) + + app_bin_file = bld.path.get_bld().make_node('%s/pebble-app.bin' % (app_name)) + # Use a dummy timestamp for the metadata in stored_apps. This timestamp is only used + # to describe the resource version, and stored_apps have no resources. If we use the + # real timestamp that will cause the CRC of the app to change from build to build, + # which causes the pbpack's CRC to change from build to build, which means we have to + # keep using image_resources in development every time we rebuild, even though the + # content didn't change. + pebble_sdk_gcc.gen_inject_metadata_rule(bld, src_bin_file=app_raw_bin_file, + dst_bin_file=app_bin_file, elf_file=app_elf_file, + resource_file=None, timestamp=0, has_pkjs=False, + has_worker=False) + + # ----------------------------------------------------------------------------------- + # Copy into the resources directory as a .reso file + def _make_reso(task): + app_bin_data = task.inputs[0].read(flags='rb') + + reso = ResourceObject( + ResourceDefinition('raw', 'STORED_APP_{}'.format(app_name.upper()), None), + app_bin_data) + reso.dump(task.outputs[0]) + + resources_bld_node = bld.bldnode.make_node('resources/') + app_resource_node = resources_bld_node.make_node('normal/base/raw/app_%s.bin.reso' % (app_name)) + bld(rule=_make_reso, source=app_bin_file, target=app_resource_node, name='stored_app_reso') + + bld.DYNAMIC_RESOURCES.append(app_resource_node) + + +# ----------------------------------------------------------------------------------- +# When you add a new stored app, you must also do the following to include it into the +# system resources and register it with the launcher: +# 1.) Add a raw resource entry with a name of "STORED_APP_" to +# resources/normal/resource_map.json. +# The "file" field should be set to normal/raw/app_.bin +# 2.) Add a new entry to the INIT_STORED_APPS array in system_app_registry.h +apps = bld.path.ant_glob('*', dir=True, src=False) +for app in apps: + build_app(bld, app.name) + +# vim:filetype=python diff --git a/tests/fakes/fake_GAPAPI.c b/tests/fakes/fake_GAPAPI.c index 1f317e4bf7..70cf4338c9 100644 --- a/tests/fakes/fake_GAPAPI.c +++ b/tests/fakes/fake_GAPAPI.c @@ -1,22 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_GAPAPI.h" #include "bluetopia_interface.h" +#include #include @@ -27,21 +15,21 @@ static bool s_is_le_advertising_enabled; static GAP_LE_Event_Callback_t s_le_adv_connection_event_callback; static unsigned long s_le_adv_connection_callback_param; -static uint16_t s_min_advertising_interval_slots; -static uint16_t s_max_advertising_interval_slots; +static uint32_t s_min_advertising_interval_ms; +static uint32_t s_max_advertising_interval_ms; void gap_le_set_advertising_disabled(void) { s_is_le_advertising_enabled = false; - s_min_advertising_interval_slots = 0; - s_max_advertising_interval_slots = 0; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; } int GAP_LE_Advertising_Disable(unsigned int BluetoothStackID) { s_is_le_advertising_enabled = false; s_le_adv_connection_event_callback = NULL; s_le_adv_connection_callback_param = 0; - s_min_advertising_interval_slots = 0; - s_max_advertising_interval_slots = 0; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; return 0; } @@ -54,22 +42,33 @@ int GAP_LE_Advertising_Enable(unsigned int BluetoothStackID, s_is_le_advertising_enabled = true; s_le_adv_connection_event_callback = GAP_LE_Event_Callback; s_le_adv_connection_callback_param = CallbackParameter; - if (GAP_LE_Advertising_Parameters) { - // Convert from ms to slots: - s_min_advertising_interval_slots = - (GAP_LE_Advertising_Parameters->Advertising_Interval_Min * 16) / 10; - s_max_advertising_interval_slots = - (GAP_LE_Advertising_Parameters->Advertising_Interval_Max * 16) / 10; - } else { - s_min_advertising_interval_slots = 0; - s_max_advertising_interval_slots = 0; - } return 0; } -void gap_le_assert_advertising_interval(uint16_t expected_min_slots, uint16_t expected_max_slots) { - cl_assert_equal_i(s_min_advertising_interval_slots, expected_min_slots); - cl_assert_equal_i(s_max_advertising_interval_slots, expected_max_slots); +// bt_driver_advert fakes used by gap_le_advert.c +bool bt_driver_advert_advertising_enable(uint32_t min_interval_ms, uint32_t max_interval_ms) { + s_is_le_advertising_enabled = true; + s_min_advertising_interval_ms = min_interval_ms; + s_max_advertising_interval_ms = max_interval_ms; + return true; +} + +void bt_driver_advert_advertising_disable(void) { + s_is_le_advertising_enabled = false; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; +} + +// Expected ms values for each interval preset +static const uint32_t s_expected_interval_ms[] = { + [GAPLEAdvertisingInterval_Short] = 20, + [GAPLEAdvertisingInterval_Long] = 1022, +}; + +void gap_le_assert_advertising_interval(GAPLEAdvertisingInterval expected) { + uint32_t expected_ms = s_expected_interval_ms[expected]; + cl_assert_equal_i(s_min_advertising_interval_ms, expected_ms); + cl_assert_equal_i(s_max_advertising_interval_ms, expected_ms); } bool gap_le_is_advertising_enabled(void) { @@ -108,6 +107,22 @@ unsigned int gap_le_get_scan_response_data(Scan_Response_Data_t *scan_resp_data_ return s_scan_resp_data_length; } +bool bt_driver_advert_set_advertising_data(const BLEAdData *ad_data) { + if (ad_data) { + memcpy(&s_ad_data, ad_data->data, ad_data->ad_data_length); + s_ad_data_length = ad_data->ad_data_length; + memcpy(&s_scan_resp_data, ad_data->data + ad_data->ad_data_length, + ad_data->scan_resp_data_length); + s_scan_resp_data_length = ad_data->scan_resp_data_length; + } + + return true; +} + +bool bt_driver_advert_client_get_tx_power(int8_t *tx_power) { + return false; +} + static GAP_LE_Event_Callback_t s_le_create_connection_event_callback; static unsigned long s_le_create_connection_callback_param; @@ -354,6 +369,8 @@ void fake_GAPAPI_init(void) { s_is_le_advertising_enabled = false; s_le_adv_connection_event_callback = NULL; s_le_adv_connection_callback_param = 0; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; memset(&s_ad_data, 0, sizeof(s_ad_data)); s_ad_data_length = 0; s_le_create_connection_event_callback = NULL; diff --git a/tests/fakes/fake_GAPAPI.h b/tests/fakes/fake_GAPAPI.h index 309333d439..7d45895c33 100644 --- a/tests/fakes/fake_GAPAPI.h +++ b/tests/fakes/fake_GAPAPI.h @@ -1,24 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "GAPAPI.h" #include +#include "comm/ble/gap_le_advert.h" #include #include @@ -28,7 +16,7 @@ void gap_le_set_advertising_disabled(void); bool gap_le_is_advertising_enabled(void); -void gap_le_assert_advertising_interval(uint16_t expected_min_slots, uint16_t expected_max_slots); +void gap_le_assert_advertising_interval(GAPLEAdvertisingInterval expected); unsigned int gap_le_get_advertising_data(Advertising_Data_t *ad_data_out); unsigned int gap_le_get_scan_response_data(Scan_Response_Data_t *scan_resp_data_out); diff --git a/tests/fakes/fake_GATTAPI.c b/tests/fakes/fake_GATTAPI.c index 516099243a..6d32b203bd 100644 --- a/tests/fakes/fake_GATTAPI.c +++ b/tests/fakes/fake_GATTAPI.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_GATTAPI.h" diff --git a/tests/fakes/fake_GATTAPI.h b/tests/fakes/fake_GATTAPI.h index 89250c5900..7559c58077 100644 --- a/tests/fakes/fake_GATTAPI.h +++ b/tests/fakes/fake_GATTAPI.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_GATTAPI_test_vectors.c b/tests/fakes/fake_GATTAPI_test_vectors.c index d5ac9ae3e9..f68e494990 100644 --- a/tests/fakes/fake_GATTAPI_test_vectors.c +++ b/tests/fakes/fake_GATTAPI_test_vectors.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_GATTAPI_test_vectors.h" #include "fake_GATTAPI.h" diff --git a/tests/fakes/fake_GATTAPI_test_vectors.h b/tests/fakes/fake_GATTAPI_test_vectors.h index 804df456cd..280d772c2d 100644 --- a/tests/fakes/fake_GATTAPI_test_vectors.h +++ b/tests/fakes/fake_GATTAPI_test_vectors.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_HCIAPI.c b/tests/fakes/fake_HCIAPI.c index 38097d3fbd..a5fee601c9 100644 --- a/tests/fakes/fake_HCIAPI.c +++ b/tests/fakes/fake_HCIAPI.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_HCIAPI.h" diff --git a/tests/fakes/fake_HCIAPI.h b/tests/fakes/fake_HCIAPI.h index 1dbb3c5b69..58df07aa56 100644 --- a/tests/fakes/fake_HCIAPI.h +++ b/tests/fakes/fake_HCIAPI.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_accel_service.c b/tests/fakes/fake_accel_service.c index f35fb61b61..fd86459e46 100644 --- a/tests/fakes/fake_accel_service.c +++ b/tests/fakes/fake_accel_service.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_accel_service.h" @@ -20,6 +7,7 @@ #include #include "applib/accel_service_private.h" +#include "pbl/services/accel_manager.h" #include "system/passert.h" static AccelDataHandler s_handler; @@ -107,3 +95,7 @@ int accel_session_set_samples_per_update(AccelServiceState *session, uint32_t sa } +void accel_manager_set_motion_backlight_enabled(bool enabled) { +} + + diff --git a/tests/fakes/fake_accel_service.h b/tests/fakes/fake_accel_service.h index b390d67259..75393cde3a 100644 --- a/tests/fakes/fake_accel_service.h +++ b/tests/fakes/fake_accel_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_accessory.c b/tests/fakes/fake_accessory.c deleted file mode 100644 index d3c3dd1d27..0000000000 --- a/tests/fakes/fake_accessory.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fake_accessory.h" - -#include "clar_asserts.h" - -#include "services/normal/accessory/smartstrap_comms.h" - -#include - -#define BUFFER_LENGTH 200 - -static uint8_t s_buffer[BUFFER_LENGTH]; -static int s_buffer_index = 0; -static bool s_did_send_byte = false; - -void accessory_disable_input(void) { -} - -void accessory_enable_input(void) { -} - -void accessory_use_dma(bool use_dma) { -} - -void accessory_send_byte(uint8_t data) { - cl_assert(s_buffer_index < BUFFER_LENGTH); - s_buffer[s_buffer_index++] = data; - s_did_send_byte = true; -} - -void accessory_send_stream(AccessoryDataStreamCallback callback, void *context) { - s_buffer_index = 0; - memset(s_buffer, 0, BUFFER_LENGTH); - while (callback(context)) { - cl_assert(s_did_send_byte); - } -} - -void fake_accessory_get_buffer(uint8_t **buffer, int *length) { - *buffer = s_buffer; - *length = s_buffer_index; -} diff --git a/tests/fakes/fake_accessory.h b/tests/fakes/fake_accessory.h deleted file mode 100644 index 4127ace79d..0000000000 --- a/tests/fakes/fake_accessory.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -void accessory_enable_input(void); -void accessory_disable_input(void); - -void accessory_use_dma(bool use_dma); - -void accessory_send_byte(uint8_t data); - -typedef bool (*AccessoryDataStreamCallback)(void *context); -void accessory_send_stream(AccessoryDataStreamCallback callback, void *context); - -void fake_accessory_get_buffer(uint8_t **buffer, int *length); diff --git a/tests/fakes/fake_aes.c b/tests/fakes/fake_aes.c index bad73f8cc7..baf161a7ae 100644 --- a/tests/fakes/fake_aes.c +++ b/tests/fakes/fake_aes.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "aes.h" diff --git a/tests/fakes/fake_animation.c b/tests/fakes/fake_animation.c index c3c79602b9..58ddd79fe7 100644 --- a/tests/fakes/fake_animation.c +++ b/tests/fakes/fake_animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // Need to use the real struct because the product code accesses the structure directly. It would // be nice to instead create a dummy with only the fields we need, but oh well. @@ -147,7 +134,7 @@ uint32_t animation_get_duration(Animation *animation, bool include_delay, bool i return ((AnimationPrivate *)animation)->duration_ms; } -static void prv_call_started(AnimationPrivate *animation, uintptr_t UNUSED context) { +static void prv_call_started(AnimationPrivate *animation, uintptr_t PBL_UNUSED context) { if (animation->implementation && animation->implementation->setup) { animation->implementation->setup((Animation *)animation); } diff --git a/tests/fakes/fake_animation.h b/tests/fakes/fake_animation.h index ef027c6854..847639fb02 100644 --- a/tests/fakes/fake_animation.h +++ b/tests/fakes/fake_animation.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! @file fake_animation.h //! diff --git a/tests/fakes/fake_app_malloc.h b/tests/fakes/fake_app_malloc.h index 8653842157..d372919243 100644 --- a/tests/fakes/fake_app_malloc.h +++ b/tests/fakes/fake_app_malloc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_app_manager.h b/tests/fakes/fake_app_manager.h index 68e7167d56..8dabe6dcf6 100644 --- a/tests/fakes/fake_app_manager.h +++ b/tests/fakes/fake_app_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_app_state.h b/tests/fakes/fake_app_state.h index a778a7ff17..f3aa23e054 100644 --- a/tests/fakes/fake_app_state.h +++ b/tests/fakes/fake_app_state.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_graphics_context.h" diff --git a/tests/fakes/fake_app_timer.h b/tests/fakes/fake_app_timer.h index 455f817514..5717f6655f 100644 --- a/tests/fakes/fake_app_timer.h +++ b/tests/fakes/fake_app_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -122,7 +109,7 @@ static void prv_unlink_and_free_timer(FakeAppTimer *timer) { prev_timer = prev_timer->next; } if (!prev_timer) { - PBL_LOG(LOG_LEVEL_ERROR, "Tried to unlink and free non-existing timer %p", timer); + PBL_LOG_ERR("Tried to unlink and free non-existing timer %p", timer); return; } prev_timer->next = timer->next; diff --git a/tests/fakes/fake_applib_resource.c b/tests/fakes/fake_applib_resource.c index 14206c37d6..1436d41a3d 100644 --- a/tests/fakes/fake_applib_resource.c +++ b/tests/fakes/fake_applib_resource.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/applib_resource_private.h" #include "fake_resource_syscalls.h" diff --git a/tests/fakes/fake_battery.c b/tests/fakes/fake_battery.c index 784143d7be..2121febef8 100644 --- a/tests/fakes/fake_battery.c +++ b/tests/fakes/fake_battery.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_battery.h" #include "kernel/events.h" diff --git a/tests/fakes/fake_battery.h b/tests/fakes/fake_battery.h index d794187c62..6b97124ca5 100644 --- a/tests/fakes/fake_battery.h +++ b/tests/fakes/fake_battery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_blobdb.c b/tests/fakes/fake_blobdb.c index e72f482f83..25e91d21fe 100644 --- a/tests/fakes/fake_blobdb.c +++ b/tests/fakes/fake_blobdb.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar_asserts.h" @@ -20,7 +7,7 @@ #include #include "test_db.h" -#include "services/normal/blob_db/api.h" +#include "pbl/services/blob_db/api.h" static BlobDBId s_blobdb_id = BlobDBIdTest; diff --git a/tests/fakes/fake_blobdb.h b/tests/fakes/fake_blobdb.h index 1eaac00a40..812fea20f2 100644 --- a/tests/fakes/fake_blobdb.h +++ b/tests/fakes/fake_blobdb.h @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/api.h" +#include "pbl/services/blob_db/api.h" void fake_blob_db_set_id(BlobDBId id); diff --git a/tests/fakes/fake_bluetooth_persistent_storage.c b/tests/fakes/fake_bluetooth_persistent_storage.c index 919d962433..2b33960e66 100644 --- a/tests/fakes/fake_bluetooth_persistent_storage.c +++ b/tests/fakes/fake_bluetooth_persistent_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_bluetooth_persistent_storage.h" diff --git a/tests/fakes/fake_bluetooth_persistent_storage.h b/tests/fakes/fake_bluetooth_persistent_storage.h index 34f1959877..40a6e2a88a 100644 --- a/tests/fakes/fake_bluetooth_persistent_storage.h +++ b/tests/fakes/fake_bluetooth_persistent_storage.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" void fake_bt_persistent_storage_reset(void); diff --git a/tests/fakes/fake_bonding_sync.h b/tests/fakes/fake_bonding_sync.h index ec0f05eafb..71946a8165 100644 --- a/tests/fakes/fake_bonding_sync.h +++ b/tests/fakes/fake_bonding_sync.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -68,10 +55,14 @@ bool bonding_sync_contains_pairing_info(const SMPairingInfo *pairing_info, bool } void bt_driver_handle_host_removed_bonding(const BleBonding *bonding) { + // Match the qemu/stub driver behavior: removing a bonding the driver was never told about is a + // no-op rather than a fault. Production code may issue a remove even when the driver-side state + // was never populated (e.g. cleaning up stale entries at boot). BLEBondingNode *found_node = (BLEBondingNode *)list_find((ListNode *)s_ble_bonding_head, prv_list_find_cb, (void *)bonding); - PBL_ASSERTN(found_node); - prv_remove_node(found_node); + if (found_node) { + prv_remove_node(found_node); + } } void bonding_sync_init(void) { diff --git a/tests/fakes/fake_bootbits.c b/tests/fakes/fake_bootbits.c index 6a56ee3c75..ede28a9fd3 100644 --- a/tests/fakes/fake_bootbits.c +++ b/tests/fakes/fake_bootbits.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fakes/fake_bootbits.h b/tests/fakes/fake_bootbits.h index ed74acc941..e89b621c71 100644 --- a/tests/fakes/fake_bootbits.h +++ b/tests/fakes/fake_bootbits.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_bt_driver_advert.c b/tests/fakes/fake_bt_driver_advert.c new file mode 100644 index 0000000000..93a3e7b316 --- /dev/null +++ b/tests/fakes/fake_bt_driver_advert.c @@ -0,0 +1,103 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "fake_bt_driver_advert.h" + +#include + +#include + +#include "clar_asserts.h" + +// Simulated state of the BT controller, as driven through the bt_driver_advert +// contract by gap_le_advert.c. +static bool s_is_advertising_enabled; +static uint32_t s_min_advertising_interval_ms; +static uint32_t s_max_advertising_interval_ms; + +static Advertising_Data_t s_ad_data; +static unsigned int s_ad_data_length; + +static Scan_Response_Data_t s_scan_resp_data; +static unsigned int s_scan_resp_data_length; + +void fake_bt_driver_advert_init(void) { + s_is_advertising_enabled = false; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; + memset(&s_ad_data, 0, sizeof(s_ad_data)); + s_ad_data_length = 0; + memset(&s_scan_resp_data, 0, sizeof(s_scan_resp_data)); + s_scan_resp_data_length = 0; +} + +void gap_le_set_advertising_disabled(void) { + // Simulate the controller stopping advertising on an inbound connection, + // without touching the configured payload. + s_is_advertising_enabled = false; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; +} + +// -- bt_driver_advert contract ------------------------------------------------- + +bool bt_driver_advert_advertising_enable(uint32_t min_interval_ms, uint32_t max_interval_ms) { + s_is_advertising_enabled = true; + s_min_advertising_interval_ms = min_interval_ms; + s_max_advertising_interval_ms = max_interval_ms; + return true; +} + +void bt_driver_advert_advertising_disable(void) { + s_is_advertising_enabled = false; + s_min_advertising_interval_ms = 0; + s_max_advertising_interval_ms = 0; +} + +bool bt_driver_advert_set_advertising_data(const BLEAdData *ad_data) { + if (!ad_data) { + return false; + } + // The payload concatenates the ad data and the scan response data; split them + // back out the way the controller would receive them separately. + memcpy(&s_ad_data, ad_data->data, ad_data->ad_data_length); + s_ad_data_length = ad_data->ad_data_length; + memcpy(&s_scan_resp_data, ad_data->data + ad_data->ad_data_length, + ad_data->scan_resp_data_length); + s_scan_resp_data_length = ad_data->scan_resp_data_length; + return true; +} + +bool bt_driver_advert_client_get_tx_power(int8_t *tx_power) { + // No tx-power source in the fake; gap_le_advert keeps its cached value. + return false; +} + +// -- test accessors ------------------------------------------------------------ + +bool gap_le_is_advertising_enabled(void) { + return s_is_advertising_enabled; +} + +// Expected ms values for each interval preset, matching s_interval_ms in +// gap_le_advert.c. +static const uint32_t s_expected_interval_ms[] = { + [GAPLEAdvertisingInterval_Short] = 20, + [GAPLEAdvertisingInterval_Long] = 1022, +}; + +void gap_le_assert_advertising_interval(GAPLEAdvertisingInterval expected) { + const uint32_t expected_ms = s_expected_interval_ms[expected]; + cl_assert_equal_i(s_min_advertising_interval_ms, expected_ms); + cl_assert_equal_i(s_max_advertising_interval_ms, expected_ms); +} + +unsigned int gap_le_get_advertising_data(Advertising_Data_t *ad_data_out) { + *ad_data_out = s_ad_data; + return s_ad_data_length; +} + +unsigned int gap_le_get_scan_response_data(Scan_Response_Data_t *scan_resp_data_out) { + *scan_resp_data_out = s_scan_resp_data; + return s_scan_resp_data_length; +} diff --git a/tests/fakes/fake_bt_driver_advert.h b/tests/fakes/fake_bt_driver_advert.h new file mode 100644 index 0000000000..2259914ace --- /dev/null +++ b/tests/fakes/fake_bt_driver_advert.h @@ -0,0 +1,42 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include "comm/ble/gap_le_advert.h" + +#include +#include + +//! Byte buffers mirroring what the controller would hold. Sized to the maximum +//! advertising report length, matching GAP_LE_AD_REPORT_DATA_MAX_LENGTH. +typedef struct { + uint8_t data[GAP_LE_AD_REPORT_DATA_MAX_LENGTH]; +} Advertising_Data_t; + +typedef struct { + uint8_t data[GAP_LE_AD_REPORT_DATA_MAX_LENGTH]; +} Scan_Response_Data_t; + +//! Resets all simulated controller state. Call from test initialize(). +void fake_bt_driver_advert_init(void); + +//! Simulates the controller dropping advertising due to an inbound connection, +//! without clearing the configured ad/scan-response payload. +void gap_le_set_advertising_disabled(void); + +//! True iff the simulated controller currently has advertising enabled. +bool gap_le_is_advertising_enabled(void); + +//! Asserts the advertising interval last programmed into the controller matches +//! the ms value for the given GAPLEAdvertisingInterval preset. +void gap_le_assert_advertising_interval(GAPLEAdvertisingInterval expected); + +//! Copies out the advertising payload last programmed into the controller. +//! @return the length, in bytes, of the advertising payload. +unsigned int gap_le_get_advertising_data(Advertising_Data_t *ad_data_out); + +//! Copies out the scan-response payload last programmed into the controller. +//! @return the length, in bytes, of the scan-response payload. +unsigned int gap_le_get_scan_response_data(Scan_Response_Data_t *scan_resp_data_out); diff --git a/tests/fakes/fake_bt_driver_gatt.c b/tests/fakes/fake_bt_driver_gatt.c new file mode 100644 index 0000000000..978e851f66 --- /dev/null +++ b/tests/fakes/fake_bt_driver_gatt.c @@ -0,0 +1,415 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "fake_bt_driver_gatt.h" + +#include +#include +#include "comm/ble/gap_le_connection.h" + +#include "kernel/pbl_malloc.h" + +#include "clar_asserts.h" + +#include +#include + +#include + +// 16-bit UUID of the GATT "Service Changed" characteristic (BT assigned numbers). +#define GATT_SERVICE_CHANGED_CHARACTERISTIC_UUID (0x2a05) + +// A long-ish nominal watchdog period; the value is irrelevant to the tests, +// which fire the timer explicitly through stub_new_timer_fire. +#define WATCHDOG_TIMEOUT_MS (10000) + +// Simulated discovery state, driven through the bt_driver_gatt contract by +// gatt_client_discovery.c. +static bool s_is_discovery_running; +static int s_start_count; +static int s_stop_count; +// Controller error codes (0 == success). Surfaced into the BTErrno space below. +static int s_start_ret_code; +static int s_stop_ret_code; + +// The simulated controller's discovery watchdog. Created lazily and re-armed +// each time a discovery is (re)started, so firing it reports a timeout against +// the connection currently being discovered. +static TimerID s_watchdog_timer = TIMER_INVALID_ID; +static GAPLEConnection *s_watchdog_connection; + +// Service Changed indications the firmware has pushed to the controller: a count +// plus a faithful copy of the last call's arguments. +static int s_service_changed_indication_count; +static BTDeviceInternal s_service_changed_last_device; +static ATTHandleRange s_service_changed_last_range; + +static BTErrno prv_code_to_bterrno(int code) { + return (code == 0) ? BTErrnoOK : (BTErrno)BTErrnoWithBluetopiaError(code); +} + +static void prv_watchdog_timeout_cb(void *data) { + // The controller reports the in-flight discovery as timed out. + bt_driver_cb_gatt_client_discovery_complete(s_watchdog_connection, + BTErrnoServiceDiscoveryTimeout); +} + +static void prv_arm_watchdog(GAPLEConnection *connection) { + if (s_watchdog_timer == TIMER_INVALID_ID) { + s_watchdog_timer = new_timer_create(); + } + s_watchdog_connection = connection; + new_timer_start(s_watchdog_timer, WATCHDOG_TIMEOUT_MS, prv_watchdog_timeout_cb, NULL, 0); +} + +// Frees the watchdog timer so no allocation outlives the discovery it guards. +static void prv_disarm_watchdog(void) { + if (s_watchdog_timer != TIMER_INVALID_ID) { + new_timer_delete(s_watchdog_timer); + s_watchdog_timer = TIMER_INVALID_ID; + } +} + +// -- bt_driver_gatt discovery contract ---------------------------------------- + +BTErrno bt_driver_gatt_start_discovery_range(const GAPLEConnection *connection, + const ATTHandleRange *data) { + ++s_start_count; + if (s_start_ret_code != 0) { + return prv_code_to_bterrno(s_start_ret_code); + } + s_is_discovery_running = true; + prv_arm_watchdog((GAPLEConnection *)connection); + return BTErrnoOK; +} + +BTErrno bt_driver_gatt_stop_discovery(GAPLEConnection *connection) { + ++s_stop_count; + if (s_stop_ret_code != 0) { + return prv_code_to_bterrno(s_stop_ret_code); + } + s_is_discovery_running = false; + prv_disarm_watchdog(); + return BTErrnoOK; +} + +void bt_driver_gatt_handle_discovery_abandoned(void) { + s_is_discovery_running = false; + prv_disarm_watchdog(); +} + +void bt_driver_gatt_respond_read_subscription(uint32_t transaction_id, uint16_t response_code) { + // The response is consumed by the controller; nothing observes it in tests. +} + +void bt_driver_gatt_send_changed_indication(const BTDeviceInternal *device, + const ATTHandleRange *data) { + ++s_service_changed_indication_count; + s_service_changed_last_device = *device; + s_service_changed_last_range = *data; +} + +TimerID bt_driver_gatt_get_watchdog_timer_id(void) { + return s_watchdog_timer; +} + +int fake_gatt_get_service_changed_indication_count(void) { + return s_service_changed_indication_count; +} + +const BTDeviceInternal *fake_gatt_get_service_changed_last_device(void) { + return &s_service_changed_last_device; +} + +ATTHandleRange fake_gatt_get_service_changed_last_range(void) { + return s_service_changed_last_range; +} + +// -- test accessors ----------------------------------------------------------- + +bool fake_gatt_is_service_discovery_running(void) { + return s_is_discovery_running; +} + +int fake_gatt_is_service_discovery_start_count(void) { + return s_start_count; +} + +int fake_gatt_is_service_discovery_stop_count(void) { + return s_stop_count; +} + +void fake_gatt_set_start_return_value(int ret_value) { + s_start_ret_code = ret_value; +} + +void fake_gatt_set_stop_return_value(int ret_value) { + s_stop_ret_code = ret_value; +} + +void fake_gatt_init(void) { + s_is_discovery_running = false; + s_start_count = 0; + s_stop_count = 0; + s_start_ret_code = 0; + s_stop_ret_code = 0; + prv_disarm_watchdog(); + s_watchdog_connection = NULL; + s_service_changed_indication_count = 0; + s_service_changed_last_device = (BTDeviceInternal){0}; + s_service_changed_last_range = (ATTHandleRange){0}; +} + +// -- discovery event injection ------------------------------------------------ + +// Builds the packed GATTService blob the firmware expects from the high-level +// Service description, mirroring the conversion the real driver performs, then +// pushes it through bt_driver_cb_gatt_client_discovery_handle_indication. The +// firmware takes ownership of the heap blob. +void fake_gatt_put_discovery_indication_service(unsigned int connection_id, + const Service *service) { + // Once the connection is gone (e.g. a disconnect raced the indication) the + // driver no longer has a valid GAPLEConnection to hand the firmware, so it + // drops the late indication rather than delivering a dangling pointer. + GAPLEConnection *connection = gap_le_connection_by_gatt_id(connection_id); + if (connection == NULL || !s_is_discovery_running) { + return; + } + + uint8_t num_descriptors = 0; + for (uint8_t c = 0; c < service->num_characteristics; ++c) { + num_descriptors += service->characteristics[c].num_descriptors; + } + + const size_t size_bytes = COMPUTE_GATTSERVICE_SIZE_BYTES( + service->num_characteristics, num_descriptors, service->num_included_services); + GATTService *blob = kernel_zalloc_check(size_bytes); + + blob->uuid = service->uuid; + blob->size_bytes = size_bytes; + blob->att_handle = service->handle; + blob->num_characteristics = service->num_characteristics; + blob->num_descriptors = num_descriptors; + blob->num_att_handles_included_services = service->num_included_services; + + // Characteristics, each immediately followed by its descriptors. + uint8_t *end_ptr = (uint8_t *)blob->characteristics; + for (uint8_t c = 0; c < service->num_characteristics; ++c) { + const Characteristic *src = &service->characteristics[c]; + GATTCharacteristic *dst = (GATTCharacteristic *)end_ptr; + *dst = (GATTCharacteristic){ + .uuid = src->uuid, + .att_handle_offset = src->handle - service->handle, + .properties = src->properties, + .num_descriptors = src->num_descriptors, + }; + for (uint8_t d = 0; d < src->num_descriptors; ++d) { + dst->descriptors[d] = (GATTDescriptor){ + .uuid = src->descriptors[d].uuid, + .att_handle_offset = src->descriptors[d].handle - service->handle, + }; + } + end_ptr += sizeof(GATTCharacteristic) + sizeof(GATTDescriptor) * src->num_descriptors; + } + + // Included service ATT handles are tacked on after the last characteristic. + uint16_t *included = (uint16_t *)end_ptr; + for (uint8_t i = 0; i < service->num_included_services; ++i) { + included[i] = service->included_services[i]->handle; + } + + bt_driver_cb_gatt_client_discovery_handle_indication(connection, blob, BTErrnoOK); + + // Mirror the driver: when the discovered service carries the GATT "Service + // Changed" characteristic, the driver subscribes to it and reports its handle + // to the firmware so future Service Changed indications can be matched. + const Uuid service_changed_uuid = bt_uuid_expand_16bit(GATT_SERVICE_CHANGED_CHARACTERISTIC_UUID); + for (uint8_t c = 0; c < service->num_characteristics; ++c) { + if (uuid_equal(&service->characteristics[c].uuid, &service_changed_uuid)) { + bt_driver_cb_gatt_client_discovery_handle_service_changed( + connection, service->characteristics[c].handle); + } + } +} + +void fake_gatt_put_discovery_complete_event(uint8_t status, unsigned int connection_id) { + cl_assert_equal_b(s_is_discovery_running, true); + s_is_discovery_running = false; + prv_disarm_watchdog(); + + GAPLEConnection *connection = gap_le_connection_by_gatt_id(connection_id); + cl_assert(connection != NULL); + + // A non-success status is surfaced as a discovery error in the BTErrno space, + // which the firmware forwards verbatim into the client event. + const BTErrno errno = prv_code_to_bterrno(status); + bt_driver_cb_gatt_client_discovery_complete(connection, errno); +} + +// -- reference service descriptions ------------------------------------------- + +static Service s_health_thermometer_service; + +const Service *fake_gatt_get_health_thermometer_service(void) { + s_health_thermometer_service = (const Service){ + .uuid = bt_uuid_expand_16bit(0x1809), + .handle = 0x11, + .num_characteristics = 1, + .characteristics = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2a1c), + .properties = 0x02, + .handle = 0x13, + .num_descriptors = 1, + .descriptors = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2902), + .handle = 0x15, + }, + }, + }, + }, + }; + return &s_health_thermometer_service; +} + +void fake_gatt_put_discovery_indication_health_thermometer_service(unsigned int connection_id) { + fake_gatt_put_discovery_indication_service(connection_id, + fake_gatt_get_health_thermometer_service()); +} + +static Service s_blood_pressure_service; +#define BP_START_ATT_HANDLE 0x1 +#define BP_END_ATT_HANDLE 0x9 + +const Service *fake_gatt_get_blood_pressure_service(void) { + // Ensure the included Health Thermometer reference is populated. + fake_gatt_get_health_thermometer_service(); + s_blood_pressure_service = (const Service){ + .uuid = bt_uuid_expand_16bit(0x1810), + .handle = BP_START_ATT_HANDLE, + .num_characteristics = 2, + .characteristics = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2a35), + .properties = 0x20, // Indicatable + .handle = 0x3, + .num_descriptors = 1, + .descriptors = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2902), + .handle = 0x05, + }, + }, + }, + [1] = { + .uuid = bt_uuid_expand_16bit(0x2a49), + .properties = 0x02, + .handle = 0x7, + .num_descriptors = 1, + .descriptors = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2902), + .handle = BP_END_ATT_HANDLE, + }, + }, + }, + }, + .num_included_services = 1, + .included_services = { + [0] = &s_health_thermometer_service, + }, + }; + return &s_blood_pressure_service; +} + +void fake_gatt_put_discovery_indication_blood_pressure_service(unsigned int connection_id) { + fake_gatt_put_discovery_indication_service(connection_id, + fake_gatt_get_blood_pressure_service()); +} + +void fake_gatt_get_bp_att_handle_range(uint16_t *start, uint16_t *end) { + *start = BP_START_ATT_HANDLE; + *end = BP_END_ATT_HANDLE; +} + +static Service s_random_128bit_service; + +const Service *fake_gatt_get_random_128bit_uuid_service(void) { + s_random_128bit_service = (const Service){ + .uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, + 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB0), + .handle = 0x17, + .num_characteristics = 2, + .characteristics = { + [0] = { + .uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, + 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB1), + .properties = 0x02, + .handle = 0x19, + .num_descriptors = 1, + .descriptors = { + [0] = { + .uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, + 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB2), + .handle = 0x21, + }, + }, + }, + [1] = { + .uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, + 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB3), + .properties = 0x02, + .handle = 0x23, + .num_descriptors = 1, + .descriptors = { + [0] = { + .uuid = UuidMake(0xF7, 0x68, 0x09, 0x5B, 0x1B, 0xFA, 0x4F, 0x63, + 0x97, 0xEE, 0xFD, 0xED, 0xAC, 0x66, 0xF9, 0xB4), + .handle = 0x25, + }, + }, + }, + }, + }; + return &s_random_128bit_service; +} + +void fake_gatt_put_discovery_indication_random_128bit_uuid_service(unsigned int connection_id) { + fake_gatt_put_discovery_indication_service(connection_id, + fake_gatt_get_random_128bit_uuid_service()); +} + +static Service s_gatt_profile_service; + +void fake_gatt_put_discovery_indication_gatt_profile_service( + unsigned int connection_id, bool has_service_changed_characteristic) { + s_gatt_profile_service = (const Service){ + .uuid = bt_uuid_expand_16bit(0x1801), + .handle = 0x1, + .num_characteristics = has_service_changed_characteristic ? 1 : 0, + .characteristics = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2a05), + .properties = 0x20, + .handle = 0x3, + .num_descriptors = 1, + .descriptors = { + [0] = { + .uuid = bt_uuid_expand_16bit(0x2902), + .handle = 0x05, + }, + }, + }, + }, + }; + fake_gatt_put_discovery_indication_service(connection_id, &s_gatt_profile_service); +} + +uint16_t fake_gatt_gatt_profile_service_service_changed_att_handle(void) { + return 3; // .handle = 0x3 +} + +uint16_t fake_gatt_gatt_profile_service_service_changed_cccd_att_handle(void) { + return 5; // descriptor .handle = 0x05 +} diff --git a/tests/fakes/fake_bt_driver_gatt.h b/tests/fakes/fake_bt_driver_gatt.h new file mode 100644 index 0000000000..8d8d2d6dda --- /dev/null +++ b/tests/fakes/fake_bt_driver_gatt.h @@ -0,0 +1,126 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include +#include +#include "pbl/services/new_timer/new_timer.h" +#include "util/uuid.h" + +#include +#include + +//! High-level description of a remote GATT service used by the unit tests. The +//! fake converts these into the packed GATTService blobs the firmware expects +//! and pushes them through the bt_driver_cb_gatt_client_discovery_* contract. +typedef struct { + Uuid uuid; + uint16_t handle; +} Descriptor; + +typedef struct { + Uuid uuid; + uint8_t properties; + uint16_t handle; + uint8_t num_descriptors; + Descriptor descriptors[3]; +} Characteristic; + +typedef struct Service { + Uuid uuid; + uint16_t handle; + uint8_t num_characteristics; + Characteristic characteristics[3]; + uint8_t num_included_services; + struct Service *included_services[2]; +} Service; + +//! Status codes the tests pass to fake_gatt_put_discovery_complete_event. They +//! mirror the Bluetopia discovery status values the firmware was originally +//! tested against; the fake maps them onto the bt_driver BTErrno contract. +#define GATT_SERVICE_DISCOVERY_STATUS_SUCCESS (0x00) +#define GATT_SERVICE_DISCOVERY_STATUS_RESPONSE_TIMEOUT (0x01) + +//! An opaque controller-level GATT error code. The driver surfaces it into the +//! BTErrno space (via BTErrnoWithBluetopiaError) when returned from a discovery +//! start/stop, just as the real driver maps internal error codes. +#define BTGATT_ERROR_INVALID_PARAMETER (0x07) + +//! Resets all simulated discovery state. Call from test initialize(). +void fake_gatt_init(void); + +//! @return true while a discovery started through bt_driver_gatt_start_discovery_range +//! has not yet completed. +bool fake_gatt_is_service_discovery_running(void); + +//! @return the number of bt_driver_gatt_start_discovery_range calls so far. +int fake_gatt_is_service_discovery_start_count(void); + +//! @return the number of bt_driver_gatt_stop_discovery calls so far. +int fake_gatt_is_service_discovery_stop_count(void); + +//! Sets the controller error code that bt_driver_gatt_start_discovery_range +//! reports. A non-zero code is surfaced as BTErrnoWithBluetopiaError(code); zero +//! means success (BTErrnoOK). +void fake_gatt_set_start_return_value(int ret_value); + +//! As fake_gatt_set_start_return_value, but for bt_driver_gatt_stop_discovery. +void fake_gatt_set_stop_return_value(int ret_value); + +//! @return the TimerID of the simulated discovery watchdog. Firing it (via +//! stub_new_timer_fire) makes the controller report a discovery timeout. +TimerID bt_driver_gatt_get_watchdog_timer_id(void); + +//! @return the number of Service Changed indications the firmware has pushed to +//! the controller through bt_driver_gatt_send_changed_indication. +int fake_gatt_get_service_changed_indication_count(void); + +//! @return the device from the most recent bt_driver_gatt_send_changed_indication call. +const BTDeviceInternal *fake_gatt_get_service_changed_last_device(void); + +//! @return the ATT handle range from the most recent bt_driver_gatt_send_changed_indication call. +ATTHandleRange fake_gatt_get_service_changed_last_range(void); + +//! Feeds a single discovered service to the firmware, as the driver would. +void fake_gatt_put_discovery_indication_service(unsigned int connection_id, + const Service *service); + +//! Simulates the driver reporting service discovery completion with the given +//! status (see GATT_SERVICE_DISCOVERY_STATUS_*). +void fake_gatt_put_discovery_complete_event(uint8_t status, unsigned int connection_id); + +// Health Thermometer Service 0x1809 : 0x11 +// Temperature Measurement 0x2a1c : 0x13 (properties=0x02) +// CCCD 0x2902 : 0x15 +void fake_gatt_put_discovery_indication_health_thermometer_service(unsigned int connection_id); +const Service *fake_gatt_get_health_thermometer_service(void); + +// Blood Pressure Service 0x1810 : 0x01 +// Pressure Characteristic 0x2a35 : 0x03 (properties=0x20) +// CCCD 0x2902 : 0x05 +// Feature Characteristic 0x2a49 : 0x07 (properties=0x02) +// CCCD 0x2902 : 0x09 +// Included Services : Points to the fake Health Thermometer Service +void fake_gatt_put_discovery_indication_blood_pressure_service(unsigned int connection_id); +const Service *fake_gatt_get_blood_pressure_service(void); + +// Service F768095B-1BFA-4F63-97EE-FDEDAC66F9B0 : 0x17 +// Char1 F768095B-1BFA-4F63-97EE-FDEDAC66F9B1 : 0x19 (properties=0x02) +// Desc1 F768095B-1BFA-4F63-97EE-FDEDAC66F9B2 : 0x21 +// Char2 F768095B-1BFA-4F63-97EE-FDEDAC66F9B3 : 0x23 (properties=0x02) +// Desc2 F768095B-1BFA-4F63-97EE-FDEDAC66F9B4 : 0x25 +void fake_gatt_put_discovery_indication_random_128bit_uuid_service(unsigned int connection_id); +const Service *fake_gatt_get_random_128bit_uuid_service(void); + +//! Returns the starting ATT handle (Service, 0x1) and ending ATT handle (Desc2 0x09) +//! for the BP service. +void fake_gatt_get_bp_att_handle_range(uint16_t *start, uint16_t *end); + +// GATT Profile Service 0x1801 : 0x01 +// Service Changed 0x2a05 : 0x03 (properties=0x20) +// CCCD 0x2902 : 0x05 +void fake_gatt_put_discovery_indication_gatt_profile_service( + unsigned int connection_id, bool has_service_changed_characteristic); +uint16_t fake_gatt_gatt_profile_service_service_changed_att_handle(void); +uint16_t fake_gatt_gatt_profile_service_service_changed_cccd_att_handle(void); diff --git a/tests/fakes/fake_clock.c b/tests/fakes/fake_clock.c index 2a7be4c129..e3ccee22ab 100644 --- a/tests/fakes/fake_clock.c +++ b/tests/fakes/fake_clock.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/i18n/i18n.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/i18n/i18n.h" #include "util/attributes.h" #include "util/math.h" diff --git a/tests/fakes/fake_content_indicator.h b/tests/fakes/fake_content_indicator.h index c834ee2a22..d93ae62278 100644 --- a/tests/fakes/fake_content_indicator.h +++ b/tests/fakes/fake_content_indicator.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_cron.h b/tests/fakes/fake_cron.h index b08c327e91..c4db9feead 100644 --- a/tests/fakes/fake_cron.h +++ b/tests/fakes/fake_cron.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_display.c b/tests/fakes/fake_display.c index a5a50d0f46..f530b10ee9 100644 --- a/tests/fakes/fake_display.c +++ b/tests/fakes/fake_display.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_display.h" @@ -22,6 +9,4 @@ void display_clear(void) {} void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb) {} -void display_enter_static(void) {} - -void display_pulse_vcom(void) {} +void display_enter_static(void) {} \ No newline at end of file diff --git a/tests/fakes/fake_display.h b/tests/fakes/fake_display.h index 749b187596..22ef172512 100644 --- a/tests/fakes/fake_display.h +++ b/tests/fakes/fake_display.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -49,6 +36,4 @@ void display_clear(void); void display_update(NextRowCallback nrcb, UpdateCompleteCallback uccb); -void display_enter_static(void); - -void display_pulse_vcom(void); +void display_enter_static(void); \ No newline at end of file diff --git a/tests/fakes/fake_event_gatt_service_buffer.h b/tests/fakes/fake_event_gatt_service_buffer.h new file mode 100644 index 0000000000..69d6c94cbf --- /dev/null +++ b/tests/fakes/fake_event_gatt_service_buffer.h @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "fake_events.h" +#include "fake_pbl_malloc.h" +#include "kernel/events.h" + +//! Strong override of the WEAK fake_events.c implementation, for tests that +//! exercise the GATT client service-change event (which carries a heap-allocated +//! info pointer that must be freed). +//! +//! The default implementation reads the discriminant from gatt_client.subtype, +//! matching the firmware. That works on-target, but on a 64-bit unit-test host +//! uintptr_t is wider than on-target, so the overlapping gatt_client and +//! gatt_client_service union members place their subtype fields at different +//! offsets and neither member alone reliably identifies the variant. The only +//! robust signal on the host is the info pointer itself: a real service-change +//! event holds a live tracked allocation there, whereas the other gatt_client +//! variants leave that slot NULL or filled with non-pointer data. Free it only +//! when it is a tracked allocation. +//! +//! Include this header in the test's main translation unit (the one that pulls +//! in fake_pbl_malloc.h) so the override sees the same allocation tracking list. +void **fake_event_get_buffer(PebbleEvent *event) { + if (event->type != PEBBLE_BLE_GATT_CLIENT_EVENT) { + return NULL; + } + void **info = (void **)(&event->bluetooth.le.gatt_client_service.info); + if (*info && fake_pbl_malloc_contains(*info)) { + return info; + } + return NULL; +} diff --git a/tests/fakes/fake_event_service.h b/tests/fakes/fake_event_service.h index 0811507df3..8faa2a1fb6 100644 --- a/tests/fakes/fake_event_service.h +++ b/tests/fakes/fake_event_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_evented_timer.h b/tests/fakes/fake_evented_timer.h index 459e310acf..cf898c3946 100644 --- a/tests/fakes/fake_evented_timer.h +++ b/tests/fakes/fake_evented_timer.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#include "services/common/evented_timer.h" +#include "pbl/services/evented_timer.h" typedef struct { uint32_t timeout_ms; diff --git a/tests/fakes/fake_events.c b/tests/fakes/fake_events.c index 79b2894778..8aeb579563 100644 --- a/tests/fakes/fake_events.c +++ b/tests/fakes/fake_events.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_events.h" #include "kernel/pbl_malloc.h" diff --git a/tests/fakes/fake_events.h b/tests/fakes/fake_events.h index a95ff94d4a..104a5b044b 100644 --- a/tests/fakes/fake_events.h +++ b/tests/fakes/fake_events.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_flash_region.c b/tests/fakes/fake_flash_region.c index 67e5585d6b..1076317d36 100644 --- a/tests/fakes/fake_flash_region.c +++ b/tests/fakes/fake_flash_region.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/flash.h" #include "flash_region/flash_region.h" diff --git a/tests/fakes/fake_fonts.c b/tests/fakes/fake_fonts.c index 71604bd2d7..687bdeb045 100644 --- a/tests/fakes/fake_fonts.c +++ b/tests/fakes/fake_fonts.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_fonts.h" @@ -45,6 +32,11 @@ static FontHelper s_font_helpers[] = { {.key = FONT_KEY_LECO_32_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_32_BOLD_NUMBERS}, {.key = FONT_KEY_LECO_36_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_36_BOLD_NUMBERS}, {.key = FONT_KEY_LECO_38_BOLD_NUMBERS, .handle = RESOURCE_ID_LECO_38_BOLD_NUMBERS}, + {.key = FONT_KEY_LECO_42_NUMBERS, .handle = RESOURCE_ID_LECO_42_NUMBERS}, + {.key = FONT_KEY_LECO_28_LIGHT_NUMBERS, .handle = RESOURCE_ID_LECO_28_LIGHT_NUMBERS}, +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) + {.key = FONT_KEY_LECO_60_NUMBERS_AM_PM, .handle = RESOURCE_ID_LECO_60_NUMBERS_AM_PM}, +#endif {.key = FONT_KEY_GOTHIC_14_EMOJI, .handle = RESOURCE_ID_GOTHIC_14_EMOJI}, {.key = FONT_KEY_GOTHIC_18_EMOJI, .handle = RESOURCE_ID_GOTHIC_18_EMOJI}, {.key = FONT_KEY_GOTHIC_24, .handle = RESOURCE_ID_GOTHIC_24}, @@ -54,11 +46,7 @@ static FontHelper s_font_helpers[] = { {.key = FONT_KEY_GOTHIC_28_BOLD, .handle = RESOURCE_ID_GOTHIC_28_BOLD}, {.key = FONT_KEY_GOTHIC_36, .handle = RESOURCE_ID_GOTHIC_36}, {.key = FONT_KEY_GOTHIC_36_BOLD, .handle = RESOURCE_ID_GOTHIC_36_BOLD}, -#if (PLATFORM_SNOWY || PLATFORM_SPALDING) - {.key = FONT_KEY_AGENCY_FB_36_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_36_NUMBERS_AM_PM }, - {.key = FONT_KEY_AGENCY_FB_60_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_60_NUMBERS_AM_PM }, - {.key = FONT_KEY_AGENCY_FB_60_THIN_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_60_THIN_NUMBERS_AM_PM }, -#elif PLATFORM_ROBERT || PLATFORM_OBELIX +#if defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_BOARD_FAMILY_GETAFIX) {.key = FONT_KEY_AGENCY_FB_46_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM }, {.key = FONT_KEY_AGENCY_FB_88_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM }, {.key = FONT_KEY_AGENCY_FB_88_THIN_NUMBERS_AM_PM, .handle = RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM }, diff --git a/tests/fakes/fake_fonts.h b/tests/fakes/fake_fonts.h index 9cbf2ce717..5c514c9bc8 100644 --- a/tests/fakes/fake_fonts.h +++ b/tests/fakes/fake_fonts.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_framebuffer.c b/tests/fakes/fake_framebuffer.c index bcf5f5217f..331dd5b2d8 100644 --- a/tests/fakes/fake_framebuffer.c +++ b/tests/fakes/fake_framebuffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "framebuffer.h" diff --git a/tests/fakes/fake_gap_le.c b/tests/fakes/fake_gap_le.c index 816fab34b5..013e4c092b 100644 --- a/tests/fakes/fake_gap_le.c +++ b/tests/fakes/fake_gap_le.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_gap_le.h" diff --git a/tests/fakes/fake_gap_le.h b/tests/fakes/fake_gap_le.h index 6fc7daee8a..ea04a43ea4 100644 --- a/tests/fakes/fake_gap_le.h +++ b/tests/fakes/fake_gap_le.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_gap_le_connect_params.c b/tests/fakes/fake_gap_le_connect_params.c index f1e781a434..5c1ca1288c 100644 --- a/tests/fakes/fake_gap_le_connect_params.c +++ b/tests/fakes/fake_gap_le_connect_params.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_gap_le_connect_params.h" -#include "GAPAPI.h" +#include "pbl/services/new_timer/new_timer.h" static ResponseTimeState s_last_requested_desired_state; @@ -25,20 +12,12 @@ void gap_le_connect_params_request(GAPLEConnection *connection, s_last_requested_desired_state = desired_state; } -void gap_le_connect_params_setup_connection(GAPLEConnection *connection) { +void gap_le_connect_params_setup_connection(GAPLEConnection *connection, TimerID timer) { } void gap_le_connect_params_cleanup_by_connection(GAPLEConnection *connection) { } -void gap_le_connect_params_handle_update(unsigned int stack_id, - const GAP_LE_Connection_Parameter_Updated_Event_Data_t *event) { -} - -void gap_le_connect_params_handle_connection_parameter_update_response( - const GAP_LE_Connection_Parameter_Update_Response_Event_Data_t *event_data) { -} - static ResponseTimeState s_actual_state; ResponseTimeState gap_le_connect_params_get_actual_state(GAPLEConnection *connection) { return s_actual_state; diff --git a/tests/fakes/fake_gap_le_connect_params.h b/tests/fakes/fake_gap_le_connect_params.h index 45bc6dfa45..1173d974e7 100644 --- a/tests/fakes/fake_gap_le_connect_params.h +++ b/tests/fakes/fake_gap_le_connect_params.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_gatt_client_discovery.c b/tests/fakes/fake_gatt_client_discovery.c index eba06b2b5e..5625acfb09 100644 --- a/tests/fakes/fake_gatt_client_discovery.c +++ b/tests/fakes/fake_gatt_client_discovery.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/gatt_client_discovery.h" diff --git a/tests/fakes/fake_gatt_client_operations.c b/tests/fakes/fake_gatt_client_operations.c index 5878c6ff01..5ce8eb74ca 100644 --- a/tests/fakes/fake_gatt_client_operations.c +++ b/tests/fakes/fake_gatt_client_operations.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_gatt_client_operations.h" diff --git a/tests/fakes/fake_gatt_client_operations.h b/tests/fakes/fake_gatt_client_operations.h index 54bfd83885..fb739f3b99 100644 --- a/tests/fakes/fake_gatt_client_operations.h +++ b/tests/fakes/fake_gatt_client_operations.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_gatt_client_subscriptions.c b/tests/fakes/fake_gatt_client_subscriptions.c index 3bc6b058e6..ff187a646f 100644 --- a/tests/fakes/fake_gatt_client_subscriptions.c +++ b/tests/fakes/fake_gatt_client_subscriptions.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_gatt_client_subscriptions.h" diff --git a/tests/fakes/fake_gatt_client_subscriptions.h b/tests/fakes/fake_gatt_client_subscriptions.h index 742a45b3ff..55a79bb7de 100644 --- a/tests/fakes/fake_gatt_client_subscriptions.h +++ b/tests/fakes/fake_gatt_client_subscriptions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_gbitmap_get_data_row.c b/tests/fakes/fake_gbitmap_get_data_row.c index 0010f3371a..5a6b40cbe3 100644 --- a/tests/fakes/fake_gbitmap_get_data_row.c +++ b/tests/fakes/fake_gbitmap_get_data_row.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_gbitmap_get_data_row.h" diff --git a/tests/fakes/fake_gbitmap_get_data_row.h b/tests/fakes/fake_gbitmap_get_data_row.h index f61f56c20e..af1e6d3279 100644 --- a/tests/fakes/fake_gbitmap_get_data_row.h +++ b/tests/fakes/fake_gbitmap_get_data_row.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_gbitmap_png.c b/tests/fakes/fake_gbitmap_png.c index 03274cfa7b..a79a80fa4c 100644 --- a/tests/fakes/fake_gbitmap_png.c +++ b/tests/fakes/fake_gbitmap_png.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" #include "upng.h" diff --git a/tests/fakes/fake_graphics_context.c b/tests/fakes/fake_graphics_context.c index de714b54f0..8278986a5a 100644 --- a/tests/fakes/fake_graphics_context.c +++ b/tests/fakes/fake_graphics_context.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #define FAKE_GRAPHICS_CONTEXT_C (1) diff --git a/tests/fakes/fake_graphics_context.h b/tests/fakes/fake_graphics_context.h index cd31773c65..056e4dbcfb 100644 --- a/tests/fakes/fake_graphics_context.h +++ b/tests/fakes/fake_graphics_context.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_kernel_malloc.h b/tests/fakes/fake_kernel_malloc.h index 3a111a10f8..1330fda75c 100644 --- a/tests/fakes/fake_kernel_malloc.h +++ b/tests/fakes/fake_kernel_malloc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_kernel_services_notifications.c b/tests/fakes/fake_kernel_services_notifications.c index 7a515f5fc8..794ae0dc42 100644 --- a/tests/fakes/fake_kernel_services_notifications.c +++ b/tests/fakes/fake_kernel_services_notifications.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_kernel_services_notifications.h" -#include "services/normal/notifications/notification_storage.h" +#include "pbl/services/notifications/notification_storage.h" static uint32_t s_ancs_count = 0; static uint32_t s_acted_upon_count = 0; diff --git a/tests/fakes/fake_kernel_services_notifications.h b/tests/fakes/fake_kernel_services_notifications.h index c1e6f34188..aec44a39e4 100644 --- a/tests/fakes/fake_kernel_services_notifications.h +++ b/tests/fakes/fake_kernel_services_notifications.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" void notifications_handle_ancs_message(TimelineItem *notification); diff --git a/tests/fakes/fake_light.c b/tests/fakes/fake_light.c index b2ae356e3a..8dbbe582ce 100644 --- a/tests/fakes/fake_light.c +++ b/tests/fakes/fake_light.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ void light_enable_interaction(void) { } diff --git a/tests/fakes/fake_logging.h b/tests/fakes/fake_logging.h index 14308d9404..6a06b10951 100644 --- a/tests/fakes/fake_logging.h +++ b/tests/fakes/fake_logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_memory_layout.h b/tests/fakes/fake_memory_layout.h index 8c52aa0076..3c7701f558 100644 --- a/tests/fakes/fake_memory_layout.h +++ b/tests/fakes/fake_memory_layout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/mpu.h" diff --git a/tests/fakes/fake_mutex.h b/tests/fakes/fake_mutex.h index 29559f8254..30c13c76e7 100644 --- a/tests/fakes/fake_mutex.h +++ b/tests/fakes/fake_mutex.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_new_timer.h b/tests/fakes/fake_new_timer.h index d856e090b5..56f7b5e65b 100644 --- a/tests/fakes/fake_new_timer.h +++ b/tests/fakes/fake_new_timer.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "fake_pbl_malloc.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "util/list.h" #include "drivers/rtc.h" #include "system/passert.h" diff --git a/tests/fakes/fake_notification_storage.h b/tests/fakes/fake_notification_storage.h index 45d9e1069c..06fc484281 100644 --- a/tests/fakes/fake_notification_storage.h +++ b/tests/fakes/fake_notification_storage.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/timeline/item.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/item.h" static TimelineItem s_last_stored_notification = {}; static int s_notification_store_count = 0; diff --git a/tests/fakes/fake_otp.c b/tests/fakes/fake_otp.c index 22a39f77c8..3e768a6a14 100644 --- a/tests/fakes/fake_otp.c +++ b/tests/fakes/fake_otp.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_otp.h" diff --git a/tests/fakes/fake_otp.h b/tests/fakes/fake_otp.h index caf125185f..953248ab53 100644 --- a/tests/fakes/fake_otp.h +++ b/tests/fakes/fake_otp.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_pbl_malloc.h b/tests/fakes/fake_pbl_malloc.h index 452f87998b..fdb9f13b4d 100644 --- a/tests/fakes/fake_pbl_malloc.h +++ b/tests/fakes/fake_pbl_malloc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -46,6 +33,13 @@ static void prv_pointer_list_add(void *ptr, size_t bytes, void *lr) { s_pointer_list = (PointerListNode *)list_prepend((ListNode *)s_pointer_list, &node->list_node); } +//! @return true iff ptr is currently a live tracked allocation. Useful for +//! safely deciding whether a pointer recovered from a union (where it may +//! overlap non-pointer data) is a real heap allocation that must be freed. +static bool fake_pbl_malloc_contains(void *ptr) { + return list_find((ListNode *)s_pointer_list, prv_pointer_list_filter, ptr) != NULL; +} + static void prv_pointer_list_remove(void *ptr) { ListNode *node = list_find((ListNode *)s_pointer_list, prv_pointer_list_filter, ptr); if (!node && ptr) { diff --git a/tests/fakes/fake_pbl_std.h b/tests/fakes/fake_pbl_std.h index b599ac3709..76be82b727 100644 --- a/tests/fakes/fake_pbl_std.h +++ b/tests/fakes/fake_pbl_std.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "util/time/time.h" diff --git a/tests/fakes/fake_pebble_tasks.h b/tests/fakes/fake_pebble_tasks.h index f9d94027b9..63aa99a62a 100644 --- a/tests/fakes/fake_pebble_tasks.h +++ b/tests/fakes/fake_pebble_tasks.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_property_animation.c b/tests/fakes/fake_property_animation.c index 8b56ba3616..756eb8f43e 100644 --- a/tests/fakes/fake_property_animation.c +++ b/tests/fakes/fake_property_animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/property_animation_private.h" #include "applib/ui/layer.h" diff --git a/tests/fakes/fake_put_bytes_storage_mem.c b/tests/fakes/fake_put_bytes_storage_mem.c index 0795e16797..2aa2ce6076 100644 --- a/tests/fakes/fake_put_bytes_storage_mem.c +++ b/tests/fakes/fake_put_bytes_storage_mem.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_put_bytes_storage_mem.h" #include "clar_asserts.h" #include "kernel/pbl_malloc.h" -#include "services/common/put_bytes/put_bytes_storage_internal.h" +#include "pbl/services/put_bytes/put_bytes_storage_internal.h" #include "system/passert.h" #define FAKE_STORAGE_MAX_SIZE (512 * 1024) diff --git a/tests/fakes/fake_put_bytes_storage_mem.h b/tests/fakes/fake_put_bytes_storage_mem.h index e5d5107f65..0a43e80114 100644 --- a/tests/fakes/fake_put_bytes_storage_mem.h +++ b/tests/fakes/fake_put_bytes_storage_mem.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/put_bytes/put_bytes.h" +#include "pbl/services/put_bytes/put_bytes.h" #include "system/firmware_storage.h" #include diff --git a/tests/fakes/fake_queue.c b/tests/fakes/fake_queue.c index 8431d9fb26..38ded0292a 100644 --- a/tests/fakes/fake_queue.c +++ b/tests/fakes/fake_queue.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_queue.h" diff --git a/tests/fakes/fake_queue.h b/tests/fakes/fake_queue.h index 5bd88a43f5..9548ce7866 100644 --- a/tests/fakes/fake_queue.h +++ b/tests/fakes/fake_queue.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_regular_timer.h b/tests/fakes/fake_regular_timer.h index 78c1efe5b3..29013e9fd7 100644 --- a/tests/fakes/fake_regular_timer.h +++ b/tests/fakes/fake_regular_timer.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" static ListNode s_seconds_callbacks; static ListNode s_minutes_callbacks; diff --git a/tests/fakes/fake_reminder_db.h b/tests/fakes/fake_reminder_db.h index dcc0b7f441..190d0d5af7 100644 --- a/tests/fakes/fake_reminder_db.h +++ b/tests/fakes/fake_reminder_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ bool s_empty = true; diff --git a/tests/fakes/fake_resource_storage.c b/tests/fakes/fake_resource_storage.c index a32cfa4862..60252149ec 100644 --- a/tests/fakes/fake_resource_storage.c +++ b/tests/fakes/fake_resource_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource/resource_storage_flash.h" #include "resource/resource_storage_impl.h" diff --git a/tests/fakes/fake_resource_storage.h b/tests/fakes/fake_resource_storage.h index 7386bd29a9..f8ea510f99 100644 --- a/tests/fakes/fake_resource_storage.h +++ b/tests/fakes/fake_resource_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_resource_syscalls.c b/tests/fakes/fake_resource_syscalls.c index 3442a36f07..b1001d49f5 100644 --- a/tests/fakes/fake_resource_syscalls.c +++ b/tests/fakes/fake_resource_syscalls.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_resource_syscalls.h" diff --git a/tests/fakes/fake_resource_syscalls.h b/tests/fakes/fake_resource_syscalls.h index 623d8f78de..0e0da5ec23 100644 --- a/tests/fakes/fake_resource_syscalls.h +++ b/tests/fakes/fake_resource_syscalls.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_rng.h b/tests/fakes/fake_rng.h index bc5e2d1401..baafe42d00 100644 --- a/tests/fakes/fake_rng.h +++ b/tests/fakes/fake_rng.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_rtc.c b/tests/fakes/fake_rtc.c index 55a087ca98..5f90313ecd 100644 --- a/tests/fakes/fake_rtc.c +++ b/tests/fakes/fake_rtc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_rtc.h" @@ -129,6 +116,7 @@ void fake_rtc_init(RtcTicks initial_ticks, time_t initial_time) { s_rtc_tick_count = initial_ticks; s_time_tick_base = initial_ticks; s_time_base = initial_time; + s_time_ms_base = 0; } void fake_rtc_increment_time(time_t inc) { diff --git a/tests/fakes/fake_rtc.h b/tests/fakes/fake_rtc.h index a2adf4bee0..370e63dba0 100644 --- a/tests/fakes/fake_rtc.h +++ b/tests/fakes/fake_rtc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_session.c b/tests/fakes/fake_session.c index 68d501f883..033aa49123 100644 --- a/tests/fakes/fake_session.c +++ b/tests/fakes/fake_session.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_session.h" @@ -20,9 +7,9 @@ #include "kernel/pbl_malloc.h" #include "system/logging.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/system_task.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/system_task.h" #include "util/circular_buffer.h" #include "system/hexdump.h" @@ -110,7 +97,7 @@ void comm_session_close(CommSession *session, CommSessionCloseReason reason) { void comm_session_receive_router_write(CommSession *session, const uint8_t *received_data, size_t num_bytes_to_copy) { - PBL_LOG(LOG_LEVEL_DEBUG, "Received Data:"); + PBL_LOG_DBG("Received Data:"); PBL_HEXDUMP(LOG_LEVEL_DEBUG, received_data, num_bytes_to_copy); } @@ -353,7 +340,7 @@ static void prv_fake_transport_send_next(Transport *transport) { if (fake_transport->sent_cb) { fake_transport->sent_cb(pp_header.endpoint_id, buffer, pp_header.length); } else { - PBL_LOG(LOG_LEVEL_DEBUG, "Sending Data to PP endpoint %u (0x%x):", + PBL_LOG_DBG("Sending Data to PP endpoint %u (0x%x):", pp_header.endpoint_id, pp_header.endpoint_id); PBL_HEXDUMP(LOG_LEVEL_DEBUG, buffer, pp_header.length); diff --git a/tests/fakes/fake_session.h b/tests/fakes/fake_session.h index 0a8292b661..4b03aafbd8 100644 --- a/tests/fakes/fake_session.h +++ b/tests/fakes/fake_session.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session_transport.h" +#include "pbl/services/comm_session/session_transport.h" // // Usage hints: diff --git a/tests/fakes/fake_session_send_buffer.c b/tests/fakes/fake_session_send_buffer.c index 348d1f1cd3..fa04476892 100644 --- a/tests/fakes/fake_session_send_buffer.c +++ b/tests/fakes/fake_session_send_buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_session_send_buffer.h" diff --git a/tests/fakes/fake_session_send_buffer.h b/tests/fakes/fake_session_send_buffer.h index 54466a2358..04db53a6d3 100644 --- a/tests/fakes/fake_session_send_buffer.h +++ b/tests/fakes/fake_session_send_buffer.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session_send_buffer.h" +#include "pbl/services/comm_session/session_send_buffer.h" #include diff --git a/tests/fakes/fake_settings_file.c b/tests/fakes/fake_settings_file.c index c972a436ff..bf7870a3ba 100644 --- a/tests/fakes/fake_settings_file.c +++ b/tests/fakes/fake_settings_file.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/settings/settings_file.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/settings/settings_file.h" #include "system/status_codes.h" #include "util/crc8.h" @@ -116,6 +103,16 @@ status_t settings_file_open(SettingsFile *file, const char *name, } } +status_t settings_file_open_growable(SettingsFile *file, const char *name, + int max_used_space, int initial_alloc_size) { + return settings_file_open(file, name, max_used_space); +} + +status_t settings_file_compact(SettingsFile *file) { + cl_assert(s_settings_file.open); + return S_SUCCESS; +} + void settings_file_close(SettingsFile *file) { cl_assert(s_settings_file.open); s_settings_file.open = false; diff --git a/tests/fakes/fake_settings_file.h b/tests/fakes/fake_settings_file.h index 9125d235fc..f1e0e2ba3f 100644 --- a/tests/fakes/fake_settings_file.h +++ b/tests/fakes/fake_settings_file.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_shared_prf_storage.c b/tests/fakes/fake_shared_prf_storage.c index 66be55bd6c..37a179121a 100644 --- a/tests/fakes/fake_shared_prf_storage.c +++ b/tests/fakes/fake_shared_prf_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_shared_prf_storage.h" @@ -21,9 +8,6 @@ static int s_prf_storage_ble_store_count; static int s_prf_storage_ble_delete_count; -static int s_prf_storage_bt_classic_store_count; -static int s_prf_storage_bt_classic_platform_bits_count; -static int s_prf_storage_bt_classic_delete_count; /////////////////////////////////////////////////////////////////////////////////////////////////// //! Test functions @@ -31,9 +15,6 @@ static int s_prf_storage_bt_classic_delete_count; void fake_shared_prf_storage_reset_counts(void) { s_prf_storage_ble_store_count = 0; s_prf_storage_ble_delete_count = 0; - s_prf_storage_bt_classic_store_count = 0; - s_prf_storage_bt_classic_platform_bits_count = 0; - s_prf_storage_bt_classic_delete_count = 0; } int fake_shared_prf_storage_get_ble_store_count(void) { @@ -44,18 +25,6 @@ int fake_shared_prf_storage_get_ble_delete_count(void) { return s_prf_storage_ble_delete_count; } -int fake_shared_prf_storage_get_bt_classic_store_count(void) { - return s_prf_storage_bt_classic_store_count; -} - -int fake_shared_prf_storage_get_bt_classic_platform_bits_count(void) { - return s_prf_storage_bt_classic_platform_bits_count; -} - -int fake_shared_prf_storage_get_bt_classic_delete_count(void) { - return s_prf_storage_bt_classic_delete_count; -} - /////////////////////////////////////////////////////////////////////////////////////////////////// //! Custom Local Device Name @@ -113,16 +82,12 @@ void shared_prf_storage_store_bt_classic_pairing_data(BTDeviceAddress *addr, char *device_name, SM128BitKey *link_key, uint8_t platform_bits) { - s_prf_storage_bt_classic_delete_count++; - s_prf_storage_bt_classic_store_count++; } void shared_prf_storage_store_platform_bits(uint8_t platform_bits) { - s_prf_storage_bt_classic_platform_bits_count++; } void shared_prf_storage_erase_bt_classic_pairing_data(void) { - s_prf_storage_bt_classic_delete_count++; } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/fakes/fake_shared_prf_storage.h b/tests/fakes/fake_shared_prf_storage.h index 5158418e92..62f1782a11 100644 --- a/tests/fakes/fake_shared_prf_storage.h +++ b/tests/fakes/fake_shared_prf_storage.h @@ -1,24 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once void fake_shared_prf_storage_reset_counts(void); int fake_shared_prf_storage_get_ble_store_count(void); int fake_shared_prf_storage_get_ble_delete_count(void); -int fake_shared_prf_storage_get_bt_classic_store_count(void); -int fake_shared_prf_storage_get_bt_classic_platform_bits_count(void); -int fake_shared_prf_storage_get_bt_classic_delete_count(void); diff --git a/tests/fakes/fake_smartstrap_connection.c b/tests/fakes/fake_smartstrap_connection.c deleted file mode 100644 index 92b1035668..0000000000 --- a/tests/fakes/fake_smartstrap_connection.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fake_smartstrap_connection.h" - -void smartstrap_connection_kick_monitor(void) { -} - -void sys_smartstrap_subscribe(void) { -} - -void sys_smartstrap_unsubscribe(void) { -} diff --git a/tests/fakes/fake_smartstrap_connection.h b/tests/fakes/fake_smartstrap_connection.h deleted file mode 100644 index fb99455493..0000000000 --- a/tests/fakes/fake_smartstrap_connection.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void smartstrap_connection_kick_monitor(void); -void sys_smartstrap_subscribe(void); -void sys_smartstrap_unsubscribe(void); diff --git a/tests/fakes/fake_smartstrap_profiles.c b/tests/fakes/fake_smartstrap_profiles.c deleted file mode 100644 index fe8d5ead0c..0000000000 --- a/tests/fakes/fake_smartstrap_profiles.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fake_smartstrap_profiles.h" - -#include "clar_asserts.h" - -static bool s_did_read; -static bool s_read_success; -static SmartstrapProfile s_read_profile; -static uint32_t s_read_length; - -static bool s_did_notify; -static bool s_notify_success; -static bool s_notify_profile; - -static bool s_did_request; -static SmartstrapRequest s_request; - - -void smartstrap_profiles_handle_read(bool success, SmartstrapProfile profile, uint32_t length) { - s_read_success = success; - s_read_profile = profile; - s_read_length = length; - s_did_read = true; -} - -void smartstrap_profiles_handle_read_aborted(SmartstrapProfile profile) { -} - -void fake_smartstrap_profiles_check_read_params(bool success, SmartstrapProfile profile, - uint32_t length) { - cl_assert(s_did_read); - cl_assert(success == s_read_success); - cl_assert(profile == s_read_profile); - cl_assert(length == s_read_length); - s_did_read = false; -} - -void smartstrap_profiles_handle_notification(bool success, SmartstrapProfile profile) { - s_notify_success = success; - s_notify_profile = profile; - s_did_notify = true; -} - -void fake_smartstrap_profiles_check_notify_params(bool success, SmartstrapProfile profile) { - cl_assert(s_did_notify); - cl_assert(success = s_notify_success); - cl_assert(profile = s_notify_profile); - s_did_notify = false; -} - -SmartstrapResult smartstrap_profiles_handle_request(const SmartstrapRequest *request) { - s_request = *request; - s_did_request = true; - return SmartstrapResultOk; -} - -void fake_smartstrap_profiles_check_request_params(const SmartstrapRequest *request) { - cl_assert(s_did_request); - cl_assert(s_request.service_id == request->service_id); - cl_assert(s_request.attribute_id == request->attribute_id); - cl_assert((s_request.write_mbuf == NULL) == (request->write_mbuf == NULL)); - cl_assert((s_request.read_mbuf == NULL) == (request->read_mbuf == NULL)); - cl_assert(s_request.timeout_ms == request->timeout_ms); - s_did_request = false; -} diff --git a/tests/fakes/fake_smartstrap_profiles.h b/tests/fakes/fake_smartstrap_profiles.h deleted file mode 100644 index dc2dc51414..0000000000 --- a/tests/fakes/fake_smartstrap_profiles.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "services/normal/accessory/smartstrap_comms.h" - -#include -#include - -void smartstrap_profiles_handle_read(bool success, SmartstrapProfile profile, uint32_t length); - -void fake_smartstrap_profiles_check_read_params(bool success, SmartstrapProfile profile, - uint32_t length); - -void smartstrap_profiles_handle_notification(bool success, SmartstrapProfile profile); - -void fake_smartstrap_profiles_check_notify_params(bool success, SmartstrapProfile profile); - -SmartstrapResult smartstrap_profiles_handle_request(const SmartstrapRequest *request); - -void smartstrap_profiles_handle_read_aborted(SmartstrapProfile profile); - -void fake_smartstrap_profiles_check_request_params(const SmartstrapRequest *request); diff --git a/tests/fakes/fake_smartstrap_state.c b/tests/fakes/fake_smartstrap_state.c deleted file mode 100644 index 4370339ac7..0000000000 --- a/tests/fakes/fake_smartstrap_state.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar_asserts.h" - -#include "fake_smartstrap_state.h" - -static bool s_locked = false; -static SmartstrapState s_fsm_state = SmartstrapStateUnsubscribed; - -static void prv_check_fsm_transition(SmartstrapState prev_state, SmartstrapState new_state) { - if (new_state == SmartstrapStateUnsubscribed) { - } else if ((prev_state == SmartstrapStateUnsubscribed) && - (new_state == SmartstrapStateReadReady)) { - } else if ((prev_state == SmartstrapStateReadReady) && - (new_state == SmartstrapStateNotifyInProgress)) { - } else if ((prev_state == SmartstrapStateReadReady) && - (new_state == SmartstrapStateReadDisabled)) { - } else if ((prev_state == SmartstrapStateNotifyInProgress) && - (new_state == SmartstrapStateReadComplete)) { - } else if ((prev_state == SmartstrapStateReadDisabled) && - (new_state == SmartstrapStateReadInProgress)) { - } else if ((prev_state == SmartstrapStateReadDisabled) && - (new_state == SmartstrapStateReadReady)) { - } else if ((prev_state == SmartstrapStateReadInProgress) && - (new_state == SmartstrapStateReadComplete)) { - } else if ((prev_state == SmartstrapStateReadComplete) && - (new_state == SmartstrapStateReadReady)) { - } else { - // all other transitions are invalid - cl_assert(false); - } -} - -SmartstrapState smartstrap_fsm_state_get(void) { - return s_fsm_state; -} - -void smartstrap_fsm_state_reset(void) { - s_fsm_state = SmartstrapStateReadReady; -} - -bool smartstrap_fsm_state_test_and_set(SmartstrapState expected_state, SmartstrapState next_state) { - if (s_fsm_state != expected_state) { - return false; - } - prv_check_fsm_transition(s_fsm_state, next_state); - s_fsm_state = next_state; - return true; -} - -void smartstrap_fsm_state_set(SmartstrapState next_state) { - prv_check_fsm_transition(s_fsm_state, next_state); - s_fsm_state = next_state; -} - -void smartstrap_state_lock(void) { - cl_assert(!s_locked); - s_locked = true; -} - -void smartstrap_state_unlock(void) { - cl_assert(s_locked); - s_locked = false; -} - -void smartstrap_state_assert_locked_by_current_task(void) { - cl_assert(s_locked); -} - -bool sys_smartstrap_is_service_connected(uint16_t service_id) { - return true; -} diff --git a/tests/fakes/fake_smartstrap_state.h b/tests/fakes/fake_smartstrap_state.h deleted file mode 100644 index c52e7d6498..0000000000 --- a/tests/fakes/fake_smartstrap_state.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -typedef enum { - SmartstrapStateUnsubscribed, - SmartstrapStateReadReady, - SmartstrapStateNotifyInProgress, - SmartstrapStateReadDisabled, - SmartstrapStateReadInProgress, - SmartstrapStateReadComplete -} SmartstrapState; - - -SmartstrapState smartstrap_fsm_state_get(void); -void smartstrap_fsm_state_reset(void); -bool smartstrap_fsm_state_test_and_set(SmartstrapState expected_state, SmartstrapState next_state); -void smartstrap_fsm_state_set(SmartstrapState next_state); -void smartstrap_state_lock(void); -void smartstrap_state_unlock(void); -void smartstrap_state_assert_locked_by_current_task(void); -bool sys_smartstrap_is_service_connected(uint16_t service_id); diff --git a/tests/fakes/fake_spi_flash.c b/tests/fakes/fake_spi_flash.c index 83cfc90930..54fdb52951 100644 --- a/tests/fakes/fake_spi_flash.c +++ b/tests/fakes/fake_spi_flash.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_spi_flash.h" @@ -56,6 +43,31 @@ void fake_spi_flash_init(uint32_t offset, uint32_t length) { // Clients are not required to cleanup due to prior code, so do so here. fake_spi_flash_cleanup(); + // Tests historically pass (0, 0x1000000) — sized for snowy's flash layout where the filesystem + // sits inside the first 16 MB. On platforms with a non-zero FLASH_REGION_BASE_ADDRESS (e.g. + // obelix, whose flash addresses are absolute and start at 0x12000000), translate offset 0 to the + // base address so the fake window doesn't span the huge unused gap below it, and then expand the + // window if it still doesn't reach FLASH_REGION_FILESYSTEM_END. Without this, obelix tests would + // allocate ~319 MB per test and trip DUMA's protect-free OOM guard. +#if defined(FLASH_REGION_BASE_ADDRESS) && (FLASH_REGION_BASE_ADDRESS > 0) + if (offset == 0) { + offset = FLASH_REGION_BASE_ADDRESS; + } +#endif +#ifdef FLASH_REGION_FILESYSTEM_END + if (offset + length < FLASH_REGION_FILESYSTEM_END) { + length = FLASH_REGION_FILESYSTEM_END - offset; + } +#endif +#ifdef FLASH_REGION_SHARED_PRF_STORAGE_END + // On platforms with regions beyond the filesystem (TZINFO, MFG_INFO, + // SHARED_PRF_STORAGE — present on gd25q256e/obelix layouts), grow far enough + // to cover them so flash_read_bytes doesn't trip the bounds check. + if (offset + length < FLASH_REGION_SHARED_PRF_STORAGE_END) { + length = FLASH_REGION_SHARED_PRF_STORAGE_END - offset; + } +#endif + s_state.offset = offset; s_state.length = length; s_state.storage = malloc(length); @@ -159,12 +171,6 @@ static void erase_block(uint32_t block_addr, uint32_t block_size) { } void flash_erase_sector_blocking(uint32_t sector_addr) { -#if PLATFORM_SNOWY - if (sector_addr <= BOTTOM_BOOT_REGION_END) { - erase_block(sector_addr, BOTTOM_BOOT_SECTOR_SIZE); - return; - } -#endif erase_block(sector_addr, SECTOR_SIZE_BYTES); } @@ -177,12 +183,6 @@ void flash_erase_subsector_blocking(uint32_t subsector_addr) { } uint32_t flash_get_sector_base_address(uint32_t flash_addr) { -#if PLATFORM_SNOWY - if (flash_addr <= BOTTOM_BOOT_REGION_END) { - return (flash_addr & ~(BOTTOM_BOOT_SECTOR_SIZE - 1)); - } -#endif - return (flash_addr & ~(SECTOR_SIZE_BYTES - 1)); } diff --git a/tests/fakes/fake_spi_flash.h b/tests/fakes/fake_spi_flash.h index d6a42a3f80..9106947b93 100644 --- a/tests/fakes/fake_spi_flash.h +++ b/tests/fakes/fake_spi_flash.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_system_app_registry_apps.c b/tests/fakes/fake_system_app_registry_apps.c index 274a7a34ef..690430edcd 100644 --- a/tests/fakes/fake_system_app_registry_apps.c +++ b/tests/fakes/fake_system_app_registry_apps.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_system_app_registry_apps.h" #include "resource/resource_ids.auto.h" diff --git a/tests/fakes/fake_system_app_registry_apps.h b/tests/fakes/fake_system_app_registry_apps.h index 71980ba1cb..0839ad1bfb 100644 --- a/tests/fakes/fake_system_app_registry_apps.h +++ b/tests/fakes/fake_system_app_registry_apps.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_system_task.h b/tests/fakes/fake_system_task.h index ea1b77c495..253aa5e4d9 100644 --- a/tests/fakes/fake_system_task.h +++ b/tests/fakes/fake_system_task.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" #include "fake_pebble_tasks.h" #include "util/list.h" diff --git a/tests/fakes/fake_time.h b/tests/fakes/fake_time.h index 76fe0c2bf3..fc670dff1f 100644 --- a/tests/fakes/fake_time.h +++ b/tests/fakes/fake_time.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/fake_time_timeshift_js.h b/tests/fakes/fake_time_timeshift_js.h deleted file mode 100644 index 2a1041abae..0000000000 --- a/tests/fakes/fake_time_timeshift_js.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#ifndef EMSCRIPTEN -#error "Expecting emscripten build!" -#endif - -#include - -static void prv_check_is_timeshift_loaded(void) { - EM_ASM( - if (Module.setTime === undefined || Module.setTimezoneOffset === undefined) { - throw new Error('timeshift.js not loaded?'); - } - ); -} - -uint16_t time_ms(time_t *tloc, uint16_t *out_ms) { - EM_ASM(throw new Error('NYI')); - return 0; -} - -int32_t time_get_gmtoffset(void) { - EM_ASM(throw new Error('NYI')); - return 0; -} - -int32_t time_get_dstoffset(void) { - EM_ASM(throw new Error('NYI')); - return 0; -} - -bool time_get_isdst(time_t utc_time) { - EM_ASM(throw new Error('NYI')); - return false; -} - -time_t time_utc_to_local(time_t utc_time) { - EM_ASM(throw new Error('NYI')); - return 0; -} - -time_t time_local_to_utc(time_t local_time) { - EM_ASM(throw new Error('NYI')); - return 0; -} - -void fake_time_init(time_t initial_time, uint16_t initial_ms) { - prv_check_is_timeshift_loaded(); - EM_ASM_INT({ - Module.setTimezoneOffset(0); - Module.setTime($0 * 1000 + $1); - }, initial_time, initial_ms); -} - -void fake_time_set_dst(int32_t offset, int32_t start, int32_t stop) { - EM_ASM(throw new Error('NYI')); -} - -void fake_time_set_gmtoff(int32_t gmtoff) { - EM_ASM(throw new Error('NYI')); -} diff --git a/tests/fakes/fake_workout_service.c b/tests/fakes/fake_workout_service.c index 04ff5fc4a6..c20383d703 100644 --- a/tests/fakes/fake_workout_service.c +++ b/tests/fakes/fake_workout_service.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "fake_workout_service.h" diff --git a/tests/fakes/fake_workout_service.h b/tests/fakes/fake_workout_service.h index caf8e4247e..4d1865e6f5 100644 --- a/tests/fakes/fake_workout_service.h +++ b/tests/fakes/fake_workout_service.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/activity/activity.h" -#include "services/normal/activity/hr_util.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/hr_util.h" #include #include diff --git a/tests/fakes/ram_storage.c b/tests/fakes/ram_storage.c index de9a59ca26..bb63626dc2 100644 --- a/tests/fakes/ram_storage.c +++ b/tests/fakes/ram_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "ram_storage.h" diff --git a/tests/fakes/ram_storage.h b/tests/fakes/ram_storage.h index c45b02c325..e4f44f42ee 100644 --- a/tests/fakes/ram_storage.h +++ b/tests/fakes/ram_storage.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fakes/test_db.c b/tests/fakes/test_db.c index cd35e56f4a..e040f0b002 100644 --- a/tests/fakes/test_db.c +++ b/tests/fakes/test_db.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "test_db.h" diff --git a/tests/fakes/test_db.h b/tests/fakes/test_db.h index 7db8356541..41ae85585e 100644 --- a/tests/fakes/test_db.h +++ b/tests/fakes/test_db.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include -#include "services/normal/blob_db/api.h" +#include "pbl/services/blob_db/api.h" #include "system/status_codes.h" diff --git a/tests/fakes_tests/test_fake_mutex.c b/tests/fakes_tests/test_fake_mutex.c index 39a08912e4..962325f10f 100644 --- a/tests/fakes_tests/test_fake_mutex.c +++ b/tests/fakes_tests/test_fake_mutex.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fakes_tests/wscript b/tests/fakes_tests/wscript deleted file mode 100644 index b877815834..0000000000 --- a/tests/fakes_tests/wscript +++ /dev/null @@ -1,8 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - test_sources_ant_glob = "test_fake_mutex.c") - -# vim:filetype=python - diff --git a/tests/fakes_tests/wscript_build b/tests/fakes_tests/wscript_build new file mode 100644 index 0000000000..99a66ac598 --- /dev/null +++ b/tests/fakes_tests/wscript_build @@ -0,0 +1,6 @@ +from waftools.pebble_test import clar + +clar(ctx, + test_sources_ant_glob = "test_fake_mutex.c") + +# vim:filetype=python diff --git a/tests/fixtures/activity/activity_samples/run_shut_down.c b/tests/fixtures/activity/activity_samples/run_shut_down.c index 8f35527233..19046b027f 100644 --- a/tests/fixtures/activity/activity_samples/run_shut_down.c +++ b/tests/fixtures/activity/activity_samples/run_shut_down.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/activity_samples/walk_activities_0.c b/tests/fixtures/activity/activity_samples/walk_activities_0.c index b63a09747d..0b2cc028e8 100644 --- a/tests/fixtures/activity/activity_samples/walk_activities_0.c +++ b/tests/fixtures/activity/activity_samples/walk_activities_0.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-25945.c b/tests/fixtures/activity/sleep_samples/pbl-25945.c index 21dc85bef4..d66dc7897c 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-25945.c +++ b/tests/fixtures/activity/sleep_samples/pbl-25945.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-25972.c b/tests/fixtures/activity/sleep_samples/pbl-25972.c index 3da32aadc8..86f99a9061 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-25972.c +++ b/tests/fixtures/activity/sleep_samples/pbl-25972.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- // Sample captured at: 2015-10-07 11:31:00 local, 2015-10-07 18:31:00 GMT diff --git a/tests/fixtures/activity/sleep_samples/pbl-27896.c b/tests/fixtures/activity/sleep_samples/pbl-27896.c index 9f057869dc..9586da8dd8 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-27896.c +++ b/tests/fixtures/activity/sleep_samples/pbl-27896.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-27921.c b/tests/fixtures/activity/sleep_samples/pbl-27921.c index 852184780a..c35e95b85a 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-27921.c +++ b/tests/fixtures/activity/sleep_samples/pbl-27921.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-27937.c b/tests/fixtures/activity/sleep_samples/pbl-27937.c index a2f204592f..cda68b75f5 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-27937.c +++ b/tests/fixtures/activity/sleep_samples/pbl-27937.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/pbl-28362.c b/tests/fixtures/activity/sleep_samples/pbl-28362.c index 265e0ba8a3..79c2f6664d 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-28362.c +++ b/tests/fixtures/activity/sleep_samples/pbl-28362.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-28365.c b/tests/fixtures/activity/sleep_samples/pbl-28365.c index 53ea8d1bf7..544e510b2f 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-28365.c +++ b/tests/fixtures/activity/sleep_samples/pbl-28365.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- // Sample captured at: 2015-10-25 07:28:00 local, 2015-10-25 14:28:00 GMT diff --git a/tests/fixtures/activity/sleep_samples/pbl-28371-plugged-in.c b/tests/fixtures/activity/sleep_samples/pbl-28371-plugged-in.c index 16ac4976b1..a753a1fada 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-28371-plugged-in.c +++ b/tests/fixtures/activity/sleep_samples/pbl-28371-plugged-in.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-28371.c b/tests/fixtures/activity/sleep_samples/pbl-28371.c index 9ba1ad0bc5..5bbb2b51e4 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-28371.c +++ b/tests/fixtures/activity/sleep_samples/pbl-28371.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-29016.c b/tests/fixtures/activity/sleep_samples/pbl-29016.c index fe5b3dae87..b647569b34 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-29016.c +++ b/tests/fixtures/activity/sleep_samples/pbl-29016.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/pbl-29288.c b/tests/fixtures/activity/sleep_samples/pbl-29288.c index ca713d2616..b9f2f2b42f 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-29288.c +++ b/tests/fixtures/activity/sleep_samples/pbl-29288.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-29350.c b/tests/fixtures/activity/sleep_samples/pbl-29350.c index 32bb8d67ae..def48c7abd 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-29350.c +++ b/tests/fixtures/activity/sleep_samples/pbl-29350.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/pbl-29359.c b/tests/fixtures/activity/sleep_samples/pbl-29359.c index 6658a546f0..2c323e0d55 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-29359.c +++ b/tests/fixtures/activity/sleep_samples/pbl-29359.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-29468.c b/tests/fixtures/activity/sleep_samples/pbl-29468.c index 6b525aa11c..9dbd12062f 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-29468.c +++ b/tests/fixtures/activity/sleep_samples/pbl-29468.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-30867.c b/tests/fixtures/activity/sleep_samples/pbl-30867.c index 4196a6ed1c..f57b40d9ca 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-30867.c +++ b/tests/fixtures/activity/sleep_samples/pbl-30867.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/pbl-31065.c b/tests/fixtures/activity/sleep_samples/pbl-31065.c index 45345d69be..47b85bf1f6 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-31065.c +++ b/tests/fixtures/activity/sleep_samples/pbl-31065.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/pbl-37567.c b/tests/fixtures/activity/sleep_samples/pbl-37567.c index ac5ce35f39..f2952796b5 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-37567.c +++ b/tests/fixtures/activity/sleep_samples/pbl-37567.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/pbl-37734.c b/tests/fixtures/activity/sleep_samples/pbl-37734.c index b476ac5765..bae65d4a8c 100644 --- a/tests/fixtures/activity/sleep_samples/pbl-37734.c +++ b/tests/fixtures/activity/sleep_samples/pbl-37734.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/sleep_samples_1.c b/tests/fixtures/activity/sleep_samples/sleep_samples_1.c index 181d7b11ab..49beda7222 100644 --- a/tests/fixtures/activity/sleep_samples/sleep_samples_1.c +++ b/tests/fixtures/activity/sleep_samples/sleep_samples_1.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/sleep_samples/sleep_samples_v1.c b/tests/fixtures/activity/sleep_samples/sleep_samples_v1.c index 8bce792ebb..4ce045749a 100644 --- a/tests/fixtures/activity/sleep_samples/sleep_samples_v1.c +++ b/tests/fixtures/activity/sleep_samples/sleep_samples_v1.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/sleep_shut_down.c b/tests/fixtures/activity/sleep_samples/sleep_shut_down.c index 1ef4c4b295..b6ede9b0aa 100644 --- a/tests/fixtures/activity/sleep_samples/sleep_shut_down.c +++ b/tests/fixtures/activity/sleep_samples/sleep_shut_down.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // DESCRIPTION: diff --git a/tests/fixtures/activity/sleep_samples/temp-113048.c b/tests/fixtures/activity/sleep_samples/temp-113048.c index e8cbd85da8..fc1055c92b 100644 --- a/tests/fixtures/activity/sleep_samples/temp-113048.c +++ b/tests/fixtures/activity/sleep_samples/temp-113048.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/temp-113136.c b/tests/fixtures/activity/sleep_samples/temp-113136.c index f9dc0db2b2..653a98774e 100644 --- a/tests/fixtures/activity/sleep_samples/temp-113136.c +++ b/tests/fixtures/activity/sleep_samples/temp-113136.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/sleep_samples/temp-118176.c b/tests/fixtures/activity/sleep_samples/temp-118176.c index 4961516f17..e8b096bb71 100644 --- a/tests/fixtures/activity/sleep_samples/temp-118176.c +++ b/tests/fixtures/activity/sleep_samples/temp-118176.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25296.c b/tests/fixtures/activity/step_samples/pbl-25296.c index 7802cfc94e..a3dd3ee789 100644 --- a/tests/fixtures/activity/step_samples/pbl-25296.c +++ b/tests/fixtures/activity/step_samples/pbl-25296.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25297.c b/tests/fixtures/activity/step_samples/pbl-25297.c index dae3809073..e7d95cb27f 100644 --- a/tests/fixtures/activity/step_samples/pbl-25297.c +++ b/tests/fixtures/activity/step_samples/pbl-25297.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25298.c b/tests/fixtures/activity/step_samples/pbl-25298.c index 882817ba01..81099c273d 100644 --- a/tests/fixtures/activity/step_samples/pbl-25298.c +++ b/tests/fixtures/activity/step_samples/pbl-25298.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25299.c b/tests/fixtures/activity/step_samples/pbl-25299.c index f70b517922..b7db275694 100644 --- a/tests/fixtures/activity/step_samples/pbl-25299.c +++ b/tests/fixtures/activity/step_samples/pbl-25299.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25300.c b/tests/fixtures/activity/step_samples/pbl-25300.c index 623c861f3c..fa56b41a78 100644 --- a/tests/fixtures/activity/step_samples/pbl-25300.c +++ b/tests/fixtures/activity/step_samples/pbl-25300.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25301.c b/tests/fixtures/activity/step_samples/pbl-25301.c index 866fc1cd9e..9907a57c50 100644 --- a/tests/fixtures/activity/step_samples/pbl-25301.c +++ b/tests/fixtures/activity/step_samples/pbl-25301.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25302.c b/tests/fixtures/activity/step_samples/pbl-25302.c index 743dd7face..b7278aa798 100644 --- a/tests/fixtures/activity/step_samples/pbl-25302.c +++ b/tests/fixtures/activity/step_samples/pbl-25302.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25303.c b/tests/fixtures/activity/step_samples/pbl-25303.c index 952c93bcac..6718cde782 100644 --- a/tests/fixtures/activity/step_samples/pbl-25303.c +++ b/tests/fixtures/activity/step_samples/pbl-25303.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25471.c b/tests/fixtures/activity/step_samples/pbl-25471.c index 412a70ceb0..685b4aa95b 100644 --- a/tests/fixtures/activity/step_samples/pbl-25471.c +++ b/tests/fixtures/activity/step_samples/pbl-25471.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25472.c b/tests/fixtures/activity/step_samples/pbl-25472.c index 10d5b70380..8084f2fc38 100644 --- a/tests/fixtures/activity/step_samples/pbl-25472.c +++ b/tests/fixtures/activity/step_samples/pbl-25472.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25477.c b/tests/fixtures/activity/step_samples/pbl-25477.c index 3c4ac15625..1557761db1 100644 --- a/tests/fixtures/activity/step_samples/pbl-25477.c +++ b/tests/fixtures/activity/step_samples/pbl-25477.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25478.c b/tests/fixtures/activity/step_samples/pbl-25478.c index f946ca8cc8..792d6eccbe 100644 --- a/tests/fixtures/activity/step_samples/pbl-25478.c +++ b/tests/fixtures/activity/step_samples/pbl-25478.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25479.c b/tests/fixtures/activity/step_samples/pbl-25479.c index befb36ef58..5bcad417c7 100644 --- a/tests/fixtures/activity/step_samples/pbl-25479.c +++ b/tests/fixtures/activity/step_samples/pbl-25479.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25480.c b/tests/fixtures/activity/step_samples/pbl-25480.c index cc85232113..378fc51207 100644 --- a/tests/fixtures/activity/step_samples/pbl-25480.c +++ b/tests/fixtures/activity/step_samples/pbl-25480.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25482.c b/tests/fixtures/activity/step_samples/pbl-25482.c index 02129332be..47a4739adc 100644 --- a/tests/fixtures/activity/step_samples/pbl-25482.c +++ b/tests/fixtures/activity/step_samples/pbl-25482.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25530.c b/tests/fixtures/activity/step_samples/pbl-25530.c index 13a39ff22f..d7421e5a67 100644 --- a/tests/fixtures/activity/step_samples/pbl-25530.c +++ b/tests/fixtures/activity/step_samples/pbl-25530.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25531.c b/tests/fixtures/activity/step_samples/pbl-25531.c index 33f3222521..57290e45c9 100644 --- a/tests/fixtures/activity/step_samples/pbl-25531.c +++ b/tests/fixtures/activity/step_samples/pbl-25531.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25532.c b/tests/fixtures/activity/step_samples/pbl-25532.c index 2fccbdd944..71b84bcad4 100644 --- a/tests/fixtures/activity/step_samples/pbl-25532.c +++ b/tests/fixtures/activity/step_samples/pbl-25532.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25533.c b/tests/fixtures/activity/step_samples/pbl-25533.c index 89ca0f5f90..3538f74c60 100644 --- a/tests/fixtures/activity/step_samples/pbl-25533.c +++ b/tests/fixtures/activity/step_samples/pbl-25533.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25534.c b/tests/fixtures/activity/step_samples/pbl-25534.c index de48336cd5..8496d512c6 100644 --- a/tests/fixtures/activity/step_samples/pbl-25534.c +++ b/tests/fixtures/activity/step_samples/pbl-25534.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25655.c b/tests/fixtures/activity/step_samples/pbl-25655.c index b68e0f1d5d..e43fe4e8d0 100644 --- a/tests/fixtures/activity/step_samples/pbl-25655.c +++ b/tests/fixtures/activity/step_samples/pbl-25655.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25656.c b/tests/fixtures/activity/step_samples/pbl-25656.c index b921095d29..bdb20cb659 100644 --- a/tests/fixtures/activity/step_samples/pbl-25656.c +++ b/tests/fixtures/activity/step_samples/pbl-25656.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25657.c b/tests/fixtures/activity/step_samples/pbl-25657.c index 8afe6aae3b..8c61770d37 100644 --- a/tests/fixtures/activity/step_samples/pbl-25657.c +++ b/tests/fixtures/activity/step_samples/pbl-25657.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25658.c b/tests/fixtures/activity/step_samples/pbl-25658.c index 564c8556dc..a550ad29d8 100644 --- a/tests/fixtures/activity/step_samples/pbl-25658.c +++ b/tests/fixtures/activity/step_samples/pbl-25658.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25659.c b/tests/fixtures/activity/step_samples/pbl-25659.c index dc370ac4b0..84a9fcff76 100644 --- a/tests/fixtures/activity/step_samples/pbl-25659.c +++ b/tests/fixtures/activity/step_samples/pbl-25659.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25660.c b/tests/fixtures/activity/step_samples/pbl-25660.c index e5e649f11c..2bd1783972 100644 --- a/tests/fixtures/activity/step_samples/pbl-25660.c +++ b/tests/fixtures/activity/step_samples/pbl-25660.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25661.c b/tests/fixtures/activity/step_samples/pbl-25661.c index 31af331071..063cb89cd1 100644 --- a/tests/fixtures/activity/step_samples/pbl-25661.c +++ b/tests/fixtures/activity/step_samples/pbl-25661.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25662.c b/tests/fixtures/activity/step_samples/pbl-25662.c index 0174d28034..02f5fd02d1 100644 --- a/tests/fixtures/activity/step_samples/pbl-25662.c +++ b/tests/fixtures/activity/step_samples/pbl-25662.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25663.c b/tests/fixtures/activity/step_samples/pbl-25663.c index 61335d27dc..fb7ca7dad0 100644 --- a/tests/fixtures/activity/step_samples/pbl-25663.c +++ b/tests/fixtures/activity/step_samples/pbl-25663.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25664.c b/tests/fixtures/activity/step_samples/pbl-25664.c index e50d085e85..013608a866 100644 --- a/tests/fixtures/activity/step_samples/pbl-25664.c +++ b/tests/fixtures/activity/step_samples/pbl-25664.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25665.c b/tests/fixtures/activity/step_samples/pbl-25665.c index 936e2d8c52..f087258a5b 100644 --- a/tests/fixtures/activity/step_samples/pbl-25665.c +++ b/tests/fixtures/activity/step_samples/pbl-25665.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25666.c b/tests/fixtures/activity/step_samples/pbl-25666.c index ef6b8f5db8..0a7d6f7d3d 100644 --- a/tests/fixtures/activity/step_samples/pbl-25666.c +++ b/tests/fixtures/activity/step_samples/pbl-25666.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25667.c b/tests/fixtures/activity/step_samples/pbl-25667.c index 9aa3b6a48b..7194e05e0f 100644 --- a/tests/fixtures/activity/step_samples/pbl-25667.c +++ b/tests/fixtures/activity/step_samples/pbl-25667.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25668.c b/tests/fixtures/activity/step_samples/pbl-25668.c index c865133a80..aa717e9aaa 100644 --- a/tests/fixtures/activity/step_samples/pbl-25668.c +++ b/tests/fixtures/activity/step_samples/pbl-25668.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25669.c b/tests/fixtures/activity/step_samples/pbl-25669.c index 3fb905178f..7eb9ccdcd8 100644 --- a/tests/fixtures/activity/step_samples/pbl-25669.c +++ b/tests/fixtures/activity/step_samples/pbl-25669.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25670.c b/tests/fixtures/activity/step_samples/pbl-25670.c index 69d3f25a80..eefe819821 100644 --- a/tests/fixtures/activity/step_samples/pbl-25670.c +++ b/tests/fixtures/activity/step_samples/pbl-25670.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25671.c b/tests/fixtures/activity/step_samples/pbl-25671.c index f6805c7e19..fceb6c89d6 100644 --- a/tests/fixtures/activity/step_samples/pbl-25671.c +++ b/tests/fixtures/activity/step_samples/pbl-25671.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25672.c b/tests/fixtures/activity/step_samples/pbl-25672.c index e41e433556..c97e278062 100644 --- a/tests/fixtures/activity/step_samples/pbl-25672.c +++ b/tests/fixtures/activity/step_samples/pbl-25672.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25673.c b/tests/fixtures/activity/step_samples/pbl-25673.c index 7191ce7a3f..2802de8e9d 100644 --- a/tests/fixtures/activity/step_samples/pbl-25673.c +++ b/tests/fixtures/activity/step_samples/pbl-25673.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-25674.c b/tests/fixtures/activity/step_samples/pbl-25674.c index dbb63f874c..a69656a6d5 100644 --- a/tests/fixtures/activity/step_samples/pbl-25674.c +++ b/tests/fixtures/activity/step_samples/pbl-25674.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-25675.c b/tests/fixtures/activity/step_samples/pbl-25675.c index 1c3fce9359..a221c6482b 100644 --- a/tests/fixtures/activity/step_samples/pbl-25675.c +++ b/tests/fixtures/activity/step_samples/pbl-25675.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- // Sample captured: 2015-10-05 20:28:54 local diff --git a/tests/fixtures/activity/step_samples/pbl-25820.c b/tests/fixtures/activity/step_samples/pbl-25820.c index 3f0603e4b2..f11567db78 100644 --- a/tests/fixtures/activity/step_samples/pbl-25820.c +++ b/tests/fixtures/activity/step_samples/pbl-25820.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-26005.c b/tests/fixtures/activity/step_samples/pbl-26005.c index 182acbe587..ba315ea943 100644 --- a/tests/fixtures/activity/step_samples/pbl-26005.c +++ b/tests/fixtures/activity/step_samples/pbl-26005.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-27713.c b/tests/fixtures/activity/step_samples/pbl-27713.c index 24be44b743..4f6ea97442 100644 --- a/tests/fixtures/activity/step_samples/pbl-27713.c +++ b/tests/fixtures/activity/step_samples/pbl-27713.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-27927.c b/tests/fixtures/activity/step_samples/pbl-27927.c index a5ea89b798..7d3f2b9f35 100644 --- a/tests/fixtures/activity/step_samples/pbl-27927.c +++ b/tests/fixtures/activity/step_samples/pbl-27927.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-28359.c b/tests/fixtures/activity/step_samples/pbl-28359.c index 87f880cb35..766b15ac7a 100644 --- a/tests/fixtures/activity/step_samples/pbl-28359.c +++ b/tests/fixtures/activity/step_samples/pbl-28359.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28360.c b/tests/fixtures/activity/step_samples/pbl-28360.c index c1bf04e13a..9cc5b76c3a 100644 --- a/tests/fixtures/activity/step_samples/pbl-28360.c +++ b/tests/fixtures/activity/step_samples/pbl-28360.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-28361.c b/tests/fixtures/activity/step_samples/pbl-28361.c index 71830a80a0..6bb9208954 100644 --- a/tests/fixtures/activity/step_samples/pbl-28361.c +++ b/tests/fixtures/activity/step_samples/pbl-28361.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28385.c b/tests/fixtures/activity/step_samples/pbl-28385.c index 08694f85b0..9dff3b9b2d 100644 --- a/tests/fixtures/activity/step_samples/pbl-28385.c +++ b/tests/fixtures/activity/step_samples/pbl-28385.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-28386.c b/tests/fixtures/activity/step_samples/pbl-28386.c index bd0b1bd162..036ed52ae5 100644 --- a/tests/fixtures/activity/step_samples/pbl-28386.c +++ b/tests/fixtures/activity/step_samples/pbl-28386.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28394.c b/tests/fixtures/activity/step_samples/pbl-28394.c index 2dccf010a3..56ea658c7b 100644 --- a/tests/fixtures/activity/step_samples/pbl-28394.c +++ b/tests/fixtures/activity/step_samples/pbl-28394.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28396.c b/tests/fixtures/activity/step_samples/pbl-28396.c index 1340446689..a9d2639d05 100644 --- a/tests/fixtures/activity/step_samples/pbl-28396.c +++ b/tests/fixtures/activity/step_samples/pbl-28396.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28397.c b/tests/fixtures/activity/step_samples/pbl-28397.c index efdf4c0a00..81909aeb9e 100644 --- a/tests/fixtures/activity/step_samples/pbl-28397.c +++ b/tests/fixtures/activity/step_samples/pbl-28397.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28403.c b/tests/fixtures/activity/step_samples/pbl-28403.c index 1a630deef5..467ac3cfbb 100644 --- a/tests/fixtures/activity/step_samples/pbl-28403.c +++ b/tests/fixtures/activity/step_samples/pbl-28403.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // ---------------------------------------------------------------- diff --git a/tests/fixtures/activity/step_samples/pbl-28404.c b/tests/fixtures/activity/step_samples/pbl-28404.c index 98159df39e..65ac310707 100644 --- a/tests/fixtures/activity/step_samples/pbl-28404.c +++ b/tests/fixtures/activity/step_samples/pbl-28404.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-28406.c b/tests/fixtures/activity/step_samples/pbl-28406.c index a7db8fda9d..f5a3823d5c 100644 --- a/tests/fixtures/activity/step_samples/pbl-28406.c +++ b/tests/fixtures/activity/step_samples/pbl-28406.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/pbl-34795.c b/tests/fixtures/activity/step_samples/pbl-34795.c index 27569ae179..74727697d2 100644 --- a/tests/fixtures/activity/step_samples/pbl-34795.c +++ b/tests/fixtures/activity/step_samples/pbl-34795.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/step_samples_1.c b/tests/fixtures/activity/step_samples/step_samples_1.c index 6e64d2c334..7f54e1b724 100644 --- a/tests/fixtures/activity/step_samples/step_samples_1.c +++ b/tests/fixtures/activity/step_samples/step_samples_1.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fixtures/activity/step_samples/step_samples_2.c b/tests/fixtures/activity/step_samples/step_samples_2.c index 68c219d45c..373542fc1f 100644 --- a/tests/fixtures/activity/step_samples/step_samples_2.c +++ b/tests/fixtures/activity/step_samples/step_samples_2.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fixtures/activity/step_samples/step_samples_3.c b/tests/fixtures/activity/step_samples/step_samples_3.c index 131698d501..f35c098bcc 100644 --- a/tests/fixtures/activity/step_samples/step_samples_3.c +++ b/tests/fixtures/activity/step_samples/step_samples_3.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fixtures/activity/step_samples/temp-94473.c b/tests/fixtures/activity/step_samples/temp-94473.c index 1b245d1a17..92ce2ffe5e 100644 --- a/tests/fixtures/activity/step_samples/temp-94473.c +++ b/tests/fixtures/activity/step_samples/temp-94473.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/temp-95861.c b/tests/fixtures/activity/step_samples/temp-95861.c index 4fc4ff0942..62caf79417 100644 --- a/tests/fixtures/activity/step_samples/temp-95861.c +++ b/tests/fixtures/activity/step_samples/temp-95861.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/activity/step_samples/temp-95866.c b/tests/fixtures/activity/step_samples/temp-95866.c index 6a50e37854..580416fe9f 100644 --- a/tests/fixtures/activity/step_samples/temp-95866.c +++ b/tests/fixtures/activity/step_samples/temp-95866.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/fixtures/load_test_resources.h b/tests/fixtures/load_test_resources.h index 086518107e..4f626d9722 100644 --- a/tests/fixtures/load_test_resources.h +++ b/tests/fixtures/load_test_resources.h @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "flash_region/flash_region.h" #include "resource/resource.h" #include "resource/resource_version.auto.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include #include @@ -33,12 +20,7 @@ #define FRENCH_FIXTURE_NAME "fr_FR.pbpack" #define CHINESE_FIXTURE_NAME "zh_CN.pbpack" -// We used to implicitly use the snowy pbpack for tintin and spalding unit tests; now it's explicit -#if PLATFORM_TINTIN || PLATFORM_SPALDING -#define SYSTEM_RESOURCES_FIXTURE_NAME "system_resources_snowy.pbpack" -#else #define SYSTEM_RESOURCES_FIXTURE_NAME "system_resources_"PLATFORM_NAME".pbpack" -#endif void load_resource_fixture_in_flash(const char *fixture_path, const char *name, bool is_next) { char res_path[strlen(CLAR_FIXTURE_PATH) + strlen(fixture_path) + strlen(name) + 3]; diff --git a/tests/fixtures/resources/builtin_resources.auto.c b/tests/fixtures/resources/builtin_resources.auto.c index 142e396677..fcd70c5887 100644 --- a/tests/fixtures/resources/builtin_resources.auto.c +++ b/tests/fixtures/resources/builtin_resources.auto.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource/resource_ids.auto.h" #include "resource/resource_storage.h" @@ -20,12 +7,12 @@ __attribute__ ((aligned (8))) static const uint8_t ACTION_BAR_ICON_CHECK_builtin_bytes[] = { - 0x05, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x18, /* bytes 0 - 16 */ - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0x80, /* bytes 16 - 32 */ - 0x04, 0x00, 0x1f, 0xfe, 0x00, 0x1e, 0x00, 0x7f, 0xf8, 0x00, 0x7f, 0x81, 0xff, 0xe0, 0x00, 0xbf, /* bytes 32 - 48 */ - 0xe7, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xfe, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x07, 0xff, /* bytes 48 - 64 */ - 0xe0, 0x00, 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, /* bytes 64 - 80 */ - 0x00, 0x00, 0x00, 0x7f, 0xbf, 0xff, + 0x05, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x34, /* bytes 0 - 16 */ + 0x00, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x03, 0xaa, 0x40, 0x00, 0x00, 0x0e, 0xaa, 0x40, /* bytes 16 - 32 */ + 0x0c, 0x00, 0x3a, 0xa9, 0x00, 0x39, 0x00, 0xea, 0xa4, 0x00, 0xea, 0x43, 0xaa, 0x90, 0x00, 0x6a, /* bytes 32 - 48 */ + 0x9e, 0xaa, 0x40, 0x00, 0x2a, 0xaa, 0xa9, 0x00, 0x00, 0x3a, 0xaa, 0xa4, 0x00, 0x00, 0x0e, 0xaa, /* bytes 48 - 64 */ + 0x90, 0x00, 0x00, 0x03, 0xaa, 0x40, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, /* bytes 64 - 80 */ + 0x00, 0x00, 0x00, 0xbf, 0xff, 0x7f, }; __attribute__ ((aligned (8))) static const uint8_t GENERIC_WARNING_LARGE_builtin_bytes[] = { @@ -48,102 +35,455 @@ static const uint8_t GENERIC_WARNING_LARGE_builtin_bytes[] = { }; __attribute__ ((aligned (8))) static const uint8_t FONT_FALLBACK_INTERNAL_builtin_bytes[] = { - 0x03, 0x0e, 0x13, 0x00, 0xaf, 0x25, 0xff, 0x02, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, /* bytes 16 - 32 */ - 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, /* bytes 32 - 48 */ - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, /* bytes 48 - 64 */ - 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, /* bytes 64 - 80 */ - 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, /* bytes 80 - 96 */ - 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, /* bytes 96 - 112 */ - 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, /* bytes 112 - 128 */ - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, /* bytes 128 - 144 */ - 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, /* bytes 144 - 160 */ - 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, /* bytes 160 - 176 */ - 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2d, 0x00, /* bytes 176 - 192 */ - 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x31, 0x01, /* bytes 192 - 208 */ - 0x04, 0x00, 0x32, 0x01, 0x08, 0x00, 0x33, 0x01, 0x0c, 0x00, 0x34, 0x01, 0x10, 0x00, 0x35, 0x01, /* bytes 208 - 224 */ - 0x14, 0x00, 0x36, 0x01, 0x18, 0x00, 0x37, 0x01, 0x1c, 0x00, 0x38, 0x01, 0x20, 0x00, 0x39, 0x01, /* bytes 224 - 240 */ - 0x24, 0x00, 0x3a, 0x00, 0x28, 0x00, 0x3b, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x28, 0x00, 0x3d, 0x00, /* bytes 240 - 256 */ - 0x28, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x40, 0x00, 0x28, 0x00, 0x41, 0x00, /* bytes 256 - 272 */ - 0x28, 0x00, 0x42, 0x00, 0x28, 0x00, 0x43, 0x00, 0x28, 0x00, 0x44, 0x00, 0x28, 0x00, 0x45, 0x00, /* bytes 272 - 288 */ - 0x28, 0x00, 0x46, 0x01, 0x28, 0x00, 0x47, 0x00, 0x2c, 0x00, 0x48, 0x00, 0x2c, 0x00, 0x49, 0x00, /* bytes 288 - 304 */ - 0x2c, 0x00, 0x4a, 0x00, 0x2c, 0x00, 0x4b, 0x00, 0x2c, 0x00, 0x4c, 0x00, 0x2c, 0x00, 0x4d, 0x00, /* bytes 304 - 320 */ - 0x2c, 0x00, 0x4e, 0x00, 0x2c, 0x00, 0x4f, 0x00, 0x2c, 0x00, 0x50, 0x00, 0x2c, 0x00, 0x51, 0x00, /* bytes 320 - 336 */ - 0x2c, 0x00, 0x52, 0x00, 0x2c, 0x00, 0x53, 0x00, 0x2c, 0x00, 0x54, 0x00, 0x2c, 0x00, 0x55, 0x00, /* bytes 336 - 352 */ - 0x2c, 0x00, 0x56, 0x00, 0x2c, 0x00, 0x57, 0x00, 0x2c, 0x00, 0x58, 0x00, 0x2c, 0x00, 0x59, 0x00, /* bytes 352 - 368 */ - 0x2c, 0x00, 0x5a, 0x00, 0x2c, 0x00, 0x5b, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x2c, 0x00, 0x5d, 0x00, /* bytes 368 - 384 */ - 0x2c, 0x00, 0x5e, 0x00, 0x2c, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x60, 0x00, 0x2c, 0x00, 0x61, 0x01, /* bytes 384 - 400 */ - 0x2c, 0x00, 0x62, 0x01, 0x30, 0x00, 0x63, 0x01, 0x34, 0x00, 0x64, 0x01, 0x38, 0x00, 0x65, 0x01, /* bytes 400 - 416 */ - 0x3c, 0x00, 0x66, 0x01, 0x40, 0x00, 0x67, 0x00, 0x44, 0x00, 0x68, 0x00, 0x44, 0x00, 0x69, 0x00, /* bytes 416 - 432 */ - 0x44, 0x00, 0x6a, 0x00, 0x44, 0x00, 0x6b, 0x00, 0x44, 0x00, 0x6c, 0x00, 0x44, 0x00, 0x6d, 0x00, /* bytes 432 - 448 */ - 0x44, 0x00, 0x6e, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44, 0x00, 0x71, 0x00, /* bytes 448 - 464 */ - 0x44, 0x00, 0x72, 0x00, 0x44, 0x00, 0x73, 0x00, 0x44, 0x00, 0x74, 0x00, 0x44, 0x00, 0x75, 0x00, /* bytes 464 - 480 */ - 0x44, 0x00, 0x76, 0x00, 0x44, 0x00, 0x77, 0x00, 0x44, 0x00, 0x78, 0x01, 0x44, 0x00, 0x79, 0x00, /* bytes 480 - 496 */ - 0x48, 0x00, 0x7a, 0x00, 0x48, 0x00, 0x7b, 0x00, 0x48, 0x00, 0x7c, 0x00, 0x48, 0x00, 0x7d, 0x00, /* bytes 496 - 512 */ - 0x48, 0x00, 0x7e, 0x00, 0x48, 0x00, 0x7f, 0x00, 0x48, 0x00, 0x80, 0x00, 0x48, 0x00, 0x81, 0x00, /* bytes 512 - 528 */ - 0x48, 0x00, 0x82, 0x00, 0x48, 0x00, 0x83, 0x00, 0x48, 0x00, 0x84, 0x00, 0x48, 0x00, 0x85, 0x00, /* bytes 528 - 544 */ - 0x48, 0x00, 0x86, 0x00, 0x48, 0x00, 0x87, 0x00, 0x48, 0x00, 0x88, 0x00, 0x48, 0x00, 0x89, 0x00, /* bytes 544 - 560 */ - 0x48, 0x00, 0x8a, 0x00, 0x48, 0x00, 0x8b, 0x00, 0x48, 0x00, 0x8c, 0x00, 0x48, 0x00, 0x8d, 0x00, /* bytes 560 - 576 */ - 0x48, 0x00, 0x8e, 0x00, 0x48, 0x00, 0x8f, 0x00, 0x48, 0x00, 0x90, 0x00, 0x48, 0x00, 0x91, 0x00, /* bytes 576 - 592 */ - 0x48, 0x00, 0x92, 0x00, 0x48, 0x00, 0x93, 0x00, 0x48, 0x00, 0x94, 0x00, 0x48, 0x00, 0x95, 0x00, /* bytes 592 - 608 */ - 0x48, 0x00, 0x96, 0x00, 0x48, 0x00, 0x97, 0x00, 0x48, 0x00, 0x98, 0x00, 0x48, 0x00, 0x99, 0x00, /* bytes 608 - 624 */ - 0x48, 0x00, 0x9a, 0x00, 0x48, 0x00, 0x9b, 0x00, 0x48, 0x00, 0x9c, 0x00, 0x48, 0x00, 0x9d, 0x00, /* bytes 624 - 640 */ - 0x48, 0x00, 0x9e, 0x00, 0x48, 0x00, 0x9f, 0x00, 0x48, 0x00, 0xa0, 0x00, 0x48, 0x00, 0xa1, 0x00, /* bytes 640 - 656 */ - 0x48, 0x00, 0xa2, 0x00, 0x48, 0x00, 0xa3, 0x00, 0x48, 0x00, 0xa4, 0x00, 0x48, 0x00, 0xa5, 0x00, /* bytes 656 - 672 */ - 0x48, 0x00, 0xa6, 0x00, 0x48, 0x00, 0xa7, 0x00, 0x48, 0x00, 0xa8, 0x00, 0x48, 0x00, 0xa9, 0x00, /* bytes 672 - 688 */ - 0x48, 0x00, 0xaa, 0x00, 0x48, 0x00, 0xab, 0x00, 0x48, 0x00, 0xac, 0x00, 0x48, 0x00, 0xad, 0x00, /* bytes 688 - 704 */ - 0x48, 0x00, 0xae, 0x00, 0x48, 0x00, 0xaf, 0x00, 0x48, 0x00, 0xb0, 0x00, 0x48, 0x00, 0xb1, 0x00, /* bytes 704 - 720 */ - 0x48, 0x00, 0xb2, 0x00, 0x48, 0x00, 0xb3, 0x00, 0x48, 0x00, 0xb4, 0x00, 0x48, 0x00, 0xb5, 0x00, /* bytes 720 - 736 */ - 0x48, 0x00, 0xb6, 0x00, 0x48, 0x00, 0xb7, 0x00, 0x48, 0x00, 0xb8, 0x00, 0x48, 0x00, 0xb9, 0x00, /* bytes 736 - 752 */ - 0x48, 0x00, 0xba, 0x00, 0x48, 0x00, 0xbb, 0x00, 0x48, 0x00, 0xbc, 0x00, 0x48, 0x00, 0xbd, 0x00, /* bytes 752 - 768 */ - 0x48, 0x00, 0xbe, 0x00, 0x48, 0x00, 0xbf, 0x00, 0x48, 0x00, 0xc0, 0x00, 0x48, 0x00, 0xc1, 0x00, /* bytes 768 - 784 */ - 0x48, 0x00, 0xc2, 0x00, 0x48, 0x00, 0xc3, 0x00, 0x48, 0x00, 0xc4, 0x00, 0x48, 0x00, 0xc5, 0x00, /* bytes 784 - 800 */ - 0x48, 0x00, 0xc6, 0x00, 0x48, 0x00, 0xc7, 0x00, 0x48, 0x00, 0xc8, 0x00, 0x48, 0x00, 0xc9, 0x00, /* bytes 800 - 816 */ - 0x48, 0x00, 0xca, 0x00, 0x48, 0x00, 0xcb, 0x00, 0x48, 0x00, 0xcc, 0x00, 0x48, 0x00, 0xcd, 0x00, /* bytes 816 - 832 */ - 0x48, 0x00, 0xce, 0x00, 0x48, 0x00, 0xcf, 0x00, 0x48, 0x00, 0xd0, 0x00, 0x48, 0x00, 0xd1, 0x00, /* bytes 832 - 848 */ - 0x48, 0x00, 0xd2, 0x00, 0x48, 0x00, 0xd3, 0x00, 0x48, 0x00, 0xd4, 0x01, 0x48, 0x00, 0xd5, 0x00, /* bytes 848 - 864 */ - 0x4c, 0x00, 0xd6, 0x00, 0x4c, 0x00, 0xd7, 0x00, 0x4c, 0x00, 0xd8, 0x00, 0x4c, 0x00, 0xd9, 0x00, /* bytes 864 - 880 */ - 0x4c, 0x00, 0xda, 0x00, 0x4c, 0x00, 0xdb, 0x00, 0x4c, 0x00, 0xdc, 0x00, 0x4c, 0x00, 0xdd, 0x00, /* bytes 880 - 896 */ - 0x4c, 0x00, 0xde, 0x00, 0x4c, 0x00, 0xdf, 0x00, 0x4c, 0x00, 0xe0, 0x00, 0x4c, 0x00, 0xe1, 0x00, /* bytes 896 - 912 */ - 0x4c, 0x00, 0xe2, 0x00, 0x4c, 0x00, 0xe3, 0x00, 0x4c, 0x00, 0xe4, 0x00, 0x4c, 0x00, 0xe5, 0x00, /* bytes 912 - 928 */ - 0x4c, 0x00, 0xe6, 0x00, 0x4c, 0x00, 0xe7, 0x00, 0x4c, 0x00, 0xe8, 0x00, 0x4c, 0x00, 0xe9, 0x00, /* bytes 928 - 944 */ - 0x4c, 0x00, 0xea, 0x00, 0x4c, 0x00, 0xeb, 0x00, 0x4c, 0x00, 0xec, 0x00, 0x4c, 0x00, 0xed, 0x00, /* bytes 944 - 960 */ - 0x4c, 0x00, 0xee, 0x00, 0x4c, 0x00, 0xef, 0x00, 0x4c, 0x00, 0xf0, 0x00, 0x4c, 0x00, 0xf1, 0x00, /* bytes 960 - 976 */ - 0x4c, 0x00, 0xf2, 0x00, 0x4c, 0x00, 0xf3, 0x00, 0x4c, 0x00, 0xf4, 0x00, 0x4c, 0x00, 0xf5, 0x00, /* bytes 976 - 992 */ - 0x4c, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0xf7, 0x00, 0x4c, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0xf9, 0x00, /* bytes 992 - 1008 */ - 0x4c, 0x00, 0xfa, 0x00, 0x4c, 0x00, 0xfb, 0x00, 0x4c, 0x00, 0xfc, 0x00, 0x4c, 0x00, 0xfd, 0x00, /* bytes 1008 - 1024 */ - 0x4c, 0x00, 0xfe, 0x00, 0x4c, 0x00, 0x30, 0x00, 0x11, 0x00, 0x31, 0x00, 0x1e, 0x00, 0x32, 0x00, /* bytes 1024 - 1040 */ - 0x27, 0x00, 0x33, 0x00, 0x34, 0x00, 0x34, 0x00, 0x41, 0x00, 0x35, 0x00, 0x4e, 0x00, 0x36, 0x00, /* bytes 1040 - 1056 */ - 0x5b, 0x00, 0x37, 0x00, 0x68, 0x00, 0x38, 0x00, 0x75, 0x00, 0x39, 0x00, 0x82, 0x00, 0x26, 0x20, /* bytes 1056 - 1072 */ - 0xda, 0x00, 0x61, 0x00, 0x8f, 0x00, 0x62, 0x00, 0x98, 0x00, 0x63, 0x00, 0xa5, 0x00, 0x64, 0x00, /* bytes 1072 - 1088 */ - 0xae, 0x00, 0x65, 0x00, 0xbb, 0x00, 0x66, 0x00, 0xc4, 0x00, 0x78, 0x00, 0xd1, 0x00, 0xaf, 0x25, /* bytes 1088 - 1104 */ - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x3f, 0xc6, 0x18, 0x63, 0x8c, /* bytes 1104 - 1120 */ - 0x31, 0xc6, 0x0f, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x99, 0x99, 0x99, 0x06, 0x00, 0x00, 0x00, /* bytes 1120 - 1136 */ - 0x03, 0x09, 0x02, 0x05, 0x06, 0x9a, 0x24, 0x49, 0x07, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x89, /* bytes 1136 - 1152 */ - 0x24, 0x11, 0x0f, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x88, 0x86, 0x98, 0x06, /* bytes 1152 - 1168 */ - 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x05, 0x06, 0x88, 0x31, 0xa5, 0x3e, 0x42, 0x08, 0x00, 0x00, /* bytes 1168 - 1184 */ - 0x04, 0x09, 0x01, 0x05, 0x06, 0x1f, 0x71, 0x89, 0x99, 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, /* bytes 1184 - 1200 */ - 0x05, 0x06, 0x96, 0x19, 0x97, 0x99, 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x8f, /* bytes 1200 - 1216 */ - 0x44, 0x24, 0x22, 0x02, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x99, 0x96, 0x99, /* bytes 1216 - 1232 */ - 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x99, 0x8e, 0x99, 0x06, 0x00, 0x00, /* bytes 1232 - 1248 */ - 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x96, 0xe8, 0x99, 0x0e, 0x04, 0x09, 0x01, 0x05, 0x06, 0x11, /* bytes 1248 - 1264 */ - 0x97, 0x99, 0x99, 0x07, 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x96, 0x11, 0x91, 0x06, /* bytes 1264 - 1280 */ - 0x04, 0x09, 0x01, 0x05, 0x06, 0x88, 0x9e, 0x99, 0x99, 0x0e, 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, /* bytes 1280 - 1296 */ - 0x07, 0x06, 0x96, 0xf9, 0x91, 0x06, 0x04, 0x09, 0x00, 0x05, 0x04, 0x2c, 0x27, 0x22, 0x22, 0x02, /* bytes 1296 - 1312 */ - 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x99, 0x66, 0x96, 0x09, 0x07, 0x02, 0x01, 0x0c, /* bytes 1312 - 1328 */ - 0x09, 0xc9, 0x24, 0x00, 0x00, + 0x03, 0x0e, 0x73, 0x01, 0xaf, 0x25, 0xff, 0x02, 0x0a, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, /* bytes 0 - 16 */ + 0x08, 0x00, 0x02, 0x01, 0x10, 0x00, 0x03, 0x01, 0x14, 0x00, 0x04, 0x01, 0x18, 0x00, 0x05, 0x01, /* bytes 16 - 32 */ + 0x1c, 0x00, 0x06, 0x01, 0x20, 0x00, 0x07, 0x01, 0x24, 0x00, 0x08, 0x01, 0x28, 0x00, 0x09, 0x01, /* bytes 32 - 48 */ + 0x2c, 0x00, 0x0a, 0x01, 0x30, 0x00, 0x0b, 0x01, 0x34, 0x00, 0x0c, 0x01, 0x38, 0x00, 0x0d, 0x01, /* bytes 48 - 64 */ + 0x3c, 0x00, 0x0e, 0x01, 0x40, 0x00, 0x0f, 0x01, 0x44, 0x00, 0x10, 0x01, 0x48, 0x00, 0x11, 0x01, /* bytes 64 - 80 */ + 0x4c, 0x00, 0x12, 0x01, 0x50, 0x00, 0x13, 0x01, 0x54, 0x00, 0x14, 0x01, 0x58, 0x00, 0x15, 0x01, /* bytes 80 - 96 */ + 0x5c, 0x00, 0x16, 0x01, 0x60, 0x00, 0x17, 0x01, 0x64, 0x00, 0x18, 0x01, 0x68, 0x00, 0x19, 0x01, /* bytes 96 - 112 */ + 0x6c, 0x00, 0x1a, 0x02, 0x70, 0x00, 0x1b, 0x02, 0x78, 0x00, 0x1c, 0x02, 0x80, 0x00, 0x1d, 0x02, /* bytes 112 - 128 */ + 0x88, 0x00, 0x1e, 0x01, 0x90, 0x00, 0x1f, 0x01, 0x94, 0x00, 0x20, 0x02, 0x98, 0x00, 0x21, 0x02, /* bytes 128 - 144 */ + 0xa0, 0x00, 0x22, 0x02, 0xa8, 0x00, 0x23, 0x02, 0xb0, 0x00, 0x24, 0x03, 0xb8, 0x00, 0x25, 0x02, /* bytes 144 - 160 */ + 0xc4, 0x00, 0x26, 0x02, 0xcc, 0x00, 0x27, 0x02, 0xd4, 0x00, 0x28, 0x03, 0xdc, 0x00, 0x29, 0x02, /* bytes 160 - 176 */ + 0xe8, 0x00, 0x2a, 0x02, 0xf0, 0x00, 0x2b, 0x02, 0xf8, 0x00, 0x2c, 0x02, 0x00, 0x01, 0x2d, 0x02, /* bytes 176 - 192 */ + 0x08, 0x01, 0x2e, 0x02, 0x10, 0x01, 0x2f, 0x02, 0x18, 0x01, 0x30, 0x02, 0x20, 0x01, 0x31, 0x03, /* bytes 192 - 208 */ + 0x28, 0x01, 0x32, 0x02, 0x34, 0x01, 0x33, 0x04, 0x3c, 0x01, 0x34, 0x04, 0x4c, 0x01, 0x35, 0x02, /* bytes 208 - 224 */ + 0x5c, 0x01, 0x36, 0x02, 0x64, 0x01, 0x37, 0x02, 0x6c, 0x01, 0x38, 0x03, 0x74, 0x01, 0x39, 0x03, /* bytes 224 - 240 */ + 0x80, 0x01, 0x3a, 0x03, 0x8c, 0x01, 0x3b, 0x02, 0x98, 0x01, 0x3c, 0x04, 0xa0, 0x01, 0x3d, 0x03, /* bytes 240 - 256 */ + 0xb0, 0x01, 0x3e, 0x03, 0xbc, 0x01, 0x3f, 0x02, 0xc8, 0x01, 0x40, 0x04, 0xd0, 0x01, 0x41, 0x03, /* bytes 256 - 272 */ + 0xe0, 0x01, 0x42, 0x03, 0xec, 0x01, 0x43, 0x03, 0xf8, 0x01, 0x44, 0x02, 0x04, 0x02, 0x45, 0x02, /* bytes 272 - 288 */ + 0x0c, 0x02, 0x46, 0x03, 0x14, 0x02, 0x47, 0x03, 0x20, 0x02, 0x48, 0x02, 0x2c, 0x02, 0x49, 0x02, /* bytes 288 - 304 */ + 0x34, 0x02, 0x4a, 0x02, 0x3c, 0x02, 0x4b, 0x02, 0x44, 0x02, 0x4c, 0x02, 0x4c, 0x02, 0x4d, 0x03, /* bytes 304 - 320 */ + 0x54, 0x02, 0x4e, 0x02, 0x60, 0x02, 0x4f, 0x02, 0x68, 0x02, 0x50, 0x03, 0x70, 0x02, 0x51, 0x02, /* bytes 320 - 336 */ + 0x7c, 0x02, 0x52, 0x02, 0x84, 0x02, 0x53, 0x02, 0x8c, 0x02, 0x54, 0x02, 0x94, 0x02, 0x55, 0x02, /* bytes 336 - 352 */ + 0x9c, 0x02, 0x56, 0x02, 0xa4, 0x02, 0x57, 0x02, 0xac, 0x02, 0x58, 0x02, 0xb4, 0x02, 0x59, 0x03, /* bytes 352 - 368 */ + 0xbc, 0x02, 0x5a, 0x03, 0xc8, 0x02, 0x5b, 0x02, 0xd4, 0x02, 0x5c, 0x02, 0xdc, 0x02, 0x5d, 0x02, /* bytes 368 - 384 */ + 0xe4, 0x02, 0x5e, 0x02, 0xec, 0x02, 0x5f, 0x02, 0xf4, 0x02, 0x60, 0x02, 0xfc, 0x02, 0x61, 0x02, /* bytes 384 - 400 */ + 0x04, 0x03, 0x62, 0x02, 0x0c, 0x03, 0x63, 0x02, 0x14, 0x03, 0x64, 0x03, 0x1c, 0x03, 0x65, 0x02, /* bytes 400 - 416 */ + 0x28, 0x03, 0x66, 0x02, 0x30, 0x03, 0x67, 0x02, 0x38, 0x03, 0x68, 0x02, 0x40, 0x03, 0x69, 0x02, /* bytes 416 - 432 */ + 0x48, 0x03, 0x6a, 0x03, 0x50, 0x03, 0x6b, 0x02, 0x5c, 0x03, 0x6c, 0x02, 0x64, 0x03, 0x6d, 0x02, /* bytes 432 - 448 */ + 0x6c, 0x03, 0x6e, 0x02, 0x74, 0x03, 0x6f, 0x02, 0x7c, 0x03, 0x70, 0x02, 0x84, 0x03, 0x71, 0x02, /* bytes 448 - 464 */ + 0x8c, 0x03, 0x72, 0x02, 0x94, 0x03, 0x73, 0x02, 0x9c, 0x03, 0x74, 0x02, 0xa4, 0x03, 0x75, 0x02, /* bytes 464 - 480 */ + 0xac, 0x03, 0x76, 0x02, 0xb4, 0x03, 0x77, 0x02, 0xbc, 0x03, 0x78, 0x02, 0xc4, 0x03, 0x79, 0x02, /* bytes 480 - 496 */ + 0xcc, 0x03, 0x7a, 0x02, 0xd4, 0x03, 0x7b, 0x02, 0xdc, 0x03, 0x7c, 0x02, 0xe4, 0x03, 0x7d, 0x02, /* bytes 496 - 512 */ + 0xec, 0x03, 0x7e, 0x02, 0xf4, 0x03, 0x7f, 0x01, 0xfc, 0x03, 0x80, 0x01, 0x00, 0x04, 0x81, 0x00, /* bytes 512 - 528 */ + 0x04, 0x04, 0x82, 0x01, 0x04, 0x04, 0x83, 0x00, 0x08, 0x04, 0x84, 0x00, 0x08, 0x04, 0x85, 0x00, /* bytes 528 - 544 */ + 0x08, 0x04, 0x86, 0x01, 0x08, 0x04, 0x87, 0x01, 0x0c, 0x04, 0x88, 0x00, 0x10, 0x04, 0x89, 0x00, /* bytes 544 - 560 */ + 0x10, 0x04, 0x8a, 0x00, 0x10, 0x04, 0x8b, 0x00, 0x10, 0x04, 0x8c, 0x00, 0x10, 0x04, 0x8d, 0x00, /* bytes 560 - 576 */ + 0x10, 0x04, 0x8e, 0x00, 0x10, 0x04, 0x8f, 0x00, 0x10, 0x04, 0x90, 0x00, 0x10, 0x04, 0x91, 0x00, /* bytes 576 - 592 */ + 0x10, 0x04, 0x92, 0x00, 0x10, 0x04, 0x93, 0x01, 0x10, 0x04, 0x94, 0x00, 0x14, 0x04, 0x95, 0x00, /* bytes 592 - 608 */ + 0x14, 0x04, 0x96, 0x00, 0x14, 0x04, 0x97, 0x00, 0x14, 0x04, 0x98, 0x00, 0x14, 0x04, 0x99, 0x00, /* bytes 608 - 624 */ + 0x14, 0x04, 0x9a, 0x00, 0x14, 0x04, 0x9b, 0x00, 0x14, 0x04, 0x9c, 0x00, 0x14, 0x04, 0x9d, 0x00, /* bytes 624 - 640 */ + 0x14, 0x04, 0x9e, 0x00, 0x14, 0x04, 0x9f, 0x00, 0x14, 0x04, 0xa0, 0x01, 0x14, 0x04, 0xa1, 0x01, /* bytes 640 - 656 */ + 0x18, 0x04, 0xa2, 0x01, 0x1c, 0x04, 0xa3, 0x01, 0x20, 0x04, 0xa4, 0x01, 0x24, 0x04, 0xa5, 0x01, /* bytes 656 - 672 */ + 0x28, 0x04, 0xa6, 0x01, 0x2c, 0x04, 0xa7, 0x01, 0x30, 0x04, 0xa8, 0x01, 0x34, 0x04, 0xa9, 0x01, /* bytes 672 - 688 */ + 0x38, 0x04, 0xaa, 0x01, 0x3c, 0x04, 0xab, 0x01, 0x40, 0x04, 0xac, 0x01, 0x44, 0x04, 0xad, 0x00, /* bytes 688 - 704 */ + 0x48, 0x04, 0xae, 0x01, 0x48, 0x04, 0xaf, 0x01, 0x4c, 0x04, 0xb0, 0x01, 0x50, 0x04, 0xb1, 0x01, /* bytes 704 - 720 */ + 0x54, 0x04, 0xb2, 0x01, 0x58, 0x04, 0xb3, 0x01, 0x5c, 0x04, 0xb4, 0x01, 0x60, 0x04, 0xb5, 0x01, /* bytes 720 - 736 */ + 0x64, 0x04, 0xb6, 0x01, 0x68, 0x04, 0xb7, 0x01, 0x6c, 0x04, 0xb8, 0x01, 0x70, 0x04, 0xb9, 0x01, /* bytes 736 - 752 */ + 0x74, 0x04, 0xba, 0x02, 0x78, 0x04, 0xbb, 0x01, 0x80, 0x04, 0xbc, 0x01, 0x84, 0x04, 0xbd, 0x01, /* bytes 752 - 768 */ + 0x88, 0x04, 0xbe, 0x01, 0x8c, 0x04, 0xbf, 0x01, 0x90, 0x04, 0xc0, 0x01, 0x94, 0x04, 0xc1, 0x01, /* bytes 768 - 784 */ + 0x98, 0x04, 0xc2, 0x01, 0x9c, 0x04, 0xc3, 0x02, 0xa0, 0x04, 0xc4, 0x01, 0xa8, 0x04, 0xc5, 0x01, /* bytes 784 - 800 */ + 0xac, 0x04, 0xc6, 0x01, 0xb0, 0x04, 0xc7, 0x01, 0xb4, 0x04, 0xc8, 0x02, 0xb8, 0x04, 0xc9, 0x02, /* bytes 800 - 816 */ + 0xc0, 0x04, 0xca, 0x01, 0xc8, 0x04, 0xcb, 0x01, 0xcc, 0x04, 0xcc, 0x02, 0xd0, 0x04, 0xcd, 0x01, /* bytes 816 - 832 */ + 0xd8, 0x04, 0xce, 0x01, 0xdc, 0x04, 0xcf, 0x01, 0xe0, 0x04, 0xd0, 0x01, 0xe4, 0x04, 0xd1, 0x01, /* bytes 832 - 848 */ + 0xe8, 0x04, 0xd2, 0x01, 0xec, 0x04, 0xd3, 0x01, 0xf0, 0x04, 0xd4, 0x02, 0xf4, 0x04, 0xd5, 0x01, /* bytes 848 - 864 */ + 0xfc, 0x04, 0xd6, 0x01, 0x00, 0x05, 0xd7, 0x00, 0x04, 0x05, 0xd8, 0x01, 0x04, 0x05, 0xd9, 0x01, /* bytes 864 - 880 */ + 0x08, 0x05, 0xda, 0x02, 0x0c, 0x05, 0xdb, 0x02, 0x14, 0x05, 0xdc, 0x02, 0x1c, 0x05, 0xdd, 0x02, /* bytes 880 - 896 */ + 0x24, 0x05, 0xde, 0x02, 0x2c, 0x05, 0xdf, 0x02, 0x34, 0x05, 0xe0, 0x01, 0x3c, 0x05, 0xe1, 0x01, /* bytes 896 - 912 */ + 0x40, 0x05, 0xe2, 0x01, 0x44, 0x05, 0xe3, 0x01, 0x48, 0x05, 0xe4, 0x01, 0x4c, 0x05, 0xe5, 0x01, /* bytes 912 - 928 */ + 0x50, 0x05, 0xe6, 0x01, 0x54, 0x05, 0xe7, 0x01, 0x58, 0x05, 0xe8, 0x01, 0x5c, 0x05, 0xe9, 0x01, /* bytes 928 - 944 */ + 0x60, 0x05, 0xea, 0x01, 0x64, 0x05, 0xeb, 0x01, 0x68, 0x05, 0xec, 0x01, 0x6c, 0x05, 0xed, 0x01, /* bytes 944 - 960 */ + 0x70, 0x05, 0xee, 0x01, 0x74, 0x05, 0xef, 0x02, 0x78, 0x05, 0xf0, 0x01, 0x80, 0x05, 0xf1, 0x01, /* bytes 960 - 976 */ + 0x84, 0x05, 0xf2, 0x01, 0x88, 0x05, 0xf3, 0x01, 0x8c, 0x05, 0xf4, 0x01, 0x90, 0x05, 0xf5, 0x01, /* bytes 976 - 992 */ + 0x94, 0x05, 0xf6, 0x01, 0x98, 0x05, 0xf7, 0x01, 0x9c, 0x05, 0xf8, 0x01, 0xa0, 0x05, 0xf9, 0x01, /* bytes 992 - 1008 */ + 0xa4, 0x05, 0xfa, 0x01, 0xa8, 0x05, 0xfb, 0x01, 0xac, 0x05, 0xfc, 0x02, 0xb0, 0x05, 0xfd, 0x03, /* bytes 1008 - 1024 */ + 0xb8, 0x05, 0xfe, 0x02, 0xc4, 0x05, 0xff, 0x00, 0xb9, 0x08, 0xfe, 0x01, 0x51, 0x0f, 0x00, 0x01, /* bytes 1024 - 1040 */ + 0xc6, 0x08, 0xff, 0x01, 0x62, 0x0f, 0x01, 0x01, 0xd3, 0x08, 0x02, 0x01, 0xe0, 0x08, 0x03, 0x01, /* bytes 1040 - 1056 */ + 0xed, 0x08, 0x04, 0x01, 0xfa, 0x08, 0x05, 0x01, 0x0b, 0x09, 0x06, 0x01, 0x18, 0x09, 0x07, 0x01, /* bytes 1056 - 1072 */ + 0x25, 0x09, 0x08, 0x01, 0x32, 0x09, 0x09, 0x01, 0x3f, 0x09, 0x0a, 0x01, 0x4c, 0x09, 0x0b, 0x01, /* bytes 1072 - 1088 */ + 0x59, 0x09, 0x0c, 0x01, 0x66, 0x09, 0x0d, 0x01, 0x73, 0x09, 0x0e, 0x01, 0x80, 0x09, 0x0f, 0x01, /* bytes 1088 - 1104 */ + 0x8d, 0x09, 0x10, 0x01, 0x9a, 0x09, 0x11, 0x01, 0xa7, 0x09, 0x12, 0x01, 0xb4, 0x09, 0x13, 0x01, /* bytes 1104 - 1120 */ + 0xc1, 0x09, 0x14, 0x01, 0xce, 0x09, 0x15, 0x01, 0xdb, 0x09, 0x16, 0x01, 0xe8, 0x09, 0x17, 0x01, /* bytes 1120 - 1136 */ + 0xf5, 0x09, 0x18, 0x01, 0x02, 0x0a, 0x19, 0x01, 0x0f, 0x0a, 0x18, 0x02, 0x6f, 0x0f, 0x1a, 0x01, /* bytes 1136 - 1152 */ + 0x1c, 0x0a, 0x19, 0x02, 0x7c, 0x0f, 0x1b, 0x01, 0x29, 0x0a, 0x1a, 0x02, 0x89, 0x0f, 0x1c, 0x01, /* bytes 1152 - 1168 */ + 0x36, 0x0a, 0x1b, 0x02, 0x96, 0x0f, 0x1d, 0x01, 0x43, 0x0a, 0x1e, 0x01, 0x50, 0x0a, 0x20, 0x00, /* bytes 1168 - 1184 */ + 0x11, 0x00, 0x1f, 0x01, 0x5d, 0x0a, 0x21, 0x00, 0x16, 0x00, 0x20, 0x01, 0x6a, 0x0a, 0x22, 0x00, /* bytes 1184 - 1200 */ + 0x1f, 0x00, 0x21, 0x01, 0x77, 0x0a, 0x23, 0x00, 0x28, 0x00, 0x22, 0x01, 0x84, 0x0a, 0x24, 0x00, /* bytes 1200 - 1216 */ + 0x35, 0x00, 0x23, 0x01, 0x91, 0x0a, 0x02, 0x22, 0xbb, 0x10, 0x25, 0x00, 0x42, 0x00, 0x24, 0x01, /* bytes 1216 - 1232 */ + 0x9e, 0x0a, 0x26, 0x00, 0x53, 0x00, 0x25, 0x01, 0xab, 0x0a, 0x27, 0x00, 0x60, 0x00, 0x26, 0x01, /* bytes 1232 - 1248 */ + 0xb8, 0x0a, 0x28, 0x00, 0x69, 0x00, 0x27, 0x01, 0xc5, 0x0a, 0x06, 0x22, 0xc8, 0x10, 0x29, 0x00, /* bytes 1248 - 1264 */ + 0x76, 0x00, 0x28, 0x01, 0xd2, 0x0a, 0x2a, 0x00, 0x83, 0x00, 0x29, 0x01, 0xdf, 0x0a, 0x2b, 0x00, /* bytes 1264 - 1280 */ + 0x8c, 0x00, 0x2a, 0x01, 0xec, 0x0a, 0x2c, 0x00, 0x95, 0x00, 0x2b, 0x01, 0xf9, 0x0a, 0x2d, 0x00, /* bytes 1280 - 1296 */ + 0x9e, 0x00, 0x2c, 0x01, 0x02, 0x0b, 0x2e, 0x00, 0xa7, 0x00, 0x2d, 0x01, 0x0f, 0x0b, 0x2f, 0x00, /* bytes 1296 - 1312 */ + 0xb0, 0x00, 0x2e, 0x01, 0x1c, 0x0b, 0x30, 0x00, 0xbd, 0x00, 0x2f, 0x01, 0x29, 0x0b, 0x31, 0x00, /* bytes 1312 - 1328 */ + 0xca, 0x00, 0x30, 0x01, 0x36, 0x0b, 0x0f, 0x22, 0xd5, 0x10, 0x32, 0x00, 0xd3, 0x00, 0x31, 0x01, /* bytes 1328 - 1344 */ + 0x3f, 0x0b, 0x33, 0x00, 0xe0, 0x00, 0x32, 0x01, 0x48, 0x0b, 0x13, 0x20, 0xf4, 0x0f, 0x11, 0x22, /* bytes 1344 - 1360 */ + 0xe2, 0x10, 0x34, 0x00, 0xed, 0x00, 0x33, 0x01, 0x55, 0x0b, 0x14, 0x20, 0xfd, 0x0f, 0x12, 0x22, /* bytes 1360 - 1376 */ + 0xef, 0x10, 0x35, 0x00, 0xfa, 0x00, 0x34, 0x01, 0x62, 0x0b, 0x36, 0x00, 0x07, 0x01, 0x35, 0x01, /* bytes 1376 - 1392 */ + 0x6f, 0x0b, 0x37, 0x00, 0x14, 0x01, 0x36, 0x01, 0x7c, 0x0b, 0x38, 0x00, 0x21, 0x01, 0x37, 0x01, /* bytes 1392 - 1408 */ + 0x89, 0x0b, 0x18, 0x20, 0x06, 0x10, 0x39, 0x00, 0x2e, 0x01, 0x38, 0x01, 0x96, 0x0b, 0x19, 0x20, /* bytes 1408 - 1424 */ + 0x0f, 0x10, 0x3a, 0x00, 0x3b, 0x01, 0x39, 0x01, 0x9f, 0x0b, 0x1a, 0x20, 0x18, 0x10, 0x3b, 0x00, /* bytes 1424 - 1440 */ + 0x44, 0x01, 0x3a, 0x01, 0xac, 0x0b, 0x3c, 0x00, 0x4d, 0x01, 0x3b, 0x01, 0xb5, 0x0b, 0x1c, 0x20, /* bytes 1440 - 1456 */ + 0x21, 0x10, 0x1a, 0x22, 0xf8, 0x10, 0x3d, 0x00, 0x56, 0x01, 0x3c, 0x01, 0xc2, 0x0b, 0x1d, 0x20, /* bytes 1456 - 1472 */ + 0x2a, 0x10, 0x3e, 0x00, 0x5f, 0x01, 0x3d, 0x01, 0xcb, 0x0b, 0x1e, 0x20, 0x33, 0x10, 0x3f, 0x00, /* bytes 1472 - 1488 */ + 0x68, 0x01, 0x3e, 0x01, 0xd8, 0x0b, 0x40, 0x00, 0x75, 0x01, 0x3f, 0x01, 0xe5, 0x0b, 0x20, 0x20, /* bytes 1488 - 1504 */ + 0x3c, 0x10, 0x1e, 0x22, 0x09, 0x11, 0x41, 0x00, 0x86, 0x01, 0x40, 0x01, 0xf2, 0x0b, 0x21, 0x20, /* bytes 1504 - 1520 */ + 0x45, 0x10, 0x42, 0x00, 0x93, 0x01, 0x41, 0x01, 0xfb, 0x0b, 0x22, 0x20, 0x4e, 0x10, 0x43, 0x00, /* bytes 1520 - 1536 */ + 0xa0, 0x01, 0x42, 0x01, 0x08, 0x0c, 0x22, 0x21, 0xa1, 0x10, 0x44, 0x00, 0xad, 0x01, 0x43, 0x01, /* bytes 1536 - 1552 */ + 0x11, 0x0c, 0x45, 0x00, 0xba, 0x01, 0x44, 0x01, 0x1e, 0x0c, 0x46, 0x00, 0xc7, 0x01, 0x45, 0x01, /* bytes 1552 - 1568 */ + 0x2b, 0x0c, 0x26, 0x20, 0x57, 0x10, 0x47, 0x00, 0xd4, 0x01, 0x46, 0x01, 0x38, 0x0c, 0x26, 0x21, /* bytes 1568 - 1584 */ + 0xae, 0x10, 0x48, 0x00, 0xe1, 0x01, 0x47, 0x01, 0x45, 0x0c, 0x49, 0x00, 0xee, 0x01, 0x48, 0x01, /* bytes 1584 - 1600 */ + 0x52, 0x0c, 0x4a, 0x00, 0xf7, 0x01, 0x49, 0x01, 0x5f, 0x0c, 0x4b, 0x00, 0x00, 0x02, 0x4a, 0x01, /* bytes 1600 - 1616 */ + 0x6c, 0x0c, 0x4c, 0x00, 0x0d, 0x02, 0x4b, 0x01, 0x79, 0x0c, 0x4d, 0x00, 0x1a, 0x02, 0x4c, 0x01, /* bytes 1616 - 1632 */ + 0x86, 0x0c, 0x2b, 0x22, 0x16, 0x11, 0x4e, 0x00, 0x27, 0x02, 0x4d, 0x01, 0x93, 0x0c, 0x4f, 0x00, /* bytes 1632 - 1648 */ + 0x34, 0x02, 0x4e, 0x01, 0xa0, 0x0c, 0x50, 0x00, 0x41, 0x02, 0x4f, 0x01, 0xad, 0x0c, 0x30, 0x20, /* bytes 1648 - 1664 */ + 0x60, 0x10, 0x51, 0x00, 0x4e, 0x02, 0x50, 0x01, 0xba, 0x0c, 0x52, 0x00, 0x5b, 0x02, 0x51, 0x01, /* bytes 1664 - 1680 */ + 0xc7, 0x0c, 0x53, 0x00, 0x68, 0x02, 0x52, 0x01, 0xd4, 0x0c, 0x54, 0x00, 0x75, 0x02, 0x53, 0x01, /* bytes 1680 - 1696 */ + 0xe5, 0x0c, 0x55, 0x00, 0x82, 0x02, 0x54, 0x01, 0xf2, 0x0c, 0x56, 0x00, 0x8f, 0x02, 0x55, 0x01, /* bytes 1696 - 1712 */ + 0xff, 0x0c, 0x57, 0x00, 0x9c, 0x02, 0x56, 0x01, 0x08, 0x0d, 0x58, 0x00, 0xad, 0x02, 0x57, 0x01, /* bytes 1712 - 1728 */ + 0x15, 0x0d, 0x59, 0x00, 0xba, 0x02, 0x58, 0x01, 0x1e, 0x0d, 0x39, 0x20, 0x75, 0x10, 0x5a, 0x00, /* bytes 1728 - 1744 */ + 0xc7, 0x02, 0x59, 0x01, 0x2b, 0x0d, 0x3a, 0x20, 0x7e, 0x10, 0x5b, 0x00, 0xd4, 0x02, 0x5a, 0x01, /* bytes 1744 - 1760 */ + 0x34, 0x0d, 0x5c, 0x00, 0xdd, 0x02, 0x5b, 0x01, 0x41, 0x0d, 0x5d, 0x00, 0xea, 0x02, 0x5c, 0x01, /* bytes 1760 - 1776 */ + 0x4e, 0x0d, 0x5e, 0x00, 0xf3, 0x02, 0x5d, 0x01, 0x5b, 0x0d, 0x5f, 0x00, 0xfc, 0x02, 0x5e, 0x01, /* bytes 1776 - 1792 */ + 0x68, 0x0d, 0x60, 0x00, 0x05, 0x03, 0x5f, 0x01, 0x75, 0x0d, 0x61, 0x00, 0x0e, 0x03, 0x60, 0x01, /* bytes 1792 - 1808 */ + 0x82, 0x0d, 0x62, 0x00, 0x17, 0x03, 0x61, 0x01, 0x8f, 0x0d, 0x63, 0x00, 0x24, 0x03, 0x62, 0x01, /* bytes 1808 - 1824 */ + 0x9c, 0x0d, 0x64, 0x00, 0x2d, 0x03, 0x63, 0x01, 0xa9, 0x0d, 0x44, 0x20, 0x87, 0x10, 0x65, 0x00, /* bytes 1824 - 1840 */ + 0x3a, 0x03, 0x64, 0x01, 0xb6, 0x0d, 0x66, 0x00, 0x43, 0x03, 0x65, 0x01, 0xc3, 0x0d, 0x67, 0x00, /* bytes 1840 - 1856 */ + 0x50, 0x03, 0x66, 0x01, 0xd0, 0x0d, 0x68, 0x00, 0x5d, 0x03, 0x67, 0x01, 0xdd, 0x0d, 0x69, 0x00, /* bytes 1856 - 1872 */ + 0x6a, 0x03, 0x68, 0x01, 0xe6, 0x0d, 0x6a, 0x00, 0x73, 0x03, 0x69, 0x01, 0xf3, 0x0d, 0x48, 0x22, /* bytes 1872 - 1888 */ + 0x27, 0x11, 0x6b, 0x00, 0x7c, 0x03, 0x6a, 0x01, 0x00, 0x0e, 0x6c, 0x00, 0x89, 0x03, 0x6b, 0x01, /* bytes 1888 - 1904 */ + 0x0d, 0x0e, 0x6d, 0x00, 0x92, 0x03, 0x6c, 0x01, 0x1a, 0x0e, 0x6e, 0x00, 0x9f, 0x03, 0x6d, 0x01, /* bytes 1904 - 1920 */ + 0x27, 0x0e, 0x6f, 0x00, 0xa8, 0x03, 0x6e, 0x01, 0x34, 0x0e, 0x70, 0x00, 0xb1, 0x03, 0x6f, 0x01, /* bytes 1920 - 1936 */ + 0x45, 0x0e, 0x71, 0x00, 0xbe, 0x03, 0x70, 0x01, 0x52, 0x0e, 0x72, 0x00, 0xcb, 0x03, 0x71, 0x01, /* bytes 1936 - 1952 */ + 0x5f, 0x0e, 0x73, 0x00, 0xd4, 0x03, 0x72, 0x01, 0x6c, 0x0e, 0x74, 0x00, 0xdd, 0x03, 0x73, 0x01, /* bytes 1952 - 1968 */ + 0x79, 0x0e, 0x75, 0x00, 0xe6, 0x03, 0x74, 0x01, 0x86, 0x0e, 0x76, 0x00, 0xef, 0x03, 0x75, 0x01, /* bytes 1968 - 1984 */ + 0x9b, 0x0e, 0x77, 0x00, 0xfc, 0x03, 0x76, 0x01, 0xac, 0x0e, 0x78, 0x00, 0x09, 0x04, 0x77, 0x01, /* bytes 1984 - 2000 */ + 0xb9, 0x0e, 0x79, 0x00, 0x12, 0x04, 0x78, 0x01, 0xc6, 0x0e, 0x7a, 0x00, 0x1f, 0x04, 0x79, 0x01, /* bytes 2000 - 2016 */ + 0xd3, 0x0e, 0x7b, 0x00, 0x28, 0x04, 0x7a, 0x01, 0xe0, 0x0e, 0x7c, 0x00, 0x35, 0x04, 0x7b, 0x01, /* bytes 2016 - 2032 */ + 0xe9, 0x0e, 0x7d, 0x00, 0x3e, 0x04, 0x7c, 0x01, 0xf6, 0x0e, 0x7e, 0x00, 0x4b, 0x04, 0x7d, 0x01, /* bytes 2032 - 2048 */ + 0xff, 0x0e, 0x7e, 0x01, 0x0c, 0x0f, 0x7f, 0x01, 0x15, 0x0f, 0x60, 0x22, 0x30, 0x11, 0x64, 0x22, /* bytes 2048 - 2064 */ + 0x3d, 0x11, 0x65, 0x22, 0x46, 0x11, 0x92, 0x01, 0x22, 0x0f, 0xa0, 0x00, 0x54, 0x04, 0xa1, 0x00, /* bytes 2064 - 2080 */ + 0x59, 0x04, 0xa2, 0x00, 0x62, 0x04, 0xa3, 0x00, 0x6f, 0x04, 0xa4, 0x00, 0x7c, 0x04, 0xa5, 0x00, /* bytes 2080 - 2096 */ + 0x85, 0x04, 0xa6, 0x00, 0x92, 0x04, 0xa7, 0x00, 0x9b, 0x04, 0xa8, 0x00, 0xa8, 0x04, 0xa9, 0x00, /* bytes 2096 - 2112 */ + 0xb1, 0x04, 0xaa, 0x00, 0xc6, 0x04, 0xab, 0x00, 0xcf, 0x04, 0xac, 0x00, 0xd8, 0x04, 0xae, 0x00, /* bytes 2112 - 2128 */ + 0xe1, 0x04, 0xaf, 0x00, 0xee, 0x04, 0xb0, 0x00, 0xf7, 0x04, 0xb1, 0x00, 0x00, 0x05, 0xb2, 0x00, /* bytes 2128 - 2144 */ + 0x09, 0x05, 0xb3, 0x00, 0x12, 0x05, 0xb4, 0x00, 0x1b, 0x05, 0xb5, 0x00, 0x24, 0x05, 0xb6, 0x00, /* bytes 2144 - 2160 */ + 0x31, 0x05, 0xb7, 0x00, 0x42, 0x05, 0xb8, 0x00, 0x4b, 0x05, 0xb9, 0x00, 0x54, 0x05, 0xba, 0x00, /* bytes 2160 - 2176 */ + 0x5d, 0x05, 0xc3, 0xf6, 0x58, 0x11, 0xbb, 0x00, 0x66, 0x05, 0xbc, 0x00, 0x6f, 0x05, 0xbd, 0x00, /* bytes 2176 - 2192 */ + 0x80, 0x05, 0xbe, 0x00, 0x91, 0x05, 0xbf, 0x00, 0xa2, 0x05, 0xc0, 0x00, 0xaf, 0x05, 0xc1, 0x00, /* bytes 2192 - 2208 */ + 0xbc, 0x05, 0xc2, 0x00, 0xc9, 0x05, 0xc3, 0x00, 0xd6, 0x05, 0xc0, 0x03, 0xeb, 0x0f, 0xc4, 0x00, /* bytes 2208 - 2224 */ + 0xe3, 0x05, 0xc5, 0x00, 0xf0, 0x05, 0xc6, 0x00, 0xfd, 0x05, 0xc7, 0x00, 0x0a, 0x06, 0xc8, 0x00, /* bytes 2224 - 2240 */ + 0x17, 0x06, 0xc6, 0x02, 0xa3, 0x0f, 0xc9, 0x00, 0x24, 0x06, 0xc7, 0x02, 0xac, 0x0f, 0xca, 0x00, /* bytes 2240 - 2256 */ + 0x31, 0x06, 0xcb, 0x00, 0x3e, 0x06, 0xcc, 0x00, 0x4b, 0x06, 0xac, 0x20, 0x94, 0x10, 0xcd, 0x00, /* bytes 2256 - 2272 */ + 0x54, 0x06, 0xce, 0x00, 0x5d, 0x06, 0xcf, 0x00, 0x6a, 0x06, 0xd0, 0x00, 0x77, 0x06, 0xd1, 0x00, /* bytes 2272 - 2288 */ + 0x84, 0x06, 0xd2, 0x00, 0x91, 0x06, 0xd3, 0x00, 0x9e, 0x06, 0xd4, 0x00, 0xab, 0x06, 0xaf, 0x25, /* bytes 2288 - 2304 */ + 0x04, 0x00, 0xd5, 0x00, 0xb8, 0x06, 0xd6, 0x00, 0xc5, 0x06, 0xd8, 0x00, 0xd2, 0x06, 0xd9, 0x00, /* bytes 2304 - 2320 */ + 0xdf, 0x06, 0xda, 0x00, 0xec, 0x06, 0xd8, 0x02, 0xb5, 0x0f, 0xdb, 0x00, 0xf9, 0x06, 0xd9, 0x02, /* bytes 2320 - 2336 */ + 0xbe, 0x0f, 0xdc, 0x00, 0x06, 0x07, 0xda, 0x02, 0xc7, 0x0f, 0xdd, 0x00, 0x13, 0x07, 0xdb, 0x02, /* bytes 2336 - 2352 */ + 0xd0, 0x0f, 0xde, 0x00, 0x20, 0x07, 0xdc, 0x02, 0xd9, 0x0f, 0xdf, 0x00, 0x2d, 0x07, 0xdd, 0x02, /* bytes 2352 - 2368 */ + 0xe2, 0x0f, 0xe0, 0x00, 0x3a, 0x07, 0xe1, 0x00, 0x47, 0x07, 0xe2, 0x00, 0x54, 0x07, 0xe3, 0x00, /* bytes 2368 - 2384 */ + 0x61, 0x07, 0xe4, 0x00, 0x6e, 0x07, 0xe5, 0x00, 0x7b, 0x07, 0xe6, 0x00, 0x88, 0x07, 0xe7, 0x00, /* bytes 2384 - 2400 */ + 0x95, 0x07, 0xe8, 0x00, 0xa2, 0x07, 0xe9, 0x00, 0xaf, 0x07, 0xea, 0x00, 0xbc, 0x07, 0xeb, 0x00, /* bytes 2400 - 2416 */ + 0xc9, 0x07, 0xec, 0x00, 0xd6, 0x07, 0xed, 0x00, 0xdf, 0x07, 0xee, 0x00, 0xe8, 0x07, 0xef, 0x00, /* bytes 2416 - 2432 */ + 0xf1, 0x07, 0xca, 0x25, 0x4f, 0x11, 0xf0, 0x00, 0xfa, 0x07, 0xf1, 0x00, 0x07, 0x08, 0xf2, 0x00, /* bytes 2432 - 2448 */ + 0x14, 0x08, 0xf3, 0x00, 0x21, 0x08, 0xf4, 0x00, 0x2e, 0x08, 0xf5, 0x00, 0x3b, 0x08, 0xf6, 0x00, /* bytes 2448 - 2464 */ + 0x48, 0x08, 0xf7, 0x00, 0x55, 0x08, 0xf8, 0x00, 0x5e, 0x08, 0xf9, 0x00, 0x6b, 0x08, 0xfa, 0x00, /* bytes 2464 - 2480 */ + 0x78, 0x08, 0xfb, 0x00, 0x85, 0x08, 0xfc, 0x00, 0x92, 0x08, 0x01, 0xfb, 0x61, 0x11, 0xfd, 0x00, /* bytes 2480 - 2496 */ + 0x9f, 0x08, 0xfc, 0x01, 0x2f, 0x0f, 0x02, 0xfb, 0x6e, 0x11, 0xfe, 0x00, 0xac, 0x08, 0xfd, 0x01, /* bytes 2496 - 2512 */ + 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x3f, 0xc6, 0x18, 0x63, 0x8c, /* bytes 2512 - 2528 */ + 0x31, 0xc6, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x03, 0x01, 0x09, 0x01, 0x05, 0x03, 0x3f, 0x01, 0x00, /* bytes 2528 - 2544 */ + 0x00, 0x03, 0x04, 0x01, 0x05, 0x05, 0x6d, 0x0b, 0x00, 0x00, 0x07, 0x06, 0x00, 0x05, 0x07, 0x28, /* bytes 2544 - 2560 */ + 0x3f, 0x85, 0xf2, 0x53, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x01, 0x04, 0x07, 0xc4, 0xd5, 0x6a, 0x18, /* bytes 2560 - 2576 */ + 0xad, 0xd5, 0x11, 0x00, 0x09, 0x09, 0x01, 0x05, 0x0b, 0x06, 0xf3, 0x25, 0x49, 0x61, 0x0d, 0x25, /* bytes 2576 - 2592 */ + 0x49, 0x91, 0xc1, 0x00, 0x00, 0x06, 0x09, 0x01, 0x05, 0x08, 0x46, 0x92, 0x18, 0x66, 0x9a, 0x65, /* bytes 2592 - 2608 */ + 0x26, 0x00, 0x01, 0x04, 0x01, 0x05, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x01, 0x04, 0x05, /* bytes 2608 - 2624 */ + 0x94, 0x92, 0x24, 0x12, 0x01, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x01, 0x04, 0x05, 0x91, 0x48, 0x92, /* bytes 2624 - 2640 */ + 0x52, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x01, 0x05, 0x07, 0xa4, 0x3a, 0x57, 0x09, 0x05, 0x05, /* bytes 2640 - 2656 */ + 0x01, 0x08, 0x07, 0x84, 0x7c, 0x42, 0x00, 0x02, 0x04, 0x00, 0x0c, 0x03, 0x6a, 0x00, 0x00, 0x00, /* bytes 2656 - 2672 */ + 0x03, 0x01, 0x01, 0x0a, 0x05, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x0c, 0x03, 0x03, 0x00, /* bytes 2672 - 2688 */ + 0x00, 0x00, 0x04, 0x0b, 0x01, 0x04, 0x06, 0x88, 0x44, 0x24, 0x22, 0x11, 0x01, 0x00, 0x00, 0x04, /* bytes 2688 - 2704 */ + 0x09, 0x01, 0x05, 0x06, 0x96, 0x99, 0x99, 0x99, 0x06, 0x00, 0x00, 0x00, 0x03, 0x09, 0x02, 0x05, /* bytes 2704 - 2720 */ + 0x06, 0x9a, 0x24, 0x49, 0x07, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x89, 0x24, 0x11, 0x0f, 0x00, /* bytes 2720 - 2736 */ + 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x88, 0x86, 0x98, 0x06, 0x00, 0x00, 0x00, 0x05, /* bytes 2736 - 2752 */ + 0x09, 0x00, 0x05, 0x06, 0x88, 0x31, 0xa5, 0x3e, 0x42, 0x08, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, /* bytes 2752 - 2768 */ + 0x06, 0x1f, 0x71, 0x89, 0x99, 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x19, /* bytes 2768 - 2784 */ + 0x97, 0x99, 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x8f, 0x44, 0x24, 0x22, 0x02, /* bytes 2784 - 2800 */ + 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x99, 0x96, 0x99, 0x06, 0x00, 0x00, 0x00, /* bytes 2800 - 2816 */ + 0x04, 0x09, 0x01, 0x05, 0x06, 0x96, 0x99, 0x8e, 0x99, 0x06, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, /* bytes 2816 - 2832 */ + 0x07, 0x03, 0x63, 0x00, 0x00, 0x00, 0x02, 0x09, 0x00, 0x07, 0x03, 0x0a, 0xa8, 0x01, 0x00, 0x04, /* bytes 2832 - 2848 */ + 0x07, 0x01, 0x07, 0x06, 0x48, 0x12, 0x42, 0x08, 0x05, 0x03, 0x01, 0x09, 0x07, 0x1f, 0x7c, 0x00, /* bytes 2848 - 2864 */ + 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x21, 0x84, 0x24, 0x01, 0x04, 0x09, 0x00, 0x05, 0x05, 0x96, /* bytes 2864 - 2880 */ + 0x48, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x09, 0x01, 0x05, 0x0a, 0x3c, 0x42, 0xb9, 0xa5, /* bytes 2880 - 2896 */ + 0xa5, 0xa5, 0x59, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x84, 0x10, 0xa5, /* bytes 2896 - 2912 */ + 0xd4, 0x8f, 0x11, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x2f, 0xc6, 0xf8, 0x62, 0x8c, 0x0f, /* bytes 2912 - 2928 */ + 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x2e, 0xc6, 0x10, 0x42, 0x8c, 0x0e, 0x00, 0x00, 0x05, /* bytes 2928 - 2944 */ + 0x09, 0x01, 0x05, 0x07, 0x2f, 0xc6, 0x18, 0x63, 0x8c, 0x0f, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, /* bytes 2944 - 2960 */ + 0x06, 0x1f, 0x11, 0x17, 0x11, 0x0f, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x05, 0x1f, 0x11, /* bytes 2960 - 2976 */ + 0x17, 0x11, 0x01, 0x00, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x2e, 0xc6, 0x10, 0x72, 0x8c, /* bytes 2976 - 2992 */ + 0x1e, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x31, 0xc6, 0xf8, 0x63, 0x8c, 0x11, 0x00, 0x00, /* bytes 2992 - 3008 */ + 0x01, 0x09, 0x01, 0x05, 0x03, 0xff, 0x01, 0x00, 0x00, 0x03, 0x09, 0x00, 0x05, 0x04, 0x24, 0x49, /* bytes 3008 - 3024 */ + 0x92, 0x03, 0x05, 0x09, 0x01, 0x05, 0x06, 0x31, 0xa5, 0x32, 0x4a, 0x4a, 0x11, 0x00, 0x00, 0x04, /* bytes 3024 - 3040 */ + 0x09, 0x01, 0x05, 0x05, 0x11, 0x11, 0x11, 0x11, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x09, 0x01, 0x05, /* bytes 3040 - 3056 */ + 0x09, 0xe3, 0xf1, 0xb8, 0x5a, 0x4d, 0x26, 0x93, 0x41, 0x05, 0x09, 0x01, 0x05, 0x07, 0x71, 0xce, /* bytes 3056 - 3072 */ + 0x5a, 0x73, 0x8e, 0x11, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x2e, 0xc6, 0x18, 0x63, 0x8c, /* bytes 3072 - 3088 */ + 0x0e, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x2f, 0xc6, 0xf8, 0x42, 0x08, 0x01, 0x00, 0x00, /* bytes 3088 - 3104 */ + 0x05, 0x0b, 0x01, 0x05, 0x07, 0x2e, 0xc6, 0x18, 0x63, 0x8c, 0x8e, 0x60, 0x00, 0x05, 0x09, 0x01, /* bytes 3104 - 3120 */ + 0x05, 0x07, 0x2f, 0xc6, 0xf8, 0x52, 0x8a, 0x11, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x2e, /* bytes 3120 - 3136 */ + 0x46, 0x41, 0x50, 0x8c, 0x0e, 0x00, 0x00, 0x05, 0x09, 0x00, 0x05, 0x05, 0x9f, 0x10, 0x42, 0x08, /* bytes 3136 - 3152 */ + 0x21, 0x04, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x31, 0xc6, 0x18, 0x63, 0x8c, 0x0e, 0x00, /* bytes 3152 - 3168 */ + 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x31, 0x46, 0xa5, 0x14, 0x21, 0x04, 0x00, 0x00, 0x09, 0x09, /* bytes 3168 - 3184 */ + 0x01, 0x05, 0x0b, 0x11, 0x23, 0x46, 0x54, 0xa5, 0x4a, 0x15, 0x11, 0x22, 0x44, 0x00, 0x00, 0x05, /* bytes 3184 - 3200 */ + 0x09, 0x01, 0x05, 0x07, 0x31, 0x2a, 0x45, 0x94, 0x8a, 0x11, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, /* bytes 3200 - 3216 */ + 0x07, 0x31, 0x2a, 0x45, 0x08, 0x21, 0x04, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x8f, 0x44, /* bytes 3216 - 3232 */ + 0x22, 0x11, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x01, 0x04, 0x04, 0x57, 0x55, 0x35, 0x00, 0x04, /* bytes 3232 - 3248 */ + 0x0b, 0x01, 0x04, 0x06, 0x11, 0x22, 0x42, 0x44, 0x88, 0x08, 0x00, 0x00, 0x02, 0x0b, 0x01, 0x04, /* bytes 3248 - 3264 */ + 0x04, 0xab, 0xaa, 0x3a, 0x00, 0x03, 0x02, 0x02, 0x04, 0x07, 0x2a, 0x00, 0x00, 0x00, 0x06, 0x01, /* bytes 3264 - 3280 */ + 0x00, 0x0e, 0x06, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x04, 0x05, 0x09, 0x00, 0x00, 0x00, /* bytes 3280 - 3296 */ + 0x04, 0x07, 0x01, 0x07, 0x06, 0x96, 0xe8, 0x99, 0x0e, 0x04, 0x09, 0x01, 0x05, 0x06, 0x11, 0x97, /* bytes 3296 - 3312 */ + 0x99, 0x99, 0x07, 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x96, 0x11, 0x91, 0x06, 0x04, /* bytes 3312 - 3328 */ + 0x09, 0x01, 0x05, 0x06, 0x88, 0x9e, 0x99, 0x99, 0x0e, 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, 0x07, /* bytes 3328 - 3344 */ + 0x06, 0x96, 0xf9, 0x91, 0x06, 0x04, 0x09, 0x00, 0x05, 0x04, 0x2c, 0x27, 0x22, 0x22, 0x02, 0x00, /* bytes 3344 - 3360 */ + 0x00, 0x00, 0x04, 0x0a, 0x01, 0x06, 0x06, 0x68, 0x99, 0x16, 0x96, 0x69, 0x00, 0x00, 0x00, 0x04, /* bytes 3360 - 3376 */ + 0x09, 0x01, 0x05, 0x06, 0x11, 0xbd, 0x99, 0x99, 0x09, 0x00, 0x00, 0x00, 0x01, 0x09, 0x01, 0x05, /* bytes 3376 - 3392 */ + 0x03, 0xfd, 0x01, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x05, 0x03, 0xa2, 0xaa, 0x1a, 0x00, 0x04, 0x09, /* bytes 3392 - 3408 */ + 0x01, 0x05, 0x06, 0x11, 0x99, 0x35, 0x95, 0x09, 0x00, 0x00, 0x00, 0x01, 0x09, 0x01, 0x05, 0x03, /* bytes 3408 - 3424 */ + 0xff, 0x01, 0x00, 0x00, 0x07, 0x07, 0x01, 0x07, 0x09, 0xed, 0x6d, 0x32, 0x99, 0x4c, 0x26, 0x01, /* bytes 3424 - 3440 */ + 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0xbd, 0x99, 0x99, 0x09, 0x04, 0x07, 0x01, 0x07, 0x06, 0x96, /* bytes 3440 - 3456 */ + 0x99, 0x99, 0x06, 0x04, 0x09, 0x01, 0x07, 0x06, 0x97, 0x99, 0x99, 0x17, 0x01, 0x00, 0x00, 0x00, /* bytes 3456 - 3472 */ + 0x04, 0x09, 0x01, 0x07, 0x06, 0x9e, 0x99, 0x99, 0x8e, 0x08, 0x00, 0x00, 0x00, 0x03, 0x07, 0x01, /* bytes 3472 - 3488 */ + 0x07, 0x04, 0x5d, 0x92, 0x04, 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x96, 0x42, 0x98, 0x06, 0x03, /* bytes 3488 - 3504 */ + 0x09, 0x00, 0x05, 0x04, 0xd2, 0x25, 0x49, 0x06, 0x04, 0x07, 0x01, 0x07, 0x06, 0x99, 0x99, 0xd9, /* bytes 3504 - 3520 */ + 0x0b, 0x05, 0x07, 0x01, 0x07, 0x07, 0x31, 0x2a, 0xa5, 0x08, 0x01, 0x00, 0x00, 0x00, 0x07, 0x07, /* bytes 3520 - 3536 */ + 0x01, 0x07, 0x09, 0xc9, 0x64, 0xb5, 0x2a, 0x12, 0x89, 0x00, 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, /* bytes 3536 - 3552 */ + 0x99, 0x66, 0x96, 0x09, 0x05, 0x09, 0x01, 0x07, 0x07, 0x31, 0x2a, 0xa5, 0x08, 0x21, 0x03, 0x00, /* bytes 3552 - 3568 */ + 0x00, 0x03, 0x07, 0x01, 0x07, 0x05, 0xa7, 0x94, 0x1c, 0x00, 0x03, 0x0b, 0x00, 0x04, 0x04, 0x94, /* bytes 3568 - 3584 */ + 0xa4, 0x48, 0x12, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0b, 0x01, 0x04, 0x03, 0xff, 0x07, 0x00, 0x00, /* bytes 3584 - 3600 */ + 0x03, 0x0b, 0x01, 0x04, 0x04, 0x91, 0x24, 0x4a, 0x52, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x01, /* bytes 3600 - 3616 */ + 0x04, 0x07, 0xb6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x03, 0x01, 0x09, 0x01, 0x05, 0x03, /* bytes 3616 - 3632 */ + 0xf9, 0x01, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x64, 0x19, 0x11, 0x69, 0x04, 0x00, 0x00, /* bytes 3632 - 3648 */ + 0x00, 0x06, 0x09, 0x00, 0x05, 0x06, 0x8c, 0x24, 0x09, 0x8f, 0x20, 0x98, 0x19, 0x00, 0x06, 0x05, /* bytes 3648 - 3664 */ + 0x00, 0x08, 0x06, 0xad, 0x24, 0x49, 0x2d, 0x05, 0x09, 0x01, 0x05, 0x07, 0x31, 0x2a, 0xf5, 0xc9, /* bytes 3664 - 3680 */ + 0x27, 0x04, 0x00, 0x00, 0x01, 0x0b, 0x01, 0x04, 0x03, 0xdf, 0x07, 0x00, 0x00, 0x04, 0x0b, 0x01, /* bytes 3680 - 3696 */ + 0x04, 0x06, 0x96, 0x21, 0x95, 0x4a, 0x98, 0x06, 0x00, 0x00, 0x03, 0x01, 0x01, 0x05, 0x05, 0x05, /* bytes 3696 - 3712 */ + 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x01, 0x04, 0x0c, 0x78, 0x18, 0x26, 0x53, 0x92, 0x09, 0x26, 0x98, /* bytes 3712 - 3728 */ + 0xa4, 0x4c, 0x86, 0xe1, 0x01, 0x00, 0x00, 0x00, 0x04, 0x07, 0x01, 0x05, 0x06, 0x96, 0x9e, 0x0e, /* bytes 3728 - 3744 */ + 0x0f, 0x06, 0x05, 0x01, 0x08, 0x08, 0xa4, 0x94, 0x48, 0x24, 0x05, 0x03, 0x01, 0x09, 0x07, 0x1f, /* bytes 3744 - 3760 */ + 0x42, 0x00, 0x00, 0x07, 0x08, 0x01, 0x04, 0x09, 0x1c, 0x51, 0xb3, 0xda, 0xac, 0x8a, 0x38, 0x00, /* bytes 3760 - 3776 */ + 0x04, 0x01, 0x00, 0x05, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x04, 0x04, 0x01, 0x04, 0x06, 0x96, 0x69, /* bytes 3776 - 3792 */ + 0x00, 0x00, 0x05, 0x06, 0x01, 0x08, 0x07, 0x84, 0x7c, 0x42, 0x3e, 0x03, 0x05, 0x01, 0x05, 0x05, /* bytes 3792 - 3808 */ + 0xa3, 0x72, 0x00, 0x00, 0x03, 0x05, 0x01, 0x05, 0x05, 0xa3, 0x38, 0x00, 0x00, 0x02, 0x02, 0x01, /* bytes 3808 - 3824 */ + 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x07, 0x06, 0x99, 0x99, 0xd9, 0x1b, 0x01, /* bytes 3824 - 3840 */ + 0x00, 0x00, 0x00, 0x06, 0x0b, 0x01, 0x05, 0x08, 0xfe, 0x75, 0x5d, 0x16, 0x45, 0x51, 0x14, 0x45, /* bytes 3840 - 3856 */ + 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x09, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, /* bytes 3856 - 3872 */ + 0x0e, 0x02, 0x06, 0x00, 0x00, 0x00, 0x02, 0x05, 0x01, 0x05, 0x05, 0xae, 0x02, 0x00, 0x00, 0x04, /* bytes 3872 - 3888 */ + 0x07, 0x01, 0x05, 0x06, 0x96, 0x99, 0x06, 0x0f, 0x06, 0x05, 0x01, 0x08, 0x08, 0x89, 0x44, 0x4a, /* bytes 3888 - 3904 */ + 0x09, 0x09, 0x09, 0x00, 0x05, 0x0a, 0x42, 0x86, 0x88, 0x10, 0x21, 0x09, 0x19, 0x2a, 0xf2, 0x84, /* bytes 3904 - 3920 */ + 0x00, 0x00, 0x09, 0x09, 0x00, 0x05, 0x0a, 0x42, 0x86, 0x88, 0x10, 0x21, 0x0d, 0x21, 0x22, 0x22, /* bytes 3920 - 3936 */ + 0xc4, 0x01, 0x00, 0x08, 0x09, 0x01, 0x05, 0x0a, 0x23, 0x24, 0x12, 0x14, 0x4b, 0x64, 0x54, 0xf2, /* bytes 3936 - 3952 */ + 0x42, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x05, 0x04, 0x40, 0x24, 0x91, 0x06, 0x00, 0x00, /* bytes 3952 - 3968 */ + 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x82, 0x00, 0x42, 0x88, 0x52, 0xea, 0xc7, 0x08, 0x05, 0x0c, /* bytes 3968 - 3984 */ + 0x01, 0x02, 0x07, 0x88, 0x00, 0x42, 0x88, 0x52, 0xea, 0xc7, 0x08, 0x05, 0x0c, 0x01, 0x02, 0x07, /* bytes 3984 - 4000 */ + 0x44, 0x01, 0x42, 0x88, 0x52, 0xea, 0xc7, 0x08, 0x05, 0x0c, 0x01, 0x02, 0x07, 0xb6, 0x01, 0x42, /* bytes 4000 - 4016 */ + 0x88, 0x52, 0xea, 0xc7, 0x08, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x11, 0x10, 0x42, 0x94, 0x52, 0x3f, /* bytes 4016 - 4032 */ + 0x46, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x2e, 0x46, 0x47, 0x88, 0x52, 0xea, 0xc7, 0x08, 0x07, /* bytes 4032 - 4048 */ + 0x09, 0x01, 0x05, 0x09, 0x7c, 0x06, 0x43, 0xa1, 0x73, 0x24, 0x12, 0x79, 0x05, 0x0b, 0x01, 0x05, /* bytes 4048 - 4064 */ + 0x07, 0x2e, 0xc6, 0x10, 0x42, 0x8c, 0x8e, 0x08, 0x00, 0x04, 0x0c, 0x01, 0x02, 0x06, 0x42, 0xf0, /* bytes 4064 - 4080 */ + 0x11, 0x71, 0x11, 0xf1, 0x00, 0x00, 0x04, 0x0c, 0x01, 0x02, 0x06, 0x24, 0xf0, 0x11, 0x71, 0x11, /* bytes 4080 - 4096 */ + 0xf1, 0x00, 0x00, 0x04, 0x0c, 0x01, 0x02, 0x06, 0xa4, 0xf0, 0x11, 0x71, 0x11, 0xf1, 0x00, 0x00, /* bytes 4096 - 4112 */ + 0x04, 0x0b, 0x01, 0x03, 0x06, 0x09, 0x1f, 0x11, 0x17, 0x11, 0x0f, 0x00, 0x00, 0x02, 0x0c, 0x00, /* bytes 4112 - 4128 */ + 0x02, 0x03, 0x89, 0xaa, 0xaa, 0x00, 0x02, 0x0c, 0x01, 0x02, 0x03, 0x46, 0x55, 0x55, 0x00, 0x03, /* bytes 4128 - 4144 */ + 0x0c, 0x00, 0x02, 0x03, 0x2a, 0x24, 0x49, 0x92, 0x04, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x00, 0x03, /* bytes 4144 - 4160 */ + 0x03, 0x85, 0x24, 0x49, 0x92, 0x00, 0x00, 0x00, 0x00, 0x06, 0x09, 0x00, 0x05, 0x07, 0x9e, 0x28, /* bytes 4160 - 4176 */ + 0x8a, 0xa7, 0x28, 0x8a, 0x1e, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0xb6, 0x81, 0x38, 0x67, 0xad, /* bytes 4176 - 4192 */ + 0x39, 0xc7, 0x08, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x82, 0x00, 0x17, 0x63, 0x8c, 0x31, 0x46, 0x07, /* bytes 4192 - 4208 */ + 0x05, 0x0c, 0x01, 0x02, 0x07, 0x88, 0x00, 0x17, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0c, 0x01, /* bytes 4208 - 4224 */ + 0x02, 0x07, 0x44, 0x01, 0x17, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0c, 0x01, 0x02, 0x07, 0xb6, /* bytes 4224 - 4240 */ + 0x01, 0x17, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x11, 0xb8, 0x18, 0x63, /* bytes 4240 - 4256 */ + 0x8c, 0x31, 0x3a, 0x00, 0x07, 0x09, 0x00, 0x05, 0x07, 0x5c, 0x91, 0x48, 0xa6, 0x32, 0x89, 0x44, /* bytes 4256 - 4272 */ + 0x1d, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x82, 0x80, 0x18, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0c, /* bytes 4272 - 4288 */ + 0x01, 0x02, 0x07, 0x88, 0x80, 0x18, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0c, 0x01, 0x02, 0x07, /* bytes 4288 - 4304 */ + 0x44, 0x81, 0x18, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x11, 0xc4, 0x18, /* bytes 4304 - 4320 */ + 0x63, 0x8c, 0x31, 0x3a, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x88, 0x80, 0x18, 0x95, 0x22, 0x84, /* bytes 4320 - 4336 */ + 0x10, 0x02, 0x05, 0x09, 0x01, 0x05, 0x07, 0x21, 0xbc, 0x18, 0xe3, 0x0b, 0x01, 0x00, 0x00, 0x05, /* bytes 4336 - 4352 */ + 0x09, 0x00, 0x05, 0x06, 0x4c, 0xca, 0x25, 0xa5, 0x94, 0x0a, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, /* bytes 4352 - 4368 */ + 0x06, 0x42, 0x60, 0x89, 0x9e, 0xe9, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, 0x60, /* bytes 4368 - 4384 */ + 0x89, 0x9e, 0xe9, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0xa4, 0x60, 0x89, 0x9e, 0xe9, /* bytes 4384 - 4400 */ + 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x5a, 0x60, 0x89, 0x9e, 0xe9, 0x00, 0x00, 0x00, /* bytes 4400 - 4416 */ + 0x04, 0x09, 0x01, 0x05, 0x06, 0x09, 0x96, 0xe8, 0x99, 0x0e, 0x00, 0x00, 0x00, 0x04, 0x0b, 0x01, /* bytes 4416 - 4432 */ + 0x03, 0x06, 0x96, 0x06, 0x96, 0xe8, 0x99, 0x0e, 0x00, 0x00, 0x07, 0x07, 0x01, 0x07, 0x09, 0xb6, /* bytes 4432 - 4448 */ + 0x24, 0xd2, 0x9f, 0x48, 0xda, 0x00, 0x00, 0x04, 0x09, 0x01, 0x07, 0x06, 0x96, 0x11, 0x91, 0x46, /* bytes 4448 - 4464 */ + 0x02, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x42, 0x60, 0x99, 0x1f, 0x69, 0x00, 0x00, /* bytes 4464 - 4480 */ + 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, 0x60, 0x99, 0x1f, 0x69, 0x00, 0x00, 0x00, 0x04, 0x0a, /* bytes 4480 - 4496 */ + 0x01, 0x04, 0x06, 0xa4, 0x60, 0x99, 0x1f, 0x69, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, /* bytes 4496 - 4512 */ + 0x09, 0x96, 0xf9, 0x91, 0x06, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x04, 0x03, 0x89, 0xaa, 0x0a, /* bytes 4512 - 4528 */ + 0x00, 0x02, 0x0a, 0x01, 0x04, 0x03, 0x46, 0x55, 0x05, 0x00, 0x03, 0x0a, 0x00, 0x04, 0x03, 0x2a, /* bytes 4528 - 4544 */ + 0x24, 0x49, 0x12, 0x03, 0x09, 0x00, 0x05, 0x03, 0x85, 0x24, 0x49, 0x02, 0x04, 0x09, 0x01, 0x05, /* bytes 4544 - 4560 */ + 0x06, 0x4a, 0x8a, 0x9e, 0x99, 0x06, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x5a, 0xd0, /* bytes 4560 - 4576 */ + 0x9b, 0x99, 0x99, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x42, 0x60, 0x99, 0x99, 0x69, /* bytes 4576 - 4592 */ + 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, 0x60, 0x99, 0x99, 0x69, 0x00, 0x00, 0x00, /* bytes 4592 - 4608 */ + 0x04, 0x0a, 0x01, 0x04, 0x06, 0xa4, 0x60, 0x99, 0x99, 0x69, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, /* bytes 4608 - 4624 */ + 0x04, 0x06, 0x5a, 0x60, 0x99, 0x99, 0x69, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x09, /* bytes 4624 - 4640 */ + 0x96, 0x99, 0x99, 0x06, 0x00, 0x00, 0x00, 0x05, 0x05, 0x01, 0x08, 0x07, 0x04, 0x7c, 0x40, 0x00, /* bytes 4640 - 4656 */ + 0x06, 0x07, 0x00, 0x07, 0x06, 0xac, 0xa4, 0x59, 0x92, 0xd4, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, /* bytes 4656 - 4672 */ + 0x04, 0x06, 0x42, 0x90, 0x99, 0x99, 0xbd, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, /* bytes 4672 - 4688 */ + 0x90, 0x99, 0x99, 0xbd, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, 0xa4, 0x90, 0x99, 0x99, /* bytes 4688 - 4704 */ + 0xbd, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x09, 0x99, 0x99, 0xd9, 0x0b, 0x00, 0x00, /* bytes 4704 - 4720 */ + 0x00, 0x05, 0x0c, 0x01, 0x04, 0x07, 0x88, 0x80, 0x18, 0x95, 0x52, 0x84, 0x90, 0x01, 0x04, 0x0b, /* bytes 4720 - 4736 */ + 0x01, 0x05, 0x06, 0x11, 0x97, 0x99, 0x99, 0x17, 0x01, 0x00, 0x00, 0x05, 0x0b, 0x01, 0x05, 0x07, /* bytes 4736 - 4752 */ + 0x11, 0xc4, 0xa8, 0x94, 0x22, 0x84, 0x0c, 0x00, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x1e, 0x10, 0x42, /* bytes 4752 - 4768 */ + 0x94, 0x52, 0x3f, 0x46, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x0f, 0x96, 0xe8, 0x99, 0x0e, 0x00, /* bytes 4768 - 4784 */ + 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x92, 0x01, 0x42, 0x88, 0x52, 0xea, 0xc7, 0x08, 0x04, /* bytes 4784 - 4800 */ + 0x0a, 0x01, 0x04, 0x06, 0x69, 0x60, 0x89, 0x9e, 0xe9, 0x00, 0x00, 0x00, 0x06, 0x0b, 0x01, 0x05, /* bytes 4800 - 4816 */ + 0x07, 0x04, 0x41, 0x28, 0x8a, 0xf2, 0x45, 0x11, 0x02, 0x03, 0x00, 0x00, 0x00, 0x05, 0x09, 0x01, /* bytes 4816 - 4832 */ + 0x07, 0x06, 0x26, 0x21, 0x97, 0x92, 0x23, 0x18, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x88, /* bytes 4832 - 4848 */ + 0x00, 0x17, 0x63, 0x08, 0x21, 0x46, 0x07, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, 0x60, 0x19, 0x11, /* bytes 4848 - 4864 */ + 0x69, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x44, 0x01, 0x17, 0x63, 0x08, 0x21, 0x46, /* bytes 4864 - 4880 */ + 0x07, 0x04, 0x0a, 0x01, 0x04, 0x06, 0xa4, 0x60, 0x19, 0x11, 0x69, 0x00, 0x00, 0x00, 0x05, 0x0b, /* bytes 4880 - 4896 */ + 0x01, 0x03, 0x07, 0x04, 0xb8, 0x18, 0x43, 0x08, 0x31, 0x3a, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, /* bytes 4896 - 4912 */ + 0x04, 0x96, 0x11, 0x91, 0x06, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x8a, 0x00, 0x17, /* bytes 4912 - 4928 */ + 0x63, 0x08, 0x21, 0x46, 0x07, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x4a, 0x60, 0x19, 0x11, 0x69, 0x00, /* bytes 4928 - 4944 */ + 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x8a, 0x80, 0x17, 0x63, 0x8c, 0x31, 0xc6, 0x07, 0x07, /* bytes 4944 - 4960 */ + 0x09, 0x01, 0x05, 0x09, 0x48, 0xa4, 0x33, 0x95, 0x48, 0x24, 0x12, 0x0e, 0x06, 0x09, 0x00, 0x05, /* bytes 4960 - 4976 */ + 0x07, 0x9e, 0x28, 0x8a, 0xa7, 0x28, 0x8a, 0x1e, 0x00, 0x05, 0x0a, 0x01, 0x04, 0x06, 0x88, 0x23, /* bytes 4976 - 4992 */ + 0x97, 0x52, 0x4a, 0xc9, 0x01, 0x00, 0x04, 0x0b, 0x01, 0x03, 0x06, 0x0f, 0x1f, 0x11, 0x17, 0x11, /* bytes 4992 - 5008 */ + 0x0f, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x0f, 0x96, 0xf9, 0x91, 0x06, 0x00, 0x00, 0x00, /* bytes 5008 - 5024 */ + 0x04, 0x0c, 0x01, 0x02, 0x06, 0x69, 0xf0, 0x11, 0x71, 0x11, 0xf1, 0x00, 0x00, 0x04, 0x0a, 0x01, /* bytes 5024 - 5040 */ + 0x04, 0x06, 0x69, 0x60, 0x99, 0x1f, 0x69, 0x00, 0x00, 0x00, 0x04, 0x0b, 0x01, 0x03, 0x06, 0x04, /* bytes 5040 - 5056 */ + 0x1f, 0x11, 0x17, 0x11, 0x0f, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x04, 0x96, 0xf9, 0x91, /* bytes 5056 - 5072 */ + 0x06, 0x00, 0x00, 0x00, 0x04, 0x0b, 0x01, 0x05, 0x06, 0x1f, 0x11, 0x17, 0x11, 0x2f, 0x0c, 0x00, /* bytes 5072 - 5088 */ + 0x00, 0x04, 0x09, 0x01, 0x07, 0x06, 0x96, 0xf9, 0x91, 0x26, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x0c, /* bytes 5088 - 5104 */ + 0x01, 0x02, 0x06, 0x4a, 0xf0, 0x11, 0x71, 0x11, 0xf1, 0x00, 0x00, 0x04, 0x0a, 0x01, 0x04, 0x06, /* bytes 5104 - 5120 */ + 0x4a, 0x60, 0x99, 0x1f, 0x69, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x44, 0x01, 0x17, /* bytes 5120 - 5136 */ + 0x63, 0x08, 0x39, 0x46, 0x0f, 0x04, 0x0c, 0x01, 0x04, 0x06, 0xa4, 0xe0, 0x99, 0x16, 0x96, 0x69, /* bytes 5136 - 5152 */ + 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x92, 0x01, 0x17, 0x63, 0x08, 0x39, 0x46, 0x0f, 0x04, /* bytes 5152 - 5168 */ + 0x0c, 0x01, 0x04, 0x06, 0x69, 0x68, 0x99, 0x16, 0x96, 0x69, 0x00, 0x00, 0x05, 0x0b, 0x01, 0x03, /* bytes 5168 - 5184 */ + 0x07, 0x04, 0xb8, 0x18, 0x43, 0xc8, 0x31, 0x7a, 0x00, 0x04, 0x0b, 0x01, 0x05, 0x06, 0x82, 0x96, /* bytes 5184 - 5200 */ + 0x69, 0x61, 0x99, 0x06, 0x00, 0x00, 0x05, 0x0b, 0x01, 0x05, 0x07, 0x2e, 0xc6, 0x10, 0x72, 0x8c, /* bytes 5200 - 5216 */ + 0x1e, 0x11, 0x00, 0x04, 0x0d, 0x01, 0x03, 0x06, 0x24, 0x82, 0x96, 0x69, 0x61, 0x99, 0x06, 0x00, /* bytes 5216 - 5232 */ + 0x05, 0x0c, 0x01, 0x02, 0x07, 0x44, 0x81, 0x18, 0x63, 0xfc, 0x31, 0xc6, 0x08, 0x04, 0x0c, 0x01, /* bytes 5232 - 5248 */ + 0x02, 0x06, 0xa4, 0x10, 0xd1, 0x9b, 0x99, 0x99, 0x00, 0x00, 0x07, 0x09, 0x00, 0x05, 0x07, 0x22, /* bytes 5248 - 5264 */ + 0xd1, 0x5f, 0xe4, 0x13, 0x89, 0x44, 0x22, 0x05, 0x0a, 0x00, 0x04, 0x06, 0xe2, 0x09, 0x6d, 0xa5, /* bytes 5264 - 5280 */ + 0x94, 0x52, 0x02, 0x00, 0x05, 0x0c, 0xff, 0x02, 0x03, 0xb6, 0x01, 0x42, 0x08, 0x21, 0x84, 0x10, /* bytes 5280 - 5296 */ + 0x02, 0x05, 0x0a, 0xff, 0x04, 0x03, 0xb6, 0x01, 0x42, 0x08, 0x21, 0x84, 0x00, 0x00, 0x04, 0x0b, /* bytes 5296 - 5312 */ + 0x00, 0x03, 0x03, 0x0f, 0x22, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x03, 0x09, 0x00, 0x05, 0x03, /* bytes 5312 - 5328 */ + 0x87, 0x24, 0x49, 0x02, 0x04, 0x0c, 0x00, 0x02, 0x03, 0x69, 0x20, 0x22, 0x22, 0x22, 0x22, 0x00, /* bytes 5328 - 5344 */ + 0x00, 0x04, 0x0a, 0x00, 0x04, 0x03, 0x69, 0x20, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x03, 0x0b, /* bytes 5344 - 5360 */ + 0x00, 0x05, 0x03, 0x92, 0x24, 0x49, 0x8a, 0x01, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x00, 0x05, 0x03, /* bytes 5360 - 5376 */ + 0x82, 0x24, 0x49, 0x8a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0b, 0x01, 0x03, 0x03, 0xfd, 0x07, 0x00, /* bytes 5376 - 5392 */ + 0x00, 0x01, 0x07, 0x01, 0x07, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x31, /* bytes 5392 - 5408 */ + 0xc6, 0x18, 0x63, 0x8c, 0x0d, 0x00, 0x00, 0x04, 0x0b, 0x01, 0x05, 0x06, 0x09, 0x99, 0x99, 0x99, /* bytes 5408 - 5424 */ + 0x89, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x00, 0x02, 0x04, 0xa4, 0x40, 0x44, 0x44, 0x44, 0x34, 0x00, /* bytes 5424 - 5440 */ + 0x00, 0x03, 0x0c, 0x00, 0x04, 0x03, 0x2a, 0x24, 0x49, 0x92, 0x02, 0x00, 0x00, 0x00, 0x05, 0x0b, /* bytes 5440 - 5456 */ + 0x01, 0x05, 0x06, 0x31, 0xa5, 0x32, 0x4a, 0x4a, 0x91, 0x08, 0x00, 0x04, 0x0b, 0x01, 0x05, 0x06, /* bytes 5456 - 5472 */ + 0x11, 0x99, 0x35, 0x95, 0x49, 0x02, 0x00, 0x00, 0x04, 0x07, 0x01, 0x07, 0x06, 0x99, 0x35, 0x95, /* bytes 5472 - 5488 */ + 0x09, 0x04, 0x0c, 0x01, 0x02, 0x05, 0x24, 0x10, 0x11, 0x11, 0x11, 0xf1, 0x00, 0x00, 0x02, 0x0c, /* bytes 5488 - 5504 */ + 0x01, 0x02, 0x03, 0x46, 0x55, 0x55, 0x00, 0x04, 0x0b, 0x01, 0x05, 0x05, 0x11, 0x11, 0x11, 0x11, /* bytes 5504 - 5520 */ + 0x4f, 0x02, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x05, 0x03, 0xaa, 0xaa, 0x1a, 0x00, 0x04, 0x09, 0x01, /* bytes 5520 - 5536 */ + 0x05, 0x05, 0x55, 0x35, 0x11, 0x11, 0x0f, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x99, /* bytes 5536 - 5552 */ + 0x59, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 0x04, 0x09, 0x01, 0x05, 0x05, 0x11, 0x11, 0x55, 0x11, /* bytes 5552 - 5568 */ + 0x0f, 0x00, 0x00, 0x00, 0x03, 0x09, 0x01, 0x05, 0x05, 0x49, 0xd2, 0x26, 0x01, 0x05, 0x09, 0x00, /* bytes 5568 - 5584 */ + 0x05, 0x05, 0x42, 0x08, 0x33, 0x84, 0x10, 0x1e, 0x00, 0x00, 0x03, 0x09, 0x00, 0x05, 0x03, 0x92, /* bytes 5584 - 5600 */ + 0x3c, 0x49, 0x02, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x88, 0x80, 0x38, 0x67, 0xad, 0x39, 0xc7, 0x08, /* bytes 5600 - 5616 */ + 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, 0xd0, 0x9b, 0x99, 0x99, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x01, /* bytes 5616 - 5632 */ + 0x05, 0x07, 0x71, 0xce, 0x5a, 0x73, 0x8e, 0x91, 0x08, 0x00, 0x04, 0x09, 0x01, 0x07, 0x06, 0xbd, /* bytes 5632 - 5648 */ + 0x99, 0x99, 0x49, 0x02, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x8a, 0x80, 0x38, 0x67, /* bytes 5648 - 5664 */ + 0xad, 0x39, 0xc7, 0x08, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x4a, 0xd0, 0x9b, 0x99, 0x99, 0x00, 0x00, /* bytes 5664 - 5680 */ + 0x00, 0x05, 0x0a, 0x00, 0x04, 0x06, 0x22, 0x00, 0x6d, 0xa5, 0x94, 0x52, 0x02, 0x00, 0x05, 0x0b, /* bytes 5680 - 5696 */ + 0x01, 0x05, 0x07, 0x71, 0xce, 0x5a, 0x73, 0x8e, 0x11, 0x32, 0x00, 0x04, 0x09, 0x01, 0x07, 0x06, /* bytes 5696 - 5712 */ + 0xbd, 0x99, 0x99, 0x89, 0x04, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x1e, 0xb8, 0x18, /* bytes 5712 - 5728 */ + 0x63, 0x8c, 0x31, 0x3a, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x0f, 0x96, 0x99, 0x99, 0x06, 0x00, /* bytes 5728 - 5744 */ + 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x92, 0x01, 0x17, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x04, /* bytes 5744 - 5760 */ + 0x0a, 0x01, 0x04, 0x06, 0x69, 0x60, 0x99, 0x99, 0x69, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, /* bytes 5760 - 5776 */ + 0x07, 0x32, 0x01, 0x17, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0a, 0x01, 0x04, 0x06, 0x32, 0x01, /* bytes 5776 - 5792 */ + 0x93, 0x52, 0x4a, 0xc9, 0x00, 0x00, 0x08, 0x09, 0x01, 0x05, 0x0a, 0xfe, 0x11, 0x11, 0x11, 0x71, /* bytes 5792 - 5808 */ + 0x11, 0x11, 0x11, 0xfe, 0x00, 0x00, 0x00, 0x07, 0x07, 0x01, 0x07, 0x09, 0xb6, 0x64, 0x32, 0x9f, /* bytes 5808 - 5824 */ + 0x48, 0xda, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x44, 0x80, 0x17, 0x63, 0x7c, 0x29, 0xc5, /* bytes 5824 - 5840 */ + 0x08, 0x03, 0x0a, 0x01, 0x04, 0x04, 0x14, 0xba, 0x24, 0x09, 0x05, 0x0b, 0x01, 0x05, 0x07, 0x2f, /* bytes 5840 - 5856 */ + 0xc6, 0xf8, 0x52, 0x8a, 0x91, 0x08, 0x00, 0x03, 0x09, 0x01, 0x07, 0x04, 0x5d, 0x92, 0x44, 0x01, /* bytes 5856 - 5872 */ + 0x05, 0x0c, 0x01, 0x02, 0x07, 0x8a, 0x80, 0x17, 0x63, 0x7c, 0x29, 0xc5, 0x08, 0x03, 0x0a, 0x01, /* bytes 5872 - 5888 */ + 0x04, 0x04, 0x15, 0xba, 0x24, 0x09, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x44, 0x00, 0x17, 0xa3, 0x20, /* bytes 5888 - 5904 */ + 0x28, 0x46, 0x07, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x24, 0x60, 0x29, 0x84, 0x69, 0x00, 0x00, 0x00, /* bytes 5904 - 5920 */ + 0x05, 0x0c, 0x01, 0x02, 0x07, 0x44, 0x01, 0x17, 0xa3, 0x20, 0x28, 0x46, 0x07, 0x04, 0x0a, 0x01, /* bytes 5920 - 5936 */ + 0x04, 0x06, 0xa4, 0x60, 0x29, 0x84, 0x69, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x01, 0x05, 0x07, 0x2e, /* bytes 5936 - 5952 */ + 0x46, 0x41, 0x50, 0x8c, 0x8e, 0x08, 0x00, 0x04, 0x09, 0x01, 0x07, 0x06, 0x96, 0x42, 0x98, 0x46, /* bytes 5952 - 5968 */ + 0x02, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x8a, 0x00, 0x17, 0xa3, 0x20, 0x28, 0x46, /* bytes 5968 - 5984 */ + 0x07, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x4a, 0x60, 0x29, 0x84, 0x69, 0x00, 0x00, 0x00, 0x05, 0x0b, /* bytes 5984 - 6000 */ + 0x00, 0x05, 0x05, 0x9f, 0x10, 0x42, 0x08, 0x21, 0x84, 0x08, 0x00, 0x03, 0x0b, 0x00, 0x05, 0x04, /* bytes 6000 - 6016 */ + 0xd2, 0x25, 0x49, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x00, 0x02, 0x05, 0x8a, 0x80, 0x4f, /* bytes 6016 - 6032 */ + 0x08, 0x21, 0x84, 0x10, 0x02, 0x05, 0x09, 0x00, 0x05, 0x06, 0x52, 0x5e, 0x25, 0x84, 0x10, 0x06, /* bytes 6032 - 6048 */ + 0x00, 0x00, 0x05, 0x09, 0x00, 0x05, 0x05, 0x9f, 0x10, 0xe2, 0x08, 0x21, 0x04, 0x00, 0x00, 0x03, /* bytes 6048 - 6064 */ + 0x09, 0x00, 0x05, 0x04, 0xd2, 0xa5, 0x4b, 0x06, 0x05, 0x0c, 0x01, 0x02, 0x07, 0xb6, 0x81, 0x18, /* bytes 6064 - 6080 */ + 0x63, 0x8c, 0x31, 0x46, 0x07, 0x05, 0x0a, 0x01, 0x04, 0x06, 0xb6, 0x81, 0x94, 0x52, 0x4a, 0x6d, /* bytes 6080 - 6096 */ + 0x01, 0x00, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x1e, 0xc4, 0x18, 0x63, 0x8c, 0x31, 0x3a, 0x00, 0x04, /* bytes 6096 - 6112 */ + 0x09, 0x01, 0x05, 0x06, 0x0f, 0x99, 0x99, 0xd9, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, /* bytes 6112 - 6128 */ + 0x07, 0x92, 0x81, 0x18, 0x63, 0x8c, 0x31, 0x46, 0x07, 0x04, 0x0a, 0x01, 0x04, 0x06, 0x69, 0x90, /* bytes 6128 - 6144 */ + 0x99, 0x99, 0xbd, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x01, 0x01, 0x07, 0x4c, 0x32, 0x10, 0x63, 0x8c, /* bytes 6144 - 6160 */ + 0x31, 0xc6, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0b, 0x01, 0x03, 0x06, 0x96, 0x06, 0x99, 0x99, /* bytes 6160 - 6176 */ + 0xd9, 0x0b, 0x00, 0x00, 0x05, 0x0c, 0x01, 0x02, 0x07, 0x32, 0x81, 0x18, 0x63, 0x8c, 0x31, 0x46, /* bytes 6176 - 6192 */ + 0x07, 0x05, 0x0a, 0x01, 0x04, 0x06, 0x32, 0x81, 0x94, 0x52, 0x4a, 0x6d, 0x01, 0x00, 0x05, 0x0b, /* bytes 6192 - 6208 */ + 0x01, 0x05, 0x07, 0x31, 0xc6, 0x18, 0x63, 0x8c, 0x8e, 0x60, 0x00, 0x05, 0x09, 0x01, 0x07, 0x06, /* bytes 6208 - 6224 */ + 0x29, 0xa5, 0x94, 0xda, 0x22, 0x18, 0x00, 0x00, 0x09, 0x0c, 0x01, 0x02, 0x0b, 0x10, 0x50, 0x00, /* bytes 6224 - 6240 */ + 0x88, 0x18, 0x31, 0xa2, 0x2a, 0x55, 0xaa, 0x88, 0x10, 0x21, 0x02, 0x00, 0x00, 0x07, 0x0a, 0x01, /* bytes 6240 - 6256 */ + 0x04, 0x09, 0x08, 0x0a, 0x20, 0x99, 0xac, 0x56, 0x45, 0x22, 0x11, 0x00, 0x00, 0x00, 0x05, 0x0c, /* bytes 6256 - 6272 */ + 0x01, 0x02, 0x07, 0x44, 0x81, 0x18, 0x95, 0x22, 0x84, 0x10, 0x02, 0x05, 0x0c, 0x01, 0x04, 0x07, /* bytes 6272 - 6288 */ + 0x44, 0x81, 0x18, 0x95, 0x52, 0x84, 0x90, 0x01, 0x05, 0x0b, 0x01, 0x03, 0x07, 0x11, 0xc4, 0xa8, /* bytes 6288 - 6304 */ + 0x14, 0x21, 0x84, 0x10, 0x00, 0x04, 0x0c, 0x01, 0x02, 0x06, 0x24, 0xf0, 0x48, 0x24, 0x12, 0xf1, /* bytes 6304 - 6320 */ + 0x00, 0x00, 0x03, 0x0a, 0x01, 0x04, 0x05, 0x14, 0x4e, 0x29, 0x39, 0x04, 0x0b, 0x01, 0x03, 0x06, /* bytes 6320 - 6336 */ + 0x04, 0x8f, 0x44, 0x22, 0x11, 0x0f, 0x00, 0x00, 0x03, 0x09, 0x01, 0x05, 0x05, 0xc2, 0x29, 0x25, /* bytes 6336 - 6352 */ + 0x07, 0x04, 0x0c, 0x01, 0x02, 0x06, 0x4a, 0xf0, 0x48, 0x24, 0x12, 0xf1, 0x00, 0x00, 0x03, 0x0a, /* bytes 6352 - 6368 */ + 0x01, 0x04, 0x05, 0x15, 0x4e, 0x29, 0x39, 0x04, 0x09, 0x00, 0x05, 0x04, 0x2c, 0x23, 0x22, 0x22, /* bytes 6368 - 6384 */ + 0x02, 0x00, 0x00, 0x00, 0x05, 0x0b, 0xff, 0x05, 0x04, 0x98, 0x38, 0x42, 0x08, 0x21, 0x84, 0x0c, /* bytes 6384 - 6400 */ + 0x00, 0x07, 0x0c, 0x01, 0x02, 0x09, 0x10, 0x04, 0x80, 0xcf, 0x60, 0x28, 0x74, 0x8e, 0x44, 0x22, /* bytes 6400 - 6416 */ + 0x0f, 0x00, 0x07, 0x0a, 0x01, 0x04, 0x09, 0x10, 0x04, 0xc0, 0x96, 0x44, 0xfa, 0x13, 0x49, 0x1b, /* bytes 6416 - 6432 */ + 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x02, 0x07, 0x10, 0x04, 0x80, 0x2b, 0x12, 0xc9, 0x54, 0x26, /* bytes 6432 - 6448 */ + 0x91, 0xa8, 0x03, 0x00, 0x06, 0x0a, 0x00, 0x04, 0x06, 0x08, 0x01, 0xb0, 0x92, 0x66, 0x49, 0x52, /* bytes 6448 - 6464 */ + 0x03, 0x05, 0x0b, 0x01, 0x05, 0x07, 0x2e, 0x46, 0x41, 0x50, 0x8c, 0x8e, 0x08, 0x00, 0x04, 0x09, /* bytes 6464 - 6480 */ + 0x01, 0x07, 0x06, 0x96, 0x42, 0x98, 0x46, 0x02, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x00, 0x05, 0x05, /* bytes 6480 - 6496 */ + 0x9f, 0x10, 0x42, 0x08, 0x21, 0x84, 0x08, 0x00, 0x03, 0x0b, 0x00, 0x05, 0x04, 0xd2, 0x25, 0x49, /* bytes 6496 - 6512 */ + 0xa6, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x04, 0x03, 0x2a, 0x00, 0x00, 0x00, 0x03, 0x02, /* bytes 6512 - 6528 */ + 0x01, 0x04, 0x05, 0x15, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x04, 0x04, 0x69, 0x00, 0x00, 0x00, /* bytes 6528 - 6544 */ + 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x04, 0x03, 0x01, 0x03, 0x06, 0x96, 0x06, /* bytes 6544 - 6560 */ + 0x00, 0x00, 0x03, 0x02, 0x01, 0x0e, 0x04, 0x31, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x04, 0x05, /* bytes 6560 - 6576 */ + 0xb6, 0x01, 0x00, 0x00, 0x05, 0x02, 0x00, 0x04, 0x05, 0x32, 0x01, 0x00, 0x00, 0x04, 0x07, 0x01, /* bytes 6576 - 6592 */ + 0x07, 0x06, 0x9f, 0x99, 0x99, 0x09, 0x05, 0x01, 0x01, 0x0a, 0x07, 0x1f, 0x00, 0x00, 0x00, 0x0a, /* bytes 6592 - 6608 */ + 0x01, 0x01, 0x0a, 0x0c, 0xff, 0x03, 0x00, 0x00, 0x02, 0x04, 0x01, 0x05, 0x03, 0x56, 0x00, 0x00, /* bytes 6608 - 6624 */ + 0x00, 0x02, 0x04, 0x00, 0x05, 0x03, 0x6a, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x0c, 0x03, 0x6a, /* bytes 6624 - 6640 */ + 0x00, 0x00, 0x00, 0x04, 0x04, 0x01, 0x05, 0x05, 0x5a, 0x55, 0x00, 0x00, 0x04, 0x04, 0x00, 0x05, /* bytes 6640 - 6656 */ + 0x05, 0xaa, 0x5a, 0x00, 0x00, 0x04, 0x04, 0x00, 0x0c, 0x05, 0xaa, 0x5a, 0x00, 0x00, 0x03, 0x06, /* bytes 6656 - 6672 */ + 0x01, 0x05, 0x05, 0xba, 0x24, 0x01, 0x00, 0x03, 0x09, 0x01, 0x05, 0x05, 0xba, 0x24, 0xe9, 0x02, /* bytes 6672 - 6688 */ + 0x04, 0x04, 0x01, 0x08, 0x06, 0xf6, 0x6f, 0x00, 0x00, 0x07, 0x02, 0x01, 0x0c, 0x09, 0xc9, 0x24, /* bytes 6688 - 6704 */ + 0x00, 0x00, 0x0c, 0x09, 0x01, 0x05, 0x0e, 0x06, 0x91, 0x0f, 0x49, 0x90, 0x02, 0xd6, 0x86, 0x92, /* bytes 6704 - 6720 */ + 0x24, 0x29, 0x92, 0xc1, 0x06, 0x00, 0x00, 0x03, 0x05, 0x01, 0x08, 0x05, 0x54, 0x44, 0x00, 0x00, /* bytes 6720 - 6736 */ + 0x03, 0x05, 0x01, 0x08, 0x05, 0x11, 0x15, 0x00, 0x00, 0x04, 0x0b, 0x01, 0x04, 0x06, 0x88, 0x44, /* bytes 6736 - 6752 */ + 0x24, 0x22, 0x11, 0x01, 0x00, 0x00, 0x06, 0x09, 0x00, 0x05, 0x07, 0x9c, 0x28, 0x3e, 0xc2, 0x23, /* bytes 6752 - 6768 */ + 0x8a, 0x1c, 0x00, 0x09, 0x05, 0x01, 0x04, 0x0b, 0x17, 0x65, 0x4b, 0x95, 0x28, 0x11, 0x00, 0x00, /* bytes 6768 - 6784 */ + 0x05, 0x09, 0x01, 0x05, 0x07, 0x2e, 0xc6, 0x18, 0xa3, 0x52, 0x1b, 0x00, 0x00, 0x04, 0x09, 0x01, /* bytes 6784 - 6800 */ + 0x05, 0x06, 0x42, 0xc4, 0x9a, 0x99, 0x06, 0x00, 0x00, 0x00, 0x05, 0x09, 0x01, 0x05, 0x07, 0x84, /* bytes 6800 - 6816 */ + 0x10, 0xa5, 0x54, 0x8c, 0x1f, 0x00, 0x00, 0x06, 0x09, 0x01, 0x05, 0x08, 0xbf, 0x24, 0x49, 0x92, /* bytes 6816 - 6832 */ + 0x24, 0x49, 0x12, 0x00, 0x04, 0x09, 0x01, 0x05, 0x06, 0x1f, 0x42, 0x48, 0x12, 0x0f, 0x00, 0x00, /* bytes 6832 - 6848 */ + 0x00, 0x03, 0x01, 0x01, 0x0a, 0x05, 0x07, 0x00, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x04, 0x09, 0xc0, /* bytes 6848 - 6864 */ + 0x81, 0x80, 0x00, 0x01, 0x42, 0xc2, 0x05, 0x0a, 0x08, 0x10, 0x00, 0x09, 0x05, 0x01, 0x08, 0x0b, /* bytes 6864 - 6880 */ + 0xc6, 0x52, 0x46, 0x4c, 0x69, 0x0c, 0x00, 0x00, 0x05, 0x0e, 0xff, 0x02, 0x03, 0x98, 0x10, 0x42, /* bytes 6880 - 6896 */ + 0x08, 0x21, 0x84, 0x10, 0x42, 0x06, 0x00, 0x00, 0x00, 0x06, 0x05, 0x01, 0x07, 0x08, 0x66, 0x06, /* bytes 6896 - 6912 */ + 0x98, 0x19, 0x05, 0x07, 0x01, 0x07, 0x07, 0x08, 0x7d, 0xf2, 0x85, 0x00, 0x00, 0x00, 0x00, 0x05, /* bytes 6912 - 6928 */ + 0x06, 0x01, 0x08, 0x07, 0x88, 0x08, 0x82, 0x3e, 0x05, 0x06, 0x01, 0x08, 0x07, 0x82, 0x20, 0x22, /* bytes 6928 - 6944 */ + 0x3e, 0x05, 0x05, 0x01, 0x08, 0x07, 0x44, 0x45, 0x45, 0x00, 0x02, 0x02, 0x00, 0x0e, 0x03, 0x06, /* bytes 6944 - 6960 */ + 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x05, 0x06, 0x54, 0x7c, 0x29, 0xa5, 0x94, 0x12, 0x00, 0x00, /* bytes 6960 - 6976 */ + 0x05, 0x09, 0x00, 0x05, 0x06, 0x5c, 0x5e, 0x29, 0xa5, 0x94, 0x12, 0x00, 0x00, }; __attribute__ ((aligned (8))) static const uint8_t ACTION_BAR_ICON_UP_builtin_bytes[] = { - 0x03, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, /* bytes 0 - 16 */ - 0xbc, 0x00, 0x02, 0xff, 0x00, 0x0b, 0xff, 0x40, 0x0f, 0xff, 0xd0, 0x1f, 0xff, 0xf4, 0x7f, 0xff, /* bytes 16 - 32 */ - 0xfd, 0x00, 0xbf, 0x7f, 0xff, + 0x03, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x38, 0x00, 0x00, /* bytes 0 - 16 */ + 0x94, 0x00, 0x02, 0x55, 0x00, 0x09, 0x55, 0xc0, 0x05, 0x55, 0x70, 0x35, 0x55, 0x5c, 0xd5, 0x55, /* bytes 16 - 32 */ + 0x57, 0x00, 0xff, 0x7f, 0xbf, }; __attribute__ ((aligned (8))) static const uint8_t ACTION_BAR_ICON_DOWN_builtin_bytes[] = { - 0x03, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x15, 0x55, 0x54, 0x85, /* bytes 0 - 16 */ - 0x55, 0x52, 0xa1, 0x55, 0x4a, 0xa8, 0x55, 0x6a, 0xaa, 0x15, 0xea, 0xaa, 0x87, 0xaa, 0xaa, 0xae, /* bytes 16 - 32 */ - 0xaa, 0xbf, 0xff, 0x00, 0x7f, + 0x03, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0xd5, 0x55, 0x57, 0x35, /* bytes 0 - 16 */ + 0x55, 0x5c, 0x0d, 0x55, 0x70, 0x03, 0x55, 0x40, 0x00, 0xd5, 0x80, 0x00, 0x36, 0x00, 0x00, 0x08, /* bytes 16 - 32 */ + 0x00, 0x00, 0xff, 0x7f, 0xbf, }; __attribute__ ((aligned (8))) static const uint8_t GENERIC_WARNING_TINY_builtin_bytes[] = { @@ -194,9 +534,9 @@ static const uint8_t WATCH_DISCONNECTED_LARGE_builtin_bytes[] = { }; __attribute__ ((aligned (8))) static const uint8_t ARROW_DOWN_builtin_bytes[] = { - 0x03, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x15, 0x55, 0x54, 0x85, /* bytes 0 - 16 */ - 0x55, 0x52, 0xa1, 0x55, 0x4a, 0xa8, 0x55, 0x2a, 0xaa, 0x14, 0xaa, 0xaa, 0x82, 0xaa, 0x80, 0xc0, /* bytes 16 - 32 */ - 0x00, 0x00, + 0x03, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x2a, 0xaa, 0xa8, 0x4a, /* bytes 0 - 16 */ + 0xaa, 0xa1, 0x52, 0xaa, 0x85, 0x54, 0xaa, 0x15, 0x55, 0x28, 0x55, 0x55, 0x41, 0x55, 0x80, 0x00, /* bytes 16 - 32 */ + 0xc0, 0x00, }; __attribute__ ((aligned (8))) static const uint8_t VOICE_MICROPHONE_LARGE_builtin_bytes[] = { @@ -220,97 +560,7824 @@ static const uint8_t VOICE_MICROPHONE_LARGE_builtin_bytes[] = { 0x02, }; __attribute__ ((aligned (8))) -static const uint8_t JS_TICTOC_builtin_bytes[] = { - 0x50, 0x4a, 0x53, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, /* bytes 0 - 16 */ - 0xc0, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x05, 0x00, /* bytes 16 - 32 */ - 0x00, 0x06, 0x14, 0x18, 0x1f, 0x00, 0x1a, 0x00, 0x26, 0x00, 0x34, 0x00, 0x15, 0x00, 0x0e, 0x00, /* bytes 32 - 48 */ - 0x2a, 0x00, 0x69, 0x00, 0x21, 0x00, 0x5b, 0x00, 0xab, 0x00, 0x59, 0x00, 0x2c, 0x00, 0x23, 0x00, /* bytes 48 - 64 */ - 0xad, 0x00, 0x17, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x03, 0x00, 0x12, 0x00, 0x24, 0x00, /* bytes 64 - 80 */ - 0x32, 0x00, 0x4c, 0x00, 0x41, 0x03, 0xcb, 0x04, 0x00, 0xcb, 0x14, 0x01, 0x14, 0x14, 0x28, 0x08, /* bytes 80 - 96 */ - 0x18, 0x07, 0x28, 0x0a, 0x18, 0x09, 0x31, 0x18, 0x0b, 0x18, 0x06, 0x14, 0x28, 0x0d, 0x18, 0x07, /* bytes 96 - 112 */ - 0x28, 0x0e, 0x18, 0x09, 0x31, 0x18, 0x0b, 0x18, 0x0c, 0xc8, 0x02, 0xcb, 0x15, 0x03, 0x3b, 0x00, /* bytes 112 - 128 */ - 0x0f, 0x29, 0x10, 0x16, 0xc4, 0x3b, 0x00, 0x0f, 0x29, 0x11, 0x17, 0xc4, 0x3b, 0x05, 0x12, 0x28, /* bytes 128 - 144 */ - 0x13, 0xbe, 0x46, 0x61, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x01, /* bytes 144 - 160 */ - 0x01, 0x07, 0x0d, 0x0e, 0x00, 0x00, 0x90, 0x00, 0x97, 0x00, 0x89, 0x00, 0x9b, 0x00, 0x9d, 0x00, /* bytes 160 - 176 */ - 0xa1, 0x00, 0x94, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x7f, 0x00, 0x83, 0x00, 0x20, 0x00, /* bytes 176 - 192 */ - 0x41, 0x03, 0x42, 0x04, 0x00, 0x42, 0x05, 0x0d, 0x28, 0x04, 0x1d, 0x05, 0x28, 0x06, 0x3e, 0xc8, /* bytes 192 - 208 */ - 0x04, 0x3b, 0x04, 0x07, 0xb7, 0x32, 0x3b, 0x8d, 0xc8, 0x01, 0x3b, 0x04, 0x08, 0xb7, 0x32, 0x3b, /* bytes 208 - 224 */ - 0x8d, 0xc8, 0x02, 0x3b, 0x04, 0x09, 0xb7, 0x32, 0x0b, 0x90, 0x85, 0x02, 0x32, 0x0b, 0x8d, 0xc8, /* bytes 224 - 240 */ - 0x03, 0x29, 0x05, 0x01, 0xba, 0xd1, 0x0a, 0x29, 0x05, 0x02, 0xba, 0xd1, 0x0b, 0x29, 0x05, 0x03, /* bytes 240 - 256 */ - 0xba, 0xd1, 0x0c, 0x46, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x21, 0x00, 0x02, 0x01, /* bytes 256 - 272 */ - 0x01, 0x02, 0x04, 0x04, 0x00, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0x89, 0x02, 0x00, 0x32, /* bytes 272 - 288 */ - 0x01, 0x8a, 0x36, 0x01, 0x03, 0x8a, 0x45, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x21, 0x00, 0x09, 0x05, /* bytes 288 - 304 */ - 0x05, 0x06, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, /* bytes 304 - 320 */ - 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x61, 0x00, 0x63, 0x00, 0x59, 0x00, /* bytes 320 - 336 */ - 0x5b, 0x00, 0x5d, 0x00, 0x51, 0x00, 0x29, 0x01, 0x06, 0x32, 0x07, 0xc5, 0x2a, 0x01, 0x07, 0x00, /* bytes 336 - 352 */ - 0x35, 0x08, 0xc5, 0x3b, 0x01, 0x09, 0xb6, 0x3b, 0x01, 0x0a, 0x29, 0x02, 0x03, 0xc2, 0x3b, 0x01, /* bytes 352 - 368 */ - 0x0b, 0x29, 0x02, 0x05, 0x3a, 0x0c, 0x36, 0x00, 0x0d, 0xbd, 0x36, 0x00, 0x0e, 0x8a, 0x8b, 0x04, /* bytes 368 - 384 */ - 0x84, 0x29, 0x03, 0x05, 0x3a, 0x0f, 0x36, 0x00, 0x0d, 0xbd, 0x36, 0x00, 0x0e, 0x8a, 0x8b, 0x04, /* bytes 384 - 400 */ - 0x84, 0xc2, 0x3b, 0x01, 0x10, 0xb6, 0x46, 0x00, 0x1a, 0x00, 0x01, 0x00, 0x21, 0x00, 0x07, 0x01, /* bytes 400 - 416 */ - 0x07, 0x0a, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 416 - 432 */ - 0x00, 0x00, 0xa6, 0x00, 0x34, 0x00, 0x26, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x41, 0x00, /* bytes 432 - 448 */ - 0x46, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x2c, 0x00, /* bytes 448 - 464 */ - 0x69, 0x00, 0x21, 0x00, 0x5b, 0x00, 0x59, 0x00, 0x23, 0x00, 0x36, 0x00, 0x0a, 0xc8, 0x01, 0x36, /* bytes 464 - 480 */ - 0x01, 0x0b, 0x35, 0x0c, 0xc8, 0x02, 0x36, 0x01, 0x0b, 0x35, 0x0d, 0xc8, 0x03, 0x2a, 0x01, 0x0e, /* bytes 480 - 496 */ - 0x0f, 0xc5, 0x3b, 0x01, 0x10, 0x31, 0x31, 0x36, 0x01, 0x0b, 0x35, 0x11, 0x36, 0x01, 0x0b, 0x35, /* bytes 496 - 512 */ - 0x12, 0xb0, 0x04, 0x28, 0x02, 0x32, 0x01, 0x8d, 0xc8, 0x04, 0x28, 0x03, 0x32, 0x01, 0x8d, 0xc8, /* bytes 512 - 528 */ - 0x05, 0x3b, 0x07, 0x13, 0x29, 0x02, 0x03, 0x32, 0x01, 0x32, 0x09, 0x8a, 0x87, 0xc3, 0x32, 0x01, /* bytes 528 - 544 */ - 0x8d, 0xc8, 0x06, 0x29, 0x08, 0x09, 0x35, 0x14, 0x2a, 0x01, 0x04, 0x05, 0x28, 0x06, 0xad, 0x05, /* bytes 544 - 560 */ - 0x29, 0x08, 0x09, 0x35, 0x15, 0x2a, 0x01, 0x04, 0x05, 0x28, 0x06, 0xad, 0x05, 0x28, 0x08, 0x14, /* bytes 560 - 576 */ - 0x28, 0x17, 0x18, 0x16, 0x31, 0x18, 0x18, 0x31, 0x18, 0x19, 0x29, 0x01, 0x04, 0x32, 0x07, 0x31, /* bytes 576 - 592 */ - 0xad, 0x05, 0x28, 0x08, 0x14, 0x28, 0x1a, 0x18, 0x16, 0x31, 0x18, 0x18, 0x31, 0x18, 0x19, 0x2a, /* bytes 592 - 608 */ - 0x01, 0x04, 0x05, 0x31, 0xad, 0x05, 0x46, 0x00, 0x09, 0x00, 0x01, 0x00, 0x21, 0x00, 0x03, 0x01, /* bytes 608 - 624 */ - 0x02, 0x05, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x9b, 0x00, /* bytes 624 - 640 */ - 0x2a, 0x00, 0x59, 0x00, 0x7f, 0x00, 0x2c, 0x00, 0x83, 0x00, 0x11, 0x00, 0x29, 0x02, 0x00, 0x35, /* bytes 640 - 656 */ - 0x05, 0x3f, 0xc8, 0x01, 0x36, 0x03, 0x06, 0x36, 0x01, 0x08, 0xce, 0x07, 0x36, 0x03, 0x09, 0x36, /* bytes 656 - 672 */ - 0x01, 0x0a, 0xce, 0x07, 0x3b, 0x04, 0x0b, 0xb6, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 672 - 688 */ - 0x3e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x00, /* bytes 688 - 704 */ - 0x0f, 0x00, 0x54, 0x69, 0x63, 0x54, 0x6f, 0x63, 0x20, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x65, /* bytes 704 - 720 */ - 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x64, 0x72, 0x61, 0x77, 0x00, 0x00, 0x0c, 0x00, 0x6d, 0x69, /* bytes 720 - 736 */ - 0x6e, 0x75, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x07, 0x00, 0x63, 0x6f, /* bytes 736 - 752 */ - 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, /* bytes 752 - 768 */ - 0x74, 0x44, 0x72, 0x61, 0x77, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5f, 0x72, 0x6f, 0x63, 0x6b, 0x79, /* bytes 768 - 784 */ - 0x02, 0x00, 0x6f, 0x6e, 0x03, 0x00, 0x77, 0x66, 0x68, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x57, 0x61, /* bytes 784 - 800 */ - 0x74, 0x63, 0x68, 0x66, 0x61, 0x63, 0x65, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x00, 0x00, 0x00, /* bytes 800 - 816 */ - 0x05, 0x00, 0x72, 0x6f, 0x63, 0x6b, 0x79, 0x00, 0x05, 0x00, 0x77, 0x68, 0x69, 0x74, 0x65, 0x00, /* bytes 816 - 832 */ - 0x03, 0x00, 0x72, 0x65, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x65, 0x00, 0x0b, 0x00, 0x72, 0x65, /* bytes 832 - 848 */ - 0x6e, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6d, 0x69, /* bytes 848 - 864 */ - 0x6e, 0x75, 0x74, 0x65, 0x04, 0x00, 0x68, 0x6f, 0x75, 0x72, 0x00, 0x00, 0x0c, 0x00, 0x63, 0x6c, /* bytes 864 - 880 */ - 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 0x03, 0x00, 0x6d, 0x69, /* bytes 880 - 896 */ - 0x6e, 0x00, 0x00, 0x00, 0x08, 0x00, 0x64, 0x72, 0x61, 0x77, 0x48, 0x61, 0x6e, 0x64, 0x00, 0x00, /* bytes 896 - 912 */ - 0x05, 0x00, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x00, 0x08, 0x00, 0x66, 0x69, 0x6c, 0x6c, 0x52, 0x65, /* bytes 912 - 928 */ - 0x63, 0x74, 0x00, 0x00, 0x0b, 0x00, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x57, 0x69, 0x64, 0x74, /* bytes 928 - 944 */ - 0x68, 0x00, 0x00, 0x00, 0x01, 0x00, 0x68, 0x00, 0x12, 0x00, 0x75, 0x6e, 0x6f, 0x62, 0x73, 0x74, /* bytes 944 - 960 */ - 0x72, 0x75, 0x63, 0x74, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x09, 0x00, 0x66, 0x69, /* bytes 960 - 976 */ - 0x6c, 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x77, 0x00, 0x06, 0x00, 0x63, 0x61, /* bytes 976 - 992 */ - 0x6e, 0x76, 0x61, 0x73, 0x11, 0x00, 0x75, 0x6e, 0x6f, 0x62, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, /* bytes 992 - 1008 */ - 0x65, 0x64, 0x57, 0x69, 0x64, 0x74, 0x68, 0x00, 0x06, 0x00, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, /* bytes 1008 - 1024 */ - 0x09, 0x00, 0x64, 0x72, 0x61, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x00, 0x07, 0x00, 0x63, 0x6f, /* bytes 1024 - 1040 */ - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, /* bytes 1040 - 1056 */ - 0x05, 0x00, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x03, 0x00, 0x63, 0x6f, 0x73, 0x00, 0x00, 0x00, /* bytes 1056 - 1072 */ - 0x06, 0x00, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x06, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x54, 0x6f, /* bytes 1072 - 1088 */ - 0x03, 0x00, 0x73, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, /* bytes 1088 - 1104 */ - 0x53, 0x74, 0x79, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x00, /* bytes 1104 - 1120 */ - 0x09, 0x00, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x00, 0x02, 0x00, 0x63, 0x79, /* bytes 1120 - 1136 */ - 0x09, 0x00, 0x6d, 0x61, 0x78, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x00, 0x09, 0x00, 0x6c, 0x69, /* bytes 1136 - 1152 */ - 0x6e, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x00, 0x09, 0x00, 0x68, 0x61, 0x6e, 0x64, 0x53, 0x74, /* bytes 1152 - 1168 */ - 0x61, 0x74, 0x65, 0x00, 0x03, 0x00, 0x63, 0x74, 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x63, 0x78, /* bytes 1168 - 1184 */ - 0x0b, 0x00, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x41, 0x6e, 0x67, 0x6c, 0x65, 0x00, 0x00, 0x00, /* bytes 1184 - 1200 */ - 0x0b, 0x00, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x67, 0x6c, 0x65, 0x00, 0x00, 0x00, /* bytes 1200 - 1216 */ - 0x09, 0x00, 0x68, 0x6f, 0x75, 0x72, 0x41, 0x6e, 0x67, 0x6c, 0x65, 0x00, 0x0a, 0x00, 0x67, 0x65, /* bytes 1216 - 1232 */ - 0x74, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x0c, 0x00, 0x68, 0x6f, 0x75, 0x72, 0x46, 0x72, /* bytes 1232 - 1248 */ - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x08, 0x00, 0x67, 0x65, 0x74, 0x48, 0x6f, 0x75, /* bytes 1248 - 1264 */ - 0x72, 0x73, 0x00, 0x00, 0x0e, 0x00, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x46, 0x72, 0x61, 0x63, /* bytes 1264 - 1280 */ - 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x00, 0x67, 0x65, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, /* bytes 1280 - 1296 */ - 0x0e, 0x00, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x46, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, /* bytes 1296 - 1312 */ - 0x04, 0x00, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, 0x0c, 0x00, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x77, /* bytes 1312 - 1328 */ - 0x69, 0x73, 0x65, 0x52, 0x61, 0x64, 0x00, 0x00, 0x04, 0x00, 0x44, 0x61, 0x74, 0x65, 0x00, 0x00, /* bytes 1328 - 1344 */ - 0x08, 0x00, 0x66, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x04, 0x00, 0x4d, 0x61, /* bytes 1344 - 1360 */ - 0x74, 0x68, 0x00, 0x00, 0x02, 0x00, 0x50, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, /* bytes 1360 - 1376 */ - 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xe9, 0x3f, 0x52, 0xb8, 0x1e, 0x85, 0xeb, 0x51, 0xe0, 0x3f, +static const uint8_t ALARM_CLOCK_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0xb8, 0x11, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0xff, 0xff, /* bytes 0 - 16 */ + 0x1f, 0x00, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, /* bytes 16 - 32 */ + 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, /* bytes 32 - 48 */ + 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, /* bytes 48 - 64 */ + 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 64 - 80 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, /* bytes 80 - 96 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, /* bytes 96 - 112 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, /* bytes 112 - 128 */ + 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, /* bytes 128 - 144 */ + 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, /* bytes 144 - 160 */ + 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 160 - 176 */ + 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, /* bytes 176 - 192 */ + 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, /* bytes 192 - 208 */ + 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, /* bytes 208 - 224 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, /* bytes 224 - 240 */ + 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x54, 0x00, 0x1c, /* bytes 240 - 256 */ + 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, /* bytes 256 - 272 */ + 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, /* bytes 272 - 288 */ + 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, /* bytes 288 - 304 */ + 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 304 - 320 */ + 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, /* bytes 320 - 336 */ + 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, /* bytes 336 - 352 */ + 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, /* bytes 352 - 368 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, /* bytes 368 - 384 */ + 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x54, /* bytes 384 - 400 */ + 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, /* bytes 400 - 416 */ + 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, /* bytes 416 - 432 */ + 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 432 - 448 */ + 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 448 - 464 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, /* bytes 464 - 480 */ + 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, /* bytes 480 - 496 */ + 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, /* bytes 496 - 512 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, /* bytes 512 - 528 */ + 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, /* bytes 528 - 544 */ + 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 544 - 560 */ + 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 560 - 576 */ + 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 576 - 592 */ + 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, /* bytes 592 - 608 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, /* bytes 608 - 624 */ + 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, /* bytes 624 - 640 */ + 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, /* bytes 640 - 656 */ + 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, /* bytes 656 - 672 */ + 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 672 - 688 */ + 0x00, 0xc4, 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 688 - 704 */ + 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 704 - 720 */ + 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 720 - 736 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, /* bytes 736 - 752 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, /* bytes 752 - 768 */ + 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, /* bytes 768 - 784 */ + 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, /* bytes 784 - 800 */ + 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, /* bytes 800 - 816 */ + 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 816 - 832 */ + 0x00, 0x02, 0x00, 0x1c, 0x02, 0xba, 0x00, 0xc4, 0x01, 0x6a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 832 - 848 */ + 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 848 - 864 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x03, 0x00, 0xc0, /* bytes 864 - 880 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x6f, 0x00, 0x5c, 0x00, 0xbc, 0x00, 0x23, 0x00, /* bytes 880 - 896 */ + 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, /* bytes 896 - 912 */ + 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, /* bytes 912 - 928 */ + 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, /* bytes 928 - 944 */ + 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 944 - 960 */ + 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 960 - 976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, /* bytes 976 - 992 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xe0, 0x00, 0xb4, 0x00, 0x8d, 0x00, 0x03, 0x00, /* bytes 992 - 1008 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x8c, 0x00, 0x1c, 0x02, 0xe0, 0x00, 0x03, /* bytes 1008 - 1024 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, /* bytes 1024 - 1040 */ + 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x74, 0x00, 0x0d, /* bytes 1040 - 1056 */ + 0x01, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, /* bytes 1056 - 1072 */ + 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0x10, 0x01, 0x8c, 0x01, 0x9d, /* bytes 1072 - 1088 */ + 0x00, 0xec, 0x00, 0x9a, 0x00, 0x74, 0x00, 0x0d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1088 - 1104 */ + 0x03, 0x00, 0xf4, 0x00, 0x9f, 0x01, 0x3c, 0x01, 0x5a, 0x01, 0x3c, 0x01, 0xad, 0x00, 0x03, 0x00, /* bytes 1104 - 1120 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x94, 0x00, 0x5c, 0x00, 0xf0, 0x00, 0x03, /* bytes 1120 - 1136 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb4, 0x01, 0x8c, 0x00, 0x14, 0x02, /* bytes 1136 - 1152 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x02, 0xef, 0x00, 0xc4, 0x01, 0x93, /* bytes 1152 - 1168 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, /* bytes 1168 - 1184 */ + 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x74, /* bytes 1184 - 1200 */ + 0x00, 0x33, 0x01, 0x74, 0x00, 0x91, 0x01, 0xa4, 0x00, 0xbc, 0x01, 0xec, 0x00, 0x01, 0x02, 0x8c, /* bytes 1200 - 1216 */ + 0x01, 0x03, 0x02, 0xd4, 0x01, 0xbf, 0x01, 0x04, 0x02, 0x94, 0x01, 0x04, 0x02, 0x33, 0x01, 0x8c, /* bytes 1216 - 1232 */ + 0x01, 0xbb, 0x00, 0xec, 0x00, 0xbb, 0x00, 0x74, 0x00, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1232 - 1248 */ + 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xca, 0x01, 0x3c, 0x01, 0x82, 0x01, 0x3c, 0x01, 0xed, 0x00, /* bytes 1248 - 1264 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x18, 0x02, 0xa4, 0x00, 0xbc, /* bytes 1264 - 1280 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x3f, 0x00, 0x5c, 0x00, /* bytes 1280 - 1296 */ + 0xf5, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x17, 0x02, 0xd4, /* bytes 1296 - 1312 */ + 0x01, 0xbf, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x02, 0xf4, 0x00, /* bytes 1312 - 1328 */ + 0xc4, 0x01, 0x47, 0x00, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 1328 - 1344 */ + 0x00, 0x74, 0x00, 0xcc, 0x00, 0xa4, 0x00, 0x05, 0x01, 0xec, 0x00, 0x5c, 0x01, 0x8c, 0x01, 0x5c, /* bytes 1344 - 1360 */ + 0x01, 0xd4, 0x01, 0x05, 0x01, 0x04, 0x02, 0xcc, 0x00, 0x04, 0x02, 0x0c, 0x00, 0x8c, 0x01, 0x7c, /* bytes 1360 - 1376 */ + 0xff, 0xec, 0x00, 0x7c, 0xff, 0x74, 0x00, 0x0c, 0x00, 0x74, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, /* bytes 1376 - 1392 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xc2, 0x00, 0x3c, 0x01, 0x6c, 0x00, 0x3c, 0x01, /* bytes 1392 - 1408 */ + 0xc9, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x5e, 0xff, 0x1c, /* bytes 1408 - 1424 */ + 0x02, 0xc8, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0x05, 0x01, /* bytes 1424 - 1440 */ + 0x8c, 0x00, 0x08, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xc8, /* bytes 1440 - 1456 */ + 0xff, 0xb4, 0x00, 0x5e, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, /* bytes 1456 - 1472 */ + 0x05, 0x01, 0xec, 0x01, 0x08, 0x02, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 1472 - 1488 */ + 0x00, 0x0b, 0x00, 0xcd, 0x00, 0x29, 0x00, 0x87, 0x00, 0x04, 0x00, 0x57, 0x00, 0xe9, 0xff, 0x59, /* bytes 1488 - 1504 */ + 0x00, 0x84, 0xff, 0xd3, 0x00, 0x3c, 0xff, 0x76, 0x01, 0x3a, 0xff, 0xec, 0x01, 0x83, 0xff, 0xe4, /* bytes 1504 - 1520 */ + 0x01, 0xe3, 0xff, 0xb4, 0x01, 0x02, 0x00, 0x73, 0x01, 0x29, 0x00, 0xcd, 0x00, 0x29, 0x00, 0x03, /* bytes 1520 - 1536 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xd8, 0x00, 0xde, 0xff, 0x20, 0x01, 0xb3, 0xff, /* bytes 1536 - 1552 */ + 0x18, 0x01, 0x67, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x02, /* bytes 1552 - 1568 */ + 0x00, 0xd1, 0x01, 0x66, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfd, 0x01, /* bytes 1568 - 1584 */ + 0x4d, 0xff, 0xa5, 0x01, 0x1a, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6d, /* bytes 1584 - 1600 */ + 0x00, 0x62, 0x00, 0x87, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1600 - 1616 */ + 0x3d, 0x00, 0x4d, 0xff, 0x95, 0x00, 0x1a, 0xff, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 1616 - 1632 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0x32, 0x02, 0xf3, 0xff, 0xc0, 0x01, 0x8d, 0xff, 0x19, 0x01, 0x7c, /* bytes 1632 - 1648 */ + 0xff, 0x82, 0x00, 0xf7, 0xff, 0x7f, 0x00, 0x76, 0x00, 0xac, 0x00, 0x96, 0x00, 0xfb, 0x00, 0xe0, /* bytes 1648 - 1664 */ + 0x00, 0x9a, 0x01, 0xc5, 0x00, 0xde, 0x01, 0xa4, 0x00, 0x2d, 0x02, 0x8f, 0x00, 0x32, 0x02, 0xf3, /* bytes 1664 - 1680 */ + 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0e, 0x01, 0x76, 0x00, 0x56, 0x01, /* bytes 1680 - 1696 */ + 0x3c, 0x00, 0x5a, 0x01, 0xd9, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x06, /* bytes 1696 - 1712 */ + 0x02, 0x28, 0x01, 0xde, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1712 - 1728 */ + 0xd5, 0x00, 0x82, 0xff, 0x7d, 0x00, 0xc5, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 1728 - 1744 */ + 0x00, 0xe5, 0x01, 0x81, 0xff, 0x3e, 0x02, 0xc5, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1744 - 1760 */ + 0x02, 0x00, 0xac, 0x00, 0x96, 0x00, 0xae, 0x00, 0x34, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, /* bytes 1760 - 1776 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xec, 0x01, 0x7d, 0x00, 0xed, 0x01, 0xdb, 0xff, 0x88, /* bytes 1776 - 1792 */ + 0x01, 0x31, 0xff, 0xd7, 0x00, 0x48, 0xff, 0x67, 0x00, 0xcd, 0xff, 0x58, 0x00, 0x70, 0x00, 0x80, /* bytes 1792 - 1808 */ + 0x00, 0x8d, 0x00, 0xe2, 0x00, 0x04, 0x01, 0x7b, 0x01, 0xde, 0x00, 0xb2, 0x01, 0xa7, 0x00, 0xec, /* bytes 1808 - 1824 */ + 0x01, 0x7d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xe1, 0x00, 0x6c, 0x00, /* bytes 1824 - 1840 */ + 0x29, 0x01, 0x24, 0x00, 0x2d, 0x01, 0xc0, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 1840 - 1856 */ + 0x00, 0x7c, 0x00, 0x50, 0x01, 0x80, 0x00, 0x8d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1856 - 1872 */ + 0x02, 0x00, 0xb2, 0x01, 0xa7, 0x00, 0xe2, 0x01, 0x1a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1872 - 1888 */ + 0x00, 0x02, 0x00, 0x0c, 0x02, 0x96, 0xff, 0xb4, 0x01, 0x44, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1888 - 1904 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x96, 0xff, 0xa4, 0x00, 0x44, 0xff, 0x23, 0x00, 0x06, 0x00, /* bytes 1904 - 1920 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x9b, 0x01, 0x02, 0x01, 0xc6, 0x01, 0xac, /* bytes 1920 - 1936 */ + 0x00, 0x0b, 0x02, 0x9e, 0x00, 0x17, 0x02, 0xf5, 0xff, 0x90, 0x01, 0x62, 0xff, 0x07, 0x01, 0x75, /* bytes 1936 - 1952 */ + 0xff, 0x86, 0x00, 0xf1, 0xff, 0x6f, 0x00, 0x8f, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xff, 0x00, 0x1a, /* bytes 1952 - 1968 */ + 0x01, 0x9b, 0x01, 0x02, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x35, 0x01, /* bytes 1968 - 1984 */ + 0xcf, 0xff, 0x46, 0x01, 0x33, 0x00, 0xfe, 0x00, 0x7b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1984 - 2000 */ + 0x00, 0x02, 0x00, 0x30, 0x02, 0x8c, 0xff, 0xd9, 0x01, 0x3a, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2000 - 2016 */ + 0x01, 0x00, 0x02, 0x00, 0xa0, 0x00, 0x38, 0x01, 0xb0, 0x00, 0xb1, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 2016 - 2032 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xc6, 0x01, 0xac, 0x00, 0x00, 0x02, 0x21, 0x01, 0x03, 0x00, 0xc0, /* bytes 2032 - 2048 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc8, 0x00, 0x3a, 0xff, 0x70, 0x00, 0x8c, 0xff, 0x23, 0x00, /* bytes 2048 - 2064 */ + 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xc0, 0x01, 0x8e, 0x00, 0xed, /* bytes 2064 - 2080 */ + 0x01, 0x66, 0x00, 0xeb, 0x01, 0xb9, 0xff, 0x91, 0x01, 0x5d, 0xff, 0xe9, 0x00, 0x51, 0xff, 0x5d, /* bytes 2080 - 2096 */ + 0x00, 0x87, 0xff, 0x67, 0x00, 0x50, 0x00, 0xa9, 0x00, 0x98, 0x00, 0xfa, 0x00, 0xd4, 0x00, 0xa6, /* bytes 2096 - 2112 */ + 0x01, 0xce, 0x00, 0xc0, 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 2112 - 2128 */ + 0xf4, 0x00, 0x52, 0x00, 0x3c, 0x01, 0x0a, 0x00, 0x14, 0x01, 0x83, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 2128 - 2144 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xef, 0x01, 0xe4, 0x00, 0xc0, 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, /* bytes 2144 - 2160 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa9, 0x00, 0x98, 0x00, 0x87, 0x00, 0xe9, 0x00, 0x03, 0x00, /* bytes 2160 - 2176 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x17, 0x02, 0x4b, 0xff, 0xbf, 0x01, 0xfc, 0xfe, 0x03, /* bytes 2176 - 2192 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xaf, 0x00, 0xfc, 0xfe, 0x57, 0x00, 0x4c, 0xff, /* bytes 2192 - 2208 */ + 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xfd, 0x01, 0xab, /* bytes 2208 - 2224 */ + 0xff, 0x7e, 0x01, 0x67, 0xff, 0xdc, 0x00, 0x3a, 0xff, 0x70, 0x00, 0xaf, 0xff, 0x6c, 0x00, 0x66, /* bytes 2224 - 2240 */ + 0x00, 0xa0, 0x00, 0x8b, 0x00, 0xe6, 0x00, 0xdc, 0x00, 0x92, 0x01, 0xcf, 0x00, 0xc9, 0x01, 0x9d, /* bytes 2240 - 2256 */ + 0x00, 0xf7, 0x01, 0x5e, 0x00, 0xfd, 0x01, 0xab, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2256 - 2272 */ + 0x03, 0x00, 0x19, 0x01, 0x95, 0xff, 0x32, 0x01, 0x1a, 0x00, 0xea, 0x00, 0x62, 0x00, 0x03, 0x00, /* bytes 2272 - 2288 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc9, 0x01, 0x9d, 0x00, 0xe1, 0x01, 0xff, 0x00, 0x03, /* bytes 2288 - 2304 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6f, 0x00, 0x13, 0x01, 0xa0, 0x00, 0x8b, 0x00, /* bytes 2304 - 2320 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x97, 0x00, 0x35, 0xff, 0x3e, 0x00, 0x83, /* bytes 2320 - 2336 */ + 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa7, 0x01, 0x35, 0xff, 0xff, 0x01, /* bytes 2336 - 2352 */ + 0x83, 0xff, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xc8, /* bytes 2352 - 2368 */ + 0x00, 0x84, 0x00, 0x01, 0x01, 0xdb, 0x00, 0xab, 0x01, 0xce, 0x00, 0xeb, 0x01, 0x7f, 0x00, 0x12, /* bytes 2368 - 2384 */ + 0x02, 0x50, 0x00, 0x18, 0x02, 0x99, 0xff, 0xaf, 0x01, 0x16, 0xff, 0xf9, 0x00, 0x14, 0xff, 0x8c, /* bytes 2384 - 2400 */ + 0x00, 0x9d, 0xff, 0x7e, 0x00, 0x41, 0x00, 0xc8, 0x00, 0x84, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2400 - 2416 */ + 0x01, 0x00, 0x03, 0x00, 0x4a, 0x01, 0x6f, 0xff, 0x4f, 0x01, 0xf3, 0xff, 0x07, 0x01, 0x3b, 0x00, /* bytes 2416 - 2432 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x79, 0x00, 0x6b, 0xff, 0xd1, 0x00, 0x1e, /* bytes 2432 - 2448 */ + 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc8, 0x00, 0x84, 0x00, 0xa9, 0x00, /* bytes 2448 - 2464 */ + 0xed, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xeb, 0x01, 0x7f, 0x00, 0x01, /* bytes 2464 - 2480 */ + 0x02, 0xd4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe2, 0x01, 0x1e, 0xff, /* bytes 2480 - 2496 */ + 0x39, 0x02, 0x6a, 0xff, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 2496 - 2512 */ + 0x00, 0x5d, 0x00, 0x72, 0x00, 0x94, 0x00, 0x9e, 0x00, 0xd1, 0x00, 0xdf, 0x00, 0x73, 0x01, 0xd8, /* bytes 2512 - 2528 */ + 0x00, 0xbe, 0x01, 0x9b, 0x00, 0xe4, 0x01, 0x63, 0x00, 0xe3, 0x01, 0xae, 0xff, 0x6e, 0x01, 0x33, /* bytes 2528 - 2544 */ + 0xff, 0xc8, 0x00, 0x2e, 0xff, 0x5b, 0x00, 0xae, 0xff, 0x5d, 0x00, 0x72, 0x00, 0x03, 0x00, 0xc0, /* bytes 2544 - 2560 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x7e, 0xff, 0x22, 0x01, 0x08, 0x00, 0xda, 0x00, /* bytes 2560 - 2576 */ + 0x50, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x1b, 0xff, 0x03, /* bytes 2576 - 2592 */ + 0x02, 0x66, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x43, 0x00, 0x66, 0xff, /* bytes 2592 - 2608 */ + 0x9b, 0x00, 0x1c, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x73, 0x00, 0xdc, /* bytes 2608 - 2624 */ + 0x00, 0x94, 0x00, 0x9e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbe, 0x01, /* bytes 2624 - 2640 */ + 0x9b, 0x00, 0xc9, 0x01, 0xe0, 0x00, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 2640 - 2656 */ + 0x00, 0x0b, 0x00, 0x9e, 0x01, 0x77, 0xff, 0x01, 0x01, 0x75, 0xff, 0x91, 0x00, 0xef, 0xff, 0x95, /* bytes 2656 - 2672 */ + 0x00, 0x8d, 0x00, 0xbb, 0x00, 0xc6, 0x00, 0x02, 0x01, 0x05, 0x01, 0x9f, 0x01, 0x0e, 0x01, 0xf1, /* bytes 2672 - 2688 */ + 0x01, 0xc5, 0x00, 0x23, 0x02, 0xa5, 0x00, 0x1c, 0x02, 0xf2, 0xff, 0x9e, 0x01, 0x77, 0xff, 0x03, /* bytes 2688 - 2704 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x54, 0x01, 0xc4, 0xff, 0x58, 0x01, 0x44, 0x00, /* bytes 2704 - 2720 */ + 0x10, 0x01, 0x8c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x00, 0xb3, /* bytes 2720 - 2736 */ + 0xff, 0xd0, 0x00, 0x55, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x38, 0x02, /* bytes 2736 - 2752 */ + 0xb4, 0xff, 0xe0, 0x01, 0x54, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbb, /* bytes 2752 - 2768 */ + 0x00, 0xc6, 0x00, 0xa8, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2768 - 2784 */ + 0x0a, 0x02, 0x21, 0x01, 0xf1, 0x01, 0xc5, 0x00, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 2784 - 2800 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0xd0, 0x01, 0x13, 0x01, 0x08, 0x02, 0xc9, 0x00, 0x03, 0x02, 0x0d, /* bytes 2800 - 2816 */ + 0x00, 0x8d, 0x01, 0x78, 0xff, 0xee, 0x00, 0x71, 0xff, 0x71, 0x00, 0xff, 0xff, 0x78, 0x00, 0xcc, /* bytes 2816 - 2832 */ + 0x00, 0xa4, 0x00, 0x0c, 0x01, 0xe9, 0x00, 0x5e, 0x01, 0x8d, 0x01, 0x60, 0x01, 0xd0, 0x01, 0x13, /* bytes 2832 - 2848 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xc3, 0x00, 0x3c, 0x01, /* bytes 2848 - 2864 */ + 0x6b, 0x00, 0x3b, 0x01, 0xcd, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x59, /* bytes 2864 - 2880 */ + 0x00, 0xbb, 0xff, 0xb1, 0x00, 0x4b, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2880 - 2896 */ + 0xc2, 0x01, 0x4b, 0xff, 0x1a, 0x02, 0xbb, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 2896 - 2912 */ + 0x00, 0x89, 0x00, 0x82, 0x01, 0xa4, 0x00, 0x0c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2912 - 2928 */ + 0x02, 0x00, 0xd0, 0x01, 0x13, 0x01, 0xed, 0x01, 0x7f, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, /* bytes 2928 - 2944 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xa3, 0x00, 0xd0, 0x01, 0xec, 0x00, 0x06, 0x02, 0x8c, /* bytes 2944 - 2960 */ + 0x01, 0x05, 0x02, 0xd3, 0x01, 0xcf, 0x01, 0x02, 0x02, 0xac, 0x01, 0x04, 0x02, 0x33, 0x01, 0x8e, /* bytes 2960 - 2976 */ + 0x01, 0xd9, 0x00, 0xed, 0x00, 0xd9, 0x00, 0x74, 0x00, 0x33, 0x01, 0x73, 0x00, 0xaf, 0x01, 0xa3, /* bytes 2976 - 2992 */ + 0x00, 0xd0, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3b, 0x01, 0x07, 0x01, /* bytes 2992 - 3008 */ + 0x3c, 0x01, 0x70, 0x01, 0xf4, 0x00, 0xa6, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 3008 - 3024 */ + 0x00, 0xa3, 0x00, 0xd0, 0x01, 0x8d, 0x00, 0x16, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 3024 - 3040 */ + 0x02, 0x00, 0xc6, 0x01, 0xd1, 0xff, 0x1e, 0x02, 0x13, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 3040 - 3056 */ + 0x00, 0x02, 0x00, 0xee, 0x01, 0x18, 0x02, 0xd3, 0x01, 0xcf, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3056 - 3072 */ + 0x01, 0x00, 0x02, 0x00, 0xb6, 0x00, 0x19, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x23, 0x00, 0x06, 0x00, /* bytes 3072 - 3088 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x1e, 0x01, 0x8d, 0x01, 0xbe, /* bytes 3088 - 3104 */ + 0x00, 0xed, 0x00, 0xbf, 0x00, 0x74, 0x00, 0x1d, 0x01, 0x75, 0x00, 0x9a, 0x01, 0xa3, 0x00, 0xbf, /* bytes 3104 - 3120 */ + 0x01, 0xec, 0x00, 0xf6, 0x01, 0x8c, 0x01, 0xf7, 0x01, 0xd4, 0x01, 0xbf, 0x01, 0x05, 0x02, 0x9b, /* bytes 3120 - 3136 */ + 0x01, 0x04, 0x02, 0x1e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3b, 0x01, /* bytes 3136 - 3152 */ + 0xf2, 0x00, 0x3c, 0x01, 0x5b, 0x01, 0xf4, 0x00, 0x93, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 3152 - 3168 */ + 0x00, 0x02, 0x00, 0xec, 0x01, 0x13, 0x02, 0xd4, 0x01, 0xbf, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3168 - 3184 */ + 0x01, 0x00, 0x02, 0x00, 0x5d, 0x00, 0xf5, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3184 - 3200 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x13, 0x02, 0xa3, 0x00, 0xbf, 0x01, 0x03, 0x00, 0xc0, /* bytes 3200 - 3216 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0xf5, 0x00, 0xc5, 0x01, 0xb0, 0x00, 0x23, 0x00, /* bytes 3216 - 3232 */ + 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xec, 0x00, 0x46, 0x00, 0x74, /* bytes 3232 - 3248 */ + 0x00, 0xc7, 0x00, 0x74, 0x00, 0x73, 0x01, 0xa4, 0x00, 0xa8, 0x01, 0xec, 0x00, 0xf5, 0x01, 0x8c, /* bytes 3248 - 3264 */ + 0x01, 0xf5, 0x01, 0xd4, 0x01, 0xa7, 0x01, 0x04, 0x02, 0x74, 0x01, 0x04, 0x02, 0xc8, 0x00, 0x8c, /* bytes 3264 - 3280 */ + 0x01, 0x46, 0x00, 0xec, 0x00, 0x46, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 3280 - 3296 */ + 0xf4, 0x00, 0x6b, 0x01, 0x3c, 0x01, 0x1e, 0x01, 0x3c, 0x01, 0x8c, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3296 - 3312 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x17, 0x02, 0xd4, 0x01, 0xa7, 0x01, 0x03, 0x00, 0xc0, /* bytes 3312 - 3328 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x4d, 0x00, 0x1d, 0x02, 0xac, 0x00, 0x03, 0x00, /* bytes 3328 - 3344 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x18, 0x02, 0xa4, 0x00, 0xa8, 0x01, 0x03, /* bytes 3344 - 3360 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x4d, 0x00, /* bytes 3360 - 3376 */ + 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x7b, /* bytes 3376 - 3392 */ + 0x01, 0x04, 0x02, 0xd8, 0x00, 0x8c, 0x01, 0x5e, 0x00, 0xec, 0x00, 0x5e, 0x00, 0x74, 0x00, 0xd8, /* bytes 3392 - 3408 */ + 0x00, 0x74, 0x00, 0x7b, 0x01, 0xa4, 0x00, 0xac, 0x01, 0xec, 0x00, 0xf5, 0x01, 0x8c, 0x01, 0xf5, /* bytes 3408 - 3424 */ + 0x01, 0xd4, 0x01, 0xac, 0x01, 0x04, 0x02, 0x7b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 3424 - 3440 */ + 0x03, 0x00, 0x3c, 0x01, 0x9f, 0x00, 0x3c, 0x01, 0x2a, 0x01, 0xf4, 0x00, 0x73, 0x01, 0x03, 0x00, /* bytes 3440 - 3456 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x28, 0x00, 0x5c, 0x00, 0x82, 0x00, 0x03, /* bytes 3456 - 3472 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x02, 0xa9, 0x00, 0xc4, 0x01, 0x4f, 0x00, /* bytes 3472 - 3488 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xac, 0x01, 0x8c, 0x00, 0x12, /* bytes 3488 - 3504 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xac, 0x01, 0xec, 0x01, /* bytes 3504 - 3520 */ + 0x12, 0x02, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, /* bytes 3520 - 3536 */ + 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, /* bytes 3536 - 3552 */ + 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, /* bytes 3552 - 3568 */ + 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3568 - 3584 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, /* bytes 3584 - 3600 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x00, 0x46, 0x00, 0x5c, 0x00, 0x9e, /* bytes 3600 - 3616 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb4, 0x01, 0xec, 0x01, /* bytes 3616 - 3632 */ + 0x14, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x02, 0x9b, 0x00, 0xc4, /* bytes 3632 - 3648 */ + 0x01, 0x43, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, /* bytes 3648 - 3664 */ + 0xa4, 0x00, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 3664 - 3680 */ + 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, /* bytes 3680 - 3696 */ + 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, /* bytes 3696 - 3712 */ + 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, /* bytes 3712 - 3728 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, /* bytes 3728 - 3744 */ + 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, /* bytes 3744 - 3760 */ + 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, /* bytes 3760 - 3776 */ + 0xd4, 0x01, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xa9, /* bytes 3776 - 3792 */ + 0x00, 0xb4, 0x00, 0x51, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x02, /* bytes 3792 - 3808 */ + 0xa8, 0x00, 0xc4, 0x01, 0x50, 0x00, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 3808 - 3824 */ + 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, /* bytes 3824 - 3840 */ + 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, /* bytes 3840 - 3856 */ + 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, /* bytes 3856 - 3872 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, /* bytes 3872 - 3888 */ + 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x54, /* bytes 3888 - 3904 */ + 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, /* bytes 3904 - 3920 */ + 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, /* bytes 3920 - 3936 */ + 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3936 - 3952 */ + 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3952 - 3968 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, /* bytes 3968 - 3984 */ + 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, /* bytes 3984 - 4000 */ + 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, /* bytes 4000 - 4016 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, /* bytes 4016 - 4032 */ + 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, /* bytes 4032 - 4048 */ + 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4048 - 4064 */ + 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 4064 - 4080 */ + 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4080 - 4096 */ + 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, /* bytes 4096 - 4112 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, 0x00, 0x8c, /* bytes 4112 - 4128 */ + 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, 0x01, 0xa4, /* bytes 4128 - 4144 */ + 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, 0x01, 0x04, /* bytes 4144 - 4160 */ + 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, 0xac, 0x00, /* bytes 4160 - 4176 */ + 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 4176 - 4192 */ + 0x00, 0xc4, 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4192 - 4208 */ + 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 4208 - 4224 */ + 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 4224 - 4240 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x06, 0x00, /* bytes 4240 - 4256 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, 0x02, 0xe4, /* bytes 4256 - 4272 */ + 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, 0x00, 0x84, /* bytes 4272 - 4288 */ + 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, 0x01, 0xb4, /* bytes 4288 - 4304 */ + 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x01, /* bytes 4304 - 4320 */ + 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 4320 - 4336 */ + 0x00, 0x02, 0x00, 0xc4, 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 4336 - 4352 */ + 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 4352 - 4368 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, /* bytes 4368 - 4384 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, 0x23, 0x00, /* bytes 4384 - 4400 */ + 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x02, 0x84, 0x01, 0x04, /* bytes 4400 - 4416 */ + 0x02, 0xe4, 0x00, 0x8c, 0x01, 0x6c, 0x00, 0xec, 0x00, 0x6c, 0x00, 0x74, 0x00, 0xe4, 0x00, 0x74, /* bytes 4416 - 4432 */ + 0x00, 0x84, 0x01, 0xa4, 0x00, 0xb4, 0x01, 0xec, 0x00, 0xfc, 0x01, 0x8c, 0x01, 0xfc, 0x01, 0xd4, /* bytes 4432 - 4448 */ + 0x01, 0xb4, 0x01, 0x04, 0x02, 0x84, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 4448 - 4464 */ + 0x3c, 0x01, 0xac, 0x00, 0x3c, 0x01, 0x34, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 4464 - 4480 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xc4, 0x01, 0x54, 0x00, 0x1c, 0x02, 0xac, 0x00, 0x03, 0x00, 0xc0, /* bytes 4480 - 4496 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x00, 0x14, 0x02, 0xa4, 0x00, 0xb4, 0x01, 0x03, 0x00, /* bytes 4496 - 4512 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x00, 0xac, 0x00, 0xb4, 0x00, 0x54, 0x00, 0x03, /* bytes 4512 - 4528 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x14, 0x02, 0xd4, 0x01, 0xb4, 0x01, +}; +__attribute__ ((aligned (8))) +static const uint8_t BATTERY_ICON_CHARGING_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x66, 0x53, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0xff, 0xff, /* bytes 0 - 16 */ + 0xae, 0x00, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, /* bytes 16 - 32 */ + 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, /* bytes 32 - 48 */ + 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, /* bytes 48 - 64 */ + 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, /* bytes 64 - 80 */ + 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, /* bytes 80 - 96 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, /* bytes 96 - 112 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, 0x01, /* bytes 112 - 128 */ + 0x34, 0x02, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, /* bytes 128 - 144 */ + 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, /* bytes 144 - 160 */ + 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, /* bytes 160 - 176 */ + 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, /* bytes 176 - 192 */ + 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, /* bytes 192 - 208 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, /* bytes 208 - 224 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xec, 0xff, 0xe4, 0x01, /* bytes 224 - 240 */ + 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, /* bytes 240 - 256 */ + 0x01, 0x34, 0x02, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, /* bytes 256 - 272 */ + 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, /* bytes 272 - 288 */ + 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, /* bytes 288 - 304 */ + 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, /* bytes 304 - 320 */ + 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, /* bytes 320 - 336 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, /* bytes 336 - 352 */ + 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, /* bytes 352 - 368 */ + 0x01, 0x34, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x6c, 0x00, /* bytes 368 - 384 */ + 0xe4, 0x01, 0x4c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x02, /* bytes 384 - 400 */ + 0x00, 0xe4, 0x01, 0x22, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 400 - 416 */ + 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, /* bytes 416 - 432 */ + 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, /* bytes 432 - 448 */ + 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, /* bytes 448 - 464 */ + 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, /* bytes 464 - 480 */ + 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, /* bytes 480 - 496 */ + 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, /* bytes 496 - 512 */ + 0x02, 0xbc, 0x01, 0x34, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, /* bytes 512 - 528 */ + 0x08, 0x00, 0xe4, 0x01, 0x28, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, /* bytes 528 - 544 */ + 0x01, 0xac, 0x00, 0xe4, 0x01, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 544 - 560 */ + 0xe4, 0x01, 0x82, 0x00, 0xe4, 0x01, 0x62, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 560 - 576 */ + 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, /* bytes 576 - 592 */ + 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, /* bytes 592 - 608 */ + 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 608 - 624 */ + 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, /* bytes 624 - 640 */ + 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 640 - 656 */ + 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 656 - 672 */ + 0x0c, 0x02, 0x34, 0x02, 0xbc, 0x01, 0x34, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 672 - 688 */ + 0x00, 0xe4, 0x01, 0x0b, 0x00, 0xe4, 0x01, 0x2b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 688 - 704 */ + 0x02, 0x00, 0xe4, 0x01, 0x88, 0x00, 0xe4, 0x01, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 704 - 720 */ + 0x00, 0x02, 0x00, 0xe4, 0x01, 0xc2, 0x00, 0xe4, 0x01, 0xe2, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, /* bytes 720 - 736 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, /* bytes 736 - 752 */ + 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, /* bytes 752 - 768 */ + 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 768 - 784 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, /* bytes 784 - 800 */ + 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 800 - 816 */ + 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 816 - 832 */ + 0x00, 0x02, 0x00, 0xe4, 0x01, 0x2c, 0x00, 0xe4, 0x01, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 832 - 848 */ + 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, 0x01, 0x34, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 848 - 864 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xc8, 0x00, 0xe4, 0x01, 0xe8, 0x00, 0x03, 0x00, 0xc0, /* bytes 864 - 880 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x6b, 0x00, 0xe4, 0x01, 0x8b, 0x00, 0x23, 0x00, /* bytes 880 - 896 */ + 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 896 - 912 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 912 - 928 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 928 - 944 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 944 - 960 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 960 - 976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 976 - 992 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x30, 0x00, 0xe4, 0x01, 0x10, 0x00, 0x03, 0x00, /* bytes 992 - 1008 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xf0, 0x00, 0xe4, 0x01, 0xd0, 0x00, 0x03, /* bytes 1008 - 1024 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x33, 0x02, 0xbc, 0x01, 0x34, 0x02, /* bytes 1024 - 1040 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x90, 0x00, 0xe4, 0x01, 0x70, /* bytes 1040 - 1056 */ + 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 1056 - 1072 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 1072 - 1088 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 1088 - 1104 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 1104 - 1120 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 1120 - 1136 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 1136 - 1152 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x34, 0x00, 0xe4, 0x01, 0x54, /* bytes 1152 - 1168 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0x34, 0x02, 0x0c, 0x02, /* bytes 1168 - 1184 */ + 0x16, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xb4, 0x00, 0xe4, /* bytes 1184 - 1200 */ + 0x01, 0x94, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xf4, 0x00, /* bytes 1200 - 1216 */ + 0xe4, 0x01, 0x14, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, /* bytes 1216 - 1232 */ + 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, /* bytes 1232 - 1248 */ + 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, /* bytes 1248 - 1264 */ + 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, /* bytes 1264 - 1280 */ + 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, /* bytes 1280 - 1296 */ + 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, /* bytes 1296 - 1312 */ + 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0xf1, 0x01, /* bytes 1312 - 1328 */ + 0xbc, 0x01, 0x1f, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xba, /* bytes 1328 - 1344 */ + 0x00, 0xe4, 0x01, 0x9a, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 1344 - 1360 */ + 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, /* bytes 1360 - 1376 */ + 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, /* bytes 1376 - 1392 */ + 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, /* bytes 1392 - 1408 */ + 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, /* bytes 1408 - 1424 */ + 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, /* bytes 1424 - 1440 */ + 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0xf9, /* bytes 1440 - 1456 */ + 0x01, 0x0c, 0x02, 0xe4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, /* bytes 1456 - 1472 */ + 0x12, 0x01, 0xe4, 0x01, 0xf2, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 1472 - 1488 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 1488 - 1504 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 1504 - 1520 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 1520 - 1536 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 1536 - 1552 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 1552 - 1568 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, /* bytes 1568 - 1584 */ + 0x12, 0x01, 0xe4, 0x01, 0x1a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbb, /* bytes 1584 - 1600 */ + 0x01, 0xe4, 0x01, 0x0d, 0x02, 0xe7, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 1600 - 1616 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 1616 - 1632 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 1632 - 1648 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 1648 - 1664 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 1664 - 1680 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 1680 - 1696 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0d, /* bytes 1696 - 1712 */ + 0x02, 0xeb, 0x01, 0xbb, 0x01, 0xe2, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 1712 - 1728 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 1728 - 1744 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 1744 - 1760 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 1760 - 1776 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 1776 - 1792 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 1792 - 1808 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbb, /* bytes 1808 - 1824 */ + 0x01, 0xe3, 0x01, 0x0d, 0x02, 0xec, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 1824 - 1840 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 1840 - 1856 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 1856 - 1872 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 1872 - 1888 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 1888 - 1904 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 1904 - 1920 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 1920 - 1936 */ + 0x02, 0xed, 0x01, 0xba, 0x01, 0xe5, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 1936 - 1952 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 1952 - 1968 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 1968 - 1984 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 1984 - 2000 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2000 - 2016 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2016 - 2032 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 2032 - 2048 */ + 0x02, 0xea, 0x01, 0xba, 0x01, 0xf4, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2048 - 2064 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2064 - 2080 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2080 - 2096 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2096 - 2112 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2112 - 2128 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2128 - 2144 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 2144 - 2160 */ + 0x01, 0xf8, 0x01, 0x0e, 0x02, 0xf4, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2160 - 2176 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2176 - 2192 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2192 - 2208 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2208 - 2224 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2224 - 2240 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2240 - 2256 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 2256 - 2272 */ + 0x02, 0xf8, 0x01, 0xba, 0x01, 0xfa, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2272 - 2288 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2288 - 2304 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2304 - 2320 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2320 - 2336 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2336 - 2352 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2352 - 2368 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 2368 - 2384 */ + 0x02, 0xfa, 0x01, 0xba, 0x01, 0xfd, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2384 - 2400 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2400 - 2416 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2416 - 2432 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2432 - 2448 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2448 - 2464 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2464 - 2480 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 2480 - 2496 */ + 0x02, 0xfd, 0x01, 0xba, 0x01, 0xfe, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2496 - 2512 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2512 - 2528 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2528 - 2544 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2544 - 2560 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2560 - 2576 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2576 - 2592 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 2592 - 2608 */ + 0x02, 0xfe, 0x01, 0xba, 0x01, 0xfe, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2608 - 2624 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2624 - 2640 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2640 - 2656 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2656 - 2672 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2672 - 2688 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2688 - 2704 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 2704 - 2720 */ + 0x01, 0xfe, 0x01, 0x0e, 0x02, 0xfe, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2720 - 2736 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2736 - 2752 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2752 - 2768 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2768 - 2784 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2784 - 2800 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2800 - 2816 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 2816 - 2832 */ + 0x01, 0xfc, 0x01, 0x0e, 0x02, 0xfe, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2832 - 2848 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2848 - 2864 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2864 - 2880 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2880 - 2896 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 2896 - 2912 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 2912 - 2928 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 2928 - 2944 */ + 0x02, 0xfc, 0x01, 0xba, 0x01, 0xfb, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2944 - 2960 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 2960 - 2976 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 2976 - 2992 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 2992 - 3008 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3008 - 3024 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3024 - 3040 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3040 - 3056 */ + 0x01, 0xf8, 0x01, 0x0e, 0x02, 0xfb, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3056 - 3072 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3072 - 3088 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3088 - 3104 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3104 - 3120 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3120 - 3136 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3136 - 3152 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3152 - 3168 */ + 0x01, 0xf6, 0x01, 0x0e, 0x02, 0xf8, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3168 - 3184 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3184 - 3200 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3200 - 3216 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3216 - 3232 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3232 - 3248 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3248 - 3264 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 3264 - 3280 */ + 0x02, 0xf6, 0x01, 0xba, 0x01, 0xf3, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3280 - 3296 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3296 - 3312 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3312 - 3328 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3328 - 3344 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3344 - 3360 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3360 - 3376 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3376 - 3392 */ + 0x01, 0xf1, 0x01, 0x0e, 0x02, 0xf3, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3392 - 3408 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3408 - 3424 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3424 - 3440 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3440 - 3456 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3456 - 3472 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3472 - 3488 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3488 - 3504 */ + 0x01, 0xf0, 0x01, 0x0e, 0x02, 0xf1, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3504 - 3520 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3520 - 3536 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3536 - 3552 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3552 - 3568 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3568 - 3584 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3584 - 3600 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3600 - 3616 */ + 0x01, 0xee, 0x01, 0x0e, 0x02, 0xf0, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3616 - 3632 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3632 - 3648 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3648 - 3664 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3664 - 3680 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3680 - 3696 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3696 - 3712 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3712 - 3728 */ + 0x01, 0xee, 0x01, 0x0e, 0x02, 0xee, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3728 - 3744 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3744 - 3760 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3760 - 3776 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3776 - 3792 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3792 - 3808 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3808 - 3824 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 3824 - 3840 */ + 0x02, 0xee, 0x01, 0xba, 0x01, 0xee, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3840 - 3856 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3856 - 3872 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3872 - 3888 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 3888 - 3904 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 3904 - 3920 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 3920 - 3936 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 3936 - 3952 */ + 0x01, 0xee, 0x01, 0x0e, 0x02, 0xee, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3952 - 3968 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 3968 - 3984 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 3984 - 4000 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4000 - 4016 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4016 - 4032 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4032 - 4048 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 4048 - 4064 */ + 0x02, 0xee, 0x01, 0xba, 0x01, 0xef, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4064 - 4080 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4080 - 4096 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4096 - 4112 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4112 - 4128 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4128 - 4144 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4144 - 4160 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 4160 - 4176 */ + 0x01, 0xf0, 0x01, 0x0e, 0x02, 0xef, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4176 - 4192 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4192 - 4208 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4208 - 4224 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4224 - 4240 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4240 - 4256 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4256 - 4272 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 4272 - 4288 */ + 0x01, 0xf2, 0x01, 0x0e, 0x02, 0xf0, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4288 - 4304 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4304 - 4320 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4320 - 4336 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4336 - 4352 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4352 - 4368 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4368 - 4384 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 4384 - 4400 */ + 0x01, 0xf3, 0x01, 0x0e, 0x02, 0xf2, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4400 - 4416 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4416 - 4432 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4432 - 4448 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4448 - 4464 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4464 - 4480 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4480 - 4496 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 4496 - 4512 */ + 0x02, 0xf3, 0x01, 0xba, 0x01, 0xf5, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4512 - 4528 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4528 - 4544 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4544 - 4560 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4560 - 4576 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4576 - 4592 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4592 - 4608 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, /* bytes 4608 - 4624 */ + 0x02, 0xf5, 0x01, 0xba, 0x01, 0xf6, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4624 - 4640 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4640 - 4656 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4656 - 4672 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4672 - 4688 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4688 - 4704 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4704 - 4720 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 4720 - 4736 */ + 0x01, 0xf7, 0x01, 0x0e, 0x02, 0xf6, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4736 - 4752 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4752 - 4768 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4768 - 4784 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4784 - 4800 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4800 - 4816 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4816 - 4832 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 4832 - 4848 */ + 0x01, 0xf8, 0x01, 0x0e, 0x02, 0xf7, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4848 - 4864 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4864 - 4880 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4880 - 4896 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 4896 - 4912 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 4912 - 4928 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 4928 - 4944 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, /* bytes 4944 - 4960 */ + 0x01, 0xf8, 0x01, 0x0e, 0x02, 0xf8, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 4960 - 4976 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 4976 - 4992 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 4992 - 5008 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 5008 - 5024 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 5024 - 5040 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 5040 - 5056 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, /* bytes 5056 - 5072 */ + 0x01, 0xec, 0xff, 0xe4, 0x01, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5072 - 5088 */ + 0xba, 0x01, 0xf8, 0x01, 0x0e, 0x02, 0xf8, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 5088 - 5104 */ + 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, /* bytes 5104 - 5120 */ + 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, /* bytes 5120 - 5136 */ + 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 5136 - 5152 */ + 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, /* bytes 5152 - 5168 */ + 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 5168 - 5184 */ + 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5184 - 5200 */ + 0x0e, 0x02, 0xf8, 0x01, 0xba, 0x01, 0xf8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 5200 - 5216 */ + 0x00, 0xe4, 0x01, 0x6c, 0x00, 0xe4, 0x01, 0x4c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 5216 - 5232 */ + 0x02, 0x00, 0xe4, 0x01, 0x02, 0x00, 0xe4, 0x01, 0x22, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, /* bytes 5232 - 5248 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, /* bytes 5248 - 5264 */ + 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, /* bytes 5264 - 5280 */ + 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 5280 - 5296 */ + 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, /* bytes 5296 - 5312 */ + 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5312 - 5328 */ + 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 5328 - 5344 */ + 0x02, 0x00, 0xe4, 0x01, 0x08, 0x00, 0xe4, 0x01, 0x28, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 5344 - 5360 */ + 0x00, 0x02, 0x00, 0x0e, 0x02, 0xf8, 0x01, 0xba, 0x01, 0xf7, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 5360 - 5376 */ + 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xac, 0x00, 0xe4, 0x01, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 5376 - 5392 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x82, 0x00, 0xe4, 0x01, 0x62, 0x00, 0x23, 0x00, 0x07, /* bytes 5392 - 5408 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, /* bytes 5408 - 5424 */ + 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, /* bytes 5424 - 5440 */ + 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, /* bytes 5440 - 5456 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, /* bytes 5456 - 5472 */ + 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 5472 - 5488 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 5488 - 5504 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x0b, 0x00, 0xe4, 0x01, 0x2b, 0x00, 0x03, 0x00, 0xc0, /* bytes 5504 - 5520 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x02, 0xf7, 0x01, 0xba, 0x01, 0xf6, 0x01, 0x03, 0x00, /* bytes 5520 - 5536 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x88, 0x00, 0xe4, 0x01, 0x68, 0x00, 0x03, /* bytes 5536 - 5552 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xc2, 0x00, 0xe4, 0x01, 0xe2, 0x00, /* bytes 5552 - 5568 */ + 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, /* bytes 5568 - 5584 */ + 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, /* bytes 5584 - 5600 */ + 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, /* bytes 5600 - 5616 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, /* bytes 5616 - 5632 */ + 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, /* bytes 5632 - 5648 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, /* bytes 5648 - 5664 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x2c, 0x00, 0xe4, 0x01, 0x0c, 0x00, /* bytes 5664 - 5680 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, 0x01, 0xf5, 0x01, 0x0e, 0x02, 0xf6, /* bytes 5680 - 5696 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xc8, 0x00, 0xe4, 0x01, /* bytes 5696 - 5712 */ + 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x6b, 0x00, 0xe4, /* bytes 5712 - 5728 */ + 0x01, 0x8b, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, /* bytes 5728 - 5744 */ + 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, /* bytes 5744 - 5760 */ + 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, /* bytes 5760 - 5776 */ + 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, /* bytes 5776 - 5792 */ + 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, /* bytes 5792 - 5808 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, /* bytes 5808 - 5824 */ + 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x30, 0x00, 0xe4, /* bytes 5824 - 5840 */ + 0x01, 0x10, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xf0, 0x00, /* bytes 5840 - 5856 */ + 0xe4, 0x01, 0xd0, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x90, /* bytes 5856 - 5872 */ + 0x00, 0xe4, 0x01, 0x70, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x02, /* bytes 5872 - 5888 */ + 0xf4, 0x01, 0xba, 0x01, 0xf4, 0x01, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 5888 - 5904 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 5904 - 5920 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 5920 - 5936 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 5936 - 5952 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 5952 - 5968 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 5968 - 5984 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x02, /* bytes 5984 - 6000 */ + 0xd6, 0x01, 0xba, 0x01, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, /* bytes 6000 - 6016 */ + 0x01, 0x34, 0x00, 0xe4, 0x01, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6016 - 6032 */ + 0xe4, 0x01, 0xb4, 0x00, 0xe4, 0x01, 0x94, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 6032 - 6048 */ + 0x00, 0xe4, 0x01, 0xf4, 0x00, 0xe4, 0x01, 0x14, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, /* bytes 6048 - 6064 */ + 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, /* bytes 6064 - 6080 */ + 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, /* bytes 6080 - 6096 */ + 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 6096 - 6112 */ + 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, /* bytes 6112 - 6128 */ + 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6128 - 6144 */ + 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 6144 - 6160 */ + 0x00, 0xe4, 0x01, 0xba, 0x00, 0xe4, 0x01, 0x9a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 6160 - 6176 */ + 0x02, 0x00, 0x0e, 0x02, 0xb1, 0x01, 0xba, 0x01, 0xdf, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, /* bytes 6176 - 6192 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, /* bytes 6192 - 6208 */ + 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, /* bytes 6208 - 6224 */ + 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 6224 - 6240 */ + 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, /* bytes 6240 - 6256 */ + 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 6256 - 6272 */ + 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 6272 - 6288 */ + 0x02, 0x00, 0xe4, 0x01, 0x12, 0x01, 0xe4, 0x01, 0xf2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 6288 - 6304 */ + 0x00, 0x02, 0x00, 0xb9, 0x01, 0xb9, 0x01, 0x0f, 0x02, 0xa4, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, /* bytes 6304 - 6320 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, /* bytes 6320 - 6336 */ + 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, /* bytes 6336 - 6352 */ + 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 6352 - 6368 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, /* bytes 6368 - 6384 */ + 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6384 - 6400 */ + 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 6400 - 6416 */ + 0x00, 0x02, 0x00, 0xe4, 0x01, 0x12, 0x01, 0xe4, 0x01, 0x1a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6416 - 6432 */ + 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0xa4, 0x01, 0x10, 0x02, 0xa7, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 6432 - 6448 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 6448 - 6464 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 6464 - 6480 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 6480 - 6496 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 6496 - 6512 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6512 - 6528 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6528 - 6544 */ + 0x01, 0x00, 0x02, 0x00, 0x11, 0x02, 0xab, 0x01, 0xb7, 0x01, 0xa2, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 6544 - 6560 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 6560 - 6576 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 6576 - 6592 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 6592 - 6608 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 6608 - 6624 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6624 - 6640 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6640 - 6656 */ + 0x01, 0x00, 0x02, 0x00, 0xb6, 0x01, 0xa3, 0x01, 0x12, 0x02, 0xac, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 6656 - 6672 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 6672 - 6688 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 6688 - 6704 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 6704 - 6720 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 6720 - 6736 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6736 - 6752 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6752 - 6768 */ + 0x01, 0x00, 0x02, 0x00, 0x12, 0x02, 0xad, 0x01, 0xb6, 0x01, 0xa5, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 6768 - 6784 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 6784 - 6800 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 6800 - 6816 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 6816 - 6832 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 6832 - 6848 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6848 - 6864 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6864 - 6880 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xaa, 0x01, 0xb5, 0x01, 0xb4, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 6880 - 6896 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 6896 - 6912 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 6912 - 6928 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 6928 - 6944 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 6944 - 6960 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6960 - 6976 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6976 - 6992 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb8, 0x01, 0x13, 0x02, 0xb4, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 6992 - 7008 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7008 - 7024 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7024 - 7040 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7040 - 7056 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7056 - 7072 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7072 - 7088 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7088 - 7104 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb8, 0x01, 0xb5, 0x01, 0xba, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7104 - 7120 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7120 - 7136 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7136 - 7152 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7152 - 7168 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7168 - 7184 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7184 - 7200 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7200 - 7216 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xbd, 0x01, 0x13, 0x02, 0xba, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7216 - 7232 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7232 - 7248 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7248 - 7264 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7264 - 7280 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7280 - 7296 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7296 - 7312 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7312 - 7328 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xbd, 0x01, 0xb5, 0x01, 0xbe, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7328 - 7344 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7344 - 7360 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7360 - 7376 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7376 - 7392 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7392 - 7408 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7408 - 7424 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7424 - 7440 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xbe, 0x01, 0xb5, 0x01, 0xbe, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7440 - 7456 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7456 - 7472 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7472 - 7488 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7488 - 7504 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7504 - 7520 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7520 - 7536 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7536 - 7552 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xbe, 0x01, 0x13, 0x02, 0xbe, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7552 - 7568 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7568 - 7584 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7584 - 7600 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7600 - 7616 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7616 - 7632 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7632 - 7648 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7648 - 7664 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xbe, 0x01, 0xb5, 0x01, 0xbc, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7664 - 7680 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7680 - 7696 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7696 - 7712 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7712 - 7728 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7728 - 7744 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7744 - 7760 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7760 - 7776 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xbb, 0x01, 0x13, 0x02, 0xbc, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7776 - 7792 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7792 - 7808 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7808 - 7824 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7824 - 7840 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7840 - 7856 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7856 - 7872 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7872 - 7888 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb8, 0x01, 0x13, 0x02, 0xbb, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 7888 - 7904 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 7904 - 7920 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 7920 - 7936 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 7936 - 7952 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 7952 - 7968 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7968 - 7984 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7984 - 8000 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb8, 0x01, 0xb5, 0x01, 0xb6, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8000 - 8016 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8016 - 8032 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8032 - 8048 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8048 - 8064 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8064 - 8080 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8080 - 8096 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8096 - 8112 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb3, 0x01, 0x13, 0x02, 0xb6, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8112 - 8128 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8128 - 8144 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8144 - 8160 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8160 - 8176 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8176 - 8192 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8192 - 8208 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8208 - 8224 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb1, 0x01, 0x13, 0x02, 0xb3, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8224 - 8240 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8240 - 8256 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8256 - 8272 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8272 - 8288 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8288 - 8304 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8304 - 8320 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8320 - 8336 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb1, 0x01, 0xb5, 0x01, 0xb0, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8336 - 8352 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8352 - 8368 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8368 - 8384 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8384 - 8400 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8400 - 8416 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8416 - 8432 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8432 - 8448 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb0, 0x01, 0xb5, 0x01, 0xae, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8448 - 8464 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8464 - 8480 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8480 - 8496 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8496 - 8512 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8512 - 8528 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8528 - 8544 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8544 - 8560 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xae, 0x01, 0x13, 0x02, 0xae, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8560 - 8576 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8576 - 8592 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8592 - 8608 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8608 - 8624 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8624 - 8640 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8640 - 8656 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8656 - 8672 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xae, 0x01, 0xb5, 0x01, 0xae, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8672 - 8688 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8688 - 8704 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8704 - 8720 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8720 - 8736 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8736 - 8752 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8752 - 8768 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8768 - 8784 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xae, 0x01, 0xb5, 0x01, 0xae, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8784 - 8800 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8800 - 8816 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8816 - 8832 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8832 - 8848 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8848 - 8864 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8864 - 8880 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8880 - 8896 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xaf, 0x01, 0x13, 0x02, 0xae, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 8896 - 8912 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 8912 - 8928 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 8928 - 8944 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 8944 - 8960 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 8960 - 8976 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8976 - 8992 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8992 - 9008 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb0, 0x01, 0x13, 0x02, 0xaf, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9008 - 9024 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9024 - 9040 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9040 - 9056 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9056 - 9072 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9072 - 9088 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9088 - 9104 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9104 - 9120 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb0, 0x01, 0xb5, 0x01, 0xb2, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9120 - 9136 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9136 - 9152 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9152 - 9168 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9168 - 9184 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9184 - 9200 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9200 - 9216 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9216 - 9232 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb2, 0x01, 0xb5, 0x01, 0xb3, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9232 - 9248 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9248 - 9264 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9264 - 9280 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9280 - 9296 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9296 - 9312 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9312 - 9328 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9328 - 9344 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb3, 0x01, 0xb5, 0x01, 0xb5, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9344 - 9360 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9360 - 9376 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9376 - 9392 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9392 - 9408 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9408 - 9424 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9424 - 9440 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9440 - 9456 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb5, 0x01, 0xb5, 0x01, 0xb6, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9456 - 9472 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9472 - 9488 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9488 - 9504 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9504 - 9520 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9520 - 9536 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9536 - 9552 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9552 - 9568 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb7, 0x01, 0x13, 0x02, 0xb6, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9568 - 9584 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9584 - 9600 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9600 - 9616 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9616 - 9632 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9632 - 9648 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9648 - 9664 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9664 - 9680 */ + 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb8, 0x01, 0x13, 0x02, 0xb7, 0x01, 0x23, 0x00, 0x04, 0x00, /* bytes 9680 - 9696 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9696 - 9712 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9712 - 9728 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9728 - 9744 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9744 - 9760 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9760 - 9776 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9776 - 9792 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb8, 0x01, 0xb5, 0x01, 0xb8, 0x01, 0x23, 0x00, 0x05, 0x00, /* bytes 9792 - 9808 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 9808 - 9824 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 9824 - 9840 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 9840 - 9856 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 9856 - 9872 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9872 - 9888 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9888 - 9904 */ + 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xec, 0xff, 0xe4, 0x01, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 9904 - 9920 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb8, 0x01, 0x13, 0x02, 0xb8, 0x01, 0x23, 0x00, 0x06, /* bytes 9920 - 9936 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, /* bytes 9936 - 9952 */ + 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, /* bytes 9952 - 9968 */ + 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, /* bytes 9968 - 9984 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, /* bytes 9984 - 10000 */ + 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 10000 - 10016 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 10016 - 10032 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb8, 0x01, 0xb5, 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, /* bytes 10032 - 10048 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x6c, 0x00, 0xe4, 0x01, 0x4c, 0x00, 0x03, 0x00, /* bytes 10048 - 10064 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x02, 0x00, 0xe4, 0x01, 0x22, 0x00, 0x23, /* bytes 10064 - 10080 */ + 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, /* bytes 10080 - 10096 */ + 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, /* bytes 10096 - 10112 */ + 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, /* bytes 10112 - 10128 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, /* bytes 10128 - 10144 */ + 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, /* bytes 10144 - 10160 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, /* bytes 10160 - 10176 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xb7, 0x01, 0x13, 0x02, 0xb8, 0x01, 0x03, /* bytes 10176 - 10192 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x08, 0x00, 0xe4, 0x01, 0x28, 0x00, /* bytes 10192 - 10208 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xac, 0x00, 0xe4, 0x01, 0xcc, /* bytes 10208 - 10224 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x82, 0x00, 0xe4, 0x01, /* bytes 10224 - 10240 */ + 0x62, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, /* bytes 10240 - 10256 */ + 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, /* bytes 10256 - 10272 */ + 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, /* bytes 10272 - 10288 */ + 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, /* bytes 10288 - 10304 */ + 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, /* bytes 10304 - 10320 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, /* bytes 10320 - 10336 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x0b, 0x00, 0xe4, 0x01, /* bytes 10336 - 10352 */ + 0x2b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xb7, 0x01, 0xb5, /* bytes 10352 - 10368 */ + 0x01, 0xb6, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x88, 0x00, /* bytes 10368 - 10384 */ + 0xe4, 0x01, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xc2, /* bytes 10384 - 10400 */ + 0x00, 0xe4, 0x01, 0xe2, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 10400 - 10416 */ + 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, /* bytes 10416 - 10432 */ + 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, /* bytes 10432 - 10448 */ + 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, /* bytes 10448 - 10464 */ + 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, /* bytes 10464 - 10480 */ + 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, /* bytes 10480 - 10496 */ + 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x2c, /* bytes 10496 - 10512 */ + 0x00, 0xe4, 0x01, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, /* bytes 10512 - 10528 */ + 0xc8, 0x00, 0xe4, 0x01, 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, /* bytes 10528 - 10544 */ + 0x01, 0x6b, 0x00, 0xe4, 0x01, 0x8b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10544 - 10560 */ + 0x13, 0x02, 0xb6, 0x01, 0xb5, 0x01, 0xb5, 0x01, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 10560 - 10576 */ + 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, /* bytes 10576 - 10592 */ + 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, /* bytes 10592 - 10608 */ + 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 10608 - 10624 */ + 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, /* bytes 10624 - 10640 */ + 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 10640 - 10656 */ + 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10656 - 10672 */ + 0xe4, 0x01, 0x30, 0x00, 0xe4, 0x01, 0x10, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 10672 - 10688 */ + 0x00, 0xe4, 0x01, 0xf0, 0x00, 0xe4, 0x01, 0xd0, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 10688 - 10704 */ + 0x02, 0x00, 0xb5, 0x01, 0xb4, 0x01, 0x13, 0x02, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10704 - 10720 */ + 0x00, 0x02, 0x00, 0xe4, 0x01, 0x90, 0x00, 0xe4, 0x01, 0x70, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, /* bytes 10720 - 10736 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, /* bytes 10736 - 10752 */ + 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, /* bytes 10752 - 10768 */ + 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 10768 - 10784 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, /* bytes 10784 - 10800 */ + 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 10800 - 10816 */ + 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10816 - 10832 */ + 0x00, 0x02, 0x00, 0xe4, 0x01, 0x34, 0x00, 0xe4, 0x01, 0x54, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 10832 - 10848 */ + 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0x96, 0x01, 0xb5, 0x01, 0xb4, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 10848 - 10864 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xb4, 0x00, 0xe4, 0x01, 0x94, 0x00, 0x03, 0x00, 0xc0, /* bytes 10864 - 10880 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xf4, 0x00, 0xe4, 0x01, 0x14, 0x01, 0x23, 0x00, /* bytes 10880 - 10896 */ + 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 10896 - 10912 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 10912 - 10928 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 10928 - 10944 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 10944 - 10960 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 10960 - 10976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 10976 - 10992 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0x9f, 0x01, 0x13, 0x02, 0x71, 0x01, 0x03, 0x00, /* bytes 10992 - 11008 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xba, 0x00, 0xe4, 0x01, 0x9a, 0x00, 0x23, /* bytes 11008 - 11024 */ + 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, /* bytes 11024 - 11040 */ + 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, /* bytes 11040 - 11056 */ + 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, /* bytes 11056 - 11072 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, /* bytes 11072 - 11088 */ + 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, /* bytes 11088 - 11104 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, /* bytes 11104 - 11120 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x02, 0x64, 0x01, 0xb4, 0x01, 0x79, 0x01, 0x03, /* bytes 11120 - 11136 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x12, 0x01, 0xe4, 0x01, 0xf2, 0x00, /* bytes 11136 - 11152 */ + 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, /* bytes 11152 - 11168 */ + 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, /* bytes 11168 - 11184 */ + 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, /* bytes 11184 - 11200 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, /* bytes 11200 - 11216 */ + 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, /* bytes 11216 - 11232 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, /* bytes 11232 - 11248 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x12, 0x01, 0xe4, 0x01, 0x1a, 0x01, /* bytes 11248 - 11264 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x15, 0x02, 0x67, 0x01, 0xb3, 0x01, 0x64, /* bytes 11264 - 11280 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11280 - 11296 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11296 - 11312 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11312 - 11328 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 11328 - 11344 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 11344 - 11360 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 11360 - 11376 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb2, 0x01, 0x62, 0x01, 0x16, 0x02, 0x6b, /* bytes 11376 - 11392 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11392 - 11408 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11408 - 11424 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11424 - 11440 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 11440 - 11456 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 11456 - 11472 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 11472 - 11488 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x17, 0x02, 0x6c, 0x01, 0xb1, 0x01, 0x63, /* bytes 11488 - 11504 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11504 - 11520 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11520 - 11536 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11536 - 11552 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 11552 - 11568 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 11568 - 11584 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 11584 - 11600 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x65, 0x01, 0x18, 0x02, 0x6d, /* bytes 11600 - 11616 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11616 - 11632 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11632 - 11648 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11648 - 11664 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 11664 - 11680 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 11680 - 11696 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 11696 - 11712 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x74, 0x01, 0x18, 0x02, 0x6a, /* bytes 11712 - 11728 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11728 - 11744 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11744 - 11760 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11760 - 11776 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 11776 - 11792 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 11792 - 11808 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 11808 - 11824 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x74, 0x01, 0xb0, 0x01, 0x78, /* bytes 11824 - 11840 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11840 - 11856 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11856 - 11872 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11872 - 11888 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 11888 - 11904 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 11904 - 11920 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 11920 - 11936 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x78, 0x01, 0xb0, 0x01, 0x7a, /* bytes 11936 - 11952 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 11952 - 11968 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 11968 - 11984 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 11984 - 12000 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12000 - 12016 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12016 - 12032 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12032 - 12048 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x7a, 0x01, 0xb0, 0x01, 0x7d, /* bytes 12048 - 12064 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12064 - 12080 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12080 - 12096 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12096 - 12112 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12112 - 12128 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12128 - 12144 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12144 - 12160 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x7e, 0x01, 0x18, 0x02, 0x7d, /* bytes 12160 - 12176 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12176 - 12192 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12192 - 12208 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12208 - 12224 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12224 - 12240 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12240 - 12256 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12256 - 12272 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x7e, 0x01, 0x18, 0x02, 0x7e, /* bytes 12272 - 12288 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12288 - 12304 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12304 - 12320 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12320 - 12336 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12336 - 12352 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12352 - 12368 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12368 - 12384 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x7e, 0x01, 0x18, 0x02, 0x7e, /* bytes 12384 - 12400 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12400 - 12416 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12416 - 12432 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12432 - 12448 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12448 - 12464 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12464 - 12480 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12480 - 12496 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x7c, 0x01, 0x18, 0x02, 0x7e, /* bytes 12496 - 12512 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12512 - 12528 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12528 - 12544 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12544 - 12560 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12560 - 12576 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12576 - 12592 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12592 - 12608 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x7b, 0x01, 0x18, 0x02, 0x7c, /* bytes 12608 - 12624 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12624 - 12640 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12640 - 12656 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12656 - 12672 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12672 - 12688 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12688 - 12704 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12704 - 12720 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x78, 0x01, 0x18, 0x02, 0x7b, /* bytes 12720 - 12736 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12736 - 12752 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12752 - 12768 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12768 - 12784 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12784 - 12800 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12800 - 12816 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12816 - 12832 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x76, 0x01, 0x18, 0x02, 0x78, /* bytes 12832 - 12848 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12848 - 12864 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12864 - 12880 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12880 - 12896 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 12896 - 12912 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 12912 - 12928 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 12928 - 12944 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x73, 0x01, 0x18, 0x02, 0x76, /* bytes 12944 - 12960 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 12960 - 12976 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 12976 - 12992 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 12992 - 13008 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13008 - 13024 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13024 - 13040 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13040 - 13056 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x73, 0x01, 0xb0, 0x01, 0x71, /* bytes 13056 - 13072 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13072 - 13088 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13088 - 13104 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13104 - 13120 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13120 - 13136 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13136 - 13152 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13152 - 13168 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x71, 0x01, 0xb0, 0x01, 0x70, /* bytes 13168 - 13184 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13184 - 13200 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13200 - 13216 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13216 - 13232 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13232 - 13248 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13248 - 13264 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13264 - 13280 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x6e, 0x01, 0x18, 0x02, 0x70, /* bytes 13280 - 13296 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13296 - 13312 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13312 - 13328 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13328 - 13344 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13344 - 13360 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13360 - 13376 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13376 - 13392 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x6e, 0x01, 0xb0, 0x01, 0x6e, /* bytes 13392 - 13408 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13408 - 13424 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13424 - 13440 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13440 - 13456 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13456 - 13472 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13472 - 13488 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13488 - 13504 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x6e, 0x01, 0x18, 0x02, 0x6e, /* bytes 13504 - 13520 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13520 - 13536 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13536 - 13552 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13552 - 13568 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13568 - 13584 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13584 - 13600 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13600 - 13616 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x6e, 0x01, 0x18, 0x02, 0x6e, /* bytes 13616 - 13632 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13632 - 13648 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13648 - 13664 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13664 - 13680 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13680 - 13696 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13696 - 13712 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13712 - 13728 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x6f, 0x01, 0x18, 0x02, 0x6e, /* bytes 13728 - 13744 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13744 - 13760 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13760 - 13776 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13776 - 13792 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13792 - 13808 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13808 - 13824 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13824 - 13840 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x6f, 0x01, 0xb0, 0x01, 0x70, /* bytes 13840 - 13856 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13856 - 13872 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13872 - 13888 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 13888 - 13904 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 13904 - 13920 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 13920 - 13936 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 13936 - 13952 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x70, 0x01, 0xb0, 0x01, 0x72, /* bytes 13952 - 13968 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 13968 - 13984 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 13984 - 14000 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14000 - 14016 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14016 - 14032 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14032 - 14048 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14048 - 14064 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x73, 0x01, 0x18, 0x02, 0x72, /* bytes 14064 - 14080 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 14080 - 14096 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 14096 - 14112 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14112 - 14128 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14128 - 14144 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14144 - 14160 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14160 - 14176 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x73, 0x01, 0xb0, 0x01, 0x75, /* bytes 14176 - 14192 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 14192 - 14208 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 14208 - 14224 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14224 - 14240 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14240 - 14256 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14256 - 14272 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14272 - 14288 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x76, 0x01, 0x18, 0x02, 0x75, /* bytes 14288 - 14304 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 14304 - 14320 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 14320 - 14336 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14336 - 14352 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14352 - 14368 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14368 - 14384 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14384 - 14400 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x77, 0x01, 0x18, 0x02, 0x76, /* bytes 14400 - 14416 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 14416 - 14432 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 14432 - 14448 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14448 - 14464 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14464 - 14480 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14480 - 14496 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14496 - 14512 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x78, 0x01, 0x18, 0x02, 0x77, /* bytes 14512 - 14528 */ + 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 14528 - 14544 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 14544 - 14560 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14560 - 14576 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14576 - 14592 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14592 - 14608 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14608 - 14624 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x78, 0x01, 0xb0, 0x01, 0x78, /* bytes 14624 - 14640 */ + 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, /* bytes 14640 - 14656 */ + 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, /* bytes 14656 - 14672 */ + 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, /* bytes 14672 - 14688 */ + 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, /* bytes 14688 - 14704 */ + 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, /* bytes 14704 - 14720 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, /* bytes 14720 - 14736 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xec, 0xff, 0xe4, 0x01, 0x0c, /* bytes 14736 - 14752 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x78, 0x01, 0xb0, 0x01, /* bytes 14752 - 14768 */ + 0x78, 0x01, 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, /* bytes 14768 - 14784 */ + 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, /* bytes 14784 - 14800 */ + 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, /* bytes 14800 - 14816 */ + 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, /* bytes 14816 - 14832 */ + 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, /* bytes 14832 - 14848 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, /* bytes 14848 - 14864 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x78, 0x01, 0x18, 0x02, /* bytes 14864 - 14880 */ + 0x78, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x6c, 0x00, 0xe4, /* bytes 14880 - 14896 */ + 0x01, 0x4c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x02, 0x00, /* bytes 14896 - 14912 */ + 0xe4, 0x01, 0x22, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, /* bytes 14912 - 14928 */ + 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, /* bytes 14928 - 14944 */ + 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, /* bytes 14944 - 14960 */ + 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, /* bytes 14960 - 14976 */ + 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, /* bytes 14976 - 14992 */ + 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, /* bytes 14992 - 15008 */ + 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x78, 0x01, /* bytes 15008 - 15024 */ + 0xb0, 0x01, 0x77, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x08, /* bytes 15024 - 15040 */ + 0x00, 0xe4, 0x01, 0x28, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, /* bytes 15040 - 15056 */ + 0xac, 0x00, 0xe4, 0x01, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, /* bytes 15056 - 15072 */ + 0x01, 0x82, 0x00, 0xe4, 0x01, 0x62, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 15072 - 15088 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 15088 - 15104 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 15104 - 15120 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 15120 - 15136 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 15136 - 15152 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 15152 - 15168 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, /* bytes 15168 - 15184 */ + 0x01, 0x0b, 0x00, 0xe4, 0x01, 0x2b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 15184 - 15200 */ + 0xe4, 0x01, 0x88, 0x00, 0xe4, 0x01, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 15200 - 15216 */ + 0x00, 0x18, 0x02, 0x77, 0x01, 0xb0, 0x01, 0x76, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 15216 - 15232 */ + 0x02, 0x00, 0xe4, 0x01, 0xc2, 0x00, 0xe4, 0x01, 0xe2, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, /* bytes 15232 - 15248 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, /* bytes 15248 - 15264 */ + 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, /* bytes 15264 - 15280 */ + 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 15280 - 15296 */ + 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, /* bytes 15296 - 15312 */ + 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 15312 - 15328 */ + 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 15328 - 15344 */ + 0x02, 0x00, 0xe4, 0x01, 0x2c, 0x00, 0xe4, 0x01, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 15344 - 15360 */ + 0x00, 0x02, 0x00, 0xe4, 0x01, 0xc8, 0x00, 0xe4, 0x01, 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 15360 - 15376 */ + 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x75, 0x01, 0x18, 0x02, 0x76, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 15376 - 15392 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x6b, 0x00, 0xe4, 0x01, 0x8b, 0x00, 0x23, 0x00, 0x07, /* bytes 15392 - 15408 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, /* bytes 15408 - 15424 */ + 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, /* bytes 15424 - 15440 */ + 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, /* bytes 15440 - 15456 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, /* bytes 15456 - 15472 */ + 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 15472 - 15488 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 15488 - 15504 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x30, 0x00, 0xe4, 0x01, 0x10, 0x00, 0x03, 0x00, 0xc0, /* bytes 15504 - 15520 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0x75, 0x01, 0xb0, 0x01, 0x74, 0x01, 0x03, 0x00, /* bytes 15520 - 15536 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xd0, 0x00, 0xe4, 0x01, 0xf0, 0x00, 0x03, /* bytes 15536 - 15552 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x90, 0x00, 0xe4, 0x01, 0x70, 0x00, /* bytes 15552 - 15568 */ + 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, /* bytes 15568 - 15584 */ + 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, /* bytes 15584 - 15600 */ + 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, /* bytes 15600 - 15616 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, /* bytes 15616 - 15632 */ + 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, /* bytes 15632 - 15648 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, /* bytes 15648 - 15664 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x34, 0x00, 0xe4, 0x01, 0x54, 0x00, /* bytes 15664 - 15680 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x94, 0x00, 0xe4, 0x01, 0xb4, /* bytes 15680 - 15696 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xf4, 0x00, 0xe4, 0x01, /* bytes 15696 - 15712 */ + 0x14, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x74, 0x01, 0x18, /* bytes 15712 - 15728 */ + 0x02, 0x65, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, /* bytes 15728 - 15744 */ + 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, /* bytes 15744 - 15760 */ + 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, /* bytes 15760 - 15776 */ + 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, /* bytes 15776 - 15792 */ + 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, /* bytes 15792 - 15808 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, /* bytes 15808 - 15824 */ + 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x6a, 0x01, 0x18, /* bytes 15824 - 15840 */ + 0x02, 0x52, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0xba, 0x00, /* bytes 15840 - 15856 */ + 0xe4, 0x01, 0x9a, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, /* bytes 15856 - 15872 */ + 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, /* bytes 15872 - 15888 */ + 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, /* bytes 15888 - 15904 */ + 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, /* bytes 15904 - 15920 */ + 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, /* bytes 15920 - 15936 */ + 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, /* bytes 15936 - 15952 */ + 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x12, 0x01, /* bytes 15952 - 15968 */ + 0xe4, 0x01, 0xf2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xaf, 0x01, 0x56, /* bytes 15968 - 15984 */ + 0x01, 0x19, 0x02, 0x4c, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 15984 - 16000 */ + 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, /* bytes 16000 - 16016 */ + 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, /* bytes 16016 - 16032 */ + 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, /* bytes 16032 - 16048 */ + 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, /* bytes 16048 - 16064 */ + 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, /* bytes 16064 - 16080 */ + 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0x01, 0x12, /* bytes 16080 - 16096 */ + 0x01, 0xe4, 0x01, 0x1a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xae, 0x01, /* bytes 16096 - 16112 */ + 0x4c, 0x01, 0x1a, 0x02, 0x4f, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16112 - 16128 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16128 - 16144 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16144 - 16160 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16160 - 16176 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16176 - 16192 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16192 - 16208 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xad, 0x01, /* bytes 16208 - 16224 */ + 0x4c, 0x01, 0x1b, 0x02, 0x53, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16224 - 16240 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16240 - 16256 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16256 - 16272 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16272 - 16288 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16288 - 16304 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16304 - 16320 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x02, /* bytes 16320 - 16336 */ + 0x56, 0x01, 0xac, 0x01, 0x4e, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16336 - 16352 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16352 - 16368 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16368 - 16384 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16384 - 16400 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16400 - 16416 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16416 - 16432 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, /* bytes 16432 - 16448 */ + 0x52, 0x01, 0x1d, 0x02, 0x58, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16448 - 16464 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16464 - 16480 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16480 - 16496 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16496 - 16512 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16512 - 16528 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16528 - 16544 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, /* bytes 16544 - 16560 */ + 0x57, 0x01, 0xab, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16560 - 16576 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16576 - 16592 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16592 - 16608 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16608 - 16624 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16624 - 16640 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16640 - 16656 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, /* bytes 16656 - 16672 */ + 0x60, 0x01, 0x1d, 0x02, 0x5c, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16672 - 16688 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16688 - 16704 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16704 - 16720 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16720 - 16736 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16736 - 16752 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16752 - 16768 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, /* bytes 16768 - 16784 */ + 0x60, 0x01, 0xab, 0x01, 0x62, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16784 - 16800 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16800 - 16816 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16816 - 16832 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16832 - 16848 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16848 - 16864 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16864 - 16880 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, /* bytes 16880 - 16896 */ + 0x62, 0x01, 0xab, 0x01, 0x65, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 16896 - 16912 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 16912 - 16928 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 16928 - 16944 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 16944 - 16960 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 16960 - 16976 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 16976 - 16992 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, /* bytes 16992 - 17008 */ + 0x65, 0x01, 0xab, 0x01, 0x66, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 17008 - 17024 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 17024 - 17040 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 17040 - 17056 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 17056 - 17072 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 17072 - 17088 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 17088 - 17104 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, /* bytes 17104 - 17120 */ + 0x66, 0x01, 0xab, 0x01, 0x66, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 17120 - 17136 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 17136 - 17152 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 17152 - 17168 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 17168 - 17184 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 17184 - 17200 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x01, 0xf8, /* bytes 17200 - 17216 */ + 0x00, 0xf0, 0x01, 0x10, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 17216 - 17232 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, /* bytes 17232 - 17248 */ + 0x02, 0x66, 0x01, 0xab, 0x01, 0x66, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 17248 - 17264 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 17264 - 17280 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 17280 - 17296 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 17296 - 17312 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 17312 - 17328 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0x01, /* bytes 17328 - 17344 */ + 0xd2, 0x00, 0xeb, 0x01, 0xfe, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 17344 - 17360 */ + 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 17360 - 17376 */ + 0x1d, 0x02, 0x66, 0x01, 0xab, 0x01, 0x64, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 17376 - 17392 */ + 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, /* bytes 17392 - 17408 */ + 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, /* bytes 17408 - 17424 */ + 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 17424 - 17440 */ + 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, /* bytes 17440 - 17456 */ + 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf6, /* bytes 17456 - 17472 */ + 0x01, 0xbe, 0x00, 0xfc, 0x01, 0xc6, 0x00, 0xe9, 0x01, 0xf1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 17472 - 17488 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 17488 - 17504 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, 0x63, 0x01, 0x1d, 0x02, 0x64, 0x01, 0x23, 0x00, 0x05, /* bytes 17504 - 17520 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, /* bytes 17520 - 17536 */ + 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, /* bytes 17536 - 17552 */ + 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, /* bytes 17552 - 17568 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, /* bytes 17568 - 17584 */ + 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 17584 - 17600 */ + 0x01, 0x00, 0x03, 0x00, 0xe6, 0x01, 0xe6, 0x00, 0xfa, 0x01, 0xba, 0x00, 0xe9, 0x01, 0xaa, 0x00, /* bytes 17600 - 17616 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, /* bytes 17616 - 17632 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x63, 0x01, 0xab, 0x01, /* bytes 17632 - 17648 */ + 0x60, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, /* bytes 17648 - 17664 */ + 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, /* bytes 17664 - 17680 */ + 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, /* bytes 17680 - 17696 */ + 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, /* bytes 17696 - 17712 */ + 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, /* bytes 17712 - 17728 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xe4, 0x01, 0xdb, 0x00, 0xf8, 0x01, 0xaf, /* bytes 17728 - 17744 */ + 0x00, 0xd8, 0x01, 0x97, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, /* bytes 17744 - 17760 */ + 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, /* bytes 17760 - 17776 */ + 0x02, 0x60, 0x01, 0xab, 0x01, 0x5e, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 17776 - 17792 */ + 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, /* bytes 17792 - 17808 */ + 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, /* bytes 17808 - 17824 */ + 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, /* bytes 17824 - 17840 */ + 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, /* bytes 17840 - 17856 */ + 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xe3, 0x01, /* bytes 17856 - 17872 */ + 0xd5, 0x00, 0xf7, 0x01, 0xa9, 0x00, 0xd7, 0x01, 0x91, 0x00, 0xde, 0x01, 0x80, 0x00, 0x03, 0x00, /* bytes 17872 - 17888 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, /* bytes 17888 - 17904 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, 0x5b, 0x01, 0x1d, 0x02, 0x5e, 0x01, /* bytes 17904 - 17920 */ + 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, /* bytes 17920 - 17936 */ + 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, /* bytes 17936 - 17952 */ + 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, /* bytes 17952 - 17968 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, /* bytes 17968 - 17984 */ + 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, /* bytes 17984 - 18000 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xe2, 0x01, 0xce, 0x00, 0xf6, 0x01, 0xa3, 0x00, 0xd6, /* bytes 18000 - 18016 */ + 0x01, 0x8a, 0x00, 0xe4, 0x01, 0x6c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 18016 - 18032 */ + 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 18032 - 18048 */ + 0x00, 0xab, 0x01, 0x59, 0x01, 0x1d, 0x02, 0x5b, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, /* bytes 18048 - 18064 */ + 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, /* bytes 18064 - 18080 */ + 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, /* bytes 18080 - 18096 */ + 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 18096 - 18112 */ + 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, /* bytes 18112 - 18128 */ + 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 18128 - 18144 */ + 0xe1, 0x01, 0xc7, 0x00, 0xf4, 0x01, 0x9b, 0x00, 0xd5, 0x01, 0x83, 0x00, 0xec, 0x01, 0x5b, 0x00, /* bytes 18144 - 18160 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, /* bytes 18160 - 18176 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x59, 0x01, 0xab, 0x01, /* bytes 18176 - 18192 */ + 0x58, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, /* bytes 18192 - 18208 */ + 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, /* bytes 18208 - 18224 */ + 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, /* bytes 18224 - 18240 */ + 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, /* bytes 18240 - 18256 */ + 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, /* bytes 18256 - 18272 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xee, 0x01, 0x4e, 0x00, 0xd4, 0x01, 0x7d, /* bytes 18272 - 18288 */ + 0x00, 0xf4, 0x01, 0x96, 0x00, 0xe1, 0x01, 0xbf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 18288 - 18304 */ + 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 18304 - 18320 */ + 0x00, 0x02, 0x00, 0xab, 0x01, 0x56, 0x01, 0x1d, 0x02, 0x58, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, /* bytes 18320 - 18336 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, /* bytes 18336 - 18352 */ + 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, /* bytes 18352 - 18368 */ + 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 18368 - 18384 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, /* bytes 18384 - 18400 */ + 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 18400 - 18416 */ + 0x04, 0x00, 0xe3, 0x01, 0xb5, 0x00, 0xf3, 0x01, 0x92, 0x00, 0xd4, 0x01, 0x79, 0x00, 0xf0, 0x01, /* bytes 18416 - 18432 */ + 0x45, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, /* bytes 18432 - 18448 */ + 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x56, 0x01, /* bytes 18448 - 18464 */ + 0xab, 0x01, 0x56, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, /* bytes 18464 - 18480 */ + 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, /* bytes 18480 - 18496 */ + 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, /* bytes 18496 - 18512 */ + 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, /* bytes 18512 - 18528 */ + 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, /* bytes 18528 - 18544 */ + 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xe6, 0x01, 0xaa, 0x00, 0xf3, /* bytes 18544 - 18560 */ + 0x01, 0x8e, 0x00, 0xd3, 0x01, 0x76, 0x00, 0xf1, 0x01, 0x3e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 18560 - 18576 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 18576 - 18592 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x56, 0x01, 0xab, 0x01, 0x56, 0x01, 0x23, 0x00, 0x05, /* bytes 18592 - 18608 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, /* bytes 18608 - 18624 */ + 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, /* bytes 18624 - 18640 */ + 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, /* bytes 18640 - 18656 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, /* bytes 18656 - 18672 */ + 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 18672 - 18688 */ + 0x01, 0x00, 0x04, 0x00, 0xec, 0x01, 0x9c, 0x00, 0xf3, 0x01, 0x8b, 0x00, 0xd3, 0x01, 0x72, 0x00, /* bytes 18688 - 18704 */ + 0xf0, 0x01, 0x3b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, /* bytes 18704 - 18720 */ + 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, /* bytes 18720 - 18736 */ + 0x56, 0x01, 0x1d, 0x02, 0x56, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 18736 - 18752 */ + 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, /* bytes 18752 - 18768 */ + 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, /* bytes 18768 - 18784 */ + 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 18784 - 18800 */ + 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, /* bytes 18800 - 18816 */ + 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xf0, 0x01, 0x39, /* bytes 18816 - 18832 */ + 0x00, 0xd3, 0x01, 0x6f, 0x00, 0xef, 0x01, 0x86, 0x00, 0xed, 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, /* bytes 18832 - 18848 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, /* bytes 18848 - 18864 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x56, 0x01, 0xab, 0x01, 0x57, 0x01, 0x23, /* bytes 18864 - 18880 */ + 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, /* bytes 18880 - 18896 */ + 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, /* bytes 18896 - 18912 */ + 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, /* bytes 18912 - 18928 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, /* bytes 18928 - 18944 */ + 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, /* bytes 18944 - 18960 */ + 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xe9, 0x01, 0x7f, 0x00, 0xd3, 0x01, 0x6b, 0x00, 0xf0, 0x01, /* bytes 18960 - 18976 */ + 0x37, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, /* bytes 18976 - 18992 */ + 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, 0x58, 0x01, /* bytes 18992 - 19008 */ + 0x1d, 0x02, 0x57, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, /* bytes 19008 - 19024 */ + 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, /* bytes 19024 - 19040 */ + 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, /* bytes 19040 - 19056 */ + 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, /* bytes 19056 - 19072 */ + 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, /* bytes 19072 - 19088 */ + 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf1, 0x01, 0x35, 0x00, 0xd3, /* bytes 19088 - 19104 */ + 0x01, 0x69, 0x00, 0xda, 0x01, 0x70, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 19104 - 19120 */ + 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 19120 - 19136 */ + 0x00, 0x1d, 0x02, 0x58, 0x01, 0xab, 0x01, 0x5a, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, /* bytes 19136 - 19152 */ + 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, 0x00, /* bytes 19152 - 19168 */ + 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, 0x01, /* bytes 19168 - 19184 */ + 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 19184 - 19200 */ + 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, 0x4c, /* bytes 19200 - 19216 */ + 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 19216 - 19232 */ + 0xf1, 0x01, 0x32, 0x00, 0xd7, 0x01, 0x60, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 19232 - 19248 */ + 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 19248 - 19264 */ + 0x02, 0x00, 0xab, 0x01, 0x5b, 0x01, 0x1d, 0x02, 0x5a, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, /* bytes 19264 - 19280 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, 0x0c, /* bytes 19280 - 19296 */ + 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, 0x0c, /* bytes 19296 - 19312 */ + 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 19312 - 19328 */ + 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, 0x01, /* bytes 19328 - 19344 */ + 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 19344 - 19360 */ + 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 19360 - 19376 */ + 0x02, 0x00, 0xf1, 0x01, 0x30, 0x00, 0xdf, 0x01, 0x50, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 19376 - 19392 */ + 0x00, 0x02, 0x00, 0x1d, 0x02, 0x5b, 0x01, 0xab, 0x01, 0x5d, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, /* bytes 19392 - 19408 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, 0x02, /* bytes 19408 - 19424 */ + 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, 0x01, /* bytes 19424 - 19440 */ + 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 19440 - 19456 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, 0x1c, /* bytes 19456 - 19472 */ + 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 19472 - 19488 */ + 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 19488 - 19504 */ + 0x00, 0x02, 0x00, 0xf1, 0x01, 0x2e, 0x00, 0xe6, 0x01, 0x43, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 19504 - 19520 */ + 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x5d, 0x01, 0xab, 0x01, 0x5e, 0x01, 0x23, 0x00, 0x05, 0x00, /* bytes 19520 - 19536 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, 0x6c, /* bytes 19536 - 19552 */ + 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, 0xf4, /* bytes 19552 - 19568 */ + 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, /* bytes 19568 - 19584 */ + 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, 0x02, /* bytes 19584 - 19600 */ + 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 19600 - 19616 */ + 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 19616 - 19632 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x38, 0x00, 0xf1, 0x01, 0x2d, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 19632 - 19648 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x5e, 0x01, 0xab, 0x01, 0x5f, 0x01, 0x23, 0x00, 0x05, /* bytes 19648 - 19664 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, 0x01, /* bytes 19664 - 19680 */ + 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, 0x00, /* bytes 19680 - 19696 */ + 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, 0x00, /* bytes 19696 - 19712 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, 0x64, /* bytes 19712 - 19728 */ + 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 19728 - 19744 */ + 0x01, 0x00, 0x02, 0x00, 0xf2, 0x01, 0x2c, 0x00, 0xf1, 0x01, 0x2f, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 19744 - 19760 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 19760 - 19776 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, 0x5c, 0x01, 0x1d, 0x02, 0x5f, 0x01, 0x23, 0x00, /* bytes 19776 - 19792 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 19792 - 19808 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 19808 - 19824 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 19824 - 19840 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 19840 - 19856 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 19856 - 19872 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 19872 - 19888 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, 0x5e, 0x01, 0x1d, 0x02, 0x63, 0x01, 0x23, 0x00, /* bytes 19888 - 19904 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 19904 - 19920 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 19920 - 19936 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 19936 - 19952 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 19952 - 19968 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 19968 - 19984 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 19984 - 20000 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x02, 0x68, 0x01, 0xab, 0x01, 0x65, 0x01, 0x23, 0x00, /* bytes 20000 - 20016 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20016 - 20032 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20032 - 20048 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20048 - 20064 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20064 - 20080 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20080 - 20096 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20096 - 20112 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, 0x84, 0x01, 0x1d, 0x02, 0x84, 0x01, 0x23, 0x00, /* bytes 20112 - 20128 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20128 - 20144 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20144 - 20160 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20160 - 20176 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20176 - 20192 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20192 - 20208 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20208 - 20224 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4b, 0x02, 0xbc, 0x01, 0x4c, 0x02, 0x23, 0x00, /* bytes 20224 - 20240 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20240 - 20256 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20256 - 20272 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20272 - 20288 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20288 - 20304 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20304 - 20320 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20320 - 20336 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x46, 0x02, 0xbc, 0x01, 0x47, 0x02, 0x23, 0x00, /* bytes 20336 - 20352 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20352 - 20368 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20368 - 20384 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20384 - 20400 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20400 - 20416 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20416 - 20432 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20432 - 20448 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0x43, 0x02, 0x0c, 0x02, 0x42, 0x02, 0x23, 0x00, /* bytes 20448 - 20464 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20464 - 20480 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20480 - 20496 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20496 - 20512 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20512 - 20528 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20528 - 20544 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20544 - 20560 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x3e, 0x02, 0xbc, 0x01, 0x3f, 0x02, 0x23, 0x00, /* bytes 20560 - 20576 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20576 - 20592 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20592 - 20608 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20608 - 20624 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20624 - 20640 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20640 - 20656 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20656 - 20672 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x39, 0x02, 0xbc, 0x01, 0x3a, 0x02, 0x23, 0x00, /* bytes 20672 - 20688 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20688 - 20704 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20704 - 20720 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20720 - 20736 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20736 - 20752 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20752 - 20768 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20768 - 20784 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0x37, 0x02, 0x0c, 0x02, 0x36, 0x02, 0x23, 0x00, /* bytes 20784 - 20800 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20800 - 20816 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20816 - 20832 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20832 - 20848 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20848 - 20864 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20864 - 20880 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20880 - 20896 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, 0x01, 0x34, 0x02, 0x23, 0x00, /* bytes 20896 - 20912 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 20912 - 20928 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 20928 - 20944 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 20944 - 20960 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 20960 - 20976 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 20976 - 20992 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 20992 - 21008 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, 0x01, 0x33, 0x02, 0x23, 0x00, /* bytes 21008 - 21024 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 21024 - 21040 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 21040 - 21056 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 21056 - 21072 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 21072 - 21088 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 21088 - 21104 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 21104 - 21120 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x34, 0x02, 0xbc, 0x01, 0x32, 0x02, 0x23, 0x00, /* bytes 21120 - 21136 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 21136 - 21152 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 21152 - 21168 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 21168 - 21184 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 21184 - 21200 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 21200 - 21216 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 21216 - 21232 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0x32, 0x02, 0x0c, 0x02, 0x33, 0x02, 0x23, 0x00, /* bytes 21232 - 21248 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x09, 0x00, 0x2c, 0x01, 0x44, 0x02, 0x2c, /* bytes 21248 - 21264 */ + 0x01, 0x6c, 0x02, 0x0c, 0x00, 0x6c, 0x02, 0x0c, 0x00, 0x44, 0x02, 0x2c, 0x00, 0x24, 0x02, 0x2c, /* bytes 21264 - 21280 */ + 0x00, 0xf4, 0x01, 0x0c, 0x01, 0xf4, 0x01, 0x0c, 0x01, 0x24, 0x02, 0x2c, 0x01, 0x44, 0x02, 0x03, /* bytes 21280 - 21296 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x01, 0x6c, 0x02, 0x64, 0x01, 0x1c, 0x01, /* bytes 21296 - 21312 */ + 0x64, 0x02, 0x1c, 0x01, 0x4c, 0x02, 0x6c, 0x02, 0x7c, 0x01, 0x6c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 21312 - 21328 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x00, 0x20, 0x02, 0x5e, 0x00, 0x20, 0x02, 0x03, 0x00, 0xc0, /* bytes 21328 - 21344 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0x31, 0x02, 0x0c, 0x02, 0x32, 0x02, +}; +__attribute__ ((aligned (8))) +static const uint8_t RESULT_DELETED_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0xb7, 0x14, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x21, 0x00, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, /* bytes 16 - 32 */ + 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, /* bytes 32 - 48 */ + 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, /* bytes 48 - 64 */ + 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, /* bytes 64 - 80 */ + 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 80 - 96 */ + 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 96 - 112 */ + 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 112 - 128 */ + 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 128 - 144 */ + 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 144 - 160 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 160 - 176 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0x03, 0x00, 0xc0, /* bytes 176 - 192 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, /* bytes 192 - 208 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, 0x23, /* bytes 208 - 224 */ + 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, /* bytes 224 - 240 */ + 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, /* bytes 240 - 256 */ + 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, /* bytes 256 - 272 */ + 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, /* bytes 272 - 288 */ + 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, /* bytes 288 - 304 */ + 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, /* bytes 304 - 320 */ + 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, /* bytes 320 - 336 */ + 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 336 - 352 */ + 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 352 - 368 */ + 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 368 - 384 */ + 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 384 - 400 */ + 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 400 - 416 */ + 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, 0x23, 0x00, 0x09, 0x00, /* bytes 416 - 432 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, /* bytes 432 - 448 */ + 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, /* bytes 448 - 464 */ + 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, /* bytes 464 - 480 */ + 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, /* bytes 480 - 496 */ + 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, /* bytes 496 - 512 */ + 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, /* bytes 512 - 528 */ + 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, /* bytes 528 - 544 */ + 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, /* bytes 544 - 560 */ + 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, /* bytes 560 - 576 */ + 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, /* bytes 576 - 592 */ + 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 592 - 608 */ + 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 608 - 624 */ + 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, /* bytes 624 - 640 */ + 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, /* bytes 640 - 656 */ + 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, /* bytes 656 - 672 */ + 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, /* bytes 672 - 688 */ + 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, /* bytes 688 - 704 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, /* bytes 704 - 720 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, /* bytes 720 - 736 */ + 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, /* bytes 736 - 752 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, /* bytes 752 - 768 */ + 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, /* bytes 768 - 784 */ + 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, /* bytes 784 - 800 */ + 0xbc, 0x00, 0x3c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, /* bytes 800 - 816 */ + 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, /* bytes 816 - 832 */ + 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 832 - 848 */ + 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, /* bytes 848 - 864 */ + 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, /* bytes 864 - 880 */ + 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, /* bytes 880 - 896 */ + 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 896 - 912 */ + 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, /* bytes 912 - 928 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, /* bytes 928 - 944 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, /* bytes 944 - 960 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, /* bytes 960 - 976 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, /* bytes 976 - 992 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, /* bytes 992 - 1008 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, /* bytes 1008 - 1024 */ + 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0xed, 0x00, 0xac, /* bytes 1024 - 1040 */ + 0x01, 0x3c, 0x01, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, /* bytes 1040 - 1056 */ + 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, /* bytes 1056 - 1072 */ + 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, /* bytes 1072 - 1088 */ + 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, /* bytes 1088 - 1104 */ + 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 1104 - 1120 */ + 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 1120 - 1136 */ + 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 1136 - 1152 */ + 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1152 - 1168 */ + 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 1168 - 1184 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, /* bytes 1184 - 1200 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0x03, 0x00, /* bytes 1200 - 1216 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, /* bytes 1216 - 1232 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0xf0, 0x00, 0xac, 0x01, 0x3c, 0x01, /* bytes 1232 - 1248 */ + 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, /* bytes 1248 - 1264 */ + 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, /* bytes 1264 - 1280 */ + 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, /* bytes 1280 - 1296 */ + 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, /* bytes 1296 - 1312 */ + 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, /* bytes 1312 - 1328 */ + 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, /* bytes 1328 - 1344 */ + 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1344 - 1360 */ + 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1360 - 1376 */ + 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1376 - 1392 */ + 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1392 - 1408 */ + 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1408 - 1424 */ + 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0xbc, 0x00, 0xee, 0x00, 0x23, 0x00, 0x07, 0x00, /* bytes 1424 - 1440 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, /* bytes 1440 - 1456 */ + 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, /* bytes 1456 - 1472 */ + 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, /* bytes 1472 - 1488 */ + 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, /* bytes 1488 - 1504 */ + 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, /* bytes 1504 - 1520 */ + 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, /* bytes 1520 - 1536 */ + 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, /* bytes 1536 - 1552 */ + 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, /* bytes 1552 - 1568 */ + 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, /* bytes 1568 - 1584 */ + 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, /* bytes 1584 - 1600 */ + 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 1600 - 1616 */ + 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, /* bytes 1616 - 1632 */ + 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, /* bytes 1632 - 1648 */ + 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, /* bytes 1648 - 1664 */ + 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, /* bytes 1664 - 1680 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, /* bytes 1680 - 1696 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, /* bytes 1696 - 1712 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, /* bytes 1712 - 1728 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, /* bytes 1728 - 1744 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, /* bytes 1744 - 1760 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, /* bytes 1760 - 1776 */ + 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, /* bytes 1776 - 1792 */ + 0x01, 0x2c, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, /* bytes 1792 - 1808 */ + 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, /* bytes 1808 - 1824 */ + 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, /* bytes 1824 - 1840 */ + 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, /* bytes 1840 - 1856 */ + 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 1856 - 1872 */ + 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 1872 - 1888 */ + 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 1888 - 1904 */ + 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1904 - 1920 */ + 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 1920 - 1936 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, /* bytes 1936 - 1952 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, /* bytes 1952 - 1968 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, 0x01, 0xe4, 0x00, 0x23, /* bytes 1968 - 1984 */ + 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, /* bytes 1984 - 2000 */ + 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, /* bytes 2000 - 2016 */ + 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, /* bytes 2016 - 2032 */ + 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, /* bytes 2032 - 2048 */ + 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, /* bytes 2048 - 2064 */ + 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, /* bytes 2064 - 2080 */ + 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, /* bytes 2080 - 2096 */ + 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2096 - 2112 */ + 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2112 - 2128 */ + 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2128 - 2144 */ + 0x02, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0xbc, 0x00, 0xe4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2144 - 2160 */ + 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2160 - 2176 */ + 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0xea, 0x00, 0xac, 0x01, 0x3c, 0x01, 0x23, 0x00, 0x09, 0x00, /* bytes 2176 - 2192 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, /* bytes 2192 - 2208 */ + 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, /* bytes 2208 - 2224 */ + 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, /* bytes 2224 - 2240 */ + 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, /* bytes 2240 - 2256 */ + 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, /* bytes 2256 - 2272 */ + 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, /* bytes 2272 - 2288 */ + 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, /* bytes 2288 - 2304 */ + 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, /* bytes 2304 - 2320 */ + 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, /* bytes 2320 - 2336 */ + 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, /* bytes 2336 - 2352 */ + 0x00, 0x3c, 0x01, 0xbc, 0x00, 0xea, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2352 - 2368 */ + 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 2368 - 2384 */ + 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, /* bytes 2384 - 2400 */ + 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, /* bytes 2400 - 2416 */ + 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, /* bytes 2416 - 2432 */ + 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, /* bytes 2432 - 2448 */ + 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, /* bytes 2448 - 2464 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, /* bytes 2464 - 2480 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, /* bytes 2480 - 2496 */ + 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, /* bytes 2496 - 2512 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, /* bytes 2512 - 2528 */ + 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, /* bytes 2528 - 2544 */ + 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0x3c, 0x01, /* bytes 2544 - 2560 */ + 0xbc, 0x00, 0xec, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, /* bytes 2560 - 2576 */ + 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, /* bytes 2576 - 2592 */ + 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 2592 - 2608 */ + 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, /* bytes 2608 - 2624 */ + 0x01, 0x04, 0x02, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, /* bytes 2624 - 2640 */ + 0x01, 0x2c, 0x00, 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, /* bytes 2640 - 2656 */ + 0x00, 0x9c, 0x01, 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 2656 - 2672 */ + 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, /* bytes 2672 - 2688 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, /* bytes 2688 - 2704 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, /* bytes 2704 - 2720 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, /* bytes 2720 - 2736 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, /* bytes 2736 - 2752 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, /* bytes 2752 - 2768 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, /* bytes 2768 - 2784 */ + 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, /* bytes 2784 - 2800 */ + 0x01, 0xec, 0x00, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, /* bytes 2800 - 2816 */ + 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0xa4, 0x01, 0x04, 0x02, /* bytes 2816 - 2832 */ + 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x0c, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x2c, 0x00, /* bytes 2832 - 2848 */ + 0xc4, 0x00, 0x2c, 0x00, 0x5c, 0x00, 0x9c, 0x00, 0x5c, 0x00, 0x5c, 0x01, 0xc4, 0x00, 0x9c, 0x01, /* bytes 2848 - 2864 */ + 0xc4, 0x00, 0x04, 0x02, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 2864 - 2880 */ + 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 2880 - 2896 */ + 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 2896 - 2912 */ + 0x00, 0x02, 0x00, 0x34, 0x01, 0xcc, 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2912 - 2928 */ + 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 2928 - 2944 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, /* bytes 2944 - 2960 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0xec, 0x00, 0xbc, 0x00, 0x3c, 0x01, 0x03, 0x00, /* bytes 2960 - 2976 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x24, 0x01, 0x6c, 0x01, 0x44, 0x01, 0x6c, 0x01, 0x03, /* bytes 2976 - 2992 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x3c, 0x01, 0xac, 0x01, 0xec, 0x00, /* bytes 2992 - 3008 */ + 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, /* bytes 3008 - 3024 */ + 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0x91, 0x01, 0xf1, 0x01, 0xa4, 0x01, 0x9c, /* bytes 3024 - 3040 */ + 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x03, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x39, 0x00, 0xc7, 0x00, 0x39, /* bytes 3040 - 3056 */ + 0x00, 0x69, 0x00, 0x9c, 0x00, 0x5f, 0x00, 0x59, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xd7, 0x00, 0xf1, /* bytes 3056 - 3072 */ + 0x01, 0xfc, 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, /* bytes 3072 - 3088 */ + 0x04, 0x02, 0xfc, 0x00, 0xc4, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, /* bytes 3088 - 3104 */ + 0x01, 0xc4, 0x01, 0x6c, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3104 - 3120 */ + 0x34, 0x01, 0x04, 0x02, 0x34, 0x01, 0xc4, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 3120 - 3136 */ + 0x00, 0x0c, 0x01, 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 3136 - 3152 */ + 0x02, 0x00, 0x64, 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 3152 - 3168 */ + 0x00, 0x02, 0x00, 0x44, 0x01, 0x68, 0x01, 0x24, 0x01, 0x68, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3168 - 3184 */ + 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3184 - 3200 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0xf4, 0x00, 0xac, 0x01, 0x2c, 0x01, 0x23, 0x00, 0x09, /* bytes 3200 - 3216 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, /* bytes 3216 - 3232 */ + 0x04, 0x02, 0x6c, 0x01, 0x04, 0x02, 0x8d, 0x01, 0xed, 0x01, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, /* bytes 3232 - 3248 */ + 0x5c, 0x01, 0x01, 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x3b, 0x00, 0xc8, 0x00, 0x3b, 0x00, 0x6b, 0x00, /* bytes 3248 - 3264 */ + 0x9c, 0x00, 0x60, 0x00, 0x58, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xdb, 0x00, 0xed, 0x01, 0xfc, 0x00, /* bytes 3264 - 3280 */ + 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, /* bytes 3280 - 3296 */ + 0x00, 0xbf, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0xbf, 0x01, /* bytes 3296 - 3312 */ + 0x6c, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xbf, /* bytes 3312 - 3328 */ + 0x01, 0x34, 0x01, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, /* bytes 3328 - 3344 */ + 0x34, 0x02, 0x64, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, /* bytes 3344 - 3360 */ + 0x01, 0x34, 0x02, 0x0c, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3360 - 3376 */ + 0xbc, 0x00, 0xfe, 0x00, 0xbc, 0x00, 0x22, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 3376 - 3392 */ + 0x00, 0xac, 0x01, 0x1d, 0x01, 0xac, 0x01, 0xfe, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 3392 - 3408 */ + 0x02, 0x00, 0x24, 0x01, 0x66, 0x01, 0x44, 0x01, 0x66, 0x01, 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, /* bytes 3408 - 3424 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0xfc, 0x00, 0x04, 0x02, 0x34, 0x01, 0x04, 0x02, 0x6c, /* bytes 3424 - 3440 */ + 0x01, 0x04, 0x02, 0x8c, 0x01, 0xec, 0x01, 0xa4, 0x01, 0x9c, 0x01, 0x0c, 0x02, 0x5c, 0x01, 0x00, /* bytes 3440 - 3456 */ + 0x02, 0x9c, 0x00, 0xa4, 0x01, 0x3c, 0x00, 0xc8, 0x00, 0x3c, 0x00, 0x6c, 0x00, 0x9c, 0x00, 0x60, /* bytes 3456 - 3472 */ + 0x00, 0x58, 0x01, 0xc4, 0x00, 0x9c, 0x01, 0xdc, 0x00, 0xec, 0x01, 0xfc, 0x00, 0x04, 0x02, 0x03, /* bytes 3472 - 3488 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xfc, 0x00, 0xbc, 0x01, /* bytes 3488 - 3504 */ + 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x04, 0x02, 0x6c, 0x01, 0xbc, /* bytes 3504 - 3520 */ + 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xbc, 0x01, 0x34, 0x01, /* bytes 3520 - 3536 */ + 0x04, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0x50, 0x02, 0x64, /* bytes 3536 - 3552 */ + 0x01, 0xa8, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x01, 0xa8, 0x02, /* bytes 3552 - 3568 */ + 0x64, 0x01, 0x50, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x14, /* bytes 3568 - 3584 */ + 0x01, 0xac, 0x01, 0x04, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x00, /* bytes 3584 - 3600 */ + 0x04, 0x01, 0xbc, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, /* bytes 3600 - 3616 */ + 0x01, 0x64, 0x01, 0x24, 0x01, 0x64, 0x01, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 3616 - 3632 */ + 0x00, 0x00, 0x0e, 0x00, 0xf4, 0x01, 0xd1, 0x01, 0x41, 0x02, 0x5c, 0x01, 0x28, 0x02, 0x9c, 0x00, /* bytes 3632 - 3648 */ + 0xc4, 0x01, 0x32, 0x00, 0xca, 0x00, 0x17, 0x00, 0x55, 0x00, 0x70, 0x00, 0x29, 0x00, 0x36, 0x01, /* bytes 3648 - 3664 */ + 0x6b, 0x00, 0xae, 0x01, 0x96, 0x00, 0x11, 0x02, 0xd8, 0x00, 0x3a, 0x02, 0x34, 0x01, 0x4a, 0x02, /* bytes 3664 - 3680 */ + 0x75, 0x01, 0x48, 0x02, 0xc0, 0x01, 0x2c, 0x02, 0xf4, 0x01, 0xd1, 0x01, 0x03, 0x00, 0xc0, 0x02, /* bytes 3680 - 3696 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xd8, 0x00, 0x3a, 0x02, 0xef, 0x00, 0xe3, 0x01, 0x03, 0x00, 0xc0, /* bytes 3696 - 3712 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x75, 0x01, 0x48, 0x02, 0x79, 0x01, 0xe3, 0x01, 0x03, 0x00, /* bytes 3712 - 3728 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0x4a, 0x02, 0x34, 0x01, 0xe9, 0x01, 0x03, /* bytes 3728 - 3744 */ + 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa0, 0x00, 0x1c, 0x01, 0x9d, 0x00, 0xf8, 0x00, /* bytes 3744 - 3760 */ + 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0x51, 0x01, 0x75, 0x01, 0x31, 0x01, 0x75, /* bytes 3760 - 3776 */ + 0x01, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0xe5, 0x00, 0xb0, 0x01, /* bytes 3776 - 3792 */ + 0xf8, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0x34, /* bytes 3792 - 3808 */ + 0x01, 0x90, 0x02, 0x7e, 0x01, 0x8b, 0x02, 0xf5, 0x01, 0x6c, 0x02, 0x44, 0x02, 0x07, 0x02, 0x77, /* bytes 3808 - 3824 */ + 0x02, 0x5c, 0x01, 0x4f, 0x02, 0x9c, 0x00, 0xe4, 0x01, 0x28, 0x00, 0xcc, 0x00, 0xf3, 0xff, 0x3e, /* bytes 3824 - 3840 */ + 0x00, 0x43, 0x00, 0xf3, 0xff, 0x14, 0x01, 0x12, 0x00, 0xc0, 0x01, 0x50, 0x00, 0x37, 0x02, 0xb5, /* bytes 3840 - 3856 */ + 0x00, 0x70, 0x02, 0x34, 0x01, 0x90, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3856 - 3872 */ + 0x34, 0x01, 0x90, 0x02, 0x34, 0x01, 0x17, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 3872 - 3888 */ + 0x00, 0xe1, 0x00, 0x09, 0x02, 0xb5, 0x00, 0x70, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 3888 - 3904 */ + 0x02, 0x00, 0x87, 0x01, 0x09, 0x02, 0x7e, 0x01, 0x8b, 0x02, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, /* bytes 3904 - 3920 */ + 0x00, 0x02, 0x00, 0xb4, 0x01, 0xc7, 0x00, 0xb4, 0x01, 0xdc, 0x00, 0x03, 0x00, 0xc0, 0x0a, 0x00, /* bytes 3920 - 3936 */ + 0x01, 0x00, 0x02, 0x00, 0x84, 0x00, 0x1c, 0x01, 0x7f, 0x00, 0xec, 0x00, 0x03, 0x00, 0xc0, 0x0a, /* bytes 3936 - 3952 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x5f, 0x01, 0x87, 0x01, 0x3f, 0x01, 0x87, 0x01, 0x23, 0x00, 0x07, /* bytes 3952 - 3968 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0x94, 0x02, 0x3c, 0x02, 0xac, 0x02, /* bytes 3968 - 3984 */ + 0x5c, 0x01, 0x77, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x1f, 0x00, 0xcf, 0x00, 0xcf, 0xff, 0x27, 0x00, /* bytes 3984 - 4000 */ + 0x17, 0x00, 0xbc, 0xff, 0xf1, 0x00, 0xb9, 0xff, 0xd1, 0x01, 0x09, 0x00, 0x5c, 0x02, 0x91, 0x00, /* bytes 4000 - 4016 */ + 0xa7, 0x02, 0x34, 0x01, 0xd7, 0x02, 0x87, 0x01, 0xcf, 0x02, 0x29, 0x02, 0xac, 0x02, 0x94, 0x02, /* bytes 4016 - 4032 */ + 0x3c, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x00, 0x30, 0x02, 0x91, /* bytes 4032 - 4048 */ + 0x00, 0xa7, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x01, 0xcf, 0x02, /* bytes 4048 - 4064 */ + 0x94, 0x01, 0x30, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xd7, /* bytes 4064 - 4080 */ + 0x02, 0x34, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, /* bytes 4080 - 4096 */ + 0xa8, 0x00, 0xb8, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, /* bytes 4096 - 4112 */ + 0x01, 0x98, 0x01, 0x6c, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4112 - 4128 */ + 0x60, 0x00, 0xe0, 0x00, 0x68, 0x00, 0x1c, 0x01, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 4128 - 4144 */ + 0xff, 0x00, 0x00, 0x0e, 0x00, 0x34, 0x01, 0xd6, 0x02, 0x87, 0x01, 0xcc, 0x02, 0x28, 0x02, 0xa9, /* bytes 4144 - 4160 */ + 0x02, 0x90, 0x02, 0x38, 0x02, 0xab, 0x02, 0x5c, 0x01, 0x77, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x1f, /* bytes 4160 - 4176 */ + 0x00, 0xd0, 0x00, 0xd4, 0xff, 0x29, 0x00, 0x1c, 0x00, 0xc0, 0xff, 0xf4, 0x00, 0xbd, 0xff, 0xd0, /* bytes 4176 - 4192 */ + 0x01, 0x0a, 0x00, 0x5c, 0x02, 0x92, 0x00, 0xa6, 0x02, 0x34, 0x01, 0xd6, 0x02, 0x03, 0x00, 0xc0, /* bytes 4192 - 4208 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xd6, 0x02, 0x34, 0x01, 0x43, 0x02, 0x03, 0x00, /* bytes 4208 - 4224 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x94, 0x01, 0x2f, 0x02, 0x87, 0x01, 0xcc, 0x02, 0x03, /* bytes 4224 - 4240 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x92, 0x00, 0xa6, 0x02, 0xd4, 0x00, 0x2f, 0x02, /* bytes 4240 - 4256 */ + 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0xa8, 0x00, 0xb8, 0x01, 0xc0, /* bytes 4256 - 4272 */ + 0x00, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x01, 0x97, 0x01, 0x4c, 0x01, /* bytes 4272 - 4288 */ + 0x97, 0x01, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x00, 0x1c, 0x01, 0x60, /* bytes 4288 - 4304 */ + 0x00, 0xe0, 0x00, 0x23, 0x00, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, /* bytes 4304 - 4320 */ + 0x4e, 0x00, 0x5d, 0x00, 0x03, 0x00, 0x22, 0x01, 0x1c, 0x00, 0xba, 0x01, 0x48, 0x00, 0x47, 0x02, /* bytes 4320 - 4336 */ + 0xb3, 0x00, 0x82, 0x02, 0x38, 0x01, 0xa4, 0x02, 0x96, 0x01, 0x87, 0x02, 0x01, 0x02, 0x5a, 0x02, /* bytes 4336 - 4352 */ + 0x42, 0x02, 0xe9, 0x01, 0x81, 0x02, 0x52, 0x01, 0x6f, 0x02, 0x8a, 0x00, 0xfa, 0x01, 0x18, 0x00, /* bytes 4352 - 4368 */ + 0xe0, 0x00, 0x13, 0x00, 0x4e, 0x00, 0x5d, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 4368 - 4384 */ + 0x00, 0x34, 0x01, 0x17, 0x02, 0x38, 0x01, 0xa4, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 4384 - 4400 */ + 0x02, 0x00, 0xb3, 0x00, 0x82, 0x02, 0xe1, 0x00, 0x09, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 4400 - 4416 */ + 0x00, 0x02, 0x00, 0x96, 0x01, 0x87, 0x02, 0x87, 0x01, 0x09, 0x02, 0x03, 0x00, 0xc0, 0x0a, 0x00, /* bytes 4416 - 4432 */ + 0x01, 0x00, 0x02, 0x00, 0x71, 0x00, 0xe7, 0x00, 0x77, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x0a, /* bytes 4432 - 4448 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb6, 0x01, 0xcf, 0x00, 0xb6, 0x01, 0xb9, 0x00, 0x03, 0x00, 0xc0, /* bytes 4448 - 4464 */ + 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5f, 0x01, 0x87, 0x01, 0x3f, 0x01, 0x87, 0x01, 0x23, 0x00, /* bytes 4464 - 4480 */ + 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0x04, 0x02, 0x1f, 0x00, 0x0c, /* bytes 4480 - 4496 */ + 0x01, 0xc4, 0xff, 0x2f, 0x00, 0x19, 0x00, 0xb1, 0xff, 0xf4, 0x00, 0xb9, 0xff, 0xe1, 0x01, 0x0c, /* bytes 4496 - 4512 */ + 0x00, 0x6c, 0x02, 0x97, 0x00, 0xb4, 0x02, 0x34, 0x01, 0xe9, 0x02, 0x9f, 0x01, 0xd9, 0x02, 0x41, /* bytes 4512 - 4528 */ + 0x02, 0xac, 0x02, 0xa7, 0x02, 0x1f, 0x02, 0xb9, 0x02, 0x44, 0x01, 0x81, 0x02, 0x99, 0x00, 0x04, /* bytes 4528 - 4544 */ + 0x02, 0x1f, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x00, 0x30, 0x02, /* bytes 4544 - 4560 */ + 0x97, 0x00, 0xb4, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9f, 0x01, 0xd9, /* bytes 4560 - 4576 */ + 0x02, 0x94, 0x01, 0x30, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, /* bytes 4576 - 4592 */ + 0xe9, 0x02, 0x34, 0x01, 0x44, 0x02, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, /* bytes 4592 - 4608 */ + 0x01, 0xa8, 0x00, 0xb8, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4608 - 4624 */ + 0x4c, 0x01, 0x98, 0x01, 0x6c, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x0a, 0x00, 0x01, 0x00, 0x02, /* bytes 4624 - 4640 */ + 0x00, 0x60, 0x00, 0xe0, 0x00, 0x68, 0x00, 0x1c, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, /* bytes 4640 - 4656 */ + 0x04, 0xff, 0x00, 0x00, 0x0e, 0x00, 0x1d, 0x00, 0x33, 0x00, 0xbc, 0xff, 0xf1, 0x00, 0xf1, 0xff, /* bytes 4656 - 4672 */ + 0x79, 0x01, 0xe1, 0xff, 0x84, 0x02, 0x71, 0x00, 0x9f, 0x02, 0x34, 0x01, 0xf3, 0x02, 0xcb, 0x01, /* bytes 4672 - 4688 */ + 0xcb, 0x02, 0x75, 0x02, 0xfc, 0x02, 0x50, 0x02, 0x10, 0x02, 0xfc, 0x02, 0x5c, 0x01, 0x63, 0x02, /* bytes 4688 - 4704 */ + 0xe4, 0x00, 0x2c, 0x02, 0xcf, 0xff, 0x47, 0x01, 0x67, 0x00, 0x1d, 0x00, 0x33, 0x00, 0x03, 0x00, /* bytes 4704 - 4720 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x00, 0x30, 0x02, 0x71, 0x00, 0x9f, 0x02, 0x03, /* bytes 4720 - 4736 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xcb, 0x01, 0xcb, 0x02, 0x94, 0x01, 0x30, 0x02, /* bytes 4736 - 4752 */ + 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x34, 0x01, 0xf3, 0x02, 0x34, 0x01, 0x44, /* bytes 4752 - 4768 */ + 0x02, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x79, 0x01, /* bytes 4768 - 4784 */ + 0x47, 0x00, 0x74, 0x01, 0x7a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xcc, /* bytes 4784 - 4800 */ + 0xff, 0x88, 0x02, 0x10, 0x00, 0x67, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4800 - 4816 */ + 0x56, 0x00, 0x6c, 0x01, 0xfd, 0xff, 0x5d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 4816 - 4832 */ + 0x00, 0xb6, 0x02, 0x89, 0x00, 0x83, 0x02, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4832 - 4848 */ + 0x02, 0x00, 0x84, 0x02, 0xbc, 0x01, 0xba, 0x02, 0xb7, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, /* bytes 4848 - 4864 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x43, 0x00, 0x6d, 0x01, 0xe2, 0xff, 0x40, 0x01, 0x03, /* bytes 4864 - 4880 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x86, 0x02, 0xbb, 0x01, 0xd8, 0x02, 0xd4, 0x01, /* bytes 4880 - 4896 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x00, 0x6b, 0x02, 0xa6, 0xff, 0x97, /* bytes 4896 - 4912 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7a, 0x01, 0x21, 0x00, 0x73, 0x01, /* bytes 4912 - 4928 */ + 0x7a, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x84, 0x02, 0xac, 0x00, 0xd7, /* bytes 4928 - 4944 */ + 0x02, 0x78, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4944 - 4960 */ + 0x98, 0x02, 0xa4, 0x00, 0x17, 0x03, 0x53, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4960 - 4976 */ + 0x00, 0x17, 0x00, 0x65, 0x01, 0xab, 0xff, 0x0d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 4976 - 4992 */ + 0x02, 0x00, 0xe9, 0xff, 0x79, 0x02, 0x5e, 0xff, 0xb4, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 4992 - 5008 */ + 0x00, 0x02, 0x00, 0x9a, 0x02, 0xbd, 0x01, 0x14, 0x03, 0x01, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 5008 - 5024 */ + 0x01, 0x00, 0x02, 0x00, 0x7d, 0x01, 0xd7, 0xff, 0x74, 0x01, 0x62, 0x00, 0x23, 0x00, 0x05, 0x00, /* bytes 5024 - 5040 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x19, 0x03, 0x5c, 0x00, 0xf3, 0x02, 0x6b, /* bytes 5040 - 5056 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0xff, 0x28, 0x01, 0xaa, 0xff, /* bytes 5056 - 5072 */ + 0x06, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7f, 0x01, 0xc4, 0xff, 0x7c, /* bytes 5072 - 5088 */ + 0x01, 0xe6, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x83, 0xff, 0x9f, 0x02, /* bytes 5088 - 5104 */ + 0x5a, 0xff, 0xae, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xdb, 0x02, 0xf2, /* bytes 5104 - 5120 */ + 0x01, 0x14, 0x03, 0x0c, 0x02, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5120 - 5136 */ + 0x02, 0x00, 0x0b, 0x03, 0x0b, 0x02, 0x14, 0x03, 0x0c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5136 - 5152 */ + 0x00, 0x02, 0x00, 0x19, 0x03, 0x5c, 0x00, 0xfe, 0x02, 0x67, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 5152 - 5168 */ + 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xdf, 0xff, 0x81, 0x01, 0xbb, 0xff, 0x03, 0x00, 0xc0, 0x03, /* bytes 5168 - 5184 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x7a, 0xff, 0xa3, 0x02, 0x5a, 0xff, 0xae, 0x02, 0x03, 0x00, 0xc0, /* bytes 5184 - 5200 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xaa, 0xff, 0x06, 0x01, 0xad, 0xff, 0x0a, 0x01, 0x23, 0x00, /* bytes 5200 - 5216 */ + 0x03, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9f, 0xf7, 0x90, 0x00, 0xa7, /* bytes 5216 - 5232 */ + 0xf7, 0x73, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x09, 0x9c, 0x03, /* bytes 5232 - 5248 */ + 0x2d, 0x09, 0xa3, 0x03, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4b, 0xf9, 0x22, /* bytes 5248 - 5264 */ + 0x01, 0x35, 0xf9, 0x2a, 0x01, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 5264 - 5280 */ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, /* bytes 5280 - 5296 */ + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +__attribute__ ((aligned (8))) +static const uint8_t RESULT_SHREDDED_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x4a, 0x48, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x27, 0x00, 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, /* bytes 16 - 32 */ + 0x01, 0xb8, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, /* bytes 32 - 48 */ + 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, /* bytes 48 - 64 */ + 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, /* bytes 64 - 80 */ + 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, /* bytes 80 - 96 */ + 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, /* bytes 96 - 112 */ + 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, /* bytes 112 - 128 */ + 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, /* bytes 128 - 144 */ + 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, /* bytes 144 - 160 */ + 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, /* bytes 160 - 176 */ + 0x01, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, /* bytes 176 - 192 */ + 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, /* bytes 192 - 208 */ + 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 208 - 224 */ + 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, /* bytes 224 - 240 */ + 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 240 - 256 */ + 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, /* bytes 256 - 272 */ + 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, /* bytes 272 - 288 */ + 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, /* bytes 288 - 304 */ + 0x00, 0x30, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, /* bytes 304 - 320 */ + 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, /* bytes 320 - 336 */ + 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 336 - 352 */ + 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 352 - 368 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, /* bytes 368 - 384 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, /* bytes 384 - 400 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, /* bytes 400 - 416 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, /* bytes 416 - 432 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, /* bytes 432 - 448 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, /* bytes 448 - 464 */ + 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, /* bytes 464 - 480 */ + 0x01, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 480 - 496 */ + 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 496 - 512 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, /* bytes 512 - 528 */ + 0x01, 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, /* bytes 528 - 544 */ + 0xb8, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, /* bytes 544 - 560 */ + 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 560 - 576 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, /* bytes 576 - 592 */ + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, /* bytes 592 - 608 */ + 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, /* bytes 608 - 624 */ + 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, /* bytes 624 - 640 */ + 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 640 - 656 */ + 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, /* bytes 656 - 672 */ + 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, /* bytes 672 - 688 */ + 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, /* bytes 688 - 704 */ + 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, /* bytes 704 - 720 */ + 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 720 - 736 */ + 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, /* bytes 736 - 752 */ + 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 752 - 768 */ + 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, /* bytes 768 - 784 */ + 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, /* bytes 784 - 800 */ + 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, /* bytes 800 - 816 */ + 0x30, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, /* bytes 816 - 832 */ + 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, /* bytes 832 - 848 */ + 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 848 - 864 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 864 - 880 */ + 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 880 - 896 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, 0xc0, /* bytes 896 - 912 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, 0x00, /* bytes 912 - 928 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, /* bytes 928 - 944 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, 0x00, /* bytes 944 - 960 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x00, /* bytes 960 - 976 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, 0x01, /* bytes 976 - 992 */ + 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, /* bytes 992 - 1008 */ + 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, /* bytes 1008 - 1024 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, /* bytes 1024 - 1040 */ + 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, /* bytes 1040 - 1056 */ + 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, /* bytes 1056 - 1072 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 1072 - 1088 */ + 0x98, 0x01, 0x60, 0x01, 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, /* bytes 1088 - 1104 */ + 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, /* bytes 1104 - 1120 */ + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, /* bytes 1120 - 1136 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, /* bytes 1136 - 1152 */ + 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, /* bytes 1152 - 1168 */ + 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, /* bytes 1168 - 1184 */ + 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, /* bytes 1184 - 1200 */ + 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, /* bytes 1200 - 1216 */ + 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, /* bytes 1216 - 1232 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1232 - 1248 */ + 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, /* bytes 1248 - 1264 */ + 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 1264 - 1280 */ + 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, /* bytes 1280 - 1296 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, /* bytes 1296 - 1312 */ + 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, 0x30, /* bytes 1312 - 1328 */ + 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, /* bytes 1328 - 1344 */ + 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, /* bytes 1344 - 1360 */ + 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1360 - 1376 */ + 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1376 - 1392 */ + 0x00, 0x02, 0x00, 0x80, 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1392 - 1408 */ + 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 1408 - 1424 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, /* bytes 1424 - 1440 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, /* bytes 1440 - 1456 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, 0x00, 0x03, /* bytes 1456 - 1472 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x00, 0x01, /* bytes 1472 - 1488 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, 0x01, 0x68, /* bytes 1488 - 1504 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 1504 - 1520 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 1520 - 1536 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 1536 - 1552 */ + 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, 0x01, /* bytes 1552 - 1568 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, 0x01, /* bytes 1568 - 1584 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 1584 - 1600 */ + 0x01, 0x60, 0x01, 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, /* bytes 1600 - 1616 */ + 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 1616 - 1632 */ + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, /* bytes 1632 - 1648 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, /* bytes 1648 - 1664 */ + 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 1664 - 1680 */ + 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 1680 - 1696 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 1696 - 1712 */ + 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, /* bytes 1712 - 1728 */ + 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, /* bytes 1728 - 1744 */ + 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1744 - 1760 */ + 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, /* bytes 1760 - 1776 */ + 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 1776 - 1792 */ + 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, /* bytes 1792 - 1808 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, /* bytes 1808 - 1824 */ + 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, 0x30, 0x00, /* bytes 1824 - 1840 */ + 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, /* bytes 1840 - 1856 */ + 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, /* bytes 1856 - 1872 */ + 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1872 - 1888 */ + 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1888 - 1904 */ + 0x02, 0x00, 0x80, 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1904 - 1920 */ + 0x00, 0x02, 0x00, 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1920 - 1936 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 1936 - 1952 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, /* bytes 1952 - 1968 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, 0x00, 0x03, 0x00, /* bytes 1968 - 1984 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x00, 0x01, 0x03, /* bytes 1984 - 2000 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, 0x01, 0x68, 0x00, /* bytes 2000 - 2016 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, /* bytes 2016 - 2032 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 2032 - 2048 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, /* bytes 2048 - 2064 */ + 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, 0x01, 0x00, /* bytes 2064 - 2080 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, 0x01, 0x03, /* bytes 2080 - 2096 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 2096 - 2112 */ + 0x60, 0x01, 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, /* bytes 2112 - 2128 */ + 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, /* bytes 2128 - 2144 */ + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, /* bytes 2144 - 2160 */ + 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, /* bytes 2160 - 2176 */ + 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, /* bytes 2176 - 2192 */ + 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, /* bytes 2192 - 2208 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x88, /* bytes 2208 - 2224 */ + 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, /* bytes 2224 - 2240 */ + 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, /* bytes 2240 - 2256 */ + 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2256 - 2272 */ + 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, /* bytes 2272 - 2288 */ + 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 2288 - 2304 */ + 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, /* bytes 2304 - 2320 */ + 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, /* bytes 2320 - 2336 */ + 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, 0x30, 0x00, 0x88, /* bytes 2336 - 2352 */ + 0x00, 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, /* bytes 2352 - 2368 */ + 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, /* bytes 2368 - 2384 */ + 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2384 - 2400 */ + 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2400 - 2416 */ + 0x00, 0x80, 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2416 - 2432 */ + 0x02, 0x00, 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2432 - 2448 */ + 0x00, 0x02, 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2448 - 2464 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 2464 - 2480 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, 0x00, 0x03, 0x00, 0xc0, /* bytes 2480 - 2496 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x00, 0x01, 0x03, 0x00, /* bytes 2496 - 2512 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, 0x01, 0x68, 0x00, 0x03, /* bytes 2512 - 2528 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, /* bytes 2528 - 2544 */ + 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 2544 - 2560 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0f, /* bytes 2560 - 2576 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, 0x01, 0x00, 0x01, /* bytes 2576 - 2592 */ + 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, 0x01, 0x03, 0x00, /* bytes 2592 - 2608 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 2608 - 2624 */ + 0x01, 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, /* bytes 2624 - 2640 */ + 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, /* bytes 2640 - 2656 */ + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, /* bytes 2656 - 2672 */ + 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, /* bytes 2672 - 2688 */ + 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, /* bytes 2688 - 2704 */ + 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, /* bytes 2704 - 2720 */ + 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x88, 0x00, /* bytes 2720 - 2736 */ + 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, /* bytes 2736 - 2752 */ + 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, /* bytes 2752 - 2768 */ + 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, /* bytes 2768 - 2784 */ + 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, /* bytes 2784 - 2800 */ + 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, /* bytes 2800 - 2816 */ + 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, /* bytes 2816 - 2832 */ + 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, /* bytes 2832 - 2848 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, 0x30, 0x00, 0x88, 0x00, /* bytes 2848 - 2864 */ + 0x50, 0x00, 0x88, 0x00, 0x88, 0x00, 0x38, 0x00, 0x88, 0x00, 0x38, 0x00, 0x50, 0x00, 0x58, 0x00, /* bytes 2864 - 2880 */ + 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0xd8, 0x01, 0x30, 0x00, 0xe8, 0x01, 0x30, 0x00, 0x08, 0x02, /* bytes 2880 - 2896 */ + 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, /* bytes 2896 - 2912 */ + 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2912 - 2928 */ + 0x80, 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2928 - 2944 */ + 0x00, 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2944 - 2960 */ + 0x02, 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2960 - 2976 */ + 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2976 - 2992 */ + 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 2992 - 3008 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, /* bytes 3008 - 3024 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, 0x01, 0x68, 0x00, 0x03, 0x00, /* bytes 3024 - 3040 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 3040 - 3056 */ + 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3056 - 3072 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0f, 0x00, /* bytes 3072 - 3088 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, 0x01, 0x00, 0x01, 0x98, /* bytes 3088 - 3104 */ + 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, /* bytes 3104 - 3120 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 3120 - 3136 */ + 0xf8, 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, /* bytes 3136 - 3152 */ + 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, /* bytes 3152 - 3168 */ + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 3168 - 3184 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, /* bytes 3184 - 3200 */ + 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, /* bytes 3200 - 3216 */ + 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 3216 - 3232 */ + 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x88, 0x00, 0x39, /* bytes 3232 - 3248 */ + 0x00, 0x87, 0x00, 0x3b, 0x00, 0x4e, 0x00, 0x58, 0x00, 0x31, 0x00, 0x68, 0x00, 0x31, 0x00, 0xd8, /* bytes 3248 - 3264 */ + 0x01, 0x31, 0x00, 0xe8, 0x01, 0x31, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, /* bytes 3264 - 3280 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, /* bytes 3280 - 3296 */ + 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, /* bytes 3296 - 3312 */ + 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, /* bytes 3312 - 3328 */ + 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, /* bytes 3328 - 3344 */ + 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, /* bytes 3344 - 3360 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, 0x31, 0x00, 0x88, 0x00, 0x50, /* bytes 3360 - 3376 */ + 0x00, 0x88, 0x00, 0x88, 0x00, 0x39, 0x00, 0x87, 0x00, 0x3b, 0x00, 0x4e, 0x00, 0x58, 0x00, 0x31, /* bytes 3376 - 3392 */ + 0x00, 0x68, 0x00, 0x31, 0x00, 0xd8, 0x01, 0x31, 0x00, 0xe8, 0x01, 0x31, 0x00, 0x08, 0x02, 0x50, /* bytes 3392 - 3408 */ + 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, /* bytes 3408 - 3424 */ + 0x98, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, /* bytes 3424 - 3440 */ + 0x01, 0xc8, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3440 - 3456 */ + 0x80, 0x01, 0x98, 0x00, 0xb8, 0x00, 0x98, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 3456 - 3472 */ + 0x00, 0x70, 0x00, 0x98, 0x01, 0x70, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 3472 - 3488 */ + 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3488 - 3504 */ + 0x00, 0x02, 0x00, 0xb8, 0x00, 0x38, 0x00, 0xb0, 0x01, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3504 - 3520 */ + 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3520 - 3536 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x68, 0x00, 0xb0, 0x01, 0x68, 0x00, 0x03, 0x00, 0xc0, /* bytes 3536 - 3552 */ + 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, /* bytes 3552 - 3568 */ + 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 3568 - 3584 */ + 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x10, 0x00, 0x03, /* bytes 3584 - 3600 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, 0x01, 0x00, 0x01, 0x98, 0x01, /* bytes 3600 - 3616 */ + 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3616 - 3632 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf8, /* bytes 3632 - 3648 */ + 0x01, 0x90, 0x01, 0xf8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, /* bytes 3648 - 3664 */ + 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, /* bytes 3664 - 3680 */ + 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 3680 - 3696 */ + 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, /* bytes 3696 - 3712 */ + 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, /* bytes 3712 - 3728 */ + 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, /* bytes 3728 - 3744 */ + 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x72, 0x00, 0x3d, 0x00, /* bytes 3744 - 3760 */ + 0x6d, 0x00, 0x3d, 0x00, 0x4c, 0x00, 0x58, 0x00, 0x33, 0x00, 0x68, 0x00, 0x33, 0x00, 0xd8, 0x01, /* bytes 3760 - 3776 */ + 0x33, 0x00, 0xe8, 0x01, 0x33, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 3776 - 3792 */ + 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, /* bytes 3792 - 3808 */ + 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, /* bytes 3808 - 3824 */ + 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x30, /* bytes 3824 - 3840 */ + 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, /* bytes 3840 - 3856 */ + 0x00, 0x98, 0x01, 0xd0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0xc8, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, /* bytes 3856 - 3872 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x58, 0x00, 0x33, 0x00, 0x3d, 0x00, 0x4c, 0x00, /* bytes 3872 - 3888 */ + 0x3d, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x72, 0x00, 0x88, 0x00, 0x50, 0x00, 0x68, 0x00, 0x33, 0x00, /* bytes 3888 - 3904 */ + 0xd8, 0x01, 0x33, 0x00, 0xe8, 0x01, 0x33, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, /* bytes 3904 - 3920 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x45, 0x00, 0xb8, 0x00, 0x45, /* bytes 3920 - 3936 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, /* bytes 3936 - 3952 */ + 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, /* bytes 3952 - 3968 */ + 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, /* bytes 3968 - 3984 */ + 0xc0, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, /* bytes 3984 - 4000 */ + 0x01, 0x88, 0x00, 0x72, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, /* bytes 4000 - 4016 */ + 0x75, 0x00, 0xb0, 0x01, 0x75, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, /* bytes 4016 - 4032 */ + 0x01, 0xd5, 0x00, 0xb8, 0x00, 0xd5, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4032 - 4048 */ + 0x80, 0x01, 0xa5, 0x00, 0xb8, 0x00, 0xa5, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4048 - 4064 */ + 0x00, 0x58, 0x00, 0x33, 0x00, 0x68, 0x00, 0x33, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4064 - 4080 */ + 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, /* bytes 4080 - 4096 */ + 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, /* bytes 4096 - 4112 */ + 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x10, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 4112 - 4128 */ + 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xb8, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 4128 - 4144 */ + 0xd0, 0x00, 0xb8, 0x01, 0x00, 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, /* bytes 4144 - 4160 */ + 0x00, 0x60, 0x01, 0xfb, 0x01, 0x90, 0x01, 0xfb, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 4160 - 4176 */ + 0x01, 0x60, 0x01, 0xfb, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, /* bytes 4176 - 4192 */ + 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, /* bytes 4192 - 4208 */ + 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 4208 - 4224 */ + 0xf0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, /* bytes 4224 - 4240 */ + 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xcc, 0x01, 0xa0, 0x00, 0xcc, 0x01, 0xa0, 0x00, /* bytes 4240 - 4256 */ + 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, /* bytes 4256 - 4272 */ + 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x6c, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x3f, 0x00, /* bytes 4272 - 4288 */ + 0x4a, 0x00, 0x58, 0x00, 0x36, 0x00, 0x68, 0x00, 0x36, 0x00, 0xd8, 0x01, 0x36, 0x00, 0xe8, 0x01, /* bytes 4288 - 4304 */ + 0x36, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 4304 - 4320 */ + 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xa0, 0x00, 0x98, 0x01, 0xa0, /* bytes 4320 - 4336 */ + 0x00, 0xcc, 0x01, 0xd0, 0x00, 0xcc, 0x01, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, /* bytes 4336 - 4352 */ + 0x01, 0xc0, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x60, 0x01, 0xf0, 0x01, 0x60, /* bytes 4352 - 4368 */ + 0x01, 0x98, 0x01, 0x90, 0x01, 0x98, 0x01, 0x90, 0x01, 0xd8, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0xc0, /* bytes 4368 - 4384 */ + 0x01, 0xc0, 0x01, 0xf0, 0x01, 0xc0, 0x01, 0xf0, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4384 - 4400 */ + 0x01, 0x00, 0x0a, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x08, 0x02, 0x50, 0x00, 0xe8, 0x01, 0x36, 0x00, /* bytes 4400 - 4416 */ + 0xd8, 0x01, 0x36, 0x00, 0x68, 0x00, 0x36, 0x00, 0x58, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x4a, 0x00, /* bytes 4416 - 4432 */ + 0x3f, 0x00, 0x66, 0x00, 0x88, 0x00, 0x6c, 0x00, 0x88, 0x00, 0x50, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 4432 - 4448 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x51, 0x00, 0xb8, 0x00, 0x51, 0x00, 0x03, 0x00, 0xc0, /* bytes 4448 - 4464 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 4464 - 4480 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x50, 0x00, 0x68, 0x00, 0x36, 0x00, 0x03, /* bytes 4480 - 4496 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, /* bytes 4496 - 4512 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc0, /* bytes 4512 - 4528 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 4528 - 4544 */ + 0x6c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x81, 0x00, 0xb0, /* bytes 4544 - 4560 */ + 0x01, 0x81, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0xb1, 0x00, /* bytes 4560 - 4576 */ + 0xb8, 0x00, 0xb1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xe1, /* bytes 4576 - 4592 */ + 0x00, 0x80, 0x01, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, /* bytes 4592 - 4608 */ + 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 4608 - 4624 */ + 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, /* bytes 4624 - 4640 */ + 0x01, 0x34, 0x01, 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 4640 - 4656 */ + 0x00, 0x01, 0xb8, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, /* bytes 4656 - 4672 */ + 0x00, 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x43, /* bytes 4672 - 4688 */ + 0x02, 0x60, 0x01, 0x43, 0x02, 0x60, 0x01, 0x98, 0x01, 0x90, 0x01, 0x98, 0x01, 0x90, 0x01, 0x43, /* bytes 4688 - 4704 */ + 0x02, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, /* bytes 4704 - 4720 */ + 0x98, 0x01, 0xf0, 0x01, 0xc2, 0x01, 0xc0, 0x01, 0xc2, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, /* bytes 4720 - 4736 */ + 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf1, 0x01, 0x30, 0x01, /* bytes 4736 - 4752 */ + 0xf1, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, /* bytes 4752 - 4768 */ + 0x98, 0x01, 0xd0, 0x00, 0xde, 0x01, 0xa0, 0x00, 0xde, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, /* bytes 4768 - 4784 */ + 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 4784 - 4800 */ + 0x00, 0x01, 0x88, 0x00, 0x63, 0x00, 0x49, 0x00, 0x5e, 0x00, 0x49, 0x00, 0x42, 0x00, 0x58, 0x00, /* bytes 4800 - 4816 */ + 0x38, 0x00, 0x68, 0x00, 0x38, 0x00, 0xd8, 0x01, 0x38, 0x00, 0xe8, 0x01, 0x38, 0x00, 0x08, 0x02, /* bytes 4816 - 4832 */ + 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 4832 - 4848 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc2, 0x01, 0xc0, /* bytes 4848 - 4864 */ + 0x01, 0xc2, 0x01, 0xc0, 0x01, 0xd8, 0x01, 0x90, 0x01, 0xd8, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 4864 - 4880 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0xf1, 0x01, 0x30, 0x01, 0xf1, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, /* bytes 4880 - 4896 */ + 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xde, 0x01, 0xa0, /* bytes 4896 - 4912 */ + 0x00, 0xde, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, /* bytes 4912 - 4928 */ + 0x68, 0x00, 0x38, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x63, 0x00, 0x49, 0x00, 0x5e, 0x00, /* bytes 4928 - 4944 */ + 0x49, 0x00, 0x42, 0x00, 0x58, 0x00, 0x38, 0x00, 0x68, 0x00, 0x38, 0x00, 0xd8, 0x01, 0x38, 0x00, /* bytes 4944 - 4960 */ + 0xe8, 0x01, 0x38, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 4960 - 4976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc2, 0x01, 0x03, 0x00, 0xc0, /* bytes 4976 - 4992 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0x80, 0x01, 0xbe, 0x00, 0x03, 0x00, /* bytes 4992 - 5008 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0xee, 0x00, 0xb8, 0x00, 0xee, 0x00, 0x03, /* bytes 5008 - 5024 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, /* bytes 5024 - 5040 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x5e, 0x00, 0xb0, 0x01, 0x5e, /* bytes 5040 - 5056 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 5056 - 5072 */ + 0x63, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x8e, 0x00, 0xb0, /* bytes 5072 - 5088 */ + 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, /* bytes 5088 - 5104 */ + 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, /* bytes 5104 - 5120 */ + 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, /* bytes 5120 - 5136 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, /* bytes 5136 - 5152 */ + 0x34, 0x01, 0x23, 0x00, 0x11, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, /* bytes 5152 - 5168 */ + 0x01, 0xb8, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x01, 0x00, /* bytes 5168 - 5184 */ + 0x01, 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x40, 0x02, /* bytes 5184 - 5200 */ + 0x60, 0x01, 0x40, 0x02, 0x60, 0x01, 0x98, 0x01, 0x90, 0x01, 0x98, 0x01, 0x90, 0x01, 0x40, 0x02, /* bytes 5200 - 5216 */ + 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, /* bytes 5216 - 5232 */ + 0x01, 0xf0, 0x01, 0xee, 0x01, 0xc0, 0x01, 0xee, 0x01, 0xc0, 0x01, 0xdd, 0x01, 0x90, 0x01, 0xdd, /* bytes 5232 - 5248 */ + 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf6, 0x01, 0x30, 0x01, 0xf6, /* bytes 5248 - 5264 */ + 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, /* bytes 5264 - 5280 */ + 0x01, 0xd0, 0x00, 0xdd, 0x01, 0xa0, 0x00, 0xdd, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, /* bytes 5280 - 5296 */ + 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, /* bytes 5296 - 5312 */ + 0x01, 0x88, 0x00, 0x5a, 0x00, 0x4c, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x40, 0x00, 0x58, 0x00, 0x38, /* bytes 5312 - 5328 */ + 0x00, 0x68, 0x00, 0x38, 0x00, 0xd8, 0x01, 0x38, 0x00, 0xe8, 0x01, 0x38, 0x00, 0x08, 0x02, 0x50, /* bytes 5328 - 5344 */ + 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 5344 - 5360 */ + 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xa0, 0x00, 0x98, 0x01, 0xa0, 0x00, 0xdd, 0x01, 0xd0, 0x00, /* bytes 5360 - 5376 */ + 0xdd, 0x01, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x30, 0x01, /* bytes 5376 - 5392 */ + 0xc0, 0x01, 0x30, 0x01, 0xf6, 0x01, 0x60, 0x01, 0xf6, 0x01, 0x60, 0x01, 0x98, 0x01, 0x90, 0x01, /* bytes 5392 - 5408 */ + 0x98, 0x01, 0x90, 0x01, 0xdd, 0x01, 0xc0, 0x01, 0xdd, 0x01, 0xc0, 0x01, 0xee, 0x01, 0xf0, 0x01, /* bytes 5408 - 5424 */ + 0xee, 0x01, 0xf0, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, /* bytes 5424 - 5440 */ + 0x00, 0x50, 0x00, 0x88, 0x00, 0x5a, 0x00, 0x4c, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x40, 0x00, 0x58, /* bytes 5440 - 5456 */ + 0x00, 0x38, 0x00, 0x68, 0x00, 0x38, 0x00, 0xd8, 0x01, 0x38, 0x00, 0xe8, 0x01, 0x38, 0x00, 0x08, /* bytes 5456 - 5472 */ + 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5472 - 5488 */ + 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xee, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5488 - 5504 */ + 0x00, 0x80, 0x01, 0xfb, 0x00, 0xb8, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5504 - 5520 */ + 0x02, 0x00, 0x80, 0x01, 0xcb, 0x00, 0xb8, 0x00, 0xcb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5520 - 5536 */ + 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 5536 - 5552 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 5552 - 5568 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x50, 0x00, 0x68, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, /* bytes 5568 - 5584 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x5a, 0x00, 0x03, 0x00, /* bytes 5584 - 5600 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x6b, 0x00, 0xb0, 0x01, 0x6b, 0x00, 0x03, /* bytes 5600 - 5616 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x3b, 0x00, 0xb8, 0x00, 0x3b, 0x00, /* bytes 5616 - 5632 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x9b, 0x00, 0xb8, 0x00, 0x9b, /* bytes 5632 - 5648 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 5648 - 5664 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 5664 - 5680 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 5680 - 5696 */ + 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xd8, 0x01, /* bytes 5696 - 5712 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xd8, 0x01, 0x00, 0x01, 0xd8, 0x01, /* bytes 5712 - 5728 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 5728 - 5744 */ + 0x01, 0x60, 0x01, 0x40, 0x02, 0x90, 0x01, 0x40, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, /* bytes 5744 - 5760 */ + 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0xe8, 0x01, 0x37, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, /* bytes 5760 - 5776 */ + 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 5776 - 5792 */ + 0xec, 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xdd, 0x01, 0x90, 0x01, 0xdd, 0x01, 0x90, 0x01, /* bytes 5792 - 5808 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, /* bytes 5808 - 5824 */ + 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 5824 - 5840 */ + 0xdd, 0x01, 0xa0, 0x00, 0xdd, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 5840 - 5856 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 5856 - 5872 */ + 0x3b, 0x00, 0x58, 0x00, 0x32, 0x00, 0x58, 0x00, 0x35, 0x00, 0x58, 0x00, 0x37, 0x00, 0x68, 0x00, /* bytes 5872 - 5888 */ + 0x37, 0x00, 0xd8, 0x01, 0x37, 0x00, 0xe8, 0x01, 0x37, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5888 - 5904 */ + 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, /* bytes 5904 - 5920 */ + 0x01, 0xdd, 0x01, 0x90, 0x01, 0xdd, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 5920 - 5936 */ + 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, /* bytes 5936 - 5952 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xdd, 0x01, 0xa0, 0x00, 0xdd, 0x01, 0xa0, /* bytes 5952 - 5968 */ + 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x68, 0x00, 0x37, 0x00, /* bytes 5968 - 5984 */ + 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x3b, 0x00, 0x58, 0x00, 0x32, 0x00, 0x58, 0x00, 0x35, 0x00, /* bytes 5984 - 6000 */ + 0x58, 0x00, 0x37, 0x00, 0x68, 0x00, 0x37, 0x00, 0xd8, 0x01, 0x37, 0x00, 0xe8, 0x01, 0x37, 0x00, /* bytes 6000 - 6016 */ + 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 6016 - 6032 */ + 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6032 - 6048 */ + 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6048 - 6064 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xec, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6064 - 6080 */ + 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xd8, 0x00, 0x80, 0x01, 0xd8, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 6080 - 6096 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xa8, 0x00, 0xb0, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, /* bytes 6096 - 6112 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x3b, 0x00, 0x03, 0x00, /* bytes 6112 - 6128 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x48, 0x00, 0xb8, 0x00, 0x48, 0x00, 0x03, /* bytes 6128 - 6144 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x78, 0x00, 0xb0, 0x01, 0x78, 0x00, /* bytes 6144 - 6160 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, /* bytes 6160 - 6176 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 6176 - 6192 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, /* bytes 6192 - 6208 */ + 0x10, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, /* bytes 6208 - 6224 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x02, 0x03, /* bytes 6224 - 6240 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 6240 - 6256 */ + 0x60, 0x01, 0x40, 0x02, 0x90, 0x01, 0x40, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, /* bytes 6256 - 6272 */ + 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xec, /* bytes 6272 - 6288 */ + 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xe2, 0x01, 0x90, 0x01, 0xe2, 0x01, 0x90, 0x01, 0x98, /* bytes 6288 - 6304 */ + 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xc0, /* bytes 6304 - 6320 */ + 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xdd, /* bytes 6320 - 6336 */ + 0x01, 0xa0, 0x00, 0xdd, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, /* bytes 6336 - 6352 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x3e, /* bytes 6352 - 6368 */ + 0x00, 0x6a, 0x00, 0x3f, 0x00, 0x68, 0x00, 0x40, 0x00, 0x68, 0x00, 0x3f, 0x00, 0x78, 0x00, 0x3f, /* bytes 6368 - 6384 */ + 0x00, 0xe8, 0x01, 0x3f, 0x00, 0xf8, 0x01, 0x3f, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, /* bytes 6384 - 6400 */ + 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6400 - 6416 */ + 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, 0x01, /* bytes 6416 - 6432 */ + 0xe2, 0x01, 0x90, 0x01, 0xe2, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 6432 - 6448 */ + 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x00, 0x01, 0xc0, 0x01, 0x00, 0x01, /* bytes 6448 - 6464 */ + 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xdd, 0x01, 0xa0, 0x00, 0xdd, 0x01, 0xa0, 0x00, /* bytes 6464 - 6480 */ + 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, /* bytes 6480 - 6496 */ + 0x00, 0x3e, 0x00, 0x6a, 0x00, 0x3f, 0x00, 0x68, 0x00, 0x40, 0x00, 0x68, 0x00, 0x3f, 0x00, 0x78, /* bytes 6496 - 6512 */ + 0x00, 0x3f, 0x00, 0xe8, 0x01, 0x3f, 0x00, 0xf8, 0x01, 0x3f, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, /* bytes 6512 - 6528 */ + 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0xb6, 0x00, /* bytes 6528 - 6544 */ + 0xb8, 0x00, 0xb6, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x86, /* bytes 6544 - 6560 */ + 0x00, 0xb0, 0x01, 0x86, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, /* bytes 6560 - 6576 */ + 0xe6, 0x00, 0xb8, 0x00, 0xe6, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 6576 - 6592 */ + 0x01, 0x98, 0x01, 0x30, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6592 - 6608 */ + 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xec, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 6608 - 6624 */ + 0x00, 0x88, 0x00, 0x50, 0x00, 0x78, 0x00, 0x3f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6624 - 6640 */ + 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x3e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6640 - 6656 */ + 0x00, 0x02, 0x00, 0xb8, 0x00, 0x56, 0x00, 0xb0, 0x01, 0x56, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6656 - 6672 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 6672 - 6688 */ + 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, /* bytes 6688 - 6704 */ + 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 6704 - 6720 */ + 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, /* bytes 6720 - 6736 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, /* bytes 6736 - 6752 */ + 0x01, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 6752 - 6768 */ + 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x40, 0x02, 0x90, 0x01, 0x40, 0x02, 0x90, 0x01, 0x98, 0x01, /* bytes 6768 - 6784 */ + 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, /* bytes 6784 - 6800 */ + 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xec, /* bytes 6800 - 6816 */ + 0x01, 0xc0, 0x01, 0xe2, 0x01, 0x90, 0x01, 0xe2, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 6816 - 6832 */ + 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xca, 0x01, 0x00, 0x01, 0xca, /* bytes 6832 - 6848 */ + 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xe6, 0x01, 0xa0, 0x00, 0xe6, /* bytes 6848 - 6864 */ + 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, /* bytes 6864 - 6880 */ + 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x46, 0x00, 0x77, 0x00, 0x4b, /* bytes 6880 - 6896 */ + 0x00, 0x78, 0x00, 0x4b, 0x00, 0x78, 0x00, 0x47, 0x00, 0x88, 0x00, 0x47, 0x00, 0xf8, 0x01, 0x47, /* bytes 6896 - 6912 */ + 0x00, 0x08, 0x02, 0x47, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, /* bytes 6912 - 6928 */ + 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, /* bytes 6928 - 6944 */ + 0x98, 0x01, 0xf0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xe2, 0x01, 0x90, 0x01, /* bytes 6944 - 6960 */ + 0xe2, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, /* bytes 6960 - 6976 */ + 0xf5, 0x01, 0x30, 0x01, 0xca, 0x01, 0x00, 0x01, 0xca, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, /* bytes 6976 - 6992 */ + 0x98, 0x01, 0xd0, 0x00, 0xe6, 0x01, 0xa0, 0x00, 0xe6, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 6992 - 7008 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x88, 0x00, 0x47, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, /* bytes 7008 - 7024 */ + 0x00, 0x46, 0x00, 0x77, 0x00, 0x4b, 0x00, 0x78, 0x00, 0x4b, 0x00, 0x78, 0x00, 0x47, 0x00, 0x88, /* bytes 7024 - 7040 */ + 0x00, 0x47, 0x00, 0xf8, 0x01, 0x47, 0x00, 0x08, 0x02, 0x47, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, /* bytes 7040 - 7056 */ + 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0xc1, 0x00, /* bytes 7056 - 7072 */ + 0xb8, 0x00, 0xc1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x91, /* bytes 7072 - 7088 */ + 0x00, 0xb8, 0x00, 0x91, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, /* bytes 7088 - 7104 */ + 0x98, 0x01, 0x30, 0x01, 0xca, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, /* bytes 7104 - 7120 */ + 0x01, 0x98, 0x01, 0xc0, 0x01, 0xec, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7120 - 7136 */ + 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7136 - 7152 */ + 0x00, 0x88, 0x00, 0x46, 0x00, 0x88, 0x00, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 7152 - 7168 */ + 0x02, 0x00, 0xb8, 0x00, 0x61, 0x00, 0xb0, 0x01, 0x61, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 7168 - 7184 */ + 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, /* bytes 7184 - 7200 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7200 - 7216 */ + 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 7216 - 7232 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0x00, /* bytes 7232 - 7248 */ + 0x02, 0xd0, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, /* bytes 7248 - 7264 */ + 0x05, 0x00, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x84, 0x02, 0x90, 0x01, 0x84, 0x02, 0x90, 0x01, /* bytes 7264 - 7280 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, /* bytes 7280 - 7296 */ + 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, /* bytes 7296 - 7312 */ + 0x01, 0xe2, 0x01, 0x90, 0x01, 0xe2, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 7312 - 7328 */ + 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xca, 0x01, 0x00, 0x01, 0xca, 0x01, 0x00, /* bytes 7328 - 7344 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0xf3, 0x01, 0xa0, /* bytes 7344 - 7360 */ + 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, /* bytes 7360 - 7376 */ + 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x4d, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, /* bytes 7376 - 7392 */ + 0x00, 0x41, 0x00, 0x88, 0x00, 0x3c, 0x00, 0x98, 0x00, 0x3c, 0x00, 0x08, 0x02, 0x3c, 0x00, 0x08, /* bytes 7392 - 7408 */ + 0x02, 0x3c, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, /* bytes 7408 - 7424 */ + 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, /* bytes 7424 - 7440 */ + 0xf0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xec, 0x01, 0xc0, 0x01, 0xe2, 0x01, 0x90, 0x01, 0xe2, 0x01, /* bytes 7440 - 7456 */ + 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, /* bytes 7456 - 7472 */ + 0x30, 0x01, 0xca, 0x01, 0x00, 0x01, 0xca, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 7472 - 7488 */ + 0xd0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 7488 - 7504 */ + 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, 0x00, 0x50, 0x00, 0x88, 0x00, 0x4d, 0x00, 0x88, 0x00, 0x50, /* bytes 7504 - 7520 */ + 0x00, 0x88, 0x00, 0x41, 0x00, 0x88, 0x00, 0x3c, 0x00, 0x98, 0x00, 0x3c, 0x00, 0x08, 0x02, 0x3c, /* bytes 7520 - 7536 */ + 0x00, 0x08, 0x02, 0x3c, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, /* bytes 7536 - 7552 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0xce, 0x00, 0xb8, 0x00, 0xce, 0x00, 0x03, 0x00, /* bytes 7552 - 7568 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x9e, 0x00, 0xb8, 0x00, 0x9e, 0x00, 0x03, /* bytes 7568 - 7584 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xca, 0x01, /* bytes 7584 - 7600 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xec, /* bytes 7600 - 7616 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x50, 0x00, 0x98, 0x00, /* bytes 7616 - 7632 */ + 0x3c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, /* bytes 7632 - 7648 */ + 0x00, 0x4d, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x6e, 0x00, /* bytes 7648 - 7664 */ + 0xb8, 0x00, 0x6e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, /* bytes 7664 - 7680 */ + 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, /* bytes 7680 - 7696 */ + 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 7696 - 7712 */ + 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, /* bytes 7712 - 7728 */ + 0x01, 0x34, 0x01, 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 7728 - 7744 */ + 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x02, /* bytes 7744 - 7760 */ + 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x88, /* bytes 7760 - 7776 */ + 0x02, 0x60, 0x01, 0x88, 0x02, 0x60, 0x01, 0x98, 0x01, 0x90, 0x01, 0x98, 0x01, 0x90, 0x01, 0x88, /* bytes 7776 - 7792 */ + 0x02, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, /* bytes 7792 - 7808 */ + 0x98, 0x01, 0xf0, 0x01, 0x1b, 0x02, 0xc0, 0x01, 0x1b, 0x02, 0xc0, 0x01, 0xe7, 0x01, 0x90, 0x01, /* bytes 7808 - 7824 */ + 0xe7, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, /* bytes 7824 - 7840 */ + 0xf5, 0x01, 0x30, 0x01, 0xd3, 0x01, 0x00, 0x01, 0xd3, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, /* bytes 7840 - 7856 */ + 0x98, 0x01, 0xd0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, /* bytes 7856 - 7872 */ + 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 7872 - 7888 */ + 0x00, 0x01, 0x88, 0x00, 0x54, 0x00, 0x88, 0x00, 0x55, 0x00, 0x88, 0x00, 0x4f, 0x00, 0x88, 0x00, /* bytes 7888 - 7904 */ + 0x49, 0x00, 0x98, 0x00, 0x49, 0x00, 0x08, 0x02, 0x49, 0x00, 0x08, 0x02, 0x49, 0x00, 0x08, 0x02, /* bytes 7904 - 7920 */ + 0x50, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 7920 - 7936 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x1b, 0x02, 0xc0, /* bytes 7936 - 7952 */ + 0x01, 0x1b, 0x02, 0xc0, 0x01, 0xe7, 0x01, 0x90, 0x01, 0xe7, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 7952 - 7968 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xf5, 0x01, 0x30, 0x01, 0xd3, 0x01, 0x00, /* bytes 7968 - 7984 */ + 0x01, 0xd3, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xf3, 0x01, 0xa0, /* bytes 7984 - 8000 */ + 0x00, 0xf3, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, /* bytes 8000 - 8016 */ + 0x08, 0x02, 0xfc, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0x49, 0x00, 0x08, 0x02, 0x49, 0x00, /* bytes 8016 - 8032 */ + 0x98, 0x00, 0x49, 0x00, 0x88, 0x00, 0x49, 0x00, 0x88, 0x00, 0x4f, 0x00, 0x88, 0x00, 0x55, 0x00, /* bytes 8032 - 8048 */ + 0x88, 0x00, 0x54, 0x00, 0x88, 0x00, 0x50, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 8048 - 8064 */ + 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x1b, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8064 - 8080 */ + 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8080 - 8096 */ + 0x00, 0x02, 0x00, 0x88, 0x00, 0x50, 0x00, 0x98, 0x00, 0x49, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8096 - 8112 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xd3, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 8112 - 8128 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x54, 0x00, 0x03, 0x00, 0xc0, /* bytes 8128 - 8144 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x7b, 0x00, 0xb0, 0x01, 0x7b, 0x00, 0x03, 0x00, /* bytes 8144 - 8160 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xdb, 0x00, 0xb0, 0x01, 0xdb, 0x00, 0x03, /* bytes 8160 - 8176 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xab, 0x00, 0xb0, 0x01, 0xab, 0x00, /* bytes 8176 - 8192 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, /* bytes 8192 - 8208 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 8208 - 8224 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, /* bytes 8224 - 8240 */ + 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, /* bytes 8240 - 8256 */ + 0x01, 0x98, 0x01, 0x00, 0x01, 0x03, 0x02, 0xd0, 0x00, 0x03, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, /* bytes 8256 - 8272 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 8272 - 8288 */ + 0x60, 0x01, 0x88, 0x02, 0x90, 0x01, 0x88, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, /* bytes 8288 - 8304 */ + 0xff, 0x00, 0x00, 0x21, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x17, /* bytes 8304 - 8320 */ + 0x02, 0xc0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0xe7, 0x01, 0x90, 0x01, 0xe7, 0x01, 0x90, 0x01, 0x98, /* bytes 8320 - 8336 */ + 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf6, 0x01, 0x30, 0x01, 0xf6, 0x01, 0x30, 0x01, 0xd3, /* bytes 8336 - 8352 */ + 0x01, 0x00, 0x01, 0xd3, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xf3, /* bytes 8352 - 8368 */ + 0x01, 0xa0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, /* bytes 8368 - 8384 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x5b, /* bytes 8384 - 8400 */ + 0x00, 0x88, 0x00, 0x5a, 0x00, 0x88, 0x00, 0x5c, 0x00, 0x88, 0x00, 0x58, 0x00, 0x98, 0x00, 0x58, /* bytes 8400 - 8416 */ + 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0xf5, 0x00, 0x08, 0x02, 0xfc, /* bytes 8416 - 8432 */ + 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8432 - 8448 */ + 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0x17, 0x02, 0xc0, 0x01, /* bytes 8448 - 8464 */ + 0xe7, 0x01, 0x90, 0x01, 0xe7, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 8464 - 8480 */ + 0xf6, 0x01, 0x30, 0x01, 0xf6, 0x01, 0x30, 0x01, 0xd3, 0x01, 0x00, 0x01, 0xd3, 0x01, 0x00, 0x01, /* bytes 8480 - 8496 */ + 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0xf3, 0x01, 0xa0, 0x00, /* bytes 8496 - 8512 */ + 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x98, 0x00, 0x58, 0x00, 0x88, /* bytes 8512 - 8528 */ + 0x00, 0x58, 0x00, 0x88, 0x00, 0x5b, 0x00, 0x88, 0x00, 0x5a, 0x00, 0x88, 0x00, 0x5c, 0x00, 0x88, /* bytes 8528 - 8544 */ + 0x00, 0x58, 0x00, 0x98, 0x00, 0x58, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, /* bytes 8544 - 8560 */ + 0x02, 0xf5, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 8560 - 8576 */ + 0xb8, 0x00, 0x88, 0x00, 0xb0, 0x01, 0x88, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 8576 - 8592 */ + 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xd3, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8592 - 8608 */ + 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x17, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8608 - 8624 */ + 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8624 - 8640 */ + 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x5b, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 8640 - 8656 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xb8, 0x00, 0xb0, 0x01, 0xb8, 0x00, 0x03, 0x00, 0xc0, /* bytes 8656 - 8672 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xe8, 0x00, 0xb0, 0x01, 0xe8, 0x00, 0x03, 0x00, /* bytes 8672 - 8688 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 8688 - 8704 */ + 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8704 - 8720 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0f, 0x00, /* bytes 8720 - 8736 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, /* bytes 8736 - 8752 */ + 0x01, 0x00, 0x01, 0x4b, 0x02, 0xd0, 0x00, 0x4b, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, /* bytes 8752 - 8768 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 8768 - 8784 */ + 0x88, 0x02, 0x90, 0x01, 0x88, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, /* bytes 8784 - 8800 */ + 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x17, 0x02, 0xc0, /* bytes 8800 - 8816 */ + 0x01, 0x17, 0x02, 0xc0, 0x01, 0xec, 0x01, 0x90, 0x01, 0xec, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 8816 - 8832 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0xfb, 0x01, 0x30, 0x01, 0xfb, 0x01, 0x30, 0x01, 0xdd, 0x01, 0x00, /* bytes 8832 - 8848 */ + 0x01, 0xdd, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xf3, 0x01, 0xa0, /* bytes 8848 - 8864 */ + 0x00, 0xf3, 0x01, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 8864 - 8880 */ + 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, /* bytes 8880 - 8896 */ + 0x00, 0xfa, 0x00, 0x88, 0x00, 0x67, 0x00, 0x88, 0x00, 0x65, 0x00, 0x08, 0x02, 0x65, 0x00, 0x08, /* bytes 8896 - 8912 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8912 - 8928 */ + 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0x17, 0x02, /* bytes 8928 - 8944 */ + 0xc0, 0x01, 0xec, 0x01, 0x90, 0x01, 0xec, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 8944 - 8960 */ + 0x60, 0x01, 0xfb, 0x01, 0x30, 0x01, 0xfb, 0x01, 0x30, 0x01, 0xdd, 0x01, 0x00, 0x01, 0xdd, 0x01, /* bytes 8960 - 8976 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xf3, 0x01, 0xa0, 0x00, 0xf3, 0x01, /* bytes 8976 - 8992 */ + 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x07, 0x00, 0x88, 0x00, 0x67, /* bytes 8992 - 9008 */ + 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0x65, /* bytes 9008 - 9024 */ + 0x00, 0x08, 0x02, 0x65, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 9024 - 9040 */ + 0x02, 0x00, 0x88, 0x00, 0x67, 0x00, 0x88, 0x00, 0x65, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9040 - 9056 */ + 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xdd, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 9056 - 9072 */ + 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x17, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 9072 - 9088 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xc2, 0x00, 0xb0, 0x01, 0xc2, 0x00, 0x03, 0x00, 0xc0, /* bytes 9088 - 9104 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xf2, 0x00, 0xb0, 0x01, 0xf2, 0x00, 0x03, 0x00, /* bytes 9104 - 9120 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0x92, 0x00, 0xb0, 0x01, 0x92, 0x00, 0x03, /* bytes 9120 - 9136 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, /* bytes 9136 - 9152 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, /* bytes 9152 - 9168 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 9168 - 9184 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 9184 - 9200 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 9200 - 9216 */ + 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, /* bytes 9216 - 9232 */ + 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0x48, 0x02, 0xd0, 0x00, 0x48, 0x02, 0xd0, 0x00, 0x98, 0x01, /* bytes 9232 - 9248 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 9248 - 9264 */ + 0x01, 0x60, 0x01, 0x88, 0x02, 0x90, 0x01, 0x88, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, /* bytes 9264 - 9280 */ + 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 9280 - 9296 */ + 0x17, 0x02, 0xc0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0xec, 0x01, 0x90, 0x01, 0xec, 0x01, 0x90, 0x01, /* bytes 9296 - 9312 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, /* bytes 9312 - 9328 */ + 0xe1, 0x01, 0x00, 0x01, 0xe1, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 9328 - 9344 */ + 0x01, 0x02, 0xa0, 0x00, 0x01, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 9344 - 9360 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 9360 - 9376 */ + 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x71, 0x00, 0x88, 0x00, 0x71, 0x00, 0x88, 0x00, /* bytes 9376 - 9392 */ + 0x71, 0x00, 0x08, 0x02, 0x71, 0x00, 0x08, 0x02, 0x71, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 9392 - 9408 */ + 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, /* bytes 9408 - 9424 */ + 0x01, 0x98, 0x01, 0xf0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0xec, 0x01, 0x90, /* bytes 9424 - 9440 */ + 0x01, 0xec, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, 0x01, 0x30, /* bytes 9440 - 9456 */ + 0x01, 0xfa, 0x01, 0x30, 0x01, 0xe1, 0x01, 0x00, 0x01, 0xe1, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, /* bytes 9456 - 9472 */ + 0x00, 0x98, 0x01, 0xd0, 0x00, 0x01, 0x02, 0xa0, 0x00, 0x01, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, /* bytes 9472 - 9488 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, /* bytes 9488 - 9504 */ + 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x71, 0x00, 0x88, 0x00, 0x71, 0x00, 0x88, 0x00, 0x71, 0x00, /* bytes 9504 - 9520 */ + 0x08, 0x02, 0x71, 0x00, 0x08, 0x02, 0x71, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 9520 - 9536 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xe1, 0x01, 0x03, 0x00, 0xc0, /* bytes 9536 - 9552 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x17, 0x02, 0x03, 0x00, /* bytes 9552 - 9568 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0x71, 0x00, 0x03, /* bytes 9568 - 9584 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, /* bytes 9584 - 9600 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, /* bytes 9600 - 9616 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0xcc, 0x00, 0xb8, 0x00, /* bytes 9616 - 9632 */ + 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0x9c, 0x00, 0xb8, /* bytes 9632 - 9648 */ + 0x00, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 9648 - 9664 */ + 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 9664 - 9680 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, /* bytes 9680 - 9696 */ + 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, /* bytes 9696 - 9712 */ + 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0x48, 0x02, 0xd0, 0x00, 0x48, 0x02, 0xd0, 0x00, /* bytes 9712 - 9728 */ + 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0xb5, 0x02, 0x60, /* bytes 9728 - 9744 */ + 0x01, 0xb5, 0x02, 0x60, 0x01, 0x98, 0x01, 0x90, 0x01, 0x98, 0x01, 0x90, 0x01, 0xb5, 0x02, 0x03, /* bytes 9744 - 9760 */ + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, /* bytes 9760 - 9776 */ + 0xf0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0xf0, 0x01, 0x90, 0x01, 0xf0, 0x01, /* bytes 9776 - 9792 */ + 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, /* bytes 9792 - 9808 */ + 0x30, 0x01, 0xe6, 0x01, 0x00, 0x01, 0xe6, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 9808 - 9824 */ + 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, /* bytes 9824 - 9840 */ + 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, /* bytes 9840 - 9856 */ + 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x7a, 0x00, 0x88, 0x00, 0x7b, 0x00, /* bytes 9856 - 9872 */ + 0x88, 0x00, 0x7b, 0x00, 0x08, 0x02, 0x7b, 0x00, 0x08, 0x02, 0x7b, 0x00, 0x08, 0x02, 0xfc, 0x00, /* bytes 9872 - 9888 */ + 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, /* bytes 9888 - 9904 */ + 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0x17, 0x02, 0xc0, 0x01, 0xf0, /* bytes 9904 - 9920 */ + 0x01, 0x90, 0x01, 0xf0, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, /* bytes 9920 - 9936 */ + 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xe6, 0x01, 0x00, 0x01, 0xe6, 0x01, 0x00, 0x01, 0x98, /* bytes 9936 - 9952 */ + 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x98, /* bytes 9952 - 9968 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, 0x00, 0x7b, 0x00, 0x88, 0x00, /* bytes 9968 - 9984 */ + 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x7a, 0x00, 0x88, 0x00, /* bytes 9984 - 10000 */ + 0x7b, 0x00, 0x88, 0x00, 0x7b, 0x00, 0x08, 0x02, 0x7b, 0x00, 0x08, 0x02, 0x7b, 0x00, 0x08, 0x02, /* bytes 10000 - 10016 */ + 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xa5, 0x00, 0xb0, /* bytes 10016 - 10032 */ + 0x01, 0xa5, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, /* bytes 10032 - 10048 */ + 0x30, 0x01, 0xe6, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, /* bytes 10048 - 10064 */ + 0x01, 0xc0, 0x01, 0x17, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, /* bytes 10064 - 10080 */ + 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, /* bytes 10080 - 10096 */ + 0x00, 0xd5, 0x00, 0xb0, 0x01, 0xd5, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10096 - 10112 */ + 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, /* bytes 10112 - 10128 */ + 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, /* bytes 10128 - 10144 */ + 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, /* bytes 10144 - 10160 */ + 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 10160 - 10176 */ + 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0x48, 0x02, 0xd0, /* bytes 10176 - 10192 */ + 0x00, 0x48, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 10192 - 10208 */ + 0x90, 0x01, 0xf9, 0x02, 0x60, 0x01, 0xf9, 0x02, 0x60, 0x01, 0x98, 0x01, 0x90, 0x01, 0x98, 0x01, /* bytes 10208 - 10224 */ + 0x90, 0x01, 0xf9, 0x02, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, /* bytes 10224 - 10240 */ + 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x4a, 0x02, 0xc0, 0x01, 0x4a, 0x02, 0xc0, 0x01, 0xf0, /* bytes 10240 - 10256 */ + 0x01, 0x90, 0x01, 0xf0, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, /* bytes 10256 - 10272 */ + 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xf1, 0x01, 0x00, 0x01, 0xf1, 0x01, 0x00, 0x01, 0x98, /* bytes 10272 - 10288 */ + 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x98, /* bytes 10288 - 10304 */ + 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, /* bytes 10304 - 10320 */ + 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x84, /* bytes 10320 - 10336 */ + 0x00, 0x88, 0x00, 0x85, 0x00, 0x88, 0x00, 0x85, 0x00, 0x08, 0x02, 0x85, 0x00, 0x08, 0x02, 0x85, /* bytes 10336 - 10352 */ + 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 10352 - 10368 */ + 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x4a, 0x02, 0xc0, 0x01, /* bytes 10368 - 10384 */ + 0x4a, 0x02, 0xc0, 0x01, 0xf0, 0x01, 0x90, 0x01, 0xf0, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 10384 - 10400 */ + 0x98, 0x01, 0x60, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xf1, 0x01, 0x00, 0x01, /* bytes 10400 - 10416 */ + 0xf1, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, /* bytes 10416 - 10432 */ + 0x08, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x88, /* bytes 10432 - 10448 */ + 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x84, 0x00, 0x88, /* bytes 10448 - 10464 */ + 0x00, 0x85, 0x00, 0x88, 0x00, 0x85, 0x00, 0x08, 0x02, 0x85, 0x00, 0x08, 0x02, 0x85, 0x00, 0x08, /* bytes 10464 - 10480 */ + 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, /* bytes 10480 - 10496 */ + 0x30, 0x01, 0xf1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, /* bytes 10496 - 10512 */ + 0x01, 0xc0, 0x01, 0x4a, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, /* bytes 10512 - 10528 */ + 0xfb, 0x00, 0x88, 0x00, 0x85, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, /* bytes 10528 - 10544 */ + 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10544 - 10560 */ + 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 10560 - 10576 */ + 0x00, 0xb8, 0x00, 0xaf, 0x00, 0xb0, 0x01, 0xaf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 10576 - 10592 */ + 0x02, 0x00, 0xb8, 0x00, 0xdf, 0x00, 0xb0, 0x01, 0xdf, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10592 - 10608 */ + 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, /* bytes 10608 - 10624 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10624 - 10640 */ + 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 10640 - 10656 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x48, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, /* bytes 10656 - 10672 */ + 0x01, 0xd0, 0x00, 0x48, 0x02, 0x00, 0x01, 0x48, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, /* bytes 10672 - 10688 */ + 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xf8, 0x02, 0x90, 0x01, /* bytes 10688 - 10704 */ + 0xf8, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x1e, 0x00, 0x2c, /* bytes 10704 - 10720 */ + 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, 0x02, 0xc0, /* bytes 10720 - 10736 */ + 0x01, 0xf0, 0x01, 0x90, 0x01, 0xf0, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 10736 - 10752 */ + 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x00, 0x01, 0xf0, 0x01, 0x00, /* bytes 10752 - 10768 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x08, 0x02, 0xa0, /* bytes 10768 - 10784 */ + 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, /* bytes 10784 - 10800 */ + 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, /* bytes 10800 - 10816 */ + 0x00, 0x9c, 0x00, 0x88, 0x00, 0x9a, 0x00, 0x08, 0x02, 0x9a, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, /* bytes 10816 - 10832 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, /* bytes 10832 - 10848 */ + 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0xf0, 0x01, /* bytes 10848 - 10864 */ + 0x90, 0x01, 0xf0, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, 0x01, /* bytes 10864 - 10880 */ + 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x00, 0x01, 0xf0, 0x01, 0x00, 0x01, 0x98, 0x01, /* bytes 10880 - 10896 */ + 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x98, 0x01, /* bytes 10896 - 10912 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x08, 0x00, 0x88, 0x00, 0x9a, 0x00, 0x88, 0x00, 0xfb, /* bytes 10912 - 10928 */ + 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0x9c, 0x00, 0x88, 0x00, 0x9a, /* bytes 10928 - 10944 */ + 0x00, 0x08, 0x02, 0x9a, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 10944 - 10960 */ + 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xf0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 10960 - 10976 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x43, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 10976 - 10992 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 10992 - 11008 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, /* bytes 11008 - 11024 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xf5, 0x00, 0xb0, 0x01, 0xf5, 0x00, 0x03, 0x00, /* bytes 11024 - 11040 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x00, 0xc5, 0x00, 0xb0, 0x01, 0xc5, 0x00, 0x03, /* bytes 11040 - 11056 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, /* bytes 11056 - 11072 */ + 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 11072 - 11088 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0c, /* bytes 11088 - 11104 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, /* bytes 11104 - 11120 */ + 0x98, 0x01, 0x00, 0x01, 0xad, 0x02, 0xd0, 0x00, 0xad, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 11120 - 11136 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 11136 - 11152 */ + 0x01, 0xf8, 0x02, 0x90, 0x01, 0xf8, 0x02, 0x90, 0x01, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, /* bytes 11152 - 11168 */ + 0x00, 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, /* bytes 11168 - 11184 */ + 0xc0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0xf5, 0x01, 0x90, 0x01, 0xf5, 0x01, 0x90, 0x01, 0x98, 0x01, /* bytes 11184 - 11200 */ + 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, /* bytes 11200 - 11216 */ + 0x00, 0x01, 0xfa, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, /* bytes 11216 - 11232 */ + 0xa0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, /* bytes 11232 - 11248 */ + 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, /* bytes 11248 - 11264 */ + 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xa9, 0x00, 0x88, 0x00, 0xa4, 0x00, 0x08, 0x02, 0xa4, 0x00, /* bytes 11264 - 11280 */ + 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 11280 - 11296 */ + 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, /* bytes 11296 - 11312 */ + 0x02, 0xc0, 0x01, 0xf5, 0x01, 0x90, 0x01, 0xf5, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 11312 - 11328 */ + 0x01, 0x60, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x00, 0x01, 0xfa, /* bytes 11328 - 11344 */ + 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x08, 0x02, 0xa0, 0x00, 0x08, /* bytes 11344 - 11360 */ + 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x08, 0x00, 0x88, 0x00, /* bytes 11360 - 11376 */ + 0xa4, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, /* bytes 11376 - 11392 */ + 0xa9, 0x00, 0x88, 0x00, 0xa4, 0x00, 0x08, 0x02, 0xa4, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, /* bytes 11392 - 11408 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x43, 0x02, 0x03, /* bytes 11408 - 11424 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0xfa, 0x01, /* bytes 11424 - 11440 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, /* bytes 11440 - 11456 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, 0xce, 0x00, 0xb8, 0x00, /* bytes 11456 - 11472 */ + 0xce, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, /* bytes 11472 - 11488 */ + 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 11488 - 11504 */ + 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 11504 - 11520 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, /* bytes 11520 - 11536 */ + 0x01, 0x23, 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, /* bytes 11536 - 11552 */ + 0xb8, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x02, 0x00, 0x01, /* bytes 11552 - 11568 */ + 0xb8, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x04, 0x03, 0x60, /* bytes 11568 - 11584 */ + 0x01, 0x04, 0x03, 0x60, 0x01, 0xa4, 0x01, 0x90, 0x01, 0xa4, 0x01, 0x90, 0x01, 0x04, 0x03, 0x03, /* bytes 11584 - 11600 */ + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, /* bytes 11600 - 11616 */ + 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0xf5, 0x01, 0x90, 0x01, 0xf5, 0x01, /* bytes 11616 - 11632 */ + 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfb, 0x01, 0x30, 0x01, 0xfb, 0x01, /* bytes 11632 - 11648 */ + 0x30, 0x01, 0xfa, 0x01, 0x00, 0x01, 0xfa, 0x01, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 11648 - 11664 */ + 0xd0, 0x00, 0x1c, 0x02, 0xa0, 0x00, 0x1c, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, /* bytes 11664 - 11680 */ + 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, /* bytes 11680 - 11696 */ + 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xbc, 0x00, 0x88, 0x00, 0xb5, 0x00, /* bytes 11696 - 11712 */ + 0x88, 0x00, 0xb5, 0x00, 0x08, 0x02, 0xb5, 0x00, 0x08, 0x02, 0xb5, 0x00, 0x08, 0x02, 0xfc, 0x00, /* bytes 11712 - 11728 */ + 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, /* bytes 11728 - 11744 */ + 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0xf5, /* bytes 11744 - 11760 */ + 0x01, 0x90, 0x01, 0xf5, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0xfb, /* bytes 11760 - 11776 */ + 0x01, 0x30, 0x01, 0xfb, 0x01, 0x30, 0x01, 0xfa, 0x01, 0x00, 0x01, 0xfa, 0x01, 0x00, 0x01, 0x98, /* bytes 11776 - 11792 */ + 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1c, 0x02, 0xa0, 0x00, 0x1c, 0x02, 0xa0, 0x00, 0x98, /* bytes 11792 - 11808 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, 0x00, 0xb5, 0x00, 0x88, 0x00, /* bytes 11808 - 11824 */ + 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xbc, 0x00, 0x88, 0x00, /* bytes 11824 - 11840 */ + 0xb5, 0x00, 0x88, 0x00, 0xb5, 0x00, 0x08, 0x02, 0xb5, 0x00, 0x08, 0x02, 0xb5, 0x00, 0x08, 0x02, /* bytes 11840 - 11856 */ + 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, /* bytes 11856 - 11872 */ + 0x01, 0xfa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, /* bytes 11872 - 11888 */ + 0xc0, 0x01, 0x43, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, /* bytes 11888 - 11904 */ + 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x01, /* bytes 11904 - 11920 */ + 0xdc, 0x00, 0xb8, 0x00, 0xdc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, /* bytes 11920 - 11936 */ + 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, /* bytes 11936 - 11952 */ + 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, /* bytes 11952 - 11968 */ + 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, /* bytes 11968 - 11984 */ + 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, /* bytes 11984 - 12000 */ + 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0xb8, 0x02, 0xd0, 0x00, /* bytes 12000 - 12016 */ + 0xb8, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, /* bytes 12016 - 12032 */ + 0x01, 0xce, 0x01, 0x60, 0x01, 0xce, 0x01, 0x60, 0x01, 0x2e, 0x03, 0x90, 0x01, 0x2e, 0x03, 0x90, /* bytes 12032 - 12048 */ + 0x01, 0xce, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 12048 - 12064 */ + 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0xfa, 0x01, /* bytes 12064 - 12080 */ + 0x90, 0x01, 0xfa, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, /* bytes 12080 - 12096 */ + 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x03, 0x02, 0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x98, 0x01, /* bytes 12096 - 12112 */ + 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x98, 0x01, /* bytes 12112 - 12128 */ + 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, /* bytes 12128 - 12144 */ + 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xda, 0x00, /* bytes 12144 - 12160 */ + 0x88, 0x00, 0xd3, 0x00, 0x88, 0x00, 0xd3, 0x00, 0x08, 0x02, 0xd3, 0x00, 0x08, 0x02, 0xd3, 0x00, /* bytes 12160 - 12176 */ + 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 12176 - 12192 */ + 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x43, 0x02, 0xc0, 0x01, 0x43, /* bytes 12192 - 12208 */ + 0x02, 0xc0, 0x01, 0xfa, 0x01, 0x90, 0x01, 0xfa, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, /* bytes 12208 - 12224 */ + 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x03, 0x02, 0x00, 0x01, 0x03, /* bytes 12224 - 12240 */ + 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x1d, /* bytes 12240 - 12256 */ + 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, 0x00, /* bytes 12256 - 12272 */ + 0xd3, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, /* bytes 12272 - 12288 */ + 0xda, 0x00, 0x88, 0x00, 0xd3, 0x00, 0x88, 0x00, 0xd3, 0x00, 0x08, 0x02, 0xd3, 0x00, 0x08, 0x02, /* bytes 12288 - 12304 */ + 0xd3, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 12304 - 12320 */ + 0x01, 0x98, 0x01, 0x30, 0x01, 0x03, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 12320 - 12336 */ + 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x43, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 12336 - 12352 */ + 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 12352 - 12368 */ + 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 12368 - 12384 */ + 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, /* bytes 12384 - 12400 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 12400 - 12416 */ + 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 12416 - 12432 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, 0x00, 0x01, 0xb8, /* bytes 12432 - 12448 */ + 0x02, 0xd0, 0x00, 0xb8, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, /* bytes 12448 - 12464 */ + 0x05, 0x00, 0x90, 0x01, 0x96, 0x03, 0x60, 0x01, 0x96, 0x03, 0x60, 0x01, 0x36, 0x02, 0x90, 0x01, /* bytes 12464 - 12480 */ + 0x36, 0x02, 0x90, 0x01, 0x96, 0x03, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x30, /* bytes 12480 - 12496 */ + 0x01, 0x00, 0x02, 0x30, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, /* bytes 12496 - 12512 */ + 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, /* bytes 12512 - 12528 */ + 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, /* bytes 12528 - 12544 */ + 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xe7, 0x00, 0x88, /* bytes 12544 - 12560 */ + 0x00, 0xe1, 0x00, 0x88, 0x00, 0xe1, 0x00, 0x08, 0x02, 0xe1, 0x00, 0x08, 0x02, 0xe1, 0x00, 0x08, /* bytes 12560 - 12576 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, /* bytes 12576 - 12592 */ + 0x01, 0x6b, 0x02, 0xc0, 0x01, 0x6b, 0x02, 0xc0, 0x01, 0xfa, 0x01, 0x90, 0x01, 0xfa, 0x01, 0x90, /* bytes 12592 - 12608 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x03, /* bytes 12608 - 12624 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6b, 0x02, /* bytes 12624 - 12640 */ + 0xc0, 0x01, 0x6b, 0x02, 0xc0, 0x01, 0xfa, 0x01, 0x90, 0x01, 0xfa, 0x01, 0x90, 0x01, 0x98, 0x01, /* bytes 12640 - 12656 */ + 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x08, 0x02, /* bytes 12656 - 12672 */ + 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, 0x02, /* bytes 12672 - 12688 */ + 0xa0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, /* bytes 12688 - 12704 */ + 0x00, 0x88, 0x00, 0xe1, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, /* bytes 12704 - 12720 */ + 0x00, 0x88, 0x00, 0xe7, 0x00, 0x88, 0x00, 0xe1, 0x00, 0x88, 0x00, 0xe1, 0x00, 0x08, 0x02, 0xe1, /* bytes 12720 - 12736 */ + 0x00, 0x08, 0x02, 0xe1, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 12736 - 12752 */ + 0x02, 0x00, 0x30, 0x01, 0x08, 0x02, 0x30, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 12752 - 12768 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x6b, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 12768 - 12784 */ + 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 12784 - 12800 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, /* bytes 12800 - 12816 */ + 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, /* bytes 12816 - 12832 */ + 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 12832 - 12848 */ + 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0b, 0x00, 0x03, /* bytes 12848 - 12864 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x98, 0x01, 0x00, 0x01, 0x98, 0x01, /* bytes 12864 - 12880 */ + 0x00, 0x01, 0xb8, 0x02, 0xd0, 0x00, 0xb8, 0x02, 0xd0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 12880 - 12896 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0xd6, 0x02, 0x60, 0x01, 0xd6, 0x02, 0x60, 0x01, 0x36, /* bytes 12896 - 12912 */ + 0x04, 0x90, 0x01, 0x36, 0x04, 0x90, 0x01, 0xd6, 0x02, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, /* bytes 12912 - 12928 */ + 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, 0x02, 0xc0, 0x01, /* bytes 12928 - 12944 */ + 0x6f, 0x02, 0xc0, 0x01, 0x00, 0x02, 0x90, 0x01, 0x00, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 12944 - 12960 */ + 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x0d, 0x02, 0x00, 0x01, /* bytes 12960 - 12976 */ + 0x0d, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, 0x02, 0xa0, 0x00, /* bytes 12976 - 12992 */ + 0x1d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, /* bytes 12992 - 13008 */ + 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, /* bytes 13008 - 13024 */ + 0xfa, 0x00, 0x88, 0x00, 0xf3, 0x00, 0x88, 0x00, 0xef, 0x00, 0x08, 0x02, 0xef, 0x00, 0x08, 0x02, /* bytes 13024 - 13040 */ + 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 13040 - 13056 */ + 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0x6f, 0x02, 0xc0, /* bytes 13056 - 13072 */ + 0x01, 0x00, 0x02, 0x90, 0x01, 0x00, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, /* bytes 13072 - 13088 */ + 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x0d, 0x02, 0x00, 0x01, 0x0d, 0x02, 0x00, /* bytes 13088 - 13104 */ + 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x1d, 0x02, 0xa0, /* bytes 13104 - 13120 */ + 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x08, 0x00, 0x88, 0x00, 0xef, 0x00, /* bytes 13120 - 13136 */ + 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xf3, 0x00, /* bytes 13136 - 13152 */ + 0x88, 0x00, 0xef, 0x00, 0x08, 0x02, 0xef, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 13152 - 13168 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x0d, 0x02, 0x03, 0x00, 0xc0, /* bytes 13168 - 13184 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x6f, 0x02, 0x03, 0x00, /* bytes 13184 - 13200 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, /* bytes 13200 - 13216 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, /* bytes 13216 - 13232 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, /* bytes 13232 - 13248 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 13248 - 13264 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, /* bytes 13264 - 13280 */ + 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0xb8, 0x02, 0x00, /* bytes 13280 - 13296 */ + 0x01, 0xb8, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0xb8, 0x02, 0x03, /* bytes 13296 - 13312 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x90, 0x01, 0x97, 0x03, 0x60, 0x01, 0x97, 0x03, /* bytes 13312 - 13328 */ + 0x60, 0x01, 0xf7, 0x04, 0x90, 0x01, 0xf7, 0x04, 0x90, 0x01, 0x97, 0x03, 0x03, 0x00, 0x00, 0x00, /* bytes 13328 - 13344 */ + 0xff, 0x00, 0x00, 0x1f, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, /* bytes 13344 - 13360 */ + 0x02, 0xc0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0xff, 0x01, 0x90, 0x01, 0xff, 0x01, 0x90, 0x01, 0x98, /* bytes 13360 - 13376 */ + 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x17, /* bytes 13376 - 13392 */ + 0x02, 0x00, 0x01, 0x17, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x1d, /* bytes 13392 - 13408 */ + 0x02, 0xa0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, /* bytes 13408 - 13424 */ + 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, /* bytes 13424 - 13440 */ + 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xff, /* bytes 13440 - 13456 */ + 0x00, 0x08, 0x02, 0xff, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, /* bytes 13456 - 13472 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 13472 - 13488 */ + 0x6f, 0x02, 0xc0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0xff, 0x01, 0x90, 0x01, 0xff, 0x01, 0x90, 0x01, /* bytes 13488 - 13504 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, /* bytes 13504 - 13520 */ + 0x17, 0x02, 0x00, 0x01, 0x17, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 13520 - 13536 */ + 0x1d, 0x02, 0xa0, 0x00, 0x1d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 13536 - 13552 */ + 0x00, 0x08, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, /* bytes 13552 - 13568 */ + 0x00, 0xff, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xff, 0x00, 0x08, 0x02, 0xff, 0x00, 0x08, /* bytes 13568 - 13584 */ + 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x17, 0x02, /* bytes 13584 - 13600 */ + 0x30, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, /* bytes 13600 - 13616 */ + 0x01, 0xc0, 0x01, 0x6f, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, /* bytes 13616 - 13632 */ + 0xfb, 0x00, 0x88, 0x00, 0xff, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, /* bytes 13632 - 13648 */ + 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 13648 - 13664 */ + 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, /* bytes 13664 - 13680 */ + 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, /* bytes 13680 - 13696 */ + 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, /* bytes 13696 - 13712 */ + 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 13712 - 13728 */ + 0x00, 0x05, 0x00, 0xd0, 0x00, 0xd1, 0x01, 0x00, 0x01, 0xd1, 0x01, 0x00, 0x01, 0xf1, 0x02, 0xd0, /* bytes 13728 - 13744 */ + 0x00, 0xf1, 0x02, 0xd0, 0x00, 0xd1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 13744 - 13760 */ + 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, /* bytes 13760 - 13776 */ + 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, /* bytes 13776 - 13792 */ + 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0xff, /* bytes 13792 - 13808 */ + 0x01, 0x90, 0x01, 0xff, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, /* bytes 13808 - 13824 */ + 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x16, 0x02, 0x00, 0x01, 0x16, 0x02, 0x00, 0x01, 0x98, /* bytes 13824 - 13840 */ + 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x36, 0x02, 0xa0, 0x00, 0x36, 0x02, 0xa0, 0x00, 0x98, /* bytes 13840 - 13856 */ + 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, /* bytes 13856 - 13872 */ + 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, /* bytes 13872 - 13888 */ + 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0x00, /* bytes 13888 - 13904 */ + 0x01, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, /* bytes 13904 - 13920 */ + 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, 0x02, 0xc0, 0x01, /* bytes 13920 - 13936 */ + 0x6f, 0x02, 0xc0, 0x01, 0xff, 0x01, 0x90, 0x01, 0xff, 0x01, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 13936 - 13952 */ + 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x16, 0x02, 0x00, 0x01, /* bytes 13952 - 13968 */ + 0x16, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x36, 0x02, 0xa0, 0x00, /* bytes 13968 - 13984 */ + 0x36, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, /* bytes 13984 - 14000 */ + 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, /* bytes 14000 - 14016 */ + 0x00, 0xff, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, /* bytes 14016 - 14032 */ + 0x02, 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 14032 - 14048 */ + 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x6f, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 14048 - 14064 */ + 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x16, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 14064 - 14080 */ + 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 14080 - 14096 */ + 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 14096 - 14112 */ + 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, /* bytes 14112 - 14128 */ + 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 14128 - 14144 */ + 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, /* bytes 14144 - 14160 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x00, 0x20, 0x03, 0x00, 0x01, 0x20, 0x03, 0x00, 0x01, /* bytes 14160 - 14176 */ + 0x00, 0x02, 0xd0, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x20, 0x03, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 14176 - 14192 */ + 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, /* bytes 14192 - 14208 */ + 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, /* bytes 14208 - 14224 */ + 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0x6f, 0x02, /* bytes 14224 - 14240 */ + 0xc0, 0x01, 0x04, 0x02, 0x90, 0x01, 0x04, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 14240 - 14256 */ + 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x20, 0x02, 0x00, 0x01, 0x20, 0x02, /* bytes 14256 - 14272 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, 0x02, 0xa0, 0x00, 0x33, 0x02, /* bytes 14272 - 14288 */ + 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, /* bytes 14288 - 14304 */ + 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, /* bytes 14304 - 14320 */ + 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, /* bytes 14320 - 14336 */ + 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, /* bytes 14336 - 14352 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x6f, /* bytes 14352 - 14368 */ + 0x02, 0xc0, 0x01, 0x6f, 0x02, 0xc0, 0x01, 0x04, 0x02, 0x90, 0x01, 0x04, 0x02, 0x90, 0x01, 0x98, /* bytes 14368 - 14384 */ + 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x20, /* bytes 14384 - 14400 */ + 0x02, 0x00, 0x01, 0x20, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, /* bytes 14400 - 14416 */ + 0x02, 0xa0, 0x00, 0x33, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 14416 - 14432 */ + 0x0a, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, /* bytes 14432 - 14448 */ + 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, /* bytes 14448 - 14464 */ + 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 14464 - 14480 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x6f, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 14480 - 14496 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 14496 - 14512 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, /* bytes 14512 - 14528 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 14528 - 14544 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 14544 - 14560 */ + 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 14560 - 14576 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0c, 0x00, /* bytes 14576 - 14592 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x78, 0x03, 0x00, 0x01, 0x58, /* bytes 14592 - 14608 */ + 0x02, 0xd0, 0x00, 0x58, 0x02, 0xd0, 0x00, 0x78, 0x03, 0x00, 0x01, 0x78, 0x03, 0x03, 0x00, 0xc0, /* bytes 14608 - 14624 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, 0x05, 0x90, 0x01, /* bytes 14624 - 14640 */ + 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, /* bytes 14640 - 14656 */ + 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x8c, 0x02, 0xc0, /* bytes 14656 - 14672 */ + 0x01, 0x8c, 0x02, 0xc0, 0x01, 0x04, 0x02, 0x90, 0x01, 0x04, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 14672 - 14688 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x20, 0x02, 0x00, /* bytes 14688 - 14704 */ + 0x01, 0x20, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, 0x02, 0xa0, /* bytes 14704 - 14720 */ + 0x00, 0x33, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 14720 - 14736 */ + 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, /* bytes 14736 - 14752 */ + 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfe, 0x00, 0x08, 0x02, 0xfe, 0x00, 0x08, /* bytes 14752 - 14768 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 14768 - 14784 */ + 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x8c, 0x02, 0xc0, 0x01, 0x8c, 0x02, /* bytes 14784 - 14800 */ + 0xc0, 0x01, 0x04, 0x02, 0x90, 0x01, 0x04, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 14800 - 14816 */ + 0x60, 0x01, 0x00, 0x02, 0x30, 0x01, 0x00, 0x02, 0x30, 0x01, 0x20, 0x02, 0x00, 0x01, 0x20, 0x02, /* bytes 14816 - 14832 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, 0x02, 0xa0, 0x00, 0x33, 0x02, /* bytes 14832 - 14848 */ + 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x07, 0x00, 0x88, 0x00, 0xfb, /* bytes 14848 - 14864 */ + 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfe, /* bytes 14864 - 14880 */ + 0x00, 0x08, 0x02, 0xfe, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 14880 - 14896 */ + 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfe, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 14896 - 14912 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x8c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 14912 - 14928 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x20, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 14928 - 14944 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, /* bytes 14944 - 14960 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 14960 - 14976 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 14976 - 14992 */ + 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 14992 - 15008 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0c, 0x00, /* bytes 15008 - 15024 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0xf6, 0x03, 0x00, 0x01, 0xd6, /* bytes 15024 - 15040 */ + 0x02, 0xd0, 0x00, 0xd6, 0x02, 0xd0, 0x00, 0xf6, 0x03, 0x00, 0x01, 0xf6, 0x03, 0x03, 0x00, 0xc0, /* bytes 15040 - 15056 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, 0x05, 0x90, 0x01, /* bytes 15056 - 15072 */ + 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, /* bytes 15072 - 15088 */ + 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x9b, 0x02, 0xc0, /* bytes 15088 - 15104 */ + 0x01, 0x9b, 0x02, 0xc0, 0x01, 0x09, 0x02, 0x90, 0x01, 0x09, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 15104 - 15120 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x03, 0x02, 0x30, 0x01, 0x03, 0x02, 0x30, 0x01, 0x2a, 0x02, 0x00, /* bytes 15120 - 15136 */ + 0x01, 0x2a, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, 0x02, 0xa0, /* bytes 15136 - 15152 */ + 0x00, 0x33, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 15152 - 15168 */ + 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, /* bytes 15168 - 15184 */ + 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfc, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x08, /* bytes 15184 - 15200 */ + 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 15200 - 15216 */ + 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x9b, 0x02, 0xc0, 0x01, 0x9b, 0x02, /* bytes 15216 - 15232 */ + 0xc0, 0x01, 0x09, 0x02, 0x90, 0x01, 0x09, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, /* bytes 15232 - 15248 */ + 0x60, 0x01, 0x03, 0x02, 0x30, 0x01, 0x03, 0x02, 0x30, 0x01, 0x2a, 0x02, 0x00, 0x01, 0x2a, 0x02, /* bytes 15248 - 15264 */ + 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, 0x02, 0xa0, 0x00, 0x33, 0x02, /* bytes 15264 - 15280 */ + 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x07, 0x00, 0x88, 0x00, 0xfb, /* bytes 15280 - 15296 */ + 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfc, /* bytes 15296 - 15312 */ + 0x00, 0x08, 0x02, 0xfc, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 15312 - 15328 */ + 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x2a, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 15328 - 15344 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x9b, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 15344 - 15360 */ + 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 15360 - 15376 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, /* bytes 15376 - 15392 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 15392 - 15408 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 15408 - 15424 */ + 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 15424 - 15440 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0c, 0x00, /* bytes 15440 - 15456 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x73, 0x03, 0xd0, 0x00, 0x73, /* bytes 15456 - 15472 */ + 0x03, 0xd0, 0x00, 0x93, 0x04, 0x00, 0x01, 0x93, 0x04, 0x00, 0x01, 0x73, 0x03, 0x03, 0x00, 0xc0, /* bytes 15472 - 15488 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, 0x05, 0x90, 0x01, /* bytes 15488 - 15504 */ + 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, /* bytes 15504 - 15520 */ + 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x9a, 0x02, 0xc0, /* bytes 15520 - 15536 */ + 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x09, 0x02, 0x90, 0x01, 0x09, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 15536 - 15552 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, 0x2a, 0x02, 0x00, /* bytes 15552 - 15568 */ + 0x01, 0x2a, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x33, 0x02, 0xa0, /* bytes 15568 - 15584 */ + 0x00, 0x33, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 15584 - 15600 */ + 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, /* bytes 15600 - 15616 */ + 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x08, /* bytes 15616 - 15632 */ + 0x02, 0xf9, 0x00, 0x08, 0x02, 0xf9, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, /* bytes 15632 - 15648 */ + 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, /* bytes 15648 - 15664 */ + 0xf0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x09, 0x02, 0x90, 0x01, 0x09, 0x02, /* bytes 15664 - 15680 */ + 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, /* bytes 15680 - 15696 */ + 0x30, 0x01, 0x2a, 0x02, 0x00, 0x01, 0x2a, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 15696 - 15712 */ + 0xd0, 0x00, 0x33, 0x02, 0xa0, 0x00, 0x33, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 15712 - 15728 */ + 0x00, 0x01, 0x00, 0x09, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, /* bytes 15728 - 15744 */ + 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x08, 0x02, 0xf9, /* bytes 15744 - 15760 */ + 0x00, 0x08, 0x02, 0xf9, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 15760 - 15776 */ + 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 15776 - 15792 */ + 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x9a, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 15792 - 15808 */ + 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x2a, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 15808 - 15824 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, /* bytes 15824 - 15840 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, /* bytes 15840 - 15856 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 15856 - 15872 */ + 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 15872 - 15888 */ + 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, 0x00, 0x0b, 0x00, /* bytes 15888 - 15904 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x28, 0x05, 0x00, 0x01, 0x08, /* bytes 15904 - 15920 */ + 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x28, 0x05, 0x00, 0x01, 0x28, 0x05, 0x03, 0x00, 0xc0, /* bytes 15920 - 15936 */ + 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, 0x05, 0x90, 0x01, /* bytes 15936 - 15952 */ + 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x00, /* bytes 15952 - 15968 */ + 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0x9a, 0x02, 0xc0, /* bytes 15968 - 15984 */ + 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x0b, 0x02, 0x90, 0x01, 0x0b, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 15984 - 16000 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, 0x33, 0x02, 0x00, /* bytes 16000 - 16016 */ + 0x01, 0x33, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x4a, 0x02, 0xa0, /* bytes 16016 - 16032 */ + 0x00, 0x4a, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, /* bytes 16032 - 16048 */ + 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, /* bytes 16048 - 16064 */ + 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xf8, 0x00, 0x88, 0x00, 0xf8, 0x00, 0x08, /* bytes 16064 - 16080 */ + 0x02, 0xf8, 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, /* bytes 16080 - 16096 */ + 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, /* bytes 16096 - 16112 */ + 0xf0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x0b, 0x02, 0x90, 0x01, 0x0b, 0x02, /* bytes 16112 - 16128 */ + 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, /* bytes 16128 - 16144 */ + 0x30, 0x01, 0x33, 0x02, 0x00, 0x01, 0x33, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, /* bytes 16144 - 16160 */ + 0xd0, 0x00, 0x4a, 0x02, 0xa0, 0x00, 0x4a, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 16160 - 16176 */ + 0x00, 0x01, 0x00, 0x0a, 0x00, 0x88, 0x00, 0xf8, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, /* bytes 16176 - 16192 */ + 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xf8, 0x00, 0x88, 0x00, 0xf8, /* bytes 16192 - 16208 */ + 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, /* bytes 16208 - 16224 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x33, 0x02, 0x03, 0x00, /* bytes 16224 - 16240 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x9a, 0x02, 0x03, /* bytes 16240 - 16256 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, /* bytes 16256 - 16272 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, /* bytes 16272 - 16288 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 16288 - 16304 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 16304 - 16320 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 16320 - 16336 */ + 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x28, 0x05, /* bytes 16336 - 16352 */ + 0x00, 0x01, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x28, 0x05, 0x00, 0x01, 0x28, 0x05, /* bytes 16352 - 16368 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, /* bytes 16368 - 16384 */ + 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, /* bytes 16384 - 16400 */ + 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 16400 - 16416 */ + 0x9a, 0x02, 0xc0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x0e, 0x02, 0x90, 0x01, 0x0e, 0x02, 0x90, 0x01, /* bytes 16416 - 16432 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, /* bytes 16432 - 16448 */ + 0x38, 0x02, 0x00, 0x01, 0x38, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 16448 - 16464 */ + 0x48, 0x02, 0xa0, 0x00, 0x48, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 16464 - 16480 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 16480 - 16496 */ + 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x88, 0x00, /* bytes 16496 - 16512 */ + 0xf9, 0x00, 0x08, 0x02, 0xf9, 0x00, 0x08, 0x02, 0xf9, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 16512 - 16528 */ + 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, /* bytes 16528 - 16544 */ + 0x01, 0x98, 0x01, 0xf0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x9a, 0x02, 0xc0, 0x01, 0x0e, 0x02, 0x90, /* bytes 16544 - 16560 */ + 0x01, 0x0e, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, /* bytes 16560 - 16576 */ + 0x01, 0x05, 0x02, 0x30, 0x01, 0x38, 0x02, 0x00, 0x01, 0x38, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, /* bytes 16576 - 16592 */ + 0x00, 0x98, 0x01, 0xd0, 0x00, 0x48, 0x02, 0xa0, 0x00, 0x48, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, /* bytes 16592 - 16608 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, /* bytes 16608 - 16624 */ + 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x88, 0x00, 0xf9, 0x00, /* bytes 16624 - 16640 */ + 0x08, 0x02, 0xf9, 0x00, 0x08, 0x02, 0xf9, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 16640 - 16656 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x38, 0x02, 0x30, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, /* bytes 16656 - 16672 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0x9a, 0x02, 0x03, 0x00, /* bytes 16672 - 16688 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xf9, 0x00, 0x03, /* bytes 16688 - 16704 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, /* bytes 16704 - 16720 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, /* bytes 16720 - 16736 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 16736 - 16752 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 16752 - 16768 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 16768 - 16784 */ + 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x28, 0x05, /* bytes 16784 - 16800 */ + 0x00, 0x01, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x28, 0x05, 0x00, 0x01, 0x28, 0x05, /* bytes 16800 - 16816 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, /* bytes 16816 - 16832 */ + 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, /* bytes 16832 - 16848 */ + 0x00, 0xff, 0x00, 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 16848 - 16864 */ + 0xad, 0x02, 0xc0, 0x01, 0xad, 0x02, 0xc0, 0x01, 0x0e, 0x02, 0x90, 0x01, 0x0e, 0x02, 0x90, 0x01, /* bytes 16864 - 16880 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, /* bytes 16880 - 16896 */ + 0x3d, 0x02, 0x00, 0x01, 0x3d, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 16896 - 16912 */ + 0x48, 0x02, 0xa0, 0x00, 0x48, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 16912 - 16928 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 16928 - 16944 */ + 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x08, 0x02, /* bytes 16944 - 16960 */ + 0xfb, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 16960 - 16976 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xad, 0x02, 0xc0, /* bytes 16976 - 16992 */ + 0x01, 0xad, 0x02, 0xc0, 0x01, 0x0e, 0x02, 0x90, 0x01, 0x0e, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 16992 - 17008 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, 0x3d, 0x02, 0x00, /* bytes 17008 - 17024 */ + 0x01, 0x3d, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x48, 0x02, 0xa0, /* bytes 17024 - 17040 */ + 0x00, 0x48, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x07, 0x00, /* bytes 17040 - 17056 */ + 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, /* bytes 17056 - 17072 */ + 0x88, 0x00, 0xfb, 0x00, 0x08, 0x02, 0xfb, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 17072 - 17088 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, /* bytes 17088 - 17104 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x3d, 0x02, 0x03, 0x00, /* bytes 17104 - 17120 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xad, 0x02, 0x03, /* bytes 17120 - 17136 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, /* bytes 17136 - 17152 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, /* bytes 17152 - 17168 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 17168 - 17184 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 17184 - 17200 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 17200 - 17216 */ + 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x28, 0x05, /* bytes 17216 - 17232 */ + 0x00, 0x01, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x28, 0x05, 0x00, 0x01, 0x28, 0x05, /* bytes 17232 - 17248 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, /* bytes 17248 - 17264 */ + 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, /* bytes 17264 - 17280 */ + 0x00, 0xff, 0x00, 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 17280 - 17296 */ + 0xc7, 0x02, 0xc0, 0x01, 0xc7, 0x02, 0xc0, 0x01, 0x13, 0x02, 0x90, 0x01, 0x13, 0x02, 0x90, 0x01, /* bytes 17296 - 17312 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, /* bytes 17312 - 17328 */ + 0x47, 0x02, 0x00, 0x01, 0x47, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 17328 - 17344 */ + 0x48, 0x02, 0xa0, 0x00, 0x48, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 17344 - 17360 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 17360 - 17376 */ + 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0xfe, 0x00, 0x08, 0x02, /* bytes 17376 - 17392 */ + 0xfe, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 17392 - 17408 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc7, 0x02, 0xc0, /* bytes 17408 - 17424 */ + 0x01, 0xc7, 0x02, 0xc0, 0x01, 0x13, 0x02, 0x90, 0x01, 0x13, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 17424 - 17440 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, 0x47, 0x02, 0x00, /* bytes 17440 - 17456 */ + 0x01, 0x47, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x48, 0x02, 0xa0, /* bytes 17456 - 17472 */ + 0x00, 0x48, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x07, 0x00, /* bytes 17472 - 17488 */ + 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, /* bytes 17488 - 17504 */ + 0x88, 0x00, 0xfe, 0x00, 0x08, 0x02, 0xfe, 0x00, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 17504 - 17520 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0xc7, 0x02, 0xc0, 0x01, 0x98, 0x01, 0x03, 0x00, 0xc0, /* bytes 17520 - 17536 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x47, 0x02, 0x03, 0x00, /* bytes 17536 - 17552 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfe, 0x00, 0x03, /* bytes 17552 - 17568 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, /* bytes 17568 - 17584 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, /* bytes 17584 - 17600 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 17600 - 17616 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 17616 - 17632 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 17632 - 17648 */ + 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x28, 0x05, /* bytes 17648 - 17664 */ + 0x00, 0x01, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x28, 0x05, 0x00, 0x01, 0x28, 0x05, /* bytes 17664 - 17680 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, /* bytes 17680 - 17696 */ + 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, /* bytes 17696 - 17712 */ + 0x00, 0xff, 0x00, 0x00, 0x20, 0x00, 0x90, 0x01, 0x13, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, /* bytes 17712 - 17728 */ + 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, 0x01, 0x05, 0x02, 0x30, 0x01, 0x46, 0x02, 0x00, 0x01, /* bytes 17728 - 17744 */ + 0x46, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x5a, 0x02, 0xa0, 0x00, /* bytes 17744 - 17760 */ + 0x5a, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, /* bytes 17760 - 17776 */ + 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, /* bytes 17776 - 17792 */ + 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, /* bytes 17792 - 17808 */ + 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, /* bytes 17808 - 17824 */ + 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc6, 0x02, 0xc0, 0x01, 0xc6, 0x02, 0xc0, 0x01, /* bytes 17824 - 17840 */ + 0x13, 0x02, 0x90, 0x01, 0x13, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, /* bytes 17840 - 17856 */ + 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc6, 0x02, 0xc0, 0x01, 0xc6, 0x02, 0xc0, 0x01, 0x13, 0x02, 0x90, /* bytes 17856 - 17872 */ + 0x01, 0x13, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x05, 0x02, 0x30, /* bytes 17872 - 17888 */ + 0x01, 0x05, 0x02, 0x30, 0x01, 0x46, 0x02, 0x00, 0x01, 0x46, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, /* bytes 17888 - 17904 */ + 0x00, 0x98, 0x01, 0xd0, 0x00, 0x5a, 0x02, 0xa0, 0x00, 0x5a, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, /* bytes 17904 - 17920 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xff, 0x00, /* bytes 17920 - 17936 */ + 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0x00, 0x01, /* bytes 17936 - 17952 */ + 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 17952 - 17968 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc6, 0x02, 0x03, 0x00, 0xc0, /* bytes 17968 - 17984 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, 0x46, 0x02, 0x03, 0x00, /* bytes 17984 - 18000 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, 0x70, 0x00, 0x98, 0x01, 0x03, /* bytes 18000 - 18016 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, /* bytes 18016 - 18032 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, /* bytes 18032 - 18048 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x3c, 0x00, /* bytes 18048 - 18064 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 18064 - 18080 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, 0x34, 0x01, 0x23, /* bytes 18080 - 18096 */ + 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x28, 0x05, /* bytes 18096 - 18112 */ + 0x00, 0x01, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x28, 0x05, 0x00, 0x01, 0x28, 0x05, /* bytes 18112 - 18128 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x01, 0x08, 0x04, 0x60, 0x01, 0x68, /* bytes 18128 - 18144 */ + 0x05, 0x90, 0x01, 0x68, 0x05, 0x90, 0x01, 0x08, 0x04, 0x60, 0x01, 0x08, 0x04, 0x03, 0x00, 0x00, /* bytes 18144 - 18160 */ + 0x00, 0xff, 0x00, 0x00, 0x1e, 0x00, 0x2c, 0x02, 0x94, 0x01, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, /* bytes 18160 - 18176 */ + 0xc6, 0x02, 0xc0, 0x01, 0xc6, 0x02, 0xc0, 0x01, 0x18, 0x02, 0x90, 0x01, 0x18, 0x02, 0x90, 0x01, /* bytes 18176 - 18192 */ + 0x98, 0x01, 0x60, 0x01, 0x98, 0x01, 0x60, 0x01, 0x07, 0x02, 0x30, 0x01, 0x07, 0x02, 0x30, 0x01, /* bytes 18192 - 18208 */ + 0x50, 0x02, 0x00, 0x01, 0x50, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, /* bytes 18208 - 18224 */ + 0x5d, 0x02, 0xa0, 0x00, 0x5d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x70, 0x00, 0x98, 0x01, 0x3c, 0x00, /* bytes 18224 - 18240 */ + 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x70, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, /* bytes 18240 - 18256 */ + 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, /* bytes 18256 - 18272 */ + 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, 0x01, 0x03, 0x00, /* bytes 18272 - 18288 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0xf0, 0x01, 0x98, 0x01, 0xf0, 0x01, 0xc6, 0x02, 0xc0, /* bytes 18288 - 18304 */ + 0x01, 0xc6, 0x02, 0xc0, 0x01, 0x18, 0x02, 0x90, 0x01, 0x18, 0x02, 0x90, 0x01, 0x98, 0x01, 0x60, /* bytes 18304 - 18320 */ + 0x01, 0x98, 0x01, 0x60, 0x01, 0x07, 0x02, 0x30, 0x01, 0x07, 0x02, 0x30, 0x01, 0x50, 0x02, 0x00, /* bytes 18320 - 18336 */ + 0x01, 0x50, 0x02, 0x00, 0x01, 0x98, 0x01, 0xd0, 0x00, 0x98, 0x01, 0xd0, 0x00, 0x5d, 0x02, 0xa0, /* bytes 18336 - 18352 */ + 0x00, 0x5d, 0x02, 0xa0, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x08, 0x00, /* bytes 18352 - 18368 */ + 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfb, 0x00, 0x88, 0x00, 0xfa, 0x00, /* bytes 18368 - 18384 */ + 0x88, 0x00, 0xff, 0x00, 0x88, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x01, 0x08, 0x02, 0xfc, 0x00, /* bytes 18384 - 18400 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x01, 0x98, 0x01, 0xc0, 0x01, 0xc6, /* bytes 18400 - 18416 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x98, 0x01, 0x30, 0x01, /* bytes 18416 - 18432 */ + 0x50, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x01, 0x88, /* bytes 18432 - 18448 */ + 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x01, /* bytes 18448 - 18464 */ + 0x70, 0x00, 0x98, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x05, 0x00, 0x2c, 0x02, 0x94, /* bytes 18464 - 18480 */ + 0x01, 0x3c, 0x00, 0x94, 0x01, 0x3c, 0x00, 0xfc, 0x00, 0x2c, 0x02, 0xfc, 0x00, 0x2c, 0x02, 0x94, /* bytes 18480 - 18496 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x01, 0x34, 0x01, 0xf2, 0x01, /* bytes 18496 - 18512 */ + 0x34, 0x01, +}; +__attribute__ ((aligned (8))) +static const uint8_t RESULT_DISMISSED_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x5e, 0x14, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x1f, 0x00, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, /* bytes 16 - 32 */ + 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, /* bytes 32 - 48 */ + 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, /* bytes 48 - 64 */ + 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, /* bytes 64 - 80 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, /* bytes 80 - 96 */ + 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, /* bytes 96 - 112 */ + 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, /* bytes 112 - 128 */ + 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, /* bytes 128 - 144 */ + 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, /* bytes 144 - 160 */ + 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, /* bytes 160 - 176 */ + 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 176 - 192 */ + 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 192 - 208 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 208 - 224 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, /* bytes 224 - 240 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, /* bytes 240 - 256 */ + 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, /* bytes 256 - 272 */ + 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, /* bytes 272 - 288 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, /* bytes 288 - 304 */ + 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 304 - 320 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, /* bytes 320 - 336 */ + 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 336 - 352 */ + 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, /* bytes 352 - 368 */ + 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, /* bytes 368 - 384 */ + 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, /* bytes 384 - 400 */ + 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, /* bytes 400 - 416 */ + 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, /* bytes 416 - 432 */ + 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 432 - 448 */ + 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 448 - 464 */ + 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 464 - 480 */ + 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, /* bytes 480 - 496 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, /* bytes 496 - 512 */ + 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 512 - 528 */ + 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, /* bytes 528 - 544 */ + 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, /* bytes 544 - 560 */ + 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, /* bytes 560 - 576 */ + 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, /* bytes 576 - 592 */ + 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, /* bytes 592 - 608 */ + 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, /* bytes 608 - 624 */ + 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, /* bytes 624 - 640 */ + 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, /* bytes 640 - 656 */ + 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, /* bytes 656 - 672 */ + 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, /* bytes 672 - 688 */ + 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, /* bytes 688 - 704 */ + 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, /* bytes 704 - 720 */ + 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 720 - 736 */ + 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, /* bytes 736 - 752 */ + 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 752 - 768 */ + 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, /* bytes 768 - 784 */ + 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, /* bytes 784 - 800 */ + 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, /* bytes 800 - 816 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, /* bytes 816 - 832 */ + 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, /* bytes 832 - 848 */ + 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, /* bytes 848 - 864 */ + 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, /* bytes 864 - 880 */ + 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, /* bytes 880 - 896 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, /* bytes 896 - 912 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, /* bytes 912 - 928 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, /* bytes 928 - 944 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, /* bytes 944 - 960 */ + 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, /* bytes 960 - 976 */ + 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, /* bytes 976 - 992 */ + 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, /* bytes 992 - 1008 */ + 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, /* bytes 1008 - 1024 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, /* bytes 1024 - 1040 */ + 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, /* bytes 1040 - 1056 */ + 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, /* bytes 1056 - 1072 */ + 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, /* bytes 1072 - 1088 */ + 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, /* bytes 1088 - 1104 */ + 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, /* bytes 1104 - 1120 */ + 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1120 - 1136 */ + 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1136 - 1152 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 1152 - 1168 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, /* bytes 1168 - 1184 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, /* bytes 1184 - 1200 */ + 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, /* bytes 1200 - 1216 */ + 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, /* bytes 1216 - 1232 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, /* bytes 1232 - 1248 */ + 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 1248 - 1264 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, /* bytes 1264 - 1280 */ + 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 1280 - 1296 */ + 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, /* bytes 1296 - 1312 */ + 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, /* bytes 1312 - 1328 */ + 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, /* bytes 1328 - 1344 */ + 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, /* bytes 1344 - 1360 */ + 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, /* bytes 1360 - 1376 */ + 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1376 - 1392 */ + 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 1392 - 1408 */ + 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1408 - 1424 */ + 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, /* bytes 1424 - 1440 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, /* bytes 1440 - 1456 */ + 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 1456 - 1472 */ + 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, /* bytes 1472 - 1488 */ + 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, /* bytes 1488 - 1504 */ + 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, /* bytes 1504 - 1520 */ + 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, /* bytes 1520 - 1536 */ + 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, /* bytes 1536 - 1552 */ + 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, /* bytes 1552 - 1568 */ + 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, /* bytes 1568 - 1584 */ + 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, /* bytes 1584 - 1600 */ + 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, /* bytes 1600 - 1616 */ + 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, /* bytes 1616 - 1632 */ + 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, /* bytes 1632 - 1648 */ + 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, /* bytes 1648 - 1664 */ + 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 1664 - 1680 */ + 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, /* bytes 1680 - 1696 */ + 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 1696 - 1712 */ + 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, /* bytes 1712 - 1728 */ + 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, /* bytes 1728 - 1744 */ + 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, /* bytes 1744 - 1760 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, /* bytes 1760 - 1776 */ + 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, /* bytes 1776 - 1792 */ + 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, /* bytes 1792 - 1808 */ + 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, /* bytes 1808 - 1824 */ + 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, /* bytes 1824 - 1840 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, /* bytes 1840 - 1856 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, /* bytes 1856 - 1872 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, /* bytes 1872 - 1888 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, /* bytes 1888 - 1904 */ + 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, /* bytes 1904 - 1920 */ + 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, /* bytes 1920 - 1936 */ + 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, /* bytes 1936 - 1952 */ + 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, /* bytes 1952 - 1968 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, /* bytes 1968 - 1984 */ + 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, /* bytes 1984 - 2000 */ + 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, /* bytes 2000 - 2016 */ + 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, /* bytes 2016 - 2032 */ + 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, /* bytes 2032 - 2048 */ + 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, /* bytes 2048 - 2064 */ + 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2064 - 2080 */ + 0x00, 0x02, 0x00, 0x78, 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2080 - 2096 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 2096 - 2112 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, /* bytes 2112 - 2128 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, /* bytes 2128 - 2144 */ + 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, /* bytes 2144 - 2160 */ + 0x00, 0xf0, 0x01, 0xc0, 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, /* bytes 2160 - 2176 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, /* bytes 2176 - 2192 */ + 0x10, 0x00, 0x28, 0x01, 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 2192 - 2208 */ + 0xff, 0x00, 0x00, 0x05, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, /* bytes 2208 - 2224 */ + 0x00, 0xe8, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 2224 - 2240 */ + 0x11, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, /* bytes 2240 - 2256 */ + 0x04, 0x01, 0x9c, 0x00, 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, /* bytes 2256 - 2272 */ + 0xdc, 0x01, 0x7c, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, /* bytes 2272 - 2288 */ + 0x6c, 0x01, 0x14, 0x02, 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, /* bytes 2288 - 2304 */ + 0x9c, 0x00, 0x34, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, /* bytes 2304 - 2320 */ + 0x01, 0x50, 0x01, 0x40, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2320 - 2336 */ + 0x70, 0x02, 0x20, 0x01, 0x78, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 2336 - 2352 */ + 0x00, 0x04, 0x01, 0xcc, 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2352 - 2368 */ + 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, /* bytes 2368 - 2384 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x10, 0x01, 0x18, 0x02, 0xe8, 0x00, 0xf0, 0x01, 0xc0, /* bytes 2384 - 2400 */ + 0x00, 0x18, 0x02, 0xe8, 0x00, 0x40, 0x02, 0x10, 0x01, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 2400 - 2416 */ + 0x00, 0x00, 0x05, 0x00, 0x60, 0x00, 0x28, 0x01, 0x38, 0x00, 0x50, 0x01, 0x10, 0x00, 0x28, 0x01, /* bytes 2416 - 2432 */ + 0x38, 0x00, 0x00, 0x01, 0x60, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, /* bytes 2432 - 2448 */ + 0x00, 0xc0, 0x00, 0x38, 0x00, 0x98, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x88, 0x00, 0xe8, 0x00, 0x60, /* bytes 2448 - 2464 */ + 0x00, 0xc0, 0x00, 0x38, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x34, 0x01, /* bytes 2464 - 2480 */ + 0x9c, 0x00, 0x04, 0x01, 0xcc, 0x00, 0xd4, 0x00, 0xcc, 0x00, 0x9c, 0x00, 0x04, 0x01, 0x9c, 0x00, /* bytes 2480 - 2496 */ + 0x6c, 0x01, 0xd4, 0x00, 0xa4, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x34, 0x01, 0xdc, 0x01, 0x7c, 0x01, /* bytes 2496 - 2512 */ + 0xdc, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xdc, 0x01, 0xa4, 0x01, 0x14, 0x02, 0x6c, 0x01, 0x14, 0x02, /* bytes 2512 - 2528 */ + 0x04, 0x01, 0xdc, 0x01, 0xcc, 0x00, 0xac, 0x01, 0xcc, 0x00, 0x7c, 0x01, 0x9c, 0x00, 0x34, 0x01, /* bytes 2528 - 2544 */ + 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7e, 0x01, 0x50, 0x01, 0x4a, /* bytes 2544 - 2560 */ + 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7a, 0x02, 0x20, 0x01, /* bytes 2560 - 2576 */ + 0x8a, 0x01, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x01, 0xcc, /* bytes 2576 - 2592 */ + 0x00, 0x2c, 0x01, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, /* bytes 2592 - 2608 */ + 0x74, 0x01, 0xfc, 0x00, 0xa4, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 2608 - 2624 */ + 0x00, 0x05, 0x00, 0xaf, 0x00, 0x02, 0x02, 0x88, 0x00, 0x2a, 0x02, 0xaf, 0x00, 0x51, 0x02, 0xd6, /* bytes 2624 - 2640 */ + 0x00, 0x2a, 0x02, 0xaf, 0x00, 0x02, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, /* bytes 2640 - 2656 */ + 0x2e, 0x00, 0x28, 0x01, 0x08, 0x00, 0x4e, 0x01, 0xe2, 0xff, 0x28, 0x01, 0x08, 0x00, 0x02, 0x01, /* bytes 2656 - 2672 */ + 0x2e, 0x00, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x78, 0x00, 0x20, /* bytes 2672 - 2688 */ + 0x00, 0x51, 0x00, 0x46, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x9e, 0x00, 0x46, 0x00, 0x78, 0x00, 0x20, /* bytes 2688 - 2704 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0xac, 0x01, 0x94, 0x01, 0xec, 0x01, /* bytes 2704 - 2720 */ + 0x94, 0x01, 0x4c, 0x02, 0x6c, 0x01, 0x4c, 0x02, 0x04, 0x01, 0xd4, 0x01, 0xd4, 0x00, 0xa4, 0x01, /* bytes 2720 - 2736 */ + 0xd4, 0x00, 0x74, 0x01, 0xac, 0x00, 0x2c, 0x01, 0xac, 0x00, 0xfc, 0x00, 0xcc, 0x00, 0xdc, 0x00, /* bytes 2736 - 2752 */ + 0xcc, 0x00, 0xa4, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x64, 0x01, 0xdc, 0x00, 0xa4, 0x01, 0xfc, 0x00, /* bytes 2752 - 2768 */ + 0xa4, 0x01, 0x2c, 0x01, 0xcc, 0x01, 0x74, 0x01, 0xcc, 0x01, 0xac, 0x01, 0x94, 0x01, 0x03, 0x00, /* bytes 2768 - 2784 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0x50, 0x01, 0x6c, 0x02, 0x50, 0x01, 0x03, /* bytes 2784 - 2800 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x03, 0x20, 0x01, 0x8a, 0x01, 0x20, 0x01, /* bytes 2800 - 2816 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2e, 0x01, 0x04, 0x01, 0xfc, 0x00, 0xcc, /* bytes 2816 - 2832 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2e, 0x01, 0x6a, 0x01, 0xfc, 0x00, /* bytes 2832 - 2848 */ + 0xa4, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x69, /* bytes 2848 - 2864 */ + 0x00, 0x3d, 0x02, 0x8e, 0x00, 0x62, 0x02, 0xb3, 0x00, 0x3d, 0x02, 0x8e, 0x00, 0x18, 0x02, 0x69, /* bytes 2864 - 2880 */ + 0x00, 0x3d, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x0f, 0x00, 0x28, 0x01, /* bytes 2880 - 2896 */ + 0xf0, 0xff, 0x47, 0x01, 0xd1, 0xff, 0x28, 0x01, 0xf0, 0xff, 0x09, 0x01, 0x0f, 0x00, 0x28, 0x01, /* bytes 2896 - 2912 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x54, 0x00, 0x50, /* bytes 2912 - 2928 */ + 0x00, 0x76, 0x00, 0x2e, 0x00, 0x54, 0x00, 0x0b, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x03, 0x00, 0xc0, /* bytes 2928 - 2944 */ + 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0xe4, 0x00, 0x2c, 0x01, 0xe4, 0x00, 0x74, 0x01, 0x24, 0x01, /* bytes 2944 - 2960 */ + 0xa4, 0x01, 0x4c, 0x01, 0xdc, 0x01, 0x9c, 0x01, 0xdc, 0x01, 0xdc, 0x01, 0xb4, 0x01, 0x0c, 0x02, /* bytes 2960 - 2976 */ + 0xac, 0x01, 0x3c, 0x02, 0x84, 0x01, 0x7c, 0x02, 0x84, 0x01, 0x9c, 0x02, 0x5c, 0x01, 0x9c, 0x02, /* bytes 2976 - 2992 */ + 0x0c, 0x01, 0x74, 0x02, 0xe4, 0x00, 0x04, 0x02, 0xdc, 0x00, 0xa4, 0x01, 0x9c, 0x00, 0x4c, 0x01, /* bytes 2992 - 3008 */ + 0x9c, 0x00, 0x14, 0x01, 0xfc, 0x00, 0xe4, 0x00, 0x2c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3008 - 3024 */ + 0x00, 0x02, 0x00, 0xb8, 0x01, 0x50, 0x01, 0xbc, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3024 - 3040 */ + 0x01, 0x00, 0x02, 0x00, 0xc2, 0x03, 0x20, 0x01, 0x60, 0x02, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 3040 - 3056 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x01, 0x9c, 0x00, 0xb6, 0x01, 0xe4, 0x00, 0x03, 0x00, 0xc0, /* bytes 3056 - 3072 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x01, 0x0a, 0x01, 0x14, 0x01, 0xfc, 0x00, 0x03, 0x00, /* bytes 3072 - 3088 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x40, 0x01, 0x84, 0x01, 0x24, 0x01, 0xa4, 0x01, 0x03, /* bytes 3088 - 3104 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9c, 0x01, 0xdc, 0x01, 0xba, 0x01, 0x92, 0x01, /* bytes 3104 - 3120 */ + 0x23, 0x00, 0x09, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0xe4, 0x00, 0x1e, /* bytes 3120 - 3136 */ + 0x01, 0xcd, 0x00, 0x35, 0x01, 0xcd, 0x00, 0x58, 0x01, 0xe4, 0x00, 0x6f, 0x01, 0xe4, 0x00, 0x86, /* bytes 3136 - 3152 */ + 0x01, 0xff, 0x00, 0xa1, 0x01, 0x31, 0x01, 0xa1, 0x01, 0x4c, 0x01, 0x86, 0x01, 0x4c, 0x01, 0x73, /* bytes 3152 - 3168 */ + 0x01, 0x67, 0x01, 0x58, 0x01, 0x67, 0x01, 0x35, 0x01, 0x4c, 0x01, 0x1a, 0x01, 0x4c, 0x01, 0x07, /* bytes 3168 - 3184 */ + 0x01, 0x31, 0x01, 0xec, 0x00, 0xff, 0x00, 0xec, 0x00, 0xe4, 0x00, 0x07, 0x01, 0xe4, 0x00, 0x1e, /* bytes 3184 - 3200 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x58, 0x01, 0xd5, 0x01, 0x68, 0x01, /* bytes 3200 - 3216 */ + 0xe8, 0x01, 0x77, 0x01, 0xf4, 0x01, 0x8f, 0x01, 0xf4, 0x01, 0xa2, 0x01, 0xe8, 0x01, 0xa9, 0x01, /* bytes 3216 - 3232 */ + 0xde, 0x01, 0xbf, 0x01, 0xda, 0x01, 0xc1, 0x01, 0xda, 0x01, 0xcd, 0x01, 0xce, 0x01, 0xcd, 0x01, /* bytes 3232 - 3248 */ + 0xb6, 0x01, 0xc1, 0x01, 0xaa, 0x01, 0xae, 0x01, 0xa7, 0x01, 0x91, 0x01, 0x94, 0x01, 0x77, 0x01, /* bytes 3248 - 3264 */ + 0x94, 0x01, 0x66, 0x01, 0xa7, 0x01, 0x58, 0x01, 0xb8, 0x01, 0x58, 0x01, 0xd5, 0x01, 0x03, 0x00, /* bytes 3264 - 3280 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0xc1, 0x01, 0x9c, 0x00, 0xae, 0x01, 0x99, 0x00, 0x91, /* bytes 3280 - 3296 */ + 0x01, 0x86, 0x00, 0x77, 0x01, 0x86, 0x00, 0x66, 0x01, 0x99, 0x00, 0x58, 0x01, 0xaa, 0x00, 0x58, /* bytes 3296 - 3312 */ + 0x01, 0xc7, 0x00, 0x68, 0x01, 0xda, 0x00, 0x77, 0x01, 0xe6, 0x00, 0x8f, 0x01, 0xe6, 0x00, 0xa2, /* bytes 3312 - 3328 */ + 0x01, 0xda, 0x00, 0xa9, 0x01, 0xd0, 0x00, 0xbf, 0x01, 0xcc, 0x00, 0xc1, 0x01, 0xcc, 0x00, 0xcd, /* bytes 3328 - 3344 */ + 0x01, 0xc0, 0x00, 0xcd, 0x01, 0xa8, 0x00, 0xc1, 0x01, 0x9c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 3344 - 3360 */ + 0x00, 0x00, 0x05, 0x00, 0x79, 0x00, 0x2c, 0x02, 0x57, 0x00, 0x4d, 0x02, 0x79, 0x00, 0x6f, 0x02, /* bytes 3360 - 3376 */ + 0x9a, 0x00, 0x4d, 0x02, 0x79, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, /* bytes 3376 - 3392 */ + 0x00, 0xf7, 0xff, 0x28, 0x01, 0xe3, 0xff, 0x3c, 0x01, 0xcf, 0xff, 0x28, 0x01, 0xe3, 0xff, 0x14, /* bytes 3392 - 3408 */ + 0x01, 0xf7, 0xff, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x25, 0x00, /* bytes 3408 - 3424 */ + 0x1d, 0x00, 0x41, 0x00, 0x38, 0x00, 0x5c, 0x00, 0x1d, 0x00, 0x41, 0x00, 0x02, 0x00, 0x25, 0x00, /* bytes 3424 - 3440 */ + 0x1d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0x11, 0x03, 0x6e, 0x01, 0x27, /* bytes 3440 - 3456 */ + 0x03, 0x52, 0x01, 0x27, 0x03, 0x1b, 0x01, 0x0b, 0x03, 0xff, 0x00, 0xbe, 0x02, 0xfa, 0x00, 0x7c, /* bytes 3456 - 3472 */ + 0x02, 0xce, 0x00, 0x3f, 0x02, 0xce, 0x00, 0x18, 0x02, 0xfa, 0x00, 0xf7, 0x01, 0x20, 0x01, 0xf7, /* bytes 3472 - 3488 */ + 0x01, 0x63, 0x01, 0x1e, 0x02, 0x8f, 0x01, 0x3f, 0x02, 0xaa, 0x01, 0x76, 0x02, 0xaa, 0x01, 0xa2, /* bytes 3488 - 3504 */ + 0x02, 0x8f, 0x01, 0xb3, 0x02, 0x79, 0x01, 0xe5, 0x02, 0x6e, 0x01, 0x11, 0x03, 0x6e, 0x01, 0x03, /* bytes 3504 - 3520 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x02, 0xad, 0x01, 0x76, 0x02, 0xaa, 0x01, /* bytes 3520 - 3536 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x02, 0xf8, 0x00, 0x18, 0x02, 0xfa, /* bytes 3536 - 3552 */ + 0x00, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x1b, 0x01, /* bytes 3552 - 3568 */ + 0x90, 0x01, 0x31, 0x01, 0x7b, 0x01, 0x31, 0x01, 0x59, 0x01, 0x3c, 0x01, 0x4d, 0x01, 0x3c, 0x01, /* bytes 3568 - 3584 */ + 0x2c, 0x01, 0x1e, 0x01, 0x0d, 0x01, 0xfc, 0x00, 0x0d, 0x01, 0xee, 0x00, 0x1b, 0x01, 0xd1, 0x00, /* bytes 3584 - 3600 */ + 0x1b, 0x01, 0xbc, 0x00, 0x31, 0x01, 0xbc, 0x00, 0x4d, 0x01, 0xae, 0x00, 0x5c, 0x01, 0xae, 0x00, /* bytes 3600 - 3616 */ + 0x7d, 0x01, 0xcd, 0x00, 0x9c, 0x01, 0xee, 0x00, 0x9c, 0x01, 0xfa, 0x00, 0x90, 0x01, 0x1b, 0x01, /* bytes 3616 - 3632 */ + 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0xfd, 0x01, 0x31, 0x01, 0xfd, /* bytes 3632 - 3648 */ + 0x01, 0x51, 0x01, 0x12, 0x02, 0x66, 0x01, 0x32, 0x02, 0x66, 0x01, 0x3d, 0x02, 0x72, 0x01, 0x5e, /* bytes 3648 - 3664 */ + 0x02, 0x72, 0x01, 0x7c, 0x02, 0x54, 0x01, 0x7c, 0x02, 0x33, 0x01, 0x6e, 0x02, 0x26, 0x01, 0x6e, /* bytes 3664 - 3680 */ + 0x02, 0x0a, 0x01, 0x59, 0x02, 0xf5, 0x00, 0x3d, 0x02, 0xf5, 0x00, 0x30, 0x02, 0xe7, 0x00, 0x0f, /* bytes 3680 - 3696 */ + 0x02, 0xe7, 0x00, 0xf1, 0x01, 0x05, 0x01, 0xf1, 0x01, 0x26, 0x01, 0xfd, 0x01, 0x31, 0x01, 0x03, /* bytes 3696 - 3712 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x58, 0x01, 0xf6, 0x01, 0x6c, 0x01, 0x0a, 0x02, /* bytes 3712 - 3728 */ + 0x82, 0x01, 0x0a, 0x02, 0x8a, 0x01, 0x03, 0x02, 0xa0, 0x01, 0x03, 0x02, 0xae, 0x01, 0xf5, 0x01, /* bytes 3728 - 3744 */ + 0xae, 0x01, 0xdf, 0x01, 0xb5, 0x01, 0xd7, 0x01, 0xb5, 0x01, 0xc1, 0x01, 0xa1, 0x01, 0xad, 0x01, /* bytes 3744 - 3760 */ + 0x8b, 0x01, 0xad, 0x01, 0x82, 0x01, 0xb6, 0x01, 0x6f, 0x01, 0xb6, 0x01, 0x61, 0x01, 0xc4, 0x01, /* bytes 3760 - 3776 */ + 0x61, 0x01, 0xd7, 0x01, 0x58, 0x01, 0xe0, 0x01, 0x58, 0x01, 0xf6, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3776 - 3792 */ + 0xff, 0x00, 0x00, 0x11, 0x00, 0x6e, 0x01, 0x6e, 0x00, 0x5a, 0x01, 0x82, 0x00, 0x5a, 0x01, 0x98, /* bytes 3792 - 3808 */ + 0x00, 0x61, 0x01, 0xa0, 0x00, 0x61, 0x01, 0xb6, 0x00, 0x6f, 0x01, 0xc4, 0x00, 0x85, 0x01, 0xc4, /* bytes 3808 - 3824 */ + 0x00, 0x8d, 0x01, 0xcb, 0x00, 0xa3, 0x01, 0xcb, 0x00, 0xb7, 0x01, 0xb7, 0x00, 0xb7, 0x01, 0xa1, /* bytes 3824 - 3840 */ + 0x00, 0xae, 0x01, 0x98, 0x00, 0xae, 0x01, 0x85, 0x00, 0xa0, 0x01, 0x77, 0x00, 0x8d, 0x01, 0x77, /* bytes 3840 - 3856 */ + 0x00, 0x84, 0x01, 0x6e, 0x00, 0x6e, 0x01, 0x6e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, /* bytes 3856 - 3872 */ + 0x05, 0x00, 0x6b, 0x00, 0x76, 0x02, 0x4e, 0x00, 0x59, 0x02, 0x6b, 0x00, 0x3d, 0x02, 0x87, 0x00, /* bytes 3872 - 3888 */ + 0x59, 0x02, 0x6b, 0x00, 0x76, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0xdd, /* bytes 3888 - 3904 */ + 0xff, 0x30, 0x01, 0xd5, 0xff, 0x28, 0x01, 0xdd, 0xff, 0x20, 0x01, 0xe5, 0xff, 0x28, 0x01, 0xdd, /* bytes 3904 - 3920 */ + 0xff, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x25, 0x00, 0x13, 0x00, /* bytes 3920 - 3936 */ + 0x37, 0x00, 0x25, 0x00, 0x48, 0x00, 0x13, 0x00, 0x37, 0x00, 0x01, 0x00, 0x25, 0x00, 0x13, 0x00, /* bytes 3936 - 3952 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x11, 0x00, 0xd7, 0x03, 0x13, 0x01, 0xa2, 0x03, 0x0f, /* bytes 3952 - 3968 */ + 0x01, 0x75, 0x03, 0xfc, 0x00, 0x48, 0x03, 0x1e, 0x01, 0x31, 0x03, 0x1e, 0x01, 0x1b, 0x03, 0x34, /* bytes 3968 - 3984 */ + 0x01, 0x1b, 0x03, 0x4b, 0x01, 0x2d, 0x03, 0x5a, 0x01, 0x44, 0x03, 0x5e, 0x01, 0x71, 0x03, 0x78, /* bytes 3984 - 4000 */ + 0x01, 0x8f, 0x03, 0x74, 0x01, 0x9a, 0x03, 0x65, 0x01, 0xbc, 0x03, 0x5e, 0x01, 0xda, 0x03, 0x5e, /* bytes 4000 - 4016 */ + 0x01, 0xe9, 0x03, 0x4b, 0x01, 0xe9, 0x03, 0x25, 0x01, 0xd7, 0x03, 0x13, 0x01, 0x03, 0x00, 0xc0, /* bytes 4016 - 4032 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4b, 0x03, 0x50, 0x01, 0x2d, 0x03, 0x5a, 0x01, 0x03, 0x00, /* bytes 4032 - 4048 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x71, 0x03, 0x78, 0x01, 0x72, 0x03, 0x7a, 0x01, 0x03, /* bytes 4048 - 4064 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4b, 0x03, 0x35, 0x01, 0x31, 0x03, 0x1e, 0x01, /* bytes 4064 - 4080 */ + 0x23, 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x01, 0x01, 0x3f, /* bytes 4080 - 4096 */ + 0x01, 0xf0, 0x00, 0x2f, 0x01, 0xd7, 0x00, 0x2f, 0x01, 0xc7, 0x00, 0x3f, 0x01, 0xb6, 0x00, 0x3f, /* bytes 4096 - 4112 */ + 0x01, 0xa3, 0x00, 0x53, 0x01, 0xa3, 0x00, 0x77, 0x01, 0xb6, 0x00, 0x8a, 0x01, 0xc4, 0x00, 0x8a, /* bytes 4112 - 4128 */ + 0x01, 0xd7, 0x00, 0x9d, 0x01, 0xf0, 0x00, 0x9d, 0x01, 0x03, 0x01, 0x8a, 0x01, 0x11, 0x01, 0x8a, /* bytes 4128 - 4144 */ + 0x01, 0x25, 0x01, 0x77, 0x01, 0x25, 0x01, 0x53, 0x01, 0x11, 0x01, 0x3f, 0x01, 0x01, 0x01, 0x3f, /* bytes 4144 - 4160 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0xf7, 0x01, 0xf6, 0x00, 0xe3, 0x01, /* bytes 4160 - 4176 */ + 0x0a, 0x01, 0xe3, 0x01, 0x2e, 0x01, 0xf7, 0x01, 0x42, 0x01, 0x05, 0x02, 0x42, 0x01, 0x18, 0x02, /* bytes 4176 - 4192 */ + 0x56, 0x01, 0x32, 0x02, 0x56, 0x01, 0x45, 0x02, 0x42, 0x01, 0x53, 0x02, 0x42, 0x01, 0x67, 0x02, /* bytes 4192 - 4208 */ + 0x2e, 0x01, 0x67, 0x02, 0x0a, 0x01, 0x53, 0x02, 0xf6, 0x00, 0x43, 0x02, 0xf6, 0x00, 0x32, 0x02, /* bytes 4208 - 4224 */ + 0xe5, 0x00, 0x18, 0x02, 0xe5, 0x00, 0x08, 0x02, 0xf6, 0x00, 0xf7, 0x01, 0xf6, 0x00, 0x03, 0x00, /* bytes 4224 - 4240 */ + 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x84, 0x01, 0xd5, 0x01, 0x6f, 0x01, 0xd5, 0x01, 0x64, /* bytes 4240 - 4256 */ + 0x01, 0xe0, 0x01, 0x64, 0x01, 0xea, 0x01, 0x5a, 0x01, 0xf3, 0x01, 0x5a, 0x01, 0x02, 0x02, 0x64, /* bytes 4256 - 4272 */ + 0x01, 0x0b, 0x02, 0x64, 0x01, 0x15, 0x02, 0x6f, 0x01, 0x20, 0x02, 0x84, 0x01, 0x20, 0x02, 0x8f, /* bytes 4272 - 4288 */ + 0x01, 0x15, 0x02, 0x8f, 0x01, 0x0d, 0x02, 0x9a, 0x01, 0x02, 0x02, 0x9a, 0x01, 0xf3, 0x01, 0x8f, /* bytes 4288 - 4304 */ + 0x01, 0xe8, 0x01, 0x8f, 0x01, 0xe0, 0x01, 0x84, 0x01, 0xd5, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 4304 - 4320 */ + 0x00, 0x00, 0x11, 0x00, 0x90, 0x01, 0x8e, 0x00, 0x9a, 0x01, 0x85, 0x00, 0x9a, 0x01, 0x76, 0x00, /* bytes 4320 - 4336 */ + 0x90, 0x01, 0x6d, 0x00, 0x90, 0x01, 0x63, 0x00, 0x85, 0x01, 0x58, 0x00, 0x70, 0x01, 0x58, 0x00, /* bytes 4336 - 4352 */ + 0x65, 0x01, 0x63, 0x00, 0x65, 0x01, 0x6b, 0x00, 0x5a, 0x01, 0x76, 0x00, 0x5a, 0x01, 0x85, 0x00, /* bytes 4352 - 4368 */ + 0x65, 0x01, 0x90, 0x00, 0x65, 0x01, 0x98, 0x00, 0x70, 0x01, 0xa3, 0x00, 0x85, 0x01, 0xa3, 0x00, /* bytes 4368 - 4384 */ + 0x90, 0x01, 0x98, 0x00, 0x90, 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, /* bytes 4384 - 4400 */ + 0x00, 0x62, 0x00, 0x4c, 0x02, 0x4c, 0x00, 0x62, 0x02, 0x62, 0x00, 0x78, 0x02, 0x78, 0x00, 0x62, /* bytes 4400 - 4416 */ + 0x02, 0x62, 0x00, 0x4c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x05, 0x00, 0x34, 0x00, /* bytes 4416 - 4432 */ + 0x08, 0x00, 0x2c, 0x00, 0x10, 0x00, 0x34, 0x00, 0x18, 0x00, 0x3c, 0x00, 0x10, 0x00, 0x34, 0x00, /* bytes 4432 - 4448 */ + 0x08, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x95, /* bytes 4448 - 4464 */ + 0x00, 0x71, 0x01, 0xa0, 0x00, 0x7c, 0x01, 0xa8, 0x00, 0x7c, 0x01, 0xb2, 0x00, 0x87, 0x01, 0xc0, /* bytes 4464 - 4480 */ + 0x00, 0x87, 0x01, 0xcb, 0x00, 0x7c, 0x01, 0xd3, 0x00, 0x7c, 0x01, 0xde, 0x00, 0x71, 0x01, 0xde, /* bytes 4480 - 4496 */ + 0x00, 0x5d, 0x01, 0xd3, 0x00, 0x52, 0x01, 0xca, 0x00, 0x52, 0x01, 0xc0, 0x00, 0x49, 0x01, 0xb2, /* bytes 4496 - 4512 */ + 0x00, 0x49, 0x01, 0xa9, 0x00, 0x52, 0x01, 0xa0, 0x00, 0x52, 0x01, 0x95, 0x00, 0x5d, 0x01, 0x95, /* bytes 4512 - 4528 */ + 0x00, 0x71, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x38, 0x02, 0x19, 0x01, /* bytes 4528 - 4544 */ + 0x38, 0x02, 0x05, 0x01, 0x2e, 0x02, 0xfa, 0x00, 0x24, 0x02, 0xfa, 0x00, 0x1b, 0x02, 0xf1, 0x00, /* bytes 4544 - 4560 */ + 0x0e, 0x02, 0xf1, 0x00, 0x05, 0x02, 0xfa, 0x00, 0xfb, 0x01, 0xfa, 0x00, 0xf1, 0x01, 0x05, 0x01, /* bytes 4560 - 4576 */ + 0xf1, 0x01, 0x19, 0x01, 0xfb, 0x01, 0x23, 0x01, 0x03, 0x02, 0x23, 0x01, 0x0e, 0x02, 0x2e, 0x01, /* bytes 4576 - 4592 */ + 0x1b, 0x02, 0x2e, 0x01, 0x26, 0x02, 0x23, 0x01, 0x2e, 0x02, 0x23, 0x01, 0x38, 0x02, 0x19, 0x01, /* bytes 4592 - 4608 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x80, 0x01, 0xfc, 0x01, 0x80, 0x01, 0xfa, /* bytes 4608 - 4624 */ + 0x01, 0x7e, 0x01, 0xf8, 0x01, 0x7e, 0x01, 0xf6, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x78, 0x01, 0xf4, /* bytes 4624 - 4640 */ + 0x01, 0x76, 0x01, 0xf6, 0x01, 0x76, 0x01, 0xf8, 0x01, 0x74, 0x01, 0xfa, 0x01, 0x74, 0x01, 0xfc, /* bytes 4640 - 4656 */ + 0x01, 0x76, 0x01, 0xfe, 0x01, 0x76, 0x01, 0x00, 0x02, 0x78, 0x01, 0x02, 0x02, 0x7c, 0x01, 0x02, /* bytes 4656 - 4672 */ + 0x02, 0x7e, 0x01, 0x00, 0x02, 0x7e, 0x01, 0xfe, 0x01, 0x80, 0x01, 0xfc, 0x01, 0x03, 0x00, 0xc0, /* bytes 4672 - 4688 */ + 0x03, 0xff, 0x00, 0x00, 0x11, 0x00, 0x76, 0x01, 0x7e, 0x00, 0x78, 0x01, 0x80, 0x00, 0x7c, 0x01, /* bytes 4688 - 4704 */ + 0x80, 0x00, 0x7e, 0x01, 0x7e, 0x00, 0x7e, 0x01, 0x7c, 0x00, 0x80, 0x01, 0x7a, 0x00, 0x80, 0x01, /* bytes 4704 - 4720 */ + 0x78, 0x00, 0x7e, 0x01, 0x76, 0x00, 0x7e, 0x01, 0x74, 0x00, 0x7c, 0x01, 0x72, 0x00, 0x78, 0x01, /* bytes 4720 - 4736 */ + 0x72, 0x00, 0x76, 0x01, 0x74, 0x00, 0x76, 0x01, 0x76, 0x00, 0x74, 0x01, 0x78, 0x00, 0x74, 0x01, /* bytes 4736 - 4752 */ + 0x7a, 0x00, 0x76, 0x01, 0x7c, 0x00, 0x76, 0x01, 0x7e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 4752 - 4768 */ + 0x00, 0x05, 0x00, 0x4f, 0x00, 0x67, 0x02, 0x5d, 0x00, 0x75, 0x02, 0x6c, 0x00, 0x67, 0x02, 0x5d, /* bytes 4768 - 4784 */ + 0x00, 0x58, 0x02, 0x4f, 0x00, 0x67, 0x02, 0x23, 0x00, 0x02, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, /* bytes 4784 - 4800 */ + 0x00, 0x00, 0x11, 0x00, 0x90, 0x00, 0x65, 0x01, 0x8e, 0x00, 0x65, 0x01, 0x8c, 0x00, 0x67, 0x01, /* bytes 4800 - 4816 */ + 0x8b, 0x00, 0x67, 0x01, 0x89, 0x00, 0x68, 0x01, 0x89, 0x00, 0x6b, 0x01, 0x8b, 0x00, 0x6d, 0x01, /* bytes 4816 - 4832 */ + 0x8c, 0x00, 0x6d, 0x01, 0x8e, 0x00, 0x6f, 0x01, 0x90, 0x00, 0x6f, 0x01, 0x92, 0x00, 0x6d, 0x01, /* bytes 4832 - 4848 */ + 0x93, 0x00, 0x6d, 0x01, 0x94, 0x00, 0x6b, 0x01, 0x94, 0x00, 0x68, 0x01, 0x93, 0x00, 0x67, 0x01, /* bytes 4848 - 4864 */ + 0x91, 0x00, 0x67, 0x01, 0x90, 0x00, 0x65, 0x01, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x11, /* bytes 4864 - 4880 */ + 0x00, 0xf5, 0x01, 0xfa, 0x00, 0xf3, 0x01, 0xfb, 0x00, 0xf1, 0x01, 0xfb, 0x00, 0xef, 0x01, 0xfe, /* bytes 4880 - 4896 */ + 0x00, 0xef, 0x01, 0x01, 0x01, 0xf1, 0x01, 0x03, 0x01, 0xf3, 0x01, 0x03, 0x01, 0xf5, 0x01, 0x06, /* bytes 4896 - 4912 */ + 0x01, 0xf8, 0x01, 0x06, 0x01, 0xfa, 0x01, 0x03, 0x01, 0xfb, 0x01, 0x03, 0x01, 0xfd, 0x01, 0x01, /* bytes 4912 - 4928 */ + 0x01, 0xfd, 0x01, 0xfe, 0x00, 0xfb, 0x01, 0xfb, 0x00, 0xf9, 0x01, 0xfb, 0x00, 0xf8, 0x01, 0xfa, /* bytes 4928 - 4944 */ + 0x00, 0xf5, 0x01, 0xfa, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 4944 - 4960 */ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, /* bytes 4960 - 4976 */ + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, /* bytes 4976 - 4992 */ + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 4992 - 5008 */ + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, /* bytes 5008 - 5024 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, /* bytes 5024 - 5040 */ + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, /* bytes 5040 - 5056 */ + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 5056 - 5072 */ + 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5072 - 5088 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, /* bytes 5088 - 5104 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, /* bytes 5104 - 5120 */ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 5120 - 5136 */ + 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, /* bytes 5136 - 5152 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* bytes 5152 - 5168 */ + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, /* bytes 5168 - 5184 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 5184 - 5200 */ + 0x00, 0x23, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, /* bytes 5200 - 5216 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +__attribute__ ((aligned (8))) +static const uint8_t GENERIC_CONFIRMATION_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0xf2, 0x21, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x2a, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, /* bytes 16 - 32 */ + 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, /* bytes 32 - 48 */ + 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, /* bytes 48 - 64 */ + 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 64 - 80 */ + 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, /* bytes 80 - 96 */ + 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, /* bytes 96 - 112 */ + 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, /* bytes 112 - 128 */ + 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, /* bytes 128 - 144 */ + 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 144 - 160 */ + 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 160 - 176 */ + 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 176 - 192 */ + 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 192 - 208 */ + 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 208 - 224 */ + 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, /* bytes 224 - 240 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, /* bytes 240 - 256 */ + 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, /* bytes 256 - 272 */ + 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, /* bytes 272 - 288 */ + 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, /* bytes 288 - 304 */ + 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, /* bytes 304 - 320 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, /* bytes 320 - 336 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, /* bytes 336 - 352 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, /* bytes 352 - 368 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, /* bytes 368 - 384 */ + 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, /* bytes 384 - 400 */ + 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, /* bytes 400 - 416 */ + 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, /* bytes 416 - 432 */ + 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, /* bytes 432 - 448 */ + 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 448 - 464 */ + 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, /* bytes 464 - 480 */ + 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, /* bytes 480 - 496 */ + 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, /* bytes 496 - 512 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, /* bytes 512 - 528 */ + 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 528 - 544 */ + 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 544 - 560 */ + 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 560 - 576 */ + 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 576 - 592 */ + 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 592 - 608 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, /* bytes 608 - 624 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, /* bytes 624 - 640 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, /* bytes 640 - 656 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, /* bytes 656 - 672 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, /* bytes 672 - 688 */ + 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, /* bytes 688 - 704 */ + 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, /* bytes 704 - 720 */ + 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 720 - 736 */ + 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, /* bytes 736 - 752 */ + 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, /* bytes 752 - 768 */ + 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, /* bytes 768 - 784 */ + 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, /* bytes 784 - 800 */ + 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, /* bytes 800 - 816 */ + 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, /* bytes 816 - 832 */ + 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 832 - 848 */ + 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 848 - 864 */ + 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 864 - 880 */ + 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 880 - 896 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, /* bytes 896 - 912 */ + 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, /* bytes 912 - 928 */ + 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, /* bytes 928 - 944 */ + 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, /* bytes 944 - 960 */ + 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 960 - 976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, /* bytes 976 - 992 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, /* bytes 992 - 1008 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, /* bytes 1008 - 1024 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, /* bytes 1024 - 1040 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, /* bytes 1040 - 1056 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, /* bytes 1056 - 1072 */ + 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, /* bytes 1072 - 1088 */ + 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, /* bytes 1088 - 1104 */ + 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 1104 - 1120 */ + 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, /* bytes 1120 - 1136 */ + 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, /* bytes 1136 - 1152 */ + 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, /* bytes 1152 - 1168 */ + 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, /* bytes 1168 - 1184 */ + 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, /* bytes 1184 - 1200 */ + 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1200 - 1216 */ + 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1216 - 1232 */ + 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1232 - 1248 */ + 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1248 - 1264 */ + 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1264 - 1280 */ + 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 1280 - 1296 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, /* bytes 1296 - 1312 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, /* bytes 1312 - 1328 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, /* bytes 1328 - 1344 */ + 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, /* bytes 1344 - 1360 */ + 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, /* bytes 1360 - 1376 */ + 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 1376 - 1392 */ + 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, /* bytes 1392 - 1408 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, /* bytes 1408 - 1424 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, /* bytes 1424 - 1440 */ + 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, /* bytes 1440 - 1456 */ + 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, /* bytes 1456 - 1472 */ + 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, /* bytes 1472 - 1488 */ + 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, /* bytes 1488 - 1504 */ + 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, /* bytes 1504 - 1520 */ + 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1520 - 1536 */ + 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 1536 - 1552 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, /* bytes 1552 - 1568 */ + 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, /* bytes 1568 - 1584 */ + 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, /* bytes 1584 - 1600 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, /* bytes 1600 - 1616 */ + 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1616 - 1632 */ + 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1632 - 1648 */ + 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 1648 - 1664 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, /* bytes 1664 - 1680 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, /* bytes 1680 - 1696 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, /* bytes 1696 - 1712 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, /* bytes 1712 - 1728 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, /* bytes 1728 - 1744 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, /* bytes 1744 - 1760 */ + 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, /* bytes 1760 - 1776 */ + 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, /* bytes 1776 - 1792 */ + 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, /* bytes 1792 - 1808 */ + 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1808 - 1824 */ + 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, /* bytes 1824 - 1840 */ + 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, /* bytes 1840 - 1856 */ + 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, /* bytes 1856 - 1872 */ + 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, /* bytes 1872 - 1888 */ + 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1888 - 1904 */ + 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1904 - 1920 */ + 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1920 - 1936 */ + 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1936 - 1952 */ + 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1952 - 1968 */ + 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, /* bytes 1968 - 1984 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, /* bytes 1984 - 2000 */ + 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x50, /* bytes 2000 - 2016 */ + 0x00, 0x8f, 0x01, 0xed, 0x00, 0x28, 0x02, 0x0b, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, /* bytes 2016 - 2032 */ + 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, /* bytes 2032 - 2048 */ + 0x81, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x89, 0x01, 0xf9, 0x00, 0x85, 0x01, 0x03, 0x00, /* bytes 2048 - 2064 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x43, 0x01, 0x20, 0x01, 0x0b, 0x01, 0x03, /* bytes 2064 - 2080 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x0b, 0x01, 0x58, 0x01, 0x43, 0x01, /* bytes 2080 - 2096 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xb8, 0x00, 0x93, 0x00, 0xa8, /* bytes 2096 - 2112 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, /* bytes 2112 - 2128 */ + 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xb8, 0x00, 0xe5, /* bytes 2128 - 2144 */ + 0x01, 0xa8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, /* bytes 2144 - 2160 */ + 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x33, /* bytes 2160 - 2176 */ + 0x01, 0x46, 0x02, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, /* bytes 2176 - 2192 */ + 0x33, 0x01, 0x3a, 0x00, 0x33, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 2192 - 2208 */ + 0x00, 0x0b, 0x00, 0x3c, 0x01, 0xcb, 0x01, 0xa8, 0x00, 0x19, 0x02, 0xc4, 0x00, 0x73, 0x01, 0x50, /* bytes 2208 - 2224 */ + 0x00, 0x0b, 0x01, 0xe9, 0x00, 0xed, 0x00, 0x3c, 0x01, 0x67, 0x00, 0x8f, 0x01, 0x02, 0x01, 0x28, /* bytes 2224 - 2240 */ + 0x02, 0x22, 0x01, 0xb4, 0x01, 0x73, 0x01, 0xd0, 0x01, 0x19, 0x02, 0x3c, 0x01, 0xcb, 0x01, 0x03, /* bytes 2240 - 2256 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x84, 0x01, 0x5f, 0x01, 0x89, 0x01, /* bytes 2256 - 2272 */ + 0x19, 0x01, 0x8e, 0x01, 0xf9, 0x00, 0x88, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2272 - 2288 */ + 0x00, 0x3e, 0x02, 0x34, 0x01, 0x46, 0x02, 0x34, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2288 - 2304 */ + 0x02, 0x00, 0xa4, 0x00, 0xbb, 0x00, 0x93, 0x00, 0xab, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2304 - 2320 */ + 0x00, 0x02, 0x00, 0xd4, 0x01, 0xba, 0x00, 0xe5, 0x01, 0xaa, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2320 - 2336 */ + 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x1f, 0x01, 0x5e, 0x01, 0x3f, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 2336 - 2352 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, /* bytes 2352 - 2368 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x35, 0x01, 0x3a, 0x00, 0x35, 0x01, 0x03, 0x00, /* bytes 2368 - 2384 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf8, 0x01, 0x9c, 0x01, 0x07, 0x02, 0xaa, 0x01, 0x03, /* bytes 2384 - 2400 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x10, 0x01, 0x1e, 0x01, 0x44, 0x01, /* bytes 2400 - 2416 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xb5, 0x01, 0x74, /* bytes 2416 - 2432 */ + 0x01, 0xcf, 0x01, 0x17, 0x02, 0x3c, 0x01, 0xcc, 0x01, 0xa9, 0x00, 0x17, 0x02, 0xc3, 0x00, 0x74, /* bytes 2432 - 2448 */ + 0x01, 0x52, 0x00, 0x2f, 0x01, 0xe8, 0x00, 0x0f, 0x01, 0x3c, 0x01, 0x81, 0x00, 0x90, 0x01, 0x18, /* bytes 2448 - 2464 */ + 0x01, 0x26, 0x02, 0x39, 0x01, 0xb5, 0x01, 0x74, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2464 - 2480 */ + 0x04, 0x00, 0x7f, 0x01, 0x87, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, 0x94, 0x01, 0xf9, 0x00, /* bytes 2480 - 2496 */ + 0x8a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x01, 0xbf, 0x00, 0xe5, /* bytes 2496 - 2512 */ + 0x01, 0xaf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x2e, 0x01, /* bytes 2512 - 2528 */ + 0x64, 0x01, 0x3d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xc3, /* bytes 2528 - 2544 */ + 0x00, 0x93, 0x00, 0xb3, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, /* bytes 2544 - 2560 */ + 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, /* bytes 2560 - 2576 */ + 0x01, 0x2c, 0x01, 0x12, 0x01, 0x4b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2576 - 2592 */ + 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2592 - 2608 */ + 0x00, 0x46, 0x02, 0x39, 0x01, 0x3e, 0x02, 0x39, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2608 - 2624 */ + 0x02, 0x00, 0x3a, 0x00, 0x3a, 0x01, 0x32, 0x00, 0x3a, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 2624 - 2640 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x55, 0x00, 0x47, 0x01, 0xe7, 0x00, 0x25, 0x01, 0x3c, /* bytes 2640 - 2656 */ + 0x01, 0x91, 0x00, 0x91, 0x01, 0x25, 0x01, 0x23, 0x02, 0x47, 0x01, 0xb6, 0x01, 0x74, 0x01, 0xcd, /* bytes 2656 - 2672 */ + 0x01, 0x15, 0x02, 0x3c, 0x01, 0xcd, 0x01, 0xab, 0x00, 0x15, 0x02, 0xc2, 0x00, 0x74, 0x01, 0x55, /* bytes 2672 - 2688 */ + 0x00, 0x47, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xf9, 0x00, 0x8c, 0x01, /* bytes 2688 - 2704 */ + 0x19, 0x01, 0x96, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x7f, 0x01, 0x88, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 2704 - 2720 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x00, 0x41, 0x01, 0x32, 0x00, 0x41, 0x01, 0x03, 0x00, 0xc0, /* bytes 2720 - 2736 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x46, 0x02, 0x3f, 0x01, 0x3e, 0x02, 0x3f, 0x01, 0x03, 0x00, /* bytes 2736 - 2752 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x35, 0x01, 0x0f, 0x01, 0x4d, 0x01, 0x03, /* bytes 2752 - 2768 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x66, 0x01, 0x3c, 0x01, 0x58, 0x01, 0x35, 0x01, /* bytes 2768 - 2784 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, /* bytes 2784 - 2800 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe5, 0x01, 0xb6, 0x00, 0xd4, 0x01, /* bytes 2800 - 2816 */ + 0xc6, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x93, 0x00, 0xbd, 0x00, 0xa4, /* bytes 2816 - 2832 */ + 0x00, 0xcd, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf8, 0x01, 0x9c, 0x01, /* bytes 2832 - 2848 */ + 0x07, 0x02, 0xaa, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 2848 - 2864 */ + 0x00, 0xb7, 0x01, 0x74, 0x01, 0xcc, 0x01, 0x13, 0x02, 0x3c, 0x01, 0xce, 0x01, 0xac, 0x00, 0x13, /* bytes 2864 - 2880 */ + 0x02, 0xc1, 0x00, 0x74, 0x01, 0x58, 0x00, 0x51, 0x01, 0xe7, 0x00, 0x2f, 0x01, 0x3c, 0x01, 0x9b, /* bytes 2880 - 2896 */ + 0x00, 0x91, 0x01, 0x2c, 0x01, 0x20, 0x02, 0x4f, 0x01, 0xb7, 0x01, 0x74, 0x01, 0x03, 0x00, 0xc0, /* bytes 2896 - 2912 */ + 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x89, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x19, 0x01, /* bytes 2912 - 2928 */ + 0x98, 0x01, 0xf9, 0x00, 0x8c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 2928 - 2944 */ + 0x01, 0x39, 0x01, 0x67, 0x01, 0x3b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2944 - 2960 */ + 0xe5, 0x01, 0xbc, 0x00, 0xd4, 0x01, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2960 - 2976 */ + 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2976 - 2992 */ + 0x02, 0x00, 0x46, 0x02, 0x43, 0x01, 0x3e, 0x02, 0x43, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2992 - 3008 */ + 0x00, 0x02, 0x00, 0xa4, 0x00, 0xd6, 0x00, 0x93, 0x00, 0xc6, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3008 - 3024 */ + 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3024 - 3040 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x47, 0x01, 0x3a, 0x00, 0x47, 0x01, 0x03, 0x00, 0xc0, /* bytes 3040 - 3056 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x39, 0x01, 0x0d, 0x01, 0x4e, 0x01, 0x23, 0x00, /* bytes 3056 - 3072 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xb8, 0x01, 0x75, 0x01, 0xcb, /* bytes 3072 - 3088 */ + 0x01, 0x12, 0x02, 0x3c, 0x01, 0xcf, 0x01, 0xad, 0x00, 0x12, 0x02, 0xc0, 0x00, 0x75, 0x01, 0x59, /* bytes 3088 - 3104 */ + 0x00, 0x54, 0x01, 0xe6, 0x00, 0x31, 0x01, 0x3c, 0x01, 0xa0, 0x00, 0x92, 0x01, 0x30, 0x01, 0x1f, /* bytes 3104 - 3120 */ + 0x02, 0x53, 0x01, 0xb8, 0x01, 0x75, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 3120 - 3136 */ + 0xf9, 0x00, 0x8c, 0x01, 0x19, 0x01, 0x98, 0x01, 0x5f, 0x01, 0x89, 0x01, 0x7f, 0x01, 0x89, 0x01, /* bytes 3136 - 3152 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x3a, 0x01, 0x68, 0x01, 0x3b, /* bytes 3152 - 3168 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xda, 0x00, 0x93, 0x00, /* bytes 3168 - 3184 */ + 0xca, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, /* bytes 3184 - 3200 */ + 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x02, 0x46, 0x01, /* bytes 3200 - 3216 */ + 0x46, 0x02, 0x46, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, /* bytes 3216 - 3232 */ + 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, /* bytes 3232 - 3248 */ + 0x3a, 0x01, 0x0d, 0x01, 0x4e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, /* bytes 3248 - 3264 */ + 0x00, 0x4a, 0x01, 0x3a, 0x00, 0x4a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3264 - 3280 */ + 0xd4, 0x01, 0xcf, 0x00, 0xe5, 0x01, 0xbf, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3280 - 3296 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0x92, 0x01, 0x31, 0x01, 0x1e, 0x02, 0x54, 0x01, 0xb8, 0x01, 0x75, /* bytes 3296 - 3312 */ + 0x01, 0xcb, 0x01, 0x12, 0x02, 0x3c, 0x01, 0xcf, 0x01, 0xad, 0x00, 0x12, 0x02, 0xc0, 0x00, 0x75, /* bytes 3312 - 3328 */ + 0x01, 0x59, 0x00, 0x31, 0x01, 0xe6, 0x00, 0x29, 0x01, 0x3c, 0x01, 0xa2, 0x00, 0x92, 0x01, 0x31, /* bytes 3328 - 3344 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xdf, 0x00, 0x54, 0x01, 0x19, 0x01, /* bytes 3344 - 3360 */ + 0x90, 0x01, 0x60, 0x01, 0x7d, 0x01, 0xa6, 0x01, 0x47, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3360 - 3376 */ + 0x00, 0x02, 0x00, 0x64, 0x01, 0x56, 0x01, 0x64, 0x01, 0x10, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3376 - 3392 */ + 0x01, 0x00, 0x02, 0x00, 0x46, 0x02, 0x47, 0x01, 0x3e, 0x02, 0x47, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3392 - 3408 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x00, 0x9c, 0x01, 0x71, 0x00, 0xaa, 0x01, 0x03, 0x00, 0xc0, /* bytes 3408 - 3424 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x17, 0x01, 0x4f, 0x01, 0x24, 0x01, 0x1f, 0x01, 0x03, 0x00, /* bytes 3424 - 3440 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x02, 0xaa, 0x01, 0xf8, 0x01, 0x9c, 0x01, 0x03, /* bytes 3440 - 3456 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa4, 0x00, 0xdc, 0x00, 0x93, 0x00, 0xcc, 0x00, /* bytes 3456 - 3472 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe5, 0x01, 0xc0, 0x00, 0xd4, 0x01, 0xd0, /* bytes 3472 - 3488 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x00, 0x4b, 0x01, 0x32, 0x00, /* bytes 3488 - 3504 */ + 0x4b, 0x01, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x88, /* bytes 3504 - 3520 */ + 0x00, 0xfc, 0x00, 0x5e, 0x00, 0x7e, 0x00, 0x09, 0x01, 0x5b, 0x00, 0xba, 0x01, 0xc0, 0xff, 0xe0, /* bytes 3520 - 3536 */ + 0x01, 0x61, 0x00, 0x9f, 0x02, 0x99, 0x00, 0xc8, 0x01, 0x1e, 0x01, 0x7f, 0x01, 0x1d, 0x02, 0xe0, /* bytes 3536 - 3552 */ + 0x00, 0x8c, 0x01, 0x05, 0x00, 0xca, 0x01, 0x88, 0x00, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3552 - 3568 */ + 0x01, 0x00, 0x04, 0x00, 0x83, 0x01, 0xef, 0x00, 0x30, 0x01, 0x1c, 0x01, 0xd0, 0x00, 0x18, 0x01, /* bytes 3568 - 3584 */ + 0xb3, 0x00, 0xd0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, 0x01, 0x67, /* bytes 3584 - 3600 */ + 0x00, 0x59, 0x01, 0xbc, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0b, 0x01, /* bytes 3600 - 3616 */ + 0xb3, 0x00, 0x2c, 0x01, 0x6a, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 3616 - 3632 */ + 0x00, 0x0b, 0x00, 0x6d, 0x00, 0x5b, 0x00, 0x18, 0x01, 0x34, 0x00, 0xc8, 0x01, 0x9b, 0xff, 0xf2, /* bytes 3632 - 3648 */ + 0x01, 0x36, 0x00, 0xb1, 0x02, 0x6a, 0x00, 0xdd, 0x01, 0xf0, 0x00, 0x96, 0x01, 0xe8, 0x01, 0xf4, /* bytes 3648 - 3664 */ + 0x00, 0x60, 0x01, 0x18, 0x00, 0xa0, 0x01, 0x98, 0x00, 0xd5, 0x00, 0x6d, 0x00, 0x5b, 0x00, 0x03, /* bytes 3664 - 3680 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd0, 0x00, 0xba, 0x00, 0xe4, 0x00, 0xea, 0x00, /* bytes 3680 - 3696 */ + 0x42, 0x01, 0xf1, 0x00, 0x85, 0x01, 0xc9, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 3696 - 3712 */ + 0x00, 0xe2, 0x00, 0xc7, 0xff, 0xe1, 0x00, 0xc5, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 3712 - 3728 */ + 0x02, 0x00, 0x3c, 0x01, 0x4a, 0x00, 0x1b, 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3728 - 3744 */ + 0x00, 0x02, 0x00, 0x64, 0x01, 0x93, 0x00, 0x84, 0x01, 0x4a, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3744 - 3760 */ + 0x01, 0x00, 0x02, 0x00, 0x8d, 0x02, 0xb2, 0xff, 0x87, 0x02, 0xb5, 0xff, 0x03, 0x00, 0xc0, 0x03, /* bytes 3760 - 3776 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, 0x02, 0x0e, 0x01, 0x4b, 0x02, 0x13, 0x01, 0x03, 0x00, 0xc0, /* bytes 3776 - 3792 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0d, 0x03, 0x66, 0x00, 0x09, 0x03, 0x66, 0x00, 0x03, 0x00, /* bytes 3792 - 3808 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x54, 0x00, 0x1b, 0x00, 0x54, 0x00, 0x03, /* bytes 3808 - 3824 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x00, 0xdb, 0x00, 0x18, 0x00, 0xdf, 0x00, /* bytes 3824 - 3840 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x62, /* bytes 3840 - 3856 */ + 0x01, 0x1a, 0x00, 0xa0, 0x01, 0x96, 0x00, 0xd7, 0x00, 0x6c, 0x00, 0x5d, 0x00, 0x16, 0x01, 0x35, /* bytes 3856 - 3872 */ + 0x00, 0xc5, 0x01, 0x9f, 0xff, 0xf2, 0x01, 0x36, 0x00, 0xae, 0x02, 0x6b, 0x00, 0xde, 0x01, 0xf1, /* bytes 3872 - 3888 */ + 0x00, 0x98, 0x01, 0xe6, 0x01, 0xf4, 0x00, 0x62, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 3888 - 3904 */ + 0x04, 0x00, 0x82, 0x01, 0xcb, 0x00, 0x43, 0x01, 0xf3, 0x00, 0xe5, 0x00, 0xeb, 0x00, 0xd2, 0x00, /* bytes 3904 - 3920 */ + 0xbf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x42, 0x02, 0x0a, 0x01, 0x49, /* bytes 3920 - 3936 */ + 0x02, 0x14, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x23, 0x00, 0xdc, 0x00, /* bytes 3936 - 3952 */ + 0x19, 0x00, 0xe2, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xdf, 0x00, 0xc8, /* bytes 3952 - 3968 */ + 0xff, 0xe2, 0x00, 0xce, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x22, 0x00, /* bytes 3968 - 3984 */ + 0x57, 0x00, 0x1d, 0x00, 0x57, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, /* bytes 3984 - 4000 */ + 0x03, 0x67, 0x00, 0xfb, 0x02, 0x67, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4000 - 4016 */ + 0x63, 0x01, 0x94, 0x00, 0x82, 0x01, 0x4f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4016 - 4032 */ + 0x00, 0x8a, 0x02, 0xb5, 0xff, 0x7e, 0x02, 0xbc, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 4032 - 4048 */ + 0x02, 0x00, 0x1b, 0x01, 0x91, 0x00, 0x3b, 0x01, 0x4e, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 4048 - 4064 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xa4, 0x02, 0x7d, 0x00, 0xd8, 0x01, 0x04, 0x01, 0x92, /* bytes 4064 - 4080 */ + 0x01, 0xf8, 0x01, 0xed, 0x00, 0x76, 0x01, 0x14, 0x00, 0xb2, 0x01, 0x8e, 0x00, 0xe8, 0x00, 0x65, /* bytes 4080 - 4096 */ + 0x00, 0x6d, 0x00, 0x0e, 0x01, 0x45, 0x00, 0xbc, 0x01, 0xb0, 0xff, 0xeb, 0x01, 0x47, 0x00, 0xa4, /* bytes 4096 - 4112 */ + 0x02, 0x7d, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xcb, 0x00, 0xd0, 0x00, /* bytes 4112 - 4128 */ + 0xdf, 0x00, 0xfc, 0x00, 0x3d, 0x01, 0x04, 0x01, 0x7c, 0x01, 0xdd, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 4128 - 4144 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x01, 0xa5, 0x00, 0x7c, 0x01, 0x5f, 0x00, 0x03, 0x00, 0xc0, /* bytes 4144 - 4160 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x02, 0x7c, 0x00, 0xdf, 0x02, 0x7c, 0x00, 0x03, 0x00, /* bytes 4160 - 4176 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd9, 0x00, 0xdc, 0xff, 0xde, 0x00, 0xe4, 0xff, 0x03, /* bytes 4176 - 4192 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7a, 0x02, 0xcc, 0xff, 0x69, 0x02, 0xd7, 0xff, /* bytes 4192 - 4208 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x39, 0x02, 0x27, 0x01, 0x30, 0x02, 0x18, /* bytes 4208 - 4224 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x69, 0x00, 0x24, 0x00, /* bytes 4224 - 4240 */ + 0x6a, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x00, 0xf4, 0x00, 0x27, /* bytes 4240 - 4256 */ + 0x00, 0xea, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x01, 0xa2, 0x00, /* bytes 4256 - 4272 */ + 0x34, 0x01, 0x5e, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 4272 - 4288 */ + 0x00, 0x9d, 0x02, 0x8e, 0x00, 0xd3, 0x01, 0x15, 0x01, 0x8d, 0x01, 0x0a, 0x02, 0xe7, 0x00, 0x87, /* bytes 4288 - 4304 */ + 0x01, 0x0e, 0x00, 0xc3, 0x01, 0x87, 0x00, 0xf9, 0x00, 0x5d, 0x00, 0x7d, 0x00, 0x07, 0x01, 0x54, /* bytes 4304 - 4320 */ + 0x00, 0xb4, 0x01, 0xbf, 0xff, 0xe5, 0x01, 0x57, 0x00, 0x9d, 0x02, 0x8e, 0x00, 0x03, 0x00, 0xc0, /* bytes 4320 - 4336 */ + 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x76, 0x01, 0xed, 0x00, 0x37, 0x01, 0x15, 0x01, 0xd9, 0x00, /* bytes 4336 - 4352 */ + 0x0c, 0x01, 0xc5, 0x00, 0xe0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, /* bytes 4352 - 4368 */ + 0x01, 0xb5, 0x00, 0x76, 0x01, 0x6f, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4368 - 4384 */ + 0x2e, 0x01, 0x6d, 0x00, 0x0e, 0x01, 0xb1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4384 - 4400 */ + 0x00, 0xea, 0x02, 0x8e, 0x00, 0xd9, 0x02, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 4400 - 4416 */ + 0x02, 0x00, 0x62, 0x02, 0xe7, 0xff, 0x7a, 0x02, 0xd8, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 4416 - 4432 */ + 0x00, 0x02, 0x00, 0xd0, 0x00, 0xe7, 0xff, 0xd6, 0x00, 0xf3, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4432 - 4448 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x79, 0x00, 0x13, 0x00, 0x79, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 4448 - 4464 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x00, 0xfb, 0x00, 0x0b, 0x00, 0x08, 0x01, 0x03, 0x00, 0xc0, /* bytes 4464 - 4480 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x38, 0x02, 0x3f, 0x01, 0x2b, 0x02, 0x2a, 0x01, 0x23, 0x00, /* bytes 4480 - 4496 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x8c, 0x01, 0x15, 0x02, 0xe5, /* bytes 4496 - 4512 */ + 0x00, 0x91, 0x01, 0x0b, 0x00, 0xcd, 0x01, 0x83, 0x00, 0x02, 0x01, 0x58, 0x00, 0x85, 0x00, 0x02, /* bytes 4512 - 4528 */ + 0x01, 0x5d, 0x00, 0xaf, 0x01, 0xc8, 0xff, 0xe1, 0x01, 0x5f, 0x00, 0x9a, 0x02, 0x97, 0x00, 0xd0, /* bytes 4528 - 4544 */ + 0x01, 0x1e, 0x01, 0x8c, 0x01, 0x15, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 4544 - 4560 */ + 0xc2, 0x00, 0xe9, 0x00, 0xd7, 0x00, 0x15, 0x01, 0x35, 0x01, 0x1e, 0x01, 0x74, 0x01, 0xf6, 0x00, /* bytes 4560 - 4576 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x02, 0xdd, 0xff, 0x5f, 0x02, 0xef, /* bytes 4576 - 4592 */ + 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x15, 0x01, 0x1b, 0x00, /* bytes 4592 - 4608 */ + 0x05, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd1, 0x00, 0xfb, 0xff, 0xc9, /* bytes 4608 - 4624 */ + 0x00, 0xed, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0x02, 0x98, 0x00, /* bytes 4624 - 4640 */ + 0xd7, 0x02, 0x97, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x02, 0x4e, /* bytes 4640 - 4656 */ + 0x01, 0x29, 0x02, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x73, 0x01, /* bytes 4656 - 4672 */ + 0x77, 0x00, 0x54, 0x01, 0xbd, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0b, /* bytes 4672 - 4688 */ + 0x00, 0x82, 0x00, 0x17, 0x00, 0x82, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4688 - 4704 */ + 0x2b, 0x01, 0x76, 0x00, 0x0b, 0x01, 0xba, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 4704 - 4720 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x60, 0x00, 0x55, 0x00, 0x88, 0x00, 0x82, 0x00, 0x05, /* bytes 4720 - 4736 */ + 0x01, 0x09, 0x00, 0xd2, 0x01, 0xe5, 0x00, 0x93, 0x01, 0x8f, 0x01, 0x19, 0x02, 0xd0, 0x01, 0x20, /* bytes 4736 - 4752 */ + 0x01, 0x9b, 0x02, 0x99, 0x00, 0xdf, 0x01, 0x62, 0x00, 0xad, 0x01, 0xc9, 0xff, 0x00, 0x01, 0x60, /* bytes 4752 - 4768 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x75, 0x01, 0xf7, 0x00, 0x36, 0x01, /* bytes 4768 - 4784 */ + 0x1f, 0x01, 0xd7, 0x00, 0x17, 0x01, 0xc2, 0x00, 0xeb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 4784 - 4800 */ + 0x00, 0x02, 0x00, 0xce, 0x00, 0xfe, 0xff, 0xc4, 0x00, 0xec, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4800 - 4816 */ + 0x01, 0x00, 0x02, 0x00, 0x72, 0x01, 0x79, 0x00, 0x54, 0x01, 0xbf, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 4816 - 4832 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x00, 0x08, 0x01, 0xfa, 0xff, 0x1c, 0x01, 0x03, 0x00, 0xc0, /* bytes 4832 - 4848 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2a, 0x01, 0x78, 0x00, 0x0b, 0x01, 0xbc, 0x00, 0x03, 0x00, /* bytes 4848 - 4864 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd8, 0x02, 0x99, 0x00, 0xf1, 0x02, 0x9a, 0x00, 0x03, /* bytes 4864 - 4880 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2b, 0x02, 0x37, 0x01, 0x40, 0x02, 0x56, 0x01, /* bytes 4880 - 4896 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x00, 0x85, 0x00, 0x06, 0x00, 0x85, /* bytes 4896 - 4912 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5f, 0x02, 0xf1, 0xff, 0x82, 0x02, /* bytes 4912 - 4928 */ + 0xdb, 0xff, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xd1, /* bytes 4928 - 4944 */ + 0x01, 0x1e, 0x01, 0x93, 0x01, 0x19, 0x02, 0xe7, 0x00, 0x91, 0x01, 0x08, 0x00, 0xd3, 0x01, 0x83, /* bytes 4944 - 4960 */ + 0x00, 0x04, 0x01, 0x52, 0x00, 0x87, 0x00, 0x00, 0x01, 0x60, 0x00, 0xad, 0x01, 0xc7, 0xff, 0xdf, /* bytes 4960 - 4976 */ + 0x01, 0x61, 0x00, 0x9f, 0x02, 0x96, 0x00, 0xd1, 0x01, 0x1e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4976 - 4992 */ + 0x01, 0x00, 0x04, 0x00, 0x77, 0x01, 0xf5, 0x00, 0x38, 0x01, 0x1e, 0x01, 0xd8, 0x00, 0x16, 0x01, /* bytes 4992 - 5008 */ + 0xc3, 0x00, 0xea, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xcd, 0x00, 0xfd, /* bytes 5008 - 5024 */ + 0xff, 0xc1, 0x00, 0xe9, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x13, 0x00, /* bytes 5024 - 5040 */ + 0x85, 0x00, 0x02, 0x00, 0x85, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x73, /* bytes 5040 - 5056 */ + 0x01, 0x77, 0x00, 0x56, 0x01, 0xbd, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5056 - 5072 */ + 0x18, 0x00, 0x09, 0x01, 0xf4, 0xff, 0x20, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5072 - 5088 */ + 0x00, 0x2b, 0x01, 0x76, 0x00, 0x0c, 0x01, 0xba, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5088 - 5104 */ + 0x02, 0x00, 0x47, 0x02, 0x59, 0x01, 0x2f, 0x02, 0x36, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5104 - 5120 */ + 0x00, 0x02, 0x00, 0x88, 0x02, 0xd5, 0xff, 0x60, 0x02, 0xef, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 5120 - 5136 */ + 0x01, 0x00, 0x02, 0x00, 0xdb, 0x02, 0x96, 0x00, 0xf8, 0x02, 0x97, 0x00, 0x23, 0x00, 0x0a, 0x00, /* bytes 5136 - 5152 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xea, 0x00, 0x8f, 0x01, 0x08, 0x00, 0xd4, /* bytes 5152 - 5168 */ + 0x01, 0x84, 0x00, 0x03, 0x01, 0x50, 0x00, 0x86, 0x00, 0x00, 0x01, 0x5f, 0x00, 0xac, 0x01, 0xc4, /* bytes 5168 - 5184 */ + 0xff, 0xde, 0x01, 0x60, 0x00, 0xa3, 0x02, 0x92, 0x00, 0xd2, 0x01, 0x1b, 0x01, 0x98, 0x01, 0x18, /* bytes 5184 - 5200 */ + 0x02, 0xea, 0x00, 0x8f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xc5, 0x00, /* bytes 5200 - 5216 */ + 0xe7, 0x00, 0xda, 0x00, 0x13, 0x01, 0x3b, 0x01, 0x1b, 0x01, 0x7a, 0x01, 0xf2, 0x00, 0x03, 0x00, /* bytes 5216 - 5232 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x17, 0x00, 0x08, 0x01, 0xef, 0xff, 0x23, 0x01, 0x03, /* bytes 5232 - 5248 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0x02, 0x93, 0x00, 0xde, 0x02, 0x93, 0x00, /* bytes 5248 - 5264 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4f, 0x02, 0x5b, 0x01, 0x33, 0x02, 0x33, /* bytes 5264 - 5280 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbe, 0x00, 0xe4, 0xff, 0xcc, 0x00, /* bytes 5280 - 5296 */ + 0xfb, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0e, 0x01, 0xb8, 0x00, 0x2c, /* bytes 5296 - 5312 */ + 0x01, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x11, 0x00, 0x84, 0x00, /* bytes 5312 - 5328 */ + 0xff, 0xff, 0x84, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0xbb, /* bytes 5328 - 5344 */ + 0x00, 0x75, 0x01, 0x75, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x62, 0x02, /* bytes 5344 - 5360 */ + 0xec, 0xff, 0x8f, 0x02, 0xce, 0xff, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 5360 - 5376 */ + 0x00, 0x0b, 0x00, 0x85, 0x00, 0x02, 0x01, 0x4f, 0x00, 0x85, 0x00, 0x00, 0x01, 0x5e, 0x00, 0xac, /* bytes 5376 - 5392 */ + 0x01, 0xc2, 0xff, 0xde, 0x01, 0x5e, 0x00, 0xa5, 0x02, 0x8f, 0x00, 0xd4, 0x01, 0x19, 0x01, 0x9c, /* bytes 5392 - 5408 */ + 0x01, 0x16, 0x02, 0xec, 0x00, 0x8d, 0x01, 0x08, 0x00, 0xd4, 0x01, 0x85, 0x00, 0x02, 0x01, 0x03, /* bytes 5408 - 5424 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7c, 0x01, 0xef, 0x00, 0x3d, 0x01, 0x18, 0x01, /* bytes 5424 - 5440 */ + 0xdc, 0x00, 0x11, 0x01, 0xc6, 0x00, 0xe5, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5440 - 5456 */ + 0x00, 0xbb, 0x00, 0xe1, 0xff, 0xca, 0x00, 0xf9, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5456 - 5472 */ + 0x02, 0x00, 0x16, 0x00, 0x09, 0x01, 0xea, 0xff, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5472 - 5488 */ + 0x00, 0x02, 0x00, 0x10, 0x00, 0x84, 0x00, 0xfb, 0xff, 0x83, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 5488 - 5504 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0xe9, 0xff, 0x95, 0x02, 0xc8, 0xff, 0x03, 0x00, 0xc0, 0x03, /* bytes 5504 - 5520 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x0f, 0x01, 0xb6, 0x00, 0x2d, 0x01, 0x72, 0x00, 0x03, 0x00, 0xc0, /* bytes 5520 - 5536 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, 0x02, 0x5d, 0x01, 0x37, 0x02, 0x31, 0x01, 0x03, 0x00, /* bytes 5536 - 5552 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x76, 0x01, 0x72, 0x00, 0x59, 0x01, 0xb8, 0x00, 0x03, /* bytes 5552 - 5568 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x03, 0x90, 0x00, 0xe1, 0x02, 0x8f, 0x00, /* bytes 5568 - 5584 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x4d, 0x00, 0x86, /* bytes 5584 - 5600 */ + 0x00, 0xff, 0x00, 0x5e, 0x00, 0xaa, 0x01, 0xc2, 0xff, 0xdf, 0x01, 0x5d, 0x00, 0xa6, 0x02, 0x8e, /* bytes 5600 - 5616 */ + 0x00, 0xd6, 0x01, 0x17, 0x01, 0xa0, 0x01, 0x14, 0x02, 0xee, 0x00, 0x8c, 0x01, 0x09, 0x00, 0xd4, /* bytes 5616 - 5632 */ + 0x01, 0x85, 0x00, 0x02, 0x01, 0x4d, 0x00, 0x86, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5632 - 5648 */ + 0x04, 0x00, 0xc7, 0x00, 0xe4, 0x00, 0xde, 0x00, 0x10, 0x01, 0x40, 0x01, 0x16, 0x01, 0x7e, 0x01, /* bytes 5648 - 5664 */ + 0xee, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2d, 0x01, 0x71, 0x00, 0x10, /* bytes 5664 - 5680 */ + 0x01, 0xb4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, 0x01, 0x71, 0x00, /* bytes 5680 - 5696 */ + 0x5b, 0x01, 0xb7, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf8, 0xff, 0x83, /* bytes 5696 - 5712 */ + 0x00, 0x0e, 0x00, 0x84, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x14, 0x00, /* bytes 5712 - 5728 */ + 0x0a, 0x01, 0xe4, 0xff, 0x29, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5e, /* bytes 5728 - 5744 */ + 0x02, 0x60, 0x01, 0x3b, 0x02, 0x31, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5744 - 5760 */ + 0x9b, 0x02, 0xc3, 0xff, 0x65, 0x02, 0xe6, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5760 - 5776 */ + 0x00, 0xc8, 0x00, 0xf8, 0xff, 0xb8, 0x00, 0xde, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5776 - 5792 */ + 0x02, 0x00, 0xe4, 0x02, 0x8d, 0x00, 0x0c, 0x03, 0x8e, 0x00, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, /* bytes 5792 - 5808 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xd7, 0x01, 0x17, 0x01, 0xa3, 0x01, 0x12, 0x02, 0xef, /* bytes 5808 - 5824 */ + 0x00, 0x8c, 0x01, 0x0a, 0x00, 0xd4, 0x01, 0x84, 0x00, 0x02, 0x01, 0x4d, 0x00, 0x87, 0x00, 0xfe, /* bytes 5824 - 5840 */ + 0x00, 0x5e, 0x00, 0xa8, 0x01, 0xc3, 0xff, 0xdf, 0x01, 0x5d, 0x00, 0xa5, 0x02, 0x8d, 0x00, 0xd7, /* bytes 5840 - 5856 */ + 0x01, 0x17, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xc8, 0x00, 0xe4, 0x00, /* bytes 5856 - 5872 */ + 0xdf, 0x00, 0x0f, 0x01, 0x41, 0x01, 0x15, 0x01, 0x80, 0x01, 0xed, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 5872 - 5888 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x12, 0x00, 0x0c, 0x01, 0xdf, 0xff, 0x2d, 0x01, 0x03, 0x00, 0xc0, /* bytes 5888 - 5904 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa0, 0x02, 0xbf, 0xff, 0x67, 0x02, 0xe4, 0xff, 0x03, 0x00, /* bytes 5904 - 5920 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x00, 0xdb, 0xff, 0xc6, 0x00, 0xf8, 0xff, 0x03, /* bytes 5920 - 5936 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x84, 0x00, 0xf4, 0xff, 0x84, 0x00, /* bytes 5936 - 5952 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x11, 0x03, 0x8c, 0x00, 0xe7, 0x02, 0x8c, /* bytes 5952 - 5968 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x65, 0x02, 0x63, 0x01, 0x3f, 0x02, /* bytes 5968 - 5984 */ + 0x31, 0x01, 0x23, 0x00, 0x08, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xda, /* bytes 5984 - 6000 */ + 0x01, 0x16, 0x01, 0xa5, 0x01, 0x0f, 0x02, 0xf0, 0x00, 0x8e, 0x01, 0x0d, 0x00, 0xd4, 0x01, 0x83, /* bytes 6000 - 6016 */ + 0x00, 0x03, 0x01, 0x4c, 0x00, 0x88, 0x00, 0xfc, 0x00, 0x5e, 0x00, 0xa6, 0x01, 0xc5, 0xff, 0xdf, /* bytes 6016 - 6032 */ + 0x01, 0x5c, 0x00, 0xa3, 0x02, 0x8d, 0x00, 0xda, 0x01, 0x16, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6032 - 6048 */ + 0x01, 0x00, 0x04, 0x00, 0x82, 0x01, 0xec, 0x00, 0x43, 0x01, 0x15, 0x01, 0xe1, 0x00, 0x0f, 0x01, /* bytes 6048 - 6064 */ + 0xc9, 0x00, 0xe4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x02, 0x67, /* bytes 6064 - 6080 */ + 0x01, 0x43, 0x02, 0x32, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x69, 0x02, /* bytes 6080 - 6096 */ + 0xe3, 0xff, 0xa5, 0x02, 0xbb, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd9, /* bytes 6096 - 6112 */ + 0xff, 0x32, 0x01, 0x0f, 0x00, 0x0e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6112 - 6128 */ + 0x09, 0x00, 0x85, 0x00, 0xf0, 0xff, 0x85, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 6128 - 6144 */ + 0x00, 0x17, 0x03, 0x8b, 0x00, 0xea, 0x02, 0x8b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6144 - 6160 */ + 0x02, 0x00, 0xc4, 0x00, 0xf7, 0xff, 0xb1, 0x00, 0xda, 0xff, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 6160 - 6176 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xa4, 0x01, 0xc7, 0xff, 0xe0, 0x01, 0x5b, 0x00, 0xa1, /* bytes 6176 - 6192 */ + 0x02, 0x8d, 0x00, 0xdc, 0x01, 0x16, 0x01, 0xa7, 0x01, 0x0c, 0x02, 0xf1, 0x00, 0x8f, 0x01, 0x0f, /* bytes 6192 - 6208 */ + 0x00, 0xd3, 0x01, 0x82, 0x00, 0x04, 0x01, 0x4c, 0x00, 0x89, 0x00, 0xfb, 0x00, 0x5e, 0x00, 0xa4, /* bytes 6208 - 6224 */ + 0x01, 0xc7, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xc9, 0x00, 0xe3, 0x00, /* bytes 6224 - 6240 */ + 0xe2, 0x00, 0x0f, 0x01, 0x45, 0x01, 0x14, 0x01, 0x83, 0x01, 0xeb, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 6240 - 6256 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x5e, 0x01, 0xb5, 0x00, 0x6d, 0x01, 0x8c, 0x00, 0x03, 0x00, 0xc0, /* bytes 6256 - 6272 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x11, 0x01, 0xd4, 0xff, 0x36, 0x01, 0x03, 0x00, /* bytes 6272 - 6288 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0x02, 0x33, 0x01, 0x73, 0x02, 0x6b, 0x01, 0x03, /* bytes 6288 - 6304 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6c, 0x02, 0xe1, 0xff, 0xa9, 0x02, 0xb8, 0xff, /* bytes 6304 - 6320 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x06, 0x00, 0x86, 0x00, 0xec, 0xff, 0x86, /* bytes 6320 - 6336 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x03, 0x8a, 0x00, 0xee, 0x02, /* bytes 6336 - 6352 */ + 0x8a, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xad, 0x00, 0xd8, 0xff, 0xc1, /* bytes 6352 - 6368 */ + 0x00, 0xf6, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x12, 0x01, 0xb3, 0x00, /* bytes 6368 - 6384 */ + 0x22, 0x01, 0x8c, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 6384 - 6400 */ + 0x00, 0xa1, 0x02, 0x8c, 0x00, 0xdd, 0x01, 0x16, 0x01, 0xaa, 0x01, 0x0b, 0x02, 0xf2, 0x00, 0x8f, /* bytes 6400 - 6416 */ + 0x01, 0x11, 0x00, 0xd3, 0x01, 0x81, 0x00, 0x05, 0x01, 0x4b, 0x00, 0x8a, 0x00, 0xfa, 0x00, 0x5e, /* bytes 6416 - 6432 */ + 0x00, 0xa2, 0x01, 0xc8, 0xff, 0xe0, 0x01, 0x5b, 0x00, 0xa1, 0x02, 0x8c, 0x00, 0x03, 0x00, 0xc0, /* bytes 6432 - 6448 */ + 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xca, 0x00, 0xe3, 0x00, 0xe3, 0x00, 0x0e, 0x01, 0x47, 0x01, /* bytes 6448 - 6464 */ + 0x13, 0x01, 0x85, 0x01, 0xea, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6f, /* bytes 6464 - 6480 */ + 0x02, 0xde, 0xff, 0xae, 0x02, 0xb4, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6480 - 6496 */ + 0x7d, 0x01, 0x65, 0x00, 0x5f, 0x01, 0xb4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 6496 - 6512 */ + 0x00, 0x7a, 0x02, 0x6e, 0x01, 0x4c, 0x02, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6512 - 6528 */ + 0x02, 0x00, 0x09, 0x00, 0x14, 0x01, 0xcf, 0xff, 0x3a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6528 - 6544 */ + 0x00, 0x02, 0x00, 0xe8, 0xff, 0x87, 0x00, 0x03, 0x00, 0x87, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6544 - 6560 */ + 0x01, 0x00, 0x02, 0x00, 0x32, 0x01, 0x65, 0x00, 0x13, 0x01, 0xb3, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 6560 - 6576 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x21, 0x03, 0x89, 0x00, 0xf1, 0x02, 0x88, 0x00, 0x03, 0x00, 0xc0, /* bytes 6576 - 6592 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbf, 0x00, 0xf5, 0xff, 0xa9, 0x00, 0xd6, 0xff, 0x23, 0x00, /* bytes 6592 - 6608 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xdf, 0x01, 0x15, 0x01, 0xad, /* bytes 6608 - 6624 */ + 0x01, 0x0a, 0x02, 0xf3, 0x00, 0x8f, 0x01, 0x11, 0x00, 0xd4, 0x01, 0x81, 0x00, 0x05, 0x01, 0x49, /* bytes 6624 - 6640 */ + 0x00, 0x8b, 0x00, 0xf9, 0x00, 0x5e, 0x00, 0xa0, 0x01, 0xc8, 0xff, 0xe0, 0x01, 0x5a, 0x00, 0xa1, /* bytes 6640 - 6656 */ + 0x02, 0x8b, 0x00, 0xdf, 0x01, 0x15, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 6656 - 6672 */ + 0xcb, 0x00, 0xe3, 0x00, 0xe4, 0x00, 0x0e, 0x01, 0x48, 0x01, 0x12, 0x01, 0x87, 0x01, 0xe9, 0x00, /* bytes 6672 - 6688 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x26, 0x03, 0x87, 0x00, 0xf5, 0x02, 0x87, /* bytes 6688 - 6704 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x00, 0x17, 0x01, 0xca, 0xff, /* bytes 6704 - 6720 */ + 0x3e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa6, 0x00, 0xd4, 0xff, 0xbc, /* bytes 6720 - 6736 */ + 0x00, 0xf4, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x52, 0x02, 0x37, 0x01, /* bytes 6736 - 6752 */ + 0x80, 0x02, 0x71, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb2, 0x02, 0xb1, /* bytes 6752 - 6768 */ + 0xff, 0x72, 0x02, 0xdb, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe4, 0xff, /* bytes 6768 - 6784 */ + 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x60, /* bytes 6784 - 6800 */ + 0x01, 0xb3, 0x00, 0x7a, 0x01, 0x6e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6800 - 6816 */ + 0x2f, 0x01, 0x6e, 0x00, 0x14, 0x01, 0xb2, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 6816 - 6832 */ + 0xff, 0x00, 0x00, 0x0b, 0x00, 0xf8, 0x00, 0x5f, 0x00, 0x47, 0x00, 0x8c, 0x00, 0x81, 0x00, 0x06, /* bytes 6832 - 6848 */ + 0x01, 0x11, 0x00, 0xd6, 0x01, 0xf5, 0x00, 0x8f, 0x01, 0xb0, 0x01, 0x0a, 0x02, 0xdf, 0x01, 0x14, /* bytes 6848 - 6864 */ + 0x01, 0xa3, 0x02, 0x8a, 0x00, 0xdf, 0x01, 0x5b, 0x00, 0x9f, 0x01, 0xc8, 0xff, 0xf8, 0x00, 0x5f, /* bytes 6864 - 6880 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xcc, 0x00, 0xe2, 0x00, 0xe6, 0x00, /* bytes 6880 - 6896 */ + 0x0d, 0x01, 0x4a, 0x01, 0x12, 0x01, 0x89, 0x01, 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6896 - 6912 */ + 0x00, 0x02, 0x00, 0xfc, 0xff, 0x89, 0x00, 0xe1, 0xff, 0x89, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6912 - 6928 */ + 0x01, 0x00, 0x02, 0x00, 0x2f, 0x01, 0x6e, 0x00, 0x15, 0x01, 0xb2, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 6928 - 6944 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xc6, 0xff, 0x42, 0x01, 0x00, 0x00, 0x1b, 0x01, 0x03, 0x00, 0xc0, /* bytes 6944 - 6960 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa2, 0x00, 0xd3, 0xff, 0xb8, 0x00, 0xf2, 0xff, 0x03, 0x00, /* bytes 6960 - 6976 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, 0x02, 0xd7, 0xff, 0xb5, 0x02, 0xad, 0xff, 0x03, /* bytes 6976 - 6992 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x02, 0x73, 0x01, 0x58, 0x02, 0x3a, 0x01, /* bytes 6992 - 7008 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x61, 0x01, 0xb2, 0x00, 0x7b, 0x01, 0x6d, /* bytes 7008 - 7024 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfa, 0x02, 0x86, 0x00, 0x2a, 0x03, /* bytes 7024 - 7040 */ + 0x86, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xf7, /* bytes 7040 - 7056 */ + 0x00, 0x60, 0x00, 0x44, 0x00, 0x8c, 0x00, 0x81, 0x00, 0x06, 0x01, 0x10, 0x00, 0xd9, 0x01, 0xf7, /* bytes 7056 - 7072 */ + 0x00, 0x8e, 0x01, 0xb5, 0x01, 0x0c, 0x02, 0xe0, 0x01, 0x14, 0x01, 0xa6, 0x02, 0x89, 0x00, 0xde, /* bytes 7072 - 7088 */ + 0x01, 0x5b, 0x00, 0x9e, 0x01, 0xc6, 0xff, 0xf7, 0x00, 0x60, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 7088 - 7104 */ + 0x01, 0x00, 0x04, 0x00, 0x8a, 0x01, 0xe7, 0x00, 0x4c, 0x01, 0x11, 0x01, 0xe7, 0x00, 0x0d, 0x01, /* bytes 7104 - 7120 */ + 0xcd, 0x00, 0xe2, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc2, 0xff, 0x45, /* bytes 7120 - 7136 */ + 0x01, 0xfa, 0xff, 0x1f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x15, 0x01, /* bytes 7136 - 7152 */ + 0xb1, 0x00, 0x2f, 0x01, 0x6d, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf8, /* bytes 7152 - 7168 */ + 0xff, 0x8a, 0x00, 0xdd, 0xff, 0x8a, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7168 - 7184 */ + 0x5f, 0x02, 0x3e, 0x01, 0x8d, 0x02, 0x75, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7184 - 7200 */ + 0x00, 0x2f, 0x03, 0x84, 0x00, 0x00, 0x03, 0x85, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 7200 - 7216 */ + 0x02, 0x00, 0x7b, 0x01, 0x6c, 0x00, 0x62, 0x01, 0xb1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7216 - 7232 */ + 0x00, 0x02, 0x00, 0x7c, 0x02, 0xd3, 0xff, 0xb9, 0x02, 0xaa, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 7232 - 7248 */ + 0x01, 0x00, 0x02, 0x00, 0x9f, 0x00, 0xd1, 0xff, 0xb5, 0x00, 0xef, 0xff, 0x23, 0x00, 0x0a, 0x00, /* bytes 7248 - 7264 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x82, 0x00, 0x06, 0x01, 0x41, 0x00, 0x8c, /* bytes 7264 - 7280 */ + 0x00, 0xf7, 0x00, 0x61, 0x00, 0x9d, 0x01, 0xc5, 0xff, 0xdd, 0x01, 0x5b, 0x00, 0xa9, 0x02, 0x87, /* bytes 7280 - 7296 */ + 0x00, 0xe0, 0x01, 0x13, 0x01, 0xb9, 0x01, 0x0d, 0x02, 0xf8, 0x00, 0x8d, 0x01, 0x0e, 0x00, 0xdb, /* bytes 7296 - 7312 */ + 0x01, 0x82, 0x00, 0x06, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x8c, 0x01, /* bytes 7312 - 7328 */ + 0xe6, 0x00, 0x4e, 0x01, 0x10, 0x01, 0xe8, 0x00, 0x0c, 0x01, 0xcd, 0x00, 0xe2, 0x00, 0x03, 0x00, /* bytes 7328 - 7344 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x67, 0x02, 0x43, 0x01, 0x93, 0x02, 0x77, 0x01, 0x03, /* bytes 7344 - 7360 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x6d, 0x00, 0x16, 0x01, 0xb0, 0x00, /* bytes 7360 - 7376 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0x6b, 0x00, 0x64, 0x01, 0xb1, /* bytes 7376 - 7392 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbb, 0x02, 0xa8, 0xff, 0x83, 0x02, /* bytes 7392 - 7408 */ + 0xce, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf3, 0xff, 0x8b, 0x00, 0xda, /* bytes 7408 - 7424 */ + 0xff, 0x8b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbe, 0xff, 0x49, 0x01, /* bytes 7424 - 7440 */ + 0xf3, 0xff, 0x25, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb0, 0x00, 0xec, /* bytes 7440 - 7456 */ + 0xff, 0x9c, 0x00, 0xd0, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x33, 0x03, /* bytes 7456 - 7472 */ + 0x83, 0x00, 0x07, 0x03, 0x83, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 7472 - 7488 */ + 0x00, 0x0b, 0x00, 0xf6, 0x00, 0x61, 0x00, 0x3f, 0x00, 0x8d, 0x00, 0x82, 0x00, 0x07, 0x01, 0x0e, /* bytes 7488 - 7504 */ + 0x00, 0xdd, 0x01, 0xfa, 0x00, 0x8d, 0x01, 0xbc, 0x01, 0x0d, 0x02, 0xe1, 0x01, 0x12, 0x01, 0xaa, /* bytes 7504 - 7520 */ + 0x02, 0x86, 0x00, 0xdd, 0x01, 0x5b, 0x00, 0x9c, 0x01, 0xc4, 0xff, 0xf6, 0x00, 0x61, 0x00, 0x03, /* bytes 7520 - 7536 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x8e, 0x01, 0xe5, 0x00, 0x50, 0x01, 0x0f, 0x01, /* bytes 7536 - 7552 */ + 0xea, 0x00, 0x0b, 0x01, 0xce, 0x00, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7552 - 7568 */ + 0x00, 0xab, 0x00, 0xe8, 0xff, 0x99, 0x00, 0xcf, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 7568 - 7584 */ + 0x02, 0x00, 0xbb, 0xff, 0x4c, 0x01, 0xea, 0xff, 0x2c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7584 - 7600 */ + 0x00, 0x02, 0x00, 0x65, 0x01, 0xb0, 0x00, 0x7c, 0x01, 0x6b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 7600 - 7616 */ + 0x01, 0x00, 0x02, 0x00, 0xd7, 0xff, 0x8c, 0x00, 0xed, 0xff, 0x8b, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 7616 - 7632 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x17, 0x01, 0xb0, 0x00, 0x30, 0x01, 0x6c, 0x00, 0x03, 0x00, 0xc0, /* bytes 7632 - 7648 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x36, 0x03, 0x82, 0x00, 0x0f, 0x03, 0x82, 0x00, 0x03, 0x00, /* bytes 7648 - 7664 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x99, 0x02, 0x78, 0x01, 0x71, 0x02, 0x4b, 0x01, 0x03, /* bytes 7664 - 7680 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8c, 0x02, 0xc7, 0xff, 0xbe, 0x02, 0xa5, 0xff, /* bytes 7680 - 7696 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x0f, 0x00, 0xde, /* bytes 7696 - 7712 */ + 0x01, 0x82, 0x00, 0x07, 0x01, 0x3d, 0x00, 0x8d, 0x00, 0xf5, 0x00, 0x62, 0x00, 0x9a, 0x01, 0xc5, /* bytes 7712 - 7728 */ + 0xff, 0xdc, 0x01, 0x5b, 0x00, 0xab, 0x02, 0x85, 0x00, 0xe2, 0x01, 0x11, 0x01, 0xc0, 0x01, 0x0d, /* bytes 7728 - 7744 */ + 0x02, 0xfb, 0x00, 0x8d, 0x01, 0x0f, 0x00, 0xde, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 7744 - 7760 */ + 0x04, 0x00, 0xcf, 0x00, 0xe1, 0x00, 0xeb, 0x00, 0x0b, 0x01, 0x52, 0x01, 0x0e, 0x01, 0x8f, 0x01, /* bytes 7760 - 7776 */ + 0xe4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe6, 0xff, 0x8c, 0x00, 0xd4, /* bytes 7776 - 7792 */ + 0xff, 0x8d, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9e, 0x02, 0x79, 0x01, /* bytes 7792 - 7808 */ + 0x7e, 0x02, 0x55, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7d, 0x01, 0x6a, /* bytes 7808 - 7824 */ + 0x00, 0x66, 0x01, 0xaf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x39, 0x03, /* bytes 7824 - 7840 */ + 0x80, 0x00, 0x1b, 0x03, 0x81, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, /* bytes 7840 - 7856 */ + 0x01, 0x6c, 0x00, 0x18, 0x01, 0xaf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7856 - 7872 */ + 0x99, 0x02, 0xbe, 0xff, 0xbf, 0x02, 0xa3, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7872 - 7888 */ + 0x00, 0xb8, 0xff, 0x4e, 0x01, 0xdd, 0xff, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 7888 - 7904 */ + 0x02, 0x00, 0x96, 0x00, 0xcf, 0xff, 0xa4, 0x00, 0xe2, 0xff, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 7904 - 7920 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x62, 0x00, 0x3d, 0x00, 0x8e, 0x00, 0x82, /* bytes 7920 - 7936 */ + 0x00, 0x08, 0x01, 0x10, 0x00, 0xde, 0x01, 0xfd, 0x00, 0x8d, 0x01, 0xc2, 0x01, 0x0b, 0x02, 0xe4, /* bytes 7936 - 7952 */ + 0x01, 0x11, 0x01, 0xaa, 0x02, 0x84, 0x00, 0xdc, 0x01, 0x5b, 0x00, 0x98, 0x01, 0xc5, 0xff, 0xf4, /* bytes 7952 - 7968 */ + 0x00, 0x62, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd0, 0x00, 0xe0, 0x00, /* bytes 7968 - 7984 */ + 0xec, 0x00, 0x0a, 0x01, 0x53, 0x01, 0x0d, 0x01, 0x91, 0x01, 0xe3, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 7984 - 8000 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x9c, 0x00, 0xd9, 0xff, 0x93, 0x00, 0xce, 0xff, 0x03, 0x00, 0xc0, /* bytes 8000 - 8016 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x18, 0x01, 0xaf, 0x00, 0x31, 0x01, 0x6b, 0x00, 0x03, 0x00, /* bytes 8016 - 8032 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa3, 0x02, 0x79, 0x01, 0x90, 0x02, 0x64, 0x01, 0x03, /* bytes 8032 - 8048 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x03, 0x7f, 0x00, 0x2a, 0x03, 0x7f, 0x00, /* bytes 8048 - 8064 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc0, 0x02, 0xa2, 0xff, 0xaa, 0x02, 0xb1, /* bytes 8064 - 8080 */ + 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x67, 0x01, 0xae, 0x00, 0x7e, 0x01, /* bytes 8080 - 8096 */ + 0x69, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xdc, 0xff, 0x8d, 0x00, 0xd1, /* bytes 8096 - 8112 */ + 0xff, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb6, 0xff, 0x51, 0x01, /* bytes 8112 - 8128 */ + 0xcb, 0xff, 0x42, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, /* bytes 8128 - 8144 */ + 0x00, 0xfe, 0x00, 0x8e, 0x01, 0x13, 0x00, 0xde, 0x01, 0x81, 0x00, 0x08, 0x01, 0x3c, 0x00, 0x8f, /* bytes 8144 - 8160 */ + 0x00, 0xf3, 0x00, 0x61, 0x00, 0x96, 0x01, 0xc7, 0xff, 0xdd, 0x01, 0x5a, 0x00, 0xa8, 0x02, 0x84, /* bytes 8160 - 8176 */ + 0x00, 0xe6, 0x01, 0x10, 0x01, 0xc4, 0x01, 0x08, 0x02, 0xfe, 0x00, 0x8e, 0x01, 0x03, 0x00, 0xc0, /* bytes 8176 - 8192 */ + 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x93, 0x01, 0xe2, 0x00, 0x55, 0x01, 0x0c, 0x01, 0xee, 0x00, /* bytes 8192 - 8208 */ + 0x0a, 0x01, 0xd1, 0x00, 0xe0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x94, /* bytes 8208 - 8224 */ + 0x00, 0xd2, 0xff, 0x91, 0x00, 0xce, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 8224 - 8240 */ + 0xa7, 0x02, 0x79, 0x01, 0xa1, 0x02, 0x72, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 8240 - 8256 */ + 0x00, 0x31, 0x01, 0x6a, 0x00, 0x19, 0x01, 0xae, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8256 - 8272 */ + 0x02, 0x00, 0x7e, 0x01, 0x68, 0x00, 0x68, 0x01, 0xad, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8272 - 8288 */ + 0x00, 0x02, 0x00, 0xbb, 0xff, 0x4e, 0x01, 0xb4, 0xff, 0x52, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8288 - 8304 */ + 0x01, 0x00, 0x02, 0x00, 0x3e, 0x03, 0x7d, 0x00, 0x38, 0x03, 0x7e, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 8304 - 8320 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xcf, 0xff, 0x8e, 0x00, 0xd3, 0xff, 0x8e, 0x00, 0x03, 0x00, 0xc0, /* bytes 8320 - 8336 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xba, 0x02, 0xa5, 0xff, 0xc1, 0x02, 0xa0, 0xff, 0x23, 0x00, /* bytes 8336 - 8352 */ + 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x94, 0x01, 0xc9, 0xff, 0xdd, /* bytes 8352 - 8368 */ + 0x01, 0x59, 0x00, 0xa6, 0x02, 0x84, 0x00, 0xe8, 0x01, 0x10, 0x01, 0xc6, 0x01, 0x05, 0x02, 0xff, /* bytes 8368 - 8384 */ + 0x00, 0x8f, 0x01, 0x15, 0x00, 0xdd, 0x01, 0x7f, 0x00, 0x09, 0x01, 0x3c, 0x00, 0x91, 0x00, 0xf2, /* bytes 8384 - 8400 */ + 0x00, 0x61, 0x00, 0x94, 0x01, 0xc9, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 8400 - 8416 */ + 0x94, 0x01, 0xe0, 0x00, 0x57, 0x01, 0x0b, 0x01, 0xef, 0x00, 0x09, 0x01, 0xd2, 0x00, 0xdf, 0x00, /* bytes 8416 - 8432 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7f, 0x01, 0x68, 0x00, 0x69, 0x01, 0xad, /* bytes 8432 - 8448 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1a, 0x01, 0xad, 0x00, 0x31, 0x01, /* bytes 8448 - 8464 */ + 0x6a, 0x00, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0b, 0x00, 0xc9, /* bytes 8464 - 8480 */ + 0x01, 0x02, 0x02, 0x00, 0x01, 0x8f, 0x01, 0x17, 0x00, 0xdc, 0x01, 0x7f, 0x00, 0x0a, 0x01, 0x3c, /* bytes 8480 - 8496 */ + 0x00, 0x92, 0x00, 0xf0, 0x00, 0x61, 0x00, 0x92, 0x01, 0xca, 0xff, 0xde, 0x01, 0x58, 0x00, 0xa5, /* bytes 8496 - 8512 */ + 0x02, 0x84, 0x00, 0xea, 0x01, 0x10, 0x01, 0xc9, 0x01, 0x02, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8512 - 8528 */ + 0x01, 0x00, 0x04, 0x00, 0x96, 0x01, 0xdf, 0x00, 0x59, 0x01, 0x0a, 0x01, 0xf0, 0x00, 0x08, 0x01, /* bytes 8528 - 8544 */ + 0xd3, 0x00, 0xdf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x01, 0x69, /* bytes 8544 - 8560 */ + 0x00, 0x1b, 0x01, 0xad, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7f, 0x01, /* bytes 8560 - 8576 */ + 0x67, 0x00, 0x6a, 0x01, 0xac, 0x00, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 8576 - 8592 */ + 0x00, 0x0b, 0x00, 0x01, 0x01, 0x8f, 0x01, 0x18, 0x00, 0xdd, 0x01, 0x7e, 0x00, 0x0a, 0x01, 0x3a, /* bytes 8592 - 8608 */ + 0x00, 0x92, 0x00, 0xef, 0x00, 0x61, 0x00, 0x90, 0x01, 0xcb, 0xff, 0xdd, 0x01, 0x58, 0x00, 0xa5, /* bytes 8608 - 8624 */ + 0x02, 0x83, 0x00, 0xeb, 0x01, 0x0f, 0x01, 0xcc, 0x01, 0x01, 0x02, 0x01, 0x01, 0x8f, 0x01, 0x03, /* bytes 8624 - 8640 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x98, 0x01, 0xde, 0x00, 0x5b, 0x01, 0x09, 0x01, /* bytes 8640 - 8656 */ + 0xf2, 0x00, 0x08, 0x01, 0xd4, 0x00, 0xde, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 8656 - 8672 */ + 0x00, 0x80, 0x01, 0x66, 0x00, 0x6b, 0x01, 0xab, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8672 - 8688 */ + 0x02, 0x00, 0x1c, 0x01, 0xac, 0x00, 0x32, 0x01, 0x69, 0x00, +}; +__attribute__ ((aligned (8))) +static const uint8_t INCOMING_PHONE_CALL_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x31, 0x35, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0xff, 0xff, /* bytes 0 - 16 */ + 0x2f, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, /* bytes 16 - 32 */ + 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, /* bytes 32 - 48 */ + 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, /* bytes 48 - 64 */ + 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, /* bytes 64 - 80 */ + 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, /* bytes 80 - 96 */ + 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, /* bytes 96 - 112 */ + 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, /* bytes 112 - 128 */ + 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, /* bytes 128 - 144 */ + 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 144 - 160 */ + 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 160 - 176 */ + 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 176 - 192 */ + 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 192 - 208 */ + 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 208 - 224 */ + 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 224 - 240 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, /* bytes 240 - 256 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, /* bytes 256 - 272 */ + 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, /* bytes 272 - 288 */ + 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 288 - 304 */ + 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, /* bytes 304 - 320 */ + 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, /* bytes 320 - 336 */ + 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 336 - 352 */ + 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, /* bytes 352 - 368 */ + 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 368 - 384 */ + 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 384 - 400 */ + 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 400 - 416 */ + 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 416 - 432 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, /* bytes 432 - 448 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, /* bytes 448 - 464 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, /* bytes 464 - 480 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, /* bytes 480 - 496 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, /* bytes 496 - 512 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, /* bytes 512 - 528 */ + 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, /* bytes 528 - 544 */ + 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 544 - 560 */ + 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, /* bytes 560 - 576 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, /* bytes 576 - 592 */ + 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, /* bytes 592 - 608 */ + 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, /* bytes 608 - 624 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, /* bytes 624 - 640 */ + 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, /* bytes 640 - 656 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, /* bytes 656 - 672 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, /* bytes 672 - 688 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, /* bytes 688 - 704 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, /* bytes 704 - 720 */ + 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, /* bytes 720 - 736 */ + 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, /* bytes 736 - 752 */ + 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, /* bytes 752 - 768 */ + 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, /* bytes 768 - 784 */ + 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 784 - 800 */ + 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 800 - 816 */ + 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, /* bytes 816 - 832 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, /* bytes 832 - 848 */ + 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, /* bytes 848 - 864 */ + 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, /* bytes 864 - 880 */ + 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, /* bytes 880 - 896 */ + 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, /* bytes 896 - 912 */ + 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, /* bytes 912 - 928 */ + 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, /* bytes 928 - 944 */ + 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, /* bytes 944 - 960 */ + 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, /* bytes 960 - 976 */ + 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, /* bytes 976 - 992 */ + 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 992 - 1008 */ + 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1008 - 1024 */ + 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1024 - 1040 */ + 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1040 - 1056 */ + 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1056 - 1072 */ + 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 1072 - 1088 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, /* bytes 1088 - 1104 */ + 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, /* bytes 1104 - 1120 */ + 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 1120 - 1136 */ + 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, /* bytes 1136 - 1152 */ + 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, /* bytes 1152 - 1168 */ + 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1168 - 1184 */ + 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, /* bytes 1184 - 1200 */ + 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1200 - 1216 */ + 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1216 - 1232 */ + 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1232 - 1248 */ + 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 1248 - 1264 */ + 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 1264 - 1280 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, /* bytes 1280 - 1296 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, /* bytes 1296 - 1312 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, /* bytes 1312 - 1328 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, /* bytes 1328 - 1344 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, /* bytes 1344 - 1360 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, /* bytes 1360 - 1376 */ + 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1376 - 1392 */ + 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, /* bytes 1392 - 1408 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, /* bytes 1408 - 1424 */ + 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, /* bytes 1424 - 1440 */ + 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, /* bytes 1440 - 1456 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, /* bytes 1456 - 1472 */ + 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, /* bytes 1472 - 1488 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, /* bytes 1488 - 1504 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, /* bytes 1504 - 1520 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, /* bytes 1520 - 1536 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, /* bytes 1536 - 1552 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, /* bytes 1552 - 1568 */ + 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, /* bytes 1568 - 1584 */ + 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, /* bytes 1584 - 1600 */ + 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, /* bytes 1600 - 1616 */ + 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, /* bytes 1616 - 1632 */ + 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, /* bytes 1632 - 1648 */ + 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, /* bytes 1648 - 1664 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, /* bytes 1664 - 1680 */ + 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, /* bytes 1680 - 1696 */ + 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, /* bytes 1696 - 1712 */ + 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, /* bytes 1712 - 1728 */ + 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, /* bytes 1728 - 1744 */ + 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, /* bytes 1744 - 1760 */ + 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, /* bytes 1760 - 1776 */ + 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, /* bytes 1776 - 1792 */ + 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, /* bytes 1792 - 1808 */ + 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, /* bytes 1808 - 1824 */ + 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, /* bytes 1824 - 1840 */ + 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1840 - 1856 */ + 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 1856 - 1872 */ + 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 1872 - 1888 */ + 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 1888 - 1904 */ + 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1904 - 1920 */ + 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, /* bytes 1920 - 1936 */ + 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, /* bytes 1936 - 1952 */ + 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 1952 - 1968 */ + 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, /* bytes 1968 - 1984 */ + 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, /* bytes 1984 - 2000 */ + 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2000 - 2016 */ + 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, /* bytes 2016 - 2032 */ + 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2032 - 2048 */ + 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2048 - 2064 */ + 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2064 - 2080 */ + 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2080 - 2096 */ + 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2096 - 2112 */ + 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 2112 - 2128 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, /* bytes 2128 - 2144 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, /* bytes 2144 - 2160 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, /* bytes 2160 - 2176 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, /* bytes 2176 - 2192 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, /* bytes 2192 - 2208 */ + 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2208 - 2224 */ + 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, /* bytes 2224 - 2240 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, /* bytes 2240 - 2256 */ + 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, /* bytes 2256 - 2272 */ + 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, /* bytes 2272 - 2288 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, /* bytes 2288 - 2304 */ + 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 2304 - 2320 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, /* bytes 2320 - 2336 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, /* bytes 2336 - 2352 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, /* bytes 2352 - 2368 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, /* bytes 2368 - 2384 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, /* bytes 2384 - 2400 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, /* bytes 2400 - 2416 */ + 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, /* bytes 2416 - 2432 */ + 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, /* bytes 2432 - 2448 */ + 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, /* bytes 2448 - 2464 */ + 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, /* bytes 2464 - 2480 */ + 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, /* bytes 2480 - 2496 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, /* bytes 2496 - 2512 */ + 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, /* bytes 2512 - 2528 */ + 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, /* bytes 2528 - 2544 */ + 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, /* bytes 2544 - 2560 */ + 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, /* bytes 2560 - 2576 */ + 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, /* bytes 2576 - 2592 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, /* bytes 2592 - 2608 */ + 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, /* bytes 2608 - 2624 */ + 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, /* bytes 2624 - 2640 */ + 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, /* bytes 2640 - 2656 */ + 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, /* bytes 2656 - 2672 */ + 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, /* bytes 2672 - 2688 */ + 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2688 - 2704 */ + 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2704 - 2720 */ + 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2720 - 2736 */ + 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2736 - 2752 */ + 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, /* bytes 2752 - 2768 */ + 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, /* bytes 2768 - 2784 */ + 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, /* bytes 2784 - 2800 */ + 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, /* bytes 2800 - 2816 */ + 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, /* bytes 2816 - 2832 */ + 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2832 - 2848 */ + 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, /* bytes 2848 - 2864 */ + 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, /* bytes 2864 - 2880 */ + 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2880 - 2896 */ + 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 2896 - 2912 */ + 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 2912 - 2928 */ + 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 2928 - 2944 */ + 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2944 - 2960 */ + 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 2960 - 2976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, /* bytes 2976 - 2992 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, /* bytes 2992 - 3008 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, /* bytes 3008 - 3024 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, /* bytes 3024 - 3040 */ + 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 3040 - 3056 */ + 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, /* bytes 3056 - 3072 */ + 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, /* bytes 3072 - 3088 */ + 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, /* bytes 3088 - 3104 */ + 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, /* bytes 3104 - 3120 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, /* bytes 3120 - 3136 */ + 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3136 - 3152 */ + 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3152 - 3168 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, /* bytes 3168 - 3184 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, /* bytes 3184 - 3200 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, /* bytes 3200 - 3216 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, /* bytes 3216 - 3232 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, /* bytes 3232 - 3248 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, /* bytes 3248 - 3264 */ + 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, /* bytes 3264 - 3280 */ + 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, /* bytes 3280 - 3296 */ + 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, /* bytes 3296 - 3312 */ + 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, /* bytes 3312 - 3328 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, /* bytes 3328 - 3344 */ + 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, /* bytes 3344 - 3360 */ + 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, /* bytes 3360 - 3376 */ + 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, /* bytes 3376 - 3392 */ + 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, /* bytes 3392 - 3408 */ + 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, /* bytes 3408 - 3424 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, /* bytes 3424 - 3440 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, /* bytes 3440 - 3456 */ + 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, /* bytes 3456 - 3472 */ + 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, /* bytes 3472 - 3488 */ + 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, /* bytes 3488 - 3504 */ + 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, /* bytes 3504 - 3520 */ + 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, /* bytes 3520 - 3536 */ + 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3536 - 3552 */ + 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 3552 - 3568 */ + 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 3568 - 3584 */ + 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, /* bytes 3584 - 3600 */ + 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, /* bytes 3600 - 3616 */ + 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, /* bytes 3616 - 3632 */ + 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, /* bytes 3632 - 3648 */ + 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, /* bytes 3648 - 3664 */ + 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, /* bytes 3664 - 3680 */ + 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, /* bytes 3680 - 3696 */ + 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, /* bytes 3696 - 3712 */ + 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, /* bytes 3712 - 3728 */ + 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3728 - 3744 */ + 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 3744 - 3760 */ + 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 3760 - 3776 */ + 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3776 - 3792 */ + 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3792 - 3808 */ + 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3808 - 3824 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, /* bytes 3824 - 3840 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, /* bytes 3840 - 3856 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, /* bytes 3856 - 3872 */ + 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3872 - 3888 */ + 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3888 - 3904 */ + 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, /* bytes 3904 - 3920 */ + 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, /* bytes 3920 - 3936 */ + 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, /* bytes 3936 - 3952 */ + 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, /* bytes 3952 - 3968 */ + 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3968 - 3984 */ + 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 3984 - 4000 */ + 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 4000 - 4016 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, /* bytes 4016 - 4032 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, /* bytes 4032 - 4048 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, /* bytes 4048 - 4064 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, /* bytes 4064 - 4080 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, /* bytes 4080 - 4096 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, /* bytes 4096 - 4112 */ + 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, /* bytes 4112 - 4128 */ + 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, /* bytes 4128 - 4144 */ + 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 4144 - 4160 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0e, /* bytes 4160 - 4176 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, /* bytes 4176 - 4192 */ + 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, /* bytes 4192 - 4208 */ + 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, /* bytes 4208 - 4224 */ + 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, /* bytes 4224 - 4240 */ + 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, /* bytes 4240 - 4256 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, /* bytes 4256 - 4272 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, /* bytes 4272 - 4288 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, /* bytes 4288 - 4304 */ + 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, /* bytes 4304 - 4320 */ + 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, /* bytes 4320 - 4336 */ + 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7b, 0x01, 0x5a, /* bytes 4336 - 4352 */ + 0xff, 0x6c, 0x01, 0x75, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, /* bytes 4352 - 4368 */ + 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, /* bytes 4368 - 4384 */ + 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4384 - 4400 */ + 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4400 - 4416 */ + 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4416 - 4432 */ + 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, /* bytes 4432 - 4448 */ + 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, /* bytes 4448 - 4464 */ + 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, /* bytes 4464 - 4480 */ + 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, /* bytes 4480 - 4496 */ + 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, /* bytes 4496 - 4512 */ + 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, /* bytes 4512 - 4528 */ + 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, /* bytes 4528 - 4544 */ + 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5d, 0x01, /* bytes 4544 - 4560 */ + 0x93, 0xff, 0x7b, 0x01, 0x5f, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, /* bytes 4560 - 4576 */ + 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4576 - 4592 */ + 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4592 - 4608 */ + 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 4608 - 4624 */ + 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 4624 - 4640 */ + 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4640 - 4656 */ + 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 4656 - 4672 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, /* bytes 4672 - 4688 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, /* bytes 4688 - 4704 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, /* bytes 4704 - 4720 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, /* bytes 4720 - 4736 */ + 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 4736 - 4752 */ + 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, /* bytes 4752 - 4768 */ + 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, /* bytes 4768 - 4784 */ + 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, /* bytes 4784 - 4800 */ + 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, /* bytes 4800 - 4816 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, /* bytes 4816 - 4832 */ + 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4832 - 4848 */ + 0x01, 0x00, 0x04, 0x00, 0x75, 0x01, 0xb1, 0xff, 0x76, 0x01, 0xad, 0xff, 0x5d, 0x01, 0x98, 0xff, /* bytes 4848 - 4864 */ + 0x7b, 0x01, 0x63, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, /* bytes 4864 - 4880 */ + 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, /* bytes 4880 - 4896 */ + 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, /* bytes 4896 - 4912 */ + 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4912 - 4928 */ + 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 4928 - 4944 */ + 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 4944 - 4960 */ + 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 4960 - 4976 */ + 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 4976 - 4992 */ + 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 4992 - 5008 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, /* bytes 5008 - 5024 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, /* bytes 5024 - 5040 */ + 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, /* bytes 5040 - 5056 */ + 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 5056 - 5072 */ + 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, /* bytes 5072 - 5088 */ + 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, /* bytes 5088 - 5104 */ + 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, /* bytes 5104 - 5120 */ + 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, /* bytes 5120 - 5136 */ + 0x01, 0x88, 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5136 - 5152 */ + 0x04, 0x00, 0x72, 0x01, 0xcd, 0xff, 0x7c, 0x01, 0xb7, 0xff, 0x5d, 0x01, 0x9e, 0xff, 0x7a, 0x01, /* bytes 5152 - 5168 */ + 0x67, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, /* bytes 5168 - 5184 */ + 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, /* bytes 5184 - 5200 */ + 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, /* bytes 5200 - 5216 */ + 0x01, 0x58, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, /* bytes 5216 - 5232 */ + 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, /* bytes 5232 - 5248 */ + 0x01, 0x60, 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5248 - 5264 */ + 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5264 - 5280 */ + 0x00, 0x68, 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 5280 - 5296 */ + 0x02, 0x00, 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5296 - 5312 */ + 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 5312 - 5328 */ + 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, /* bytes 5328 - 5344 */ + 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, /* bytes 5344 - 5360 */ + 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 5360 - 5376 */ + 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, /* bytes 5376 - 5392 */ + 0x8c, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, /* bytes 5392 - 5408 */ + 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 5408 - 5424 */ + 0x00, 0x05, 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, /* bytes 5424 - 5440 */ + 0x01, 0x00, 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 5440 - 5456 */ + 0x77, 0x01, 0x71, 0xff, 0x5b, 0x01, 0xa5, 0xff, 0x7b, 0x01, 0xbd, 0xff, 0x69, 0x01, 0xe5, 0xff, /* bytes 5456 - 5472 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, /* bytes 5472 - 5488 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, /* bytes 5488 - 5504 */ + 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, /* bytes 5504 - 5520 */ + 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, /* bytes 5520 - 5536 */ + 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, /* bytes 5536 - 5552 */ + 0x01, 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, /* bytes 5552 - 5568 */ + 0xc0, 0x01, 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, /* bytes 5568 - 5584 */ + 0x01, 0x60, 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 5584 - 5600 */ + 0x7c, 0x01, 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 5600 - 5616 */ + 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 5616 - 5632 */ + 0x04, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, /* bytes 5632 - 5648 */ + 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, /* bytes 5648 - 5664 */ + 0x01, 0x4c, 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, /* bytes 5664 - 5680 */ + 0xd4, 0x00, 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, /* bytes 5680 - 5696 */ + 0x3c, 0x01, 0x8c, 0x00, 0xd4, 0x01, 0x8c, 0x00, 0x0c, 0x02, 0xc4, 0x00, 0xe4, 0x01, 0x6c, 0x01, /* bytes 5696 - 5712 */ + 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, /* bytes 5712 - 5728 */ + 0x00, 0x98, 0x01, 0xc0, 0x00, 0x18, 0x01, 0xc0, 0x00, 0x08, 0x01, 0x00, 0x01, 0x88, 0x01, 0x00, /* bytes 5728 - 5744 */ + 0x01, 0x98, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6b, 0x01, /* bytes 5744 - 5760 */ + 0x8c, 0xff, 0x58, 0x01, 0xaf, 0xff, 0x78, 0x01, 0xc7, 0xff, 0x64, 0x01, 0xf3, 0xff, 0x03, 0x00, /* bytes 5760 - 5776 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x30, 0x01, 0x20, 0x01, 0x30, 0x01, 0x03, /* bytes 5776 - 5792 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, /* bytes 5792 - 5808 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x30, 0x01, 0x58, 0x01, 0x30, /* bytes 5808 - 5824 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, /* bytes 5824 - 5840 */ + 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, 0xf0, /* bytes 5840 - 5856 */ + 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, /* bytes 5856 - 5872 */ + 0x48, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x60, /* bytes 5872 - 5888 */ + 0x01, 0x48, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, /* bytes 5888 - 5904 */ + 0xf4, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 5904 - 5920 */ + 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 5920 - 5936 */ + 0xd4, 0x01, 0x8c, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, /* bytes 5936 - 5952 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8c, 0x00, 0x4c, 0x01, 0x4c, /* bytes 5952 - 5968 */ + 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x01, /* bytes 5968 - 5984 */ + 0x90, 0x00, 0x0c, 0x02, 0xc9, 0x00, 0xe4, 0x01, 0x6c, 0x01, 0xb4, 0x01, 0x2c, 0x02, 0xd4, 0x00, /* bytes 5984 - 6000 */ + 0x2c, 0x02, 0x9c, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x34, 0x01, 0xf4, 0x00, 0x8c, 0x00, 0x3c, 0x01, /* bytes 6000 - 6016 */ + 0x8d, 0x00, 0xd4, 0x01, 0x90, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x88, /* bytes 6016 - 6032 */ + 0x01, 0x04, 0x01, 0x08, 0x01, 0x04, 0x01, 0x18, 0x01, 0xc4, 0x00, 0x98, 0x01, 0xc4, 0x00, 0x88, /* bytes 6032 - 6048 */ + 0x01, 0x04, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x5b, 0x01, 0xfe, 0xff, /* bytes 6048 - 6064 */ + 0x6f, 0x01, 0xd3, 0xff, 0x50, 0x01, 0xba, 0xff, 0x53, 0x01, 0xb1, 0xff, 0x03, 0x00, 0xc0, 0x03, /* bytes 6064 - 6080 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x34, 0x01, 0x78, 0x01, 0x34, 0x01, 0x03, 0x00, 0xc0, /* bytes 6080 - 6096 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc0, 0x01, 0xd0, 0x00, 0xc0, 0x01, 0x03, 0x00, /* bytes 6096 - 6112 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, /* bytes 6112 - 6128 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, /* bytes 6128 - 6144 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x34, 0x01, 0x20, 0x01, 0x34, /* bytes 6144 - 6160 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xc0, 0x01, 0x48, 0x01, /* bytes 6160 - 6176 */ + 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, 0x01, 0x60, 0x01, 0x68, /* bytes 6176 - 6192 */ + 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x60, 0x01, /* bytes 6192 - 6208 */ + 0xf0, 0x00, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x2c, /* bytes 6208 - 6224 */ + 0x02, 0x7c, 0x01, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, /* bytes 6224 - 6240 */ + 0x90, 0x00, 0xac, 0x01, 0x34, 0x01, 0x7c, 0x01, 0xf4, 0x01, 0x9c, 0x00, 0xf4, 0x01, 0x03, 0x00, /* bytes 6240 - 6256 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x8d, 0x00, 0x4c, 0x01, 0x4c, 0x00, 0x23, /* bytes 6256 - 6272 */ + 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd4, 0x00, 0x2c, 0x02, /* bytes 6272 - 6288 */ + 0x9d, 0x00, 0xf4, 0x01, 0xcc, 0x00, 0x47, 0x01, 0xf4, 0x00, 0xa1, 0x00, 0x3c, 0x01, 0xa1, 0x00, /* bytes 6288 - 6304 */ + 0xd4, 0x01, 0x9f, 0x00, 0x0c, 0x02, 0xd7, 0x00, 0xe3, 0x01, 0x6b, 0x01, 0xb5, 0x01, 0x2c, 0x02, /* bytes 6304 - 6320 */ + 0xd4, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x97, 0x01, 0xd4, /* bytes 6320 - 6336 */ + 0x00, 0x18, 0x01, 0xd4, 0x00, 0x08, 0x01, 0x14, 0x01, 0x88, 0x01, 0x14, 0x01, 0x97, 0x01, 0xd4, /* bytes 6336 - 6352 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0x51, 0x01, 0x0e, 0x00, 0x65, 0x01, /* bytes 6352 - 6368 */ + 0xe3, 0xff, 0x5c, 0x01, 0xd6, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, /* bytes 6368 - 6384 */ + 0x01, 0x6a, 0x01, 0x68, 0x01, 0x6a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6384 - 6400 */ + 0x48, 0x01, 0xc0, 0x01, 0x28, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 6400 - 6416 */ + 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 6416 - 6432 */ + 0x02, 0x00, 0xf0, 0x00, 0x6a, 0x01, 0x10, 0x01, 0x6a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 6432 - 6448 */ + 0x00, 0x02, 0x00, 0xb5, 0x01, 0x2c, 0x02, 0x7b, 0x01, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6448 - 6464 */ + 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xc0, 0x01, 0xf0, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 6464 - 6480 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x44, 0x01, 0x78, 0x01, 0x44, 0x01, 0x03, 0x00, 0xc0, /* bytes 6480 - 6496 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x90, 0x01, 0xe0, 0x00, 0x90, 0x01, 0x03, 0x00, /* bytes 6496 - 6512 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x44, 0x01, 0x20, 0x01, 0x44, 0x01, 0x03, /* bytes 6512 - 6528 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, 0x01, 0x9f, 0x00, 0xad, 0x01, 0x35, 0x01, /* bytes 6528 - 6544 */ + 0x7b, 0x01, 0xf4, 0x01, 0x9d, 0x00, 0xf4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 6544 - 6560 */ + 0x00, 0x3c, 0x01, 0xa1, 0x00, 0x4c, 0x01, 0x4b, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, /* bytes 6560 - 6576 */ + 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xde, 0x01, 0x7f, 0x01, 0xb4, 0x01, 0x2a, 0x02, 0xcf, 0x00, /* bytes 6576 - 6592 */ + 0x2b, 0x02, 0x9f, 0x00, 0xee, 0x01, 0xce, 0x00, 0x38, 0x01, 0xf1, 0x00, 0x92, 0x00, 0x32, 0x01, /* bytes 6592 - 6608 */ + 0x94, 0x00, 0xcc, 0x01, 0x8b, 0x00, 0x0f, 0x02, 0xc5, 0x00, 0xde, 0x01, 0x7f, 0x01, 0x03, 0x00, /* bytes 6608 - 6624 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x95, 0x01, 0xc1, 0x00, 0x14, 0x01, 0xc4, 0x00, 0x07, /* bytes 6624 - 6640 */ + 0x01, 0x06, 0x01, 0x88, 0x01, 0x03, 0x01, 0x95, 0x01, 0xc1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 6640 - 6656 */ + 0x01, 0x00, 0x02, 0x00, 0x5b, 0x01, 0x35, 0x01, 0x79, 0x01, 0x32, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 6656 - 6672 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x0f, 0x01, 0x70, 0x01, 0xf1, 0x00, 0x71, 0x01, 0x03, 0x00, 0xc0, /* bytes 6672 - 6688 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x27, 0x01, 0xc7, 0x01, 0x48, 0x01, 0xc7, 0x01, 0x03, 0x00, /* bytes 6688 - 6704 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x23, 0x01, 0x34, 0x01, 0x02, 0x01, 0x35, 0x01, 0x03, /* bytes 6704 - 6720 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x01, 0x0a, 0x02, 0xb4, 0x01, 0x2a, 0x02, /* bytes 6720 - 6736 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, 0x01, 0xa3, 0x01, 0x38, 0x01, 0xa3, /* bytes 6736 - 6752 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0xc8, 0x01, 0xd0, 0x00, /* bytes 6752 - 6768 */ + 0xc7, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe1, 0x00, 0xa3, 0x01, 0xff, /* bytes 6768 - 6784 */ + 0x00, 0xa2, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x69, 0x01, 0x72, 0x01, /* bytes 6784 - 6800 */ + 0x48, 0x01, 0x72, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xcc, 0x01, 0x8b, /* bytes 6800 - 6816 */ + 0x00, 0xa7, 0x01, 0x47, 0x01, 0x80, 0x01, 0x0a, 0x02, 0x9f, 0x00, 0xee, 0x01, 0x03, 0x00, 0xc0, /* bytes 6816 - 6832 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x01, 0x94, 0x00, 0x44, 0x01, 0x5d, 0x00, 0x23, 0x00, /* bytes 6832 - 6848 */ + 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0x2f, 0x01, 0x57, 0x00, 0xc4, /* bytes 6848 - 6864 */ + 0x01, 0x49, 0x00, 0xf9, 0x01, 0x83, 0x00, 0xee, 0x01, 0x78, 0x01, 0xb5, 0x01, 0x33, 0x02, 0xd7, /* bytes 6864 - 6880 */ + 0x00, 0x37, 0x02, 0x99, 0x00, 0xf9, 0x01, 0xc7, 0x00, 0x0b, 0x01, 0xe4, 0x00, 0x5b, 0x00, 0x2f, /* bytes 6880 - 6896 */ + 0x01, 0x57, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x8c, 0x01, 0x80, 0x00, /* bytes 6896 - 6912 */ + 0x0b, 0x01, 0x81, 0x00, 0xff, 0x00, 0xc3, 0x00, 0x7f, 0x01, 0xbf, 0x00, 0x8c, 0x01, 0x80, 0x00, /* bytes 6912 - 6928 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x01, 0xf2, 0x00, 0xfb, 0x00, 0xf2, /* bytes 6928 - 6944 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf5, 0x00, 0x5e, 0x01, 0x13, 0x01, /* bytes 6944 - 6960 */ + 0x5d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x61, 0x01, 0x9a, 0x01, 0x41, /* bytes 6960 - 6976 */ + 0x01, 0x9a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe8, 0x00, 0x9c, 0x01, /* bytes 6976 - 6992 */ + 0x08, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x72, 0x01, 0xef, /* bytes 6992 - 7008 */ + 0x00, 0x52, 0x01, 0xf0, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4d, 0x01, /* bytes 7008 - 7024 */ + 0x5d, 0x01, 0x6e, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4a, /* bytes 7024 - 7040 */ + 0x01, 0xd2, 0x01, 0x28, 0x01, 0xd2, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7040 - 7056 */ + 0xd0, 0x00, 0xd2, 0x01, 0xf0, 0x00, 0xd4, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7056 - 7072 */ + 0x00, 0xb5, 0x01, 0x33, 0x02, 0x86, 0x01, 0x05, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7072 - 7088 */ + 0x04, 0x00, 0x99, 0x00, 0xf9, 0x01, 0x86, 0x01, 0x05, 0x02, 0xa4, 0x01, 0x47, 0x01, 0xc4, 0x01, /* bytes 7088 - 7104 */ + 0x49, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2f, 0x01, 0x57, 0x00, 0x3e, /* bytes 7104 - 7120 */ + 0x01, 0x5e, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, /* bytes 7120 - 7136 */ + 0xd8, 0x00, 0x45, 0x02, 0xa2, 0x00, 0x00, 0x02, 0xcd, 0x00, 0xe0, 0x00, 0xed, 0x00, 0x32, 0x00, /* bytes 7136 - 7152 */ + 0x2c, 0x01, 0x34, 0x00, 0xce, 0x01, 0x30, 0x00, 0x02, 0x02, 0x68, 0x00, 0xed, 0x01, 0x53, 0x01, /* bytes 7152 - 7168 */ + 0xba, 0x01, 0x32, 0x02, 0xd8, 0x00, 0x45, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, /* bytes 7168 - 7184 */ + 0x00, 0x7f, 0x01, 0xa4, 0x00, 0x00, 0x01, 0xa9, 0x00, 0x0c, 0x01, 0x67, 0x00, 0x8b, 0x01, 0x65, /* bytes 7184 - 7200 */ + 0x00, 0x7f, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x83, 0x01, /* bytes 7200 - 7216 */ + 0xe3, 0x01, 0xba, 0x01, 0x32, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x52, /* bytes 7216 - 7232 */ + 0x01, 0xd6, 0x00, 0x71, 0x01, 0xd4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7232 - 7248 */ + 0xec, 0x00, 0x1a, 0x01, 0x0d, 0x01, 0x1b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7248 - 7264 */ + 0x00, 0xfa, 0x00, 0xd6, 0x00, 0x1c, 0x01, 0xd7, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 7264 - 7280 */ + 0x02, 0x00, 0xda, 0x00, 0xc2, 0x01, 0xfb, 0x00, 0xc1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7280 - 7296 */ + 0x00, 0x02, 0x00, 0x46, 0x01, 0x17, 0x01, 0x67, 0x01, 0x15, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 7296 - 7312 */ + 0x01, 0x00, 0x02, 0x00, 0x52, 0x01, 0xbe, 0x01, 0x33, 0x01, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 7312 - 7328 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x3e, 0x01, 0x72, 0x01, 0x5d, 0x01, 0x71, 0x01, 0x03, 0x00, 0xc0, /* bytes 7328 - 7344 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x01, 0x76, 0x01, 0xe5, 0x00, 0x75, 0x01, 0x03, 0x00, /* bytes 7344 - 7360 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xa2, 0x00, 0x00, 0x02, 0x83, 0x01, 0xe3, 0x01, 0xa9, /* bytes 7360 - 7376 */ + 0x01, 0x22, 0x01, 0xce, 0x01, 0x30, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7376 - 7392 */ + 0x3d, 0x01, 0x36, 0x00, 0x2c, 0x01, 0x34, 0x00, 0x23, 0x00, 0x0e, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 7392 - 7408 */ + 0xff, 0x00, 0x00, 0x0a, 0x00, 0x09, 0x02, 0x83, 0x00, 0xe8, 0x01, 0x11, 0x01, 0xc9, 0x01, 0x1c, /* bytes 7408 - 7424 */ + 0x02, 0xe5, 0x00, 0x27, 0x02, 0xaf, 0x00, 0xf7, 0x01, 0xc2, 0x00, 0xe1, 0x00, 0xf5, 0x00, 0x3c, /* bytes 7424 - 7440 */ + 0x00, 0x38, 0x01, 0x3c, 0x00, 0xcf, 0x01, 0x41, 0x00, 0x09, 0x02, 0x83, 0x00, 0x03, 0x00, 0xc0, /* bytes 7440 - 7456 */ + 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x11, 0x01, 0x75, 0x00, 0x01, 0x01, 0xb4, 0x00, 0x83, 0x01, /* bytes 7456 - 7472 */ + 0xb3, 0x00, 0x92, 0x01, 0x74, 0x00, 0x11, 0x01, 0x75, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 7472 - 7488 */ + 0x00, 0x02, 0x00, 0x1d, 0x01, 0xe2, 0x00, 0xfc, 0x00, 0xe4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 7488 - 7504 */ + 0x01, 0x00, 0x02, 0x00, 0xe7, 0x00, 0x06, 0x01, 0x07, 0x01, 0x05, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 7504 - 7520 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x3f, 0x01, 0x05, 0x01, 0x5f, 0x01, 0x04, 0x01, 0x03, 0x00, 0xc0, /* bytes 7520 - 7536 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc9, 0x01, 0x1c, 0x02, 0x82, 0x01, 0xa3, 0x01, 0x03, 0x00, /* bytes 7536 - 7552 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, 0x01, 0x39, 0x01, 0x39, 0x01, 0x39, 0x01, 0x03, /* bytes 7552 - 7568 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2b, 0x01, 0x7d, 0x01, 0x4c, 0x01, 0x7a, 0x01, /* bytes 7568 - 7584 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd4, 0x00, 0x7f, 0x01, 0xf2, 0x00, 0x80, /* bytes 7584 - 7600 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x51, 0x01, 0xe4, 0x00, 0x72, 0x01, /* bytes 7600 - 7616 */ + 0xe2, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0x00, 0x3c, 0x01, 0xe1, /* bytes 7616 - 7632 */ + 0x00, 0x3e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xaf, 0x00, 0xf7, 0x01, /* bytes 7632 - 7648 */ + 0x82, 0x01, 0xa3, 0x01, 0xa8, 0x01, 0xe4, 0x00, 0xcf, 0x01, 0x41, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 7648 - 7664 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x38, 0x01, 0x3c, 0x00, 0x3b, 0x01, 0xf4, 0xff, 0x03, 0x00, 0xc0, /* bytes 7664 - 7680 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x74, 0x00, 0xfc, 0x00, 0x94, 0x00, 0x14, 0x01, 0x23, 0x00, /* bytes 7680 - 7696 */ + 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xdd, 0x00, 0xe5, 0x01, 0xa5, /* bytes 7696 - 7712 */ + 0x00, 0xaa, 0x01, 0xc9, 0x00, 0x0d, 0x01, 0xf2, 0x00, 0x69, 0x00, 0x3a, 0x01, 0x6a, 0x00, 0xd1, /* bytes 7712 - 7728 */ + 0x01, 0x69, 0x00, 0x0a, 0x02, 0xa8, 0x00, 0xde, 0x01, 0x17, 0x01, 0xc3, 0x01, 0xdd, 0x01, 0xdd, /* bytes 7728 - 7744 */ + 0x00, 0xe5, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x98, 0x01, 0x9e, 0x00, /* bytes 7744 - 7760 */ + 0x17, 0x01, 0x9d, 0x00, 0x08, 0x01, 0xdd, 0x00, 0x89, 0x01, 0xdd, 0x00, 0x98, 0x01, 0x9e, 0x00, /* bytes 7760 - 7776 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x0d, 0x01, 0x21, 0x01, 0x0d, /* bytes 7776 - 7792 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x59, 0x01, 0x0d, 0x01, 0x79, 0x01, /* bytes 7792 - 7808 */ + 0x0d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf8, 0x00, 0x3a, 0x01, 0xd9, /* bytes 7808 - 7824 */ + 0x00, 0x3a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x30, 0x01, 0x3a, 0x01, /* bytes 7824 - 7840 */ + 0x51, 0x01, 0x3a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x69, 0x01, 0x27, /* bytes 7840 - 7856 */ + 0x01, 0x49, 0x01, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc3, 0x01, /* bytes 7856 - 7872 */ + 0xdd, 0x01, 0x78, 0x01, 0xa0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x42, /* bytes 7872 - 7888 */ + 0x01, 0x64, 0x01, 0x21, 0x01, 0x65, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7888 - 7904 */ + 0xe8, 0x00, 0x65, 0x01, 0xc9, 0x00, 0x66, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 7904 - 7920 */ + 0x00, 0xf1, 0x00, 0x27, 0x01, 0x10, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7920 - 7936 */ + 0x04, 0x00, 0xa5, 0x00, 0xaa, 0x01, 0x78, 0x01, 0xa0, 0x01, 0xa2, 0x01, 0xdc, 0x00, 0xd1, 0x01, /* bytes 7936 - 7952 */ + 0x69, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x73, 0x00, 0xfb, 0x00, 0x93, /* bytes 7952 - 7968 */ + 0x00, 0x13, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x34, 0x01, /* bytes 7968 - 7984 */ + 0x84, 0x00, 0x44, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x01, 0x6a, /* bytes 7984 - 8000 */ + 0x00, 0x47, 0x01, 0xf5, 0xff, 0x23, 0x00, 0x10, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 8000 - 8016 */ + 0x0a, 0x00, 0xb6, 0x01, 0xf1, 0x01, 0xe6, 0x01, 0x52, 0x01, 0x0b, 0x02, 0xe2, 0x00, 0xd4, 0x01, /* bytes 8016 - 8032 */ + 0xa9, 0x00, 0x3c, 0x01, 0xa9, 0x00, 0xf3, 0x00, 0xa7, 0x00, 0xcb, 0x00, 0x4f, 0x01, 0x9b, 0x00, /* bytes 8032 - 8048 */ + 0xb4, 0x01, 0xd1, 0x00, 0xe9, 0x01, 0xb6, 0x01, 0xf1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8048 - 8064 */ + 0x00, 0x05, 0x00, 0x98, 0x01, 0xdd, 0x00, 0x18, 0x01, 0xdd, 0x00, 0x08, 0x01, 0x1d, 0x01, 0x88, /* bytes 8064 - 8080 */ + 0x01, 0x1d, 0x01, 0x98, 0x01, 0xdd, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 8080 - 8096 */ + 0xb6, 0x01, 0xf1, 0x01, 0x7f, 0x01, 0xd8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 8096 - 8112 */ + 0x00, 0x4a, 0x01, 0x96, 0x01, 0x2a, 0x01, 0x96, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8112 - 8128 */ + 0x02, 0x00, 0x10, 0x01, 0x63, 0x01, 0xf0, 0x00, 0x62, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8128 - 8144 */ + 0x00, 0x02, 0x00, 0x68, 0x01, 0x63, 0x01, 0x48, 0x01, 0x62, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8144 - 8160 */ + 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x4d, 0x01, 0x78, 0x01, 0x4d, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 8160 - 8176 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xf2, 0x00, 0x96, 0x01, 0xd2, 0x00, 0x96, 0x01, 0x03, 0x00, 0xc0, /* bytes 8176 - 8192 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x78, 0x01, 0xe1, 0x00, 0x78, 0x01, 0x03, 0x00, /* bytes 8192 - 8208 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x4d, 0x01, 0x20, 0x01, 0x4d, 0x01, 0x03, /* bytes 8208 - 8224 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x01, 0x78, 0x01, 0x59, 0x01, 0x78, 0x01, /* bytes 8224 - 8240 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x9b, 0x00, 0xb4, 0x01, 0x7f, 0x01, 0xd8, /* bytes 8240 - 8256 */ + 0x01, 0xae, 0x01, 0x17, 0x01, 0xd4, 0x01, 0xa9, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 8256 - 8272 */ + 0x02, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0x6e, 0x00, 0xf3, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8272 - 8288 */ + 0x00, 0x02, 0x00, 0x4c, 0x00, 0x7c, 0x01, 0x7c, 0x00, 0x74, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8288 - 8304 */ + 0x01, 0x00, 0x02, 0x00, 0x82, 0x00, 0x43, 0x01, 0x49, 0x00, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 8304 - 8320 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x3c, 0x01, 0xa9, 0x00, 0x4b, 0x01, 0x35, 0x00, 0x23, 0x00, 0x10, /* bytes 8320 - 8336 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xcc, 0x00, 0x5a, 0x01, 0xf4, 0x00, /* bytes 8336 - 8352 */ + 0xb2, 0x00, 0x3c, 0x01, 0xb2, 0x00, 0xd4, 0x01, 0xb2, 0x00, 0x0c, 0x02, 0xea, 0x00, 0xe4, 0x01, /* bytes 8352 - 8368 */ + 0x8f, 0x01, 0xb3, 0x01, 0x48, 0x02, 0xd3, 0x00, 0x47, 0x02, 0x9b, 0x00, 0x0f, 0x02, 0xcc, 0x00, /* bytes 8368 - 8384 */ + 0x5a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x08, 0x01, 0x26, 0x01, 0x88, /* bytes 8384 - 8400 */ + 0x01, 0x26, 0x01, 0x98, 0x01, 0xe6, 0x00, 0x18, 0x01, 0xe6, 0x00, 0x08, 0x01, 0x26, 0x01, 0x03, /* bytes 8400 - 8416 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x56, 0x01, 0x78, 0x01, 0x56, 0x01, /* bytes 8416 - 8432 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0xb3, 0x01, 0x38, 0x01, 0xb3, /* bytes 8432 - 8448 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf0, 0x00, 0x85, 0x01, 0x10, 0x01, /* bytes 8448 - 8464 */ + 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xe0, 0x01, 0xf0, /* bytes 8464 - 8480 */ + 0x00, 0xe0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0x16, 0x02, /* bytes 8480 - 8496 */ + 0xb3, 0x01, 0x48, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, 0x01, 0xe0, /* bytes 8496 - 8512 */ + 0x01, 0x28, 0x01, 0xe0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, 0x01, /* bytes 8512 - 8528 */ + 0x85, 0x01, 0x68, 0x01, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x20, /* bytes 8528 - 8544 */ + 0x01, 0x56, 0x01, 0x00, 0x01, 0x56, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 8544 - 8560 */ + 0xe0, 0x00, 0xb3, 0x01, 0x00, 0x01, 0xb3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 8560 - 8576 */ + 0x00, 0xd4, 0x01, 0xb2, 0x00, 0xac, 0x01, 0x56, 0x01, 0x7c, 0x01, 0x16, 0x02, 0x9b, 0x00, 0x0f, /* bytes 8576 - 8592 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x74, 0x00, 0x3f, 0x01, 0x31, 0x00, /* bytes 8592 - 8608 */ + 0x2b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x74, 0x00, 0xfc, 0x00, 0x44, /* bytes 8608 - 8624 */ + 0x00, 0xbc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7a, 0x00, 0x74, 0x01, /* bytes 8624 - 8640 */ + 0x4a, 0x00, 0x7d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x6f, /* bytes 8640 - 8656 */ + 0x00, 0x3c, 0x01, 0xb2, 0x00, 0x23, 0x00, 0x10, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 8656 - 8672 */ + 0x0a, 0x00, 0xf4, 0x00, 0xb1, 0x00, 0x3c, 0x01, 0xb1, 0x00, 0xd5, 0x01, 0xb1, 0x00, 0x0c, 0x02, /* bytes 8672 - 8688 */ + 0xe9, 0x00, 0xe4, 0x01, 0x92, 0x01, 0xb4, 0x01, 0x51, 0x02, 0xd4, 0x00, 0x51, 0x02, 0x9c, 0x00, /* bytes 8688 - 8704 */ + 0x19, 0x02, 0xcc, 0x00, 0x59, 0x01, 0xf4, 0x00, 0xb1, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8704 - 8720 */ + 0x00, 0x05, 0x00, 0x18, 0x01, 0xe5, 0x00, 0x08, 0x01, 0x25, 0x01, 0x88, 0x01, 0x25, 0x01, 0x98, /* bytes 8720 - 8736 */ + 0x01, 0xe5, 0x00, 0x18, 0x01, 0xe5, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 8736 - 8752 */ + 0x00, 0x01, 0xb6, 0x01, 0xe0, 0x00, 0xb6, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 8752 - 8768 */ + 0x00, 0xf0, 0x00, 0x86, 0x01, 0x10, 0x01, 0x86, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 8768 - 8784 */ + 0x02, 0x00, 0x7c, 0x01, 0x1b, 0x02, 0xb4, 0x01, 0x51, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 8784 - 8800 */ + 0x00, 0x02, 0x00, 0x20, 0x01, 0x55, 0x01, 0x00, 0x01, 0x55, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 8800 - 8816 */ + 0x01, 0x00, 0x02, 0x00, 0x38, 0x01, 0xb7, 0x01, 0x58, 0x01, 0xb7, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 8816 - 8832 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x01, 0x55, 0x01, 0x58, 0x01, 0x55, 0x01, 0x03, 0x00, 0xc0, /* bytes 8832 - 8848 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xe6, 0x01, 0xf0, 0x00, 0xe6, 0x01, 0x03, 0x00, /* bytes 8848 - 8864 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x28, 0x01, 0xe6, 0x01, 0x48, 0x01, 0xe6, 0x01, 0x03, /* bytes 8864 - 8880 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x68, 0x01, 0x86, 0x01, 0x48, 0x01, 0x86, 0x01, /* bytes 8880 - 8896 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd5, 0x01, 0xb1, 0x00, 0xac, 0x01, 0x5b, /* bytes 8896 - 8912 */ + 0x01, 0x7c, 0x01, 0x1b, 0x02, 0x9c, 0x00, 0x19, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 8912 - 8928 */ + 0x02, 0x00, 0xdc, 0xff, 0x0c, 0x01, 0x4c, 0x00, 0x34, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8928 - 8944 */ + 0x00, 0x02, 0x00, 0x74, 0x00, 0xfc, 0x00, 0x4b, 0x00, 0xc3, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8944 - 8960 */ + 0x01, 0x00, 0x02, 0x00, 0x6e, 0x00, 0x76, 0x01, 0x3a, 0x00, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 8960 - 8976 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x73, 0x00, 0x3c, 0x01, 0xb1, 0x00, 0x23, 0x00, 0x10, /* bytes 8976 - 8992 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xe5, 0x01, 0x8a, 0x01, 0xb3, 0x01, /* bytes 8992 - 9008 */ + 0x4f, 0x02, 0xd6, 0x00, 0x5b, 0x02, 0x9a, 0x00, 0x1d, 0x02, 0xc9, 0x00, 0x53, 0x01, 0xf2, 0x00, /* bytes 9008 - 9024 */ + 0xa8, 0x00, 0x3a, 0x01, 0xa8, 0x00, 0xdb, 0x01, 0xb0, 0x00, 0x0e, 0x02, 0xe1, 0x00, 0xe5, 0x01, /* bytes 9024 - 9040 */ + 0x8a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x88, 0x01, 0x1d, 0x01, 0x09, /* bytes 9040 - 9056 */ + 0x01, 0x1d, 0x01, 0x19, 0x01, 0xdd, 0x00, 0x99, 0x01, 0xde, 0x00, 0x88, 0x01, 0x1d, 0x01, 0x03, /* bytes 9056 - 9072 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x11, 0x01, 0x81, 0x01, 0xf2, 0x00, 0x81, 0x01, /* bytes 9072 - 9088 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x39, 0x01, 0xb5, 0x01, 0x59, 0x01, 0xb3, /* bytes 9088 - 9104 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x59, 0x01, 0x4e, 0x01, 0x79, 0x01, /* bytes 9104 - 9120 */ + 0x4e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0xb2, 0x01, 0xe2, /* bytes 9120 - 9136 */ + 0x00, 0xb1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x47, 0x01, 0x80, 0x01, /* bytes 9136 - 9152 */ + 0x68, 0x01, 0x80, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd1, 0x00, 0xe4, /* bytes 9152 - 9168 */ + 0x01, 0xf2, 0x00, 0xe5, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, /* bytes 9168 - 9184 */ + 0x4f, 0x01, 0x22, 0x01, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x49, /* bytes 9184 - 9200 */ + 0x01, 0xe4, 0x01, 0x28, 0x01, 0xe4, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 9200 - 9216 */ + 0xb3, 0x01, 0x4f, 0x02, 0x85, 0x01, 0x1d, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 9216 - 9232 */ + 0x00, 0xdb, 0x01, 0xb0, 0x00, 0xb1, 0x01, 0x57, 0x01, 0x85, 0x01, 0x1d, 0x02, 0x9a, 0x00, 0x1d, /* bytes 9232 - 9248 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0xff, 0xa4, 0x01, 0x4c, 0x00, /* bytes 9248 - 9264 */ + 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x34, 0x01, 0xea, /* bytes 9264 - 9280 */ + 0xff, 0x13, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x01, 0xa8, 0x00, /* bytes 9280 - 9296 */ + 0x4a, 0x01, 0x6c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x74, 0x00, 0xfc, /* bytes 9296 - 9312 */ + 0x00, 0x4c, 0x00, 0xc4, 0x00, 0x23, 0x00, 0x10, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 9312 - 9328 */ + 0x0a, 0x00, 0x99, 0x00, 0x1a, 0x02, 0xcf, 0x00, 0x3b, 0x01, 0xf8, 0x00, 0x9b, 0x00, 0x3e, 0x01, /* bytes 9328 - 9344 */ + 0xa0, 0x00, 0xde, 0x01, 0xa0, 0x00, 0x0e, 0x02, 0xdd, 0x00, 0xee, 0x01, 0x88, 0x01, 0xb5, 0x01, /* bytes 9344 - 9360 */ + 0x51, 0x02, 0xd0, 0x00, 0x55, 0x02, 0x99, 0x00, 0x1a, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9360 - 9376 */ + 0x00, 0x05, 0x00, 0x1e, 0x01, 0xcf, 0x00, 0x0a, 0x01, 0x0e, 0x01, 0x8a, 0x01, 0x10, 0x01, 0x9b, /* bytes 9376 - 9392 */ + 0x01, 0xd2, 0x00, 0x1e, 0x01, 0xcf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 9392 - 9408 */ + 0x44, 0x01, 0x77, 0x01, 0x65, 0x01, 0x76, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 9408 - 9424 */ + 0x00, 0x29, 0x01, 0xdd, 0x01, 0x49, 0x01, 0xde, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 9424 - 9440 */ + 0x02, 0x00, 0xec, 0x00, 0x74, 0x01, 0x0c, 0x01, 0x75, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 9440 - 9456 */ + 0x00, 0x02, 0x00, 0xff, 0x00, 0xa9, 0x01, 0xe0, 0x00, 0xa7, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 9456 - 9472 */ + 0x01, 0x00, 0x02, 0x00, 0x20, 0x01, 0x40, 0x01, 0xff, 0x00, 0x40, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 9472 - 9488 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x59, 0x01, 0xa8, 0x01, 0x38, 0x01, 0xa9, 0x01, 0x03, 0x00, 0xc0, /* bytes 9488 - 9504 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdd, 0x01, 0xf2, 0x00, 0xde, 0x01, 0x03, 0x00, /* bytes 9504 - 9520 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, 0x01, 0x41, 0x01, 0x76, 0x01, 0x43, 0x01, 0x03, /* bytes 9520 - 9536 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0x51, 0x02, 0x7b, 0x01, 0x0b, 0x02, /* bytes 9536 - 9552 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x99, 0x00, 0x1a, 0x02, 0x7b, 0x01, 0x0b, /* bytes 9552 - 9568 */ + 0x02, 0xa9, 0x01, 0x55, 0x01, 0xde, 0x01, 0xa0, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 9568 - 9584 */ + 0x02, 0x00, 0x4c, 0x00, 0x34, 0x01, 0xec, 0xff, 0x14, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 9584 - 9600 */ + 0x00, 0x02, 0x00, 0x4c, 0x00, 0x7c, 0x01, 0x0a, 0x00, 0x9d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9600 - 9616 */ + 0x01, 0x00, 0x02, 0x00, 0x3e, 0x01, 0xa0, 0x00, 0x4a, 0x01, 0x61, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 9616 - 9632 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x32, 0x00, 0x7c, 0x00, 0x4c, 0x00, 0xc4, 0x00, 0x23, 0x00, 0x10, /* bytes 9632 - 9648 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xe7, 0x01, 0x7c, 0x00, 0x19, 0x02, /* bytes 9648 - 9664 */ + 0xc0, 0x00, 0xe3, 0x01, 0x7b, 0x01, 0xa3, 0x01, 0x47, 0x02, 0xc3, 0x00, 0x42, 0x02, 0x8d, 0x00, /* bytes 9664 - 9680 */ + 0x08, 0x02, 0xd3, 0x00, 0x21, 0x01, 0x08, 0x01, 0x76, 0x00, 0x49, 0x01, 0x7d, 0x00, 0xe7, 0x01, /* bytes 9680 - 9696 */ + 0x7c, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x11, 0x01, 0xee, 0x00, 0x91, /* bytes 9696 - 9712 */ + 0x01, 0xf1, 0x00, 0xa6, 0x01, 0xb3, 0x00, 0x26, 0x01, 0xaf, 0x00, 0x11, 0x01, 0xee, 0x00, 0x03, /* bytes 9712 - 9728 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x01, 0x5d, 0x01, 0xf1, 0x00, 0x5d, 0x01, /* bytes 9728 - 9744 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5e, 0x01, 0x21, 0x01, 0x7e, 0x01, 0x21, /* bytes 9744 - 9760 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x26, 0x01, 0x1e, 0x01, 0x06, 0x01, /* bytes 9760 - 9776 */ + 0x1d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x54, 0x01, 0x97, 0x01, 0x35, /* bytes 9776 - 9792 */ + 0x01, 0x96, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfd, 0x00, 0x97, 0x01, /* bytes 9792 - 9808 */ + 0xdd, 0x00, 0x95, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x40, 0x01, 0xce, /* bytes 9808 - 9824 */ + 0x01, 0x20, 0x01, 0xcf, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4a, 0x01, /* bytes 9824 - 9840 */ + 0x5f, 0x01, 0x6a, 0x01, 0x5f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa3, /* bytes 9840 - 9856 */ + 0x01, 0x47, 0x02, 0x71, 0x01, 0x03, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 9856 - 9872 */ + 0xe8, 0x00, 0xcd, 0x01, 0xc9, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 9872 - 9888 */ + 0x00, 0xe7, 0x01, 0x7c, 0x00, 0xa8, 0x01, 0x42, 0x01, 0x71, 0x01, 0x03, 0x02, 0x8d, 0x00, 0x08, /* bytes 9888 - 9904 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xec, 0xff, 0x14, 0x01, 0x9c, 0xff, /* bytes 9904 - 9920 */ + 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x7c, 0x01, 0x0c, /* bytes 9920 - 9936 */ + 0x00, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x49, 0x01, 0x7d, 0x00, /* bytes 9936 - 9952 */ + 0x57, 0x01, 0x58, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, 0x00, 0xba, /* bytes 9952 - 9968 */ + 0x00, 0x31, 0x00, 0x7a, 0x00, 0x23, 0x00, 0x0f, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 9968 - 9984 */ + 0x0a, 0x00, 0x95, 0x00, 0xef, 0x01, 0xce, 0x00, 0x01, 0x01, 0x07, 0x01, 0x59, 0x00, 0x4e, 0x01, /* bytes 9984 - 10000 */ + 0x5a, 0x00, 0xe5, 0x01, 0x60, 0x00, 0x19, 0x02, 0x98, 0x00, 0xe5, 0x01, 0x5c, 0x01, 0xaa, 0x01, /* bytes 10000 - 10016 */ + 0x2d, 0x02, 0xca, 0x00, 0x29, 0x02, 0x95, 0x00, 0xef, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 10016 - 10032 */ + 0x00, 0x05, 0x00, 0x26, 0x01, 0x8e, 0x00, 0x11, 0x01, 0xcd, 0x00, 0x91, 0x01, 0xd1, 0x00, 0xa6, /* bytes 10032 - 10048 */ + 0x01, 0x92, 0x00, 0x26, 0x01, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10048 - 10064 */ + 0xaa, 0x01, 0x2d, 0x02, 0x71, 0x01, 0xe1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 10064 - 10080 */ + 0x00, 0xf1, 0x00, 0x34, 0x01, 0x11, 0x01, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 10080 - 10096 */ + 0x02, 0x00, 0x69, 0x01, 0x38, 0x01, 0x49, 0x01, 0x37, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 10096 - 10112 */ + 0x00, 0x02, 0x00, 0xcc, 0x00, 0xaa, 0x01, 0xec, 0x00, 0xab, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 10112 - 10128 */ + 0x01, 0x00, 0x02, 0x00, 0x5f, 0x01, 0xff, 0x00, 0x7e, 0x01, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 10128 - 10144 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x56, 0x01, 0x78, 0x01, 0x36, 0x01, 0x76, 0x01, 0x03, 0x00, 0xc0, /* bytes 10144 - 10160 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0x01, 0xae, 0x01, 0x24, 0x01, 0xae, 0x01, 0x03, 0x00, /* bytes 10160 - 10176 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfe, 0x00, 0x74, 0x01, 0xde, 0x00, 0x73, 0x01, 0x03, /* bytes 10176 - 10192 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x06, 0x01, 0xfd, 0x00, 0x26, 0x01, 0xfe, 0x00, /* bytes 10192 - 10208 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x95, 0x00, 0xef, 0x01, 0x71, 0x01, 0xe1, /* bytes 10208 - 10224 */ + 0x01, 0xb1, 0x01, 0x23, 0x01, 0xe5, 0x01, 0x60, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 10224 - 10240 */ + 0x02, 0x00, 0xe1, 0xff, 0x10, 0x01, 0x9a, 0xff, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10240 - 10256 */ + 0x00, 0x02, 0x00, 0xcc, 0xff, 0xcc, 0x01, 0x0c, 0x00, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 10256 - 10272 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x01, 0x36, 0x00, 0x4e, 0x01, 0x5a, 0x00, 0x23, 0x00, 0x0e, 0x00, /* bytes 10272 - 10288 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0x90, 0x00, 0xc3, 0x01, 0xce, 0x00, 0x08, /* bytes 10288 - 10304 */ + 0x01, 0xfd, 0x00, 0x62, 0x00, 0x45, 0x01, 0x65, 0x00, 0xdc, 0x01, 0x6b, 0x00, 0x11, 0x02, 0xa6, /* bytes 10304 - 10320 */ + 0x00, 0xe7, 0x01, 0x3e, 0x01, 0xa4, 0x01, 0x06, 0x02, 0xc4, 0x00, 0xff, 0x01, 0x90, 0x00, 0xc3, /* bytes 10320 - 10336 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x1e, 0x01, 0x9b, 0x00, 0x0b, 0x01, /* bytes 10336 - 10352 */ + 0xdb, 0x00, 0x8c, 0x01, 0xdd, 0x00, 0x9e, 0x01, 0x9e, 0x00, 0x1e, 0x01, 0x9b, 0x00, 0x03, 0x00, /* bytes 10352 - 10368 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x12, 0x01, 0x2e, 0x01, 0xf2, 0x00, 0x2d, 0x01, 0x03, /* bytes 10368 - 10384 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6a, 0x01, 0x31, 0x01, 0x4a, 0x01, 0x30, 0x01, /* bytes 10384 - 10400 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x38, 0x01, 0x5d, 0x01, 0x58, 0x01, 0x5e, /* bytes 10400 - 10416 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xee, 0x00, 0x8c, 0x01, 0xce, 0x00, /* bytes 10416 - 10432 */ + 0x8b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x46, 0x01, 0x8e, 0x01, 0x26, /* bytes 10432 - 10448 */ + 0x01, 0x8d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x21, 0x01, 0x0b, 0x01, /* bytes 10448 - 10464 */ + 0x01, 0x01, 0x0a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x5b, /* bytes 10464 - 10480 */ + 0x01, 0xe0, 0x00, 0x5a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x59, 0x01, /* bytes 10480 - 10496 */ + 0x0c, 0x01, 0x79, 0x01, 0x0d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, /* bytes 10496 - 10512 */ + 0x01, 0xc3, 0x01, 0xa4, 0x01, 0x06, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 10512 - 10528 */ + 0x90, 0x00, 0xc3, 0x01, 0x77, 0x01, 0xc3, 0x01, 0xb2, 0x01, 0x05, 0x01, 0xdc, 0x01, 0x6b, 0x00, /* bytes 10528 - 10544 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0xa3, 0x01, 0xc9, 0xff, 0xce, /* bytes 10544 - 10560 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x45, 0x01, 0x65, 0x00, 0x5f, 0x01, /* bytes 10560 - 10576 */ + 0x1a, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0x3d, /* bytes 10576 - 10592 */ + 0x01, 0x97, 0x00, 0xd5, 0x01, 0x98, 0x00, 0x0c, 0x02, 0xd0, 0x00, 0xe0, 0x01, 0x60, 0x01, 0xab, /* bytes 10592 - 10608 */ + 0x01, 0x00, 0x02, 0xcb, 0x00, 0xf9, 0x01, 0x96, 0x00, 0xc0, 0x01, 0xcb, 0x00, 0x3d, 0x01, 0xf5, /* bytes 10608 - 10624 */ + 0x00, 0x96, 0x00, 0x3d, 0x01, 0x97, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, /* bytes 10624 - 10640 */ + 0x18, 0x01, 0xcb, 0x00, 0x08, 0x01, 0x0b, 0x01, 0x88, 0x01, 0x0b, 0x01, 0x98, 0x01, 0xcc, 0x00, /* bytes 10640 - 10656 */ + 0x18, 0x01, 0xcb, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x42, 0x01, 0xa1, /* bytes 10656 - 10672 */ + 0x01, 0x22, 0x01, 0xa0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xee, 0x00, /* bytes 10672 - 10688 */ + 0x65, 0x01, 0x0e, 0x01, 0x65, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x66, /* bytes 10688 - 10704 */ + 0x01, 0x66, 0x01, 0x46, 0x01, 0x66, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10704 - 10720 */ + 0x58, 0x01, 0x3b, 0x01, 0x78, 0x01, 0x3b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 10720 - 10736 */ + 0x00, 0x73, 0x01, 0xe5, 0x01, 0xab, 0x01, 0x00, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 10736 - 10752 */ + 0x02, 0x00, 0xca, 0x00, 0x9e, 0x01, 0xea, 0x00, 0x9f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 10752 - 10768 */ + 0x00, 0x02, 0x00, 0xfb, 0x00, 0x81, 0x01, 0xdb, 0x00, 0x80, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 10768 - 10784 */ + 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x3b, 0x01, 0x20, 0x01, 0x3b, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 10784 - 10800 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x53, 0x01, 0x82, 0x01, 0x33, 0x01, 0x81, 0x01, 0x03, 0x00, 0xc0, /* bytes 10800 - 10816 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd5, 0x01, 0x98, 0x00, 0xaa, 0x01, 0x25, 0x01, 0x73, 0x01, /* bytes 10816 - 10832 */ + 0xe5, 0x01, 0x96, 0x00, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3d, /* bytes 10832 - 10848 */ + 0x01, 0x97, 0x00, 0x51, 0x01, 0x3e, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 10848 - 10864 */ + 0x00, 0x00, 0x0a, 0x00, 0x0e, 0x02, 0xd7, 0x00, 0xe4, 0x01, 0x7a, 0x01, 0xaf, 0x01, 0x34, 0x02, /* bytes 10864 - 10880 */ + 0xce, 0x00, 0x31, 0x02, 0x97, 0x00, 0xf9, 0x01, 0xca, 0x00, 0x42, 0x01, 0xf7, 0x00, 0x9a, 0x00, /* bytes 10880 - 10896 */ + 0x3f, 0x01, 0x9b, 0x00, 0xd7, 0x01, 0x9e, 0x00, 0x0e, 0x02, 0xd7, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 10896 - 10912 */ + 0x00, 0x01, 0x00, 0x05, 0x00, 0x1a, 0x01, 0xcf, 0x00, 0x07, 0x01, 0x0e, 0x01, 0x87, 0x01, 0x11, /* bytes 10912 - 10928 */ + 0x01, 0x9a, 0x01, 0xd1, 0x00, 0x1a, 0x01, 0xcf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 10928 - 10944 */ + 0x02, 0x00, 0x00, 0x01, 0x9e, 0x01, 0xe0, 0x00, 0x9e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 10944 - 10960 */ + 0x00, 0x02, 0x00, 0xef, 0x00, 0xcc, 0x01, 0xcf, 0x00, 0xcc, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 10960 - 10976 */ + 0x01, 0x00, 0x02, 0x00, 0x47, 0x01, 0x6f, 0x01, 0x67, 0x01, 0x6f, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 10976 - 10992 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x38, 0x01, 0x9e, 0x01, 0x58, 0x01, 0x9e, 0x01, 0x03, 0x00, 0xc0, /* bytes 10992 - 11008 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x76, 0x01, 0x40, 0x01, 0x56, 0x01, 0x40, 0x01, 0x03, 0x00, /* bytes 11008 - 11024 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x27, 0x01, 0xcc, 0x01, 0x47, 0x01, 0xcc, 0x01, 0x03, /* bytes 11024 - 11040 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0f, 0x01, 0x6f, 0x01, 0xef, 0x00, 0x6f, 0x01, /* bytes 11040 - 11056 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfe, 0x00, 0x3e, 0x01, 0x1e, 0x01, 0x3f, /* bytes 11056 - 11072 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x01, 0x02, 0x02, 0xaf, 0x01, /* bytes 11072 - 11088 */ + 0x34, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd7, 0x01, 0x9e, 0x00, 0xac, /* bytes 11088 - 11104 */ + 0x01, 0x42, 0x01, 0x7c, 0x01, 0x02, 0x02, 0x97, 0x00, 0xf9, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 11104 - 11120 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5a, 0x00, 0x3f, 0x01, 0x9b, 0x00, 0x23, 0x00, 0x0d, 0x00, /* bytes 11120 - 11136 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xf7, 0x00, 0x97, 0x00, 0x3f, 0x01, 0x98, /* bytes 11136 - 11152 */ + 0x00, 0xd6, 0x01, 0x99, 0x00, 0x0d, 0x02, 0xd2, 0x00, 0xdd, 0x01, 0x7f, 0x01, 0xb1, 0x01, 0x3c, /* bytes 11152 - 11168 */ + 0x02, 0xd2, 0x00, 0x3b, 0x02, 0x9a, 0x00, 0x03, 0x02, 0xca, 0x00, 0x3f, 0x01, 0xf7, 0x00, 0x97, /* bytes 11168 - 11184 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x08, 0x01, 0x0b, 0x01, 0x88, 0x01, /* bytes 11184 - 11200 */ + 0x0d, 0x01, 0x99, 0x01, 0xcd, 0x00, 0x19, 0x01, 0xcb, 0x00, 0x08, 0x01, 0x0b, 0x01, 0x03, 0x00, /* bytes 11200 - 11216 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2f, 0x01, 0x9e, 0x01, 0x4f, 0x01, 0x9f, 0x01, 0x03, /* bytes 11216 - 11232 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0a, 0x01, 0x6c, 0x01, 0xea, 0x00, 0x6b, 0x01, /* bytes 11232 - 11248 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6e, 0x01, 0x04, 0x02, 0xb1, 0x01, 0x3c, /* bytes 11248 - 11264 */ + 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x42, 0x01, 0x6d, 0x01, 0x62, 0x01, /* bytes 11264 - 11280 */ + 0x6e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x01, 0xce, 0x01, 0x3f, /* bytes 11280 - 11296 */ + 0x01, 0xcf, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd7, 0x00, 0x9c, 0x01, /* bytes 11296 - 11312 */ + 0xf7, 0x00, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x01, 0x3b, /* bytes 11312 - 11328 */ + 0x01, 0xff, 0x00, 0x3b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, 0x01, /* bytes 11328 - 11344 */ + 0x3c, 0x01, 0x77, 0x01, 0x3c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc7, /* bytes 11344 - 11360 */ + 0x00, 0xcd, 0x01, 0xe7, 0x00, 0xcd, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 11360 - 11376 */ + 0x9a, 0x00, 0x03, 0x02, 0x6e, 0x01, 0x04, 0x02, 0xa8, 0x01, 0x45, 0x01, 0xd6, 0x01, 0x99, 0x00, /* bytes 11376 - 11392 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x55, 0x01, 0x5b, 0x00, 0x3f, 0x01, 0x98, /* bytes 11392 - 11408 */ + 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xc4, 0x00, /* bytes 11408 - 11424 */ + 0x37, 0x02, 0x8f, 0x00, 0xfd, 0x01, 0xca, 0x00, 0x39, 0x01, 0xf7, 0x00, 0x91, 0x00, 0x3f, 0x01, /* bytes 11424 - 11440 */ + 0x92, 0x00, 0xd8, 0x01, 0x94, 0x00, 0x0e, 0x02, 0xcd, 0x00, 0xe4, 0x01, 0x76, 0x01, 0xa5, 0x01, /* bytes 11440 - 11456 */ + 0x3c, 0x02, 0xc4, 0x00, 0x37, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x87, /* bytes 11456 - 11472 */ + 0x01, 0x07, 0x01, 0x07, 0x01, 0x05, 0x01, 0x1a, 0x01, 0xc5, 0x00, 0x9a, 0x01, 0xc7, 0x00, 0x87, /* bytes 11472 - 11488 */ + 0x01, 0x07, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfe, 0x00, 0x35, 0x01, /* bytes 11488 - 11504 */ + 0x1e, 0x01, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x45, 0x01, 0xcb, /* bytes 11504 - 11520 */ + 0x01, 0x25, 0x01, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, /* bytes 11520 - 11536 */ + 0x9a, 0x01, 0xe0, 0x00, 0x9a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0f, /* bytes 11536 - 11552 */ + 0x01, 0x68, 0x01, 0xef, 0x00, 0x68, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 11552 - 11568 */ + 0x38, 0x01, 0x9a, 0x01, 0x58, 0x01, 0x9a, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 11568 - 11584 */ + 0x00, 0xed, 0x00, 0xcb, 0x01, 0xcd, 0x00, 0xca, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 11584 - 11600 */ + 0x02, 0x00, 0x67, 0x01, 0x68, 0x01, 0x47, 0x01, 0x68, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 11600 - 11616 */ + 0x00, 0x02, 0x00, 0x76, 0x01, 0x37, 0x01, 0x56, 0x01, 0x36, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 11616 - 11632 */ + 0x01, 0x00, 0x02, 0x00, 0xa5, 0x01, 0x3c, 0x02, 0x7c, 0x01, 0xfe, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 11632 - 11648 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xd8, 0x01, 0x94, 0x00, 0xac, 0x01, 0x3f, 0x01, 0x7c, 0x01, 0xfe, /* bytes 11648 - 11664 */ + 0x01, 0x8f, 0x00, 0xfd, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, /* bytes 11664 - 11680 */ + 0x56, 0x00, 0x3f, 0x01, 0x92, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 11680 - 11696 */ + 0x00, 0x0a, 0x00, 0xca, 0x00, 0x34, 0x01, 0xf7, 0x00, 0x8d, 0x00, 0x3f, 0x01, 0x8e, 0x00, 0xd6, /* bytes 11696 - 11712 */ + 0x01, 0x8f, 0x00, 0x0d, 0x02, 0xc7, 0x00, 0xdd, 0x01, 0x74, 0x01, 0xb1, 0x01, 0x34, 0x02, 0xd2, /* bytes 11712 - 11728 */ + 0x00, 0x34, 0x02, 0x9a, 0x00, 0xfb, 0x01, 0xca, 0x00, 0x34, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 11728 - 11744 */ + 0x01, 0x00, 0x05, 0x00, 0x88, 0x01, 0x02, 0x01, 0x08, 0x01, 0x01, 0x01, 0x19, 0x01, 0xc1, 0x00, /* bytes 11744 - 11760 */ + 0x99, 0x01, 0xc2, 0x00, 0x88, 0x01, 0x02, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 11760 - 11776 */ + 0x00, 0x42, 0x01, 0x63, 0x01, 0x62, 0x01, 0x64, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 11776 - 11792 */ + 0x02, 0x00, 0xea, 0x00, 0x61, 0x01, 0x0a, 0x01, 0x61, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 11792 - 11808 */ + 0x00, 0x02, 0x00, 0x3f, 0x01, 0xc5, 0x01, 0x1f, 0x01, 0xc5, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 11808 - 11824 */ + 0x01, 0x00, 0x02, 0x00, 0x1f, 0x01, 0x31, 0x01, 0xff, 0x00, 0x31, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 11824 - 11840 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xf7, 0x00, 0x92, 0x01, 0xd7, 0x00, 0x91, 0x01, 0x03, 0x00, 0xc0, /* bytes 11840 - 11856 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe7, 0x00, 0xc4, 0x01, 0xc7, 0x00, 0xc3, 0x01, 0x03, 0x00, /* bytes 11856 - 11872 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2f, 0x01, 0x93, 0x01, 0x4f, 0x01, 0x94, 0x01, 0x03, /* bytes 11872 - 11888 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb1, 0x01, 0x34, 0x02, 0x6e, 0x01, 0xfa, 0x01, /* bytes 11888 - 11904 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, 0x01, 0x32, 0x01, 0x77, 0x01, 0x32, /* bytes 11904 - 11920 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x9a, 0x00, 0xfb, 0x01, 0x6e, 0x01, /* bytes 11920 - 11936 */ + 0xfa, 0x01, 0xa8, 0x01, 0x3b, 0x01, 0xd6, 0x01, 0x8f, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 11936 - 11952 */ + 0x00, 0x02, 0x00, 0x55, 0x01, 0x50, 0x00, 0x3f, 0x01, 0x8e, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, /* bytes 11952 - 11968 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xf7, 0x00, 0x8a, 0x00, 0x3f, 0x01, 0x8b, 0x00, /* bytes 11968 - 11984 */ + 0xd8, 0x01, 0x8d, 0x00, 0x0e, 0x02, 0xc6, 0x00, 0xe4, 0x01, 0x6d, 0x01, 0xa5, 0x01, 0x31, 0x02, /* bytes 11984 - 12000 */ + 0xc4, 0x00, 0x2c, 0x02, 0x8f, 0x00, 0xf3, 0x01, 0xca, 0x00, 0x32, 0x01, 0xf7, 0x00, 0x8a, 0x00, /* bytes 12000 - 12016 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x07, 0x01, 0xfe, 0x00, 0x87, 0x01, 0x00, /* bytes 12016 - 12032 */ + 0x01, 0x9a, 0x01, 0xc0, 0x00, 0x1a, 0x01, 0xbe, 0x00, 0x07, 0x01, 0xfe, 0x00, 0x03, 0x00, 0xc0, /* bytes 12032 - 12048 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x90, 0x01, 0x00, 0x01, 0x90, 0x01, 0x03, 0x00, /* bytes 12048 - 12064 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa5, 0x01, 0x31, 0x02, 0x7c, 0x01, 0xf5, 0x01, 0x03, /* bytes 12064 - 12080 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xed, 0x00, 0xc1, 0x01, 0xcd, 0x00, 0xc0, 0x01, /* bytes 12080 - 12096 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x56, 0x01, 0x2f, 0x01, 0x76, 0x01, 0x30, /* bytes 12096 - 12112 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x25, 0x01, 0xc1, 0x01, 0x45, 0x01, /* bytes 12112 - 12128 */ + 0xc1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x90, 0x01, 0x38, /* bytes 12128 - 12144 */ + 0x01, 0x90, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xef, 0x00, 0x5f, 0x01, /* bytes 12144 - 12160 */ + 0x0f, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x67, 0x01, 0x60, /* bytes 12160 - 12176 */ + 0x01, 0x47, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1e, 0x01, /* bytes 12176 - 12192 */ + 0x2e, 0x01, 0xfe, 0x00, 0x2e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd8, /* bytes 12192 - 12208 */ + 0x01, 0x8d, 0x00, 0xac, 0x01, 0x35, 0x01, 0x7c, 0x01, 0xf5, 0x01, 0x8f, 0x00, 0xf3, 0x01, 0x03, /* bytes 12208 - 12224 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3f, 0x01, 0x8b, 0x00, 0x4c, 0x01, 0x4d, 0x00, /* bytes 12224 - 12240 */ + 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xd2, 0x00, 0x2c, /* bytes 12240 - 12256 */ + 0x02, 0x9a, 0x00, 0xf3, 0x01, 0xca, 0x00, 0x30, 0x01, 0xf7, 0x00, 0x89, 0x00, 0x3f, 0x01, 0x8a, /* bytes 12256 - 12272 */ + 0x00, 0xd6, 0x01, 0x8c, 0x00, 0x0d, 0x02, 0xc4, 0x00, 0xdd, 0x01, 0x6e, 0x01, 0xb1, 0x01, 0x2c, /* bytes 12272 - 12288 */ + 0x02, 0xd2, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x99, 0x01, /* bytes 12288 - 12304 */ + 0xbf, 0x00, 0x19, 0x01, 0xbe, 0x00, 0x08, 0x01, 0xfd, 0x00, 0x88, 0x01, 0xff, 0x00, 0x99, 0x01, /* bytes 12304 - 12320 */ + 0xbf, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x01, 0xbe, 0x01, 0x3f, /* bytes 12320 - 12336 */ + 0x01, 0xbf, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf7, 0x00, 0x8c, 0x01, /* bytes 12336 - 12352 */ + 0xd7, 0x00, 0x8b, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, 0x01, 0x2f, /* bytes 12352 - 12368 */ + 0x01, 0x57, 0x01, 0x2e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xea, 0x00, /* bytes 12368 - 12384 */ + 0x5c, 0x01, 0x0a, 0x01, 0x5d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, /* bytes 12384 - 12400 */ + 0x01, 0x2e, 0x01, 0xff, 0x00, 0x2d, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 12400 - 12416 */ + 0xc7, 0x00, 0xbc, 0x01, 0xe7, 0x00, 0xbd, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 12416 - 12432 */ + 0x00, 0x42, 0x01, 0x5e, 0x01, 0x62, 0x01, 0x5f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 12432 - 12448 */ + 0x02, 0x00, 0x4f, 0x01, 0x8f, 0x01, 0x2f, 0x01, 0x8e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 12448 - 12464 */ + 0x00, 0x02, 0x00, 0x6e, 0x01, 0xf4, 0x01, 0xb1, 0x01, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 12464 - 12480 */ + 0x01, 0x00, 0x04, 0x00, 0xd6, 0x01, 0x8c, 0x00, 0xa8, 0x01, 0x35, 0x01, 0x6e, 0x01, 0xf4, 0x01, /* bytes 12480 - 12496 */ + 0x9a, 0x00, 0xf3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x55, 0x01, 0x4b, /* bytes 12496 - 12512 */ + 0x00, 0x3f, 0x01, 0x8a, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 12512 - 12528 */ + 0x0a, 0x00, 0x0e, 0x02, 0xc6, 0x00, 0xe4, 0x01, 0x6a, 0x01, 0xa5, 0x01, 0x2c, 0x02, 0xc4, 0x00, /* bytes 12528 - 12544 */ + 0x27, 0x02, 0x8f, 0x00, 0xee, 0x01, 0xca, 0x00, 0x31, 0x01, 0xf7, 0x00, 0x89, 0x00, 0x3f, 0x01, /* bytes 12544 - 12560 */ + 0x8a, 0x00, 0xd8, 0x01, 0x8d, 0x00, 0x0e, 0x02, 0xc6, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 12560 - 12576 */ + 0x00, 0x05, 0x00, 0x07, 0x01, 0xfd, 0x00, 0x87, 0x01, 0xff, 0x00, 0x9a, 0x01, 0xc0, 0x00, 0x1a, /* bytes 12576 - 12592 */ + 0x01, 0xbd, 0x00, 0x07, 0x01, 0xfd, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 12592 - 12608 */ + 0xed, 0x00, 0xbd, 0x01, 0xcd, 0x00, 0xbd, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 12608 - 12624 */ + 0x00, 0x47, 0x01, 0x5e, 0x01, 0x67, 0x01, 0x5e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 12624 - 12640 */ + 0x02, 0x00, 0x45, 0x01, 0xbe, 0x01, 0x25, 0x01, 0xbe, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 12640 - 12656 */ + 0x00, 0x02, 0x00, 0x58, 0x01, 0x8e, 0x01, 0x38, 0x01, 0x8e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 12656 - 12672 */ + 0x01, 0x00, 0x02, 0x00, 0xa5, 0x01, 0x2c, 0x02, 0x7c, 0x01, 0xf2, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 12672 - 12688 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xef, 0x00, 0x5e, 0x01, 0x0f, 0x01, 0x5e, 0x01, 0x03, 0x00, 0xc0, /* bytes 12688 - 12704 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x8e, 0x01, 0xe0, 0x00, 0x8e, 0x01, 0x03, 0x00, /* bytes 12704 - 12720 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfe, 0x00, 0x2d, 0x01, 0x1e, 0x01, 0x2d, 0x01, 0x03, /* bytes 12720 - 12736 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x76, 0x01, 0x2f, 0x01, 0x56, 0x01, 0x2e, 0x01, /* bytes 12736 - 12752 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x8f, 0x00, 0xee, 0x01, 0x7c, 0x01, 0xf2, /* bytes 12752 - 12768 */ + 0x01, 0xac, 0x01, 0x32, 0x01, 0xd8, 0x01, 0x8d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 12768 - 12784 */ + 0x02, 0x00, 0x4c, 0x01, 0x4a, 0x00, 0x3f, 0x01, 0x8a, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, /* bytes 12784 - 12800 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x01, 0x8b, 0x00, 0xd6, 0x01, 0x8c, 0x00, 0x0d, /* bytes 12800 - 12816 */ + 0x02, 0xc5, 0x00, 0xdd, 0x01, 0x6e, 0x01, 0xb1, 0x01, 0x2b, 0x02, 0xd2, 0x00, 0x2a, 0x02, 0x9a, /* bytes 12816 - 12832 */ + 0x00, 0xf1, 0x01, 0xca, 0x00, 0x31, 0x01, 0xf7, 0x00, 0x8a, 0x00, 0x3f, 0x01, 0x8b, 0x00, 0x03, /* bytes 12832 - 12848 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x99, 0x01, 0xc0, 0x00, 0x19, 0x01, 0xbe, 0x00, /* bytes 12848 - 12864 */ + 0x08, 0x01, 0xfe, 0x00, 0x88, 0x01, 0xff, 0x00, 0x99, 0x01, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x03, /* bytes 12864 - 12880 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xea, 0x00, 0x5c, 0x01, 0x0a, 0x01, 0x5d, 0x01, 0x03, 0x00, 0xc0, /* bytes 12880 - 12896 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6e, 0x01, 0xf3, 0x01, 0xb1, 0x01, 0x2b, 0x02, 0x03, 0x00, /* bytes 12896 - 12912 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x42, 0x01, 0x5e, 0x01, 0x62, 0x01, 0x5f, 0x01, 0x03, /* bytes 12912 - 12928 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x01, 0xbd, 0x01, 0x3f, 0x01, 0xbe, 0x01, /* bytes 12928 - 12944 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4f, 0x01, 0x8e, 0x01, 0x2f, 0x01, 0x8d, /* bytes 12944 - 12960 */ + 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc7, 0x00, 0xbb, 0x01, 0xe7, 0x00, /* bytes 12960 - 12976 */ + 0xbc, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd7, 0x00, 0x8b, 0x01, 0xf7, /* bytes 12976 - 12992 */ + 0x00, 0x8c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, 0x01, 0x2f, 0x01, /* bytes 12992 - 13008 */ + 0x57, 0x01, 0x2f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xff, 0x00, 0x2e, /* bytes 13008 - 13024 */ + 0x01, 0x1f, 0x01, 0x2e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd6, 0x01, /* bytes 13024 - 13040 */ + 0x8c, 0x00, 0xa8, 0x01, 0x35, 0x01, 0x6e, 0x01, 0xf3, 0x01, 0x9a, 0x00, 0xf1, 0x01, 0x03, 0x00, /* bytes 13040 - 13056 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3f, 0x01, 0x8b, 0x00, 0x55, 0x01, 0x4a, 0x00, 0x23, /* bytes 13056 - 13072 */ + 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, 0x00, 0xca, 0x00, 0x32, 0x01, /* bytes 13072 - 13088 */ + 0xf7, 0x00, 0x8a, 0x00, 0x3f, 0x01, 0x8b, 0x00, 0xd8, 0x01, 0x8e, 0x00, 0x0e, 0x02, 0xc7, 0x00, /* bytes 13088 - 13104 */ + 0xe4, 0x01, 0x6b, 0x01, 0xa5, 0x01, 0x2d, 0x02, 0xc4, 0x00, 0x27, 0x02, 0x8f, 0x00, 0xee, 0x01, /* bytes 13104 - 13120 */ + 0xca, 0x00, 0x32, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x05, 0x00, 0x07, 0x01, 0xfe, /* bytes 13120 - 13136 */ + 0x00, 0x87, 0x01, 0x00, 0x01, 0x9a, 0x01, 0xc1, 0x00, 0x1a, 0x01, 0xbf, 0x00, 0x07, 0x01, 0xfe, /* bytes 13136 - 13152 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe0, 0x00, 0x8f, 0x01, 0x00, 0x01, /* bytes 13152 - 13168 */ + 0x8f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1e, 0x01, 0x2f, 0x01, 0xfe, /* bytes 13168 - 13184 */ + 0x00, 0x2e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x8f, 0x01, /* bytes 13184 - 13200 */ + 0x38, 0x01, 0x8f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x67, 0x01, 0x5f, /* bytes 13200 - 13216 */ + 0x01, 0x47, 0x01, 0x5f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x25, 0x01, /* bytes 13216 - 13232 */ + 0xbe, 0x01, 0x45, 0x01, 0xbf, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa5, /* bytes 13232 - 13248 */ + 0x01, 0x2d, 0x02, 0x7c, 0x01, 0xf3, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 13248 - 13264 */ + 0x76, 0x01, 0x30, 0x01, 0x56, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 13264 - 13280 */ + 0x00, 0x0f, 0x01, 0x5f, 0x01, 0xef, 0x00, 0x5f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 13280 - 13296 */ + 0x02, 0x00, 0xcd, 0x00, 0xbe, 0x01, 0xed, 0x00, 0xbe, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 13296 - 13312 */ + 0x00, 0x04, 0x00, 0xd8, 0x01, 0x8e, 0x00, 0xac, 0x01, 0x33, 0x01, 0x7c, 0x01, 0xf3, 0x01, 0x8f, /* bytes 13312 - 13328 */ + 0x00, 0xee, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x4b, 0x00, /* bytes 13328 - 13344 */ + 0x3f, 0x01, 0x8b, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x0a, /* bytes 13344 - 13360 */ + 0x00, 0xf7, 0x00, 0x8b, 0x00, 0x3f, 0x01, 0x8c, 0x00, 0xd6, 0x01, 0x8e, 0x00, 0x0d, 0x02, 0xc6, /* bytes 13360 - 13376 */ + 0x00, 0xdd, 0x01, 0x70, 0x01, 0xb1, 0x01, 0x2c, 0x02, 0xd2, 0x00, 0x2b, 0x02, 0x9a, 0x00, 0xf3, /* bytes 13376 - 13392 */ + 0x01, 0xca, 0x00, 0x32, 0x01, 0xf7, 0x00, 0x8b, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 13392 - 13408 */ + 0x05, 0x00, 0x88, 0x01, 0x01, 0x01, 0x08, 0x01, 0xff, 0x00, 0x19, 0x01, 0xc0, 0x00, 0x99, 0x01, /* bytes 13408 - 13424 */ + 0xc1, 0x00, 0x88, 0x01, 0x01, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x57, /* bytes 13424 - 13440 */ + 0x01, 0x30, 0x01, 0x77, 0x01, 0x30, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 13440 - 13456 */ + 0x1f, 0x01, 0x30, 0x01, 0xff, 0x00, 0x2f, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 13456 - 13472 */ + 0x00, 0x62, 0x01, 0x60, 0x01, 0x42, 0x01, 0x60, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 13472 - 13488 */ + 0x02, 0x00, 0xea, 0x00, 0x5d, 0x01, 0x0a, 0x01, 0x5e, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 13488 - 13504 */ + 0x00, 0x02, 0x00, 0xc7, 0x00, 0xbd, 0x01, 0xe7, 0x00, 0xbd, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 13504 - 13520 */ + 0x01, 0x00, 0x02, 0x00, 0x4f, 0x01, 0x90, 0x01, 0x2f, 0x01, 0x8f, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 13520 - 13536 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xf7, 0x00, 0x8d, 0x01, 0xd7, 0x00, 0x8c, 0x01, 0x03, 0x00, 0xc0, /* bytes 13536 - 13552 */ + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6e, 0x01, 0xf5, 0x01, 0xb1, 0x01, 0x2c, 0x02, 0x03, 0x00, /* bytes 13552 - 13568 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x01, 0xbf, 0x01, 0x3f, 0x01, 0xbf, 0x01, 0x03, /* bytes 13568 - 13584 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd6, 0x01, 0x8e, 0x00, 0xa8, 0x01, 0x36, 0x01, /* bytes 13584 - 13600 */ + 0x6e, 0x01, 0xf5, 0x01, 0x9a, 0x00, 0xf3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 13600 - 13616 */ + 0x00, 0x3f, 0x01, 0x8c, 0x00, 0x55, 0x01, 0x4c, 0x00, +}; +__attribute__ ((aligned (8))) +static const uint8_t RESULT_MUTE_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x6a, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x44, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, /* bytes 16 - 32 */ + 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, /* bytes 32 - 48 */ + 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, /* bytes 48 - 64 */ + 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, /* bytes 64 - 80 */ + 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, /* bytes 80 - 96 */ + 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, /* bytes 96 - 112 */ + 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, /* bytes 112 - 128 */ + 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, /* bytes 128 - 144 */ + 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 144 - 160 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 160 - 176 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 176 - 192 */ + 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 192 - 208 */ + 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, /* bytes 208 - 224 */ + 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 224 - 240 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, /* bytes 240 - 256 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, /* bytes 256 - 272 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, /* bytes 272 - 288 */ + 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 288 - 304 */ + 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 304 - 320 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, /* bytes 320 - 336 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, /* bytes 336 - 352 */ + 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, /* bytes 352 - 368 */ + 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 368 - 384 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 384 - 400 */ + 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, /* bytes 400 - 416 */ + 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, /* bytes 416 - 432 */ + 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, /* bytes 432 - 448 */ + 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, /* bytes 448 - 464 */ + 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, /* bytes 464 - 480 */ + 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, /* bytes 480 - 496 */ + 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, /* bytes 496 - 512 */ + 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, /* bytes 512 - 528 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 528 - 544 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 544 - 560 */ + 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 560 - 576 */ + 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, /* bytes 576 - 592 */ + 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, /* bytes 592 - 608 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, /* bytes 608 - 624 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, /* bytes 624 - 640 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, /* bytes 640 - 656 */ + 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 656 - 672 */ + 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, /* bytes 672 - 688 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, /* bytes 688 - 704 */ + 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, /* bytes 704 - 720 */ + 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, /* bytes 720 - 736 */ + 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 736 - 752 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 752 - 768 */ + 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, /* bytes 768 - 784 */ + 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, /* bytes 784 - 800 */ + 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, /* bytes 800 - 816 */ + 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, /* bytes 816 - 832 */ + 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, /* bytes 832 - 848 */ + 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, /* bytes 848 - 864 */ + 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, /* bytes 864 - 880 */ + 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, /* bytes 880 - 896 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 896 - 912 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 912 - 928 */ + 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 928 - 944 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, /* bytes 944 - 960 */ + 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 960 - 976 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, /* bytes 976 - 992 */ + 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, /* bytes 992 - 1008 */ + 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, /* bytes 1008 - 1024 */ + 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1024 - 1040 */ + 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, /* bytes 1040 - 1056 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, /* bytes 1056 - 1072 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, /* bytes 1072 - 1088 */ + 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, /* bytes 1088 - 1104 */ + 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1104 - 1120 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 1120 - 1136 */ + 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, /* bytes 1136 - 1152 */ + 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, /* bytes 1152 - 1168 */ + 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, /* bytes 1168 - 1184 */ + 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, /* bytes 1184 - 1200 */ + 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, /* bytes 1200 - 1216 */ + 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, /* bytes 1216 - 1232 */ + 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, /* bytes 1232 - 1248 */ + 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, /* bytes 1248 - 1264 */ + 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 1264 - 1280 */ + 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, /* bytes 1280 - 1296 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, /* bytes 1296 - 1312 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, /* bytes 1312 - 1328 */ + 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, /* bytes 1328 - 1344 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, /* bytes 1344 - 1360 */ + 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, /* bytes 1360 - 1376 */ + 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, /* bytes 1376 - 1392 */ + 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, /* bytes 1392 - 1408 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, /* bytes 1408 - 1424 */ + 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, /* bytes 1424 - 1440 */ + 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1440 - 1456 */ + 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 1456 - 1472 */ + 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1472 - 1488 */ + 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 1488 - 1504 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, /* bytes 1504 - 1520 */ + 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, /* bytes 1520 - 1536 */ + 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, /* bytes 1536 - 1552 */ + 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, /* bytes 1552 - 1568 */ + 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, /* bytes 1568 - 1584 */ + 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, /* bytes 1584 - 1600 */ + 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, /* bytes 1600 - 1616 */ + 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, /* bytes 1616 - 1632 */ + 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 1632 - 1648 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, /* bytes 1648 - 1664 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, /* bytes 1664 - 1680 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 1680 - 1696 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 1696 - 1712 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, /* bytes 1712 - 1728 */ + 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, /* bytes 1728 - 1744 */ + 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, /* bytes 1744 - 1760 */ + 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, /* bytes 1760 - 1776 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, /* bytes 1776 - 1792 */ + 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, /* bytes 1792 - 1808 */ + 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1808 - 1824 */ + 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1824 - 1840 */ + 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1840 - 1856 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 1856 - 1872 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 1872 - 1888 */ + 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, /* bytes 1888 - 1904 */ + 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, /* bytes 1904 - 1920 */ + 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, /* bytes 1920 - 1936 */ + 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, /* bytes 1936 - 1952 */ + 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, /* bytes 1952 - 1968 */ + 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, /* bytes 1968 - 1984 */ + 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, /* bytes 1984 - 2000 */ + 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 2000 - 2016 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 2016 - 2032 */ + 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, /* bytes 2032 - 2048 */ + 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 2048 - 2064 */ + 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 2064 - 2080 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 2080 - 2096 */ + 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 2096 - 2112 */ + 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 2112 - 2128 */ + 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, /* bytes 2128 - 2144 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, /* bytes 2144 - 2160 */ + 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, /* bytes 2160 - 2176 */ + 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2176 - 2192 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 2192 - 2208 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 2208 - 2224 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 2224 - 2240 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 2240 - 2256 */ + 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, /* bytes 2256 - 2272 */ + 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, /* bytes 2272 - 2288 */ + 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, /* bytes 2288 - 2304 */ + 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, /* bytes 2304 - 2320 */ + 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, /* bytes 2320 - 2336 */ + 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, /* bytes 2336 - 2352 */ + 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, /* bytes 2352 - 2368 */ + 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 2368 - 2384 */ + 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, /* bytes 2384 - 2400 */ + 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, /* bytes 2400 - 2416 */ + 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, /* bytes 2416 - 2432 */ + 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, /* bytes 2432 - 2448 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2448 - 2464 */ + 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 2464 - 2480 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2480 - 2496 */ + 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, /* bytes 2496 - 2512 */ + 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, /* bytes 2512 - 2528 */ + 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 2528 - 2544 */ + 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, /* bytes 2544 - 2560 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, /* bytes 2560 - 2576 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, /* bytes 2576 - 2592 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 2592 - 2608 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, /* bytes 2608 - 2624 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, /* bytes 2624 - 2640 */ + 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, /* bytes 2640 - 2656 */ + 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, /* bytes 2656 - 2672 */ + 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, /* bytes 2672 - 2688 */ + 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, /* bytes 2688 - 2704 */ + 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, /* bytes 2704 - 2720 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, /* bytes 2720 - 2736 */ + 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, /* bytes 2736 - 2752 */ + 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, /* bytes 2752 - 2768 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, /* bytes 2768 - 2784 */ + 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, /* bytes 2784 - 2800 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, /* bytes 2800 - 2816 */ + 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2816 - 2832 */ + 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 2832 - 2848 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 2848 - 2864 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, /* bytes 2864 - 2880 */ + 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, /* bytes 2880 - 2896 */ + 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2896 - 2912 */ + 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, /* bytes 2912 - 2928 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, /* bytes 2928 - 2944 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, /* bytes 2944 - 2960 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 2960 - 2976 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, /* bytes 2976 - 2992 */ + 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, /* bytes 2992 - 3008 */ + 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, /* bytes 3008 - 3024 */ + 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, /* bytes 3024 - 3040 */ + 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, /* bytes 3040 - 3056 */ + 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, /* bytes 3056 - 3072 */ + 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, /* bytes 3072 - 3088 */ + 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, /* bytes 3088 - 3104 */ + 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 3104 - 3120 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 3120 - 3136 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3136 - 3152 */ + 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 3152 - 3168 */ + 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, /* bytes 3168 - 3184 */ + 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 3184 - 3200 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, /* bytes 3200 - 3216 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, /* bytes 3216 - 3232 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, /* bytes 3232 - 3248 */ + 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 3248 - 3264 */ + 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 3264 - 3280 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, /* bytes 3280 - 3296 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, /* bytes 3296 - 3312 */ + 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, /* bytes 3312 - 3328 */ + 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 3328 - 3344 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 3344 - 3360 */ + 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, /* bytes 3360 - 3376 */ + 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, /* bytes 3376 - 3392 */ + 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, /* bytes 3392 - 3408 */ + 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, /* bytes 3408 - 3424 */ + 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, /* bytes 3424 - 3440 */ + 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, /* bytes 3440 - 3456 */ + 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, /* bytes 3456 - 3472 */ + 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, /* bytes 3472 - 3488 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 3488 - 3504 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 3504 - 3520 */ + 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 3520 - 3536 */ + 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, /* bytes 3536 - 3552 */ + 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, /* bytes 3552 - 3568 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, /* bytes 3568 - 3584 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, /* bytes 3584 - 3600 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, /* bytes 3600 - 3616 */ + 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 3616 - 3632 */ + 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, /* bytes 3632 - 3648 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, /* bytes 3648 - 3664 */ + 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, /* bytes 3664 - 3680 */ + 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, /* bytes 3680 - 3696 */ + 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 3696 - 3712 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 3712 - 3728 */ + 0x00, 0x26, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, /* bytes 3728 - 3744 */ + 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, /* bytes 3744 - 3760 */ + 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, /* bytes 3760 - 3776 */ + 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, /* bytes 3776 - 3792 */ + 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, /* bytes 3792 - 3808 */ + 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, /* bytes 3808 - 3824 */ + 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, /* bytes 3824 - 3840 */ + 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, /* bytes 3840 - 3856 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 3856 - 3872 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 3872 - 3888 */ + 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 3888 - 3904 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, /* bytes 3904 - 3920 */ + 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 3920 - 3936 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, /* bytes 3936 - 3952 */ + 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, /* bytes 3952 - 3968 */ + 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, /* bytes 3968 - 3984 */ + 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3984 - 4000 */ + 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, /* bytes 4000 - 4016 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, /* bytes 4016 - 4032 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, /* bytes 4032 - 4048 */ + 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, /* bytes 4048 - 4064 */ + 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4064 - 4080 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 4080 - 4096 */ + 0xff, 0x00, 0x00, 0x26, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, /* bytes 4096 - 4112 */ + 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, /* bytes 4112 - 4128 */ + 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 4128 - 4144 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 4144 - 4160 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, /* bytes 4160 - 4176 */ + 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, /* bytes 4176 - 4192 */ + 0x00, 0x84, 0x00, 0x01, 0x00, 0x6c, 0x00, 0xe9, 0xff, 0x70, 0x00, 0xdd, 0xff, 0x6c, 0x00, 0xc9, /* bytes 4192 - 4208 */ + 0xff, 0x8c, 0x00, 0xa9, 0xff, 0xb4, 0x00, 0xa9, 0xff, 0xd4, 0x00, 0xc9, 0xff, 0xd0, 0x00, 0xdd, /* bytes 4208 - 4224 */ + 0xff, 0xd4, 0x00, 0xe9, 0xff, 0xbc, 0x00, 0x01, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, /* bytes 4224 - 4240 */ + 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0x03, 0x00, 0xc0, /* bytes 4240 - 4256 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0xdd, 0xff, 0xd0, 0x00, 0xdd, 0xff, 0x03, 0x00, /* bytes 4256 - 4272 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, /* bytes 4272 - 4288 */ + 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, /* bytes 4288 - 4304 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, /* bytes 4304 - 4320 */ + 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, /* bytes 4320 - 4336 */ + 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, /* bytes 4336 - 4352 */ + 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, /* bytes 4352 - 4368 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, /* bytes 4368 - 4384 */ + 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, /* bytes 4384 - 4400 */ + 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4400 - 4416 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 4416 - 4432 */ + 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4432 - 4448 */ + 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 4448 - 4464 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xd2, 0x00, 0xcc, 0xff, 0xce, 0x00, 0xe0, 0xff, 0xd2, /* bytes 4464 - 4480 */ + 0x00, 0xec, 0xff, 0xba, 0x00, 0x04, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, /* bytes 4480 - 4496 */ + 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, /* bytes 4496 - 4512 */ + 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, /* bytes 4512 - 4528 */ + 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 4528 - 4544 */ + 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, /* bytes 4544 - 4560 */ + 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, /* bytes 4560 - 4576 */ + 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, /* bytes 4576 - 4592 */ + 0x00, 0xec, 0x00, 0x82, 0x00, 0x04, 0x00, 0x6a, 0x00, 0xec, 0xff, 0x6e, 0x00, 0xe0, 0xff, 0x6a, /* bytes 4592 - 4608 */ + 0x00, 0xcc, 0xff, 0x8a, 0x00, 0xac, 0xff, 0xb2, 0x00, 0xac, 0xff, 0xd2, 0x00, 0xcc, 0xff, 0x03, /* bytes 4608 - 4624 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6e, 0x00, 0xe0, 0xff, 0xce, 0x00, 0xe0, 0xff, /* bytes 4624 - 4640 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 4640 - 4656 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 4656 - 4672 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, /* bytes 4672 - 4688 */ + 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 4688 - 4704 */ + 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, /* bytes 4704 - 4720 */ + 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, 0x00, 0xec, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x03, /* bytes 4720 - 4736 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, /* bytes 4736 - 4752 */ + 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, /* bytes 4752 - 4768 */ + 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4768 - 4784 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 4784 - 4800 */ + 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 4800 - 4816 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 4816 - 4832 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xcf, 0x00, 0xd1, 0xff, 0xcb, 0x00, 0xe5, /* bytes 4832 - 4848 */ + 0xff, 0xcf, 0x00, 0xf1, 0xff, 0xb7, 0x00, 0x09, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, /* bytes 4848 - 4864 */ + 0x00, 0xf3, 0x00, 0x8c, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0x33, 0x01, 0x5c, 0x00, 0xe3, 0x01, 0x5b, /* bytes 4864 - 4880 */ + 0x00, 0x03, 0x02, 0x73, 0x00, 0x23, 0x02, 0x53, 0x00, 0x53, 0x02, 0x53, 0x00, 0x7b, 0x02, 0x7b, /* bytes 4880 - 4896 */ + 0x00, 0x7c, 0x02, 0xbb, 0x00, 0x3c, 0x02, 0xfb, 0x00, 0x3c, 0x02, 0x13, 0x01, 0x04, 0x02, 0x4b, /* bytes 4896 - 4912 */ + 0x01, 0x04, 0x02, 0x9b, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, /* bytes 4912 - 4928 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, /* bytes 4928 - 4944 */ + 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, /* bytes 4944 - 4960 */ + 0x01, 0x84, 0x00, 0xed, 0x00, 0x7f, 0x00, 0x09, 0x00, 0x67, 0x00, 0xf1, 0xff, 0x6b, 0x00, 0xe5, /* bytes 4960 - 4976 */ + 0xff, 0x67, 0x00, 0xd1, 0xff, 0x87, 0x00, 0xb1, 0xff, 0xaf, 0x00, 0xb1, 0xff, 0xcf, 0x00, 0xd1, /* bytes 4976 - 4992 */ + 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6b, 0x00, 0xe5, 0xff, 0xcb, 0x00, /* bytes 4992 - 5008 */ + 0xe5, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4b, 0x01, 0x04, /* bytes 5008 - 5024 */ + 0x02, 0xdb, 0x00, 0xd4, 0x01, 0xab, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 5024 - 5040 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 5040 - 5056 */ + 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 5056 - 5072 */ + 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 5072 - 5088 */ + 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, 0x00, 0xec, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, /* bytes 5088 - 5104 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x02, 0x73, 0x00, 0x3b, 0x02, /* bytes 5104 - 5120 */ + 0x9b, 0x00, 0x3c, 0x02, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, /* bytes 5120 - 5136 */ + 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 5136 - 5152 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 5152 - 5168 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, /* bytes 5168 - 5184 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 5184 - 5200 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x3c, 0x02, 0x12, 0x01, 0x04, /* bytes 5200 - 5216 */ + 0x02, 0x4a, 0x01, 0x05, 0x02, 0x9a, 0x01, 0xb4, 0x01, 0x4b, 0x01, 0xb4, 0x01, 0x5b, 0x01, 0xb5, /* bytes 5216 - 5232 */ + 0x01, 0xa3, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, /* bytes 5232 - 5248 */ + 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x75, 0x01, 0xc4, 0x00, 0x45, 0x01, 0xc4, 0x00, 0x2d, 0x01, 0xbc, /* bytes 5248 - 5264 */ + 0x00, 0x25, 0x01, 0x83, 0x00, 0xee, 0x00, 0x7e, 0x00, 0x0c, 0x00, 0x66, 0x00, 0xf4, 0xff, 0x6a, /* bytes 5264 - 5280 */ + 0x00, 0xe8, 0xff, 0x66, 0x00, 0xd4, 0xff, 0x86, 0x00, 0xb4, 0xff, 0xae, 0x00, 0xb4, 0xff, 0xce, /* bytes 5280 - 5296 */ + 0x00, 0xd4, 0xff, 0xca, 0x00, 0xe8, 0xff, 0xce, 0x00, 0xf4, 0xff, 0xb6, 0x00, 0x0c, 0x00, 0xbb, /* bytes 5296 - 5312 */ + 0x00, 0xcd, 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xf3, 0x00, 0x8d, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0x32, /* bytes 5312 - 5328 */ + 0x01, 0x5c, 0x00, 0xe2, 0x01, 0x5b, 0x00, 0x02, 0x02, 0x72, 0x00, 0x22, 0x02, 0x52, 0x00, 0x52, /* bytes 5328 - 5344 */ + 0x02, 0x52, 0x00, 0x7a, 0x02, 0x79, 0x00, 0x7b, 0x02, 0xb9, 0x00, 0x3b, 0x02, 0xfa, 0x00, 0x3c, /* bytes 5344 - 5360 */ + 0x02, 0x12, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xca, 0x00, 0xe8, 0xff, /* bytes 5360 - 5376 */ + 0x6a, 0x00, 0xe8, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4a, /* bytes 5376 - 5392 */ + 0x01, 0x03, 0x02, 0xda, 0x00, 0xd3, 0x01, 0xab, 0x00, 0x93, 0x01, 0xe3, 0x00, 0x94, 0x01, 0x2b, /* bytes 5392 - 5408 */ + 0x01, 0xb4, 0x01, 0x4b, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 5408 - 5424 */ + 0x06, 0x00, 0xb5, 0x01, 0xa3, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 5424 - 5440 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x05, 0x02, 0x9a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 5440 - 5456 */ + 0x00, 0x04, 0x00, 0xbc, 0x00, 0x25, 0x01, 0xec, 0x00, 0x25, 0x01, 0xeb, 0x00, 0xe9, 0x00, 0xbb, /* bytes 5456 - 5472 */ + 0x00, 0xcd, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x02, 0x72, 0x00, /* bytes 5472 - 5488 */ + 0x3b, 0x02, 0x9a, 0x00, 0x3b, 0x02, 0xfa, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 5488 - 5504 */ + 0x00, 0x24, 0x01, 0x4c, 0x01, 0x23, 0x01, 0xbc, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, /* bytes 5504 - 5520 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5b, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, /* bytes 5520 - 5536 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, /* bytes 5536 - 5552 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 5552 - 5568 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x4c, 0x01, 0x4b, /* bytes 5568 - 5584 */ + 0x01, 0x25, 0x01, 0x4d, 0x01, 0x1d, 0x01, 0x4d, 0x01, 0xf6, 0x00, 0x76, 0x01, 0xc4, 0x00, 0x48, /* bytes 5584 - 5600 */ + 0x01, 0xc4, 0x00, 0x30, 0x01, 0xbb, 0x00, 0x28, 0x01, 0x82, 0x00, 0xf2, 0x00, 0x7d, 0x00, 0x0e, /* bytes 5600 - 5616 */ + 0x00, 0x65, 0x00, 0xf6, 0xff, 0x69, 0x00, 0xea, 0xff, 0x65, 0x00, 0xd6, 0xff, 0x85, 0x00, 0xb6, /* bytes 5616 - 5632 */ + 0xff, 0xad, 0x00, 0xb6, 0xff, 0xcd, 0x00, 0xd6, 0xff, 0xc9, 0x00, 0xea, 0xff, 0xcd, 0x00, 0xf6, /* bytes 5632 - 5648 */ + 0xff, 0xb5, 0x00, 0x0e, 0x00, 0xb9, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0xc8, 0x00, 0xef, 0x00, 0x8e, /* bytes 5648 - 5664 */ + 0x00, 0x07, 0x01, 0xa6, 0x00, 0x2d, 0x01, 0x5c, 0x00, 0xdd, 0x01, 0x57, 0x00, 0xfe, 0x01, 0x6e, /* bytes 5664 - 5680 */ + 0x00, 0x1d, 0x02, 0x4d, 0x00, 0x4d, 0x02, 0x4b, 0x00, 0x76, 0x02, 0x72, 0x00, 0x78, 0x02, 0xb2, /* bytes 5680 - 5696 */ + 0x00, 0x3a, 0x02, 0xf4, 0x00, 0x3b, 0x02, 0x0c, 0x01, 0x04, 0x02, 0x46, 0x01, 0x07, 0x02, 0x96, /* bytes 5696 - 5712 */ + 0x01, 0xb4, 0x01, 0x48, 0x01, 0xb5, 0x01, 0x58, 0x01, 0xb7, 0x01, 0xa0, 0x01, 0x4d, 0x01, 0x5b, /* bytes 5712 - 5728 */ + 0x01, 0x4c, 0x01, 0x4b, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x69, 0x00, /* bytes 5728 - 5744 */ + 0xea, 0xff, 0xc9, 0x00, 0xea, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, /* bytes 5744 - 5760 */ + 0x01, 0x4b, 0x01, 0xb4, 0x01, 0x48, 0x01, 0x93, 0x01, 0x29, 0x01, 0x91, 0x01, 0xe1, 0x00, 0xcf, /* bytes 5760 - 5776 */ + 0x01, 0xa7, 0x00, 0x01, 0x02, 0xd6, 0x00, 0x04, 0x02, 0x46, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 5776 - 5792 */ + 0x01, 0x00, 0x06, 0x00, 0x07, 0x02, 0x96, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 5792 - 5808 */ + 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb7, 0x01, 0xa0, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 5808 - 5824 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xbb, 0x00, 0x28, 0x01, 0xeb, 0x00, 0x27, 0x01, 0xe9, 0x00, 0xeb, /* bytes 5824 - 5840 */ + 0x00, 0xb9, 0x00, 0xd0, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xfe, 0x01, /* bytes 5840 - 5856 */ + 0x6e, 0x00, 0x37, 0x02, 0x94, 0x00, 0x3a, 0x02, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 5856 - 5872 */ + 0x00, 0x03, 0x00, 0x25, 0x01, 0x4d, 0x01, 0x20, 0x01, 0xbd, 0x00, 0x07, 0x01, 0xa6, 0x00, 0x03, /* bytes 5872 - 5888 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 5888 - 5904 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, /* bytes 5904 - 5920 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0x58, 0x01, 0x4d, 0x01, /* bytes 5920 - 5936 */ + 0x5b, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xb5, /* bytes 5936 - 5952 */ + 0x01, 0x47, 0x01, 0xb5, 0x01, 0x57, 0x01, 0xb8, 0x01, 0x9f, 0x01, 0x4d, 0x01, 0x5b, 0x01, 0x4d, /* bytes 5952 - 5968 */ + 0x01, 0x4b, 0x01, 0x25, 0x01, 0x4d, 0x01, 0x1d, 0x01, 0x4d, 0x01, 0xf6, 0x00, 0x77, 0x01, 0xc4, /* bytes 5968 - 5984 */ + 0x00, 0x49, 0x01, 0xc3, 0x00, 0x31, 0x01, 0xbb, 0x00, 0x29, 0x01, 0x81, 0x00, 0xf4, 0x00, 0x7c, /* bytes 5984 - 6000 */ + 0x00, 0x0f, 0x00, 0x64, 0x00, 0xf7, 0xff, 0x68, 0x00, 0xeb, 0xff, 0x64, 0x00, 0xd7, 0xff, 0x84, /* bytes 6000 - 6016 */ + 0x00, 0xb7, 0xff, 0xac, 0x00, 0xb7, 0xff, 0xcc, 0x00, 0xd7, 0xff, 0xc8, 0x00, 0xeb, 0xff, 0xcc, /* bytes 6016 - 6032 */ + 0x00, 0xf7, 0xff, 0xb4, 0x00, 0x0f, 0x00, 0xb8, 0x00, 0xd1, 0x00, 0xb7, 0x00, 0xc9, 0x00, 0xed, /* bytes 6032 - 6048 */ + 0x00, 0x8f, 0x00, 0x06, 0x01, 0xa6, 0x00, 0x2b, 0x01, 0x5d, 0x00, 0xdb, 0x01, 0x55, 0x00, 0xfc, /* bytes 6048 - 6064 */ + 0x01, 0x6c, 0x00, 0x1a, 0x02, 0x4b, 0x00, 0x4a, 0x02, 0x49, 0x00, 0x74, 0x02, 0x6f, 0x00, 0x76, /* bytes 6064 - 6080 */ + 0x02, 0xaf, 0x00, 0x39, 0x02, 0xf2, 0x00, 0x3a, 0x02, 0x0a, 0x01, 0x04, 0x02, 0x44, 0x01, 0x08, /* bytes 6080 - 6096 */ + 0x02, 0x94, 0x01, 0xb5, 0x01, 0x47, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6096 - 6112 */ + 0x68, 0x00, 0xeb, 0xff, 0xc8, 0x00, 0xeb, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 6112 - 6128 */ + 0x00, 0x4d, 0x01, 0x4b, 0x01, 0xb5, 0x01, 0x47, 0x01, 0x93, 0x01, 0x28, 0x01, 0x90, 0x01, 0xe0, /* bytes 6128 - 6144 */ + 0x00, 0xce, 0x01, 0xa6, 0x00, 0x00, 0x02, 0xd4, 0x00, 0x04, 0x02, 0x44, 0x01, 0x03, 0x00, 0xc0, /* bytes 6144 - 6160 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x02, 0x94, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, /* bytes 6160 - 6176 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb8, 0x01, 0x9f, 0x01, 0x03, 0x00, /* bytes 6176 - 6192 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbb, 0x00, 0x29, 0x01, 0xeb, 0x00, 0x27, 0x01, 0xe9, /* bytes 6192 - 6208 */ + 0x00, 0xeb, 0x00, 0xb8, 0x00, 0xd1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 6208 - 6224 */ + 0xfc, 0x01, 0x6c, 0x00, 0x35, 0x02, 0x92, 0x00, 0x39, 0x02, 0xf2, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 6224 - 6240 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x25, 0x01, 0x4d, 0x01, 0x1f, 0x01, 0xbd, 0x00, 0x06, 0x01, 0xa6, /* bytes 6240 - 6256 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 6256 - 6272 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, /* bytes 6272 - 6288 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0x57, 0x01, /* bytes 6288 - 6304 */ + 0x4d, 0x01, 0x5b, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 6304 - 6320 */ + 0x00, 0xb7, 0x00, 0xd2, 0x00, 0xb4, 0x00, 0x10, 0x00, 0xcc, 0x00, 0xf8, 0xff, 0xc8, 0x00, 0xec, /* bytes 6320 - 6336 */ + 0xff, 0xcc, 0x00, 0xd8, 0xff, 0xac, 0x00, 0xb8, 0xff, 0x84, 0x00, 0xb8, 0xff, 0x64, 0x00, 0xd8, /* bytes 6336 - 6352 */ + 0xff, 0x68, 0x00, 0xec, 0xff, 0x64, 0x00, 0xf8, 0xff, 0x7c, 0x00, 0x10, 0x00, 0x81, 0x00, 0xf4, /* bytes 6352 - 6368 */ + 0x00, 0xbb, 0x00, 0x2a, 0x01, 0xc3, 0x00, 0x31, 0x01, 0xc4, 0x00, 0x49, 0x01, 0xf6, 0x00, 0x77, /* bytes 6368 - 6384 */ + 0x01, 0x1d, 0x01, 0x4d, 0x01, 0x25, 0x01, 0x4d, 0x01, 0x4d, 0x01, 0x4b, 0x01, 0x4d, 0x01, 0x5b, /* bytes 6384 - 6400 */ + 0x01, 0xb8, 0x01, 0x9f, 0x01, 0xb5, 0x01, 0x57, 0x01, 0xb5, 0x01, 0x47, 0x01, 0x08, 0x02, 0x93, /* bytes 6400 - 6416 */ + 0x01, 0x05, 0x02, 0x43, 0x01, 0x3a, 0x02, 0x09, 0x01, 0x39, 0x02, 0xf1, 0x00, 0x76, 0x02, 0xae, /* bytes 6416 - 6432 */ + 0x00, 0x73, 0x02, 0x6e, 0x00, 0x4a, 0x02, 0x48, 0x00, 0x1a, 0x02, 0x4a, 0x00, 0xfb, 0x01, 0x6c, /* bytes 6432 - 6448 */ + 0x00, 0xda, 0x01, 0x55, 0x00, 0x2a, 0x01, 0x5d, 0x00, 0x05, 0x01, 0xa6, 0x00, 0xec, 0x00, 0x8f, /* bytes 6448 - 6464 */ + 0x00, 0xb7, 0x00, 0xca, 0x00, 0xb7, 0x00, 0xd2, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 6464 - 6480 */ + 0x02, 0x00, 0xc8, 0x00, 0xec, 0xff, 0x68, 0x00, 0xec, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 6480 - 6496 */ + 0x00, 0x07, 0x00, 0x4d, 0x01, 0x4b, 0x01, 0xb5, 0x01, 0x47, 0x01, 0x93, 0x01, 0x28, 0x01, 0x90, /* bytes 6496 - 6512 */ + 0x01, 0xe0, 0x00, 0xce, 0x01, 0xa6, 0x00, 0x00, 0x02, 0xd3, 0x00, 0x05, 0x02, 0x43, 0x01, 0x03, /* bytes 6512 - 6528 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb8, 0x01, 0x9f, 0x01, 0xb4, 0x01, 0x24, 0x02, /* bytes 6528 - 6544 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x08, 0x02, 0x93, 0x01, /* bytes 6544 - 6560 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbb, 0x00, 0x2a, 0x01, 0xeb, 0x00, 0x28, /* bytes 6560 - 6576 */ + 0x01, 0xe8, 0x00, 0xec, 0x00, 0xb7, 0x00, 0xd2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 6576 - 6592 */ + 0x03, 0x00, 0xfb, 0x01, 0x6c, 0x00, 0x35, 0x02, 0x91, 0x00, 0x39, 0x02, 0xf1, 0x00, 0x03, 0x00, /* bytes 6592 - 6608 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x05, 0x01, 0xa6, 0x00, 0x1f, 0x01, 0xbd, 0x00, 0x25, /* bytes 6608 - 6624 */ + 0x01, 0x4d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 6624 - 6640 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, /* bytes 6640 - 6656 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, /* bytes 6656 - 6672 */ + 0x57, 0x01, 0x4d, 0x01, 0x5b, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 6672 - 6688 */ + 0x00, 0x26, 0x00, 0x3a, 0x02, 0x09, 0x01, 0x05, 0x02, 0x43, 0x01, 0x09, 0x02, 0x93, 0x01, 0xb5, /* bytes 6688 - 6704 */ + 0x01, 0x47, 0x01, 0xb5, 0x01, 0x57, 0x01, 0xba, 0x01, 0x9f, 0x01, 0x4d, 0x01, 0x5b, 0x01, 0x4d, /* bytes 6704 - 6720 */ + 0x01, 0x4b, 0x01, 0x25, 0x01, 0x4d, 0x01, 0x1d, 0x01, 0x4d, 0x01, 0xf7, 0x00, 0x77, 0x01, 0xc4, /* bytes 6720 - 6736 */ + 0x00, 0x49, 0x01, 0xc3, 0x00, 0x31, 0x01, 0xbb, 0x00, 0x2a, 0x01, 0x81, 0x00, 0xf4, 0x00, 0x7c, /* bytes 6736 - 6752 */ + 0x00, 0x10, 0x00, 0x64, 0x00, 0xf8, 0xff, 0x68, 0x00, 0xec, 0xff, 0x64, 0x00, 0xd8, 0xff, 0x84, /* bytes 6752 - 6768 */ + 0x00, 0xb8, 0xff, 0xac, 0x00, 0xb8, 0xff, 0xcc, 0x00, 0xd8, 0xff, 0xc8, 0x00, 0xec, 0xff, 0xcc, /* bytes 6768 - 6784 */ + 0x00, 0xf8, 0xff, 0xb4, 0x00, 0x10, 0x00, 0xb7, 0x00, 0xd2, 0x00, 0xb7, 0x00, 0xca, 0x00, 0xec, /* bytes 6784 - 6800 */ + 0x00, 0x8f, 0x00, 0x05, 0x01, 0xa6, 0x00, 0x2a, 0x01, 0x5d, 0x00, 0xda, 0x01, 0x55, 0x00, 0xfb, /* bytes 6800 - 6816 */ + 0x01, 0x6b, 0x00, 0x1a, 0x02, 0x4a, 0x00, 0x4a, 0x02, 0x48, 0x00, 0x73, 0x02, 0x6e, 0x00, 0x76, /* bytes 6816 - 6832 */ + 0x02, 0xae, 0x00, 0x39, 0x02, 0xf1, 0x00, 0x3a, 0x02, 0x09, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 6832 - 6848 */ + 0x01, 0x00, 0x02, 0x00, 0x68, 0x00, 0xec, 0xff, 0xc8, 0x00, 0xec, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 6848 - 6864 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x05, 0x02, 0x43, 0x01, 0xff, 0x01, 0xd3, 0x00, 0xcd, 0x01, 0xa5, /* bytes 6864 - 6880 */ + 0x00, 0x90, 0x01, 0xe1, 0x00, 0x93, 0x01, 0x29, 0x01, 0xb5, 0x01, 0x47, 0x01, 0x4d, 0x01, 0x4b, /* bytes 6880 - 6896 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xba, 0x01, 0x9f, 0x01, 0xb5, 0x01, /* bytes 6896 - 6912 */ + 0x22, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x05, 0x02, 0x2a, 0x02, 0x09, 0x02, /* bytes 6912 - 6928 */ + 0x93, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb7, 0x00, 0xd2, 0x00, 0xe8, /* bytes 6928 - 6944 */ + 0x00, 0xec, 0x00, 0xeb, 0x00, 0x28, 0x01, 0xbb, 0x00, 0x2a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6944 - 6960 */ + 0x01, 0x00, 0x03, 0x00, 0x25, 0x01, 0x4d, 0x01, 0x1e, 0x01, 0xbd, 0x00, 0x05, 0x01, 0xa6, 0x00, /* bytes 6960 - 6976 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x39, 0x02, 0xf1, 0x00, 0x35, 0x02, 0x91, /* bytes 6976 - 6992 */ + 0x00, 0xfb, 0x01, 0x6b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 6992 - 7008 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 7008 - 7024 */ + 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7024 - 7040 */ + 0x4d, 0x01, 0x5b, 0x01, 0xb5, 0x01, 0x57, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 7040 - 7056 */ + 0xff, 0x00, 0x00, 0x26, 0x00, 0x06, 0x02, 0x53, 0x01, 0x07, 0x02, 0xa3, 0x01, 0xb6, 0x01, 0x53, /* bytes 7056 - 7072 */ + 0x01, 0xb2, 0x01, 0x62, 0x01, 0xb7, 0x01, 0xab, 0x01, 0x4a, 0x01, 0x5d, 0x01, 0x4b, 0x01, 0x4d, /* bytes 7072 - 7088 */ + 0x01, 0x23, 0x01, 0x4b, 0x01, 0x1b, 0x01, 0x4a, 0x01, 0xf1, 0x00, 0x70, 0x01, 0xc4, 0x00, 0x3e, /* bytes 7088 - 7104 */ + 0x01, 0xc5, 0x00, 0x26, 0x01, 0xbd, 0x00, 0x1e, 0x01, 0x82, 0x00, 0xda, 0x00, 0xad, 0x00, 0xfd, /* bytes 7104 - 7120 */ + 0xff, 0x95, 0x00, 0xe4, 0xff, 0x9a, 0x00, 0xd8, 0xff, 0x96, 0x00, 0xc4, 0xff, 0xb8, 0x00, 0xa5, /* bytes 7120 - 7136 */ + 0xff, 0xdf, 0x00, 0xa6, 0xff, 0xfe, 0x00, 0xc7, 0xff, 0xfa, 0x00, 0xdb, 0xff, 0xfd, 0x00, 0xe7, /* bytes 7136 - 7152 */ + 0xff, 0xe4, 0x00, 0xff, 0xff, 0xc2, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xfd, 0x00, 0x89, /* bytes 7152 - 7168 */ + 0x00, 0x14, 0x01, 0xa2, 0x00, 0x3f, 0x01, 0x5c, 0x00, 0xef, 0x01, 0x65, 0x00, 0x0e, 0x02, 0x7e, /* bytes 7168 - 7184 */ + 0x00, 0x2f, 0x02, 0x60, 0x00, 0x5f, 0x02, 0x62, 0x00, 0x85, 0x02, 0x8c, 0x00, 0x82, 0x02, 0xcc, /* bytes 7184 - 7200 */ + 0x00, 0x3f, 0x02, 0x09, 0x01, 0x3e, 0x02, 0x21, 0x01, 0x06, 0x02, 0x53, 0x01, 0x03, 0x00, 0xc0, /* bytes 7200 - 7216 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfa, 0x00, 0xdb, 0xff, 0x9a, 0x00, 0xd8, 0xff, 0x03, 0x00, /* bytes 7216 - 7232 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x06, 0x02, 0x53, 0x01, 0x06, 0x02, 0xe3, 0x00, 0xd6, /* bytes 7232 - 7248 */ + 0x01, 0xb3, 0x00, 0x96, 0x01, 0xeb, 0x00, 0x96, 0x01, 0x33, 0x01, 0xb6, 0x01, 0x53, 0x01, 0x4b, /* bytes 7248 - 7264 */ + 0x01, 0x4d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb7, 0x01, 0xab, 0x01, /* bytes 7264 - 7280 */ + 0xb7, 0x01, 0x1b, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x07, 0x02, 0x23, 0x02, /* bytes 7280 - 7296 */ + 0x07, 0x02, 0xa3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbd, 0x00, 0x1e, /* bytes 7296 - 7312 */ + 0x01, 0xed, 0x00, 0x20, 0x01, 0xf0, 0x00, 0xe4, 0x00, 0xc2, 0x00, 0xc6, 0x00, 0x03, 0x00, 0xc0, /* bytes 7312 - 7328 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0e, 0x02, 0x7e, 0x00, 0x44, 0x02, 0xa9, 0x00, 0x3f, 0x02, /* bytes 7328 - 7344 */ + 0x09, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x23, 0x01, 0x4b, 0x01, 0x2a, /* bytes 7344 - 7360 */ + 0x01, 0xbb, 0x00, 0x14, 0x01, 0xa2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7360 - 7376 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 7376 - 7392 */ + 0x00, 0xb2, 0x01, 0x62, 0x01, 0x4a, 0x01, 0x5d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7392 - 7408 */ + 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 7408 - 7424 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xfd, 0x00, 0x9e, 0xff, 0x1b, 0x01, 0xc0, 0xff, 0x16, /* bytes 7424 - 7440 */ + 0x01, 0xd3, 0xff, 0x19, 0x01, 0xe0, 0xff, 0x00, 0x01, 0xf6, 0xff, 0xd4, 0x00, 0xbb, 0x00, 0xd5, /* bytes 7440 - 7456 */ + 0x00, 0xb3, 0x00, 0x14, 0x01, 0x84, 0x00, 0x28, 0x01, 0x9f, 0x00, 0x5a, 0x01, 0x5d, 0x00, 0x08, /* bytes 7456 - 7472 */ + 0x02, 0x75, 0x00, 0x25, 0x02, 0x92, 0x00, 0x49, 0x02, 0x76, 0x00, 0x78, 0x02, 0x7d, 0x00, 0x9a, /* bytes 7472 - 7488 */ + 0x02, 0xaa, 0x00, 0x92, 0x02, 0xea, 0x00, 0x49, 0x02, 0x20, 0x01, 0x46, 0x02, 0x38, 0x01, 0x0b, /* bytes 7488 - 7504 */ + 0x02, 0x5d, 0x01, 0x07, 0x02, 0xad, 0x01, 0xbb, 0x01, 0x58, 0x01, 0xb5, 0x01, 0x6c, 0x01, 0xb6, /* bytes 7504 - 7520 */ + 0x01, 0xb0, 0x01, 0x4e, 0x01, 0x5e, 0x01, 0x51, 0x01, 0x4e, 0x01, 0x29, 0x01, 0x49, 0x01, 0x21, /* bytes 7520 - 7536 */ + 0x01, 0x47, 0x01, 0xf4, 0x00, 0x6a, 0x01, 0xcb, 0x00, 0x33, 0x01, 0xce, 0x00, 0x1c, 0x01, 0xc8, /* bytes 7536 - 7552 */ + 0x00, 0x13, 0x01, 0x8f, 0x00, 0xc5, 0x00, 0xc8, 0x00, 0xf2, 0xff, 0xb1, 0x00, 0xd9, 0xff, 0xb6, /* bytes 7552 - 7568 */ + 0x00, 0xcd, 0xff, 0xb3, 0x00, 0xb9, 0xff, 0xd5, 0x00, 0x9b, 0xff, 0xfd, 0x00, 0x9e, 0xff, 0x03, /* bytes 7568 - 7584 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x16, 0x01, 0xd3, 0xff, 0xb6, 0x00, 0xcd, 0xff, /* bytes 7584 - 7600 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x51, 0x01, 0x4e, 0x01, 0xbb, 0x01, 0x58, /* bytes 7600 - 7616 */ + 0x01, 0x9d, 0x01, 0x36, 0x01, 0xa1, 0x01, 0xef, 0x00, 0xe5, 0x01, 0xba, 0x00, 0x12, 0x02, 0xed, /* bytes 7616 - 7632 */ + 0x00, 0x0b, 0x02, 0x5d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb6, 0x01, /* bytes 7632 - 7648 */ + 0xb0, 0x01, 0xb9, 0x01, 0x14, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x09, 0x02, /* bytes 7648 - 7664 */ + 0x1c, 0x02, 0x07, 0x02, 0xad, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd4, /* bytes 7664 - 7680 */ + 0x00, 0xbb, 0x00, 0xff, 0x00, 0xde, 0x00, 0xf7, 0x00, 0x19, 0x01, 0xc8, 0x00, 0x13, 0x01, 0x03, /* bytes 7680 - 7696 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x49, 0x02, 0x20, 0x01, 0x57, 0x02, 0xc1, 0x00, /* bytes 7696 - 7712 */ + 0x25, 0x02, 0x92, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x28, 0x01, 0x9f, /* bytes 7712 - 7728 */ + 0x00, 0x3d, 0x01, 0xba, 0x00, 0x29, 0x01, 0x49, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7728 - 7744 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 7744 - 7760 */ + 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 7760 - 7776 */ + 0x01, 0x00, 0x02, 0x00, 0x4e, 0x01, 0x5e, 0x01, 0xb5, 0x01, 0x6c, 0x01, 0x23, 0x00, 0x0a, 0x00, /* bytes 7776 - 7792 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xdd, 0x00, 0x12, 0x01, 0xd7, 0x00, 0x09, /* bytes 7792 - 7808 */ + 0x01, 0xa4, 0x00, 0xb4, 0x00, 0xd8, 0x00, 0xed, 0xff, 0xc3, 0x00, 0xd3, 0xff, 0xc8, 0x00, 0xc7, /* bytes 7808 - 7824 */ + 0xff, 0xc5, 0x00, 0xb3, 0xff, 0xe8, 0x00, 0x96, 0xff, 0x10, 0x01, 0x9a, 0xff, 0x2d, 0x01, 0xbd, /* bytes 7824 - 7840 */ + 0xff, 0x27, 0x01, 0xd0, 0xff, 0x2a, 0x01, 0xdd, 0xff, 0x10, 0x01, 0xf2, 0xff, 0xeb, 0x00, 0xb3, /* bytes 7840 - 7856 */ + 0x00, 0xec, 0x00, 0xab, 0x00, 0x2f, 0x01, 0x81, 0x00, 0x41, 0x01, 0x9d, 0x00, 0x78, 0x01, 0x60, /* bytes 7856 - 7872 */ + 0x00, 0x24, 0x02, 0x86, 0x00, 0x3e, 0x02, 0xa5, 0x00, 0x64, 0x02, 0x8c, 0x00, 0x93, 0x02, 0x97, /* bytes 7872 - 7888 */ + 0x00, 0xb1, 0x02, 0xc7, 0x00, 0xa3, 0x02, 0x05, 0x01, 0x57, 0x02, 0x36, 0x01, 0x52, 0x02, 0x4d, /* bytes 7888 - 7904 */ + 0x01, 0x13, 0x02, 0x61, 0x01, 0x09, 0x02, 0xb0, 0x01, 0xc4, 0x01, 0x57, 0x01, 0xbd, 0x01, 0x75, /* bytes 7904 - 7920 */ + 0x01, 0xb9, 0x01, 0xae, 0x01, 0x58, 0x01, 0x5f, 0x01, 0x5b, 0x01, 0x4f, 0x01, 0x34, 0x01, 0x46, /* bytes 7920 - 7936 */ + 0x01, 0x2c, 0x01, 0x45, 0x01, 0xfc, 0x00, 0x63, 0x01, 0xd8, 0x00, 0x2a, 0x01, 0xdd, 0x00, 0x12, /* bytes 7936 - 7952 */ + 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x27, 0x01, 0xd0, 0xff, 0xc8, 0x00, /* bytes 7952 - 7968 */ + 0xc7, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x13, 0x02, 0x61, 0x01, 0x22, /* bytes 7968 - 7984 */ + 0x02, 0xf2, 0x00, 0xf8, 0x01, 0xbc, 0x00, 0xb2, 0x01, 0xec, 0x00, 0xa8, 0x01, 0x33, 0x01, 0xc4, /* bytes 7984 - 8000 */ + 0x01, 0x57, 0x01, 0x5b, 0x01, 0x4f, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 8000 - 8016 */ + 0xb9, 0x01, 0xae, 0x01, 0xbb, 0x01, 0x0f, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 8016 - 8032 */ + 0x0b, 0x02, 0x17, 0x02, 0x09, 0x02, 0xb0, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 8032 - 8048 */ + 0x00, 0xeb, 0x00, 0xb3, 0x00, 0x13, 0x01, 0xd9, 0x00, 0x06, 0x01, 0x13, 0x01, 0xd7, 0x00, 0x09, /* bytes 8048 - 8064 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x41, 0x01, 0x9d, 0x00, 0x54, 0x01, /* bytes 8064 - 8080 */ + 0xba, 0x00, 0x34, 0x01, 0x46, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3e, /* bytes 8080 - 8096 */ + 0x02, 0xa5, 0x00, 0x6c, 0x02, 0xd8, 0x00, 0x57, 0x02, 0x36, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8096 - 8112 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 8112 - 8128 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 8128 - 8144 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbd, 0x01, 0x75, 0x01, 0x58, 0x01, 0x5f, 0x01, 0x23, 0x00, /* bytes 8144 - 8160 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xcf, 0x00, 0xb2, 0xff, 0xf3, /* bytes 8160 - 8176 */ + 0x00, 0x95, 0xff, 0x1a, 0x01, 0x9a, 0xff, 0x37, 0x01, 0xbd, 0xff, 0x30, 0x01, 0xd1, 0xff, 0x33, /* bytes 8176 - 8192 */ + 0x01, 0xdd, 0xff, 0x19, 0x01, 0xf2, 0xff, 0xf9, 0x00, 0xac, 0x00, 0xfb, 0x00, 0xa4, 0x00, 0x41, /* bytes 8192 - 8208 */ + 0x01, 0x7f, 0x00, 0x51, 0x01, 0x9d, 0x00, 0x8c, 0x01, 0x63, 0x00, 0x35, 0x02, 0x96, 0x00, 0x4d, /* bytes 8208 - 8224 */ + 0x02, 0xb6, 0x00, 0x74, 0x02, 0xa0, 0x00, 0xa2, 0x02, 0xae, 0x00, 0xbd, 0x02, 0xe0, 0x00, 0xab, /* bytes 8224 - 8240 */ + 0x02, 0x1d, 0x01, 0x5b, 0x02, 0x48, 0x01, 0x54, 0x02, 0x5f, 0x01, 0x15, 0x02, 0x6a, 0x01, 0x06, /* bytes 8240 - 8256 */ + 0x02, 0xb8, 0x01, 0xc7, 0x01, 0x5b, 0x01, 0xbd, 0x01, 0x7d, 0x01, 0xb6, 0x01, 0xb1, 0x01, 0x5a, /* bytes 8256 - 8272 */ + 0x01, 0x5f, 0x01, 0x5e, 0x01, 0x50, 0x01, 0x38, 0x01, 0x44, 0x01, 0x30, 0x01, 0x42, 0x01, 0xff, /* bytes 8272 - 8288 */ + 0x00, 0x5d, 0x01, 0xde, 0x00, 0x21, 0x01, 0xe5, 0x00, 0x0a, 0x01, 0xe0, 0x00, 0x00, 0x01, 0xb2, /* bytes 8288 - 8304 */ + 0x00, 0xa6, 0x00, 0xe1, 0x00, 0xec, 0xff, 0xcc, 0x00, 0xd1, 0xff, 0xd1, 0x00, 0xc6, 0xff, 0xcf, /* bytes 8304 - 8320 */ + 0x00, 0xb2, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd1, 0x00, 0xc6, 0xff, /* bytes 8320 - 8336 */ + 0x30, 0x01, 0xd1, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x15, 0x02, 0x6a, /* bytes 8336 - 8352 */ + 0x01, 0x2b, 0x02, 0xfc, 0x00, 0x05, 0x02, 0xc4, 0x00, 0xbb, 0x01, 0xef, 0x00, 0xad, 0x01, 0x35, /* bytes 8352 - 8368 */ + 0x01, 0xc7, 0x01, 0x5b, 0x01, 0x5e, 0x01, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 8368 - 8384 */ + 0x06, 0x00, 0xb6, 0x01, 0xb1, 0x01, 0xbc, 0x01, 0x0d, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 8384 - 8400 */ + 0x5c, 0x02, 0x0c, 0x02, 0x15, 0x02, 0x06, 0x02, 0xb8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8400 - 8416 */ + 0x00, 0x04, 0x00, 0xe0, 0x00, 0x00, 0x01, 0x0e, 0x01, 0x0e, 0x01, 0x1f, 0x01, 0xd5, 0x00, 0xf9, /* bytes 8416 - 8432 */ + 0x00, 0xac, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x38, 0x01, 0x44, 0x01, /* bytes 8432 - 8448 */ + 0x61, 0x01, 0xbb, 0x00, 0x51, 0x01, 0x9d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 8448 - 8464 */ + 0x00, 0x4d, 0x02, 0xb6, 0x00, 0x77, 0x02, 0xec, 0x00, 0x5b, 0x02, 0x48, 0x01, 0x03, 0x00, 0xc0, /* bytes 8464 - 8480 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 8480 - 8496 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, /* bytes 8496 - 8512 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbd, 0x01, 0x7d, 0x01, 0x5a, 0x01, 0x5f, 0x01, /* bytes 8512 - 8528 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x96, 0x01, 0x66, /* bytes 8528 - 8544 */ + 0x00, 0x3d, 0x02, 0x9f, 0x00, 0x53, 0x02, 0xc0, 0x00, 0x7c, 0x02, 0xac, 0x00, 0xa9, 0x02, 0xbc, /* bytes 8544 - 8560 */ + 0x00, 0xc2, 0x02, 0xef, 0x00, 0xad, 0x02, 0x2b, 0x01, 0x5c, 0x02, 0x53, 0x01, 0x54, 0x02, 0x6a, /* bytes 8560 - 8576 */ + 0x01, 0x15, 0x02, 0x70, 0x01, 0x03, 0x02, 0xbe, 0x01, 0xc7, 0x01, 0x5e, 0x01, 0xbc, 0x01, 0x81, /* bytes 8576 - 8592 */ + 0x01, 0xb3, 0x01, 0xb4, 0x01, 0x5a, 0x01, 0x5f, 0x01, 0x5f, 0x01, 0x50, 0x01, 0x39, 0x01, 0x43, /* bytes 8592 - 8608 */ + 0x01, 0x31, 0x01, 0x41, 0x01, 0xfe, 0x00, 0x5a, 0x01, 0xe1, 0x00, 0x1d, 0x01, 0xe9, 0x00, 0x06, /* bytes 8608 - 8624 */ + 0x01, 0xe4, 0x00, 0xfc, 0x00, 0xb9, 0x00, 0x9f, 0x00, 0xe1, 0x00, 0xee, 0xff, 0xcc, 0x00, 0xd3, /* bytes 8624 - 8640 */ + 0xff, 0xd2, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xb3, 0xff, 0xf4, 0x00, 0x97, 0xff, 0x1c, 0x01, 0x9c, /* bytes 8640 - 8656 */ + 0xff, 0x38, 0x01, 0xc0, 0xff, 0x31, 0x01, 0xd3, 0xff, 0x34, 0x01, 0xe0, 0xff, 0x19, 0x01, 0xf5, /* bytes 8656 - 8672 */ + 0xff, 0x00, 0x01, 0xa8, 0x00, 0x03, 0x01, 0xa1, 0x00, 0x4a, 0x01, 0x7e, 0x00, 0x59, 0x01, 0x9d, /* bytes 8672 - 8688 */ + 0x00, 0x96, 0x01, 0x66, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd2, 0x00, /* bytes 8688 - 8704 */ + 0xc8, 0xff, 0x31, 0x01, 0xd3, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x15, /* bytes 8704 - 8720 */ + 0x02, 0x70, 0x01, 0x2e, 0x02, 0x03, 0x01, 0x0a, 0x02, 0xca, 0x00, 0xbf, 0x01, 0xf2, 0x00, 0xaf, /* bytes 8720 - 8736 */ + 0x01, 0x38, 0x01, 0xc7, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 8736 - 8752 */ + 0x01, 0x00, 0x06, 0x00, 0xb3, 0x01, 0xb4, 0x01, 0xbc, 0x01, 0x0c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 8752 - 8768 */ + 0xb8, 0x01, 0x5c, 0x02, 0x0c, 0x02, 0x14, 0x02, 0x03, 0x02, 0xbe, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 8768 - 8784 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0xa8, 0x00, 0x24, 0x01, 0xd3, 0x00, 0x11, 0x01, 0x0b, /* bytes 8784 - 8800 */ + 0x01, 0xe4, 0x00, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x53, 0x02, /* bytes 8800 - 8816 */ + 0xc0, 0x00, 0x7b, 0x02, 0xf8, 0x00, 0x5c, 0x02, 0x53, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8816 - 8832 */ + 0x00, 0x03, 0x00, 0x39, 0x01, 0x43, 0x01, 0x68, 0x01, 0xbb, 0x00, 0x59, 0x01, 0x9d, 0x00, 0x03, /* bytes 8832 - 8848 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 8848 - 8864 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xbc, 0x01, 0x81, 0x01, 0x5a, 0x01, 0x5f, /* bytes 8864 - 8880 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 8880 - 8896 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xc0, /* bytes 8896 - 8912 */ + 0x01, 0xc4, 0x01, 0xa9, 0x01, 0x56, 0x01, 0xb2, 0x01, 0x7e, 0x01, 0x79, 0x01, 0x9f, 0x01, 0x4f, /* bytes 8912 - 8928 */ + 0x01, 0x5f, 0x01, 0x54, 0x01, 0x50, 0x01, 0x2d, 0x01, 0x44, 0x01, 0x26, 0x01, 0x42, 0x01, 0xf4, /* bytes 8928 - 8944 */ + 0x00, 0x5c, 0x01, 0xd4, 0x00, 0x20, 0x01, 0xdb, 0x00, 0x09, 0x01, 0xd6, 0x00, 0xff, 0x00, 0xb1, /* bytes 8944 - 8960 */ + 0x00, 0xb9, 0x00, 0xd5, 0x00, 0x0c, 0x00, 0xbd, 0x00, 0xf4, 0xff, 0xc1, 0x00, 0xe8, 0xff, 0xbd, /* bytes 8960 - 8976 */ + 0x00, 0xd4, 0xff, 0xdd, 0x00, 0xb4, 0xff, 0x05, 0x01, 0xb4, 0xff, 0x25, 0x01, 0xd4, 0xff, 0x21, /* bytes 8976 - 8992 */ + 0x01, 0xe8, 0xff, 0x25, 0x01, 0xf4, 0xff, 0x0d, 0x01, 0x0c, 0x00, 0xf0, 0x00, 0xab, 0x00, 0xf3, /* bytes 8992 - 9008 */ + 0x00, 0xa3, 0x00, 0x39, 0x01, 0x7f, 0x00, 0x49, 0x01, 0x9d, 0x00, 0x84, 0x01, 0x64, 0x00, 0x2c, /* bytes 9008 - 9024 */ + 0x02, 0x98, 0x00, 0x44, 0x02, 0xb9, 0x00, 0x6c, 0x02, 0xa4, 0x00, 0x9a, 0x02, 0xb2, 0x00, 0xb4, /* bytes 9024 - 9040 */ + 0x02, 0xe4, 0x00, 0xa1, 0x02, 0x21, 0x01, 0x51, 0x02, 0x4b, 0x01, 0x49, 0x02, 0x62, 0x01, 0xec, /* bytes 9040 - 9056 */ + 0x01, 0x82, 0x01, 0xc0, 0x01, 0xc4, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 9056 - 9072 */ + 0xc1, 0x00, 0xe8, 0xff, 0x21, 0x01, 0xe8, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 9072 - 9088 */ + 0x00, 0xec, 0x01, 0x82, 0x01, 0x2a, 0x02, 0x24, 0x01, 0x1c, 0x02, 0xe1, 0x00, 0xc8, 0x01, 0xed, /* bytes 9088 - 9104 */ + 0x00, 0xa0, 0x01, 0x29, 0x01, 0xa9, 0x01, 0x56, 0x01, 0x54, 0x01, 0x50, 0x01, 0x03, 0x00, 0xc0, /* bytes 9104 - 9120 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xc0, 0x01, 0xc4, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, /* bytes 9120 - 9136 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x18, 0x02, 0x79, 0x01, 0x9f, 0x01, 0x03, 0x00, /* bytes 9136 - 9152 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd6, 0x00, 0xff, 0x00, 0x04, 0x01, 0x0d, 0x01, 0x16, /* bytes 9152 - 9168 */ + 0x01, 0xd4, 0x00, 0xf0, 0x00, 0xab, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 9168 - 9184 */ + 0x44, 0x02, 0xb9, 0x00, 0x6d, 0x02, 0xf0, 0x00, 0x51, 0x02, 0x4b, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 9184 - 9200 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x2d, 0x01, 0x44, 0x01, 0x58, 0x01, 0xbb, 0x00, 0x49, 0x01, 0x9d, /* bytes 9200 - 9216 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 9216 - 9232 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, /* bytes 9232 - 9248 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb2, 0x01, 0x7e, 0x01, /* bytes 9248 - 9264 */ + 0x4f, 0x01, 0x5f, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 9264 - 9280 */ + 0x00, 0x8f, 0x01, 0x0c, 0x01, 0x6d, 0x01, 0x0c, 0x01, 0x51, 0x01, 0x06, 0x01, 0x48, 0x01, 0x01, /* bytes 9280 - 9296 */ + 0x01, 0x47, 0x01, 0x00, 0x01, 0x3c, 0x01, 0xf3, 0x00, 0x32, 0x01, 0xf9, 0x00, 0x2a, 0x01, 0xfd, /* bytes 9296 - 9312 */ + 0x00, 0x20, 0x01, 0x02, 0x01, 0xe6, 0x00, 0x36, 0x01, 0xc2, 0x00, 0x2f, 0x01, 0xa1, 0x00, 0xc6, /* bytes 9312 - 9328 */ + 0x00, 0x60, 0x00, 0xcf, 0x00, 0xa7, 0x00, 0x78, 0x00, 0xe4, 0x00, 0xee, 0x00, 0x23, 0x01, 0xaa, /* bytes 9328 - 9344 */ + 0x00, 0x07, 0x01, 0x90, 0x00, 0x1b, 0x01, 0x43, 0x00, 0x3c, 0x01, 0x4c, 0x00, 0xc3, 0x01, 0xfc, /* bytes 9344 - 9360 */ + 0xff, 0x6d, 0x02, 0x2a, 0x00, 0x85, 0x02, 0x47, 0x00, 0xae, 0x02, 0x30, 0x00, 0xdc, 0x02, 0x3c, /* bytes 9360 - 9376 */ + 0x00, 0xf8, 0x02, 0x6d, 0x00, 0xe8, 0x02, 0xab, 0x00, 0x9a, 0x02, 0xd8, 0x00, 0x93, 0x02, 0xf2, /* bytes 9376 - 9392 */ + 0x00, 0x43, 0x02, 0x1e, 0x01, 0x24, 0x02, 0x68, 0x01, 0xf9, 0x01, 0xff, 0x00, 0xfd, 0x01, 0x15, /* bytes 9392 - 9408 */ + 0x01, 0xd7, 0x01, 0x50, 0x01, 0x98, 0x01, 0xfa, 0x00, 0x9c, 0x01, 0xea, 0x00, 0xa5, 0x01, 0xd1, /* bytes 9408 - 9424 */ + 0x00, 0x9e, 0x01, 0xd5, 0x00, 0x8f, 0x01, 0x0c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 9424 - 9440 */ + 0x02, 0x00, 0x60, 0x00, 0xcf, 0x00, 0x2a, 0x01, 0xfd, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 9440 - 9456 */ + 0x00, 0x07, 0x00, 0x43, 0x02, 0x1e, 0x01, 0x6f, 0x02, 0xb7, 0x00, 0x55, 0x02, 0x78, 0x00, 0x05, /* bytes 9456 - 9472 */ + 0x02, 0x93, 0x00, 0xe8, 0x01, 0xd5, 0x00, 0xf9, 0x01, 0xff, 0x00, 0x9c, 0x01, 0xea, 0x00, 0x03, /* bytes 9472 - 9488 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xd7, 0x01, 0x50, 0x01, 0xb4, 0x01, 0x24, 0x02, /* bytes 9488 - 9504 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x04, 0x02, 0x24, 0x02, 0x68, 0x01, /* bytes 9504 - 9520 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x48, 0x01, 0x01, 0x01, 0x62, 0x01, 0xc3, /* bytes 9520 - 9536 */ + 0x00, 0x40, 0x01, 0x91, 0x00, 0x23, 0x01, 0xaa, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 9536 - 9552 */ + 0x03, 0x00, 0xa5, 0x01, 0xd1, 0x00, 0x5d, 0x01, 0x55, 0x00, 0x3c, 0x01, 0x4c, 0x00, 0x03, 0x00, /* bytes 9552 - 9568 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x85, 0x02, 0x47, 0x00, 0xb2, 0x02, 0x7e, 0x00, 0x9a, /* bytes 9568 - 9584 */ + 0x02, 0xd8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 9584 - 9600 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfd, 0x01, 0x15, /* bytes 9600 - 9616 */ + 0x01, 0x98, 0x01, 0xfa, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 9616 - 9632 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 9632 - 9648 */ + 0x00, 0x23, 0x00, 0x52, 0x01, 0xf1, 0x00, 0x5a, 0x01, 0xe3, 0x00, 0x5a, 0x01, 0xe3, 0x00, 0xea, /* bytes 9648 - 9664 */ + 0x00, 0x3f, 0x01, 0xc6, 0x00, 0x37, 0x01, 0xa5, 0x00, 0xcf, 0x00, 0x63, 0x00, 0xd8, 0x00, 0xab, /* bytes 9664 - 9680 */ + 0x00, 0x81, 0x00, 0xe8, 0x00, 0xf6, 0x00, 0x23, 0x01, 0xb0, 0x00, 0x03, 0x01, 0x98, 0x00, 0x13, /* bytes 9680 - 9696 */ + 0x01, 0x4a, 0x00, 0x37, 0x01, 0x4f, 0x00, 0xcf, 0x01, 0xe3, 0xff, 0x89, 0x02, 0x08, 0x00, 0xa0, /* bytes 9696 - 9712 */ + 0x02, 0x21, 0x00, 0xcf, 0x02, 0x05, 0x00, 0x01, 0x03, 0x0f, 0x00, 0x20, 0x03, 0x40, 0x00, 0x0f, /* bytes 9712 - 9728 */ + 0x03, 0x7e, 0x00, 0xbf, 0x02, 0xaf, 0x00, 0xb4, 0x02, 0xd0, 0x00, 0x80, 0x02, 0xd8, 0x00, 0x6b, /* bytes 9728 - 9744 */ + 0x02, 0x26, 0x01, 0x32, 0x02, 0xc7, 0x00, 0x13, 0x02, 0xf9, 0x00, 0x1a, 0x02, 0x1e, 0x01, 0xad, /* bytes 9744 - 9760 */ + 0x01, 0xe3, 0x00, 0xb1, 0x01, 0xd3, 0x00, 0xb2, 0x01, 0xbe, 0x00, 0x9f, 0x01, 0x07, 0x01, 0x7b, /* bytes 9760 - 9776 */ + 0x01, 0x0a, 0x01, 0x5c, 0x01, 0x07, 0x01, 0x51, 0x01, 0x02, 0x01, 0x52, 0x01, 0xf1, 0x00, 0x03, /* bytes 9776 - 9792 */ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x63, 0x00, 0xd8, 0x00, 0x5a, 0x01, 0xe3, 0x00, /* bytes 9792 - 9808 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xb1, 0x01, 0xd3, 0x00, 0x32, 0x02, 0xc7, /* bytes 9808 - 9824 */ + 0x00, 0x1b, 0x02, 0xa2, 0x00, 0x2e, 0x02, 0x5b, 0x00, 0x7b, 0x02, 0x31, 0x00, 0x9d, 0x02, 0x6a, /* bytes 9824 - 9840 */ + 0x00, 0x80, 0x02, 0xd8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x6b, 0x02, /* bytes 9840 - 9856 */ + 0x26, 0x01, 0x04, 0x02, 0x00, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 9856 - 9872 */ + 0xfc, 0x01, 0x1a, 0x02, 0x1e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x23, /* bytes 9872 - 9888 */ + 0x01, 0xb0, 0x00, 0x40, 0x01, 0x94, 0x00, 0x69, 0x01, 0xc2, 0x00, 0x51, 0x01, 0x02, 0x01, 0x03, /* bytes 9888 - 9904 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xbf, 0x02, 0xaf, 0x00, 0xd4, 0x02, 0x5a, 0x00, /* bytes 9904 - 9920 */ + 0xa0, 0x02, 0x21, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb2, 0x01, 0xbe, /* bytes 9920 - 9936 */ + 0x00, 0x5b, 0x01, 0x55, 0x00, 0x37, 0x01, 0x4f, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 9936 - 9952 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 9952 - 9968 */ + 0x00, 0x02, 0x00, 0x13, 0x02, 0xf9, 0x00, 0xad, 0x01, 0xe3, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9968 - 9984 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 9984 - 10000 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x22, 0x00, 0x22, 0x01, 0xaf, 0x00, 0xf5, 0x00, 0x07, /* bytes 10000 - 10016 */ + 0x01, 0xb9, 0x00, 0x91, 0x00, 0x71, 0x00, 0xe8, 0x00, 0xb2, 0x00, 0xdf, 0x00, 0xd4, 0x00, 0x48, /* bytes 10016 - 10032 */ + 0x01, 0xf8, 0x00, 0x4f, 0x01, 0x56, 0x01, 0xf3, 0x00, 0x56, 0x01, 0xf8, 0x00, 0x58, 0x01, 0xfe, /* bytes 10032 - 10048 */ + 0x00, 0x64, 0x01, 0x02, 0x01, 0x84, 0x01, 0x03, 0x01, 0xaa, 0x01, 0xfd, 0x00, 0xb8, 0x01, 0xb2, /* bytes 10048 - 10064 */ + 0x00, 0xb9, 0x01, 0xc9, 0x00, 0xb5, 0x01, 0xd9, 0x00, 0x20, 0x02, 0x13, 0x01, 0x17, 0x02, 0xed, /* bytes 10064 - 10080 */ + 0x00, 0x37, 0x02, 0xbc, 0x00, 0x73, 0x02, 0x19, 0x01, 0x89, 0x02, 0xca, 0x00, 0xbf, 0x02, 0xc0, /* bytes 10080 - 10096 */ + 0x00, 0xce, 0x02, 0x95, 0x00, 0x1e, 0x03, 0x64, 0x00, 0x2f, 0x03, 0x24, 0x00, 0x0f, 0x03, 0xf5, /* bytes 10096 - 10112 */ + 0xff, 0xdc, 0x02, 0xec, 0xff, 0xa5, 0x02, 0x0c, 0x00, 0x90, 0x02, 0xf8, 0xff, 0xcc, 0x01, 0xda, /* bytes 10112 - 10128 */ + 0xff, 0x31, 0x01, 0x4d, 0x00, 0x0a, 0x01, 0x4b, 0x00, 0xff, 0x00, 0x99, 0x00, 0x22, 0x01, 0xaf, /* bytes 10128 - 10144 */ + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x56, 0x01, 0xf3, 0x00, 0x71, 0x00, /* bytes 10144 - 10160 */ + 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xb9, 0x01, 0xc9, 0x00, 0x37, /* bytes 10160 - 10176 */ + 0x02, 0xbc, 0x00, 0x1f, 0x02, 0x97, 0x00, 0x32, 0x02, 0x50, 0x00, 0x82, 0x02, 0x24, 0x00, 0xa6, /* bytes 10176 - 10192 */ + 0x02, 0x5c, 0x00, 0x89, 0x02, 0xca, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 10192 - 10208 */ + 0x73, 0x02, 0x19, 0x01, 0x04, 0x02, 0x00, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 10208 - 10224 */ + 0xb4, 0x01, 0xf8, 0x01, 0x20, 0x02, 0x13, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 10224 - 10240 */ + 0x00, 0x58, 0x01, 0xfe, 0x00, 0x6d, 0x01, 0xbc, 0x00, 0x3e, 0x01, 0x91, 0x00, 0x22, 0x01, 0xaf, /* bytes 10240 - 10256 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xce, 0x02, 0x95, 0x00, 0xdf, 0x02, /* bytes 10256 - 10272 */ + 0x4a, 0x00, 0xa5, 0x02, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb8, /* bytes 10272 - 10288 */ + 0x01, 0xb2, 0x00, 0x56, 0x01, 0x50, 0x00, 0x31, 0x01, 0x4d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 10288 - 10304 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 10304 - 10320 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0xd9, 0x00, 0x17, 0x02, 0xed, 0x00, 0x03, 0x00, 0xc0, /* bytes 10320 - 10336 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 10336 - 10352 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x22, 0x00, 0x83, 0x02, 0xbe, 0x00, 0x6f, /* bytes 10352 - 10368 */ + 0x02, 0x0d, 0x01, 0x31, 0x02, 0xb0, 0x00, 0x12, 0x02, 0xdd, 0x00, 0x1b, 0x02, 0x07, 0x01, 0xb6, /* bytes 10368 - 10384 */ + 0x01, 0xca, 0x00, 0xba, 0x01, 0xba, 0x00, 0xbb, 0x01, 0x9b, 0x00, 0xb1, 0x01, 0xe6, 0x00, 0x8c, /* bytes 10384 - 10400 */ + 0x01, 0xee, 0x00, 0x6c, 0x01, 0xef, 0x00, 0x60, 0x01, 0xec, 0x00, 0x5f, 0x01, 0xea, 0x00, 0x5c, /* bytes 10400 - 10416 */ + 0x01, 0xe8, 0x00, 0x10, 0x01, 0x57, 0x01, 0xe4, 0x00, 0x50, 0x01, 0xc2, 0x00, 0xe7, 0x00, 0x81, /* bytes 10416 - 10432 */ + 0x00, 0xf0, 0x00, 0xc9, 0x00, 0x99, 0x00, 0x05, 0x01, 0x0f, 0x01, 0x24, 0x01, 0xa1, 0x00, 0xff, /* bytes 10432 - 10448 */ + 0x00, 0x8b, 0x00, 0x05, 0x01, 0x3e, 0x00, 0x2d, 0x01, 0x3e, 0x00, 0xc0, 0x01, 0xcc, 0xff, 0x89, /* bytes 10448 - 10464 */ + 0x02, 0xe5, 0xff, 0x99, 0x02, 0xf5, 0xff, 0xd5, 0x02, 0xd0, 0xff, 0x08, 0x03, 0xd9, 0xff, 0x28, /* bytes 10464 - 10480 */ + 0x03, 0x08, 0x00, 0x18, 0x03, 0x48, 0x00, 0xcc, 0x02, 0x79, 0x00, 0xb9, 0x02, 0xaf, 0x00, 0x83, /* bytes 10480 - 10496 */ + 0x02, 0xbe, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5c, 0x01, 0xe8, 0x00, /* bytes 10496 - 10512 */ + 0x81, 0x00, 0xf0, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x83, 0x02, 0xbe, /* bytes 10512 - 10528 */ + 0x00, 0xa0, 0x02, 0x4f, 0x00, 0x7b, 0x02, 0x18, 0x00, 0x2b, 0x02, 0x44, 0x00, 0x19, 0x02, 0x8b, /* bytes 10528 - 10544 */ + 0x00, 0x31, 0x02, 0xb0, 0x00, 0xba, 0x01, 0xba, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 10544 - 10560 */ + 0x06, 0x00, 0x1b, 0x02, 0x07, 0x01, 0xb4, 0x01, 0xf8, 0x01, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 10560 - 10576 */ + 0x5c, 0x02, 0x04, 0x02, 0x02, 0x02, 0x6f, 0x02, 0x0d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10576 - 10592 */ + 0x00, 0x04, 0x00, 0x60, 0x01, 0xec, 0x00, 0x70, 0x01, 0xaa, 0x00, 0x3e, 0x01, 0x81, 0x00, 0x24, /* bytes 10592 - 10608 */ + 0x01, 0xa1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x99, 0x02, 0xf5, 0xff, /* bytes 10608 - 10624 */ + 0xd8, 0x02, 0x38, 0x00, 0xcc, 0x02, 0x79, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 10624 - 10640 */ + 0x00, 0x2d, 0x01, 0x3e, 0x00, 0x51, 0x01, 0x3f, 0x00, 0xbb, 0x01, 0x9b, 0x00, 0x03, 0x00, 0xc0, /* bytes 10640 - 10656 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 10656 - 10672 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, /* bytes 10672 - 10688 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x12, 0x02, 0xdd, 0x00, 0xb6, 0x01, 0xca, 0x00, /* bytes 10688 - 10704 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x22, 0x00, 0x72, 0x01, 0xf9, /* bytes 10704 - 10720 */ + 0x00, 0x66, 0x01, 0xf7, 0x00, 0x63, 0x01, 0xff, 0x00, 0x69, 0x01, 0xff, 0x00, 0x2a, 0x01, 0x5f, /* bytes 10720 - 10736 */ + 0x01, 0xf6, 0x00, 0x58, 0x01, 0xd5, 0x00, 0xef, 0x00, 0x94, 0x00, 0xf8, 0x00, 0xdc, 0x00, 0xa1, /* bytes 10736 - 10752 */ + 0x00, 0x18, 0x01, 0x17, 0x01, 0x25, 0x01, 0xb0, 0x00, 0xff, 0x00, 0x9a, 0x00, 0xff, 0x00, 0x4f, /* bytes 10752 - 10768 */ + 0x00, 0x29, 0x01, 0x4c, 0x00, 0xaf, 0x01, 0xdd, 0xff, 0x80, 0x02, 0xee, 0xff, 0x8c, 0x02, 0xfb, /* bytes 10768 - 10784 */ + 0xff, 0xcd, 0x02, 0xd2, 0xff, 0xff, 0x02, 0xda, 0xff, 0x20, 0x03, 0x0a, 0x00, 0x10, 0x03, 0x49, /* bytes 10784 - 10800 */ + 0x00, 0xc7, 0x02, 0x7a, 0x00, 0xb2, 0x02, 0xba, 0x00, 0x7e, 0x02, 0xd7, 0x00, 0x6a, 0x02, 0x26, /* bytes 10800 - 10816 */ + 0x01, 0x2c, 0x02, 0xca, 0x00, 0x0b, 0x02, 0xea, 0x00, 0x16, 0x02, 0x21, 0x01, 0xb7, 0x01, 0xd7, /* bytes 10816 - 10832 */ + 0x00, 0xba, 0x01, 0xc8, 0x00, 0xbb, 0x01, 0xa2, 0x00, 0xb7, 0x01, 0xec, 0x00, 0x92, 0x01, 0xf7, /* bytes 10832 - 10848 */ + 0x00, 0x72, 0x01, 0xf9, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x69, 0x01, /* bytes 10848 - 10864 */ + 0xff, 0x00, 0x94, 0x00, 0xf8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x7e, /* bytes 10864 - 10880 */ + 0x02, 0xd7, 0x00, 0x99, 0x02, 0x68, 0x00, 0x74, 0x02, 0x30, 0x00, 0x24, 0x02, 0x5e, 0x00, 0x13, /* bytes 10880 - 10896 */ + 0x02, 0xa5, 0x00, 0x2c, 0x02, 0xca, 0x00, 0xba, 0x01, 0xc8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 10896 - 10912 */ + 0x01, 0x00, 0x06, 0x00, 0x16, 0x02, 0x21, 0x01, 0xb4, 0x01, 0xfa, 0x01, 0x58, 0x01, 0x5c, 0x02, /* bytes 10912 - 10928 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x03, 0x02, 0x6a, 0x02, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 10928 - 10944 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x25, 0x01, 0xb0, 0x00, 0x3d, 0x01, 0x8f, 0x00, 0x71, 0x01, 0xb4, /* bytes 10944 - 10960 */ + 0x00, 0x66, 0x01, 0xf7, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x29, 0x01, /* bytes 10960 - 10976 */ + 0x4c, 0x00, 0x4b, 0x01, 0x4c, 0x00, 0xbb, 0x01, 0xa2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10976 - 10992 */ + 0x00, 0x03, 0x00, 0x8c, 0x02, 0xfb, 0xff, 0xcf, 0x02, 0x43, 0x00, 0xc7, 0x02, 0x7a, 0x00, 0x03, /* bytes 10992 - 11008 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 11008 - 11024 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, /* bytes 11024 - 11040 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0b, 0x02, 0xea, 0x00, 0xb7, 0x01, /* bytes 11040 - 11056 */ + 0xd7, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x22, 0x00, 0x28, /* bytes 11056 - 11072 */ + 0x02, 0xe0, 0x00, 0x06, 0x02, 0xfb, 0x00, 0x13, 0x02, 0x37, 0x01, 0xb9, 0x01, 0xe9, 0x00, 0xbd, /* bytes 11072 - 11088 */ + 0x01, 0xda, 0x00, 0xbc, 0x01, 0xb0, 0x00, 0xbd, 0x01, 0xfb, 0x00, 0x99, 0x01, 0x07, 0x01, 0x79, /* bytes 11088 - 11104 */ + 0x01, 0x0c, 0x01, 0x6d, 0x01, 0x0a, 0x01, 0x74, 0x01, 0x09, 0x01, 0x64, 0x01, 0x1a, 0x01, 0x30, /* bytes 11104 - 11120 */ + 0x01, 0x61, 0x01, 0xfe, 0x00, 0x5a, 0x01, 0xdd, 0x00, 0xf1, 0x00, 0x9c, 0x00, 0xfa, 0x00, 0xe4, /* bytes 11120 - 11136 */ + 0x00, 0xa3, 0x00, 0x20, 0x01, 0x19, 0x01, 0x27, 0x01, 0xc7, 0x00, 0xfe, 0x00, 0xb0, 0x00, 0xf9, /* bytes 11136 - 11152 */ + 0x00, 0x69, 0x00, 0x26, 0x01, 0x61, 0x00, 0x99, 0x01, 0xf6, 0xff, 0x7a, 0x02, 0xf7, 0xff, 0x82, /* bytes 11152 - 11168 */ + 0x02, 0x07, 0x00, 0xc6, 0x02, 0xdb, 0xff, 0xf8, 0x02, 0xe3, 0xff, 0x19, 0x03, 0x12, 0x00, 0x0a, /* bytes 11168 - 11184 */ + 0x03, 0x51, 0x00, 0xc4, 0x02, 0x83, 0x00, 0xad, 0x02, 0xc9, 0x00, 0x7a, 0x02, 0xec, 0x00, 0x67, /* bytes 11184 - 11200 */ + 0x02, 0x3b, 0x01, 0x28, 0x02, 0xe0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 11200 - 11216 */ + 0x64, 0x01, 0x1a, 0x01, 0x9c, 0x00, 0xfa, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 11216 - 11232 */ + 0x00, 0xbd, 0x01, 0xda, 0x00, 0x28, 0x02, 0xe0, 0x00, 0x0e, 0x02, 0xbb, 0x00, 0x1f, 0x02, 0x74, /* bytes 11232 - 11248 */ + 0x00, 0x6e, 0x02, 0x46, 0x00, 0x94, 0x02, 0x7c, 0x00, 0x7a, 0x02, 0xec, 0x00, 0x03, 0x00, 0xc0, /* bytes 11248 - 11264 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x13, 0x02, 0x37, 0x01, 0xb4, 0x01, 0xfb, 0x01, 0x58, 0x01, /* bytes 11264 - 11280 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x04, 0x02, 0x67, 0x02, 0x3b, 0x01, 0x03, 0x00, /* bytes 11280 - 11296 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x27, 0x01, 0xc7, 0x00, 0x3c, 0x01, 0xa5, 0x00, 0x73, /* bytes 11296 - 11312 */ + 0x01, 0xc7, 0x00, 0x6d, 0x01, 0x0a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 11312 - 11328 */ + 0xc4, 0x02, 0x83, 0x00, 0xc9, 0x02, 0x52, 0x00, 0x82, 0x02, 0x07, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 11328 - 11344 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0xbc, 0x01, 0xb0, 0x00, 0x45, 0x01, 0x62, 0x00, 0x26, 0x01, 0x61, /* bytes 11344 - 11360 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 11360 - 11376 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, /* bytes 11376 - 11392 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x06, 0x02, 0xfb, 0x00, /* bytes 11392 - 11408 */ + 0xb9, 0x01, 0xe9, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 11408 - 11424 */ + 0x00, 0x07, 0x03, 0x51, 0x00, 0xc3, 0x02, 0x83, 0x00, 0xac, 0x02, 0xcc, 0x00, 0x79, 0x02, 0xef, /* bytes 11424 - 11440 */ + 0x00, 0x68, 0x02, 0x3f, 0x01, 0x27, 0x02, 0xe4, 0x00, 0x06, 0x02, 0x00, 0x01, 0x14, 0x02, 0x3c, /* bytes 11440 - 11456 */ + 0x01, 0x93, 0x01, 0xf1, 0x00, 0x96, 0x01, 0xe1, 0x00, 0x9e, 0x01, 0xbb, 0x00, 0x91, 0x01, 0x01, /* bytes 11456 - 11472 */ + 0x01, 0x67, 0x01, 0xfc, 0x00, 0x47, 0x01, 0xf8, 0x00, 0x3b, 0x01, 0xf6, 0x00, 0x37, 0x01, 0x0b, /* bytes 11472 - 11488 */ + 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0x14, 0x01, 0xb9, 0x00, 0xeb, 0x00, 0x89, /* bytes 11488 - 11504 */ + 0x00, 0xf8, 0x00, 0x4a, 0x00, 0x2d, 0x01, 0x49, 0x00, 0x7e, 0x01, 0x09, 0x00, 0x77, 0x02, 0xf1, /* bytes 11504 - 11520 */ + 0xff, 0x7c, 0x02, 0x09, 0x00, 0xc1, 0x02, 0xdb, 0xff, 0xf3, 0x02, 0xe2, 0xff, 0x15, 0x03, 0x11, /* bytes 11520 - 11536 */ + 0x00, 0x07, 0x03, 0x51, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 11536 - 11552 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x96, /* bytes 11552 - 11568 */ + 0x01, 0xe1, 0x00, 0x27, 0x02, 0xe4, 0x00, 0x0d, 0x02, 0xc0, 0x00, 0x1c, 0x02, 0x79, 0x00, 0x6a, /* bytes 11568 - 11584 */ + 0x02, 0x4a, 0x00, 0x91, 0x02, 0x80, 0x00, 0x79, 0x02, 0xef, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 11584 - 11600 */ + 0x01, 0x00, 0x06, 0x00, 0x14, 0x02, 0x3c, 0x01, 0xb4, 0x01, 0xfc, 0x01, 0x58, 0x01, 0x5c, 0x02, /* bytes 11600 - 11616 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x04, 0x02, 0x68, 0x02, 0x3f, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 11616 - 11632 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x3b, 0x01, 0xf6, 0x00, 0x52, 0x01, 0xc1, 0x00, 0x27, 0x01, 0x93, /* bytes 11632 - 11648 */ + 0x00, 0x14, 0x01, 0xb9, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x7c, 0x02, /* bytes 11648 - 11664 */ + 0x09, 0x00, 0xc5, 0x02, 0x55, 0x00, 0xc3, 0x02, 0x83, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 11664 - 11680 */ + 0x00, 0x03, 0x00, 0x9e, 0x01, 0xbb, 0x00, 0x53, 0x01, 0x56, 0x00, 0x2d, 0x01, 0x49, 0x00, 0x03, /* bytes 11680 - 11696 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 11696 - 11712 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x06, 0x02, 0x00, 0x01, 0x93, 0x01, 0xf1, /* bytes 11712 - 11728 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 11728 - 11744 */ + 0x5c, 0x02, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x71, /* bytes 11744 - 11760 */ + 0x01, 0x27, 0x01, 0x49, 0x01, 0x25, 0x01, 0x2a, 0x01, 0x24, 0x01, 0x1f, 0x01, 0x24, 0x01, 0x15, /* bytes 11760 - 11776 */ + 0x01, 0x31, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xf8, 0x00, 0xe8, 0x00, 0xcf, /* bytes 11776 - 11792 */ + 0x00, 0xbb, 0x00, 0xdb, 0x00, 0x79, 0x00, 0x19, 0x01, 0x76, 0x00, 0x4c, 0x01, 0x34, 0x00, 0x29, /* bytes 11792 - 11808 */ + 0x02, 0x13, 0x00, 0x3a, 0x02, 0x30, 0x00, 0x6b, 0x02, 0x0d, 0x00, 0x98, 0x02, 0x12, 0x00, 0xb2, /* bytes 11808 - 11824 */ + 0x02, 0x38, 0x00, 0xab, 0x02, 0x78, 0x00, 0x76, 0x02, 0xa9, 0x00, 0x6a, 0x02, 0xeb, 0x00, 0x2c, /* bytes 11824 - 11840 */ + 0x02, 0x1d, 0x01, 0x23, 0x02, 0x6c, 0x01, 0xdc, 0x01, 0x14, 0x01, 0xda, 0x01, 0x24, 0x01, 0xd2, /* bytes 11840 - 11856 */ + 0x01, 0x6c, 0x01, 0x72, 0x01, 0x19, 0x01, 0x73, 0x01, 0x09, 0x01, 0x7d, 0x01, 0xdd, 0x00, 0x71, /* bytes 11856 - 11872 */ + 0x01, 0x27, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 11872 - 11888 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x79, 0x00, 0xe1, /* bytes 11888 - 11904 */ + 0x01, 0x70, 0x00, 0xc8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x73, 0x01, /* bytes 11904 - 11920 */ + 0x09, 0x01, 0xdc, 0x01, 0x14, 0x01, 0xbf, 0x01, 0xf1, 0x00, 0xc7, 0x01, 0xa9, 0x00, 0x0d, 0x02, /* bytes 11920 - 11936 */ + 0x78, 0x00, 0x38, 0x02, 0xad, 0x00, 0x2c, 0x02, 0x1d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 11936 - 11952 */ + 0x00, 0x06, 0x00, 0x23, 0x02, 0x6c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, /* bytes 11952 - 11968 */ + 0x01, 0x5c, 0x02, 0xb4, 0x01, 0xfc, 0x01, 0xd2, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 11968 - 11984 */ + 0x01, 0x00, 0x04, 0x00, 0x1f, 0x01, 0x24, 0x01, 0x34, 0x01, 0xeb, 0x00, 0x0a, 0x01, 0xc0, 0x00, /* bytes 11984 - 12000 */ + 0xf8, 0x00, 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x76, 0x02, 0xa9, /* bytes 12000 - 12016 */ + 0x00, 0x77, 0x02, 0x73, 0x00, 0x3a, 0x02, 0x30, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 12016 - 12032 */ + 0x03, 0x00, 0x19, 0x01, 0x76, 0x00, 0x32, 0x01, 0x7c, 0x00, 0x7d, 0x01, 0xdd, 0x00, 0x03, 0x00, /* bytes 12032 - 12048 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, /* bytes 12048 - 12064 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xda, 0x01, 0x24, 0x01, 0x72, 0x01, 0x19, 0x01, /* bytes 12064 - 12080 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, /* bytes 12080 - 12096 */ + 0x02, 0x23, 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xe3, 0x01, /* bytes 12096 - 12112 */ + 0x9f, 0x00, 0xfe, 0x01, 0x90, 0x00, 0x24, 0x02, 0x91, 0x00, 0x33, 0x02, 0xa8, 0x00, 0x34, 0x02, /* bytes 12112 - 12128 */ + 0xe7, 0x00, 0x11, 0x02, 0x18, 0x01, 0x12, 0x02, 0x4b, 0x01, 0xe6, 0x01, 0x82, 0x01, 0xe7, 0x01, /* bytes 12128 - 12144 */ + 0xd1, 0x01, 0x99, 0x01, 0x80, 0x01, 0x9b, 0x01, 0x90, 0x01, 0x9b, 0x01, 0xd7, 0x01, 0x3f, 0x01, /* bytes 12144 - 12160 */ + 0x8d, 0x01, 0x3f, 0x01, 0x7d, 0x01, 0x32, 0x01, 0x40, 0x01, 0x3a, 0x01, 0x8a, 0x01, 0x18, 0x01, /* bytes 12160 - 12176 */ + 0x92, 0x01, 0xfe, 0x00, 0x98, 0x01, 0xf4, 0x00, 0x9a, 0x01, 0xf2, 0x00, 0xad, 0x01, 0xae, 0x00, /* bytes 12176 - 12192 */ + 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xc4, 0x00, 0x6a, 0x01, 0x96, 0x00, 0x47, 0x01, 0x90, 0x00, /* bytes 12192 - 12208 */ + 0x05, 0x01, 0xce, 0x00, 0xf3, 0x00, 0x03, 0x01, 0xad, 0x00, 0xbf, 0x01, 0x7a, 0x00, 0xe3, 0x01, /* bytes 12208 - 12224 */ + 0x9f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, /* bytes 12224 - 12240 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x43, 0x00, 0xe2, 0x01, /* bytes 12240 - 12256 */ + 0x60, 0x00, 0xfc, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x78, 0x00, 0xe0, /* bytes 12256 - 12272 */ + 0x01, 0x70, 0x00, 0xc7, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xe6, 0x01, /* bytes 12272 - 12288 */ + 0x82, 0x01, 0xe3, 0x01, 0x12, 0x01, 0xb4, 0x01, 0xe1, 0x00, 0x78, 0x01, 0x17, 0x01, 0x7a, 0x01, /* bytes 12288 - 12304 */ + 0x5f, 0x01, 0x99, 0x01, 0x80, 0x01, 0x3f, 0x01, 0x7d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 12304 - 12320 */ + 0x00, 0x06, 0x00, 0x9b, 0x01, 0xd7, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, /* bytes 12320 - 12336 */ + 0x01, 0x5c, 0x02, 0x04, 0x02, 0x36, 0x02, 0xe7, 0x01, 0xd1, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 12336 - 12352 */ + 0x01, 0x00, 0x04, 0x00, 0xf4, 0x00, 0x9a, 0x01, 0xf8, 0x00, 0x5e, 0x01, 0xc9, 0x00, 0x3e, 0x01, /* bytes 12352 - 12368 */ + 0xc4, 0x00, 0x6a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xe3, 0x01, 0x9f, /* bytes 12368 - 12384 */ + 0x00, 0x0f, 0x02, 0xd4, 0x00, 0x11, 0x02, 0x18, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 12384 - 12400 */ + 0x03, 0x00, 0x32, 0x01, 0x40, 0x01, 0xda, 0x00, 0xf3, 0x00, 0xce, 0x00, 0xf3, 0x00, 0x03, 0x00, /* bytes 12400 - 12416 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, /* bytes 12416 - 12432 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, /* bytes 12432 - 12448 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9b, 0x01, 0x90, 0x01, 0x3f, 0x01, 0x8d, /* bytes 12448 - 12464 */ + 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xcb, 0x01, /* bytes 12464 - 12480 */ + 0x65, 0x00, 0xf5, 0x01, 0x87, 0x00, 0x12, 0x02, 0x61, 0x00, 0x3e, 0x02, 0x5f, 0x00, 0x66, 0x02, /* bytes 12480 - 12496 */ + 0x87, 0x00, 0x69, 0x02, 0xc7, 0x00, 0x2c, 0x02, 0xfd, 0x00, 0x2f, 0x02, 0x30, 0x01, 0xfa, 0x01, /* bytes 12496 - 12512 */ + 0x6b, 0x01, 0xff, 0x01, 0xbb, 0x01, 0xaa, 0x01, 0x6f, 0x01, 0xab, 0x01, 0x7f, 0x01, 0xaf, 0x01, /* bytes 12512 - 12528 */ + 0xc7, 0x01, 0x44, 0x01, 0x85, 0x01, 0x43, 0x01, 0x75, 0x01, 0x40, 0x01, 0x4f, 0x01, 0x45, 0x01, /* bytes 12528 - 12544 */ + 0x95, 0x01, 0x20, 0x01, 0x97, 0x01, 0x03, 0x01, 0x99, 0x01, 0xf8, 0x00, 0x9a, 0x01, 0xed, 0x00, /* bytes 12544 - 12560 */ + 0xac, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xc7, 0x00, 0x65, 0x01, 0x97, 0x00, /* bytes 12560 - 12576 */ + 0x3e, 0x01, 0x94, 0x00, 0xff, 0x00, 0xe4, 0x00, 0xfb, 0x00, 0xf8, 0x00, 0xa9, 0x00, 0xcb, 0x01, /* bytes 12576 - 12592 */ + 0x65, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, /* bytes 12592 - 12608 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5f, 0x00, 0xfb, 0x01, /* bytes 12608 - 12624 */ + 0x42, 0x00, 0xe0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x77, 0x00, 0xda, /* bytes 12624 - 12640 */ + 0x01, 0x70, 0x00, 0xc1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x48, 0x00, /* bytes 12640 - 12656 */ + 0x18, 0x02, 0x67, 0x00, 0x22, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x43, /* bytes 12656 - 12672 */ + 0x01, 0x75, 0x01, 0xaa, 0x01, 0x6f, 0x01, 0x89, 0x01, 0x51, 0x01, 0x85, 0x01, 0x09, 0x01, 0xc1, /* bytes 12672 - 12688 */ + 0x01, 0xce, 0x00, 0xf4, 0x01, 0xfb, 0x00, 0xfa, 0x01, 0x6b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 12688 - 12704 */ + 0x01, 0x00, 0x06, 0x00, 0xff, 0x01, 0xbb, 0x01, 0x04, 0x02, 0x36, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 12704 - 12720 */ + 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x2e, 0x02, 0xaf, 0x01, 0xc7, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 12720 - 12736 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xf8, 0x00, 0x9a, 0x01, 0xff, 0x00, 0x62, 0x01, 0xcf, 0x00, 0x3e, /* bytes 12736 - 12752 */ + 0x01, 0xc7, 0x00, 0x65, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x40, 0x01, /* bytes 12752 - 12768 */ + 0x4f, 0x01, 0xe5, 0x00, 0xfb, 0x00, 0xe4, 0x00, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 12768 - 12784 */ + 0x00, 0x03, 0x00, 0xf5, 0x01, 0x87, 0x00, 0x28, 0x02, 0xb8, 0x00, 0x2c, 0x02, 0xfd, 0x00, 0x03, /* bytes 12784 - 12800 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 12800 - 12816 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, /* bytes 12816 - 12832 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0x01, 0x85, 0x01, 0xab, 0x01, /* bytes 12832 - 12848 */ + 0x7f, 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x4b, /* bytes 12848 - 12864 */ + 0x01, 0x3b, 0x01, 0x50, 0x01, 0x81, 0x01, 0x2a, 0x01, 0x83, 0x01, 0x0e, 0x01, 0x84, 0x01, 0x03, /* bytes 12864 - 12880 */ + 0x01, 0x85, 0x01, 0xf0, 0x00, 0x97, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, /* bytes 12880 - 12896 */ + 0x00, 0x50, 0x01, 0xa2, 0x00, 0x29, 0x01, 0x9f, 0x00, 0xea, 0x00, 0xef, 0x00, 0xe6, 0x00, 0xfc, /* bytes 12896 - 12912 */ + 0x00, 0x95, 0x00, 0xcf, 0x01, 0x51, 0x00, 0xf9, 0x01, 0x73, 0x00, 0x15, 0x02, 0x4d, 0x00, 0x41, /* bytes 12912 - 12928 */ + 0x02, 0x4b, 0x00, 0x69, 0x02, 0x73, 0x00, 0x6d, 0x02, 0xb3, 0x00, 0x30, 0x02, 0xe9, 0x00, 0x32, /* bytes 12928 - 12944 */ + 0x02, 0x1c, 0x01, 0xfd, 0x01, 0x57, 0x01, 0x02, 0x02, 0xa7, 0x01, 0xae, 0x01, 0x5b, 0x01, 0xae, /* bytes 12944 - 12960 */ + 0x01, 0x6b, 0x01, 0xb2, 0x01, 0xb3, 0x01, 0x47, 0x01, 0x71, 0x01, 0x46, 0x01, 0x61, 0x01, 0x4b, /* bytes 12960 - 12976 */ + 0x01, 0x3b, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 12976 - 12992 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x46, 0x00, 0x18, /* bytes 12992 - 13008 */ + 0x02, 0x66, 0x00, 0x21, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, /* bytes 13008 - 13024 */ + 0xc8, 0x01, 0x6f, 0x00, 0x92, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 13024 - 13040 */ + 0x00, 0xf4, 0x01, 0x36, 0x00, 0xd4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, /* bytes 13040 - 13056 */ + 0xfd, 0x01, 0x57, 0x01, 0xf7, 0x01, 0xe7, 0x00, 0xc5, 0x01, 0xba, 0x00, 0x88, 0x01, 0xf5, 0x00, /* bytes 13056 - 13072 */ + 0x8c, 0x01, 0x3d, 0x01, 0xae, 0x01, 0x5b, 0x01, 0x46, 0x01, 0x61, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 13072 - 13088 */ + 0x00, 0x01, 0x00, 0x06, 0x00, 0x02, 0x02, 0xa7, 0x01, 0x04, 0x02, 0x31, 0x02, 0xb8, 0x01, 0x5c, /* bytes 13088 - 13104 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x2e, 0x02, 0xb2, 0x01, 0xb3, 0x01, 0x03, 0x00, 0xc0, /* bytes 13104 - 13120 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x03, 0x01, 0x85, 0x01, 0x0a, 0x01, 0x4e, 0x01, 0xd9, 0x00, /* bytes 13120 - 13136 */ + 0x29, 0x01, 0xd2, 0x00, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4b, /* bytes 13136 - 13152 */ + 0x01, 0x3b, 0x01, 0xf0, 0x00, 0xe6, 0x00, 0xef, 0x00, 0xe6, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 13152 - 13168 */ + 0x01, 0x00, 0x03, 0x00, 0xf9, 0x01, 0x73, 0x00, 0x2c, 0x02, 0xa4, 0x00, 0x30, 0x02, 0xe9, 0x00, /* bytes 13168 - 13184 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, /* bytes 13184 - 13200 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, /* bytes 13200 - 13216 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xae, 0x01, 0x6b, 0x01, 0x47, /* bytes 13216 - 13232 */ + 0x01, 0x71, 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, /* bytes 13232 - 13248 */ + 0xd8, 0x00, 0x3b, 0x01, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0xf5, 0x00, 0x83, 0x01, /* bytes 13248 - 13264 */ + 0x08, 0x01, 0x70, 0x01, 0x13, 0x01, 0x70, 0x01, 0x30, 0x01, 0x6f, 0x01, 0x55, 0x01, 0x6e, 0x01, /* bytes 13264 - 13280 */ + 0x52, 0x01, 0x28, 0x01, 0x4c, 0x01, 0x4e, 0x01, 0x4d, 0x01, 0x5e, 0x01, 0xb7, 0x01, 0xa3, 0x01, /* bytes 13280 - 13296 */ + 0xb5, 0x01, 0x5b, 0x01, 0xb4, 0x01, 0x4b, 0x01, 0x07, 0x02, 0x98, 0x01, 0x04, 0x02, 0x49, 0x01, /* bytes 13296 - 13312 */ + 0x3a, 0x02, 0x0f, 0x01, 0x39, 0x02, 0xdc, 0x00, 0x77, 0x02, 0xa7, 0x00, 0x75, 0x02, 0x67, 0x00, /* bytes 13312 - 13328 */ + 0x4e, 0x02, 0x3e, 0x00, 0x22, 0x02, 0x3f, 0x00, 0x05, 0x02, 0x64, 0x00, 0xdc, 0x01, 0x42, 0x00, /* bytes 13328 - 13344 */ + 0x07, 0x01, 0x80, 0x00, 0xf8, 0x00, 0xd1, 0x00, 0xa8, 0x00, 0xd4, 0x00, 0xa9, 0x00, 0x13, 0x01, /* bytes 13344 - 13360 */ + 0xd8, 0x00, 0x3b, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, /* bytes 13360 - 13376 */ + 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x43, 0x00, /* bytes 13376 - 13392 */ + 0xe2, 0x01, 0x0d, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, /* bytes 13392 - 13408 */ + 0x00, 0xc8, 0x01, 0x70, 0x00, 0x99, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 13408 - 13424 */ + 0x3a, 0x00, 0x18, 0x02, 0x5e, 0x00, 0x1f, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 13424 - 13440 */ + 0x00, 0x4c, 0x01, 0x4e, 0x01, 0xb4, 0x01, 0x4b, 0x01, 0x93, 0x01, 0x2c, 0x01, 0x91, 0x01, 0xe4, /* bytes 13440 - 13456 */ + 0x00, 0xcf, 0x01, 0xaa, 0x00, 0x01, 0x02, 0xd9, 0x00, 0x04, 0x02, 0x49, 0x01, 0x03, 0x00, 0xc0, /* bytes 13456 - 13472 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb7, 0x01, 0xa3, 0x01, 0xb4, 0x01, 0x29, 0x02, 0x58, 0x01, /* bytes 13472 - 13488 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2e, 0x02, 0x07, 0x02, 0x98, 0x01, 0x03, 0x00, /* bytes 13488 - 13504 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd8, 0x00, 0x3b, 0x01, 0xe1, 0x00, 0x13, 0x01, 0x11, /* bytes 13504 - 13520 */ + 0x01, 0x39, 0x01, 0x08, 0x01, 0x70, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 13520 - 13536 */ + 0x39, 0x02, 0xdc, 0x00, 0x37, 0x02, 0x97, 0x00, 0x05, 0x02, 0x64, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 13536 - 13552 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0xf8, 0x00, 0xd1, 0x00, 0xf9, 0x00, 0xd1, 0x00, 0x52, 0x01, 0x28, /* bytes 13552 - 13568 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 13568 - 13584 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, /* bytes 13584 - 13600 */ + 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, 0x5b, 0x01, /* bytes 13600 - 13616 */ + 0x4d, 0x01, 0x5e, 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 13616 - 13632 */ + 0x00, 0xfa, 0x00, 0x75, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xdc, 0x00, 0x2d, /* bytes 13632 - 13648 */ + 0x01, 0xae, 0x00, 0x04, 0x01, 0xad, 0x00, 0xc4, 0x00, 0xfe, 0x00, 0xc3, 0x00, 0x12, 0x01, 0x73, /* bytes 13648 - 13664 */ + 0x00, 0xe8, 0x01, 0x39, 0x00, 0x10, 0x02, 0x5d, 0x00, 0x2e, 0x02, 0x39, 0x00, 0x5a, 0x02, 0x38, /* bytes 13664 - 13680 */ + 0x00, 0x80, 0x02, 0x62, 0x00, 0x81, 0x02, 0xa2, 0x00, 0x41, 0x02, 0xd5, 0x00, 0x42, 0x02, 0x08, /* bytes 13680 - 13696 */ + 0x01, 0x0a, 0x02, 0x41, 0x01, 0x0b, 0x02, 0x91, 0x01, 0xba, 0x01, 0x42, 0x01, 0xba, 0x01, 0x52, /* bytes 13696 - 13712 */ + 0x01, 0xbb, 0x01, 0x9a, 0x01, 0x52, 0x01, 0x53, 0x01, 0x52, 0x01, 0x43, 0x01, 0x56, 0x01, 0x1c, /* bytes 13712 - 13728 */ + 0x01, 0x58, 0x01, 0x63, 0x01, 0x32, 0x01, 0x63, 0x01, 0x15, 0x01, 0x63, 0x01, 0x0b, 0x01, 0x63, /* bytes 13728 - 13744 */ + 0x01, 0xfa, 0x00, 0x75, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 13744 - 13760 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x43, /* bytes 13760 - 13776 */ + 0x00, 0xe2, 0x01, 0x13, 0x00, 0xb1, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 13776 - 13792 */ + 0x0c, 0x00, 0x18, 0x02, 0x48, 0x00, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 13792 - 13808 */ + 0x00, 0x70, 0x00, 0xc8, 0x01, 0x70, 0x00, 0x9a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 13808 - 13824 */ + 0x07, 0x00, 0x0a, 0x02, 0x41, 0x01, 0x09, 0x02, 0xd1, 0x00, 0xd9, 0x01, 0xa1, 0x00, 0x99, 0x01, /* bytes 13824 - 13840 */ + 0xda, 0x00, 0x9a, 0x01, 0x22, 0x01, 0xba, 0x01, 0x42, 0x01, 0x52, 0x01, 0x43, 0x01, 0x03, 0x00, /* bytes 13840 - 13856 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x0b, 0x02, 0x91, 0x01, 0x04, 0x02, 0x2b, 0x02, 0xb8, /* bytes 13856 - 13872 */ + 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x26, 0x02, 0xbb, 0x01, 0x9a, 0x01, 0x03, /* bytes 13872 - 13888 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xdc, 0x00, 0x2d, 0x01, 0xe5, 0x00, 0x06, 0x01, /* bytes 13888 - 13904 */ + 0x14, 0x01, 0x2d, 0x01, 0x0b, 0x01, 0x63, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 13904 - 13920 */ + 0x00, 0x41, 0x02, 0xd5, 0x00, 0x41, 0x02, 0x90, 0x00, 0x10, 0x02, 0x5d, 0x00, 0x03, 0x00, 0xc0, /* bytes 13920 - 13936 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xfe, 0x00, 0xc3, 0x00, 0xff, 0x00, 0xc4, 0x00, 0x56, 0x01, /* bytes 13936 - 13952 */ + 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, /* bytes 13952 - 13968 */ + 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x52, 0x01, 0x53, 0x01, /* bytes 13968 - 13984 */ + 0xba, 0x01, 0x52, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, /* bytes 13984 - 14000 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 14000 - 14016 */ + 0x1d, 0x00, 0x85, 0x02, 0x63, 0x00, 0x85, 0x02, 0xa3, 0x00, 0x44, 0x02, 0xd6, 0x00, 0x44, 0x02, /* bytes 14016 - 14032 */ + 0x09, 0x01, 0x0c, 0x02, 0x41, 0x01, 0x0c, 0x02, 0x91, 0x01, 0xbc, 0x01, 0x40, 0x01, 0xbc, 0x01, /* bytes 14032 - 14048 */ + 0x50, 0x01, 0xbc, 0x01, 0x98, 0x01, 0x54, 0x01, 0x50, 0x01, 0x54, 0x01, 0x40, 0x01, 0x55, 0x01, /* bytes 14048 - 14064 */ + 0x1a, 0x01, 0x56, 0x01, 0x60, 0x01, 0x30, 0x01, 0x60, 0x01, 0x14, 0x01, 0x60, 0x01, 0x09, 0x01, /* bytes 14064 - 14080 */ + 0x60, 0x01, 0xfc, 0x00, 0x72, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xdb, 0x00, /* bytes 14080 - 14096 */ + 0x29, 0x01, 0xad, 0x00, 0xff, 0x00, 0xae, 0x00, 0xc0, 0x00, 0xfe, 0x00, 0xc0, 0x00, 0x16, 0x01, /* bytes 14096 - 14112 */ + 0x70, 0x00, 0xed, 0x01, 0x39, 0x00, 0x15, 0x02, 0x5d, 0x00, 0x33, 0x02, 0x39, 0x00, 0x5f, 0x02, /* bytes 14112 - 14128 */ + 0x39, 0x00, 0x85, 0x02, 0x63, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, /* bytes 14128 - 14144 */ + 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 14144 - 14160 */ + 0x17, 0x00, 0x18, 0x02, 0x48, 0x00, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 14160 - 14176 */ + 0x00, 0x14, 0x00, 0xb2, 0x01, 0x43, 0x00, 0xe2, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 14176 - 14192 */ + 0x02, 0x00, 0x7d, 0x00, 0x68, 0x01, 0x70, 0x00, 0x9a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 14192 - 14208 */ + 0x00, 0x07, 0x00, 0x0c, 0x02, 0x41, 0x01, 0x0c, 0x02, 0xd1, 0x00, 0xdd, 0x01, 0xa1, 0x00, 0x9c, /* bytes 14208 - 14224 */ + 0x01, 0xd8, 0x00, 0x9c, 0x01, 0x20, 0x01, 0xbc, 0x01, 0x40, 0x01, 0x54, 0x01, 0x40, 0x01, 0x03, /* bytes 14224 - 14240 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x0c, 0x02, 0x91, 0x01, 0x04, 0x02, 0x2b, 0x02, /* bytes 14240 - 14256 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x23, 0x02, 0xbc, 0x01, 0x98, 0x01, /* bytes 14256 - 14272 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x09, 0x01, 0x60, 0x01, 0x13, 0x01, 0x29, /* bytes 14272 - 14288 */ + 0x01, 0xe5, 0x00, 0x02, 0x01, 0xdb, 0x00, 0x29, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 14288 - 14304 */ + 0x03, 0x00, 0x15, 0x02, 0x5d, 0x00, 0x45, 0x02, 0x91, 0x00, 0x44, 0x02, 0xd6, 0x00, 0x03, 0x00, /* bytes 14304 - 14320 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x55, 0x01, 0x1a, 0x01, 0x00, 0x01, 0xc0, 0x00, 0xfe, /* bytes 14320 - 14336 */ + 0x00, 0xc0, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 14336 - 14352 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x54, 0x01, 0x50, /* bytes 14352 - 14368 */ + 0x01, 0xbc, 0x01, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 14368 - 14384 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 14384 - 14400 */ + 0x00, 0x1d, 0x00, 0xd9, 0x00, 0x2b, 0x01, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0xfb, /* bytes 14400 - 14416 */ + 0x00, 0x73, 0x01, 0x07, 0x01, 0x62, 0x01, 0x11, 0x01, 0x62, 0x01, 0x2e, 0x01, 0x62, 0x01, 0x54, /* bytes 14416 - 14432 */ + 0x01, 0x62, 0x01, 0x53, 0x01, 0x1c, 0x01, 0x53, 0x01, 0x42, 0x01, 0x53, 0x01, 0x52, 0x01, 0xbb, /* bytes 14432 - 14448 */ + 0x01, 0x9b, 0x01, 0xbb, 0x01, 0x53, 0x01, 0xbb, 0x01, 0x43, 0x01, 0x0b, 0x02, 0x94, 0x01, 0x0b, /* bytes 14448 - 14464 */ + 0x02, 0x44, 0x01, 0x44, 0x02, 0x0c, 0x01, 0x44, 0x02, 0xd9, 0x00, 0x84, 0x02, 0xa7, 0x00, 0x85, /* bytes 14464 - 14480 */ + 0x02, 0x67, 0x00, 0x5f, 0x02, 0x3c, 0x00, 0x33, 0x02, 0x3c, 0x00, 0x15, 0x02, 0x60, 0x00, 0xed, /* bytes 14480 - 14496 */ + 0x01, 0x3b, 0x00, 0x16, 0x01, 0x72, 0x00, 0xfc, 0x00, 0xc2, 0x00, 0xac, 0x00, 0xc1, 0x00, 0xab, /* bytes 14496 - 14512 */ + 0x00, 0x01, 0x01, 0xd9, 0x00, 0x2b, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 14512 - 14528 */ + 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, /* bytes 14528 - 14544 */ + 0x00, 0x72, 0x00, 0x93, 0x01, 0x7e, 0x00, 0x67, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 14544 - 14560 */ + 0x02, 0x00, 0x14, 0x00, 0xb2, 0x01, 0xeb, 0xff, 0x8c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 14560 - 14576 */ + 0x00, 0x02, 0x00, 0x18, 0x00, 0x18, 0x02, 0x48, 0x00, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 14576 - 14592 */ + 0x01, 0x00, 0x07, 0x00, 0x0b, 0x02, 0x44, 0x01, 0x0c, 0x02, 0xd4, 0x00, 0xdc, 0x01, 0xa3, 0x00, /* bytes 14592 - 14608 */ + 0x9c, 0x01, 0xdb, 0x00, 0x9b, 0x01, 0x23, 0x01, 0xbb, 0x01, 0x43, 0x01, 0x53, 0x01, 0x42, 0x01, /* bytes 14608 - 14624 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x0b, 0x02, 0x94, 0x01, 0x04, 0x02, 0x2b, /* bytes 14624 - 14640 */ + 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x23, 0x02, 0xbb, 0x01, 0x9b, /* bytes 14640 - 14656 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd9, 0x00, 0x2b, 0x01, 0xe3, 0x00, /* bytes 14656 - 14672 */ + 0x04, 0x01, 0x11, 0x01, 0x2b, 0x01, 0x07, 0x01, 0x62, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 14672 - 14688 */ + 0x00, 0x03, 0x00, 0x44, 0x02, 0xd9, 0x00, 0x44, 0x02, 0x94, 0x00, 0x15, 0x02, 0x60, 0x00, 0x03, /* bytes 14688 - 14704 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x53, 0x01, 0x1c, 0x01, 0xfd, 0x00, 0xc2, 0x00, /* bytes 14704 - 14720 */ + 0xfc, 0x00, 0xc2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, /* bytes 14720 - 14736 */ + 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x53, 0x01, /* bytes 14736 - 14752 */ + 0x52, 0x01, 0xbb, 0x01, 0x53, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, /* bytes 14752 - 14768 */ + 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 14768 - 14784 */ + 0x00, 0x00, 0x1d, 0x00, 0x08, 0x02, 0x98, 0x01, 0xb8, 0x01, 0x47, 0x01, 0xb8, 0x01, 0x57, 0x01, /* bytes 14784 - 14800 */ + 0xb8, 0x01, 0x9f, 0x01, 0x50, 0x01, 0x57, 0x01, 0x50, 0x01, 0x47, 0x01, 0x4f, 0x01, 0x21, 0x01, /* bytes 14800 - 14816 */ + 0x50, 0x01, 0x67, 0x01, 0x2b, 0x01, 0x67, 0x01, 0x0e, 0x01, 0x66, 0x01, 0x03, 0x01, 0x66, 0x01, /* bytes 14816 - 14832 */ + 0xf8, 0x00, 0x78, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd5, 0x00, 0x2f, 0x01, /* bytes 14832 - 14848 */ + 0xa8, 0x00, 0x05, 0x01, 0xa8, 0x00, 0xc6, 0x00, 0xf9, 0x00, 0xc6, 0x00, 0x13, 0x01, 0x76, 0x00, /* bytes 14848 - 14864 */ + 0xea, 0x01, 0x3f, 0x00, 0x12, 0x02, 0x64, 0x00, 0x30, 0x02, 0x40, 0x00, 0x5c, 0x02, 0x40, 0x00, /* bytes 14864 - 14880 */ + 0x82, 0x02, 0x6b, 0x00, 0x81, 0x02, 0xab, 0x00, 0x41, 0x02, 0xdd, 0x00, 0x41, 0x02, 0x10, 0x01, /* bytes 14880 - 14896 */ + 0x08, 0x02, 0x48, 0x01, 0x08, 0x02, 0x98, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, /* bytes 14896 - 14912 */ + 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 14912 - 14928 */ + 0x02, 0x00, 0xe4, 0xff, 0x22, 0x02, 0x18, 0x00, 0x18, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 14928 - 14944 */ + 0x00, 0x02, 0x00, 0xea, 0xff, 0x8b, 0x01, 0x0e, 0x00, 0xad, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 14944 - 14960 */ + 0x01, 0x00, 0x07, 0x00, 0x50, 0x01, 0x47, 0x01, 0xb8, 0x01, 0x47, 0x01, 0x99, 0x01, 0x27, 0x01, /* bytes 14960 - 14976 */ + 0x99, 0x01, 0xdf, 0x00, 0xd9, 0x01, 0xa7, 0x00, 0x09, 0x02, 0xd8, 0x00, 0x08, 0x02, 0x48, 0x01, /* bytes 14976 - 14992 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb8, 0x01, 0x9f, 0x01, 0xb4, 0x01, 0x23, /* bytes 14992 - 15008 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x08, 0x02, 0x98, /* bytes 15008 - 15024 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd5, 0x00, 0x2f, 0x01, 0xdf, 0x00, /* bytes 15024 - 15040 */ + 0x08, 0x01, 0x0d, 0x01, 0x30, 0x01, 0x03, 0x01, 0x66, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 15040 - 15056 */ + 0x00, 0x03, 0x00, 0xf9, 0x00, 0xc6, 0x00, 0xfa, 0x00, 0xc6, 0x00, 0x4f, 0x01, 0x21, 0x01, 0x03, /* bytes 15056 - 15072 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x12, 0x02, 0x64, 0x00, 0x41, 0x02, 0x98, 0x00, /* bytes 15072 - 15088 */ + 0x41, 0x02, 0xdd, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, /* bytes 15088 - 15104 */ + 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, /* bytes 15104 - 15120 */ + 0x57, 0x01, 0x50, 0x01, 0x57, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, /* bytes 15120 - 15136 */ + 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 15136 - 15152 */ + 0x00, 0x00, 0x1d, 0x00, 0xa5, 0x00, 0xca, 0x00, 0xf5, 0x00, 0xca, 0x00, 0x10, 0x01, 0x7a, 0x00, /* bytes 15152 - 15168 */ + 0xe6, 0x01, 0x43, 0x00, 0x0e, 0x02, 0x67, 0x00, 0x2d, 0x02, 0x43, 0x00, 0x59, 0x02, 0x43, 0x00, /* bytes 15168 - 15184 */ + 0x7e, 0x02, 0x6e, 0x00, 0x7e, 0x02, 0xae, 0x00, 0x3e, 0x02, 0xe0, 0x00, 0x3e, 0x02, 0x13, 0x01, /* bytes 15184 - 15200 */ + 0x05, 0x02, 0x4b, 0x01, 0x05, 0x02, 0x9b, 0x01, 0xb5, 0x01, 0x4b, 0x01, 0xb5, 0x01, 0x5b, 0x01, /* bytes 15200 - 15216 */ + 0xb5, 0x01, 0xa3, 0x01, 0x4d, 0x01, 0x5b, 0x01, 0x4d, 0x01, 0x4b, 0x01, 0x4c, 0x01, 0x24, 0x01, /* bytes 15216 - 15232 */ + 0x4e, 0x01, 0x6b, 0x01, 0x28, 0x01, 0x6b, 0x01, 0x0b, 0x01, 0x6a, 0x01, 0x01, 0x01, 0x6a, 0x01, /* bytes 15232 - 15248 */ + 0xf5, 0x00, 0x7c, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x33, 0x01, /* bytes 15248 - 15264 */ + 0xa5, 0x00, 0x0a, 0x01, 0xa5, 0x00, 0xca, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, /* bytes 15264 - 15280 */ + 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, /* bytes 15280 - 15296 */ + 0x02, 0x00, 0xe1, 0xff, 0x22, 0x02, 0x10, 0x00, 0x19, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 15296 - 15312 */ + 0x00, 0x07, 0x00, 0x4d, 0x01, 0x4b, 0x01, 0xb5, 0x01, 0x4b, 0x01, 0x96, 0x01, 0x2b, 0x01, 0x96, /* bytes 15312 - 15328 */ + 0x01, 0xe3, 0x00, 0xd6, 0x01, 0xab, 0x00, 0x06, 0x02, 0xdb, 0x00, 0x05, 0x02, 0x4b, 0x01, 0x03, /* bytes 15328 - 15344 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb5, 0x01, 0xa3, 0x01, 0xb4, 0x01, 0x24, 0x02, /* bytes 15344 - 15360 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x05, 0x02, 0x9b, 0x01, /* bytes 15360 - 15376 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x01, 0x6a, 0x01, 0x0b, 0x01, 0x34, /* bytes 15376 - 15392 */ + 0x01, 0xdc, 0x00, 0x0c, 0x01, 0xd2, 0x00, 0x33, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 15392 - 15408 */ + 0x03, 0x00, 0xf5, 0x00, 0xca, 0x00, 0xf7, 0x00, 0xcb, 0x00, 0x4c, 0x01, 0x24, 0x01, 0x03, 0x00, /* bytes 15408 - 15424 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3e, 0x02, 0xe0, 0x00, 0x3e, 0x02, 0x9b, 0x00, 0x0e, /* bytes 15424 - 15440 */ + 0x02, 0x67, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 15440 - 15456 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4d, 0x01, 0x5b, /* bytes 15456 - 15472 */ + 0x01, 0xb5, 0x01, 0x5b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 15472 - 15488 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 15488 - 15504 */ + 0x00, 0x1d, 0x00, 0x57, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6f, 0x00, 0x7c, 0x02, 0xaf, 0x00, 0x3c, /* bytes 15504 - 15520 */ + 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 15520 - 15536 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 15536 - 15552 */ + 0x01, 0x4c, 0x01, 0x4b, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0a, /* bytes 15552 - 15568 */ + 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, /* bytes 15568 - 15584 */ + 0x00, 0x5c, 0x02, 0xd1, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, /* bytes 15584 - 15600 */ + 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2b, /* bytes 15600 - 15616 */ + 0x02, 0x44, 0x00, 0x57, 0x02, 0x44, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 15616 - 15632 */ + 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 15632 - 15648 */ + 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, /* bytes 15648 - 15664 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 15664 - 15680 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, /* bytes 15680 - 15696 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, /* bytes 15696 - 15712 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd1, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, /* bytes 15712 - 15728 */ + 0x01, 0x36, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 15728 - 15744 */ + 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 15744 - 15760 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4b, 0x01, 0x26, /* bytes 15760 - 15776 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 15776 - 15792 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, /* bytes 15792 - 15808 */ + 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 15808 - 15824 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 15824 - 15840 */ + 0x00, 0x0a, 0x01, 0x6d, 0x01, 0x00, 0x01, 0x6d, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, /* bytes 15840 - 15856 */ + 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd1, 0x00, 0x36, 0x01, 0xa4, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0xcd, /* bytes 15856 - 15872 */ + 0x00, 0xf4, 0x00, 0xcd, 0x00, 0x0d, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, /* bytes 15872 - 15888 */ + 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6f, 0x00, 0x7c, 0x02, 0xaf, /* bytes 15888 - 15904 */ + 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 15904 - 15920 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 15920 - 15936 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4b, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6d, /* bytes 15936 - 15952 */ + 0x01, 0x0a, 0x01, 0x6d, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 15952 - 15968 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, /* bytes 15968 - 15984 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, /* bytes 15984 - 16000 */ + 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 16000 - 16016 */ + 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 16016 - 16032 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 16032 - 16048 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6d, 0x01, 0x0a, 0x01, 0x36, 0x01, 0xdb, 0x00, 0x0e, /* bytes 16048 - 16064 */ + 0x01, 0xd1, 0x00, 0x36, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, /* bytes 16064 - 16080 */ + 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 16080 - 16096 */ + 0x00, 0x03, 0x00, 0x4b, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcd, 0x00, 0xf4, 0x00, 0xcd, 0x00, 0x03, /* bytes 16096 - 16112 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 16112 - 16128 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, /* bytes 16128 - 16144 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, /* bytes 16144 - 16160 */ + 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xb4, /* bytes 16160 - 16176 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4b, 0x01, 0x26, 0x01, 0x4d, /* bytes 16176 - 16192 */ + 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, /* bytes 16192 - 16208 */ + 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, /* bytes 16208 - 16224 */ + 0x00, 0x0c, 0x01, 0xa4, 0x00, 0xcd, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0d, 0x01, 0x7c, 0x00, 0xe4, /* bytes 16224 - 16240 */ + 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, /* bytes 16240 - 16256 */ + 0x02, 0x6f, 0x00, 0x7c, 0x02, 0xaf, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 16256 - 16272 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 16272 - 16288 */ + 0x01, 0xa4, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 16288 - 16304 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, /* bytes 16304 - 16320 */ + 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, /* bytes 16320 - 16336 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 16336 - 16352 */ + 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 16352 - 16368 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 16368 - 16384 */ + 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x36, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, /* bytes 16384 - 16400 */ + 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, /* bytes 16400 - 16416 */ + 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 16416 - 16432 */ + 0x00, 0x4b, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcd, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, /* bytes 16432 - 16448 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 16448 - 16464 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, /* bytes 16464 - 16480 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 16480 - 16496 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x0c, 0x02, 0x68, /* bytes 16496 - 16512 */ + 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, /* bytes 16512 - 16528 */ + 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 16528 - 16544 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 16544 - 16560 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, /* bytes 16560 - 16576 */ + 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, /* bytes 16576 - 16592 */ + 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0xcc, /* bytes 16592 - 16608 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0d, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, /* bytes 16608 - 16624 */ + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, /* bytes 16624 - 16640 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 16640 - 16656 */ + 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 16656 - 16672 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 16672 - 16688 */ + 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 16688 - 16704 */ + 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 16704 - 16720 */ + 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, /* bytes 16720 - 16736 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, /* bytes 16736 - 16752 */ + 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, /* bytes 16752 - 16768 */ + 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 16768 - 16784 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 16784 - 16800 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 16800 - 16816 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 16816 - 16832 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 16832 - 16848 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 16848 - 16864 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, /* bytes 16864 - 16880 */ + 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, /* bytes 16880 - 16896 */ + 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, /* bytes 16896 - 16912 */ + 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, /* bytes 16912 - 16928 */ + 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, /* bytes 16928 - 16944 */ + 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0x03, /* bytes 16944 - 16960 */ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, /* bytes 16960 - 16976 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 16976 - 16992 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 16992 - 17008 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, /* bytes 17008 - 17024 */ + 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 17024 - 17040 */ + 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, /* bytes 17040 - 17056 */ + 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, /* bytes 17056 - 17072 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, /* bytes 17072 - 17088 */ + 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, /* bytes 17088 - 17104 */ + 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 17104 - 17120 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 17120 - 17136 */ + 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 17136 - 17152 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, /* bytes 17152 - 17168 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xd2, 0x00, 0x35, 0x01, 0x87, 0x00, 0x5c, /* bytes 17168 - 17184 */ + 0x02, 0xae, 0x00, 0x5c, 0x02, 0xf4, 0x00, 0x7e, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, /* bytes 17184 - 17200 */ + 0x01, 0x27, 0x01, 0x6c, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4c, 0x01, 0x4c, /* bytes 17200 - 17216 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 17216 - 17232 */ + 0x01, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x3c, 0x02, 0x14, 0x01, 0x3c, 0x02, 0xe1, /* bytes 17232 - 17248 */ + 0x00, 0x7c, 0x02, 0xae, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x56, 0x02, 0x44, 0x00, 0x2a, 0x02, 0x44, /* bytes 17248 - 17264 */ + 0x00, 0x0c, 0x02, 0x68, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xf4, 0x00, 0xcc, /* bytes 17264 - 17280 */ + 0x00, 0xa4, 0x00, 0xcc, 0x00, 0xa4, 0x00, 0x0b, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0x00, /* bytes 17280 - 17296 */ + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, /* bytes 17296 - 17312 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, /* bytes 17312 - 17328 */ + 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, /* bytes 17328 - 17344 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, /* bytes 17344 - 17360 */ + 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, /* bytes 17360 - 17376 */ + 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, /* bytes 17376 - 17392 */ + 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, /* bytes 17392 - 17408 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, /* bytes 17408 - 17424 */ + 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, /* bytes 17424 - 17440 */ + 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 17440 - 17456 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 17456 - 17472 */ + 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 17472 - 17488 */ + 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 17488 - 17504 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, /* bytes 17504 - 17520 */ + 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 17520 - 17536 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 17536 - 17552 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, /* bytes 17552 - 17568 */ + 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, /* bytes 17568 - 17584 */ + 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, /* bytes 17584 - 17600 */ + 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, /* bytes 17600 - 17616 */ + 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, /* bytes 17616 - 17632 */ + 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 17632 - 17648 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, /* bytes 17648 - 17664 */ + 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 17664 - 17680 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, /* bytes 17680 - 17696 */ + 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, /* bytes 17696 - 17712 */ + 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, /* bytes 17712 - 17728 */ + 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 17728 - 17744 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, /* bytes 17744 - 17760 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, /* bytes 17760 - 17776 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 17776 - 17792 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 17792 - 17808 */ + 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 17808 - 17824 */ + 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 17824 - 17840 */ + 0xff, 0x00, 0x00, 0x1d, 0x00, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, /* bytes 17840 - 17856 */ + 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, /* bytes 17856 - 17872 */ + 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, /* bytes 17872 - 17888 */ + 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, /* bytes 17888 - 17904 */ + 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 17904 - 17920 */ + 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 17920 - 17936 */ + 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, /* bytes 17936 - 17952 */ + 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 17952 - 17968 */ + 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 17968 - 17984 */ + 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, /* bytes 17984 - 18000 */ + 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, /* bytes 18000 - 18016 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, /* bytes 18016 - 18032 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, /* bytes 18032 - 18048 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, /* bytes 18048 - 18064 */ + 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 18064 - 18080 */ + 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, /* bytes 18080 - 18096 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, /* bytes 18096 - 18112 */ + 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, /* bytes 18112 - 18128 */ + 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, /* bytes 18128 - 18144 */ + 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 18144 - 18160 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 18160 - 18176 */ + 0x00, 0x1d, 0x00, 0xd2, 0x00, 0x35, 0x01, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0xf4, /* bytes 18176 - 18192 */ + 0x00, 0x7e, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x4d, /* bytes 18192 - 18208 */ + 0x01, 0x6c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0xb4, /* bytes 18208 - 18224 */ + 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0x04, /* bytes 18224 - 18240 */ + 0x02, 0x4c, 0x01, 0x3c, 0x02, 0x14, 0x01, 0x3c, 0x02, 0xe1, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x7c, /* bytes 18240 - 18256 */ + 0x02, 0x6e, 0x00, 0x56, 0x02, 0x44, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0xe4, /* bytes 18256 - 18272 */ + 0x01, 0x44, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xa4, 0x00, 0xcc, 0x00, 0xa4, /* bytes 18272 - 18288 */ + 0x00, 0x0b, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 18288 - 18304 */ + 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 18304 - 18320 */ + 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, /* bytes 18320 - 18336 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 18336 - 18352 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, /* bytes 18352 - 18368 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, /* bytes 18368 - 18384 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, /* bytes 18384 - 18400 */ + 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 18400 - 18416 */ + 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 18416 - 18432 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, /* bytes 18432 - 18448 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 18448 - 18464 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, /* bytes 18464 - 18480 */ + 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 18480 - 18496 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 18496 - 18512 */ + 0x00, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, /* bytes 18512 - 18528 */ + 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, /* bytes 18528 - 18544 */ + 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, /* bytes 18544 - 18560 */ + 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, /* bytes 18560 - 18576 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 18576 - 18592 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, /* bytes 18592 - 18608 */ + 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, /* bytes 18608 - 18624 */ + 0x01, 0xf4, 0x00, 0x7e, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 18624 - 18640 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, /* bytes 18640 - 18656 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, /* bytes 18656 - 18672 */ + 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 18672 - 18688 */ + 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 18688 - 18704 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 18704 - 18720 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, /* bytes 18720 - 18736 */ + 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, /* bytes 18736 - 18752 */ + 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 18752 - 18768 */ + 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, /* bytes 18768 - 18784 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 18784 - 18800 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, /* bytes 18800 - 18816 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 18816 - 18832 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x2a, /* bytes 18832 - 18848 */ + 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, /* bytes 18848 - 18864 */ + 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 18864 - 18880 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 18880 - 18896 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, /* bytes 18896 - 18912 */ + 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, /* bytes 18912 - 18928 */ + 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, /* bytes 18928 - 18944 */ + 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, /* bytes 18944 - 18960 */ + 0x02, 0x44, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 18960 - 18976 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, /* bytes 18976 - 18992 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, /* bytes 18992 - 19008 */ + 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 19008 - 19024 */ + 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, /* bytes 19024 - 19040 */ + 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 19040 - 19056 */ + 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, /* bytes 19056 - 19072 */ + 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, /* bytes 19072 - 19088 */ + 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 19088 - 19104 */ + 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, /* bytes 19104 - 19120 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 19120 - 19136 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, /* bytes 19136 - 19152 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, /* bytes 19152 - 19168 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xf4, 0x00, 0x7e, /* bytes 19168 - 19184 */ + 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, /* bytes 19184 - 19200 */ + 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, /* bytes 19200 - 19216 */ + 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, /* bytes 19216 - 19232 */ + 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, /* bytes 19232 - 19248 */ + 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, /* bytes 19248 - 19264 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, /* bytes 19264 - 19280 */ + 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, /* bytes 19280 - 19296 */ + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, /* bytes 19296 - 19312 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 19312 - 19328 */ + 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 19328 - 19344 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 19344 - 19360 */ + 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 19360 - 19376 */ + 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 19376 - 19392 */ + 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, /* bytes 19392 - 19408 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, /* bytes 19408 - 19424 */ + 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, /* bytes 19424 - 19440 */ + 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 19440 - 19456 */ + 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 19456 - 19472 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 19472 - 19488 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 19488 - 19504 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 19504 - 19520 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 19520 - 19536 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, /* bytes 19536 - 19552 */ + 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, /* bytes 19552 - 19568 */ + 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, /* bytes 19568 - 19584 */ + 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, /* bytes 19584 - 19600 */ + 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, /* bytes 19600 - 19616 */ + 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x03, /* bytes 19616 - 19632 */ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, /* bytes 19632 - 19648 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 19648 - 19664 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 19664 - 19680 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, /* bytes 19680 - 19696 */ + 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 19696 - 19712 */ + 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, /* bytes 19712 - 19728 */ + 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, /* bytes 19728 - 19744 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, /* bytes 19744 - 19760 */ + 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, /* bytes 19760 - 19776 */ + 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 19776 - 19792 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 19792 - 19808 */ + 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 19808 - 19824 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 19824 - 19840 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, /* bytes 19840 - 19856 */ + 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, /* bytes 19856 - 19872 */ + 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, /* bytes 19872 - 19888 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 19888 - 19904 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, /* bytes 19904 - 19920 */ + 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, /* bytes 19920 - 19936 */ + 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, /* bytes 19936 - 19952 */ + 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0x00, /* bytes 19952 - 19968 */ + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, /* bytes 19968 - 19984 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, /* bytes 19984 - 20000 */ + 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, /* bytes 20000 - 20016 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, /* bytes 20016 - 20032 */ + 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, /* bytes 20032 - 20048 */ + 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, /* bytes 20048 - 20064 */ + 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, /* bytes 20064 - 20080 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, /* bytes 20080 - 20096 */ + 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, /* bytes 20096 - 20112 */ + 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 20112 - 20128 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 20128 - 20144 */ + 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 20144 - 20160 */ + 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 20160 - 20176 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, /* bytes 20176 - 20192 */ + 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, /* bytes 20192 - 20208 */ + 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, /* bytes 20208 - 20224 */ + 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, /* bytes 20224 - 20240 */ + 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, /* bytes 20240 - 20256 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 20256 - 20272 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 20272 - 20288 */ + 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, /* bytes 20288 - 20304 */ + 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 20304 - 20320 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, /* bytes 20320 - 20336 */ + 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, /* bytes 20336 - 20352 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, /* bytes 20352 - 20368 */ + 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, /* bytes 20368 - 20384 */ + 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, /* bytes 20384 - 20400 */ + 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 20400 - 20416 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, /* bytes 20416 - 20432 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, /* bytes 20432 - 20448 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 20448 - 20464 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, /* bytes 20464 - 20480 */ + 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 20480 - 20496 */ + 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 20496 - 20512 */ + 0xff, 0x00, 0x00, 0x1d, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, /* bytes 20512 - 20528 */ + 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, /* bytes 20528 - 20544 */ + 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 20544 - 20560 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 20560 - 20576 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, /* bytes 20576 - 20592 */ + 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, /* bytes 20592 - 20608 */ + 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, /* bytes 20608 - 20624 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 20624 - 20640 */ + 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 20640 - 20656 */ + 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, /* bytes 20656 - 20672 */ + 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, /* bytes 20672 - 20688 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, /* bytes 20688 - 20704 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, /* bytes 20704 - 20720 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, /* bytes 20720 - 20736 */ + 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 20736 - 20752 */ + 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, /* bytes 20752 - 20768 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, /* bytes 20768 - 20784 */ + 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 20784 - 20800 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, /* bytes 20800 - 20816 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 20816 - 20832 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 20832 - 20848 */ + 0x00, 0x1d, 0x00, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, /* bytes 20848 - 20864 */ + 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, /* bytes 20864 - 20880 */ + 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, /* bytes 20880 - 20896 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 20896 - 20912 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 20912 - 20928 */ + 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, /* bytes 20928 - 20944 */ + 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, /* bytes 20944 - 20960 */ + 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 20960 - 20976 */ + 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 20976 - 20992 */ + 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, /* bytes 20992 - 21008 */ + 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 21008 - 21024 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, /* bytes 21024 - 21040 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, /* bytes 21040 - 21056 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, /* bytes 21056 - 21072 */ + 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 21072 - 21088 */ + 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 21088 - 21104 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, /* bytes 21104 - 21120 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 21120 - 21136 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, /* bytes 21136 - 21152 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, /* bytes 21152 - 21168 */ + 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 21168 - 21184 */ + 0x00, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, /* bytes 21184 - 21200 */ + 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, /* bytes 21200 - 21216 */ + 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, /* bytes 21216 - 21232 */ + 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, /* bytes 21232 - 21248 */ + 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, /* bytes 21248 - 21264 */ + 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, /* bytes 21264 - 21280 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 21280 - 21296 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 21296 - 21312 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, /* bytes 21312 - 21328 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, /* bytes 21328 - 21344 */ + 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 21344 - 21360 */ + 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 21360 - 21376 */ + 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 21376 - 21392 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, /* bytes 21392 - 21408 */ + 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, /* bytes 21408 - 21424 */ + 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 21424 - 21440 */ + 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, /* bytes 21440 - 21456 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, /* bytes 21456 - 21472 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, /* bytes 21472 - 21488 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 21488 - 21504 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x0b, /* bytes 21504 - 21520 */ + 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, /* bytes 21520 - 21536 */ + 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, /* bytes 21536 - 21552 */ + 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, /* bytes 21552 - 21568 */ + 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, /* bytes 21568 - 21584 */ + 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 21584 - 21600 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 21600 - 21616 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, /* bytes 21616 - 21632 */ + 0x01, 0x6c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 21632 - 21648 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, /* bytes 21648 - 21664 */ + 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, /* bytes 21664 - 21680 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 21680 - 21696 */ + 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 21696 - 21712 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 21712 - 21728 */ + 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, /* bytes 21728 - 21744 */ + 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, /* bytes 21744 - 21760 */ + 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 21760 - 21776 */ + 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, /* bytes 21776 - 21792 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 21792 - 21808 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, /* bytes 21808 - 21824 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, /* bytes 21824 - 21840 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x0b, 0x01, 0x6c, /* bytes 21840 - 21856 */ + 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, /* bytes 21856 - 21872 */ + 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, /* bytes 21872 - 21888 */ + 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, /* bytes 21888 - 21904 */ + 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, /* bytes 21904 - 21920 */ + 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 21920 - 21936 */ + 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 21936 - 21952 */ + 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, /* bytes 21952 - 21968 */ + 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, /* bytes 21968 - 21984 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 21984 - 22000 */ + 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 22000 - 22016 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 22016 - 22032 */ + 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 22032 - 22048 */ + 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 22048 - 22064 */ + 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, /* bytes 22064 - 22080 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x3c, 0x02, /* bytes 22080 - 22096 */ + 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, /* bytes 22096 - 22112 */ + 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 22112 - 22128 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 22128 - 22144 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, /* bytes 22144 - 22160 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 22160 - 22176 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 22176 - 22192 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 22192 - 22208 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, /* bytes 22208 - 22224 */ + 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, /* bytes 22224 - 22240 */ + 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, /* bytes 22240 - 22256 */ + 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, /* bytes 22256 - 22272 */ + 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, /* bytes 22272 - 22288 */ + 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x03, /* bytes 22288 - 22304 */ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, /* bytes 22304 - 22320 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 22320 - 22336 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 22336 - 22352 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, /* bytes 22352 - 22368 */ + 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 22368 - 22384 */ + 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, /* bytes 22384 - 22400 */ + 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, /* bytes 22400 - 22416 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, /* bytes 22416 - 22432 */ + 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, /* bytes 22432 - 22448 */ + 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 22448 - 22464 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 22464 - 22480 */ + 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 22480 - 22496 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 22496 - 22512 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 22512 - 22528 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, /* bytes 22528 - 22544 */ + 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, /* bytes 22544 - 22560 */ + 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, /* bytes 22560 - 22576 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, /* bytes 22576 - 22592 */ + 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, /* bytes 22592 - 22608 */ + 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 22608 - 22624 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0x00, /* bytes 22624 - 22640 */ + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, /* bytes 22640 - 22656 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, /* bytes 22656 - 22672 */ + 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, /* bytes 22672 - 22688 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, /* bytes 22688 - 22704 */ + 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, /* bytes 22704 - 22720 */ + 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, /* bytes 22720 - 22736 */ + 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, /* bytes 22736 - 22752 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, /* bytes 22752 - 22768 */ + 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, /* bytes 22768 - 22784 */ + 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 22784 - 22800 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 22800 - 22816 */ + 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 22816 - 22832 */ + 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 22832 - 22848 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xd2, 0x00, 0x35, 0x01, 0x87, 0x00, 0x5c, 0x02, 0xae, /* bytes 22848 - 22864 */ + 0x00, 0x5c, 0x02, 0xf4, 0x00, 0x7e, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x27, /* bytes 22864 - 22880 */ + 0x01, 0x6c, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 22880 - 22896 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x04, /* bytes 22896 - 22912 */ + 0x02, 0x9c, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x3c, 0x02, 0x14, 0x01, 0x3c, 0x02, 0xe1, 0x00, 0x7c, /* bytes 22912 - 22928 */ + 0x02, 0xae, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x56, 0x02, 0x44, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x0c, /* bytes 22928 - 22944 */ + 0x02, 0x68, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xa4, /* bytes 22944 - 22960 */ + 0x00, 0xcc, 0x00, 0xa4, 0x00, 0x0b, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, /* bytes 22960 - 22976 */ + 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 22976 - 22992 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, /* bytes 22992 - 23008 */ + 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 23008 - 23024 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, /* bytes 23024 - 23040 */ + 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, /* bytes 23040 - 23056 */ + 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, /* bytes 23056 - 23072 */ + 0x00, 0x0e, 0x01, 0x0a, 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 23072 - 23088 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, /* bytes 23088 - 23104 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, /* bytes 23104 - 23120 */ + 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 23120 - 23136 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, /* bytes 23136 - 23152 */ + 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 23152 - 23168 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 23168 - 23184 */ + 0xff, 0x00, 0x00, 0x1d, 0x00, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, /* bytes 23184 - 23200 */ + 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, /* bytes 23200 - 23216 */ + 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, /* bytes 23216 - 23232 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 23232 - 23248 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, /* bytes 23248 - 23264 */ + 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, /* bytes 23264 - 23280 */ + 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, /* bytes 23280 - 23296 */ + 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* bytes 23296 - 23312 */ + 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 23312 - 23328 */ + 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, /* bytes 23328 - 23344 */ + 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, /* bytes 23344 - 23360 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, /* bytes 23360 - 23376 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, /* bytes 23376 - 23392 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, /* bytes 23392 - 23408 */ + 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 23408 - 23424 */ + 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, 0x01, 0x03, 0x00, /* bytes 23424 - 23440 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, /* bytes 23440 - 23456 */ + 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 23456 - 23472 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, /* bytes 23472 - 23488 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, /* bytes 23488 - 23504 */ + 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 23504 - 23520 */ + 0x00, 0x1d, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, /* bytes 23520 - 23536 */ + 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, /* bytes 23536 - 23552 */ + 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, /* bytes 23552 - 23568 */ + 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, /* bytes 23568 - 23584 */ + 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, /* bytes 23584 - 23600 */ + 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 23600 - 23616 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 23616 - 23632 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 23632 - 23648 */ + 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 23648 - 23664 */ + 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, /* bytes 23664 - 23680 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 23680 - 23696 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, /* bytes 23696 - 23712 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, /* bytes 23712 - 23728 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd2, 0x00, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0x0a, /* bytes 23728 - 23744 */ + 0x01, 0x35, 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 23744 - 23760 */ + 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 23760 - 23776 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0xf6, 0x00, 0xcc, 0x00, 0x4c, 0x01, 0x26, /* bytes 23776 - 23792 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, /* bytes 23792 - 23808 */ + 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, /* bytes 23808 - 23824 */ + 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 23824 - 23840 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 23840 - 23856 */ + 0x00, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, /* bytes 23856 - 23872 */ + 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, /* bytes 23872 - 23888 */ + 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, /* bytes 23888 - 23904 */ + 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, /* bytes 23904 - 23920 */ + 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 23920 - 23936 */ + 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 23936 - 23952 */ + 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, /* bytes 23952 - 23968 */ + 0x01, 0x00, 0x01, 0x6c, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 23968 - 23984 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, /* bytes 23984 - 24000 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, /* bytes 24000 - 24016 */ + 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 24016 - 24032 */ + 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 24032 - 24048 */ + 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 24048 - 24064 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, /* bytes 24064 - 24080 */ + 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x02, /* bytes 24080 - 24096 */ + 0x68, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 24096 - 24112 */ + 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, /* bytes 24112 - 24128 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 24128 - 24144 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 24144 - 24160 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 24160 - 24176 */ + 0x5c, 0x02, +}; +__attribute__ ((aligned (8))) +static const uint8_t RESULT_SENT_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x99, 0x12, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x24, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, /* bytes 16 - 32 */ + 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, /* bytes 32 - 48 */ + 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 48 - 64 */ + 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, /* bytes 64 - 80 */ + 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, /* bytes 80 - 96 */ + 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, /* bytes 96 - 112 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, /* bytes 112 - 128 */ + 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, /* bytes 128 - 144 */ + 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 144 - 160 */ + 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, /* bytes 160 - 176 */ + 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, /* bytes 176 - 192 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, /* bytes 192 - 208 */ + 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 208 - 224 */ + 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, /* bytes 224 - 240 */ + 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, /* bytes 240 - 256 */ + 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 256 - 272 */ + 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, /* bytes 272 - 288 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, /* bytes 288 - 304 */ + 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, /* bytes 304 - 320 */ + 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, /* bytes 320 - 336 */ + 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, /* bytes 336 - 352 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, /* bytes 352 - 368 */ + 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 368 - 384 */ + 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, /* bytes 384 - 400 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, /* bytes 400 - 416 */ + 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, /* bytes 416 - 432 */ + 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, /* bytes 432 - 448 */ + 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 448 - 464 */ + 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, /* bytes 464 - 480 */ + 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, /* bytes 480 - 496 */ + 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 496 - 512 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, /* bytes 512 - 528 */ + 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, /* bytes 528 - 544 */ + 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 544 - 560 */ + 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, /* bytes 560 - 576 */ + 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, /* bytes 576 - 592 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, /* bytes 592 - 608 */ + 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 608 - 624 */ + 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, /* bytes 624 - 640 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, /* bytes 640 - 656 */ + 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 656 - 672 */ + 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, /* bytes 672 - 688 */ + 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, /* bytes 688 - 704 */ + 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, /* bytes 704 - 720 */ + 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, /* bytes 720 - 736 */ + 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, /* bytes 736 - 752 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, /* bytes 752 - 768 */ + 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, /* bytes 768 - 784 */ + 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, /* bytes 784 - 800 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, /* bytes 800 - 816 */ + 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, /* bytes 816 - 832 */ + 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, /* bytes 832 - 848 */ + 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 848 - 864 */ + 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, /* bytes 864 - 880 */ + 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, /* bytes 880 - 896 */ + 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 896 - 912 */ + 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, /* bytes 912 - 928 */ + 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, /* bytes 928 - 944 */ + 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 944 - 960 */ + 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, /* bytes 960 - 976 */ + 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, /* bytes 976 - 992 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, /* bytes 992 - 1008 */ + 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 1008 - 1024 */ + 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, /* bytes 1024 - 1040 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, /* bytes 1040 - 1056 */ + 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1056 - 1072 */ + 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, /* bytes 1072 - 1088 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, /* bytes 1088 - 1104 */ + 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, /* bytes 1104 - 1120 */ + 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, /* bytes 1120 - 1136 */ + 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 1136 - 1152 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, /* bytes 1152 - 1168 */ + 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, /* bytes 1168 - 1184 */ + 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, /* bytes 1184 - 1200 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, /* bytes 1200 - 1216 */ + 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, /* bytes 1216 - 1232 */ + 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, /* bytes 1232 - 1248 */ + 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 1248 - 1264 */ + 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, /* bytes 1264 - 1280 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, /* bytes 1280 - 1296 */ + 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1296 - 1312 */ + 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, /* bytes 1312 - 1328 */ + 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, /* bytes 1328 - 1344 */ + 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, /* bytes 1344 - 1360 */ + 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, /* bytes 1360 - 1376 */ + 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, /* bytes 1376 - 1392 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, /* bytes 1392 - 1408 */ + 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, /* bytes 1408 - 1424 */ + 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, /* bytes 1424 - 1440 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, /* bytes 1440 - 1456 */ + 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 1456 - 1472 */ + 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, /* bytes 1472 - 1488 */ + 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, /* bytes 1488 - 1504 */ + 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, /* bytes 1504 - 1520 */ + 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, /* bytes 1520 - 1536 */ + 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1536 - 1552 */ + 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, /* bytes 1552 - 1568 */ + 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, /* bytes 1568 - 1584 */ + 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, /* bytes 1584 - 1600 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, /* bytes 1600 - 1616 */ + 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, /* bytes 1616 - 1632 */ + 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, /* bytes 1632 - 1648 */ + 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 1648 - 1664 */ + 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, /* bytes 1664 - 1680 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, /* bytes 1680 - 1696 */ + 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1696 - 1712 */ + 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, /* bytes 1712 - 1728 */ + 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, /* bytes 1728 - 1744 */ + 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, /* bytes 1744 - 1760 */ + 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x6c, 0x01, 0xa4, 0x01, /* bytes 1760 - 1776 */ + 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 1776 - 1792 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, 0x90, 0x01, 0x44, 0x00, 0xa0, /* bytes 1792 - 1808 */ + 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb0, 0x00, /* bytes 1808 - 1824 */ + 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, 0x00, 0xe8, 0x01, 0x03, 0x00, /* bytes 1824 - 1840 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, 0x2c, 0x01, 0x74, 0x01, 0xac, /* bytes 1840 - 1856 */ + 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1856 - 1872 */ + 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 1872 - 1888 */ + 0xff, 0x00, 0x00, 0x07, 0x00, 0x2c, 0x00, 0x1c, 0x01, 0xd4, 0x00, 0x54, 0x01, 0x04, 0x01, 0xfc, /* bytes 1888 - 1904 */ + 0x01, 0x6c, 0x01, 0xa4, 0x01, 0xac, 0x01, 0xd4, 0x01, 0x64, 0x02, 0x7c, 0x00, 0x2c, 0x00, 0x1c, /* bytes 1904 - 1920 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x90, 0x00, 0x80, 0x01, 0x6a, 0x00, /* bytes 1920 - 1936 */ + 0x90, 0x01, 0x44, 0x00, 0xa0, 0x01, 0x30, 0x00, 0xa8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1936 - 1952 */ + 0x00, 0x04, 0x00, 0xb0, 0x00, 0xc0, 0x01, 0xa6, 0x00, 0xc8, 0x01, 0x92, 0x00, 0xda, 0x01, 0x80, /* bytes 1952 - 1968 */ + 0x00, 0xe8, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6c, 0x01, 0xa4, 0x01, /* bytes 1968 - 1984 */ + 0x2c, 0x01, 0x74, 0x01, 0xac, 0x01, 0xfc, 0x00, 0xd4, 0x00, 0x54, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 1984 - 2000 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x2c, 0x01, 0x74, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x23, 0x00, 0x05, /* bytes 2000 - 2016 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x2d, 0x00, 0x22, 0x01, 0xcf, 0x00, /* bytes 2016 - 2032 */ + 0x5d, 0x01, 0xfa, 0x00, 0x06, 0x02, 0x62, 0x01, 0xb0, 0x01, 0xa0, 0x01, 0xe2, 0x01, 0x6b, 0x02, /* bytes 2032 - 2048 */ + 0x90, 0x00, 0x2d, 0x00, 0x22, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xfa, /* bytes 2048 - 2064 */ + 0x00, 0x06, 0x02, 0x24, 0x01, 0x7f, 0x01, 0xae, 0x01, 0x0b, 0x01, 0xcf, 0x00, 0x5d, 0x01, 0x03, /* bytes 2064 - 2080 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x75, 0x00, 0xec, 0x01, 0x87, 0x00, 0xde, 0x01, /* bytes 2080 - 2096 */ + 0x9b, 0x00, 0xcc, 0x01, 0xa5, 0x00, 0xc4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 2096 - 2112 */ + 0x00, 0x2c, 0x00, 0xac, 0x01, 0x3f, 0x00, 0xa5, 0x01, 0x65, 0x00, 0x96, 0x01, 0x87, 0x00, 0x88, /* bytes 2112 - 2128 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x62, 0x01, 0xb0, 0x01, 0x24, 0x01, /* bytes 2128 - 2144 */ + 0x7f, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x84, /* bytes 2144 - 2160 */ + 0x02, 0xb8, 0x00, 0x7b, 0x01, 0x00, 0x02, 0x45, 0x01, 0xca, 0x01, 0xdd, 0x00, 0x1b, 0x02, 0xbf, /* bytes 2160 - 2176 */ + 0x00, 0x6f, 0x01, 0x2e, 0x00, 0x2c, 0x01, 0x84, 0x02, 0xb8, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2176 - 2192 */ + 0x01, 0x00, 0x04, 0x00, 0xdd, 0x00, 0x1b, 0x02, 0x0d, 0x01, 0x95, 0x01, 0xb5, 0x01, 0x29, 0x01, /* bytes 2192 - 2208 */ + 0xbf, 0x00, 0x6f, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x85, 0x00, 0xd2, /* bytes 2208 - 2224 */ + 0x01, 0x7b, 0x00, 0xda, 0x01, 0x67, 0x00, 0xec, 0x01, 0x55, 0x00, 0xfa, 0x01, 0x03, 0x00, 0xc0, /* bytes 2224 - 2240 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6e, 0x00, 0x9c, 0x01, 0x56, 0x00, 0xa7, 0x01, 0x30, 0x00, /* bytes 2240 - 2256 */ + 0xb4, 0x01, 0x1f, 0x00, 0xb9, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x45, /* bytes 2256 - 2272 */ + 0x01, 0xca, 0x01, 0x0d, 0x01, 0x95, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, /* bytes 2272 - 2288 */ + 0x00, 0x00, 0x07, 0x00, 0x20, 0x00, 0x32, 0x01, 0xa8, 0x00, 0x79, 0x01, 0xbf, 0x00, 0x27, 0x02, /* bytes 2288 - 2304 */ + 0x26, 0x01, 0xd9, 0x01, 0x58, 0x01, 0x11, 0x02, 0x7a, 0x02, 0xd3, 0x00, 0x20, 0x00, 0x32, 0x01, /* bytes 2304 - 2320 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xa8, 0x00, 0x79, 0x01, 0xa7, 0x01, 0x3c, /* bytes 2320 - 2336 */ + 0x01, 0xf1, 0x00, 0xa1, 0x01, 0xbf, 0x00, 0x27, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2336 - 2352 */ + 0x04, 0x00, 0x62, 0x00, 0xa7, 0x01, 0x4e, 0x00, 0xb0, 0x01, 0x28, 0x00, 0xbc, 0x01, 0x17, 0x00, /* bytes 2352 - 2368 */ + 0xc1, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x74, 0x00, 0xd9, 0x01, 0x6a, /* bytes 2368 - 2384 */ + 0x00, 0xe1, 0x01, 0x56, 0x00, 0xf3, 0x01, 0x44, 0x00, 0x01, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2384 - 2400 */ + 0x01, 0x00, 0x02, 0x00, 0xf1, 0x00, 0xa1, 0x01, 0x26, 0x01, 0xd9, 0x01, 0x23, 0x00, 0x05, 0x00, /* bytes 2400 - 2416 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0xac, 0x00, 0x2c, 0x02, 0x12, 0x01, 0xdf, /* bytes 2416 - 2432 */ + 0x01, 0x43, 0x01, 0x19, 0x02, 0x64, 0x02, 0xe5, 0x00, 0x14, 0x00, 0x35, 0x01, 0x98, 0x00, 0x7d, /* bytes 2432 - 2448 */ + 0x01, 0xac, 0x00, 0x2c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x6e, 0x00, /* bytes 2448 - 2464 */ + 0xdb, 0x01, 0x64, 0x00, 0xe3, 0x01, 0x50, 0x00, 0xf5, 0x01, 0x3e, 0x00, 0x03, 0x02, 0x03, 0x00, /* bytes 2464 - 2480 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xac, 0x00, 0x2c, 0x02, 0xdf, 0x00, 0xa7, 0x01, 0x96, /* bytes 2480 - 2496 */ + 0x01, 0x47, 0x01, 0x98, 0x00, 0x7d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 2496 - 2512 */ + 0x5d, 0x00, 0xab, 0x01, 0x4b, 0x00, 0xb3, 0x01, 0x25, 0x00, 0xbf, 0x01, 0x15, 0x00, 0xc3, 0x01, /* bytes 2512 - 2528 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xdf, 0x00, 0xa7, 0x01, 0x12, 0x01, 0xdf, /* bytes 2528 - 2544 */ + 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, /* bytes 2544 - 2560 */ + 0x35, 0x01, 0x93, 0x00, 0x7e, 0x01, 0xa5, 0x00, 0x2e, 0x02, 0x0c, 0x01, 0xe1, 0x01, 0x3c, 0x01, /* bytes 2560 - 2576 */ + 0x1b, 0x02, 0x4b, 0x02, 0xf4, 0x00, 0x10, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2576 - 2592 */ + 0x00, 0x04, 0x00, 0x6c, 0x00, 0xdc, 0x01, 0x62, 0x00, 0xe4, 0x01, 0x4e, 0x00, 0xf6, 0x01, 0x3c, /* bytes 2592 - 2608 */ + 0x00, 0x04, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x14, 0x00, 0xc4, 0x01, /* bytes 2608 - 2624 */ + 0x24, 0x00, 0xc0, 0x01, 0x4a, 0x00, 0xb4, 0x01, 0x5c, 0x00, 0xac, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 2624 - 2640 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x93, 0x00, 0x7e, 0x01, 0x89, 0x01, 0x4e, 0x01, 0xd9, 0x00, 0xa8, /* bytes 2640 - 2656 */ + 0x01, 0xa5, 0x00, 0x2e, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd9, 0x00, /* bytes 2656 - 2672 */ + 0xa8, 0x01, 0x0c, 0x01, 0xe1, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 2672 - 2688 */ + 0x00, 0x07, 0x00, 0x07, 0x03, 0xdd, 0x00, 0xcc, 0x02, 0xf0, 0x01, 0x61, 0x02, 0xb7, 0x01, 0x0e, /* bytes 2688 - 2704 */ + 0x02, 0xf5, 0x01, 0xf9, 0x01, 0x7f, 0x01, 0x75, 0x01, 0x55, 0x01, 0x07, 0x03, 0xdd, 0x00, 0x03, /* bytes 2704 - 2720 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7b, 0x01, 0xda, 0x01, 0x3b, 0x01, 0xe4, 0x01, /* bytes 2720 - 2736 */ + 0xd8, 0x00, 0xf7, 0x01, 0x9e, 0x00, 0x02, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 2736 - 2752 */ + 0x00, 0x57, 0x00, 0xbf, 0x01, 0x8e, 0x00, 0xbb, 0x01, 0xdf, 0x00, 0xb1, 0x01, 0x10, 0x01, 0xa8, /* bytes 2752 - 2768 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xf9, 0x01, 0x7f, 0x01, 0x95, 0x02, /* bytes 2768 - 2784 */ + 0x46, 0x01, 0x57, 0x02, 0xb0, 0x01, 0x61, 0x02, 0xb7, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2784 - 2800 */ + 0x00, 0x02, 0x00, 0x8f, 0x02, 0x52, 0x01, 0x0e, 0x02, 0xf5, 0x01, 0x23, 0x00, 0x05, 0x00, 0x03, /* bytes 2800 - 2816 */ + 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x07, 0x00, 0xc5, 0x02, 0x50, 0x01, 0x43, 0x03, 0x70, 0x01, /* bytes 2816 - 2832 */ + 0xc5, 0x02, 0x90, 0x00, 0xc8, 0x01, 0x24, 0x01, 0x58, 0x02, 0x32, 0x01, 0x81, 0x02, 0x96, 0x01, /* bytes 2832 - 2848 */ + 0xc5, 0x02, 0x50, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x9a, 0x00, 0xba, /* bytes 2848 - 2864 */ + 0x01, 0xf9, 0x00, 0xb6, 0x01, 0x73, 0x01, 0xaf, 0x01, 0xc4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, /* bytes 2864 - 2880 */ + 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x01, 0xf0, 0x01, 0x61, 0x01, 0xe9, 0x01, 0x13, 0x02, /* bytes 2880 - 2896 */ + 0xd3, 0x01, 0x3b, 0x02, 0xcb, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0xc5, /* bytes 2896 - 2912 */ + 0x02, 0x50, 0x01, 0xa3, 0x02, 0xf3, 0x00, 0x58, 0x02, 0x32, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, /* bytes 2912 - 2928 */ + 0x01, 0x00, 0x02, 0x00, 0x81, 0x02, 0x96, 0x01, 0xa3, 0x02, 0xf3, 0x00, 0x23, 0x00, 0x05, 0x00, /* bytes 2928 - 2944 */ + 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x07, 0x00, 0xc7, 0x02, 0xee, 0x00, 0x8d, 0x02, 0x4a, /* bytes 2944 - 2960 */ + 0x01, 0x56, 0x02, 0x00, 0x01, 0xea, 0x01, 0x12, 0x01, 0x5c, 0x02, 0x4a, 0x00, 0x60, 0x03, 0xee, /* bytes 2960 - 2976 */ + 0x00, 0xc7, 0x02, 0xee, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x17, 0x02, /* bytes 2976 - 2992 */ + 0x89, 0x01, 0xb7, 0x01, 0xa0, 0x01, 0x27, 0x01, 0xb3, 0x01, 0xc2, 0x00, 0xb8, 0x01, 0x03, 0x00, /* bytes 2992 - 3008 */ + 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xc7, 0x02, 0xee, 0x00, 0x84, 0x02, 0xad, 0x00, 0x6a, /* bytes 3008 - 3024 */ + 0x02, 0xf8, 0x00, 0x56, 0x02, 0x00, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 3024 - 3040 */ + 0xc0, 0x02, 0x89, 0x01, 0x93, 0x02, 0xaa, 0x01, 0x36, 0x02, 0xcb, 0x01, 0x9c, 0x01, 0xe9, 0x01, /* bytes 3040 - 3056 */ + 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8d, 0x02, 0x4a, 0x01, 0x79, 0x02, 0xcc, /* bytes 3056 - 3072 */ + 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, 0x00, 0x07, 0x00, 0x53, 0x02, /* bytes 3072 - 3088 */ + 0xe8, 0x00, 0xec, 0x01, 0xfb, 0x00, 0x21, 0x02, 0x35, 0x00, 0x67, 0x03, 0xce, 0x00, 0xca, 0x02, /* bytes 3088 - 3104 */ + 0xd4, 0x00, 0x8e, 0x02, 0x22, 0x01, 0x53, 0x02, 0xe8, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3104 - 3120 */ + 0x00, 0x04, 0x00, 0x53, 0x02, 0xe8, 0x00, 0x7d, 0x02, 0xd8, 0x00, 0x72, 0x02, 0x93, 0x00, 0xca, /* bytes 3120 - 3136 */ + 0x02, 0xd4, 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xe2, 0x02, 0x4e, 0x01, /* bytes 3136 - 3152 */ + 0xd5, 0x02, 0x70, 0x01, 0x88, 0x02, 0xa8, 0x01, 0xe0, 0x01, 0xdb, 0x01, 0x03, 0x00, 0xc0, 0x03, /* bytes 3152 - 3168 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x1e, 0x01, 0xaa, 0x01, 0xb7, 0x01, 0x93, 0x01, 0x24, 0x02, 0x7e, /* bytes 3168 - 3184 */ + 0x01, 0x6f, 0x02, 0x65, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x8e, 0x02, /* bytes 3184 - 3200 */ + 0x22, 0x01, 0x7d, 0x02, 0xd8, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x03, 0xff, 0x00, /* bytes 3200 - 3216 */ + 0x00, 0x07, 0x00, 0x83, 0x02, 0x90, 0x00, 0x64, 0x02, 0xe3, 0x00, 0x28, 0x02, 0xaf, 0x00, 0xbb, /* bytes 3216 - 3232 */ + 0x01, 0xd0, 0x00, 0x86, 0x01, 0x07, 0x00, 0x09, 0x03, 0x7b, 0x00, 0x83, 0x02, 0x90, 0x00, 0x03, /* bytes 3232 - 3248 */ + 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x9c, 0x02, 0x39, 0x01, 0x54, 0x02, 0x62, 0x01, /* bytes 3248 - 3264 */ + 0xe3, 0x01, 0x88, 0x01, 0x54, 0x01, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, /* bytes 3264 - 3280 */ + 0x00, 0x83, 0x02, 0x90, 0x00, 0x00, 0x02, 0x5c, 0x00, 0x3f, 0x02, 0x9d, 0x00, 0x64, 0x02, 0xe3, /* bytes 3280 - 3296 */ + 0x00, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0xe2, 0x02, 0x23, 0x01, 0xd8, 0x02, /* bytes 3296 - 3312 */ + 0x5c, 0x01, 0xa4, 0x02, 0x8d, 0x01, 0x42, 0x02, 0xc0, 0x01, 0x03, 0x00, 0xc0, 0x03, 0x00, 0x01, /* bytes 3312 - 3328 */ + 0x00, 0x02, 0x00, 0x3f, 0x02, 0x9d, 0x00, 0x28, 0x02, 0xaf, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, /* bytes 3328 - 3344 */ + 0x00, 0xc0, 0x02, 0xff, 0x00, 0x00, 0x07, 0x00, 0xf9, 0x00, 0xdb, 0xff, 0x41, 0x01, 0xa0, 0x00, /* bytes 3344 - 3360 */ + 0xa9, 0x01, 0x75, 0x00, 0xc0, 0x01, 0xa1, 0x00, 0xec, 0x01, 0x54, 0x00, 0x5d, 0x02, 0x33, 0x00, /* bytes 3360 - 3376 */ + 0xf9, 0x00, 0xdb, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x8b, 0x02, 0xf6, /* bytes 3376 - 3392 */ + 0x00, 0x93, 0x02, 0x19, 0x01, 0x85, 0x02, 0x37, 0x01, 0x50, 0x02, 0x6c, 0x01, 0x03, 0x00, 0xc0, /* bytes 3392 - 3408 */ + 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0xec, 0x01, 0x54, 0x00, 0x73, 0x01, 0x26, 0x00, 0xb2, 0x01, /* bytes 3408 - 3424 */ + 0x66, 0x00, 0xa9, 0x01, 0x75, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0xa9, /* bytes 3424 - 3440 */ + 0x02, 0xcb, 0x00, 0xc3, 0x02, 0xf4, 0x00, 0xd0, 0x02, 0x30, 0x01, 0xb0, 0x02, 0x75, 0x01, 0x03, /* bytes 3440 - 3456 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb2, 0x01, 0x66, 0x00, 0xc0, 0x01, 0xa1, 0x00, /* bytes 3456 - 3472 */ + 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x02, 0xff, 0x00, 0x00, 0x07, 0x00, 0x9f, 0x00, 0x4e, /* bytes 3472 - 3488 */ + 0x00, 0xba, 0x00, 0xb8, 0xff, 0x9e, 0x01, 0x18, 0x00, 0x30, 0x01, 0x28, 0x00, 0x1c, 0x01, 0x5b, /* bytes 3488 - 3504 */ + 0x00, 0xfb, 0x00, 0x39, 0x00, 0x9f, 0x00, 0x4e, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 3504 - 3520 */ + 0x04, 0x00, 0x1c, 0x01, 0x5b, 0x00, 0xfa, 0x00, 0x30, 0x00, 0xf7, 0x00, 0xfc, 0xff, 0x30, 0x01, /* bytes 3520 - 3536 */ + 0x28, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0xcb, 0x02, 0x43, 0x01, 0xb5, /* bytes 3536 - 3552 */ + 0x02, 0xfe, 0x00, 0x67, 0x02, 0xb1, 0x00, 0x04, 0x02, 0x83, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 3552 - 3568 */ + 0x01, 0x00, 0x04, 0x00, 0xc3, 0x01, 0xaf, 0x00, 0x07, 0x02, 0xc7, 0x00, 0x3e, 0x02, 0xf1, 0x00, /* bytes 3568 - 3584 */ + 0x67, 0x02, 0x34, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfa, 0x00, 0x30, /* bytes 3584 - 3600 */ + 0x00, 0xfb, 0x00, 0x39, 0x00, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x02, 0xff, 0x00, 0x00, /* bytes 3600 - 3616 */ + 0x07, 0x00, 0xab, 0x00, 0xa0, 0xff, 0x56, 0x00, 0x0b, 0x00, 0xa1, 0x00, 0x05, 0x00, 0xbb, 0x00, /* bytes 3616 - 3632 */ + 0x23, 0x00, 0xd4, 0x00, 0xfd, 0xff, 0x25, 0x01, 0xfc, 0xff, 0xab, 0x00, 0xa0, 0xff, 0x03, 0x00, /* bytes 3632 - 3648 */ + 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x5e, 0x01, 0x5a, 0x00, 0xa7, 0x01, 0x79, 0x00, 0xf0, /* bytes 3648 - 3664 */ + 0x01, 0x9e, 0x00, 0x3c, 0x02, 0xd7, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 3664 - 3680 */ + 0xd8, 0x01, 0xe5, 0x00, 0xa9, 0x01, 0xc3, 0x00, 0x3c, 0x01, 0x87, 0x00, 0xf3, 0x00, 0x6e, 0x00, /* bytes 3680 - 3696 */ + 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x99, 0x00, 0xfe, 0xff, 0xbb, 0x00, 0xd9, /* bytes 3696 - 3712 */ + 0xff, 0xd4, 0x00, 0xfd, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9a, 0x0c, /* bytes 3712 - 3728 */ + 0x29, 0xfd, 0x9a, 0x0c, 0x29, 0xfd, 0x23, 0x00, 0x05, 0x00, 0x03, 0x00, 0xc0, 0x02, 0xff, 0x00, /* bytes 3728 - 3744 */ + 0x00, 0x07, 0x00, 0xed, 0x00, 0xea, 0xff, 0xdc, 0x00, 0x8f, 0xff, 0x49, 0x00, 0xca, 0xff, 0x8e, /* bytes 3744 - 3760 */ + 0x00, 0xd3, 0xff, 0xa7, 0x00, 0xf6, 0xff, 0xc2, 0x00, 0xe0, 0xff, 0xed, 0x00, 0xea, 0xff, 0x03, /* bytes 3760 - 3776 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x8e, 0x00, 0xd3, 0xff, 0xb6, 0x00, 0xba, 0xff, /* bytes 3776 - 3792 */ + 0xb8, 0x00, 0xd9, 0xff, 0xc2, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, /* bytes 3792 - 3808 */ + 0x00, 0xff, 0x00, 0x9b, 0x00, 0xe0, 0x00, 0x86, 0x00, 0xba, 0x00, 0x5c, 0x00, 0xa5, 0x00, 0x32, /* bytes 3808 - 3824 */ + 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0xda, 0x00, 0x36, 0x00, 0xf1, 0x00, /* bytes 3824 - 3840 */ + 0x58, 0x00, 0x14, 0x01, 0x76, 0x00, 0x4e, 0x01, 0x97, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 3840 - 3856 */ + 0x00, 0x02, 0x00, 0x3e, 0xf8, 0x54, 0xfc, 0x3d, 0xf8, 0x54, 0xfc, 0x23, 0x00, 0x06, 0x00, 0x03, /* bytes 3856 - 3872 */ + 0x00, 0xc0, 0x02, 0xff, 0x00, 0x00, 0x07, 0x00, 0xc2, 0x00, 0xae, 0xff, 0xd8, 0x00, 0xcf, 0xff, /* bytes 3872 - 3888 */ + 0xf3, 0x00, 0xbf, 0xff, 0x07, 0x01, 0xc9, 0xff, 0x20, 0x01, 0x7d, 0xff, 0x89, 0x00, 0xa1, 0xff, /* bytes 3888 - 3904 */ + 0xc2, 0x00, 0xae, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0xba, 0x00, 0x06, /* bytes 3904 - 3920 */ + 0x00, 0xc2, 0x00, 0x1e, 0x00, 0xd3, 0x00, 0x3a, 0x00, 0xec, 0x00, 0x50, 0x00, 0x03, 0x00, 0xc0, /* bytes 3920 - 3936 */ + 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x8d, 0x00, 0xf6, 0xff, 0x8c, 0x00, 0x13, 0x00, 0x99, 0x00, /* bytes 3936 - 3952 */ + 0x33, 0x00, 0xae, 0x00, 0x55, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0xfe, /* bytes 3952 - 3968 */ + 0xfb, 0x80, 0xfc, 0xfe, 0xfb, 0x80, 0xfc, 0xfe, 0xfb, 0x80, 0xfc, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 3968 - 3984 */ + 0x01, 0x00, 0x02, 0x00, 0xc2, 0x00, 0xae, 0xff, 0xef, 0x00, 0x9d, 0xff, 0x03, 0x00, 0xc0, 0x02, /* bytes 3984 - 4000 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xde, 0x00, 0xb6, 0xff, 0xf3, 0x00, 0xbf, 0xff, 0x23, 0x00, 0x06, /* bytes 4000 - 4016 */ + 0x00, 0x03, 0x00, 0xc0, 0x01, 0xff, 0x00, 0x00, 0x07, 0x00, 0x49, 0x01, 0xaa, 0xff, 0x84, 0x01, /* bytes 4016 - 4032 */ + 0x6c, 0xff, 0xec, 0x00, 0x7d, 0xff, 0x17, 0x01, 0x8d, 0xff, 0x23, 0x01, 0xac, 0xff, 0x3b, 0x01, /* bytes 4032 - 4048 */ + 0xa0, 0xff, 0x49, 0x01, 0xaa, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x0d, /* bytes 4048 - 4064 */ + 0x01, 0xb5, 0xff, 0xf0, 0x00, 0xcb, 0xff, 0xdd, 0x00, 0xf1, 0xff, 0xd9, 0x00, 0x18, 0x00, 0x03, /* bytes 4064 - 4080 */ + 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0xa9, 0x00, 0xe2, 0xff, 0xc7, 0x00, 0xb9, 0xff, /* bytes 4080 - 4096 */ + 0xe0, 0x00, 0xa9, 0xff, 0x01, 0x01, 0x9e, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x03, /* bytes 4096 - 4112 */ + 0x00, 0x3b, 0x01, 0xa0, 0xff, 0x2c, 0x01, 0x96, 0xff, 0x4a, 0x01, 0x83, 0xff, 0x03, 0x00, 0xc0, /* bytes 4112 - 4128 */ + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0xcd, 0xfe, 0x9f, 0xfc, 0xcd, 0xfe, 0x9f, 0xfc, 0x03, 0x00, /* bytes 4128 - 4144 */ + 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4b, 0x01, 0x86, 0xff, 0x17, 0x01, 0x8d, 0xff, 0x23, /* bytes 4144 - 4160 */ + 0x00, 0x06, 0x00, 0x03, 0x00, 0xc0, 0x01, 0xff, 0x00, 0x00, 0x07, 0x00, 0x94, 0x01, 0x59, 0xff, /* bytes 4160 - 4176 */ + 0xb0, 0x01, 0x6a, 0xff, 0xb5, 0x01, 0x86, 0xff, 0xc8, 0x01, 0x7d, 0xff, 0xd1, 0x01, 0x87, 0xff, /* bytes 4176 - 4192 */ + 0x26, 0x02, 0x59, 0xff, 0x94, 0x01, 0x59, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, /* bytes 4192 - 4208 */ + 0x00, 0xf1, 0x00, 0x9a, 0xff, 0x26, 0x01, 0x82, 0xff, 0x58, 0x01, 0x77, 0xff, 0x8d, 0x01, 0x6f, /* bytes 4208 - 4224 */ + 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x92, 0x01, 0x86, 0xff, 0x5c, 0x01, /* bytes 4224 - 4240 */ + 0x96, 0xff, 0x2b, 0x01, 0xae, 0xff, 0xf5, 0x00, 0xce, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, /* bytes 4240 - 4256 */ + 0x00, 0x02, 0x00, 0xc8, 0x01, 0x7d, 0xff, 0xbf, 0x01, 0x73, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, /* bytes 4256 - 4272 */ + 0x01, 0x00, 0x02, 0x00, 0x62, 0x01, 0xbb, 0xfc, 0x62, 0x01, 0xbb, 0xfc, 0x03, 0x00, 0xc0, 0x01, /* bytes 4272 - 4288 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xd1, 0x01, 0x6a, 0xff, 0xb0, 0x01, 0x6a, 0xff, 0x23, 0x00, 0x06, /* bytes 4288 - 4304 */ + 0x00, 0x03, 0x00, 0xc0, 0x01, 0xff, 0x00, 0x00, 0x07, 0x00, 0x7b, 0x03, 0x4d, 0xff, 0x1b, 0x03, /* bytes 4304 - 4320 */ + 0x6b, 0xff, 0x15, 0x03, 0x62, 0xff, 0x03, 0x03, 0x6a, 0xff, 0x04, 0x03, 0x50, 0xff, 0xef, 0x02, /* bytes 4320 - 4336 */ + 0x40, 0xff, 0x7b, 0x03, 0x4d, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x62, /* bytes 4336 - 4352 */ + 0x02, 0x7d, 0xff, 0x84, 0x02, 0x74, 0xff, 0x9e, 0x02, 0x70, 0xff, 0xea, 0x02, 0x65, 0xff, 0x03, /* bytes 4352 - 4368 */ + 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x22, 0x02, 0x65, 0xff, 0x7b, 0x02, 0x56, 0xff, /* bytes 4368 - 4384 */ + 0xb4, 0x02, 0x4f, 0xff, 0xe5, 0x02, 0x4f, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, /* bytes 4384 - 4400 */ + 0x00, 0xd4, 0x03, 0xd2, 0xfc, 0xd4, 0x03, 0xd2, 0xfc, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, /* bytes 4400 - 4416 */ + 0x02, 0x00, 0x0f, 0x03, 0x59, 0xff, 0x15, 0x03, 0x62, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, /* bytes 4416 - 4432 */ + 0x00, 0x02, 0x00, 0x04, 0x03, 0x50, 0xff, 0x25, 0x03, 0x54, 0xff, 0x23, 0x00, 0x06, 0x00, 0x03, /* bytes 4432 - 4448 */ + 0x00, 0xc0, 0x01, 0xff, 0x00, 0x00, 0x07, 0x00, 0xff, 0x03, 0x6c, 0xff, 0x11, 0x04, 0x65, 0xff, /* bytes 4448 - 4464 */ + 0x15, 0x04, 0x6e, 0xff, 0x80, 0x04, 0x59, 0xff, 0xf3, 0x03, 0x44, 0xff, 0x04, 0x04, 0x54, 0xff, /* bytes 4464 - 4480 */ + 0xff, 0x03, 0x6c, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x3a, 0x03, 0x4d, /* bytes 4480 - 4496 */ + 0xff, 0xa3, 0x03, 0x3e, 0xff, 0xdc, 0x03, 0x37, 0xff, 0x0d, 0x04, 0x37, 0xff, 0x03, 0x00, 0xc0, /* bytes 4496 - 4512 */ + 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x22, 0x03, 0x67, 0xff, 0x74, 0x03, 0x57, 0xff, 0xb6, 0x03, /* bytes 4512 - 4528 */ + 0x4a, 0xff, 0x12, 0x04, 0x41, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, /* bytes 4528 - 4544 */ + 0x04, 0x5c, 0xff, 0x11, 0x04, 0x65, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4544 - 4560 */ + 0x04, 0x04, 0x54, 0xff, 0x26, 0x04, 0x5a, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, /* bytes 4560 - 4576 */ + 0x00, 0xcf, 0x05, 0xe3, 0xfc, 0xcf, 0x05, 0xe3, 0xfc, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, /* bytes 4576 - 4592 */ + 0x01, 0xff, 0x00, 0x00, 0x07, 0x00, 0xff, 0x03, 0x6c, 0xff, 0x11, 0x04, 0x65, 0xff, 0x15, 0x04, /* bytes 4592 - 4608 */ + 0x6e, 0xff, 0x80, 0x04, 0x59, 0xff, 0xf3, 0x03, 0x44, 0xff, 0x04, 0x04, 0x54, 0xff, 0xff, 0x03, /* bytes 4608 - 4624 */ + 0x6c, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x04, 0x5c, 0xff, 0x11, /* bytes 4624 - 4640 */ + 0x04, 0x65, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x04, 0x54, 0xff, /* bytes 4640 - 4656 */ + 0x26, 0x04, 0x5a, 0xff, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0xcf, 0x05, 0xe3, /* bytes 4656 - 4672 */ + 0xfc, 0xcf, 0x05, 0xe3, 0xfc, 0x23, 0x00, 0x04, 0x00, 0x03, 0x00, 0xc0, 0x01, 0xff, 0x00, 0x00, /* bytes 4672 - 4688 */ + 0x07, 0x00, 0xff, 0x03, 0x6c, 0xff, 0x11, 0x04, 0x65, 0xff, 0x15, 0x04, 0x6e, 0xff, 0x80, 0x04, /* bytes 4688 - 4704 */ + 0x59, 0xff, 0xf3, 0x03, 0x44, 0xff, 0x04, 0x04, 0x54, 0xff, 0xff, 0x03, 0x6c, 0xff, 0x03, 0x00, /* bytes 4704 - 4720 */ + 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x04, 0x5c, 0xff, 0x11, 0x04, 0x65, 0xff, 0x03, /* bytes 4720 - 4736 */ + 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x04, 0x54, 0xff, 0x26, 0x04, 0x5a, 0xff, /* bytes 4736 - 4752 */ + 0x03, 0x00, 0xc0, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0xcf, 0x05, 0xe3, 0xfc, 0xcf, 0x05, 0xe3, /* bytes 4752 - 4768 */ + 0xfc, +}; +__attribute__ ((aligned (8))) +static const uint8_t RESULT_UNMUTE_LARGE_builtin_bytes[] = { + 0x50, 0x44, 0x43, 0x53, 0x6e, 0x47, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x50, 0x00, 0x01, 0x00, /* bytes 0 - 16 */ + 0x33, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x7c, /* bytes 16 - 32 */ + 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 32 - 48 */ + 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, /* bytes 48 - 64 */ + 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, /* bytes 64 - 80 */ + 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, /* bytes 80 - 96 */ + 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, /* bytes 96 - 112 */ + 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, /* bytes 112 - 128 */ + 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, /* bytes 128 - 144 */ + 0x02, 0xae, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 144 - 160 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, /* bytes 160 - 176 */ + 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, /* bytes 176 - 192 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 192 - 208 */ + 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 208 - 224 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 224 - 240 */ + 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, /* bytes 240 - 256 */ + 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, /* bytes 256 - 272 */ + 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 272 - 288 */ + 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, /* bytes 288 - 304 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 304 - 320 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, /* bytes 320 - 336 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 336 - 352 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x7c, 0x02, 0xae, /* bytes 352 - 368 */ + 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 368 - 384 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 384 - 400 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, /* bytes 400 - 416 */ + 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, /* bytes 416 - 432 */ + 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, /* bytes 432 - 448 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, /* bytes 448 - 464 */ + 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, /* bytes 464 - 480 */ + 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, /* bytes 480 - 496 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 496 - 512 */ + 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 512 - 528 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 528 - 544 */ + 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 544 - 560 */ + 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 560 - 576 */ + 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, /* bytes 576 - 592 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, /* bytes 592 - 608 */ + 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, /* bytes 608 - 624 */ + 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 624 - 640 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 640 - 656 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, /* bytes 656 - 672 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 672 - 688 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, /* bytes 688 - 704 */ + 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 704 - 720 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 720 - 736 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, /* bytes 736 - 752 */ + 0x01, 0x6c, 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, /* bytes 752 - 768 */ + 0x00, 0x5c, 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, /* bytes 768 - 784 */ + 0x00, 0xcc, 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, /* bytes 784 - 800 */ + 0x02, 0x44, 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x03, /* bytes 800 - 816 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, /* bytes 816 - 832 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 832 - 848 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 848 - 864 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, /* bytes 864 - 880 */ + 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, /* bytes 880 - 896 */ + 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, /* bytes 896 - 912 */ + 0x01, 0x6c, 0x01, 0x0a, 0x01, 0x35, 0x01, 0xdb, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, /* bytes 912 - 928 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, /* bytes 928 - 944 */ + 0x0c, 0x02, 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, /* bytes 944 - 960 */ + 0x01, 0xf6, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 960 - 976 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 976 - 992 */ + 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 992 - 1008 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 1008 - 1024 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, /* bytes 1024 - 1040 */ + 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 1040 - 1056 */ + 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 1056 - 1072 */ + 0x01, 0x4c, 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, /* bytes 1072 - 1088 */ + 0x01, 0x00, 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, /* bytes 1088 - 1104 */ + 0x02, 0xd2, 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, /* bytes 1104 - 1120 */ + 0x00, 0x0e, 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, /* bytes 1120 - 1136 */ + 0x00, 0x56, 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x03, 0x00, 0xc0, /* bytes 1136 - 1152 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, /* bytes 1152 - 1168 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, /* bytes 1168 - 1184 */ + 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, /* bytes 1184 - 1200 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, /* bytes 1200 - 1216 */ + 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, /* bytes 1216 - 1232 */ + 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, /* bytes 1232 - 1248 */ + 0x01, 0x0a, 0x01, 0x36, 0x01, 0xdc, 0x00, 0x0e, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, /* bytes 1248 - 1264 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, /* bytes 1264 - 1280 */ + 0x68, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, /* bytes 1280 - 1296 */ + 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1296 - 1312 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 1312 - 1328 */ + 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1328 - 1344 */ + 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 1344 - 1360 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, /* bytes 1360 - 1376 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 1376 - 1392 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 1392 - 1408 */ + 0x01, 0x26, 0x01, 0x4d, 0x01, 0x6c, 0x01, 0x27, 0x01, 0x6c, 0x01, 0x0b, 0x01, 0x6c, 0x01, 0x00, /* bytes 1408 - 1424 */ + 0x01, 0x6c, 0x01, 0xf4, 0x00, 0x7e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xd2, /* bytes 1424 - 1440 */ + 0x00, 0x35, 0x01, 0xa4, 0x00, 0x0b, 0x01, 0xa4, 0x00, 0xcc, 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x0e, /* bytes 1440 - 1456 */ + 0x01, 0x7c, 0x00, 0xe4, 0x01, 0x44, 0x00, 0x0c, 0x02, 0x68, 0x00, 0x2a, 0x02, 0x44, 0x00, 0x56, /* bytes 1456 - 1472 */ + 0x02, 0x44, 0x00, 0x7c, 0x02, 0x6e, 0x00, 0x7c, 0x02, 0xae, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 1472 - 1488 */ + 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 1488 - 1504 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, /* bytes 1504 - 1520 */ + 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 1520 - 1536 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, /* bytes 1536 - 1552 */ + 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, /* bytes 1552 - 1568 */ + 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x01, 0x6c, 0x01, 0x0a, /* bytes 1568 - 1584 */ + 0x01, 0x37, 0x01, 0xdc, 0x00, 0x0f, 0x01, 0xd2, 0x00, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 1584 - 1600 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xe1, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x0c, 0x02, 0x68, 0x00, /* bytes 1600 - 1616 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x26, 0x01, 0xf6, 0x00, 0xcc, /* bytes 1616 - 1632 */ + 0x00, 0xf4, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 1632 - 1648 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, /* bytes 1648 - 1664 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 1664 - 1680 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 1680 - 1696 */ + 0xff, 0x00, 0x00, 0x1d, 0x00, 0xec, 0x01, 0xb0, 0x01, 0xa3, 0x01, 0x5a, 0x01, 0xa8, 0x01, 0x6c, /* bytes 1696 - 1712 */ + 0x01, 0x9b, 0x01, 0xb2, 0x01, 0x40, 0x01, 0x6c, 0x01, 0x40, 0x01, 0x5c, 0x01, 0x40, 0x01, 0x36, /* bytes 1712 - 1728 */ + 0x01, 0x41, 0x01, 0x7c, 0x01, 0x1b, 0x01, 0x7c, 0x01, 0xff, 0x00, 0x7c, 0x01, 0xf4, 0x00, 0x7c, /* bytes 1728 - 1744 */ + 0x01, 0xe8, 0x00, 0x8e, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xc6, 0x00, 0x45, /* bytes 1744 - 1760 */ + 0x01, 0x98, 0x00, 0x1c, 0x01, 0x98, 0x00, 0xdc, 0x00, 0xe8, 0x00, 0xdc, 0x00, 0x02, 0x01, 0x8c, /* bytes 1760 - 1776 */ + 0x00, 0xd8, 0x01, 0x54, 0x00, 0x00, 0x02, 0x78, 0x00, 0x1f, 0x02, 0x54, 0x00, 0x4b, 0x02, 0x54, /* bytes 1776 - 1792 */ + 0x00, 0x70, 0x02, 0x7f, 0x00, 0x70, 0x02, 0xbf, 0x00, 0x30, 0x02, 0xf1, 0x00, 0x30, 0x02, 0x24, /* bytes 1792 - 1808 */ + 0x01, 0xf2, 0x01, 0x61, 0x01, 0xec, 0x01, 0xb0, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 1808 - 1824 */ + 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 1824 - 1840 */ + 0x00, 0x07, 0x00, 0x40, 0x01, 0x5c, 0x01, 0xa3, 0x01, 0x5a, 0x01, 0x85, 0x01, 0x37, 0x01, 0x8b, /* bytes 1840 - 1856 */ + 0x01, 0xf0, 0x00, 0xd0, 0x01, 0xbd, 0x00, 0xfc, 0x01, 0xf1, 0x00, 0xf2, 0x01, 0x61, 0x01, 0x03, /* bytes 1856 - 1872 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x9b, 0x01, 0xb2, 0x01, 0xba, 0x01, 0x19, 0x02, /* bytes 1872 - 1888 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x0a, 0x02, 0x20, 0x02, 0xec, 0x01, 0xb0, 0x01, /* bytes 1888 - 1904 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xc6, 0x00, 0x45, 0x01, 0xd1, 0x00, 0x20, /* bytes 1904 - 1920 */ + 0x01, 0xfe, 0x00, 0x48, 0x01, 0xf4, 0x00, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 1920 - 1936 */ + 0x03, 0x00, 0x30, 0x02, 0xf1, 0x00, 0x30, 0x02, 0xac, 0x00, 0x00, 0x02, 0x78, 0x00, 0x03, 0x00, /* bytes 1936 - 1952 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xe8, 0x00, 0xdc, 0x00, 0xea, 0x00, 0xdc, 0x00, 0x40, /* bytes 1952 - 1968 */ + 0x01, 0x36, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 1968 - 1984 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, /* bytes 1984 - 2000 */ + 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x40, 0x01, /* bytes 2000 - 2016 */ + 0x6c, 0x01, 0xa8, 0x01, 0x6c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 2016 - 2032 */ + 0x00, 0x1d, 0x00, 0x36, 0x01, 0x6a, 0x01, 0x36, 0x01, 0x44, 0x01, 0x37, 0x01, 0x8a, 0x01, 0x12, /* bytes 2032 - 2048 */ + 0x01, 0x8a, 0x01, 0xf5, 0x00, 0x8a, 0x01, 0xea, 0x00, 0x8a, 0x01, 0xde, 0x00, 0x9b, 0x01, 0xae, /* bytes 2048 - 2064 */ + 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xbc, 0x00, 0x53, 0x01, 0x8e, 0x00, 0x29, 0x01, 0x8e, /* bytes 2064 - 2080 */ + 0x00, 0xea, 0x00, 0xdf, 0x00, 0xea, 0x00, 0xf8, 0x00, 0x9a, 0x00, 0xce, 0x01, 0x62, 0x00, 0xf6, /* bytes 2080 - 2096 */ + 0x01, 0x86, 0x00, 0x15, 0x02, 0x62, 0x00, 0x41, 0x02, 0x62, 0x00, 0x66, 0x02, 0x8c, 0x00, 0x66, /* bytes 2096 - 2112 */ + 0x02, 0xcc, 0x00, 0x26, 0x02, 0xfe, 0x00, 0x26, 0x02, 0x32, 0x01, 0xe4, 0x01, 0x71, 0x01, 0xd7, /* bytes 2112 - 2128 */ + 0x01, 0xc0, 0x01, 0x95, 0x01, 0x65, 0x01, 0x9e, 0x01, 0x7a, 0x01, 0x87, 0x01, 0xbc, 0x01, 0x36, /* bytes 2128 - 2144 */ + 0x01, 0x7a, 0x01, 0x36, 0x01, 0x6a, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 2144 - 2160 */ + 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 2160 - 2176 */ + 0x00, 0x36, 0x01, 0x6a, 0x01, 0x95, 0x01, 0x65, 0x01, 0x7a, 0x01, 0x41, 0x01, 0x85, 0x01, 0xf9, /* bytes 2176 - 2192 */ + 0x00, 0xcd, 0x01, 0xcc, 0x00, 0xf5, 0x01, 0x03, 0x01, 0xe4, 0x01, 0x71, 0x01, 0x03, 0x00, 0xc0, /* bytes 2192 - 2208 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x87, 0x01, 0xbc, 0x01, 0xbe, 0x01, 0x10, 0x02, 0x58, 0x01, /* bytes 2208 - 2224 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x0f, 0x02, 0x17, 0x02, 0xd7, 0x01, 0xc0, 0x01, 0x03, 0x00, /* bytes 2224 - 2240 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x53, 0x01, 0xc7, 0x00, 0x2d, 0x01, 0xf4, /* bytes 2240 - 2256 */ + 0x00, 0x56, 0x01, 0xea, 0x00, 0x8a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 2256 - 2272 */ + 0xf6, 0x01, 0x86, 0x00, 0x26, 0x02, 0xba, 0x00, 0x26, 0x02, 0xfe, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 2272 - 2288 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x36, 0x01, 0x44, 0x01, 0xe0, 0x00, 0xea, 0x00, 0xdf, 0x00, 0xea, /* bytes 2288 - 2304 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x36, 0x01, 0x7a, 0x01, 0x9e, 0x01, /* bytes 2304 - 2320 */ + 0x7a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, /* bytes 2320 - 2336 */ + 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 2336 - 2352 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 2352 - 2368 */ + 0x00, 0x2f, 0x01, 0x74, 0x01, 0x2f, 0x01, 0x4e, 0x01, 0x30, 0x01, 0x94, 0x01, 0x0a, 0x01, 0x94, /* bytes 2368 - 2384 */ + 0x01, 0xee, 0x00, 0x94, 0x01, 0xe3, 0x00, 0x94, 0x01, 0xd7, 0x00, 0xa5, 0x01, 0xae, 0x00, 0x5c, /* bytes 2384 - 2400 */ + 0x02, 0x87, 0x00, 0x5c, 0x02, 0xb5, 0x00, 0x5d, 0x01, 0x87, 0x00, 0x33, 0x01, 0x87, 0x00, 0xf4, /* bytes 2400 - 2416 */ + 0x00, 0xd7, 0x00, 0xf4, 0x00, 0xf1, 0x00, 0xa4, 0x00, 0xc7, 0x01, 0x6c, 0x00, 0xef, 0x01, 0x90, /* bytes 2416 - 2432 */ + 0x00, 0x0e, 0x02, 0x6c, 0x00, 0x3a, 0x02, 0x6c, 0x00, 0x5f, 0x02, 0x96, 0x00, 0x5f, 0x02, 0xd6, /* bytes 2432 - 2448 */ + 0x00, 0x1f, 0x02, 0x08, 0x01, 0x1f, 0x02, 0x3c, 0x01, 0xd8, 0x01, 0x7d, 0x01, 0xc8, 0x01, 0xcc, /* bytes 2448 - 2464 */ + 0x01, 0x8a, 0x01, 0x6d, 0x01, 0x97, 0x01, 0x84, 0x01, 0x78, 0x01, 0xc3, 0x01, 0x2f, 0x01, 0x84, /* bytes 2464 - 2480 */ + 0x01, 0x2f, 0x01, 0x74, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, /* bytes 2480 - 2496 */ + 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xd8, /* bytes 2496 - 2512 */ + 0x01, 0x7d, 0x01, 0xef, 0x01, 0x10, 0x01, 0xca, 0x01, 0xd7, 0x00, 0x80, 0x01, 0x01, 0x01, 0x71, /* bytes 2512 - 2528 */ + 0x01, 0x47, 0x01, 0x8a, 0x01, 0x6d, 0x01, 0x2f, 0x01, 0x74, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 2528 - 2544 */ + 0x01, 0x00, 0x06, 0x00, 0xc8, 0x01, 0xcc, 0x01, 0x12, 0x02, 0x11, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 2544 - 2560 */ + 0x58, 0x01, 0x5c, 0x02, 0xc2, 0x01, 0x09, 0x02, 0x78, 0x01, 0xc3, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 2560 - 2576 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xb5, 0x00, 0x5d, 0x01, 0xc0, 0x00, 0x38, 0x01, 0xed, 0x00, 0x61, /* bytes 2576 - 2592 */ + 0x01, 0xe3, 0x00, 0x94, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x1f, 0x02, /* bytes 2592 - 2608 */ + 0x08, 0x01, 0x1f, 0x02, 0xc4, 0x00, 0xef, 0x01, 0x90, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2608 - 2624 */ + 0x00, 0x03, 0x00, 0x2f, 0x01, 0x4e, 0x01, 0xd9, 0x00, 0xf4, 0x00, 0xd7, 0x00, 0xf4, 0x00, 0x03, /* bytes 2624 - 2640 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 2640 - 2656 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x2f, 0x01, 0x84, 0x01, 0x97, 0x01, 0x84, /* bytes 2656 - 2672 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 2672 - 2688 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x5c, /* bytes 2688 - 2704 */ + 0x02, 0x9a, 0x00, 0x5c, 0x02, 0xda, 0x00, 0x1c, 0x02, 0x0d, 0x01, 0x1c, 0x02, 0x40, 0x01, 0xd4, /* bytes 2704 - 2720 */ + 0x01, 0x83, 0x01, 0xc2, 0x01, 0xd1, 0x01, 0x86, 0x01, 0x71, 0x01, 0x94, 0x01, 0x88, 0x01, 0x72, /* bytes 2720 - 2736 */ + 0x01, 0xc6, 0x01, 0x2c, 0x01, 0x88, 0x01, 0x2c, 0x01, 0x78, 0x01, 0x2c, 0x01, 0x52, 0x01, 0x2d, /* bytes 2736 - 2752 */ + 0x01, 0x98, 0x01, 0x07, 0x01, 0x98, 0x01, 0xeb, 0x00, 0x98, 0x01, 0xe0, 0x00, 0x98, 0x01, 0xd4, /* bytes 2752 - 2768 */ + 0x00, 0xaa, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xb2, 0x00, 0x61, 0x01, 0x84, /* bytes 2768 - 2784 */ + 0x00, 0x37, 0x01, 0x84, 0x00, 0xf8, 0x00, 0xd4, 0x00, 0xf8, 0x00, 0xee, 0x00, 0xa8, 0x00, 0xc4, /* bytes 2784 - 2800 */ + 0x01, 0x70, 0x00, 0xec, 0x01, 0x94, 0x00, 0x0a, 0x02, 0x70, 0x00, 0x36, 0x02, 0x70, 0x00, 0x5c, /* bytes 2800 - 2816 */ + 0x02, 0x9a, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, /* bytes 2816 - 2832 */ + 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xd4, 0x01, 0x83, /* bytes 2832 - 2848 */ + 0x01, 0xed, 0x01, 0x16, 0x01, 0xc9, 0x01, 0xdc, 0x00, 0x7e, 0x01, 0x04, 0x01, 0x6e, 0x01, 0x4a, /* bytes 2848 - 2864 */ + 0x01, 0x86, 0x01, 0x71, 0x01, 0x2c, 0x01, 0x78, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 2864 - 2880 */ + 0x06, 0x00, 0xc2, 0x01, 0xd1, 0x01, 0x13, 0x02, 0x0d, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, /* bytes 2880 - 2896 */ + 0x5c, 0x02, 0xc3, 0x01, 0x06, 0x02, 0x72, 0x01, 0xc6, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 2896 - 2912 */ + 0x00, 0x04, 0x00, 0xe0, 0x00, 0x98, 0x01, 0xea, 0x00, 0x66, 0x01, 0xbe, 0x00, 0x3d, 0x01, 0xb2, /* bytes 2912 - 2928 */ + 0x00, 0x61, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x1c, 0x02, 0x0d, 0x01, /* bytes 2928 - 2944 */ + 0x1c, 0x02, 0xc8, 0x00, 0xec, 0x01, 0x94, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 2944 - 2960 */ + 0x00, 0x2c, 0x01, 0x52, 0x01, 0xd6, 0x00, 0xf8, 0x00, 0xd4, 0x00, 0xf8, 0x00, 0x03, 0x00, 0xc0, /* bytes 2960 - 2976 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 2976 - 2992 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x94, 0x01, 0x88, 0x01, 0x2c, 0x01, 0x88, 0x01, 0x03, /* bytes 2992 - 3008 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 3008 - 3024 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x75, 0x01, 0xe2, /* bytes 3024 - 3040 */ + 0x00, 0x71, 0x01, 0x28, 0x01, 0x4b, 0x01, 0x25, 0x01, 0x2f, 0x01, 0x22, 0x01, 0x24, 0x01, 0x21, /* bytes 3040 - 3056 */ + 0x01, 0x16, 0x01, 0x32, 0x01, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0xfa, 0x00, 0xe7, /* bytes 3056 - 3072 */ + 0x00, 0xd0, 0x00, 0xb9, 0x00, 0xd6, 0x00, 0x7a, 0x00, 0x26, 0x01, 0x81, 0x00, 0x46, 0x01, 0x33, /* bytes 3072 - 3088 */ + 0x00, 0x20, 0x02, 0x0e, 0x00, 0x45, 0x02, 0x35, 0x00, 0x66, 0x02, 0x14, 0x00, 0x92, 0x02, 0x17, /* bytes 3088 - 3104 */ + 0x00, 0xb4, 0x02, 0x45, 0x00, 0xaf, 0x02, 0x85, 0x00, 0x6b, 0x02, 0xb1, 0x00, 0x66, 0x02, 0xe4, /* bytes 3104 - 3120 */ + 0x00, 0x1e, 0x02, 0x1e, 0x01, 0x0b, 0x02, 0x6c, 0x01, 0xd0, 0x01, 0x0b, 0x01, 0xd9, 0x01, 0x21, /* bytes 3120 - 3136 */ + 0x01, 0xbb, 0x01, 0x60, 0x01, 0x71, 0x01, 0x18, 0x01, 0x72, 0x01, 0x08, 0x01, 0x75, 0x01, 0xe2, /* bytes 3136 - 3152 */ + 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, /* bytes 3152 - 3168 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x1e, 0x02, 0x1e, 0x01, 0x39, /* bytes 3168 - 3184 */ + 0x02, 0xb2, 0x00, 0x16, 0x02, 0x77, 0x00, 0xca, 0x01, 0x9e, 0x00, 0xb9, 0x01, 0xe4, 0x00, 0xd0, /* bytes 3184 - 3200 */ + 0x01, 0x0b, 0x01, 0x72, 0x01, 0x08, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 3200 - 3216 */ + 0xbb, 0x01, 0x60, 0x01, 0xb7, 0x01, 0xfe, 0x01, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 3216 - 3232 */ + 0x14, 0x02, 0x0c, 0x02, 0x0b, 0x02, 0x6c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 3232 - 3248 */ + 0x00, 0xfa, 0x00, 0xe7, 0x00, 0x0a, 0x01, 0xc4, 0x00, 0x32, 0x01, 0xf1, 0x00, 0x24, 0x01, 0x21, /* bytes 3248 - 3264 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x75, 0x01, 0xe2, 0x00, 0x27, 0x01, /* bytes 3264 - 3280 */ + 0x81, 0x00, 0x26, 0x01, 0x81, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x45, /* bytes 3280 - 3296 */ + 0x02, 0x35, 0x00, 0x70, 0x02, 0x6d, 0x00, 0x6b, 0x02, 0xb1, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3296 - 3312 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 3312 - 3328 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 3328 - 3344 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd9, 0x01, 0x21, 0x01, 0x71, 0x01, 0x18, 0x01, 0x23, 0x00, /* bytes 3344 - 3360 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x89, 0x02, 0xce, 0x00, 0x44, /* bytes 3360 - 3376 */ + 0x02, 0x00, 0x01, 0x32, 0x02, 0x4e, 0x01, 0xf6, 0x01, 0xee, 0x00, 0xf8, 0x01, 0x00, 0x01, 0xe2, /* bytes 3376 - 3392 */ + 0x01, 0x44, 0x01, 0x9b, 0x01, 0xf9, 0x00, 0x9b, 0x01, 0xe3, 0x00, 0x99, 0x01, 0xbb, 0x00, 0x8f, /* bytes 3392 - 3408 */ + 0x01, 0x00, 0x01, 0x6a, 0x01, 0xfa, 0x00, 0x4e, 0x01, 0xf6, 0x00, 0x43, 0x01, 0xf4, 0x00, 0x3d, /* bytes 3408 - 3424 */ + 0x01, 0xfc, 0x00, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0x1e, 0x01, 0xb7, 0x00, 0xf7, /* bytes 3424 - 3440 */ + 0x00, 0x87, 0x00, 0x01, 0x01, 0x48, 0x00, 0x50, 0x01, 0x55, 0x00, 0x76, 0x01, 0x09, 0x00, 0x52, /* bytes 3440 - 3456 */ + 0x02, 0xf3, 0xff, 0x7a, 0x02, 0x25, 0x00, 0x99, 0x02, 0x15, 0x00, 0xb9, 0x02, 0x1c, 0x00, 0xcc, /* bytes 3456 - 3472 */ + 0x02, 0x4c, 0x00, 0xc2, 0x02, 0x80, 0x00, 0x94, 0x02, 0x92, 0x00, 0x89, 0x02, 0xce, 0x00, 0x03, /* bytes 3472 - 3488 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, /* bytes 3488 - 3504 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x44, 0x02, 0x00, 0x01, 0x5d, 0x02, 0x93, /* bytes 3504 - 3520 */ + 0x00, 0x39, 0x02, 0x59, 0x00, 0xee, 0x01, 0x82, 0x00, 0xde, 0x01, 0xc8, 0x00, 0xf6, 0x01, 0xee, /* bytes 3520 - 3536 */ + 0x00, 0x9b, 0x01, 0xe3, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x32, 0x02, /* bytes 3536 - 3552 */ + 0x4e, 0x01, 0x07, 0x02, 0x05, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 3552 - 3568 */ + 0xfc, 0x01, 0xe2, 0x01, 0x44, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x1e, /* bytes 3568 - 3584 */ + 0x01, 0xb7, 0x00, 0x30, 0x01, 0x96, 0x00, 0x54, 0x01, 0xc5, 0x00, 0x43, 0x01, 0xf4, 0x00, 0x03, /* bytes 3584 - 3600 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x50, 0x01, 0x55, 0x00, 0x52, 0x01, 0x55, 0x00, /* bytes 3600 - 3616 */ + 0x99, 0x01, 0xbb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x7a, 0x02, 0x25, /* bytes 3616 - 3632 */ + 0x00, 0x9c, 0x02, 0x58, 0x00, 0x94, 0x02, 0x92, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 3632 - 3648 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 3648 - 3664 */ + 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 3664 - 3680 */ + 0x01, 0x00, 0x02, 0x00, 0xf8, 0x01, 0x00, 0x01, 0x9b, 0x01, 0xf9, 0x00, 0x23, 0x00, 0x0a, 0x00, /* bytes 3680 - 3696 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xa2, 0x01, 0xbd, 0x00, 0x9a, 0x01, 0x02, /* bytes 3696 - 3712 */ + 0x01, 0x74, 0x01, 0xfd, 0x00, 0x58, 0x01, 0xf9, 0x00, 0x4d, 0x01, 0xf8, 0x00, 0x4b, 0x01, 0xfd, /* bytes 3712 - 3728 */ + 0x00, 0xae, 0x00, 0x5c, 0x02, 0x87, 0x00, 0x5c, 0x02, 0x27, 0x01, 0xbb, 0x00, 0x00, 0x01, 0x8b, /* bytes 3728 - 3744 */ + 0x00, 0x08, 0x01, 0x4d, 0x00, 0x58, 0x01, 0x58, 0x00, 0x7b, 0x01, 0x03, 0x00, 0x57, 0x02, 0xed, /* bytes 3744 - 3760 */ + 0xff, 0x80, 0x02, 0x20, 0x00, 0x9d, 0x02, 0x12, 0x00, 0xbe, 0x02, 0x18, 0x00, 0xd0, 0x02, 0x49, /* bytes 3760 - 3776 */ + 0x00, 0xc3, 0x02, 0x7b, 0x00, 0x99, 0x02, 0x8b, 0x00, 0x8e, 0x02, 0xc8, 0x00, 0x4e, 0x02, 0xf7, /* bytes 3776 - 3792 */ + 0x00, 0x41, 0x02, 0x46, 0x01, 0xfe, 0x01, 0xea, 0x00, 0xfc, 0x01, 0xfa, 0x00, 0xf1, 0x01, 0x41, /* bytes 3792 - 3808 */ + 0x01, 0xa9, 0x01, 0xfc, 0x00, 0xa7, 0x01, 0xdf, 0x00, 0xa2, 0x01, 0xbd, 0x00, 0x03, 0x00, 0xc0, /* bytes 3808 - 3824 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x87, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5c, 0x02, 0x03, 0x00, /* bytes 3824 - 3840 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xa7, 0x01, 0xdf, 0x00, 0xfe, 0x01, 0xea, 0x00, 0xe4, /* bytes 3840 - 3856 */ + 0x01, 0xc6, 0x00, 0xef, 0x01, 0x7f, 0x00, 0x37, 0x02, 0x51, 0x00, 0x5f, 0x02, 0x88, 0x00, 0x4e, /* bytes 3856 - 3872 */ + 0x02, 0xf7, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x41, 0x02, 0x46, 0x01, /* bytes 3872 - 3888 */ + 0x04, 0x02, 0x04, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0xfc, 0x01, /* bytes 3888 - 3904 */ + 0xf1, 0x01, 0x41, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x27, 0x01, 0xbb, /* bytes 3904 - 3920 */ + 0x00, 0x39, 0x01, 0x9a, 0x00, 0x5e, 0x01, 0xc9, 0x00, 0x4d, 0x01, 0xf8, 0x00, 0x03, 0x00, 0xc0, /* bytes 3920 - 3936 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x99, 0x02, 0x8b, 0x00, 0xa0, 0x02, 0x52, 0x00, 0x80, 0x02, /* bytes 3936 - 3952 */ + 0x20, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xa2, 0x01, 0xbd, 0x00, 0x59, /* bytes 3952 - 3968 */ + 0x01, 0x58, 0x00, 0x58, 0x01, 0x58, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 3968 - 3984 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 3984 - 4000 */ + 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4000 - 4016 */ + 0x02, 0x00, 0xfc, 0x01, 0xfa, 0x00, 0xa9, 0x01, 0xfc, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 4016 - 4032 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x9b, 0x02, 0x8e, 0x00, 0x8f, 0x02, 0xcc, 0x00, 0x4e, /* bytes 4032 - 4048 */ + 0x02, 0xfa, 0x00, 0x41, 0x02, 0x49, 0x01, 0xff, 0x01, 0xed, 0x00, 0xfd, 0x01, 0xfd, 0x00, 0xf1, /* bytes 4048 - 4064 */ + 0x01, 0x44, 0x01, 0xa9, 0x01, 0xfe, 0x00, 0xa8, 0x01, 0xe1, 0x00, 0xa5, 0x01, 0xc7, 0x00, 0x9c, /* bytes 4064 - 4080 */ + 0x01, 0x0d, 0x01, 0x77, 0x01, 0x08, 0x01, 0x5a, 0x01, 0x04, 0x01, 0x50, 0x01, 0x02, 0x01, 0x4d, /* bytes 4080 - 4096 */ + 0x01, 0xfc, 0x00, 0xae, 0x00, 0x5b, 0x02, 0x86, 0x00, 0x5c, 0x02, 0x29, 0x01, 0xc6, 0x00, 0x03, /* bytes 4096 - 4112 */ + 0x01, 0x95, 0x00, 0x0c, 0x01, 0x57, 0x00, 0x5b, 0x01, 0x62, 0x00, 0x7e, 0x01, 0x04, 0x00, 0x5b, /* bytes 4112 - 4128 */ + 0x02, 0xf1, 0xff, 0x83, 0x02, 0x24, 0x00, 0xa1, 0x02, 0x18, 0x00, 0xc1, 0x02, 0x1e, 0x00, 0xd2, /* bytes 4128 - 4144 */ + 0x02, 0x4f, 0x00, 0xc3, 0x02, 0x81, 0x00, 0x9b, 0x02, 0x8e, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 4144 - 4160 */ + 0x01, 0x00, 0x02, 0x00, 0x86, 0x00, 0x5c, 0x02, 0xae, 0x00, 0x5b, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 4160 - 4176 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x4e, 0x02, 0xfa, 0x00, 0x61, 0x02, 0x8c, 0x00, 0x3a, 0x02, 0x54, /* bytes 4176 - 4192 */ + 0x00, 0xf1, 0x01, 0x81, 0x00, 0xe5, 0x01, 0xc8, 0x00, 0xff, 0x01, 0xed, 0x00, 0xa8, 0x01, 0xe1, /* bytes 4192 - 4208 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xf1, 0x01, 0x44, 0x01, 0xb4, 0x01, /* bytes 4208 - 4224 */ + 0x04, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0xfc, 0x01, 0x41, 0x02, /* bytes 4224 - 4240 */ + 0x49, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x50, 0x01, 0x02, 0x01, 0x61, /* bytes 4240 - 4256 */ + 0x01, 0xd4, 0x00, 0x3c, 0x01, 0xa4, 0x00, 0x29, 0x01, 0xc6, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 4256 - 4272 */ + 0x01, 0x00, 0x03, 0x00, 0x83, 0x02, 0x24, 0x00, 0xa3, 0x02, 0x56, 0x00, 0x9b, 0x02, 0x8e, 0x00, /* bytes 4272 - 4288 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x5b, 0x01, 0x62, 0x00, 0x5b, 0x01, 0x62, /* bytes 4288 - 4304 */ + 0x00, 0xa5, 0x01, 0xc7, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 4304 - 4320 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, /* bytes 4320 - 4336 */ + 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4336 - 4352 */ + 0xa9, 0x01, 0xfe, 0x00, 0xfd, 0x01, 0xfd, 0x00, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 4352 - 4368 */ + 0xff, 0x00, 0x00, 0x1d, 0x00, 0xce, 0x02, 0x37, 0x00, 0xdc, 0x02, 0x69, 0x00, 0xc9, 0x02, 0x9a, /* bytes 4368 - 4384 */ + 0x00, 0xa2, 0x02, 0xa3, 0x00, 0x92, 0x02, 0xe1, 0x00, 0x4f, 0x02, 0x0c, 0x01, 0x3e, 0x02, 0x5a, /* bytes 4384 - 4400 */ + 0x01, 0x01, 0x02, 0xfa, 0x00, 0xfe, 0x01, 0x0a, 0x01, 0xee, 0x01, 0x50, 0x01, 0xab, 0x01, 0x06, /* bytes 4400 - 4416 */ + 0x01, 0xaa, 0x01, 0xe9, 0x00, 0xad, 0x01, 0xcf, 0x00, 0xa1, 0x01, 0x14, 0x01, 0x7c, 0x01, 0x0c, /* bytes 4416 - 4432 */ + 0x01, 0x60, 0x01, 0x07, 0x01, 0x56, 0x01, 0x05, 0x01, 0x50, 0x01, 0xff, 0x00, 0xae, 0x00, 0x5b, /* bytes 4432 - 4448 */ + 0x02, 0x86, 0x00, 0x5b, 0x02, 0x32, 0x01, 0xc7, 0x00, 0x0e, 0x01, 0x94, 0x00, 0x1a, 0x01, 0x56, /* bytes 4448 - 4464 */ + 0x00, 0x69, 0x01, 0x66, 0x00, 0x8d, 0x01, 0x0a, 0x00, 0x6a, 0x02, 0x03, 0x00, 0x90, 0x02, 0x39, /* bytes 4464 - 4480 */ + 0x00, 0xae, 0x02, 0x30, 0x00, 0xce, 0x02, 0x37, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 4480 - 4496 */ + 0x02, 0x00, 0xae, 0x00, 0x5b, 0x02, 0x86, 0x00, 0x5b, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 4496 - 4512 */ + 0x00, 0x07, 0x00, 0x4f, 0x02, 0x0c, 0x01, 0x68, 0x02, 0x9e, 0x00, 0x44, 0x02, 0x65, 0x00, 0xf9, /* bytes 4512 - 4528 */ + 0x01, 0x8d, 0x00, 0xe9, 0x01, 0xd4, 0x00, 0x01, 0x02, 0xfa, 0x00, 0xaa, 0x01, 0xe9, 0x00, 0x03, /* bytes 4528 - 4544 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xee, 0x01, 0x50, 0x01, 0xb4, 0x01, 0xfc, 0x01, /* bytes 4544 - 4560 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x04, 0x02, 0x3e, 0x02, 0x5a, 0x01, /* bytes 4560 - 4576 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x32, 0x01, 0xc7, 0x00, 0x46, 0x01, 0xa6, /* bytes 4576 - 4592 */ + 0x00, 0x69, 0x01, 0xd8, 0x00, 0x56, 0x01, 0x05, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 4592 - 4608 */ + 0x03, 0x00, 0xa2, 0x02, 0xa3, 0x00, 0xad, 0x02, 0x6c, 0x00, 0x90, 0x02, 0x39, 0x00, 0x03, 0x00, /* bytes 4608 - 4624 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xad, 0x01, 0xcf, 0x00, 0x68, 0x01, 0x66, 0x00, 0x69, /* bytes 4624 - 4640 */ + 0x01, 0x66, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 4640 - 4656 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, /* bytes 4656 - 4672 */ + 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xab, 0x01, /* bytes 4672 - 4688 */ + 0x06, 0x01, 0xfe, 0x01, 0x0a, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 4688 - 4704 */ + 0x00, 0x1d, 0x00, 0x72, 0x01, 0x14, 0x01, 0x56, 0x01, 0x0d, 0x01, 0x4c, 0x01, 0x0a, 0x01, 0x50, /* bytes 4704 - 4720 */ + 0x01, 0x00, 0x01, 0xae, 0x00, 0x5a, 0x02, 0x85, 0x00, 0x5b, 0x02, 0x2c, 0x01, 0xca, 0x00, 0x0d, /* bytes 4720 - 4736 */ + 0x01, 0x94, 0x00, 0x1e, 0x01, 0x57, 0x00, 0x6b, 0x01, 0x6d, 0x00, 0x91, 0x01, 0x0f, 0x00, 0x6f, /* bytes 4736 - 4752 */ + 0x02, 0x0c, 0x00, 0x94, 0x02, 0x43, 0x00, 0xb1, 0x02, 0x3b, 0x00, 0xd1, 0x02, 0x42, 0x00, 0xde, /* bytes 4752 - 4768 */ + 0x02, 0x74, 0x00, 0xca, 0x02, 0xa5, 0x00, 0xa3, 0x02, 0xac, 0x00, 0x92, 0x02, 0xeb, 0x00, 0x4e, /* bytes 4768 - 4784 */ + 0x02, 0x14, 0x01, 0x3b, 0x02, 0x61, 0x01, 0x01, 0x02, 0x00, 0x01, 0xfd, 0x01, 0x10, 0x01, 0xeb, /* bytes 4784 - 4800 */ + 0x01, 0x56, 0x01, 0xaa, 0x01, 0x0b, 0x01, 0xaa, 0x01, 0xee, 0x00, 0xa8, 0x01, 0xda, 0x00, 0x96, /* bytes 4800 - 4816 */ + 0x01, 0x1e, 0x01, 0x72, 0x01, 0x14, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 4816 - 4832 */ + 0x85, 0x00, 0x5b, 0x02, 0xae, 0x00, 0x5a, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 4832 - 4848 */ + 0x00, 0xaa, 0x01, 0xee, 0x00, 0x01, 0x02, 0x00, 0x01, 0xe9, 0x01, 0xda, 0x00, 0xfb, 0x01, 0x94, /* bytes 4848 - 4864 */ + 0x00, 0x46, 0x02, 0x6d, 0x00, 0x69, 0x02, 0xa7, 0x00, 0x4e, 0x02, 0x14, 0x01, 0x03, 0x00, 0xc0, /* bytes 4864 - 4880 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x3b, 0x02, 0x61, 0x01, 0x04, 0x02, 0xfc, 0x01, 0xb8, 0x01, /* bytes 4880 - 4896 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x04, 0x02, 0xeb, 0x01, 0x56, 0x01, 0x03, 0x00, /* bytes 4896 - 4912 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x4c, 0x01, 0x0a, 0x01, 0x62, 0x01, 0xde, 0x00, 0x44, /* bytes 4912 - 4928 */ + 0x01, 0xaa, 0x00, 0x2c, 0x01, 0xca, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 4928 - 4944 */ + 0xa8, 0x01, 0xda, 0x00, 0x69, 0x01, 0x6c, 0x00, 0x6b, 0x01, 0x6d, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 4944 - 4960 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x94, 0x02, 0x43, 0x00, 0xaf, 0x02, 0x76, 0x00, 0xa3, 0x02, 0xac, /* bytes 4960 - 4976 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 4976 - 4992 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, /* bytes 4992 - 5008 */ + 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfd, 0x01, 0x10, 0x01, /* bytes 5008 - 5024 */ + 0xaa, 0x01, 0x0b, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, /* bytes 5024 - 5040 */ + 0x00, 0xad, 0x00, 0x59, 0x02, 0x51, 0x01, 0x01, 0x01, 0x4f, 0x01, 0x08, 0x01, 0x59, 0x01, 0x0b, /* bytes 5040 - 5056 */ + 0x01, 0x75, 0x01, 0x12, 0x01, 0x99, 0x01, 0x1c, 0x01, 0xaa, 0x01, 0xd8, 0x00, 0xa9, 0x01, 0xef, /* bytes 5056 - 5072 */ + 0x00, 0xa8, 0x01, 0x0c, 0x01, 0xeb, 0x01, 0x57, 0x01, 0xfc, 0x01, 0x11, 0x01, 0xff, 0x01, 0x01, /* bytes 5072 - 5088 */ + 0x01, 0x3a, 0x02, 0x62, 0x01, 0x4d, 0x02, 0x14, 0x01, 0x91, 0x02, 0xeb, 0x00, 0xa2, 0x02, 0xaa, /* bytes 5088 - 5104 */ + 0x00, 0xc8, 0x02, 0xa5, 0x00, 0xdc, 0x02, 0x74, 0x00, 0xcf, 0x02, 0x42, 0x00, 0xaf, 0x02, 0x3b, /* bytes 5104 - 5120 */ + 0x00, 0x92, 0x02, 0x44, 0x00, 0x6d, 0x02, 0x0c, 0x00, 0x8f, 0x01, 0x0f, 0x00, 0x6d, 0x01, 0x6a, /* bytes 5120 - 5136 */ + 0x00, 0x1f, 0x01, 0x56, 0x00, 0x0f, 0x01, 0x93, 0x00, 0x2e, 0x01, 0xc9, 0x00, 0x85, 0x00, 0x5a, /* bytes 5136 - 5152 */ + 0x02, 0xad, 0x00, 0x59, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x85, 0x00, /* bytes 5152 - 5168 */ + 0x5a, 0x02, 0xad, 0x00, 0x59, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4d, /* bytes 5168 - 5184 */ + 0x02, 0x14, 0x01, 0x68, 0x02, 0xa7, 0x00, 0x44, 0x02, 0x6d, 0x00, 0xf9, 0x01, 0x94, 0x00, 0xe8, /* bytes 5184 - 5200 */ + 0x01, 0xda, 0x00, 0xff, 0x01, 0x01, 0x01, 0xa9, 0x01, 0xef, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 5200 - 5216 */ + 0x01, 0x00, 0x06, 0x00, 0x3a, 0x02, 0x62, 0x01, 0x04, 0x02, 0x04, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 5216 - 5232 */ + 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0xfc, 0x01, 0xeb, 0x01, 0x57, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 5232 - 5248 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x4f, 0x01, 0x08, 0x01, 0x65, 0x01, 0xdc, 0x00, 0x46, 0x01, 0xa8, /* bytes 5248 - 5264 */ + 0x00, 0x2e, 0x01, 0xc9, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x92, 0x02, /* bytes 5264 - 5280 */ + 0x44, 0x00, 0xad, 0x02, 0x76, 0x00, 0xa2, 0x02, 0xaa, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 5280 - 5296 */ + 0x00, 0x03, 0x00, 0xaa, 0x01, 0xd8, 0x00, 0x6a, 0x01, 0x69, 0x00, 0x6d, 0x01, 0x6a, 0x00, 0x03, /* bytes 5296 - 5312 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 5312 - 5328 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfc, 0x01, 0x11, 0x01, 0xa8, 0x01, 0x0c, /* bytes 5328 - 5344 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 5344 - 5360 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x53, /* bytes 5360 - 5376 */ + 0x01, 0x01, 0x01, 0xad, 0x00, 0x59, 0x02, 0x84, 0x00, 0x5a, 0x02, 0x2d, 0x01, 0xcd, 0x00, 0x0e, /* bytes 5376 - 5392 */ + 0x01, 0x97, 0x00, 0x1d, 0x01, 0x59, 0x00, 0x6b, 0x01, 0x6d, 0x00, 0x96, 0x01, 0x12, 0x00, 0x74, /* bytes 5392 - 5408 */ + 0x02, 0x14, 0x00, 0x98, 0x02, 0x4d, 0x00, 0xb5, 0x02, 0x46, 0x00, 0xd5, 0x02, 0x4d, 0x00, 0xe0, /* bytes 5408 - 5424 */ + 0x02, 0x80, 0x00, 0xcb, 0x02, 0xb0, 0x00, 0xa5, 0x02, 0xb3, 0x00, 0x92, 0x02, 0xf4, 0x00, 0x4e, /* bytes 5424 - 5440 */ + 0x02, 0x1b, 0x01, 0x39, 0x02, 0x69, 0x01, 0x01, 0x02, 0x06, 0x01, 0xfc, 0x01, 0x16, 0x01, 0xe9, /* bytes 5440 - 5456 */ + 0x01, 0x5b, 0x01, 0xa9, 0x01, 0x0f, 0x01, 0xab, 0x01, 0xf2, 0x00, 0xaa, 0x01, 0xd9, 0x00, 0x9a, /* bytes 5456 - 5472 */ + 0x01, 0x1d, 0x01, 0x76, 0x01, 0x14, 0x01, 0x5a, 0x01, 0x0d, 0x01, 0x50, 0x01, 0x0b, 0x01, 0x53, /* bytes 5472 - 5488 */ + 0x01, 0x01, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xad, 0x00, 0x59, 0x02, /* bytes 5488 - 5504 */ + 0x84, 0x00, 0x5a, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xab, 0x01, 0xf2, /* bytes 5504 - 5520 */ + 0x00, 0x01, 0x02, 0x06, 0x01, 0xea, 0x01, 0xdf, 0x00, 0xfd, 0x01, 0x9a, 0x00, 0x49, 0x02, 0x74, /* bytes 5520 - 5536 */ + 0x00, 0x6b, 0x02, 0xaf, 0x00, 0x4e, 0x02, 0x1b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 5536 - 5552 */ + 0x06, 0x00, 0x39, 0x02, 0x69, 0x01, 0x04, 0x02, 0xfc, 0x01, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, /* bytes 5552 - 5568 */ + 0x5c, 0x02, 0xb4, 0x01, 0x04, 0x02, 0xe9, 0x01, 0x5b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 5568 - 5584 */ + 0x00, 0x04, 0x00, 0x2d, 0x01, 0xcd, 0x00, 0x45, 0x01, 0xab, 0x00, 0x65, 0x01, 0xdf, 0x00, 0x50, /* bytes 5584 - 5600 */ + 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xaa, 0x01, 0xd9, 0x00, /* bytes 5600 - 5616 */ + 0x66, 0x01, 0x6b, 0x00, 0x6b, 0x01, 0x6d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 5616 - 5632 */ + 0x00, 0x98, 0x02, 0x4d, 0x00, 0xb2, 0x02, 0x80, 0x00, 0xa5, 0x02, 0xb3, 0x00, 0x03, 0x00, 0xc0, /* bytes 5632 - 5648 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 5648 - 5664 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, /* bytes 5664 - 5680 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa9, 0x01, 0x0f, 0x01, 0xfc, 0x01, 0x16, 0x01, /* bytes 5680 - 5696 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x4f, 0x02, 0x2d, /* bytes 5696 - 5712 */ + 0x01, 0x36, 0x02, 0x78, 0x01, 0x04, 0x02, 0x13, 0x01, 0xfe, 0x01, 0x22, 0x01, 0xe7, 0x01, 0x66, /* bytes 5712 - 5728 */ + 0x01, 0xac, 0x01, 0x16, 0x01, 0xaf, 0x01, 0xf9, 0x00, 0xb5, 0x01, 0xdd, 0x00, 0xa1, 0x01, 0x21, /* bytes 5728 - 5744 */ + 0x01, 0x7d, 0x01, 0x16, 0x01, 0x62, 0x01, 0x0d, 0x01, 0x57, 0x01, 0x0a, 0x01, 0x59, 0x01, 0x01, /* bytes 5744 - 5760 */ + 0x01, 0xad, 0x00, 0x58, 0x02, 0x83, 0x00, 0x5a, 0x02, 0x38, 0x01, 0xcb, 0x00, 0x1c, 0x01, 0x92, /* bytes 5760 - 5776 */ + 0x00, 0x2f, 0x01, 0x56, 0x00, 0x7c, 0x01, 0x6e, 0x00, 0xa9, 0x01, 0x18, 0x00, 0x86, 0x02, 0x28, /* bytes 5776 - 5792 */ + 0x00, 0xa7, 0x02, 0x63, 0x00, 0xc4, 0x02, 0x5e, 0x00, 0xe3, 0x02, 0x67, 0x00, 0xeb, 0x02, 0x9b, /* bytes 5792 - 5808 */ + 0x00, 0xd2, 0x02, 0xc9, 0x00, 0xad, 0x02, 0xc9, 0x00, 0x96, 0x02, 0x0a, 0x01, 0x4f, 0x02, 0x2d, /* bytes 5808 - 5824 */ + 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x83, 0x00, 0x5a, 0x02, 0xad, 0x00, /* bytes 5824 - 5840 */ + 0x58, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4f, 0x02, 0x2d, 0x01, 0x73, /* bytes 5840 - 5856 */ + 0x02, 0xc3, 0x00, 0x56, 0x02, 0x86, 0x00, 0x07, 0x02, 0xa6, 0x00, 0xf0, 0x01, 0xea, 0x00, 0x04, /* bytes 5856 - 5872 */ + 0x02, 0x13, 0x01, 0xaf, 0x01, 0xf9, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 5872 - 5888 */ + 0x36, 0x02, 0x78, 0x01, 0x04, 0x02, 0x04, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 5888 - 5904 */ + 0xb4, 0x01, 0xfc, 0x01, 0xe7, 0x01, 0x66, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 5904 - 5920 */ + 0x00, 0x57, 0x01, 0x0a, 0x01, 0x6f, 0x01, 0xdf, 0x00, 0x53, 0x01, 0xaa, 0x00, 0x38, 0x01, 0xcb, /* bytes 5920 - 5936 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xad, 0x02, 0xc9, 0x00, 0xbd, 0x02, /* bytes 5936 - 5952 */ + 0x98, 0x00, 0xa7, 0x02, 0x63, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb5, /* bytes 5952 - 5968 */ + 0x01, 0xdd, 0x00, 0x76, 0x01, 0x6c, 0x00, 0x7c, 0x01, 0x6e, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 5968 - 5984 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 5984 - 6000 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 6000 - 6016 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x01, 0x16, 0x01, 0xfe, 0x01, 0x22, 0x01, 0x23, 0x00, /* bytes 6016 - 6032 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xb0, 0x02, 0xd1, 0x00, 0x98, /* bytes 6032 - 6048 */ + 0x02, 0x12, 0x01, 0x50, 0x02, 0x33, 0x01, 0x34, 0x02, 0x7e, 0x01, 0x05, 0x02, 0x17, 0x01, 0xff, /* bytes 6048 - 6064 */ + 0x01, 0x26, 0x01, 0xe6, 0x01, 0x6a, 0x01, 0xad, 0x01, 0x18, 0x01, 0xb1, 0x01, 0xfb, 0x00, 0xb0, /* bytes 6064 - 6080 */ + 0x01, 0xe7, 0x00, 0x98, 0x01, 0x29, 0x01, 0x75, 0x01, 0x1b, 0x01, 0x5a, 0x01, 0x11, 0x01, 0x50, /* bytes 6080 - 6096 */ + 0x01, 0x0d, 0x01, 0x5c, 0x01, 0x01, 0x01, 0xac, 0x00, 0x58, 0x02, 0x81, 0x00, 0x5a, 0x02, 0x35, /* bytes 6096 - 6112 */ + 0x01, 0xcc, 0x00, 0x1e, 0x01, 0x91, 0x00, 0x36, 0x01, 0x56, 0x00, 0x80, 0x01, 0x74, 0x00, 0xb1, /* bytes 6112 - 6128 */ + 0x01, 0x1b, 0x00, 0x8d, 0x02, 0x30, 0x00, 0xac, 0x02, 0x6c, 0x00, 0xca, 0x02, 0x68, 0x00, 0xe9, /* bytes 6128 - 6144 */ + 0x02, 0x72, 0x00, 0xef, 0x02, 0xa5, 0x00, 0xd5, 0x02, 0xd3, 0x00, 0xb0, 0x02, 0xd1, 0x00, 0x03, /* bytes 6144 - 6160 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x81, 0x00, 0x5a, 0x02, 0xac, 0x00, 0x58, 0x02, /* bytes 6160 - 6176 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x50, 0x02, 0x33, 0x01, 0x77, 0x02, 0xca, /* bytes 6176 - 6192 */ + 0x00, 0x5a, 0x02, 0x8c, 0x00, 0x0b, 0x02, 0xab, 0x00, 0xf2, 0x01, 0xee, 0x00, 0x05, 0x02, 0x17, /* bytes 6192 - 6208 */ + 0x01, 0xb1, 0x01, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x34, 0x02, /* bytes 6208 - 6224 */ + 0x7e, 0x01, 0x04, 0x02, 0xfc, 0x01, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 6224 - 6240 */ + 0x04, 0x02, 0xe6, 0x01, 0x6a, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x35, /* bytes 6240 - 6256 */ + 0x01, 0xcc, 0x00, 0x53, 0x01, 0xad, 0x00, 0x6b, 0x01, 0xe4, 0x00, 0x50, 0x01, 0x0d, 0x01, 0x03, /* bytes 6256 - 6272 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb0, 0x02, 0xd1, 0x00, 0xc1, 0x02, 0xa1, 0x00, /* bytes 6272 - 6288 */ + 0xac, 0x02, 0x6c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x80, 0x01, 0x74, /* bytes 6288 - 6304 */ + 0x00, 0x7a, 0x01, 0x71, 0x00, 0xb0, 0x01, 0xe7, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 6304 - 6320 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 6320 - 6336 */ + 0x00, 0x02, 0x00, 0xad, 0x01, 0x18, 0x01, 0xff, 0x01, 0x26, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6336 - 6352 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 6352 - 6368 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0xb0, 0x02, 0xce, 0x00, 0x98, 0x02, 0x0f, /* bytes 6368 - 6384 */ + 0x01, 0x50, 0x02, 0x31, 0x01, 0x35, 0x02, 0x7c, 0x01, 0x05, 0x02, 0x16, 0x01, 0xff, 0x01, 0x25, /* bytes 6384 - 6400 */ + 0x01, 0xe7, 0x01, 0x69, 0x01, 0xad, 0x01, 0x17, 0x01, 0xb1, 0x01, 0xfa, 0x00, 0xb5, 0x01, 0xe5, /* bytes 6400 - 6416 */ + 0x00, 0x9d, 0x01, 0x27, 0x01, 0x79, 0x01, 0x19, 0x01, 0x5f, 0x01, 0x0f, 0x01, 0x55, 0x01, 0x0b, /* bytes 6416 - 6432 */ + 0x01, 0x5d, 0x01, 0xff, 0x00, 0xac, 0x00, 0x58, 0x02, 0x80, 0x00, 0x5a, 0x02, 0x38, 0x01, 0xcb, /* bytes 6432 - 6448 */ + 0x00, 0x22, 0x01, 0x90, 0x00, 0x38, 0x01, 0x55, 0x00, 0x83, 0x01, 0x72, 0x00, 0xaf, 0x01, 0x1a, /* bytes 6448 - 6464 */ + 0x00, 0x8b, 0x02, 0x2e, 0x00, 0xab, 0x02, 0x69, 0x00, 0xc8, 0x02, 0x65, 0x00, 0xe8, 0x02, 0x6e, /* bytes 6464 - 6480 */ + 0x00, 0xee, 0x02, 0xa2, 0x00, 0xd4, 0x02, 0xd0, 0x00, 0xb0, 0x02, 0xce, 0x00, 0x03, 0x00, 0xc0, /* bytes 6480 - 6496 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xac, 0x00, 0x58, 0x02, 0x80, 0x00, 0x5a, 0x02, 0x03, 0x00, /* bytes 6496 - 6512 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x50, 0x02, 0x31, 0x01, 0x76, 0x02, 0xc8, 0x00, 0x59, /* bytes 6512 - 6528 */ + 0x02, 0x8a, 0x00, 0x0a, 0x02, 0xa9, 0x00, 0xf2, 0x01, 0xed, 0x00, 0x05, 0x02, 0x16, 0x01, 0xb1, /* bytes 6528 - 6544 */ + 0x01, 0xfa, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xe7, 0x01, 0x69, 0x01, /* bytes 6544 - 6560 */ + 0xb4, 0x01, 0xfc, 0x01, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x04, 0x02, /* bytes 6560 - 6576 */ + 0x35, 0x02, 0x7c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x38, 0x01, 0xcb, /* bytes 6576 - 6592 */ + 0x00, 0x56, 0x01, 0xab, 0x00, 0x6f, 0x01, 0xe2, 0x00, 0x55, 0x01, 0x0b, 0x01, 0x03, 0x00, 0xc0, /* bytes 6592 - 6608 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb5, 0x01, 0xe5, 0x00, 0x7b, 0x01, 0x6f, 0x00, 0x83, 0x01, /* bytes 6608 - 6624 */ + 0x72, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb0, 0x02, 0xce, 0x00, 0xc0, /* bytes 6624 - 6640 */ + 0x02, 0x9e, 0x00, 0xab, 0x02, 0x69, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 6640 - 6656 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 6656 - 6672 */ + 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 6672 - 6688 */ + 0x02, 0x00, 0xff, 0x01, 0x25, 0x01, 0xad, 0x01, 0x17, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 6688 - 6704 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x55, 0x01, 0x0e, 0x01, 0x60, 0x01, 0xff, 0x00, 0xab, /* bytes 6704 - 6720 */ + 0x00, 0x59, 0x02, 0x7f, 0x00, 0x5b, 0x02, 0x36, 0x01, 0xcf, 0x00, 0x1f, 0x01, 0x94, 0x00, 0x34, /* bytes 6720 - 6736 */ + 0x01, 0x59, 0x00, 0x80, 0x01, 0x73, 0x00, 0xb4, 0x01, 0x1c, 0x00, 0x90, 0x02, 0x33, 0x00, 0xae, /* bytes 6736 - 6752 */ + 0x02, 0x6f, 0x00, 0xcc, 0x02, 0x6b, 0x00, 0xeb, 0x02, 0x75, 0x00, 0xf1, 0x02, 0xa9, 0x00, 0xd6, /* bytes 6752 - 6768 */ + 0x02, 0xd6, 0x00, 0xb2, 0x02, 0xd3, 0x00, 0x98, 0x02, 0x15, 0x01, 0x50, 0x02, 0x35, 0x01, 0x34, /* bytes 6768 - 6784 */ + 0x02, 0x80, 0x01, 0x05, 0x02, 0x19, 0x01, 0x00, 0x02, 0x28, 0x01, 0xe6, 0x01, 0x6b, 0x01, 0xae, /* bytes 6784 - 6800 */ + 0x01, 0x19, 0x01, 0xb2, 0x01, 0xfc, 0x00, 0xb4, 0x01, 0xe5, 0x00, 0x9e, 0x01, 0x28, 0x01, 0x7a, /* bytes 6800 - 6816 */ + 0x01, 0x1b, 0x01, 0x5f, 0x01, 0x11, 0x01, 0x55, 0x01, 0x0e, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 6816 - 6832 */ + 0x01, 0x00, 0x02, 0x00, 0xab, 0x00, 0x59, 0x02, 0x7f, 0x00, 0x5b, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 6832 - 6848 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x50, 0x02, 0x35, 0x01, 0x78, 0x02, 0xcd, 0x00, 0x5c, 0x02, 0x8f, /* bytes 6848 - 6864 */ + 0x00, 0x0c, 0x02, 0xac, 0x00, 0xf3, 0x01, 0xf0, 0x00, 0x05, 0x02, 0x19, 0x01, 0xb2, 0x01, 0xfc, /* bytes 6864 - 6880 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x34, 0x02, 0x80, 0x01, 0x04, 0x02, /* bytes 6880 - 6896 */ + 0xfc, 0x01, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x04, 0x02, 0xe6, 0x01, /* bytes 6896 - 6912 */ + 0x6b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x55, 0x01, 0x0e, 0x01, 0x6e, /* bytes 6912 - 6928 */ + 0x01, 0xe4, 0x00, 0x54, 0x01, 0xae, 0x00, 0x36, 0x01, 0xcf, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 6928 - 6944 */ + 0x01, 0x00, 0x03, 0x00, 0xb4, 0x01, 0xe5, 0x00, 0x76, 0x01, 0x70, 0x00, 0x80, 0x01, 0x73, 0x00, /* bytes 6944 - 6960 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb2, 0x02, 0xd3, 0x00, 0xc3, 0x02, 0xa5, /* bytes 6960 - 6976 */ + 0x00, 0xae, 0x02, 0x6f, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 6976 - 6992 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xae, /* bytes 6992 - 7008 */ + 0x01, 0x19, 0x01, 0x00, 0x02, 0x28, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7008 - 7024 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 7024 - 7040 */ + 0xff, 0x00, 0x00, 0x1d, 0x00, 0xf8, 0x02, 0xbf, 0x00, 0xdb, 0x02, 0xeb, 0x00, 0xb7, 0x02, 0xe6, /* bytes 7040 - 7056 */ + 0x00, 0x9a, 0x02, 0x27, 0x01, 0x50, 0x02, 0x43, 0x01, 0x30, 0x02, 0x8d, 0x01, 0x07, 0x02, 0x23, /* bytes 7056 - 7072 */ + 0x01, 0x01, 0x02, 0x32, 0x01, 0xe4, 0x01, 0x74, 0x01, 0xb0, 0x01, 0x1f, 0x01, 0xb5, 0x01, 0x02, /* bytes 7072 - 7088 */ + 0x01, 0xbd, 0x01, 0xe9, 0x00, 0xa4, 0x01, 0x2b, 0x01, 0x81, 0x01, 0x1c, 0x01, 0x66, 0x01, 0x12, /* bytes 7088 - 7104 */ + 0x01, 0x5c, 0x01, 0x0e, 0x01, 0x65, 0x01, 0x00, 0x01, 0xab, 0x00, 0x59, 0x02, 0x7f, 0x00, 0x5b, /* bytes 7104 - 7120 */ + 0x02, 0x3f, 0x01, 0xce, 0x00, 0x2c, 0x01, 0x91, 0x00, 0x43, 0x01, 0x57, 0x00, 0x8e, 0x01, 0x75, /* bytes 7120 - 7136 */ + 0x00, 0xc2, 0x01, 0x22, 0x00, 0x9d, 0x02, 0x45, 0x00, 0xb8, 0x02, 0x82, 0x00, 0xd6, 0x02, 0x80, /* bytes 7136 - 7152 */ + 0x00, 0xf4, 0x02, 0x8c, 0x00, 0xf8, 0x02, 0xbf, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 7152 - 7168 */ + 0x02, 0x00, 0xab, 0x00, 0x59, 0x02, 0x7f, 0x00, 0x5b, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 7168 - 7184 */ + 0x00, 0x07, 0x00, 0xb5, 0x01, 0x02, 0x01, 0x07, 0x02, 0x23, 0x01, 0xf7, 0x01, 0xf9, 0x00, 0x13, /* bytes 7184 - 7200 */ + 0x02, 0xb7, 0x00, 0x64, 0x02, 0x9e, 0x00, 0x7d, 0x02, 0xdd, 0x00, 0x50, 0x02, 0x43, 0x01, 0x03, /* bytes 7200 - 7216 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x30, 0x02, 0x8d, 0x01, 0x04, 0x02, 0x04, 0x02, /* bytes 7216 - 7232 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0xfc, 0x01, 0xe4, 0x01, 0x74, 0x01, /* bytes 7232 - 7248 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x5c, 0x01, 0x0e, 0x01, 0x77, 0x01, 0xe5, /* bytes 7248 - 7264 */ + 0x00, 0x60, 0x01, 0xae, 0x00, 0x3f, 0x01, 0xce, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7264 - 7280 */ + 0x03, 0x00, 0xbd, 0x01, 0xe9, 0x00, 0x84, 0x01, 0x71, 0x00, 0x8e, 0x01, 0x75, 0x00, 0x03, 0x00, /* bytes 7280 - 7296 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb7, 0x02, 0xe6, 0x00, 0xca, 0x02, 0xb9, 0x00, 0xb8, /* bytes 7296 - 7312 */ + 0x02, 0x82, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 7312 - 7328 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, /* bytes 7328 - 7344 */ + 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x02, /* bytes 7344 - 7360 */ + 0x32, 0x01, 0xb0, 0x01, 0x1f, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 7360 - 7376 */ + 0x00, 0x1d, 0x00, 0x69, 0x01, 0x12, 0x01, 0x5f, 0x01, 0x0f, 0x01, 0x66, 0x01, 0xff, 0x00, 0xab, /* bytes 7376 - 7392 */ + 0x00, 0x59, 0x02, 0x7e, 0x00, 0x5b, 0x02, 0x3d, 0x01, 0xd2, 0x00, 0x26, 0x01, 0x96, 0x00, 0x3a, /* bytes 7392 - 7408 */ + 0x01, 0x5a, 0x00, 0x86, 0x01, 0x73, 0x00, 0xc0, 0x01, 0x21, 0x00, 0x9b, 0x02, 0x43, 0x00, 0xb7, /* bytes 7408 - 7424 */ + 0x02, 0x80, 0x00, 0xd5, 0x02, 0x7e, 0x00, 0xf3, 0x02, 0x89, 0x00, 0xf7, 0x02, 0xbd, 0x00, 0xda, /* bytes 7424 - 7440 */ + 0x02, 0xe9, 0x00, 0xb7, 0x02, 0xe4, 0x00, 0x9a, 0x02, 0x24, 0x01, 0x50, 0x02, 0x42, 0x01, 0x31, /* bytes 7440 - 7456 */ + 0x02, 0x8b, 0x01, 0x07, 0x02, 0x22, 0x01, 0x00, 0x02, 0x31, 0x01, 0xe4, 0x01, 0x73, 0x01, 0xaf, /* bytes 7456 - 7472 */ + 0x01, 0x1e, 0x01, 0xb5, 0x01, 0x02, 0x01, 0xbd, 0x01, 0xe4, 0x00, 0xa8, 0x01, 0x27, 0x01, 0x85, /* bytes 7472 - 7488 */ + 0x01, 0x1b, 0x01, 0x69, 0x01, 0x12, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7488 - 7504 */ + 0xab, 0x00, 0x59, 0x02, 0x7e, 0x00, 0x5b, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 7504 - 7520 */ + 0x00, 0x50, 0x02, 0x42, 0x01, 0x7d, 0x02, 0xdb, 0x00, 0x63, 0x02, 0x9c, 0x00, 0x13, 0x02, 0xb6, /* bytes 7520 - 7536 */ + 0x00, 0xf6, 0x01, 0xf8, 0x00, 0x07, 0x02, 0x22, 0x01, 0xb5, 0x01, 0x02, 0x01, 0x03, 0x00, 0xc0, /* bytes 7536 - 7552 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x5f, 0x01, 0x0f, 0x01, 0x77, 0x01, 0xe5, 0x00, 0x5c, 0x01, /* bytes 7552 - 7568 */ + 0xaf, 0x00, 0x3d, 0x01, 0xd2, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x64, /* bytes 7568 - 7584 */ + 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, /* bytes 7584 - 7600 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb7, 0x02, 0x80, 0x00, 0xc9, 0x02, 0xb6, 0x00, /* bytes 7600 - 7616 */ + 0xb7, 0x02, 0xe4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x31, 0x02, 0x8b, /* bytes 7616 - 7632 */ + 0x01, 0x03, 0x02, 0x01, 0x02, 0xb4, 0x01, 0x5d, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7632 - 7648 */ + 0x03, 0x00, 0xe4, 0x01, 0x73, 0x01, 0xb4, 0x01, 0x01, 0x02, 0x62, 0x01, 0x53, 0x02, 0x03, 0x00, /* bytes 7648 - 7664 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xbd, 0x01, 0xe4, 0x00, 0x7b, 0x01, 0x6f, 0x00, 0x86, /* bytes 7664 - 7680 */ + 0x01, 0x73, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x31, 0x01, /* bytes 7680 - 7696 */ + 0xaf, 0x01, 0x1e, 0x01, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x25, /* bytes 7696 - 7712 */ + 0x00, 0x4a, 0x02, 0x24, 0x01, 0xf5, 0x01, 0xda, 0x00, 0xf5, 0x01, 0xc9, 0x00, 0xd3, 0x01, 0x8e, /* bytes 7712 - 7728 */ + 0x00, 0x98, 0x01, 0xb4, 0x00, 0x71, 0x01, 0x7b, 0x00, 0x74, 0x01, 0x64, 0x00, 0x6e, 0x01, 0x5a, /* bytes 7728 - 7744 */ + 0x00, 0x38, 0x01, 0xf0, 0xff, 0x01, 0x02, 0x3b, 0xff, 0x06, 0x02, 0x19, 0xff, 0x11, 0x02, 0x16, /* bytes 7744 - 7760 */ + 0xff, 0x1f, 0x02, 0x06, 0xff, 0x4c, 0x02, 0x0d, 0xff, 0x64, 0x02, 0x2d, 0xff, 0x5e, 0x02, 0x5a, /* bytes 7760 - 7776 */ + 0xff, 0x4b, 0x02, 0x63, 0xff, 0x44, 0x02, 0x6d, 0xff, 0x22, 0x02, 0x68, 0xff, 0x82, 0x01, 0x06, /* bytes 7776 - 7792 */ + 0x00, 0x85, 0x01, 0xfa, 0xff, 0xc8, 0x01, 0xd1, 0xff, 0xdc, 0x01, 0xf2, 0xff, 0xe7, 0x01, 0xdb, /* bytes 7792 - 7808 */ + 0xff, 0xa2, 0x02, 0xd9, 0xff, 0xb9, 0x02, 0xfd, 0xff, 0xdb, 0x02, 0xdf, 0xff, 0x0b, 0x03, 0xe2, /* bytes 7808 - 7824 */ + 0xff, 0x23, 0x03, 0x0c, 0x00, 0x1f, 0x03, 0x4c, 0x00, 0xe8, 0x02, 0x88, 0x00, 0xeb, 0x02, 0x9c, /* bytes 7824 - 7840 */ + 0x00, 0xa6, 0x02, 0xd8, 0x00, 0x9b, 0x02, 0x27, 0x01, 0x57, 0x02, 0xcd, 0x00, 0x5b, 0x02, 0xdf, /* bytes 7840 - 7856 */ + 0x00, 0x4a, 0x02, 0x24, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5d, 0x00, /* bytes 7856 - 7872 */ + 0x2c, 0x02, 0x51, 0x00, 0x21, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, /* bytes 7872 - 7888 */ + 0x00, 0x3c, 0x02, 0x85, 0x00, 0x2d, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 7888 - 7904 */ + 0x11, 0x02, 0x16, 0xff, 0x4b, 0x02, 0x63, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, /* bytes 7904 - 7920 */ + 0x00, 0x70, 0x00, 0x1b, 0x02, 0x72, 0x00, 0x2e, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 7920 - 7936 */ + 0x07, 0x00, 0xf5, 0x01, 0xc9, 0x00, 0x57, 0x02, 0xcd, 0x00, 0x3c, 0x02, 0xa9, 0x00, 0x46, 0x02, /* bytes 7936 - 7952 */ + 0x61, 0x00, 0x8d, 0x02, 0x33, 0x00, 0xb6, 0x02, 0x69, 0x00, 0xa6, 0x02, 0xd8, 0x00, 0x03, 0x00, /* bytes 7952 - 7968 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x82, 0x01, 0x06, 0x00, 0xab, 0x01, 0x28, 0x00, 0x9d, /* bytes 7968 - 7984 */ + 0x01, 0x62, 0x00, 0x6e, 0x01, 0x5a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, /* bytes 7984 - 8000 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, /* bytes 8000 - 8016 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4a, 0x02, 0x24, 0x01, 0x87, 0x01, 0x04, /* bytes 8016 - 8032 */ + 0x02, 0x32, 0x01, 0x56, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xd3, 0x01, /* bytes 8032 - 8048 */ + 0x8e, 0x00, 0xec, 0x01, 0x0a, 0x00, 0xdc, 0x01, 0xf2, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8048 - 8064 */ + 0x00, 0x03, 0x00, 0x94, 0x01, 0x4a, 0x02, 0xe9, 0x01, 0xf7, 0x01, 0x9b, 0x02, 0x27, 0x01, 0x03, /* bytes 8064 - 8080 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xb9, 0x02, 0xfd, 0xff, 0xf3, 0x02, 0x24, 0x00, /* bytes 8080 - 8096 */ + 0xe8, 0x02, 0x88, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x5b, 0x02, 0xdf, /* bytes 8096 - 8112 */ + 0x00, 0xf5, 0x01, 0xda, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, /* bytes 8112 - 8128 */ + 0x26, 0x00, 0x13, 0x02, 0xdb, 0xfe, 0x1f, 0x02, 0xca, 0xfe, 0x4c, 0x02, 0xc9, 0xfe, 0x69, 0x02, /* bytes 8128 - 8144 */ + 0xe5, 0xfe, 0x69, 0x02, 0x12, 0xff, 0x58, 0x02, 0x1e, 0xff, 0x53, 0x02, 0x29, 0xff, 0x31, 0x02, /* bytes 8144 - 8160 */ + 0x2a, 0xff, 0xa5, 0x01, 0xdd, 0xff, 0xa8, 0x01, 0xd6, 0xff, 0xf3, 0x01, 0xbb, 0xff, 0xfe, 0x01, /* bytes 8160 - 8176 */ + 0xdb, 0xff, 0xf0, 0x01, 0xcf, 0xff, 0xa8, 0x02, 0xca, 0xff, 0xd9, 0x02, 0xe1, 0xff, 0xe0, 0x02, /* bytes 8176 - 8192 */ + 0xcc, 0xff, 0x10, 0x03, 0xcd, 0xff, 0x2f, 0x03, 0xf6, 0xff, 0x2e, 0x03, 0x36, 0x00, 0xf5, 0x02, /* bytes 8192 - 8208 */ + 0x74, 0x00, 0xfc, 0x02, 0x84, 0x00, 0xab, 0x02, 0xcd, 0x00, 0x99, 0x02, 0x1b, 0x01, 0x5e, 0x02, /* bytes 8208 - 8224 */ + 0xbb, 0x00, 0x6b, 0x02, 0xd2, 0x00, 0x49, 0x02, 0x10, 0x01, 0x03, 0x02, 0xd0, 0x00, 0x03, 0x02, /* bytes 8224 - 8240 */ + 0xc0, 0x00, 0xcc, 0x01, 0x7d, 0x00, 0xc5, 0x01, 0x7a, 0x00, 0x90, 0x01, 0x8d, 0x00, 0x79, 0x01, /* bytes 8240 - 8256 */ + 0x4d, 0x00, 0x83, 0x01, 0x38, 0x00, 0x7f, 0x01, 0x2d, 0x00, 0x60, 0x01, 0xdf, 0xff, 0x09, 0x02, /* bytes 8256 - 8272 */ + 0x03, 0xff, 0x08, 0x02, 0xe1, 0xfe, 0x13, 0x02, 0xdb, 0xfe, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 8272 - 8288 */ + 0x00, 0x02, 0x00, 0x8a, 0x00, 0x29, 0x02, 0x82, 0x00, 0x3a, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 8288 - 8304 */ + 0x01, 0x00, 0x02, 0x00, 0x59, 0x00, 0x2a, 0x02, 0x46, 0x00, 0x1c, 0x02, 0x03, 0x00, 0xc0, 0x02, /* bytes 8304 - 8320 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x13, 0x02, 0xdb, 0xfe, 0x58, 0x02, 0x1e, 0xff, 0x03, 0x00, 0xc0, /* bytes 8320 - 8336 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x72, 0x00, 0x2b, 0x02, 0x6e, 0x00, 0x13, 0x02, 0x03, 0x00, /* bytes 8336 - 8352 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x03, 0x02, 0xc0, 0x00, 0x5e, 0x02, 0xbb, 0x00, 0x46, /* bytes 8352 - 8368 */ + 0x02, 0x94, 0x00, 0x57, 0x02, 0x4e, 0x00, 0xa2, 0x02, 0x27, 0x00, 0xc6, 0x02, 0x61, 0x00, 0xab, /* bytes 8368 - 8384 */ + 0x02, 0xcd, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x7f, 0x01, 0x2d, 0x00, /* bytes 8384 - 8400 */ + 0xab, 0x01, 0x41, 0x00, 0xc4, 0x01, 0x0b, 0x00, 0xa5, 0x01, 0xdd, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 8400 - 8416 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, /* bytes 8416 - 8432 */ + 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xd9, 0x02, /* bytes 8432 - 8448 */ + 0xe1, 0xff, 0xfe, 0x02, 0x0c, 0x00, 0xf5, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8448 - 8464 */ + 0x00, 0x03, 0x00, 0xfe, 0x01, 0xdb, 0xff, 0x0a, 0x02, 0xfb, 0xff, 0xcc, 0x01, 0x7d, 0x00, 0x03, /* bytes 8464 - 8480 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x49, 0x02, 0x10, 0x01, 0xaa, 0x01, 0xd0, 0x01, /* bytes 8480 - 8496 */ + 0x4b, 0x01, 0x29, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xbe, 0x01, 0x07, /* bytes 8496 - 8512 */ + 0x02, 0x19, 0x02, 0xba, 0x01, 0x99, 0x02, 0x1b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 8512 - 8528 */ + 0x02, 0x00, 0x03, 0x02, 0xd0, 0x00, 0x6b, 0x02, 0xd2, 0x00, 0x23, 0x00, 0x0d, 0x00, 0x03, 0x00, /* bytes 8528 - 8544 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xb4, 0x01, 0x6a, 0x00, 0xac, 0x01, 0x67, 0x00, 0x7b, /* bytes 8544 - 8560 */ + 0x01, 0x82, 0x00, 0x5a, 0x01, 0x46, 0x00, 0x61, 0x01, 0x2f, 0x00, 0x5c, 0x01, 0x25, 0x00, 0x2d, /* bytes 8560 - 8576 */ + 0x01, 0xe3, 0xff, 0xa3, 0x01, 0xff, 0xfe, 0x9d, 0x01, 0xdd, 0xfe, 0xa7, 0x01, 0xd6, 0xfe, 0xb0, /* bytes 8576 - 8592 */ + 0x01, 0xc3, 0xfe, 0xdc, 0x01, 0xbb, 0xfe, 0xfd, 0x01, 0xd2, 0xfe, 0x05, 0x02, 0xfe, 0xfe, 0xf6, /* bytes 8592 - 8608 */ + 0x01, 0x0d, 0xff, 0xf3, 0x01, 0x18, 0xff, 0xd1, 0x01, 0x1e, 0xff, 0x75, 0x01, 0xd1, 0xff, 0x78, /* bytes 8608 - 8624 */ + 0x01, 0xc9, 0xff, 0xbe, 0x01, 0xa4, 0xff, 0xce, 0x01, 0xc2, 0xff, 0xbf, 0x01, 0xae, 0xff, 0x90, /* bytes 8624 - 8640 */ + 0x02, 0xb2, 0xff, 0xc4, 0x02, 0xc5, 0xff, 0xcd, 0x02, 0xaf, 0xff, 0xff, 0x02, 0xb0, 0xff, 0x1f, /* bytes 8640 - 8656 */ + 0x03, 0xdc, 0xff, 0x1e, 0x03, 0x19, 0x00, 0xe9, 0x02, 0x4e, 0x00, 0xe7, 0x02, 0x68, 0x00, 0x97, /* bytes 8656 - 8672 */ + 0x02, 0xa7, 0x00, 0x81, 0x02, 0xf4, 0x00, 0x4a, 0x02, 0x91, 0x00, 0x3a, 0x02, 0xb1, 0x00, 0x32, /* bytes 8672 - 8688 */ + 0x02, 0xe6, 0x00, 0xd2, 0x01, 0xaf, 0x00, 0xd2, 0x01, 0x9f, 0x00, 0xb4, 0x01, 0x6a, 0x00, 0x03, /* bytes 8688 - 8704 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x92, 0x00, 0x25, 0x02, 0x85, 0x00, 0x39, 0x02, /* bytes 8704 - 8720 */ + 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf6, 0x01, 0x0d, 0xff, 0xa7, 0x01, 0xd6, /* bytes 8720 - 8736 */ + 0xfe, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3f, 0x00, 0x22, 0x02, 0x53, 0x00, /* bytes 8736 - 8752 */ + 0x2e, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0x28, 0x02, 0x6a, /* bytes 8752 - 8768 */ + 0x00, 0x07, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x97, 0x02, 0xa7, 0x00, /* bytes 8768 - 8784 */ + 0xb6, 0x02, 0x3b, 0x00, 0x95, 0x02, 0x00, 0x00, 0x48, 0x02, 0x24, 0x00, 0x34, 0x02, 0x69, 0x00, /* bytes 8784 - 8800 */ + 0x4a, 0x02, 0x91, 0x00, 0xd2, 0x01, 0x9f, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 8800 - 8816 */ + 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, /* bytes 8816 - 8832 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x5c, 0x01, 0x25, 0x00, 0x8a, 0x01, /* bytes 8832 - 8848 */ + 0x33, 0x00, 0x9b, 0x01, 0xfa, 0xff, 0x75, 0x01, 0xd1, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 8848 - 8864 */ + 0x00, 0x03, 0x00, 0xb4, 0x01, 0x6a, 0x00, 0xde, 0x01, 0xe0, 0xff, 0xce, 0x01, 0xc2, 0xff, 0x03, /* bytes 8864 - 8880 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xa7, 0x01, 0x22, 0x02, 0xfb, 0x01, 0x7d, 0x01, /* bytes 8880 - 8896 */ + 0x81, 0x02, 0xf4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4c, 0x01, 0x32, /* bytes 8896 - 8912 */ + 0x02, 0xad, 0x01, 0x96, 0x01, 0x32, 0x02, 0xe6, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 8912 - 8928 */ + 0x03, 0x00, 0xe9, 0x02, 0x4e, 0x00, 0xe9, 0x02, 0xf0, 0xff, 0xc4, 0x02, 0xc5, 0xff, 0x03, 0x00, /* bytes 8928 - 8944 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3a, 0x02, 0xb1, 0x00, 0xd2, 0x01, 0xaf, 0x00, 0x23, /* bytes 8944 - 8960 */ + 0x00, 0x0d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xcf, 0x00, 0x67, 0xff, /* bytes 8960 - 8976 */ + 0xaf, 0x00, 0x87, 0xff, 0xb2, 0x00, 0x9c, 0xff, 0xaf, 0x00, 0xa7, 0xff, 0xc7, 0x00, 0xbf, 0xff, /* bytes 8976 - 8992 */ + 0xb9, 0x00, 0x35, 0x00, 0xee, 0x00, 0x6a, 0x00, 0xf4, 0x00, 0x74, 0x00, 0xef, 0x00, 0x8b, 0x00, /* bytes 8992 - 9008 */ + 0x14, 0x01, 0xc4, 0x00, 0x43, 0x01, 0xa6, 0x00, 0x4b, 0x01, 0xa7, 0x00, 0x79, 0x01, 0xc5, 0x00, /* bytes 9008 - 9024 */ + 0x79, 0x01, 0xd5, 0x00, 0xeb, 0x01, 0xff, 0x00, 0xe1, 0x01, 0xd5, 0x00, 0x06, 0x02, 0xab, 0x00, /* bytes 9024 - 9040 */ + 0x3a, 0x02, 0x0f, 0x01, 0x52, 0x02, 0xc3, 0x00, 0xa9, 0x02, 0x75, 0x00, 0xaa, 0x02, 0x64, 0x00, /* bytes 9040 - 9056 */ + 0xf0, 0x02, 0x1c, 0x00, 0xeb, 0x02, 0xd9, 0xff, 0xbc, 0x02, 0xb1, 0xff, 0x93, 0x02, 0xb1, 0xff, /* bytes 9056 - 9072 */ + 0x83, 0x02, 0xd2, 0xff, 0x51, 0x02, 0xbd, 0xff, 0x61, 0x01, 0xd5, 0xff, 0x57, 0x01, 0xfe, 0xff, /* bytes 9072 - 9088 */ + 0x45, 0x01, 0xe2, 0xff, 0x02, 0x01, 0x0d, 0x00, 0x01, 0x01, 0x14, 0x00, 0xff, 0x00, 0xbf, 0xff, /* bytes 9088 - 9104 */ + 0x17, 0x01, 0xa7, 0xff, 0x12, 0x01, 0x9c, 0xff, 0x17, 0x01, 0x87, 0xff, 0xf7, 0x00, 0x67, 0xff, /* bytes 9104 - 9120 */ + 0xcf, 0x00, 0x67, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0x00, 0x2f, /* bytes 9120 - 9136 */ + 0x02, 0x2e, 0x00, 0x27, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x67, 0x00, /* bytes 9136 - 9152 */ + 0xf7, 0x01, 0x6a, 0x00, 0x12, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xa5, /* bytes 9152 - 9168 */ + 0x00, 0x1e, 0x02, 0x92, 0x00, 0x31, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 9168 - 9184 */ + 0xb2, 0x00, 0x9c, 0xff, 0x12, 0x01, 0x9c, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 9184 - 9200 */ + 0x00, 0x79, 0x01, 0xc5, 0x00, 0x06, 0x02, 0xab, 0x00, 0xf1, 0x01, 0x83, 0x00, 0x06, 0x02, 0x3e, /* bytes 9200 - 9216 */ + 0x00, 0x54, 0x02, 0x1c, 0x00, 0x73, 0x02, 0x58, 0x00, 0x52, 0x02, 0xc3, 0x00, 0x03, 0x00, 0xc0, /* bytes 9216 - 9232 */ + 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, /* bytes 9232 - 9248 */ + 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xee, /* bytes 9248 - 9264 */ + 0x00, 0x6a, 0x00, 0x1d, 0x01, 0x74, 0x00, 0x2a, 0x01, 0x3a, 0x00, 0x01, 0x01, 0x14, 0x00, 0x03, /* bytes 9264 - 9280 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xeb, 0x01, 0xff, 0x00, 0x5d, 0x01, 0xa6, 0x01, /* bytes 9280 - 9296 */ + 0x4c, 0x01, 0x42, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x57, 0x01, 0xfe, /* bytes 9296 - 9312 */ + 0xff, 0x6a, 0x01, 0x1b, 0x00, 0x4b, 0x01, 0xa7, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 9312 - 9328 */ + 0x03, 0x00, 0xaa, 0x02, 0x64, 0x00, 0xa9, 0x02, 0xfd, 0xff, 0x83, 0x02, 0xd2, 0xff, 0x03, 0x00, /* bytes 9328 - 9344 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xa7, 0x01, 0x32, 0x02, 0xfb, 0x01, 0xdd, 0x01, 0x3a, /* bytes 9344 - 9360 */ + 0x02, 0x0f, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xe1, 0x01, 0xd5, 0x00, /* bytes 9360 - 9376 */ + 0x79, 0x01, 0xd5, 0x00, 0x23, 0x00, 0x0c, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 9376 - 9392 */ + 0x00, 0xde, 0x00, 0x68, 0x01, 0xd6, 0x00, 0x60, 0x01, 0x9b, 0x00, 0x26, 0x01, 0x83, 0x00, 0x7a, /* bytes 9392 - 9408 */ + 0x00, 0x6b, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x6b, 0x00, 0x42, 0x00, 0x8b, 0x00, 0x22, /* bytes 9408 - 9424 */ + 0x00, 0xb3, 0x00, 0x22, 0x00, 0xd3, 0x00, 0x42, 0x00, 0xcf, 0x00, 0x56, 0x00, 0xd3, 0x00, 0x62, /* bytes 9424 - 9440 */ + 0x00, 0xbb, 0x00, 0x7a, 0x00, 0xd1, 0x00, 0x08, 0x01, 0xd1, 0x00, 0x00, 0x01, 0x06, 0x01, 0xc5, /* bytes 9440 - 9456 */ + 0x00, 0x1f, 0x01, 0xdc, 0x00, 0x4b, 0x01, 0x96, 0x00, 0x1f, 0x02, 0x6d, 0x00, 0x41, 0x02, 0x85, /* bytes 9456 - 9472 */ + 0x00, 0x60, 0x02, 0x67, 0x00, 0x8e, 0x02, 0x66, 0x00, 0xb5, 0x02, 0x8f, 0x00, 0xb4, 0x02, 0xce, /* bytes 9472 - 9488 */ + 0x00, 0x77, 0x02, 0x08, 0x01, 0x77, 0x02, 0x25, 0x01, 0x45, 0x02, 0x7b, 0x01, 0x3f, 0x02, 0xcb, /* bytes 9488 - 9504 */ + 0x01, 0xf5, 0x01, 0x75, 0x01, 0xcb, 0x01, 0x95, 0x01, 0xee, 0x01, 0xcd, 0x01, 0x63, 0x01, 0x95, /* bytes 9504 - 9520 */ + 0x01, 0x63, 0x01, 0x85, 0x01, 0x40, 0x01, 0x83, 0x01, 0x38, 0x01, 0x83, 0x01, 0x12, 0x01, 0xad, /* bytes 9520 - 9536 */ + 0x01, 0xe0, 0x00, 0x80, 0x01, 0xde, 0x00, 0x68, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 9536 - 9552 */ + 0x02, 0x00, 0xcf, 0x00, 0x56, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, /* bytes 9552 - 9568 */ + 0x00, 0x02, 0x00, 0x95, 0x00, 0x2e, 0x02, 0xa6, 0x00, 0x1d, 0x02, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 9568 - 9584 */ + 0x01, 0x00, 0x02, 0x00, 0x66, 0x00, 0xf6, 0x01, 0x6a, 0x00, 0x0e, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 9584 - 9600 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x63, 0x01, 0x85, 0x01, 0xf5, 0x01, 0x75, 0x01, 0xd8, 0x01, 0x53, /* bytes 9600 - 9616 */ + 0x01, 0xdd, 0x01, 0x0b, 0x01, 0x21, 0x02, 0xd8, 0x00, 0x4e, 0x02, 0x0c, 0x01, 0x45, 0x02, 0x7b, /* bytes 9616 - 9632 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x3f, 0x02, 0xcb, 0x01, 0x04, 0x02, /* bytes 9632 - 9648 */ + 0x3c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xac, 0x01, 0x34, 0x02, 0xee, 0x01, /* bytes 9648 - 9664 */ + 0xcd, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xd6, 0x00, 0x60, 0x01, 0x06, /* bytes 9664 - 9680 */ + 0x01, 0x5e, 0x01, 0x03, 0x01, 0x22, 0x01, 0xd1, 0x00, 0x08, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 9680 - 9696 */ + 0x01, 0x00, 0x03, 0x00, 0x41, 0x02, 0x85, 0x00, 0x77, 0x02, 0xad, 0x00, 0x77, 0x02, 0x08, 0x01, /* bytes 9696 - 9712 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x1f, 0x01, 0xdc, 0x00, 0x38, 0x01, 0xf3, /* bytes 9712 - 9728 */ + 0x00, 0x40, 0x01, 0x83, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 9728 - 9744 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, /* bytes 9744 - 9760 */ + 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 9760 - 9776 */ + 0xcb, 0x01, 0x95, 0x01, 0x63, 0x01, 0x95, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 9776 - 9792 */ + 0xff, 0x00, 0x00, 0x26, 0x00, 0xb9, 0x00, 0x2f, 0x01, 0xac, 0x00, 0x63, 0x00, 0xc4, 0x00, 0x4b, /* bytes 9792 - 9808 */ + 0x00, 0xc0, 0x00, 0x3f, 0x00, 0xc4, 0x00, 0x2b, 0x00, 0xa4, 0x00, 0x0b, 0x00, 0x7c, 0x00, 0x0b, /* bytes 9808 - 9824 */ + 0x00, 0x5c, 0x00, 0x2b, 0x00, 0x60, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x4b, 0x00, 0x74, 0x00, 0x63, /* bytes 9824 - 9840 */ + 0x00, 0x87, 0x00, 0x4d, 0x01, 0xbb, 0x00, 0x87, 0x01, 0xc3, 0x00, 0x8f, 0x01, 0xc4, 0x00, 0xa7, /* bytes 9840 - 9856 */ + 0x01, 0xf5, 0x00, 0xd5, 0x01, 0x1c, 0x01, 0xac, 0x01, 0x24, 0x01, 0xac, 0x01, 0x4f, 0x01, 0xad, /* bytes 9856 - 9872 */ + 0x01, 0x4f, 0x01, 0xbd, 0x01, 0xc4, 0x01, 0xfb, 0x01, 0xb7, 0x01, 0xbc, 0x01, 0xc8, 0x01, 0xa3, /* bytes 9872 - 9888 */ + 0x01, 0x14, 0x02, 0xf7, 0x01, 0x18, 0x02, 0xa7, 0x01, 0x44, 0x02, 0x6a, 0x01, 0x41, 0x02, 0x53, /* bytes 9888 - 9904 */ + 0x01, 0x7f, 0x02, 0x17, 0x01, 0x7f, 0x02, 0xd8, 0x00, 0x59, 0x02, 0xaf, 0x00, 0x2a, 0x02, 0xaf, /* bytes 9904 - 9920 */ + 0x00, 0x0a, 0x02, 0xce, 0x00, 0xe0, 0x01, 0xc2, 0x00, 0x37, 0x01, 0xbd, 0x00, 0x07, 0x01, 0x05, /* bytes 9920 - 9936 */ + 0x01, 0xef, 0x00, 0xed, 0x00, 0xb8, 0x00, 0x27, 0x01, 0xb9, 0x00, 0x2f, 0x01, 0x03, 0x00, 0xc0, /* bytes 9936 - 9952 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x60, 0x00, 0x3f, 0x00, 0xc0, 0x00, 0x3f, 0x00, 0x03, 0x00, /* bytes 9952 - 9968 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x18, 0x02, 0xa7, 0x01, 0x1e, 0x02, 0x37, 0x01, 0xf1, /* bytes 9968 - 9984 */ + 0x01, 0x05, 0x01, 0xae, 0x01, 0x39, 0x01, 0xaa, 0x01, 0x81, 0x01, 0xc8, 0x01, 0xa3, 0x01, 0x4f, /* bytes 9984 - 10000 */ + 0x01, 0xad, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x14, 0x02, 0xf7, 0x01, /* bytes 10000 - 10016 */ + 0x1c, 0x02, 0x41, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xca, 0x01, 0x31, 0x02, /* bytes 10016 - 10032 */ + 0xc4, 0x01, 0xfb, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xb9, 0x00, 0x2f, /* bytes 10032 - 10048 */ + 0x01, 0xe9, 0x00, 0x49, 0x01, 0xeb, 0x00, 0x85, 0x01, 0xbb, 0x00, 0x87, 0x01, 0x03, 0x00, 0xc0, /* bytes 10048 - 10064 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0x01, 0x05, 0x01, 0x20, 0x01, 0x1c, 0x01, 0x24, 0x01, /* bytes 10064 - 10080 */ + 0xac, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0a, 0x02, 0xce, 0x00, 0x44, /* bytes 10080 - 10096 */ + 0x02, 0xf2, 0x00, 0x41, 0x02, 0x53, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 10096 - 10112 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 10112 - 10128 */ + 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 10128 - 10144 */ + 0x02, 0x00, 0x4f, 0x01, 0xbd, 0x01, 0xb7, 0x01, 0xbc, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 10144 - 10160 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x70, 0x02, 0x10, 0x01, 0x31, 0x02, 0x4f, 0x01, 0x32, /* bytes 10160 - 10176 */ + 0x02, 0x67, 0x01, 0xfa, 0x01, 0xa2, 0x01, 0xf8, 0x01, 0xf2, 0x01, 0xaa, 0x01, 0xa1, 0x01, 0xa9, /* bytes 10176 - 10192 */ + 0x01, 0xb2, 0x01, 0xa8, 0x01, 0xf9, 0x01, 0x41, 0x01, 0xb2, 0x01, 0x41, 0x01, 0xa2, 0x01, 0x14, /* bytes 10192 - 10208 */ + 0x01, 0xa3, 0x01, 0x0b, 0x01, 0xa3, 0x01, 0xe4, 0x00, 0xcb, 0x01, 0xb3, 0x00, 0x9b, 0x01, 0xb3, /* bytes 10208 - 10224 */ + 0x00, 0x83, 0x01, 0xab, 0x00, 0x7b, 0x01, 0x79, 0x00, 0x43, 0x01, 0x7f, 0x00, 0x4b, 0x00, 0x67, /* bytes 10224 - 10240 */ + 0x00, 0x33, 0x00, 0x6b, 0x00, 0x27, 0x00, 0x67, 0x00, 0x13, 0x00, 0x87, 0x00, 0xf3, 0xff, 0xaf, /* bytes 10240 - 10256 */ + 0x00, 0xf3, 0xff, 0xcf, 0x00, 0x13, 0x00, 0xcb, 0x00, 0x27, 0x00, 0xcf, 0x00, 0x33, 0x00, 0xb7, /* bytes 10256 - 10272 */ + 0x00, 0x4b, 0x00, 0xab, 0x00, 0x23, 0x01, 0xab, 0x00, 0x1b, 0x01, 0xe3, 0x00, 0xe3, 0x00, 0xfb, /* bytes 10272 - 10288 */ + 0x00, 0xfb, 0x00, 0x28, 0x01, 0xb2, 0x00, 0xd6, 0x01, 0xb4, 0x00, 0xfa, 0x01, 0xc8, 0x00, 0x19, /* bytes 10288 - 10304 */ + 0x02, 0xa9, 0x00, 0x49, 0x02, 0xa8, 0x00, 0x71, 0x02, 0xd1, 0x00, 0x70, 0x02, 0x10, 0x01, 0x03, /* bytes 10304 - 10320 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6b, 0x00, 0x27, 0x00, 0xcb, 0x00, 0x27, 0x00, /* bytes 10320 - 10336 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xfa, 0x01, 0xa2, 0x01, 0xfc, 0x01, 0x32, /* bytes 10336 - 10352 */ + 0x01, 0xcd, 0x01, 0x01, 0x01, 0x8c, 0x01, 0x38, 0x01, 0x8a, 0x01, 0x80, 0x01, 0xaa, 0x01, 0xa1, /* bytes 10352 - 10368 */ + 0x01, 0x41, 0x01, 0xa2, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xa8, 0x01, /* bytes 10368 - 10384 */ + 0xf9, 0x01, 0xac, 0x01, 0x34, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, /* bytes 10384 - 10400 */ + 0x3c, 0x02, 0xf8, 0x01, 0xf2, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xab, /* bytes 10400 - 10416 */ + 0x00, 0x23, 0x01, 0xdb, 0x00, 0x3f, 0x01, 0xdb, 0x00, 0x7b, 0x01, 0xab, 0x00, 0x7b, 0x01, 0x03, /* bytes 10416 - 10432 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xfb, 0x00, 0xfb, 0x00, 0x13, 0x01, 0x13, 0x01, /* bytes 10432 - 10448 */ + 0x14, 0x01, 0xa3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xfa, 0x01, 0xc8, /* bytes 10448 - 10464 */ + 0x00, 0x32, 0x02, 0xef, 0x00, 0x31, 0x02, 0x4f, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 10464 - 10480 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 10480 - 10496 */ + 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 10496 - 10512 */ + 0x01, 0x00, 0x02, 0x00, 0xa9, 0x01, 0xb2, 0x01, 0x41, 0x01, 0xb2, 0x01, 0x23, 0x00, 0x0a, 0x00, /* bytes 10512 - 10528 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xba, 0x00, 0xff, 0xff, 0xa3, 0x00, 0xe4, /* bytes 10528 - 10544 */ + 0x00, 0xa3, 0x00, 0xdc, 0x00, 0xdb, 0x00, 0xa4, 0x00, 0xf3, 0x00, 0xbc, 0x00, 0x1e, 0x01, 0x73, /* bytes 10544 - 10560 */ + 0x00, 0xcd, 0x01, 0x74, 0x00, 0xee, 0x01, 0x8a, 0x00, 0x0e, 0x02, 0x6b, 0x00, 0x3e, 0x02, 0x6a, /* bytes 10560 - 10576 */ + 0x00, 0x66, 0x02, 0x93, 0x00, 0x66, 0x02, 0xd2, 0x00, 0x26, 0x02, 0x12, 0x01, 0x26, 0x02, 0x2a, /* bytes 10576 - 10592 */ + 0x01, 0xee, 0x01, 0x63, 0x01, 0xee, 0x01, 0xb3, 0x01, 0x9e, 0x01, 0x63, 0x01, 0x9e, 0x01, 0x73, /* bytes 10592 - 10608 */ + 0x01, 0x9e, 0x01, 0xbb, 0x01, 0x36, 0x01, 0x73, 0x01, 0x36, 0x01, 0x63, 0x01, 0x0b, 0x01, 0x64, /* bytes 10608 - 10624 */ + 0x01, 0x03, 0x01, 0x64, 0x01, 0xdb, 0x00, 0x8c, 0x01, 0xab, 0x00, 0x5c, 0x01, 0xab, 0x00, 0x44, /* bytes 10624 - 10640 */ + 0x01, 0xa3, 0x00, 0x3b, 0x01, 0x6e, 0x00, 0x04, 0x01, 0x82, 0x00, 0xff, 0xff, 0x6a, 0x00, 0xe7, /* bytes 10640 - 10656 */ + 0xff, 0x6e, 0x00, 0xdb, 0xff, 0x6b, 0x00, 0xc7, 0xff, 0x8a, 0x00, 0xa7, 0xff, 0xb3, 0x00, 0xa7, /* bytes 10656 - 10672 */ + 0xff, 0xd2, 0x00, 0xc7, 0xff, 0xce, 0x00, 0xdb, 0xff, 0xd2, 0x00, 0xe7, 0xff, 0xba, 0x00, 0xff, /* bytes 10672 - 10688 */ + 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x6e, 0x00, 0xdb, 0xff, 0xce, 0x00, /* bytes 10688 - 10704 */ + 0xdb, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0xee, 0x01, 0x63, 0x01, 0xee, /* bytes 10704 - 10720 */ + 0x01, 0xf3, 0x00, 0xbe, 0x01, 0xc3, 0x00, 0x7e, 0x01, 0xfb, 0x00, 0x7e, 0x01, 0x43, 0x01, 0x9e, /* bytes 10720 - 10736 */ + 0x01, 0x63, 0x01, 0x36, 0x01, 0x63, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 10736 - 10752 */ + 0x9e, 0x01, 0xbb, 0x01, 0xb1, 0x01, 0x29, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 10752 - 10768 */ + 0x04, 0x02, 0x35, 0x02, 0xee, 0x01, 0xb3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 10768 - 10784 */ + 0x00, 0xa3, 0x00, 0x3b, 0x01, 0xd3, 0x00, 0x3b, 0x01, 0xd3, 0x00, 0xff, 0x00, 0xa3, 0x00, 0xe4, /* bytes 10784 - 10800 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xf3, 0x00, 0xbc, 0x00, 0x0b, 0x01, /* bytes 10800 - 10816 */ + 0xd3, 0x00, 0x0b, 0x01, 0x64, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0xee, /* bytes 10816 - 10832 */ + 0x01, 0x8a, 0x00, 0x26, 0x02, 0xb2, 0x00, 0x26, 0x02, 0x12, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 10832 - 10848 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 10848 - 10864 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 10864 - 10880 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x9e, 0x01, 0x73, 0x01, 0x36, 0x01, 0x73, 0x01, 0x23, 0x00, /* bytes 10880 - 10896 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x0b, 0x02, 0x91, 0x01, 0xbb, /* bytes 10896 - 10912 */ + 0x01, 0x41, 0x01, 0xbb, 0x01, 0x51, 0x01, 0xbb, 0x01, 0x99, 0x01, 0x53, 0x01, 0x51, 0x01, 0x53, /* bytes 10912 - 10928 */ + 0x01, 0x41, 0x01, 0x2a, 0x01, 0x41, 0x01, 0x22, 0x01, 0x41, 0x01, 0xfa, 0x00, 0x69, 0x01, 0xca, /* bytes 10928 - 10944 */ + 0x00, 0x39, 0x01, 0xca, 0x00, 0x21, 0x01, 0xc2, 0x00, 0x19, 0x01, 0x8b, 0x00, 0xe1, 0x00, 0x8f, /* bytes 10944 - 10960 */ + 0x00, 0xe0, 0xff, 0x77, 0x00, 0xc8, 0xff, 0x7b, 0x00, 0xbd, 0xff, 0x77, 0x00, 0xa9, 0xff, 0x97, /* bytes 10960 - 10976 */ + 0x00, 0x88, 0xff, 0xbf, 0x00, 0x88, 0xff, 0xdf, 0x00, 0xa9, 0xff, 0xdb, 0x00, 0xbd, 0xff, 0xdf, /* bytes 10976 - 10992 */ + 0x00, 0xc8, 0xff, 0xc7, 0x00, 0xe0, 0xff, 0xc2, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xb9, 0x00, 0xfa, /* bytes 10992 - 11008 */ + 0x00, 0x81, 0x00, 0x12, 0x01, 0x99, 0x00, 0x3b, 0x01, 0x51, 0x00, 0xea, 0x01, 0x51, 0x00, 0x0b, /* bytes 11008 - 11024 */ + 0x02, 0x69, 0x00, 0x2b, 0x02, 0x49, 0x00, 0x5b, 0x02, 0x49, 0x00, 0x83, 0x02, 0x71, 0x00, 0x83, /* bytes 11024 - 11040 */ + 0x02, 0xb1, 0x00, 0x43, 0x02, 0xf0, 0x00, 0x43, 0x02, 0x08, 0x01, 0x0b, 0x02, 0x41, 0x01, 0x0b, /* bytes 11040 - 11056 */ + 0x02, 0x91, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xdb, 0x00, 0xbd, 0xff, /* bytes 11056 - 11072 */ + 0x7b, 0x00, 0xbd, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x53, 0x01, 0x41, /* bytes 11072 - 11088 */ + 0x01, 0xbb, 0x01, 0x41, 0x01, 0x9b, 0x01, 0x21, 0x01, 0x9b, 0x01, 0xd9, 0x00, 0xdb, 0x01, 0xa1, /* bytes 11088 - 11104 */ + 0x00, 0x0b, 0x02, 0xd1, 0x00, 0x0b, 0x02, 0x41, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 11104 - 11120 */ + 0x06, 0x00, 0xbb, 0x01, 0x99, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 11120 - 11136 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x0b, 0x02, 0x91, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 11136 - 11152 */ + 0x00, 0x04, 0x00, 0xc2, 0x00, 0xc1, 0x00, 0xf2, 0x00, 0xdd, 0x00, 0xf2, 0x00, 0x19, 0x01, 0xc2, /* bytes 11152 - 11168 */ + 0x00, 0x19, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x2a, 0x01, 0x41, 0x01, /* bytes 11168 - 11184 */ + 0x2a, 0x01, 0xb1, 0x00, 0x12, 0x01, 0x99, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 11184 - 11200 */ + 0x00, 0x0b, 0x02, 0x69, 0x00, 0x43, 0x02, 0x90, 0x00, 0x43, 0x02, 0xf0, 0x00, 0x03, 0x00, 0xc0, /* bytes 11200 - 11216 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 11216 - 11232 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, /* bytes 11232 - 11248 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x53, 0x01, 0x51, 0x01, 0xbb, 0x01, 0x51, 0x01, /* bytes 11248 - 11264 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x7c, 0x00, 0xb4, /* bytes 11264 - 11280 */ + 0xff, 0x78, 0x00, 0x9f, 0xff, 0x98, 0x00, 0x7f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x9f, /* bytes 11280 - 11296 */ + 0xff, 0xdc, 0x00, 0xb4, 0xff, 0xe0, 0x00, 0xbf, 0xff, 0xc8, 0x00, 0xd7, 0xff, 0xce, 0x00, 0xb5, /* bytes 11296 - 11312 */ + 0x00, 0xce, 0x00, 0xad, 0x00, 0x06, 0x01, 0x75, 0x00, 0x1e, 0x01, 0x8d, 0x00, 0x46, 0x01, 0x45, /* bytes 11312 - 11328 */ + 0x00, 0xf6, 0x01, 0x45, 0x00, 0x16, 0x02, 0x5d, 0x00, 0x36, 0x02, 0x3d, 0x00, 0x66, 0x02, 0x3d, /* bytes 11328 - 11344 */ + 0x00, 0x8e, 0x02, 0x65, 0x00, 0x8e, 0x02, 0xa5, 0x00, 0x4e, 0x02, 0xe5, 0x00, 0x4e, 0x02, 0xfd, /* bytes 11344 - 11360 */ + 0x00, 0x16, 0x02, 0x35, 0x01, 0x16, 0x02, 0x85, 0x01, 0xc6, 0x01, 0x35, 0x01, 0xc6, 0x01, 0x45, /* bytes 11360 - 11376 */ + 0x01, 0xc6, 0x01, 0x8d, 0x01, 0x5e, 0x01, 0x45, 0x01, 0x5e, 0x01, 0x35, 0x01, 0x36, 0x01, 0x35, /* bytes 11376 - 11392 */ + 0x01, 0x2e, 0x01, 0x35, 0x01, 0x06, 0x01, 0x5d, 0x01, 0xd6, 0x00, 0x2d, 0x01, 0xd6, 0x00, 0x15, /* bytes 11392 - 11408 */ + 0x01, 0xce, 0x00, 0x0d, 0x01, 0x96, 0x00, 0xd5, 0x00, 0x90, 0x00, 0xd7, 0xff, 0x78, 0x00, 0xbf, /* bytes 11408 - 11424 */ + 0xff, 0x7c, 0x00, 0xb4, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x7c, 0x00, /* bytes 11424 - 11440 */ + 0xb4, 0xff, 0xdc, 0x00, 0xb4, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x5e, /* bytes 11440 - 11456 */ + 0x01, 0x35, 0x01, 0xc6, 0x01, 0x35, 0x01, 0xa6, 0x01, 0x15, 0x01, 0xa6, 0x01, 0xcd, 0x00, 0xe6, /* bytes 11456 - 11472 */ + 0x01, 0x95, 0x00, 0x16, 0x02, 0xc5, 0x00, 0x16, 0x02, 0x35, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 11472 - 11488 */ + 0x01, 0x00, 0x06, 0x00, 0xc6, 0x01, 0x8d, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 11488 - 11504 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x16, 0x02, 0x85, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 11504 - 11520 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xce, 0x00, 0x0d, 0x01, 0xfe, 0x00, 0x0d, 0x01, 0xfe, 0x00, 0xd1, /* bytes 11520 - 11536 */ + 0x00, 0xce, 0x00, 0xb5, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x16, 0x02, /* bytes 11536 - 11552 */ + 0x5d, 0x00, 0x4e, 0x02, 0x85, 0x00, 0x4e, 0x02, 0xe5, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 11552 - 11568 */ + 0x00, 0x03, 0x00, 0x1e, 0x01, 0x8d, 0x00, 0x36, 0x01, 0xa5, 0x00, 0x36, 0x01, 0x35, 0x01, 0x03, /* bytes 11568 - 11584 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc6, 0x01, 0x45, 0x01, 0x5e, 0x01, 0x45, 0x01, /* bytes 11584 - 11600 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, /* bytes 11600 - 11616 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 11616 - 11632 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x11, /* bytes 11632 - 11648 */ + 0x02, 0x8c, 0x01, 0xc1, 0x01, 0x3c, 0x01, 0xc1, 0x01, 0x4c, 0x01, 0xc1, 0x01, 0x94, 0x01, 0x59, /* bytes 11648 - 11664 */ + 0x01, 0x4c, 0x01, 0x59, 0x01, 0x3c, 0x01, 0x32, 0x01, 0x3c, 0x01, 0x29, 0x01, 0x3c, 0x01, 0x02, /* bytes 11664 - 11680 */ + 0x01, 0x64, 0x01, 0xd1, 0x00, 0x34, 0x01, 0xd2, 0x00, 0x1c, 0x01, 0xc9, 0x00, 0x14, 0x01, 0x91, /* bytes 11680 - 11696 */ + 0x00, 0xdc, 0x00, 0x87, 0x00, 0xe7, 0xff, 0x6f, 0x00, 0xcf, 0xff, 0x73, 0x00, 0xc3, 0xff, 0x6f, /* bytes 11696 - 11712 */ + 0x00, 0xaf, 0xff, 0x8f, 0x00, 0x8f, 0xff, 0xb7, 0x00, 0x8f, 0xff, 0xd7, 0x00, 0xaf, 0xff, 0xd3, /* bytes 11712 - 11728 */ + 0x00, 0xc3, 0xff, 0xd7, 0x00, 0xcf, 0xff, 0xbf, 0x00, 0xe7, 0xff, 0xca, 0x00, 0xbc, 0x00, 0xca, /* bytes 11728 - 11744 */ + 0x00, 0xb4, 0x00, 0x02, 0x01, 0x7c, 0x00, 0x1a, 0x01, 0x94, 0x00, 0x41, 0x01, 0x4c, 0x00, 0xf1, /* bytes 11744 - 11760 */ + 0x01, 0x4c, 0x00, 0x11, 0x02, 0x64, 0x00, 0x31, 0x02, 0x44, 0x00, 0x61, 0x02, 0x44, 0x00, 0x89, /* bytes 11760 - 11776 */ + 0x02, 0x6c, 0x00, 0x89, 0x02, 0xac, 0x00, 0x49, 0x02, 0xec, 0x00, 0x49, 0x02, 0x04, 0x01, 0x11, /* bytes 11776 - 11792 */ + 0x02, 0x3c, 0x01, 0x11, 0x02, 0x8c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 11792 - 11808 */ + 0x73, 0x00, 0xc3, 0xff, 0xd3, 0x00, 0xc3, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 11808 - 11824 */ + 0x00, 0x11, 0x02, 0x3c, 0x01, 0x11, 0x02, 0xcc, 0x00, 0xe1, 0x01, 0x9c, 0x00, 0xa1, 0x01, 0xd4, /* bytes 11824 - 11840 */ + 0x00, 0xa1, 0x01, 0x1c, 0x01, 0xc1, 0x01, 0x3c, 0x01, 0x59, 0x01, 0x3c, 0x01, 0x03, 0x00, 0xc0, /* bytes 11840 - 11856 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x11, 0x02, 0x8c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, /* bytes 11856 - 11872 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xc1, 0x01, 0x94, 0x01, 0x03, 0x00, /* bytes 11872 - 11888 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xfa, 0x00, 0xd8, 0x00, 0xf9, /* bytes 11888 - 11904 */ + 0x00, 0x14, 0x01, 0xc9, 0x00, 0x14, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 11904 - 11920 */ + 0x1a, 0x01, 0x94, 0x00, 0x32, 0x01, 0xac, 0x00, 0x32, 0x01, 0x3c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 11920 - 11936 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x49, 0x02, 0xec, 0x00, 0x49, 0x02, 0x8c, 0x00, 0x11, 0x02, 0x64, /* bytes 11936 - 11952 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 11952 - 11968 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, /* bytes 11968 - 11984 */ + 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xc1, 0x01, 0x4c, 0x01, /* bytes 11984 - 12000 */ + 0x59, 0x01, 0x4c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 12000 - 12016 */ + 0x00, 0xba, 0x01, 0x55, 0x01, 0xba, 0x01, 0x9d, 0x01, 0x52, 0x01, 0x55, 0x01, 0x52, 0x01, 0x45, /* bytes 12016 - 12032 */ + 0x01, 0x2a, 0x01, 0x45, 0x01, 0x22, 0x01, 0x45, 0x01, 0xfa, 0x00, 0x6d, 0x01, 0xca, 0x00, 0x3d, /* bytes 12032 - 12048 */ + 0x01, 0xca, 0x00, 0x25, 0x01, 0xc2, 0x00, 0x1d, 0x01, 0x8a, 0x00, 0xe5, 0x00, 0x85, 0x00, 0xf8, /* bytes 12048 - 12064 */ + 0xff, 0x6d, 0x00, 0xe0, 0xff, 0x70, 0x00, 0xd5, 0xff, 0x6d, 0x00, 0xc0, 0xff, 0x8d, 0x00, 0xa0, /* bytes 12064 - 12080 */ + 0xff, 0xb5, 0x00, 0xa0, 0xff, 0xd5, 0x00, 0xc0, 0xff, 0xd0, 0x00, 0xd5, 0xff, 0xd5, 0x00, 0xe0, /* bytes 12080 - 12096 */ + 0xff, 0xbd, 0x00, 0xf8, 0xff, 0xc2, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbd, 0x00, 0xfa, 0x00, 0x85, /* bytes 12096 - 12112 */ + 0x00, 0x12, 0x01, 0x9d, 0x00, 0x3a, 0x01, 0x55, 0x00, 0xea, 0x01, 0x55, 0x00, 0x0a, 0x02, 0x6d, /* bytes 12112 - 12128 */ + 0x00, 0x2a, 0x02, 0x4d, 0x00, 0x5a, 0x02, 0x4d, 0x00, 0x82, 0x02, 0x75, 0x00, 0x82, 0x02, 0xb5, /* bytes 12128 - 12144 */ + 0x00, 0x42, 0x02, 0xf5, 0x00, 0x42, 0x02, 0x0d, 0x01, 0x0a, 0x02, 0x45, 0x01, 0x0a, 0x02, 0x95, /* bytes 12144 - 12160 */ + 0x01, 0xba, 0x01, 0x45, 0x01, 0xba, 0x01, 0x55, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 12160 - 12176 */ + 0x02, 0x00, 0xd0, 0x00, 0xd5, 0xff, 0x70, 0x00, 0xd5, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 12176 - 12192 */ + 0x00, 0x07, 0x00, 0x0a, 0x02, 0x45, 0x01, 0x0a, 0x02, 0xd5, 0x00, 0xda, 0x01, 0xa5, 0x00, 0x9a, /* bytes 12192 - 12208 */ + 0x01, 0xdd, 0x00, 0x9a, 0x01, 0x25, 0x01, 0xba, 0x01, 0x45, 0x01, 0x52, 0x01, 0x45, 0x01, 0x03, /* bytes 12208 - 12224 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x0a, 0x02, 0x95, 0x01, 0x04, 0x02, 0x2c, 0x02, /* bytes 12224 - 12240 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xba, 0x01, 0x9d, 0x01, /* bytes 12240 - 12256 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xf2, 0x00, 0xe1, /* bytes 12256 - 12272 */ + 0x00, 0xf2, 0x00, 0x1d, 0x01, 0xc2, 0x00, 0x1d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 12272 - 12288 */ + 0x03, 0x00, 0x12, 0x01, 0x9d, 0x00, 0x2a, 0x01, 0xb5, 0x00, 0x2a, 0x01, 0x45, 0x01, 0x03, 0x00, /* bytes 12288 - 12304 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x42, 0x02, 0xf5, 0x00, 0x42, 0x02, 0x95, 0x00, 0x0a, /* bytes 12304 - 12320 */ + 0x02, 0x6d, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 12320 - 12336 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x52, 0x01, 0x55, /* bytes 12336 - 12352 */ + 0x01, 0xba, 0x01, 0x55, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, /* bytes 12352 - 12368 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 12368 - 12384 */ + 0x00, 0x26, 0x00, 0x0d, 0x01, 0xa3, 0x00, 0x35, 0x01, 0x5b, 0x00, 0xe5, 0x01, 0x5b, 0x00, 0x05, /* bytes 12384 - 12400 */ + 0x02, 0x73, 0x00, 0x25, 0x02, 0x53, 0x00, 0x55, 0x02, 0x53, 0x00, 0x7d, 0x02, 0x7b, 0x00, 0x7d, /* bytes 12400 - 12416 */ + 0x02, 0xbb, 0x00, 0x3d, 0x02, 0xfb, 0x00, 0x3d, 0x02, 0x13, 0x01, 0x05, 0x02, 0x4b, 0x01, 0x05, /* bytes 12416 - 12432 */ + 0x02, 0x9b, 0x01, 0xb5, 0x01, 0x4b, 0x01, 0xb5, 0x01, 0x5b, 0x01, 0xb5, 0x01, 0xa3, 0x01, 0x4d, /* bytes 12432 - 12448 */ + 0x01, 0x5b, 0x01, 0x4d, 0x01, 0x4b, 0x01, 0x25, 0x01, 0x4b, 0x01, 0x1d, 0x01, 0x4b, 0x01, 0xf5, /* bytes 12448 - 12464 */ + 0x00, 0x73, 0x01, 0xc5, 0x00, 0x43, 0x01, 0xc5, 0x00, 0x2b, 0x01, 0xbd, 0x00, 0x23, 0x01, 0x85, /* bytes 12464 - 12480 */ + 0x00, 0xec, 0x00, 0x85, 0x00, 0xfe, 0xff, 0x6d, 0x00, 0xe6, 0xff, 0x71, 0x00, 0xdb, 0xff, 0x6d, /* bytes 12480 - 12496 */ + 0x00, 0xc6, 0xff, 0x8d, 0x00, 0xa6, 0xff, 0xb5, 0x00, 0xa6, 0xff, 0xd5, 0x00, 0xc6, 0xff, 0xd1, /* bytes 12496 - 12512 */ + 0x00, 0xdb, 0xff, 0xd5, 0x00, 0xe6, 0xff, 0xbd, 0x00, 0xfe, 0xff, 0xbd, 0x00, 0xcb, 0x00, 0xbd, /* bytes 12512 - 12528 */ + 0x00, 0xc3, 0x00, 0xf5, 0x00, 0x8b, 0x00, 0x0d, 0x01, 0xa3, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 12528 - 12544 */ + 0x01, 0x00, 0x02, 0x00, 0xd1, 0x00, 0xdb, 0xff, 0x71, 0x00, 0xdb, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 12544 - 12560 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x05, 0x02, 0x4b, 0x01, 0x05, 0x02, 0xdb, 0x00, 0xd5, 0x01, 0xab, /* bytes 12560 - 12576 */ + 0x00, 0x95, 0x01, 0xe3, 0x00, 0x95, 0x01, 0x2b, 0x01, 0xb5, 0x01, 0x4b, 0x01, 0x4d, 0x01, 0x4b, /* bytes 12576 - 12592 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x05, 0x02, 0x9b, 0x01, 0x04, 0x02, /* bytes 12592 - 12608 */ + 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb5, 0x01, /* bytes 12608 - 12624 */ + 0xa3, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbd, 0x00, 0x23, 0x01, 0xed, /* bytes 12624 - 12640 */ + 0x00, 0x23, 0x01, 0xed, 0x00, 0xe7, 0x00, 0xbd, 0x00, 0xcb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 12640 - 12656 */ + 0x01, 0x00, 0x03, 0x00, 0x0d, 0x01, 0xa3, 0x00, 0x25, 0x01, 0xbb, 0x00, 0x25, 0x01, 0x4b, 0x01, /* bytes 12656 - 12672 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x05, 0x02, 0x73, 0x00, 0x3d, 0x02, 0x9b, /* bytes 12672 - 12688 */ + 0x00, 0x3d, 0x02, 0xfb, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb5, 0x01, /* bytes 12688 - 12704 */ + 0x5b, 0x01, 0x4d, 0x01, 0x5b, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, /* bytes 12704 - 12720 */ + 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 12720 - 12736 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 12736 - 12752 */ + 0xff, 0x00, 0x00, 0x26, 0x00, 0xbb, 0x00, 0xce, 0x00, 0xbd, 0x00, 0x00, 0x00, 0xd5, 0x00, 0xe8, /* bytes 12752 - 12768 */ + 0xff, 0xd0, 0x00, 0xdd, 0xff, 0xd5, 0x00, 0xc8, 0xff, 0xb5, 0x00, 0xa8, 0xff, 0x8d, 0x00, 0xa8, /* bytes 12768 - 12784 */ + 0xff, 0x6d, 0x00, 0xc8, 0xff, 0x70, 0x00, 0xdd, 0xff, 0x6d, 0x00, 0xe8, 0xff, 0x85, 0x00, 0x00, /* bytes 12784 - 12800 */ + 0x00, 0x83, 0x00, 0xee, 0x00, 0xbb, 0x00, 0x26, 0x01, 0xc3, 0x00, 0x2e, 0x01, 0xc3, 0x00, 0x46, /* bytes 12800 - 12816 */ + 0x01, 0xf3, 0x00, 0x76, 0x01, 0x1b, 0x01, 0x4e, 0x01, 0x23, 0x01, 0x4e, 0x01, 0x4b, 0x01, 0x4e, /* bytes 12816 - 12832 */ + 0x01, 0x4b, 0x01, 0x5e, 0x01, 0xb3, 0x01, 0xa6, 0x01, 0xb3, 0x01, 0x5e, 0x01, 0xb3, 0x01, 0x4e, /* bytes 12832 - 12848 */ + 0x01, 0x03, 0x02, 0x9e, 0x01, 0x03, 0x02, 0x4e, 0x01, 0x3b, 0x02, 0x16, 0x01, 0x3b, 0x02, 0xfe, /* bytes 12848 - 12864 */ + 0x00, 0x7b, 0x02, 0xbe, 0x00, 0x7b, 0x02, 0x7e, 0x00, 0x53, 0x02, 0x56, 0x00, 0x23, 0x02, 0x56, /* bytes 12864 - 12880 */ + 0x00, 0x03, 0x02, 0x76, 0x00, 0xe3, 0x01, 0x5e, 0x00, 0x33, 0x01, 0x5e, 0x00, 0x0b, 0x01, 0xa6, /* bytes 12880 - 12896 */ + 0x00, 0xf3, 0x00, 0x8e, 0x00, 0xbb, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xce, 0x00, 0x03, 0x00, 0xc0, /* bytes 12896 - 12912 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0xdd, 0xff, 0xd0, 0x00, 0xdd, 0xff, 0x03, 0x00, /* bytes 12912 - 12928 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4b, 0x01, 0x4e, 0x01, 0xb3, 0x01, 0x4e, 0x01, 0x93, /* bytes 12928 - 12944 */ + 0x01, 0x2e, 0x01, 0x93, 0x01, 0xe6, 0x00, 0xd3, 0x01, 0xae, 0x00, 0x03, 0x02, 0xde, 0x00, 0x03, /* bytes 12944 - 12960 */ + 0x02, 0x4e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x03, 0x02, 0x9e, 0x01, /* bytes 12960 - 12976 */ + 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, /* bytes 12976 - 12992 */ + 0xb3, 0x01, 0xa6, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbb, 0x00, 0xce, /* bytes 12992 - 13008 */ + 0x00, 0xeb, 0x00, 0xea, 0x00, 0xeb, 0x00, 0x26, 0x01, 0xbb, 0x00, 0x26, 0x01, 0x03, 0x00, 0xc0, /* bytes 13008 - 13024 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0b, 0x01, 0xa6, 0x00, 0x23, 0x01, 0xbe, 0x00, 0x23, 0x01, /* bytes 13024 - 13040 */ + 0x4e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x02, 0x76, 0x00, 0x3b, /* bytes 13040 - 13056 */ + 0x02, 0x9e, 0x00, 0x3b, 0x02, 0xfe, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 13056 - 13072 */ + 0x4b, 0x01, 0x5e, 0x01, 0xb3, 0x01, 0x5e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 13072 - 13088 */ + 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 13088 - 13104 */ + 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 13104 - 13120 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, /* bytes 13120 - 13136 */ + 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdd, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, /* bytes 13136 - 13152 */ + 0x00, 0x00, 0x00, 0xbb, 0x00, 0xce, 0x00, 0xbb, 0x00, 0xc6, 0x00, 0xf3, 0x00, 0x8e, 0x00, 0x0b, /* bytes 13152 - 13168 */ + 0x01, 0xa6, 0x00, 0x33, 0x01, 0x5e, 0x00, 0xe3, 0x01, 0x5e, 0x00, 0x03, 0x02, 0x76, 0x00, 0x23, /* bytes 13168 - 13184 */ + 0x02, 0x56, 0x00, 0x53, 0x02, 0x56, 0x00, 0x7b, 0x02, 0x7e, 0x00, 0x7b, 0x02, 0xbe, 0x00, 0x3b, /* bytes 13184 - 13200 */ + 0x02, 0xfe, 0x00, 0x3b, 0x02, 0x16, 0x01, 0x03, 0x02, 0x4e, 0x01, 0x03, 0x02, 0x9e, 0x01, 0xb3, /* bytes 13200 - 13216 */ + 0x01, 0x4e, 0x01, 0xb3, 0x01, 0x5e, 0x01, 0xb3, 0x01, 0xa6, 0x01, 0x4b, 0x01, 0x5e, 0x01, 0x4b, /* bytes 13216 - 13232 */ + 0x01, 0x4e, 0x01, 0x23, 0x01, 0x4e, 0x01, 0x1b, 0x01, 0x4e, 0x01, 0xf3, 0x00, 0x76, 0x01, 0xc3, /* bytes 13232 - 13248 */ + 0x00, 0x46, 0x01, 0xc3, 0x00, 0x2e, 0x01, 0xbb, 0x00, 0x26, 0x01, 0x83, 0x00, 0xee, 0x00, 0x84, /* bytes 13248 - 13264 */ + 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdd, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x03, /* bytes 13264 - 13280 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdd, 0xff, 0x70, 0x00, 0xdd, 0xff, /* bytes 13280 - 13296 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x03, 0x02, 0x4e, 0x01, 0x03, 0x02, 0xde, /* bytes 13296 - 13312 */ + 0x00, 0xd3, 0x01, 0xae, 0x00, 0x93, 0x01, 0xe6, 0x00, 0x93, 0x01, 0x2e, 0x01, 0xb3, 0x01, 0x4e, /* bytes 13312 - 13328 */ + 0x01, 0x4b, 0x01, 0x4e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x03, 0x02, /* bytes 13328 - 13344 */ + 0x9e, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 13344 - 13360 */ + 0x24, 0x02, 0xb3, 0x01, 0xa6, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbb, /* bytes 13360 - 13376 */ + 0x00, 0xce, 0x00, 0xeb, 0x00, 0xea, 0x00, 0xeb, 0x00, 0x26, 0x01, 0xbb, 0x00, 0x26, 0x01, 0x03, /* bytes 13376 - 13392 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x23, 0x01, 0x4e, 0x01, 0x23, 0x01, 0xbe, 0x00, /* bytes 13392 - 13408 */ + 0x0b, 0x01, 0xa6, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3b, 0x02, 0xfe, /* bytes 13408 - 13424 */ + 0x00, 0x3b, 0x02, 0x9e, 0x00, 0x03, 0x02, 0x76, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 13424 - 13440 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 13440 - 13456 */ + 0x00, 0x02, 0x00, 0xb3, 0x01, 0x5e, 0x01, 0x4b, 0x01, 0x5e, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 13456 - 13472 */ + 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, /* bytes 13472 - 13488 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xf3, 0x00, 0x75, 0x01, 0xc3, 0x00, 0x45, /* bytes 13488 - 13504 */ + 0x01, 0xc3, 0x00, 0x2d, 0x01, 0xbb, 0x00, 0x25, 0x01, 0x83, 0x00, 0xed, 0x00, 0x84, 0x00, 0x00, /* bytes 13504 - 13520 */ + 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdd, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, /* bytes 13520 - 13536 */ + 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdd, 0xff, 0xd4, 0x00, 0xe8, /* bytes 13536 - 13552 */ + 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbb, 0x00, 0xcd, 0x00, 0xbb, 0x00, 0xc5, 0x00, 0xf3, 0x00, 0x8d, /* bytes 13552 - 13568 */ + 0x00, 0x0b, 0x01, 0xa5, 0x00, 0x33, 0x01, 0x5d, 0x00, 0xe3, 0x01, 0x5d, 0x00, 0x03, 0x02, 0x75, /* bytes 13568 - 13584 */ + 0x00, 0x23, 0x02, 0x55, 0x00, 0x53, 0x02, 0x55, 0x00, 0x7b, 0x02, 0x7d, 0x00, 0x7b, 0x02, 0xbd, /* bytes 13584 - 13600 */ + 0x00, 0x3b, 0x02, 0xfd, 0x00, 0x3b, 0x02, 0x15, 0x01, 0x03, 0x02, 0x4d, 0x01, 0x03, 0x02, 0x9d, /* bytes 13600 - 13616 */ + 0x01, 0xb3, 0x01, 0x4d, 0x01, 0xb3, 0x01, 0x5d, 0x01, 0xb3, 0x01, 0xa5, 0x01, 0x4b, 0x01, 0x5d, /* bytes 13616 - 13632 */ + 0x01, 0x4b, 0x01, 0x4d, 0x01, 0x23, 0x01, 0x4d, 0x01, 0x1b, 0x01, 0x4d, 0x01, 0xf3, 0x00, 0x75, /* bytes 13632 - 13648 */ + 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0xdd, 0xff, 0xd0, 0x00, /* bytes 13648 - 13664 */ + 0xdd, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4b, 0x01, 0x4d, 0x01, 0xb3, /* bytes 13664 - 13680 */ + 0x01, 0x4d, 0x01, 0x93, 0x01, 0x2d, 0x01, 0x93, 0x01, 0xe5, 0x00, 0xd3, 0x01, 0xad, 0x00, 0x03, /* bytes 13680 - 13696 */ + 0x02, 0xdd, 0x00, 0x03, 0x02, 0x4d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 13696 - 13712 */ + 0xb3, 0x01, 0xa5, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 13712 - 13728 */ + 0x04, 0x02, 0x2c, 0x02, 0x03, 0x02, 0x9d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 13728 - 13744 */ + 0x00, 0xbb, 0x00, 0xcd, 0x00, 0xeb, 0x00, 0xe9, 0x00, 0xeb, 0x00, 0x25, 0x01, 0xbb, 0x00, 0x25, /* bytes 13744 - 13760 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x02, 0x75, 0x00, 0x3b, 0x02, /* bytes 13760 - 13776 */ + 0x9d, 0x00, 0x3b, 0x02, 0xfd, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0b, /* bytes 13776 - 13792 */ + 0x01, 0xa5, 0x00, 0x23, 0x01, 0xbd, 0x00, 0x23, 0x01, 0x4d, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 13792 - 13808 */ + 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, /* bytes 13808 - 13824 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x4b, 0x01, 0x5d, 0x01, 0xb3, 0x01, 0x5d, 0x01, 0x03, 0x00, 0xc0, /* bytes 13824 - 13840 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 13840 - 13856 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xd4, 0x00, 0xc8, 0xff, 0xd0, /* bytes 13856 - 13872 */ + 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, /* bytes 13872 - 13888 */ + 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, /* bytes 13888 - 13904 */ + 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, /* bytes 13904 - 13920 */ + 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, /* bytes 13920 - 13936 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, /* bytes 13936 - 13952 */ + 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, /* bytes 13952 - 13968 */ + 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, /* bytes 13968 - 13984 */ + 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, /* bytes 13984 - 14000 */ + 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, /* bytes 14000 - 14016 */ + 0x00, 0xc8, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0xdc, 0xff, /* bytes 14016 - 14032 */ + 0xd0, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, /* bytes 14032 - 14048 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, /* bytes 14048 - 14064 */ + 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 14064 - 14080 */ + 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 14080 - 14096 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 14096 - 14112 */ + 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, /* bytes 14112 - 14128 */ + 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x01, 0xa4, 0x00, /* bytes 14128 - 14144 */ + 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 14144 - 14160 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, /* bytes 14160 - 14176 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 14176 - 14192 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, /* bytes 14192 - 14208 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 14208 - 14224 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x84, 0x00, 0xec, /* bytes 14224 - 14240 */ + 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, /* bytes 14240 - 14256 */ + 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, /* bytes 14256 - 14272 */ + 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, /* bytes 14272 - 14288 */ + 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, /* bytes 14288 - 14304 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, /* bytes 14304 - 14320 */ + 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, /* bytes 14320 - 14336 */ + 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, /* bytes 14336 - 14352 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, /* bytes 14352 - 14368 */ + 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, /* bytes 14368 - 14384 */ + 0x01, 0x84, 0x00, 0xec, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, /* bytes 14384 - 14400 */ + 0xdc, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, /* bytes 14400 - 14416 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, /* bytes 14416 - 14432 */ + 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 14432 - 14448 */ + 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 14448 - 14464 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 14464 - 14480 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, /* bytes 14480 - 14496 */ + 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x04, 0x02, /* bytes 14496 - 14512 */ + 0x74, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 14512 - 14528 */ + 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, /* bytes 14528 - 14544 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, /* bytes 14544 - 14560 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, /* bytes 14560 - 14576 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, /* bytes 14576 - 14592 */ + 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x3c, /* bytes 14592 - 14608 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4b, 0x01, 0x04, 0x02, 0x9b, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 14608 - 14624 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, /* bytes 14624 - 14640 */ + 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, /* bytes 14640 - 14656 */ + 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, /* bytes 14656 - 14672 */ + 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, /* bytes 14672 - 14688 */ + 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, /* bytes 14688 - 14704 */ + 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, /* bytes 14704 - 14720 */ + 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, /* bytes 14720 - 14736 */ + 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, /* bytes 14736 - 14752 */ + 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 14752 - 14768 */ + 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 14768 - 14784 */ + 0x00, 0x04, 0x02, 0x4b, 0x01, 0x04, 0x02, 0xdb, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, /* bytes 14784 - 14800 */ + 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 14800 - 14816 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9b, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, /* bytes 14816 - 14832 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, /* bytes 14832 - 14848 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, /* bytes 14848 - 14864 */ + 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 14864 - 14880 */ + 0x04, 0x02, 0x74, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 14880 - 14896 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, /* bytes 14896 - 14912 */ + 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, /* bytes 14912 - 14928 */ + 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, /* bytes 14928 - 14944 */ + 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, /* bytes 14944 - 14960 */ + 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 14960 - 14976 */ + 0x00, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, /* bytes 14976 - 14992 */ + 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, /* bytes 14992 - 15008 */ + 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, /* bytes 15008 - 15024 */ + 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, /* bytes 15024 - 15040 */ + 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 15040 - 15056 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 15056 - 15072 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, /* bytes 15072 - 15088 */ + 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, /* bytes 15088 - 15104 */ + 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, /* bytes 15104 - 15120 */ + 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 15120 - 15136 */ + 0x02, 0x00, 0x70, 0x00, 0xdc, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 15136 - 15152 */ + 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, /* bytes 15152 - 15168 */ + 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, /* bytes 15168 - 15184 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, /* bytes 15184 - 15200 */ + 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, /* bytes 15200 - 15216 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, /* bytes 15216 - 15232 */ + 0x00, 0xec, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 15232 - 15248 */ + 0x03, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x03, 0x00, /* bytes 15248 - 15264 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, /* bytes 15264 - 15280 */ + 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 15280 - 15296 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, /* bytes 15296 - 15312 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, /* bytes 15312 - 15328 */ + 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, /* bytes 15328 - 15344 */ + 0x00, 0x26, 0x00, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, /* bytes 15344 - 15360 */ + 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, /* bytes 15360 - 15376 */ + 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, /* bytes 15376 - 15392 */ + 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, /* bytes 15392 - 15408 */ + 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, /* bytes 15408 - 15424 */ + 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, /* bytes 15424 - 15440 */ + 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, /* bytes 15440 - 15456 */ + 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, /* bytes 15456 - 15472 */ + 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, /* bytes 15472 - 15488 */ + 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, /* bytes 15488 - 15504 */ + 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, 0xdc, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, /* bytes 15504 - 15520 */ + 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, /* bytes 15520 - 15536 */ + 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, /* bytes 15536 - 15552 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, /* bytes 15552 - 15568 */ + 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, /* bytes 15568 - 15584 */ + 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, /* bytes 15584 - 15600 */ + 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 15600 - 15616 */ + 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, /* bytes 15616 - 15632 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, /* bytes 15632 - 15648 */ + 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, /* bytes 15648 - 15664 */ + 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, /* bytes 15664 - 15680 */ + 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 15680 - 15696 */ + 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 15696 - 15712 */ + 0xff, 0x00, 0x00, 0x26, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, /* bytes 15712 - 15728 */ + 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, /* bytes 15728 - 15744 */ + 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, /* bytes 15744 - 15760 */ + 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, /* bytes 15760 - 15776 */ + 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, /* bytes 15776 - 15792 */ + 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, /* bytes 15792 - 15808 */ + 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, /* bytes 15808 - 15824 */ + 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, /* bytes 15824 - 15840 */ + 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, /* bytes 15840 - 15856 */ + 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x03, 0x00, 0xc0, /* bytes 15856 - 15872 */ + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, /* bytes 15872 - 15888 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, /* bytes 15888 - 15904 */ + 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, /* bytes 15904 - 15920 */ + 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, /* bytes 15920 - 15936 */ + 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, /* bytes 15936 - 15952 */ + 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, /* bytes 15952 - 15968 */ + 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, /* bytes 15968 - 15984 */ + 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, /* bytes 15984 - 16000 */ + 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, /* bytes 16000 - 16016 */ + 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 16016 - 16032 */ + 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, /* bytes 16032 - 16048 */ + 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 16048 - 16064 */ + 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, /* bytes 16064 - 16080 */ + 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 16080 - 16096 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, /* bytes 16096 - 16112 */ + 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, /* bytes 16112 - 16128 */ + 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, /* bytes 16128 - 16144 */ + 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, /* bytes 16144 - 16160 */ + 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, /* bytes 16160 - 16176 */ + 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, /* bytes 16176 - 16192 */ + 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, /* bytes 16192 - 16208 */ + 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, /* bytes 16208 - 16224 */ + 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0x03, /* bytes 16224 - 16240 */ + 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, /* bytes 16240 - 16256 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, /* bytes 16256 - 16272 */ + 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, /* bytes 16272 - 16288 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, /* bytes 16288 - 16304 */ + 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, /* bytes 16304 - 16320 */ + 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, /* bytes 16320 - 16336 */ + 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, 0x00, 0xec, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x03, /* bytes 16336 - 16352 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, /* bytes 16352 - 16368 */ + 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x04, 0x02, 0x74, /* bytes 16368 - 16384 */ + 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 16384 - 16400 */ + 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 16400 - 16416 */ + 0x00, 0x02, 0x00, 0x58, 0x01, 0x5c, 0x02, 0x1c, 0x00, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 16416 - 16432 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, /* bytes 16432 - 16448 */ + 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, /* bytes 16448 - 16464 */ + 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, /* bytes 16464 - 16480 */ + 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, /* bytes 16480 - 16496 */ + 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, /* bytes 16496 - 16512 */ + 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, /* bytes 16512 - 16528 */ + 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, /* bytes 16528 - 16544 */ + 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, /* bytes 16544 - 16560 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, /* bytes 16560 - 16576 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, /* bytes 16576 - 16592 */ + 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, /* bytes 16592 - 16608 */ + 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, /* bytes 16608 - 16624 */ + 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, /* bytes 16624 - 16640 */ + 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, /* bytes 16640 - 16656 */ + 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, /* bytes 16656 - 16672 */ + 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 16672 - 16688 */ + 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, /* bytes 16688 - 16704 */ + 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, 0x00, 0xec, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, /* bytes 16704 - 16720 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x04, 0x02, 0x74, 0x00, 0x3c, 0x02, /* bytes 16720 - 16736 */ + 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, /* bytes 16736 - 16752 */ + 0x01, 0xa4, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 16752 - 16768 */ + 0x01, 0x00, 0x02, 0x00, 0x4c, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 16768 - 16784 */ + 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, 0x02, 0x03, 0x00, 0xc0, /* bytes 16784 - 16800 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0x23, 0x00, /* bytes 16800 - 16816 */ + 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x1c, /* bytes 16816 - 16832 */ + 0x01, 0x4c, 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, /* bytes 16832 - 16848 */ + 0x00, 0x24, 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, /* bytes 16848 - 16864 */ + 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, /* bytes 16864 - 16880 */ + 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, /* bytes 16880 - 16896 */ + 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, /* bytes 16896 - 16912 */ + 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, /* bytes 16912 - 16928 */ + 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, /* bytes 16928 - 16944 */ + 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, /* bytes 16944 - 16960 */ + 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, /* bytes 16960 - 16976 */ + 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, /* bytes 16976 - 16992 */ + 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x4c, 0x01, 0x4c, /* bytes 16992 - 17008 */ + 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x94, 0x01, 0x2c, 0x01, 0x94, 0x01, 0xe4, 0x00, 0xd4, 0x01, 0xac, /* bytes 17008 - 17024 */ + 0x00, 0x04, 0x02, 0xdc, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 17024 - 17040 */ + 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, /* bytes 17040 - 17056 */ + 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 17056 - 17072 */ + 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, /* bytes 17072 - 17088 */ + 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x04, 0x02, 0x74, 0x00, /* bytes 17088 - 17104 */ + 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, /* bytes 17104 - 17120 */ + 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, /* bytes 17120 - 17136 */ + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, /* bytes 17136 - 17152 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x03, /* bytes 17152 - 17168 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 17168 - 17184 */ + 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0x6c, 0x00, 0xc8, /* bytes 17184 - 17200 */ + 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, 0xff, 0xd0, 0x00, 0xdc, /* bytes 17200 - 17216 */ + 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xbc, 0x00, 0xc4, /* bytes 17216 - 17232 */ + 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, 0x00, 0xe4, 0x01, 0x5c, /* bytes 17232 - 17248 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, /* bytes 17248 - 17264 */ + 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, /* bytes 17264 - 17280 */ + 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, /* bytes 17280 - 17296 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, /* bytes 17296 - 17312 */ + 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, /* bytes 17312 - 17328 */ + 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, /* bytes 17328 - 17344 */ + 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0xd0, 0x00, /* bytes 17344 - 17360 */ + 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, /* bytes 17360 - 17376 */ + 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, 0x00, 0x94, /* bytes 17376 - 17392 */ + 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, /* bytes 17392 - 17408 */ + 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, 0x58, 0x01, 0x5c, 0x02, /* bytes 17408 - 17424 */ + 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, 0x03, 0x00, 0xc0, 0x04, /* bytes 17424 - 17440 */ + 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, 0x00, 0xec, 0x00, 0x24, /* bytes 17440 - 17456 */ + 0x01, 0xbc, 0x00, 0x24, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x3c, 0x02, /* bytes 17456 - 17472 */ + 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 17472 - 17488 */ + 0x00, 0x03, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x03, /* bytes 17488 - 17504 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, /* bytes 17504 - 17520 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb8, 0x01, 0x5c, 0x02, 0x64, 0x02, 0x5c, /* bytes 17520 - 17536 */ + 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, 0x01, /* bytes 17536 - 17552 */ + 0x5c, 0x02, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, 0x00, 0xbc, /* bytes 17552 - 17568 */ + 0x00, 0xcc, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xd4, 0x00, 0xe8, 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, /* bytes 17568 - 17584 */ + 0x00, 0xc8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x70, /* bytes 17584 - 17600 */ + 0x00, 0xdc, 0xff, 0x6c, 0x00, 0xe8, 0xff, 0x84, 0x00, 0x00, 0x00, 0x84, 0x00, 0xec, 0x00, 0xbc, /* bytes 17600 - 17616 */ + 0x00, 0x24, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xf4, 0x00, 0x74, 0x01, 0x1c, /* bytes 17616 - 17632 */ + 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x5c, 0x01, 0xb4, /* bytes 17632 - 17648 */ + 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x04, 0x02, 0x9c, 0x01, 0x04, /* bytes 17648 - 17664 */ + 0x02, 0x4c, 0x01, 0x3c, 0x02, 0x14, 0x01, 0x3c, 0x02, 0xfc, 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x7c, /* bytes 17664 - 17680 */ + 0x02, 0x7c, 0x00, 0x54, 0x02, 0x54, 0x00, 0x24, 0x02, 0x54, 0x00, 0x04, 0x02, 0x74, 0x00, 0xe4, /* bytes 17680 - 17696 */ + 0x01, 0x5c, 0x00, 0x34, 0x01, 0x5c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0xbc, /* bytes 17696 - 17712 */ + 0x00, 0xc4, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, /* bytes 17712 - 17728 */ + 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x07, /* bytes 17728 - 17744 */ + 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, 0x01, 0xe4, /* bytes 17744 - 17760 */ + 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, 0x00, 0xc0, /* bytes 17760 - 17776 */ + 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x02, 0x9c, 0x01, 0x04, 0x02, 0x2c, 0x02, 0xb8, 0x01, /* bytes 17776 - 17792 */ + 0x5c, 0x02, 0x58, 0x01, 0x5c, 0x02, 0xb4, 0x01, 0x24, 0x02, 0xb4, 0x01, 0xa4, 0x01, 0x03, 0x00, /* bytes 17792 - 17808 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0xec, 0x00, 0xe8, 0x00, 0xec, /* bytes 17808 - 17824 */ + 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, /* bytes 17824 - 17840 */ + 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x04, /* bytes 17840 - 17856 */ + 0x00, 0x01, 0x00, 0x03, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x24, 0x01, 0xbc, 0x00, 0x24, 0x01, 0x4c, /* bytes 17856 - 17872 */ + 0x01, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, 0xb8, 0x01, /* bytes 17872 - 17888 */ + 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, 0x02, 0x58, /* bytes 17888 - 17904 */ + 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, 0x5c, 0x01, /* bytes 17904 - 17920 */ + 0x4c, 0x01, 0x5c, 0x01, 0x23, 0x00, 0x0a, 0x00, 0x03, 0x00, 0xc0, 0x04, 0xff, 0x00, 0x00, 0x26, /* bytes 17920 - 17936 */ + 0x00, 0x04, 0x02, 0x74, 0x00, 0x24, 0x02, 0x54, 0x00, 0x54, 0x02, 0x54, 0x00, 0x7c, 0x02, 0x7c, /* bytes 17936 - 17952 */ + 0x00, 0x7c, 0x02, 0xbc, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x3c, 0x02, 0x14, 0x01, 0x04, 0x02, 0x4c, /* bytes 17952 - 17968 */ + 0x01, 0x04, 0x02, 0x9c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0xb4, 0x01, 0x5c, 0x01, 0xb4, 0x01, 0xa4, /* bytes 17968 - 17984 */ + 0x01, 0x4c, 0x01, 0x5c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x24, 0x01, 0x4c, 0x01, 0x1c, 0x01, 0x4c, /* bytes 17984 - 18000 */ + 0x01, 0xf4, 0x00, 0x74, 0x01, 0xc4, 0x00, 0x44, 0x01, 0xc4, 0x00, 0x2c, 0x01, 0xbc, 0x00, 0x24, /* bytes 18000 - 18016 */ + 0x01, 0x84, 0x00, 0xec, 0x00, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x00, 0xe8, 0xff, 0x70, 0x00, 0xdc, /* bytes 18016 - 18032 */ + 0xff, 0x6c, 0x00, 0xc8, 0xff, 0x8c, 0x00, 0xa8, 0xff, 0xb4, 0x00, 0xa8, 0xff, 0xd4, 0x00, 0xc8, /* bytes 18032 - 18048 */ + 0xff, 0xd0, 0x00, 0xdc, 0xff, 0xd4, 0x00, 0xe8, 0xff, 0xbc, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xcc, /* bytes 18048 - 18064 */ + 0x00, 0xbc, 0x00, 0xc4, 0x00, 0xf4, 0x00, 0x8c, 0x00, 0x0c, 0x01, 0xa4, 0x00, 0x34, 0x01, 0x5c, /* bytes 18064 - 18080 */ + 0x00, 0xe4, 0x01, 0x5c, 0x00, 0x04, 0x02, 0x74, 0x00, 0x03, 0x00, 0xc0, 0x02, 0x00, 0x01, 0x00, /* bytes 18080 - 18096 */ + 0x02, 0x00, 0xd0, 0x00, 0xdc, 0xff, 0x70, 0x00, 0xdc, 0xff, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, /* bytes 18096 - 18112 */ + 0x00, 0x07, 0x00, 0x04, 0x02, 0x4c, 0x01, 0x04, 0x02, 0xdc, 0x00, 0xd4, 0x01, 0xac, 0x00, 0x94, /* bytes 18112 - 18128 */ + 0x01, 0xe4, 0x00, 0x94, 0x01, 0x2c, 0x01, 0xb4, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x4c, 0x01, 0x03, /* bytes 18128 - 18144 */ + 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0xb4, 0x01, 0xa4, 0x01, 0xb4, 0x01, 0x24, 0x02, /* bytes 18144 - 18160 */ + 0x58, 0x01, 0x5c, 0x02, 0xb8, 0x01, 0x5c, 0x02, 0x04, 0x02, 0x2c, 0x02, 0x04, 0x02, 0x9c, 0x01, /* bytes 18160 - 18176 */ + 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0xbc, 0x00, 0x24, 0x01, 0xec, 0x00, 0x24, /* bytes 18176 - 18192 */ + 0x01, 0xec, 0x00, 0xe8, 0x00, 0xbc, 0x00, 0xcc, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, /* bytes 18192 - 18208 */ + 0x03, 0x00, 0x04, 0x02, 0x74, 0x00, 0x3c, 0x02, 0x9c, 0x00, 0x3c, 0x02, 0xfc, 0x00, 0x03, 0x00, /* bytes 18208 - 18224 */ + 0xc0, 0x04, 0x00, 0x01, 0x00, 0x03, 0x00, 0x24, 0x01, 0x4c, 0x01, 0x24, 0x01, 0xbc, 0x00, 0x0c, /* bytes 18224 - 18240 */ + 0x01, 0xa4, 0x00, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x64, 0x02, 0x5c, 0x02, /* bytes 18240 - 18256 */ + 0xb8, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x5c, /* bytes 18256 - 18272 */ + 0x02, 0x58, 0x01, 0x5c, 0x02, 0x03, 0x00, 0xc0, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0xb4, 0x01, /* bytes 18272 - 18288 */ + 0x5c, 0x01, 0x4c, 0x01, 0x5c, 0x01, }; -const uint32_t g_num_builtin_resources = 11; +const uint32_t g_num_builtin_resources = 20; const BuiltInResourceData g_builtin_resources[] = { { RESOURCE_ID_ACTION_BAR_ICON_CHECK, ACTION_BAR_ICON_CHECK_builtin_bytes, sizeof(ACTION_BAR_ICON_CHECK_builtin_bytes) }, { RESOURCE_ID_GENERIC_WARNING_LARGE, GENERIC_WARNING_LARGE_builtin_bytes, sizeof(GENERIC_WARNING_LARGE_builtin_bytes) }, @@ -322,5 +8389,14 @@ const BuiltInResourceData g_builtin_resources[] = { { RESOURCE_ID_WATCH_DISCONNECTED_LARGE, WATCH_DISCONNECTED_LARGE_builtin_bytes, sizeof(WATCH_DISCONNECTED_LARGE_builtin_bytes) }, { RESOURCE_ID_ARROW_DOWN, ARROW_DOWN_builtin_bytes, sizeof(ARROW_DOWN_builtin_bytes) }, { RESOURCE_ID_VOICE_MICROPHONE_LARGE, VOICE_MICROPHONE_LARGE_builtin_bytes, sizeof(VOICE_MICROPHONE_LARGE_builtin_bytes) }, - { RESOURCE_ID_JS_TICTOC, JS_TICTOC_builtin_bytes, sizeof(JS_TICTOC_builtin_bytes) }, + { RESOURCE_ID_ALARM_CLOCK_LARGE, ALARM_CLOCK_LARGE_builtin_bytes, sizeof(ALARM_CLOCK_LARGE_builtin_bytes) }, + { RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE, BATTERY_ICON_CHARGING_LARGE_builtin_bytes, sizeof(BATTERY_ICON_CHARGING_LARGE_builtin_bytes) }, + { RESOURCE_ID_RESULT_DELETED_LARGE, RESULT_DELETED_LARGE_builtin_bytes, sizeof(RESULT_DELETED_LARGE_builtin_bytes) }, + { RESOURCE_ID_RESULT_SHREDDED_LARGE, RESULT_SHREDDED_LARGE_builtin_bytes, sizeof(RESULT_SHREDDED_LARGE_builtin_bytes) }, + { RESOURCE_ID_RESULT_DISMISSED_LARGE, RESULT_DISMISSED_LARGE_builtin_bytes, sizeof(RESULT_DISMISSED_LARGE_builtin_bytes) }, + { RESOURCE_ID_GENERIC_CONFIRMATION_LARGE, GENERIC_CONFIRMATION_LARGE_builtin_bytes, sizeof(GENERIC_CONFIRMATION_LARGE_builtin_bytes) }, + { RESOURCE_ID_INCOMING_PHONE_CALL_LARGE, INCOMING_PHONE_CALL_LARGE_builtin_bytes, sizeof(INCOMING_PHONE_CALL_LARGE_builtin_bytes) }, + { RESOURCE_ID_RESULT_MUTE_LARGE, RESULT_MUTE_LARGE_builtin_bytes, sizeof(RESULT_MUTE_LARGE_builtin_bytes) }, + { RESOURCE_ID_RESULT_SENT_LARGE, RESULT_SENT_LARGE_builtin_bytes, sizeof(RESULT_SENT_LARGE_builtin_bytes) }, + { RESOURCE_ID_RESULT_UNMUTE_LARGE, RESULT_UNMUTE_LARGE_builtin_bytes, sizeof(RESULT_UNMUTE_LARGE_builtin_bytes) }, }; diff --git a/tests/fixtures/resources/pfs_resource_table.c b/tests/fixtures/resources/pfs_resource_table.c index 8276c9b3b5..7cac8e3035 100644 --- a/tests/fixtures/resources/pfs_resource_table.c +++ b/tests/fixtures/resources/pfs_resource_table.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // diff --git a/tests/fixtures/resources/system_resources_asterix.pbpack b/tests/fixtures/resources/system_resources_asterix.pbpack new file mode 100644 index 0000000000..bc1a9f8118 Binary files /dev/null and b/tests/fixtures/resources/system_resources_asterix.pbpack differ diff --git a/tests/fixtures/resources/system_resources_gabbro.pbpack b/tests/fixtures/resources/system_resources_gabbro.pbpack new file mode 100644 index 0000000000..c89fcddfe3 Binary files /dev/null and b/tests/fixtures/resources/system_resources_gabbro.pbpack differ diff --git a/tests/fixtures/resources/system_resources_obelix.pbpack b/tests/fixtures/resources/system_resources_obelix.pbpack new file mode 100644 index 0000000000..5ec920ae68 Binary files /dev/null and b/tests/fixtures/resources/system_resources_obelix.pbpack differ diff --git a/tests/fixtures/resources/system_resources_robert.pbpack b/tests/fixtures/resources/system_resources_robert.pbpack deleted file mode 100644 index f86c017b9e..0000000000 Binary files a/tests/fixtures/resources/system_resources_robert.pbpack and /dev/null differ diff --git a/tests/fixtures/resources/system_resources_silk.pbpack b/tests/fixtures/resources/system_resources_silk.pbpack deleted file mode 100644 index 806c2139fb..0000000000 Binary files a/tests/fixtures/resources/system_resources_silk.pbpack and /dev/null differ diff --git a/tests/fixtures/resources/system_resources_snowy.pbpack b/tests/fixtures/resources/system_resources_snowy.pbpack deleted file mode 100644 index 51b6224e74..0000000000 Binary files a/tests/fixtures/resources/system_resources_snowy.pbpack and /dev/null differ diff --git a/tests/fixtures/resources/timeline_resource_table.auto.c b/tests/fixtures/resources/timeline_resource_table.auto.c index 2c965bbe64..7cda4dec72 100644 --- a/tests/fixtures/resources/timeline_resource_table.auto.c +++ b/tests/fixtures/resources/timeline_resource_table.auto.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // @@ -21,7 +8,7 @@ // #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include diff --git a/tests/fw/applib/bluetooth/test_ble_ad_parse.c b/tests/fw/applib/bluetooth/test_ble_ad_parse.c index 7a28d09b55..d3e7c4e5e7 100644 --- a/tests/fw/applib/bluetooth/test_ble_ad_parse.c +++ b/tests/fw/applib/bluetooth/test_ble_ad_parse.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/bluetooth/ble_ad_parse.h" diff --git a/tests/fw/applib/bluetooth/test_ble_ibeacon.c b/tests/fw/applib/bluetooth/test_ble_ibeacon.c index c3a7d50b8b..96b49624c9 100644 --- a/tests/fw/applib/bluetooth/test_ble_ibeacon.c +++ b/tests/fw/applib/bluetooth/test_ble_ibeacon.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/bluetooth/ble_ibeacon.h" #include diff --git a/tests/fw/applib/bluetooth/wscript b/tests/fw/applib/bluetooth/wscript deleted file mode 100644 index 621404ef26..0000000000 --- a/tests/fw/applib/bluetooth/wscript +++ /dev/null @@ -1,23 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = - "src/fw/applib/bluetooth/ble_ad_parse.c " - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/system/hexdump.c " - "src/fw/util/buffer.c " - "tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_ble_ad_parse.c") - - clar(ctx, - sources_ant_glob = - "src/fw/applib/bluetooth/ble_ibeacon.c " - "src/fw/applib/bluetooth/ble_ad_parse.c " - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_ble_ibeacon.c") - -# vim:filetype=python diff --git a/tests/fw/applib/bluetooth/wscript_build b/tests/fw/applib/bluetooth/wscript_build new file mode 100644 index 0000000000..35cb98bb03 --- /dev/null +++ b/tests/fw/applib/bluetooth/wscript_build @@ -0,0 +1,22 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = + "src/fw/applib/bluetooth/ble_ad_parse.c " + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/system/hexdump.c " + "src/fw/util/buffer.c " + "tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_ble_ad_parse.c") + +clar(ctx, + sources_ant_glob = + "src/fw/applib/bluetooth/ble_ibeacon.c " + "src/fw/applib/bluetooth/ble_ad_parse.c " + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_ble_ibeacon.c") + +# vim:filetype=python diff --git a/tests/fw/applib/test_app_glance.c b/tests/fw/applib/test_app_glance.c index eee19bc88d..dea2bb27b4 100644 --- a/tests/fw/applib/test_app_glance.c +++ b/tests/fw/applib/test_app_glance.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -22,11 +9,11 @@ #include "process_management/app_install_manager.h" #include "resource/resource_ids.auto.h" #include "resource/timeline_resource_ids.auto.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "services/normal/blob_db/app_glance_db.h" -#include "services/normal/blob_db/app_glance_db_private.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "pbl/services/blob_db/app_glance_db.h" +#include "pbl/services/blob_db/app_glance_db_private.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/timeline/timeline_resources.h" #include "util/uuid.h" #define APP_GLANCE_TEST_UUID \ diff --git a/tests/fw/applib/test_app_inbox.c b/tests/fw/applib/test_app_inbox.c index fd272878a6..33fd3a6e34 100644 --- a/tests/fw/applib/test_app_inbox.c +++ b/tests/fw/applib/test_app_inbox.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "applib/app_inbox.h" #include "kernel/events.h" -#include "services/normal/app_inbox_service.h" +#include "pbl/services/app_inbox_service.h" #include "util/list.h" extern bool app_inbox_service_has_inbox_for_tag(AppInboxServiceTag tag); diff --git a/tests/fw/applib/test_app_message.c b/tests/fw/applib/test_app_message.c index ac4c16cc97..089956e55c 100644 --- a/tests/fw/applib/test_app_message.c +++ b/tests/fw/applib/test_app_message.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -214,7 +201,7 @@ static void prv_receive_test_data(uint8_t transaction_id, const bool oversized) message->transaction_id = transaction_id; message->payload->push.uuid = s_remote_app_uuid; memcpy(&message->payload->push.dictionary, s_expected_buffer, dict_length); - PBL_LOG(LOG_LEVEL_DEBUG, "message->transaction_id = %"PRIu32, message->transaction_id); + PBL_LOG_DBG("message->transaction_id = %"PRIu32, message->transaction_id); CommSession *session = s_fake_app_comm_session; app_message_app_protocol_msg_callback(session, buffer, message_length, NULL); @@ -224,7 +211,7 @@ static void prv_receive_ack_nack_callback(uint16_t endpoint_id, const uint8_t* data, unsigned int length) { AppMessage *message = (AppMessage*)data; cl_assert(length == sizeof(AppMessage)); - PBL_LOG(LOG_LEVEL_DEBUG, "message %"PRIu32", id1 %"PRIu32", id2 %"PRIu32, message->transaction_id, + PBL_LOG_DBG("message %"PRIu32", id1 %"PRIu32", id2 %"PRIu32, message->transaction_id, TEST_TRANSACTION_ID_1, TEST_TRANSACTION_ID_2); if (message->transaction_id == TEST_TRANSACTION_ID_1) { cl_assert_equal_b(s_ack_received_for_id_1, false); @@ -277,9 +264,6 @@ size_t sys_app_pp_app_message_inbox_size_maximum(void) { return 600; } -void sys_app_pp_app_message_analytics_count_drop(void) { -} - bool sys_get_current_app_is_js_allowed(void) { return false; } diff --git a/tests/fw/applib/test_app_outbox.c b/tests/fw/applib/test_app_outbox.c index a33bad1fe9..e12c3b20c4 100644 --- a/tests/fw/applib/test_app_outbox.c +++ b/tests/fw/applib/test_app_outbox.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_outbox.h" #include "applib/event_service_client.h" -#include "services/normal/app_outbox_service.h" +#include "pbl/services/app_outbox_service.h" #include "clar.h" extern void app_outbox_service_deinit(void); diff --git a/tests/fw/applib/test_app_smartstrap.c b/tests/fw/applib/test_app_smartstrap.c deleted file mode 100644 index c30ef70b60..0000000000 --- a/tests/fw/applib/test_app_smartstrap.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "applib/event_service_client.h" -#include "services/normal/accessory/smartstrap_attribute.h" -#include "kernel/pbl_malloc.h" - -#include "fake_smartstrap_profiles.h" -#include "fake_smartstrap_state.h" -#include "fake_system_task.h" - -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_mutex.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_serial.h" - -#define NON_NULL_MBUF ((MBuf *)1) -#define assert_result_ok(result) cl_assert(result == SmartstrapResultOk) -#define assert_result_invalid(result) cl_assert(result == SmartstrapResultInvalidArgs) -#define assert_result_busy(result) cl_assert(result == SmartstrapResultBusy) - -typedef void (*EventServiceEventHandler)(PebbleEvent *e, void *context); -typedef struct { - bool active; - SmartstrapAttribute *attribute; - size_t length; -} PendingInfo; - -static EventServiceEventHandler s_event_handler; -static PendingInfo s_pending_did_read; -static PendingInfo s_pending_did_write; -static PendingInfo s_pending_notified; - - -// Stubs / fakes - -void event_service_client_subscribe(EventServiceInfo *info) { - cl_assert(info->type == PEBBLE_SMARTSTRAP_EVENT); - s_event_handler = info->handler; -} - -void event_service_client_unsubscribe(EventServiceInfo *info) { - cl_assert(info->type == PEBBLE_SMARTSTRAP_EVENT); - s_event_handler = NULL; -} - -bool process_manager_send_event_to_process(PebbleTask task, PebbleEvent *e) { - cl_assert(task == PebbleTask_App); - cl_assert(e->type == PEBBLE_SMARTSTRAP_EVENT); - cl_assert(s_event_handler); - s_event_handler(e, NULL); - return true; -} - -void smartstrap_cancel_send(void) { -} - - -// Helper functions -static void prv_prepare_for_did_read(SmartstrapAttribute *attribute, uint16_t read_length) { - cl_assert(!s_pending_did_read.active); - s_pending_did_read = (PendingInfo) { - .active = true, - .attribute = attribute, - .length = read_length - }; -} - -static void prv_did_read_handler(SmartstrapAttribute *attribute, SmartstrapResult result, - const uint8_t *data, size_t length) { - cl_assert(data == (uint8_t *)attribute); - cl_assert(result == SmartstrapResultOk); - cl_assert(s_pending_did_read.active); - cl_assert(s_pending_did_read.attribute == attribute); - cl_assert(s_pending_did_read.length == length); - s_pending_did_read.active = false; -} - -static void prv_prepare_for_did_write(SmartstrapAttribute *attribute) { - cl_assert(!s_pending_did_write.active); - s_pending_did_write = (PendingInfo) { - .active = true, - .attribute = attribute - }; -} - -static void prv_did_write_handler(SmartstrapAttribute *attribute, SmartstrapResult result) { - cl_assert(result == SmartstrapResultOk); - cl_assert(s_pending_did_write.active); - cl_assert(s_pending_did_write.attribute == attribute); - s_pending_did_write.active = false; -} - -static void prv_prepare_for_notified(SmartstrapAttribute *attribute) { - cl_assert(!s_pending_notified.active); - s_pending_notified = (PendingInfo) { - .active = true, - .attribute = attribute - }; -} - -static void prv_notified_handler(SmartstrapAttribute *attribute) { - cl_assert(s_pending_notified.active); - cl_assert(s_pending_notified.attribute == attribute); - s_pending_notified.active = false; -} - - -// Setup - -void test_app_smartstrap__initialize(void) { - smartstrap_attribute_init(); - app_smartstrap_subscribe((SmartstrapHandlers) { - .did_read = prv_did_read_handler, - .did_write = prv_did_write_handler, - .notified = prv_notified_handler - }); -} - -void test_app_smartstrap__cleanup(void) { -} - - -// Tests - -void test_app_smartstrap__invalid_args(void) { - // create test attribute - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - - // smartstrap_attribute_create() - cl_assert(app_smartstrap_attribute_create(0x1111, 0x2222, 0) == NULL); - - // smartstrap_attribute_destroy() - app_smartstrap_attribute_destroy(NULL); - - // smartstrap_attribute_get_*_id() - cl_assert(app_smartstrap_attribute_get_service_id(NULL) == 0); - cl_assert(app_smartstrap_attribute_get_attribute_id(NULL) == 0); - - // smartstrap_attribute_begin_write() - uint8_t *buffer; - size_t buffer_len; - assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, NULL, NULL)); - assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, &buffer, NULL)); - assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, NULL, &buffer_len)); - assert_result_invalid(app_smartstrap_attribute_begin_write(NULL, &buffer, &buffer_len)); - assert_result_invalid(app_smartstrap_attribute_begin_write(attr, NULL, NULL)); - assert_result_invalid(app_smartstrap_attribute_begin_write(attr, &buffer, NULL)); - assert_result_invalid(app_smartstrap_attribute_begin_write(attr, NULL, &buffer_len)); - - // smartstrap_attribute_end_write() - assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 0, false)); - assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 0, true)); - assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 100, false)); - assert_result_invalid(app_smartstrap_attribute_end_write(NULL, 100, true)); - assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, false)); - assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, true)); - assert_result_invalid(app_smartstrap_attribute_end_write(attr, 100, false)); - assert_result_invalid(app_smartstrap_attribute_end_write(attr, 100, true)); - - // smartstrap_attribute_read() - assert_result_invalid(app_smartstrap_attribute_read(NULL)); - - // destroy test attribute - app_smartstrap_attribute_destroy(attr); -} - -void test_app_smartstrap__check_ids(void) { - // craete an attribute - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - cl_assert(attr != NULL); - - // verify the ids - cl_assert(app_smartstrap_attribute_get_service_id(attr) == 0x1111); - cl_assert(app_smartstrap_attribute_get_attribute_id(attr) == 0x2222); - - // destroy the attribute - app_smartstrap_attribute_destroy(attr); - - // verify that we can no longer get the ids - cl_assert(app_smartstrap_attribute_get_service_id(attr) == 0); - cl_assert(app_smartstrap_attribute_get_attribute_id(attr) == 0); -} - -void test_app_smartstrap__create_duplicate(void) { - // create the attribute once - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - cl_assert(attr != NULL); - - // try to create it again - SmartstrapAttribute *attr_dup = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - cl_assert(attr_dup == NULL); - - // destroy the attribute - app_smartstrap_attribute_destroy(attr); - - // destroy again (shouldn't do anything bad) - app_smartstrap_attribute_destroy(attr); -} - -void test_app_smartstrap__read(void) { - // create the attribute - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - - // start a read request - stub_pebble_tasks_set_current(PebbleTask_App); - assert_result_ok(app_smartstrap_attribute_read(attr)); - - // attempt to issue another read request - assert_result_busy(app_smartstrap_attribute_read(attr)); - - // trigger the read request to be sent and expect a did_write handler call - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - prv_prepare_for_did_write(attr); - cl_assert(smartstrap_attribute_send_pending()); - cl_assert(!s_pending_did_write.active); - - // attempt to issue another read request (should report busy) - assert_result_busy(app_smartstrap_attribute_read(attr)); - - // check that it was sent successfully - SmartstrapRequest request = { - .service_id = 0x1111, - .attribute_id = 0x2222, - .write_mbuf = NULL, - .read_mbuf = NON_NULL_MBUF, - .timeout_ms = SMARTSTRAP_TIMEOUT_DEFAULT - }; - fake_smartstrap_profiles_check_request_params(&request); - - // fake the response and expect a did_read handler call - prv_prepare_for_did_read(attr, 10); - smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, 0x1111, 0x2222, 10); - cl_assert(!s_pending_did_read.active); - - // destroy the attribute - app_smartstrap_attribute_destroy(attr); -} - -void test_app_smartstrap__write(void) { - // create the attribute - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - - // start a write request - stub_pebble_tasks_set_current(PebbleTask_App); - uint8_t *write_buffer = NULL; - size_t write_length = 0; - assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); - cl_assert(write_buffer == (uint8_t *)attr); - cl_assert(write_length == 100); - - // attempt to start another write request - assert_result_busy(app_smartstrap_attribute_read(attr)); - uint8_t *write_buffer2 = NULL; - size_t write_length2 = 0; - assert_result_busy(app_smartstrap_attribute_begin_write(attr, &write_buffer2, &write_length2)); - cl_assert(write_buffer2 == NULL); - cl_assert(write_length2 == 0); - - // end the write request without sending anything - assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, false)); - - // start the write request again - write_buffer = NULL; - write_length = 0; - assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); - cl_assert(write_buffer == (uint8_t *)attr); - cl_assert(write_length == 100); - - // end the write request - assert_result_ok(app_smartstrap_attribute_end_write(attr, 100, false)); - - // trigger the write request to be sent - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - cl_assert(smartstrap_attribute_send_pending()); - - // check that it was sent successfully - SmartstrapRequest request = { - .service_id = 0x1111, - .attribute_id = 0x2222, - .write_mbuf = NON_NULL_MBUF, - .read_mbuf = NULL, - .timeout_ms = SMARTSTRAP_TIMEOUT_DEFAULT - }; - - // fake the ACK and expect a did_write handler call - fake_smartstrap_profiles_check_request_params(&request); - prv_prepare_for_did_write(attr); - smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, 0x1111, 0x2222, 100); - cl_assert(!s_pending_did_write.active); - - // destroy the attribute - app_smartstrap_attribute_destroy(attr); -} - -void test_app_smartstrap__write_read(void) { - // create the attribute - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - - // start a write request - stub_pebble_tasks_set_current(PebbleTask_App); - uint8_t *write_buffer = NULL; - size_t write_length = 0; - assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); - cl_assert(write_buffer == (uint8_t *)attr); - cl_assert(write_length == 100); - - // end the write request without sending anything - assert_result_invalid(app_smartstrap_attribute_end_write(attr, 0, true)); - - // start the write request again - write_buffer = NULL; - write_length = 0; - assert_result_ok(app_smartstrap_attribute_begin_write(attr, &write_buffer, &write_length)); - cl_assert(write_buffer == (uint8_t *)attr); - cl_assert(write_length == 100); - - // end the write request with request_read=true - assert_result_ok(app_smartstrap_attribute_end_write(attr, 100, true)); - - // trigger the write request to be sent and expect a did_write handler call - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - prv_prepare_for_did_write(attr); - cl_assert(smartstrap_attribute_send_pending()); - cl_assert(!s_pending_did_write.active); - - // check that it was sent successfully - SmartstrapRequest request = { - .service_id = 0x1111, - .attribute_id = 0x2222, - .write_mbuf = NON_NULL_MBUF, - .read_mbuf = NON_NULL_MBUF, - .timeout_ms = SMARTSTRAP_TIMEOUT_DEFAULT - }; - - // fake the response and expect a did_write and a did_read handler call - fake_smartstrap_profiles_check_request_params(&request); - prv_prepare_for_did_read(attr, 100); - smartstrap_attribute_send_event(SmartstrapDataReceivedEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, 0x1111, 0x2222, 100); - cl_assert(!s_pending_did_read.active); - - // destroy the attribute - app_smartstrap_attribute_destroy(attr); -} - -void test_app_smartstrap__notify(void) { - // create the attribute - SmartstrapAttribute *attr = app_smartstrap_attribute_create(0x1111, 0x2222, 100); - - // send a notification and expect a notified handler call - prv_prepare_for_notified(attr); - smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, 0x1111, 0x2222, 0); - cl_assert(!s_pending_notified.active); - - // send a notification for a non-created attribute which shouldn't cause a notified handler call - smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, 0x1111, 0x3333, 0); - - // destroy the attribute - app_smartstrap_attribute_destroy(attr); - - // send a notification for the destroyed attribute which shouldn't cause a notified handler call - smartstrap_attribute_send_event(SmartstrapNotifyEvent, SmartstrapProfileGenericService, - SmartstrapResultOk, 0x1111, 0x2222, 0); -} diff --git a/tests/fw/applib/test_bitmap_layer.c b/tests/fw/applib/test_bitmap_layer.c index d04f414512..fd28cfc2db 100644 --- a/tests/fw/applib/test_bitmap_layer.c +++ b/tests/fw/applib/test_bitmap_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/applib/test_cpu_cache.c b/tests/fw/applib/test_cpu_cache.c index 0876782fad..b62a7e6426 100644 --- a/tests/fw/applib/test_cpu_cache.c +++ b/tests/fw/applib/test_cpu_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/cpu_cache.h" #include "mcu/cache.h" diff --git a/tests/fw/applib/test_pbl_std.c b/tests/fw/applib/test_pbl_std.c index a2960093e4..d4327f5db0 100644 --- a/tests/fw/applib/test_pbl_std.c +++ b/tests/fw/applib/test_pbl_std.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/pbl_std/pbl_std.h" #include "applib/pbl_std/locale.h" @@ -46,7 +33,7 @@ time_t sys_time_utc_to_local(time_t t) { return t; } -int localized_strftime(char* s, size_t maxsize, const char* format, +size_t localized_strftime(char* s, size_t maxsize, const char* format, const struct tm* tim_p, char *locale) { return 0; } const char *get_timezone_abbr(void) { @@ -54,6 +41,9 @@ const char *get_timezone_abbr(void) { return s_timezone_abbr; } +int32_t time_get_gmtoffset(void) { return 0; } +int32_t time_get_dstoffset(void) { return 0; } + void sys_copy_timezone_abbr(char* timezone_abbr, time_t time) { const char* sys_tz = get_timezone_abbr(); strncpy(timezone_abbr, sys_tz, TZ_LEN); diff --git a/tests/fw/applib/test_persist.c b/tests/fw/applib/test_persist.c index 0a96979b9b..23c45cba63 100644 --- a/tests/fw/applib/test_persist.c +++ b/tests/fw/applib/test_persist.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -23,8 +10,8 @@ #include "flash_region/flash_region.h" #include "process_management/app_install_manager.h" #include "process_management/pebble_process_md.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/persist.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/persist.h" #include "system/logging.h" // Stubs @@ -282,7 +269,7 @@ void test_persist__legacy2_max_usage(void) { PBL_LOG_VERBOSE("n = %d", n); for (int i = 0; i < n; ++i) { - PBL_LOG(LOG_LEVEL_DEBUG, "i = %d", i); + PBL_LOG_DBG("i = %d", i); cl_assert_equal_i(persist_write_data(i, &buffer, sizeof(buffer)), sizeof(buffer)); } diff --git a/tests/fw/applib/test_template_string.c b/tests/fw/applib/test_template_string.c index 17f4b96c4e..74998fbac1 100644 --- a/tests/fw/applib/test_template_string.c +++ b/tests/fw/applib/test_template_string.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/applib/test_text_render.c b/tests/fw/applib/test_text_render.c index 8803ed6c0e..103fc47bd8 100644 --- a/tests/fw/applib/test_text_render.c +++ b/tests/fw/applib/test_text_render.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" #include "applib/graphics/text_render.h" diff --git a/tests/fw/applib/test_text_resources.c b/tests/fw/applib/test_text_resources.c index 604a4425f8..fb8ded8050 100644 --- a/tests/fw/applib/test_text_resources.c +++ b/tests/fw/applib/test_text_resources.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "fixtures/load_test_resources.h" @@ -299,7 +286,14 @@ void test_text_resources__test_glyph_decompression(void) { // To do this, simply copy the GOTHIC_18 stanza in resource/normal/base/resource_map.json, change // the name to include _COMPRESSED, and add the field: "compress": "RLE4". Rebuild, and run // ./tools/update_system_pbpack.sh - uint32_t gothic_18_compressed_handle = RESOURCE_ID_GOTHIC_18_COMPRESSED; // Read source to fix + // GOTHIC_18_COMPRESSED isn't in the regular pbpack; it has to be added by hand (see comment + // above) and the resource_ids.auto.h override updated to give it a real ID. When it's absent + // the symbol isn't defined, so skip the decompression coverage entirely. +#ifdef RESOURCE_ID_GOTHIC_18_COMPRESSED + uint32_t gothic_18_compressed_handle = RESOURCE_ID_GOTHIC_18_COMPRESSED; + if (gothic_18_compressed_handle == INVALID_RESOURCE) { + return; + } cl_assert(text_resources_init_font(0, gothic_18_compressed_handle, 0, &font_info_compressed)); cl_assert_equal_i(FONT_VERSION(font_info_compressed.base.md.version), 3); cl_assert(HAS_FEATURE(font_info_compressed.base.md.version, VERSION_FIELD_FEATURE_RLE4)); @@ -325,4 +319,5 @@ void test_text_resources__test_glyph_decompression(void) { cl_assert_equal_m(glyph->data, glyph_buffer, glyph_size); } } +#endif } diff --git a/tests/fw/applib/test_unobstructed_area_service.c b/tests/fw/applib/test_unobstructed_area_service.c index 7a14d6ed29..79eb56d971 100644 --- a/tests/fw/applib/test_unobstructed_area_service.c +++ b/tests/fw/applib/test_unobstructed_area_service.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "pebble_asserts.h" -#include "applib/unobstructed_area_service.h" +#include "applib/unobstructed_area_service_private.h" // Stubs ///////////////////// @@ -97,6 +84,8 @@ void test_unobstructed_area_service__cleanup(void) { void test_unobstructed_area_service__subscribe(void) { // Unsubscribing first should not crash app_unobstructed_area_service_unsubscribe(); + cl_assert(!unobstructed_area_service_is_subscribed(app_state_get_unobstructed_area_state())); + cl_assert(!unobstructed_area_service_has_requested_area(app_state_get_unobstructed_area_state())); cl_assert(!app_state_get_unobstructed_area_state()->handlers.will_change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.did_change); @@ -112,9 +101,11 @@ void test_unobstructed_area_service__subscribe(void) { cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.will_change, prv_will_change); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.change, prv_change); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.did_change, prv_did_change); + cl_assert(unobstructed_area_service_is_subscribed(app_state_get_unobstructed_area_state())); // Unsubscribing after subscription should cancel subscriptions app_unobstructed_area_service_unsubscribe(); + cl_assert(!unobstructed_area_service_is_subscribed(app_state_get_unobstructed_area_state())); cl_assert(!app_state_get_unobstructed_area_state()->handlers.will_change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.did_change); @@ -277,6 +268,7 @@ void test_unobstructed_area_service__did_change_no_will(void) { void test_unobstructed_area_service__layer_no_clip(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); + cl_assert(!unobstructed_area_service_has_requested_area(app_state_get_unobstructed_area_state())); Layer root_layer = { .bounds = GRect(100, 100, 200, 200), @@ -284,6 +276,7 @@ void test_unobstructed_area_service__layer_no_clip(void) { GRect unobstructed_bounds; layer_get_unobstructed_bounds(&root_layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, root_layer.bounds); + cl_assert(unobstructed_area_service_has_requested_area(app_state_get_unobstructed_area_state())); } void test_unobstructed_area_service__layer_clip_x_y(void) { diff --git a/tests/fw/applib/wscript b/tests/fw/applib/wscript deleted file mode 100644 index 6014b6fec2..0000000000 --- a/tests/fw/applib/wscript +++ /dev/null @@ -1,161 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob=" src/fw/applib/app_message/app_message.c" - " src/fw/applib/app_message/app_message_outbox.c" - " src/fw/applib/app_message/app_message_inbox.c" - " src/fw/util/rand/rand.c" - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" - " src/fw/process_management/pebble_process_info.c" - " src/fw/util/dict.c", - test_sources_ant_glob="test_app_message.c") - - clar(ctx, - sources_ant_glob=(" src/fw/applib/app_inbox.c" - " src/fw/services/normal/app_inbox_service.c" - ), - test_sources_ant_glob="test_app_inbox.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=(" src/fw/applib/app_outbox.c" - " src/fw/services/normal/app_outbox_service.c" - ), - test_sources_ant_glob="test_app_outbox.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=(" src/fw/applib/cpu_cache.c" - " src/libos/mcu/cache_arm.c" - ), - test_sources_ant_glob="test_cpu_cache.c", - defines=["UNITTEST_WITH_SYSCALL_PRIVILEGES=1"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = ( - " src/fw/flash_region/filesystem_regions.c" - " src/fw/flash_region/flash_region.c " - " src/fw/util/dict.c" - " src/fw/applib/persist.c" - " src/fw/util/rand/rand.c" - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" - " src/fw/services/normal/filesystem/app_file.c" - " src/fw/services/normal/filesystem/flash_translation.c" - " src/fw/services/normal/filesystem/pfs.c" - " src/fw/services/normal/legacy/persist_map.c" - " src/fw/services/normal/persist.c" - " src/fw/services/normal/settings/settings_file.c" - " src/fw/services/normal/settings/settings_raw_iter.c" - " src/fw/util/crc8.c" - " src/fw/util/legacy_checksum.c" - " tests/fakes/fake_rtc.c" - " tests/fakes/fake_spi_flash.c" - " tests/fixtures/resources/builtin_resources.auto.c" - ), - test_sources_ant_glob = "test_persist.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = ( - " src/fw/flash_region/flash_region.c" - " src/fw/applib/pbl_std/pbl_std.c" - " tests/fakes/fake_rtc.c" - " tests/fakes/fake_spi_flash.c" - ), - test_sources_ant_glob = "test_pbl_std.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = ( - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/text_render.c" - " src/fw/applib/fonts/codepoint.c" - " tests/fakes/fake_gbitmap_png.c" - ), - test_sources_ant_glob = "test_text_render.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/fonts/fonts.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c", - test_sources_ant_glob = "test_text_resources.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = " src/fw/applib/ui/bitmap_layer.c" \ - " src/fw/applib/ui/layer.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c", - test_sources_ant_glob = "test_bitmap_layer.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/applib/app_smartstrap.c" \ - " src/fw/services/normal/accessory/smartstrap_attribute.c" \ - " src/fw/util/mbuf.c" \ - " tests/fakes/fake_smartstrap_connection.c" \ - " tests/fakes/fake_smartstrap_profiles.c" \ - " tests/fakes/fake_smartstrap_state.c", - test_sources_ant_glob = "test_app_smartstrap.c", - defines=["CAPABILITY_HAS_ACCESSORY_CONNECTOR=1"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=( - "src/fw/applib/graphics/gtypes.c " - "src/fw/applib/ui/layer.c " - "src/fw/applib/unobstructed_area_service.c " - "tests/fakes/fake_events.c " - ), - test_sources_ant_glob="test_unobstructed_area_service.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=( - " src/fw/applib/app_glance.c" - " src/fw/applib/template_string.c" - " src/fw/services/normal/app_glances/app_glance_service.c" - " src/fw/services/normal/blob_db/app_glance_db.c" - " src/fw/services/normal/timeline/attribute.c" - " src/fw/util/crc8.c" - " src/fw/util/lru_cache.c" - " tests/fakes/fake_rtc.c" - " tests/fakes/fake_settings_file.c" - ), - test_sources_ant_glob="test_app_glance.c", - defines=["CAPABILITY_HAS_APP_GLANCES=1"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=( - "src/fw/applib/template_string.c " - ), - test_sources_ant_glob="test_template_string.c", - override_includes=['dummy_board']) - - -# vim:filetype=python diff --git a/tests/fw/applib/wscript_build b/tests/fw/applib/wscript_build new file mode 100644 index 0000000000..861fe79b98 --- /dev/null +++ b/tests/fw/applib/wscript_build @@ -0,0 +1,147 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob=" src/fw/applib/app_message/app_message.c" + " src/fw/applib/app_message/app_message_outbox.c" + " src/fw/applib/app_message/app_message_inbox.c" + " src/fw/util/rand/rand.c" + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" + " src/fw/process_management/pebble_process_info.c" + " src/fw/util/dict.c", + test_sources_ant_glob="test_app_message.c") + +clar(ctx, + sources_ant_glob=(" src/fw/applib/app_inbox.c" + " src/fw/services/app_inbox_service/service.c" + ), + test_sources_ant_glob="test_app_inbox.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=(" src/fw/applib/app_outbox.c" + " src/fw/services/app_outbox_service/service.c" + ), + test_sources_ant_glob="test_app_outbox.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=(" src/fw/applib/cpu_cache.c" + " src/libos/mcu/cache_arm.c" + ), + test_sources_ant_glob="test_cpu_cache.c", + defines=["UNITTEST_WITH_SYSCALL_PRIVILEGES=1"], + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = ( + " src/fw/flash_region/filesystem_regions.c" + " src/fw/flash_region/flash_region.c " + " src/fw/util/dict.c" + " src/fw/applib/persist.c" + " src/fw/util/rand/rand.c" + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" + " src/fw/services/filesystem/app_file.c" + " src/fw/services/filesystem/flash_translation.c" + " src/fw/services/filesystem/pfs.c" + " src/fw/services/legacy/persist_map.c" + " src/fw/services/persist/service.c" + " src/fw/services/settings/settings_file.c" + " src/fw/services/settings/settings_raw_iter.c" + " src/fw/util/crc8.c" + " src/fw/util/legacy_checksum.c" + " tests/fakes/fake_rtc.c" + " tests/fakes/fake_spi_flash.c" + " tests/fixtures/resources/builtin_resources.auto.c" + ), + test_sources_ant_glob = "test_persist.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = ( + " src/fw/flash_region/flash_region.c" + " src/fw/applib/pbl_std/pbl_std.c" + " tests/fakes/fake_rtc.c" + " tests/fakes/fake_spi_flash.c" + ), + test_sources_ant_glob = "test_pbl_std.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = ( + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/text_render.c" + " src/fw/applib/fonts/codepoint.c" + " tests/fakes/fake_gbitmap_png.c" + ), + test_sources_ant_glob = "test_text_render.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/fonts/fonts.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c", + test_sources_ant_glob = "test_text_resources.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = " src/fw/applib/ui/bitmap_layer.c" \ + " src/fw/applib/ui/layer.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c", + test_sources_ant_glob = "test_bitmap_layer.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + "src/fw/applib/graphics/gtypes.c " + "src/fw/applib/ui/layer.c " + "src/fw/applib/unobstructed_area_service.c " + "tests/fakes/fake_events.c " + ), + test_sources_ant_glob="test_unobstructed_area_service.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + " src/fw/applib/app_glance.c" + " src/fw/applib/template_string.c" + " src/fw/services/app_glances/app_glance_service.c" + " src/fw/services/blob_db/app_glance_db.c" + " src/fw/services/timeline/attribute.c" + " src/fw/util/crc8.c" + " src/fw/util/lru_cache.c" + " tests/fakes/fake_rtc.c" + " tests/fakes/fake_settings_file.c" + ), + test_sources_ant_glob="test_app_glance.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + "src/fw/applib/template_string.c " + ), + test_sources_ant_glob="test_template_string.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/apps/system_apps/health/test_health_activity_detail_card.c b/tests/fw/apps/system_apps/health/test_health_activity_detail_card.c index 4ef16d9c5c..5f29b176b4 100644 --- a/tests/fw/apps/system_apps/health/test_health_activity_detail_card.c +++ b/tests/fw/apps/system_apps/health/test_health_activity_detail_card.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_activity_detail_card.h" -#include "apps/system_apps/health/health_detail_card.h" -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" -#include "apps/system_apps/health/health_progress.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/activity_detail_card.h" +#include "apps/system/health/detail_card.h" +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" +#include "apps/system/health/progress.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_activity_summary_card.c b/tests/fw/apps/system_apps/health/test_health_activity_summary_card.c index 1dc3bc75dc..d14d4a4cf9 100644 --- a/tests/fw/apps/system_apps/health/test_health_activity_summary_card.c +++ b/tests/fw/apps/system_apps/health/test_health_activity_summary_card.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" -#include "apps/system_apps/health/health_activity_summary_card.h" -#include "apps/system_apps/health/health_activity_detail_card.h" -#include "apps/system_apps/health/health_detail_card.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" +#include "apps/system/health/activity_summary_card.h" +#include "apps/system/health/activity_detail_card.h" +#include "apps/system/health/detail_card.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_app_includes.h b/tests/fw/apps/system_apps/health/test_health_app_includes.h index 839f93e2d4..2e9fdb31c4 100644 --- a/tests/fw/apps/system_apps/health/test_health_app_includes.h +++ b/tests/fw/apps/system_apps/health/test_health_app_includes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/window_private.h" #include "util/size.h" @@ -55,6 +42,7 @@ #include "stubs_status_bar_layer.h" #include "stubs_syscalls.h" #include "stubs_task_watchdog.h" +#include "stubs_vibes.h" #include "stubs_window_manager.h" #include "stubs_window_stack.h" diff --git a/tests/fw/apps/system_apps/health/test_health_card_view.c b/tests/fw/apps/system_apps/health/test_health_card_view.c index 00ebbae86b..a374183938 100644 --- a/tests/fw/apps/system_apps/health/test_health_card_view.c +++ b/tests/fw/apps/system_apps/health/test_health_card_view.c @@ -1,28 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "apps/system_apps/health/health_card_view.h" -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" -#include "apps/system_apps/health/health_detail_card.h" -#include "apps/system_apps/health/health_activity_summary_card.h" -#include "apps/system_apps/health/health_activity_detail_card.h" -#include "apps/system_apps/health/health_hr_summary_card.h" -#include "apps/system_apps/health/health_sleep_summary_card.h" -#include "apps/system_apps/health/health_sleep_detail_card.h" +#include "apps/system/health/card_view.h" +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" +#include "apps/system/health/detail_card.h" +#include "apps/system/health/activity_summary_card.h" +#include "apps/system/health/activity_detail_card.h" +#include "apps/system/health/hr_summary_card.h" +#include "apps/system/health/sleep_summary_card.h" +#include "apps/system/health/sleep_detail_card.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_detail_card.c b/tests/fw/apps/system_apps/health/test_health_detail_card.c index f6729396d0..c3fb10df3c 100644 --- a/tests/fw/apps/system_apps/health/test_health_detail_card.c +++ b/tests/fw/apps/system_apps/health/test_health_detail_card.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_detail_card.h" -#include "apps/system_apps/health/health_progress.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/detail_card.h" +#include "apps/system/health/progress.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_hr_detail_card.c b/tests/fw/apps/system_apps/health/test_health_hr_detail_card.c index 579cb4f77c..2a9060c7e9 100644 --- a/tests/fw/apps/system_apps/health/test_health_hr_detail_card.c +++ b/tests/fw/apps/system_apps/health/test_health_hr_detail_card.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_hr_detail_card.h" -#include "apps/system_apps/health/health_detail_card.h" -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/hr_detail_card.h" +#include "apps/system/health/detail_card.h" +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_hr_summary_card.c b/tests/fw/apps/system_apps/health/test_health_hr_summary_card.c index b48f276089..c0b9214b3d 100644 --- a/tests/fw/apps/system_apps/health/test_health_hr_summary_card.c +++ b/tests/fw/apps/system_apps/health/test_health_hr_summary_card.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" -#include "apps/system_apps/health/health_hr_summary_card.h" -#include "apps/system_apps/health/health_hr_detail_card.h" -#include "apps/system_apps/health/health_detail_card.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" +#include "apps/system/health/hr_summary_card.h" +#include "apps/system/health/hr_detail_card.h" +#include "apps/system/health/detail_card.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_sleep_detail_card.c b/tests/fw/apps/system_apps/health/test_health_sleep_detail_card.c index 10a720fdf2..c0a3e11327 100644 --- a/tests/fw/apps/system_apps/health/test_health_sleep_detail_card.c +++ b/tests/fw/apps/system_apps/health/test_health_sleep_detail_card.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_sleep_detail_card.h" -#include "apps/system_apps/health/health_detail_card.h" -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/sleep_detail_card.h" +#include "apps/system/health/detail_card.h" +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/test_health_sleep_summary_card.c b/tests/fw/apps/system_apps/health/test_health_sleep_summary_card.c index 283d566183..0795087664 100644 --- a/tests/fw/apps/system_apps/health/test_health_sleep_summary_card.c +++ b/tests/fw/apps/system_apps/health/test_health_sleep_summary_card.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/health/health_data.h" -#include "apps/system_apps/health/health_data_private.h" -#include "apps/system_apps/health/health_sleep_summary_card.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/health/data.h" +#include "apps/system/health/data_private.h" +#include "apps/system/health/sleep_summary_card.h" #include "test_health_app_includes.h" diff --git a/tests/fw/apps/system_apps/health/wscript b/tests/fw/apps/system_apps/health/wscript deleted file mode 100644 index 2070de41a6..0000000000 --- a/tests/fw/apps/system_apps/health/wscript +++ /dev/null @@ -1,193 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - rendering_sources = \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" \ - " src/fw/applib/graphics/gbitmap_sequence.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/ui/kino/kino_reel.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ - " src/fw/applib/ui/kino/kino_reel_pdci.c" \ - " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/menu_layer_system_cells.c" \ - " src/fw/applib/vendor/tinflate/tinflate.c" \ - " src/fw/applib/vendor/uPNG/upng.c" \ - " src/fw/board/displays/display_spalding.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " tests/stubs/stubs_animation.c" \ - " tests/stubs/stubs_system_theme.c" - - health_app_sources = \ - " src/fw/applib/graphics/gpath_builder.c" \ - " src/fw/applib/ui/action_button.c" \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/kino/kino_layer.c" \ - " src/fw/applib/ui/kino/kino_player.c" \ - " src/fw/applib/ui/kino/kino_reel/transform.c" \ - " src/fw/applib/ui/kino/kino_reel_custom.c" \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/apps/system_apps/health/health_data.c" \ - " src/fw/apps/system_apps/health/health_progress.c" \ - " src/fw/apps/system_apps/health/health_ui.c" \ - " src/fw/apps/system_apps/timeline/text_node.c" \ - " src/fw/services/normal/activity/health_util.c" \ - " src/fw/services/normal/timeline/timeline_resources.c" \ - " src/fw/util/buffer.c" \ - " src/fw/util/stats.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_clock.c" \ - " tests/fakes/fake_fonts.c" \ - " tests/fixtures/resources/timeline_resource_table.auto.c" - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_activity_detail_card.c" \ - " src/fw/apps/system_apps/health/health_activity_summary_card.c" \ - " src/fw/apps/system_apps/health/health_hr_detail_card.c" \ - " src/fw/apps/system_apps/health/health_hr_summary_card.c" \ - " src/fw/apps/system_apps/health/health_sleep_detail_card.c" \ - " src/fw/apps/system_apps/health/health_sleep_summary_card.c" \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_card_view.c", - test_sources_ant_glob = "test_health_card_view.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_activity_detail_card.c" \ - " src/fw/apps/system_apps/health/health_activity_summary_card.c", - test_sources_ant_glob = "test_health_activity_summary_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_sleep_detail_card.c" \ - " src/fw/apps/system_apps/health/health_sleep_summary_card.c", - test_sources_ant_glob = "test_health_sleep_summary_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_hr_detail_card.c" \ - " src/fw/apps/system_apps/health/health_hr_summary_card.c", - test_sources_ant_glob = "test_health_hr_summary_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_activity_detail_card.c", - test_sources_ant_glob = "test_health_activity_detail_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_sleep_detail_card.c", - test_sources_ant_glob = "test_health_sleep_detail_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c" \ - " src/fw/apps/system_apps/health/health_hr_detail_card.c", - test_sources_ant_glob = "test_health_hr_detail_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - health_app_sources + \ - " src/fw/apps/system_apps/health/health_detail_card.c", - test_sources_ant_glob = "test_health_detail_card.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - -# vim:filetype=python diff --git a/tests/fw/apps/system_apps/health/wscript_build b/tests/fw/apps/system_apps/health/wscript_build new file mode 100644 index 0000000000..87e787877e --- /dev/null +++ b/tests/fw/apps/system_apps/health/wscript_build @@ -0,0 +1,194 @@ +from waftools.pebble_test import clar + +rendering_sources = \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" \ + " src/fw/applib/graphics/gbitmap_sequence.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/ui/kino/kino_reel.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ + " src/fw/applib/ui/kino/kino_reel_pdci.c" \ + " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/menu_layer_system_cells.c" \ + " src/fw/applib/vendor/tinflate/tinflate.c" \ + " src/fw/applib/vendor/uPNG/upng.c" \ + " src/fw/board/displays/display_getafix.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " tests/stubs/stubs_animation.c" \ + " tests/stubs/stubs_system_theme.c" + +health_app_sources = \ + " src/fw/applib/graphics/gpath_builder.c" \ + " src/fw/applib/ui/action_button.c" \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/kino/kino_layer.c" \ + " src/fw/applib/ui/kino/kino_player.c" \ + " src/fw/applib/ui/kino/kino_reel/transform.c" \ + " src/fw/applib/ui/kino/kino_reel_custom.c" \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/apps/system/health/data.c" \ + " src/fw/apps/system/health/progress.c" \ + " src/fw/apps/system/health/ui.c" \ + " src/fw/apps/system/timeline/text_node.c" \ + " src/fw/services/activity/health_util.c" \ + " src/fw/services/timeline/timeline_resources.c" \ + " src/fw/util/buffer.c" \ + " src/fw/util/stats.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_clock.c" \ + " tests/fakes/fake_fonts.c" \ + " tests/fixtures/resources/timeline_resource_table.auto.c" + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/activity_detail_card.c" \ + " src/fw/apps/system/health/activity_summary_card.c" \ + " src/fw/apps/system/health/hr_detail_card.c" \ + " src/fw/apps/system/health/hr_summary_card.c" \ + " src/fw/apps/system/health/sleep_detail_card.c" \ + " src/fw/apps/system/health/sleep_summary_card.c" \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/card_view.c", + test_sources_ant_glob = "test_health_card_view.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/activity_detail_card.c" \ + " src/fw/apps/system/health/activity_summary_card.c", + test_sources_ant_glob = "test_health_activity_summary_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/sleep_detail_card.c" \ + " src/fw/apps/system/health/sleep_summary_card.c", + test_sources_ant_glob = "test_health_sleep_summary_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/hr_detail_card.c" \ + " src/fw/apps/system/health/hr_summary_card.c", + test_sources_ant_glob = "test_health_hr_summary_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['asterix']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/activity_detail_card.c", + test_sources_ant_glob = "test_health_activity_detail_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/sleep_detail_card.c", + test_sources_ant_glob = "test_health_sleep_detail_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c" \ + " src/fw/apps/system/health/hr_detail_card.c", + test_sources_ant_glob = "test_health_hr_detail_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + health_app_sources + \ + " src/fw/apps/system/health/detail_card.c", + test_sources_ant_glob = "test_health_detail_card.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +# vim:filetype=python diff --git a/tests/fw/apps/system_apps/launcher/test_launcher_menu_layer.c b/tests/fw/apps/system_apps/launcher/test_launcher_menu_layer.c index 0388c9f896..7f713473ef 100644 --- a/tests/fw/apps/system_apps/launcher/test_launcher_menu_layer.c +++ b/tests/fw/apps/system_apps/launcher/test_launcher_menu_layer.c @@ -1,26 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" +#include "applib/ui/vibes.h" #include "applib/ui/window_private.h" -#include "apps/system_apps/launcher/default/launcher_menu_layer.h" +#include "apps/system/launcher/default/menu_layer.h" +#include "shell/prefs.h" #include "resource/resource_ids.auto.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "services/normal/blob_db/app_glance_db.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "pbl/services/blob_db/app_glance_db.h" #include "util/size.h" static GContext s_ctx; @@ -32,7 +21,7 @@ static GContext s_ctx; #include "fake_settings_file.h" #include "fake_spi_flash.h" #include "fixtures/load_test_resources.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" extern const uint16_t g_timeline_resources[][TimelineResourceSizeCount]; #define TIMELINE_RESOURCE_TEST_FAKE_PNG (9999 | 0x80000000) @@ -229,6 +218,24 @@ bool timeline_resources_is_system(TimelineResourceId timeline_id) { #include "stubs_workout_service.h" #include "stubs_workout_utils.h" +GColor shell_prefs_get_theme_highlight_color(void) { + return GColorWhite; +} + +bool alerts_preferences_get_notification_alternative_design(void) { + return false; +} + +MenuScrollVibeBehavior shell_prefs_get_menu_scroll_vibe_behavior(void) { + return MenuScrollNoVibe; +} + +bool shell_prefs_get_menu_scroll_wrap_around_enable(void) { + return false; +} + +void vibes_enqueue_custom_pattern(VibePattern pattern) {} + // We can't include stubs_process_manager.h because it conflicts with the two helper includes below void process_manager_send_callback_event_to_process(PebbleTask task, void (*callback)(void *), void *data) {} diff --git a/tests/fw/apps/system_apps/launcher/wscript b/tests/fw/apps/system_apps/launcher/wscript deleted file mode 100644 index 77f133c92d..0000000000 --- a/tests/fw/apps/system_apps/launcher/wscript +++ /dev/null @@ -1,109 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - common_sources_ant_glob = ( - " src/fw/applib/fonts/codepoint.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/gdraw_command.c" - " src/fw/applib/graphics/gdraw_command_frame.c" - " src/fw/applib/graphics/gdraw_command_image.c" - " src/fw/applib/graphics/gdraw_command_list.c" - " src/fw/applib/graphics/gdraw_command_sequence.c" - " src/fw/applib/graphics/gdraw_command_transforms.c" - " src/fw/applib/graphics/gpath.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/graphics_bitmap.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/text_layout.c" - " src/fw/applib/graphics/text_render.c" - " src/fw/applib/graphics/text_resources.c" - " src/fw/applib/graphics/utf8.c" - " src/fw/applib/ui/animation_interpolate.c" - " src/fw/applib/ui/animation_timing.c" - " src/fw/applib/ui/inverter_layer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/ui/menu_layer.c" - " src/fw/applib/ui/scroll_layer.c" - " src/fw/applib/ui/shadows.c" - " src/fw/applib/ui/window.c" - " src/fw/drivers/flash/flash_crc.c" - " src/fw/flash_region/filesystem_regions.c" - " src/fw/flash_region/flash_region.c" - " src/fw/resource/resource.c" - " src/fw/resource/resource_storage.c" - " src/fw/resource/resource_storage_builtin.c" - " src/fw/resource/resource_storage_file.c" - " src/fw/resource/resource_storage_flash.c" - " src/fw/services/normal/filesystem/flash_translation.c" - " src/fw/services/normal/filesystem/pfs.c" - " src/fw/system/hexdump.c" - " src/fw/util/crc8.c" - " src/fw/util/legacy_checksum.c" - " tests/fakes/fake_display.c" - " tests/fakes/fake_rtc.c" - " tests/fakes/fake_spi_flash.c" - " tests/fixtures/resources/builtin_resources.auto.c" - " tests/fixtures/resources/pfs_resource_table.c" - ) - - clar(ctx, - sources_ant_glob=( - common_sources_ant_glob + - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gbitmap_sequence.c" - " src/fw/applib/template_string.c" - " src/fw/applib/ui/content_indicator.c" - " src/fw/applib/ui/kino/kino_reel.c" - " src/fw/applib/ui/kino/kino_reel_custom.c" - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" - " src/fw/applib/ui/kino/kino_reel_pdci.c" - " src/fw/applib/ui/kino/kino_reel_pdcs.c" - " src/fw/applib/ui/menu_layer_system_cells.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_alarms.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_generic.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_music.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_notifications.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_private.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_service.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_settings.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_structured.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_watchfaces.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_weather.c" - " src/fw/apps/system_apps/launcher/default/launcher_app_glance_workout.c" - " src/fw/apps/system_apps/launcher/default/launcher_menu_layer.c" - " src/fw/apps/system_apps/timeline/text_node.c" - " src/fw/board/displays/display_spalding.c" - " src/fw/services/normal/app_glances/app_glance_service.c" - " src/fw/services/normal/blob_db/app_glance_db.c" - " src/fw/services/normal/filesystem/app_file.c" - " src/fw/services/normal/timeline/attribute.c" - " src/fw/util/buffer.c" - " src/fw/util/lru_cache.c" - " tests/fakes/fake_applib_resource.c" - " tests/fakes/fake_fonts.c" - " tests/fakes/fake_settings_file.c" - " tests/fixtures/resources/timeline_resource_table.auto.c" - " tests/stubs/stubs_animation.c" - " tests/stubs/stubs_app_install_manager.c" - " tests/stubs/stubs_app_menu_data_source.c" - ), - test_sources_ant_glob="test_launcher_menu_layer.c", - defines=ctx.env.test_image_defines, - runtime_deps=(ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos), - override_includes=["dummy_board"], - platforms=["silk", "snowy", "spalding", "robert"]) - -# vim:filetype=python diff --git a/tests/fw/apps/system_apps/launcher/wscript_build b/tests/fw/apps/system_apps/launcher/wscript_build new file mode 100644 index 0000000000..dd34084f8f --- /dev/null +++ b/tests/fw/apps/system_apps/launcher/wscript_build @@ -0,0 +1,110 @@ +from waftools.pebble_test import clar + +common_sources_ant_glob = ( + " src/fw/applib/fonts/codepoint.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/gdraw_command.c" + " src/fw/applib/graphics/gdraw_command_frame.c" + " src/fw/applib/graphics/gdraw_command_image.c" + " src/fw/applib/graphics/gdraw_command_list.c" + " src/fw/applib/graphics/gdraw_command_sequence.c" + " src/fw/applib/graphics/gdraw_command_transforms.c" + " src/fw/applib/graphics/gpath.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/graphics_bitmap.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/text_layout.c" + " src/fw/applib/graphics/rtl_support.c" + " src/fw/applib/graphics/arabic_shaping.c" + " src/fw/applib/graphics/text_render.c" + " src/fw/applib/graphics/text_resources.c" + " src/fw/applib/graphics/utf8.c" + " src/fw/applib/ui/animation_interpolate.c" + " src/fw/applib/ui/animation_timing.c" + " src/fw/applib/ui/inverter_layer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/ui/menu_layer.c" + " src/fw/applib/ui/scroll_layer.c" + " src/fw/applib/ui/shadows.c" + " src/fw/applib/ui/window.c" + " src/fw/drivers/flash/flash_crc.c" + " src/fw/flash_region/filesystem_regions.c" + " src/fw/flash_region/flash_region.c" + " src/fw/resource/resource.c" + " src/fw/resource/resource_storage.c" + " src/fw/resource/resource_storage_builtin.c" + " src/fw/resource/resource_storage_file.c" + " src/fw/resource/resource_storage_flash.c" + " src/fw/services/filesystem/flash_translation.c" + " src/fw/services/filesystem/pfs.c" + " src/fw/system/hexdump.c" + " src/fw/util/crc8.c" + " src/fw/util/legacy_checksum.c" + " tests/fakes/fake_display.c" + " tests/fakes/fake_rtc.c" + " tests/fakes/fake_spi_flash.c" + " tests/fixtures/resources/builtin_resources.auto.c" + " tests/fixtures/resources/pfs_resource_table.c" +) + +clar(ctx, + sources_ant_glob=( + common_sources_ant_glob + + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gbitmap_sequence.c" + " src/fw/applib/template_string.c" + " src/fw/applib/ui/content_indicator.c" + " src/fw/applib/ui/kino/kino_reel.c" + " src/fw/applib/ui/kino/kino_reel_custom.c" + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" + " src/fw/applib/ui/kino/kino_reel_pdci.c" + " src/fw/applib/ui/kino/kino_reel_pdcs.c" + " src/fw/applib/ui/menu_layer_system_cells.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/apps/system/launcher/default/app_glance.c" + " src/fw/apps/system/launcher/default/app_glance_alarms.c" + " src/fw/apps/system/launcher/default/app_glance_generic.c" + " src/fw/apps/system/launcher/default/app_glance_music.c" + " src/fw/apps/system/launcher/default/app_glance_notifications.c" + " src/fw/apps/system/launcher/default/app_glance_private.c" + " src/fw/apps/system/launcher/default/app_glance_service.c" + " src/fw/apps/system/launcher/default/app_glance_settings.c" + " src/fw/apps/system/launcher/default/app_glance_structured.c" + " src/fw/apps/system/launcher/default/app_glance_watchfaces.c" + " src/fw/apps/system/launcher/default/app_glance_weather.c" + " src/fw/apps/system/launcher/default/app_glance_workout.c" + " src/fw/apps/system/launcher/default/menu_layer.c" + " src/fw/apps/system/timeline/text_node.c" + " src/fw/board/displays/display_getafix.c" + " src/fw/services/app_glances/app_glance_service.c" + " src/fw/services/blob_db/app_glance_db.c" + " src/fw/services/filesystem/app_file.c" + " src/fw/services/timeline/attribute.c" + " src/fw/util/buffer.c" + " src/fw/util/lru_cache.c" + " tests/fakes/fake_applib_resource.c" + " tests/fakes/fake_fonts.c" + " tests/fakes/fake_settings_file.c" + " tests/fixtures/resources/timeline_resource_table.auto.c" + " tests/stubs/stubs_animation.c" + " tests/stubs/stubs_app_install_manager.c" + " tests/stubs/stubs_app_menu_data_source.c" + ), + test_sources_ant_glob="test_launcher_menu_layer.c", + defines=ctx.env.test_image_defines, + runtime_deps=(ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos), + override_includes=["dummy_board"], + platforms=["obelix", "gabbro"]) + +# vim:filetype=python diff --git a/tests/fw/apps/system_apps/timeline/test_timeline_app_includes.h b/tests/fw/apps/system_apps/timeline/test_timeline_app_includes.h index 6c687f7524..d7218f37a9 100644 --- a/tests/fw/apps/system_apps/timeline/test_timeline_app_includes.h +++ b/tests/fw/apps/system_apps/timeline/test_timeline_app_includes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/window_private.h" #include "util/size.h" @@ -36,6 +23,8 @@ #include "stubs_action_menu.h" #include "stubs_activity.h" +#include "stubs_alerts.h" +#include "stubs_alerts_preferences.h" #include "stubs_analytics.h" #include "stubs_ancs.h" #include "stubs_animation_timing.h" @@ -87,6 +76,9 @@ #include "stubs_timeline_layout_animations.h" #include "stubs_timeline_peek.h" #include "stubs_timezone_database.h" +#include "stubs_vibe_score.h" +#include "stubs_vibe_score_info.h" +#include "stubs_vibes.h" #include "stubs_wakeup.h" #include "stubs_watchface.h" #include "stubs_window_manager.h" diff --git a/tests/fw/apps/system_apps/timeline/test_timeline_list_view.c b/tests/fw/apps/system_apps/timeline/test_timeline_list_view.c index bf42651d9f..5fea308050 100644 --- a/tests/fw/apps/system_apps/timeline/test_timeline_list_view.c +++ b/tests/fw/apps/system_apps/timeline/test_timeline_list_view.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/timeline/timeline.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/timeline/timeline.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "test_timeline_app_includes.h" @@ -157,17 +144,13 @@ static void prv_create_and_render_title_and_subtitle(bool past, uint16_t first_d } void test_timeline_list_view__title_and_subtitle_overlap_future(void) { -#if !PLATFORM_SPALDING prv_create_and_render_title_and_subtitle(false /* past */, MINUTES_PER_HOUR); FAKE_GRAPHICS_CONTEXT_CHECK_DEST_BITMAP_FILE(); -#endif } void test_timeline_list_view__title_and_subtitle_back_to_back_future(void) { -#if !PLATFORM_SPALDING prv_create_and_render_title_and_subtitle(false /* past */, MINUTES_PER_HOUR / 2); FAKE_GRAPHICS_CONTEXT_CHECK_DEST_BITMAP_FILE(); -#endif } void test_timeline_list_view__title_and_subtitle_free_time_future(void) { diff --git a/tests/fw/apps/system_apps/timeline/test_timeline_no_events.c b/tests/fw/apps/system_apps/timeline/test_timeline_no_events.c index 9d42ba5122..9b391f2fa1 100644 --- a/tests/fw/apps/system_apps/timeline/test_timeline_no_events.c +++ b/tests/fw/apps/system_apps/timeline/test_timeline_no_events.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/timeline/timeline.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/timeline/timeline.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "test_timeline_app_includes.h" diff --git a/tests/fw/apps/system_apps/timeline/wscript b/tests/fw/apps/system_apps/timeline/wscript deleted file mode 100644 index b8ee09f6db..0000000000 --- a/tests/fw/apps/system_apps/timeline/wscript +++ /dev/null @@ -1,148 +0,0 @@ -from waftools.pebble_test import clar - - -def build(ctx): - rendering_sources = ( - "src/fw/applib/fonts/codepoint.c " - "src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c " - "src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c " - "src/fw/applib/graphics/bitblt.c " - "src/fw/applib/graphics/framebuffer.c " - "src/fw/applib/graphics/gbitmap.c " - "src/fw/applib/graphics/gbitmap_png.c " - "src/fw/applib/graphics/gbitmap_sequence.c " - "src/fw/applib/graphics/gcolor_definitions.c " - "src/fw/applib/graphics/gdraw_command.c " - "src/fw/applib/graphics/gdraw_command_frame.c " - "src/fw/applib/graphics/gdraw_command_image.c " - "src/fw/applib/graphics/gdraw_command_list.c " - "src/fw/applib/graphics/gdraw_command_sequence.c " - "src/fw/applib/graphics/gpath.c " - "src/fw/applib/graphics/graphics.c " - "src/fw/applib/graphics/graphics_bitmap.c " - "src/fw/applib/graphics/graphics_circle.c " - "src/fw/applib/graphics/graphics_line.c " - "src/fw/applib/graphics/graphics_private.c " - "src/fw/applib/graphics/graphics_private_raw.c " - "src/fw/applib/graphics/gtransform.c " - "src/fw/applib/graphics/gtypes.c " - "src/fw/applib/graphics/perimeter.c " - "src/fw/applib/graphics/text_layout.c " - "src/fw/applib/graphics/text_render.c " - "src/fw/applib/graphics/text_resources.c " - "src/fw/applib/graphics/utf8.c " - "src/fw/applib/ui/kino/kino_reel.c " - "src/fw/applib/ui/kino/kino_reel_gbitmap.c " - "src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c " - "src/fw/applib/ui/kino/kino_reel_pdci.c " - "src/fw/applib/ui/kino/kino_reel_pdcs.c " - "src/fw/applib/ui/layer.c " - "src/fw/applib/ui/menu_layer_system_cells.c " - "src/fw/applib/vendor/tinflate/tinflate.c " - "src/fw/applib/vendor/uPNG/upng.c " - "src/fw/board/displays/display_spalding.c " - "src/fw/drivers/flash/flash_crc.c " - "src/fw/flash_region/filesystem_regions.c " - "src/fw/flash_region/flash_region.c " - "src/fw/resource/resource.c " - "src/fw/resource/resource_storage.c " - "src/fw/resource/resource_storage_builtin.c " - "src/fw/resource/resource_storage_file.c " - "src/fw/resource/resource_storage_flash.c " - "src/fw/services/normal/filesystem/app_file.c " - "src/fw/services/normal/filesystem/flash_translation.c " - "src/fw/services/normal/filesystem/pfs.c " - "src/fw/system/hexdump.c " - "src/fw/util/crc8.c " - "src/fw/util/legacy_checksum.c " - "tests/fakes/fake_animation.c " - "tests/fakes/fake_applib_resource.c " - "tests/fakes/fake_display.c " - "tests/fakes/fake_gbitmap_get_data_row.c " - "tests/fakes/fake_graphics_context.c " - "tests/fakes/fake_property_animation.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_spi_flash.c " - "tests/fixtures/resources/builtin_resources.auto.c " - "tests/fixtures/resources/pfs_resource_table.c " - "tests/stubs/stubs_animation.c " - ) - - timeline_app_sources = ( - "src/fw/applib/graphics/gdraw_command_transforms.c " - "src/fw/applib/graphics/gpath_builder.c " - "src/fw/applib/ui/action_button.c " - "src/fw/applib/ui/animation_interpolate.c " - "src/fw/applib/ui/content_indicator.c " - "src/fw/applib/ui/inverter_layer.c " - "src/fw/applib/ui/kino/kino_layer.c " - "src/fw/applib/ui/kino/kino_player.c " - "src/fw/applib/ui/kino/kino_reel/scale_segmented.c " - "src/fw/applib/ui/kino/kino_reel/transform.c " - "src/fw/applib/ui/kino/kino_reel/unfold.c " - "src/fw/applib/ui/kino/kino_reel_custom.c " - "src/fw/applib/ui/menu_layer.c " - "src/fw/applib/ui/scroll_layer.c " - "src/fw/applib/ui/shadows.c " - "src/fw/applib/ui/status_bar_layer.c " - "src/fw/applib/ui/text_layer.c " - "src/fw/applib/ui/text_layer_flow.c " - "src/fw/applib/ui/window.c " - "src/fw/apps/system_apps/timeline/peek_layer.c " - "src/fw/apps/system_apps/timeline/pin_window.c " - "src/fw/apps/system_apps/timeline/text_node.c " - "src/fw/apps/system_apps/timeline/timeline.c " - "src/fw/apps/system_apps/timeline/timeline_animations.c " - "src/fw/apps/system_apps/timeline/timeline_layer.c " - "src/fw/apps/system_apps/timeline/timeline_model.c " - "src/fw/apps/system_apps/timeline/timeline_relbar.c " - "src/fw/popups/timeline/timeline_item_layer.c " - "src/fw/services/common/clock.c " - "src/fw/services/normal/blob_db/pin_db.c " - "src/fw/services/normal/blob_db/timeline_item_storage.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/generic_layout.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/services/normal/timeline/layout_layer.c " - "src/fw/services/normal/timeline/layout_node.c " - "src/fw/services/normal/timeline/timeline.c " - "src/fw/services/normal/timeline/timeline_layout.c " - "src/fw/services/normal/timeline/timeline_resources.c " - "src/fw/shell/system_theme.c " - "src/fw/util/buffer.c " - "src/fw/util/stats.c " - "src/fw/util/stringlist.c " - "src/fw/util/time/mktime.c " - "src/fw/util/time/time.c " - "tests/fakes/fake_fonts.c " - "tests/fakes/fake_settings_file.c " - "tests/fixtures/resources/timeline_resource_table.auto.c " - "tests/stubs/stubs_evented_timer.c " - ) - - clar(ctx, - sources_ant_glob=( - rendering_sources + - timeline_app_sources - ), - test_sources_ant_glob="test_timeline_list_view.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk', 'robert']) - - clar(ctx, - sources_ant_glob=( - rendering_sources + - timeline_app_sources - ), - test_sources_ant_glob="test_timeline_no_events.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - # The no events is a builtin on silk and thus cannot be used in a unit test currently - platforms=['snowy', 'spalding', 'robert']) - -# vim:filetype=python diff --git a/tests/fw/apps/system_apps/timeline/wscript_build b/tests/fw/apps/system_apps/timeline/wscript_build new file mode 100644 index 0000000000..81d05147d2 --- /dev/null +++ b/tests/fw/apps/system_apps/timeline/wscript_build @@ -0,0 +1,148 @@ +from waftools.pebble_test import clar + +rendering_sources = ( + "src/fw/applib/fonts/codepoint.c " + "src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c " + "src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c " + "src/fw/applib/graphics/bitblt.c " + "src/fw/applib/graphics/framebuffer.c " + "src/fw/applib/graphics/gbitmap.c " + "src/fw/applib/graphics/gbitmap_png.c " + "src/fw/applib/graphics/gbitmap_sequence.c " + "src/fw/applib/graphics/gcolor_definitions.c " + "src/fw/applib/graphics/gdraw_command.c " + "src/fw/applib/graphics/gdraw_command_frame.c " + "src/fw/applib/graphics/gdraw_command_image.c " + "src/fw/applib/graphics/gdraw_command_list.c " + "src/fw/applib/graphics/gdraw_command_sequence.c " + "src/fw/applib/graphics/gpath.c " + "src/fw/applib/graphics/graphics.c " + "src/fw/applib/graphics/graphics_bitmap.c " + "src/fw/applib/graphics/graphics_circle.c " + "src/fw/applib/graphics/graphics_line.c " + "src/fw/applib/graphics/graphics_private.c " + "src/fw/applib/graphics/graphics_private_raw.c " + "src/fw/applib/graphics/gtransform.c " + "src/fw/applib/graphics/gtypes.c " + "src/fw/applib/graphics/perimeter.c " + "src/fw/applib/graphics/text_layout.c " + "src/fw/applib/graphics/rtl_support.c " + "src/fw/applib/graphics/arabic_shaping.c " + "src/fw/applib/graphics/text_render.c " + "src/fw/applib/graphics/text_resources.c " + "src/fw/applib/graphics/utf8.c " + "src/fw/applib/ui/kino/kino_reel.c " + "src/fw/applib/ui/kino/kino_reel_gbitmap.c " + "src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c " + "src/fw/applib/ui/kino/kino_reel_pdci.c " + "src/fw/applib/ui/kino/kino_reel_pdcs.c " + "src/fw/applib/ui/layer.c " + "src/fw/applib/ui/menu_layer_system_cells.c " + "src/fw/applib/vendor/tinflate/tinflate.c " + "src/fw/applib/vendor/uPNG/upng.c " + "src/fw/board/displays/display_getafix.c " + "src/fw/drivers/flash/flash_crc.c " + "src/fw/flash_region/filesystem_regions.c " + "src/fw/flash_region/flash_region.c " + "src/fw/resource/resource.c " + "src/fw/resource/resource_storage.c " + "src/fw/resource/resource_storage_builtin.c " + "src/fw/resource/resource_storage_file.c " + "src/fw/resource/resource_storage_flash.c " + "src/fw/services/filesystem/app_file.c " + "src/fw/services/filesystem/flash_translation.c " + "src/fw/services/filesystem/pfs.c " + "src/fw/system/hexdump.c " + "src/fw/util/crc8.c " + "src/fw/util/legacy_checksum.c " + "tests/fakes/fake_animation.c " + "tests/fakes/fake_applib_resource.c " + "tests/fakes/fake_display.c " + "tests/fakes/fake_gbitmap_get_data_row.c " + "tests/fakes/fake_graphics_context.c " + "tests/fakes/fake_property_animation.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_spi_flash.c " + "tests/fixtures/resources/builtin_resources.auto.c " + "tests/fixtures/resources/pfs_resource_table.c " + "tests/stubs/stubs_animation.c " +) + +timeline_app_sources = ( + "src/fw/applib/graphics/gdraw_command_transforms.c " + "src/fw/applib/graphics/gpath_builder.c " + "src/fw/applib/ui/action_button.c " + "src/fw/applib/ui/animation_interpolate.c " + "src/fw/applib/ui/content_indicator.c " + "src/fw/applib/ui/inverter_layer.c " + "src/fw/applib/ui/kino/kino_layer.c " + "src/fw/applib/ui/kino/kino_player.c " + "src/fw/applib/ui/kino/kino_reel/scale_segmented.c " + "src/fw/applib/ui/kino/kino_reel/transform.c " + "src/fw/applib/ui/kino/kino_reel/unfold.c " + "src/fw/applib/ui/kino/kino_reel_custom.c " + "src/fw/applib/ui/menu_layer.c " + "src/fw/applib/ui/scroll_layer.c " + "src/fw/applib/ui/shadows.c " + "src/fw/applib/ui/status_bar_layer.c " + "src/fw/applib/ui/text_layer.c " + "src/fw/applib/ui/text_layer_flow.c " + "src/fw/applib/ui/window.c " + "src/fw/apps/system/timeline/peek_layer.c " + "src/fw/apps/system/timeline/pin_window.c " + "src/fw/apps/system/timeline/text_node.c " + "src/fw/apps/system/timeline/timeline.c " + "src/fw/apps/system/timeline/animations.c " + "src/fw/apps/system/timeline/layer.c " + "src/fw/apps/system/timeline/model.c " + "src/fw/apps/system/timeline/relbar.c " + "src/fw/popups/timeline/timeline_item_layer.c " + "src/fw/services/clock/service.c " + "src/fw/services/blob_db/pin_db.c " + "src/fw/services/blob_db/timeline_item_storage.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/generic_layout.c " + "src/fw/services/timeline/item.c " + "src/fw/services/timeline/layout_layer.c " + "src/fw/services/timeline/layout_node.c " + "src/fw/services/timeline/timeline.c " + "src/fw/services/timeline/timeline_layout.c " + "src/fw/services/timeline/timeline_resources.c " + "src/fw/shell/system_theme.c " + "src/fw/util/buffer.c " + "src/fw/util/stats.c " + "src/fw/util/stringlist.c " + "src/fw/util/time/mktime.c " + "src/fw/util/time/time.c " + "tests/fakes/fake_fonts.c " + "tests/fakes/fake_settings_file.c " + "tests/fixtures/resources/timeline_resource_table.auto.c " + "tests/stubs/stubs_evented_timer.c " +) + +clar(ctx, + sources_ant_glob=( + rendering_sources + + timeline_app_sources + ), + test_sources_ant_glob="test_timeline_list_view.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob=( + rendering_sources + + timeline_app_sources + ), + test_sources_ant_glob="test_timeline_no_events.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + # The no events is a builtin on asterix and thus cannot be used in a unit test currently + platforms=['obelix', 'gabbro']) + +# vim:filetype=python diff --git a/tests/fw/apps/system_apps/weather/test_weather_app_layout.c b/tests/fw/apps/system_apps/weather/test_weather_app_layout.c index 938a39f37b..2a65f09229 100644 --- a/tests/fw/apps/system_apps/weather/test_weather_app_layout.c +++ b/tests/fw/apps/system_apps/weather/test_weather_app_layout.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" -#include "apps/system_apps/weather/weather_app_layout.h" +#include "apps/system/weather/layout.h" #include "applib/ui/app_window_stack.h" #include "applib/ui/content_indicator.h" #include "applib/ui/content_indicator_private.h" @@ -25,7 +12,7 @@ #include "applib/ui/window_private.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "util/buffer.h" #include "util/graphics.h" diff --git a/tests/fw/apps/system_apps/weather/wscript b/tests/fw/apps/system_apps/weather/wscript deleted file mode 100644 index d1aae27b20..0000000000 --- a/tests/fw/apps/system_apps/weather/wscript +++ /dev/null @@ -1,92 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - rendering_sources = \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/8_bit/bitblt_private.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" \ - " src/fw/applib/graphics/gbitmap_sequence.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/ui/kino/kino_reel.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ - " src/fw/applib/ui/kino/kino_reel_pdci.c" \ - " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/apps/system_apps/timeline/text_node.c" \ - " src/fw/board/displays/display_spalding.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/applib/vendor/tinflate/tinflate.c" \ - " src/fw/applib/vendor/uPNG/upng.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " tests/stubs/stubs_animation.c" - - clar(ctx, - sources_ant_glob = rendering_sources + \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/kino/kino_layer.c" \ - " src/fw/applib/ui/kino/kino_player.c" \ - " src/fw/applib/ui/kino/kino_reel_custom.c" \ - " src/fw/applib/ui/kino/kino_reel/transform.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " tests/fakes/fake_fonts.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/apps/system_apps/weather/weather_app_layout.c" \ - " src/fw/services/normal/timeline/timeline_resources.c" \ - " src/fw/services/normal/weather/weather_types.c" \ - " src/fw/util/buffer.c" \ - " tests/fixtures/resources/timeline_resource_table.auto.c", - test_sources_ant_glob = "test_weather_app_layout.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - -# vim:filetype=python diff --git a/tests/fw/apps/system_apps/weather/wscript_build b/tests/fw/apps/system_apps/weather/wscript_build new file mode 100644 index 0000000000..e4528e1b27 --- /dev/null +++ b/tests/fw/apps/system_apps/weather/wscript_build @@ -0,0 +1,93 @@ +from waftools.pebble_test import clar + +rendering_sources = \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/8_bit/bitblt_private.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" \ + " src/fw/applib/graphics/gbitmap_sequence.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/ui/kino/kino_reel.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ + " src/fw/applib/ui/kino/kino_reel_pdci.c" \ + " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/apps/system/timeline/text_node.c" \ + " src/fw/board/displays/display_getafix.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/applib/vendor/tinflate/tinflate.c" \ + " src/fw/applib/vendor/uPNG/upng.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " tests/stubs/stubs_animation.c" + +clar(ctx, + sources_ant_glob = rendering_sources + \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/kino/kino_layer.c" \ + " src/fw/applib/ui/kino/kino_player.c" \ + " src/fw/applib/ui/kino/kino_reel_custom.c" \ + " src/fw/applib/ui/kino/kino_reel/transform.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " tests/fakes/fake_fonts.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/apps/system/weather/layout.c" \ + " src/fw/services/timeline/timeline_resources.c" \ + " src/fw/services/weather/weather_types.c" \ + " src/fw/util/buffer.c" \ + " tests/fixtures/resources/timeline_resource_table.auto.c", + test_sources_ant_glob = "test_weather_app_layout.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +# vim:filetype=python diff --git a/tests/fw/apps/system_apps/workout/test_workout_active.c b/tests/fw/apps/system_apps/workout/test_workout_active.c index 22fc5fcf23..d8170a3bfb 100644 --- a/tests/fw/apps/system_apps/workout/test_workout_active.c +++ b/tests/fw/apps/system_apps/workout/test_workout_active.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/workout/workout.h" -#include "apps/system_apps/workout/workout_active.h" -#include "apps/system_apps/workout/workout_data.h" -#include "apps/system_apps/workout/workout_dialog.h" - -#include "services/normal/activity/health_util.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/workout/workout.h" +#include "apps/system/workout/active.h" +#include "apps/system/workout/data.h" +#include "apps/system/workout/dialog.h" + +#include "pbl/services/activity/health_util.h" #include "test_workout_app_includes.h" diff --git a/tests/fw/apps/system_apps/workout/test_workout_app_includes.h b/tests/fw/apps/system_apps/workout/test_workout_app_includes.h index fa101af58e..629f7fa864 100644 --- a/tests/fw/apps/system_apps/workout/test_workout_app_includes.h +++ b/tests/fw/apps/system_apps/workout/test_workout_app_includes.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/window_private.h" #include "util/size.h" diff --git a/tests/fw/apps/system_apps/workout/test_workout_dialog.c b/tests/fw/apps/system_apps/workout/test_workout_dialog.c index e06857ad3c..78a3c0a506 100644 --- a/tests/fw/apps/system_apps/workout/test_workout_dialog.c +++ b/tests/fw/apps/system_apps/workout/test_workout_dialog.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/workout/workout_dialog.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/workout/dialog.h" #include "resource/resource_ids.auto.h" diff --git a/tests/fw/apps/system_apps/workout/test_workout_selection.c b/tests/fw/apps/system_apps/workout/test_workout_selection.c index a9e5c63892..c50d65b15d 100644 --- a/tests/fw/apps/system_apps/workout/test_workout_selection.c +++ b/tests/fw/apps/system_apps/workout/test_workout_selection.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/workout/workout_selection.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/workout/selection.h" #include "test_workout_app_includes.h" diff --git a/tests/fw/apps/system_apps/workout/test_workout_summary.c b/tests/fw/apps/system_apps/workout/test_workout_summary.c index 909a3d4bde..e0850f11b5 100644 --- a/tests/fw/apps/system_apps/workout/test_workout_summary.c +++ b/tests/fw/apps/system_apps/workout/test_workout_summary.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/workout/workout_summary.h" -#include "apps/system_apps/workout/workout_utils.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/workout/summary.h" +#include "apps/system/workout/utils.h" #include "test_workout_app_includes.h" diff --git a/tests/fw/apps/system_apps/workout/test_workout_utils.c b/tests/fw/apps/system_apps/workout/test_workout_utils.c index 96a8503b50..b3c9261abd 100644 --- a/tests/fw/apps/system_apps/workout/test_workout_utils.c +++ b/tests/fw/apps/system_apps/workout/test_workout_utils.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/workout/workout_utils.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/workout/utils.h" #include "clar.h" -#include "services/normal/activity/activity.h" +#include "pbl/services/activity/activity.h" // --------------------------------------------------------------------------------------- #include "stubs_attribute.h" diff --git a/tests/fw/apps/system_apps/workout/wscript b/tests/fw/apps/system_apps/workout/wscript deleted file mode 100644 index cb0992c422..0000000000 --- a/tests/fw/apps/system_apps/workout/wscript +++ /dev/null @@ -1,155 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - rendering_sources = \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" \ - " src/fw/applib/graphics/gbitmap_sequence.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/ui/action_bar_layer.c" \ - " src/fw/applib/ui/status_bar_layer.c" \ - " src/fw/applib/ui/kino/kino_reel.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ - " src/fw/applib/ui/kino/kino_reel_pdci.c" \ - " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/menu_layer_system_cells.c" \ - " src/fw/applib/vendor/tinflate/tinflate.c" \ - " src/fw/applib/vendor/uPNG/upng.c" \ - " src/fw/board/displays/display_spalding.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " tests/stubs/stubs_animation.c" \ - " tests/stubs/stubs_system_theme.c" - - workout_app_sources = \ - " src/fw/applib/graphics/gpath_builder.c" \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " src/fw/applib/ui/dialogs/dialog.c" \ - " src/fw/applib/ui/dialogs/dialog_private.c" \ - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/kino/kino_layer.c" \ - " src/fw/applib/ui/kino/kino_player.c" \ - " src/fw/applib/ui/kino/kino_reel/transform.c" \ - " src/fw/applib/ui/kino/kino_reel_custom.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/apps/system_apps/timeline/text_node.c" \ - " src/fw/services/normal/activity/health_util.c" \ - " src/fw/services/normal/activity/hr_util.c" \ - " src/fw/services/normal/timeline/timeline_resources.c" \ - " src/fw/util/buffer.c" \ - " src/fw/util/stats.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_clock.c" \ - " tests/fakes/fake_fonts.c" \ - " tests/fakes/fake_workout_service.c" \ - " tests/fixtures/resources/timeline_resource_table.auto.c" - - clar(ctx, - sources_ant_glob = rendering_sources + \ - workout_app_sources + \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/apps/system_apps/workout/workout_countdown.c" + \ - " src/fw/apps/system_apps/workout/workout_selection.c" + \ - " src/fw/apps/system_apps/workout/workout_dialog.c" + \ - " src/fw/apps/system_apps/workout/workout_utils.c" + \ - " src/fw/apps/system_apps/workout/workout_summary.c", - test_sources_ant_glob = "test_workout_summary.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - workout_app_sources + \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/apps/system_apps/workout/workout_data.c" + \ - " src/fw/apps/system_apps/workout/workout_dialog.c" + \ - " src/fw/apps/system_apps/workout/workout_active.c", - test_sources_ant_glob = "test_workout_active.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - workout_app_sources + \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/apps/system_apps/workout/workout_data.c" + \ - " src/fw/apps/system_apps/workout/workout_dialog.c", - test_sources_ant_glob = "test_workout_dialog.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = rendering_sources + \ - workout_app_sources + \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/apps/system_apps/workout/workout_utils.c" + \ - " src/fw/apps/system_apps/workout/workout_selection.c", - test_sources_ant_glob = "test_workout_selection.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk']) - - clar(ctx, - sources_ant_glob = " src/fw/apps/system_apps/workout/workout_utils.c", - test_sources_ant_glob = "test_workout_utils.c", - override_includes=['dummy_board']) - -# vim:filetype=python diff --git a/tests/fw/apps/system_apps/workout/wscript_build b/tests/fw/apps/system_apps/workout/wscript_build new file mode 100644 index 0000000000..910742ce49 --- /dev/null +++ b/tests/fw/apps/system_apps/workout/wscript_build @@ -0,0 +1,156 @@ +from waftools.pebble_test import clar + +rendering_sources = \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" \ + " src/fw/applib/graphics/gbitmap_sequence.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/ui/action_bar_layer.c" \ + " src/fw/applib/ui/status_bar_layer.c" \ + " src/fw/applib/ui/kino/kino_reel.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ + " src/fw/applib/ui/kino/kino_reel_pdci.c" \ + " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/menu_layer_system_cells.c" \ + " src/fw/applib/vendor/tinflate/tinflate.c" \ + " src/fw/applib/vendor/uPNG/upng.c" \ + " src/fw/board/displays/display_getafix.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " tests/stubs/stubs_animation.c" \ + " tests/stubs/stubs_system_theme.c" + +workout_app_sources = \ + " src/fw/applib/graphics/gpath_builder.c" \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " src/fw/applib/ui/dialogs/dialog.c" \ + " src/fw/applib/ui/dialogs/dialog_private.c" \ + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/kino/kino_layer.c" \ + " src/fw/applib/ui/kino/kino_player.c" \ + " src/fw/applib/ui/kino/kino_reel/transform.c" \ + " src/fw/applib/ui/kino/kino_reel_custom.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/apps/system/timeline/text_node.c" \ + " src/fw/services/activity/health_util.c" \ + " src/fw/services/activity/hr_util.c" \ + " src/fw/services/timeline/timeline_resources.c" \ + " src/fw/util/buffer.c" \ + " src/fw/util/stats.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_clock.c" \ + " tests/fakes/fake_fonts.c" \ + " tests/fakes/fake_workout_service.c" \ + " tests/fixtures/resources/timeline_resource_table.auto.c" + +clar(ctx, + sources_ant_glob = rendering_sources + \ + workout_app_sources + \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/apps/system/workout/countdown.c" + \ + " src/fw/apps/system/workout/selection.c" + \ + " src/fw/apps/system/workout/dialog.c" + \ + " src/fw/apps/system/workout/utils.c" + \ + " src/fw/apps/system/workout/summary.c", + test_sources_ant_glob = "test_workout_summary.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + workout_app_sources + \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/apps/system/workout/data.c" + \ + " src/fw/apps/system/workout/dialog.c" + \ + " src/fw/apps/system/workout/active.c", + test_sources_ant_glob = "test_workout_active.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + workout_app_sources + \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/apps/system/workout/data.c" + \ + " src/fw/apps/system/workout/dialog.c", + test_sources_ant_glob = "test_workout_dialog.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = rendering_sources + \ + workout_app_sources + \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/apps/system/workout/utils.c" + \ + " src/fw/apps/system/workout/selection.c", + test_sources_ant_glob = "test_workout_selection.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = " src/fw/apps/system/workout/utils.c", + test_sources_ant_glob = "test_workout_utils.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/apps/watch/kickstart/test_kickstart.c b/tests/fw/apps/watch/kickstart/test_kickstart.c index 57c70f358d..ffdc340a00 100644 --- a/tests/fw/apps/watch/kickstart/test_kickstart.c +++ b/tests/fw/apps/watch/kickstart/test_kickstart.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "apps/watch/kickstart/kickstart.h" #include "popups/timeline/peek.h" @@ -167,7 +154,7 @@ void test_kickstart__render_steps_above_daily_avg(void) { } void test_kickstart__render_hr_bpm(void) { -#if (PLATFORM_SILK || PLATFORM_ROBERT) +#ifdef CONFIG_BOARD_FAMILY_ASTERIX prv_set_data(&s_data, 10323, 7500, 13000, 82); window_set_on_screen(&s_data.window, true, true); @@ -177,7 +164,7 @@ void test_kickstart__render_hr_bpm(void) { } void test_kickstart__render_hr_bpm_obstructed(void) { -#if (PLATFORM_SILK || PLATFORM_ROBERT) +#ifdef CONFIG_BOARD_FAMILY_ASTERIX prv_set_data(&s_data, 10323, 7500, 13000, 82); prv_set_unobstructed_area_height(TIMELINE_PEEK_HEIGHT); @@ -197,7 +184,7 @@ void test_kickstart__render_steps_above_daily_avg_24h(void) { } void test_kickstart__render_hr_bpm_24h(void) { -#if (PLATFORM_SILK || PLATFORM_ROBERT) +#ifdef CONFIG_BOARD_FAMILY_ASTERIX s_clock_is_24h_style = true; prv_set_data(&s_data, 10323, 7500, 13000, 82); @@ -208,7 +195,7 @@ void test_kickstart__render_hr_bpm_24h(void) { } void test_kickstart__render_hr_bpm_obstructed_24h(void) { -#if (PLATFORM_SILK || PLATFORM_ROBERT) +#ifdef CONFIG_BOARD_FAMILY_ASTERIX s_clock_is_24h_style = true; prv_set_data(&s_data, 10323, 7500, 13000, 82); prv_set_unobstructed_area_height(TIMELINE_PEEK_HEIGHT); diff --git a/tests/fw/apps/watch/kickstart/wscript b/tests/fw/apps/watch/kickstart/wscript deleted file mode 100644 index cc8616ce44..0000000000 --- a/tests/fw/apps/watch/kickstart/wscript +++ /dev/null @@ -1,87 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - rendering_sources = \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" \ - " src/fw/applib/graphics/gbitmap_sequence.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/ui/status_bar_layer.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/vendor/tinflate/tinflate.c" \ - " src/fw/applib/vendor/uPNG/upng.c" \ - " src/fw/board/displays/display_spalding.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " tests/stubs/stubs_animation.c" \ - " tests/stubs/stubs_system_theme.c" - - kickstart_app_sources = \ - " src/fw/applib/graphics/gpath_builder.c" \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/apps/system_apps/timeline/text_node.c" \ - " src/fw/services/normal/activity/health_util.c" \ - " src/fw/util/buffer.c" \ - " src/fw/util/stats.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_clock.c" \ - " tests/fakes/fake_fonts.c" - - clar(ctx, - sources_ant_glob = rendering_sources + \ - kickstart_app_sources + \ - " src/fw/apps/watch/kickstart/kickstart.c", - test_sources_ant_glob = "test_kickstart.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk', 'robert']) - -# vim:filetype=python diff --git a/tests/fw/apps/watch/kickstart/wscript_build b/tests/fw/apps/watch/kickstart/wscript_build new file mode 100644 index 0000000000..03dede02f2 --- /dev/null +++ b/tests/fw/apps/watch/kickstart/wscript_build @@ -0,0 +1,88 @@ +from waftools.pebble_test import clar + +rendering_sources = \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" \ + " src/fw/applib/graphics/gbitmap_sequence.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/ui/status_bar_layer.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/vendor/tinflate/tinflate.c" \ + " src/fw/applib/vendor/uPNG/upng.c" \ + " src/fw/board/displays/display_getafix.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " tests/stubs/stubs_animation.c" \ + " tests/stubs/stubs_system_theme.c" + +kickstart_app_sources = \ + " src/fw/applib/graphics/gpath_builder.c" \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/apps/system/timeline/text_node.c" \ + " src/fw/services/activity/health_util.c" \ + " src/fw/util/buffer.c" \ + " src/fw/util/stats.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_clock.c" \ + " tests/fakes/fake_fonts.c" + +clar(ctx, + sources_ant_glob = rendering_sources + \ + kickstart_app_sources + \ + " src/fw/apps/watch/kickstart/kickstart.c", + test_sources_ant_glob = "test_kickstart.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +# vim:filetype=python diff --git a/tests/fw/comm/ancs_test_data.h b/tests/fw/comm/ancs_test_data.h index 1507de896f..397095a0bf 100644 --- a/tests/fw/comm/ancs_test_data.h +++ b/tests/fw/comm/ancs_test_data.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -196,10 +183,10 @@ static const uint8_t s_missing_last_attribute[] = { 0x72, 0x61, 0x67, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, 0x21 }; -// Length of attribute too long +// Length of attribute too long: the title claims 129 bytes, over TITLE_MAX_LENGTH (128). static const uint8_t s_invalid_attribute_length[] = { 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, - 0x65, 0x2e, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x53, 0x4d, 0x53, 0x01, 0x29, 0x00, 0x72, 0x6a, + 0x65, 0x2e, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x53, 0x4d, 0x53, 0x01, 0x81, 0x00, 0x72, 0x6a, 0x63, 0x61, 0x73, 0x65, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x61, 0x72, 0x65, 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x6d, 0x61, 0x6e, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x00, 0x73, 0x20, 0x61, 0x20, 0x62, 0x65, 0x76, 0x65, diff --git a/tests/fw/comm/test_ams.c b/tests/fw/comm/test_ams.c index e172838615..a9a3cf1f96 100644 --- a/tests/fw/comm/test_ams.c +++ b/tests/fw/comm/test_ams.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ams/ams.h" #include "comm/bt_conn_mgr.h" -#include "services/normal/music.h" -#include "services/normal/music_internal.h" +#include "pbl/services/music.h" +#include "pbl/services/music_internal.h" #include "clar.h" @@ -43,9 +30,6 @@ #include "stubs_serial.h" #include "stubs_tick.h" -void analytics_event_ams(uint8_t type, int32_t aux_info) { -} - struct { ResponseTimeState state; uint16_t max_period_secs; diff --git a/tests/fw/comm/test_ams_util.c b/tests/fw/comm/test_ams_util.c index 30386aeec4..306b9020af 100644 --- a/tests/fw/comm/test_ams_util.c +++ b/tests/fw/comm/test_ams_util.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ams/ams_util.h" diff --git a/tests/fw/comm/test_ancs.c b/tests/fw/comm/test_ancs.c index 22b02ec158..e35c8d7b25 100644 --- a/tests/fw/comm/test_ancs.c +++ b/tests/fw/comm/test_ancs.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ancs/ancs.h" #include "comm/ble/kernel_le_client/ancs/ancs_util.h" @@ -21,9 +8,9 @@ #include "comm/ble/gap_le_connection.h" #include "comm/ble/gap_le_task.h" -#include "services/common/evented_timer.h" -#include "services/common/regular_timer.h" -#include "services/normal/notifications/ancs/ancs_notifications.h" +#include "pbl/services/evented_timer.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/notifications/ancs/ancs_notifications.h" #include "util/size.h" #include "clar.h" @@ -47,7 +34,6 @@ #include "stubs_timeline.h" #include "stubs_queue.h" #include "stubs_rand_ptr.h" -#include "stubs_reconnect.h" #include "stubs_reminder_db.h" #include "stubs_reminders.h" #include "stubs_serial.h" @@ -70,6 +56,7 @@ PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char /////////////////////////////////////////////////////////// #include "fake_events.h" +#include "fake_gatt_client_subscriptions.h" #include "fake_kernel_services_notifications.h" #include "fake_new_timer.h" #include "fake_notification_storage.h" @@ -303,6 +290,7 @@ void test_ancs__initialize(void) { fake_kernel_services_notifications_reset(); fake_notification_storage_reset(); fake_event_init(); + fake_gatt_client_subscriptions_set_subscribe_return_value(BTErrnoOK); ancs_create(); ancs_handle_service_discovered(s_characteristics); @@ -315,6 +303,20 @@ void test_ancs__cleanup(void) { regular_timer_deinit(); } +// A "fake ANCS": some phones advertise a GATT service matching the ANCS UUIDs +// whose characteristics can't be subscribed to (e.g. missing CCCD). Subscribing +// fails; we must reject the service instead of asserting (which crash-loops the +// watch into recovery). +void test_ancs__should_fail_soft_on_subscribe_failure(void) { + cl_assert(ancs_can_handle_characteristic(s_characteristics[ANCSCharacteristicData])); + + fake_gatt_client_subscriptions_set_subscribe_return_value(BTErrnoInvalidParameter); + ancs_handle_service_discovered(s_characteristics); + + // ANCS is left disconnected rather than crashing: + cl_assert(!ancs_can_handle_characteristic(s_characteristics[ANCSCharacteristicData])); +} + // Janky black box smoke-test to exercise the ANCS message re-assembly state // machine void test_ancs__should_handle_small_and_large_messages(void) { diff --git a/tests/fw/comm/test_ancs_app_storage.c b/tests/fw/comm/test_ancs_app_storage.c index 936a265a45..8944cdf1a4 100644 --- a/tests/fw/comm/test_ancs_app_storage.c +++ b/tests/fw/comm/test_ancs_app_storage.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -130,7 +117,7 @@ void test_ancs_app_storage__hash_collisions(void) { app_data.display_name = name; app_data.is_meta_changed = true; uint32_t key = get_key(name); - PBL_LOG(LOG_LEVEL_DEBUG, "name: %s, key: %u", name, (unsigned) key); + PBL_LOG_DBG("name: %s, key: %u", name, (unsigned) key); ancs_app_storage_save(&app_data); } } @@ -177,7 +164,7 @@ void test_ancs_app_storage__iter(void) { ANCSAppData app_data_out = { 0 }; for (unsigned int i = 0; i < ARRAY_LENGTH(apps); ++i) { - PBL_LOG(LOG_LEVEL_DEBUG, "i: %d, name: %s", i, apps[i].bundle_id); + PBL_LOG_DBG("i: %d, name: %s", i, apps[i].bundle_id); ancs_app_storage_load(apps[i].bundle_id, &app_data_out); cl_assert_equal_s(apps[i].bundle_id, app_data_out.bundle_id); @@ -189,7 +176,7 @@ void test_ancs_app_storage__iter(void) { ancs_app_storage_iter_begin(); for (unsigned int i = 0; ancs_app_storage_next(&app_data_out); ++i) { - PBL_LOG(LOG_LEVEL_DEBUG, "i: %d, name: %s, name_out: %s", i, apps[i].bundle_id, app_data_out.bundle_id); + PBL_LOG_DBG("i: %d, name: %s, name_out: %s", i, apps[i].bundle_id, app_data_out.bundle_id); cl_assert_equal_s(apps[i].bundle_id, app_data_out.bundle_id); } } diff --git a/tests/fw/comm/test_ancs_util.c b/tests/fw/comm/test_ancs_util.c index 21488f6cd2..ca7c167c91 100644 --- a/tests/fw/comm/test_ancs_util.c +++ b/tests/fw/comm/test_ancs_util.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ancs/ancs.h" #include "comm/ble/kernel_le_client/ancs/ancs_types.h" @@ -39,7 +26,6 @@ #include "stubs_system_reset.h" #include "stubs_task_watchdog.h" #include "stubs_pbl_malloc.h" -#include "stubs_reconnect.h" // Test data /////////////////////////////////////////////////////////// diff --git a/tests/fw/comm/test_bt_conn_mgr.c b/tests/fw/comm/test_bt_conn_mgr.c index 419f347c5d..219433cf6d 100644 --- a/tests/fw/comm/test_bt_conn_mgr.c +++ b/tests/fw/comm/test_bt_conn_mgr.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "comm/ble/gap_le_connection.h" #include "comm/bt_conn_mgr.h" #include "comm/bt_conn_mgr_impl.h" -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" // Fakes #include "fake_gap_le_connect_params.h" diff --git a/tests/fw/comm/test_gap_le_advert.c b/tests/fw/comm/test_gap_le_advert.c index acc5e81cdb..1afa4f922a 100644 --- a/tests/fw/comm/test_gap_le_advert.c +++ b/tests/fw/comm/test_gap_le_advert.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/gap_le_advert.h" #include "comm/ble/gap_le_connection.h" -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" #include "util/size.h" #include "clar.h" @@ -24,7 +11,7 @@ // Fakes /////////////////////////////////////////////////////////// -#include "fake_GAPAPI.h" +#include "fake_bt_driver_advert.h" #include "fake_new_timer.h" #include "fake_rtc.h" #include "fake_system_task.h" @@ -43,11 +30,6 @@ #include "stubs_passert.h" #include "stubs_prompt.h" -void gap_le_connect_bluetopia_connection_callback(uint32_t stack_id, - GAP_LE_Event_Data_t* event_data, - uint32_t CallbackParameter) { -} - bool static s_is_connected_as_slave = false; bool gap_le_connect_is_connected_as_slave(void) { @@ -66,10 +48,6 @@ void gap_le_slave_reconnect_start(void) { void gatt_service_changed_server_cleanup_by_connection(GAPLEConnection *connection) { } -int HCI_LE_Set_Advertise_Enable(unsigned int BluetoothStackID, ...) { - return (0); -} - void launcher_task_add_callback(void (*callback)(void *data), void *data) { callback(data); } @@ -89,7 +67,7 @@ static void unscheduled_callback(GAPLEAdvertisingJobRef job, } void test_gap_le_advert__initialize(void) { - fake_GAPAPI_init(); + fake_bt_driver_advert_init(); s_unscheduled_cb_count = 0; s_unscheduled_job = NULL; @@ -101,6 +79,11 @@ void test_gap_le_advert__initialize(void) { regular_timer_init(); gap_le_advert_init(); + + // gap_le_advert keeps its slave-connection state in a static that init() does + // not clear, so a prior test that connected as slave would leave advertising + // paused. Reset it through the public API for a clean slate each test. + gap_le_advert_handle_disconnect_as_slave(); } void test_gap_le_advert__cleanup(void) { @@ -155,8 +138,7 @@ void test_gap_le_advert__single_job(void) { BLEAdData *ad = create_ad(ad_data, scan_resp_data); GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 241, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 10, }; GAPLEAdvertisingJobRef job; @@ -208,13 +190,11 @@ void test_gap_le_advert__single_job_multiple_terms_silence_and_loop_around(void) GAPLEAdvertisingJobTerm advert_terms[] = { { - .min_interval_slots = 160, - .max_interval_slots = 240, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 1, }, { - .min_interval_slots = 320, - .max_interval_slots = 480, + .interval = GAPLEAdvertisingInterval_Long, .duration_secs = 1, }, { @@ -232,24 +212,21 @@ void test_gap_le_advert__single_job_multiple_terms_silence_and_loop_around(void) // First term: cl_assert(gap_le_is_advertising_enabled()); assert_ad_data("yo"); - gap_le_assert_advertising_interval(advert_terms[0].min_interval_slots, - advert_terms[0].max_interval_slots); + gap_le_assert_advertising_interval(GAPLEAdvertisingInterval_Short); regular_timer_fire_seconds(1); // Second term: cl_assert(gap_le_is_advertising_enabled()); assert_ad_data("yo"); - gap_le_assert_advertising_interval(advert_terms[1].min_interval_slots, - advert_terms[1].max_interval_slots); + gap_le_assert_advertising_interval(GAPLEAdvertisingInterval_Long); regular_timer_fire_seconds(1); // Looped around to second term (index==1): cl_assert(gap_le_is_advertising_enabled()); assert_ad_data("yo"); - gap_le_assert_advertising_interval(advert_terms[1].min_interval_slots, - advert_terms[1].max_interval_slots); + gap_le_assert_advertising_interval(GAPLEAdvertisingInterval_Long); cl_assert_equal_i(s_unscheduled_cb_count, 0); } @@ -259,21 +236,14 @@ void test_gap_le_advert__single_job_multiple_terms(void) { const char scan_resp_data[] = "scan resp data"; BLEAdData *ad = create_ad(ad_data, scan_resp_data); - GAPLEAdvertisingJobTerm advert_terms[3] = + GAPLEAdvertisingJobTerm advert_terms[2] = { { - .min_interval_slots = 160, - .max_interval_slots = 240, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 4, }, { - .min_interval_slots = 320, - .max_interval_slots = 400, - .duration_secs = 4, - }, - { - .min_interval_slots = 640, - .max_interval_slots = 800, + .interval = GAPLEAdvertisingInterval_Long, .duration_secs = 4, }, }; @@ -305,14 +275,17 @@ void test_gap_le_advert__single_job_multiple_terms(void) { // Unschedule callback should not have been called: cl_assert_equal_i(s_unscheduled_cb_count, 0); - // Make sure the all the terms in the job are run: + // Make sure all the terms in the job are run: + GAPLEAdvertisingInterval expected_intervals[] = { + GAPLEAdvertisingInterval_Short, + GAPLEAdvertisingInterval_Long, + }; for (int term = 0; term < ARRAY_LENGTH(advert_terms); ++term) { for (int second_tick = 0; second_tick < 4; ++second_tick) { cl_assert_equal_i(s_unscheduled_cb_count, 0); assert_ad_data("ad data"); - gap_le_assert_advertising_interval(advert_terms[term].min_interval_slots, - advert_terms[term].max_interval_slots); + gap_le_assert_advertising_interval(expected_intervals[term]); regular_timer_fire_seconds(1); } } @@ -329,8 +302,7 @@ void test_gap_le_advert__single_job_multiple_terms(void) { void test_gap_le_advert__job_round_robin(void) { GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = GAPLE_ADVERTISING_DURATION_INFINITE, }; @@ -468,13 +440,11 @@ void test_gap_le_advert__job_round_robin_multiple_terms(void) { GAPLEAdvertisingJobTerm advert_terms[2] = { { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 5, }, { - .min_interval_slots = 480, - .max_interval_slots = 620, + .interval = GAPLEAdvertisingInterval_Long, .duration_secs = 5, }, }; @@ -530,8 +500,7 @@ void test_gap_le_advert__expiring_job(void) { // Test that a job is expired correctly after the set duration: uint16_t seconds_left = 5; GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = seconds_left, }; @@ -567,49 +536,16 @@ void test_gap_le_advert__invalid_params(void) { BLEAdData *ad = create_ad(NULL, NULL); GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 31, - .max_interval_slots = 31, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 1, }; - // Minimum interval boundary (no scan resp): - job = gap_le_advert_schedule(ad, &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), - unscheduled_callback, s_unscheduled_cb_data, 0); - cl_assert_equal_p(job, NULL); - - advert_term.min_interval_slots = 32; - advert_term.max_interval_slots = 32; + // Valid term should succeed: job = gap_le_advert_schedule(ad, &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), unscheduled_callback, s_unscheduled_cb_data, 0); cl_assert(job); - // Minimum interval boundary (with scan resp): - char scan_resp_data[] = "scan resp data"; - BLEAdData *ad_scannable = create_ad(NULL, scan_resp_data); - advert_term.min_interval_slots = 159; - advert_term.max_interval_slots = 159; - job = gap_le_advert_schedule(ad_scannable, - &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), - unscheduled_callback, s_unscheduled_cb_data, 0); - cl_assert_equal_p(job, NULL); - - advert_term.min_interval_slots = 160; - advert_term.max_interval_slots = 160; - job = gap_le_advert_schedule(ad_scannable, - &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), - unscheduled_callback, s_unscheduled_cb_data, 0); - cl_assert(job); - - // Max < Min: - advert_term.min_interval_slots = 200; - advert_term.max_interval_slots = 32; - job = gap_le_advert_schedule(ad, &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), - unscheduled_callback, s_unscheduled_cb_data, 0); - cl_assert_equal_p(job, NULL); - // Loop-around in the first term: - advert_term.min_interval_slots = 200; - advert_term.max_interval_slots = 200; advert_term.duration_secs = GAPLE_ADVERTISING_DURATION_LOOP_AROUND; job = gap_le_advert_schedule(ad, &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), unscheduled_callback, s_unscheduled_cb_data, 0); @@ -621,8 +557,6 @@ void test_gap_le_advert__invalid_params(void) { cl_assert_equal_p(job, NULL); // No ad data: - advert_term.min_interval_slots = 200; - advert_term.max_interval_slots = 200; advert_term.duration_secs = 1; job = gap_le_advert_schedule(NULL, &advert_term, sizeof(advert_term)/sizeof(GAPLEAdvertisingJobTerm), @@ -630,7 +564,6 @@ void test_gap_le_advert__invalid_params(void) { cl_assert_equal_p(job, NULL); free(ad); - free(ad_scannable); } void test_gap_le_advert__unschedule_non_existent(void) { @@ -645,8 +578,7 @@ void test_gap_le_advert__deinit_unschedules(void) { BLEAdData *ad = create_ad(NULL, NULL); GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 10, }; GAPLEAdvertisingJobRef job; @@ -665,8 +597,7 @@ void test_gap_le_advert__cant_schedule_after_deinit(void) { gap_le_advert_deinit(); BLEAdData *ad = create_ad(NULL, NULL); GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 10, }; GAPLEAdvertisingJobRef job; @@ -679,8 +610,7 @@ void test_gap_le_advert__cant_schedule_after_deinit(void) { void test_gap_le_advert__continue_after_slave_connection(void) { BLEAdData *ad = create_ad(NULL, NULL); GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 10, }; GAPLEAdvertisingJobRef job; @@ -697,9 +627,14 @@ void test_gap_le_advert__continue_after_slave_connection(void) { // we should have stopped advertising for reconnection cl_assert_equal_b(gap_le_is_advertising_enabled(), false); - // Trigger the advertising to start up again. Since we still have an advertisement job set, - // it should continue. + // While connected as slave, the cycle timer must not re-enable advertising: + // the bt_driver contract does not advertise during a connection. regular_timer_fire_seconds(1); + cl_assert_equal_b(gap_le_is_advertising_enabled(), false); + + // Once disconnected, advertising resumes since the advertisement job is still + // scheduled: + gap_le_advert_handle_disconnect_as_slave(); cl_assert_equal_b(gap_le_is_advertising_enabled(), true); free(ad); @@ -708,8 +643,7 @@ void test_gap_le_advert__continue_after_slave_connection(void) { void test_gap_le_advert__unschedule_job_types(void) { BLEAdData *ad = create_ad(NULL, NULL); GAPLEAdvertisingJobTerm advert_term = { - .min_interval_slots = 160, - .max_interval_slots = 320, + .interval = GAPLEAdvertisingInterval_Short, .duration_secs = 10, }; GAPLEAdvertisingJobRef job_a; diff --git a/tests/fw/comm/test_gap_le_connect.c b/tests/fw/comm/test_gap_le_connect.c index c6136039df..dd8b11a56b 100644 --- a/tests/fw/comm/test_gap_le_connect.c +++ b/tests/fw/comm/test_gap_le_connect.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bluetooth/gap_le_connect.h" #include "comm/ble/gap_le_connect.h" @@ -20,7 +7,7 @@ #include "comm/ble/gap_le_task.h" #include "kernel/events.h" -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" #include "clar.h" @@ -101,16 +88,10 @@ void launcher_task_add_callback(void (*callback)(void *data), void *data) { callback(data); } -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) { -} - void bluetooth_analytics_handle_connection_disconnection_event( AnalyticsEvent type, uint8_t reason, const BleRemoteVersionInfo *vers_info) { } -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { -} - // Helpers /////////////////////////////////////////////////////////// diff --git a/tests/fw/comm/test_gatt_client_accessors.c b/tests/fw/comm/test_gatt_client_accessors.c index 29400813da..d1eeca69af 100644 --- a/tests/fw/comm/test_gatt_client_accessors.c +++ b/tests/fw/comm/test_gatt_client_accessors.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -31,19 +18,17 @@ // Fakes /////////////////////////////////////////////////////////// -#include "fake_GAPAPI.h" -#include "fake_GATTAPI.h" -#include "fake_GATTAPI_test_vectors.h" +#include "fake_bt_driver_gatt.h" #include "fake_events.h" #include "fake_new_timer.h" #include "fake_system_task.h" #include "fake_pbl_malloc.h" +#include "fake_event_gatt_service_buffer.h" + // Stubs /////////////////////////////////////////////////////////// -#include "stubs_bluetopia_interface.h" -#include "stubs_bt_driver_gatt.h" #include "stubs_bt_lock.h" #include "stubs_gatt_client_subscriptions.h" #include "stubs_logging.h" @@ -86,7 +71,7 @@ static BTDeviceInternal prv_dummy_device(uint8_t octet) { static BTDeviceInternal prv_connected_dummy_device(uint8_t octet) { BTDeviceInternal device = prv_dummy_device(octet); - gap_le_connection_add(&device, NULL, true /* local_is_master */); + gap_le_connection_add(&device, NULL, true /* local_is_master */, TIMER_INVALID_ID); GAPLEConnection *connection = gap_le_connection_by_device(&device); connection->gatt_connection_id = TEST_GATT_CONNECTION_ID; return device; diff --git a/tests/fw/comm/test_gatt_client_discovery.c b/tests/fw/comm/test_gatt_client_discovery.c index 3a1e823f41..d01bdfe561 100644 --- a/tests/fw/comm/test_gatt_client_discovery.c +++ b/tests/fw/comm/test_gatt_client_discovery.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/gatt_client_discovery.h" #include "comm/ble/gatt_service_changed.h" @@ -28,19 +15,17 @@ // Fakes /////////////////////////////////////////////////////////// -#include "fake_GAPAPI.h" -#include "fake_GATTAPI.h" -#include "fake_GATTAPI_test_vectors.h" +#include "fake_bt_driver_gatt.h" #include "fake_events.h" #include "fake_new_timer.h" #include "fake_pbl_malloc.h" #include "fake_system_task.h" +#include "fake_event_gatt_service_buffer.h" + // Stubs /////////////////////////////////////////////////////////// -#include "stubs_bluetopia_interface.h" -#include "stubs_bt_driver_gatt.h" #include "stubs_bt_lock.h" #include "stubs_gatt_client_subscriptions.h" #include "stubs_logging.h" @@ -63,8 +48,6 @@ uint16_t gaps_get_starting_att_handle(void) { // Helpers /////////////////////////////////////////////////////////// -extern TimerID bt_driver_gatt_get_watchdog_timer_id(void); - #define TEST_GATT_CONNECTION_ID (1234) static BTDeviceInternal prv_dummy_device(uint8_t octet) { @@ -84,7 +67,7 @@ static BTDeviceInternal prv_dummy_device(uint8_t octet) { static BTDeviceInternal prv_connected_dummy_device(uint8_t octet) { BTDeviceInternal device = prv_dummy_device(octet); - gap_le_connection_add(&device, NULL, true /* local_is_master */); + gap_le_connection_add(&device, NULL, true /* local_is_master */, TIMER_INVALID_ID); GAPLEConnection *connection = gap_le_connection_by_device(&device); connection->gatt_connection_id = TEST_GATT_CONNECTION_ID; return device; diff --git a/tests/fw/comm/test_gatt_client_subscriptions.c b/tests/fw/comm/test_gatt_client_subscriptions.c index b1c80dd0ff..387c904dab 100644 --- a/tests/fw/comm/test_gatt_client_subscriptions.c +++ b/tests/fw/comm/test_gatt_client_subscriptions.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include @@ -34,12 +21,13 @@ #include "fake_events.h" #include "fake_pbl_malloc.h" -#include "fake_GATTAPI.h" -#include "fake_GATTAPI_test_vectors.h" +#include "fake_bt_driver_gatt.h" #include "fake_new_timer.h" #include "fake_queue.h" #include "fake_system_task.h" +#include "fake_event_gatt_service_buffer.h" + #include "stubs_regular_timer.h" static BTErrno s_write_descriptor_cccd_result; @@ -60,8 +48,6 @@ void fake_kernel_malloc_mark_assert_equal(void) { } /////////////////////////////////////////////////////////// #include "stubs_analytics.h" -#include "stubs_bluetopia_interface.h" -#include "stubs_bt_driver_gatt.h" #include "stubs_bt_lock.h" #include "stubs_logging.h" #include "stubs_mutex.h" @@ -123,7 +109,7 @@ static BTDeviceInternal prv_dummy_device(uint8_t octet) { static BTDeviceInternal prv_connected_dummy_device(uint8_t octet) { BTDeviceInternal device = prv_dummy_device(octet); - gap_le_connection_add(&device, NULL, true /* local_is_master */); + gap_le_connection_add(&device, NULL, true /* local_is_master */, TIMER_INVALID_ID); s_connection = gap_le_connection_by_device(&device); s_connection->gatt_connection_id = TEST_GATT_CONNECTION_ID; return device; diff --git a/tests/fw/comm/test_gatt_service_changed_client.c b/tests/fw/comm/test_gatt_service_changed_client.c index dd7e0ec6e1..c5039042de 100644 --- a/tests/fw/comm/test_gatt_service_changed_client.c +++ b/tests/fw/comm/test_gatt_service_changed_client.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/gatt_service_changed.h" #include "comm/ble/gap_le_connection.h" @@ -26,9 +13,7 @@ // Fakes /////////////////////////////////////////////////////////// -#include "fake_GAPAPI.h" -#include "fake_GATTAPI.h" -#include "fake_GATTAPI_test_vectors.h" +#include "fake_bt_driver_gatt.h" #include "fake_pbl_malloc.h" #include "fake_new_timer.h" #include "fake_rtc.h" @@ -37,8 +22,6 @@ // Stubs /////////////////////////////////////////////////////////// -#include "stubs_bluetopia_interface.h" -#include "stubs_bt_driver_gatt.h" #include "stubs_bt_lock.h" #include "stubs_events.h" #include "stubs_gatt_client_subscriptions.h" @@ -49,10 +32,6 @@ #include "stubs_rand_ptr.h" #include "stubs_regular_timer.h" -extern bool prv_contains_service_changed_characteristic( - GAPLEConnection *connection, - const GATT_Service_Discovery_Indication_Data_t *event); - void core_dump_reset(bool is_forced) { } @@ -112,43 +91,24 @@ void fake_kernel_malloc_mark_assert_equal(void) { } /////////////////////////////////////////////////////////// #define TEST_GATT_CONNECTION_ID (1234) -#define TEST_BT_STACK_ID (1) - -typedef enum { - Unknown, - Handled, - Unhandled, -} HandleResult; - -static HandleResult s_last_handle_discovery_result; - -static void prv_bluetopia_service_discovery_cb(unsigned int stack_id, - GATT_Service_Discovery_Event_Data_t *event, - unsigned long callback_param) { - cl_assert_equal_i(stack_id, TEST_BT_STACK_ID); - if (event->Event_Data_Type == etGATT_Service_Discovery_Indication) { - const GATT_Service_Discovery_Indication_Data_t *indication = - event->Event_Data.GATT_Service_Discovery_Indication_Data; - cl_assert_equal_i(s_connection.gatt_connection_id, TEST_GATT_CONNECTION_ID); - cl_assert_equal_i(indication->ConnectionID, TEST_GATT_CONNECTION_ID); - s_last_handle_discovery_result = - prv_contains_service_changed_characteristic(&s_connection, indication) ? Handled : Unhandled; - } -} +static const BTDeviceInternal s_device = { + .address = { .octets = { 1, 2, 3, 4, 5, 6 } }, +}; // Tests /////////////////////////////////////////////////////////// void test_gatt_service_changed_client__initialize(void) { - s_last_handle_discovery_result = Unknown; fake_gatt_init(); s_connection = (GAPLEConnection) { + .device = s_device, .gatt_connection_id = TEST_GATT_CONNECTION_ID, .gatt_service_changed_att_handle = 0, }; - GATT_Start_Service_Discovery_Handle_Range(TEST_BT_STACK_ID, TEST_GATT_CONNECTION_ID, NULL, 0, NULL, - prv_bluetopia_service_discovery_cb, 0); + // Kick off a discovery so the fake driver is in the running state and will + // deliver the service indications the tests inject below. + cl_assert_equal_i(gatt_client_discovery_discover_all(&s_device), BTErrnoOK); } void test_gatt_service_changed_client__cleanup(void) { @@ -159,28 +119,27 @@ void test_gatt_service_changed_client__cleanup(void) { void test_gatt_service_changed_client__handle_non_gatt_profile_service(void) { fake_gatt_put_discovery_indication_blood_pressure_service(TEST_GATT_CONNECTION_ID); - cl_assert_equal_i(s_last_handle_discovery_result, Unhandled); + // A service without the Service Changed characteristic leaves the recorded + // handle untouched. + cl_assert_equal_i(s_connection.gatt_service_changed_att_handle, 0); } void test_gatt_service_changed_client__handle_gatt_profile_service(void) { fake_gatt_put_discovery_indication_gatt_profile_service(TEST_GATT_CONNECTION_ID, true /* has_service_changed_characteristic */); - cl_assert_equal_i(s_last_handle_discovery_result, Handled); - - // Verify the CCCD of the Service Changed characteristic has been written: - cl_assert_equal_i(fake_gatt_write_last_written_handle(), - fake_gatt_gatt_profile_service_service_changed_cccd_att_handle()); - - // Simulate getting a Write Response confirmation for the written CCCD: - fake_gatt_put_write_response_for_last_write(); - - // Today we don't really do anything upon getting the confirmation + // The driver reports the Service Changed characteristic's handle, which the + // firmware records so it can match future Service Changed indications. (The + // CCCD subscription that the old Bluetopia client performed now lives in the + // driver; see commit 3b9276848 onward and src/bluetooth-fw/nimble.) + cl_assert_equal_i(s_connection.gatt_service_changed_att_handle, + fake_gatt_gatt_profile_service_service_changed_att_handle()); } void test_gatt_service_changed_client__handle_gatt_profile_service_missing_service_changed(void) { fake_gatt_put_discovery_indication_gatt_profile_service(TEST_GATT_CONNECTION_ID, false /* has_service_changed_characteristic */); - cl_assert_equal_i(s_last_handle_discovery_result, Handled); + // No Service Changed characteristic in the profile service: nothing recorded. + cl_assert_equal_i(s_connection.gatt_service_changed_att_handle, 0); } // Characteristic Value Indications @@ -198,6 +157,10 @@ void test_gatt_service_changed_client__handle_indication_non_service_changed(voi void test_gatt_service_changed_client__handle_indication_service_changed(void) { fake_gatt_put_discovery_indication_gatt_profile_service(TEST_GATT_CONNECTION_ID, true /* has_service_changed_characteristic */); + // Finish the initial discovery so the indication-triggered rediscovery can + // start a fresh one. + fake_gatt_put_discovery_complete_event(GATT_SERVICE_DISCOVERY_STATUS_SUCCESS, + TEST_GATT_CONNECTION_ID); const uint16_t att_handle = fake_gatt_gatt_profile_service_service_changed_att_handle(); fake_kernel_malloc_mark(); diff --git a/tests/fw/comm/test_gatt_service_changed_server.c b/tests/fw/comm/test_gatt_service_changed_server.c index 2a9b578ed0..8602924a87 100644 --- a/tests/fw/comm/test_gatt_service_changed_server.c +++ b/tests/fw/comm/test_gatt_service_changed_server.c @@ -1,22 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/gatt_service_changed.h" #include "comm/ble/gap_le_connection.h" -#include "bluetopia_interface.h" #include "kernel/events.h" @@ -29,9 +15,7 @@ extern void gatt_service_changed_server_init(void); // Fakes /////////////////////////////////////////////////////////// -#include "fake_GAPAPI.h" -#include "fake_GATTAPI.h" -#include "fake_GATTAPI_test_vectors.h" +#include "fake_bt_driver_gatt.h" #include "fake_pbl_malloc.h" #include "fake_new_timer.h" #include "fake_rtc.h" @@ -40,9 +24,6 @@ extern void gatt_service_changed_server_init(void); // Stubs /////////////////////////////////////////////////////////// -#include "stubs_bluetopia_interface.h" -#include "stubs_bt_driver_gatt.h" -#include "stubs_bt_driver_gatt_client_discovery.h" #include "stubs_bt_lock.h" #include "stubs_events.h" #include "stubs_gatt_client_subscriptions.h" @@ -123,7 +104,7 @@ void test_gatt_service_changed_server__initialize(void) { gatt_service_changed_server_init(); fake_gatt_init(); gap_le_connection_init(); - gap_le_connection_add(&s_device, NULL, false /* local_is_master */); + gap_le_connection_add(&s_device, NULL, false /* local_is_master */, TIMER_INVALID_ID); s_connection = gap_le_connection_by_device(&s_device); cl_assert(s_connection); s_connection->gatt_connection_id = s_connection_id; @@ -176,7 +157,7 @@ void test_gatt_service_changed_server__reconnect_resubscribe_stop_sending_after_ static const int max_times = 5; for (int i = 0; i < max_times + 1; ++i) { - gap_le_connection_add(&s_device, NULL, false /* local_is_master */); + gap_le_connection_add(&s_device, NULL, false /* local_is_master */, TIMER_INVALID_ID); s_connection = gap_le_connection_by_device(&s_device); cl_assert(s_connection); s_connection->gatt_connection_id = s_connection_id; @@ -211,3 +192,18 @@ void test_gatt_service_changed_server__disconnect_during_delay(void) { prv_expect_service_changed_indication_api_call_count(0); } +void test_gatt_service_changed_server__indication_carries_connection_and_full_range(void) { + gatt_service_changed_server_handle_fw_update(); + prv_cccd_write(true /* is_subscribing */); + + // Fire the delayed indication and let the system-task callback run. + prv_expect_service_changed_indication_api_call_count(1); + + // The service layer must hand the driver the connection currently subscribed, + // and a "rediscover everything" range (0x0001 - 0xFFFF) so the remote cache is + // fully invalidated after the firmware update. + cl_assert(bt_device_equal(fake_gatt_get_service_changed_last_device(), &s_connection->device)); + const ATTHandleRange range = fake_gatt_get_service_changed_last_range(); + cl_assert_equal_i(range.start, 0x0001); + cl_assert_equal_i(range.end, 0xFFFF); +} diff --git a/tests/fw/comm/test_kernel_le_client.c b/tests/fw/comm/test_kernel_le_client.c index 9e9194dbbc..0484696c6c 100644 --- a/tests/fw/comm/test_kernel_le_client.c +++ b/tests/fw/comm/test_kernel_le_client.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "comm/ble/gap_le_advert.h" @@ -111,9 +98,6 @@ void ppogatt_destroy(void) { void ppogatt_handle_buffer_empty(void) { } -void bt_driver_reconnect_try_now(bool ignore_paused) { -} - void gatt_client_op_cleanup(GAPLEClient client) { } diff --git a/tests/fw/comm/test_ppogatt.c b/tests/fw/comm/test_ppogatt.c index 03ff717359..821632dc8e 100644 --- a/tests/fw/comm/test_ppogatt.c +++ b/tests/fw/comm/test_ppogatt.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/kernel_le_client/ppogatt/ppogatt.h" #include "comm/ble/kernel_le_client/ppogatt/ppogatt_internal.h" -#include "services/common/comm_session/session_transport.h" -#include "services/common/regular_timer.h" +#include "pbl/services/comm_session/session_transport.h" +#include "pbl/services/regular_timer.h" #include @@ -36,6 +23,7 @@ #include "stubs_print.h" #include "stubs_prompt.h" #include "stubs_rand_ptr.h" +#include "stubs_rtc.h" #include "stubs_serial.h" // Fakes @@ -69,6 +57,29 @@ GAPLEConnection *gatt_client_characteristic_get_connection(BLECharacteristic cha return NULL; } +// The bt_lock-held write path (see prv_send_next_packets) is only taken when +// bt_lock_is_held() is true; the test's bt_lock stub never reports it held, so +// these entry points must not be reached. Fail loudly if they are. +uint16_t gatt_client_characteristic_get_handle_and_connection( + BLECharacteristic characteristic_ref, GAPLEConnection **connection_out) { + cl_fail("unexpected call: bt_lock is never held in this test"); + return 0; +} + +BTErrno bt_driver_gatt_write_without_response(GAPLEConnection *connection, const uint8_t *value, + size_t value_length, uint16_t att_handle) { + cl_fail("unexpected call: bt_lock is never held in this test"); + return BTErrnoOK; +} + +// Meta rediscovery bails out in prv_request_meta_rediscovery when the +// characteristic has no connection, which the stub above always returns, so the +// kernelbg callback never runs and this is never reached. +BTErrno gatt_client_discovery_rediscover_all(const BTDeviceInternal *device) { + cl_fail("unexpected call: meta rediscovery has no connection in this test"); + return BTErrnoOK; +} + static BTDeviceInternal s_device = {}; BTDeviceInternal gatt_client_characteristic_get_device(BLECharacteristic characteristic_ref) { @@ -481,12 +492,31 @@ void test_ppogatt__cleanup_client_when_meta_read_fails(void) { cl_assert_equal_i(ppogatt_client_count(), 0); } +// Fires the pending meta-read retry timer and re-delivers a failing meta read +// response, advancing one step through the PPOGATT_META_READ_RETRY_COUNT_MAX +// retry budget. Used to drive a retriable meta failure to client deletion. +static void prv_fail_meta_read_retry(BLECharacteristic meta, const uint8_t *value, + size_t value_length, BLEGATTError error) { + stub_new_timer_invoke(1); + ppogatt_handle_read_or_notification(meta, value, value_length, error); +} + void test_ppogatt__cleanup_client_when_meta_read_gets_error_response(void) { fake_gatt_client_op_set_read_return_value(BTErrnoOK); prv_notify_services_discovered(1); - ppogatt_handle_read_or_notification(s_characteristics[0][PPoGATTCharacteristicMeta], - NULL, 0, - BLEGATTErrorInvalidHandle); + + BLECharacteristic meta = s_characteristics[0][PPoGATTCharacteristicMeta]; + + // A failing meta read is now retriable (commit 8651be8fb): the first failure + // schedules a retry rather than deleting the client. + ppogatt_handle_read_or_notification(meta, NULL, 0, BLEGATTErrorInvalidHandle); + cl_assert_equal_i(ppogatt_client_count(), 1); + + // Exhaust the remaining retries; the client is deleted only after + // PPOGATT_META_READ_RETRY_COUNT_MAX failures. + for (int i = 1; i < PPOGATT_META_READ_RETRY_COUNT_MAX; i++) { + prv_fail_meta_read_retry(meta, NULL, 0, BLEGATTErrorInvalidHandle); + } cl_assert_equal_i(ppogatt_client_count(), 0); } @@ -495,10 +525,21 @@ void test_ppogatt__cleanup_client_when_data_subscription_cccd_write_failed(void) prv_notify_services_discovered(1); - ppogatt_handle_read_or_notification(s_characteristics[0][PPoGATTCharacteristicMeta], - (const uint8_t *) &s_meta_v0_system, - sizeof(s_meta_v0_system), - BLEGATTErrorSuccess); + BLECharacteristic meta = s_characteristics[0][PPoGATTCharacteristicMeta]; + + // The meta read succeeds, but the data subscription cccd write fails. That is + // now treated as a retriable failure (commit 8651be8fb), so the first attempt + // schedules a retry instead of deleting the client. + ppogatt_handle_read_or_notification(meta, (const uint8_t *) &s_meta_v0_system, + sizeof(s_meta_v0_system), BLEGATTErrorSuccess); + cl_assert_equal_i(ppogatt_client_count(), 1); + + // Each retry re-reads the meta (success) and re-attempts the failing + // subscription; the client is deleted only after the retry budget is spent. + for (int i = 1; i < PPOGATT_META_READ_RETRY_COUNT_MAX; i++) { + prv_fail_meta_read_retry(meta, (const uint8_t *) &s_meta_v0_system, + sizeof(s_meta_v0_system), BLEGATTErrorSuccess); + } cl_assert_equal_i(ppogatt_client_count(), 0); cl_assert_equal_b(ppogatt_has_client_for_uuid(&s_meta_v0_system.app_uuid), false); } diff --git a/tests/fw/comm/wscript b/tests/fw/comm/wscript deleted file mode 100644 index adb995aa86..0000000000 --- a/tests/fw/comm/wscript +++ /dev/null @@ -1,182 +0,0 @@ -from waftools.pebble_test import clar -import waftools.ragel - -def build(bld): - clar(bld, - sources_ant_glob = "src/fw/comm/ble/kernel_le_client/ams/ams_util.c " - "src/fw/comm/ble/kernel_le_client/ams/ams.c " - "src/fw/services/normal/music.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_gatt_client_operations.c " - "tests/fakes/fake_gatt_client_subscriptions.c " - "tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_ams.c") - - clar(bld, - sources_ant_glob = "src/fw/comm/ble/kernel_le_client/ams/ams_util.c", - test_sources_ant_glob = "test_ams_util.c") - - # TODO: the bt_driver calls here should really be a fake and not rely on - # the cc2564 module - clar(bld, - sources_ant_glob = "src/fw/comm/ble/gap_le_advert.c " - "src/fw/comm/ble/gap_le_connection.c " - "src/fw/services/common/regular_timer.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_GAPAPI.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_HCIAPI.c " - "tests/fakes/fake_gap_le_connect_params.c ", - test_sources_ant_glob = "test_gap_le_advert.c", - override_includes=['dummy_board']) - - clar(bld, - sources_ant_glob = "src/fw/comm/internals/bt_conn_mgr.c " - "src/fw/services/common/regular_timer.c " - "tests/fakes/fake_gap_le_connect_params.c " - "tests/fakes/fake_rtc.c ", - test_sources_ant_glob = "test_bt_conn_mgr.c") - - clar(bld, - sources_ant_glob = "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/comm/ble/gap_le_connection.c " - "src/fw/comm/ble/gatt_client_accessors.c " - "src/fw/comm/ble/gatt_client_discovery.c " - "src/fw/comm/ble/gatt_service_changed.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_GATTAPI.c " - "tests/fakes/fake_GATTAPI_test_vectors.c " - "tests/fakes/fake_gap_le_connect_params.c ", - test_sources_ant_glob = "test_gatt_client_accessors.c") - - clar(bld, - sources_ant_glob = "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/comm/ble/gap_le_connection.c " - "src/fw/comm/ble/gatt_client_accessors.c " - "src/fw/comm/ble/gatt_client_discovery.c " - "src/fw/comm/ble/gatt_service_changed.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_gap_le_connect_params.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_GATTAPI.c " - "tests/fakes/fake_GATTAPI_test_vectors.c ", - test_sources_ant_glob = "test_gatt_client_discovery.c") - - clar(bld, - sources_ant_glob = "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/comm/ble/gap_le_connection.c " - "src/fw/comm/ble/gap_le_task.c " - "src/fw/comm/ble/gatt_client_accessors.c " - "src/fw/comm/ble/gatt_client_discovery.c " - "src/fw/comm/ble/gatt_client_subscriptions.c " - "src/fw/comm/ble/gatt_service_changed.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_gap_le_connect_params.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_GATTAPI.c " - "tests/fakes/fake_GATTAPI_test_vectors.c " - "tests/fakes/fake_queue.c " - "tests/fakes/fake_rtc.c ", - test_sources_ant_glob = "test_gatt_client_subscriptions.c") - - clar(bld, - sources_ant_glob = "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/comm/ble/gatt_client_discovery.c " - "src/fw/comm/ble/gatt_service_changed.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_gap_le_connect_params.c " - "tests/fakes/fake_GATTAPI.c " - "tests/fakes/fake_GATTAPI_test_vectors.c " - "tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_gatt_service_changed_client.c") - - clar(bld, - sources_ant_glob = "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/comm/ble/gap_le_connection.c " - "src/fw/comm/ble/gatt_client_discovery.c " - "src/fw/comm/ble/gatt_service_changed.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_gap_le_connect_params.c " - "tests/fakes/fake_GATTAPI.c " - "tests/fakes/fake_GATTAPI_test_vectors.c " - "tests/fakes/fake_rtc.c", - test_sources_ant_glob="test_gatt_service_changed_server.c") - - clar(bld, - sources_ant_glob = "src/fw/comm/ble/gap_le_connect.c " - "src/fw/comm/ble/gap_le_connection.c " - "src/fw/comm/ble/gap_le_task.c " - "src/fw/comm/internals/bt_conn_mgr.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_GAPAPI.c " - "tests/fakes/fake_bluetooth_persistent_storage.c " - "tests/fakes/fake_gap_le_connect_params.c " - "tests/fakes/fake_HCIAPI.c " - "tests/fakes/fake_rtc.c ", - test_sources_ant_glob = "test_gap_le_connect.c", - override_includes=['dummy_board']) - - clar(bld, - sources_ant_glob = "src/fw/util/time/mktime.c " \ - "src/fw/util/buffer.c " \ - "src/fw/util/time/time.c " \ - "src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c " \ - "tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_ancs_util.c") - - clar(bld, - sources_ant_glob = "src/fw/util/buffer.c " \ - "src/fw/util/pstring.c " \ - "src/fw/util/time/mktime.c " \ - "src/fw/util/time/time.c "\ - "src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c " \ - "src/fw/comm/ble/kernel_le_client/ancs/ancs.c " \ - "src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.c " \ - "src/fw/services/common/regular_timer.c " \ - "src/fw/util/rand/rand.c " \ - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " \ - "tests/fakes/fake_events.c " \ - "tests/fakes/fake_kernel_services_notifications.c " \ - "tests/fakes/fake_gatt_client_subscriptions.c " \ - "tests/fakes/fake_rtc.c " \ - "src/fw/services/normal/notifications/ancs/ancs_notifications.c " \ - " src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \ - "src/fw/services/normal/notifications/ancs/ancs_item.c " \ - "src/fw/services/normal/notifications/ancs/ancs_filtering.c " \ - "src/fw/services/normal/notifications/ancs/ancs_phone_call.c " \ - "src/fw/services/normal/timeline/item.c " \ - "src/fw/services/normal/timeline/attributes_actions.c " \ - "src/fw/services/normal/timeline/attribute_group.c " \ - "src/fw/services/normal/timeline/attribute.c", - test_sources_ant_glob = "test_ancs.c") - - clar(bld, - sources_ant_glob="src/fw/comm/ble/kernel_le_client/kernel_le_client.c " - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob="test_kernel_le_client.c") - - for ppogatt_version in [ 0, 1]: - clar(bld, - sources_ant_glob = "src/fw/system/hexdump.c " \ - "src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.c " \ - "src/fw/services/common/regular_timer.c " \ - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "tests/fakes/fake_gatt_client_operations.c " \ - "tests/fakes/fake_gatt_client_subscriptions.c " \ - "tests/fakes/fake_rtc.c " \ - "tests/fakes/fake_session.c", - defines=["USE_PPOGATT_VERSION=%d" % ppogatt_version], - test_name='test_ppogatt_v%d' % ppogatt_version, - test_sources_ant_glob = "test_ppogatt.c") - -# vim:filetype=python diff --git a/tests/fw/comm/wscript_build b/tests/fw/comm/wscript_build new file mode 100644 index 0000000000..09de3b6bff --- /dev/null +++ b/tests/fw/comm/wscript_build @@ -0,0 +1,170 @@ +from waftools.pebble_test import clar + +clar(bld, + sources_ant_glob = "src/fw/comm/ble/kernel_le_client/ams/ams_util.c " + "src/fw/comm/ble/kernel_le_client/ams/ams.c " + "src/fw/services/music/service.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_gatt_client_operations.c " + "tests/fakes/fake_gatt_client_subscriptions.c " + "tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_ams.c") + +clar(bld, + sources_ant_glob = "src/fw/comm/ble/kernel_le_client/ams/ams_util.c", + test_sources_ant_glob = "test_ams_util.c") + +clar(bld, + sources_ant_glob = "src/fw/comm/ble/gap_le_advert.c " + "src/fw/services/regular_timer/service.c " + "tests/fakes/fake_bt_driver_advert.c " + "tests/fakes/fake_rtc.c ", + test_sources_ant_glob = "test_gap_le_advert.c", + override_includes=['dummy_board']) + +clar(bld, + sources_ant_glob = "src/fw/comm/internals/bt_conn_mgr.c " + "src/fw/services/regular_timer/service.c " + "tests/fakes/fake_gap_le_connect_params.c " + "tests/fakes/fake_rtc.c ", + test_sources_ant_glob = "test_bt_conn_mgr.c") + +clar(bld, + sources_ant_glob = "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/comm/ble/gap_le_connection.c " + "src/fw/comm/ble/gatt_client_accessors.c " + "src/fw/comm/ble/gatt_client_discovery.c " + "src/fw/comm/ble/gatt_service_changed.c " + "src/fw/comm/internals/bt_conn_mgr.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_bt_driver_gatt.c " + "tests/fakes/fake_gap_le_connect_params.c ", + test_sources_ant_glob = "test_gatt_client_accessors.c") + +clar(bld, + sources_ant_glob = "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/comm/ble/gap_le_connection.c " + "src/fw/comm/ble/gatt_client_accessors.c " + "src/fw/comm/ble/gatt_client_discovery.c " + "src/fw/comm/ble/gatt_service_changed.c " + "src/fw/comm/internals/bt_conn_mgr.c " + "tests/fakes/fake_gap_le_connect_params.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_bt_driver_gatt.c ", + test_sources_ant_glob = "test_gatt_client_discovery.c") + +clar(bld, + sources_ant_glob = "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/comm/ble/gap_le_connection.c " + "src/fw/comm/ble/gap_le_task.c " + "src/fw/comm/ble/gatt_client_accessors.c " + "src/fw/comm/ble/gatt_client_discovery.c " + "src/fw/comm/ble/gatt_client_subscriptions.c " + "src/fw/comm/ble/gatt_service_changed.c " + "src/fw/comm/internals/bt_conn_mgr.c " + "tests/fakes/fake_gap_le_connect_params.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_bt_driver_gatt.c " + "tests/fakes/fake_queue.c " + "tests/fakes/fake_rtc.c ", + test_sources_ant_glob = "test_gatt_client_subscriptions.c", + defines=['CONFIG_BLE_GATT_NOTIF_WRITE_TIMEOUT_MS=100', + 'CONFIG_BLE_GATT_SUBSCRIPTION_DEPTH=4']) + +clar(bld, + sources_ant_glob = "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/comm/ble/gatt_client_discovery.c " + "src/fw/comm/ble/gatt_service_changed.c " + "src/fw/comm/internals/bt_conn_mgr.c " + "tests/fakes/fake_gap_le_connect_params.c " + "tests/fakes/fake_bt_driver_gatt.c " + "tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_gatt_service_changed_client.c") + +clar(bld, + sources_ant_glob = "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/comm/ble/gap_le_connection.c " + "src/fw/comm/ble/gatt_client_discovery.c " + "src/fw/comm/ble/gatt_service_changed.c " + "src/fw/comm/internals/bt_conn_mgr.c " + "tests/fakes/fake_gap_le_connect_params.c " + "tests/fakes/fake_bt_driver_gatt.c " + "tests/fakes/fake_rtc.c", + test_sources_ant_glob="test_gatt_service_changed_server.c") + +clar(bld, + sources_ant_glob = "src/fw/comm/ble/gap_le_connect.c " + "src/fw/comm/ble/gap_le_connection.c " + "src/fw/comm/ble/gap_le_task.c " + "src/fw/comm/internals/bt_conn_mgr.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_GAPAPI.c " + "tests/fakes/fake_bluetooth_persistent_storage.c " + "tests/fakes/fake_gap_le_connect_params.c " + "tests/fakes/fake_HCIAPI.c " + "tests/fakes/fake_rtc.c ", + test_sources_ant_glob = "test_gap_le_connect.c", + override_includes=['dummy_board']) + +clar(bld, + sources_ant_glob = "src/fw/util/time/mktime.c " \ + "src/fw/util/buffer.c " \ + "src/fw/util/time/time.c " \ + "src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c " \ + "tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_ancs_util.c") + +clar(bld, + sources_ant_glob = "src/fw/util/buffer.c " \ + "src/fw/util/pstring.c " \ + "src/fw/util/time/mktime.c " \ + "src/fw/util/time/time.c "\ + "src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c " \ + "src/fw/comm/ble/kernel_le_client/ancs/ancs.c " \ + "src/fw/comm/ble/kernel_le_client/ancs/ancs_app_name_storage.c " \ + "src/fw/services/regular_timer/service.c " \ + "src/fw/util/rand/rand.c " \ + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " \ + "tests/fakes/fake_events.c " \ + "tests/fakes/fake_kernel_services_notifications.c " \ + "tests/fakes/fake_gatt_client_subscriptions.c " \ + "tests/fakes/fake_rtc.c " \ + "src/fw/services/notifications/ancs/ancs_notifications.c " \ + " src/fw/services/notifications/ancs/ancs_notifications_util.c " \ + "src/fw/services/notifications/ancs/ancs_item.c " \ + "src/fw/services/notifications/ancs/ancs_filtering.c " \ + "src/fw/services/notifications/ancs/ancs_phone_call.c " \ + "src/fw/services/timeline/item.c " \ + "src/fw/services/timeline/attributes_actions.c " \ + "src/fw/services/timeline/attribute_group.c " \ + "src/fw/services/timeline/attribute.c", + test_sources_ant_glob = "test_ancs.c") + +clar(bld, + sources_ant_glob="src/fw/comm/ble/kernel_le_client/kernel_le_client.c " + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob="test_kernel_le_client.c") + +for ppogatt_version in [ 0, 1]: + clar(bld, + sources_ant_glob = "src/fw/system/hexdump.c " \ + "src/fw/comm/ble/kernel_le_client/ppogatt/ppogatt.c " \ + "src/fw/services/regular_timer/service.c " \ + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "tests/fakes/fake_gatt_client_operations.c " \ + "tests/fakes/fake_gatt_client_subscriptions.c " \ + "tests/fakes/fake_session.c", + defines=["USE_PPOGATT_VERSION=%d" % ppogatt_version], + test_name='test_ppogatt_v%d' % ppogatt_version, + test_sources_ant_glob = "test_ppogatt.c") + +# vim:filetype=python diff --git a/tests/fw/console/test_cobs_decode.c b/tests/fw/console/test_cobs_decode.c index 093e6f844e..55a15def84 100644 --- a/tests/fw/console/test_cobs_decode.c +++ b/tests/fw/console/test_cobs_decode.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fw/console/test_cobs_encode.c b/tests/fw/console/test_cobs_encode.c index 9ff32227bc..34bcd67e6c 100644 --- a/tests/fw/console/test_cobs_encode.c +++ b/tests/fw/console/test_cobs_encode.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fw/console/wscript b/tests/fw/console/wscript deleted file mode 100644 index 14fbc17a48..0000000000 --- a/tests/fw/console/wscript +++ /dev/null @@ -1,13 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/fw/console/cobs.c", - test_sources_ant_glob = "test_cobs_decode.c") - - clar(ctx, - sources_ant_glob = "src/fw/console/cobs.c", - test_sources_ant_glob = "test_cobs_encode.c") - -# vim:filetype=python - diff --git a/tests/fw/console/wscript_build b/tests/fw/console/wscript_build new file mode 100644 index 0000000000..649d7512db --- /dev/null +++ b/tests/fw/console/wscript_build @@ -0,0 +1,11 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/console/cobs.c", + test_sources_ant_glob = "test_cobs_decode.c") + +clar(ctx, + sources_ant_glob = "src/fw/console/cobs.c", + test_sources_ant_glob = "test_cobs_encode.c") + +# vim:filetype=python diff --git a/tests/fw/drivers/test_battery.c b/tests/fw/drivers/test_battery.c deleted file mode 100644 index 72bb5df0ee..0000000000 --- a/tests/fw/drivers/test_battery.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "drivers/battery.h" - -static uint32_t prv_convert_millivolts_to_12bit_reading(int millivolts) { - return 4095 * millivolts / 1800; -} - -#define VREF_VOLTAGE 1200 - -void test_battery__reading_conversion_boring(void) { - ADCVoltageMonitorReading reading = { - .vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE), - .vmon_total = prv_convert_millivolts_to_12bit_reading(1800) - }; - - uint32_t result = battery_convert_reading_to_millivolts(reading, 1, 1); - cl_assert_equal_i(result, 1800); - - reading.vmon_total = prv_convert_millivolts_to_12bit_reading(1200); - - result = battery_convert_reading_to_millivolts(reading, 1, 1); - cl_assert_equal_i(result, 1200); - - reading.vmon_total = prv_convert_millivolts_to_12bit_reading(0); - - result = battery_convert_reading_to_millivolts(reading, 1, 1); - cl_assert_equal_i(result, 0); -} - -void test_battery__reading_conversion_40_samples(void) { - ADCVoltageMonitorReading reading = { - .vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE) * 40, - .vmon_total = prv_convert_millivolts_to_12bit_reading(1800) * 40 - }; - - uint32_t result = battery_convert_reading_to_millivolts(reading, 1, 1); - cl_assert_equal_i(result, 1800); - - reading.vmon_total = prv_convert_millivolts_to_12bit_reading(1200) * 40; - - result = battery_convert_reading_to_millivolts(reading, 1, 1); - cl_assert_equal_i(result, 1200); - - reading.vmon_total = prv_convert_millivolts_to_12bit_reading(0); - - result = battery_convert_reading_to_millivolts(reading, 1, 1); - cl_assert_equal_i(result, 0); -} - -// Make sure our new method for calculating battery usage matches the old one for stm32f2 -/////////////////////////////////////////////////////////// -static int prv_legacy_f2_calculation_millivolts(ADCVoltageMonitorReading reading) { - return (int) ((reading.vmon_total * (2730) / reading.vref_total) * 295) / 256; -} - -void test_battery__reading_conversion_f2(void) { - ADCVoltageMonitorReading reading = { - .vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE), - .vmon_total = prv_convert_millivolts_to_12bit_reading(1800) - }; - - uint32_t result = battery_convert_reading_to_millivolts(reading, 3599, 1373); - cl_assert_equal_i(result, prv_legacy_f2_calculation_millivolts(reading)); -} - - -// Make sure our new method for calculating battery usage matches the old one for stm32f4 -/////////////////////////////////////////////////////////// -static int prv_legacy_f4_calculation_millivolts(ADCVoltageMonitorReading reading) { - return (int) (((reading.vmon_total * (2730) / reading.vref_total) * 120) / 91); -} - -void test_battery__reading_conversion_f4(void) { - ADCVoltageMonitorReading reading = { - .vref_total = prv_convert_millivolts_to_12bit_reading(VREF_VOLTAGE), - .vmon_total = prv_convert_millivolts_to_12bit_reading(1800) - }; - - uint32_t result = battery_convert_reading_to_millivolts(reading, 3, 1); - cl_assert_equal_i(result, prv_legacy_f4_calculation_millivolts(reading)); -} - diff --git a/tests/fw/drivers/test_flash_api.c b/tests/fw/drivers/test_flash_api.c index 23a867333d..406277608b 100644 --- a/tests/fw/drivers/test_flash_api.c +++ b/tests/fw/drivers/test_flash_api.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "fake_new_timer.h" @@ -166,6 +153,27 @@ status_t flash_impl_get_nvram_erase_status(bool *is_subsector, return S_FALSE; } +status_t flash_impl_read_security_register(uint32_t addr, uint8_t *val) { + return S_SUCCESS; +} + +status_t flash_impl_security_register_is_locked(uint32_t address, bool *locked) { + *locked = false; + return S_SUCCESS; +} + +status_t flash_impl_erase_security_register(uint32_t addr) { + return S_SUCCESS; +} + +status_t flash_impl_write_security_register(uint32_t addr, uint8_t val) { + return S_SUCCESS; +} + +const FlashSecurityRegisters *flash_impl_security_registers_info(void) { + return NULL; +} + void flash_erase_init(void) { } diff --git a/tests/fw/drivers/test_flash_erase.c b/tests/fw/drivers/test_flash_erase.c index 78247c4053..1b347cd3d8 100644 --- a/tests/fw/drivers/test_flash_erase.c +++ b/tests/fw/drivers/test_flash_erase.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "drivers/flash.h" -#include "services/common/new_timer/new_timer.h" +#include "pbl/services/new_timer/new_timer.h" #include "clar.h" diff --git a/tests/fw/drivers/test_fpc_pinstrap.c b/tests/fw/drivers/test_fpc_pinstrap.c deleted file mode 100644 index d83cbbba08..0000000000 --- a/tests/fw/drivers/test_fpc_pinstrap.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "board/board.h" -#include "drivers/fpc_pinstrap.h" - -static bool s_pin_pull_up_enabled[2] = { false, false }; - -void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd) { - s_pin_pull_up_enabled[input_cfg->gpio_pin] = (pupd == GPIO_PuPd_UP); -} - -void gpio_analog_init(const InputConfig *input_cfg) { -} - -static enum { - PinstrapResult_GND, - PinstrapResult_Vplus, - PinstrapResult_Float -} s_pinstrap_results[2]; - -bool gpio_input_read(const InputConfig *input_cfg) { - switch (s_pinstrap_results[input_cfg->gpio_pin]) { - case PinstrapResult_GND: - return false; - case PinstrapResult_Vplus: - return true; - case PinstrapResult_Float: - return s_pin_pull_up_enabled[input_cfg->gpio_pin]; - } -} - -void test_fpc_pinstrap__simple(void) { - s_pinstrap_results[0] = PinstrapResult_GND; - - s_pinstrap_results[1] = PinstrapResult_GND; - cl_assert(fpc_pinstrap_get_value() == 0x0); - - s_pinstrap_results[1] = PinstrapResult_Vplus; - cl_assert(fpc_pinstrap_get_value() == 0x1); - - s_pinstrap_results[1] = PinstrapResult_Float; - cl_assert(fpc_pinstrap_get_value() == 0x2); - - - s_pinstrap_results[0] = PinstrapResult_Vplus; - - s_pinstrap_results[1] = PinstrapResult_GND; - cl_assert(fpc_pinstrap_get_value() == 0x3); - - s_pinstrap_results[1] = PinstrapResult_Vplus; - cl_assert(fpc_pinstrap_get_value() == 0x4); - - s_pinstrap_results[1] = PinstrapResult_Float; - cl_assert(fpc_pinstrap_get_value() == 0x5); - - - s_pinstrap_results[0] = PinstrapResult_Float; - - s_pinstrap_results[1] = PinstrapResult_GND; - cl_assert(fpc_pinstrap_get_value() == 0x6); - - s_pinstrap_results[1] = PinstrapResult_Vplus; - cl_assert(fpc_pinstrap_get_value() == 0x7); - - s_pinstrap_results[1] = PinstrapResult_Float; - cl_assert(fpc_pinstrap_get_value() == 0x8); -} diff --git a/tests/fw/drivers/test_i2c_timingr.c b/tests/fw/drivers/test_i2c_timingr.c deleted file mode 100644 index 92a6b5a3b4..0000000000 --- a/tests/fw/drivers/test_i2c_timingr.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "drivers/stm32f7/i2c_timingr.h" - -#define MHZ_TO_HZ(val) ((val) * 1000000) -#define KHZ_TO_HZ(val) ((val) * 1000) - - -static void prv_check_result(uint32_t timingr, uint32_t expected_prescaler, - uint32_t expected_scl_low, uint32_t expected_scl_high, - uint32_t expected_scl_delay) { - cl_assert_(timingr != I2C_TIMINGR_INVALID_VALUE, "timingr == I2C_TIMINGR_INVALID_VALUE"); - cl_assert_equal_i((timingr >> 28) + 1, expected_prescaler); - cl_assert_equal_i((timingr & 0xFF) + 1, expected_scl_low); - cl_assert_equal_i(((timingr >> 8) & 0xFF) + 1, expected_scl_high); - cl_assert_equal_i(((timingr >> 20) & 0xF) + 1, expected_scl_delay); -} - -void test_i2c_timingr__valid_no_prescaler_no_rise_fall_time(void) { - // We'll use a base clock speed of 36Mhz and try to get to 400kHz I2C. We should be able to do - // this with a prescaler of 1. - // - // 36MHz / 400kHz = 90 cycles => 90 - 6 sync cycles = 84 cycles to play with - // minimum low = ceil(1300ns / (1 / 36MHz)) = 47 cycles - // minimum high = ceil(600ns / (1 / 36MHz)) = 22 cycles - // extra cycles = 15 => (7 low, 8 high) => SCLL of 54 and SCLH of 30 - // - // SCLDEL = ceil((t_r 0 + t_SU 100ns) / (1 / 36MHz)) = 4 cycles - prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(36), I2CBusMode_FastMode, - KHZ_TO_HZ(400), 0, 0), - 1, 54, 30, 4); -} - -void test_i2c_timingr__valid_prescaler_no_rise_fall_time(void) { - // We'll use a base clock speed of 360Mhz and try to get to 100kHz I2C. This requires a prescaler - // of 8 which gets us down to a base clock speed of 45MHz. - // - // 45MHz / 100kHz = 450 cycles => 450 - ceil(6 / 8) sync cycles = 449 cycles to play with - // minimum low = ceil(4700ns / (1 / 45MHz)) = 212 cycles - // minimum high = ceil(4000ns / (1 / 45MHz)) = 181 cycles - // extra cycles = 56 => (28 low, 28 high) => SCLL of 240 and SCLH of 209 - // - // SCLDEL = ceil((t_r 0 + t_SU 250ns) / (1 / 45MHz)) = 12 - prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(360), I2CBusMode_Standard, - KHZ_TO_HZ(100), 0, 0), - 8, 240, 209, 12); -} - -void test_i2c_timingr__valid_no_prescaler_rise_fall_time(void) { - // We'll use a base clock speed of 20MHz and try to get to 100kHz I2C with fall and rise times of - // 500ns each. - // - // 20MHz / 100kHz = 200 cycles => 200 - 6 sync cycles - (2 * 500ns / (1 / 20MHz)) = 174 cycles to - // play with - // minimum low = ceil(4700ns / (1 / 20MHz)) = 94 cycles - // minimum high = ceil(4000ns / (1 / 20MHz)) = 80 cycles - // extra cycles = 0 => (0 low, 0 high) => SCLL of 94 and SCLH of 80 - // - // SCLDEL = ceil((t_r 500ns + t_SU 250ns) / (1 / 20MHz)) = 15 - prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(20), I2CBusMode_Standard, - KHZ_TO_HZ(100), 500, 500), - 1, 94, 80, 15); -} - -void test_i2c_timingr__data_delay_requires_prescaler(void) { - // We'll increase the rise time enough that the required SCLDEL will exceed the max value with - // no prescaler, forcing the use of the prescaler even though the SCLL and SCLH values wouldn't - // otherwise require it. - // - // With prescaler 1: SCLDEL = ceil((800ns + 250ns) / (1 / 20MHz)) = 21 > 16 - // With prescaler 2: base clock is 10MHz. - // 10MHz / 100kHz = 100 cycles => 100 - 6 sync cycles - ((800ns + 200ns) / (1 / 10 MHz)) = 84 - // cycles to play with - // minimum low = ceil(4700ns / (1 / 10MHz)) = 47 cycles - // minimum high = ceil(4000ns / (1 / 10MHz)) = 40 cycles - // extra cycles = 0 => (0 low, 0 high) => SCLL of 47 and SCLH of 80 - // - // SCLDEL = ceil((t_r 800ns + t_SU 250ns) / (1 / 10MHz)) = 11 - prv_check_result(i2c_timingr_calculate(MHZ_TO_HZ(20), I2CBusMode_Standard, - KHZ_TO_HZ(100), 800, 200), - 2, 47, 40, 11); -} - -void test_i2c_timingr__invalid_speed_too_high(void) { - // We'll use a base clock speed of 1Mhz and try to get to 400KHz I2C, which won't be possible - // because the sync cycles alone will make us way slower than 400kHz. - cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(1), I2CBusMode_FastMode, - KHZ_TO_HZ(400), 0, 0), - I2C_TIMINGR_INVALID_VALUE); -} - -void test_i2c_timingr__invalid_speed_too_low(void) { - // We'll use a base clock speed of 1600Mhz and try to get to 100KHz I2C, which won't be possible - // because the max prescaler is 16, which still leaves us with 1000 clock periods which is too - // many to fit. - cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(1600), I2CBusMode_Standard, - KHZ_TO_HZ(100), 0, 0), - I2C_TIMINGR_INVALID_VALUE); -} - -void test_i2c_timingr__invalid_speed_too_high_for_mode(void) { - // Try calculating timing for 400kHz in Standard mode, which is out of spec for that mode. - cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(36), I2CBusMode_Standard, - KHZ_TO_HZ(400), 0, 0), - I2C_TIMINGR_INVALID_VALUE); -} - -void test_i2c_timingr__invalid_long_rise_fall(void) { - // We'll use a base clock speed of 100Mhz and try to get to 100KHz I2C with very long (out of - // spec) 5us rise and fall times which prevent us from hitting the target frequency. - cl_assert_equal_i(i2c_timingr_calculate(MHZ_TO_HZ(100), I2CBusMode_Standard, - KHZ_TO_HZ(100), 5000, 5000), - I2C_TIMINGR_INVALID_VALUE); -} diff --git a/tests/fw/drivers/test_qemu_serial.c b/tests/fw/drivers/test_qemu_serial.c index dfbe6c93b3..4ab64d5502 100644 --- a/tests/fw/drivers/test_qemu_serial.c +++ b/tests/fw/drivers/test_qemu_serial.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/tests/fw/drivers/test_rtc_calibration.c b/tests/fw/drivers/test_rtc_calibration.c deleted file mode 100644 index 4f6789ef7e..0000000000 --- a/tests/fw/drivers/test_rtc_calibration.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "drivers/stm32f2/rtc_calibration.h" - -#include "clar.h" - -// Stubs and stuff -#include "stubs_logging.h" -#include "mcu.h" - -#define TARGET_FREQUENCY_mHZ (32768 * 1000) -#define ALTERNATE_FREQUENCY_mHZ (1000000 * 1000) - -// Tests - -void test_rtc_calibration__no_calibration_required(void) { - RTCCalibConfig config = rtc_calibration_get_config(32768000, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 0); -} - -void test_rtc_calibration__slightly_slow_but_not_enough_to_calibrate(void) { - // Approximately -2.01ppm - RTCCalibConfig config = rtc_calibration_get_config(32767934, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 0); -} - -void test_rtc_calibration__just_slow_enough_to_calibrate(void) { - // Approximately -2.04ppm - RTCCalibConfig config = rtc_calibration_get_config(32767933, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 1); - cl_assert_equal_i(config.sign, RTC_CalibSign_Positive); -} - -void test_rtc_calibration__slightly_fast_but_not_enough_to_calibrate(void) { - // Approximately +1.01ppm - RTCCalibConfig config = rtc_calibration_get_config(32768033, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 0); -} - -void test_rtc_calibration__just_fast_enough_to_calibrate(void) { - // Approximately +1.04ppm - RTCCalibConfig config = rtc_calibration_get_config(32768034, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 1); - cl_assert_equal_i(config.sign, RTC_CalibSign_Negative); -} - -void test_rtc_calibration__out_of_bounds_slow(void) { - // Approximately -130ppm - RTCCalibConfig config = rtc_calibration_get_config(32763740, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 31); - cl_assert_equal_i(config.sign, RTC_CalibSign_Positive); -} - -void test_rtc_calibration__out_of_bounds_fast(void) { - // Approximately +70ppm - RTCCalibConfig config = rtc_calibration_get_config(32770294, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 31); - cl_assert_equal_i(config.sign, RTC_CalibSign_Negative); -} - -void test_rtc_calibration__different_target_frequency_not_fast_enough(void) { - // Approximately +1.017ppm - RTCCalibConfig config = rtc_calibration_get_config(1000001017, ALTERNATE_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 0); -} - -void test_rtc_calibration__different_target_frequency_just_fast_enough(void) { - // Approximately +1.018ppm - RTCCalibConfig config = rtc_calibration_get_config(1000001018, ALTERNATE_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 1); - cl_assert_equal_i(config.sign, RTC_CalibSign_Negative); -} - -void test_rtc_calibration__invalid_frequency(void) { - // Bigboards don't have a frequency stored in their mfg info registry. - RTCCalibConfig config = rtc_calibration_get_config(0, TARGET_FREQUENCY_mHZ); - cl_assert_equal_i(config.units, 0); -} diff --git a/tests/fw/drivers/wscript b/tests/fw/drivers/wscript deleted file mode 100644 index 9d026428ff..0000000000 --- a/tests/fw/drivers/wscript +++ /dev/null @@ -1,41 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - - clar(ctx, - sources_ant_glob = "src/fw/drivers/qemu/qemu_serial_util.c" \ - " src/fw/util/shared_circular_buffer.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_qemu_serial.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob="src/fw/drivers/fpc_pinstrap/fpc_pinstrap_snowy.c ", - test_sources_ant_glob="test_fpc_pinstrap.c", - override_includes=['fpc_pinstrap_board']) - - clar(ctx, - sources_ant_glob = "src/fw/drivers/battery/battery_adc_conversion.c", - test_sources_ant_glob = "test_battery.c") - - clar(ctx, - sources_ant_glob = "src/fw/drivers/stm32f2/rtc_calibration.c", - test_sources_ant_glob = "test_rtc_calibration.c", - override_includes=['rtc_calibration']) - - clar(ctx, - sources_ant_glob = ('src/fw/drivers/stm32f7/i2c_timingr.c'), - test_sources_ant_glob = 'test_i2c_timingr.c') - - clar(ctx, - sources_ant_glob = ('src/fw/drivers/flash/flash_api.c'), - test_sources_ant_glob = 'test_flash_api.c', - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=('src/fw/drivers/flash/flash_erase.c'), - test_sources_ant_glob='test_flash_erase.c', - override_includes=['dummy_board'], - platforms=['tintin']) - -# vim:filetype=python diff --git a/tests/fw/drivers/wscript_build b/tests/fw/drivers/wscript_build new file mode 100644 index 0000000000..8304d1ef21 --- /dev/null +++ b/tests/fw/drivers/wscript_build @@ -0,0 +1,21 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/drivers/qemu/qemu_serial_util.c" \ + " src/fw/util/shared_circular_buffer.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_qemu_serial.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = ('src/fw/drivers/flash/flash_api.c'), + test_sources_ant_glob = 'test_flash_api.c', + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=('src/fw/drivers/flash/flash_erase.c'), + test_sources_ant_glob='test_flash_erase.c', + override_includes=['dummy_board'], + platforms=['obelix']) + +# vim:filetype=python diff --git a/tests/fw/graphics/1bit/test_framebuffer.h b/tests/fw/graphics/1bit/test_framebuffer.h index 668131bac4..8b239e53e7 100644 --- a/tests/fw/graphics/1bit/test_framebuffer.h +++ b/tests/fw/graphics/1bit/test_framebuffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fw/graphics/8bit/test_framebuffer.h b/tests/fw/graphics/8bit/test_framebuffer.h index 6a5c87c3fb..a0a71b31d1 100644 --- a/tests/fw/graphics/8bit/test_framebuffer.h +++ b/tests/fw/graphics/8bit/test_framebuffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fw/graphics/graphics_common_stubs.h b/tests/fw/graphics/graphics_common_stubs.h index 2fddc7ca5d..c726d75c4c 100644 --- a/tests/fw/graphics/graphics_common_stubs.h +++ b/tests/fw/graphics/graphics_common_stubs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_app_state.h" #include "stubs_heap.h" diff --git a/tests/fw/graphics/test_bitblt.c b/tests/fw/graphics/test_bitblt.c index eeb925a1f6..daeb458ed5 100644 --- a/tests/fw/graphics/test_bitblt.c +++ b/tests/fw/graphics/test_bitblt.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/bitblt.h" diff --git a/tests/fw/graphics/test_bitblt_circular.c b/tests/fw/graphics/test_bitblt_circular.c deleted file mode 100644 index 354e46169a..0000000000 --- a/tests/fw/graphics/test_bitblt_circular.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/graphics/gtypes.h" -#include "applib/graphics/bitblt.h" - -#include "clar.h" -#include "util.h" - -#include -#include - -// Stubs -//////////////////////////////////// -#include "graphics_common_stubs.h" -#include "stubs_applib_resource.h" - -// Helper Functions -//////////////////////////////////// -#include "test_graphics.h" - -#if SCREEN_COLOR_DEPTH_BITS == 8 - #include "8bit/test_framebuffer.h" -#else - Static_assert(false, "These tests are only for color displays"); -#endif - -static GContext *ctx; -static FrameBuffer *fb = NULL; - -// Setup -void test_bitblt_circular__initialize(void) { - fb = malloc(sizeof(FrameBuffer)); - framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS}); - ctx = malloc(sizeof(GContext)); - test_graphics_context_init(ctx, fb); - framebuffer_clear(fb); -} - -// Teardown -void test_bitblt_circular__cleanup(void) { - free(fb); - free(ctx); -} - -// Tests -//////////////////////////////////// - -// Reference PNGs reside in "tests/test_images/" -// and are created at build time, with the test PBI file generated -// by bitmapgen.py from the reference PNG copied to TEST_IMAGES_PATH -// covers 1-bit b&w image -// covers 1,2,4,8 bit palettized - -// Tests 1-bit black&white PBI loading into gbitmap -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_1_bit_bw(void) { - const char test_file[] = "test_bitblt_circular__color_1_bit_bw.1bit.pbi"; - GBitmap *bitmap = setup_pbi_test(test_file); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - native_framebuffer->info.format = GBitmapFormat8BitCircular; - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, test_file)); -} - -// Tests 1-bit red&white palettized PBI loading into gbitmap -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_1_bit(void) { - GBitmap *bitmap = setup_png_test(TEST_PNG_FILE); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -// Tests 2-bit palettized PBI loading into gbitmap -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_2_bit(void) { - GBitmap *bitmap = setup_png_test(TEST_PNG_FILE); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -// Tests 4-bit bitblt palettized to circular display buffer -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_4_bit(void) { - GBitmap *bitmap = setup_png_test(TEST_PNG_FILE); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -// Tests 8-bit bitblt to circular display buffer -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_8_bit(void) { - GBitmap *bitmap = setup_png_test(TEST_PNG_FILE); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -// Tests 8-bit bitblt tiling support to circular display buffer -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_8_bit_tiling(void) { - GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__tile")); - cl_assert(bitmap->info.format == GBitmapFormat8Bit); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap_tiled( - native_framebuffer, bitmap, native_framebuffer->bounds, - GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -// Tests palettized bitblt non-power-of-two tiling support to circular display buffer -// Result: -// - gbitmap matches platform loaded PBI -void test_bitblt_circular__color_8_bit_tiling_palettized(void) { - GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__tile_palettized")); - cl_assert(bitmap->info.format == GBitmapFormat2BitPalette); - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - bitblt_bitmap_into_bitmap_tiled( - native_framebuffer, bitmap, native_framebuffer->bounds, - GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -void test_bitblt_circular__8_bit_converted_circular(void) { - GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__spiral")); - cl_assert(bitmap->info.format == GBitmapFormat8Bit); - - // Convert input PNG from rectangular to circular - gbitmap_8bit_to_8bit_circular(bitmap); - cl_assert(bitmap->info.format == GBitmapFormat8BitCircular); - - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - GRect framebuffer_bounds = gbitmap_get_bounds(native_framebuffer); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - - // Set screen to black to match empty region with test image - graphics_context_set_fill_color(ctx, GColorBlack); - graphics_fill_rect(ctx, &framebuffer_bounds); - - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} - -void test_bitblt_circular__8_bit_converted_circular_offset(void) { - GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__spiral")); - cl_assert(bitmap->info.format == GBitmapFormat8Bit); - - // Convert input PNG from rectangular to circular - gbitmap_8bit_to_8bit_circular(bitmap); - cl_assert(bitmap->info.format == GBitmapFormat8BitCircular); - - GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx); - GRect framebuffer_bounds = gbitmap_get_bounds(native_framebuffer); - cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular); - cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944); - - // Set screen to black to match empty region with test image - graphics_context_set_fill_color(ctx, GColorBlack); - graphics_fill_rect(ctx, &framebuffer_bounds); - - // Shift the output by non-power-of-2 values for testing - bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPoint(33, 10), GCompOpAssign, GColorWhite); - cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE)); -} diff --git a/tests/fw/graphics/test_bitblt_palette.c b/tests/fw/graphics/test_bitblt_palette.c index 7bb2e15950..def48621e9 100644 --- a/tests/fw/graphics/test_bitblt_palette.c +++ b/tests/fw/graphics/test_bitblt_palette.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/bitblt.h" @@ -53,7 +40,7 @@ static GBitmap dest_bitmap = { // Utilities //////////////////////////////////// -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 extern GColor get_bitmap_color(GBitmap *bmp, int x, int y); #endif @@ -130,7 +117,7 @@ void test_bitblt_palette__1Bit_color(void) { } memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data)); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 char print_buf[20]; for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { @@ -174,7 +161,7 @@ void test_bitblt_palette__4Bit_assign(void) { } memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data)); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 char print_buf[20]; for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { @@ -223,7 +210,7 @@ static void prv_opaque_2bit_simple(GCompOp compositing_mode) { memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data)); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 char print_buf[20]; for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { diff --git a/tests/fw/graphics/test_bitblt_palette_1bit.c b/tests/fw/graphics/test_bitblt_palette_1bit.c index d46c9ffeb1..a840d3049f 100644 --- a/tests/fw/graphics/test_bitblt_palette_1bit.c +++ b/tests/fw/graphics/test_bitblt_palette_1bit.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/graphics_private.h" diff --git a/tests/fw/graphics/test_blending.template.c b/tests/fw/graphics/test_blending.template.c index a531ae00cf..4ceaed29e9 100644 --- a/tests/fw/graphics/test_blending.template.c +++ b/tests/fw/graphics/test_blending.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -60,11 +47,11 @@ void prv_test_plot_horizontal_line(GBitmap *framebuffer, GRect area, GColor colo int16_t x_max = MIN(MAX(x0, x1), framebuffer->bounds.origin.x + framebuffer->bounds.size.w); for (int i=x_min; i < x_max; i++) { -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 GColor *output = (GColor*)framebuffer->addr + y * framebuffer->row_size_bytes + i; // src_col, dest_col output->argb = gcolor_alpha_blend(color, (*output)).argb; -#endif // SCREEN_COLOR_DEPTH_BITS == 8 +#endif // CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 } } @@ -80,7 +67,7 @@ void test_blending_${BIT_DEPTH_NAME}__photoshop(void) { setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1); graphics_draw_bitmap_in_rect(&ctx, background_0_100, &ORIGIN_RECT_NO_CLIP); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // Sanity check cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "blendtest_0_100_backdrop.pbi")); diff --git a/tests/fw/graphics/test_framebuffer.template.c b/tests/fw/graphics/test_framebuffer.template.c index a5e31d3ef2..5c4647c8b0 100644 --- a/tests/fw/graphics/test_framebuffer.template.c +++ b/tests/fw/graphics/test_framebuffer.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -41,11 +28,11 @@ void test_framebuffer_${BIT_DEPTH_NAME}__framebuffer_clear(void) { framebuffer_clear(&framebuffer); // check the clearing -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 for (int i = 0; i < ARRAY_LENGTH(framebuffer.buffer); i++) { cl_assert(framebuffer.buffer[i] == 0xffffffff); } -#elif SCREEN_COLOR_DEPTH_BITS == 8 +#elif CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 for (int i = 0; i < ARRAY_LENGTH(framebuffer.buffer); i++) { cl_assert(framebuffer.buffer[i] == GColorWhite.argb); } diff --git a/tests/fw/graphics/test_framebuffer_clipping.c b/tests/fw/graphics/test_framebuffer_clipping.c deleted file mode 100644 index 7440f6073b..0000000000 --- a/tests/fw/graphics/test_framebuffer_clipping.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/graphics/graphics.h" -#include "applib/graphics/graphics_private.h" -#include "applib/graphics/graphics_private_raw.h" -#include "applib/graphics/framebuffer.h" -#include "util/trig.h" - -#include "applib/ui/window_private.h" -#include "applib/ui/layer.h" - - -#include "clar.h" -#include "util.h" - -#include - -// Helper Functions -//////////////////////////////////// -#include "test_graphics.h" -#include "8bit/test_framebuffer.h" - -// Stubs -//////////////////////////////////// -#include "graphics_common_stubs.h" -#include "stubs_applib_resource.h" - -static FrameBuffer *fb = NULL; - -#define CLIP_RECT_DRAW_BOX GRect(0, 0, DISP_COLS, DISP_ROWS) -#define CLIP_RECT_CLIP_BOX GRect(0, 0, DISP_COLS, DISP_ROWS) - -// Setup and Teardown -//////////////////////////////////// - -// Setup -void test_framebuffer_clipping__initialize(void) { - fb = malloc(sizeof(FrameBuffer)); - framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS }); -} - -// Teardown -void test_framebuffer_clipping__cleanup(void) { - free(fb); -} - -// Tests -//////////////////////////////////// - -void test_framebuffer_clipping__off_screen_left_aa_clipping(void) { - cl_assert_equal_i(GBITMAP_NATIVE_FORMAT, GBitmapFormat8BitCircular); - - GContext ctx; - test_graphics_context_init(&ctx, fb); - setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1); - - cl_assert_equal_i(ctx.dest_bitmap.info.format, GBitmapFormat8BitCircular); - - memset(ctx.dest_bitmap.addr, GColorRedARGB8, FRAMEBUFFER_SIZE_BYTES); - - GRect radial_container_rect = ctx.dest_bitmap.bounds; - radial_container_rect = grect_inset(radial_container_rect, GEdgeInsets(-10)); - const uint16_t inset_thickness = radial_container_rect.size.w / 4; - graphics_context_set_fill_color(&ctx, GColorGreen); - graphics_fill_radial(&ctx, radial_container_rect, GOvalScaleModeFillCircle, inset_thickness, - TRIG_MAX_ANGLE / 2, TRIG_MAX_ANGLE); - - cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("off_screen_left_aa_clipping"))); -}; - -void test_framebuffer_clipping__off_screen_right_aa_clipping(void) { - cl_assert_equal_i(GBITMAP_NATIVE_FORMAT, GBitmapFormat8BitCircular); - - GContext ctx; - test_graphics_context_init(&ctx, fb); - setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1); - cl_assert_equal_i(ctx.dest_bitmap.info.format, GBitmapFormat8BitCircular); - - memset(ctx.dest_bitmap.addr, GColorRedARGB8, FRAMEBUFFER_SIZE_BYTES); - - GRect radial_container_rect = ctx.dest_bitmap.bounds; - radial_container_rect = grect_inset(radial_container_rect, GEdgeInsets(-10)); - const uint16_t inset_thickness = radial_container_rect.size.w / 4; - graphics_context_set_fill_color(&ctx, GColorGreen); - graphics_fill_radial(&ctx, radial_container_rect, GOvalScaleModeFillCircle, inset_thickness, 0, - TRIG_MAX_ANGLE / 2); - - cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("off_screen_right_aa_clipping"))); -}; diff --git a/tests/fw/graphics/test_framebuffer_duma.c b/tests/fw/graphics/test_framebuffer_duma.c index 403d4b2858..c5b0d61357 100644 --- a/tests/fw/graphics/test_framebuffer_duma.c +++ b/tests/fw/graphics/test_framebuffer_duma.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" diff --git a/tests/fw/graphics/test_gbitmap_data_row_info.c b/tests/fw/graphics/test_gbitmap_data_row_info.c index 2a87d9aec3..34658d9d48 100644 --- a/tests/fw/graphics/test_gbitmap_data_row_info.c +++ b/tests/fw/graphics/test_gbitmap_data_row_info.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_gbitmap_formats.c b/tests/fw/graphics/test_gbitmap_formats.c index c4e0cbb92f..928fe293ce 100644 --- a/tests/fw/graphics/test_gbitmap_formats.c +++ b/tests/fw/graphics/test_gbitmap_formats.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" @@ -31,44 +18,14 @@ ///////////////////////////// -const void * const g_gbitmap_spalding_data_row_infos = &g_gbitmap_spalding_data_row_infos; - void test_gbitmap_formats__create_blank(void) { const GSize s10 = GSize(10, 10); - const GSize s180 = GSize(180, 180); + const GSize s_full = GSize(DISP_COLS, DISP_ROWS); GBitmap *bmp = NULL; cl_assert((void*)&bmp->palette == (void*)&bmp->data_row_infos); // union with .palette - cl_assert(NULL != g_gbitmap_spalding_data_row_infos); // make sure unit-test fixture is ok - - -#ifdef PLATFORM_TINTIN - cl_assert(!process_manager_compiled_with_legacy2_sdk()); - bmp = gbitmap_create_blank(s10, GBitmapFormat1Bit); - cl_assert(NULL != bmp); - cl_assert(NULL == bmp->data_row_infos); - - bmp = gbitmap_create_blank(s10, GBitmapFormat8Bit); - cl_assert(NULL == bmp); - - bmp = gbitmap_create_blank(s10, GBitmapFormat1BitPalette); - cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette - - bmp = gbitmap_create_blank(s10, GBitmapFormat2BitPalette); - cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette - - bmp = gbitmap_create_blank(s10, GBitmapFormat4BitPalette); - cl_assert(NULL == bmp); - - bmp = gbitmap_create_blank(s10, GBitmapFormat8BitCircular); - cl_assert(NULL == bmp); - bmp = gbitmap_create_blank(s180, GBitmapFormat8BitCircular); - cl_assert(NULL == bmp); -#endif -#ifdef PLATFORM_SNOWY +#ifdef CONFIG_PLATFORM_GABBRO bmp = gbitmap_create_blank(s10, GBitmapFormat1Bit); cl_assert(NULL != bmp); cl_assert(NULL == bmp->data_row_infos); @@ -79,78 +36,32 @@ void test_gbitmap_formats__create_blank(void) { bmp = gbitmap_create_blank(s10, GBitmapFormat1BitPalette); cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette + cl_assert(g_gbitmap_data_row_infos != bmp->data_row_infos); // union with .palette bmp = gbitmap_create_blank(s10, GBitmapFormat2BitPalette); cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette + cl_assert(g_gbitmap_data_row_infos != bmp->data_row_infos); // union with .palette bmp = gbitmap_create_blank(s10, GBitmapFormat4BitPalette); cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette + cl_assert(g_gbitmap_data_row_infos != bmp->data_row_infos); // union with .palette bmp = gbitmap_create_blank(s10, GBitmapFormat8BitCircular); cl_assert(NULL == bmp); - bmp = gbitmap_create_blank(s180, GBitmapFormat8BitCircular); - cl_assert(NULL == bmp); -#endif -#ifdef PLATFORM_SPALDING - bmp = gbitmap_create_blank(s10, GBitmapFormat1Bit); - cl_assert(NULL != bmp); - cl_assert(NULL == bmp->data_row_infos); - - bmp = gbitmap_create_blank(s10, GBitmapFormat8Bit); - cl_assert(NULL != bmp); - cl_assert(NULL == bmp->data_row_infos); - - bmp = gbitmap_create_blank(s10, GBitmapFormat1BitPalette); - cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette - - bmp = gbitmap_create_blank(s10, GBitmapFormat2BitPalette); - cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette - - bmp = gbitmap_create_blank(s10, GBitmapFormat4BitPalette); + bmp = gbitmap_create_blank(s_full, GBitmapFormat8BitCircular); cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette - - bmp = gbitmap_create_blank(s10, GBitmapFormat8BitCircular); - cl_assert(NULL == bmp); - - bmp = gbitmap_create_blank(s180, GBitmapFormat8BitCircular); - cl_assert(NULL != bmp); - cl_assert(g_gbitmap_spalding_data_row_infos == bmp->data_row_infos); + cl_assert(g_gbitmap_data_row_infos == bmp->data_row_infos); #endif } void test_gbitmap_formats__create_blank_with_palette(void) { const GSize s10 = GSize(10, 10); - const GSize s180 = GSize(180, 180); + const GSize s_full = GSize(DISP_COLS, DISP_ROWS); GBitmap *bmp; GColor8 *p = (GColor8 *)&p; // some value to test against -#ifdef PLATFORM_TINTIN - cl_assert(!process_manager_compiled_with_legacy2_sdk()); - cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat1Bit, p, true)); - cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8Bit, p, true)); - - bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat1BitPalette, p, true); - cl_assert(NULL != bmp); - cl_assert(p == gbitmap_get_palette(bmp)); - - bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat2BitPalette, p, true); - cl_assert(NULL != bmp); - cl_assert(p == gbitmap_get_palette(bmp)); - - bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat4BitPalette, p, true); - cl_assert(NULL == bmp); - - cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8BitCircular, p, true)); - cl_assert(NULL == gbitmap_create_blank_with_palette(s180, GBitmapFormat8BitCircular, p, true)); -#endif -#ifdef PLATFORM_SNOWY +#ifdef CONFIG_PLATFORM_GABBRO cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat1Bit, p, true)); cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8Bit, p, true)); @@ -167,39 +78,19 @@ void test_gbitmap_formats__create_blank_with_palette(void) { cl_assert(p == gbitmap_get_palette(bmp)); cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8BitCircular, p, true)); - cl_assert(NULL == gbitmap_create_blank_with_palette(s180, GBitmapFormat8BitCircular, p, true)); -#endif -#ifdef PLATFORM_SPALDING - cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat1Bit, p, true)); - cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8Bit, p, true)); - - bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat1BitPalette, p, true); - cl_assert(NULL != bmp); - cl_assert(p == gbitmap_get_palette(bmp)); - - bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat2BitPalette, p, true); - cl_assert(NULL != bmp); - cl_assert(p == gbitmap_get_palette(bmp)); - - bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat4BitPalette, p, true); - cl_assert(NULL != bmp); - cl_assert(p == gbitmap_get_palette(bmp)); - - cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8BitCircular, p, true)); - cl_assert(NULL == gbitmap_create_blank_with_palette(s180, GBitmapFormat8BitCircular, p, true)); + cl_assert(NULL == gbitmap_create_blank_with_palette(s_full, GBitmapFormat8BitCircular, p, true)); #endif } void test_gbitmap_formats__display_framebuffer_bytes(void) { -#ifdef PLATFORM_TINTIN +#ifdef CONFIG_BOARD_FAMILY_ASTERIX const size_t expected = 20 * 168; // 20 * 8 == 144px + 2 bytes padding per scanline #endif -#ifdef PLATFORM_SNOWY - const size_t expected = 144 * 168; +#ifdef CONFIG_BOARD_FAMILY_OBELIX + const size_t expected = 200 * 228; #endif -#ifdef PLATFORM_SPALDING - // all pixels + 2*76 - const size_t expected = 25944; +#ifdef CONFIG_PLATFORM_GABBRO + const size_t expected = 260 * 260; #endif cl_assert_equal_i(expected, DISPLAY_FRAMEBUFFER_BYTES); } @@ -215,8 +106,6 @@ void test_gbitmap_formats__size_for_data(void) { cl_assert_equal_i( 0, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat8BitCircular)); const size_t expected = PBL_IF_RECT_ELSE(0, DISPLAY_FRAMEBUFFER_BYTES); - cl_assert_equal_i(expected, - prv_gbitmap_size_for_data(GSize(180, 180), GBitmapFormat8BitCircular)); cl_assert_equal_i(expected, prv_gbitmap_size_for_data(GSize(DISP_COLS, DISP_ROWS), GBitmapFormat8BitCircular)); } diff --git a/tests/fw/graphics/test_gbitmap_processor.c b/tests/fw/graphics/test_gbitmap_processor.c index f1f1823ffb..e1d69b24b2 100644 --- a/tests/fw/graphics/test_gbitmap_processor.c +++ b/tests/fw/graphics/test_gbitmap_processor.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_gbitmap_resource_validation.c b/tests/fw/graphics/test_gbitmap_resource_validation.c index 489b98e93d..10549a447e 100644 --- a/tests/fw/graphics/test_gbitmap_resource_validation.c +++ b/tests/fw/graphics/test_gbitmap_resource_validation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_gbitmap_sequence.c b/tests/fw/graphics/test_gbitmap_sequence.c index 2dd18553bc..824fe8a2ea 100644 --- a/tests/fw/graphics/test_gbitmap_sequence.c +++ b/tests/fw/graphics/test_gbitmap_sequence.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gbitmap_sequence.h" #include "applib/graphics/graphics.h" @@ -36,9 +23,9 @@ // http://opengameart.org/content/medals-3 // http://opengameart.org/content/open-pixel-platformer-tiles-sprites -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 #include "applib/graphics/8_bit/framebuffer.c" -#elif SCREEN_COLOR_DEPTH_BITS == 1 +#elif CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 #include "applib/graphics/1_bit/framebuffer.c" #endif @@ -129,7 +116,7 @@ void test_gbitmap_sequence__color_2bit_bouncing_ball(void) { // Result: // - gbitmaps matches platform decoded APNG void test_gbitmap_sequence__color_8bit_fight(void) { -#if PLATFORM_SPALDING +#ifdef CONFIG_BOARD_QEMU_GABBRO bool status = false; uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME); cl_assert(resource_id != UINT32_MAX); @@ -175,7 +162,7 @@ void test_gbitmap_sequence__color_8bit_fight(void) { // Result: // - gbitmaps matches platform decoded APNG void test_gbitmap_sequence__color_8bit_coin(void) { -#if PLATFORM_SPALDING +#ifdef CONFIG_BOARD_QEMU_GABBRO uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME); cl_assert(resource_id != UINT32_MAX); @@ -211,7 +198,7 @@ void test_gbitmap_sequence__color_8bit_coin(void) { // Result: // - gbitmaps matches platform decoded APNG void test_gbitmap_sequence__color_8bit_coin_round(void) { -#if PLATFORM_SPALDING +#ifdef CONFIG_BOARD_QEMU_GABBRO uint32_t resource_id = sys_resource_load_file_as_resource( TEST_IMAGES_PATH, "test_gbitmap_sequence__color_8bit_coin.apng"); cl_assert(resource_id != UINT32_MAX); @@ -245,7 +232,7 @@ void test_gbitmap_sequence__color_8bit_coin_round(void) { // Result: // - gbitmaps matches platform decoded APNG void test_gbitmap_sequence__color_8bit_bounds(void) { -#if PLATFORM_SPALDING +#ifdef CONFIG_BOARD_QEMU_GABBRO uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME); cl_assert(resource_id != UINT32_MAX); @@ -295,7 +282,7 @@ void test_gbitmap_sequence__color_8bit_bounds(void) { // Result: // - gbitmaps matches platform decoded APNG void test_gbitmap_sequence__color_8bit_yoshi(void) { -#if PLATFORM_SPALDING +#ifdef CONFIG_BOARD_QEMU_GABBRO bool status = false; uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME); cl_assert(resource_id != UINT32_MAX); diff --git a/tests/fw/graphics/test_gdraw_command.c b/tests/fw/graphics/test_gdraw_command.c index 85758a02e1..0c54276f5b 100644 --- a/tests/fw/graphics/test_gdraw_command.c +++ b/tests/fw/graphics/test_gdraw_command.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_gdraw_command_resources.c b/tests/fw/graphics/test_gdraw_command_resources.c index 61ce8f2467..d1458973e6 100644 --- a/tests/fw/graphics/test_gdraw_command_resources.c +++ b/tests/fw/graphics/test_gdraw_command_resources.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_gdraw_command_sequence.c b/tests/fw/graphics/test_gdraw_command_sequence.c index bf57de3541..19accf2989 100644 --- a/tests/fw/graphics/test_gdraw_command_sequence.c +++ b/tests/fw/graphics/test_gdraw_command_sequence.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_gdraw_command_transforms.c b/tests/fw/graphics/test_gdraw_command_transforms.c index 04588aff13..3ae3be62f7 100644 --- a/tests/fw/graphics/test_gdraw_command_transforms.c +++ b/tests/fw/graphics/test_gdraw_command_transforms.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_graphics.h b/tests/fw/graphics/test_graphics.h index ef446471f2..7a1e4c0912 100644 --- a/tests/fw/graphics/test_graphics.h +++ b/tests/fw/graphics/test_graphics.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fw/graphics/test_graphics_blending.c b/tests/fw/graphics/test_graphics_blending.c index e6ebe4e2c2..2cce852b7c 100644 --- a/tests/fw/graphics/test_graphics_blending.c +++ b/tests/fw/graphics/test_graphics_blending.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_graphics_circle.c b/tests/fw/graphics/test_graphics_circle.c index 3c0b8f597f..f009422a5b 100644 --- a/tests/fw/graphics/test_graphics_circle.c +++ b/tests/fw/graphics/test_graphics_circle.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics_circle.h" #include "util/trig.h" @@ -36,7 +23,7 @@ bool graphics_release_frame_buffer(GContext* ctx, GBitmap* buffer) { return true; } -void graphics_draw_pixel(){} +void graphics_draw_pixel(GContext* ctx, GPoint point) {} void graphics_fill_rect(GContext* ctx, const GRect *rect) {} void graphics_private_draw_horizontal_line(){} void graphics_private_draw_vertical_line(){} diff --git a/tests/fw/graphics/test_graphics_colors.c b/tests/fw/graphics/test_graphics_colors.c index 6e20cd66d7..4fa2469012 100644 --- a/tests/fw/graphics/test_graphics_colors.c +++ b/tests/fw/graphics/test_graphics_colors.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" diff --git a/tests/fw/graphics/test_graphics_context.template.c b/tests/fw/graphics/test_graphics_context.template.c index b4bd4d239a..e14e208903 100644 --- a/tests/fw/graphics/test_graphics_context.template.c +++ b/tests/fw/graphics/test_graphics_context.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" #include "applib/graphics/graphics.h" @@ -681,7 +668,7 @@ void test_graphics_context_${BIT_DEPTH_NAME}__fill_antialiased(void) { setup_test(&context, true, 5, GColorBlack, GColorBlack, false); graphics_fill_rect(&context, &GRect(10, 20, 40, 10)); -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 ASSERT_CALLED(prv_fill_rect_non_aa(&context, &GRect(10, 20, 40, 10), 0, GCornerNone, GColorBlack)); #else ASSERT_CALLED(prv_fill_rect_aa(&context, &GRect(10, 20, 40, 10), 0, GCornerNone, GColorBlack)); @@ -697,7 +684,7 @@ void test_graphics_context_${BIT_DEPTH_NAME}__fill_antialiased(void) { setup_test(&context, true, 5, GColorBlack, GColorBlack, false); graphics_fill_round_rect(&context, &GRect(20, 80, 40, 10), 4, GCornersAll); -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 ASSERT_CALLED(prv_fill_rect_non_aa(&context, &GRect(20, 80, 40, 10), 4, GCornersAll, GColorBlack)); #else ASSERT_CALLED(prv_fill_rect_aa(&context, &GRect(20, 80, 40, 10), 4, GCornersAll, GColorBlack)); diff --git a/tests/fw/graphics/test_graphics_context_mask.c b/tests/fw/graphics/test_graphics_context_mask.c deleted file mode 100644 index a100b43b06..0000000000 --- a/tests/fw/graphics/test_graphics_context_mask.c +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/graphics/graphics.h" -#include "applib/graphics/graphics_private.h" -#include "applib/graphics/graphics_private_raw.h" -#include "applib/graphics/graphics_private_raw_mask.h" -#include "applib/graphics/gtypes.h" -#include "applib/graphics/framebuffer.h" -#include "applib/ui/window_private.h" -#include "applib/ui/layer.h" -#include "util/graphics.h" -#include "util/trig.h" - -#include "clar.h" -#include "util.h" - -#include - -// Helper Includes -//////////////////////////////////// -#include "test_graphics.h" -#include "test_graphics_mask.h" -#include "8bit/test_framebuffer.h" - -// Stubs -//////////////////////////////////// -#include "graphics_common_stubs.h" - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Setup and Teardown -//////////////////////////////////////////////////////////////////////////////////////////////////// - -static FrameBuffer *s_fb = NULL; -static GContext *s_ctx = NULL; -static GBitmap *s_dest_bitmap = NULL; - -// Setup -void test_graphics_context_mask__initialize(void) { - s_fb = malloc(sizeof(*s_fb)); - s_ctx = malloc(sizeof(*s_ctx)); - - framebuffer_init(s_fb, &(GSize) {DISP_COLS, DISP_ROWS}); - test_graphics_context_init(s_ctx, s_fb); - framebuffer_clear(s_fb); -} - -void test_graphics_context_mask__cleanup(void) { - free(s_ctx); - free(s_fb); - - gbitmap_destroy(s_dest_bitmap); - s_dest_bitmap = NULL; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Helpers -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////// -// COMMON HELPERS // -/////////////////////////////////////// - -#define CHECK_EXPECTED_TEST_IMAGE(ctx) (cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, TEST_PBI_FILE))) - -static const int16_t num_pixels_per_mask_value = 5; -static const int16_t num_mask_values = 4; -static const int16_t num_src_colors = 256; // 8-bit color -static const int16_t num_dest_colors = 64; // Framebuffer ignores alpha, so only 6-bit color - -static void prv_prepare_canvas(GContext *ctx, GSize desired_size) { - s_dest_bitmap = gbitmap_create_blank(desired_size, GBitmapFormat8Bit); - - ctx->dest_bitmap = *s_dest_bitmap; - ctx->draw_state.clip_box.size = desired_size; - ctx->draw_state.drawing_box.size = desired_size; -} - -/////////////////////////////////////// -// RECORDING HORIZONTAL LINE HELPERS // -/////////////////////////////////////// - -static void prv_prepare_canvas_for_hline_recording_test(GContext *ctx) { - const GSize bitmap_size = GSize(num_mask_values * num_pixels_per_mask_value, num_src_colors); - prv_prepare_canvas(ctx, bitmap_size); -} - -static GDrawMask *prv_create_mask_for_hline_recording_test(GContext *ctx) { - // The initial transparency doesn't really matter since we're about to overwrite it - const bool transparent = false; - GDrawMask *mask = graphics_context_mask_create(ctx, transparent); - cl_assert(mask); - - const GSize mask_size = ctx->dest_bitmap.bounds.size; - for (int16_t x = 0; x < mask_size.w; x++) { - const uint8_t mask_pixel_value = (uint8_t)(x / num_pixels_per_mask_value); - for (int16_t y = 0; y < mask_size.h; y++) { - test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value, - GPoint(x, y)); - } - } - - return mask; -} - -typedef void (*HorizontalClippingMaskRecordFunc)(GContext *ctx, int16_t y, int16_t x1, int16_t x2, - GColor color); - -static void prv_mask_record_hline_test_pattern(HorizontalClippingMaskRecordFunc record_func) { - GContext *ctx = s_ctx; - - prv_prepare_canvas_for_hline_recording_test(ctx); - - graphics_context_set_antialiased(ctx, true); - - GDrawMask *mask = prv_create_mask_for_hline_recording_test(ctx); - - cl_assert(graphics_context_mask_record(ctx, mask)); - - for (int16_t y = 0; y < num_src_colors; y++) { - GColor src_color = (GColor) { .argb = (uint8_t)y }; - for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) { - int16_t x1 = (int16_t)(mask_value_index * num_pixels_per_mask_value); - int16_t x2 = (int16_t)(x1 + num_pixels_per_mask_value - 1); - record_func(ctx, y, x1, x2, src_color); - } - } - - cl_assert(graphics_context_mask_record(ctx, NULL)); - - test_graphics_context_mask_debug(ctx, mask); - - graphics_context_mask_destroy(ctx, mask); -} - -/////////////////////////////////////// -// RECORDING VERTICAL LINE HELPERS // -/////////////////////////////////////// - -static void prv_prepare_canvas_for_vline_recording_test(GContext *ctx) { - const GSize bitmap_size = GSize(num_src_colors, num_mask_values * num_pixels_per_mask_value); - prv_prepare_canvas(ctx, bitmap_size); -} - -static GDrawMask *prv_create_mask_for_vline_recording_test(GContext *ctx) { - // The initial transparency doesn't really matter since we're about to overwrite it - const bool transparent = false; - GDrawMask *mask = graphics_context_mask_create(ctx, transparent); - cl_assert(mask); - - const GSize mask_size = ctx->dest_bitmap.bounds.size; - for (int16_t y = 0; y < mask_size.h; y++) { - const uint8_t mask_pixel_value = (uint8_t)(y / num_pixels_per_mask_value); - for (int16_t x = 0; x < mask_size.w; x++) { - test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value, - GPoint(x, y)); - } - } - - return mask; -} - -typedef void (*VerticalClippingMaskRecordFunc)(GContext *ctx, int16_t x, int16_t y1, int16_t y2, - GColor color); - -static void prv_mask_record_vline_test_pattern(VerticalClippingMaskRecordFunc record_func) { - GContext *ctx = s_ctx; - - prv_prepare_canvas_for_vline_recording_test(ctx); - - graphics_context_set_antialiased(ctx, true); - - GDrawMask *mask = prv_create_mask_for_vline_recording_test(ctx); - - cl_assert(graphics_context_mask_record(ctx, mask)); - - for (int16_t x = 0; x < num_src_colors; x++) { - GColor src_color = (GColor) { .argb = (uint8_t)x }; - for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) { - int16_t y1 = (int16_t)(mask_value_index * num_pixels_per_mask_value); - int16_t y2 = (int16_t)(y1 + num_pixels_per_mask_value - 1); - record_func(ctx, x, y1, y2, src_color); - } - } - - cl_assert(graphics_context_mask_record(ctx, NULL)); - - test_graphics_context_mask_debug(ctx, mask); - - graphics_context_mask_destroy(ctx, mask); -} - -/////////////////////////////////////// -// APPLYING HORIZONTAL LINE HELPERS // -/////////////////////////////////////// - -static const int16_t hline_applying_test_column_width = num_mask_values * num_pixels_per_mask_value; - -static void prv_prepare_canvas_for_hline_applying_test(GContext *ctx) { - const GSize bitmap_size = GSize(num_dest_colors * hline_applying_test_column_width, - num_src_colors); - prv_prepare_canvas(ctx, bitmap_size); - - // Fill the canvas so each column (of width hline_applying_test_column_width) is set to one of the - // different possible dest_colors - for (int16_t y = 0; y < bitmap_size.h; y++) { - for (int16_t column_index = 0; column_index < num_dest_colors; column_index++) { - const int16_t starting_x = column_index * hline_applying_test_column_width; - for (int16_t x = starting_x; x < starting_x + hline_applying_test_column_width; x++) { - ctx->draw_state.stroke_color = (GColor) { .argb = (uint8_t)(column_index | 0b11000000) }; - graphics_draw_pixel(ctx, GPoint(x, y)); - } - } - } -} - -static GDrawMask *prv_create_mask_for_hline_applying_test(GContext *ctx) { - // The initial transparency doesn't really matter since we're about to overwrite it - const bool transparent = false; - GDrawMask *mask = graphics_context_mask_create(ctx, transparent); - cl_assert(mask); - - const GSize mask_size = ctx->dest_bitmap.bounds.size; - for (int16_t x = 0; x < mask_size.w; x++) { - const uint8_t mask_pixel_value = (uint8_t)((x % hline_applying_test_column_width) / - num_pixels_per_mask_value); - for (int16_t y = 0; y < mask_size.h; y++) { - test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value, - GPoint(x, y)); - } - } - - return mask; -} - -typedef void (*HorizontalClippingMaskApplyFunc)(GContext *ctx, int16_t y, int16_t x1, int16_t x2, - GColor color); - -static void prv_mask_apply_hline_test_pattern(HorizontalClippingMaskApplyFunc apply_func) { - GContext *ctx = s_ctx; - - prv_prepare_canvas_for_hline_applying_test(ctx); - - graphics_context_set_antialiased(ctx, true); - - GDrawMask *mask = prv_create_mask_for_hline_applying_test(ctx); - - cl_assert(graphics_context_mask_use(ctx, mask)); - - for (int16_t y = 0; y < num_src_colors; y++) { - GColor src_color = (GColor) { .argb = (uint8_t)y }; - for (int dest_color_index = 0; dest_color_index < num_dest_colors; dest_color_index++) { - for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) { - int16_t x1 = (int16_t)((dest_color_index * hline_applying_test_column_width) + - (mask_value_index * num_pixels_per_mask_value)); - int16_t x2 = (int16_t)(x1 + num_pixels_per_mask_value - 1); - apply_func(ctx, y, x1, x2, src_color); - } - } - } - - graphics_context_mask_destroy(ctx, mask); -} - -////////////////////////////////////// -// APPLYING VERTICAL LINE HELPERS // -////////////////////////////////////// - -static const int16_t vline_applying_test_row_height = hline_applying_test_column_width; - -static void prv_prepare_canvas_for_vline_applying_test(GContext *ctx) { - const GSize bitmap_size = GSize(num_src_colors, - num_dest_colors * hline_applying_test_column_width); - prv_prepare_canvas(ctx, bitmap_size); - - // Fill the canvas so each row (of width vline_applying_test_row_height) is set to one of the - // different possible dest_colors - for (int16_t x = 0; x < bitmap_size.w; x++) { - for (int16_t row_index = 0; row_index < num_dest_colors; row_index++) { - const int16_t starting_y = row_index * vline_applying_test_row_height; - for (int16_t y = starting_y; y < starting_y + vline_applying_test_row_height; y++) { - ctx->draw_state.stroke_color = (GColor) { .argb = (uint8_t)(row_index | 0b11000000) }; - graphics_draw_pixel(ctx, GPoint(x, y)); - } - } - } -} - -static GDrawMask *prv_create_mask_for_vline_applying_test(GContext *ctx) { - // The initial transparency doesn't really matter since we're about to overwrite it - const bool transparent = false; - GDrawMask *mask = graphics_context_mask_create(ctx, transparent); - cl_assert(mask); - - const GSize mask_size = ctx->dest_bitmap.bounds.size; - for (int16_t y = 0; y < mask_size.h; y++) { - const uint8_t mask_pixel_value = (uint8_t)((y % vline_applying_test_row_height) / - num_pixels_per_mask_value); - for (int16_t x = 0; x < mask_size.w; x++) { - test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value, - GPoint(x, y)); - } - } - - return mask; -} - -typedef void (*VerticalClippingMaskApplyFunc)(GContext *ctx, int16_t x, int16_t y1, int16_t y2, - GColor color); - -static void prv_mask_apply_vline_test_pattern(VerticalClippingMaskApplyFunc apply_func) { - GContext *ctx = s_ctx; - - prv_prepare_canvas_for_vline_applying_test(ctx); - - graphics_context_set_antialiased(ctx, true); - - GDrawMask *mask = prv_create_mask_for_vline_applying_test(ctx); - - cl_assert(graphics_context_mask_use(ctx, mask)); - - for (int16_t x = 0; x < num_src_colors; x++) { - GColor src_color = (GColor) { .argb = (uint8_t)x }; - for (int dest_color_index = 0; dest_color_index < num_dest_colors; dest_color_index++) { - for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) { - int16_t y1 = (int16_t)((dest_color_index * vline_applying_test_row_height) + - (mask_value_index * num_pixels_per_mask_value)); - int16_t y2 = (int16_t)(y1 + num_pixels_per_mask_value - 1); - apply_func(ctx, x, y1, y2, src_color); - } - } - } - - graphics_context_mask_destroy(ctx, mask); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Tests -//////////////////////////////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////// -// RECORDING HORIZONTAL LINE TESTS // -///////////////////////////////////// - -// These tests initialize a mask that has 4 columns, each of width `num_pixels_per_mask_value`. -// Each of these columns in the mask is initialized to have one of the 4 possible mask values. Then, -// the test iterates over the mask's 256 rows, each of which uses a different src color -// (256 possibilities for 8-bit color) to draw a horizontal line (using the test's specified raw -// drawing function) for each column into the mask (recording). - -void prv_mask_recording_assign_horizontal_line(GContext *ctx, int16_t y, Fixed_S16_3 x1, - Fixed_S16_3 x2, GColor color); - -static void prv_hline_pattern_record_assign_horizontal_line_raw(GContext *ctx, int16_t y, - int16_t x1, int16_t x2, - GColor color) { - // x1 and x2 here will be the integer start/end of the line, so we need to adjust them so we see - // the same blending on the first and last pixel - x2--; - const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { - .integer = x1, - .fraction = 4, - }; - const Fixed_S16_3 x2_fixed = (Fixed_S16_3) { - .integer = x2, - .fraction = 4, - }; - prv_mask_recording_assign_horizontal_line(ctx, y, x1_fixed, x2_fixed, color); -} - -void test_graphics_context_mask__record_assign_horizontal_line_raw(void) { - prv_mask_record_hline_test_pattern(prv_hline_pattern_record_assign_horizontal_line_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -void prv_mask_recording_blend_horizontal_line_raw(GContext *ctx, int16_t y, int16_t x1, - int16_t x2, GColor color); - -void test_graphics_context_mask__record_blend_horizontal_line_raw(void) { - prv_mask_record_hline_test_pattern(prv_mask_recording_blend_horizontal_line_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -void prv_mask_recording_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y, - Fixed_S16_3 x1, Fixed_S16_3 x2, - uint8_t left_aa_offset, - uint8_t right_aa_offset, - int16_t clip_box_min_x, - int16_t clip_box_max_x, - GColor color); - -static void prv_hline_pattern_record_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y, - int16_t x1, int16_t x2, - GColor color) { - const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { .integer = x1 }; - Fixed_S16_3 x2_fixed = (Fixed_S16_3) { .integer = x2 }; - const uint8_t gradient_width = (uint8_t)((x2 - x1) / 2); - x2_fixed.integer -= gradient_width; - - const int16_t clip_box_min_x = ctx->draw_state.clip_box.origin.x; - const int16_t clip_box_max_x = (int16_t)(grect_get_max_x(&ctx->draw_state.clip_box) - 1); - prv_mask_recording_assign_horizontal_line_delta_raw(ctx, y, x1_fixed, x2_fixed, gradient_width, - gradient_width, clip_box_min_x, - clip_box_max_x, color); -} - -void test_graphics_context_mask__record_assign_horizontal_line_delta_raw(void) { - prv_mask_record_hline_test_pattern(prv_hline_pattern_record_assign_horizontal_line_delta_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -///////////////////////////////////// -// RECORDING VERTICAL LINE TESTS // -///////////////////////////////////// - -// These tests initialize a mask that has 4 rows, each of height `num_pixels_per_mask_value`. -// Each of these rows in the mask is initialized to have one of the 4 possible mask values. Then, -// the test iterates over the mask's 256 columns, each of which uses a different src color -// (256 possibilities for 8-bit color) to draw a vertical line (using the test's specified raw -// drawing function) for each row into the mask (recording). - -void prv_mask_recording_assign_vertical_line(GContext *ctx, int16_t x, Fixed_S16_3 y1, - Fixed_S16_3 y2, GColor color); - -static void prv_vline_pattern_record_assign_vertical_line(GContext *ctx, int16_t x, int16_t y1, - int16_t y2, GColor color) { - // y1 and y2 here will be the integer start/end of the line, so we need to adjust them so we see - // the same blending on the first and last pixel - y2--; - const Fixed_S16_3 y1_fixed = (Fixed_S16_3) { - .integer = y1, - .fraction = 4, - }; - const Fixed_S16_3 y2_fixed = (Fixed_S16_3) { - .integer = y2, - .fraction = 4, - }; - prv_mask_recording_assign_vertical_line(ctx, x, y1_fixed, y2_fixed, color); -} - -void test_graphics_context_mask__record_assign_vertical_line_raw(void) { - prv_mask_record_vline_test_pattern(prv_vline_pattern_record_assign_vertical_line); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -void prv_mask_recording_blend_vertical_line_raw(GContext *ctx, int16_t x, int16_t y1, int16_t y2, - GColor color); - -void test_graphics_context_mask__record_blend_vertical_line_raw(void) { - prv_mask_record_vline_test_pattern(prv_mask_recording_blend_vertical_line_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -///////////////////////////////////// -// APPLYING HORIZONTAL LINE TESTS // -///////////////////////////////////// - -// These tests initialize a mask that has 4 * 64 columns, each of width `num_pixels_per_mask_value`. -// Each quad grouping of these columns in the mask is initialized to have one of the 4 possible mask -// values. Additionally, the test initializes the framebuffer to have 64 columns, each of width -// `4 * num_pixels_per_mask_value`, where each column is one of the 64 possible dest_colors (the -// framebuffer ignores alpha so 6-bit color). Then, the test activates the mask and iterates over -// the framebuffer's 256 rows, each of which uses a different src color -// (256 possibilities for 8-bit color) to draw a horizontal line (using the test's specified raw -// drawing function) for each `num_pixels_per_mask_value`-wide column. - -void prv_assign_horizontal_line_raw(GContext *ctx, int16_t y, Fixed_S16_3 x1, Fixed_S16_3 x2, - GColor color); - -static void prv_hline_pattern_apply_assign_horizontal_line_raw(GContext *ctx, int16_t y, - int16_t x1, int16_t x2, - GColor color) { - // x1 and x2 here will be the integer start/end of the line, so we need to adjust them so we see - // the same blending on the first and last pixel - x2--; - const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { - .integer = x1, - .fraction = 4, - }; - const Fixed_S16_3 x2_fixed = (Fixed_S16_3) { - .integer = x2, - .fraction = 4, - }; - prv_assign_horizontal_line_raw(ctx, y, x1_fixed, x2_fixed, color); -} - -void test_graphics_context_mask__apply_assign_horizontal_line_raw(void) { - prv_mask_apply_hline_test_pattern(prv_hline_pattern_apply_assign_horizontal_line_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -void prv_blend_horizontal_line_raw(GContext *ctx, int16_t y, int16_t x1, int16_t x2, - GColor color); - -void test_graphics_context_mask__apply_blend_horizontal_line_raw(void) { - prv_mask_apply_hline_test_pattern(prv_blend_horizontal_line_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -void prv_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y, - Fixed_S16_3 x1, Fixed_S16_3 x2, - uint8_t left_aa_offset, uint8_t right_aa_offset, - int16_t clip_box_min_x, int16_t clip_box_max_x, - GColor color); - -static void prv_hline_pattern_apply_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y, - int16_t x1, int16_t x2, - GColor color) { - // FIXME PBL-34552: This test produces an incorrect image, see JIRA - const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { .integer = x1 }; - Fixed_S16_3 x2_fixed = (Fixed_S16_3) { .integer = x2 }; - const uint8_t gradient_width = (uint8_t)((x2 - x1) / 2); - x2_fixed.integer -= gradient_width; - - const int16_t clip_box_min_x = ctx->draw_state.clip_box.origin.x; - const int16_t clip_box_max_x = (int16_t)(grect_get_max_x(&ctx->draw_state.clip_box) - 1); - prv_assign_horizontal_line_delta_raw(ctx, y, x1_fixed, x2_fixed, gradient_width, gradient_width, - clip_box_min_x, clip_box_max_x, color); -} - -void test_graphics_context_mask__apply_assign_horizontal_line_delta_raw(void) { - prv_mask_apply_hline_test_pattern(prv_hline_pattern_apply_assign_horizontal_line_delta_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -////////////////////////////////// -// APPLYING VERTICAL LINE TESTS // -////////////////////////////////// - -// These tests initialize a mask that has 4 * 64 rows, each of height -// `vline_applying_test_row_height`. Each quad grouping of these rows in the mask is initialized to -// have one of the 4 possible mask values. Additionally, the test initializes the framebuffer to -// have 64 rows, each of height `4 * num_pixels_per_mask_value`, where each row is one of the 64 -// possible dest_colors (the framebuffer ignores alpha so 6-bit color). Then, the test activates the -// mask and iterates over the framebuffer's 256 columns, each of which uses a different src color -// (256 possibilities for 8-bit color) to draw a vertical line (using the test's specified raw -// drawing function) for each `num_pixels_per_mask_value`-tall row. - -void prv_assign_vertical_line_raw(GContext *ctx, int16_t x, Fixed_S16_3 y1, Fixed_S16_3 y2, - GColor color); - -static void prv_vline_pattern_apply_assign_vertical_line(GContext *ctx, int16_t x, int16_t y1, - int16_t y2, GColor color) { - // y1 and y2 here will be the integer start/end of the line, so we need to adjust them so we see - // the same blending on the first and last pixel - y2--; - const Fixed_S16_3 y1_fixed = (Fixed_S16_3) { - .integer = y1, - .fraction = 4, - }; - const Fixed_S16_3 y2_fixed = (Fixed_S16_3) { - .integer = y2, - .fraction = 4, - }; - prv_assign_vertical_line_raw(ctx, x, y1_fixed, y2_fixed, color); -} - -void test_graphics_context_mask__apply_assign_vertical_line_raw(void) { - prv_mask_apply_vline_test_pattern(prv_vline_pattern_apply_assign_vertical_line); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -void prv_blend_vertical_line_raw(GContext *ctx, int16_t x, int16_t y1, int16_t y2, GColor color); - -void test_graphics_context_mask__apply_blend_vertical_line_raw(void) { - // FIXME PBL-34141: This test produces an incorrect image, see JIRA - prv_mask_apply_vline_test_pattern(prv_blend_vertical_line_raw); - CHECK_EXPECTED_TEST_IMAGE(s_ctx); -}; - -///////////////// -// BASIC TESTS // -///////////////// - -// These tests test the basic functionality of the clipping mask API. - -static void prv_verify_mask_pixel_values(const GContext *ctx, const GDrawMask *mask, - uint8_t expected_value) { - // Assumes rectangular framebuffer - cl_assert(ctx->dest_bitmap.info.format == GBitmapFormat8Bit); - - const GSize framebuffer_size = ctx->dest_bitmap.bounds.size; - - for (int16_t x = 0; x < framebuffer_size.w; x++) { - for (int16_t y = 0; y < framebuffer_size.h; y++) { - cl_assert_equal_i(test_graphics_context_mask_get_value_for_coordinate(ctx, mask, - GPoint(x, y)), - expected_value); - } - } -} - -void test_graphics_context_mask__basic_create(void) { - GContext *ctx = s_ctx; - - // Should be safe to call create with NULL values - cl_assert_equal_p(graphics_context_mask_create(NULL, NULL), NULL); - - GDrawMask *transparent_mask = graphics_context_mask_create(ctx, true /* transparent */); - cl_assert(transparent_mask); - // Verify all mask pixels are initialized to be transparent (0) - prv_verify_mask_pixel_values(ctx, transparent_mask, 0); - graphics_context_mask_destroy(ctx, transparent_mask); - - GDrawMask *opaque_mask = graphics_context_mask_create(ctx, false /* transparent */); - cl_assert(opaque_mask); - // Verify all mask pixels are initialized to be opaque (3) - prv_verify_mask_pixel_values(ctx, opaque_mask, 3); - graphics_context_mask_destroy(ctx, opaque_mask); -} - -//extern const GDrawRawImplementation g_mask_recording_draw_implementation; - -void test_graphics_context_mask__basic_record(void) { - GContext *ctx = s_ctx; - - // Should be safe to call record with NULL values, will return false - cl_assert(!graphics_context_mask_record(NULL, NULL)); - - // Should start with default draw implementation - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - - GDrawMask *mask1 = graphics_context_mask_create(ctx, true /* transparent */); - cl_assert(mask1); - cl_assert(graphics_context_mask_record(ctx, mask1)); - // Should have switched to mask recording draw implementation - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_mask_recording_draw_implementation); - // Should have attached mask1 to GContext - cl_assert_equal_p(ctx->draw_state.draw_mask, mask1); - - GDrawMask *mask2 = graphics_context_mask_create(ctx, true /* transparent */); - cl_assert(mask2); - cl_assert(graphics_context_mask_record(ctx, mask2)); - // Should still be on mask recording draw implementation - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_mask_recording_draw_implementation); - // Should now have mask2 attached to GContext - cl_assert_equal_p(ctx->draw_state.draw_mask, mask2); - - // Calling record with NULL should reset draw impl to default and mask in GContext to NULL - cl_assert(graphics_context_mask_record(ctx, NULL)); - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - cl_assert_equal_p(ctx->draw_state.draw_mask, NULL); - - graphics_context_mask_destroy(ctx, mask1); - graphics_context_mask_destroy(ctx, mask2); -} - -void test_graphics_context_mask__basic_use(void) { - GContext *ctx = s_ctx; - - // Should be safe to call use with NULL values, will return false - cl_assert(!graphics_context_mask_use(NULL, NULL)); - - // Should start with default draw implementation - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - - GDrawMask *mask1 = graphics_context_mask_create(ctx, true /* transparent */); - cl_assert(mask1); - cl_assert(graphics_context_mask_use(ctx, mask1)); - // Should still be on default draw implementation - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - // Should have attached mask1 to GContext - cl_assert_equal_p(ctx->draw_state.draw_mask, mask1); - - GDrawMask *mask2 = graphics_context_mask_create(ctx, true /* transparent */); - cl_assert(mask2); - cl_assert(graphics_context_mask_use(ctx, mask2)); - // Should still be on default draw implementation - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - // Should now have mask2 attached to GContext - cl_assert_equal_p(ctx->draw_state.draw_mask, mask2); - - // Calling use with NULL should reset draw impl to default and mask in GContext to NULL - cl_assert(graphics_context_mask_use(ctx, NULL)); - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - cl_assert_equal_p(ctx->draw_state.draw_mask, NULL); - - graphics_context_mask_destroy(ctx, mask1); - graphics_context_mask_destroy(ctx, mask2); -} - -void test_graphics_context_mask__basic_destroy(void) { - // Should be safe to call destroy on NULL values - graphics_context_mask_destroy(NULL, NULL); -} diff --git a/tests/fw/graphics/test_graphics_draw_bitmap.c b/tests/fw/graphics/test_graphics_draw_bitmap.c index ca4224f3d3..4ff669fdbd 100644 --- a/tests/fw/graphics/test_graphics_draw_bitmap.c +++ b/tests/fw/graphics/test_graphics_draw_bitmap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -30,12 +17,12 @@ #include "test_graphics.h" #include "../graphics/util.h" -#if (SCREEN_COLOR_DEPTH_BITS == 1) +#if (CONFIG_SCREEN_COLOR_DEPTH_BITS == 1) #include "1bit/test_framebuffer.h" -#elif (SCREEN_COLOR_DEPTH_BITS == 8) +#elif (CONFIG_SCREEN_COLOR_DEPTH_BITS == 8) #include "8bit/test_framebuffer.h" #else -#error "Unrecognized SCREEN_COLOR_DEPTH_BITS" +#error "Unrecognized CONFIG_SCREEN_COLOR_DEPTH_BITS" #endif diff --git a/tests/fw/graphics/test_graphics_draw_circle.template.c b/tests/fw/graphics/test_graphics_draw_circle.template.c index 1ccfd0120c..3906f2588a 100644 --- a/tests/fw/graphics/test_graphics_draw_circle.template.c +++ b/tests/fw/graphics/test_graphics_draw_circle.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/graphics_circle_private.h" diff --git a/tests/fw/graphics/test_graphics_draw_core.c b/tests/fw/graphics/test_graphics_draw_core.c index 154b8b57e9..07736c3cd0 100644 --- a/tests/fw/graphics/test_graphics_draw_core.c +++ b/tests/fw/graphics/test_graphics_draw_core.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/graphics_private.h" diff --git a/tests/fw/graphics/test_graphics_draw_implementation.c b/tests/fw/graphics/test_graphics_draw_implementation.c index a8c799836c..12875b091b 100644 --- a/tests/fw/graphics/test_graphics_draw_implementation.c +++ b/tests/fw/graphics/test_graphics_draw_implementation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/graphics_private.h" diff --git a/tests/fw/graphics/test_graphics_draw_line.c b/tests/fw/graphics/test_graphics_draw_line.c index c1011ff7c8..2f0ca011cd 100644 --- a/tests/fw/graphics/test_graphics_draw_line.c +++ b/tests/fw/graphics/test_graphics_draw_line.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -30,7 +17,7 @@ //////////////////////////////////// #include "test_graphics.h" -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 #include "8bit/test_framebuffer.h" #else #include "1bit/test_framebuffer.h" @@ -189,7 +176,7 @@ void test_graphics_draw_line__clear(void) { TEST_NAMED_PBI_FILE("draw_line_inside_origin_layer"))); layer_set_update_proc(&layer, &clear_layer_update_callback); layer_render_tree(&layer, &ctx); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_clear"))); #else cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite)); @@ -859,7 +846,7 @@ void test_graphics_draw_line__same_point(void) { draw_lines_same_point(&ctx); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_same_point"))); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 graphics_context_set_stroke_color(&ctx, GColorRed); draw_lines_same_point(&ctx); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_same_point_color"))); diff --git a/tests/fw/graphics/test_graphics_draw_pixel.template.c b/tests/fw/graphics/test_graphics_draw_pixel.template.c index 8546403ae8..f01339a882 100644 --- a/tests/fw/graphics/test_graphics_draw_pixel.template.c +++ b/tests/fw/graphics/test_graphics_draw_pixel.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -200,7 +187,7 @@ void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__clear(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_inside_origin_layer.${BIT_DEPTH_NAME}.pbi")); layer_set_update_proc(&layer, &clear_layer_update_callback); layer_render_tree(&layer, &ctx); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_clear.8bit.pbi")); #else cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite)); diff --git a/tests/fw/graphics/test_graphics_draw_rect.template.c b/tests/fw/graphics/test_graphics_draw_rect.template.c index 35f2769733..aede09c74e 100644 --- a/tests/fw/graphics/test_graphics_draw_rect.template.c +++ b/tests/fw/graphics/test_graphics_draw_rect.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -218,7 +205,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 4 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4); @@ -249,7 +236,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 11 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); @@ -312,7 +299,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 4 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 4); @@ -343,7 +330,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 11 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11); @@ -413,7 +400,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 4 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 4); @@ -444,7 +431,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 11 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 11); @@ -507,7 +494,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 4 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 4); @@ -538,7 +525,7 @@ void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 11 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 11); @@ -661,7 +648,7 @@ static void prv_draw_dither_rects(GContext* ctx, GColor8 fill_color) { } void test_graphics_draw_rect_${BIT_DEPTH_NAME}__dithering_gray(void) { -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 GContext ctx; prv_draw_dither_rects(&ctx, GColorLightGray); prv_draw_dither_rects(&ctx, GColorDarkGray); diff --git a/tests/fw/graphics/test_graphics_draw_rotated_bitmap.c b/tests/fw/graphics/test_graphics_draw_rotated_bitmap.c index aa17147d53..c1f1666981 100644 --- a/tests/fw/graphics/test_graphics_draw_rotated_bitmap.c +++ b/tests/fw/graphics/test_graphics_draw_rotated_bitmap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" @@ -31,7 +18,7 @@ // Helper Functions //////////////////////////////////// #include "test_graphics.h" -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 #include "1bit/test_framebuffer.h" #else #include "8bit/test_framebuffer.h" @@ -52,9 +39,9 @@ static GBitmap *test_image_bw; static GBitmap *test_image_color; static FrameBuffer *fb = NULL; -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 extern bool get_bitmap_bit(GBitmap *bmp, int x, int y); -#elif SCREEN_COLOR_DEPTH_BITS == 8 +#elif CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 extern GColor get_bitmap_color(GBitmap *bmp, int x, int y); #endif @@ -107,13 +94,13 @@ static void setup_test_rotate_bitmap(GContext *ctx, FrameBuffer *fb, // Tests //////////////////////////////////// void test_graphics_draw_rotated_bitmap__get_color(void) { -#if SCREEN_COLOR_DEPTH_BITS == 1 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 1 cl_check(get_bitmap_bit(test_image_bw, 8, 16) == 1); cl_check(get_bitmap_bit(test_image_bw, 8, 24) == 0); cl_check(get_bitmap_bit(test_image_color, 30, 2) == 0); cl_check(get_bitmap_bit(test_image_color, 30, 10) == 0); cl_check(get_bitmap_bit(test_image_color, 30, 30) == 1); -#elif SCREEN_COLOR_DEPTH_BITS == 8 +#elif CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gcolor_equal(get_bitmap_color(test_image_bw, 8, 16), GColorWhite)); cl_check(gcolor_equal(get_bitmap_color(test_image_bw, 8, 24), GColorBlack)); cl_check(gcolor_equal(get_bitmap_color(test_image_color, 30, 2), GColorClear)); diff --git a/tests/fw/graphics/test_graphics_draw_round_rect.template.c b/tests/fw/graphics/test_graphics_draw_round_rect.template.c index d69c356991..d15748bdec 100644 --- a/tests/fw/graphics/test_graphics_draw_round_rect.template.c +++ b/tests/fw/graphics/test_graphics_draw_round_rect.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -173,7 +160,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__clear(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi")); layer_set_update_proc(&layer, &clear_layer_update_callback); layer_render_tree(&layer, &ctx); -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_clear.8bit.pbi")); #else cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite)); @@ -195,7 +182,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) { test_graphics_context_init(&ctx, fb); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 1 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); @@ -226,14 +213,14 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) { setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 3); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw3_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 3); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi")); #endif @@ -242,7 +229,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 4 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); @@ -274,14 +261,14 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) { setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi")); #endif @@ -335,7 +322,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 4 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 4); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); @@ -416,40 +403,40 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_radius_aa_sw(void) setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r0_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r1_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r2_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r3_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2)); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax1_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); @@ -465,7 +452,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_radius_sw(void) { test_graphics_context_init(&ctx, fb); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = false, SW = 1 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1); graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0); @@ -542,7 +529,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) { test_graphics_context_init(&ctx, fb); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 1 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); @@ -574,14 +561,14 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) { setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 3); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw3_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 3); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi")); #endif @@ -590,7 +577,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // AA = true, SW = 4 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 4); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); @@ -622,14 +609,14 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) { setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 11); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi")); #endif setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 11); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi")); #endif @@ -643,7 +630,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_sw(void) { test_graphics_context_init(&ctx, fb); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 1 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 1); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); @@ -685,7 +672,7 @@ void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_sw(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // SW = 4 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 4); graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT); diff --git a/tests/fw/graphics/test_graphics_draw_stroke.template.c b/tests/fw/graphics/test_graphics_draw_stroke.template.c index ddd74cee13..79e33214ac 100644 --- a/tests/fw/graphics/test_graphics_draw_stroke.template.c +++ b/tests/fw/graphics/test_graphics_draw_stroke.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -132,7 +119,7 @@ void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__origin_layer(void) { test_graphics_context_init(&ctx, fb); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_WIDTH); graphics_draw_line(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_inside_origin_layer.${BIT_DEPTH_NAME}.pbi")); @@ -160,7 +147,7 @@ void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__offset_layer(void) { test_graphics_context_init(&ctx, fb); // TODO: Fix offset calculation and reenable this: - PBL-16509 -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, STROKE_WIDTH); graphics_draw_line(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_inside_offset_layer.${BIT_DEPTH_NAME}.pbi")); diff --git a/tests/fw/graphics/test_graphics_draw_stroke_precise.template.c b/tests/fw/graphics/test_graphics_draw_stroke_precise.template.c index d0fd4543ae..b862c23fb1 100644 --- a/tests/fw/graphics/test_graphics_draw_stroke_precise.template.c +++ b/tests/fw/graphics/test_graphics_draw_stroke_precise.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" diff --git a/tests/fw/graphics/test_graphics_draw_text.template.c b/tests/fw/graphics/test_graphics_draw_text.template.c index 74136bb7c9..8383595754 100644 --- a/tests/fw/graphics/test_graphics_draw_text.template.c +++ b/tests/fw/graphics/test_graphics_draw_text.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "fixtures/load_test_resources.h" diff --git a/tests/fw/graphics/test_graphics_draw_text_flow.c b/tests/fw/graphics/test_graphics_draw_text_flow.c index 677a7c4909..bdfcbae562 100644 --- a/tests/fw/graphics/test_graphics_draw_text_flow.c +++ b/tests/fw/graphics/test_graphics_draw_text_flow.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "fixtures/load_test_resources.h" @@ -76,8 +63,15 @@ static char *s_text = "A B C D E F G " void prv_prepare_fb_steps_xy(GSize size, int16_t steps_x, int16_t steps_y) { gbitmap_destroy(s_dest_bitmap); + // These are off-screen, tiled scratch buffers for side-by-side comparison, not + // the real display framebuffer. On a round platform GBITMAP_NATIVE_FORMAT is + // GBitmapFormat8BitCircular, whose data is addressed through a per-row offset + // table (g_gbitmap_data_row_infos) that is only attached to a full-screen + // (DISP_COLS x DISP_ROWS) bitmap. A non-full-screen circular bitmap has no row + // table, so reading a data row dereferences an unset pointer and segfaults. + // Use a plain rectangular 8-bit format, which addresses rows by row_size_bytes. s_dest_bitmap = gbitmap_create_blank(GSize(size.w * steps_x, size.h * steps_y), - GBITMAP_NATIVE_FORMAT); + GBitmapFormat8Bit); ctx.dest_bitmap = *s_dest_bitmap; ctx.draw_state.clip_box = (GRect){.size = size}; ctx.draw_state.drawing_box = ctx.draw_state.clip_box; @@ -309,9 +303,9 @@ GRangeHorizontal perimeter_for_display_rect(const GPerimeter *perimeter, GRangeVertical vertical_range, uint16_t inset); -// easiest way to make these dimensions identical to spalding although the tests take -// defaults from basalt's screen resolution. The original `perimeter_for_display_round` -// uses the platform-specific `DISP_FRAME` +// easiest way to make these dimensions identical to a 180x180 round display although the +// tests take defaults from basalt's screen resolution. The original +// `perimeter_for_display_round` uses the platform-specific `DISP_FRAME` static GRangeHorizontal prv_perimeter_for_display_round(const GPerimeter *perimeter, const GSize *ctx_size, GRangeVertical vertical_range, @@ -421,6 +415,10 @@ void test_graphics_draw_text_flow__max_used_size_draw_text_doom(void) { } void test_graphics_draw_text_flow__no_infinite_loop(void) { + // This case uses the rect-display perimeter, which only exists on rect + // platforms. The suite builds for the round (gabbro) platform, so the body is + // compiled out there; clar still discovers the test because it scans textually. +#if PBL_RECT TextLayoutExtended layout = { .flow_data = { .perimeter.impl = &(GPerimeter){.callback = perimeter_for_display_rect}, @@ -454,6 +452,7 @@ void test_graphics_draw_text_flow__no_infinite_loop(void) { } cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE)); +#endif // PBL_RECT } void test_graphics_draw_text_flow__no_infinite_loop2(void) { diff --git a/tests/fw/graphics/test_graphics_fill_circle.template.c b/tests/fw/graphics/test_graphics_fill_circle.template.c index bfa06a4f95..b2d09e909d 100644 --- a/tests/fw/graphics/test_graphics_fill_circle.template.c +++ b/tests/fw/graphics/test_graphics_fill_circle.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/graphics_circle_private.h" diff --git a/tests/fw/graphics/test_graphics_fill_rect.template.c b/tests/fw/graphics/test_graphics_fill_rect.template.c index 81e980a27e..279e3e4290 100644 --- a/tests/fw/graphics/test_graphics_fill_rect.template.c +++ b/tests/fw/graphics/test_graphics_fill_rect.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -294,7 +281,7 @@ void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_radius_aa(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r2_aa_no_clip.${BIT_DEPTH_NAME}.pbi")); // TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 // TODO: Fix left corners PBL-15915 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1); graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3, GCornersAll); @@ -326,7 +313,7 @@ void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_radius_aa(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r8_aa_no_clip.${BIT_DEPTH_NAME}.pbi")); // TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1); graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 9, GCornersAll); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r9_aa_no_clip.${BIT_DEPTH_NAME}.pbi")); @@ -351,7 +338,7 @@ void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_radius_aa(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r0_aa_clip_xy.${BIT_DEPTH_NAME}.pbi")); // TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1); graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, 1, GCornersAll); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r1_aa_clip_xy.${BIT_DEPTH_NAME}.pbi")); @@ -384,7 +371,7 @@ void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_radius_aa(void) { cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r0_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi")); // TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1); graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , 1, GCornersAll); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r1_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi")); @@ -456,7 +443,7 @@ void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_aa_corners(void) { test_graphics_context_init(&ctx, fb); // TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1); graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersBottom); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_bottom.${BIT_DEPTH_NAME}.pbi")); diff --git a/tests/fw/graphics/test_graphics_gpath.template.c b/tests/fw/graphics/test_graphics_gpath.template.c index 98da594352..0cc89b6801 100644 --- a/tests/fw/graphics/test_graphics_gpath.template.c +++ b/tests/fw/graphics/test_graphics_gpath.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_graphics_gtransform.template.c b/tests/fw/graphics/test_graphics_gtransform.template.c index 1cc0c49435..fcdc9346ed 100644 --- a/tests/fw/graphics/test_graphics_gtransform.template.c +++ b/tests/fw/graphics/test_graphics_gtransform.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -289,10 +276,10 @@ void test_graphics_gtransform_${BIT_DEPTH_NAME}__rotation(void) { // a = b = 10*cos(45) - 10*sin(45) // c = d = 10*sin(45) + 10*cos(45) t_c = GTransform(GTransformNumberFromNumber(0), - GTransformNumberFromNumber(0), - (Fixed_S32_16){ .raw_value = (int32_t)(923960) }, - (Fixed_S32_16){ .raw_value = (int32_t)(923960) }, - GTransformNumberFromNumber(10), + GTransformNumberFromNumber(0), + (Fixed_S32_16){ .raw_value = (int32_t)(926800) }, + (Fixed_S32_16){ .raw_value = (int32_t)(926800) }, + GTransformNumberFromNumber(10), GTransformNumberFromNumber(10)); gtransform_rotate(&t_new, &t1, DEG_TO_TRIGANGLE(45)); diff --git a/tests/fw/graphics/test_graphics_mask.h b/tests/fw/graphics/test_graphics_mask.h deleted file mode 100644 index 47b3612773..0000000000 --- a/tests/fw/graphics/test_graphics_mask.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" -#include "applib/graphics/graphics.h" -#include "applib/graphics/graphics_private_raw_mask.h" -#include "applib/graphics/framebuffer.h" -#include "util/graphics.h" - -static uint8_t test_graphics_context_mask_get_value_for_coordinate(const GContext *ctx, - const GDrawMask *mask, - GPoint p) { - const uint8_t mask_pixel_value = graphics_private_raw_mask_get_value(ctx, mask, p); - - cl_assert(WITHIN(mask_pixel_value, 0, (1 << (GDRAWMASK_BITS_PER_PIXEL)) - 1)); - - return mask_pixel_value; -} - -static void test_graphics_context_mask_set_value_for_coordinate(GContext *ctx, GDrawMask *mask, - uint8_t value, GPoint p) { - cl_assert(WITHIN(value, 0, (1 << (GDRAWMASK_BITS_PER_PIXEL)) - 1)); - - graphics_private_raw_mask_set_value(ctx, mask, p, value); -} - -static void test_graphics_context_mask_debug(GContext *ctx, GDrawMask *mask) { - GBitmap *framebuffer_bitmap = &ctx->dest_bitmap; - - // Naive approach - for (int y = 0; y < framebuffer_bitmap->bounds.size.h; y++) { - const GBitmapDataRowInfo current_data_row_info = gbitmap_get_data_row_info(framebuffer_bitmap, - y); - - // Iterate over the relevant mask row pixel values and use each to draw our debug image - for (int x = current_data_row_info.min_x; x <= current_data_row_info.max_x; x++) { - const uint8_t mask_pixel_value = - test_graphics_context_mask_get_value_for_coordinate(ctx, mask, GPoint(x, y)); - const GColor color_lookup[1 << GDRAWMASK_BITS_PER_PIXEL] = { - GColorBlack, - GColorDarkGray, - GColorLightGray, - GColorWhite, - }; - const GColor pixel_color = color_lookup[mask_pixel_value]; - current_data_row_info.data[x] = pixel_color.argb; - } - } -} diff --git a/tests/fw/graphics/test_graphics_stroke_circle.template.c b/tests/fw/graphics/test_graphics_stroke_circle.template.c index a9bc27a09a..e4dd9dfe20 100644 --- a/tests/fw/graphics/test_graphics_stroke_circle.template.c +++ b/tests/fw/graphics/test_graphics_stroke_circle.template.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/framebuffer.h" @@ -70,7 +57,7 @@ void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__cleanup(void) { void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__origin_layer(void) { -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 GContext ctx; test_graphics_context_init(&ctx, fb); @@ -131,7 +118,7 @@ void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__origin_layer(void) { graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_NONE); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r0_no_clip.${BIT_DEPTH_NAME}.pbi")); -#endif // SCREEN_COLOR_DEPTH_BITS == 8 +#endif // CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 } @@ -143,7 +130,7 @@ void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__origin_layer(void) { void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__offset_layer_aa(void) { -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 GContext ctx; test_graphics_context_init(&ctx, fb); @@ -187,7 +174,7 @@ void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__offset_layer_aa(void) { graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_SMALL); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi")); -#endif // SCREEN_COLOR_DEPTH_BITS == 8 +#endif // CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 } @@ -197,7 +184,7 @@ extern void graphics_circle_quadrant_draw_stroked_non_aa( void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__quadrants(void) { -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 GContext ctx; test_graphics_context_init(&ctx, fb); @@ -234,7 +221,7 @@ void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__quadrants(void) { graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersLeft); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quads_left.${BIT_DEPTH_NAME}.pbi")); -#endif // SCREEN_COLOR_DEPTH_BITS == 8 +#endif // CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 } @@ -244,7 +231,7 @@ extern void graphics_circle_quadrant_draw_stroked_aa( void test_graphics_draw_circle_${BIT_DEPTH_NAME}__quadrants_aa(void) { -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 GContext ctx; test_graphics_context_init(&ctx, fb); @@ -281,6 +268,6 @@ void test_graphics_draw_circle_${BIT_DEPTH_NAME}__quadrants_aa(void) { graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersLeft); cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quads_left.${BIT_DEPTH_NAME}.pbi")); -#endif // SCREEN_COLOR_DEPTH_BITS == 8 +#endif // CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 } diff --git a/tests/fw/graphics/test_graphics_text_node.c b/tests/fw/graphics/test_graphics_text_node.c index 5082695b80..52a8de2628 100644 --- a/tests/fw/graphics/test_graphics_text_node.c +++ b/tests/fw/graphics/test_graphics_text_node.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/timeline/text_node.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/timeline/text_node.h" #include "clar.h" #include "pebble_asserts.h" diff --git a/tests/fw/graphics/test_graphics_window_stack_animation.c b/tests/fw/graphics/test_graphics_window_stack_animation.c deleted file mode 100644 index 1f909b664d..0000000000 --- a/tests/fw/graphics/test_graphics_window_stack_animation.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "applib/graphics/graphics.h" -#include "applib/graphics/graphics_private.h" -#include "applib/graphics/graphics_private_raw.h" -#include "applib/graphics/gtypes.h" -#include "applib/graphics/framebuffer.h" -#include "applib/ui/window_private.h" -#include "applib/ui/window_stack_animation_round.h" -#include "applib/ui/layer.h" -#include "board/displays/display_spalding.h" -#include "services/common/compositor/compositor_transitions.h" -#include "util/trig.h" - -#include "clar.h" -#include "util.h" - -#include - -// Helper Functions -//////////////////////////////////// -#include "test_graphics.h" -#include "8bit/test_framebuffer.h" - -// Stubs -//////////////////////////////////// -#include "graphics_common_stubs.h" -#include "stubs_applib_resource.h" - -void window_transition_context_appearance_call_all(WindowTransitioningContext *ctx) {} - -void window_render(Window *window, GContext *ctx) {} - -bool compositor_transition_app_to_app_should_be_skipped(void) { - return false; -} - -AnimationPrivate *animation_private_animation_find(Animation *handle) { - return NULL; -} - -AnimationProgress animation_private_get_animation_progress(const AnimationPrivate *animation) { - return 0; -} - -const AnimationImplementation* animation_get_implementation(Animation *animation_h) { - return NULL; -} - -const GDrawRawImplementation g_compositor_transitions_app_fb_draw_implementation = {0}; - -void compositor_port_hole_transition_draw_outer_ring(GContext *ctx, int16_t pixels, - GColor ring_color) { -} - -// Setup and Teardown -//////////////////////////////////// - -static FrameBuffer *fb = NULL; - -// Setup -void test_graphics_window_stack_animation__initialize(void) { - fb = malloc(sizeof(FrameBuffer)); - framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS }); -} - -void test_graphics_window_stack_animation__cleanup(void) { - free(fb); -} - -// Helpers -//////////////////////////////////// - -typedef void (*ClippingMaskDrawFunc)(GContext *ctx); - -static void prv_test_clipping_mask(ClippingMaskDrawFunc draw_func, const char *expected_image) { - GContext *ctx = malloc(sizeof(GContext)); - test_graphics_context_init(ctx, fb); - framebuffer_clear(fb); - - graphics_context_set_antialiased(ctx, true); - - // Start by filling the framebuffer with green pixels to make things easier to see - memset(ctx->dest_bitmap.addr, GColorGreenARGB8, FRAMEBUFFER_SIZE_BYTES); - - const bool transparent = true; - GDrawMask *mask = graphics_context_mask_create(ctx, transparent); - cl_assert(mask); - - cl_assert(graphics_context_mask_record(ctx, mask)); - - draw_func(ctx); - - cl_assert(graphics_context_mask_record(ctx, NULL)); - cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation); - - cl_assert(graphics_context_mask_use(ctx, mask)); - cl_assert_equal_p(ctx->draw_state.draw_mask, mask); - - graphics_context_set_fill_color(ctx, GColorRed); - graphics_fill_rect(ctx, &ctx->dest_bitmap.bounds); - - cl_assert(graphics_context_mask_use(ctx, NULL)); - cl_assert_equal_p(ctx->draw_state.draw_mask, NULL); - - cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, TEST_NAMED_PBI_FILE(expected_image))); - - graphics_context_mask_destroy(ctx, mask); - - free(ctx); -} - -// Tests -//////////////////////////////////// - -// NOTE: All of the following tests first fill the framebuffer with green to make it easier to see -// incorrect pixels - -// This test records a clipping mask of the first frame of the left "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_left_flip_first_frame_clipping(GContext *ctx) { - compositor_round_flip_transitions_flip_animation_update(ctx, 0, - CompositorTransitionDirectionLeft, - GColorWhite); -} - -void test_graphics_window_stack_animation__left_flip_first_frame_clipping(void) { - prv_test_clipping_mask(prv_left_flip_first_frame_clipping, "left_flip_first_frame_clipping"); -}; - -// This test records a clipping mask of the 1/4 progress frame of the left "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_left_flip_first_quarter_frame_clipping(GContext *ctx) { - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 4, - CompositorTransitionDirectionLeft, - GColorWhite); -} - -void test_graphics_window_stack_animation__left_flip_first_quarter_frame_clipping(void) { - prv_test_clipping_mask(prv_left_flip_first_quarter_frame_clipping, - "left_flip_first_quarter_frame_clipping"); -}; - -// This test records a clipping mask of the half progress frame of the left "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_left_flip_half_frame_clipping(GContext *ctx) { - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 2, - CompositorTransitionDirectionLeft, - GColorWhite); -} - -void test_graphics_window_stack_animation__left_flip_half_frame_clipping(void) { - prv_test_clipping_mask(prv_left_flip_half_frame_clipping, "left_flip_half_frame_clipping"); -}; - -// This test records a clipping mask of the 3/4 progress frame of the left "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_left_flip_third_quarter_frame_clipping(GContext *ctx) { - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX * 3 / 4, - CompositorTransitionDirectionLeft, - GColorWhite); -} - -void test_graphics_window_stack_animation__left_flip_third_quarter_frame_clipping(void) { - prv_test_clipping_mask(prv_left_flip_third_quarter_frame_clipping, - "left_flip_third_quarter_frame_clipping"); -}; - -// This test records a clipping mask of the last frame of the left "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_left_flip_last_frame_clipping(GContext *ctx) { - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX, - CompositorTransitionDirectionLeft, - GColorWhite); -} - -void test_graphics_window_stack_animation__left_flip_last_frame_clipping(void) { - prv_test_clipping_mask(prv_left_flip_last_frame_clipping, "left_flip_last_frame_clipping"); -}; - -// This test records a clipping mask of the first frame of the right "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_right_flip_first_frame_clipping(GContext *ctx) { - // The first frame is ANIMATION_NORMALIZED_MAX because the right flip animation is actually - // played backwards - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX, - CompositorTransitionDirectionRight, - GColorWhite); -} - -void test_graphics_window_stack_animation__right_flip_first_frame_clipping(void) { - prv_test_clipping_mask(prv_right_flip_first_frame_clipping, "right_flip_first_frame_clipping"); -}; - -// This test records a clipping mask of the 1/4 progress frame of the right "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_right_flip_first_quarter_frame_clipping(GContext *ctx) { - // The 1/4 progress frame is ANIMATION_NORMALIZED_MAX * 3/4 because the right flip animation is - // actually played backwards - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX * 3 / 4, - CompositorTransitionDirectionRight, - GColorWhite); -} - -void test_graphics_window_stack_animation__right_flip_first_quarter_frame_clipping(void) { - prv_test_clipping_mask(prv_right_flip_first_quarter_frame_clipping, - "right_flip_first_quarter_frame_clipping"); -}; - -// This test records a clipping mask of the half progress frame of the right "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_right_flip_half_frame_clipping(GContext *ctx) { - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 2, - CompositorTransitionDirectionRight, - GColorWhite); -} - -void test_graphics_window_stack_animation__right_flip_half_frame_clipping(void) { - prv_test_clipping_mask(prv_right_flip_half_frame_clipping, "right_flip_half_frame_clipping"); -}; - -// This test records a clipping mask of the 3/4 progress frame of the right "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_right_flip_third_quarter_frame_clipping(GContext *ctx) { - // The 3/4 progress frame is ANIMATION_NORMALIZED_MAX * 1/4 because the right flip animation is - // actually played backwards - compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 4, - CompositorTransitionDirectionRight, - GColorWhite); -} - -void test_graphics_window_stack_animation__right_flip_third_quarter_frame_clipping(void) { - prv_test_clipping_mask(prv_right_flip_third_quarter_frame_clipping, - "right_flip_third_quarter_frame_clipping"); -}; - -// This test records a clipping mask of the last frame of the right "round flip" compositor -// transition animation and then clips a full-screen red rectangle to the resulting mask - -static void prv_right_flip_last_frame_clipping(GContext *ctx) { - // The last frame is 0 because the right flip animation is actually played backwards - compositor_round_flip_transitions_flip_animation_update(ctx, 0, - CompositorTransitionDirectionRight, - GColorWhite); -} - -void test_graphics_window_stack_animation__right_flip_last_frame_clipping(void) { - prv_test_clipping_mask(prv_right_flip_last_frame_clipping, "right_flip_last_frame_clipping"); -}; - -void test_graphics_window_stack_animation__move_pixels_horizontally(void) { - GContext *ctx = malloc(sizeof(GContext)); - test_graphics_context_init(ctx, fb); - framebuffer_clear(fb); - - GBitmap *const bitmap = &ctx->dest_bitmap; - - // vertical test pattern - for (int16_t y = 0; y < DISP_ROWS; y++) { - GBitmapDataRowInfo row_info = prv_gbitmap_get_data_row_info(bitmap, y); - for (int x = 0; x < DISP_COLS; x++) { - GColor8 color = GColorFromRGB(1 * x * UINT8_MAX / DISP_COLS, - 2 * x * UINT8_MAX / DISP_COLS, - 4 * x * UINT8_MAX / DISP_COLS); - if (row_info.min_x <= x && x <= row_info.max_x) { - row_info.data[x] = color.argb; - } - } - } - - - // nop - graphics_private_move_pixels_horizontally(NULL, 50, false); - graphics_private_move_pixels_horizontally(bitmap, 0, false); - - graphics_private_move_pixels_horizontally(bitmap, 50, false); - cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_right"))); - - graphics_private_move_pixels_horizontally(bitmap, -100, false); - cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_left"))); - - graphics_private_move_pixels_horizontally(bitmap, 400, false); - cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_right_too_far"))); - - graphics_private_move_pixels_horizontally(bitmap, -400, true); - cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_left_filled"))); - - free(ctx); -} diff --git a/tests/fw/graphics/test_gtypes.c b/tests/fw/graphics/test_gtypes.c index bc3c6cbc72..28affbab98 100644 --- a/tests/fw/graphics/test_gtypes.c +++ b/tests/fw/graphics/test_gtypes.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" @@ -130,11 +117,9 @@ void test_gtypes__grect_crop_asserts_for_large_insets(void) { } void test_gtypes__pbl_if_rect_else(void) { -#if defined(PLATFORM_TINTIN) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) cl_assert_equal_i(1, PBL_IF_RECT_ELSE(1,2)); -#elif defined(PLATFORM_SNOWY) - cl_assert_equal_i(1, PBL_IF_RECT_ELSE(1,2)); -#elif defined(PLATFORM_SPALDING) +#elif defined(CONFIG_PLATFORM_GABBRO) cl_assert_equal_i(2, PBL_IF_RECT_ELSE(1,2)); #else #error "unknown platform" @@ -142,11 +127,9 @@ void test_gtypes__pbl_if_rect_else(void) { } void test_gtypes__pbl_if_round_else(void) { -#if defined(PLATFORM_TINTIN) - cl_assert_equal_i(2, PBL_IF_ROUND_ELSE(1,2)); -#elif defined(PLATFORM_SNOWY) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) || defined(CONFIG_BOARD_FAMILY_OBELIX) cl_assert_equal_i(2, PBL_IF_ROUND_ELSE(1,2)); -#elif defined(PLATFORM_SPALDING) +#elif defined(CONFIG_PLATFORM_GABBRO) cl_assert_equal_i(1, PBL_IF_ROUND_ELSE(1,2)); #else #error "unknown platform" @@ -154,11 +137,9 @@ void test_gtypes__pbl_if_round_else(void) { } void test_gtypes__pbl_if_bw_else(void) { -#if defined(PLATFORM_TINTIN) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) cl_assert_equal_i(1, PBL_IF_BW_ELSE(1,2)); -#elif defined(PLATFORM_SNOWY) - cl_assert_equal_i(2, PBL_IF_BW_ELSE(1,2)); -#elif defined(PLATFORM_SPALDING) +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_PLATFORM_GABBRO) cl_assert_equal_i(2, PBL_IF_BW_ELSE(1,2)); #else #error "unknown platform" @@ -166,11 +147,9 @@ void test_gtypes__pbl_if_bw_else(void) { } void test_gtypes__pbl_if_color_else(void) { -#if defined(PLATFORM_TINTIN) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) cl_assert_equal_i(2, PBL_IF_COLOR_ELSE(1,2)); -#elif defined(PLATFORM_SNOWY) - cl_assert_equal_i(1, PBL_IF_COLOR_ELSE(1,2)); -#elif defined(PLATFORM_SPALDING) +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_PLATFORM_GABBRO) cl_assert_equal_i(1, PBL_IF_COLOR_ELSE(1,2)); #else #error "unknown platform" @@ -178,11 +157,9 @@ void test_gtypes__pbl_if_color_else(void) { } void test_gtypes__color_fallback(void) { -#if defined(PLATFORM_TINTIN) +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) cl_assert_equal_i(2, COLOR_FALLBACK(1,2)); -#elif defined(PLATFORM_SNOWY) - cl_assert_equal_i(1, COLOR_FALLBACK(1,2)); -#elif defined(PLATFORM_SPALDING) +#elif defined(CONFIG_BOARD_FAMILY_OBELIX) || defined(CONFIG_PLATFORM_GABBRO) cl_assert_equal_i(1, COLOR_FALLBACK(1,2)); #else #error "unknown platform" diff --git a/tests/fw/graphics/test_palettized_conversion.c b/tests/fw/graphics/test_palettized_conversion.c index 43d0ad269e..0e54ed5d37 100644 --- a/tests/fw/graphics/test_palettized_conversion.c +++ b/tests/fw/graphics/test_palettized_conversion.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/graphics.h" #include "applib/graphics/gbitmap_png.h" diff --git a/tests/fw/graphics/test_pbi.c b/tests/fw/graphics/test_pbi.c index d4038efd52..ce2014b6c0 100644 --- a/tests/fw/graphics/test_pbi.c +++ b/tests/fw/graphics/test_pbi.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gtypes.h" diff --git a/tests/fw/graphics/test_pdc.c b/tests/fw/graphics/test_pdc.c index 35219d4082..c70cc9c1ce 100644 --- a/tests/fw/graphics/test_pdc.c +++ b/tests/fw/graphics/test_pdc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/graphics/test_perimeter.c b/tests/fw/graphics/test_perimeter.c index a152d4a840..e629f94cdc 100644 --- a/tests/fw/graphics/test_perimeter.c +++ b/tests/fw/graphics/test_perimeter.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/perimeter.h" @@ -53,7 +40,12 @@ GRangeHorizontal perimeter_for_display_rect(const GPerimeter *perimeter, GRangeVertical vertical_range, uint16_t inset); +// perimeter_for_circle and perimeter_for_display_round exist only on round +// displays, perimeter_for_display_rect only on rectangular ones. clar scans +// this file textually (no preprocessing), so every test_ function must stay +// defined on both platforms; guard each body by display shape instead. void test_perimeter__perimeter_for_circle(void) { +#if PBL_ROUND GRect bounds = GRect(0,0,180,180); GPoint center = grect_center_point(&bounds); int16_t radius = bounds.size.w / 2; @@ -62,7 +54,7 @@ void test_perimeter__perimeter_for_circle(void) { for (int y = 0; y < bounds.size.h; y++) { GRangeHorizontal h_range = perimeter_for_circle( (GRangeVertical) {.origin_y = y, .size_h = 0}, center, radius); - + // internally we use integer_sqrt, which causes precision loss // so we need to mirror some of the fixed point truncation here int16_t height = 90 - y; @@ -73,6 +65,7 @@ void test_perimeter__perimeter_for_circle(void) { cl_assert(BETWEEN(h_range.origin_x, test_origin - 1, test_origin + 1)); cl_assert(BETWEEN(h_range.size_w, (width - 1) * 2, (width + 1) * 2)); } +#endif } #define cl_assert_equal_rangehorizontal(r1, r2) \ @@ -82,6 +75,7 @@ void test_perimeter__perimeter_for_circle(void) { } while(0) void test_perimeter__perimeter_for_display_rect(void) { +#if PBL_RECT GPerimeter p = { .callback = perimeter_for_display_rect, }; @@ -93,9 +87,11 @@ void test_perimeter__perimeter_for_display_rect(void) { expected = (GRangeHorizontal){.origin_x = 5, .size_w = DISP_COLS - 10}; cl_assert_equal_rangehorizontal(expected, perimeter_for_display_rect(&p, &ctx_size, r, 5)); cl_assert_equal_i(0, perimeter_for_display_rect(&p, &ctx_size, r, 500).size_w); +#endif } void test_perimeter__perimeter_for_display_round(void) { +#if PBL_ROUND GPerimeter p = { .callback = perimeter_for_display_round, }; @@ -109,6 +105,7 @@ void test_perimeter__perimeter_for_display_round(void) { expected = perimeter_for_circle(r, grect_center_point(&disp), grect_shortest_side(disp) / 2 - 5); cl_assert_equal_rangehorizontal(expected, perimeter_for_display_round(&p, &ctx_size, r, 5)); cl_assert_equal_i(0, perimeter_for_display_round(&p, &ctx_size, r, 500).size_w); +#endif } void test_perimeter__g_perimeter_for_display(void) { diff --git a/tests/fw/graphics/test_png.c b/tests/fw/graphics/test_png.c index 34790988f7..54ff5cfbd0 100644 --- a/tests/fw/graphics/test_png.c +++ b/tests/fw/graphics/test_png.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/gbitmap_png.h" diff --git a/tests/fw/graphics/util.h b/tests/fw/graphics/util.h index 3e92183d65..9f2ca78c51 100644 --- a/tests/fw/graphics/util.h +++ b/tests/fw/graphics/util.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -67,7 +54,7 @@ static const char *namecat(const char* str1, const char* str2){ if (filename_xbit) { // Support using ".Xbit" for the native bitdepth - filename_xbit[1] = (SCREEN_COLOR_DEPTH_BITS == 8) ? '8' : '1'; + filename_xbit[1] = (CONFIG_SCREEN_COLOR_DEPTH_BITS == 8) ? '8' : '1'; printf("filename and filename_xbit %s : %s\n", filename, filename_xbit); } else { #if !PLATFORM_DEFAULT @@ -156,7 +143,7 @@ GBitmap *get_gbitmap_from_pbi(const char *filename) { // Support using ".Xbit" for the native bitdepth char *filename_xbit = strstr(full_path, ".Xbit"); if (filename_xbit) { - filename_xbit[1] = (SCREEN_COLOR_DEPTH_BITS == 8) ? '8' : '1'; + filename_xbit[1] = (CONFIG_SCREEN_COLOR_DEPTH_BITS == 8) ? '8' : '1'; } FILE *file = fopen(full_path, "r"); @@ -484,27 +471,3 @@ void setup_test_aa_sw(GContext *ctx, FrameBuffer *fb, GRect clip_box, GRect draw &draw_state, NULL); } -#if PLATFORM_SPALDING -bool gbitmap_8bit_to_8bit_circular(GBitmap *bitmap) { - // Only allow conversion of 8Bit 180x180 rectangular bitmaps to circular - if (!bitmap || (gbitmap_get_format(bitmap) != GBitmapFormat8Bit) || - (bitmap->bounds.size.w != DISP_COLS) || (bitmap->bounds.size.h != DISP_COLS)) { - return false; - } - // Using realloc or copying to a new buffer has high memory overhead - // attempt to shuffle bytes in place to allow 3rd party watchapps use - uint8_t *data = gbitmap_get_data(bitmap); - - // Convert format and link to data_row_infos table - bitmap->info.format = GBitmapFormat8BitCircular; - bitmap->data_row_infos = g_gbitmap_spalding_data_row_infos; - - for (uint32_t y = 0; y < DISP_ROWS; y++) { - GBitmapDataRowInfo row_info = gbitmap_get_data_row_info(bitmap, y); - // copy (using memmove for overlapping region) valid bytes between min_x and max_x - memmove(&row_info.data[row_info.min_x], &data[y * DISP_COLS + row_info.min_x], - row_info.max_x - row_info.min_x + 1); - } - return true; -} -#endif diff --git a/tests/fw/graphics/util_pbi.h b/tests/fw/graphics/util_pbi.h index 5eb736befe..0135f481c9 100644 --- a/tests/fw/graphics/util_pbi.h +++ b/tests/fw/graphics/util_pbi.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -49,7 +36,7 @@ bool write_gbitmap_to_pbi(GBitmap *bmp, const char *filepath, const char *pbi2pn // PBL-24228 Support Circular PBIs uint16_t info_flags = bmp->info_flags; -#ifdef PLATFORM_SPALDING +#if PBL_ROUND if(bmp->info.format == GBitmapFormat8BitCircular) { // Have to force output format to 8Bit; ((BitmapInfo*)&info_flags)->format = GBitmapFormat8Bit; @@ -65,7 +52,7 @@ bool write_gbitmap_to_pbi(GBitmap *bmp, const char *filepath, const char *pbi2pn fwrite(&info_flags, 2, 1, file); fwrite(&entire_bounds, sizeof(GRect), 1, file); -#ifdef PLATFORM_SPALDING +#if PBL_ROUND if(bmp->info.format == GBitmapFormat8BitCircular) { for (int y = 0; y < entire_bounds.size.h; ++y) { // 8-Bit circular buffer is centered in padded rows, so just grab row and write DISP_COLS @@ -94,7 +81,7 @@ bool write_gbitmap_to_pbi(GBitmap *bmp, const char *filepath, const char *pbi2pn #endif size_t data_size = bmp->row_size_bytes * (entire_bounds.size.h); fwrite(bmp->addr, 1, data_size, file); -#ifdef PLATFORM_SPALDING +#if PBL_ROUND } #endif diff --git a/tests/fw/graphics/weather_app_resources.c b/tests/fw/graphics/weather_app_resources.c index 5a13571811..7c278560b8 100644 --- a/tests/fw/graphics/weather_app_resources.c +++ b/tests/fw/graphics/weather_app_resources.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "weather_app_resources.h" #include "kernel/pbl_malloc.h" @@ -22,8 +9,6 @@ #include -#if !TINTIN_FORCE_FIT - #if defined(UNITTEST) #include #endif @@ -195,5 +180,4 @@ GDrawCommandImage *weather_app_resource_create_sun_25px(void) { COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 3)->points, c3_points) COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 4)->points, c4_points) return result; -} -#endif +} \ No newline at end of file diff --git a/tests/fw/graphics/weather_app_resources.h b/tests/fw/graphics/weather_app_resources.h index 7daea83419..81f2740636 100644 --- a/tests/fw/graphics/weather_app_resources.h +++ b/tests/fw/graphics/weather_app_resources.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fw/graphics/wscript b/tests/fw/graphics/wscript deleted file mode 100644 index 26ecbab323..0000000000 --- a/tests/fw/graphics/wscript +++ /dev/null @@ -1,704 +0,0 @@ -from waftools.pebble_test import clar, get_bitdepth_for_platform -import waflib.Utils - -def _process_template(task): - from string import Template - template = Template(open(task.inputs[0].srcpath(), 'r').read()) - processed = template.safe_substitute(BIT_DEPTH_NAME=task.generator.bit_depth_name) - waflib.Utils.writef(task.outputs[0].srcpath(), processed) - -def graphics_process_test_template(ctx, source, bit_depth): - path = ctx.path.find_node(source).srcpath() - bit_depth_name = "{}bit".format(bit_depth) - - # Format the output filename. - # - ".template.c" => ".auto.c" - # - Add bit depth to filename - # eg. "test_graphics.template.c" => "test_graphics_8bit.c" - split = source.replace("template.", "").split('.') - target_name = split[0] + '_' + bit_depth_name + '.' + split[1] - - out_path = ctx.path.get_bld().make_node(target_name) - - ctx(name='generate_test_from_template', rule=_process_template, source=ctx.path.find_node(source), - target=out_path, bit_depth_name=bit_depth_name) - return out_path - - -def build(ctx): - graphics_sources_ant_glob = \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/bitmap_layer.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/gpath.c" - - graphics_test_sources = [ - "test_framebuffer.template.c", - "test_graphics_draw_pixel.template.c", - "test_graphics_draw_stroke.template.c", - "test_graphics_draw_rect.template.c", - "test_graphics_fill_rect.template.c", - "test_graphics_draw_circle.template.c", - "test_graphics_stroke_circle.template.c", - "test_graphics_fill_circle.template.c", - "test_graphics_draw_round_rect.template.c", - "test_graphics_gpath.template.c", - "test_graphics_gtransform.template.c", - "test_graphics_context.template.c", - "test_blending.template.c", - "test_graphics_draw_stroke_precise.template.c" - ] - - for platform in ['tintin', 'snowy']: - bit_depth = get_bitdepth_for_platform(ctx, platform) - defines = ctx.env.test_image_defines - for test in graphics_test_sources: - generated_source = graphics_process_test_template(ctx, test, bit_depth) - clar(ctx, - sources_ant_glob=graphics_sources_ant_glob, - test_sources=[generated_source], - add_includes=[ctx.path.abspath()], - defines=defines, - platforms=[platform]) - - clar(ctx, - sources_ant_glob=" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/graphics_bitmap.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/ui/bitmap_layer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/vendor/uPNG/upng.c", - test_sources_ant_glob='test_graphics_draw_bitmap.c', - defines=ctx.env.test_image_defines, - platforms=['snowy', 'silk']) - - templated_graphics_draw_text_sources_ant_glob = \ - " src/fw/applib/graphics/{depth_dir}/framebuffer.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/{depth_dir}/bitblt_private.c" \ - " src/fw/applib/ui/layer.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " tests/fakes/fake_display.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_fonts.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" - - graphics_draw_text_test_sources = [ - "test_graphics_draw_text.template.c" - ] - - for platform in ['tintin', 'snowy']: - bit_depth = 1 if platform == 'tintin' else 8 - depth_dir = '{}_bit'.format(bit_depth) - graphics_sources_ant_glob = templated_graphics_draw_text_sources_ant_glob.format(depth_dir=depth_dir) - - defines = ctx.env.test_image_defines - for test in graphics_draw_text_test_sources: - generated_source = graphics_process_test_template(ctx, test, bit_depth) - clar(ctx, - sources_ant_glob=graphics_sources_ant_glob, - test_sources=[generated_source], - add_includes=[ctx.path.abspath()], - defines=defines, - override_includes=['dummy_board'], - platforms=[platform]) - - clar(ctx, - sources_ant_glob=templated_graphics_draw_text_sources_ant_glob.format(depth_dir="8_bit"), - test_sources_ant_glob='test_graphics_draw_text_flow.c', - defines = ctx.env.test_image_defines, - override_includes=['dummy_board']) - - - # All tests that are bit-depth agnostic should follow. - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c", \ - test_sources_ant_glob = "test_graphics_colors.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gtypes.c", - test_sources_ant_glob = "test_gtypes.c", - platforms=["tintin", "snowy", "spalding"]) - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/graphics_circle.c", - test_sources_ant_glob = "test_graphics_circle.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gtypes.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " tests/fakes/fake_resource_syscalls.c" \ - " src/fw/applib/graphics/gbitmap.c", - test_sources_ant_glob = "test_gbitmap_data_row_info.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " tests/fakes/fake_applib_resource.c", - test_sources_ant_glob = "test_gbitmap_resource_validation.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gtypes.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " tests/fakes/fake_resource_syscalls.c" \ - " src/fw/applib/graphics/gbitmap.c", - test_sources_ant_glob = "test_gbitmap_formats.c", - platforms=["tintin", "snowy", "spalding"]) - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/8_bit/framebuffer.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " tests/fakes/fake_resource_syscalls.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/stubs/stubs_bitblt.c", - test_sources_ant_glob = "test_gbitmap_processor.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/8_bit/bitblt_private.c", \ - test_sources_ant_glob = "test_graphics_blending.c", - platforms=['snowy']) - - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c", - test_sources_ant_glob = "test_gdraw_command.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c", - test_sources_ant_glob = "test_gdraw_command_sequence.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " tests/fakes/fake_resource_syscalls.c" \ - " tests/fakes/fake_applib_resource.c", - runtime_deps=ctx.env.test_pdcs, - defines=ctx.env.test_image_defines, - test_sources_ant_glob = "test_gdraw_command_resources.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/8_bit/framebuffer.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/8_bit/bitblt_private.c" \ - - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/animation_timing.c" \ - - " tests/fakes/fake_gbitmap_png.c" \ - - " tests/fw/graphics/weather_app_resources.c" \ - - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_transforms.c", \ - test_sources_ant_glob = "test_gdraw_command_transforms.c", - defines=ctx.env.test_image_defines, - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/gtypes.c", - runtime_deps=ctx.env.pdcs2png_test_files, - test_sources_ant_glob="test_pdc.c", - defines=ctx.env.test_image_defines) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/gtypes.c", - test_sources_ant_glob="test_png.c", - defines=ctx.env.test_image_defines, - runtime_deps=filter(lambda x: 'test_png__' in str(x), ctx.env.test_pngs)) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gbitmap_sequence.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/board/displays/display_spalding.c" - " tests/fakes/fake_resource_syscalls.c", - test_sources_ant_glob="test_gbitmap_sequence.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['tintin', 'spalding']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" \ - " tests/fakes/fake_gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_pbi.c", - defines=ctx.env.test_image_defines, - runtime_deps=filter(lambda x: 'test_pbi__' in str(x), ctx.env.test_pbis)) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_palettized_conversion.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis) - - clar(ctx, - sources_ant_glob=( - "src/fw/applib/graphics/gtypes.c " - "src/fw/apps/system_apps/timeline/text_node.c " - "tests/stubs/stubs_text_layout.c " - ), - test_sources_ant_glob="test_graphics_text_node.c") - - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/perimeter.c" - " src/fw/applib/graphics/gtypes.c", - test_sources_ant_glob="test_perimeter.c", - test_libs=['m'], - defines=defines, - platforms=['snowy', 'spalding']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_graphics_draw_line.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['tintin', 'snowy']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_bitblt_palette_1bit.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['tintin']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/board/displays/display_spalding.c", - test_sources_ant_glob="test_framebuffer_duma.c", - defines=ctx.env.test_image_defines, - platforms=['snowy', 'spalding']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " tests/fakes/fake_gbitmap_get_data_row.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_graphics_draw_core.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['snowy']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " tests/fakes/fake_gbitmap_get_data_row.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/board/displays/display_spalding.c", - test_sources_ant_glob="test_framebuffer_clipping.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['spalding']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " tests/fakes/fake_gbitmap_get_data_row.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_private_raw_mask.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics_mask.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/ui/window_stack_animation_round.c" - " tests/stubs/stubs_animation.c" - " src/fw/services/common/compositor/default/compositor_round_flip_transitions.c" - " src/fw/applib/ui/animation_interpolate.c" - " src/fw/board/displays/display_spalding.c", - test_sources_ant_glob="test_graphics_window_stack_animation.c", - defines=ctx.env.test_image_defines + ['CAPABILITY_HAS_MASKING=1'], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['spalding']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_private_raw_mask.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics_mask.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/board/displays/display_spalding.c", - test_sources_ant_glob="test_graphics_context_mask.c", - defines=ctx.env.test_image_defines + ['CAPABILITY_HAS_MASKING=1'], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=['snowy']) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " tests/fakes/fake_gbitmap_get_data_row.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_graphics_draw_implementation.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/board/displays/display_$PLATFORM.c", - test_sources_ant_glob="test_bitblt_circular.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=["spalding"]) - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " tests/fakes/fake_gbitmap_png.c" - " tests/fakes/fake_gbitmap_get_data_row.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_bitmap.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics.c", - test_sources_ant_glob="test_graphics_draw_rotated_bitmap.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, - platforms=["tintin", "snowy"]) - - ############################### - # 8-bit specific bitblt tests # - ############################### - graphics_sources_ant_glob = \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/8_bit/framebuffer.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/8_bit/bitblt_private.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/ui/layer.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " tests/fakes/fake_display.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" - - graphics_test_sources_8bit = [ - "test_bitblt.c", - "test_bitblt_palette.c" - ] - - for test in graphics_test_sources_8bit: - defines = ctx.env.test_image_defines - - clar(ctx, - sources_ant_glob = graphics_sources_ant_glob, - test_sources_ant_glob = test, - defines=defines) - -# vim:filetype=python diff --git a/tests/fw/graphics/wscript_build b/tests/fw/graphics/wscript_build new file mode 100644 index 0000000000..ef26ec76fc --- /dev/null +++ b/tests/fw/graphics/wscript_build @@ -0,0 +1,611 @@ +from waftools.pebble_test import clar, get_bitdepth_for_platform +import waflib.Utils + +def _process_template(task): + from string import Template + template = Template(open(task.inputs[0].srcpath(), 'r').read()) + processed = template.safe_substitute(BIT_DEPTH_NAME=task.generator.bit_depth_name) + waflib.Utils.writef(task.outputs[0].srcpath(), processed) + +def graphics_process_test_template(ctx, source, bit_depth): + path = ctx.path.find_node(source).srcpath() + bit_depth_name = "{}bit".format(bit_depth) + + # Format the output filename. + # - ".template.c" => ".auto.c" + # - Add bit depth to filename + # eg. "test_graphics.template.c" => "test_graphics_8bit.c" + split = source.replace("template.", "").split('.') + target_name = split[0] + '_' + bit_depth_name + '.' + split[1] + + out_path = ctx.path.get_bld().make_node(target_name) + + ctx(name='generate_test_from_template', rule=_process_template, source=ctx.path.find_node(source), + target=out_path, bit_depth_name=bit_depth_name) + return out_path + +graphics_sources_ant_glob = \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/bitmap_layer.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/gpath.c" + +graphics_test_sources = [ + "test_framebuffer.template.c", + "test_graphics_draw_pixel.template.c", + "test_graphics_draw_stroke.template.c", + "test_graphics_draw_rect.template.c", + "test_graphics_fill_rect.template.c", + "test_graphics_draw_circle.template.c", + "test_graphics_stroke_circle.template.c", + "test_graphics_fill_circle.template.c", + "test_graphics_draw_round_rect.template.c", + "test_graphics_gpath.template.c", + "test_graphics_gtransform.template.c", + "test_graphics_context.template.c", + "test_blending.template.c", + "test_graphics_draw_stroke_precise.template.c" +] + +for platform in ['obelix']: + bit_depth = get_bitdepth_for_platform(ctx, platform) + defines = ctx.env.test_image_defines + for test in graphics_test_sources: + generated_source = graphics_process_test_template(ctx, test, bit_depth) + clar(ctx, + sources_ant_glob=graphics_sources_ant_glob, + test_sources=[generated_source], + add_includes=[ctx.path.abspath()], + defines=defines, + platforms=[platform]) + +clar(ctx, + sources_ant_glob=" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/graphics_bitmap.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/ui/bitmap_layer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/vendor/uPNG/upng.c", + test_sources_ant_glob='test_graphics_draw_bitmap.c', + defines=ctx.env.test_image_defines, + platforms=['obelix']) + +templated_graphics_draw_text_sources_ant_glob = \ + " src/fw/applib/graphics/{depth_dir}/framebuffer.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/{depth_dir}/bitblt_private.c" \ + " src/fw/applib/ui/layer.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " tests/fakes/fake_display.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " tests/fakes/fake_fonts.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" + +graphics_draw_text_test_sources = [ + "test_graphics_draw_text.template.c" +] + +for platform in ['obelix']: + bit_depth = 8 + depth_dir = '{}_bit'.format(bit_depth) + graphics_sources_ant_glob = templated_graphics_draw_text_sources_ant_glob.format(depth_dir=depth_dir) + + defines = ctx.env.test_image_defines + for test in graphics_draw_text_test_sources: + generated_source = graphics_process_test_template(ctx, test, bit_depth) + clar(ctx, + sources_ant_glob=graphics_sources_ant_glob, + test_sources=[generated_source], + add_includes=[ctx.path.abspath()], + defines=defines, + override_includes=['dummy_board'], + platforms=[platform]) + +# This test exercises round-display text flow (perimeter_for_display_round), so +# it must build for a round platform. gabbro is the round test platform, which +# maps to the getafix display: pull in display_getafix.c for the round +# per-row data extent tables (g_gbitmap_data_row_infos et al.). +clar(ctx, + sources_ant_glob=templated_graphics_draw_text_sources_ant_glob.format(depth_dir="8_bit") + + " src/fw/board/displays/display_getafix.c", + test_sources_ant_glob='test_graphics_draw_text_flow.c', + defines = ctx.env.test_image_defines, + override_includes=['dummy_board'], + platforms=['gabbro']) + + +# All tests that are bit-depth agnostic should follow. +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c", \ + test_sources_ant_glob = "test_graphics_colors.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gtypes.c", + test_sources_ant_glob = "test_gtypes.c", + platforms=["obelix", "gabbro"]) + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/graphics_circle.c", + test_sources_ant_glob = "test_graphics_circle.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gtypes.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " tests/fakes/fake_resource_syscalls.c" \ + " src/fw/applib/graphics/gbitmap.c", + test_sources_ant_glob = "test_gbitmap_data_row_info.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " tests/fakes/fake_applib_resource.c", + test_sources_ant_glob = "test_gbitmap_resource_validation.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gtypes.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " tests/fakes/fake_resource_syscalls.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/board/displays/display_getafix.c", + test_sources_ant_glob = "test_gbitmap_formats.c", + platforms=["obelix", "gabbro"]) + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/8_bit/framebuffer.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " tests/fakes/fake_resource_syscalls.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/stubs/stubs_bitblt.c", + test_sources_ant_glob = "test_gbitmap_processor.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/8_bit/bitblt_private.c", \ + test_sources_ant_glob = "test_graphics_blending.c", + platforms=['obelix']) + + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c", + test_sources_ant_glob = "test_gdraw_command.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c", + test_sources_ant_glob = "test_gdraw_command_sequence.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " tests/fakes/fake_resource_syscalls.c" \ + " tests/fakes/fake_applib_resource.c", + runtime_deps=ctx.env.test_pdcs, + defines=ctx.env.test_image_defines, + test_sources_ant_glob = "test_gdraw_command_resources.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/8_bit/framebuffer.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/8_bit/bitblt_private.c" \ + + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/animation_timing.c" \ + + " tests/fakes/fake_gbitmap_png.c" \ + + " tests/fw/graphics/weather_app_resources.c" \ + + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_transforms.c", \ + test_sources_ant_glob = "test_gdraw_command_transforms.c", + defines=ctx.env.test_image_defines, + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/gtypes.c", + runtime_deps=ctx.env.pdcs2png_test_files, + test_sources_ant_glob="test_pdc.c", + defines=ctx.env.test_image_defines) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/gtypes.c", + test_sources_ant_glob="test_png.c", + defines=ctx.env.test_image_defines, + runtime_deps=filter(lambda x: 'test_png__' in str(x), ctx.env.test_pngs)) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gbitmap_sequence.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/board/displays/display_getafix.c" + " tests/fakes/fake_resource_syscalls.c", + test_sources_ant_glob="test_gbitmap_sequence.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, + platforms=['gabbro']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" \ + " tests/fakes/fake_gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_pbi.c", + defines=ctx.env.test_image_defines, + runtime_deps=filter(lambda x: 'test_pbi__' in str(x), ctx.env.test_pbis)) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_palettized_conversion.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis) + +clar(ctx, + sources_ant_glob=( + "src/fw/applib/graphics/gtypes.c " + "src/fw/apps/system/timeline/text_node.c " + "tests/stubs/stubs_text_layout.c " + ), + test_sources_ant_glob="test_graphics_text_node.c") + + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/perimeter.c" + " src/fw/applib/graphics/gtypes.c", + test_sources_ant_glob="test_perimeter.c", + test_libs=['m'], + defines=defines, + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" + " tests/fakes/fake_gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_graphics_draw_line.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_bitblt_palette_1bit.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, + platforms=['asterix']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/board/displays/display_getafix.c", + test_sources_ant_glob="test_framebuffer_duma.c", + defines=ctx.env.test_image_defines, + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" + " tests/fakes/fake_gbitmap_png.c" + " tests/fakes/fake_gbitmap_get_data_row.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_graphics_draw_core.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, + platforms=['obelix']) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" + " tests/fakes/fake_gbitmap_png.c" + " tests/fakes/fake_gbitmap_get_data_row.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_graphics_draw_implementation.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis) + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" + " tests/fakes/fake_gbitmap_png.c" + " tests/fakes/fake_gbitmap_get_data_row.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_bitmap.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/graphics.c", + test_sources_ant_glob="test_graphics_draw_rotated_bitmap.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis, + platforms=["obelix", "gabbro"]) + +############################### +# 8-bit specific bitblt tests # +############################### +graphics_sources_ant_glob = \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/8_bit/framebuffer.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/8_bit/bitblt_private.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/ui/layer.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " tests/fakes/fake_display.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" + +graphics_test_sources_8bit = [ + "test_bitblt.c", + "test_bitblt_palette.c" +] + +for test in graphics_test_sources_8bit: + defines = ctx.env.test_image_defines + + clar(ctx, + sources_ant_glob = graphics_sources_ant_glob, + test_sources_ant_glob = test, + defines=defines) + +# vim:filetype=python diff --git a/tests/fw/javascript/test_jerry_port_common.h b/tests/fw/javascript/test_jerry_port_common.h deleted file mode 100644 index 59f14ecb56..0000000000 --- a/tests/fw/javascript/test_jerry_port_common.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "jmem-heap.h" - -static int s_app_heap_analytics_log_stats_to_app_heartbeat_call_count; -void app_heap_analytics_log_stats_to_app_heartbeat(bool is_rocky_app) { - s_app_heap_analytics_log_stats_to_app_heartbeat_call_count++; -} - -static int s_app_heap_analytics_log_rocky_heap_oom_fault_call_count; -void app_heap_analytics_log_rocky_heap_oom_fault(void) { - s_app_heap_analytics_log_rocky_heap_oom_fault_call_count++; -} diff --git a/tests/fw/javascript/test_js.c b/tests/fw/javascript/test_js.c deleted file mode 100644 index 6138061f33..0000000000 --- a/tests/fw/javascript/test_js.c +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/graphics/gpath.h" -#include "applib/preferred_content_size.h" -#include "applib/rockyjs/api/rocky_api.h" -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_timers.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" -#include "applib/rockyjs/api/rocky_api_tickservice.h" -#include "applib/rockyjs/api/rocky_api_util.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include "syscall/syscall.h" - -// Standard -#include "string.h" -#include "applib/rockyjs/rocky.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_pbl_malloc.h" -#include "fake_time.h" -#include "fake_logging.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -const RockyGlobalAPI APP_MESSAGE_APIS = {}; -const RockyGlobalAPI WATCHINFO_APIS = {}; - -size_t heap_bytes_free(void) { - return 123456; -} - -void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { -} - -bool sys_get_current_app_is_rocky_app(void) { - return true; -} - -void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) {} - -static Window s_app_window_stack_get_top_window; -Window *app_window_stack_get_top_window() { - return &s_app_window_stack_get_top_window; -} - -PreferredContentSize preferred_content_size(void) { - return PreferredContentSizeMedium; -} - -static MockCallRecordings s_layer_mark_dirty; -void layer_mark_dirty(Layer *layer) { - s_layer_mark_dirty.call_count++; - s_layer_mark_dirty.last_call = (MockCallRecording){.layer = layer}; -} - -static MockCallRecordings s_graphics_context_set_fill_color; -void graphics_context_set_fill_color(GContext* ctx, GColor color) { - s_graphics_context_set_fill_color.call_count++; - s_graphics_context_set_fill_color.last_call = (MockCallRecording){.ctx = ctx, .color = color}; -} - -static MockCallRecordings s_graphics_context_set_stroke_color; -void graphics_context_set_stroke_color(GContext* ctx, GColor color) { - s_graphics_context_set_stroke_color.call_count++; - s_graphics_context_set_stroke_color.last_call = (MockCallRecording){.ctx = ctx, .color = color}; -} - -static MockCallRecordings s_graphics_context_set_stroke_width; -void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) { - s_graphics_context_set_stroke_width.call_count++; - s_graphics_context_set_stroke_width.last_call = - (MockCallRecording){.ctx = ctx, .width =stroke_width}; -} - - -static MockCallRecordings s_graphics_line_draw_precise_stroked; -void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) { - record_mock_call(s_graphics_line_draw_precise_stroked) { - .ctx = ctx, .pp0 = p0, .pp1 = p1, - }; -} - -static MockCallRecordings s_graphics_draw_line; -void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1) { - s_graphics_draw_line.call_count++; - s_graphics_draw_line.last_call = - (MockCallRecording){.ctx = ctx, .p0 = p0, .p1 = p1}; -} - -static MockCallRecordings s_graphics_fill_rect; -void graphics_fill_rect(GContext *ctx, const GRect *rect) { - s_graphics_fill_rect.call_count++; - s_graphics_fill_rect.last_call = (MockCallRecording){.ctx = ctx, .rect = *rect}; -} - -static MockCallRecordings s_graphics_fill_rect; -void graphics_fill_round_rect_by_value(GContext* ctx, GRect rect, uint16_t radius, - GCornerMask corner_mask) { - s_graphics_fill_rect.call_count++; - s_graphics_fill_rect.last_call = (MockCallRecording) { - .ctx = ctx, - .rect = rect, - .radius = radius, - .corner_mask = corner_mask, - }; -} - -GPointPrecise gpoint_from_polar_precise(const GPointPrecise *precise_center, - uint16_t precise_radius, int32_t angle) { - return GPointPreciseFromGPoint(GPointZero); -} - -void graphics_draw_arc_precise_internal(GContext *ctx, GPointPrecise center, Fixed_S16_3 radius, - int32_t angle_start, int32_t angle_end) {} - -void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {} - -void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center, - Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer, - int32_t angle_start, int32_t angle_end) {} - -void gpath_draw_filled(GContext* ctx, GPath *path) {} - -void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) { - *bounds_out = layer->bounds; -} - -GFont fonts_get_system_font(const char *font_key) { - return (GFont)123; -} - -void graphics_draw_text(GContext *ctx, const char *text, GFont const font, const GRect box, - const GTextOverflowMode overflow_mode, const GTextAlignment alignment, - GTextAttributes *text_attributes) {} - -void graphics_text_attributes_destroy(GTextAttributes *text_attributes) {} - -GSize graphics_text_layout_get_max_used_size(GContext *ctx, const char *text, - GFont const font, const GRect box, - const GTextOverflowMode overflow_mode, - const GTextAlignment alignment, - GTextLayoutCacheRef layout) { - return GSizeZero; -} - -uint32_t resource_storage_get_num_entries(ResAppNum app_num, uint32_t resource_id) { - return 0; -} - -static bool s_skip_mem_leak_check; - -static void prv_init(void) { - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); -} - -static void prv_deinit(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); -} - -void test_js__initialize(void) { - fake_pbl_malloc_clear_tracking(); - s_skip_mem_leak_check = false; - - fake_app_timer_init(); - prv_init(); - s_app_window_stack_get_top_window = (Window){}; - s_app_state_get_graphics_context = NULL; - - s_layer_mark_dirty = (MockCallRecordings){}; - s_graphics_context_set_fill_color = (MockCallRecordings){}; - s_graphics_context_set_stroke_color = (MockCallRecordings){}; - s_graphics_context_set_stroke_width = (MockCallRecordings){}; - s_graphics_line_draw_precise_stroked = (MockCallRecordings){}; - s_graphics_draw_line = (MockCallRecordings){}; - s_graphics_fill_rect = (MockCallRecordings){}; - - s_app_event_loop_callback = NULL; - s_log_internal__expected = NULL; - s_app_heap_analytics_log_stats_to_app_heartbeat_call_count = 0; - s_app_heap_analytics_log_rocky_heap_oom_fault_call_count = 0; -} - -void test_js__cleanup(void) { - fake_app_timer_deinit(); - s_log_internal__expected = NULL; - - // some tests deinitialize the engine, avoid double de-init - if (app_state_get_rocky_runtime_context() != NULL) { - prv_deinit(); - } - - // PBL-40702: test_js__init_deinit is leaking memory... - if (!s_skip_mem_leak_check) { - fake_pbl_malloc_check_net_allocs(); - } -} - -void test_js__addition(void) { - char script[] = "var a = 1; var b = 2; var c = a + b;"; - EXECUTE_SCRIPT(script); - ASSERT_JS_GLOBAL_EQUALS_I("c", 3.0); -} - -void test_js__eval_error(void) { - prv_deinit(); // engine will be re-initialized in rocky_event_loop~ - char script[] = "function f({;"; - s_log_internal__expected = (const char *[]){ - "Not a snapshot, interpreting buffer as JS source code", - "Exception while Evaluating JS", - "SyntaxError: Identifier expected. [line: 1, column: 12]", - NULL }; - rocky_event_loop_with_string_or_snapshot(script, sizeof(script)); - cl_assert(*s_log_internal__expected == NULL); -} - -AppTimer *rocky_timer_get_app_timer(void *data); - -void test_js__init_deinit(void) { - // PBL-40702: test_js__init_deinit is leaking memory... - s_skip_mem_leak_check = true; - - prv_deinit(); - - char *script = - "var num_times = 0;" - "var extra_arg = 0;" - "var timer = setInterval(function(extra) {" - "num_times++;" - "extra_arg = extra;" - "}, 1000, 5);"; - - for (int i = 0; i < 30; ++i) { - prv_init(); - TIMER_APIS.init(); - EXECUTE_SCRIPT(script); - prv_deinit(); - } - - prv_init(); -} - -static char *prv_load_js(char *suffix) { - char path[512] = {0}; - snprintf(path, sizeof(path), "%s/js/tictoc~rect~%s.js", CLAR_FIXTURE_PATH, suffix); - FILE *f = fopen(path, "r"); - cl_assert(f); - fseek(f, 0, SEEK_END); - size_t length = (size_t)ftell(f); - fseek (f, 0, SEEK_SET); - char *buffer = malloc(length + 1); - memset(buffer, 0, length + 1); - cl_assert(buffer); - fread (buffer, 1, length, f); - fclose (f); - return buffer; -} - -void test_js__call_cleanup_twice(void) { - prv_deinit(); - char *script = "function f(i) { return i * 4; } f(5);"; - bool result = rocky_event_loop_with_string_or_snapshot(script, strlen(script)); - cl_assert(result); -} - -static bool s_tictoc_callback_is_color; -static void prv_rocky_tictoc_callback(void) { - Layer *root_layer = &s_app_window_stack_get_top_window.layer; - root_layer->bounds = GRect(10, 20, 30, 40); - cl_assert(root_layer->update_proc); - GContext ctx = {.lock = true}; - root_layer->update_proc(root_layer, &ctx); - - if (s_tictoc_callback_is_color) { - cl_assert_equal_i(1, s_graphics_fill_rect.call_count); - cl_assert_equal_i(4, s_graphics_line_draw_precise_stroked.call_count); - cl_assert_equal_i(0, s_graphics_draw_line.call_count); - cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count); - cl_assert_equal_i(4, s_graphics_context_set_stroke_color.call_count); - cl_assert_equal_i(4, s_graphics_context_set_stroke_width.call_count); - } else { - cl_assert_equal_i(2, s_graphics_fill_rect.call_count); - cl_assert_equal_i(0, s_graphics_line_draw_precise_stroked.call_count); - cl_assert_equal_i(0, s_graphics_draw_line.call_count); - cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count); - cl_assert_equal_i(0, s_graphics_context_set_stroke_color.call_count); - cl_assert_equal_i(0, s_graphics_context_set_stroke_width.call_count); - } - - // run update proc multiple times to verify we don't have a memory leak - for (int i = 1024; i >=0; i--) { - root_layer->update_proc(root_layer, &ctx); - } -} - -void test_js__rocky_tictoc_color(void) { - prv_deinit(); // engine will be re-initialized in rocky_event_loop~ - char *script = prv_load_js("color"); - s_tictoc_callback_is_color = true; - s_app_event_loop_callback = prv_rocky_tictoc_callback; - bool result = rocky_event_loop_with_string_or_snapshot(script, strlen(script)); - cl_assert(result); -} - -void test_js__rocky_tictoc_bw(void) { - GContext ctx = {}; - s_app_state_get_graphics_context = &ctx; - prv_deinit(); // engine will be re-initialized in rocky_event_loop~ - char *script = prv_load_js("bw"); - s_tictoc_callback_is_color = false; - s_app_event_loop_callback = prv_rocky_tictoc_callback; - bool result = rocky_event_loop_with_string_or_snapshot(script, strlen(script)); - cl_assert(result); -} - -void test_js__recursion(void) { - const char script[] = - "function f(i) { \n" - " if (i == 0) {_rocky.requestDraw();} \n" - " else {f(i-1)}\n" - "}\n" - "f(10)"; - static const RockyGlobalAPI *apis[] = { - &GRAPHIC_APIS, - NULL, - }; - rocky_global_init(apis); - EXECUTE_SCRIPT(script); - - cl_assert_equal_i(1, s_layer_mark_dirty.call_count); -} - -void test_js__no_print_builtin(void) { - JS_VAR global_obj = jerry_get_global_object(); - JS_VAR print_builtin = jerry_get_object_field(global_obj, "print"); - cl_assert_equal_b(true, jerry_value_is_undefined(print_builtin)); -} - -void test_js__sin_cos(void) { - EXECUTE_SCRIPT( - "var s1 = 100 + 50 * Math.sin(0);\n" - "var s2 = 100 + 50 * Math.sin(2 * Math.PI);\n" - "var c1 = 100 + 50 * Math.cos(0);\n" - "var c2 = 100 + 50 * Math.cos(2 * Math.PI);\n" - ); - cl_assert_equal_i(100, (int32_t)jerry_get_number_value(prv_js_global_get_value("s1"))); - cl_assert_equal_i(99, (int32_t)jerry_get_number_value(prv_js_global_get_value("s2"))); - cl_assert_equal_i(150, (int32_t)jerry_get_number_value(prv_js_global_get_value("c1"))); - cl_assert_equal_i(150, (int32_t)jerry_get_number_value(prv_js_global_get_value("c2"))); - - cl_assert_equal_i(100, jerry_get_int32_value(prv_js_global_get_value("s1"))); - cl_assert_equal_i(100, jerry_get_int32_value(prv_js_global_get_value("s2"))); - cl_assert_equal_i(150, jerry_get_int32_value(prv_js_global_get_value("c1"))); - cl_assert_equal_i(150, jerry_get_int32_value(prv_js_global_get_value("c2"))); -} - -void test_js__date(void) { - const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC - // Thu Mar 17 14:40:51 2016 PDT - const uint16_t cur_millis = 123; - fake_time_init(cur_time, cur_millis); - fake_time_set_gmtoff(-8 * 60 * 60); // PST - fake_time_set_dst(1 * 60 * 60, 1458111600, 1465628400); // PDT 3/16 -> 11/6 2016 - - char *script = - "var date_now = new Date();" - "var now = date_now.getTime();" - "var local_day = date_now.getDay();" - "var local_hour = date_now.getHours();"; - EXECUTE_SCRIPT(script); - - ASSERT_JS_GLOBAL_EQUALS_D("now", (double)cur_time * 1000.0 + (double)cur_millis); - ASSERT_JS_GLOBAL_EQUALS_D("local_day", 4.0); // Thursday - ASSERT_JS_GLOBAL_EQUALS_D("local_hour", 14.0); // 1pm -} - -void test_js__log_exception(void) { - char *script = - "var e1;\n" - "var f1 = function(){throw new Error('test')};\n" - "var f2 = function(){throw new 'test';};\n" - "var f2 = function(){throw new 123;};\n" - "try {f1();} catch(e) {e1 = e;}\n" - "try {f2();} catch(e) {e2 = e;}\n" - "try {f3();} catch(e) {e3 = e;}\n"; - - EXECUTE_SCRIPT(script); - jerry_value_t e1 = prv_js_global_get_value("e1"); - jerry_value_t e2 = prv_js_global_get_value("e2"); - jerry_value_t e3 = prv_js_global_get_value("e3"); - - // error - s_log_internal__expected = (const char *[]){ - "Exception while e1", "Error: test", NULL, - }; - rocky_log_exception("e1", e1); - cl_assert(*s_log_internal__expected == NULL); - - // string - s_log_internal__expected = (const char *[]){ - "Exception while e2", "TypeError", NULL, - }; - rocky_log_exception("e2", e2); - cl_assert(*s_log_internal__expected == NULL); - - // number - s_log_internal__expected = (const char *[]){ - "Exception while e3", "ReferenceError", NULL, - }; - rocky_log_exception("e3", e3); - cl_assert(*s_log_internal__expected == NULL); -} - -/* - * FIXME: JS Tests should be built in a 32-bit env -void test_js__size(void) { - cl_assert_equal_i(4, sizeof(size_t)); -} -*/ - -void test_js__snapshot(void) { - prv_deinit(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_SHOW_OPCODES); - char *const script = prv_load_js("color"); - uint8_t snapshot[65536] = { 0 }; - - // make sure snapshot data starts with expected Rocky header - const size_t header_size = sizeof(ROCKY_EXPECTED_SNAPSHOT_HEADER); - cl_assert_equal_i(8, header_size); - // NOTE: the snapshot header in this unit test is fixed to - // CAPABILITY_JAVASCRIPT_BYTECODE_VERSION=1 only use the resulting binary - // if the true JS version matches - memcpy(snapshot, &ROCKY_EXPECTED_SNAPSHOT_HEADER, header_size); - const size_t snapshot_size = jerry_parse_and_save_snapshot((const jerry_char_t *)script, - strlen(script), - true, /* is_for_global */ - false, /* is_strict */ - snapshot + header_size, - sizeof(snapshot) - header_size); - cl_assert(snapshot_size > 512); // make sure it contains "something" and compiling didn't fail - - prv_deinit(); - - bool result = rocky_event_loop_with_string_or_snapshot(snapshot, snapshot_size); - cl_assert(result); -} - -static int s_cleanup_calls; -static void prv_cleanup_cb(const uintptr_t native_p) { - ++s_cleanup_calls; -} - -void test_js__js_value_cleanup(void) { - s_cleanup_calls = 0; - - { - // Sanity check: - // we don't clean up when a bare jerry_value_t goes out of scope. - jerry_value_t value = jerry_create_object(); - jerry_set_object_native_handle(value, 0, prv_cleanup_cb); - } - jerry_gc(); // Perform GC in case refcount = 0 - cl_assert_equal_i(s_cleanup_calls, 0); // Never release()d, so wasn't cleaned. - - { - // When this goes out of scope, we do clean up - JS_VAR value = jerry_create_object(); - jerry_set_object_native_handle(value, 0, prv_cleanup_cb); - } - jerry_gc(); // Perform GC so it will be cleaned up if refcount = 0 - cl_assert_equal_i(s_cleanup_calls, 1); // Make sure that it was cleaned up - - { - // Create a regular value, attach the native handle - jerry_value_t value = jerry_create_object(); - jerry_set_object_native_handle(value, 0, prv_cleanup_cb); - - // Create an autoreleased variable that points to the same, it will be cleaned up. - JS_UNUSED_VAL = value; - } - jerry_gc(); - cl_assert_equal_i(s_cleanup_calls, 2); - - { - // Naming check on unused variables, shouldn't clash. - // This is really just a compile-time test. - JS_UNUSED_VAL = jerry_create_object(); - JS_UNUSED_VAL = jerry_create_object(); - } -} - -void test_js__get_global_builtin(void) { - jerry_value_t date_builtin = jerry_get_global_builtin((const jerry_char_t *)"Date"); - cl_assert(!jerry_value_is_undefined(date_builtin)); - cl_assert(jerry_value_is_constructor(date_builtin)); - jerry_release_value(date_builtin); - - jerry_value_t json_builtin = jerry_get_global_builtin((const jerry_char_t *)"JSON"); - cl_assert(jerry_value_is_object(json_builtin)); - jerry_release_value(json_builtin); - - jerry_value_t not_builtin = jerry_get_global_builtin((const jerry_char_t *)"_not_builtin_"); - cl_assert(jerry_value_is_undefined(not_builtin)); -} - -void test_js__get_global_builtin_compare(void) { - jerry_value_t date_builtin = jerry_get_global_builtin((const jerry_char_t *)"Date"); - jerry_value_t global_object = jerry_get_global_object(); - - // Compare that the global Date is the same object as the builtin - jerry_value_t global_date = jerry_get_object_field(global_object, "Date"); - cl_assert(date_builtin == global_date); - - jerry_release_value(global_date); - jerry_release_value(global_object); - jerry_release_value(date_builtin); -} - -void test_js__get_global_builtin_changed(void) { - jerry_value_t date_builtin = jerry_get_global_builtin((const jerry_char_t *)"Date"); - jerry_value_t global_object = jerry_get_global_object(); - - const char *source = "Date = 'some string';"; - jerry_eval((const jerry_char_t *)source, strlen(source), false); - - // After changing the global date object, it should not match our builtin - jerry_value_t global_date = jerry_get_object_field(global_object, "Date"); - cl_assert(jerry_value_is_string(global_date)); - cl_assert(date_builtin != global_date); - - jerry_release_value(global_date); - jerry_release_value(global_object); - jerry_release_value(date_builtin); -} - -void test_js__capture_mem_stats_upon_exiting_event_loop(void) { - prv_deinit(); - - s_app_event_loop_callback = NULL; - const char *source = ";"; - cl_assert_equal_b(true, rocky_event_loop_with_string_or_snapshot(source, strlen(source))); - cl_assert_equal_i(s_app_heap_analytics_log_stats_to_app_heartbeat_call_count, 1); -} - -void test_js__jmem_heap_stats_largest_free_block_bytes(void) { - jmem_heap_stats_t stats = {}; - jmem_heap_get_stats(&stats); - // Note: this might fail in the future if JerryScript would happen to cause fragmentation right - // upon initializing the engine: - cl_assert_equal_i(stats.size - stats.allocated_bytes, stats.largest_free_block_bytes); -} - -void test_js__capture_jerry_heap_oom_stats(void) { - const char *source = "var big = []; for (;;) { big += 'bigger'; };"; - cl_assert_passert(jerry_eval((const jerry_char_t *)source, strlen(source), false)); - cl_assert_equal_i(s_app_heap_analytics_log_rocky_heap_oom_fault_call_count, 1); -} diff --git a/tests/fw/javascript/test_rocky_api_app_message.c b/tests/fw/javascript/test_rocky_api_app_message.c deleted file mode 100644 index dbb99c5c37..0000000000 --- a/tests/fw/javascript/test_rocky_api_app_message.c +++ /dev/null @@ -1,971 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_app_message.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include "applib/app_message/app_message.h" -#include "util/dict.h" -#include "util/size.h" - -#include - -// Fakes -#include "fake_app_timer.h" -#include "fake_event_service.h" -#include "fake_pbl_malloc.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_state.h" -#include "stubs_comm_session.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_serial.h" -#include "stubs_sys_exit.h" - -extern PostMessageState rocky_api_app_message_get_state(void); -extern AppTimer *rocky_api_app_message_get_app_msg_retry_timer(void); -extern AppTimer *rocky_api_app_message_get_session_closed_object_queue_timer(void); - -T_STATIC jerry_value_t prv_json_stringify(jerry_value_t object); -T_STATIC jerry_value_t prv_json_parse(const char *); - -T_STATIC void prv_handle_connection(void); -T_STATIC void prv_handle_disconnection(void); - -// App message mocks - -static AppMessageInboxReceived s_received_callback; -AppMessageInboxReceived app_message_register_inbox_received( - AppMessageInboxReceived received_callback) { - AppMessageInboxReceived prev_cb = s_received_callback; - s_received_callback = received_callback; - return prev_cb; -} - -static AppMessageInboxDropped s_dropped_callback; -AppMessageInboxDropped app_message_register_inbox_dropped(AppMessageInboxDropped dropped_callback) { - AppMessageInboxDropped prev_cb = s_dropped_callback; - s_dropped_callback = dropped_callback; - return prev_cb; -} - -static AppMessageOutboxSent s_sent_callback; -AppMessageOutboxSent app_message_register_outbox_sent(AppMessageOutboxSent sent_callback) { - AppMessageOutboxSent prev_cb = s_sent_callback; - s_sent_callback = sent_callback; - return prev_cb; -} - -static AppMessageOutboxFailed s_failed_callback; -AppMessageOutboxFailed app_message_register_outbox_failed(AppMessageOutboxFailed failed_callback) { - AppMessageOutboxFailed prev_cb = s_failed_callback; - s_failed_callback = failed_callback; - return prev_cb; -} - -void app_message_deregister_callbacks(void) { - s_received_callback = NULL; - s_dropped_callback = NULL; - s_sent_callback = NULL; - s_failed_callback = NULL; -} - -static uint32_t s_inbox_size; -static uint32_t s_outbox_size; -AppMessageResult app_message_open(const uint32_t size_inbound, const uint32_t size_outbound) { - s_inbox_size = size_inbound; - s_outbox_size = size_outbound; - return APP_MSG_OK; -} - -static bool s_is_outbox_message_pending; -static DictionaryIterator s_outbox_iterator; -static uint8_t *s_outbox_buffer; -AppMessageResult app_message_outbox_begin(DictionaryIterator **iterator) { - cl_assert_equal_b(s_is_outbox_message_pending, false); - if (!s_outbox_buffer) { - s_outbox_buffer = malloc(s_outbox_size); - } - dict_write_begin(&s_outbox_iterator, s_outbox_buffer, s_outbox_size); - *iterator = &s_outbox_iterator; - - return APP_MSG_OK; -} - -static int s_app_message_outbox_send_call_count; -AppMessageResult app_message_outbox_send(void) { - ++s_app_message_outbox_send_call_count; - s_is_outbox_message_pending = true; - return APP_MSG_OK; -} - -static bool s_comm_session_connected; -CommSession *sys_app_pp_get_comm_session(void) { - return (CommSession *)s_comm_session_connected; -} - -static void prv_rcv_app_message_ack(AppMessageResult result) { - void *context = NULL; - cl_assert_equal_b(s_is_outbox_message_pending, true); - s_is_outbox_message_pending = false; - if (result == APP_MSG_OK) { - s_sent_callback(&s_outbox_iterator, context); - } else { - s_failed_callback(&s_outbox_iterator, result, context); - } -} - -static void prv_app_message_setup(void) { - s_inbox_size = 0; - s_outbox_size = 0; - s_outbox_buffer = NULL; - s_app_message_outbox_send_call_count = 0; - s_is_outbox_message_pending = false; - app_message_deregister_callbacks(); -} - -static void prv_app_message_teardown(void) { - if (s_outbox_buffer) { - free(s_outbox_buffer); - } -} - -// Statics and Utilities - -static void prv_init_and_goto_session_open(void); - -static void prv_simulate_transport_connection_event(bool is_connected) { - // FIXME: use events here instead of poking at the internals! - if (is_connected) { - prv_handle_connection(); - } else { - prv_handle_disconnection(); - } -} - -static const RockyGlobalAPI *s_app_message_api[] = { - &APP_MESSAGE_APIS, - NULL, -}; - -static void prv_init_api(bool start_connected) { - s_comm_session_connected = start_connected; - rocky_global_init(s_app_message_api); -} - -// Setup - -void test_rocky_api_app_message__initialize(void) { - fake_app_timer_init(); - fake_pbl_malloc_clear_tracking(); - prv_app_message_setup(); - - s_process_manager_callback = NULL; - s_process_manager_callback_data = NULL; - - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); -} - -void test_rocky_api_app_message__cleanup(void) { - rocky_global_deinit(); - jerry_cleanup(); - rocky_runtime_context_deinit(); - prv_app_message_teardown(); - fake_pbl_malloc_check_net_allocs(); - fake_app_timer_deinit(); -} - -static const PostMessageResetCompletePayload VALID_RESET_COMPLETE = { - .min_supported_version = POSTMESSAGE_PROTOCOL_MIN_VERSION, - .max_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION, - .max_tx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_TX_CHUNK_SIZE, - .max_rx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_RX_CHUNK_SIZE, -}; - -static const size_t TINY_CHUNK_SIZE = 4; - -static const PostMessageResetCompletePayload TINY_RESET_COMPLETE = { - .min_supported_version = POSTMESSAGE_PROTOCOL_MIN_VERSION, - .max_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION, - .max_tx_chunk_size = TINY_CHUNK_SIZE, - .max_rx_chunk_size = TINY_CHUNK_SIZE, -}; - -#define RCV_APP_MESSAGE(...) \ - do { \ - Tuplet tuplets[] = { __VA_ARGS__ }; \ - uint32_t buffer_size = dict_calc_buffer_size_from_tuplets(tuplets, ARRAY_LENGTH(tuplets)); \ - uint8_t buffer[buffer_size]; \ - DictionaryIterator it; \ - const DictionaryResult result = \ - dict_serialize_tuplets_to_buffer_with_iter(&it, tuplets, ARRAY_LENGTH(tuplets), \ - buffer, &buffer_size); \ - cl_assert_equal_i(DICT_OK, result); \ - if (s_received_callback) { \ - s_received_callback(&it, NULL); \ - } \ - } while(0); - - -#define RCV_RESET_REQUEST() \ - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetRequest, NULL, 0)); - -#define RCV_RESET_COMPLETE() \ - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete, \ - (const uint8_t *)&VALID_RESET_COMPLETE, sizeof(VALID_RESET_COMPLETE))); - -#define RCV_DUMMY_CHUNK() \ - do { \ - PostMessageChunkPayload chunk = {}; \ - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyChunk, (const uint8_t *) &chunk, sizeof(chunk))); \ - } while(0); - -//! Asserts whether the outbox has a pending message containing the tuples passed to this macro. -//! The value and type of the tuples is also asserted. -//! @note Only asserts if expected tuples are MISSING. It will not trip if there are other -//! (non-expected) tuples in the set. -#define EXPECT_OUTBOX_MESSAGE_PENDING(...) \ - do { \ - cl_assert_equal_b(true, s_is_outbox_message_pending); \ - /* The cursor must be updated! */ \ - cl_assert(s_outbox_iterator.cursor != s_outbox_iterator.dictionary->head); \ - Tuplet tuplets[] = { __VA_ARGS__ }; \ - uint32_t buffer_size = dict_calc_buffer_size_from_tuplets(tuplets, ARRAY_LENGTH(tuplets)); \ - uint8_t buffer[buffer_size]; \ - DictionaryIterator expected_it; \ - const DictionaryResult result = \ - dict_serialize_tuplets_to_buffer_with_iter(&expected_it, tuplets, ARRAY_LENGTH(tuplets), \ - buffer, &buffer_size); \ - cl_assert_equal_i(DICT_OK, result); \ - for (Tuple *expected_t = dict_read_first(&expected_it); expected_t != NULL; \ - expected_t = dict_read_next(&expected_it)) { \ - Tuple *found_t = dict_find(&s_outbox_iterator, expected_t->key); \ - cl_assert(found_t); \ - cl_assert_equal_i(found_t->type, expected_t->type); \ - cl_assert_equal_i(found_t->length, expected_t->length); \ - if (expected_t->length) { \ - cl_assert_equal_i(0, memcmp(found_t->value[0].data, expected_t->value[0].data, \ - expected_t->length)); \ - } \ - } \ - } while (0); - -#define EXPECT_OUTBOX_NO_MESSAGE_PENDING() \ - cl_assert_equal_b(false, s_is_outbox_message_pending); - -#define EXPECT_OUTBOX_RESET_REQUEST() \ - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyResetRequest, NULL, 0)); - -#define EXPECT_OUTBOX_RESET_COMPLETE_PENDING() \ - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyResetComplete, \ - (const uint8_t *) &VALID_RESET_COMPLETE, \ - sizeof(VALID_RESET_COMPLETE))); - -//////////////////////////////////////////////////////////////////////////////// -// Negotiation Steps -//////////////////////////////////////////////////////////////////////////////// - -void test_rocky_api_app_message__disconnected__ignore_any_app_message(void) { - prv_init_api(false /* start_connected */); - - for (PostMessageKey key = PostMessageKeyResetRequest; key < PostMessageKey_Count; ++key) { - uint8_t dummy_data[] = {0, 1, 2}; - RCV_APP_MESSAGE(TupletBytes(key, dummy_data, sizeof(dummy_data))); - } - - cl_assert_equal_i(0, s_app_message_outbox_send_call_count); - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateDisconnected); -} - -void test_rocky_api_app_message__awaiting_reset_request__receive_reset_request(void) { - prv_init_api(true /* start_connected */); - - RCV_RESET_REQUEST(); - - // Expect responding with a ResetComplete: - EXPECT_OUTBOX_RESET_COMPLETE_PENDING(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteRemoteInitiated); -} - -void test_rocky_api_app_message__awaiting_reset_request__receive_chunk(void) { - prv_init_api(false /* start_connected */); - prv_simulate_transport_connection_event(true /* is_connected */); - - RCV_DUMMY_CHUNK(); - // https://pebbletechnology.atlassian.net/browse/PBL-42466 - // TODO: assert that app message was NACK'd - - // Expect responding with a ResetRequest: - EXPECT_OUTBOX_RESET_REQUEST(); - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyResetRequest, NULL, 0)); - // TODO: check fields - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteLocalInitiated); -} - -void test_rocky_api_app_message__awaiting_reset_request__disconnect(void) { - prv_init_api(false /* start_connected */); - prv_simulate_transport_connection_event(true /* is_connected */); - prv_simulate_transport_connection_event(false /* is_connected */); - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateDisconnected); -} - -static void prv_init_and_goto_awaiting_reset_complete_remote_initiated(void) { - prv_init_api(true /* start_connected */); - - RCV_RESET_REQUEST(); - - EXPECT_OUTBOX_RESET_COMPLETE_PENDING(); - prv_rcv_app_message_ack(APP_MSG_OK); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteRemoteInitiated); -} - -static void prv_init_and_goto_awaiting_reset_complete_local_initiated(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - RCV_DUMMY_CHUNK(); - EXPECT_OUTBOX_RESET_REQUEST(); - prv_rcv_app_message_ack(APP_MSG_OK); - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteLocalInitiated); -} - -void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_complete_valid_version(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - - RCV_RESET_COMPLETE(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen); - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); -} - -void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_complete_unsupported_ver(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - - const PostMessageResetCompletePayload unsupported = { - .min_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION + 1, - .max_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION + 1, - .max_tx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_TX_CHUNK_SIZE, - .max_rx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_RX_CHUNK_SIZE, - }; - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete, - (const uint8_t *)&unsupported, sizeof(unsupported))); - - // Expect No UnsupportedError! - - // Immediately go back to AwaitingResetRequest: - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateAwaitingResetRequest); - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); -} - -void test_rocky_api_app_message__awaiting_reset_complete_rem_init__malformed_reset_complete(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - - // Receive malformed ResetComplete: - uint8_t malformed_payload[sizeof(PostMessageResetCompletePayload) - 1] = {}; - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete, - malformed_payload, sizeof(malformed_payload))); - - // Expect Error: - const PostMessageUnsupportedErrorPayload expected_error = { - .error_code = PostMessageErrorMalformedResetComplete, - }; - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyUnsupportedError, - (const uint8_t *) &expected_error, - sizeof(expected_error))); - - // Immediately go back to AwaitingResetRequest: - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateAwaitingResetRequest); - prv_rcv_app_message_ack(APP_MSG_OK); - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); -} - -void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_request(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - - RCV_RESET_REQUEST(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteRemoteInitiated); - - EXPECT_OUTBOX_RESET_COMPLETE_PENDING(); -} - -void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_chunk(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - - RCV_DUMMY_CHUNK(); - - EXPECT_OUTBOX_RESET_REQUEST(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteLocalInitiated); - - // Receive yet another chunk in "Awaiting Reset Complete Local Initiated": - RCV_DUMMY_CHUNK(); - // https://pebbletechnology.atlassian.net/browse/PBL-42466 - // TODO: assert that chunk is NACKd - - // Receive ACK for the ResetRequest: - prv_rcv_app_message_ack(APP_MSG_OK); - - // Chunk is ignored, no new reset request is sent out. - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); - - // TODO: timeout + retry ResetRequest if no ResetComplete follows within N secs. -} - -void test_rocky_api_app_message__awaiting_reset_complete_loc_init__(void) { - prv_init_and_goto_awaiting_reset_complete_local_initiated(); -} - -void test_rocky_api_app_message__awaiting_reset_complete_loc_init__rcv_reset_request(void) { - prv_init_and_goto_awaiting_reset_complete_local_initiated(); - - RCV_RESET_REQUEST(); - - EXPECT_OUTBOX_RESET_COMPLETE_PENDING(); - prv_rcv_app_message_ack(APP_MSG_OK); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteRemoteInitiated); - - RCV_RESET_COMPLETE(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen); -} - -void test_rocky_api_app_message__awaiting_reset_complete_loc_init__rcv_chunk(void) { - prv_init_and_goto_awaiting_reset_complete_local_initiated(); - - RCV_DUMMY_CHUNK(); - - // https://pebbletechnology.atlassian.net/browse/PBL-42466 - // TODO: assert that chunk is NACK'd - - // Chunk is ignored, state isn't changed: - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteLocalInitiated); - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); -} - -static void prv_init_and_goto_session_open(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - RCV_RESET_COMPLETE(); - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen); -} - -void test_rocky_api_app_message__session_open__rcv_reset_request(void) { - prv_init_and_goto_session_open(); - - EXECUTE_SCRIPT("var isCalled = false;" - "_rocky.on('postmessagedisconnected', function() { isCalled = true; });"); - - ASSERT_JS_GLOBAL_EQUALS_B("isCalled", false); - - RCV_RESET_REQUEST(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteRemoteInitiated); - EXPECT_OUTBOX_RESET_COMPLETE_PENDING(); - - ASSERT_JS_GLOBAL_EQUALS_B("isCalled", true); - - // TODO: assert: - // - flushed recv chunk reassembly buffer -} - -void test_rocky_api_app_message__session_open__rcv_reset_complete(void) { - prv_init_and_goto_session_open(); - - EXECUTE_SCRIPT("var isCalled = false;" - "_rocky.on('postmessagedisconnected', function() { isCalled = true; });"); - - ASSERT_JS_GLOBAL_EQUALS_B("isCalled", false); - - RCV_RESET_COMPLETE(); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteLocalInitiated); - EXPECT_OUTBOX_RESET_REQUEST(); - - ASSERT_JS_GLOBAL_EQUALS_B("isCalled", true); - - // TODO: assert: - // - flushed recv chunk reassembly buffer -} - -//////////////////////////////////////////////////////////////////////////////// -// postmessageconnected / postmessagedisconnected -//////////////////////////////////////////////////////////////////////////////// - -static void prv_postmessageconnected_postmessagedisconnected_init(bool start_connected) { - prv_init_api(start_connected); - - EXECUTE_SCRIPT("var c = 0; var d = 0;\n" - "_rocky.on('postmessageconnected', function() { c++; });\n" - "_rocky.on('postmessagedisconnected', function() { d++; });\n"); - - // Make sure this race is handled (see comment in prv_handle_connection()): - prv_simulate_transport_connection_event(start_connected /* is_connected */); -} - -static void prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(void) { - // Negotiate: - RCV_RESET_REQUEST(); - - EXPECT_OUTBOX_RESET_COMPLETE_PENDING(); - prv_rcv_app_message_ack(APP_MSG_OK); - - cl_assert_equal_i(rocky_api_app_message_get_state(), - PostMessageStateAwaitingResetCompleteRemoteInitiated); - - RCV_RESET_COMPLETE(); - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen); -} - -void test_rocky_api_app_message__postmessageconnected_and_postmessagedisconnected_remote_rr(void) { - prv_postmessageconnected_postmessagedisconnected_init(false /* start_connected */); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - prv_simulate_transport_connection_event(true /* is_connected */); - ASSERT_JS_GLOBAL_EQUALS_I("c", 0); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(); - ASSERT_JS_GLOBAL_EQUALS_I("c", 1); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - - // Get a ResetRequest: - RCV_RESET_REQUEST(); - ASSERT_JS_GLOBAL_EQUALS_I("d", 2); -} - -void test_rocky_api_app_message__postmessageconnected_and_postmessagedisconnected_local_rr(void) { - prv_postmessageconnected_postmessagedisconnected_init(false /* start_connected */); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - prv_simulate_transport_connection_event(true /* is_connected */); - ASSERT_JS_GLOBAL_EQUALS_I("c", 0); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(); - ASSERT_JS_GLOBAL_EQUALS_I("c", 1); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - - // Get a ResetComplete (unexpected message), should trigger initiating (local) ResetRequest: - RCV_RESET_COMPLETE(); - ASSERT_JS_GLOBAL_EQUALS_I("d", 2); -} - -void test_rocky_api_app_message__postmessageconnected_and_postmessagedisconnected_start_conn(void) { - prv_postmessageconnected_postmessagedisconnected_init(true /* start_connected */); - ASSERT_JS_GLOBAL_EQUALS_I("c", 0); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - - prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(); - - ASSERT_JS_GLOBAL_EQUALS_I("c", 1); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); -} - -// TODO: test various min/max version combos -// TODO: test RX/TX buffer size combos - -//////////////////////////////////////////////////////////////////////////////// -// Generic Tests -//////////////////////////////////////////////////////////////////////////////// - -void test_rocky_api_app_message__json_stringify(void) { - JS_VAR obj = jerry_create_object(); - JS_VAR json_str = prv_json_stringify(obj); - char *json_c_str = rocky_string_alloc_and_copy(json_str); - cl_assert_equal_s(json_c_str, "{}"); - task_free(json_c_str); -} - -void test_rocky_api_app_message__json_parse(void) { - JS_VAR number = prv_json_parse("1"); - cl_assert(jerry_value_is_number(number)); - cl_assert_equal_d(jerry_get_number_value(number), 1.0); - - JS_VAR object = prv_json_parse("{ \"x\" : 42 }"); - cl_assert(jerry_value_is_object(object)); - JS_VAR x = jerry_get_object_field(object, "x"); - cl_assert(jerry_value_is_number(x)); - cl_assert_equal_d(jerry_get_number_value(x), 42.0); -} - -void test_rocky_api_app_message__json_parse_stress(void) { - const int num_attempts = 0x3ff + 10; // Want this to be higher than the max refcount, - // which will also be high enough for a memory stress test - for (int i = 0; i < num_attempts; ++i) { - JS_UNUSED_VAL = prv_json_parse( - "var msg = { " - "\"key\" : " - "\"Bacon ipsum dolor amet kevin filet mignon id ut, aute sausage tri-tip " - "frankfurter pork loin. Boudin ullamco landjaeger, kevin tongue minim tri-tip " - "ground round dolore. Ham hock tongue swine, cillum jowl pancetta fugiat " - "deserunt sirloin fatback tenderloin culpa andouille. Incididunt qui bacon " - "nostrud ham hock adipisicing et ham. Ullamco esse eu capicola, ea culpa irure " - "meatball proident laboris ut reprehenderit ex incididunt.\" };\n"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// .postMessage() Tests -//////////////////////////////////////////////////////////////////////////////// - -#define SIMPLE_TEST_OBJECT "{ \"x\" : 1 }" - -static void prv_assert_simple_test_object_pending(void) { - const char * const expected_json = "{\"x\":1}"; - const size_t expected_json_size = strlen(expected_json) + 1; - const size_t expected_size = sizeof(PostMessageChunkPayload) + strlen(expected_json) + 1; - uint8_t *buffer = task_malloc(expected_size); - - PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer; - *chunk = (PostMessageChunkPayload) { - .total_size_bytes = expected_json_size, - .is_first = true, - }; - memcpy(chunk->chunk_data, expected_json, expected_json_size); - - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk, - (const uint8_t *) chunk, expected_size)); - - // Compare with hard-coded byte array, to catch accidental changes to the ABI: - const uint8_t raw_bytes_v1[] = { - 0x08, 0x00, 0x00, 0x80, 0x7b, 0x22, 0x78, 0x22, 0x3a, 0x31, 0x7d, 0x00, - }; - cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size); - cl_assert_equal_m(raw_bytes_v1, buffer, expected_size); - - task_free(buffer); -} - -void test_rocky_api_app_message__postmessage_just_before_connected(void) { - prv_init_api(false /* start_connected */); - - EXECUTE_SCRIPT("var x = " SIMPLE_TEST_OBJECT ";" - "var hasError = false;" - "_rocky.on('postmessageerror', function() { hasError = true; });" - "_rocky.postMessage(x);"); - - // First send attempt fails because not in SessionOpen - ASSERT_JS_GLOBAL_EQUALS_B("hasError", false); - - prv_simulate_transport_connection_event(true /* is_connected */); - prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(); - - prv_assert_simple_test_object_pending(); - - prv_rcv_app_message_ack(APP_MSG_OK); - - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); - - ASSERT_JS_GLOBAL_EQUALS_B("hasError", false); -} - -void test_rocky_api_app_message__post_message_single_chunk(void) { - prv_init_and_goto_session_open(); - - EXECUTE_SCRIPT("var x = " SIMPLE_TEST_OBJECT "; _rocky.postMessage(x);"); - prv_assert_simple_test_object_pending(); - - prv_rcv_app_message_ack(APP_MSG_OK); - - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); -} - -static void prv_init_and_goto_session_open_with_tiny_buffers(void) { - prv_init_and_goto_awaiting_reset_complete_remote_initiated(); - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete, \ - (const uint8_t *)&TINY_RESET_COMPLETE, sizeof(TINY_RESET_COMPLETE))); - cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen); -} - -void test_rocky_api_app_message__post_message_multi_chunk(void) { - prv_init_and_goto_session_open_with_tiny_buffers(); - - EXECUTE_SCRIPT("var x = { \"x\" : 123 }; _rocky.postMessage(x);"); - - const char * const expected_json = "{\"x\":123}"; - const size_t expected_json_size = strlen(expected_json) + 1; - size_t json_bytes_remaining = expected_json_size; - - uint8_t *buffer = task_malloc(sizeof(PostMessageChunkPayload) + TINY_CHUNK_SIZE); - - // Chunk 1: - { - const size_t json_bytes_size = MIN(TINY_CHUNK_SIZE, json_bytes_remaining); - const size_t expected_size = sizeof(PostMessageChunkPayload) + json_bytes_size; - - PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer; - *chunk = (PostMessageChunkPayload) { - .total_size_bytes = expected_json_size, - .is_first = true, - }; - memcpy(chunk->chunk_data, expected_json, TINY_CHUNK_SIZE); - - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk, - (const uint8_t *) chunk, expected_size)); - - // Compare with hard-coded byte array, to catch accidental changes to the ABI: - const uint8_t raw_bytes_v1[] = { - 0x0a, 0x00, 0x00, 0x80, '{', '"', 'x', '"', - }; - cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size); - cl_assert_equal_m(raw_bytes_v1, buffer, expected_size); - - prv_rcv_app_message_ack(APP_MSG_OK); - json_bytes_remaining -= json_bytes_size; - } - - // Chunk 2: - { - const size_t json_bytes_size = MIN(TINY_CHUNK_SIZE, json_bytes_remaining); - const size_t expected_size = sizeof(PostMessageChunkPayload) + json_bytes_size; - const int payload_offset = expected_json_size - json_bytes_remaining; - - PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer; - *chunk = (PostMessageChunkPayload) { - .offset_bytes = payload_offset, - .continuation_is_first = false, - }; - memcpy(chunk->chunk_data, expected_json + payload_offset, TINY_CHUNK_SIZE); - - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk, - (const uint8_t *) chunk, expected_size)); - - // Compare with hard-coded byte array, to catch accidental changes to the ABI: - const uint8_t raw_bytes_v1[] = { - 0x04, 0x00, 0x00, 0x00, ':', '1', '2', '3', - }; - cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size); - cl_assert_equal_m(raw_bytes_v1, buffer, expected_size); - - prv_rcv_app_message_ack(APP_MSG_OK); - json_bytes_remaining -= json_bytes_size; - } - - // Chunk 3: - { - const size_t json_bytes_size = MIN(TINY_CHUNK_SIZE, json_bytes_remaining); - const size_t expected_size = sizeof(PostMessageChunkPayload) + json_bytes_size; - const int payload_offset = expected_json_size - json_bytes_remaining; - - PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer; - *chunk = (PostMessageChunkPayload) { - .offset_bytes = payload_offset, - .continuation_is_first = false, - }; - memcpy(chunk->chunk_data, expected_json + payload_offset, TINY_CHUNK_SIZE); - - EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk, - (const uint8_t *) chunk, expected_size)); - - // Compare with hard-coded byte array, to catch accidental changes to the ABI: - const uint8_t raw_bytes_v1[] = { - 0x08, 0x00, 0x00, 0x00, '}', '\0', - }; - cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size); - cl_assert_equal_m(raw_bytes_v1, buffer, expected_size); - - prv_rcv_app_message_ack(APP_MSG_OK); - json_bytes_remaining -= json_bytes_size; - } - - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); - - task_free(buffer); -} - -void test_rocky_api_app_message__postmessage_not_jsonable(void) { - prv_init_and_goto_session_open(); - - const char *not_jsonable_error = - "TypeError: Argument at index 0 is not a JSON.stringify()-able object"; - - EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage(undefined);", not_jsonable_error); - EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage(function() {});", not_jsonable_error); - EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage({toJSON: function() {throw 'toJSONError';}});", - "toJSONError"); -} - -void test_rocky_api_app_message__postmessage_no_args(void) { - prv_init_api(false /* start_connected */); - EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage();", "TypeError: Not enough arguments"); -} - -void test_rocky_api_app_message__postmessage_oom(void) { - prv_init_api(false /* start_connected */); - - fake_malloc_set_largest_free_block(0); - - EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage('x');", - "RangeError: Out of memory: can't postMessage() -- object too large"); -} - -//////////////////////////////////////////////////////////////////////////////// -// Receive Tests -//////////////////////////////////////////////////////////////////////////////// -void test_rocky_api_app_message__receive_message_multi_chunk(void) { - prv_init_and_goto_session_open_with_tiny_buffers(); - - EXECUTE_SCRIPT("var event = null;\n" - "var json_str = null;\n" - "_rocky.on('message', function(e) {\n" - " json_str = JSON.stringify(e.data);\n" // stringify again to make assert simple - " event = e;\n" - "});"); - JS_VAR event_null = prv_js_global_get_value("event"); - cl_assert_equal_b(true, jerry_value_is_null(event_null)); - - // Get 3x the same message in a row: - for (int j = 0; j < 3; ++j) { - - // Chunks for: {"x":123} - const struct { - uint8_t byte_array[8]; - size_t length; - } chunk_msg_defs[] = { - { - .byte_array = {0x0a, 0x00, 0x00, 0x80, '{', '"', 'x', '"'}, - .length = 8, - }, - { - .byte_array = {0x04, 0x00, 0x00, 0x00, ':', '1', '2', '3'}, - .length = 8, - }, - { - .byte_array = {0x08, 0x00, 0x00, 0x00, '}', '\0'}, - .length = 6, - } - }; - - for (int i = 0; i < ARRAY_LENGTH(chunk_msg_defs); ++i) { - RCV_APP_MESSAGE(TupletBytes(PostMessageKeyChunk, - (const uint8_t *) chunk_msg_defs[i].byte_array, - chunk_msg_defs[i].length)); - } - - JS_VAR event_valid = prv_js_global_get_value("event"); - cl_assert_equal_b(true, jerry_value_is_object(event_valid)); - - // Make sure that there is a "data" property - JS_VAR data_prop = jerry_get_object_field(event_valid, "data"); - cl_assert_equal_b(true, jerry_value_is_object(data_prop)); - - // Make sure the re-serialized object matches: - ASSERT_JS_GLOBAL_EQUALS_S("json_str", "{\"x\":123}"); - - EXECUTE_SCRIPT("json_str = null;\n" - "event = null"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// "postmessageerror" event -//////////////////////////////////////////////////////////////////////////////// - -void test_rocky_api_app_message__postmessageerror(void) { - prv_init_and_goto_session_open(); - - EXECUTE_SCRIPT("var didError = false;" - "var x = { \"x\" : 1 };" - "var dataJSON = undefined;" - "_rocky.on('postmessageerror', " - " function(e) { didError = true; dataJSON = JSON.stringify(e.data); });" - "_rocky.postMessage(x);" - "x.x = 2;"); - - ASSERT_JS_GLOBAL_EQUALS_B("didError", false); - - for (int i = 0; i < 3; ++i) { - cl_assert_equal_b(s_is_outbox_message_pending, true); - - // NACK - prv_rcv_app_message_ack(APP_MSG_BUSY); - - AppTimer *t = rocky_api_app_message_get_app_msg_retry_timer(); - cl_assert(t != EVENTED_TIMER_INVALID_ID); - cl_assert_equal_b(fake_app_timer_is_scheduled(t), true); - - // Enqueuing more objects shouldn't affect the pace at which things are retried: - EXECUTE_SCRIPT("_rocky.postMessage('')"); - - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); - - cl_assert_equal_b(app_timer_trigger(t), true); - } - - ASSERT_JS_GLOBAL_EQUALS_B("didError", true); - ASSERT_JS_GLOBAL_EQUALS_S("dataJSON", "{\"x\":1}"); -} - -void test_rocky_api_app_message__postmessageerror_while_disconnected(void) { - prv_init_api(false /* start_connected */); - - EXECUTE_SCRIPT("var didError = false;" - "var x = " SIMPLE_TEST_OBJECT ";" - "_rocky.on('postmessageerror', " - " function(e) { didError = true; dataJSON = JSON.stringify(e.data); });" - /* 3x postMessage(): */ - "_rocky.postMessage(x);" - "_rocky.postMessage(x);" - "_rocky.postMessage(x);"); - - // Let the first 2 timeout: - for (int i = 0; i < 2; ++i) { - ASSERT_JS_GLOBAL_EQUALS_B("didError", false); - - AppTimer *t = rocky_api_app_message_get_session_closed_object_queue_timer(); - cl_assert(t != EVENTED_TIMER_INVALID_ID); - cl_assert_equal_b(fake_app_timer_is_scheduled(t), true); - - EXPECT_OUTBOX_NO_MESSAGE_PENDING(); - - cl_assert_equal_b(app_timer_trigger(t), true); - - ASSERT_JS_GLOBAL_EQUALS_B("didError", true); - - EXECUTE_SCRIPT("didError = false;"); - } - - // Timer for the 3rd should be set: - AppTimer *t = rocky_api_app_message_get_session_closed_object_queue_timer(); - cl_assert(t != EVENTED_TIMER_INVALID_ID); - cl_assert_equal_b(fake_app_timer_is_scheduled(t), true); - - // Connect: - prv_simulate_transport_connection_event(true /* is_connected */); - prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(); - - // Timer for the 3rd should be cancelled now: - cl_assert(EVENTED_TIMER_INVALID_ID == rocky_api_app_message_get_session_closed_object_queue_timer()); - - prv_assert_simple_test_object_pending(); -} diff --git a/tests/fw/javascript/test_rocky_api_console.c b/tests/fw/javascript/test_rocky_api_console.c deleted file mode 100644 index 2886fb5094..0000000000 --- a/tests/fw/javascript/test_rocky_api_console.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_console.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" -#include "fake_logging.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -static const RockyGlobalAPI *s_api[] = { - &CONSOLE_APIS, - NULL, -}; - -void test_rocky_api_console__initialize(void) { - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - s_log_internal__expected = NULL; -} - -void test_rocky_api_console__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); -} - -void test_rocky_api_console__functions_exist(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT( - "var c = typeof console;\n" - "var cl = typeof console.log;\n" - "var cw = typeof console.warn;\n" - "var ce = typeof console.error;\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("c", "object"); - ASSERT_JS_GLOBAL_EQUALS_S("cl", "function"); - ASSERT_JS_GLOBAL_EQUALS_S("cw", "function"); - ASSERT_JS_GLOBAL_EQUALS_S("ce", "function"); -} - -void test_rocky_api_console__logs_single_values(void) { - rocky_global_init(s_api); - - s_log_internal__expected = (const char *[]){ - "some string", - "1234", - "true", - "undefined", - "[object Object]", - NULL - }; - - EXECUTE_SCRIPT( - "console.log('some string');\n" - "console.log(1230 + 4);\n" - "console.log(1 == 1);\n" - "console.log(undefined);\n" - "console.log({a:123, b:[1,2]});\n" - ); - - cl_assert_equal_s(NULL, *s_log_internal__expected); -} - -void test_rocky_api_console__warn_error_multiple(void) { - rocky_global_init(s_api); - - s_log_internal__expected = (const char *[]){ - "foo", - "1", - "2", - "true", - "false", - NULL - }; - - EXECUTE_SCRIPT( - "console.warn('foo', 1, 2);\n" - "console.error(true, false);\n" - ); - - cl_assert_equal_s(NULL, *s_log_internal__expected); -} \ No newline at end of file diff --git a/tests/fw/javascript/test_rocky_api_datetime.c b/tests/fw/javascript/test_rocky_api_datetime.c deleted file mode 100644 index 0ab56ad28a..0000000000 --- a/tests/fw/javascript/test_rocky_api_datetime.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_datetime.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -// Standard -#include - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -static bool s_clock_is_24h_style; -bool clock_is_24h_style() { - return s_clock_is_24h_style; -} - - -void test_rocky_api_datetime__initialize(void) { - // Mon Jul 25 2005 20:04:05 GMT-03:00 - s_time = 1122332645; - s_gmt_off = -3 * 60 * 60; - - s_clock_is_24h_style = false; - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); -} - -void test_rocky_api_datetime__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); -} - -static const RockyGlobalAPI *s_api[] = { - &DATETIME_APIS, - NULL, -}; - -void test_rocky_api_datetime__jerry_script_default(void) { - static const RockyGlobalAPI *apis[] = {NULL}; - rocky_global_init(apis); - EXECUTE_SCRIPT( - "var d = new Date();\n" - "var s1 = d.toString();\n" - "var f = typeof(d.toLocaleTimeString);\n" - "var s2 = d.toLocaleTimeString();\n" - "var s3 = d.toLocaleDateString();\n" - "var s4 = d.toLocaleString();\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("s1", "Mon Jul 25 2005 20:04:05 GMT-03:00"); - ASSERT_JS_GLOBAL_EQUALS_S("f", "function"); - // yes, JerryScript provides some default behavior but it's not what we want - ASSERT_JS_GLOBAL_EQUALS_S("s2", "23:04:05.000"); - ASSERT_JS_GLOBAL_EQUALS_S("s3", "2005-07-25"); - ASSERT_JS_GLOBAL_EQUALS_S("s4", "Mon Jul 25 2005 20:04:05 GMT-03:00"); -} - -void test_rocky_api_datetime__locale_time_string_12h(void) { - s_clock_is_24h_style = false; - rocky_global_init(s_api); - EXECUTE_SCRIPT( - "var d = new Date();\n" - "var s = d.toLocaleTimeString();\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("s", "8:04:05 PM"); - - s_time += 4 * SECONDS_PER_HOUR; - EXECUTE_SCRIPT( - "var d = new Date();\n" - "var s = d.toLocaleTimeString();\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("s", "12:04:05 AM"); -} - -void test_rocky_api_datetime__locale_time_string_24h(void) { - s_clock_is_24h_style = true; - rocky_global_init(s_api); - EXECUTE_SCRIPT( - "var d = new Date();\n" - "var s = d.toLocaleTimeString();\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("s", "20:04:05"); - - s_time += 4 * SECONDS_PER_HOUR; - EXECUTE_SCRIPT( - "var d = new Date();\n" - "var s = d.toLocaleTimeString();\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("s", "00:04:05"); -} - -void test_rocky_api_datetime__locale(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT( - "var d = new Date();\n" - "d.toLocaleTimeString(undefined);\n" - ); - EXECUTE_SCRIPT_EXPECT_ERROR( - "d.toLocaleTimeString('en-us');", - "TypeError: Unsupported locale" - ); - - EXECUTE_SCRIPT( - "var d = new Date();\n" - "d.toLocaleDateString(undefined);\n" - ); - EXECUTE_SCRIPT_EXPECT_ERROR( - "d.toLocaleDateString('de');", - "TypeError: Unsupported locale" - ); - - EXECUTE_SCRIPT( - "var d = new Date();\n" - "d.toLocaleString(undefined);\n" - ); - EXECUTE_SCRIPT_EXPECT_ERROR( - "d.toLocaleString('de');", - "TypeError: Unsupported locale" - ); -} - -void test_rocky_api_datetime__locale_time_string_options(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT("var d = new Date();"); - - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "5"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "05"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "4"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "04"); - - s_clock_is_24h_style = false; - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, " - "{hour: 'numeric', hour12: true});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, " - "{hour: 'numeric', hour12: false});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20"); - - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "08 PM"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "8:04:05 PM"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, " - "{hour: undefined, minute: undefined, second: undefined});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "8:04:05 PM"); - - - s_clock_is_24h_style = true; - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, " - "{hour: 'numeric', hour12: true});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, " - "{hour: 'numeric', hour12: false});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20:04:05"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, " - "{hour: undefined, minute: undefined, second: undefined});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20:04:05"); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "d.toLocaleTimeString(undefined, {minute: 'numeric', hour: '2-digit'})", - "TypeError: Unsupported options" - ); -} - -void test_rocky_api_datetime__locale_time_string_date_options(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT("var d = new Date();"); - - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {day: 'short'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Mon, 8:04:05 PM"); -} - -void test_rocky_api_datetime__locale_date_string(void) { - rocky_global_init(s_api); - EXECUTE_SCRIPT( - "var d = new Date();\n" - "var s = d.toLocaleDateString();\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05"); -} - -void test_rocky_api_datetime__locale_date_string_options(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT("var d = new Date();"); - - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "25"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "25"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'short'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Mon"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'long'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Monday"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "7"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "07"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'short'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Jul"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'long'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "July"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "2005"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "05"); -} - -void test_rocky_api_datetime__locale_date_string_time_options(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT("var d = new Date();"); - - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {hour: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05, 8 PM"); -} - -void test_rocky_api_datetime__locale_string_options(void) { - rocky_global_init(s_api); - EXECUTE_SCRIPT("var d = new Date();"); - - s_clock_is_24h_style = false; - EXECUTE_SCRIPT("s = d.toLocaleString(undefined, {});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05, 8:04:05 PM"); - s_clock_is_24h_style = true; - EXECUTE_SCRIPT("s = d.toLocaleString(undefined, {});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05, 20:04:05"); - - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "5"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "05"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "4"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "04"); - - s_clock_is_24h_style = false; - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "08 PM"); - s_clock_is_24h_style = true; - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20"); - EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "20"); - - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "25"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "25"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'short'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Mon"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'long'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Monday"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "7"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "07"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'short'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "Jul"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'long'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "July"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: 'numeric'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "2005"); - EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: '2-digit'});"); - ASSERT_JS_GLOBAL_EQUALS_S("s", "05"); -} diff --git a/tests/fw/javascript/test_rocky_api_global.c b/tests/fw/javascript/test_rocky_api_global.c deleted file mode 100644 index 05a920f377..0000000000 --- a/tests/fw/javascript/test_rocky_api_global.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" -#include "fake_logging.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -static Window s_app_window_stack_get_top_window; -Window *app_window_stack_get_top_window() { - return &s_app_window_stack_get_top_window; -} - -static int s_prv_api_init__callcount; -static void prv_api_init(void) { - s_prv_api_init__callcount++; -} - -static int s_prv_api_add__callcount; -static bool s_prv_api_add__result; -static bool prv_api_add(const char *event_name, jerry_value_t handler) { - s_prv_api_add__callcount++; - return s_prv_api_add__result; -} - -static int s_prv_api_remove__callcount; -static void prv_api_remove(const char *event_name, jerry_value_t handler) { - s_prv_api_remove__callcount++; -} - -int s_prv_listener_a1__callcount; -JERRY_FUNCTION(prv_listener_a1) { - s_prv_listener_a1__callcount++; - return jerry_create_undefined(); -} - -int s_prv_listener_a2__callcount; -JERRY_FUNCTION(prv_listener_a2) { - s_prv_listener_a2__callcount++; - return jerry_create_undefined(); -} - -int s_prv_listener_b__callcount; -JERRY_FUNCTION(prv_listener_b) { - s_prv_listener_b__callcount++; - return jerry_create_undefined(); -} - -void test_rocky_api_global__initialize(void) { - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - s_app_window_stack_get_top_window = (Window){}; - - s_app_event_loop_callback = NULL; - s_log_internal__expected = NULL; - - s_prv_api_init__callcount = 0; - s_prv_api_add__callcount = 0; - s_prv_api_add__result = false; - s_prv_api_remove__callcount = 0; - - s_prv_listener_a1__callcount = 0; - s_prv_listener_a2__callcount = 0; - s_prv_listener_b__callcount = 0; -} - -void test_rocky_api_global__cleanup(void) { - fake_app_timer_deinit(); - s_log_internal__expected = NULL; - - jerry_cleanup(); - rocky_runtime_context_deinit(); - rocky_global_deinit(); -} - -void test_rocky_api_global__global(void) { - char test_object[] = "var t = typeof _rocky"; - - // global doesn't exist in plain Jerry context - EXECUTE_SCRIPT(test_object); - ASSERT_JS_GLOBAL_EQUALS_S("t", "undefined"); - - // rocky_global_init() injects global... - static const RockyGlobalAPI *apis[] = { - NULL, - }; - rocky_global_init(apis); - - EXECUTE_SCRIPT(test_object); - ASSERT_JS_GLOBAL_EQUALS_S("t", "object"); - - // ...which also has a method .on() - EXECUTE_SCRIPT("var t = typeof _rocky.on"); - ASSERT_JS_GLOBAL_EQUALS_S("t", "function"); - - /// ...which is an alias of .addEventListener() - EXECUTE_SCRIPT("var a = (_rocky.on === _rocky.addEventListener);"); - ASSERT_JS_GLOBAL_EQUALS_B("a", true); -} - -void test_rocky_api_global__calls_init_and_notifies_about_apis(void) { - static const RockyGlobalAPI api = { - .init = prv_api_init, - .add_handler = prv_api_add, - }; - static const RockyGlobalAPI *apis[] = { - &api, - NULL, - }; - rocky_global_init(apis); - cl_assert_equal_i(1, s_prv_api_init__callcount); - - s_prv_api_add__result = true; - s_log_internal__expected = (const char *[]){NULL}; - EXECUTE_SCRIPT("_rocky.on('foo', function(){})"); - cl_assert_equal_i(1, s_prv_api_add__callcount); - cl_assert_equal_b(true, rocky_global_has_event_handlers("foo")); - - s_prv_api_add__result = false; - s_log_internal__expected = (const char *[]){ - "Unknown event 'bar'", - NULL }; - EXECUTE_SCRIPT("_rocky.on('bar', function(){})"); - cl_assert_equal_i(2, s_prv_api_add__callcount); - cl_assert_equal_b(false, rocky_global_has_event_handlers("bar")); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_global__can_unsubsribe_event_handlers(void) { - static const RockyGlobalAPI api = { - .add_handler = prv_api_add, - .remove_handler = prv_api_remove, - }; - static const RockyGlobalAPI *apis[] = { - &api, - NULL, - }; - rocky_global_init(apis); - - s_prv_api_add__result = true; - EXECUTE_SCRIPT( - "var f1 = function(){};\n" - "var f2 = function(){};\n" - "_rocky.on('foo', f1)\n" - ); - cl_assert_equal_i(1, s_prv_api_add__callcount); - cl_assert_equal_b(true, rocky_global_has_event_handlers("foo")); - - // variables f1, f2 continue to exist between EXECUTE_SCRIPT calls - EXECUTE_SCRIPT("var t = typeof f2;"); - ASSERT_JS_GLOBAL_EQUALS_S("t", "function"); - - // rocky.off exists - EXECUTE_SCRIPT( - "t = typeof _rocky.off;\n" - "var eq = _rocky.off === _rocky.removeEventListener;\n" - ); - ASSERT_JS_GLOBAL_EQUALS_S("t", "function"); - ASSERT_JS_GLOBAL_EQUALS_B("eq", true); - - // from MDN docs: - // Calling removeEventListener() with arguments that do not identify - // any currently registered EventListener on the EventTarget has no effect. - EXECUTE_SCRIPT( - "_rocky.off('foo', f2);\n" - "_rocky.off('unknownevent', f1);\n" - ); - cl_assert_equal_i(0, s_prv_api_remove__callcount); - cl_assert_equal_b(true, rocky_global_has_event_handlers("foo")); - - EXECUTE_SCRIPT("_rocky.off('foo', f1);\n"); - cl_assert_equal_i(1, s_prv_api_remove__callcount); - cl_assert_equal_b(false, rocky_global_has_event_handlers("foo")); -} - -void prv_add_event_listener_to_list(const char *event_name, jerry_value_t listener); -int jerry_obj_refcount(jerry_value_t o); - -void test_rocky_api_global__refcount(void) { - jerry_value_t o = jerry_create_object(); - cl_assert_equal_i(1, jerry_obj_refcount(o)); - jerry_acquire_value(o); - cl_assert_equal_i(2, jerry_obj_refcount(o)); - jerry_acquire_value(o); - cl_assert_equal_i(3, jerry_obj_refcount(o)); - jerry_release_value(o); - cl_assert_equal_i(2, jerry_obj_refcount(o)); - jerry_release_value(o); - cl_assert_equal_i(1, jerry_obj_refcount(o)); - jerry_release_value(o); - cl_assert_equal_i(0, jerry_obj_refcount(o)); -} - -void test_rocky_api_global__calls_listeners(void) { - static const RockyGlobalAPI *apis[] = {NULL}; - rocky_global_init(apis); - - prv_add_event_listener_to_list("a", jerry_create_external_function(prv_listener_a1)); - cl_assert_equal_b(true, rocky_global_has_event_handlers("a")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("b")); - - - prv_add_event_listener_to_list("b", jerry_create_external_function(prv_listener_b)); - cl_assert_equal_b(true, rocky_global_has_event_handlers("a")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("b")); - - prv_add_event_listener_to_list("a", jerry_create_external_function(prv_listener_a2)); - cl_assert_equal_b(true, rocky_global_has_event_handlers("a")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("b")); - - jerry_value_t a_event = rocky_global_create_event("a"); - rocky_global_call_event_handlers(a_event); - cl_assert_equal_i(1, s_prv_listener_a1__callcount); - cl_assert_equal_i(1, s_prv_listener_a2__callcount); - cl_assert_equal_i(0, s_prv_listener_b__callcount); - jerry_release_value(a_event); - - jerry_value_t b_event = rocky_global_create_event("b"); - rocky_global_call_event_handlers(b_event); - cl_assert_equal_i(1, s_prv_listener_a1__callcount); - cl_assert_equal_i(1, s_prv_listener_a2__callcount); - cl_assert_equal_i(1, s_prv_listener_b__callcount); - jerry_release_value(b_event); -} - -void test_rocky_api_global__adds_listener_only_once(void) { - static const RockyGlobalAPI *apis[] = {NULL}; - rocky_global_init(apis); - - const jerry_value_t f = jerry_create_external_function(prv_listener_a1); - prv_add_event_listener_to_list("a", f); - prv_add_event_listener_to_list("a", f); - cl_assert_equal_b(true, rocky_global_has_event_handlers("a")); - - jerry_value_t a_event = rocky_global_create_event("a"); - rocky_global_call_event_handlers(a_event); - // as second .on('a', f) "replaces" first, f will only be called once - cl_assert_equal_i(1, s_prv_listener_a1__callcount); - jerry_release_value(a_event); -} - -void test_rocky_api_global__event_constructor(void) { - static const RockyGlobalAPI *apis[] = {NULL}; - rocky_global_init(apis); - - EXECUTE_SCRIPT( - "_rocky.Event.prototype.myCustomThing = 'xyz';\n" - "var e = new _rocky.Event('myevent');\n" - "var t = e.type;\n" - "var c = e.myCustomThing;\n" - ); - ASSERT_JS_GLOBAL_EQUALS_S("t", "myevent"); - ASSERT_JS_GLOBAL_EQUALS_S("c", "xyz"); -} - -void test_rocky_api_global__call_event_handlers_async(void) { - static const RockyGlobalAPI api = { - .init = prv_api_init, - .add_handler = prv_api_add, - }; - static const RockyGlobalAPI *apis[] = { - &api, - NULL, - }; - rocky_global_init(apis); - - s_prv_api_add__result = true; - EXECUTE_SCRIPT("var is_called = false; _rocky.on('a', function(e) { is_called = true; });"); - jerry_value_t a_event = rocky_global_create_event("a"); - rocky_global_call_event_handlers_async(a_event); - ASSERT_JS_GLOBAL_EQUALS_B("is_called", false); - - s_process_manager_callback(s_process_manager_callback_data); - ASSERT_JS_GLOBAL_EQUALS_B("is_called", true); -} diff --git a/tests/fw/javascript/test_rocky_api_graphics.c b/tests/fw/javascript/test_rocky_api_graphics.c deleted file mode 100644 index 0e5b9193ee..0000000000 --- a/tests/fw/javascript/test_rocky_api_graphics.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/graphics/gtypes.h" -#include "applib/rockyjs/api/rocky_api.h" -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" -#include "applib/rockyjs/api/rocky_api_graphics_text.h" -#include "applib/rockyjs/pbl_jerry_port.h" -#include "util/trig.h" - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -static Window s_app_window_stack_get_top_window; -Window *app_window_stack_get_top_window() { - return &s_app_window_stack_get_top_window; -} - -void rocky_api_graphics_path2d_add_canvas_methods(jerry_value_t obj) {} -void rocky_api_graphics_path2d_cleanup(void) {} -void rocky_api_graphics_path2d_reset_state(void) {} - -GContext s_context; - -// mocks -static MockCallRecordings s_graphics_context_set_fill_color; -void graphics_context_set_fill_color(GContext* ctx, GColor color) { - record_mock_call(s_graphics_context_set_fill_color) { - .ctx = ctx, .color = color, - }; - ctx->draw_state.fill_color = color; -} - -static MockCallRecordings s_graphics_context_set_stroke_color; -void graphics_context_set_stroke_color(GContext* ctx, GColor color) { - record_mock_call(s_graphics_context_set_stroke_color) { - .ctx = ctx, .color = color, - }; - ctx->draw_state.stroke_color = color; -} - -static MockCallRecordings s_graphics_context_set_stroke_width; -void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) { - record_mock_call(s_graphics_context_set_stroke_width) { - .ctx = ctx, .width = stroke_width, - }; - ctx->draw_state.stroke_width = stroke_width; -} - -static MockCallRecordings s_graphics_fill_rect; -static GColor s_graphics_fill_rect__color; -void graphics_fill_rect(GContext *ctx, const GRect *rect) { - s_graphics_fill_rect__color = s_context.draw_state.fill_color; - record_mock_call(s_graphics_fill_rect) { - .ctx = ctx, .rect = *rect, - }; -} - -static MockCallRecordings s_graphics_draw_rect_precise; -void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) { - record_mock_call(s_graphics_draw_rect_precise) { - .ctx = ctx, .prect = *rect, - }; -} - -static MockCallRecordings s_graphics_fill_radial_precise_internal; -void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center, - Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer, - int32_t angle_start, int32_t angle_end) { - record_mock_call(s_graphics_fill_radial_precise_internal) { - .ctx = ctx, - .fill_radial_precise.center = center, - .fill_radial_precise.radius_inner = radius_inner, - .fill_radial_precise.radius_outer = radius_outer, - .fill_radial_precise.angle_start = angle_start, - .fill_radial_precise.angle_end = angle_end, - }; -} - - -void graphics_fill_round_rect_by_value(GContext *ctx, GRect rect, uint16_t corner_radius, - GCornerMask corner_mask) {} -static MockCallRecordings s_layer_mark_dirty; -void layer_mark_dirty(Layer *layer) { - record_mock_call(s_layer_mark_dirty){ - .layer = layer - }; -} - -static MockCallRecordings s_fonts_get_system_font; -GFont s_fonts_get_system_font__result; -GFont fonts_get_system_font(const char *font_key) { - record_mock_call(s_fonts_get_system_font){ - .font_key = font_key - }; - return s_fonts_get_system_font__result; -} - -static MockCallRecordings s_graphics_draw_text; -void graphics_draw_text(GContext *ctx, const char *text, GFont const font, const GRect box, - const GTextOverflowMode overflow_mode, const GTextAlignment alignment, - GTextAttributes *text_attributes) { - record_mock_call(s_graphics_draw_text){ - .draw_text.box = box, - .draw_text.color = ctx->draw_state.text_color, - }; - strncpy(s_graphics_draw_text.last_call.draw_text.text, - text, sizeof(s_graphics_draw_text.last_call.draw_text.text)); -} - -static MockCallRecordings s_graphics_text_attributes_destroy; -void graphics_text_attributes_destroy(GTextAttributes *text_attributes) { - record_mock_call(s_graphics_text_attributes_destroy){}; -} - -static MockCallRecordings s_graphics_text_layout_get_max_used_size; -static GSize s_graphics_text_layout_get_max_used_size__result; -GSize graphics_text_layout_get_max_used_size(GContext *ctx, const char *text, - GFont const font, const GRect box, - const GTextOverflowMode overflow_mode, - const GTextAlignment alignment, - GTextLayoutCacheRef layout) { - record_mock_call(s_graphics_text_layout_get_max_used_size){ - .max_used_size.font = font, - .max_used_size.box = box, - .max_used_size.overflow_mode = overflow_mode, - .max_used_size.alignment = alignment, - }; - strncpy(s_graphics_text_layout_get_max_used_size.last_call.max_used_size.text, - text, sizeof(s_graphics_text_layout_get_max_used_size.last_call.max_used_size.text)); - - return s_graphics_text_layout_get_max_used_size__result; -} - -void test_rocky_api_graphics__initialize(void) { - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - - s_app_window_stack_get_top_window = (Window){}; - s_context = (GContext){}; - s_app_state_get_graphics_context = &s_context; - s_app_event_loop_callback = NULL; - - s_graphics_context_set_stroke_color = (MockCallRecordings){0}; - s_graphics_context_set_stroke_width = (MockCallRecordings){0}; - s_graphics_context_set_fill_color = (MockCallRecordings){0}; - s_graphics_fill_rect = (MockCallRecordings){0}; - s_graphics_fill_rect__color = GColorClear; - s_graphics_draw_rect_precise = (MockCallRecordings){0}; - s_graphics_fill_radial_precise_internal = (MockCallRecordings){0}; - s_layer_mark_dirty = (MockCallRecordings){0}; - s_fonts_get_system_font = (MockCallRecordings){0}; - s_graphics_draw_text = (MockCallRecordings){0}; - s_graphics_text_attributes_destroy = (MockCallRecordings){0}; - s_graphics_text_layout_get_max_used_size = (MockCallRecordings){0}; - s_graphics_text_layout_get_max_used_size__result = (GSize){0}; -} - -void test_rocky_api_graphics__cleanup(void) { - fake_app_timer_deinit(); - - // some tests deinitialize the engine, avoid double de-init - if (app_state_get_rocky_runtime_context() != NULL) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - } -} - -static const RockyGlobalAPI *s_graphics_api[] = { - &GRAPHIC_APIS, - NULL, -}; - -extern RockyAPITextState s_rocky_text_state; - -void test_rocky_api_graphics__handles_text_state(void) { - cl_assert_equal_i(0, s_fonts_get_system_font.call_count); - cl_assert_equal_i(0, s_graphics_text_attributes_destroy.call_count); - rocky_global_init(s_graphics_api); - cl_assert_equal_i(1, s_fonts_get_system_font.call_count); - cl_assert_equal_i(0, s_graphics_text_attributes_destroy.call_count); - rocky_global_deinit(); - cl_assert_equal_i(1, s_fonts_get_system_font.call_count); - cl_assert_equal_i(0, s_graphics_text_attributes_destroy.call_count); - - s_rocky_text_state.text_attributes = (GTextAttributes *)123; - rocky_global_deinit(); - cl_assert_equal_i(1, s_fonts_get_system_font.call_count); - cl_assert_equal_i(1, s_graphics_text_attributes_destroy.call_count); -} - -void test_rocky_api_graphics__request_draw(void) { - rocky_global_init(s_graphics_api); - - cl_assert_equal_i(0, s_layer_mark_dirty.call_count); - EXECUTE_SCRIPT("_rocky.requestDraw();"); - cl_assert_equal_i(1, s_layer_mark_dirty.call_count); - cl_assert_equal_p(&s_app_window_stack_get_top_window.layer, s_layer_mark_dirty.last_call.layer); -} - -void test_rocky_api_graphics__provides_draw_event(void) { - rocky_global_init(s_graphics_api); - - cl_assert_equal_b(false, rocky_global_has_event_handlers("draw")); - EXECUTE_SCRIPT("_rocky.on('draw', function() {});"); - cl_assert_equal_b(true, rocky_global_has_event_handlers("draw")); -} - -void test_rocky_api_graphics__draw_event_has_ctx(void) { - rocky_global_init(s_graphics_api); - - EXECUTE_SCRIPT( - "var event = null;\n" - "_rocky.on('draw', function(e) {event = e;});" - ); - - const jerry_value_t event_null = prv_js_global_get_value("event"); - cl_assert_equal_b(true, jerry_value_is_null(event_null)); - jerry_release_value(event_null); - - Layer *l = &app_window_stack_get_top_window()->layer; - l->update_proc(l, NULL); - const jerry_value_t event = prv_js_global_get_value("event"); - cl_assert_equal_b(true, jerry_value_is_object(event)); - - const jerry_value_t context_2d = jerry_get_object_field(event, "context"); - cl_assert_equal_b(true, jerry_value_is_object(context_2d)); - jerry_release_value(context_2d); - jerry_release_value(event); -} - -jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer); -void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) { - *bounds_out = GRect(5, 6, 7, 8); -} - -void test_rocky_api_graphics__canvas_offers_size(void) { - rocky_global_init(s_graphics_api); - - Layer l = {.bounds = GRect(1, 2, 3, 4)}; - const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l); - jerry_set_object_field(jerry_get_global_object(), "ctx", ctx); - - EXECUTE_SCRIPT( - "var w = ctx.canvas.clientWidth;\n" - "var h = ctx.canvas.clientHeight;\n" - "var uol = ctx.canvas.unobstructedLeft;\n" - "var uot = ctx.canvas.unobstructedTop;\n" - "var uow = ctx.canvas.unobstructedWidth;\n" - "var uoh = ctx.canvas.unobstructedHeight;\n" - ); - ASSERT_JS_GLOBAL_EQUALS_I("w", 3); - ASSERT_JS_GLOBAL_EQUALS_I("h", 4); - ASSERT_JS_GLOBAL_EQUALS_I("uol", 5); - ASSERT_JS_GLOBAL_EQUALS_I("uot", 6); - ASSERT_JS_GLOBAL_EQUALS_I("uow", 7); - ASSERT_JS_GLOBAL_EQUALS_I("uoh", 8); -} - -static const jerry_value_t prv_global_init_and_set_ctx(void) { - rocky_global_init(s_graphics_api); - - // make this easily testable by putting it int JS context as global - Layer l = {.bounds = GRect(0, 0, 144, 168)}; - const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l); - cl_assert_equal_b(jerry_value_is_object(ctx), true); - jerry_set_object_field(jerry_get_global_object(), "ctx", ctx); - - return ctx; -} - -void test_rocky_api_graphics__drawing_rects(void) { - prv_global_init_and_set_ctx(); - - s_context.draw_state.fill_color = GColorJaegerGreen; - - EXECUTE_SCRIPT( - "ctx.clearRect(1, 2, 3, 4);\n" - ); - - cl_assert_equal_i(1, s_graphics_fill_rect.call_count); - cl_assert_equal_rect(GRect(1, 2, 3, 4), s_graphics_fill_rect.last_call.rect); - cl_assert_equal_i(GColorBlackARGB8, s_graphics_fill_rect__color.argb); - cl_assert_equal_i(GColorJaegerGreenARGB8, s_context.draw_state.fill_color.argb); - - s_graphics_fill_rect = (MockCallRecordings){}; - EXECUTE_SCRIPT( - "ctx.fillRect(5, 6, 7, 8);\n" - ); - - cl_assert_equal_i(1, s_graphics_fill_rect.call_count); - cl_assert_equal_rect(GRect(5, 6, 7, 8), s_graphics_fill_rect.last_call.rect); - - s_graphics_draw_rect_precise = (MockCallRecordings){}; - EXECUTE_SCRIPT( - "ctx.strokeRect(9, 10.2, 11.5, 12.8);\n" - ); - - cl_assert_equal_i(1, s_graphics_draw_rect_precise.call_count); - GRectPrecise expected_rect = {(int)(8.5*8), 78, (int)(11.5*8), (int)(12.8*8)}; - cl_assert_equal_rect_precise(expected_rect, s_graphics_draw_rect_precise.last_call.prect); -} - -#define PP(x, y) (GPointPrecise( \ - (int16_t)((x) * FIXED_S16_3_FACTOR), \ - (int16_t)((y) * FIXED_S16_3_FACTOR))) - -void test_rocky_api_graphics__fill_radial(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT( - "ctx.rockyFillRadial(30, 40, 10, 20, 0, Math.PI);\n" - ); - - cl_assert_equal_i(1, s_graphics_fill_radial_precise_internal.call_count); - MockCallRecording *const lc = &s_graphics_fill_radial_precise_internal.last_call; - cl_assert_equal_point_precise(PP(29.5, 39.5), lc->fill_radial_precise.center); - cl_assert_equal_i(10*8, lc->fill_radial_precise.radius_inner.raw_value); - cl_assert_equal_i(20*8, lc->fill_radial_precise.radius_outer.raw_value); - cl_assert_equal_i(TRIG_MAX_ANGLE * 1 / 4, lc->fill_radial_precise.angle_start); - cl_assert_equal_i(TRIG_MAX_ANGLE * 3 / 4, lc->fill_radial_precise.angle_end); - - EXECUTE_SCRIPT( - "ctx.rockyFillRadial(30, 40, 10, 30, 0, 2 * Math.PI);\n" - ); - - cl_assert_equal_i(2, s_graphics_fill_radial_precise_internal.call_count); - cl_assert_equal_point_precise(PP(29.5, 39.5), lc->fill_radial_precise.center); - cl_assert_equal_i(10*8, lc->fill_radial_precise.radius_inner.raw_value); - cl_assert_equal_i(30*8, lc->fill_radial_precise.radius_outer.raw_value); - cl_assert_equal_i(TRIG_MAX_ANGLE * 1 / 4, lc->fill_radial_precise.angle_start); - cl_assert_equal_i(TRIG_MAX_ANGLE * 5 / 4, lc->fill_radial_precise.angle_end); - - EXECUTE_SCRIPT( - "ctx.rockyFillRadial(30.5, 40.1, 30, 10, 0, 2 * Math.PI);\n" - ); - - cl_assert_equal_i(3, s_graphics_fill_radial_precise_internal.call_count); - cl_assert_equal_point_precise(PP(30, 39.625), lc->fill_radial_precise.center); - cl_assert_equal_i(10*8, lc->fill_radial_precise.radius_inner.raw_value); - cl_assert_equal_i(30*8, lc->fill_radial_precise.radius_outer.raw_value); -} - -void test_rocky_api_graphics__fill_radial_not_enough_args(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.rockyFillRadial(30, 40, 10, 20, 0);\n", - "TypeError: Not enough arguments" - ); -} - -void test_rocky_api_graphics__fill_radial_type_error(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.rockyFillRadial(30, 40, 10, 20, 0, false);\n", - "TypeError: Argument at index 5 is not a Number" - ); -} - -void test_rocky_api_graphics__fill_radial_range_check(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.rockyFillRadial(4096, 40, 10, 20, 0, false);\n", - "TypeError: Argument at index 0 is invalid: Value out of bounds for native type" - ); -} - -void test_rocky_api_graphics__fill_radial_zero_radius(void) { - prv_global_init_and_set_ctx(); - // inner radius = 0 - EXECUTE_SCRIPT( - "ctx.rockyFillRadial(30, 40, 0, 20, 0, Math.PI);\n" - ); - MockCallRecording *const lc = &s_graphics_fill_radial_precise_internal.last_call; - cl_assert_equal_i(1, s_graphics_fill_radial_precise_internal.call_count); - cl_assert_equal_point_precise((PP(29.5, 39.5)), lc->fill_radial_precise.center); - cl_assert_equal_i(0, lc->fill_radial_precise.radius_inner.raw_value); - cl_assert_equal_i(20 * 8, lc->fill_radial_precise.radius_outer.raw_value); - - // inner radius capped to >= 0 - EXECUTE_SCRIPT( - "ctx.rockyFillRadial(30, 40, -10, 20, 0, Math.PI);\n" - ); - cl_assert_equal_i(2, s_graphics_fill_radial_precise_internal.call_count); - cl_assert_equal_point_precise((PP(29.5, 39.5)), lc->fill_radial_precise.center); - cl_assert_equal_i(0, lc->fill_radial_precise.radius_inner.raw_value); - cl_assert_equal_i(20 * 8, lc->fill_radial_precise.radius_outer.raw_value); - - // outer radius capped to >= 0 - EXECUTE_SCRIPT( - "ctx.rockyFillRadial(30, 40, -10, -20, 0, Math.PI);\n" - ); - cl_assert_equal_i(3, s_graphics_fill_radial_precise_internal.call_count); - cl_assert_equal_point_precise((PP(29.5, 39.5)), lc->fill_radial_precise.center); - cl_assert_equal_i(0, lc->fill_radial_precise.radius_inner.raw_value); - cl_assert_equal_i(0, lc->fill_radial_precise.radius_outer.raw_value); -} - -void test_rocky_api_graphics__line_styles(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT( - "ctx.lineWidth = 8;\n" - "var w = ctx.lineWidth;\n" - ); - - cl_assert_equal_i(1, s_graphics_context_set_stroke_width.call_count); - cl_assert_equal_i(8, s_graphics_context_set_stroke_width.last_call.width); - ASSERT_JS_GLOBAL_EQUALS_I("w", s_graphics_context_set_stroke_width.last_call.width); - - EXECUTE_SCRIPT( - "ctx.lineWidth = 2.1;\n" - "var w = ctx.lineWidth;\n" - ); - ASSERT_JS_GLOBAL_EQUALS_I("w", 2); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.lineWidth = -4;\n", - "TypeError: Argument at index 0 is invalid: Value out of bounds for native type" - ); - EXECUTE_SCRIPT("var w = ctx.lineWidth;\n"); - ASSERT_JS_GLOBAL_EQUALS_I("w", 2); -} - -void test_rocky_api_graphics__line_styles_check_bounds(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.lineWidth = -1;", - "TypeError: Argument at index 0 is invalid: Value out of bounds for native type" - ); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.lineWidth = 256;", - "TypeError: Argument at index 0 is invalid: Value out of bounds for native type" - ); -} - -void test_rocky_api_graphics__fill_and_stroke_styles(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT( - "ctx.fillStyle = '#f00';\n" - "ctx.strokeStyle = 'white';\n" - "var c = ctx.fillStyle;\n" - ); - - cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count); - cl_assert_equal_i(GColorRedARGB8, s_graphics_context_set_fill_color.last_call.color.argb); - cl_assert_equal_i(1, s_graphics_context_set_stroke_color.call_count); - cl_assert_equal_i(GColorWhiteARGB8, s_graphics_context_set_stroke_color.last_call.color.argb); - - // ignores invalid values - EXECUTE_SCRIPT( - "ctx.fillStyle = 'unknown';\n" - "ctx.strokeStyle = '4%2F';\n" - ); - cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count); - cl_assert_equal_i(1, s_graphics_context_set_stroke_color.call_count); -} - -void test_rocky_api_graphics__canvas_state(void) { - prv_global_init_and_set_ctx(); - - // calling restore if nothing was stored is a no-op - s_context.draw_state.fill_color.argb = 1; - EXECUTE_SCRIPT("ctx.restore()\n"); - cl_assert_equal_i(1, s_context.draw_state.fill_color.argb); - - EXECUTE_SCRIPT("ctx.save()\n"); // 1 - s_context.draw_state.fill_color.argb = 2; - EXECUTE_SCRIPT("ctx.save()\n"); // 2 - s_context.draw_state.fill_color.argb = 3; - - EXECUTE_SCRIPT("ctx.restore()\n"); // -> 2 (one element left) - cl_assert_equal_i(2, s_context.draw_state.fill_color.argb); - - EXECUTE_SCRIPT("ctx.restore()\n"); // -> 1 (no element left) - cl_assert_equal_i(1, s_context.draw_state.fill_color.argb); - - EXECUTE_SCRIPT("ctx.restore()\n"); // no-op - cl_assert_equal_i(1, s_context.draw_state.fill_color.argb); -} - -static const int16_t large_int = 10000; - -void test_rocky_api_graphics__fill_text(void) { - prv_global_init_and_set_ctx(); - - // we do this in C and not JS as color binding is not linked in this unit-test - // what we want to test though is that the text color is taken from fill color - rocky_api_graphics_get_gcontext()->draw_state.fill_color = GColorRed; - EXECUTE_SCRIPT( - "ctx.fillText('some text', 10, 10);\n" - ); - - cl_assert_equal_i(1, s_graphics_draw_text.call_count); - cl_assert_equal_s("some text", s_graphics_draw_text.last_call.draw_text.text); - cl_assert_equal_i(GColorRedARGB8, s_graphics_draw_text.last_call.draw_text.color.argb); - cl_assert_equal_rect((GRect(10, 10, large_int, large_int)), - s_graphics_draw_text.last_call.draw_text.box); - - rocky_api_graphics_get_gcontext()->draw_state.fill_color = GColorBlue; - EXECUTE_SCRIPT( - "ctx.fillText('more text', -10.5, 5000, 60);\n" - ); - - cl_assert_equal_i(2, s_graphics_draw_text.call_count); - cl_assert_equal_s("more text", s_graphics_draw_text.last_call.draw_text.text); - cl_assert_equal_i(GColorBlueARGB8, s_graphics_draw_text.last_call.draw_text.color.argb); - cl_assert_equal_rect((GRect(-11, 5000, 60, large_int)), - s_graphics_draw_text.last_call.draw_text.box); -} - -void test_rocky_api_graphics__fill_text_coordinates(void) { - prv_global_init_and_set_ctx(); - EXECUTE_SCRIPT("ctx.fillText('some text', 0, 1.5);"); - cl_assert_equal_rect((GRect(0, 2, large_int, large_int)), - s_graphics_draw_text.last_call.draw_text.box); - - EXECUTE_SCRIPT("ctx.fillText('some text', -0.2, 1.2, 10.5);"); - cl_assert_equal_rect((GRect(0, 1, 11, large_int)), - s_graphics_draw_text.last_call.draw_text.box); - - EXECUTE_SCRIPT("ctx.fillText('some text', -0.5, 1.2, -0.5);"); - cl_assert_equal_rect((GRect(-1, 1, -1, large_int)), - s_graphics_draw_text.last_call.draw_text.box); -} - -void test_rocky_api_graphics__fill_text_aligned(void) { - prv_global_init_and_set_ctx(); - - // we do this in C and not JS as color binding is not linked in this unit-test - // what we want to test though is that the text color is taken from fill color - - EXECUTE_SCRIPT( - "ctx.textAlign = 'left';\n" - "ctx.fillText('some text', 100, 100);\n" - ); - - cl_assert_equal_i(1, s_graphics_draw_text.call_count); - cl_assert_equal_rect((GRect(100, 100, large_int, large_int)), - s_graphics_draw_text.last_call.draw_text.box); - - EXECUTE_SCRIPT( - "ctx.textAlign = 'center';\n" - "ctx.fillText('some text', 100, 100);\n" - ); - - cl_assert_equal_i(2, s_graphics_draw_text.call_count); - cl_assert_equal_rect((GRect(-4900, 100, large_int, large_int)), - s_graphics_draw_text.last_call.draw_text.box); - - EXECUTE_SCRIPT( - "ctx.textAlign = 'right';\n" - "ctx.fillText('some text', 100, 100);\n" - ); - - cl_assert_equal_i(3, s_graphics_draw_text.call_count); - cl_assert_equal_rect((GRect(-9900, 100, large_int, large_int)), - s_graphics_draw_text.last_call.draw_text.box); -} - -void test_rocky_api_graphics__text_align(void) { - prv_global_init_and_set_ctx(); - - // intial value - cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment); - - s_rocky_text_state.alignment = (GTextAlignment)-1; - // unsupported values don't change the value - EXECUTE_SCRIPT("ctx.textAlign = 123;\n"); - cl_assert_equal_i(-1, s_rocky_text_state.alignment); - EXECUTE_SCRIPT("ctx.textAlign = 'unknown';\n"); - cl_assert_equal_i(-1, s_rocky_text_state.alignment); - - - EXECUTE_SCRIPT("ctx.textAlign = 'left';\nvar a = ctx.textAlign;\n"); - cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment); - ASSERT_JS_GLOBAL_EQUALS_S("a", "left"); - - EXECUTE_SCRIPT("ctx.textAlign = 'right';\nvar a = ctx.textAlign;\n"); - cl_assert_equal_i(GTextAlignmentRight, s_rocky_text_state.alignment); - ASSERT_JS_GLOBAL_EQUALS_S("a", "right"); - - EXECUTE_SCRIPT("ctx.textAlign = 'center';\nvar a = ctx.textAlign;\n"); - cl_assert_equal_i(GTextAlignmentCenter, s_rocky_text_state.alignment); - ASSERT_JS_GLOBAL_EQUALS_S("a", "center"); - - // we only support LTR - EXECUTE_SCRIPT("ctx.textAlign = 'start';\nvar a = ctx.textAlign;\n"); - cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment); - ASSERT_JS_GLOBAL_EQUALS_S("a", "left"); - - EXECUTE_SCRIPT("ctx.textAlign = 'end';\nvar a = ctx.textAlign;\n"); - cl_assert_equal_i(GTextAlignmentRight, s_rocky_text_state.alignment); - ASSERT_JS_GLOBAL_EQUALS_S("a", "right"); -} - -void test_rocky_api_graphics__text_font(void) { - cl_assert_equal_i(0, s_fonts_get_system_font.call_count); - s_fonts_get_system_font__result = (GFont)123; - rocky_global_init(s_graphics_api); - cl_assert_equal_i(1, s_fonts_get_system_font.call_count); - cl_assert_equal_p((GFont)123, s_rocky_text_state.font); - - // make this easily testable by putting it int JS context as global - Layer l = {.bounds = GRect(0, 0, 144, 168)}; - const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l); - jerry_set_object_field(jerry_get_global_object(), "ctx", ctx); - - - s_rocky_text_state.font = (GFont)-1; - // unsupported values don't change the value - EXECUTE_SCRIPT("ctx.font = 123;\n"); - cl_assert_equal_p((GFont)-1, s_rocky_text_state.font); - EXECUTE_SCRIPT("ctx.font = 'unknown';\n"); - cl_assert_equal_p((GFont)-1, s_rocky_text_state.font); - cl_assert_equal_i(1, s_fonts_get_system_font.call_count); - - EXECUTE_SCRIPT("ctx.font = '14px bold Gothic';\n"); - cl_assert_equal_i(2, s_fonts_get_system_font.call_count); - cl_assert_equal_p(FONT_KEY_GOTHIC_14_BOLD, s_fonts_get_system_font.last_call.font_key); - - EXECUTE_SCRIPT("ctx.font = '28px Gothic';\nvar f = ctx.font;\n"); - ASSERT_JS_GLOBAL_EQUALS_S("f", "28px Gothic"); -} - -extern T_STATIC void prv_graphics_color_to_char_buffer(GColor8 color, char *buf_out); - -#define TEST_COLOR_STRING(gcolor, expect_str) do { \ - char buf[12]; \ - prv_graphics_color_to_char_buffer(gcolor, buf); \ - cl_assert_equal_s(buf, expect_str); \ - } while(0); - -void test_rocky_api_graphics__color_names(void) { - TEST_COLOR_STRING(GColorClear, "transparent"); - TEST_COLOR_STRING((GColor){ .a = 1 }, "transparent"); - TEST_COLOR_STRING(GColorRed, "#FF0000"); - TEST_COLOR_STRING(GColorMalachite, "#00FF55"); -} - -extern T_STATIC const RockyAPISystemFontDefinition s_font_definitions[]; -bool prv_font_definition_from_value(jerry_value_t value, RockyAPISystemFontDefinition **result); - -void test_rocky_api_graphics__text_font_names_unique(void) { - rocky_global_init(s_graphics_api); - - const RockyAPISystemFontDefinition *def = s_font_definitions; - while (def->js_name) { - const jerry_value_t name_js = jerry_create_string((jerry_char_t *)def->js_name); - RockyAPISystemFontDefinition *cmp_def = NULL; - bool actual = prv_font_definition_from_value(name_js, &cmp_def); - cl_assert_equal_b(true, actual); - cl_assert_equal_s(cmp_def->res_key, def->res_key); - jerry_release_value(name_js); - def++; - } -} - -void test_rocky_api_graphics__measure_text(void) { - prv_global_init_and_set_ctx(); - - // fill text_state with unique values we can test against - s_rocky_text_state = (RockyAPITextState) { - .font = (GFont)-1, - .overflow_mode = (GTextOverflowMode)-2, - .alignment = (GTextAlignment)-3, - .text_attributes = (GTextAttributes *)-4, - }; - - s_graphics_text_layout_get_max_used_size__result = GSize(123, 456); - EXECUTE_SCRIPT( - "var tm = ctx.measureText('foo');\n" - "var tm_w = tm.width;\n" - "var tm_h = tm.height;\n" - ); - ASSERT_JS_GLOBAL_EQUALS_I("tm_w", 123); - ASSERT_JS_GLOBAL_EQUALS_I("tm_h", 456); - - cl_assert_equal_i(1, s_graphics_text_layout_get_max_used_size.call_count); - const MockCallRecording *lc = &s_graphics_text_layout_get_max_used_size.last_call; - cl_assert_equal_s("foo", lc->max_used_size.text); - cl_assert_equal_p(s_rocky_text_state.font, lc->max_used_size.font); - cl_assert_equal_rect((GRect(0, 0, INT16_MAX, INT16_MAX)), lc->max_used_size.box); - cl_assert_equal_i(s_rocky_text_state.overflow_mode, lc->max_used_size.overflow_mode); - cl_assert_equal_i(s_rocky_text_state.alignment, lc->max_used_size.alignment); -} - -void test_rocky_api_graphics__state_initialized_between_renders(void) { - prv_global_init_and_set_ctx(); - - // fill text_state with unique values we can test against - s_rocky_text_state = (RockyAPITextState) { - .font = (GFont)-1, - .overflow_mode = (GTextOverflowMode)-2, - .alignment = (GTextAlignment)-3, - .text_attributes = (GTextAttributes *)-4, - }; - - EXECUTE_SCRIPT("_rocky.on('draw', function(e) {});"); - Layer *l = &app_window_stack_get_top_window()->layer; - l->update_proc(l, NULL); - - cl_assert_equal_i(1, s_fonts_get_system_font.call_count); - cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment); - cl_assert_equal_i(GTextOverflowModeWordWrap, s_rocky_text_state.overflow_mode); - cl_assert_equal_p(NULL, s_rocky_text_state.text_attributes); -} - -void test_rocky_api_graphics__context_2d_prototype_wrap_function(void) { - prv_global_init_and_set_ctx(); - - EXECUTE_SCRIPT("var origFillRect = _rocky.CanvasRenderingContext2D.prototype.fillRect;\n" - "_rocky.CanvasRenderingContext2D.prototype.fillRect = function(x, y, w, h) {\n" - " w *= 2;\n" - " h *= 2;\n" - " origFillRect.call(this, x, y, w, h);\n" - "};\n" - "ctx.fillRect(5, 6, 7, 8);\n" - ); - - cl_assert_equal_i(1, s_graphics_fill_rect.call_count); - cl_assert_equal_rect(GRect(5, 6, 7 * 2, 8 * 2), s_graphics_fill_rect.last_call.rect); -} diff --git a/tests/fw/javascript/test_rocky_api_graphics_color.c b/tests/fw/javascript/test_rocky_api_graphics_color.c deleted file mode 100644 index 5d095ebabc..0000000000 --- a/tests/fw/javascript/test_rocky_api_graphics_color.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/graphics/gtypes.h" -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" -#include "applib/rockyjs/api/rocky_api_graphics_color.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) { - *bounds_out = layer->bounds; -} - -static Window s_app_window_stack_get_top_window; -Window *app_window_stack_get_top_window() { - return &s_app_window_stack_get_top_window; -} - -GContext s_context; - -// mocks -static MockCallRecordings s_graphics_context_set_fill_color; -void graphics_context_set_fill_color(GContext* ctx, GColor color) { - record_mock_call(s_graphics_context_set_fill_color) { - .ctx = ctx, .color = color, - }; -} - -static MockCallRecordings s_graphics_context_set_stroke_color; -void graphics_context_set_stroke_color(GContext* ctx, GColor color) { - record_mock_call(s_graphics_context_set_stroke_color) { - .ctx = ctx, .color = color, - }; -} - -void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {} - -void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1) {} - -void graphics_fill_rect(GContext *ctx, const GRect *rect) {} - -void graphics_fill_round_rect_by_value(GContext *ctx, GRect rect, uint16_t corner_radius, - GCornerMask corner_mask) {} - -void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {} - -void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center, - Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer, - int32_t angle_start, int32_t angle_end) {} - -void layer_mark_dirty(Layer *layer) {} - -void rocky_api_graphics_path2d_add_canvas_methods(jerry_value_t obj) {} -void rocky_api_graphics_path2d_cleanup(void) {} -void rocky_api_graphics_path2d_reset_state(void) {} -void rocky_api_graphics_text_init(void) {} -void rocky_api_graphics_text_deinit(void) {} -void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {} -void rocky_api_graphics_text_reset_state(void) {} - -jerry_value_t prv_rocky_api_graphics_get_canvas_context_2d(void); - -void test_rocky_api_graphics_color__initialize(void) { - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - - s_app_window_stack_get_top_window = (Window){}; - s_context = (GContext){}; - s_app_state_get_graphics_context = &s_context; - s_app_event_loop_callback = NULL; - - s_graphics_context_set_stroke_color = (MockCallRecordings){0}; - s_graphics_context_set_fill_color = (MockCallRecordings){0}; -} - -void test_rocky_api_graphics_color__cleanup(void) { - fake_app_timer_deinit(); - - // some tests deinitialize the engine, avoid double de-init - if (app_state_get_rocky_runtime_context() != NULL) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - } -} - -static const RockyGlobalAPI *s_graphics_api[] = { - &GRAPHIC_APIS, - NULL, -}; - -#define cl_assert_parsed_color(str, expected_color_ptr) do { \ - GColor actual_color = {0}; \ - actual_color.argb = 123; \ - const bool actual_bool = rocky_api_graphics_color_parse(str, &actual_color); \ - if (expected_color_ptr) { \ - cl_assert_equal_b(true, actual_bool); \ - cl_assert_equal_i(((GColor*)(expected_color_ptr))->argb, actual_color.argb); \ - } else { \ - cl_assert_equal_b(false, actual_bool); \ - }\ - } while(0) - - -void test_rocky_api_graphics_color__parse_names(void) { - rocky_global_init(s_graphics_api); - - cl_assert_parsed_color("unknown", NULL); - cl_assert_parsed_color("clear", &GColorClear); - cl_assert_parsed_color("black", &GColorBlack); - cl_assert_parsed_color("red", &GColorRed); - cl_assert_parsed_color("white", &GColorWhite); - cl_assert_parsed_color("gray", &GColorLightGray); -} - -extern const RockyAPIGraphicsColorDefinition s_color_definitions[]; - - -void test_rocky_api_graphics_color__color_names_consistent(void) { - rocky_global_init(s_graphics_api); - - const RockyAPIGraphicsColorDefinition *def = s_color_definitions; - while (def->name) { - GColor8 actual; - const bool result = rocky_api_graphics_color_parse(def->name, &actual); - cl_assert_equal_b(true, result); - cl_assert_equal_i(def->value, actual.argb); - def++; - } -} - -void test_rocky_api_graphics_color__hex(void) { - // invalid cases - cl_assert_parsed_color("#", NULL); - cl_assert_parsed_color("##q3", NULL); - cl_assert_parsed_color("", NULL); - cl_assert_parsed_color("#00zz10", NULL); - cl_assert_parsed_color("#123456789", NULL); - - // different lengths - cl_assert_parsed_color("#f00", &GColorRed); - cl_assert_parsed_color("#FF0000", &GColorRed); - cl_assert_parsed_color("#F00f", &GColorRed); - cl_assert_parsed_color("#FF0000FF", &GColorRed); - - // discard rgb components if alpha == 0 - cl_assert_parsed_color("#12345600", &GColorClear); - cl_assert_parsed_color("#1230", &GColorClear); - - // correctly assign different components - cl_assert_parsed_color("#00FF00", &GColorGreen); - cl_assert_parsed_color("#0000FF", &GColorBlue); - -} \ No newline at end of file diff --git a/tests/fw/javascript/test_rocky_api_graphics_path2d.c b/tests/fw/javascript/test_rocky_api_graphics_path2d.c deleted file mode 100644 index 9bed1970a1..0000000000 --- a/tests/fw/javascript/test_rocky_api_graphics_path2d.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/graphics/gpath.h" -#include "applib/graphics/gtypes.h" -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" -#include "applib/rockyjs/api/rocky_api_graphics_color.h" -#include "applib/rockyjs/pbl_jerry_port.h" -#include "util/trig.h" - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_logging.h" -#include "fake_pbl_malloc.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) { - *bounds_out = layer->bounds; -} - -bool rocky_api_graphics_color_parse(const char *color_value, GColor8 *parsed_color) { - return false; -} - -bool rocky_api_graphics_color_from_value(jerry_value_t value, GColor *result) { - return false; -} - -static Window s_app_window_stack_get_top_window; -Window *app_window_stack_get_top_window() { - return &s_app_window_stack_get_top_window; -} - -GPointPrecise gpoint_from_polar_precise(const GPointPrecise *precise_center, - uint16_t precise_radius, int32_t angle) { - return GPointPreciseFromGPoint(GPointZero); -} - -GContext s_context; - -void rocky_api_graphics_text_init(void) {} -void rocky_api_graphics_text_deinit(void) {} -void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {} -void rocky_api_graphics_text_reset_state(void) {} - -void graphics_context_set_fill_color(GContext* ctx, GColor color) {} -void graphics_context_set_stroke_color(GContext* ctx, GColor color) {} -void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {} - -// mocks - - -static MockCallRecordings s_graphics_line_draw_precise_stroked; -void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) { - record_mock_call(s_graphics_line_draw_precise_stroked) { - .ctx = ctx, .pp0 = p0, .pp1 = p1, - }; -} - -void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1) { - // TODO: remove me PBL-42458 (still used for drawing arc) - record_mock_call(s_graphics_line_draw_precise_stroked) {.ctx = ctx}; -} - -MockCallRecordings s_graphics_draw_arc_precise; -void graphics_draw_arc_precise_internal(GContext *ctx, GPointPrecise center, Fixed_S16_3 radius, - int32_t angle_start, int32_t angle_end) { - record_mock_call(s_graphics_draw_arc_precise) { - .draw_arc.center = center, - .draw_arc.radius = radius, - .draw_arc.angle_start = angle_start, - .draw_arc.angle_end = angle_end, - }; -} - -MockCallRecordings s_gpath_draw_filled; -void gpath_draw_filled(GContext* ctx, GPath *path) { - record_mock_call(s_gpath_draw_filled) { - .path.num_points = path->num_points, - }; - memcpy(s_gpath_draw_filled.last_call.path.points, - path->points, sizeof(path->points[0]) * path->num_points); -} - - -void graphics_fill_rect(GContext *ctx, const GRect *rect) {} - -void graphics_fill_round_rect_by_value(GContext *ctx, GRect rect, uint16_t corner_radius, - GCornerMask corner_mask) {} - -void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {} - -void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center, - Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer, - int32_t angle_start, int32_t angle_end) {} - -void layer_mark_dirty(Layer *layer) {} - -jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer); -static void prv_create_global_ctx(void) { - // make this easily testable by putting it int JS context as global - Layer l = {.bounds = GRect(0, 0, 144, 168)}; - const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l); - cl_assert_equal_b(jerry_value_is_object(ctx), true); - jerry_set_object_field(jerry_get_global_object(), "ctx", ctx); -} - -void test_rocky_api_graphics_path2d__initialize(void) { - fake_malloc_set_largest_free_block(~0); - s_log_internal__expected = NULL; - - rocky_runtime_context_init(); - fake_app_timer_init(); - jerry_init(JERRY_INIT_EMPTY); - - s_app_window_stack_get_top_window = (Window){}; - s_context = (GContext){}; - s_app_state_get_graphics_context = &s_context; - s_app_event_loop_callback = NULL; - - s_graphics_line_draw_precise_stroked = (MockCallRecordings){0}; - s_graphics_draw_arc_precise = (MockCallRecordings){0}; - s_gpath_draw_filled = (MockCallRecordings){0}; -} - -void test_rocky_api_graphics_path2d__cleanup(void) { - fake_app_timer_deinit(); - - // Frees the internal path steps array (): - rocky_api_graphics_path2d_reset_state(); - - // some tests deinitialize the engine, avoid double de-init - if (app_state_get_rocky_runtime_context() != NULL) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - } - - fake_pbl_malloc_check_net_allocs(); -} - -static const RockyGlobalAPI *s_graphics_api[] = { - &GRAPHIC_APIS, - NULL, -}; - -#define PP(x, y) \ - GPointPrecise((int16_t)(((x)) * FIXED_S16_3_FACTOR), \ - (int16_t)(((y)) * FIXED_S16_3_FACTOR)) - -void test_rocky_api_graphics_path2d__invalid_coords(void) { - rocky_global_init(s_graphics_api); - prv_create_global_ctx(); - - EXECUTE_SCRIPT("ctx.moveTo(4095.375, -4095.5);"); - EXECUTE_SCRIPT_EXPECT_ERROR("ctx.moveTo(4096.5, 0);", "TypeError: Value out of bounds"); - EXECUTE_SCRIPT_EXPECT_ERROR("ctx.moveTo(0, -4095.625);", "TypeError: Value out of bounds"); - - EXECUTE_SCRIPT("ctx.lineTo(4095.375, -4095.5);"); - EXECUTE_SCRIPT_EXPECT_ERROR("ctx.lineTo(4096.5, 0);", "TypeError: Value out of bounds"); - EXECUTE_SCRIPT_EXPECT_ERROR("ctx.lineTo(0, -4095.625);", "TypeError: Value out of bounds"); -} - -void test_rocky_api_graphics_path2d__minimal_path(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT( - "ctx.beginPath();\n" - "ctx.moveTo(1, 2);\n" - "ctx.lineTo(3.5, -4.5);\n" - "ctx.stroke();\n" - ); - - cl_assert_equal_i(1, s_graphics_line_draw_precise_stroked.call_count); - cl_assert_equal_point_precise(PP(0.5, 1.5), s_graphics_line_draw_precise_stroked.last_call.pp0); - cl_assert_equal_point_precise(PP(3, -5), s_graphics_line_draw_precise_stroked.last_call.pp1); - - EXECUTE_SCRIPT( - "ctx.fill();\n" - ); - cl_assert_equal_i(0, s_gpath_draw_filled.call_count); -} - - -void test_rocky_api_graphics_path2d__more_lines(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT( - "ctx.beginPath();\n" - "ctx.moveTo(1, 2);\n" - "ctx.lineTo(3, 4);\n" - "ctx.lineTo(5, 6);\n" - "ctx.lineTo(7, 8);\n" - "ctx.moveTo(9, 10);\n" - "ctx.lineTo(11, 12);\n" - "ctx.stroke();\n" - ); - - cl_assert_equal_i(4, s_graphics_line_draw_precise_stroked.call_count); - cl_assert_equal_point_precise(PP(8.5, 9.5), s_graphics_line_draw_precise_stroked.last_call.pp0); - cl_assert_equal_point_precise(PP(10.5, 11.5), s_graphics_line_draw_precise_stroked.last_call.pp1); - - EXECUTE_SCRIPT( - "ctx.fill();\n" - ); - // only first shape has at least 3 points - cl_assert_equal_i(1, s_gpath_draw_filled.call_count); - MockCallRecording *lc = &s_gpath_draw_filled.last_call; - cl_assert_equal_i(4, lc->path.num_points); - cl_assert_equal_point(GPoint(0, 1), lc->path.points[0]); - cl_assert_equal_point(GPoint(2, 3), lc->path.points[1]); - cl_assert_equal_point(GPoint(4, 5), lc->path.points[2]); - cl_assert_equal_point(GPoint(6, 7), lc->path.points[3]); -} - -void test_rocky_api_graphics_path2d__fill(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT( - "ctx.moveTo(1, 2);\n" - "ctx.lineTo(3, 4);\n" - "ctx.fill();\n" - ); - // only 2 points - cl_assert_equal_i(0, s_gpath_draw_filled.call_count); - - EXECUTE_SCRIPT( - "ctx.lineTo(5, 6);\n" - "ctx.fill();\n" - ); - cl_assert_equal_i(1, s_gpath_draw_filled.call_count); - MockCallRecording *lc = &s_gpath_draw_filled.last_call; - cl_assert_equal_i(3, lc->path.num_points); - cl_assert_equal_point(GPoint(0, 1), lc->path.points[0]); - cl_assert_equal_point(GPoint(2, 3), lc->path.points[1]); - cl_assert_equal_point(GPoint(4, 5), lc->path.points[2]); - - s_gpath_draw_filled.call_count = 0; - EXECUTE_SCRIPT( - "ctx.moveTo(7, 8);\n" - "ctx.lineTo(9, 10);\n" - "ctx.fill();\n" - ); - - // still only the first part (before the .moveTo()) as the second only has two points - cl_assert_equal_i(1, s_gpath_draw_filled.call_count); - cl_assert_equal_i(3, lc->path.num_points); - cl_assert_equal_point(GPoint(0, 1), lc->path.points[0]); - cl_assert_equal_point(GPoint(2, 3), lc->path.points[1]); - cl_assert_equal_point(GPoint(4, 5), lc->path.points[2]); - - s_gpath_draw_filled.call_count = 0; - EXECUTE_SCRIPT( - "ctx.lineTo(11.5, 12.7);\n" - "ctx.fill();\n" - ); - // still only the first part (before the .moveTo()) as the second only has two points - cl_assert_equal_i(2, s_gpath_draw_filled.call_count); - cl_assert_equal_i(3, lc->path.num_points); - cl_assert_equal_point(GPoint(6, 7), lc->path.points[0]); - cl_assert_equal_point(GPoint(8, 9), lc->path.points[1]); - cl_assert_equal_point(GPoint(11, 12), lc->path.points[2]); - - EXECUTE_SCRIPT_EXPECT_ERROR( - "ctx.arc(1, 2, 3, 4, 5);\n" - "ctx.fill();\n" - , "TypeError: fill() does not support arc()"); -} - -void test_rocky_api_graphics_path2d__fill_oom(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT( - "ctx.moveTo(1, 2);\n" - "ctx.lineTo(3, 4);\n" - "ctx.lineTo(5, 6);\n" - ); - - // OOM! - fake_malloc_set_largest_free_block(0); - - // Call implementation directly instead of executing a script, to avoid mallocs by the VM itself: - extern jerry_value_t rocky_api_graphics_path2d_call_fill(void); - const jerry_value_t error_value = rocky_api_graphics_path2d_call_fill(); - ASSERT_JS_ERROR(error_value, "RangeError: Out of memory: too many points to fill"); -} - -void test_rocky_api_graphics_path2d__arc(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT( - "ctx.beginPath();\n" - "ctx.moveTo(1, 2);\n" - "ctx.arc(50, 40, 30, Math.PI, 0);\n" - "ctx.arc(60, 80.1, 20.5, 0, Math.PI, false);\n" - "ctx.stroke();\n" - ); - - cl_assert_equal_i(2, s_graphics_line_draw_precise_stroked.call_count); - cl_assert_equal_i(2, s_graphics_draw_arc_precise.call_count); - MockCallRecording *lc = &s_graphics_draw_arc_precise.last_call; - cl_assert_equal_point_precise(PP(59.5, 79.625), lc->draw_arc.center); - cl_assert_equal_i(20.5 * 8, lc->draw_arc.radius.raw_value); - cl_assert_equal_i(TRIG_MAX_ANGLE * 1 / 4, lc->draw_arc.angle_start); - cl_assert_equal_i(TRIG_MAX_ANGLE * 3 / 4, lc->draw_arc.angle_end); -} - -void test_rocky_api_graphics_path2d__anti_clockwise(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT( - "ctx.beginPath();\n" - "ctx.moveTo(80, 40);\n" - "ctx.arc(60, 80, 20, 0, Math.PI, true);\n" - "ctx.stroke();\n" - ); - - cl_assert_equal_i(1, s_graphics_line_draw_precise_stroked.call_count); - - cl_assert_equal_i(1, s_graphics_draw_arc_precise.call_count); - MockCallRecording *lc = &s_graphics_draw_arc_precise.last_call; - cl_assert_equal_point_precise(PP(59.5, 79.5), lc->draw_arc.center); - cl_assert_equal_i(20 * 8, lc->draw_arc.radius.raw_value); - cl_assert_equal_i(TRIG_MAX_ANGLE * 3 / 4, lc->draw_arc.angle_start); - cl_assert_equal_i(TRIG_MAX_ANGLE * 5 / 4, lc->draw_arc.angle_end); -} - -void test_rocky_api_graphics_path2d__unsupported(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - EXECUTE_SCRIPT_EXPECT_UNDEFINED("ctx.arcTo"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("ctx.bezierCurveTo"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("ctx.quadraticCurveTo"); -} - -extern size_t s_rocky_path_steps_num; -extern RockyAPIPathStep *s_rocky_path_steps; - - -void test_rocky_api_graphics_path2d__state_initialized_between_renders(void) { - rocky_global_init(s_graphics_api); - - s_rocky_path_steps_num = 2; - EXECUTE_SCRIPT("_rocky.on('draw', function(e) {});"); - Layer *l = &app_window_stack_get_top_window()->layer; - l->update_proc(l, NULL); - - cl_assert_equal_i(0, s_rocky_path_steps_num); -} - -void test_rocky_api_graphics_path2d__rect(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - cl_assert_equal_i(0, s_rocky_path_steps_num); - EXECUTE_SCRIPT( - "ctx.moveTo(1, 2);\n" - "ctx.rect(3, 4, 5, 6);\n" - ); - cl_assert_equal_i(6, s_rocky_path_steps_num); - - EXECUTE_SCRIPT( - "ctx.rect(7, 8, 9, 10);\n" - ); - cl_assert_equal_i(11, s_rocky_path_steps_num); - cl_assert_equal_i(RockyAPIPathStepType_MoveTo, s_rocky_path_steps[0].type); - cl_assert_equal_i(RockyAPIPathStepType_MoveTo, s_rocky_path_steps[1].type); - cl_assert_equal_i(RockyAPIPathStepType_LineTo, s_rocky_path_steps[5].type); - cl_assert_equal_i(RockyAPIPathStepType_MoveTo, s_rocky_path_steps[6].type); - - cl_assert_equal_point_precise((GPointPrecise(20, 28)), s_rocky_path_steps[1].pt.xy); - cl_assert_equal_point_precise((GPointPrecise(60, 76)), s_rocky_path_steps[3].pt.xy); - cl_assert_equal_point_precise((GPointPrecise(20, 28)), s_rocky_path_steps[5].pt.xy); - - // actual correctness of these values is test in test_rocky_api_graphics_rendering.c - cl_assert_equal_vector_precise((GVectorPrecise(0, 8)), s_rocky_path_steps[1].pt.fill_delta); - cl_assert_equal_vector_precise((GVectorPrecise(8, 0)), s_rocky_path_steps[3].pt.fill_delta); - cl_assert_equal_vector_precise((GVectorPrecise(0, 8)), s_rocky_path_steps[5].pt.fill_delta); -} - -void test_rocky_api_graphics_path2d__close_path(void) { - rocky_global_init(s_graphics_api); - - prv_create_global_ctx(); - - cl_assert_equal_i(0, s_rocky_path_steps_num); - EXECUTE_SCRIPT( - "ctx.moveTo(1, 2);\n" - "ctx.closePath();\n" - ); - cl_assert_equal_i(1, s_rocky_path_steps_num); - EXECUTE_SCRIPT( - "ctx.lineTo(3, 4);\n" - "ctx.closePath();\n" - ); - cl_assert_equal_i(3, s_rocky_path_steps_num); - cl_assert_equal_i(RockyAPIPathStepType_LineTo, s_rocky_path_steps[2].type); - cl_assert_equal_point_precise(GPointPrecise(4, 12), (s_rocky_path_steps[0].pt.xy)); - cl_assert_equal_point_precise(GPointPrecise(4, 12), (s_rocky_path_steps[2].pt.xy)); -} - -extern jerry_value_t rocky_api_graphics_path2d_try_allocate_steps(size_t increment_steps); -extern size_t rocky_api_graphics_path2d_min_array_len(void); -extern size_t rocky_api_graphics_path2d_array_len(void); - -void test_rocky_api_graphics_path2d__initial_increment_larger_than_initial_size(void) { - cl_assert_equal_i(rocky_api_graphics_path2d_array_len(), 0); - const size_t min_size = rocky_api_graphics_path2d_min_array_len(); - const jerry_value_t rv = rocky_api_graphics_path2d_try_allocate_steps(min_size + 1); - ASSERT_JS_ERROR(rv, NULL); - jerry_release_value(rv); - const size_t actual_size = rocky_api_graphics_path2d_array_len(); - cl_assert(actual_size >= min_size + 1); -} - -void test_rocky_api_graphics_path2d__array_realloc_oom(void) { - fake_malloc_set_largest_free_block(0); - const jerry_value_t rv = rocky_api_graphics_path2d_try_allocate_steps(1); - ASSERT_JS_ERROR(rv, "RangeError: Out of memory: can't create more path steps"); - jerry_release_value(rv); -} diff --git a/tests/fw/javascript/test_rocky_api_graphics_rendering.c b/tests/fw/javascript/test_rocky_api_graphics_rendering.c deleted file mode 100644 index 93b0cc3e1c..0000000000 --- a/tests/fw/javascript/test_rocky_api_graphics_rendering.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" - -#define DO_NOT_STUB_LEGACY2 1 -#include "test_rocky_common.h" - -#include "applib/graphics/gtypes.h" -#include "applib/rockyjs/api/rocky_api.h" -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_graphics.h" -#include "applib/rockyjs/api/rocky_api_graphics_text.h" -#include "applib/rockyjs/pbl_jerry_port.h" -#include "util/trig.h" -#include "applib/graphics/framebuffer.h" - -#include "../graphics/util.h" - - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -bool gbitmap_init_with_png_data(GBitmap *bitmap, const uint8_t *data, size_t data_size) { - return false; -} - -bool gbitmap_png_data_is_png(const uint8_t *data, size_t data_size) { - return false; -} - -void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) { - *bounds_out = layer->bounds; -} - -void layer_mark_dirty(Layer *layer) {} - -static Window s_app_window_stack_get_top_window; -Window *app_window_stack_get_top_window() { - return &s_app_window_stack_get_top_window; -} - -// no text rendering in this test -void rocky_api_graphics_text_init(void) {} -void rocky_api_graphics_text_deinit(void) {} -void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {} -void rocky_api_graphics_text_reset_state(void) {} - - -//GBitmap s_bitmap; -//uint8_t s_bitmap_data[DISPLAY_FRAMEBUFFER_BYTES]; -GContext s_context; -FrameBuffer *s_framebuffer; -GBitmap *s_pixels; - -static void prv_init_gcontext(GSize size) { - graphics_context_init(&s_context, s_framebuffer, GContextInitializationMode_App); - framebuffer_clear(s_framebuffer); - if (s_pixels) { - gbitmap_destroy(s_pixels); - } - s_pixels = gbitmap_create_blank(size, GBITMAP_NATIVE_FORMAT); - memset(s_pixels->addr, 0xff, size.h * s_pixels->row_size_bytes); - s_context.dest_bitmap = *s_pixels; - s_context.draw_state.clip_box = (GRect){.size = size}; - s_context.draw_state.drawing_box = s_context.draw_state.clip_box; - s_app_state_get_graphics_context = &s_context; -} - -void test_rocky_api_graphics_rendering__initialize(void) { - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - - s_framebuffer = malloc(sizeof(FrameBuffer)); - framebuffer_init(s_framebuffer, &(GSize) { DISP_COLS, DISP_ROWS }); - s_app_window_stack_get_top_window = (Window){}; - - prv_init_gcontext((GSize) { DISP_COLS, DISP_ROWS }); - s_app_event_loop_callback = NULL; -} - -void test_rocky_api_graphics_rendering__cleanup(void) { - fake_app_timer_deinit(); - - // some tests deinitialize the engine, avoid double de-init - if (app_state_get_rocky_runtime_context() != NULL) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - } - gbitmap_destroy(s_pixels); - s_pixels = NULL; - free(s_framebuffer); -} - -static const RockyGlobalAPI *s_graphics_api[] = { - &GRAPHIC_APIS, - NULL, -}; - -jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer); - -static const jerry_value_t prv_global_init_and_set_ctx(void) { - rocky_global_init(s_graphics_api); - - // make this easily testable by putting it int JS context as global - Layer l = {.bounds = GRect(0, 0, 144, 168)}; - const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l); - jerry_set_object_field(jerry_get_global_object(), "ctx", ctx); - - return ctx; -} - - -void test_rocky_api_graphics_rendering__lines(void) { - prv_global_init_and_set_ctx(); - - // taken from http://fiddle.jshell.net/9298zub9/2/ - EXECUTE_SCRIPT( - "var t1 = 10;\n" - "var b1 = 20.5;\n" - "var t2 = 30.5;\n" - "var b2 = 40;\n" - " \n" - "for (var i = 1; i <= 5; i++) {\n" - " ctx.beginPath();\n" - " var x1 = 20 * i;\n" - " var x2 = x1 + 10.5; \n" - " ctx.moveTo(x1, t1);\n" - " ctx.lineTo(x1, b1);\n" - " ctx.moveTo(x2, t1);\n" - " ctx.lineTo(x2, b1);\n" - "\n" - " ctx.moveTo(x1, t2);\n" - " ctx.lineTo(x1, b2);\n" - " ctx.moveTo(x2, t2);\n" - " ctx.lineTo(x2, b2);\n" - "\n" - " ctx.lineWidth = i;\n" - " ctx.stroke();\n" - "}\n" - "for (var i = 1; i <= 5; i++) {\n" - " ctx.beginPath();\n" - " var y1 = 40 + i * 20;\n" - " var y2 = y1 + 10.5;\n" - " ctx.moveTo(t1, y1);\n" - " ctx.lineTo(b1, y1);\n" - " ctx.moveTo(t1, y2);\n" - " ctx.lineTo(b1, y2);\n" - " \n" - " ctx.moveTo(t2, y1);\n" - " ctx.lineTo(b2, y1);\n" - " ctx.moveTo(t2, y2);\n" - " ctx.lineTo(b2, y2);\n" - "\n" - " ctx.lineWidth = i;\n" - " ctx.stroke();\n" - "}\n" - "for (var i = 1; i <= 5; i++) {\n" - " ctx.beginPath();\n" - " var xx = 50;\n" - " var yy = 50;\n" - " var d = 15 * i;\n" - " ctx.moveTo(xx, yy + d);\n" - " ctx.lineTo(xx + d, yy);\n" - "\n" - " ctx.lineWidth = i;\n" - " ctx.stroke();\n" - "}" - ); - - const bool eq_result = - gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_lines")); - cl_check(eq_result); -} - -void test_rocky_api_graphics_rendering__rect(void) { - prv_init_gcontext(GSize(500, 150)); - prv_global_init_and_set_ctx(); - - // taken from http://fiddle.jshell.net/a5gjzb7c/6/ - EXECUTE_SCRIPT( - "function render(x, y, f) {\n" - " f(x + 10, y + 10, 10, 10);\n" - " f(x + 30.2, y + 10, 10, 10.2);\n" - " f(x + 50.5, y + 10, 10, 10);\n" - " f(x + 70.7, y + 10, 10.5, 10.8);\n" - " f(x + 10, y + 30.5, 10, 10);\n" - " f(x + 30.2, y + 30.5, 10, 10.2);\n" - " f(x + 50.5, y + 30.5, 10, 10);\n" - " f(x + 70.7, y + 30.5, 10.5, 10.8);\n" - " \n" - " f(x + 90, y + 10, 0, 0);\n" - " f(x + 110, y + 10, 0.5, 0.5);\n" - " f(x + 90, y + 30, -2, -2);\n" - " f(x + 110, y + 30, -5.5, -6);\n" - "}" - "\n" - "for (var i = 0; i <= 3; i++) {\n" - " ctx.lineWidth = i;\n" - " var x = 120 * i;\n" - " render(x, 0, ctx[i == 0 ? 'fillRect' : 'strokeRect'].bind(ctx));\n" - " render(x, 50, function(x,y,w,h) {\n" - " ctx.beginPath();\n" - " ctx.rect(x, y, w, h);\n" - " ctx[i == 0? 'fill' : 'stroke'](); \n" - " });\n" - " render(x, 100, function r(x, y, w, h) {\n" - " ctx.beginPath();\n" - " ctx.moveTo(x, y);\n" - " ctx.lineTo(x + w, y);\n" - " ctx.lineTo(x + w, y + h);\n" - " ctx.lineTo(x, y + h);\n" - " ctx.lineTo(x, y);\n" - " ctx[i == 0? 'fill' : 'stroke'](); \n" - " });\n" - "}" - ); - - const bool eq_result = - gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_rect")); - cl_check(eq_result); -} - - -void test_rocky_api_graphics_rendering__arc(void) { - prv_init_gcontext(GSize(500, 300)); - prv_global_init_and_set_ctx(); - - // http://fiddle.jshell.net/uopr1ez2/2/ - EXECUTE_SCRIPT( - "var xx = 200;\n" - "\n" - "function f(x, y, r, a1, a2) {\n" - " ctx.beginPath();\n" - " ctx.arc(x, y, r, a1, a2, false);\n" - " ctx.stroke();\n" - "\n" - " ctx.rockyFillRadial(x + xx, y, 0, r, a1, a2);\n" - "}\n" - "\n" - "function g(x, y, a1, a2) {\n" - " f(x, y, 5, a1, a2);\n" - " f(x, y, 15.5, a1, a2);\n" - " f(x, y, 25.2, a1, a2);\n" - " f(x, y, 34.8, a1, a2);\n" - "}\n" - "\n" - "function h(x, y, a1, a2) {\n" - " for (var i = 0; i < 4; i++) {\n" - " ctx.lineWidth = i + 1;\n" - " g(x, y + 40 * i, a1, a2);\n" - " }\n" - "}\n" - "\n" - "h(2, 2, 0, 0.5 * Math.PI);\n" - "h(50.5, 2.5, 0, 0.5 * Math.PI);\n" - "h(100.2, 2.2, 0, 0.5 * Math.PI);\n" - "h(150.8, 2.8, 0, 0.5 * Math.PI);\n" - "\n" - "ctx.lineWidth = 1;\n" - "f(20, 200, 10, 0, 2 * Math.PI);\n" - "f(60.5, 200, 10, 0, 2 * Math.PI);\n" - "f(100.5, 200.5, 10, 0, 2 * Math.PI);\n" - "f(140, 200.5, 10, 0, 2 * Math.PI);\n" - "\n" - "f(20, 240, 11, 0, 2 * Math.PI);\n" - "f(60.5, 240, 11, 0, 2 * Math.PI);\n" - "f(100.5, 240.5, 11, 0, 2 * Math.PI);\n" - "f(140, 240.5, 11, 0, 2 * Math.PI);\n" - "\n" - "f(20, 280, 11, 0, -0.5 * Math.PI);" - ); - - const bool eq_result = - gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_arc")); - cl_check(eq_result); -} \ No newline at end of file diff --git a/tests/fw/javascript/test_rocky_api_memory.c b/tests/fw/javascript/test_rocky_api_memory.c deleted file mode 100644 index ff865e6969..0000000000 --- a/tests/fw/javascript/test_rocky_api_memory.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_memory.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include "syscall/syscall.h" - -#include - -// Standard -#include - -// Fakes -#include "fake_app_timer.h" -#include "fake_logging.h" -#include "fake_pbl_malloc.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -static int s_sys_analytics_inc_call_count; -void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { - cl_assert_equal_i(metric, ANALYTICS_APP_METRIC_MEM_ROCKY_RECURSIVE_MEMORYPRESSURE_EVENT_COUNT); - ++s_sys_analytics_inc_call_count; -} - -static const RockyGlobalAPI *s_api[] = { - &MEMORY_APIS, - NULL, -}; - -static bool s_skip_pbl_malloc_check; - -#define assert_oom_app_fault() \ - cl_assert_equal_i(s_app_heap_analytics_log_rocky_heap_oom_fault_call_count, 1) - -#define assert_no_oom_app_fault() \ - cl_assert_equal_i(s_app_heap_analytics_log_rocky_heap_oom_fault_call_count, 0) - -void test_rocky_api_memory__initialize(void) { - s_sys_analytics_inc_call_count = 0; - s_app_heap_analytics_log_rocky_heap_oom_fault_call_count = 0; - - fake_pbl_malloc_clear_tracking(); - s_skip_pbl_malloc_check = false; - - s_log_internal__expected = NULL; - - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - rocky_global_init(s_api); -} - -void test_rocky_api_memory__cleanup(void) { - rocky_global_deinit(); - jerry_cleanup(); - rocky_runtime_context_deinit(); - - if (!s_skip_pbl_malloc_check) { - fake_pbl_malloc_check_net_allocs(); - } -} - -void test_rocky_api_memory__event(void) { - cl_assert_equal_b(false, rocky_global_has_event_handlers("memorypressure")); - - jmem_heap_stats_t before_stats = {}; - jmem_heap_get_stats(&before_stats); - - EXECUTE_SCRIPT("_rocky.on('memorypressure', function(){});"); - - // After registering a handler for 'memorypressure', expect a drop of more than N bytes of heap - // because of the reservation of headroom space: - jmem_heap_stats_t after_stats = {}; - jmem_heap_get_stats(&after_stats); - cl_assert(after_stats.allocated_bytes - before_stats.allocated_bytes >= - ROCKY_API_MEMORY_HEADROOM_DESIRED_SIZE_BYTES); - - cl_assert_equal_b(true, rocky_global_has_event_handlers("memorypressure")); -} - -void test_rocky_api_memory__oom_app_fault_if_handler_allocates_more_than_headroom(void) { - s_skip_pbl_malloc_check = true; - cl_assert_passert(EXECUTE_SCRIPT( - "var data = [];\n" - "_rocky.on('memorypressure', function(){\n" - " var handlerData = [];\n" - " for (var i = 0; i < 100000; i++) {handlerData.push(i);}\n" - "});\n" - "for (var i = 0; i < 100000; i++) {data.push(i);}\n" - )); - assert_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 1); -} - -void test_rocky_api_memory__no_oom_app_fault_if_handler_frees_up_enough_memory_empty_array(void) { - // Note to the reader: the lifecycle of `data` is not what you might think it is on first sight: - // When `data = [];` executes, the original `data` will still be retained, because the original - // execution context is still on the stack. Only after the the 'memorypressure' handler returns - // and that for(){} block finishes, is the original `data` released! - - EXECUTE_SCRIPT( - "var data = [];\n" - "var level = undefined;" - "_rocky.on('memorypressure', function(e){\n" - " level = e.level;\n" - " data = [];\n" - "});\n" - "for (var i = 0; i < 100000; i++) {data.push(i);}\n" - ); - assert_no_oom_app_fault(); - ASSERT_JS_GLOBAL_EQUALS_S("level", "high"); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); -} - -void test_rocky_api_memory__no_oom_app_fault_if_handler_frees_up_enough_memory_empty_object(void) { - EXECUTE_SCRIPT( - "var data = {};\n" - "_rocky.on('memorypressure', function(e){\n" - " data = {};\n" - "});\n" - "for (var i = 0; i < 100000; i++) {data[i] = i;}\n" - ); - assert_no_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); -} - -void test_rocky_api_memory__no_oom_app_fault_if_handler_frees_up_enough_memory_put_props_for(void) { - // This example uses a lot of properties on an Object to store things. - // When running out of memory, these are dropped to free up memory, using the `delete` operator. - EXECUTE_SCRIPT( - "var first = 0;\n" - "var i = 0;\n" - "var obj = {};\n" - "_rocky.on('memorypressure', function(e){\n" - " for (var j = first; j < i; j++) {\n" - " delete obj[j];\n" - " }\n" - " first = i;\n" - "});\n" - "for (i = first; i < 100000; i++) {\n" - " obj[i] = i;" - "}\n" - ); - assert_no_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); -} - -#if 0 -// This doesn't work at the moment, because the `in` operator allocates a ton of memory... :( for -// the same reason as why Array.pop() has a footprint that's proportional to the number of elements. -void test_rocky_api_memory -__no_oom_app_fault_if_handler_frees_up_enough_mem_put_props_for_in(void) { - EXECUTE_SCRIPT( - "var obj = {};\n" - "_rocky.on('memorypressure', function(e){\n" - " for (var p in obj) {\n" - " delete obj[p];\n" - " }\n" - "});\n" - "for (var i = 0; i < 100000; i++) {\n" - " obj['' + i] = i;" - "}\n" - ); - assert_no_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); -} -#endif - -#if 0 -// This doesn't work because the putting the `length` property of an array end up calling -// ecma_op_object_get_property_names, which is has a memory footprint proportional to the number of -// elements/properties.. -void test_rocky_api_memory -__no_oom_app_fault_if_handler_frees_up_enough_memory_put_length(void) { - EXECUTE_SCRIPT( - "var cache = [];\n" - "_rocky.on('memorypressure', function(event) {\n" - " while (cache.length > 0) {\n" - " delete cache[cache.length - 1];\n" - " --cache.length;\n" - " }\n" - "})\n;" - "for (var i = 0; i < 100000; i++) {\n" - " cache.push(i);\n" - "}\n" - ); - assert_no_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); -} -#endif - -#if 0 -// This doesn't work at the moment, because https://github.com/Samsung/jerryscript/issues/1370 -void test_rocky_api_memory -__no_oom_app_fault_if_handler_frees_up_enough_memory_simple(void) { - EXECUTE_SCRIPT( - "var cache = [];\n" - "_rocky.on('memorypressure', function(event) {\n" - " while (cache.length > 0) {\n" - " cache.pop();\n" - " }\n" - "})\n;" - "for (var i = 0; i < 100000; i++) {\n" - " cache.push(i);\n" - "}\n" - ); - assert_no_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); -} -#endif - -void test_rocky_api_memory__oom_app_fault_if_handler_does_not_free_up_enough_memory(void) { - s_skip_pbl_malloc_check = true; - - s_log_internal__expected_regex = (const char *[]){ - "Memory pressure level: high", - "heap size: [0-9]+, alloc'd: [0-9]+, waste: [0-9]+, largest free block: [0-9]+,", - "used blocks: [0-9]+, free blocks: [0-9]+", - "Fatal Error: 10", - NULL }; - - cl_assert_passert(EXECUTE_SCRIPT( - "var data = [];\n" - "var shouldContinue = true;\n" - "_rocky.on('memorypressure', function(){\n" - " shouldContinue = false;\n" - "});\n" - "for (var i = 0; shouldContinue && i < 100000; i++) {data.push(i);}\n" - )); - assert_oom_app_fault(); - cl_assert_equal_i(s_sys_analytics_inc_call_count, 0); - cl_assert(*s_log_internal__expected_regex == NULL); -} diff --git a/tests/fw/javascript/test_rocky_api_preferences.c b/tests/fw/javascript/test_rocky_api_preferences.c deleted file mode 100644 index 375ba20a09..0000000000 --- a/tests/fw/javascript/test_rocky_api_preferences.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_preferences.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include -#include - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_serial.h" -#include "stubs_sys_exit.h" - -//////////////////////////////////////////////////////////////////////////////// -// Fakes / Stubs -//////////////////////////////////////////////////////////////////////////////// - -static PreferredContentSize s_preferred_content_size; -PreferredContentSize preferred_content_size(void) { - return s_preferred_content_size; -} - -static const RockyGlobalAPI *s_preferences_api[] = { - &PREFERENCES_APIS, - NULL, -}; - -void test_rocky_api_preferences__initialize(void) { - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - s_preferred_content_size = PreferredContentSizeMedium; -} - -void test_rocky_api_preferences__cleanup(void) { - if (app_state_get_rocky_runtime_context() != NULL) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - } -} - -void test_rocky_api_preferences__unknown(void) { - s_preferred_content_size = (PreferredContentSize) -1; - rocky_global_init(s_preferences_api); - - EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize"); - ASSERT_JS_GLOBAL_EQUALS_S("size", "medium"); -} - -void test_rocky_api_preferences__always_valid(void) { - s_preferred_content_size = NumPreferredContentSizes; - rocky_global_init(s_preferences_api); - - EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize"); - ASSERT_JS_GLOBAL_EQUALS_S("size", "medium"); -} - -void test_rocky_api_preferences__small(void) { - s_preferred_content_size = PreferredContentSizeSmall; - rocky_global_init(s_preferences_api); - - EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize"); - ASSERT_JS_GLOBAL_EQUALS_S("size", "small"); -} - -void test_rocky_api_preferences__medium(void) { - s_preferred_content_size = PreferredContentSizeMedium; - rocky_global_init(s_preferences_api); - - EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize"); - ASSERT_JS_GLOBAL_EQUALS_S("size", "medium"); -} - -void test_rocky_api_preferences__large(void) { - s_preferred_content_size = PreferredContentSizeLarge; - rocky_global_init(s_preferences_api); - - EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize"); - ASSERT_JS_GLOBAL_EQUALS_S("size", "large"); -} - -void test_rocky_api_preferences__extra_large(void) { - s_preferred_content_size = PreferredContentSizeExtraLarge; - rocky_global_init(s_preferences_api); - - EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize"); - ASSERT_JS_GLOBAL_EQUALS_S("size", "x-large"); -} diff --git a/tests/fw/javascript/test_rocky_api_tickservice.c b/tests/fw/javascript/test_rocky_api_tickservice.c deleted file mode 100644 index ae0e747f21..0000000000 --- a/tests/fw/javascript/test_rocky_api_tickservice.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_tickservice.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -// Standard -#include "string.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_logging.h" -#if EMSCRIPTEN -#include "fake_time_timeshift_js.h" -#else -#include "fake_time.h" -#endif - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - -size_t heap_bytes_free(void) { - return 123456; -} - -void tick_timer_service_handle_time_change(void) {} - -MockCallRecordings s_tick_timer_service_subscribe; -void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) { - record_mock_call(s_tick_timer_service_subscribe) { - .tick_units = tick_units, - }; -} - -void test_rocky_api_tickservice__initialize(void) { - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - s_tick_timer_service_subscribe = (MockCallRecordings){0}; - s_log_internal__expected = NULL; -} - -void test_rocky_api_tickservice__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); -} - -static const RockyGlobalAPI *s_api[] = { - &TICKSERVICE_APIS, - NULL, -}; - -void test_rocky_api_tickservice__provides_events(void) { - rocky_global_init(s_api); - - cl_assert_equal_i(0, s_tick_timer_service_subscribe.call_count); - cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("hourchange")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("daychange")); - - EXECUTE_SCRIPT("_rocky.on('daychange', function() {});"); - cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("hourchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); - cl_assert_equal_i(1, s_tick_timer_service_subscribe.call_count); - cl_assert_equal_i(DAY_UNIT | MONTH_UNIT | YEAR_UNIT, - s_tick_timer_service_subscribe.last_call.tick_units); - - EXECUTE_SCRIPT( - "var hourHandler = function() {};\n" - "_rocky.on('hourchange', hourHandler);\n" - ); - cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); - cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); - cl_assert_equal_i(2, s_tick_timer_service_subscribe.call_count); - cl_assert_equal_i(HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, - s_tick_timer_service_subscribe.last_call.tick_units); - - EXECUTE_SCRIPT("_rocky.on('minutechange', function() {});"); - cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); - cl_assert_equal_i(3, s_tick_timer_service_subscribe.call_count); - cl_assert_equal_i(MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, - s_tick_timer_service_subscribe.last_call.tick_units); - - // register for minute again - EXECUTE_SCRIPT("_rocky.on('minutechange', function() {});"); - cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); - cl_assert_equal_i(4, s_tick_timer_service_subscribe.call_count); - cl_assert_equal_i(MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, - s_tick_timer_service_subscribe.last_call.tick_units); - - - EXECUTE_SCRIPT("_rocky.on('secondchange', function() {});"); - cl_assert_equal_b(true, rocky_global_has_event_handlers("secondchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange")); - cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange")); - cl_assert_equal_i(5, s_tick_timer_service_subscribe.call_count); - cl_assert_equal_i(SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT, - s_tick_timer_service_subscribe.last_call.tick_units); -} - - -void prv_tick_handler(struct tm *tick_time, TimeUnits units_changed); - -void test_rocky_api_tickservice__calls_handlers(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT( - "var s = 0;\n" - "var m = 0;\n" - "var h = 0;\n" - "var d = 0;\n" - "_rocky.on('secondchange', function(e) {s++;});" - "_rocky.on('minutechange', function(e) {m++;});" - "_rocky.on('hourchange', function(e) {h++;});" - "_rocky.on('daychange', function(e) {d++;});" - ); - - // subscribing already triggers a call - ASSERT_JS_GLOBAL_EQUALS_I("s", 1); - ASSERT_JS_GLOBAL_EQUALS_I("m", 1); - ASSERT_JS_GLOBAL_EQUALS_I("h", 1); - ASSERT_JS_GLOBAL_EQUALS_I("d", 1); - - // all handlers will be called as year change means minute change - prv_tick_handler(NULL, YEAR_UNIT); - ASSERT_JS_GLOBAL_EQUALS_I("s", 2); - ASSERT_JS_GLOBAL_EQUALS_I("m", 2); - ASSERT_JS_GLOBAL_EQUALS_I("h", 2); - ASSERT_JS_GLOBAL_EQUALS_I("d", 2); - - // same here, each time a day changes, a second changes, too - prv_tick_handler(NULL, MINUTE_UNIT | DAY_UNIT); - ASSERT_JS_GLOBAL_EQUALS_I("s", 3); - ASSERT_JS_GLOBAL_EQUALS_I("m", 3); - ASSERT_JS_GLOBAL_EQUALS_I("h", 3); - ASSERT_JS_GLOBAL_EQUALS_I("d", 3); - - prv_tick_handler(NULL, HOUR_UNIT); - ASSERT_JS_GLOBAL_EQUALS_I("s", 4); - ASSERT_JS_GLOBAL_EQUALS_I("m", 4); - ASSERT_JS_GLOBAL_EQUALS_I("h", 4); - ASSERT_JS_GLOBAL_EQUALS_I("d", 3); - - prv_tick_handler(NULL, MINUTE_UNIT); - ASSERT_JS_GLOBAL_EQUALS_I("s", 5); - ASSERT_JS_GLOBAL_EQUALS_I("m", 5); - ASSERT_JS_GLOBAL_EQUALS_I("h", 4); - ASSERT_JS_GLOBAL_EQUALS_I("d", 3); - - prv_tick_handler(NULL, SECOND_UNIT); - ASSERT_JS_GLOBAL_EQUALS_I("s", 6); - ASSERT_JS_GLOBAL_EQUALS_I("m", 5); - ASSERT_JS_GLOBAL_EQUALS_I("h", 4); - ASSERT_JS_GLOBAL_EQUALS_I("d", 3); -} - -void test_rocky_api_tickservice__event_types(void) { - rocky_global_init(s_api); - - EXECUTE_SCRIPT( - "var s = null;\n" - "var m = null;\n" - "var h = null;\n" - "var d = null;\n" - "_rocky.on('secondchange', function(e) {s = e.type;});" - "_rocky.on('minutechange', function(e) {m = e.type;});" - "_rocky.on('hourchange', function(e) {h = e.type;});" - "_rocky.on('daychange', function(e) {d = e.type;});" - ); - - // subscribing already triggers a call - ASSERT_JS_GLOBAL_EQUALS_S("s", "secondchange"); - ASSERT_JS_GLOBAL_EQUALS_S("m", "minutechange"); - ASSERT_JS_GLOBAL_EQUALS_S("h", "hourchange"); - ASSERT_JS_GLOBAL_EQUALS_S("d", "daychange"); -} - -void test_rocky_api_tickservice__error_in_handler_on_register(void) { - rocky_global_init(s_api); - - s_log_internal__expected = (const char *[]){ - "Unhandled exception", - " secondchange", - NULL - }; - EXECUTE_SCRIPT( - "_rocky.on('secondchange', function(e) { throw e.type; });" - ); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_tickservice__provides_event_date(void) { - rocky_global_init(s_api); - - s_log_internal__expected = (const char *[]){ NULL }; - - const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC - // Thu Mar 17 14:40:51 2016 PDT - const uint16_t cur_millis = 123; - fake_time_init(cur_time, cur_millis); - - EXECUTE_SCRIPT( - "var s = null;\n" - "var m = null;\n" - "var h = null;\n" - "var d = null;\n" - "_rocky.on('secondchange', function(e) { s = e.date.getSeconds(); });\n" - "_rocky.on('minutechange', function(e) { m = e.date.getMinutes(); });\n" - "_rocky.on('hourchange', function(e) { h = e.date.getHours(); });\n" - "_rocky.on('daychange', function(e) { d = e.date.getDate(); });\n" - ); - - ASSERT_JS_GLOBAL_EQUALS_D("s", 51.0); - ASSERT_JS_GLOBAL_EQUALS_D("m", 40.0); - ASSERT_JS_GLOBAL_EQUALS_D("h", 21.0); - ASSERT_JS_GLOBAL_EQUALS_D("d", 17.0); - - EXECUTE_SCRIPT( - "s = null;\n" - "m = null;\n" - "h = null;\n" - "d = null;\n" - ); - - struct tm tm = { - .tm_sec = 1, - .tm_min = 2, - .tm_hour = 3, - .tm_mday = 4, - .tm_mon = 5, - .tm_year = 116, // 2016 - }; - - prv_tick_handler(&tm, SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT); - ASSERT_JS_GLOBAL_EQUALS_D("s", 1.0); - ASSERT_JS_GLOBAL_EQUALS_D("m", 2.0); - ASSERT_JS_GLOBAL_EQUALS_D("h", 3.0); - ASSERT_JS_GLOBAL_EQUALS_D("d", 4.0); - - cl_assert(*s_log_internal__expected == NULL); -} diff --git a/tests/fw/javascript/test_rocky_api_timers.c b/tests/fw/javascript/test_rocky_api_timers.c deleted file mode 100644 index 9f4e46942e..0000000000 --- a/tests/fw/javascript/test_rocky_api_timers.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_timers.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_pbl_malloc.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_resources.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - - -void test_rocky_api_timers__initialize(void) { - fake_pbl_malloc_clear_tracking(); - fake_app_timer_init(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - TIMER_APIS.init(); -} - -void test_rocky_api_timers__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - fake_app_timer_deinit(); - fake_pbl_malloc_check_net_allocs(); -} - - -void test_rocky_api_timers__setInterval(void) { - char *script = - "var num_times = 0;" - "var extra_arg = 0;" - "var timer = setInterval(function(extra) {" - "num_times++;" - "extra_arg = extra;" - "}, 1000, 5);"; - - EXECUTE_SCRIPT(script); - - AppTimer *timer = (AppTimer *)(uintptr_t)prv_js_global_get_double("timer"); - - for (double d = 0.0; d < 5.0; d += 1.0) { - ASSERT_JS_GLOBAL_EQUALS_I("num_times", d); - cl_assert(fake_app_timer_is_scheduled(timer)); - cl_assert(app_timer_trigger(timer)); - ASSERT_JS_GLOBAL_EQUALS_I("extra_arg", 5.0); - } - - script = "clearInterval(timer);"; - EXECUTE_SCRIPT(script); - cl_assert(fake_app_timer_is_scheduled(timer) == false); -} - -void test_rocky_api_timers__setTimeout(void) { - char *script = - "var num_times = 0;" - "var f = function(extra) {" - " num_times++;" - "};" - "var timer = setTimeout('f()', '1000');"; - - EXECUTE_SCRIPT(script); - - AppTimer *timer = (AppTimer *)(uintptr_t)prv_js_global_get_double("timer"); - cl_assert_equal_i(fake_app_timer_get_timeout(timer), 1000); - cl_assert(fake_app_timer_is_scheduled(timer)); - cl_assert(app_timer_trigger(timer)); - - ASSERT_JS_GLOBAL_EQUALS_I("num_times", 1.0); - - // Verified timer will not trigger again - cl_assert(fake_app_timer_is_scheduled(timer) == false); -} - -void test_rocky_api_timers__bogus_clearInterval(void) { - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(0)"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(1234)"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(-1234)"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(undefined)"); -} - -void test_rocky_api_timers__bogus_clearTimeout(void) { - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(0)"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(1234)"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(-1234)"); - EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(undefined)"); -} diff --git a/tests/fw/javascript/test_rocky_api_util.c b/tests/fw/javascript/test_rocky_api_util.c deleted file mode 100644 index 6a74db42fd..0000000000 --- a/tests/fw/javascript/test_rocky_api_util.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" -#include "applib/rockyjs/pbl_jerry_port.h" -#include "../../third_party/jerryscript/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h" - -#include "applib/rockyjs/api/rocky_api_global.h" - -#include -#include - -// Fakes -#include "fake_logging.h" -#include "fake_pbl_malloc.h" -#if EMSCRIPTEN -#include "fake_time_timeshift_js.h" -#else -#include "fake_time.h" -#endif - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_serial.h" -#include "stubs_sys_exit.h" - - -#define FUNC_NAME "f" -#define ERROR_STRING "Oops!" - -T_STATIC void prv_log_uncaught_error(const jerry_value_t result); - -static int s_test_func_imp_call_count; -static int s_method_func_imp_call_count; - -void tick_timer_service_handle_time_change(void) {} - -//////////////////////////////////////////////////////////////////////////////// -// Initialization & Setup -//////////////////////////////////////////////////////////////////////////////// - -void test_rocky_api_util__initialize(void) { - s_test_func_imp_call_count = 0; - s_method_func_imp_call_count = 0; - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - s_log_internal__expected = NULL; -} - -void test_rocky_api_util__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - fake_pbl_malloc_check_net_allocs(); - s_log_internal__expected = NULL; -} - - -//////////////////////////////////////////////////////////////////////////////// -// Helpers for Tests -//////////////////////////////////////////////////////////////////////////////// - -static void prv_do_call_user_function(const char *script) { - const jerry_value_t rv = jerry_eval((jerry_char_t *)script, strlen(script), - false /* is_strict */); - cl_assert_equal_b(jerry_value_has_error_flag(rv), false); - jerry_release_value(rv); - - const jerry_value_t func = JS_GLOBAL_GET_VALUE(FUNC_NAME); - rocky_util_call_user_function_and_log_uncaught_error(func, jerry_create_undefined(), NULL, 0); - jerry_release_value(func); -} - -static void prv_do_eval(const char *eval_str) { - rocky_util_eval_and_log_uncaught_error((const jerry_char_t *)eval_str, strlen(eval_str)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests -//////////////////////////////////////////////////////////////////////////////// - -JERRY_FUNCTION(test_func_imp) { - ++s_test_func_imp_call_count; - return jerry_create_undefined(); -} - -JERRY_FUNCTION(method_func_imp) { - ++s_method_func_imp_call_count; - return jerry_create_undefined(); -} - -void test_rocky_api_util__rocky_add_constructor(void) { - static const RockyGlobalAPI *s_api[] = { - NULL, - }; - rocky_global_init(s_api); - - JS_VAR prototype = rocky_add_constructor("test", test_func_imp); - cl_assert_equal_b(jerry_value_is_object(prototype), true); - EXECUTE_SCRIPT("_rocky.test();"); - cl_assert_equal_i(1, s_test_func_imp_call_count); - - rocky_add_function(prototype, "method", method_func_imp); - EXECUTE_SCRIPT("var y = new _rocky.test(); y.method();"); - cl_assert_equal_i(1, s_method_func_imp_call_count); -} - -void test_rocky_api_util__error_print(void) { - s_log_internal__expected = (const char *[]){ - "Unhandled Error", - " "ERROR_STRING, - NULL - }; - - jerry_value_t error_val = - jerry_create_error(JERRY_ERROR_COMMON, (const jerry_char_t *)ERROR_STRING); - cl_assert(jerry_value_has_error_flag(error_val)); - - // NOTE: prv_log_uncaught_error() will call jerry_release_value(), so don't use error_val after - // this call returns: - prv_log_uncaught_error(error_val); - - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__call_no_error(void) { - s_log_internal__expected = (const char *[]){ NULL }; - const char *script = "var "FUNC_NAME" = function() { return 1; };"; - prv_do_call_user_function(script); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__call_throw_string(void) { - s_log_internal__expected = (const char *[]){ - "Unhandled exception", - " "ERROR_STRING, - NULL - }; - - const char *script = "var "FUNC_NAME" = function() { throw '"ERROR_STRING"'; };"; - prv_do_call_user_function(script); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__call_throw_number(void) { - s_log_internal__expected = (const char *[]){ - "Unhandled exception", - " 1", - NULL - }; - const char *script = "var "FUNC_NAME" = function() { throw 1; };"; - prv_do_call_user_function(script); - - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__call_throw_error(void) { - s_log_internal__expected = (const char *[]){ - "Unhandled Error", - " "ERROR_STRING, - NULL - }; - const char *script = "var "FUNC_NAME" = function() { throw new Error('"ERROR_STRING"'); };"; - prv_do_call_user_function(script); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__eval_no_error(void) { - s_log_internal__expected = (const char *[]){ NULL }; - prv_do_eval("1+1;"); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__eval_throw_string(void) { - s_log_internal__expected = (const char *[]){ - "Unhandled exception", - " "ERROR_STRING, - NULL - }; - prv_do_eval("throw '"ERROR_STRING"';"); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__eval_throw_error(void) { - s_log_internal__expected = (const char *[]){ - "Unhandled Error", - " "ERROR_STRING, - NULL - }; - prv_do_eval("throw new Error('"ERROR_STRING"');"); - cl_assert(*s_log_internal__expected == NULL); -} - -void test_rocky_api_util__create_date_now(void) { - const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC - // Thu Mar 17 14:40:51 2016 PDT - const uint16_t cur_millis = 123; - fake_time_init(cur_time, cur_millis); - - jerry_value_t now = rocky_util_create_date(NULL); - jerry_value_t getSeconds = jerry_get_object_field(now, "getSeconds"); - jerry_value_t getMinutes = jerry_get_object_field(now, "getMinutes"); - jerry_value_t getHours = jerry_get_object_field(now, "getHours"); - jerry_value_t getDate = jerry_get_object_field(now, "getDate"); - - jerry_value_t result_seconds = jerry_call_function(getSeconds, now, NULL, 0); - jerry_value_t result_minutes = jerry_call_function(getMinutes, now, NULL, 0); - jerry_value_t result_hours = jerry_call_function(getHours, now, NULL, 0); - jerry_value_t result_date = jerry_call_function(getDate, now, NULL, 0); - - cl_assert(jerry_get_number_value(result_seconds) == 51.0); - cl_assert(jerry_get_number_value(result_minutes) == 40.0); - cl_assert(jerry_get_number_value(result_hours) == 21.0); - cl_assert(jerry_get_number_value(result_date) == 17.0); - - jerry_release_value(result_date); - jerry_release_value(result_hours); - jerry_release_value(result_minutes); - jerry_release_value(result_seconds); - jerry_release_value(getDate); - jerry_release_value(getHours); - jerry_release_value(getMinutes); - jerry_release_value(getSeconds); - jerry_release_value(now); -} - -void test_rocky_api_util__ecma_date_make_day(void) { -#ifdef EMSCRIPTEN - printf("Skipping test %s", __FUNCTION__); -#else - cl_assert_equal_d(16861, ecma_date_make_day(2016, 2, 1)); // JerryScript's unit-test - cl_assert_equal_d(-25294, ecma_date_make_day(1900, 9, 1)); // not a leap year! - cl_assert_equal_d(17075, ecma_date_make_day(2016, 8, 31)); // Sept-31 == Oct-01 - cl_assert_equal_d(17075, ecma_date_make_day(2016, 9, 1)); // Oct-01 - cl_assert_equal_d(17045, ecma_date_make_day(2016, 8, 1)); // Sept-01 -#endif // EMSCRIPTEN -} - -void test_rocky_api_util__ecma_date_make_day_list(void) { -#ifdef EMSCRIPTEN - printf("Skipping test %s", __FUNCTION__); -#else - int fail_count = 0; - for(int y = 1950; y < 2050; y++) { - for(int m = 0; m < 12; m++) { - for (int d = 1; d < 32; d++) { - const ecma_number_t result = ecma_date_make_day(y, m, d); - if (isnan(result)) { - printf("failed for %04d-%02d-%02d\n", y, (m + 1), d); - fail_count++; - } else { -// printf("passed for %04d-%02d-%02d: %d\n", y, (m + 1), d, (int)result); - } - } - } - } - cl_assert_equal_i(0, fail_count); -#endif // EMSCRIPTEN -} - -void test_rocky_api_util__create_date_tm(void) { - const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC - // Thu Mar 17 14:40:51 2016 PDT - const uint16_t cur_millis = 123; - fake_time_init(cur_time, cur_millis); - struct tm tick_time = { - .tm_sec = 28, - .tm_min = 38, - .tm_hour = 18, - .tm_mday = 30, - .tm_mon = 9, - .tm_year = 116, - .tm_wday = 1, - .tm_yday = 275, - .tm_zone = "\000\000\000\000\000", - }; - - jerry_value_t now = rocky_util_create_date(&tick_time); - cl_assert(jerry_value_is_object(now)); - jerry_value_t getSeconds = jerry_get_object_field(now, "getSeconds"); - jerry_value_t getMinutes = jerry_get_object_field(now, "getMinutes"); - jerry_value_t getHours = jerry_get_object_field(now, "getHours"); - jerry_value_t getDate = jerry_get_object_field(now, "getDate"); - jerry_value_t getMonth = jerry_get_object_field(now, "getMonth"); - jerry_value_t getYear = jerry_get_object_field(now, "getYear"); - - jerry_value_t result_seconds = jerry_call_function(getSeconds, now, NULL, 0); - jerry_value_t result_minutes = jerry_call_function(getMinutes, now, NULL, 0); - jerry_value_t result_hours = jerry_call_function(getHours, now, NULL, 0); - jerry_value_t result_date = jerry_call_function(getDate, now, NULL, 0); - jerry_value_t result_month = jerry_call_function(getMonth, now, NULL, 0); - jerry_value_t result_year = jerry_call_function(getYear, now, NULL, 0); - - cl_assert_equal_d(jerry_get_number_value(result_seconds), 28.0); - cl_assert_equal_d(jerry_get_number_value(result_minutes), 38.0); - cl_assert_equal_d(jerry_get_number_value(result_hours), 18.0); - cl_assert_equal_d(jerry_get_number_value(result_date), 30.0); - cl_assert_equal_d(jerry_get_number_value(result_month), 9.0); - cl_assert_equal_d(jerry_get_number_value(result_year), 116.0); - - jerry_release_value(result_year); - jerry_release_value(result_month); - jerry_release_value(result_date); - jerry_release_value(result_hours); - jerry_release_value(result_minutes); - jerry_release_value(result_seconds); - jerry_release_value(getYear); - jerry_release_value(getMonth); - jerry_release_value(getDate); - jerry_release_value(getHours); - jerry_release_value(getMinutes); - jerry_release_value(getSeconds); - jerry_release_value(now); -} diff --git a/tests/fw/javascript/test_rocky_api_util_args.c b/tests/fw/javascript/test_rocky_api_util_args.c deleted file mode 100644 index 8d5d9c9484..0000000000 --- a/tests/fw/javascript/test_rocky_api_util_args.c +++ /dev/null @@ -1,620 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "applib/rockyjs/api/rocky_api_util_args.h" - -#include "applib/rockyjs/api/rocky_api_errors.h" -#include "applib/rockyjs/api/rocky_api_util.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include - -#include -#include - -// Fakes -#include "fake_pbl_malloc.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_state.h" -#include "stubs_passert.h" -#include "stubs_logging.h" -#include "stubs_serial.h" -#include "stubs_sys_exit.h" - -#define JERRY_ARGS_MAKE(...) \ - jerry_value_t argv[] = { \ - __VA_ARGS__ \ - }; \ - jerry_length_t argc = ARRAY_LENGTH(argv); - -#define JERRY_ARGS_RELEASE() \ - while(argc--) { \ - jerry_release_value(argv[argc]); \ - argv[argc] = 0; \ - } - -#define ROCKY_ARGS_ASSIGN(...) \ - const RockyArgBinding bindings[] = { \ - __VA_ARGS__ \ - }; \ - JS_VAR error_value = \ - rocky_args_assign(argc, argv, bindings, ARRAY_LENGTH(bindings)); \ - - -void test_rocky_api_util_args__initialize(void) { - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); -} - -void test_rocky_api_util_args__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - fake_pbl_malloc_check_net_allocs(); // Make sure no memory was leaked -} - -void test_rocky_api_util_args__missing_args(void) { - JERRY_ARGS_MAKE(/* argc == 0 */); - - uint8_t v; - ROCKY_ARGS_ASSIGN(ROCKY_ARG(v)); - ASSERT_JS_ERROR(error_value, "TypeError: Not enough arguments"); -} - -void test_rocky_api_util_args__numbers_get_rounded_when_converting_to_integer(void) { - struct { - double input; - int16_t expected_output; - } cases[] = { - { - .input = 0.5, - .expected_output = 1, - }, - { - .input = -0.5, - .expected_output = -1, - }, - { - .input = 0.0, - .expected_output = 0, - }, - { - .input = -0.3, - .expected_output = 0, - }, - }; - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - int16_t output = ~0; - jerry_value_t v = jerry_create_number(cases[i].input); - JERRY_ARGS_MAKE(v); - ROCKY_ARGS_ASSIGN(ROCKY_ARG(output)); - cl_assert_equal_i(output, cases[i].expected_output); - ASSERT_JS_ERROR(error_value, NULL); - jerry_release_value(v); - } -} - -void test_rocky_api_util_args__numbers(void) { - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - int8_t s8; - int16_t s16; - int32_t s32; - int64_t s64; - double d; - - typedef struct { - long double u8; - long double u16; - long double u32; - long double u64; - long double s8; - long double s16; - long double s32; - long double s64; - double d; - char *expected_error_msg; - } NumbersTestCase; - - enum { - WithinLowerBounds, - WithinUpperBounds, - UnderLowerBounds, - OverUpperBounds, - }; - - // FIXME: fix limits.h / stdint.h / float.h so we can use the standard defines for these values.. - NumbersTestCase cases[4] = { - [WithinLowerBounds] = { - .u8 = 0.0, - .u16 = 0.0, - .u32 = 0.0, - .u64 = 0.0, - .s8 = -128.0, - .s16 = -32768.0, - .s32 = -2147483648.0, - .s64 = -9223372036854775808.0, - .d = 2.2250738585072014e-308, - .expected_error_msg = NULL, - }, - [WithinUpperBounds] = { - .u8 = 255.0, - .u16 = 65535.0, - .u32 = 4294967295.0, - .u64 = 9223372036854775807.0, - .s8 = 127.0, - .s16 = 32767.0, - .s32 = 2147483647.0, - .s64 = 9223372036854775807.0, - .d = 1.7976931348623157e+308, - .expected_error_msg = NULL, - }, - }; - - const long double margin = 0.001; - cases[UnderLowerBounds] = (NumbersTestCase) { - .u8 = cases[WithinLowerBounds].u8 - margin, - .u16 = cases[WithinLowerBounds].u16 - margin, - .u32 = cases[WithinLowerBounds].u32 - margin, - .u64 = cases[WithinLowerBounds].u64 - margin, - .s8 = cases[WithinLowerBounds].s8 - margin, - .s16 = cases[WithinLowerBounds].s16 - margin, - .s32 = cases[WithinLowerBounds].s32 - margin, - .s64 = cases[WithinLowerBounds].s64 - margin, - .d = cases[WithinLowerBounds].d - margin, - .expected_error_msg = - "TypeError: Argument at index 0 is invalid: Value out of bounds for native type", - }; - cases[OverUpperBounds] = (NumbersTestCase) { - .u8 = cases[WithinUpperBounds].u8 + margin, - .u16 = cases[WithinUpperBounds].u16 + margin, - .u32 = cases[WithinUpperBounds].u32 + margin, - .u64 = cases[WithinUpperBounds].u64 + margin, - .s8 = cases[WithinUpperBounds].s8 + margin, - .s16 = cases[WithinUpperBounds].s16 + margin, - .s32 = cases[WithinUpperBounds].s32 + margin, - .s64 = cases[WithinUpperBounds].s64 + margin, - .d = cases[WithinUpperBounds].d - margin, - .expected_error_msg = - "TypeError: Argument at index 0 is invalid: Value out of bounds for native type", - }; - - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - NumbersTestCase *c = &cases[i]; - - // Initialize to something that's not the expected value: - u8 = 1 + (uint8_t)c->u8; - u16 = 1 + (uint16_t)c->u16; - u32 = 1 + (uint16_t)c->u32; - u64 = 1 + (uint16_t)c->u64; - s8 = 1 + (int8_t)c->s8; - s16 = 1 + (int16_t)c->s16; - s32 = 1 + (int16_t)c->s32; - s64 = 1 + (int16_t)c->s64; - d = 1 + (double)c->d; - - JERRY_ARGS_MAKE( - jerry_create_number(c->u8), - jerry_create_number(c->u16), - jerry_create_number(c->u32), - jerry_create_number(c->u64), - jerry_create_number(c->s8), - jerry_create_number(c->s16), - jerry_create_number(c->s32), - jerry_create_number(c->s64), - jerry_create_number(c->d), - ); - ROCKY_ARGS_ASSIGN( - ROCKY_ARG(u8), - ROCKY_ARG(u16), - ROCKY_ARG(u32), - ROCKY_ARG(u64), - ROCKY_ARG(s8), - ROCKY_ARG(s16), - ROCKY_ARG(s32), - ROCKY_ARG(s64), - ROCKY_ARG(d), - ); - ASSERT_JS_ERROR(error_value, c->expected_error_msg); - if (!c->expected_error_msg) { - cl_assert_equal_i(u8, (uint8_t)c->u8); - cl_assert_equal_i(u16, (uint16_t)c->u16); - cl_assert_equal_i(u32, (uint32_t)c->u32); - cl_assert_equal_i(u64, (uint64_t)c->u64); - cl_assert_equal_i(s8, (int8_t)c->s8); - cl_assert_equal_i(s16, (int16_t)c->s16); - cl_assert_equal_i(s32, (int32_t)c->s32); - cl_assert_equal_i(s64, (int64_t)c->s64); - cl_assert_equal_d(d, (double)c->d); - } - JERRY_ARGS_RELEASE(); - } -} - -void test_rocky_api_util_args__number_type_mismatch(void) { - jerry_value_t mismatch_args[] = { - jerry_create_null(), - jerry_create_string((const jerry_char_t *)"one"), - jerry_create_string((const jerry_char_t *)"1"), - jerry_create_array(1), - jerry_create_boolean(true), - jerry_create_object(), - }; - - for (int i = 0; i < ARRAY_LENGTH(mismatch_args); ++i) { - jerry_value_t arg = mismatch_args[i]; - JERRY_ARGS_MAKE(arg); - - for (RockyArgType type = RockyArgTypeUInt8; type <= RockyArgTypeDouble; ++type) { - uint8_t buffer_untouched[8]; // The type check fails, so nothing is supposed to be written. - ROCKY_ARGS_ASSIGN( - ROCKY_ARG_MAKE(buffer_untouched, type, {}), - ); - ASSERT_JS_ERROR(error_value, "TypeError: Argument at index 0 is not a Number"); - } - - jerry_release_value(arg); - mismatch_args[i] = 0; - } -} - -static jerry_value_t prv_dummy(const jerry_value_t function_obj_p, - const jerry_value_t this_val, - const jerry_value_t args_p[], - const jerry_length_t args_count) { - return 0; -} - -void test_rocky_api_util_args__boolean(void) { - // No API to create NaN :( - char *nan_script = "Number.NaN"; - jerry_value_t nan = jerry_eval((jerry_char_t *)nan_script, - strlen(nan_script), - false /* is_strict */); - - struct { - jerry_value_t input; - bool expected_output; - } cases[] = { - // Falsy: false, 0, "", null, undefined, and NaN: - { - .input = jerry_create_boolean(false), - .expected_output = false, - }, - { - .input = jerry_create_number(0.0), - .expected_output = false, - }, - { - .input = jerry_create_string((const jerry_char_t *)""), - .expected_output = false, - }, - { - .input = jerry_create_null(), - .expected_output = false, - }, - { - .input = jerry_create_undefined(), - .expected_output = false, - }, - { - .input = nan, - .expected_output = false, - }, - // Truthy values: - { - .input = jerry_create_boolean(true), - .expected_output = true, - }, - { - .input = jerry_create_number(1.0), - .expected_output = true, - }, - { - .input = jerry_create_string((const jerry_char_t *)" "), - .expected_output = true, - }, - { - .input = jerry_create_array(0), - .expected_output = true, - }, - { - .input = jerry_create_object(), - .expected_output = true, - }, - { - .input = jerry_create_external_function(prv_dummy), - .expected_output = true, - }, - }; - - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - bool output = !cases[i].expected_output; - - jerry_value_t input = cases[i].input; - JERRY_ARGS_MAKE(input); - - ROCKY_ARGS_ASSIGN(ROCKY_ARG(output)); - cl_assert_equal_b(output, cases[i].expected_output); - ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */); - - jerry_release_value(input); - cases[i].input = 0; - } -} - -void test_rocky_api_util_args__string(void) { - struct { - jerry_value_t input; - char *expected_output; - } cases[] = { - { - .input = jerry_create_boolean(false), - .expected_output = "false", - }, - { - .input = jerry_create_number(0.0), - .expected_output = "0", - }, - { - .input = jerry_create_number(1.234e+60), - .expected_output = "1.234e+60", - }, - { - .input = jerry_create_string((const jerry_char_t *)""), - .expected_output = "", - }, - { - .input = jerry_create_string((const jerry_char_t *)"js"), - .expected_output = "js", - }, - { - .input = jerry_create_null(), - .expected_output = "null", - }, - { - .input = jerry_create_undefined(), - .expected_output = "undefined", - }, - { - .input = jerry_create_array(0), - .expected_output = "", // Kinda weird? - }, - { - .input = jerry_create_object(), - .expected_output = "[object Object]", - }, - }; - - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - jerry_value_t input = cases[i].input; - JERRY_ARGS_MAKE(input); - - // Exercise ROCKY_ARG (automatic binding creation, defaults to malloc'd string for char *): - { - char *output = NULL; - ROCKY_ARGS_ASSIGN(ROCKY_ARG(output)); - cl_assert_equal_s(output, cases[i].expected_output); - ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */); - task_free(output); - } - - // Exercise ROCKY_ARG (automatic binding creation, defaults to copy/no-malloc for char[]): - { - char output[16]; - ROCKY_ARGS_ASSIGN(ROCKY_ARG(output)); - cl_assert_equal_s(output, cases[i].expected_output); - ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */); - } - - // Exercise ROCKY_ARG_STR (no malloc, explicit binding creation): - { - char output[16]; - ROCKY_ARGS_ASSIGN(ROCKY_ARG_STR(output, sizeof(output))); - cl_assert_equal_s(output, cases[i].expected_output); - ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */); - } - - // Exercise ROCKY_ARG_STR (too small buffer provided): - { - char output[1] = {0xff}; - ROCKY_ARGS_ASSIGN(ROCKY_ARG_STR(output, 0)); - cl_assert_equal_s(output, ""); - ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */); - } - - jerry_release_value(input); - cases[i].input = 0; - } -} - -#define PP(_x, _y, _w, _h) { \ - .origin.x.raw_value = (_x), \ - .origin.y.raw_value = (_y), \ - .size.w.raw_value = (_w), \ - .size.h.raw_value = (_h), \ - } - -void test_rocky_api_util_args__grect_precise(void) { - struct { - jerry_value_t argv[5]; - size_t argc; - GRectPrecise expected_output; - char *error_msg; - } cases[] = { - { - .argv = { - [0] = jerry_create_number(0.0), - [1] = jerry_create_number(0.0), - [2] = jerry_create_number(0.0), - [3] = jerry_create_number(0.0), - }, - .argc = 4, - .expected_output = PP(0, 0, 0, 0), - }, - { - .argv = { - [0] = jerry_create_number(-0.5), - [1] = jerry_create_number(-0.2), - [2] = jerry_create_number(0.3), - [3] = jerry_create_number(0.5), - }, - .argc = 4, - .expected_output = PP(-4, -2, 2, 4), - }, - { - .argv = { - [0] = jerry_create_number(-4096.0), - [1] = jerry_create_number(-4096.0), - [2] = jerry_create_number(4095.875), - [3] = jerry_create_number(4095.875), - }, - .argc = 4, - .expected_output = PP(-32768, -32768, 32767, 32767), - }, - { - .argv = { - [0] = jerry_create_number(0), - [1] = jerry_create_number(0), - [2] = jerry_create_number(0), - [3] = jerry_create_number(4096.0), - }, - .argc = 4, - .error_msg = "TypeError: Argument at index 3 is invalid: Value out of bounds for native type", - }, - { - .argv = { - [0] = jerry_create_number(0), - [1] = jerry_create_number(0), - [2] = jerry_create_number(0), - }, - .argc = 3, - .error_msg = "TypeError: Not enough arguments", - }, - { - .argv = { - [0] = jerry_create_number(0), - [1] = jerry_create_number(0), - [2] = jerry_create_number(0), - [3] = jerry_create_null(), - }, - .argc = 4, - .error_msg = "TypeError: Argument at index 3 is not a Number", - }, - { - .argv = { - [0] = jerry_create_number(0), - [1] = jerry_create_number(0), - [2] = jerry_create_number(0), - [3] = jerry_create_string((const jerry_char_t *)"123"), - }, - .argc = 4, - .error_msg = "TypeError: Argument at index 3 is not a Number", - }, - }; - - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - jerry_value_t *argv = cases[i].argv; - jerry_length_t argc = cases[i].argc; - - GRectPrecise output; - memset(&output, 0x55, sizeof(output)); - - ROCKY_ARGS_ASSIGN(ROCKY_ARG(output)); - ASSERT_JS_ERROR(error_value, cases[i].error_msg); - if (!cases[i].error_msg) { - cl_assert_equal_i(output.origin.x.raw_value, cases[i].expected_output.origin.x.raw_value); - cl_assert_equal_i(output.origin.y.raw_value, cases[i].expected_output.origin.y.raw_value); - cl_assert_equal_i(output.size.w.raw_value, cases[i].expected_output.size.w.raw_value); - cl_assert_equal_i(output.size.h.raw_value, cases[i].expected_output.size.h.raw_value); - } - - for (uint32_t j = 0; j < argc; ++j) { - jerry_release_value(argv[j]); - } - } -} - -void test_rocky_api_util_args__gcolor(void) { - const char *type_error_msg = - "TypeError: Argument at index 0 is not a String ('color name' or '#hex') or Number"; - const char *invalid_value_msg = - "TypeError: Argument at index 0 is invalid: " \ - "Expecting String ('color name' or '#hex') or Number"; - struct { - jerry_value_t input; - GColor expected_output; - const char *error_msg; - } cases[] = { - { - .input = jerry_create_number(0.0), - .expected_output = { - .argb = 0, - }, - }, - { - .input = jerry_create_number(GColorJaegerGreenARGB8), - .expected_output = { - .argb = GColorJaegerGreenARGB8, - } - }, - { - .input = jerry_create_string((const jerry_char_t *)"red"), - .expected_output = { - .r = 0b11, - .g = 0, - .b = 0, - .a = 0b11, - } - }, - { - .input = jerry_create_string((const jerry_char_t *)"unknown-color"), - .error_msg = invalid_value_msg, - }, - { - .input = jerry_create_null(), - .error_msg = type_error_msg, - }, - }; - - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - jerry_value_t input = cases[i].input; - JERRY_ARGS_MAKE(input); - - GColor output; - memset(&output, 0x55, sizeof(output)); - - ROCKY_ARGS_ASSIGN(ROCKY_ARG(output)); - ASSERT_JS_ERROR(error_value, cases[i].error_msg); - if (!cases[i].error_msg) { - cl_assert_equal_i(output.a, cases[i].expected_output.a); - cl_assert_equal_i(output.r, cases[i].expected_output.r); - cl_assert_equal_i(output.g, cases[i].expected_output.g); - cl_assert_equal_i(output.b, cases[i].expected_output.b); - } - - jerry_release_value(cases[i].input); - cases[i].input = 0; - } -} diff --git a/tests/fw/javascript/test_rocky_api_watchinfo.c b/tests/fw/javascript/test_rocky_api_watchinfo.c deleted file mode 100644 index d05026c1ba..0000000000 --- a/tests/fw/javascript/test_rocky_api_watchinfo.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" - -#include "applib/rockyjs/api/rocky_api_global.h" -#include "applib/rockyjs/api/rocky_api_watchinfo.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include "applib/app_watch_info.h" -#include "system/version.h" - -#include - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_serial.h" -#include "stubs_sys_exit.h" - -//////////////////////////////////////////////////////////////////////////////// -// Fakes / Stubs -//////////////////////////////////////////////////////////////////////////////// - -#define TEST_LOCALE "test_locale" -#define VERSION_PREFIX "v4.0" -#define VERSION_SUFFIX "beta5" -#define VERSION_TAG VERSION_PREFIX"-"VERSION_SUFFIX -#define VERSION_MAJOR 4 -#define VERSION_MINOR 0 -#define VERSION_PATCH 122 - -char *app_get_system_locale(void) { - return TEST_LOCALE; -} - -bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata) { - strncpy(out_metadata->version_tag, VERSION_TAG, FW_METADATA_VERSION_TAG_BYTES); - return true; -} - -WatchInfoVersion watch_info_get_firmware_version(void) { - return (WatchInfoVersion) { - .major = VERSION_MAJOR, - .minor = VERSION_MINOR, - .patch = VERSION_PATCH - }; -} - -static WatchInfoColor s_watch_info_color; -WatchInfoColor sys_watch_info_get_color(void) { - return s_watch_info_color; -} - -static WatchInfoModel s_watch_info_model; -WatchInfoModel watch_info_get_model(void) { - return s_watch_info_model; -} - -static PlatformType s_current_app_sdk_platform; -PlatformType sys_get_current_app_sdk_platform(void) { - return s_current_app_sdk_platform; -} - - -static const RockyGlobalAPI *s_watchinfo_api[] = { - &WATCHINFO_APIS, - NULL, -}; - -void test_rocky_api_watchinfo__initialize(void) { - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); - s_watch_info_model = WATCH_INFO_MODEL_PEBBLE_TIME_STEEL; - s_watch_info_color = WATCH_INFO_COLOR_TIME_STEEL_GOLD; - s_current_app_sdk_platform = PlatformTypeBasalt; -} - -void test_rocky_api_watchinfo__cleanup(void) { - if (app_state_get_rocky_runtime_context() != NULL) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - } -} - -void test_rocky_api_watchinfo__model(void) { - rocky_global_init(s_watchinfo_api); - - EXECUTE_SCRIPT("var model = _rocky.watchInfo.model"); - ASSERT_JS_GLOBAL_EQUALS_S("model", "pebble_time_steel_gold"); -} - -void test_rocky_api_watchinfo__qemu_model(void) { - s_watch_info_color = -1; - rocky_global_init(s_watchinfo_api); - - EXECUTE_SCRIPT("var model = _rocky.watchInfo.model"); - ASSERT_JS_GLOBAL_EQUALS_S("model", "qemu_platform_basalt"); -} - -void test_rocky_api_watchinfo__language(void) { - rocky_global_init(s_watchinfo_api); - - EXECUTE_SCRIPT("var language = _rocky.watchInfo.language"); - ASSERT_JS_GLOBAL_EQUALS_S("language", TEST_LOCALE); -} - -void test_rocky_api_watchinfo__platform(void) { - rocky_global_init(s_watchinfo_api); - - EXECUTE_SCRIPT("var platform = _rocky.watchInfo.platform"); - ASSERT_JS_GLOBAL_EQUALS_S("platform", "basalt"); -} - -void test_rocky_api_watchinfo__platform_unknown(void) { - s_current_app_sdk_platform = -1; // Some unknown / invalid - rocky_global_init(s_watchinfo_api); - - EXECUTE_SCRIPT("var platform = _rocky.watchInfo.platform"); - ASSERT_JS_GLOBAL_EQUALS_S("platform", "unknown"); -} - -void test_rocky_api_watchinfo__fw_version(void) { - rocky_global_init(s_watchinfo_api); - - EXECUTE_SCRIPT("var major = _rocky.watchInfo.firmware.major"); - ASSERT_JS_GLOBAL_EQUALS_I("major", VERSION_MAJOR); - - EXECUTE_SCRIPT("var minor = _rocky.watchInfo.firmware.minor"); - ASSERT_JS_GLOBAL_EQUALS_I("minor", VERSION_MINOR); - - EXECUTE_SCRIPT("var patch = _rocky.watchInfo.firmware.patch"); - ASSERT_JS_GLOBAL_EQUALS_I("patch", VERSION_PATCH); - - EXECUTE_SCRIPT("var suffix = _rocky.watchInfo.firmware.suffix"); - ASSERT_JS_GLOBAL_EQUALS_S("suffix", VERSION_SUFFIX); -} diff --git a/tests/fw/javascript/test_rocky_common.h b/tests/fw/javascript/test_rocky_common.h deleted file mode 100644 index ded67c13a8..0000000000 --- a/tests/fw/javascript/test_rocky_common.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "applib/graphics/gtypes.h" -#include "applib/graphics/gcontext.h" -#include "applib/graphics/graphics_circle.h" -#include "applib/rockyjs/api/rocky_api_util.h" -#include "applib/tick_timer_service.h" -#include "applib/ui/layer.h" - -#include "kernel/events.h" -#include "syscall/syscall.h" - -#include -#include - -#include - -#define ASSERT_JS_GLOBAL_EQUALS_B(name, value) \ - cl_assert_equal_i(prv_js_global_get_boolean(name), value); - -#define ASSERT_JS_GLOBAL_EQUALS_I(name, value) \ - cl_assert_equal_i(prv_js_global_get_double(name), value); - -#define ASSERT_JS_GLOBAL_EQUALS_D(name, value) \ - cl_assert_equal_d(prv_js_global_get_double(name), value); - -#define ASSERT_JS_GLOBAL_EQUALS_S(name, value) \ - do { \ - char str_buffer[1024] = {}; \ - prv_js_global_get_string(name, str_buffer, sizeof(str_buffer)); \ - cl_assert_equal_s(str_buffer, value); \ - } while(0) - -#define JS_GLOBAL_GET_VALUE(name) \ - prv_js_global_get_value(name) - -#define ASSERT_JS_ERROR(error_value, expected_error_string) \ - do { \ - const bool expects_error = (expected_error_string) != NULL; \ - const bool is_error = jerry_value_has_error_flag(error_value); \ - if (is_error) { \ - if (expects_error) { \ - jerry_char_t buffer[100] = {0}; \ - jerry_object_to_string_to_utf8_char_buffer(error_value, buffer, sizeof(buffer)); \ - cl_assert_equal_s((char *)(expected_error_string), (char *)buffer); \ - } else {\ - rocky_log_exception("ASSERT_JS_ERROR", error_value); \ - cl_fail("Error value while no error was expected!"); \ - } \ - } else { \ - if (expects_error) { \ - cl_fail("expected error during JS execution did not occur"); \ - } \ - } \ - } while(0) - -#define EXECUTE_SCRIPT_EXPECT_UNDEFINED(script) \ - do { \ - JS_VAR rv = jerry_eval((jerry_char_t *)script, \ - strlen(script), \ - false /* is_strict */); \ - cl_assert_equal_b(true, jerry_value_is_undefined(rv)); \ - } while(0) - - -#define EXECUTE_SCRIPT_EXPECT_ERROR(script, expected_error) \ - do { \ - JS_VAR rv = jerry_eval((jerry_char_t *)script, \ - strlen(script), \ - false /* is_strict */); \ - ASSERT_JS_ERROR(rv, expected_error); \ - } while(0) - -#define EXECUTE_SCRIPT(script) EXECUTE_SCRIPT_EXPECT_ERROR((script), NULL) - -#define EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S(script, expected_c_string) \ - do { \ - JS_VAR rv = jerry_eval((jerry_char_t *)script, \ - strlen(script), false /* is_strict */); \ - ASSERT_JS_ERROR(rv, NULL); \ - JS_VAR rv_string = jerry_value_to_string(rv); \ - jerry_size_t sz = jerry_get_utf8_string_size(rv_string); \ - cl_assert(sz); \ - jerry_char_t *buffer = malloc(sz + 1); \ - memset(buffer, 0, sz + 1); \ - cl_assert(buffer); \ - cl_assert_equal_i(sz, jerry_string_to_utf8_char_buffer(rv_string, buffer, sz)); \ - cl_assert_equal_s((char *)buffer, expected_c_string); \ - free(buffer); \ - } while(0) - -#ifndef DO_NOT_STUB_LEGACY2 - -UNUSED bool process_manager_compiled_with_legacy2_sdk(void) { - return false; -} - -#endif - -UNUSED static jerry_value_t prv_js_global_get_value(char *name) { - JS_VAR global_obj = jerry_get_global_object(); - cl_assert_equal_b(jerry_value_is_undefined(global_obj), false); - - JS_VAR val = jerry_get_object_field(global_obj, name); - cl_assert_equal_b(jerry_value_is_undefined(val), false); - return jerry_acquire_value(val); -} - -UNUSED static bool prv_js_global_get_boolean(char *name) { - JS_VAR val = prv_js_global_get_value(name); - cl_assert(jerry_value_is_boolean(val)); - double rv = jerry_get_boolean_value(val); - return rv; -} - -UNUSED static double prv_js_global_get_double(char *name) { - JS_VAR val = prv_js_global_get_value(name); - cl_assert(jerry_value_is_number(val)); - double rv = jerry_get_number_value(val); - return rv; -} - -UNUSED static void prv_js_global_get_string(char *name, char *buffer, size_t buffer_size) { - JS_VAR val = prv_js_global_get_value(name); - cl_assert(jerry_value_is_string(val)); - ssize_t num_bytes = jerry_string_to_char_buffer(val, (jerry_char_t *)buffer, buffer_size); - cl_assert(num_bytes <= buffer_size); -} - -void (*s_app_event_loop_callback)(void); - -void app_event_loop_common(void) { - if (s_app_event_loop_callback) { - s_app_event_loop_callback(); - } -} - -#define cl_assert_equal_point(a, b) \ - do { \ - const GPoint __pt_a = (a); \ - const GPoint __pt_b = (b); \ - cl_assert_equal_i(__pt_a.x, __pt_b.x); \ - cl_assert_equal_i(__pt_a.y, __pt_b.y); \ - } while(0) - -#define cl_assert_equal_point_precise(a, b) \ - do { \ - const GPointPrecise __pt_a = (a); \ - const GPointPrecise __pt_b = (b); \ - cl_assert_equal_i(__pt_a.x.raw_value, __pt_b.x.raw_value); \ - cl_assert_equal_i(__pt_a.y.raw_value, __pt_b.y.raw_value); \ - } while(0) - -#define cl_assert_equal_vector_precise(a, b) \ - do { \ - const GVectorPrecise __a = (a); \ - const GVectorPrecise __b = (b); \ - cl_assert_equal_i(__a.dx.raw_value, __b.dx.raw_value); \ - cl_assert_equal_i(__a.dy.raw_value, __b.dy.raw_value); \ - } while(0) - -#define cl_assert_equal_size(a, b) \ - do { \ - const GSize __sz_a = (a); \ - const GSize __sz_b = (b); \ - cl_assert_equal_i(__sz_a.w, __sz_b.w); \ - cl_assert_equal_i(__sz_a.h, __sz_b.h); \ - } while(0) - -#define cl_assert_equal_size_precise(a, b) \ - do { \ - const GSizePrecise __sz_a = (a); \ - const GSizePrecise __sz_b = (b); \ - cl_assert_equal_i(__sz_a.w.raw_value, __sz_b.w.raw_value); \ - cl_assert_equal_i(__sz_a.h.raw_value, __sz_b.h.raw_value); \ - } while(0) - -#define cl_assert_equal_rect(a, b) \ - do { \ - const GRect __a = (a); \ - const GRect __b = (b); \ - cl_assert_equal_point(__a.origin, __b.origin); \ - cl_assert_equal_size(__a.size, __b.size); \ - } while(0) - -#define cl_assert_equal_rect_precise(a, b) \ - do { \ - const GRectPrecise __a = (a); \ - const GRectPrecise __b = (b); \ - cl_assert_equal_point_precise(__a.origin, __b.origin); \ - cl_assert_equal_size_precise(__a.size, __b.size); \ - } while(0) - - -typedef struct { - union { - Layer *layer; - GContext *ctx; - }; - union { - GColor color; - uint8_t width; - struct { - GPoint p0; - GPoint p1; - }; - struct { - GPointPrecise pp0; - GPointPrecise pp1; - }; - struct { - GPointPrecise center; - Fixed_S16_3 radius; - int32_t angle_start; - int32_t angle_end; - } draw_arc; - struct { - GRect rect; - uint16_t radius; - GCornerMask corner_mask; - }; - struct { - GRectPrecise prect; - }; - TimeUnits tick_units; - const char *font_key; - struct { - char text[200]; - GRect box; - GColor color; - } draw_text; - struct { - char text[200]; - GFont font; - GRect box; - GTextOverflowMode overflow_mode; - GTextAlignment alignment; - } max_used_size; - struct { - GPoint points[200]; - size_t num_points; - } path; - struct { - GPointPrecise center; - Fixed_S16_3 radius_inner; - Fixed_S16_3 radius_outer; - int32_t angle_start; - int32_t angle_end; - } fill_radial_precise; - }; -} MockCallRecording; - -typedef struct { - int call_count; - MockCallRecording last_call; -} MockCallRecordings; - -#define record_mock_call(var) \ - var.call_count++; \ - var.last_call = (MockCallRecording) - -// Handy for poking at .js things when debugging a unit test with gdb, for example: -// (gdb) call js_eval("1 + 1") -// 2 -// (gdb) call js_eval("Date()") -// Thu Jan 01 1970 00:00:00 GMT+00:00 -void js_eval(const char *src) { - JS_VAR rv = jerry_eval((const jerry_char_t *)src, strlen(src), false); - char buf[256] = {}; - jerry_object_to_string_to_utf8_char_buffer(rv, (jerry_char_t *)buf, sizeof(buf)); - printf("%s\n", buf); -} - -static void (*s_process_manager_callback)(void *data); -static void *s_process_manager_callback_data; -void sys_current_process_schedule_callback(CallbackEventCallback async_cb, - void *ctx) { - s_process_manager_callback = async_cb; - s_process_manager_callback_data = ctx; -} diff --git a/tests/fw/javascript/test_rocky_res.c b/tests/fw/javascript/test_rocky_res.c deleted file mode 100644 index 43c366ee0c..0000000000 --- a/tests/fw/javascript/test_rocky_res.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "test_jerry_port_common.h" - -#include "applib/rockyjs/rocky.h" -#include "applib/rockyjs/rocky_res.h" - -// Fakes -#include "fake_app_timer.h" -#include "fake_time.h" - -// Stubs -#include "stubs_app_manager.h" -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" -#include "stubs_pbl_malloc.h" -#include "stubs_sleep.h" -#include "stubs_serial.h" -#include "stubs_syscalls.h" -#include "stubs_sys_exit.h" - - -// instead of including internal jerry script headers here and pulling the whole dependency we will -// duplicate this value here instead -// if this fails, duplicate value from third_party/jerryscript/jerryscript/jerry-core/jerry-snapshot.h -#define JERRY_SNAPSHOT_VERSION (6u) - -void app_event_loop_common(void) {} - -bool sys_get_current_app_is_rocky_app(void) { - return true; -} - -size_t heap_bytes_free(void) { - return 123456; -} - -static uint32_t s_resource_storage_get_num_entries__result; -uint32_t resource_storage_get_num_entries(ResAppNum app_num, uint32_t resource_id) { - return s_resource_storage_get_num_entries__result; -} - -void rocky_api_watchface_init(void){} -void rocky_api_deinit(void){} - -size_t resource_size(ResAppNum app_num, uint32_t id) { - return 0; -} - -bool resource_is_valid(ResAppNum app_num, uint32_t resource_id) { - return true; -} - -int process_metadata_get_res_bank_num(const PebbleProcessMd *md) { - return 123; -} - -size_t resource_load_byte_range_system(ResAppNum app_num, uint32_t id, uint32_t start_offset, uint8_t *data, size_t num_bytes) { - const size_t some_bytes = 20; // a real snapshot is larger than just the header - - // in our test setup, we will treat resource - // 10 as an invalid snapshot header, and - // 20 as a valid one - switch (id) { - case 10: { - cl_assert(num_bytes >= sizeof(RockySnapshotHeader)); - *(RockySnapshotHeader*)data = (RockySnapshotHeader) { - .version = 123, // invalid - }; - return sizeof(RockySnapshotHeader); - } - case 20: { - const size_t result = sizeof(RockySnapshotHeader) + sizeof(uint64_t); - cl_assert(num_bytes >= result); - - RockySnapshotHeader *header = (RockySnapshotHeader*)data; - *header = ROCKY_EXPECTED_SNAPSHOT_HEADER; - // first uint64_t after our header is the jerry script buffer which starts with a version - *(uint64_t*)(header + 1) = JERRY_SNAPSHOT_VERSION; - return result; - } - default: - return 0; - } -} - -void test_rocky_res__initialize(void) { - s_resource_storage_get_num_entries__result = 0; -} - -void test_rocky_res__no_snapshot(void) { - s_resource_storage_get_num_entries__result = 5; - cl_assert_equal_b(false, rocky_app_has_compatible_bytecode_res(123)); -} - -void test_rocky_res__only_invalid_snapshot(void) { - s_resource_storage_get_num_entries__result = 15; - cl_assert_equal_b(false, rocky_app_has_compatible_bytecode_res(123)); -} - -void test_rocky_res__valid_snapshot(void) { - s_resource_storage_get_num_entries__result = 25; - cl_assert_equal_b(true, rocky_app_has_compatible_bytecode_res(123)); -} diff --git a/tests/fw/javascript/test_rocky_text_encoding.c b/tests/fw/javascript/test_rocky_text_encoding.c deleted file mode 100644 index d7de227251..0000000000 --- a/tests/fw/javascript/test_rocky_text_encoding.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "test_jerry_port_common.h" -#include "test_rocky_common.h" -#include "applib/rockyjs/pbl_jerry_port.h" - -#include "jerry-api.h" - -#include - -#include -#include - -// Fakes -#include "fake_time.h" -#include "fake_logging.h" -#include "fake_pbl_malloc.h" - -// Stubs -#include "stubs_app_state.h" -#include "stubs_logging.h" -#include "stubs_passert.h" - -// Great read-up on JavaScript and its text encoding quirks: -// https://mathiasbynens.be/notes/javascript-unicode - -void test_rocky_text_encoding__initialize(void) { - fake_pbl_malloc_clear_tracking(); - rocky_runtime_context_init(); - jerry_init(JERRY_INIT_EMPTY); -} - -void test_rocky_text_encoding__cleanup(void) { - jerry_cleanup(); - rocky_runtime_context_deinit(); - fake_pbl_malloc_check_net_allocs(); -} - -void test_rocky_text_encoding__jerry_handles_cesu8_strings_in_source(void) { - // Although CESU-8 and UTF-8 are not compatible on paper, JerryScript's lexer doesn't mind if - // we feed it CESU-8 encoded strings... Test this, so we know when this changes in the future: - EXECUTE_SCRIPT("var pileOfPooCESU8 = '\xed\xa0\xbd\xed\xb2\xa9';"); - // Expect a pair of surrogate code points: - EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooCESU8.charCodeAt(0).toString(16)", "d83d"); - EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooCESU8.charCodeAt(1).toString(16)", "dca9"); -} - -void test_rocky_text_encoding__jerry_handles_utf8_strings_in_source(void) { - // Source is be UTF-8 encoded. - // Have a string variable with Pile of Poo (💩) or U+1F4A9 in it, encoded using 4-bytes: - EXECUTE_SCRIPT("var pileOfPooUTF8 = '\xF0\x9F\x92\xA9';"); - // Expect a pair of surrogate code points: - EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooUTF8.charCodeAt(0).toString(16)", "d83d"); - EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooUTF8.charCodeAt(1).toString(16)", "dca9"); -} - -void test_rocky_text_encoding__jerry_asserts_utf8_non_bmp_codepoint_in_identifier(void) { - // It's forbidden to have an identifier contain a non-BMP codepoint (UTF-8 encoded): - EXECUTE_SCRIPT_EXPECT_ERROR("var poo\xF0\x9F\x92\xA9poo = 'pileOfPoo';", - "SyntaxError: Invalid (unexpected) character. [line: 1, column: 8]"); -} - -void test_rocky_text_encoding__jerry_asserts_cesu8_non_bmp_codepoint_in_identifier(void) { - // It's forbidden to have an identifier contain a non-BMP codepoint (CESU-8 encoded): - EXECUTE_SCRIPT_EXPECT_ERROR("var poo\xed\xa0\xbd\xed\xb2\xa9poo = 'pileOfPoo';", - "SyntaxError: Invalid (unexpected) character. [line: 1, column: 8]"); -} - -void test_rocky_text_encoding__string_length(void) { - EXECUTE_SCRIPT("var pileOfPooUTF8 = '\xF0\x9F\x92\xA9';"); - // String.length is expected to count the surrogate code points that make up a non-BMP codepoint: - EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooUTF8.length.toString()", "2"); -} - -void test_rocky_text_encoding__jerry_cesu8_to_utf8_conversion(void) { - struct { - const char *const script; - size_t expected_utf_size; - const char *const expected_utf_data; - } cases[] = { - [0] = { - .script = "var str = '\\uDCA9';", // low surrogate only - .expected_utf_size = 0, - }, - [1] = { - .script = "var str = '\\uD83D';", // high surrogate only - .expected_utf_size = 0, - }, - [2] = { - .script = "var str = '\\uDCA9\\uD83D';", // reversed order - .expected_utf_size = 0, - }, - [3] = { - .script = "var str = '\\uD83Dx\\uDCA9';", // non-surrogate in between pair - .expected_utf_size = 1, - .expected_utf_data = "x", - }, - [4] = { - .script = "var str = '\\uD83Dx';", // high surrogate followed by non-surrogate - .expected_utf_size = 1, - .expected_utf_data = "x", - }, - [5] = { - .script = "var str = '\\uDCA9x';", // low surrogate followed by non-surrogate - .expected_utf_size = 1, - .expected_utf_data = "x", - }, - [6] = { - .script = "var str = 'AB';", - .expected_utf_size = 2, - .expected_utf_data = "AB", - }, - [7] = { - .script = "var str = '\xC4\x91';", // 2-byte codepoint (U+0111) - .expected_utf_size = 2, - .expected_utf_data = "\xC4\x91", - }, - [8] = { - .script = "var str = '\xE0\xA0\x95';", // 3-byte codepoint (U+0815) - .expected_utf_size = 3, - .expected_utf_data = "\xE0\xA0\x95", - }, - [9] = { - .script = "var str = '\\uD83D\\uDCA9';", // 4-byte codepoint (U+1F4A9, escaped data) - .expected_utf_size = 4, - .expected_utf_data = "\xF0\x9F\x92\xA9", - }, - [10] = { - .script = "var str = '\xF0\x9F\x92\xA9';", // 4-byte codepoint (U+1F4A9, UTF-8 data in source) - .expected_utf_size = 4, - .expected_utf_data = "\xF0\x9F\x92\xA9", - }, - }; - - for (int j = 0; j < 2; ++j) { - const bool is_overflow_test = (j == 1); - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - printf("Case %i (is_overflow_test=%u): %s\n", i, is_overflow_test, cases[i].script); - - EXECUTE_SCRIPT(cases[i].script); - const jerry_value_t s = JS_GLOBAL_GET_VALUE("str"); - - const jerry_size_t utf8_size = jerry_get_utf8_string_size(s); - // U+1F4A9 is expected to get encoded into 4 bytes of UTF-8: - cl_assert_equal_i(utf8_size, cases[i].expected_utf_size); - - const size_t buffer_size = utf8_size ? (is_overflow_test ? (utf8_size - 1) : utf8_size) : 0; - - // malloc, so DUMA will detect buffer overflows: - jerry_char_t *utf8_buffer = malloc(buffer_size); - - const jerry_size_t copied_size = - jerry_string_to_utf8_char_buffer(s, utf8_buffer, buffer_size); - if (!is_overflow_test) { - cl_assert_equal_i(copied_size, cases[i].expected_utf_size); - if (cases[i].expected_utf_size) { - cl_assert_equal_m(utf8_buffer, cases[i].expected_utf_data, cases[i].expected_utf_size); - } - } else { - // When buffer is too small, expect 0 bytes copied: - cl_assert_equal_i(copied_size, 0); - } - jerry_release_value(s); - - free(utf8_buffer); - } - } -} - -void test_rocky_text_encoding__jerry_utf8_to_cesu8_conversion(void) { - struct { - const char *const utf8_input; - const char *const cesu8_output; - } cases[] = { - { - .utf8_input = "", - .cesu8_output = "", - }, - { - .utf8_input = "abc", - .cesu8_output = "abc", - }, - { - // U+1F4A9 expands to surrogate pair: - .utf8_input = "abc\xF0\x9F\x92\xA9xyz", - .cesu8_output = "abc\xed\xa0\xbd\xed\xb2\xa9xyz", - }, - { - // Be lax with surrogates: even though they're not supposed to appear in UTF-8, - // just copy them over to the CESU-8 output, even a "half pair": - .utf8_input = "\xed\xa0\xbd", - .cesu8_output = "\xed\xa0\xbd", - }, - }; - for (int i = 0; i < ARRAY_LENGTH(cases); ++i) { - jerry_char_t output[32] = {}; - const jerry_value_t s = jerry_create_string_utf8((const jerry_char_t *)cases[i].utf8_input); - const jerry_size_t copied_bytes = jerry_string_to_char_buffer(s, output, sizeof(output)); - cl_assert_equal_i(copied_bytes, strlen(cases[i].cesu8_output)); - if (copied_bytes) { - cl_assert_equal_m(output, cases[i].cesu8_output, copied_bytes); - } - // TODO: test equality/hash - jerry_release_value(s); - } -} diff --git a/tests/fw/javascript/wscript b/tests/fw/javascript/wscript deleted file mode 100644 index 3883cb8726..0000000000 --- a/tests/fw/javascript/wscript +++ /dev/null @@ -1,231 +0,0 @@ -import os - -from waftools.pebble_test import clar -import sh - - -def rocky_clar(ctx, **kwargs): - if ctx.variant == 'test_rocky_emx': - kwargs["sources_ant_glob"] += " applib-targets/emscripten/emscripten_jerry_port.c" - jerry_uses = ['emscripten_jerry_api', 'jerry_port_includes'] - else: - kwargs["sources_ant_glob"] += " src/fw/applib/rockyjs/jerry_port.c" - jerry_uses = ['jerry_port_includes', 'jerry_core', 'jerry_libm'] - - kwargs["use"] = jerry_uses + (kwargs["use"] if "use" in kwargs else []) - jerry_defines = ['CAPABILITY_HAS_JAVASCRIPT=1', 'CAPABILITY_JAVASCRIPT_BYTECODE_VERSION=1', - 'JMEM_STATS=1'] - kwargs["defines"] = jerry_defines + kwargs.get("defines", []) - kwargs["defines"].extend(ctx.env.test_image_defines) - clar(ctx, **kwargs) - - -def build(ctx): - if ctx.env.NOJS: - return - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_global.c") - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_color.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_text.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_util_args.c", - test_sources_ant_glob = "test_rocky_api_graphics.c") - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_color.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_util_args.c", - test_sources_ant_glob = "test_rocky_api_graphics_color.c") - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_path2d.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_util_args.c", - test_sources_ant_glob = "test_rocky_api_graphics_path2d.c") - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/graphics/gpath.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_private_raw_mask.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_color.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_path2d.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_util_args.c", - test_sources_ant_glob = "test_rocky_api_graphics_rendering.c" - ) - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_tickservice.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_tickservice.c") - - rocky_clar(ctx, - sources_ant_glob = - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_preferences.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_preferences.c") - - rocky_clar(ctx, - sources_ant_glob = - " src/fw/util/dict.c" - " tests/fakes/fake_events.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_app_message.c", - test_sources_ant_glob = "test_rocky_api_app_message.c") - - rocky_clar(ctx, - sources_ant_glob = - " src/fw/util/dict.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_watchinfo.c", - test_sources_ant_glob = "test_rocky_api_watchinfo.c") - - rocky_clar(ctx, - sources_ant_glob = - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_util.c") - - rocky_clar(ctx, - sources_ant_glob = - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_graphics_color.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c" - " src/fw/applib/rockyjs/api/rocky_api_util_args.c", - test_sources_ant_glob = "test_rocky_api_util_args.c") - - # Tests that should be skipped when running unit tests with the fake - # JerryScript (emscripten_jerry_api.c): - if ctx.variant != 'test_rocky_emx': - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_clock.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/rockyjs/rocky.c" \ - " src/fw/applib/rockyjs/api/rocky_api.c" \ - " src/fw/applib/rockyjs/api/rocky_api_datetime.c" \ - " src/fw/applib/rockyjs/api/rocky_api_global.c" \ - " src/fw/applib/rockyjs/api/rocky_api_errors.c" \ - " src/fw/applib/rockyjs/api/rocky_api_tickservice.c" \ - " src/fw/applib/rockyjs/api/rocky_api_timers.c" \ - " src/fw/applib/rockyjs/api/rocky_api_graphics.c" \ - " src/fw/applib/rockyjs/api/rocky_api_graphics_path2d.c" \ - " src/fw/applib/rockyjs/api/rocky_api_graphics_color.c" \ - " src/fw/applib/rockyjs/api/rocky_api_graphics_text.c" \ - " src/fw/applib/rockyjs/api/rocky_api_memory.c" \ - " src/fw/applib/rockyjs/api/rocky_api_console.c" \ - " src/fw/applib/rockyjs/api/rocky_api_preferences.c" \ - " src/fw/applib/rockyjs/api/rocky_api_util.c" \ - " src/fw/applib/rockyjs/api/rocky_api_util_args.c", - test_sources_ant_glob = "test_js.c") - - # No snapshot support in emscripten_jerry_api.c :D - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/rocky.c" - " src/fw/applib/rockyjs/rocky_res.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_res.c") - - # When building unit tests with emscripten, skip this one because we're - # using the console.log/warn/error implementations of the browser/node. - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_console.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_console.c") - - rocky_clar(ctx, - sources_ant_glob = - " tests/fakes/fake_applib_resource.c" - " tests/fakes/fake_clock.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_datetime.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_datetime.c", - defines=['CAPABILITY_HAS_JAVASCRIPT=1', - 'CAPABILITY_JAVASCRIPT_BYTECODE_VERSION=1']) - - rocky_clar(ctx, - sources_ant_glob=" tests/fakes/fake_applib_resource.c" - " src/fw/applib/rockyjs/api/rocky_api_global.c" - " src/fw/applib/rockyjs/api/rocky_api_memory.c" - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_memory.c") - - # Currently we're leaving the browser/node's setTimeout + friends alone - rocky_clar(ctx, - sources_ant_glob = - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_timers.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_api_timers.c") - - # Emscripten transpiled jerry-api uses UTF8 internally. - rocky_clar(ctx, - sources_ant_glob= - " src/fw/applib/rockyjs/api/rocky_api_errors.c" - " src/fw/applib/rockyjs/api/rocky_api_util.c", - test_sources_ant_glob = "test_rocky_text_encoding.c") - - -# vim:filetype=python diff --git a/tests/fw/kernel/test_interval_timer.c b/tests/fw/kernel/test_interval_timer.c index 005da65799..2fb33fa54b 100644 --- a/tests/fw/kernel/test_interval_timer.c +++ b/tests/fw/kernel/test_interval_timer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/util/interval_timer.h" diff --git a/tests/fw/kernel/test_pulse_logging.c b/tests/fw/kernel/test_pulse_logging.c index c18ab7cd7f..0b3d5265eb 100644 --- a/tests/fw/kernel/test_pulse_logging.c +++ b/tests/fw/kernel/test_pulse_logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/kernel/wscript b/tests/fw/kernel/wscript deleted file mode 100644 index e76199b8c8..0000000000 --- a/tests/fw/kernel/wscript +++ /dev/null @@ -1,14 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = - " src/fw/kernel/pulse_logging.c", - test_sources_ant_glob="test_pulse_logging.c") - - clar(ctx, - sources_ant_glob = - " src/fw/kernel/util/interval_timer.c" - " tests/fakes/fake_rtc.c", - test_sources_ant_glob="test_interval_timer.c") - diff --git a/tests/fw/kernel/wscript_build b/tests/fw/kernel/wscript_build new file mode 100644 index 0000000000..3738a562b2 --- /dev/null +++ b/tests/fw/kernel/wscript_build @@ -0,0 +1,12 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = + " src/fw/kernel/pulse_logging.c", + test_sources_ant_glob="test_pulse_logging.c") + +clar(ctx, + sources_ant_glob = + " src/fw/kernel/util/interval_timer.c" + " tests/fakes/fake_rtc.c", + test_sources_ant_glob="test_interval_timer.c") diff --git a/tests/fw/mfg/test_mfg_serials.c b/tests/fw/mfg/test_mfg_serials.c index 8de14e630e..4a77729188 100644 --- a/tests/fw/mfg/test_mfg_serials.c +++ b/tests/fw/mfg/test_mfg_serials.c @@ -1,21 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "mfg/mfg_serials.h" #include "console/prompt_commands.h" +#include "drivers/otp.h" #include "clar.h" @@ -52,37 +40,6 @@ void test_mfg_serials__hw_version(void) { hw_version = mfg_get_hw_version(); cl_assert(strcmp(written_hw_version1, hw_version) == 0); -#if (BOARD_SILK_BB || BOARD_CALCULUS) - // Write a second time, too long. - const char* written_hw_version2_long = "abcdefghijkxyz"; - command_hwver_write(written_hw_version2_long); - hw_version = mfg_get_hw_version(); - cl_assert_equal_s(written_hw_version1, hw_version); - - // Write second time - const char* written_hw_version2 = "HIJKLMN"; - command_hwver_write(written_hw_version2); - hw_version = mfg_get_hw_version(); - cl_assert_equal_s(written_hw_version2, hw_version); - - // Write third time - const char* written_hw_version3 = "OPQRSTU"; - command_hwver_write(written_hw_version3); - hw_version = mfg_get_hw_version(); - cl_assert_equal_s(written_hw_version3, hw_version); - - // Write fourth time - const char* written_hw_version4 = "VWXYZ12"; - command_hwver_write(written_hw_version4); - hw_version = mfg_get_hw_version(); - cl_assert_equal_s(written_hw_version4, hw_version); - - // Write fifth time - const char* written_hw_version5 = "3456789"; - command_hwver_write(written_hw_version5); - hw_version = mfg_get_hw_version(); - cl_assert_equal_s(written_hw_version5, hw_version); -#endif // BOARD_SILK || BOARD_CALCULUS } void test_mfg_serials__serial_number_console(void) { @@ -112,29 +69,18 @@ void test_mfg_serials__pcba_serial_number(void) { pcba_serial = mfg_get_pcba_serial_number(); cl_assert_equal_s(written_pcba_serial1, pcba_serial); - // Write second time, but too long - const char* written_pcba_serial2_long = "abcdefghijkxyz"; - command_pcba_serial_write(written_pcba_serial2_long); + // Reject overly long writes; original preserved. + const char* written_pcba_serial_long = "abcdefghijkxyz"; + command_pcba_serial_write(written_pcba_serial_long); pcba_serial = mfg_get_pcba_serial_number(); cl_assert_equal_s(written_pcba_serial1, pcba_serial); - // Write second time + // OTP_PCBA_SERIAL only has one slot, so subsequent valid writes also fail + // and the original value is preserved. const char* written_pcba_serial2 = "abcdefghijkx"; command_pcba_serial_write(written_pcba_serial2); pcba_serial = mfg_get_pcba_serial_number(); - cl_assert_equal_s(written_pcba_serial2, pcba_serial); - - // Write third time - const char* written_pcba_serial3 = "asdfghjklq"; - command_pcba_serial_write(written_pcba_serial3); - pcba_serial = mfg_get_pcba_serial_number(); - cl_assert_equal_s(written_pcba_serial3, pcba_serial); - - // No more space: Reading should return the last successfully written value. - const char *pcba_serial4 = "XXXXXXXXXXXX"; - command_pcba_serial_write(pcba_serial4); - pcba_serial = mfg_get_pcba_serial_number(); - cl_assert_equal_s(written_pcba_serial3, pcba_serial); + cl_assert_equal_s(written_pcba_serial1, pcba_serial); } void test_mfg_serials__serial_number_fails(void) { @@ -176,44 +122,15 @@ void test_mfg_serials__serial_numbers(void) { const char *first_sn = "ABCDEFGHIJKL"; r = mfg_write_serial_number(first_sn, strlen(first_sn), &index); sn = mfg_get_serial_number(); - cl_assert_equal_i(index, 0); + cl_assert_equal_i(index, OTP_SERIAL); cl_assert_equal_i(r, MfgSerialsResultSuccess); cl_assert_equal_s(sn, first_sn); - // Second time: + // OTP_SERIAL only has one slot, so subsequent writes fail and the original + // value is preserved. const char *second_sn = "012345678901"; r = mfg_write_serial_number(second_sn, strlen(second_sn), &index); + cl_assert_equal_i(r, MfgSerialsResultFailNoMoreSpace); sn = mfg_get_serial_number(); - cl_assert_equal_i(index, 3); // SERIAL2 lives at index 3 - cl_assert_equal_i(r, MfgSerialsResultSuccess); - cl_assert_equal_s(sn, second_sn); - - // Third time: - const char *third_sn = "!@#$%^&*()-="; - r = mfg_write_serial_number(third_sn, strlen(third_sn), &index); - sn = mfg_get_serial_number(); - cl_assert_equal_i(r, MfgSerialsResultSuccess); - cl_assert_equal_s(sn, third_sn); - cl_assert_equal_i(index, 4); // SERIAL3 lives at index 4 - - // Fourth time: - const char *fourth_sn = "mnbvcxzlkjhg"; - r = mfg_write_serial_number(fourth_sn, strlen(fourth_sn), &index); - sn = mfg_get_serial_number(); - cl_assert_equal_i(r, MfgSerialsResultSuccess); - cl_assert_equal_s(sn, fourth_sn); - cl_assert_equal_i(index, 5); // SERIAL4 lives at index 5 - - // Fifth time: - const char *fifth_sn = "7ujn8ikm9olm"; - r = mfg_write_serial_number(fifth_sn, strlen(fifth_sn), &index); - sn = mfg_get_serial_number(); - cl_assert_equal_i(r, MfgSerialsResultSuccess); - cl_assert_equal_s(sn, fifth_sn); - cl_assert_equal_i(index, 6); // SERIAL5 lives at index 6 - - // No more space: - const char *sixth_sn = "XXXXXXXXXXXX"; - r = mfg_write_serial_number(sixth_sn, strlen(sixth_sn), &index); - cl_assert(r == MfgSerialsResultFailNoMoreSpace); + cl_assert_equal_s(sn, first_sn); } diff --git a/tests/fw/mfg/test_snowy_mfg_info.c b/tests/fw/mfg/test_snowy_mfg_info.c deleted file mode 100644 index cdb77227c1..0000000000 --- a/tests/fw/mfg/test_snowy_mfg_info.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "mfg/mfg_info.h" -#include "mfg/snowy/mfg_private.h" -#include "flash_region/flash_region_s29vs.h" - -#include "fake_spi_flash.c" -#include "stubs_logging.h" - -void mfg_info_write_boot_fpga_bitstream(void) {} -bool mfg_info_is_boot_fpga_bitstream_written(void) { return true; } - -void test_snowy_mfg_info__initialize(void) { - fake_spi_flash_init(FLASH_REGION_MFG_INFO_BEGIN, - FLASH_REGION_MFG_INFO_END - FLASH_REGION_MFG_INFO_BEGIN); -} - -void test_snowy_mfg_info__cleanup(void) { - fake_spi_flash_cleanup(); -} - -void test_snowy_mfg_info__color(void) { - cl_assert_equal_i(mfg_info_get_watch_color(), 0); - - mfg_info_set_watch_color(WATCH_INFO_COLOR_RED); - cl_assert_equal_i(mfg_info_get_watch_color(), WATCH_INFO_COLOR_RED); - - mfg_info_set_watch_color(WATCH_INFO_COLOR_GREEN); - cl_assert_equal_i(mfg_info_get_watch_color(), WATCH_INFO_COLOR_GREEN); -} - -void test_snowy_mfg_info__rtc_freq(void) { - cl_assert_equal_i(mfg_info_get_rtc_freq(), 0); - - mfg_info_set_rtc_freq(0xfefefefe); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 0xfefefefe); - - mfg_info_set_rtc_freq(1337); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 1337); -} - -void test_snowy_mfg_info__model(void) { - // Intentionally make the buffer too long so we can check for truncation. - char buffer[MFG_INFO_MODEL_STRING_LENGTH + 1] = { 0 }; - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - mfg_info_set_model("test_model"); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "test_model"); - - { - char long_string[] = "01234567890123456789"; - mfg_info_set_model(long_string); - - // We only expect to see the first 15 (MFG_INFO_MODEL_STRING_LENGTH - 1) characters - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "012345678901234"); - } -} - -void test_snowy_mfg_info__1_to_2_conversion(void) { - // Force in an old data version. - typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - } MfgDataV1; - - MfgDataV1 old_data = { - .data_version = 1, - .color = 3, - .rtc_freq = 4 - }; - - flash_write_bytes((const uint8_t*) &old_data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(old_data)); - - // Now use the info functions to read the data and make sure it's sane. A conversion will have - // happened behind the scenes to the latest version. - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - char buffer[MFG_INFO_MODEL_STRING_LENGTH] = { 0 }; - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - // Set color and make sure others don't change. - mfg_info_set_watch_color(5); - - cl_assert_equal_i(mfg_info_get_watch_color(), 5); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - // Make sure we have space for the model. - mfg_info_set_model("test_model"); - - cl_assert_equal_i(mfg_info_get_watch_color(), 5); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "test_model"); -} - - diff --git a/tests/fw/mfg/test_spalding_mfg_info.c b/tests/fw/mfg/test_spalding_mfg_info.c deleted file mode 100644 index 7d4b4cd84e..0000000000 --- a/tests/fw/mfg/test_spalding_mfg_info.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "applib/graphics/gtypes.h" -#include "mfg/mfg_info.h" -#include "mfg/snowy/mfg_private.h" -#include "flash_region/flash_region_s29vs.h" - -#include "fake_spi_flash.c" -#include "stubs_logging.h" -#include "stubs_pbl_malloc.h" - -// This will come from overrides and will include a smaller boot fpga image -#include "mfg/spalding/spalding_boot.fpga.auto.h" - -// Stubs for fake_crc.c -#include "services/normal/filesystem/pfs.h" -int pfs_read(int fd, void *buf, size_t size) { return 0; } -int pfs_seek(int fd, int offset, FSeekType seek_type) { return 0; } - -// Test Code! - -void test_spalding_mfg_info__initialize(void) { - fake_spi_flash_init(FLASH_REGION_MFG_INFO_BEGIN, - FLASH_REGION_MFG_INFO_END - FLASH_REGION_MFG_INFO_BEGIN); -} - -void test_spalding_mfg_info__cleanup(void) { - fake_spi_flash_cleanup(); -} - -void test_spalding_mfg_info__color(void) { - cl_assert_equal_i(mfg_info_get_watch_color(), 0); - - mfg_info_set_watch_color(WATCH_INFO_COLOR_RED); - cl_assert_equal_i(mfg_info_get_watch_color(), WATCH_INFO_COLOR_RED); - - mfg_info_set_watch_color(WATCH_INFO_COLOR_GREEN); - cl_assert_equal_i(mfg_info_get_watch_color(), WATCH_INFO_COLOR_GREEN); -} - -void test_spalding_mfg_info__rtc_freq(void) { - cl_assert_equal_i(mfg_info_get_rtc_freq(), 0); - - mfg_info_set_rtc_freq(0xfefefefe); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 0xfefefefe); - - mfg_info_set_rtc_freq(1337); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 1337); -} - -void test_spalding_mfg_info__model(void) { - // Intentionally make the buffer too long so we can check for truncation. - char buffer[MFG_INFO_MODEL_STRING_LENGTH + 1] = { 0 }; - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - mfg_info_set_model("test_model"); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "test_model"); - - { - char long_string[] = "01234567890123456789"; - mfg_info_set_model(long_string); - - // We only expect to see the first 15 (MFG_INFO_MODEL_STRING_LENGTH - 1) characters - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "012345678901234"); - } -} - -void test_spalding_mfg_info__1_to_2_conversion(void) { - // Force in an old data version. - typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - } MfgDataV1; - - MfgDataV1 old_data = { - .data_version = 1, - .color = 3, - .rtc_freq = 4 - }; - - flash_write_bytes((const uint8_t*) &old_data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(old_data)); - - // Now use the info functions to read the data and make sure it's sane. A conversion will have - // happened behind the scenes to the latest version. - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - char buffer[MFG_INFO_MODEL_STRING_LENGTH] = { 0 }; - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - // Set color and make sure others don't change. - mfg_info_set_watch_color(5); - - cl_assert_equal_i(mfg_info_get_watch_color(), 5); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - // Make sure we have space for the model. - mfg_info_set_model("test_model"); - - cl_assert_equal_i(mfg_info_get_watch_color(), 5); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "test_model"); -} - - -void test_spalding_mfg_info__1_to_3_conversion(void) { - // Force in an old data version. - typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - } MfgDataV1; - - MfgDataV1 old_data = { - .data_version = 1, - .color = 3, - .rtc_freq = 4 - }; - - flash_write_bytes((const uint8_t*) &old_data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(old_data)); - - // Now use the info functions to read the data and make sure it's sane. A conversion will have - // happened behind the scenes to the latest version. - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - char buffer[MFG_INFO_MODEL_STRING_LENGTH] = { 0 }; - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - cl_assert_equal_i(mfg_info_get_disp_offsets().x, 0); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 0); - - // Set x and y offsets and make sure others don't change. - mfg_info_set_disp_offsets((GPoint) {-2, 1}); - - cl_assert_equal_i(mfg_info_get_disp_offsets().x, -2); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 1); - - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - // Make sure we have space for the model. - mfg_info_set_model("test_model"); - - cl_assert_equal_i(mfg_info_get_disp_offsets().x, -2); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 1); - - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "test_model"); -} - -void test_spalding_mfg_info__2_to_3_conversion(void) { - // Force in an old data version. - typedef struct { - uint32_t data_version; - - uint32_t color; - uint32_t rtc_freq; - char model[MFG_INFO_MODEL_STRING_LENGTH]; - } MfgDataV2; - - MfgDataV2 old_data = { - .data_version = 1, - .color = 3, - .rtc_freq = 4, - .model[0] = '\0' - }; - - flash_write_bytes((const uint8_t*) &old_data, FLASH_REGION_MFG_INFO_BEGIN, sizeof(old_data)); - - // Now use the info functions to read the data and make sure it's sane. A conversion will have - // happened behind the scenes to the latest version. - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - char buffer[MFG_INFO_MODEL_STRING_LENGTH] = { 0 }; - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - cl_assert_equal_i(mfg_info_get_disp_offsets().x, 0); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 0); - - // Set x and y offsets and make sure others don't change. - mfg_info_set_disp_offsets((GPoint) {-2, 1}); - - cl_assert_equal_i(mfg_info_get_disp_offsets().x, -2); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 1); - - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, ""); - - // Make sure we have space for the model. - mfg_info_set_model("test_model"); - - cl_assert_equal_i(mfg_info_get_disp_offsets().x, -2); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 1); - - cl_assert_equal_i(mfg_info_get_watch_color(), 3); - cl_assert_equal_i(mfg_info_get_rtc_freq(), 4); - - mfg_info_get_model(buffer); - cl_assert_equal_s(buffer, "test_model"); -} - -void test_spalding_mfg_info__boot_fpga_persistence(void) { - // Make sure no FPGA image is stored - const uintptr_t BOOT_FPGA_FLASH_ADDR = FLASH_REGION_MFG_INFO_BEGIN + 0x10000; - const uint32_t HEADER_SIZE = 4; // sizeof(BootFPGAHeader) - - uint8_t fpga_buffer[HEADER_SIZE + sizeof(s_boot_fpga)]; - flash_read_bytes(fpga_buffer, BOOT_FPGA_FLASH_ADDR, sizeof(fpga_buffer)); - for (int i = 0; i < sizeof(s_boot_fpga); ++i) { - cl_assert(fpga_buffer[i] == 0xff); - } - - // Write some data in - mfg_info_set_rtc_freq(1); - - // The first time we write something into mfg_info we'll actually write the boot fpga as a side - // effect. Make sure it's there. - flash_read_bytes(fpga_buffer, BOOT_FPGA_FLASH_ADDR, sizeof(fpga_buffer)); - cl_assert(memcmp(fpga_buffer + HEADER_SIZE, s_boot_fpga, sizeof(s_boot_fpga)) == 0); - - mfg_info_set_disp_offsets((GPoint) { 2, 3 }); - - char model[MFG_INFO_MODEL_STRING_LENGTH] = "123456789012345"; - mfg_info_set_model(model); - - // Now let's write in an fpga image - mfg_info_update_constant_data(); - - // Let's make sure the mfg data is still persisted - cl_assert_equal_i(mfg_info_get_rtc_freq(), 1); - cl_assert_equal_i(mfg_info_get_disp_offsets().x, 2); - cl_assert_equal_i(mfg_info_get_disp_offsets().y, 3); - - char result_model[MFG_INFO_MODEL_STRING_LENGTH]; - mfg_info_get_model(result_model); - cl_assert_equal_s(model, result_model); - - // Make sure the boot fpga is still correct - flash_read_bytes(fpga_buffer, BOOT_FPGA_FLASH_ADDR, sizeof(fpga_buffer)); - cl_assert(memcmp(fpga_buffer + HEADER_SIZE, s_boot_fpga, sizeof(s_boot_fpga)) == 0); - - // Now invalidate the section and write it back. Make sure it comes back - flash_write_bytes((const uint8_t*) "xxxx", BOOT_FPGA_FLASH_ADDR + HEADER_SIZE, 4); - - // Make sure it's corrupted - flash_read_bytes(fpga_buffer, BOOT_FPGA_FLASH_ADDR, sizeof(fpga_buffer)); - cl_assert(memcmp(fpga_buffer + HEADER_SIZE, s_boot_fpga, sizeof(s_boot_fpga)) != 0); - - // Now update it and make sure we healed the corruption - mfg_info_update_constant_data(); - - flash_read_bytes(fpga_buffer, BOOT_FPGA_FLASH_ADDR, sizeof(fpga_buffer)); - cl_assert(memcmp(fpga_buffer + HEADER_SIZE, s_boot_fpga, sizeof(s_boot_fpga)) == 0); -} diff --git a/tests/fw/mfg/wscript b/tests/fw/mfg/wscript deleted file mode 100644 index e21a8b2c46..0000000000 --- a/tests/fw/mfg/wscript +++ /dev/null @@ -1,25 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/fw/mfg/mfg_serials.c tests/fakes/fake_otp.c", - test_sources_ant_glob = "test_mfg_serials.c", - override_includes=['dummy_board', 'silk']) - - clar(ctx, - sources_ant_glob="src/fw/mfg/snowy/mfg_info.c", - test_sources_ant_glob="test_snowy_mfg_info.c", - override_includes=['dummy_board', 'snowy_mfg_board'], - platforms=['snowy']) - - clar(ctx, - sources_ant_glob="src/fw/mfg/spalding/mfg_info.c " - "src/fw/mfg/spalding/boot_fpga.c " - "src/fw/util/legacy_checksum.c " - "src/fw/drivers/flash/flash_crc.c", - test_sources_ant_glob="test_spalding_mfg_info.c", - override_includes=['dummy_board', 'snowy_mfg_board'], - platforms=['spalding']) - -# vim:filetype=python - diff --git a/tests/fw/mfg/wscript_build b/tests/fw/mfg/wscript_build new file mode 100644 index 0000000000..0b78e40071 --- /dev/null +++ b/tests/fw/mfg/wscript_build @@ -0,0 +1,9 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/mfg/mfg_serials.c tests/fakes/fake_otp.c", + test_sources_ant_glob = "test_mfg_serials.c", + override_includes=['dummy_board'], + platforms=['obelix']) + +# vim:filetype=python diff --git a/tests/fw/pebble_actions/stubs_common.h b/tests/fw/pebble_actions/stubs_common.h index 75f2eb32ca..87510d6db1 100644 --- a/tests/fw/pebble_actions/stubs_common.h +++ b/tests/fw/pebble_actions/stubs_common.h @@ -1,33 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once // These are the stubs that are common between the ancs_pebble_action and timeline_action tests // This huge list is mainly due to the inclusion of timeline_actions.c which handles both UI and // a large portion of action logic, which will hopefully be fixed eventually #include "applib/ui/action_menu_window_private.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/blob_db/sync.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/phone_call_util.h" -#include "services/normal/timeline/actions_endpoint.h" -#include "services/normal/timeline/timeline.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/blob_db/sync.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/phone_call_util.h" +#include "pbl/services/timeline/actions_endpoint.h" +#include "pbl/services/timeline/timeline.h" +#include "pbl/services/timeline/timeline_actions.h" #include "util/size.h" #include "stubs_action_chaining_window.h" diff --git a/tests/fw/pebble_actions/test_ancs_pebble_actions.c b/tests/fw/pebble_actions/test_ancs_pebble_actions.c index 7e02d0268c..aced7c6a33 100644 --- a/tests/fw/pebble_actions/test_ancs_pebble_actions.c +++ b/tests/fw/pebble_actions/test_ancs_pebble_actions.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/notifications/ancs/ancs_notifications.h" -#include "services/normal/notifications/ancs/ancs_notifications_util.h" -#include "services/normal/notifications/ancs/ancs_item.h" +#include "pbl/services/notifications/ancs/ancs_notifications.h" +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" +#include "pbl/services/notifications/ancs/ancs_item.h" #include "comm/ble/kernel_le_client/ancs/ancs_util.h" #include "comm/ble/kernel_le_client/ancs/ancs_types.h" diff --git a/tests/fw/pebble_actions/test_data.h b/tests/fw/pebble_actions/test_data.h index 95e4d7ee29..8fc9f2a9ca 100644 --- a/tests/fw/pebble_actions/test_data.h +++ b/tests/fw/pebble_actions/test_data.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/tests/fw/pebble_actions/test_timeline_actions.c b/tests/fw/pebble_actions/test_timeline_actions.c index fa066058ad..66b10eace8 100644 --- a/tests/fw/pebble_actions/test_timeline_actions.c +++ b/tests/fw/pebble_actions/test_timeline_actions.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/timeline/timeline_actions.h" // Test Data /////////////////////////////////////////////////////////// diff --git a/tests/fw/pebble_actions/wscript b/tests/fw/pebble_actions/wscript deleted file mode 100644 index 72a81f8b7e..0000000000 --- a/tests/fw/pebble_actions/wscript +++ /dev/null @@ -1,49 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - "src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c " \ - "src/fw/flash_region/filesystem_regions.c " \ - "src/fw/flash_region/flash_region.c " \ - "src/fw/services/normal/blob_db/ios_notif_pref_db.c " \ - "src/fw/services/normal/filesystem/flash_translation.c " \ - "src/fw/services/normal/filesystem/pfs.c " \ - "src/fw/services/normal/notifications/ancs/ancs_filtering.c " \ - "src/fw/services/normal/notifications/ancs/ancs_item.c " \ - "src/fw/services/normal/notifications/ancs/ancs_notifications.c " \ - "src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \ - "src/fw/services/normal/notifications/ancs/ancs_phone_call.c " \ - "src/fw/services/normal/settings/settings_file.c " \ - "src/fw/services/normal/settings/settings_raw_iter.c " \ - "src/fw/services/normal/timeline/actions_endpoint.c " \ - "src/fw/services/normal/timeline/attribute.c " \ - "src/fw/services/normal/timeline/attribute_group.c " \ - "src/fw/services/normal/timeline/attributes_actions.c " \ - "src/fw/services/normal/timeline/item.c " \ - "src/fw/services/normal/timeline/timeline.c " \ - "src/fw/services/normal/timeline/timeline_actions.c " \ - "src/fw/util/buffer.c " \ - "src/fw/util/crc8.c " \ - "src/fw/util/legacy_checksum.c " \ - "src/fw/util/pstring.c " \ - "src/fw/util/rand/rand.c " \ - "src/fw/util/time/time.c "\ - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " \ - "tests/fakes/fake_spi_flash.c ", - test_sources_ant_glob = "test_ancs_pebble_actions.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = - "src/fw/services/normal/timeline/actions_endpoint.c " \ - "src/fw/services/normal/timeline/attribute.c " \ - "src/fw/services/normal/timeline/attribute_group.c " \ - "src/fw/services/normal/timeline/attributes_actions.c " \ - "src/fw/services/normal/timeline/item.c " \ - "src/fw/services/normal/timeline/timeline.c " \ - "src/fw/services/normal/timeline/timeline_actions.c " \ - "src/fw/util/time/time.c ", - test_sources_ant_glob = "test_timeline_actions.c", - override_includes=['dummy_board']) -# vim:filetype=python diff --git a/tests/fw/pebble_actions/wscript_build b/tests/fw/pebble_actions/wscript_build new file mode 100644 index 0000000000..81207ac4e0 --- /dev/null +++ b/tests/fw/pebble_actions/wscript_build @@ -0,0 +1,49 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = \ + "src/fw/comm/ble/kernel_le_client/ancs/ancs_util.c " \ + "src/fw/flash_region/filesystem_regions.c " \ + "src/fw/flash_region/flash_region.c " \ + "src/fw/services/blob_db/ios_notif_pref_db.c " \ + "src/fw/services/filesystem/flash_translation.c " \ + "src/fw/services/filesystem/pfs.c " \ + "src/fw/services/notifications/ancs/ancs_filtering.c " \ + "src/fw/services/notifications/ancs/ancs_item.c " \ + "src/fw/services/notifications/ancs/ancs_notifications.c " \ + "src/fw/services/notifications/ancs/ancs_notifications_util.c " \ + "src/fw/services/notifications/ancs/ancs_phone_call.c " \ + "src/fw/services/settings/settings_file.c " \ + "src/fw/services/settings/settings_raw_iter.c " \ + "src/fw/services/timeline/actions_endpoint.c " \ + "src/fw/services/timeline/attribute.c " \ + "src/fw/services/timeline/attribute_group.c " \ + "src/fw/services/timeline/attributes_actions.c " \ + "src/fw/services/timeline/item.c " \ + "src/fw/services/timeline/timeline.c " \ + "src/fw/services/timeline/timeline_actions.c " \ + "src/fw/util/buffer.c " \ + "src/fw/util/crc8.c " \ + "src/fw/util/legacy_checksum.c " \ + "src/fw/util/pstring.c " \ + "src/fw/util/rand/rand.c " \ + "src/fw/util/time/time.c "\ + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " \ + "tests/fakes/fake_spi_flash.c ", + test_sources_ant_glob = "test_ancs_pebble_actions.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = + "src/fw/services/timeline/actions_endpoint.c " \ + "src/fw/services/timeline/attribute.c " \ + "src/fw/services/timeline/attribute_group.c " \ + "src/fw/services/timeline/attributes_actions.c " \ + "src/fw/services/timeline/item.c " \ + "src/fw/services/timeline/timeline.c " \ + "src/fw/services/timeline/timeline_actions.c " \ + "src/fw/util/time/time.c ", + test_sources_ant_glob = "test_timeline_actions.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/services/activity/kraepelin_reference/ProjectK_worker.c b/tests/fw/services/activity/kraepelin_reference/ProjectK_worker.c index 56619f14f5..eebf5836ff 100644 --- a/tests/fw/services/activity/kraepelin_reference/ProjectK_worker.c +++ b/tests/fw/services/activity/kraepelin_reference/ProjectK_worker.c @@ -525,7 +525,7 @@ void ref_minute_stats(uint8_t *orientation, uint8_t *vmc) { } uint32_t kcals_min = calc_x1000_kcal(r_c_ary); - PBL_LOG(LOG_LEVEL_DEBUG, "minute_stats vmc: %d, orientation:0x%x, calories: %d", + PBL_LOG_DBG("minute_stats vmc: %d, orientation:0x%x, calories: %d", (int)*vmc, (int)*orientation, (int)kcals_min); reset_summ_metrics(); diff --git a/tests/fw/services/activity/kraepelin_reference/raw_stats.c b/tests/fw/services/activity/kraepelin_reference/raw_stats.c index f431aed3cb..c1463f7a17 100644 --- a/tests/fw/services/activity/kraepelin_reference/raw_stats.c +++ b/tests/fw/services/activity/kraepelin_reference/raw_stats.c @@ -560,7 +560,7 @@ int16_t calc_stepc_5sec(int16_t *work_ary, int16_t dlen_smp, int16_t dlenpwr_ary // stepc_tmp = max_mag_hz - ((uint16_t) (rand()%2)); stepc_tmp = max_mag_hz; } - PBL_LOG(LOG_LEVEL_DEBUG, "steps: %d, freq: %d, vmc: %d, score: %d", stepc_tmp, max_mag_hz, + PBL_LOG_DBG("steps: %d, freq: %d, vmc: %d, score: %d", stepc_tmp, max_mag_hz, real_vmc_5s, score0); return stepc_tmp; } diff --git a/tests/fw/services/activity/sleep_samples_v1.c b/tests/fw/services/activity/sleep_samples_v1.c index 9ef2b5d35f..6c09aa3751 100644 --- a/tests/fw/services/activity/sleep_samples_v1.c +++ b/tests/fw/services/activity/sleep_samples_v1.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include -#include "services/normal/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_algorithm.h" #include "util/size.h" diff --git a/tests/fw/services/activity/step_samples.c b/tests/fw/services/activity/step_samples.c index 4820586807..f6d71ff683 100644 --- a/tests/fw/services/activity/step_samples.c +++ b/tests/fw/services/activity/step_samples.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fw/services/activity/test_activity.c b/tests/fw/services/activity/test_activity.c index 5839489a9b..bdd69bf913 100644 --- a/tests/fw/services/activity/test_activity.c +++ b/tests/fw/services/activity/test_activity.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/accel_service.h" #include "applib/data_logging.h" @@ -21,14 +8,14 @@ #include "drivers/rtc.h" #include "drivers/vibe.h" #include "kernel/events.h" -#include "services/common/hrm/hrm_manager_private.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/kraepelin/activity_algorithm_kraepelin.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/protobuf_log/protobuf_log.h" +#include "pbl/services/hrm/hrm_manager_private.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/protobuf_log/protobuf_log.h" #include "shell/prefs.h" #include "system/logging.h" #include "system/passert.h" @@ -51,6 +38,7 @@ #include "stubs_analytics.h" #include "stubs_app_install_manager.h" #include "stubs_battery.h" +#include "stubs_event_loop.h" #include "stubs_freertos.h" #include "stubs_health_db.h" #include "stubs_hexdump.h" @@ -59,12 +47,18 @@ #include "stubs_mutex.h" #include "stubs_passert.h" #include "stubs_pebble_process_info.h" +#include "stubs_powermode_service.h" #include "stubs_prompt.h" #include "stubs_sleep.h" #include "stubs_system_theme.h" #include "stubs_task_watchdog.h" +#include "stubs_timeline_peek.h" #include "stubs_worker_manager.h" #include "stubs_workout_service.h" +#include "stubs_ambient_light.h" + +void prefs_sync_init(void) { +} // Fakes #include "fake_accel_service.h" @@ -96,16 +90,11 @@ const int s_exp_full_day_resting_kcalories = 1455; // Stub for health tracking disabled UI void health_tracking_ui_feature_show_disabled(void) { } -void health_tracking_ui_app_show_disabled(void) { } // These are declared as T_STATIC in activity.c void prv_hrm_subscription_cb(PebbleHRMEvent *hrm_event, void *context); void prv_minute_system_task_cb(void *data); -bool mfg_info_is_hrm_present(void) { - return true; -} - void hrm_manager_handle_prefs_changed(void) { } #define ASSERT_EQUAL_I(i1,i2,file,line) \ @@ -938,7 +927,7 @@ static bool prv_activity_iterate_cb(HealthActivity activity, time_t time_start, local_tm = localtime(&time_end); strftime(time_end_text, sizeof(time_end_text), "%F %r", local_tm); - PBL_LOG(LOG_LEVEL_DEBUG, "Got activity: %d %s to %s (%d min)", (int)activity, time_start_text, + PBL_LOG_DBG("Got activity: %d %s to %s (%d min)", (int)activity, time_start_text, time_end_text, (int)((time_end - time_start) / SECONDS_PER_MINUTE)); @@ -974,7 +963,7 @@ static void prv_sleep_sessions_using_health_service(uint32_t *session_entries, health_service_activities_iterate(HealthActivityMaskAll, now - (2 * SECONDS_PER_DAY), now, direction, prv_activity_iterate_cb, NULL); - PBL_LOG(LOG_LEVEL_DEBUG, "Found %"PRIu32" activities", s_health_sessions_count); + PBL_LOG_DBG("Found %"PRIu32" activities", s_health_sessions_count); *session_entries = s_health_sessions_count; } @@ -2348,7 +2337,7 @@ void test_activity__hrm_sampling_period(void) { fake_system_task_callbacks_invoke_pending(); s_test_alg_state.orientation = 0x11; // Not flat - prv_advance_time_hr(ACTIVITY_DEFAULT_HR_PERIOD_SEC, 100 /*bpm*/, HRMQuality_Good, false /*force_continuous*/); + prv_advance_time_hr((10 * SECONDS_PER_MINUTE), 100 /*bpm*/, HRMQuality_Good, false /*force_continuous*/); // Should be 1 second sampling when we start up cl_assert_equal_i(s_hrm_manager_update_interval, 1); @@ -2378,12 +2367,19 @@ void test_activity__hrm_sampling_period(void) { // Advance to our next sampling period, but the watch is flat so we shouldn't start sampling s_test_alg_state.orientation = 0x00; // Flat - prv_advance_time_hr(ACTIVITY_DEFAULT_HR_PERIOD_SEC, 100 /*bpm*/, HRMQuality_Good, false /*force_continuous*/); + prv_advance_time_hr((10 * SECONDS_PER_MINUTE), 100 /*bpm*/, HRMQuality_Good, false /*force_continuous*/); cl_assert(s_hrm_manager_update_interval > SECONDS_PER_HOUR); - // Advance to our next sampling period, the watch is no longer flat so we should be sampling + // Advance to our next sampling period, the watch is no longer flat so we should be sampling. + // The period has already expired during the flat advance above, so just advance until + // the next minute boundary triggers the subscription update and starts sampling. s_test_alg_state.orientation = 0x22; // Not flat - prv_advance_time_hr(ACTIVITY_DEFAULT_HR_PERIOD_SEC, 100 /*bpm*/, HRMQuality_Good, false /*force_continuous*/); + for (uint32_t i = 0; i < (10 * SECONDS_PER_MINUTE); i++) { + prv_advance_time_hr(1, 100 /*bpm*/, HRMQuality_Good, false /*force_continuous*/); + if (s_hrm_manager_update_interval == 1) { + break; + } + } cl_assert_equal_i(s_hrm_manager_update_interval, 1); } diff --git a/tests/fw/services/activity/test_activity_algorithm_kraepelin.c b/tests/fw/services/activity/test_activity_algorithm_kraepelin.c index 0b472213c4..2d76188ee9 100644 --- a/tests/fw/services/activity/test_activity_algorithm_kraepelin.c +++ b/tests/fw/services/activity/test_activity_algorithm_kraepelin.c @@ -1,41 +1,28 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/accel_service.h" #include "applib/data_logging.h" #include "drivers/ambient_light.h" #include "drivers/rtc.h" -#include "services/common/regular_timer.h" -#include "services/common/battery/battery_state.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/kraepelin/activity_algorithm_kraepelin.h" -#include "services/normal/activity/kraepelin/kraepelin_algorithm.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/common/system_task.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h" +#include "pbl/services/activity/kraepelin/kraepelin_algorithm.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/system_task.h" #include "util/math.h" #include "util/size.h" #include #include #include -#include +#include #include "clar.h" @@ -151,7 +138,7 @@ void activity_sessions_prv_add_activity_session(ActivitySession *session) { // If no more room, fail if (s_activity_sessions_count >= ACTIVITY_MAX_ACTIVITY_SESSIONS_COUNT) { - PBL_LOG(LOG_LEVEL_WARNING, "No more room for additional activities"); + PBL_LOG_WRN("No more room for additional activities"); return; } @@ -246,6 +233,13 @@ void activity_metrics_prv_reset_hr_stats(void) { s_activity_next_heart_rate_zone = 0; } +void activity_metrics_prv_set_hrm_worn_status(time_t now_utc, bool is_offwrist) { +} + +bool activity_metrics_prv_is_hrm_offwrist(time_t now_utc) { + return false; +} + // ============================================================================================= // Algorithm stubs @@ -273,7 +267,8 @@ void kalg_set_weight(KAlgState *state, uint32_t grams) { } void kalg_activities_update(KAlgState *state, time_t utc_now, uint16_t steps, uint16_t vmc, - uint8_t orientation, bool plugged_in, uint32_t resting_calories, + uint8_t orientation, bool definitely_not_worn, + uint32_t resting_calories, uint32_t active_calories, uint32_t distance_mm, bool shutting_down, KAlgActivitySessionCallback sessions_cb, void *context) { } diff --git a/tests/fw/services/activity/test_activity_calculators.c b/tests/fw/services/activity/test_activity_calculators.c index 514a121a78..087fcf4b64 100644 --- a/tests/fw/services/activity/test_activity_calculators.c +++ b/tests/fw/services/activity/test_activity_calculators.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/activity_calculators.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/activity_calculators.h" #include "util/units.h" diff --git a/tests/fw/services/activity/test_activity_insights.c b/tests/fw/services/activity/test_activity_insights.c index e0707b02bd..bebbf23b02 100644 --- a/tests/fw/services/activity/test_activity_insights.c +++ b/tests/fw/services/activity/test_activity_insights.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_insights.h" -#include "services/normal/activity/activity_private.h" -#include "services/normal/activity/insights_settings.h" -#include "services/normal/filesystem/pfs.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_insights.h" +#include "pbl/services/activity/activity_private.h" +#include "pbl/services/activity/insights_settings.h" +#include "pbl/services/filesystem/pfs.h" #include "util/attributes.h" #include @@ -42,6 +29,10 @@ #include "stubs_stringlist.h" #include "stubs_system_task.h" +bool activity_is_initialized(void) { + return true; +} + // Fakes #include "fake_kernel_services_notifications.h" #include "fake_pbl_malloc.h" @@ -796,6 +787,14 @@ void test_activity_insights__sleep_summary_no_history(void) { } // --------------------------------------------------------------------------------------- +// The "activation delay" insights (the day 1 / day 4 / day 10 "pebble health nag" +// notifications) were removed from production in commit aaf2f45f3 +// ("services/normal/activity_insights: remove pebble health nag"), which deleted the +// ActivationDelayInsight table and prv_do_activation_delay_insights() from +// activity_insights.c. activity_insights_process_minute_data() therefore no longer pushes any +// activation-delay notification. These cases now assert that contract: stepping the clock across +// the windows that used to trigger the day 1 / day 4 / day 10 nags produces no ANCS +// notifications. void test_activity_insights__activation_delay_insights_time_trigger(void) { time_t now = mktime(&s_init_time_tm); prv_set_activation_time(now); @@ -808,30 +807,32 @@ void test_activity_insights__activation_delay_insights_time_trigger(void) { activity_insights_process_minute_data(now); cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 0); - now += 8 * SECONDS_PER_HOUR; // Jan 2 @ 6:00pm + now += 8 * SECONDS_PER_HOUR; // Jan 2 @ 6:00pm - used to fire the day 1 nag rtc_set_time(now); activity_insights_process_minute_data(now); - cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 1); + cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 0); now += (3 * SECONDS_PER_DAY) + (2 * SECONDS_PER_HOUR); // Jan 5 @ 8:00pm rtc_set_time(now); activity_insights_process_minute_data(now); - cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 1); + cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 0); s_health_app_opened_version = 1; - now += 30 * SECONDS_PER_MINUTE; // Jan 5 @ 8:30pm + now += 30 * SECONDS_PER_MINUTE; // Jan 5 @ 8:30pm - used to fire the day 4 nag rtc_set_time(now); activity_insights_process_minute_data(now); - cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 2); + cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 0); - now += 6 * SECONDS_PER_DAY; // Jan 11 @ 8:30pm + now += 6 * SECONDS_PER_DAY; // Jan 11 @ 8:30pm - used to fire the day 10 nag rtc_set_time(now); activity_insights_process_minute_data(now); - cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 3); + cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 0); } // --------------------------------------------------------------------------------------- +// See the note above: the activation-delay nag was removed in aaf2f45f3, so the 15-minute +// retry window that used to fire the day 1 nag now produces no notification either. void test_activity_insights__activation_delay_insights_fifteen_interval_trigger(void) { time_t now = mktime(&s_init_time_tm); prv_set_activation_time(now); @@ -847,7 +848,7 @@ void test_activity_insights__activation_delay_insights_fifteen_interval_trigger( now += (10 * SECONDS_PER_MINUTE); // Jan 2 @ 6:15pm rtc_set_time(now); activity_insights_process_minute_data(now); - cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 1); + cl_assert_equal_i(fake_kernel_services_notifications_ancs_notifications_count(), 0); } // Make sure that when the watch resets, we retain state properly diff --git a/tests/fw/services/activity/test_health_util.c b/tests/fw/services/activity/test_health_util.c index e14be14fb6..3b257c129a 100644 --- a/tests/fw/services/activity/test_health_util.c +++ b/tests/fw/services/activity/test_health_util.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -22,7 +9,7 @@ #include "stubs_text_node.h" #include "shell/prefs.h" -#include "services/normal/activity/health_util.h" +#include "pbl/services/activity/health_util.h" UnitsDistance shell_prefs_get_units_distance(void) { return UnitsDistance_Miles; diff --git a/tests/fw/services/activity/test_hr_util.c b/tests/fw/services/activity/test_hr_util.c index 2ff3d41f13..849af3683d 100644 --- a/tests/fw/services/activity/test_hr_util.c +++ b/tests/fw/services/activity/test_hr_util.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/activity/hr_util.h" +#include "pbl/services/activity/hr_util.h" #include "stubs_activity.h" diff --git a/tests/fw/services/activity/test_kraepelin_algorithm.c b/tests/fw/services/activity/test_kraepelin_algorithm.c index f2ff7fabee..abd0508480 100644 --- a/tests/fw/services/activity/test_kraepelin_algorithm.c +++ b/tests/fw/services/activity/test_kraepelin_algorithm.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include #include "applib/accel_service.h" -#include "services/common/hrm/hrm_manager_private.h" -#include "services/normal/activity/activity_algorithm.h" -#include "services/normal/activity/kraepelin/activity_algorithm_kraepelin.h" -#include "services/normal/activity/kraepelin/kraepelin_algorithm.h" +#include "pbl/services/hrm/hrm_manager_private.h" +#include "pbl/services/activity/activity_algorithm.h" +#include "pbl/services/activity/kraepelin/activity_algorithm_kraepelin.h" +#include "pbl/services/activity/kraepelin/kraepelin_algorithm.h" #include "system/logging.h" #include "system/passert.h" #include "util/list.h" @@ -482,7 +469,7 @@ static uint32_t prv_feed_reference_samples(AccelRawData *data, int num_samples) steps = ref_finish_epoch(); ref_minute_stats(&orientation, &vmc); - PBL_LOG(LOG_LEVEL_DEBUG, "processed %d samples (%d seconds) of data: %d steps", + PBL_LOG_DBG("processed %d samples (%d seconds) of data: %d steps", num_samples, num_samples / KALG_SAMPLE_HZ, steps); return steps; } @@ -1779,7 +1766,7 @@ void test_kraepelin_algorithm__activity_tests(void) { for (int i = 0; i < entry->num_samples; i++) { const bool shutting_down = (entry->force_shut_down_at == i); kalg_activities_update(s_kalg_state, now, entry->samples[i].v5_fields.steps, 0 /*vmc*/, - 0 /*orientation*/, false /*plugged_in*/, 0 /*rest_cals*/, + 0 /*orientation*/, false /*definitely_not_worn*/, 0 /*rest_cals*/, 0 /*active_cals*/, 0 /*distance*/, shutting_down, prv_activity_session_callback, NULL); if (shutting_down) { @@ -2008,7 +1995,7 @@ static void prv_feed_activity_minutes(KAlgTestActivityMinute *samples, int sampl // NOTE: We feed in a significant VMC to simulate activity so that the sleep algorithm // doesn't think we're sleeping kalg_activities_update(s_kalg_state, now, samples[i].steps, 7000 /*vmc*/, 0 /*orientation*/, - true /*plugged_in*/, samples[i].resting_calories, + true /*definitely_not_worn*/, samples[i].resting_calories, samples[i].active_calories, samples[i].distance_mm, false /* shutting_down */, prv_activity_session_callback, NULL); now += SECONDS_PER_MINUTE; diff --git a/tests/fw/services/activity/test_workout_service.c b/tests/fw/services/activity/test_workout_service.c index 5752e58dc3..aff73b2179 100644 --- a/tests/fw/services/activity/test_workout_service.c +++ b/tests/fw/services/activity/test_workout_service.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/activity/activity.h" -#include "services/normal/activity/activity_calculators.h" -#include "services/normal/activity/workout_service.h" -#include "services/common/hrm/hrm_manager.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/activity_calculators.h" +#include "pbl/services/activity/workout_service.h" +#include "pbl/services/hrm/hrm_manager.h" #include "process_management/app_install_types.h" #include "util/time/time.h" #include "util/units.h" @@ -109,6 +96,12 @@ bool sys_hrm_manager_unsubscribe(HRMSessionRef ref) { return true; } +bool sys_hrm_manager_set_update_interval(HRMSessionRef session, uint32_t update_interval_s, + uint16_t expire_s) { + s_hrm_expiration = expire_s; + return true; +} + uint32_t time_get_uptime_seconds(void) { return SECONDS_PER_DAY + rtc_get_time(); } @@ -598,6 +591,26 @@ void test_workout_service__app_open_wait_close_valid_workout(void) { cl_assert_equal_i(s_hrm_expiration, 0); } +// --------------------------------------------------------------------------------------- +// Stopping a workout should immediately apply a recovery-window expiration to the active HR +// subscription so sampling drops back to the user's preferred rate within a bounded time, even +// if the user lingers on the summary screen or the app exit path is otherwise delayed. +void test_workout_service__stop_workout_starts_recovery_timer(void) { + prv_inc_time(1 * SECONDS_PER_MINUTE); + + workout_service_frontend_opened(); + cl_assert_equal_b(s_hrm_subscribed, true); + cl_assert_equal_i(s_hrm_expiration, 0); + + cl_assert(workout_service_start_workout(ActivitySessionType_Run)); + prv_inc_time(2 * SECONDS_PER_MINUTE); + cl_assert(workout_service_stop_workout()); + + // The 1-second subscription should now have a 10-minute expiration so it doesn't run forever. + cl_assert_equal_b(s_hrm_subscribed, true); + cl_assert_equal_i(s_hrm_expiration, 10 * SECONDS_PER_MINUTE); +} + // --------------------------------------------------------------------------------------- void test_workout_service__heart_rate_zone_time(void) { const int ZONE_0_HR = 100; diff --git a/tests/fw/services/activity/wscript b/tests/fw/services/activity/wscript deleted file mode 100644 index 333f5a5df5..0000000000 --- a/tests/fw/services/activity/wscript +++ /dev/null @@ -1,122 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/health_service.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/util/base64.c" \ - " src/fw/util/stats.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/services/common/regular_timer.c " \ - " src/fw/services/normal/activity/activity.c" \ - " src/fw/services/normal/activity/activity_metrics.c" \ - " src/fw/services/normal/activity/activity_sessions.c" \ - " src/fw/services/normal/activity/activity_calculators.c" \ - " src/fw/services/normal/activity/hr_util.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/shell/normal/prefs.c" \ - " tests/fakes/fake_accel_service.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_activity.c", - override_includes=['dummy_board'], - defines=["DUMA_DISABLED", "CAPABILITY_HAS_HEALTH_TRACKING=1", - "CAPABILITY_HAS_BUILTIN_HRM=1"]) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/base64.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/stats.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/services/normal/activity/activity_insights.c" \ - " src/fw/services/normal/activity/insights_settings.c" \ - " src/fw/services/normal/timeline/metricgroup.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_clock.c" \ - " tests/fakes/fake_settings_file.c" \ - " tests/fakes/fake_kernel_services_notifications.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_activity_insights.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/activity/kraepelin/kraepelin_algorithm.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fw/services/activity/step_samples.c" \ - " tests/fw/services/activity/sleep_samples_v1.c" \ - " tests/fw/services/activity/kraepelin_reference/fourier.c" \ - " tests/fw/services/activity/kraepelin_reference/helper_worker.c" \ - " tests/fw/services/activity/kraepelin_reference/ProjectK_worker.c" \ - " tests/fw/services/activity/kraepelin_reference/raw_stats.c", - defines=['DUMA_DISABLED'], # DUMA false-positive - test_sources_ant_glob = "test_kraepelin_algorithm.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/util/base64.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/shared_circular_buffer.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/services/common/regular_timer.c " \ - " src/fw/services/normal/activity/kraepelin/activity_algorithm_kraepelin.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " tests/fakes/fake_accel_service.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_activity_algorithm_kraepelin.c", - defines=['DUMA_DISABLED'], # DUMA false-positive, therefore disabled - override_includes=['dummy_board']) - - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/activity/health_util.c", - test_sources_ant_glob = "test_health_util.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/activity/hr_util.c", - test_sources_ant_glob = "test_hr_util.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/activity/activity_calculators.c" \ - " src/fw/services/normal/activity/hr_util.c" \ - " src/fw/services/normal/activity/workout_service.c" \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_workout_service.c", - defines=["CAPABILITY_HAS_BUILTIN_HRM=1"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/activity/activity_calculators.c", - test_sources_ant_glob = "test_activity_calculators.c", - override_includes=['dummy_board']) - -# vim:filetype=python diff --git a/tests/fw/services/activity/wscript_build b/tests/fw/services/activity/wscript_build new file mode 100644 index 0000000000..ac91339080 --- /dev/null +++ b/tests/fw/services/activity/wscript_build @@ -0,0 +1,122 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/health_service.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/util/base64.c" \ + " src/fw/util/stats.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/services/regular_timer/service.c " \ + " src/fw/services/activity/activity.c" \ + " src/fw/services/activity/activity_metrics.c" \ + " src/fw/services/activity/activity_sessions.c" \ + " src/fw/services/activity/activity_calculators.c" \ + " src/fw/services/activity/hr_util.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/shell/normal/prefs.c" \ + " tests/stubs/stubs_i18n.c" \ + " tests/fakes/fake_accel_service.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_activity.c", + override_includes=['dummy_board'], + defines=["DUMA_DISABLED", "CAPABILITY_HAS_HEALTH_TRACKING=1", + "CONFIG_HRM"]) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/base64.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/stats.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/services/activity/activity_insights.c" \ + " src/fw/services/activity/insights_settings.c" \ + " src/fw/services/timeline/metricgroup.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_clock.c" \ + " tests/fakes/fake_settings_file.c" \ + " tests/fakes/fake_kernel_services_notifications.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_activity_insights.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/activity/kraepelin/kraepelin_algorithm.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fw/services/activity/step_samples.c" \ + " tests/fw/services/activity/sleep_samples_v1.c" \ + " tests/fw/services/activity/kraepelin_reference/fourier.c" \ + " tests/fw/services/activity/kraepelin_reference/helper_worker.c" \ + " tests/fw/services/activity/kraepelin_reference/ProjectK_worker.c" \ + " tests/fw/services/activity/kraepelin_reference/raw_stats.c", + defines=['DUMA_DISABLED'], # DUMA false-positive + test_sources_ant_glob = "test_kraepelin_algorithm.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/util/base64.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/shared_circular_buffer.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/services/regular_timer/service.c " \ + " src/fw/services/activity/kraepelin/activity_algorithm_kraepelin.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " tests/fakes/fake_accel_service.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_activity_algorithm_kraepelin.c", + defines=['DUMA_DISABLED'], # DUMA false-positive, therefore disabled + override_includes=['dummy_board']) + + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/activity/health_util.c", + test_sources_ant_glob = "test_health_util.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/activity/hr_util.c", + test_sources_ant_glob = "test_hr_util.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/activity/activity_calculators.c" \ + " src/fw/services/activity/hr_util.c" \ + " src/fw/services/activity/workout_service.c" \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_workout_service.c", + defines=["CONFIG_HRM"], + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/activity/activity_calculators.c", + test_sources_ant_glob = "test_activity_calculators.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/services/analytics/test_analytics_event.c b/tests/fw/services/analytics/test_analytics_event.c deleted file mode 100644 index 1da5d48642..0000000000 --- a/tests/fw/services/analytics/test_analytics_event.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics_event.h" -#include "services/common/analytics/analytics_logging.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_analytics.h" - -#include - -#include - -// -// Stubs -// -#include "stubs_bt_lock.h" -#include "stubs_passert.h" - -CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session) { - return CommSessionTransportType_PlainSPP; -} - -bool comm_session_is_valid(const CommSession *session) { - return false; -} - -typedef struct GAPLEConnection GAPLEConnection; - -GAPLEConnection *gap_le_connection_get_gateway(void) { - return NULL; -} - -const PebbleProcessMd *launcher_menu_app_get_app_info(void) { - return NULL; -} - -void sys_analytics_logging_log_event(AnalyticsEventBlob *event_blob) { -} - -bool sys_process_manager_get_current_process_uuid(Uuid *uuid_out) { - return false; -} - -// -// Fakes & helpers -// - -#define TEST_EVENT_BLOB_VERSION (0) -#define TEST_EVENT_TIMESTAMP (0) - -static AnalyticsEventBlob s_last_blob; -void analytics_logging_log_event(AnalyticsEventBlob *event_blob) { - s_last_blob = *event_blob; - s_last_blob.version = TEST_EVENT_BLOB_VERSION; - s_last_blob.timestamp = TEST_EVENT_TIMESTAMP; - s_last_blob.kind = ANALYTICS_BLOB_KIND_EVENT; -} - -#define cl_assert_equal_last_blob(b) \ - cl_assert_equal_m(&s_last_blob, &b, sizeof(s_last_blob)); - -// -// Tests -// -void test_analytics_event__initialization(void) { - s_last_blob = (AnalyticsEventBlob) {}; -} - -void test_analytics_event__cleanup(void) { - -} - -void test_analytics_event__analytics_event_app_crash(void) { - const Uuid app_uuid = UuidMake(0xBE, 0x85, 0x14, 0x68, 0x70, 0x21, 0x43, 0xC6, - 0xAB, 0x44, 0xB8, 0x36, 0xFC, 0xD0, 0x33, 0x04); - const uint32_t pc = 0x8888888; - const uint32_t lr = 0x2222222; - const uint8_t build_id[BUILD_ID_EXPECTED_LEN] = { - 0x53, 0x98, 0xB6, 0x7E, 0x98, 0xA2, 0x44, 0x35, 0x67, 0x9B, - 0xA4, 0xB0, 0x08, 0x95, 0xB8, 0x8F, 0x14, 0xDA, 0x5A, 0x11, - }; - - AnalyticsEventBlob expected_blob = { - .kind = ANALYTICS_BLOB_KIND_EVENT, - .version = TEST_EVENT_BLOB_VERSION, - .timestamp = TEST_EVENT_TIMESTAMP, - .event = AnalyticsEvent_AppCrash, - .app_crash_report = { - .uuid = app_uuid, - .pc = pc, - .lr = lr, - .build_id_slice = { - build_id[0], build_id[1], build_id[2], build_id[3], - }, - }, - }; - - // Non-Rocky.js app: - analytics_event_app_crash(&app_uuid, pc, lr, build_id, false /* is_rocky_app */); - expected_blob.event = AnalyticsEvent_AppCrash; - cl_assert_equal_last_blob(expected_blob); - - // Rocky.js app: - expected_blob.event = AnalyticsEvent_RockyAppCrash; - analytics_event_app_crash(&app_uuid, pc, lr, build_id, true /* is_rocky_app */); - cl_assert_equal_last_blob(expected_blob); -} diff --git a/tests/fw/services/analytics/test_analytics_heartbeat.c b/tests/fw/services/analytics/test_analytics_heartbeat.c deleted file mode 100644 index a21808142c..0000000000 --- a/tests/fw/services/analytics/test_analytics_heartbeat.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/analytics/analytics_heartbeat.h" -#include "services/common/analytics/analytics_metric.h" -#include "clar.h" -#include "fake_kernel_malloc.h" -#include "fake_rtc.h" -#include "stubs_logging.h" -#include "stubs_mutex.h" -#include "stubs_passert.h" -#include "stubs_pebble_tasks.h" -#include "stubs_rand_ptr.h" - -void test_analytics_heartbeat__initialize(void) { - analytics_metric_init(); -} - -void test_analytics_heartbeat__cleanup(void) { - -} - -static Uuid test_uuid = (Uuid){0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; - -// A minimal, basic test that heartbeats don't overwrite adjacent data when -// fields next to eachother are set. We set UUID first (well, create_app does), -// and then set the fields on either side, and verify that UUID remains -// unchanged. -// struct AppHeartbeat { -// [...] -// uint32 TIME_INTERVAL -// Uuid UUID -// uint8 SDK_MAJOR_VERSION -// [...] -// } -void test_analytics_heartbeat__test_read_write_sanity(void) { - // Set Metrics - AnalyticsHeartbeat *heartbeat = analytics_heartbeat_app_create(&test_uuid); - int64_t time_interval = 0x10111213; - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_TIME_INTERVAL, time_interval); - int64_t sdk_major_version = 0x14; - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_SDK_MAJOR_VERSION, sdk_major_version); - - // Verify that things were set as expected, and adjacent metrics were not - // overwritten. - int64_t got_time_interval = analytics_heartbeat_get(heartbeat, ANALYTICS_APP_METRIC_TIME_INTERVAL); - cl_assert(time_interval == got_time_interval); - int64_t got_sdk_major_version = analytics_heartbeat_get(heartbeat, ANALYTICS_APP_METRIC_SDK_MAJOR_VERSION); - cl_assert(sdk_major_version == got_sdk_major_version); - for (int i = 0; i < sizeof(test_uuid); i++) { - int64_t expected_uuid_byte = i; - int64_t got_uuid_byte = analytics_heartbeat_get_array(heartbeat, ANALYTICS_APP_METRIC_UUID, i); - cl_assert(got_uuid_byte == expected_uuid_byte); - } - - kernel_free(heartbeat); -} - -// Repeat a given bit several times. -static int64_t pattern(uint8_t i, size_t n) { - uint64_t pat = 0; - uint64_t mask = i; - for (size_t j = 0; j < n; j++) { - pat |= mask << (j*8); - } - pat &= 0x7f; // truncate so there are no overflows - return *(int64_t*)&pat; -} - -static void verify_metric(AnalyticsHeartbeat *heartbeat, AnalyticsMetric metric, uint8_t i, size_t j) { - uint32_t item_size = analytics_metric_element_size(metric); - int64_t expected = pattern(i, item_size); - int64_t got; - if (j == -1) { - got = analytics_heartbeat_get(heartbeat, metric); - } else { - got = analytics_heartbeat_get_array(heartbeat, metric, j); - } - printf("Expected %"PRIx64", got %"PRIx64" (item_size=%"PRIu32")\n", - expected, got, item_size); - cl_assert(got == expected); -} - -void test_analytics_heartbeat__clipping(void) { - AnalyticsHeartbeat *heartbeat = analytics_heartbeat_app_create(&test_uuid); - int64_t time_interval = 0x10111213; - - // uint8_t overflow - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_LAUNCH_COUNT, 300); - uint8_t val8 = analytics_heartbeat_get(heartbeat, - ANALYTICS_APP_METRIC_LAUNCH_COUNT); - cl_assert(val8 == (uint8_t)0xff); - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_LAUNCH_COUNT, 80); - val8 = analytics_heartbeat_get(heartbeat, - ANALYTICS_APP_METRIC_LAUNCH_COUNT); - cl_assert(val8 == 80); - - // uint16_t overflow - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_MSG_DROP_COUNT, - 70000); - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_MSG_DROP_COUNT, - 70001); - uint16_t val16 = analytics_heartbeat_get(heartbeat, - ANALYTICS_APP_METRIC_MSG_DROP_COUNT); - cl_assert(val16 == (uint16_t)0xffff); - - // uint32_t overflow - analytics_heartbeat_set(heartbeat, ANALYTICS_APP_METRIC_MSG_BYTE_IN_COUNT, - ((uint64_t)0x1) << 34); - uint32_t val32 = analytics_heartbeat_get(heartbeat, - ANALYTICS_APP_METRIC_MSG_BYTE_IN_COUNT); - cl_assert(val32 == (uint32_t)0xffffffff); - - kernel_free(heartbeat); -} - -// Set every single app metric defined in the app heartbeat, and verify they -// are read out correctly, without overwriting any adjacent fields. Our malloc() -// mock also verifies that we don't write past the end of the heartbeat. -void test_analytics_heartbeat__test_read_write_all_app_metrics(void) { - printf("Starting test...\n"); - AnalyticsHeartbeat *heartbeat = analytics_heartbeat_app_create(&test_uuid); - - uint8_t i = 0x80; - for (AnalyticsMetric metric = ANALYTICS_APP_METRIC_START + 1; - metric < ANALYTICS_APP_METRIC_END; metric++) { - if (analytics_metric_is_array(metric)) { - for (int j = 0; j < analytics_metric_num_elements(metric); j++) { - analytics_heartbeat_set_array(heartbeat, metric, j, pattern(i, 8)); - i++; - } - } else { - analytics_heartbeat_set(heartbeat, metric, pattern(i, 8)); - i++; - } - } - - analytics_heartbeat_print(heartbeat); - - i = 0x80; - for (AnalyticsMetric metric = ANALYTICS_APP_METRIC_START + 1; - metric < ANALYTICS_APP_METRIC_END; metric++) { - if (analytics_metric_is_array(metric)) { - for (int j = 0; j < analytics_metric_num_elements(metric); j++) { - verify_metric(heartbeat, metric, i, j); - i++; - } - } else { - verify_metric(heartbeat, metric, i, -1); - i++; - } - } - kernel_free(heartbeat); -} diff --git a/tests/fw/services/analytics/wscript b/tests/fw/services/analytics/wscript deleted file mode 100644 index fc27aad800..0000000000 --- a/tests/fw/services/analytics/wscript +++ /dev/null @@ -1,18 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/analytics/analytics_heartbeat.c" \ - " src/fw/services/normal/analytics/analytics_metric.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_analytics_heartbeat.c") - - clar(ctx, - sources_ant_glob=( - " src/fw/services/normal/analytics/analytics_event.c" - ), - test_sources_ant_glob="test_analytics_event.c", - override_includes=['dummy_board']) diff --git a/tests/fw/services/app_message/test_app_message_receiver.c b/tests/fw/services/app_message/test_app_message_receiver.c index 88febdc4e7..15dcbbe890 100644 --- a/tests/fw/services/app_message/test_app_message_receiver.c +++ b/tests/fw/services/app_message/test_app_message_receiver.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -20,7 +7,7 @@ #include "applib/app_message/app_message_internal.h" #include "applib/app_message/app_message_receiver.h" #include "comm/bt_conn_mgr.h" -#include "services/common/comm_session/session_receive_router.h" +#include "pbl/services/comm_session/session_receive_router.h" #include "kernel/events.h" #include "process_management/app_install_types.h" diff --git a/tests/fw/services/app_message/test_app_message_sender.c b/tests/fw/services/app_message/test_app_message_sender.c index 601ffe4a36..53d0b9713e 100644 --- a/tests/fw/services/app_message/test_app_message_sender.c +++ b/tests/fw/services/app_message/test_app_message_sender.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/app_message/app_message_internal.h" #include "clar.h" -#include "services/normal/app_message/app_message_sender.h" -#include "services/common/comm_session/session_internal.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/app_message/app_message_sender.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" #include "process_management/app_install_manager.h" -#include "services/normal/app_outbox_service.h" +#include "pbl/services/app_outbox_service.h" #include "util/math.h" #include "util/net.h" @@ -78,9 +65,6 @@ void app_outbox_service_cleanup_all_pending_messages(void) { s_is_message_cancelled = true; } -void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length) { -} - static CommSession s_system_session; static CommSession *s_system_session_ptr; CommSession *comm_session_get_system_session(void) { diff --git a/tests/fw/services/app_message/test_app_session_capabilities.c b/tests/fw/services/app_message/test_app_session_capabilities.c index 9de2963812..d68114d415 100644 --- a/tests/fw/services/app_message/test_app_session_capabilities.c +++ b/tests/fw/services/app_message/test_app_session_capabilities.c @@ -1,26 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/comm_session/app_session_capabilities.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/app_session_capabilities.h" +#include "pbl/services/comm_session/session.h" #include "process_management/pebble_process_md.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/settings/settings_file.h" #include "system/status_codes.h" +#include "stubs_logging.h" +#include "stubs_mutex.h" +#include "stubs_passert.h" + static const CommSessionCapability s_live_capabilities = (CommSessionInfiniteLogDumping); // Fakes & Stubs diff --git a/tests/fw/services/app_message/wscript b/tests/fw/services/app_message/wscript deleted file mode 100644 index 90aadb0119..0000000000 --- a/tests/fw/services/app_message/wscript +++ /dev/null @@ -1,31 +0,0 @@ -from waftools.pebble_test import clar - - -def build(bld): - clar(bld, - sources_ant_glob=( - "src/fw/applib/app_inbox.c " - "src/fw/applib/app_message/app_message_receiver.c " - "src/fw/services/normal/app_message/app_message_receiver.c " - "src/fw/services/normal/app_inbox_service.c " - ), - test_sources_ant_glob="test_app_message_receiver.c", - override_includes=['applib_malloc']) - - clar(bld, - sources_ant_glob=( - "src/fw/services/normal/app_message/app_message_sender.c " - "src/fw/services/common/comm_session/session_send_queue.c " - ), - test_sources_ant_glob="test_app_message_sender.c", - override_includes=[]) - - clar(bld, - sources_ant_glob=( - "src/fw/services/normal/comm_session/app_session_capabilities.c " - ), - test_sources_ant_glob="test_app_session_capabilities.c", - override_includes=[]) - - -# vim:filetype=python diff --git a/tests/fw/services/app_message/wscript_build b/tests/fw/services/app_message/wscript_build new file mode 100644 index 0000000000..3b788c7c9a --- /dev/null +++ b/tests/fw/services/app_message/wscript_build @@ -0,0 +1,28 @@ +from waftools.pebble_test import clar + +clar(bld, + sources_ant_glob=( + "src/fw/applib/app_inbox.c " + "src/fw/applib/app_message/app_message_receiver.c " + "src/fw/services/app_message/app_message_receiver.c " + "src/fw/services/app_inbox_service/service.c " + ), + test_sources_ant_glob="test_app_message_receiver.c", + override_includes=['applib_malloc']) + +clar(bld, + sources_ant_glob=( + "src/fw/services/app_message/app_message_sender.c " + "src/fw/services/comm_session/session_send_queue.c " + ), + test_sources_ant_glob="test_app_message_sender.c", + override_includes=[]) + +clar(bld, + sources_ant_glob=( + "src/fw/services/comm_session/app_session_capabilities.c " + ), + test_sources_ant_glob="test_app_session_capabilities.c", + override_includes=[]) + +# vim:filetype=python diff --git a/tests/fw/services/blob_db/test_app_db.c b/tests/fw/services/blob_db/test_app_db.c index 1476b8cd36..290fab469b 100644 --- a/tests/fw/services/blob_db/test_app_db.c +++ b/tests/fw/services/blob_db/test_app_db.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "process_management/app_install_types.h" #include "process_management/pebble_process_info.h" #include "process_management/pebble_process_md.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/blob_db/app_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/blob_db/app_db.h" // Fixture //////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/blob_db/test_app_glance_db.c b/tests/fw/services/blob_db/test_app_glance_db.c index f3617e529f..648261fd98 100644 --- a/tests/fw/services/blob_db/test_app_glance_db.c +++ b/tests/fw/services/blob_db/test_app_glance_db.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -20,10 +7,10 @@ #include "drivers/rtc.h" #include "kernel/pbl_malloc.h" #include "resource/resource_ids.auto.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "services/normal/blob_db/app_glance_db.h" -#include "services/normal/blob_db/app_glance_db_private.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "pbl/services/blob_db/app_glance_db.h" +#include "pbl/services/blob_db/app_glance_db_private.h" +#include "pbl/services/filesystem/pfs.h" #include "util/uuid.h" // Fakes diff --git a/tests/fw/services/blob_db/test_blob_db2_endpoint.c b/tests/fw/services/blob_db/test_blob_db2_endpoint.c index effcaa4689..f696912f9d 100644 --- a/tests/fw/services/blob_db/test_blob_db2_endpoint.c +++ b/tests/fw/services/blob_db/test_blob_db2_endpoint.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/comm_session/session.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/blob_db/endpoint.h" -#include "services/normal/blob_db/sync.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/endpoint.h" +#include "pbl/services/blob_db/sync.h" #include @@ -32,6 +19,8 @@ //////////////////////////////////// #include "stubs_analytics.h" #include "stubs_app_cache.h" +#include "stubs_app_glance_db.h" +#include "stubs_health_db.h" #include "stubs_ios_notif_pref_db.h" #include "stubs_app_db.h" #include "stubs_contacts_db.h" @@ -50,6 +39,7 @@ #include "stubs_pbl_malloc.h" #include "stubs_reminders.h" #include "stubs_regular_timer.h" +#include "stubs_settings_blob_db.h" CommSession *comm_session_get_system_session(void) { @@ -121,9 +111,27 @@ status_t blob_db_sync_db(BlobDBId db_id) { return S_SUCCESS; } +// A fake dirty item is needed because the error path of the write/writeback response handler +// logs the rejected item by dereferencing sync_session->dirty_list->key. Allocate a real +// BlobDBDirtyItem (which has a flexible key[] member) so that path reads valid memory. +static const char s_fake_dirty_key[] = "fakekey"; + +#define FAKE_DIRTY_ITEM_SIZE (sizeof(BlobDBDirtyItem) + sizeof(s_fake_dirty_key)) +static uint8_t s_fake_dirty_item_storage[FAKE_DIRTY_ITEM_SIZE]; +static BlobDBSyncSession s_fake_sync_session; + BlobDBSyncSession *blob_db_sync_get_session_for_token(BlobDBToken token) { + BlobDBDirtyItem *dirty_item = (BlobDBDirtyItem *)s_fake_dirty_item_storage; + dirty_item->node = (ListNode){ 0 }; + dirty_item->last_updated = 0; + dirty_item->key_len = sizeof(s_fake_dirty_key); + memcpy(dirty_item->key, s_fake_dirty_key, sizeof(s_fake_dirty_key)); + + s_fake_sync_session = (BlobDBSyncSession){ + .dirty_list = dirty_item, + }; // Don't return NULL - return (BlobDBSyncSession *)1; + return &s_fake_sync_session; } extern void blob_db2_set_accepting_messages(bool ehh); @@ -196,9 +204,14 @@ void test_blob_db2_endpoint__handle_write_response(void) { sizeof(s_start_write_response_success)); cl_assert(did_sync_next); + // A rejected (error) write response still advances the sync: the item is logged and then marked + // synced via blob_db_sync_next() to avoid re-sending it on every sync. The endpoint never + // cancels a sync in response to a write/writeback error. + did_sync_next = false; blob_db2_protocol_msg_callback(NULL, s_start_write_response_error, sizeof(s_start_write_response_error)); - cl_assert(did_sync_cancel); + cl_assert(did_sync_next); + cl_assert(!did_sync_cancel); } @@ -219,9 +232,13 @@ void test_blob_db2_endpoint__handle_writeback_response(void) { sizeof(s_start_writeback_response_success)); cl_assert(did_sync_next); + // As with the write response above, a rejected writeback advances the sync rather than + // cancelling it. + did_sync_next = false; blob_db2_protocol_msg_callback(NULL, s_start_writeback_response_error, sizeof(s_start_writeback_response_error)); - cl_assert(did_sync_cancel); + cl_assert(did_sync_next); + cl_assert(!did_sync_cancel); } static const uint8_t s_sync_done_response[] = { diff --git a/tests/fw/services/blob_db/test_blob_db_endpoint.c b/tests/fw/services/blob_db/test_blob_db_endpoint.c index 9415093f11..49653d9f7c 100644 --- a/tests/fw/services/blob_db/test_blob_db_endpoint.c +++ b/tests/fw/services/blob_db/test_blob_db_endpoint.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/comm_session/session.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/normal/blob_db/api.h" -#include "services/normal/blob_db/endpoint.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/endpoint.h" #include "util/attributes.h" #include @@ -42,6 +29,8 @@ #include "stubs_reminder_db.h" #include "stubs_watch_app_prefs_db.h" #include "stubs_weather_db.h" +#include "stubs_health_db.h" +#include "stubs_app_glance_db.h" #include "stubs_bt_lock.h" #include "stubs_evented_timer.h" #include "stubs_events.h" @@ -51,6 +40,7 @@ #include "stubs_pbl_malloc.h" #include "stubs_reminders.h" #include "stubs_regular_timer.h" +#include "stubs_settings_blob_db.h" void bt_persistent_storage_set_unfaithful(bool is_unfaithful) { return; diff --git a/tests/fw/services/blob_db/test_blob_db_sync.c b/tests/fw/services/blob_db/test_blob_db_sync.c index e7c9e29c21..0dcfd56c4c 100644 --- a/tests/fw/services/blob_db/test_blob_db_sync.c +++ b/tests/fw/services/blob_db/test_blob_db_sync.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -31,9 +18,9 @@ // FW Includes /////////////// -#include "services/normal/blob_db/api.h" -#include "services/normal/blob_db/util.h" -#include "services/normal/blob_db/sync.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/util.h" +#include "pbl/services/blob_db/sync.h" #include "util/size.h" @@ -70,7 +57,7 @@ BlobDBToken blob_db_endpoint_send_writeback(BlobDBId db_id, BlobDBSyncSession *session = blob_db_sync_get_session_for_id(db_id); cl_assert(session != NULL); if (s_num_until_timeout != 0 && s_num_writebacks >= s_num_until_timeout) { - fake_regular_timer_trigger(&session->timeout_timer); + // Don't respond - simulates timeout (message lost/no response from phone) } else { system_task_add_callback(prv_handle_response_from_phone, session); } @@ -259,6 +246,12 @@ void test_blob_db_sync__timeout_and_retry(void) { cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS); prv_generate_responses_from_phone(); cl_assert_equal_i(s_num_writebacks, s_num_until_timeout); + + // Cancel the timed-out session so we can start a fresh sync + BlobDBSyncSession *session = blob_db_sync_get_session_for_id(BlobDBIdTest); + cl_assert(session != NULL); + blob_db_sync_cancel(session); + s_num_until_timeout = 0; cl_assert(blob_db_sync_db(BlobDBIdTest) == S_SUCCESS); prv_generate_responses_from_phone(); diff --git a/tests/fw/services/blob_db/test_contacts_db.c b/tests/fw/services/blob_db/test_contacts_db.c index b10dbd5182..818c3ce524 100644 --- a/tests/fw/services/blob_db/test_contacts_db.c +++ b/tests/fw/services/blob_db/test_contacts_db.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/blob_db/contacts_db.h" -#include "services/normal/contacts/contacts.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/blob_db/contacts_db.h" +#include "pbl/services/contacts/contacts.h" #include "util/uuid.h" // Fixture diff --git a/tests/fw/services/blob_db/test_health_db.c b/tests/fw/services/blob_db/test_health_db.c index 669586a6b9..98e91e9608 100644 --- a/tests/fw/services/blob_db/test_health_db.c +++ b/tests/fw/services/blob_db/test_health_db.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/blob_db/health_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/blob_db/health_db.h" #include "util/size.h" #include diff --git a/tests/fw/services/blob_db/test_ios_notif_pref_db.c b/tests/fw/services/blob_db/test_ios_notif_pref_db.c index 135c419fb5..e83a02b8de 100644 --- a/tests/fw/services/blob_db/test_ios_notif_pref_db.c +++ b/tests/fw/services/blob_db/test_ios_notif_pref_db.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" #include "util/size.h" // Fixture @@ -109,11 +96,23 @@ void test_ios_notif_pref_db__read_flags(void) { void test_ios_notif_pref_db__store_prefs(void) { // Create an attribute list and action group + struct { + StringList list; + char data[9]; + } filtering_rules = { + .list = { + .serialized_byte_length = 9, + }, + .data = { 0x01, 0x00, 0x00, 0x00, 's', 'p', 'a', 'm', '\0' }, + }; + AttributeList attr_list; attribute_list_init_list(0, &attr_list); attribute_list_add_cstring(&attr_list, AttributeIdShortTitle, "Title"); attribute_list_add_uint8(&attr_list, AttributeIdMuteDayOfWeek, 0x1F); attribute_list_add_cstring(&attr_list, AttributeIdAppName, "GMail"); + attribute_list_add_string_list(&attr_list, AttributeIdNotificationFilteringRules, + &filtering_rules.list); TimelineItemActionGroup action_group = { .num_actions = 0, }; @@ -135,13 +134,20 @@ void test_ios_notif_pref_db__store_prefs(void) { Attribute *name = attribute_find(¬if_prefs->attr_list, AttributeIdAppName); cl_assert(name); cl_assert_equal_s(name->cstring, "GMail"); + StringList *rules = attribute_get_string_list(¬if_prefs->attr_list, + AttributeIdNotificationFilteringRules); + cl_assert(rules); + cl_assert_equal_i(rules->serialized_byte_length, filtering_rules.list.serialized_byte_length); + cl_assert_equal_m(rules->data, filtering_rules.data, filtering_rules.list.serialized_byte_length); // Update the current entry with a new attribute attribute_list_add_uint32(&attr_list, AttributeIdLastUpdated, 123456); + attribute_list_add_uint32(&attr_list, AttributeIdMuteExpiration, 1767322800); ios_notif_pref_db_store_prefs((uint8_t *)key, key_len, &attr_list, &action_group); // Make sure we can get all the data back + ios_notif_pref_db_free_prefs(notif_prefs); notif_prefs = ios_notif_pref_db_get_prefs((uint8_t *)key, key_len); cl_assert(notif_prefs); title = attribute_find(¬if_prefs->attr_list, AttributeIdShortTitle); @@ -153,9 +159,17 @@ void test_ios_notif_pref_db__store_prefs(void) { name = attribute_find(¬if_prefs->attr_list, AttributeIdAppName); cl_assert(name); cl_assert_equal_s(name->cstring, "GMail"); + rules = attribute_get_string_list(¬if_prefs->attr_list, + AttributeIdNotificationFilteringRules); + cl_assert(rules); + cl_assert_equal_i(rules->serialized_byte_length, filtering_rules.list.serialized_byte_length); + cl_assert_equal_m(rules->data, filtering_rules.data, filtering_rules.list.serialized_byte_length); Attribute *updated = attribute_find(¬if_prefs->attr_list, AttributeIdLastUpdated); cl_assert(updated); cl_assert_equal_i(updated->uint32, 123456); + Attribute *expiration = attribute_find(¬if_prefs->attr_list, AttributeIdMuteExpiration); + cl_assert(expiration); + cl_assert_equal_i(expiration->uint32, 1767322800); attribute_list_destroy_list(&attr_list); ios_notif_pref_db_free_prefs(notif_prefs); diff --git a/tests/fw/services/blob_db/test_notif_db.c b/tests/fw/services/blob_db/test_notif_db.c index 3bdfbcab9a..c146f8b633 100644 --- a/tests/fw/services/blob_db/test_notif_db.c +++ b/tests/fw/services/blob_db/test_notif_db.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "util/uuid.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/blob_db/notif_db.h" -#include "services/normal/notifications/notification_storage.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/blob_db/notif_db.h" +#include "pbl/services/notifications/notification_storage.h" // Fixture //////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/blob_db/test_pin_db.c b/tests/fw/services/blob_db/test_pin_db.c index 4a6785fccd..1976d8b2cc 100644 --- a/tests/fw/services/blob_db/test_pin_db.c +++ b/tests/fw/services/blob_db/test_pin_db.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/timeline.h" // Fixture //////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/blob_db/test_prefs_db.c b/tests/fw/services/blob_db/test_prefs_db.c index 4d1fb5980a..ffdfde7165 100644 --- a/tests/fw/services/blob_db/test_prefs_db.c +++ b/tests/fw/services/blob_db/test_prefs_db.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "util/uuid.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/blob_db/prefs_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/blob_db/prefs_db.h" #include "shell/prefs.h" #include "shell/prefs_private.h" @@ -35,22 +22,30 @@ //////////////////////////////////////////////////////////////// #include "stubs_analytics.h" #include "stubs_app_install_manager.h" +#include "stubs_event_loop.h" #include "stubs_hexdump.h" #include "stubs_logging.h" #include "stubs_mfg_info.h" #include "stubs_mutex.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" +#include "stubs_powermode_service.h" #include "stubs_prompt.h" #include "stubs_rand_ptr.h" #include "stubs_sleep.h" #include "stubs_system_theme.h" #include "stubs_task_watchdog.h" +#include "stubs_timeline_peek.h" +#include "stubs_ambient_light.h" +#include "stubs_activity.h" -void i18n_enable(bool enable) { +void prefs_sync_init(void) { +} + +void event_put(PebbleEvent *event) { } -void display_set_offset(GPoint offset) { +void i18n_enable(bool enable) { } void test_prefs_db__initialize(void) { diff --git a/tests/fw/services/blob_db/test_reminder_db.c b/tests/fw/services/blob_db/test_reminder_db.c index eac4a598f3..e6ce618b87 100644 --- a/tests/fw/services/blob_db/test_reminder_db.c +++ b/tests/fw/services/blob_db/test_reminder_db.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/blob_db/reminder_db.h" +#include "pbl/services/blob_db/reminder_db.h" // Fixture //////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/blob_db/test_timeline.c b/tests/fw/services/blob_db/test_timeline.c index 9f81040feb..d3867df0bd 100644 --- a/tests/fw/services/blob_db/test_timeline.c +++ b/tests/fw/services/blob_db/test_timeline.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "util/uuid.h" -#include "services/normal/filesystem/pfs.h" -#include "services/common/regular_timer.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/timeline.h" #include "util/list.h" #include "util/size.h" @@ -592,11 +579,7 @@ void test_timeline__long_middle_past(void) { timeline_init(&head); cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast, 14700), 0); -#if !CAPABILITY_HAS_CORE_NAVIGATION4 - cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id)); - - cl_assert(iter_next(&iterator)); -#endif + cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[1].header.id)); cl_assert(iter_next(&iterator)); @@ -605,11 +588,6 @@ void test_timeline__long_middle_past(void) { cl_assert(iter_prev(&iterator)); cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[1].header.id)); -#if !CAPABILITY_HAS_CORE_NAVIGATION4 - cl_assert(iter_prev(&iterator)); - cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id)); -#endif - cl_assert(!iter_prev(&iterator)); } @@ -623,11 +601,11 @@ void test_timeline__long_middle_future(void) { timeline_init(&head); cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, 14700), 0); -#if CAPABILITY_HAS_CORE_NAVIGATION4 + cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id)); cl_assert(iter_next(&iterator)); -#endif + cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[3].header.id)); cl_assert(iter_next(&iterator)); @@ -637,10 +615,8 @@ void test_timeline__long_middle_future(void) { cl_assert(iter_prev(&iterator)); cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[3].header.id)); -#if CAPABILITY_HAS_CORE_NAVIGATION4 cl_assert(iter_prev(&iterator)); cl_assert(uuid_equal(&state.pin.header.id, &s_long_items[2].header.id)); -#endif cl_assert(!iter_prev(&iterator)); } @@ -913,11 +889,11 @@ void test_timeline__extra_case_middle_future(void) { timeline_init(&head); cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionFuture, s_feb_5_midnight + 8 * SECONDS_PER_HOUR + 16 * SECONDS_PER_MINUTE), 0); -#if CAPABILITY_HAS_CORE_NAVIGATION4 + cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[1].header.id)); cl_assert(iter_next(&iterator)); -#endif + cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[2].header.id)); cl_assert(!iter_next(&iterator)); @@ -934,11 +910,7 @@ void test_timeline__extra_case_middle_past(void) { timeline_init(&head); cl_assert_equal_i(timeline_iter_init(&iterator, &state, &head, TimelineIterDirectionPast, s_feb_5_midnight + 8 * 60 * 60 + 16 * 60), 0); -#if !CAPABILITY_HAS_CORE_NAVIGATION4 - cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[1].header.id)); - cl_assert(iter_next(&iterator)); -#endif cl_assert(uuid_equal(&state.pin.header.id, &s_extra_case_items[0].header.id)); cl_assert(!iter_next(&iterator)); diff --git a/tests/fw/services/blob_db/test_watch_app_prefs_db.c b/tests/fw/services/blob_db/test_watch_app_prefs_db.c index 49ffc5cf23..5e1dfa24bb 100644 --- a/tests/fw/services/blob_db/test_watch_app_prefs_db.c +++ b/tests/fw/services/blob_db/test_watch_app_prefs_db.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/blob_db/watch_app_prefs_db.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/weather/weather_service_private.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/weather/weather_service_private.h" #include "util/uuid.h" // Fixture diff --git a/tests/fw/services/blob_db/test_weather_db.c b/tests/fw/services/blob_db/test_weather_db.c index a7e8a769a1..4cad47ef3b 100644 --- a/tests/fw/services/blob_db/test_weather_db.c +++ b/tests/fw/services/blob_db/test_weather_db.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "util/attributes.h" #include "util/pstring.h" -#include "services/normal/blob_db/weather_db.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/weather/weather_types.h" +#include "pbl/services/blob_db/weather_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/weather/weather_types.h" #include "weather_data_shared.h" // Fixture diff --git a/tests/fw/services/blob_db/weather_data_shared.c b/tests/fw/services/blob_db/weather_data_shared.c index d9c3faff66..9ca0d57b7f 100644 --- a/tests/fw/services/blob_db/weather_data_shared.c +++ b/tests/fw/services/blob_db/weather_data_shared.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "weather_data_shared.h" @@ -20,9 +7,9 @@ #include "drivers/rtc.h" #include "kernel/pbl_malloc.h" -#include "services/normal/blob_db/watch_app_prefs_db.h" -#include "services/normal/blob_db/weather_db.h" -#include "services/normal/weather/weather_service_private.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" +#include "pbl/services/blob_db/weather_db.h" +#include "pbl/services/weather/weather_service_private.h" #define WEATHER_PREFS_DATA_SIZE (sizeof(SerializedWeatherAppPrefs) + \ (sizeof(Uuid) * WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES)) diff --git a/tests/fw/services/blob_db/weather_data_shared.h b/tests/fw/services/blob_db/weather_data_shared.h index 982967a848..e93cd72fd1 100644 --- a/tests/fw/services/blob_db/weather_data_shared.h +++ b/tests/fw/services/blob_db/weather_data_shared.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/weather_db.h" +#include "pbl/services/blob_db/weather_db.h" #define WEATHER_DATA_SHARED_WEATHER_DB_NUM_DB_ENTRIES (5) #define WEATHER_DATA_SHARED_NUM_VALID_TIMESTAMP_ENTRIES \ diff --git a/tests/fw/services/blob_db/wscript b/tests/fw/services/blob_db/wscript deleted file mode 100644 index b23deaef10..0000000000 --- a/tests/fw/services/blob_db/wscript +++ /dev/null @@ -1,254 +0,0 @@ -from waftools.pebble_test import clar - - -def build(ctx): - - def test_timeline_service(test_name, defines=None): - clar(ctx, - test_name=test_name, - defines=defines, - sources_ant_glob=( - "src/fw/services/normal/blob_db/pin_db.c " - "src/fw/services/normal/blob_db/sync_util.c " - "src/fw/services/normal/blob_db/timeline_item_storage.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/services/normal/timeline/timeline.c " - "src/fw/util/crc8.c " - "src/fw/util/time/mktime.c " - "src/fw/util/time/time.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_settings_file.c " - ), - test_sources_ant_glob="test_timeline.c", - override_includes=['dummy_board']) - - test_timeline_service(test_name='test_timeline') - test_timeline_service(test_name='test_timeline~ux4', - defines=['CAPABILITY_HAS_CORE_NAVIGATION4=1']) - - clar(ctx, - sources_ant_glob=( - "src/fw/services/normal/blob_db/pin_db.c " - "src/fw/services/normal/blob_db/sync_util.c " - "src/fw/services/normal/blob_db/timeline_item_storage.c " - "src/fw/services/normal/blob_db/util.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/util/crc8.c " - "src/fw/util/time/mktime.c " - "src/fw/util/time/time.c " - "tests/fakes/fake_blobdb.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_session.c " - "tests/fakes/fake_settings_file.c " - "tests/fakes/ram_storage.c " - "tests/fakes/test_db.c " - ), - test_sources_ant_glob="test_pin_db.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/blob_db/util.c" \ - " src/fw/services/normal/blob_db/sync.c" \ - " tests/fakes/ram_storage.c" \ - " tests/fakes/test_db.c" \ - " tests/fakes/fake_blobdb.c", - test_sources_ant_glob = "test_blob_db_sync.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/blob_db/api.c" \ - " src/fw/services/normal/blob_db/endpoint.c " \ - " src/fw/services/normal/blob_db/endpoint_private.c " \ - " src/fw/services/normal/blob_db/util.c" \ - " tests/fakes/fake_session.c", - test_sources_ant_glob = "test_blob_db_endpoint.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/blob_db/api.c" \ - " src/fw/services/normal/blob_db/endpoint2.c " \ - " src/fw/services/normal/blob_db/endpoint_private.c " \ - " src/fw/services/normal/blob_db/util.c", - test_sources_ant_glob = "test_blob_db2_endpoint.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/app_db.c", - test_sources_ant_glob = "test_app_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/blob_db/app_glance_db.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/util/crc8.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_settings_file.c" \ - " tests/fakes/fake_events.c" \ - "", - test_sources_ant_glob = "test_app_glance_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/contacts_db.c", - test_sources_ant_glob = "test_contacts_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/watch_app_prefs_db.c", - test_sources_ant_glob = "test_watch_app_prefs_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/util/time/time.c" \ - " src/fw/services/normal/blob_db/timeline_item_storage.c" \ - " tests/fakes/fake_settings_file.c" \ - " src/fw/services/normal/blob_db/reminder_db.c" \ - " src/fw/services/normal/blob_db/sync_util.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/item.c", - test_sources_ant_glob = "test_reminder_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fakes/fake_kernel_services_notifications.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/util/time/time.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/notifications/notification_storage.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/blob_db/notif_db.c", - test_sources_ant_glob = "test_notif_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/pstring.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/weather_db.c" \ - " tests/fw/services/blob_db/weather_data_shared.c", - test_sources_ant_glob = "test_weather_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/util/time/time.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/blob_db/prefs_db.c" \ - " src/fw/shell/normal/prefs.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_prefs_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/libutil/uuid.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_settings_file.c" \ - " src/fw/util/time/time.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" - " src/fw/services/normal/blob_db/sync_util.c" \ - " src/fw/services/normal/blob_db/ios_notif_pref_db.c" , - test_sources_ant_glob = "test_ios_notif_pref_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/crc8.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_settings_file.c" \ - " src/fw/services/normal/blob_db/health_db.c" , - test_sources_ant_glob = "test_health_db.c", - override_includes=['dummy_board']) - - -# vim:filetype=python diff --git a/tests/fw/services/blob_db/wscript_build b/tests/fw/services/blob_db/wscript_build new file mode 100644 index 0000000000..9ee7868d95 --- /dev/null +++ b/tests/fw/services/blob_db/wscript_build @@ -0,0 +1,250 @@ +from waftools.pebble_test import clar + +def test_timeline_service(test_name, defines=None): + clar(ctx, + test_name=test_name, + defines=defines, + sources_ant_glob=( + "src/fw/services/blob_db/pin_db.c " + "src/fw/services/blob_db/sync_util.c " + "src/fw/services/blob_db/timeline_item_storage.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/item.c " + "src/fw/services/timeline/timeline.c " + "src/fw/util/crc8.c " + "src/fw/util/time/mktime.c " + "src/fw/util/time/time.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_settings_file.c " + ), + test_sources_ant_glob="test_timeline.c", + override_includes=['dummy_board']) + +test_timeline_service(test_name='test_timeline') + +clar(ctx, + sources_ant_glob=( + "src/fw/services/blob_db/pin_db.c " + "src/fw/services/blob_db/sync_util.c " + "src/fw/services/blob_db/timeline_item_storage.c " + "src/fw/services/blob_db/util.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/item.c " + "src/fw/util/crc8.c " + "src/fw/util/time/mktime.c " + "src/fw/util/time/time.c " + "tests/fakes/fake_blobdb.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_session.c " + "tests/fakes/fake_settings_file.c " + "tests/fakes/ram_storage.c " + "tests/fakes/test_db.c " + ), + test_sources_ant_glob="test_pin_db.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/blob_db/util.c" \ + " src/fw/services/blob_db/sync.c" \ + " tests/fakes/ram_storage.c" \ + " tests/fakes/test_db.c" \ + " tests/fakes/fake_blobdb.c", + test_sources_ant_glob = "test_blob_db_sync.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/blob_db/api.c" \ + " src/fw/services/blob_db/endpoint.c " \ + " src/fw/services/blob_db/endpoint_private.c " \ + " src/fw/services/blob_db/util.c" \ + " tests/fakes/fake_session.c", + test_sources_ant_glob = "test_blob_db_endpoint.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/blob_db/api.c" \ + " src/fw/services/blob_db/endpoint2.c " \ + " src/fw/services/blob_db/endpoint_private.c " \ + " src/fw/services/blob_db/util.c", + test_sources_ant_glob = "test_blob_db2_endpoint.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/app_db.c", + test_sources_ant_glob = "test_app_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/blob_db/app_glance_db.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/util/crc8.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_settings_file.c" \ + " tests/fakes/fake_events.c" \ + "", + test_sources_ant_glob = "test_app_glance_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/contacts_db.c", + test_sources_ant_glob = "test_contacts_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/watch_app_prefs_db.c", + test_sources_ant_glob = "test_watch_app_prefs_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/util/time/time.c" \ + " src/fw/services/blob_db/timeline_item_storage.c" \ + " tests/fakes/fake_settings_file.c" \ + " src/fw/services/blob_db/reminder_db.c" \ + " src/fw/services/blob_db/sync_util.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/item.c", + test_sources_ant_glob = "test_reminder_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fakes/fake_kernel_services_notifications.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/util/time/time.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/notifications/notification_storage.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/blob_db/notif_db.c", + test_sources_ant_glob = "test_notif_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/pstring.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/weather_db.c" \ + " tests/fw/services/blob_db/weather_data_shared.c", + test_sources_ant_glob = "test_weather_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/util/time/time.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/blob_db/prefs_db.c" \ + " src/fw/shell/normal/prefs.c" \ + " tests/stubs/stubs_i18n.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_accel_service.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_prefs_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/libutil/uuid.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_settings_file.c" \ + " src/fw/util/time/time.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" + " src/fw/services/blob_db/sync_util.c" \ + " src/fw/services/blob_db/ios_notif_pref_db.c" , + test_sources_ant_glob = "test_ios_notif_pref_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/crc8.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_settings_file.c" \ + " src/fw/services/blob_db/health_db.c" , + test_sources_ant_glob = "test_health_db.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/services/bluetooth/test_ble_hrm.c b/tests/fw/services/bluetooth/test_ble_hrm.c index 88dbec8313..fc4597ef84 100644 --- a/tests/fw/services/bluetooth/test_ble_hrm.c +++ b/tests/fw/services/bluetooth/test_ble_hrm.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/bluetooth/ble_hrm.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/ble_hrm.h" #include "comm/ble/gap_le_connection.h" -#include "services/common/hrm/hrm_manager_private.h" +#include "pbl/services/hrm/hrm_manager_private.h" #include #include @@ -479,12 +466,6 @@ void test_ble_hrm__handle_hrm_event(void) { cl_assert_equal_i(1, s_last_num_permitted_devices); cl_assert_equal_m(&s_last_permitted_devices[0], s_device_a, sizeof(*s_device_a)); - prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_NoSignal, - true /* expect bt driver cb */, false /* expected_is_on_wrist */); - - prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_NoAccel, - true /* expect bt driver cb */, false /* expected_is_on_wrist */); - prv_put_and_assert_hrm_event(HRMEvent_BPM, 80, HRMQuality_OffWrist, true /* expect bt driver cb */, false /* expected_is_on_wrist */); diff --git a/tests/fw/services/bluetooth/test_bluetooth_persistent_storage.c b/tests/fw/services/bluetooth/test_bluetooth_persistent_storage.c index 8511b30681..326999d5f8 100644 --- a/tests/fw/services/bluetooth/test_bluetooth_persistent_storage.c +++ b/tests/fw/services/bluetooth/test_bluetooth_persistent_storage.c @@ -1,32 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include #include -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/normal/bluetooth/bluetooth_persistent_storage_unittest_impl.h" - -#include "services/normal/settings/settings_file.h" -#include "services/normal/filesystem/pfs.h" -#include "services/common/event_service.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_external.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage_unittest_impl.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/event_service.h" #include "flash_region/flash_region.h" // Stubs @@ -45,10 +30,8 @@ typedef struct GAPLEConnection GAPLEConnection; #include "stubs_bluetopia_interface.h" #include "stubs_bluetooth_persistent_storage_debug.h" -#include "stubs_bt_driver.h" #include "stubs_bt_lock.h" #include "stubs_gap_le_advert.h" -#include "stubs_bluetooth_analytics.h" #include "stubs_gatt_client_discovery.h" #include "stubs_gatt_client_subscriptions.h" #include "stubs_logging.h" @@ -67,8 +50,6 @@ static int s_ble_bonding_change_add_count; static int s_ble_bonding_change_update_count; static int s_ble_bonding_change_delete_count; -static int s_analytics_ble_pairings_count; - typedef bool (*BondingSyncFilterCb)(const BleBonding *bonding, void *ctx); const BleBonding *bonding_sync_find(BondingSyncFilterCb cb, void *ctx) { return NULL; @@ -96,10 +77,6 @@ void bt_driver_cb_pairing_confirm_handle_completed(const PairingUserConfirmation bool success) { } -void cc2564A_bad_le_connection_complete_handle(unsigned int stack_id, - const GAP_LE_Current_Connection_Parameters_t *params) { -} - void gap_le_connect_handle_bonding_change(BTBondingID bonding_id, BtPersistBondingOp op) { } @@ -126,30 +103,28 @@ void kernel_le_client_handle_bonding_change(BTBondingID bonding, BtPersistBondin return; } -void analytics_set(AnalyticsMetric metric, int64_t val, AnalyticsClient client) { - if (metric == ANALYTICS_DEVICE_METRIC_BLE_PAIRING_RECORDS_COUNT) { - s_analytics_ble_pairings_count = val; - } +uint16_t gaps_get_starting_att_handle(void) { + return 4; } -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) { +void gatt_service_changed_server_cleanup_by_connection(GAPLEConnection *connection) { } -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { +void launcher_task_add_callback(void (*callback)(void *data), void *data) { + callback(data); } -void gap_update_bt_classic_connectability(void) { +bool launcher_task_is_current_task(void) { + return true; } -uint16_t gaps_get_starting_att_handle(void) { - return 4; +void bt_driver_handle_host_added_cccd(const BleCCCD *cccd) { } -void gatt_service_changed_server_cleanup_by_connection(GAPLEConnection *connection) { +void bt_driver_handle_host_removed_cccd(const BleCCCD *cccd) { } -void launcher_task_add_callback(void (*callback)(void *data), void *data) { - callback(data); +void sys_pbl_analytics_set_unsigned(enum pbl_analytics_key key, uint32_t unsigned_value) { } // Tests @@ -163,7 +138,6 @@ void test_bluetooth_persistent_storage__initialize(void) { s_ble_bonding_change_add_count = 0; s_ble_bonding_change_update_count = 0; s_ble_bonding_change_delete_count = 0; - s_analytics_ble_pairings_count = 0; fake_shared_prf_storage_reset_counts(); @@ -277,7 +251,8 @@ void test_bluetooth_persistent_storage__ble_store_and_get(void) { cl_assert_equal_m(&irk_out, &pairing_1.irk, sizeof(irk_out)); cl_assert_equal_m(&device_out, &pairing_1.identity, sizeof(device_out)); - // Store another pairing + // Store a pairing with a different identity. Only one BLE pairing is allowed at a time, so this + // must replace the previous one. SMPairingInfo pairing_2; memset(&pairing_2, 0x00, sizeof(pairing_2)); pairing_2 = (SMPairingInfo) { @@ -298,46 +273,41 @@ void test_bluetooth_persistent_storage__ble_store_and_get(void) { }, .is_remote_identity_info_valid = true, }; - BTBondingID id_2 = bt_persistent_storage_store_ble_pairing(&pairing_2, false /* is_gateway */, + BTBondingID id_2 = bt_persistent_storage_store_ble_pairing(&pairing_2, true /* is_gateway */, NULL, false /* requires_address_pinning */, false /* auto_accept_re_pairing */); cl_assert(id_2 != BT_BONDING_ID_INVALID); + cl_assert(id_2 != id_1); cl_assert_equal_i(s_ble_bonding_change_add_count, 2); - cl_assert_equal_i(fake_shared_prf_storage_get_ble_store_count(), 1); // This wasn't a gateway + // pairing_1 should have been removed automatically. + cl_assert_equal_i(s_ble_bonding_change_delete_count, 1); - // Read both pairings back + // pairing_1 is gone, pairing_2 remains. ret = bt_persistent_storage_get_ble_pairing_by_id(id_1, &irk_out, &device_out, NULL /* name */); - cl_assert(ret); - cl_assert_equal_m(&irk_out, &pairing_1.irk, sizeof(irk_out)); - cl_assert_equal_m(&device_out, &pairing_1.identity, sizeof(device_out)); + cl_assert(!ret); ret = bt_persistent_storage_get_ble_pairing_by_id(id_2, &irk_out, &device_out, NULL /* name */); cl_assert(ret); cl_assert_equal_m(&irk_out, &pairing_2.irk, sizeof(irk_out)); cl_assert_equal_m(&device_out, &pairing_2.identity, sizeof(device_out)); - // Update first pairing (with the same data) - BTBondingID id_X = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, + // Re-store the same pairing (same identity): this is an update, not an add, and must not delete + // anything. + BTBondingID id_X = bt_persistent_storage_store_ble_pairing(&pairing_2, true /* is_gateway */, NULL, false /* requires_address_pinning */, false /* auto_accept_re_pairing */); - cl_assert_equal_i(id_1, id_X); + cl_assert_equal_i(id_2, id_X); cl_assert_equal_i(s_ble_bonding_change_update_count, 1); - cl_assert_equal_i(fake_shared_prf_storage_get_ble_store_count(), 1); - - // Read both pairings back again - ret = bt_persistent_storage_get_ble_pairing_by_id(id_1, &irk_out, &device_out, NULL /* name */); - cl_assert(ret); - cl_assert_equal_m(&irk_out, &pairing_1.irk, sizeof(irk_out)); - cl_assert_equal_m(&device_out, &pairing_1.identity, sizeof(device_out)); + cl_assert_equal_i(s_ble_bonding_change_delete_count, 1); ret = bt_persistent_storage_get_ble_pairing_by_id(id_2, &irk_out, &device_out, NULL /* name */); cl_assert(ret); cl_assert_equal_m(&irk_out, &pairing_2.irk, sizeof(irk_out)); cl_assert_equal_m(&device_out, &pairing_2.identity, sizeof(device_out)); - // Add a thrid pairing + // Store yet another distinct pairing: pairing_2 should be replaced. SMPairingInfo pairing_3; memset(&pairing_3, 0x00, sizeof(pairing_3)); pairing_3 = (SMPairingInfo) { @@ -364,18 +334,15 @@ void test_bluetooth_persistent_storage__ble_store_and_get(void) { false /* auto_accept_re_pairing */); cl_assert(id_3 != BT_BONDING_ID_INVALID); cl_assert_equal_i(s_ble_bonding_change_add_count, 3); - cl_assert_equal_i(fake_shared_prf_storage_get_ble_store_count(), 2); + cl_assert_equal_i(s_ble_bonding_change_delete_count, 2); - // Read all three pairings back - ret = bt_persistent_storage_get_ble_pairing_by_id(id_1, &irk_out, &device_out, NULL /* name */); - cl_assert(ret); - cl_assert_equal_m(&irk_out, &pairing_1.irk, sizeof(irk_out)); - cl_assert_equal_m(&device_out, &pairing_1.identity, sizeof(device_out)); + // Only pairing_3 should be findable by identity. Bonding slot IDs may be reused after a delete, + // so check by address rather than by stale id values. + ret = bt_persistent_storage_get_ble_pairing_by_addr(&pairing_1.identity, &irk_out, NULL); + cl_assert(!ret); - ret = bt_persistent_storage_get_ble_pairing_by_id(id_2, &irk_out, &device_out, NULL /* name */); - cl_assert(ret); - cl_assert_equal_m(&irk_out, &pairing_2.irk, sizeof(irk_out)); - cl_assert_equal_m(&device_out, &pairing_2.identity, sizeof(device_out)); + ret = bt_persistent_storage_get_ble_pairing_by_addr(&pairing_2.identity, &irk_out, NULL); + cl_assert(!ret); ret = bt_persistent_storage_get_ble_pairing_by_id(id_3, &irk_out, &device_out, NULL /* name */); cl_assert(ret); @@ -383,8 +350,8 @@ void test_bluetooth_persistent_storage__ble_store_and_get(void) { cl_assert_equal_m(&device_out, &pairing_3.identity, sizeof(device_out)); bt_persistent_storage_register_existing_ble_bondings(); - cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_1, true), true); - cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_2, false), true); + cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_1, true), false); + cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_2, true), false); cl_assert_equal_b(bonding_sync_contains_pairing_info(&pairing_3, true), true); } @@ -503,54 +470,6 @@ void test_bluetooth_persistent_storage__delete_ble_pairing_by_id(void) { cl_assert(!ret); } - -void test_bluetooth_persistent_storage__analytics_external_collect_ble_pairing_info(void) { - // No pairings yet - analytics_external_collect_ble_pairing_info(); - cl_assert_equal_i(s_analytics_ble_pairings_count, 0); - - // Store a pairing - SMPairingInfo pairing = (SMPairingInfo) { - .irk = (SMIdentityResolvingKey) { - .data = { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, - }, - }, - .identity = (BTDeviceInternal) { - .address = (BTDeviceAddress) { - .octets = { - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - }, - }, - .is_classic = false, - .is_random_address = false, - }, - .is_remote_identity_info_valid = true, - }; - BleBonding ble_bonding = (BleBonding) { - .is_gateway = true, - .pairing_info = pairing, - }; - bonding_sync_add_bonding(&ble_bonding); - BTBondingID id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL, - false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - cl_assert(id != BT_BONDING_ID_INVALID); - - // We should now be at 1 - analytics_external_collect_ble_pairing_info(); - cl_assert_equal_i(s_analytics_ble_pairings_count, 1); - - // Delete the Pairing - bt_persistent_storage_delete_ble_pairing_by_id(id); - cl_assert_equal_i(s_ble_bonding_change_delete_count, 1); - - // We should now be at 0 - analytics_external_collect_ble_pairing_info(); - cl_assert_equal_i(s_analytics_ble_pairings_count, 0); -} - void test_bluetooth_persistent_storage__ble_ancs_bonding(void) { bool ret; @@ -643,487 +562,6 @@ void test_bluetooth_persistent_storage__ble_device_name(void) { cl_assert_equal_i(strcmp(new_device_name, device_name_out), 0); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! BT Classic Pairing Info - -void test_bluetooth_persistent_storage__bt_classic_store_and_get(void) { - bool ret; - - // Output variables - BTDeviceAddress addr_out; - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_1 = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_1 = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_1 = 0x11; - BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - cl_assert(id_1 != BT_BONDING_ID_INVALID); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 1); - - // Read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_1, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_1, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_1, name_out); - cl_assert_equal_i(platform_bits_1, platform_bits_out); - - // Store another pairing - BTDeviceAddress addr_2 = {{0x21, 0x22, 0x23, 0x24, 0x25, 0x26}}; - SM128BitKey link_key_2 = {{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}}; - char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2"; - uint8_t platform_bits_2 = 0x22; - BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2, - name_2, &platform_bits_2); - cl_assert(id_2 != BT_BONDING_ID_INVALID); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 2); - - // Read both pairings back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_1, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_1, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_1, name_out); - cl_assert_equal_i(platform_bits_1, platform_bits_out); - - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_2, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_2, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_2, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_2, name_out); - cl_assert_equal_i(platform_bits_2, platform_bits_out); - - // Update first pairing (with the same data) - BTBondingID id_X = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - cl_assert_equal_i(id_1, id_X); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 2); - - // Read both pairings back again - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_1, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_1, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_1, name_out); - cl_assert_equal_i(platform_bits_1, platform_bits_out); - - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_2, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_2, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_2, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_2, name_out); - cl_assert_equal_i(platform_bits_2, platform_bits_out); - - // Add a thrid pairing - BTDeviceAddress addr_3 = {{0x31, 0x32, 0x33, 0x34, 0x35, 0x36}}; - SM128BitKey link_key_3 = {{0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30}}; - char name_3[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 3"; - uint8_t platform_bits_3 = 0x33; - BTBondingID id_3 = bt_persistent_storage_store_bt_classic_pairing(&addr_3, &link_key_3, - name_3, &platform_bits_3); - cl_assert(id_3 != BT_BONDING_ID_INVALID); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 3); - - - // Read all three pairings back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_1, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_1, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_1, name_out); - cl_assert_equal_i(platform_bits_1, platform_bits_out); - - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_2, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_2, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_2, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_2, name_out); - cl_assert_equal_i(platform_bits_2, platform_bits_out); - - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_3, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_3, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_3, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_3, name_out); - cl_assert_equal_i(platform_bits_3, platform_bits_out); - - // Add a fourth pairing - BTDeviceAddress addr_4 = {{0x41, 0x42, 0x43, 0x34, 0x35, 0x44}}; - SM128BitKey link_key_4 = {{0x40, 0x40, 0x30, 0x30, 0x30, 0x30, 0x30, 0x40, - 0x40, 0x40, 0x30, 0x30, 0x30, 0x30, 0x30, 0x40}}; - char name_4[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 4"; - uint8_t platform_bits_4 = 0x44; - - // Don't add the platform bits - BTBondingID id_4 = bt_persistent_storage_store_bt_classic_pairing(&addr_4, &link_key_4, name_4, NULL); - cl_assert(id_4 != BT_BONDING_ID_INVALID); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 4); - - // Update with platform bits - id_4 = bt_persistent_storage_store_bt_classic_pairing(&addr_4, NULL, NULL, &platform_bits_4); - cl_assert(id_4 != BT_BONDING_ID_INVALID); -} - -void test_bluetooth_persistent_storage__get_bt_classic_pairing_by_addr(void) { - // Output variables - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_in = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_in = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_in = 0x11; - - BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - cl_assert(id != BT_BONDING_ID_INVALID); - - // Read it back - BTBondingID id_out = bt_persistent_storage_get_bt_classic_pairing_by_addr(&addr_in, &link_key_out, - name_out, &platform_bits_out); - cl_assert_equal_i(id, id_out); - cl_assert_equal_m(&link_key_in, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_in, name_out); - cl_assert_equal_i(platform_bits_in, platform_bits_out); - - // Now try to read out a pairing that doesn't exist - addr_in.octets[0] = 0xff; - id_out = bt_persistent_storage_get_bt_classic_pairing_by_addr(&addr_in, &link_key_out, - name_out, &platform_bits_out); - cl_assert(id_out == BT_BONDING_ID_INVALID); -} - - -void test_bluetooth_persistent_storage__delete_bt_classic_pairing_by_id(void) { - bool ret; - - // Output variables - BTDeviceAddress addr_out; - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_in = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_in = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_in = 0x11; - - BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - cl_assert(id != BT_BONDING_ID_INVALID); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 1); - - // Delete the Pairing - bt_persistent_storage_delete_bt_classic_pairing_by_id(id); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_delete_count(), 2); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); - - // Add the pairing again - id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - cl_assert(id != BT_BONDING_ID_INVALID); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_store_count(), 2); - - // Delete a pairing that doesn't exist - bt_persistent_storage_delete_bt_classic_pairing_by_id(9); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_delete_count(), 3); - - // Make sure the pairing is actually still there - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - - // And delete is again - bt_persistent_storage_delete_bt_classic_pairing_by_id(id); - cl_assert_equal_i(fake_shared_prf_storage_get_bt_classic_delete_count(), 4); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); -} - -void test_bluetooth_persistent_storage__delete_bt_classic_pairing_by_addr(void) { - bool ret; - - // Output variables - BTDeviceAddress addr_out; - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_in = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_in = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_in = 0x11; - - BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - cl_assert(id != BT_BONDING_ID_INVALID); - - // Delete the Pairing - bt_persistent_storage_delete_bt_classic_pairing_by_addr(&addr_in); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); - - // Add the pairing again - id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - cl_assert(id != BT_BONDING_ID_INVALID); - - // Delete a pairing that doesn't exist - BTDeviceAddress dummy_addr = {{0xff, 0x11, 0x22, 0x14, 0x15, 0x16}}; - bt_persistent_storage_delete_bt_classic_pairing_by_addr(&dummy_addr); - - // Make sure the pairing is actually still there - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - - // And delete is again - bt_persistent_storage_delete_bt_classic_pairing_by_addr(&addr_in); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -//! Local Device Info - -void test_bluetooth_persistent_storage__test_active_gateway(void) { - bool ret; - - BtPersistBondingType type_out; - BTBondingID id_out; - - // Nothing is stored, so no active gateways yet - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Store a new BT Classic pairing - BTDeviceAddress addr_1 = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_1 = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_1 = 0x11; - - BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - cl_assert(id_1 != BT_BONDING_ID_INVALID); - - // It should be the active gateway - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(ret); - cl_assert_equal_i(id_out, id_1); - cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic); - - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Store another BT Classic pairing - BTDeviceAddress addr_2 = {{0x22, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_2 = {{0x22, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2"; - uint8_t platform_bits_2 = 0x22; - BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2, - name_2, &platform_bits_2); - cl_assert(id_2 != BT_BONDING_ID_INVALID); - - // The new pairing should be the active gateway - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(ret); - cl_assert_equal_i(id_out, id_2); - cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic); - - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Delete the pairing. - bt_persistent_storage_delete_bt_classic_pairing_by_id(id_2); - - // There should be no active gateway now - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Store a new BLE pairing - SMPairingInfo pairing_1 = (SMPairingInfo) { - .irk = (SMIdentityResolvingKey) {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}}, - .identity = (BTDeviceInternal) { - .address = (BTDeviceAddress) {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}, - .is_classic = false, - .is_random_address = false, - }, - .is_remote_identity_info_valid = true, - }; - BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, - NULL, - false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - cl_assert(id_3 != BT_BONDING_ID_INVALID); - - // There should still be no active gateway - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Manually set the active gateway - bt_persistent_storage_set_active_gateway(id_1); - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(ret); - cl_assert_equal_i(id_out, id_1); - cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic); - - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Manually set the active gateway again (to the ble pairing) - bt_persistent_storage_set_active_gateway(id_3); - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(ret); - cl_assert_equal_i(id_out, id_3); - cl_assert_equal_i(type_out, BtPersistBondingTypeBLE); - - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(ret); -} - -void test_bluetooth_persistent_storage__test_is_faithful(void) { - bool ret; - - // No pairing yet, we should be unfaithful - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(ret); - - // Add a pairing, still unfaithful - BTDeviceAddress addr_1 = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_1 = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_1 = 0x11; - - BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - cl_assert(id_1 != BT_BONDING_ID_INVALID); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(ret); - - // A "sync" happened. We are now faithful - bt_persistent_storage_set_unfaithful(false); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(!ret); - - // Add a new pairing, the active gateway should have changed making us unfaithful - BTDeviceAddress addr_2 = {{0x22, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_2 = {{0x22, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2"; - uint8_t platform_bits_2 = 0x22; - BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2, - name_2, &platform_bits_2); - cl_assert(id_2 != BT_BONDING_ID_INVALID); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(ret); - - // A "sync" happened. We are now faithful - bt_persistent_storage_set_unfaithful(false); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(!ret); - - // Add a BLE pairing. We should still be faithful (no PPoGATT yet) - SMPairingInfo pairing_1 = (SMPairingInfo) { - .irk = (SMIdentityResolvingKey) {{ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00 - }}, - .identity = (BTDeviceInternal) { - .address = (BTDeviceAddress) {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}, - .is_classic = false, - .is_random_address = false, - }, - .is_remote_identity_info_valid = true, - }; - BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, - NULL, - false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - cl_assert(id_3 != BT_BONDING_ID_INVALID); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(!ret); - - // Manually set a new active gateway. - bt_persistent_storage_set_active_gateway(id_3); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(ret); - - // We should be unfaithful - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(ret); - - // A "sync" happened. We are now faithful - bt_persistent_storage_set_unfaithful(false); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(!ret); - - // Another "sync" happened. We should still be faithful - bt_persistent_storage_set_unfaithful(false); - ret = bt_persistent_storage_is_unfaithful(); - cl_assert(!ret); -} - void test_bluetooth_persistent_storage__test_root_keys(void) { SM128BitKey keys[2] = { {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}, @@ -1151,27 +589,6 @@ void test_bluetooth_persistent_storage__delete_all(void) { bool ret; // Add some pairings - // BT Classic pairing 1 - BTDeviceAddress addr_1 = {{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_1 = {{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_1 = 0x11; - - BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - cl_assert(id_1 != BT_BONDING_ID_INVALID); - - // BT Classic pairing 2 - BTDeviceAddress addr_2 = {{0x22, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_2 = {{0x22, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}; - char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2"; - uint8_t platform_bits_2 = 0x22; - BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2, - name_2, &platform_bits_2); - cl_assert(id_2 != BT_BONDING_ID_INVALID); - // BLE pairing 1 SMPairingInfo pairing_1 = (SMPairingInfo) { .irk = (SMIdentityResolvingKey) {{ @@ -1185,7 +602,7 @@ void test_bluetooth_persistent_storage__delete_all(void) { }, .is_remote_identity_info_valid = true, }; - BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, + BTBondingID id_1 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, NULL, false /* requires_address_pinning */, false /* auto_accept_re_pairing */); @@ -1203,7 +620,7 @@ void test_bluetooth_persistent_storage__delete_all(void) { }, .is_remote_identity_info_valid = true, }; - BTBondingID id_4 = bt_persistent_storage_store_ble_pairing(&pairing_2, false /* is_gateway */, + BTBondingID id_2 = bt_persistent_storage_store_ble_pairing(&pairing_2, false /* is_gateway */, NULL, false /* requires_address_pinning */, false /* auto_accept_re_pairing */); @@ -1212,48 +629,14 @@ void test_bluetooth_persistent_storage__delete_all(void) { bt_persistent_storage_delete_all_pairings(); // Try to get the pairings - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, NULL, NULL, NULL, NULL); - cl_assert(!ret); - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_2, NULL, NULL, NULL, NULL); + ret = bt_persistent_storage_get_ble_pairing_by_id(id_1, NULL, NULL, NULL); cl_assert(!ret); - ret = bt_persistent_storage_get_ble_pairing_by_id(id_3, NULL, NULL, NULL); - cl_assert(!ret); - ret = bt_persistent_storage_get_ble_pairing_by_id(id_4, NULL, NULL, NULL); + ret = bt_persistent_storage_get_ble_pairing_by_id(id_2, NULL, NULL, NULL); cl_assert(!ret); } // Test to make sure we don't accidentally change the serialized data formats. void test_bluetooth_persistent_storage__ble_serialized_data(void) { -#if UNITTEST_BT_PERSISTENT_STORAGE_VERSION == 1 - - //0000 01 00 69 50 68 6f 6e 65 20 4d 61 72 74 79 00 00 ..iPhone Marty.. - //0010 00 00 00 00 00 00 3f f9 92 8a 00 00 00 00 75 36 ......?. ......u6 - //0020 9c 6e 1a 1b eb 5f fb 89 db 0b ec a5 95 7a 44 f6 .n..._.. .....zD. - //0030 1c 47 90 53 43 18 f3 e7 00 00 00 00 00 00 d1 6d .G.SC... .......m - //0040 89 95 83 aa 5e 7f ff 39 b3 47 36 e4 37 7e 05 1b ....^..9 .G6.7~.. - //0050 85 e3 b8 98 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ - //0060 00 00 00 00 00 00 07 00 00 00 00 00 00 00 ........ ...... - - const uint8_t expected_raw_data[] = { - 0x01, 0x00, 0x69, 0x50, 0x68, 0x6f, 0x6e, 0x65, - 0x20, 0x4d, 0x61, 0x72, 0x74, 0x79, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf9, - 0x92, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x75, 0x36, - 0x9c, 0x6e, 0x1a, 0x1b, 0xeb, 0x5f, 0xfb, 0x89, - 0xdb, 0x0b, 0xec, 0xa5, 0x95, 0x7a, 0x44, 0xf6, - 0x1c, 0x47, 0x90, 0x53, 0x43, 0x18, 0xf3, 0xe7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x6d, - 0x89, 0x95, 0x83, 0xaa, 0x5e, 0x7f, 0xff, 0x39, - 0xb3, 0x47, 0x36, 0xe4, 0x37, 0x7e, 0x05, 0x1b, - 0x85, 0xe3, 0xb8, 0x98, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - size_t data_size = 110; - -#elif UNITTEST_BT_PERSISTENT_STORAGE_VERSION == 2 - //0000 01 00 69 50 68 6f 6e 65 20 4d 61 72 74 79 00 00 ..iPhone Marty.. //0010 00 00 00 00 00 00 90 36 9c 6e 1a 1b eb 5f fb 89 .......6.n..._.. //0020 db 0b ec a5 95 ab 92 8a aa f6 1c 47 90 53 43 ff ...........G.SC. @@ -1261,7 +644,7 @@ void test_bluetooth_persistent_storage__ble_serialized_data(void) { //0040 f3 e7 44 f6 1c 47 90 53 43 18 d1 6d 89 95 83 aa ..D..G.SC..m.... //0050 5e 7f ff 39 b3 47 36 e4 37 7e 05 1b 85 e3 b8 98 ^..9.G6.7~...... //0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - //0070 00 00 17 ... ... + //0070 00 00 17 ... const uint8_t expected_raw_data[] = { 0x01, 0x00, 0x69, 0x50, 0x68, 0x6f, 0x6e, 0x65, @@ -1282,10 +665,6 @@ void test_bluetooth_persistent_storage__ble_serialized_data(void) { }; size_t data_size = 115; -#else -# error "Unknown version!" -#endif - const SMPairingInfo pairing_info = { .local_encryption_info = { .ltk = { @@ -1346,54 +725,3 @@ void test_bluetooth_persistent_storage__ble_serialized_data(void) { cl_assert_equal_m(expected_raw_data, data, sizeof(expected_raw_data)); } -void test_bluetooth_persistent_storage__v1_bt_classic(void) { -#if UNITTEST_BT_PERSISTENT_STORAGE_VERSION != 1 - // We only care about BT Classic in v1. - return; -#endif - - BTDeviceAddress address = { - .octets = {0x5, 0x1b, 0x85, 0xe3, 0xb8, 0x98}, - }; - SM128BitKey link_key = { - .data = { - 0xb5, 0xa8, 0x09, 0xcc, 0x1a, 0xdf, 0xfa, 0x8e, - 0x96, 0x87, 0x76, 0xac, 0xcf, 0xb8, 0x15, 0x12, - }, - }; - uint8_t platform_bits = 0x01; - BTBondingID key = bt_persistent_storage_store_bt_classic_pairing(&address, &link_key, - "iPhone Marty", &platform_bits); - cl_assert(key != BT_BONDING_ID_INVALID); - size_t v1_size = 110; - uint8_t v1_data[v1_size]; - memset(v1_data, 0, sizeof(v1_data)); - int data_len = bt_persistent_storage_get_raw_data(&key, sizeof(key), v1_data, v1_size); - cl_assert_equal_i(data_len, v1_size); - - // 00000000: 0005 1b85 e3b8 98b5 a809 cc1a dffa 8e96 ................ - // 00000010: 8776 accf b815 1269 5068 6f6e 6520 4d61 .v.....iPhone Ma - // 00000020: 7274 7900 0000 0000 0000 0001 0000 0000 rty............. - // 00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ - // 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ - // 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ - // 00000060: 0000 0000 0000 0000 0000 0000 0000 .............. - - const uint8_t expected_raw_data[] = { - 0x00, 0x05, 0x1b, 0x85, 0xe3, 0xb8, 0x98, 0xb5, - 0xa8, 0x09, 0xcc, 0x1a, 0xdf, 0xfa, 0x8e, 0x96, - 0x87, 0x76, 0xac, 0xcf, 0xb8, 0x15, 0x12, 0x69, - 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x20, 0x4d, 0x61, - 0x72, 0x74, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - cl_assert_equal_m(expected_raw_data, v1_data, sizeof(expected_raw_data)); -} diff --git a/tests/fw/services/bluetooth/test_bluetooth_persistent_storage_prf.c b/tests/fw/services/bluetooth/test_bluetooth_persistent_storage_prf.c index ffd6aad400..0a74d6c055 100644 --- a/tests/fw/services/bluetooth/test_bluetooth_persistent_storage_prf.c +++ b/tests/fw/services/bluetooth/test_bluetooth_persistent_storage_prf.c @@ -1,30 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include #include -#include "services/common/analytics/analytics.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/analytics/analytics.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" -#include "services/common/system_task.h" -#include "flash_region/flash_region_s29vs.h" +#include "pbl/services/system_task.h" +#include "flash_region/flash_region.h" #include "util/size.h" typedef struct GAPLEConnection GAPLEConnection; @@ -48,16 +35,6 @@ typedef struct GAPLEConnection GAPLEConnection; #include "stubs_passert.h" #include "stubs_pebble_pairing_service.h" -bool bt_driver_supports_bt_classic(void) { - return true; -} - -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) { -} - -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) { -} - typedef bool (*BondingSyncFilterCb)(const BleBonding *bonding, void *ctx); const BleBonding *bonding_sync_find(BondingSyncFilterCb cb, void *ctx) { return NULL; @@ -88,10 +65,12 @@ void bt_driver_cb_pairing_confirm_handle_completed(const PairingUserConfirmation void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) { } -extern RegularTimerInfo *shared_prf_storage_get_writeback_timer(void); -static void prv_fire_writeback_timer(void) { - fake_regular_timer_trigger(shared_prf_storage_get_writeback_timer()); -} +// The v3 shared_prf_storage implementation (promoted to the sole implementation in commit +// 990d9a37d, which removed the old v2_sprf) writes to flash synchronously inside +// shared_prf_storage_store_*(), rather than deferring the write to a regular "writeback" timer as +// v2 did. There is therefore no shared_prf_storage_get_writeback_timer() to fire: the pairing data +// is already persisted by the time each store call returns, so the read-back assertions below +// exercise the same persistence contract directly. static int s_bonding_change_count; static BtPersistBondingOp s_bonding_change_ops[2]; @@ -109,10 +88,6 @@ static void prv_reset_change_op_tracking(void) { } } -void cc2564A_bad_le_connection_complete_handle(unsigned int stack_id, - const GAP_LE_Current_Connection_Parameters_t *params) { -} - void gap_le_connect_handle_bonding_change(BTBondingID bonding_id, BtPersistBondingOp op) { } @@ -187,9 +162,7 @@ void test_bluetooth_persistent_storage_prf__ble_store_and_get(void) { BTBondingID id_1 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id_1 != BT_BONDING_ID_INVALID); + false /* auto_accept_re_pairing */); cl_assert(id_1 != BT_BONDING_ID_INVALID); cl_assert_equal_i(s_bonding_change_count, 1); cl_assert_equal_i(s_bonding_change_ops[0], BtPersistBondingOpDidAdd); @@ -206,9 +179,7 @@ void test_bluetooth_persistent_storage_prf__ble_store_and_get(void) { prv_reset_change_op_tracking(); id_1 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id_1 != BT_BONDING_ID_INVALID); + false /* auto_accept_re_pairing */); cl_assert(id_1 != BT_BONDING_ID_INVALID); cl_assert_equal_i(s_bonding_change_count, 1); cl_assert_equal_i(s_bonding_change_ops[0], BtPersistBondingOpDidChange); @@ -235,9 +206,7 @@ void test_bluetooth_persistent_storage_prf__ble_store_and_get(void) { BTBondingID id_2 = bt_persistent_storage_store_ble_pairing(&pairing_2, true /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id_2 != BT_BONDING_ID_INVALID); + false /* auto_accept_re_pairing */); cl_assert(id_2 != BT_BONDING_ID_INVALID); cl_assert_equal_i(s_bonding_change_count, 2); cl_assert_equal_i(s_bonding_change_ops[0], BtPersistBondingOpWillDelete); cl_assert_equal_i(s_bonding_change_ops[1], BtPersistBondingOpDidAdd); @@ -271,9 +240,7 @@ void test_bluetooth_persistent_storage_prf__ble_store_and_get(void) { BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_2, false /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id_3 == BT_BONDING_ID_INVALID); + false /* auto_accept_re_pairing */); cl_assert(id_3 == BT_BONDING_ID_INVALID); cl_assert_equal_i(s_bonding_change_count, 0); // Read out the stored pairing (id_2 should still be stored) @@ -316,9 +283,7 @@ void test_bluetooth_persistent_storage_prf__get_ble_by_address(void) { BTBondingID id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); + false /* auto_accept_re_pairing */); cl_assert(id != BT_BONDING_ID_INVALID); // Read it back ret = bt_persistent_storage_get_ble_pairing_by_addr(&pairing.identity, &irk_out, NULL); @@ -361,9 +326,7 @@ void test_bluetooth_persistent_storage_prf__delete_ble_pairing_by_id(void) { bonding_sync_add_bonding(&ble_bonding); BTBondingID id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); + false /* auto_accept_re_pairing */); cl_assert(id != BT_BONDING_ID_INVALID); // Delete the Pairing bt_persistent_storage_delete_ble_pairing_by_id(id); @@ -375,331 +338,7 @@ void test_bluetooth_persistent_storage_prf__delete_ble_pairing_by_id(void) { // Add the pairing again id = bt_persistent_storage_store_ble_pairing(&pairing, true /* is_gateway */, NULL, false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); -} - - -// /////////////////////////////////////////////////////////////////////////////////////////////////// -// //! BT Classic Pairing Info - -void test_bluetooth_persistent_storage_prf__bt_classic_store_and_get(void) { - bool ret; - - // Output variables - BTDeviceAddress addr_out; - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_1 = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_1 = { - .data = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10} - }; - char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_1 = 0x11; - BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - prv_fire_writeback_timer(); - cl_assert(id_1 != BT_BONDING_ID_INVALID); - - // Read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_1, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_1, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_1, name_out); - cl_assert_equal_i(platform_bits_1, platform_bits_out); - - // Store another pairing - BTDeviceAddress addr_2 = {.octets = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26}}; - SM128BitKey link_key_2 = { - .data = { - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - }, - }; - char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2"; - uint8_t platform_bits_2 = 0x22; - BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2, - name_2, &platform_bits_2); - prv_fire_writeback_timer(); - cl_assert(id_2 != BT_BONDING_ID_INVALID); - - // Read it pairings back (purposefully using the wrong bonding ID cause it doesn't matter) - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_2, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_2, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_2, name_out); - cl_assert_equal_i(platform_bits_2, platform_bits_out); - - // Add a thrid pairing - BTDeviceAddress addr_3 = {.octets = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36}}; - SM128BitKey link_key_3 = { - .data = { - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - }, - }; - char name_3[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 3"; - uint8_t platform_bits_3 = 0x33; - BTBondingID id_3 = bt_persistent_storage_store_bt_classic_pairing(&addr_3, &link_key_3, - name_3, &platform_bits_3); - prv_fire_writeback_timer(); - cl_assert(id_3 != BT_BONDING_ID_INVALID); - - // Read all three pairings back (purposefully using the wrong bonding ID cause it doesn't matter) - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id_1, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(ret); - cl_assert_equal_m(&addr_3, &addr_out, sizeof(addr_out)); - cl_assert_equal_m(&link_key_3, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_3, name_out); - cl_assert_equal_i(platform_bits_3, platform_bits_out); -} - -void test_bluetooth_persistent_storage_prf__get_bt_classic_pairing_by_addr(void) { - // Output variables - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_in = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_in = { - .data = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - }, - }; - char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_in = 0x11; - - BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); - - // Read it back - BTBondingID id_out = bt_persistent_storage_get_bt_classic_pairing_by_addr(&addr_in, &link_key_out, - name_out, &platform_bits_out); - cl_assert_equal_i(id, id_out); - cl_assert_equal_m(&link_key_in, &link_key_out, sizeof(link_key_out)); - cl_assert_equal_s(name_in, name_out); - cl_assert_equal_i(platform_bits_in, platform_bits_out); -} - - -void test_bluetooth_persistent_storage_prf__delete_bt_classic_pairing_by_id(void) { - bool ret; - - // Output variables - BTDeviceAddress addr_out; - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_in = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_in = { - .data = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - }, - }; - char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_in = 0x11; - - BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); - - // Delete the Pairing - bt_persistent_storage_delete_bt_classic_pairing_by_id(id); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); - - // Add the pairing again - id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); - - // And delete is again - bt_persistent_storage_delete_bt_classic_pairing_by_id(id); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); -} - -void test_bluetooth_persistent_storage_prf__delete_bt_classic_pairing_by_addr(void) { - bool ret; - - // Output variables - BTDeviceAddress addr_out; - SM128BitKey link_key_out; - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - uint8_t platform_bits_out; - - // Store a new pairing - BTDeviceAddress addr_in = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_in = { - .data = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - }, - }; - char name_in[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_in = 0x11; - - BTBondingID id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); - - // Delete the Pairing - bt_persistent_storage_delete_bt_classic_pairing_by_addr(&addr_in); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); - - // Add the pairing again - id = bt_persistent_storage_store_bt_classic_pairing(&addr_in, &link_key_in, - name_in, &platform_bits_in); - prv_fire_writeback_timer(); - cl_assert(id != BT_BONDING_ID_INVALID); - - // And delete is again - bt_persistent_storage_delete_bt_classic_pairing_by_addr(&addr_in); - - // Try to read it back - ret = bt_persistent_storage_get_bt_classic_pairing_by_id(id, &addr_out, &link_key_out, - name_out, &platform_bits_out); - cl_assert(!ret); + false /* auto_accept_re_pairing */); cl_assert(id != BT_BONDING_ID_INVALID); } -// /////////////////////////////////////////////////////////////////////////////////////////////////// -// //! Local Device Info - -void test_bluetooth_persistent_storage_prf__test_active_gateway(void) { - bool ret; - - BtPersistBondingType type_out; - BTBondingID id_out; - - // Nothing is stored, so no active gateways yet - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Store a new BT Classic pairing - BTDeviceAddress addr_1 = {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_1 = { - .data = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - }, - }; - char name_1[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 1"; - uint8_t platform_bits_1 = 0x11; - - BTBondingID id_1 = bt_persistent_storage_store_bt_classic_pairing(&addr_1, &link_key_1, - name_1, &platform_bits_1); - prv_fire_writeback_timer(); - cl_assert(id_1 != BT_BONDING_ID_INVALID); - - // It should be the active gateway - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(ret); - cl_assert_equal_i(id_out, id_1); - cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic); - - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Store another BT Classic pairing - BTDeviceAddress addr_2 = {.octets = {0x22, 0x12, 0x13, 0x14, 0x15, 0x16}}; - SM128BitKey link_key_2 = { - .data = { - 0x22, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - }, - }; - char name_2[BT_DEVICE_NAME_BUFFER_SIZE] = "Device 2"; - uint8_t platform_bits_2 = 0x22; - BTBondingID id_2 = bt_persistent_storage_store_bt_classic_pairing(&addr_2, &link_key_2, - name_2, &platform_bits_2); - prv_fire_writeback_timer(); - cl_assert(id_2 != BT_BONDING_ID_INVALID); - - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(ret); - cl_assert_equal_i(type_out, BtPersistBondingTypeBTClassic); - - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Delete the pairing. - bt_persistent_storage_delete_bt_classic_pairing_by_id(id_2); - - // There should be no active gateway now - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(!ret); - - // Store a new BLE pairing - SMPairingInfo pairing_1 = (SMPairingInfo) { - .irk = (SMIdentityResolvingKey) { - .data= { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00 - }, - }, - .identity = (BTDeviceInternal) { - .address = (BTDeviceAddress) {.octets = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16}}, - .is_classic = false, - .is_random_address = false, - }, - .is_remote_identity_info_valid = true, - }; - BTBondingID id_3 = bt_persistent_storage_store_ble_pairing(&pairing_1, true /* is_gateway */, - NULL, - false /* requires_address_pinning */, - false /* auto_accept_re_pairing */); - prv_fire_writeback_timer(); - cl_assert(id_3 != BT_BONDING_ID_INVALID); - - // There should now be an active BLE gateway - ret = bt_persistent_storage_get_active_gateway(&id_out, &type_out); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_bt_classic_gateway_bonding(); - cl_assert(!ret); - ret = bt_persistent_storage_has_active_ble_gateway_bonding(); - cl_assert(ret); -} diff --git a/tests/fw/services/bluetooth/test_local_addr.c b/tests/fw/services/bluetooth/test_local_addr.c index cf18d14e2a..199f7e2480 100644 --- a/tests/fw/services/bluetooth/test_local_addr.c +++ b/tests/fw/services/bluetooth/test_local_addr.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/bluetooth/local_addr.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/bluetooth/local_addr.h" #include diff --git a/tests/fw/services/bluetooth/wscript b/tests/fw/services/bluetooth/wscript deleted file mode 100644 index 14016f63cc..0000000000 --- a/tests/fw/services/bluetooth/wscript +++ /dev/null @@ -1,60 +0,0 @@ -from waftools.pebble_test import clar - - -def _test_bluetooth_persistent_storage(bld, version=1): - test_name = "test_bluetooth_persistent_storage_v%u" % version - version_override_include = 'bluetooth_persistent_storage_v%u' % version - clar(bld, - sources_ant_glob=( - "src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c " - "src/fw/services/normal/settings/settings_file.c " - "src/fw/services/normal/settings/settings_raw_iter.c " - "src/fw/services/normal/filesystem/pfs.c " - "src/fw/services/normal/filesystem/flash_translation.c " - "src/fw/flash_region/flash_region.c " - "src/fw/flash_region/filesystem_regions.c " - "src/fw/util/crc8.c " - "src/fw/util/legacy_checksum.c " - "src/fw/system/hexdump.c " - "tests/fakes/fake_shared_prf_storage.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_spi_flash.c " - "tests/fakes/fake_rtc.c " - ), - test_sources_ant_glob="test_bluetooth_persistent_storage.c", - test_name=test_name, - override_includes=['dummy_board', version_override_include]) - - -def build(bld): - clar(bld, - sources_ant_glob=( - "src/fw/services/normal/bluetooth/ble_hrm.c " - "tests/fakes/fake_events.c " - ), - test_sources_ant_glob="test_ble_hrm.c", - defines=['CAPABILITY_HAS_BUILTIN_HRM=1']) - - clar(bld, - sources_ant_glob=( - "src/fw/services/common/bluetooth/local_addr.c" - ), - test_sources_ant_glob="test_local_addr.c") - - clar(bld, - sources_ant_glob=( - "src/fw/services/prf/bluetooth/bluetooth_persistent_storage.c " - "src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage.c " - "tests/fakes/fake_spi_flash.c " - "tests/fakes/fake_events.c " - ), - test_sources_ant_glob="test_bluetooth_persistent_storage_prf.c", - override_includes=['snowy_mfg_board']) - - # Run the bluetooth_persistent_storage.c unit tests - # for the v1 and v2 serialization formats: - _test_bluetooth_persistent_storage(bld, version=1) - _test_bluetooth_persistent_storage(bld, version=2) - - -# vim:filetype=python diff --git a/tests/fw/services/bluetooth/wscript_build b/tests/fw/services/bluetooth/wscript_build new file mode 100644 index 0000000000..d91bee9a80 --- /dev/null +++ b/tests/fw/services/bluetooth/wscript_build @@ -0,0 +1,55 @@ +from waftools.pebble_test import clar + + +def _test_bluetooth_persistent_storage(bld): + clar(bld, + sources_ant_glob=( + "src/fw/services/bluetooth/bluetooth_persistent_storage_normal.c " + "src/fw/services/settings/settings_file.c " + "src/fw/services/settings/settings_raw_iter.c " + "src/fw/services/filesystem/pfs.c " + "src/fw/services/filesystem/flash_translation.c " + "src/fw/flash_region/flash_region.c " + "src/fw/flash_region/filesystem_regions.c " + "src/fw/util/crc8.c " + "src/fw/util/legacy_checksum.c " + "src/fw/system/hexdump.c " + "tests/fakes/fake_shared_prf_storage.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_spi_flash.c " + "tests/fakes/fake_rtc.c " + ), + test_sources_ant_glob="test_bluetooth_persistent_storage.c", + override_includes=['dummy_board', 'bluetooth_persistent_storage_v2']) + +clar(bld, + sources_ant_glob=( + "src/fw/services/bluetooth/ble_hrm.c " + "tests/fakes/fake_events.c " + ), + test_sources_ant_glob="test_ble_hrm.c", + defines=['CONFIG_HRM']) + +clar(bld, + sources_ant_glob=( + "src/fw/services/bluetooth/local_addr.c" + ), + test_sources_ant_glob="test_local_addr.c") + +clar(bld, + sources_ant_glob=( + "src/fw/services/bluetooth/bluetooth_persistent_storage_prf.c " + "src/fw/services/shared_prf_storage/shared_prf_storage.c " + "tests/fakes/fake_flash_region.c " + "tests/fakes/fake_spi_flash.c " + "tests/fakes/fake_events.c " + ), + test_sources_ant_glob="test_bluetooth_persistent_storage_prf.c", + platforms=['obelix'], + override_includes=['shared_prf_storage_v3']) + +# Run the bluetooth_persistent_storage.c unit tests for the v2 serialization format. +# (The v1 format was removed.) +_test_bluetooth_persistent_storage(bld) + +# vim:filetype=python diff --git a/tests/fw/services/comm_session/test_default_kernel_receiver.c b/tests/fw/services/comm_session/test_default_kernel_receiver.c index fbfbf57dbe..a9c1478573 100644 --- a/tests/fw/services/comm_session/test_default_kernel_receiver.c +++ b/tests/fw/services/comm_session/test_default_kernel_receiver.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/comm_session/session_receive_router.h" -#include "services/common/system_task.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/session_receive_router.h" +#include "pbl/services/system_task.h" #include "clar.h" @@ -65,6 +52,13 @@ typedef struct { } ExpectedData; static ExpectedData s_expected_data; +static bool s_skip_data_assert; + +//! Record of data received by handlers, for verifying batched delivery order. +#define MAX_RECORDED_CALLS 16 +static uint8_t s_recorded_data[MAX_RECORDED_CALLS]; +static size_t s_recorded_lens[MAX_RECORDED_CALLS]; +static int s_recorded_count; static void prv_set_expected_data(void *data, size_t len) { s_expected_data.buf = data; @@ -72,6 +66,9 @@ static void prv_set_expected_data(void *data, size_t len) { } static void prv_assert_data_matches_expected(const void *data, size_t len) { + if (s_skip_data_assert) { + return; + } cl_assert_equal_i(len, s_expected_data.len); cl_assert(memcmp(s_expected_data.buf, data, len) == 0); } @@ -85,6 +82,11 @@ static void prv_assert_no_handler_calls(void) { static void prv_endpoint_handler_a( CommSession *session, const uint8_t *data, size_t length) { s_handler_call_count[HandlerA]++; + if (s_recorded_count < MAX_RECORDED_CALLS) { + s_recorded_data[s_recorded_count] = data[0]; + s_recorded_lens[s_recorded_count] = length; + s_recorded_count++; + } prv_assert_data_matches_expected(data, length); } @@ -121,6 +123,8 @@ static const PebbleProtocolEndpoint s_endpoints[NumHandlers] = { void test_default_kernel_receiver__cleanup(void) { memset(s_handler_call_count, 0x0, sizeof(s_handler_call_count)); s_kernel_main_schedule_count = 0; + s_skip_data_assert = false; + s_recorded_count = 0; } @@ -198,7 +202,8 @@ void test_default_kernel_receiver__prepare_write_finish_multiple_sessions(void) //! It's possible the same session can receiver multiple messages before any //! are processed on kernel BG. Make sure they do not interfere with one -//! another +//! another. With coalesced callbacks, a single system_task callback drains +//! all pending messages in order. void test_default_kernel_receiver__same_session_batched(void) { const int batch_num = 10; char data = 'a'; @@ -217,12 +222,17 @@ void test_default_kernel_receiver__same_session_batched(void) { prv_assert_no_handler_calls(); - char expected_data = 'a'; + // All 10 messages are coalesced into a single system_task callback. + // Skip per-handler expected_data assertion; verify order via recording. + s_skip_data_assert = true; + fake_system_task_callbacks_invoke_pending(); + cl_assert_equal_i(s_handler_call_count[HandlerA], batch_num); + + // Verify that handlers were called in the correct order with correct data + cl_assert_equal_i(s_recorded_count, batch_num); for (int i = 0; i < batch_num; i++) { - prv_set_expected_data(&expected_data, 1); - fake_system_task_callbacks_invoke(1); - cl_assert_equal_i(s_handler_call_count[HandlerA], i + 1); - expected_data += 1; + cl_assert_equal_i(s_recorded_data[i], 'a' + i); + cl_assert_equal_i(s_recorded_lens[i], 1); } cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0); diff --git a/tests/fw/services/comm_session/test_meta_endpoint.c b/tests/fw/services/comm_session/test_meta_endpoint.c index 8b182a7153..6f782ffc20 100644 --- a/tests/fw/services/comm_session/test_meta_endpoint.c +++ b/tests/fw/services/comm_session/test_meta_endpoint.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/comm_session/meta_endpoint.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/meta_endpoint.h" #include "clar.h" diff --git a/tests/fw/services/comm_session/test_session.c b/tests/fw/services/comm_session/test_session.c index 0a5d567606..4fefc3dd7a 100644 --- a/tests/fw/services/comm_session/test_session.c +++ b/tests/fw/services/comm_session/test_session.c @@ -1,26 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "BSCAPI.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "clar.h" #include "bluetooth/bt_driver_comm.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/comm_session/session_transport.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/comm_session/session_transport.h" #include "kernel/events.h" extern void comm_session_set_capabilities(CommSession *session, diff --git a/tests/fw/services/comm_session/test_session_receive_router.c b/tests/fw/services/comm_session/test_session_receive_router.c index bcb9df4a52..82ad402876 100644 --- a/tests/fw/services/comm_session/test_session_receive_router.c +++ b/tests/fw/services/comm_session/test_session_receive_router.c @@ -1,27 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "BSCAPI.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "clar.h" #include "kernel/events.h" -#include "services/common/comm_session/meta_endpoint.h" -#include "services/common/comm_session/session_receive_router.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/common/comm_session/session_transport.h" -#include "services/common/comm_session/test_endpoint_ids.h" +#include "pbl/services/comm_session/meta_endpoint.h" +#include "pbl/services/comm_session/session_receive_router.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/comm_session/session_transport.h" +#include "pbl/services/comm_session/test_endpoint_ids.h" #include "system/logging.h" // Stubs @@ -48,9 +34,6 @@ bool bt_driver_comm_is_current_task_send_next_task(void) { return false; } -void comm_session_analytics_inc_bytes_received(CommSession *session, uint16_t length) { -} - void comm_session_analytics_open_session(CommSession *session) { } @@ -197,7 +180,7 @@ static void prv_system_test_receiver_write(Receiver *receiver, memcpy(s_write_buffer + s_write_length, data, length); s_write_length += length; - PBL_LOG(LOG_LEVEL_DEBUG, "Wrote %zu bytes", length); + PBL_LOG_DBG("Wrote %zu bytes", length); } static void prv_system_test_receiver_finish(Receiver *receiver) { diff --git a/tests/fw/services/comm_session/test_session_remote_version.c b/tests/fw/services/comm_session/test_session_remote_version.c index 95f5303ded..f1ece0fc87 100644 --- a/tests/fw/services/comm_session/test_session_remote_version.c +++ b/tests/fw/services/comm_session/test_session_remote_version.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/bluetooth/bluetooth_persistent_storage.h" -#include "services/common/comm_session/session_internal.h" -#include "services/common/comm_session/session_remote_os.h" -#include "services/common/comm_session/session_remote_version.h" +#include "pbl/services/bluetooth/bluetooth_persistent_storage.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_remote_os.h" +#include "pbl/services/comm_session/session_remote_version.h" #include "util/net.h" static CommSession s_session; @@ -40,8 +27,6 @@ extern void session_remote_version_protocol_msg_callback(CommSession *session, #include "stubs_passert.h" #include "stubs_rtc.h" -void bt_driver_reconnect_notify_platform_bitfield(uint32_t p) {} - static bool s_session_is_system; bool comm_session_is_system(CommSession *session) { return s_session_is_system; @@ -69,11 +54,6 @@ void comm_session_set_capabilities(CommSession *session, CommSessionCapability c s_capability_flags = capability_flags; } -BTBondingID bt_persistent_storage_store_bt_classic_pairing(BTDeviceAddress *address, SM128BitKey *link_key, - char *name, uint8_t *platform_bits) { - return 1; -} - // Helpers //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/comm_session/test_session_send_buffer.c b/tests/fw/services/comm_session/test_session_send_buffer.c index cd96eda154..35fc3ff0aa 100644 --- a/tests/fw/services/comm_session/test_session_send_buffer.c +++ b/tests/fw/services/comm_session/test_session_send_buffer.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/comm_session/default_kernel_sender.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/comm_session/session_transport.h" -#include "services/common/comm_session/session_internal.h" -#include "services/common/comm_session/session_send_queue.h" -#include "services/common/comm_session/protocol.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/comm_session/default_kernel_sender.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/comm_session/session_transport.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_send_queue.h" +#include "pbl/services/comm_session/protocol.h" #include "util/net.h" #include "util/size.h" @@ -44,9 +31,6 @@ extern void comm_session_send_queue_cleanup(CommSession *session); #include "stubs_passert.h" #include "stubs_analytics.h" -void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length) { -} - // Fakes /////////////////////////////////////////////////////////// diff --git a/tests/fw/services/comm_session/test_session_send_queue.c b/tests/fw/services/comm_session/test_session_send_queue.c index 72a30ed15a..e3638c9f23 100644 --- a/tests/fw/services/comm_session/test_session_send_queue.c +++ b/tests/fw/services/comm_session/test_session_send_queue.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/comm_session/session_internal.h" -#include "services/common/comm_session/session_send_queue.h" +#include "pbl/services/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_send_queue.h" #include "util/math.h" extern void comm_session_send_queue_cleanup(CommSession *session); @@ -34,9 +21,6 @@ extern void comm_session_send_queue_cleanup(CommSession *session); static CommSession s_session; static CommSession *s_valid_session; -void comm_session_analytics_inc_bytes_sent(CommSession *session, uint16_t length) { -} - bool comm_session_is_valid(const CommSession *session) { if (!session) { return false; diff --git a/tests/fw/services/comm_session/wscript b/tests/fw/services/comm_session/wscript deleted file mode 100644 index f7629a0fb7..0000000000 --- a/tests/fw/services/comm_session/wscript +++ /dev/null @@ -1,64 +0,0 @@ -from waftools.pebble_test import clar - - -def build(bld): - clar(bld, - sources_ant_glob=( - "src/fw/services/common/comm_session/meta_endpoint.c " - "tests/fakes/fake_session.c " - ), - test_sources_ant_glob="test_meta_endpoint.c") - - clar(bld, - sources_ant_glob=( - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/services/common/comm_session/session.c " - "tests/fakes/fake_session_send_buffer.c" - ), - test_sources_ant_glob="test_session.c", - override_includes=['dummy_board']) - - clar(bld, - sources_ant_glob=( - "src/fw/services/common/comm_session/default_kernel_sender.c " - "src/fw/services/common/comm_session/session_send_queue.c " - "tests/fakes/fake_queue.c " - "tests/fakes/fake_rtc.c" - ), - test_sources_ant_glob="test_session_send_buffer.c") - - clar(bld, - sources_ant_glob=( - "src/fw/services/common/comm_session/session_send_queue.c " - ), - test_sources_ant_glob="test_session_send_queue.c") - - clar(bld, - sources_ant_glob=( - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "src/fw/services/common/comm_session/session.c " - "src/fw/services/common/comm_session/session_receive_router.c " - "tests/fakes/fake_session_send_buffer.c " - ), - test_sources_ant_glob="test_session_receive_router.c", - override_includes=['dummy_board', 'pp_endpoints']) - - clar(bld, - sources_ant_glob=( - "src/fw/services/common/comm_session/session_remote_version.c " - "tests/fakes/fake_events.c " - ), - test_sources_ant_glob="test_session_remote_version.c", - override_includes=['dummy_board']) - - clar(bld, - sources_ant_glob=( - "src/fw/services/common/comm_session/default_kernel_receiver.c " - ), - test_sources_ant_glob="test_default_kernel_receiver.c", - override_includes=['dummy_board']) - - -# vim:filetype=python diff --git a/tests/fw/services/comm_session/wscript_build b/tests/fw/services/comm_session/wscript_build new file mode 100644 index 0000000000..15c58fa1f1 --- /dev/null +++ b/tests/fw/services/comm_session/wscript_build @@ -0,0 +1,61 @@ +from waftools.pebble_test import clar + +clar(bld, + sources_ant_glob=( + "src/fw/services/comm_session/meta_endpoint.c " + "tests/fakes/fake_session.c " + ), + test_sources_ant_glob="test_meta_endpoint.c") + +clar(bld, + sources_ant_glob=( + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/services/comm_session/session.c " + "tests/fakes/fake_session_send_buffer.c" + ), + test_sources_ant_glob="test_session.c", + override_includes=['dummy_board']) + +clar(bld, + sources_ant_glob=( + "src/fw/services/comm_session/default_kernel_sender.c " + "src/fw/services/comm_session/session_send_queue.c " + "tests/fakes/fake_queue.c " + "tests/fakes/fake_rtc.c" + ), + test_sources_ant_glob="test_session_send_buffer.c") + +clar(bld, + sources_ant_glob=( + "src/fw/services/comm_session/session_send_queue.c " + ), + test_sources_ant_glob="test_session_send_queue.c") + +clar(bld, + sources_ant_glob=( + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "src/fw/services/comm_session/session.c " + "src/fw/services/comm_session/session_receive_router.c " + "tests/fakes/fake_session_send_buffer.c " + ), + test_sources_ant_glob="test_session_receive_router.c", + override_includes=['dummy_board', 'pp_endpoints']) + +clar(bld, + sources_ant_glob=( + "src/fw/services/comm_session/session_remote_version.c " + "tests/fakes/fake_events.c " + ), + test_sources_ant_glob="test_session_remote_version.c", + override_includes=['dummy_board']) + +clar(bld, + sources_ant_glob=( + "src/fw/services/comm_session/default_kernel_receiver.c " + ), + test_sources_ant_glob="test_default_kernel_receiver.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/services/compositor/test_compositor.c b/tests/fw/services/compositor/test_compositor.c index b7c547c32b..bd7f037451 100644 --- a/tests/fw/services/compositor/test_compositor.c +++ b/tests/fw/services/compositor/test_compositor.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -20,13 +7,12 @@ #include "applib/graphics/gtypes.h" #include "kernel/events.h" #include "kernel/ui/modals/modal_manager.h" -#include "services/common/compositor/compositor.h" +#include "pbl/services/compositor/compositor.h" // Stubs /////////////////////////////////////////////////////////// #include "stubs_compiled_with_legacy2_sdk.h" -#include "stubs_compositor_dma.h" #include "stubs_framebuffer.h" #include "stubs_gbitmap.h" #include "stubs_logging.h" @@ -76,7 +62,9 @@ bool animation_destroy(Animation *animation) { static int s_app_window_render_count; FrameBuffer* app_state_get_framebuffer(void) { - // Not a great proxy for app rendering but good enough. This gets called twice per app render. + // Not a great proxy for app rendering but good enough. The compositor fetches the app + // framebuffer once per app render (via compositor_get_app_framebuffer_as_bitmap() on the + // fast path where the app framebuffer matches the display), so this increments once per render. ++s_app_window_render_count; return compositor_get_framebuffer(); @@ -123,8 +111,16 @@ void framebuffer_clear(FrameBuffer* f) { void framebuffer_dirty_all(FrameBuffer *fb) { } +// Back the framebuffer bitmap with a real buffer sized for the display. compositor_render_app() +// memsets framebuffer_get_size_bytes() bytes through this bitmap's addr, so it must point at at +// least DISP_COLS * DISP_ROWS bytes of valid storage. +static uint8_t s_framebuffer_data[DISP_COLS * DISP_ROWS]; GBitmap framebuffer_get_as_bitmap(FrameBuffer *fb, const GSize *size) { - return (GBitmap) { }; + return (GBitmap) { + .addr = s_framebuffer_data, + .row_size_bytes = DISP_COLS, + .bounds = GRect(0, 0, size->w, size->h), + }; } void framebuffer_set_line(FrameBuffer* f, uint8_t y, const uint8_t* buffer) { @@ -296,15 +292,22 @@ void test_compositor__app_render_busy(void) { cl_assert_equal_i(s_count_display_update, 2); cl_assert_equal_i(s_count_animation_schedule, 0); - // transition is started from the deferred transition event + // The deferred transition now fires. Since this is an App-to-App transition, the compositor + // requests a fresh app render and schedules the transition animation rather than rendering the + // app framebuffer straight to the display (see commit 6dcc0ff6a, which added the missing + // prv_send_app_render_request() to the App-to-App branch to fix a frozen-screen bug). The + // deferred app render that follows is consumed by scheduling the animation, so no extra display + // update happens here. s_display_update_in_progress = false; prv_handle_display_update_complete(); - cl_assert_equal_i(s_count_display_update, 3); + cl_assert_equal_i(s_count_display_update, 2); + cl_assert_equal_i(s_count_animation_schedule, 1); - // subsequent app render starts animation + // A subsequent app render arrives mid-transition; the animation is already scheduled and the + // display is not updated again here. compositor_app_render_ready(); cl_assert_equal_i(s_count_animation_schedule, 1); - cl_assert_equal_i(s_count_display_update, 3); + cl_assert_equal_i(s_count_display_update, 2); } void test_compositor__modal_transition_cancels_deferred_app(void) { @@ -369,7 +372,7 @@ void test_compositor__app_not_ready_modal_push_pop(void) { // Now the app has rendered something and we should actually update the display. compositor_app_render_ready(); - cl_assert_equal_i(s_app_window_render_count, 2); + cl_assert_equal_i(s_app_window_render_count, 1); } void test_compositor__app_not_ready_cancelled_animation_deferred(void) { @@ -401,7 +404,7 @@ void test_compositor__app_not_ready_cancelled_animation_deferred(void) { // Now the app has rendered something and we should actually update the display. compositor_app_render_ready(); - cl_assert_equal_i(s_app_window_render_count, 2); + cl_assert_equal_i(s_app_window_render_count, 1); cl_assert_equal_i(s_count_animation_destroy, 1); } diff --git a/tests/fw/services/compositor/wscript b/tests/fw/services/compositor/wscript deleted file mode 100644 index 811f5cba15..0000000000 --- a/tests/fw/services/compositor/wscript +++ /dev/null @@ -1,15 +0,0 @@ -from waftools.pebble_test import clar - - -def build(ctx): - clar(ctx, - sources_ant_glob=( - "src/fw/applib/graphics/gtypes.c " - "src/fw/services/common/compositor/compositor.c " - "tests/stubs/stubs_modal_manager.c " - "tests/stubs/stubs_app_state.c " - ), - test_sources_ant_glob="test_compositor.c", - override_includes=['dummy_board']) - -# vim:filetype=python diff --git a/tests/fw/services/compositor/wscript_build b/tests/fw/services/compositor/wscript_build new file mode 100644 index 0000000000..b963c2dced --- /dev/null +++ b/tests/fw/services/compositor/wscript_build @@ -0,0 +1,13 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob=( + "src/fw/applib/graphics/gtypes.c " + "src/fw/services/compositor/compositor.c " + "tests/stubs/stubs_modal_manager.c " + "tests/stubs/stubs_app_state.c " + ), + test_sources_ant_glob="test_compositor.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/services/health/test_health.c b/tests/fw/services/health/test_health.c index 2d90d9b97d..303b9e3375 100644 --- a/tests/fw/services/health/test_health.c +++ b/tests/fw/services/health/test_health.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "applib/health_service_private.h" -#include "services/normal/activity/activity.h" +#include "pbl/services/activity/activity.h" #include "shell/prefs_syscalls.h" #include "util/size.h" @@ -33,6 +20,10 @@ #include "fake_rtc.h" #include "fake_pbl_std.h" +bool sys_activity_is_initialized(void) { + return true; +} + static HealthServiceState s_health_service; // ----------------------------------- @@ -767,7 +758,7 @@ void test_health__metric_hr_averaged_accessible(void) { test->in.time_start, test->in.time_end, test->in.scope); - PBL_LOG(LOG_LEVEL_DEBUG, "%s\nMetric: %d, start: %d, end: %d, Scope: %d", + PBL_LOG_DBG("%s\nMetric: %d, start: %d, end: %d, Scope: %d", test->desc, (int)test->in.metric, (int)test->in.time_start, (int)test->in.time_end, (int)test->in.scope); cl_assert_equal_i(accessible, tests[i].out.accessible); @@ -853,7 +844,7 @@ void test_health__metric_hr_aggregate_averaged_accessible(void) { test->in.time_end, test->in.aggregation, test->in.scope); - PBL_LOG(LOG_LEVEL_DEBUG, "%s\nMetric: %d, start: %d, end: %d, Aggregation: %d, Scope: %d", + PBL_LOG_DBG("%s\nMetric: %d, start: %d, end: %d, Aggregation: %d, Scope: %d", test->desc, (int)test->in.metric, (int)test->in.time_start, (int)test->in.time_end, (int)test->in.aggregation, (int)test->in.scope); cl_assert_equal_i(accessible, tests[i].out.accessible); @@ -1547,7 +1538,7 @@ void DISABLED_test_health__min_max_avg_full_days(void) { bool yesterday_was_weekend = (yesterday_day_in_week == Sunday) || (yesterday_day_in_week == Saturday); - PBL_LOG(LOG_LEVEL_DEBUG, "yesterday day in week: %d", yesterday_day_in_week); + PBL_LOG_DBG("yesterday day in week: %d", yesterday_day_in_week); // ---------------------------------------- // Let's fill in some known data for the daily totals and accumulate the stats @@ -1578,7 +1569,7 @@ void DISABLED_test_health__min_max_avg_full_days(void) { HealthValue value = 1000 + (i * 50); s_sys_activity_get_metric_values.out.history[i] = value; - PBL_LOG(LOG_LEVEL_DEBUG, "Day #%d, day_of_week: %d, value: %"PRIi32" ", i, day_in_week, + PBL_LOG_DBG("Day #%d, day_of_week: %d, value: %"PRIi32" ", i, day_in_week, value); // Day 0 is not included in the stats if (i == 0) { @@ -1599,7 +1590,7 @@ void DISABLED_test_health__min_max_avg_full_days(void) { // Update stats if (day_in_week == yesterday_day_in_week) { prv_update_stats(&weekly_stats, value); - PBL_LOG(LOG_LEVEL_DEBUG, "Updating weekly stats with %"PRIi32": sum: %"PRIi32", " + PBL_LOG_DBG("Updating weekly stats with %"PRIi32": sum: %"PRIi32", " "avg: %"PRIi32" ", value, weekly_stats.sum, weekly_stats.avg); } if (day_in_week == Sunday || day_in_week == Saturday) { @@ -1676,7 +1667,7 @@ void DISABLED_test_health__min_max_avg_full_days(void) { HealthValue result; result = health_service_aggregate_averaged(HealthMetricHeartRateBPM, time_start, time_end, agg, scope); - PBL_LOG(LOG_LEVEL_INFO, "Testing %-16s %-16s exp_value: %5"PRIi32", act_value: " + PBL_LOG_INFO("Testing %-16s %-16s exp_value: %5"PRIi32", act_value: " "%5"PRIi32" " , scope_str, agg_str, exp_value, result); if (scope != HealthServiceTimeScopeOnce) { @@ -1747,7 +1738,7 @@ void test_health__heart_rate_scope_once(void) { s_sys_activity_get_minute_history_values.stage = 0; if (agg == HealthAggregationMin) { - PBL_LOG(LOG_LEVEL_DEBUG, "foo"); + PBL_LOG_DBG("foo"); } HealthValue result = health_service_aggregate_averaged(HealthMetricHeartRateBPM, time_start, time_end, diff --git a/tests/fw/services/health/wscript b/tests/fw/services/health/wscript deleted file mode 100644 index 7c2565b678..0000000000 --- a/tests/fw/services/health/wscript +++ /dev/null @@ -1,15 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/health_service.c" \ - " src/fw/process_management/pebble_process_info.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/stats.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_health.c", - override_includes=['dummy_board'], - defines=["CAPABILITY_HAS_HEALTH_TRACKING=1"]) - diff --git a/tests/fw/services/health/wscript_build b/tests/fw/services/health/wscript_build new file mode 100644 index 0000000000..dff110b846 --- /dev/null +++ b/tests/fw/services/health/wscript_build @@ -0,0 +1,13 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/health_service.c" \ + " src/fw/process_management/pebble_process_info.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/stats.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_health.c", + override_includes=['dummy_board'], + defines=["CAPABILITY_HAS_HEALTH_TRACKING=1"]) diff --git a/tests/fw/services/notifications/test_ancs_filtering.c b/tests/fw/services/notifications/test_ancs_filtering.c index 5c3fad2614..092b582c7c 100644 --- a/tests/fw/services/notifications/test_ancs_filtering.c +++ b/tests/fw/services/notifications/test_ancs_filtering.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/ancs/ancs_filtering.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/timeline/attributes_actions.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/ancs/ancs_filtering.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/timeline/attributes_actions.h" // Stubs @@ -65,6 +52,16 @@ static uint8_t title_data[] = { }; static ANCSAttribute *s_title_attr = (ANCSAttribute *)&title_data; +static struct { + StringList list; + char data[1]; +} s_empty_filtering_rules = { + .list = { + .serialized_byte_length = 1, + }, + .data = { 0 }, +}; + status_t ios_notif_pref_db_store_prefs(const uint8_t *app_id, int length, AttributeList *attr_list, TimelineItemActionGroup *action_group) { s_performed_store = true; @@ -110,13 +107,15 @@ void test_ancs_filtering__record_app_no_action_needed(void) { // We don't need to insert anything iOSNotifPrefs prefs = { .attr_list = { - .num_attributes = 5, + .num_attributes = 7, .attributes = (Attribute[]) { { .id = AttributeIdTitle, .cstring = "Title" }, { .id = AttributeIdBody, .cstring = "Body" }, { .id = AttributeIdAppName, .cstring = "Awesome" }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_Always }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, }, }, }; @@ -131,10 +130,12 @@ void test_ancs_filtering__record_app_no_prefs_yet(void) { iOSNotifPrefs *existing_prefs = NULL; AttributeList attr_list = { - .num_attributes = 3, + .num_attributes = 5, .attributes = (Attribute[]) { { .id = AttributeIdAppName, .cstring = "Awesome" }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, } }; @@ -166,12 +167,14 @@ void test_ancs_filtering__record_app_existing_mute(void) { iOSNotifPrefs *existing_prefs = &prefs; AttributeList expected_attributes = { - .num_attributes = 5, + .num_attributes = 7, .attributes = (Attribute[]) { { .id = AttributeIdTitle, .cstring = "Title" }, { .id = AttributeIdBody, .cstring = "Body" }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_Always }, { .id = AttributeIdAppName, .cstring = "Awesome" }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, } }; @@ -200,10 +203,12 @@ void test_ancs_filtering__record_app_existing_display_name(void) { iOSNotifPrefs *existing_prefs = &prefs; AttributeList expected_attributes = { - .num_attributes = 3, + .num_attributes = 5, .attributes = (Attribute[]) { { .id = AttributeIdAppName, .cstring = "Awesome" }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, } }; @@ -224,10 +229,12 @@ void test_ancs_filtering__record_app_update_timestamp(void) { // notification from this source iOSNotifPrefs prefs = { .attr_list = { - .num_attributes = 3, + .num_attributes = 5, .attributes = (Attribute[]) { { .id = AttributeIdAppName, .cstring = "Awesome" }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, }, }, @@ -240,10 +247,12 @@ void test_ancs_filtering__record_app_update_timestamp(void) { s_now += 2; AttributeList expected_attributes = { - .num_attributes = 3, + .num_attributes = 5, .attributes = (Attribute[]) { { .id = AttributeIdAppName, .cstring = "Awesome" }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, } }; @@ -308,15 +317,76 @@ void test_ancs_filtering__should_ignore_because_muted(void) { cl_assert(ancs_filtering_is_muted(&mute_weekdays)); } +void test_ancs_filtering__record_app_existing_mute_expiration(void) { + // We already have a mute expiration attribute, make sure it doesn't change + iOSNotifPrefs prefs = { + .attr_list = { + .num_attributes = 4, + .attributes = (Attribute[]) { + { .id = AttributeIdTitle, .cstring = "Title" }, + { .id = AttributeIdBody, .cstring = "Body" }, + { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 12345678 }, + }, + }, + }; + iOSNotifPrefs *existing_prefs = &prefs; + + AttributeList expected_attributes = { + .num_attributes = 7, + .attributes = (Attribute[]) { + { .id = AttributeIdTitle, .cstring = "Title" }, + { .id = AttributeIdBody, .cstring = "Body" }, + { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 12345678 }, + { .id = AttributeIdAppName, .cstring = "Awesome" }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, + { .id = AttributeIdLastUpdated, .uint32 = s_now }, + } + }; + s_expected_attributes = &expected_attributes; + + ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr); + cl_assert(s_performed_store); + + // Make sure the our existing prefs got updated + iOSNotifPrefs expected_prefs = { + .attr_list = expected_attributes, + }; + prv_compare_notif_prefs(existing_prefs, &expected_prefs); +} + +void test_ancs_filtering__should_ignore_because_timed_muted(void) { + iOSNotifPrefs muted = { + .attr_list = { + .num_attributes = 1, + .attributes = (Attribute[]) { + { .id = AttributeIdMuteExpiration, .uint32 = 2000 }, + } + } + }; + + s_now = 1000; + cl_assert(ancs_filtering_is_muted(&muted)); + + s_now = 2000; + cl_assert(!ancs_filtering_is_muted(&muted)); + + s_now = 3000; + cl_assert(!ancs_filtering_is_muted(&muted)); +} + void test_ancs_filtering__record_app_no_display_name(void) { iOSNotifPrefs *existing_prefs = NULL; // No display name so we expect the app name to be the title AttributeList expected_attributes = { - .num_attributes = 3, + .num_attributes = 5, .attributes = (Attribute[]) { { .id = AttributeIdAppName, .cstring = "Apple Pay = :(" }, { .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None }, + { .id = AttributeIdMuteExpiration, .uint32 = 0 }, + { .id = AttributeIdNotificationFilteringRules, .string_list = &s_empty_filtering_rules.list }, { .id = AttributeIdLastUpdated, .uint32 = s_now }, } }; @@ -331,3 +401,119 @@ void test_ancs_filtering__record_app_no_display_name(void) { }; prv_compare_notif_prefs(existing_prefs, &expected_prefs); } + +void test_ancs_filtering__matches_text_rule_body_case_insensitive(void) { + static uint8_t body_data[] = { + 0x03, // id + 0x12, 0x00, // length + 'M', 'e', 'e', 't', 'i', 'n', 'g', ' ', 'i', 's', ' ', 'S', 'P', 'A', 'M', ' ', 'n', 'o' + }; + ANCSAttribute *body_attr = (ANCSAttribute *)&body_data; + + struct { + StringList list; + char data[9]; + } filtering_rules = { + .list = { + .serialized_byte_length = 9, + }, + // count=1, type=text, field=body, case=insensitive, pattern="spam\0" + .data = { 0x01, 0x00, 0x02, 0x00, 's', 'p', 'a', 'm', '\0' }, + }; + + iOSNotifPrefs prefs = { + .attr_list = { + .num_attributes = 1, + .attributes = (Attribute[]) { + { .id = AttributeIdNotificationFilteringRules, .string_list = &filtering_rules.list }, + }, + }, + }; + + cl_assert(ancs_filtering_matches_rules(&prefs, s_title_attr, NULL, body_attr)); +} + +void test_ancs_filtering__matches_text_rule_title_case_sensitive(void) { + struct { + StringList list; + char data[9]; + } filtering_rules = { + .list = { + .serialized_byte_length = 9, + }, + // count=1, type=text, field=title, case=sensitive, pattern="Apple\0" + .data = { 0x01, 0x00, 0x01, 0x01, 'A', 'p', 'p', 'l', 'e' }, + }; + filtering_rules.data[8] = '\0'; + + iOSNotifPrefs prefs = { + .attr_list = { + .num_attributes = 1, + .attributes = (Attribute[]) { + { .id = AttributeIdNotificationFilteringRules, .string_list = &filtering_rules.list }, + }, + }, + }; + + cl_assert(ancs_filtering_matches_rules(&prefs, s_title_attr, NULL, NULL)); +} + +void test_ancs_filtering__does_not_match_regex_rule(void) { + struct { + StringList list; + char data[9]; + } filtering_rules = { + .list = { + .serialized_byte_length = 9, + }, + // count=1, type=regex, field=any, case=insensitive, pattern=".*\0" + .data = { 0x01, 0x01, 0x00, 0x00, '.', '*', '\0', 0x00, 0x00 }, + }; + filtering_rules.list.serialized_byte_length = 7; + + iOSNotifPrefs prefs = { + .attr_list = { + .num_attributes = 1, + .attributes = (Attribute[]) { + { .id = AttributeIdNotificationFilteringRules, .string_list = &filtering_rules.list }, + }, + }, + }; + + cl_assert(!ancs_filtering_matches_rules(&prefs, s_title_attr, NULL, NULL)); +} + +void test_ancs_filtering__matches_text_rule_title_via_subtitle(void) { + static uint8_t subtitle_data[] = { + 0x02, // id + 0x0B, 0x00, // length + 'S', 'o', 'm', 'e', ' ', 'S', 'e', 'r', 'v', 'e', 'r' // Value + }; + ANCSAttribute *subtitle_attr = (ANCSAttribute *)&subtitle_data; + + struct { + StringList list; + char data[11]; + } filtering_rules = { + .list = { + .serialized_byte_length = 11, + }, + // count=1, type=text, field=title, case=insensitive, pattern="server\0" + .data = { 0x01, 0x00, 0x01, 0x00, 's', 'e', 'r', 'v', 'e', 'r', '\0' }, + }; + + iOSNotifPrefs prefs = { + .attr_list = { + .num_attributes = 1, + .attributes = (Attribute[]) { + { .id = AttributeIdNotificationFilteringRules, .string_list = &filtering_rules.list }, + }, + }, + }; + + // "server" is in the subtitle, not the title: a title rule must match it. + cl_assert(ancs_filtering_matches_rules(&prefs, s_title_attr, subtitle_attr, NULL)); + // A body rule must not consider the subtitle. + filtering_rules.data[2] = 0x02; + cl_assert(!ancs_filtering_matches_rules(&prefs, s_title_attr, subtitle_attr, NULL)); +} diff --git a/tests/fw/services/notifications/test_ancs_notifications.c b/tests/fw/services/notifications/test_ancs_notifications.c index f4c4d87f1a..324b7608d6 100644 --- a/tests/fw/services/notifications/test_ancs_notifications.c +++ b/tests/fw/services/notifications/test_ancs_notifications.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/notifications/alerts_preferences.h" -#include "services/normal/notifications/ancs/ancs_notifications.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" +#include "pbl/services/notifications/alerts_preferences.h" +#include "pbl/services/notifications/ancs/ancs_notifications.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" // Stubs @@ -45,7 +32,7 @@ void ios_notif_pref_db_free_prefs(iOSNotifPrefs *prefs) { return; } -void ancs_filtering_record_app(iOSNotifPrefs *app_notif_prefs, +void ancs_filtering_record_app(iOSNotifPrefs **app_notif_prefs, const ANCSAttribute *app_id, const ANCSAttribute *display_name, const ANCSAttribute *title) { @@ -55,6 +42,13 @@ bool ancs_filtering_is_muted(const iOSNotifPrefs *app_notif_prefs) { return false; } +bool ancs_filtering_matches_rules(const iOSNotifPrefs *app_notif_prefs, + const ANCSAttribute *title, + const ANCSAttribute *subtitle, + const ANCSAttribute *body) { + return false; +} + // Fakes //////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/notifications/test_ancs_notifications_util.c b/tests/fw/services/notifications/test_ancs_notifications_util.c index a9f3bb568a..9f04f423b0 100644 --- a/tests/fw/services/notifications/test_ancs_notifications_util.c +++ b/tests/fw/services/notifications/test_ancs_notifications_util.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/notifications/ancs/ancs_notifications_util.h" +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" // Stubs //////////////////////////////////////////////////////////////// @@ -57,6 +44,7 @@ void test_ancs_notifications_util__parse_timestamp(void) { .tm_mon = 3, // Apr .tm_year = 2015 - 1900, .tm_isdst = 1, + .tm_gmtoff = SECONDS_PER_HOUR, // DST offset (base 0 + 1h DST) }; // DST info for US/Canada 2015 diff --git a/tests/fw/services/notifications/test_nexmo.c b/tests/fw/services/notifications/test_nexmo.c index fa98d8ebd7..4e7c7ca33e 100644 --- a/tests/fw/services/notifications/test_nexmo.c +++ b/tests/fw/services/notifications/test_nexmo.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/notifications/ancs/nexmo.h" -#include "services/normal/notifications/ancs/ancs_notifications_util.h" -#include "services/normal/timeline/attributes_actions.h" +#include "pbl/services/notifications/ancs/nexmo.h" +#include "pbl/services/notifications/ancs/ancs_notifications_util.h" +#include "pbl/services/timeline/attributes_actions.h" // Stubs //////////////////////////////////////////////////////////////// diff --git a/tests/fw/services/notifications/test_notification_storage.c b/tests/fw/services/notifications/test_notification_storage.c index 7016f42b62..8e1e658e28 100644 --- a/tests/fw/services/notifications/test_notification_storage.c +++ b/tests/fw/services/notifications/test_notification_storage.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/notifications/notification_storage.h" -#include "services/normal/notifications/notification_storage_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/notification_storage.h" +#include "pbl/services/notifications/notification_storage_private.h" #include "flash_region/flash_region.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "util/size.h" #include "clar.h" diff --git a/tests/fw/services/notifications/wscript b/tests/fw/services/notifications/wscript deleted file mode 100644 index 70683bafe3..0000000000 --- a/tests/fw/services/notifications/wscript +++ /dev/null @@ -1,73 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/fw/services/normal/notifications/notification_storage.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/util/stringlist.c" \ - " src/fw/util/time/time.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_notification_storage.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/notifications/ancs/ancs_notifications.c " \ - " src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \ - " src/fw/services/normal/notifications/ancs/ancs_item.c " \ - " src/fw/services/normal/notifications/ancs/ancs_phone_call.c " \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/phone_call_util.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/util/pstring.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_events.c", - test_sources_ant_glob = "test_ancs_notifications.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \ - " src/fw/util/time/mktime.c " \ - " src/fw/util/pstring.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_ancs_notifications_util.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/notifications/ancs/ancs_filtering.c " \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/util/pstring.c" \ - " src/fw/util/time/time.c", - test_sources_ant_glob = "test_ancs_filtering.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/notifications/ancs/nexmo.c " \ - " src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/util/pstring.c" \ - " src/fw/util/time/time.c " \ - " tests/fakes/fake_rtc.c ", - test_sources_ant_glob = "test_nexmo.c") -# vim:filetype=python diff --git a/tests/fw/services/notifications/wscript_build b/tests/fw/services/notifications/wscript_build new file mode 100644 index 0000000000..ae7daa7877 --- /dev/null +++ b/tests/fw/services/notifications/wscript_build @@ -0,0 +1,73 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/services/notifications/notification_storage.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/util/stringlist.c" \ + " src/fw/util/time/time.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_notification_storage.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/notifications/ancs/ancs_notifications.c " \ + " src/fw/services/notifications/ancs/ancs_notifications_util.c " \ + " src/fw/services/notifications/ancs/ancs_item.c " \ + " src/fw/services/notifications/ancs/ancs_phone_call.c " \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/phone_call/util.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/util/pstring.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_events.c", + test_sources_ant_glob = "test_ancs_notifications.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/notifications/ancs/ancs_notifications_util.c " \ + " src/fw/util/time/mktime.c " \ + " src/fw/util/pstring.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_ancs_notifications_util.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/notifications/ancs/ancs_filtering.c " \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/util/pstring.c" \ + " src/fw/util/time/time.c", + test_sources_ant_glob = "test_ancs_filtering.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/notifications/ancs/nexmo.c " \ + " src/fw/services/notifications/ancs/ancs_notifications_util.c " \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/util/pstring.c" \ + " src/fw/util/time/time.c " \ + " tests/fakes/fake_rtc.c ", + test_sources_ant_glob = "test_nexmo.c") + +# vim:filetype=python diff --git a/tests/fw/services/protobuf_log/protobuf_log_test_helpers.c b/tests/fw/services/protobuf_log/protobuf_log_test_helpers.c index a7a164cde6..f751f3abd9 100644 --- a/tests/fw/services/protobuf_log/protobuf_log_test_helpers.c +++ b/tests/fw/services/protobuf_log/protobuf_log_test_helpers.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "protobuf_log_test_helpers.h" -#include "services/normal/protobuf_log/protobuf_log_private.h" +#include "pbl/services/protobuf_log/protobuf_log_private.h" #include #include diff --git a/tests/fw/services/protobuf_log/protobuf_log_test_helpers.h b/tests/fw/services/protobuf_log/protobuf_log_test_helpers.h index 8b10569d84..c13b56b63b 100644 --- a/tests/fw/services/protobuf_log/protobuf_log_test_helpers.h +++ b/tests/fw/services/protobuf_log/protobuf_log_test_helpers.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fw/services/protobuf_log/test_protobuf_log.c b/tests/fw/services/protobuf_log/test_protobuf_log.c index 7ae7551bb0..9e5a7e8f29 100644 --- a/tests/fw/services/protobuf_log/test_protobuf_log.c +++ b/tests/fw/services/protobuf_log/test_protobuf_log.c @@ -1,33 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "protobuf_log_test_helpers.h" -#include "services/normal/protobuf_log/protobuf_log.h" -#include "services/normal/protobuf_log/protobuf_log_private.h" -#include "services/normal/protobuf_log/protobuf_log_test.h" -#include "services/normal/protobuf_log/protobuf_log_hr.h" -#include "services/normal/protobuf_log/protobuf_log_activity_sessions.h" -#include "services/normal/activity/activity.h" +#include "pbl/services/protobuf_log/protobuf_log.h" +#include "pbl/services/protobuf_log/protobuf_log_private.h" +#include "pbl/services/protobuf_log/protobuf_log_test.h" +#include "pbl/services/protobuf_log/protobuf_log_hr.h" +#include "pbl/services/protobuf_log/protobuf_log_activity_sessions.h" +#include "pbl/services/activity/activity.h" #include "applib/data_logging.h" #include "drivers/rtc.h" -#include "services/normal/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/data_logging_service.h" #include "system/logging.h" #include "util/attributes.h" #include "util/size.h" @@ -48,7 +35,7 @@ #define WRITE_TO_FILE 0 #define LOG(fmt, args...) \ - PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args) + PBL_LOG_DBG(fmt, ## args) extern uint32_t prv_hr_quality_int(HRMQuality quality); @@ -122,7 +109,7 @@ const char* mfg_get_serial_number(void) { #define GIT_TAG_V_MAJOR 4 #define GIT_TAG_V_MINOR 17 -#define GIT_TAG_V_PATCH "ROBERT-mfg4-6-gb91951a" +#define GIT_TAG_V_PATCH "v4.17-6-gb91951a" void version_get_major_minor_patch(unsigned int *major, unsigned int *minor, const char **patch_ptr) { *major = GIT_TAG_V_MAJOR; diff --git a/tests/fw/services/protobuf_log/wscript b/tests/fw/services/protobuf_log/wscript deleted file mode 100644 index 6114f0b4dc..0000000000 --- a/tests/fw/services/protobuf_log/wscript +++ /dev/null @@ -1,22 +0,0 @@ -from waftools.pebble_test import clar - -import generate_c_byte_array -import generate_timezone_data - -def build(ctx): - clar(ctx, - sources_ant_glob = ( - " src/fw/services/normal/protobuf_log/protobuf_log.c" - " src/fw/services/normal/protobuf_log/protobuf_log_hr.c" - " src/fw/services/normal/protobuf_log/protobuf_log_activity_sessions.c" - " src/fw/services/normal/protobuf_log/protobuf_log_test.c" - " src/fw/services/normal/protobuf_log/protobuf_log_util.c" - " src/fw/util/time/mktime.c" - " src/fw/util/time/time.c" - " tests/fakes/fake_rtc.c" - " tests/fw/services/protobuf_log/protobuf_log_test_helpers.c" - ), - test_sources_ant_glob="test_protobuf_log.c", - use=['nanopb', 'proto_schemas']) - -# vim:filetype=python diff --git a/tests/fw/services/protobuf_log/wscript_build b/tests/fw/services/protobuf_log/wscript_build new file mode 100644 index 0000000000..c19e188fd0 --- /dev/null +++ b/tests/fw/services/protobuf_log/wscript_build @@ -0,0 +1,21 @@ +from waftools.pebble_test import clar + +import generate_c_byte_array +import generate_timezone_data + +clar(ctx, + sources_ant_glob = ( + " src/fw/services/protobuf_log/protobuf_log.c" + " src/fw/services/protobuf_log/protobuf_log_hr.c" + " src/fw/services/protobuf_log/protobuf_log_activity_sessions.c" + " src/fw/services/protobuf_log/protobuf_log_test.c" + " src/fw/services/protobuf_log/protobuf_log_util.c" + " src/fw/util/time/mktime.c" + " src/fw/util/time/time.c" + " tests/fakes/fake_rtc.c" + " tests/fw/services/protobuf_log/protobuf_log_test_helpers.c" + ), + test_sources_ant_glob="test_protobuf_log.c", + use=['nanopb', 'proto_schemas']) + +# vim:filetype=python diff --git a/tests/fw/services/settings/test_settings_file.c b/tests/fw/services/settings/test_settings_file.c index bce297739a..2a01098516 100644 --- a/tests/fw/services/settings/test_settings_file.c +++ b/tests/fw/services/settings/test_settings_file.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/settings/settings_file.h" -#include "services/normal/settings/settings_raw_iter.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/settings/settings_raw_iter.h" #include "system/hexdump.h" #include "clar.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" #include "flash_region/flash_region.h" #include @@ -53,7 +40,9 @@ void test_settings_file__initialize(void) { pfs_init(false); } -void test_settings_file__cleanup(void) {} +void test_settings_file__cleanup(void) { + stub_pbl_malloc_set_kernel_malloc_should_fail(false); +} #include @@ -624,6 +613,274 @@ void test_settings_file__reallocate_larger(void) { verify(&file, key, key_len, val, val_len); } +// Test that a growable file automatically grows when writes exceed the initial allocation +// but stay within the enforcement cap. +void test_settings_file__growable_auto_growth(void) { + printf("\nTesting growable file auto-growth...\n"); + + // PFS allocates in 4096-byte pages, so the initial_alloc must span at least + // one page. Use an initial_alloc that results in one PFS page and a max_cap + // that allows multiple pages. Write enough large records to exceed one page. + const int initial_alloc = 2048; + const int max_cap = 32768; + SettingsFile file; + cl_must_pass(settings_file_open_growable(&file, "tg", max_cap, initial_alloc)); + + int initial_file_size = pfs_get_file_size(file.iter.fd); + printf("Initial file size: %d, max_space_total: %d\n", + initial_file_size, file.max_space_total); + + // Write large records to fill and exceed the initial allocation. + // Each record: 8 (header) + 4 (key) + 128 (val) = 140 bytes. + // One PFS page holds ~4000 usable bytes, so ~28 records fills it. + uint8_t key[5]; + int key_len = 4; + uint8_t val[128]; + int val_len = sizeof(val); + memset(val, 0xAB, val_len); + // 8114 / 140 ≈ 57 records to fill the initial allocation. Write 65 to force growth. + int num_records = 65; + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; // Make each value unique + cl_must_pass(settings_file_set(&file, key, key_len, val, val_len)); + } + + int grown_file_size = pfs_get_file_size(file.iter.fd); + printf("Grown file size: %d\n", grown_file_size); + cl_assert(grown_file_size > initial_file_size); + + // Verify all data survived the growth + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + verify(&file, key, key_len, val, val_len); + } + + settings_file_close(&file); +} + +// Test that a growable file returns E_OUT_OF_STORAGE when the enforcement cap is hit. +void test_settings_file__growable_enforces_cap(void) { + printf("\nTesting growable file cap enforcement...\n"); + + // Use a cap of 4096 (one PFS page worth) and initial_alloc of 2048. + // Write large records to hit the cap. + const int initial_alloc = 2048; + const int max_cap = 4096; + SettingsFile file; + cl_must_pass(settings_file_open_growable(&file, "tc", max_cap, initial_alloc)); + + uint8_t key[5]; + int key_len = 4; + uint8_t val[128]; + int val_len = sizeof(val); + memset(val, 0xCD, val_len); + // Each record = 8 + 4 + 128 = 140 bytes. max_cap of 4096 holds ~28 records. + int i; + for (i = 0; i < 100; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + status_t status = settings_file_set(&file, key, key_len, val, val_len); + if (status == E_OUT_OF_STORAGE) { + printf("Hit storage cap at record %d\n", i); + break; + } + cl_must_pass(status); + } + + // We should have hit the cap before writing 100 records + cl_assert(i < 100); + + // Verify the records that were written are still readable + for (int j = 0; j < i; j++) { + snprintf((char *)key, sizeof(key), "k%03d", j); + val[0] = (uint8_t)j; + verify(&file, key, key_len, val, val_len); + } + + settings_file_close(&file); +} + +// Test that closing and reopening a grown file preserves its size and data. +void test_settings_file__growable_reopen(void) { + printf("\nTesting growable file reopen after growth...\n"); + + const int initial_alloc = 2048; + const int max_cap = 32768; + SettingsFile file; + cl_must_pass(settings_file_open_growable(&file, "tr", max_cap, initial_alloc)); + + // Write enough large records to force growth past the initial page + uint8_t key[5]; + int key_len = 4; + uint8_t val[128]; + int val_len = sizeof(val); + memset(val, 0xEF, val_len); + int num_records = 65; + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + cl_must_pass(settings_file_set(&file, key, key_len, val, val_len)); + } + + int grown_file_size = pfs_get_file_size(file.iter.fd); + printf("Grown file size: %d\n", grown_file_size); + settings_file_close(&file); + + // Reopen with the same growable parameters + cl_must_pass(settings_file_open_growable(&file, "tr", max_cap, initial_alloc)); + + int reopened_file_size = pfs_get_file_size(file.iter.fd); + printf("Reopened file size: %d\n", reopened_file_size); + // The file should not have shrunk + cl_assert(reopened_file_size >= grown_file_size); + + // Verify all data survived the close/reopen + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + verify(&file, key, key_len, val, val_len); + } + + settings_file_close(&file); +} + +// Compaction shrinks a grown growable file back toward the initial allocation when most +// records have been deleted, so a brief storage burst doesn't permanently hold flash. +void test_settings_file__growable_shrinks_on_compact(void) { + printf("\nTesting growable file shrink on compact...\n"); + + const int initial_alloc = 2048; + const int max_cap = 32768; + SettingsFile file; + cl_must_pass(settings_file_open_growable(&file, "tk", max_cap, initial_alloc)); + + uint8_t key[5]; + int key_len = 4; + uint8_t val[128]; + int val_len = sizeof(val); + memset(val, 0x77, val_len); + + // Drive enough writes to push past several doublings. + const int num_records = 65; + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + cl_must_pass(settings_file_set(&file, key, key_len, val, val_len)); + } + + const int grown_file_size = pfs_get_file_size(file.iter.fd); + const int grown_alloc = file.alloc_used_space; + printf("Grown: file_size=%d alloc=%d\n", grown_file_size, grown_alloc); + cl_assert(grown_alloc > initial_alloc); + + // Delete almost everything. Leave one record so we still exercise the rewrite path. + for (int i = 0; i < num_records - 1; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + cl_must_pass(settings_file_delete(&file, key, key_len)); + } + + // Force compact. Should drop alloc to the smallest doubling that fits 2x used. + cl_must_pass(settings_file_compact(&file)); + + const int shrunk_file_size = pfs_get_file_size(file.iter.fd); + const int shrunk_alloc = file.alloc_used_space; + printf("Shrunk: file_size=%d alloc=%d\n", shrunk_file_size, shrunk_alloc); + cl_assert(shrunk_alloc < grown_alloc); + cl_assert(shrunk_file_size < grown_file_size); + + // The remaining record must still be readable after the shrink-rewrite. + snprintf((char *)key, sizeof(key), "k%03d", num_records - 1); + val[0] = (uint8_t)(num_records - 1); + verify(&file, key, key_len, val, val_len); + + settings_file_close(&file); +} + +// A non-growable file (settings_file_open with the legacy API) must not shrink during +// compaction. The pre-allocation guarantee is part of the contract. +void test_settings_file__non_growable_does_not_shrink_on_compact(void) { + printf("\nTesting non-growable file does not shrink on compact...\n"); + + const int max_used = 8192; + SettingsFile file; + cl_must_pass(settings_file_open(&file, "tn", max_used)); + + const int initial_alloc = file.alloc_used_space; + const int initial_file_size = pfs_get_file_size(file.iter.fd); + printf("Initial: file_size=%d alloc=%d\n", initial_file_size, initial_alloc); + + uint8_t key[5]; + int key_len = 4; + uint8_t val[32]; + int val_len = sizeof(val); + memset(val, 0x33, val_len); + + // Write a few small records, then delete all of them. + for (int i = 0; i < 10; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + cl_must_pass(settings_file_set(&file, key, key_len, val, val_len)); + } + for (int i = 0; i < 10; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + cl_must_pass(settings_file_delete(&file, key, key_len)); + } + cl_must_pass(settings_file_compact(&file)); + + const int post_alloc = file.alloc_used_space; + const int post_file_size = pfs_get_file_size(file.iter.fd); + printf("After compact: file_size=%d alloc=%d\n", post_file_size, post_alloc); + cl_assert_equal_i(post_alloc, initial_alloc); + cl_assert_equal_i(post_file_size, initial_file_size); + + settings_file_close(&file); +} + +// Migration: a file written by a prior firmware via settings_file_open with a small +// max_used_space must reopen as growable without losing data, and its physical allocation +// must be adopted (not silently shrunk to the new initial_alloc_size). +void test_settings_file__growable_migration_from_non_growable(void) { + printf("\nTesting migration from non-growable to growable...\n"); + + const int legacy_max = 6144; + uint8_t key[5]; + int key_len = 4; + uint8_t val[64]; + int val_len = sizeof(val); + memset(val, 0x5A, val_len); + + SettingsFile file; + cl_must_pass(settings_file_open(&file, "tm", legacy_max)); + int legacy_file_size = pfs_get_file_size(file.iter.fd); + printf("Legacy file size: %d\n", legacy_file_size); + + const int num_records = 20; + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + cl_must_pass(settings_file_set(&file, key, key_len, val, val_len)); + } + settings_file_close(&file); + + const int new_initial_alloc = 4096; + const int new_max = 1024 * 1024; + cl_must_pass(settings_file_open_growable(&file, "tm", new_max, new_initial_alloc)); + + int reopened_file_size = pfs_get_file_size(file.iter.fd); + printf("Reopened (as growable) file size: %d\n", reopened_file_size); + // The pre-allocated legacy file must not be shrunk on reopen. + cl_assert(reopened_file_size >= legacy_file_size); + + for (int i = 0; i < num_records; i++) { + snprintf((char *)key, sizeof(key), "k%03d", i); + val[0] = (uint8_t)i; + verify(&file, key, key_len, val, val_len); + } + + settings_file_close(&file); +} + // Test that we can start searching beginning at the record we previously found in a recent // call into settings file. This makes sure we don't start searching at the beginning of a file // each API call. @@ -681,3 +938,36 @@ void test_settings_file__iterator_wrapping(void) { after_count = settings_raw_iter_prv_get_num_record_searches(); cl_assert_equal_i(NUM_RECORDS - 1, after_count - before_count); } + +void test_settings_file__rewrite_filtered_oom(void) { + printf("\nTesting that compaction aborts cleanly when kernel_malloc fails...\n"); + SettingsFile file; + cl_must_pass(settings_file_open(&file, "test_file_rewrite_oom", 4096)); + uint8_t key[4]; + int key_len = sizeof(key) - 1; + memcpy(key, "key", sizeof(key)); + uint8_t val[4]; + int val_len = sizeof(val) - 1; + memcpy(val, "val", sizeof(val)); + set_and_verify(&file, key, key_len, val, val_len); + + // Compaction under OOM must fail without crashing and without touching + // the on-disk file. + stub_pbl_malloc_set_kernel_malloc_should_fail(true); + cl_assert_equal_i(E_OUT_OF_MEMORY, settings_file_compact(&file)); + stub_pbl_malloc_set_kernel_malloc_should_fail(false); + + // The file must still be open and intact. + verify(&file, key, key_len, val, val_len); + + // Once memory is available again, compaction must succeed. + cl_must_pass(settings_file_compact(&file)); + verify(&file, key, key_len, val, val_len); + + settings_file_close(&file); + + // And the data must survive a reopen. + cl_must_pass(settings_file_open(&file, "test_file_rewrite_oom", 4096)); + verify(&file, key, key_len, val, val_len); + settings_file_close(&file); +} diff --git a/tests/fw/services/settings/wscript b/tests/fw/services/settings/wscript deleted file mode 100644 index e2b9b9d08c..0000000000 --- a/tests/fw/services/settings/wscript +++ /dev/null @@ -1,24 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - " src/fw/util/dict.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_settings_file.c", - defines=['DUMA_DISABLED'], # DUMA false-positive, therefore disabled - override_includes=['dummy_board']) - -# vim:filetype=python diff --git a/tests/fw/services/settings/wscript_build b/tests/fw/services/settings/wscript_build new file mode 100644 index 0000000000..c495d1279a --- /dev/null +++ b/tests/fw/services/settings/wscript_build @@ -0,0 +1,23 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/dict.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob = "test_settings_file.c", + defines=['DUMA_DISABLED'], # DUMA false-positive, therefore disabled + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/services/test_accel_manager.c b/tests/fw/services/test_accel_manager.c index 7cac7cd67b..64ad4934f8 100644 --- a/tests/fw/services/test_accel_manager.c +++ b/tests/fw/services/test_accel_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -23,7 +10,6 @@ #include "fake_system_task.h" #include "stubs_analytics.h" -#include "stubs_analytics_external.h" #include "stubs_gettext.h" #include "stubs_logging.h" #include "stubs_logging.h" @@ -38,7 +24,7 @@ #include "stubs_worker_manager.h" #include "drivers/accel.h" -#include "services/common/event_service.h" +#include "pbl/services/event_service.h" #include "util/math.h" #include "util/size.h" @@ -59,6 +45,10 @@ int32_t sys_vibe_get_vibe_strength(void) { return 0; } void accel_set_shake_sensitivity_high(bool sensitivity_high) {} +void accel_set_shake_sensitivity_percent(uint8_t percent) {} +bool shell_prefs_get_accel_shake_log_info_enabled(void) { + return false; +} QueueHandle_t pebble_task_get_to_queue(PebbleTask task) { return NULL; } @@ -98,14 +88,6 @@ bool accel_get_double_tap_detection_enabled(void) { return false; } -bool accel_run_selftest(void) { - return true; -} - -bool gyro_run_selftest(void) { - return true; -} - bool new_timer_add_work_callback_from_isr(NewTimerWorkCallback cb, void *data) { return false; } diff --git a/tests/fw/services/test_analytics.c b/tests/fw/services/test_analytics.c deleted file mode 100644 index 009e3315e1..0000000000 --- a/tests/fw/services/test_analytics.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/uuid.h" -#include "services/common/analytics/analytics.h" -#include "services/common/analytics/analytics_heartbeat.h" -#include "services/common/analytics/analytics_storage.h" -#include "services/common/analytics/analytics_logging.h" - -#include "services/common/comm_session/session_transport.h" - -#include "services/normal/data_logging/data_logging_service.h" - -#include "clar.h" -#include "stubs_bt_lock.h" -#include "stubs_analytics_external.h" -#include "stubs_logging.h" -#include "stubs_mutex.h" -#include "stubs_passert.h" -#include "stubs_rand_ptr.h" -#include "stubs_tick.h" -#include "stubs_worker_manager.h" - -#include "fake_app_manager.h" -#include "fake_pbl_malloc.h" -#include "fake_new_timer.h" -#include "fake_rtc.h" -#include "fake_system_task.h" -#include "fake_time.h" - -void dls_clear() {} -bool dls_initialized(void) {return true;} - -const PebbleProcessMd* launcher_menu_app_get_app_info(void) { - return NULL; -} - -GAPLEConnection *gap_le_connection_get_gateway(void) { - return NULL; -} - -void launcher_task_add_callback(void (*callback)(void *data), void *data) { - callback(data); -} - -bool comm_session_is_valid(const CommSession *session) { - return true; -} - -CommSessionTransportType comm_session_analytics_get_transport_type(CommSession *session) { - return CommSessionTransportType_PPoGATT; -} - -void sys_analytics_logging_log_event(AnalyticsEventBlob *event_blob) { -} - -void test_analytics__initialize(void) { - analytics_init(); - fake_rtc_init(0, 0); -} - -void test_analytics__cleanup(void) { - -} - -// Make sure the stopwatches record time elapsed in ms (not ticks) -void test_analytics__stopwatches_should_record_ms(void) { - const AnalyticsMetric metric = ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME; - analytics_stopwatch_start(metric, AnalyticsClient_System); - fake_rtc_increment_ticks(1024); - - analytics_storage_take_lock(); - extern void analytics_stopwatches_update(uint64_t current_ticks); - analytics_stopwatches_update(rtc_get_ticks()); - - AnalyticsHeartbeat *heartbeat = analytics_storage_find(metric, NULL, AnalyticsClient_System); - int64_t value = analytics_heartbeat_get(heartbeat, metric); - analytics_storage_give_lock(); - - printf("Battery change period: %"PRId64"\n", value); - // 1024 ticks === 1000 ms - cl_assert(value == 1000); -} - - -static bool dls_log_called = false; -static int64_t expected_value = 254307546; -void test_analytics__minimal_logging_test(void) { - analytics_set(ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME, expected_value, - AnalyticsClient_System); - - // After calling analytics_logging_system_task_cb, the analytics code should call into - // dls_log with the device heartbeat data, where we check that the value - // therein is stored correctly. - analytics_logging_system_task_cb(NULL); - cl_assert(dls_log_called); -} - -DataLoggingResult dls_log(DataLoggingSession *logging_session, const void *data, - uint32_t num_items) { - // Make sure we aren't called more than once. - cl_assert(dls_log_called == false); - - // We only set a device metric, getting app heartbeat data is a bug. - uint8_t got_type = *((uint8_t *)data); - printf("Type: %"PRIu8"\n", got_type); - cl_assert(got_type == ANALYTICS_HEARTBEAT_KIND_DEVICE); - - uint16_t got_version = *((uint16_t *)(data + 1)); - printf("Version: %"PRIu16"\n", got_version); - cl_assert(got_version == ANALYTICS_DEVICE_HEARTBEAT_BLOB_VERSION); - - uint32_t got_value = *((uint32_t *)(data + 28)); - printf("Battery change period: %"PRIu32"\n", got_value); - cl_assert(got_value == expected_value); - - dls_log_called = true; - return DATA_LOGGING_SUCCESS; -} - -DataLoggingSession *dls_create(uint32_t tag, DataLoggingItemType item_type, uint16_t item_size, - bool buffered, bool resume, const Uuid *uuid) { - // We want a truthy value where dereferencing it *should* crash. - return (DataLoggingSession*)0x1; -} diff --git a/tests/fw/services/test_app_cache.c b/tests/fw/services/test_app_cache.c index b32117362c..d1b20bfcc8 100644 --- a/tests/fw/services/test_app_cache.c +++ b/tests/fw/services/test_app_cache.c @@ -1,30 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/app_cache.h" +#include "pbl/services/app_cache.h" #include "process_management/app_install_manager.h" #include "resource/resource_storage.h" #include "process_management/app_install_types.h" -#include "services/normal/filesystem/app_file.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/process_management/app_storage.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/filesystem/app_file.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/process_management/app_storage.h" +#include "pbl/services/settings/settings_file.h" #include "shell/normal/quick_launch.h" #include #include "system/logging.h" @@ -498,7 +485,7 @@ void test_app_cache__free_up_space_save_defaults(void) { prv_cleanup(); to_free = SIZE_SUM - after_free; before_size = app_cache_get_size(); - PBL_LOG(LOG_LEVEL_DEBUG, "%d %d %d", to_free, after_free, before_size); + PBL_LOG_DBG("%d %d %d", to_free, after_free, before_size); cl_assert(before_size >= to_free); cl_assert_equal_i(S_SUCCESS, app_cache_free_up_space(to_free)); fake_system_task_callbacks_invoke_pending(); diff --git a/tests/fw/services/test_app_fetch_endpoint.c b/tests/fw/services/test_app_fetch_endpoint.c index cb05637e7b..4eb126109d 100644 --- a/tests/fw/services/test_app_fetch_endpoint.c +++ b/tests/fw/services/test_app_fetch_endpoint.c @@ -1,24 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "applib/rockyjs/rocky_res.h" -#include "services/common/comm_session/session.h" -#include "services/normal/app_fetch_endpoint.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/app_fetch_endpoint.h" #include "system/logging.h" #include "util/attributes.h" @@ -58,17 +44,6 @@ void put_bytes_expect_init(uint32_t timeout_ms) { void app_storage_delete_bank(uint32_t bank) { } -const PebbleProcessMd *app_install_get_md(AppInstallId id, bool worker) { - return NULL; -} - -void app_install_release_md(const PebbleProcessMd *md) { -} - -RockyResourceValidation s_rocky_app_validate_resources__result; -RockyResourceValidation rocky_app_validate_resources(const PebbleProcessMd *md) { - return s_rocky_app_validate_resources__result; -} typedef struct PACKED { uint16_t length; @@ -77,11 +52,11 @@ typedef struct PACKED { enum { APP_FETCH_INSTALL_COMMAND = 0x01, -} AppFetchCommand; +}; enum { APP_FETCH_INSTALL_RESPONSE = 0x01, -} AppFetchResponse; +}; typedef struct AppFetchData { CommSession *session; @@ -134,7 +109,7 @@ static void prv_check_valid_app_fetch_request(uint16_t endpoint_id, const uint8_ unsigned int data_length) { // AppFetchRequest request = *(AppFetchRequest *)data; - PBL_LOG(LOG_LEVEL_DEBUG, "sizeof: %lu length: %u", sizeof(AppFetchRequest), data_length); + PBL_LOG_DBG("sizeof: %lu length: %u", sizeof(AppFetchRequest), data_length); AppFetchRequest request = *(AppFetchRequest *)data; cl_assert_equal_i(endpoint_id, APP_FETCH_ENDPOINT_ID); @@ -178,21 +153,10 @@ static void prv_fetch_complete_app() { fake_system_task_callbacks_invoke_pending(); } -void test_app_fetch_endpoint__no_incompatible_js(void) { - s_rocky_app_validate_resources__result = RockyResourceValidation_Valid; +void test_app_fetch_endpoint__fetch_complete(void) { prv_fetch_complete_app(); const PebbleEvent e = fake_event_get_last(); cl_assert_equal_i(PEBBLE_APP_FETCH_EVENT, e.type); cl_assert_equal_i(AppFetchEventTypeFinish, e.app_fetch.type); } - -void test_app_fetch_endpoint__incompatible_js(void) { - s_rocky_app_validate_resources__result = RockyResourceValidation_Invalid; - prv_fetch_complete_app(); - - const PebbleEvent e = fake_event_get_last(); - cl_assert_equal_i(PEBBLE_APP_FETCH_EVENT, e.type); - cl_assert_equal_i(AppFetchEventTypeError, e.app_fetch.type); - cl_assert_equal_i(AppFetchResultIncompatibleJSFailure, e.app_fetch.error_code); -} diff --git a/tests/fw/services/test_app_file.c b/tests/fw/services/test_app_file.c index de31eeea0f..40c4c7afc6 100644 --- a/tests/fw/services/test_app_file.c +++ b/tests/fw/services/test_app_file.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -20,7 +7,7 @@ #include #include -#include "services/normal/filesystem/app_file.h" +#include "pbl/services/filesystem/app_file.h" #include "stubs_passert.h" diff --git a/tests/fw/services/test_app_glance_service.c b/tests/fw/services/test_app_glance_service.c index 89cf6b2233..32ed8cf472 100644 --- a/tests/fw/services/test_app_glance_service.c +++ b/tests/fw/services/test_app_glance_service.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -21,10 +8,10 @@ #include "kernel/pbl_malloc.h" #include "process_management/app_install_manager.h" #include "resource/resource_ids.auto.h" -#include "services/normal/app_glances/app_glance_service.h" -#include "services/normal/blob_db/app_glance_db.h" -#include "services/normal/blob_db/app_glance_db_private.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/app_glances/app_glance_service.h" +#include "pbl/services/blob_db/app_glance_db.h" +#include "pbl/services/blob_db/app_glance_db_private.h" +#include "pbl/services/filesystem/pfs.h" #include "util/uuid.h" // Fakes diff --git a/tests/fw/services/test_app_install_manager.c b/tests/fw/services/test_app_install_manager.c index c8194fe3c2..810d1b97ca 100644 --- a/tests/fw/services/test_app_install_manager.c +++ b/tests/fw/services/test_app_install_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "pebble_asserts.h" @@ -20,15 +7,15 @@ #include "apps/system_app_ids.h" #include "flash_region/flash_region.h" #include "process_management/app_install_manager.h" -#include "services/normal/process_management/app_storage.h" +#include "pbl/services/process_management/app_storage.h" #include "process_management/pebble_process_info.h" #include "process_management/pebble_process_md.h" #include "resource/resource.h" #include "resource/resource_storage.h" #include "resource/resource_storage_file.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/app_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/app_db.h" #include "util/build_id.h" #include "util/time/time.h" diff --git a/tests/fw/services/test_app_menu_data_source.c b/tests/fw/services/test_app_menu_data_source.c index b4c2a9dc48..1ef7358056 100644 --- a/tests/fw/services/test_app_menu_data_source.c +++ b/tests/fw/services/test_app_menu_data_source.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -24,10 +11,10 @@ #include "resource/resource.h" #include "resource/resource_storage.h" #include "resource/resource_storage_file.h" -#include "services/common/system_task.h" -#include "services/normal/app_cache.h" -#include "services/normal/blob_db/app_db.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/system_task.h" +#include "pbl/services/app_cache.h" +#include "pbl/services/blob_db/app_db.h" +#include "pbl/services/filesystem/pfs.h" #include "util/build_id.h" #include "util/size.h" #include "fixtures/load_test_resources.h" @@ -91,6 +78,9 @@ //////////////////////////////////// #include "fake_spi_flash.h" +// Test reset function for app_order_storage cached state +extern void app_order_storage_reset_for_tests(void); + const uint32_t g_num_file_resource_stores = 0; const FileResourceData g_file_resource_stores[] = {}; @@ -206,6 +196,10 @@ void test_app_menu_data_source__initialize(void) { pfs_init(false); pfs_format(false); + // Reset app_order_storage cached state - file_known_missing persists between tests + // and would cause app_order_read_order to return NULL even when the file exists + app_order_storage_reset_for_tests(); + app_install_manager_init(); app_db_init(); app_cache_init(); @@ -553,13 +547,11 @@ void test_app_menu_data_source__settings_app_floats_to_top_if_absent_from_storag APP_ID_WATCHFACES, APP_ID_WORKOUT, // Install ID (smallest first) + // Note: BIG_TIME_APP_ID is excluded because it's a watchface (PROCESS_INFO_WATCH_FACE) + // and app_filter_callback filters out watchfaces MENU_LAYER_APP_ID, - BIG_TIME_APP_ID, }; - _Static_assert(MENU_LAYER_APP_ID < BIG_TIME_APP_ID, - "MENU_LAYER_APP_ID is unexpectedly >= BIG_TIME_APP_ID."); - const uint8_t num_entries = ARRAY_LENGTH(storage_order); prv_write_order_to_file(storage_order, num_entries); diff --git a/tests/fw/services/test_audio_endpoint.c b/tests/fw/services/test_audio_endpoint.c index cda38c3365..678d600c2e 100644 --- a/tests/fw/services/test_audio_endpoint.c +++ b/tests/fw/services/test_audio_endpoint.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/audio_endpoint.h" -#include "services/normal/audio_endpoint_private.h" +#include "pbl/services/audio_endpoint.h" +#include "pbl/services/audio_endpoint_private.h" #include "util/circular_buffer.h" #include "util/list.h" @@ -66,12 +53,6 @@ static void prv_test_stop_transfer_msg(uint16_t endpoint_id, const uint8_t* data cl_assert(msg->session_id == s_session_id); } -static int s_setup_complete_call_count; -static void prv_test_setup_complete_callback(AudioEndpointSessionId session_id) { - cl_assert(session_id == s_session_id); - ++s_setup_complete_call_count; -} - static void prv_test_stop_transfer_callback(AudioEndpointSessionId session_id) { cl_assert(session_id == s_session_id); } @@ -80,42 +61,17 @@ Transport *s_transport; void test_audio_endpoint__initialize(void) { fake_comm_session_init(); - s_setup_complete_call_count = 0; s_transport = fake_transport_create(TransportDestinationSystem, NULL, NULL); s_session = fake_transport_set_connected(s_transport, true); - s_session_id = audio_endpoint_setup_transfer(prv_test_setup_complete_callback, - prv_test_stop_transfer_callback); + s_session_id = audio_endpoint_setup_transfer(prv_test_stop_transfer_callback); cl_assert(s_session_id != AUDIO_ENDPOINT_SESSION_INVALID_ID); } -void test_audio_endpoint__setup_complete_callback_call_once(void) { - ResponsivenessGrantedHandler granted_handler = - fake_comm_session_get_last_responsiveness_granted_handler(); - cl_assert_equal_i(s_setup_complete_call_count, 0); - - granted_handler(); - cl_assert_equal_i(s_setup_complete_call_count, 1); - - granted_handler(); - cl_assert_equal_i(s_setup_complete_call_count, 1); - - audio_endpoint_stop_transfer(s_session_id); -} - -void test_audio_endpoint__dont_call_setup_complete_callback_if_session_stopped(void) { - ResponsivenessGrantedHandler granted_handler = - fake_comm_session_get_last_responsiveness_granted_handler(); - audio_endpoint_stop_transfer(s_session_id); - - granted_handler(); - cl_assert_equal_i(s_setup_complete_call_count, 0); -} - void test_audio_endpoint__session_control(void) { // Test that it is not possible to start another transfer session if one is already on-going: - AudioEndpointSessionId session_id = audio_endpoint_setup_transfer(NULL, NULL); + AudioEndpointSessionId session_id = audio_endpoint_setup_transfer(NULL); cl_assert(session_id == AUDIO_ENDPOINT_SESSION_INVALID_ID); audio_endpoint_stop_transfer(s_session_id); diff --git a/tests/fw/services/test_clock.c b/tests/fw/services/test_clock.c index b76a0429bf..6810c9ed4b 100644 --- a/tests/fw/services/test_clock.c +++ b/tests/fw/services/test_clock.c @@ -1,26 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/clock.h" -#include "services/normal/timezone_database.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/clock.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/timezone_database.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/vibes/vibe_score.h" #include "resource/resource_ids.auto.h" -#include "flash_region/flash_region_s29vs.h" #include "resource/resource.h" #include "resource/resource_version.auto.h" @@ -37,7 +25,9 @@ // Stubs //////////////////////////////////// +#include "stubs_alerts_preferences.h" #include "stubs_analytics.h" +#include "stubs_vibe_score_info.h" #include "stubs_hexdump.h" #include "stubs_language_ui.h" #include "stubs_logging.h" @@ -67,10 +57,26 @@ void test_clock__initialize(void) { } extern void prv_update_time_info_and_generate_event(time_t *t, TimezoneInfo *tz_info); -void clock_set_time(time_t t) { - prv_update_time_info_and_generate_event(&t, NULL); +extern void prv_watch_dst(void *user); + +// Hourly chime instrumentation +////////////////////////////// +static bool s_should_vibrate; +static int s_vibe_create_count; + +bool alerts_should_vibrate_for_type(AlertType type) { + return s_should_vibrate; +} + +VibeScore *vibe_score_create_with_resource_system(ResAppNum app_num, uint32_t resource_id) { + s_vibe_create_count++; + return NULL; } +void vibe_score_do_vibe(VibeScore *score) {} + +void vibe_score_destroy(VibeScore *score) {} + static void prv_clock_reset(int32_t gmtoff) { TimezoneInfo tzinfo = {{0}}; tzinfo.dst_id = 0; @@ -103,6 +109,11 @@ bool shell_prefs_is_timezone_source_manual(void) { } void shell_prefs_set_timezone_source_manual(bool manual) { } +bool shell_prefs_is_time_source_manual(void) { + return false; +} +void shell_prefs_set_time_source_manual(bool manual) { +} int16_t shell_prefs_get_automatic_timezone_id(void) { return -1; } @@ -125,6 +136,68 @@ void launcher_task_add_callback(void (*callback)(void *data), void *data) { // Tests /////////////////////////// + +// FIRM-2072: on first boot after an OTA the RTC is snapped to a timestamp that +// is exactly on the hour, and the per-second DST timer is registered before +// resource_init() runs. The chime must not touch the (uninitialized) resource +// subsystem on that first tick. +void test_clock__hourly_chime_skips_first_tick_after_boot(void) { + s_prefs_24h_style = false; + fake_rtc_init(0, 0); + rtc_timezone_clear(); + rtc_set_time(1262304000); // 2010-01-01 00:00:00 UTC, divisible by 3600 + clock_init(); + + s_should_vibrate = true; + s_vibe_create_count = 0; + + // First tick after boot: must not create a vibe score even though we are + // exactly on the hour. + prv_watch_dst((void *)false); + cl_assert_equal_i(s_vibe_create_count, 0); + + // A later tick still on the hour chimes normally. + prv_watch_dst((void *)false); + cl_assert_equal_i(s_vibe_create_count, 1); +} + +void test_clock__hourly_chime_only_on_the_hour(void) { + s_prefs_24h_style = false; + fake_rtc_init(0, 0); + rtc_timezone_clear(); + rtc_set_time(1262304000); + clock_init(); + + s_should_vibrate = true; + s_vibe_create_count = 0; + + prv_watch_dst((void *)false); // arm + cl_assert_equal_i(s_vibe_create_count, 0); + + rtc_set_time(1262304000 + 42); // not on the hour + prv_watch_dst((void *)false); + cl_assert_equal_i(s_vibe_create_count, 0); + + rtc_set_time(1262304000 + SECONDS_PER_HOUR); // top of the hour + prv_watch_dst((void *)false); + cl_assert_equal_i(s_vibe_create_count, 1); +} + +void test_clock__hourly_chime_respects_vibrate_setting(void) { + s_prefs_24h_style = false; + fake_rtc_init(0, 0); + rtc_timezone_clear(); + rtc_set_time(1262304000); + clock_init(); + + s_should_vibrate = false; + s_vibe_create_count = 0; + + prv_watch_dst((void *)false); // arm + prv_watch_dst((void *)false); // on the hour but vibing disabled + cl_assert_equal_i(s_vibe_create_count, 0); +} + void test_clock__basic_no_timezone_set_time(void) { s_prefs_24h_style = false; fake_rtc_init(0, 0); @@ -229,8 +302,8 @@ static const time_t s_dst_correct_values[DST_ID_COUNT][3] = { *Rule Brazil 2012 only - Feb Sun>=22 0:00 0 - *Rule Brazil 2013 2014 - Feb Sun>=15 0:00 0 - * THESE TWO RULES REPEAT FROM NOW ONWARDS - Oct 19th 2014 03:00 UTC ~ Feb 22nd 2015 02:00 UTC, GMT-3 */ - [ 6]={ 1413687600, 1424570400,-3 * SECONDS_PER_HOUR }, + * Chile abandoned DST in 2017. */ + [ 6]={ 0, 0,-3 * SECONDS_PER_HOUR }, /* C-Eur (Central Europe) [Nowhere actually uses this anymore lol] Rule C-Eur 1981 max - Mar lastSun 2:00s 1:00 S @@ -293,24 +366,26 @@ static const time_t s_dst_correct_values[DST_ID_COUNT][3] = { [15]={ 1427590800, 1445734800, 2 * SECONDS_PER_HOUR }, /* Egypt (Egypt) [Africa/Cairo] - * Egypt has abandoned DST */ - [16]={ 0, 0, 2 * SECONDS_PER_HOUR }, + * Egypt re-enacted DST in 2023. + Apr 24th 2015 22:00 UTC ~ Oct 30th 2015 22:00 UTC, GMT+2 */ + [16]={ 1429826400, 1446152400, 2 * SECONDS_PER_HOUR }, /* Fiji (Fiji Islands) [Pacific/Fiji] Rule Fiji 2014 max - Nov Sun>=1 2:00 1:00 S Rule Fiji 2015 max - Jan Sun>=18 3:00 0 - - Nov 1st 2014 14:00 UTC ~ Jan 17th 2015 14:00 UTC, GMT+12 */ - [17]={ 1414850400, 1421503200,12 * SECONDS_PER_HOUR }, + * Fiji abandoned DST in 2021. */ + [17]={ 0, 0,12 * SECONDS_PER_HOUR }, /* Haiti (Haiti) [America/Port-au-Prince] - * Haiti has abandoned DST */ - [18]={ 0, 0,-5 * SECONDS_PER_HOUR }, + * Haiti re-enacted DST in 2017. + Mar 8th 2015 07:00 UTC ~ Nov 1st 2015 06:00 UTC, GMT-5 */ + [18]={ 1425798000, 1446357600,-5 * SECONDS_PER_HOUR }, /* Jordan (Jordan) [Asia/Amman] Rule Jordan 2014 max - Mar lastThu 24:00 1:00 S Rule Jordan 2014 max - Oct lastFri 0:00s 0 - - Mar 26th 2015 22:00 UTC ~ Oct 29th 2015 22:00 UTC, GMT+2 */ - [19]={ 1427407200, 1446156000, 2 * SECONDS_PER_HOUR }, + * Jordan abandoned DST in 2022. */ + [19]={ 0, 0, 2 * SECONDS_PER_HOUR }, /* LH (Lord Howe Island) [Australia/Lord_Howe] Rule LH 2008 max - Apr Sun>=1 2:00 0 S @@ -327,16 +402,17 @@ static const time_t s_dst_correct_values[DST_ID_COUNT][3] = { /* Mexico (Mexico) [America/Mexico_City] Rule Mexico 2002 max - Apr Sun>=1 2:00 1:00 D Rule Mexico 2002 max - Oct lastSun 2:00 0 S - Apr 5th 2015 08:00 UTC ~ Oct 25th 2015 07:00 UTC, GMT-6 */ - [22]={ 1428220800, 1445756400,-6 * SECONDS_PER_HOUR }, + * Mexico abandoned DST in 2022. */ + [22]={ 0, 0,-6 * SECONDS_PER_HOUR }, /* Morocco (Morocco) [Africa/Casablanca] Rule Azer 1997 max - Mar lastSun 4:00 1:00 S Rule Azer 1997 max - Oct lastSun 5:00 0 - * TODO: * At least as insane as Egypt, without the possibility of parole. - Mar 29th 2015 02:00 UTC ~ Oct 25th 2015 02:00 UTC, GMT+0 */ - [23]={ 1427594400, 1445738400, 0 * SECONDS_PER_HOUR }, + * Morocco's DST rules now keep it on permanent +1 with brief Ramadan + * pauses; the simple per-year start/end model no longer applies. */ + [23]={ 0, 0, 0 * SECONDS_PER_HOUR }, /* NZ (New Zealand) [Pacific/Auckland] Rule NZ 2007 max - Sep lastSun 2:00s 1:00 D @@ -347,20 +423,21 @@ static const time_t s_dst_correct_values[DST_ID_COUNT][3] = { /* Namibia (Namibia) [Africa/Windhoek] Rule Namibia 1994 max - Sep Sun>=1 2:00 1:00 S Rule Namibia 1995 max - Apr Sun>=1 2:00 0 - - Sep 7th 2014 01:00 UTC ~ Apr 5th 2015 00:00 UTC, GMT+1 */ - [25]={ 1410051600, 1428192000, 1 * SECONDS_PER_HOUR }, + * Namibia abandoned DST in 2017. */ + [25]={ 0, 0, 1 * SECONDS_PER_HOUR }, /* Palestine (Gaza/West Bank) [Asia/Gaza] Rule Palestine 2016 max - Mar lastSat 1:00 1:00 S Rule Palestine 2016 max - Oct lastSat 1:00 0 - - Mar 27th 2015 23:00 UTC ~ Sep 24th 2015 21:00 UTC, GMT+2 */ - [26]={ 1427497200, 1446242400, 2 * SECONDS_PER_HOUR }, + * Palestine's DST schedule now follows year-by-year exceptions and + * the static-rule model no longer matches. */ + [26]={ 0, 0, 2 * SECONDS_PER_HOUR }, /* Para (Paraguay) [America/Asuncion] Rule Para 2010 max - Oct Sun>=1 0:00 1:00 S Rule Para 2013 max - Mar Sun>=22 0:00 0 - - Oct 5th 2014 04:00 UTC ~ Mar 22nd 2015 03:00 UTC, GMT-4 */ - [27]={ 1412481600, 1426993200,-4 * SECONDS_PER_HOUR }, + * Paraguay abandoned DST in 2024. */ + [27]={ 0, 0,-4 * SECONDS_PER_HOUR }, /* RussiaAsia (Some Asian Russian areas) [Nowhere uses this anymore] [Asia/Yerevan] Rule RussiaAsia 1993 max - Mar lastSun 2:00s 1:00 S @@ -372,8 +449,8 @@ static const time_t s_dst_correct_values[DST_ID_COUNT][3] = { /* Syria (Syria) [Asia/Damascus] Rule Syria 2012 max - Mar lastFri 0:00 1:00 S Rule Syria 2009 max - Oct lastFri 0:00 0 - - Mar 26th 2015 22:00 UTC ~ Oct 29th 2015 21:00 UTC, GMT+2 */ - [29]={ 1427407200, 1446152400, 2 * SECONDS_PER_HOUR }, + * Syria abandoned DST in 2022. */ + [29]={ 0, 0, 2 * SECONDS_PER_HOUR }, /* Thule (Thule Air Base) [America/Thule] Rule Thule 2007 max - Mar Sun>=8 2:00 1:00 D @@ -401,8 +478,8 @@ static const time_t s_dst_correct_values[DST_ID_COUNT][3] = { /* WS (Western Samoa) [Pacific/Apia] Rule WS 2012 max - Apr Sun>=1 4:00 0 S Rule WS 2012 max - Sep lastSun 3:00 1 D - Sep 27th 2014 14:00 UTC ~ Apr 4th 2015 14:00 UTC, GMT+13 */ - [34]={ 1411826400, 1428156000,13 * SECONDS_PER_HOUR }, + * Western Samoa abandoned DST in 2021. */ + [34]={ 0, 0,13 * SECONDS_PER_HOUR }, /* Zion (Israel) [Asia/Jerusalem] Rule Zion 2013 max - Mar Fri>=23 2:00 1:00 D @@ -516,7 +593,7 @@ void test_clock__cross_dst(void) { .tm_mon = 9, // Oct .tm_year = 2015 - 1900, .tm_isdst = 1, - .tm_gmtoff = -5 * SECONDS_PER_HOUR, + .tm_gmtoff = -4 * SECONDS_PER_HOUR, // EDT (base -5h + 1h DST) }; struct tm nov_7 = { @@ -550,7 +627,7 @@ void test_clock__today(void) { .tm_mon = 4, // May .tm_year = 2016 - 1900, .tm_isdst = 1, - .tm_gmtoff = -5 * SECONDS_PER_HOUR, + .tm_gmtoff = -4 * SECONDS_PER_HOUR, // EDT (base -5h + 1h DST) }; struct tm may_31 = { @@ -561,7 +638,7 @@ void test_clock__today(void) { .tm_mon = 4, // May .tm_year = 2016 - 1900, .tm_isdst = 1, - .tm_gmtoff = -5 * SECONDS_PER_HOUR, + .tm_gmtoff = -4 * SECONDS_PER_HOUR, // EDT (base -5h + 1h DST) }; // DST info for US/Canada 2016 diff --git a/tests/fw/services/test_compass_cal.c b/tests/fw/services/test_compass_cal.c index 4ecf1a0347..8a267e9b4d 100644 --- a/tests/fw/services/test_compass_cal.c +++ b/tests/fw/services/test_compass_cal.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/common/ecompass.h" +#include "pbl/services/ecompass.h" #include "util/math.h" #include "clar.h" @@ -89,12 +76,6 @@ static SampleData s_sample_data[6] = { static int16_t expected_final_solution[3] = { 3017, -1948, -1668 }; -void test_analytics__initialize(void) { -} - -void test_analytics__cleanup(void) { -} - int32_t integer_sqrt(int64_t x) { if (x < 0) { return 0; diff --git a/tests/fw/services/test_contacts.c b/tests/fw/services/test_contacts.c index e8a021b8c3..17f7c2042d 100644 --- a/tests/fw/services/test_contacts.c +++ b/tests/fw/services/test_contacts.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/contacts/attributes_address.h" -#include "services/normal/contacts/contacts.h" -#include "services/normal/blob_db/contacts_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/contacts/attributes_address.h" +#include "pbl/services/contacts/contacts.h" +#include "pbl/services/blob_db/contacts_db.h" #include "util/size.h" diff --git a/tests/fw/services/test_cron.c b/tests/fw/services/test_cron.c index 62b0c86919..0c9577b521 100644 --- a/tests/fw/services/test_cron.c +++ b/tests/fw/services/test_cron.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/cron.h" +#include "pbl/services/cron.h" #include "util/size.h" #include diff --git a/tests/fw/services/test_debounced_connection_service.c b/tests/fw/services/test_debounced_connection_service.c index 0a887304b5..a7bde34848 100644 --- a/tests/fw/services/test_debounced_connection_service.c +++ b/tests/fw/services/test_debounced_connection_service.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/debounced_connection_service.h" -#include "services/common/regular_timer.h" +#include "pbl/services/debounced_connection_service.h" +#include "pbl/services/regular_timer.h" #include "syscall/syscall.h" #include "fake_new_timer.h" @@ -25,11 +12,15 @@ #include "fake_system_task.h" #include "fake_pbl_malloc.h" #include "fake_session.h" +#include "stubs_alerts.h" +#include "stubs_alerts_preferences.h" #include "stubs_bt_lock.h" #include "stubs_logging.h" #include "stubs_mutex.h" #include "stubs_hexdump.h" #include "stubs_passert.h" +#include "stubs_vibe_score.h" +#include "stubs_vibe_score_info.h" // Fakes /////////////////////////////////////////////////////////// diff --git a/tests/fw/services/test_do_not_disturb.c b/tests/fw/services/test_do_not_disturb.c index 623c2fc0ad..1e7afc2248 100644 --- a/tests/fw/services/test_do_not_disturb.c +++ b/tests/fw/services/test_do_not_disturb.c @@ -1,30 +1,17 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/notifications/do_not_disturb.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/do_not_disturb.h" #include "applib/ui/action_toggle.h" #include "kernel/events.h" #include "resource/resource.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/common/system_task.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/system_task.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "pbl/services/settings/settings_file.h" #include #include diff --git a/tests/fw/services/test_evented_timer.c b/tests/fw/services/test_evented_timer.c index 8cefc3dfa5..0f41670bac 100644 --- a/tests/fw/services/test_evented_timer.c +++ b/tests/fw/services/test_evented_timer.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "kernel/events.h" -#include "services/common/evented_timer.h" +#include "pbl/services/evented_timer.h" #include "kernel/pebble_tasks.h" #include "stubs_events.h" diff --git a/tests/fw/services/test_hrm_manager.c b/tests/fw/services/test_hrm_manager.c index 441f17c9b2..fbca95803d 100644 --- a/tests/fw/services/test_hrm_manager.c +++ b/tests/fw/services/test_hrm_manager.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "drivers/hrm.h" #include "os/tick.h" -#include "services/common/hrm/hrm_manager.h" -#include "services/common/hrm/hrm_manager_private.h" +#include "pbl/services/hrm/hrm_manager.h" +#include "pbl/services/hrm/hrm_manager_private.h" #include "util/size.h" #include "fake_app_manager.h" @@ -42,7 +29,7 @@ #include -#include +#include // ----------------------------------------------------------------------------- @@ -56,6 +43,7 @@ extern uint32_t prv_num_system_task_events_queued(void); extern TimerID prv_get_timer_id(void); extern bool prv_can_turn_sensor_on(void); extern void prv_charger_event_cb(PebbleEvent *e); +extern uint32_t prv_get_dropped_events_count(void); // ----------------------------------------------------------------------------- @@ -66,7 +54,7 @@ static struct { bool enabled; } s_hrm_state; -void hrm_enable(HRMDevice *dev) { s_hrm_state.enabled = true; } +bool hrm_enable(HRMDevice *dev) { s_hrm_state.enabled = true; return true; } void hrm_disable(HRMDevice *dev) { s_hrm_state.enabled = false; } bool hrm_is_enabled(HRMDevice *dev) { return s_hrm_state.enabled; } @@ -76,10 +64,14 @@ bool hrm_is_enabled(HRMDevice *dev) { return s_hrm_state.enabled; } static const QueueHandle_t FAKE_APP_QUEUE = (QueueHandle_t) 1337; static uint32_t s_event_count; +static bool s_queue_full; static PebbleEvent s_events_received[16]; signed portBASE_TYPE xQueueGenericSend(QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, portBASE_TYPE xCopyPosition) { cl_assert_equal_i((intptr_t) xQueue, (intptr_t) FAKE_APP_QUEUE); + if (s_queue_full) { + return pdFALSE; + } if (s_event_count < ARRAY_LENGTH(s_events_received)) { s_events_received[s_event_count] = *((PebbleEvent *)pvItemToQueue); } @@ -101,9 +93,6 @@ QueueHandle_t pebble_task_get_to_queue(PebbleTask task) { // ----------------------------------------------------------------------------- // Fakes // ----------------------------------------------------------------------------- -bool mfg_info_is_hrm_present(void) { - return true; -} static bool s_activity_prefs_heart_rate_is_enabled = true; bool activity_prefs_heart_rate_is_enabled(void) { @@ -120,8 +109,7 @@ bool battery_is_usb_connected(void) { #define TO_SESSION_REF(x) ((HRMSessionRef)(long)(x)) static const HRMData s_hrm_event_data = { - .led_current_ua = 243, - + .features = HRMFeature_BPM, .hrm_bpm = 82, .hrm_quality = HRMQuality_Excellent, }; @@ -170,6 +158,7 @@ void test_hrm_manager__initialize(void) { s_activity_prefs_heart_rate_is_enabled = true; s_event_count = 0; + s_queue_full = false; s_num_cb_events_1 = 0; s_num_cb_events_2 = 0; memset(&s_hrm_state, 0, sizeof(s_hrm_state)); @@ -245,6 +234,31 @@ void test_hrm_manager__app_cleanup(void) { sys_hrm_manager_unsubscribe(session_ref); } +// If the app set its own expiration shorter than HRM_MANAGER_APP_EXIT_EXPIRATION_SEC (e.g. the +// workout app's post-workout recovery window), process cleanup must not lengthen it back to the +// default — that would defeat the app's explicit choice and leave the HR sensor on too long. +void test_hrm_manager__app_cleanup_preserves_shorter_expiration(void) { + stub_pebble_tasks_set_current(PebbleTask_App); + + AppInstallId app_id = 1; + const uint32_t update_interval_s = 1; + const uint16_t shorter_expire_s = 10 * SECONDS_PER_MINUTE; + HRMFeature features = HRMFeature_BPM; + + HRMSessionRef session_ref = sys_hrm_manager_app_subscribe(app_id, update_interval_s, + shorter_expire_s, features); + uint16_t ret_expire_s; + sys_hrm_manager_get_subscription_info(session_ref, NULL, NULL, &ret_expire_s, NULL); + cl_assert_equal_i(ret_expire_s, shorter_expire_s); + + hrm_manager_process_cleanup(PebbleTask_App, app_id); + + sys_hrm_manager_get_subscription_info(session_ref, NULL, NULL, &ret_expire_s, NULL); + cl_assert_equal_i(ret_expire_s, shorter_expire_s); + + sys_hrm_manager_unsubscribe(session_ref); +} + // Test that app subscriptions expire correctly void test_hrm_manager__app_expiration(void) { @@ -395,22 +409,16 @@ void test_hrm_manager__different_feature_callbacks(void) { AppInstallId app_id = 1; const uint16_t expire_s = SECONDS_PER_MINUTE; HRMSessionRef bpm_session = sys_hrm_manager_app_subscribe(app_id, 1, expire_s, HRMFeature_BPM); - HRMSessionRef led_session = sys_hrm_manager_app_subscribe(app_id + 1, 1, expire_s, - HRMFeature_LEDCurrent); - HRMSessionRef all_session = sys_hrm_manager_app_subscribe(app_id + 2, 1, expire_s, - HRMFeature_BPM|HRMFeature_LEDCurrent); HRMSessionRef no_session = sys_hrm_manager_app_subscribe(app_id + 3, 1, expire_s, 0 /* no features */); prv_fake_send_new_data(); fake_system_task_callbacks_invoke_pending(); - // Expect 4 events: 1 for BPM, 1 for LED, 2 for subscribing to all, none for no feature. - cl_assert_equal_i(s_event_count, 4); + // Expect 1 event: 1 for BPM, none for no feature. + cl_assert_equal_i(s_event_count, 1); sys_hrm_manager_unsubscribe(bpm_session); - sys_hrm_manager_unsubscribe(led_session); - sys_hrm_manager_unsubscribe(all_session); sys_hrm_manager_unsubscribe(no_session); } @@ -422,13 +430,13 @@ void test_hrm_manager__multiple_feature_callbacks(void) { for (int i = 0; i < num_refs; ++i, app_id++) { session_refs[i] = TO_SESSION_REF(i+1); const uint16_t expire_s = SECONDS_PER_MINUTE; - sys_hrm_manager_app_subscribe(app_id, 1, expire_s, HRMFeature_BPM|HRMFeature_LEDCurrent); + sys_hrm_manager_app_subscribe(app_id, 1, expire_s, HRMFeature_BPM); } prv_fake_send_new_data(); - // Two events for each app subscriber - cl_assert_equal_i(s_event_count, num_refs * 2); + // One BPM event for each app subscriber + cl_assert_equal_i(s_event_count, num_refs); for (int i = 0; i < num_refs; ++i) { sys_hrm_manager_unsubscribe(session_refs[i]); @@ -511,13 +519,13 @@ void test_hrm_manager__set_features(void) { // Starts off with BPM enabled cl_assert_equal_i(state->features, HRMFeature_BPM); - // Change to only LED Current - sys_hrm_manager_set_features(session_ref, HRMFeature_LEDCurrent); - cl_assert_equal_i(state->features, HRMFeature_LEDCurrent); + // Change to none + sys_hrm_manager_set_features(session_ref, 0); + cl_assert_equal_i(state->features, 0); - // Change to LEDCurrent + BPM - sys_hrm_manager_set_features(session_ref, HRMFeature_LEDCurrent | HRMFeature_BPM); - cl_assert_equal_i(state->features, HRMFeature_LEDCurrent | HRMFeature_BPM); + // Change to BPM + sys_hrm_manager_set_features(session_ref, HRMFeature_BPM); + cl_assert_equal_i(state->features, HRMFeature_BPM); } void test_hrm_manager__set_update_internal(void) { @@ -540,7 +548,7 @@ void test_hrm_manager__set_update_internal(void) { cl_assert(state->expire_utc == rtc_get_time() + expire_b_s); } -#define NUM_TEST_EVENTS 2 +#define NUM_TEST_EVENTS 1 void test_hrm_manager__circular_buffer_event_copy(void) { // Make sure there will be unaligned data const uint16_t buf_size = sizeof(PebbleHRMEvent) * 2 + sizeof(PebbleHRMEvent) / 2; @@ -551,7 +559,6 @@ void test_hrm_manager__circular_buffer_event_copy(void) { PebbleHRMEvent event[NUM_TEST_EVENTS] = { { .event_type = HRMEvent_BPM, .bpm = { .bpm = 65, .quality = 5 } }, - { .event_type = HRMEvent_LEDCurrent, .led = { .current_ua = 243 } }, }; { // These events will insert properly aligned in the buffer for (int i = 0; i < NUM_TEST_EVENTS; ++i) { @@ -745,3 +752,58 @@ void test_hrm_manager__can_turn_sensor_on(void) { cl_assert(hrm_is_enabled(HRM)); } +// A full app event queue (app not draining events fast enough) must not panic the firmware. +// The HRM sample is dropped and counted, and delivery resumes once the queue drains. +void test_hrm_manager__app_queue_full_drops_without_panic(void) { + stub_pebble_tasks_set_current(PebbleTask_App); + + AppInstallId app_id = 1; + const uint16_t expire_s = SECONDS_PER_MINUTE; + HRMSessionRef session_ref = sys_hrm_manager_app_subscribe(app_id, 1, expire_s, HRMFeature_BPM); + + // App is not draining its event queue. + s_queue_full = true; + + // Must not panic; the event is dropped (not delivered) and counted. + prv_fake_send_new_data(); + cl_assert_equal_i(s_event_count, 0); + cl_assert_equal_i(prv_get_dropped_events_count(), 1); + + // Subscription survives and delivery resumes once the queue drains. + s_queue_full = false; + prv_fake_send_new_data(); + cl_assert_equal_i(s_event_count, 1); + cl_assert_equal_i(s_events_received[0].type, PEBBLE_HRM_EVENT); + cl_assert_equal_i(s_events_received[0].hrm.event_type, HRMEvent_BPM); + cl_assert_equal_i(prv_get_dropped_events_count(), 1); + + sys_hrm_manager_unsubscribe(session_ref); +} + +// Test that OffWrist quality is delivered immediately without delay +void test_hrm_manager__immediate_off_wrist(void) { + stub_pebble_tasks_set_current(PebbleTask_KernelBackground); + s_num_cb_events_1 = 0; + + const uint16_t expire_s = SECONDS_PER_MINUTE; + HRMSessionRef session_ref = hrm_manager_subscribe_with_callback(INSTALL_ID_INVALID, 1, + expire_s, HRMFeature_BPM, + prv_fake_hrm_1_cb, NULL); + fake_system_task_callbacks_invoke_pending(); + + // Send OffWrist data immediately (no prior good readings) + HRMData off_wrist_data = { + .features = HRMFeature_BPM, + .hrm_bpm = 0, + .hrm_quality = HRMQuality_OffWrist, + }; + hrm_manager_new_data_cb(&off_wrist_data); + fake_system_task_callbacks_invoke_pending(); + + // OffWrist should be delivered immediately + cl_assert_equal_i(s_num_cb_events_1, 1); + cl_assert_equal_i(s_cb_events_1[0].event_type, HRMEvent_BPM); + cl_assert_equal_i(s_cb_events_1[0].bpm.quality, HRMQuality_OffWrist); + + sys_hrm_manager_unsubscribe(session_ref); +} diff --git a/tests/fw/services/test_light.c b/tests/fw/services/test_light.c index cc6725b58d..c2b46e8798 100644 --- a/tests/fw/services/test_light.c +++ b/tests/fw/services/test_light.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "board/board.h" #include "drivers/backlight.h" -#include "services/common/light.h" +#include "pbl/services/light.h" #include "system/passert.h" #include "fake_new_timer.h" @@ -31,13 +18,16 @@ #include "stubs_print.h" #include "stubs_passert.h" #include "stubs_analytics.h" -#include "stubs_analytics_external.h" #include "stubs_ambient_light.h" #include "stubs_battery_monitor.h" #include "stubs_low_power.h" #include "stubs_serial.h" #include "stubs_logging.h" #include "stubs_mutex.h" +#include "stubs_rtc.h" + +void vTaskDelay(uint32_t ticks) { +} // the time that the backlight remains on but there is zero user interaction extern const uint32_t INACTIVE_LIGHT_TIMEOUT_MS; @@ -53,14 +43,15 @@ extern const uint32_t LIGHT_FADE_STEPS; static TimerID s_light_timer; -static uint16_t s_backlight_brightness; +static uint8_t s_backlight_brightness; +static bool s_backlight_enabled = true; BacklightBehaviour backlight_get_behaviour(void) { return BacklightBehaviour_On; } bool backlight_is_enabled(void) { - return true; + return s_backlight_enabled; } bool backlight_is_ambient_sensor_enabled(void) { @@ -68,15 +59,19 @@ bool backlight_is_ambient_sensor_enabled(void) { } void backlight_set_enabled(bool enabled) { + s_backlight_enabled = enabled; } void backlight_set_ambient_sensor_enabled(bool enabled) { } -void backlight_set_brightness(uint16_t brightness) { +void backlight_set_brightness(uint8_t brightness) { s_backlight_brightness = brightness; } +void backlight_refresh(void) { +} + bool backlight_is_motion_enabled(void) { return false; } @@ -93,25 +88,21 @@ void backlight_set_timeout_ms(uint32_t timeout_ms) { uint16_t s_backlight_intensity; -uint16_t backlight_get_intensity(void) { +uint8_t backlight_get_intensity(void) { return s_backlight_intensity; } -uint8_t backlight_get_intensity_percent(void) { - return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX; -} - -void backlight_set_intensity_percent(uint8_t percent_intensity) { +void backlight_set_intensity(uint8_t percent_intensity) { PBL_ASSERTN(percent_intensity > 0 && percent_intensity <= 100); - s_backlight_intensity = (BACKLIGHT_BRIGHTNESS_MAX * (uint32_t)percent_intensity) / 100; + s_backlight_intensity = percent_intensity; } // Helper functions /////////////////////////////////////////////////////////// -static uint16_t get_expected_brightness() { - return ((BACKLIGHT_BRIGHTNESS_MAX * backlight_get_intensity_percent()) / 100); +static uint8_t get_expected_brightness() { + return backlight_get_intensity(); } static void check_on(void) { @@ -130,7 +121,7 @@ static void check_on_timed_and_consume_partial(void) { stub_new_timer_fire(s_light_timer); - cl_assert_equal_i(s_backlight_brightness, BACKLIGHT_BRIGHTNESS_MAX - (BACKLIGHT_BRIGHTNESS_MAX / LIGHT_FADE_STEPS)); + cl_assert_equal_i(s_backlight_brightness, 100 - (100 / LIGHT_FADE_STEPS)); cl_assert(stub_new_timer_is_scheduled(s_light_timer)); } @@ -147,7 +138,7 @@ static void check_on_timed_and_consume(void) { } static void check_off(void) { - cl_assert_equal_i(s_backlight_brightness, BACKLIGHT_BRIGHTNESS_OFF); + cl_assert_equal_i(s_backlight_brightness, 0); cl_assert(!stub_new_timer_is_scheduled(s_light_timer)); } @@ -159,11 +150,13 @@ void test_light__initialize(void) { light_init(); light_allow(true); s_light_timer = ((StubTimer*) s_idle_timers)->id; - backlight_set_intensity_percent(BOARD_CONFIG.backlight_on_percent); + backlight_set_intensity(100); + s_backlight_enabled = true; } void test_light__cleanup(void) { s_backlight_brightness = 0; + s_backlight_enabled = true; stub_new_timer_delete(s_light_timer); } @@ -242,6 +235,18 @@ void test_light__button_press_during_fading(void) { check_on_timed_and_consume(); } +void test_light__toggle_disabled_while_button_pressed_turns_off_immediately(void) { + light_button_pressed(); + check_on(); + + light_toggle_enabled(); + cl_assert(!backlight_is_enabled()); + check_off(); + + light_button_released(); + check_off(); +} + void test_light__interaction_during_fading(void) { light_button_pressed(); check_on(); @@ -252,4 +257,3 @@ void test_light__interaction_during_fading(void) { light_enable_interaction(); check_on_timed_and_consume(); } - diff --git a/tests/fw/services/test_migrate_wakeup.c b/tests/fw/services/test_migrate_wakeup.c index cecb80affd..6b55c8cc5e 100644 --- a/tests/fw/services/test_migrate_wakeup.c +++ b/tests/fw/services/test_migrate_wakeup.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "flash_region/flash_region.h" #include "syscall/syscall.h" -#include "services/normal/wakeup.h" -#include "services/common/event_service.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/wakeup.h" +#include "pbl/services/event_service.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" #include "process_management/app_install_manager.h" #include "util/attributes.h" diff --git a/tests/fw/services/test_music_endpoint.c b/tests/fw/services/test_music_endpoint.c index b6a3903a21..d9eb948581 100644 --- a/tests/fw/services/test_music_endpoint.c +++ b/tests/fw/services/test_music_endpoint.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/music.h" -#include "services/normal/music_endpoint.h" -#include "services/normal/music_internal.h" +#include "pbl/services/music.h" +#include "pbl/services/music_endpoint.h" +#include "pbl/services/music_internal.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_remote_os.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_remote_os.h" #include "kernel/events.h" @@ -46,6 +33,8 @@ #include "stubs_serial.h" #include "stubs_tick.h" +void ams_music_disconnect(void) {} + extern void music_protocol_msg_callback(CommSession *session, const uint8_t* msg, size_t length); // Helpers diff --git a/tests/fw/services/test_pfs.c b/tests/fw/services/test_pfs.c index 1b6221a563..0ab2b937db 100644 --- a/tests/fw/services/test_pfs.c +++ b/tests/fw/services/test_pfs.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include #include "drivers/flash.h" #include "flash_region/flash_region.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/filesystem/flash_translation.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/filesystem/flash_translation.h" #include "system/logging.h" #include "system/passert.h" #include "util/math.h" @@ -663,6 +650,13 @@ void test_pfs__get_size(void) { } void test_pfs__migration(void) { + // The filesystem migration path grows the filesystem by adding flash regions one at a time. + // This only happens on legacy multi-region (non-contiguous) flash layouts (Pebble OG / Steel, + // PLATFORM_TINTIN / n25q), which expose FLASH_REGION_FILESYSTEM_2_BEGIN. Modern platforms (e.g. + // obelix) have a single contiguous filesystem region, so there is no second region to migrate + // into and ftl_populate_region_list() is a no-op. Guard the migration-specific assertions so + // this case only exercises behaviour that exists on the platform under test. +#ifdef FLASH_REGION_FILESYSTEM_2_BEGIN // reset the flash fake_spi_flash_cleanup(); fake_spi_flash_init(0, 0x1000000); @@ -696,7 +690,7 @@ void test_pfs__migration(void) { int original_page_count = num_pages(); ftl_populate_region_list(); // make sure something was added - PBL_LOG(LOG_LEVEL_DEBUG, "original pages %u, num pages: %u", original_page_count, num_pages()); + PBL_LOG_DBG("original pages %u, num pages: %u", original_page_count, num_pages()); cl_assert(original_page_count < num_pages()); for (int i = 0; i < original_page_count; i++) { @@ -711,6 +705,7 @@ void test_pfs__migration(void) { cl_assert(fd >= 0); cl_assert(pfs_close(fd) == S_SUCCESS); } +#endif // FLASH_REGION_FILESYSTEM_2_BEGIN } static uint32_t s_watch_file_callback_called_count = 0; diff --git a/tests/fw/services/test_phone_call.c b/tests/fw/services/test_phone_call.c index cd33982c07..3705508a94 100644 --- a/tests/fw/services/test_phone_call.c +++ b/tests/fw/services/test_phone_call.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "kernel/events.h" -#include "services/common/comm_session/session.h" -#include "services/normal/notifications/alerts.h" -#include "services/normal/phone_call.h" -#include "services/normal/phone_call_util.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/phone_call.h" +#include "pbl/services/phone_call_util.h" extern T_STATIC void prv_handle_phone_event(PebbleEvent *e, void *context); extern T_STATIC void prv_handle_mobile_app_event(PebbleEvent *e, void *context); diff --git a/tests/fw/services/test_phone_pp.c b/tests/fw/services/test_phone_pp.c index 71f6b1db14..86e83586dc 100644 --- a/tests/fw/services/test_phone_pp.c +++ b/tests/fw/services/test_phone_pp.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/phone_pp.h" -#include "services/normal/phone_call_util.h" +#include "pbl/services/phone_pp.h" +#include "pbl/services/phone_call_util.h" -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" #include "kernel/events.h" #include "fake_events.h" diff --git a/tests/fw/services/test_put_bytes.c b/tests/fw/services/test_put_bytes.c index b9cbed6423..f63a500f2b 100644 --- a/tests/fw/services/test_put_bytes.c +++ b/tests/fw/services/test_put_bytes.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/put_bytes/put_bytes.h" - -#include "services/common/comm_session/session_receive_router.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/put_bytes/put_bytes.h" + +#include "pbl/services/comm_session/session_receive_router.h" #include "os/tick.h" #include "system/bootbits.h" #include "system/firmware_storage.h" @@ -183,7 +170,7 @@ static void prv_receive_data(CommSession *session, const uint8_t* data, size_t l g_put_bytes_receiver_impl.write(r, data, length); g_put_bytes_receiver_impl.finish(r); } else { - PBL_LOG(LOG_LEVEL_ERROR, "No receiver returned!"); + PBL_LOG_ERR("No receiver returned!"); } } diff --git a/tests/fw/services/test_registry_endpoint.c b/tests/fw/services/test_registry_endpoint.c index fa236a0bd4..bb84296d8c 100644 --- a/tests/fw/services/test_registry_endpoint.c +++ b/tests/fw/services/test_registry_endpoint.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "applib/app_watch_info.h" -#include "services/common/comm_session/session.h" -#include "services/common/system_task.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/system_task.h" #include diff --git a/tests/fw/services/test_shared_prf_storage_v2.c b/tests/fw/services/test_shared_prf_storage_v2.c deleted file mode 100644 index 9ccecb5956..0000000000 --- a/tests/fw/services/test_shared_prf_storage_v2.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/common/system_task.h" -#include "flash_region/flash_region_s29vs.h" - -#include - -#include - -#include "clar.h" - -// Fakes -////////////////////////////////////////////////////////// - -#include "fake_spi_flash.h" -#include "fake_regular_timer.h" -#include "stubs_pbl_malloc.h" -#include "stubs_passert.h" -#include "stubs_logging.h" -#include "stubs_mutex.h" - -bool system_task_add_callback(SystemTaskEventCallback cb, void *data) { - cb(data); - return true; -} - -bool sm_is_pairing_info_empty(const SMPairingInfo *p) { - return false; -} - -extern RegularTimerInfo *shared_prf_storage_get_writeback_timer(void); -static void prv_fire_writeback_timer(void) { - fake_regular_timer_trigger(shared_prf_storage_get_writeback_timer()); -} - -// Tests -/////////////////////////////////////////////////////////// - -static SMPairingInfo s_pairing_info = (const SMPairingInfo) { - .local_encryption_info = { - .ediv = 123, - .div = 456, - }, - - .remote_encryption_info = { - .ltk = (const SMLongTermKey) { - .data = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, - }, - }, - .rand = 0x11223344, - .ediv = 9876, - }, - - .irk = (const SMIdentityResolvingKey) { - .data = { - 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - }, - }, - .identity = (const BTDeviceInternal) { - .opaque.opaque_64 = 0x1122334455667788, - }, - - .csrk = { - .data = { - 0xcc, 0xdd, 0xee, 0xff, 0x88, 0x99, 0xaa, 0xbb, - 0x44, 0x55, 0x66, 0x77, 0x00, 0x11, 0x22, 0x33, - }, - }, - - .is_local_encryption_info_valid = true, - .is_remote_encryption_info_valid = true, - .is_remote_identity_info_valid = true, - .is_remote_signing_info_valid = true, -}; - -void test_shared_prf_storage_v2__initialize(void) { - fake_spi_flash_init(FLASH_REGION_SHARED_PRF_STORAGE_BEGIN, - FLASH_REGION_SHARED_PRF_STORAGE_END - FLASH_REGION_SHARED_PRF_STORAGE_BEGIN); - shared_prf_storage_init(); -} - -void test_shared_prf_storage_v2__cleanup(void) { - fake_spi_flash_cleanup(); -} - -void test_shared_prf_storage_v2__wipe_all(void) { - SMPairingInfo sm_pairing_info; - memset(&sm_pairing_info, 0xaa, sizeof(sm_pairing_info)); - sm_pairing_info.is_local_encryption_info_valid = true; - sm_pairing_info.is_remote_signing_info_valid = true; - sm_pairing_info.is_remote_identity_info_valid = true; - shared_prf_storage_store_ble_pairing_data(&sm_pairing_info, "Blabla", - false /* requires_address_pinning */, 0 /* flags */); - prv_fire_writeback_timer(); - cl_assert_equal_b(shared_prf_storage_get_ble_pairing_data(NULL, NULL, NULL, NULL), true); - shared_prf_storage_wipe_all(); - - cl_assert_equal_b(shared_prf_storage_get_ble_pairing_data(NULL, NULL, NULL, NULL), false); -} - -void test_shared_prf_storage_v2__getting_started_complete(void) { - shared_prf_storage_wipe_all(); - cl_assert_equal_b(shared_prf_storage_get_getting_started_complete(), false); - shared_prf_storage_set_getting_started_complete(true); - cl_assert_equal_b(shared_prf_storage_get_getting_started_complete(), true); - shared_prf_storage_wipe_all(); - cl_assert_equal_b(shared_prf_storage_get_getting_started_complete(), false); -} - -static void prv_validate_ble_pairing_info(const SMPairingInfo *pairing_info, char *device_name) { - char name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - memset(name_out, 0, sizeof(name_out)); - SMPairingInfo pairing_info_out = {}; - cl_assert_equal_b(shared_prf_storage_get_ble_pairing_data(&pairing_info_out, name_out, - NULL, NULL), true); - cl_assert_equal_i(strcmp(device_name, name_out), 0); - cl_assert_equal_b(pairing_info->is_remote_signing_info_valid, - pairing_info_out.is_remote_signing_info_valid); - cl_assert_equal_b(pairing_info->is_remote_identity_info_valid, - pairing_info_out.is_remote_identity_info_valid); - cl_assert_equal_b(pairing_info->is_remote_encryption_info_valid, - pairing_info_out.is_remote_encryption_info_valid); - cl_assert_equal_b(pairing_info->is_local_encryption_info_valid, - pairing_info_out.is_local_encryption_info_valid); - cl_assert_equal_i(pairing_info->local_encryption_info.ediv, - pairing_info_out.local_encryption_info.ediv); - cl_assert_equal_i(pairing_info->local_encryption_info.div, - pairing_info_out.local_encryption_info.div); - cl_assert_equal_i(pairing_info->identity.opaque.opaque_64, - pairing_info_out.identity.opaque.opaque_64); - cl_assert_equal_i(pairing_info->remote_encryption_info.rand, - pairing_info_out.remote_encryption_info.rand); - cl_assert_equal_i(pairing_info->remote_encryption_info.ediv, - pairing_info_out.remote_encryption_info.ediv); - cl_assert_equal_i(memcmp(&pairing_info->remote_encryption_info.ltk, - &pairing_info_out.remote_encryption_info.ltk, sizeof(SMLongTermKey)), 0); - cl_assert_equal_i(memcmp(&pairing_info->irk, &pairing_info_out.irk, - sizeof(SMIdentityResolvingKey)), 0); - cl_assert_equal_i(memcmp(&pairing_info->csrk, &pairing_info_out.csrk, - sizeof(SM128BitKey)), 0); - -} - -void test_shared_prf_storage_v2__bt_classic_and_le_pairing(void) { - shared_prf_storage_wipe_all(); - cl_assert_equal_b(shared_prf_storage_get_bt_classic_pairing_data(NULL, NULL, NULL, NULL), false); - - // Store a classic pairing - BTDeviceAddress addr = { - .octets = { 0x11, 0x22, 0x33, 0x44, 0x55,}, - }; - char device_name_classic[BT_DEVICE_NAME_BUFFER_SIZE] = "CLASSIC"; - SM128BitKey link_key = { - .data = { 0x55, }, - }; - shared_prf_storage_store_bt_classic_pairing_data(&addr, device_name_classic, &link_key, 0x00); - - // change the classic addr - addr.octets[0] = 0x99; - shared_prf_storage_store_bt_classic_pairing_data(&addr, device_name_classic, &link_key, 0x00); - - // Store an LE pairing - char device_name_le[BT_DEVICE_NAME_BUFFER_SIZE] = "LE"; - shared_prf_storage_store_ble_pairing_data(&s_pairing_info, device_name_le, - false /* requires_address_pinning */, 0 /* flags */); - - // Sync LE and Classic data - prv_fire_writeback_timer(); - - // Make sure everything checks out - prv_validate_ble_pairing_info(&s_pairing_info, device_name_le); - - BTDeviceAddress out_addr = {}; - char device_name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - memset(device_name_out, 0, BT_DEVICE_NAME_BUFFER_SIZE); - SM128BitKey link_key_out = {}; - uint8_t platform_bits_out = 0; - cl_assert_equal_p(shared_prf_storage_get_bt_classic_pairing_data(&out_addr, device_name_out, - &link_key_out, - &platform_bits_out), true); - cl_assert_equal_i(strcmp(device_name_out, device_name_classic), 0); - cl_assert_equal_i(memcmp(&out_addr, &addr, sizeof(addr)), 0); - cl_assert_equal_i(memcmp(&link_key_out, &link_key_out, sizeof(link_key)), 0); -} - -void test_shared_prf_storage_v2__bt_classic_pairing(void) { - shared_prf_storage_wipe_all(); - cl_assert_equal_b(shared_prf_storage_get_bt_classic_pairing_data(NULL, NULL, NULL, NULL), false); - - BTDeviceAddress addr = { - .octets = { - 0x11, 0x22, 0x33, 0x44, 0x55, - }, - }; - char device_name[BT_DEVICE_NAME_BUFFER_SIZE] = "ABCDEFGHIJKLMNOPQRS"; - SM128BitKey link_key = { - .data = { - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, - 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, - }, - }; - shared_prf_storage_store_bt_classic_pairing_data(&addr, device_name, &link_key, 0x00); - prv_fire_writeback_timer(); - shared_prf_storage_store_platform_bits(0xaa); - - BTDeviceAddress out_addr = {}; - char device_name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - memset(device_name_out, 0, BT_DEVICE_NAME_BUFFER_SIZE); - SM128BitKey link_key_out = {}; - uint8_t platform_bits_out = 0; - cl_assert_equal_p(shared_prf_storage_get_bt_classic_pairing_data(&out_addr, device_name_out, - &link_key_out, - &platform_bits_out), true); - cl_assert_equal_i(strcmp(device_name_out, device_name), 0); - cl_assert_equal_i(memcmp(&out_addr, &addr, sizeof(addr)), 0); - cl_assert_equal_i(memcmp(&link_key_out, &link_key_out, sizeof(link_key)), 0); - cl_assert_equal_i(platform_bits_out, 0xaa); - - shared_prf_storage_erase_bt_classic_pairing_data(); - cl_assert_equal_b(shared_prf_storage_get_bt_classic_pairing_data(NULL, NULL, NULL, NULL), false); -} - -void test_shared_prf_storage_v2__ble_pairing(void) { - shared_prf_storage_wipe_all(); - cl_assert_equal_b(shared_prf_storage_get_ble_pairing_data(NULL, NULL, NULL, NULL), false); - - - char device_name[BT_DEVICE_NAME_BUFFER_SIZE] = "ABCDEFGHIJKLMNOPQRS"; - shared_prf_storage_store_ble_pairing_data(&s_pairing_info, device_name, - false /* requires_address_pinning */, 0 /* flags */); - prv_fire_writeback_timer(); - - prv_validate_ble_pairing_info(&s_pairing_info, device_name); - - shared_prf_storage_erase_ble_pairing_data(); - cl_assert_equal_b(shared_prf_storage_get_ble_pairing_data(NULL, NULL, NULL, NULL), false); -} - -void test_shared_prf_storage_v2__root_keys(void) { - shared_prf_storage_wipe_all(); - - cl_assert_equal_b(shared_prf_storage_get_root_key(SMRootKeyTypeEncryption, NULL), false); - cl_assert_equal_b(shared_prf_storage_get_root_key(SMRootKeyTypeIdentity, NULL), false); - - SM128BitKey keys[2]; - for (int i = 0; i < sizeof(keys); ++i) { - ((uint8_t *) keys)[i] = i; - } - - shared_prf_storage_set_root_keys(keys); - - SM128BitKey keys_out[2]; - for (SMRootKeyType key_type = 0; key_type < SMRootKeyTypeNum; ++key_type) { - cl_assert_equal_b(shared_prf_storage_get_root_key(key_type, &keys_out[key_type]), true); - // It's a byte array inside, so memcmp should be OK to use: - cl_assert_equal_i(memcmp(&keys[key_type], &keys_out[key_type], sizeof(keys[0])), 0); - } -} - -void test_shared_prf_storage_v2__local_device_name(void) { - shared_prf_storage_wipe_all(); - - cl_assert_equal_b(shared_prf_storage_get_local_device_name(NULL, 0), false); - - char device_name[BT_DEVICE_NAME_BUFFER_SIZE] = "ABCDEFGHIJKLMNOPQRS"; - shared_prf_storage_set_local_device_name(device_name); - - char device_name_out[BT_DEVICE_NAME_BUFFER_SIZE]; - cl_assert_equal_b(shared_prf_storage_get_local_device_name(device_name_out, - sizeof(device_name_out)), true); - cl_assert_equal_s(device_name, device_name_out); -} - -void test_shared_prf_storage_v2__dont_rewrite_if_no_changes(void) { - shared_prf_storage_wipe_all(); - uint32_t write_count = fake_flash_write_count(); - uint32_t erase_count = fake_flash_erase_count(); - - shared_prf_storage_wipe_all(); - // Already wiped, so no change, - cl_assert_equal_i(write_count, fake_flash_write_count()); - cl_assert_equal_i(erase_count, fake_flash_erase_count()); - - write_count = fake_flash_write_count(); - erase_count = fake_flash_erase_count(); - shared_prf_storage_set_getting_started_complete(true); - // Expect flash getting touched: - cl_assert(write_count < fake_flash_write_count()); - cl_assert(erase_count < fake_flash_erase_count()); - - write_count = fake_flash_write_count(); - erase_count = fake_flash_erase_count(); - shared_prf_storage_set_getting_started_complete(true); - // Expect flash NOT getting touched: - cl_assert_equal_i(write_count, fake_flash_write_count()); - cl_assert_equal_i(erase_count, fake_flash_erase_count()); -} diff --git a/tests/fw/services/test_shared_prf_storage_v3.c b/tests/fw/services/test_shared_prf_storage_v3.c index 6bcc2e55de..4deeddb4ec 100644 --- a/tests/fw/services/test_shared_prf_storage_v3.c +++ b/tests/fw/services/test_shared_prf_storage_v3.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/shared_prf_storage/shared_prf_storage.h" -#include "services/common/shared_prf_storage/v3_sprf/shared_prf_storage_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/shared_prf_storage/v3_sprf/shared_prf_storage_private.h" #include "flash_region/flash_region.h" #include "drivers/flash.h" #include "util/size.h" diff --git a/tests/fw/services/test_smartstrap_comms.c b/tests/fw/services/test_smartstrap_comms.c deleted file mode 100644 index 3697594036..0000000000 --- a/tests/fw/services/test_smartstrap_comms.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "services/normal/accessory/smartstrap_comms.h" -#include "util/mbuf.h" - -#include - -#include "stubs_freertos.h" -#include "stubs_logging.h" -#include "stubs_mutex.h" -#include "stubs_passert.h" -#include "stubs_serial.h" - -#include "fake_accessory.h" -#include "fake_new_timer.h" -#include "fake_smartstrap_profiles.h" -#include "fake_smartstrap_state.h" -#include "fake_system_task.h" - - -// Fakes -static struct { - bool enabled; - int after_bytes; -} s_faked_bus_contention; - -bool accessory_bus_contention_detected(void) { - return s_faked_bus_contention.enabled && (s_faked_bus_contention.after_bytes-- <= 0); -} - - -// Setup - -void test_smartstrap_comms__initialize(void) { - smartstrap_comms_init(); - s_faked_bus_contention.enabled = false; -} - -void test_smartstrap_comms__cleanup(void) { -} - - -// Helper functions - -static void prv_do_send(MBuf *write_mbuf, MBuf *read_mbuf, uint8_t *expect_data, - int expect_length) { - // send the data out the smartstrap port - smartstrap_fsm_state_reset(); - smartstrap_state_lock(); - SmartstrapResult result = smartstrap_send(SmartstrapProfileRawData, write_mbuf, read_mbuf, 1000); - smartstrap_state_unlock(); - cl_assert(result == SmartstrapResultOk); - if (read_mbuf) { - cl_assert(smartstrap_fsm_state_get() == SmartstrapStateReadInProgress); - } else { - cl_assert(smartstrap_fsm_state_get() == SmartstrapStateReadReady); - } - - // verify the data that was sent out the accessory port - uint8_t *buffer; - int length; - fake_accessory_get_buffer(&buffer, &length); - cl_assert(length == expect_length); - for (int i = 0; i < length; i++) { - cl_assert(buffer[i] == expect_data[i]); - } -} - -static void prv_do_send_bus_contention(MBuf *write_mbuf, MBuf *read_mbuf, uint8_t *expect_data, - int expect_length) { - const int BUS_CONTENTION_AFTER = 5; - // setup faked bus contention - s_faked_bus_contention.enabled = true; - s_faked_bus_contention.after_bytes = BUS_CONTENTION_AFTER; - // send the data out the smartstrap port - smartstrap_fsm_state_reset(); - smartstrap_state_lock(); - SmartstrapResult result = smartstrap_send(SmartstrapProfileRawData, write_mbuf, read_mbuf, 1000); - smartstrap_state_unlock(); - cl_assert(result == SmartstrapResultBusy); - cl_assert(smartstrap_fsm_state_get() == SmartstrapStateReadReady); - - // verify the data that was sent out the accessory port - uint8_t *buffer; - int length; - fake_accessory_get_buffer(&buffer, &length); - cl_assert(length == BUS_CONTENTION_AFTER + 1); - for (int i = 0; i < length; i++) { - cl_assert(buffer[i] == expect_data[i]); - } -} - -static void prv_do_read(uint8_t *data, int length, MBuf *read_mbuf, uint8_t *expect_data, - int expect_length) { - for (int i = 0; i < length; i++) { - smartstrap_handle_data_from_isr(data[i]); - } - fake_system_task_callbacks_invoke_pending(); - fake_smartstrap_profiles_check_read_params(true, SmartstrapProfileRawData, expect_length); - uint8_t *read_data = mbuf_get_data(read_mbuf); - int read_length = mbuf_get_length(read_mbuf); - cl_assert(read_length == expect_length); - for (int i = 0; i < read_length; i++) { - cl_assert(read_data[i] == expect_data[i]); - } -} - -static void prv_do_read_notify(uint8_t *data, int length) { - for (int i = 0; i < length; i++) { - smartstrap_handle_data_from_isr(data[i]); - } - fake_system_task_callbacks_invoke_pending(); - cl_assert(smartstrap_fsm_state_get() == SmartstrapStateReadReady); - fake_smartstrap_profiles_check_notify_params(true, SmartstrapProfileRawData); -} - - -// Tests - -void test_smartstrap_comms__send_receive_data(void) { - // write Mbuf - MBuf write_mbuf = MBUF_EMPTY; - uint8_t test_data[] = {0x00, 0x01}; - mbuf_set_data(&write_mbuf, test_data, sizeof(test_data)); - // read mbuf - MBuf read_mbuf = MBUF_EMPTY; - uint8_t read_data[sizeof(test_data)] = {0}; - mbuf_set_data(&read_mbuf, read_data, sizeof(read_data)); - // expected on-the-wire data for send - uint8_t expected[] = {0x7E, 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xEF, 0x7E}; - // faked on-the-wire data for response - uint8_t response_raw[] = {0x7E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x43, 0x7E}; - - // send the rquest - prv_do_send(&write_mbuf, &read_mbuf, expected, sizeof(expected)); - // process the fake response - prv_do_read(response_raw, sizeof(response_raw), &read_mbuf, test_data, sizeof(test_data)); -} - -void test_smartstrap_comms__send_receive_escaped_data(void) { - // write Mbuf - MBuf write_mbuf = MBUF_EMPTY; - uint8_t test_data[] = {0x7D, 0x7E, 0x00, 0x7E, 0x7D, 0x00}; - mbuf_set_data(&write_mbuf, test_data, sizeof(test_data)); - // read MBuf - MBuf read_mbuf = MBUF_EMPTY; - uint8_t read_data[sizeof(test_data)] = {0}; - mbuf_set_data(&read_mbuf, read_data, sizeof(read_data)); - // expected on-the-wire data from send - uint8_t send_raw[] = {0x7E, 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x7D, 0x5D, 0x7D, 0x5E, - 0x00, 0x7D, 0x5E, 0x7D, 0x5D, 0x00, 0x59, 0x7E}; - // faked on-the-wire data for response - uint8_t response_raw[] = {0x7E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x7D, 0x5D, 0x7D, 0x5E, - 0x00, 0x7D, 0x5E, 0x7D, 0x5D, 0x00, 0xC5, 0x7E}; - - // send the request - prv_do_send(&write_mbuf, &read_mbuf, send_raw, sizeof(send_raw)); - // process the fake response - prv_do_read(response_raw, sizeof(response_raw), &read_mbuf, test_data, sizeof(test_data)); -} - -void test_smartstrap_comms__send_data(void) { - // write Mbuf - MBuf write_mbuf = MBUF_EMPTY; - uint8_t test_data[] = {0x01, 0x11}; - mbuf_set_data(&write_mbuf, test_data, sizeof(test_data)); - // expected on-the-wire data from send - uint8_t send_raw[] = {0x7E, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x11, 0xCC, 0x7E}; - // send the write - prv_do_send(&write_mbuf, NULL, send_raw, sizeof(send_raw)); -} - -void test_smartstrap_comms__send_data_bus_contention(void) { - // write Mbuf - MBuf write_mbuf = MBUF_EMPTY; - uint8_t test_data[] = {0x01, 0x11}; - mbuf_set_data(&write_mbuf, test_data, sizeof(test_data)); - // expected on-the-wire data from send - uint8_t send_raw[] = {0x7E, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x11, 0xCC, 0x7E}; - // send the write - prv_do_send_bus_contention(&write_mbuf, NULL, send_raw, sizeof(send_raw)); -} - -void test_smartstrap_comms__send_receive_data_bus_contention(void) { - // write Mbuf - MBuf write_mbuf = MBUF_EMPTY; - uint8_t test_data[] = {0x01, 0x11}; - mbuf_set_data(&write_mbuf, test_data, sizeof(test_data)); - // read MBuf - MBuf read_mbuf = MBUF_EMPTY; - uint8_t read_data[sizeof(test_data)] = {0}; - mbuf_set_data(&read_mbuf, read_data, sizeof(read_data)); - // expected on-the-wire data from send - uint8_t send_raw[] = {0x7E, 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x11, 0xA8, 0x7E}; - // send the write - prv_do_send_bus_contention(&write_mbuf, &read_mbuf, send_raw, sizeof(send_raw)); -} - -void test_smartstrap_comms__notification(void) { - // faked on-the-wire data for the context frame - uint8_t notify_context_raw[] = {0x7E, 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x7E}; - - // send a break character - smartstrap_fsm_state_reset(); - smartstrap_handle_break_from_isr(); - fake_system_task_callbacks_invoke_pending(); - cl_assert(smartstrap_fsm_state_get() == SmartstrapStateNotifyInProgress); - - // process the fake context frame - prv_do_read_notify(notify_context_raw, sizeof(notify_context_raw)); -} diff --git a/tests/fw/services/test_timeline_item.c b/tests/fw/services/test_timeline_item.c index a9d26b8f9c..15678578f0 100644 --- a/tests/fw/services/test_timeline_item.c +++ b/tests/fw/services/test_timeline_item.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/timeline/item.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/item.h" #include "util/size.h" diff --git a/tests/fw/services/test_timezone_database.c b/tests/fw/services/test_timezone_database.c index 4192bab741..03431c392a 100644 --- a/tests/fw/services/test_timezone_database.c +++ b/tests/fw/services/test_timezone_database.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/timezone_database.h" -#include "services/common/clock.h" +#include "pbl/services/timezone_database.h" +#include "pbl/services/clock.h" #include "../timezone_fixture.auto.h" @@ -41,8 +28,11 @@ size_t resource_load_byte_range_system(ResAppNum app_num, uint32_t resource_id, void test_timezone_database__get_region_count(void) { // Note this test will break every time we update the timezone database and that's ok. Just - // make sure the new number is sane and update the expected number. - cl_assert_equal_i(timezone_database_get_region_count(), 336); + // make sure the new number is sane and update the expected number. The count is derived from + // the number of Zone entries in resources/normal/base/tzdata/timezones_olson.txt after the + // generator's filtering (build_zoneinfo_list); it dropped to 308 as the bundled tzdata has been + // updated since the original 336. + cl_assert_equal_i(timezone_database_get_region_count(), 308); } void test_timezone_database__find_region_by_name_simple(void) { @@ -125,6 +115,8 @@ void test_timezone_database__kazakhstan(void) { cl_assert(result); cl_assert_equal_i(tz_info.dst_id, 0); // No DST - cl_assert_equal_i(tz_info.tm_gmtoff, 6 * 60 * 60); // +6 hours + // Kazakhstan unified its time zones and moved from UTC+6 to UTC+5 on + // 2024-03-01 (tzdata 2024a); Asia/Almaty's current offset is now +5 hours. + cl_assert_equal_i(tz_info.tm_gmtoff, 5 * 60 * 60); // +5 hours } } diff --git a/tests/fw/services/test_touch.c b/tests/fw/services/test_touch.c index b79d9f416f..29ca356319 100644 --- a/tests/fw/services/test_touch.c +++ b/tests/fw/services/test_touch.c @@ -1,385 +1,299 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "applib/graphics/gtypes.h" #include "kernel/events.h" -#include "services/common/touch/touch.h" -#include "services/common/touch/touch_event.h" -#include "services/common/touch/touch_client.h" -#include "util/size.h" +#include "kernel/pebble_tasks.h" +#include "pbl/services/event_service.h" +#include "pbl/services/touch/touch.h" +#include "pbl/services/touch/touch_event.h" -#include -#include #include +#include #include "fake_events.h" // Stubs +#include "stubs_analytics.h" #include "stubs_logging.h" #include "stubs_mutex.h" #include "stubs_passert.h" -bool gpoint_equal(const GPoint * const point_a, const GPoint * const point_b) { - return (point_a->x == point_b->x && point_a->y == point_b->y); -} +void kernel_free(void *p) {} -void kernel_free(void *p) { +static EventServiceAddSubscriberCallback s_add_subscriber_cb; +static EventServiceRemoveSubscriberCallback s_remove_subscriber_cb; +void event_service_init(PebbleEventType type, EventServiceAddSubscriberCallback add_cb, + EventServiceRemoveSubscriberCallback remove_cb) { + cl_assert(type == PEBBLE_TOUCH_EVENT || type == PEBBLE_GESTURE_EVENT); + s_add_subscriber_cb = add_cb; + s_remove_subscriber_cb = remove_cb; } -extern TouchEvent *touch_event_queue_get_event(TouchIdx touch_idx, uint32_t queue_idx); -extern void touch_set_touch_state(TouchIdx touch_idx, TouchState touch_state, GPoint touch_down_pos, - uint64_t touch_down_time_ms, TouchPressure touch_down_pressure); +static int s_touch_sensor_enable_count; +static int s_touch_sensor_disable_count; +static bool s_touch_sensor_enabled; + +void touch_sensor_set_enabled(bool enabled) { + if (enabled) { + s_touch_sensor_enable_count++; + } else { + s_touch_sensor_disable_count++; + } + s_touch_sensor_enabled = enabled; +} // setup and teardown void test_touch__initialize(void) { fake_event_init(); + s_add_subscriber_cb = NULL; + s_remove_subscriber_cb = NULL; + s_touch_sensor_enable_count = 0; + s_touch_sensor_disable_count = 0; + s_touch_sensor_enabled = false; + touch_init(); touch_reset(); + // Make sure the global kill switch is reset between tests — it's a module + // static in touch.c and a failed test could otherwise leak its state. + touch_service_set_globally_enabled(true); } void test_touch__cleanup(void) { - } -void prv_test_touch_event(TouchEvent *touch_event, TouchIdx idx, TouchEventType type, GPoint *start_pos, - uint64_t start_time_ms, TouchPressure start_pressure, GPoint *diff_pos, - uint64_t diff_time_ms, TouchPressure diff_pressure, bool test_diff) { - cl_assert(touch_event); - cl_assert_equal_i(touch_event->type, type); - cl_assert_equal_i(touch_event->index, idx); - cl_assert_equal_i(touch_event->start_time_ms, start_time_ms); - cl_assert(gpoint_equal(&touch_event->start_pos, start_pos)); - cl_assert_equal_i(touch_event->start_pressure, start_pressure); - if (type != TouchEvent_Touchdown) { - cl_assert_equal_i(touch_event->diff_time_ms, diff_time_ms); - cl_assert(gpoint_equal(&touch_event->diff_pos, diff_pos)); - cl_assert_equal_i(touch_event->diff_pressure, diff_pressure); - } -} - -// tests -void test_touch__handle_update_touchdown(void) { - touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 100), 3, 3686400); +static void prv_assert_touch_event(TouchEventType type, int16_t x, int16_t y) { PebbleEvent event = fake_event_get_last(); cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_Touchdown, &GPoint(15, 100), 3686400, 3, - &GPointZero, 0, 0, true); + cl_assert_equal_i(event.touch.event.type, type); + cl_assert_equal_i(event.touch.event.x, x); + cl_assert_equal_i(event.touch.event.y, y); +} - touch_event = touch_event_queue_get_event(1, 0); - cl_assert_equal_p(touch_event, NULL); +// tests +void test_touch__touchdown(void) { + touch_handle_update(TouchState_FingerDown, 15, 100); + cl_assert_equal_i(fake_event_get_count(), 1); + prv_assert_touch_event(TouchEvent_Touchdown, 15, 100); +} - // Test second touch - touch_handle_update(1, TouchState_FingerDown, &GPoint(1, 13), 5, 3686401); - event = fake_event_get_last(); - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable); - touch_event = touch_event_queue_get_event(1, 0); - prv_test_touch_event(touch_event, 1, TouchEvent_Touchdown, &GPoint(1, 13), 3686401, 5, - &GPointZero, 0, 0, true); +void test_touch__liftoff(void) { + touch_handle_update(TouchState_FingerDown, 15, 100); + touch_handle_update(TouchState_FingerUp, 20, 120); + cl_assert_equal_i(fake_event_get_count(), 2); + prv_assert_touch_event(TouchEvent_Liftoff, 20, 120); } -void test_touch__handle_update_liftoff(void) { - // Test first touch - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerUp, &GPoint(15, 100), 0, 3686400); - PebbleEvent event = fake_event_get_last(); - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0, - &GPoint(15, 100), 20, 0, true); - - // Ensure nothing recorded for second touch - touch_event = touch_event_queue_get_event(1, 0); - cl_assert_equal_p(touch_event, NULL); - - // Test second touch - touch_set_touch_state(1, TouchState_FingerDown, GPointZero, 0, 0); - touch_handle_update(1, TouchState_FingerUp, &GPoint(1, 13), 0, 3686401); - event = fake_event_get_last(); - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable); - touch_event = touch_event_queue_get_event(1, 0); - prv_test_touch_event(touch_event, 1, TouchEvent_Liftoff, &GPointZero, 0, 0, &GPoint(1, 13), - 3686401, 0, true); +void test_touch__position_update(void) { + touch_handle_update(TouchState_FingerDown, 10, 10); + touch_handle_update(TouchState_FingerDown, 13, 13); + cl_assert_equal_i(fake_event_get_count(), 2); + prv_assert_touch_event(TouchEvent_PositionUpdate, 13, 13); + + touch_handle_update(TouchState_FingerDown, 18, 5); + cl_assert_equal_i(fake_event_get_count(), 3); + prv_assert_touch_event(TouchEvent_PositionUpdate, 18, 5); } -void test_touch__handle_update_liftoff_null_pos(void) { - touch_handle_update(0, TouchState_FingerDown, &GPoint(1, 13), 5, 3686400); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_Touchdown, &GPoint(1, 13), 3686400, 5, - &GPointZero, 0, 0, false); - touch_handle_update(0, TouchState_FingerUp, NULL, 0, 3686410); - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPoint(1, 13), 3686400, 5, &GPointZero, - 10, -5, true); +void test_touch__position_stationary(void) { + touch_handle_update(TouchState_FingerDown, 10, 10); + fake_event_reset_count(); + // No event generated when position is unchanged + touch_handle_update(TouchState_FingerDown, 10, 10); + cl_assert_equal_i(fake_event_get_count(), 0); } -void test_touch__handle_update_position(void) { - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400); - PebbleEvent event = fake_event_get_last(); - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(10, 10), 20, 5, true); +void test_touch__no_event_when_idle(void) { + // Liftoff while already up produces no event + touch_handle_update(TouchState_FingerUp, 0, 0); + cl_assert_equal_i(fake_event_get_count(), 0); +} +void test_touch__reset_clears_state(void) { + touch_handle_update(TouchState_FingerDown, 10, 10); + touch_reset(); fake_event_reset_count(); - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - cl_assert_equal_i(fake_event_get_count(), 0); // no event if previous one not handled - - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(13, 13), 40, 6, true); + // After reset, a FingerDown update should emit a Touchdown (not PositionUpdate) + touch_handle_update(TouchState_FingerDown, 50, 50); + cl_assert_equal_i(fake_event_get_count(), 1); + prv_assert_touch_event(TouchEvent_Touchdown, 50, 50); } -void test_touch__handle_update_position_stationary(void) { - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(10, 10), 20, 5, true); - // No touch event generated when finger remains stationary - touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686420); - touch_event = touch_event_queue_get_event(0, 1); - cl_assert_equal_p(touch_event, NULL); -} +void test_touch__subscriber_enables_sensor(void) { + cl_assert(s_add_subscriber_cb != NULL); + cl_assert(s_remove_subscriber_cb != NULL); -void test_touch__handle_update_merge_position(void) { - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(10, 10), 20, 5, true); - - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(13, 13), 40, 6, true); - - touch_handle_update(0, TouchState_FingerDown, &GPoint(18, 5), 1, 3686440); - // Test the same event (event at index 1): it should update to reflect the difference between this - // and the first event - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(18, 5), 60, 1, true); -} + s_add_subscriber_cb(PebbleTask_App); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); + cl_assert(s_touch_sensor_enabled); -void test_touch__handle_update_merge_liftoff(void) { - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(10, 10), 20, 5, true); - - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(13, 13), 40, 6, true); - - touch_handle_update(0, TouchState_FingerUp, &GPoint(18, 5), 0, 3686440); - // Test the same event (event at index 1): it should update to reflect the difference between this - // and the first event and that it is a liftoff event - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0, &GPoint(18, 5), - 60, 0, true); -} + // Additional subscribers don't re-enable the sensor + s_add_subscriber_cb(PebbleTask_App); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); -void test_touch__handle_update_merge_liftoff_null_pos(void) { - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerDown, &GPoint(10, 10), 5, 3686400); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(10, 10), 20, 5, true); - - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_PositionUpdate, &GPointZero, 3686380, 0, - &GPoint(13, 13), 40, 6, true); - - touch_handle_update(0, TouchState_FingerUp, NULL, 0, 3686440); - touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0, - &GPoint(13, 13), 60, 0, true); + // Sensor stays enabled until the last subscriber leaves + s_remove_subscriber_cb(PebbleTask_App); + cl_assert_equal_i(s_touch_sensor_disable_count, 0); + cl_assert(s_touch_sensor_enabled); + + s_remove_subscriber_cb(PebbleTask_App); + cl_assert_equal_i(s_touch_sensor_disable_count, 1); + cl_assert(!s_touch_sensor_enabled); } -void test_touch__assert_null_pos_not_liftoff(void) { - // NULL position not valid for touchdown event - cl_assert_passert(touch_handle_update(0, TouchState_FingerDown, NULL, 5, 3686400)); +void test_touch__backlight_toggles_sensor(void) { + touch_set_backlight_enabled(true); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); + cl_assert(s_touch_sensor_enabled); + + // Idempotent: enabling again is a no-op + touch_set_backlight_enabled(true); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 0, 0); - // NULL position not valid for position update event - cl_assert_passert(touch_handle_update(0, TouchState_FingerDown, NULL, 5, 3686400)); + touch_set_backlight_enabled(false); + cl_assert_equal_i(s_touch_sensor_disable_count, 1); + cl_assert(!s_touch_sensor_enabled); + + // Idempotent: disabling again is a no-op + touch_set_backlight_enabled(false); + cl_assert_equal_i(s_touch_sensor_disable_count, 1); } -void test_touch__handle_update_reset_queue_touchdown(void) { - touch_set_touch_state(0, TouchState_FingerDown, GPointZero, 3686380, 0); - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_handle_update(0, TouchState_FingerUp, &GPoint(15, 100), 0, 3686400); - TouchEvent *touch_event = touch_event_queue_get_event(0, 1); - prv_test_touch_event(touch_event, 0, TouchEvent_Liftoff, &GPointZero, 3686380, 0, - &GPoint(15, 100), 20, 0, true); - - // touchdown event should reset the touch event queue regardless of what is in it - touch_handle_update(0, TouchState_FingerDown, &GPoint(31, 1), 6, 3686500); - touch_event = touch_event_queue_get_event(0, 0); - prv_test_touch_event(touch_event, 0, TouchEvent_Touchdown, &GPoint(31, 1), 3686500, 6, - &GPointZero, 0, 0, true); - touch_event = touch_event_queue_get_event(0, 1); - cl_assert_equal_p(touch_event, NULL); +void test_touch__backlight_and_app_share_sensor(void) { + touch_set_backlight_enabled(true); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); + + // App subscription while backlight already holds the sensor: no extra enable + s_add_subscriber_cb(PebbleTask_App); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); + + // Dropping the backlight while an app subscriber remains keeps the sensor on + touch_set_backlight_enabled(false); + cl_assert_equal_i(s_touch_sensor_disable_count, 0); + cl_assert(s_touch_sensor_enabled); + + s_remove_subscriber_cb(PebbleTask_App); + cl_assert_equal_i(s_touch_sensor_disable_count, 1); + cl_assert(!s_touch_sensor_enabled); } -void test_touch__handle_update_pressure(void) { - //TODO: We're not passing pressure updates to the UI yet (not so useful?) +void test_touch__has_app_subscribers_app(void) { + cl_assert(!touch_has_app_subscribers()); + + s_add_subscriber_cb(PebbleTask_App); + cl_assert(touch_has_app_subscribers()); + + s_remove_subscriber_cb(PebbleTask_App); + cl_assert(!touch_has_app_subscribers()); } -typedef struct TouchEventContext { - TouchEvent touch_events[4]; - uint32_t idx; -} TouchEventContext; +void test_touch__has_app_subscribers_backlight(void) { + // The backlight subscription must not register as an app subscriber: the + // event loop uses touch_has_app_subscribers() as an override that fires the + // backlight on any gesture, so counting the backlight's own subscription + // would defeat the wake-gesture filter. + cl_assert(!touch_has_app_subscribers()); + + touch_set_backlight_enabled(true); + cl_assert(!touch_has_app_subscribers()); + + // With an app also subscribed, the call reflects the app, regardless of the + // backlight subscription state. + s_add_subscriber_cb(PebbleTask_App); + cl_assert(touch_has_app_subscribers()); + + touch_set_backlight_enabled(false); + cl_assert(touch_has_app_subscribers()); -static void prv_touch_event_dispatch_cb(const TouchEvent *event, void *context) { - TouchEventContext *ctx = context; - cl_assert(ctx->idx < ARRAY_LENGTH(ctx->touch_events)); - ctx->touch_events[ctx->idx++] = *event; + s_remove_subscriber_cb(PebbleTask_App); + cl_assert(!touch_has_app_subscribers()); } -void test_touch__dispatch_touch_events_single_finger(void) { - TouchEventContext ctx = { - .idx = 0 - }; - touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 0); - - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440); - touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 2); - prv_test_touch_event(&ctx.touch_events[0], 0, TouchEvent_Touchdown, &GPoint(13, 13), 3686420, 6, - NULL, 0, 0, false); - prv_test_touch_event(&ctx.touch_events[1], 0, TouchEvent_PositionUpdate, &GPoint(13, 13), 3686420, - 6, &GPoint(2, 2), 20, 0, true); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - cl_assert_equal_p(touch_event, NULL); +void test_touch__globally_enabled_default_true(void) { + // Default state after init is enabled; no setter call required. + cl_assert(touch_service_is_globally_enabled()); } -void test_touch__dispatch_touch_events_two_fingers(void) { - TouchEventContext ctx = { - .idx = 0 - }; - touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 0); - - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440); - touch_handle_update(1, TouchState_FingerDown, &GPoint(55, 55), 2, 3686480); - touch_handle_update(1, TouchState_FingerDown, &GPoint(33, 33), 7, 3686500); - touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 2); - prv_test_touch_event(&ctx.touch_events[0], 0, TouchEvent_Touchdown, &GPoint(13, 13), 3686420, 6, - NULL, 0, 0, false); - prv_test_touch_event(&ctx.touch_events[1], 0, TouchEvent_PositionUpdate, &GPoint(13, 13), 3686420, - 6, &GPoint(2, 2), 20, 0, true); - - touch_dispatch_touch_events(1, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 4); - prv_test_touch_event(&ctx.touch_events[2], 1, TouchEvent_Touchdown, &GPoint(55, 55), 3686480, 2, - NULL, 0, 0, false); - prv_test_touch_event(&ctx.touch_events[3], 1, TouchEvent_PositionUpdate, &GPoint(55, 55), - 3686480, 2, &GPoint(-22, -22), 20, 5, true); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - cl_assert_equal_p(touch_event, NULL); +void test_touch__global_disable_drops_events(void) { + touch_service_set_globally_enabled(false); + touch_handle_update(TouchState_FingerDown, 10, 20); + cl_assert_equal_i(fake_event_get_count(), 0); + + touch_handle_update(TouchState_FingerUp, 10, 20); + cl_assert_equal_i(fake_event_get_count(), 0); + + touch_service_set_globally_enabled(true); } -void test_touch__cancel_touches(void) { - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440); - touch_handle_update(1, TouchState_FingerDown, &GPoint(55, 55), 2, 3686480); - touch_handle_update(1, TouchState_FingerDown, &GPoint(33, 33), 7, 3686500); +void test_touch__global_disable_powers_down_sensor(void) { + // Subscriber brings the sensor up. + s_add_subscriber_cb(PebbleTask_App); + cl_assert(s_touch_sensor_enabled); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); - touch_handle_driver_event(TouchDriverEvent_ControllerError); - PebbleEvent event = fake_event_get_last(); - // Touches cancelled event generated - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesCancelled); + // Disabling globally powers the sensor down even though a subscriber remains. + touch_service_set_globally_enabled(false); + cl_assert(!s_touch_sensor_enabled); + cl_assert_equal_i(s_touch_sensor_disable_count, 1); + + // Re-enabling brings it back up for the existing subscriber. + touch_service_set_globally_enabled(true); + cl_assert(s_touch_sensor_enabled); + cl_assert_equal_i(s_touch_sensor_enable_count, 2); - // no more touches - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - cl_assert_equal_p(touch_event, NULL); - touch_event = touch_event_queue_get_event(1, 0); - cl_assert_equal_p(touch_event, NULL); + s_remove_subscriber_cb(PebbleTask_App); } -// test that the first dispatch after a cancel event is pended does not return any touches, even -// if new touches have arrived - this is to ensure that the valid new touches are not cancelled -// by the cancellation event if it is pended before previous touches -void test_touch__cancel_touches_handle_first_dispatch(void) { - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_handle_driver_event(TouchDriverEvent_ControllerError); - PebbleEvent event = fake_event_get_last(); - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesCancelled); - touch_handle_update(0, TouchState_FingerDown, &GPoint(15, 15), 6, 3686440); - // make sure that another event is, in fact, pended - event = fake_event_get_last(); - cl_assert_equal_i(event.type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(event.touch.type, PebbleTouchEvent_TouchesAvailable); - - TouchEventContext ctx = { - .idx = 0 - }; - // handle first TouchesAvailable event - touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 0); - - // handle second TouchesAvailable event - touch_dispatch_touch_events(0, prv_touch_event_dispatch_cb, &ctx); - cl_assert_equal_i(ctx.idx, 1); - prv_test_touch_event(&ctx.touch_events[0], 0, TouchEvent_Touchdown, &GPoint(15, 15), 3686440, 6, - NULL, 0, 0, false); +void test_touch__subscribe_while_disabled_keeps_sensor_off(void) { + touch_service_set_globally_enabled(false); + + // Subscribing while disabled must not power the sensor up. + s_add_subscriber_cb(PebbleTask_App); + cl_assert(!s_touch_sensor_enabled); + cl_assert_equal_i(s_touch_sensor_enable_count, 0); + + // Re-enabling globally powers it up for the pre-existing subscriber. + touch_service_set_globally_enabled(true); + cl_assert(s_touch_sensor_enabled); + cl_assert_equal_i(s_touch_sensor_enable_count, 1); + + s_remove_subscriber_cb(PebbleTask_App); } -static PebbleEvent s_expected_palm_events[2]; -static int s_palm_event_count = 0; +void test_touch__global_disable_drops_gestures(void) { + // Gestures (tap/double-tap) must honor the kill switch too, not just the + // finger up/down updates. + touch_service_set_globally_enabled(false); + + touch_handle_gesture(TouchGesture_Tap, 10, 20); + cl_assert_equal_i(fake_event_get_count(), 0); + + touch_handle_gesture(TouchGesture_DoubleTap, 30, 40); + cl_assert_equal_i(fake_event_get_count(), 0); -static void prv_handle_palm_events(PebbleEvent *e) { - s_expected_palm_events[s_palm_event_count++] = *e; + touch_service_set_globally_enabled(true); } -void test_touch__palm_detect_event(void) { - touch_handle_update(0, TouchState_FingerDown, &GPoint(13, 13), 6, 3686420); - touch_handle_update(1, TouchState_FingerDown, &GPoint(55, 55), 2, 3686480); +void test_touch__gestures_delivered_when_enabled(void) { + touch_handle_gesture(TouchGesture_Tap, 10, 20); + cl_assert_equal_i(fake_event_get_count(), 1); + + PebbleEvent event = fake_event_get_last(); + cl_assert_equal_i(event.type, PEBBLE_GESTURE_EVENT); + cl_assert_equal_i(event.gesture.event.type, GestureEvent_Tap); +} - fake_event_set_callback(prv_handle_palm_events); - touch_handle_driver_event(TouchDriverEvent_PalmDetect); +void test_touch__global_disable_sleeps_unsubscribed_sensor(void) { + // The driver can re-arm the sensor outside the service's subscriber + // bookkeeping (e.g. its I2C error-recovery path). Disabling globally must + // still put the chip to sleep, even when s_subscriber_count is 0. + s_touch_sensor_enabled = true; - // Cancelled event, followed by a palm detection event - cl_assert_equal_i(s_expected_palm_events[0].type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(s_expected_palm_events[0].touch.type, PebbleTouchEvent_TouchesCancelled); - cl_assert_equal_i(s_expected_palm_events[1].type, PEBBLE_TOUCH_EVENT); - cl_assert_equal_i(s_expected_palm_events[1].touch.type, PebbleTouchEvent_PalmDetected); + touch_service_set_globally_enabled(false); + cl_assert(!s_touch_sensor_enabled); + cl_assert(s_touch_sensor_disable_count >= 1); - TouchEvent *touch_event = touch_event_queue_get_event(0, 0); - cl_assert_equal_p(touch_event, NULL); - touch_event = touch_event_queue_get_event(1, 0); - cl_assert_equal_p(touch_event, NULL); + touch_service_set_globally_enabled(true); } diff --git a/tests/fw/services/test_transcription.c b/tests/fw/services/test_transcription.c index 5ce6e7fe40..30d1ebb699 100644 --- a/tests/fw/services/test_transcription.c +++ b/tests/fw/services/test_transcription.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/voice/transcription.h" +#include "pbl/services/voice/transcription.h" #include "test_transcription_example.h" diff --git a/tests/fw/services/test_transcription_example.h b/tests/fw/services/test_transcription_example.h index 326b5477cf..75fe0e8dce 100644 --- a/tests/fw/services/test_transcription_example.h +++ b/tests/fw/services/test_transcription_example.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ static uint8_t s_test_transcription_example[] = { diff --git a/tests/fw/services/test_vibe.c b/tests/fw/services/test_vibe.c index f6a3051694..472ff35e41 100644 --- a/tests/fw/services/test_vibe.c +++ b/tests/fw/services/test_vibe.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -27,7 +14,7 @@ #include "stubs_passert.h" #include "stubs_analytics.h" -#include "services/common/vibe_pattern.h" +#include "pbl/services/vibe_pattern.h" #include "applib/ui/vibes.h" #include "util/size.h" @@ -42,9 +29,22 @@ bool sys_vibe_history_was_vibrating(uint64_t time_search); int32_t sys_vibe_get_vibe_strength(void); //stub +static bool s_vibe_on = false; +static int s_vibe_ctl_count = 0; void vibe_ctl(bool on) { + s_vibe_on = on; + s_vibe_ctl_count++; } + +static int8_t s_last_strength_set = 0; +static int s_strength_set_count = 0; void vibe_set_strength(int8_t strength) { + s_last_strength_set = strength; + s_strength_set_count++; +} + +bool shell_prefs_get_vibe_log_info_enabled(void) { + return false; } //helpers @@ -55,10 +55,22 @@ static uint64_t prv_get_current_time() { return ((uint64_t)s * 1000) + ms; } +//! Snap the fake RTC's tick counter to the smallest value whose floored +//! conversion `(ticks * 1000) / RTC_TICKS_HZ` matches the current wall ms. +//! vibe_pattern's drift-compensating scheduler runs that floor on the elapsed +//! tick delta; if we leave ticks unaligned, each step's converted elapsed_ms +//! drifts by ±1 from the wall clock the timer fires against and `prv_confirm_history` +//! starts mis-attributing segment boundaries. +static void prv_snap_ticks_to_wall(void) { + const uint64_t wall_ms = prv_get_current_time(); + fake_rtc_set_ticks((wall_ms * RTC_TICKS_HZ + 999) / 1000); +} + static void prv_run_vibes() { TimerID timer = stub_new_timer_get_next(); while (timer != TIMER_INVALID_ID) { fake_rtc_increment_time_ms(stub_new_timer_timeout(timer)); + prv_snap_ticks_to_wall(); stub_new_timer_fire(timer); timer = stub_new_timer_get_next(); } @@ -83,6 +95,11 @@ static bool prv_confirm_history(const VibePattern pattern, int64_t start_time) { void test_vibe__initialize(void) { vibes_init(); fake_rtc_init(0, 100); + prv_snap_ticks_to_wall(); + s_last_strength_set = 0; + s_strength_set_count = 0; + s_vibe_on = false; + s_vibe_ctl_count = 0; } @@ -134,3 +151,128 @@ void test_vibe__check_vibe_history_multiple(void) { cl_assert(prv_confirm_history(custom_pattern_2, time_start_2)); sys_vibe_history_stop_collecting(); } + +void test_vibe__custom_pattern_with_amplitudes(void) { + const uint32_t durations[] = { 200, 100, 400 }; + const uint32_t amplitudes[] = { 80, 50, 20 }; + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = amplitudes, + .num_segments = ARRAY_LENGTH(durations), + }; + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + prv_run_vibes(); + // Pattern engine turns motor off via vibe_ctl(false) at end + cl_assert_equal_b(s_vibe_on, false); + // All 3 segments set strength (no alternation — every segment has an amplitude) + cl_assert(s_strength_set_count >= 3); +} + +void test_vibe__custom_pattern_with_amplitudes_clamped(void) { + const uint32_t durations[] = { 100 }; + const uint32_t amplitudes[] = { 200 }; // exceeds 100, should be clamped + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = amplitudes, + .num_segments = ARRAY_LENGTH(durations), + }; + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + prv_run_vibes(); + cl_assert_equal_b(s_vibe_on, false); + // Clamped to 100, verify it was set + cl_assert_equal_i(s_last_strength_set, 100); +} + +void test_vibe__custom_pattern_with_null_amplitudes(void) { + const uint32_t durations[] = { 100 }; + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = NULL, + .num_segments = ARRAY_LENGTH(durations), + }; + // Should return without crashing (early return on null amplitudes) + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + cl_assert_equal_i(s_strength_set_count, 0); + cl_assert_equal_i(s_vibe_ctl_count, 0); +} + +void test_vibe__custom_pattern_with_amplitudes_null_durations(void) { + const uint32_t amplitudes[] = { 80 }; + const VibePatternWithAmplitudes pattern = { + .durations = NULL, + .amplitudes = amplitudes, + .num_segments = 1, + }; + // Should return without crashing (early return on null durations) + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + cl_assert_equal_i(s_strength_set_count, 0); + cl_assert_equal_i(s_vibe_ctl_count, 0); +} + +void test_vibe__custom_pattern_with_amplitudes_single(void) { + const uint32_t durations[] = { 300 }; + const uint32_t amplitudes[] = { 50 }; + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = amplitudes, + .num_segments = 1, + }; + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + prv_run_vibes(); + cl_assert_equal_b(s_vibe_on, false); + cl_assert_equal_i(s_last_strength_set, 50); +} + +void test_vibe__custom_pattern_with_zero_amplitude(void) { + const uint32_t durations[] = { 200, 100, 300 }; + const uint32_t amplitudes[] = { 0, 0, 100 }; + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = amplitudes, + .num_segments = ARRAY_LENGTH(durations), + }; + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + prv_run_vibes(); + cl_assert_equal_b(s_vibe_on, false); + // Amplitude 0 segments use vibe_ctl(false), not vibe_set_strength(0), + // so only the non-zero segment (100) calls vibe_set_strength + cl_assert(s_strength_set_count >= 1); + // Last segment had amplitude 100 + cl_assert_equal_i(s_last_strength_set, 100); +} + +void test_vibe__custom_pattern_with_amplitudes_verifies_strength(void) { + const uint32_t durations[] = { 100, 50, 100 }; + const uint32_t amplitudes[] = { 75, 50, 25 }; + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = amplitudes, + .num_segments = ARRAY_LENGTH(durations), + }; + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + prv_run_vibes(); + // vibe_set_strength called for all 3 segments (75, 50, 25) + cl_assert(s_strength_set_count >= 3); + // Last strength set was 25 (third segment) + cl_assert_equal_i(s_last_strength_set, 25); + // Motor is off after pattern completes + cl_assert_equal_b(s_vibe_on, false); +} + +void test_vibe__custom_pattern_ramp_down(void) { + const uint32_t durations[] = { 200, 200, 200, 200 }; + const uint32_t amplitudes[] = { 100, 75, 50, 25 }; + const VibePatternWithAmplitudes pattern = { + .durations = durations, + .amplitudes = amplitudes, + .num_segments = ARRAY_LENGTH(durations), + }; + vibes_enqueue_custom_pattern_with_amplitudes(pattern); + prv_run_vibes(); + // All 4 segments set strength + cl_assert(s_strength_set_count >= 4); + // Last strength was 25 + cl_assert_equal_i(s_last_strength_set, 25); + // Motor is off after pattern completes + cl_assert_equal_b(s_vibe_on, false); +} diff --git a/tests/fw/services/test_vibe_intensity.c b/tests/fw/services/test_vibe_intensity.c index 9711a02d25..23afcc8740 100644 --- a/tests/fw/services/test_vibe_intensity.c +++ b/tests/fw/services/test_vibe_intensity.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/vibes/vibe_intensity.h" +#include "pbl/services/vibes/vibe_intensity.h" // Stubs ///////// diff --git a/tests/fw/services/test_vibe_score.c b/tests/fw/services/test_vibe_score.c index 54e791b50f..e369305f6c 100644 --- a/tests/fw/services/test_vibe_score.c +++ b/tests/fw/services/test_vibe_score.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/normal/vibes/vibe_score.h" +#include "pbl/services/vibes/vibe_score.h" #include "clar.h" #include "string.h" diff --git a/tests/fw/services/test_vibe_score_info.c b/tests/fw/services/test_vibe_score_info.c index d5af88f387..94efe049b6 100644 --- a/tests/fw/services/test_vibe_score_info.c +++ b/tests/fw/services/test_vibe_score_info.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/vibes/vibe_score_info.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/vibes/vibe_score_info.h" #include "clar.h" #include "resource/resource_ids.auto.h" @@ -96,9 +83,10 @@ void test_vibe_score_info__cycle_next_alarms(void) { VibeScoreId_Jackhammer, VibeScoreId_Reveille, VibeScoreId_Mario, + VibeScoreId_Gentle, }; - prv_test_cycle_next(VibeClient_Alarms, alarm_scores, 7, VibeScoreId_NudgeNudge, 3); + prv_test_cycle_next(VibeClient_Alarms, alarm_scores, 8, VibeScoreId_NudgeNudge, 3); } void test_vibe_score_info__is_valid_true_for_valid_score(void) { diff --git a/tests/fw/services/test_voice_endpoint.c b/tests/fw/services/test_voice_endpoint.c index ce0063c784..2c4f17a4a4 100644 --- a/tests/fw/services/test_voice_endpoint.c +++ b/tests/fw/services/test_voice_endpoint.c @@ -1,26 +1,13 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/voice/transcription.h" -#include "services/normal/voice_endpoint.h" -#include "services/normal/audio_endpoint.h" +#include "pbl/services/voice/transcription.h" +#include "pbl/services/voice_endpoint.h" +#include "pbl/services/audio_endpoint.h" -#include "services/normal/voice_endpoint_private.h" +#include "pbl/services/voice_endpoint_private.h" #include "fake_session.h" #include "fake_system_task.h" diff --git a/tests/fw/services/test_wakeup.c b/tests/fw/services/test_wakeup.c index 06df798a23..3b17977adf 100644 --- a/tests/fw/services/test_wakeup.c +++ b/tests/fw/services/test_wakeup.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include -#include "services/normal/wakeup.h" +#include "pbl/services/wakeup.h" #include "syscall/syscall.h" #include "flash_region/flash_region.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/common/event_service.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/event_service.h" #include "process_management/app_install_manager.h" #include "clar.h" @@ -306,7 +293,9 @@ void test_wakeup__handle_clock_change_scheduled_jump(void) { // Jump the remainder plus an offset (missing the event) rtc_set_time(sys_get_time() + final_timeout / 1000 + time_jump_seconds); - wakeup_handle_clock_change(); + // A large jump that misses an event is a significant clock change: that is + // the path that deletes past events and raises the missed-event popup. + wakeup_handle_significant_clock_change(); // There should be a missed wakeup event and a popup displayed cl_assert_equal_b(s_popup_occurred, true); diff --git a/tests/fw/services/test_weather_service.c b/tests/fw/services/test_weather_service.c index 7366156802..d4f907b626 100644 --- a/tests/fw/services/test_weather_service.c +++ b/tests/fw/services/test_weather_service.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -20,12 +7,12 @@ #include "applib/event_service_client.h" #include "kernel/events.h" -#include "services/common/comm_session/session_remote_version.h" -#include "services/normal/blob_db/weather_db.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/weather/weather_service.h" -#include "services/normal/weather/weather_service_private.h" -#include "services/normal/weather/weather_types.h" +#include "pbl/services/comm_session/session_remote_version.h" +#include "pbl/services/blob_db/weather_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/weather/weather_service.h" +#include "pbl/services/weather/weather_service_private.h" +#include "pbl/services/weather/weather_types.h" #include "util/pstring.h" // Fixture diff --git a/tests/fw/services/timeline/test_alarm_layout.c b/tests/fw/services/timeline/test_alarm_layout.c index 7c5765131e..0f8d069d6d 100644 --- a/tests/fw/services/timeline/test_alarm_layout.c +++ b/tests/fw/services/timeline/test_alarm_layout.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/common/clock.h" -#include "services/normal/alarms/alarm.h" -#include "services/normal/timeline/alarm_layout.h" -#include "services/normal/timeline/attribute.h" +#include "pbl/services/clock.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/timeline/alarm_layout.h" +#include "pbl/services/timeline/attribute.h" // Stubs //////////////////////////////////////////////////////////////// @@ -41,6 +28,7 @@ #include "stubs_rtc.h" #include "stubs_settings_file.h" #include "stubs_system_task.h" +#include "stubs_text_node.h" #include "stubs_timeline_event.h" #include "stubs_timeline_layout.h" diff --git a/tests/fw/services/timeline/test_attribute.c b/tests/fw/services/timeline/test_attribute.c index cc7bfb9f37..66af4aede4 100644 --- a/tests/fw/services/timeline/test_attribute.c +++ b/tests/fw/services/timeline/test_attribute.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/timeline/attribute.h" +#include "pbl/services/timeline/attribute.h" #include "util/size.h" #include diff --git a/tests/fw/services/timeline/test_calendar.c b/tests/fw/services/timeline/test_calendar.c index 5fad139895..141ecf649c 100644 --- a/tests/fw/services/timeline/test_calendar.c +++ b/tests/fw/services/timeline/test_calendar.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/events.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/calendar.h" -#include "services/normal/timeline/event.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/calendar.h" +#include "pbl/services/timeline/event.h" +#include "pbl/services/timeline/timeline.h" #include "system/logging.h" #include "clar.h" diff --git a/tests/fw/services/timeline/test_reminders.c b/tests/fw/services/timeline/test_reminders.c index 7be90c0fda..844e07f905 100644 --- a/tests/fw/services/timeline/test_reminders.c +++ b/tests/fw/services/timeline/test_reminders.c @@ -1,23 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/timeline/reminders.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/reminders.h" #include "kernel/events.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/blob_db/reminder_db.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/regular_timer.h" #include "clar.h" @@ -27,9 +16,9 @@ // Fakes //////////////////////////////////////////////////////////////// -#include "fake_new_timer.h" #include "fake_pbl_malloc.h" #include "fake_pebble_tasks.h" +#include "fake_regular_timer.h" #include "fake_spi_flash.h" #include "fake_system_task.h" #include "stubs_layout_layer.h" @@ -58,16 +47,31 @@ void event_put(PebbleEvent* event) { // Stubs //////////////////////////////////////////////////////////////// #include "stubs_analytics.h" +#include "stubs_blob_db_sync.h" +#include "stubs_blob_db_sync_util.h" +#include "stubs_crc.h" #include "stubs_hexdump.h" #include "stubs_logging.h" #include "stubs_mutex.h" #include "stubs_passert.h" +#include "stubs_pin_db.h" #include "stubs_prompt.h" -#include "stubs_regular_timer.h" +#include "stubs_rand_ptr.h" #include "stubs_sleep.h" #include "stubs_task_watchdog.h" -extern TimerID get_reminder_timer_id(void); +extern RegularTimerInfo *get_reminder_timer(void); +extern bool get_reminder_armed(void); +extern time_t get_reminder_timestamp(void); +extern ReminderId *get_reminder_id(void); + +// Drives the seconds-callback the same way the regular_timer service would. +// system_task_add_callback is faked to enqueue, so we then drain the queue. +static void prv_advance_to_and_fire(time_t target) { + now = target; + fake_regular_timer_trigger(get_reminder_timer()); + fake_system_task_callbacks_invoke(1); +} static TimelineItem item1 = { .header = { @@ -120,6 +124,9 @@ void test_reminders__initialize(void) { pfs_init(false); reminder_db_init(); + // Register the seconds callback so fake_regular_timer_trigger() can drive it. + cl_assert_equal_i(reminders_init(), S_SUCCESS); + // add all four explicitly out of order cl_assert(S_SUCCESS == reminders_insert(&item4)); @@ -138,55 +145,43 @@ void test_reminders__cleanup(void) { //////////////////////////////////////////////////////////////// void test_reminders__timer_test(void) { - cl_assert(stub_new_timer_timeout(get_reminder_timer_id()) == 0); - cl_assert(memcmp(&item1.header.id, stub_new_timer_callback_data(get_reminder_timer_id()), sizeof(TimelineItemId)) == 0); - stub_pebble_tasks_set_current(PebbleTask_NewTimers); - cl_assert(stub_new_timer_fire(get_reminder_timer_id())); - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - fake_system_task_callbacks_invoke(1); + // item1 (timestamp 0) is next; we're at now=0 so it should fire immediately. + cl_assert(get_reminder_armed()); + cl_assert_equal_i(get_reminder_timestamp(), 0); + cl_assert_equal_m(&item1.header.id, get_reminder_id(), sizeof(TimelineItemId)); + prv_advance_to_and_fire(0); cl_assert_equal_i(num_events_put, 1); - // item 2 is now the top reminder... - cl_assert_equal_m(&item2.header.id, stub_new_timer_callback_data(get_reminder_timer_id()), sizeof(TimelineItemId)); - cl_assert(stub_new_timer_timeout(get_reminder_timer_id()) == 100 * 1000); - // ...until we insert item 1 back + // item2 (timestamp 100) is now next. + cl_assert(get_reminder_armed()); + cl_assert_equal_m(&item2.header.id, get_reminder_id(), sizeof(TimelineItemId)); + cl_assert_equal_i(get_reminder_timestamp(), 100); + + // ...until we insert item 1 back; it has timestamp 0 (in the past). cl_assert(S_SUCCESS == reminders_insert(&item1)); - cl_assert_equal_m(&item1.header.id, stub_new_timer_callback_data(get_reminder_timer_id()), sizeof(TimelineItemId)); - cl_assert(stub_new_timer_timeout(get_reminder_timer_id()) == 0); - stub_pebble_tasks_set_current(PebbleTask_NewTimers); - cl_assert(stub_new_timer_fire(get_reminder_timer_id())); - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - fake_system_task_callbacks_invoke(1); + cl_assert(get_reminder_armed()); + cl_assert_equal_m(&item1.header.id, get_reminder_id(), sizeof(TimelineItemId)); + cl_assert_equal_i(get_reminder_timestamp(), 0); + prv_advance_to_and_fire(0); cl_assert_equal_i(num_events_put, 2); - cl_assert_equal_m(&item2.header.id, stub_new_timer_callback_data(get_reminder_timer_id()), sizeof(TimelineItemId)); - cl_assert(stub_new_timer_timeout(get_reminder_timer_id()) == 100 * 1000); - now = 100; - stub_pebble_tasks_set_current(PebbleTask_NewTimers); - cl_assert(stub_new_timer_fire(get_reminder_timer_id())); - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - fake_system_task_callbacks_invoke(1); + cl_assert_equal_m(&item2.header.id, get_reminder_id(), sizeof(TimelineItemId)); + cl_assert_equal_i(get_reminder_timestamp(), 100); + prv_advance_to_and_fire(100); cl_assert_equal_i(num_events_put, 3); - cl_assert_equal_m(&item3.header.id, stub_new_timer_callback_data(get_reminder_timer_id()), sizeof(TimelineItemId)); - cl_assert(stub_new_timer_timeout(get_reminder_timer_id()) == 200 * 1000); - now += 200; - stub_pebble_tasks_set_current(PebbleTask_NewTimers); - cl_assert(stub_new_timer_fire(get_reminder_timer_id())); - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - fake_system_task_callbacks_invoke(1); + cl_assert_equal_m(&item3.header.id, get_reminder_id(), sizeof(TimelineItemId)); + cl_assert_equal_i(get_reminder_timestamp(), 300); + prv_advance_to_and_fire(300); cl_assert_equal_i(num_events_put, 4); - cl_assert_equal_m(&item4.header.id, stub_new_timer_callback_data(get_reminder_timer_id()), sizeof(TimelineItemId)); - cl_assert(stub_new_timer_timeout(get_reminder_timer_id()) == 1037 * 1000); - now += 1037; - stub_pebble_tasks_set_current(PebbleTask_NewTimers); - cl_assert(stub_new_timer_fire(get_reminder_timer_id())); - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - fake_system_task_callbacks_invoke(1); + cl_assert_equal_m(&item4.header.id, get_reminder_id(), sizeof(TimelineItemId)); + cl_assert_equal_i(get_reminder_timestamp(), 1337); + prv_advance_to_and_fire(1337); cl_assert_equal_i(num_events_put, 5); - cl_assert(!new_timer_scheduled(get_reminder_timer_id(), NULL)); + // No more reminders pending. + cl_assert(!get_reminder_armed()); } void test_reminders__first_init_test(void) { @@ -194,6 +189,26 @@ void test_reminders__first_init_test(void) { test_reminders__timer_test(); } +// Verifies the early-fire case: callback runs when RTC has not yet reached the +// reminder timestamp -> nothing should be enqueued. +void test_reminders__not_ready_yet(void) { + cl_assert_equal_i(reminders_init(), 0); + // item1 (ts=0) fires immediately. Drain it. + prv_advance_to_and_fire(0); + cl_assert_equal_i(num_events_put, 1); + + // item2 has ts=100; before that, tick should do nothing. + now = 50; + fake_regular_timer_trigger(get_reminder_timer()); + fake_system_task_callbacks_invoke(1); + cl_assert_equal_i(num_events_put, 1); + cl_assert(get_reminder_armed()); + cl_assert_equal_i(get_reminder_timestamp(), 100); + + prv_advance_to_and_fire(100); + cl_assert_equal_i(num_events_put, 2); +} + static TimelineItem s_stale_reminder = { .header = { .id = {0x3C, 0xAF, 0x17, 0xD5, 0xBE, 0x15, 0x4B, 0xFD, 0xAE, 0x2A, @@ -211,15 +226,14 @@ void test_reminders__stale_item_insert(void) { void test_reminders__stale_item_init(void) { cl_assert_equal_i(reminders_insert(&s_stale_reminder), S_SUCCESS); - stub_new_timer_stop(get_reminder_timer_id()); now = 1 * 60 * 60; reminders_init(); - cl_assert(new_timer_scheduled(get_reminder_timer_id(), NULL)); + cl_assert(get_reminder_armed()); now = 3 * 60 * 60; reminders_init(); - cl_assert(!new_timer_scheduled(get_reminder_timer_id(), NULL)); + cl_assert(!get_reminder_armed()); } static TimezoneInfo s_tz = { @@ -257,19 +271,17 @@ void test_reminders__all_day(void) { // set time to 16:00 PST March 4 now = 1425513600; reminders_init(); - cl_assert_equal_i(stub_new_timer_timeout(get_reminder_timer_id()), 5 * 60 * 60 * 1000); + cl_assert(get_reminder_armed()); + cl_assert_equal_i(get_reminder_timestamp(), 1425531600); cl_assert(uuid_equal(&s_reminder_before_all_day_reminder.header.id, - (Uuid *)stub_new_timer_callback_data(get_reminder_timer_id()))); + (Uuid *)get_reminder_id())); // set time to 21:00 PST March 4 - now = 1425531600; - stub_pebble_tasks_set_current(PebbleTask_NewTimers); - cl_assert(stub_new_timer_fire(get_reminder_timer_id())); - stub_pebble_tasks_set_current(PebbleTask_KernelBackground); - fake_system_task_callbacks_invoke(1); + prv_advance_to_and_fire(1425531600); - cl_assert_equal_i(stub_new_timer_timeout(get_reminder_timer_id()), (2 * 60 + 30) * 60 * 1000); + // s_all_day_reminder is next. + cl_assert(get_reminder_armed()); cl_assert(uuid_equal(&s_all_day_reminder.header.id, - (Uuid *)stub_new_timer_callback_data(get_reminder_timer_id()))); + (Uuid *)get_reminder_id())); } void test_reminders__stale_all_day(void) { @@ -284,21 +296,3 @@ void test_reminders__stale_all_day(void) { // since it "seems" to be timestamped at 15:30 PST, but it should be accepted cl_assert_equal_i(reminders_insert(&s_all_day_reminder), S_SUCCESS); } - -void test_reminders__calculate_snooze_time(void) { - // Test half-time snooze - now = 0; - cl_assert_equal_i(50, reminders_calculate_snooze_time(&item2)); - now = 50; - cl_assert_equal_i(25, reminders_calculate_snooze_time(&item2)); - - // Test constant snooze - now = 80; - cl_assert_equal_i(600, reminders_calculate_snooze_time(&item2)); - now = 100 + 48 * 60 * 60; - cl_assert_equal_i(600, reminders_calculate_snooze_time(&item2)); - - // Test no snooze - now = 100 + 48 * 60 * 60 + 1; - cl_assert_equal_i(0, reminders_calculate_snooze_time(&item2)); -} diff --git a/tests/fw/services/timeline/test_timeline_layouts.c b/tests/fw/services/timeline/test_timeline_layouts.c index 8904a53ba2..f266eb089f 100644 --- a/tests/fw/services/timeline/test_timeline_layouts.c +++ b/tests/fw/services/timeline/test_timeline_layouts.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/system_apps/timeline/pin_window.h" -#include "services/normal/timeline/weather_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/system/timeline/pin_window.h" +#include "pbl/services/timeline/weather_layout.h" #include "clar.h" @@ -69,6 +56,7 @@ void clock_get_since_time(char *buffer, int buf_size, time_t timestamp) { ///////////////////// #include "stubs_action_menu.h" +#include "stubs_alerts_preferences.h" #include "stubs_analytics.h" #include "stubs_animation_timing.h" #include "stubs_app_install_manager.h" @@ -99,6 +87,7 @@ void clock_get_since_time(char *buffer, int buf_size, time_t timestamp) { #include "stubs_timeline_item.h" #include "stubs_timeline_layer.h" #include "stubs_timeline_peek.h" +#include "stubs_vibes.h" #include "stubs_window_manager.h" #include "stubs_window_stack.h" diff --git a/tests/fw/services/timeline/test_timeline_model.c b/tests/fw/services/timeline/test_timeline_model.c index fa38ad7de7..c942065493 100644 --- a/tests/fw/services/timeline/test_timeline_model.c +++ b/tests/fw/services/timeline/test_timeline_model.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "util/uuid.h" -#include "services/normal/filesystem/pfs.h" -#include "services/common/regular_timer.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/timeline.h" -#include "apps/system_apps/timeline/timeline_model.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/regular_timer.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/timeline.h" +#include "apps/system/timeline/model.h" #include "util/size.h" // Fixture diff --git a/tests/fw/services/timeline/test_timeline_peek_event.c b/tests/fw/services/timeline/test_timeline_peek_event.c index 8c17a3d7b5..efa2077cc0 100644 --- a/tests/fw/services/timeline/test_timeline_peek_event.c +++ b/tests/fw/services/timeline/test_timeline_peek_event.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/events.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/event.h" -#include "services/normal/timeline/peek.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/event.h" +#include "pbl/services/timeline/peek.h" +#include "pbl/services/timeline/timeline.h" #include "system/logging.h" #include "clar.h" @@ -563,7 +550,7 @@ void test_timeline_peek_event__dismiss_event(void) { void test_timeline_peek_event__first_event_with_past_event(void) { TimelineItem item = DEFINE_EVENT( .id = 0x01, .timestamp = 20 * SECONDS_PER_MINUTE, .duration = 70 ); - TimelineItem UNUSED item2 = + TimelineItem PBL_UNUSED item2 = DEFINE_EVENT( .id = 0x02, .timestamp = -50 * SECONDS_PER_MINUTE, .duration = 30 ); unsigned int timeout_s = item.header.timestamp - TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S; CHECK_EVENT( .count = 3, .item_id = item.header.id, .num_concurrent = 0, @@ -575,7 +562,7 @@ void test_timeline_peek_event__first_event_with_all_day_event_before(void) { // All day events show up if no timed event has yet passed TimelineItem item = DEFINE_EVENT( .id = 0x01, .timestamp = 20 * SECONDS_PER_MINUTE, .duration = 70 ); - TimelineItem UNUSED item2 = + TimelineItem PBL_UNUSED item2 = DEFINE_EVENT( .id = 0x02, .timestamp = 0, .duration = MINUTES_PER_DAY, .all_day = true ); unsigned int timeout_s = item.header.timestamp - TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S; CHECK_EVENT( .count = 3, .item_id = item.header.id, .num_concurrent = 0, @@ -589,9 +576,9 @@ void test_timeline_peek_event__first_event_with_all_day_event_after(void) { TimelineItem item = DEFINE_EVENT( .id = 0x01, .timestamp = SECONDS_PER_HOUR + 20 * SECONDS_PER_MINUTE, .duration = 70 ); - TimelineItem UNUSED item2 = + TimelineItem PBL_UNUSED item2 = DEFINE_EVENT( .id = 0x02, .timestamp = 0, .duration = MINUTES_PER_DAY, .all_day = true ); - TimelineItem UNUSED item3 = + TimelineItem PBL_UNUSED item3 = DEFINE_EVENT( .id = 0x03, .timestamp = 0, .duration = 10 ); unsigned int timeout_s = 600; CHECK_EVENT( .count = 4, .item_id = item.header.id, .num_concurrent = 0, @@ -738,7 +725,12 @@ void test_timeline_peek_event__one_persistent_event_lifecycle(void) { .timeout_ms = timeout_s * MS_PER_SECOND, .time_type = TimelinePeekTimeType_ShowStarted, .is_first_event = true ); prv_invoke_timer(timeout_s); - CHECK_NO_EVENTS( .count = 6, .is_future_empty = true ); + // The peek fires at exactly the persistent event's end time. prv_show_event() in the future + // direction treats an event as "in future until it ends" using an inclusive boundary + // (timestamp >= now - duration), so at now == end the just-ended event still counts towards + // future_has_event. is_future_empty is therefore false here, matching the non-persistent + // lifecycle tests above which leave .is_future_empty defaulted to false at this same boundary. + CHECK_NO_EVENTS( .count = 6 ); } void test_timeline_peek_event__upcoming_priotized_over_persistent_event_lifecycle(void) { @@ -768,19 +760,30 @@ void test_timeline_peek_event__upcoming_priotized_over_persistent_event_lifecycl .time_type = TimelinePeekTimeType_ShowStarted, .is_first_event = true ); prv_invoke_timer(timeout_s); timeout_s = TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S; + // The upcoming non-persistent item2 is prioritised for display while both items peek, but + // is_first_event reflects the earliest event in the future direction (prv_peek_filter tracks + // first_header separately from the displayed item). The persistent item1 has the earlier + // timestamp and is still within its peeking window, so item1 remains the first event and the + // peek for item2 reports is_first_event == false. CHECK_EVENT( .count = 7, .item_id = item2.header.id, .num_concurrent = 1, .timeout_ms = timeout_s * MS_PER_SECOND, - .time_type = TimelinePeekTimeType_ShowWillStart, .is_first_event = true ); + .time_type = TimelinePeekTimeType_ShowWillStart, .is_first_event = false ); prv_invoke_timer(timeout_s); timeout_s = TIMELINE_PEEK_HIDE_AFTER_TIME_S; + // item2 is still the displayed peek while the persistent item1 remains the earliest future + // event, so is_first_event stays false (see the count == 7 note above). CHECK_EVENT( .count = 8, .item_id = item2.header.id, .num_concurrent = 1, .timeout_ms = timeout_s * MS_PER_SECOND, - .time_type = TimelinePeekTimeType_ShowStarted, .is_first_event = true ); + .time_type = TimelinePeekTimeType_ShowStarted, .is_first_event = false ); prv_invoke_timer(timeout_s); timeout_s = 30 * SECONDS_PER_MINUTE; // time until persistent event ends + // item2 has finished; the persistent item1 is again the only (and earliest) event being shown, + // so is_first_event is true here. CHECK_EVENT( .count = 9, .item_id = item.header.id, .num_concurrent = 0, .timeout_ms = timeout_s * MS_PER_SECOND, - .time_type = TimelinePeekTimeType_ShowStarted ); + .time_type = TimelinePeekTimeType_ShowStarted, .is_first_event = true ); prv_invoke_timer(timeout_s); - CHECK_NO_EVENTS( .count = 10, .is_future_empty = true ); + // Fires at exactly the persistent event's end time, where the inclusive future boundary still + // counts the just-ended event (see one_persistent_event_lifecycle), so future is not empty. + CHECK_NO_EVENTS( .count = 10 ); } diff --git a/tests/fw/services/timeline/test_timeline_resources.c b/tests/fw/services/timeline/test_timeline_resources.c index 05ba0eed26..665bf85360 100644 --- a/tests/fw/services/timeline/test_timeline_resources.c +++ b/tests/fw/services/timeline/test_timeline_resources.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "process_management/app_install_manager.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "system/passert.h" #include "util/struct.h" diff --git a/tests/fw/services/timeline/wscript b/tests/fw/services/timeline/wscript deleted file mode 100644 index 227e964c7e..0000000000 --- a/tests/fw/services/timeline/wscript +++ /dev/null @@ -1,226 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/util/time/time.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/blob_db/timeline_item_storage.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/reminder_db.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/reminders.c", - test_sources_ant_glob = "test_reminders.c", - override_includes=['dummy_board']) - -def build(ctx): - clar(ctx, - sources_ant_glob=( - "src/fw/services/normal/blob_db/pin_db.c " - "src/fw/services/normal/blob_db/timeline_item_storage.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/calendar.c " - "src/fw/services/normal/timeline/event.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/services/normal/timeline/timeline.c " - "src/fw/util/crc8.c " - "src/fw/util/time/time.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_settings_file.c " - ), - test_sources_ant_glob="test_calendar.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=( - "src/fw/services/normal/blob_db/pin_db.c " - "src/fw/services/normal/blob_db/timeline_item_storage.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/event.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/services/normal/timeline/peek.c " - "src/fw/services/normal/timeline/timeline.c " - "src/fw/util/crc8.c " - "src/fw/util/time/time.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_settings_file.c " - ), - test_sources_ant_glob = "test_timeline_peek_event.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/timeline/attribute.c", - test_sources_ant_glob = "test_attribute.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/services/normal/blob_db/timeline_item_storage.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/timeline.c" \ - " src/fw/services/normal/blob_db/pin_db.c" \ - " src/fw/apps/system_apps/timeline/timeline_model.c", - test_sources_ant_glob = "test_timeline_model.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob=( - " src/fw/process_management/pebble_process_info.c" - " src/fw/services/normal/timeline/timeline_resources.c" - " tests/fixtures/resources/timeline_resource_table.auto.c" - ), - test_sources_ant_glob = "test_timeline_resources.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/alarms/alarm.c" \ - " src/fw/services/normal/timeline/alarm_layout.c" \ - " src/fw/services/normal/timeline/attribute.c", - test_sources_ant_glob = "test_alarm_layout.c", - defines=["CAPABILITY_HAS_HEALTH_TRACKING=0"], - override_includes=['dummy_board'], - platforms=['snowy', 'spalding']) - - rendering_sources = \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" \ - " src/fw/applib/graphics/gbitmap_sequence.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/ui/action_button.c" \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/kino/kino_layer.c" \ - " src/fw/applib/ui/kino/kino_player.c" \ - " src/fw/applib/ui/kino/kino_reel.c" \ - " src/fw/applib/ui/kino/kino_reel/transform.c" \ - " src/fw/applib/ui/kino/kino_reel_custom.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ - " src/fw/applib/ui/kino/kino_reel_pdci.c" \ - " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/applib/ui/menu_layer_system_cells.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/applib/ui/status_bar_layer.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/applib/vendor/tinflate/tinflate.c" \ - " src/fw/applib/vendor/uPNG/upng.c" \ - " src/fw/apps/system_apps/timeline/text_node.c" \ - " src/fw/board/displays/display_spalding.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/util/buffer.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/util/stringlist.c" \ - " src/fw/util/time/time.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_clock.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_fonts.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " tests/fixtures/resources/timeline_resource_table.auto.c" \ - " tests/stubs/stubs_animation.c" - - clar(ctx, - sources_ant_glob=( - rendering_sources + - " src/fw/apps/system_apps/timeline/pin_window.c" - " src/fw/popups/timeline/timeline_item_layer.c" - " src/fw/services/normal/timeline/attribute.c" - " src/fw/services/normal/timeline/generic_layout.c" - " src/fw/services/normal/timeline/layout_layer.c" - " src/fw/services/normal/timeline/layout_node.c" - " src/fw/services/normal/timeline/timeline_layout.c" - " src/fw/services/normal/timeline/timeline_resources.c" - " src/fw/services/normal/timeline/weather_layout.c" - " src/fw/services/normal/weather/weather_types.c" - " src/fw/shell/system_theme.c" - " tests/stubs/stubs_clock.c" - " tests/stubs/stubs_timeline_layout.c" - ), - test_sources_ant_glob = "test_timeline_layouts.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk', 'robert']) - -# vim:filetype=python diff --git a/tests/fw/services/timeline/wscript_build b/tests/fw/services/timeline/wscript_build new file mode 100644 index 0000000000..bfb6895697 --- /dev/null +++ b/tests/fw/services/timeline/wscript_build @@ -0,0 +1,226 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/util/time/time.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/blob_db/timeline_item_storage.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/reminder_db.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/reminders.c", + test_sources_ant_glob = "test_reminders.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + "src/fw/services/blob_db/pin_db.c " + "src/fw/services/blob_db/timeline_item_storage.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/calendar.c " + "src/fw/services/timeline/event.c " + "src/fw/services/timeline/item.c " + "src/fw/services/timeline/timeline.c " + "src/fw/util/crc8.c " + "src/fw/util/time/time.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_settings_file.c " + ), + test_sources_ant_glob="test_calendar.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + "src/fw/services/blob_db/pin_db.c " + "src/fw/services/blob_db/timeline_item_storage.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/event.c " + "src/fw/services/timeline/item.c " + "src/fw/services/timeline/peek.c " + "src/fw/services/timeline/timeline.c " + "src/fw/util/crc8.c " + "src/fw/util/time/time.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_settings_file.c " + ), + test_sources_ant_glob = "test_timeline_peek_event.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/timeline/attribute.c", + test_sources_ant_glob = "test_attribute.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/services/blob_db/timeline_item_storage.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/timeline.c" \ + " src/fw/services/blob_db/pin_db.c" \ + " src/fw/apps/system/timeline/model.c", + test_sources_ant_glob = "test_timeline_model.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + " src/fw/process_management/pebble_process_info.c" + " src/fw/services/timeline/timeline_resources.c" + " tests/fixtures/resources/timeline_resource_table.auto.c" + ), + test_sources_ant_glob = "test_timeline_resources.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/alarms/alarm.c" \ + " src/fw/services/timeline/alarm_layout.c" \ + " src/fw/services/timeline/attribute.c", + test_sources_ant_glob = "test_alarm_layout.c", + defines=["CAPABILITY_HAS_HEALTH_TRACKING=0"], + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +rendering_sources = \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" \ + " src/fw/applib/graphics/gbitmap_sequence.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/ui/action_button.c" \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/kino/kino_layer.c" \ + " src/fw/applib/ui/kino/kino_player.c" \ + " src/fw/applib/ui/kino/kino_reel.c" \ + " src/fw/applib/ui/kino/kino_reel/transform.c" \ + " src/fw/applib/ui/kino/kino_reel_custom.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ + " src/fw/applib/ui/kino/kino_reel_pdci.c" \ + " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/applib/ui/menu_layer_system_cells.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/applib/ui/status_bar_layer.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/applib/vendor/tinflate/tinflate.c" \ + " src/fw/applib/vendor/uPNG/upng.c" \ + " src/fw/apps/system/timeline/text_node.c" \ + " src/fw/board/displays/display_getafix.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/util/buffer.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/util/stringlist.c" \ + " src/fw/util/time/time.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/fakes/fake_clock.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_fonts.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " tests/fixtures/resources/timeline_resource_table.auto.c" \ + " tests/stubs/stubs_animation.c" + +clar(ctx, + sources_ant_glob=( + rendering_sources + + " src/fw/apps/system/timeline/pin_window.c" + " src/fw/popups/timeline/timeline_item_layer.c" + " src/fw/services/timeline/attribute.c" + " src/fw/services/timeline/generic_layout.c" + " src/fw/services/timeline/layout_layer.c" + " src/fw/services/timeline/layout_node.c" + " src/fw/services/timeline/timeline_layout.c" + " src/fw/services/timeline/timeline_resources.c" + " src/fw/services/timeline/weather_layout.c" + " src/fw/services/weather/weather_types.c" + " src/fw/shell/system_theme.c" + " tests/stubs/stubs_clock.c" + " tests/stubs/stubs_timeline_layout.c" + ), + test_sources_ant_glob = "test_timeline_layouts.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +# vim:filetype=python diff --git a/tests/fw/services/wscript b/tests/fw/services/wscript deleted file mode 100644 index a2b9063039..0000000000 --- a/tests/fw/services/wscript +++ /dev/null @@ -1,511 +0,0 @@ -from waftools.pebble_test import clar - -import generate_c_byte_array -import generate_timezone_data - -def build(ctx): - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_pfs.c", - defines=['DUMA_DISABLED'], # PBL-18355 Invalid memory read access - override_includes=['dummy_board'], - platforms=['tintin']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/put_bytes/put_bytes.c" \ - " src/fw/services/common/put_bytes/put_bytes_storage.c" \ - " tests/fakes/fake_session.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_put_bytes_storage_mem.c" \ - " tests/fakes/fake_queue.c" - " tests/fakes/fake_resource_storage.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_put_bytes.c", - platforms=['snowy','silk'], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/analytics/analytics.c" \ - " src/fw/services/normal/analytics/analytics_heartbeat.c" \ - " src/fw/services/normal/analytics/analytics_metric.c" \ - " src/fw/services/normal/analytics/analytics_storage.c" \ - " src/fw/services/normal/analytics/analytics_logging.c" \ - " src/fw/services/normal/analytics/analytics_event.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_analytics.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " tests/fakes/fake_rtc.c" \ - " src/fw/services/common/cron.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c", - test_sources_ant_glob = "test_cron.c") - - clar(ctx, - sources_ant_glob = \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/contacts/attributes_address.c" \ - " src/fw/services/normal/contacts/contacts.c" \ - " src/fw/services/normal/blob_db/contacts_db.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/util/stringlist.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/util/rand/rand.c" \ - " src/fw/util/time/time.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_contacts.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/evented_timer.c", - test_sources_ant_glob = "test_evented_timer.c") - - clar(ctx, - sources_ant_glob = \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_session.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/services/normal/music_endpoint.c" \ - " src/fw/services/normal/music.c", - test_sources_ant_glob = "test_music_endpoint.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/ecompass_correction.c", - test_sources_ant_glob = "test_compass_cal.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/light.c", - test_sources_ant_glob = "test_light.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_session.c" \ - " src/fw/services/normal/phone_call_util.c" \ - " src/fw/services/common/phone_pp.c", - test_sources_ant_glob = "test_phone_pp.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/phone_call.c", - test_sources_ant_glob = "test_phone_call.c",) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/regular_timer.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_session.c" \ - " src/fw/services/common/debounced_connection_service.c", - test_sources_ant_glob = "test_debounced_connection_service.c") - - clar(ctx, - sources_ant_glob = \ - " tests/fakes/fake_rtc.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/util/stringlist.c" \ - " src/fw/util/time/time.c", - test_sources_ant_glob = "test_timeline_item.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/i18n/i18n.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " tests/fakes/fake_bootbits.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " src/fw/services/normal/timezone_database.c" \ - " src/fw/services/common/clock.c", - test_sources_ant_glob = "test_clock.c", - override_includes=['test_timezone', 'dummy_board']) - - - olson_txt = ctx.srcnode.make_node('resources/normal/base/tzdata/timezones_olson.txt') - timezone_fixture_h = ctx.path.get_bld().make_node('timezone_fixture.auto.h') - - def _generate_timezone_fixture_h(task): - reso = generate_timezone_data.generate_resource_object(task.inputs[0].abspath()) - with open(task.outputs[0].abspath(), 'w') as f: - generate_c_byte_array.write(f, reso.data, 's_timezone_database') - ctx(rule=_generate_timezone_fixture_h, source=olson_txt, target=timezone_fixture_h) - - clar(ctx, - sources_ant_glob = \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/services/normal/timezone_database.c", - test_sources_ant_glob="test_timezone_database.c", - override_includes=['test_timezone']) - - clar(ctx, - sources_ant_glob = " ".join([ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/process_management/pebble_process_info.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/wakeup.c" \ - " tests/fakes/fake_bootbits.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c"]), - test_sources_ant_glob = "test_wakeup.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = " ".join([ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/process_management/pebble_process_info.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/wakeup.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " tests/fakes/fake_bootbits.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - ]), - test_sources_ant_glob = "test_migrate_wakeup.c", - override_includes=['dummy_board']) - - - clar(ctx, - sources_ant_glob = " ".join([ - "src/fw/services/common/registry_endpoint.c"]), - test_sources_ant_glob = "test_registry_endpoint.c") - - clar(ctx, - defines=["PLATFORM_SNOWY"], - sources_ant_glob = " ".join([ - " src/fw/services/normal/audio_endpoint.c " \ - " tests/fakes/fake_session.c"]),\ - test_sources_ant_glob = "test_audio_endpoint.c") - - clar(ctx, - sources_ant_glob = " ".join([ - " src/fw/services/normal/voice/transcription.c"]), - test_sources_ant_glob = "test_transcription.c") - - clar(ctx, - sources_ant_glob = " ".join([ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/util/generic_attribute.c" \ - " src/fw/services/normal/voice/transcription.c" \ - " src/fw/services/normal/voice_endpoint.c " \ - " tests/fakes/fake_session.c " \ - ]), - defines=["PLATFORM_SNOWY", "CAPABILITY_HAS_MICROPHONE=1"], - test_sources_ant_glob = "test_voice_endpoint.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/app_cache.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fakes/fake_events.c", - test_sources_ant_glob = "test_app_cache.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/app_cache.c" \ - " src/fw/services/normal/process_management/app_storage.c" \ - " src/fw/process_management/app_install_manager.c" \ - " src/fw/process_management/pebble_process_md.c" \ - " src/fw/services/normal/blob_db/app_db.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/process_management/pebble_process_info.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fakes/fake_system_app_registry_apps.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_app_install_manager.c", - override_includes=['dummy_board', 'fake_app_registry']) - - clar(ctx, - sources_ant_glob=( - "src/fw/applib/graphics/gcolor_definitions.c " - "src/fw/applib/graphics/gtypes.c " - "src/fw/drivers/flash/flash_crc.c " - "src/fw/flash_region/filesystem_regions.c " - "src/fw/flash_region/flash_region.c " - "src/fw/process_management/app_install_manager.c " - "src/fw/process_management/app_menu_data_source.c " - "src/fw/process_management/pebble_process_info.c " - "src/fw/process_management/pebble_process_md.c " - "src/fw/resource/resource.c " - "src/fw/resource/resource_storage.c " - "src/fw/resource/resource_storage_builtin.c " - "src/fw/resource/resource_storage_file.c " - "src/fw/resource/resource_storage_flash.c " - "src/fw/services/normal/app_cache.c " - "src/fw/services/normal/blob_db/app_db.c " - "src/fw/services/normal/filesystem/app_file.c " - "src/fw/services/normal/filesystem/flash_translation.c " - "src/fw/services/normal/filesystem/pfs.c " - "src/fw/services/normal/process_management/app_order_storage.c " - "src/fw/services/normal/process_management/app_storage.c " - "src/fw/services/normal/settings/settings_file.c " - "src/fw/services/normal/settings/settings_raw_iter.c " - "src/fw/util/crc8.c " - "src/fw/util/legacy_checksum.c " - "src/fw/util/rand/rand.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_spi_flash.c " - "tests/fakes/fake_system_app_registry_apps.c " - "tests/fixtures/resources/builtin_resources.auto.c " - ), - test_sources_ant_glob="test_app_menu_data_source.c", - override_includes=['dummy_board', 'fake_app_registry']) - - clar(ctx, - sources_ant_glob = - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/services/normal/app_fetch_endpoint.c" \ - " tests/fakes/fake_events.c " \ - " tests/fakes/fake_session.c ", - test_sources_ant_glob = "test_app_fetch_endpoint.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = - " src/fw/util/shared_circular_buffer.c" \ - " src/fw/services/common/accel_manager.c" \ - " tests/fakes/fake_events.c " \ - , - test_sources_ant_glob = "test_accel_manager.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = " ".join([ - " src/fw/services/common/shared_prf_storage/v3_sprf/shared_prf_storage.c " \ - " tests/fakes/fake_flash_region.c" \ - " tests/fakes/fake_spi_flash.c" \ - ]), - test_sources_ant_glob = "test_shared_prf_storage_v3.c", - platforms=['silk'], - override_includes=['shared_prf_storage_v3']) - - clar(ctx, - sources_ant_glob = " ".join([ - " src/fw/services/common/shared_prf_storage/v2_sprf/shared_prf_storage.c " \ - " tests/fakes/fake_spi_flash.c" \ - ]), - test_sources_ant_glob = "test_shared_prf_storage_v2.c", - override_includes=['snowy_mfg_board']) - - clar(ctx, - sources_ant_glob = 'src/fw/services/normal/filesystem/app_file.c', - test_sources_ant_glob = 'test_app_file.c') - - clar(ctx, - sources_ant_glob = \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/notifications/alerts_preferences.c" \ - " src/fw/services/normal/notifications/do_not_disturb.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - defines=['CAPABILITY_HAS_HEALTH_TRACKING=1'], - test_sources_ant_glob = "test_do_not_disturb.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/util/pstring.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/weather_db.c" \ - " src/fw/services/normal/weather/weather_service.c" \ - " tests/fw/services/blob_db/weather_data_shared.c", - test_sources_ant_glob = "test_weather_service.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/services/normal/accessory/smartstrap_comms.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/hdlc.c" \ - " src/fw/util/mbuf.c" \ - " src/fw/util/mbuf_iterator.c" \ - " tests/fakes/fake_accessory.c" \ - " tests/fakes/fake_smartstrap_profiles.c" \ - " tests/fakes/fake_smartstrap_state.c", - test_sources_ant_glob = "test_smartstrap_comms.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/common/vibe_pattern.c" \ - " src/fw/applib/ui/vibes.c" \ - " tests/fakes/fake_events.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_vibe.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/normal/vibes/vibe_intensity.c", - test_sources_ant_glob = "test_vibe_intensity.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/normal/vibes/vibe_score.c" \ - " src/fw/util/generic_attribute.c", - test_sources_ant_glob = "test_vibe_score.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/normal/vibes/vibe_score_info.c", - test_sources_ant_glob = "test_vibe_score_info.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/common/touch/touch.c" \ - " tests/fakes/fake_events.c", - test_sources_ant_glob = "test_touch.c") - - clar(ctx, - sources_ant_glob = \ - "src/fw/services/common/hrm/hrm_manager.c " \ - "src/libos/tick.c " \ - "tests/fakes/fake_events.c " \ - "tests/fakes/fake_rtc.c " \ - "tests/fakes/fake_accel_service.c ", - test_sources_ant_glob = "test_hrm_manager.c", - override_includes=['dummy_board'], - defines=["CAPABILITY_HAS_BUILTIN_HRM=1"]) - - clar(ctx, - sources_ant_glob=( - " src/fw/services/normal/app_glances/app_glance_service.c" - " src/fw/services/normal/blob_db/app_glance_db.c" - " src/fw/services/normal/timeline/attribute.c" - " src/fw/util/crc8.c" - " tests/fakes/fake_rtc.c" - " tests/fakes/fake_settings_file.c" - ), - test_sources_ant_glob="test_app_glance_service.c", - override_includes=['dummy_board']) - -# vim:filetype=python diff --git a/tests/fw/services/wscript_build b/tests/fw/services/wscript_build new file mode 100644 index 0000000000..814d62ccba --- /dev/null +++ b/tests/fw/services/wscript_build @@ -0,0 +1,474 @@ +from waftools.pebble_test import clar + +import generate_c_byte_array +import generate_timezone_data + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_pfs.c", + defines=['DUMA_DISABLED'], # PBL-18355 Invalid memory read access + override_includes=['dummy_board'], + platforms=['obelix']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/put_bytes/put_bytes.c" \ + " src/fw/services/put_bytes/put_bytes_storage.c" \ + " tests/fakes/fake_session.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_put_bytes_storage_mem.c" \ + " tests/fakes/fake_queue.c" + " tests/fakes/fake_resource_storage.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_put_bytes.c", + platforms=['obelix', 'gabbro'], + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " tests/fakes/fake_rtc.c" \ + " src/fw/services/cron/service.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c", + test_sources_ant_glob = "test_cron.c") + +clar(ctx, + sources_ant_glob = \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/contacts/attributes_address.c" \ + " src/fw/services/contacts/contacts.c" \ + " src/fw/services/blob_db/contacts_db.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/util/stringlist.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/util/rand/rand.c" \ + " src/fw/util/time/time.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob = "test_contacts.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/evented_timer/service.c", + test_sources_ant_glob = "test_evented_timer.c") + +clar(ctx, + sources_ant_glob = \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_session.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/services/music/endpoint.c" \ + " src/fw/services/music/service.c", + test_sources_ant_glob = "test_music_endpoint.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/ecompass/correction.c", + test_sources_ant_glob = "test_compass_cal.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/light/service.c", + test_sources_ant_glob = "test_light.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_session.c" \ + " src/fw/services/phone_call/util.c" \ + " src/fw/services/phone_pp/service.c", + test_sources_ant_glob = "test_phone_pp.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/phone_call/service.c", + test_sources_ant_glob = "test_phone_call.c",) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/regular_timer/service.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_session.c" \ + " src/fw/services/debounced_connection_service/service.c", + test_sources_ant_glob = "test_debounced_connection_service.c") + +clar(ctx, + sources_ant_glob = \ + " tests/fakes/fake_rtc.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/util/stringlist.c" \ + " src/fw/util/time/time.c", + test_sources_ant_glob = "test_timeline_item.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/i18n/i18n.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " tests/fakes/fake_bootbits.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " src/fw/services/timezone_database/service.c" \ + " src/fw/services/clock/service.c", + test_sources_ant_glob = "test_clock.c", + override_includes=['dummy_board']) + + +olson_txt = ctx.srcnode.make_node('resources/normal/base/tzdata/timezones_olson.txt') +timezone_fixture_h = ctx.path.get_bld().make_node('timezone_fixture.auto.h') + +def _generate_timezone_fixture_h(task): + reso = generate_timezone_data.generate_resource_object(task.inputs[0].abspath()) + with open(task.outputs[0].abspath(), 'w') as f: + generate_c_byte_array.write(f, reso.data, 's_timezone_database') +ctx(rule=_generate_timezone_fixture_h, source=olson_txt, target=timezone_fixture_h) + +clar(ctx, + sources_ant_glob = \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/services/timezone_database/service.c", + test_sources_ant_glob="test_timezone_database.c", + override_includes=['test_timezone']) + +clar(ctx, + sources_ant_glob = " ".join([ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/process_management/pebble_process_info.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/wakeup/service.c" \ + " tests/fakes/fake_bootbits.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c"]), + test_sources_ant_glob = "test_wakeup.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = " ".join([ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/process_management/pebble_process_info.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/wakeup/service.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " tests/fakes/fake_bootbits.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + ]), + test_sources_ant_glob = "test_migrate_wakeup.c", + override_includes=['dummy_board']) + + +clar(ctx, + sources_ant_glob = " ".join([ + "src/fw/services/registry_endpoint/service.c"]), + test_sources_ant_glob = "test_registry_endpoint.c") + +clar(ctx, + sources_ant_glob = " ".join([ + " src/fw/services/audio_endpoint/service.c " \ + " tests/fakes/fake_session.c"]),\ + test_sources_ant_glob = "test_audio_endpoint.c") + +clar(ctx, + sources_ant_glob = " ".join([ + " src/fw/services/voice/transcription.c"]), + test_sources_ant_glob = "test_transcription.c") + +clar(ctx, + sources_ant_glob = " ".join([ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/util/generic_attribute.c" \ + " src/fw/services/voice/transcription.c" \ + " src/fw/services/voice_endpoint/service.c " \ + " tests/fakes/fake_session.c " \ + ]), + defines=["CONFIG_MIC"], + test_sources_ant_glob = "test_voice_endpoint.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/app_cache/service.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fakes/fake_events.c", + test_sources_ant_glob = "test_app_cache.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/app_cache/service.c" \ + " src/fw/services/process_management/app_storage.c" \ + " src/fw/process_management/app_install_manager.c" \ + " src/fw/process_management/pebble_process_md.c" \ + " src/fw/services/blob_db/app_db.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/process_management/pebble_process_info.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fakes/fake_system_app_registry_apps.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_app_install_manager.c", + override_includes=['dummy_board', 'fake_app_registry']) + +clar(ctx, + sources_ant_glob=( + "src/fw/applib/graphics/gcolor_definitions.c " + "src/fw/applib/graphics/gtypes.c " + "src/fw/drivers/flash/flash_crc.c " + "src/fw/flash_region/filesystem_regions.c " + "src/fw/flash_region/flash_region.c " + "src/fw/process_management/app_install_manager.c " + "src/fw/process_management/app_menu_data_source.c " + "src/fw/process_management/pebble_process_info.c " + "src/fw/process_management/pebble_process_md.c " + "src/fw/resource/resource.c " + "src/fw/resource/resource_storage.c " + "src/fw/resource/resource_storage_builtin.c " + "src/fw/resource/resource_storage_file.c " + "src/fw/resource/resource_storage_flash.c " + "src/fw/services/app_cache/service.c " + "src/fw/services/blob_db/app_db.c " + "src/fw/services/filesystem/app_file.c " + "src/fw/services/filesystem/flash_translation.c " + "src/fw/services/filesystem/pfs.c " + "src/fw/services/process_management/app_order_storage.c " + "src/fw/services/process_management/app_storage.c " + "src/fw/services/settings/settings_file.c " + "src/fw/services/settings/settings_raw_iter.c " + "src/fw/util/crc8.c " + "src/fw/util/legacy_checksum.c " + "src/fw/util/rand/rand.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_spi_flash.c " + "tests/fakes/fake_system_app_registry_apps.c " + "tests/fixtures/resources/builtin_resources.auto.c " + ), + test_sources_ant_glob="test_app_menu_data_source.c", + override_includes=['dummy_board', 'fake_app_registry']) + +clar(ctx, + sources_ant_glob = + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/services/app_fetch_endpoint/service.c" \ + " tests/fakes/fake_events.c " \ + " tests/fakes/fake_session.c ", + test_sources_ant_glob = "test_app_fetch_endpoint.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = + " src/fw/util/shared_circular_buffer.c" \ + " src/fw/services/accel_manager/service.c" \ + " tests/fakes/fake_events.c " \ + , + test_sources_ant_glob = "test_accel_manager.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = " ".join([ + " src/fw/services/shared_prf_storage/shared_prf_storage.c " \ + " tests/fakes/fake_flash_region.c" \ + " tests/fakes/fake_spi_flash.c" \ + ]), + test_sources_ant_glob = "test_shared_prf_storage_v3.c", + platforms=['obelix'], + override_includes=['shared_prf_storage_v3']) + +clar(ctx, + sources_ant_glob = 'src/fw/services/filesystem/app_file.c', + test_sources_ant_glob = 'test_app_file.c') + +clar(ctx, + sources_ant_glob = \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/notifications/alerts_preferences.c" \ + " src/fw/services/notifications/do_not_disturb.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_do_not_disturb.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/util/pstring.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/weather_db.c" \ + " src/fw/services/weather/weather_service.c" \ + " tests/fw/services/blob_db/weather_data_shared.c", + test_sources_ant_glob = "test_weather_service.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/services/vibe_pattern/service.c" \ + " src/fw/applib/ui/vibes.c" \ + " tests/fakes/fake_events.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_vibe.c") + +clar(ctx, + sources_ant_glob = "src/fw/services/vibes/vibe_intensity.c", + test_sources_ant_glob = "test_vibe_intensity.c") + +clar(ctx, + sources_ant_glob = "src/fw/services/vibes/vibe_score.c" \ + " src/fw/util/generic_attribute.c", + test_sources_ant_glob = "test_vibe_score.c") + +clar(ctx, + sources_ant_glob = "src/fw/services/vibes/vibe_score_info.c", + test_sources_ant_glob = "test_vibe_score_info.c") + +clar(ctx, + sources_ant_glob = "src/fw/services/touch/touch.c" \ + " tests/fakes/fake_events.c", + test_sources_ant_glob = "test_touch.c") + +clar(ctx, + sources_ant_glob = \ + "src/fw/services/hrm/hrm_manager.c " \ + "src/libos/tick.c " \ + "tests/fakes/fake_events.c " \ + "tests/fakes/fake_rtc.c " \ + "tests/fakes/fake_accel_service.c ", + test_sources_ant_glob = "test_hrm_manager.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob=( + " src/fw/services/app_glances/app_glance_service.c" + " src/fw/services/blob_db/app_glance_db.c" + " src/fw/services/timeline/attribute.c" + " src/fw/util/crc8.c" + " tests/fakes/fake_rtc.c" + " tests/fakes/fake_settings_file.c" + ), + test_sources_ant_glob="test_app_glance_service.c", + override_includes=['dummy_board']) + +# vim:filetype=python diff --git a/tests/fw/shell/normal/test_battery_ui_fsm.c b/tests/fw/shell/normal/test_battery_ui_fsm.c index c1feaea588..51aa5823a7 100644 --- a/tests/fw/shell/normal/test_battery_ui_fsm.c +++ b/tests/fw/shell/normal/test_battery_ui_fsm.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -25,8 +12,7 @@ #include "kernel/ui/modals/modal_manager.h" #include "kernel/util/standby.h" #include "process_management/app_manager.h" -#include "services/common/battery/battery_curve.h" -#include "services/common/status_led.h" +#include "pbl/services/battery/battery_curve.h" #include "shell/normal/battery_ui.h" #include "util/ratio.h" @@ -137,6 +123,7 @@ void modal_manager_set_min_priority(ModalPriority priority) { static PreciseBatteryChargeState prv_make_state(uint8_t percent, bool is_charging, bool is_plugged) { PreciseBatteryChargeState state = (PreciseBatteryChargeState) { .charge_percent = ratio32_from_percent(percent), + .pct = percent, .is_charging = is_charging, .is_plugged = is_plugged }; @@ -144,11 +131,6 @@ static PreciseBatteryChargeState prv_make_state(uint8_t percent, bool is_chargin return state; } -static StatusLedState s_led_state; -void status_led_set(StatusLedState state) { - s_led_state = state; -} - bool s_is_charging; BatteryChargeState battery_get_charge_state(void) { // Don't bother setting other fields, they're not used. @@ -169,7 +151,6 @@ void test_battery_ui_fsm__initialize(void) { s_low_power = false; s_critical = false; s_shutdown_charging = false; - s_led_state = StatusLedState_Off; s_is_charging = false; battery_ui_reset_fsm_for_tests(); @@ -201,57 +182,47 @@ void test_battery_ui_fsm__transitions(void) { // Good - shouldn't do anything prv_change_state(nop); cl_assert(!s_modal_onscreen && !s_low_power && !s_critical); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // Charging - should open charging modal prv_change_state(charging); cl_assert(s_modal_onscreen && s_modal_charging); - cl_assert_equal_i(s_led_state, StatusLedState_Charging); // Fully charged - should trigger another event, opening fully charged modal prv_change_state(fully_charged); cl_assert(s_modal_onscreen && !s_modal_charging); - cl_assert_equal_i(s_led_state, StatusLedState_FullyCharged); // Back to good - modal should have closed prv_change_state(nop); cl_assert(!s_modal_onscreen); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // Warning - Should trigger various modals prv_change_state(warning_18h); cl_assert(s_modal_onscreen && s_modal_percent == battery_curve_get_percent_remaining(18)); prv_change_state(warning_12h); cl_assert(s_modal_onscreen && s_modal_percent == battery_curve_get_percent_remaining(12)); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // Low Power - should enter low power watchface, modal should have closed prv_set_state(PowerLow); prv_change_state(nop); cl_assert(!s_modal_onscreen && s_low_power); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // Critical - should enter critical app, low power should have closed prv_set_state(PowerCritical); prv_change_state(nop); cl_assert(!s_low_power && s_critical); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // Charging - critical should disable, modal should appear prv_set_state(PowerGood); prv_change_state(charging); cl_assert(!s_critical && s_modal_onscreen); - cl_assert_equal_i(s_led_state, StatusLedState_Charging); // Enter shutdown charging - modal should close, shutdown charging app should launch battery_ui_handle_shut_down(); cl_assert(!s_modal_onscreen && s_shutdown_charging); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // Shouldn't be able to transition out prv_change_state(warning_18h); cl_assert(!s_modal_onscreen && s_shutdown_charging); - cl_assert_equal_i(s_led_state, StatusLedState_Off); } void test_battery_ui_fsm__shutdown(void) { @@ -283,78 +254,88 @@ void test_battery_ui_fsm__warning(void) { cl_assert(s_modal_onscreen); cl_assert_equal_i(s_modal_percent, battery_curve_get_percent_remaining(12)); cl_assert_equal_i(s_vibe_count, 1); - cl_assert_equal_i(s_led_state, StatusLedState_Off); // But we can jump around as long as we switch first prv_change_state(nop); cl_assert(!s_modal_onscreen); - cl_assert_equal_i(s_led_state, StatusLedState_Off); prv_change_state(warning_12h); cl_assert(s_modal_onscreen && s_modal_percent == battery_curve_get_percent_remaining(12)); - cl_assert_equal_i(s_led_state, StatusLedState_Off); +} + +void test_battery_ui_fsm__skip_first_warning_when_next_is_close(void) { + // If the first warning would fire at a battery level already within + // BATTERY_WARNING_MIN_HOURS_HEADROOM (3h) of the next threshold, the + // gray warning is skipped so the user doesn't get contradictory daypart + // messages (e.g. "Powered 'til tomorrow" followed by "Powered 'til tonight"). + PreciseBatteryChargeState nop = prv_make_state(50, false, false); + // 14h remaining is within 3h of the 12h red threshold -> gray should be skipped. + PreciseBatteryChargeState warning_14h = + prv_make_state(battery_curve_get_percent_remaining(14), false, false); + PreciseBatteryChargeState warning_12h = + prv_make_state(battery_curve_get_percent_remaining(12), false, false); + + prv_change_state(warning_14h); + cl_assert(!s_modal_onscreen); + cl_assert_equal_i(s_vibe_count, 0); + + // Dropping below the red threshold should fire red normally. + prv_change_state(warning_12h); + cl_assert(s_modal_onscreen); + cl_assert_equal_i(s_modal_percent, battery_curve_get_percent_remaining(12)); + cl_assert_equal_i(s_vibe_count, 1); } void test_battery_ui_fsm__honor_dnd(void) { PreciseBatteryChargeState nop = prv_make_state(50, false, false), charging = prv_make_state(50, true, true), - warning = prv_make_state(15, false, false); + warning = prv_make_state( + battery_curve_get_percent_remaining(18), false, false); s_dnd_on = true; prv_change_state(charging); cl_assert(s_modal_onscreen && s_modal_charging); cl_assert_equal_i(s_vibe_count, 0); - cl_assert_equal_i(s_led_state, StatusLedState_Charging); // With DND off, another charging event shouldn't vibe since we didn't update s_dnd_on = false; prv_change_state(charging); cl_assert_equal_i(s_vibe_count, 0); - cl_assert_equal_i(s_led_state, StatusLedState_Charging); // Now we should vibe prv_change_state(nop); - cl_assert_equal_i(s_led_state, StatusLedState_Off); prv_change_state(charging); cl_assert(s_modal_onscreen && s_modal_charging); cl_assert_equal_i(s_vibe_count, 1); - cl_assert_equal_i(s_led_state, StatusLedState_Charging); // Same for warnings s_dnd_on = true; prv_change_state(warning); cl_assert(s_modal_onscreen && s_modal_percent); cl_assert_equal_i(s_vibe_count, 1); - cl_assert_equal_i(s_led_state, StatusLedState_Off); s_dnd_on = false; prv_change_state(warning); cl_assert_equal_i(s_vibe_count, 1); - cl_assert_equal_i(s_led_state, StatusLedState_Off); prv_change_state(nop); prv_change_state(warning); cl_assert(s_modal_onscreen && s_modal_percent); cl_assert_equal_i(s_vibe_count, 2); - cl_assert_equal_i(s_led_state, StatusLedState_Off); } void test_battery_ui_fsm__no_vibe_complete(void) { PreciseBatteryChargeState charging = prv_make_state(50, true, true), fully_charged = prv_make_state(100, false, true); - cl_assert_equal_i(s_led_state, StatusLedState_Off); - s_dnd_on = false; // Charging starts prv_change_state(charging); cl_assert(s_modal_onscreen && s_modal_charging); cl_assert_equal_i(s_vibe_count, 1); - cl_assert_equal_i(s_led_state, StatusLedState_Charging); // Charging completes prv_change_state(fully_charged); cl_assert(s_modal_onscreen && !s_modal_charging); cl_assert_equal_i(s_vibe_count, 1); - cl_assert_equal_i(s_led_state, StatusLedState_FullyCharged); } diff --git a/tests/fw/shell/normal/test_display_calibration_prompt.c b/tests/fw/shell/normal/test_display_calibration_prompt.c deleted file mode 100644 index a7da42f8ef..0000000000 --- a/tests/fw/shell/normal/test_display_calibration_prompt.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "clar.h" - -#include "shell/normal/display_calibration_prompt.h" -#include "kernel/events.h" - -// stubs -////////////////////// - -#include "stubs_confirmation_dialog.h" -#include "stubs_dialog.h" -#include "stubs_i18n.h" -#include "stubs_logging.h" -#include "stubs_modal_manager.h" -#include "stubs_passert.h" - -void window_single_click_subscribe(ButtonId button_id, ClickHandler handler) {} -void settings_display_calibration_push(WindowStack *window_stack) {} - -// fakes -////////////////////// - -#include "fake_new_timer.h" - -static const char s_mfg_serial_failing[] = "Q402445E027E"; -static const char s_mfg_serial_passing[] = "Q402445FAYYY"; - -static bool s_should_prompt_display_calibration; -static GPoint s_mfg_offset; -static GPoint s_user_offset; -static bool s_launcher_callback_added; -static const char *s_mfg_serial; - -GPoint mfg_info_get_disp_offsets(void) { - return s_mfg_offset; -} -const char* mfg_get_serial_number(void) { - return s_mfg_serial; -} -GPoint shell_prefs_get_display_offset(void) { - return gpoint_add(s_mfg_offset, s_user_offset); -} -bool shell_prefs_should_prompt_display_calibration(void) { - return s_should_prompt_display_calibration; -} -void shell_prefs_set_should_prompt_display_calibration(bool should_prompt) { - s_should_prompt_display_calibration = should_prompt; -} -bool gpoint_equal(const GPoint * const point_a, const GPoint * const point_b) { - return (point_a->x == point_b->x && point_a->y == point_b->y); -} -void launcher_task_add_callback(CallbackEventCallback callback, void *data) { - cl_assert(s_launcher_callback_added == false); - s_launcher_callback_added = true; -} - - -// helpers -////////////////////// - -static bool prv_does_open_dialog() { - TimerID timer = stub_new_timer_get_next(); - if (timer == TIMER_INVALID_ID) { - return false; - } - stub_new_timer_fire(stub_new_timer_get_next()); - bool callback_fired = s_launcher_callback_added; - s_launcher_callback_added = false; - return callback_fired; -} - -// defined in display_calibration_prompt.c -bool prv_is_known_misaligned_serial_number(const char *serial); - -// Tests -////////////////////// - -void test_display_calibration_prompt__initialize(void) { - s_should_prompt_display_calibration = true; - s_mfg_offset = GPointZero; - s_user_offset = GPointZero; - s_launcher_callback_added = false; - s_mfg_serial = s_mfg_serial_failing; -} - -void test_display_calibration_prompt__clean_system(void) { - // clean system startup -> open dialog - display_calibration_prompt_show_if_needed(); - cl_assert(prv_does_open_dialog()); -} - -void test_display_calibration_prompt__mfg_offset(void) { - // startup with existing mfg offset but no user offset -> open dialog - s_mfg_offset = GPoint(0, 0); - display_calibration_prompt_show_if_needed(); - cl_assert(prv_does_open_dialog()); -} - -void test_display_calibration_prompt__user_offset(void) { - // startup with existing user offset -> don't open dialog - s_user_offset = GPoint(1, 2); - display_calibration_prompt_show_if_needed(); - cl_assert(!prv_does_open_dialog()); -} - -void test_display_calibration_prompt__prefs(void) { - // startup with should_prompt_display_calibration already false -> don't open dialog - s_should_prompt_display_calibration = false; - display_calibration_prompt_show_if_needed(); - cl_assert(!prv_does_open_dialog()); -} - -void test_display_calibration_prompt__conditions(void) { - // watch isn't recognized as a watch with known calibration issues -> don't open dialog - s_mfg_serial = s_mfg_serial_passing; - display_calibration_prompt_show_if_needed(); - cl_assert(!prv_does_open_dialog()); -} - -void test_display_calibration_prompt__serials(void) { - // test the prv_is_known_misaligned_serial_number function - cl_assert(!prv_is_known_misaligned_serial_number(s_mfg_serial_passing)); - cl_assert(prv_is_known_misaligned_serial_number(s_mfg_serial_failing)); -} diff --git a/tests/fw/shell/normal/test_normal_system_app_state_machine.c b/tests/fw/shell/normal/test_normal_system_app_state_machine.c index 29f8986224..f76e085830 100644 --- a/tests/fw/shell/normal/test_normal_system_app_state_machine.c +++ b/tests/fw/shell/normal/test_normal_system_app_state_machine.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/shell/normal/wscript b/tests/fw/shell/normal/wscript deleted file mode 100644 index d6a85bd8f7..0000000000 --- a/tests/fw/shell/normal/wscript +++ /dev/null @@ -1,20 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = " src/fw/shell/normal/system_app_state_machine.c", - test_sources_ant_glob = "test_normal_system_app_state_machine.c") - - clar(ctx, - sources_ant_glob = " src/fw/shell/normal/battery_ui_fsm.c" - " src/fw/services/common/battery/battery_curve.c", - test_sources_ant_glob = 'test_battery_ui_fsm.c', - override_includes=['dummy_board'], - platforms = ['snowy']) - - clar(ctx, - sources_ant_glob = " src/fw/shell/normal/display_calibration_prompt.c", - test_sources_ant_glob = 'test_display_calibration_prompt.c', - override_includes=['dummy_board'], - platforms = ['spalding']) -# vim:filetype=python diff --git a/tests/fw/shell/normal/wscript_build b/tests/fw/shell/normal/wscript_build new file mode 100644 index 0000000000..4c7cd0e164 --- /dev/null +++ b/tests/fw/shell/normal/wscript_build @@ -0,0 +1,14 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = " src/fw/shell/normal/system_app_state_machine.c", + test_sources_ant_glob = "test_normal_system_app_state_machine.c") + +clar(ctx, + sources_ant_glob = " src/fw/shell/normal/battery_ui_fsm.c" + " src/fw/services/battery/voltage/battery_curve.c", + test_sources_ant_glob = 'test_battery_ui_fsm.c', + override_includes=['dummy_board'], + platforms = ['obelix']) + +# vim:filetype=python diff --git a/tests/fw/shell/test_system_theme.c b/tests/fw/shell/test_system_theme.c index f7f2e35476..7456c93a68 100644 --- a/tests/fw/shell/test_system_theme.c +++ b/tests/fw/shell/test_system_theme.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -51,11 +38,11 @@ void test_system_theme__convert_content_size_between_platforms(void) { // Passing in an invalid from_platform or to_platform should assert cl_assert_passert(prv_convert_content_size_between_platforms(PreferredContentSizeSmall, - PlatformTypeEmery + 1, + PlatformTypeGabbro + 1, PlatformTypeBasalt)); cl_assert_passert(prv_convert_content_size_between_platforms(PreferredContentSizeSmall, PlatformTypeBasalt, - PlatformTypeEmery + 1)); + PlatformTypeGabbro + 1)); // Converting from Emery to Basalt should return one size smaller cl_assert_equal_i(prv_convert_content_size_between_platforms(PreferredContentSizeLarge, diff --git a/tests/fw/shell/wscript b/tests/fw/shell/wscript deleted file mode 100644 index e523686d57..0000000000 --- a/tests/fw/shell/wscript +++ /dev/null @@ -1,10 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob=( - " src/fw/shell/system_theme.c" - ), - test_sources_ant_glob="test_system_theme.c") - -# vim:filetype=python diff --git a/tests/fw/shell/wscript_build b/tests/fw/shell/wscript_build new file mode 100644 index 0000000000..299dae8422 --- /dev/null +++ b/tests/fw/shell/wscript_build @@ -0,0 +1,9 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob=( + " src/fw/shell/system_theme.c" + ), + test_sources_ant_glob="test_system_theme.c") + +# vim:filetype=python diff --git a/tests/fw/system/test_flash_region.c b/tests/fw/system/test_flash_region.c index fab8aad976..dc0edec879 100644 --- a/tests/fw/system/test_flash_region.c +++ b/tests/fw/system/test_flash_region.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "flash_region/flash_region.h" diff --git a/tests/fw/system/wscript b/tests/fw/system/wscript deleted file mode 100644 index dd00c5a1cc..0000000000 --- a/tests/fw/system/wscript +++ /dev/null @@ -1,10 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/fw/flash_region/flash_region.c", - test_sources_ant_glob = "test_flash_region.c", - override_includes=['dummy_board'], - platforms=['tintin']) - -# vim:filetype=python diff --git a/tests/fw/system/wscript_build b/tests/fw/system/wscript_build new file mode 100644 index 0000000000..1e568a66cc --- /dev/null +++ b/tests/fw/system/wscript_build @@ -0,0 +1,9 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/flash_region/flash_region.c", + test_sources_ant_glob = "test_flash_region.c", + override_includes=['dummy_board'], + platforms=['obelix']) + +# vim:filetype=python diff --git a/tests/fw/test_alarm.c b/tests/fw/test_alarm.c index aea5f34734..20f408ea0e 100644 --- a/tests/fw/test_alarm.c +++ b/tests/fw/test_alarm.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "test_alarm_common.h" diff --git a/tests/fw/test_alarm_common.h b/tests/fw/test_alarm_common.h index aaf9a8aeaf..2e015191a0 100644 --- a/tests/fw/test_alarm_common.h +++ b/tests/fw/test_alarm_common.h @@ -1,32 +1,20 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/alarms/alarm.h" -#include "services/normal/alarms/alarm_pin.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/alarms/alarm.h" +#include "pbl/services/alarms/alarm_pin.h" #include "drivers/rtc.h" #include "resource/timeline_resource_ids.auto.h" -#include "services/common/cron.h" -#include "services/common/new_timer/new_timer.h" -#include "services/common/system_task.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/settings/settings_file.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/cron.h" +#include "pbl/services/new_timer/new_timer.h" +#include "pbl/services/system_task.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/settings/settings_file.h" +#include "pbl/services/timeline/item.h" #include "util/attributes.h" #include diff --git a/tests/fw/test_alarm_smart.c b/tests/fw/test_alarm_smart.c index c87d0ca7d6..9fd01d8dfb 100644 --- a/tests/fw/test_alarm_smart.c +++ b/tests/fw/test_alarm_smart.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "test_alarm_common.h" @@ -198,7 +185,7 @@ void test_alarm_smart__trigger_15_min_early_light_sleep(void) { s_sleep_state_seconds += 5 * SECONDS_PER_MINUTE; s_last_vmc = i == 2 ? 1 : 0; prv_set_time(s_current_day, 10, (i + 1) * 5); - PBL_LOG(LOG_LEVEL_DEBUG, "Iteration #%d, sleep %d seconds", i, s_sleep_state_seconds); + PBL_LOG_DBG("Iteration #%d, sleep %d seconds", i, s_sleep_state_seconds); stub_new_timer_invoke(1); if (i < num_checks - 1) { // Smart alarm non-trigger checks @@ -242,7 +229,7 @@ void test_alarm_smart__trigger_at_timeout(void) { s_sleep_state_seconds = (i + 1) * 5 * SECONDS_PER_MINUTE; s_last_vmc = (i == 5); prv_set_time(s_current_day, 10, i * 5); - PBL_LOG(LOG_LEVEL_DEBUG, "Iteration #%d, sleep %d seconds", i, s_sleep_state_seconds); + PBL_LOG_DBG("Iteration #%d, sleep %d seconds", i, s_sleep_state_seconds); stub_new_timer_invoke(1); if (i < num_checks - 1) { // Smart alarm non-trigger checks diff --git a/tests/fw/test_alerts.c b/tests/fw/test_alerts.c index 21b3342fd2..9007b7cb68 100644 --- a/tests/fw/test_alerts.c +++ b/tests/fw/test_alerts.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/notifications/alerts.h" -#include "services/normal/notifications/alerts_private.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/notifications/alerts.h" +#include "pbl/services/notifications/alerts_private.h" #include "clar.h" diff --git a/tests/fw/test_app_manager.c b/tests/fw/test_app_manager.c index ff4f3a0779..a1a480c138 100644 --- a/tests/fw/test_app_manager.c +++ b/tests/fw/test_app_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -20,7 +7,6 @@ #include "applib/app_comm.h" #include "applib/graphics/framebuffer.h" -#include "applib/rockyjs/rocky_res.h" #include "applib/ui/window_stack.h" #include "applib/ui/window_stack_private.h" #include "drivers/mpu.h" @@ -41,7 +27,6 @@ // Stubs #include "stubs_accel_service.h" #include "stubs_analytics.h" -#include "stubs_analytics_external.h" #include "stubs_animation_service.h" #include "stubs_app_state.h" #include "stubs_applib_resource.h" @@ -58,6 +43,7 @@ #include "stubs_mutex.h" #include "stubs_passert.h" #include "stubs_persist.h" +#include "stubs_powermode_service.h" #include "stubs_print.h" #include "stubs_prompt.h" #include "stubs_rand_ptr.h" @@ -252,6 +238,9 @@ void launcher_cancel_force_quit(void) { void light_reset_user_controlled(void) { } +void light_set_system_color(void) { +} + void mpu_set_task_configurable_regions(MemoryRegion_t *task_params, const MpuRegion **region_ptrs) { } @@ -289,10 +278,6 @@ void reboot_set_slot_of_last_launched_app(uint32_t app_slot) { void sys_exit(int status) { } -RockyResourceValidation rocky_app_validate_resources(const PebbleProcessMd *md) { - return RockyResourceValidation_NotRocky; -} - status_t app_cache_app_launched(AppInstallId id) { return 0; } @@ -306,9 +291,18 @@ const PebbleProcessMd* system_app_state_machine_get_last_registered_app(void) { void system_app_state_machine_register_app_launch(const PebbleProcessMd* app) { } +void health_tracking_ui_register_app_launch(AppInstallId app_id) { +} + void sys_vibe_history_stop_collecting(void) { } +void sys_vibe_pattern_clear(void) { +} + +void speaker_service_stop_for_task(PebbleTask task) { +} + Heap *worker_state_get_heap(void) { return NULL; } diff --git a/tests/fw/test_app_run_state.c b/tests/fw/test_app_run_state.c index edbf88b7de..6fea8cabf8 100644 --- a/tests/fw/test_app_run_state.c +++ b/tests/fw/test_app_run_state.c @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" //#include "comm/remote.h" #include "process_management/app_run_state.h" -#include "services/common/comm_session/protocol.h" +#include "pbl/services/comm_session/protocol.h" #include "system/passert.h" #include "util/attributes.h" diff --git a/tests/fw/test_battery_monitor.c b/tests/fw/test_battery_monitor.c index 6d14b78e06..5ec595a178 100644 --- a/tests/fw/test_battery_monitor.c +++ b/tests/fw/test_battery_monitor.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/battery/battery_monitor.h" -#include "services/common/battery/battery_state.h" -#include "services/common/battery/battery_curve.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/battery/battery_monitor.h" +#include "pbl/services/battery/battery_state.h" +#include "pbl/services/battery/battery_curve.h" #include "clar.h" @@ -135,6 +122,11 @@ void test_battery_monitor__initialize(void) { s_stop_mode_allowed = true; fake_rtc_init(0, 0); fake_rtc_auto_increment_ticks(0); + + // The discharge curve's 100% reference is mutated by + // battery_curve_set_full_voltage() during charge-complete handling and + // persists across tests; restore it so each test starts from a clean curve. + battery_curve_reset_for_tests(); } void test_battery_monitor__cleanup(void) { @@ -285,10 +277,15 @@ critical -> lpm lpm -> critical critical -> good */ +// Must mirror the PowerStateID enum in src/fw/services/battery/battery_monitor.c. +// PowerStatePluggedIn was added in 9d3a9548f ("add plugged in status"): whenever +// the watch is plugged in, it ignores battery level/charge status and operates +// normally, so the state machine reports PluggedIn rather than Good/LowPower. typedef enum { PowerStateGood, PowerStateLowPower, PowerStateCritical, + PowerStatePluggedIn, PowerStateStandby } PowerStateID; extern PowerStateID s_power_state; @@ -311,12 +308,12 @@ void test_battery_monitor__transitions(void) { cl_assert(s_in_low_power); cl_assert_equal_i(s_power_state, PowerStateLowPower); - // lpm -> good + // lpm -> good (plugged in => PluggedIn, which resumes normal operation) fake_battery_set_charging(true); fake_battery_set_connected(true); periodic_timer_trigger(1); cl_assert(!s_in_low_power); - cl_assert_equal_i(s_power_state, PowerStateGood); + cl_assert_equal_i(s_power_state, PowerStatePluggedIn); // good -> critical fake_battery_set_millivolts(critical_mv); @@ -340,14 +337,14 @@ void test_battery_monitor__transitions(void) { cl_assert(s_in_low_power); cl_assert_equal_i(s_power_state, PowerStateCritical); - // critical -> good + // critical -> good (plugged in => PluggedIn, which resumes normal operation) fake_battery_set_charging(true); fake_battery_set_connected(true); fake_battery_set_millivolts(good_mv); periodic_timer_trigger(20); cl_assert(!battery_monitor_critical_lockout()); cl_assert(!s_in_low_power); - cl_assert_equal_i(s_power_state, PowerStateGood); + cl_assert_equal_i(s_power_state, PowerStatePluggedIn); } void test_battery_monitor__low_first_run(void) { @@ -398,10 +395,13 @@ void test_battery_monitor__critical_plugged_in(void) { cl_assert(battery_monitor_critical_lockout()); cl_assert_equal_i(standby_timer_get_timeout(), 30000); + + // Plugging in transitions to PowerStatePluggedIn, whose exit-from-critical + // action (prv_exit_critical) stops the standby timer, so standby is averted. fake_battery_set_charging(true); fake_battery_set_connected(true); periodic_timer_trigger(1); - standby_timer_trigger(1); + cl_assert(!standby_timer_is_scheduled()); cl_assert(!s_entered_standby); } @@ -419,12 +419,12 @@ void test_battery_monitor__increase_discharging(void) { // Should be stable by now // Shouldn't update percent (actually, shouldn't even send events.) - PBL_LOG(LOG_LEVEL_DEBUG, "Shouldn't be any updates"); - PBL_LOG(LOG_LEVEL_DEBUG, "▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼"); + PBL_LOG_DBG("Shouldn't be any updates"); + PBL_LOG_DBG("▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼"); fake_battery_set_millivolts(high_mv); periodic_timer_trigger(20); cl_assert_equal_i(battery_get_charge_state().charge_percent, 50); - PBL_LOG(LOG_LEVEL_DEBUG, "▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲"); + PBL_LOG_DBG("▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲"); // Should still update if it goes lower fake_battery_set_millivolts(lower_mv); @@ -480,10 +480,16 @@ void test_battery_monitor__connection_states(void) { periodic_timer_trigger(1); cl_assert(!s_in_low_power && battery_get_charge_state().charge_percent == 60); - // Discharging but connected - The charge should update so 60% is 100% + // Discharging but connected: prv_update_done_charging() shifts the discharge + // curve's 100% reference down to (current voltage - fudge). With the current + // gabbro/silk curve data the 60% charge voltage (3970mV) is far below the + // discharge curve's 90% point (4120mV), and battery_curve_set_full_voltage() + // clamps the full-voltage so it cannot drop below that 90% point + 1. So the + // 3970mV reading interpolates to ~73% raw, which battery_get_charge_state() + // bins to 70% - it no longer clamps to 100%. fake_battery_set_charging(false); periodic_timer_trigger(1); - cl_assert_equal_i(battery_get_charge_state().charge_percent, 100); + cl_assert_equal_i(battery_get_charge_state().charge_percent, 70); } void test_battery_monitor__battery_get_charge_state(void) { diff --git a/tests/fw/test_char_iterator.c b/tests/fw/test_char_iterator.c index 1c82ec85ce..19d9cab727 100644 --- a/tests/fw/test_char_iterator.c +++ b/tests/fw/test_char_iterator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/iterator.h" #include "applib/graphics/framebuffer.h" diff --git a/tests/fw/test_clar.c b/tests/fw/test_clar.c index fe4b334dbd..699cf087a8 100644 --- a/tests/fw/test_clar.c +++ b/tests/fw/test_clar.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "pebble_asserts.h" diff --git a/tests/fw/test_codepoint.c b/tests/fw/test_codepoint.c new file mode 100644 index 0000000000..5ddf46bd61 --- /dev/null +++ b/tests/fw/test_codepoint.c @@ -0,0 +1,154 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "applib/fonts/codepoint.h" + +#include "clar.h" + +/////////////////////////////////////////////////////////// +// Stubs + +#include "stubs_logging.h" +#include "stubs_passert.h" + +/////////////////////////////////////////////////////////// +// Tests + +void test_codepoint__initialize(void) { +} + +void test_codepoint__cleanup(void) { +} + +void test_codepoint__is_unicode_space(void) { + // All Unicode space variants should be recognized + cl_assert(codepoint_is_unicode_space(NO_BREAK_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(EN_QUAD_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(EM_QUAD_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(EN_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(THREE_PER_EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(FOUR_PER_EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(SIX_PER_EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(FIGURE_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(PUNCTUATION_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(THIN_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(HAIR_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(NARROW_NO_BREAK_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(MEDIUM_MATHEMATICAL_SPACE_CODEPOINT)); + cl_assert(codepoint_is_unicode_space(IDEOGRAPHIC_SPACE_CODEPOINT)); + + // Regular space and other characters should not match + cl_assert(!codepoint_is_unicode_space(SPACE_CODEPOINT)); + cl_assert(!codepoint_is_unicode_space('A')); + cl_assert(!codepoint_is_unicode_space(ZERO_WIDTH_SPACE_CODEPOINT)); + cl_assert(!codepoint_is_unicode_space(NEWLINE_CODEPOINT)); + cl_assert(!codepoint_is_unicode_space(ELLIPSIS_CODEPOINT)); +} + +void test_codepoint__end_of_word_breakable_spaces(void) { + // Unicode spaces with TR14 class BA (Break After) should be word breaks + cl_assert(codepoint_is_end_of_word(EN_QUAD_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(EM_QUAD_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(EN_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(THREE_PER_EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(FOUR_PER_EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(SIX_PER_EM_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(PUNCTUATION_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(THIN_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(HAIR_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(MEDIUM_MATHEMATICAL_SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(IDEOGRAPHIC_SPACE_CODEPOINT)); +} + +void test_codepoint__end_of_word_non_breaking_spaces(void) { + // Non-breaking spaces (TR14 class GL) must NOT be word breaks + cl_assert(!codepoint_is_end_of_word(NO_BREAK_SPACE_CODEPOINT)); + cl_assert(!codepoint_is_end_of_word(FIGURE_SPACE_CODEPOINT)); + cl_assert(!codepoint_is_end_of_word(NARROW_NO_BREAK_SPACE_CODEPOINT)); +} + +void test_codepoint__end_of_word_existing(void) { + // Existing word break characters should still work + cl_assert(codepoint_is_end_of_word(NULL_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(NEWLINE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(SPACE_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(HYPHEN_CODEPOINT)); + cl_assert(codepoint_is_end_of_word(ZERO_WIDTH_SPACE_CODEPOINT)); + + // Regular characters should not be word breaks + cl_assert(!codepoint_is_end_of_word('A')); + cl_assert(!codepoint_is_end_of_word('0')); +} + +void test_codepoint__should_skip_controls(void) { + // C0 controls (except newline) should be skipped + cl_assert(codepoint_should_skip(NULL_CODEPOINT)); + cl_assert(codepoint_should_skip(0x01)); + cl_assert(codepoint_should_skip(0x07)); // bell + cl_assert(codepoint_should_skip(0x09)); // tab + cl_assert(codepoint_should_skip(0x0D)); // carriage return + cl_assert(codepoint_should_skip(0x1F)); + + // Newline must NOT be skipped (it's a line break) + cl_assert(!codepoint_should_skip(NEWLINE_CODEPOINT)); + + // C1 controls (U+0080-U+009F) should be skipped — they have no visible glyphs + // and otherwise render as the font's wildcard box. + cl_assert(codepoint_should_skip(0x80)); + cl_assert(codepoint_should_skip(0x85)); // NEL + cl_assert(codepoint_should_skip(0x9F)); + + // Word-Joiner block (U+2061-U+206F): invisible math operators, BiDi isolates, + // deprecated formatting — none have visible glyphs in our fonts. + // U+2060 WORD JOINER is excluded — it is needed for CJK word segmentation. + cl_assert(!codepoint_should_skip(0x2060)); // word joiner — handled separately + cl_assert(codepoint_should_skip(0x2062)); // invisible times + cl_assert(codepoint_should_skip(0x2066)); // LRI + cl_assert(codepoint_should_skip(0x2069)); // PDI + cl_assert(codepoint_should_skip(0x206F)); // nominal digit shapes (deprecated) + + // Interlinear annotation anchors and tag characters + cl_assert(codepoint_should_skip(0xFFF9)); // interlinear anchor + cl_assert(codepoint_should_skip(0xFFFB)); // interlinear terminator + cl_assert(codepoint_should_skip(0xE0001)); // language tag + cl_assert(codepoint_should_skip(0xE007F)); // cancel tag + + // Supplementary Private Use Area-B (Apple SF Symbols) should be skipped + cl_assert(codepoint_should_skip(0xF0000)); + cl_assert(codepoint_should_skip(0x100000)); // start of typical SF Symbols range + cl_assert(codepoint_should_skip(0x10FFFD)); + + // Boundary characters around C1 must NOT be skipped + cl_assert(!codepoint_should_skip(0x7F)); // DEL — handled by formatting indicator + cl_assert(!codepoint_should_skip(0xA0)); // no-break space — printable + cl_assert(!codepoint_should_skip(' ')); + cl_assert(!codepoint_should_skip('A')); + + // Codepoints just outside the new ranges must NOT be skipped + cl_assert(!codepoint_should_skip(0x205F)); // medium math space — printable + cl_assert(!codepoint_should_skip(0x2070)); // superscript zero — printable + cl_assert(!codepoint_should_skip(0xFFF8)); // unassigned, but not in our skip range + cl_assert(!codepoint_should_skip(0xFFFC)); // object replacement — handled by formatting indicator + cl_assert(!codepoint_should_skip(0xEFFFF)); +} + +void test_codepoint__formatting_indicator_invisibles(void) { + // Object replacement character (used by iOS for inline attachments) must be + // treated as a formatting indicator so it doesn't render as a tofu box. + cl_assert(codepoint_is_formatting_indicator(0xFFFC)); + // BiDi controls — full U+202A-U+202E block including the previously-missed + // RLE (U+202B) and RLO (U+202E) + cl_assert(codepoint_is_formatting_indicator(0x202A)); + cl_assert(codepoint_is_formatting_indicator(0x202B)); + cl_assert(codepoint_is_formatting_indicator(0x202C)); + cl_assert(codepoint_is_formatting_indicator(0x202D)); + cl_assert(codepoint_is_formatting_indicator(0x202E)); + // Existing entries still recognised + cl_assert(codepoint_is_formatting_indicator(0x7F)); + cl_assert(codepoint_is_formatting_indicator(0xFEFF)); + // Unrelated codepoints are not + cl_assert(!codepoint_is_formatting_indicator('A')); + cl_assert(!codepoint_is_formatting_indicator(0xFFFD)); // replacement char +} diff --git a/tests/fw/test_data_logging.c b/tests/fw/test_data_logging.c index 8a44297229..963b8896f8 100644 --- a/tests/fw/test_data_logging.c +++ b/tests/fw/test_data_logging.c @@ -1,38 +1,25 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/data_logging.h" #include "util/uuid.h" #include "process_management/pebble_process_md.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/filesystem/flash_translation.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/filesystem/flash_translation.h" -#include "services/common/comm_session/protocol.h" -#include "services/common/comm_session/session.h" -#include "services/common/comm_session/session_send_buffer.h" -#include "services/common/comm_session/session_transport.h" +#include "pbl/services/comm_session/protocol.h" +#include "pbl/services/comm_session/session.h" +#include "pbl/services/comm_session/session_send_buffer.h" +#include "pbl/services/comm_session/session_transport.h" -#include "services/normal/data_logging/data_logging_service.h" -#include "services/normal/data_logging/dls_private.h" -#include "services/normal/data_logging/dls_list.h" -#include "services/normal/data_logging/dls_storage.h" +#include "pbl/services/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/dls_private.h" +#include "pbl/services/data_logging/dls_list.h" +#include "pbl/services/data_logging/dls_storage.h" -#include "services/common/regular_timer.h" +#include "pbl/services/regular_timer.h" #include "system/logging.h" #include "system/passert.h" @@ -103,7 +90,7 @@ static uint32_t s_prev_send_data_bytes; static void prv_transport_sent_data_cb(uint16_t endpoint_id, const uint8_t* data, unsigned int data_length) { - PBL_LOG(LOG_LEVEL_INFO, "Received %d bytes of data from watch", data_length); + PBL_LOG_INFO("Received %d bytes of data from watch", data_length); if (data_length >= sizeof(s_prev_send_data_hdr)) { memcpy(&s_prev_send_data_hdr, data, sizeof(s_prev_send_data_hdr)); data_length -= sizeof(s_prev_send_data_hdr); @@ -125,7 +112,7 @@ static void prv_init_fake_flash(void) { pfs_init(false); pfs_format(false /* write erase headers */); - PBL_LOG(LOG_LEVEL_INFO, "\nFile system size: %d, avail: %d", (int)pfs_get_size(), + PBL_LOG_INFO("\nFile system size: %d, avail: %d", (int)pfs_get_size(), (int)get_available_pfs_space()); } @@ -181,7 +168,7 @@ static void prv_check_session_data(DataLoggingSessionRef logging_session, uint32 // log some random data, return its crc32 static uint32_t prv_log_random_data(DataLoggingSessionRef logging_session, int item_size, int num_items) { - PBL_LOG(LOG_LEVEL_INFO, "Logging %d bytes", item_size * num_items); + PBL_LOG_INFO("Logging %d bytes", item_size * num_items); uint8_t *random_buf; uint32_t random_crc = prv_get_random_buffer(&random_buf, item_size * num_items); @@ -336,11 +323,11 @@ void test_data_logging__fill_quota(void) { int total_bytes = 0; for (int i = 0; i < num_sessions; i++) { uint32_t size = dls_test_get_num_bytes(logging_sessions[i]); - PBL_LOG(LOG_LEVEL_INFO, "Size of session %d: %d", i, size); + PBL_LOG_INFO("Size of session %d: %d", i, size); total_bytes += size; } - PBL_LOG(LOG_LEVEL_INFO, "total bytes: %d", total_bytes); + PBL_LOG_INFO("total bytes: %d", total_bytes); cl_assert(total_bytes < DLS_TOTAL_STORAGE_BYTES); // We should still be able to create more sessions up to the max @@ -355,11 +342,11 @@ void test_data_logging__fill_quota(void) { total_bytes = 0; for (int i = 0; i < num_sessions; i++) { uint32_t size = dls_test_get_num_bytes(logging_sessions[i]); - PBL_LOG(LOG_LEVEL_INFO, "Size of session %d: %d", i, size); + PBL_LOG_INFO("Size of session %d: %d", i, size); total_bytes += size; } - PBL_LOG(LOG_LEVEL_INFO, "total bytes: %d", total_bytes); + PBL_LOG_INFO("total bytes: %d", total_bytes); cl_assert(total_bytes < DLS_TOTAL_STORAGE_BYTES); } diff --git a/tests/fw/test_debug_db.c b/tests/fw/test_debug_db.c index 88e1645252..aebf17a521 100644 --- a/tests/fw/test_debug_db.c +++ b/tests/fw/test_debug_db.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "debug/legacy/debug_db.h" diff --git a/tests/fw/test_flash_logging.c b/tests/fw/test_flash_logging.c index d9981a5d1b..1ac9046e21 100644 --- a/tests/fw/test_flash_logging.c +++ b/tests/fw/test_flash_logging.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" @@ -40,17 +27,15 @@ #include #include -#define FLASH_SIZE (4 * 1024 * 1024) - void test_flash_logging__initialize(void) { - fake_spi_flash_init(0, FLASH_SIZE); + fake_spi_flash_init(0, BOARD_NOR_FLASH_SIZE); } void test_flash_logging__cleanup(void) { uint32_t size = FLASH_REGION_DEBUG_DB_BEGIN - 0; fake_flash_assert_region_untouched(0, size); - size = FLASH_SIZE - FLASH_REGION_DEBUG_DB_END; + size = BOARD_NOR_FLASH_SIZE - FLASH_REGION_DEBUG_DB_END; fake_flash_assert_region_untouched(FLASH_REGION_DEBUG_DB_END, size); fake_spi_flash_cleanup(); } @@ -135,7 +120,7 @@ static bool prv_flash_log_line_dump(uint8_t *msg, uint32_t tot_len) { bool s_completed = false; bool s_completed_success = false; static void prv_flash_log_dump_completed_cb(bool success) { - PBL_LOG(LOG_LEVEL_DEBUG, "Called prv_flash_log_dump_completed_cb(%d)", (int)success); + PBL_LOG_DBG("Called prv_flash_log_dump_completed_cb(%d)", (int)success); s_completed = true; s_completed_success = success; } @@ -323,7 +308,7 @@ static bool flash_log_line_dump_long_lived(uint8_t *msg, uint32_t tot_len) { memcpy(&buf, (char*)msg, tot_len); buf[tot_len] = '\0'; - PBL_LOG(LOG_LEVEL_DEBUG, "flash_log_line_dump_long_lived: got %s", buf); + PBL_LOG_DBG("flash_log_line_dump_long_lived: got %s", buf); int curr_val; int filled = sscanf(buf, "Loop Counter %d", &curr_val); @@ -334,7 +319,7 @@ static bool flash_log_line_dump_long_lived(uint8_t *msg, uint32_t tot_len) { } s_long_lived_last_val = curr_val; - PBL_LOG(LOG_LEVEL_DEBUG, "flash_log_line_dump_long_lived: got %s, last_val:%d", buf, + PBL_LOG_DBG("flash_log_line_dump_long_lived: got %s, last_val:%d", buf, s_long_lived_last_val); return (true); diff --git a/tests/fw/test_freertos_utils.c b/tests/fw/test_freertos_utils.c index ad1ee66eea..c7e62a5cda 100644 --- a/tests/fw/test_freertos_utils.c +++ b/tests/fw/test_freertos_utils.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "os/tick.h" diff --git a/tests/fw/test_getting_started_button_combo.c b/tests/fw/test_getting_started_button_combo.c index 2dded959bb..9d6beb6f9b 100644 --- a/tests/fw/test_getting_started_button_combo.c +++ b/tests/fw/test_getting_started_button_combo.c @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "apps/prf_apps/recovery_first_use_app/getting_started_button_combo.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "apps/prf/recovery_first_use/getting_started_button_combo.h" #include "clar.h" @@ -59,9 +46,6 @@ void process_manager_send_callback_event_to_process(PebbleTask task, void (*call callback(data); } -void accessory_imaging_enable(bool enable) { -} - // Tests /////////////////////////////////////////////////////////////////////////////// diff --git a/tests/fw/test_heap.c b/tests/fw/test_heap.c index a9893c9b6b..d0ca6f3c51 100644 --- a/tests/fw/test_heap.c +++ b/tests/fw/test_heap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/heap.h" diff --git a/tests/fw/test_i18n.c b/tests/fw/test_i18n.c index 8e477b4ddb..5c7403c77b 100644 --- a/tests/fw/test_i18n.c +++ b/tests/fw/test_i18n.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "fixtures/load_test_resources.h" -#include "services/common/i18n/i18n.h" -#include "services/common/i18n/mo.h" -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/i18n/i18n.h" +#include "pbl/services/i18n/mo.h" +#include "pbl/services/filesystem/pfs.h" #include "resource/resource_ids.auto.h" #include "flash_region/flash_region.h" diff --git a/tests/fw/test_launcher_app_message.c b/tests/fw/test_launcher_app_message.c index f281bf54bb..15d14cd4cc 100644 --- a/tests/fw/test_launcher_app_message.c +++ b/tests/fw/test_launcher_app_message.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "applib/app_message/app_message_internal.h" #include "process_management/app_run_state.h" #include "process_management/launcher_app_message.h" -#include "services/common/comm_session/session_internal.h" +#include "pbl/services/comm_session/session_internal.h" #include "system/passert.h" #include "util/dict.h" #include "util/uuid.h" diff --git a/tests/fw/test_line_layout.c b/tests/fw/test_line_layout.c index fbf4d4e185..fc81129c85 100644 --- a/tests/fw/test_line_layout.c +++ b/tests/fw/test_line_layout.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/iterator.h" #include "applib/graphics/framebuffer.h" diff --git a/tests/fw/test_memory_layout.c b/tests/fw/test_memory_layout.c index a269c0fa86..01b2c38cee 100644 --- a/tests/fw/test_memory_layout.c +++ b/tests/fw/test_memory_layout.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/test_pebble_app_info.c b/tests/fw/test_pebble_app_info.c index 89423be338..5bd542661d 100644 --- a/tests/fw/test_pebble_app_info.c +++ b/tests/fw/test_pebble_app_info.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/test_pebble_process_md.c b/tests/fw/test_pebble_process_md.c index 3424e193bb..3b8ee28558 100644 --- a/tests/fw/test_pebble_process_md.c +++ b/tests/fw/test_pebble_process_md.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/test_phone_formatting.c b/tests/fw/test_phone_formatting.c index 90ee427745..74ab517cec 100644 --- a/tests/fw/test_phone_formatting.c +++ b/tests/fw/test_phone_formatting.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "popups/phone_formatting.h" diff --git a/tests/fw/test_process_manager.c b/tests/fw/test_process_manager.c index 8fcba4d5df..ab2ab81706 100644 --- a/tests/fw/test_process_manager.c +++ b/tests/fw/test_process_manager.c @@ -1,22 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "applib/rockyjs/rocky_res.h" #include "process_management/process_manager.h" #include "process_management/app_install_manager.h" #include "process_management/pebble_process_info.h" @@ -25,7 +11,6 @@ // Stubs #include "stubs_accel_service.h" #include "stubs_analytics.h" -#include "stubs_analytics_external.h" #include "stubs_animation_service.h" #include "stubs_app_cache.h" #include "stubs_app_manager.h" @@ -181,11 +166,6 @@ int process_metadata_get_res_bank_num(const PebbleProcessMd *md) { return s_process_metadata_get_res_bank_num__result; } -static RockyResourceValidation s_rocky_app_validate_resources__result; -RockyResourceValidation rocky_app_validate_resources(const PebbleProcessMd *md) { - return s_rocky_app_validate_resources__result; -} - static PebbleEvent* s_event_put__event; void event_put(PebbleEvent* event) { s_event_put__event = event; @@ -200,7 +180,6 @@ void event_reset_from_process_queue(PebbleTask task) { cl_fail("unexpected"); } void test_process_manager__initialize(void) { s_app_install_get_md__result = NULL; s_process_metadata_get_res_bank_num__result = 123; - s_rocky_app_validate_resources__result = RockyResourceValidation_NotRocky; s_app_manager_launch_new_app__callcount = 0; s_app_manager_launch_new_app__config = (__typeof__(s_app_manager_launch_new_app__config)){}; s_event_put__event = NULL; @@ -213,25 +192,3 @@ void test_process_manager__check_SDK_compatible(void) { } } -void test_process_manager__launch_valid_rocky_app(void) { - s_app_install_get_md__result = &(PebbleProcessMd){.is_rocky_app = true}; - s_rocky_app_validate_resources__result = RockyResourceValidation_Valid; - process_manager_launch_process(&(ProcessLaunchConfig){.id=1}); - - // app was launched, no events (especially no fetch event) on the queue - cl_assert_equal_b(1, s_app_manager_launch_new_app__callcount); - cl_assert_equal_p(s_app_install_get_md__result, - s_app_manager_launch_new_app__config.md); - cl_assert(s_event_put__event == NULL); -} - -void test_process_manager__launch_invalid_rocky_app(void) { - s_app_install_get_md__result = &(PebbleProcessMd){.is_rocky_app = true}; - s_rocky_app_validate_resources__result = RockyResourceValidation_Invalid; - process_manager_launch_process(&(ProcessLaunchConfig){.id=1}); - - // app wasn't launched, instead we see a fetch request - cl_assert_equal_b(0, s_app_manager_launch_new_app__callcount); - cl_assert(s_event_put__event != NULL); - cl_assert_equal_i(PEBBLE_APP_FETCH_REQUEST_EVENT, s_event_put__event->type); -} diff --git a/tests/fw/test_resource.c b/tests/fw/test_resource.c index eb3b123070..84c3627749 100644 --- a/tests/fw/test_resource.c +++ b/tests/fw/test_resource.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource/resource_ids.auto.h" #include "resource/resource_storage.h" diff --git a/tests/fw/test_text_layout.c b/tests/fw/test_text_layout.c index ac0ff01cff..604765138d 100644 --- a/tests/fw/test_text_layout.c +++ b/tests/fw/test_text_layout.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/iterator.h" #include "applib/graphics/utf8.h" @@ -44,7 +31,7 @@ #include "stubs_syscalls.h" #include "stubs_compiled_with_legacy2_sdk.h" -#if SCREEN_COLOR_DEPTH_BITS == 8 +#if CONFIG_SCREEN_COLOR_DEPTH_BITS == 8 #define FONT_LINE_DELTA 2 #else #define FONT_LINE_DELTA 0 diff --git a/tests/fw/test_timeline_api.c b/tests/fw/test_timeline_api.c index 7f463ed145..bf7abe9d4b 100644 --- a/tests/fw/test_timeline_api.c +++ b/tests/fw/test_timeline_api.c @@ -1,27 +1,14 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "util/uuid.h" -#include "services/normal/filesystem/pfs.h" -#include "services/normal/blob_db/pin_db.h" -#include "services/normal/timeline/attribute.h" -#include "services/normal/timeline/item.h" -#include "services/normal/timeline/timeline.h" +#include "pbl/services/filesystem/pfs.h" +#include "pbl/services/blob_db/pin_db.h" +#include "pbl/services/timeline/attribute.h" +#include "pbl/services/timeline/item.h" +#include "pbl/services/timeline/timeline.h" // Fixture //////////////////////////////////////////////////////////////// diff --git a/tests/fw/test_utf8.c b/tests/fw/test_utf8.c index 0dc5147652..f8feb27c9a 100644 --- a/tests/fw/test_utf8.c +++ b/tests/fw/test_utf8.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/utf8.h" #include "utf8_test_data.h" diff --git a/tests/fw/test_utf8_iterator.c b/tests/fw/test_utf8_iterator.c index 93dc58efca..1b090f51d2 100644 --- a/tests/fw/test_utf8_iterator.c +++ b/tests/fw/test_utf8_iterator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/iterator.h" #include "utf8_test_data.h" diff --git a/tests/fw/test_word_iterator.c b/tests/fw/test_word_iterator.c index 5699542e22..afbccd4527 100644 --- a/tests/fw/test_word_iterator.c +++ b/tests/fw/test_word_iterator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/iterator.h" #include "applib/graphics/framebuffer.h" @@ -184,6 +171,53 @@ void test_word_iterator__test_string_consecutive_newlines(void) { cl_assert(!iter_next(&word_iter)); } +void test_word_iterator__test_em_space_word_break(void) { + // EM SPACE (U+2003, UTF-8: \xe2\x80\x83) should act as a word break, + // so the first word is "AB" (not "ABCD"). + Iterator word_iter = (Iterator) { 0 }; + WordIterState word_iter_state = (WordIterState) { 0 }; + FontInfo font_info = { .max_height = 10 }; + + bool success = false; + const Utf8Bounds utf8_bounds = utf8_get_bounds(&success, "AB\xe2\x80\x83" "CD"); + cl_assert(success); + + const TextBoxParams text_box_params = (TextBoxParams) { + .utf8_bounds = &utf8_bounds, + .font = &font_info, + }; + + word_iter_init(&word_iter, &word_iter_state, &s_ctx, &text_box_params, utf8_bounds.start); + + // First word is "AB" — EM SPACE breaks the word + cl_assert(*word_iter_state.current.start == 'A'); + cl_assert_equal_i(word_iter_state.current.width_px, 2 * HORIZ_ADVANCE_PX); + // end points to the EM SPACE byte, not past "CD" + cl_assert((unsigned char)*word_iter_state.current.end == 0xe2); +} + +void test_word_iterator__test_no_break_space_no_word_break(void) { + // NO-BREAK SPACE (U+00A0, UTF-8: \xc2\xa0) should NOT act as a word break + Iterator word_iter = (Iterator) { 0 }; + WordIterState word_iter_state = (WordIterState) { 0 }; + FontInfo font_info = { .max_height = 10 }; + + bool success = false; + const Utf8Bounds utf8_bounds = utf8_get_bounds(&success, "AB\xc2\xa0" "CD"); + cl_assert(success); + + const TextBoxParams text_box_params = (TextBoxParams) { + .utf8_bounds = &utf8_bounds, + .font = &font_info, + }; + + word_iter_init(&word_iter, &word_iter_state, &s_ctx, &text_box_params, utf8_bounds.start); + + // Should be a single word (NO-BREAK SPACE doesn't break) + cl_assert(*word_iter_state.current.start == 'A'); + cl_assert(!iter_next(&word_iter)); +} + void test_word_iterator__test_string_terminating_newlines(void) { // Allocate mutable types Iterator word_iter = (Iterator) { 0 }; diff --git a/tests/fw/ui/recognizer/test_recognizer.c b/tests/fw/ui/recognizer/test_recognizer.c index 75e543574a..f6f14f750b 100644 --- a/tests/fw/ui/recognizer/test_recognizer.c +++ b/tests/fw/ui/recognizer/test_recognizer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/ui/recognizer/test_recognizer_impl.c b/tests/fw/ui/recognizer/test_recognizer_impl.c index 85cb2ae490..7f573ddd0e 100644 --- a/tests/fw/ui/recognizer/test_recognizer_impl.c +++ b/tests/fw/ui/recognizer/test_recognizer_impl.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "test_recognizer_impl.h" diff --git a/tests/fw/ui/recognizer/test_recognizer_impl.h b/tests/fw/ui/recognizer/test_recognizer_impl.h index 4c49bdf0da..53bdc852d9 100644 --- a/tests/fw/ui/recognizer/test_recognizer_impl.h +++ b/tests/fw/ui/recognizer/test_recognizer_impl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/fw/ui/recognizer/test_recognizer_manager.c b/tests/fw/ui/recognizer/test_recognizer_manager.c index 826d388b49..613748ccb0 100644 --- a/tests/fw/ui/recognizer/test_recognizer_manager.c +++ b/tests/fw/ui/recognizer/test_recognizer_manager.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/ui/recognizer/wscript b/tests/fw/ui/recognizer/wscript deleted file mode 100644 index a613546ed8..0000000000 --- a/tests/fw/ui/recognizer/wscript +++ /dev/null @@ -1,17 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = " src/fw/applib/ui/recognizer/recognizer.c" \ - " tests/fw/ui/recognizer/test_recognizer_impl.c", - test_sources_ant_glob = "test_recognizer.c") - - clar(ctx, - sources_ant_glob = " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/recognizer/recognizer.c" \ - " src/fw/applib/ui/recognizer/recognizer_manager.c" \ - " tests/fw/ui/recognizer/test_recognizer_impl.c", - defines = ['CAPABILITY_HAS_TOUCHSCREEN'], - test_sources_ant_glob = "test_recognizer_manager.c") -# vim:filetype=python diff --git a/tests/fw/ui/recognizer/wscript_build b/tests/fw/ui/recognizer/wscript_build new file mode 100644 index 0000000000..83b675d964 --- /dev/null +++ b/tests/fw/ui/recognizer/wscript_build @@ -0,0 +1,17 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = " src/fw/applib/ui/recognizer/recognizer.c" \ + " tests/fw/ui/recognizer/test_recognizer_impl.c", + test_sources_ant_glob = "test_recognizer.c") + +clar(ctx, + sources_ant_glob = " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/recognizer/recognizer.c" \ + " src/fw/applib/ui/recognizer/recognizer_manager.c" \ + " tests/fw/ui/recognizer/test_recognizer_impl.c", + defines = ['CONFIG_TOUCH'], + test_sources_ant_glob = "test_recognizer_manager.c") + +# vim:filetype=python diff --git a/tests/fw/ui/test_action_menu_window.c b/tests/fw/ui/test_action_menu_window.c index 18f8855179..9e11e3eceb 100644 --- a/tests/fw/ui/test_action_menu_window.c +++ b/tests/fw/ui/test_action_menu_window.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" @@ -23,7 +10,7 @@ #include "applib/ui/app_window_stack.h" #include "applib/ui/content_indicator.h" #include "applib/ui/content_indicator_private.h" -#include "apps/system_apps/settings/settings_notifications_private.h" +#include "apps/system/settings/notifications_private.h" #include "resource/resource.h" #include "shell/system_theme.h" #include "system/passert.h" @@ -77,6 +64,7 @@ GContext *graphics_context_get_current_context(void) { #include "stubs_syscall_internal.h" #include "stubs_syscalls.h" #include "stubs_task_watchdog.h" +#include "stubs_vibes.h" #include "stubs_window_manager.h" #include "stubs_window_stack.h" diff --git a/tests/fw/ui/test_animation.c b/tests/fw/ui/test_animation.c index c56afdd98c..445e57737d 100644 --- a/tests/fw/ui/test_animation.c +++ b/tests/fw/ui/test_animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/ui/test_animation_interpolate.c b/tests/fw/ui/test_animation_interpolate.c index ae2fc9c5b1..8b8a749466 100644 --- a/tests/fw/ui/test_animation_interpolate.c +++ b/tests/fw/ui/test_animation_interpolate.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/animation_interpolate.h" #include "applib/ui/animation.h" diff --git a/tests/fw/ui/test_animation_timing.c b/tests/fw/ui/test_animation_timing.c index 363e0a9207..e4abe7c295 100644 --- a/tests/fw/ui/test_animation_timing.c +++ b/tests/fw/ui/test_animation_timing.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "pebble_asserts.h" diff --git a/tests/fw/ui/test_content_indicator.c b/tests/fw/ui/test_content_indicator.c index 333ae78919..c57bdc86e1 100644 --- a/tests/fw/ui/test_content_indicator.c +++ b/tests/fw/ui/test_content_indicator.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/ui/test_emoji_fonts.c b/tests/fw/ui/test_emoji_fonts.c index 3f83e74e21..2c2b8b57f1 100644 --- a/tests/fw/ui/test_emoji_fonts.c +++ b/tests/fw/ui/test_emoji_fonts.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" diff --git a/tests/fw/ui/test_expandable_dialog.c b/tests/fw/ui/test_expandable_dialog.c index f42c08cdfc..eed1a61bae 100644 --- a/tests/fw/ui/test_expandable_dialog.c +++ b/tests/fw/ui/test_expandable_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" @@ -24,7 +11,7 @@ #include "applib/ui/window_private.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "util/graphics.h" #include "util/hash.h" diff --git a/tests/fw/ui/test_jumboji.c b/tests/fw/ui/test_jumboji.c index cdec859ce9..3858d61b51 100644 --- a/tests/fw/ui/test_jumboji.c +++ b/tests/fw/ui/test_jumboji.c @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/timeline/notification_jumboji_table.h" -#include "services/normal/timeline/notification_layout.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/timeline/notification_jumboji_table.h" +#include "pbl/services/timeline/notification_layout.h" #include "util/size.h" #include "clar.h" diff --git a/tests/fw/ui/test_kino_player.c b/tests/fw/ui/test_kino_player.c index d190c27fc4..3a95642e5f 100644 --- a/tests/fw/ui/test_kino_player.c +++ b/tests/fw/ui/test_kino_player.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/kino/kino_player.h" #include "applib/ui/kino/kino_reel.h" diff --git a/tests/fw/ui/test_kino_reel.c b/tests/fw/ui/test_kino_reel.c index 2b9db828ab..14f33c06c1 100644 --- a/tests/fw/ui/test_kino_reel.c +++ b/tests/fw/ui/test_kino_reel.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/kino/kino_reel.h" #include "applib/ui/kino/kino_reel_custom.h" diff --git a/tests/fw/ui/test_layer.c b/tests/fw/ui/test_layer.c index 153e237312..bd748cc6d4 100644 --- a/tests/fw/ui/test_layer.c +++ b/tests/fw/ui/test_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/layer.h" #include "applib/ui/layer_private.h" diff --git a/tests/fw/ui/test_layer_rect.c b/tests/fw/ui/test_layer_rect.c index 19308af26a..411eda5bd9 100644 --- a/tests/fw/ui/test_layer_rect.c +++ b/tests/fw/ui/test_layer_rect.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/layer.h" #include "applib/ui/layer_private.h" diff --git a/tests/fw/ui/test_menu_layer.c b/tests/fw/ui/test_menu_layer.c index 2092063c81..1599cc19eb 100644 --- a/tests/fw/ui/test_menu_layer.c +++ b/tests/fw/ui/test_menu_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "pebble_asserts.h" @@ -23,6 +10,7 @@ // Stubs ///////////////////// #include "stubs_app_state.h" +#include "stubs_click.h" #include "stubs_graphics.h" #include "stubs_heap.h" #include "stubs_logging.h" @@ -32,6 +20,7 @@ #include "stubs_ui_window.h" #include "stubs_process_manager.h" #include "stubs_unobstructed_area.h" +#include "stubs_vibes.h" // Fakes diff --git a/tests/fw/ui/test_menu_layer_system_cells.c b/tests/fw/ui/test_menu_layer_system_cells.c index 32659a062c..38b1427e81 100644 --- a/tests/fw/ui/test_menu_layer_system_cells.c +++ b/tests/fw/ui/test_menu_layer_system_cells.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" @@ -340,7 +327,7 @@ void prv_prepare_canvas_and_render_cells(MenuCellType cell_type, int16_t cell_wi ////////////////////// void test_menu_layer_system_cells__basic_cell_width_144_legacy2(void) { -#if PLATFORM_TINTIN || PLATFORM_SILK || PLATFORM_ASTERIX +#if defined(CONFIG_BOARD_FAMILY_ASTERIX) // NOTE: The generated bitmap will look really funky because it's rendering 8bit gbitmaps as // 1bit due to the legacy2 check in gbitmap_get_format. This is normal and expected. diff --git a/tests/fw/ui/test_notification_window.c b/tests/fw/ui/test_notification_window.c index c7095aaf95..2332d984e9 100644 --- a/tests/fw/ui/test_notification_window.c +++ b/tests/fw/ui/test_notification_window.c @@ -1,28 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "applib/preferred_content_size.h" #include "applib/ui/window_private.h" -#include "apps/system_apps/settings/settings_notifications_private.h" +#include "apps/system/settings/notifications_private.h" #include "popups/notifications/notification_window.h" #include "popups/notifications/notification_window_private.h" #include "resource/timeline_resource_ids.auto.h" -#include "services/normal/timeline/notification_layout.h" +#include "pbl/services/timeline/notification_layout.h" #include "util/trig.h" #include @@ -33,6 +20,7 @@ #include "stubs_action_menu.h" #include "stubs_alarm_layout.h" #include "stubs_alerts.h" +// stubs_alerts_preferences.h intentionally omitted; see local replacements below #include "stubs_analytics.h" #include "stubs_ancs_filtering.h" #include "stubs_app_install_manager.h" @@ -92,10 +80,45 @@ #include "stubs_timeline_layer.h" #include "stubs_timeline_peek.h" #include "stubs_vibes.h" +#include "stubs_vibe_client.h" +#include "stubs_vibe_score.h" +#include "stubs_vibe_score_info.h" #include "stubs_weather_layout.h" #include "stubs_window_manager.h" #include "stubs_window_stack.h" +#include "pbl/services/notifications/alerts_preferences_private.h" + +// Local replacements for stubs_alerts_preferences.h so the notification status +// bar style is settable per test (a strong override cannot share a TU with the +// header's WEAK definition). +static NotificationStatusBarStyle s_notification_status_bar_style = + NotificationStatusBarStyle_Default; + +NotificationStatusBarStyle alerts_preferences_get_notification_status_bar_style(void) { + return s_notification_status_bar_style; +} + +VibeScoreId alerts_preferences_get_vibe_score_for_client(VibeClient client) { + return VibeScoreId_Invalid; +} + +VibeIntensity alerts_preferences_get_vibe_intensity(void) { + return VibeIntensityLow; +} + +bool alerts_preferences_get_notification_alternative_design(void) { + return false; +} + +DndNotificationMode alerts_preferences_dnd_get_show_notifications(void) { + return DndNotificationModeShow; +} + +bool alerts_preferences_get_notification_vibe_delay(void) { + return false; +} + int16_t interpolate_int16(int32_t normalized, int16_t from, int16_t to) { return to; } @@ -296,12 +319,21 @@ bool graphics_release_frame_buffer(GContext *ctx, GBitmap *buffer) { return true; } +extern NotificationWindowData s_notification_window_data; +extern bool s_in_use; + void test_notification_window__initialize(void) { fake_app_state_init(); load_system_resources_fixture(); attribute_list_destroy_list(&s_test_data.statics.attr_list); s_test_data = (NotificationWindowTestData) {}; + s_notification_status_bar_style = NotificationStatusBarStyle_Default; + // Reset the notification window so each test gets a fresh prv_init_notification_window + // call with the correct status-bar style. Without this, s_in_use=true from a previous + // test (e.g. big_bold setting LargeBold mode) would prevent re-initialization and leave + // both the status-bar mode and swap-layer frame stale. + s_in_use = false; } void test_notification_window__cleanup(void) { @@ -459,3 +491,19 @@ void test_notification_window__body_icon(void) { prv_prepare_canvas_and_render_notification_windows(0 /* num_down_scrolls */); FAKE_GRAPHICS_CONTEXT_CHECK_DEST_BITMAP_FILE(); } + +void test_notification_window__big_bold(void) { + s_notification_status_bar_style = NotificationStatusBarStyle_LargeBold; + s_test_data = (NotificationWindowTestData) { + .icon_id = TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER, + .title = "Henry Levak", + .body = "Nu, Shara. Where are my designs, blat?", + .show_notification_timestamp = true, + .timestamp = "Just now", + .background_color = GColorPictonBlue, + }; + const unsigned int num_down_scrolls = + PBL_IF_RECT_ELSE((PreferredContentSizeDefault < PreferredContentSizeLarge) ? 1 : 0, 0); + prv_prepare_canvas_and_render_notification_windows(num_down_scrolls); + FAKE_GRAPHICS_CONTEXT_CHECK_DEST_BITMAP_FILE(); +} diff --git a/tests/fw/ui/test_option_menu_window.c b/tests/fw/ui/test_option_menu_window.c index db90c398b5..a101c7b068 100644 --- a/tests/fw/ui/test_option_menu_window.c +++ b/tests/fw/ui/test_option_menu_window.c @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/option_menu_window.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "clar.h" @@ -58,6 +45,7 @@ #include "stubs_syscalls.h" #include "stubs_task_watchdog.h" #include "stubs_unobstructed_area.h" +#include "stubs_vibes.h" #include "stubs_window_manager.h" #include "stubs_window_stack.h" diff --git a/tests/fw/ui/test_scroll_layer.c b/tests/fw/ui/test_scroll_layer.c index 954069248f..3897ace391 100644 --- a/tests/fw/ui/test_scroll_layer.c +++ b/tests/fw/ui/test_scroll_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/scroll_layer.h" diff --git a/tests/fw/ui/test_selection_windows.c b/tests/fw/ui/test_selection_windows.c index 1abafac203..b32cea1dc3 100644 --- a/tests/fw/ui/test_selection_windows.c +++ b/tests/fw/ui/test_selection_windows.c @@ -1,25 +1,12 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" #include "applib/ui/time_range_selection_window.h" #include "applib/ui/time_selection_window.h" #include "applib/ui/app_window_stack.h" -#include "apps/system_apps/settings/settings_notifications_private.h" +#include "apps/system/settings/notifications_private.h" #include "resource/resource.h" #include "shell/system_theme.h" #include "system/passert.h" diff --git a/tests/fw/ui/test_simple_dialog.c b/tests/fw/ui/test_simple_dialog.c index 5949d8a259..5cdb292ab4 100644 --- a/tests/fw/ui/test_simple_dialog.c +++ b/tests/fw/ui/test_simple_dialog.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/framebuffer.h" #include "applib/graphics/graphics.h" @@ -24,7 +11,7 @@ #include "applib/ui/window_private.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "shell/system_theme.h" #include "system/passert.h" #include "util/graphics.h" @@ -279,9 +266,22 @@ void test_simple_dialog__does_text_fit(void) { msg = "This error is too long for rect displays"; text_fits = simple_dialog_does_text_fit(msg, DISP_FRAME.size, icon_size, use_status_bar); +#if PBL_DISPLAY_WIDTH >= 200 + // The wider obelix display still fits this string with the larger 28-bold + // font, just like the round gabbro display does. + cl_assert(text_fits); +#else PBL_IF_RECT_ELSE(cl_assert(!text_fits), cl_assert(text_fits)); +#endif msg = "This error is too long to fit on any display shape :("; text_fits = simple_dialog_does_text_fit(msg, DISP_FRAME.size, icon_size, use_status_bar); +#if PBL_DISPLAY_WIDTH >= 200 + // With the larger 28-bold font, this longer string overflows the obelix + // (rect) layout; gabbro's round display still fits it because screen text + // flow wraps it within the 2-line cap. + PBL_IF_RECT_ELSE(cl_assert(!text_fits), cl_assert(text_fits)); +#else cl_assert(!text_fits); +#endif } diff --git a/tests/fw/ui/test_status_bar_layer.c b/tests/fw/ui/test_status_bar_layer.c index 0fb8300e6a..04d31383ad 100644 --- a/tests/fw/ui/test_status_bar_layer.c +++ b/tests/fw/ui/test_status_bar_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/status_bar_layer.h" #include "util/list.h" @@ -91,3 +78,30 @@ void test_status_bar_layer__modify_height(void) { cl_assert_status_bar_height(status_bar); } +//! Switching to the large-bold clock mode must grow the bar to the per-platform +//! large height, and that height must survive subsequent frame/bounds pokes +//! (the property-changed proc re-derives height from the mode). +void test_status_bar_layer__large_bold_height(void) { + StatusBarLayer status_bar; + status_bar_layer_init(&status_bar); + cl_assert_status_bar_height(status_bar); // default height + + status_bar_layer_set_mode(&status_bar, StatusBarLayerModeClockLargeBold); + cl_assert(status_bar.layer.frame.size.h == STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT); + cl_assert(status_bar.layer.bounds.size.h == STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT); + + GRect frame = status_bar.layer.frame; + frame.size.h = STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT - 5; + layer_set_frame(&status_bar.layer, &frame); + cl_assert(status_bar.layer.frame.size.h == STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT); + cl_assert(status_bar.layer.bounds.size.h == STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT); + + GRect bounds = status_bar.layer.bounds; + bounds.size.h = STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT + 5; + layer_set_bounds(&status_bar.layer, &bounds); + cl_assert(status_bar.layer.bounds.size.h == STATUS_BAR_LAYER_LARGE_BOLD_HEIGHT); + + status_bar_layer_set_mode(&status_bar, StatusBarLayerModeClock); + cl_assert_status_bar_height(status_bar); // back to default +} + diff --git a/tests/fw/ui/test_text_layer.c b/tests/fw/ui/test_text_layer.c index 2fd93dda20..6bd9a47cd1 100644 --- a/tests/fw/ui/test_text_layer.c +++ b/tests/fw/ui/test_text_layer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/ui/test_text_layer_flow.c b/tests/fw/ui/test_text_layer_flow.c index 134675662a..4510fd3d8a 100644 --- a/tests/fw/ui/test_text_layer_flow.c +++ b/tests/fw/ui/test_text_layer_flow.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" #include "pebble_asserts.h" diff --git a/tests/fw/ui/test_timeline_peek.c b/tests/fw/ui/test_timeline_peek.c index b2a2c8e3ce..ce22017c84 100644 --- a/tests/fw/ui/test_timeline_peek.c +++ b/tests/fw/ui/test_timeline_peek.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/graphics/bitblt.h" #include "applib/graphics/framebuffer.h" @@ -23,7 +10,7 @@ #include "popups/timeline/peek_private.h" #include "resource/resource.h" #include "resource/resource_ids.auto.h" -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "util/buffer.h" #include "util/graphics.h" #include "util/hash.h" @@ -50,6 +37,7 @@ void clock_get_until_time(char *buffer, int buf_size, time_t timestamp, int max_ ///////////////////// #include "stubs_activity.h" +#include "stubs_alerts_preferences.h" #include "stubs_analytics.h" #include "stubs_animation_timing.h" #include "stubs_app_install_manager.h" diff --git a/tests/fw/ui/test_window_stack.c b/tests/fw/ui/test_window_stack.c index de282bee20..b0d25bf747 100644 --- a/tests/fw/ui/test_window_stack.c +++ b/tests/fw/ui/test_window_stack.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/app_window_stack.h" #include "applib/ui/window.h" @@ -42,8 +29,10 @@ #include "stubs_graphics_context.h" #include "stubs_heap.h" #include "stubs_logging.h" +#include "stubs_new_timer.h" #include "stubs_passert.h" #include "stubs_persist.h" +#include "stubs_powermode_service.h" #include "stubs_plugin_service.h" #include "stubs_print.h" #include "stubs_process_manager.h" diff --git a/tests/fw/ui/wscript b/tests/fw/ui/wscript deleted file mode 100644 index 8ac55c9cb8..0000000000 --- a/tests/fw/ui/wscript +++ /dev/null @@ -1,462 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/gtypes.c " - "src/fw/applib/graphics/graphics_private_raw.c " - "tests/fakes/fake_gbitmap_png.c " - "src/fw/applib/ui/layer.c ", - test_sources_ant_glob="test_layer.c", - defines=['CAPABILITY_HAS_TOUCHSCREEN']) - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/graphics_private_raw.c " - "src/fw/applib/graphics/gtypes.c " - "tests/fakes/fake_gbitmap_png.c " - "src/fw/applib/ui/layer.c ", - test_sources_ant_glob="test_layer_rect.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/gtypes.c " - "tests/stubs/stubs_animation.c " - "tests/fakes/fake_gbitmap_png.c " - "src/fw/applib/ui/animation_interpolate.c " - "src/fw/applib/ui/layer.c " - "src/fw/applib/ui/shadows.c " - "src/fw/applib/ui/scroll_layer.c ", - test_sources_ant_glob="test_scroll_layer.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/graphics/8_bit/framebuffer.c" - " src/fw/applib/graphics/framebuffer.c" - " src/fw/applib/graphics/bitblt.c" - " src/fw/applib/graphics/8_bit/bitblt_private.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/graphics.c" - " src/fw/applib/graphics/graphics_private.c" - " src/fw/applib/graphics/graphics_private_raw.c" - " src/fw/applib/graphics/graphics_circle.c" - " src/fw/applib/graphics/graphics_line.c" - " src/fw/applib/graphics/text_layout.c" - " src/fw/applib/graphics/utf8.c" - " src/fw/applib/graphics/text_render.c" - " src/fw/applib/graphics/text_resources.c" - " src/fw/applib/fonts/codepoint.c" - " tests/fakes/fake_clock.c" - " tests/fakes/fake_fonts.c" - " tests/fakes/fake_gbitmap_png.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/ui/status_bar_layer.c", - test_sources_ant_glob="test_status_bar_layer.c", - defines=['FONT_KEY_GOTHIC_14="RESOURCE_ID_GOTHIC_14"']) - - clar(ctx, - sources_ant_glob = "src/fw/applib/ui/animation.c " - "src/fw/applib/ui/animation_interpolate.c " - "src/fw/applib/ui/animation_timing.c " - "src/fw/applib/ui/property_animation.c " - "src/fw/applib/legacy2/ui/animation_legacy2.c " - "src/fw/applib/legacy2/ui/property_animation_legacy2.c " - "src/fw/applib/graphics/gtypes.c " - "src/fw/applib/graphics/gcolor_definitions.c " - "src/fw/applib/ui/layer.c " - "tests/fakes/fake_events.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_gbitmap_png.c " - "src/fw/services/common/animation_service.c", - defines=['DUMA_DISABLED'], # PBL-18358 Invalid memory read access - test_sources_ant_glob = "test_animation.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/ui/animation_interpolate.c ", - test_sources_ant_glob = "test_animation_interpolate.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/ui/animation_timing.c", - test_sources_ant_glob = "test_animation_timing.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/gtypes.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/ui/kino/kino_reel.c" - " src/fw/applib/ui/kino/kino_reel_pdci.c" - " src/fw/applib/ui/kino/kino_reel_pdcs.c" - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" - " src/fw/applib/ui/kino/kino_reel_custom.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/graphics/gbitmap_sequence.c" - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/gdraw_command.c" - " src/fw/applib/graphics/gdraw_command_list.c" - " src/fw/applib/graphics/gdraw_command_image.c" - " src/fw/applib/graphics/gdraw_command_frame.c" - " src/fw/applib/graphics/gdraw_command_sequence.c" - " tests/fakes/fake_resource_syscalls.c" - " tests/fakes/fake_applib_resource.c" - " tests/fakes/fake_rtc.c", - defines = ctx.env.test_image_defines, - test_sources_ant_glob = "test_kino_reel.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/gtypes.c" - " src/fw/applib/graphics/gbitmap.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/ui/animation_interpolate.c" - " src/fw/applib/ui/kino/kino_player.c" - " src/fw/applib/ui/kino/kino_reel.c" - " src/fw/applib/ui/kino/kino_reel_pdci.c" - " src/fw/applib/ui/kino/kino_reel_pdcs.c" - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" - " src/fw/applib/ui/kino/kino_reel_custom.c" - " src/fw/applib/graphics/gbitmap_sequence.c" - " src/fw/applib/graphics/gbitmap_png.c" - " src/fw/applib/vendor/uPNG/upng.c" - " src/fw/applib/vendor/tinflate/tinflate.c" - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/gdraw_command.c" - " src/fw/applib/graphics/gdraw_command_list.c" - " src/fw/applib/graphics/gdraw_command_image.c" - " src/fw/applib/graphics/gdraw_command_frame.c" - " src/fw/applib/graphics/gdraw_command_sequence.c" - " tests/stubs/stubs_animation.c" - " tests/fakes/fake_resource_syscalls.c" - " tests/fakes/fake_rtc.c", - defines = ctx.env.test_image_defines, - test_sources_ant_glob = "test_kino_player.c") - - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gcolor_definitions.c" - " src/fw/applib/graphics/gtypes.c" - " src/fw/applib/ui/app_window_stack.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/ui/window.c" - " src/fw/applib/ui/window_manager.c" - " src/fw/applib/ui/window_stack.c" - " src/fw/kernel/ui/modals/modal_manager.c" - " tests/fakes/fake_animation.c" - " tests/fakes/fake_events.c" - " tests/stubs/stubs_click.c", - test_sources_ant_glob = "test_window_stack.c", - defines=['SCREEN_COLOR_DEPTH_BITS=8'], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/gtypes.c" - " src/fw/util/buffer.c" - " src/fw/applib/ui/layer.c" - " src/fw/applib/ui/content_indicator.c", - test_sources_ant_glob = "test_content_indicator.c") - - clar(ctx, - sources_ant_glob = " " \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/applib/ui/animation_interpolate.c " - " src/fw/applib/ui/animation_timing.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " tests/stubs/stubs_animation.c", - test_sources_ant_glob = "test_menu_layer.c") - - menu_layer_system_cell_rendering_sources = \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ - " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gbitmap_png.c" \ - " src/fw/applib/graphics/gbitmap_sequence.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gdraw_command.c" \ - " src/fw/applib/graphics/gdraw_command_frame.c" \ - " src/fw/applib/graphics/gdraw_command_image.c" \ - " src/fw/applib/graphics/gdraw_command_list.c" \ - " src/fw/applib/graphics/gdraw_command_sequence.c" \ - " src/fw/applib/graphics/gpath.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/graphics_bitmap.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/perimeter.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/graphics/text_render.c" \ - " src/fw/applib/graphics/text_resources.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/ui/kino/kino_reel.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ - " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ - " src/fw/applib/ui/kino/kino_reel_pdci.c" \ - " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/menu_layer_system_cells.c" \ - " src/fw/board/displays/display_spalding.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/applib/vendor/tinflate/tinflate.c" \ - " src/fw/applib/vendor/uPNG/upng.c" \ - " tests/fakes/fake_applib_resource.c" \ - " tests/fakes/fake_display.c" \ - " tests/fakes/fake_gbitmap_get_data_row.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c" \ - " tests/stubs/stubs_animation.c" \ - " tests/stubs/stubs_system_theme.c" - - clar(ctx, - sources_ant_glob = menu_layer_system_cell_rendering_sources + \ - " src/fw/shell/system_theme.c" \ - " tests/fakes/fake_fonts.c" \ - " tests/fixtures/resources/timeline_resource_table.auto.c", - test_sources_ant_glob = "test_menu_layer_system_cells.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk', 'robert']) - - clar(ctx, - sources_ant_glob = menu_layer_system_cell_rendering_sources + \ - " src/fw/applib/ui/action_menu_hierarchy.c" \ - " src/fw/applib/ui/action_menu_layer.c" \ - " src/fw/applib/ui/action_menu_window.c" \ - " src/fw/applib/ui/crumbs_layer.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " tests/fakes/fake_fonts.c" \ - " src/fw/shell/system_theme.c" \ - " src/fw/applib/ui/shadows.c", - test_sources_ant_glob = "test_action_menu_window.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['tintin', 'snowy', 'spalding', 'robert']) - - clar(ctx, - sources_ant_glob=( - menu_layer_system_cell_rendering_sources + " " + - " src/fw/applib/ui/action_button.c" - " src/fw/applib/ui/kino/kino_layer.c" - " src/fw/applib/ui/kino/kino_player.c" - " src/fw/applib/ui/scroll_layer.c" - " src/fw/applib/ui/shadows.c" - " src/fw/applib/ui/status_bar_layer.c" - " src/fw/applib/ui/window.c" - " src/fw/apps/system_apps/timeline/text_node.c" - " src/fw/popups/notifications/notification_window.c" - " src/fw/popups/notifications/notifications_presented_list.c" - " src/fw/services/normal/timeline/attribute.c" - " src/fw/services/normal/timeline/layout_layer.c" - " src/fw/services/normal/timeline/layout_node.c" - " src/fw/services/normal/timeline/notification_layout.c" - " src/fw/services/normal/timeline/swap_layer.c" - " src/fw/services/normal/timeline/timeline_layout.c" - " src/fw/services/normal/timeline/timeline_resources.c" - " src/fw/shell/system_theme.c" - " tests/fakes/fake_animation.c" - " tests/fakes/fake_fonts.c" - " tests/fakes/fake_graphics_context.c" - " tests/fixtures/resources/timeline_resource_table.auto.c" - " tests/stubs/stubs_clock.c" - ), - test_sources_ant_glob = "test_notification_window.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['silk', 'snowy', 'spalding', 'robert']) - - clar(ctx, - sources_ant_glob = menu_layer_system_cell_rendering_sources + \ - " src/fw/applib/ui/dialogs/simple_dialog.c" \ - " src/fw/applib/ui/dialogs/dialog.c" \ - " src/fw/applib/ui/dialogs/dialog_private.c" \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/kino/kino_layer.c" \ - " src/fw/applib/ui/kino/kino_player.c" \ - " src/fw/applib/ui/kino/kino_reel_custom.c" \ - " src/fw/applib/ui/kino/kino_reel/transform.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/ui/window.c" \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/shadows.c" - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " tests/fakes/fake_fonts.c", - test_sources_ant_glob = "test_simple_dialog.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['tintin', 'snowy', 'spalding']) - - clar(ctx, - sources_ant_glob = menu_layer_system_cell_rendering_sources + \ - " src/fw/applib/fonts/fonts.c", - test_sources_ant_glob = "test_emoji_fonts.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = menu_layer_system_cell_rendering_sources + \ - " src/fw/applib/ui/action_bar_layer.c" \ - " src/fw/applib/ui/animation_interpolate.c" \ - " src/fw/applib/ui/content_indicator.c" \ - " src/fw/applib/ui/dialogs/dialog.c" \ - " src/fw/applib/ui/dialogs/dialog_private.c" \ - " src/fw/applib/ui/dialogs/expandable_dialog.c" \ - " src/fw/applib/ui/inverter_layer.c" \ - " src/fw/applib/ui/kino/kino_layer.c" \ - " src/fw/applib/ui/kino/kino_player.c" \ - " src/fw/applib/ui/kino/kino_reel/transform.c" \ - " src/fw/applib/ui/kino/kino_reel_custom.c" \ - " src/fw/applib/ui/menu_layer.c" \ - " src/fw/applib/ui/scroll_layer.c" \ - " src/fw/applib/ui/shadows.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/ui/window.c" \ - " tests/fakes/fake_fonts.c", - test_sources_ant_glob = "test_expandable_dialog.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=["dummy_board"], - platforms=["spalding"]) - - clar(ctx, - sources_ant_glob = " " \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c", \ - test_sources_ant_glob = "test_text_layer.c") - - clar(ctx, - sources_ant_glob=( - menu_layer_system_cell_rendering_sources + - " src/fw/applib/ui/date_time_selection_window_private.c" - " src/fw/applib/ui/selection_layer.c" - " src/fw/applib/ui/text_layer.c" - " src/fw/applib/ui/time_range_selection_window.c" - " src/fw/applib/ui/time_selection_window.c" - " src/fw/applib/ui/window.c" - " src/fw/shell/system_theme.c" - " src/fw/util/date.c" - " tests/fakes/fake_clock.c" - " tests/fakes/fake_fonts.c" - ), - test_sources_ant_glob="test_selection_windows.c", - defines=ctx.env.test_image_defines, - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['tintin', 'snowy', 'spalding', 'robert']) - - clar(ctx, - sources_ant_glob = " " \ - " src/fw/applib/ui/layer.c" \ - " src/fw/applib/ui/text_layer.c" \ - " src/fw/applib/ui/text_layer_flow.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/gtypes.c", \ - test_sources_ant_glob = "test_text_layer_flow.c") - - clar(ctx, - sources_ant_glob = " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/utf8.c" \ - " src/fw/services/normal/timeline/notification_layout.c", - test_sources_ant_glob = "test_jumboji.c") - - clar(ctx, - sources_ant_glob=( - menu_layer_system_cell_rendering_sources + " " + - "src/fw/applib/ui/animation_interpolate.c " - "src/fw/applib/ui/kino/kino_layer.c " - "src/fw/applib/ui/kino/kino_player.c " - "src/fw/applib/ui/text_layer.c " - "src/fw/applib/ui/text_layer_flow.c " - "src/fw/applib/ui/window.c " - "src/fw/apps/system_apps/timeline/text_node.c " - "src/fw/popups/timeline/peek.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/generic_layout.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/services/normal/timeline/layout_layer.c " - "src/fw/services/normal/timeline/layout_node.c " - "src/fw/services/normal/timeline/timeline_layout.c " - "src/fw/services/normal/timeline/timeline_resources.c " - "src/fw/shell/system_theme.c " - "src/fw/util/stringlist.c " - "src/fw/util/time/time.c " - "tests/fakes/fake_clock.c " - "tests/fakes/fake_fonts.c " - "tests/fixtures/resources/timeline_resource_table.auto.c " - "tests/stubs/stubs_app_manager.c " - "tests/stubs/stubs_clock.c " - "tests/stubs/stubs_timeline_layout.c " - "tests/stubs/stubs_timeline_peek.c " - ), - test_sources_ant_glob="test_timeline_peek.c", - defines=ctx.env.test_image_defines + ["CAPABILITY_HAS_TIMELINE_PEEK=1", - "USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk', 'robert']) - - clar(ctx, - sources_ant_glob=( - menu_layer_system_cell_rendering_sources + " " + - "src/fw/applib/ui/animation_interpolate.c " - "src/fw/applib/ui/content_indicator.c " - "src/fw/applib/ui/inverter_layer.c " - "src/fw/applib/ui/menu_layer.c " - "src/fw/applib/ui/option_menu_window.c " - "src/fw/applib/ui/scroll_layer.c " - "src/fw/applib/ui/shadows.c " - "src/fw/applib/ui/status_bar_layer.c " - "src/fw/applib/ui/window.c " - "src/fw/shell/system_theme.c " - "tests/fakes/fake_clock.c " - "tests/fakes/fake_fonts.c " - "tests/fakes/fake_graphics_context.c " - ), - test_sources_ant_glob="test_option_menu_window.c", - defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, - override_includes=['dummy_board'], - platforms=['snowy', 'spalding', 'silk', 'robert']) - -# vim:filetype=vim diff --git a/tests/fw/ui/wscript_build b/tests/fw/ui/wscript_build new file mode 100644 index 0000000000..21962376b6 --- /dev/null +++ b/tests/fw/ui/wscript_build @@ -0,0 +1,464 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/gtypes.c " + "src/fw/applib/graphics/graphics_private_raw.c " + "tests/fakes/fake_gbitmap_png.c " + "src/fw/applib/ui/layer.c ", + test_sources_ant_glob="test_layer.c", + defines=['CONFIG_TOUCH']) + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/graphics_private_raw.c " + "src/fw/applib/graphics/gtypes.c " + "tests/fakes/fake_gbitmap_png.c " + "src/fw/applib/ui/layer.c ", + test_sources_ant_glob="test_layer_rect.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/gtypes.c " + "tests/stubs/stubs_animation.c " + "tests/fakes/fake_gbitmap_png.c " + "src/fw/applib/ui/animation_interpolate.c " + "src/fw/applib/ui/layer.c " + "src/fw/applib/ui/shadows.c " + "src/fw/applib/ui/scroll_layer.c ", + test_sources_ant_glob="test_scroll_layer.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/graphics/8_bit/framebuffer.c" + " src/fw/applib/graphics/framebuffer.c" + " src/fw/applib/graphics/bitblt.c" + " src/fw/applib/graphics/8_bit/bitblt_private.c" + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/graphics.c" + " src/fw/applib/graphics/graphics_private.c" + " src/fw/applib/graphics/graphics_private_raw.c" + " src/fw/applib/graphics/graphics_circle.c" + " src/fw/applib/graphics/graphics_line.c" + " src/fw/applib/graphics/text_layout.c" + " src/fw/applib/graphics/rtl_support.c" + " src/fw/applib/graphics/arabic_shaping.c" + " src/fw/applib/graphics/utf8.c" + " src/fw/applib/graphics/text_render.c" + " src/fw/applib/graphics/text_resources.c" + " src/fw/applib/fonts/codepoint.c" + " tests/fakes/fake_clock.c" + " tests/fakes/fake_fonts.c" + " tests/fakes/fake_gbitmap_png.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/ui/status_bar_layer.c", + test_sources_ant_glob="test_status_bar_layer.c", + defines=['FONT_KEY_GOTHIC_14="RESOURCE_ID_GOTHIC_14"']) + +clar(ctx, + sources_ant_glob = "src/fw/applib/ui/animation.c " + "src/fw/applib/ui/animation_interpolate.c " + "src/fw/applib/ui/animation_timing.c " + "src/fw/applib/ui/property_animation.c " + "src/fw/applib/legacy2/ui/animation_legacy2.c " + "src/fw/applib/legacy2/ui/property_animation_legacy2.c " + "src/fw/applib/graphics/gtypes.c " + "src/fw/applib/graphics/gcolor_definitions.c " + "src/fw/applib/ui/layer.c " + "tests/fakes/fake_events.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_gbitmap_png.c " + "src/fw/services/animation_service/service.c", + defines=['DUMA_DISABLED'], # PBL-18358 Invalid memory read access + test_sources_ant_glob = "test_animation.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/ui/animation_interpolate.c ", + test_sources_ant_glob = "test_animation_interpolate.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/ui/animation_timing.c", + test_sources_ant_glob = "test_animation_timing.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/gtypes.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/ui/kino/kino_reel.c" + " src/fw/applib/ui/kino/kino_reel_pdci.c" + " src/fw/applib/ui/kino/kino_reel_pdcs.c" + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" + " src/fw/applib/ui/kino/kino_reel_custom.c" + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/graphics/gbitmap_sequence.c" + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/gdraw_command.c" + " src/fw/applib/graphics/gdraw_command_list.c" + " src/fw/applib/graphics/gdraw_command_image.c" + " src/fw/applib/graphics/gdraw_command_frame.c" + " src/fw/applib/graphics/gdraw_command_sequence.c" + " tests/fakes/fake_resource_syscalls.c" + " tests/fakes/fake_applib_resource.c" + " tests/fakes/fake_rtc.c", + defines = ctx.env.test_image_defines, + test_sources_ant_glob = "test_kino_reel.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/gtypes.c" + " src/fw/applib/graphics/gbitmap.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/ui/animation_interpolate.c" + " src/fw/applib/ui/kino/kino_player.c" + " src/fw/applib/ui/kino/kino_reel.c" + " src/fw/applib/ui/kino/kino_reel_pdci.c" + " src/fw/applib/ui/kino/kino_reel_pdcs.c" + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" + " src/fw/applib/ui/kino/kino_reel_custom.c" + " src/fw/applib/graphics/gbitmap_sequence.c" + " src/fw/applib/graphics/gbitmap_png.c" + " src/fw/applib/vendor/uPNG/upng.c" + " src/fw/applib/vendor/tinflate/tinflate.c" + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/gdraw_command.c" + " src/fw/applib/graphics/gdraw_command_list.c" + " src/fw/applib/graphics/gdraw_command_image.c" + " src/fw/applib/graphics/gdraw_command_frame.c" + " src/fw/applib/graphics/gdraw_command_sequence.c" + " tests/stubs/stubs_animation.c" + " tests/fakes/fake_resource_syscalls.c" + " tests/fakes/fake_rtc.c", + defines = ctx.env.test_image_defines, + test_sources_ant_glob = "test_kino_player.c") + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/gcolor_definitions.c" + " src/fw/applib/graphics/gtypes.c" + " src/fw/applib/ui/app_window_stack.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/ui/window.c" + " src/fw/applib/ui/window_manager.c" + " src/fw/applib/ui/window_stack.c" + " src/fw/kernel/ui/modals/modal_manager.c" + " tests/fakes/fake_animation.c" + " tests/fakes/fake_events.c" + " tests/stubs/stubs_click.c", + test_sources_ant_glob = "test_window_stack.c", + defines=['CONFIG_SCREEN_COLOR_DEPTH_BITS=8'], + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/gtypes.c" + " src/fw/util/buffer.c" + " src/fw/applib/ui/layer.c" + " src/fw/applib/ui/content_indicator.c", + test_sources_ant_glob = "test_content_indicator.c") + +clar(ctx, + sources_ant_glob = " " \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/applib/ui/animation_interpolate.c " + " src/fw/applib/ui/animation_timing.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " tests/stubs/stubs_animation.c", + test_sources_ant_glob = "test_menu_layer.c") + +menu_layer_system_cell_rendering_sources = \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \ + " src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gbitmap_png.c" \ + " src/fw/applib/graphics/gbitmap_sequence.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gdraw_command.c" \ + " src/fw/applib/graphics/gdraw_command_frame.c" \ + " src/fw/applib/graphics/gdraw_command_image.c" \ + " src/fw/applib/graphics/gdraw_command_list.c" \ + " src/fw/applib/graphics/gdraw_command_sequence.c" \ + " src/fw/applib/graphics/gpath.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/graphics_bitmap.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/perimeter.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/graphics/text_render.c" \ + " src/fw/applib/graphics/text_resources.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/ui/kino/kino_reel.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap.c" \ + " src/fw/applib/ui/kino/kino_reel_gbitmap_sequence.c" \ + " src/fw/applib/ui/kino/kino_reel_pdci.c" \ + " src/fw/applib/ui/kino/kino_reel_pdcs.c" \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/menu_layer_system_cells.c" \ + " src/fw/board/displays/display_getafix.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/app_file.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/system/hexdump.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/applib/vendor/tinflate/tinflate.c" \ + " src/fw/applib/vendor/uPNG/upng.c" \ + " tests/fakes/fake_applib_resource.c" \ + " tests/fakes/fake_display.c" \ + " tests/fakes/fake_gbitmap_get_data_row.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c" \ + " tests/stubs/stubs_animation.c" \ + " tests/stubs/stubs_system_theme.c" + +clar(ctx, + sources_ant_glob = menu_layer_system_cell_rendering_sources + \ + " src/fw/shell/system_theme.c" \ + " tests/fakes/fake_fonts.c" \ + " tests/fixtures/resources/timeline_resource_table.auto.c", + test_sources_ant_glob = "test_menu_layer_system_cells.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = menu_layer_system_cell_rendering_sources + \ + " src/fw/applib/ui/action_menu_hierarchy.c" \ + " src/fw/applib/ui/action_menu_layer.c" \ + " src/fw/applib/ui/action_menu_window.c" \ + " src/fw/applib/ui/crumbs_layer.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " tests/fakes/fake_fonts.c" \ + " src/fw/shell/system_theme.c" \ + " src/fw/applib/ui/shadows.c", + test_sources_ant_glob = "test_action_menu_window.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob=( + menu_layer_system_cell_rendering_sources + " " + + " src/fw/applib/ui/action_button.c" + " src/fw/applib/ui/kino/kino_layer.c" + " src/fw/applib/ui/kino/kino_player.c" + " src/fw/applib/ui/scroll_layer.c" + " src/fw/applib/ui/shadows.c" + " src/fw/applib/ui/status_bar_layer.c" + " src/fw/applib/ui/window.c" + " src/fw/apps/system/timeline/text_node.c" + " src/fw/popups/notifications/notification_window.c" + " src/fw/popups/notifications/notifications_presented_list.c" + " src/fw/services/timeline/attribute.c" + " src/fw/services/timeline/layout_layer.c" + " src/fw/services/timeline/layout_node.c" + " src/fw/services/timeline/notification_layout.c" + " src/fw/services/timeline/swap_layer.c" + " src/fw/services/timeline/timeline_layout.c" + " src/fw/services/timeline/timeline_resources.c" + " src/fw/shell/system_theme.c" + " tests/fakes/fake_animation.c" + " tests/fakes/fake_fonts.c" + " tests/fakes/fake_graphics_context.c" + " tests/fixtures/resources/timeline_resource_table.auto.c" + " tests/stubs/stubs_clock.c" + ), + test_sources_ant_glob = "test_notification_window.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = menu_layer_system_cell_rendering_sources + \ + " src/fw/applib/ui/dialogs/simple_dialog.c" \ + " src/fw/applib/ui/dialogs/dialog.c" \ + " src/fw/applib/ui/dialogs/dialog_private.c" \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/kino/kino_layer.c" \ + " src/fw/applib/ui/kino/kino_player.c" \ + " src/fw/applib/ui/kino/kino_reel_custom.c" \ + " src/fw/applib/ui/kino/kino_reel/transform.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/ui/window.c" \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/shadows.c" + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " tests/fakes/fake_fonts.c", + test_sources_ant_glob = "test_simple_dialog.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = menu_layer_system_cell_rendering_sources + \ + " src/fw/applib/fonts/fonts.c", + test_sources_ant_glob = "test_emoji_fonts.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = menu_layer_system_cell_rendering_sources + \ + " src/fw/applib/ui/action_bar_layer.c" \ + " src/fw/applib/ui/animation_interpolate.c" \ + " src/fw/applib/ui/content_indicator.c" \ + " src/fw/applib/ui/dialogs/dialog.c" \ + " src/fw/applib/ui/dialogs/dialog_private.c" \ + " src/fw/applib/ui/dialogs/expandable_dialog.c" \ + " src/fw/applib/ui/inverter_layer.c" \ + " src/fw/applib/ui/kino/kino_layer.c" \ + " src/fw/applib/ui/kino/kino_player.c" \ + " src/fw/applib/ui/kino/kino_reel/transform.c" \ + " src/fw/applib/ui/kino/kino_reel_custom.c" \ + " src/fw/applib/ui/menu_layer.c" \ + " src/fw/applib/ui/scroll_layer.c" \ + " src/fw/applib/ui/shadows.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/ui/window.c" \ + " tests/fakes/fake_fonts.c", + test_sources_ant_glob = "test_expandable_dialog.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=["dummy_board"], + platforms=["gabbro"]) + +clar(ctx, + sources_ant_glob = " " \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c", \ + test_sources_ant_glob = "test_text_layer.c") + +clar(ctx, + sources_ant_glob=( + menu_layer_system_cell_rendering_sources + + " src/fw/applib/ui/date_time_selection_window_private.c" + " src/fw/applib/ui/selection_layer.c" + " src/fw/applib/ui/text_layer.c" + " src/fw/applib/ui/time_range_selection_window.c" + " src/fw/applib/ui/time_selection_window.c" + " src/fw/applib/ui/window.c" + " src/fw/shell/system_theme.c" + " src/fw/util/date.c" + " tests/fakes/fake_clock.c" + " tests/fakes/fake_fonts.c" + ), + test_sources_ant_glob="test_selection_windows.c", + defines=ctx.env.test_image_defines, + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = " " \ + " src/fw/applib/ui/layer.c" \ + " src/fw/applib/ui/text_layer.c" \ + " src/fw/applib/ui/text_layer_flow.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/gtypes.c", \ + test_sources_ant_glob = "test_text_layer_flow.c") + +clar(ctx, + sources_ant_glob = " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/utf8.c" \ + " src/fw/services/timeline/notification_layout.c", + test_sources_ant_glob = "test_jumboji.c") + +clar(ctx, + sources_ant_glob=( + menu_layer_system_cell_rendering_sources + " " + + "src/fw/applib/ui/animation_interpolate.c " + "src/fw/applib/ui/kino/kino_layer.c " + "src/fw/applib/ui/kino/kino_player.c " + "src/fw/applib/ui/text_layer.c " + "src/fw/applib/ui/text_layer_flow.c " + "src/fw/applib/ui/window.c " + "src/fw/apps/system/timeline/text_node.c " + "src/fw/popups/timeline/peek.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/generic_layout.c " + "src/fw/services/timeline/item.c " + "src/fw/services/timeline/layout_layer.c " + "src/fw/services/timeline/layout_node.c " + "src/fw/services/timeline/timeline_layout.c " + "src/fw/services/timeline/timeline_resources.c " + "src/fw/shell/system_theme.c " + "src/fw/util/stringlist.c " + "src/fw/util/time/time.c " + "tests/fakes/fake_clock.c " + "tests/fakes/fake_fonts.c " + "tests/fixtures/resources/timeline_resource_table.auto.c " + "tests/stubs/stubs_app_manager.c " + "tests/stubs/stubs_clock.c " + "tests/stubs/stubs_timeline_layout.c " + "tests/stubs/stubs_timeline_peek.c " + ), + test_sources_ant_glob="test_timeline_peek.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob=( + menu_layer_system_cell_rendering_sources + " " + + "src/fw/applib/ui/animation_interpolate.c " + "src/fw/applib/ui/content_indicator.c " + "src/fw/applib/ui/inverter_layer.c " + "src/fw/applib/ui/menu_layer.c " + "src/fw/applib/ui/option_menu_window.c " + "src/fw/applib/ui/scroll_layer.c " + "src/fw/applib/ui/shadows.c " + "src/fw/applib/ui/status_bar_layer.c " + "src/fw/applib/ui/window.c " + "src/fw/shell/system_theme.c " + "tests/fakes/fake_clock.c " + "tests/fakes/fake_fonts.c " + "tests/fakes/fake_graphics_context.c " + ), + test_sources_ant_glob="test_option_menu_window.c", + defines=ctx.env.test_image_defines + ["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis + ctx.env.test_pfos, + override_includes=['dummy_board'], + platforms=['obelix', 'gabbro']) + +# vim:filetype=vim diff --git a/tests/fw/utf8_test_data.h b/tests/fw/utf8_test_data.h index b481dd882f..8640f074c5 100644 --- a/tests/fw/utf8_test_data.h +++ b/tests/fw/utf8_test_data.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/tests/fw/util/test_base64.c b/tests/fw/util/test_base64.c index be341ae3a5..7a05b97419 100644 --- a/tests/fw/util/test_base64.c +++ b/tests/fw/util/test_base64.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/base64.h" diff --git a/tests/fw/util/test_buffer.c b/tests/fw/util/test_buffer.c index 8057e91657..5cf837b107 100644 --- a/tests/fw/util/test_buffer.c +++ b/tests/fw/util/test_buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/buffer.h" diff --git a/tests/fw/util/test_dict.c b/tests/fw/util/test_dict.c index e097a83207..917f08cdb6 100644 --- a/tests/fw/util/test_dict.c +++ b/tests/fw/util/test_dict.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/dict.h" #include "util/math.h" diff --git a/tests/fw/util/test_generic_attribute.c b/tests/fw/util/test_generic_attribute.c index f50c8c3371..38121b4819 100644 --- a/tests/fw/util/test_generic_attribute.c +++ b/tests/fw/util/test_generic_attribute.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" -#include "services/normal/voice_endpoint_private.h" +#include "pbl/services/voice_endpoint_private.h" #include "fake_pebble_tasks.h" diff --git a/tests/fw/util/test_graphics.c b/tests/fw/util/test_graphics.c index 49b7935489..9f9e3fcb0e 100644 --- a/tests/fw/util/test_graphics.c +++ b/tests/fw/util/test_graphics.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/tests/fw/util/test_hdlc.c b/tests/fw/util/test_hdlc.c index 698029155d..e72566e672 100644 --- a/tests/fw/util/test_hdlc.c +++ b/tests/fw/util/test_hdlc.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/util/test_ihex.c b/tests/fw/util/test_ihex.c index 755f17a8e2..ff5624e275 100644 --- a/tests/fw/util/test_ihex.c +++ b/tests/fw/util/test_ihex.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/util/test_legacy_checksum.c b/tests/fw/util/test_legacy_checksum.c index 0dd44f8792..8573217269 100644 --- a/tests/fw/util/test_legacy_checksum.c +++ b/tests/fw/util/test_legacy_checksum.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/util/test_lru_cache.c b/tests/fw/util/test_lru_cache.c index 077cde3452..1d86b43e5e 100644 --- a/tests/fw/util/test_lru_cache.c +++ b/tests/fw/util/test_lru_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/lru_cache.h" diff --git a/tests/fw/util/test_mbuf.c b/tests/fw/util/test_mbuf.c index 787221a64d..cd87bfec56 100644 --- a/tests/fw/util/test_mbuf.c +++ b/tests/fw/util/test_mbuf.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/util/test_mktime.c b/tests/fw/util/test_mktime.c index 0530ea8d0c..865ec148ff 100644 --- a/tests/fw/util/test_mktime.c +++ b/tests/fw/util/test_mktime.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fw/util/test_pstring.c b/tests/fw/util/test_pstring.c index fed5aafc4f..ebd82baa90 100644 --- a/tests/fw/util/test_pstring.c +++ b/tests/fw/util/test_pstring.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/util/test_rand.c b/tests/fw/util/test_rand.c index f0490290a0..558b991b4e 100644 --- a/tests/fw/util/test_rand.c +++ b/tests/fw/util/test_rand.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/fw/util/test_shared_circular_buffer.c b/tests/fw/util/test_shared_circular_buffer.c index 6c32b4762e..7573c79a99 100644 --- a/tests/fw/util/test_shared_circular_buffer.c +++ b/tests/fw/util/test_shared_circular_buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/shared_circular_buffer.h" diff --git a/tests/fw/util/test_sle.c b/tests/fw/util/test_sle.c index 79ff206167..f9bb3ef483 100644 --- a/tests/fw/util/test_sle.c +++ b/tests/fw/util/test_sle.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/fw/util/test_stats.c b/tests/fw/util/test_stats.c index 602ccab4d6..e1679421b8 100644 --- a/tests/fw/util/test_stats.c +++ b/tests/fw/util/test_stats.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/size.h" #include "util/stats.h" diff --git a/tests/fw/util/test_stringlist.c b/tests/fw/util/test_stringlist.c index 019041f2ec..3600711c45 100644 --- a/tests/fw/util/test_stringlist.c +++ b/tests/fw/util/test_stringlist.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/stringlist.h" diff --git a/tests/fw/util/test_time.c b/tests/fw/util/test_time.c index baa705737b..136e47dcda 100644 --- a/tests/fw/util/test_time.c +++ b/tests/fw/util/test_time.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/time/time.h" diff --git a/tests/fw/util/wscript b/tests/fw/util/wscript deleted file mode 100644 index 32987c1298..0000000000 --- a/tests/fw/util/wscript +++ /dev/null @@ -1,77 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/fw/util/base64.c", - test_sources_ant_glob = "test_base64.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/shared_circular_buffer.c", - test_sources_ant_glob = "test_shared_circular_buffer.c") - - clar(ctx, - sources_ant_glob = " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_time.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/dict.c", - test_sources_ant_glob = "test_dict.c") - - #clar(ctx, - # sources_ant_glob = "src/fw/util/mktime.c", - # test_sources_ant_glob = "test_mktime.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/buffer.c", - test_sources_ant_glob = "test_buffer.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/lru_cache.c", - test_sources_ant_glob = "test_lru_cache.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/mbuf.c src/fw/util/mbuf_iterator.c", - test_sources_ant_glob = "test_mbuf.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_rand.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/stats.c", - test_sources_ant_glob = "test_stats.c") - - clar(ctx, - sources_ant_glob='src/fw/util/legacy_checksum.c', - test_sources_ant_glob='test_legacy_checksum.c') - - clar(ctx, - sources_ant_glob = " src/fw/util/rand/rand.c" \ - " src/fw/util/generic_attribute.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_generic_attribute.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/hdlc.c", - test_sources_ant_glob = "test_hdlc.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/stringlist.c", - test_sources_ant_glob = "test_stringlist.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/pstring.c", - test_sources_ant_glob = "test_pstring.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/ihex.c", - test_sources_ant_glob = "test_ihex.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/sle.c", - test_sources_ant_glob = "test_sle.c") - -# vim:filetype=python diff --git a/tests/fw/util/wscript_build b/tests/fw/util/wscript_build new file mode 100644 index 0000000000..61c4c950fc --- /dev/null +++ b/tests/fw/util/wscript_build @@ -0,0 +1,76 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/fw/util/base64.c", + test_sources_ant_glob = "test_base64.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/shared_circular_buffer.c", + test_sources_ant_glob = "test_shared_circular_buffer.c") + +clar(ctx, + sources_ant_glob = " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_time.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/dict.c", + test_sources_ant_glob = "test_dict.c") + +#clar(ctx, +# sources_ant_glob = "src/fw/util/mktime.c", +# test_sources_ant_glob = "test_mktime.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/buffer.c", + test_sources_ant_glob = "test_buffer.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/lru_cache.c", + test_sources_ant_glob = "test_lru_cache.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/mbuf.c src/fw/util/mbuf_iterator.c", + test_sources_ant_glob = "test_mbuf.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob = "test_rand.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/stats.c", + test_sources_ant_glob = "test_stats.c") + +clar(ctx, + sources_ant_glob='src/fw/util/legacy_checksum.c', + test_sources_ant_glob='test_legacy_checksum.c') + +clar(ctx, + sources_ant_glob = " src/fw/util/rand/rand.c" \ + " src/fw/util/generic_attribute.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob = "test_generic_attribute.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/hdlc.c", + test_sources_ant_glob = "test_hdlc.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/stringlist.c", + test_sources_ant_glob = "test_stringlist.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/pstring.c", + test_sources_ant_glob = "test_pstring.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/ihex.c", + test_sources_ant_glob = "test_ihex.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/sle.c", + test_sources_ant_glob = "test_sle.c") + +# vim:filetype=python diff --git a/tests/fw/wscript b/tests/fw/wscript deleted file mode 100644 index e8e1e8d9b8..0000000000 --- a/tests/fw/wscript +++ /dev/null @@ -1,345 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = - " src/fw/applib/graphics/gtypes.c" - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_clar.c") - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ - " tests/fakes/fake_gbitmap_png.c", - test_sources_ant_glob = "test_utf8.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/common/i18n/i18n.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " tests/fakes/fake_bootbits.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c", - test_sources_ant_glob = "test_i18n.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/utf8.c", - test_sources_ant_glob = "test_utf8_iterator.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " tests/fakes/fake_gbitmap_png.c", - test_sources_ant_glob = "test_char_iterator.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " tests/fakes/fake_gbitmap_png.c", - test_sources_ant_glob = "test_word_iterator.c") - - clar(ctx, - sources_ant_glob = "src/fw/util/rand/rand.c " \ - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " \ - "src/fw/process_management/launcher_app_message.c " \ - "src/fw/util/dict.c " \ - "tests/fakes/fake_session.c ", - test_sources_ant_glob="test_launcher_app_message.c") - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " tests/fakes/fake_gbitmap_png.c", - test_sources_ant_glob = "test_line_layout.c") - - clar(ctx, - sources_ant_glob = "src/fw/services/normal/alarms/alarm.c" \ - " src/fw/services/normal/alarms/alarm_pin.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/services/common/cron.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/timeline_item_storage.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_alarm.c", - defines=["CAPABILITY_HAS_HEALTH_TRACKING=0"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/services/normal/alarms/alarm.c" \ - " src/fw/services/normal/alarms/alarm_pin.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/util/time/time.c" \ - " src/fw/util/time/mktime.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/services/common/cron.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/blob_db/timeline_item_storage.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " src/fw/services/normal/timeline/attribute.c" \ - " src/fw/services/normal/timeline/item.c" \ - " src/fw/services/normal/timeline/attributes_actions.c" \ - " src/fw/services/normal/timeline/attribute_group.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_clock.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_alarm_smart.c", - defines=["CAPABILITY_HAS_HEALTH_TRACKING=1"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/common/battery/battery_state.c " \ - " src/fw/services/common/battery/battery_curve.c " \ - " src/fw/services/common/battery/battery_monitor.c " \ - " tests/fakes/fake_battery.c " \ - " tests/fakes/fake_rtc.c ", - test_sources_ant_glob = "test_battery_monitor.c", - defines=['PLATFORM_SNOWY'], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/debug/legacy/debug_db.c", - test_sources_ant_glob = "test_debug_db.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/notifications/alerts_preferences.c" \ - " src/fw/services/normal/notifications/alerts.c" \ - " src/fw/services/normal/settings/settings_file.c" \ - " src/fw/services/normal/settings/settings_raw_iter.c" \ - " tests/fakes/fake_battery.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c", - test_sources_ant_glob = "test_alerts.c", - override_includes=['dummy_board']) - - clar(ctx, - test_sources_ant_glob = "test_freertos_utils.c") - - clar(ctx, - sources_ant_glob = - " src/fw/applib/app_heap_util.c", - test_sources_ant_glob = "test_heap.c") - - for platform in ['silk', 'snowy']: - clar(ctx, - sources_ant_glob = - " src/fw/debug/default/flash_logging.c" - " src/fw/flash_region/flash_region.c" - " tests/fakes/fake_spi_flash.c", - defines=['DUMA_DISABLED', 'PLATFORM_%s' % platform.upper()], # DUMA false-positive - test_sources_ant_glob = "test_flash_logging.c", - override_includes=['dummy_board'], - platforms=[platform]) - - clar(ctx, - sources_ant_glob = \ - " src/fw/services/normal/data_logging/dls_main.c" \ - " src/fw/services/normal/data_logging/dls_list.c" \ - " src/fw/services/normal/data_logging/dls_storage.c" \ - " src/fw/services/normal/data_logging/dls_endpoint.c" \ - " src/fw/services/normal/data_logging/dls_syscalls.c" \ - " src/fw/services/common/regular_timer.c" \ - " src/fw/util/shared_circular_buffer.c" \ - " tests/fakes/fake_spi_flash.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " tests/fakes/fake_session.c" \ - " tests/fakes/fake_rtc.c" \ - " src/fw/applib/data_logging.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c", - test_sources_ant_glob = "test_data_logging.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/kernel/memory_layout.c", - test_sources_ant_glob = "test_memory_layout.c") - - clar(ctx, - sources_ant_glob = \ - " src/fw/applib/app_exit_reason.c" \ - " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/process_management/process_manager.c" \ - " src/fw/process_management/pebble_process_info.c" \ - " src/fw/process_management/app_manager.c" \ - " src/fw/process_management/pebble_process_md.c" \ - " src/fw/kernel/memory_layout.c" \ - " src/fw/kernel/util/segment.c" \ - " tests/fakes/fake_rtc.c", - test_sources_ant_glob = "test_app_manager.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = \ - " src/fw/process_management/pebble_process_info.c" \ - " src/fw/process_management/process_manager.c", - test_sources_ant_glob = "test_process_manager.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = "src/fw/process_management/pebble_process_info.c", - test_sources_ant_glob = "test_pebble_app_info.c") - - clar(ctx, - sources_ant_glob = - " src/fw/process_management/pebble_process_info.c" - " src/fw/process_management/pebble_process_md.c", - test_sources_ant_glob = "test_pebble_process_md.c", - platforms=['snowy', 'spalding', 'silk', 'robert']) - - clar(ctx, - sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ - " src/fw/popups/phone_formatting.c", - test_sources_ant_glob = "test_phone_formatting.c") - - clar(ctx, - sources_ant_glob=( - "src/fw/flash_region/filesystem_regions.c " - "src/fw/flash_region/flash_region.c " - "src/fw/services/normal/blob_db/pin_db.c " - "src/fw/services/normal/blob_db/timeline_item_storage.c " - "src/fw/services/normal/filesystem/flash_translation.c " - "src/fw/services/normal/filesystem/pfs.c " - "src/fw/services/normal/settings/settings_file.c " - "src/fw/services/normal/settings/settings_raw_iter.c " - "src/fw/services/normal/timeline/attribute.c " - "src/fw/services/normal/timeline/attribute_group.c " - "src/fw/services/normal/timeline/attributes_actions.c " - "src/fw/services/normal/timeline/item.c " - "src/fw/services/normal/timeline/timeline.c " - "src/fw/util/crc8.c " - "src/fw/util/legacy_checksum.c " - "src/fw/util/rand/rand.c " - "src/fw/util/time/time.c " - "third_party/tinymt/TinyMT/tinymt/tinymt32.c " - "tests/fakes/fake_rtc.c " - "tests/fakes/fake_spi_flash.c " - "tests/stubs/stubs_blob_db.c " - ), - test_sources_ant_glob="test_timeline_api.c", - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = " src/fw/flash_region/flash_region.c" \ - " src/fw/flash_region/filesystem_regions.c" \ - " src/fw/system/hexdump.c" \ - " src/fw/resource/resource.c" \ - " src/fw/resource/resource_storage.c" \ - " src/fw/resource/resource_storage_builtin.c" \ - " src/fw/resource/resource_storage_file.c" \ - " src/fw/resource/resource_storage_flash.c" \ - " src/fw/services/normal/filesystem/flash_translation.c" \ - " src/fw/services/normal/filesystem/pfs.c" \ - " src/fw/services/normal/filesystem/app_file.c" \ - " tests/fakes/fake_bootbits.c" \ - " src/fw/util/crc8.c" \ - " src/fw/util/legacy_checksum.c" \ - " src/fw/drivers/flash/flash_crc.c" \ - " tests/fakes/fake_rtc.c" \ - " tests/fakes/fake_spi_flash.c" \ - " tests/fixtures/resources/builtin_resources.auto.c" \ - " tests/fixtures/resources/pfs_resource_table.c", - test_sources_ant_glob = "test_resource.c", - defines=["CAPABILITY_HAS_MAPPABLE_FLASH=1"], - override_includes=['dummy_board']) - - clar(ctx, - sources_ant_glob = " src/fw/util/rand/rand.c" \ - " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ - " src/fw/process_management/app_run_state.c", - test_sources_ant_glob = "test_app_run_state.c") - - - # The following tests rely on the framebuffer, so build for both 1 and 8 - # bit implementations - templated_sources_ant_glob = \ - "src/fw/applib/graphics/utf8.c" \ - " src/fw/applib/graphics/gbitmap.c" \ - " src/fw/applib/graphics/gtypes.c" \ - " src/fw/applib/graphics/gcolor_definitions.c" \ - " src/fw/applib/graphics/{depth_dir}/framebuffer.c" \ - " src/fw/applib/graphics/framebuffer.c" \ - " src/fw/applib/graphics/graphics.c" \ - " src/fw/applib/graphics/gtransform.c" \ - " src/fw/applib/graphics/bitblt.c" \ - " src/fw/applib/graphics/{depth_dir}/bitblt_private.c" \ - " src/fw/applib/graphics/text_layout.c" \ - " tests/fakes/fake_gbitmap_png.c" \ - " src/fw/applib/fonts/codepoint.c" \ - " src/fw/applib/graphics/graphics_private.c" \ - " src/fw/applib/graphics/graphics_private_raw.c" \ - " src/fw/applib/graphics/graphics_circle.c" \ - " src/fw/applib/graphics/graphics_line.c" - - for platform in ['tintin', 'snowy']: - bit_depth = 1 if platform == 'tintin' else 8 - depth_dir = '{}_bit'.format(bit_depth) - sources_ant_glob = templated_sources_ant_glob.format(depth_dir=depth_dir) - clar(ctx, - sources_ant_glob=sources_ant_glob, - test_sources_ant_glob="test_text_layout.c", - test_name="test_text_layout_%u_bit" % bit_depth, - defines=["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], - platforms=[platform]) - - clar(ctx, - sources_ant_glob="src/fw/apps/prf_apps/recovery_first_use_app/getting_started_button_combo.c ", - test_sources_ant_glob="test_getting_started_button_combo.c", - defines=["RECOVERY_FW"]) # Enable all combos - -# vim:filetype=python - diff --git a/tests/fw/wscript_build b/tests/fw/wscript_build new file mode 100644 index 0000000000..cbd6da216d --- /dev/null +++ b/tests/fw/wscript_build @@ -0,0 +1,330 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = + " src/fw/applib/graphics/gtypes.c" + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob = "test_clar.c") +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ + " tests/fakes/fake_gbitmap_png.c", + test_sources_ant_glob = "test_utf8.c") + +clar(ctx, + sources_ant_glob = "src/fw/services/i18n/i18n.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/resource/resource.c" \ + " src/fw/resource/resource_storage.c" \ + " src/fw/resource/resource_storage_builtin.c" \ + " src/fw/resource/resource_storage_file.c" \ + " src/fw/resource/resource_storage_flash.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/app_file.c" \ + " tests/fakes/fake_bootbits.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/drivers/flash/flash_crc.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c" \ + " tests/fixtures/resources/builtin_resources.auto.c" \ + " tests/fixtures/resources/pfs_resource_table.c", + test_sources_ant_glob = "test_i18n.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/utf8.c", + test_sources_ant_glob = "test_utf8_iterator.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/fonts/codepoint.c", + test_sources_ant_glob = "test_codepoint.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " tests/fakes/fake_gbitmap_png.c", + test_sources_ant_glob = "test_char_iterator.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " tests/fakes/fake_gbitmap_png.c", + test_sources_ant_glob = "test_word_iterator.c") + +clar(ctx, + sources_ant_glob = "src/fw/util/rand/rand.c " \ + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " \ + "src/fw/process_management/launcher_app_message.c " \ + "src/fw/util/dict.c " \ + "tests/fakes/fake_session.c ", + test_sources_ant_glob="test_launcher_app_message.c") + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " tests/fakes/fake_gbitmap_png.c", + test_sources_ant_glob = "test_line_layout.c") + +clar(ctx, + sources_ant_glob = "src/fw/services/alarms/alarm.c" \ + " src/fw/services/alarms/alarm_pin.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/services/cron/service.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/timeline_item_storage.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_alarm.c", + defines=["CAPABILITY_HAS_HEALTH_TRACKING=0"], + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/services/alarms/alarm.c" \ + " src/fw/services/alarms/alarm_pin.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/util/time/time.c" \ + " src/fw/util/time/mktime.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/services/cron/service.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/blob_db/timeline_item_storage.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " src/fw/services/timeline/attribute.c" \ + " src/fw/services/timeline/item.c" \ + " src/fw/services/timeline/attributes_actions.c" \ + " src/fw/services/timeline/attribute_group.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_clock.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_alarm_smart.c", + defines=["CAPABILITY_HAS_HEALTH_TRACKING=1"], + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/battery/voltage/battery_state.c " \ + " src/fw/services/battery/voltage/battery_curve.c " \ + " src/fw/services/battery/battery_monitor.c " \ + " tests/fakes/fake_battery.c " \ + " tests/fakes/fake_rtc.c ", + test_sources_ant_glob = "test_battery_monitor.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/debug/legacy/debug_db.c", + test_sources_ant_glob = "test_debug_db.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/notifications/alerts_preferences.c" \ + " src/fw/services/notifications/alerts.c" \ + " src/fw/services/settings/settings_file.c" \ + " src/fw/services/settings/settings_raw_iter.c" \ + " tests/fakes/fake_battery.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " tests/fakes/fake_rtc.c" \ + " tests/fakes/fake_spi_flash.c", + test_sources_ant_glob = "test_alerts.c", + override_includes=['dummy_board']) + +clar(ctx, + test_sources_ant_glob = "test_freertos_utils.c") + +clar(ctx, + sources_ant_glob = + " src/fw/applib/app_heap_util.c", + test_sources_ant_glob = "test_heap.c") + +for platform in ['asterix']: + clar(ctx, + sources_ant_glob = + " src/fw/debug/default/flash_logging.c" + " src/fw/flash_region/flash_region.c" + " tests/fakes/fake_spi_flash.c", + defines=['DUMA_DISABLED', 'PLATFORM_%s' % platform.upper()], # DUMA false-positive + test_sources_ant_glob = "test_flash_logging.c", + override_includes=['dummy_board'], + platforms=[platform]) + +clar(ctx, + sources_ant_glob = \ + " src/fw/services/data_logging/dls_main.c" \ + " src/fw/services/data_logging/dls_list.c" \ + " src/fw/services/data_logging/dls_storage.c" \ + " src/fw/services/data_logging/dls_endpoint.c" \ + " src/fw/services/data_logging/dls_syscalls.c" \ + " src/fw/services/regular_timer/service.c" \ + " src/fw/util/shared_circular_buffer.c" \ + " tests/fakes/fake_spi_flash.c" \ + " src/fw/util/crc8.c" \ + " src/fw/util/legacy_checksum.c" \ + " src/fw/flash_region/flash_region.c" \ + " src/fw/flash_region/filesystem_regions.c" \ + " src/fw/services/filesystem/pfs.c" \ + " src/fw/services/filesystem/flash_translation.c" \ + " tests/fakes/fake_session.c" \ + " tests/fakes/fake_rtc.c" \ + " src/fw/applib/data_logging.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c", + test_sources_ant_glob = "test_data_logging.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/kernel/memory_layout.c", + test_sources_ant_glob = "test_memory_layout.c") + +clar(ctx, + sources_ant_glob = \ + " src/fw/applib/app_exit_reason.c" \ + " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/process_management/process_manager.c" \ + " src/fw/process_management/pebble_process_info.c" \ + " src/fw/process_management/app_manager.c" \ + " src/fw/process_management/pebble_process_md.c" \ + " src/fw/kernel/memory_layout.c" \ + " src/fw/kernel/util/segment.c" \ + " tests/fakes/fake_rtc.c", + test_sources_ant_glob = "test_app_manager.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = \ + " src/fw/process_management/pebble_process_info.c" \ + " src/fw/process_management/process_manager.c", + test_sources_ant_glob = "test_process_manager.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = "src/fw/process_management/pebble_process_info.c", + test_sources_ant_glob = "test_pebble_app_info.c") + +clar(ctx, + sources_ant_glob = + " src/fw/process_management/pebble_process_info.c" + " src/fw/process_management/pebble_process_md.c", + test_sources_ant_glob = "test_pebble_process_md.c", + platforms=['obelix', 'gabbro']) + +clar(ctx, + sources_ant_glob = "src/fw/applib/graphics/utf8.c" \ + " src/fw/popups/phone_formatting.c", + test_sources_ant_glob = "test_phone_formatting.c") + +clar(ctx, + sources_ant_glob=( + "src/fw/flash_region/filesystem_regions.c " + "src/fw/flash_region/flash_region.c " + "src/fw/services/blob_db/pin_db.c " + "src/fw/services/blob_db/timeline_item_storage.c " + "src/fw/services/filesystem/flash_translation.c " + "src/fw/services/filesystem/pfs.c " + "src/fw/services/settings/settings_file.c " + "src/fw/services/settings/settings_raw_iter.c " + "src/fw/services/timeline/attribute.c " + "src/fw/services/timeline/attribute_group.c " + "src/fw/services/timeline/attributes_actions.c " + "src/fw/services/timeline/item.c " + "src/fw/services/timeline/timeline.c " + "src/fw/util/crc8.c " + "src/fw/util/legacy_checksum.c " + "src/fw/util/rand/rand.c " + "src/fw/util/time/time.c " + "third_party/tinymt/TinyMT/tinymt/tinymt32.c " + "tests/fakes/fake_rtc.c " + "tests/fakes/fake_spi_flash.c " + "tests/stubs/stubs_blob_db.c " + ), + test_sources_ant_glob="test_timeline_api.c", + override_includes=['dummy_board']) + +clar(ctx, + sources_ant_glob = " src/fw/util/rand/rand.c" \ + " third_party/tinymt/TinyMT/tinymt/tinymt32.c" \ + " src/fw/process_management/app_run_state.c", + test_sources_ant_glob = "test_app_run_state.c") + + +# The following tests rely on the framebuffer, so build for both 1 and 8 +# bit implementations +templated_sources_ant_glob = \ + "src/fw/applib/graphics/utf8.c" \ + " src/fw/applib/graphics/gbitmap.c" \ + " src/fw/applib/graphics/gtypes.c" \ + " src/fw/applib/graphics/gcolor_definitions.c" \ + " src/fw/applib/graphics/{depth_dir}/framebuffer.c" \ + " src/fw/applib/graphics/framebuffer.c" \ + " src/fw/applib/graphics/graphics.c" \ + " src/fw/applib/graphics/gtransform.c" \ + " src/fw/applib/graphics/bitblt.c" \ + " src/fw/applib/graphics/{depth_dir}/bitblt_private.c" \ + " src/fw/applib/graphics/text_layout.c" \ + " src/fw/applib/graphics/rtl_support.c" \ + " src/fw/applib/graphics/arabic_shaping.c" \ + " tests/fakes/fake_gbitmap_png.c" \ + " src/fw/applib/fonts/codepoint.c" \ + " src/fw/applib/graphics/graphics_private.c" \ + " src/fw/applib/graphics/graphics_private_raw.c" \ + " src/fw/applib/graphics/graphics_circle.c" \ + " src/fw/applib/graphics/graphics_line.c" + +for platform in ['obelix']: + bit_depth = 8 + depth_dir = '{}_bit'.format(bit_depth) + sources_ant_glob = templated_sources_ant_glob.format(depth_dir=depth_dir) + clar(ctx, + sources_ant_glob=sources_ant_glob, + test_sources_ant_glob="test_text_layout.c", + test_name="test_text_layout_%u_bit" % bit_depth, + defines=["USE_DISPLAY_PERIMETER_ON_FONT_LAYOUT=1"], + platforms=[platform]) + +clar(ctx, + sources_ant_glob="src/fw/apps/prf/recovery_first_use/getting_started_button_combo.c ", + test_sources_ant_glob="test_getting_started_button_combo.c", + defines=["CONFIG_RECOVERY_FW"]) # Enable all combos + +# vim:filetype=python diff --git a/tests/libc/math/gen_log_reference.py b/tests/libc/math/gen_log_reference.py new file mode 100644 index 0000000000..86113883ed --- /dev/null +++ b/tests/libc/math/gen_log_reference.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +"""Generate correctly-rounded reference values for the log() unit test. + +The test exercises log(v) for v = i * 0.001, i in [1, 10000). Using the host +libm as an oracle makes the expected values host-specific, because glibc and +Apple's libm disagree in the last ULP. Instead this script computes the +correctly-rounded double for each input ahead of time, using the stdlib +decimal module at high precision, and emits the bit patterns as C. The table +is therefore identical on every host and the test no longer calls host libm. + +For each i: + * v is the IEEE-754 double nearest i * 0.001, computed exactly as the C test + computes it (float multiply), so the reference matches the input the test + actually feeds to pblibc_log. + * log(v) is evaluated in decimal at 80 significant digits, then rounded to + the nearest double via float(Decimal), which is round-half-to-even, i.e. + correct rounding. + +Run from the repo root or this directory: + python3 tests/libc/math/gen_log_reference.py > tests/libc/math/log_reference.h +""" + +import struct +import sys +from decimal import Decimal, getcontext + +# 80 significant digits is far more than the 17 a double needs; the final +# round to double is what fixes the result, the surplus only guards the +# rounding decision near a tie. +getcontext().prec = 80 + +COUNT = 10000 # i in [1, COUNT) +STEP = 0.001 + + +def double_to_bits(value: float) -> int: + """Return the raw 64-bit IEEE-754 representation of a double.""" + return struct.unpack(" float: + """Correctly-rounded double for the natural log of v, via decimal.""" + # Decimal(v) is the exact value of the double v, so no input rounding error + # is introduced before the transcendental evaluation. + return float(Decimal(v).ln()) + + +def main() -> int: + rows = [] + for i in range(1, COUNT): + v = i * STEP + expected = correctly_rounded_log(v) + rows.append((double_to_bits(v), double_to_bits(expected))) + + out = sys.stdout + out.write("/* SPDX-FileCopyrightText: 2026 Core Devices LLC */\n") + out.write("/* SPDX-License-Identifier: Apache-2.0 */\n\n") + out.write("/* Generated by gen_log_reference.py. Do not edit by hand. */\n") + out.write( + "/* Correctly-rounded reference for log(i * 0.001), i in [1, 10000). */\n\n" + ) + out.write("#pragma once\n\n") + out.write("#include \n\n") + out.write("typedef struct {\n") + out.write(" uint64_t input_bits; /* IEEE-754 bits of v = i * 0.001 */\n") + out.write(" uint64_t expected_bits; /* IEEE-754 bits of log(v) */\n") + out.write("} LogReferenceEntry;\n\n") + out.write(f"#define LOG_REFERENCE_COUNT {len(rows)}\n\n") + out.write( + "static const LogReferenceEntry s_log_reference[LOG_REFERENCE_COUNT] = {\n" + ) + for input_bits, expected_bits in rows: + out.write(f" {{ 0x{input_bits:016x}ull, 0x{expected_bits:016x}ull }},\n") + out.write("};\n") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/libc/math/gen_pow_reference.py b/tests/libc/math/gen_pow_reference.py new file mode 100644 index 0000000000..5c3316f9ae --- /dev/null +++ b/tests/libc/math/gen_pow_reference.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +"""Generate correctly-rounded reference values for the pow() unit test. + +The test exercises pow(2, v) for v = i * 0.001, i in [0, 10000). Using the +host libm as an oracle makes the expected values host-specific, because glibc +and Apple's libm disagree in the last ULP. Instead this script computes the +correctly-rounded double for each input ahead of time, using the stdlib +decimal module at high precision, and emits the bit patterns as C. The table +is therefore identical on every host and the test no longer calls host libm. + +For each i: + * v is the IEEE-754 double nearest i * 0.001, computed exactly as the C test + computes it (float multiply), so the reference matches the input the test + actually feeds to pblibc_pow. + * pow(2, v) is evaluated as exp(v * ln 2) in decimal at 80 significant + digits, then rounded to the nearest double via float(Decimal), which is + round-half-to-even, i.e. correct rounding. + +Run from the repo root or this directory: + python3 tests/libc/math/gen_pow_reference.py > tests/libc/math/pow_reference.h +""" + +import struct +import sys +from decimal import Decimal, getcontext + +# 80 significant digits is far more than the 17 a double needs; the final +# round to double is what fixes the result, the surplus only guards the +# rounding decision near a tie. +getcontext().prec = 80 + +COUNT = 10000 +STEP = 0.001 +BASE = 2 + + +def double_to_bits(value: float) -> int: + """Return the raw 64-bit IEEE-754 representation of a double.""" + return struct.unpack(" float: + """Correctly-rounded double for 2 ** v, via exp(v * ln 2) in decimal.""" + ln2 = Decimal(2).ln() + # Decimal(v) is the exact value of the double v, so no input rounding error + # is introduced before the transcendental evaluation. + return float((Decimal(v) * ln2).exp()) + + +def main() -> int: + rows = [] + for i in range(COUNT): + v = i * STEP + expected = correctly_rounded_pow2(v) + rows.append((double_to_bits(v), double_to_bits(expected))) + + out = sys.stdout + out.write("/* SPDX-FileCopyrightText: 2026 Core Devices LLC */\n") + out.write("/* SPDX-License-Identifier: Apache-2.0 */\n\n") + out.write("/* Generated by gen_pow_reference.py. Do not edit by hand. */\n") + out.write( + "/* Correctly-rounded reference for pow(2, i * 0.001), i in [0, 10000). */\n\n" + ) + out.write("#pragma once\n\n") + out.write("#include \n\n") + out.write("typedef struct {\n") + out.write(" uint64_t input_bits; /* IEEE-754 bits of v = i * 0.001 */\n") + out.write(" uint64_t expected_bits; /* IEEE-754 bits of pow(2, v) */\n") + out.write("} PowReferenceEntry;\n\n") + out.write(f"#define POW_REFERENCE_COUNT {COUNT}\n\n") + out.write( + "static const PowReferenceEntry s_pow_reference[POW_REFERENCE_COUNT] = {\n" + ) + for input_bits, expected_bits in rows: + out.write(f" {{ 0x{input_bits:016x}ull, 0x{expected_bits:016x}ull }},\n") + out.write("};\n") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/libc/math/log_reference.h b/tests/libc/math/log_reference.h new file mode 100644 index 0000000000..3d4167ef0f --- /dev/null +++ b/tests/libc/math/log_reference.h @@ -0,0 +1,10018 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Generated by gen_log_reference.py. Do not edit by hand. */ +/* Correctly-rounded reference for log(i * 0.001), i in [1, 10000). */ + +#pragma once + +#include + +typedef struct { + uint64_t input_bits; /* IEEE-754 bits of v = i * 0.001 */ + uint64_t expected_bits; /* IEEE-754 bits of log(v) */ +} LogReferenceEntry; + +#define LOG_REFERENCE_COUNT 9999 + +static const LogReferenceEntry s_log_reference[LOG_REFERENCE_COUNT] = { + { 0x3f50624dd2f1a9fcull, 0xc01ba18a998fffa0ull }, + { 0x3f60624dd2f1a9fcull, 0xc018dbc239b0b862ull }, + { 0x3f689374bc6a7efaull, 0xc0173c8ffae4bedeull }, + { 0x3f70624dd2f1a9fcull, 0xc01615f9d9d17124ull }, + { 0x3f747ae147ae147bull, 0xc015317a1b949c53ull }, + { 0x3f789374bc6a7efaull, 0xc01476c79b0577a0ull }, + { 0x3f7cac083126e979ull, 0xc013d8edee03690aull }, + { 0x3f80624dd2f1a9fcull, 0xc013503179f229e7ull }, + { 0x3f826e978d4fdf3cull, 0xc012d7955c397e1bull }, + { 0x3f847ae147ae147bull, 0xc0126bb1bbb55515ull }, + { 0x3f86872b020c49baull, 0xc0120a18bdd063faull }, + { 0x3f889374bc6a7efaull, 0xc011b0ff3b263062ull }, + { 0x3f8a9fbe76c8b43aull, 0xc0115f0883f73978ull }, + { 0x3f8cac083126e979ull, 0xc01113258e2421cdull }, + { 0x3f8eb851eb851eb8ull, 0xc010cc7f7ce95b91ull }, + { 0x3f90624dd2f1a9fcull, 0xc0108a691a12e2a9ull }, + { 0x3f916872b020c49cull, 0xc0104c54b892bfe3ull }, + { 0x3f926e978d4fdf3cull, 0xc01011ccfc5a36ddull }, + { 0x3f9374bc6a7ef9dbull, 0xc00fb4df2d1b4c14ull }, + { 0x3f947ae147ae147bull, 0xc00f4bd2b7ac1bafull }, + { 0x3f95810624dd2f1bull, 0xc00ee7e69eb0508full }, + { 0x3f96872b020c49baull, 0xc00e88a0bbe23979ull }, + { 0x3f978d4fdf3b645aull, 0xc00e2d97354e4b51ull }, + { 0x3f989374bc6a7efaull, 0xc00dd66db68dd248ull }, + { 0x3f9999999999999aull, 0xc00d82d33b32720dull }, + { 0x3f9a9fbe76c8b43aull, 0xc00d3280482fe474ull }, + { 0x3f9ba5e353f7ced9ull, 0xc00ce5357b1c7ab1ull }, + { 0x3f9cac083126e979ull, 0xc00c9aba5c89b51dull }, + { 0x3f9db22d0e560419ull, 0xc00c52dc67520122ull }, + { 0x3f9eb851eb851eb8ull, 0xc00c0d6e3a1428a6ull }, + { 0x3f9fbe76c8b43958ull, 0xc00bca46ea8ed3e7ull }, + { 0x3fa0624dd2f1a9fcull, 0xc00b8941746736d5ull }, + { 0x3fa0e5604189374cull, 0xc00b4a3c3e4a466full }, + { 0x3fa16872b020c49cull, 0xc00b0d18b166f14bull }, + { 0x3fa1eb851eb851ecull, 0xc00ad1bae0100b7bull }, + { 0x3fa26e978d4fdf3cull, 0xc00a980938f5df3eull }, + { 0x3fa2f1a9fbe76c8bull, 0xc00a5fec44e53783ull }, + { 0x3fa374bc6a7ef9dbull, 0xc00a294e6d5cbd98ull }, + { 0x3fa3f7ced916872bull, 0xc009f41bca97f16aull }, + { 0x3fa47ae147ae147bull, 0xc009c041f7ed8d33ull }, + { 0x3fa4fdf3b645a1cbull, 0xc0098dafed9152f3ull }, + { 0x3fa5810624dd2f1bull, 0xc0095c55def1c214ull }, + { 0x3fa604189374bc6bull, 0xc0092c251d0b132full }, + { 0x3fa6872b020c49baull, 0xc008fd0ffc23aafdull }, + { 0x3fa70a3d70a3d70aull, 0xc008cf09bc7c359cull }, + { 0x3fa78d4fdf3b645aull, 0xc008a206758fbcd5ull }, + { 0x3fa810624dd2f1aaull, 0xc00875fb038f0e8aull }, + { 0x3fa89374bc6a7efaull, 0xc0084adcf6cf43ccull }, + { 0x3fa916872b020c4aull, 0xc00820a284eda4e9ull }, + { 0x3fa999999999999aull, 0xc007f7427b73e391ull }, + { 0x3faa1cac083126eaull, 0xc007ceb433cefe41ull }, + { 0x3faa9fbe76c8b43aull, 0xc007a6ef887155f8ull }, + { 0x3fab22d0e5604189ull, 0xc0077feccafdc296ull }, + { 0x3faba5e353f7ced9ull, 0xc00759a4bb5dec35ull }, + { 0x3fac28f5c28f5c29ull, 0xc00734107faa015bull }, + { 0x3facac083126e979ull, 0xc0070f299ccb26a1ull }, + { 0x3fad2f1a9fbe76c9ull, 0xc006eae9efc4ca8full }, + { 0x3fadb22d0e560419ull, 0xc006c74ba79372a6ull }, + { 0x3fae353f7ced9169ull, 0xc006a4493f91ac4full }, + { 0x3faeb851eb851eb8ull, 0xc00681dd7a559a2aull }, + { 0x3faf3b645a1cac08ull, 0xc00660035cfb2727ull }, + { 0x3fafbe76c8b43958ull, 0xc0063eb62ad0456bull }, + { 0x3fb020c49ba5e354ull, 0xc0061df16159cf0aull }, + { 0x3fb0624dd2f1a9fcull, 0xc005fdb0b4a8a85aull }, + { 0x3fb0a3d70a3d70a4ull, 0xc005ddf00bf7ac56ull }, + { 0x3fb0e5604189374cull, 0xc005beab7e8bb7f4ull }, + { 0x3fb126e978d4fdf4ull, 0xc0059fdf50cfdce6ull }, + { 0x3fb16872b020c49cull, 0xc0058187f1a862cfull }, + { 0x3fb1a9fbe76c8b44ull, 0xc00563a1f7f7c9cbull }, + { 0x3fb1eb851eb851ecull, 0xc005462a20517cffull }, + { 0x3fb22d0e56041894ull, 0xc005291d4ad651aeull }, + { 0x3fb26e978d4fdf3cull, 0xc0050c78793750c2ull }, + { 0x3fb2b020c49ba5e3ull, 0xc004f038ccdb9f74ull }, + { 0x3fb2f1a9fbe76c8bull, 0xc004d45b8526a908ull }, + { 0x3fb3333333333333ull, 0xc004b8ddfddbf088ull }, + { 0x3fb374bc6a7ef9dbull, 0xc0049dbdad9e2f1dull }, + { 0x3fb3b645a1cac083ull, 0xc00482f824879ac9ull }, + { 0x3fb3f7ced916872bull, 0xc004688b0ad962eeull }, + { 0x3fb4395810624dd3ull, 0xc0044e741fc09c5bull }, + { 0x3fb47ae147ae147bull, 0xc00434b1382efeb8ull }, + { 0x3fb4bc6a7ef9db23ull, 0xc0041b403dc5f92bull }, + { 0x3fb4fdf3b645a1cbull, 0xc004021f2dd2c477ull }, + { 0x3fb53f7ced916873ull, 0xc003e94c185a363dull }, + { 0x3fb5810624dd2f1bull, 0xc003d0c51f333398ull }, + { 0x3fb5c28f5c28f5c3ull, 0xc003b888752eb92dull }, + { 0x3fb604189374bc6bull, 0xc003a0945d4c84b3ull }, + { 0x3fb645a1cac08313ull, 0xc00388e729fb7f9cull }, + { 0x3fb6872b020c49baull, 0xc003717f3c651c82ull }, + { 0x3fb6c8b439581062ull, 0xc0035a5b03c2e954ull }, + { 0x3fb70a3d70a3d70aull, 0xc0034378fcbda721ull }, + { 0x3fb74bc6a7ef9db2ull, 0xc0032cd7b0d545c4ull }, + { 0x3fb78d4fdf3b645aull, 0xc0031675b5d12e59ull }, + { 0x3fb7ced916872b02ull, 0xc0030051ad385261ull }, + { 0x3fb810624dd2f1aaull, 0xc002ea6a43d0800eull }, + { 0x3fb851eb851eb852ull, 0xc002d4be3124857aull }, + { 0x3fb89374bc6a7efaull, 0xc002bf4c3710b550ull }, + { 0x3fb8d4fdf3b645a2ull, 0xc002aa1321556750ull }, + { 0x3fb916872b020c4aull, 0xc0029511c52f166dull }, + { 0x3fb95810624dd2f2ull, 0xc002804700f3c4eaull }, + { 0x3fb999999999999aull, 0xc0026bb1bbb55515ull }, + { 0x3fb9db22d0e56042ull, 0xc0025750e4e88adfull }, + { 0x3fba1cac083126eaull, 0xc002432374106fc5ull }, + { 0x3fba5e353f7ced92ull, 0xc0022f28686dd769ull }, + { 0x3fba9fbe76c8b43aull, 0xc0021b5ec8b2c77cull }, + { 0x3fbae147ae147ae1ull, 0xc00207c5a2b989f6ull }, + { 0x3fbb22d0e5604189ull, 0xc001f45c0b3f341aull }, + { 0x3fbb645a1cac0831ull, 0xc001e1211da1708aull }, + { 0x3fbba5e353f7ced9ull, 0xc001ce13fb9f5db9ull }, + { 0x3fbbe76c8b439581ull, 0xc001bb33cd1d5534ull }, + { 0x3fbc28f5c28f5c29ull, 0xc001a87fbfeb72dfull }, + { 0x3fbc6a7ef9db22d1ull, 0xc00195f7078eb5feull }, + { 0x3fbcac083126e979ull, 0xc0018398dd0c9826ull }, + { 0x3fbced916872b021ull, 0xc00171647eb8f889ull }, + { 0x3fbd2f1a9fbe76c9ull, 0xc0015f5930063c13ull }, + { 0x3fbd70a3d70a3d71ull, 0xc0014d76395784b7ull }, + { 0x3fbdb22d0e560419ull, 0xc0013bbae7d4e42aull }, + { 0x3fbdf3b645a1cac1ull, 0xc0012a268d416fe5ull }, + { 0x3fbe353f7ced9169ull, 0xc00118b87fd31dd3ull }, + { 0x3fbe76c8b4395811ull, 0xc00107701a0c529bull }, + { 0x3fbeb851eb851eb8ull, 0xc000f64cba970baeull }, + { 0x3fbef9db22d0e560ull, 0xc000e54dc42190a9ull }, + { 0x3fbf3b645a1cac08ull, 0xc000d4729d3c98abull }, + { 0x3fbf7ced916872b0ull, 0xc000c3bab03ad16eull }, + { 0x3fbfbe76c8b43958ull, 0xc000b3256b11b6efull }, + { 0x3fc0000000000000ull, 0xc000a2b23f3bab73ull }, + { 0x3fc020c49ba5e354ull, 0xc0009260a19b408eull }, + { 0x3fc04189374bc6a8ull, 0xc00082300a5fa2bcull }, + { 0x3fc0624dd2f1a9fcull, 0xc000721ff4ea19deull }, + { 0x3fc083126e978d50ull, 0xc000622fdfb491aaull }, + { 0x3fc0a3d70a3d70a4ull, 0xc000525f4c391ddaull }, + { 0x3fc0c49ba5e353f8ull, 0xc00042adbeda6e7cull }, + { 0x3fc0e5604189374cull, 0xc000331abecd2978ull }, + { 0x3fc10624dd2f1aa0ull, 0xc00023a5d6021ee8ull }, + { 0x3fc126e978d4fdf4ull, 0xc000144e91114e6aull }, + { 0x3fc147ae147ae148ull, 0xc00005147f25b417ull }, + { 0x3fc16872b020c49cull, 0xbfffebee63d3a8a6ull }, + { 0x3fc189374bc6a7f0ull, 0xbfffcdec7ae9fc0dull }, + { 0x3fc1a9fbe76c8b44ull, 0xbfffb0227072769eull }, + { 0x3fc1cac083126e98ull, 0xbfff928f75e3d237ull }, + { 0x3fc1eb851eb851ecull, 0xbfff7532c125dd07ull }, + { 0x3fc20c49ba5e3540ull, 0xbfff580b8c711a09ull }, + { 0x3fc22d0e56041894ull, 0xbfff3b19162f8665ull }, + { 0x3fc24dd2f1a9fbe8ull, 0xbfff1e5aa0de7747ull }, + { 0x3fc26e978d4fdf3cull, 0xbfff01cf72f1848dull }, + { 0x3fc28f5c28f5c28full, 0xbffee576d6b67510ull }, + { 0x3fc2b020c49ba5e3ull, 0xbffec9501a3a21f1ull }, + { 0x3fc2d0e560418937ull, 0xbffead5a8f2e46c8ull }, + { 0x3fc2f1a9fbe76c8bull, 0xbffe91958ad03518ull }, + { 0x3fc3126e978d4fdfull, 0xbffe760065d061ceull }, + { 0x3fc3333333333333ull, 0xbffe5a9a7c3ac418ull }, + { 0x3fc353f7ced91687ull, 0xbffe3f632d5ffd35ull }, + { 0x3fc374bc6a7ef9dbull, 0xbffe2459dbbf4141ull }, + { 0x3fc395810624dd2full, 0xbffe097decf0f978ull }, + { 0x3fc3b645a1cac083ull, 0xbffdeecec992189bull }, + { 0x3fc3d70a3d70a3d7ull, 0xbffdd44bdd301a9aull }, + { 0x3fc3f7ced916872bull, 0xbffdb9f49635a8e5ull }, + { 0x3fc4189374bc6a7full, 0xbffd9fc865d7dd0full }, + { 0x3fc4395810624dd3ull, 0xbffd85c6c0041bbdull }, + { 0x3fc45a1cac083127ull, 0xbffd6bef1b4e8221ull }, + { 0x3fc47ae147ae147bull, 0xbffd5240f0e0e078ull }, + { 0x3fc49ba5e353f7cfull, 0xbffd38bbbc6a3c4aull }, + { 0x3fc4bc6a7ef9db23ull, 0xbffd1f5efc0ed55full }, + { 0x3fc4dd2f1a9fbe77ull, 0xbffd062a3058a895ull }, + { 0x3fc4fdf3b645a1cbull, 0xbffced1cdc286bf6ull }, + { 0x3fc51eb851eb851full, 0xbffcd43684a6ffabull }, + { 0x3fc53f7ced916873ull, 0xbffcbb76b1374f83ull }, + { 0x3fc5604189374bc7ull, 0xbffca2dceb68a106ull }, + { 0x3fc5810624dd2f1bull, 0xbffc8a68bee94a38ull }, + { 0x3fc5a1cac083126full, 0xbffc7219b979cd3dull }, + { 0x3fc5c28f5c28f5c3ull, 0xbffc59ef6ae05562ull }, + { 0x3fc5e353f7ced917ull, 0xbffc41e964dc9213ull }, + { 0x3fc604189374bc6bull, 0xbffc2a073b1bec6full }, + { 0x3fc624dd2f1a9fbfull, 0xbffc1248832e1466ull }, + { 0x3fc645a1cac08313ull, 0xbffbfaacd479e241ull }, + { 0x3fc6666666666667ull, 0xbffbe333c83289c2ull }, + { 0x3fc6872b020c49baull, 0xbffbcbdcf94d1c0bull }, + { 0x3fc6a7ef9db22d0eull, 0xbffbb4a804765594ull }, + { 0x3fc6c8b439581062ull, 0xbffb9d948808b5b1ull }, + { 0x3fc6e978d4fdf3b6ull, 0xbffb86a22402dd21ull }, + { 0x3fc70a3d70a3d70aull, 0xbffb6fd079fe3149ull }, + { 0x3fc72b020c49ba5eull, 0xbffb591f2d25c1d7ull }, + { 0x3fc74bc6a7ef9db2ull, 0xbffb428de22d6e90ull }, + { 0x3fc76c8b43958106ull, 0xbffb2c1c3f494b43ull }, + { 0x3fc78d4fdf3b645aull, 0xbffb15c9ec253fbaull }, + { 0x3fc7ae147ae147aeull, 0xbffaff9691dce1d3ull }, + { 0x3fc7ced916872b02ull, 0xbffae981daf387cbull }, + { 0x3fc7ef9db22d0e56ull, 0xbffad38b734c90f6ull }, + { 0x3fc810624dd2f1aaull, 0xbffabdb30823e325ull }, + { 0x3fc83126e978d4feull, 0xbffaa7f848069b0aull }, + { 0x3fc851eb851eb852ull, 0xbffa925ae2cbedfdull }, + { 0x3fc872b020c49ba6ull, 0xbffa7cda898e3b99ull }, + { 0x3fc89374bc6a7efaull, 0xbffa6776eea44da9ull }, + { 0x3fc8b4395810624eull, 0xbffa522fc59ac4f9ull }, + { 0x3fc8d4fdf3b645a2ull, 0xbffa3d04c32db1a9ull }, + { 0x3fc8f5c28f5c28f6ull, 0xbffa27f59d4255a1ull }, + { 0x3fc916872b020c4aull, 0xbffa13020ae10fe3ull }, + { 0x3fc9374bc6a7ef9eull, 0xbff9fe29c42f6f7dull }, + { 0x3fc95810624dd2f2ull, 0xbff9e96c826a6cddull }, + { 0x3fc978d4fdf3b646ull, 0xbff9d4c9ffe0c868ull }, + { 0x3fc999999999999aull, 0xbff9c041f7ed8d33ull }, + { 0x3fc9ba5e353f7ceeull, 0xbff9abd426f2b6c1ull }, + { 0x3fc9db22d0e56042ull, 0xbff997804a53f8c6ull }, + { 0x3fc9fbe76c8b4396ull, 0xbff983462071a7ecull }, + { 0x3fca1cac083126eaull, 0xbff96f2568a3c293ull }, + { 0x3fca3d70a3d70a3eull, 0xbff95b1de33518b2ull }, + { 0x3fca5e353f7ced92ull, 0xbff9472f515e91daull }, + { 0x3fca7ef9db22d0e6ull, 0xbff933597542908bull }, + { 0x3fca9fbe76c8b43aull, 0xbff91f9c11e87200ull }, + { 0x3fcac083126e978dull, 0xbff90bf6eb382991ull }, + { 0x3fcae147ae147ae1ull, 0xbff8f869c5f5f6f4ull }, + { 0x3fcb020c49ba5e35ull, 0xbff8e4f467be3696ull }, + { 0x3fcb22d0e5604189ull, 0xbff8d19697014b3dull }, + { 0x3fcb4395810624ddull, 0xbff8be501affa053ull }, + { 0x3fcb645a1cac0831ull, 0xbff8ab20bbc5c41cull }, + { 0x3fcb851eb851eb85ull, 0xbff898084228992cull }, + { 0x3fcba5e353f7ced9ull, 0xbff8850677c19e7bull }, + { 0x3fcbc6a7ef9db22dull, 0xbff8721b26eb4d76ull }, + { 0x3fcbe76c8b439581ull, 0xbff85f461abd8d70ull }, + { 0x3fcc083126e978d5ull, 0xbff84c871f0a3bdeull }, + { 0x3fcc28f5c28f5c29ull, 0xbff839de0059c8c7ull }, + { 0x3fcc49ba5e353f7dull, 0xbff8274a8be7e6ebull }, + { 0x3fcc6a7ef9db22d1ull, 0xbff814cc8fa04f04ull }, + { 0x3fcc8b4395810625ull, 0xbff80263da1b95a9ull }, + { 0x3fccac083126e979ull, 0xbff7f0103a9c1354ull }, + { 0x3fcccccccccccccdull, 0xbff7ddd1810ade05ull }, + { 0x3fcced916872b021ull, 0xbff7cba77df4d41aull }, + { 0x3fcd0e5604189375ull, 0xbff7b9920287b7d6ull }, + { 0x3fcd2f1a9fbe76c9ull, 0xbff7a790e08f5b2eull }, + { 0x3fcd4fdf3b645a1dull, 0xbff795a3ea72db76ull }, + { 0x3fcd70a3d70a3d71ull, 0xbff783caf331ec76ull }, + { 0x3fcd916872b020c5ull, 0xbff77205ce623287ull }, + { 0x3fcdb22d0e560419ull, 0xbff76054502cab5dull }, + { 0x3fcdd2f1a9fbe76dull, 0xbff74eb64d4b2503ull }, + { 0x3fcdf3b645a1cac1ull, 0xbff73d2b9b05c2d2ull }, + { 0x3fce147ae147ae15ull, 0xbff72bb40f308fe1ull }, + { 0x3fce353f7ced9169ull, 0xbff71a4f80291eafull }, + { 0x3fce5604189374bdull, 0xbff708fdc4d435aaull }, + { 0x3fce76c8b4395811ull, 0xbff6f7beb49b883eull }, + { 0x3fce978d4fdf3b65ull, 0xbff6e692276b7c1aull }, + { 0x3fceb851eb851eb8ull, 0xbff6d577f5b0fa65ull }, + { 0x3fced916872b020cull, 0xbff6c46ff8574c88ull }, + { 0x3fcef9db22d0e560ull, 0xbff6b37a08c6045bull }, + { 0x3fcf1a9fbe76c8b4ull, 0xbff6a29600deef4cull }, + { 0x3fcf3b645a1cac08ull, 0xbff691c3bafc145eull }, + { 0x3fcf5c28f5c28f5cull, 0xbff6810311edbc9full }, + { 0x3fcf7ced916872b0ull, 0xbff67053e0f885e4ull }, + { 0x3fcf9db22d0e5604ull, 0xbff65fb603d37f86ull }, + { 0x3fcfbe76c8b43958ull, 0xbff64f2956a650e6ull }, + { 0x3fcfdf3b645a1cacull, 0xbff63eadb6076970ull }, + { 0x3fd0000000000000ull, 0xbff62e42fefa39efull }, + { 0x3fd010624dd2f1aaull, 0xbff61de90eed76f7ull }, + { 0x3fd020c49ba5e354ull, 0xbff60d9fc3b96425ull }, + { 0x3fd03126e978d4feull, 0xbff5fd66fb9e280aull }, + { 0x3fd04189374bc6a8ull, 0xbff5ed3e95422881ull }, + { 0x3fd051eb851eb852ull, 0xbff5dd266fb06f4full }, + { 0x3fd0624dd2f1a9fcull, 0xbff5cd1e6a5716c4ull }, + { 0x3fd072b020c49ba6ull, 0xbff5bd266505be3eull }, + { 0x3fd083126e978d50ull, 0xbff5ad3e3fec065dull }, + { 0x3fd09374bc6a7efaull, 0xbff59d65db9814afull }, + { 0x3fd0a3d70a3d70a4ull, 0xbff58d9d18f51ebcull }, + { 0x3fd0b4395810624eull, 0xbff57de3d949fc2eull }, + { 0x3fd0c49ba5e353f8ull, 0xbff56e39fe37c000ull }, + { 0x3fd0d4fdf3b645a2ull, 0xbff55e9f69b8587eull }, + { 0x3fd0e5604189374cull, 0xbff54f13fe1d35f8ull }, + { 0x3fd0f5c28f5c28f6ull, 0xbff53f979e0df7f8ull }, + { 0x3fd10624dd2f1aa0ull, 0xbff5302a2c8720d9ull }, + { 0x3fd116872b020c4aull, 0xbff520cb8cd8cf9dull }, + { 0x3fd126e978d4fdf4ull, 0xbff5117ba2a57fdcull }, + { 0x3fd1374bc6a7ef9eull, 0xbff5023a51e0cfa5ull }, + { 0x3fd147ae147ae148ull, 0xbff4f3077ece4b36ull }, + { 0x3fd15810624dd2f2ull, 0xbff4e3e30e003e69ull }, + { 0x3fd16872b020c49cull, 0xbff4d4cce4568bafull }, + { 0x3fd178d4fdf3b646ull, 0xbff4c5c4e6fd887dull }, + { 0x3fd189374bc6a7f0ull, 0xbff4b6cafb6cdf16ull }, + { 0x3fd199999999999aull, 0xbff4a7df07667582ull }, + { 0x3fd1a9fbe76c8b44ull, 0xbff49900f0f559a7ull }, + { 0x3fd1ba5e353f7ceeull, 0xbff48a309e6cb255ull }, + { 0x3fd1cac083126e98ull, 0xbff47b6df666b540ull }, + { 0x3fd1db22d0e56042ull, 0xbff46cb8dfc3a1b8ull }, + { 0x3fd1eb851eb851ecull, 0xbff45e1141a8c00full }, + { 0x3fd1fbe76c8b4396ull, 0xbff44f77037f6597ull }, + { 0x3fd20c49ba5e3540ull, 0xbff440ea0cf3fd12ull }, + { 0x3fd21cac083126eaull, 0xbff4326a45f51389ull }, + { 0x3fd22d0e56041894ull, 0xbff423f796b2696eull }, + { 0x3fd23d70a3d70a3eull, 0xbff41591e79c07eaull }, + { 0x3fd24dd2f1a9fbe8ull, 0xbff4073921615a50ull }, + { 0x3fd25e353f7ced92ull, 0xbff3f8ed2cf04b8eull }, + { 0x3fd26e978d4fdf3cull, 0xbff3eaadf3746795ull }, + { 0x3fd27ef9db22d0e5ull, 0xbff3dc7b5e56009aull }, + { 0x3fd28f5c28f5c28full, 0xbff3ce5557395819ull }, + { 0x3fd29fbe76c8b439ull, 0xbff3c03bc7fdcb97ull }, + { 0x3fd2b020c49ba5e3ull, 0xbff3b22e9abd04f9ull }, + { 0x3fd2c083126e978dull, 0xbff3a42db9ca2e6full }, + { 0x3fd2d0e560418937ull, 0xbff396390fb129d0ull }, + { 0x3fd2e147ae147ae1ull, 0xbff388508735cb6bull }, + { 0x3fd2f1a9fbe76c8bull, 0xbff37a740b531820ull }, + { 0x3fd3020c49ba5e35ull, 0xbff36ca3873a86caull }, + { 0x3fd3126e978d4fdfull, 0xbff35edee65344d6ull }, + { 0x3fd322d0e5604189ull, 0xbff3512614397dffull }, + { 0x3fd3333333333333ull, 0xbff34378fcbda721ull }, + { 0x3fd34395810624ddull, 0xbff335d78be3cc08ull }, + { 0x3fd353f7ced91687ull, 0xbff32841ade2e03dull }, + { 0x3fd3645a1cac0831ull, 0xbff31ab74f2412b4ull }, + { 0x3fd374bc6a7ef9dbull, 0xbff30d385c42244aull }, + { 0x3fd3851eb851eb85ull, 0xbff2ffc4c208c11aull }, + { 0x3fd395810624dd2full, 0xbff2f25c6d73dc81ull }, + { 0x3fd3a5e353f7ced9ull, 0xbff2e4ff4baf0fd0ull }, + { 0x3fd3b645a1cac083ull, 0xbff2d7ad4a14fba3ull }, + { 0x3fd3c6a7ef9db22dull, 0xbff2ca66562eabc7ull }, + { 0x3fd3d70a3d70a3d7ull, 0xbff2bd2a5db2fda2ull }, + { 0x3fd3e76c8b439581ull, 0xbff2aff94e86091cull }, + { 0x3fd3f7ced916872bull, 0xbff2a2d316b88beeull }, + { 0x3fd4083126e978d5ull, 0xbff295b7a4875754ull }, + { 0x3fd4189374bc6a7full, 0xbff288a6e65ac018ull }, + { 0x3fd428f5c28f5c29ull, 0xbff27ba0cac610e1ull }, + { 0x3fd4395810624dd3ull, 0xbff26ea54086fec6ull }, + { 0x3fd449ba5e353f7dull, 0xbff261b436852010ull }, + { 0x3fd45a1cac083127ull, 0xbff254cd9bd16529ull }, + { 0x3fd46a7ef9db22d1ull, 0xbff247f15fa593acull }, + { 0x3fd47ae147ae147bull, 0xbff23b1f7163c380ull }, + { 0x3fd48b4395810625ull, 0xbff22e57c095de09ull }, + { 0x3fd49ba5e353f7cfull, 0xbff2219a3ced1f52ull }, + { 0x3fd4ac083126e979ull, 0xbff214e6d6419934ull }, + { 0x3fd4bc6a7ef9db23ull, 0xbff2083d7c91b867ull }, + { 0x3fd4cccccccccccdull, 0xbff1fb9e2001cb78ull }, + { 0x3fd4dd2f1a9fbe77ull, 0xbff1ef08b0db8b9dull }, + { 0x3fd4ed916872b021ull, 0xbff1e27d1f8da75dull }, + { 0x3fd4fdf3b645a1cbull, 0xbff1d5fb5cab4effull }, + { 0x3fd50e5604189375ull, 0xbff1c98358ebc2bdull }, + { 0x3fd51eb851eb851full, 0xbff1bd150529e2b4ull }, + { 0x3fd52f1a9fbe76c9ull, 0xbff1b0b05263c081ull }, + { 0x3fd53f7ced916873ull, 0xbff1a45531ba328bull }, + { 0x3fd54fdf3b645a1dull, 0xbff19803947068f1ull }, + { 0x3fd5604189374bc7ull, 0xbff18bbb6beb840full }, + { 0x3fd570a3d70a3d71ull, 0xbff17f7ca9b22c98ull }, + { 0x3fd5810624dd2f1bull, 0xbff173473f6c2d40ull }, + { 0x3fd5916872b020c5ull, 0xbff1671b1ee20de8ull }, + { 0x3fd5a1cac083126full, 0xbff15af839fcb045ull }, + { 0x3fd5b22d0e560419ull, 0xbff14ede82c4ee07ull }, + { 0x3fd5c28f5c28f5c3ull, 0xbff142cdeb63386aull }, + { 0x3fd5d2f1a9fbe76dull, 0xbff136c6661f3935ull }, + { 0x3fd5e353f7ced917ull, 0xbff12ac7e55f751bull }, + { 0x3fd5f3b645a1cac1ull, 0xbff11ed25ba8ef7bull }, + { 0x3fd604189374bc6bull, 0xbff112e5bb9ecf78ull }, + { 0x3fd6147ae147ae15ull, 0xbff10701f8020663ull }, + { 0x3fd624dd2f1a9fbfull, 0xbff0fb2703b0f76full }, + { 0x3fd6353f7ced9169ull, 0xbff0ef54d1a720adull }, + { 0x3fd645a1cac08313ull, 0xbff0e38b54fcc549ull }, + { 0x3fd65604189374bdull, 0xbff0d7ca80e69900ull }, + { 0x3fd6666666666667ull, 0xbff0cc1248b56ccbull }, + { 0x3fd676c8b4395811ull, 0xbff0c0629fd5dcbfull }, + { 0x3fd6872b020c49baull, 0xbff0b4bb79cfff14ull }, + { 0x3fd6978d4fdf3b64ull, 0xbff0a91cca471450ull }, + { 0x3fd6a7ef9db22d0eull, 0xbff09d8684f9389cull }, + { 0x3fd6b851eb851eb8ull, 0xbff091f89dbf162aull }, + { 0x3fd6c8b439581062ull, 0xbff08673088b98b9ull }, + { 0x3fd6d916872b020cull, 0xbff07af5b96ba22cull }, + { 0x3fd6e978d4fdf3b6ull, 0xbff06f80a485c029ull }, + { 0x3fd6f9db22d0e560ull, 0xbff06413be19e2ccull }, + { 0x3fd70a3d70a3d70aull, 0xbff058aefa811452ull }, + { 0x3fd71a9fbe76c8b4ull, 0xbff04d524e2d31cfull }, + { 0x3fd72b020c49ba5eull, 0xbff041fdada8a4dfull }, + { 0x3fd73b645a1cac08ull, 0xbff036b10d961e48ull }, + { 0x3fd74bc6a7ef9db2ull, 0xbff02b6c62b05199ull }, + { 0x3fd75c28f5c28f5cull, 0xbff0202fa1c9b1b5ull }, + { 0x3fd76c8b43958106ull, 0xbff014fabfcc2e4bull }, + { 0x3fd77ced916872b0ull, 0xbff009cdb1b8f237ull }, + { 0x3fd78d4fdf3b645aull, 0xbfeffd50d9504585ull }, + { 0x3fd79db22d0e5604ull, 0xbfefe715cb913fa1ull }, + { 0x3fd7ae147ae147aeull, 0xbfefd0ea24bf89b7ull }, + { 0x3fd7be76c8b43958ull, 0xbfefbacdcf9255a9ull }, + { 0x3fd7ced916872b02ull, 0xbfefa4c0b6ecd5a7ull }, + { 0x3fd7df3b645a1cacull, 0xbfef8ec2c5ddc33eull }, + { 0x3fd7ef9db22d0e56ull, 0xbfef78d3e79ee7fdull }, + { 0x3fd8000000000000ull, 0xbfef62f40794a7b8ull }, + { 0x3fd810624dd2f1aaull, 0xbfef4d23114d8c5bull }, + { 0x3fd820c49ba5e354ull, 0xbfef3760f081d343ull }, + { 0x3fd83126e978d4feull, 0xbfef21ad9112fc25ull }, + { 0x3fd84189374bc6a8ull, 0xbfef0c08df0b596aull }, + { 0x3fd851eb851eb852ull, 0xbfeef672c69da20bull }, + { 0x3fd8624dd2f1a9fcull, 0xbfeee0eb342484dcull }, + { 0x3fd872b020c49ba6ull, 0xbfeecb7214223d43ull }, + { 0x3fd883126e978d50ull, 0xbfeeb60753402957ull }, + { 0x3fd89374bc6a7efaull, 0xbfeea0aade4e6162ull }, + { 0x3fd8a3d70a3d70a4ull, 0xbfee8b5ca24350beull }, + { 0x3fd8b4395810624eull, 0xbfee761c8c3b5003ull }, + { 0x3fd8c49ba5e353f8ull, 0xbfee60ea89784093ull }, + { 0x3fd8d4fdf3b645a2ull, 0xbfee4bc687612963ull }, + { 0x3fd8e5604189374cull, 0xbfee36b07381d516ull }, + { 0x3fd8f5c28f5c28f6ull, 0xbfee21a83b8a7153ull }, + { 0x3fd90624dd2f1aa0ull, 0xbfee0cadcd4f2f5aull }, + { 0x3fd916872b020c4aull, 0xbfedf7c116c7e5d7ull }, + { 0x3fd926e978d4fdf4ull, 0xbfede2e2060fb3d9ull }, + { 0x3fd9374bc6a7ef9eull, 0xbfedce108964a50aull }, + { 0x3fd947ae147ae148ull, 0xbfedb94c8f275703ull }, + { 0x3fd95810624dd2f2ull, 0xbfeda49605da9fcaull }, + { 0x3fd96872b020c49cull, 0xbfed8fecdc233575ull }, + { 0x3fd978d4fdf3b646ull, 0xbfed7b5100c756e1ull }, + { 0x3fd989374bc6a7f0ull, 0xbfed66c262ae758cull }, + { 0x3fd999999999999aull, 0xbfed5240f0e0e077ull }, + { 0x3fd9a9fbe76c8b44ull, 0xbfed3dcc9a877023ull }, + { 0x3fd9ba5e353f7ceeull, 0xbfed29654eeb3392ull }, + { 0x3fd9cac083126e98ull, 0xbfed150afd751e56ull }, + { 0x3fd9db22d0e56042ull, 0xbfed00bd95adb79dull }, + { 0x3fd9eb851eb851ecull, 0xbfecec7d073cca46ull }, + { 0x3fd9fbe76c8b4396ull, 0xbfecd84941e915e9ull }, + { 0x3fda0c49ba5e3540ull, 0xbfecc422359800deull }, + { 0x3fda1cac083126eaull, 0xbfecb007d24d4b37ull }, + { 0x3fda2d0e56041894ull, 0xbfec9bfa082ac2adull }, + { 0x3fda3d70a3d70a3eull, 0xbfec87f8c76ff775ull }, + { 0x3fda4dd2f1a9fbe8ull, 0xbfec74040079f206ull }, + { 0x3fda5e353f7ced92ull, 0xbfec601ba3c2e9c4ull }, + { 0x3fda6e978d4fdf3cull, 0xbfec4c3fa1e1fc8dull }, + { 0x3fda7ef9db22d0e6ull, 0xbfec386feb8ae728ull }, + { 0x3fda8f5c28f5c290ull, 0xbfec24ac718dbe8eull }, + { 0x3fda9fbe76c8b43aull, 0xbfec10f524d6aa11ull }, + { 0x3fdab020c49ba5e3ull, 0xbfebfd49f66d9e5bull }, + { 0x3fdac083126e978dull, 0xbfebe9aad7761933ull }, + { 0x3fdad0e560418937ull, 0xbfebd617b92ede28ull }, + { 0x3fdae147ae147ae1ull, 0xbfebc2908cf1b3f9ull }, + { 0x3fdaf1a9fbe76c8bull, 0xbfebaf15443322d0ull }, + { 0x3fdb020c49ba5e35ull, 0xbfeb9ba5d082333dull }, + { 0x3fdb126e978d4fdfull, 0xbfeb884223882dffull }, + { 0x3fdb22d0e5604189ull, 0xbfeb74ea2f085c8aull }, + { 0x3fdb333333333333ull, 0xbfeb619de4dfca4dull }, + { 0x3fdb4395810624ddull, 0xbfeb4e5d370506b6ull }, + { 0x3fdb53f7ced91687ull, 0xbfeb3b281787e7edull }, + { 0x3fdb645a1cac0831ull, 0xbfeb27fe78914e48ull }, + { 0x3fdb74bc6a7ef9dbull, 0xbfeb14e04c62e87aull }, + { 0x3fdb851eb851eb85ull, 0xbfeb01cd8556f868ull }, + { 0x3fdb95810624dd2full, 0xbfeaeec615e018b7ull }, + { 0x3fdba5e353f7ced9ull, 0xbfeadbc9f0890306ull }, + { 0x3fdbb645a1cac083ull, 0xbfeac8d907f456d1ull }, + { 0x3fdbc6a7ef9db22dull, 0xbfeab5f34edc60fdull }, + { 0x3fdbd70a3d70a3d7ull, 0xbfeaa318b812e40bull }, + { 0x3fdbe76c8b439581ull, 0xbfea90493680e0f1ull }, + { 0x3fdbf7ced916872bull, 0xbfea7d84bd266090ull }, + { 0x3fdc083126e978d5ull, 0xbfea6acb3f1a3dccull }, + { 0x3fdc189374bc6a7full, 0xbfea581caf89f043ull }, + { 0x3fdc28f5c28f5c29ull, 0xbfea457901b9579full }, + { 0x3fdc395810624dd3ull, 0xbfea32e02902877aull }, + { 0x3fdc49ba5e353f7dull, 0xbfea205218d593e8ull }, + { 0x3fdc5a1cac083127ull, 0xbfea0dcec4b85e82ull }, + { 0x3fdc6a7ef9db22d1ull, 0xbfe9fb562046641aull }, + { 0x3fdc7ae147ae147bull, 0xbfe9e8e81f308ae9ull }, + { 0x3fdc8b4395810625ull, 0xbfe9d684b53cf164ull }, + { 0x3fdc9ba5e353f7cfull, 0xbfe9c42bd646bd85ull }, + { 0x3fdcac083126e979ull, 0xbfe9b1dd763decb8ull }, + { 0x3fdcbc6a7ef9db23ull, 0xbfe99f998927243cull }, + { 0x3fdccccccccccccdull, 0xbfe98d60031b821bull }, + { 0x3fdcdd2f1a9fbe77ull, 0xbfe97b30d8486e9cull }, + { 0x3fdced916872b021ull, 0xbfe9690bfcef6e46ull }, + { 0x3fdcfdf3b645a1cbull, 0xbfe956f16565f454ull }, + { 0x3fdd0e5604189375ull, 0xbfe944e1061535bdull }, + { 0x3fdd1eb851eb851full, 0xbfe932dad379fca8ull }, + { 0x3fdd2f1a9fbe76c9ull, 0xbfe920dec2247c6dull }, + { 0x3fdd3f7ced916873ull, 0xbfe90eecc6b82605ull }, + { 0x3fdd4fdf3b645a1dull, 0xbfe8fd04d5eb7cfdull }, + { 0x3fdd604189374bc7ull, 0xbfe8eb26e487ecdbull }, + { 0x3fdd70a3d70a3d71ull, 0xbfe8d952e7699efcull }, + { 0x3fdd810624dd2f1bull, 0xbfe8c788d37f50eaull }, + { 0x3fdd916872b020c5ull, 0xbfe8b5c89dca2b20ull }, + { 0x3fdda1cac083126full, 0xbfe8a4123b5d9842ull }, + { 0x3fddb22d0e560419ull, 0xbfe89265a15f1ccaull }, + { 0x3fddc28f5c28f5c3ull, 0xbfe880c2c5062f1eull }, + { 0x3fddd2f1a9fbe76dull, 0xbfe86f299b9c1017ull }, + { 0x3fdde353f7ced917ull, 0xbfe85d9a1a7ba3f3ull }, + { 0x3fddf3b645a1cac1ull, 0xbfe84c1437114bb5ull }, + { 0x3fde04189374bc6bull, 0xbfe83a97e6dabee8ull }, + { 0x3fde147ae147ae15ull, 0xbfe829251f66e5d2ull }, + { 0x3fde24dd2f1a9fbfull, 0xbfe817bbd655b409ull }, + { 0x3fde353f7ced9169ull, 0xbfe8065c0158036eull }, + { 0x3fde45a1cac08313ull, 0xbfe7f505962f6f8eull }, + { 0x3fde5604189374bdull, 0xbfe7e3b88aae3165ull }, + { 0x3fde666666666667ull, 0xbfe7d274d4b6fb82ull }, + { 0x3fde76c8b4395811ull, 0xbfe7c13a6a3cd68dull }, + { 0x3fde872b020c49bbull, 0xbfe7b0094142fe2cull }, + { 0x3fde978d4fdf3b65ull, 0xbfe79ee14fdcbe45ull }, + { 0x3fdea7ef9db22d0full, 0xbfe78dc28c2d509full }, + { 0x3fdeb851eb851eb8ull, 0xbfe77cacec67badaull }, + { 0x3fdec8b439581062ull, 0xbfe76ba066ceaccaull }, + { 0x3fded916872b020cull, 0xbfe75a9cf1b45f21ull }, + { 0x3fdee978d4fdf3b6ull, 0xbfe749a2837a727eull }, + { 0x3fdef9db22d0e560ull, 0xbfe738b11291cec6ull }, + { 0x3fdf0a3d70a3d70aull, 0xbfe727c8957a82dcull }, + { 0x3fdf1a9fbe76c8b4ull, 0xbfe716e902c3a4a9ull }, + { 0x3fdf2b020c49ba5eull, 0xbfe70612510b3181ull }, + { 0x3fdf3b645a1cac08ull, 0xbfe6f54476fdeeceull }, + { 0x3fdf4bc6a7ef9db2ull, 0xbfe6e47f6b574b15ull }, + { 0x3fdf5c28f5c28f5cull, 0xbfe6d3c324e13f4full }, + { 0x3fdf6c8b43958106ull, 0xbfe6c30f9a743088ull }, + { 0x3fdf7ced916872b0ull, 0xbfe6b264c2f6d1d8ull }, + { 0x3fdf8d4fdf3b645aull, 0xbfe6a1c2955e06a0ull }, + { 0x3fdf9db22d0e5604ull, 0xbfe6912908acc51dull }, + { 0x3fdfae147ae147aeull, 0xbfe6809813f3f942ull }, + { 0x3fdfbe76c8b43958ull, 0xbfe6700fae5267deull }, + { 0x3fdfced916872b02ull, 0xbfe65f8fcef4920cull }, + { 0x3fdfdf3b645a1cacull, 0xbfe64f186d1498f1ull }, + { 0x3fdfef9db22d0e56ull, 0xbfe63ea97ffa21bbull }, + { 0x3fe0000000000000ull, 0xbfe62e42fefa39efull }, + { 0x3fe0083126e978d5ull, 0xbfe61de4e1773bf8ull }, + { 0x3fe010624dd2f1aaull, 0xbfe60d8f1ee0b3ffull }, + { 0x3fe0189374bc6a7full, 0xbfe5fd41aeb3450aull }, + { 0x3fe020c49ba5e354ull, 0xbfe5ecfc88788e5bull }, + { 0x3fe028f5c28f5c29ull, 0xbfe5dcbfa3c71115ull }, + { 0x3fe03126e978d4feull, 0xbfe5cc8af8421624ull }, + { 0x3fe0395810624dd3ull, 0xbfe5bc5e7d999465ull }, + { 0x3fe04189374bc6a8ull, 0xbfe5ac3a2b8a1713ull }, + { 0x3fe049ba5e353f7dull, 0xbfe59c1df9dca470ull }, + { 0x3fe051eb851eb852ull, 0xbfe58c09e066a4b0ull }, + { 0x3fe05a1cac083127ull, 0xbfe57bfdd709c922ull }, + { 0x3fe0624dd2f1a9fcull, 0xbfe56bf9d5b3f399ull }, + { 0x3fe06a7ef9db22d1ull, 0xbfe55bfdd45f1e11ull }, + { 0x3fe072b020c49ba6ull, 0xbfe54c09cb11428dull }, + { 0x3fe07ae147ae147bull, 0xbfe53c1db1dc433cull }, + { 0x3fe083126e978d50ull, 0xbfe52c3980ddd2caull }, + { 0x3fe08b4395810625ull, 0xbfe51c5d303f5cfaull }, + { 0x3fe09374bc6a7efaull, 0xbfe50c88b835ef6full }, + { 0x3fe09ba5e353f7cfull, 0xbfe4fcbc110222b8ull }, + { 0x3fe0a3d70a3d70a4ull, 0xbfe4ecf732f00389ull }, + { 0x3fe0ac083126e979ull, 0xbfe4dd3a1656fc3aull }, + { 0x3fe0b4395810624eull, 0xbfe4cd84b399be6dull }, + { 0x3fe0bc6a7ef9db23ull, 0xbfe4bdd703262cfdull }, + { 0x3fe0c49ba5e353f8ull, 0xbfe4ae30fd754610ull }, + { 0x3fe0cccccccccccdull, 0xbfe49e929b0b0d70ull }, + { 0x3fe0d4fdf3b645a2ull, 0xbfe48efbd476770cull }, + { 0x3fe0dd2f1a9fbe77ull, 0xbfe47f6ca25151b3ull }, + { 0x3fe0e5604189374cull, 0xbfe46fe4fd403201ull }, + { 0x3fe0ed916872b021ull, 0xbfe46064ddf25d81ull }, + { 0x3fe0f5c28f5c28f6ull, 0xbfe450ec3d21b601ull }, + { 0x3fe0fdf3b645a1cbull, 0xbfe4417b1392a512ull }, + { 0x3fe10624dd2f1aa0ull, 0xbfe432115a1407c3ull }, + { 0x3fe10e5604189375ull, 0xbfe422af097f1a87ull }, + { 0x3fe116872b020c4aull, 0xbfe413541ab7654bull }, + { 0x3fe11eb851eb851full, 0xbfe4040086aaa7bfull }, + { 0x3fe126e978d4fdf4ull, 0xbfe3f4b44650c5c9ull }, + { 0x3fe12f1a9fbe76c9ull, 0xbfe3e56f52abb42cull }, + { 0x3fe1374bc6a7ef9eull, 0xbfe3d631a4c7655aull }, + { 0x3fe13f7ced916873ull, 0xbfe3c6fb35b9b675ull }, + { 0x3fe147ae147ae148ull, 0xbfe3b7cbfea25c7dull }, + { 0x3fe14fdf3b645a1dull, 0xbfe3a8a3f8aad1a6ull }, + { 0x3fe15810624dd2f2ull, 0xbfe399831d0642e4ull }, + { 0x3fe1604189374bc7ull, 0xbfe38a6964f17d97ull }, + { 0x3fe16872b020c49cull, 0xbfe37b56c9b2dd6eull }, + { 0x3fe170a3d70a3d71ull, 0xbfe36c4b449a3a68ull }, + { 0x3fe178d4fdf3b646ull, 0xbfe35d46cf00d70aull }, + { 0x3fe1810624dd2f1bull, 0xbfe34e4962494eb9ull }, + { 0x3fe189374bc6a7f0ull, 0xbfe33f52f7df843cull }, + { 0x3fe1916872b020c5ull, 0xbfe3306389389070ull }, + { 0x3fe199999999999aull, 0xbfe3217b0fd2b116ull }, + { 0x3fe1a1cac083126full, 0xbfe31299853537d5ull }, + { 0x3fe1a9fbe76c8b44ull, 0xbfe303bee2f0795eull }, + { 0x3fe1b22d0e560419ull, 0xbfe2f4eb229dbcbbull }, + { 0x3fe1ba5e353f7ceeull, 0xbfe2e61e3ddf2abaull }, + { 0x3fe1c28f5c28f5c3ull, 0xbfe2d7582e5fbd90ull }, + { 0x3fe1cac083126e98ull, 0xbfe2c898edd33091ull }, + { 0x3fe1d2f1a9fbe76dull, 0xbfe2b9e075f5f013ull }, + { 0x3fe1db22d0e56042ull, 0xbfe2ab2ec08d0980ull }, + { 0x3fe1e353f7ced917ull, 0xbfe29c83c7661b79ull }, + { 0x3fe1eb851eb851ecull, 0xbfe28ddf8457462full }, + { 0x3fe1f3b645a1cac1ull, 0xbfe27f41f13f1bd6ull }, + { 0x3fe1fbe76c8b4396ull, 0xbfe270ab0804913full }, + { 0x3fe204189374bc6bull, 0xbfe2621ac296ee93ull }, + { 0x3fe20c49ba5e3540ull, 0xbfe253911aedc034ull }, + { 0x3fe2147ae147ae15ull, 0xbfe2450e0b08c7bdull }, + { 0x3fe21cac083126eaull, 0xbfe236918cefed23ull }, + { 0x3fe224dd2f1a9fbfull, 0xbfe2281b9ab32ffeull }, + { 0x3fe22d0e56041894ull, 0xbfe219ac2e6a98ecull }, + { 0x3fe2353f7ced9169ull, 0xbfe20b4342362b19ull }, + { 0x3fe23d70a3d70a3eull, 0xbfe1fce0d03dd5e4ull }, + { 0x3fe245a1cac08313ull, 0xbfe1ee84d2b166acull }, + { 0x3fe24dd2f1a9fbe8ull, 0xbfe1e02f43c87ab0ull }, + { 0x3fe25604189374bdull, 0xbfe1d1e01dc2711cull }, + { 0x3fe25e353f7ced92ull, 0xbfe1c3975ae65d2dull }, + { 0x3fe2666666666667ull, 0xbfe1b554f582f873ull }, + { 0x3fe26e978d4fdf3cull, 0xbfe1a718e7ee953bull }, + { 0x3fe276c8b4395810ull, 0xbfe198e32c87110dull }, + { 0x3fe27ef9db22d0e5ull, 0xbfe18ab3bdb1c745ull }, + { 0x3fe2872b020c49baull, 0xbfe17c8a95db83deull }, + { 0x3fe28f5c28f5c28full, 0xbfe16e67af787643ull }, + { 0x3fe2978d4fdf3b64ull, 0xbfe1604b05042447ull }, + { 0x3fe29fbe76c8b439ull, 0xbfe1523491015d3eull }, + { 0x3fe2a7ef9db22d0eull, 0xbfe144244dfa2d29ull }, + { 0x3fe2b020c49ba5e3ull, 0xbfe1361a367fd003ull }, + { 0x3fe2b851eb851eb8ull, 0xbfe12816452aa52eull }, + { 0x3fe2c083126e978dull, 0xbfe11a18749a22efull }, + { 0x3fe2c8b439581062ull, 0xbfe10c20bf74ca16ull }, + { 0x3fe2d0e560418937ull, 0xbfe0fe2f206819b2ull }, + { 0x3fe2d916872b020cull, 0xbfe0f043922882e9ull }, + { 0x3fe2e147ae147ae1ull, 0xbfe0e25e0f715ce7ull }, + { 0x3fe2e978d4fdf3b6ull, 0xbfe0d47e9304d8e5ull }, + { 0x3fe2f1a9fbe76c8bull, 0xbfe0c6a517abf651ull }, + { 0x3fe2f9db22d0e560ull, 0xbfe0b8d198367706ull }, + { 0x3fe3020c49ba5e35ull, 0xbfe0ab040f7ad3a5ull }, + { 0x3fe30a3d70a3d70aull, 0xbfe09d3c78563006ull }, + { 0x3fe3126e978d4fdfull, 0xbfe08f7acdac4fbdull }, + { 0x3fe31a9fbe76c8b4ull, 0xbfe081bf0a678abcull }, + { 0x3fe322d0e5604189ull, 0xbfe074092978c20full }, + { 0x3fe32b020c49ba5eull, 0xbfe0665925d754aaull }, + { 0x3fe3333333333333ull, 0xbfe058aefa811452ull }, + { 0x3fe33b645a1cac08ull, 0xbfe04b0aa27a3aa2ull }, + { 0x3fe34395810624ddull, 0xbfe03d6c18cd5e20ull }, + { 0x3fe34bc6a7ef9db2ull, 0xbfe02fd3588b676dull }, + { 0x3fe353f7ced91687ull, 0xbfe022405ccb868cull }, + { 0x3fe35c28f5c28f5cull, 0xbfe014b320ab283eull }, + { 0x3fe3645a1cac0831ull, 0xbfe0072b9f4deb78ull }, + { 0x3fe36c8b43958106ull, 0xbfdff353a7bb2dd6ull }, + { 0x3fe374bc6a7ef9dbull, 0xbfdfd85b73141d49ull }, + { 0x3fe37ced916872b0ull, 0xbfdfbd6e97129387ull }, + { 0x3fe3851eb851eb85ull, 0xbfdfa28d0a2e908aull }, + { 0x3fe38d4fdf3b645aull, 0xbfdf87b6c2ec11caull }, + { 0x3fe395810624dd2full, 0xbfdf6cebb7dafe25ull }, + { 0x3fe39db22d0e5604ull, 0xbfdf522bdf9711f8ull }, + { 0x3fe3a5e353f7ced9ull, 0xbfdf377730c7cb60ull }, + { 0x3fe3ae147ae147aeull, 0xbfdf1ccda220569full }, + { 0x3fe3b645a1cac083ull, 0xbfdf022f2a5f7aaeull }, + { 0x3fe3be76c8b43958ull, 0xbfdee79bc04f85f9ull }, + { 0x3fe3c6a7ef9db22dull, 0xbfdecd135ac63b3dull }, + { 0x3fe3ced916872b02ull, 0xbfdeb295f0a4be96ull }, + { 0x3fe3d70a3d70a3d7ull, 0xbfde982378d782aaull }, + { 0x3fe3df3b645a1cacull, 0xbfde7dbbea563605ull }, + { 0x3fe3e76c8b439581ull, 0xbfde635f3c23b092ull }, + { 0x3fe3ef9db22d0e56ull, 0xbfde490d654de143ull }, + { 0x3fe3f7ced916872bull, 0xbfde2ec65cedbbd8ull }, + { 0x3fe4000000000000ull, 0xbfde148a1a2726ceull }, + { 0x3fe4083126e978d5ull, 0xbfddfa589428e971ull }, + { 0x3fe410624dd2f1aaull, 0xbfdde031c22c9a19ull }, + { 0x3fe4189374bc6a7full, 0xbfddc6159b768c81ull }, + { 0x3fe420c49ba5e354ull, 0xbfddac041755c04cull }, + { 0x3fe428f5c28f5c29ull, 0xbfdd91fd2d23cfa6ull }, + { 0x3fe43126e978d4feull, 0xbfdd7800d444de0dull }, + { 0x3fe4395810624dd3ull, 0xbfdd5e0f04278739ull }, + { 0x3fe44189374bc6a8ull, 0xbfdd4427b444ce2cull }, + { 0x3fe449ba5e353f7dull, 0xbfdd2a4adc200c60ull }, + { 0x3fe451eb851eb852ull, 0xbfdd10787346e115ull }, + { 0x3fe45a1cac083127ull, 0xbfdcf6b0715120c7ull }, + { 0x3fe4624dd2f1a9fcull, 0xbfdcdcf2cde0c4c1ull }, + { 0x3fe46a7ef9db22d1ull, 0xbfdcc33f80a1dad2ull }, + { 0x3fe472b020c49ba6ull, 0xbfdca996814a7520ull }, + { 0x3fe47ae147ae147bull, 0xbfdc8ff7c79a9a21ull }, + { 0x3fe483126e978d50ull, 0xbfdc76634b5c34b1ull }, + { 0x3fe48b4395810625ull, 0xbfdc5cd904630444ull }, + { 0x3fe49374bc6a7efaull, 0xbfdc4358ea8c8d3full }, + { 0x3fe49ba5e353f7cfull, 0xbfdc29e2f5c00969ull }, + { 0x3fe4a3d70a3d70a4ull, 0xbfdc10771dee5883ull }, + { 0x3fe4ac083126e979ull, 0xbfdbf7155b11f0f3ull }, + { 0x3fe4b4395810624eull, 0xbfdbddbda52ed09aull }, + { 0x3fe4bc6a7ef9db23ull, 0xbfdbc46ff4526dbfull }, + { 0x3fe4c49ba5e353f8ull, 0xbfdbab2c4093a81bull }, + { 0x3fe4cccccccccccdull, 0xbfdb91f28212ba02ull }, + { 0x3fe4d4fdf3b645a2ull, 0xbfdb78c2b0f929acull }, + { 0x3fe4dd2f1a9fbe77ull, 0xbfdb5f9cc579ba97ull }, + { 0x3fe4e5604189374cull, 0xbfdb4680b7d05f03ull }, + { 0x3fe4ed916872b021ull, 0xbfdb2d6e80422996ull }, + { 0x3fe4f5c28f5c28f6ull, 0xbfdb1466171d3f0full }, + { 0x3fe4fdf3b645a1cbull, 0xbfdafb6774b8c81cull }, + { 0x3fe50624dd2f1aa0ull, 0xbfdae2729174e34cull }, + { 0x3fe50e5604189375ull, 0xbfdac98765ba9715ull }, + { 0x3fe516872b020c4aull, 0xbfdab0a5e9fbc402ull }, + { 0x3fe51eb851eb851full, 0xbfda97ce16b316f1ull }, + { 0x3fe526e978d4fdf4ull, 0xbfda7effe463fb6cull }, + { 0x3fe52f1a9fbe76c9ull, 0xbfda663b4b9a8e25ull }, + { 0x3fe5374bc6a7ef9eull, 0xbfda4d8044eb8f83ull }, + { 0x3fe53f7ced916873ull, 0xbfda34cec8f4564eull }, + { 0x3fe547ae147ae148ull, 0xbfda1c26d05ac275ull }, + { 0x3fe54fdf3b645a1dull, 0xbfda038853cd2fe6ull }, + { 0x3fe55810624dd2f2ull, 0xbfd9eaf34c02698dull }, + { 0x3fe5604189374bc7ull, 0xbfd9d267b1b99c5cull }, + { 0x3fe56872b020c49cull, 0xbfd9b9e57dba4a7aull }, + { 0x3fe570a3d70a3d71ull, 0xbfd9a16ca8d43e81ull }, + { 0x3fe578d4fdf3b646ull, 0xbfd988fd2bdf7ed7ull }, + { 0x3fe5810624dd2f1bull, 0xbfd97096ffbc4123ull }, + { 0x3fe589374bc6a7f0ull, 0xbfd9583a1d52ddd4ull }, + { 0x3fe5916872b020c5ull, 0xbfd93fe67d93c3c2ull }, + { 0x3fe599999999999aull, 0xbfd9279c19776be9ull }, + { 0x3fe5a1cac083126full, 0xbfd90f5ae9fe4d36ull }, + { 0x3fe5a9fbe76c8b44ull, 0xbfd8f722e830d073ull }, + { 0x3fe5b22d0e560419ull, 0xbfd8def40d1f443full }, + { 0x3fe5ba5e353f7ceeull, 0xbfd8c6ce51e1d126ull }, + { 0x3fe5c28f5c28f5c3ull, 0xbfd8aeb1af986dccull }, + { 0x3fe5cac083126e98ull, 0xbfd8969e1f6ad32dull }, + { 0x3fe5d2f1a9fbe76dull, 0xbfd87e939a8870f7ull }, + { 0x3fe5db22d0e56042ull, 0xbfd866921a2861f4ull }, + { 0x3fe5e353f7ced917ull, 0xbfd84e999789608eull }, + { 0x3fe5eb851eb851ecull, 0xbfd836aa0bf1bb68ull }, + { 0x3fe5f3b645a1cac1ull, 0xbfd81ec370af4a0cull }, + { 0x3fe5fbe76c8b4396ull, 0xbfd806e5bf1761aeull }, + { 0x3fe604189374bc6bull, 0xbfd7ef10f086ca00ull }, + { 0x3fe60c49ba5e3540ull, 0xbfd7d744fe61b225ull }, + { 0x3fe6147ae147ae15ull, 0xbfd7bf81e213a5acull }, + { 0x3fe61cac083126eaull, 0xbfd7a7c7950f81a9ull }, + { 0x3fe624dd2f1a9fbfull, 0xbfd7901610cf69dcull }, + { 0x3fe62d0e56041894ull, 0xbfd7786d4ed4bdf3ull }, + { 0x3fe6353f7ced9169ull, 0xbfd760cd48a80ed6ull }, + { 0x3fe63d70a3d70a3eull, 0xbfd74935f7d91410ull }, + { 0x3fe645a1cac08313ull, 0xbfd731a755fea147ull }, + { 0x3fe64dd2f1a9fbe8ull, 0xbfd71a215cb69bc7ull }, + { 0x3fe65604189374bdull, 0xbfd702a405a5f021ull }, + { 0x3fe65e353f7ced92ull, 0xbfd6eb2f4a7887e1ull }, + { 0x3fe6666666666667ull, 0xbfd6d3c324e13f4dull }, + { 0x3fe66e978d4fdf3cull, 0xbfd6bc5f8e99db42ull }, + { 0x3fe676c8b4395811ull, 0xbfd6a5048162ff1dull }, + { 0x3fe67ef9db22d0e6ull, 0xbfd68db1f70422b5ull }, + { 0x3fe6872b020c49baull, 0xbfd67667e94b8871ull }, + { 0x3fe68f5c28f5c28full, 0xbfd65f26520e335aull }, + { 0x3fe6978d4fdf3b64ull, 0xbfd647ed2b27dd63ull }, + { 0x3fe69fbe76c8b439ull, 0xbfd630bc6e7aed9cull }, + { 0x3fe6a7ef9db22d0eull, 0xbfd6199415f06e93ull }, + { 0x3fe6b020c49ba5e3ull, 0xbfd602741b7804b4ull }, + { 0x3fe6b851eb851eb8ull, 0xbfd5eb5c7907e4caull }, + { 0x3fe6c083126e978dull, 0xbfd5d44d289cca80ull }, + { 0x3fe6c8b439581062ull, 0xbfd5bd462439ef06ull }, + { 0x3fe6d0e560418937ull, 0xbfd5a64765e8ffb5ull }, + { 0x3fe6d916872b020cull, 0xbfd58f50e7ba14d1ull }, + { 0x3fe6e147ae147ae1ull, 0xbfd57862a3c3a852ull }, + { 0x3fe6e978d4fdf3b6ull, 0xbfd5617c94228cc7ull }, + { 0x3fe6f1a9fbe76c8bull, 0xbfd54a9eb2f9e441ull }, + { 0x3fe6f9db22d0e560ull, 0xbfd533c8fa731752ull }, + { 0x3fe7020c49ba5e35ull, 0xbfd51cfb64bdcc1aull }, + { 0x3fe70a3d70a3d70aull, 0xbfd50635ec0fdd69ull }, + { 0x3fe7126e978d4fdfull, 0xbfd4ef788aa551e9ull }, + { 0x3fe71a9fbe76c8b4ull, 0xbfd4d8c33ac0535full }, + { 0x3fe722d0e5604189ull, 0xbfd4c215f6a925f7ull }, + { 0x3fe72b020c49ba5eull, 0xbfd4ab70b8ae1f9eull }, + { 0x3fe7333333333333ull, 0xbfd494d37b239f74ull }, + { 0x3fe73b645a1cac08ull, 0xbfd47e3e38640541ull }, + { 0x3fe74395810624ddull, 0xbfd467b0eacfa902ull }, + { 0x3fe74bc6a7ef9db2ull, 0xbfd4512b8cccd284ull }, + { 0x3fe753f7ced91687ull, 0xbfd43aae18c7b107ull }, + { 0x3fe75c28f5c28f5cull, 0xbfd42438893252f5ull }, + { 0x3fe7645a1cac0831ull, 0xbfd40dcad8849daeull }, + { 0x3fe76c8b43958106ull, 0xbfd3f765013c454full }, + { 0x3fe774bc6a7ef9dbull, 0xbfd3e106fddcc4a1ull }, + { 0x3fe77ced916872b0ull, 0xbfd3cab0c8ef54fdull }, + { 0x3fe7851eb851eb85ull, 0xbfd3b4625d02e652ull }, + { 0x3fe78d4fdf3b645aull, 0xbfd39e1bb4ac172cull }, + { 0x3fe795810624dd2full, 0xbfd387dcca852cd1ull }, + { 0x3fe79db22d0e5604ull, 0xbfd371a5992e0b64ull }, + { 0x3fe7a5e353f7ced9ull, 0xbfd35b761b4c2e1full }, + { 0x3fe7ae147ae147aeull, 0xbfd3454e4b8a9f90ull }, + { 0x3fe7b645a1cac083ull, 0xbfd32f2e2499f1efull }, + { 0x3fe7be76c8b43958ull, 0xbfd31915a1303773ull }, + { 0x3fe7c6a7ef9db22dull, 0xbfd30304bc08fac5ull }, + { 0x3fe7ced916872b02ull, 0xbfd2ecfb6fe5376full }, + { 0x3fe7d70a3d70a3d7ull, 0xbfd2d6f9b78b5268ull }, + { 0x3fe7df3b645a1cacull, 0xbfd2c0ff8dc7129dull }, + { 0x3fe7e76c8b439581ull, 0xbfd2ab0ced699996ull }, + { 0x3fe7ef9db22d0e56ull, 0xbfd29521d1495c1bull }, + { 0x3fe7f7ced916872bull, 0xbfd27f3e34421af0ull }, + { 0x3fe8000000000000ull, 0xbfd269621134db92ull }, + { 0x3fe8083126e978d5ull, 0xbfd2538d6307e10full }, + { 0x3fe810624dd2f1aaull, 0xbfd23dc024a6a4d8ull }, + { 0x3fe8189374bc6a7full, 0xbfd227fa5101cfb3ull }, + { 0x3fe820c49ba5e354ull, 0xbfd2123be30f32a8ull }, + { 0x3fe828f5c28f5c29ull, 0xbfd1fc84d5c9c006ull }, + { 0x3fe83126e978d4feull, 0xbfd1e6d52431846bull }, + { 0x3fe8395810624dd3ull, 0xbfd1d12cc94b9fdfull }, + { 0x3fe84189374bc6a8ull, 0xbfd1bb8bc0223ef5ull }, + { 0x3fe849ba5e353f7dull, 0xbfd1a5f203c493fcull }, + { 0x3fe851eb851eb852ull, 0xbfd1905f8f46d038ull }, + { 0x3fe85a1cac083127ull, 0xbfd17ad45dc21d28ull }, + { 0x3fe8624dd2f1a9fcull, 0xbfd165506a5495daull }, + { 0x3fe86a7ef9db22d1ull, 0xbfd14fd3b0214042ull }, + { 0x3fe872b020c49ba6ull, 0xbfd13a5e2a5006a7ull }, + { 0x3fe87ae147ae147bull, 0xbfd124efd40db113ull }, + { 0x3fe883126e978d50ull, 0xbfd10f88a88bded0ull }, + { 0x3fe88b4395810625ull, 0xbfd0fa28a300fff1ull }, + { 0x3fe89374bc6a7efaull, 0xbfd0e4cfbea84ee6ull }, + { 0x3fe89ba5e353f7cfull, 0xbfd0cf7df6c1ca1bull }, + { 0x3fe8a3d70a3d70a4ull, 0xbfd0ba3346922d9dull }, + { 0x3fe8ac083126e979ull, 0xbfd0a4efa962eccfull }, + { 0x3fe8b4395810624eull, 0xbfd08fb31a822c28ull }, + { 0x3fe8bc6a7ef9db23ull, 0xbfd07a7d9542bafbull }, + { 0x3fe8c49ba5e353f8ull, 0xbfd0654f14fc0d48ull }, + { 0x3fe8cccccccccccdull, 0xbfd05027950a3599ull }, + { 0x3fe8d4fdf3b645a2ull, 0xbfd03b0710cddee8ull }, + { 0x3fe8dd2f1a9fbe77ull, 0xbfd025ed83ac4692ull }, + { 0x3fe8e5604189374cull, 0xbfd010dae90f364eull }, + { 0x3fe8ed916872b021ull, 0xbfcff79e78c9fc65ull }, + { 0x3fe8f5c28f5c28f6ull, 0xbfcfcd94f240dd8eull }, + { 0x3fe8fdf3b645a1cbull, 0xbfcfa3993571a62dull }, + { 0x3fe90624dd2f1aa0ull, 0xbfcf79ab3953d5adull }, + { 0x3fe90e5604189375ull, 0xbfcf4fcaf4e7c91dull }, + { 0x3fe916872b020c4aull, 0xbfcf25f85f36af9full }, + { 0x3fe91eb851eb851full, 0xbfcefc336f527edfull }, + { 0x3fe926e978d4fdf4ull, 0xbfced27c1c55e7a8ull }, + { 0x3fe92f1a9fbe76c9ull, 0xbfcea8d25d644a84ull }, + { 0x3fe9374bc6a7ef9eull, 0xbfce7f3629a9ac6dull }, + { 0x3fe93f7ced916873ull, 0xbfce55a7785aab99ull }, + { 0x3fe947ae147ae148ull, 0xbfce2c2640b4744full }, + { 0x3fe94fdf3b645a1dull, 0xbfce02b279fcb5d5ull }, + { 0x3fe95810624dd2f2ull, 0xbfcdd94c1b81976bull }, + { 0x3fe9604189374bc7ull, 0xbfcdaff31c99ad5bull }, + { 0x3fe96872b020c49cull, 0xbfcd86a774a3ee16ull }, + { 0x3fe970a3d70a3d71ull, 0xbfcd5d691b07a76bull }, + { 0x3fe978d4fdf3b646ull, 0xbfcd3438073473c8ull }, + { 0x3fe9810624dd2f1bull, 0xbfcd0b1430a22f8dull }, + { 0x3fe989374bc6a7f0ull, 0xbfcce1fd8ed0ee73ull }, + { 0x3fe9916872b020c5ull, 0xbfccb8f41948f104ull }, + { 0x3fe999999999999aull, 0xbfcc8ff7c79a9a20ull }, + { 0x3fe9a1cac083126full, 0xbfcc6708915e6496ull }, + { 0x3fe9a9fbe76c8b44ull, 0xbfcc3e266e34d8cfull }, + { 0x3fe9b22d0e560419ull, 0xbfcc155155c68285ull }, + { 0x3fe9ba5e353f7ceeull, 0xbfcbec893fc3e68cull }, + { 0x3fe9c28f5c28f5c3ull, 0xbfcbc3ce23e578b0ull }, + { 0x3fe9cac083126e98ull, 0xbfcb9b1ff9eb919bull }, + { 0x3fe9d2f1a9fbe76dull, 0xbfcb727eb99e64d0ull }, + { 0x3fe9db22d0e56042ull, 0xbfcb49ea5acdf6b8ull }, + { 0x3fe9e353f7ced917ull, 0xbfcb2162d55212b8ull }, + { 0x3fe9eb851eb851ecull, 0xbfcaf8e8210a415bull }, + { 0x3fe9f3b645a1cac1ull, 0xbfcad07a35ddbe8eull }, + { 0x3fe9fbe76c8b4396ull, 0xbfcaa8190bbb6fe6ull }, + { 0x3fea04189374bc6bull, 0xbfca7fc49a99daf6ull }, + { 0x3fea0c49ba5e3540ull, 0xbfca577cda771bbaull }, + { 0x3fea147ae147ae15ull, 0xbfca2f41c358db0aull }, + { 0x3fea1cac083126eaull, 0xbfca07134d4c4521ull }, + { 0x3fea24dd2f1a9fbfull, 0xbfc9def17066002dull }, + { 0x3fea2d0e56041894ull, 0xbfc9b6dc24c222f8ull }, + { 0x3fea353f7ced9169ull, 0xbfc98ed362842b92ull }, + { 0x3fea3d70a3d70a3eull, 0xbfc966d721d6f616ull }, + { 0x3fea45a1cac08313ull, 0xbfc93ee75aecb377ull }, + { 0x3fea4dd2f1a9fbe8ull, 0xbfc9170405fee05aull }, + { 0x3fea5604189374bdull, 0xbfc8ef2d1b4e3c04ull }, + { 0x3fea5e353f7ced92ull, 0xbfc8c7629322bf52ull }, + { 0x3fea666666666667ull, 0xbfc89fa465cb93bfull }, + { 0x3fea6e978d4fdf3cull, 0xbfc877f28b9f0a77ull }, + { 0x3fea76c8b4395811ull, 0xbfc8504cfcfa9380ull }, + { 0x3fea7ef9db22d0e6ull, 0xbfc828b3b242b4e2ull }, + { 0x3fea872b020c49bbull, 0xbfc80126a3e301ebull }, + { 0x3fea8f5c28f5c290ull, 0xbfc7d9a5ca4e127aull }, + { 0x3fea978d4fdf3b65ull, 0xbfc7b2311dfd7a52ull }, + { 0x3fea9fbe76c8b43aull, 0xbfc78ac89771c088ull }, + { 0x3feaa7ef9db22d0eull, 0xbfc7636c2f3256f8ull }, + { 0x3feab020c49ba5e3ull, 0xbfc73c1bddcd91afull }, + { 0x3feab851eb851eb8ull, 0xbfc714d79bd89e9bull }, + { 0x3feac083126e978dull, 0xbfc6ed9f61ef7d0eull }, + { 0x3feac8b439581062ull, 0xbfc6c67328b4f56dull }, + { 0x3fead0e560418937ull, 0xbfc69f52e8d290e2ull }, + { 0x3fead916872b020cull, 0xbfc6783e9af8911full }, + { 0x3feae147ae147ae1ull, 0xbfc6513637dde829ull }, + { 0x3feae978d4fdf3b6ull, 0xbfc62a39b8403032ull }, + { 0x3feaf1a9fbe76c8bull, 0xbfc6034914e3a383ull }, + { 0x3feaf9db22d0e560ull, 0xbfc5dc6446931468ull }, + { 0x3feb020c49ba5e35ull, 0xbfc5b58b461fe536ull }, + { 0x3feb0a3d70a3d70aull, 0xbfc58ebe0c62004full }, + { 0x3feb126e978d4fdfull, 0xbfc567fc9237d03eull }, + { 0x3feb1a9fbe76c8b4ull, 0xbfc54146d08637d8ull }, + { 0x3feb22d0e5604189ull, 0xbfc51a9cc0388a6bull }, + { 0x3feb2b020c49ba5eull, 0xbfc4f3fe5a4083f8ull }, + { 0x3feb333333333333ull, 0xbfc4cd6b97964179ull }, + { 0x3feb3b645a1cac08ull, 0xbfc4a6e471383935ull }, + { 0x3feb4395810624ddull, 0xbfc48068e02b331cull }, + { 0x3feb4bc6a7ef9db2ull, 0xbfc459f8dd7a412full }, + { 0x3feb53f7ced91687ull, 0xbfc433946236b7f6ull }, + { 0x3feb5c28f5c28f5cull, 0xbfc40d3b677826fdull }, + { 0x3feb645a1cac0831ull, 0xbfc3e6ede65c5165ull }, + { 0x3feb6c8b43958106ull, 0xbfc3c0abd8072670ull }, + { 0x3feb74bc6a7ef9dbull, 0xbfc39a7535a2ba2cull }, + { 0x3feb7ced916872b0ull, 0xbfc37449f85f3e1aull }, + { 0x3feb851eb851eb85ull, 0xbfc34e2a1972f9e3ull }, + { 0x3feb8d4fdf3b645aull, 0xbfc32815921a441full }, + { 0x3feb95810624dd2full, 0xbfc3020c5b977b1eull }, + { 0x3feb9db22d0e5604ull, 0xbfc2dc0e6f32fdc2ull }, + { 0x3feba5e353f7ced9ull, 0xbfc2b61bc63b245bull }, + { 0x3febae147ae147aeull, 0xbfc290345a04399aull }, + { 0x3febb645a1cac083ull, 0xbfc26a5823e87386ull }, + { 0x3febbe76c8b43958ull, 0xbfc244871d47ec7aull }, + { 0x3febc6a7ef9db22dull, 0xbfc21ec13f889c35ull }, + { 0x3febced916872b02ull, 0xbfc1f906841650edull }, + { 0x3febd70a3d70a3d7ull, 0xbfc1d356e462a870ull }, + { 0x3febdf3b645a1cacull, 0xbfc1adb259e5094dull }, + { 0x3febe76c8b439581ull, 0xbfc18818de1a9c09ull }, + { 0x3febef9db22d0e56ull, 0xbfc1628a6a86445full }, + { 0x3febf7ced916872bull, 0xbfc13d06f8b09a84ull }, + { 0x3fec000000000000ull, 0xbfc1178e8227e47cull }, + { 0x3fec083126e978d5ull, 0xbfc0f22100800f74ull }, + { 0x3fec10624dd2f1aaull, 0xbfc0ccbe6d52a928ull }, + { 0x3fec189374bc6a7full, 0xbfc0a766c23ed951ull }, + { 0x3fec20c49ba5e354ull, 0xbfc08219f8e95b21ull }, + { 0x3fec28f5c28f5c29ull, 0xbfc05cd80afc76beull }, + { 0x3fec3126e978d4feull, 0xbfc037a0f227fad6ull }, + { 0x3fec395810624dd3ull, 0xbfc01274a821362dull }, + { 0x3fec4189374bc6a8ull, 0xbfbfdaa64d45e27bull }, + { 0x3fec49ba5e353f7dull, 0xbfbf9078cedacfc3ull }, + { 0x3fec51eb851eb852ull, 0xbfbf4660c88c8603ull }, + { 0x3fec5a1cac083127ull, 0xbfbefc5e2df12499ull }, + { 0x3fec624dd2f1a9fcull, 0xbfbeb270f2a98bfdull }, + { 0x3fec6a7ef9db22d1ull, 0xbfbe68990a615153ull }, + { 0x3fec72b020c49ba6ull, 0xbfbe1ed668ceb215ull }, + { 0x3fec7ae147ae147bull, 0xbfbdd52901b287d1ull }, + { 0x3fec83126e978d50ull, 0xbfbd8b90c8d83bf3ull }, + { 0x3fec8b4395810625ull, 0xbfbd420db215bba3ull }, + { 0x3fec9374bc6a7efaull, 0xbfbcf89fb14b6bb6ull }, + { 0x3fec9ba5e353f7cfull, 0xbfbcaf46ba641cb0ull }, + { 0x3feca3d70a3d70a4ull, 0xbfbc6602c154fed4ull }, + { 0x3fecac083126e979ull, 0xbfbc1cd3ba1d9647ull }, + { 0x3fecb4395810624eull, 0xbfbbd3b998c7af45ull }, + { 0x3fecbc6a7ef9db23ull, 0xbfbb8ab451675268ull }, + { 0x3fecc49ba5e353f8ull, 0xbfbb41c3d81ab8faull }, + { 0x3feccccccccccccdull, 0xbfbaf8e8210a415cull }, + { 0x3fecd4fdf3b645a2ull, 0xbfbab0212068637full }, + { 0x3fecdd2f1a9fbe77ull, 0xbfba676eca71a569ull }, + { 0x3fece5604189374cull, 0xbfba1ed1136c8fceull }, + { 0x3feced916872b021ull, 0xbfb9d647efa9a2b4ull }, + { 0x3fecf5c28f5c28f6ull, 0xbfb98dd353834a30ull }, + { 0x3fecfdf3b645a1cbull, 0xbfb94573335dd329ull }, + { 0x3fed0624dd2f1aa0ull, 0xbfb8fd2783a76031ull }, + { 0x3fed0e5604189375ull, 0xbfb8b4f038d7de6eull }, + { 0x3fed16872b020c4aull, 0xbfb86ccd4770fa8cull }, + { 0x3fed1eb851eb851full, 0xbfb824bea3fe15c9ull }, + { 0x3fed26e978d4fdf4ull, 0xbfb7dcc443143b07ull }, + { 0x3fed2f1a9fbe76c9ull, 0xbfb794de195213f0ull }, + { 0x3fed374bc6a7ef9eull, 0xbfb74d0c1b5fde30ull }, + { 0x3fed3f7ced916873ull, 0xbfb7054e3def60b2ull }, + { 0x3fed47ae147ae148ull, 0xbfb6bda475bbe0f6ull }, + { 0x3fed4fdf3b645a1dull, 0xbfb6760eb78a1871ull }, + { 0x3fed5810624dd2f2ull, 0xbfb62e8cf82829ffull }, + { 0x3fed604189374bc7ull, 0xbfb5e71f2c6d975eull }, + { 0x3fed6872b020c49cull, 0xbfb59fc5493b36c2ull }, + { 0x3fed70a3d70a3d71ull, 0xbfb5587f437b2869ull }, + { 0x3fed78d4fdf3b646ull, 0xbfb5114d1020cc4dull }, + { 0x3fed810624dd2f1bull, 0xbfb4ca2ea428b7d9ull }, + { 0x3fed89374bc6a7f0ull, 0xbfb48323f498abb1ull }, + { 0x3fed916872b020c5ull, 0xbfb43c2cf67f8985ull }, + { 0x3fed99999999999aull, 0xbfb3f5499ef549f9ull }, + { 0x3feda1cac083126full, 0xbfb3ae79e31af295ull }, + { 0x3feda9fbe76c8b44ull, 0xbfb367bdb81a8bc1ull }, + { 0x3fedb22d0e560419ull, 0xbfb32115132716d5ull }, + { 0x3fedba5e353f7ceeull, 0xbfb2da7fe97c8435ull }, + { 0x3fedc28f5c28f5c3ull, 0xbfb293fe305fa975ull }, + { 0x3fedcac083126e98ull, 0xbfb24d8fdd1e3794ull }, + { 0x3fedd2f1a9fbe76dull, 0xbfb20734e50eb13dull }, + { 0x3feddb22d0e56042ull, 0xbfb1c0ed3d906115ull }, + { 0x3fede353f7ced917ull, 0xbfb17ab8dc0b5020ull }, + { 0x3fedeb851eb851ecull, 0xbfb13497b5f03c25ull }, + { 0x3fedf3b645a1cac1ull, 0xbfb0ee89c0b88e2dull }, + { 0x3fedfbe76c8b4396ull, 0xbfb0a88ef1e65102ull }, + { 0x3fee04189374bc6bull, 0xbfb062a73f0427c6ull }, + { 0x3fee0c49ba5e3540ull, 0xbfb01cd29da5448full }, + { 0x3fee147ae147ae15ull, 0xbfafae2206cabe2full }, + { 0x3fee1cac083126eaull, 0xbfaf22c4cbd156e3ull }, + { 0x3fee24dd2f1a9fbfull, 0xbfae978d75b7a19full }, + { 0x3fee2d0e56041894ull, 0xbfae0c7befe7c0acull }, + { 0x3fee353f7ced9169ull, 0xbfad819025dc97f0ull }, + { 0x3fee3d70a3d70a3eull, 0xbfacf6ca0321bac8ull }, + { 0x3fee45a1cac08313ull, 0xbfac6c29735359f2ull }, + { 0x3fee4dd2f1a9fbe8ull, 0xbfabe1ae621e3195ull }, + { 0x3fee5604189374bdull, 0xbfab5758bb3f775full }, + { 0x3fee5e353f7ced92ull, 0xbfaacd286a84c8bfull }, + { 0x3fee666666666667ull, 0xbfaa431d5bcc192dull }, + { 0x3fee6e978d4fdf3cull, 0xbfa9b9377b03a099ull }, + { 0x3fee76c8b4395811ull, 0xbfa92f76b429c9dfull }, + { 0x3fee7ef9db22d0e6ull, 0xbfa8a5daf34d2169ull }, + { 0x3fee872b020c49bbull, 0xbfa81c64248c43d1ull }, + { 0x3fee8f5c28f5c290ull, 0xbfa793123415ccacull }, + { 0x3fee978d4fdf3b65ull, 0xbfa709e50e284564ull }, + { 0x3fee9fbe76c8b43aull, 0xbfa680dc9f121427ull }, + { 0x3feea7ef9db22d0full, 0xbfa5f7f8d3316af7ull }, + { 0x3feeb020c49ba5e4ull, 0xbfa56f3996f436c3ull }, + { 0x3feeb851eb851eb8ull, 0xbfa4e69ed6d80eb3ull }, + { 0x3feec083126e978dull, 0xbfa45e287f6a232full }, + { 0x3feec8b439581062ull, 0xbfa3d5d67d472da6ull }, + { 0x3feed0e560418937ull, 0xbfa34da8bd1b5fc8ull }, + { 0x3feed916872b020cull, 0xbfa2c59f2ba25321ull }, + { 0x3feee147ae147ae1ull, 0xbfa23db9b5a6f8c1ull }, + { 0x3feee978d4fdf3b6ull, 0xbfa1b5f8480388f4ull }, + { 0x3feef1a9fbe76c8bull, 0xbfa12e5acfa17314ull }, + { 0x3feef9db22d0e560ull, 0xbfa0a6e139794d70ull }, + { 0x3fef020c49ba5e35ull, 0xbfa01f8b7292c543ull }, + { 0x3fef0a3d70a3d70aull, 0xbf9f30b2d0091d8aull }, + { 0x3fef126e978d4fdfull, 0xbf9e22960de8aaa3ull }, + { 0x3fef1a9fbe76c8b4ull, 0xbf9d14c0792d5741ull }, + { 0x3fef22d0e5604189ull, 0xbf9c0731ec5df82aull }, + { 0x3fef2b020c49ba5eull, 0xbf9af9ea421ef240ull }, + { 0x3fef333333333333ull, 0xbf99ece955321b70ull }, + { 0x3fef3b645a1cac08ull, 0xbf98e02f00769bceull }, + { 0x3fef4395810624ddull, 0xbf97d3bb1ee8ced0ull }, + { 0x3fef4bc6a7ef9db2ull, 0xbf96c78d8ba224bcull }, + { 0x3fef53f7ced91687ull, 0xbf95bba621d90435ull }, + { 0x3fef5c28f5c28f5cull, 0xbf94b004bce0abf7ull }, + { 0x3fef645a1cac0831ull, 0xbf93a4a9382914b6ull }, + { 0x3fef6c8b43958106ull, 0xbf9299936f3ed324ull }, + { 0x3fef74bc6a7ef9dbull, 0xbf918ec33dcafa23ull }, + { 0x3fef7ced916872b0ull, 0xbf9084387f92fd19ull }, + { 0x3fef851eb851eb85ull, 0xbf8ef3e620f124d2ull }, + { 0x3fef8d4fdf3b645aull, 0xbf8cdfe598f32c36ull }, + { 0x3fef95810624dd2full, 0xbf8acc6f1f5fd940ull }, + { 0x3fef9db22d0e5604ull, 0xbf88b9826ca2cb84ull }, + { 0x3fefa5e353f7ced9ull, 0xbf86a71f395f3f6full }, + { 0x3fefae147ae147aeull, 0xbf8495453e6fd4bcull }, + { 0x3fefb645a1cac083ull, 0xbf8283f434e6552eull }, + { 0x3fefbe76c8b43958ull, 0xbf80732bd60b7b99ull }, + { 0x3fefc6a7ef9db22dull, 0xbf7cc5d7b6bd7664ull }, + { 0x3fefced916872b02ull, 0xbf78a667fd2c0e51ull }, + { 0x3fefd70a3d70a3d7ull, 0xbf748807f33b3513ull }, + { 0x3fefdf3b645a1cacull, 0xbf706ab70d2f80d0ull }, + { 0x3fefe76c8b439581ull, 0xbf689ce97f727142ull }, + { 0x3fefef9db22d0e56ull, 0xbf606680ffe7cc09ull }, + { 0x3feff7ced916872bull, 0xbf5064670d979b73ull }, + { 0x3ff0000000000000ull, 0x0000000000000000ull }, + { 0x3ff004189374bc6bull, 0x3f5060354f8c40c3ull }, + { 0x3ff0083126e978d5ull, 0x3f605e1d82fdf749ull }, + { 0x3ff00c49ba5e3540ull, 0x3f688a09a34cabd4ull }, + { 0x3ff010624dd2f1aaull, 0x3f7059f00cc2f7fcull }, + { 0x3ff0147ae147ae15ull, 0x3f746dd0fad672f8ull }, + { 0x3ff0189374bc6a7full, 0x3f7880a8237a7261ull }, + { 0x3ff01cac083126eaull, 0x3f7c92760de12dc2ull }, + { 0x3ff020c49ba5e354ull, 0x3f80519da06ae4f3ull }, + { 0x3ff024dd2f1a9fbfull, 0x3f82597c215e64b5ull }, + { 0x3ff028f5c28f5c29ull, 0x3f8460d6ccca367cull }, + { 0x3ff02d0e56041894ull, 0x3f8667ade57a9160ull }, + { 0x3ff03126e978d4feull, 0x3f886e01ae08f2d9ull }, + { 0x3ff0353f7ced9169ull, 0x3f8a73d268dc55fdull }, + { 0x3ff0395810624dd3ull, 0x3f8c792058296295ull }, + { 0x3ff03d70a3d70a3eull, 0x3f8e7debbdf2a3dbull }, + { 0x3ff04189374bc6a8ull, 0x3f90411a6e045b8aull }, + { 0x3ff045a1cac08313ull, 0x3f9142fdfa0540e4ull }, + { 0x3ff049ba5e353f7dull, 0x3f9244a0a3b2afeeull }, + { 0x3ff04dd2f1a9fbe8ull, 0x3f9346028baaacaaull }, + { 0x3ff051eb851eb852ull, 0x3f944723d272a7f6ull }, + { 0x3ff05604189374bdull, 0x3f95480498779a2bull }, + { 0x3ff05a1cac083127ull, 0x3f9648a4fe0e19b0ull }, + { 0x3ff05e353f7ced92ull, 0x3f9749052372755bull }, + { 0x3ff0624dd2f1a9fcull, 0x3f98492528c8cac5ull }, + { 0x3ff0666666666666ull, 0x3f9949052e1d202eull }, + { 0x3ff06a7ef9db22d1ull, 0x3f9a48a553637bd3ull }, + { 0x3ff06e978d4fdf3bull, 0x3f9b4805b877fb21ull }, + { 0x3ff072b020c49ba6ull, 0x3f9c47267d1eec3eull }, + { 0x3ff076c8b4395810ull, 0x3f9d4607c104e3c9ull }, + { 0x3ff07ae147ae147bull, 0x3f9e44a9a3bed66bull }, + { 0x3ff07ef9db22d0e5ull, 0x3f9f430c44ca2e59ull }, + { 0x3ff083126e978d50ull, 0x3fa02097e1c67254ull }, + { 0x3ff0872b020c49baull, 0x3fa09f8a1faacb4dull }, + { 0x3ff08b4395810625ull, 0x3fa11e5cebadcf5aull }, + { 0x3ff08f5c28f5c28full, 0x3fa19d10555f94e8ull }, + { 0x3ff09374bc6a7efaull, 0x3fa21ba46c44a801ull }, + { 0x3ff0978d4fdf3b64ull, 0x3fa29a193fd614b8ull }, + { 0x3ff09ba5e353f7cfull, 0x3fa3186edf817377ull }, + { 0x3ff09fbe76c8b439ull, 0x3fa396a55aa8f35aull }, + { 0x3ff0a3d70a3d70a4ull, 0x3fa414bcc0a3665dull }, + { 0x3ff0a7ef9db22d0eull, 0x3fa492b520bc4b99ull }, + { 0x3ff0ac083126e979ull, 0x3fa5108e8a33db58ull }, + { 0x3ff0b020c49ba5e3ull, 0x3fa58e490c3f1137ull }, + { 0x3ff0b4395810624eull, 0x3fa60be4b607b81full }, + { 0x3ff0b851eb851eb8ull, 0x3fa6896196ac744cull }, + { 0x3ff0bc6a7ef9db23ull, 0x3fa706bfbd40cf29ull }, + { 0x3ff0c083126e978dull, 0x3fa783ff38cd413eull }, + { 0x3ff0c49ba5e353f8ull, 0x3fa80120184f3df2ull }, + { 0x3ff0c8b439581062ull, 0x3fa87e226ab93d5dull }, + { 0x3ff0cccccccccccdull, 0x3fa8fb063ef2c7f0ull }, + { 0x3ff0d0e560418937ull, 0x3fa977cba3d8802bull }, + { 0x3ff0d4fdf3b645a2ull, 0x3fa9f472a83c2e32ull }, + { 0x3ff0d916872b020cull, 0x3faa70fb5ae4c960ull }, + { 0x3ff0dd2f1a9fbe77ull, 0x3faaed65ca8e83c8ull }, + { 0x3ff0e147ae147ae1ull, 0x3fab69b205ead3adull }, + { 0x3ff0e5604189374cull, 0x3fabe5e01ba07ee6ull }, + { 0x3ff0e978d4fdf3b6ull, 0x3fac61f01a4ba446ull }, + { 0x3ff0ed916872b021ull, 0x3facdde2107dc6ddull }, + { 0x3ff0f1a9fbe76c8bull, 0x3fad59b60cbdd74aull }, + { 0x3ff0f5c28f5c28f6ull, 0x3fadd56c1d883ee6ull }, + { 0x3ff0f9db22d0e560ull, 0x3fae5104514ee8fdull }, + { 0x3ff0fdf3b645a1cbull, 0x3faecc7eb6794ddaull }, + { 0x3ff1020c49ba5e35ull, 0x3faf47db5b647befull }, + { 0x3ff10624dd2f1aa0ull, 0x3fafc31a4e6322c7ull }, + { 0x3ff10a3d70a3d70aull, 0x3fb01f1dcedece09ull }, + { 0x3ff10e5604189375ull, 0x3fb05c9fabd8fb42ull }, + { 0x3ff1126e978d4fdfull, 0x3fb09a12c539ff63ull }, + { 0x3ff116872b020c4aull, 0x3fb0d7772216a51eull }, + { 0x3ff11a9fbe76c8b4ull, 0x3fb114ccc97e9ff2ull }, + { 0x3ff11eb851eb851full, 0x3fb15213c27c9180ull }, + { 0x3ff122d0e5604189ull, 0x3fb18f4c14160deeull }, + { 0x3ff126e978d4fdf4ull, 0x3fb1cc75c54ba132ull }, + { 0x3ff12b020c49ba5eull, 0x3fb20990dd18d36aull }, + { 0x3ff12f1a9fbe76c9ull, 0x3fb2469d62742e1bull }, + { 0x3ff1333333333333ull, 0x3fb2839b5c4f407cull }, + { 0x3ff1374bc6a7ef9eull, 0x3fb2c08ad196a4aaull }, + { 0x3ff13b645a1cac08ull, 0x3fb2fd6bc93203e9ull }, + { 0x3ff13f7ced916873ull, 0x3fb33a3e4a041bcfull }, + { 0x3ff14395810624ddull, 0x3fb377025aeac274ull }, + { 0x3ff147ae147ae148ull, 0x3fb3b3b802beeb94ull }, + { 0x3ff14bc6a7ef9db2ull, 0x3fb3f05f4854acb9ull }, + { 0x3ff14fdf3b645a1dull, 0x3fb42cf8327b424bull }, + { 0x3ff153f7ced91687ull, 0x3fb46982c7fd13b2ull }, + { 0x3ff15810624dd2f2ull, 0x3fb4a5ff0f9fb85eull }, + { 0x3ff15c28f5c28f5cull, 0x3fb4e26d1023fbd7ull }, + { 0x3ff1604189374bc7ull, 0x3fb51eccd045e2c0ull }, + { 0x3ff1645a1cac0831ull, 0x3fb55b1e56bcaeddull }, + { 0x3ff16872b020c49cull, 0x3fb59761aa3ae408ull }, + { 0x3ff16c8b43958106ull, 0x3fb5d396d16e4c2eull }, + { 0x3ff170a3d70a3d71ull, 0x3fb60fbdd2fffc37ull }, + { 0x3ff174bc6a7ef9dbull, 0x3fb64bd6b59457fcull }, + { 0x3ff178d4fdf3b646ull, 0x3fb687e17fcb1726ull }, + { 0x3ff17ced916872b0ull, 0x3fb6c3de383f4915ull }, + { 0x3ff1810624dd2f1bull, 0x3fb6ffcce58759b3ull }, + { 0x3ff1851eb851eb85ull, 0x3fb73bad8e35155aull }, + { 0x3ff189374bc6a7f0ull, 0x3fb7778038d5ad96ull }, + { 0x3ff18d4fdf3b645aull, 0x3fb7b344ebf1bd00ull }, + { 0x3ff1916872b020c5ull, 0x3fb7eefbae0d4bfaull }, + { 0x3ff195810624dd2full, 0x3fb82aa485a7d47dull }, + { 0x3ff199999999999aull, 0x3fb8663f793c46ccull }, + { 0x3ff19db22d0e5604ull, 0x3fb8a1cc8f410d3cull }, + { 0x3ff1a1cac083126full, 0x3fb8dd4bce2810d4ull }, + { 0x3ff1a5e353f7ced9ull, 0x3fb918bd3c5ebd13ull }, + { 0x3ff1a9fbe76c8b44ull, 0x3fb95420e04e0486ull }, + { 0x3ff1ae147ae147aeull, 0x3fb98f76c05a647eull }, + { 0x3ff1b22d0e560419ull, 0x3fb9cabee2e3e9a4ull }, + { 0x3ff1b645a1cac083ull, 0x3fba05f94e4633a0ull }, + { 0x3ff1ba5e353f7ceeull, 0x3fba412608d879a6ull }, + { 0x3ff1be76c8b43958ull, 0x3fba7c4518ed8e15ull }, + { 0x3ff1c28f5c28f5c3ull, 0x3fbab75684d3e2f6ull }, + { 0x3ff1c6a7ef9db22dull, 0x3fbaf25a52d58d95ull }, + { 0x3ff1cac083126e98ull, 0x3fbb2d5089384af5ull }, + { 0x3ff1ced916872b02ull, 0x3fbb68392e3d8364ull }, + { 0x3ff1d2f1a9fbe76dull, 0x3fbba31448224edeull }, + { 0x3ff1d70a3d70a3d7ull, 0x3fbbdde1dd1f789full }, + { 0x3ff1db22d0e56042ull, 0x3fbc18a1f369837aull }, + { 0x3ff1df3b645a1cacull, 0x3fbc53549130ad5full }, + { 0x3ff1e353f7ced917ull, 0x3fbc8df9bca0f3b2ull }, + { 0x3ff1e76c8b439581ull, 0x3fbcc8917be216bcull }, + { 0x3ff1eb851eb851ecull, 0x3fbd031bd5179e02ull }, + { 0x3ff1ef9db22d0e56ull, 0x3fbd3d98ce60dbabull }, + { 0x3ff1f3b645a1cac1ull, 0x3fbd78086dd8f0caull }, + { 0x3ff1f7ced916872bull, 0x3fbdb26ab996d0c2ull }, + { 0x3ff1fbe76c8b4396ull, 0x3fbdecbfb7ad4583ull }, + { 0x3ff2000000000000ull, 0x3fbe27076e2af2e6ull }, + { 0x3ff204189374bc6bull, 0x3fbe6141e31a5ae1ull }, + { 0x3ff2083126e978d5ull, 0x3fbe9b6f1c81e0dbull }, + { 0x3ff20c49ba5e3540ull, 0x3fbed58f2063cdd8ull }, + { 0x3ff210624dd2f1aaull, 0x3fbf0fa1f4be53c3ull }, + { 0x3ff2147ae147ae15ull, 0x3fbf49a79f8b9195ull }, + { 0x3ff2189374bc6a7full, 0x3fbf83a026c19690ull }, + { 0x3ff21cac083126eaull, 0x3fbfbd8b90526664ull }, + { 0x3ff220c49ba5e354ull, 0x3fbff769e22bfc60ull }, + { 0x3ff224dd2f1a9fbfull, 0x3fc0189d911c27c6ull }, + { 0x3ff228f5c28f5c29ull, 0x3fc0357fab2eaaeaull }, + { 0x3ff22d0e56041894ull, 0x3fc0525b423e840dull }, + { 0x3ff23126e978d4feull, 0x3fc06f30593ab2aeull }, + { 0x3ff2353f7ced9169ull, 0x3fc08bfef3103b5bull }, + { 0x3ff2395810624dd3ull, 0x3fc0a8c712aa2941ull }, + { 0x3ff23d70a3d70a3eull, 0x3fc0c588baf1902cull }, + { 0x3ff24189374bc6a8ull, 0x3fc0e243eecd8e13ull }, + { 0x3ff245a1cac08313ull, 0x3fc0fef8b1234d0eull }, + { 0x3ff249ba5e353f7dull, 0x3fc11ba704d604e4ull }, + { 0x3ff24dd2f1a9fbe8ull, 0x3fc1384eecc6fcfdull }, + { 0x3ff251eb851eb852ull, 0x3fc154f06bd58de8ull }, + { 0x3ff25604189374bdull, 0x3fc1718b84df234dull }, + { 0x3ff25a1cac083127ull, 0x3fc18e203abf3d6bull }, + { 0x3ff25e353f7ced92ull, 0x3fc1aaae904f730bull }, + { 0x3ff2624dd2f1a9fcull, 0x3fc1c736886772f9ull }, + { 0x3ff2666666666667ull, 0x3fc1e3b825dd05f0ull }, + { 0x3ff26a7ef9db22d1ull, 0x3fc200336b841012ull }, + { 0x3ff26e978d4fdf3cull, 0x3fc21ca85c2e92cfull }, + { 0x3ff272b020c49ba6ull, 0x3fc23916faacae5bull }, + { 0x3ff276c8b4395810ull, 0x3fc2557f49cca389ull }, + { 0x3ff27ae147ae147bull, 0x3fc271e14c5ad55eull }, + { 0x3ff27ef9db22d0e5ull, 0x3fc28e3d0521caa8ull }, + { 0x3ff283126e978d50ull, 0x3fc2aa9276ea2fcfull }, + { 0x3ff2872b020c49baull, 0x3fc2c6e1a47ad845ull }, + { 0x3ff28b4395810625ull, 0x3fc2e32a9098c05cull }, + { 0x3ff28f5c28f5c28full, 0x3fc2ff6d3e070eb3ull }, + { 0x3ff29374bc6a7efaull, 0x3fc31ba9af871608ull }, + { 0x3ff2978d4fdf3b64ull, 0x3fc337dfe7d856a0ull }, + { 0x3ff29ba5e353f7cfull, 0x3fc3540fe9b88018ull }, + { 0x3ff29fbe76c8b439ull, 0x3fc37039b7e372c4ull }, + { 0x3ff2a3d70a3d70a4ull, 0x3fc38c5d55134183ull }, + { 0x3ff2a7ef9db22d0eull, 0x3fc3a87ac4003319ull }, + { 0x3ff2ac083126e979ull, 0x3fc3c4920760c3fcull }, + { 0x3ff2b020c49ba5e3ull, 0x3fc3e0a321e9a7afull }, + { 0x3ff2b4395810624eull, 0x3fc3fcae164dca87ull }, + { 0x3ff2b851eb851eb8ull, 0x3fc418b2e73e5307ull }, + { 0x3ff2bc6a7ef9db23ull, 0x3fc434b1976aa39eull }, + { 0x3ff2c083126e978dull, 0x3fc450aa29805c02ull }, + { 0x3ff2c49ba5e353f8ull, 0x3fc46c9ca02b5aebull }, + { 0x3ff2c8b439581062ull, 0x3fc48888fe15bf67ull }, + { 0x3ff2cccccccccccdull, 0x3fc4a46f45e7ea95ull }, + { 0x3ff2d0e560418937ull, 0x3fc4c04f7a4880f6ull }, + { 0x3ff2d4fdf3b645a2ull, 0x3fc4dc299ddc6c23ull }, + { 0x3ff2d916872b020cull, 0x3fc4f7fdb346dc19ull }, + { 0x3ff2dd2f1a9fbe77ull, 0x3fc513cbbd2948f0ull }, + { 0x3ff2e147ae147ae1ull, 0x3fc52f93be237421ull }, + { 0x3ff2e5604189374cull, 0x3fc54b55b8d36a3cull }, + { 0x3ff2e978d4fdf3b6ull, 0x3fc56711afd58428ull }, + { 0x3ff2ed916872b021ull, 0x3fc582c7a5c468d3ull }, + { 0x3ff2f1a9fbe76c8bull, 0x3fc59e779d390e79ull }, + { 0x3ff2f5c28f5c28f6ull, 0x3fc5ba2198cabc49ull }, + { 0x3ff2f9db22d0e560ull, 0x3fc5d5c59b0f0ba7ull }, + { 0x3ff2fdf3b645a1cbull, 0x3fc5f163a699e9d3ull }, + { 0x3ff3020c49ba5e35ull, 0x3fc60cfbbdfd9929ull }, + { 0x3ff30624dd2f1aa0ull, 0x3fc6288de3cab2c0ull }, + { 0x3ff30a3d70a3d70aull, 0x3fc6441a1a9027a6ull }, + { 0x3ff30e5604189375ull, 0x3fc65fa064db4282ull }, + { 0x3ff3126e978d4fdfull, 0x3fc67b20c537a8caull }, + { 0x3ff316872b020c4aull, 0x3fc6969b3e2f5c62ull }, + { 0x3ff31a9fbe76c8b4ull, 0x3fc6b20fd24abcccull }, + { 0x3ff31eb851eb851full, 0x3fc6cd7e841088c8ull }, + { 0x3ff322d0e5604189ull, 0x3fc6e8e75605df80ull }, + { 0x3ff326e978d4fdf4ull, 0x3fc7044a4aae4225ull }, + { 0x3ff32b020c49ba5eull, 0x3fc71fa7648b9516ull }, + { 0x3ff32f1a9fbe76c9ull, 0x3fc73afea61e217eull }, + { 0x3ff3333333333333ull, 0x3fc7565011e49675ull }, + { 0x3ff3374bc6a7ef9eull, 0x3fc7719baa5c0a99ull }, + { 0x3ff33b645a1cac08ull, 0x3fc78ce171fffd34ull }, + { 0x3ff33f7ced916873ull, 0x3fc7a8216b4a57c9ull }, + { 0x3ff34395810624ddull, 0x3fc7c35b98b36f3cull }, + { 0x3ff347ae147ae148ull, 0x3fc7de8ffcb2055dull }, + { 0x3ff34bc6a7ef9db2ull, 0x3fc7f9be99bb4a09ull }, + { 0x3ff34fdf3b645a1dull, 0x3fc814e77242dcb5ull }, + { 0x3ff353f7ced91687ull, 0x3fc8300a88bacd8eull }, + { 0x3ff35810624dd2f2ull, 0x3fc84b27df939efdull }, + { 0x3ff35c28f5c28f5cull, 0x3fc8663f793c46c6ull }, + { 0x3ff3604189374bc7ull, 0x3fc8815158222f88ull }, + { 0x3ff3645a1cac0831ull, 0x3fc89c5d7eb139ddull }, + { 0x3ff36872b020c49cull, 0x3fc8b763ef53bdd5ull }, + { 0x3ff36c8b43958106ull, 0x3fc8d264ac728c10ull }, + { 0x3ff370a3d70a3d71ull, 0x3fc8ed5fb874ef3dull }, + { 0x3ff374bc6a7ef9dbull, 0x3fc9085515c0ad2bull }, + { 0x3ff378d4fdf3b646ull, 0x3fc92344c6ba0847ull }, + { 0x3ff37ced916872b0ull, 0x3fc93e2ecdc3c0afull }, + { 0x3ff3810624dd2f1bull, 0x3fc959132d3f15a6ull }, + { 0x3ff3851eb851eb85ull, 0x3fc973f1e78bc6a8ull }, + { 0x3ff389374bc6a7f0ull, 0x3fc98ecaff0814deull }, + { 0x3ff38d4fdf3b645aull, 0x3fc9a99e7610c429ull }, + { 0x3ff3916872b020c5ull, 0x3fc9c46c4f011c99ull }, + { 0x3ff395810624dd2full, 0x3fc9df348c32eb74ull }, + { 0x3ff399999999999aull, 0x3fc9f9f72ffe84a6ull }, + { 0x3ff39db22d0e5604ull, 0x3fca14b43cbac3ccull }, + { 0x3ff3a1cac083126full, 0x3fca2f6bb4bd0da0ull }, + { 0x3ff3a5e353f7ced9ull, 0x3fca4a1d9a5950fcull }, + { 0x3ff3a9fbe76c8b44ull, 0x3fca64c9efe20849ull }, + { 0x3ff3ae147ae147aeull, 0x3fca7f70b7a83a7full }, + { 0x3ff3b22d0e560419ull, 0x3fca9a11f3fb7c8eull }, + { 0x3ff3b645a1cac083ull, 0x3fcab4ada729f260ull }, + { 0x3ff3ba5e353f7ceeull, 0x3fcacf43d380503eull }, + { 0x3ff3be76c8b43958ull, 0x3fcae9d47b49dbcbull }, + { 0x3ff3c28f5c28f5c3ull, 0x3fcb045fa0d06d6dull }, + { 0x3ff3c6a7ef9db22dull, 0x3fcb1ee5465c7142ull }, + { 0x3ff3cac083126e98ull, 0x3fcb39656e34e888ull }, + { 0x3ff3ced916872b02ull, 0x3fcb53e01a9f6a91ull }, + { 0x3ff3d2f1a9fbe76dull, 0x3fcb6e554de02621ull }, + { 0x3ff3d70a3d70a3d7ull, 0x3fcb88c50a39e268ull }, + { 0x3ff3db22d0e56042ull, 0x3fcba32f51ee005eull }, + { 0x3ff3df3b645a1cacull, 0x3fcbbd94273c7bb3ull }, + { 0x3ff3e353f7ced917ull, 0x3fcbd7f38c63ec2dull }, + { 0x3ff3e76c8b439581ull, 0x3fcbf24d83a18698ull }, + { 0x3ff3eb851eb851ecull, 0x3fcc0ca20f311e1eull }, + { 0x3ff3ef9db22d0e56ull, 0x3fcc26f1314d2536ull }, + { 0x3ff3f3b645a1cac1ull, 0x3fcc413aec2eaef8ull }, + { 0x3ff3f7ced916872bull, 0x3fcc5b7f420d700cull }, + { 0x3ff3fbe76c8b4396ull, 0x3fcc75be351fbffdull }, + { 0x3ff4000000000000ull, 0x3fcc8ff7c79a9a22ull }, + { 0x3ff404189374bc6bull, 0x3fccaa2bfbb19eeeull }, + { 0x3ff4083126e978d5ull, 0x3fccc45ad39714dbull }, + { 0x3ff40c49ba5e3540ull, 0x3fccde84517be9b8ull }, + { 0x3ff410624dd2f1aaull, 0x3fccf8a8778fb38cull }, + { 0x3ff4147ae147ae15ull, 0x3fcd12c74800b1e5ull }, + { 0x3ff4189374bc6a7full, 0x3fcd2ce0c4fbcebbull }, + { 0x3ff41cac083126eaull, 0x3fcd46f4f0ac9fbbull }, + { 0x3ff420c49ba5e354ull, 0x3fcd6103cd3d6725ull }, + { 0x3ff424dd2f1a9fbfull, 0x3fcd7b0d5cd71519ull }, + { 0x3ff428f5c28f5c29ull, 0x3fcd9511a1a14871ull }, + { 0x3ff42d0e56041894ull, 0x3fcdaf109dc2500bull }, + { 0x3ff43126e978d4feull, 0x3fcdc90a535f2ba4ull }, + { 0x3ff4353f7ced9169ull, 0x3fcde2fec49b8d1aull }, + { 0x3ff4395810624dd3ull, 0x3fcdfcedf399d94bull }, + { 0x3ff43d70a3d70a3eull, 0x3fce16d7e27b2952ull }, + { 0x3ff44189374bc6a8ull, 0x3fce30bc935f4b64ull }, + { 0x3ff445a1cac08313ull, 0x3fce4a9c0864c40aull }, + { 0x3ff449ba5e353f7dull, 0x3fce647643a8cefdull }, + { 0x3ff44dd2f1a9fbe8ull, 0x3fce7e4b4747605full }, + { 0x3ff451eb851eb852ull, 0x3fce981b155b2593ull }, + { 0x3ff45604189374bdull, 0x3fceb1e5affd8675ull }, + { 0x3ff45a1cac083127ull, 0x3fcecbab1946a62full }, + { 0x3ff45e353f7ced92ull, 0x3fcee56b534d646eull }, + { 0x3ff4624dd2f1a9fcull, 0x3fceff2660275e3aull }, + { 0x3ff4666666666667ull, 0x3fcf18dc41e8ef24ull }, + { 0x3ff46a7ef9db22d1ull, 0x3fcf328cfaa53219ull }, + { 0x3ff46e978d4fdf3cull, 0x3fcf4c388c6e0298ull }, + { 0x3ff472b020c49ba6ull, 0x3fcf65def953fd7dull }, + { 0x3ff476c8b4395811ull, 0x3fcf7f8043668231ull }, + { 0x3ff47ae147ae147bull, 0x3fcf991c6cb3b37aull }, + { 0x3ff47ef9db22d0e5ull, 0x3fcfb2b3774878a1ull }, + { 0x3ff483126e978d50ull, 0x3fcfcc4565307e5bull }, + { 0x3ff4872b020c49baull, 0x3fcfe5d2387637b5ull }, + { 0x3ff48b4395810625ull, 0x3fcfff59f322df35ull }, + { 0x3ff48f5c28f5c28full, 0x3fd00c6e4b9f3bd3ull }, + { 0x3ff49374bc6a7efaull, 0x3fd0192d1367e6a0ull }, + { 0x3ff4978d4fdf3b64ull, 0x3fd025e951ee3b35ull }, + { 0x3ff49ba5e353f7cfull, 0x3fd032a308346a75ull }, + { 0x3ff49fbe76c8b439ull, 0x3fd03f5a373c0b51ull }, + { 0x3ff4a3d70a3d70a4ull, 0x3fd04c0ee0061b5bull }, + { 0x3ff4a7ef9db22d0eull, 0x3fd058c10392ff2bull }, + { 0x3ff4ac083126e979ull, 0x3fd06570a2e282ebull }, + { 0x3ff4b020c49ba5e3ull, 0x3fd0721dbef3dabcull }, + { 0x3ff4b4395810624eull, 0x3fd07ec858c5a344ull }, + { 0x3ff4b851eb851eb8ull, 0x3fd08b707155e20full }, + { 0x3ff4bc6a7ef9db23ull, 0x3fd0981609a2061full }, + { 0x3ff4c083126e978dull, 0x3fd0a4b922a6e849ull }, + { 0x3ff4c49ba5e353f8ull, 0x3fd0b159bd60cbc4ull }, + { 0x3ff4c8b439581062ull, 0x3fd0bdf7dacb5e88ull }, + { 0x3ff4cccccccccccdull, 0x3fd0ca937be1b9dcull }, + { 0x3ff4d0e560418937ull, 0x3fd0d72ca19e62b2ull }, + { 0x3ff4d4fdf3b645a2ull, 0x3fd0e3c34cfb4a32ull }, + { 0x3ff4d916872b020cull, 0x3fd0f0577ef1ce1aull }, + { 0x3ff4dd2f1a9fbe77ull, 0x3fd0fce9387ab948ull }, + { 0x3ff4e147ae147ae1ull, 0x3fd109787a8e4413ull }, + { 0x3ff4e5604189374cull, 0x3fd11605462414dbull }, + { 0x3ff4e978d4fdf3b6ull, 0x3fd1228f9c33405full }, + { 0x3ff4ed916872b021ull, 0x3fd12f177db24a48ull }, + { 0x3ff4f1a9fbe76c8bull, 0x3fd13b9ceb972584ull }, + { 0x3ff4f5c28f5c28f6ull, 0x3fd1481fe6d734cfull }, + { 0x3ff4f9db22d0e560ull, 0x3fd154a070674b09ull }, + { 0x3ff4fdf3b645a1cbull, 0x3fd1611e893babc2ull }, + { 0x3ff5020c49ba5e35ull, 0x3fd16d9a32480b8full }, + { 0x3ff50624dd2f1aa0ull, 0x3fd17a136c7f9093ull }, + { 0x3ff50a3d70a3d70aull, 0x3fd1868a38d4d2d6ull }, + { 0x3ff50e5604189375ull, 0x3fd192fe9839dcc9ull }, + { 0x3ff5126e978d4fdfull, 0x3fd19f708ba02ba3ull }, + { 0x3ff516872b020c4aull, 0x3fd1abe013f8afdcull }, + { 0x3ff51a9fbe76c8b4ull, 0x3fd1b84d3233cd8dull }, + { 0x3ff51eb851eb851full, 0x3fd1c4b7e7415ceeull }, + { 0x3ff522d0e5604189ull, 0x3fd1d1203410aaadull }, + { 0x3ff526e978d4fdf4ull, 0x3fd1dd8619907873ull }, + { 0x3ff52b020c49ba5eull, 0x3fd1e9e998aefd35ull }, + { 0x3ff52f1a9fbe76c9ull, 0x3fd1f64ab259e5baull }, + { 0x3ff5333333333333ull, 0x3fd202a9677e54ecull }, + { 0x3ff5374bc6a7ef9eull, 0x3fd20f05b908e45cull }, + { 0x3ff53b645a1cac08ull, 0x3fd21b5fa7e5a492ull }, + { 0x3ff53f7ced916873ull, 0x3fd227b735001d90ull }, + { 0x3ff54395810624ddull, 0x3fd2340c61434f25ull }, + { 0x3ff547ae147ae148ull, 0x3fd2405f2d99b16aull }, + { 0x3ff54bc6a7ef9db2ull, 0x3fd24caf9aed3514ull }, + { 0x3ff54fdf3b645a1dull, 0x3fd258fdaa2743f8ull }, + { 0x3ff553f7ced91687ull, 0x3fd265495c30c153ull }, + { 0x3ff55810624dd2f2ull, 0x3fd27192b1f20a51ull }, + { 0x3ff55c28f5c28f5cull, 0x3fd27dd9ac52f657ull }, + { 0x3ff5604189374bc7ull, 0x3fd28a1e4c3ad782ull }, + { 0x3ff5645a1cac0831ull, 0x3fd2966092907af8ull }, + { 0x3ff56872b020c49cull, 0x3fd2a2a0803a2964ull }, + { 0x3ff56c8b43958106ull, 0x3fd2aede161da742ull }, + { 0x3ff570a3d70a3d71ull, 0x3fd2bb195520355dull }, + { 0x3ff574bc6a7ef9dbull, 0x3fd2c7523e26911full }, + { 0x3ff578d4fdf3b646ull, 0x3fd2d388d214f507ull }, + { 0x3ff57ced916872b0ull, 0x3fd2dfbd11cf18faull }, + { 0x3ff5810624dd2f1bull, 0x3fd2ebeefe3832bbull }, + { 0x3ff5851eb851eb85ull, 0x3fd2f81e9832f63aull }, + { 0x3ff589374bc6a7f0ull, 0x3fd3044be0a1960aull }, + { 0x3ff58d4fdf3b645aull, 0x3fd31076d865c3b1ull }, + { 0x3ff5916872b020c5ull, 0x3fd31c9f8060b01cull }, + { 0x3ff595810624dd2full, 0x3fd328c5d9730beeull }, + { 0x3ff599999999999aull, 0x3fd334e9e47d07f6ull }, + { 0x3ff59db22d0e5604ull, 0x3fd3410ba25e5578ull }, + { 0x3ff5a1cac083126full, 0x3fd34d2b13f626a8ull }, + { 0x3ff5a5e353f7ced9ull, 0x3fd359483a232ef1ull }, + { 0x3ff5a9fbe76c8b44ull, 0x3fd3656315c3a36bull }, + { 0x3ff5ae147ae147aeull, 0x3fd3717ba7b53b27ull }, + { 0x3ff5b22d0e560419ull, 0x3fd37d91f0d52fa0ull }, + { 0x3ff5b645a1cac083ull, 0x3fd389a5f2003d07ull }, + { 0x3ff5ba5e353f7ceeull, 0x3fd395b7ac12a2b9ull }, + { 0x3ff5be76c8b43958ull, 0x3fd3a1c71fe82381ull }, + { 0x3ff5c28f5c28f5c3ull, 0x3fd3add44e5c0613ull }, + { 0x3ff5c6a7ef9db22dull, 0x3fd3b9df3849154eull }, + { 0x3ff5cac083126e98ull, 0x3fd3c5e7de89a0b1ull }, + { 0x3ff5ced916872b02ull, 0x3fd3d1ee41f77ca4ull }, + { 0x3ff5d2f1a9fbe76dull, 0x3fd3ddf2636c02e7ull }, + { 0x3ff5d70a3d70a3d7ull, 0x3fd3e9f443c012d9ull }, + { 0x3ff5db22d0e56042ull, 0x3fd3f5f3e3cc11ebull }, + { 0x3ff5df3b645a1cacull, 0x3fd401f14467ebe3ull }, + { 0x3ff5e353f7ced917ull, 0x3fd40dec666b1351ull }, + { 0x3ff5e76c8b439581ull, 0x3fd419e54aac81cfull }, + { 0x3ff5eb851eb851ecull, 0x3fd425dbf202b876ull }, + { 0x3ff5ef9db22d0e56ull, 0x3fd431d05d43c01full }, + { 0x3ff5f3b645a1cac1ull, 0x3fd43dc28d4529d2ull }, + { 0x3ff5f7ced916872bull, 0x3fd449b282dc0f0dull }, + { 0x3ff5fbe76c8b4396ull, 0x3fd455a03edd1230ull }, + { 0x3ff6000000000000ull, 0x3fd4618bc21c5ec2ull }, + { 0x3ff604189374bc6bull, 0x3fd46d750d6da9deull }, + { 0x3ff6083126e978d5ull, 0x3fd4795c21a43274ull }, + { 0x3ff60c49ba5e3540ull, 0x3fd48540ff92c1b9ull }, + { 0x3ff610624dd2f1aaull, 0x3fd49123a80bab69ull }, + { 0x3ff6147ae147ae15ull, 0x3fd49d041be0ce32ull }, + { 0x3ff6189374bc6a7full, 0x3fd4a8e25be393f6ull }, + { 0x3ff61cac083126eaull, 0x3fd4b4be68e4f236ull }, + { 0x3ff620c49ba5e354ull, 0x3fd4c09843b56a55ull }, + { 0x3ff624dd2f1a9fbfull, 0x3fd4cc6fed250a02ull }, + { 0x3ff628f5c28f5c29ull, 0x3fd4d84566036b79ull }, + { 0x3ff62d0e56041894ull, 0x3fd4e418af1fb5ecull }, + { 0x3ff63126e978d4feull, 0x3fd4efe9c9489dc3ull }, + { 0x3ff6353f7ced9169ull, 0x3fd4fbb8b54c6508ull }, + { 0x3ff6395810624dd3ull, 0x3fd5078573f8dba6ull }, + { 0x3ff63d70a3d70a3eull, 0x3fd51350061b5fceull }, + { 0x3ff64189374bc6a8ull, 0x3fd51f186c80de3bull }, + { 0x3ff645a1cac08313ull, 0x3fd52adea7f5d297ull }, + { 0x3ff649ba5e353f7dull, 0x3fd536a2b94647bcull }, + { 0x3ff64dd2f1a9fbe8ull, 0x3fd54264a13dd818ull }, + { 0x3ff651eb851eb852ull, 0x3fd54e2460a7adeeull }, + { 0x3ff65604189374bdull, 0x3fd559e1f84e83bdull }, + { 0x3ff65a1cac083127ull, 0x3fd5659d68fca47bull }, + { 0x3ff65e353f7ced92ull, 0x3fd57156b37bebfeull }, + { 0x3ff6624dd2f1a9fcull, 0x3fd57d0dd895c734ull }, + { 0x3ff6666666666667ull, 0x3fd588c2d9133491ull }, + { 0x3ff66a7ef9db22d1ull, 0x3fd59475b5bcc443ull }, + { 0x3ff66e978d4fdf3cull, 0x3fd5a0266f5a989cull }, + { 0x3ff672b020c49ba6ull, 0x3fd5abd506b4664aull }, + { 0x3ff676c8b4395811ull, 0x3fd5b7817c9174c1ull }, + { 0x3ff67ae147ae147bull, 0x3fd5c32bd1b89e70ull }, + { 0x3ff67ef9db22d0e6ull, 0x3fd5ced406f05129ull }, + { 0x3ff683126e978d50ull, 0x3fd5da7a1cfe8e5aull }, + { 0x3ff6872b020c49baull, 0x3fd5e61e14a8eb6dull }, + { 0x3ff68b4395810625ull, 0x3fd5f1bfeeb49213ull }, + { 0x3ff68f5c28f5c28full, 0x3fd5fd5fabe64084ull }, + { 0x3ff69374bc6a7efaull, 0x3fd608fd4d0249e4ull }, + { 0x3ff6978d4fdf3b64ull, 0x3fd61498d2cc967cull }, + { 0x3ff69ba5e353f7cfull, 0x3fd620323e08a417ull }, + { 0x3ff69fbe76c8b439ull, 0x3fd62bc98f798642ull }, + { 0x3ff6a3d70a3d70a4ull, 0x3fd6375ec7e1e6a8ull }, + { 0x3ff6a7ef9db22d0eull, 0x3fd642f1e804054cull }, + { 0x3ff6ac083126e979ull, 0x3fd64e82f0a1b8e9ull }, + { 0x3ff6b020c49ba5e3ull, 0x3fd65a11e27c6f2aull }, + { 0x3ff6b4395810624eull, 0x3fd6659ebe552d0dull }, + { 0x3ff6b851eb851eb8ull, 0x3fd6712984ec8f15ull }, + { 0x3ff6bc6a7ef9db23ull, 0x3fd67cb23702c9adull }, + { 0x3ff6c083126e978dull, 0x3fd68838d557a95eull }, + { 0x3ff6c49ba5e353f8ull, 0x3fd693bd60aa932full }, + { 0x3ff6c8b439581062ull, 0x3fd69f3fd9ba84d8ull }, + { 0x3ff6cccccccccccdull, 0x3fd6aac041461526ull }, + { 0x3ff6d0e560418937ull, 0x3fd6b63e980b7429ull }, + { 0x3ff6d4fdf3b645a2ull, 0x3fd6c1badec86b9bull }, + { 0x3ff6d916872b020cull, 0x3fd6cd35163a5f0eull }, + { 0x3ff6dd2f1a9fbe77ull, 0x3fd6d8ad3f1e4c4cull }, + { 0x3ff6e147ae147ae1ull, 0x3fd6e4235a30cb8dull }, + { 0x3ff6e5604189374cull, 0x3fd6ef97682e0fd1ull }, + { 0x3ff6e978d4fdf3b6ull, 0x3fd6fb0969d1e717ull }, + { 0x3ff6ed916872b021ull, 0x3fd706795fd7bab9ull }, + { 0x3ff6f1a9fbe76c8bull, 0x3fd711e74afa8f9dull }, + { 0x3ff6f5c28f5c28f6ull, 0x3fd71d532bf50695ull }, + { 0x3ff6f9db22d0e560ull, 0x3fd728bd03815c8dull }, + { 0x3ff6fdf3b645a1cbull, 0x3fd73424d2596aecull }, + { 0x3ff7020c49ba5e35ull, 0x3fd73f8a9936a7c4ull }, + { 0x3ff70624dd2f1aa0ull, 0x3fd74aee58d2262dull }, + { 0x3ff70a3d70a3d70aull, 0x3fd7565011e49676ull }, + { 0x3ff70e5604189375ull, 0x3fd761afc5264681ull }, + { 0x3ff7126e978d4fdfull, 0x3fd76d0d734f21f5ull }, + { 0x3ff716872b020c4aull, 0x3fd778691d16b299ull }, + { 0x3ff71a9fbe76c8b4ull, 0x3fd783c2c334207full }, + { 0x3ff71eb851eb851full, 0x3fd78f1a665e3267ull }, + { 0x3ff722d0e5604189ull, 0x3fd79a70074b4de8ull }, + { 0x3ff726e978d4fdf4ull, 0x3fd7a5c3a6b177ccull }, + { 0x3ff72b020c49ba5eull, 0x3fd7b11545465441ull }, + { 0x3ff72f1a9fbe76c9ull, 0x3fd7bc64e3bf272full }, + { 0x3ff7333333333333ull, 0x3fd7c7b282d0d46bull }, + { 0x3ff7374bc6a7ef9eull, 0x3fd7d2fe232fe00dull }, + { 0x3ff73b645a1cac08ull, 0x3fd7de47c5906e9eull }, + { 0x3ff73f7ced916873ull, 0x3fd7e98f6aa64574ull }, + { 0x3ff74395810624ddull, 0x3fd7f4d51324cadcull }, + { 0x3ff747ae147ae148ull, 0x3fd80018bfbf0675ull }, + { 0x3ff74bc6a7ef9db2ull, 0x3fd80b5a7127a15aull }, + { 0x3ff74fdf3b645a1dull, 0x3fd8169a2810e67full }, + { 0x3ff753f7ced91687ull, 0x3fd821d7e52cc2d8ull }, + { 0x3ff75810624dd2f2ull, 0x3fd82d13a92cc5b4ull }, + { 0x3ff75c28f5c28f5cull, 0x3fd8384d74c220e9ull }, + { 0x3ff7604189374bc7ull, 0x3fd84385489da929ull }, + { 0x3ff7645a1cac0831ull, 0x3fd84ebb256fd631ull }, + { 0x3ff76872b020c49cull, 0x3fd859ef0be8c31bull }, + { 0x3ff76c8b43958106ull, 0x3fd86520fcb82e8full }, + { 0x3ff770a3d70a3d71ull, 0x3fd87050f88d7b14ull }, + { 0x3ff774bc6a7ef9dbull, 0x3fd87b7f0017af3eull }, + { 0x3ff778d4fdf3b646ull, 0x3fd886ab14057602ull }, + { 0x3ff77ced916872b0ull, 0x3fd891d535051ee1ull }, + { 0x3ff7810624dd2f1bull, 0x3fd89cfd63c49e40ull }, + { 0x3ff7851eb851eb85ull, 0x3fd8a823a0f18d8cull }, + { 0x3ff789374bc6a7f0ull, 0x3fd8b347ed392b95ull }, + { 0x3ff78d4fdf3b645aull, 0x3fd8be6a49485cb2ull }, + { 0x3ff7916872b020c5ull, 0x3fd8c98ab5cbab1bull }, + { 0x3ff795810624dd2full, 0x3fd8d4a9336f470eull }, + { 0x3ff799999999999aull, 0x3fd8dfc5c2df0724ull }, + { 0x3ff79db22d0e5604ull, 0x3fd8eae064c6687aull }, + { 0x3ff7a1cac083126full, 0x3fd8f5f919d08f07ull }, + { 0x3ff7a5e353f7ced9ull, 0x3fd9010fe2a845c0ull }, + { 0x3ff7a9fbe76c8b44ull, 0x3fd90c24bff7feecull }, + { 0x3ff7ae147ae147aeull, 0x3fd91737b269d44eull }, + { 0x3ff7b22d0e560419ull, 0x3fd92248baa78778ull }, + { 0x3ff7b645a1cac083ull, 0x3fd92d57d95a81f0ull }, + { 0x3ff7ba5e353f7ceeull, 0x3fd938650f2bd582ull }, + { 0x3ff7be76c8b43958ull, 0x3fd943705cc43c6bull }, + { 0x3ff7c28f5c28f5c3ull, 0x3fd94e79c2cc19a7ull }, + { 0x3ff7c6a7ef9db22dull, 0x3fd9598141eb791aull }, + { 0x3ff7cac083126e98ull, 0x3fd96486daca0fdeull }, + { 0x3ff7ced916872b02ull, 0x3fd96f8a8e0f3c6full }, + { 0x3ff7d2f1a9fbe76dull, 0x3fd97a8c5c6206f7ull }, + { 0x3ff7d70a3d70a3d7ull, 0x3fd9858c46692177ull }, + { 0x3ff7db22d0e56042ull, 0x3fd9908a4ccae814ull }, + { 0x3ff7df3b645a1cacull, 0x3fd99b86702d6142ull }, + { 0x3ff7e353f7ced917ull, 0x3fd9a680b1363e0eull }, + { 0x3ff7e76c8b439581ull, 0x3fd9b179108ada49ull }, + { 0x3ff7eb851eb851ecull, 0x3fd9bc6f8ed03cd3ull }, + { 0x3ff7ef9db22d0e56ull, 0x3fd9c7642cab17c3ull }, + { 0x3ff7f3b645a1cac1ull, 0x3fd9d256eabfc8b5ull }, + { 0x3ff7f7ced916872bull, 0x3fd9dd47c9b258efull }, + { 0x3ff7fbe76c8b4396ull, 0x3fd9e836ca267dadull }, + { 0x3ff8000000000000ull, 0x3fd9f323ecbf984cull }, + { 0x3ff804189374bc6bull, 0x3fd9fe0f3220b691ull }, + { 0x3ff8083126e978d5ull, 0x3fda08f89aec92d0ull }, + { 0x3ff80c49ba5e3540ull, 0x3fda13e027c5943cull }, + { 0x3ff810624dd2f1aaull, 0x3fda1ec5d94dcf07ull }, + { 0x3ff8147ae147ae15ull, 0x3fda29a9b02704b1ull }, + { 0x3ff8189374bc6a7full, 0x3fda348bacf2a42cull }, + { 0x3ff81cac083126eaull, 0x3fda3f6bd051ca28ull }, + { 0x3ff820c49ba5e354ull, 0x3fda4a4a1ae54137ull }, + { 0x3ff824dd2f1a9fbfull, 0x3fda55268d4d8217ull }, + { 0x3ff828f5c28f5c29ull, 0x3fda6001282ab3d9ull }, + { 0x3ff82d0e56041894ull, 0x3fda6ad9ec1cac29ull }, + { 0x3ff83126e978d4feull, 0x3fda75b0d9c2ef74ull }, + { 0x3ff8353f7ced9169ull, 0x3fda8085f1bcb12full }, + { 0x3ff8395810624dd3ull, 0x3fda8b5934a8d400ull }, + { 0x3ff83d70a3d70a3eull, 0x3fda962aa325ea01ull }, + { 0x3ff84189374bc6a8ull, 0x3fdaa0fa3dd234eaull }, + { 0x3ff845a1cac08313ull, 0x3fdaabc8054ba654ull }, + { 0x3ff849ba5e353f7dull, 0x3fdab693fa2fdfe3ull }, + { 0x3ff84dd2f1a9fbe8ull, 0x3fdac15e1d1c3387ull }, + { 0x3ff851eb851eb852ull, 0x3fdacc266eada3a7ull }, + { 0x3ff85604189374bdull, 0x3fdad6ecef80e362ull }, + { 0x3ff85a1cac083127ull, 0x3fdae1b1a03256b6ull }, + { 0x3ff85e353f7ced92ull, 0x3fdaec74815e12c9ull }, + { 0x3ff8624dd2f1a9fcull, 0x3fdaf735939fde05ull }, + { 0x3ff8666666666667ull, 0x3fdb01f4d7933067ull }, + { 0x3ff86a7ef9db22d1ull, 0x3fdb0cb24dd3339cull }, + { 0x3ff86e978d4fdf3cull, 0x3fdb176df6fac34cull }, + { 0x3ff872b020c49ba6ull, 0x3fdb2227d3a46d37ull }, + { 0x3ff876c8b4395811ull, 0x3fdb2cdfe46a7180ull }, + { 0x3ff87ae147ae147bull, 0x3fdb379629e6c2cbull }, + { 0x3ff87ef9db22d0e6ull, 0x3fdb424aa4b30688ull }, + { 0x3ff883126e978d50ull, 0x3fdb4cfd5568950full }, + { 0x3ff8872b020c49bbull, 0x3fdb57ae3ca079e8ull }, + { 0x3ff88b4395810625ull, 0x3fdb625d5af373eeull }, + { 0x3ff88f5c28f5c290ull, 0x3fdb6d0ab0f9f591ull }, + { 0x3ff89374bc6a7efaull, 0x3fdb77b63f4c24f8ull }, + { 0x3ff8978d4fdf3b64ull, 0x3fdb82600681dc46ull }, + { 0x3ff89ba5e353f7cfull, 0x3fdb8d080732a9c3ull }, + { 0x3ff89fbe76c8b439ull, 0x3fdb97ae41f5d008ull }, + { 0x3ff8a3d70a3d70a4ull, 0x3fdba252b7624642ull }, + { 0x3ff8a7ef9db22d0eull, 0x3fdbacf5680eb851ull }, + { 0x3ff8ac083126e979ull, 0x3fdbb79654918710ull }, + { 0x3ff8b020c49ba5e3ull, 0x3fdbc2357d80c86dull }, + { 0x3ff8b4395810624eull, 0x3fdbccd2e37247b7ull }, + { 0x3ff8b851eb851eb8ull, 0x3fdbd76e86fb85b1ull }, + { 0x3ff8bc6a7ef9db23ull, 0x3fdbe20868b1b8e3ull }, + { 0x3ff8c083126e978dull, 0x3fdbeca08929cdafull }, + { 0x3ff8c49ba5e353f8ull, 0x3fdbf736e8f86696ull }, + { 0x3ff8c8b439581062ull, 0x3fdc01cb88b1dc5cull }, + { 0x3ff8cccccccccccdull, 0x3fdc0c5e68ea3e46ull }, + { 0x3ff8d0e560418937ull, 0x3fdc16ef8a355236ull }, + { 0x3ff8d4fdf3b645a2ull, 0x3fdc217eed2694f6ull }, + { 0x3ff8d916872b020cull, 0x3fdc2c0c92513a4eull }, + { 0x3ff8dd2f1a9fbe77ull, 0x3fdc36987a482d4cull }, + { 0x3ff8e147ae147ae1ull, 0x3fdc4122a59e105cull }, + { 0x3ff8e5604189374cull, 0x3fdc4bab14e53d91ull }, + { 0x3ff8e978d4fdf3b6ull, 0x3fdc5631c8afc6baull }, + { 0x3ff8ed916872b021ull, 0x3fdc60b6c18f75acull }, + { 0x3ff8f1a9fbe76c8bull, 0x3fdc6b3a0015cc5aull }, + { 0x3ff8f5c28f5c28f6ull, 0x3fdc75bb84d40518ull }, + { 0x3ff8f9db22d0e560ull, 0x3fdc803b505b12b7ull }, + { 0x3ff8fdf3b645a1cbull, 0x3fdc8ab9633ba0c8ull }, + { 0x3ff9020c49ba5e35ull, 0x3fdc9535be0613b6ull }, + { 0x3ff90624dd2f1aa0ull, 0x3fdc9fb0614a8908ull }, + { 0x3ff90a3d70a3d70aull, 0x3fdcaa294d98d77eull }, + { 0x3ff90e5604189375ull, 0x3fdcb4a083808f50ull }, + { 0x3ff9126e978d4fdfull, 0x3fdcbf160390fa4aull }, + { 0x3ff916872b020c4aull, 0x3fdcc989ce591c0full }, + { 0x3ff91a9fbe76c8b4ull, 0x3fdcd3fbe467b230ull }, + { 0x3ff91eb851eb851full, 0x3fdcde6c464b346full }, + { 0x3ff922d0e5604189ull, 0x3fdce8daf491d4daull }, + { 0x3ff926e978d4fdf4ull, 0x3fdcf347efc9800aull }, + { 0x3ff92b020c49ba5eull, 0x3fdcfdb3387fdd3full }, + { 0x3ff92f1a9fbe76c9ull, 0x3fdd081ccf424e9dull }, + { 0x3ff9333333333333ull, 0x3fdd1284b49df149ull }, + { 0x3ff9374bc6a7ef9eull, 0x3fdd1ceae91f9da8ull }, + { 0x3ff93b645a1cac08ull, 0x3fdd274f6d53e779ull }, + { 0x3ff93f7ced916873ull, 0x3fdd31b241c71e12ull }, + { 0x3ff94395810624ddull, 0x3fdd3c1367054c7eull }, + { 0x3ff947ae147ae148ull, 0x3fdd4672dd9a39b7ull }, + { 0x3ff94bc6a7ef9db2ull, 0x3fdd50d0a61168c4ull }, + { 0x3ff94fdf3b645a1dull, 0x3fdd5b2cc0f618f4ull }, + { 0x3ff953f7ced91687ull, 0x3fdd65872ed345faull }, + { 0x3ff95810624dd2f2ull, 0x3fdd6fdff033a829ull }, + { 0x3ff95c28f5c28f5cull, 0x3fdd7a3705a1b48full }, + { 0x3ff9604189374bc7ull, 0x3fdd848c6fa79d31ull }, + { 0x3ff9645a1cac0831ull, 0x3fdd8ee02ecf5126ull }, + { 0x3ff96872b020c49cull, 0x3fdd993243a27cd3ull }, + { 0x3ff96c8b43958106ull, 0x3fdda382aeaa8a04ull }, + { 0x3ff970a3d70a3d71ull, 0x3fddadd17070a029ull }, + { 0x3ff974bc6a7ef9dbull, 0x3fddb81e897da46eull }, + { 0x3ff978d4fdf3b646ull, 0x3fddc269fa5a39faull }, + { 0x3ff97ced916872b0ull, 0x3fddccb3c38ec206ull }, + { 0x3ff9810624dd2f1bull, 0x3fddd6fbe5a35c18ull }, + { 0x3ff9851eb851eb85ull, 0x3fdde142611fe61dull }, + { 0x3ff989374bc6a7f0ull, 0x3fddeb87368bfca5ull }, + { 0x3ff98d4fdf3b645aull, 0x3fddf5ca666efafaull }, + { 0x3ff9916872b020c5ull, 0x3fde000bf14ffb5cull }, + { 0x3ff995810624dd2full, 0x3fde0a4bd7b5d71bull }, + { 0x3ff999999999999aull, 0x3fde148a1a2726cfull }, + { 0x3ff99db22d0e5604ull, 0x3fde1ec6b92a426full }, + { 0x3ff9a1cac083126full, 0x3fde2901b5454193ull }, + { 0x3ff9a5e353f7ced9ull, 0x3fde333b0efdfb83ull }, + { 0x3ff9a9fbe76c8b44ull, 0x3fde3d72c6da0777ull }, + { 0x3ff9ae147ae147aeull, 0x3fde47a8dd5ebcabull }, + { 0x3ff9b22d0e560419ull, 0x3fde51dd5311329cull }, + { 0x3ff9b645a1cac083ull, 0x3fde5c102876411full }, + { 0x3ff9ba5e353f7ceeull, 0x3fde66415e128098ull }, + { 0x3ff9be76c8b43958ull, 0x3fde7070f46a4a16ull }, + { 0x3ff9c28f5c28f5c3ull, 0x3fde7a9eec01b787ull }, + { 0x3ff9c6a7ef9db22dull, 0x3fde84cb455ca3d2ull }, + { 0x3ff9cac083126e98ull, 0x3fde8ef600feab11ull }, + { 0x3ff9ced916872b02ull, 0x3fde991f1f6b2aa7ull }, + { 0x3ff9d2f1a9fbe76dull, 0x3fdea346a1254176ull }, + { 0x3ff9d70a3d70a3d7ull, 0x3fdead6c86afcffbull }, + { 0x3ff9db22d0e56042ull, 0x3fdeb790d08d7882ull }, + { 0x3ff9df3b645a1cacull, 0x3fdec1b37f409f3eull }, + { 0x3ff9e353f7ced917ull, 0x3fdecbd4934b6a83ull }, + { 0x3ff9e76c8b439581ull, 0x3fded5f40d2fc2d7ull }, + { 0x3ff9eb851eb851ecull, 0x3fdee011ed6f5331ull }, + { 0x3ff9ef9db22d0e56ull, 0x3fdeea2e348b890aull }, + { 0x3ff9f3b645a1cac1ull, 0x3fdef448e3059497ull }, + { 0x3ff9f7ced916872bull, 0x3fdefe61f95e68ddull }, + { 0x3ff9fbe76c8b4396ull, 0x3fdf08797816bbebull }, + { 0x3ffa000000000000ull, 0x3fdf128f5faf06edull }, + { 0x3ffa04189374bc6bull, 0x3fdf1ca3b0a78663ull }, + { 0x3ffa083126e978d5ull, 0x3fdf26b66b803a3bull }, + { 0x3ffa0c49ba5e3540ull, 0x3fdf30c790b8e601ull }, + { 0x3ffa10624dd2f1aaull, 0x3fdf3ad720d110fbull }, + { 0x3ffa147ae147ae15ull, 0x3fdf44e51c480659ull }, + { 0x3ffa189374bc6a7full, 0x3fdf4ef1839cd550ull }, + { 0x3ffa1cac083126eaull, 0x3fdf58fc574e514eull }, + { 0x3ffa20c49ba5e354ull, 0x3fdf630597db120bull }, + { 0x3ffa24dd2f1a9fbfull, 0x3fdf6d0d45c173c8ull }, + { 0x3ffa28f5c28f5c29ull, 0x3fdf7713617f9758ull }, + { 0x3ffa2d0e56041894ull, 0x3fdf8117eb936262ull }, + { 0x3ffa3126e978d4feull, 0x3fdf8b1ae47a7f6cull }, + { 0x3ffa353f7ced9169ull, 0x3fdf951c4cb25e16ull }, + { 0x3ffa395810624dd3ull, 0x3fdf9f1c24b83329ull }, + { 0x3ffa3d70a3d70a3eull, 0x3fdfa91a6d08f8d3ull }, + { 0x3ffa4189374bc6a8ull, 0x3fdfb31726216eb8ull }, + { 0x3ffa45a1cac08313ull, 0x3fdfbd12507e1a23ull }, + { 0x3ffa49ba5e353f7dull, 0x3fdfc70bec9b4621ull }, + { 0x3ffa4dd2f1a9fbe8ull, 0x3fdfd103faf503b1ull }, + { 0x3ffa51eb851eb852ull, 0x3fdfdafa7c0729daull }, + { 0x3ffa5604189374bdull, 0x3fdfe4ef704d55dcull }, + { 0x3ffa5a1cac083127ull, 0x3fdfeee2d842eb49ull }, + { 0x3ffa5e353f7ced92ull, 0x3fdff8d4b4631435ull }, + { 0x3ffa624dd2f1a9fcull, 0x3fe00162829460a5ull }, + { 0x3ffa666666666667ull, 0x3fe00659e5875500ull }, + { 0x3ffa6a7ef9db22d1ull, 0x3fe00b508347a653ull }, + { 0x3ffa6e978d4fdf3cull, 0x3fe010465c127751ull }, + { 0x3ffa72b020c49ba6ull, 0x3fe0153b7024ce40ull }, + { 0x3ffa76c8b4395811ull, 0x3fe01a2fbfbb950full }, + { 0x3ffa7ae147ae147bull, 0x3fe01f234b139965ull }, + { 0x3ffa7ef9db22d0e6ull, 0x3fe0241612698cb7ull }, + { 0x3ffa83126e978d50ull, 0x3fe0290815fa0451ull }, + { 0x3ffa872b020c49bbull, 0x3fe02df956017974ull }, + { 0x3ffa8b4395810625ull, 0x3fe032e9d2bc495aull }, + { 0x3ffa8f5c28f5c290ull, 0x3fe037d98c66b551ull }, + { 0x3ffa9374bc6a7efaull, 0x3fe03cc8833ce2c5ull }, + { 0x3ffa978d4fdf3b65ull, 0x3fe041b6b77adb5bull }, + { 0x3ffa9ba5e353f7cfull, 0x3fe046a4295c8cf4ull }, + { 0x3ffa9fbe76c8b43aull, 0x3fe04b90d91dc9cdull }, + { 0x3ffaa3d70a3d70a4ull, 0x3fe0507cc6fa4884ull }, + { 0x3ffaa7ef9db22d0eull, 0x3fe05567f32da431ull }, + { 0x3ffaac083126e979ull, 0x3fe05a525df35c75ull }, + { 0x3ffab020c49ba5e3ull, 0x3fe05f3c0786d583ull }, + { 0x3ffab4395810624eull, 0x3fe06424f0235842ull }, + { 0x3ffab851eb851eb8ull, 0x3fe0690d18041249ull }, + { 0x3ffabc6a7ef9db23ull, 0x3fe06df47f641601ull }, + { 0x3ffac083126e978dull, 0x3fe072db267e5aacull }, + { 0x3ffac49ba5e353f8ull, 0x3fe077c10d8dbc7aull }, + { 0x3ffac8b439581062ull, 0x3fe07ca634ccfc94ull }, + { 0x3ffacccccccccccdull, 0x3fe0818a9c76c137ull }, + { 0x3ffad0e560418937ull, 0x3fe0866e44c595b7ull }, + { 0x3ffad4fdf3b645a2ull, 0x3fe08b512df3ea9cull }, + { 0x3ffad916872b020cull, 0x3fe09033583c15a7ull }, + { 0x3ffadd2f1a9fbe77ull, 0x3fe09514c3d851f0ull }, + { 0x3ffae147ae147ae1ull, 0x3fe099f57102bfe5ull }, + { 0x3ffae5604189374cull, 0x3fe09ed55ff5656bull }, + { 0x3ffae978d4fdf3b6ull, 0x3fe0a3b490ea2de3ull }, + { 0x3ffaed916872b021ull, 0x3fe0a893041aea3full }, + { 0x3ffaf1a9fbe76c8bull, 0x3fe0ad70b9c1510full }, + { 0x3ffaf5c28f5c28f6ull, 0x3fe0b24db216fe96ull }, + { 0x3ffaf9db22d0e560ull, 0x3fe0b729ed5574d5ull }, + { 0x3ffafdf3b645a1cbull, 0x3fe0bc056bb61b9full }, + { 0x3ffb020c49ba5e35ull, 0x3fe0c0e02d7240a2ull }, + { 0x3ffb0624dd2f1aa0ull, 0x3fe0c5ba32c31782ull }, + { 0x3ffb0a3d70a3d70aull, 0x3fe0ca937be1b9dcull }, + { 0x3ffb0e5604189375ull, 0x3fe0cf6c09072762ull }, + { 0x3ffb126e978d4fdfull, 0x3fe0d443da6c45e0ull }, + { 0x3ffb16872b020c4aull, 0x3fe0d91af049e155ull }, + { 0x3ffb1a9fbe76c8b4ull, 0x3fe0ddf14ad8abf9ull }, + { 0x3ffb1eb851eb851full, 0x3fe0e2c6ea513e58ull }, + { 0x3ffb22d0e5604189ull, 0x3fe0e79bceec1754ull }, + { 0x3ffb26e978d4fdf4ull, 0x3fe0ec6ff8e19c44ull }, + { 0x3ffb2b020c49ba5eull, 0x3fe0f143686a18f1ull }, + { 0x3ffb2f1a9fbe76c9ull, 0x3fe0f6161dbdbfbaull }, + { 0x3ffb333333333333ull, 0x3fe0fae81914a991ull }, + { 0x3ffb374bc6a7ef9eull, 0x3fe0ffb95aa6d617ull }, + { 0x3ffb3b645a1cac08ull, 0x3fe10489e2ac2ba2ull }, + { 0x3ffb3f7ced916873ull, 0x3fe10959b15c7756ull }, + { 0x3ffb4395810624ddull, 0x3fe10e28c6ef6d28ull }, + { 0x3ffb47ae147ae148ull, 0x3fe112f7239ca7fcull }, + { 0x3ffb4bc6a7ef9db2ull, 0x3fe117c4c79ba9a3ull }, + { 0x3ffb4fdf3b645a1dull, 0x3fe11c91b323dafcull }, + { 0x3ffb53f7ced91687ull, 0x3fe1215de66c8bf2ull }, + { 0x3ffb5810624dd2f2ull, 0x3fe1262961acf398ull }, + { 0x3ffb5c28f5c28f5cull, 0x3fe12af4251c3030ull }, + { 0x3ffb604189374bc7ull, 0x3fe12fbe30f1473full }, + { 0x3ffb645a1cac0831ull, 0x3fe1348785632596ull }, + { 0x3ffb6872b020c49cull, 0x3fe1395022a89f69ull }, + { 0x3ffb6c8b43958106ull, 0x3fe13e1808f87053ull }, + { 0x3ffb70a3d70a3d71ull, 0x3fe142df38893b71ull }, + { 0x3ffb74bc6a7ef9dbull, 0x3fe147a5b1918b64ull }, + { 0x3ffb78d4fdf3b646ull, 0x3fe14c6b7447d26bull }, + { 0x3ffb7ced916872b0ull, 0x3fe1513080e26a69ull }, + { 0x3ffb810624dd2f1bull, 0x3fe155f4d79794f9ull }, + { 0x3ffb851eb851eb85ull, 0x3fe15ab8789d7b77ull }, + { 0x3ffb89374bc6a7f0ull, 0x3fe15f7b642a2f16ull }, + { 0x3ffb8d4fdf3b645aull, 0x3fe1643d9a73a8e7ull }, + { 0x3ffb916872b020c5ull, 0x3fe168ff1bafc9eeull }, + { 0x3ffb95810624dd2full, 0x3fe16dbfe8145b28ull }, + { 0x3ffb99999999999aull, 0x3fe1727fffd70da1ull }, + { 0x3ffb9db22d0e5604ull, 0x3fe1773f632d7a7full }, + { 0x3ffba1cac083126full, 0x3fe17bfe124d2310ull }, + { 0x3ffba5e353f7ced9ull, 0x3fe180bc0d6b70d9ull }, + { 0x3ffba9fbe76c8b44ull, 0x3fe1857954bdb5a3ull }, + { 0x3ffbae147ae147aeull, 0x3fe18a35e8792b89ull }, + { 0x3ffbb22d0e560419ull, 0x3fe18ef1c8d2f509ull }, + { 0x3ffbb645a1cac083ull, 0x3fe193acf6001d0eull }, + { 0x3ffbba5e353f7ceeull, 0x3fe1986770359700ull }, + { 0x3ffbbe76c8b43958ull, 0x3fe19d2137a83ed1ull }, + { 0x3ffbc28f5c28f5c3ull, 0x3fe1a1da4c8cd90cull }, + { 0x3ffbc6a7ef9db22dull, 0x3fe1a692af1812e2ull }, + { 0x3ffbcac083126e98ull, 0x3fe1ab4a5f7e8238ull }, + { 0x3ffbced916872b02ull, 0x3fe1b0015df4a5b4ull }, + { 0x3ffbd2f1a9fbe76dull, 0x3fe1b4b7aaaee4cdull }, + { 0x3ffbd70a3d70a3d7ull, 0x3fe1b96d45e18fd3ull }, + { 0x3ffbdb22d0e56042ull, 0x3fe1be222fc0e007ull }, + { 0x3ffbdf3b645a1cacull, 0x3fe1c2d66880f79cull }, + { 0x3ffbe353f7ced917ull, 0x3fe1c789f055e1cfull }, + { 0x3ffbe76c8b439581ull, 0x3fe1cc3cc77392edull }, + { 0x3ffbeb851eb851ecull, 0x3fe1d0eeee0de867ull }, + { 0x3ffbef9db22d0e56ull, 0x3fe1d5a06458a8d7ull }, + { 0x3ffbf3b645a1cac1ull, 0x3fe1da512a87841aull }, + { 0x3ffbf7ced916872bull, 0x3fe1df0140ce134eull }, + { 0x3ffbfbe76c8b4396ull, 0x3fe1e3b0a75fd8edull }, + { 0x3ffc000000000000ull, 0x3fe1e85f5e7040d0ull }, + { 0x3ffc04189374bc6bull, 0x3fe1ed0d6632a045ull }, + { 0x3ffc083126e978d5ull, 0x3fe1f1babeda3612ull }, + { 0x3ffc0c49ba5e3540ull, 0x3fe1f667689a2a8full }, + { 0x3ffc10624dd2f1aaull, 0x3fe1fb1363a58fa5ull }, + { 0x3ffc147ae147ae15ull, 0x3fe1ffbeb02f60e9ull }, + { 0x3ffc189374bc6a7full, 0x3fe204694e6a839bull }, + { 0x3ffc1cac083126eaull, 0x3fe209133e89c6c1ull }, + { 0x3ffc20c49ba5e354ull, 0x3fe20dbc80bfe327ull }, + { 0x3ffc24dd2f1a9fbfull, 0x3fe21265153f7b78ull }, + { 0x3ffc28f5c28f5c29ull, 0x3fe2170cfc3b1c40ull }, + { 0x3ffc2d0e56041894ull, 0x3fe21bb435e53c01ull }, + { 0x3ffc3126e978d4feull, 0x3fe2205ac2703b3aull }, + { 0x3ffc353f7ced9169ull, 0x3fe22500a20e647aull }, + { 0x3ffc395810624dd3ull, 0x3fe229a5d4f1ec64ull }, + { 0x3ffc3d70a3d70a3eull, 0x3fe22e4a5b4cf1c7ull }, + { 0x3ffc4189374bc6a8ull, 0x3fe232ee35517da0ull }, + { 0x3ffc45a1cac08313ull, 0x3fe237916331832dull }, + { 0x3ffc49ba5e353f7dull, 0x3fe23c33e51edff7ull }, + { 0x3ffc4dd2f1a9fbe8ull, 0x3fe240d5bb4b5be1ull }, + { 0x3ffc51eb851eb852ull, 0x3fe24576e5e8a92full }, + { 0x3ffc5604189374bdull, 0x3fe24a176528649cull }, + { 0x3ffc5a1cac083127ull, 0x3fe24eb7393c155cull }, + { 0x3ffc5e353f7ced92ull, 0x3fe2535662552d31ull }, + { 0x3ffc624dd2f1a9fcull, 0x3fe257f4e0a50870ull }, + { 0x3ffc666666666667ull, 0x3fe25c92b45cee14ull }, + { 0x3ffc6a7ef9db22d1ull, 0x3fe2612fddae0fc5ull }, + { 0x3ffc6e978d4fdf3cull, 0x3fe265cc5cc989eaull }, + { 0x3ffc72b020c49ba6ull, 0x3fe26a6831e063adull }, + { 0x3ffc76c8b4395811ull, 0x3fe26f035d238f11ull }, + { 0x3ffc7ae147ae147bull, 0x3fe2739ddec3e8f5ull }, + { 0x3ffc7ef9db22d0e6ull, 0x3fe27837b6f23929ull }, + { 0x3ffc83126e978d50ull, 0x3fe27cd0e5df3271ull }, + { 0x3ffc872b020c49bbull, 0x3fe281696bbb7299ull }, + { 0x3ffc8b4395810625ull, 0x3fe2860148b7827bull }, + { 0x3ffc8f5c28f5c290ull, 0x3fe28a987d03d610ull }, + { 0x3ffc9374bc6a7efaull, 0x3fe28f2f08d0cc78ull }, + { 0x3ffc978d4fdf3b65ull, 0x3fe293c4ec4eb00aull }, + { 0x3ffc9ba5e353f7cfull, 0x3fe2985a27adb659ull }, + { 0x3ffc9fbe76c8b43aull, 0x3fe29ceebb1e004aull }, + { 0x3ffca3d70a3d70a4ull, 0x3fe2a182a6cf9a15ull }, + { 0x3ffca7ef9db22d0full, 0x3fe2a615eaf27b5aull }, + { 0x3ffcac083126e979ull, 0x3fe2aaa887b68726ull }, + { 0x3ffcb020c49ba5e3ull, 0x3fe2af3a7d4b8c04ull }, + { 0x3ffcb4395810624eull, 0x3fe2b3cbcbe14407ull }, + { 0x3ffcb851eb851eb8ull, 0x3fe2b85c73a754cfull }, + { 0x3ffcbc6a7ef9db23ull, 0x3fe2bcec74cd4fa2ull }, + { 0x3ffcc083126e978dull, 0x3fe2c17bcf82b16cull }, + { 0x3ffcc49ba5e353f8ull, 0x3fe2c60a83f6e2d0ull }, + { 0x3ffcc8b439581062ull, 0x3fe2ca9892593832ull }, + { 0x3ffccccccccccccdull, 0x3fe2cf25fad8f1c4ull }, + { 0x3ffcd0e560418937ull, 0x3fe2d3b2bda53b8eull }, + { 0x3ffcd4fdf3b645a2ull, 0x3fe2d83edaed2d7full }, + { 0x3ffcd916872b020cull, 0x3fe2dcca52dfcb73ull }, + { 0x3ffcdd2f1a9fbe77ull, 0x3fe2e15525ac0542ull }, + { 0x3ffce147ae147ae1ull, 0x3fe2e5df5380b6c8ull }, + { 0x3ffce5604189374cull, 0x3fe2ea68dc8ca7f6ull }, + { 0x3ffce978d4fdf3b6ull, 0x3fe2eef1c0fe8cd4ull }, + { 0x3ffced916872b021ull, 0x3fe2f37a01050599ull }, + { 0x3ffcf1a9fbe76c8bull, 0x3fe2f8019cce9ea8ull }, + { 0x3ffcf5c28f5c28f6ull, 0x3fe2fc889489d0a9ull }, + { 0x3ffcf9db22d0e560ull, 0x3fe3010ee8650088ull }, + { 0x3ffcfdf3b645a1cbull, 0x3fe30594988e7f8aull }, + { 0x3ffd020c49ba5e35ull, 0x3fe30a19a5348b50ull }, + { 0x3ffd0624dd2f1aa0ull, 0x3fe30e9e0e854de9ull }, + { 0x3ffd0a3d70a3d70aull, 0x3fe31321d4aeddd7ull }, + { 0x3ffd0e5604189375ull, 0x3fe317a4f7df3e21ull }, + { 0x3ffd126e978d4fdfull, 0x3fe31c2778445e56ull }, + { 0x3ffd16872b020c4aull, 0x3fe320a9560c1a9eull }, + { 0x3ffd1a9fbe76c8b4ull, 0x3fe3252a91643bc0ull }, + { 0x3ffd1eb851eb851full, 0x3fe329ab2a7a7736ull }, + { 0x3ffd22d0e5604189ull, 0x3fe32e2b217c6f2aull }, + { 0x3ffd26e978d4fdf4ull, 0x3fe332aa7697b28eull }, + { 0x3ffd2b020c49ba5eull, 0x3fe3372929f9bd1eull }, + { 0x3ffd2f1a9fbe76c9ull, 0x3fe33ba73bcff771ull }, + { 0x3ffd333333333333ull, 0x3fe34024ac47b6fdull }, + { 0x3ffd374bc6a7ef9eull, 0x3fe344a17b8e3e29ull }, + { 0x3ffd3b645a1cac08ull, 0x3fe3491da9d0bc52ull }, + { 0x3ffd3f7ced916873ull, 0x3fe34d99373c4dd9ull }, + { 0x3ffd4395810624ddull, 0x3fe3521423fdfc2bull }, + { 0x3ffd47ae147ae148ull, 0x3fe3568e7042bdd0ull }, + { 0x3ffd4bc6a7ef9db2ull, 0x3fe35b081c377670ull }, + { 0x3ffd4fdf3b645a1dull, 0x3fe35f812808f6e1ull }, + { 0x3ffd53f7ced91687ull, 0x3fe363f993e3fd30ull }, + { 0x3ffd5810624dd2f2ull, 0x3fe368715ff534afull }, + { 0x3ffd5c28f5c28f5cull, 0x3fe36ce88c6935f9ull }, + { 0x3ffd604189374bc7ull, 0x3fe3715f196c8703ull }, + { 0x3ffd645a1cac0831ull, 0x3fe375d5072b9b22ull }, + { 0x3ffd6872b020c49cull, 0x3fe37a4a55d2d317ull }, + { 0x3ffd6c8b43958106ull, 0x3fe37ebf058e7d19ull }, + { 0x3ffd70a3d70a3d71ull, 0x3fe38333168ad4e2ull }, + { 0x3ffd74bc6a7ef9dbull, 0x3fe387a688f403b3ull }, + { 0x3ffd78d4fdf3b646ull, 0x3fe38c195cf62066ull }, + { 0x3ffd7ced916872b0ull, 0x3fe3908b92bd2f70ull }, + { 0x3ffd810624dd2f1bull, 0x3fe394fd2a7522f4ull }, + { 0x3ffd851eb851eb85ull, 0x3fe3996e2449dac6ull }, + { 0x3ffd89374bc6a7f0ull, 0x3fe39dde80672479ull }, + { 0x3ffd8d4fdf3b645aull, 0x3fe3a24e3ef8bb67ull }, + { 0x3ffd916872b020c5ull, 0x3fe3a6bd602a48bfull }, + { 0x3ffd95810624dd2full, 0x3fe3ab2be4276386ull }, + { 0x3ffd99999999999aull, 0x3fe3af99cb1b90b0ull }, + { 0x3ffd9db22d0e5604ull, 0x3fe3b40715324319ull }, + { 0x3ffda1cac083126full, 0x3fe3b873c296db9dull }, + { 0x3ffda5e353f7ced9ull, 0x3fe3bcdfd374a917ull }, + { 0x3ffda9fbe76c8b44ull, 0x3fe3c14b47f6e877ull }, + { 0x3ffdae147ae147aeull, 0x3fe3c5b62048c4beull }, + { 0x3ffdb22d0e560419ull, 0x3fe3ca205c955715ull }, + { 0x3ffdb645a1cac083ull, 0x3fe3ce89fd07a6caull }, + { 0x3ffdba5e353f7ceeull, 0x3fe3d2f301caa969ull }, + { 0x3ffdbe76c8b43958ull, 0x3fe3d75b6b0942b5ull }, + { 0x3ffdc28f5c28f5c3ull, 0x3fe3dbc338ee44c1ull }, + { 0x3ffdc6a7ef9db22dull, 0x3fe3e02a6ba46fedull }, + { 0x3ffdcac083126e98ull, 0x3fe3e491035672fdull }, + { 0x3ffdced916872b02ull, 0x3fe3e8f7002eeb13ull }, + { 0x3ffdd2f1a9fbe76dull, 0x3fe3ed5c625863c8ull }, + { 0x3ffdd70a3d70a3d7ull, 0x3fe3f1c129fd5729ull }, + { 0x3ffddb22d0e56042ull, 0x3fe3f62557482dcdull }, + { 0x3ffddf3b645a1cacull, 0x3fe3fa88ea633ed0ull }, + { 0x3ffde353f7ced917ull, 0x3fe3feebe378cfebull }, + { 0x3ffde76c8b439581ull, 0x3fe4034e42b31573ull }, + { 0x3ffdeb851eb851ecull, 0x3fe407b0083c326bull }, + { 0x3ffdef9db22d0e56ull, 0x3fe40c11343e3881ull }, + { 0x3ffdf3b645a1cac1ull, 0x3fe41071c6e3282aull }, + { 0x3ffdf7ced916872bull, 0x3fe414d1c054f096ull }, + { 0x3ffdfbe76c8b4396ull, 0x3fe4193120bd6fcfull }, + { 0x3ffe000000000000ull, 0x3fe41d8fe84672aeull }, + { 0x3ffe04189374bc6bull, 0x3fe421ee1719b4f7ull }, + { 0x3ffe083126e978d5ull, 0x3fe4264bad60e150ull }, + { 0x3ffe0c49ba5e3540ull, 0x3fe42aa8ab45915dull }, + { 0x3ffe10624dd2f1aaull, 0x3fe42f0510f14dbaull }, + { 0x3ffe147ae147ae15ull, 0x3fe43360de8d8e0cull }, + { 0x3ffe189374bc6a7full, 0x3fe437bc1443b909ull }, + { 0x3ffe1cac083126eaull, 0x3fe43c16b23d2481ull }, + { 0x3ffe20c49ba5e354ull, 0x3fe44070b8a31565ull }, + { 0x3ffe24dd2f1a9fbfull, 0x3fe444ca279ebfd5ull }, + { 0x3ffe28f5c28f5c29ull, 0x3fe44922ff594724ull }, + { 0x3ffe2d0e56041894ull, 0x3fe44d7b3ffbbde4ull }, + { 0x3ffe3126e978d4feull, 0x3fe451d2e9af25efull }, + { 0x3ffe353f7ced9169ull, 0x3fe45629fc9c7070ull }, + { 0x3ffe395810624dd3ull, 0x3fe45a8078ec7de9ull }, + { 0x3ffe3d70a3d70a3eull, 0x3fe45ed65ec81e43ull }, + { 0x3ffe4189374bc6a8ull, 0x3fe4632bae5810cdull }, + { 0x3ffe45a1cac08313ull, 0x3fe4678067c50450ull }, + { 0x3ffe49ba5e353f7dull, 0x3fe46bd48b37970full }, + { 0x3ffe4dd2f1a9fbe8ull, 0x3fe4702818d856d6ull }, + { 0x3ffe51eb851eb852ull, 0x3fe4747b10cfc0fdull }, + { 0x3ffe5604189374bdull, 0x3fe478cd73464279ull }, + { 0x3ffe5a1cac083127ull, 0x3fe47d1f406437dbull }, + { 0x3ffe5e353f7ced92ull, 0x3fe481707851ed63ull }, + { 0x3ffe624dd2f1a9fcull, 0x3fe485c11b379effull }, + { 0x3ffe666666666667ull, 0x3fe48a11293d785cull }, + { 0x3ffe6a7ef9db22d1ull, 0x3fe48e60a28b94eaull }, + { 0x3ffe6e978d4fdf3cull, 0x3fe492af8749ffe6ull }, + { 0x3ffe72b020c49ba6ull, 0x3fe496fdd7a0b462ull }, + { 0x3ffe76c8b4395811ull, 0x3fe49b4b93b79d51ull }, + { 0x3ffe7ae147ae147bull, 0x3fe49f98bbb6958bull }, + { 0x3ffe7ef9db22d0e6ull, 0x3fe4a3e54fc567d9ull }, + { 0x3ffe83126e978d50ull, 0x3fe4a831500bcefaull }, + { 0x3ffe872b020c49bbull, 0x3fe4ac7cbcb175b2ull }, + { 0x3ffe8b4395810625ull, 0x3fe4b0c795ddf6cbull }, + { 0x3ffe8f5c28f5c290ull, 0x3fe4b511dbb8dd24ull }, + { 0x3ffe9374bc6a7efaull, 0x3fe4b95b8e69a3b4ull }, + { 0x3ffe978d4fdf3b65ull, 0x3fe4bda4ae17b599ull }, + { 0x3ffe9ba5e353f7cfull, 0x3fe4c1ed3aea6e17ull }, + { 0x3ffe9fbe76c8b43aull, 0x3fe4c635350918adull }, + { 0x3ffea3d70a3d70a4ull, 0x3fe4ca7c9c9af110ull }, + { 0x3ffea7ef9db22d0full, 0x3fe4cec371c72340ull }, + { 0x3ffeac083126e979ull, 0x3fe4d309b4b4cb85ull }, + { 0x3ffeb020c49ba5e4ull, 0x3fe4d74f658af683ull }, + { 0x3ffeb4395810624eull, 0x3fe4db948470a136ull }, + { 0x3ffeb851eb851eb8ull, 0x3fe4dfd9118cb904ull }, + { 0x3ffebc6a7ef9db23ull, 0x3fe4e41d0d061bc3ull }, + { 0x3ffec083126e978dull, 0x3fe4e860770397bcull }, + { 0x3ffec49ba5e353f8ull, 0x3fe4eca34fabebbdull }, + { 0x3ffec8b439581062ull, 0x3fe4f0e59725c715ull }, + { 0x3ffecccccccccccdull, 0x3fe4f5274d97c9a9ull }, + { 0x3ffed0e560418937ull, 0x3fe4f968732883f3ull }, + { 0x3ffed4fdf3b645a2ull, 0x3fe4fda907fe770eull }, + { 0x3ffed916872b020cull, 0x3fe501e90c4014bdull }, + { 0x3ffedd2f1a9fbe77ull, 0x3fe506288013bf76ull }, + { 0x3ffee147ae147ae1ull, 0x3fe50a67639fca63ull }, + { 0x3ffee5604189374cull, 0x3fe50ea5b70a7975ull }, + { 0x3ffee978d4fdf3b6ull, 0x3fe512e37a7a0160ull }, + { 0x3ffeed916872b021ull, 0x3fe51720ae1487aeull }, + { 0x3ffef1a9fbe76c8bull, 0x3fe51b5d520022beull }, + { 0x3ffef5c28f5c28f6ull, 0x3fe51f996662d9d4ull }, + { 0x3ffef9db22d0e560ull, 0x3fe523d4eb62a518ull }, + { 0x3ffefdf3b645a1cbull, 0x3fe5280fe1256daaull }, + { 0x3fff020c49ba5e35ull, 0x3fe52c4a47d10d9bull }, + { 0x3fff0624dd2f1aa0ull, 0x3fe530841f8b5004ull }, + { 0x3fff0a3d70a3d70aull, 0x3fe534bd6879f103ull }, + { 0x3fff0e5604189375ull, 0x3fe538f622c29dc8ull }, + { 0x3fff126e978d4fdfull, 0x3fe53d2e4e8af49aull }, + { 0x3fff16872b020c4aull, 0x3fe54165ebf884e4ull }, + { 0x3fff1a9fbe76c8b4ull, 0x3fe5459cfb30cf35ull }, + { 0x3fff1eb851eb851full, 0x3fe549d37c594551ull }, + { 0x3fff22d0e5604189ull, 0x3fe54e096f974a2eull }, + { 0x3fff26e978d4fdf4ull, 0x3fe5523ed5103208ull }, + { 0x3fff2b020c49ba5eull, 0x3fe55673ace9425dull }, + { 0x3fff2f1a9fbe76c9ull, 0x3fe55aa7f747b200ull }, + { 0x3fff333333333333ull, 0x3fe55edbb450a914ull }, + { 0x3fff374bc6a7ef9eull, 0x3fe5630ee4294121ull }, + { 0x3fff3b645a1cac08ull, 0x3fe5674186f68511ull }, + { 0x3fff3f7ced916873ull, 0x3fe56b739cdd713full }, + { 0x3fff4395810624ddull, 0x3fe56fa52602f379ull }, + { 0x3fff47ae147ae148ull, 0x3fe573d6228beb0cull }, + { 0x3fff4bc6a7ef9db2ull, 0x3fe57806929d28c9ull }, + { 0x3fff4fdf3b645a1dull, 0x3fe57c36765b6f0full }, + { 0x3fff53f7ced91687ull, 0x3fe58065cdeb71ceull }, + { 0x3fff5810624dd2f2ull, 0x3fe584949971d694ull }, + { 0x3fff5c28f5c28f5cull, 0x3fe588c2d913348full }, + { 0x3fff604189374bc7ull, 0x3fe58cf08cf4149eull }, + { 0x3fff645a1cac0831ull, 0x3fe5911db538f14aull }, + { 0x3fff6872b020c49cull, 0x3fe5954a520636dbull }, + { 0x3fff6c8b43958106ull, 0x3fe5997663804356ull }, + { 0x3fff70a3d70a3d71ull, 0x3fe59da1e9cb668eull }, + { 0x3fff74bc6a7ef9dbull, 0x3fe5a1cce50be21eull }, + { 0x3fff78d4fdf3b646ull, 0x3fe5a5f75565e980ull }, + { 0x3fff7ced916872b0ull, 0x3fe5aa213afda206ull }, + { 0x3fff810624dd2f1bull, 0x3fe5ae4a95f722eeull }, + { 0x3fff851eb851eb85ull, 0x3fe5b2736676755cull }, + { 0x3fff89374bc6a7f0ull, 0x3fe5b69bac9f946full }, + { 0x3fff8d4fdf3b645aull, 0x3fe5bac368966d3eull }, + { 0x3fff916872b020c5ull, 0x3fe5beea9a7edee6ull }, + { 0x3fff95810624dd2full, 0x3fe5c311427cba8aull }, + { 0x3fff99999999999aull, 0x3fe5c73760b3c364ull }, + { 0x3fff9db22d0e5604ull, 0x3fe5cb5cf547aec1ull }, + { 0x3fffa1cac083126full, 0x3fe5cf82005c2414ull }, + { 0x3fffa5e353f7ced9ull, 0x3fe5d3a68214bcf1ull }, + { 0x3fffa9fbe76c8b44ull, 0x3fe5d7ca7a950521ull }, + { 0x3fffae147ae147aeull, 0x3fe5dbedea007a9cull }, + { 0x3fffb22d0e560419ull, 0x3fe5e010d07a8d9cull }, + { 0x3fffb645a1cac083ull, 0x3fe5e4332e26a09aull }, + { 0x3fffba5e353f7ceeull, 0x3fe5e8550328085full }, + { 0x3fffbe76c8b43958ull, 0x3fe5ec764fa20c01ull }, + { 0x3fffc28f5c28f5c3ull, 0x3fe5f09713b7e4f2ull }, + { 0x3fffc6a7ef9db22dull, 0x3fe5f4b74f8cbf02ull }, + { 0x3fffcac083126e98ull, 0x3fe5f8d70343b86cull }, + { 0x3fffced916872b02ull, 0x3fe5fcf62effe1d3ull }, + { 0x3fffd2f1a9fbe76dull, 0x3fe60114d2e43e54ull }, + { 0x3fffd70a3d70a3d7ull, 0x3fe60532ef13c385ull }, + { 0x3fffdb22d0e56042ull, 0x3fe6095083b15982ull }, + { 0x3fffdf3b645a1cacull, 0x3fe60d6d90dfdaeeull }, + { 0x3fffe353f7ced917ull, 0x3fe6118a16c214feull }, + { 0x3fffe76c8b439581ull, 0x3fe615a6157ac77eull }, + { 0x3fffeb851eb851ecull, 0x3fe619c18d2ca4dbull }, + { 0x3fffef9db22d0e56ull, 0x3fe61ddc7dfa5223ull }, + { 0x3ffff3b645a1cac1ull, 0x3fe621f6e8066716ull }, + { 0x3ffff7ced916872bull, 0x3fe62610cb736e21ull }, + { 0x3ffffbe76c8b4396ull, 0x3fe62a2a2863e472ull }, + { 0x4000000000000000ull, 0x3fe62e42fefa39efull }, + { 0x4000020c49ba5e35ull, 0x3fe6325b4f58d14eull }, + { 0x400004189374bc6bull, 0x3fe6367319a20010ull }, + { 0x40000624dd2f1aa0ull, 0x3fe63a8a5df80e87ull }, + { 0x4000083126e978d5ull, 0x3fe63ea11c7d37e6ull }, + { 0x40000a3d70a3d70aull, 0x3fe642b75553aa43ull }, + { 0x40000c49ba5e3540ull, 0x3fe646cd089d869bull }, + { 0x40000e5604189375ull, 0x3fe64ae2367ce0d8ull }, + { 0x400010624dd2f1aaull, 0x3fe64ef6df13bfdfull }, + { 0x4000126e978d4fdfull, 0x3fe6530b02841d91ull }, + { 0x4000147ae147ae15ull, 0x3fe6571ea0efe6d5ull }, + { 0x400016872b020c4aull, 0x3fe65b31ba78fb95ull }, + { 0x4000189374bc6a7full, 0x3fe65f444f412ed4ull }, + { 0x40001a9fbe76c8b4ull, 0x3fe663565f6a46a9ull }, + { 0x40001cac083126eaull, 0x3fe66767eb15fc4bull }, + { 0x40001eb851eb851full, 0x3fe66b78f265fc11ull }, + { 0x400020c49ba5e354ull, 0x3fe66f89757be583ull }, + { 0x400022d0e5604189ull, 0x3fe6739974794b58ull }, + { 0x400024dd2f1a9fbfull, 0x3fe677a8ef7fb382ull }, + { 0x400026e978d4fdf4ull, 0x3fe67bb7e6b0972bull }, + { 0x400028f5c28f5c29ull, 0x3fe67fc65a2d62c9ull }, + { 0x40002b020c49ba5eull, 0x3fe683d44a17761bull }, + { 0x40002d0e56041894ull, 0x3fe687e1b6902435ull }, + { 0x40002f1a9fbe76c9ull, 0x3fe68bee9fb8b37cull }, + { 0x40003126e978d4feull, 0x3fe68ffb05b25dbbull }, + { 0x4000333333333333ull, 0x3fe69406e89e5020ull }, + { 0x4000353f7ced9169ull, 0x3fe69812489dab47ull }, + { 0x4000374bc6a7ef9eull, 0x3fe69c1d25d18338ull }, + { 0x4000395810624dd3ull, 0x3fe6a027805adf7aull }, + { 0x40003b645a1cac08ull, 0x3fe6a431585abb0eull }, + { 0x40003d70a3d70a3eull, 0x3fe6a83aadf2047full }, + { 0x40003f7ced916873ull, 0x3fe6ac4381419ddcull }, + { 0x40004189374bc6a8ull, 0x3fe6b04bd26a5cccull }, + { 0x40004395810624ddull, 0x3fe6b453a18d0a8bull }, + { 0x400045a1cac08313ull, 0x3fe6b85aeeca63f6ull }, + { 0x400047ae147ae148ull, 0x3fe6bc61ba431989ull }, + { 0x400049ba5e353f7dull, 0x3fe6c0680417cf6full }, + { 0x40004bc6a7ef9db2ull, 0x3fe6c46dcc691d81ull }, + { 0x40004dd2f1a9fbe8ull, 0x3fe6c87313578f55ull }, + { 0x40004fdf3b645a1dull, 0x3fe6cc77d903a433ull }, + { 0x400051eb851eb852ull, 0x3fe6d07c1d8dcf2full }, + { 0x400053f7ced91687ull, 0x3fe6d47fe1167725ull }, + { 0x40005604189374bdull, 0x3fe6d88323bdf6c1ull }, + { 0x40005810624dd2f2ull, 0x3fe6dc85e5a49c7full }, + { 0x40005a1cac083127ull, 0x3fe6e08826eaaabdull }, + { 0x40005c28f5c28f5cull, 0x3fe6e489e7b057b9ull }, + { 0x40005e353f7ced92ull, 0x3fe6e88b2815cd9aull }, + { 0x4000604189374bc7ull, 0x3fe6ec8be83b2a70ull }, + { 0x4000624dd2f1a9fcull, 0x3fe6f08c28408045ull }, + { 0x4000645a1cac0831ull, 0x3fe6f48be845d51aull }, + { 0x4000666666666666ull, 0x3fe6f88b286b22f1ull }, + { 0x40006872b020c49cull, 0x3fe6fc89e8d057d2ull }, + { 0x40006a7ef9db22d1ull, 0x3fe70088299555ceull }, + { 0x40006c8b43958106ull, 0x3fe70485ead9f30bull }, + { 0x40006e978d4fdf3bull, 0x3fe708832cbdf9c8ull }, + { 0x400070a3d70a3d71ull, 0x3fe70c7fef612861ull }, + { 0x400072b020c49ba6ull, 0x3fe7107c32e33151ull }, + { 0x400074bc6a7ef9dbull, 0x3fe71477f763bb43ull }, + { 0x400076c8b4395810ull, 0x3fe718733d02610dull }, + { 0x400078d4fdf3b646ull, 0x3fe71c6e03deb1c1ull }, + { 0x40007ae147ae147bull, 0x3fe720684c1830a3ull }, + { 0x40007ced916872b0ull, 0x3fe7246215ce553dull }, + { 0x40007ef9db22d0e5ull, 0x3fe7285b61208b62ull }, + { 0x4000810624dd2f1bull, 0x3fe72c542e2e3330ull }, + { 0x400083126e978d50ull, 0x3fe7304c7d16a114ull }, + { 0x4000851eb851eb85ull, 0x3fe734444df91dd9ull }, + { 0x4000872b020c49baull, 0x3fe7383ba0f4e6a4ull }, + { 0x400089374bc6a7f0ull, 0x3fe73c3276292d03ull }, + { 0x40008b4395810625ull, 0x3fe74028cdb516e5ull }, + { 0x40008d4fdf3b645aull, 0x3fe7441ea7b7beb0ull }, + { 0x40008f5c28f5c28full, 0x3fe748140450333eull }, + { 0x4000916872b020c5ull, 0x3fe74c08e39d77e3ull }, + { 0x40009374bc6a7efaull, 0x3fe74ffd45be846full }, + { 0x400095810624dd2full, 0x3fe753f12ad24540ull }, + { 0x4000978d4fdf3b64ull, 0x3fe757e492f79b3bull }, + { 0x400099999999999aull, 0x3fe75bd77e4d5bd9ull }, + { 0x40009ba5e353f7cfull, 0x3fe75fc9ecf25127ull }, + { 0x40009db22d0e5604ull, 0x3fe763bbdf0539d0ull }, + { 0x40009fbe76c8b439ull, 0x3fe767ad54a4c925ull }, + { 0x4000a1cac083126full, 0x3fe76b9e4defa71cull }, + { 0x4000a3d70a3d70a4ull, 0x3fe76f8ecb047055ull }, + { 0x4000a5e353f7ced9ull, 0x3fe7737ecc01b62aull }, + { 0x4000a7ef9db22d0eull, 0x3fe7776e5105fea9ull }, + { 0x4000a9fbe76c8b44ull, 0x3fe77b5d5a2fc4a3ull }, + { 0x4000ac083126e979ull, 0x3fe77f4be79d77a5ull }, + { 0x4000ae147ae147aeull, 0x3fe78339f96d7c0cull }, + { 0x4000b020c49ba5e3ull, 0x3fe787278fbe2b03ull }, + { 0x4000b22d0e560419ull, 0x3fe78b14aaadd288ull }, + { 0x4000b4395810624eull, 0x3fe78f014a5ab571ull }, + { 0x4000b645a1cac083ull, 0x3fe792ed6ee30b76ull }, + { 0x4000b851eb851eb8ull, 0x3fe796d918650134ull }, + { 0x4000ba5e353f7ceeull, 0x3fe79ac446feb832ull }, + { 0x4000bc6a7ef9db23ull, 0x3fe79eaeface46e2ull }, + { 0x4000be76c8b43958ull, 0x3fe7a29933f1b8b1ull }, + { 0x4000c083126e978dull, 0x3fe7a682f2870e03ull }, + { 0x4000c28f5c28f5c3ull, 0x3fe7aa6c36ac3c40ull }, + { 0x4000c49ba5e353f8ull, 0x3fe7ae55007f2dceull }, + { 0x4000c6a7ef9db22dull, 0x3fe7b23d501dc224ull }, + { 0x4000c8b439581062ull, 0x3fe7b62525a5cdc5ull }, + { 0x4000cac083126e98ull, 0x3fe7ba0c81351a4eull }, + { 0x4000cccccccccccdull, 0x3fe7bdf362e9666eull }, + { 0x4000ced916872b02ull, 0x3fe7c1d9cae065fdull }, + { 0x4000d0e560418937ull, 0x3fe7c5bfb937c1f2ull }, + { 0x4000d2f1a9fbe76dull, 0x3fe7c9a52e0d1874ull }, + { 0x4000d4fdf3b645a2ull, 0x3fe7cd8a297dfcd2ull }, + { 0x4000d70a3d70a3d7ull, 0x3fe7d16eaba7f797ull }, + { 0x4000d916872b020cull, 0x3fe7d552b4a88685ull }, + { 0x4000db22d0e56042ull, 0x3fe7d936449d1ca0ull }, + { 0x4000dd2f1a9fbe77ull, 0x3fe7dd195ba3222cull }, + { 0x4000df3b645a1cacull, 0x3fe7e0fbf9d7f4baull }, + { 0x4000e147ae147ae1ull, 0x3fe7e4de1f58e72aull }, + { 0x4000e353f7ced917ull, 0x3fe7e8bfcc4341b3ull }, + { 0x4000e5604189374cull, 0x3fe7eca100b441deull }, + { 0x4000e76c8b439581ull, 0x3fe7f081bcc91a99ull }, + { 0x4000e978d4fdf3b6ull, 0x3fe7f462009ef434ull }, + { 0x4000eb851eb851ecull, 0x3fe7f841cc52ec69ull }, + { 0x4000ed916872b021ull, 0x3fe7fc212002165dull }, + { 0x4000ef9db22d0e56ull, 0x3fe7fffffbc97aaaull }, + { 0x4000f1a9fbe76c8bull, 0x3fe803de5fc61764ull }, + { 0x4000f3b645a1cac1ull, 0x3fe807bc4c14e01bull }, + { 0x4000f5c28f5c28f6ull, 0x3fe80b99c0d2bddeull }, + { 0x4000f7ced916872bull, 0x3fe80f76be1c8f48ull }, + { 0x4000f9db22d0e560ull, 0x3fe81353440f287full }, + { 0x4000fbe76c8b4396ull, 0x3fe8172f52c7533dull }, + { 0x4000fdf3b645a1cbull, 0x3fe81b0aea61cecdull }, + { 0x4001000000000000ull, 0x3fe81ee60afb501aull }, + { 0x4001020c49ba5e35ull, 0x3fe822c0b4b081aeull }, + { 0x400104189374bc6bull, 0x3fe8269ae79e03bcull }, + { 0x40010624dd2f1aa0ull, 0x3fe82a74a3e06c1cull }, + { 0x4001083126e978d5ull, 0x3fe82e4de9944658ull }, + { 0x40010a3d70a3d70aull, 0x3fe83226b8d613b0ull }, + { 0x40010c49ba5e3540ull, 0x3fe835ff11c24b1full }, + { 0x40010e5604189375ull, 0x3fe839d6f4755957ull }, + { 0x400110624dd2f1aaull, 0x3fe83dae610ba0d5ull }, + { 0x4001126e978d4fdfull, 0x3fe8418557a179dcull }, + { 0x4001147ae147ae15ull, 0x3fe8455bd853327bull }, + { 0x400116872b020c4aull, 0x3fe84931e33d0e93ull }, + { 0x4001189374bc6a7full, 0x3fe84d07787b47ddull }, + { 0x40011a9fbe76c8b4ull, 0x3fe850dc982a0dedull }, + { 0x40011cac083126eaull, 0x3fe854b14265863cull }, + { 0x40011eb851eb851full, 0x3fe858857749cc1full }, + { 0x400120c49ba5e354ull, 0x3fe85c5936f2f0deull }, + { 0x400122d0e5604189ull, 0x3fe8602c817cfbadull }, + { 0x400124dd2f1a9fbfull, 0x3fe863ff5703e9b6ull }, + { 0x400126e978d4fdf4ull, 0x3fe867d1b7a3ae15ull }, + { 0x400128f5c28f5c29ull, 0x3fe86ba3a37831edull }, + { 0x40012b020c49ba5eull, 0x3fe86f751a9d545dull }, + { 0x40012d0e56041894ull, 0x3fe873461d2eea8eull }, + { 0x40012f1a9fbe76c9ull, 0x3fe87716ab48bfb3ull }, + { 0x40013126e978d4feull, 0x3fe87ae6c506950full }, + { 0x4001333333333333ull, 0x3fe87eb66a8421ffull }, + { 0x4001353f7ced9169ull, 0x3fe882859bdd13f6ull }, + { 0x4001374bc6a7ef9eull, 0x3fe88654592d0e84ull }, + { 0x4001395810624dd3ull, 0x3fe88a22a28fab62ull }, + { 0x40013b645a1cac08ull, 0x3fe88df078207a6cull }, + { 0x40013d70a3d70a3eull, 0x3fe891bdd9fb01b1ull }, + { 0x40013f7ced916873ull, 0x3fe8958ac83abd69ull }, + { 0x40014189374bc6a8ull, 0x3fe8995742fb2009ull }, + { 0x40014395810624ddull, 0x3fe89d234a57923eull }, + { 0x400145a1cac08313ull, 0x3fe8a0eede6b72f7ull }, + { 0x400147ae147ae148ull, 0x3fe8a4b9ff521762ull }, + { 0x400149ba5e353f7dull, 0x3fe8a884ad26cafaull }, + { 0x40014bc6a7ef9db2ull, 0x3fe8ac4ee804cf86ull }, + { 0x40014dd2f1a9fbe8ull, 0x3fe8b018b0075d22ull }, + { 0x40014fdf3b645a1dull, 0x3fe8b3e20549a239ull }, + { 0x400151eb851eb852ull, 0x3fe8b7aae7e6c396ull }, + { 0x400153f7ced91687ull, 0x3fe8bb7357f9dc66ull }, + { 0x40015604189374bdull, 0x3fe8bf3b559dfe36ull }, + { 0x40015810624dd2f2ull, 0x3fe8c302e0ee30fbull }, + { 0x40015a1cac083127ull, 0x3fe8c6c9fa05731aull }, + { 0x40015c28f5c28f5cull, 0x3fe8ca90a0feb96aull }, + { 0x40015e353f7ced92ull, 0x3fe8ce56d5f4ef38ull }, + { 0x4001604189374bc7ull, 0x3fe8d21c9902f647ull }, + { 0x4001624dd2f1a9fcull, 0x3fe8d5e1ea43a6e0ull }, + { 0x4001645a1cac0831ull, 0x3fe8d9a6c9d1cfcbull }, + { 0x4001666666666667ull, 0x3fe8dd6b37c8365dull }, + { 0x40016872b020c49cull, 0x3fe8e12f34419670ull }, + { 0x40016a7ef9db22d1ull, 0x3fe8e4f2bf58a277ull }, + { 0x40016c8b43958106ull, 0x3fe8e8b5d9280375ull }, + { 0x40016e978d4fdf3bull, 0x3fe8ec7881ca590aull }, + { 0x400170a3d70a3d71ull, 0x3fe8f03ab95a3976ull }, + { 0x400172b020c49ba6ull, 0x3fe8f3fc7ff23194ull }, + { 0x400174bc6a7ef9dbull, 0x3fe8f7bdd5acc4efull }, + { 0x400176c8b4395810ull, 0x3fe8fb7ebaa46db8ull }, + { 0x400178d4fdf3b646ull, 0x3fe8ff3f2ef39cd4ull }, + { 0x40017ae147ae147bull, 0x3fe902ff32b4b9d7ull }, + { 0x40017ced916872b0ull, 0x3fe906bec6022312ull }, + { 0x40017ef9db22d0e5ull, 0x3fe90a7de8f62d91ull }, + { 0x4001810624dd2f1bull, 0x3fe90e3c9bab2526ull }, + { 0x400183126e978d50ull, 0x3fe911fade3b4c5eull }, + { 0x4001851eb851eb85ull, 0x3fe915b8b0c0dc9aull }, + { 0x4001872b020c49baull, 0x3fe9197613560606ull }, + { 0x400189374bc6a7f0ull, 0x3fe91d330614efa2ull }, + { 0x40018b4395810625ull, 0x3fe920ef8917b73full }, + { 0x40018d4fdf3b645aull, 0x3fe924ab9c78718full }, + { 0x40018f5c28f5c28full, 0x3fe9286740512a23ull }, + { 0x4001916872b020c5ull, 0x3fe92c2274bbe36eull }, + { 0x40019374bc6a7efaull, 0x3fe92fdd39d296caull }, + { 0x400195810624dd2full, 0x3fe933978faf347full }, + { 0x4001978d4fdf3b64ull, 0x3fe93751766ba3c4ull }, + { 0x400199999999999aull, 0x3fe93b0aee21c2c9ull }, + { 0x40019ba5e353f7cfull, 0x3fe93ec3f6eb66aeull }, + { 0x40019db22d0e5604ull, 0x3fe9427c90e25b97ull }, + { 0x40019fbe76c8b439ull, 0x3fe94634bc2064a7ull }, + { 0x4001a1cac083126full, 0x3fe949ec78bf3c0aull }, + { 0x4001a3d70a3d70a4ull, 0x3fe94da3c6d892edull }, + { 0x4001a5e353f7ced9ull, 0x3fe9515aa6861192ull }, + { 0x4001a7ef9db22d0eull, 0x3fe9551117e1574aull }, + { 0x4001a9fbe76c8b44ull, 0x3fe958c71b03fa80ull }, + { 0x4001ac083126e979ull, 0x3fe95c7cb00788b1ull }, + { 0x4001ae147ae147aeull, 0x3fe96031d705867full }, + { 0x4001b020c49ba5e3ull, 0x3fe963e690176facull }, + { 0x4001b22d0e560419ull, 0x3fe9679adb56b724ull }, + { 0x4001b4395810624eull, 0x3fe96b4eb8dcc6f5ull }, + { 0x4001b645a1cac083ull, 0x3fe96f0228c30063ull }, + { 0x4001b851eb851eb8ull, 0x3fe972b52b22bbe4ull }, + { 0x4001ba5e353f7ceeull, 0x3fe97667c0154924ull }, + { 0x4001bc6a7ef9db23ull, 0x3fe97a19e7b3ef06ull }, + { 0x4001be76c8b43958ull, 0x3fe97dcba217ebb2ull }, + { 0x4001c083126e978dull, 0x3fe9817cef5a748full }, + { 0x4001c28f5c28f5c3ull, 0x3fe9852dcf94b64eull }, + { 0x4001c49ba5e353f8ull, 0x3fe988de42dfd4e6ull }, + { 0x4001c6a7ef9db22dull, 0x3fe98c8e4954eba2ull }, + { 0x4001c8b439581062ull, 0x3fe9903de30d0d1dull }, + { 0x4001cac083126e98ull, 0x3fe993ed1021434eull }, + { 0x4001cccccccccccdull, 0x3fe9979bd0aa8f7eull }, + { 0x4001ced916872b02ull, 0x3fe99b4a24c1ea5cull }, + { 0x4001d0e560418937ull, 0x3fe99ef80c8043f8ull }, + { 0x4001d2f1a9fbe76dull, 0x3fe9a2a587fe83cbull }, + { 0x4001d4fdf3b645a2ull, 0x3fe9a652975588b3ull }, + { 0x4001d70a3d70a3d7ull, 0x3fe9a9ff3a9e2903ull }, + { 0x4001d916872b020cull, 0x3fe9adab71f1327eull }, + { 0x4001db22d0e56042ull, 0x3fe9b1573d676a5eull }, + { 0x4001dd2f1a9fbe77ull, 0x3fe9b5029d198d57ull }, + { 0x4001df3b645a1cacull, 0x3fe9b8ad91204f9bull }, + { 0x4001e147ae147ae1ull, 0x3fe9bc5819945ce1ull }, + { 0x4001e353f7ced917ull, 0x3fe9c002368e5865ull }, + { 0x4001e5604189374cull, 0x3fe9c3abe826dcebull }, + { 0x4001e76c8b439581ull, 0x3fe9c7552e767cc7ull }, + { 0x4001e978d4fdf3b6ull, 0x3fe9cafe0995c1dfull }, + { 0x4001eb851eb851ecull, 0x3fe9cea6799d2dafull }, + { 0x4001ed916872b021ull, 0x3fe9d24e7ea5394bull }, + { 0x4001ef9db22d0e56ull, 0x3fe9d5f618c65565ull }, + { 0x4001f1a9fbe76c8bull, 0x3fe9d99d4818ea50ull }, + { 0x4001f3b645a1cac1ull, 0x3fe9dd440cb55808ull }, + { 0x4001f5c28f5c28f6ull, 0x3fe9e0ea66b3f62bull }, + { 0x4001f7ced916872bull, 0x3fe9e490562d1407ull }, + { 0x4001f9db22d0e560ull, 0x3fe9e835db38f89dull }, + { 0x4001fbe76c8b4396ull, 0x3fe9ebdaf5efe2a0ull }, + { 0x4001fdf3b645a1cbull, 0x3fe9ef7fa66a0877ull }, + { 0x4002000000000000ull, 0x3fe9f323ecbf984cull }, + { 0x4002020c49ba5e35ull, 0x3fe9f6c7c908b804ull }, + { 0x400204189374bc6bull, 0x3fe9fa6b3b5d854bull }, + { 0x40020624dd2f1aa0ull, 0x3fe9fe0e43d6158eull }, + { 0x4002083126e978d5ull, 0x3fea01b0e28a760bull }, + { 0x40020a3d70a3d70aull, 0x3fea05531792abcaull }, + { 0x40020c49ba5e3540ull, 0x3fea08f4e306b3aaull }, + { 0x40020e5604189375ull, 0x3fea0c9644fe825aull }, + { 0x400210624dd2f1aaull, 0x3fea10373d920468ull }, + { 0x4002126e978d4fdfull, 0x3fea13d7ccd91e3bull }, + { 0x4002147ae147ae15ull, 0x3fea1777f2ebac22ull }, + { 0x400216872b020c4aull, 0x3fea1b17afe18246ull }, + { 0x4002189374bc6a7full, 0x3fea1eb703d26cc1ull }, + { 0x40021a9fbe76c8b4ull, 0x3fea2255eed62f97ull }, + { 0x40021cac083126eaull, 0x3fea25f4710486bcull }, + { 0x40021eb851eb851full, 0x3fea29928a752613ull }, + { 0x400220c49ba5e354ull, 0x3fea2d303b3fb97bull }, + { 0x400222d0e5604189ull, 0x3fea30cd837be4cdull }, + { 0x400224dd2f1a9fbfull, 0x3fea346a634143e1ull }, + { 0x400226e978d4fdf4ull, 0x3fea3806daa76a8aull }, + { 0x400228f5c28f5c29ull, 0x3fea3ba2e9c5e4aaull }, + { 0x40022b020c49ba5eull, 0x3fea3f3e90b43625ull }, + { 0x40022d0e56041894ull, 0x3fea42d9cf89daf3ull }, + { 0x40022f1a9fbe76c9ull, 0x3fea4674a65e4711ull }, + { 0x40023126e978d4feull, 0x3fea4a0f1548e69bull }, + { 0x4002333333333333ull, 0x3fea4da91c611dbeull }, + { 0x4002353f7ced9169ull, 0x3fea5142bbbe48c6ull }, + { 0x4002374bc6a7ef9eull, 0x3fea54dbf377bc18ull }, + { 0x4002395810624dd3ull, 0x3fea5874c3a4c43full }, + { 0x40023b645a1cac08ull, 0x3fea5c0d2c5ca5edull }, + { 0x40023d70a3d70a3eull, 0x3fea5fa52db69dfaull }, + { 0x40023f7ced916873ull, 0x3fea633cc7c9e16aull }, + { 0x40024189374bc6a8ull, 0x3fea66d3faad9d74ull }, + { 0x40024395810624ddull, 0x3fea6a6ac678f781ull }, + { 0x400245a1cac08313ull, 0x3fea6e012b430d33ull }, + { 0x400247ae147ae148ull, 0x3fea71972922f462ull }, + { 0x400249ba5e353f7dull, 0x3fea752cc02fbb28ull }, + { 0x40024bc6a7ef9db2ull, 0x3fea78c1f08067e1ull }, + { 0x40024dd2f1a9fbe8ull, 0x3fea7c56ba2bf92full }, + { 0x40024fdf3b645a1dull, 0x3fea7feb1d4965f5ull }, + { 0x400251eb851eb852ull, 0x3fea837f19ef9d69ull }, + { 0x400253f7ced91687ull, 0x3fea8712b0358710ull }, + { 0x40025604189374bdull, 0x3fea8aa5e03202c2ull }, + { 0x40025810624dd2f2ull, 0x3fea8e38a9fbe8a8ull }, + { 0x40025a1cac083127ull, 0x3fea91cb0daa094aull }, + { 0x40025c28f5c28f5cull, 0x3fea955d0b532d8bull }, + { 0x40025e353f7ced92ull, 0x3fea98eea30e16b2ull }, + { 0x4002604189374bc7ull, 0x3fea9c7fd4f17e62ull }, + { 0x4002624dd2f1a9fcull, 0x3feaa010a11416adull }, + { 0x4002645a1cac0831ull, 0x3feaa3a1078c8a0dull }, + { 0x4002666666666667ull, 0x3feaa73108717b6bull }, + { 0x40026872b020c49cull, 0x3feaaac0a3d9861dull }, + { 0x40026a7ef9db22d1ull, 0x3feaae4fd9db3df4ull }, + { 0x40026c8b43958106ull, 0x3feab1deaa8d2f34ull }, + { 0x40026e978d4fdf3cull, 0x3feab56d1605dea3ull }, + { 0x400270a3d70a3d71ull, 0x3feab8fb1c5bc97dull }, + { 0x400272b020c49ba6ull, 0x3feabc88bda56586ull }, + { 0x400274bc6a7ef9dbull, 0x3feac015f9f92107ull }, + { 0x400276c8b4395810ull, 0x3feac3a2d16d62d1ull }, + { 0x400278d4fdf3b646ull, 0x3feac72f44188a44ull }, + { 0x40027ae147ae147bull, 0x3feacabb5210ef47ull }, + { 0x40027ced916872b0ull, 0x3feace46fb6ce25cull }, + { 0x40027ef9db22d0e5ull, 0x3fead1d24042ac99ull }, + { 0x4002810624dd2f1bull, 0x3fead55d20a88faeull }, + { 0x400283126e978d50ull, 0x3fead8e79cb4c5e3ull }, + { 0x4002851eb851eb85ull, 0x3feadc71b47d8224ull }, + { 0x4002872b020c49baull, 0x3feadffb6818f000ull }, + { 0x400289374bc6a7f0ull, 0x3feae384b79d33adull }, + { 0x40028b4395810625ull, 0x3feae70da3206a06ull }, + { 0x40028d4fdf3b645aull, 0x3feaea962ab8a897ull }, + { 0x40028f5c28f5c28full, 0x3feaee1e4e7bfd9cull }, + { 0x4002916872b020c5ull, 0x3feaf1a60e807004ull }, + { 0x40029374bc6a7efaull, 0x3feaf52d6adbff71ull }, + { 0x400295810624dd2full, 0x3feaf8b463a4a444ull }, + { 0x4002978d4fdf3b64ull, 0x3feafc3af8f04f97ull }, + { 0x400299999999999aull, 0x3feaffc12ad4eb49ull }, + { 0x40029ba5e353f7cfull, 0x3feb0346f96859f5ull }, + { 0x40029db22d0e5604ull, 0x3feb06cc64c07702ull }, + { 0x40029fbe76c8b439ull, 0x3feb0a516cf316a0ull }, + { 0x4002a1cac083126full, 0x3feb0dd6121605ccull }, + { 0x4002a3d70a3d70a4ull, 0x3feb115a543f0a50ull }, + { 0x4002a5e353f7ced9ull, 0x3feb14de3383e2ccull }, + { 0x4002a7ef9db22d0eull, 0x3feb1861affa46b5ull }, + { 0x4002a9fbe76c8b44ull, 0x3feb1be4c9b7e65eull }, + { 0x4002ac083126e979ull, 0x3feb1f6780d26aeeull }, + { 0x4002ae147ae147aeull, 0x3feb22e9d55f7673ull }, + { 0x4002b020c49ba5e3ull, 0x3feb266bc774a3dbull }, + { 0x4002b22d0e560419ull, 0x3feb29ed572786fcull }, + { 0x4002b4395810624eull, 0x3feb2d6e848dac91ull }, + { 0x4002b645a1cac083ull, 0x3feb30ef4fbc9a45ull }, + { 0x4002b851eb851eb8ull, 0x3feb346fb8c9ceb1ull }, + { 0x4002ba5e353f7ceeull, 0x3feb37efbfcac162ull }, + { 0x4002bc6a7ef9db23ull, 0x3feb3b6f64d4e2d7ull }, + { 0x4002be76c8b43958ull, 0x3feb3eeea7fd9c89ull }, + { 0x4002c083126e978dull, 0x3feb426d895a50f0ull }, + { 0x4002c28f5c28f5c3ull, 0x3feb45ec09005b7full }, + { 0x4002c49ba5e353f8ull, 0x3feb496a270510aaull }, + { 0x4002c6a7ef9db22dull, 0x3feb4ce7e37dbdecull }, + { 0x4002c8b439581062ull, 0x3feb50653e7fa9c9ull }, + { 0x4002cac083126e98ull, 0x3feb53e2382013ceull }, + { 0x4002cccccccccccdull, 0x3feb575ed0743494ull }, + { 0x4002ced916872b02ull, 0x3feb5adb07913dc9ull }, + { 0x4002d0e560418937ull, 0x3feb5e56dd8c5a2dull }, + { 0x4002d2f1a9fbe76dull, 0x3feb61d2527aad98ull }, + { 0x4002d4fdf3b645a2ull, 0x3feb654d667154f8ull }, + { 0x4002d70a3d70a3d7ull, 0x3feb68c81985665dull }, + { 0x4002d916872b020cull, 0x3feb6c426bcbf0f5ull }, + { 0x4002db22d0e56042ull, 0x3feb6fbc5d59fd13ull }, + { 0x4002dd2f1a9fbe77ull, 0x3feb7335ee448c2bull }, + { 0x4002df3b645a1cacull, 0x3feb76af1ea098deull }, + { 0x4002e147ae147ae1ull, 0x3feb7a27ee8316f8ull }, + { 0x4002e353f7ced917ull, 0x3feb7da05e00f375ull }, + { 0x4002e5604189374cull, 0x3feb81186d2f147eull }, + { 0x4002e76c8b439581ull, 0x3feb84901c225977ull }, + { 0x4002e978d4fdf3b6ull, 0x3feb88076aef9af9ull }, + { 0x4002eb851eb851ecull, 0x3feb8b7e59abaad9ull }, + { 0x4002ed916872b021ull, 0x3feb8ef4e86b5424ull }, + { 0x4002ef9db22d0e56ull, 0x3feb926b17435b2full }, + { 0x4002f1a9fbe76c8bull, 0x3feb95e0e6487d8eull }, + { 0x4002f3b645a1cac1ull, 0x3feb9956558f721eull }, + { 0x4002f5c28f5c28f6ull, 0x3feb9ccb652ce901ull }, + { 0x4002f7ced916872bull, 0x3feba04015358baaull }, + { 0x4002f9db22d0e560ull, 0x3feba3b465bdfcd9ull }, + { 0x4002fbe76c8b4396ull, 0x3feba72856dad8a0ull }, + { 0x4002fdf3b645a1cbull, 0x3febaa9be8a0b464ull }, + { 0x4003000000000000ull, 0x3febae0f1b241ee4ull }, + { 0x4003020c49ba5e35ull, 0x3febb181ee79a039ull }, + { 0x400304189374bc6bull, 0x3febb4f462b5b9dcull }, + { 0x40030624dd2f1aa0ull, 0x3febb86677ece69full }, + { 0x4003083126e978d5ull, 0x3febbbd82e339abeull }, + { 0x40030a3d70a3d70aull, 0x3febbf49859e43d9ull }, + { 0x40030c49ba5e3540ull, 0x3febc2ba7e4148f9ull }, + { 0x40030e5604189375ull, 0x3febc62b18310a90ull }, + { 0x400310624dd2f1aaull, 0x3febc99b5381e281ull }, + { 0x4003126e978d4fdfull, 0x3febcd0b30482422ull }, + { 0x4003147ae147ae15ull, 0x3febd07aae981c3bull }, + { 0x400316872b020c4aull, 0x3febd3e9ce861108ull }, + { 0x4003189374bc6a7full, 0x3febd75890264243ull }, + { 0x40031a9fbe76c8b4ull, 0x3febdac6f38ce922ull }, + { 0x40031cac083126eaull, 0x3febde34f8ce385bull }, + { 0x40031eb851eb851full, 0x3febe1a29ffe5c21ull }, + { 0x400320c49ba5e354ull, 0x3febe50fe9317a31ull }, + { 0x400322d0e5604189ull, 0x3febe87cd47bb1cfull }, + { 0x400324dd2f1a9fbfull, 0x3febebe961f11bcaull }, + { 0x400326e978d4fdf4ull, 0x3febef5591a5ca78ull }, + { 0x400328f5c28f5c29ull, 0x3febf2c163adc9c8ull }, + { 0x40032b020c49ba5eull, 0x3febf62cd81d1f35ull }, + { 0x40032d0e56041894ull, 0x3febf997ef07c9d5ull }, + { 0x40032f1a9fbe76c9ull, 0x3febfd02a881c24full }, + { 0x40033126e978d4feull, 0x3fec006d049efaebull }, + { 0x4003333333333333ull, 0x3fec03d703735f8cull }, + { 0x4003353f7ced9169ull, 0x3fec0740a512d5b9ull }, + { 0x4003374bc6a7ef9eull, 0x3fec0aa9e9913c96ull }, + { 0x4003395810624dd3ull, 0x3fec0e12d1026cefull }, + { 0x40033b645a1cac08ull, 0x3fec117b5b7a393cull }, + { 0x40033d70a3d70a3eull, 0x3fec14e3890c6d9eull }, + { 0x40033f7ced916873ull, 0x3fec184b59cccfe1ull }, + { 0x40034189374bc6a8ull, 0x3fec1bb2cdcf1f86ull }, + { 0x40034395810624ddull, 0x3fec1f19e52715beull }, + { 0x400345a1cac08313ull, 0x3fec22809fe86574ull }, + { 0x400347ae147ae148ull, 0x3fec25e6fe26bb46ull }, + { 0x400349ba5e353f7dull, 0x3fec294cfff5bd92ull }, + { 0x40034bc6a7ef9db2ull, 0x3fec2cb2a5690c71ull }, + { 0x40034dd2f1a9fbe8ull, 0x3fec3017ee9441c1ull }, + { 0x40034fdf3b645a1dull, 0x3fec337cdb8af11dull }, + { 0x400351eb851eb852ull, 0x3fec36e16c60a7e9ull }, + { 0x400353f7ced91687ull, 0x3fec3a45a128ed53ull }, + { 0x40035604189374bdull, 0x3fec3da979f74254ull }, + { 0x40035810624dd2f2ull, 0x3fec410cf6df21afull }, + { 0x40035a1cac083127ull, 0x3fec447017f3fffaull }, + { 0x40035c28f5c28f5cull, 0x3fec47d2dd494ba1ull }, + { 0x40035e353f7ced92ull, 0x3fec4b3546f26ce2ull }, + { 0x4003604189374bc7ull, 0x3fec4e975502c5d1ull }, + { 0x4003624dd2f1a9fcull, 0x3fec51f9078db263ull }, + { 0x4003645a1cac0831ull, 0x3fec555a5ea68866ull }, + { 0x4003666666666667ull, 0x3fec58bb5a60978dull }, + { 0x40036872b020c49cull, 0x3fec5c1bfacf2964ull }, + { 0x40036a7ef9db22d1ull, 0x3fec5f7c40058167ull }, + { 0x40036c8b43958106ull, 0x3fec62dc2a16dcf3ull }, + { 0x40036e978d4fdf3cull, 0x3fec663bb9167355ull }, + { 0x400370a3d70a3d71ull, 0x3fec699aed1775beull }, + { 0x400372b020c49ba6ull, 0x3fec6cf9c62d0f58ull }, + { 0x400374bc6a7ef9dbull, 0x3fec7058446a653aull }, + { 0x400376c8b4395810ull, 0x3fec73b667e29670ull }, + { 0x400378d4fdf3b646ull, 0x3fec771430a8bc01ull }, + { 0x40037ae147ae147bull, 0x3fec7a719ecfe8e6ull }, + { 0x40037ced916872b0ull, 0x3fec7dceb26b2a1bull }, + { 0x40037ef9db22d0e5ull, 0x3fec812b6b8d8698ull }, + { 0x4003810624dd2f1bull, 0x3fec8487ca49ff59ull }, + { 0x400383126e978d50ull, 0x3fec87e3ceb38f57ull }, + { 0x4003851eb851eb85ull, 0x3fec8b3f78dd2b99ull }, + { 0x4003872b020c49baull, 0x3fec8e9ac8d9c32bull }, + { 0x400389374bc6a7f0ull, 0x3fec91f5bebc3f27ull }, + { 0x40038b4395810625ull, 0x3fec95505a9782aeull }, + { 0x40038d4fdf3b645aull, 0x3fec98aa9c7e6af9ull }, + { 0x40038f5c28f5c28full, 0x3fec9c048483cf51ull }, + { 0x4003916872b020c5ull, 0x3fec9f5e12ba8116ull }, + { 0x40039374bc6a7efaull, 0x3feca2b747354bb9ull }, + { 0x400395810624dd2full, 0x3feca6102206f4ccull }, + { 0x4003978d4fdf3b64ull, 0x3feca968a3423bfdull }, + { 0x400399999999999aull, 0x3fecacc0caf9db19ull }, + { 0x40039ba5e353f7cfull, 0x3fecb01899408609ull }, + { 0x40039db22d0e5604ull, 0x3fecb3700e28eae2ull }, + { 0x40039fbe76c8b439ull, 0x3fecb6c729c5b1dbull }, + { 0x4003a1cac083126full, 0x3fecba1dec297d57ull }, + { 0x4003a3d70a3d70a4ull, 0x3fecbd745566e9dfull }, + { 0x4003a5e353f7ced9ull, 0x3fecc0ca65908e2eull }, + { 0x4003a7ef9db22d0eull, 0x3fecc4201cb8fb30ull }, + { 0x4003a9fbe76c8b44ull, 0x3fecc7757af2bc01ull }, + { 0x4003ac083126e979ull, 0x3feccaca805055f3ull }, + { 0x4003ae147ae147aeull, 0x3fecce1f2ce4488full }, + { 0x4003b020c49ba5e3ull, 0x3fecd17380c10d99ull }, + { 0x4003b22d0e560419ull, 0x3fecd4c77bf91913ull }, + { 0x4003b4395810624eull, 0x3fecd81b1e9ed937ull }, + { 0x4003b645a1cac083ull, 0x3fecdb6e68c4b687ull }, + { 0x4003b851eb851eb8ull, 0x3fecdec15a7d13c6ull }, + { 0x4003ba5e353f7ceeull, 0x3fece213f3da4dffull }, + { 0x4003bc6a7ef9db23ull, 0x3fece56634eebc7eull }, + { 0x4003be76c8b43958ull, 0x3fece8b81dccb0e2ull }, + { 0x4003c083126e978dull, 0x3fecec09ae867713ull }, + { 0x4003c28f5c28f5c3ull, 0x3fecef5ae72e554aull }, + { 0x4003c49ba5e353f8ull, 0x3fecf2abc7d68c0full }, + { 0x4003c6a7ef9db22dull, 0x3fecf5fc50915640ull }, + { 0x4003c8b439581062ull, 0x3fecf94c8170e911ull }, + { 0x4003cac083126e98ull, 0x3fecfc9c5a877411ull }, + { 0x4003cccccccccccdull, 0x3fecffebdbe72125ull }, + { 0x4003ced916872b02ull, 0x3fed033b05a21493ull }, + { 0x4003d0e560418937ull, 0x3fed0689d7ca6d01ull }, + { 0x4003d2f1a9fbe76dull, 0x3fed09d852724377ull }, + { 0x4003d4fdf3b645a2ull, 0x3fed0d2675abab5eull }, + { 0x4003d70a3d70a3d7ull, 0x3fed10744188b289ull }, + { 0x4003d916872b020cull, 0x3fed13c1b61b6134ull }, + { 0x4003db22d0e56042ull, 0x3fed170ed375ba07ull }, + { 0x4003dd2f1a9fbe77ull, 0x3fed1a5b99a9ba12ull }, + { 0x4003df3b645a1cacull, 0x3fed1da808c958dcull }, + { 0x4003e147ae147ae1ull, 0x3fed20f420e6885bull }, + { 0x4003e353f7ced917ull, 0x3fed243fe21334faull }, + { 0x4003e5604189374cull, 0x3fed278b4c61459aull }, + { 0x4003e76c8b439581ull, 0x3fed2ad65fe29b95ull }, + { 0x4003e978d4fdf3b6ull, 0x3fed2e211ca912c3ull }, + { 0x4003eb851eb851ecull, 0x3fed316b82c68177ull }, + { 0x4003ed916872b021ull, 0x3fed34b5924cb882ull }, + { 0x4003ef9db22d0e56ull, 0x3fed37ff4b4d833dull }, + { 0x4003f1a9fbe76c8bull, 0x3fed3b48addaa780ull }, + { 0x4003f3b645a1cac1ull, 0x3fed3e91ba05e5adull }, + { 0x4003f5c28f5c28f6ull, 0x3fed41da6fe0f8acull }, + { 0x4003f7ced916872bull, 0x3fed4522cf7d95f2ull }, + { 0x4003f9db22d0e560ull, 0x3fed486ad8ed6d82ull }, + { 0x4003fbe76c8b4396ull, 0x3fed4bb28c4229efull }, + { 0x4003fdf3b645a1cbull, 0x3fed4ef9e98d7058ull }, + { 0x4004000000000000ull, 0x3fed5240f0e0e078ull }, + { 0x4004020c49ba5e35ull, 0x3fed5587a24e149bull }, + { 0x400404189374bc6bull, 0x3fed58cdfde6a1abull }, + { 0x40040624dd2f1aa0ull, 0x3fed5c1403bc1724ull }, + { 0x4004083126e978d5ull, 0x3fed5f59b3dfff26ull }, + { 0x40040a3d70a3d70aull, 0x3fed629f0e63de6full }, + { 0x40040c49ba5e3540ull, 0x3fed65e41359345dull }, + { 0x40040e5604189375ull, 0x3fed6928c2d17af1ull }, + { 0x400410624dd2f1aaull, 0x3fed6c6d1cde26d2ull }, + { 0x4004126e978d4fdfull, 0x3fed6fb12190a751ull }, + { 0x4004147ae147ae15ull, 0x3fed72f4d0fa6668ull }, + { 0x400416872b020c4aull, 0x3fed76382b2cc8bbull }, + { 0x4004189374bc6a7full, 0x3fed797b30392d9eull }, + { 0x40041a9fbe76c8b4ull, 0x3fed7cbde030ef17ull }, + { 0x40041cac083126eaull, 0x3fed80003b2561deull }, + { 0x40041eb851eb851full, 0x3fed83424127d55dull }, + { 0x400420c49ba5e354ull, 0x3fed8683f24993b8ull }, + { 0x400422d0e5604189ull, 0x3fed89c54e9be1cdull }, + { 0x400424dd2f1a9fbfull, 0x3fed8d06562fff35ull }, + { 0x400426e978d4fdf4ull, 0x3fed904709172642ull }, + { 0x400428f5c28f5c29ull, 0x3fed938767628c0bull }, + { 0x40042b020c49ba5eull, 0x3fed96c771236067ull }, + { 0x40042d0e56041894ull, 0x3fed9a07266acdf2ull }, + { 0x40042f1a9fbe76c9ull, 0x3fed9d468749fa09ull }, + { 0x40043126e978d4feull, 0x3feda08593d204d8ull }, + { 0x4004333333333333ull, 0x3feda3c44c140951ull }, + { 0x4004353f7ced9169ull, 0x3feda702b0211d36ull }, + { 0x4004374bc6a7ef9eull, 0x3fedaa40c00a5111ull }, + { 0x4004395810624dd3ull, 0x3fedad7e7be0b042ull }, + { 0x40043b645a1cac08ull, 0x3fedb0bbe3b540fbull }, + { 0x40043d70a3d70a3eull, 0x3fedb3f8f7990444ull }, + { 0x40043f7ced916873ull, 0x3fedb735b79cf5f6ull }, + { 0x40044189374bc6a8ull, 0x3fedba7223d20cc8ull }, + { 0x40044395810624ddull, 0x3fedbdae3c493a4cull }, + { 0x400445a1cac08313ull, 0x3fedc0ea01136af2ull }, + { 0x400447ae147ae148ull, 0x3fedc42572418602ull }, + { 0x400449ba5e353f7dull, 0x3fedc7608fe46daeull }, + { 0x40044bc6a7ef9db2ull, 0x3fedca9b5a0cff08ull }, + { 0x40044dd2f1a9fbe8ull, 0x3fedcdd5d0cc1207ull }, + { 0x40044fdf3b645a1dull, 0x3fedd10ff4327988ull }, + { 0x400451eb851eb852ull, 0x3fedd449c4510354ull }, + { 0x400453f7ced91687ull, 0x3fedd7834138781full }, + { 0x40045604189374bdull, 0x3feddabc6af99b8cull }, + { 0x40045810624dd2f2ull, 0x3fedddf541a52c2aull }, + { 0x40045a1cac083127ull, 0x3fede12dc54be37bull }, + { 0x40045c28f5c28f5cull, 0x3fede465f5fe75f7ull }, + { 0x40045e353f7ced92ull, 0x3fede79dd3cd930bull }, + { 0x4004604189374bc7ull, 0x3fedead55ec9e518ull }, + { 0x4004624dd2f1a9fcull, 0x3fedee0c9704117eull }, + { 0x4004645a1cac0831ull, 0x3fedf1437c8cb895ull }, + { 0x4004666666666667ull, 0x3fedf47a0f7475b8ull }, + { 0x40046872b020c49cull, 0x3fedf7b04fcbdf3aull }, + { 0x40046a7ef9db22d1ull, 0x3fedfae63da38675ull }, + { 0x40046c8b43958106ull, 0x3fedfe1bd90bf7c8ull }, + { 0x40046e978d4fdf3cull, 0x3fee01512215ba95ull }, + { 0x400470a3d70a3d71ull, 0x3fee048618d15146ull }, + { 0x400472b020c49ba6ull, 0x3fee07babd4f394full }, + { 0x400474bc6a7ef9dbull, 0x3fee0aef0f9feb31ull }, + { 0x400476c8b4395811ull, 0x3fee0e230fd3da7cull }, + { 0x400478d4fdf3b646ull, 0x3fee1156bdfb75caull }, + { 0x40047ae147ae147bull, 0x3fee148a1a2726ceull }, + { 0x40047ced916872b0ull, 0x3fee17bd2467524aull }, + { 0x40047ef9db22d0e5ull, 0x3fee1aefdccc5817ull }, + { 0x4004810624dd2f1bull, 0x3fee1e2243669329ull }, + { 0x400483126e978d50ull, 0x3fee215458465986ull }, + { 0x4004851eb851eb85ull, 0x3fee24861b7bfc56ull }, + { 0x4004872b020c49baull, 0x3fee27b78d17c7dcull }, + { 0x400489374bc6a7f0ull, 0x3fee2ae8ad2a037eull }, + { 0x40048b4395810625ull, 0x3fee2e197bc2f1bcull }, + { 0x40048d4fdf3b645aull, 0x3fee3149f8f2d041ull }, + { 0x40048f5c28f5c28full, 0x3fee347a24c9d7d9ull }, + { 0x4004916872b020c5ull, 0x3fee37a9ff583c7aull }, + { 0x40049374bc6a7efaull, 0x3fee3ad988ae2d3full }, + { 0x400495810624dd2full, 0x3fee3e08c0dbd472ull }, + { 0x4004978d4fdf3b64ull, 0x3fee4137a7f1578aull }, + { 0x400499999999999aull, 0x3fee44663dfed72bull }, + { 0x40049ba5e353f7cfull, 0x3fee479483146f2aull }, + { 0x40049db22d0e5604ull, 0x3fee4ac27742368full }, + { 0x40049fbe76c8b439ull, 0x3fee4df01a983f98ull }, + { 0x4004a1cac083126full, 0x3fee511d6d2697b9ull }, + { 0x4004a3d70a3d70a4ull, 0x3fee544a6efd479dull }, + { 0x4004a5e353f7ced9ull, 0x3fee5777202c532aull }, + { 0x4004a7ef9db22d0eull, 0x3fee5aa380c3b985ull }, + { 0x4004a9fbe76c8b44ull, 0x3fee5dcf90d3750eull }, + { 0x4004ac083126e979ull, 0x3fee60fb506b7b65ull }, + { 0x4004ae147ae147aeull, 0x3fee6426bf9bbd6dull }, + { 0x4004b020c49ba5e3ull, 0x3fee6751de74274dull }, + { 0x4004b22d0e560419ull, 0x3fee6a7cad04a074ull }, + { 0x4004b4395810624eull, 0x3fee6da72b5d0b91ull }, + { 0x4004b645a1cac083ull, 0x3fee70d1598d46a5ull }, + { 0x4004b851eb851eb8ull, 0x3fee73fb37a52af7ull }, + { 0x4004ba5e353f7ceeull, 0x3fee7724c5b48d1full }, + { 0x4004bc6a7ef9db23ull, 0x3fee7a4e03cb3cffull }, + { 0x4004be76c8b43958ull, 0x3fee7d76f1f905ceull }, + { 0x4004c083126e978dull, 0x3fee809f904dae14ull }, + { 0x4004c28f5c28f5c3ull, 0x3fee83c7ded8f7afull }, + { 0x4004c49ba5e353f8ull, 0x3fee86efddaa9fd1ull }, + { 0x4004c6a7ef9db22dull, 0x3fee8a178cd25f06ull }, + { 0x4004c8b439581062ull, 0x3fee8d3eec5fe933ull }, + { 0x4004cac083126e98ull, 0x3fee9065fc62ed9cull }, + { 0x4004cccccccccccdull, 0x3fee938cbceb16ddull }, + { 0x4004ced916872b02ull, 0x3fee96b32e080af7ull }, + { 0x4004d0e560418937ull, 0x3fee99d94fc96b48ull }, + { 0x4004d2f1a9fbe76dull, 0x3fee9cff223ed497ull }, + { 0x4004d4fdf3b645a2ull, 0x3feea024a577df08ull }, + { 0x4004d70a3d70a3d7ull, 0x3feea349d9841e2dull }, + { 0x4004d916872b020cull, 0x3feea66ebe7320fcull }, + { 0x4004db22d0e56042ull, 0x3feea993545471dbull }, + { 0x4004dd2f1a9fbe77ull, 0x3feeacb79b379693ull }, + { 0x4004df3b645a1cacull, 0x3feeafdb932c1064ull }, + { 0x4004e147ae147ae1ull, 0x3feeb2ff3c415bf9ull }, + { 0x4004e353f7ced917ull, 0x3feeb6229686f172ull }, + { 0x4004e5604189374cull, 0x3feeb945a20c445dull }, + { 0x4004e76c8b439581ull, 0x3feebc685ee0c3c2ull }, + { 0x4004e978d4fdf3b6ull, 0x3feebf8acd13da1full }, + { 0x4004eb851eb851ecull, 0x3feec2acecb4ed6bull }, + { 0x4004ed916872b021ull, 0x3feec5cebdd35f13ull }, + { 0x4004ef9db22d0e56ull, 0x3feec8f0407e8c07ull }, + { 0x4004f1a9fbe76c8bull, 0x3feecc1174c5ccb1ull }, + { 0x4004f3b645a1cac1ull, 0x3feecf325ab874feull }, + { 0x4004f5c28f5c28f6ull, 0x3feed252f265d457ull }, + { 0x4004f7ced916872bull, 0x3feed5733bdd35acull }, + { 0x4004f9db22d0e560ull, 0x3feed893372ddf74ull }, + { 0x4004fbe76c8b4396ull, 0x3feedbb2e46713aaull }, + { 0x4004fdf3b645a1cbull, 0x3feeded243980fd0ull }, + { 0x4005000000000000ull, 0x3feee1f154d00cf6ull }, + { 0x4005020c49ba5e35ull, 0x3feee510181e3fb7ull }, + { 0x400504189374bc6bull, 0x3feee82e8d91d83bull }, + { 0x40050624dd2f1aa0ull, 0x3feeeb4cb53a0239ull }, + { 0x4005083126e978d5ull, 0x3feeee6a8f25e4faull }, + { 0x40050a3d70a3d70aull, 0x3feef1881b64a35aull }, + { 0x40050c49ba5e3540ull, 0x3feef4a55a055bccull }, + { 0x40050e5604189375ull, 0x3feef7c24b172854ull }, + { 0x400510624dd2f1aaull, 0x3feefadeeea91e92ull }, + { 0x4005126e978d4fdfull, 0x3feefdfb44ca4fc1ull }, + { 0x4005147ae147ae15ull, 0x3fef01174d89c8b4ull }, + { 0x400516872b020c4aull, 0x3fef043308f691ddull }, + { 0x4005189374bc6a7full, 0x3fef074e771faf4dull }, + { 0x40051a9fbe76c8b4ull, 0x3fef0a69981420b6ull }, + { 0x40051cac083126eaull, 0x3fef0d846be2e16cull }, + { 0x40051eb851eb851full, 0x3fef109ef29ae866ull }, + { 0x400520c49ba5e354ull, 0x3fef13b92c4b2842ull }, + { 0x400522d0e5604189ull, 0x3fef16d319028f46ull }, + { 0x400524dd2f1a9fbfull, 0x3fef19ecb8d00760ull }, + { 0x400526e978d4fdf4ull, 0x3fef1d060bc27629ull }, + { 0x400528f5c28f5c29ull, 0x3fef201f11e8bce5ull }, + { 0x40052b020c49ba5eull, 0x3fef2337cb51b88aull }, + { 0x40052d0e56041894ull, 0x3fef2650380c41bbull }, + { 0x40052f1a9fbe76c9ull, 0x3fef296858272cccull }, + { 0x40053126e978d4feull, 0x3fef2c802bb149c6ull }, + { 0x4005333333333333ull, 0x3fef2f97b2b96465ull }, + { 0x4005353f7ced9169ull, 0x3fef32aeed4e4420ull }, + { 0x4005374bc6a7ef9eull, 0x3fef35c5db7eac1dull }, + { 0x4005395810624dd3ull, 0x3fef38dc7d595b45ull }, + { 0x40053b645a1cac08ull, 0x3fef3bf2d2ed0c38ull }, + { 0x40053d70a3d70a3eull, 0x3fef3f08dc487556ull }, + { 0x40053f7ced916873ull, 0x3fef421e997a48b7ull }, + { 0x40054189374bc6a8ull, 0x3fef45340a91343cull }, + { 0x40054395810624ddull, 0x3fef48492f9be182ull }, + { 0x400545a1cac08313ull, 0x3fef4b5e08a8f5eeull }, + { 0x400547ae147ae148ull, 0x3fef4e7295c712a4ull }, + { 0x400549ba5e353f7dull, 0x3fef5186d704d496ull }, + { 0x40054bc6a7ef9db2ull, 0x3fef549acc70d479ull }, + { 0x40054dd2f1a9fbe8ull, 0x3fef57ae7619a6d3ull }, + { 0x40054fdf3b645a1dull, 0x3fef5ac1d40ddbebull }, + { 0x400551eb851eb852ull, 0x3fef5dd4e65bffdfull }, + { 0x400553f7ced91687ull, 0x3fef60e7ad129a99ull }, + { 0x40055604189374bdull, 0x3fef63fa28402fd3ull }, + { 0x40055810624dd2f2ull, 0x3fef670c57f33f18ull }, + { 0x40055a1cac083127ull, 0x3fef6a1e3c3a43c8ull }, + { 0x40055c28f5c28f5cull, 0x3fef6d2fd523b51bull }, + { 0x40055e353f7ced92ull, 0x3fef704122be061cull }, + { 0x4005604189374bc7ull, 0x3fef73522517a5b0ull }, + { 0x4005624dd2f1a9fcull, 0x3fef7662dc3efe97ull }, + { 0x4005645a1cac0831ull, 0x3fef79734842776bull }, + { 0x4005666666666667ull, 0x3fef7c83693072a8ull }, + { 0x40056872b020c49cull, 0x3fef7f933f174ea1ull }, + { 0x40056a7ef9db22d1ull, 0x3fef82a2ca056590ull }, + { 0x40056c8b43958106ull, 0x3fef85b20a090d90ull }, + { 0x40056e978d4fdf3cull, 0x3fef88c0ff30989full }, + { 0x400570a3d70a3d71ull, 0x3fef8bcfa98a549eull }, + { 0x400572b020c49ba6ull, 0x3fef8ede09248b58ull }, + { 0x400574bc6a7ef9dbull, 0x3fef91ec1e0d827full }, + { 0x400576c8b4395811ull, 0x3fef94f9e8537bb1ull }, + { 0x400578d4fdf3b646ull, 0x3fef98076804b473ull }, + { 0x40057ae147ae147bull, 0x3fef9b149d2f663bull }, + { 0x40057ced916872b0ull, 0x3fef9e2187e1c66cull }, + { 0x40057ef9db22d0e6ull, 0x3fefa12e282a065cull }, + { 0x4005810624dd2f1bull, 0x3fefa43a7e16534dull }, + { 0x400583126e978d50ull, 0x3fefa74689b4d678ull }, + { 0x4005851eb851eb85ull, 0x3fefaa524b13b50cull }, + { 0x4005872b020c49baull, 0x3fefad5dc241102cull }, + { 0x400589374bc6a7f0ull, 0x3fefb068ef4b04f4ull }, + { 0x40058b4395810625ull, 0x3fefb373d23fac78ull }, + { 0x40058d4fdf3b645aull, 0x3fefb67e6b2d1bc8ull }, + { 0x40058f5c28f5c28full, 0x3fefb988ba2163f0ull }, + { 0x4005916872b020c5ull, 0x3fefbc92bf2a91fdull }, + { 0x40059374bc6a7efaull, 0x3fefbf9c7a56aef6ull }, + { 0x400595810624dd2full, 0x3fefc2a5ebb3bfe6ull }, + { 0x4005978d4fdf3b64ull, 0x3fefc5af134fc5dcull }, + { 0x400599999999999aull, 0x3fefc8b7f138bdeaull }, + { 0x40059ba5e353f7cfull, 0x3fefcbc0857ca125ull }, + { 0x40059db22d0e5604ull, 0x3fefcec8d02964abull }, + { 0x40059fbe76c8b439ull, 0x3fefd1d0d14cf9a5ull }, + { 0x4005a1cac083126full, 0x3fefd4d888f54d43ull }, + { 0x4005a3d70a3d70a4ull, 0x3fefd7dff73048c1ull }, + { 0x4005a5e353f7ced9ull, 0x3fefdae71c0bd168ull }, + { 0x4005a7ef9db22d0eull, 0x3fefddedf795c891ull }, + { 0x4005a9fbe76c8b44ull, 0x3fefe0f489dc0ba5ull }, + { 0x4005ac083126e979ull, 0x3fefe3fad2ec741bull }, + { 0x4005ae147ae147aeull, 0x3fefe700d2d4d783ull }, + { 0x4005b020c49ba5e3ull, 0x3fefea0689a3077cull }, + { 0x4005b22d0e560419ull, 0x3fefed0bf764d1bfull }, + { 0x4005b4395810624eull, 0x3feff0111c280019ull }, + { 0x4005b645a1cac083ull, 0x3feff315f7fa5873ull }, + { 0x4005b851eb851eb8ull, 0x3feff61a8ae99cceull }, + { 0x4005ba5e353f7ceeull, 0x3feff91ed5038b4cull }, + { 0x4005bc6a7ef9db23ull, 0x3feffc22d655de23ull }, + { 0x4005be76c8b43958ull, 0x3fefff268eee4bb0ull }, + { 0x4005c083126e978dull, 0x3ff00114ff6d4337ull }, + { 0x4005c28f5c28f5c3ull, 0x3ff0029693141e7cull }, + { 0x4005c49ba5e353f8ull, 0x3ff0041802728d08ull }, + { 0x4005c6a7ef9db22dull, 0x3ff005994d8f624bull }, + { 0x4005c8b439581062ull, 0x3ff0071a74716fccull }, + { 0x4005cac083126e98ull, 0x3ff0089b771f8524ull }, + { 0x4005cccccccccccdull, 0x3ff00a1c55a06fffull }, + { 0x4005ced916872b02ull, 0x3ff00b9d0ffafc21ull }, + { 0x4005d0e560418937ull, 0x3ff00d1da635f361ull }, + { 0x4005d2f1a9fbe76dull, 0x3ff00e9e18581db1ull }, + { 0x4005d4fdf3b645a2ull, 0x3ff0101e66684116ull }, + { 0x4005d70a3d70a3d7ull, 0x3ff0119e906d21aeull }, + { 0x4005d916872b020cull, 0x3ff0131e966d81b1ull }, + { 0x4005db22d0e56042ull, 0x3ff0149e78702172ull }, + { 0x4005dd2f1a9fbe77ull, 0x3ff0161e367bbf5aull }, + { 0x4005df3b645a1cacull, 0x3ff0179dd09717f0ull }, + { 0x4005e147ae147ae1ull, 0x3ff0191d46c8e5d6ull }, + { 0x4005e353f7ced917ull, 0x3ff01a9c9917e1ccull }, + { 0x4005e5604189374cull, 0x3ff01c1bc78ac2aaull }, + { 0x4005e76c8b439581ull, 0x3ff01d9ad2283d6bull }, + { 0x4005e978d4fdf3b6ull, 0x3ff01f19b8f70527ull }, + { 0x4005eb851eb851ecull, 0x3ff020987bfdcb15ull }, + { 0x4005ed916872b021ull, 0x3ff022171b433e8bull }, + { 0x4005ef9db22d0e56ull, 0x3ff0239596ce0cffull }, + { 0x4005f1a9fbe76c8bull, 0x3ff02513eea4e20cull }, + { 0x4005f3b645a1cac1ull, 0x3ff0269222ce676cull }, + { 0x4005f5c28f5c28f6ull, 0x3ff02810335144fbull }, + { 0x4005f7ced916872bull, 0x3ff0298e203420bbull }, + { 0x4005f9db22d0e560ull, 0x3ff02b0be97d9ed0ull }, + { 0x4005fbe76c8b4396ull, 0x3ff02c898f346184ull }, + { 0x4005fdf3b645a1cbull, 0x3ff02e07115f0945ull }, + { 0x4006000000000000ull, 0x3ff02f84700434a8ull }, + { 0x4006020c49ba5e35ull, 0x3ff03101ab2a806aull }, + { 0x400604189374bc6bull, 0x3ff0327ec2d8876full }, + { 0x40060624dd2f1aa0ull, 0x3ff033fbb714e2c1ull }, + { 0x4006083126e978d5ull, 0x3ff0357887e62995ull }, + { 0x40060a3d70a3d70aull, 0x3ff036f53552f149ull }, + { 0x40060c49ba5e3540ull, 0x3ff03871bf61cd66ull }, + { 0x40060e5604189375ull, 0x3ff039ee26194f9eull }, + { 0x400610624dd2f1aaull, 0x3ff03b6a698007d2ull }, + { 0x4006126e978d4fdfull, 0x3ff03ce6899c840cull }, + { 0x4006147ae147ae15ull, 0x3ff03e6286755084ull }, + { 0x400616872b020c4aull, 0x3ff03fde6010f7a0ull }, + { 0x4006189374bc6a7full, 0x3ff0415a167601f5ull }, + { 0x40061a9fbe76c8b4ull, 0x3ff042d5a9aaf645ull }, + { 0x40061cac083126eaull, 0x3ff0445119b65985ull }, + { 0x40061eb851eb851full, 0x3ff045cc669eaed6ull }, + { 0x400620c49ba5e354ull, 0x3ff04747906a778dull }, + { 0x400622d0e5604189ull, 0x3ff048c297203330ull }, + { 0x400624dd2f1a9fbfull, 0x3ff04a3d7ac65f78ull }, + { 0x400626e978d4fdf4ull, 0x3ff04bb83b637850ull }, + { 0x400628f5c28f5c29ull, 0x3ff04d32d8fdf7d6ull }, + { 0x40062b020c49ba5eull, 0x3ff04ead539c565eull }, + { 0x40062d0e56041894ull, 0x3ff05027ab450a72ull }, + { 0x40062f1a9fbe76c9ull, 0x3ff051a1dffe88cfull }, + { 0x40063126e978d4feull, 0x3ff0531bf1cf4468ull }, + { 0x4006333333333333ull, 0x3ff05495e0bdae6bull }, + { 0x4006353f7ced9169ull, 0x3ff0560facd0363aull }, + { 0x4006374bc6a7ef9eull, 0x3ff05789560d496full }, + { 0x4006395810624dd3ull, 0x3ff05902dc7b53e1ull }, + { 0x40063b645a1cac08ull, 0x3ff05a7c4020bf9dull }, + { 0x40063d70a3d70a3eull, 0x3ff05bf58103f4ebull }, + { 0x40063f7ced916873ull, 0x3ff05d6e9f2b5a4full }, + { 0x40064189374bc6a8ull, 0x3ff05ee79a9d5486ull }, + { 0x40064395810624ddull, 0x3ff060607360468eull }, + { 0x400645a1cac08313ull, 0x3ff061d9297a919dull }, + { 0x400647ae147ae148ull, 0x3ff06351bcf2952aull }, + { 0x400649ba5e353f7dull, 0x3ff064ca2dceaee7ull }, + { 0x40064bc6a7ef9db2ull, 0x3ff066427c153ac7ull }, + { 0x40064dd2f1a9fbe8ull, 0x3ff067baa7cc92feull }, + { 0x40064fdf3b645a1dull, 0x3ff06932b0fb0ffbull }, + { 0x400651eb851eb852ull, 0x3ff06aaa97a70873ull }, + { 0x400653f7ced91687ull, 0x3ff06c225bd6d15aull }, + { 0x40065604189374bdull, 0x3ff06d99fd90bde7ull }, + { 0x40065810624dd2f2ull, 0x3ff06f117cdb1f91ull }, + { 0x40065a1cac083127ull, 0x3ff07088d9bc4616ull }, + { 0x40065c28f5c28f5cull, 0x3ff07200143a7f76ull }, + { 0x40065e353f7ced92ull, 0x3ff073772c5c17f7ull }, + { 0x4006604189374bc7ull, 0x3ff074ee22275a21ull }, + { 0x4006624dd2f1a9fcull, 0x3ff07664f5a28ec5ull }, + { 0x4006645a1cac0831ull, 0x3ff077dba6d3fcf9ull }, + { 0x4006666666666667ull, 0x3ff0795235c1ea1cull }, + { 0x40066872b020c49cull, 0x3ff07ac8a27299d2ull }, + { 0x40066a7ef9db22d1ull, 0x3ff07c3eecec4e08ull }, + { 0x40066c8b43958106ull, 0x3ff07db5153546f7ull }, + { 0x40066e978d4fdf3cull, 0x3ff07f2b1b53c31full }, + { 0x400670a3d70a3d71ull, 0x3ff080a0ff4dff48ull }, + { 0x400672b020c49ba6ull, 0x3ff08216c12a368aull }, + { 0x400674bc6a7ef9dbull, 0x3ff0838c60eea245ull }, + { 0x400676c8b4395811ull, 0x3ff08501dea17a28ull }, + { 0x400678d4fdf3b646ull, 0x3ff086773a48f42aull }, + { 0x40067ae147ae147bull, 0x3ff087ec73eb4494ull }, + { 0x40067ced916872b0ull, 0x3ff089618b8e9dfaull }, + { 0x40067ef9db22d0e6ull, 0x3ff08ad681393142ull }, + { 0x4006810624dd2f1bull, 0x3ff08c4b54f12d9dull }, + { 0x400683126e978d50ull, 0x3ff08dc006bcc08eull }, + { 0x4006851eb851eb85ull, 0x3ff08f3496a215e9ull }, + { 0x4006872b020c49baull, 0x3ff090a904a757d3ull }, + { 0x400689374bc6a7f0ull, 0x3ff0921d50d2aec2ull }, + { 0x40068b4395810625ull, 0x3ff093917b2a417cull }, + { 0x40068d4fdf3b645aull, 0x3ff0950583b4351full }, + { 0x40068f5c28f5c28full, 0x3ff096796a76ad19ull }, + { 0x4006916872b020c5ull, 0x3ff097ed2f77cb2cull }, + { 0x40069374bc6a7efaull, 0x3ff09960d2bdaf71ull }, + { 0x400695810624dd2full, 0x3ff09ad4544e7853ull }, + { 0x4006978d4fdf3b64ull, 0x3ff09c47b4304297ull }, + { 0x400699999999999aull, 0x3ff09dbaf2692955ull }, + { 0x40069ba5e353f7cfull, 0x3ff09f2e0eff45fdull }, + { 0x40069db22d0e5604ull, 0x3ff0a0a109f8b059ull }, + { 0x40069fbe76c8b439ull, 0x3ff0a213e35b7e88ull }, + { 0x4006a1cac083126full, 0x3ff0a3869b2dc505ull }, + { 0x4006a3d70a3d70a4ull, 0x3ff0a4f9317596a2ull }, + { 0x4006a5e353f7ced9ull, 0x3ff0a66ba639048cull }, + { 0x4006a7ef9db22d0eull, 0x3ff0a7ddf97e1e4bull }, + { 0x4006a9fbe76c8b44ull, 0x3ff0a9502b4af1c3ull }, + { 0x4006ac083126e979ull, 0x3ff0aac23ba58b32ull }, + { 0x4006ae147ae147aeull, 0x3ff0ac342a93f534ull }, + { 0x4006b020c49ba5e3ull, 0x3ff0ada5f81c38c2ull }, + { 0x4006b22d0e560419ull, 0x3ff0af17a4445d33ull }, + { 0x4006b4395810624eull, 0x3ff0b0892f12683bull }, + { 0x4006b645a1cac083ull, 0x3ff0b1fa988c5dedull }, + { 0x4006b851eb851eb8ull, 0x3ff0b36be0b840bdull }, + { 0x4006ba5e353f7ceeull, 0x3ff0b4dd079c117eull }, + { 0x4006bc6a7ef9db23ull, 0x3ff0b64e0d3dcf63ull }, + { 0x4006be76c8b43958ull, 0x3ff0b7bef1a37801ull }, + { 0x4006c083126e978dull, 0x3ff0b92fb4d3074full }, + { 0x4006c28f5c28f5c3ull, 0x3ff0baa056d277a7ull }, + { 0x4006c49ba5e353f8ull, 0x3ff0bc10d7a7c1c3ull }, + { 0x4006c6a7ef9db22dull, 0x3ff0bd813758dcc4ull }, + { 0x4006c8b439581062ull, 0x3ff0bef175ebbe2eull }, + { 0x4006cac083126e98ull, 0x3ff0c061936659e8ull }, + { 0x4006cccccccccccdull, 0x3ff0c1d18fcea241ull }, + { 0x4006ced916872b02ull, 0x3ff0c3416b2a87ebull }, + { 0x4006d0e560418937ull, 0x3ff0c4b1257ffa02ull }, + { 0x4006d2f1a9fbe76dull, 0x3ff0c620bed4e606ull }, + { 0x4006d4fdf3b645a2ull, 0x3ff0c790372f37deull }, + { 0x4006d70a3d70a3d7ull, 0x3ff0c8ff8e94d9ddull }, + { 0x4006d916872b020cull, 0x3ff0ca6ec50bb4bbull }, + { 0x4006db22d0e56042ull, 0x3ff0cbddda99af9cull }, + { 0x4006dd2f1a9fbe77ull, 0x3ff0cd4ccf44b00bull }, + { 0x4006df3b645a1cacull, 0x3ff0cebba31299ffull }, + { 0x4006e147ae147ae1ull, 0x3ff0d02a56094fdbull }, + { 0x4006e353f7ced917ull, 0x3ff0d198e82eb26cull }, + { 0x4006e5604189374cull, 0x3ff0d3075988a0ecull }, + { 0x4006e76c8b439581ull, 0x3ff0d475aa1cf900ull }, + { 0x4006e978d4fdf3b6ull, 0x3ff0d5e3d9f196bdull }, + { 0x4006eb851eb851ecull, 0x3ff0d751e90c54a5ull }, + { 0x4006ed916872b021ull, 0x3ff0d8bfd7730ba6ull }, + { 0x4006ef9db22d0e56ull, 0x3ff0da2da52b931full }, + { 0x4006f1a9fbe76c8bull, 0x3ff0db9b523bc0dfull }, + { 0x4006f3b645a1cac1ull, 0x3ff0dd08dea96924ull }, + { 0x4006f5c28f5c28f6ull, 0x3ff0de764a7a5e9dull }, + { 0x4006f7ced916872bull, 0x3ff0dfe395b47269ull }, + { 0x4006f9db22d0e560ull, 0x3ff0e150c05d741bull }, + { 0x4006fbe76c8b4396ull, 0x3ff0e2bdca7b31b7ull }, + { 0x4006fdf3b645a1cbull, 0x3ff0e42ab41377b3ull }, + { 0x4007000000000000ull, 0x3ff0e5977d2c10f9ull }, + { 0x4007020c49ba5e35ull, 0x3ff0e70425cac6e9ull }, + { 0x400704189374bc6bull, 0x3ff0e870adf56154ull }, + { 0x40070624dd2f1aa0ull, 0x3ff0e9dd15b1a683ull }, + { 0x4007083126e978d5ull, 0x3ff0eb495d055b32ull }, + { 0x40070a3d70a3d70aull, 0x3ff0ecb583f64295ull }, + { 0x40070c49ba5e3540ull, 0x3ff0ee218a8a1e57ull }, + { 0x40070e5604189375ull, 0x3ff0ef8d70c6ae98ull }, + { 0x400710624dd2f1aaull, 0x3ff0f0f936b1b1f1ull }, + { 0x4007126e978d4fdfull, 0x3ff0f264dc50e575ull }, + { 0x4007147ae147ae15ull, 0x3ff0f3d061aa04aeull }, + { 0x400716872b020c4aull, 0x3ff0f53bc6c2c99eull }, + { 0x4007189374bc6a7full, 0x3ff0f6a70ba0ecc4ull }, + { 0x40071a9fbe76c8b4ull, 0x3ff0f812304a2517ull }, + { 0x40071cac083126eaull, 0x3ff0f97d34c4280dull }, + { 0x40071eb851eb851full, 0x3ff0fae81914a991ull }, + { 0x400720c49ba5e354ull, 0x3ff0fc52dd415c10ull }, + { 0x400722d0e5604189ull, 0x3ff0fdbd814ff072ull }, + { 0x400724dd2f1a9fbfull, 0x3ff0ff280546161aull }, + { 0x400726e978d4fdf4ull, 0x3ff1009269297aebull }, + { 0x400728f5c28f5c29ull, 0x3ff101fcacffcb45ull }, + { 0x40072b020c49ba5eull, 0x3ff10366d0ceb208ull }, + { 0x40072d0e56041894ull, 0x3ff104d0d49bd893ull }, + { 0x40072f1a9fbe76c9ull, 0x3ff1063ab86ce6c3ull }, + { 0x40073126e978d4feull, 0x3ff107a47c4782f9ull }, + { 0x4007333333333333ull, 0x3ff1090e20315212ull }, + { 0x4007353f7ced9169ull, 0x3ff10a77a42ff772ull }, + { 0x4007374bc6a7ef9eull, 0x3ff10be1084914fbull }, + { 0x4007395810624dd3ull, 0x3ff10d4a4c824b11ull }, + { 0x40073b645a1cac08ull, 0x3ff10eb370e1389full }, + { 0x40073d70a3d70a3eull, 0x3ff1101c756b7b11ull }, + { 0x40073f7ced916873ull, 0x3ff111855a26ae55ull }, + { 0x40074189374bc6a8ull, 0x3ff112ee1f186ce1ull }, + { 0x40074395810624ddull, 0x3ff11456c4464fafull }, + { 0x400745a1cac08313ull, 0x3ff115bf49b5ee3full }, + { 0x400747ae147ae148ull, 0x3ff11727af6cde95ull }, + { 0x400749ba5e353f7dull, 0x3ff1188ff570b53eull }, + { 0x40074bc6a7ef9db2ull, 0x3ff119f81bc7054eull }, + { 0x40074dd2f1a9fbe8ull, 0x3ff11b6022756061ull }, + { 0x40074fdf3b645a1dull, 0x3ff11cc809815697ull }, + { 0x400751eb851eb852ull, 0x3ff11e2fd0f0769full }, + { 0x400753f7ced91687ull, 0x3ff11f9778c84daeull }, + { 0x40075604189374bdull, 0x3ff120ff010e6782ull }, + { 0x40075810624dd2f2ull, 0x3ff1226669c84e65ull }, + { 0x40075a1cac083127ull, 0x3ff123cdb2fb8b2aull }, + { 0x40075c28f5c28f5cull, 0x3ff12534dcada532ull }, + { 0x40075e353f7ced92ull, 0x3ff1269be6e42268ull }, + { 0x4007604189374bc7ull, 0x3ff12802d1a48742ull }, + { 0x4007624dd2f1a9fcull, 0x3ff129699cf456c5ull }, + { 0x4007645a1cac0831ull, 0x3ff12ad048d91284ull }, + { 0x4007666666666667ull, 0x3ff12c36d5583a9dull }, + { 0x40076872b020c49cull, 0x3ff12d9d42774dbeull }, + { 0x40076a7ef9db22d1ull, 0x3ff12f03903bc924ull }, + { 0x40076c8b43958106ull, 0x3ff13069beab289bull }, + { 0x40076e978d4fdf3cull, 0x3ff131cfcdcae680ull }, + { 0x400770a3d70a3d71ull, 0x3ff13335bda07bbdull }, + { 0x400772b020c49ba6ull, 0x3ff1349b8e315fd0ull }, + { 0x400774bc6a7ef9dbull, 0x3ff136013f8308c7ull }, + { 0x400776c8b4395811ull, 0x3ff13766d19aeb44ull }, + { 0x400778d4fdf3b646ull, 0x3ff138cc447e7a78ull }, + { 0x40077ae147ae147bull, 0x3ff13a3198332829ull }, + { 0x40077ced916872b0ull, 0x3ff13b96ccbe64b0ull }, + { 0x40077ef9db22d0e6ull, 0x3ff13cfbe2259efaull }, + { 0x4007810624dd2f1bull, 0x3ff13e60d86e4488ull }, + { 0x400783126e978d50ull, 0x3ff13fc5af9dc16full }, + { 0x4007851eb851eb85ull, 0x3ff1412a67b9805bull }, + { 0x4007872b020c49bbull, 0x3ff1428f00c6ea8dull }, + { 0x400789374bc6a7f0ull, 0x3ff143f37acb67ddull }, + { 0x40078b4395810625ull, 0x3ff14557d5cc5eb8ull }, + { 0x40078d4fdf3b645aull, 0x3ff146bc11cf3424ull }, + { 0x40078f5c28f5c28full, 0x3ff148202ed94bbfull }, + { 0x4007916872b020c5ull, 0x3ff149842cf007beull }, + { 0x40079374bc6a7efaull, 0x3ff14ae80c18c8f0ull }, + { 0x400795810624dd2full, 0x3ff14c4bcc58eebbull }, + { 0x4007978d4fdf3b64ull, 0x3ff14daf6db5d722ull }, + { 0x400799999999999aull, 0x3ff14f12f034dec0ull }, + { 0x40079ba5e353f7cfull, 0x3ff1507653db60ccull }, + { 0x40079db22d0e5604ull, 0x3ff151d998aeb716ull }, + { 0x40079fbe76c8b439ull, 0x3ff1533cbeb43a0dull }, + { 0x4007a1cac083126full, 0x3ff1549fc5f140b9ull }, + { 0x4007a3d70a3d70a4ull, 0x3ff15602ae6b20c1ull }, + { 0x4007a5e353f7ced9ull, 0x3ff1576578272e68ull }, + { 0x4007a7ef9db22d0eull, 0x3ff158c8232abc8dull }, + { 0x4007a9fbe76c8b44ull, 0x3ff15a2aaf7b1cb3ull }, + { 0x4007ac083126e979ull, 0x3ff15b8d1d1d9ef3ull }, + { 0x4007ae147ae147aeull, 0x3ff15cef6c17920bull }, + { 0x4007b020c49ba5e3ull, 0x3ff15e519c6e4358ull }, + { 0x4007b22d0e560419ull, 0x3ff15fb3ae26fed6ull }, + { 0x4007b4395810624eull, 0x3ff16115a1470f20ull }, + { 0x4007b645a1cac083ull, 0x3ff1627775d3bd74ull }, + { 0x4007b851eb851eb8ull, 0x3ff163d92bd251b1ull }, + { 0x4007ba5e353f7ceeull, 0x3ff1653ac3481258ull }, + { 0x4007bc6a7ef9db23ull, 0x3ff1669c3c3a448cull }, + { 0x4007be76c8b43958ull, 0x3ff167fd96ae2c12ull }, + { 0x4007c083126e978dull, 0x3ff1695ed2a90b55ull }, + { 0x4007c28f5c28f5c3ull, 0x3ff16abff0302361ull }, + { 0x4007c49ba5e353f8ull, 0x3ff16c20ef48b3e8ull }, + { 0x4007c6a7ef9db22dull, 0x3ff16d81cff7fb3eull }, + { 0x4007c8b439581062ull, 0x3ff16ee292433660ull }, + { 0x4007cac083126e98ull, 0x3ff17043362fa0efull }, + { 0x4007cccccccccccdull, 0x3ff171a3bbc27531ull }, + { 0x4007ced916872b02ull, 0x3ff173042300ec13ull }, + { 0x4007d0e560418937ull, 0x3ff174646bf03d2bull }, + { 0x4007d2f1a9fbe76dull, 0x3ff175c496959eb5ull }, + { 0x4007d4fdf3b645a2ull, 0x3ff17724a2f64594ull }, + { 0x4007d70a3d70a3d7ull, 0x3ff1788491176555ull }, + { 0x4007d916872b020cull, 0x3ff179e460fe302eull }, + { 0x4007db22d0e56042ull, 0x3ff17b4412afd6fdull }, + { 0x4007dd2f1a9fbe77ull, 0x3ff17ca3a631894aull }, + { 0x4007df3b645a1cacull, 0x3ff17e031b887548ull }, + { 0x4007e147ae147ae1ull, 0x3ff17f6272b9c7d5ull }, + { 0x4007e353f7ced917ull, 0x3ff180c1abcaac7bull }, + { 0x4007e5604189374cull, 0x3ff18220c6c04d6cull }, + { 0x4007e76c8b439581ull, 0x3ff1837fc39fd38aull }, + { 0x4007e978d4fdf3b6ull, 0x3ff184dea26e6661ull }, + { 0x4007eb851eb851ecull, 0x3ff1863d63312c2cull }, + { 0x4007ed916872b021ull, 0x3ff1879c05ed49d2ull }, + { 0x4007ef9db22d0e56ull, 0x3ff188fa8aa7e2e8ull }, + { 0x4007f1a9fbe76c8bull, 0x3ff18a58f16619b3ull }, + { 0x4007f3b645a1cac1ull, 0x3ff18bb73a2d0f25ull }, + { 0x4007f5c28f5c28f6ull, 0x3ff18d156501e2dfull }, + { 0x4007f7ced916872bull, 0x3ff18e7371e9b333ull }, + { 0x4007f9db22d0e560ull, 0x3ff18fd160e99d24ull }, + { 0x4007fbe76c8b4396ull, 0x3ff1912f3206bc63ull }, + { 0x4007fdf3b645a1cbull, 0x3ff1928ce5462b53ull }, + { 0x4008000000000000ull, 0x3ff193ea7aad030bull }, + { 0x4008020c49ba5e35ull, 0x3ff19547f2405b4full }, + { 0x400804189374bc6bull, 0x3ff196a54c054a9cull }, + { 0x40080624dd2f1aa0ull, 0x3ff198028800e61aull }, + { 0x4008083126e978d5ull, 0x3ff1995fa63841acull }, + { 0x40080a3d70a3d70aull, 0x3ff19abca6b06fe2ull }, + { 0x40080c49ba5e3540ull, 0x3ff19c19896e8207ull }, + { 0x40080e5604189375ull, 0x3ff19d764e778813ull }, + { 0x400810624dd2f1aaull, 0x3ff19ed2f5d090b9ull }, + { 0x4008126e978d4fdfull, 0x3ff1a02f7f7ea960ull }, + { 0x4008147ae147ae15ull, 0x3ff1a18beb86de24ull }, + { 0x400816872b020c4aull, 0x3ff1a2e839ee39d7ull }, + { 0x4008189374bc6a7full, 0x3ff1a4446ab9c603ull }, + { 0x40081a9fbe76c8b4ull, 0x3ff1a5a07dee8ae9ull }, + { 0x40081cac083126eaull, 0x3ff1a6fc73918f82ull }, + { 0x40081eb851eb851full, 0x3ff1a8584ba7d97dull }, + { 0x400820c49ba5e354ull, 0x3ff1a9b406366d45ull }, + { 0x400822d0e5604189ull, 0x3ff1ab0fa3424dfcull }, + { 0x400824dd2f1a9fbfull, 0x3ff1ac6b22d07d7dull }, + { 0x400826e978d4fdf4ull, 0x3ff1adc684e5fc5eull }, + { 0x400828f5c28f5c29ull, 0x3ff1af21c987c9eeull }, + { 0x40082b020c49ba5eull, 0x3ff1b07cf0bae438ull }, + { 0x40082d0e56041894ull, 0x3ff1b1d7fa844802ull }, + { 0x40082f1a9fbe76c9ull, 0x3ff1b332e6e8f0ccull }, + { 0x40083126e978d4feull, 0x3ff1b48db5edd8d4ull }, + { 0x4008333333333333ull, 0x3ff1b5e86797f915ull }, + { 0x4008353f7ced9169ull, 0x3ff1b742fbec4943ull }, + { 0x4008374bc6a7ef9eull, 0x3ff1b89d72efbfd4ull }, + { 0x4008395810624dd3ull, 0x3ff1b9f7cca751f8ull }, + { 0x40083b645a1cac08ull, 0x3ff1bb520917f39full }, + { 0x40083d70a3d70a3eull, 0x3ff1bcac28469778ull }, + { 0x40083f7ced916873ull, 0x3ff1be062a382eefull }, + { 0x40084189374bc6a8ull, 0x3ff1bf600ef1aa32ull }, + { 0x40084395810624ddull, 0x3ff1c0b9d677f82dull }, + { 0x400845a1cac08313ull, 0x3ff1c21380d0068dull }, + { 0x400847ae147ae148ull, 0x3ff1c36d0dfec1beull }, + { 0x400849ba5e353f7dull, 0x3ff1c4c67e0914f0ull }, + { 0x40084bc6a7ef9db2ull, 0x3ff1c61fd0f3ea13ull }, + { 0x40084dd2f1a9fbe8ull, 0x3ff1c77906c429d9ull }, + { 0x40084fdf3b645a1dull, 0x3ff1c8d21f7ebbb6ull }, + { 0x400851eb851eb852ull, 0x3ff1ca2b1b2885e1ull }, + { 0x400853f7ced91687ull, 0x3ff1cb83f9c66d55ull }, + { 0x40085604189374bdull, 0x3ff1ccdcbb5d55d0ull }, + { 0x40085810624dd2f2ull, 0x3ff1ce355ff221d3ull }, + { 0x40085a1cac083127ull, 0x3ff1cf8de789b2a5ull }, + { 0x40085c28f5c28f5cull, 0x3ff1d0e65228e852ull }, + { 0x40085e353f7ced92ull, 0x3ff1d23e9fd4a1aaull }, + { 0x4008604189374bc7ull, 0x3ff1d396d091bc42ull }, + { 0x4008624dd2f1a9fcull, 0x3ff1d4eee4651479ull }, + { 0x4008645a1cac0831ull, 0x3ff1d646db538570ull }, + { 0x4008666666666667ull, 0x3ff1d79eb561e911ull }, + { 0x40086872b020c49cull, 0x3ff1d8f67295180eull }, + { 0x40086a7ef9db22d1ull, 0x3ff1da4e12f1e9dfull }, + { 0x40086c8b43958106ull, 0x3ff1dba5967d34c5ull }, + { 0x40086e978d4fdf3cull, 0x3ff1dcfcfd3bcdcbull }, + { 0x400870a3d70a3d71ull, 0x3ff1de54473288c1ull }, + { 0x400872b020c49ba6ull, 0x3ff1dfab74663845ull }, + { 0x400874bc6a7ef9dbull, 0x3ff1e10284dbadbdull }, + { 0x400876c8b4395811ull, 0x3ff1e2597897b958ull }, + { 0x400878d4fdf3b646ull, 0x3ff1e3b04f9f2a0full }, + { 0x40087ae147ae147bull, 0x3ff1e50709f6cdaaull }, + { 0x40087ced916872b0ull, 0x3ff1e65da7a370b9ull }, + { 0x40087ef9db22d0e6ull, 0x3ff1e7b428a9de9aull }, + { 0x4008810624dd2f1bull, 0x3ff1e90a8d0ee173ull }, + { 0x400883126e978d50ull, 0x3ff1ea60d4d7423bull }, + { 0x4008851eb851eb85ull, 0x3ff1ebb70007c8b5ull }, + { 0x4008872b020c49bbull, 0x3ff1ed0d0ea53b72ull }, + { 0x400889374bc6a7f0ull, 0x3ff1ee6300b45fcdull }, + { 0x40088b4395810625ull, 0x3ff1efb8d639f9f3ull }, + { 0x40088d4fdf3b645aull, 0x3ff1f10e8f3accdfull }, + { 0x40088f5c28f5c290ull, 0x3ff1f2642bbb9a5cull }, + { 0x4008916872b020c5ull, 0x3ff1f3b9abc12300ull }, + { 0x40089374bc6a7efaull, 0x3ff1f50f0f502636ull }, + { 0x400895810624dd2full, 0x3ff1f664566d6236ull }, + { 0x4008978d4fdf3b64ull, 0x3ff1f7b9811d9409ull }, + { 0x400899999999999aull, 0x3ff1f90e8f65778cull }, + { 0x40089ba5e353f7cfull, 0x3ff1fa638149c768ull }, + { 0x40089db22d0e5604ull, 0x3ff1fbb856cf3d1dull }, + { 0x40089fbe76c8b439ull, 0x3ff1fd0d0ffa90faull }, + { 0x4008a1cac083126full, 0x3ff1fe61acd07a21ull }, + { 0x4008a3d70a3d70a4ull, 0x3ff1ffb62d55ae88ull }, + { 0x4008a5e353f7ced9ull, 0x3ff2010a918ee2f7ull }, + { 0x4008a7ef9db22d0eull, 0x3ff2025ed980cb0cull }, + { 0x4008a9fbe76c8b44ull, 0x3ff203b305301937ull }, + { 0x4008ac083126e979ull, 0x3ff2050714a17ebcull }, + { 0x4008ae147ae147aeull, 0x3ff2065b07d9abb5ull }, + { 0x4008b020c49ba5e3ull, 0x3ff207aededd4f13ull }, + { 0x4008b22d0e560419ull, 0x3ff2090299b1169aull }, + { 0x4008b4395810624eull, 0x3ff20a563859aee5ull }, + { 0x4008b645a1cac083ull, 0x3ff20ba9badbc366ull }, + { 0x4008b851eb851eb8ull, 0x3ff20cfd213bfe64ull }, + { 0x4008ba5e353f7ceeull, 0x3ff20e506b7f0900ull }, + { 0x4008bc6a7ef9db23ull, 0x3ff20fa399a98b30ull }, + { 0x4008be76c8b43958ull, 0x3ff210f6abc02bc4ull }, + { 0x4008c083126e978dull, 0x3ff21249a1c79063ull }, + { 0x4008c28f5c28f5c3ull, 0x3ff2139c7bc45d8eull }, + { 0x4008c49ba5e353f8ull, 0x3ff214ef39bb369dull }, + { 0x4008c6a7ef9db22dull, 0x3ff21641dbb0bdc4ull }, + { 0x4008c8b439581062ull, 0x3ff2179461a9940full }, + { 0x4008cac083126e98ull, 0x3ff218e6cbaa5966ull }, + { 0x4008cccccccccccdull, 0x3ff21a3919b7ac89ull }, + { 0x4008ced916872b02ull, 0x3ff21b8b4bd62b16ull }, + { 0x4008d0e560418937ull, 0x3ff21cdd620a7185ull }, + { 0x4008d2f1a9fbe76dull, 0x3ff21e2f5c591b2aull }, + { 0x4008d4fdf3b645a2ull, 0x3ff21f813ac6c235ull }, + { 0x4008d70a3d70a3d7ull, 0x3ff220d2fd57ffb2ull }, + { 0x4008d916872b020cull, 0x3ff22224a4116b8bull }, + { 0x4008db22d0e56042ull, 0x3ff223762ef79c88ull }, + { 0x4008dd2f1a9fbe77ull, 0x3ff224c79e0f284bull }, + { 0x4008df3b645a1cacull, 0x3ff22618f15ca357ull }, + { 0x4008e147ae147ae1ull, 0x3ff2276a28e4a10full }, + { 0x4008e353f7ced917ull, 0x3ff228bb44abb3b1ull }, + { 0x4008e5604189374cull, 0x3ff22a0c44b66c5cull }, + { 0x4008e76c8b439581ull, 0x3ff22b5d29095b0eull }, + { 0x4008e978d4fdf3b6ull, 0x3ff22cadf1a90ea6ull }, + { 0x4008eb851eb851ecull, 0x3ff22dfe9e9a14e3ull }, + { 0x4008ed916872b021ull, 0x3ff22f4f2fe0fa63ull }, + { 0x4008ef9db22d0e56ull, 0x3ff2309fa5824aa6ull }, + { 0x4008f1a9fbe76c8bull, 0x3ff231efff82900eull }, + { 0x4008f3b645a1cac1ull, 0x3ff233403de653dfull }, + { 0x4008f5c28f5c28f6ull, 0x3ff2349060b21e3eull }, + { 0x4008f7ced916872bull, 0x3ff235e067ea7631ull }, + { 0x4008f9db22d0e560ull, 0x3ff237305393e1a5ull }, + { 0x4008fbe76c8b4396ull, 0x3ff2388023b2e568ull }, + { 0x4008fdf3b645a1cbull, 0x3ff239cfd84c052aull }, + { 0x4009000000000000ull, 0x3ff23b1f7163c380ull }, + { 0x4009020c49ba5e35ull, 0x3ff23c6eeefea1e5ull }, + { 0x400904189374bc6bull, 0x3ff23dbe512120b8ull }, + { 0x40090624dd2f1aa0ull, 0x3ff23f0d97cfbf3aull }, + { 0x4009083126e978d5ull, 0x3ff2405cc30efb95ull }, + { 0x40090a3d70a3d70aull, 0x3ff241abd2e352d7ull }, + { 0x40090c49ba5e3540ull, 0x3ff242fac75140f6ull }, + { 0x40090e5604189375ull, 0x3ff24449a05d40ccull }, + { 0x400910624dd2f1aaull, 0x3ff245985e0bcc1aull }, + { 0x4009126e978d4fdfull, 0x3ff246e700615b8aull }, + { 0x4009147ae147ae15ull, 0x3ff24835876266aeull }, + { 0x400916872b020c4aull, 0x3ff24983f31363fbull }, + { 0x4009189374bc6a7full, 0x3ff24ad24378c8d5ull }, + { 0x40091a9fbe76c8b4ull, 0x3ff24c2078970984ull }, + { 0x40091cac083126eaull, 0x3ff24d6e9272993aull }, + { 0x40091eb851eb851full, 0x3ff24ebc910fea13ull }, + { 0x400920c49ba5e354ull, 0x3ff2500a74736d15ull }, + { 0x400922d0e5604189ull, 0x3ff251583ca1922eull }, + { 0x400924dd2f1a9fbfull, 0x3ff252a5e99ec839ull }, + { 0x400926e978d4fdf4ull, 0x3ff253f37b6f7cfaull }, + { 0x400928f5c28f5c29ull, 0x3ff25540f2181d20ull }, + { 0x40092b020c49ba5eull, 0x3ff2568e4d9d1447ull }, + { 0x40092d0e56041894ull, 0x3ff257db8e02ccf6ull }, + { 0x40092f1a9fbe76c9ull, 0x3ff25928b34db09full }, + { 0x40093126e978d4feull, 0x3ff25a75bd8227a1ull }, + { 0x4009333333333333ull, 0x3ff25bc2aca4994aull }, + { 0x4009353f7ced9169ull, 0x3ff25d0f80b96bd3ull }, + { 0x4009374bc6a7ef9eull, 0x3ff25e5c39c50462ull }, + { 0x4009395810624dd3ull, 0x3ff25fa8d7cbc70cull }, + { 0x40093b645a1cac08ull, 0x3ff260f55ad216d6ull }, + { 0x40093d70a3d70a3eull, 0x3ff26241c2dc55b1ull }, + { 0x40093f7ced916873ull, 0x3ff2638e0feee47cull }, + { 0x40094189374bc6a8ull, 0x3ff264da420e2309ull }, + { 0x40094395810624ddull, 0x3ff26626593e7017ull }, + { 0x400945a1cac08313ull, 0x3ff2677255842956ull }, + { 0x400947ae147ae148ull, 0x3ff268be36e3ab65ull }, + { 0x400949ba5e353f7dull, 0x3ff26a09fd6151d6ull }, + { 0x40094bc6a7ef9db2ull, 0x3ff26b55a9017729ull }, + { 0x40094dd2f1a9fbe8ull, 0x3ff26ca139c874d2ull }, + { 0x40094fdf3b645a1dull, 0x3ff26decafbaa335ull }, + { 0x400951eb851eb852ull, 0x3ff26f380adc59a8ull }, + { 0x400953f7ced91687ull, 0x3ff270834b31ee76ull }, + { 0x40095604189374bdull, 0x3ff271ce70bfb6daull }, + { 0x40095810624dd2f2ull, 0x3ff273197b8a0702ull }, + { 0x40095a1cac083127ull, 0x3ff274646b953210ull }, + { 0x40095c28f5c28f5cull, 0x3ff275af40e58a1bull }, + { 0x40095e353f7ced92ull, 0x3ff276f9fb7f602dull }, + { 0x4009604189374bc7ull, 0x3ff278449b670444ull }, + { 0x4009624dd2f1a9fcull, 0x3ff2798f20a0c552ull }, + { 0x4009645a1cac0831ull, 0x3ff27ad98b30f141ull }, + { 0x4009666666666667ull, 0x3ff27c23db1bd4eeull }, + { 0x40096872b020c49cull, 0x3ff27d6e1065bc2cull }, + { 0x40096a7ef9db22d1ull, 0x3ff27eb82b12f1c5ull }, + { 0x40096c8b43958106ull, 0x3ff280022b27bf79ull }, + { 0x40096e978d4fdf3cull, 0x3ff2814c10a86dfeull }, + { 0x400970a3d70a3d71ull, 0x3ff28295db994502ull }, + { 0x400972b020c49ba6ull, 0x3ff283df8bfe8b2aull }, + { 0x400974bc6a7ef9dbull, 0x3ff2852921dc8613ull }, + { 0x400976c8b4395811ull, 0x3ff286729d377a53ull }, + { 0x400978d4fdf3b646ull, 0x3ff287bbfe13ab76ull }, + { 0x40097ae147ae147bull, 0x3ff2890544755c03ull }, + { 0x40097ced916872b0ull, 0x3ff28a4e7060cd79ull }, + { 0x40097ef9db22d0e6ull, 0x3ff28b9781da4052ull }, + { 0x4009810624dd2f1bull, 0x3ff28ce078e5f3feull }, + { 0x400983126e978d50ull, 0x3ff28e29558826eaull }, + { 0x4009851eb851eb85ull, 0x3ff28f7217c5167full }, + { 0x4009872b020c49bbull, 0x3ff290babfa0ff1dull }, + { 0x400989374bc6a7f0ull, 0x3ff292034d201c21ull }, + { 0x40098b4395810625ull, 0x3ff2934bc046a7e3ull }, + { 0x40098d4fdf3b645aull, 0x3ff294941918dbb6ull }, + { 0x40098f5c28f5c290ull, 0x3ff295dc579aefecull }, + { 0x4009916872b020c5ull, 0x3ff297247bd11bcfull }, + { 0x40099374bc6a7efaull, 0x3ff2986c85bf95a8ull }, + { 0x400995810624dd2full, 0x3ff299b4756a92beull }, + { 0x4009978d4fdf3b65ull, 0x3ff29afc4ad64755ull }, + { 0x400999999999999aull, 0x3ff29c440606e6abull }, + { 0x40099ba5e353f7cfull, 0x3ff29d8ba700a301ull }, + { 0x40099db22d0e5604ull, 0x3ff29ed32dc7ad93ull }, + { 0x40099fbe76c8b439ull, 0x3ff2a01a9a60369eull }, + { 0x4009a1cac083126full, 0x3ff2a161ecce6d5cull }, + { 0x4009a3d70a3d70a4ull, 0x3ff2a2a925168007ull }, + { 0x4009a5e353f7ced9ull, 0x3ff2a3f0433c9bd8ull }, + { 0x4009a7ef9db22d0eull, 0x3ff2a5374744ed0aull }, + { 0x4009a9fbe76c8b44ull, 0x3ff2a67e31339ed5ull }, + { 0x4009ac083126e979ull, 0x3ff2a7c5010cdb74ull }, + { 0x4009ae147ae147aeull, 0x3ff2a90bb6d4cc22ull }, + { 0x4009b020c49ba5e3ull, 0x3ff2aa52528f991cull }, + { 0x4009b22d0e560419ull, 0x3ff2ab98d441699full }, + { 0x4009b4395810624eull, 0x3ff2acdf3bee63eaull }, + { 0x4009b645a1cac083ull, 0x3ff2ae25899aad3full }, + { 0x4009b851eb851eb8ull, 0x3ff2af6bbd4a69e3ull }, + { 0x4009ba5e353f7ceeull, 0x3ff2b0b1d701bd1eull }, + { 0x4009bc6a7ef9db23ull, 0x3ff2b1f7d6c4c937ull }, + { 0x4009be76c8b43958ull, 0x3ff2b33dbc97af7dull }, + { 0x4009c083126e978dull, 0x3ff2b483887e9041ull }, + { 0x4009c28f5c28f5c3ull, 0x3ff2b5c93a7d8ad9ull }, + { 0x4009c49ba5e353f8ull, 0x3ff2b70ed298bd9dull }, + { 0x4009c6a7ef9db22dull, 0x3ff2b85450d445ecull }, + { 0x4009c8b439581062ull, 0x3ff2b999b5344029ull }, + { 0x4009cac083126e98ull, 0x3ff2badeffbcc7bcull }, + { 0x4009cccccccccccdull, 0x3ff2bc243071f713ull }, + { 0x4009ced916872b02ull, 0x3ff2bd694757e7a1ull }, + { 0x4009d0e560418937ull, 0x3ff2beae4472b1e2ull }, + { 0x4009d2f1a9fbe76dull, 0x3ff2bff327c66d55ull }, + { 0x4009d4fdf3b645a2ull, 0x3ff2c137f1573082ull }, + { 0x4009d70a3d70a3d7ull, 0x3ff2c27ca12910f6ull }, + { 0x4009d916872b020cull, 0x3ff2c3c137402349ull }, + { 0x4009db22d0e56042ull, 0x3ff2c505b3a07b18ull }, + { 0x4009dd2f1a9fbe77ull, 0x3ff2c64a164e2b08ull }, + { 0x4009df3b645a1cacull, 0x3ff2c78e5f4d44c7ull }, + { 0x4009e147ae147ae1ull, 0x3ff2c8d28ea1d90dull }, + { 0x4009e353f7ced917ull, 0x3ff2ca16a44ff798ull }, + { 0x4009e5604189374cull, 0x3ff2cb5aa05baf32ull }, + { 0x4009e76c8b439581ull, 0x3ff2cc9e82c90dadull }, + { 0x4009e978d4fdf3b6ull, 0x3ff2cde24b9c1fe6ull }, + { 0x4009eb851eb851ecull, 0x3ff2cf25fad8f1c4ull }, + { 0x4009ed916872b021ull, 0x3ff2d06990838e36ull }, + { 0x4009ef9db22d0e56ull, 0x3ff2d1ad0c9fff3aull }, + { 0x4009f1a9fbe76c8bull, 0x3ff2d2f06f324dd6ull }, + { 0x4009f3b645a1cac1ull, 0x3ff2d433b83e821dull }, + { 0x4009f5c28f5c28f6ull, 0x3ff2d576e7c8a32dull }, + { 0x4009f7ced916872bull, 0x3ff2d6b9fdd4b72full }, + { 0x4009f9db22d0e560ull, 0x3ff2d7fcfa66c35aull }, + { 0x4009fbe76c8b4396ull, 0x3ff2d93fdd82cbf2ull }, + { 0x4009fdf3b645a1cbull, 0x3ff2da82a72cd446ull }, + { 0x400a000000000000ull, 0x3ff2dbc55768deb3ull }, + { 0x400a020c49ba5e35ull, 0x3ff2dd07ee3aeca3ull }, + { 0x400a04189374bc6bull, 0x3ff2de4a6ba6fe90ull }, + { 0x400a0624dd2f1aa0ull, 0x3ff2df8ccfb11400ull }, + { 0x400a083126e978d5ull, 0x3ff2e0cf1a5d2b86ull }, + { 0x400a0a3d70a3d70aull, 0x3ff2e2114baf42c8ull }, + { 0x400a0c49ba5e3540ull, 0x3ff2e35363ab5678ull }, + { 0x400a0e5604189375ull, 0x3ff2e49562556257ull }, + { 0x400a10624dd2f1aaull, 0x3ff2e5d747b16136ull }, + { 0x400a126e978d4fdfull, 0x3ff2e71913c34cf8ull }, + { 0x400a147ae147ae15ull, 0x3ff2e85ac68f1e8eull }, + { 0x400a16872b020c4aull, 0x3ff2e99c6018cdf9ull }, + { 0x400a189374bc6a7full, 0x3ff2eadde064524cull }, + { 0x400a1a9fbe76c8b4ull, 0x3ff2ec1f4775a1abull }, + { 0x400a1cac083126eaull, 0x3ff2ed609550b14bull }, + { 0x400a1eb851eb851full, 0x3ff2eea1c9f97573ull }, + { 0x400a20c49ba5e354ull, 0x3ff2efe2e573e17aull }, + { 0x400a22d0e5604189ull, 0x3ff2f123e7c3e7cdull }, + { 0x400a24dd2f1a9fbfull, 0x3ff2f264d0ed79eaull }, + { 0x400a26e978d4fdf4ull, 0x3ff2f3a5a0f4885eull }, + { 0x400a28f5c28f5c29ull, 0x3ff2f4e657dd02ceull }, + { 0x400a2b020c49ba5eull, 0x3ff2f626f5aad7f0ull }, + { 0x400a2d0e56041894ull, 0x3ff2f7677a61f590ull }, + { 0x400a2f1a9fbe76c9ull, 0x3ff2f8a7e606488bull }, + { 0x400a3126e978d4feull, 0x3ff2f9e8389bbcd3ull }, + { 0x400a333333333333ull, 0x3ff2fb2872263d6full }, + { 0x400a353f7ced9169ull, 0x3ff2fc6892a9b47dull }, + { 0x400a374bc6a7ef9eull, 0x3ff2fda89a2a0b2bull }, + { 0x400a395810624dd3ull, 0x3ff2fee888ab29c2ull }, + { 0x400a3b645a1cac08ull, 0x3ff300285e30f79cull }, + { 0x400a3d70a3d70a3eull, 0x3ff301681abf5b2cull }, + { 0x400a3f7ced916873ull, 0x3ff302a7be5a39fbull }, + { 0x400a4189374bc6a8ull, 0x3ff303e7490578a5ull }, + { 0x400a4395810624ddull, 0x3ff30526bac4fae3ull }, + { 0x400a45a1cac08313ull, 0x3ff30666139ca380ull }, + { 0x400a47ae147ae148ull, 0x3ff307a553905461ull }, + { 0x400a49ba5e353f7dull, 0x3ff308e47aa3ee80ull }, + { 0x400a4bc6a7ef9db2ull, 0x3ff30a2388db51f2ull }, + { 0x400a4dd2f1a9fbe8ull, 0x3ff30b627e3a5de4ull }, + { 0x400a4fdf3b645a1dull, 0x3ff30ca15ac4f099ull }, + { 0x400a51eb851eb852ull, 0x3ff30de01e7ee76eull }, + { 0x400a53f7ced91687ull, 0x3ff30f1ec96c1edbull }, + { 0x400a5604189374bdull, 0x3ff3105d5b90726full }, + { 0x400a5810624dd2f2ull, 0x3ff3119bd4efbcd2ull }, + { 0x400a5a1cac083127ull, 0x3ff312da358dd7caull }, + { 0x400a5c28f5c28f5cull, 0x3ff314187d6e9c33ull }, + { 0x400a5e353f7ced92ull, 0x3ff31556ac95e205ull }, + { 0x400a604189374bc7ull, 0x3ff31694c3078053ull }, + { 0x400a624dd2f1a9fcull, 0x3ff317d2c0c74d4aull }, + { 0x400a645a1cac0831ull, 0x3ff31910a5d91e35ull }, + { 0x400a666666666667ull, 0x3ff31a4e7240c777ull }, + { 0x400a6872b020c49cull, 0x3ff31b8c26021c92ull }, + { 0x400a6a7ef9db22d1ull, 0x3ff31cc9c120f021ull }, + { 0x400a6c8b43958106ull, 0x3ff31e0743a113deull }, + { 0x400a6e978d4fdf3cull, 0x3ff31f44ad8658a0ull }, + { 0x400a70a3d70a3d71ull, 0x3ff32081fed48e58ull }, + { 0x400a72b020c49ba6ull, 0x3ff321bf378f8418ull }, + { 0x400a74bc6a7ef9dbull, 0x3ff322fc57bb080bull }, + { 0x400a76c8b4395811ull, 0x3ff324395f5ae77full }, + { 0x400a78d4fdf3b646ull, 0x3ff325764e72eedcull }, + { 0x400a7ae147ae147bull, 0x3ff326b32506e9aaull }, + { 0x400a7ced916872b0ull, 0x3ff327efe31aa290ull }, + { 0x400a7ef9db22d0e6ull, 0x3ff3292c88b1e353ull }, + { 0x400a810624dd2f1bull, 0x3ff32a6915d074d7ull }, + { 0x400a83126e978d50ull, 0x3ff32ba58a7a1f20ull }, + { 0x400a851eb851eb85ull, 0x3ff32ce1e6b2a952ull }, + { 0x400a872b020c49bbull, 0x3ff32e1e2a7dd9b2ull }, + { 0x400a89374bc6a7f0ull, 0x3ff32f5a55df75a1ull }, + { 0x400a8b4395810625ull, 0x3ff3309668db41a5ull }, + { 0x400a8d4fdf3b645aull, 0x3ff331d263750162ull }, + { 0x400a8f5c28f5c290ull, 0x3ff3330e45b077a0ull }, + { 0x400a916872b020c5ull, 0x3ff3344a0f916645ull }, + { 0x400a9374bc6a7efaull, 0x3ff33585c11b8e5aull }, + { 0x400a95810624dd2full, 0x3ff336c15a52b00bull }, + { 0x400a978d4fdf3b65ull, 0x3ff337fcdb3a8aa5ull }, + { 0x400a99999999999aull, 0x3ff3393843d6dc96ull }, + { 0x400a9ba5e353f7cfull, 0x3ff33a73942b6372ull }, + { 0x400a9db22d0e5604ull, 0x3ff33baecc3bdbecull }, + { 0x400a9fbe76c8b43aull, 0x3ff33ce9ec0c01deull }, + { 0x400aa1cac083126full, 0x3ff33e24f39f9043ull }, + { 0x400aa3d70a3d70a4ull, 0x3ff33f5fe2fa413aull }, + { 0x400aa5e353f7ced9ull, 0x3ff3409aba1fce06ull }, + { 0x400aa7ef9db22d0eull, 0x3ff341d57913ef10ull }, + { 0x400aa9fbe76c8b44ull, 0x3ff343101fda5be4ull }, + { 0x400aac083126e979ull, 0x3ff3444aae76cb32ull }, + { 0x400aae147ae147aeull, 0x3ff3458524ecf2d0ull }, + { 0x400ab020c49ba5e3ull, 0x3ff346bf834087b9ull }, + { 0x400ab22d0e560419ull, 0x3ff347f9c9753e10ull }, + { 0x400ab4395810624eull, 0x3ff34933f78ec918ull }, + { 0x400ab645a1cac083ull, 0x3ff34a6e0d90db41ull }, + { 0x400ab851eb851eb8ull, 0x3ff34ba80b7f261cull }, + { 0x400aba5e353f7ceeull, 0x3ff34ce1f15d5a64ull }, + { 0x400abc6a7ef9db23ull, 0x3ff34e1bbf2f27f8ull }, + { 0x400abe76c8b43958ull, 0x3ff34f5574f83de1ull }, + { 0x400ac083126e978dull, 0x3ff3508f12bc4a4dull }, + { 0x400ac28f5c28f5c3ull, 0x3ff351c8987efa95ull }, + { 0x400ac49ba5e353f8ull, 0x3ff353020643fb34ull }, + { 0x400ac6a7ef9db22dull, 0x3ff3543b5c0ef7d4ull }, + { 0x400ac8b439581062ull, 0x3ff3557499e39b42ull }, + { 0x400acac083126e98ull, 0x3ff356adbfc58f77ull }, + { 0x400acccccccccccdull, 0x3ff357e6cdb87d93ull }, + { 0x400aced916872b02ull, 0x3ff3591fc3c00de1ull }, + { 0x400ad0e560418937ull, 0x3ff35a58a1dfe7d3ull }, + { 0x400ad2f1a9fbe76dull, 0x3ff35b91681bb208ull }, + { 0x400ad4fdf3b645a2ull, 0x3ff35cca16771245ull }, + { 0x400ad70a3d70a3d7ull, 0x3ff35e02acf5ad7dull }, + { 0x400ad916872b020cull, 0x3ff35f3b2b9b27cbull }, + { 0x400adb22d0e56042ull, 0x3ff36073926b2477ull }, + { 0x400add2f1a9fbe77ull, 0x3ff361abe16945f0ull }, + { 0x400adf3b645a1cacull, 0x3ff362e418992dd3ull }, + { 0x400ae147ae147ae1ull, 0x3ff3641c37fe7ceaull }, + { 0x400ae353f7ced917ull, 0x3ff365543f9cd329ull }, + { 0x400ae5604189374cull, 0x3ff3668c2f77cfadull }, + { 0x400ae76c8b439581ull, 0x3ff367c4079310c5ull }, + { 0x400ae978d4fdf3b6ull, 0x3ff368fbc7f233e9ull }, + { 0x400aeb851eb851ecull, 0x3ff36a337098d5beull }, + { 0x400aed916872b021ull, 0x3ff36b6b018a9217ull }, + { 0x400aef9db22d0e56ull, 0x3ff36ca27acb03f3ull }, + { 0x400af1a9fbe76c8bull, 0x3ff36dd9dc5dc57full }, + { 0x400af3b645a1cac1ull, 0x3ff36f1126467017ull }, + { 0x400af5c28f5c28f6ull, 0x3ff3704858889c43ull }, + { 0x400af7ced916872bull, 0x3ff3717f7327e1baull }, + { 0x400af9db22d0e560ull, 0x3ff372b67627d762ull }, + { 0x400afbe76c8b4396ull, 0x3ff373ed618c1350ull }, + { 0x400afdf3b645a1cbull, 0x3ff3752435582ac7ull }, + { 0x400b000000000000ull, 0x3ff3765af18fb239ull }, + { 0x400b020c49ba5e35ull, 0x3ff3779196363d49ull }, + { 0x400b04189374bc6bull, 0x3ff378c8234f5ec8ull }, + { 0x400b0624dd2f1aa0ull, 0x3ff379fe98dea8b8ull }, + { 0x400b083126e978d5ull, 0x3ff37b34f6e7ac4cull }, + { 0x400b0a3d70a3d70aull, 0x3ff37c6b3d6df9e5ull }, + { 0x400b0c49ba5e3540ull, 0x3ff37da16c752119ull }, + { 0x400b0e5604189375ull, 0x3ff37ed78400b0a8ull }, + { 0x400b10624dd2f1aaull, 0x3ff3800d8414368bull }, + { 0x400b126e978d4fdfull, 0x3ff381436cb33fe7ull }, + { 0x400b147ae147ae15ull, 0x3ff382793de15917ull }, + { 0x400b16872b020c4aull, 0x3ff383aef7a20da2ull }, + { 0x400b189374bc6a7full, 0x3ff384e499f8e847ull }, + { 0x400b1a9fbe76c8b4ull, 0x3ff3861a24e972f4ull }, + { 0x400b1cac083126eaull, 0x3ff3874f987736cdull }, + { 0x400b1eb851eb851full, 0x3ff38884f4a5bc24ull }, + { 0x400b20c49ba5e354ull, 0x3ff389ba39788a82ull }, + { 0x400b22d0e5604189ull, 0x3ff38aef66f328a2ull }, + { 0x400b24dd2f1a9fbfull, 0x3ff38c247d191c74ull }, + { 0x400b26e978d4fdf4ull, 0x3ff38d597bedeb19ull }, + { 0x400b28f5c28f5c29ull, 0x3ff38e8e637518eaull }, + { 0x400b2b020c49ba5eull, 0x3ff38fc333b22970ull }, + { 0x400b2d0e56041894ull, 0x3ff390f7eca89f6dull }, + { 0x400b2f1a9fbe76c9ull, 0x3ff3922c8e5bfcd5ull }, + { 0x400b3126e978d4feull, 0x3ff3936118cfc2d1ull }, + { 0x400b333333333333ull, 0x3ff394958c0771c0ull }, + { 0x400b353f7ced9169ull, 0x3ff395c9e8068938ull }, + { 0x400b374bc6a7ef9eull, 0x3ff396fe2cd08803ull }, + { 0x400b395810624dd3ull, 0x3ff398325a68ec21ull }, + { 0x400b3b645a1cac08ull, 0x3ff3996670d332c9ull }, + { 0x400b3d70a3d70a3eull, 0x3ff39a9a7012d868ull }, + { 0x400b3f7ced916873ull, 0x3ff39bce582b58a2ull }, + { 0x400b4189374bc6a8ull, 0x3ff39d0229202e53ull }, + { 0x400b4395810624ddull, 0x3ff39e35e2f4d38cull }, + { 0x400b45a1cac08313ull, 0x3ff39f6985acc197ull }, + { 0x400b47ae147ae148ull, 0x3ff3a09d114b70f5ull }, + { 0x400b49ba5e353f7dull, 0x3ff3a1d085d45960ull }, + { 0x400b4bc6a7ef9db2ull, 0x3ff3a303e34af1c9ull }, + { 0x400b4dd2f1a9fbe8ull, 0x3ff3a43729b2b05bull }, + { 0x400b4fdf3b645a1dull, 0x3ff3a56a590f0a76ull }, + { 0x400b51eb851eb852ull, 0x3ff3a69d716374b6ull }, + { 0x400b53f7ced91687ull, 0x3ff3a7d072b362f1ull }, + { 0x400b5604189374bdull, 0x3ff3a9035d024833ull }, + { 0x400b5810624dd2f2ull, 0x3ff3aa36305396c4ull }, + { 0x400b5a1cac083127ull, 0x3ff3ab68ecaac024ull }, + { 0x400b5c28f5c28f5cull, 0x3ff3ac9b920b3510ull }, + { 0x400b5e353f7ced92ull, 0x3ff3adce2078657cull }, + { 0x400b604189374bc7ull, 0x3ff3af0097f5c097ull }, + { 0x400b624dd2f1a9fcull, 0x3ff3b032f886b4cdull }, + { 0x400b645a1cac0831ull, 0x3ff3b165422eafc3ull }, + { 0x400b666666666667ull, 0x3ff3b29774f11e59ull }, + { 0x400b6872b020c49cull, 0x3ff3b3c990d16cacull }, + { 0x400b6a7ef9db22d1ull, 0x3ff3b4fb95d30613ull }, + { 0x400b6c8b43958106ull, 0x3ff3b62d83f95521ull }, + { 0x400b6e978d4fdf3cull, 0x3ff3b75f5b47c3a7ull }, + { 0x400b70a3d70a3d71ull, 0x3ff3b8911bc1bab0ull }, + { 0x400b72b020c49ba6ull, 0x3ff3b9c2c56aa284ull }, + { 0x400b74bc6a7ef9dbull, 0x3ff3baf45845e2aaull }, + { 0x400b76c8b4395811ull, 0x3ff3bc25d456e1e3ull }, + { 0x400b78d4fdf3b646ull, 0x3ff3bd5739a1062dull }, + { 0x400b7ae147ae147bull, 0x3ff3be888827b4c8ull }, + { 0x400b7ced916872b0ull, 0x3ff3bfb9bfee522cull }, + { 0x400b7ef9db22d0e6ull, 0x3ff3c0eae0f84214ull }, + { 0x400b810624dd2f1bull, 0x3ff3c21beb48e774ull }, + { 0x400b83126e978d50ull, 0x3ff3c34cdee3a482ull }, + { 0x400b851eb851eb85ull, 0x3ff3c47dbbcbdab3ull }, + { 0x400b872b020c49bbull, 0x3ff3c5ae8204eab8ull }, + { 0x400b89374bc6a7f0ull, 0x3ff3c6df31923483ull }, + { 0x400b8b4395810625ull, 0x3ff3c80fca771744ull }, + { 0x400b8d4fdf3b645aull, 0x3ff3c9404cb6f16bull }, + { 0x400b8f5c28f5c290ull, 0x3ff3ca70b85520aaull }, + { 0x400b916872b020c5ull, 0x3ff3cba10d5501efull }, + { 0x400b9374bc6a7efaull, 0x3ff3ccd14bb9f16aull }, + { 0x400b95810624dd2full, 0x3ff3ce0173874a8bull }, + { 0x400b978d4fdf3b65ull, 0x3ff3cf3184c06805ull }, + { 0x400b99999999999aull, 0x3ff3d0617f68a3c8ull }, + { 0x400b9ba5e353f7cfull, 0x3ff3d19163835707ull }, + { 0x400b9db22d0e5604ull, 0x3ff3d2c13113da37ull }, + { 0x400b9fbe76c8b43aull, 0x3ff3d3f0e81d850dull }, + { 0x400ba1cac083126full, 0x3ff3d52088a3ae80ull }, + { 0x400ba3d70a3d70a4ull, 0x3ff3d65012a9acc9ull }, + { 0x400ba5e353f7ced9ull, 0x3ff3d77f8632d564ull }, + { 0x400ba7ef9db22d0eull, 0x3ff3d8aee3427d0eull }, + { 0x400ba9fbe76c8b44ull, 0x3ff3d9de29dbf7c9ull }, + { 0x400bac083126e979ull, 0x3ff3db0d5a0298d6ull }, + { 0x400bae147ae147aeull, 0x3ff3dc3c73b9b2bcull }, + { 0x400bb020c49ba5e3ull, 0x3ff3dd6b77049744ull }, + { 0x400bb22d0e560419ull, 0x3ff3de9a63e6977cull }, + { 0x400bb4395810624eull, 0x3ff3dfc93a6303b3ull }, + { 0x400bb645a1cac083ull, 0x3ff3e0f7fa7d2b7eull }, + { 0x400bb851eb851eb8ull, 0x3ff3e226a4385db6ull }, + { 0x400bba5e353f7ceeull, 0x3ff3e3553797e878ull }, + { 0x400bbc6a7ef9db23ull, 0x3ff3e483b49f1923ull }, + { 0x400bbe76c8b43958ull, 0x3ff3e5b21b513c60ull }, + { 0x400bc083126e978dull, 0x3ff3e6e06bb19e18ull }, + { 0x400bc28f5c28f5c3ull, 0x3ff3e80ea5c3897eull }, + { 0x400bc49ba5e353f8ull, 0x3ff3e93cc98a4905ull }, + { 0x400bc6a7ef9db22dull, 0x3ff3ea6ad7092669ull }, + { 0x400bc8b439581062ull, 0x3ff3eb98ce436aabull }, + { 0x400bcac083126e98ull, 0x3ff3ecc6af3c5e14ull }, + { 0x400bcccccccccccdull, 0x3ff3edf479f7482full }, + { 0x400bced916872b02ull, 0x3ff3ef222e776fd2ull }, + { 0x400bd0e560418937ull, 0x3ff3f04fccc01b16ull }, + { 0x400bd2f1a9fbe76dull, 0x3ff3f17d54d48f5eull }, + { 0x400bd4fdf3b645a2ull, 0x3ff3f2aac6b81152ull }, + { 0x400bd70a3d70a3d7ull, 0x3ff3f3d8226de4e1ull }, + { 0x400bd916872b020cull, 0x3ff3f50567f94d44ull }, + { 0x400bdb22d0e56042ull, 0x3ff3f632975d8cfbull }, + { 0x400bdd2f1a9fbe77ull, 0x3ff3f75fb09de5ccull }, + { 0x400bdf3b645a1cacull, 0x3ff3f88cb3bd98c6ull }, + { 0x400be147ae147ae1ull, 0x3ff3f9b9a0bfe641ull }, + { 0x400be353f7ced917ull, 0x3ff3fae677a80ddfull }, + { 0x400be5604189374cull, 0x3ff3fc1338794e88ull }, + { 0x400be76c8b439581ull, 0x3ff3fd3fe336e66eull }, + { 0x400be978d4fdf3b6ull, 0x3ff3fe6c77e4130dull }, + { 0x400beb851eb851ecull, 0x3ff3ff98f684112bull }, + { 0x400bed916872b021ull, 0x3ff400c55f1a1cd5ull }, + { 0x400bef9db22d0e56ull, 0x3ff401f1b1a97163ull }, + { 0x400bf1a9fbe76c8bull, 0x3ff4031dee35497aull }, + { 0x400bf3b645a1cac1ull, 0x3ff4044a14c0df05ull }, + { 0x400bf5c28f5c28f6ull, 0x3ff40576254f6b3bull }, + { 0x400bf7ced916872bull, 0x3ff406a21fe4269full }, + { 0x400bf9db22d0e560ull, 0x3ff407ce048248fdull }, + { 0x400bfbe76c8b4396ull, 0x3ff408f9d32d096eull }, + { 0x400bfdf3b645a1cbull, 0x3ff40a258be79e55ull }, + { 0x400c000000000000ull, 0x3ff40b512eb53d60ull }, + { 0x400c020c49ba5e35ull, 0x3ff40c7cbb991b8aull }, + { 0x400c04189374bc6bull, 0x3ff40da832966d1aull }, + { 0x400c0624dd2f1aa0ull, 0x3ff40ed393b065a2ull }, + { 0x400c083126e978d5ull, 0x3ff40ffedeea3801ull }, + { 0x400c0a3d70a3d70aull, 0x3ff4112a14471662ull }, + { 0x400c0c49ba5e3540ull, 0x3ff4125533ca323full }, + { 0x400c0e5604189375ull, 0x3ff413803d76bc5cull }, + { 0x400c10624dd2f1aaull, 0x3ff414ab314fe4caull }, + { 0x400c126e978d4fdfull, 0x3ff415d60f58daebull }, + { 0x400c147ae147ae15ull, 0x3ff41700d794cd6cull }, + { 0x400c16872b020c4aull, 0x3ff4182b8a06ea47ull }, + { 0x400c189374bc6a7full, 0x3ff4195626b25ec5ull }, + { 0x400c1a9fbe76c8b4ull, 0x3ff41a80ad9a577eull }, + { 0x400c1cac083126eaull, 0x3ff41bab1ec20058ull }, + { 0x400c1eb851eb851full, 0x3ff41cd57a2c8486ull }, + { 0x400c20c49ba5e354ull, 0x3ff41dffbfdd0e8bull }, + { 0x400c22d0e5604189ull, 0x3ff41f29efd6c83aull }, + { 0x400c24dd2f1a9fbfull, 0x3ff420540a1cdab4ull }, + { 0x400c26e978d4fdf4ull, 0x3ff4217e0eb26e68ull }, + { 0x400c28f5c28f5c29ull, 0x3ff422a7fd9aab17ull }, + { 0x400c2b020c49ba5eull, 0x3ff423d1d6d8b7d2ull }, + { 0x400c2d0e56041894ull, 0x3ff424fb9a6fbaf8ull }, + { 0x400c2f1a9fbe76c9ull, 0x3ff426254862da38ull }, + { 0x400c3126e978d4feull, 0x3ff4274ee0b53a94ull }, + { 0x400c333333333333ull, 0x3ff42878636a005dull }, + { 0x400c353f7ced9169ull, 0x3ff429a1d0844f34ull }, + { 0x400c374bc6a7ef9eull, 0x3ff42acb28074a0cull }, + { 0x400c395810624dd3ull, 0x3ff42bf469f6132aull }, + { 0x400c3b645a1cac08ull, 0x3ff42d1d9653cc21ull }, + { 0x400c3d70a3d70a3eull, 0x3ff42e46ad2395dbull }, + { 0x400c3f7ced916873ull, 0x3ff42f6fae68908full }, + { 0x400c4189374bc6a8ull, 0x3ff430989a25dbc8ull }, + { 0x400c4395810624ddull, 0x3ff431c1705e9662ull }, + { 0x400c45a1cac08313ull, 0x3ff432ea3115de8eull }, + { 0x400c47ae147ae148ull, 0x3ff43412dc4ed1cdull }, + { 0x400c49ba5e353f7dull, 0x3ff4353b720c8cf3ull }, + { 0x400c4bc6a7ef9db2ull, 0x3ff43663f2522c28ull }, + { 0x400c4dd2f1a9fbe8ull, 0x3ff4378c5d22cae8ull }, + { 0x400c4fdf3b645a1dull, 0x3ff438b4b28183ffull }, + { 0x400c51eb851eb852ull, 0x3ff439dcf271718full }, + { 0x400c53f7ced91687ull, 0x3ff43b051cf5ad0eull }, + { 0x400c5604189374bdull, 0x3ff43c2d32114f46ull }, + { 0x400c5810624dd2f2ull, 0x3ff43d5531c77052ull }, + { 0x400c5a1cac083127ull, 0x3ff43e7d1c1b27a6ull }, + { 0x400c5c28f5c28f5cull, 0x3ff43fa4f10f8c06ull }, + { 0x400c5e353f7ced92ull, 0x3ff440ccb0a7b390ull }, + { 0x400c604189374bc7ull, 0x3ff441f45ae6b3b1ull }, + { 0x400c624dd2f1a9fcull, 0x3ff4431befcfa12full }, + { 0x400c645a1cac0831ull, 0x3ff444436f659025ull }, + { 0x400c666666666667ull, 0x3ff4456ad9ab9402ull }, + { 0x400c6872b020c49cull, 0x3ff446922ea4bf8aull }, + { 0x400c6a7ef9db22d1ull, 0x3ff447b96e5424daull }, + { 0x400c6c8b43958106ull, 0x3ff448e098bcd563ull }, + { 0x400c6e978d4fdf3cull, 0x3ff44a07ade1e1ecull }, + { 0x400c70a3d70a3d71ull, 0x3ff44b2eadc65a94ull }, + { 0x400c72b020c49ba6ull, 0x3ff44c55986d4eceull }, + { 0x400c74bc6a7ef9dbull, 0x3ff44d7c6dd9cd66ull }, + { 0x400c76c8b4395811ull, 0x3ff44ea32e0ee480ull }, + { 0x400c78d4fdf3b646ull, 0x3ff44fc9d90fa193ull }, + { 0x400c7ae147ae147bull, 0x3ff450f06edf1172ull }, + { 0x400c7ced916872b0ull, 0x3ff45216ef804045ull }, + { 0x400c7ef9db22d0e6ull, 0x3ff4533d5af6398cull }, + { 0x400c810624dd2f1bull, 0x3ff45463b1440820ull }, + { 0x400c83126e978d50ull, 0x3ff45589f26cb630ull }, + { 0x400c851eb851eb85ull, 0x3ff456b01e734d46ull }, + { 0x400c872b020c49bbull, 0x3ff457d6355ad644ull }, + { 0x400c89374bc6a7f0ull, 0x3ff458fc37265963ull }, + { 0x400c8b4395810625ull, 0x3ff45a2223d8de35ull }, + { 0x400c8d4fdf3b645aull, 0x3ff45b47fb756ba8ull }, + { 0x400c8f5c28f5c290ull, 0x3ff45c6dbdff0800ull }, + { 0x400c916872b020c5ull, 0x3ff45d936b78b8dcull }, + { 0x400c9374bc6a7efaull, 0x3ff45eb903e58334ull }, + { 0x400c95810624dd2full, 0x3ff45fde87486b5bull }, + { 0x400c978d4fdf3b65ull, 0x3ff46103f5a474fdull }, + { 0x400c99999999999aull, 0x3ff462294efca31full }, + { 0x400c9ba5e353f7cfull, 0x3ff4634e9353f824ull }, + { 0x400c9db22d0e5604ull, 0x3ff46473c2ad75c6ull }, + { 0x400c9fbe76c8b43aull, 0x3ff46598dd0c1d1dull }, + { 0x400ca1cac083126full, 0x3ff466bde272ee97ull }, + { 0x400ca3d70a3d70a4ull, 0x3ff467e2d2e4ea02ull }, + { 0x400ca5e353f7ced9ull, 0x3ff46907ae650e85ull }, + { 0x400ca7ef9db22d0full, 0x3ff46a2c74f65aa5ull }, + { 0x400ca9fbe76c8b44ull, 0x3ff46b51269bcc3dull }, + { 0x400cac083126e979ull, 0x3ff46c75c358608bull }, + { 0x400cae147ae147aeull, 0x3ff46d9a4b2f1423ull }, + { 0x400cb020c49ba5e3ull, 0x3ff46ebebe22e2faull }, + { 0x400cb22d0e560419ull, 0x3ff46fe31c36c85eull }, + { 0x400cb4395810624eull, 0x3ff47107656dbefbull }, + { 0x400cb645a1cac083ull, 0x3ff4722b99cac0d9ull }, + { 0x400cb851eb851eb8ull, 0x3ff4734fb950c75full }, + { 0x400cba5e353f7ceeull, 0x3ff47473c402cb4full }, + { 0x400cbc6a7ef9db23ull, 0x3ff47597b9e3c4c9ull }, + { 0x400cbe76c8b43958ull, 0x3ff476bb9af6ab4aull }, + { 0x400cc083126e978dull, 0x3ff477df673e75adull }, + { 0x400cc28f5c28f5c3ull, 0x3ff479031ebe1a2dull }, + { 0x400cc49ba5e353f8ull, 0x3ff47a26c1788e60ull }, + { 0x400cc6a7ef9db22dull, 0x3ff47b4a4f70c73aull }, + { 0x400cc8b439581062ull, 0x3ff47c6dc8a9b910ull }, + { 0x400ccac083126e98ull, 0x3ff47d912d265796ull }, + { 0x400ccccccccccccdull, 0x3ff47eb47ce995d9ull }, + { 0x400cced916872b02ull, 0x3ff47fd7b7f6664dull }, + { 0x400cd0e560418937ull, 0x3ff480fade4fbabeull }, + { 0x400cd2f1a9fbe76dull, 0x3ff4821deff8845eull }, + { 0x400cd4fdf3b645a2ull, 0x3ff48340ecf3b3b7ull }, + { 0x400cd70a3d70a3d7ull, 0x3ff48463d54438b9ull }, + { 0x400cd916872b020cull, 0x3ff48586a8ed02b1ull }, + { 0x400cdb22d0e56042ull, 0x3ff486a967f1004dull }, + { 0x400cdd2f1a9fbe77ull, 0x3ff487cc12531f99ull }, + { 0x400cdf3b645a1cacull, 0x3ff488eea8164e03ull }, + { 0x400ce147ae147ae1ull, 0x3ff48a11293d785cull }, + { 0x400ce353f7ced917ull, 0x3ff48b3395cb8ad1ull }, + { 0x400ce5604189374cull, 0x3ff48c55edc370f2ull }, + { 0x400ce76c8b439581ull, 0x3ff48d78312815b2ull }, + { 0x400ce978d4fdf3b6ull, 0x3ff48e9a5ffc6362ull }, + { 0x400ceb851eb851ecull, 0x3ff48fbc7a4343b6ull }, + { 0x400ced916872b021ull, 0x3ff490de7fff9fc4ull }, + { 0x400cef9db22d0e56ull, 0x3ff4920071346003ull }, + { 0x400cf1a9fbe76c8bull, 0x3ff493224de46c4cull }, + { 0x400cf3b645a1cac1ull, 0x3ff494441612abdbull }, + { 0x400cf5c28f5c28f6ull, 0x3ff49565c9c2054cull }, + { 0x400cf7ced916872bull, 0x3ff4968768f55ea1ull }, + { 0x400cf9db22d0e560ull, 0x3ff497a8f3af9d3cull }, + { 0x400cfbe76c8b4396ull, 0x3ff498ca69f3a5e2ull }, + { 0x400cfdf3b645a1cbull, 0x3ff499ebcbc45cbdull }, + { 0x400d000000000000ull, 0x3ff49b0d1924a557ull }, + { 0x400d020c49ba5e35ull, 0x3ff49c2e5217629full }, + { 0x400d04189374bc6bull, 0x3ff49d4f769f76eaull }, + { 0x400d0624dd2f1aa0ull, 0x3ff49e7086bfc3ecull }, + { 0x400d083126e978d5ull, 0x3ff49f91827b2ac0ull }, + { 0x400d0a3d70a3d70aull, 0x3ff4a0b269d48be3ull }, + { 0x400d0c49ba5e3540ull, 0x3ff4a1d33ccec73aull }, + { 0x400d0e5604189375ull, 0x3ff4a2f3fb6cbc08ull }, + { 0x400d10624dd2f1aaull, 0x3ff4a414a5b148fbull }, + { 0x400d126e978d4fdfull, 0x3ff4a5353b9f4c22ull }, + { 0x400d147ae147ae15ull, 0x3ff4a655bd39a2f3ull }, + { 0x400d16872b020c4aull, 0x3ff4a7762a832a46ull }, + { 0x400d189374bc6a7full, 0x3ff4a896837ebe5cull }, + { 0x400d1a9fbe76c8b4ull, 0x3ff4a9b6c82f3ad8ull }, + { 0x400d1cac083126eaull, 0x3ff4aad6f8977ac5ull }, + { 0x400d1eb851eb851full, 0x3ff4abf714ba5893ull }, + { 0x400d20c49ba5e354ull, 0x3ff4ad171c9aae16ull }, + { 0x400d22d0e5604189ull, 0x3ff4ae37103b548dull }, + { 0x400d24dd2f1a9fbfull, 0x3ff4af56ef9f2498ull }, + { 0x400d26e978d4fdf4ull, 0x3ff4b076bac8f63full }, + { 0x400d28f5c28f5c29ull, 0x3ff4b19671bba0f2ull }, + { 0x400d2b020c49ba5eull, 0x3ff4b2b61479fb87ull }, + { 0x400d2d0e56041894ull, 0x3ff4b3d5a306dc3bull }, + { 0x400d2f1a9fbe76c9ull, 0x3ff4b4f51d6518b0ull }, + { 0x400d3126e978d4feull, 0x3ff4b614839785f3ull }, + { 0x400d333333333333ull, 0x3ff4b733d5a0f876ull }, + { 0x400d353f7ced9169ull, 0x3ff4b85313844413ull }, + { 0x400d374bc6a7ef9eull, 0x3ff4b9723d443c0cull }, + { 0x400d395810624dd3ull, 0x3ff4ba9152e3b30bull }, + { 0x400d3b645a1cac08ull, 0x3ff4bbb054657b20ull }, + { 0x400d3d70a3d70a3eull, 0x3ff4bccf41cc65c8ull }, + { 0x400d3f7ced916873ull, 0x3ff4bdee1b1b43e4ull }, + { 0x400d4189374bc6a8ull, 0x3ff4bf0ce054e5bfull }, + { 0x400d4395810624ddull, 0x3ff4c02b917c1b0dull }, + { 0x400d45a1cac08313ull, 0x3ff4c14a2e93b2ecull }, + { 0x400d47ae147ae148ull, 0x3ff4c268b79e7be0ull }, + { 0x400d49ba5e353f7dull, 0x3ff4c3872c9f43d9ull }, + { 0x400d4bc6a7ef9db2ull, 0x3ff4c4a58d98d82full }, + { 0x400d4dd2f1a9fbe8ull, 0x3ff4c5c3da8e05a6ull }, + { 0x400d4fdf3b645a1dull, 0x3ff4c6e213819868ull }, + { 0x400d51eb851eb852ull, 0x3ff4c80038765c0bull }, + { 0x400d53f7ced91687ull, 0x3ff4c91e496f1b90ull }, + { 0x400d5604189374bdull, 0x3ff4ca3c466ea160ull }, + { 0x400d5810624dd2f2ull, 0x3ff4cb5a2f77b74full }, + { 0x400d5a1cac083127ull, 0x3ff4cc78048d269dull }, + { 0x400d5c28f5c28f5cull, 0x3ff4cd95c5b1b7f4ull }, + { 0x400d5e353f7ced92ull, 0x3ff4ceb372e83369ull }, + { 0x400d604189374bc7ull, 0x3ff4cfd10c336079ull }, + { 0x400d624dd2f1a9fcull, 0x3ff4d0ee91960612ull }, + { 0x400d645a1cac0831ull, 0x3ff4d20c0312ea88ull }, + { 0x400d666666666667ull, 0x3ff4d32960acd39full }, + { 0x400d6872b020c49cull, 0x3ff4d446aa668683ull }, + { 0x400d6a7ef9db22d1ull, 0x3ff4d563e042c7ceull }, + { 0x400d6c8b43958106ull, 0x3ff4d68102445b84ull }, + { 0x400d6e978d4fdf3cull, 0x3ff4d79e106e0519ull }, + { 0x400d70a3d70a3d71ull, 0x3ff4d8bb0ac28769ull }, + { 0x400d72b020c49ba6ull, 0x3ff4d9d7f144a4beull }, + { 0x400d74bc6a7ef9dbull, 0x3ff4daf4c3f71ed1ull }, + { 0x400d76c8b4395811ull, 0x3ff4dc1182dcb6c5ull }, + { 0x400d78d4fdf3b646ull, 0x3ff4dd2e2df82d2aull }, + { 0x400d7ae147ae147bull, 0x3ff4de4ac54c41ffull }, + { 0x400d7ced916872b0ull, 0x3ff4df6748dbb4afull }, + { 0x400d7ef9db22d0e6ull, 0x3ff4e083b8a94414ull }, + { 0x400d810624dd2f1bull, 0x3ff4e1a014b7ae72ull }, + { 0x400d83126e978d50ull, 0x3ff4e2bc5d09b17eull }, + { 0x400d851eb851eb85ull, 0x3ff4e3d891a20a5aull }, + { 0x400d872b020c49bbull, 0x3ff4e4f4b2837598ull }, + { 0x400d89374bc6a7f0ull, 0x3ff4e610bfb0af34ull }, + { 0x400d8b4395810625ull, 0x3ff4e72cb92c729cull }, + { 0x400d8d4fdf3b645aull, 0x3ff4e8489ef97aabull }, + { 0x400d8f5c28f5c290ull, 0x3ff4e964711a81acull }, + { 0x400d916872b020c5ull, 0x3ff4ea802f924157ull }, + { 0x400d9374bc6a7efaull, 0x3ff4eb9bda6372d4ull }, + { 0x400d95810624dd2full, 0x3ff4ecb77190cebbull }, + { 0x400d978d4fdf3b65ull, 0x3ff4edd2f51d0d12ull }, + { 0x400d99999999999aull, 0x3ff4eeee650ae550ull }, + { 0x400d9ba5e353f7cfull, 0x3ff4f009c15d0e59ull }, + { 0x400d9db22d0e5604ull, 0x3ff4f1250a163e84ull }, + { 0x400d9fbe76c8b43aull, 0x3ff4f2403f392b97ull }, + { 0x400da1cac083126full, 0x3ff4f35b60c88ac6ull }, + { 0x400da3d70a3d70a4ull, 0x3ff4f4766ec710b8ull }, + { 0x400da5e353f7ced9ull, 0x3ff4f59169377183ull }, + { 0x400da7ef9db22d0full, 0x3ff4f6ac501c60afull }, + { 0x400da9fbe76c8b44ull, 0x3ff4f7c723789133ull }, + { 0x400dac083126e979ull, 0x3ff4f8e1e34eb578ull }, + { 0x400dae147ae147aeull, 0x3ff4f9fc8fa17f57ull }, + { 0x400db020c49ba5e4ull, 0x3ff4fb172873a01cull }, + { 0x400db22d0e560419ull, 0x3ff4fc31adc7c882ull }, + { 0x400db4395810624eull, 0x3ff4fd4c1fa0a8b8ull }, + { 0x400db645a1cac083ull, 0x3ff4fe667e00f05dull }, + { 0x400db851eb851eb8ull, 0x3ff4ff80c8eb4e82ull }, + { 0x400dba5e353f7ceeull, 0x3ff5009b006271acull }, + { 0x400dbc6a7ef9db23ull, 0x3ff501b5246907ceull }, + { 0x400dbe76c8b43958ull, 0x3ff502cf3501be52ull }, + { 0x400dc083126e978dull, 0x3ff503e9322f4211ull }, + { 0x400dc28f5c28f5c3ull, 0x3ff505031bf43f58ull }, + { 0x400dc49ba5e353f8ull, 0x3ff5061cf25361e6ull }, + { 0x400dc6a7ef9db22dull, 0x3ff50736b54f54eeull }, + { 0x400dc8b439581062ull, 0x3ff5085064eac316ull }, + { 0x400dcac083126e98ull, 0x3ff5096a01285676ull }, + { 0x400dcccccccccccdull, 0x3ff50a838a0ab89aull }, + { 0x400dced916872b02ull, 0x3ff50b9cff949281ull }, + { 0x400dd0e560418937ull, 0x3ff50cb661c88c9full }, + { 0x400dd2f1a9fbe76dull, 0x3ff50dcfb0a94edbull }, + { 0x400dd4fdf3b645a2ull, 0x3ff50ee8ec398090ull }, + { 0x400dd70a3d70a3d7ull, 0x3ff51002147bc88cull }, + { 0x400dd916872b020cull, 0x3ff5111b2972cd13ull }, + { 0x400ddb22d0e56042ull, 0x3ff512342b2133deull }, + { 0x400ddd2f1a9fbe77ull, 0x3ff5134d1989a217ull }, + { 0x400ddf3b645a1cacull, 0x3ff51465f4aebc5full }, + { 0x400de147ae147ae1ull, 0x3ff5157ebc9326ceull }, + { 0x400de353f7ced917ull, 0x3ff51697713984edull }, + { 0x400de5604189374cull, 0x3ff517b012a479bcull }, + { 0x400de76c8b439581ull, 0x3ff518c8a0d6a7b1ull }, + { 0x400de978d4fdf3b6ull, 0x3ff519e11bd2b0b7ull }, + { 0x400deb851eb851ecull, 0x3ff51af9839b362dull }, + { 0x400ded916872b021ull, 0x3ff51c11d832d8e9ull }, + { 0x400def9db22d0e56ull, 0x3ff51d2a199c3938ull }, + { 0x400df1a9fbe76c8bull, 0x3ff51e4247d9f6dcull }, + { 0x400df3b645a1cac1ull, 0x3ff51f5a62eeb10cull }, + { 0x400df5c28f5c28f6ull, 0x3ff520726add0678ull }, + { 0x400df7ced916872bull, 0x3ff5218a5fa79543ull }, + { 0x400df9db22d0e560ull, 0x3ff522a24150fb0aull }, + { 0x400dfbe76c8b4396ull, 0x3ff523ba0fdbd4dfull }, + { 0x400dfdf3b645a1cbull, 0x3ff524d1cb4abf4bull }, + { 0x400e000000000000ull, 0x3ff525e973a0564full }, + { 0x400e020c49ba5e35ull, 0x3ff5270108df3562ull }, + { 0x400e04189374bc6bull, 0x3ff528188b09f773ull }, + { 0x400e0624dd2f1aa0ull, 0x3ff5292ffa2336e8ull }, + { 0x400e083126e978d5ull, 0x3ff52a47562d8da0ull }, + { 0x400e0a3d70a3d70aull, 0x3ff52b5e9f2b94f0ull }, + { 0x400e0c49ba5e3540ull, 0x3ff52c75d51fe5a6ull }, + { 0x400e0e5604189375ull, 0x3ff52d8cf80d1808ull }, + { 0x400e10624dd2f1aaull, 0x3ff52ea407f5c3d4ull }, + { 0x400e126e978d4fdfull, 0x3ff52fbb04dc8041ull }, + { 0x400e147ae147ae15ull, 0x3ff530d1eec3e3feull }, + { 0x400e16872b020c4aull, 0x3ff531e8c5ae8531ull }, + { 0x400e189374bc6a7full, 0x3ff532ff899ef97cull }, + { 0x400e1a9fbe76c8b4ull, 0x3ff534163a97d5f8ull }, + { 0x400e1cac083126eaull, 0x3ff5352cd89baf38ull }, + { 0x400e1eb851eb851full, 0x3ff5364363ad1947ull }, + { 0x400e20c49ba5e354ull, 0x3ff53759dbcea7aaull }, + { 0x400e22d0e5604189ull, 0x3ff538704102ed60ull }, + { 0x400e24dd2f1a9fbfull, 0x3ff53986934c7ce2ull }, + { 0x400e26e978d4fdf4ull, 0x3ff53a9cd2ade821ull }, + { 0x400e28f5c28f5c29ull, 0x3ff53bb2ff29c089ull }, + { 0x400e2b020c49ba5eull, 0x3ff53cc918c29701ull }, + { 0x400e2d0e56041894ull, 0x3ff53ddf1f7afbeaull }, + { 0x400e2f1a9fbe76c9ull, 0x3ff53ef513557f1dull }, + { 0x400e3126e978d4feull, 0x3ff5400af454afefull }, + { 0x400e333333333333ull, 0x3ff54120c27b1d32ull }, + { 0x400e353f7ced9169ull, 0x3ff542367dcb5530ull }, + { 0x400e374bc6a7ef9eull, 0x3ff5434c2647e5adull }, + { 0x400e395810624dd3ull, 0x3ff54461bbf35becull }, + { 0x400e3b645a1cac08ull, 0x3ff545773ed044a8ull }, + { 0x400e3d70a3d70a3eull, 0x3ff5468caee12c19ull }, + { 0x400e3f7ced916873ull, 0x3ff547a20c289df1ull }, + { 0x400e4189374bc6a8ull, 0x3ff548b756a9255eull }, + { 0x400e4395810624ddull, 0x3ff549cc8e654d0bull }, + { 0x400e45a1cac08313ull, 0x3ff54ae1b35f9f20ull }, + { 0x400e47ae147ae148ull, 0x3ff54bf6c59aa53cull }, + { 0x400e49ba5e353f7dull, 0x3ff54d0bc518e87full }, + { 0x400e4bc6a7ef9db2ull, 0x3ff54e20b1dcf184ull }, + { 0x400e4dd2f1a9fbe8ull, 0x3ff54f358be94863ull }, + { 0x400e4fdf3b645a1dull, 0x3ff5504a534074aeull }, + { 0x400e51eb851eb852ull, 0x3ff5515f07e4fd76ull }, + { 0x400e53f7ced91687ull, 0x3ff55273a9d9694aull }, + { 0x400e5604189374bdull, 0x3ff5538839203e34ull }, + { 0x400e5810624dd2f2ull, 0x3ff5549cb5bc01bbull }, + { 0x400e5a1cac083127ull, 0x3ff555b11faf38e5ull }, + { 0x400e5c28f5c28f5cull, 0x3ff556c576fc6834ull }, + { 0x400e5e353f7ced92ull, 0x3ff557d9bba613a9ull }, + { 0x400e604189374bc7ull, 0x3ff558ededaebec1ull }, + { 0x400e624dd2f1a9fcull, 0x3ff55a020d18ec77ull }, + { 0x400e645a1cac0831ull, 0x3ff55b1619e71f46ull }, + { 0x400e666666666667ull, 0x3ff55c2a141bd926ull }, + { 0x400e6872b020c49cull, 0x3ff55d3dfbb99b8cull }, + { 0x400e6a7ef9db22d1ull, 0x3ff55e51d0c2e76cull }, + { 0x400e6c8b43958106ull, 0x3ff55f65933a3d3bull }, + { 0x400e6e978d4fdf3cull, 0x3ff5607943221ceaull }, + { 0x400e70a3d70a3d71ull, 0x3ff5618ce07d05eaull }, + { 0x400e72b020c49ba6ull, 0x3ff562a06b4d7728ull }, + { 0x400e74bc6a7ef9dbull, 0x3ff563b3e395ef16ull }, + { 0x400e76c8b4395811ull, 0x3ff564c74958eba0ull }, + { 0x400e78d4fdf3b646ull, 0x3ff565da9c98ea34ull }, + { 0x400e7ae147ae147bull, 0x3ff566eddd5867bdull }, + { 0x400e7ced916872b0ull, 0x3ff568010b99e0a9ull }, + { 0x400e7ef9db22d0e6ull, 0x3ff56914275fd0e4ull }, + { 0x400e810624dd2f1bull, 0x3ff56a2730acb3d9ull }, + { 0x400e83126e978d50ull, 0x3ff56b3a27830474ull }, + { 0x400e851eb851eb85ull, 0x3ff56c4d0be53d23ull }, + { 0x400e872b020c49bbull, 0x3ff56d5fddd5d7d1ull }, + { 0x400e89374bc6a7f0ull, 0x3ff56e729d574deaull }, + { 0x400e8b4395810625ull, 0x3ff56f854a6c185dull }, + { 0x400e8d4fdf3b645aull, 0x3ff57097e516af98ull }, + { 0x400e8f5c28f5c290ull, 0x3ff571aa6d598b8aull }, + { 0x400e916872b020c5ull, 0x3ff572bce33723a2ull }, + { 0x400e9374bc6a7efaull, 0x3ff573cf46b1eed2ull }, + { 0x400e95810624dd2full, 0x3ff574e197cc638cull }, + { 0x400e978d4fdf3b65ull, 0x3ff575f3d688f7c4ull }, + { 0x400e99999999999aull, 0x3ff5770602ea20efull }, + { 0x400e9ba5e353f7cfull, 0x3ff578181cf25403ull }, + { 0x400e9db22d0e5604ull, 0x3ff5792a24a4057aull }, + { 0x400e9fbe76c8b43aull, 0x3ff57a3c1a01a94eull }, + { 0x400ea1cac083126full, 0x3ff57b4dfd0db2fbull }, + { 0x400ea3d70a3d70a4ull, 0x3ff57c5fcdca9580ull }, + { 0x400ea5e353f7ced9ull, 0x3ff57d718c3ac35dull }, + { 0x400ea7ef9db22d0full, 0x3ff57e833860ae97ull }, + { 0x400ea9fbe76c8b44ull, 0x3ff57f94d23ec8b3ull }, + { 0x400eac083126e979ull, 0x3ff580a659d782baull }, + { 0x400eae147ae147aeull, 0x3ff581b7cf2d4d37ull }, + { 0x400eb020c49ba5e4ull, 0x3ff582c932429839ull }, + { 0x400eb22d0e560419ull, 0x3ff583da8319d351ull }, + { 0x400eb4395810624eull, 0x3ff584ebc1b56d92ull }, + { 0x400eb645a1cac083ull, 0x3ff585fcee17d597ull }, + { 0x400eb851eb851eb8ull, 0x3ff5870e0843797aull }, + { 0x400eba5e353f7ceeull, 0x3ff5881f103ac6daull }, + { 0x400ebc6a7ef9db23ull, 0x3ff5893006002ad9ull }, + { 0x400ebe76c8b43958ull, 0x3ff58a40e996121full }, + { 0x400ec083126e978dull, 0x3ff58b51bafee8d6ull }, + { 0x400ec28f5c28f5c3ull, 0x3ff58c627a3d1aadull }, + { 0x400ec49ba5e353f8ull, 0x3ff58d73275312d6ull }, + { 0x400ec6a7ef9db22dull, 0x3ff58e83c2433c09ull }, + { 0x400ec8b439581062ull, 0x3ff58f944b100082ull }, + { 0x400ecac083126e98ull, 0x3ff590a4c1bbca02ull }, + { 0x400ecccccccccccdull, 0x3ff591b5264901ccull }, + { 0x400eced916872b02ull, 0x3ff592c578ba10acull }, + { 0x400ed0e560418937ull, 0x3ff593d5b9115ef1ull }, + { 0x400ed2f1a9fbe76dull, 0x3ff594e5e751546full }, + { 0x400ed4fdf3b645a2ull, 0x3ff595f6037c587full }, + { 0x400ed70a3d70a3d7ull, 0x3ff597060d94d200ull }, + { 0x400ed916872b020cull, 0x3ff59816059d2756ull }, + { 0x400edb22d0e56042ull, 0x3ff59925eb97be6dull }, + { 0x400edd2f1a9fbe77ull, 0x3ff59a35bf86fcb3ull }, + { 0x400edf3b645a1cacull, 0x3ff59b45816d471dull }, + { 0x400ee147ae147ae1ull, 0x3ff59c55314d0229ull }, + { 0x400ee353f7ced917ull, 0x3ff59d64cf2891d8ull }, + { 0x400ee5604189374cull, 0x3ff59e745b0259b2ull }, + { 0x400ee76c8b439581ull, 0x3ff59f83d4dcbcc5ull }, + { 0x400ee978d4fdf3b6ull, 0x3ff5a0933cba1da8ull }, + { 0x400eeb851eb851ecull, 0x3ff5a1a2929cde75ull }, + { 0x400eed916872b021ull, 0x3ff5a2b1d68760cfull }, + { 0x400eef9db22d0e56ull, 0x3ff5a3c1087c05dfull }, + { 0x400ef1a9fbe76c8bull, 0x3ff5a4d0287d2e57ull }, + { 0x400ef3b645a1cac1ull, 0x3ff5a5df368d3a6dull }, + { 0x400ef5c28f5c28f6ull, 0x3ff5a6ee32ae89e1ull }, + { 0x400ef7ced916872bull, 0x3ff5a7fd1ce37bfaull }, + { 0x400ef9db22d0e560ull, 0x3ff5a90bf52e6f84ull }, + { 0x400efbe76c8b4396ull, 0x3ff5aa1abb91c2d6ull }, + { 0x400efdf3b645a1cbull, 0x3ff5ab29700fd3ccull }, + { 0x400f000000000000ull, 0x3ff5ac3812aaffcdull }, + { 0x400f020c49ba5e35ull, 0x3ff5ad46a365a3c5ull }, + { 0x400f04189374bc6bull, 0x3ff5ae5522421c2aull }, + { 0x400f0624dd2f1aa0ull, 0x3ff5af638f42c4faull }, + { 0x400f083126e978d5ull, 0x3ff5b071ea69f9baull }, + { 0x400f0a3d70a3d70aull, 0x3ff5b18033ba1579ull }, + { 0x400f0c49ba5e3540ull, 0x3ff5b28e6b3572cfull }, + { 0x400f0e5604189375ull, 0x3ff5b39c90de6bdcull }, + { 0x400f10624dd2f1aaull, 0x3ff5b4aaa4b75a47ull }, + { 0x400f126e978d4fdfull, 0x3ff5b5b8a6c29745ull }, + { 0x400f147ae147ae15ull, 0x3ff5b6c697027b8full }, + { 0x400f16872b020c4aull, 0x3ff5b7d475795f6aull }, + { 0x400f189374bc6a7full, 0x3ff5b8e242299aa3ull }, + { 0x400f1a9fbe76c8b4ull, 0x3ff5b9effd158492ull }, + { 0x400f1cac083126eaull, 0x3ff5bafda63f7419ull }, + { 0x400f1eb851eb851full, 0x3ff5bc0b3da9bfa0ull }, + { 0x400f20c49ba5e354ull, 0x3ff5bd18c356bd1dull }, + { 0x400f22d0e5604189ull, 0x3ff5be263748c20full }, + { 0x400f24dd2f1a9fbfull, 0x3ff5bf339982237dull }, + { 0x400f26e978d4fdf4ull, 0x3ff5c040ea0535fcull }, + { 0x400f28f5c28f5c29ull, 0x3ff5c14e28d44da7ull }, + { 0x400f2b020c49ba5eull, 0x3ff5c25b55f1be26ull }, + { 0x400f2d0e56041894ull, 0x3ff5c368715fdaadull }, + { 0x400f2f1a9fbe76c9ull, 0x3ff5c4757b20f5f7ull }, + { 0x400f3126e978d4feull, 0x3ff5c5827337624dull }, + { 0x400f333333333333ull, 0x3ff5c68f59a57181ull }, + { 0x400f353f7ced9169ull, 0x3ff5c79c2e6d74f2ull }, + { 0x400f374bc6a7ef9eull, 0x3ff5c8a8f191bd88ull }, + { 0x400f395810624dd3ull, 0x3ff5c9b5a3149bb7ull }, + { 0x400f3b645a1cac08ull, 0x3ff5cac242f85f80ull }, + { 0x400f3d70a3d70a3eull, 0x3ff5cbced13f586eull }, + { 0x400f3f7ced916873ull, 0x3ff5ccdb4debd597ull }, + { 0x400f4189374bc6a8ull, 0x3ff5cde7b900259full }, + { 0x400f4395810624ddull, 0x3ff5cef4127e96b4ull }, + { 0x400f45a1cac08313ull, 0x3ff5d0005a697692ull }, + { 0x400f47ae147ae148ull, 0x3ff5d10c90c3127eull }, + { 0x400f49ba5e353f7dull, 0x3ff5d218b58db74cull }, + { 0x400f4bc6a7ef9db2ull, 0x3ff5d324c8cbb15cull }, + { 0x400f4dd2f1a9fbe8ull, 0x3ff5d430ca7f4c9bull }, + { 0x400f4fdf3b645a1dull, 0x3ff5d53cbaaad47full }, + { 0x400f51eb851eb852ull, 0x3ff5d64899509410ull }, + { 0x400f53f7ced91687ull, 0x3ff5d7546672d5deull }, + { 0x400f5604189374bdull, 0x3ff5d8602213e40bull }, + { 0x400f5810624dd2f2ull, 0x3ff5d96bcc360841ull }, + { 0x400f5a1cac083127ull, 0x3ff5da7764db8bbbull }, + { 0x400f5c28f5c28f5cull, 0x3ff5db82ec06b73full }, + { 0x400f5e353f7ced92ull, 0x3ff5dc8e61b9d323ull }, + { 0x400f604189374bc7ull, 0x3ff5dd99c5f72747ull }, + { 0x400f624dd2f1a9fcull, 0x3ff5dea518c0fb1bull }, + { 0x400f645a1cac0831ull, 0x3ff5dfb05a19959cull }, + { 0x400f666666666667ull, 0x3ff5e0bb8a033d58ull }, + { 0x400f6872b020c49cull, 0x3ff5e1c6a8803865ull }, + { 0x400f6a7ef9db22d1ull, 0x3ff5e2d1b592cc6cull }, + { 0x400f6c8b43958106ull, 0x3ff5e3dcb13d3ea3ull }, + { 0x400f6e978d4fdf3cull, 0x3ff5e4e79b81d3ceull }, + { 0x400f70a3d70a3d71ull, 0x3ff5e5f27462d03eull }, + { 0x400f72b020c49ba6ull, 0x3ff5e6fd3be277d7ull }, + { 0x400f74bc6a7ef9dbull, 0x3ff5e807f2030e07ull }, + { 0x400f76c8b4395811ull, 0x3ff5e91296c6d5cdull }, + { 0x400f78d4fdf3b646ull, 0x3ff5ea1d2a3011b8ull }, + { 0x400f7ae147ae147bull, 0x3ff5eb27ac4103e3ull }, + { 0x400f7ced916872b0ull, 0x3ff5ec321cfbedfbull }, + { 0x400f7ef9db22d0e6ull, 0x3ff5ed3c7c63113bull }, + { 0x400f810624dd2f1bull, 0x3ff5ee46ca78ae6eull }, + { 0x400f83126e978d50ull, 0x3ff5ef51073f05efull }, + { 0x400f851eb851eb85ull, 0x3ff5f05b32b857a6ull }, + { 0x400f872b020c49bbull, 0x3ff5f1654ce6e30eull }, + { 0x400f89374bc6a7f0ull, 0x3ff5f26f55cce72full }, + { 0x400f8b4395810625ull, 0x3ff5f3794d6ca2a4ull }, + { 0x400f8d4fdf3b645aull, 0x3ff5f48333c85397ull }, + { 0x400f8f5c28f5c290ull, 0x3ff5f58d08e237c0ull }, + { 0x400f916872b020c5ull, 0x3ff5f696ccbc8c6bull }, + { 0x400f9374bc6a7efaull, 0x3ff5f7a07f598e70ull }, + { 0x400f95810624dd2full, 0x3ff5f8aa20bb7a3dull }, + { 0x400f978d4fdf3b65ull, 0x3ff5f9b3b0e48bccull }, + { 0x400f99999999999aull, 0x3ff5fabd2fd6feaaull }, + { 0x400f9ba5e353f7cfull, 0x3ff5fbc69d950df4ull }, + { 0x400f9db22d0e5604ull, 0x3ff5fccffa20f458ull }, + { 0x400f9fbe76c8b43aull, 0x3ff5fdd9457cec18ull }, + { 0x400fa1cac083126full, 0x3ff5fee27fab2f02ull }, + { 0x400fa3d70a3d70a4ull, 0x3ff5ffeba8adf679ull }, + { 0x400fa5e353f7ced9ull, 0x3ff600f4c0877b70ull }, + { 0x400fa7ef9db22d0full, 0x3ff601fdc739f66eull }, + { 0x400fa9fbe76c8b44ull, 0x3ff60306bcc79f88ull }, + { 0x400fac083126e979ull, 0x3ff6040fa132ae67ull }, + { 0x400fae147ae147aeull, 0x3ff60518747d5a46ull }, + { 0x400fb020c49ba5e4ull, 0x3ff6062136a9d9f1ull }, + { 0x400fb22d0e560419ull, 0x3ff60729e7ba63c6ull }, + { 0x400fb4395810624eull, 0x3ff6083287b12db6ull }, + { 0x400fb645a1cac083ull, 0x3ff6093b16906d45ull }, + { 0x400fb851eb851eb9ull, 0x3ff60a43945a5788ull }, + { 0x400fba5e353f7ceeull, 0x3ff60b4c01112127ull }, + { 0x400fbc6a7ef9db23ull, 0x3ff60c545cb6fe5dull }, + { 0x400fbe76c8b43958ull, 0x3ff60d5ca74e22f8ull }, + { 0x400fc083126e978dull, 0x3ff60e64e0d8c258ull }, + { 0x400fc28f5c28f5c3ull, 0x3ff60f6d09590f71ull }, + { 0x400fc49ba5e353f8ull, 0x3ff6107520d13cc8ull }, + { 0x400fc6a7ef9db22dull, 0x3ff6117d27437c79ull }, + { 0x400fc8b439581062ull, 0x3ff612851cb20030ull }, + { 0x400fcac083126e98ull, 0x3ff6138d011ef92eull }, + { 0x400fcccccccccccdull, 0x3ff61494d48c9846ull }, + { 0x400fced916872b02ull, 0x3ff6159c96fd0de1ull }, + { 0x400fd0e560418937ull, 0x3ff616a4487289faull }, + { 0x400fd2f1a9fbe76dull, 0x3ff617abe8ef3c22ull }, + { 0x400fd4fdf3b645a2ull, 0x3ff618b37875537aull }, + { 0x400fd70a3d70a3d7ull, 0x3ff619baf706febaull }, + { 0x400fd916872b020cull, 0x3ff61ac264a66c2full }, + { 0x400fdb22d0e56042ull, 0x3ff61bc9c155c9b9ull }, + { 0x400fdd2f1a9fbe77ull, 0x3ff61cd10d1744cbull }, + { 0x400fdf3b645a1cacull, 0x3ff61dd847ed0a6eull }, + { 0x400fe147ae147ae1ull, 0x3ff61edf71d94741ull }, + { 0x400fe353f7ced917ull, 0x3ff61fe68ade2777ull }, + { 0x400fe5604189374cull, 0x3ff620ed92fdd6d4ull }, + { 0x400fe76c8b439581ull, 0x3ff621f48a3a80b7ull }, + { 0x400fe978d4fdf3b6ull, 0x3ff622fb7096500full }, + { 0x400feb851eb851ecull, 0x3ff6240246136f65ull }, + { 0x400fed916872b021ull, 0x3ff625090ab408d2ull }, + { 0x400fef9db22d0e56ull, 0x3ff6260fbe7a4609ull }, + { 0x400ff1a9fbe76c8bull, 0x3ff6271661685050ull }, + { 0x400ff3b645a1cac1ull, 0x3ff6281cf3805083ull }, + { 0x400ff5c28f5c28f6ull, 0x3ff6292374c46f13ull }, + { 0x400ff7ced916872bull, 0x3ff62a29e536d408ull }, + { 0x400ff9db22d0e560ull, 0x3ff62b3044d9a701ull }, + { 0x400ffbe76c8b4396ull, 0x3ff62c3693af0f30ull }, + { 0x400ffdf3b645a1cbull, 0x3ff62d3cd1b93360ull }, + { 0x4010000000000000ull, 0x3ff62e42fefa39efull }, + { 0x4010010624dd2f1bull, 0x3ff62f491b7448d6ull }, + { 0x4010020c49ba5e35ull, 0x3ff6304f2729859eull }, + { 0x401003126e978d50ull, 0x3ff63155221c156full }, + { 0x401004189374bc6bull, 0x3ff6325b0c4e1cffull }, + { 0x4010051eb851eb85ull, 0x3ff63360e5c1c0a0ull }, + { 0x40100624dd2f1aa0ull, 0x3ff63466ae79243bull }, + { 0x4010072b020c49baull, 0x3ff6356c66766b4cull }, + { 0x4010083126e978d5ull, 0x3ff636720dbbb8ebull }, + { 0x401009374bc6a7f0ull, 0x3ff63777a44b2fc4ull }, + { 0x40100a3d70a3d70aull, 0x3ff6387d2a26f219ull }, + { 0x40100b4395810625ull, 0x3ff639829f5121c9ull }, + { 0x40100c49ba5e3540ull, 0x3ff63a8803cbe045ull }, + { 0x40100d4fdf3b645aull, 0x3ff63b8d57994e97ull }, + { 0x40100e5604189375ull, 0x3ff63c929abb8d64ull }, + { 0x40100f5c28f5c28full, 0x3ff63d97cd34bce2ull }, + { 0x401010624dd2f1aaull, 0x3ff63e9cef06fce7ull }, + { 0x4010116872b020c5ull, 0x3ff63fa200346cdcull }, + { 0x4010126e978d4fdfull, 0x3ff640a700bf2bc0ull }, + { 0x40101374bc6a7efaull, 0x3ff641abf0a95832ull }, + { 0x4010147ae147ae15ull, 0x3ff642b0cff51062ull }, + { 0x401015810624dd2full, 0x3ff643b59ea4721bull }, + { 0x401016872b020c4aull, 0x3ff644ba5cb99ac2ull }, + { 0x4010178d4fdf3b64ull, 0x3ff645bf0a36a752ull }, + { 0x4010189374bc6a7full, 0x3ff646c3a71db462ull }, + { 0x401019999999999aull, 0x3ff647c83370de1eull }, + { 0x40101a9fbe76c8b4ull, 0x3ff648ccaf32404cull }, + { 0x40101ba5e353f7cfull, 0x3ff649d11a63f64eull }, + { 0x40101cac083126eaull, 0x3ff64ad575081b1dull }, + { 0x40101db22d0e5604ull, 0x3ff64bd9bf20c949ull }, + { 0x40101eb851eb851full, 0x3ff64cddf8b01b00ull }, + { 0x40101fbe76c8b439ull, 0x3ff64de221b82a05ull }, + { 0x401020c49ba5e354ull, 0x3ff64ee63a3b0fb9ull }, + { 0x401021cac083126full, 0x3ff64fea423ae513ull }, + { 0x401022d0e5604189ull, 0x3ff650ee39b9c2a4ull }, + { 0x401023d70a3d70a4ull, 0x3ff651f220b9c099ull }, + { 0x401024dd2f1a9fbfull, 0x3ff652f5f73cf6b9ull }, + { 0x401025e353f7ced9ull, 0x3ff653f9bd457c61ull }, + { 0x401026e978d4fdf4ull, 0x3ff654fd72d5688dull }, + { 0x401027ef9db22d0eull, 0x3ff6560117eed1d1ull }, + { 0x401028f5c28f5c29ull, 0x3ff65704ac93ce5cull }, + { 0x401029fbe76c8b44ull, 0x3ff6580830c673f7ull }, + { 0x40102b020c49ba5eull, 0x3ff6590ba488d805ull }, + { 0x40102c083126e979ull, 0x3ff65a0f07dd0f86ull }, + { 0x40102d0e56041894ull, 0x3ff65b125ac52f12ull }, + { 0x40102e147ae147aeull, 0x3ff65c159d434adcull }, + { 0x40102f1a9fbe76c9ull, 0x3ff65d18cf5976b5ull }, + { 0x40103020c49ba5e3ull, 0x3ff65e1bf109c606ull }, + { 0x40103126e978d4feull, 0x3ff65f1f02564bd5ull }, + { 0x4010322d0e560419ull, 0x3ff6602203411ac2ull }, + { 0x4010333333333333ull, 0x3ff66124f3cc4508ull }, + { 0x401034395810624eull, 0x3ff66227d3f9dc7full }, + { 0x4010353f7ced9169ull, 0x3ff6632aa3cbf29bull }, + { 0x40103645a1cac083ull, 0x3ff6642d63449869ull }, + { 0x4010374bc6a7ef9eull, 0x3ff665301265de94ull }, + { 0x40103851eb851eb8ull, 0x3ff66632b131d561ull }, + { 0x4010395810624dd3ull, 0x3ff667353faa8cb4ull }, + { 0x40103a5e353f7ceeull, 0x3ff66837bdd2140bull }, + { 0x40103b645a1cac08ull, 0x3ff6693a2baa7a7eull }, + { 0x40103c6a7ef9db23ull, 0x3ff66a3c8935cec7ull }, + { 0x40103d70a3d70a3eull, 0x3ff66b3ed6761f37ull }, + { 0x40103e76c8b43958ull, 0x3ff66c41136d79bcull }, + { 0x40103f7ced916873ull, 0x3ff66d43401debe5ull }, + { 0x40104083126e978dull, 0x3ff66e455c8982d9ull }, + { 0x40104189374bc6a8ull, 0x3ff66f4768b24b5dull }, + { 0x4010428f5c28f5c3ull, 0x3ff67049649a51d5ull }, + { 0x40104395810624ddull, 0x3ff6714b5043a23dull }, + { 0x4010449ba5e353f8ull, 0x3ff6724d2bb04834ull }, + { 0x401045a1cac08313ull, 0x3ff6734ef6e24ef3ull }, + { 0x401046a7ef9db22dull, 0x3ff67450b1dbc14eull }, + { 0x401047ae147ae148ull, 0x3ff675525c9ea9bcull }, + { 0x401048b439581062ull, 0x3ff67653f72d124cull }, + { 0x401049ba5e353f7dull, 0x3ff67755818904afull }, + { 0x40104ac083126e98ull, 0x3ff67856fbb48a30ull }, + { 0x40104bc6a7ef9db2ull, 0x3ff6795865b1abb8ull }, + { 0x40104ccccccccccdull, 0x3ff67a59bf8271d2ull }, + { 0x40104dd2f1a9fbe8ull, 0x3ff67b5b0928e4a2ull }, + { 0x40104ed916872b02ull, 0x3ff67c5c42a70bebull }, + { 0x40104fdf3b645a1dull, 0x3ff67d5d6bfeef11ull }, + { 0x401050e560418937ull, 0x3ff67e5e85329512ull }, + { 0x401051eb851eb852ull, 0x3ff67f5f8e44048full }, + { 0x401052f1a9fbe76dull, 0x3ff68060873543c4ull }, + { 0x401053f7ced91687ull, 0x3ff681617008588aull }, + { 0x401054fdf3b645a2ull, 0x3ff6826248bf485eull }, + { 0x40105604189374bdull, 0x3ff68363115c1858ull }, + { 0x4010570a3d70a3d7ull, 0x3ff68463c9e0cd2dull }, + { 0x40105810624dd2f2ull, 0x3ff68564724f6b37ull }, + { 0x40105916872b020cull, 0x3ff686650aa9f668ull }, + { 0x40105a1cac083127ull, 0x3ff6876592f27256ull }, + { 0x40105b22d0e56042ull, 0x3ff688660b2ae234ull }, + { 0x40105c28f5c28f5cull, 0x3ff68966735548d4ull }, + { 0x40105d2f1a9fbe77ull, 0x3ff68a66cb73a8a9ull }, + { 0x40105e353f7ced92ull, 0x3ff68b67138803c5ull }, + { 0x40105f3b645a1cacull, 0x3ff68c674b945bd6ull }, + { 0x4010604189374bc7ull, 0x3ff68d67739ab230ull }, + { 0x40106147ae147ae1ull, 0x3ff68e678b9d07c1ull }, + { 0x4010624dd2f1a9fcull, 0x3ff68f67939d5d1aull }, + { 0x40106353f7ced917ull, 0x3ff690678b9db26cull }, + { 0x4010645a1cac0831ull, 0x3ff6916773a00785ull }, + { 0x401065604189374cull, 0x3ff692674ba65bd7ull }, + { 0x4010666666666666ull, 0x3ff6936713b2ae70ull }, + { 0x4010676c8b439581ull, 0x3ff69466cbc6fe03ull }, + { 0x40106872b020c49cull, 0x3ff6956673e548e1ull }, + { 0x40106978d4fdf3b6ull, 0x3ff696660c0f8cf8ull }, + { 0x40106a7ef9db22d1ull, 0x3ff697659447c7dfull }, + { 0x40106b851eb851ecull, 0x3ff698650c8ff6c5ull }, + { 0x40106c8b43958106ull, 0x3ff6996474ea167dull }, + { 0x40106d916872b021ull, 0x3ff69a63cd58237full }, + { 0x40106e978d4fdf3bull, 0x3ff69b6315dc19dcull }, + { 0x40106f9db22d0e56ull, 0x3ff69c624e77f54dull }, + { 0x401070a3d70a3d71ull, 0x3ff69d61772db128ull }, + { 0x401071a9fbe76c8bull, 0x3ff69e608fff4865ull }, + { 0x401072b020c49ba6ull, 0x3ff69f5f98eeb5a0ull }, + { 0x401073b645a1cac1ull, 0x3ff6a05e91fdf313ull }, + { 0x401074bc6a7ef9dbull, 0x3ff6a15d7b2efa99ull }, + { 0x401075c28f5c28f6ull, 0x3ff6a25c5483c5b3ull }, + { 0x401076c8b4395810ull, 0x3ff6a35b1dfe4d7eull }, + { 0x401077ced916872bull, 0x3ff6a459d7a08abfull }, + { 0x401078d4fdf3b646ull, 0x3ff6a558816c75d8ull }, + { 0x401079db22d0e560ull, 0x3ff6a6571b6406ceull }, + { 0x40107ae147ae147bull, 0x3ff6a755a5893549ull }, + { 0x40107be76c8b4396ull, 0x3ff6a8541fddf893ull }, + { 0x40107ced916872b0ull, 0x3ff6a9528a644796ull }, + { 0x40107df3b645a1cbull, 0x3ff6aa50e51e18e3ull }, + { 0x40107ef9db22d0e5ull, 0x3ff6ab4f300d62a9ull }, + { 0x4010800000000000ull, 0x3ff6ac4d6b341abbull }, + { 0x4010810624dd2f1bull, 0x3ff6ad4b96943690ull }, + { 0x4010820c49ba5e35ull, 0x3ff6ae49b22fab3eull }, + { 0x401083126e978d50ull, 0x3ff6af47be086d82ull }, + { 0x401084189374bc6bull, 0x3ff6b045ba2071b9ull }, + { 0x4010851eb851eb85ull, 0x3ff6b143a679abe4ull }, + { 0x40108624dd2f1aa0ull, 0x3ff6b24183160fa8ull }, + { 0x4010872b020c49baull, 0x3ff6b33f4ff7904aull }, + { 0x4010883126e978d5ull, 0x3ff6b43d0d2020b6ull }, + { 0x401089374bc6a7f0ull, 0x3ff6b53aba91b379ull }, + { 0x40108a3d70a3d70aull, 0x3ff6b638584e3ac3ull }, + { 0x40108b4395810625ull, 0x3ff6b735e657a86aull }, + { 0x40108c49ba5e3540ull, 0x3ff6b83364afede5ull }, + { 0x40108d4fdf3b645aull, 0x3ff6b930d358fc50ull }, + { 0x40108e5604189375ull, 0x3ff6ba2e3254c46aull }, + { 0x40108f5c28f5c28full, 0x3ff6bb2b81a53696ull }, + { 0x401090624dd2f1aaull, 0x3ff6bc28c14c42ddull }, + { 0x4010916872b020c5ull, 0x3ff6bd25f14bd8e9ull }, + { 0x4010926e978d4fdfull, 0x3ff6be2311a5e808ull }, + { 0x40109374bc6a7efaull, 0x3ff6bf20225c5f2full }, + { 0x4010947ae147ae15ull, 0x3ff6c01d23712cf6ull }, + { 0x401095810624dd2full, 0x3ff6c11a14e63f98ull }, + { 0x401096872b020c4aull, 0x3ff6c216f6bd84f6ull }, + { 0x4010978d4fdf3b64ull, 0x3ff6c313c8f8ea95ull }, + { 0x4010989374bc6a7full, 0x3ff6c4108b9a5da0ull }, + { 0x401099999999999aull, 0x3ff6c50d3ea3cae4ull }, + { 0x40109a9fbe76c8b4ull, 0x3ff6c609e2171ed5ull }, + { 0x40109ba5e353f7cfull, 0x3ff6c70675f6458bull }, + { 0x40109cac083126eaull, 0x3ff6c802fa432ac4ull }, + { 0x40109db22d0e5604ull, 0x3ff6c8ff6effb9e0ull }, + { 0x40109eb851eb851full, 0x3ff6c9fbd42ddde9ull }, + { 0x40109fbe76c8b439ull, 0x3ff6caf829cf818aull }, + { 0x4010a0c49ba5e354ull, 0x3ff6cbf46fe68f17ull }, + { 0x4010a1cac083126full, 0x3ff6ccf0a674f085ull }, + { 0x4010a2d0e5604189ull, 0x3ff6cdeccd7c8f72ull }, + { 0x4010a3d70a3d70a4ull, 0x3ff6cee8e4ff5522ull }, + { 0x4010a4dd2f1a9fbfull, 0x3ff6cfe4ecff2a7cull }, + { 0x4010a5e353f7ced9ull, 0x3ff6d0e0e57df80cull }, + { 0x4010a6e978d4fdf4ull, 0x3ff6d1dcce7da60aull }, + { 0x4010a7ef9db22d0eull, 0x3ff6d2d8a8001c4cull }, + { 0x4010a8f5c28f5c29ull, 0x3ff6d3d472074255ull }, + { 0x4010a9fbe76c8b44ull, 0x3ff6d4d02c94ff49ull }, + { 0x4010ab020c49ba5eull, 0x3ff6d5cbd7ab39f4ull }, + { 0x4010ac083126e979ull, 0x3ff6d6c7734bd8caull }, + { 0x4010ad0e56041894ull, 0x3ff6d7c2ff78c1e3ull }, + { 0x4010ae147ae147aeull, 0x3ff6d8be7c33dafeull }, + { 0x4010af1a9fbe76c9ull, 0x3ff6d9b9e97f0981ull }, + { 0x4010b020c49ba5e3ull, 0x3ff6dab5475c3279ull }, + { 0x4010b126e978d4feull, 0x3ff6dbb095cd3a99ull }, + { 0x4010b22d0e560419ull, 0x3ff6dcabd4d4063cull }, + { 0x4010b33333333333ull, 0x3ff6dda704727960ull }, + { 0x4010b4395810624eull, 0x3ff6dea224aa77b0ull }, + { 0x4010b53f7ced9169ull, 0x3ff6df9d357de47aull }, + { 0x4010b645a1cac083ull, 0x3ff6e09836eea2b3ull }, + { 0x4010b74bc6a7ef9eull, 0x3ff6e19328fe94faull }, + { 0x4010b851eb851eb8ull, 0x3ff6e28e0baf9d92ull }, + { 0x4010b95810624dd3ull, 0x3ff6e388df039e68ull }, + { 0x4010ba5e353f7ceeull, 0x3ff6e483a2fc7911ull }, + { 0x4010bb645a1cac08ull, 0x3ff6e57e579c0ec4ull }, + { 0x4010bc6a7ef9db23ull, 0x3ff6e678fce44068ull }, + { 0x4010bd70a3d70a3eull, 0x3ff6e77392d6ee87ull }, + { 0x4010be76c8b43958ull, 0x3ff6e86e1975f950ull }, + { 0x4010bf7ced916873ull, 0x3ff6e96890c340a1ull }, + { 0x4010c083126e978dull, 0x3ff6ea62f8c0a3f9ull }, + { 0x4010c189374bc6a8ull, 0x3ff6eb5d51700286ull }, + { 0x4010c28f5c28f5c3ull, 0x3ff6ec579ad33b18ull }, + { 0x4010c395810624ddull, 0x3ff6ed51d4ec2c29ull }, + { 0x4010c49ba5e353f8ull, 0x3ff6ee4bffbcb3dfull }, + { 0x4010c5a1cac08313ull, 0x3ff6ef461b46b003ull }, + { 0x4010c6a7ef9db22dull, 0x3ff6f040278bfe09ull }, + { 0x4010c7ae147ae148ull, 0x3ff6f13a248e7b10ull }, + { 0x4010c8b439581062ull, 0x3ff6f234125003daull }, + { 0x4010c9ba5e353f7dull, 0x3ff6f32df0d274d8ull }, + { 0x4010cac083126e98ull, 0x3ff6f427c017aa1eull }, + { 0x4010cbc6a7ef9db2ull, 0x3ff6f52180217f6dull }, + { 0x4010cccccccccccdull, 0x3ff6f61b30f1d02full }, + { 0x4010cdd2f1a9fbe8ull, 0x3ff6f714d28a7774ull }, + { 0x4010ced916872b02ull, 0x3ff6f80e64ed4ff6ull }, + { 0x4010cfdf3b645a1dull, 0x3ff6f907e81c341cull }, + { 0x4010d0e560418937ull, 0x3ff6fa015c18fdf1ull }, + { 0x4010d1eb851eb852ull, 0x3ff6fafac0e5872dull }, + { 0x4010d2f1a9fbe76dull, 0x3ff6fbf41683a932ull }, + { 0x4010d3f7ced91687ull, 0x3ff6fced5cf53d06ull }, + { 0x4010d4fdf3b645a2ull, 0x3ff6fde6943c1b61ull }, + { 0x4010d604189374bdull, 0x3ff6fedfbc5a1c9eull }, + { 0x4010d70a3d70a3d7ull, 0x3ff6ffd8d55118c3ull }, + { 0x4010d810624dd2f2ull, 0x3ff700d1df22e784ull }, + { 0x4010d916872b020cull, 0x3ff701cad9d1603aull }, + { 0x4010da1cac083127ull, 0x3ff702c3c55e59ecull }, + { 0x4010db22d0e56042ull, 0x3ff703bca1cbab48ull }, + { 0x4010dc28f5c28f5cull, 0x3ff704b56f1b2aa6ull }, + { 0x4010dd2f1a9fbe77ull, 0x3ff705ae2d4eae0dull }, + { 0x4010de353f7ced92ull, 0x3ff706a6dc680b2aull }, + { 0x4010df3b645a1cacull, 0x3ff7079f7c691754ull }, + { 0x4010e04189374bc7ull, 0x3ff708980d53a791ull }, + { 0x4010e147ae147ae1ull, 0x3ff709908f29908dull }, + { 0x4010e24dd2f1a9fcull, 0x3ff70a8901eca6a1ull }, + { 0x4010e353f7ced917ull, 0x3ff70b81659ebdd1ull }, + { 0x4010e45a1cac0831ull, 0x3ff70c79ba41a9caull }, + { 0x4010e5604189374cull, 0x3ff70d71ffd73de6ull }, + { 0x4010e66666666666ull, 0x3ff70e6a36614d29ull }, + { 0x4010e76c8b439581ull, 0x3ff70f625de1aa44ull }, + { 0x4010e872b020c49cull, 0x3ff7105a765a2790ull }, + { 0x4010e978d4fdf3b6ull, 0x3ff711527fcc9711ull }, + { 0x4010ea7ef9db22d1ull, 0x3ff7124a7a3aca7cull }, + { 0x4010eb851eb851ecull, 0x3ff7134265a6932cull }, + { 0x4010ec8b43958106ull, 0x3ff7143a4211c228ull }, + { 0x4010ed916872b021ull, 0x3ff715320f7e2826ull }, + { 0x4010ee978d4fdf3bull, 0x3ff71629cded9583ull }, + { 0x4010ef9db22d0e56ull, 0x3ff717217d61da4dull }, + { 0x4010f0a3d70a3d71ull, 0x3ff718191ddcc639ull }, + { 0x4010f1a9fbe76c8bull, 0x3ff71910af6028aaull }, + { 0x4010f2b020c49ba6ull, 0x3ff71a0831edd0b0ull }, + { 0x4010f3b645a1cac1ull, 0x3ff71affa5878d05ull }, + { 0x4010f4bc6a7ef9dbull, 0x3ff71bf70a2f2c10ull }, + { 0x4010f5c28f5c28f6ull, 0x3ff71cee5fe67be6ull }, + { 0x4010f6c8b4395810ull, 0x3ff71de5a6af4a45ull }, + { 0x4010f7ced916872bull, 0x3ff71edcde8b649bull }, + { 0x4010f8d4fdf3b646ull, 0x3ff71fd4077c9800ull }, + { 0x4010f9db22d0e560ull, 0x3ff720cb2184b137ull }, + { 0x4010fae147ae147bull, 0x3ff721c22ca57cb5ull }, + { 0x4010fbe76c8b4396ull, 0x3ff722b928e0c696ull }, + { 0x4010fced916872b0ull, 0x3ff723b016385aa6ull }, + { 0x4010fdf3b645a1cbull, 0x3ff724a6f4ae045eull }, + { 0x4010fef9db22d0e5ull, 0x3ff7259dc4438ee1ull }, + { 0x4011000000000000ull, 0x3ff7269484fac504ull }, + { 0x4011010624dd2f1bull, 0x3ff7278b36d57145ull }, + { 0x4011020c49ba5e35ull, 0x3ff72881d9d55dcfull }, + { 0x401103126e978d50ull, 0x3ff729786dfc547dull }, + { 0x401104189374bc6bull, 0x3ff72a6ef34c1ed6ull }, + { 0x4011051eb851eb85ull, 0x3ff72b6569c6860dull }, + { 0x40110624dd2f1aa0ull, 0x3ff72c5bd16d5305ull }, + { 0x4011072b020c49baull, 0x3ff72d522a424e4dull }, + { 0x4011083126e978d5ull, 0x3ff72e4874474024ull }, + { 0x401109374bc6a7f0ull, 0x3ff72f3eaf7df072ull }, + { 0x40110a3d70a3d70aull, 0x3ff73034dbe826d0ull }, + { 0x40110b4395810625ull, 0x3ff7312af987aa86ull }, + { 0x40110c49ba5e3540ull, 0x3ff73221085e4287ull }, + { 0x40110d4fdf3b645aull, 0x3ff73317086db576ull }, + { 0x40110e5604189375ull, 0x3ff7340cf9b7c9a3ull }, + { 0x40110f5c28f5c28full, 0x3ff73502dc3e450dull }, + { 0x401110624dd2f1aaull, 0x3ff735f8b002ed62ull }, + { 0x4011116872b020c5ull, 0x3ff736ee750787fdull }, + { 0x4011126e978d4fdfull, 0x3ff737e42b4dd9e5ull }, + { 0x40111374bc6a7efaull, 0x3ff738d9d2d7a7d6ull }, + { 0x4011147ae147ae15ull, 0x3ff739cf6ba6b635ull }, + { 0x401115810624dd2full, 0x3ff73ac4f5bcc917ull }, + { 0x401116872b020c4aull, 0x3ff73bba711ba441ull }, + { 0x4011178d4fdf3b64ull, 0x3ff73cafddc50b25ull }, + { 0x4011189374bc6a7full, 0x3ff73da53bbac0e6ull }, + { 0x401119999999999aull, 0x3ff73e9a8afe8854ull }, + { 0x40111a9fbe76c8b4ull, 0x3ff73f8fcb9223eeull }, + { 0x40111ba5e353f7cfull, 0x3ff74084fd7755e5ull }, + { 0x40111cac083126eaull, 0x3ff7417a20afe016ull }, + { 0x40111db22d0e5604ull, 0x3ff7426f353d840cull }, + { 0x40111eb851eb851full, 0x3ff743643b220307ull }, + { 0x40111fbe76c8b439ull, 0x3ff74459325f1df1ull }, + { 0x401120c49ba5e354ull, 0x3ff7454e1af69567ull }, + { 0x401121cac083126full, 0x3ff74642f4ea29b2ull }, + { 0x401122d0e5604189ull, 0x3ff74737c03b9aceull }, + { 0x401123d70a3d70a4ull, 0x3ff7482c7ceca866ull }, + { 0x401124dd2f1a9fbfull, 0x3ff749212aff11d2ull }, + { 0x401125e353f7ced9ull, 0x3ff74a15ca74961dull }, + { 0x401126e978d4fdf4ull, 0x3ff74b0a5b4ef402ull }, + { 0x401127ef9db22d0eull, 0x3ff74bfedd8fe9e9ull }, + { 0x401128f5c28f5c29ull, 0x3ff74cf3513935eeull }, + { 0x401129fbe76c8b44ull, 0x3ff74de7b64c95daull }, + { 0x40112b020c49ba5eull, 0x3ff74edc0ccbc726ull }, + { 0x40112c083126e979ull, 0x3ff74fd054b886ffull }, + { 0x40112d0e56041894ull, 0x3ff750c48e14923full }, + { 0x40112e147ae147aeull, 0x3ff751b8b8e1a570ull }, + { 0x40112f1a9fbe76c9ull, 0x3ff752acd5217cd1ull }, + { 0x40113020c49ba5e3ull, 0x3ff753a0e2d5d44cull }, + { 0x40113126e978d4feull, 0x3ff75494e200677full }, + { 0x4011322d0e560419ull, 0x3ff75588d2a2f1b9ull }, + { 0x4011333333333333ull, 0x3ff7567cb4bf2df7ull }, + { 0x401134395810624eull, 0x3ff757708856d6eaull }, + { 0x4011353f7ced9169ull, 0x3ff758644d6ba6f3ull }, + { 0x40113645a1cac083ull, 0x3ff7595803ff5821ull }, + { 0x4011374bc6a7ef9eull, 0x3ff75a4bac13a43aull }, + { 0x40113851eb851eb8ull, 0x3ff75b3f45aa44afull }, + { 0x4011395810624dd3ull, 0x3ff75c32d0c4f2a9ull }, + { 0x40113a5e353f7ceeull, 0x3ff75d264d6566fbull }, + { 0x40113b645a1cac08ull, 0x3ff75e19bb8d5a2eull }, + { 0x40113c6a7ef9db23ull, 0x3ff75f0d1b3e847cull }, + { 0x40113d70a3d70a3eull, 0x3ff760006c7a9dd0ull }, + { 0x40113e76c8b43958ull, 0x3ff760f3af435dc5ull }, + { 0x40113f7ced916873ull, 0x3ff761e6e39a7bacull }, + { 0x40114083126e978dull, 0x3ff762da0981ae82ull }, + { 0x40114189374bc6a8ull, 0x3ff763cd20faacfcull }, + { 0x4011428f5c28f5c3ull, 0x3ff764c02a072d7cull }, + { 0x40114395810624ddull, 0x3ff765b324a8e616ull }, + { 0x4011449ba5e353f8ull, 0x3ff766a610e18c96ull }, + { 0x401145a1cac08313ull, 0x3ff76798eeb2d673ull }, + { 0x401146a7ef9db22dull, 0x3ff7688bbe1e78d9ull }, + { 0x401147ae147ae148ull, 0x3ff7697e7f2628a8ull }, + { 0x401148b439581062ull, 0x3ff76a7131cb9a70ull }, + { 0x401149ba5e353f7dull, 0x3ff76b63d6108275ull }, + { 0x40114ac083126e98ull, 0x3ff76c566bf694abull }, + { 0x40114bc6a7ef9db2ull, 0x3ff76d48f37f84bbull }, + { 0x40114ccccccccccdull, 0x3ff76e3b6cad0600ull }, + { 0x40114dd2f1a9fbe8ull, 0x3ff76f2dd780cb89ull }, + { 0x40114ed916872b02ull, 0x3ff7702033fc8813ull }, + { 0x40114fdf3b645a1dull, 0x3ff771128221ee14ull }, + { 0x401150e560418937ull, 0x3ff77204c1f2afb0ull }, + { 0x401151eb851eb852ull, 0x3ff772f6f3707ec3ull }, + { 0x401152f1a9fbe76dull, 0x3ff773e9169d0cd7ull }, + { 0x401153f7ced91687ull, 0x3ff774db2b7a0b2aull }, + { 0x401154fdf3b645a2ull, 0x3ff775cd32092ab2ull }, + { 0x40115604189374bdull, 0x3ff776bf2a4c1c13ull }, + { 0x4011570a3d70a3d7ull, 0x3ff777b114448fa4ull }, + { 0x40115810624dd2f2ull, 0x3ff778a2eff43575ull }, + { 0x40115916872b020cull, 0x3ff77994bd5cbd43ull }, + { 0x40115a1cac083127ull, 0x3ff77a867c7fd685ull }, + { 0x40115b22d0e56042ull, 0x3ff77b782d5f305full }, + { 0x40115c28f5c28f5cull, 0x3ff77c69cffc79adull }, + { 0x40115d2f1a9fbe77ull, 0x3ff77d5b645960feull }, + { 0x40115e353f7ced92ull, 0x3ff77e4cea779494ull }, + { 0x40115f3b645a1cacull, 0x3ff77f3e6258c264ull }, + { 0x4011604189374bc7ull, 0x3ff7802fcbfe981bull }, + { 0x40116147ae147ae1ull, 0x3ff78121276ac316ull }, + { 0x4011624dd2f1a9fcull, 0x3ff78212749ef067ull }, + { 0x40116353f7ced917ull, 0x3ff78303b39cccd7ull }, + { 0x4011645a1cac0831ull, 0x3ff783f4e46604ddull }, + { 0x401165604189374cull, 0x3ff784e606fc44acull }, + { 0x4011666666666667ull, 0x3ff785d71b613826ull }, + { 0x4011676c8b439581ull, 0x3ff786c821968ae2ull }, + { 0x40116872b020c49cull, 0x3ff787b9199de830ull }, + { 0x40116978d4fdf3b6ull, 0x3ff788aa0378fb0dull }, + { 0x40116a7ef9db22d1ull, 0x3ff7899adf296e33ull }, + { 0x40116b851eb851ecull, 0x3ff78a8bacb0ec0aull }, + { 0x40116c8b43958106ull, 0x3ff78b7c6c111eb2ull }, + { 0x40116d916872b021ull, 0x3ff78c6d1d4bb000ull }, + { 0x40116e978d4fdf3bull, 0x3ff78d5dc062497dull }, + { 0x40116f9db22d0e56ull, 0x3ff78e4e55569467ull }, + { 0x401170a3d70a3d71ull, 0x3ff78f3edc2a39b3ull }, + { 0x401171a9fbe76c8bull, 0x3ff7902f54dee206ull }, + { 0x401172b020c49ba6ull, 0x3ff7911fbf7635c2ull }, + { 0x401173b645a1cac1ull, 0x3ff792101bf1dcf8ull }, + { 0x401174bc6a7ef9dbull, 0x3ff793006a537f6full }, + { 0x401175c28f5c28f6ull, 0x3ff793f0aa9cc4a8ull }, + { 0x401176c8b4395810ull, 0x3ff794e0dccf53d3ull }, + { 0x401177ced916872bull, 0x3ff795d100ecd3ddull }, + { 0x401178d4fdf3b646ull, 0x3ff796c116f6eb62ull }, + { 0x401179db22d0e560ull, 0x3ff797b11eef40b5ull }, + { 0x40117ae147ae147bull, 0x3ff798a118d779e3ull }, + { 0x40117be76c8b4396ull, 0x3ff7999104b13cabull }, + { 0x40117ced916872b0ull, 0x3ff79a80e27e2e81ull }, + { 0x40117df3b645a1cbull, 0x3ff79b70b23ff492ull }, + { 0x40117ef9db22d0e5ull, 0x3ff79c6073f833c0ull }, + { 0x4011800000000000ull, 0x3ff79d5027a890a4ull }, + { 0x4011810624dd2f1bull, 0x3ff79e3fcd52af8aull }, + { 0x4011820c49ba5e35ull, 0x3ff79f2f64f83477ull }, + { 0x401183126e978d50ull, 0x3ff7a01eee9ac327ull }, + { 0x401184189374bc6bull, 0x3ff7a10e6a3bff09ull }, + { 0x4011851eb851eb85ull, 0x3ff7a1fdd7dd8b45ull }, + { 0x40118624dd2f1aa0ull, 0x3ff7a2ed37810abaull }, + { 0x4011872b020c49baull, 0x3ff7a3dc89281ffbull }, + { 0x4011883126e978d5ull, 0x3ff7a4cbccd46d55ull }, + { 0x401189374bc6a7f0ull, 0x3ff7a5bb028794c9ull }, + { 0x40118a3d70a3d70aull, 0x3ff7a6aa2a43380eull }, + { 0x40118b4395810625ull, 0x3ff7a7994408f897ull }, + { 0x40118c49ba5e3540ull, 0x3ff7a8884fda7789ull }, + { 0x40118d4fdf3b645aull, 0x3ff7a9774db955bfull }, + { 0x40118e5604189375ull, 0x3ff7aa663da733d1ull }, + { 0x40118f5c28f5c28full, 0x3ff7ab551fa5b209ull }, + { 0x401190624dd2f1aaull, 0x3ff7ac43f3b6706aull }, + { 0x4011916872b020c5ull, 0x3ff7ad32b9db0eafull }, + { 0x4011926e978d4fdfull, 0x3ff7ae2172152c47ull }, + { 0x40119374bc6a7efaull, 0x3ff7af101c66685dull }, + { 0x4011947ae147ae15ull, 0x3ff7affeb8d061d0ull }, + { 0x401195810624dd2full, 0x3ff7b0ed4754b737ull }, + { 0x401196872b020c4aull, 0x3ff7b1dbc7f506e3ull }, + { 0x4011978d4fdf3b64ull, 0x3ff7b2ca3ab2eedaull }, + { 0x4011989374bc6a7full, 0x3ff7b3b89f900cdbull }, + { 0x401199999999999aull, 0x3ff7b4a6f68dfe5cull }, + { 0x40119a9fbe76c8b4ull, 0x3ff7b5953fae608bull }, + { 0x40119ba5e353f7cfull, 0x3ff7b6837af2d04eull }, + { 0x40119cac083126eaull, 0x3ff7b771a85cea45ull }, + { 0x40119db22d0e5604ull, 0x3ff7b85fc7ee4ac3ull }, + { 0x40119eb851eb851full, 0x3ff7b94dd9a88dd9ull }, + { 0x40119fbe76c8b439ull, 0x3ff7ba3bdd8d4f4bull }, + { 0x4011a0c49ba5e354ull, 0x3ff7bb29d39e2a9aull }, + { 0x4011a1cac083126full, 0x3ff7bc17bbdcbafcull }, + { 0x4011a2d0e5604189ull, 0x3ff7bd05964a9b60ull }, + { 0x4011a3d70a3d70a4ull, 0x3ff7bdf362e9666eull }, + { 0x4011a4dd2f1a9fbfull, 0x3ff7bee121bab686ull }, + { 0x4011a5e353f7ced9ull, 0x3ff7bfced2c025c0ull }, + { 0x4011a6e978d4fdf4ull, 0x3ff7c0bc75fb4df0ull }, + { 0x4011a7ef9db22d0eull, 0x3ff7c1aa0b6dc89dull }, + { 0x4011a8f5c28f5c29ull, 0x3ff7c29793192f0cull }, + { 0x4011a9fbe76c8b44ull, 0x3ff7c3850cff1a38ull }, + { 0x4011ab020c49ba5eull, 0x3ff7c472792122d4ull }, + { 0x4011ac083126e979ull, 0x3ff7c55fd780e150ull }, + { 0x4011ad0e56041894ull, 0x3ff7c64d281fedd2ull }, + { 0x4011ae147ae147aeull, 0x3ff7c73a6affe037ull }, + { 0x4011af1a9fbe76c9ull, 0x3ff7c827a022501bull }, + { 0x4011b020c49ba5e3ull, 0x3ff7c914c788d4ceull }, + { 0x4011b126e978d4feull, 0x3ff7ca01e135055cull }, + { 0x4011b22d0e560419ull, 0x3ff7caeeed287889ull }, + { 0x4011b33333333333ull, 0x3ff7cbdbeb64c4d3ull }, + { 0x4011b4395810624eull, 0x3ff7ccc8dbeb8072ull }, + { 0x4011b53f7ced9169ull, 0x3ff7cdb5bebe4156ull }, + { 0x4011b645a1cac083ull, 0x3ff7cea293de9d29ull }, + { 0x4011b74bc6a7ef9eull, 0x3ff7cf8f5b4e2951ull }, + { 0x4011b851eb851eb8ull, 0x3ff7d07c150e7aeaull }, + { 0x4011b95810624dd3ull, 0x3ff7d168c12126ccull }, + { 0x4011ba5e353f7ceeull, 0x3ff7d2555f87c18aull }, + { 0x4011bb645a1cac08ull, 0x3ff7d341f043df6cull }, + { 0x4011bc6a7ef9db23ull, 0x3ff7d42e7357147bull }, + { 0x4011bd70a3d70a3eull, 0x3ff7d51ae8c2f474ull }, + { 0x4011be76c8b43958ull, 0x3ff7d607508912d1ull }, + { 0x4011bf7ced916873ull, 0x3ff7d6f3aaab02c5ull }, + { 0x4011c083126e978dull, 0x3ff7d7dff72a573full }, + { 0x4011c189374bc6a8ull, 0x3ff7d8cc3608a2e7ull }, + { 0x4011c28f5c28f5c3ull, 0x3ff7d9b86747781full }, + { 0x4011c395810624ddull, 0x3ff7daa48ae86902ull }, + { 0x4011c49ba5e353f8ull, 0x3ff7db90a0ed076bull }, + { 0x4011c5a1cac08313ull, 0x3ff7dc7ca956e4e9ull }, + { 0x4011c6a7ef9db22dull, 0x3ff7dd68a42792c8ull }, + { 0x4011c7ae147ae148ull, 0x3ff7de549160a212ull }, + { 0x4011c8b439581062ull, 0x3ff7df407103a386ull }, + { 0x4011c9ba5e353f7dull, 0x3ff7e02c431227a3ull }, + { 0x4011cac083126e98ull, 0x3ff7e118078dbe9full }, + { 0x4011cbc6a7ef9db2ull, 0x3ff7e203be77f86bull }, + { 0x4011cccccccccccdull, 0x3ff7e2ef67d264b6ull }, + { 0x4011cdd2f1a9fbe8ull, 0x3ff7e3db039e92e9ull }, + { 0x4011ced916872b02ull, 0x3ff7e4c691de1225ull }, + { 0x4011cfdf3b645a1dull, 0x3ff7e5b21292714cull }, + { 0x4011d0e560418937ull, 0x3ff7e69d85bd3ef4ull }, + { 0x4011d1eb851eb852ull, 0x3ff7e788eb600974ull }, + { 0x4011d2f1a9fbe76dull, 0x3ff7e874437c5eddull }, + { 0x4011d3f7ced91687ull, 0x3ff7e95f8e13ccf9ull }, + { 0x4011d4fdf3b645a2ull, 0x3ff7ea4acb27e151ull }, + { 0x4011d604189374bdull, 0x3ff7eb35faba2927ull }, + { 0x4011d70a3d70a3d7ull, 0x3ff7ec211ccc3179ull }, + { 0x4011d810624dd2f2ull, 0x3ff7ed0c315f8702ull }, + { 0x4011d916872b020cull, 0x3ff7edf73875b636ull }, + { 0x4011da1cac083127ull, 0x3ff7eee232104b49ull }, + { 0x4011db22d0e56042ull, 0x3ff7efcd1e30d227ull }, + { 0x4011dc28f5c28f5cull, 0x3ff7f0b7fcd8d678ull }, + { 0x4011dd2f1a9fbe77ull, 0x3ff7f1a2ce09e3a3ull }, + { 0x4011de353f7ced92ull, 0x3ff7f28d91c584c9ull }, + { 0x4011df3b645a1cacull, 0x3ff7f378480d44c5ull }, + { 0x4011e04189374bc7ull, 0x3ff7f462f0e2ae34ull }, + { 0x4011e147ae147ae1ull, 0x3ff7f54d8c474b68ull }, + { 0x4011e24dd2f1a9fcull, 0x3ff7f6381a3ca676ull }, + { 0x4011e353f7ced917ull, 0x3ff7f7229ac4492aull }, + { 0x4011e45a1cac0831ull, 0x3ff7f80d0ddfbd0full }, + { 0x4011e5604189374cull, 0x3ff7f8f773908b6dull }, + { 0x4011e66666666667ull, 0x3ff7f9e1cbd83d47ull }, + { 0x4011e76c8b439581ull, 0x3ff7facc16b85b5bull }, + { 0x4011e872b020c49cull, 0x3ff7fbb654326e29ull }, + { 0x4011e978d4fdf3b6ull, 0x3ff7fca08447fde7ull }, + { 0x4011ea7ef9db22d1ull, 0x3ff7fd8aa6fa928eull }, + { 0x4011eb851eb851ecull, 0x3ff7fe74bc4bb3cfull }, + { 0x4011ec8b43958106ull, 0x3ff7ff5ec43ce91aull }, + { 0x4011ed916872b021ull, 0x3ff80048becfb99dull }, + { 0x4011ee978d4fdf3bull, 0x3ff80132ac05ac3full }, + { 0x4011ef9db22d0e56ull, 0x3ff8021c8be047aaull }, + { 0x4011f0a3d70a3d71ull, 0x3ff803065e61123full }, + { 0x4011f1a9fbe76c8bull, 0x3ff803f023899220ull }, + { 0x4011f2b020c49ba6ull, 0x3ff804d9db5b4d2bull }, + { 0x4011f3b645a1cac1ull, 0x3ff805c385d7c8fcull }, + { 0x4011f4bc6a7ef9dbull, 0x3ff806ad23008aeaull }, + { 0x4011f5c28f5c28f6ull, 0x3ff80796b2d7180dull }, + { 0x4011f6c8b4395810ull, 0x3ff80880355cf537ull }, + { 0x4011f7ced916872bull, 0x3ff80969aa93a6fbull }, + { 0x4011f8d4fdf3b646ull, 0x3ff80a53127cb1a7ull }, + { 0x4011f9db22d0e560ull, 0x3ff80b3c6d199946ull }, + { 0x4011fae147ae147bull, 0x3ff80c25ba6be1a4ull }, + { 0x4011fbe76c8b4396ull, 0x3ff80d0efa750e47ull }, + { 0x4011fced916872b0ull, 0x3ff80df82d36a275ull }, + { 0x4011fdf3b645a1cbull, 0x3ff80ee152b22133ull }, + { 0x4011fef9db22d0e5ull, 0x3ff80fca6ae90d40ull }, + { 0x4012000000000000ull, 0x3ff810b375dce91eull }, + { 0x4012010624dd2f1bull, 0x3ff8119c738f3708ull }, + { 0x4012020c49ba5e35ull, 0x3ff81285640178faull }, + { 0x401203126e978d50ull, 0x3ff8136e473530afull }, + { 0x401204189374bc6bull, 0x3ff814571d2bdf9dull }, + { 0x4012051eb851eb85ull, 0x3ff8153fe5e706fbull }, + { 0x40120624dd2f1aa0ull, 0x3ff81628a16827bfull }, + { 0x4012072b020c49baull, 0x3ff817114fb0c299ull }, + { 0x4012083126e978d5ull, 0x3ff817f9f0c257fdull }, + { 0x401209374bc6a7f0ull, 0x3ff818e2849e681aull }, + { 0x40120a3d70a3d70aull, 0x3ff819cb0b4672ddull }, + { 0x40120b4395810625ull, 0x3ff81ab384bbf7f5ull }, + { 0x40120c49ba5e3540ull, 0x3ff81b9bf10076cdull }, + { 0x40120d4fdf3b645aull, 0x3ff81c8450156e8eull }, + { 0x40120e5604189375ull, 0x3ff81d6ca1fc5e25ull }, + { 0x40120f5c28f5c28full, 0x3ff81e54e6b6c436ull }, + { 0x401210624dd2f1aaull, 0x3ff81f3d1e461f2bull }, + { 0x4012116872b020c5ull, 0x3ff8202548abed2aull }, + { 0x4012126e978d4fdfull, 0x3ff8210d65e9ac15ull }, + { 0x40121374bc6a7efaull, 0x3ff821f57600d994ull }, + { 0x4012147ae147ae15ull, 0x3ff822dd78f2f309ull }, + { 0x401215810624dd2full, 0x3ff823c56ec17594ull }, + { 0x401216872b020c4aull, 0x3ff824ad576dde1bull }, + { 0x4012178d4fdf3b64ull, 0x3ff8259532f9a93bull }, + { 0x4012189374bc6a7full, 0x3ff8267d01665358ull }, + { 0x401219999999999aull, 0x3ff82764c2b55891ull }, + { 0x40121a9fbe76c8b4ull, 0x3ff8284c76e834c3ull }, + { 0x40121ba5e353f7cfull, 0x3ff829341e006390ull }, + { 0x40121cac083126eaull, 0x3ff82a1bb7ff6055ull }, + { 0x40121db22d0e5604ull, 0x3ff82b0344e6a631ull }, + { 0x40121eb851eb851full, 0x3ff82beac4b7b001ull }, + { 0x40121fbe76c8b439ull, 0x3ff82cd23773f863ull }, + { 0x401220c49ba5e354ull, 0x3ff82db99d1cf9b5ull }, + { 0x401221cac083126full, 0x3ff82ea0f5b42e15ull }, + { 0x401222d0e5604189ull, 0x3ff82f88413b0f5eull }, + { 0x401223d70a3d70a4ull, 0x3ff8306f7fb31730ull }, + { 0x401224dd2f1a9fbfull, 0x3ff83156b11dbee8ull }, + { 0x401225e353f7ced9ull, 0x3ff8323dd57c7fa2ull }, + { 0x401226e978d4fdf4ull, 0x3ff83324ecd0d23dull }, + { 0x401227ef9db22d0eull, 0x3ff8340bf71c2f56ull }, + { 0x401228f5c28f5c29ull, 0x3ff834f2f4600f4cull }, + { 0x401229fbe76c8b44ull, 0x3ff835d9e49dea3full }, + { 0x40122b020c49ba5eull, 0x3ff836c0c7d7380aull }, + { 0x40122c083126e979ull, 0x3ff837a79e0d7051ull }, + { 0x40122d0e56041894ull, 0x3ff8388e67420a71ull }, + { 0x40122e147ae147aeull, 0x3ff8397523767d8aull }, + { 0x40122f1a9fbe76c9ull, 0x3ff83a5bd2ac4080ull }, + { 0x40123020c49ba5e3ull, 0x3ff83b4274e4c9f2ull }, + { 0x40123126e978d4feull, 0x3ff83c290a219045ull }, + { 0x4012322d0e560419ull, 0x3ff83d0f9264099bull }, + { 0x4012333333333333ull, 0x3ff83df60dadabd7ull }, + { 0x401234395810624eull, 0x3ff83edc7bffec9full }, + { 0x4012353f7ced9169ull, 0x3ff83fc2dd5c415bull }, + { 0x40123645a1cac083ull, 0x3ff840a931c41f2eull }, + { 0x4012374bc6a7ef9eull, 0x3ff8418f7938fb03ull }, + { 0x40123851eb851eb8ull, 0x3ff84275b3bc4983ull }, + { 0x4012395810624dd3ull, 0x3ff8435be14f7f17ull }, + { 0x40123a5e353f7ceeull, 0x3ff8444201f40fecull }, + { 0x40123b645a1cac08ull, 0x3ff8452815ab6feeull }, + { 0x40123c6a7ef9db23ull, 0x3ff8460e1c7712ccull }, + { 0x40123d70a3d70a3eull, 0x3ff846f416586bf5ull }, + { 0x40123e76c8b43958ull, 0x3ff847da0350ee99ull }, + { 0x40123f7ced916873ull, 0x3ff848bfe3620dadull }, + { 0x40124083126e978dull, 0x3ff849a5b68d3be2ull }, + { 0x40124189374bc6a8ull, 0x3ff84a8b7cd3ebb2ull }, + { 0x4012428f5c28f5c3ull, 0x3ff84b7136378f51ull }, + { 0x40124395810624ddull, 0x3ff84c56e2b998b8ull }, + { 0x4012449ba5e353f8ull, 0x3ff84d3c825b79a4ull }, + { 0x401245a1cac08313ull, 0x3ff84e22151ea391ull }, + { 0x401246a7ef9db22dull, 0x3ff84f079b0487bcull }, + { 0x401247ae147ae148ull, 0x3ff84fed140e9728ull }, + { 0x401248b439581062ull, 0x3ff850d2803e4296ull }, + { 0x401249ba5e353f7dull, 0x3ff851b7df94fa8cull }, + { 0x40124ac083126e98ull, 0x3ff8529d32142f4full }, + { 0x40124bc6a7ef9db2ull, 0x3ff8538277bd50e8ull }, + { 0x40124ccccccccccdull, 0x3ff85467b091cf24ull }, + { 0x40124dd2f1a9fbe8ull, 0x3ff8554cdc93198full }, + { 0x40124ed916872b02ull, 0x3ff85631fbc29f78ull }, + { 0x40124fdf3b645a1dull, 0x3ff857170e21cff2ull }, + { 0x401250e560418937ull, 0x3ff857fc13b219d1ull }, + { 0x401251eb851eb852ull, 0x3ff858e10c74ebacull }, + { 0x401252f1a9fbe76dull, 0x3ff859c5f86bb3deull }, + { 0x401253f7ced91687ull, 0x3ff85aaad797e080ull }, + { 0x401254fdf3b645a2ull, 0x3ff85b8fa9fadf73ull }, + { 0x40125604189374bdull, 0x3ff85c746f961e59ull }, + { 0x4012570a3d70a3d7ull, 0x3ff85d59286b0a93ull }, + { 0x40125810624dd2f2ull, 0x3ff85e3dd47b114cull }, + { 0x40125916872b020cull, 0x3ff85f2273c79f6aull }, + { 0x40125a1cac083127ull, 0x3ff860070652219dull }, + { 0x40125b22d0e56042ull, 0x3ff860eb8c1c0452ull }, + { 0x40125c28f5c28f5cull, 0x3ff861d00526b3bdull }, + { 0x40125d2f1a9fbe77ull, 0x3ff862b471739bd5ull }, + { 0x40125e353f7ced92ull, 0x3ff86398d1042851ull }, + { 0x40125f3b645a1cacull, 0x3ff8647d23d9c4acull }, + { 0x4012604189374bc7ull, 0x3ff8656169f5dc29ull }, + { 0x40126147ae147ae1ull, 0x3ff86645a359d9c7ull }, + { 0x4012624dd2f1a9fcull, 0x3ff86729d007284eull }, + { 0x40126353f7ced917ull, 0x3ff8680defff3247ull }, + { 0x4012645a1cac0831ull, 0x3ff868f2034361feull }, + { 0x401265604189374cull, 0x3ff869d609d52184ull }, + { 0x4012666666666667ull, 0x3ff86aba03b5daadull }, + { 0x4012676c8b439581ull, 0x3ff86b9df0e6f70full }, + { 0x40126872b020c49cull, 0x3ff86c81d169e006ull }, + { 0x40126978d4fdf3b6ull, 0x3ff86d65a53ffeb0ull }, + { 0x40126a7ef9db22d1ull, 0x3ff86e496c6abbf1ull }, + { 0x40126b851eb851ecull, 0x3ff86f2d26eb806full }, + { 0x40126c8b43958106ull, 0x3ff87010d4c3b492ull }, + { 0x40126d916872b021ull, 0x3ff870f475f4c08aull }, + { 0x40126e978d4fdf3cull, 0x3ff871d80a800c49ull }, + { 0x40126f9db22d0e56ull, 0x3ff872bb9266ff84ull }, + { 0x401270a3d70a3d71ull, 0x3ff8739f0dab01b6ull }, + { 0x401271a9fbe76c8bull, 0x3ff874827c4d7a1cull }, + { 0x401272b020c49ba6ull, 0x3ff87565de4fcfbbull }, + { 0x401273b645a1cac1ull, 0x3ff8764933b36957ull }, + { 0x401274bc6a7ef9dbull, 0x3ff8772c7c79ad7bull }, + { 0x401275c28f5c28f6ull, 0x3ff8780fb8a40278ull }, + { 0x401276c8b4395810ull, 0x3ff878f2e833ce60ull }, + { 0x401277ced916872bull, 0x3ff879d60b2a770dull }, + { 0x401278d4fdf3b646ull, 0x3ff87ab921896219ull }, + { 0x401279db22d0e560ull, 0x3ff87b9c2b51f4e6ull }, + { 0x40127ae147ae147bull, 0x3ff87c7f2885949bull }, + { 0x40127be76c8b4396ull, 0x3ff87d621925a621ull }, + { 0x40127ced916872b0ull, 0x3ff87e44fd338e26ull }, + { 0x40127df3b645a1cbull, 0x3ff87f27d4b0b11full }, + { 0x40127ef9db22d0e5ull, 0x3ff8800a9f9e7344ull }, + { 0x4012800000000000ull, 0x3ff880ed5dfe3894ull }, + { 0x4012810624dd2f1bull, 0x3ff881d00fd164cfull }, + { 0x4012820c49ba5e35ull, 0x3ff882b2b5195b7cull }, + { 0x401283126e978d50ull, 0x3ff883954dd77fe9ull }, + { 0x401284189374bc6bull, 0x3ff88477da0d3527ull }, + { 0x4012851eb851eb85ull, 0x3ff8855a59bbde0aull }, + { 0x40128624dd2f1aa0ull, 0x3ff8863ccce4dd30ull }, + { 0x4012872b020c49baull, 0x3ff8871f338994f8ull }, + { 0x4012883126e978d5ull, 0x3ff888018dab6789ull }, + { 0x401289374bc6a7f0ull, 0x3ff888e3db4bb6ceull }, + { 0x40128a3d70a3d70aull, 0x3ff889c61c6be477ull }, + { 0x40128b4395810625ull, 0x3ff88aa8510d51fbull }, + { 0x40128c49ba5e3540ull, 0x3ff88b8a79316094ull }, + { 0x40128d4fdf3b645aull, 0x3ff88c6c94d97143ull }, + { 0x40128e5604189375ull, 0x3ff88d4ea406e4d0ull }, + { 0x40128f5c28f5c28full, 0x3ff88e30a6bb1bc6ull }, + { 0x401290624dd2f1aaull, 0x3ff88f129cf77676ull }, + { 0x4012916872b020c5ull, 0x3ff88ff486bd54faull }, + { 0x4012926e978d4fdfull, 0x3ff890d6640e172bull }, + { 0x40129374bc6a7efaull, 0x3ff891b834eb1cb0ull }, + { 0x4012947ae147ae15ull, 0x3ff89299f955c4f0ull }, + { 0x401295810624dd2full, 0x3ff8937bb14f6f19ull }, + { 0x401296872b020c4aull, 0x3ff8945d5cd97a22ull }, + { 0x4012978d4fdf3b64ull, 0x3ff8953efbf544c3ull }, + { 0x4012989374bc6a7full, 0x3ff896208ea42d7full }, + { 0x401299999999999aull, 0x3ff8970214e7929cull }, + { 0x40129a9fbe76c8b4ull, 0x3ff897e38ec0d226ull }, + { 0x40129ba5e353f7cfull, 0x3ff898c4fc3149f2ull }, + { 0x40129cac083126eaull, 0x3ff899a65d3a5799ull }, + { 0x40129db22d0e5604ull, 0x3ff89a87b1dd5879ull }, + { 0x40129eb851eb851full, 0x3ff89b68fa1ba9baull }, + { 0x40129fbe76c8b439ull, 0x3ff89c4a35f6a848ull }, + { 0x4012a0c49ba5e354ull, 0x3ff89d2b656fb0d6ull }, + { 0x4012a1cac083126full, 0x3ff89e0c88881fdeull }, + { 0x4012a2d0e5604189ull, 0x3ff89eed9f41519eull }, + { 0x4012a3d70a3d70a4ull, 0x3ff89fcea99ca220ull }, + { 0x4012a4dd2f1a9fbfull, 0x3ff8a0afa79b6d2eull }, + { 0x4012a5e353f7ced9ull, 0x3ff8a190993f0e5dull }, + { 0x4012a6e978d4fdf4ull, 0x3ff8a2717e88e10aull }, + { 0x4012a7ef9db22d0eull, 0x3ff8a352577a4052ull }, + { 0x4012a8f5c28f5c29ull, 0x3ff8a43324148722ull }, + { 0x4012a9fbe76c8b44ull, 0x3ff8a513e4591027ull }, + { 0x4012ab020c49ba5eull, 0x3ff8a5f4984935d6ull }, + { 0x4012ac083126e979ull, 0x3ff8a6d53fe6526full }, + { 0x4012ad0e56041894ull, 0x3ff8a7b5db31bff4ull }, + { 0x4012ae147ae147aeull, 0x3ff8a8966a2cd831ull }, + { 0x4012af1a9fbe76c9ull, 0x3ff8a976ecd8f4b9ull }, + { 0x4012b020c49ba5e3ull, 0x3ff8aa5763376ee5ull }, + { 0x4012b126e978d4feull, 0x3ff8ab37cd499fd7ull }, + { 0x4012b22d0e560419ull, 0x3ff8ac182b10e076ull }, + { 0x4012b33333333333ull, 0x3ff8acf87c8e8971ull }, + { 0x4012b4395810624eull, 0x3ff8add8c1c3f340ull }, + { 0x4012b53f7ced9169ull, 0x3ff8aeb8fab27622ull }, + { 0x4012b645a1cac083ull, 0x3ff8af99275b6a1aull }, + { 0x4012b74bc6a7ef9eull, 0x3ff8b07947c026f8ull }, + { 0x4012b851eb851eb8ull, 0x3ff8b1595be20450ull }, + { 0x4012b95810624dd3ull, 0x3ff8b23963c2597full }, + { 0x4012ba5e353f7ceeull, 0x3ff8b3195f627da9ull }, + { 0x4012bb645a1cac08ull, 0x3ff8b3f94ec3c7b8ull }, + { 0x4012bc6a7ef9db23ull, 0x3ff8b4d931e78e63ull }, + { 0x4012bd70a3d70a3eull, 0x3ff8b5b908cf2824ull }, + { 0x4012be76c8b43958ull, 0x3ff8b698d37beb3cull }, + { 0x4012bf7ced916873ull, 0x3ff8b77891ef2dbaull }, + { 0x4012c083126e978dull, 0x3ff8b858442a456full }, + { 0x4012c189374bc6a8ull, 0x3ff8b937ea2e87f8ull }, + { 0x4012c28f5c28f5c3ull, 0x3ff8ba1783fd4ab7ull }, + { 0x4012c395810624ddull, 0x3ff8baf71197e2d7ull }, + { 0x4012c49ba5e353f8ull, 0x3ff8bbd692ffa54dull }, + { 0x4012c5a1cac08313ull, 0x3ff8bcb60835e6d3ull }, + { 0x4012c6a7ef9db22dull, 0x3ff8bd95713bfbeeull }, + { 0x4012c7ae147ae148ull, 0x3ff8be74ce1338eaull }, + { 0x4012c8b439581062ull, 0x3ff8bf541ebcf1dcull }, + { 0x4012c9ba5e353f7dull, 0x3ff8c033633a7aa1ull }, + { 0x4012cac083126e98ull, 0x3ff8c1129b8d26dfull }, + { 0x4012cbc6a7ef9db2ull, 0x3ff8c1f1c7b64a02ull }, + { 0x4012cccccccccccdull, 0x3ff8c2d0e7b73742ull }, + { 0x4012cdd2f1a9fbe8ull, 0x3ff8c3affb91419eull }, + { 0x4012ced916872b02ull, 0x3ff8c48f0345bbdcull }, + { 0x4012cfdf3b645a1dull, 0x3ff8c56dfed5f88full }, + { 0x4012d0e560418937ull, 0x3ff8c64cee434a0eull }, + { 0x4012d1eb851eb852ull, 0x3ff8c72bd18f027cull }, + { 0x4012d2f1a9fbe76dull, 0x3ff8c80aa8ba73c3ull }, + { 0x4012d3f7ced91687ull, 0x3ff8c8e973c6ef97ull }, + { 0x4012d4fdf3b645a2ull, 0x3ff8c9c832b5c774ull }, + { 0x4012d604189374bdull, 0x3ff8caa6e5884c9full }, + { 0x4012d70a3d70a3d7ull, 0x3ff8cb858c3fd026ull }, + { 0x4012d810624dd2f2ull, 0x3ff8cc6426dda2e2ull }, + { 0x4012d916872b020cull, 0x3ff8cd42b5631572ull }, + { 0x4012da1cac083127ull, 0x3ff8ce2137d17841ull }, + { 0x4012db22d0e56042ull, 0x3ff8ceffae2a1b81ull }, + { 0x4012dc28f5c28f5cull, 0x3ff8cfde186e4f2eull }, + { 0x4012dd2f1a9fbe77ull, 0x3ff8d0bc769f630dull }, + { 0x4012de353f7ced92ull, 0x3ff8d19ac8bea6aeull }, + { 0x4012df3b645a1cacull, 0x3ff8d2790ecd6967ull }, + { 0x4012e04189374bc7ull, 0x3ff8d35748ccfa5bull }, + { 0x4012e147ae147ae1ull, 0x3ff8d43576bea873ull }, + { 0x4012e24dd2f1a9fcull, 0x3ff8d51398a3c267ull }, + { 0x4012e353f7ced917ull, 0x3ff8d5f1ae7d96b2ull }, + { 0x4012e45a1cac0831ull, 0x3ff8d6cfb84d739cull }, + { 0x4012e5604189374cull, 0x3ff8d7adb614a737ull }, + { 0x4012e66666666667ull, 0x3ff8d88ba7d47f5dull }, + { 0x4012e76c8b439581ull, 0x3ff8d9698d8e49b3ull }, + { 0x4012e872b020c49cull, 0x3ff8da47674353a9ull }, + { 0x4012e978d4fdf3b6ull, 0x3ff8db2534f4ea74ull }, + { 0x4012ea7ef9db22d1ull, 0x3ff8dc02f6a45b1aull }, + { 0x4012eb851eb851ecull, 0x3ff8dce0ac52f264ull }, + { 0x4012ec8b43958106ull, 0x3ff8ddbe5601fce9ull }, + { 0x4012ed916872b021ull, 0x3ff8de9bf3b2c70aull }, + { 0x4012ee978d4fdf3cull, 0x3ff8df7985669cf0ull }, + { 0x4012ef9db22d0e56ull, 0x3ff8e0570b1eca8full }, + { 0x4012f0a3d70a3d71ull, 0x3ff8e13484dc9ba7ull }, + { 0x4012f1a9fbe76c8bull, 0x3ff8e211f2a15bbeull }, + { 0x4012f2b020c49ba6ull, 0x3ff8e2ef546e562aull }, + { 0x4012f3b645a1cac1ull, 0x3ff8e3ccaa44d607ull }, + { 0x4012f4bc6a7ef9dbull, 0x3ff8e4a9f426263aull }, + { 0x4012f5c28f5c28f6ull, 0x3ff8e58732139178ull }, + { 0x4012f6c8b4395810ull, 0x3ff8e664640e623cull }, + { 0x4012f7ced916872bull, 0x3ff8e7418a17e2cdull }, + { 0x4012f8d4fdf3b646ull, 0x3ff8e81ea4315d3cull }, + { 0x4012f9db22d0e560ull, 0x3ff8e8fbb25c1b64ull }, + { 0x4012fae147ae147bull, 0x3ff8e9d8b49966edull }, + { 0x4012fbe76c8b4396ull, 0x3ff8eab5aaea8948ull }, + { 0x4012fced916872b0ull, 0x3ff8eb929550cbaeull }, + { 0x4012fdf3b645a1cbull, 0x3ff8ec6f73cd772aull }, + { 0x4012fef9db22d0e5ull, 0x3ff8ed4c4661d489ull }, + { 0x4013000000000000ull, 0x3ff8ee290d0f2c6aull }, + { 0x4013010624dd2f1bull, 0x3ff8ef05c7d6c733ull }, + { 0x4013020c49ba5e35ull, 0x3ff8efe276b9ed14ull }, + { 0x401303126e978d50ull, 0x3ff8f0bf19b9e60eull }, + { 0x401304189374bc6bull, 0x3ff8f19bb0d7f9e6ull }, + { 0x4013051eb851eb85ull, 0x3ff8f2783c15702eull }, + { 0x40130624dd2f1aa0ull, 0x3ff8f354bb739047ull }, + { 0x4013072b020c49baull, 0x3ff8f4312ef3a158ull }, + { 0x4013083126e978d5ull, 0x3ff8f50d9696ea57ull }, + { 0x401309374bc6a7f0ull, 0x3ff8f5e9f25eb202ull }, + { 0x40130a3d70a3d70aull, 0x3ff8f6c6424c3ee4ull }, + { 0x40130b4395810625ull, 0x3ff8f7a28660d754ull }, + { 0x40130c49ba5e3540ull, 0x3ff8f87ebe9dc174ull }, + { 0x40130d4fdf3b645aull, 0x3ff8f95aeb04432full }, + { 0x40130e5604189375ull, 0x3ff8fa370b95a23full }, + { 0x40130f5c28f5c28full, 0x3ff8fb1320532428ull }, + { 0x401310624dd2f1aaull, 0x3ff8fbef293e0e38ull }, + { 0x4013116872b020c5ull, 0x3ff8fccb2657a58cull }, + { 0x4013126e978d4fdfull, 0x3ff8fda717a12f09ull }, + { 0x40131374bc6a7efaull, 0x3ff8fe82fd1bef62ull }, + { 0x4013147ae147ae15ull, 0x3ff8ff5ed6c92b15ull }, + { 0x401315810624dd2full, 0x3ff9003aa4aa266bull }, + { 0x401316872b020c4aull, 0x3ff9011666c0257bull }, + { 0x4013178d4fdf3b64ull, 0x3ff901f21d0c6c26ull }, + { 0x4013189374bc6a7full, 0x3ff902cdc7903e19ull }, + { 0x401319999999999aull, 0x3ff903a9664cdeceull }, + { 0x40131a9fbe76c8b4ull, 0x3ff90484f9439189ull }, + { 0x40131ba5e353f7cfull, 0x3ff905608075995cull }, + { 0x40131cac083126eaull, 0x3ff9063bfbe43925ull }, + { 0x40131db22d0e5604ull, 0x3ff907176b90b38cull }, + { 0x40131eb851eb851full, 0x3ff907f2cf7c4b08ull }, + { 0x40131fbe76c8b439ull, 0x3ff908ce27a841daull }, + { 0x401320c49ba5e354ull, 0x3ff909a97415da10ull }, + { 0x401321cac083126full, 0x3ff90a84b4c65586ull }, + { 0x401322d0e5604189ull, 0x3ff90b5fe9baf5dfull }, + { 0x401323d70a3d70a4ull, 0x3ff90c3b12f4fc92ull }, + { 0x401324dd2f1a9fbfull, 0x3ff90d163075aadcull }, + { 0x401325e353f7ced9ull, 0x3ff90df1423e41caull }, + { 0x401326e978d4fdf4ull, 0x3ff90ecc48500234ull }, + { 0x401327ef9db22d0eull, 0x3ff90fa742ac2cbeull }, + { 0x401328f5c28f5c29ull, 0x3ff91082315401dbull }, + { 0x401329fbe76c8b44ull, 0x3ff9115d1448c1c9ull }, + { 0x40132b020c49ba5eull, 0x3ff91237eb8bac92ull }, + { 0x40132c083126e979ull, 0x3ff91312b71e020eull }, + { 0x40132d0e56041894ull, 0x3ff913ed770101e2ull }, + { 0x40132e147ae147aeull, 0x3ff914c82b35eb7dull }, + { 0x40132f1a9fbe76c9ull, 0x3ff915a2d3bdfe1full }, + { 0x40133020c49ba5e3ull, 0x3ff9167d709a78d1ull }, + { 0x40133126e978d4feull, 0x3ff9175801cc9a6dull }, + { 0x4013322d0e560419ull, 0x3ff918328755a196ull }, + { 0x4013333333333333ull, 0x3ff9190d0136ccbeull }, + { 0x401334395810624eull, 0x3ff919e76f715a24ull }, + { 0x4013353f7ced9169ull, 0x3ff91ac1d20687d4ull }, + { 0x40133645a1cac083ull, 0x3ff91b9c28f793a6ull }, + { 0x4013374bc6a7ef9eull, 0x3ff91c767445bb42ull }, + { 0x40133851eb851eb8ull, 0x3ff91d50b3f23c1aull }, + { 0x4013395810624dd3ull, 0x3ff91e2ae7fe536full }, + { 0x40133a5e353f7ceeull, 0x3ff91f05106b3e50ull }, + { 0x40133b645a1cac08ull, 0x3ff91fdf2d3a3996ull }, + { 0x40133c6a7ef9db23ull, 0x3ff920b93e6c81ecull }, + { 0x40133d70a3d70a3eull, 0x3ff92193440353c7ull }, + { 0x40133e76c8b43958ull, 0x3ff9226d3dffeb6aull }, + { 0x40133f7ced916873ull, 0x3ff923472c6384e8ull }, + { 0x40134083126e978dull, 0x3ff924210f2f5c1eull }, + { 0x40134189374bc6a8ull, 0x3ff924fae664acbbull }, + { 0x4013428f5c28f5c3ull, 0x3ff925d4b204b236ull }, + { 0x40134395810624ddull, 0x3ff926ae7210a7d7ull }, + { 0x4013449ba5e353f8ull, 0x3ff927882689c8b5ull }, + { 0x401345a1cac08313ull, 0x3ff92861cf714fb2ull }, + { 0x401346a7ef9db22dull, 0x3ff9293b6cc8777eull }, + { 0x401347ae147ae148ull, 0x3ff92a14fe907a9bull }, + { 0x401348b439581062ull, 0x3ff92aee84ca9352ull }, + { 0x401349ba5e353f7dull, 0x3ff92bc7ff77fbc1ull }, + { 0x40134ac083126e98ull, 0x3ff92ca16e99edceull }, + { 0x40134bc6a7ef9db2ull, 0x3ff92d7ad231a330ull }, + { 0x40134ccccccccccdull, 0x3ff92e542a40556eull }, + { 0x40134dd2f1a9fbe8ull, 0x3ff92f2d76c73dd8ull }, + { 0x40134ed916872b02ull, 0x3ff93006b7c79590ull }, + { 0x40134fdf3b645a1dull, 0x3ff930dfed429586ull }, + { 0x401350e560418937ull, 0x3ff931b917397675ull }, + { 0x401351eb851eb852ull, 0x3ff9329235ad70ecull }, + { 0x401352f1a9fbe76dull, 0x3ff9336b489fbd43ull }, + { 0x401353f7ced91687ull, 0x3ff93444501193a1ull }, + { 0x401354fdf3b645a2ull, 0x3ff9351d4c042bffull }, + { 0x40135604189374bdull, 0x3ff935f63c78be22ull }, + { 0x4013570a3d70a3d7ull, 0x3ff936cf2170819bull }, + { 0x40135810624dd2f2ull, 0x3ff937a7faecadcfull }, + { 0x40135916872b020cull, 0x3ff93880c8ee79edull }, + { 0x40135a1cac083127ull, 0x3ff939598b771cf5ull }, + { 0x40135b22d0e56042ull, 0x3ff93a324287cdb5ull }, + { 0x40135c28f5c28f5cull, 0x3ff93b0aee21c2c8ull }, + { 0x40135d2f1a9fbe77ull, 0x3ff93be38e46329bull }, + { 0x40135e353f7ced92ull, 0x3ff93cbc22f65368ull }, + { 0x40135f3b645a1cacull, 0x3ff93d94ac335b37ull }, + { 0x4013604189374bc7ull, 0x3ff93e6d29fe7fe0ull }, + { 0x40136147ae147ae1ull, 0x3ff93f459c58f709ull }, + { 0x4013624dd2f1a9fcull, 0x3ff9401e0343f629ull }, + { 0x40136353f7ced917ull, 0x3ff940f65ec0b283ull }, + { 0x4013645a1cac0831ull, 0x3ff941ceaed0612bull }, + { 0x401365604189374cull, 0x3ff942a6f3743703ull }, + { 0x4013666666666667ull, 0x3ff9437f2cad68beull }, + { 0x4013676c8b439581ull, 0x3ff944575a7d2adaull }, + { 0x40136872b020c49cull, 0x3ff9452f7ce4b1aaull }, + { 0x40136978d4fdf3b6ull, 0x3ff9460793e5314aull }, + { 0x40136a7ef9db22d1ull, 0x3ff946df9f7fddabull }, + { 0x40136b851eb851ecull, 0x3ff947b79fb5ea89ull }, + { 0x40136c8b43958106ull, 0x3ff9488f94888b71ull }, + { 0x40136d916872b021ull, 0x3ff949677df8f3c0ull }, + { 0x40136e978d4fdf3cull, 0x3ff94a3f5c0856a2ull }, + { 0x40136f9db22d0e56ull, 0x3ff94b172eb7e710ull }, + { 0x401370a3d70a3d71ull, 0x3ff94beef608d7d7ull }, + { 0x401371a9fbe76c8bull, 0x3ff94cc6b1fc5b8full }, + { 0x401372b020c49ba6ull, 0x3ff94d9e6293a4a4ull }, + { 0x401373b645a1cac1ull, 0x3ff94e7607cfe54eull }, + { 0x401374bc6a7ef9dbull, 0x3ff94f4da1b24f95ull }, + { 0x401375c28f5c28f6ull, 0x3ff95025303c1553ull }, + { 0x401376c8b4395810ull, 0x3ff950fcb36e6830ull }, + { 0x401377ced916872bull, 0x3ff951d42b4a79a4ull }, + { 0x401378d4fdf3b646ull, 0x3ff952ab97d17af8ull }, + { 0x401379db22d0e560ull, 0x3ff95382f9049d42ull }, + { 0x40137ae147ae147bull, 0x3ff9545a4ee5116bull }, + { 0x40137be76c8b4396ull, 0x3ff9553199740829ull }, + { 0x40137ced916872b0ull, 0x3ff95608d8b2b205ull }, + { 0x40137df3b645a1cbull, 0x3ff956e00ca23f56ull }, + { 0x40137ef9db22d0e5ull, 0x3ff957b73543e044ull }, + { 0x4013800000000000ull, 0x3ff9588e5298c4c6ull }, + { 0x4013810624dd2f1bull, 0x3ff9596564a21ca4ull }, + { 0x4013820c49ba5e35ull, 0x3ff95a3c6b611775ull }, + { 0x401383126e978d50ull, 0x3ff95b1366d6e4a3ull }, + { 0x401384189374bc6bull, 0x3ff95bea5704b366ull }, + { 0x4013851eb851eb85ull, 0x3ff95cc13bebb2c4ull }, + { 0x40138624dd2f1aa0ull, 0x3ff95d98158d1199ull }, + { 0x4013872b020c49baull, 0x3ff95e6ee3e9fe8dull }, + { 0x4013883126e978d5ull, 0x3ff95f45a703a81bull }, + { 0x401389374bc6a7f0ull, 0x3ff9601c5edb3c8bull }, + { 0x40138a3d70a3d70aull, 0x3ff960f30b71e9f8ull }, + { 0x40138b4395810625ull, 0x3ff961c9acc8de4full }, + { 0x40138c49ba5e3540ull, 0x3ff962a042e1474aull }, + { 0x40138d4fdf3b645aull, 0x3ff96376cdbc5274ull }, + { 0x40138e5604189375ull, 0x3ff9644d4d5b2d2dull }, + { 0x40138f5c28f5c28full, 0x3ff96523c1bf04a0ull }, + { 0x401390624dd2f1aaull, 0x3ff965fa2ae905cdull }, + { 0x4013916872b020c5ull, 0x3ff966d088da5d82ull }, + { 0x4013926e978d4fdfull, 0x3ff967a6db94385eull }, + { 0x40139374bc6a7efaull, 0x3ff9687d2317c2d4ull }, + { 0x4013947ae147ae15ull, 0x3ff969535f662923ull }, + { 0x401395810624dd2full, 0x3ff96a299080975eull }, + { 0x401396872b020c4aull, 0x3ff96affb6683968ull }, + { 0x4013978d4fdf3b64ull, 0x3ff96bd5d11e3af6ull }, + { 0x4013989374bc6a7full, 0x3ff96cabe0a3c78dull }, + { 0x401399999999999aull, 0x3ff96d81e4fa0a84ull }, + { 0x40139a9fbe76c8b4ull, 0x3ff96e57de222f00ull }, + { 0x40139ba5e353f7cfull, 0x3ff96f2dcc1d5ffcull }, + { 0x40139cac083126eaull, 0x3ff97003aeecc841ull }, + { 0x40139db22d0e5604ull, 0x3ff970d986919269ull }, + { 0x40139eb851eb851full, 0x3ff971af530ce8e1ull }, + { 0x40139fbe76c8b439ull, 0x3ff97285145ff5e5ull }, + { 0x4013a0c49ba5e354ull, 0x3ff9735aca8be386ull }, + { 0x4013a1cac083126full, 0x3ff974307591dba3ull }, + { 0x4013a2d0e5604189ull, 0x3ff97506157307edull }, + { 0x4013a3d70a3d70a4ull, 0x3ff975dbaa3091e7ull }, + { 0x4013a4dd2f1a9fbfull, 0x3ff976b133cba2e6ull }, + { 0x4013a5e353f7ced9ull, 0x3ff97786b245640full }, + { 0x4013a6e978d4fdf4ull, 0x3ff9785c259efe5aull }, + { 0x4013a7ef9db22d0eull, 0x3ff979318dd99a8full }, + { 0x4013a8f5c28f5c29ull, 0x3ff97a06eaf6614bull }, + { 0x4013a9fbe76c8b44ull, 0x3ff97adc3cf67af8ull }, + { 0x4013ab020c49ba5eull, 0x3ff97bb183db0fd5ull }, + { 0x4013ac083126e979ull, 0x3ff97c86bfa547f1ull }, + { 0x4013ad0e56041894ull, 0x3ff97d5bf0564b2eull }, + { 0x4013ae147ae147aeull, 0x3ff97e3115ef413full }, + { 0x4013af1a9fbe76c9ull, 0x3ff97f06307151aaull }, + { 0x4013b020c49ba5e3ull, 0x3ff97fdb3fdda3c4ull }, + { 0x4013b126e978d4feull, 0x3ff980b044355eb8ull }, + { 0x4013b22d0e560419ull, 0x3ff981853d79a981ull }, + { 0x4013b33333333333ull, 0x3ff9825a2babaaeaull }, + { 0x4013b4395810624eull, 0x3ff9832f0ecc8993ull }, + { 0x4013b53f7ced9169ull, 0x3ff98403e6dd6beeull }, + { 0x4013b645a1cac083ull, 0x3ff984d8b3df783bull }, + { 0x4013b74bc6a7ef9eull, 0x3ff985ad75d3d493ull }, + { 0x4013b851eb851eb8ull, 0x3ff986822cbba6dbull }, + { 0x4013b95810624dd3ull, 0x3ff98756d89814ceull }, + { 0x4013ba5e353f7ceeull, 0x3ff9882b796a43f7ull }, + { 0x4013bb645a1cac08ull, 0x3ff989000f3359b4ull }, + { 0x4013bc6a7ef9db23ull, 0x3ff989d499f47b37ull }, + { 0x4013bd70a3d70a3eull, 0x3ff98aa919aecd81ull }, + { 0x4013be76c8b43958ull, 0x3ff98b7d8e637569ull }, + { 0x4013bf7ced916873ull, 0x3ff98c51f8139796ull }, + { 0x4013c083126e978dull, 0x3ff98d2656c05881ull }, + { 0x4013c189374bc6a8ull, 0x3ff98dfaaa6adc79ull }, + { 0x4013c28f5c28f5c3ull, 0x3ff98ecef314479dull }, + { 0x4013c395810624ddull, 0x3ff98fa330bdbdddull }, + { 0x4013c49ba5e353f8ull, 0x3ff99077636862ffull }, + { 0x4013c5a1cac08313ull, 0x3ff9914b8b155a9aull }, + { 0x4013c6a7ef9db22dull, 0x3ff9921fa7c5c817ull }, + { 0x4013c7ae147ae148ull, 0x3ff992f3b97aceb5ull }, + { 0x4013c8b439581062ull, 0x3ff993c7c0359180ull }, + { 0x4013c9ba5e353f7dull, 0x3ff9949bbbf7335dull }, + { 0x4013cac083126e98ull, 0x3ff9956facc0d700ull }, + { 0x4013cbc6a7ef9db2ull, 0x3ff9964392939ef0ull }, + { 0x4013cccccccccccdull, 0x3ff997176d70ad8aull }, + { 0x4013cdd2f1a9fbe8ull, 0x3ff997eb3d5924faull }, + { 0x4013ced916872b02ull, 0x3ff998bf024e2741ull }, + { 0x4013cfdf3b645a1dull, 0x3ff99992bc50d634ull }, + { 0x4013d0e560418937ull, 0x3ff99a666b625378ull }, + { 0x4013d1eb851eb852ull, 0x3ff99b3a0f83c089ull }, + { 0x4013d2f1a9fbe76dull, 0x3ff99c0da8b63eb3ull }, + { 0x4013d3f7ced91687ull, 0x3ff99ce136faef16ull }, + { 0x4013d4fdf3b645a2ull, 0x3ff99db4ba52f2a7ull }, + { 0x4013d604189374bdull, 0x3ff99e8832bf6a2bull }, + { 0x4013d70a3d70a3d7ull, 0x3ff99f5ba041763cull }, + { 0x4013d810624dd2f2ull, 0x3ff9a02f02da3749ull }, + { 0x4013d916872b020cull, 0x3ff9a1025a8acd92ull }, + { 0x4013da1cac083127ull, 0x3ff9a1d5a754592bull }, + { 0x4013db22d0e56042ull, 0x3ff9a2a8e937f9fbull }, + { 0x4013dc28f5c28f5cull, 0x3ff9a37c2036cfbdull }, + { 0x4013dd2f1a9fbe77ull, 0x3ff9a44f4c51fa01ull }, + { 0x4013de353f7ced92ull, 0x3ff9a5226d8a9827ull }, + { 0x4013df3b645a1cacull, 0x3ff9a5f583e1c966ull }, + { 0x4013e04189374bc7ull, 0x3ff9a6c88f58acc6ull }, + { 0x4013e147ae147ae1ull, 0x3ff9a79b8ff06125ull }, + { 0x4013e24dd2f1a9fcull, 0x3ff9a86e85aa0533ull }, + { 0x4013e353f7ced917ull, 0x3ff9a9417086b775ull }, + { 0x4013e45a1cac0831ull, 0x3ff9aa1450879641ull }, + { 0x4013e5604189374cull, 0x3ff9aae725adbfc5ull }, + { 0x4013e66666666667ull, 0x3ff9abb9effa51ffull }, + { 0x4013e76c8b439581ull, 0x3ff9ac8caf6e6ac2ull }, + { 0x4013e872b020c49cull, 0x3ff9ad5f640b27b8ull }, + { 0x4013e978d4fdf3b6ull, 0x3ff9ae320dd1a659ull }, + { 0x4013ea7ef9db22d1ull, 0x3ff9af04acc303f6ull }, + { 0x4013eb851eb851ecull, 0x3ff9afd740e05db3ull }, + { 0x4013ec8b43958106ull, 0x3ff9b0a9ca2ad085ull }, + { 0x4013ed916872b021ull, 0x3ff9b17c48a37939ull }, + { 0x4013ee978d4fdf3cull, 0x3ff9b24ebc4b746eull }, + { 0x4013ef9db22d0e56ull, 0x3ff9b3212523de96ull }, + { 0x4013f0a3d70a3d71ull, 0x3ff9b3f3832dd3fbull }, + { 0x4013f1a9fbe76c8bull, 0x3ff9b4c5d66a70b7ull }, + { 0x4013f2b020c49ba6ull, 0x3ff9b5981edad0bdull }, + { 0x4013f3b645a1cac1ull, 0x3ff9b66a5c800fceull }, + { 0x4013f4bc6a7ef9dbull, 0x3ff9b73c8f5b4984ull }, + { 0x4013f5c28f5c28f6ull, 0x3ff9b80eb76d994eull }, + { 0x4013f6c8b4395811ull, 0x3ff9b8e0d4b81a6bull }, + { 0x4013f7ced916872bull, 0x3ff9b9b2e73be7f1ull }, + { 0x4013f8d4fdf3b646ull, 0x3ff9ba84eefa1cccull }, + { 0x4013f9db22d0e560ull, 0x3ff9bb56ebf3d3b9ull }, + { 0x4013fae147ae147bull, 0x3ff9bc28de2a274dull }, + { 0x4013fbe76c8b4396ull, 0x3ff9bcfac59e31efull }, + { 0x4013fced916872b0ull, 0x3ff9bdcca2510ddbull }, + { 0x4013fdf3b645a1cbull, 0x3ff9be9e7443d524ull }, + { 0x4013fef9db22d0e5ull, 0x3ff9bf703b77a1adull }, + { 0x4014000000000000ull, 0x3ff9c041f7ed8d33ull }, + { 0x4014010624dd2f1bull, 0x3ff9c113a9a6b145ull }, + { 0x4014020c49ba5e35ull, 0x3ff9c1e550a42745ull }, + { 0x401403126e978d50ull, 0x3ff9c2b6ece7086eull }, + { 0x401404189374bc6bull, 0x3ff9c3887e706dcdull }, + { 0x4014051eb851eb85ull, 0x3ff9c45a05417043ull }, + { 0x40140624dd2f1aa0ull, 0x3ff9c52b815b2889ull }, + { 0x4014072b020c49baull, 0x3ff9c5fcf2beaf2bull }, + { 0x4014083126e978d5ull, 0x3ff9c6ce596d1c8bull }, + { 0x401409374bc6a7f0ull, 0x3ff9c79fb56788deull }, + { 0x40140a3d70a3d70aull, 0x3ff9c87106af0c2full }, + { 0x40140b4395810625ull, 0x3ff9c9424d44be60ull }, + { 0x40140c49ba5e3540ull, 0x3ff9ca138929b726ull }, + { 0x40140d4fdf3b645aull, 0x3ff9cae4ba5f0e0bull }, + { 0x40140e5604189375ull, 0x3ff9cbb5e0e5da70ull }, + { 0x40140f5c28f5c28full, 0x3ff9cc86fcbf3389ull }, + { 0x401410624dd2f1aaull, 0x3ff9cd580dec3061ull }, + { 0x4014116872b020c5ull, 0x3ff9ce29146de7d7ull }, + { 0x4014126e978d4fdfull, 0x3ff9cefa104570a0ull }, + { 0x40141374bc6a7efaull, 0x3ff9cfcb0173e147ull }, + { 0x4014147ae147ae15ull, 0x3ff9d09be7fa502cull }, + { 0x401415810624dd2full, 0x3ff9d16cc3d9d382ull }, + { 0x401416872b020c4aull, 0x3ff9d23d95138155ull }, + { 0x4014178d4fdf3b64ull, 0x3ff9d30e5ba86f84ull }, + { 0x4014189374bc6a7full, 0x3ff9d3df1799b3c7ull }, + { 0x401419999999999aull, 0x3ff9d4afc8e863a6ull }, + { 0x40141a9fbe76c8b4ull, 0x3ff9d5806f959483ull }, + { 0x40141ba5e353f7cfull, 0x3ff9d6510ba25b95ull }, + { 0x40141cac083126eaull, 0x3ff9d7219d0fcde7ull }, + { 0x40141db22d0e5604ull, 0x3ff9d7f223df0059ull }, + { 0x40141eb851eb851full, 0x3ff9d8c2a01107a6ull }, + { 0x40141fbe76c8b439ull, 0x3ff9d99311a6f858ull }, + { 0x401420c49ba5e354ull, 0x3ff9da6378a1e6d4ull }, + { 0x401421cac083126full, 0x3ff9db33d502e751ull }, + { 0x401422d0e5604189ull, 0x3ff9dc0426cb0ddeull }, + { 0x401423d70a3d70a4ull, 0x3ff9dcd46dfb6e61ull }, + { 0x401424dd2f1a9fbfull, 0x3ff9dda4aa951c92ull }, + { 0x401425e353f7ced9ull, 0x3ff9de74dc992c02ull }, + { 0x401426e978d4fdf4ull, 0x3ff9df450408b019ull }, + { 0x401427ef9db22d0eull, 0x3ff9e01520e4bc10ull }, + { 0x401428f5c28f5c29ull, 0x3ff9e0e5332e62fdull }, + { 0x401429fbe76c8b44ull, 0x3ff9e1b53ae6b7c7ull }, + { 0x40142b020c49ba5eull, 0x3ff9e285380ecd2bull }, + { 0x40142c083126e979ull, 0x3ff9e3552aa7b5c0ull }, + { 0x40142d0e56041894ull, 0x3ff9e42512b283f1ull }, + { 0x40142e147ae147aeull, 0x3ff9e4f4f03049fcull }, + { 0x40142f1a9fbe76c9ull, 0x3ff9e5c4c32219fcull }, + { 0x40143020c49ba5e3ull, 0x3ff9e6948b8905ddull }, + { 0x40143126e978d4feull, 0x3ff9e76449661f64ull }, + { 0x4014322d0e560419ull, 0x3ff9e833fcba782aull }, + { 0x4014333333333333ull, 0x3ff9e903a58721a0ull }, + { 0x401434395810624eull, 0x3ff9e9d343cd2d0full }, + { 0x4014353f7ced9169ull, 0x3ff9eaa2d78dab92ull }, + { 0x40143645a1cac083ull, 0x3ff9eb7260c9ae1full }, + { 0x4014374bc6a7ef9eull, 0x3ff9ec41df824580ull }, + { 0x40143851eb851eb8ull, 0x3ff9ed1153b88255ull }, + { 0x4014395810624dd3ull, 0x3ff9ede0bd6d7519ull }, + { 0x40143a5e353f7ceeull, 0x3ff9eeb01ca22e17ull }, + { 0x40143b645a1cac08ull, 0x3ff9ef7f7157bd75ull }, + { 0x40143c6a7ef9db23ull, 0x3ff9f04ebb8f3330ull }, + { 0x40143d70a3d70a3eull, 0x3ff9f11dfb499f1aull }, + { 0x40143e76c8b43958ull, 0x3ff9f1ed308810daull }, + { 0x40143f7ced916873ull, 0x3ff9f2bc5b4b97f2ull }, + { 0x40144083126e978dull, 0x3ff9f38b7b9543b9ull }, + { 0x40144189374bc6a8ull, 0x3ff9f45a9166235cull }, + { 0x4014428f5c28f5c3ull, 0x3ff9f5299cbf45dfull }, + { 0x40144395810624ddull, 0x3ff9f5f89da1ba1eull }, + { 0x4014449ba5e353f8ull, 0x3ff9f6c7940e8ecbull }, + { 0x401445a1cac08313ull, 0x3ff9f7968006d270ull }, + { 0x401446a7ef9db22dull, 0x3ff9f865618b936dull }, + { 0x401447ae147ae148ull, 0x3ff9f934389ddff9ull }, + { 0x401448b439581062ull, 0x3ff9fa03053ec622ull }, + { 0x401449ba5e353f7dull, 0x3ff9fad1c76f53cfull }, + { 0x40144ac083126e98ull, 0x3ff9fba07f3096bcull }, + { 0x40144bc6a7ef9db2ull, 0x3ff9fc6f2c839c7bull }, + { 0x40144ccccccccccdull, 0x3ff9fd3dcf69727bull }, + { 0x40144dd2f1a9fbe8ull, 0x3ff9fe0c67e325fbull }, + { 0x40144ed916872b02ull, 0x3ff9fedaf5f1c415ull }, + { 0x40144fdf3b645a1dull, 0x3ff9ffa9799659bbull }, + { 0x401450e560418937ull, 0x3ffa0077f2d1f3b5ull }, + { 0x401451eb851eb852ull, 0x3ffa014661a59ea2ull }, + { 0x401452f1a9fbe76dull, 0x3ffa0214c61266f9ull }, + { 0x401453f7ced91687ull, 0x3ffa02e320195907ull }, + { 0x401454fdf3b645a2ull, 0x3ffa03b16fbb80f5ull }, + { 0x40145604189374bdull, 0x3ffa047fb4f9eabeull }, + { 0x4014570a3d70a3d7ull, 0x3ffa054defd5a236ull }, + { 0x40145810624dd2f2ull, 0x3ffa061c204fb30cull }, + { 0x40145916872b020cull, 0x3ffa06ea466928c2ull }, + { 0x40145a1cac083127ull, 0x3ffa07b862230eb5ull }, + { 0x40145b22d0e56042ull, 0x3ffa0886737e7017ull }, + { 0x40145c28f5c28f5cull, 0x3ffa09547a7c57f3ull }, + { 0x40145d2f1a9fbe77ull, 0x3ffa0a22771dd12cull }, + { 0x40145e353f7ced92ull, 0x3ffa0af06963e67dull }, + { 0x40145f3b645a1cacull, 0x3ffa0bbe514fa276ull }, + { 0x4014604189374bc7ull, 0x3ffa0c8c2ee20f84ull }, + { 0x40146147ae147ae1ull, 0x3ffa0d5a021c37e5ull }, + { 0x4014624dd2f1a9fcull, 0x3ffa0e27caff25b6ull }, + { 0x40146353f7ced917ull, 0x3ffa0ef5898be2e8ull }, + { 0x4014645a1cac0831ull, 0x3ffa0fc33dc37942ull }, + { 0x401465604189374cull, 0x3ffa1090e7a6f269ull }, + { 0x4014666666666667ull, 0x3ffa115e873757d4ull }, + { 0x4014676c8b439581ull, 0x3ffa122c1c75b2d4ull }, + { 0x40146872b020c49cull, 0x3ffa12f9a7630c95ull }, + { 0x40146978d4fdf3b6ull, 0x3ffa13c728006e16ull }, + { 0x40146a7ef9db22d1ull, 0x3ffa14949e4ee032ull }, + { 0x40146b851eb851ecull, 0x3ffa15620a4f6b9cull }, + { 0x40146c8b43958106ull, 0x3ffa162f6c0318dcull }, + { 0x40146d916872b021ull, 0x3ffa16fcc36af055ull }, + { 0x40146e978d4fdf3cull, 0x3ffa17ca1087fa42ull }, + { 0x40146f9db22d0e56ull, 0x3ffa1897535b3eb5ull }, + { 0x401470a3d70a3d71ull, 0x3ffa19648be5c59aull }, + { 0x401471a9fbe76c8bull, 0x3ffa1a31ba2896b4ull }, + { 0x401472b020c49ba6ull, 0x3ffa1afede24b99full }, + { 0x401473b645a1cac1ull, 0x3ffa1bcbf7db35cfull }, + { 0x401474bc6a7ef9dbull, 0x3ffa1c99074d1290ull }, + { 0x401475c28f5c28f6ull, 0x3ffa1d660c7b5708ull }, + { 0x401476c8b4395811ull, 0x3ffa1e3307670a35ull }, + { 0x401477ced916872bull, 0x3ffa1efff81132ecull }, + { 0x401478d4fdf3b646ull, 0x3ffa1fccde7ad7ddull }, + { 0x401479db22d0e560ull, 0x3ffa2099baa4ff8dull }, + { 0x40147ae147ae147bull, 0x3ffa21668c90b05eull }, + { 0x40147be76c8b4396ull, 0x3ffa2233543ef089ull }, + { 0x40147ced916872b0ull, 0x3ffa230011b0c61cull }, + { 0x40147df3b645a1cbull, 0x3ffa23ccc4e73705ull }, + { 0x40147ef9db22d0e5ull, 0x3ffa24996de34903ull }, + { 0x4014800000000000ull, 0x3ffa25660ca601b5ull }, + { 0x4014810624dd2f1bull, 0x3ffa2632a130668cull }, + { 0x4014820c49ba5e35ull, 0x3ffa26ff2b837cd6ull }, + { 0x401483126e978d50ull, 0x3ffa27cbaba049bbull }, + { 0x401484189374bc6bull, 0x3ffa28982187d237ull }, + { 0x4014851eb851eb85ull, 0x3ffa29648d3b1b23ull }, + { 0x40148624dd2f1aa0ull, 0x3ffa2a30eebb292full }, + { 0x4014872b020c49baull, 0x3ffa2afd460900e6ull }, + { 0x4014883126e978d5ull, 0x3ffa2bc99325a6aaull }, + { 0x401489374bc6a7f0ull, 0x3ffa2c95d6121eb7ull }, + { 0x40148a3d70a3d70aull, 0x3ffa2d620ecf6d20ull }, + { 0x40148b4395810625ull, 0x3ffa2e2e3d5e95d6ull }, + { 0x40148c49ba5e3540ull, 0x3ffa2efa61c09c9eull }, + { 0x40148d4fdf3b645aull, 0x3ffa2fc67bf68518ull }, + { 0x40148e5604189375ull, 0x3ffa30928c0152bfull }, + { 0x40148f5c28f5c28full, 0x3ffa315e91e208e4ull }, + { 0x401490624dd2f1aaull, 0x3ffa322a8d99aab4ull }, + { 0x4014916872b020c5ull, 0x3ffa32f67f293b35ull }, + { 0x4014926e978d4fdfull, 0x3ffa33c26691bd42ull }, + { 0x40149374bc6a7efaull, 0x3ffa348e43d43397ull }, + { 0x4014947ae147ae15ull, 0x3ffa355a16f1a0c3ull }, + { 0x401495810624dd2full, 0x3ffa3625dfeb0731ull }, + { 0x401496872b020c4aull, 0x3ffa36f19ec16925ull }, + { 0x4014978d4fdf3b64ull, 0x3ffa37bd5375c8bcull }, + { 0x4014989374bc6a7full, 0x3ffa3888fe0927efull }, + { 0x401499999999999aull, 0x3ffa39549e7c888dull }, + { 0x40149a9fbe76c8b4ull, 0x3ffa3a2034d0ec40ull }, + { 0x40149ba5e353f7cfull, 0x3ffa3aebc107548cull }, + { 0x40149cac083126eaull, 0x3ffa3bb74320c2cfull }, + { 0x40149db22d0e5604ull, 0x3ffa3c82bb1e383full }, + { 0x40149eb851eb851full, 0x3ffa3d4e2900b5edull }, + { 0x40149fbe76c8b439ull, 0x3ffa3e198cc93cc3ull }, + { 0x4014a0c49ba5e354ull, 0x3ffa3ee4e678cd87ull }, + { 0x4014a1cac083126full, 0x3ffa3fb0361068d4ull }, + { 0x4014a2d0e5604189ull, 0x3ffa407b7b910f23ull }, + { 0x4014a3d70a3d70a4ull, 0x3ffa4146b6fbc0c6ull }, + { 0x4014a4dd2f1a9fbfull, 0x3ffa4211e8517de8ull }, + { 0x4014a5e353f7ced9ull, 0x3ffa42dd0f93468dull }, + { 0x4014a6e978d4fdf4ull, 0x3ffa43a82cc21a96ull }, + { 0x4014a7ef9db22d0eull, 0x3ffa44733fdef9baull }, + { 0x4014a8f5c28f5c29ull, 0x3ffa453e48eae38eull }, + { 0x4014a9fbe76c8b44ull, 0x3ffa460947e6d77full }, + { 0x4014ab020c49ba5eull, 0x3ffa46d43cd3d4d2ull }, + { 0x4014ac083126e979ull, 0x3ffa479f27b2daaaull }, + { 0x4014ad0e56041894ull, 0x3ffa486a0884e802ull }, + { 0x4014ae147ae147aeull, 0x3ffa4934df4afbaeull }, + { 0x4014af1a9fbe76c9ull, 0x3ffa49ffac06145full }, + { 0x4014b020c49ba5e3ull, 0x3ffa4aca6eb7309eull }, + { 0x4014b126e978d4feull, 0x3ffa4b95275f4ed0ull }, + { 0x4014b22d0e560419ull, 0x3ffa4c5fd5ff6d31ull }, + { 0x4014b33333333333ull, 0x3ffa4d2a7a9889dbull }, + { 0x4014b4395810624eull, 0x3ffa4df5152ba2c0ull }, + { 0x4014b53f7ced9169ull, 0x3ffa4ebfa5b9b5aeull }, + { 0x4014b645a1cac083ull, 0x3ffa4f8a2c43c04aull }, + { 0x4014b74bc6a7ef9eull, 0x3ffa5054a8cac018ull }, + { 0x4014b851eb851eb8ull, 0x3ffa511f1b4fb273ull }, + { 0x4014b95810624dd3ull, 0x3ffa51e983d39493ull }, + { 0x4014ba5e353f7ceeull, 0x3ffa52b3e2576387ull }, + { 0x4014bb645a1cac08ull, 0x3ffa537e36dc1c3bull }, + { 0x4014bc6a7ef9db23ull, 0x3ffa54488162bb77ull }, + { 0x4014bd70a3d70a3eull, 0x3ffa5512c1ec3ddaull }, + { 0x4014be76c8b43958ull, 0x3ffa55dcf8799fdeull }, + { 0x4014bf7ced916873ull, 0x3ffa56a7250bdddcull }, + { 0x4014c083126e978dull, 0x3ffa577147a3f401ull }, + { 0x4014c189374bc6a8ull, 0x3ffa583b6042de5cull }, + { 0x4014c28f5c28f5c3ull, 0x3ffa59056ee998cfull }, + { 0x4014c395810624ddull, 0x3ffa59cf73991f1dull }, + { 0x4014c49ba5e353f8ull, 0x3ffa5a996e526ce0ull }, + { 0x4014c5a1cac08313ull, 0x3ffa5b635f167d8full }, + { 0x4014c6a7ef9db22dull, 0x3ffa5c2d45e64c7bull }, + { 0x4014c7ae147ae148ull, 0x3ffa5cf722c2d4cfull }, + { 0x4014c8b439581062ull, 0x3ffa5dc0f5ad1191ull }, + { 0x4014c9ba5e353f7dull, 0x3ffa5e8abea5fda5ull }, + { 0x4014cac083126e98ull, 0x3ffa5f547dae93c6ull }, + { 0x4014cbc6a7ef9db2ull, 0x3ffa601e32c7ce8aull }, + { 0x4014cccccccccccdull, 0x3ffa60e7ddf2a866ull }, + { 0x4014cdd2f1a9fbe8ull, 0x3ffa61b17f301ba7ull }, + { 0x4014ced916872b02ull, 0x3ffa627b16812273ull }, + { 0x4014cfdf3b645a1dull, 0x3ffa6344a3e6b6d0ull }, + { 0x4014d0e560418937ull, 0x3ffa640e2761d29cull }, + { 0x4014d1eb851eb852ull, 0x3ffa64d7a0f36f91ull }, + { 0x4014d2f1a9fbe76dull, 0x3ffa65a1109c8743ull }, + { 0x4014d3f7ced91687ull, 0x3ffa666a765e1322ull }, + { 0x4014d4fdf3b645a2ull, 0x3ffa6733d2390c7cull }, + { 0x4014d604189374bdull, 0x3ffa67fd242e6c75ull }, + { 0x4014d70a3d70a3d7ull, 0x3ffa68c66c3f2c0eull }, + { 0x4014d810624dd2f2ull, 0x3ffa698faa6c4427ull }, + { 0x4014d916872b020cull, 0x3ffa6a58deb6ad76ull }, + { 0x4014da1cac083127ull, 0x3ffa6b22091f6090ull }, + { 0x4014db22d0e56042ull, 0x3ffa6beb29a755e5ull }, + { 0x4014dc28f5c28f5cull, 0x3ffa6cb4404f85bdull }, + { 0x4014dd2f1a9fbe77ull, 0x3ffa6d7d4d18e841ull }, + { 0x4014de353f7ced92ull, 0x3ffa6e4650047571ull }, + { 0x4014df3b645a1cacull, 0x3ffa6f0f49132529ull }, + { 0x4014e04189374bc7ull, 0x3ffa6fd83845ef24ull }, + { 0x4014e147ae147ae1ull, 0x3ffa70a11d9dcaf4ull }, + { 0x4014e24dd2f1a9fcull, 0x3ffa7169f91bb00aull }, + { 0x4014e353f7ced917ull, 0x3ffa7232cac095b0ull }, + { 0x4014e45a1cac0831ull, 0x3ffa72fb928d730eull }, + { 0x4014e5604189374cull, 0x3ffa73c450833f26ull }, + { 0x4014e66666666667ull, 0x3ffa748d04a2f0d7ull }, + { 0x4014e76c8b439581ull, 0x3ffa7555aeed7ed9ull }, + { 0x4014e872b020c49cull, 0x3ffa761e4f63dfc3ull }, + { 0x4014e978d4fdf3b6ull, 0x3ffa76e6e6070a07ull }, + { 0x4014ea7ef9db22d1ull, 0x3ffa77af72d7f3f2ull }, + { 0x4014eb851eb851ecull, 0x3ffa7877f5d793adull }, + { 0x4014ec8b43958106ull, 0x3ffa79406f06df3cull }, + { 0x4014ed916872b021ull, 0x3ffa7a08de66cc81ull }, + { 0x4014ee978d4fdf3cull, 0x3ffa7ad143f85139ull }, + { 0x4014ef9db22d0e56ull, 0x3ffa7b999fbc62fbull }, + { 0x4014f0a3d70a3d71ull, 0x3ffa7c61f1b3f73eull }, + { 0x4014f1a9fbe76c8bull, 0x3ffa7d2a39e00350ull }, + { 0x4014f2b020c49ba6ull, 0x3ffa7df278417c61ull }, + { 0x4014f3b645a1cac1ull, 0x3ffa7ebaacd95777ull }, + { 0x4014f4bc6a7ef9dbull, 0x3ffa7f82d7a88977ull }, + { 0x4014f5c28f5c28f6ull, 0x3ffa804af8b00723ull }, + { 0x4014f6c8b4395811ull, 0x3ffa81130ff0c518ull }, + { 0x4014f7ced916872bull, 0x3ffa81db1d6bb7ceull }, + { 0x4014f8d4fdf3b646ull, 0x3ffa82a32121d39cull }, + { 0x4014f9db22d0e560ull, 0x3ffa836b1b140cb1ull }, + { 0x4014fae147ae147bull, 0x3ffa84330b43571full }, + { 0x4014fbe76c8b4396ull, 0x3ffa84faf1b0a6cdull }, + { 0x4014fced916872b0ull, 0x3ffa85c2ce5cef81ull }, + { 0x4014fdf3b645a1cbull, 0x3ffa868aa14924e0ull }, + { 0x4014fef9db22d0e5ull, 0x3ffa87526a763a67ull }, + { 0x4015000000000000ull, 0x3ffa881a29e52373ull }, + { 0x4015010624dd2f1bull, 0x3ffa88e1df96d33bull }, + { 0x4015020c49ba5e35ull, 0x3ffa89a98b8c3cd3ull }, + { 0x401503126e978d50ull, 0x3ffa8a712dc6532dull }, + { 0x401504189374bc6bull, 0x3ffa8b38c6460915ull }, + { 0x4015051eb851eb85ull, 0x3ffa8c00550c5135ull }, + { 0x40150624dd2f1aa0ull, 0x3ffa8cc7da1a1e14ull }, + { 0x4015072b020c49baull, 0x3ffa8d8f55706213ull }, + { 0x4015083126e978d5ull, 0x3ffa8e56c7100f74ull }, + { 0x401509374bc6a7f0ull, 0x3ffa8f1e2efa1852ull }, + { 0x40150a3d70a3d70aull, 0x3ffa8fe58d2f6ea5ull }, + { 0x40150b4395810625ull, 0x3ffa90ace1b10443ull }, + { 0x40150c49ba5e3540ull, 0x3ffa91742c7fcadeull }, + { 0x40150d4fdf3b645aull, 0x3ffa923b6d9cb404ull }, + { 0x40150e5604189375ull, 0x3ffa9302a508b122ull }, + { 0x40150f5c28f5c28full, 0x3ffa93c9d2c4b37eull }, + { 0x401510624dd2f1aaull, 0x3ffa9490f6d1ac41ull }, + { 0x4015116872b020c5ull, 0x3ffa955811308c6aull }, + { 0x4015126e978d4fdfull, 0x3ffa961f21e244d8ull }, + { 0x40151374bc6a7efaull, 0x3ffa96e628e7c648ull }, + { 0x4015147ae147ae15ull, 0x3ffa97ad26420152ull }, + { 0x401515810624dd2full, 0x3ffa987419f1e66bull }, + { 0x401516872b020c4aull, 0x3ffa993b03f865e6ull }, + { 0x4015178d4fdf3b64ull, 0x3ffa9a01e4566ff3ull }, + { 0x4015189374bc6a7full, 0x3ffa9ac8bb0cf49eull }, + { 0x401519999999999aull, 0x3ffa9b8f881ce3d1ull }, + { 0x40151a9fbe76c8b4ull, 0x3ffa9c564b872d52ull }, + { 0x40151ba5e353f7cfull, 0x3ffa9d1d054cc0c6ull }, + { 0x40151cac083126eaull, 0x3ffa9de3b56e8daeull }, + { 0x40151db22d0e5604ull, 0x3ffa9eaa5bed8366ull }, + { 0x40151eb851eb851full, 0x3ffa9f70f8ca912bull }, + { 0x40151fbe76c8b439ull, 0x3ffaa0378c06a614ull }, + { 0x401520c49ba5e354ull, 0x3ffaa0fe15a2b119ull }, + { 0x401521cac083126full, 0x3ffaa1c4959fa10bull }, + { 0x401522d0e5604189ull, 0x3ffaa28b0bfe649aull }, + { 0x401523d70a3d70a4ull, 0x3ffaa35178bfea56ull }, + { 0x401524dd2f1a9fbfull, 0x3ffaa417dbe520a8ull }, + { 0x401525e353f7ced9ull, 0x3ffaa4de356ef5d7ull }, + { 0x401526e978d4fdf4ull, 0x3ffaa5a4855e580cull }, + { 0x401527ef9db22d0eull, 0x3ffaa66acbb43547ull }, + { 0x401528f5c28f5c29ull, 0x3ffaa73108717b6aull }, + { 0x401529fbe76c8b44ull, 0x3ffaa7f73b971833ull }, + { 0x40152b020c49ba5eull, 0x3ffaa8bd6525f93cull }, + { 0x40152c083126e979ull, 0x3ffaa983851f0c01ull }, + { 0x40152d0e56041894ull, 0x3ffaaa499b833dd5ull }, + { 0x40152e147ae147aeull, 0x3ffaab0fa8537beeull }, + { 0x40152f1a9fbe76c9ull, 0x3ffaabd5ab90b35eull }, + { 0x40153020c49ba5e3ull, 0x3ffaac9ba53bd112ull }, + { 0x40153126e978d4feull, 0x3ffaad619555c1dbull }, + { 0x4015322d0e560419ull, 0x3ffaae277bdf7260ull }, + { 0x4015333333333333ull, 0x3ffaaeed58d9cf2aull }, + { 0x401534395810624eull, 0x3ffaafb32c45c4a1ull }, + { 0x4015353f7ced9169ull, 0x3ffab078f6243f07ull }, + { 0x40153645a1cac083ull, 0x3ffab13eb6762a7eull }, + { 0x4015374bc6a7ef9eull, 0x3ffab2046d3c7306ull }, + { 0x40153851eb851eb8ull, 0x3ffab2ca1a78047bull }, + { 0x4015395810624dd3ull, 0x3ffab38fbe29ca9aull }, + { 0x40153a5e353f7ceeull, 0x3ffab4555852b0fbull }, + { 0x40153b645a1cac08ull, 0x3ffab51ae8f3a314ull }, + { 0x40153c6a7ef9db23ull, 0x3ffab5e0700d8c3bull }, + { 0x40153d70a3d70a3eull, 0x3ffab6a5eda157a2ull }, + { 0x40153e76c8b43958ull, 0x3ffab76b61aff05aull }, + { 0x40153f7ced916873ull, 0x3ffab830cc3a4153ull }, + { 0x40154083126e978dull, 0x3ffab8f62d413558ull }, + { 0x40154189374bc6a8ull, 0x3ffab9bb84c5b715ull }, + { 0x4015428f5c28f5c3ull, 0x3ffaba80d2c8b113ull }, + { 0x40154395810624ddull, 0x3ffabb46174b0db8ull }, + { 0x4015449ba5e353f8ull, 0x3ffabc0b524db74bull }, + { 0x401545a1cac08313ull, 0x3ffabcd083d197eeull }, + { 0x401546a7ef9db22dull, 0x3ffabd95abd799a3ull }, + { 0x401547ae147ae148ull, 0x3ffabe5aca60a64aull }, + { 0x401548b439581062ull, 0x3ffabf1fdf6da7a0ull }, + { 0x401549ba5e353f7dull, 0x3ffabfe4eaff8742ull }, + { 0x40154ac083126e98ull, 0x3ffac0a9ed172eacull }, + { 0x40154bc6a7ef9db2ull, 0x3ffac16ee5b58734ull }, + { 0x40154ccccccccccdull, 0x3ffac233d4db7a14ull }, + { 0x40154dd2f1a9fbe8ull, 0x3ffac2f8ba89f061ull }, + { 0x40154ed916872b02ull, 0x3ffac3bd96c1d30dull }, + { 0x40154fdf3b645a1dull, 0x3ffac48269840aedull }, + { 0x401550e560418937ull, 0x3ffac54732d180b0ull }, + { 0x401551eb851eb852ull, 0x3ffac60bf2ab1ce7ull }, + { 0x401552f1a9fbe76dull, 0x3ffac6d0a911c7ffull }, + { 0x401553f7ced91687ull, 0x3ffac79556066a44ull }, + { 0x401554fdf3b645a2ull, 0x3ffac859f989ebe2ull }, + { 0x40155604189374bdull, 0x3ffac91e939d34e1ull }, + { 0x4015570a3d70a3d7ull, 0x3ffac9e324412d2aull }, + { 0x40155810624dd2f2ull, 0x3ffacaa7ab76bc84ull }, + { 0x40155916872b020cull, 0x3ffacb6c293eca92ull }, + { 0x40155a1cac083127ull, 0x3ffacc309d9a3edcull }, + { 0x40155b22d0e56042ull, 0x3ffaccf5088a00c2ull }, + { 0x40155c28f5c28f5cull, 0x3ffacdb96a0ef785ull }, + { 0x40155d2f1a9fbe77ull, 0x3fface7dc22a0a47ull }, + { 0x40155e353f7ced92ull, 0x3ffacf4210dc2006ull }, + { 0x40155f3b645a1cacull, 0x3ffad00656261f9full }, + { 0x4015604189374bc7ull, 0x3ffad0ca9208efd0ull }, + { 0x40156147ae147ae1ull, 0x3ffad18ec4857733ull }, + { 0x4015624dd2f1a9fcull, 0x3ffad252ed9c9c43ull }, + { 0x40156353f7ced917ull, 0x3ffad3170d4f4559ull }, + { 0x4015645a1cac0831ull, 0x3ffad3db239e58adull }, + { 0x401565604189374cull, 0x3ffad49f308abc57ull }, + { 0x4015666666666667ull, 0x3ffad5633415564cull }, + { 0x4015676c8b439581ull, 0x3ffad6272e3f0c60ull }, + { 0x40156872b020c49cull, 0x3ffad6eb1f08c448ull }, + { 0x40156978d4fdf3b6ull, 0x3ffad7af06736397ull }, + { 0x40156a7ef9db22d1ull, 0x3ffad872e47fcfc0ull }, + { 0x40156b851eb851ecull, 0x3ffad936b92eee13ull }, + { 0x40156c8b43958106ull, 0x3ffad9fa8481a3c0ull }, + { 0x40156d916872b021ull, 0x3ffadabe4678d5d7ull }, + { 0x40156e978d4fdf3cull, 0x3ffadb81ff156947ull }, + { 0x40156f9db22d0e56ull, 0x3ffadc45ae5842ddull }, + { 0x401570a3d70a3d71ull, 0x3ffadd0954424747ull }, + { 0x401571a9fbe76c8bull, 0x3ffaddccf0d45b0full }, + { 0x401572b020c49ba6ull, 0x3ffade90840f62a3ull }, + { 0x401573b645a1cac1ull, 0x3ffadf540df4424eull }, + { 0x401574bc6a7ef9dbull, 0x3ffae0178e83de37ull }, + { 0x401575c28f5c28f6ull, 0x3ffae0db05bf1a6aull }, + { 0x401576c8b4395811ull, 0x3ffae19e73a6dad0ull }, + { 0x401577ced916872bull, 0x3ffae261d83c032full }, + { 0x401578d4fdf3b646ull, 0x3ffae325337f7731ull }, + { 0x401579db22d0e560ull, 0x3ffae3e885721a5bull }, + { 0x40157ae147ae147bull, 0x3ffae4abce14d015ull }, + { 0x40157be76c8b4396ull, 0x3ffae56f0d687ba4ull }, + { 0x40157ced916872b0ull, 0x3ffae632436e002eull }, + { 0x40157df3b645a1cbull, 0x3ffae6f5702640b7ull }, + { 0x40157ef9db22d0e6ull, 0x3ffae7b893922026ull }, + { 0x4015800000000000ull, 0x3ffae87badb2813bull }, + { 0x4015810624dd2f1bull, 0x3ffae93ebe88469eull }, + { 0x4015820c49ba5e35ull, 0x3ffaea01c61452cfull }, + { 0x401583126e978d50ull, 0x3ffaeac4c4578834ull }, + { 0x401584189374bc6bull, 0x3ffaeb87b952c90dull }, + { 0x4015851eb851eb85ull, 0x3ffaec4aa506f77eull }, + { 0x40158624dd2f1aa0ull, 0x3ffaed0d8774f588ull }, + { 0x4015872b020c49baull, 0x3ffaedd0609da50eull }, + { 0x4015883126e978d5ull, 0x3ffaee933081e7d0ull }, + { 0x401589374bc6a7f0ull, 0x3ffaef55f7229f72ull }, + { 0x40158a3d70a3d70aull, 0x3ffaf018b480ad72ull }, + { 0x40158b4395810625ull, 0x3ffaf0db689cf334ull }, + { 0x40158c49ba5e3540ull, 0x3ffaf19e137851f7ull }, + { 0x40158d4fdf3b645aull, 0x3ffaf260b513aadcull }, + { 0x40158e5604189375ull, 0x3ffaf3234d6fdee4ull }, + { 0x40158f5c28f5c28full, 0x3ffaf3e5dc8dcef0ull }, + { 0x401590624dd2f1aaull, 0x3ffaf4a8626e5bc1ull }, + { 0x4015916872b020c5ull, 0x3ffaf56adf1265f6ull }, + { 0x4015926e978d4fdfull, 0x3ffaf62d527ace11ull }, + { 0x40159374bc6a7efaull, 0x3ffaf6efbca87473ull }, + { 0x4015947ae147ae15ull, 0x3ffaf7b21d9c395bull }, + { 0x401595810624dd2full, 0x3ffaf8747556fcebull }, + { 0x401596872b020c4aull, 0x3ffaf936c3d99f24ull }, + { 0x4015978d4fdf3b64ull, 0x3ffaf9f90924ffe6ull }, + { 0x4015989374bc6a7full, 0x3ffafabb4539fef3ull }, + { 0x401599999999999aull, 0x3ffafb7d78197bedull }, + { 0x40159a9fbe76c8b4ull, 0x3ffafc3fa1c45653ull }, + { 0x40159ba5e353f7cfull, 0x3ffafd01c23b6d8aull }, + { 0x40159cac083126eaull, 0x3ffafdc3d97fa0d2ull }, + { 0x40159db22d0e5604ull, 0x3ffafe85e791cf4dull }, + { 0x40159eb851eb851full, 0x3ffaff47ec72d7ffull }, + { 0x40159fbe76c8b439ull, 0x3ffb0009e82399caull }, + { 0x4015a0c49ba5e354ull, 0x3ffb00cbdaa4f372ull }, + { 0x4015a1cac083126full, 0x3ffb018dc3f7c399ull }, + { 0x4015a2d0e5604189ull, 0x3ffb024fa41ce8c4ull }, + { 0x4015a3d70a3d70a4ull, 0x3ffb03117b154158ull }, + { 0x4015a4dd2f1a9fbfull, 0x3ffb03d348e1ab99ull }, + { 0x4015a5e353f7ced9ull, 0x3ffb04950d8305abull }, + { 0x4015a6e978d4fdf4ull, 0x3ffb0556c8fa2d97ull }, + { 0x4015a7ef9db22d0eull, 0x3ffb06187b480140ull }, + { 0x4015a8f5c28f5c29ull, 0x3ffb06da246d5e6full }, + { 0x4015a9fbe76c8b44ull, 0x3ffb079bc46b22caull }, + { 0x4015ab020c49ba5eull, 0x3ffb085d5b422bd9ull }, + { 0x4015ac083126e979ull, 0x3ffb091ee8f35705ull }, + { 0x4015ad0e56041894ull, 0x3ffb09e06d7f8198ull }, + { 0x4015ae147ae147aeull, 0x3ffb0aa1e8e788b9ull }, + { 0x4015af1a9fbe76c9ull, 0x3ffb0b635b2c4975ull }, + { 0x4015b020c49ba5e3ull, 0x3ffb0c24c44ea0b5ull }, + { 0x4015b126e978d4feull, 0x3ffb0ce6244f6b48ull }, + { 0x4015b22d0e560419ull, 0x3ffb0da77b2f85d7ull }, + { 0x4015b33333333333ull, 0x3ffb0e68c8efccf1ull }, + { 0x4015b4395810624eull, 0x3ffb0f2a0d911d04ull }, + { 0x4015b53f7ced9169ull, 0x3ffb0feb4914525full }, + { 0x4015b645a1cac083ull, 0x3ffb10ac7b7a4931ull }, + { 0x4015b74bc6a7ef9eull, 0x3ffb116da4c3dd8cull }, + { 0x4015b851eb851eb8ull, 0x3ffb122ec4f1eb5full }, + { 0x4015b95810624dd3ull, 0x3ffb12efdc054e7full }, + { 0x4015ba5e353f7ceeull, 0x3ffb13b0e9fee29dull }, + { 0x4015bb645a1cac08ull, 0x3ffb1471eedf834full }, + { 0x4015bc6a7ef9db23ull, 0x3ffb1532eaa80c09ull }, + { 0x4015bd70a3d70a3eull, 0x3ffb15f3dd595822ull }, + { 0x4015be76c8b43958ull, 0x3ffb16b4c6f442cfull }, + { 0x4015bf7ced916873ull, 0x3ffb1775a779a72cull }, + { 0x4015c083126e978dull, 0x3ffb18367eea602eull }, + { 0x4015c189374bc6a8ull, 0x3ffb18f74d4748b3ull }, + { 0x4015c28f5c28f5c3ull, 0x3ffb19b812913b74ull }, + { 0x4015c395810624ddull, 0x3ffb1a78cec9130eull }, + { 0x4015c49ba5e353f8ull, 0x3ffb1b3981efa9ffull }, + { 0x4015c5a1cac08313ull, 0x3ffb1bfa2c05daa6ull }, + { 0x4015c6a7ef9db22dull, 0x3ffb1cbacd0c7f43ull }, + { 0x4015c7ae147ae148ull, 0x3ffb1d7b650471f7ull }, + { 0x4015c8b439581062ull, 0x3ffb1e3bf3ee8cc3ull }, + { 0x4015c9ba5e353f7dull, 0x3ffb1efc79cba98eull }, + { 0x4015cac083126e98ull, 0x3ffb1fbcf69ca21cull }, + { 0x4015cbc6a7ef9db2ull, 0x3ffb207d6a625011ull }, + { 0x4015cccccccccccdull, 0x3ffb213dd51d8cf7ull }, + { 0x4015cdd2f1a9fbe8ull, 0x3ffb21fe36cf3236ull }, + { 0x4015ced916872b02ull, 0x3ffb22be8f781918ull }, + { 0x4015cfdf3b645a1dull, 0x3ffb237edf191acbull }, + { 0x4015d0e560418937ull, 0x3ffb243f25b31059ull }, + { 0x4015d1eb851eb852ull, 0x3ffb24ff6346d2b3ull }, + { 0x4015d2f1a9fbe76dull, 0x3ffb25bf97d53aa9ull }, + { 0x4015d3f7ced91687ull, 0x3ffb267fc35f20ebull }, + { 0x4015d4fdf3b645a2ull, 0x3ffb273fe5e55e0dull }, + { 0x4015d604189374bdull, 0x3ffb27ffff68ca84ull }, + { 0x4015d70a3d70a3d7ull, 0x3ffb28c00fea3ea5ull }, + { 0x4015d810624dd2f2ull, 0x3ffb2980176a92aaull }, + { 0x4015d916872b020cull, 0x3ffb2a4015ea9ea9ull }, + { 0x4015da1cac083127ull, 0x3ffb2b000b6b3aa0ull }, + { 0x4015db22d0e56042ull, 0x3ffb2bbff7ed3e6aull }, + { 0x4015dc28f5c28f5cull, 0x3ffb2c7fdb7181c5ull }, + { 0x4015dd2f1a9fbe77ull, 0x3ffb2d3fb5f8dc52ull }, + { 0x4015de353f7ced92ull, 0x3ffb2dff87842592ull }, + { 0x4015df3b645a1cacull, 0x3ffb2ebf501434e8ull }, + { 0x4015e04189374bc7ull, 0x3ffb2f7f0fa9e19aull }, + { 0x4015e147ae147ae1ull, 0x3ffb303ec64602ceull }, + { 0x4015e24dd2f1a9fcull, 0x3ffb30fe73e96f8eull }, + { 0x4015e353f7ced917ull, 0x3ffb31be1894fec3ull }, + { 0x4015e45a1cac0831ull, 0x3ffb327db449873aull }, + { 0x4015e5604189374cull, 0x3ffb333d4707dfa2ull }, + { 0x4015e66666666667ull, 0x3ffb33fcd0d0de8aull }, + { 0x4015e76c8b439581ull, 0x3ffb34bc51a55a63ull }, + { 0x4015e872b020c49cull, 0x3ffb357bc9862983ull }, + { 0x4015e978d4fdf3b6ull, 0x3ffb363b3874221full }, + { 0x4015ea7ef9db22d1ull, 0x3ffb36fa9e701a4full }, + { 0x4015eb851eb851ecull, 0x3ffb37b9fb7ae80dull }, + { 0x4015ec8b43958106ull, 0x3ffb38794f956133ull }, + { 0x4015ed916872b021ull, 0x3ffb39389ac05b82ull }, + { 0x4015ee978d4fdf3cull, 0x3ffb39f7dcfcac98ull }, + { 0x4015ef9db22d0e56ull, 0x3ffb3ab7164b29f7ull }, + { 0x4015f0a3d70a3d71ull, 0x3ffb3b7646aca904ull }, + { 0x4015f1a9fbe76c8bull, 0x3ffb3c356e21ff04ull }, + { 0x4015f2b020c49ba6ull, 0x3ffb3cf48cac0120ull }, + { 0x4015f3b645a1cac1ull, 0x3ffb3db3a24b8464ull }, + { 0x4015f4bc6a7ef9dbull, 0x3ffb3e72af015dbaull }, + { 0x4015f5c28f5c28f6ull, 0x3ffb3f31b2ce61f3ull }, + { 0x4015f6c8b4395811ull, 0x3ffb3ff0adb365c0ull }, + { 0x4015f7ced916872bull, 0x3ffb40af9fb13db2ull }, + { 0x4015f8d4fdf3b646ull, 0x3ffb416e88c8be43ull }, + { 0x4015f9db22d0e560ull, 0x3ffb422d68fabbc7ull }, + { 0x4015fae147ae147bull, 0x3ffb42ec40480a7bull }, + { 0x4015fbe76c8b4396ull, 0x3ffb43ab0eb17e7bull }, + { 0x4015fced916872b0ull, 0x3ffb4469d437ebc5ull }, + { 0x4015fdf3b645a1cbull, 0x3ffb452890dc263cull }, + { 0x4015fef9db22d0e6ull, 0x3ffb45e7449f01a3ull }, + { 0x4016000000000000ull, 0x3ffb46a5ef8151a0ull }, + { 0x4016010624dd2f1bull, 0x3ffb47649183e9bcull }, + { 0x4016020c49ba5e35ull, 0x3ffb48232aa79d62ull }, + { 0x401603126e978d50ull, 0x3ffb48e1baed3fe0ull }, + { 0x401604189374bc6bull, 0x3ffb49a04255a467ull }, + { 0x4016051eb851eb85ull, 0x3ffb4a5ec0e19e07ull }, + { 0x40160624dd2f1aa0ull, 0x3ffb4b1d3691ffb8ull }, + { 0x4016072b020c49baull, 0x3ffb4bdba3679c51ull }, + { 0x4016083126e978d5ull, 0x3ffb4c9a0763468cull }, + { 0x401609374bc6a7f0ull, 0x3ffb4d586285d107ull }, + { 0x40160a3d70a3d70aull, 0x3ffb4e16b4d00e40ull }, + { 0x40160b4395810625ull, 0x3ffb4ed4fe42d09cull }, + { 0x40160c49ba5e3540ull, 0x3ffb4f933edeea5eull }, + { 0x40160d4fdf3b645aull, 0x3ffb505176a52dadull }, + { 0x40160e5604189375ull, 0x3ffb510fa5966c96ull }, + { 0x40160f5c28f5c28full, 0x3ffb51cdcbb37904ull }, + { 0x401610624dd2f1aaull, 0x3ffb528be8fd24caull }, + { 0x4016116872b020c5ull, 0x3ffb5349fd744197ull }, + { 0x4016126e978d4fdfull, 0x3ffb54080919a103ull }, + { 0x40161374bc6a7efaull, 0x3ffb54c60bee1486ull }, + { 0x4016147ae147ae15ull, 0x3ffb558405f26d7cull }, + { 0x401615810624dd2full, 0x3ffb5641f7277d21ull }, + { 0x401616872b020c4aull, 0x3ffb56ffdf8e1498ull }, + { 0x4016178d4fdf3b64ull, 0x3ffb57bdbf2704e4ull }, + { 0x4016189374bc6a7full, 0x3ffb587b95f31eedull }, + { 0x401619999999999aull, 0x3ffb593963f3337cull }, + { 0x40161a9fbe76c8b4ull, 0x3ffb59f72928133dull }, + { 0x40161ba5e353f7cfull, 0x3ffb5ab4e5928ec2ull }, + { 0x40161cac083126eaull, 0x3ffb5b729933767dull }, + { 0x40161db22d0e5604ull, 0x3ffb5c30440b9ac2ull }, + { 0x40161eb851eb851full, 0x3ffb5cede61bcbcdull }, + { 0x40161fbe76c8b439ull, 0x3ffb5dab7f64d9b9ull }, + { 0x401620c49ba5e354ull, 0x3ffb5e690fe79484ull }, + { 0x401621cac083126full, 0x3ffb5f2697a4cc12ull }, + { 0x401622d0e5604189ull, 0x3ffb5fe4169d5028ull }, + { 0x401623d70a3d70a4ull, 0x3ffb60a18cd1f06eull }, + { 0x401624dd2f1a9fbfull, 0x3ffb615efa437c70ull }, + { 0x401625e353f7ced9ull, 0x3ffb621c5ef2c39cull }, + { 0x401626e978d4fdf4ull, 0x3ffb62d9bae09547ull }, + { 0x401627ef9db22d0eull, 0x3ffb63970e0dc0a4ull }, + { 0x401628f5c28f5c29ull, 0x3ffb6454587b14cdull }, + { 0x401629fbe76c8b44ull, 0x3ffb65119a2960bfull }, + { 0x40162b020c49ba5eull, 0x3ffb65ced3197356ull }, + { 0x40162c083126e979ull, 0x3ffb668c034c1b58ull }, + { 0x40162d0e56041894ull, 0x3ffb67492ac2276aull }, + { 0x40162e147ae147aeull, 0x3ffb6806497c6615ull }, + { 0x40162f1a9fbe76c9ull, 0x3ffb68c35f7ba5c6ull }, + { 0x40163020c49ba5e3ull, 0x3ffb69806cc0b4ceull }, + { 0x40163126e978d4feull, 0x3ffb6a3d714c6160ull }, + { 0x4016322d0e560419ull, 0x3ffb6afa6d1f7993ull }, + { 0x4016333333333333ull, 0x3ffb6bb7603acb62ull }, + { 0x401634395810624eull, 0x3ffb6c744a9f24acull }, + { 0x4016353f7ced9169ull, 0x3ffb6d312c4d5331ull }, + { 0x40163645a1cac083ull, 0x3ffb6dee05462497ull }, + { 0x4016374bc6a7ef9eull, 0x3ffb6eaad58a6667ull }, + { 0x40163851eb851eb8ull, 0x3ffb6f679d1ae60dull }, + { 0x4016395810624dd3ull, 0x3ffb70245bf870d9ull }, + { 0x40163a5e353f7ceeull, 0x3ffb70e11223d3feull }, + { 0x40163b645a1cac08ull, 0x3ffb719dbf9ddc94ull }, + { 0x40163c6a7ef9db23ull, 0x3ffb725a64675796ull }, + { 0x40163d70a3d70a3eull, 0x3ffb7317008111e3ull }, + { 0x40163e76c8b43958ull, 0x3ffb73d393ebd83bull }, + { 0x40163f7ced916873ull, 0x3ffb74901ea87746ull }, + { 0x40164083126e978dull, 0x3ffb754ca0b7bb8dull }, + { 0x40164189374bc6a8ull, 0x3ffb76091a1a717eull }, + { 0x4016428f5c28f5c3ull, 0x3ffb76c58ad1656aull }, + { 0x40164395810624ddull, 0x3ffb7781f2dd6385ull }, + { 0x4016449ba5e353f8ull, 0x3ffb783e523f37eaull }, + { 0x401645a1cac08313ull, 0x3ffb78faa8f7ae95ull }, + { 0x401646a7ef9db22dull, 0x3ffb79b6f7079365ull }, + { 0x401647ae147ae148ull, 0x3ffb7a733c6fb221ull }, + { 0x401648b439581062ull, 0x3ffb7b2f7930d66full }, + { 0x401649ba5e353f7dull, 0x3ffb7bebad4bcbdeull }, + { 0x40164ac083126e98ull, 0x3ffb7ca7d8c15dddull }, + { 0x40164bc6a7ef9db2ull, 0x3ffb7d63fb9257bfull }, + { 0x40164ccccccccccdull, 0x3ffb7e2015bf84beull }, + { 0x40164dd2f1a9fbe8ull, 0x3ffb7edc2749aff5ull }, + { 0x40164ed916872b02ull, 0x3ffb7f983031a465ull }, + { 0x40164fdf3b645a1dull, 0x3ffb805430782cf3ull }, + { 0x401650e560418937ull, 0x3ffb8110281e1465ull }, + { 0x401651eb851eb852ull, 0x3ffb81cc1724256bull }, + { 0x401652f1a9fbe76dull, 0x3ffb8287fd8b2a93ull }, + { 0x401653f7ced91687ull, 0x3ffb8343db53ee52ull }, + { 0x401654fdf3b645a2ull, 0x3ffb83ffb07f3b01ull }, + { 0x40165604189374bdull, 0x3ffb84bb7d0ddadeull }, + { 0x4016570a3d70a3d7ull, 0x3ffb857741009809ull }, + { 0x40165810624dd2f2ull, 0x3ffb8632fc583c89ull }, + { 0x40165916872b020cull, 0x3ffb86eeaf159245ull }, + { 0x40165a1cac083127ull, 0x3ffb87aa5939630eull }, + { 0x40165b22d0e56042ull, 0x3ffb8865fac47894ull }, + { 0x40165c28f5c28f5cull, 0x3ffb892193b79c6eull }, + { 0x40165d2f1a9fbe77ull, 0x3ffb89dd24139817ull }, + { 0x40165e353f7ced92ull, 0x3ffb8a98abd934efull }, + { 0x40165f3b645a1cacull, 0x3ffb8b542b093c37ull }, + { 0x4016604189374bc7ull, 0x3ffb8c0fa1a47718ull }, + { 0x40166147ae147ae1ull, 0x3ffb8ccb0fabae9full }, + { 0x4016624dd2f1a9fcull, 0x3ffb8d86751fabbcull }, + { 0x40166353f7ced917ull, 0x3ffb8e41d2013745ull }, + { 0x4016645a1cac0831ull, 0x3ffb8efd265119f1ull }, + { 0x401665604189374cull, 0x3ffb8fb872101c60ull }, + { 0x4016666666666667ull, 0x3ffb9073b53f0714ull }, + { 0x4016676c8b439581ull, 0x3ffb912eefdea272ull }, + { 0x40166872b020c49cull, 0x3ffb91ea21efb6c9ull }, + { 0x40166978d4fdf3b6ull, 0x3ffb92a54b730c46ull }, + { 0x40166a7ef9db22d1ull, 0x3ffb93606c696b00ull }, + { 0x40166b851eb851ecull, 0x3ffb941b84d39aeeull }, + { 0x40166c8b43958106ull, 0x3ffb94d694b263efull }, + { 0x40166d916872b021ull, 0x3ffb95919c068dc4ull }, + { 0x40166e978d4fdf3cull, 0x3ffb964c9ad0e016ull }, + { 0x40166f9db22d0e56ull, 0x3ffb97079112226full }, + { 0x401670a3d70a3d71ull, 0x3ffb97c27ecb1c40ull }, + { 0x401671a9fbe76c8bull, 0x3ffb987d63fc94ddull }, + { 0x401672b020c49ba6ull, 0x3ffb993840a75382ull }, + { 0x401673b645a1cac1ull, 0x3ffb99f314cc1f4cull }, + { 0x401674bc6a7ef9dbull, 0x3ffb9aade06bbf3dull }, + { 0x401675c28f5c28f6ull, 0x3ffb9b68a386fa40ull }, + { 0x401676c8b4395811ull, 0x3ffb9c235e1e9720ull }, + { 0x401677ced916872bull, 0x3ffb9cde10335c8eull }, + { 0x401678d4fdf3b646ull, 0x3ffb9d98b9c61122ull }, + { 0x401679db22d0e560ull, 0x3ffb9e535ad77b56ull }, + { 0x40167ae147ae147bull, 0x3ffb9f0df368618bull }, + { 0x40167be76c8b4396ull, 0x3ffb9fc883798a07ull }, + { 0x40167ced916872b0ull, 0x3ffba0830b0bbaf2ull }, + { 0x40167df3b645a1cbull, 0x3ffba13d8a1fba5cull }, + { 0x40167ef9db22d0e6ull, 0x3ffba1f800b64e3aull }, + { 0x4016800000000000ull, 0x3ffba2b26ed03c62ull }, + { 0x4016810624dd2f1bull, 0x3ffba36cd46e4a94ull }, + { 0x4016820c49ba5e35ull, 0x3ffba42731913e72ull }, + { 0x401683126e978d50ull, 0x3ffba4e18639dd86ull }, + { 0x401684189374bc6bull, 0x3ffba59bd268ed3aull }, + { 0x4016851eb851eb85ull, 0x3ffba656161f32e1ull }, + { 0x40168624dd2f1aa0ull, 0x3ffba710515d73b3ull }, + { 0x4016872b020c49baull, 0x3ffba7ca842474cbull }, + { 0x4016883126e978d5ull, 0x3ffba884ae74fb2bull }, + { 0x401689374bc6a7f0ull, 0x3ffba93ed04fcbb9ull }, + { 0x40168a3d70a3d70aull, 0x3ffba9f8e9b5ab41ull }, + { 0x40168b4395810625ull, 0x3ffbaab2faa75e74ull }, + { 0x40168c49ba5e3540ull, 0x3ffbab6d0325a9e8ull }, + { 0x40168d4fdf3b645aull, 0x3ffbac2703315217ull }, + { 0x40168e5604189375ull, 0x3ffbace0facb1b63ull }, + { 0x40168f5c28f5c28full, 0x3ffbad9ae9f3ca10ull }, + { 0x401690624dd2f1aaull, 0x3ffbae54d0ac224bull }, + { 0x4016916872b020c5ull, 0x3ffbaf0eaef4e824ull }, + { 0x4016926e978d4fdfull, 0x3ffbafc884cedf8full }, + { 0x40169374bc6a7efaull, 0x3ffbb082523acc68ull }, + { 0x4016947ae147ae15ull, 0x3ffbb13c17397270ull }, + { 0x401695810624dd2full, 0x3ffbb1f5d3cb954bull }, + { 0x401696872b020c4aull, 0x3ffbb2af87f1f885ull }, + { 0x4016978d4fdf3b64ull, 0x3ffbb36933ad5f8eull }, + { 0x4016989374bc6a7full, 0x3ffbb422d6fe8dbdull }, + { 0x401699999999999aull, 0x3ffbb4dc71e6464dull }, + { 0x40169a9fbe76c8b4ull, 0x3ffbb59604654c5dull }, + { 0x40169ba5e353f7cfull, 0x3ffbb64f8e7c62f5ull }, + { 0x40169cac083126eaull, 0x3ffbb709102c4d01ull }, + { 0x40169db22d0e5604ull, 0x3ffbb7c28975cd51ull }, + { 0x40169eb851eb851full, 0x3ffbb87bfa59a69dull }, + { 0x40169fbe76c8b439ull, 0x3ffbb93562d89b80ull }, + { 0x4016a0c49ba5e354ull, 0x3ffbb9eec2f36e7eull }, + { 0x4016a1cac083126full, 0x3ffbbaa81aaae1fdull }, + { 0x4016a2d0e5604189ull, 0x3ffbbb6169ffb84aull }, + { 0x4016a3d70a3d70a4ull, 0x3ffbbc1ab0f2b399ull }, + { 0x4016a4dd2f1a9fbfull, 0x3ffbbcd3ef849603ull }, + { 0x4016a5e353f7ced9ull, 0x3ffbbd8d25b62183ull }, + { 0x4016a6e978d4fdf4ull, 0x3ffbbe4653881800ull }, + { 0x4016a7ef9db22d0eull, 0x3ffbbeff78fb3b42ull }, + { 0x4016a8f5c28f5c29ull, 0x3ffbbfb896104cf9ull }, + { 0x4016a9fbe76c8b44ull, 0x3ffbc071aac80ebaull }, + { 0x4016ab020c49ba5eull, 0x3ffbc12ab72341ffull }, + { 0x4016ac083126e979ull, 0x3ffbc1e3bb22a829ull }, + { 0x4016ad0e56041894ull, 0x3ffbc29cb6c7027full }, + { 0x4016ae147ae147aeull, 0x3ffbc355aa11122cull }, + { 0x4016af1a9fbe76c9ull, 0x3ffbc40e95019842ull }, + { 0x4016b020c49ba5e3ull, 0x3ffbc4c7779955baull }, + { 0x4016b126e978d4feull, 0x3ffbc58051d90b71ull }, + { 0x4016b22d0e560419ull, 0x3ffbc63923c17a2bull }, + { 0x4016b33333333333ull, 0x3ffbc6f1ed536290ull }, + { 0x4016b4395810624eull, 0x3ffbc7aaae8f8532ull }, + { 0x4016b53f7ced9169ull, 0x3ffbc8636776a286ull }, + { 0x4016b645a1cac083ull, 0x3ffbc91c18097ae5ull }, + { 0x4016b74bc6a7ef9eull, 0x3ffbc9d4c048ce92ull }, + { 0x4016b851eb851eb8ull, 0x3ffbca8d60355db4ull }, + { 0x4016b95810624dd3ull, 0x3ffbcb45f7cfe85aull }, + { 0x4016ba5e353f7ceeull, 0x3ffbcbfe87192e76ull }, + { 0x4016bb645a1cac08ull, 0x3ffbccb70e11efe0ull }, + { 0x4016bc6a7ef9db23ull, 0x3ffbcd6f8cbaec5aull }, + { 0x4016bd70a3d70a3eull, 0x3ffbce280314e38aull }, + { 0x4016be76c8b43958ull, 0x3ffbcee0712094f9ull }, + { 0x4016bf7ced916873ull, 0x3ffbcf98d6dec01bull }, + { 0x4016c083126e978dull, 0x3ffbd05134502447ull }, + { 0x4016c189374bc6a8ull, 0x3ffbd109897580bcull }, + { 0x4016c28f5c28f5c3ull, 0x3ffbd1c1d64f949full }, + { 0x4016c395810624ddull, 0x3ffbd27a1adf1ef8ull }, + { 0x4016c49ba5e353f8ull, 0x3ffbd3325724debbull }, + { 0x4016c5a1cac08313ull, 0x3ffbd3ea8b2192bdull }, + { 0x4016c6a7ef9db22dull, 0x3ffbd4a2b6d5f9bcull }, + { 0x4016c7ae147ae148ull, 0x3ffbd55ada42d25cull }, + { 0x4016c8b439581062ull, 0x3ffbd612f568db25ull }, + { 0x4016c9ba5e353f7dull, 0x3ffbd6cb0848d28aull }, + { 0x4016cac083126e98ull, 0x3ffbd78312e376e0ull }, + { 0x4016cbc6a7ef9db2ull, 0x3ffbd83b15398663ull }, + { 0x4016cccccccccccdull, 0x3ffbd8f30f4bbf39ull }, + { 0x4016cdd2f1a9fbe8ull, 0x3ffbd9ab011adf69ull }, + { 0x4016ced916872b02ull, 0x3ffbda62eaa7a4e3ull }, + { 0x4016cfdf3b645a1dull, 0x3ffbdb1acbf2cd80ull }, + { 0x4016d0e560418937ull, 0x3ffbdbd2a4fd16f9ull }, + { 0x4016d1eb851eb852ull, 0x3ffbdc8a75c73ef6ull }, + { 0x4016d2f1a9fbe76dull, 0x3ffbdd423e5202fdull }, + { 0x4016d3f7ced91687ull, 0x3ffbddf9fe9e2080ull }, + { 0x4016d4fdf3b645a2ull, 0x3ffbdeb1b6ac54d6ull }, + { 0x4016d604189374bdull, 0x3ffbdf69667d5d3cull }, + { 0x4016d70a3d70a3d7ull, 0x3ffbe0210e11f6d5ull }, + { 0x4016d810624dd2f2ull, 0x3ffbe0d8ad6adeacull }, + { 0x4016d916872b020cull, 0x3ffbe1904488d1b3ull }, + { 0x4016da1cac083127ull, 0x3ffbe247d36c8cc1ull }, + { 0x4016db22d0e56042ull, 0x3ffbe2ff5a16cc94ull }, + { 0x4016dc28f5c28f5cull, 0x3ffbe3b6d8884dd0ull }, + { 0x4016dd2f1a9fbe77ull, 0x3ffbe46e4ec1cd02ull }, + { 0x4016de353f7ced92ull, 0x3ffbe525bcc4069cull }, + { 0x4016df3b645a1cacull, 0x3ffbe5dd228fb6f7ull }, + { 0x4016e04189374bc7ull, 0x3ffbe69480259a52ull }, + { 0x4016e147ae147ae1ull, 0x3ffbe74bd5866cd2ull }, + { 0x4016e24dd2f1a9fcull, 0x3ffbe80322b2ea87ull }, + { 0x4016e353f7ced917ull, 0x3ffbe8ba67abcf64ull }, + { 0x4016e45a1cac0831ull, 0x3ffbe971a471d742ull }, + { 0x4016e5604189374cull, 0x3ffbea28d905bde3ull }, + { 0x4016e66666666667ull, 0x3ffbeae005683ef1ull }, + { 0x4016e76c8b439581ull, 0x3ffbeb97299a15f8ull }, + { 0x4016e872b020c49cull, 0x3ffbec4e459bfe70ull }, + { 0x4016e978d4fdf3b6ull, 0x3ffbed05596eb3b5ull }, + { 0x4016ea7ef9db22d1ull, 0x3ffbedbc6512f10bull }, + { 0x4016eb851eb851ecull, 0x3ffbee736889719dull }, + { 0x4016ec8b43958106ull, 0x3ffbef2a63d2f07aull }, + { 0x4016ed916872b021ull, 0x3ffbefe156f0289eull }, + { 0x4016ee978d4fdf3cull, 0x3ffbf09841e1d4e5ull }, + { 0x4016ef9db22d0e56ull, 0x3ffbf14f24a8b017ull }, + { 0x4016f0a3d70a3d71ull, 0x3ffbf205ff4574e1ull }, + { 0x4016f1a9fbe76c8bull, 0x3ffbf2bcd1b8ddd7ull }, + { 0x4016f2b020c49ba6ull, 0x3ffbf3739c03a574ull }, + { 0x4016f3b645a1cac1ull, 0x3ffbf42a5e26861cull }, + { 0x4016f4bc6a7ef9dbull, 0x3ffbf4e118223a16ull }, + { 0x4016f5c28f5c28f6ull, 0x3ffbf597c9f77b94ull }, + { 0x4016f6c8b4395811ull, 0x3ffbf64e73a704aeull }, + { 0x4016f7ced916872bull, 0x3ffbf70515318f61ull }, + { 0x4016f8d4fdf3b646ull, 0x3ffbf7bbae97d594ull }, + { 0x4016f9db22d0e560ull, 0x3ffbf8723fda9112ull }, + { 0x4016fae147ae147bull, 0x3ffbf928c8fa7b93ull }, + { 0x4016fbe76c8b4396ull, 0x3ffbf9df49f84eaeull }, + { 0x4016fced916872b0ull, 0x3ffbfa95c2d4c3e8ull }, + { 0x4016fdf3b645a1cbull, 0x3ffbfb4c339094aaull }, + { 0x4016fef9db22d0e6ull, 0x3ffbfc029c2c7a45ull }, + { 0x4017000000000000ull, 0x3ffbfcb8fca92df1ull }, + { 0x4017010624dd2f1bull, 0x3ffbfd6f550768cdull }, + { 0x4017020c49ba5e35ull, 0x3ffbfe25a547e3e0ull }, + { 0x401703126e978d50ull, 0x3ffbfedbed6b5819ull }, + { 0x401704189374bc6bull, 0x3ffbff922d727e4cull }, + { 0x4017051eb851eb85ull, 0x3ffc0048655e0f35ull }, + { 0x40170624dd2f1aa0ull, 0x3ffc00fe952ec37aull }, + { 0x4017072b020c49bbull, 0x3ffc01b4bce553a6ull }, + { 0x4017083126e978d5ull, 0x3ffc026adc82782aull }, + { 0x401709374bc6a7f0ull, 0x3ffc0320f406e961ull }, + { 0x40170a3d70a3d70aull, 0x3ffc03d703735f8dull }, + { 0x40170b4395810625ull, 0x3ffc048d0ac892d7ull }, + { 0x40170c49ba5e3540ull, 0x3ffc05430a073b4full }, + { 0x40170d4fdf3b645aull, 0x3ffc05f9013010ecull }, + { 0x40170e5604189375ull, 0x3ffc06aef043cb8full }, + { 0x40170f5c28f5c28full, 0x3ffc0764d74322feull }, + { 0x401710624dd2f1aaull, 0x3ffc081ab62ecee9ull }, + { 0x4017116872b020c5ull, 0x3ffc08d08d0786e4ull }, + { 0x4017126e978d4fdfull, 0x3ffc09865bce026dull }, + { 0x40171374bc6a7efaull, 0x3ffc0a3c2282f8e9ull }, + { 0x4017147ae147ae15ull, 0x3ffc0af1e12721a5ull }, + { 0x401715810624dd2full, 0x3ffc0ba797bb33d5ull }, + { 0x401716872b020c4aull, 0x3ffc0c5d463fe695ull }, + { 0x4017178d4fdf3b64ull, 0x3ffc0d12ecb5f0e9ull }, + { 0x4017189374bc6a7full, 0x3ffc0dc88b1e09bbull }, + { 0x401719999999999aull, 0x3ffc0e7e2178e7dfull }, + { 0x40171a9fbe76c8b4ull, 0x3ffc0f33afc7420full }, + { 0x40171ba5e353f7cfull, 0x3ffc0fe93609ceeeull }, + { 0x40171cac083126eaull, 0x3ffc109eb4414504ull }, + { 0x40171db22d0e5604ull, 0x3ffc11542a6e5ac5ull }, + { 0x40171eb851eb851full, 0x3ffc12099891c689ull }, + { 0x40171fbe76c8b439ull, 0x3ffc12befeac3e91ull }, + { 0x401720c49ba5e354ull, 0x3ffc13745cbe7908ull }, + { 0x401721cac083126full, 0x3ffc1429b2c92bfdull }, + { 0x401722d0e5604189ull, 0x3ffc14df00cd0d69ull }, + { 0x401723d70a3d70a4ull, 0x3ffc159446cad32dull }, + { 0x401724dd2f1a9fbfull, 0x3ffc164984c33312ull }, + { 0x401725e353f7ced9ull, 0x3ffc16febab6e2c5ull }, + { 0x401726e978d4fdf4ull, 0x3ffc17b3e8a697e2ull }, + { 0x401727ef9db22d0eull, 0x3ffc18690e9307e7ull }, + { 0x401728f5c28f5c29ull, 0x3ffc191e2c7ce83cull }, + { 0x401729fbe76c8b44ull, 0x3ffc19d34264ee32ull }, + { 0x40172b020c49ba5eull, 0x3ffc1a88504bceffull }, + { 0x40172c083126e979ull, 0x3ffc1b3d56323fc5ull }, + { 0x40172d0e56041894ull, 0x3ffc1bf25418f58bull }, + { 0x40172e147ae147aeull, 0x3ffc1ca74a00a53full }, + { 0x40172f1a9fbe76c9ull, 0x3ffc1d5c37ea03bbull }, + { 0x40173020c49ba5e3ull, 0x3ffc1e111dd5c5beull }, + { 0x40173126e978d4feull, 0x3ffc1ec5fbc49ff0ull }, + { 0x4017322d0e560419ull, 0x3ffc1f7ad1b746e2ull }, + { 0x4017333333333333ull, 0x3ffc202f9fae6f0aull }, + { 0x401734395810624eull, 0x3ffc20e465aacccaull }, + { 0x4017353f7ced9169ull, 0x3ffc219923ad146aull }, + { 0x40173645a1cac083ull, 0x3ffc224dd9b5fa19ull }, + { 0x4017374bc6a7ef9eull, 0x3ffc230287c631f2ull }, + { 0x40173851eb851eb8ull, 0x3ffc23b72dde6ff4ull }, + { 0x4017395810624dd3ull, 0x3ffc246bcbff6809ull }, + { 0x40173a5e353f7ceeull, 0x3ffc25206229ce02ull }, + { 0x40173b645a1cac08ull, 0x3ffc25d4f05e5597ull }, + { 0x40173c6a7ef9db23ull, 0x3ffc2689769db26bull }, + { 0x40173d70a3d70a3eull, 0x3ffc273df4e89808ull }, + { 0x40173e76c8b43958ull, 0x3ffc27f26b3fb9dfull }, + { 0x40173f7ced916873ull, 0x3ffc28a6d9a3cb4cull }, + { 0x40174083126e978dull, 0x3ffc295b40157f90ull }, + { 0x40174189374bc6a8ull, 0x3ffc2a0f9e9589d8ull }, + { 0x4017428f5c28f5c3ull, 0x3ffc2ac3f5249d37ull }, + { 0x40174395810624ddull, 0x3ffc2b7843c36ca6ull }, + { 0x4017449ba5e353f8ull, 0x3ffc2c2c8a72ab0dull }, + { 0x401745a1cac08313ull, 0x3ffc2ce0c9330b36ull }, + { 0x401746a7ef9db22dull, 0x3ffc2d9500053fd6ull }, + { 0x401747ae147ae148ull, 0x3ffc2e492ee9fb8cull }, + { 0x401748b439581062ull, 0x3ffc2efd55e1f0dcull }, + { 0x401749ba5e353f7dull, 0x3ffc2fb174edd236ull }, + { 0x40174ac083126e98ull, 0x3ffc30658c0e51efull }, + { 0x40174bc6a7ef9db2ull, 0x3ffc31199b442246ull }, + { 0x40174ccccccccccdull, 0x3ffc31cda28ff564ull }, + { 0x40174dd2f1a9fbe8ull, 0x3ffc3281a1f27d58ull }, + { 0x40174ed916872b02ull, 0x3ffc3335996c6c1bull }, + { 0x40174fdf3b645a1dull, 0x3ffc33e988fe738full }, + { 0x401750e560418937ull, 0x3ffc349d70a9457cull }, + { 0x401751eb851eb852ull, 0x3ffc3551506d9397ull }, + { 0x401752f1a9fbe76dull, 0x3ffc3605284c0f79ull }, + { 0x401753f7ced91687ull, 0x3ffc36b8f8456aa5ull }, + { 0x401754fdf3b645a2ull, 0x3ffc376cc05a5689ull }, + { 0x40175604189374bdull, 0x3ffc3820808b847aull }, + { 0x4017570a3d70a3d7ull, 0x3ffc38d438d9a5b3ull }, + { 0x40175810624dd2f2ull, 0x3ffc3987e9456b5cull }, + { 0x40175916872b020cull, 0x3ffc3a3b91cf8684ull }, + { 0x40175a1cac083127ull, 0x3ffc3aef3278a822ull }, + { 0x40175b22d0e56042ull, 0x3ffc3ba2cb418116ull }, + { 0x40175c28f5c28f5cull, 0x3ffc3c565c2ac229ull }, + { 0x40175d2f1a9fbe77ull, 0x3ffc3d09e5351c0full }, + { 0x40175e353f7ced92ull, 0x3ffc3dbd66613f5full }, + { 0x40175f3b645a1cacull, 0x3ffc3e70dfafdc9full }, + { 0x4017604189374bc7ull, 0x3ffc3f245121a43aull }, + { 0x40176147ae147ae1ull, 0x3ffc3fd7bab74684ull }, + { 0x4017624dd2f1a9fcull, 0x3ffc408b1c7173bdull }, + { 0x40176353f7ced917ull, 0x3ffc413e7650dc0bull }, + { 0x4017645a1cac0831ull, 0x3ffc41f1c8562f7bull }, + { 0x401765604189374cull, 0x3ffc42a512821e09ull }, + { 0x4017666666666667ull, 0x3ffc435854d55795ull }, + { 0x4017676c8b439581ull, 0x3ffc440b8f508be8ull }, + { 0x40176872b020c49cull, 0x3ffc44bec1f46ab6ull }, + { 0x40176978d4fdf3b6ull, 0x3ffc4571ecc1a39bull }, + { 0x40176a7ef9db22d1ull, 0x3ffc46250fb8e61cull }, + { 0x40176b851eb851ecull, 0x3ffc46d82adae1a7ull }, + { 0x40176c8b43958106ull, 0x3ffc478b3e284593ull }, + { 0x40176d916872b021ull, 0x3ffc483e49a1c120ull }, + { 0x40176e978d4fdf3cull, 0x3ffc48f14d480377ull }, + { 0x40176f9db22d0e56ull, 0x3ffc49a4491bbbaaull }, + { 0x401770a3d70a3d71ull, 0x3ffc4a573d1d98b4ull }, + { 0x401771a9fbe76c8bull, 0x3ffc4b0a294e4979ull }, + { 0x401772b020c49ba6ull, 0x3ffc4bbd0dae7cc7ull }, + { 0x401773b645a1cac1ull, 0x3ffc4c6fea3ee154ull }, + { 0x401774bc6a7ef9dbull, 0x3ffc4d22bf0025bfull }, + { 0x401775c28f5c28f6ull, 0x3ffc4dd58bf2f891ull }, + { 0x401776c8b4395811ull, 0x3ffc4e885118083cull }, + { 0x401777ced916872bull, 0x3ffc4f3b0e700319ull }, + { 0x401778d4fdf3b646ull, 0x3ffc4fedc3fb9770ull }, + { 0x401779db22d0e560ull, 0x3ffc50a071bb736aull }, + { 0x40177ae147ae147bull, 0x3ffc515317b04521ull }, + { 0x40177be76c8b4396ull, 0x3ffc5205b5daba92ull }, + { 0x40177ced916872b0ull, 0x3ffc52b84c3b81a8ull }, + { 0x40177df3b645a1cbull, 0x3ffc536adad34834ull }, + { 0x40177ef9db22d0e6ull, 0x3ffc541d61a2bbf2ull }, + { 0x4017800000000000ull, 0x3ffc54cfe0aa8a86ull }, + { 0x4017810624dd2f1bull, 0x3ffc558257eb617full }, + { 0x4017820c49ba5e35ull, 0x3ffc5634c765ee54ull }, + { 0x401783126e978d50ull, 0x3ffc56e72f1ade66ull }, + { 0x401784189374bc6bull, 0x3ffc57998f0adf00ull }, + { 0x4017851eb851eb85ull, 0x3ffc584be7369d52ull }, + { 0x40178624dd2f1aa0ull, 0x3ffc58fe379ec67dull }, + { 0x4017872b020c49bbull, 0x3ffc59b080440785ull }, + { 0x4017883126e978d5ull, 0x3ffc5a62c1270d59ull }, + { 0x401789374bc6a7f0ull, 0x3ffc5b14fa4884d4ull }, + { 0x40178a3d70a3d70aull, 0x3ffc5bc72ba91ab7ull }, + { 0x40178b4395810625ull, 0x3ffc5c7955497bb0ull }, + { 0x40178c49ba5e3540ull, 0x3ffc5d2b772a5452ull }, + { 0x40178d4fdf3b645aull, 0x3ffc5ddd914c511cull }, + { 0x40178e5604189375ull, 0x3ffc5e8fa3b01e78ull }, + { 0x40178f5c28f5c28full, 0x3ffc5f41ae5668b6ull }, + { 0x401790624dd2f1aaull, 0x3ffc5ff3b13fdc14ull }, + { 0x4017916872b020c5ull, 0x3ffc60a5ac6d24b6ull }, + { 0x4017926e978d4fdfull, 0x3ffc61579fdeeea9ull }, + { 0x40179374bc6a7efaull, 0x3ffc62098b95e5e7ull }, + { 0x4017947ae147ae15ull, 0x3ffc62bb6f92b652ull }, + { 0x401795810624dd2full, 0x3ffc636d4bd60bb3ull }, + { 0x401796872b020c4aull, 0x3ffc641f206091c1ull }, + { 0x4017978d4fdf3b64ull, 0x3ffc64d0ed32f419ull }, + { 0x4017989374bc6a7full, 0x3ffc6582b24dde46ull }, + { 0x401799999999999aull, 0x3ffc66346fb1fbb8ull }, + { 0x40179a9fbe76c8b4ull, 0x3ffc66e6255ff7caull }, + { 0x40179ba5e353f7cfull, 0x3ffc6797d3587dc3ull }, + { 0x40179cac083126eaull, 0x3ffc6849799c38d2ull }, + { 0x40179db22d0e5604ull, 0x3ffc68fb182bd40eull }, + { 0x40179eb851eb851full, 0x3ffc69acaf07fa7bull }, + { 0x40179fbe76c8b439ull, 0x3ffc6a5e3e315704ull }, + { 0x4017a0c49ba5e354ull, 0x3ffc6b0fc5a89481ull }, + { 0x4017a1cac083126full, 0x3ffc6bc1456e5db1ull }, + { 0x4017a2d0e5604189ull, 0x3ffc6c72bd835d3dull }, + { 0x4017a3d70a3d70a4ull, 0x3ffc6d242de83db9ull }, + { 0x4017a4dd2f1a9fbfull, 0x3ffc6dd5969da9a2ull }, + { 0x4017a5e353f7ced9ull, 0x3ffc6e86f7a44b5full }, + { 0x4017a6e978d4fdf4ull, 0x3ffc6f3850fccd42ull }, + { 0x4017a7ef9db22d0eull, 0x3ffc6fe9a2a7d985ull }, + { 0x4017a8f5c28f5c29ull, 0x3ffc709aeca61a4eull }, + { 0x4017a9fbe76c8b44ull, 0x3ffc714c2ef839aaull }, + { 0x4017ab020c49ba5eull, 0x3ffc71fd699ee192ull }, + { 0x4017ac083126e979ull, 0x3ffc72ae9c9abbeaull }, + { 0x4017ad0e56041894ull, 0x3ffc735fc7ec727eull }, + { 0x4017ae147ae147aeull, 0x3ffc7410eb94af03ull }, + { 0x4017af1a9fbe76c9ull, 0x3ffc74c207941b1bull }, + { 0x4017b020c49ba5e3ull, 0x3ffc75731beb6050ull }, + { 0x4017b126e978d4feull, 0x3ffc7624289b2816ull }, + { 0x4017b22d0e560419ull, 0x3ffc76d52da41bcdull }, + { 0x4017b33333333333ull, 0x3ffc77862b06e4bcull }, + { 0x4017b4395810624eull, 0x3ffc783720c42c17ull }, + { 0x4017b53f7ced9169ull, 0x3ffc78e80edc9afaull }, + { 0x4017b645a1cac083ull, 0x3ffc7998f550da6bull }, + { 0x4017b74bc6a7ef9eull, 0x3ffc7a49d421935dull }, + { 0x4017b851eb851eb8ull, 0x3ffc7afaab4f6ea8ull }, + { 0x4017b95810624dd3ull, 0x3ffc7bab7adb1514ull }, + { 0x4017ba5e353f7ceeull, 0x3ffc7c5c42c52f50ull }, + { 0x4017bb645a1cac08ull, 0x3ffc7d0d030e65f3ull }, + { 0x4017bc6a7ef9db23ull, 0x3ffc7dbdbbb76183ull }, + { 0x4017bd70a3d70a3eull, 0x3ffc7e6e6cc0ca6eull }, + { 0x4017be76c8b43958ull, 0x3ffc7f1f162b490aull }, + { 0x4017bf7ced916873ull, 0x3ffc7fcfb7f7859bull }, + { 0x4017c083126e978dull, 0x3ffc80805226284dull }, + { 0x4017c189374bc6a8ull, 0x3ffc8130e4b7d937ull }, + { 0x4017c28f5c28f5c3ull, 0x3ffc81e16fad4059ull }, + { 0x4017c395810624ddull, 0x3ffc8291f307059full }, + { 0x4017c49ba5e353f8ull, 0x3ffc83426ec5d0dfull }, + { 0x4017c5a1cac08313ull, 0x3ffc83f2e2ea49d9ull }, + { 0x4017c6a7ef9db22dull, 0x3ffc84a34f751836ull }, + { 0x4017c7ae147ae148ull, 0x3ffc8553b466e38bull }, + { 0x4017c8b439581062ull, 0x3ffc860411c05358ull }, + { 0x4017c9ba5e353f7dull, 0x3ffc86b467820f05ull }, + { 0x4017cac083126e98ull, 0x3ffc8764b5acbde7ull }, + { 0x4017cbc6a7ef9db2ull, 0x3ffc8814fc41073aull }, + { 0x4017cccccccccccdull, 0x3ffc88c53b3f9228ull }, + { 0x4017cdd2f1a9fbe8ull, 0x3ffc897572a905c5ull }, + { 0x4017ced916872b02ull, 0x3ffc8a25a27e090bull }, + { 0x4017cfdf3b645a1dull, 0x3ffc8ad5cabf42e5ull }, + { 0x4017d0e560418937ull, 0x3ffc8b85eb6d5a23ull }, + { 0x4017d1eb851eb852ull, 0x3ffc8c360488f584ull }, + { 0x4017d2f1a9fbe76dull, 0x3ffc8ce61612bbadull }, + { 0x4017d3f7ced91687ull, 0x3ffc8d96200b5331ull }, + { 0x4017d4fdf3b645a2ull, 0x3ffc8e462273628cull }, + { 0x4017d604189374bdull, 0x3ffc8ef61d4b9025ull }, + { 0x4017d70a3d70a3d7ull, 0x3ffc8fa61094824dull }, + { 0x4017d810624dd2f2ull, 0x3ffc9055fc4edf41ull }, + { 0x4017d916872b020cull, 0x3ffc9105e07b4d25ull }, + { 0x4017da1cac083127ull, 0x3ffc91b5bd1a720dull }, + { 0x4017db22d0e56042ull, 0x3ffc9265922cf3f4ull }, + { 0x4017dc28f5c28f5cull, 0x3ffc93155fb378bfull }, + { 0x4017dd2f1a9fbe77ull, 0x3ffc93c525aea641ull }, + { 0x4017de353f7ced92ull, 0x3ffc9474e41f2235ull }, + { 0x4017df3b645a1cacull, 0x3ffc95249b059240ull }, + { 0x4017e04189374bc7ull, 0x3ffc95d44a629bf4ull }, + { 0x4017e147ae147ae1ull, 0x3ffc9683f236e4cdull }, + { 0x4017e24dd2f1a9fcull, 0x3ffc973392831232ull }, + { 0x4017e353f7ced917ull, 0x3ffc97e32b47c973ull }, + { 0x4017e45a1cac0831ull, 0x3ffc9892bc85afcbull }, + { 0x4017e5604189374cull, 0x3ffc9942463d6a64ull }, + { 0x4017e66666666667ull, 0x3ffc99f1c86f9e4dull }, + { 0x4017e76c8b439581ull, 0x3ffc9aa1431cf081ull }, + { 0x4017e872b020c49cull, 0x3ffc9b50b64605ebull }, + { 0x4017e978d4fdf3b6ull, 0x3ffc9c0021eb8359ull }, + { 0x4017ea7ef9db22d1ull, 0x3ffc9caf860e0d89ull }, + { 0x4017eb851eb851ecull, 0x3ffc9d5ee2ae4924ull }, + { 0x4017ec8b43958106ull, 0x3ffc9e0e37ccdabaull }, + { 0x4017ed916872b021ull, 0x3ffc9ebd856a66caull }, + { 0x4017ee978d4fdf3cull, 0x3ffc9f6ccb8791bbull }, + { 0x4017ef9db22d0e56ull, 0x3ffca01c0a24ffe0ull }, + { 0x4017f0a3d70a3d71ull, 0x3ffca0cb41435578ull }, + { 0x4017f1a9fbe76c8bull, 0x3ffca17a70e336abull }, + { 0x4017f2b020c49ba6ull, 0x3ffca2299905478dull }, + { 0x4017f3b645a1cac1ull, 0x3ffca2d8b9aa2c1dull }, + { 0x4017f4bc6a7ef9dbull, 0x3ffca387d2d28843ull }, + { 0x4017f5c28f5c28f6ull, 0x3ffca436e47effd7ull }, + { 0x4017f6c8b4395811ull, 0x3ffca4e5eeb03696ull }, + { 0x4017f7ced916872bull, 0x3ffca594f166d02bull }, + { 0x4017f8d4fdf3b646ull, 0x3ffca643eca3702dull }, + { 0x4017f9db22d0e560ull, 0x3ffca6f2e066ba1bull }, + { 0x4017fae147ae147bull, 0x3ffca7a1ccb15163ull }, + { 0x4017fbe76c8b4396ull, 0x3ffca850b183d95bull }, + { 0x4017fced916872b0ull, 0x3ffca8ff8edef543ull }, + { 0x4017fdf3b645a1cbull, 0x3ffca9ae64c3484bull }, + { 0x4017fef9db22d0e6ull, 0x3ffcaa5d3331758aull }, + { 0x4018000000000000ull, 0x3ffcab0bfa2a2002ull }, + { 0x4018010624dd2f1bull, 0x3ffcabbab9adeaa4ull }, + { 0x4018020c49ba5e35ull, 0x3ffcac6971bd7847ull }, + { 0x401803126e978d50ull, 0x3ffcad1822596bb2ull }, + { 0x401804189374bc6bull, 0x3ffcadc6cb826793ull }, + { 0x4018051eb851eb85ull, 0x3ffcae756d390e86ull }, + { 0x40180624dd2f1aa0ull, 0x3ffcaf24077e0312ull }, + { 0x4018072b020c49bbull, 0x3ffcafd29a51e7a8ull }, + { 0x4018083126e978d5ull, 0x3ffcb08125b55ea3ull }, + { 0x401809374bc6a7f0ull, 0x3ffcb12fa9a90a4eull }, + { 0x40180a3d70a3d70aull, 0x3ffcb1de262d8cdaull }, + { 0x40180b4395810625ull, 0x3ffcb28c9b438867ull }, + { 0x40180c49ba5e3540ull, 0x3ffcb33b08eb9efeull }, + { 0x40180d4fdf3b645aull, 0x3ffcb3e96f267294ull }, + { 0x40180e5604189375ull, 0x3ffcb497cdf4a50bull }, + { 0x40180f5c28f5c290ull, 0x3ffcb5462556d82dull }, + { 0x401810624dd2f1aaull, 0x3ffcb5f4754dadb1ull }, + { 0x4018116872b020c5ull, 0x3ffcb6a2bdd9c73bull }, + { 0x4018126e978d4fdfull, 0x3ffcb750fefbc657ull }, + { 0x40181374bc6a7efaull, 0x3ffcb7ff38b44c81ull }, + { 0x4018147ae147ae15ull, 0x3ffcb8ad6b03fb1bull }, + { 0x401815810624dd2full, 0x3ffcb95b95eb7376ull }, + { 0x401816872b020c4aull, 0x3ffcba09b96b56ceull }, + { 0x4018178d4fdf3b64ull, 0x3ffcbab7d5844649ull }, + { 0x4018189374bc6a7full, 0x3ffcbb65ea36e2faull }, + { 0x401819999999999aull, 0x3ffcbc13f783cddfull }, + { 0x40181a9fbe76c8b4ull, 0x3ffcbcc1fd6ba7e0ull }, + { 0x40181ba5e353f7cfull, 0x3ffcbd6ffbef11d4ull }, + { 0x40181cac083126eaull, 0x3ffcbe1df30eac79ull }, + { 0x40181db22d0e5604ull, 0x3ffcbecbe2cb187cull }, + { 0x40181eb851eb851full, 0x3ffcbf79cb24f675ull }, + { 0x40181fbe76c8b439ull, 0x3ffcc027ac1ce6e5ull }, + { 0x401820c49ba5e354ull, 0x3ffcc0d585b38a3dull }, + { 0x401821cac083126full, 0x3ffcc18357e980d5ull }, + { 0x401822d0e5604189ull, 0x3ffcc23122bf6af3ull }, + { 0x401823d70a3d70a4ull, 0x3ffcc2dee635e8caull }, + { 0x401824dd2f1a9fbfull, 0x3ffcc38ca24d9a75ull }, + { 0x401825e353f7ced9ull, 0x3ffcc43a57071ffcull }, + { 0x401826e978d4fdf4ull, 0x3ffcc4e804631955ull }, + { 0x401827ef9db22d0eull, 0x3ffcc595aa62265full }, + { 0x401828f5c28f5c29ull, 0x3ffcc6434904e6e5ull }, + { 0x401829fbe76c8b44ull, 0x3ffcc6f0e04bfa9full }, + { 0x40182b020c49ba5eull, 0x3ffcc79e7038012full }, + { 0x40182c083126e979ull, 0x3ffcc84bf8c99a25ull }, + { 0x40182d0e56041894ull, 0x3ffcc8f97a0164f9ull }, + { 0x40182e147ae147aeull, 0x3ffcc9a6f3e00113ull }, + { 0x40182f1a9fbe76c9ull, 0x3ffcca5466660dc4ull }, + { 0x40183020c49ba5e3ull, 0x3ffccb01d1942a49ull }, + { 0x40183126e978d4feull, 0x3ffccbaf356af5ccull }, + { 0x4018322d0e560419ull, 0x3ffccc5c91eb0f62ull }, + { 0x4018333333333333ull, 0x3ffccd09e715160cull }, + { 0x401834395810624eull, 0x3ffccdb734e9a8b7ull }, + { 0x4018353f7ced9169ull, 0x3ffcce647b69663bull }, + { 0x40183645a1cac083ull, 0x3ffccf11ba94ed5cull }, + { 0x4018374bc6a7ef9eull, 0x3ffccfbef26cdccbull }, + { 0x40183851eb851eb8ull, 0x3ffcd06c22f1d324ull }, + { 0x4018395810624dd3ull, 0x3ffcd1194c246eefull }, + { 0x40183a5e353f7ceeull, 0x3ffcd1c66e054ea0ull }, + { 0x40183b645a1cac08ull, 0x3ffcd27388951096ull }, + { 0x40183c6a7ef9db23ull, 0x3ffcd3209bd4531eull }, + { 0x40183d70a3d70a3eull, 0x3ffcd3cda7c3b46full }, + { 0x40183e76c8b43958ull, 0x3ffcd47aac63d2adull }, + { 0x40183f7ced916873ull, 0x3ffcd527a9b54be7ull }, + { 0x40184083126e978dull, 0x3ffcd5d49fb8be18ull }, + { 0x40184189374bc6a8ull, 0x3ffcd6818e6ec72aull }, + { 0x4018428f5c28f5c3ull, 0x3ffcd72e75d804eeull }, + { 0x40184395810624ddull, 0x3ffcd7db55f51524ull }, + { 0x4018449ba5e353f8ull, 0x3ffcd8882ec69579ull }, + { 0x401845a1cac08313ull, 0x3ffcd935004d2384ull }, + { 0x401846a7ef9db22dull, 0x3ffcd9e1ca895cc8ull }, + { 0x401847ae147ae148ull, 0x3ffcda8e8d7bdeb6ull }, + { 0x401848b439581062ull, 0x3ffcdb3b492546a8ull }, + { 0x401849ba5e353f7dull, 0x3ffcdbe7fd8631e8ull }, + { 0x40184ac083126e98ull, 0x3ffcdc94aa9f3da9ull }, + { 0x40184bc6a7ef9db2ull, 0x3ffcdd415071070bull }, + { 0x40184ccccccccccdull, 0x3ffcddedeefc2b1bull }, + { 0x40184dd2f1a9fbe8ull, 0x3ffcde9a864146d1ull }, + { 0x40184ed916872b02ull, 0x3ffcdf471640f711ull }, + { 0x40184fdf3b645a1dull, 0x3ffcdff39efbd8aeull }, + { 0x401850e560418937ull, 0x3ffce0a020728862ull }, + { 0x401851eb851eb852ull, 0x3ffce14c9aa5a2d9ull }, + { 0x401852f1a9fbe76dull, 0x3ffce1f90d95c4a7ull }, + { 0x401853f7ced91687ull, 0x3ffce2a579438a4dull }, + { 0x401854fdf3b645a2ull, 0x3ffce351ddaf903aull }, + { 0x40185604189374bdull, 0x3ffce3fe3ada72c8ull }, + { 0x4018570a3d70a3d7ull, 0x3ffce4aa90c4ce3cull }, + { 0x40185810624dd2f2ull, 0x3ffce556df6f3ecbull }, + { 0x40185916872b020cull, 0x3ffce60326da6091ull }, + { 0x40185a1cac083127ull, 0x3ffce6af6706cf9dull }, + { 0x40185b22d0e56042ull, 0x3ffce75b9ff527e4ull }, + { 0x40185c28f5c28f5cull, 0x3ffce807d1a60549ull }, + { 0x40185d2f1a9fbe77ull, 0x3ffce8b3fc1a039full }, + { 0x40185e353f7ced92ull, 0x3ffce9601f51bea1ull }, + { 0x40185f3b645a1cacull, 0x3ffcea0c3b4dd1f8ull }, + { 0x4018604189374bc7ull, 0x3ffceab8500ed93aull }, + { 0x40186147ae147ae1ull, 0x3ffceb645d956fe8ull }, + { 0x4018624dd2f1a9fcull, 0x3ffcec1063e23170ull }, + { 0x40186353f7ced917ull, 0x3ffcecbc62f5b92eull }, + { 0x4018645a1cac0831ull, 0x3ffced685ad0a267ull }, + { 0x401865604189374cull, 0x3ffcee144b738850ull }, + { 0x4018666666666667ull, 0x3ffceec034df0609ull }, + { 0x4018676c8b439581ull, 0x3ffcef6c1713b69dull }, + { 0x40186872b020c49cull, 0x3ffcf017f2123505ull }, + { 0x40186978d4fdf3b6ull, 0x3ffcf0c3c5db1c28ull }, + { 0x40186a7ef9db22d1ull, 0x3ffcf16f926f06d6ull }, + { 0x40186b851eb851ecull, 0x3ffcf21b57ce8fcfull }, + { 0x40186c8b43958106ull, 0x3ffcf2c715fa51bcull }, + { 0x40186d916872b021ull, 0x3ffcf372ccf2e737ull }, + { 0x40186e978d4fdf3cull, 0x3ffcf41e7cb8eac2ull }, + { 0x40186f9db22d0e56ull, 0x3ffcf4ca254cf6ceull }, + { 0x401870a3d70a3d71ull, 0x3ffcf575c6afa5b9ull }, + { 0x401871a9fbe76c8bull, 0x3ffcf62160e191cbull }, + { 0x401872b020c49ba6ull, 0x3ffcf6ccf3e3553dull }, + { 0x401873b645a1cac1ull, 0x3ffcf7787fb58a30ull }, + { 0x401874bc6a7ef9dbull, 0x3ffcf8240458cab4ull }, + { 0x401875c28f5c28f6ull, 0x3ffcf8cf81cdb0c6ull }, + { 0x401876c8b4395811ull, 0x3ffcf97af814d64full }, + { 0x401877ced916872bull, 0x3ffcfa26672ed524ull }, + { 0x401878d4fdf3b646ull, 0x3ffcfad1cf1c4707ull }, + { 0x401879db22d0e560ull, 0x3ffcfb7d2fddc5a8ull }, + { 0x40187ae147ae147bull, 0x3ffcfc288973eaa2ull }, + { 0x40187be76c8b4396ull, 0x3ffcfcd3dbdf4f7eull }, + { 0x40187ced916872b0ull, 0x3ffcfd7f27208db1ull }, + { 0x40187df3b645a1cbull, 0x3ffcfe2a6b383e9dull }, + { 0x40187ef9db22d0e6ull, 0x3ffcfed5a826fb91ull }, + { 0x4018800000000000ull, 0x3ffcff80dded5dc8ull }, + { 0x4018810624dd2f1bull, 0x3ffd002c0c8bfe6bull }, + { 0x4018820c49ba5e35ull, 0x3ffd00d73403768dull }, + { 0x401883126e978d50ull, 0x3ffd018254545f33ull }, + { 0x401884189374bc6bull, 0x3ffd022d6d7f514aull }, + { 0x4018851eb851eb85ull, 0x3ffd02d87f84e5adull }, + { 0x40188624dd2f1aa0ull, 0x3ffd03838a65b526ull }, + { 0x4018872b020c49bbull, 0x3ffd042e8e225869ull }, + { 0x4018883126e978d5ull, 0x3ffd04d98abb6819ull }, + { 0x401889374bc6a7f0ull, 0x3ffd058480317cc4ull }, + { 0x40188a3d70a3d70aull, 0x3ffd062f6e852ee7ull }, + { 0x40188b4395810625ull, 0x3ffd06da55b716ebull }, + { 0x40188c49ba5e3540ull, 0x3ffd078535c7cd25ull }, + { 0x40188d4fdf3b645aull, 0x3ffd08300eb7e9d7ull }, + { 0x40188e5604189375ull, 0x3ffd08dae0880533ull }, + { 0x40188f5c28f5c290ull, 0x3ffd0985ab38b753ull }, + { 0x401890624dd2f1aaull, 0x3ffd0a306eca9842ull }, + { 0x4018916872b020c5ull, 0x3ffd0adb2b3e3ff8ull }, + { 0x4018926e978d4fdfull, 0x3ffd0b85e0944655ull }, + { 0x40189374bc6a7efaull, 0x3ffd0c308ecd432dull }, + { 0x4018947ae147ae15ull, 0x3ffd0cdb35e9ce3dull }, + { 0x401895810624dd2full, 0x3ffd0d85d5ea7f2dull }, + { 0x401896872b020c4aull, 0x3ffd0e306ecfed98ull }, + { 0x4018978d4fdf3b64ull, 0x3ffd0edb009ab101ull }, + { 0x4018989374bc6a7full, 0x3ffd0f858b4b60dbull }, + { 0x401899999999999aull, 0x3ffd10300ee29484ull }, + { 0x40189a9fbe76c8b4ull, 0x3ffd10da8b60e347ull }, + { 0x40189ba5e353f7cfull, 0x3ffd118500c6e460ull }, + { 0x40189cac083126eaull, 0x3ffd122f6f152ef4ull }, + { 0x40189db22d0e5604ull, 0x3ffd12d9d64c5a15ull }, + { 0x40189eb851eb851full, 0x3ffd1384366cfcc5ull }, + { 0x40189fbe76c8b439ull, 0x3ffd142e8f77adf1ull }, + { 0x4018a0c49ba5e354ull, 0x3ffd14d8e16d0475ull }, + { 0x4018a1cac083126full, 0x3ffd15832c4d9719ull }, + { 0x4018a2d0e5604189ull, 0x3ffd162d7019fc90ull }, + { 0x4018a3d70a3d70a4ull, 0x3ffd16d7acd2cb80ull }, + { 0x4018a4dd2f1a9fbfull, 0x3ffd1781e2789a76ull }, + { 0x4018a5e353f7ced9ull, 0x3ffd182c110bffefull }, + { 0x4018a6e978d4fdf4ull, 0x3ffd18d6388d9257ull }, + { 0x4018a7ef9db22d0eull, 0x3ffd198058fde804ull }, + { 0x4018a8f5c28f5c29ull, 0x3ffd1a2a725d973bull }, + { 0x4018a9fbe76c8b44ull, 0x3ffd1ad484ad362eull }, + { 0x4018ab020c49ba5eull, 0x3ffd1b7e8fed5afcull }, + { 0x4018ac083126e979ull, 0x3ffd1c28941e9bb3ull }, + { 0x4018ad0e56041894ull, 0x3ffd1cd291418e4cull }, + { 0x4018ae147ae147aeull, 0x3ffd1d7c8756c8adull }, + { 0x4018af1a9fbe76c9ull, 0x3ffd1e26765ee0acull }, + { 0x4018b020c49ba5e3ull, 0x3ffd1ed05e5a6c0bull }, + { 0x4018b126e978d4feull, 0x3ffd1f7a3f4a0078ull }, + { 0x4018b22d0e560419ull, 0x3ffd2024192e3392ull }, + { 0x4018b33333333333ull, 0x3ffd20cdec079ae1ull }, + { 0x4018b4395810624eull, 0x3ffd2177b7d6cbddull }, + { 0x4018b53f7ced9169ull, 0x3ffd22217c9c5bebull }, + { 0x4018b645a1cac083ull, 0x3ffd22cb3a58e05dull }, + { 0x4018b74bc6a7ef9eull, 0x3ffd2374f10cee74ull }, + { 0x4018b851eb851eb8ull, 0x3ffd241ea0b91b5cull }, + { 0x4018b95810624dd3ull, 0x3ffd24c8495dfc30ull }, + { 0x4018ba5e353f7ceeull, 0x3ffd2571eafc25f8ull }, + { 0x4018bb645a1cac08ull, 0x3ffd261b85942da9ull }, + { 0x4018bc6a7ef9db23ull, 0x3ffd26c51926a828ull }, + { 0x4018bd70a3d70a3eull, 0x3ffd276ea5b42a44ull }, + { 0x4018be76c8b43958ull, 0x3ffd28182b3d48bcull }, + { 0x4018bf7ced916873ull, 0x3ffd28c1a9c2983bull }, + { 0x4018c083126e978dull, 0x3ffd296b2144ad5bull }, + { 0x4018c189374bc6a8ull, 0x3ffd2a1491c41ca2ull }, + { 0x4018c28f5c28f5c3ull, 0x3ffd2abdfb417a86ull }, + { 0x4018c395810624ddull, 0x3ffd2b675dbd5b66ull }, + { 0x4018c49ba5e353f8ull, 0x3ffd2c10b9385395ull }, + { 0x4018c5a1cac08313ull, 0x3ffd2cba0db2f74eull }, + { 0x4018c6a7ef9db22dull, 0x3ffd2d635b2ddabbull }, + { 0x4018c7ae147ae148ull, 0x3ffd2e0ca1a991f7ull }, + { 0x4018c8b439581062ull, 0x3ffd2eb5e126b106ull }, + { 0x4018c9ba5e353f7dull, 0x3ffd2f5f19a5cbddull }, + { 0x4018cac083126e98ull, 0x3ffd30084b27765dull }, + { 0x4018cbc6a7ef9db2ull, 0x3ffd30b175ac4454ull }, + { 0x4018cccccccccccdull, 0x3ffd315a9934c981ull }, + { 0x4018cdd2f1a9fbe8ull, 0x3ffd3203b5c1998cull }, + { 0x4018ced916872b02ull, 0x3ffd32accb53480eull }, + { 0x4018cfdf3b645a1dull, 0x3ffd3355d9ea688dull }, + { 0x4018d0e560418937ull, 0x3ffd33fee1878e7dull }, + { 0x4018d1eb851eb852ull, 0x3ffd34a7e22b4d3full }, + { 0x4018d2f1a9fbe76dull, 0x3ffd3550dbd63822ull }, + { 0x4018d3f7ced91687ull, 0x3ffd35f9ce88e263ull }, + { 0x4018d4fdf3b645a2ull, 0x3ffd36a2ba43df2dull }, + { 0x4018d604189374bdull, 0x3ffd374b9f07c198ull }, + { 0x4018d70a3d70a3d7ull, 0x3ffd37f47cd51caaull }, + { 0x4018d810624dd2f2ull, 0x3ffd389d53ac8358ull }, + { 0x4018d916872b020cull, 0x3ffd3946238e8883ull }, + { 0x4018da1cac083127ull, 0x3ffd39eeec7bbefcull }, + { 0x4018db22d0e56042ull, 0x3ffd3a97ae74b97full }, + { 0x4018dc28f5c28f5cull, 0x3ffd3b40697a0ab9ull }, + { 0x4018dd2f1a9fbe77ull, 0x3ffd3be91d8c4542ull }, + { 0x4018de353f7ced92ull, 0x3ffd3c91caabfba3ull }, + { 0x4018df3b645a1cacull, 0x3ffd3d3a70d9c04full }, + { 0x4018e04189374bc7ull, 0x3ffd3de3101625abull }, + { 0x4018e147ae147ae1ull, 0x3ffd3e8ba861be06ull }, + { 0x4018e24dd2f1a9fcull, 0x3ffd3f3439bd1ba1ull }, + { 0x4018e353f7ced917ull, 0x3ffd3fdcc428d0a9ull }, + { 0x4018e45a1cac0831ull, 0x3ffd408547a56f36ull }, + { 0x4018e5604189374cull, 0x3ffd412dc4338953ull }, + { 0x4018e66666666667ull, 0x3ffd41d639d3b0f7ull }, + { 0x4018e76c8b439581ull, 0x3ffd427ea8867806ull }, + { 0x4018e872b020c49cull, 0x3ffd4327104c7053ull }, + { 0x4018e978d4fdf3b6ull, 0x3ffd43cf71262b9eull }, + { 0x4018ea7ef9db22d1ull, 0x3ffd4477cb143b97ull }, + { 0x4018eb851eb851ecull, 0x3ffd45201e1731daull }, + { 0x4018ec8b43958106ull, 0x3ffd45c86a2f9ff3ull }, + { 0x4018ed916872b021ull, 0x3ffd4670af5e175aull }, + { 0x4018ee978d4fdf3cull, 0x3ffd4718eda32977ull }, + { 0x4018ef9db22d0e56ull, 0x3ffd47c124ff679dull }, + { 0x4018f0a3d70a3d71ull, 0x3ffd486955736312ull }, + { 0x4018f1a9fbe76c8bull, 0x3ffd49117effad06ull }, + { 0x4018f2b020c49ba6ull, 0x3ffd49b9a1a4d698ull }, + { 0x4018f3b645a1cac1ull, 0x3ffd4a61bd6370d7ull }, + { 0x4018f4bc6a7ef9dbull, 0x3ffd4b09d23c0cbdull }, + { 0x4018f5c28f5c28f6ull, 0x3ffd4bb1e02f3b35ull }, + { 0x4018f6c8b4395811ull, 0x3ffd4c59e73d8d17ull }, + { 0x4018f7ced916872bull, 0x3ffd4d01e7679329ull }, + { 0x4018f8d4fdf3b646ull, 0x3ffd4da9e0adde20ull }, + { 0x4018f9db22d0e560ull, 0x3ffd4e51d310fe9dull }, + { 0x4018fae147ae147bull, 0x3ffd4ef9be918533ull }, + { 0x4018fbe76c8b4396ull, 0x3ffd4fa1a3300260ull }, + { 0x4018fced916872b0ull, 0x3ffd504980ed0690ull }, + { 0x4018fdf3b645a1cbull, 0x3ffd50f157c92221ull }, + { 0x4018fef9db22d0e6ull, 0x3ffd519927c4e55cull }, + { 0x4019000000000000ull, 0x3ffd5240f0e0e078ull }, + { 0x4019010624dd2f1bull, 0x3ffd52e8b31da39cull }, + { 0x4019020c49ba5e35ull, 0x3ffd53906e7bbeddull }, + { 0x401903126e978d50ull, 0x3ffd543822fbc23dull }, + { 0x401904189374bc6bull, 0x3ffd54dfd09e3dafull }, + { 0x4019051eb851eb85ull, 0x3ffd55877763c111ull }, + { 0x40190624dd2f1aa0ull, 0x3ffd562f174cdc31ull }, + { 0x4019072b020c49bbull, 0x3ffd56d6b05a1eccull }, + { 0x4019083126e978d5ull, 0x3ffd577e428c188cull }, + { 0x401909374bc6a7f0ull, 0x3ffd5825cde3590bull }, + { 0x40190a3d70a3d70aull, 0x3ffd58cd52606fcfull }, + { 0x40190b4395810625ull, 0x3ffd5974d003ec4eull }, + { 0x40190c49ba5e3540ull, 0x3ffd5a1c46ce5deeull }, + { 0x40190d4fdf3b645aull, 0x3ffd5ac3b6c053ffull }, + { 0x40190e5604189375ull, 0x3ffd5b6b1fda5dc3ull }, + { 0x40190f5c28f5c290ull, 0x3ffd5c12821d0a6aull }, + { 0x401910624dd2f1aaull, 0x3ffd5cb9dd88e912ull }, + { 0x4019116872b020c5ull, 0x3ffd5d61321e88c6ull }, + { 0x4019126e978d4fdfull, 0x3ffd5e087fde7882ull }, + { 0x40191374bc6a7efaull, 0x3ffd5eafc6c9472full }, + { 0x4019147ae147ae15ull, 0x3ffd5f5706df83a5ull }, + { 0x401915810624dd2full, 0x3ffd5ffe4021bcaaull }, + { 0x401916872b020c4aull, 0x3ffd60a5729080f3ull }, + { 0x4019178d4fdf3b64ull, 0x3ffd614c9e2c5f23ull }, + { 0x4019189374bc6a7full, 0x3ffd61f3c2f5e5cdull }, + { 0x401919999999999aull, 0x3ffd629ae0eda370ull }, + { 0x40191a9fbe76c8b4ull, 0x3ffd6341f814267bull }, + { 0x40191ba5e353f7cfull, 0x3ffd63e90869fd4dull }, + { 0x40191cac083126eaull, 0x3ffd649011efb632ull }, + { 0x40191db22d0e5604ull, 0x3ffd653714a5df63ull }, + { 0x40191eb851eb851full, 0x3ffd65de108d070bull }, + { 0x40191fbe76c8b439ull, 0x3ffd668505a5bb41ull }, + { 0x401920c49ba5e354ull, 0x3ffd672bf3f08a0cull }, + { 0x401921cac083126full, 0x3ffd67d2db6e0162ull }, + { 0x401922d0e5604189ull, 0x3ffd6879bc1eaf26ull }, + { 0x401923d70a3d70a4ull, 0x3ffd69209603212aull }, + { 0x401924dd2f1a9fbfull, 0x3ffd69c7691be531ull }, + { 0x401925e353f7ced9ull, 0x3ffd6a6e356988e9ull }, + { 0x401926e978d4fdf4ull, 0x3ffd6b14faec99f2ull }, + { 0x401927ef9db22d0eull, 0x3ffd6bbbb9a5a5d8ull }, + { 0x401928f5c28f5c29ull, 0x3ffd6c6271953a18ull }, + { 0x401929fbe76c8b44ull, 0x3ffd6d0922bbe41dull }, + { 0x40192b020c49ba5eull, 0x3ffd6dafcd1a313full }, + { 0x40192c083126e979ull, 0x3ffd6e5670b0aec8ull }, + { 0x40192d0e56041894ull, 0x3ffd6efd0d7fe9eeull }, + { 0x40192e147ae147aeull, 0x3ffd6fa3a3886fd6ull }, + { 0x40192f1a9fbe76c9ull, 0x3ffd704a32cacd96ull }, + { 0x40193020c49ba5e3ull, 0x3ffd70f0bb479031ull }, + { 0x40193126e978d4feull, 0x3ffd71973cff4499ull }, + { 0x4019322d0e560419ull, 0x3ffd723db7f277afull }, + { 0x4019333333333333ull, 0x3ffd72e42c21b641ull }, + { 0x401934395810624eull, 0x3ffd738a998d8d11ull }, + { 0x4019353f7ced9169ull, 0x3ffd7431003688caull }, + { 0x40193645a1cac083ull, 0x3ffd74d7601d3609ull }, + { 0x4019374bc6a7ef9eull, 0x3ffd757db9422159ull }, + { 0x40193851eb851eb8ull, 0x3ffd76240ba5d734ull }, + { 0x4019395810624dd3ull, 0x3ffd76ca5748e404ull }, + { 0x40193a5e353f7ceeull, 0x3ffd77709c2bd420ull }, + { 0x40193b645a1cac08ull, 0x3ffd7816da4f33cdull }, + { 0x40193c6a7ef9db23ull, 0x3ffd78bd11b38f44ull }, + { 0x40193d70a3d70a3eull, 0x3ffd7963425972a8ull }, + { 0x40193e76c8b43958ull, 0x3ffd7a096c416a0cull }, + { 0x40193f7ced916873ull, 0x3ffd7aaf8f6c0174ull }, + { 0x40194083126e978dull, 0x3ffd7b55abd9c4cfull }, + { 0x40194189374bc6a8ull, 0x3ffd7bfbc18b4001ull }, + { 0x4019428f5c28f5c3ull, 0x3ffd7ca1d080fed7ull }, + { 0x40194395810624ddull, 0x3ffd7d47d8bb8d0full }, + { 0x4019449ba5e353f8ull, 0x3ffd7dedda3b7658ull }, + { 0x401945a1cac08313ull, 0x3ffd7e93d501464eull }, + { 0x401946a7ef9db22dull, 0x3ffd7f39c90d887cull }, + { 0x401947ae147ae148ull, 0x3ffd7fdfb660c85dull }, + { 0x401948b439581062ull, 0x3ffd80859cfb915aull }, + { 0x401949ba5e353f7dull, 0x3ffd812b7cde6ecdull }, + { 0x40194ac083126e98ull, 0x3ffd81d15609ebfdull }, + { 0x40194bc6a7ef9db2ull, 0x3ffd8277287e9420ull }, + { 0x40194ccccccccccdull, 0x3ffd831cf43cf25dull }, + { 0x40194dd2f1a9fbe8ull, 0x3ffd83c2b94591c9ull }, + { 0x40194ed916872b02ull, 0x3ffd84687798fd67ull }, + { 0x40194fdf3b645a1dull, 0x3ffd850e2f37c02cull }, + { 0x401950e560418937ull, 0x3ffd85b3e02264f9ull }, + { 0x401951eb851eb852ull, 0x3ffd86598a5976a0ull }, + { 0x401952f1a9fbe76dull, 0x3ffd86ff2ddd7fe2ull }, + { 0x401953f7ced91687ull, 0x3ffd87a4caaf0b6eull }, + { 0x401954fdf3b645a2ull, 0x3ffd884a60cea3e4ull }, + { 0x40195604189374bdull, 0x3ffd88eff03cd3d2ull }, + { 0x4019570a3d70a3d7ull, 0x3ffd899578fa25b4ull }, + { 0x40195810624dd2f2ull, 0x3ffd8a3afb0723f9ull }, + { 0x40195916872b020cull, 0x3ffd8ae0766458fcull }, + { 0x40195a1cac083127ull, 0x3ffd8b85eb124f08ull }, + { 0x40195b22d0e56042ull, 0x3ffd8c2b59119057ull }, + { 0x40195c28f5c28f5cull, 0x3ffd8cd0c062a713ull }, + { 0x40195d2f1a9fbe77ull, 0x3ffd8d7621061d55ull }, + { 0x40195e353f7ced92ull, 0x3ffd8e1b7afc7d25ull }, + { 0x40195f3b645a1cacull, 0x3ffd8ec0ce46507aull }, + { 0x4019604189374bc7ull, 0x3ffd8f661ae4213bull }, + { 0x40196147ae147ae1ull, 0x3ffd900b60d6793full }, + { 0x4019624dd2f1a9fcull, 0x3ffd90b0a01de24aull }, + { 0x40196353f7ced917ull, 0x3ffd9155d8bae612ull }, + { 0x4019645a1cac0831ull, 0x3ffd91fb0aae0e39ull }, + { 0x401965604189374cull, 0x3ffd92a035f7e454ull }, + { 0x4019666666666667ull, 0x3ffd93455a98f1e6ull }, + { 0x4019676c8b439581ull, 0x3ffd93ea7891c060ull }, + { 0x40196872b020c49cull, 0x3ffd948f8fe2d924ull }, + { 0x40196978d4fdf3b6ull, 0x3ffd9534a08cc583ull }, + { 0x40196a7ef9db22d1ull, 0x3ffd95d9aa900ebdull }, + { 0x40196b851eb851ecull, 0x3ffd967eaded3e02ull }, + { 0x40196c8b43958106ull, 0x3ffd9723aaa4dc70ull }, + { 0x40196d916872b021ull, 0x3ffd97c8a0b77318ull }, + { 0x40196e978d4fdf3cull, 0x3ffd986d90258af6ull }, + { 0x40196f9db22d0e56ull, 0x3ffd991278efacf7ull }, + { 0x401970a3d70a3d71ull, 0x3ffd99b75b1661f9ull }, + { 0x401971a9fbe76c8bull, 0x3ffd9a5c369a32c9ull }, + { 0x401972b020c49ba6ull, 0x3ffd9b010b7ba821ull }, + { 0x401973b645a1cac1ull, 0x3ffd9ba5d9bb4aafull }, + { 0x401974bc6a7ef9dbull, 0x3ffd9c4aa159a30bull }, + { 0x401975c28f5c28f6ull, 0x3ffd9cef625739c1ull }, + { 0x401976c8b4395811ull, 0x3ffd9d941cb4974bull }, + { 0x401977ced916872bull, 0x3ffd9e38d0724411ull }, + { 0x401978d4fdf3b646ull, 0x3ffd9edd7d90c86eull }, + { 0x401979db22d0e560ull, 0x3ffd9f822410aca9ull }, + { 0x40197ae147ae147bull, 0x3ffda026c3f278fbull }, + { 0x40197be76c8b4396ull, 0x3ffda0cb5d36b58bull }, + { 0x40197ced916872b0ull, 0x3ffda16fefddea71ull }, + { 0x40197df3b645a1cbull, 0x3ffda2147be89fb3ull }, + { 0x40197ef9db22d0e6ull, 0x3ffda2b901575d49ull }, + { 0x4019800000000000ull, 0x3ffda35d802aab17ull }, + { 0x4019810624dd2f1bull, 0x3ffda401f86310f5ull }, + { 0x4019820c49ba5e35ull, 0x3ffda4a66a0116a7ull }, + { 0x401983126e978d50ull, 0x3ffda54ad50543e2ull }, + { 0x401984189374bc6bull, 0x3ffda5ef3970204bull }, + { 0x4019851eb851eb85ull, 0x3ffda69397423376ull }, + { 0x40198624dd2f1aa0ull, 0x3ffda737ee7c04e9ull }, + { 0x4019872b020c49bbull, 0x3ffda7dc3f1e1c15ull }, + { 0x4019883126e978d5ull, 0x3ffda8808929005eull }, + { 0x401989374bc6a7f0ull, 0x3ffda924cc9d3918ull }, + { 0x40198a3d70a3d70aull, 0x3ffda9c9097b4d86ull }, + { 0x40198b4395810625ull, 0x3ffdaa6d3fc3c4daull }, + { 0x40198c49ba5e3540ull, 0x3ffdab116f772637ull }, + { 0x40198d4fdf3b645aull, 0x3ffdabb59895f8aeull }, + { 0x40198e5604189375ull, 0x3ffdac59bb20c341ull }, + { 0x40198f5c28f5c290ull, 0x3ffdacfdd7180ce3ull }, + { 0x401990624dd2f1aaull, 0x3ffdada1ec7c5c74ull }, + { 0x4019916872b020c5ull, 0x3ffdae45fb4e38c6ull }, + { 0x4019926e978d4fdfull, 0x3ffdaeea038e289aull }, + { 0x40199374bc6a7efaull, 0x3ffdaf8e053cb2a0ull }, + { 0x4019947ae147ae15ull, 0x3ffdb032005a5d79ull }, + { 0x401995810624dd2full, 0x3ffdb0d5f4e7afb6ull }, + { 0x401996872b020c4aull, 0x3ffdb179e2e52fd7ull }, + { 0x4019978d4fdf3b65ull, 0x3ffdb21dca53644cull }, + { 0x4019989374bc6a7full, 0x3ffdb2c1ab32d375ull }, + { 0x401999999999999aull, 0x3ffdb365858403a3ull }, + { 0x40199a9fbe76c8b4ull, 0x3ffdb40959477b14ull }, + { 0x40199ba5e353f7cfull, 0x3ffdb4ad267dbff9ull }, + { 0x40199cac083126eaull, 0x3ffdb550ed275871ull }, + { 0x40199db22d0e5604ull, 0x3ffdb5f4ad44ca8bull }, + { 0x40199eb851eb851full, 0x3ffdb69866d69c48ull }, + { 0x40199fbe76c8b439ull, 0x3ffdb73c19dd5396ull }, + { 0x4019a0c49ba5e354ull, 0x3ffdb7dfc6597655ull }, + { 0x4019a1cac083126full, 0x3ffdb8836c4b8a54ull }, + { 0x4019a2d0e5604189ull, 0x3ffdb9270bb41552ull }, + { 0x4019a3d70a3d70a4ull, 0x3ffdb9caa4939cffull }, + { 0x4019a4dd2f1a9fbfull, 0x3ffdba6e36eaa6f9ull }, + { 0x4019a5e353f7ced9ull, 0x3ffdbb11c2b9b8d0ull }, + { 0x4019a6e978d4fdf4ull, 0x3ffdbbb548015803ull }, + { 0x4019a7ef9db22d0eull, 0x3ffdbc58c6c20a01ull }, + { 0x4019a8f5c28f5c29ull, 0x3ffdbcfc3efc542aull }, + { 0x4019a9fbe76c8b44ull, 0x3ffdbd9fb0b0bbcdull }, + { 0x4019ab020c49ba5eull, 0x3ffdbe431bdfc628ull }, + { 0x4019ac083126e979ull, 0x3ffdbee68089f86cull }, + { 0x4019ad0e56041894ull, 0x3ffdbf89deafd7b8ull }, + { 0x4019ae147ae147aeull, 0x3ffdc02d3651e91aull }, + { 0x4019af1a9fbe76c9ull, 0x3ffdc0d08770b194ull }, + { 0x4019b020c49ba5e3ull, 0x3ffdc173d20cb613ull }, + { 0x4019b126e978d4feull, 0x3ffdc21716267b7aull }, + { 0x4019b22d0e560419ull, 0x3ffdc2ba53be8696ull }, + { 0x4019b33333333333ull, 0x3ffdc35d8ad55c28ull }, + { 0x4019b4395810624eull, 0x3ffdc400bb6b80e1ull }, + { 0x4019b53f7ced9169ull, 0x3ffdc4a3e5817961ull }, + { 0x4019b645a1cac083ull, 0x3ffdc5470917ca37ull }, + { 0x4019b74bc6a7ef9eull, 0x3ffdc5ea262ef7e5ull }, + { 0x4019b851eb851eb8ull, 0x3ffdc68d3cc786dbull }, + { 0x4019b95810624dd3ull, 0x3ffdc7304ce1fb7bull }, + { 0x4019ba5e353f7ceeull, 0x3ffdc7d3567eda15ull }, + { 0x4019bb645a1cac08ull, 0x3ffdc876599ea6ebull }, + { 0x4019bc6a7ef9db23ull, 0x3ffdc9195641e62full }, + { 0x4019bd70a3d70a3eull, 0x3ffdc9bc4c691c01ull }, + { 0x4019be76c8b43958ull, 0x3ffdca5f3c14cc75ull }, + { 0x4019bf7ced916873ull, 0x3ffdcb0225457b8cull }, + { 0x4019c083126e978dull, 0x3ffdcba507fbad39ull }, + { 0x4019c189374bc6a8ull, 0x3ffdcc47e437e55full }, + { 0x4019c28f5c28f5c3ull, 0x3ffdcceab9faa7d1ull }, + { 0x4019c395810624ddull, 0x3ffdcd8d89447851ull }, + { 0x4019c49ba5e353f8ull, 0x3ffdce305215da95ull }, + { 0x4019c5a1cac08313ull, 0x3ffdced3146f523full }, + { 0x4019c6a7ef9db22dull, 0x3ffdcf75d05162e4ull }, + { 0x4019c7ae147ae148ull, 0x3ffdd01885bc9008ull }, + { 0x4019c8b439581062ull, 0x3ffdd0bb34b15d20ull }, + { 0x4019c9ba5e353f7dull, 0x3ffdd15ddd304d92ull }, + { 0x4019cac083126e98ull, 0x3ffdd2007f39e4b4ull }, + { 0x4019cbc6a7ef9db2ull, 0x3ffdd2a31acea5c9ull }, + { 0x4019cccccccccccdull, 0x3ffdd345afef140aull }, + { 0x4019cdd2f1a9fbe8ull, 0x3ffdd3e83e9bb29dull }, + { 0x4019ced916872b02ull, 0x3ffdd48ac6d50499ull }, + { 0x4019cfdf3b645a1dull, 0x3ffdd52d489b8d05ull }, + { 0x4019d0e560418937ull, 0x3ffdd5cfc3efced9ull }, + { 0x4019d1eb851eb852ull, 0x3ffdd67238d24cfeull }, + { 0x4019d2f1a9fbe76dull, 0x3ffdd714a7438a4dull }, + { 0x4019d3f7ced91687ull, 0x3ffdd7b70f44098dull }, + { 0x4019d4fdf3b645a2ull, 0x3ffdd85970d44d79ull }, + { 0x4019d604189374bdull, 0x3ffdd8fbcbf4d8bcull }, + { 0x4019d70a3d70a3d7ull, 0x3ffdd99e20a62deeull }, + { 0x4019d810624dd2f2ull, 0x3ffdda406ee8cf9cull }, + { 0x4019d916872b020cull, 0x3ffddae2b6bd4041ull }, + { 0x4019da1cac083127ull, 0x3ffddb84f8240249ull }, + { 0x4019db22d0e56042ull, 0x3ffddc27331d9810ull }, + { 0x4019dc28f5c28f5cull, 0x3ffddcc967aa83e3ull }, + { 0x4019dd2f1a9fbe77ull, 0x3ffddd6b95cb4800ull }, + { 0x4019de353f7ced92ull, 0x3ffdde0dbd806694ull }, + { 0x4019df3b645a1cacull, 0x3ffddeafdeca61bfull }, + { 0x4019e04189374bc7ull, 0x3ffddf51f9a9bb8full }, + { 0x4019e147ae147ae1ull, 0x3ffddff40e1ef604ull }, + { 0x4019e24dd2f1a9fcull, 0x3ffde0961c2a930full }, + { 0x4019e353f7ced917ull, 0x3ffde13823cd1490ull }, + { 0x4019e45a1cac0831ull, 0x3ffde1da2506fc58ull }, + { 0x4019e5604189374cull, 0x3ffde27c1fd8cc2aull }, + { 0x4019e66666666667ull, 0x3ffde31e144305b8ull }, + { 0x4019e76c8b439581ull, 0x3ffde3c002462aa5ull }, + { 0x4019e872b020c49cull, 0x3ffde461e9e2bc86ull }, + { 0x4019e978d4fdf3b6ull, 0x3ffde503cb193cdeull }, + { 0x4019ea7ef9db22d1ull, 0x3ffde5a5a5ea2d23ull }, + { 0x4019eb851eb851ecull, 0x3ffde6477a560ebbull }, + { 0x4019ec8b43958106ull, 0x3ffde6e9485d62fcull }, + { 0x4019ed916872b021ull, 0x3ffde78b1000ab2eull }, + { 0x4019ee978d4fdf3cull, 0x3ffde82cd1406888ull }, + { 0x4019ef9db22d0e56ull, 0x3ffde8ce8c1d1c32ull }, + { 0x4019f0a3d70a3d71ull, 0x3ffde97040974746ull }, + { 0x4019f1a9fbe76c8bull, 0x3ffdea11eeaf6aceull }, + { 0x4019f2b020c49ba6ull, 0x3ffdeab3966607c4ull }, + { 0x4019f3b645a1cac1ull, 0x3ffdeb5537bb9f15ull }, + { 0x4019f4bc6a7ef9dbull, 0x3ffdebf6d2b0b19bull }, + { 0x4019f5c28f5c28f6ull, 0x3ffdec986745c024ull }, + { 0x4019f6c8b4395811ull, 0x3ffded39f57b4b6eull }, + { 0x4019f7ced916872bull, 0x3ffdeddb7d51d427ull }, + { 0x4019f8d4fdf3b646ull, 0x3ffdee7cfec9daeeull }, + { 0x4019f9db22d0e560ull, 0x3ffdef1e79e3e052ull }, + { 0x4019fae147ae147bull, 0x3ffdefbfeea064d6ull }, + { 0x4019fbe76c8b4396ull, 0x3ffdf0615cffe8eaull }, + { 0x4019fced916872b0ull, 0x3ffdf102c502ecf1ull }, + { 0x4019fdf3b645a1cbull, 0x3ffdf1a426a9f13eull }, + { 0x4019fef9db22d0e6ull, 0x3ffdf24581f57615ull }, + { 0x401a000000000000ull, 0x3ffdf2e6d6e5fbaaull }, + { 0x401a010624dd2f1bull, 0x3ffdf388257c0225ull }, + { 0x401a020c49ba5e35ull, 0x3ffdf4296db8099bull }, + { 0x401a03126e978d50ull, 0x3ffdf4caaf9a9214ull }, + { 0x401a04189374bc6bull, 0x3ffdf56beb241b88ull }, + { 0x401a051eb851eb85ull, 0x3ffdf60d205525e0ull }, + { 0x401a0624dd2f1aa0ull, 0x3ffdf6ae4f2e30f7ull }, + { 0x401a072b020c49bbull, 0x3ffdf74f77afbc98ull }, + { 0x401a083126e978d5ull, 0x3ffdf7f099da487eull }, + { 0x401a09374bc6a7f0ull, 0x3ffdf891b5ae5457ull }, + { 0x401a0a3d70a3d70aull, 0x3ffdf932cb2c5fc0ull }, + { 0x401a0b4395810625ull, 0x3ffdf9d3da54ea48ull }, + { 0x401a0c49ba5e3540ull, 0x3ffdfa74e3287370ull }, + { 0x401a0d4fdf3b645aull, 0x3ffdfb15e5a77aa6ull }, + { 0x401a0e5604189375ull, 0x3ffdfbb6e1d27f4eull }, + { 0x401a0f5c28f5c290ull, 0x3ffdfc57d7aa00bbull }, + { 0x401a10624dd2f1aaull, 0x3ffdfcf8c72e7e2eull }, + { 0x401a116872b020c5ull, 0x3ffdfd99b06076deull }, + { 0x401a126e978d4fdfull, 0x3ffdfe3a934069f0ull }, + { 0x401a1374bc6a7efaull, 0x3ffdfedb6fced67aull }, + { 0x401a147ae147ae15ull, 0x3ffdff7c460c3b86ull }, + { 0x401a15810624dd2full, 0x3ffe001d15f91809ull }, + { 0x401a16872b020c4aull, 0x3ffe00bddf95eaf0ull }, + { 0x401a178d4fdf3b65ull, 0x3ffe015ea2e33315ull }, + { 0x401a189374bc6a7full, 0x3ffe01ff5fe16f43ull }, + { 0x401a19999999999aull, 0x3ffe02a016911e39ull }, + { 0x401a1a9fbe76c8b4ull, 0x3ffe0340c6f2bea2ull }, + { 0x401a1ba5e353f7cfull, 0x3ffe03e17106cf20ull }, + { 0x401a1cac083126eaull, 0x3ffe048214cdce43ull }, + { 0x401a1db22d0e5604ull, 0x3ffe0522b2483a8aull }, + { 0x401a1eb851eb851full, 0x3ffe05c34976926aull }, + { 0x401a1fbe76c8b439ull, 0x3ffe0663da595446ull }, + { 0x401a20c49ba5e354ull, 0x3ffe070464f0fe72ull }, + { 0x401a21cac083126full, 0x3ffe07a4e93e0f35ull }, + { 0x401a22d0e5604189ull, 0x3ffe0845674104c5ull }, + { 0x401a23d70a3d70a4ull, 0x3ffe08e5defa5d4bull }, + { 0x401a24dd2f1a9fbfull, 0x3ffe0986506a96e1ull }, + { 0x401a25e353f7ced9ull, 0x3ffe0a26bb922f90ull }, + { 0x401a26e978d4fdf4ull, 0x3ffe0ac72071a555ull }, + { 0x401a27ef9db22d0eull, 0x3ffe0b677f09761dull }, + { 0x401a28f5c28f5c29ull, 0x3ffe0c07d75a1fc5ull }, + { 0x401a29fbe76c8b44ull, 0x3ffe0ca82964201eull }, + { 0x401a2b020c49ba5eull, 0x3ffe0d487527f4e8ull }, + { 0x401a2c083126e979ull, 0x3ffe0de8baa61bd5ull }, + { 0x401a2d0e56041894ull, 0x3ffe0e88f9df1288ull }, + { 0x401a2e147ae147aeull, 0x3ffe0f2932d35695ull }, + { 0x401a2f1a9fbe76c9ull, 0x3ffe0fc965836582ull }, + { 0x401a3020c49ba5e3ull, 0x3ffe106991efbcc6ull }, + { 0x401a3126e978d4feull, 0x3ffe1109b818d9caull }, + { 0x401a322d0e560419ull, 0x3ffe11a9d7ff39e7ull }, + { 0x401a333333333333ull, 0x3ffe1249f1a35a67ull }, + { 0x401a34395810624eull, 0x3ffe12ea0505b887ull }, + { 0x401a353f7ced9169ull, 0x3ffe138a1226d175ull }, + { 0x401a3645a1cac083ull, 0x3ffe142a1907224dull }, + { 0x401a374bc6a7ef9eull, 0x3ffe14ca19a72823ull }, + { 0x401a3851eb851eb8ull, 0x3ffe156a14075ff6ull }, + { 0x401a395810624dd3ull, 0x3ffe160a082846b9ull }, + { 0x401a3a5e353f7ceeull, 0x3ffe16a9f60a5952ull }, + { 0x401a3b645a1cac08ull, 0x3ffe1749ddae1493ull }, + { 0x401a3c6a7ef9db23ull, 0x3ffe17e9bf13f547ull }, + { 0x401a3d70a3d70a3eull, 0x3ffe18899a3c7824ull }, + { 0x401a3e76c8b43958ull, 0x3ffe19296f2819d4ull }, + { 0x401a3f7ced916873ull, 0x3ffe19c93dd756f2ull }, + { 0x401a4083126e978dull, 0x3ffe1a69064aac0bull }, + { 0x401a4189374bc6a8ull, 0x3ffe1b08c882959dull }, + { 0x401a428f5c28f5c3ull, 0x3ffe1ba8847f9018ull }, + { 0x401a4395810624ddull, 0x3ffe1c483a4217dbull }, + { 0x401a449ba5e353f8ull, 0x3ffe1ce7e9caa93aull }, + { 0x401a45a1cac08313ull, 0x3ffe1d879319c078ull }, + { 0x401a46a7ef9db22dull, 0x3ffe1e27362fd9caull }, + { 0x401a47ae147ae148ull, 0x3ffe1ec6d30d7158ull }, + { 0x401a48b439581062ull, 0x3ffe1f6669b30339ull }, + { 0x401a49ba5e353f7dull, 0x3ffe2005fa210b77ull }, + { 0x401a4ac083126e98ull, 0x3ffe20a58458060eull }, + { 0x401a4bc6a7ef9db2ull, 0x3ffe214508586eeaull }, + { 0x401a4ccccccccccdull, 0x3ffe21e48622c1e9ull }, + { 0x401a4dd2f1a9fbe8ull, 0x3ffe2283fdb77adcull }, + { 0x401a4ed916872b02ull, 0x3ffe23236f171582ull }, + { 0x401a4fdf3b645a1dull, 0x3ffe23c2da420d90ull }, + { 0x401a50e560418937ull, 0x3ffe24623f38deaaull }, + { 0x401a51eb851eb852ull, 0x3ffe25019dfc0466ull }, + { 0x401a52f1a9fbe76dull, 0x3ffe25a0f68bfa4bull }, + { 0x401a53f7ced91687ull, 0x3ffe264048e93bd2ull }, + { 0x401a54fdf3b645a2ull, 0x3ffe26df95144467ull }, + { 0x401a5604189374bdull, 0x3ffe277edb0d8f66ull }, + { 0x401a570a3d70a3d7ull, 0x3ffe281e1ad5981cull }, + { 0x401a5810624dd2f2ull, 0x3ffe28bd546cd9caull }, + { 0x401a5916872b020cull, 0x3ffe295c87d3cfa0ull }, + { 0x401a5a1cac083127ull, 0x3ffe29fbb50af4c1ull }, + { 0x401a5b22d0e56042ull, 0x3ffe2a9adc12c443ull }, + { 0x401a5c28f5c28f5cull, 0x3ffe2b39fcebb92aull }, + { 0x401a5d2f1a9fbe77ull, 0x3ffe2bd917964e70ull }, + { 0x401a5e353f7ced92ull, 0x3ffe2c782c12fefdull }, + { 0x401a5f3b645a1cacull, 0x3ffe2d173a6245abull }, + { 0x401a604189374bc7ull, 0x3ffe2db642849d4aull }, + { 0x401a6147ae147ae1ull, 0x3ffe2e55447a8097ull }, + { 0x401a624dd2f1a9fcull, 0x3ffe2ef440446a42ull }, + { 0x401a6353f7ced917ull, 0x3ffe2f9335e2d4edull }, + { 0x401a645a1cac0831ull, 0x3ffe303225563b2cull }, + { 0x401a65604189374cull, 0x3ffe30d10e9f1785ull }, + { 0x401a666666666667ull, 0x3ffe316ff1bde46full }, + { 0x401a676c8b439581ull, 0x3ffe320eceb31c52ull }, + { 0x401a6872b020c49cull, 0x3ffe32ada57f3989ull }, + { 0x401a6978d4fdf3b6ull, 0x3ffe334c7622b661ull }, + { 0x401a6a7ef9db22d1ull, 0x3ffe33eb409e0d19ull }, + { 0x401a6b851eb851ecull, 0x3ffe348a04f1b7dfull }, + { 0x401a6c8b43958106ull, 0x3ffe3528c31e30d6ull }, + { 0x401a6d916872b021ull, 0x3ffe35c77b23f212ull }, + { 0x401a6e978d4fdf3cull, 0x3ffe36662d037598ull }, + { 0x401a6f9db22d0e56ull, 0x3ffe3704d8bd355full }, + { 0x401a70a3d70a3d71ull, 0x3ffe37a37e51ab50ull }, + { 0x401a71a9fbe76c8bull, 0x3ffe38421dc15146ull }, + { 0x401a72b020c49ba6ull, 0x3ffe38e0b70ca10full }, + { 0x401a73b645a1cac1ull, 0x3ffe397f4a341469ull }, + { 0x401a74bc6a7ef9dbull, 0x3ffe3a1dd7382503ull }, + { 0x401a75c28f5c28f6ull, 0x3ffe3abc5e194c81ull }, + { 0x401a76c8b4395811ull, 0x3ffe3b5aded80477ull }, + { 0x401a77ced916872bull, 0x3ffe3bf95974c66aull }, + { 0x401a78d4fdf3b646ull, 0x3ffe3c97cdf00bd4ull }, + { 0x401a79db22d0e560ull, 0x3ffe3d363c4a4e1dull }, + { 0x401a7ae147ae147bull, 0x3ffe3dd4a48406a2ull }, + { 0x401a7be76c8b4396ull, 0x3ffe3e73069daeb0ull }, + { 0x401a7ced916872b0ull, 0x3ffe3f116297bf87ull }, + { 0x401a7df3b645a1cbull, 0x3ffe3fafb872b25aull }, + { 0x401a7ef9db22d0e6ull, 0x3ffe404e082f004bull }, + { 0x401a800000000000ull, 0x3ffe40ec51cd226full }, + { 0x401a810624dd2f1bull, 0x3ffe418a954d91cfull }, + { 0x401a820c49ba5e35ull, 0x3ffe4228d2b0c763ull }, + { 0x401a83126e978d50ull, 0x3ffe42c709f73c18ull }, + { 0x401a84189374bc6bull, 0x3ffe43653b2168cbull }, + { 0x401a851eb851eb85ull, 0x3ffe4403662fc64aull }, + { 0x401a8624dd2f1aa0ull, 0x3ffe44a18b22cd59ull }, + { 0x401a872b020c49bbull, 0x3ffe453fa9faf6a9ull }, + { 0x401a883126e978d5ull, 0x3ffe45ddc2b8bae1ull }, + { 0x401a89374bc6a7f0ull, 0x3ffe467bd55c9298ull }, + { 0x401a8a3d70a3d70aull, 0x3ffe4719e1e6f658ull }, + { 0x401a8b4395810625ull, 0x3ffe47b7e8585e9cull }, + { 0x401a8c49ba5e3540ull, 0x3ffe4855e8b143d2ull }, + { 0x401a8d4fdf3b645aull, 0x3ffe48f3e2f21e5aull }, + { 0x401a8e5604189375ull, 0x3ffe4991d71b6685ull }, + { 0x401a8f5c28f5c290ull, 0x3ffe4a2fc52d9498ull }, + { 0x401a90624dd2f1aaull, 0x3ffe4acdad2920c7ull }, + { 0x401a916872b020c5ull, 0x3ffe4b6b8f0e833cull }, + { 0x401a926e978d4fdfull, 0x3ffe4c096ade3411ull }, + { 0x401a9374bc6a7efaull, 0x3ffe4ca74098ab52ull }, + { 0x401a947ae147ae15ull, 0x3ffe4d45103e60fdull }, + { 0x401a95810624dd2full, 0x3ffe4de2d9cfcd03ull }, + { 0x401a96872b020c4aull, 0x3ffe4e809d4d6746ull }, + { 0x401a978d4fdf3b65ull, 0x3ffe4f1e5ab7a79dull }, + { 0x401a989374bc6a7full, 0x3ffe4fbc120f05ccull }, + { 0x401a99999999999aull, 0x3ffe5059c353f98eull }, + { 0x401a9a9fbe76c8b4ull, 0x3ffe50f76e86fa8dull }, + { 0x401a9ba5e353f7cfull, 0x3ffe519513a88069ull }, + { 0x401a9cac083126eaull, 0x3ffe5232b2b902b0ull }, + { 0x401a9db22d0e5604ull, 0x3ffe52d04bb8f8e4ull }, + { 0x401a9eb851eb851full, 0x3ffe536ddea8da79ull }, + { 0x401a9fbe76c8b43aull, 0x3ffe540b6b891ed6ull }, + { 0x401aa0c49ba5e354ull, 0x3ffe54a8f25a3d52ull }, + { 0x401aa1cac083126full, 0x3ffe5546731cad3aull }, + { 0x401aa2d0e5604189ull, 0x3ffe55e3edd0e5caull }, + { 0x401aa3d70a3d70a4ull, 0x3ffe568162775e31ull }, + { 0x401aa4dd2f1a9fbfull, 0x3ffe571ed1108d92ull }, + { 0x401aa5e353f7ced9ull, 0x3ffe57bc399ceafeull }, + { 0x401aa6e978d4fdf4ull, 0x3ffe58599c1ced7eull }, + { 0x401aa7ef9db22d0eull, 0x3ffe58f6f8910c08ull }, + { 0x401aa8f5c28f5c29ull, 0x3ffe59944ef9bd88ull }, + { 0x401aa9fbe76c8b44ull, 0x3ffe5a319f5778dcull }, + { 0x401aab020c49ba5eull, 0x3ffe5acee9aab4d1ull }, + { 0x401aac083126e979ull, 0x3ffe5b6c2df3e829ull }, + { 0x401aad0e56041894ull, 0x3ffe5c096c33899aull }, + { 0x401aae147ae147aeull, 0x3ffe5ca6a46a0fc7ull }, + { 0x401aaf1a9fbe76c9ull, 0x3ffe5d43d697f14cull }, + { 0x401ab020c49ba5e3ull, 0x3ffe5de102bda4b1ull }, + { 0x401ab126e978d4feull, 0x3ffe5e7e28dba075ull }, + { 0x401ab22d0e560419ull, 0x3ffe5f1b48f25b07ull }, + { 0x401ab33333333333ull, 0x3ffe5fb863024ac9ull }, + { 0x401ab4395810624eull, 0x3ffe6055770be610ull }, + { 0x401ab53f7ced9169ull, 0x3ffe60f2850fa322ull }, + { 0x401ab645a1cac083ull, 0x3ffe618f8d0df838ull }, + { 0x401ab74bc6a7ef9eull, 0x3ffe622c8f075b7full }, + { 0x401ab851eb851eb8ull, 0x3ffe62c98afc4313ull }, + { 0x401ab95810624dd3ull, 0x3ffe636680ed2507ull }, + { 0x401aba5e353f7ceeull, 0x3ffe640370da775bull }, + { 0x401abb645a1cac08ull, 0x3ffe64a05ac4b006ull }, + { 0x401abc6a7ef9db23ull, 0x3ffe653d3eac44f0ull }, + { 0x401abd70a3d70a3eull, 0x3ffe65da1c91abf2ull }, + { 0x401abe76c8b43958ull, 0x3ffe6676f4755ad9ull }, + { 0x401abf7ced916873ull, 0x3ffe6713c657c764ull }, + { 0x401ac083126e978dull, 0x3ffe67b092396745ull }, + { 0x401ac189374bc6a8ull, 0x3ffe684d581ab020ull }, + { 0x401ac28f5c28f5c3ull, 0x3ffe68ea17fc178cull }, + { 0x401ac395810624ddull, 0x3ffe6986d1de1311ull }, + { 0x401ac49ba5e353f8ull, 0x3ffe6a2385c1182cull }, + { 0x401ac5a1cac08313ull, 0x3ffe6ac033a59c4aull }, + { 0x401ac6a7ef9db22dull, 0x3ffe6b5cdb8c14cbull }, + { 0x401ac7ae147ae148ull, 0x3ffe6bf97d74f704ull }, + { 0x401ac8b439581062ull, 0x3ffe6c961960b839ull }, + { 0x401ac9ba5e353f7dull, 0x3ffe6d32af4fcda4ull }, + { 0x401acac083126e98ull, 0x3ffe6dcf3f42ac6eull }, + { 0x401acbc6a7ef9db2ull, 0x3ffe6e6bc939c9b6ull }, + { 0x401acccccccccccdull, 0x3ffe6f084d359a8bull }, + { 0x401acdd2f1a9fbe8ull, 0x3ffe6fa4cb3693efull }, + { 0x401aced916872b02ull, 0x3ffe7041433d2ad8ull }, + { 0x401acfdf3b645a1dull, 0x3ffe70ddb549d42eull }, + { 0x401ad0e560418937ull, 0x3ffe717a215d04cbull }, + { 0x401ad1eb851eb852ull, 0x3ffe72168777317bull }, + { 0x401ad2f1a9fbe76dull, 0x3ffe72b2e798cf00ull }, + { 0x401ad3f7ced91687ull, 0x3ffe734f41c25209ull }, + { 0x401ad4fdf3b645a2ull, 0x3ffe73eb95f42f3dull }, + { 0x401ad604189374bdull, 0x3ffe7487e42edb33ull }, + { 0x401ad70a3d70a3d7ull, 0x3ffe75242c72ca75ull }, + { 0x401ad810624dd2f2ull, 0x3ffe75c06ec07180ull }, + { 0x401ad916872b020cull, 0x3ffe765cab1844c3ull }, + { 0x401ada1cac083127ull, 0x3ffe76f8e17ab8a1ull }, + { 0x401adb22d0e56042ull, 0x3ffe779511e8416eull }, + { 0x401adc28f5c28f5cull, 0x3ffe78313c615372ull }, + { 0x401add2f1a9fbe77ull, 0x3ffe78cd60e662e7ull }, + { 0x401ade353f7ced92ull, 0x3ffe79697f77e3faull }, + { 0x401adf3b645a1cacull, 0x3ffe7a0598164acbull }, + { 0x401ae04189374bc7ull, 0x3ffe7aa1aac20b6cull }, + { 0x401ae147ae147ae1ull, 0x3ffe7b3db77b99e2ull }, + { 0x401ae24dd2f1a9fcull, 0x3ffe7bd9be436a25ull }, + { 0x401ae353f7ced917ull, 0x3ffe7c75bf19f020ull }, + { 0x401ae45a1cac0831ull, 0x3ffe7d11b9ff9fb0ull }, + { 0x401ae5604189374cull, 0x3ffe7dadaef4eca5ull }, + { 0x401ae66666666667ull, 0x3ffe7e499dfa4ac2ull }, + { 0x401ae76c8b439581ull, 0x3ffe7ee587102dbdull }, + { 0x401ae872b020c49cull, 0x3ffe7f816a37093eull }, + { 0x401ae978d4fdf3b6ull, 0x3ffe801d476f50e1ull }, + { 0x401aea7ef9db22d1ull, 0x3ffe80b91eb97833ull }, + { 0x401aeb851eb851ecull, 0x3ffe8154f015f2b6ull }, + { 0x401aec8b43958106ull, 0x3ffe81f0bb8533ddull }, + { 0x401aed916872b021ull, 0x3ffe828c8107af0full }, + { 0x401aee978d4fdf3cull, 0x3ffe8328409dd7a5ull }, + { 0x401aef9db22d0e56ull, 0x3ffe83c3fa4820eaull }, + { 0x401af0a3d70a3d71ull, 0x3ffe845fae06fe20ull }, + { 0x401af1a9fbe76c8bull, 0x3ffe84fb5bdae276ull }, + { 0x401af2b020c49ba6ull, 0x3ffe859703c44114ull }, + { 0x401af3b645a1cac1ull, 0x3ffe8632a5c38d0full }, + { 0x401af4bc6a7ef9dbull, 0x3ffe86ce41d93971ull }, + { 0x401af5c28f5c28f6ull, 0x3ffe8769d805b93aull }, + { 0x401af6c8b4395811ull, 0x3ffe880568497f5aull }, + { 0x401af7ced916872bull, 0x3ffe88a0f2a4feb2ull }, + { 0x401af8d4fdf3b646ull, 0x3ffe893c7718aa1aull }, + { 0x401af9db22d0e560ull, 0x3ffe89d7f5a4f45aull }, + { 0x401afae147ae147bull, 0x3ffe8a736e4a502full }, + { 0x401afbe76c8b4396ull, 0x3ffe8b0ee1093048ull }, + { 0x401afced916872b0ull, 0x3ffe8baa4de20746ull }, + { 0x401afdf3b645a1cbull, 0x3ffe8c45b4d547bfull }, + { 0x401afef9db22d0e6ull, 0x3ffe8ce115e36439ull }, + { 0x401b000000000000ull, 0x3ffe8d7c710ccf31ull }, + { 0x401b010624dd2f1bull, 0x3ffe8e17c651fb13ull }, + { 0x401b020c49ba5e35ull, 0x3ffe8eb315b35a40ull }, + { 0x401b03126e978d50ull, 0x3ffe8f4e5f315f0dull }, + { 0x401b04189374bc6bull, 0x3ffe8fe9a2cc7bc0ull }, + { 0x401b051eb851eb85ull, 0x3ffe9084e0852291ull }, + { 0x401b0624dd2f1aa0ull, 0x3ffe9120185bc5b0ull }, + { 0x401b072b020c49bbull, 0x3ffe91bb4a50d73bull }, + { 0x401b083126e978d5ull, 0x3ffe92567664c944ull }, + { 0x401b09374bc6a7f0ull, 0x3ffe92f19c980dd2ull }, + { 0x401b0a3d70a3d70aull, 0x3ffe938cbceb16ddull }, + { 0x401b0b4395810625ull, 0x3ffe9427d75e5652ull }, + { 0x401b0c49ba5e3540ull, 0x3ffe94c2ebf23e10ull }, + { 0x401b0d4fdf3b645aull, 0x3ffe955dfaa73fe8ull }, + { 0x401b0e5604189375ull, 0x3ffe95f9037dcda0ull }, + { 0x401b0f5c28f5c290ull, 0x3ffe9694067658f0ull }, + { 0x401b10624dd2f1aaull, 0x3ffe972f03915383ull }, + { 0x401b116872b020c5ull, 0x3ffe97c9facf2ef8ull }, + { 0x401b126e978d4fdfull, 0x3ffe9864ec305cdfull }, + { 0x401b1374bc6a7efaull, 0x3ffe98ffd7b54ebfull }, + { 0x401b147ae147ae15ull, 0x3ffe999abd5e760eull }, + { 0x401b15810624dd2full, 0x3ffe9a359d2c4437ull }, + { 0x401b16872b020c4aull, 0x3ffe9ad0771f2a9aull }, + { 0x401b178d4fdf3b65ull, 0x3ffe9b6b4b379a85ull }, + { 0x401b189374bc6a7full, 0x3ffe9c061976053eull }, + { 0x401b19999999999aull, 0x3ffe9ca0e1dadbfdull }, + { 0x401b1a9fbe76c8b4ull, 0x3ffe9d3ba4668fecull }, + { 0x401b1ba5e353f7cfull, 0x3ffe9dd661199229ull }, + { 0x401b1cac083126eaull, 0x3ffe9e7117f453c4ull }, + { 0x401b1db22d0e5604ull, 0x3ffe9f0bc8f745c2ull }, + { 0x401b1eb851eb851full, 0x3ffe9fa67422d91bull }, + { 0x401b1fbe76c8b43aull, 0x3ffea04119777eb9ull }, + { 0x401b20c49ba5e354ull, 0x3ffea0dbb8f5a779ull }, + { 0x401b21cac083126full, 0x3ffea176529dc42eull }, + { 0x401b22d0e5604189ull, 0x3ffea210e6704599ull }, + { 0x401b23d70a3d70a4ull, 0x3ffea2ab746d9c75ull }, + { 0x401b24dd2f1a9fbfull, 0x3ffea345fc96396cull }, + { 0x401b25e353f7ced9ull, 0x3ffea3e07eea8d19ull }, + { 0x401b26e978d4fdf4ull, 0x3ffea47afb6b0811ull }, + { 0x401b27ef9db22d0eull, 0x3ffea51572181ad6ull }, + { 0x401b28f5c28f5c29ull, 0x3ffea5afe2f235e1ull }, + { 0x401b29fbe76c8b44ull, 0x3ffea64a4df9c99dull }, + { 0x401b2b020c49ba5eull, 0x3ffea6e4b32f4668ull }, + { 0x401b2c083126e979ull, 0x3ffea77f12931c94ull }, + { 0x401b2d0e56041894ull, 0x3ffea8196c25bc65ull }, + { 0x401b2e147ae147aeull, 0x3ffea8b3bfe79614ull }, + { 0x401b2f1a9fbe76c9ull, 0x3ffea94e0dd919ccull }, + { 0x401b3020c49ba5e3ull, 0x3ffea9e855fab7adull }, + { 0x401b3126e978d4feull, 0x3ffeaa82984cdfc8ull }, + { 0x401b322d0e560419ull, 0x3ffeab1cd4d00224ull }, + { 0x401b333333333333ull, 0x3ffeabb70b848eb8ull }, + { 0x401b34395810624eull, 0x3ffeac513c6af571ull }, + { 0x401b353f7ced9169ull, 0x3ffeaceb6783a630ull }, + { 0x401b3645a1cac083ull, 0x3ffead858ccf10c6ull }, + { 0x401b374bc6a7ef9eull, 0x3ffeae1fac4da4fbull }, + { 0x401b3851eb851eb8ull, 0x3ffeaeb9c5ffd287ull }, + { 0x401b395810624dd3ull, 0x3ffeaf53d9e60918ull }, + { 0x401b3a5e353f7ceeull, 0x3ffeafede800b850ull }, + { 0x401b3b645a1cac08ull, 0x3ffeb087f0504fc0ull }, + { 0x401b3c6a7ef9db23ull, 0x3ffeb121f2d53ef2ull }, + { 0x401b3d70a3d70a3eull, 0x3ffeb1bbef8ff560ull }, + { 0x401b3e76c8b43958ull, 0x3ffeb255e680e277ull }, + { 0x401b3f7ced916873ull, 0x3ffeb2efd7a8759aull }, + { 0x401b4083126e978dull, 0x3ffeb389c3071e1dull }, + { 0x401b4189374bc6a8ull, 0x3ffeb423a89d4b4aull }, + { 0x401b428f5c28f5c3ull, 0x3ffeb4bd886b6c5dull }, + { 0x401b4395810624ddull, 0x3ffeb5576271f083ull }, + { 0x401b449ba5e353f8ull, 0x3ffeb5f136b146e2ull }, + { 0x401b45a1cac08313ull, 0x3ffeb68b0529de8full }, + { 0x401b46a7ef9db22dull, 0x3ffeb724cddc2693ull }, + { 0x401b47ae147ae148ull, 0x3ffeb7be90c88dedull }, + { 0x401b48b439581062ull, 0x3ffeb8584def838dull }, + { 0x401b49ba5e353f7dull, 0x3ffeb8f205517658ull }, + { 0x401b4ac083126e98ull, 0x3ffeb98bb6eed526ull }, + { 0x401b4bc6a7ef9db2ull, 0x3ffeba2562c80ec1ull }, + { 0x401b4ccccccccccdull, 0x3ffebabf08dd91eaull }, + { 0x401b4dd2f1a9fbe8ull, 0x3ffebb58a92fcd52ull }, + { 0x401b4ed916872b02ull, 0x3ffebbf243bf2fa0ull }, + { 0x401b4fdf3b645a1dull, 0x3ffebc8bd88c276dull }, + { 0x401b50e560418937ull, 0x3ffebd2567972347ull }, + { 0x401b51eb851eb852ull, 0x3ffebdbef0e091aeull }, + { 0x401b52f1a9fbe76dull, 0x3ffebe587468e116ull }, + { 0x401b53f7ced91687ull, 0x3ffebef1f2307fe8ull }, + { 0x401b54fdf3b645a2ull, 0x3ffebf8b6a37dc7full }, + { 0x401b5604189374bdull, 0x3ffec024dc7f652bull }, + { 0x401b570a3d70a3d7ull, 0x3ffec0be4907882cull }, + { 0x401b5810624dd2f2ull, 0x3ffec157afd0b3bbull }, + { 0x401b5916872b020cull, 0x3ffec1f110db5601ull }, + { 0x401b5a1cac083127ull, 0x3ffec28a6c27dd1cull }, + { 0x401b5b22d0e56042ull, 0x3ffec323c1b6b71cull }, + { 0x401b5c28f5c28f5cull, 0x3ffec3bd11885207ull }, + { 0x401b5d2f1a9fbe77ull, 0x3ffec4565b9d1bd6ull }, + { 0x401b5e353f7ced92ull, 0x3ffec4ef9ff58273ull }, + { 0x401b5f3b645a1cacull, 0x3ffec588de91f3bfull }, + { 0x401b604189374bc7ull, 0x3ffec6221772dd8full }, + { 0x401b6147ae147ae1ull, 0x3ffec6bb4a98ada7ull }, + { 0x401b624dd2f1a9fcull, 0x3ffec7547803d1c5ull }, + { 0x401b6353f7ced917ull, 0x3ffec7ed9fb4b795ull }, + { 0x401b645a1cac0831ull, 0x3ffec886c1abccbaull }, + { 0x401b65604189374cull, 0x3ffec91fdde97ecbull }, + { 0x401b666666666667ull, 0x3ffec9b8f46e3b51ull }, + { 0x401b676c8b439581ull, 0x3ffeca52053a6fc8ull }, + { 0x401b6872b020c49cull, 0x3ffecaeb104e89a4ull }, + { 0x401b6978d4fdf3b6ull, 0x3ffecb8415aaf646ull }, + { 0x401b6a7ef9db22d1ull, 0x3ffecc1d1550230aull }, + { 0x401b6b851eb851ecull, 0x3ffeccb60f3e7d3bull }, + { 0x401b6c8b43958106ull, 0x3ffecd4f03767219ull }, + { 0x401b6d916872b021ull, 0x3ffecde7f1f86ed8ull }, + { 0x401b6e978d4fdf3cull, 0x3ffece80dac4e09full }, + { 0x401b6f9db22d0e56ull, 0x3ffecf19bddc348aull }, + { 0x401b70a3d70a3d71ull, 0x3ffecfb29b3ed7a8ull }, + { 0x401b71a9fbe76c8bull, 0x3ffed04b72ed36fbull }, + { 0x401b72b020c49ba6ull, 0x3ffed0e444e7bf7cull }, + { 0x401b73b645a1cac1ull, 0x3ffed17d112ede14ull }, + { 0x401b74bc6a7ef9dbull, 0x3ffed215d7c2ffa1ull }, + { 0x401b75c28f5c28f6ull, 0x3ffed2ae98a490f7ull }, + { 0x401b76c8b4395811ull, 0x3ffed34753d3fedaull }, + { 0x401b77ced916872bull, 0x3ffed3e00951b605ull }, + { 0x401b78d4fdf3b646ull, 0x3ffed478b91e2325ull }, + { 0x401b79db22d0e560ull, 0x3ffed5116339b2dcull }, + { 0x401b7ae147ae147bull, 0x3ffed5aa07a4d1bfull }, + { 0x401b7be76c8b4396ull, 0x3ffed642a65fec58ull }, + { 0x401b7ced916872b0ull, 0x3ffed6db3f6b6f24ull }, + { 0x401b7df3b645a1cbull, 0x3ffed773d2c7c693ull }, + { 0x401b7ef9db22d0e6ull, 0x3ffed80c60755f0bull }, + { 0x401b800000000000ull, 0x3ffed8a4e874a4e4ull }, + { 0x401b810624dd2f1bull, 0x3ffed93d6ac6046bull }, + { 0x401b820c49ba5e35ull, 0x3ffed9d5e769e9e1ull }, + { 0x401b83126e978d50ull, 0x3ffeda6e5e60c17aull }, + { 0x401b84189374bc6bull, 0x3ffedb06cfaaf75full }, + { 0x401b851eb851eb85ull, 0x3ffedb9f3b48f7aaull }, + { 0x401b8624dd2f1aa0ull, 0x3ffedc37a13b2e6full }, + { 0x401b872b020c49bbull, 0x3ffedcd0018207b0ull }, + { 0x401b883126e978d5ull, 0x3ffedd685c1def65ull }, + { 0x401b89374bc6a7f0ull, 0x3ffede00b10f517aull }, + { 0x401b8a3d70a3d70aull, 0x3ffede99005699d0ull }, + { 0x401b8b4395810625ull, 0x3ffedf3149f4343bull }, + { 0x401b8c49ba5e3540ull, 0x3ffedfc98de88c83ull }, + { 0x401b8d4fdf3b645aull, 0x3ffee061cc340e63ull }, + { 0x401b8e5604189375ull, 0x3ffee0fa04d7258cull }, + { 0x401b8f5c28f5c290ull, 0x3ffee19237d23da2ull }, + { 0x401b90624dd2f1aaull, 0x3ffee22a6525c23bull }, + { 0x401b916872b020c5ull, 0x3ffee2c28cd21ee6ull }, + { 0x401b926e978d4fdfull, 0x3ffee35aaed7bf21ull }, + { 0x401b9374bc6a7efaull, 0x3ffee3f2cb370e61ull }, + { 0x401b947ae147ae15ull, 0x3ffee48ae1f0780eull }, + { 0x401b95810624dd2full, 0x3ffee522f3046783ull }, + { 0x401b96872b020c4aull, 0x3ffee5bafe734811ull }, + { 0x401b978d4fdf3b65ull, 0x3ffee653043d84fdull }, + { 0x401b989374bc6a7full, 0x3ffee6eb0463897dull }, + { 0x401b99999999999aull, 0x3ffee782fee5c0c0ull }, + { 0x401b9a9fbe76c8b4ull, 0x3ffee81af3c495e4ull }, + { 0x401b9ba5e353f7cfull, 0x3ffee8b2e30073ffull }, + { 0x401b9cac083126eaull, 0x3ffee94acc99c619ull }, + { 0x401b9db22d0e5604ull, 0x3ffee9e2b090f72full }, + { 0x401b9eb851eb851full, 0x3ffeea7a8ee67231ull }, + { 0x401b9fbe76c8b43aull, 0x3ffeeb12679aa205ull }, + { 0x401ba0c49ba5e354ull, 0x3ffeebaa3aadf182ull }, + { 0x401ba1cac083126full, 0x3ffeec420820cb77ull }, + { 0x401ba2d0e5604189ull, 0x3ffeecd9cff39aa5ull }, + { 0x401ba3d70a3d70a4ull, 0x3ffeed719226c9c1ull }, + { 0x401ba4dd2f1a9fbfull, 0x3ffeee094ebac374ull }, + { 0x401ba5e353f7ced9ull, 0x3ffeeea105aff25bull }, + { 0x401ba6e978d4fdf4ull, 0x3ffeef38b706c10bull }, + { 0x401ba7ef9db22d0eull, 0x3ffeefd062bf9a06ull }, + { 0x401ba8f5c28f5c29ull, 0x3ffef06808dae7c9ull }, + { 0x401ba9fbe76c8b44ull, 0x3ffef0ffa95914c1ull }, + { 0x401bab020c49ba5eull, 0x3ffef197443a8b50ull }, + { 0x401bac083126e979ull, 0x3ffef22ed97fb5ceull }, + { 0x401bad0e56041894ull, 0x3ffef2c66928fe85ull }, + { 0x401bae147ae147aeull, 0x3ffef35df336cfb4ull }, + { 0x401baf1a9fbe76c9ull, 0x3ffef3f577a9938eull }, + { 0x401bb020c49ba5e3ull, 0x3ffef48cf681b43cull }, + { 0x401bb126e978d4feull, 0x3ffef5246fbf9bd9ull }, + { 0x401bb22d0e560419ull, 0x3ffef5bbe363b474ull }, + { 0x401bb33333333333ull, 0x3ffef653516e6812ull }, + { 0x401bb4395810624eull, 0x3ffef6eab9e020abull }, + { 0x401bb53f7ced9169ull, 0x3ffef7821cb9482cull }, + { 0x401bb645a1cac083ull, 0x3ffef81979fa4876ull }, + { 0x401bb74bc6a7ef9eull, 0x3ffef8b0d1a38b5eull }, + { 0x401bb851eb851eb8ull, 0x3ffef94823b57aaeull }, + { 0x401bb95810624dd3ull, 0x3ffef9df70308023ull }, + { 0x401bba5e353f7ceeull, 0x3ffefa76b715056full }, + { 0x401bbb645a1cac08ull, 0x3ffefb0df8637439ull }, + { 0x401bbc6a7ef9db23ull, 0x3ffefba5341c361bull }, + { 0x401bbd70a3d70a3eull, 0x3ffefc3c6a3fb4a4ull }, + { 0x401bbe76c8b43958ull, 0x3ffefcd39ace5958ull }, + { 0x401bbf7ced916873ull, 0x3ffefd6ac5c88dadull }, + { 0x401bc083126e978dull, 0x3ffefe01eb2ebb10ull }, + { 0x401bc189374bc6a8ull, 0x3ffefe990b014ae1ull }, + { 0x401bc28f5c28f5c3ull, 0x3ffeff302540a675ull }, + { 0x401bc395810624ddull, 0x3ffeffc739ed3714ull }, + { 0x401bc49ba5e353f8ull, 0x3fff005e490765fcull }, + { 0x401bc5a1cac08313ull, 0x3fff00f5528f9c5eull }, + { 0x401bc6a7ef9db22dull, 0x3fff018c56864360ull }, + { 0x401bc7ae147ae148ull, 0x3fff022354ebc41dull }, + { 0x401bc8b439581062ull, 0x3fff02ba4dc087a3ull }, + { 0x401bc9ba5e353f7dull, 0x3fff03514104f6f5ull }, + { 0x401bcac083126e98ull, 0x3fff03e82eb97b0bull }, + { 0x401bcbc6a7ef9db2ull, 0x3fff047f16de7cd1ull }, + { 0x401bcccccccccccdull, 0x3fff0515f9746527ull }, + { 0x401bcdd2f1a9fbe8ull, 0x3fff05acd67b9ce1ull }, + { 0x401bced916872b02ull, 0x3fff0643adf48cc9ull }, + { 0x401bcfdf3b645a1dull, 0x3fff06da7fdf9d9dull }, + { 0x401bd0e560418937ull, 0x3fff07714c3d380dull }, + { 0x401bd1eb851eb852ull, 0x3fff0808130dc4c2ull }, + { 0x401bd2f1a9fbe76dull, 0x3fff089ed451ac56ull }, + { 0x401bd3f7ced91687ull, 0x3fff093590095756ull }, + { 0x401bd4fdf3b645a2ull, 0x3fff09cc46352e49ull }, + { 0x401bd604189374bdull, 0x3fff0a62f6d599a6ull }, + { 0x401bd70a3d70a3d7ull, 0x3fff0af9a1eb01d9ull }, + { 0x401bd810624dd2f2ull, 0x3fff0b904775cf44ull }, + { 0x401bd916872b020cull, 0x3fff0c26e7766a3cull }, + { 0x401bda1cac083127ull, 0x3fff0cbd81ed3b0cull }, + { 0x401bdb22d0e56042ull, 0x3fff0d5416daa9f3ull }, + { 0x401bdc28f5c28f5cull, 0x3fff0deaa63f1f22ull }, + { 0x401bdd2f1a9fbe77ull, 0x3fff0e81301b02c3ull }, + { 0x401bde353f7ced92ull, 0x3fff0f17b46ebcf2ull }, + { 0x401bdf3b645a1cacull, 0x3fff0fae333ab5bdull }, + { 0x401be04189374bc7ull, 0x3fff1044ac7f552dull }, + { 0x401be147ae147ae1ull, 0x3fff10db203d0339ull }, + { 0x401be24dd2f1a9fcull, 0x3fff11718e7427d1ull }, + { 0x401be353f7ced917ull, 0x3fff1207f7252ad7ull }, + { 0x401be45a1cac0831ull, 0x3fff129e5a507422ull }, + { 0x401be5604189374cull, 0x3fff1334b7f66b7full }, + { 0x401be66666666667ull, 0x3fff13cb101778afull }, + { 0x401be76c8b439581ull, 0x3fff146162b40366ull }, + { 0x401be872b020c49cull, 0x3fff14f7afcc734eull }, + { 0x401be978d4fdf3b6ull, 0x3fff158df7613005ull }, + { 0x401bea7ef9db22d1ull, 0x3fff16243972a11full }, + { 0x401beb851eb851ecull, 0x3fff16ba76012e23ull }, + { 0x401bec8b43958106ull, 0x3fff1750ad0d3e8cull }, + { 0x401bed916872b021ull, 0x3fff17e6de9739ccull }, + { 0x401bee978d4fdf3cull, 0x3fff187d0a9f8749ull }, + { 0x401bef9db22d0e56ull, 0x3fff191331268e5bull }, + { 0x401bf0a3d70a3d71ull, 0x3fff19a9522cb652ull }, + { 0x401bf1a9fbe76c8bull, 0x3fff1a3f6db26671ull }, + { 0x401bf2b020c49ba6ull, 0x3fff1ad583b805f0ull }, + { 0x401bf3b645a1cac1ull, 0x3fff1b6b943dfbfcull }, + { 0x401bf4bc6a7ef9dbull, 0x3fff1c019f44afb5ull }, + { 0x401bf5c28f5c28f6ull, 0x3fff1c97a4cc8833ull }, + { 0x401bf6c8b4395811ull, 0x3fff1d2da4d5ec7eull }, + { 0x401bf7ced916872bull, 0x3fff1dc39f614396ull }, + { 0x401bf8d4fdf3b646ull, 0x3fff1e59946ef471ull }, + { 0x401bf9db22d0e560ull, 0x3fff1eef83ff65f5ull }, + { 0x401bfae147ae147bull, 0x3fff1f856e12ff00ull }, + { 0x401bfbe76c8b4396ull, 0x3fff201b52aa2666ull }, + { 0x401bfced916872b0ull, 0x3fff20b131c542ebull }, + { 0x401bfdf3b645a1cbull, 0x3fff21470b64bb4cull }, + { 0x401bfef9db22d0e6ull, 0x3fff21dcdf88f63aull }, + { 0x401c000000000000ull, 0x3fff2272ae325a57ull }, + { 0x401c010624dd2f1bull, 0x3fff230877614e40ull }, + { 0x401c020c49ba5e35ull, 0x3fff239e3b163881ull }, + { 0x401c03126e978d50ull, 0x3fff2433f9517f9full }, + { 0x401c04189374bc6bull, 0x3fff24c9b2138a12ull }, + { 0x401c051eb851eb85ull, 0x3fff255f655cbe44ull }, + { 0x401c0624dd2f1aa0ull, 0x3fff25f5132d8299ull }, + { 0x401c072b020c49bbull, 0x3fff268abb863d67ull }, + { 0x401c083126e978d5ull, 0x3fff27205e6754f8ull }, + { 0x401c09374bc6a7f0ull, 0x3fff27b5fbd12f8dull }, + { 0x401c0a3d70a3d70aull, 0x3fff284b93c4335aull }, + { 0x401c0b4395810625ull, 0x3fff28e12640c688ull }, + { 0x401c0c49ba5e3540ull, 0x3fff2976b3474f37ull }, + { 0x401c0d4fdf3b645aull, 0x3fff2a0c3ad83377ull }, + { 0x401c0e5604189375ull, 0x3fff2aa1bcf3d953ull }, + { 0x401c0f5c28f5c290ull, 0x3fff2b37399aa6c6ull }, + { 0x401c10624dd2f1aaull, 0x3fff2bccb0cd01c2ull }, + { 0x401c116872b020c5ull, 0x3fff2c62228b502dull }, + { 0x401c126e978d4fdfull, 0x3fff2cf78ed5f7e3ull }, + { 0x401c1374bc6a7efaull, 0x3fff2d8cf5ad5eb3ull }, + { 0x401c147ae147ae15ull, 0x3fff2e225711ea64ull }, + { 0x401c15810624dd2full, 0x3fff2eb7b30400adull }, + { 0x401c16872b020c4aull, 0x3fff2f4d0984073eull }, + { 0x401c178d4fdf3b65ull, 0x3fff2fe25a9263bcull }, + { 0x401c189374bc6a7full, 0x3fff3077a62f7bbdull }, + { 0x401c19999999999aull, 0x3fff310cec5bb4d0ull }, + { 0x401c1a9fbe76c8b4ull, 0x3fff31a22d177476ull }, + { 0x401c1ba5e353f7cfull, 0x3fff323768632027ull }, + { 0x401c1cac083126eaull, 0x3fff32cc9e3f1d50ull }, + { 0x401c1db22d0e5604ull, 0x3fff3361ceabd14full }, + { 0x401c1eb851eb851full, 0x3fff33f6f9a9a17dull }, + { 0x401c1fbe76c8b43aull, 0x3fff348c1f38f324ull }, + { 0x401c20c49ba5e354ull, 0x3fff35213f5a2b83ull }, + { 0x401c21cac083126full, 0x3fff35b65a0dafcfull }, + { 0x401c22d0e5604189ull, 0x3fff364b6f53e531ull }, + { 0x401c23d70a3d70a4ull, 0x3fff36e07f2d30caull }, + { 0x401c24dd2f1a9fbfull, 0x3fff37758999f7abull }, + { 0x401c25e353f7ced9ull, 0x3fff380a8e9a9edeull }, + { 0x401c26e978d4fdf4ull, 0x3fff389f8e2f8b60ull }, + { 0x401c27ef9db22d0full, 0x3fff393488592223ull }, + { 0x401c28f5c28f5c29ull, 0x3fff39c97d17c80full }, + { 0x401c29fbe76c8b44ull, 0x3fff3a5e6c6be201ull }, + { 0x401c2b020c49ba5eull, 0x3fff3af35655d4caull }, + { 0x401c2c083126e979ull, 0x3fff3b883ad60530ull }, + { 0x401c2d0e56041894ull, 0x3fff3c1d19ecd7f0ull }, + { 0x401c2e147ae147aeull, 0x3fff3cb1f39ab1b8ull }, + { 0x401c2f1a9fbe76c9ull, 0x3fff3d46c7dff730ull }, + { 0x401c3020c49ba5e3ull, 0x3fff3ddb96bd0cf1ull }, + { 0x401c3126e978d4feull, 0x3fff3e706032578cull }, + { 0x401c322d0e560419ull, 0x3fff3f0524403b85ull }, + { 0x401c333333333333ull, 0x3fff3f99e2e71d55ull }, + { 0x401c34395810624eull, 0x3fff402e9c27616bull }, + { 0x401c353f7ced9169ull, 0x3fff40c350016c2cull }, + { 0x401c3645a1cac083ull, 0x3fff4157fe75a1efull }, + { 0x401c374bc6a7ef9eull, 0x3fff41eca7846704ull }, + { 0x401c3851eb851eb8ull, 0x3fff42814b2e1facull }, + { 0x401c395810624dd3ull, 0x3fff4315e9733021ull }, + { 0x401c3a5e353f7ceeull, 0x3fff43aa8253fc90ull }, + { 0x401c3b645a1cac08ull, 0x3fff443f15d0e919ull }, + { 0x401c3c6a7ef9db23ull, 0x3fff44d3a3ea59d6ull }, + { 0x401c3d70a3d70a3eull, 0x3fff45682ca0b2d3ull }, + { 0x401c3e76c8b43958ull, 0x3fff45fcaff45810ull }, + { 0x401c3f7ced916873ull, 0x3fff46912de5ad87ull }, + { 0x401c4083126e978dull, 0x3fff4725a6751720ull }, + { 0x401c4189374bc6a8ull, 0x3fff47ba19a2f8bfull }, + { 0x401c428f5c28f5c3ull, 0x3fff484e876fb639ull }, + { 0x401c4395810624ddull, 0x3fff48e2efdbb35aull }, + { 0x401c449ba5e353f8ull, 0x3fff497752e753e1ull }, + { 0x401c45a1cac08313ull, 0x3fff4a0bb092fb86ull }, + { 0x401c46a7ef9db22dull, 0x3fff4aa008df0df1ull }, + { 0x401c47ae147ae148ull, 0x3fff4b345bcbeec4ull }, + { 0x401c48b439581062ull, 0x3fff4bc8a95a0194ull }, + { 0x401c49ba5e353f7dull, 0x3fff4c5cf189a9ebull }, + { 0x401c4ac083126e98ull, 0x3fff4cf1345b4b48ull }, + { 0x401c4bc6a7ef9db2ull, 0x3fff4d8571cf4920ull }, + { 0x401c4ccccccccccdull, 0x3fff4e19a9e606deull }, + { 0x401c4dd2f1a9fbe8ull, 0x3fff4eaddc9fe7dfull }, + { 0x401c4ed916872b02ull, 0x3fff4f4209fd4f7aull }, + { 0x401c4fdf3b645a1dull, 0x3fff4fd631fea0f6ull }, + { 0x401c50e560418937ull, 0x3fff506a54a43f94ull }, + { 0x401c51eb851eb852ull, 0x3fff50fe71ee8e87ull }, + { 0x401c52f1a9fbe76dull, 0x3fff519289ddf0f8ull }, + { 0x401c53f7ced91687ull, 0x3fff52269c72ca06ull }, + { 0x401c54fdf3b645a2ull, 0x3fff52baa9ad7cc5ull }, + { 0x401c5604189374bdull, 0x3fff534eb18e6c3dull }, + { 0x401c570a3d70a3d7ull, 0x3fff53e2b415fb6dull }, + { 0x401c5810624dd2f2ull, 0x3fff5476b1448d4aull }, + { 0x401c5916872b020cull, 0x3fff550aa91a84baull }, + { 0x401c5a1cac083127ull, 0x3fff559e9b98449dull }, + { 0x401c5b22d0e56042ull, 0x3fff563288be2fc7ull }, + { 0x401c5c28f5c28f5cull, 0x3fff56c6708ca8feull }, + { 0x401c5d2f1a9fbe77ull, 0x3fff575a53041303ull }, + { 0x401c5e353f7ced92ull, 0x3fff57ee3024d088ull }, + { 0x401c5f3b645a1cacull, 0x3fff588207ef4435ull }, + { 0x401c604189374bc7ull, 0x3fff5915da63d0a9ull }, + { 0x401c6147ae147ae1ull, 0x3fff59a9a782d876ull }, + { 0x401c624dd2f1a9fcull, 0x3fff5a3d6f4cbe27ull }, + { 0x401c6353f7ced917ull, 0x3fff5ad131c1e438ull }, + { 0x401c645a1cac0831ull, 0x3fff5b64eee2ad1cull }, + { 0x401c65604189374cull, 0x3fff5bf8a6af7b3eull }, + { 0x401c666666666667ull, 0x3fff5c8c5928b0f9ull }, + { 0x401c676c8b439581ull, 0x3fff5d20064eb0a2ull }, + { 0x401c6872b020c49cull, 0x3fff5db3ae21dc82ull }, + { 0x401c6978d4fdf3b6ull, 0x3fff5e4750a296d5ull }, + { 0x401c6a7ef9db22d1ull, 0x3fff5edaedd141d2ull }, + { 0x401c6b851eb851ecull, 0x3fff5f6e85ae3f9full }, + { 0x401c6c8b43958106ull, 0x3fff60021839f25aull }, + { 0x401c6d916872b021ull, 0x3fff6095a574bc19ull }, + { 0x401c6e978d4fdf3cull, 0x3fff61292d5efee4ull }, + { 0x401c6f9db22d0e56ull, 0x3fff61bcaff91cb8ull }, + { 0x401c70a3d70a3d71ull, 0x3fff62502d43778bull }, + { 0x401c71a9fbe76c8bull, 0x3fff62e3a53e7145ull }, + { 0x401c72b020c49ba6ull, 0x3fff637717ea6bc5ull }, + { 0x401c73b645a1cac1ull, 0x3fff640a8547c8e0ull }, + { 0x401c74bc6a7ef9dbull, 0x3fff649ded56ea5eull }, + { 0x401c75c28f5c28f6ull, 0x3fff6531501831ffull }, + { 0x401c76c8b4395811ull, 0x3fff65c4ad8c0178ull }, + { 0x401c77ced916872bull, 0x3fff665805b2ba71ull }, + { 0x401c78d4fdf3b646ull, 0x3fff66eb588cbe8bull }, + { 0x401c79db22d0e560ull, 0x3fff677ea61a6f5aull }, + { 0x401c7ae147ae147bull, 0x3fff6811ee5c2e6aull }, + { 0x401c7be76c8b4396ull, 0x3fff68a531525d39ull }, + { 0x401c7ced916872b0ull, 0x3fff69386efd5d3cull }, + { 0x401c7df3b645a1cbull, 0x3fff69cba75d8fe0ull }, + { 0x401c7ef9db22d0e6ull, 0x3fff6a5eda735684ull }, + { 0x401c800000000000ull, 0x3fff6af2083f127dull }, + { 0x401c810624dd2f1bull, 0x3fff6b8530c12517ull }, + { 0x401c820c49ba5e35ull, 0x3fff6c1853f9ef93ull }, + { 0x401c83126e978d50ull, 0x3fff6cab71e9d328ull }, + { 0x401c84189374bc6bull, 0x3fff6d3e8a913100ull }, + { 0x401c851eb851eb85ull, 0x3fff6dd19df06a3eull }, + { 0x401c8624dd2f1aa0ull, 0x3fff6e64ac07dff8ull }, + { 0x401c872b020c49bbull, 0x3fff6ef7b4d7f33cull }, + { 0x401c883126e978d5ull, 0x3fff6f8ab8610509ull }, + { 0x401c89374bc6a7f0ull, 0x3fff701db6a3765aull }, + { 0x401c8a3d70a3d70aull, 0x3fff70b0af9fa81aull }, + { 0x401c8b4395810625ull, 0x3fff7143a355fb2dull }, + { 0x401c8c49ba5e3540ull, 0x3fff71d691c6d06aull }, + { 0x401c8d4fdf3b645aull, 0x3fff72697af2889full }, + { 0x401c8e5604189375ull, 0x3fff72fc5ed98491ull }, + { 0x401c8f5c28f5c290ull, 0x3fff738f3d7c24f7ull }, + { 0x401c90624dd2f1aaull, 0x3fff742216daca81ull }, + { 0x401c916872b020c5ull, 0x3fff74b4eaf5d5d3ull }, + { 0x401c926e978d4fdfull, 0x3fff7547b9cda786ull }, + { 0x401c9374bc6a7efaull, 0x3fff75da8362a02bull }, + { 0x401c947ae147ae15ull, 0x3fff766d47b52047ull }, + { 0x401c95810624dd2full, 0x3fff770006c58852ull }, + { 0x401c96872b020c4aull, 0x3fff7792c09438c0ull }, + { 0x401c978d4fdf3b65ull, 0x3fff7825752191f4ull }, + { 0x401c989374bc6a7full, 0x3fff78b8246df44bull }, + { 0x401c99999999999aull, 0x3fff794ace79c017ull }, + { 0x401c9a9fbe76c8b4ull, 0x3fff79dd7345559dull }, + { 0x401c9ba5e353f7cfull, 0x3fff7a7012d1151cull }, + { 0x401c9cac083126eaull, 0x3fff7b02ad1d5ec4ull }, + { 0x401c9db22d0e5604ull, 0x3fff7b95422a92beull }, + { 0x401c9eb851eb851full, 0x3fff7c27d1f91128ull }, + { 0x401c9fbe76c8b43aull, 0x3fff7cba5c893a14ull }, + { 0x401ca0c49ba5e354ull, 0x3fff7d4ce1db6d8cull }, + { 0x401ca1cac083126full, 0x3fff7ddf61f00b8eull }, + { 0x401ca2d0e5604189ull, 0x3fff7e71dcc7740full }, + { 0x401ca3d70a3d70a4ull, 0x3fff7f04526206faull }, + { 0x401ca4dd2f1a9fbfull, 0x3fff7f96c2c0242cull }, + { 0x401ca5e353f7ced9ull, 0x3fff80292de22b7dull }, + { 0x401ca6e978d4fdf4ull, 0x3fff80bb93c87cb7ull }, + { 0x401ca7ef9db22d0full, 0x3fff814df473779cull }, + { 0x401ca8f5c28f5c29ull, 0x3fff81e04fe37be2ull }, + { 0x401ca9fbe76c8b44ull, 0x3fff8272a618e935ull }, + { 0x401cab020c49ba5eull, 0x3fff8304f7141f38ull }, + { 0x401cac083126e979ull, 0x3fff839742d57d82ull }, + { 0x401cad0e56041894ull, 0x3fff8429895d63a3ull }, + { 0x401cae147ae147aeull, 0x3fff84bbcaac311bull }, + { 0x401caf1a9fbe76c9ull, 0x3fff854e06c24566ull }, + { 0x401cb020c49ba5e3ull, 0x3fff85e03d9ffff1ull }, + { 0x401cb126e978d4feull, 0x3fff86726f45c023ull }, + { 0x401cb22d0e560419ull, 0x3fff87049bb3e556ull }, + { 0x401cb33333333333ull, 0x3fff8796c2eaced8ull }, + { 0x401cb4395810624eull, 0x3fff8828e4eadbf2ull }, + { 0x401cb53f7ced9169ull, 0x3fff88bb01b46be0ull }, + { 0x401cb645a1cac083ull, 0x3fff894d1947ddd1ull }, + { 0x401cb74bc6a7ef9eull, 0x3fff89df2ba590efull }, + { 0x401cb851eb851eb8ull, 0x3fff8a7138cde457ull }, + { 0x401cb95810624dd3ull, 0x3fff8b0340c1371cull }, + { 0x401cba5e353f7ceeull, 0x3fff8b95437fe847ull }, + { 0x401cbb645a1cac08ull, 0x3fff8c27410a56d6ull }, + { 0x401cbc6a7ef9db23ull, 0x3fff8cb93960e1c0ull }, + { 0x401cbd70a3d70a3eull, 0x3fff8d4b2c83e7efull }, + { 0x401cbe76c8b43958ull, 0x3fff8ddd1a73c841ull }, + { 0x401cbf7ced916873ull, 0x3fff8e6f0330e190ull }, + { 0x401cc083126e978dull, 0x3fff8f00e6bb92a5ull }, + { 0x401cc189374bc6a8ull, 0x3fff8f92c5143a44ull }, + { 0x401cc28f5c28f5c3ull, 0x3fff90249e3b3725ull }, + { 0x401cc395810624ddull, 0x3fff90b67230e7f4ull }, + { 0x401cc49ba5e353f8ull, 0x3fff914840f5ab57ull }, + { 0x401cc5a1cac08313ull, 0x3fff91da0a89dfe7ull }, + { 0x401cc6a7ef9db22dull, 0x3fff926bceede432ull }, + { 0x401cc7ae147ae148ull, 0x3fff92fd8e2216bfull }, + { 0x401cc8b439581062ull, 0x3fff938f4826d608ull }, + { 0x401cc9ba5e353f7dull, 0x3fff9420fcfc8080ull }, + { 0x401ccac083126e98ull, 0x3fff94b2aca3748dull }, + { 0x401ccbc6a7ef9db2ull, 0x3fff9544571c108cull }, + { 0x401ccccccccccccdull, 0x3fff95d5fc66b2d1ull }, + { 0x401ccdd2f1a9fbe8ull, 0x3fff96679c83b9a4ull }, + { 0x401cced916872b02ull, 0x3fff96f937738344ull }, + { 0x401ccfdf3b645a1dull, 0x3fff978acd366de7ull }, + { 0x401cd0e560418937ull, 0x3fff981c5dccd7b6ull }, + { 0x401cd1eb851eb852ull, 0x3fff98ade9371ed3ull }, + { 0x401cd2f1a9fbe76dull, 0x3fff993f6f75a155ull }, + { 0x401cd3f7ced91687ull, 0x3fff99d0f088bd48ull }, + { 0x401cd4fdf3b645a2ull, 0x3fff9a626c70d0afull }, + { 0x401cd604189374bdull, 0x3fff9af3e32e3983ull }, + { 0x401cd70a3d70a3d7ull, 0x3fff9b8554c155b1ull }, + { 0x401cd810624dd2f2ull, 0x3fff9c16c12a8320ull }, + { 0x401cd916872b020cull, 0x3fff9ca8286a1fa9ull }, + { 0x401cda1cac083127ull, 0x3fff9d398a80891dull }, + { 0x401cdb22d0e56042ull, 0x3fff9dcae76e1d44ull }, + { 0x401cdc28f5c28f5cull, 0x3fff9e5c3f3339d9ull }, + { 0x401cdd2f1a9fbe77ull, 0x3fff9eed91d03c90ull }, + { 0x401cde353f7ced92ull, 0x3fff9f7edf458311ull }, + { 0x401cdf3b645a1cacull, 0x3fffa01027936afbull }, + { 0x401ce04189374bc7ull, 0x3fffa0a16aba51e3ull }, + { 0x401ce147ae147ae1ull, 0x3fffa132a8ba9553ull }, + { 0x401ce24dd2f1a9fcull, 0x3fffa1c3e19492ceull }, + { 0x401ce353f7ced917ull, 0x3fffa2551548a7c8ull }, + { 0x401ce45a1cac0831ull, 0x3fffa2e643d731b1ull }, + { 0x401ce5604189374cull, 0x3fffa3776d408deaull }, + { 0x401ce66666666667ull, 0x3fffa408918519cdull }, + { 0x401ce76c8b439581ull, 0x3fffa499b0a532a9ull }, + { 0x401ce872b020c49cull, 0x3fffa52acaa135c5ull }, + { 0x401ce978d4fdf3b6ull, 0x3fffa5bbdf798059ull }, + { 0x401cea7ef9db22d1ull, 0x3fffa64cef2e6f9aull }, + { 0x401ceb851eb851ecull, 0x3fffa6ddf9c060aeull }, + { 0x401cec8b43958106ull, 0x3fffa76eff2fb0b2ull }, + { 0x401ced916872b021ull, 0x3fffa7ffff7cbcbcull }, + { 0x401cee978d4fdf3cull, 0x3fffa890faa7e1d4ull }, + { 0x401cef9db22d0e56ull, 0x3fffa921f0b17cfaull }, + { 0x401cf0a3d70a3d71ull, 0x3fffa9b2e199eb26ull }, + { 0x401cf1a9fbe76c8bull, 0x3fffaa43cd618943ull }, + { 0x401cf2b020c49ba6ull, 0x3fffaad4b408b435ull }, + { 0x401cf3b645a1cac1ull, 0x3fffab65958fc8d2ull }, + { 0x401cf4bc6a7ef9dbull, 0x3fffabf671f723ebull }, + { 0x401cf5c28f5c28f6ull, 0x3fffac87493f2244ull }, + { 0x401cf6c8b4395811ull, 0x3fffad181b682098ull }, + { 0x401cf7ced916872bull, 0x3fffada8e8727b99ull }, + { 0x401cf8d4fdf3b646ull, 0x3fffae39b05e8fedull }, + { 0x401cf9db22d0e560ull, 0x3fffaeca732cba33ull }, + { 0x401cfae147ae147bull, 0x3fffaf5b30dd56ffull }, + { 0x401cfbe76c8b4396ull, 0x3fffafebe970c2daull }, + { 0x401cfced916872b0ull, 0x3fffb07c9ce75a44ull }, + { 0x401cfdf3b645a1cbull, 0x3fffb10d4b4179b4ull }, + { 0x401cfef9db22d0e6ull, 0x3fffb19df47f7d97ull }, + { 0x401d000000000000ull, 0x3fffb22e98a1c24eull }, + { 0x401d010624dd2f1bull, 0x3fffb2bf37a8a434ull }, + { 0x401d020c49ba5e35ull, 0x3fffb34fd1947f97ull }, + { 0x401d03126e978d50ull, 0x3fffb3e06665b0bdull }, + { 0x401d04189374bc6bull, 0x3fffb470f61c93e2ull }, + { 0x401d051eb851eb85ull, 0x3fffb50180b98536ull }, + { 0x401d0624dd2f1aa0ull, 0x3fffb592063ce0e4ull }, + { 0x401d072b020c49bbull, 0x3fffb62286a70308ull }, + { 0x401d083126e978d5ull, 0x3fffb6b301f847b7ull }, + { 0x401d09374bc6a7f0ull, 0x3fffb74378310afeull }, + { 0x401d0a3d70a3d70aull, 0x3fffb7d3e951a8dbull }, + { 0x401d0b4395810625ull, 0x3fffb864555a7d48ull }, + { 0x401d0c49ba5e3540ull, 0x3fffb8f4bc4be431ull }, + { 0x401d0d4fdf3b645aull, 0x3fffb9851e26397bull }, + { 0x401d0e5604189375ull, 0x3fffba157ae9d900ull }, + { 0x401d0f5c28f5c290ull, 0x3fffbaa5d2971e91ull }, + { 0x401d10624dd2f1aaull, 0x3fffbb36252e65f3ull }, + { 0x401d116872b020c5ull, 0x3fffbbc672b00ae5ull }, + { 0x401d126e978d4fdfull, 0x3fffbc56bb1c691aull }, + { 0x401d1374bc6a7efaull, 0x3fffbce6fe73dc3cull }, + { 0x401d147ae147ae15ull, 0x3fffbd773cb6bfebull }, + { 0x401d15810624dd2full, 0x3fffbe0775e56fbcull }, + { 0x401d16872b020c4aull, 0x3fffbe97aa00473eull }, + { 0x401d178d4fdf3b65ull, 0x3fffbf27d907a1f3ull }, + { 0x401d189374bc6a7full, 0x3fffbfb802fbdb53ull }, + { 0x401d19999999999aull, 0x3fffc04827dd4ed1ull }, + { 0x401d1a9fbe76c8b4ull, 0x3fffc0d847ac57cfull }, + { 0x401d1ba5e353f7cfull, 0x3fffc168626951adull }, + { 0x401d1cac083126eaull, 0x3fffc1f8781497bdull }, + { 0x401d1db22d0e5604ull, 0x3fffc28888ae8546ull }, + { 0x401d1eb851eb851full, 0x3fffc3189437758aull }, + { 0x401d1fbe76c8b43aull, 0x3fffc3a89aafc3beull }, + { 0x401d20c49ba5e354ull, 0x3fffc4389c17cb0eull }, + { 0x401d21cac083126full, 0x3fffc4c8986fe69dull }, + { 0x401d22d0e5604189ull, 0x3fffc5588fb87184ull }, + { 0x401d23d70a3d70a4ull, 0x3fffc5e881f1c6d3ull }, + { 0x401d24dd2f1a9fbfull, 0x3fffc6786f1c418full }, + { 0x401d25e353f7ced9ull, 0x3fffc70857383cb4ull }, + { 0x401d26e978d4fdf4ull, 0x3fffc7983a461336ull }, + { 0x401d27ef9db22d0full, 0x3fffc82818461ffeull }, + { 0x401d28f5c28f5c29ull, 0x3fffc8b7f138bdeaull }, + { 0x401d29fbe76c8b44ull, 0x3fffc947c51e47d1ull }, + { 0x401d2b020c49ba5eull, 0x3fffc9d793f7187eull }, + { 0x401d2c083126e979ull, 0x3fffca675dc38ab7ull }, + { 0x401d2d0e56041894ull, 0x3fffcaf72283f932ull }, + { 0x401d2e147ae147aeull, 0x3fffcb86e238bea0ull }, + { 0x401d2f1a9fbe76c9ull, 0x3fffcc169ce235a8ull }, + { 0x401d3020c49ba5e3ull, 0x3fffcca65280b8e5ull }, + { 0x401d3126e978d4feull, 0x3fffcd360314a2ebull }, + { 0x401d322d0e560419ull, 0x3fffcdc5ae9e4e43ull }, + { 0x401d333333333333ull, 0x3fffce55551e156eull }, + { 0x401d34395810624eull, 0x3fffcee4f69452e1ull }, + { 0x401d353f7ced9169ull, 0x3fffcf749301610bull }, + { 0x401d3645a1cac083ull, 0x3fffd0042a659a4eull }, + { 0x401d374bc6a7ef9eull, 0x3fffd093bcc15904ull }, + { 0x401d3851eb851eb8ull, 0x3fffd1234a14f77dull }, + { 0x401d395810624dd3ull, 0x3fffd1b2d260d002ull }, + { 0x401d3a5e353f7ceeull, 0x3fffd24255a53ccfull }, + { 0x401d3b645a1cac08ull, 0x3fffd2d1d3e29818ull }, + { 0x401d3c6a7ef9db23ull, 0x3fffd3614d193c08ull }, + { 0x401d3d70a3d70a3eull, 0x3fffd3f0c14982c0ull }, + { 0x401d3e76c8b43958ull, 0x3fffd4803073c657ull }, + { 0x401d3f7ced916873ull, 0x3fffd50f9a9860dcull }, + { 0x401d4083126e978dull, 0x3fffd59effb7ac52ull }, + { 0x401d4189374bc6a8ull, 0x3fffd62e5fd202b7ull }, + { 0x401d428f5c28f5c3ull, 0x3fffd6bdbae7bdfaull }, + { 0x401d4395810624ddull, 0x3fffd74d10f93805ull }, + { 0x401d449ba5e353f8ull, 0x3fffd7dc6206cab6ull }, + { 0x401d45a1cac08313ull, 0x3fffd86bae10cfe3ull }, + { 0x401d46a7ef9db22dull, 0x3fffd8faf517a158ull }, + { 0x401d47ae147ae148ull, 0x3fffd98a371b98d7ull }, + { 0x401d48b439581062ull, 0x3fffda19741d101aull }, + { 0x401d49ba5e353f7dull, 0x3fffdaa8ac1c60d0ull }, + { 0x401d4ac083126e98ull, 0x3fffdb37df19e4a1ull }, + { 0x401d4bc6a7ef9db2ull, 0x3fffdbc70d15f527ull }, + { 0x401d4ccccccccccdull, 0x3fffdc563610ebf8ull }, + { 0x401d4dd2f1a9fbe8ull, 0x3fffdce55a0b229eull }, + { 0x401d4ed916872b02ull, 0x3fffdd747904f298ull }, + { 0x401d4fdf3b645a1dull, 0x3fffde0392feb560ull }, + { 0x401d50e560418937ull, 0x3fffde92a7f8c461ull }, + { 0x401d51eb851eb852ull, 0x3fffdf21b7f37903ull }, + { 0x401d52f1a9fbe76dull, 0x3fffdfb0c2ef2c9full }, + { 0x401d53f7ced91687ull, 0x3fffe03fc8ec3887ull }, + { 0x401d54fdf3b645a2ull, 0x3fffe0cec9eaf605ull }, + { 0x401d5604189374bdull, 0x3fffe15dc5ebbe58ull }, + { 0x401d570a3d70a3d7ull, 0x3fffe1ecbceeeab4ull }, + { 0x401d5810624dd2f2ull, 0x3fffe27baef4d447ull }, + { 0x401d5916872b020cull, 0x3fffe30a9bfdd434ull }, + { 0x401d5a1cac083127ull, 0x3fffe399840a4395ull }, + { 0x401d5b22d0e56042ull, 0x3fffe428671a7b7bull }, + { 0x401d5c28f5c28f5cull, 0x3fffe4b7452ed4ecull }, + { 0x401d5d2f1a9fbe77ull, 0x3fffe5461e47a8e7ull }, + { 0x401d5e353f7ced92ull, 0x3fffe5d4f2655060ull }, + { 0x401d5f3b645a1cacull, 0x3fffe663c1882443ull }, + { 0x401d604189374bc7ull, 0x3fffe6f28bb07d71ull }, + { 0x401d6147ae147ae1ull, 0x3fffe78150deb4c3ull }, + { 0x401d624dd2f1a9fcull, 0x3fffe81011132309ull }, + { 0x401d6353f7ced917ull, 0x3fffe89ecc4e210aull }, + { 0x401d645a1cac0831ull, 0x3fffe92d82900780ull }, + { 0x401d65604189374cull, 0x3fffe9bc33d92f21ull }, + { 0x401d666666666667ull, 0x3fffea4ae029f097ull }, + { 0x401d676c8b439581ull, 0x3fffead98782a482ull }, + { 0x401d6872b020c49cull, 0x3fffeb6829e3a37bull }, + { 0x401d6978d4fdf3b6ull, 0x3fffebf6c74d460full }, + { 0x401d6a7ef9db22d1ull, 0x3fffec855fbfe4c5ull }, + { 0x401d6b851eb851ecull, 0x3fffed13f33bd819ull }, + { 0x401d6c8b43958106ull, 0x3fffeda281c1787cull }, + { 0x401d6d916872b021ull, 0x3fffee310b511e59ull }, + { 0x401d6e978d4fdf3cull, 0x3fffeebf8feb2210ull }, + { 0x401d6f9db22d0e56ull, 0x3fffef4e0f8fdbf9ull }, + { 0x401d70a3d70a3d71ull, 0x3fffefdc8a3fa460ull }, + { 0x401d71a9fbe76c8bull, 0x3ffff06afffad38bull }, + { 0x401d72b020c49ba6ull, 0x3ffff0f970c1c1b6ull }, + { 0x401d73b645a1cac1ull, 0x3ffff187dc94c712ull }, + { 0x401d74bc6a7ef9dbull, 0x3ffff21643743bc9ull }, + { 0x401d75c28f5c28f6ull, 0x3ffff2a4a56077faull }, + { 0x401d76c8b4395811ull, 0x3ffff3330259d3bdull }, + { 0x401d77ced916872bull, 0x3ffff3c15a60a71eull }, + { 0x401d78d4fdf3b646ull, 0x3ffff44fad754a22ull }, + { 0x401d79db22d0e560ull, 0x3ffff4ddfb9814c4ull }, + { 0x401d7ae147ae147bull, 0x3ffff56c44c95ef7ull }, + { 0x401d7be76c8b4396ull, 0x3ffff5fa890980a3ull }, + { 0x401d7ced916872b0ull, 0x3ffff688c858d1a7ull }, + { 0x401d7df3b645a1cbull, 0x3ffff71702b7a9dbull }, + { 0x401d7ef9db22d0e6ull, 0x3ffff7a53826610bull }, + { 0x401d800000000000ull, 0x3ffff83368a54efcull }, + { 0x401d810624dd2f1bull, 0x3ffff8c19434cb69ull }, + { 0x401d820c49ba5e35ull, 0x3ffff94fbad52e04ull }, + { 0x401d83126e978d50ull, 0x3ffff9dddc86ce75ull }, + { 0x401d84189374bc6bull, 0x3ffffa6bf94a045eull }, + { 0x401d851eb851eb85ull, 0x3ffffafa111f2752ull }, + { 0x401d8624dd2f1aa0ull, 0x3ffffb8824068ee1ull }, + { 0x401d872b020c49bbull, 0x3ffffc1632009290ull }, + { 0x401d883126e978d5ull, 0x3ffffca43b0d89d7ull }, + { 0x401d89374bc6a7f0ull, 0x3ffffd323f2dcc2cull }, + { 0x401d8a3d70a3d70aull, 0x3ffffdc03e61b0f5ull }, + { 0x401d8b4395810625ull, 0x3ffffe4e38a98f94ull }, + { 0x401d8c49ba5e3540ull, 0x3ffffedc2e05bf5full }, + { 0x401d8d4fdf3b645aull, 0x3fffff6a1e7697a3ull }, + { 0x401d8e5604189375ull, 0x3ffffff809fc6fa6ull }, + { 0x401d8f5c28f5c290ull, 0x40000042f84bcf52ull }, + { 0x401d90624dd2f1aaull, 0x40000089e9243de7ull }, + { 0x401d916872b020c5ull, 0x400000d0d787af27ull }, + { 0x401d926e978d4fdfull, 0x40000117c3764ea3ull }, + { 0x401d9374bc6a7efaull, 0x4000015eacf047e6ull }, + { 0x401d947ae147ae15ull, 0x400001a593f5c677ull }, + { 0x401d95810624dd2full, 0x400001ec7886f5d9ull }, + { 0x401d96872b020c4aull, 0x400002335aa4018bull }, + { 0x401d978d4fdf3b65ull, 0x4000027a3a4d1505ull }, + { 0x401d989374bc6a7full, 0x400002c117825bbdull }, + { 0x401d99999999999aull, 0x40000307f2440124ull }, + { 0x401d9a9fbe76c8b4ull, 0x4000034eca9230a5ull }, + { 0x401d9ba5e353f7cfull, 0x40000395a06d15a8ull }, + { 0x401d9cac083126eaull, 0x400003dc73d4db91ull }, + { 0x401d9db22d0e5604ull, 0x4000042344c9adbeull }, + { 0x401d9eb851eb851full, 0x4000046a134bb789ull }, + { 0x401d9fbe76c8b43aull, 0x400004b0df5b2447ull }, + { 0x401da0c49ba5e354ull, 0x400004f7a8f81f4aull }, + { 0x401da1cac083126full, 0x4000053e7022d3dfull }, + { 0x401da2d0e5604189ull, 0x4000058534db6d4dull }, + { 0x401da3d70a3d70a4ull, 0x400005cbf72216d8ull }, + { 0x401da4dd2f1a9fbfull, 0x40000612b6f6fbbfull }, + { 0x401da5e353f7ced9ull, 0x40000659745a473dull }, + { 0x401da6e978d4fdf4ull, 0x400006a02f4c2489ull }, + { 0x401da7ef9db22d0full, 0x400006e6e7ccbed4ull }, + { 0x401da8f5c28f5c29ull, 0x4000072d9ddc414aull }, + { 0x401da9fbe76c8b44ull, 0x40000774517ad715ull }, + { 0x401dab020c49ba5eull, 0x400007bb02a8ab5aull }, + { 0x401dac083126e979ull, 0x40000801b165e938ull }, + { 0x401dad0e56041894ull, 0x400008485db2bbcaull }, + { 0x401dae147ae147aeull, 0x4000088f078f4e27ull }, + { 0x401daf1a9fbe76c9ull, 0x400008d5aefbcb62ull }, + { 0x401db020c49ba5e4ull, 0x4000091c53f85e8aull }, + { 0x401db126e978d4feull, 0x40000962f68532a6ull }, + { 0x401db22d0e560419ull, 0x400009a996a272bdull }, + { 0x401db33333333333ull, 0x400009f0345049ceull }, + { 0x401db4395810624eull, 0x40000a36cf8ee2d8ull }, + { 0x401db53f7ced9169ull, 0x40000a7d685e68d0ull }, + { 0x401db645a1cac083ull, 0x40000ac3febf06aaull }, + { 0x401db74bc6a7ef9eull, 0x40000b0a92b0e756ull }, + { 0x401db851eb851eb8ull, 0x40000b51243435bdull }, + { 0x401db95810624dd3ull, 0x40000b97b3491cc6ull }, + { 0x401dba5e353f7ceeull, 0x40000bde3fefc752ull }, + { 0x401dbb645a1cac08ull, 0x40000c24ca28603eull }, + { 0x401dbc6a7ef9db23ull, 0x40000c6b51f31263ull }, + { 0x401dbd70a3d70a3eull, 0x40000cb1d7500895ull }, + { 0x401dbe76c8b43958ull, 0x40000cf85a3f6da5ull }, + { 0x401dbf7ced916873ull, 0x40000d3edac16c5dull }, + { 0x401dc083126e978dull, 0x40000d8558d62f84ull }, + { 0x401dc189374bc6a8ull, 0x40000dcbd47de1deull }, + { 0x401dc28f5c28f5c3ull, 0x40000e124db8ae28ull }, + { 0x401dc395810624ddull, 0x40000e58c486bf1bull }, + { 0x401dc49ba5e353f8ull, 0x40000e9f38e83f6full }, + { 0x401dc5a1cac08313ull, 0x40000ee5aadd59d3ull }, + { 0x401dc6a7ef9db22dull, 0x40000f2c1a6638f3ull }, + { 0x401dc7ae147ae148ull, 0x40000f7287830779ull }, + { 0x401dc8b439581062ull, 0x40000fb8f233f007ull }, + { 0x401dc9ba5e353f7dull, 0x40000fff5a791d3dull }, + { 0x401dcac083126e98ull, 0x40001045c052b9b7ull }, + { 0x401dcbc6a7ef9db2ull, 0x4000108c23c0f00aull }, + { 0x401dcccccccccccdull, 0x400010d284c3eac9ull }, + { 0x401dcdd2f1a9fbe8ull, 0x40001118e35bd481ull }, + { 0x401dced916872b02ull, 0x4000115f3f88d7bcull }, + { 0x401dcfdf3b645a1dull, 0x400011a5994b1f00ull }, + { 0x401dd0e560418937ull, 0x400011ebf0a2d4cbull }, + { 0x401dd1eb851eb852ull, 0x400012324590239cull }, + { 0x401dd2f1a9fbe76dull, 0x40001278981335eaull }, + { 0x401dd3f7ced91687ull, 0x400012bee82c3627ull }, + { 0x401dd4fdf3b645a2ull, 0x4000130535db4ec4ull }, + { 0x401dd604189374bdull, 0x4000134b8120aa2bull }, + { 0x401dd70a3d70a3d7ull, 0x40001391c9fc72c2ull }, + { 0x401dd810624dd2f2ull, 0x400013d8106ed2ecull }, + { 0x401dd916872b020cull, 0x4000141e5477f506ull }, + { 0x401dda1cac083127ull, 0x4000146496180369ull }, + { 0x401ddb22d0e56042ull, 0x400014aad54f286bull }, + { 0x401ddc28f5c28f5cull, 0x400014f1121d8e5bull }, + { 0x401ddd2f1a9fbe77ull, 0x400015374c835f87ull }, + { 0x401dde353f7ced92ull, 0x4000157d8480c636ull }, + { 0x401ddf3b645a1cacull, 0x400015c3ba15ecacull }, + { 0x401de04189374bc7ull, 0x40001609ed42fd27ull }, + { 0x401de147ae147ae1ull, 0x400016501e0821e3ull }, + { 0x401de24dd2f1a9fcull, 0x400016964c658516ull }, + { 0x401de353f7ced917ull, 0x400016dc785b50f2ull }, + { 0x401de45a1cac0831ull, 0x40001722a1e9afa6ull }, + { 0x401de5604189374cull, 0x40001768c910cb5aull }, + { 0x401de66666666667ull, 0x400017aeedd0ce34ull }, + { 0x401de76c8b439581ull, 0x400017f51029e254ull }, + { 0x401de872b020c49cull, 0x4000183b301c31d8ull }, + { 0x401de978d4fdf3b6ull, 0x400018814da7e6d7ull }, + { 0x401dea7ef9db22d1ull, 0x400018c768cd2b65ull }, + { 0x401deb851eb851ecull, 0x4000190d818c2992ull }, + { 0x401dec8b43958106ull, 0x4000195397e50b69ull }, + { 0x401ded916872b021ull, 0x40001999abd7faf0ull }, + { 0x401dee978d4fdf3cull, 0x400019dfbd65222bull }, + { 0x401def9db22d0e56ull, 0x40001a25cc8cab18ull }, + { 0x401df0a3d70a3d71ull, 0x40001a6bd94ebfb0ull }, + { 0x401df1a9fbe76c8bull, 0x40001ab1e3ab89eaull }, + { 0x401df2b020c49ba6ull, 0x40001af7eba333b6ull }, + { 0x401df3b645a1cac1ull, 0x40001b3df135e702ull }, + { 0x401df4bc6a7ef9dbull, 0x40001b83f463cdb6ull }, + { 0x401df5c28f5c28f6ull, 0x40001bc9f52d11b8ull }, + { 0x401df6c8b4395811ull, 0x40001c0ff391dce6ull }, + { 0x401df7ced916872bull, 0x40001c55ef92591dull }, + { 0x401df8d4fdf3b646ull, 0x40001c9be92eb035ull }, + { 0x401df9db22d0e560ull, 0x40001ce1e0670c01ull }, + { 0x401dfae147ae147bull, 0x40001d27d53b964full }, + { 0x401dfbe76c8b4396ull, 0x40001d6dc7ac78ebull }, + { 0x401dfced916872b0ull, 0x40001db3b7b9dd9bull }, + { 0x401dfdf3b645a1cbull, 0x40001df9a563ee21ull }, + { 0x401dfef9db22d0e6ull, 0x40001e3f90aad43cull }, + { 0x401e000000000000ull, 0x40001e85798eb9a3ull }, + { 0x401e010624dd2f1bull, 0x40001ecb600fc80eull }, + { 0x401e020c49ba5e35ull, 0x40001f11442e292dull }, + { 0x401e03126e978d50ull, 0x40001f5725ea06acull }, + { 0x401e04189374bc6bull, 0x40001f9d05438a35ull }, + { 0x401e051eb851eb85ull, 0x40001fe2e23add6cull }, + { 0x401e0624dd2f1aa0ull, 0x40002028bcd029f0ull }, + { 0x401e072b020c49bbull, 0x4000206e9503995dull }, + { 0x401e083126e978d5ull, 0x400020b46ad5554cull }, + { 0x401e09374bc6a7f0ull, 0x400020fa3e45874eull }, + { 0x401e0a3d70a3d70aull, 0x400021400f5458f4ull }, + { 0x401e0b4395810625ull, 0x40002185de01f3c7ull }, + { 0x401e0c49ba5e3540ull, 0x400021cbaa4e814full }, + { 0x401e0d4fdf3b645aull, 0x40002211743a2b0dull }, + { 0x401e0e5604189375ull, 0x400022573bc51a80ull }, + { 0x401e0f5c28f5c290ull, 0x4000229d00ef7921ull }, + { 0x401e10624dd2f1aaull, 0x400022e2c3b97066ull }, + { 0x401e116872b020c5ull, 0x40002328842329c0ull }, + { 0x401e126e978d4fdfull, 0x4000236e422cce9cull }, + { 0x401e1374bc6a7efaull, 0x400023b3fdd68864ull }, + { 0x401e147ae147ae15ull, 0x400023f9b720807bull }, + { 0x401e15810624dd2full, 0x4000243f6e0ae041ull }, + { 0x401e16872b020c4aull, 0x400024852295d114ull }, + { 0x401e178d4fdf3b65ull, 0x400024cad4c17c4bull }, + { 0x401e189374bc6a7full, 0x40002510848e0b3aull }, + { 0x401e19999999999aull, 0x4000255631fba730ull }, + { 0x401e1a9fbe76c8b4ull, 0x4000259bdd0a7978ull }, + { 0x401e1ba5e353f7cfull, 0x400025e185baab5aull }, + { 0x401e1cac083126eaull, 0x400026272c0c6618ull }, + { 0x401e1db22d0e5604ull, 0x4000266ccfffd2f1ull }, + { 0x401e1eb851eb851full, 0x400026b271951b1full }, + { 0x401e1fbe76c8b43aull, 0x400026f810cc67d9ull }, + { 0x401e20c49ba5e354ull, 0x4000273dada5e251ull }, + { 0x401e21cac083126full, 0x400027834821b3b4ull }, + { 0x401e22d0e5604189ull, 0x400027c8e040052cull }, + { 0x401e23d70a3d70a4ull, 0x4000280e7600ffdfull }, + { 0x401e24dd2f1a9fbfull, 0x400028540964ccedull }, + { 0x401e25e353f7ced9ull, 0x400028999a6b9574ull }, + { 0x401e26e978d4fdf4ull, 0x400028df2915828cull }, + { 0x401e27ef9db22d0full, 0x40002924b562bd4bull }, + { 0x401e28f5c28f5c29ull, 0x4000296a3f536ec1ull }, + { 0x401e29fbe76c8b44ull, 0x400029afc6e7bff9ull }, + { 0x401e2b020c49ba5eull, 0x400029f54c1fd9fcull }, + { 0x401e2c083126e979ull, 0x40002a3acefbe5cfull }, + { 0x401e2d0e56041894ull, 0x40002a804f7c0c71ull }, + { 0x401e2e147ae147aeull, 0x40002ac5cda076dcull }, + { 0x401e2f1a9fbe76c9ull, 0x40002b0b49694e0aull }, + { 0x401e3020c49ba5e4ull, 0x40002b50c2d6baedull }, + { 0x401e3126e978d4feull, 0x40002b9639e8e673ull }, + { 0x401e322d0e560419ull, 0x40002bdbae9ff989ull }, + { 0x401e333333333333ull, 0x40002c2120fc1d15ull }, + { 0x401e34395810624eull, 0x40002c6690fd79f9ull }, + { 0x401e353f7ced9169ull, 0x40002cabfea43914ull }, + { 0x401e3645a1cac083ull, 0x40002cf169f0833full }, + { 0x401e374bc6a7ef9eull, 0x40002d36d2e28152ull }, + { 0x401e3851eb851eb8ull, 0x40002d7c397a5c1full }, + { 0x401e395810624dd3ull, 0x40002dc19db83c72ull }, + { 0x401e3a5e353f7ceeull, 0x40002e06ff9c4b16ull }, + { 0x401e3b645a1cac08ull, 0x40002e4c5f26b0d0ull }, + { 0x401e3c6a7ef9db23ull, 0x40002e91bc579662ull }, + { 0x401e3d70a3d70a3eull, 0x40002ed7172f2488ull }, + { 0x401e3e76c8b43958ull, 0x40002f1c6fad83fdull }, + { 0x401e3f7ced916873ull, 0x40002f61c5d2dd74ull }, + { 0x401e4083126e978dull, 0x40002fa7199f599full }, + { 0x401e4189374bc6a8ull, 0x40002fec6b13212bull }, + { 0x401e428f5c28f5c3ull, 0x40003031ba2e5cc0ull }, + { 0x401e4395810624ddull, 0x4000307706f13502ull }, + { 0x401e449ba5e353f8ull, 0x400030bc515bd292ull }, + { 0x401e45a1cac08313ull, 0x40003101996e5e0cull }, + { 0x401e46a7ef9db22dull, 0x40003146df290008ull }, + { 0x401e47ae147ae148ull, 0x4000318c228be11aull }, + { 0x401e48b439581062ull, 0x400031d1639729d2ull }, + { 0x401e49ba5e353f7dull, 0x40003216a24b02bbull }, + { 0x401e4ac083126e98ull, 0x4000325bdea7945eull }, + { 0x401e4bc6a7ef9db2ull, 0x400032a118ad073eull }, + { 0x401e4ccccccccccdull, 0x400032e6505b83daull }, + { 0x401e4dd2f1a9fbe8ull, 0x4000332b85b332adull }, + { 0x401e4ed916872b02ull, 0x40003370b8b43c2full }, + { 0x401e4fdf3b645a1dull, 0x400033b5e95ec8d3ull }, + { 0x401e50e560418937ull, 0x400033fb17b30107ull }, + { 0x401e51eb851eb852ull, 0x4000344043b10d37ull }, + { 0x401e52f1a9fbe76dull, 0x400034856d5915c9ull }, + { 0x401e53f7ced91687ull, 0x400034ca94ab4321ull }, + { 0x401e54fdf3b645a2ull, 0x4000350fb9a7bd9cull }, + { 0x401e5604189374bdull, 0x40003554dc4ead96ull }, + { 0x401e570a3d70a3d7ull, 0x40003599fca03b64ull }, + { 0x401e5810624dd2f2ull, 0x400035df1a9c8f5aull }, + { 0x401e5916872b020cull, 0x400036243643d1c4ull }, + { 0x401e5a1cac083127ull, 0x400036694f962aeeull }, + { 0x401e5b22d0e56042ull, 0x400036ae6693c31eull }, + { 0x401e5c28f5c28f5cull, 0x400036f37b3cc296ull }, + { 0x401e5d2f1a9fbe77ull, 0x400037388d915193ull }, + { 0x401e5e353f7ced92ull, 0x4000377d9d919850ull }, + { 0x401e5f3b645a1cacull, 0x400037c2ab3dbf03ull }, + { 0x401e604189374bc7ull, 0x40003807b695eddcull }, + { 0x401e6147ae147ae1ull, 0x4000384cbf9a4d0aull }, + { 0x401e624dd2f1a9fcull, 0x40003891c64b04b7ull }, + { 0x401e6353f7ced917ull, 0x400038d6caa83d08ull }, + { 0x401e645a1cac0831ull, 0x4000391bccb21e1full }, + { 0x401e65604189374cull, 0x40003960cc68d019ull }, + { 0x401e666666666667ull, 0x400039a5c9cc7b0full }, + { 0x401e676c8b439581ull, 0x400039eac4dd4716ull }, + { 0x401e6872b020c49cull, 0x40003a2fbd9b5c42ull }, + { 0x401e6978d4fdf3b6ull, 0x40003a74b406e29dull }, + { 0x401e6a7ef9db22d1ull, 0x40003ab9a8200232ull }, + { 0x401e6b851eb851ecull, 0x40003afe99e6e306ull }, + { 0x401e6c8b43958106ull, 0x40003b43895bad19ull }, + { 0x401e6d916872b021ull, 0x40003b88767e886aull }, + { 0x401e6e978d4fdf3cull, 0x40003bcd614f9cf1ull }, + { 0x401e6f9db22d0e56ull, 0x40003c1249cf12a3ull }, + { 0x401e70a3d70a3d71ull, 0x40003c572ffd1171ull }, + { 0x401e71a9fbe76c8bull, 0x40003c9c13d9c147ull }, + { 0x401e72b020c49ba6ull, 0x40003ce0f5654a10ull }, + { 0x401e73b645a1cac1ull, 0x40003d25d49fd3b0ull }, + { 0x401e74bc6a7ef9dbull, 0x40003d6ab1898607ull }, + { 0x401e75c28f5c28f6ull, 0x40003daf8c2288f3ull }, + { 0x401e76c8b4395811ull, 0x40003df4646b044cull }, + { 0x401e77ced916872bull, 0x40003e393a631fe7ull }, + { 0x401e78d4fdf3b646ull, 0x40003e7e0e0b0396ull }, + { 0x401e79db22d0e560ull, 0x40003ec2df62d724ull }, + { 0x401e7ae147ae147bull, 0x40003f07ae6ac25aull }, + { 0x401e7be76c8b4396ull, 0x40003f4c7b22ecfeull }, + { 0x401e7ced916872b0ull, 0x40003f91458b7ed0ull }, + { 0x401e7df3b645a1cbull, 0x40003fd60da49f8dull }, + { 0x401e7ef9db22d0e6ull, 0x4000401ad36e76eeull }, + { 0x401e800000000000ull, 0x4000405f96e92ca6ull }, + { 0x401e810624dd2f1bull, 0x400040a45814e868ull }, + { 0x401e820c49ba5e35ull, 0x400040e916f1d1e0ull }, + { 0x401e83126e978d50ull, 0x4000412dd38010b6ull }, + { 0x401e84189374bc6bull, 0x400041728dbfcc90ull }, + { 0x401e851eb851eb85ull, 0x400041b745b12d0dull }, + { 0x401e8624dd2f1aa0ull, 0x400041fbfb5459ccull }, + { 0x401e872b020c49bbull, 0x40004240aea97a64ull }, + { 0x401e883126e978d5ull, 0x400042855fb0b66bull }, + { 0x401e89374bc6a7f0ull, 0x400042ca0e6a3571ull }, + { 0x401e8a3d70a3d70aull, 0x4000430ebad61f03ull }, + { 0x401e8b4395810625ull, 0x4000435364f49aaaull }, + { 0x401e8c49ba5e3540ull, 0x400043980cc5cfecull }, + { 0x401e8d4fdf3b645aull, 0x400043dcb249e648ull }, + { 0x401e8e5604189375ull, 0x400044215581053cull }, + { 0x401e8f5c28f5c290ull, 0x40004465f66b5441ull }, + { 0x401e90624dd2f1aaull, 0x400044aa9508facbull }, + { 0x401e916872b020c5ull, 0x400044ef315a204dull }, + { 0x401e926e978d4fdfull, 0x40004533cb5eec32ull }, + { 0x401e9374bc6a7efaull, 0x40004578631785e5ull }, + { 0x401e947ae147ae15ull, 0x400045bcf88414caull }, + { 0x401e95810624dd2full, 0x400046018ba4c042ull }, + { 0x401e96872b020c4aull, 0x400046461c79afabull }, + { 0x401e978d4fdf3b65ull, 0x4000468aab030a5eull }, + { 0x401e989374bc6a7full, 0x400046cf3740f7b0ull }, + { 0x401e99999999999aull, 0x40004713c1339ef3ull }, + { 0x401e9a9fbe76c8b4ull, 0x4000475848db2774ull }, + { 0x401e9ba5e353f7cfull, 0x4000479cce37b87dull }, + { 0x401e9cac083126eaull, 0x400047e151497954ull }, + { 0x401e9db22d0e5604ull, 0x40004825d2109139ull }, + { 0x401e9eb851eb851full, 0x4000486a508d276bull }, + { 0x401e9fbe76c8b43aull, 0x400048aeccbf6323ull }, + { 0x401ea0c49ba5e354ull, 0x400048f346a76b97ull }, + { 0x401ea1cac083126full, 0x40004937be4567f9ull }, + { 0x401ea2d0e5604189ull, 0x4000497c33997f77ull }, + { 0x401ea3d70a3d70a4ull, 0x400049c0a6a3d93cull }, + { 0x401ea4dd2f1a9fbfull, 0x40004a0517649c6cull }, + { 0x401ea5e353f7ced9ull, 0x40004a4985dbf02aull }, + { 0x401ea6e978d4fdf4ull, 0x40004a8df209fb96ull }, + { 0x401ea7ef9db22d0full, 0x40004ad25beee5c8ull }, + { 0x401ea8f5c28f5c29ull, 0x40004b16c38ad5d7ull }, + { 0x401ea9fbe76c8b44ull, 0x40004b5b28ddf2d6ull }, + { 0x401eab020c49ba5eull, 0x40004b9f8be863d3ull }, + { 0x401eac083126e979ull, 0x40004be3ecaa4fd9ull }, + { 0x401ead0e56041894ull, 0x40004c284b23ddefull }, + { 0x401eae147ae147aeull, 0x40004c6ca7553517ull }, + { 0x401eaf1a9fbe76c9ull, 0x40004cb1013e7c52ull }, + { 0x401eb020c49ba5e4ull, 0x40004cf558dfda98ull }, + { 0x401eb126e978d4feull, 0x40004d39ae3976e3ull }, + { 0x401eb22d0e560419ull, 0x40004d7e014b7824ull }, + { 0x401eb33333333333ull, 0x40004dc25216054cull }, + { 0x401eb4395810624eull, 0x40004e06a0994545ull }, + { 0x401eb53f7ced9169ull, 0x40004e4aecd55ef8ull }, + { 0x401eb645a1cac083ull, 0x40004e8f36ca7947ull }, + { 0x401eb74bc6a7ef9eull, 0x40004ed37e78bb14ull }, + { 0x401eb851eb851eb8ull, 0x40004f17c3e04b39ull }, + { 0x401eb95810624dd3ull, 0x40004f5c0701508eull }, + { 0x401eba5e353f7ceeull, 0x40004fa047dbf1e9ull }, + { 0x401ebb645a1cac08ull, 0x40004fe486705618ull }, + { 0x401ebc6a7ef9db23ull, 0x40005028c2bea3e8ull }, + { 0x401ebd70a3d70a3eull, 0x4000506cfcc70223ull }, + { 0x401ebe76c8b43958ull, 0x400050b13489978bull }, + { 0x401ebf7ced916873ull, 0x400050f56a068ae3ull }, + { 0x401ec083126e978dull, 0x400051399d3e02e7ull }, + { 0x401ec189374bc6a8ull, 0x4000517dce30264full }, + { 0x401ec28f5c28f5c3ull, 0x400051c1fcdd1bd2ull }, + { 0x401ec395810624ddull, 0x4000520629450a20ull }, + { 0x401ec49ba5e353f8ull, 0x4000524a536817e7ull }, + { 0x401ec5a1cac08313ull, 0x4000528e7b466bd0ull }, + { 0x401ec6a7ef9db22dull, 0x400052d2a0e02c80ull }, + { 0x401ec7ae147ae148ull, 0x40005316c435809bull }, + { 0x401ec8b439581062ull, 0x4000535ae5468ebdull }, + { 0x401ec9ba5e353f7dull, 0x4000539f04137d81ull }, + { 0x401ecac083126e98ull, 0x400053e3209c737dull }, + { 0x401ecbc6a7ef9db2ull, 0x400054273ae19743ull }, + { 0x401ecccccccccccdull, 0x4000546b52e30f62ull }, + { 0x401ecdd2f1a9fbe8ull, 0x400054af68a10265ull }, + { 0x401eced916872b02ull, 0x400054f37c1b96d2ull }, + { 0x401ecfdf3b645a1dull, 0x400055378d52f32dull }, + { 0x401ed0e560418937ull, 0x4000557b9c473df4ull }, + { 0x401ed1eb851eb852ull, 0x400055bfa8f89da4ull }, + { 0x401ed2f1a9fbe76dull, 0x40005603b36738b3ull }, + { 0x401ed3f7ced91687ull, 0x40005647bb933596ull }, + { 0x401ed4fdf3b645a2ull, 0x4000568bc17cbabbull }, + { 0x401ed604189374bdull, 0x400056cfc523ee90ull }, + { 0x401ed70a3d70a3d7ull, 0x40005713c688f77cull }, + { 0x401ed810624dd2f2ull, 0x40005757c5abfbe3ull }, + { 0x401ed916872b020cull, 0x4000579bc28d2227ull }, + { 0x401eda1cac083127ull, 0x400057dfbd2c90a4ull }, + { 0x401edb22d0e56042ull, 0x40005823b58a6db2ull }, + { 0x401edc28f5c28f5cull, 0x40005867aba6dfa7ull }, + { 0x401edd2f1a9fbe77ull, 0x400058ab9f820cd5ull }, + { 0x401ede353f7ced92ull, 0x400058ef911c1b88ull }, + { 0x401edf3b645a1cacull, 0x400059338075320aull }, + { 0x401ee04189374bc7ull, 0x400059776d8d76a2ull }, + { 0x401ee147ae147ae1ull, 0x400059bb58650f90ull }, + { 0x401ee24dd2f1a9fcull, 0x400059ff40fc2314ull }, + { 0x401ee353f7ced917ull, 0x40005a432752d768ull }, + { 0x401ee45a1cac0831ull, 0x40005a870b6952c2ull }, + { 0x401ee5604189374cull, 0x40005acaed3fbb55ull }, + { 0x401ee66666666667ull, 0x40005b0eccd63750ull }, + { 0x401ee76c8b439581ull, 0x40005b52aa2cecdfull }, + { 0x401ee872b020c49cull, 0x40005b9685440228ull }, + { 0x401ee978d4fdf3b6ull, 0x40005bda5e1b9d50ull }, + { 0x401eea7ef9db22d1ull, 0x40005c1e34b3e476ull }, + { 0x401eeb851eb851ecull, 0x40005c62090cfdb6ull }, + { 0x401eec8b43958106ull, 0x40005ca5db270f29ull }, + { 0x401eed916872b021ull, 0x40005ce9ab023ee3ull }, + { 0x401eee978d4fdf3cull, 0x40005d2d789eb2f5ull }, + { 0x401eef9db22d0e56ull, 0x40005d7143fc916bull }, + { 0x401ef0a3d70a3d71ull, 0x40005db50d1c0050ull }, + { 0x401ef1a9fbe76c8bull, 0x40005df8d3fd25a7ull }, + { 0x401ef2b020c49ba6ull, 0x40005e3c98a02773ull }, + { 0x401ef3b645a1cac1ull, 0x40005e805b052bb3ull }, + { 0x401ef4bc6a7ef9dbull, 0x40005ec41b2c585eull }, + { 0x401ef5c28f5c28f6ull, 0x40005f07d915d36dull }, + { 0x401ef6c8b4395811ull, 0x40005f4b94c1c2d1ull }, + { 0x401ef7ced916872bull, 0x40005f8f4e304c79ull }, + { 0x401ef8d4fdf3b646ull, 0x40005fd305619650ull }, + { 0x401ef9db22d0e560ull, 0x40006016ba55c63eull }, + { 0x401efae147ae147bull, 0x4000605a6d0d0225ull }, + { 0x401efbe76c8b4396ull, 0x4000609e1d876fe7ull }, + { 0x401efced916872b0ull, 0x400060e1cbc5355dull }, + { 0x401efdf3b645a1cbull, 0x4000612577c67862ull }, + { 0x401efef9db22d0e6ull, 0x40006169218b5ec9ull }, + { 0x401f000000000000ull, 0x400061acc9140e62ull }, + { 0x401f010624dd2f1bull, 0x400061f06e60acfcull }, + { 0x401f020c49ba5e35ull, 0x400062341171605eull }, + { 0x401f03126e978d50ull, 0x40006277b2464e50ull }, + { 0x401f04189374bc6bull, 0x400062bb50df9c91ull }, + { 0x401f051eb851eb85ull, 0x400062feed3d70e1ull }, + { 0x401f0624dd2f1aa0ull, 0x40006342875ff0f9ull }, + { 0x401f072b020c49bbull, 0x400063861f474290ull }, + { 0x401f083126e978d5ull, 0x400063c9b4f38b59ull }, + { 0x401f09374bc6a7f0ull, 0x4000640d4864f103ull }, + { 0x401f0a3d70a3d70aull, 0x40006450d99b9938ull }, + { 0x401f0b4395810625ull, 0x400064946897a9a2ull }, + { 0x401f0c49ba5e3540ull, 0x400064d7f55947e3ull }, + { 0x401f0d4fdf3b645aull, 0x4000651b7fe0999cull }, + { 0x401f0e5604189375ull, 0x4000655f082dc46aull }, + { 0x401f0f5c28f5c290ull, 0x400065a28e40ede4ull }, + { 0x401f10624dd2f1aaull, 0x400065e6121a3ba0ull }, + { 0x401f116872b020c5ull, 0x4000662993b9d32full }, + { 0x401f126e978d4fdfull, 0x4000666d131fda1eull }, + { 0x401f1374bc6a7efaull, 0x400066b0904c75f8ull }, + { 0x401f147ae147ae15ull, 0x400066f40b3fcc43ull }, + { 0x401f15810624dd2full, 0x4000673783fa0281ull }, + { 0x401f16872b020c4aull, 0x4000677afa7b3e31ull }, + { 0x401f178d4fdf3b65ull, 0x400067be6ec3a4cdull }, + { 0x401f189374bc6a7full, 0x40006801e0d35bcdull }, + { 0x401f19999999999aull, 0x4000684550aa88a5ull }, + { 0x401f1a9fbe76c8b4ull, 0x40006888be4950c5ull }, + { 0x401f1ba5e353f7cfull, 0x400068cc29afd998ull }, + { 0x401f1cac083126eaull, 0x4000690f92de4888ull }, + { 0x401f1db22d0e5604ull, 0x40006952f9d4c2f9ull }, + { 0x401f1eb851eb851full, 0x400069965e936e4cull }, + { 0x401f1fbe76c8b43aull, 0x400069d9c11a6fdeull }, + { 0x401f20c49ba5e354ull, 0x40006a1d2169ed0aull }, + { 0x401f21cac083126full, 0x40006a607f820b26ull }, + { 0x401f22d0e5604189ull, 0x40006aa3db62ef83ull }, + { 0x401f23d70a3d70a4ull, 0x40006ae7350cbf71ull }, + { 0x401f24dd2f1a9fbfull, 0x40006b2a8c7fa03bull }, + { 0x401f25e353f7ced9ull, 0x40006b6de1bbb727ull }, + { 0x401f26e978d4fdf4ull, 0x40006bb134c1297aull }, + { 0x401f27ef9db22d0full, 0x40006bf485901c73ull }, + { 0x401f28f5c28f5c29ull, 0x40006c37d428b54full }, + { 0x401f29fbe76c8b44ull, 0x40006c7b208b1947ull }, + { 0x401f2b020c49ba5eull, 0x40006cbe6ab76d8full }, + { 0x401f2c083126e979ull, 0x40006d01b2add759ull }, + { 0x401f2d0e56041894ull, 0x40006d44f86e7bd2ull }, + { 0x401f2e147ae147aeull, 0x40006d883bf98025ull }, + { 0x401f2f1a9fbe76c9ull, 0x40006dcb7d4f0978ull }, + { 0x401f3020c49ba5e4ull, 0x40006e0ebc6f3cecull }, + { 0x401f3126e978d4feull, 0x40006e51f95a3fa2ull }, + { 0x401f322d0e560419ull, 0x40006e95341036b5ull }, + { 0x401f333333333333ull, 0x40006ed86c91473dull }, + { 0x401f34395810624eull, 0x40006f1ba2dd964cull }, + { 0x401f353f7ced9169ull, 0x40006f5ed6f548f5ull }, + { 0x401f3645a1cac083ull, 0x40006fa208d88443ull }, + { 0x401f374bc6a7ef9eull, 0x40006fe538876d40ull }, + { 0x401f3851eb851eb9ull, 0x40007028660228f1ull }, + { 0x401f395810624dd3ull, 0x4000706b9148dc57ull }, + { 0x401f3a5e353f7ceeull, 0x400070aeba5bac72ull }, + { 0x401f3b645a1cac08ull, 0x400070f1e13abe3cull }, + { 0x401f3c6a7ef9db23ull, 0x4000713505e636abull }, + { 0x401f3d70a3d70a3eull, 0x40007178285e3ab3ull }, + { 0x401f3e76c8b43958ull, 0x400071bb48a2ef43ull }, + { 0x401f3f7ced916873ull, 0x400071fe66b47947ull }, + { 0x401f4083126e978dull, 0x400072418292fda8ull }, + { 0x401f4189374bc6a8ull, 0x400072849c3ea14bull }, + { 0x401f428f5c28f5c3ull, 0x400072c7b3b78911ull }, + { 0x401f4395810624ddull, 0x4000730ac8fdd9d6ull }, + { 0x401f449ba5e353f8ull, 0x4000734ddc11b875ull }, + { 0x401f45a1cac08313ull, 0x40007390ecf349c5ull }, + { 0x401f46a7ef9db22dull, 0x400073d3fba2b297ull }, + { 0x401f47ae147ae148ull, 0x40007417082017bbull }, + { 0x401f48b439581062ull, 0x4000745a126b9dfcull }, + { 0x401f49ba5e353f7dull, 0x4000749d1a856a22ull }, + { 0x401f4ac083126e98ull, 0x400074e0206da0f1ull }, + { 0x401f4bc6a7ef9db2ull, 0x400075232424672aull }, + { 0x401f4ccccccccccdull, 0x4000756625a9e18aull }, + { 0x401f4dd2f1a9fbe8ull, 0x400075a924fe34c9ull }, + { 0x401f4ed916872b02ull, 0x400075ec2221859eull }, + { 0x401f4fdf3b645a1dull, 0x4000762f1d13f8bbull }, + { 0x401f50e560418937ull, 0x4000767215d5b2cfull }, + { 0x401f51eb851eb852ull, 0x400076b50c66d884ull }, + { 0x401f52f1a9fbe76dull, 0x400076f800c78e81ull }, + { 0x401f53f7ced91687ull, 0x4000773af2f7f96bull }, + { 0x401f54fdf3b645a2ull, 0x4000777de2f83de2ull }, + { 0x401f5604189374bdull, 0x400077c0d0c88081ull }, + { 0x401f570a3d70a3d7ull, 0x40007803bc68e5e3ull }, + { 0x401f5810624dd2f2ull, 0x40007846a5d9929dull }, + { 0x401f5916872b020cull, 0x400078898d1aab3full }, + { 0x401f5a1cac083127ull, 0x400078cc722c5459ull }, + { 0x401f5b22d0e56042ull, 0x4000790f550eb276ull }, + { 0x401f5c28f5c28f5cull, 0x4000795235c1ea1bull }, + { 0x401f5d2f1a9fbe77ull, 0x4000799514461fceull }, + { 0x401f5e353f7ced92ull, 0x400079d7f09b780dull }, + { 0x401f5f3b645a1cacull, 0x40007a1acac21755ull }, + { 0x401f604189374bc7ull, 0x40007a5da2ba221full }, + { 0x401f6147ae147ae1ull, 0x40007aa07883bce0ull }, + { 0x401f624dd2f1a9fcull, 0x40007ae34c1f0c09ull }, + { 0x401f6353f7ced917ull, 0x40007b261d8c3409ull }, + { 0x401f645a1cac0831ull, 0x40007b68eccb594aull }, + { 0x401f65604189374cull, 0x40007babb9dca033ull }, + { 0x401f666666666667ull, 0x40007bee84c02d28ull }, + { 0x401f676c8b439581ull, 0x40007c314d762487ull }, + { 0x401f6872b020c49cull, 0x40007c7413feaaaeull }, + { 0x401f6978d4fdf3b6ull, 0x40007cb6d859e3f5ull }, + { 0x401f6a7ef9db22d1ull, 0x40007cf99a87f4b2ull }, + { 0x401f6b851eb851ecull, 0x40007d3c5a890135ull }, + { 0x401f6c8b43958106ull, 0x40007d7f185d2dcdull }, + { 0x401f6d916872b021ull, 0x40007dc1d4049ec5ull }, + { 0x401f6e978d4fdf3cull, 0x40007e048d7f7863ull }, + { 0x401f6f9db22d0e56ull, 0x40007e4744cddeeaull }, + { 0x401f70a3d70a3d71ull, 0x40007e89f9eff69bull }, + { 0x401f71a9fbe76c8bull, 0x40007eccace5e3b2ull }, + { 0x401f72b020c49ba6ull, 0x40007f0f5dafca67ull }, + { 0x401f73b645a1cac1ull, 0x40007f520c4dcef1ull }, + { 0x401f74bc6a7ef9dbull, 0x40007f94b8c0157full }, + { 0x401f75c28f5c28f6ull, 0x40007fd76306c242ull }, + { 0x401f76c8b4395811ull, 0x4000801a0b21f962ull }, + { 0x401f77ced916872bull, 0x4000805cb111df08ull }, + { 0x401f78d4fdf3b646ull, 0x4000809f54d69758ull }, + { 0x401f79db22d0e560ull, 0x400080e1f6704670ull }, + { 0x401f7ae147ae147bull, 0x4000812495df106dull }, + { 0x401f7be76c8b4396ull, 0x4000816733231969ull }, + { 0x401f7ced916872b0ull, 0x400081a9ce3c8579ull }, + { 0x401f7df3b645a1cbull, 0x400081ec672b78afull }, + { 0x401f7ef9db22d0e6ull, 0x4000822efdf0171aull }, + { 0x401f800000000000ull, 0x40008271928a84c3ull }, + { 0x401f810624dd2f1bull, 0x400082b424fae5b3ull }, + { 0x401f820c49ba5e35ull, 0x400082f6b5415dedull }, + { 0x401f83126e978d50ull, 0x40008339435e1173ull }, + { 0x401f84189374bc6bull, 0x4000837bcf512440ull }, + { 0x401f851eb851eb85ull, 0x400083be591aba4full }, + { 0x401f8624dd2f1aa0ull, 0x40008400e0baf794ull }, + { 0x401f872b020c49bbull, 0x4000844366320003ull }, + { 0x401f883126e978d5ull, 0x40008485e97ff789ull }, + { 0x401f89374bc6a7f0ull, 0x400084c86aa50213ull }, + { 0x401f8a3d70a3d70aull, 0x4000850ae9a14389ull }, + { 0x401f8b4395810625ull, 0x4000854d6674dfceull }, + { 0x401f8c49ba5e3540ull, 0x4000858fe11ffac4ull }, + { 0x401f8d4fdf3b645aull, 0x400085d259a2b847ull }, + { 0x401f8e5604189375ull, 0x40008614cffd3c33ull }, + { 0x401f8f5c28f5c290ull, 0x40008657442faa5cull }, + { 0x401f90624dd2f1aaull, 0x40008699b63a2696ull }, + { 0x401f916872b020c5ull, 0x400086dc261cd4b1ull }, + { 0x401f926e978d4fdfull, 0x4000871e93d7d878ull }, + { 0x401f9374bc6a7efaull, 0x40008760ff6b55b4ull }, + { 0x401f947ae147ae15ull, 0x400087a368d7702aull }, + { 0x401f95810624dd2full, 0x400087e5d01c4b9aull }, + { 0x401f96872b020c4aull, 0x40008828353a0bc4ull }, + { 0x401f978d4fdf3b65ull, 0x4000886a9830d462ull }, + { 0x401f989374bc6a7full, 0x400088acf900c92aull }, + { 0x401f99999999999aull, 0x400088ef57aa0dd1ull }, + { 0x401f9a9fbe76c8b4ull, 0x40008931b42cc605ull }, + { 0x401f9ba5e353f7cfull, 0x400089740e891576ull }, + { 0x401f9cac083126eaull, 0x400089b666bf1fcaull }, + { 0x401f9db22d0e5604ull, 0x400089f8bccf08a8ull }, + { 0x401f9eb851eb851full, 0x40008a3b10b8f3b2ull }, + { 0x401f9fbe76c8b43aull, 0x40008a7d627d0488ull }, + { 0x401fa0c49ba5e354ull, 0x40008abfb21b5ec3ull }, + { 0x401fa1cac083126full, 0x40008b01ff9425fdull }, + { 0x401fa2d0e5604189ull, 0x40008b444ae77dc9ull }, + { 0x401fa3d70a3d70a4ull, 0x40008b86941589b8ull }, + { 0x401fa4dd2f1a9fbfull, 0x40008bc8db1e6d59ull }, + { 0x401fa5e353f7ced9ull, 0x40008c0b20024c34ull }, + { 0x401fa6e978d4fdf4ull, 0x40008c4d62c149d1ull }, + { 0x401fa7ef9db22d0full, 0x40008c8fa35b89b3ull }, + { 0x401fa8f5c28f5c29ull, 0x40008cd1e1d12f59ull }, + { 0x401fa9fbe76c8b44ull, 0x40008d141e225e40ull }, + { 0x401fab020c49ba5eull, 0x40008d56584f39e0ull }, + { 0x401fac083126e979ull, 0x40008d989057e5afull }, + { 0x401fad0e56041894ull, 0x40008ddac63c8520ull }, + { 0x401fae147ae147aeull, 0x40008e1cf9fd3b9full }, + { 0x401faf1a9fbe76c9ull, 0x40008e5f2b9a2c98ull }, + { 0x401fb020c49ba5e4ull, 0x40008ea15b137b74ull }, + { 0x401fb126e978d4feull, 0x40008ee388694b96ull }, + { 0x401fb22d0e560419ull, 0x40008f25b39bc05full }, + { 0x401fb33333333333ull, 0x40008f67dcaafd2cull }, + { 0x401fb4395810624eull, 0x40008faa03972557ull }, + { 0x401fb53f7ced9169ull, 0x40008fec28605c37ull }, + { 0x401fb645a1cac083ull, 0x4000902e4b06c51eull }, + { 0x401fb74bc6a7ef9eull, 0x400090706b8a835dull }, + { 0x401fb851eb851eb9ull, 0x400090b289ebba40ull }, + { 0x401fb95810624dd3ull, 0x400090f4a62a8d0full }, + { 0x401fba5e353f7ceeull, 0x40009136c0471f0full }, + { 0x401fbb645a1cac08ull, 0x40009178d8419384ull }, + { 0x401fbc6a7ef9db23ull, 0x400091baee1a0daaull }, + { 0x401fbd70a3d70a3eull, 0x400091fd01d0b0bfull }, + { 0x401fbe76c8b43958ull, 0x4000923f13659ff8ull }, + { 0x401fbf7ced916873ull, 0x4000928122d8fe8bull }, + { 0x401fc083126e978dull, 0x400092c3302aefa8ull }, + { 0x401fc189374bc6a8ull, 0x400093053b5b967dull }, + { 0x401fc28f5c28f5c3ull, 0x40009347446b1634ull }, + { 0x401fc395810624ddull, 0x400093894b5991f4ull }, + { 0x401fc49ba5e353f8ull, 0x400093cb50272ce0ull }, + { 0x401fc5a1cac08313ull, 0x4000940d52d40a18ull }, + { 0x401fc6a7ef9db22dull, 0x4000944f53604cb8ull }, + { 0x401fc7ae147ae148ull, 0x4000949151cc17daull }, + { 0x401fc8b439581062ull, 0x400094d34e178e94ull }, + { 0x401fc9ba5e353f7dull, 0x400095154842d3f7ull }, + { 0x401fcac083126e98ull, 0x40009557404e0b13ull }, + { 0x401fcbc6a7ef9db2ull, 0x40009599363956f2ull }, + { 0x401fcccccccccccdull, 0x400095db2a04da9full }, + { 0x401fcdd2f1a9fbe8ull, 0x4000961d1bb0b91cull }, + { 0x401fced916872b02ull, 0x4000965f0b3d156cull }, + { 0x401fcfdf3b645a1dull, 0x400096a0f8aa128dull }, + { 0x401fd0e560418937ull, 0x400096e2e3f7d379ull }, + { 0x401fd1eb851eb852ull, 0x40009724cd267b28ull }, + { 0x401fd2f1a9fbe76dull, 0x40009766b4362c8dull }, + { 0x401fd3f7ced91687ull, 0x400097a899270a98ull }, + { 0x401fd4fdf3b645a2ull, 0x400097ea7bf93839ull }, + { 0x401fd604189374bdull, 0x4000982c5cacd857ull }, + { 0x401fd70a3d70a3d7ull, 0x4000986e3b420dd9ull }, + { 0x401fd810624dd2f2ull, 0x400098b017b8fba3ull }, + { 0x401fd916872b020cull, 0x400098f1f211c493ull }, + { 0x401fda1cac083127ull, 0x40009933ca4c8b88ull }, + { 0x401fdb22d0e56042ull, 0x40009975a0697358ull }, + { 0x401fdc28f5c28f5cull, 0x400099b774689edaull }, + { 0x401fdd2f1a9fbe77ull, 0x400099f9464a30e1ull }, + { 0x401fde353f7ced92ull, 0x40009a3b160e4c3bull }, + { 0x401fdf3b645a1cacull, 0x40009a7ce3b513b3ull }, + { 0x401fe04189374bc7ull, 0x40009abeaf3eaa12ull }, + { 0x401fe147ae147ae1ull, 0x40009b0078ab321dull }, + { 0x401fe24dd2f1a9fcull, 0x40009b423fface94ull }, + { 0x401fe353f7ced917ull, 0x40009b84052da237ull }, + { 0x401fe45a1cac0831ull, 0x40009bc5c843cfc0ull }, + { 0x401fe5604189374cull, 0x40009c07893d79e6ull }, + { 0x401fe66666666667ull, 0x40009c49481ac35dull }, + { 0x401fe76c8b439581ull, 0x40009c8b04dbced7ull }, + { 0x401fe872b020c49cull, 0x40009cccbf80bf01ull }, + { 0x401fe978d4fdf3b6ull, 0x40009d0e7809b683ull }, + { 0x401fea7ef9db22d1ull, 0x40009d502e76d807ull }, + { 0x401feb851eb851ecull, 0x40009d91e2c8462eull }, + { 0x401fec8b43958106ull, 0x40009dd394fe2399ull }, + { 0x401fed916872b021ull, 0x40009e15451892e5ull }, + { 0x401fee978d4fdf3cull, 0x40009e56f317b6abull }, + { 0x401fef9db22d0e56ull, 0x40009e989efbb180ull }, + { 0x401ff0a3d70a3d71ull, 0x40009eda48c4a5f9ull }, + { 0x401ff1a9fbe76c8bull, 0x40009f1bf072b6a4ull }, + { 0x401ff2b020c49ba6ull, 0x40009f5d9606060dull }, + { 0x401ff3b645a1cac1ull, 0x40009f9f397eb6bdull }, + { 0x401ff4bc6a7ef9dbull, 0x40009fe0dadceb3aull }, + { 0x401ff5c28f5c28f6ull, 0x4000a0227a20c605ull }, + { 0x401ff6c8b4395811ull, 0x4000a064174a699eull }, + { 0x401ff7ced916872bull, 0x4000a0a5b259f880ull }, + { 0x401ff8d4fdf3b646ull, 0x4000a0e74b4f9523ull }, + { 0x401ff9db22d0e560ull, 0x4000a128e22b61fcull }, + { 0x401ffae147ae147bull, 0x4000a16a76ed817dull }, + { 0x401ffbe76c8b4396ull, 0x4000a1ac09961614ull }, + { 0x401ffced916872b0ull, 0x4000a1ed9a25422bull }, + { 0x401ffdf3b645a1cbull, 0x4000a22f289b282cull }, + { 0x401ffef9db22d0e6ull, 0x4000a270b4f7ea79ull }, + { 0x4020000000000000ull, 0x4000a2b23f3bab73ull }, + { 0x40200083126e978dull, 0x4000a2f3c7668d7aull }, + { 0x4020010624dd2f1bull, 0x4000a3354d78b2e7ull }, + { 0x40200189374bc6a8ull, 0x4000a376d1723e10ull }, + { 0x4020020c49ba5e35ull, 0x4000a3b85353514bull }, + { 0x4020028f5c28f5c3ull, 0x4000a3f9d31c0ee8ull }, + { 0x402003126e978d50ull, 0x4000a43b50cc9933ull }, + { 0x40200395810624ddull, 0x4000a47ccc651277ull }, + { 0x402004189374bc6bull, 0x4000a4be45e59cfcull }, + { 0x4020049ba5e353f8ull, 0x4000a4ffbd4e5b02ull }, + { 0x4020051eb851eb85ull, 0x4000a541329f6eccull }, + { 0x402005a1cac08313ull, 0x4000a582a5d8fa96ull }, + { 0x40200624dd2f1aa0ull, 0x4000a5c416fb2099ull }, + { 0x402006a7ef9db22dull, 0x4000a6058606030cull }, + { 0x4020072b020c49baull, 0x4000a646f2f9c422ull }, + { 0x402007ae147ae148ull, 0x4000a6885dd6860aull }, + { 0x4020083126e978d5ull, 0x4000a6c9c69c6af1ull }, + { 0x402008b439581062ull, 0x4000a70b2d4b9500ull }, + { 0x402009374bc6a7f0ull, 0x4000a74c91e4265eull }, + { 0x402009ba5e353f7dull, 0x4000a78df466412bull }, + { 0x40200a3d70a3d70aull, 0x4000a7cf54d20788ull }, + { 0x40200ac083126e98ull, 0x4000a810b3279b92ull }, + { 0x40200b4395810625ull, 0x4000a8520f671f60ull }, + { 0x40200bc6a7ef9db2ull, 0x4000a8936990b509ull }, + { 0x40200c49ba5e3540ull, 0x4000a8d4c1a47e9eull }, + { 0x40200ccccccccccdull, 0x4000a91617a29e2full }, + { 0x40200d4fdf3b645aull, 0x4000a9576b8b35c7ull }, + { 0x40200dd2f1a9fbe8ull, 0x4000a998bd5e6770ull }, + { 0x40200e5604189375ull, 0x4000a9da0d1c552eull }, + { 0x40200ed916872b02ull, 0x4000aa1b5ac52102ull }, + { 0x40200f5c28f5c28full, 0x4000aa5ca658ecedull }, + { 0x40200fdf3b645a1dull, 0x4000aa9defd7daeaull }, + { 0x402010624dd2f1aaull, 0x4000aadf37420cefull }, + { 0x402010e560418937ull, 0x4000ab207c97a4f4ull }, + { 0x4020116872b020c5ull, 0x4000ab61bfd8c4eaull }, + { 0x402011eb851eb852ull, 0x4000aba301058ebeull }, + { 0x4020126e978d4fdfull, 0x4000abe4401e245cull }, + { 0x402012f1a9fbe76dull, 0x4000ac257d22a7adull }, + { 0x40201374bc6a7efaull, 0x4000ac66b8133a95ull }, + { 0x402013f7ced91687ull, 0x4000aca7f0effef5ull }, + { 0x4020147ae147ae15ull, 0x4000ace927b916adull }, + { 0x402014fdf3b645a2ull, 0x4000ad2a5c6ea396ull }, + { 0x402015810624dd2full, 0x4000ad6b8f10c789ull }, + { 0x40201604189374bdull, 0x4000adacbf9fa45bull }, + { 0x402016872b020c4aull, 0x4000adedee1b5bddull }, + { 0x4020170a3d70a3d7ull, 0x4000ae2f1a840fdcull }, + { 0x4020178d4fdf3b64ull, 0x4000ae7044d9e225ull }, + { 0x40201810624dd2f2ull, 0x4000aeb16d1cf47eull }, + { 0x4020189374bc6a7full, 0x4000aef2934d68adull }, + { 0x40201916872b020cull, 0x4000af33b76b6071ull }, + { 0x402019999999999aull, 0x4000af74d976fd8bull }, + { 0x40201a1cac083127ull, 0x4000afb5f97061b3ull }, + { 0x40201a9fbe76c8b4ull, 0x4000aff71757aea2ull }, + { 0x40201b22d0e56042ull, 0x4000b038332d060cull }, + { 0x40201ba5e353f7cfull, 0x4000b0794cf089a3ull }, + { 0x40201c28f5c28f5cull, 0x4000b0ba64a25b14ull }, + { 0x40201cac083126eaull, 0x4000b0fb7a429c0aull }, + { 0x40201d2f1a9fbe77ull, 0x4000b13c8dd16e2dull }, + { 0x40201db22d0e5604ull, 0x4000b17d9f4ef320ull }, + { 0x40201e353f7ced92ull, 0x4000b1beaebb4c86ull }, + { 0x40201eb851eb851full, 0x4000b1ffbc169bfcull }, + { 0x40201f3b645a1cacull, 0x4000b240c761031cull }, + { 0x40201fbe76c8b439ull, 0x4000b281d09aa37eull }, + { 0x4020204189374bc7ull, 0x4000b2c2d7c39eb8ull }, + { 0x402020c49ba5e354ull, 0x4000b303dcdc1658ull }, + { 0x40202147ae147ae1ull, 0x4000b344dfe42beeull }, + { 0x402021cac083126full, 0x4000b385e0dc0105ull }, + { 0x4020224dd2f1a9fcull, 0x4000b3c6dfc3b723ull }, + { 0x402022d0e5604189ull, 0x4000b407dc9b6fceull }, + { 0x40202353f7ced917ull, 0x4000b448d7634c86ull }, + { 0x402023d70a3d70a4ull, 0x4000b489d01b6ec8ull }, + { 0x4020245a1cac0831ull, 0x4000b4cac6c3f811ull }, + { 0x402024dd2f1a9fbfull, 0x4000b50bbb5d09d8ull }, + { 0x402025604189374cull, 0x4000b54cade6c590ull }, + { 0x402025e353f7ced9ull, 0x4000b58d9e614cacull }, + { 0x4020266666666666ull, 0x4000b5ce8cccc099ull }, + { 0x402026e978d4fdf4ull, 0x4000b60f792942c2ull }, + { 0x4020276c8b439581ull, 0x4000b6506376f48full }, + { 0x402027ef9db22d0eull, 0x4000b6914bb5f764ull }, + { 0x40202872b020c49cull, 0x4000b6d231e66ca3ull }, + { 0x402028f5c28f5c29ull, 0x4000b713160875aaull }, + { 0x40202978d4fdf3b6ull, 0x4000b753f81c33d3ull }, + { 0x402029fbe76c8b44ull, 0x4000b794d821c877ull }, + { 0x40202a7ef9db22d1ull, 0x4000b7d5b61954ebull }, + { 0x40202b020c49ba5eull, 0x4000b8169202fa7eull }, + { 0x40202b851eb851ecull, 0x4000b8576bdeda82ull }, + { 0x40202c083126e979ull, 0x4000b89843ad163full }, + { 0x40202c8b43958106ull, 0x4000b8d9196dcefeull }, + { 0x40202d0e56041894ull, 0x4000b919ed212605ull }, + { 0x40202d916872b021ull, 0x4000b95abec73c94ull }, + { 0x40202e147ae147aeull, 0x4000b99b8e6033eaull }, + { 0x40202e978d4fdf3bull, 0x4000b9dc5bec2d43ull }, + { 0x40202f1a9fbe76c9ull, 0x4000ba1d276b49d6ull }, + { 0x40202f9db22d0e56ull, 0x4000ba5df0ddaadaull }, + { 0x40203020c49ba5e3ull, 0x4000ba9eb843717full }, + { 0x402030a3d70a3d71ull, 0x4000badf7d9cbef5ull }, + { 0x40203126e978d4feull, 0x4000bb2040e9b466ull }, + { 0x402031a9fbe76c8bull, 0x4000bb61022a72fcull }, + { 0x4020322d0e560419ull, 0x4000bba1c15f1bddull }, + { 0x402032b020c49ba6ull, 0x4000bbe27e87d029ull }, + { 0x4020333333333333ull, 0x4000bc2339a4b100ull }, + { 0x402033b645a1cac1ull, 0x4000bc63f2b5df7eull }, + { 0x402034395810624eull, 0x4000bca4a9bb7cbbull }, + { 0x402034bc6a7ef9dbull, 0x4000bce55eb5a9ceull }, + { 0x4020353f7ced9169ull, 0x4000bd2611a487c9ull }, + { 0x402035c28f5c28f6ull, 0x4000bd66c28837bbull }, + { 0x40203645a1cac083ull, 0x4000bda77160dab0ull }, + { 0x402036c8b4395810ull, 0x4000bde81e2e91b1ull }, + { 0x4020374bc6a7ef9eull, 0x4000be28c8f17dc6ull }, + { 0x402037ced916872bull, 0x4000be6971a9bfefull }, + { 0x40203851eb851eb8ull, 0x4000beaa1857792cull }, + { 0x402038d4fdf3b646ull, 0x4000beeabcfaca7cull }, + { 0x4020395810624dd3ull, 0x4000bf2b5f93d4d6ull }, + { 0x402039db22d0e560ull, 0x4000bf6c0022b931ull }, + { 0x40203a5e353f7ceeull, 0x4000bfac9ea79881ull }, + { 0x40203ae147ae147bull, 0x4000bfed3b2293b5ull }, + { 0x40203b645a1cac08ull, 0x4000c02dd593cbbbull }, + { 0x40203be76c8b4396ull, 0x4000c06e6dfb617cull }, + { 0x40203c6a7ef9db23ull, 0x4000c0af045975dfull }, + { 0x40203ced916872b0ull, 0x4000c0ef98ae29c8ull }, + { 0x40203d70a3d70a3eull, 0x4000c1302af99e17ull }, + { 0x40203df3b645a1cbull, 0x4000c170bb3bf3aaull }, + { 0x40203e76c8b43958ull, 0x4000c1b149754b5aull }, + { 0x40203ef9db22d0e5ull, 0x4000c1f1d5a5c5ffull }, + { 0x40203f7ced916873ull, 0x4000c2325fcd846full }, + { 0x4020400000000000ull, 0x4000c272e7eca777ull }, + { 0x40204083126e978dull, 0x4000c2b36e034fe8ull }, + { 0x4020410624dd2f1bull, 0x4000c2f3f2119e8cull }, + { 0x40204189374bc6a8ull, 0x4000c3347417b42aull }, + { 0x4020420c49ba5e35ull, 0x4000c374f415b188ull }, + { 0x4020428f5c28f5c3ull, 0x4000c3b5720bb766ull }, + { 0x402043126e978d50ull, 0x4000c3f5edf9e683ull }, + { 0x40204395810624ddull, 0x4000c43667e05f9aull }, + { 0x402044189374bc6bull, 0x4000c476dfbf4365ull }, + { 0x4020449ba5e353f8ull, 0x4000c4b75596b296ull }, + { 0x4020451eb851eb85ull, 0x4000c4f7c966cde1ull }, + { 0x402045a1cac08313ull, 0x4000c5383b2fb5f5ull }, + { 0x40204624dd2f1aa0ull, 0x4000c578aaf18b7dull }, + { 0x402046a7ef9db22dull, 0x4000c5b918ac6f23ull }, + { 0x4020472b020c49baull, 0x4000c5f98460818bull }, + { 0x402047ae147ae148ull, 0x4000c639ee0de35aull }, + { 0x4020483126e978d5ull, 0x4000c67a55b4b52dull }, + { 0x402048b439581062ull, 0x4000c6babb5517a2ull }, + { 0x402049374bc6a7f0ull, 0x4000c6fb1eef2b52ull }, + { 0x402049ba5e353f7dull, 0x4000c73b808310d3ull }, + { 0x40204a3d70a3d70aull, 0x4000c77be010e8b9ull }, + { 0x40204ac083126e98ull, 0x4000c7bc3d98d394ull }, + { 0x40204b4395810625ull, 0x4000c7fc991af1f0ull }, + { 0x40204bc6a7ef9db2ull, 0x4000c83cf2976458ull }, + { 0x40204c49ba5e3540ull, 0x4000c87d4a0e4b53ull }, + { 0x40204ccccccccccdull, 0x4000c8bd9f7fc765ull }, + { 0x40204d4fdf3b645aull, 0x4000c8fdf2ebf90eull }, + { 0x40204dd2f1a9fbe8ull, 0x4000c93e445300cdull }, + { 0x40204e5604189375ull, 0x4000c97e93b4ff1bull }, + { 0x40204ed916872b02ull, 0x4000c9bee1121471ull }, + { 0x40204f5c28f5c28full, 0x4000c9ff2c6a6144ull }, + { 0x40204fdf3b645a1dull, 0x4000ca3f75be0604ull }, + { 0x402050624dd2f1aaull, 0x4000ca7fbd0d2321ull }, + { 0x402050e560418937ull, 0x4000cac00257d905ull }, + { 0x4020516872b020c5ull, 0x4000cb00459e481aull }, + { 0x402051eb851eb852ull, 0x4000cb4086e090c3ull }, + { 0x4020526e978d4fdfull, 0x4000cb80c61ed365ull }, + { 0x402052f1a9fbe76dull, 0x4000cbc10359305eull }, + { 0x40205374bc6a7efaull, 0x4000cc013e8fc809ull }, + { 0x402053f7ced91687ull, 0x4000cc4177c2bac1ull }, + { 0x4020547ae147ae15ull, 0x4000cc81aef228dbull }, + { 0x402054fdf3b645a2ull, 0x4000ccc1e41e32abull }, + { 0x402055810624dd2full, 0x4000cd021746f880ull }, + { 0x40205604189374bdull, 0x4000cd42486c9aa8ull }, + { 0x402056872b020c4aull, 0x4000cd82778f396bull }, + { 0x4020570a3d70a3d7ull, 0x4000cdc2a4aef512ull }, + { 0x4020578d4fdf3b64ull, 0x4000ce02cfcbede1ull }, + { 0x40205810624dd2f2ull, 0x4000ce42f8e64417ull }, + { 0x4020589374bc6a7full, 0x4000ce831ffe17f3ull }, + { 0x40205916872b020cull, 0x4000cec3451389b0ull }, + { 0x402059999999999aull, 0x4000cf036826b985ull }, + { 0x40205a1cac083127ull, 0x4000cf438937c7a7ull }, + { 0x40205a9fbe76c8b4ull, 0x4000cf83a846d447ull }, + { 0x40205b22d0e56042ull, 0x4000cfc3c553ff96ull }, + { 0x40205ba5e353f7cfull, 0x4000d003e05f69bdull }, + { 0x40205c28f5c28f5cull, 0x4000d043f96932e6ull }, + { 0x40205cac083126eaull, 0x4000d08410717b36ull }, + { 0x40205d2f1a9fbe77ull, 0x4000d0c4257862d0ull }, + { 0x40205db22d0e5604ull, 0x4000d104387e09d4ull }, + { 0x40205e353f7ced92ull, 0x4000d1444982905eull }, + { 0x40205eb851eb851full, 0x4000d18458861687ull }, + { 0x40205f3b645a1cacull, 0x4000d1c46588bc67ull }, + { 0x40205fbe76c8b439ull, 0x4000d204708aa210ull }, + { 0x4020604189374bc7ull, 0x4000d244798be794ull }, + { 0x402060c49ba5e354ull, 0x4000d284808cacffull }, + { 0x40206147ae147ae1ull, 0x4000d2c4858d125cull }, + { 0x402061cac083126full, 0x4000d304888d37b4ull }, + { 0x4020624dd2f1a9fcull, 0x4000d344898d3d09ull }, + { 0x402062d0e5604189ull, 0x4000d384888d425eull }, + { 0x40206353f7ced917ull, 0x4000d3c4858d67b2ull }, + { 0x402063d70a3d70a4ull, 0x4000d404808dccffull }, + { 0x4020645a1cac0831ull, 0x4000d444798e923eull }, + { 0x402064dd2f1a9fbfull, 0x4000d484708fd766ull }, + { 0x402065604189374cull, 0x4000d4c46591bc67ull }, + { 0x402065e353f7ced9ull, 0x4000d50458946132ull }, + { 0x4020666666666666ull, 0x4000d5444997e5b4ull }, + { 0x402066e978d4fdf4ull, 0x4000d584389c69d6ull }, + { 0x4020676c8b439581ull, 0x4000d5c425a20d7dull }, + { 0x402067ef9db22d0eull, 0x4000d60410a8f08full }, + { 0x40206872b020c49cull, 0x4000d643f9b132ecull }, + { 0x402068f5c28f5c29ull, 0x4000d683e0baf471ull }, + { 0x40206978d4fdf3b6ull, 0x4000d6c3c5c654f8ull }, + { 0x402069fbe76c8b44ull, 0x4000d703a8d3745aull }, + { 0x40206a7ef9db22d1ull, 0x4000d74389e2726bull }, + { 0x40206b020c49ba5eull, 0x4000d78368f36efdull }, + { 0x40206b851eb851ecull, 0x4000d7c3460689deull }, + { 0x40206c083126e979ull, 0x4000d803211be2daull }, + { 0x40206c8b43958106ull, 0x4000d842fa3399baull }, + { 0x40206d0e56041894ull, 0x4000d882d14dce45ull }, + { 0x40206d916872b021ull, 0x4000d8c2a66aa03bull }, + { 0x40206e147ae147aeull, 0x4000d902798a2f5eull }, + { 0x40206e978d4fdf3bull, 0x4000d9424aac9b6aull }, + { 0x40206f1a9fbe76c9ull, 0x4000d98219d20419ull }, + { 0x40206f9db22d0e56ull, 0x4000d9c1e6fa8922ull }, + { 0x40207020c49ba5e3ull, 0x4000da01b2264a39ull }, + { 0x402070a3d70a3d71ull, 0x4000da417b556710ull }, + { 0x40207126e978d4feull, 0x4000da814287ff53ull }, + { 0x402071a9fbe76c8bull, 0x4000dac107be32aeull }, + { 0x4020722d0e560419ull, 0x4000db00caf820cbull }, + { 0x402072b020c49ba6ull, 0x4000db408c35e94cull }, + { 0x4020733333333333ull, 0x4000db804b77abd5ull }, + { 0x402073b645a1cac1ull, 0x4000dbc008bd8805ull }, + { 0x402074395810624eull, 0x4000dbffc4079d78ull }, + { 0x402074bc6a7ef9dbull, 0x4000dc3f7d560bc8ull }, + { 0x4020753f7ced9169ull, 0x4000dc7f34a8f28cull }, + { 0x402075c28f5c28f6ull, 0x4000dcbeea007155ull }, + { 0x40207645a1cac083ull, 0x4000dcfe9d5ca7b6ull }, + { 0x402076c8b4395810ull, 0x4000dd3e4ebdb53bull }, + { 0x4020774bc6a7ef9eull, 0x4000dd7dfe23b970ull }, + { 0x402077ced916872bull, 0x4000ddbdab8ed3dbull }, + { 0x40207851eb851eb8ull, 0x4000ddfd56ff2403ull }, + { 0x402078d4fdf3b646ull, 0x4000de3d0074c968ull }, + { 0x4020795810624dd3ull, 0x4000de7ca7efe389ull }, + { 0x402079db22d0e560ull, 0x4000debc4d7091e3ull }, + { 0x40207a5e353f7ceeull, 0x4000defbf0f6f3eeull }, + { 0x40207ae147ae147bull, 0x4000df3b92832920ull }, + { 0x40207b645a1cac08ull, 0x4000df7b321550edull }, + { 0x40207be76c8b4396ull, 0x4000dfbacfad8ac5ull }, + { 0x40207c6a7ef9db23ull, 0x4000dffa6b4bf615ull }, + { 0x40207ced916872b0ull, 0x4000e03a04f0b247ull }, + { 0x40207d70a3d70a3eull, 0x4000e0799c9bdec3ull }, + { 0x40207df3b645a1cbull, 0x4000e0b9324d9aedull }, + { 0x40207e76c8b43958ull, 0x4000e0f8c6060628ull }, + { 0x40207ef9db22d0e5ull, 0x4000e13857c53fd0ull }, + { 0x40207f7ced916873ull, 0x4000e177e78b6743ull }, + { 0x4020800000000000ull, 0x4000e1b775589bd9ull }, + { 0x40208083126e978dull, 0x4000e1f7012cfce8ull }, + { 0x4020810624dd2f1bull, 0x4000e2368b08a9c4ull }, + { 0x40208189374bc6a8ull, 0x4000e27612ebc1bbull }, + { 0x4020820c49ba5e35ull, 0x4000e2b598d6641bull }, + { 0x4020828f5c28f5c3ull, 0x4000e2f51cc8b02full }, + { 0x402083126e978d50ull, 0x4000e3349ec2c53dull }, + { 0x40208395810624ddull, 0x4000e3741ec4c28aull }, + { 0x402084189374bc6bull, 0x4000e3b39ccec758ull }, + { 0x4020849ba5e353f8ull, 0x4000e3f318e0f2e6ull }, + { 0x4020851eb851eb85ull, 0x4000e43292fb646eull }, + { 0x402085a1cac08313ull, 0x4000e4720b1e3b2aull }, + { 0x40208624dd2f1aa0ull, 0x4000e4b181499650ull }, + { 0x402086a7ef9db22dull, 0x4000e4f0f57d9512ull }, + { 0x4020872b020c49baull, 0x4000e53067ba56a1ull }, + { 0x402087ae147ae148ull, 0x4000e56fd7fffa2aull }, + { 0x4020883126e978d5ull, 0x4000e5af464e9ed7ull }, + { 0x402088b439581062ull, 0x4000e5eeb2a663cfull }, + { 0x402089374bc6a7f0ull, 0x4000e62e1d076838ull }, + { 0x402089ba5e353f7dull, 0x4000e66d8571cb33ull }, + { 0x40208a3d70a3d70aull, 0x4000e6acebe5abddull }, + { 0x40208ac083126e98ull, 0x4000e6ec50632955ull }, + { 0x40208b4395810625ull, 0x4000e72bb2ea62b1ull }, + { 0x40208bc6a7ef9db2ull, 0x4000e76b137b7708ull }, + { 0x40208c49ba5e3540ull, 0x4000e7aa7216856full }, + { 0x40208ccccccccccdull, 0x4000e7e9cebbacf3ull }, + { 0x40208d4fdf3b645aull, 0x4000e829296b0ca4ull }, + { 0x40208dd2f1a9fbe8ull, 0x4000e8688224c38bull }, + { 0x40208e5604189375ull, 0x4000e8a7d8e8f0b1ull }, + { 0x40208ed916872b02ull, 0x4000e8e72db7b31aull }, + { 0x40208f5c28f5c28full, 0x4000e926809129c7ull }, + { 0x40208fdf3b645a1dull, 0x4000e965d17573b9ull }, + { 0x402090624dd2f1aaull, 0x4000e9a52064afeaull }, + { 0x402090e560418937ull, 0x4000e9e46d5efd55ull }, + { 0x4020916872b020c5ull, 0x4000ea23b8647af0ull }, + { 0x402091eb851eb852ull, 0x4000ea63017547aeull }, + { 0x4020926e978d4fdfull, 0x4000eaa248918280ull }, + { 0x402092f1a9fbe76dull, 0x4000eae18db94a54ull }, + { 0x40209374bc6a7efaull, 0x4000eb20d0ecbe13ull }, + { 0x402093f7ced91687ull, 0x4000eb60122bfca8ull }, + { 0x4020947ae147ae15ull, 0x4000eb9f517724f7ull }, + { 0x402094fdf3b645a2ull, 0x4000ebde8ece55e1ull }, + { 0x402095810624dd2full, 0x4000ec1dca31ae48ull }, + { 0x40209604189374bdull, 0x4000ec5d03a14d06ull }, + { 0x402096872b020c4aull, 0x4000ec9c3b1d50f7ull }, + { 0x4020970a3d70a3d7ull, 0x4000ecdb70a5d8f0ull }, + { 0x4020978d4fdf3b64ull, 0x4000ed1aa43b03c6ull }, + { 0x40209810624dd2f2ull, 0x4000ed59d5dcf04bull }, + { 0x4020989374bc6a7full, 0x4000ed99058bbd4cull }, + { 0x40209916872b020cull, 0x4000edd833478994ull }, + { 0x402099999999999aull, 0x4000ee175f1073eeull }, + { 0x40209a1cac083127ull, 0x4000ee5688e69b1dull }, + { 0x40209a9fbe76c8b4ull, 0x4000ee95b0ca1de6ull }, + { 0x40209b22d0e56042ull, 0x4000eed4d6bb1b09ull }, + { 0x40209ba5e353f7cfull, 0x4000ef13fab9b141ull }, + { 0x40209c28f5c28f5cull, 0x4000ef531cc5ff4bull }, + { 0x40209cac083126eaull, 0x4000ef923ce023deull }, + { 0x40209d2f1a9fbe77ull, 0x4000efd15b083dadull }, + { 0x40209db22d0e5604ull, 0x4000f010773e6b6cull }, + { 0x40209e353f7ced92ull, 0x4000f04f9182cbc9ull }, + { 0x40209eb851eb851full, 0x4000f08ea9d57d70ull }, + { 0x40209f3b645a1cacull, 0x4000f0cdc0369f0bull }, + { 0x40209fbe76c8b439ull, 0x4000f10cd4a64f41ull }, + { 0x4020a04189374bc7ull, 0x4000f14be724acb5ull }, + { 0x4020a0c49ba5e354ull, 0x4000f18af7b1d607ull }, + { 0x4020a147ae147ae1ull, 0x4000f1ca064de9d6ull }, + { 0x4020a1cac083126full, 0x4000f20912f906bfull }, + { 0x4020a24dd2f1a9fcull, 0x4000f2481db34b57ull }, + { 0x4020a2d0e5604189ull, 0x4000f287267cd635ull }, + { 0x4020a353f7ced917ull, 0x4000f2c62d55c5edull }, + { 0x4020a3d70a3d70a4ull, 0x4000f305323e390dull }, + { 0x4020a45a1cac0831ull, 0x4000f34435364e23ull }, + { 0x4020a4dd2f1a9fbfull, 0x4000f383363e23baull }, + { 0x4020a5604189374cull, 0x4000f3c23555d858ull }, + { 0x4020a5e353f7ced9ull, 0x4000f401327d8a82ull }, + { 0x4020a66666666666ull, 0x4000f4402db558bbull }, + { 0x4020a6e978d4fdf4ull, 0x4000f47f26fd6181ull }, + { 0x4020a76c8b439581ull, 0x4000f4be1e55c350ull }, + { 0x4020a7ef9db22d0eull, 0x4000f4fd13be9ca2ull }, + { 0x4020a872b020c49cull, 0x4000f53c07380beeull }, + { 0x4020a8f5c28f5c29ull, 0x4000f57af8c22fa6ull }, + { 0x4020a978d4fdf3b6ull, 0x4000f5b9e85d263dull }, + { 0x4020a9fbe76c8b44ull, 0x4000f5f8d6090e20ull }, + { 0x4020aa7ef9db22d1ull, 0x4000f637c1c605bbull }, + { 0x4020ab020c49ba5eull, 0x4000f676ab942b76ull }, + { 0x4020ab851eb851ecull, 0x4000f6b593739db7ull }, + { 0x4020ac083126e979ull, 0x4000f6f479647ae1ull }, + { 0x4020ac8b43958106ull, 0x4000f7335d66e154ull }, + { 0x4020ad0e56041894ull, 0x4000f7723f7aef6dull }, + { 0x4020ad916872b021ull, 0x4000f7b11fa0c388ull }, + { 0x4020ae147ae147aeull, 0x4000f7effdd87bfbull }, + { 0x4020ae978d4fdf3bull, 0x4000f82eda22371bull }, + { 0x4020af1a9fbe76c9ull, 0x4000f86db47e133dull }, + { 0x4020af9db22d0e56ull, 0x4000f8ac8cec2eacull }, + { 0x4020b020c49ba5e3ull, 0x4000f8eb636ca7b8ull }, + { 0x4020b0a3d70a3d71ull, 0x4000f92a37ff9caaull }, + { 0x4020b126e978d4feull, 0x4000f9690aa52bc8ull }, + { 0x4020b1a9fbe76c8bull, 0x4000f9a7db5d7358ull }, + { 0x4020b22d0e560419ull, 0x4000f9e6aa28919aull }, + { 0x4020b2b020c49ba6ull, 0x4000fa257706a4ccull }, + { 0x4020b33333333333ull, 0x4000fa6441f7cb2cull }, + { 0x4020b3b645a1cac1ull, 0x4000faa30afc22f2ull }, + { 0x4020b4395810624eull, 0x4000fae1d213ca54ull }, + { 0x4020b4bc6a7ef9dbull, 0x4000fb20973edf86ull }, + { 0x4020b53f7ced9169ull, 0x4000fb5f5a7d80b9ull }, + { 0x4020b5c28f5c28f6ull, 0x4000fb9e1bcfcc1aull }, + { 0x4020b645a1cac083ull, 0x4000fbdcdb35dfd5ull }, + { 0x4020b6c8b4395810ull, 0x4000fc1b98afda13ull }, + { 0x4020b74bc6a7ef9eull, 0x4000fc5a543dd8f9ull }, + { 0x4020b7ced916872bull, 0x4000fc990ddffaa9ull }, + { 0x4020b851eb851eb8ull, 0x4000fcd7c5965d45ull }, + { 0x4020b8d4fdf3b646ull, 0x4000fd167b611ee9ull }, + { 0x4020b95810624dd3ull, 0x4000fd552f405db0ull }, + { 0x4020b9db22d0e560ull, 0x4000fd93e13437b2ull }, + { 0x4020ba5e353f7ceeull, 0x4000fdd2913ccb04ull }, + { 0x4020bae147ae147bull, 0x4000fe113f5a35b8ull }, + { 0x4020bb645a1cac08ull, 0x4000fe4feb8c95deull }, + { 0x4020bbe76c8b4396ull, 0x4000fe8e95d40983ull }, + { 0x4020bc6a7ef9db23ull, 0x4000fecd3e30aeb0ull }, + { 0x4020bced916872b0ull, 0x4000ff0be4a2a36dull }, + { 0x4020bd70a3d70a3eull, 0x4000ff4a892a05bfull }, + { 0x4020bdf3b645a1cbull, 0x4000ff892bc6f3a7ull }, + { 0x4020be76c8b43958ull, 0x4000ffc7cc798b24ull }, + { 0x4020bef9db22d0e5ull, 0x400100066b41ea32ull }, + { 0x4020bf7ced916873ull, 0x4001004508202eccull }, + { 0x4020c00000000000ull, 0x40010083a31476e7ull }, + { 0x4020c083126e978dull, 0x400100c23c1ee078ull }, + { 0x4020c10624dd2f1bull, 0x40010100d33f8971ull }, + { 0x4020c189374bc6a8ull, 0x4001013f68768fbfull }, + { 0x4020c20c49ba5e35ull, 0x4001017dfbc4114eull }, + { 0x4020c28f5c28f5c3ull, 0x400101bc8d282c08ull }, + { 0x4020c3126e978d50ull, 0x400101fb1ca2fdd2ull }, + { 0x4020c395810624ddull, 0x40010239aa34a490ull }, + { 0x4020c4189374bc6bull, 0x4001027835dd3e24ull }, + { 0x4020c49ba5e353f8ull, 0x400102b6bf9ce86bull }, + { 0x4020c51eb851eb85ull, 0x400102f54773c140ull }, + { 0x4020c5a1cac08313ull, 0x40010333cd61e67dull }, + { 0x4020c624dd2f1aa0ull, 0x40010372516775f7ull }, + { 0x4020c6a7ef9db22dull, 0x400103b0d3848d81ull }, + { 0x4020c72b020c49baull, 0x400103ef53b94aebull }, + { 0x4020c7ae147ae148ull, 0x4001042dd205cc04ull }, + { 0x4020c83126e978d5ull, 0x4001046c4e6a2e96ull }, + { 0x4020c8b439581062ull, 0x400104aac8e69069ull }, + { 0x4020c9374bc6a7f0ull, 0x400104e9417b0f44ull }, + { 0x4020c9ba5e353f7dull, 0x40010527b827c8e8ull }, + { 0x4020ca3d70a3d70aull, 0x400105662cecdb15ull }, + { 0x4020cac083126e98ull, 0x400105a49fca638bull }, + { 0x4020cb4395810625ull, 0x400105e310c08002ull }, + { 0x4020cbc6a7ef9db2ull, 0x400106217fcf4e32ull }, + { 0x4020cc49ba5e3540ull, 0x4001065fecf6ebd2ull }, + { 0x4020cccccccccccdull, 0x4001069e58377693ull }, + { 0x4020cd4fdf3b645aull, 0x400106dcc1910c25ull }, + { 0x4020cdd2f1a9fbe8ull, 0x4001071b2903ca36ull }, + { 0x4020ce5604189375ull, 0x400107598e8fce6eull }, + { 0x4020ced916872b02ull, 0x40010797f2353677ull }, + { 0x4020cf5c28f5c28full, 0x400107d653f41ff4ull }, + { 0x4020cfdf3b645a1dull, 0x40010814b3cca88aull }, + { 0x4020d0624dd2f1aaull, 0x4001085311beedd5ull }, + { 0x4020d0e560418937ull, 0x400108916dcb0d74ull }, + { 0x4020d16872b020c5ull, 0x400108cfc7f12501ull }, + { 0x4020d1eb851eb852ull, 0x4001090e20315212ull }, + { 0x4020d26e978d4fdfull, 0x4001094c768bb23eull }, + { 0x4020d2f1a9fbe76dull, 0x4001098acb006315ull }, + { 0x4020d374bc6a7efaull, 0x400109c91d8f8226ull }, + { 0x4020d3f7ced91687ull, 0x40010a076e392cffull }, + { 0x4020d47ae147ae15ull, 0x40010a45bcfd812aull }, + { 0x4020d4fdf3b645a2ull, 0x40010a8409dc9c2cull }, + { 0x4020d5810624dd2full, 0x40010ac254d69b8cull }, + { 0x4020d604189374bdull, 0x40010b009deb9ccbull }, + { 0x4020d6872b020c4aull, 0x40010b3ee51bbd67ull }, + { 0x4020d70a3d70a3d7ull, 0x40010b7d2a671addull }, + { 0x4020d78d4fdf3b64ull, 0x40010bbb6dcdd2a8ull }, + { 0x4020d810624dd2f2ull, 0x40010bf9af50023eull }, + { 0x4020d89374bc6a7full, 0x40010c37eeedc713ull }, + { 0x4020d916872b020cull, 0x40010c762ca73e99ull }, + { 0x4020d9999999999aull, 0x40010cb4687c863full }, + { 0x4020da1cac083127ull, 0x40010cf2a26dbb72ull }, + { 0x4020da9fbe76c8b4ull, 0x40010d30da7afb9aull }, + { 0x4020db22d0e56042ull, 0x40010d6f10a46420ull }, + { 0x4020dba5e353f7cfull, 0x40010dad44ea1266ull }, + { 0x4020dc28f5c28f5cull, 0x40010deb774c23cfull }, + { 0x4020dcac083126eaull, 0x40010e29a7cab5baull }, + { 0x4020dd2f1a9fbe77ull, 0x40010e67d665e583ull }, + { 0x4020ddb22d0e5604ull, 0x40010ea6031dd082ull }, + { 0x4020de353f7ced92ull, 0x40010ee42df29411ull }, + { 0x4020deb851eb851full, 0x40010f2256e44d81ull }, + { 0x4020df3b645a1cacull, 0x40010f607df31a26ull }, + { 0x4020dfbe76c8b439ull, 0x40010f9ea31f174dull }, + { 0x4020e04189374bc7ull, 0x40010fdcc6686244ull }, + { 0x4020e0c49ba5e354ull, 0x4001101ae7cf1854ull }, + { 0x4020e147ae147ae1ull, 0x40011059075356c2ull }, + { 0x4020e1cac083126full, 0x4001109724f53ad5ull }, + { 0x4020e24dd2f1a9fcull, 0x400110d540b4e1ccull }, + { 0x4020e2d0e5604189ull, 0x400111135a9268e8ull }, + { 0x4020e353f7ced917ull, 0x40011151728ded64ull }, + { 0x4020e3d70a3d70a4ull, 0x4001118f88a78c7aull }, + { 0x4020e45a1cac0831ull, 0x400111cd9cdf6361ull }, + { 0x4020e4dd2f1a9fbfull, 0x4001120baf358f4dull }, + { 0x4020e5604189374cull, 0x40011249bfaa2d6full }, + { 0x4020e5e353f7ced9ull, 0x40011287ce3d5af7ull }, + { 0x4020e66666666666ull, 0x400112c5daef3510ull }, + { 0x4020e6e978d4fdf4ull, 0x40011303e5bfd8e6ull }, + { 0x4020e76c8b439581ull, 0x40011341eeaf639eull }, + { 0x4020e7ef9db22d0eull, 0x4001137ff5bdf25cull }, + { 0x4020e872b020c49cull, 0x400113bdfaeba244ull }, + { 0x4020e8f5c28f5c29ull, 0x400113fbfe389072ull }, + { 0x4020e978d4fdf3b6ull, 0x40011439ffa4da05ull }, + { 0x4020e9fbe76c8b44ull, 0x40011477ff309c15ull }, + { 0x4020ea7ef9db22d1ull, 0x400114b5fcdbf3baull }, + { 0x4020eb020c49ba5eull, 0x400114f3f8a6fe08ull }, + { 0x4020eb851eb851ecull, 0x40011531f291d812ull }, + { 0x4020ec083126e979ull, 0x4001156fea9c9ee6ull }, + { 0x4020ec8b43958106ull, 0x400115ade0c76f90ull }, + { 0x4020ed0e56041894ull, 0x400115ebd512671cull }, + { 0x4020ed916872b021ull, 0x40011629c77da28full }, + { 0x4020ee147ae147aeull, 0x40011667b8093eefull }, + { 0x4020ee978d4fdf3bull, 0x400116a5a6b5593dull }, + { 0x4020ef1a9fbe76c9ull, 0x400116e393820e7bull }, + { 0x4020ef9db22d0e56ull, 0x400117217e6f7ba2ull }, + { 0x4020f020c49ba5e3ull, 0x4001175f677dbdafull }, + { 0x4020f0a3d70a3d71ull, 0x4001179d4eacf198ull }, + { 0x4020f126e978d4feull, 0x400117db33fd3453ull }, + { 0x4020f1a9fbe76c8bull, 0x40011819176ea2d1ull }, + { 0x4020f22d0e560419ull, 0x40011856f9015a02ull }, + { 0x4020f2b020c49ba6ull, 0x40011894d8b576d4ull }, + { 0x4020f33333333333ull, 0x400118d2b68b1630ull }, + { 0x4020f3b645a1cac1ull, 0x40011910928254feull }, + { 0x4020f4395810624eull, 0x4001194e6c9b5024ull }, + { 0x4020f4bc6a7ef9dbull, 0x4001198c44d62484ull }, + { 0x4020f53f7ced9169ull, 0x400119ca1b32eefeull }, + { 0x4020f5c28f5c28f6ull, 0x40011a07efb1cc6full }, + { 0x4020f645a1cac083ull, 0x40011a45c252d9b2ull }, + { 0x4020f6c8b4395810ull, 0x40011a839316339full }, + { 0x4020f74bc6a7ef9eull, 0x40011ac161fbf70bull }, + { 0x4020f7ced916872bull, 0x40011aff2f0440caull }, + { 0x4020f851eb851eb8ull, 0x40011b3cfa2f2daaull }, + { 0x4020f8d4fdf3b646ull, 0x40011b7ac37cda7cull }, + { 0x4020f95810624dd3ull, 0x40011bb88aed6408ull }, + { 0x4020f9db22d0e560ull, 0x40011bf65080e717ull }, + { 0x4020fa5e353f7ceeull, 0x40011c3414378071ull }, + { 0x4020fae147ae147bull, 0x40011c71d6114cd6ull }, + { 0x4020fb645a1cac08ull, 0x40011caf960e6909ull }, + { 0x4020fbe76c8b4396ull, 0x40011ced542ef1c7ull }, + { 0x4020fc6a7ef9db23ull, 0x40011d2b107303cbull }, + { 0x4020fced916872b0ull, 0x40011d68cadabbcfull }, + { 0x4020fd70a3d70a3eull, 0x40011da683663688ull }, + { 0x4020fdf3b645a1cbull, 0x40011de43a1590abull }, + { 0x4020fe76c8b43958ull, 0x40011e21eee8e6e7ull }, + { 0x4020fef9db22d0e5ull, 0x40011e5fa1e055edull }, + { 0x4020ff7ced916873ull, 0x40011e9d52fbfa67ull }, + { 0x4021000000000000ull, 0x40011edb023bf0feull }, + { 0x40210083126e978dull, 0x40011f18afa0565aull }, + { 0x4021010624dd2f1bull, 0x40011f565b29471eull }, + { 0x40210189374bc6a8ull, 0x40011f9404d6dfecull }, + { 0x4021020c49ba5e35ull, 0x40011fd1aca93d63ull }, + { 0x4021028f5c28f5c3ull, 0x4001200f52a07c20ull }, + { 0x402103126e978d50ull, 0x4001204cf6bcb8baull }, + { 0x40210395810624ddull, 0x4001208a98fe0fcbull }, + { 0x402104189374bc6bull, 0x400120c839649de7ull }, + { 0x4021049ba5e353f8ull, 0x40012105d7f07f9eull }, + { 0x4021051eb851eb85ull, 0x4001214374a1d182ull }, + { 0x402105a1cac08313ull, 0x400121810f78b01full }, + { 0x40210624dd2f1aa0ull, 0x400121bea87537ffull }, + { 0x402106a7ef9db22dull, 0x400121fc3f9785a9ull }, + { 0x4021072b020c49baull, 0x40012239d4dfb5a2ull }, + { 0x402107ae147ae148ull, 0x40012277684de46full }, + { 0x4021083126e978d5ull, 0x400122b4f9e22e8eull }, + { 0x402108b439581062ull, 0x400122f2899cb07cull }, + { 0x402109374bc6a7f0ull, 0x40012330177d86b5ull }, + { 0x402109ba5e353f7dull, 0x4001236da384cdb0ull }, + { 0x40210a3d70a3d70aull, 0x400123ab2db2a1e4ull }, + { 0x40210ac083126e98ull, 0x400123e8b6071fc3ull }, + { 0x40210b4395810625ull, 0x400124263c8263bfull }, + { 0x40210bc6a7ef9db2ull, 0x40012463c1248a44ull }, + { 0x40210c49ba5e3540ull, 0x400124a143edafbfull }, + { 0x40210ccccccccccdull, 0x400124dec4ddf099ull }, + { 0x40210d4fdf3b645aull, 0x4001251c43f56937ull }, + { 0x40210dd2f1a9fbe8ull, 0x40012559c13435fdull }, + { 0x40210e5604189375ull, 0x400125973c9a734dull }, + { 0x40210ed916872b02ull, 0x400125d4b6283d86ull }, + { 0x40210f5c28f5c28full, 0x400126122dddb102ull }, + { 0x40210fdf3b645a1dull, 0x4001264fa3baea1dull }, + { 0x402110624dd2f1aaull, 0x4001268d17c0052dull }, + { 0x402110e560418937ull, 0x400126ca89ed1e86ull }, + { 0x4021116872b020c5ull, 0x40012707fa42527aull }, + { 0x402111eb851eb852ull, 0x4001274568bfbd59ull }, + { 0x4021126e978d4fdfull, 0x40012782d5657b6eull }, + { 0x402112f1a9fbe76dull, 0x400127c04033a906ull }, + { 0x40211374bc6a7efaull, 0x400127fda92a6267ull }, + { 0x402113f7ced91687ull, 0x4001283b1049c3d6ull }, + { 0x4021147ae147ae15ull, 0x400128787591e996ull }, + { 0x402114fdf3b645a2ull, 0x400128b5d902efe8ull }, + { 0x402115810624dd2full, 0x400128f33a9cf307ull }, + { 0x40211604189374bdull, 0x400129309a600f31ull }, + { 0x402116872b020c4aull, 0x4001296df84c609cull }, + { 0x4021170a3d70a3d7ull, 0x400129ab54620380ull }, + { 0x4021178d4fdf3b64ull, 0x400129e8aea1140eull }, + { 0x40211810624dd2f2ull, 0x40012a260709ae7aull }, + { 0x4021189374bc6a7full, 0x40012a635d9beeefull }, + { 0x40211916872b020cull, 0x40012aa0b257f19aull }, + { 0x402119999999999aull, 0x40012ade053dd2a6ull }, + { 0x40211a1cac083127ull, 0x40012b1b564dae37ull }, + { 0x40211a9fbe76c8b4ull, 0x40012b58a587a073ull }, + { 0x40211b22d0e56042ull, 0x40012b95f2ebc57bull }, + { 0x40211ba5e353f7cfull, 0x40012bd33e7a396eull }, + { 0x40211c28f5c28f5cull, 0x40012c1088331869ull }, + { 0x40211cac083126eaull, 0x40012c4dd0167e87ull }, + { 0x40211d2f1a9fbe77ull, 0x40012c8b162487ddull }, + { 0x40211db22d0e5604ull, 0x40012cc85a5d5082ull }, + { 0x40211e353f7ced92ull, 0x40012d059cc0f488ull }, + { 0x40211eb851eb851full, 0x40012d42dd4f8fffull }, + { 0x40211f3b645a1cacull, 0x40012d801c093ef5ull }, + { 0x40211fbe76c8b439ull, 0x40012dbd58ee1d74ull }, + { 0x4021204189374bc7ull, 0x40012dfa93fe4786ull }, + { 0x402120c49ba5e354ull, 0x40012e37cd39d92full }, + { 0x40212147ae147ae1ull, 0x40012e7504a0ee74ull }, + { 0x402121cac083126full, 0x40012eb23a33a355ull }, + { 0x4021224dd2f1a9fcull, 0x40012eef6df213d1ull }, + { 0x402122d0e5604189ull, 0x40012f2c9fdc5be3ull }, + { 0x40212353f7ced917ull, 0x40012f69cff29786ull }, + { 0x402123d70a3d70a4ull, 0x40012fa6fe34e2afull }, + { 0x4021245a1cac0831ull, 0x40012fe42aa35953ull }, + { 0x402124dd2f1a9fbfull, 0x40013021553e1765ull }, + { 0x402125604189374cull, 0x4001305e7e0538d3ull }, + { 0x402125e353f7ced9ull, 0x4001309ba4f8d98bull }, + { 0x4021266666666667ull, 0x400130d8ca191576ull }, + { 0x402126e978d4fdf4ull, 0x40013115ed66087dull }, + { 0x4021276c8b439581ull, 0x400131530edfce85ull }, + { 0x402127ef9db22d0eull, 0x400131902e868370ull }, + { 0x40212872b020c49cull, 0x400131cd4c5a4321ull }, + { 0x402128f5c28f5c29ull, 0x4001320a685b2973ull }, + { 0x40212978d4fdf3b6ull, 0x4001324782895242ull }, + { 0x402129fbe76c8b44ull, 0x400132849ae4d969ull }, + { 0x40212a7ef9db22d1ull, 0x400132c1b16ddabbull }, + { 0x40212b020c49ba5eull, 0x400132fec624720full }, + { 0x40212b851eb851ecull, 0x4001333bd908bb35ull }, + { 0x40212c083126e979ull, 0x40013378ea1ad1fbull }, + { 0x40212c8b43958106ull, 0x400133b5f95ad22full }, + { 0x40212d0e56041894ull, 0x400133f306c8d79bull }, + { 0x40212d916872b021ull, 0x400134301264fe06ull }, + { 0x40212e147ae147aeull, 0x4001346d1c2f6134ull }, + { 0x40212e978d4fdf3bull, 0x400134aa24281ce9ull }, + { 0x40212f1a9fbe76c9ull, 0x400134e72a4f4ce4ull }, + { 0x40212f9db22d0e56ull, 0x400135242ea50ce3ull }, + { 0x40213020c49ba5e3ull, 0x40013561312978a2ull }, + { 0x402130a3d70a3d71ull, 0x4001359e31dcabd8ull }, + { 0x40213126e978d4feull, 0x400135db30bec23bull }, + { 0x402131a9fbe76c8bull, 0x400136182dcfd780ull }, + { 0x4021322d0e560419ull, 0x4001365529100758ull }, + { 0x402132b020c49ba6ull, 0x40013692227f6d71ull }, + { 0x4021333333333333ull, 0x400136cf1a1e2577ull }, + { 0x402133b645a1cac1ull, 0x4001370c0fec4b15ull }, + { 0x402134395810624eull, 0x4001374903e9f9f1ull }, + { 0x402134bc6a7ef9dbull, 0x40013785f6174db0ull }, + { 0x4021353f7ced9169ull, 0x400137c2e67461f5ull }, + { 0x402135c28f5c28f6ull, 0x400137ffd501525full }, + { 0x40213645a1cac083ull, 0x4001383cc1be3a8cull }, + { 0x402136c8b4395810ull, 0x40013879acab3617ull }, + { 0x4021374bc6a7ef9eull, 0x400138b695c86099ull }, + { 0x402137ced916872bull, 0x400138f37d15d5a6ull }, + { 0x40213851eb851eb8ull, 0x400139306293b0d3ull }, + { 0x402138d4fdf3b646ull, 0x4001396d46420db2ull }, + { 0x4021395810624dd3ull, 0x400139aa282107d0ull }, + { 0x402139db22d0e560ull, 0x400139e70830babaull }, + { 0x40213a5e353f7ceeull, 0x40013a23e67141f9ull }, + { 0x40213ae147ae147bull, 0x40013a60c2e2b915ull }, + { 0x40213b645a1cac08ull, 0x40013a9d9d853b93ull }, + { 0x40213be76c8b4396ull, 0x40013ada7658e4f5ull }, + { 0x40213c6a7ef9db23ull, 0x40013b174d5dd0baull }, + { 0x40213ced916872b0ull, 0x40013b5422941a60ull }, + { 0x40213d70a3d70a3eull, 0x40013b90f5fbdd64ull }, + { 0x40213df3b645a1cbull, 0x40013bcdc795353cull }, + { 0x40213e76c8b43958ull, 0x40013c0a97603d5full }, + { 0x40213ef9db22d0e5ull, 0x40013c47655d1140ull }, + { 0x40213f7ced916873ull, 0x40013c84318bcc52ull }, + { 0x4021400000000000ull, 0x40013cc0fbec8a02ull }, + { 0x40214083126e978dull, 0x40013cfdc47f65bdull }, + { 0x4021410624dd2f1bull, 0x40013d3a8b447aedull }, + { 0x40214189374bc6a8ull, 0x40013d77503be4faull }, + { 0x4021420c49ba5e35ull, 0x40013db41365bf47ull }, + { 0x4021428f5c28f5c3ull, 0x40013df0d4c2253aull }, + { 0x402143126e978d50ull, 0x40013e2d9451322full }, + { 0x40214395810624ddull, 0x40013e6a52130187ull }, + { 0x402144189374bc6bull, 0x40013ea70e07ae9cull }, + { 0x4021449ba5e353f8ull, 0x40013ee3c82f54c7ull }, + { 0x4021451eb851eb85ull, 0x40013f20808a0f5eull }, + { 0x402145a1cac08313ull, 0x40013f5d3717f9b5ull }, + { 0x40214624dd2f1aa0ull, 0x40013f99ebd92f1eull }, + { 0x402146a7ef9db22dull, 0x40013fd69ecdcae8ull }, + { 0x4021472b020c49baull, 0x400140134ff5e860ull }, + { 0x402147ae147ae148ull, 0x4001404fff51a2d0ull }, + { 0x4021483126e978d5ull, 0x4001408cace1157full }, + { 0x402148b439581062ull, 0x400140c958a45bb4ull }, + { 0x402149374bc6a7f0ull, 0x40014106029b90b1ull }, + { 0x402149ba5e353f7dull, 0x40014142aac6cfb6ull }, + { 0x40214a3d70a3d70aull, 0x4001417f51263402ull }, + { 0x40214ac083126e98ull, 0x400141bbf5b9d8d1ull }, + { 0x40214b4395810625ull, 0x400141f89881d95cull }, + { 0x40214bc6a7ef9db2ull, 0x40014235397e50d9ull }, + { 0x40214c49ba5e3540ull, 0x40014271d8af5a7eull }, + { 0x40214ccccccccccdull, 0x400142ae7615117cull }, + { 0x40214d4fdf3b645aull, 0x400142eb11af9103ull }, + { 0x40214dd2f1a9fbe8ull, 0x40014327ab7ef440ull }, + { 0x40214e5604189375ull, 0x400143644383565eull }, + { 0x40214ed916872b02ull, 0x400143a0d9bcd285ull }, + { 0x40214f5c28f5c28full, 0x400143dd6e2b83dcull }, + { 0x40214fdf3b645a1dull, 0x4001441a00cf8586ull }, + { 0x402150624dd2f1aaull, 0x4001445691a8f2a3ull }, + { 0x402150e560418937ull, 0x4001449320b7e654ull }, + { 0x4021516872b020c5ull, 0x400144cfadfc7bb4ull }, + { 0x402151eb851eb852ull, 0x4001450c3976cdddull }, + { 0x4021526e978d4fdfull, 0x40014548c326f7e7ull }, + { 0x402152f1a9fbe76dull, 0x400145854b0d14e7ull }, + { 0x40215374bc6a7efaull, 0x400145c1d1293ff0ull }, + { 0x402153f7ced91687ull, 0x400145fe557b9411ull }, + { 0x4021547ae147ae15ull, 0x4001463ad8042c5aull }, + { 0x402154fdf3b645a2ull, 0x4001467758c323d5ull }, + { 0x402155810624dd2full, 0x400146b3d7b8958bull }, + { 0x40215604189374bdull, 0x400146f054e49c85ull }, + { 0x402156872b020c4aull, 0x4001472cd04753c5ull }, + { 0x4021570a3d70a3d7ull, 0x4001476949e0d64eull }, + { 0x4021578d4fdf3b64ull, 0x400147a5c1b13f1full }, + { 0x40215810624dd2f2ull, 0x400147e237b8a936ull }, + { 0x4021589374bc6a7full, 0x4001481eabf72f8eull }, + { 0x40215916872b020cull, 0x4001485b1e6ced1eull }, + { 0x402159999999999aull, 0x400148978f19fcddull }, + { 0x40215a1cac083127ull, 0x400148d3fdfe79beull }, + { 0x40215a9fbe76c8b4ull, 0x400149106b1a7eb3ull }, + { 0x40215b22d0e56042ull, 0x4001494cd66e26abull }, + { 0x40215ba5e353f7cfull, 0x400149893ff98c92ull }, + { 0x40215c28f5c28f5cull, 0x400149c5a7bccb52ull }, + { 0x40215cac083126eaull, 0x40014a020db7fdd3ull }, + { 0x40215d2f1a9fbe77ull, 0x40014a3e71eb3efbull }, + { 0x40215db22d0e5604ull, 0x40014a7ad456a9abull }, + { 0x40215e353f7ced92ull, 0x40014ab734fa58c6ull }, + { 0x40215eb851eb851full, 0x40014af393d66728ull }, + { 0x40215f3b645a1cacull, 0x40014b2ff0eaefaeull }, + { 0x40215fbe76c8b439ull, 0x40014b6c4c380d31ull }, + { 0x4021604189374bc7ull, 0x40014ba8a5bdda89ull }, + { 0x402160c49ba5e354ull, 0x40014be4fd7c728aull }, + { 0x40216147ae147ae1ull, 0x40014c215373f007ull }, + { 0x402161cac083126full, 0x40014c5da7a46dcfull }, + { 0x4021624dd2f1a9fcull, 0x40014c99fa0e06afull }, + { 0x402162d0e5604189ull, 0x40014cd64ab0d574ull }, + { 0x40216353f7ced917ull, 0x40014d12998cf4e7ull }, + { 0x402163d70a3d70a4ull, 0x40014d4ee6a27fcdull }, + { 0x4021645a1cac0831ull, 0x40014d8b31f190eaull }, + { 0x402164dd2f1a9fbfull, 0x40014dc77b7a4302ull }, + { 0x402165604189374cull, 0x40014e03c33cb0d2ull }, + { 0x402165e353f7ced9ull, 0x40014e400938f518ull }, + { 0x4021666666666667ull, 0x40014e7c4d6f2a8full }, + { 0x402166e978d4fdf4ull, 0x40014eb88fdf6beeull }, + { 0x4021676c8b439581ull, 0x40014ef4d089d3edull }, + { 0x402167ef9db22d0eull, 0x40014f310f6e7d3eull }, + { 0x40216872b020c49cull, 0x40014f6d4c8d8294ull }, + { 0x402168f5c28f5c29ull, 0x40014fa987e6fe9bull }, + { 0x40216978d4fdf3b6ull, 0x40014fe5c17b0c02ull }, + { 0x402169fbe76c8b44ull, 0x40015021f949c573ull }, + { 0x40216a7ef9db22d1ull, 0x4001505e2f534595ull }, + { 0x40216b020c49ba5eull, 0x4001509a6397a70eull }, + { 0x40216b851eb851ecull, 0x400150d696170481ull }, + { 0x40216c083126e979ull, 0x40015112c6d1788eull }, + { 0x40216c8b43958106ull, 0x4001514ef5c71dd5ull }, + { 0x40216d0e56041894ull, 0x4001518b22f80ef1ull }, + { 0x40216d916872b021ull, 0x400151c74e64667cull }, + { 0x40216e147ae147aeull, 0x40015203780c3f0dull }, + { 0x40216e978d4fdf3bull, 0x4001523f9fefb33aull }, + { 0x40216f1a9fbe76c9ull, 0x4001527bc60edd96ull }, + { 0x40216f9db22d0e56ull, 0x400152b7ea69d8afull }, + { 0x40217020c49ba5e3ull, 0x400152f40d00bf16ull }, + { 0x402170a3d70a3d71ull, 0x400153302dd3ab55ull }, + { 0x40217126e978d4feull, 0x4001536c4ce2b7f6ull }, + { 0x402171a9fbe76c8bull, 0x400153a86a2dff7full }, + { 0x4021722d0e560419ull, 0x400153e485b59c76ull }, + { 0x402172b020c49ba6ull, 0x400154209f79a95dull }, + { 0x4021733333333333ull, 0x4001545cb77a40b3ull }, + { 0x402173b645a1cac1ull, 0x40015498cdb77cf8ull }, + { 0x402174395810624eull, 0x400154d4e23178a4ull }, + { 0x402174bc6a7ef9dbull, 0x40015510f4e84e33ull }, + { 0x4021753f7ced9169ull, 0x4001554d05dc181bull }, + { 0x402175c28f5c28f6ull, 0x40015589150cf0d0ull }, + { 0x40217645a1cac083ull, 0x400155c5227af2c3ull }, + { 0x402176c8b4395810ull, 0x400156012e263866ull }, + { 0x4021774bc6a7ef9eull, 0x4001563d380edc24ull }, + { 0x402177ced916872bull, 0x400156794034f86aull }, + { 0x40217851eb851eb8ull, 0x400156b54698a7a0ull }, + { 0x402178d4fdf3b646ull, 0x400156f14b3a042dull }, + { 0x4021795810624dd3ull, 0x4001572d4e192873ull }, + { 0x402179db22d0e560ull, 0x400157694f362ed6ull }, + { 0x40217a5e353f7ceeull, 0x400157a54e9131b6ull }, + { 0x40217ae147ae147bull, 0x400157e14c2a4b6dull }, + { 0x40217b645a1cac08ull, 0x4001581d48019659ull }, + { 0x40217be76c8b4396ull, 0x4001585942172cd1ull }, + { 0x40217c6a7ef9db23ull, 0x400158953a6b292bull }, + { 0x40217ced916872b0ull, 0x400158d130fda5bcull }, + { 0x40217d70a3d70a3eull, 0x4001590d25cebcd5ull }, + { 0x40217df3b645a1cbull, 0x4001594918de88c5ull }, + { 0x40217e76c8b43958ull, 0x400159850a2d23d9ull }, + { 0x40217ef9db22d0e5ull, 0x400159c0f9baa85cull }, + { 0x40217f7ced916873ull, 0x400159fce7873096ull }, + { 0x4021800000000000ull, 0x40015a38d392d6ceull }, + { 0x40218083126e978dull, 0x40015a74bdddb546ull }, + { 0x4021810624dd2f1bull, 0x40015ab0a667e641ull }, + { 0x40218189374bc6a8ull, 0x40015aec8d3183fdull }, + { 0x4021820c49ba5e35ull, 0x40015b28723aa8b7ull }, + { 0x4021828f5c28f5c3ull, 0x40015b6455836eabull }, + { 0x402183126e978d50ull, 0x40015ba0370bf00full }, + { 0x40218395810624ddull, 0x40015bdc16d4471aull }, + { 0x402184189374bc6bull, 0x40015c17f4dc8e00ull }, + { 0x4021849ba5e353f8ull, 0x40015c53d124def2ull }, + { 0x4021851eb851eb85ull, 0x40015c8fabad541eull }, + { 0x402185a1cac08313ull, 0x40015ccb847607b3ull }, + { 0x40218624dd2f1aa0ull, 0x40015d075b7f13d9ull }, + { 0x402186a7ef9db22dull, 0x40015d4330c892b9ull }, + { 0x4021872b020c49baull, 0x40015d7f04529e79ull }, + { 0x402187ae147ae148ull, 0x40015dbad61d513dull }, + { 0x4021883126e978d5ull, 0x40015df6a628c526ull }, + { 0x402188b439581062ull, 0x40015e3274751453ull }, + { 0x402189374bc6a7f0ull, 0x40015e6e410258e0ull }, + { 0x402189ba5e353f7dull, 0x40015eaa0bd0ace8ull }, + { 0x40218a3d70a3d70aull, 0x40015ee5d4e02a83ull }, + { 0x40218ac083126e98ull, 0x40015f219c30ebc7ull }, + { 0x40218b4395810625ull, 0x40015f5d61c30ac7ull }, + { 0x40218bc6a7ef9db2ull, 0x40015f992596a195ull }, + { 0x40218c49ba5e3540ull, 0x40015fd4e7abca40ull }, + { 0x40218ccccccccccdull, 0x40016010a8029ed4ull }, + { 0x40218d4fdf3b645aull, 0x4001604c669b395bull }, + { 0x40218dd2f1a9fbe8ull, 0x400160882375b3dfull }, + { 0x40218e5604189375ull, 0x400160c3de922865ull }, + { 0x40218ed916872b02ull, 0x400160ff97f0b0efull }, + { 0x40218f5c28f5c28full, 0x4001613b4f916780ull }, + { 0x40218fdf3b645a1dull, 0x4001617705746618ull }, + { 0x402190624dd2f1aaull, 0x400161b2b999c6b1ull }, + { 0x402190e560418937ull, 0x400161ee6c01a347ull }, + { 0x4021916872b020c5ull, 0x4001622a1cac15d3ull }, + { 0x402191eb851eb852ull, 0x40016265cb99384aull }, + { 0x4021926e978d4fdfull, 0x400162a178c9249full }, + { 0x402192f1a9fbe76dull, 0x400162dd243bf4c5ull }, + { 0x40219374bc6a7efaull, 0x40016318cdf1c2aaull }, + { 0x402193f7ced91687ull, 0x4001635475eaa83bull }, + { 0x4021947ae147ae15ull, 0x400163901c26bf64ull }, + { 0x402194fdf3b645a2ull, 0x400163cbc0a6220bull }, + { 0x402195810624dd2full, 0x400164076368ea17ull }, + { 0x40219604189374bdull, 0x40016443046f316dull }, + { 0x402196872b020c4aull, 0x4001647ea3b911edull }, + { 0x4021970a3d70a3d7ull, 0x400164ba4146a578ull }, + { 0x4021978d4fdf3b64ull, 0x400164f5dd1805e9ull }, + { 0x40219810624dd2f2ull, 0x40016531772d4d1cull }, + { 0x4021989374bc6a7full, 0x4001656d0f8694e9ull }, + { 0x40219916872b020cull, 0x400165a8a623f727ull }, + { 0x402199999999999aull, 0x400165e43b058daaull }, + { 0x40219a1cac083127ull, 0x4001661fce2b7243ull }, + { 0x40219a9fbe76c8b4ull, 0x4001665b5f95bec1ull }, + { 0x40219b22d0e56042ull, 0x40016696ef448cf3ull }, + { 0x40219ba5e353f7cfull, 0x400166d27d37f6a3ull }, + { 0x40219c28f5c28f5cull, 0x4001670e0970159aull }, + { 0x40219cac083126eaull, 0x4001674993ed039eull }, + { 0x40219d2f1a9fbe77ull, 0x400167851caeda74ull }, + { 0x40219db22d0e5604ull, 0x400167c0a3b5b3ddull }, + { 0x40219e353f7ced92ull, 0x400167fc2901a99bull }, + { 0x40219eb851eb851full, 0x40016837ac92d568ull }, + { 0x40219f3b645a1cacull, 0x400168732e695102ull }, + { 0x40219fbe76c8b439ull, 0x400168aeae853621ull }, + { 0x4021a04189374bc7ull, 0x400168ea2ce69e7dull }, + { 0x4021a0c49ba5e354ull, 0x40016925a98da3c9ull }, + { 0x4021a147ae147ae1ull, 0x40016961247a5fb8ull }, + { 0x4021a1cac083126full, 0x4001699c9dacebfaull }, + { 0x4021a24dd2f1a9fcull, 0x400169d81525623dull }, + { 0x4021a2d0e5604189ull, 0x40016a138ae3dc2cull }, + { 0x4021a353f7ced917ull, 0x40016a4efee87371ull }, + { 0x4021a3d70a3d70a4ull, 0x40016a8a713341b3ull }, + { 0x4021a45a1cac0831ull, 0x40016ac5e1c46096ull }, + { 0x4021a4dd2f1a9fbfull, 0x40016b01509be9bfull }, + { 0x4021a5604189374cull, 0x40016b3cbdb9f6ccull }, + { 0x4021a5e353f7ced9ull, 0x40016b78291ea15cull }, + { 0x4021a66666666667ull, 0x40016bb392ca030cull }, + { 0x4021a6e978d4fdf4ull, 0x40016beefabc3574ull }, + { 0x4021a76c8b439581ull, 0x40016c2a60f5522cull }, + { 0x4021a7ef9db22d0eull, 0x40016c65c57572caull }, + { 0x4021a872b020c49cull, 0x40016ca1283cb0e1ull }, + { 0x4021a8f5c28f5c29ull, 0x40016cdc894b2602ull }, + { 0x4021a978d4fdf3b6ull, 0x40016d17e8a0ebbaull }, + { 0x4021a9fbe76c8b44ull, 0x40016d53463e1b98ull }, + { 0x4021aa7ef9db22d1ull, 0x40016d8ea222cf23ull }, + { 0x4021ab020c49ba5eull, 0x40016dc9fc4f1fe6ull }, + { 0x4021ab851eb851ecull, 0x40016e0554c32765ull }, + { 0x4021ac083126e979ull, 0x40016e40ab7eff24ull }, + { 0x4021ac8b43958106ull, 0x40016e7c0082c0a4ull }, + { 0x4021ad0e56041894ull, 0x40016eb753ce8565ull }, + { 0x4021ad916872b021ull, 0x40016ef2a56266e2ull }, + { 0x4021ae147ae147aeull, 0x40016f2df53e7e97ull }, + { 0x4021ae978d4fdf3bull, 0x40016f694362e5fdull }, + { 0x4021af1a9fbe76c9ull, 0x40016fa48fcfb689ull }, + { 0x4021af9db22d0e56ull, 0x40016fdfda8509b0ull }, + { 0x4021b020c49ba5e3ull, 0x4001701b2382f8e3ull }, + { 0x4021b0a3d70a3d71ull, 0x400170566ac99d92ull }, + { 0x4021b126e978d4feull, 0x40017091b059112aull }, + { 0x4021b1a9fbe76c8bull, 0x400170ccf4316d16ull }, + { 0x4021b22d0e560419ull, 0x400171083652cac1ull }, + { 0x4021b2b020c49ba6ull, 0x4001714376bd438full }, + { 0x4021b33333333333ull, 0x4001717eb570f0e5ull }, + { 0x4021b3b645a1cac1ull, 0x400171b9f26dec28ull }, + { 0x4021b4395810624eull, 0x400171f52db44eb5ull }, + { 0x4021b4bc6a7ef9dbull, 0x40017230674431ebull }, + { 0x4021b53f7ced9169ull, 0x4001726b9f1daf27ull }, + { 0x4021b5c28f5c28f6ull, 0x400172a6d540dfc1ull }, + { 0x4021b645a1cac083ull, 0x400172e209addd10ull }, + { 0x4021b6c8b4395810ull, 0x4001731d3c64c06bull }, + { 0x4021b74bc6a7ef9eull, 0x400173586d65a324ull }, + { 0x4021b7ced916872bull, 0x400173939cb09e8cull }, + { 0x4021b851eb851eb8ull, 0x400173ceca45cbf1ull }, + { 0x4021b8d4fdf3b646ull, 0x40017409f62544a0ull }, + { 0x4021b95810624dd3ull, 0x40017445204f21e2ull }, + { 0x4021b9db22d0e560ull, 0x4001748048c37d00ull }, + { 0x4021ba5e353f7ceeull, 0x400174bb6f826f41ull }, + { 0x4021bae147ae147bull, 0x400174f6948c11e6ull }, + { 0x4021bb645a1cac08ull, 0x40017531b7e07e32ull }, + { 0x4021bbe76c8b4396ull, 0x4001756cd97fcd64ull }, + { 0x4021bc6a7ef9db23ull, 0x400175a7f96a18b9ull }, + { 0x4021bced916872b0ull, 0x400175e3179f796cull }, + { 0x4021bd70a3d70a3eull, 0x4001761e342008b6ull }, + { 0x4021bdf3b645a1cbull, 0x400176594eebdfccull }, + { 0x4021be76c8b43958ull, 0x40017694680317e4ull }, + { 0x4021bef9db22d0e5ull, 0x400176cf7f65ca2full }, + { 0x4021bf7ced916873ull, 0x4001770a95140fdfull }, + { 0x4021c00000000000ull, 0x40017745a90e021full }, + { 0x4021c083126e978dull, 0x40017780bb53ba1bull }, + { 0x4021c10624dd2f1bull, 0x400177bbcbe550ffull }, + { 0x4021c189374bc6a8ull, 0x400177f6dac2dfefull }, + { 0x4021c20c49ba5e35ull, 0x40017831e7ec8012ull }, + { 0x4021c28f5c28f5c3ull, 0x4001786cf3624a8bull }, + { 0x4021c3126e978d50ull, 0x400178a7fd24587aull }, + { 0x4021c395810624ddull, 0x400178e30532c2fdull }, + { 0x4021c4189374bc6bull, 0x4001791e0b8da332ull }, + { 0x4021c49ba5e353f8ull, 0x4001795910351231ull }, + { 0x4021c51eb851eb85ull, 0x4001799413292914ull }, + { 0x4021c5a1cac08313ull, 0x400179cf146a00f0ull }, + { 0x4021c624dd2f1aa0ull, 0x40017a0a13f7b2d9ull }, + { 0x4021c6a7ef9db22dull, 0x40017a4511d257e0ull }, + { 0x4021c72b020c49baull, 0x40017a800dfa0915ull }, + { 0x4021c7ae147ae148ull, 0x40017abb086edf85ull }, + { 0x4021c83126e978d5ull, 0x40017af60130f43bull }, + { 0x4021c8b439581062ull, 0x40017b30f840603full }, + { 0x4021c9374bc6a7f0ull, 0x40017b6bed9d3c99ull }, + { 0x4021c9ba5e353f7dull, 0x40017ba6e147a24dull }, + { 0x4021ca3d70a3d70aull, 0x40017be1d33faa5eull }, + { 0x4021cac083126e98ull, 0x40017c1cc3856dcbull }, + { 0x4021cb4395810625ull, 0x40017c57b2190593ull }, + { 0x4021cbc6a7ef9db2ull, 0x40017c929efa8ab1ull }, + { 0x4021cc49ba5e3540ull, 0x40017ccd8a2a1620ull }, + { 0x4021cccccccccccdull, 0x40017d0873a7c0d7ull }, + { 0x4021cd4fdf3b645aull, 0x40017d435b73a3cbull }, + { 0x4021cdd2f1a9fbe8ull, 0x40017d7e418dd7f0ull }, + { 0x4021ce5604189375ull, 0x40017db925f67637ull }, + { 0x4021ced916872b02ull, 0x40017df408ad978full }, + { 0x4021cf5c28f5c28full, 0x40017e2ee9b354e4ull }, + { 0x4021cfdf3b645a1dull, 0x40017e69c907c722ull }, + { 0x4021d0624dd2f1aaull, 0x40017ea4a6ab0730ull }, + { 0x4021d0e560418937ull, 0x40017edf829d2df6ull }, + { 0x4021d16872b020c5ull, 0x40017f1a5cde5457ull }, + { 0x4021d1eb851eb852ull, 0x40017f55356e9336ull }, + { 0x4021d26e978d4fdfull, 0x40017f900c4e0372ull }, + { 0x4021d2f1a9fbe76dull, 0x40017fcae17cbdeaull }, + { 0x4021d374bc6a7efaull, 0x40018005b4fadb79ull }, + { 0x4021d3f7ced91687ull, 0x4001804086c874f8ull }, + { 0x4021d47ae147ae15ull, 0x4001807b56e5a340ull }, + { 0x4021d4fdf3b645a2ull, 0x400180b625527f24ull }, + { 0x4021d5810624dd2full, 0x400180f0f20f2179ull }, + { 0x4021d604189374bdull, 0x4001812bbd1ba310ull }, + { 0x4021d6872b020c4aull, 0x4001816686781cb6ull }, + { 0x4021d70a3d70a3d7ull, 0x400181a14e24a738ull }, + { 0x4021d78d4fdf3b64ull, 0x400181dc14215b63ull }, + { 0x4021d810624dd2f2ull, 0x40018216d86e51fdull }, + { 0x4021d89374bc6a7full, 0x400182519b0ba3cdull }, + { 0x4021d916872b020cull, 0x4001828c5bf96997ull }, + { 0x4021d9999999999aull, 0x400182c71b37bc1eull }, + { 0x4021da1cac083127ull, 0x40018301d8c6b420ull }, + { 0x4021da9fbe76c8b4ull, 0x4001833c94a66a5dull }, + { 0x4021db22d0e56042ull, 0x400183774ed6f78full }, + { 0x4021dba5e353f7cfull, 0x400183b207587470ull }, + { 0x4021dc28f5c28f5cull, 0x400183ecbe2af9b8ull }, + { 0x4021dcac083126eaull, 0x40018427734ea01bull }, + { 0x4021dd2f1a9fbe77ull, 0x4001846226c3804dull }, + { 0x4021ddb22d0e5604ull, 0x4001849cd889b2ffull }, + { 0x4021de353f7ced92ull, 0x400184d788a150e0ull }, + { 0x4021deb851eb851full, 0x40018512370a729cull }, + { 0x4021df3b645a1cacull, 0x4001854ce3c530deull }, + { 0x4021dfbe76c8b439ull, 0x400185878ed1a44full }, + { 0x4021e04189374bc7ull, 0x400185c2382fe596ull }, + { 0x4021e0c49ba5e354ull, 0x400185fcdfe00d55ull }, + { 0x4021e147ae147ae1ull, 0x4001863785e23430ull }, + { 0x4021e1cac083126full, 0x400186722a3672c7ull }, + { 0x4021e24dd2f1a9fcull, 0x400186acccdce1b7ull }, + { 0x4021e2d0e5604189ull, 0x400186e76dd5999cull }, + { 0x4021e353f7ced917ull, 0x400187220d20b311ull }, + { 0x4021e3d70a3d70a4ull, 0x4001875caabe46acull }, + { 0x4021e45a1cac0831ull, 0x4001879746ae6d03ull }, + { 0x4021e4dd2f1a9fbfull, 0x400187d1e0f13eabull }, + { 0x4021e5604189374cull, 0x4001880c7986d432ull }, + { 0x4021e5e353f7ced9ull, 0x40018847106f462aull }, + { 0x4021e66666666667ull, 0x40018881a5aaad1full }, + { 0x4021e6e978d4fdf4ull, 0x400188bc3939219cull }, + { 0x4021e76c8b439581ull, 0x400188f6cb1abc29ull }, + { 0x4021e7ef9db22d0eull, 0x400189315b4f954eull }, + { 0x4021e872b020c49cull, 0x4001896be9d7c590ull }, + { 0x4021e8f5c28f5c29ull, 0x400189a676b36570ull }, + { 0x4021e978d4fdf3b6ull, 0x400189e101e28d6full }, + { 0x4021e9fbe76c8b44ull, 0x40018a1b8b65560cull }, + { 0x4021ea7ef9db22d1ull, 0x40018a56133bd7c3ull }, + { 0x4021eb020c49ba5eull, 0x40018a9099662b0dull }, + { 0x4021eb851eb851ecull, 0x40018acb1de46863ull }, + { 0x4021ec083126e979ull, 0x40018b05a0b6a83bull }, + { 0x4021ec8b43958106ull, 0x40018b4021dd0309ull }, + { 0x4021ed0e56041894ull, 0x40018b7aa157913eull }, + { 0x4021ed916872b021ull, 0x40018bb51f266b4aull }, + { 0x4021ee147ae147aeull, 0x40018bef9b49a99bull }, + { 0x4021ee978d4fdf3bull, 0x40018c2a15c1649bull }, + { 0x4021ef1a9fbe76c9ull, 0x40018c648e8db4b6ull }, + { 0x4021ef9db22d0e56ull, 0x40018c9f05aeb251ull }, + { 0x4021f020c49ba5e3ull, 0x40018cd97b2475d1ull }, + { 0x4021f0a3d70a3d71ull, 0x40018d13eeef179bull }, + { 0x4021f126e978d4feull, 0x40018d4e610eb00full }, + { 0x4021f1a9fbe76c8bull, 0x40018d88d183578cull }, + { 0x4021f22d0e560419ull, 0x40018dc3404d266eull }, + { 0x4021f2b020c49ba6ull, 0x40018dfdad6c3511ull }, + { 0x4021f33333333333ull, 0x40018e3818e09bcdull }, + { 0x4021f3b645a1cac1ull, 0x40018e7282aa72faull }, + { 0x4021f4395810624eull, 0x40018eaceac9d2eaull }, + { 0x4021f4bc6a7ef9dbull, 0x40018ee7513ed3f1ull }, + { 0x4021f53f7ced9169ull, 0x40018f21b6098e5full }, + { 0x4021f5c28f5c28f6ull, 0x40018f5c192a1a82ull }, + { 0x4021f645a1cac083ull, 0x40018f967aa090a7ull }, + { 0x4021f6c8b4395810ull, 0x40018fd0da6d0917ull }, + { 0x4021f74bc6a7ef9eull, 0x4001900b388f9c1cull }, + { 0x4021f7ced916872bull, 0x40019045950861f9ull }, + { 0x4021f851eb851eb8ull, 0x4001907fefd772f5ull }, + { 0x4021f8d4fdf3b646ull, 0x400190ba48fce74full }, + { 0x4021f95810624dd3ull, 0x400190f4a078d749ull }, + { 0x4021f9db22d0e560ull, 0x4001912ef64b5b1full }, + { 0x4021fa5e353f7ceeull, 0x400191694a748b0eull }, + { 0x4021fae147ae147bull, 0x400191a39cf47f4eull }, + { 0x4021fb645a1cac08ull, 0x400191ddedcb5017ull }, + { 0x4021fbe76c8b4396ull, 0x400192183cf915a0ull }, + { 0x4021fc6a7ef9db23ull, 0x400192528a7de819ull }, + { 0x4021fced916872b0ull, 0x4001928cd659dfb7ull }, + { 0x4021fd70a3d70a3eull, 0x400192c7208d14a6ull }, + { 0x4021fdf3b645a1cbull, 0x4001930169179f15ull }, + { 0x4021fe76c8b43958ull, 0x4001933baff9972full }, + { 0x4021fef9db22d0e5ull, 0x40019375f533151cull }, + { 0x4021ff7ced916873ull, 0x400193b038c43104ull }, + { 0x4022000000000000ull, 0x400193ea7aad030bull }, + { 0x40220083126e978dull, 0x40019424baeda354ull }, + { 0x4022010624dd2f1bull, 0x4001945ef9862a00ull }, + { 0x40220189374bc6a8ull, 0x400194993676af2dull }, + { 0x4022020c49ba5e35ull, 0x400194d371bf4af9ull }, + { 0x4022028f5c28f5c3ull, 0x4001950dab60157dull }, + { 0x402203126e978d50ull, 0x40019547e35926d3ull }, + { 0x40220395810624ddull, 0x4001958219aa9710ull }, + { 0x402204189374bc6bull, 0x400195bc4e547e4aull }, + { 0x4022049ba5e353f8ull, 0x400195f68156f493ull }, + { 0x4022051eb851eb85ull, 0x40019630b2b211f9ull }, + { 0x402205a1cac08313ull, 0x4001966ae265ee8eull }, + { 0x40220624dd2f1aa0ull, 0x400196a51072a25bull }, + { 0x402206a7ef9db22dull, 0x400196df3cd8456cull }, + { 0x4022072b020c49baull, 0x400197196796efc8ull }, + { 0x402207ae147ae148ull, 0x4001975390aeb977ull }, + { 0x4022083126e978d5ull, 0x4001978db81fba7aull }, + { 0x402208b439581062ull, 0x400197c7ddea0ad5ull }, + { 0x402209374bc6a7f0ull, 0x40019802020dc289ull }, + { 0x402209ba5e353f7dull, 0x4001983c248af991ull }, + { 0x40220a3d70a3d70aull, 0x400198764561c7eaull }, + { 0x40220ac083126e98ull, 0x400198b06492458full }, + { 0x40220b4395810625ull, 0x400198ea821c8a76ull }, + { 0x40220bc6a7ef9db2ull, 0x400199249e00ae96ull }, + { 0x40220c49ba5e3540ull, 0x4001995eb83ec9e2ull }, + { 0x40220ccccccccccdull, 0x40019998d0d6f44cull }, + { 0x40220d4fdf3b645aull, 0x400199d2e7c945c3ull }, + { 0x40220dd2f1a9fbe8ull, 0x40019a0cfd15d636ull }, + { 0x40220e5604189375ull, 0x40019a4710bcbd8eull }, + { 0x40220ed916872b02ull, 0x40019a8122be13b7ull }, + { 0x40220f5c28f5c28full, 0x40019abb3319f097ull }, + { 0x40220fdf3b645a1dull, 0x40019af541d06c14ull }, + { 0x402210624dd2f1aaull, 0x40019b2f4ee19e12ull }, + { 0x402210e560418937ull, 0x40019b695a4d9e70ull }, + { 0x4022116872b020c5ull, 0x40019ba364148511ull }, + { 0x402211eb851eb852ull, 0x40019bdd6c3669cfull }, + { 0x4022126e978d4fdfull, 0x40019c1772b36486ull }, + { 0x402212f1a9fbe76dull, 0x40019c51778b8d11ull }, + { 0x40221374bc6a7efaull, 0x40019c8b7abefb46ull }, + { 0x402213f7ced91687ull, 0x40019cc57c4dc6faull }, + { 0x4022147ae147ae15ull, 0x40019cff7c380800ull }, + { 0x402214fdf3b645a2ull, 0x40019d397a7dd62aull }, + { 0x402215810624dd2full, 0x40019d73771f4946ull }, + { 0x40221604189374bdull, 0x40019dad721c7922ull }, + { 0x402216872b020c4aull, 0x40019de76b757d89ull }, + { 0x4022170a3d70a3d7ull, 0x40019e21632a6e44ull }, + { 0x4022178d4fdf3b64ull, 0x40019e5b593b6319ull }, + { 0x40221810624dd2f2ull, 0x40019e954da873cfull }, + { 0x4022189374bc6a7full, 0x40019ecf4071b828ull }, + { 0x40221916872b020cull, 0x40019f09319747e4ull }, + { 0x402219999999999aull, 0x40019f4321193ac4ull }, + { 0x40221a1cac083127ull, 0x40019f7d0ef7a883ull }, + { 0x40221a9fbe76c8b4ull, 0x40019fb6fb32a8ddull }, + { 0x40221b22d0e56042ull, 0x40019ff0e5ca538bull }, + { 0x40221ba5e353f7cfull, 0x4001a02acebec044ull }, + { 0x40221c28f5c28f5cull, 0x4001a064b61006bcull }, + { 0x40221cac083126eaull, 0x4001a09e9bbe3ea7ull }, + { 0x40221d2f1a9fbe77ull, 0x4001a0d87fc97fb4ull }, + { 0x40221db22d0e5604ull, 0x4001a1126231e194ull }, + { 0x40221e353f7ced92ull, 0x4001a14c42f77bf3ull }, + { 0x40221eb851eb851full, 0x4001a186221a667cull }, + { 0x40221f3b645a1cacull, 0x4001a1bfff9ab8d8ull }, + { 0x40221fbe76c8b439ull, 0x4001a1f9db788aadull }, + { 0x4022204189374bc7ull, 0x4001a233b5b3f3a1ull }, + { 0x402220c49ba5e354ull, 0x4001a26d8e4d0b56ull }, + { 0x40222147ae147ae1ull, 0x4001a2a76543e96eull }, + { 0x402221cac083126full, 0x4001a2e13a98a586ull }, + { 0x4022224dd2f1a9fcull, 0x4001a31b0e4b573cull }, + { 0x402222d0e5604189ull, 0x4001a354e05c162bull }, + { 0x40222353f7ced917ull, 0x4001a38eb0caf9ebull }, + { 0x402223d70a3d70a4ull, 0x4001a3c87f981a14ull }, + { 0x4022245a1cac0831ull, 0x4001a4024cc38e3aull }, + { 0x402224dd2f1a9fbfull, 0x4001a43c184d6df0ull }, + { 0x402225604189374cull, 0x4001a475e235d0c6ull }, + { 0x402225e353f7ced9ull, 0x4001a4afaa7cce4dull }, + { 0x4022266666666667ull, 0x4001a4e971227e10ull }, + { 0x402226e978d4fdf4ull, 0x4001a5233626f79aull }, + { 0x4022276c8b439581ull, 0x4001a55cf98a5275ull }, + { 0x402227ef9db22d0eull, 0x4001a596bb4ca627ull }, + { 0x40222872b020c49cull, 0x4001a5d07b6e0a35ull }, + { 0x402228f5c28f5c29ull, 0x4001a60a39ee9622ull }, + { 0x40222978d4fdf3b6ull, 0x4001a643f6ce616full }, + { 0x402229fbe76c8b44ull, 0x4001a67db20d839bull }, + { 0x40222a7ef9db22d1ull, 0x4001a6b76bac1423ull }, + { 0x40222b020c49ba5eull, 0x4001a6f123aa2a81ull }, + { 0x40222b851eb851ecull, 0x4001a72ada07de2full }, + { 0x40222c083126e979ull, 0x4001a7648ec546a4ull }, + { 0x40222c8b43958106ull, 0x4001a79e41e27b55ull }, + { 0x40222d0e56041894ull, 0x4001a7d7f35f93b4ull }, + { 0x40222d916872b021ull, 0x4001a811a33ca733ull }, + { 0x40222e147ae147aeull, 0x4001a84b5179cd41ull }, + { 0x40222e978d4fdf3bull, 0x4001a884fe171d4bull }, + { 0x40222f1a9fbe76c9ull, 0x4001a8bea914aebcull }, + { 0x40222f9db22d0e56ull, 0x4001a8f8527298fdull }, + { 0x40223020c49ba5e3ull, 0x4001a931fa30f375ull }, + { 0x402230a3d70a3d71ull, 0x4001a96ba04fd58aull }, + { 0x40223126e978d4feull, 0x4001a9a544cf569eull }, + { 0x402231a9fbe76c8bull, 0x4001a9dee7af8e13ull }, + { 0x4022322d0e560419ull, 0x4001aa1888f09349ull }, + { 0x402232b020c49ba6ull, 0x4001aa5228927d9cull }, + { 0x4022333333333333ull, 0x4001aa8bc6956467ull }, + { 0x402233b645a1cac1ull, 0x4001aac562f95f05ull }, + { 0x402234395810624eull, 0x4001aafefdbe84ccull }, + { 0x402234bc6a7ef9dbull, 0x4001ab3896e4ed11ull }, + { 0x4022353f7ced9169ull, 0x4001ab722e6caf29ull }, + { 0x402235c28f5c28f6ull, 0x4001ababc455e264ull }, + { 0x40223645a1cac083ull, 0x4001abe558a09e13ull }, + { 0x402236c8b4395810ull, 0x4001ac1eeb4cf982ull }, + { 0x4022374bc6a7ef9eull, 0x4001ac587c5b0bfeull }, + { 0x402237ced916872bull, 0x4001ac920bcaeccfull }, + { 0x40223851eb851eb8ull, 0x4001accb999cb33dull }, + { 0x402238d4fdf3b646ull, 0x4001ad0525d0768full }, + { 0x4022395810624dd3ull, 0x4001ad3eb0664e07ull }, + { 0x402239db22d0e560ull, 0x4001ad78395e50e8ull }, + { 0x40223a5e353f7ceeull, 0x4001adb1c0b89672ull }, + { 0x40223ae147ae147bull, 0x4001adeb467535e1ull }, + { 0x40223b645a1cac08ull, 0x4001ae24ca944673ull }, + { 0x40223be76c8b4396ull, 0x4001ae5e4d15df61ull }, + { 0x40223c6a7ef9db23ull, 0x4001ae97cdfa17e2ull }, + { 0x40223ced916872b0ull, 0x4001aed14d41072dull }, + { 0x40223d70a3d70a3eull, 0x4001af0acaeac476ull }, + { 0x40223df3b645a1cbull, 0x4001af4446f766efull }, + { 0x40223e76c8b43958ull, 0x4001af7dc16705c8ull }, + { 0x40223ef9db22d0e5ull, 0x4001afb73a39b830ull }, + { 0x40223f7ced916873ull, 0x4001aff0b16f9552ull }, + { 0x4022400000000000ull, 0x4001b02a2708b459ull }, + { 0x40224083126e978dull, 0x4001b0639b052c6dull }, + { 0x4022410624dd2f1bull, 0x4001b09d0d6514b5ull }, + { 0x40224189374bc6a8ull, 0x4001b0d67e288455ull }, + { 0x4022420c49ba5e35ull, 0x4001b10fed4f926full }, + { 0x4022428f5c28f5c3ull, 0x4001b1495ada5624ull }, + { 0x402243126e978d50ull, 0x4001b182c6c8e693ull }, + { 0x40224395810624ddull, 0x4001b1bc311b5ad8ull }, + { 0x402244189374bc6bull, 0x4001b1f599d1ca0eull }, + { 0x4022449ba5e353f8ull, 0x4001b22f00ec4b4eull }, + { 0x4022451eb851eb85ull, 0x4001b268666af5aeull }, + { 0x402245a1cac08313ull, 0x4001b2a1ca4de044ull }, + { 0x40224624dd2f1aa0ull, 0x4001b2db2c952222ull }, + { 0x402246a7ef9db22dull, 0x4001b3148d40d25aull }, + { 0x4022472b020c49baull, 0x4001b34dec5107faull }, + { 0x402247ae147ae148ull, 0x4001b38749c5da10ull }, + { 0x4022483126e978d5ull, 0x4001b3c0a59f5fa6ull }, + { 0x402248b439581062ull, 0x4001b3f9ffddafc7ull }, + { 0x402249374bc6a7f0ull, 0x4001b4335880e179ull }, + { 0x402249ba5e353f7dull, 0x4001b46caf890bc2ull }, + { 0x40224a3d70a3d70aull, 0x4001b4a604f645a4ull }, + { 0x40224ac083126e98ull, 0x4001b4df58c8a623ull }, + { 0x40224b4395810625ull, 0x4001b518ab00443dull }, + { 0x40224bc6a7ef9db2ull, 0x4001b551fb9d36f0ull }, + { 0x40224c49ba5e3540ull, 0x4001b58b4a9f9538ull }, + { 0x40224ccccccccccdull, 0x4001b5c49807760eull }, + { 0x40224d4fdf3b645aull, 0x4001b5fde3d4f06aull }, + { 0x40224dd2f1a9fbe8ull, 0x4001b6372e081b43ull }, + { 0x40224e5604189375ull, 0x4001b67076a10d8cull }, + { 0x40224ed916872b02ull, 0x4001b6a9bd9fde38ull }, + { 0x40224f5c28f5c28full, 0x4001b6e30304a436ull }, + { 0x40224fdf3b645a1dull, 0x4001b71c46cf7675ull }, + { 0x402250624dd2f1aaull, 0x4001b75589006be1ull }, + { 0x402250e560418937ull, 0x4001b78ec9979b64ull }, + { 0x4022516872b020c5ull, 0x4001b7c808951be8ull }, + { 0x402251eb851eb852ull, 0x4001b80145f90452ull }, + { 0x4022526e978d4fdfull, 0x4001b83a81c36b87ull }, + { 0x402252f1a9fbe76dull, 0x4001b873bbf4686bull }, + { 0x40225374bc6a7efaull, 0x4001b8acf48c11dcull }, + { 0x402253f7ced91687ull, 0x4001b8e62b8a7ebcull }, + { 0x4022547ae147ae15ull, 0x4001b91f60efc5e6ull }, + { 0x402254fdf3b645a2ull, 0x4001b95894bbfe35ull }, + { 0x402255810624dd2full, 0x4001b991c6ef3e84ull }, + { 0x40225604189374bdull, 0x4001b9caf7899da8ull }, + { 0x402256872b020c4aull, 0x4001ba04268b3278ull }, + { 0x4022570a3d70a3d7ull, 0x4001ba3d53f413c5ull }, + { 0x4022578d4fdf3b64ull, 0x4001ba767fc45863ull }, + { 0x40225810624dd2f2ull, 0x4001baafa9fc1722ull }, + { 0x4022589374bc6a7full, 0x4001bae8d29b66cdull }, + { 0x40225916872b020cull, 0x4001bb21f9a25e31ull }, + { 0x402259999999999aull, 0x4001bb5b1f111418ull }, + { 0x40225a1cac083127ull, 0x4001bb9442e79f4aull }, + { 0x40225a9fbe76c8b4ull, 0x4001bbcd6526168dull }, + { 0x40225b22d0e56042ull, 0x4001bc0685cc90a5ull }, + { 0x40225ba5e353f7cfull, 0x4001bc3fa4db2454ull }, + { 0x40225c28f5c28f5cull, 0x4001bc78c251e85aull }, + { 0x40225cac083126eaull, 0x4001bcb1de30f377ull }, + { 0x40225d2f1a9fbe77ull, 0x4001bceaf8785c66ull }, + { 0x40225db22d0e5604ull, 0x4001bd24112839e2ull }, + { 0x40225e353f7ced92ull, 0x4001bd5d2840a2a4ull }, + { 0x40225eb851eb851full, 0x4001bd963dc1ad62ull }, + { 0x40225f3b645a1cacull, 0x4001bdcf51ab70d2ull }, + { 0x40225fbe76c8b439ull, 0x4001be0863fe03a6ull }, + { 0x4022604189374bc7ull, 0x4001be4174b97c90ull }, + { 0x402260c49ba5e354ull, 0x4001be7a83ddf23full }, + { 0x40226147ae147ae1ull, 0x4001beb3916b7b5full }, + { 0x402261cac083126full, 0x4001beec9d622e9eull }, + { 0x4022624dd2f1a9fcull, 0x4001bf25a7c222a3ull }, + { 0x402262d0e5604189ull, 0x4001bf5eb08b6e17ull }, + { 0x40226353f7ced917ull, 0x4001bf97b7be27a0ull }, + { 0x402263d70a3d70a4ull, 0x4001bfd0bd5a65e0ull }, + { 0x4022645a1cac0831ull, 0x4001c009c1603f7bull }, + { 0x402264dd2f1a9fbfull, 0x4001c042c3cfcb10ull }, + { 0x402265604189374cull, 0x4001c07bc4a91f3eull }, + { 0x402265e353f7ced9ull, 0x4001c0b4c3ec52a0ull }, + { 0x4022666666666667ull, 0x4001c0edc1997bd2ull }, + { 0x402266e978d4fdf4ull, 0x4001c126bdb0b16cull }, + { 0x4022676c8b439581ull, 0x4001c15fb8320a03ull }, + { 0x402267ef9db22d0eull, 0x4001c198b11d9c2eull }, + { 0x40226872b020c49cull, 0x4001c1d1a8737e7full }, + { 0x402268f5c28f5c29ull, 0x4001c20a9e33c786ull }, + { 0x40226978d4fdf3b6ull, 0x4001c243925e8dd4ull }, + { 0x402269fbe76c8b44ull, 0x4001c27c84f3e7f5ull }, + { 0x40226a7ef9db22d1ull, 0x4001c2b575f3ec75ull }, + { 0x40226b020c49ba5eull, 0x4001c2ee655eb1dcull }, + { 0x40226b851eb851ecull, 0x4001c32753344eb3ull }, + { 0x40226c083126e979ull, 0x4001c3603f74d97full }, + { 0x40226c8b43958106ull, 0x4001c3992a2068c5ull }, + { 0x40226d0e56041894ull, 0x4001c3d213371305ull }, + { 0x40226d916872b021ull, 0x4001c40afab8eec1ull }, + { 0x40226e147ae147aeull, 0x4001c443e0a61276ull }, + { 0x40226e978d4fdf3cull, 0x4001c47cc4fe94a0ull }, + { 0x40226f1a9fbe76c9ull, 0x4001c4b5a7c28bbbull }, + { 0x40226f9db22d0e56ull, 0x4001c4ee88f20e3eull }, + { 0x40227020c49ba5e3ull, 0x4001c527688d32a0ull }, + { 0x402270a3d70a3d71ull, 0x4001c56046940f57ull }, + { 0x40227126e978d4feull, 0x4001c5992306bad4ull }, + { 0x402271a9fbe76c8bull, 0x4001c5d1fde54b8aull }, + { 0x4022722d0e560419ull, 0x4001c60ad72fd7e7ull }, + { 0x402272b020c49ba6ull, 0x4001c643aee67659ull }, + { 0x4022733333333333ull, 0x4001c67c85093d4bull }, + { 0x402273b645a1cac1ull, 0x4001c6b559984327ull }, + { 0x402274395810624eull, 0x4001c6ee2c939e55ull }, + { 0x402274bc6a7ef9dbull, 0x4001c726fdfb6539ull }, + { 0x4022753f7ced9169ull, 0x4001c75fcdcfae3aull }, + { 0x402275c28f5c28f6ull, 0x4001c7989c108fb8ull }, + { 0x40227645a1cac083ull, 0x4001c7d168be2014ull }, + { 0x402276c8b4395810ull, 0x4001c80a33d875acull }, + { 0x4022774bc6a7ef9eull, 0x4001c842fd5fa6ddull }, + { 0x402277ced916872bull, 0x4001c87bc553ca02ull }, + { 0x40227851eb851eb8ull, 0x4001c8b48bb4f573ull }, + { 0x402278d4fdf3b646ull, 0x4001c8ed50833f88ull }, + { 0x4022795810624dd3ull, 0x4001c92613bebe96ull }, + { 0x402279db22d0e560ull, 0x4001c95ed56788efull }, + { 0x40227a5e353f7ceeull, 0x4001c997957db4e6ull }, + { 0x40227ae147ae147bull, 0x4001c9d0540158c9ull }, + { 0x40227b645a1cac08ull, 0x4001ca0910f28ae7ull }, + { 0x40227be76c8b4396ull, 0x4001ca41cc51618cull }, + { 0x40227c6a7ef9db23ull, 0x4001ca7a861df301ull }, + { 0x40227ced916872b0ull, 0x4001cab33e58558full }, + { 0x40227d70a3d70a3eull, 0x4001caebf5009f7cull }, + { 0x40227df3b645a1cbull, 0x4001cb24aa16e70bull }, + { 0x40227e76c8b43958ull, 0x4001cb5d5d9b4281ull }, + { 0x40227ef9db22d0e5ull, 0x4001cb960f8dc81eull }, + { 0x40227f7ced916873ull, 0x4001cbcebfee8e21ull }, + { 0x4022800000000000ull, 0x4001cc076ebdaac6ull }, + { 0x40228083126e978dull, 0x4001cc401bfb3449ull }, + { 0x4022810624dd2f1bull, 0x4001cc78c7a740e3ull }, + { 0x40228189374bc6a8ull, 0x4001ccb171c1e6ccull }, + { 0x4022820c49ba5e35ull, 0x4001ccea1a4b3c3aull }, + { 0x4022828f5c28f5c3ull, 0x4001cd22c1435760ull }, + { 0x402283126e978d50ull, 0x4001cd5b66aa4e70ull }, + { 0x40228395810624ddull, 0x4001cd940a80379bull }, + { 0x402284189374bc6bull, 0x4001cdccacc5290full }, + { 0x4022849ba5e353f8ull, 0x4001ce054d7938f8ull }, + { 0x4022851eb851eb85ull, 0x4001ce3dec9c7d81ull }, + { 0x402285a1cac08313ull, 0x4001ce768a2f0cd3ull }, + { 0x40228624dd2f1aa0ull, 0x4001ceaf2630fd14ull }, + { 0x402286a7ef9db22dull, 0x4001cee7c0a2646aull }, + { 0x4022872b020c49baull, 0x4001cf20598358f8ull }, + { 0x402287ae147ae148ull, 0x4001cf58f0d3f0e0ull }, + { 0x4022883126e978d5ull, 0x4001cf9186944240ull }, + { 0x402288b439581062ull, 0x4001cfca1ac46338ull }, + { 0x402289374bc6a7f0ull, 0x4001d002ad6469e3ull }, + { 0x402289ba5e353f7dull, 0x4001d03b3e746c5aull }, + { 0x40228a3d70a3d70aull, 0x4001d073cdf480b7ull }, + { 0x40228ac083126e98ull, 0x4001d0ac5be4bd10ull }, + { 0x40228b4395810625ull, 0x4001d0e4e8453779ull }, + { 0x40228bc6a7ef9db2ull, 0x4001d11d73160605ull }, + { 0x40228c49ba5e3540ull, 0x4001d155fc573ec6ull }, + { 0x40228ccccccccccdull, 0x4001d18e8408f7c9ull }, + { 0x40228d4fdf3b645aull, 0x4001d1c70a2b471dull }, + { 0x40228dd2f1a9fbe8ull, 0x4001d1ff8ebe42ceull }, + { 0x40228e5604189375ull, 0x4001d23811c200e4ull }, + { 0x40228ed916872b02ull, 0x4001d27093369767ull }, + { 0x40228f5c28f5c28full, 0x4001d2a9131c1c5full }, + { 0x40228fdf3b645a1dull, 0x4001d2e19172a5ceull }, + { 0x402290624dd2f1aaull, 0x4001d31a0e3a49b7ull }, + { 0x402290e560418937ull, 0x4001d35289731e1bull }, + { 0x4022916872b020c5ull, 0x4001d38b031d38f9ull }, + { 0x402291eb851eb852ull, 0x4001d3c37b38b04cull }, + { 0x4022926e978d4fdfull, 0x4001d3fbf1c59a11ull }, + { 0x402292f1a9fbe76dull, 0x4001d43466c40c42ull }, + { 0x40229374bc6a7efaull, 0x4001d46cda341cd4ull }, + { 0x402293f7ced91687ull, 0x4001d4a54c15e1beull }, + { 0x4022947ae147ae15ull, 0x4001d4ddbc6970f4ull }, + { 0x402294fdf3b645a2ull, 0x4001d5162b2ee067ull }, + { 0x402295810624dd2full, 0x4001d54e98664609ull }, + { 0x40229604189374bdull, 0x4001d587040fb7c6ull }, + { 0x402296872b020c4aull, 0x4001d5bf6e2b4b8dull }, + { 0x4022970a3d70a3d7ull, 0x4001d5f7d6b91747ull }, + { 0x4022978d4fdf3b64ull, 0x4001d6303db930ddull }, + { 0x40229810624dd2f2ull, 0x4001d668a32bae38ull }, + { 0x4022989374bc6a7full, 0x4001d6a10710a53bull }, + { 0x40229916872b020cull, 0x4001d6d969682bcbull }, + { 0x402299999999999aull, 0x4001d711ca3257caull }, + { 0x40229a1cac083127ull, 0x4001d74a296f3f16ull }, + { 0x40229a9fbe76c8b4ull, 0x4001d782871ef78full }, + { 0x40229b22d0e56042ull, 0x4001d7bae3419711ull }, + { 0x40229ba5e353f7cfull, 0x4001d7f33dd73375ull }, + { 0x40229c28f5c28f5cull, 0x4001d82b96dfe295ull }, + { 0x40229cac083126eaull, 0x4001d863ee5bba48ull }, + { 0x40229d2f1a9fbe77ull, 0x4001d89c444ad063ull }, + { 0x40229db22d0e5604ull, 0x4001d8d498ad3ab8ull }, + { 0x40229e353f7ced92ull, 0x4001d90ceb830f1bull }, + { 0x40229eb851eb851full, 0x4001d9453ccc6359ull }, + { 0x40229f3b645a1cacull, 0x4001d97d8c894d41ull }, + { 0x40229fbe76c8b439ull, 0x4001d9b5dab9e2a0ull }, + { 0x4022a04189374bc7ull, 0x4001d9ee275e393full }, + { 0x4022a0c49ba5e354ull, 0x4001da26727666e7ull }, + { 0x4022a147ae147ae1ull, 0x4001da5ebc02815eull }, + { 0x4022a1cac083126full, 0x4001da9704029e6bull }, + { 0x4022a24dd2f1a9fcull, 0x4001dacf4a76d3ceull }, + { 0x4022a2d0e5604189ull, 0x4001db078f5f374bull }, + { 0x4022a353f7ced917ull, 0x4001db3fd2bbdea0ull }, + { 0x4022a3d70a3d70a4ull, 0x4001db78148cdf8cull }, + { 0x4022a45a1cac0831ull, 0x4001dbb054d24fc9ull }, + { 0x4022a4dd2f1a9fbfull, 0x4001dbe8938c4513ull }, + { 0x4022a5604189374cull, 0x4001dc20d0bad521ull }, + { 0x4022a5e353f7ced9ull, 0x4001dc590c5e15abull }, + { 0x4022a66666666667ull, 0x4001dc9146761c64ull }, + { 0x4022a6e978d4fdf4ull, 0x4001dcc97f02ff01ull }, + { 0x4022a76c8b439581ull, 0x4001dd01b604d331ull }, + { 0x4022a7ef9db22d0eull, 0x4001dd39eb7baea5ull }, + { 0x4022a872b020c49cull, 0x4001dd721f67a70bull }, + { 0x4022a8f5c28f5c29ull, 0x4001ddaa51c8d20dull }, + { 0x4022a978d4fdf3b6ull, 0x4001dde2829f4556ull }, + { 0x4022a9fbe76c8b44ull, 0x4001de1ab1eb168full }, + { 0x4022aa7ef9db22d1ull, 0x4001de52dfac5b5eull }, + { 0x4022ab020c49ba5eull, 0x4001de8b0be32967ull }, + { 0x4022ab851eb851ecull, 0x4001dec3368f964eull }, + { 0x4022ac083126e979ull, 0x4001defb5fb1b7b3ull }, + { 0x4022ac8b43958106ull, 0x4001df338749a337ull }, + { 0x4022ad0e56041894ull, 0x4001df6bad576e76ull }, + { 0x4022ad916872b021ull, 0x4001dfa3d1db2f0cull }, + { 0x4022ae147ae147aeull, 0x4001dfdbf4d4fa94ull }, + { 0x4022ae978d4fdf3cull, 0x4001e0141644e6a7ull }, + { 0x4022af1a9fbe76c9ull, 0x4001e04c362b08d9ull }, + { 0x4022af9db22d0e56ull, 0x4001e084548776bfull }, + { 0x4022b020c49ba5e3ull, 0x4001e0bc715a45eeull }, + { 0x4022b0a3d70a3d71ull, 0x4001e0f48ca38bf7ull }, + { 0x4022b126e978d4feull, 0x4001e12ca6635e67ull }, + { 0x4022b1a9fbe76c8bull, 0x4001e164be99d2ceull }, + { 0x4022b22d0e560419ull, 0x4001e19cd546feb7ull }, + { 0x4022b2b020c49ba6ull, 0x4001e1d4ea6af7abull }, + { 0x4022b33333333333ull, 0x4001e20cfe05d334ull }, + { 0x4022b3b645a1cac1ull, 0x4001e2451017a6d8ull }, + { 0x4022b4395810624eull, 0x4001e27d20a0881cull }, + { 0x4022b4bc6a7ef9dbull, 0x4001e2b52fa08c82ull }, + { 0x4022b53f7ced9169ull, 0x4001e2ed3d17c98dull }, + { 0x4022b5c28f5c28f6ull, 0x4001e325490654baull }, + { 0x4022b645a1cac083ull, 0x4001e35d536c4389ull }, + { 0x4022b6c8b4395810ull, 0x4001e3955c49ab75ull }, + { 0x4022b74bc6a7ef9eull, 0x4001e3cd639ea1f8ull }, + { 0x4022b7ced916872bull, 0x4001e405696b3c8bull }, + { 0x4022b851eb851eb8ull, 0x4001e43d6daf90a4ull }, + { 0x4022b8d4fdf3b646ull, 0x4001e475706bb3b9ull }, + { 0x4022b95810624dd3ull, 0x4001e4ad719fbb3bull }, + { 0x4022b9db22d0e560ull, 0x4001e4e5714bbc9eull }, + { 0x4022ba5e353f7ceeull, 0x4001e51d6f6fcd50ull }, + { 0x4022bae147ae147bull, 0x4001e5556c0c02bfull }, + { 0x4022bb645a1cac08ull, 0x4001e58d67207258ull }, + { 0x4022bbe76c8b4396ull, 0x4001e5c560ad3185ull }, + { 0x4022bc6a7ef9db23ull, 0x4001e5fd58b255adull }, + { 0x4022bced916872b0ull, 0x4001e6354f2ff439ull }, + { 0x4022bd70a3d70a3eull, 0x4001e66d4426228eull }, + { 0x4022bdf3b645a1cbull, 0x4001e6a53794f60dull }, + { 0x4022be76c8b43958ull, 0x4001e6dd297c841aull }, + { 0x4022bef9db22d0e5ull, 0x4001e71519dce214ull }, + { 0x4022bf7ced916873ull, 0x4001e74d08b62559ull }, + { 0x4022c00000000000ull, 0x4001e784f6086345ull }, + { 0x4022c083126e978dull, 0x4001e7bce1d3b134ull }, + { 0x4022c10624dd2f1bull, 0x4001e7f4cc18247dull }, + { 0x4022c189374bc6a8ull, 0x4001e82cb4d5d278ull }, + { 0x4022c20c49ba5e35ull, 0x4001e8649c0cd07aull }, + { 0x4022c28f5c28f5c3ull, 0x4001e89c81bd33d7ull }, + { 0x4022c3126e978d50ull, 0x4001e8d465e711e1ull }, + { 0x4022c395810624ddull, 0x4001e90c488a7fe7ull }, + { 0x4022c4189374bc6bull, 0x4001e94429a79339ull }, + { 0x4022c49ba5e353f8ull, 0x4001e97c093e6122ull }, + { 0x4022c51eb851eb85ull, 0x4001e9b3e74efeeeull }, + { 0x4022c5a1cac08313ull, 0x4001e9ebc3d981e5ull }, + { 0x4022c624dd2f1aa0ull, 0x4001ea239eddff50ull }, + { 0x4022c6a7ef9db22dull, 0x4001ea5b785c8c73ull }, + { 0x4022c72b020c49baull, 0x4001ea9350553e92ull }, + { 0x4022c7ae147ae148ull, 0x4001eacb26c82af1ull }, + { 0x4022c83126e978d5ull, 0x4001eb02fbb566cfull }, + { 0x4022c8b439581062ull, 0x4001eb3acf1d076aull }, + { 0x4022c9374bc6a7f0ull, 0x4001eb72a0ff2200ull }, + { 0x4022c9ba5e353f7dull, 0x4001ebaa715bcbccull }, + { 0x4022ca3d70a3d70aull, 0x4001ebe240331a08ull }, + { 0x4022cac083126e98ull, 0x4001ec1a0d8521ebull }, + { 0x4022cb4395810625ull, 0x4001ec51d951f8abull }, + { 0x4022cbc6a7ef9db2ull, 0x4001ec89a399b37dull }, + { 0x4022cc49ba5e3540ull, 0x4001ecc16c5c6792ull }, + { 0x4022cccccccccccdull, 0x4001ecf9339a2a1dull }, + { 0x4022cd4fdf3b645aull, 0x4001ed30f953104bull }, + { 0x4022cdd2f1a9fbe8ull, 0x4001ed68bd872f4bull }, + { 0x4022ce5604189375ull, 0x4001eda080369c47ull }, + { 0x4022ced916872b02ull, 0x4001edd841616c6aull }, + { 0x4022cf5c28f5c28full, 0x4001ee100107b4dcull }, + { 0x4022cfdf3b645a1dull, 0x4001ee47bf298ac3ull }, + { 0x4022d0624dd2f1aaull, 0x4001ee7f7bc70345ull }, + { 0x4022d0e560418937ull, 0x4001eeb736e03383ull }, + { 0x4022d16872b020c5ull, 0x4001eeeef07530a0ull }, + { 0x4022d1eb851eb852ull, 0x4001ef26a8860fbaull }, + { 0x4022d26e978d4fdfull, 0x4001ef5e5f12e5f0ull }, + { 0x4022d2f1a9fbe76dull, 0x4001ef96141bc85eull }, + { 0x4022d374bc6a7efaull, 0x4001efcdc7a0cc1dull }, + { 0x4022d3f7ced91687ull, 0x4001f00579a20647ull }, + { 0x4022d47ae147ae15ull, 0x4001f03d2a1f8bf3ull }, + { 0x4022d4fdf3b645a2ull, 0x4001f074d9197236ull }, + { 0x4022d5810624dd2full, 0x4001f0ac868fce22ull }, + { 0x4022d604189374bdull, 0x4001f0e43282b4cbull }, + { 0x4022d6872b020c4aull, 0x4001f11bdcf23b40ull }, + { 0x4022d70a3d70a3d7ull, 0x4001f15385de768full }, + { 0x4022d78d4fdf3b64ull, 0x4001f18b2d477bc5ull }, + { 0x4022d810624dd2f2ull, 0x4001f1c2d32d5fedull }, + { 0x4022d89374bc6a7full, 0x4001f1fa77903810ull }, + { 0x4022d916872b020cull, 0x4001f2321a701935ull }, + { 0x4022d9999999999aull, 0x4001f269bbcd1863ull }, + { 0x4022da1cac083127ull, 0x4001f2a15ba74a9cull }, + { 0x4022da9fbe76c8b4ull, 0x4001f2d8f9fec4e5ull }, + { 0x4022db22d0e56042ull, 0x4001f31096d39c3cull }, + { 0x4022dba5e353f7cfull, 0x4001f3483225e5a2ull }, + { 0x4022dc28f5c28f5cull, 0x4001f37fcbf5b613ull }, + { 0x4022dcac083126eaull, 0x4001f3b76443228aull }, + { 0x4022dd2f1a9fbe77ull, 0x4001f3eefb0e4002ull }, + { 0x4022ddb22d0e5604ull, 0x4001f42690572373ull }, + { 0x4022de353f7ced92ull, 0x4001f45e241de1d3ull }, + { 0x4022deb851eb851full, 0x4001f495b6629016ull }, + { 0x4022df3b645a1cacull, 0x4001f4cd4725432full }, + { 0x4022dfbe76c8b439ull, 0x4001f504d6661010ull }, + { 0x4022e04189374bc7ull, 0x4001f53c64250ba9ull }, + { 0x4022e0c49ba5e354ull, 0x4001f573f0624ae7ull }, + { 0x4022e147ae147ae1ull, 0x4001f5ab7b1de2b5ull }, + { 0x4022e1cac083126full, 0x4001f5e30457e800ull }, + { 0x4022e24dd2f1a9fcull, 0x4001f61a8c106fafull }, + { 0x4022e2d0e5604189ull, 0x4001f65212478ea9ull }, + { 0x4022e353f7ced917ull, 0x4001f68996fd59d5ull }, + { 0x4022e3d70a3d70a4ull, 0x4001f6c11a31e614ull }, + { 0x4022e45a1cac0831ull, 0x4001f6f89be5484aull }, + { 0x4022e4dd2f1a9fbfull, 0x4001f7301c179556ull }, + { 0x4022e5604189374cull, 0x4001f7679ac8e217ull }, + { 0x4022e5e353f7ced9ull, 0x4001f79f17f9436aull }, + { 0x4022e66666666667ull, 0x4001f7d693a8ce2aull }, + { 0x4022e6e978d4fdf4ull, 0x4001f80e0dd79731ull }, + { 0x4022e76c8b439581ull, 0x4001f8458685b355ull }, + { 0x4022e7ef9db22d0eull, 0x4001f87cfdb3376eull }, + { 0x4022e872b020c49cull, 0x4001f8b473603850ull }, + { 0x4022e8f5c28f5c29ull, 0x4001f8ebe78ccacdull }, + { 0x4022e978d4fdf3b6ull, 0x4001f9235a3903b6ull }, + { 0x4022e9fbe76c8b44ull, 0x4001f95acb64f7dbull }, + { 0x4022ea7ef9db22d1ull, 0x4001f9923b10bc09ull }, + { 0x4022eb020c49ba5eull, 0x4001f9c9a93c650bull }, + { 0x4022eb851eb851ecull, 0x4001fa0115e807aeull }, + { 0x4022ec083126e979ull, 0x4001fa388113b8b8ull }, + { 0x4022ec8b43958106ull, 0x4001fa6feabf8cf0ull }, + { 0x4022ed0e56041894ull, 0x4001faa752eb991dull }, + { 0x4022ed916872b021ull, 0x4001fadeb997f201ull }, + { 0x4022ee147ae147aeull, 0x4001fb161ec4ac5dull }, + { 0x4022ee978d4fdf3cull, 0x4001fb4d8271dcf4ull }, + { 0x4022ef1a9fbe76c9ull, 0x4001fb84e49f9881ull }, + { 0x4022ef9db22d0e56ull, 0x4001fbbc454df3c3ull }, + { 0x4022f020c49ba5e3ull, 0x4001fbf3a47d0375ull }, + { 0x4022f0a3d70a3d71ull, 0x4001fc2b022cdc4full }, + { 0x4022f126e978d4feull, 0x4001fc625e5d930aull }, + { 0x4022f1a9fbe76c8bull, 0x4001fc99b90f3c5bull }, + { 0x4022f22d0e560419ull, 0x4001fcd11241ecf7ull }, + { 0x4022f2b020c49ba6ull, 0x4001fd0869f5b991ull }, + { 0x4022f33333333333ull, 0x4001fd3fc02ab6d9ull }, + { 0x4022f3b645a1cac1ull, 0x4001fd7714e0f97full }, + { 0x4022f4395810624eull, 0x4001fdae68189630ull }, + { 0x4022f4bc6a7ef9dbull, 0x4001fde5b9d1a199ull }, + { 0x4022f53f7ced9169ull, 0x4001fe1d0a0c3064ull }, + { 0x4022f5c28f5c28f6ull, 0x4001fe5458c85738ull }, + { 0x4022f645a1cac083ull, 0x4001fe8ba6062abeull }, + { 0x4022f6c8b4395810ull, 0x4001fec2f1c5bf9aull }, + { 0x4022f74bc6a7ef9eull, 0x4001fefa3c072a70ull }, + { 0x4022f7ced916872bull, 0x4001ff3184ca7fe2ull }, + { 0x4022f851eb851eb8ull, 0x4001ff68cc0fd490ull }, + { 0x4022f8d4fdf3b646ull, 0x4001ffa011d73d1aull }, + { 0x4022f95810624dd3ull, 0x4001ffd75620ce1aull }, + { 0x4022f9db22d0e560ull, 0x4002000e98ec9c2eull }, + { 0x4022fa5e353f7ceeull, 0x40020045da3abbeeull }, + { 0x4022fae147ae147bull, 0x4002007d1a0b41f2ull }, + { 0x4022fb645a1cac08ull, 0x400200b4585e42d1ull }, + { 0x4022fbe76c8b4396ull, 0x400200eb9533d320ull }, + { 0x4022fc6a7ef9db23ull, 0x40020122d08c0770ull }, + { 0x4022fced916872b0ull, 0x4002015a0a66f453ull }, + { 0x4022fd70a3d70a3eull, 0x4002019142c4ae5aull }, + { 0x4022fdf3b645a1cbull, 0x400201c879a54a11ull }, + { 0x4022fe76c8b43958ull, 0x400201ffaf08dc05ull }, + { 0x4022fef9db22d0e5ull, 0x40020236e2ef78c0ull }, + { 0x4022ff7ced916873ull, 0x4002026e155934cdull }, + { 0x4023000000000000ull, 0x400202a5464624b1ull }, + { 0x40230083126e978dull, 0x400202dc75b65cf2ull }, + { 0x4023010624dd2f1bull, 0x40020313a3a9f215ull }, + { 0x40230189374bc6a8ull, 0x4002034ad020f89bull }, + { 0x4023020c49ba5e35ull, 0x40020381fb1b8506ull }, + { 0x4023028f5c28f5c3ull, 0x400203b92499abd4ull }, + { 0x402303126e978d50ull, 0x400203f04c9b8183ull }, + { 0x40230395810624ddull, 0x4002042773211a8dull }, + { 0x402304189374bc6bull, 0x4002045e982a8b6full }, + { 0x4023049ba5e353f8ull, 0x40020495bbb7e89eull }, + { 0x4023051eb851eb85ull, 0x400204ccddc94693ull }, + { 0x402305a1cac08313ull, 0x40020503fe5eb9c2ull }, + { 0x40230624dd2f1aa0ull, 0x4002053b1d78569full }, + { 0x402306a7ef9db22dull, 0x400205723b16319cull }, + { 0x4023072b020c49baull, 0x400205a957385f28ull }, + { 0x402307ae147ae148ull, 0x400205e071def3b2ull }, + { 0x4023083126e978d5ull, 0x400206178b0a03a7ull }, + { 0x402308b439581062ull, 0x4002064ea2b9a372ull }, + { 0x402309374bc6a7f0ull, 0x40020685b8ede77dull }, + { 0x402309ba5e353f7dull, 0x400206bccda6e42eull }, + { 0x40230a3d70a3d70aull, 0x400206f3e0e4adeeull }, + { 0x40230ac083126e98ull, 0x4002072af2a75920ull }, + { 0x40230b4395810625ull, 0x4002076202eefa26ull }, + { 0x40230bc6a7ef9db2ull, 0x4002079911bba563ull }, + { 0x40230c49ba5e3540ull, 0x400207d01f0d6f36ull }, + { 0x40230ccccccccccdull, 0x400208072ae46bfcull }, + { 0x40230d4fdf3b645aull, 0x4002083e3540b013ull }, + { 0x40230dd2f1a9fbe8ull, 0x400208753e224fd6ull }, + { 0x40230e5604189375ull, 0x400208ac45895f9cull }, + { 0x40230ed916872b02ull, 0x400208e34b75f3bdull }, + { 0x40230f5c28f5c28full, 0x4002091a4fe82090ull }, + { 0x40230fdf3b645a1dull, 0x4002095152dffa68ull }, + { 0x402310624dd2f1aaull, 0x40020988545d9598ull }, + { 0x402310e560418937ull, 0x400209bf54610671ull }, + { 0x4023116872b020c5ull, 0x400209f652ea6142ull }, + { 0x402311eb851eb852ull, 0x40020a2d4ff9ba58ull }, + { 0x4023126e978d4fdfull, 0x40020a644b8f2600ull }, + { 0x402312f1a9fbe76dull, 0x40020a9b45aab884ull }, + { 0x40231374bc6a7efaull, 0x40020ad23e4c862dull }, + { 0x402313f7ced91687ull, 0x40020b093574a341ull }, + { 0x4023147ae147ae15ull, 0x40020b402b232406ull }, + { 0x402314fdf3b645a2ull, 0x40020b771f581cc0ull }, + { 0x402315810624dd2full, 0x40020bae1213a1b1ull }, + { 0x40231604189374bdull, 0x40020be50355c71aull }, + { 0x402316872b020c4aull, 0x40020c1bf31ea13aull }, + { 0x4023170a3d70a3d7ull, 0x40020c52e16e444cull }, + { 0x4023178d4fdf3b64ull, 0x40020c89ce44c48full }, + { 0x40231810624dd2f2ull, 0x40020cc0b9a2363bull }, + { 0x4023189374bc6a7full, 0x40020cf7a386ad88ull }, + { 0x40231916872b020cull, 0x40020d2e8bf23eafull }, + { 0x402319999999999aull, 0x40020d6572e4fde3ull }, + { 0x40231a1cac083127ull, 0x40020d9c585eff58ull }, + { 0x40231a9fbe76c8b4ull, 0x40020dd33c605740ull }, + { 0x40231b22d0e56042ull, 0x40020e0a1ee919ccull }, + { 0x40231ba5e353f7cfull, 0x40020e40fff95b2aull }, + { 0x40231c28f5c28f5cull, 0x40020e77df912f87ull }, + { 0x40231cac083126eaull, 0x40020eaebdb0ab0eull }, + { 0x40231d2f1a9fbe77ull, 0x40020ee59a57e1eaull }, + { 0x40231db22d0e5604ull, 0x40020f1c7586e842ull }, + { 0x40231e353f7ced92ull, 0x40020f534f3dd23dull }, + { 0x40231eb851eb851full, 0x40020f8a277cb400ull }, + { 0x40231f3b645a1cacull, 0x40020fc0fe43a1aeull }, + { 0x40231fbe76c8b439ull, 0x40020ff7d392af69ull }, + { 0x4023204189374bc7ull, 0x4002102ea769f151ull }, + { 0x402320c49ba5e354ull, 0x4002106579c97b84ull }, + { 0x40232147ae147ae1ull, 0x4002109c4ab1621full }, + { 0x402321cac083126full, 0x400210d31a21b93full }, + { 0x4023224dd2f1a9fcull, 0x40021109e81a94faull }, + { 0x402322d0e5604189ull, 0x40021140b49c096bull }, + { 0x40232353f7ced917ull, 0x400211777fa62aa8ull }, + { 0x402323d70a3d70a4ull, 0x400211ae49390cc5ull }, + { 0x4023245a1cac0831ull, 0x400211e51154c3d5ull }, + { 0x402324dd2f1a9fbfull, 0x4002121bd7f963eaull }, + { 0x402325604189374cull, 0x400212529d270114ull }, + { 0x402325e353f7ced9ull, 0x4002128960ddaf61ull }, + { 0x4023266666666667ull, 0x400212c0231d82deull }, + { 0x402326e978d4fdf4ull, 0x400212f6e3e68f96ull }, + { 0x4023276c8b439581ull, 0x4002132da338e992ull }, + { 0x402327ef9db22d0eull, 0x400213646114a4dbull }, + { 0x40232872b020c49cull, 0x4002139b1d79d577ull }, + { 0x402328f5c28f5c29ull, 0x400213d1d8688f69ull }, + { 0x40232978d4fdf3b6ull, 0x4002140891e0e6b7ull }, + { 0x402329fbe76c8b44ull, 0x4002143f49e2ef61ull }, + { 0x40232a7ef9db22d1ull, 0x40021476006ebd66ull }, + { 0x40232b020c49ba5eull, 0x400214acb58464c5ull }, + { 0x40232b851eb851ecull, 0x400214e36923f97bull }, + { 0x40232c083126e979ull, 0x4002151a1b4d8f83ull }, + { 0x40232c8b43958106ull, 0x40021550cc013ad6ull }, + { 0x40232d0e56041894ull, 0x400215877b3f0f6dull }, + { 0x40232d916872b021ull, 0x400215be2907213cull }, + { 0x40232e147ae147aeull, 0x400215f4d559843aull }, + { 0x40232e978d4fdf3cull, 0x4002162b80364c5aull }, + { 0x40232f1a9fbe76c9ull, 0x40021662299d8d8bull }, + { 0x40232f9db22d0e56ull, 0x40021698d18f5bbfull }, + { 0x40233020c49ba5e3ull, 0x400216cf780bcae4ull }, + { 0x402330a3d70a3d71ull, 0x400217061d12eee7ull }, + { 0x40233126e978d4feull, 0x4002173cc0a4dbb2ull }, + { 0x402331a9fbe76c8bull, 0x4002177362c1a52full }, + { 0x4023322d0e560419ull, 0x400217aa03695f47ull }, + { 0x402332b020c49ba6ull, 0x400217e0a29c1ddeull }, + { 0x4023333333333333ull, 0x400218174059f4dbull }, + { 0x402333b645a1cac1ull, 0x4002184ddca2f820ull }, + { 0x402334395810624eull, 0x4002188477773b8eull }, + { 0x402334bc6a7ef9dbull, 0x400218bb10d6d306ull }, + { 0x4023353f7ced9169ull, 0x400218f1a8c1d266ull }, + { 0x402335c28f5c28f6ull, 0x400219283f384d8bull }, + { 0x40233645a1cac083ull, 0x4002195ed43a584full }, + { 0x402336c8b4395810ull, 0x4002199567c8068dull }, + { 0x4023374bc6a7ef9eull, 0x400219cbf9e16c1dull }, + { 0x402337ced916872bull, 0x40021a028a869cd4ull }, + { 0x40233851eb851eb8ull, 0x40021a3919b7ac89ull }, + { 0x402338d4fdf3b646ull, 0x40021a6fa774af0dull }, + { 0x4023395810624dd3ull, 0x40021aa633bdb833ull }, + { 0x402339db22d0e560ull, 0x40021adcbe92dbcbull }, + { 0x40233a5e353f7ceeull, 0x40021b1347f42da4ull }, + { 0x40233ae147ae147bull, 0x40021b49cfe1c189ull }, + { 0x40233b645a1cac08ull, 0x40021b80565bab47ull }, + { 0x40233be76c8b4396ull, 0x40021bb6db61fea7ull }, + { 0x40233c6a7ef9db23ull, 0x40021bed5ef4cf72ull }, + { 0x40233ced916872b0ull, 0x40021c23e114316dull }, + { 0x40233d70a3d70a3eull, 0x40021c5a61c0385full }, + { 0x40233df3b645a1cbull, 0x40021c90e0f8f80aull }, + { 0x40233e76c8b43958ull, 0x40021cc75ebe8431ull }, + { 0x40233ef9db22d0e5ull, 0x40021cfddb10f093ull }, + { 0x40233f7ced916873ull, 0x40021d3455f050f0ull }, + { 0x4023400000000000ull, 0x40021d6acf5cb904ull }, + { 0x40234083126e978dull, 0x40021da147563c8bull }, + { 0x4023410624dd2f1bull, 0x40021dd7bddcef40ull }, + { 0x40234189374bc6a8ull, 0x40021e0e32f0e4d9ull }, + { 0x4023420c49ba5e35ull, 0x40021e44a692310full }, + { 0x4023428f5c28f5c3ull, 0x40021e7b18c0e797ull }, + { 0x402343126e978d50ull, 0x40021eb1897d1c23ull }, + { 0x40234395810624ddull, 0x40021ee7f8c6e267ull }, + { 0x402344189374bc6bull, 0x40021f1e669e4e13ull }, + { 0x4023449ba5e353f8ull, 0x40021f54d30372d6ull }, + { 0x4023451eb851eb85ull, 0x40021f8b3df6645dull }, + { 0x402345a1cac08313ull, 0x40021fc1a7773655ull }, + { 0x40234624dd2f1aa0ull, 0x40021ff80f85fc66ull }, + { 0x402346a7ef9db22dull, 0x4002202e7622ca3bull }, + { 0x4023472b020c49baull, 0x40022064db4db37aull }, + { 0x402347ae147ae148ull, 0x4002209b3f06cbc9ull }, + { 0x4023483126e978d5ull, 0x400220d1a14e26ccull }, + { 0x402348b439581062ull, 0x400221080223d825ull }, + { 0x402349374bc6a7f0ull, 0x4002213e6187f375ull }, + { 0x402349ba5e353f7dull, 0x40022174bf7a8c5cull }, + { 0x40234a3d70a3d70aull, 0x400221ab1bfbb677ull }, + { 0x40234ac083126e98ull, 0x400221e1770b8563ull }, + { 0x40234b4395810625ull, 0x40022217d0aa0cb9ull }, + { 0x40234bc6a7ef9db2ull, 0x4002224e28d76014ull }, + { 0x40234c49ba5e3540ull, 0x400222847f93930bull }, + { 0x40234ccccccccccdull, 0x400222bad4deb933ull }, + { 0x40234d4fdf3b645aull, 0x400222f128b8e621ull }, + { 0x40234dd2f1a9fbe8ull, 0x400223277b222d68ull }, + { 0x40234e5604189375ull, 0x4002235dcc1aa299ull }, + { 0x40234ed916872b02ull, 0x400223941ba25944ull }, + { 0x40234f5c28f5c28full, 0x400223ca69b964f7ull }, + { 0x40234fdf3b645a1dull, 0x40022400b65fd93full }, + { 0x402350624dd2f1aaull, 0x400224370195c9a6ull }, + { 0x402350e560418937ull, 0x4002246d4b5b49b7ull }, + { 0x4023516872b020c5ull, 0x400224a393b06cf9ull }, + { 0x402351eb851eb852ull, 0x400224d9da9546f2ull }, + { 0x4023526e978d4fdfull, 0x400225102009eb27ull }, + { 0x402352f1a9fbe76dull, 0x40022546640e6d1dull }, + { 0x40235374bc6a7efaull, 0x4002257ca6a2e054ull }, + { 0x402353f7ced91687ull, 0x400225b2e7c7584cull }, + { 0x4023547ae147ae15ull, 0x400225e9277be885ull }, + { 0x402354fdf3b645a2ull, 0x4002261f65c0a47bull }, + { 0x402355810624dd2full, 0x40022655a2959faaull }, + { 0x40235604189374bdull, 0x4002268bddfaed8dull }, + { 0x402356872b020c4aull, 0x400226c217f0a19aull }, + { 0x4023570a3d70a3d7ull, 0x400226f85076cf49ull }, + { 0x4023578d4fdf3b64ull, 0x4002272e878d8a10ull }, + { 0x40235810624dd2f2ull, 0x40022764bd34e563ull }, + { 0x4023589374bc6a7full, 0x4002279af16cf4b3ull }, + { 0x40235916872b020cull, 0x400227d12435cb72ull }, + { 0x402359999999999aull, 0x40022807558f7d0full }, + { 0x40235a1cac083127ull, 0x4002283d857a1cf6ull }, + { 0x40235a9fbe76c8b4ull, 0x40022873b3f5be95ull }, + { 0x40235b22d0e56042ull, 0x400228a9e1027556ull }, + { 0x40235ba5e353f7cfull, 0x400228e00ca054a2ull }, + { 0x40235c28f5c28f5cull, 0x4002291636cf6fe0ull }, + { 0x40235cac083126eaull, 0x4002294c5f8fda76ull }, + { 0x40235d2f1a9fbe77ull, 0x4002298286e1a7caull }, + { 0x40235db22d0e5604ull, 0x400229b8acc4eb3cull }, + { 0x40235e353f7ced92ull, 0x400229eed139b830ull }, + { 0x40235eb851eb851full, 0x40022a24f4402204ull }, + { 0x40235f3b645a1cacull, 0x40022a5b15d83c17ull }, + { 0x40235fbe76c8b439ull, 0x40022a91360219c6ull }, + { 0x4023604189374bc7ull, 0x40022ac754bdce6cull }, + { 0x402360c49ba5e354ull, 0x40022afd720b6d62ull }, + { 0x40236147ae147ae1ull, 0x40022b338deb0a00ull }, + { 0x402361cac083126full, 0x40022b69a85cb79eull }, + { 0x4023624dd2f1a9fcull, 0x40022b9fc1608990ull }, + { 0x402362d0e5604189ull, 0x40022bd5d8f6932aull }, + { 0x40236353f7ced917ull, 0x40022c0bef1ee7beull }, + { 0x402363d70a3d70a4ull, 0x40022c4203d99a9bull }, + { 0x4023645a1cac0831ull, 0x40022c781726bf11ull }, + { 0x402364dd2f1a9fbfull, 0x40022cae2906686eull }, + { 0x402365604189374cull, 0x40022ce43978a9fdull }, + { 0x402365e353f7ced9ull, 0x40022d1a487d9709ull }, + { 0x4023666666666667ull, 0x40022d50561542dbull }, + { 0x402366e978d4fdf4ull, 0x40022d86623fc0b9ull }, + { 0x4023676c8b439581ull, 0x40022dbc6cfd23e9ull }, + { 0x402367ef9db22d0eull, 0x40022df2764d7fb0ull }, + { 0x40236872b020c49cull, 0x40022e287e30e751ull }, + { 0x402368f5c28f5c29ull, 0x40022e5e84a76e0cull }, + { 0x40236978d4fdf3b6ull, 0x40022e9489b12721ull }, + { 0x402369fbe76c8b44ull, 0x40022eca8d4e25cfull }, + { 0x40236a7ef9db22d1ull, 0x40022f008f7e7d51ull }, + { 0x40236b020c49ba5eull, 0x40022f36904240e4ull }, + { 0x40236b851eb851ecull, 0x40022f6c8f9983c0ull }, + { 0x40236c083126e979ull, 0x40022fa28d84591eull }, + { 0x40236c8b43958106ull, 0x40022fd88a02d434ull }, + { 0x40236d0e56041894ull, 0x4002300e85150838ull }, + { 0x40236d916872b021ull, 0x400230447ebb085cull }, + { 0x40236e147ae147aeull, 0x4002307a76f4e7d3ull }, + { 0x40236e978d4fdf3cull, 0x400230b06dc2b9cdull }, + { 0x40236f1a9fbe76c9ull, 0x400230e663249179ull }, + { 0x40236f9db22d0e56ull, 0x4002311c571a8204ull }, + { 0x40237020c49ba5e3ull, 0x4002315249a49e9aull }, + { 0x402370a3d70a3d71ull, 0x400231883ac2fa67ull }, + { 0x40237126e978d4feull, 0x400231be2a75a892ull }, + { 0x402371a9fbe76c8bull, 0x400231f418bcbc43ull }, + { 0x4023722d0e560419ull, 0x4002322a059848a1ull }, + { 0x402372b020c49ba6ull, 0x4002325ff10860ceull }, + { 0x4023733333333333ull, 0x40023295db0d17eeull }, + { 0x402373b645a1cac1ull, 0x400232cbc3a68123ull }, + { 0x402374395810624eull, 0x40023301aad4af8bull }, + { 0x402374bc6a7ef9dbull, 0x400233379097b646ull }, + { 0x4023753f7ced9169ull, 0x4002336d74efa871ull }, + { 0x402375c28f5c28f6ull, 0x400233a357dc9925ull }, + { 0x40237645a1cac083ull, 0x400233d9395e9b7eull }, + { 0x402376c8b4395810ull, 0x4002340f1975c294ull }, + { 0x4023774bc6a7ef9eull, 0x40023444f822217dull }, + { 0x402377ced916872bull, 0x4002347ad563cb4eull }, + { 0x40237851eb851eb8ull, 0x400234b0b13ad31bull }, + { 0x402378d4fdf3b646ull, 0x400234e68ba74bf8ull }, + { 0x4023795810624dd3ull, 0x4002351c64a948f3ull }, + { 0x402379db22d0e560ull, 0x400235523c40dd1dull }, + { 0x40237a5e353f7ceeull, 0x40023588126e1b83ull }, + { 0x40237ae147ae147bull, 0x400235bde7311731ull }, + { 0x40237b645a1cac08ull, 0x400235f3ba89e332ull }, + { 0x40237be76c8b4396ull, 0x400236298c789291ull }, + { 0x40237c6a7ef9db23ull, 0x4002365f5cfd3852ull }, + { 0x40237ced916872b0ull, 0x400236952c17e77eull }, + { 0x40237d70a3d70a3eull, 0x400236caf9c8b31aull }, + { 0x40237df3b645a1cbull, 0x40023700c60fae27ull }, + { 0x40237e76c8b43958ull, 0x4002373690eceba8ull }, + { 0x40237ef9db22d0e5ull, 0x4002376c5a607e9eull }, + { 0x40237f7ced916873ull, 0x400237a2226a7a06ull }, + { 0x4023800000000000ull, 0x400237d7e90af0dfull }, + { 0x40238083126e978dull, 0x4002380dae41f623ull }, + { 0x4023810624dd2f1bull, 0x40023843720f9cceull }, + { 0x40238189374bc6a8ull, 0x400238793473f7d7ull }, + { 0x4023820c49ba5e35ull, 0x400238aef56f1a36ull }, + { 0x4023828f5c28f5c3ull, 0x400238e4b50116e2ull }, + { 0x402383126e978d50ull, 0x4002391a732a00cdull }, + { 0x40238395810624ddull, 0x400239502fe9eaecull }, + { 0x402384189374bc6bull, 0x40023985eb40e82full }, + { 0x4023849ba5e353f8ull, 0x400239bba52f0b85ull }, + { 0x4023851eb851eb85ull, 0x400239f15db467deull }, + { 0x402385a1cac08313ull, 0x40023a2714d11026ull }, + { 0x40238624dd2f1aa0ull, 0x40023a5cca851749ull }, + { 0x402386a7ef9db22dull, 0x40023a927ed0902full }, + { 0x4023872b020c49baull, 0x40023ac831b38dc2ull }, + { 0x402387ae147ae148ull, 0x40023afde32e22eaull }, + { 0x4023883126e978d5ull, 0x40023b3393406289ull }, + { 0x402388b439581062ull, 0x40023b6941ea5f85ull }, + { 0x402389374bc6a7f0ull, 0x40023b9eef2c2cc1ull }, + { 0x402389ba5e353f7dull, 0x40023bd49b05dd1dull }, + { 0x40238a3d70a3d70aull, 0x40023c0a45778378ull }, + { 0x40238ac083126e98ull, 0x40023c3fee8132b1ull }, + { 0x40238b4395810625ull, 0x40023c759622fda3ull }, + { 0x40238bc6a7ef9db2ull, 0x40023cab3c5cf72aull }, + { 0x40238c49ba5e3540ull, 0x40023ce0e12f3221ull }, + { 0x40238ccccccccccdull, 0x40023d168499c15dull }, + { 0x40238d4fdf3b645aull, 0x40023d4c269cb7b6ull }, + { 0x40238dd2f1a9fbe8ull, 0x40023d81c7382802ull }, + { 0x40238e5604189375ull, 0x40023db7666c2512ull }, + { 0x40238ed916872b02ull, 0x40023ded0438c1bbull }, + { 0x40238f5c28f5c28full, 0x40023e22a09e10ccull }, + { 0x40238fdf3b645a1dull, 0x40023e583b9c2515ull }, + { 0x402390624dd2f1aaull, 0x40023e8dd5331162ull }, + { 0x402390e560418937ull, 0x40023ec36d62e881ull }, + { 0x4023916872b020c5ull, 0x40023ef9042bbd3dull }, + { 0x402391eb851eb852ull, 0x40023f2e998da25dull }, + { 0x4023926e978d4fdfull, 0x40023f642d88aaabull }, + { 0x402392f1a9fbe76dull, 0x40023f99c01ce8ecull }, + { 0x40239374bc6a7efaull, 0x40023fcf514a6fe6ull }, + { 0x402393f7ced91687ull, 0x40024004e111525bull }, + { 0x4023947ae147ae15ull, 0x4002403a6f71a30dull }, + { 0x402394fdf3b645a2ull, 0x4002406ffc6b74beull }, + { 0x402395810624dd2full, 0x400240a587feda2bull }, + { 0x40239604189374bdull, 0x400240db122be612ull }, + { 0x402396872b020c4aull, 0x400241109af2ab30ull }, + { 0x4023970a3d70a3d7ull, 0x4002414622533c3full }, + { 0x4023978d4fdf3b64ull, 0x4002417ba84dabf7ull }, + { 0x40239810624dd2f2ull, 0x400241b12ce20d11ull }, + { 0x4023989374bc6a7full, 0x400241e6b0107243ull }, + { 0x40239916872b020cull, 0x4002421c31d8ee40ull }, + { 0x402399999999999aull, 0x40024251b23b93beull }, + { 0x40239a1cac083127ull, 0x400242873138756cull }, + { 0x40239a9fbe76c8b4ull, 0x400242bcaecfa5fcull }, + { 0x40239b22d0e56042ull, 0x400242f22b01381cull }, + { 0x40239ba5e353f7cfull, 0x40024327a5cd3e7aull }, + { 0x40239c28f5c28f5cull, 0x4002435d1f33cbc1ull }, + { 0x40239cac083126eaull, 0x400243929734f29cull }, + { 0x40239d2f1a9fbe77ull, 0x400243c80dd0c5b4ull }, + { 0x40239db22d0e5604ull, 0x400243fd830757b0ull }, + { 0x40239e353f7ced92ull, 0x40024432f6d8bb37ull }, + { 0x40239eb851eb851full, 0x40024468694502ecull }, + { 0x40239f3b645a1cacull, 0x4002449dda4c4174ull }, + { 0x40239fbe76c8b439ull, 0x400244d349ee896eull }, + { 0x4023a04189374bc7ull, 0x40024508b82bed7eull }, + { 0x4023a0c49ba5e354ull, 0x4002453e2504803full }, + { 0x4023a147ae147ae1ull, 0x4002457390785450ull }, + { 0x4023a1cac083126full, 0x400245a8fa877c4dull }, + { 0x4023a24dd2f1a9fcull, 0x400245de63320ad0ull }, + { 0x4023a2d0e5604189ull, 0x40024613ca781272ull }, + { 0x4023a353f7ced917ull, 0x400246493059a5cbull }, + { 0x4023a3d70a3d70a4ull, 0x4002467e94d6d76full }, + { 0x4023a45a1cac0831ull, 0x400246b3f7efb9f5ull }, + { 0x4023a4dd2f1a9fbfull, 0x400246e959a45fefull }, + { 0x4023a5604189374cull, 0x4002471eb9f4dbeeull }, + { 0x4023a5e353f7ced9ull, 0x4002475418e14083ull }, + { 0x4023a66666666667ull, 0x400247897669a03dull }, + { 0x4023a6e978d4fdf4ull, 0x400247bed28e0da9ull }, + { 0x4023a76c8b439581ull, 0x400247f42d4e9b52ull }, + { 0x4023a7ef9db22d0eull, 0x4002482986ab5bc3ull }, + { 0x4023a872b020c49cull, 0x4002485edea46186ull }, + { 0x4023a8f5c28f5c29ull, 0x400248943539bf21ull }, + { 0x4023a978d4fdf3b6ull, 0x400248c98a6b871bull }, + { 0x4023a9fbe76c8b44ull, 0x400248fede39cbf8ull }, + { 0x4023aa7ef9db22d1ull, 0x4002493430a4a03bull }, + { 0x4023ab020c49ba5eull, 0x4002496981ac1666ull }, + { 0x4023ab851eb851ecull, 0x4002499ed15040faull }, + { 0x4023ac083126e979ull, 0x400249d41f913274ull }, + { 0x4023ac8b43958106ull, 0x40024a096c6efd53ull }, + { 0x4023ad0e56041894ull, 0x40024a3eb7e9b413ull }, + { 0x4023ad916872b021ull, 0x40024a740201692dull }, + { 0x4023ae147ae147aeull, 0x40024aa94ab62f1bull }, + { 0x4023ae978d4fdf3cull, 0x40024ade92081855ull }, + { 0x4023af1a9fbe76c9ull, 0x40024b13d7f73751ull }, + { 0x4023af9db22d0e56ull, 0x40024b491c839e82ull }, + { 0x4023b020c49ba5e3ull, 0x40024b7e5fad605eull }, + { 0x4023b0a3d70a3d71ull, 0x40024bb3a1748f55ull }, + { 0x4023b126e978d4feull, 0x40024be8e1d93dd8ull }, + { 0x4023b1a9fbe76c8bull, 0x40024c1e20db7e56ull }, + { 0x4023b22d0e560419ull, 0x40024c535e7b633cull }, + { 0x4023b2b020c49ba6ull, 0x40024c889ab8fef7ull }, + { 0x4023b33333333333ull, 0x40024cbdd59463f1ull }, + { 0x4023b3b645a1cac1ull, 0x40024cf30f0da493ull }, + { 0x4023b4395810624eull, 0x40024d284724d345ull }, + { 0x4023b4bc6a7ef9dbull, 0x40024d5d7dda026eull }, + { 0x4023b53f7ced9169ull, 0x40024d92b32d4473ull }, + { 0x4023b5c28f5c28f6ull, 0x40024dc7e71eabb6ull }, + { 0x4023b645a1cac083ull, 0x40024dfd19ae4a99ull }, + { 0x4023b6c8b4395810ull, 0x40024e324adc337full }, + { 0x4023b74bc6a7ef9eull, 0x40024e677aa878c5ull }, + { 0x4023b7ced916872bull, 0x40024e9ca9132ccaull }, + { 0x4023b851eb851eb8ull, 0x40024ed1d61c61e9ull }, + { 0x4023b8d4fdf3b646ull, 0x40024f0701c42a7full }, + { 0x4023b95810624dd3ull, 0x40024f3c2c0a98e3ull }, + { 0x4023b9db22d0e560ull, 0x40024f7154efbf6eull }, + { 0x4023ba5e353f7ceeull, 0x40024fa67c73b077ull }, + { 0x4023bae147ae147bull, 0x40024fdba2967e53ull }, + { 0x4023bb645a1cac08ull, 0x40025010c7583b56ull }, + { 0x4023bbe76c8b4396ull, 0x40025045eab8f9d2ull }, + { 0x4023bc6a7ef9db23ull, 0x4002507b0cb8cc17ull }, + { 0x4023bced916872b0ull, 0x400250b02d57c476ull }, + { 0x4023bd70a3d70a3eull, 0x400250e54c95f53dull }, + { 0x4023bdf3b645a1cbull, 0x4002511a6a7370b7ull }, + { 0x4023be76c8b43958ull, 0x4002514f86f04930ull }, + { 0x4023bef9db22d0e5ull, 0x40025184a20c90f2ull }, + { 0x4023bf7ced916873ull, 0x400251b9bbc85a47ull }, + { 0x4023c00000000000ull, 0x400251eed423b773ull }, + { 0x4023c083126e978dull, 0x40025223eb1ebabcull }, + { 0x4023c10624dd2f1bull, 0x4002525900b97668ull }, + { 0x4023c189374bc6a8ull, 0x4002528e14f3fcb8ull }, + { 0x4023c20c49ba5e35ull, 0x400252c327ce5feeull }, + { 0x4023c28f5c28f5c3ull, 0x400252f83948b24aull }, + { 0x4023c3126e978d50ull, 0x4002532d4963060aull }, + { 0x4023c395810624ddull, 0x40025362581d6d6aull }, + { 0x4023c4189374bc6bull, 0x400253976577faa8ull }, + { 0x4023c49ba5e353f8ull, 0x400253cc7172bffbull }, + { 0x4023c51eb851eb85ull, 0x400254017c0dcf9full }, + { 0x4023c5a1cac08313ull, 0x4002543685493bc9ull }, + { 0x4023c624dd2f1aa0ull, 0x4002546b8d2516b0ull }, + { 0x4023c6a7ef9db22dull, 0x400254a093a17288ull }, + { 0x4023c72b020c49baull, 0x400254d598be6184ull }, + { 0x4023c7ae147ae148ull, 0x4002550a9c7bf5d6ull }, + { 0x4023c83126e978d5ull, 0x4002553f9eda41afull }, + { 0x4023c8b439581062ull, 0x400255749fd9573cull }, + { 0x4023c9374bc6a7f0ull, 0x400255a99f7948acull }, + { 0x4023c9ba5e353f7dull, 0x400255de9dba282aull }, + { 0x4023ca3d70a3d70aull, 0x400256139a9c07e2ull }, + { 0x4023cac083126e98ull, 0x40025648961ef9fcull }, + { 0x4023cb4395810625ull, 0x4002567d904310a0ull }, + { 0x4023cbc6a7ef9db2ull, 0x400256b289085df4ull }, + { 0x4023cc49ba5e3540ull, 0x400256e7806ef41eull }, + { 0x4023cccccccccccdull, 0x4002571c7676e541ull }, + { 0x4023cd4fdf3b645aull, 0x400257516b20437full }, + { 0x4023cdd2f1a9fbe8ull, 0x400257865e6b20f9ull }, + { 0x4023ce5604189375ull, 0x400257bb50578fceull }, + { 0x4023ced916872b02ull, 0x400257f040e5a21cull }, + { 0x4023cf5c28f5c28full, 0x4002582530156a01ull }, + { 0x4023cfdf3b645a1dull, 0x4002585a1de6f996ull }, + { 0x4023d0624dd2f1aaull, 0x4002588f0a5a62f5ull }, + { 0x4023d0e560418937ull, 0x400258c3f56fb838ull }, + { 0x4023d16872b020c5ull, 0x400258f8df270b75ull }, + { 0x4023d1eb851eb852ull, 0x4002592dc7806ec0ull }, + { 0x4023d26e978d4fdfull, 0x40025962ae7bf430ull }, + { 0x4023d2f1a9fbe76dull, 0x400259979419add5ull }, + { 0x4023d374bc6a7efaull, 0x400259cc7859adc2ull }, + { 0x4023d3f7ced91687ull, 0x40025a015b3c0607ull }, + { 0x4023d47ae147ae15ull, 0x40025a363cc0c8b2ull }, + { 0x4023d4fdf3b645a2ull, 0x40025a6b1ce807cfull }, + { 0x4023d5810624dd2full, 0x40025a9ffbb1d56bull }, + { 0x4023d604189374bdull, 0x40025ad4d91e4391ull }, + { 0x4023d6872b020c4aull, 0x40025b09b52d6449ull }, + { 0x4023d70a3d70a3d7ull, 0x40025b3e8fdf499aull }, + { 0x4023d78d4fdf3b64ull, 0x40025b736934058bull }, + { 0x4023d810624dd2f2ull, 0x40025ba8412baa21ull }, + { 0x4023d89374bc6a7full, 0x40025bdd17c6495eull }, + { 0x4023d916872b020cull, 0x40025c11ed03f545ull }, + { 0x4023d9999999999aull, 0x40025c46c0e4bfd6ull }, + { 0x4023da1cac083127ull, 0x40025c7b9368bb11ull }, + { 0x4023da9fbe76c8b4ull, 0x40025cb0648ff8f3ull }, + { 0x4023db22d0e56042ull, 0x40025ce5345a8b79ull }, + { 0x4023dba5e353f7cfull, 0x40025d1a02c8849eull }, + { 0x4023dc28f5c28f5cull, 0x40025d4ecfd9f65aull }, + { 0x4023dcac083126eaull, 0x40025d839b8ef2a8ull }, + { 0x4023dd2f1a9fbe77ull, 0x40025db865e78b7cull }, + { 0x4023ddb22d0e5604ull, 0x40025ded2ee3d2cdull }, + { 0x4023de353f7ced92ull, 0x40025e21f683da8full }, + { 0x4023deb851eb851full, 0x40025e56bcc7b4b5ull }, + { 0x4023df3b645a1cacull, 0x40025e8b81af732full }, + { 0x4023dfbe76c8b439ull, 0x40025ec0453b27edull }, + { 0x4023e04189374bc7ull, 0x40025ef5076ae4dfull }, + { 0x4023e0c49ba5e354ull, 0x40025f29c83ebbf1ull }, + { 0x4023e147ae147ae1ull, 0x40025f5e87b6bf0eull }, + { 0x4023e1cac083126full, 0x40025f9345d30022ull }, + { 0x4023e24dd2f1a9fcull, 0x40025fc802939115ull }, + { 0x4023e2d0e5604189ull, 0x40025ffcbdf883cfull }, + { 0x4023e353f7ced917ull, 0x400260317801ea36ull }, + { 0x4023e3d70a3d70a4ull, 0x4002606630afd62full }, + { 0x4023e45a1cac0831ull, 0x4002609ae802599cull }, + { 0x4023e4dd2f1a9fbfull, 0x400260cf9df98661ull }, + { 0x4023e5604189374cull, 0x4002610452956e5eull }, + { 0x4023e5e353f7ced9ull, 0x4002613905d62372ull }, + { 0x4023e66666666667ull, 0x4002616db7bbb77bull }, + { 0x4023e6e978d4fdf4ull, 0x400261a268463c56ull }, + { 0x4023e76c8b439581ull, 0x400261d71775c3ddull }, + { 0x4023e7ef9db22d0eull, 0x4002620bc54a5febull }, + { 0x4023e872b020c49cull, 0x4002624071c42258ull }, + { 0x4023e8f5c28f5c29ull, 0x400262751ce31cfaull }, + { 0x4023e978d4fdf3b6ull, 0x400262a9c6a761a8ull }, + { 0x4023e9fbe76c8b44ull, 0x400262de6f110236ull }, + { 0x4023ea7ef9db22d1ull, 0x4002631316201077ull }, + { 0x4023eb020c49ba5eull, 0x40026347bbd49e3cull }, + { 0x4023eb851eb851ecull, 0x4002637c602ebd55ull }, + { 0x4023ec083126e979ull, 0x400263b1032e7f92ull }, + { 0x4023ec8b43958106ull, 0x400263e5a4d3f6beull }, + { 0x4023ed0e56041894ull, 0x4002641a451f34a8ull }, + { 0x4023ed916872b021ull, 0x4002644ee4104b18ull }, + { 0x4023ee147ae147aeull, 0x4002648381a74bd9ull }, + { 0x4023ee978d4fdf3cull, 0x400264b81de448b3ull }, + { 0x4023ef1a9fbe76c9ull, 0x400264ecb8c7536bull }, + { 0x4023ef9db22d0e56ull, 0x4002652152507dc7ull }, + { 0x4023f020c49ba5e3ull, 0x40026555ea7fd98bull }, + { 0x4023f0a3d70a3d71ull, 0x4002658a81557879ull }, + { 0x4023f126e978d4feull, 0x400265bf16d16c53ull }, + { 0x4023f1a9fbe76c8bull, 0x400265f3aaf3c6d7ull }, + { 0x4023f22d0e560419ull, 0x400266283dbc99c6ull }, + { 0x4023f2b020c49ba6ull, 0x4002665ccf2bf6daull }, + { 0x4023f33333333333ull, 0x400266915f41efd0ull }, + { 0x4023f3b645a1cac1ull, 0x400266c5edfe9663ull }, + { 0x4023f4395810624eull, 0x400266fa7b61fc4aull }, + { 0x4023f4bc6a7ef9dbull, 0x4002672f076c333eull }, + { 0x4023f53f7ced9169ull, 0x40026763921d4cf5ull }, + { 0x4023f5c28f5c28f6ull, 0x400267981b755b23ull }, + { 0x4023f645a1cac083ull, 0x400267cca3746f7bull }, + { 0x4023f6c8b4395811ull, 0x400268012a1a9bb1ull }, + { 0x4023f74bc6a7ef9eull, 0x40026835af67f174ull }, + { 0x4023f7ced916872bull, 0x4002686a335c8274ull }, + { 0x4023f851eb851eb8ull, 0x4002689eb5f8605full }, + { 0x4023f8d4fdf3b646ull, 0x400268d3373b9ce2ull }, + { 0x4023f95810624dd3ull, 0x40026907b72649a7ull }, + { 0x4023f9db22d0e560ull, 0x4002693c35b87858ull }, + { 0x4023fa5e353f7ceeull, 0x40026970b2f23a9full }, + { 0x4023fae147ae147bull, 0x400269a52ed3a222ull }, + { 0x4023fb645a1cac08ull, 0x400269d9a95cc087ull }, + { 0x4023fbe76c8b4396ull, 0x40026a0e228da773ull }, + { 0x4023fc6a7ef9db23ull, 0x40026a429a666889ull }, + { 0x4023fced916872b0ull, 0x40026a7710e71569ull }, + { 0x4023fd70a3d70a3eull, 0x40026aab860fbfb6ull }, + { 0x4023fdf3b645a1cbull, 0x40026adff9e0790eull }, + { 0x4023fe76c8b43958ull, 0x40026b146c59530eull }, + { 0x4023fef9db22d0e5ull, 0x40026b48dd7a5f52ull }, + { 0x4023ff7ced916873ull, 0x40026b7d4d43af77ull }, +}; diff --git a/tests/libc/math/pow_reference.h b/tests/libc/math/pow_reference.h new file mode 100644 index 0000000000..50425f7fac --- /dev/null +++ b/tests/libc/math/pow_reference.h @@ -0,0 +1,10019 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Generated by gen_pow_reference.py. Do not edit by hand. */ +/* Correctly-rounded reference for pow(2, i * 0.001), i in [0, 10000). */ + +#pragma once + +#include + +typedef struct { + uint64_t input_bits; /* IEEE-754 bits of v = i * 0.001 */ + uint64_t expected_bits; /* IEEE-754 bits of pow(2, v) */ +} PowReferenceEntry; + +#define POW_REFERENCE_COUNT 10000 + +static const PowReferenceEntry s_pow_reference[POW_REFERENCE_COUNT] = { + { 0x0000000000000000ull, 0x3ff0000000000000ull }, + { 0x3f50624dd2f1a9fcull, 0x3ff002d711c79a96ull }, + { 0x3f60624dd2f1a9fcull, 0x3ff005aea49e94faull }, + { 0x3f689374bc6a7efaull, 0x3ff00886b89bd7e7ull }, + { 0x3f70624dd2f1a9fcull, 0x3ff00b5f4dd65028ull }, + { 0x3f747ae147ae147bull, 0x3ff00e386464ee99ull }, + { 0x3f789374bc6a7efaull, 0x3ff01111fc5ea82aull }, + { 0x3f7cac083126e979ull, 0x3ff013ec15da75ddull }, + { 0x3f80624dd2f1a9fcull, 0x3ff016c6b0ef54c9ull }, + { 0x3f826e978d4fdf3cull, 0x3ff019a1cdb44618ull }, + { 0x3f847ae147ae147bull, 0x3ff01c7d6c404f0cull }, + { 0x3f86872b020c49baull, 0x3ff01f598caa78f9ull }, + { 0x3f889374bc6a7efaull, 0x3ff022362f09d14full }, + { 0x3f8a9fbe76c8b43aull, 0x3ff0251353756991ull }, + { 0x3f8cac083126e979ull, 0x3ff027f0fa04575dull }, + { 0x3f8eb851eb851eb8ull, 0x3ff02acf22cdb468ull }, + { 0x3f90624dd2f1a9fcull, 0x3ff02dadcde89e83ull }, + { 0x3f916872b020c49cull, 0x3ff0308cfb6c3798ull }, + { 0x3f926e978d4fdf3cull, 0x3ff0336cab6fa5acull }, + { 0x3f9374bc6a7ef9dbull, 0x3ff0364cde0a12e1ull }, + { 0x3f947ae147ae147bull, 0x3ff0392d9352ad75ull }, + { 0x3f95810624dd2f1bull, 0x3ff03c0ecb60a7c3ull }, + { 0x3f96872b020c49baull, 0x3ff03ef0864b3845ull }, + { 0x3f978d4fdf3b645aull, 0x3ff041d2c4299992ull }, + { 0x3f989374bc6a7efaull, 0x3ff044b585130a64ull }, + { 0x3f9999999999999aull, 0x3ff04798c91ecd91ull }, + { 0x3f9a9fbe76c8b43aull, 0x3ff04a7c90642a14ull }, + { 0x3f9ba5e353f7ced9ull, 0x3ff04d60dafa6b07ull }, + { 0x3f9cac083126e979ull, 0x3ff05045a8f8dfa8ull }, + { 0x3f9db22d0e560419ull, 0x3ff0532afa76db57ull }, + { 0x3f9eb851eb851eb8ull, 0x3ff05610cf8bb59aull }, + { 0x3f9fbe76c8b43958ull, 0x3ff058f7284eca18ull }, + { 0x3fa0624dd2f1a9fcull, 0x3ff05bde04d778a2ull }, + { 0x3fa0e5604189374cull, 0x3ff05ec5653d252cull }, + { 0x3fa16872b020c49cull, 0x3ff061ad499737d2ull }, + { 0x3fa1eb851eb851ecull, 0x3ff06495b1fd1cd8ull }, + { 0x3fa26e978d4fdf3cull, 0x3ff0677e9e8644a9ull }, + { 0x3fa2f1a9fbe76c8bull, 0x3ff06a680f4a23daull }, + { 0x3fa374bc6a7ef9dbull, 0x3ff06d520460332bull }, + { 0x3fa3f7ced916872bull, 0x3ff0703c7ddfef85ull }, + { 0x3fa47ae147ae147bull, 0x3ff073277be0d9fcull }, + { 0x3fa4fdf3b645a1cbull, 0x3ff07612fe7a77d1ull }, + { 0x3fa5810624dd2f1bull, 0x3ff078ff05c45273ull }, + { 0x3fa604189374bc6bull, 0x3ff07beb91d5f77cull }, + { 0x3fa6872b020c49baull, 0x3ff07ed8a2c6f8b5ull }, + { 0x3fa70a3d70a3d70aull, 0x3ff081c638aeec19ull }, + { 0x3fa78d4fdf3b645aull, 0x3ff084b453a56bceull }, + { 0x3fa810624dd2f1aaull, 0x3ff087a2f3c2162dull }, + { 0x3fa89374bc6a7efaull, 0x3ff08a92191c8dc2ull }, + { 0x3fa916872b020c4aull, 0x3ff08d81c3cc7947ull }, + { 0x3fa999999999999aull, 0x3ff09071f3e983adull }, + { 0x3faa1cac083126eaull, 0x3ff09362a98b5c14ull }, + { 0x3faa9fbe76c8b43aull, 0x3ff09653e4c9b5d5ull }, + { 0x3fab22d0e5604189ull, 0x3ff09945a5bc487bull }, + { 0x3faba5e353f7ced9ull, 0x3ff09c37ec7acfc7ull }, + { 0x3fac28f5c28f5c29ull, 0x3ff09f2ab91d0bb1ull }, + { 0x3facac083126e979ull, 0x3ff0a21e0bbac068ull }, + { 0x3fad2f1a9fbe76c9ull, 0x3ff0a511e46bb653ull }, + { 0x3fadb22d0e560419ull, 0x3ff0a8064347ba13ull }, + { 0x3fae353f7ced9169ull, 0x3ff0aafb28669c80ull }, + { 0x3faeb851eb851eb8ull, 0x3ff0adf093e032afull }, + { 0x3faf3b645a1cac08ull, 0x3ff0b0e685cc55edull }, + { 0x3fafbe76c8b43958ull, 0x3ff0b3dcfe42e3c6ull }, + { 0x3fb020c49ba5e354ull, 0x3ff0b6d3fd5bbdffull }, + { 0x3fb0624dd2f1a9fcull, 0x3ff0b9cb832eca9dull }, + { 0x3fb0a3d70a3d70a4ull, 0x3ff0bcc38fd3f3e2ull }, + { 0x3fb0e5604189374cull, 0x3ff0bfbc2363284full }, + { 0x3fb126e978d4fdf4ull, 0x3ff0c2b53df45aa4ull }, + { 0x3fb16872b020c49cull, 0x3ff0c5aedf9f81e2ull }, + { 0x3fb1a9fbe76c8b44ull, 0x3ff0c8a9087c994aull }, + { 0x3fb1eb851eb851ecull, 0x3ff0cba3b8a3a05full }, + { 0x3fb22d0e56041894ull, 0x3ff0ce9ef02c9ae7ull }, + { 0x3fb26e978d4fdf3cull, 0x3ff0d19aaf2f90ebull }, + { 0x3fb2b020c49ba5e3ull, 0x3ff0d496f5c48eb9ull }, + { 0x3fb2f1a9fbe76c8bull, 0x3ff0d793c403a4e2ull }, + { 0x3fb3333333333333ull, 0x3ff0da911a04e83eull }, + { 0x3fb374bc6a7ef9dbull, 0x3ff0dd8ef7e071ebull }, + { 0x3fb3b645a1cac083ull, 0x3ff0e08d5dae5f4dull }, + { 0x3fb3f7ced916872bull, 0x3ff0e38c4b86d211ull }, + { 0x3fb4395810624dd3ull, 0x3ff0e68bc181f02cull }, + { 0x3fb47ae147ae147bull, 0x3ff0e98bbfb7e3dcull }, + { 0x3fb4bc6a7ef9db23ull, 0x3ff0ec8c4640dbacull }, + { 0x3fb4fdf3b645a1cbull, 0x3ff0ef8d55350a6dull }, + { 0x3fb53f7ced916873ull, 0x3ff0f28eecaca73full }, + { 0x3fb5810624dd2f1bull, 0x3ff0f5910cbfed8eull }, + { 0x3fb5c28f5c28f5c3ull, 0x3ff0f893b5871d13ull }, + { 0x3fb604189374bc6bull, 0x3ff0fb96e71a79d3ull }, + { 0x3fb645a1cac08313ull, 0x3ff0fe9aa1924c25ull }, + { 0x3fb6872b020c49baull, 0x3ff1019ee506e0acull }, + { 0x3fb6c8b439581062ull, 0x3ff104a3b190885full }, + { 0x3fb70a3d70a3d70aull, 0x3ff107a907479882ull }, + { 0x3fb74bc6a7ef9db2ull, 0x3ff10aaee6446aaeull }, + { 0x3fb78d4fdf3b645aull, 0x3ff10db54e9f5ccdull }, + { 0x3fb7ced916872b02ull, 0x3ff110bc4070d11cull }, + { 0x3fb810624dd2f1aaull, 0x3ff113c3bbd12e2dull }, + { 0x3fb851eb851eb852ull, 0x3ff116cbc0d8dee5ull }, + { 0x3fb89374bc6a7efaull, 0x3ff119d44fa05281ull }, + { 0x3fb8d4fdf3b645a2ull, 0x3ff11cdd683ffc94ull }, + { 0x3fb916872b020c4aull, 0x3ff11fe70ad05505ull }, + { 0x3fb95810624dd2f2ull, 0x3ff122f13769d817ull }, + { 0x3fb999999999999aull, 0x3ff125fbee250664ull }, + { 0x3fb9db22d0e56042ull, 0x3ff129072f1a64dfull }, + { 0x3fba1cac083126eaull, 0x3ff12c12fa627cd5ull }, + { 0x3fba5e353f7ced92ull, 0x3ff12f1f5015dbeeull }, + { 0x3fba9fbe76c8b43aull, 0x3ff1322c304d1430ull }, + { 0x3fbae147ae147ae1ull, 0x3ff135399b20bbfbull }, + { 0x3fbb22d0e5604189ull, 0x3ff1384790a96e0dull }, + { 0x3fbb645a1cac0831ull, 0x3ff13b5610ffc982ull }, + { 0x3fbba5e353f7ced9ull, 0x3ff13e651c3c71d5ull }, + { 0x3fbbe76c8b439581ull, 0x3ff14174b2780ee0ull }, + { 0x3fbc28f5c28f5c29ull, 0x3ff14484d3cb4ce0ull }, + { 0x3fbc6a7ef9db22d1ull, 0x3ff14795804edc6full }, + { 0x3fbcac083126e979ull, 0x3ff14aa6b81b728dull }, + { 0x3fbced916872b021ull, 0x3ff14db87b49c899ull }, + { 0x3fbd2f1a9fbe76c9ull, 0x3ff150cac9f29c59ull }, + { 0x3fbd70a3d70a3d71ull, 0x3ff153dda42eaff5ull }, + { 0x3fbdb22d0e560419ull, 0x3ff156f10a16c9faull }, + { 0x3fbdf3b645a1cac1ull, 0x3ff15a04fbc3b55dull }, + { 0x3fbe353f7ced9169ull, 0x3ff15d19794e4178ull }, + { 0x3fbe76c8b4395811ull, 0x3ff1602e82cf420cull }, + { 0x3fbeb851eb851eb8ull, 0x3ff16344185f8f42ull }, + { 0x3fbef9db22d0e560ull, 0x3ff1665a3a1805afull }, + { 0x3fbf3b645a1cac08ull, 0x3ff16970e811864eull }, + { 0x3fbf7ced916872b0ull, 0x3ff16c882264f687ull }, + { 0x3fbfbe76c8b43958ull, 0x3ff16f9fe92b402cull }, + { 0x3fc0000000000000ull, 0x3ff172b83c7d517bull }, + { 0x3fc020c49ba5e354ull, 0x3ff175d11c741d20ull }, + { 0x3fc04189374bc6a8ull, 0x3ff178ea89289a34ull }, + { 0x3fc0624dd2f1a9fcull, 0x3ff17c0482b3c43full }, + { 0x3fc083126e978d50ull, 0x3ff17f1f092e9b37ull }, + { 0x3fc0a3d70a3d70a4ull, 0x3ff1823a1cb22383ull }, + { 0x3fc0c49ba5e353f8ull, 0x3ff18555bd5765fcull }, + { 0x3fc0e5604189374cull, 0x3ff18871eb376feaull }, + { 0x3fc10624dd2f1aa0ull, 0x3ff18b8ea66b5309ull }, + { 0x3fc126e978d4fdf4ull, 0x3ff18eabef0c2587ull }, + { 0x3fc147ae147ae148ull, 0x3ff191c9c5330209ull }, + { 0x3fc16872b020c49cull, 0x3ff194e828f907a5ull }, + { 0x3fc189374bc6a7f0ull, 0x3ff198071a7759e9ull }, + { 0x3fc1a9fbe76c8b44ull, 0x3ff19b2699c720d8ull }, + { 0x3fc1cac083126e98ull, 0x3ff19e46a70188edull }, + { 0x3fc1eb851eb851ecull, 0x3ff1a167423fc31bull }, + { 0x3fc20c49ba5e3540ull, 0x3ff1a4886b9b04cdull }, + { 0x3fc22d0e56041894ull, 0x3ff1a7aa232c87e9ull }, + { 0x3fc24dd2f1a9fbe8ull, 0x3ff1aacc690d8accull }, + { 0x3fc26e978d4fdf3cull, 0x3ff1adef3d575052ull }, + { 0x3fc28f5c28f5c28full, 0x3ff1b112a0231fd2ull }, + { 0x3fc2b020c49ba5e3ull, 0x3ff1b436918a451dull }, + { 0x3fc2d0e560418937ull, 0x3ff1b75b11a61085ull }, + { 0x3fc2f1a9fbe76c8bull, 0x3ff1ba80208fd6d8ull }, + { 0x3fc3126e978d4fdfull, 0x3ff1bda5be60f165ull }, + { 0x3fc3333333333333ull, 0x3ff1c0cbeb32bdf9ull }, + { 0x3fc353f7ced91687ull, 0x3ff1c3f2a71e9ee2ull }, + { 0x3fc374bc6a7ef9dbull, 0x3ff1c719f23dfaf1ull }, + { 0x3fc395810624dd2full, 0x3ff1ca41ccaa3d78ull }, + { 0x3fc3b645a1cac083ull, 0x3ff1cd6a367cd64cull }, + { 0x3fc3d70a3d70a3d7ull, 0x3ff1d0932fcf39c6ull }, + { 0x3fc3f7ced916872bull, 0x3ff1d3bcb8bae0c6ull }, + { 0x3fc4189374bc6a7full, 0x3ff1d6e6d15948adull }, + { 0x3fc4395810624dd3ull, 0x3ff1da1179c3f366ull }, + { 0x3fc45a1cac083127ull, 0x3ff1dd3cb2146762ull }, + { 0x3fc47ae147ae147bull, 0x3ff1e0687a642f9aull }, + { 0x3fc49ba5e353f7cfull, 0x3ff1e394d2ccdb8eull }, + { 0x3fc4bc6a7ef9db23ull, 0x3ff1e6c1bb67ff4bull }, + { 0x3fc4dd2f1a9fbe77ull, 0x3ff1e9ef344f3366ull }, + { 0x3fc4fdf3b645a1cbull, 0x3ff1ed1d3d9c14feull }, + { 0x3fc51eb851eb851full, 0x3ff1f04bd76845c1ull }, + { 0x3fc53f7ced916873ull, 0x3ff1f37b01cd6be7ull }, + { 0x3fc5604189374bc7ull, 0x3ff1f6aabce53238ull }, + { 0x3fc5810624dd2f1bull, 0x3ff1f9db08c94809ull }, + { 0x3fc5a1cac083126full, 0x3ff1fd0be593613dull }, + { 0x3fc5c28f5c28f5c3ull, 0x3ff2003d535d3649ull }, + { 0x3fc5e353f7ced917ull, 0x3ff2036f52408432ull }, + { 0x3fc604189374bc6bull, 0x3ff206a1e2570c8full }, + { 0x3fc624dd2f1a9fbfull, 0x3ff209d503ba9588ull }, + { 0x3fc645a1cac08313ull, 0x3ff20d08b684e9dbull }, + { 0x3fc6666666666667ull, 0x3ff2103cfacfd8d6ull }, + { 0x3fc6872b020c49baull, 0x3ff21371d0b53660ull }, + { 0x3fc6a7ef9db22d0eull, 0x3ff216a7384edaf4ull }, + { 0x3fc6c8b439581062ull, 0x3ff219dd31b6a3a1ull }, + { 0x3fc6e978d4fdf3b6ull, 0x3ff21d13bd067212ull }, + { 0x3fc70a3d70a3d70aull, 0x3ff2204ada582c86ull }, + { 0x3fc72b020c49ba5eull, 0x3ff2238289c5bdd8ull }, + { 0x3fc74bc6a7ef9db2ull, 0x3ff226bacb69157aull }, + { 0x3fc76c8b43958106ull, 0x3ff229f39f5c277bull }, + { 0x3fc78d4fdf3b645aull, 0x3ff22d2d05b8ec83ull }, + { 0x3fc7ae147ae147aeull, 0x3ff23066fe9961d8ull }, + { 0x3fc7ced916872b02ull, 0x3ff233a18a17895dull }, + { 0x3fc7ef9db22d0e56ull, 0x3ff236dca84d6991ull }, + { 0x3fc810624dd2f1aaull, 0x3ff23a1859550d94ull }, + { 0x3fc83126e978d4feull, 0x3ff23d549d488524ull }, + { 0x3fc851eb851eb852ull, 0x3ff240917441e49full }, + { 0x3fc872b020c49ba6ull, 0x3ff243cede5b4506ull }, + { 0x3fc89374bc6a7efaull, 0x3ff2470cdbaec3f9ull }, + { 0x3fc8b4395810624eull, 0x3ff24a4b6c5683beull }, + { 0x3fc8d4fdf3b645a2ull, 0x3ff24d8a906cab3cull }, + { 0x3fc8f5c28f5c28f6ull, 0x3ff250ca480b6600ull }, + { 0x3fc916872b020c4aull, 0x3ff2540a934ce43bull }, + { 0x3fc9374bc6a7ef9eull, 0x3ff2574b724b5ac5ull }, + { 0x3fc95810624dd2f2ull, 0x3ff25a8ce521031dull }, + { 0x3fc978d4fdf3b646ull, 0x3ff25dceebe81b67ull }, + { 0x3fc999999999999aull, 0x3ff2611186bae675ull }, + { 0x3fc9ba5e353f7ceeull, 0x3ff26454b5b3abbcull }, + { 0x3fc9db22d0e56042ull, 0x3ff2679878ecb760ull }, + { 0x3fc9fbe76c8b4396ull, 0x3ff26adcd0805a2eull }, + { 0x3fca1cac083126eaull, 0x3ff26e21bc88e99eull }, + { 0x3fca3d70a3d70a3eull, 0x3ff271673d20bfd7ull }, + { 0x3fca5e353f7ced92ull, 0x3ff274ad52623bacull }, + { 0x3fca7ef9db22d0e6ull, 0x3ff277f3fc67c09full }, + { 0x3fca9fbe76c8b43aull, 0x3ff27b3b3b4bb6dfull }, + { 0x3fcac083126e978dull, 0x3ff27e830f288b4dull }, + { 0x3fcae147ae147ae1ull, 0x3ff281cb7818af7cull }, + { 0x3fcb020c49ba5e35ull, 0x3ff28514763699aeull }, + { 0x3fcb22d0e5604189ull, 0x3ff2885e099cc4d9ull }, + { 0x3fcb4395810624ddull, 0x3ff28ba83265b0a6ull }, + { 0x3fcb645a1cac0831ull, 0x3ff28ef2f0abe172ull }, + { 0x3fcb851eb851eb85ull, 0x3ff2923e4489e04eull }, + { 0x3fcba5e353f7ced9ull, 0x3ff2958a2e1a3b03ull }, + { 0x3fcbc6a7ef9db22dull, 0x3ff298d6ad778410ull }, + { 0x3fcbe76c8b439581ull, 0x3ff29c23c2bc52a9ull }, + { 0x3fcc083126e978d5ull, 0x3ff29f716e0342bfull }, + { 0x3fcc28f5c28f5c29ull, 0x3ff2a2bfaf66f4f7ull }, + { 0x3fcc49ba5e353f7dull, 0x3ff2a60e87020eb4ull }, + { 0x3fcc6a7ef9db22d1ull, 0x3ff2a95df4ef3a12ull }, + { 0x3fcc8b4395810625ull, 0x3ff2acadf94925eaull }, + { 0x3fccac083126e979ull, 0x3ff2affe942a85cfull }, + { 0x3fcccccccccccccdull, 0x3ff2b34fc5ae1213ull }, + { 0x3fcced916872b021ull, 0x3ff2b6a18dee87c7ull }, + { 0x3fcd0e5604189375ull, 0x3ff2b9f3ed06a8baull }, + { 0x3fcd2f1a9fbe76c9ull, 0x3ff2bd46e3113b7bull }, + { 0x3fcd4fdf3b645a1dull, 0x3ff2c09a70290b5aull }, + { 0x3fcd70a3d70a3d71ull, 0x3ff2c3ee9468e868ull }, + { 0x3fcd916872b020c5ull, 0x3ff2c7434feba779ull }, + { 0x3fcdb22d0e560419ull, 0x3ff2ca98a2cc2224ull }, + { 0x3fcdd2f1a9fbe76dull, 0x3ff2cdee8d2536c4ull }, + { 0x3fcdf3b645a1cac1ull, 0x3ff2d1450f11c87aull }, + { 0x3fce147ae147ae15ull, 0x3ff2d49c28acbf2bull }, + { 0x3fce353f7ced9169ull, 0x3ff2d7f3da110784ull }, + { 0x3fce5604189374bdull, 0x3ff2db4c235992f9ull }, + { 0x3fce76c8b4395811ull, 0x3ff2dea504a157c6ull }, + { 0x3fce978d4fdf3b65ull, 0x3ff2e1fe7e0350f2ull }, + { 0x3fceb851eb851eb8ull, 0x3ff2e5588f9a7e4bull }, + { 0x3fced916872b020cull, 0x3ff2e8b33981e46dull }, + { 0x3fcef9db22d0e560ull, 0x3ff2ec0e7bd48cbeull }, + { 0x3fcf1a9fbe76c8b4ull, 0x3ff2ef6a56ad8570ull }, + { 0x3fcf3b645a1cac08ull, 0x3ff2f2c6ca27e185ull }, + { 0x3fcf5c28f5c28f5cull, 0x3ff2f623d65eb8caull }, + { 0x3fcf7ced916872b0ull, 0x3ff2f9817b6d27ddull }, + { 0x3fcf9db22d0e5604ull, 0x3ff2fcdfb96e502eull }, + { 0x3fcfbe76c8b43958ull, 0x3ff3003e907d57f9ull }, + { 0x3fcfdf3b645a1cacull, 0x3ff3039e00b56a50ull }, + { 0x3fd0000000000000ull, 0x3ff306fe0a31b715ull }, + { 0x3fd010624dd2f1aaull, 0x3ff30a5ead0d7300ull }, + { 0x3fd020c49ba5e354ull, 0x3ff30dbfe963d79aull }, + { 0x3fd03126e978d4feull, 0x3ff31121bf502344ull }, + { 0x3fd04189374bc6a8ull, 0x3ff314842eed9933ull }, + { 0x3fd051eb851eb852ull, 0x3ff317e738578175ull }, + { 0x3fd0624dd2f1a9fcull, 0x3ff31b4adba928edull }, + { 0x3fd072b020c49ba6ull, 0x3ff31eaf18fde159ull }, + { 0x3fd083126e978d50ull, 0x3ff32213f071014full }, + { 0x3fd09374bc6a7efaull, 0x3ff32579621de440ull }, + { 0x3fd0a3d70a3d70a4ull, 0x3ff328df6e1fea77ull }, + { 0x3fd0b4395810624eull, 0x3ff32c461492791eull }, + { 0x3fd0c49ba5e353f8ull, 0x3ff32fad5590fa38ull }, + { 0x3fd0d4fdf3b645a2ull, 0x3ff333153136dca8ull }, + { 0x3fd0e5604189374cull, 0x3ff3367da79f9430ull }, + { 0x3fd0f5c28f5c28f6ull, 0x3ff339e6b8e69970ull }, + { 0x3fd10624dd2f1aa0ull, 0x3ff33d50652769e9ull }, + { 0x3fd116872b020c4aull, 0x3ff340baac7d87fdull }, + { 0x3fd126e978d4fdf4ull, 0x3ff344258f047af1ull }, + { 0x3fd1374bc6a7ef9eull, 0x3ff347910cd7ceebull }, + { 0x3fd147ae147ae148ull, 0x3ff34afd261314f8ull }, + { 0x3fd15810624dd2f2ull, 0x3ff34e69dad1e306ull }, + { 0x3fd16872b020c49cull, 0x3ff351d72b2fd3ebull }, + { 0x3fd178d4fdf3b646ull, 0x3ff3554517488762ull }, + { 0x3fd189374bc6a7f0ull, 0x3ff358b39f37a20full }, + { 0x3fd199999999999aull, 0x3ff35c22c318cd7dull }, + { 0x3fd1a9fbe76c8b44ull, 0x3ff35f928307b81full }, + { 0x3fd1ba5e353f7ceeull, 0x3ff36302df201554ull }, + { 0x3fd1cac083126e98ull, 0x3ff36673d77d9d65ull }, + { 0x3fd1db22d0e56042ull, 0x3ff369e56c3c0d86ull }, + { 0x3fd1eb851eb851ecull, 0x3ff36d579d7727d8ull }, + { 0x3fd1fbe76c8b4396ull, 0x3ff370ca6b4ab369ull }, + { 0x3fd20c49ba5e3540ull, 0x3ff3743dd5d27c36ull }, + { 0x3fd21cac083126eaull, 0x3ff377b1dd2a532bull }, + { 0x3fd22d0e56041894ull, 0x3ff37b26816e0e23ull }, + { 0x3fd23d70a3d70a3eull, 0x3ff37e9bc2b987ebull }, + { 0x3fd24dd2f1a9fbe8ull, 0x3ff38211a128a042ull }, + { 0x3fd25e353f7ced92ull, 0x3ff385881cd73bd9ull }, + { 0x3fd26e978d4fdf3cull, 0x3ff388ff35e14454ull }, + { 0x3fd27ef9db22d0e5ull, 0x3ff38c76ec62a84cull }, + { 0x3fd28f5c28f5c28full, 0x3ff38fef40775b4full }, + { 0x3fd29fbe76c8b439ull, 0x3ff39368323b55e3ull }, + { 0x3fd2b020c49ba5e3ull, 0x3ff396e1c1ca9582ull }, + { 0x3fd2c083126e978dull, 0x3ff39a5bef411ca0ull }, + { 0x3fd2d0e560418937ull, 0x3ff39dd6babaf2a9ull }, + { 0x3fd2e147ae147ae1ull, 0x3ff3a15224542403ull }, + { 0x3fd2f1a9fbe76c8bull, 0x3ff3a4ce2c28c20full }, + { 0x3fd3020c49ba5e35ull, 0x3ff3a84ad254e328ull }, + { 0x3fd3126e978d4fdfull, 0x3ff3abc816f4a2a6ull }, + { 0x3fd322d0e5604189ull, 0x3ff3af45fa2420e0ull }, + { 0x3fd3333333333333ull, 0x3ff3b2c47bff8329ull }, + { 0x3fd34395810624ddull, 0x3ff3b6439ca2f3d3ull }, + { 0x3fd353f7ced91687ull, 0x3ff3b9c35c2aa231ull }, + { 0x3fd3645a1cac0831ull, 0x3ff3bd43bab2c296ull }, + { 0x3fd374bc6a7ef9dbull, 0x3ff3c0c4b8578e57ull }, + { 0x3fd3851eb851eb85ull, 0x3ff3c446553543ccull }, + { 0x3fd395810624dd2full, 0x3ff3c7c891682650ull }, + { 0x3fd3a5e353f7ced9ull, 0x3ff3cb4b6d0c7e41ull }, + { 0x3fd3b645a1cac083ull, 0x3ff3cecee83e9904ull }, + { 0x3fd3c6a7ef9db22dull, 0x3ff3d253031ac904ull }, + { 0x3fd3d70a3d70a3d7ull, 0x3ff3d5d7bdbd65b3ull }, + { 0x3fd3e76c8b439581ull, 0x3ff3d95d1842cb89ull }, + { 0x3fd3f7ced916872bull, 0x3ff3dce312c75c09ull }, + { 0x3fd4083126e978d5ull, 0x3ff3e069ad677dbfull }, + { 0x3fd4189374bc6a7full, 0x3ff3e3f0e83f9c42ull }, + { 0x3fd428f5c28f5c29ull, 0x3ff3e778c36c2833ull }, + { 0x3fd4395810624dd3ull, 0x3ff3eb013f099741ull }, + { 0x3fd449ba5e353f7dull, 0x3ff3ee8a5b346427ull }, + { 0x3fd45a1cac083127ull, 0x3ff3f21418090eb0ull }, + { 0x3fd46a7ef9db22d1ull, 0x3ff3f59e75a41bb3ull }, + { 0x3fd47ae147ae147bull, 0x3ff3f9297422151aull }, + { 0x3fd48b4395810625ull, 0x3ff3fcb5139f89dfull }, + { 0x3fd49ba5e353f7cfull, 0x3ff4004154390e0cull }, + { 0x3fd4ac083126e979ull, 0x3ff403ce360b3ac0ull }, + { 0x3fd4bc6a7ef9db23ull, 0x3ff4075bb932ae2cull }, + { 0x3fd4cccccccccccdull, 0x3ff40ae9ddcc0b96ull }, + { 0x3fd4dd2f1a9fbe77ull, 0x3ff40e78a3f3fb5aull }, + { 0x3fd4ed916872b021ull, 0x3ff412080bc72ae9ull }, + { 0x3fd4fdf3b645a1cbull, 0x3ff4159815624ccdull }, + { 0x3fd50e5604189375ull, 0x3ff41928c0e218a5ull }, + { 0x3fd51eb851eb851full, 0x3ff41cba0e634b2cull }, + { 0x3fd52f1a9fbe76c9ull, 0x3ff4204bfe02a635ull }, + { 0x3fd53f7ced916873ull, 0x3ff423de8fdcf0b0ull }, + { 0x3fd54fdf3b645a1dull, 0x3ff42771c40ef6a5ull }, + { 0x3fd5604189374bc7ull, 0x3ff42b059ab5893cull }, + { 0x3fd570a3d70a3d71ull, 0x3ff42e9a13ed7eb9ull }, + { 0x3fd5810624dd2f1bull, 0x3ff4322f2fd3b27full }, + { 0x3fd5916872b020c5ull, 0x3ff435c4ee850510ull }, + { 0x3fd5a1cac083126full, 0x3ff4395b501e5c0dull }, + { 0x3fd5b22d0e560419ull, 0x3ff43cf254bca23aull }, + { 0x3fd5c28f5c28f5c3ull, 0x3ff44089fc7cc77dull }, + { 0x3fd5d2f1a9fbe76dull, 0x3ff44422477bc0deull }, + { 0x3fd5e353f7ced917ull, 0x3ff447bb35d68888ull }, + { 0x3fd5f3b645a1cac1ull, 0x3ff44b54c7aa1dceull }, + { 0x3fd604189374bc6bull, 0x3ff44eeefd138525ull }, + { 0x3fd6147ae147ae15ull, 0x3ff45289d62fc82aull }, + { 0x3fd624dd2f1a9fbfull, 0x3ff45625531bf5a4ull }, + { 0x3fd6353f7ced9169ull, 0x3ff459c173f5217eull }, + { 0x3fd645a1cac08313ull, 0x3ff45d5e38d864cfull }, + { 0x3fd65604189374bdull, 0x3ff460fba1e2ddd9ull }, + { 0x3fd6666666666667ull, 0x3ff46499af31b007ull }, + { 0x3fd676c8b4395811ull, 0x3ff4683860e203f1ull }, + { 0x3fd6872b020c49baull, 0x3ff46bd7b711075cull }, + { 0x3fd6978d4fdf3b64ull, 0x3ff46f77b1dbed3cull }, + { 0x3fd6a7ef9db22d0eull, 0x3ff47318515fedb3ull }, + { 0x3fd6b851eb851eb8ull, 0x3ff476b995ba4611ull }, + { 0x3fd6c8b439581062ull, 0x3ff47a5b7f0838daull }, + { 0x3fd6d916872b020cull, 0x3ff47dfe0d670dc2ull }, + { 0x3fd6e978d4fdf3b6ull, 0x3ff481a140f411afull }, + { 0x3fd6f9db22d0e560ull, 0x3ff4854519cc96baull }, + { 0x3fd70a3d70a3d70aull, 0x3ff488e9980df434ull }, + { 0x3fd71a9fbe76c8b4ull, 0x3ff48c8ebbd5869eull }, + { 0x3fd72b020c49ba5eull, 0x3ff490348540afb4ull }, + { 0x3fd73b645a1cac08ull, 0x3ff493daf46cd666ull }, + { 0x3fd74bc6a7ef9db2ull, 0x3ff49782097766deull }, + { 0x3fd75c28f5c28f5cull, 0x3ff49b29c47dd27dull }, + { 0x3fd76c8b43958106ull, 0x3ff49ed2259d8fe1ull }, + { 0x3fd77ced916872b0ull, 0x3ff4a27b2cf41adfull }, + { 0x3fd78d4fdf3b645aull, 0x3ff4a624da9ef48bull }, + { 0x3fd79db22d0e5604ull, 0x3ff4a9cf2ebba334ull }, + { 0x3fd7ae147ae147aeull, 0x3ff4ad7a2967b268ull }, + { 0x3fd7be76c8b43958ull, 0x3ff4b125cac0b2f2ull }, + { 0x3fd7ced916872b02ull, 0x3ff4b4d212e43addull }, + { 0x3fd7df3b645a1cacull, 0x3ff4b87f01efe576ull }, + { 0x3fd7ef9db22d0e56ull, 0x3ff4bc2c9801534aull }, + { 0x3fd8000000000000ull, 0x3ff4bfdad5362a27ull }, + { 0x3fd810624dd2f1aaull, 0x3ff4c389b9ac1521ull }, + { 0x3fd820c49ba5e354ull, 0x3ff4c7394580c48eull }, + { 0x3fd83126e978d4feull, 0x3ff4cae978d1ee0bull }, + { 0x3fd84189374bc6a8ull, 0x3ff4ce9a53bd4c79ull }, + { 0x3fd851eb851eb852ull, 0x3ff4d24bd660a002ull }, + { 0x3fd8624dd2f1a9fcull, 0x3ff4d5fe00d9ae16ull }, + { 0x3fd872b020c49ba6ull, 0x3ff4d9b0d346416full }, + { 0x3fd883126e978d50ull, 0x3ff4dd644dc42a11ull }, + { 0x3fd89374bc6a7efaull, 0x3ff4e11870713d4bull }, + { 0x3fd8a3d70a3d70a4ull, 0x3ff4e4cd3b6b55b5ull }, + { 0x3fd8b4395810624eull, 0x3ff4e882aed05337ull }, + { 0x3fd8c49ba5e353f8ull, 0x3ff4ec38cabe1b05ull }, + { 0x3fd8d4fdf3b645a2ull, 0x3ff4efef8f5297a2ull }, + { 0x3fd8e5604189374cull, 0x3ff4f3a6fcabb8e0ull }, + { 0x3fd8f5c28f5c28f6ull, 0x3ff4f75f12e773e1ull }, + { 0x3fd90624dd2f1aa0ull, 0x3ff4fb17d223c319ull }, + { 0x3fd916872b020c4aull, 0x3ff4fed13a7ea64eull }, + { 0x3fd926e978d4fdf4ull, 0x3ff5028b4c16229aull }, + { 0x3fd9374bc6a7ef9eull, 0x3ff506460708426aull }, + { 0x3fd947ae147ae148ull, 0x3ff50a016b731581ull }, + { 0x3fd95810624dd2f2ull, 0x3ff50dbd7974b0f6ull }, + { 0x3fd96872b020c49cull, 0x3ff5117a312b2f3bull }, + { 0x3fd978d4fdf3b646ull, 0x3ff5153792b4b016ull }, + { 0x3fd989374bc6a7f0ull, 0x3ff518f59e2f58a9ull }, + { 0x3fd999999999999aull, 0x3ff51cb453b9536cull }, + { 0x3fd9a9fbe76c8b44ull, 0x3ff52073b370d036ull }, + { 0x3fd9ba5e353f7ceeull, 0x3ff52433bd740438ull }, + { 0x3fd9cac083126e98ull, 0x3ff527f471e129feull }, + { 0x3fd9db22d0e56042ull, 0x3ff52bb5d0d68175ull }, + { 0x3fd9eb851eb851ecull, 0x3ff52f77da724fe5ull }, + { 0x3fd9fbe76c8b4396ull, 0x3ff5333a8ed2dff9ull }, + { 0x3fda0c49ba5e3540ull, 0x3ff536fdee1681baull }, + { 0x3fda1cac083126eaull, 0x3ff53ac1f85b8a93ull }, + { 0x3fda2d0e56041894ull, 0x3ff53e86adc05553ull }, + { 0x3fda3d70a3d70a3eull, 0x3ff5424c0e63422aull }, + { 0x3fda4dd2f1a9fbe8ull, 0x3ff546121a62b6aeull }, + { 0x3fda5e353f7ced92ull, 0x3ff549d8d1dd1ddaull }, + { 0x3fda6e978d4fdf3cull, 0x3ff54da034f0e80eull }, + { 0x3fda7ef9db22d0e6ull, 0x3ff5516843bc8b13ull }, + { 0x3fda8f5c28f5c290ull, 0x3ff55530fe5e821aull }, + { 0x3fda9fbe76c8b43aull, 0x3ff558fa64f54dbbull }, + { 0x3fdab020c49ba5e3ull, 0x3ff55cc4779f73faull }, + { 0x3fdac083126e978dull, 0x3ff5608f367b8046ull }, + { 0x3fdad0e560418937ull, 0x3ff5645aa1a8037aull }, + { 0x3fdae147ae147ae1ull, 0x3ff56826b94393ddull }, + { 0x3fdaf1a9fbe76c8bull, 0x3ff56bf37d6ccd25ull }, + { 0x3fdb020c49ba5e35ull, 0x3ff56fc0ee425075ull }, + { 0x3fdb126e978d4fdfull, 0x3ff5738f0be2c463ull }, + { 0x3fdb22d0e5604189ull, 0x3ff5775dd66cd4f3ull }, + { 0x3fdb333333333333ull, 0x3ff57b2d4dff339dull }, + { 0x3fdb4395810624ddull, 0x3ff57efd72b89749ull }, + { 0x3fdb53f7ced91687ull, 0x3ff582ce44b7bc56ull }, + { 0x3fdb645a1cac0831ull, 0x3ff5869fc41b6495ull }, + { 0x3fdb74bc6a7ef9dbull, 0x3ff58a71f102574eull }, + { 0x3fdb851eb851eb85ull, 0x3ff58e44cb8b613full }, + { 0x3fdb95810624dd2full, 0x3ff5921853d5549full }, + { 0x3fdba5e353f7ced9ull, 0x3ff595ec89ff091bull }, + { 0x3fdbb645a1cac083ull, 0x3ff599c16e275bddull }, + { 0x3fdbc6a7ef9db22dull, 0x3ff59d97006d2f86ull }, + { 0x3fdbd70a3d70a3d7ull, 0x3ff5a16d40ef6c34ull }, + { 0x3fdbe76c8b439581ull, 0x3ff5a5442fccff83ull }, + { 0x3fdbf7ced916872bull, 0x3ff5a91bcd24dc89ull }, + { 0x3fdc083126e978d5ull, 0x3ff5acf41915fbdeull }, + { 0x3fdc189374bc6a7full, 0x3ff5b0cd13bf5b97ull }, + { 0x3fdc28f5c28f5c29ull, 0x3ff5b4a6bd3fff49ull }, + { 0x3fdc395810624dd3ull, 0x3ff5b88115b6f00eull }, + { 0x3fdc49ba5e353f7dull, 0x3ff5bc5c1d433c7dull }, + { 0x3fdc5a1cac083127ull, 0x3ff5c037d403f8b5ull }, + { 0x3fdc6a7ef9db22d1ull, 0x3ff5c4143a183e57ull }, + { 0x3fdc7ae147ae147bull, 0x3ff5c7f14f9f2c88ull }, + { 0x3fdc8b4395810625ull, 0x3ff5cbcf14b7e7f7ull }, + { 0x3fdc9ba5e353f7cfull, 0x3ff5cfad89819ad6ull }, + { 0x3fdcac083126e979ull, 0x3ff5d38cae1b74e2ull }, + { 0x3fdcbc6a7ef9db23ull, 0x3ff5d76c82a4ab60ull }, + { 0x3fdccccccccccccdull, 0x3ff5db4d073c7920ull }, + { 0x3fdcdd2f1a9fbe77ull, 0x3ff5df2e3c021e7dull }, + { 0x3fdced916872b021ull, 0x3ff5e3102114e15cull }, + { 0x3fdcfdf3b645a1cbull, 0x3ff5e6f2b6940d33ull }, + { 0x3fdd0e5604189375ull, 0x3ff5ead5fc9ef302ull }, + { 0x3fdd1eb851eb851full, 0x3ff5eeb9f354e95cull }, + { 0x3fdd2f1a9fbe76c9ull, 0x3ff5f29e9ad54c60ull }, + { 0x3fdd3f7ced916873ull, 0x3ff5f683f33f7dc1ull }, + { 0x3fdd4fdf3b645a1dull, 0x3ff5fa69fcb2e4c3ull }, + { 0x3fdd604189374bc7ull, 0x3ff5fe50b74eee3eull }, + { 0x3fdd70a3d70a3d71ull, 0x3ff6023823330c9dull }, + { 0x3fdd810624dd2f1bull, 0x3ff60620407eb7dfull }, + { 0x3fdd916872b020c5ull, 0x3ff60a090f516d9bull }, + { 0x3fdda1cac083126full, 0x3ff60df28fcab0feull }, + { 0x3fddb22d0e560419ull, 0x3ff611dcc20a0aceull }, + { 0x3fddc28f5c28f5c3ull, 0x3ff615c7a62f0968ull }, + { 0x3fddd2f1a9fbe76dull, 0x3ff619b33c5940c5ull }, + { 0x3fdde353f7ced917ull, 0x3ff61d9f84a84a78ull }, + { 0x3fddf3b645a1cac1ull, 0x3ff6218c7f3bc5afull }, + { 0x3fde04189374bc6bull, 0x3ff6257a2c335738ull }, + { 0x3fde147ae147ae15ull, 0x3ff629688baea97bull }, + { 0x3fde24dd2f1a9fbfull, 0x3ff62d579dcd6c81ull }, + { 0x3fde353f7ced9169ull, 0x3ff6314762af55f4ull }, + { 0x3fde45a1cac08313ull, 0x3ff63537da74211dull }, + { 0x3fde5604189374bdull, 0x3ff63929053b8ee9ull }, + { 0x3fde666666666667ull, 0x3ff63d1ae32565e4ull }, + { 0x3fde76c8b4395811ull, 0x3ff6410d74517243ull }, + { 0x3fde872b020c49bbull, 0x3ff64500b8df85ddull }, + { 0x3fde978d4fdf3b65ull, 0x3ff648f4b0ef782full }, + { 0x3fdea7ef9db22d0full, 0x3ff64ce95ca1265eull }, + { 0x3fdeb851eb851eb8ull, 0x3ff650debc147335ull }, + { 0x3fdec8b439581062ull, 0x3ff654d4cf69472bull }, + { 0x3fded916872b020cull, 0x3ff658cb96bf905full }, + { 0x3fdee978d4fdf3b6ull, 0x3ff65cc31237429bull }, + { 0x3fdef9db22d0e560ull, 0x3ff660bb41f05756ull }, + { 0x3fdf0a3d70a3d70aull, 0x3ff664b4260acdb1ull }, + { 0x3fdf1a9fbe76c8b4ull, 0x3ff668adbea6aa80ull }, + { 0x3fdf2b020c49ba5eull, 0x3ff66ca80be3f841ull }, + { 0x3fdf3b645a1cac08ull, 0x3ff670a30de2c725ull }, + { 0x3fdf4bc6a7ef9db2ull, 0x3ff6749ec4c32d0eull }, + { 0x3fdf5c28f5c28f5cull, 0x3ff6789b30a5458full }, + { 0x3fdf6c8b43958106ull, 0x3ff67c9851a931edull }, + { 0x3fdf7ced916872b0ull, 0x3ff6809627ef1923ull }, + { 0x3fdf8d4fdf3b645aull, 0x3ff68494b39727e1ull }, + { 0x3fdf9db22d0e5604ull, 0x3ff68893f4c1908cull }, + { 0x3fdfae147ae147aeull, 0x3ff68c93eb8e8b41ull }, + { 0x3fdfbe76c8b43958ull, 0x3ff69094981e55d3ull }, + { 0x3fdfced916872b02ull, 0x3ff69495fa9133d2ull }, + { 0x3fdfdf3b645a1cacull, 0x3ff6989813076e84ull }, + { 0x3fdfef9db22d0e56ull, 0x3ff69c9ae1a154edull }, + { 0x3fe0000000000000ull, 0x3ff6a09e667f3bcdull }, + { 0x3fe0083126e978d5ull, 0x3ff6a4a2a1c17d9eull }, + { 0x3fe010624dd2f1aaull, 0x3ff6a8a793887a9dull }, + { 0x3fe0189374bc6a7full, 0x3ff6acad3bf498c2ull }, + { 0x3fe020c49ba5e354ull, 0x3ff6b0b39b2643c8ull }, + { 0x3fe028f5c28f5c29ull, 0x3ff6b4bab13ded2aull }, + { 0x3fe03126e978d4feull, 0x3ff6b8c27e5c0c26ull }, + { 0x3fe0395810624dd3ull, 0x3ff6bccb02a11dbcull }, + { 0x3fe04189374bc6a8ull, 0x3ff6c0d43e2da4b2ull }, + { 0x3fe049ba5e353f7dull, 0x3ff6c4de31222993ull }, + { 0x3fe051eb851eb852ull, 0x3ff6c8e8db9f3aaeull }, + { 0x3fe05a1cac083127ull, 0x3ff6ccf43dc56c1dull }, + { 0x3fe0624dd2f1a9fcull, 0x3ff6d10057b557c1ull }, + { 0x3fe06a7ef9db22d1ull, 0x3ff6d50d298f9d43ull }, + { 0x3fe072b020c49ba6ull, 0x3ff6d91ab374e218ull }, + { 0x3fe07ae147ae147bull, 0x3ff6dd28f585d181ull }, + { 0x3fe083126e978d50ull, 0x3ff6e137efe31c8aull }, + { 0x3fe08b4395810625ull, 0x3ff6e547a2ad7a0eull }, + { 0x3fe09374bc6a7efaull, 0x3ff6e9580e05a6b5ull }, + { 0x3fe09ba5e353f7cfull, 0x3ff6ed69320c64f7ull }, + { 0x3fe0a3d70a3d70a4ull, 0x3ff6f17b0ee27d1full }, + { 0x3fe0ac083126e979ull, 0x3ff6f58da4a8bd48ull }, + { 0x3fe0b4395810624eull, 0x3ff6f9a0f37ff95dull }, + { 0x3fe0bc6a7ef9db23ull, 0x3ff6fdb4fb890b22ull }, + { 0x3fe0c49ba5e353f8ull, 0x3ff701c9bce4d22cull }, + { 0x3fe0cccccccccccdull, 0x3ff705df37b433e7ull }, + { 0x3fe0d4fdf3b645a2ull, 0x3ff709f56c181b96ull }, + { 0x3fe0dd2f1a9fbe77ull, 0x3ff70e0c5a317a54ull }, + { 0x3fe0e5604189374cull, 0x3ff7122402214714ull }, + { 0x3fe0ed916872b021ull, 0x3ff7163c64087ea5ull }, + { 0x3fe0f5c28f5c28f6ull, 0x3ff71a55800823aeull }, + { 0x3fe0fdf3b645a1cbull, 0x3ff71e6f56413eb4ull }, + { 0x3fe10624dd2f1aa0ull, 0x3ff72289e6d4de1aull }, + { 0x3fe10e5604189375ull, 0x3ff726a531e4161full }, + { 0x3fe116872b020c4aull, 0x3ff72ac1379000e3ull }, + { 0x3fe11eb851eb851full, 0x3ff72eddf7f9be64ull }, + { 0x3fe126e978d4fdf4ull, 0x3ff732fb73427484ull }, + { 0x3fe12f1a9fbe76c9ull, 0x3ff73719a98b4f05ull }, + { 0x3fe1374bc6a7ef9eull, 0x3ff73b389af57f8dull }, + { 0x3fe13f7ced916873ull, 0x3ff73f5847a23da8ull }, + { 0x3fe147ae147ae148ull, 0x3ff74378afb2c6c5ull }, + { 0x3fe14fdf3b645a1dull, 0x3ff74799d3485e3aull }, + { 0x3fe15810624dd2f2ull, 0x3ff74bbbb2844d47ull }, + { 0x3fe1604189374bc7ull, 0x3ff74fde4d87e311ull }, + { 0x3fe16872b020c49cull, 0x3ff75401a47474aaull }, + { 0x3fe170a3d70a3d71ull, 0x3ff75825b76b5d0bull }, + { 0x3fe178d4fdf3b646ull, 0x3ff75c4a868dfd1cull }, + { 0x3fe1810624dd2f1bull, 0x3ff7607011fdbbb0ull }, + { 0x3fe189374bc6a7f0ull, 0x3ff7649659dc0588ull }, + { 0x3fe1916872b020c5ull, 0x3ff768bd5e4a4d56ull }, + { 0x3fe199999999999aull, 0x3ff76ce51f6a0bb8ull }, + { 0x3fe1a1cac083126full, 0x3ff7710d9d5cbf41ull }, + { 0x3fe1a9fbe76c8b44ull, 0x3ff77536d843ec73ull }, + { 0x3fe1b22d0e560419ull, 0x3ff77960d0411dc4ull }, + { 0x3fe1ba5e353f7ceeull, 0x3ff77d8b8575e3a0ull }, + { 0x3fe1c28f5c28f5c3ull, 0x3ff781b6f803d466ull }, + { 0x3fe1cac083126e98ull, 0x3ff785e3280c8c6bull }, + { 0x3fe1d2f1a9fbe76dull, 0x3ff78a1015b1adfdull }, + { 0x3fe1db22d0e56042ull, 0x3ff78e3dc114e163ull }, + { 0x3fe1e353f7ced917ull, 0x3ff7926c2a57d4daull }, + { 0x3fe1eb851eb851ecull, 0x3ff7969b519c3c9cull }, + { 0x3fe1f3b645a1cac1ull, 0x3ff79acb3703d2e0ull }, + { 0x3fe1fbe76c8b4396ull, 0x3ff79efbdab057d6ull }, + { 0x3fe204189374bc6bull, 0x3ff7a32d3cc391aeull }, + { 0x3fe20c49ba5e3540ull, 0x3ff7a75f5d5f4c97ull }, + { 0x3fe2147ae147ae15ull, 0x3ff7ab923ca55abfull }, + { 0x3fe21cac083126eaull, 0x3ff7afc5dab79454ull }, + { 0x3fe224dd2f1a9fbfull, 0x3ff7b3fa37b7d788ull }, + { 0x3fe22d0e56041894ull, 0x3ff7b82f53c8088eull }, + { 0x3fe2353f7ced9169ull, 0x3ff7bc652f0a119full }, + { 0x3fe23d70a3d70a3eull, 0x3ff7c09bc99fe2f8ull }, + { 0x3fe245a1cac08313ull, 0x3ff7c4d323ab72ddull }, + { 0x3fe24dd2f1a9fbe8ull, 0x3ff7c90b3d4ebd97ull }, + { 0x3fe25604189374bdull, 0x3ff7cd4416abc57bull }, + { 0x3fe25e353f7ced92ull, 0x3ff7d17dafe492e5ull }, + { 0x3fe2666666666667ull, 0x3ff7d5b8091b343cull }, + { 0x3fe26e978d4fdf3cull, 0x3ff7d9f32271bdf3ull }, + { 0x3fe276c8b4395810ull, 0x3ff7de2efc0a4a87ull }, + { 0x3fe27ef9db22d0e5ull, 0x3ff7e26b9606fa89ull }, + { 0x3fe2872b020c49baull, 0x3ff7e6a8f089f492ull }, + { 0x3fe28f5c28f5c28full, 0x3ff7eae70bb5654eull }, + { 0x3fe2978d4fdf3b64ull, 0x3ff7ef25e7ab7f7bull }, + { 0x3fe29fbe76c8b439ull, 0x3ff7f365848e7be7ull }, + { 0x3fe2a7ef9db22d0eull, 0x3ff7f7a5e2809973ull }, + { 0x3fe2b020c49ba5e3ull, 0x3ff7fbe701a41d15ull }, + { 0x3fe2b851eb851eb8ull, 0x3ff80028e21b51daull }, + { 0x3fe2c083126e978dull, 0x3ff8046b840888e2ull }, + { 0x3fe2c8b439581062ull, 0x3ff808aee78e1966ull }, + { 0x3fe2d0e560418937ull, 0x3ff80cf30cce60b8ull }, + { 0x3fe2d916872b020cull, 0x3ff81137f3ebc243ull }, + { 0x3fe2e147ae147ae1ull, 0x3ff8157d9d08a78dull }, + { 0x3fe2e978d4fdf3b6ull, 0x3ff819c408478038ull }, + { 0x3fe2f1a9fbe76c8bull, 0x3ff81e0b35cac201ull }, + { 0x3fe2f9db22d0e560ull, 0x3ff8225325b4e8c5ull }, + { 0x3fe3020c49ba5e35ull, 0x3ff8269bd828767full }, + { 0x3fe30a3d70a3d70aull, 0x3ff82ae54d47f34bull }, + { 0x3fe3126e978d4fdfull, 0x3ff82f2f8535ed65ull }, + { 0x3fe31a9fbe76c8b4ull, 0x3ff8337a8014f92cull }, + { 0x3fe322d0e5604189ull, 0x3ff837c63e07b122ull }, + { 0x3fe32b020c49ba5eull, 0x3ff83c12bf30b5eeull }, + { 0x3fe3333333333333ull, 0x3ff8406003b2ae5cull }, + { 0x3fe33b645a1cac08ull, 0x3ff844ae0bb0475full }, + { 0x3fe34395810624ddull, 0x3ff848fcd74c3412ull }, + { 0x3fe34bc6a7ef9db2ull, 0x3ff84d4c66a92db7ull }, + { 0x3fe353f7ced91687ull, 0x3ff8519cb9e9f3bdull }, + { 0x3fe35c28f5c28f5cull, 0x3ff855edd1314bbbull }, + { 0x3fe3645a1cac0831ull, 0x3ff85a3faca20175ull }, + { 0x3fe36c8b43958106ull, 0x3ff85e924c5ee6deull }, + { 0x3fe374bc6a7ef9dbull, 0x3ff862e5b08ad415ull }, + { 0x3fe37ced916872b0ull, 0x3ff86739d948a769ull }, + { 0x3fe3851eb851eb85ull, 0x3ff86b8ec6bb455aull }, + { 0x3fe38d4fdf3b645aull, 0x3ff86fe479059899ull }, + { 0x3fe395810624dd2full, 0x3ff8743af04a920bull }, + { 0x3fe39db22d0e5604ull, 0x3ff878922cad28c7ull }, + { 0x3fe3a5e353f7ced9ull, 0x3ff87cea2e505a1aull }, + { 0x3fe3ae147ae147aeull, 0x3ff88142f5572986ull }, + { 0x3fe3b645a1cac083ull, 0x3ff8859c81e4a0c5ull }, + { 0x3fe3be76c8b43958ull, 0x3ff889f6d41bcfc9ull }, + { 0x3fe3c6a7ef9db22dull, 0x3ff88e51ec1fccbdull }, + { 0x3fe3ced916872b02ull, 0x3ff892adca13b407ull }, + { 0x3fe3d70a3d70a3d7ull, 0x3ff8970a6e1aa848ull }, + { 0x3fe3df3b645a1cacull, 0x3ff89b67d857d25dull }, + { 0x3fe3e76c8b439581ull, 0x3ff89fc608ee6163ull }, + { 0x3fe3ef9db22d0e56ull, 0x3ff8a42500018ab3ull }, + { 0x3fe3f7ced916872bull, 0x3ff8a884bdb489e7ull }, + { 0x3fe4000000000000ull, 0x3ff8ace5422aa0dbull }, + { 0x3fe4083126e978d5ull, 0x3ff8b1468d8717adull }, + { 0x3fe410624dd2f1aaull, 0x3ff8b5a89fed3cbcull }, + { 0x3fe4189374bc6a7full, 0x3ff8ba0b798064aeull }, + { 0x3fe420c49ba5e354ull, 0x3ff8be6f1a63ea6dull }, + { 0x3fe428f5c28f5c29ull, 0x3ff8c2d382bb2f2bull }, + { 0x3fe43126e978d4feull, 0x3ff8c738b2a99a61ull }, + { 0x3fe4395810624dd3ull, 0x3ff8cb9eaa5299cfull }, + { 0x3fe44189374bc6a8ull, 0x3ff8d00569d9a182ull }, + { 0x3fe449ba5e353f7dull, 0x3ff8d46cf1622bd1ull }, + { 0x3fe451eb851eb852ull, 0x3ff8d8d5410fb95dull }, + { 0x3fe45a1cac083127ull, 0x3ff8dd3e5905d118ull }, + { 0x3fe4624dd2f1a9fcull, 0x3ff8e1a839680041ull }, + { 0x3fe46a7ef9db22d1ull, 0x3ff8e612e259da64ull }, + { 0x3fe472b020c49ba6ull, 0x3ff8ea7e53fef961ull }, + { 0x3fe47ae147ae147bull, 0x3ff8eeea8e7afd69ull }, + { 0x3fe483126e978d50ull, 0x3ff8f35791f18cfeull }, + { 0x3fe48b4395810625ull, 0x3ff8f7c55e8654f9ull }, + { 0x3fe49374bc6a7efaull, 0x3ff8fc33f45d0886ull }, + { 0x3fe49ba5e353f7cfull, 0x3ff900a353996128ull }, + { 0x3fe4a3d70a3d70a4ull, 0x3ff905137c5f1eb9ull }, + { 0x3fe4ac083126e979ull, 0x3ff909846ed2076dull }, + { 0x3fe4b4395810624eull, 0x3ff90df62b15e7ceull }, + { 0x3fe4bc6a7ef9db23ull, 0x3ff91268b14e92c6ull }, + { 0x3fe4c49ba5e353f8ull, 0x3ff916dc019fe196ull }, + { 0x3fe4cccccccccccdull, 0x3ff91b501c2db3dfull }, + { 0x3fe4d4fdf3b645a2ull, 0x3ff91fc5011bef9full }, + { 0x3fe4dd2f1a9fbe77ull, 0x3ff9243ab08e8135ull }, + { 0x3fe4e5604189374cull, 0x3ff928b12aa95b5dull }, + { 0x3fe4ed916872b021ull, 0x3ff92d286f907738ull }, + { 0x3fe4f5c28f5c28f6ull, 0x3ff931a07f67d447ull }, + { 0x3fe4fdf3b645a1cbull, 0x3ff936195a537871ull }, + { 0x3fe50624dd2f1aa0ull, 0x3ff93a9300777002ull }, + { 0x3fe50e5604189375ull, 0x3ff93f0d71f7cda9ull }, + { 0x3fe516872b020c4aull, 0x3ff94388aef8aa80ull }, + { 0x3fe51eb851eb851full, 0x3ff94804b79e2607ull }, + { 0x3fe526e978d4fdf4ull, 0x3ff94c818c0c6628ull }, + { 0x3fe52f1a9fbe76c9ull, 0x3ff950ff2c679738ull }, + { 0x3fe5374bc6a7ef9eull, 0x3ff9557d98d3ebf8ull }, + { 0x3fe53f7ced916873ull, 0x3ff959fcd1759d95ull }, + { 0x3fe547ae147ae148ull, 0x3ff95e7cd670ebabull }, + { 0x3fe54fdf3b645a1dull, 0x3ff962fda7ea1c45ull }, + { 0x3fe55810624dd2f2ull, 0x3ff9677f46057bdeull }, + { 0x3fe5604189374bc7ull, 0x3ff96c01b0e75d62ull }, + { 0x3fe56872b020c49cull, 0x3ff97084e8b41a31ull }, + { 0x3fe570a3d70a3d71ull, 0x3ff97508ed90121dull }, + { 0x3fe578d4fdf3b646ull, 0x3ff9798dbf9fab6full }, + { 0x3fe5810624dd2f1bull, 0x3ff97e135f0752e5ull }, + { 0x3fe589374bc6a7f0ull, 0x3ff98299cbeb7bb3ull }, + { 0x3fe5916872b020c5ull, 0x3ff9872106709f88ull }, + { 0x3fe599999999999aull, 0x3ff98ba90ebb3e8aull }, + { 0x3fe5a1cac083126full, 0x3ff99031e4efdf5bull }, + { 0x3fe5a9fbe76c8b44ull, 0x3ff994bb89330f19ull }, + { 0x3fe5b22d0e560419ull, 0x3ff99945fba9615dull }, + { 0x3fe5ba5e353f7ceeull, 0x3ff99dd13c777042ull }, + { 0x3fe5c28f5c28f5c3ull, 0x3ff9a25d4bc1dc5eull }, + { 0x3fe5cac083126e98ull, 0x3ff9a6ea29ad4ccaull }, + { 0x3fe5d2f1a9fbe76dull, 0x3ff9ab77d65e6f1full }, + { 0x3fe5db22d0e56042ull, 0x3ff9b00651f9f77aull }, + { 0x3fe5e353f7ced917ull, 0x3ff9b4959ca4a07cull }, + { 0x3fe5eb851eb851ecull, 0x3ff9b925b6832b4aull }, + { 0x3fe5f3b645a1cac1ull, 0x3ff9bdb69fba5f8full }, + { 0x3fe5fbe76c8b4396ull, 0x3ff9c248586f0b7full }, + { 0x3fe604189374bc6bull, 0x3ff9c6dae0c603d5ull }, + { 0x3fe60c49ba5e3540ull, 0x3ff9cb6e38e423d7ull }, + { 0x3fe6147ae147ae15ull, 0x3ff9d00260ee4d53ull }, + { 0x3fe61cac083126eaull, 0x3ff9d497590968a7ull }, + { 0x3fe624dd2f1a9fbfull, 0x3ff9d92d215a64bbull }, + { 0x3fe62d0e56041894ull, 0x3ff9ddc3ba063707ull }, + { 0x3fe6353f7ced9169ull, 0x3ff9e25b2331db91ull }, + { 0x3fe63d70a3d70a3eull, 0x3ff9e6f35d0254f2ull }, + { 0x3fe645a1cac08313ull, 0x3ff9eb8c679cac53ull }, + { 0x3fe64dd2f1a9fbe8ull, 0x3ff9f0264325f16full }, + { 0x3fe65604189374bdull, 0x3ff9f4c0efc33a98ull }, + { 0x3fe65e353f7ced92ull, 0x3ff9f95c6d99a4b4ull }, + { 0x3fe6666666666667ull, 0x3ff9fdf8bcce533eull }, + { 0x3fe66e978d4fdf3cull, 0x3ffa0295dd86704aull }, + { 0x3fe676c8b4395811ull, 0x3ffa0733cfe72c86ull }, + { 0x3fe67ef9db22d0e6ull, 0x3ffa0bd29415bf38ull }, + { 0x3fe6872b020c49baull, 0x3ffa10722a376641ull }, + { 0x3fe68f5c28f5c28full, 0x3ffa151292716621ull }, + { 0x3fe6978d4fdf3b64ull, 0x3ffa19b3cce909f3ull }, + { 0x3fe69fbe76c8b439ull, 0x3ffa1e55d9c3a371ull }, + { 0x3fe6a7ef9db22d0eull, 0x3ffa22f8b9268af5ull }, + { 0x3fe6b020c49ba5e3ull, 0x3ffa279c6b371f7bull }, + { 0x3fe6b851eb851eb8ull, 0x3ffa2c40f01ac6a1ull }, + { 0x3fe6c083126e978dull, 0x3ffa30e647f6eca7ull }, + { 0x3fe6c8b439581062ull, 0x3ffa358c72f10473ull }, + { 0x3fe6d0e560418937ull, 0x3ffa3a33712e878full }, + { 0x3fe6d916872b020cull, 0x3ffa3edb42d4f62full }, + { 0x3fe6e147ae147ae1ull, 0x3ffa4383e809d72bull }, + { 0x3fe6e978d4fdf3b6ull, 0x3ffa482d60f2b809ull }, + { 0x3fe6f1a9fbe76c8bull, 0x3ffa4cd7adb52cf5ull }, + { 0x3fe6f9db22d0e560ull, 0x3ffa5182ce76d0caull }, + { 0x3fe7020c49ba5e35ull, 0x3ffa562ec35d450dull }, + { 0x3fe70a3d70a3d70aull, 0x3ffa5adb8c8e31f4ull }, + { 0x3fe7126e978d4fdfull, 0x3ffa5f892a2f4662ull }, + { 0x3fe71a9fbe76c8b4ull, 0x3ffa64379c6637ebull }, + { 0x3fe722d0e5604189ull, 0x3ffa68e6e358c2d4ull }, + { 0x3fe72b020c49ba5eull, 0x3ffa6d96ff2caa17ull }, + { 0x3fe7333333333333ull, 0x3ffa7247f007b75full }, + { 0x3fe73b645a1cac08ull, 0x3ffa76f9b60fbb0full }, + { 0x3fe74395810624ddull, 0x3ffa7bac516a8c3dull }, + { 0x3fe74bc6a7ef9db2ull, 0x3ffa805fc23e08baull }, + { 0x3fe753f7ced91687ull, 0x3ffa851408b0150dull }, + { 0x3fe75c28f5c28f5cull, 0x3ffa89c924e69c79ull }, + { 0x3fe7645a1cac0831ull, 0x3ffa8e7f170790fbull }, + { 0x3fe76c8b43958106ull, 0x3ffa9335df38eb4bull }, + { 0x3fe774bc6a7ef9dbull, 0x3ffa97ed7da0aae2ull }, + { 0x3fe77ced916872b0ull, 0x3ffa9ca5f264d5f4ull }, + { 0x3fe7851eb851eb85ull, 0x3ffaa15f3dab7978ull }, + { 0x3fe78d4fdf3b645aull, 0x3ffaa6195f9aa924ull }, + { 0x3fe795810624dd2full, 0x3ffaaad458587f70ull }, + { 0x3fe79db22d0e5604ull, 0x3ffaaf90280b1d99ull }, + { 0x3fe7a5e353f7ced9ull, 0x3ffab44cced8aba1ull }, + { 0x3fe7ae147ae147aeull, 0x3ffab90a4ce7584eull }, + { 0x3fe7b645a1cac083ull, 0x3ffabdc8a25d592eull }, + { 0x3fe7be76c8b43958ull, 0x3ffac287cf60ea98ull }, + { 0x3fe7c6a7ef9db22dull, 0x3ffac747d4184facull }, + { 0x3fe7ced916872b02ull, 0x3ffacc08b0a9d253ull }, + { 0x3fe7d70a3d70a3d7ull, 0x3ffad0ca653bc346ull }, + { 0x3fe7df3b645a1cacull, 0x3ffad58cf1f47a06ull }, + { 0x3fe7e76c8b439581ull, 0x3ffada5056fa54e7ull }, + { 0x3fe7ef9db22d0e56ull, 0x3ffadf149473b909ull }, + { 0x3fe7f7ced916872bull, 0x3ffae3d9aa87125full }, + { 0x3fe8000000000000ull, 0x3ffae89f995ad3adull }, + { 0x3fe8083126e978d5ull, 0x3ffaed666115768bull }, + { 0x3fe810624dd2f1aaull, 0x3ffaf22e01dd7b63ull }, + { 0x3fe8189374bc6a7full, 0x3ffaf6f67bd96978ull }, + { 0x3fe820c49ba5e354ull, 0x3ffafbbfcf2fcee0ull }, + { 0x3fe828f5c28f5c29ull, 0x3ffb0089fc07408eull }, + { 0x3fe83126e978d4feull, 0x3ffb055502865a49ull }, + { 0x3fe8395810624dd3ull, 0x3ffb0a20e2d3beb5ull }, + { 0x3fe84189374bc6a8ull, 0x3ffb0eed9d161753ull }, + { 0x3fe849ba5e353f7dull, 0x3ffb13bb3174147dull }, + { 0x3fe851eb851eb852ull, 0x3ffb1889a0146d6full }, + { 0x3fe85a1cac083127ull, 0x3ffb1d58e91de043ull }, + { 0x3fe8624dd2f1a9fcull, 0x3ffb22290cb731f1ull }, + { 0x3fe86a7ef9db22d1ull, 0x3ffb26fa0b072e58ull }, + { 0x3fe872b020c49ba6ull, 0x3ffb2bcbe434a834ull }, + { 0x3fe87ae147ae147bull, 0x3ffb309e9866792bull }, + { 0x3fe883126e978d50ull, 0x3ffb357227c381c4ull }, + { 0x3fe88b4395810625ull, 0x3ffb3a469272a96eull }, + { 0x3fe89374bc6a7efaull, 0x3ffb3f1bd89ade80ull }, + { 0x3fe89ba5e353f7cfull, 0x3ffb43f1fa63163cull }, + { 0x3fe8a3d70a3d70a4ull, 0x3ffb48c8f7f24ccaull }, + { 0x3fe8ac083126e979ull, 0x3ffb4da0d16f8542ull }, + { 0x3fe8b4395810624eull, 0x3ffb52798701c9a6ull }, + { 0x3fe8bc6a7ef9db23ull, 0x3ffb575318d02ae8ull }, + { 0x3fe8c49ba5e353f8ull, 0x3ffb5c2d8701c0e6ull }, + { 0x3fe8cccccccccccdull, 0x3ffb6108d1bdaa73ull }, + { 0x3fe8d4fdf3b645a2ull, 0x3ffb65e4f92b0d4full }, + { 0x3fe8dd2f1a9fbe77ull, 0x3ffb6ac1fd711630ull }, + { 0x3fe8e5604189374cull, 0x3ffb6f9fdeb6f8c0ull }, + { 0x3fe8ed916872b021ull, 0x3ffb747e9d23ef9eull }, + { 0x3fe8f5c28f5c28f6ull, 0x3ffb795e38df3c5eull }, + { 0x3fe8fdf3b645a1cbull, 0x3ffb7e3eb210278full }, + { 0x3fe90624dd2f1aa0ull, 0x3ffb832008de00b6ull }, + { 0x3fe90e5604189375ull, 0x3ffb88023d701e54ull }, + { 0x3fe916872b020c4aull, 0x3ffb8ce54feddde6ull }, + { 0x3fe91eb851eb851full, 0x3ffb91c9407ea3e5ull }, + { 0x3fe926e978d4fdf4ull, 0x3ffb96ae0f49dbc7ull }, + { 0x3fe92f1a9fbe76c9ull, 0x3ffb9b93bc76f804ull }, + { 0x3fe9374bc6a7ef9eull, 0x3ffba07a482d7213ull }, + { 0x3fe93f7ced916873ull, 0x3ffba561b294ca6dull }, + { 0x3fe947ae147ae148ull, 0x3ffbaa49fbd4888cull }, + { 0x3fe94fdf3b645a1dull, 0x3ffbaf3324143af2ull }, + { 0x3fe95810624dd2f2ull, 0x3ffbb41d2b7b7725ull }, + { 0x3fe9604189374bc7ull, 0x3ffbb9081231d9b1ull }, + { 0x3fe96872b020c49cull, 0x3ffbbdf3d85f0629ull }, + { 0x3fe970a3d70a3d71ull, 0x3ffbc2e07e2aa72dull }, + { 0x3fe978d4fdf3b646ull, 0x3ffbc7ce03bc6e65ull }, + { 0x3fe9810624dd2f1bull, 0x3ffbccbc693c1483ull }, + { 0x3fe989374bc6a7f0ull, 0x3ffbd1abaed1594bull }, + { 0x3fe9916872b020c5ull, 0x3ffbd69bd4a4038aull }, + { 0x3fe999999999999aull, 0x3ffbdb8cdadbe120ull }, + { 0x3fe9a1cac083126full, 0x3ffbe07ec1a0c6fdull }, + { 0x3fe9a9fbe76c8b44ull, 0x3ffbe571891a9122ull }, + { 0x3fe9b22d0e560419ull, 0x3ffbea65317122a4ull }, + { 0x3fe9ba5e353f7ceeull, 0x3ffbef59bacc65acull }, + { 0x3fe9c28f5c28f5c3ull, 0x3ffbf44f25544b7bull }, + { 0x3fe9cac083126e98ull, 0x3ffbf9457130cc66ull }, + { 0x3fe9d2f1a9fbe76dull, 0x3ffbfe3c9e89e7dcull }, + { 0x3fe9db22d0e56042ull, 0x3ffc0334ad87a465ull }, + { 0x3fe9e353f7ced917ull, 0x3ffc082d9e520fa4ull }, + { 0x3fe9eb851eb851ecull, 0x3ffc0d2771113e59ull }, + { 0x3fe9f3b645a1cac1ull, 0x3ffc122225ed4c60ull }, + { 0x3fe9fbe76c8b4396ull, 0x3ffc171dbd0e5cb4ull }, + { 0x3fea04189374bc6bull, 0x3ffc1c1a369c9971ull }, + { 0x3fea0c49ba5e3540ull, 0x3ffc211792c033d1ull }, + { 0x3fea147ae147ae15ull, 0x3ffc2615d1a16435ull }, + { 0x3fea1cac083126eaull, 0x3ffc2b14f3686a1eull }, + { 0x3fea24dd2f1a9fbfull, 0x3ffc3014f83d8c33ull }, + { 0x3fea2d0e56041894ull, 0x3ffc3515e0491843ull }, + { 0x3fea353f7ced9169ull, 0x3ffc3a17abb36341ull }, + { 0x3fea3d70a3d70a3eull, 0x3ffc3f1a5aa4c94bull }, + { 0x3fea45a1cac08313ull, 0x3ffc441ded45adaaull }, + { 0x3fea4dd2f1a9fbe8ull, 0x3ffc492263be7ad0ull }, + { 0x3fea5604189374bdull, 0x3ffc4e27be37a25dull }, + { 0x3fea5e353f7ced92ull, 0x3ffc532dfcd99d1eull }, + { 0x3fea666666666667ull, 0x3ffc58351fcceb10ull }, + { 0x3fea6e978d4fdf3cull, 0x3ffc5d3d273a135full }, + { 0x3fea76c8b4395811ull, 0x3ffc62461349a46bull }, + { 0x3fea7ef9db22d0e6ull, 0x3ffc674fe42433c4ull }, + { 0x3fea872b020c49bbull, 0x3ffc6c5a99f25e31ull }, + { 0x3fea8f5c28f5c290ull, 0x3ffc716634dcc7adull }, + { 0x3fea978d4fdf3b65ull, 0x3ffc7672b50c1b69ull }, + { 0x3fea9fbe76c8b43aull, 0x3ffc7b801aa90bd0ull }, + { 0x3feaa7ef9db22d0eull, 0x3ffc808e65dc5285ull }, + { 0x3feab020c49ba5e3ull, 0x3ffc859d96ceb067ull }, + { 0x3feab851eb851eb8ull, 0x3ffc8aadada8ed8full }, + { 0x3feac083126e978dull, 0x3ffc8fbeaa93d955ull }, + { 0x3feac8b439581062ull, 0x3ffc94d08db84a4eull }, + { 0x3fead0e560418937ull, 0x3ffc99e3573f1e4full }, + { 0x3fead916872b020cull, 0x3ffc9ef707513a70ull }, + { 0x3feae147ae147ae1ull, 0x3ffca40b9e178b09ull }, + { 0x3feae978d4fdf3b6ull, 0x3ffca9211bbb03b5ull }, + { 0x3feaf1a9fbe76c8bull, 0x3ffcae3780649f58ull }, + { 0x3feaf9db22d0e560ull, 0x3ffcb34ecc3d6017ull }, + { 0x3feb020c49ba5e35ull, 0x3ffcb866ff6e4f63ull }, + { 0x3feb0a3d70a3d70aull, 0x3ffcbd801a207df3ull }, + { 0x3feb126e978d4fdfull, 0x3ffcc29a1c7d03c9ull }, + { 0x3feb1a9fbe76c8b4ull, 0x3ffcc7b506ad0032ull }, + { 0x3feb22d0e5604189ull, 0x3ffcccd0d8d999c9ull }, + { 0x3feb2b020c49ba5eull, 0x3ffcd1ed932bfe74ull }, + { 0x3feb333333333333ull, 0x3ffcd70b35cd636cull }, + { 0x3feb3b645a1cac08ull, 0x3ffcdc29c0e70539ull }, + { 0x3feb4395810624ddull, 0x3ffce14934a227b3ull }, + { 0x3feb4bc6a7ef9db2ull, 0x3ffce66991281607ull }, + { 0x3feb53f7ced91687ull, 0x3ffceb8ad6a222b8ull }, + { 0x3feb5c28f5c28f5cull, 0x3ffcf0ad0539a79bull }, + { 0x3feb645a1cac0831ull, 0x3ffcf5d01d1805dfull }, + { 0x3feb6c8b43958106ull, 0x3ffcfaf41e66a60aull }, + { 0x3feb74bc6a7ef9dbull, 0x3ffd0019094ef7fdull }, + { 0x3feb7ced916872b0ull, 0x3ffd053eddfa72f3ull }, + { 0x3feb851eb851eb85ull, 0x3ffd0a659c929582ull }, + { 0x3feb8d4fdf3b645aull, 0x3ffd0f8d4540e5a0ull }, + { 0x3feb95810624dd2full, 0x3ffd14b5d82ef0a0ull }, + { 0x3feb9db22d0e5604ull, 0x3ffd19df55864b38ull }, + { 0x3feba5e353f7ced9ull, 0x3ffd1f09bd70917bull }, + { 0x3febae147ae147aeull, 0x3ffd2435101766e3ull }, + { 0x3febb645a1cac083ull, 0x3ffd29614da4764dull }, + { 0x3febbe76c8b43958ull, 0x3ffd2e8e764171f9ull }, + { 0x3febc6a7ef9db22dull, 0x3ffd33bc8a181391ull }, + { 0x3febced916872b02ull, 0x3ffd38eb89521c25ull }, + { 0x3febd70a3d70a3d7ull, 0x3ffd3e1b74195430ull }, + { 0x3febdf3b645a1cacull, 0x3ffd434c4a978b97ull }, + { 0x3febe76c8b439581ull, 0x3ffd487e0cf699aaull }, + { 0x3febef9db22d0e56ull, 0x3ffd4db0bb605d28ull }, + { 0x3febf7ced916872bull, 0x3ffd52e455febc3dull }, + { 0x3fec000000000000ull, 0x3ffd5818dcfba487ull }, + { 0x3fec083126e978d5ull, 0x3ffd5d4e50810b13ull }, + { 0x3fec10624dd2f1aaull, 0x3ffd6284b0b8ec63ull }, + { 0x3fec189374bc6a7full, 0x3ffd67bbfdcd4c6aull }, + { 0x3fec20c49ba5e354ull, 0x3ffd6cf437e83693ull }, + { 0x3fec28f5c28f5c29ull, 0x3ffd722d5f33bdbfull }, + { 0x3fec3126e978d4feull, 0x3ffd776773d9fc46ull }, + { 0x3fec395810624dd3ull, 0x3ffd7ca2760513f9ull }, + { 0x3fec4189374bc6a8ull, 0x3ffd81de65df2e25ull }, + { 0x3fec49ba5e353f7dull, 0x3ffd871b43927b92ull }, + { 0x3fec51eb851eb852ull, 0x3ffd8c590f493485ull }, + { 0x3fec5a1cac083127ull, 0x3ffd9197c92d98c2ull }, + { 0x3fec624dd2f1a9fcull, 0x3ffd96d77169ef8dull }, + { 0x3fec6a7ef9db22d1ull, 0x3ffd9c18082887abull }, + { 0x3fec72b020c49ba6ull, 0x3ffda1598d93b763ull }, + { 0x3fec7ae147ae147bull, 0x3ffda69c01d5dc81ull }, + { 0x3fec83126e978d50ull, 0x3ffdabdf65195c55ull }, + { 0x3fec8b4395810625ull, 0x3ffdb123b788a3b7ull }, + { 0x3fec9374bc6a7efaull, 0x3ffdb668f94e2705ull }, + { 0x3fec9ba5e353f7cfull, 0x3ffdbbaf2a946228ull }, + { 0x3feca3d70a3d70a4ull, 0x3ffdc0f64b85d892ull }, + { 0x3fecac083126e979ull, 0x3ffdc63e5c4d1543ull }, + { 0x3fecb4395810624eull, 0x3ffdcb875d14aac5ull }, + { 0x3fecbc6a7ef9db23ull, 0x3ffdd0d14e073334ull }, + { 0x3fecc49ba5e353f8ull, 0x3ffdd61c2f4f503aull }, + { 0x3feccccccccccccdull, 0x3ffddb680117ab12ull }, + { 0x3fecd4fdf3b645a2ull, 0x3ffde0b4c38af48bull }, + { 0x3fecdd2f1a9fbe77ull, 0x3ffde60276d3e507ull }, + { 0x3fece5604189374cull, 0x3ffdeb511b1d3c7dull }, + { 0x3feced916872b021ull, 0x3ffdf0a0b091c27bull }, + { 0x3fecf5c28f5c28f6ull, 0x3ffdf5f1375c4628ull }, + { 0x3fecfdf3b645a1cbull, 0x3ffdfb42afa79e43ull }, + { 0x3fed0624dd2f1aa0ull, 0x3ffe0095199ea926ull }, + { 0x3fed0e5604189375ull, 0x3ffe05e8756c4cc7ull }, + { 0x3fed16872b020c4aull, 0x3ffe0b3cc33b76b9ull }, + { 0x3fed1eb851eb851full, 0x3ffe109203371c2full }, + { 0x3fed26e978d4fdf4ull, 0x3ffe15e8358a39fbull }, + { 0x3fed2f1a9fbe76c9ull, 0x3ffe1b3f5a5fd491ull }, + { 0x3fed374bc6a7ef9eull, 0x3ffe209771e2f806ull }, + { 0x3fed3f7ced916873ull, 0x3ffe25f07c3eb817ull }, + { 0x3fed47ae147ae148ull, 0x3ffe2b4a799e3023ull }, + { 0x3fed4fdf3b645a1dull, 0x3ffe30a56a2c8331ull }, + { 0x3fed5810624dd2f2ull, 0x3ffe36014e14dbf0ull }, + { 0x3fed604189374bc7ull, 0x3ffe3b5e25826cb9ull }, + { 0x3fed6872b020c49cull, 0x3ffe40bbf0a06f90ull }, + { 0x3fed70a3d70a3d71ull, 0x3ffe461aaf9a2624ull }, + { 0x3fed78d4fdf3b646ull, 0x3ffe4b7a629ad9d5ull }, + { 0x3fed810624dd2f1bull, 0x3ffe50db09cddbadull }, + { 0x3fed89374bc6a7f0ull, 0x3ffe563ca55e846bull }, + { 0x3fed916872b020c5ull, 0x3ffe5b9f3578347dull }, + { 0x3fed99999999999aull, 0x3ffe6102ba465406ull }, + { 0x3feda1cac083126full, 0x3ffe666733f452dbull }, + { 0x3feda9fbe76c8b44ull, 0x3ffe6bcca2ada889ull }, + { 0x3fedb22d0e560419ull, 0x3ffe7133069dd453ull }, + { 0x3fedba5e353f7ceeull, 0x3ffe769a5ff05d36ull }, + { 0x3fedc28f5c28f5c3ull, 0x3ffe7c02aed0d1e8ull }, + { 0x3fedcac083126e98ull, 0x3ffe816bf36ac8daull }, + { 0x3fedd2f1a9fbe76dull, 0x3ffe86d62de9e03aull }, + { 0x3feddb22d0e56042ull, 0x3ffe8c415e79bdf4ull }, + { 0x3fede353f7ced917ull, 0x3ffe91ad85460fb5ull }, + { 0x3fedeb851eb851ecull, 0x3ffe971aa27a8ae7ull }, + { 0x3fedf3b645a1cac1ull, 0x3ffe9c88b642ecbcull }, + { 0x3fedfbe76c8b4396ull, 0x3ffea1f7c0cafa23ull }, + { 0x3fee04189374bc6bull, 0x3ffea767c23e7fd5ull }, + { 0x3fee0c49ba5e3540ull, 0x3ffeacd8bac95250ull }, + { 0x3fee147ae147ae15ull, 0x3ffeb24aaa974dd8ull }, + { 0x3fee1cac083126eaull, 0x3ffeb7bd91d4567bull }, + { 0x3fee24dd2f1a9fbfull, 0x3ffebd3170ac5814ull }, + { 0x3fee2d0e56041894ull, 0x3ffec2a6474b4646ull }, + { 0x3fee353f7ced9169ull, 0x3ffec81c15dd1c83ull }, + { 0x3fee3d70a3d70a3eull, 0x3ffecd92dc8dde0cull }, + { 0x3fee45a1cac08313ull, 0x3ffed30a9b8995f1ull }, + { 0x3fee4dd2f1a9fbe8ull, 0x3ffed88352fc5715ull }, + { 0x3fee5604189374bdull, 0x3ffeddfd03123c2dull }, + { 0x3fee5e353f7ced92ull, 0x3ffee377abf767c0ull }, + { 0x3fee666666666667ull, 0x3ffee8f34dd8042full }, + { 0x3fee6e978d4fdf3cull, 0x3ffeee6fe8e043aeull }, + { 0x3fee76c8b4395811ull, 0x3ffef3ed7d3c604bull }, + { 0x3fee7ef9db22d0e6ull, 0x3ffef96c0b189befull }, + { 0x3fee872b020c49bbull, 0x3ffefeeb92a1405aull }, + { 0x3fee8f5c28f5c290ull, 0x3fff046c14029f2eull }, + { 0x3fee978d4fdf3b65ull, 0x3fff09ed8f6911e7ull }, + { 0x3fee9fbe76c8b43aull, 0x3fff0f700500f9e2ull }, + { 0x3feea7ef9db22d0full, 0x3fff14f374f6c05cull }, + { 0x3feeb020c49ba5e4ull, 0x3fff1a77df76d674ull }, + { 0x3feeb851eb851eb8ull, 0x3fff1ffd44adb52dull }, + { 0x3feec083126e978dull, 0x3fff2583a4c7dd6full }, + { 0x3feec8b439581062ull, 0x3fff2b0afff1d809ull }, + { 0x3feed0e560418937ull, 0x3fff3093565835b0ull }, + { 0x3feed916872b020cull, 0x3fff361ca8278f03ull }, + { 0x3feee147ae147ae1ull, 0x3fff3ba6f58c848dull }, + { 0x3feee978d4fdf3b6ull, 0x3fff41323eb3bec1ull }, + { 0x3feef1a9fbe76c8bull, 0x3fff46be83c9ee03ull }, + { 0x3feef9db22d0e560ull, 0x3fff4c4bc4fbcaa5ull }, + { 0x3fef020c49ba5e35ull, 0x3fff51da027614e8ull }, + { 0x3fef0a3d70a3d70aull, 0x3fff57693c659500ull }, + { 0x3fef126e978d4fdfull, 0x3fff5cf972f71b13ull }, + { 0x3fef1a9fbe76c8b4ull, 0x3fff628aa6577f3cull }, + { 0x3fef22d0e5604189ull, 0x3fff681cd6b3a18aull }, + { 0x3fef2b020c49ba5eull, 0x3fff6db004386a06ull }, + { 0x3fef333333333333ull, 0x3fff73442f12c8b1ull }, + { 0x3fef3b645a1cac08ull, 0x3fff78d9576fb584ull }, + { 0x3fef4395810624ddull, 0x3fff7e6f7d7c3075ull }, + { 0x3fef4bc6a7ef9db2ull, 0x3fff8406a1654177ull }, + { 0x3fef53f7ced91687ull, 0x3fff899ec357f87bull }, + { 0x3fef5c28f5c28f5cull, 0x3fff8f37e3816d72ull }, + { 0x3fef645a1cac0831ull, 0x3fff94d2020ec04dull }, + { 0x3fef6c8b43958106ull, 0x3fff9a6d1f2d1901ull }, + { 0x3fef74bc6a7ef9dbull, 0x3fffa0093b09a786ull }, + { 0x3fef7ced916872b0ull, 0x3fffa5a655d1a3daull }, + { 0x3fef851eb851eb85ull, 0x3fffab446fb24e01ull }, + { 0x3fef8d4fdf3b645aull, 0x3fffb0e388d8ee09ull }, + { 0x3fef95810624dd2full, 0x3fffb683a172d408ull }, + { 0x3fef9db22d0e5604ull, 0x3fffbc24b9ad581full }, + { 0x3fefa5e353f7ced9ull, 0x3fffc1c6d1b5da7dull }, + { 0x3fefae147ae147aeull, 0x3fffc769e9b9c35eull }, + { 0x3fefb645a1cac083ull, 0x3fffcd0e01e6830cull }, + { 0x3fefbe76c8b43958ull, 0x3fffd2b31a6991e5ull }, + { 0x3fefc6a7ef9db22dull, 0x3fffd85933707057ull }, + { 0x3fefced916872b02ull, 0x3fffde004d28a6e4ull }, + { 0x3fefd70a3d70a3d7ull, 0x3fffe3a867bfc623ull }, + { 0x3fefdf3b645a1cacull, 0x3fffe951836366c3ull }, + { 0x3fefe76c8b439581ull, 0x3fffeefba0412989ull }, + { 0x3fefef9db22d0e56ull, 0x3ffff4a6be86b754ull }, + { 0x3feff7ced916872bull, 0x3ffffa52de61c11full }, + { 0x3ff0000000000000ull, 0x4000000000000000ull }, + { 0x3ff004189374bc6bull, 0x400002d711c79a96ull }, + { 0x3ff0083126e978d5ull, 0x400005aea49e94faull }, + { 0x3ff00c49ba5e3540ull, 0x40000886b89bd7e7ull }, + { 0x3ff010624dd2f1aaull, 0x40000b5f4dd65028ull }, + { 0x3ff0147ae147ae15ull, 0x40000e386464ee99ull }, + { 0x3ff0189374bc6a7full, 0x40001111fc5ea82aull }, + { 0x3ff01cac083126eaull, 0x400013ec15da75ddull }, + { 0x3ff020c49ba5e354ull, 0x400016c6b0ef54c9ull }, + { 0x3ff024dd2f1a9fbfull, 0x400019a1cdb44619ull }, + { 0x3ff028f5c28f5c29ull, 0x40001c7d6c404f0cull }, + { 0x3ff02d0e56041894ull, 0x40001f598caa78faull }, + { 0x3ff03126e978d4feull, 0x400022362f09d14full }, + { 0x3ff0353f7ced9169ull, 0x4000251353756991ull }, + { 0x3ff0395810624dd3ull, 0x400027f0fa04575dull }, + { 0x3ff03d70a3d70a3eull, 0x40002acf22cdb468ull }, + { 0x3ff04189374bc6a8ull, 0x40002dadcde89e83ull }, + { 0x3ff045a1cac08313ull, 0x4000308cfb6c3798ull }, + { 0x3ff049ba5e353f7dull, 0x4000336cab6fa5acull }, + { 0x3ff04dd2f1a9fbe8ull, 0x4000364cde0a12e2ull }, + { 0x3ff051eb851eb852ull, 0x4000392d9352ad75ull }, + { 0x3ff05604189374bdull, 0x40003c0ecb60a7c4ull }, + { 0x3ff05a1cac083127ull, 0x40003ef0864b3845ull }, + { 0x3ff05e353f7ced92ull, 0x400041d2c4299993ull }, + { 0x3ff0624dd2f1a9fcull, 0x400044b585130a64ull }, + { 0x3ff0666666666666ull, 0x40004798c91ecd91ull }, + { 0x3ff06a7ef9db22d1ull, 0x40004a7c90642a14ull }, + { 0x3ff06e978d4fdf3bull, 0x40004d60dafa6b07ull }, + { 0x3ff072b020c49ba6ull, 0x40005045a8f8dfa8ull }, + { 0x3ff076c8b4395810ull, 0x4000532afa76db57ull }, + { 0x3ff07ae147ae147bull, 0x40005610cf8bb59aull }, + { 0x3ff07ef9db22d0e5ull, 0x400058f7284eca18ull }, + { 0x3ff083126e978d50ull, 0x40005bde04d778a2ull }, + { 0x3ff0872b020c49baull, 0x40005ec5653d252cull }, + { 0x3ff08b4395810625ull, 0x400061ad499737d2ull }, + { 0x3ff08f5c28f5c28full, 0x40006495b1fd1cd8ull }, + { 0x3ff09374bc6a7efaull, 0x4000677e9e8644a9ull }, + { 0x3ff0978d4fdf3b64ull, 0x40006a680f4a23daull }, + { 0x3ff09ba5e353f7cfull, 0x40006d520460332bull }, + { 0x3ff09fbe76c8b439ull, 0x4000703c7ddfef85ull }, + { 0x3ff0a3d70a3d70a4ull, 0x400073277be0d9fcull }, + { 0x3ff0a7ef9db22d0eull, 0x40007612fe7a77d1ull }, + { 0x3ff0ac083126e979ull, 0x400078ff05c45273ull }, + { 0x3ff0b020c49ba5e3ull, 0x40007beb91d5f77bull }, + { 0x3ff0b4395810624eull, 0x40007ed8a2c6f8b6ull }, + { 0x3ff0b851eb851eb8ull, 0x400081c638aeec18ull }, + { 0x3ff0bc6a7ef9db23ull, 0x400084b453a56bceull }, + { 0x3ff0c083126e978dull, 0x400087a2f3c2162dull }, + { 0x3ff0c49ba5e353f8ull, 0x40008a92191c8dc2ull }, + { 0x3ff0c8b439581062ull, 0x40008d81c3cc7947ull }, + { 0x3ff0cccccccccccdull, 0x40009071f3e983adull }, + { 0x3ff0d0e560418937ull, 0x40009362a98b5c14ull }, + { 0x3ff0d4fdf3b645a2ull, 0x40009653e4c9b5d6ull }, + { 0x3ff0d916872b020cull, 0x40009945a5bc487bull }, + { 0x3ff0dd2f1a9fbe77ull, 0x40009c37ec7acfc7ull }, + { 0x3ff0e147ae147ae1ull, 0x40009f2ab91d0bb0ull }, + { 0x3ff0e5604189374cull, 0x4000a21e0bbac068ull }, + { 0x3ff0e978d4fdf3b6ull, 0x4000a511e46bb653ull }, + { 0x3ff0ed916872b021ull, 0x4000a8064347ba13ull }, + { 0x3ff0f1a9fbe76c8bull, 0x4000aafb28669c80ull }, + { 0x3ff0f5c28f5c28f6ull, 0x4000adf093e032afull }, + { 0x3ff0f9db22d0e560ull, 0x4000b0e685cc55edull }, + { 0x3ff0fdf3b645a1cbull, 0x4000b3dcfe42e3c6ull }, + { 0x3ff1020c49ba5e35ull, 0x4000b6d3fd5bbdffull }, + { 0x3ff10624dd2f1aa0ull, 0x4000b9cb832eca9dull }, + { 0x3ff10a3d70a3d70aull, 0x4000bcc38fd3f3e2ull }, + { 0x3ff10e5604189375ull, 0x4000bfbc23632850ull }, + { 0x3ff1126e978d4fdfull, 0x4000c2b53df45aa4ull }, + { 0x3ff116872b020c4aull, 0x4000c5aedf9f81e2ull }, + { 0x3ff11a9fbe76c8b4ull, 0x4000c8a9087c9949ull }, + { 0x3ff11eb851eb851full, 0x4000cba3b8a3a05full }, + { 0x3ff122d0e5604189ull, 0x4000ce9ef02c9ae7ull }, + { 0x3ff126e978d4fdf4ull, 0x4000d19aaf2f90ecull }, + { 0x3ff12b020c49ba5eull, 0x4000d496f5c48eb9ull }, + { 0x3ff12f1a9fbe76c9ull, 0x4000d793c403a4e3ull }, + { 0x3ff1333333333333ull, 0x4000da911a04e83eull }, + { 0x3ff1374bc6a7ef9eull, 0x4000dd8ef7e071ebull }, + { 0x3ff13b645a1cac08ull, 0x4000e08d5dae5f4dull }, + { 0x3ff13f7ced916873ull, 0x4000e38c4b86d211ull }, + { 0x3ff14395810624ddull, 0x4000e68bc181f02cull }, + { 0x3ff147ae147ae148ull, 0x4000e98bbfb7e3ddull }, + { 0x3ff14bc6a7ef9db2ull, 0x4000ec8c4640dbacull }, + { 0x3ff14fdf3b645a1dull, 0x4000ef8d55350a6dull }, + { 0x3ff153f7ced91687ull, 0x4000f28eecaca73full }, + { 0x3ff15810624dd2f2ull, 0x4000f5910cbfed8full }, + { 0x3ff15c28f5c28f5cull, 0x4000f893b5871d13ull }, + { 0x3ff1604189374bc7ull, 0x4000fb96e71a79d3ull }, + { 0x3ff1645a1cac0831ull, 0x4000fe9aa1924c25ull }, + { 0x3ff16872b020c49cull, 0x4001019ee506e0adull }, + { 0x3ff16c8b43958106ull, 0x400104a3b190885full }, + { 0x3ff170a3d70a3d71ull, 0x400107a907479883ull }, + { 0x3ff174bc6a7ef9dbull, 0x40010aaee6446aaeull }, + { 0x3ff178d4fdf3b646ull, 0x40010db54e9f5ccdull }, + { 0x3ff17ced916872b0ull, 0x400110bc4070d11cull }, + { 0x3ff1810624dd2f1bull, 0x400113c3bbd12e2dull }, + { 0x3ff1851eb851eb85ull, 0x400116cbc0d8dee5ull }, + { 0x3ff189374bc6a7f0ull, 0x400119d44fa05282ull }, + { 0x3ff18d4fdf3b645aull, 0x40011cdd683ffc93ull }, + { 0x3ff1916872b020c5ull, 0x40011fe70ad05505ull }, + { 0x3ff195810624dd2full, 0x400122f13769d817ull }, + { 0x3ff199999999999aull, 0x400125fbee250664ull }, + { 0x3ff19db22d0e5604ull, 0x400129072f1a64dfull }, + { 0x3ff1a1cac083126full, 0x40012c12fa627cd5ull }, + { 0x3ff1a5e353f7ced9ull, 0x40012f1f5015dbeeull }, + { 0x3ff1a9fbe76c8b44ull, 0x4001322c304d1431ull }, + { 0x3ff1ae147ae147aeull, 0x400135399b20bbfbull }, + { 0x3ff1b22d0e560419ull, 0x4001384790a96e0dull }, + { 0x3ff1b645a1cac083ull, 0x40013b5610ffc982ull }, + { 0x3ff1ba5e353f7ceeull, 0x40013e651c3c71d5ull }, + { 0x3ff1be76c8b43958ull, 0x40014174b2780ee0ull }, + { 0x3ff1c28f5c28f5c3ull, 0x40014484d3cb4ce0ull }, + { 0x3ff1c6a7ef9db22dull, 0x40014795804edc6full }, + { 0x3ff1cac083126e98ull, 0x40014aa6b81b728dull }, + { 0x3ff1ced916872b02ull, 0x40014db87b49c899ull }, + { 0x3ff1d2f1a9fbe76dull, 0x400150cac9f29c59ull }, + { 0x3ff1d70a3d70a3d7ull, 0x400153dda42eaff5ull }, + { 0x3ff1db22d0e56042ull, 0x400156f10a16c9fbull }, + { 0x3ff1df3b645a1cacull, 0x40015a04fbc3b55dull }, + { 0x3ff1e353f7ced917ull, 0x40015d19794e4178ull }, + { 0x3ff1e76c8b439581ull, 0x4001602e82cf420cull }, + { 0x3ff1eb851eb851ecull, 0x40016344185f8f43ull }, + { 0x3ff1ef9db22d0e56ull, 0x4001665a3a1805afull }, + { 0x3ff1f3b645a1cac1ull, 0x40016970e811864full }, + { 0x3ff1f7ced916872bull, 0x40016c882264f687ull }, + { 0x3ff1fbe76c8b4396ull, 0x40016f9fe92b402cull }, + { 0x3ff2000000000000ull, 0x400172b83c7d517bull }, + { 0x3ff204189374bc6bull, 0x400175d11c741d20ull }, + { 0x3ff2083126e978d5ull, 0x400178ea89289a34ull }, + { 0x3ff20c49ba5e3540ull, 0x40017c0482b3c43full }, + { 0x3ff210624dd2f1aaull, 0x40017f1f092e9b37ull }, + { 0x3ff2147ae147ae15ull, 0x4001823a1cb22384ull }, + { 0x3ff2189374bc6a7full, 0x40018555bd5765fcull }, + { 0x3ff21cac083126eaull, 0x40018871eb376feaull }, + { 0x3ff220c49ba5e354ull, 0x40018b8ea66b5309ull }, + { 0x3ff224dd2f1a9fbfull, 0x40018eabef0c2588ull }, + { 0x3ff228f5c28f5c29ull, 0x400191c9c5330209ull }, + { 0x3ff22d0e56041894ull, 0x400194e828f907a5ull }, + { 0x3ff23126e978d4feull, 0x400198071a7759e9ull }, + { 0x3ff2353f7ced9169ull, 0x40019b2699c720d8ull }, + { 0x3ff2395810624dd3ull, 0x40019e46a70188edull }, + { 0x3ff23d70a3d70a3eull, 0x4001a167423fc31cull }, + { 0x3ff24189374bc6a8ull, 0x4001a4886b9b04cdull }, + { 0x3ff245a1cac08313ull, 0x4001a7aa232c87e9ull }, + { 0x3ff249ba5e353f7dull, 0x4001aacc690d8accull }, + { 0x3ff24dd2f1a9fbe8ull, 0x4001adef3d575053ull }, + { 0x3ff251eb851eb852ull, 0x4001b112a0231fd2ull }, + { 0x3ff25604189374bdull, 0x4001b436918a451eull }, + { 0x3ff25a1cac083127ull, 0x4001b75b11a61085ull }, + { 0x3ff25e353f7ced92ull, 0x4001ba80208fd6d9ull }, + { 0x3ff2624dd2f1a9fcull, 0x4001bda5be60f165ull }, + { 0x3ff2666666666667ull, 0x4001c0cbeb32bdf9ull }, + { 0x3ff26a7ef9db22d1ull, 0x4001c3f2a71e9ee2ull }, + { 0x3ff26e978d4fdf3cull, 0x4001c719f23dfaf1ull }, + { 0x3ff272b020c49ba6ull, 0x4001ca41ccaa3d78ull }, + { 0x3ff276c8b4395810ull, 0x4001cd6a367cd64cull }, + { 0x3ff27ae147ae147bull, 0x4001d0932fcf39c7ull }, + { 0x3ff27ef9db22d0e5ull, 0x4001d3bcb8bae0c5ull }, + { 0x3ff283126e978d50ull, 0x4001d6e6d15948adull }, + { 0x3ff2872b020c49baull, 0x4001da1179c3f366ull }, + { 0x3ff28b4395810625ull, 0x4001dd3cb2146762ull }, + { 0x3ff28f5c28f5c28full, 0x4001e0687a642f99ull }, + { 0x3ff29374bc6a7efaull, 0x4001e394d2ccdb8full }, + { 0x3ff2978d4fdf3b64ull, 0x4001e6c1bb67ff4bull }, + { 0x3ff29ba5e353f7cfull, 0x4001e9ef344f3366ull }, + { 0x3ff29fbe76c8b439ull, 0x4001ed1d3d9c14feull }, + { 0x3ff2a3d70a3d70a4ull, 0x4001f04bd76845c1ull }, + { 0x3ff2a7ef9db22d0eull, 0x4001f37b01cd6be7ull }, + { 0x3ff2ac083126e979ull, 0x4001f6aabce53238ull }, + { 0x3ff2b020c49ba5e3ull, 0x4001f9db08c94808ull }, + { 0x3ff2b4395810624eull, 0x4001fd0be593613dull }, + { 0x3ff2b851eb851eb8ull, 0x4002003d535d3649ull }, + { 0x3ff2bc6a7ef9db23ull, 0x4002036f52408432ull }, + { 0x3ff2c083126e978dull, 0x400206a1e2570c8full }, + { 0x3ff2c49ba5e353f8ull, 0x400209d503ba9588ull }, + { 0x3ff2c8b439581062ull, 0x40020d08b684e9daull }, + { 0x3ff2cccccccccccdull, 0x4002103cfacfd8d7ull }, + { 0x3ff2d0e560418937ull, 0x40021371d0b53660ull }, + { 0x3ff2d4fdf3b645a2ull, 0x400216a7384edaf4ull }, + { 0x3ff2d916872b020cull, 0x400219dd31b6a3a1ull }, + { 0x3ff2dd2f1a9fbe77ull, 0x40021d13bd067212ull }, + { 0x3ff2e147ae147ae1ull, 0x4002204ada582c86ull }, + { 0x3ff2e5604189374cull, 0x4002238289c5bdd8ull }, + { 0x3ff2e978d4fdf3b6ull, 0x400226bacb69157aull }, + { 0x3ff2ed916872b021ull, 0x400229f39f5c277bull }, + { 0x3ff2f1a9fbe76c8bull, 0x40022d2d05b8ec83ull }, + { 0x3ff2f5c28f5c28f6ull, 0x40023066fe9961d8ull }, + { 0x3ff2f9db22d0e560ull, 0x400233a18a17895cull }, + { 0x3ff2fdf3b645a1cbull, 0x400236dca84d6991ull }, + { 0x3ff3020c49ba5e35ull, 0x40023a1859550d94ull }, + { 0x3ff30624dd2f1aa0ull, 0x40023d549d488524ull }, + { 0x3ff30a3d70a3d70aull, 0x400240917441e49full }, + { 0x3ff30e5604189375ull, 0x400243cede5b4506ull }, + { 0x3ff3126e978d4fdfull, 0x4002470cdbaec3f9ull }, + { 0x3ff316872b020c4aull, 0x40024a4b6c5683beull }, + { 0x3ff31a9fbe76c8b4ull, 0x40024d8a906cab3cull }, + { 0x3ff31eb851eb851full, 0x400250ca480b6600ull }, + { 0x3ff322d0e5604189ull, 0x4002540a934ce43bull }, + { 0x3ff326e978d4fdf4ull, 0x4002574b724b5ac5ull }, + { 0x3ff32b020c49ba5eull, 0x40025a8ce521031cull }, + { 0x3ff32f1a9fbe76c9ull, 0x40025dceebe81b68ull }, + { 0x3ff3333333333333ull, 0x4002611186bae674ull }, + { 0x3ff3374bc6a7ef9eull, 0x40026454b5b3abbcull }, + { 0x3ff33b645a1cac08ull, 0x4002679878ecb760ull }, + { 0x3ff33f7ced916873ull, 0x40026adcd0805a2eull }, + { 0x3ff34395810624ddull, 0x40026e21bc88e99eull }, + { 0x3ff347ae147ae148ull, 0x400271673d20bfd8ull }, + { 0x3ff34bc6a7ef9db2ull, 0x400274ad52623bacull }, + { 0x3ff34fdf3b645a1dull, 0x400277f3fc67c09full }, + { 0x3ff353f7ced91687ull, 0x40027b3b3b4bb6deull }, + { 0x3ff35810624dd2f2ull, 0x40027e830f288b4eull }, + { 0x3ff35c28f5c28f5cull, 0x400281cb7818af7cull }, + { 0x3ff3604189374bc7ull, 0x40028514763699afull }, + { 0x3ff3645a1cac0831ull, 0x4002885e099cc4d9ull }, + { 0x3ff36872b020c49cull, 0x40028ba83265b0a6ull }, + { 0x3ff36c8b43958106ull, 0x40028ef2f0abe171ull }, + { 0x3ff370a3d70a3d71ull, 0x4002923e4489e04eull }, + { 0x3ff374bc6a7ef9dbull, 0x4002958a2e1a3b03ull }, + { 0x3ff378d4fdf3b646ull, 0x400298d6ad778410ull }, + { 0x3ff37ced916872b0ull, 0x40029c23c2bc52a9ull }, + { 0x3ff3810624dd2f1bull, 0x40029f716e0342bfull }, + { 0x3ff3851eb851eb85ull, 0x4002a2bfaf66f4f7ull }, + { 0x3ff389374bc6a7f0ull, 0x4002a60e87020eb5ull }, + { 0x3ff38d4fdf3b645aull, 0x4002a95df4ef3a12ull }, + { 0x3ff3916872b020c5ull, 0x4002acadf94925eaull }, + { 0x3ff395810624dd2full, 0x4002affe942a85ceull }, + { 0x3ff399999999999aull, 0x4002b34fc5ae1213ull }, + { 0x3ff39db22d0e5604ull, 0x4002b6a18dee87c7ull }, + { 0x3ff3a1cac083126full, 0x4002b9f3ed06a8baull }, + { 0x3ff3a5e353f7ced9ull, 0x4002bd46e3113b7bull }, + { 0x3ff3a9fbe76c8b44ull, 0x4002c09a70290b5aull }, + { 0x3ff3ae147ae147aeull, 0x4002c3ee9468e868ull }, + { 0x3ff3b22d0e560419ull, 0x4002c7434feba779ull }, + { 0x3ff3b645a1cac083ull, 0x4002ca98a2cc2224ull }, + { 0x3ff3ba5e353f7ceeull, 0x4002cdee8d2536c5ull }, + { 0x3ff3be76c8b43958ull, 0x4002d1450f11c87aull }, + { 0x3ff3c28f5c28f5c3ull, 0x4002d49c28acbf2bull }, + { 0x3ff3c6a7ef9db22dull, 0x4002d7f3da110784ull }, + { 0x3ff3cac083126e98ull, 0x4002db4c235992f9ull }, + { 0x3ff3ced916872b02ull, 0x4002dea504a157c6ull }, + { 0x3ff3d2f1a9fbe76dull, 0x4002e1fe7e0350f2ull }, + { 0x3ff3d70a3d70a3d7ull, 0x4002e5588f9a7e4bull }, + { 0x3ff3db22d0e56042ull, 0x4002e8b33981e46eull }, + { 0x3ff3df3b645a1cacull, 0x4002ec0e7bd48cbeull }, + { 0x3ff3e353f7ced917ull, 0x4002ef6a56ad8571ull }, + { 0x3ff3e76c8b439581ull, 0x4002f2c6ca27e185ull }, + { 0x3ff3eb851eb851ecull, 0x4002f623d65eb8caull }, + { 0x3ff3ef9db22d0e56ull, 0x4002f9817b6d27ddull }, + { 0x3ff3f3b645a1cac1ull, 0x4002fcdfb96e502eull }, + { 0x3ff3f7ced916872bull, 0x4003003e907d57f9ull }, + { 0x3ff3fbe76c8b4396ull, 0x4003039e00b56a50ull }, + { 0x3ff4000000000000ull, 0x400306fe0a31b715ull }, + { 0x3ff404189374bc6bull, 0x40030a5ead0d7300ull }, + { 0x3ff4083126e978d5ull, 0x40030dbfe963d79aull }, + { 0x3ff40c49ba5e3540ull, 0x40031121bf502344ull }, + { 0x3ff410624dd2f1aaull, 0x400314842eed9933ull }, + { 0x3ff4147ae147ae15ull, 0x400317e738578175ull }, + { 0x3ff4189374bc6a7full, 0x40031b4adba928edull }, + { 0x3ff41cac083126eaull, 0x40031eaf18fde159ull }, + { 0x3ff420c49ba5e354ull, 0x40032213f071014full }, + { 0x3ff424dd2f1a9fbfull, 0x40032579621de440ull }, + { 0x3ff428f5c28f5c29ull, 0x400328df6e1fea77ull }, + { 0x3ff42d0e56041894ull, 0x40032c461492791eull }, + { 0x3ff43126e978d4feull, 0x40032fad5590fa38ull }, + { 0x3ff4353f7ced9169ull, 0x400333153136dca9ull }, + { 0x3ff4395810624dd3ull, 0x4003367da79f9430ull }, + { 0x3ff43d70a3d70a3eull, 0x400339e6b8e69970ull }, + { 0x3ff44189374bc6a8ull, 0x40033d50652769e9ull }, + { 0x3ff445a1cac08313ull, 0x400340baac7d87fdull }, + { 0x3ff449ba5e353f7dull, 0x400344258f047af1ull }, + { 0x3ff44dd2f1a9fbe8ull, 0x400347910cd7ceecull }, + { 0x3ff451eb851eb852ull, 0x40034afd261314f8ull }, + { 0x3ff45604189374bdull, 0x40034e69dad1e306ull }, + { 0x3ff45a1cac083127ull, 0x400351d72b2fd3ebull }, + { 0x3ff45e353f7ced92ull, 0x4003554517488763ull }, + { 0x3ff4624dd2f1a9fcull, 0x400358b39f37a20full }, + { 0x3ff4666666666667ull, 0x40035c22c318cd7dull }, + { 0x3ff46a7ef9db22d1ull, 0x40035f928307b81full }, + { 0x3ff46e978d4fdf3cull, 0x40036302df201555ull }, + { 0x3ff472b020c49ba6ull, 0x40036673d77d9d65ull }, + { 0x3ff476c8b4395811ull, 0x400369e56c3c0d86ull }, + { 0x3ff47ae147ae147bull, 0x40036d579d7727d8ull }, + { 0x3ff47ef9db22d0e5ull, 0x400370ca6b4ab368ull }, + { 0x3ff483126e978d50ull, 0x4003743dd5d27c36ull }, + { 0x3ff4872b020c49baull, 0x400377b1dd2a532aull }, + { 0x3ff48b4395810625ull, 0x40037b26816e0e23ull }, + { 0x3ff48f5c28f5c28full, 0x40037e9bc2b987ebull }, + { 0x3ff49374bc6a7efaull, 0x40038211a128a042ull }, + { 0x3ff4978d4fdf3b64ull, 0x400385881cd73bd9ull }, + { 0x3ff49ba5e353f7cfull, 0x400388ff35e14454ull }, + { 0x3ff49fbe76c8b439ull, 0x40038c76ec62a84cull }, + { 0x3ff4a3d70a3d70a4ull, 0x40038fef40775b50ull }, + { 0x3ff4a7ef9db22d0eull, 0x40039368323b55e3ull }, + { 0x3ff4ac083126e979ull, 0x400396e1c1ca9582ull }, + { 0x3ff4b020c49ba5e3ull, 0x40039a5bef411ca0ull }, + { 0x3ff4b4395810624eull, 0x40039dd6babaf2a9ull }, + { 0x3ff4b851eb851eb8ull, 0x4003a15224542403ull }, + { 0x3ff4bc6a7ef9db23ull, 0x4003a4ce2c28c20full }, + { 0x3ff4c083126e978dull, 0x4003a84ad254e327ull }, + { 0x3ff4c49ba5e353f8ull, 0x4003abc816f4a2a7ull }, + { 0x3ff4c8b439581062ull, 0x4003af45fa2420e0ull }, + { 0x3ff4cccccccccccdull, 0x4003b2c47bff8329ull }, + { 0x3ff4d0e560418937ull, 0x4003b6439ca2f3d3ull }, + { 0x3ff4d4fdf3b645a2ull, 0x4003b9c35c2aa231ull }, + { 0x3ff4d916872b020cull, 0x4003bd43bab2c296ull }, + { 0x3ff4dd2f1a9fbe77ull, 0x4003c0c4b8578e58ull }, + { 0x3ff4e147ae147ae1ull, 0x4003c446553543ccull }, + { 0x3ff4e5604189374cull, 0x4003c7c891682650ull }, + { 0x3ff4e978d4fdf3b6ull, 0x4003cb4b6d0c7e41ull }, + { 0x3ff4ed916872b021ull, 0x4003cecee83e9904ull }, + { 0x3ff4f1a9fbe76c8bull, 0x4003d253031ac904ull }, + { 0x3ff4f5c28f5c28f6ull, 0x4003d5d7bdbd65b3ull }, + { 0x3ff4f9db22d0e560ull, 0x4003d95d1842cb89ull }, + { 0x3ff4fdf3b645a1cbull, 0x4003dce312c75c09ull }, + { 0x3ff5020c49ba5e35ull, 0x4003e069ad677dbfull }, + { 0x3ff50624dd2f1aa0ull, 0x4003e3f0e83f9c42ull }, + { 0x3ff50a3d70a3d70aull, 0x4003e778c36c2833ull }, + { 0x3ff50e5604189375ull, 0x4003eb013f099741ull }, + { 0x3ff5126e978d4fdfull, 0x4003ee8a5b346427ull }, + { 0x3ff516872b020c4aull, 0x4003f21418090eb0ull }, + { 0x3ff51a9fbe76c8b4ull, 0x4003f59e75a41bb3ull }, + { 0x3ff51eb851eb851full, 0x4003f9297422151aull }, + { 0x3ff522d0e5604189ull, 0x4003fcb5139f89deull }, + { 0x3ff526e978d4fdf4ull, 0x4004004154390e0cull }, + { 0x3ff52b020c49ba5eull, 0x400403ce360b3abfull }, + { 0x3ff52f1a9fbe76c9ull, 0x4004075bb932ae2cull }, + { 0x3ff5333333333333ull, 0x40040ae9ddcc0b96ull }, + { 0x3ff5374bc6a7ef9eull, 0x40040e78a3f3fb5aull }, + { 0x3ff53b645a1cac08ull, 0x400412080bc72ae9ull }, + { 0x3ff53f7ced916873ull, 0x4004159815624ccdull }, + { 0x3ff54395810624ddull, 0x40041928c0e218a5ull }, + { 0x3ff547ae147ae148ull, 0x40041cba0e634b2cull }, + { 0x3ff54bc6a7ef9db2ull, 0x4004204bfe02a635ull }, + { 0x3ff54fdf3b645a1dull, 0x400423de8fdcf0b0ull }, + { 0x3ff553f7ced91687ull, 0x40042771c40ef6a5ull }, + { 0x3ff55810624dd2f2ull, 0x40042b059ab5893dull }, + { 0x3ff55c28f5c28f5cull, 0x40042e9a13ed7eb9ull }, + { 0x3ff5604189374bc7ull, 0x4004322f2fd3b280ull }, + { 0x3ff5645a1cac0831ull, 0x400435c4ee850510ull }, + { 0x3ff56872b020c49cull, 0x4004395b501e5c0dull }, + { 0x3ff56c8b43958106ull, 0x40043cf254bca23aull }, + { 0x3ff570a3d70a3d71ull, 0x40044089fc7cc77dull }, + { 0x3ff574bc6a7ef9dbull, 0x40044422477bc0ddull }, + { 0x3ff578d4fdf3b646ull, 0x400447bb35d68888ull }, + { 0x3ff57ced916872b0ull, 0x40044b54c7aa1dcdull }, + { 0x3ff5810624dd2f1bull, 0x40044eeefd138525ull }, + { 0x3ff5851eb851eb85ull, 0x40045289d62fc82aull }, + { 0x3ff589374bc6a7f0ull, 0x40045625531bf5a4ull }, + { 0x3ff58d4fdf3b645aull, 0x400459c173f5217eull }, + { 0x3ff5916872b020c5ull, 0x40045d5e38d864cfull }, + { 0x3ff595810624dd2full, 0x400460fba1e2ddd9ull }, + { 0x3ff599999999999aull, 0x40046499af31b007ull }, + { 0x3ff59db22d0e5604ull, 0x4004683860e203f1ull }, + { 0x3ff5a1cac083126full, 0x40046bd7b711075dull }, + { 0x3ff5a5e353f7ced9ull, 0x40046f77b1dbed3cull }, + { 0x3ff5a9fbe76c8b44ull, 0x40047318515fedb3ull }, + { 0x3ff5ae147ae147aeull, 0x400476b995ba4611ull }, + { 0x3ff5b22d0e560419ull, 0x40047a5b7f0838dbull }, + { 0x3ff5b645a1cac083ull, 0x40047dfe0d670dc2ull }, + { 0x3ff5ba5e353f7ceeull, 0x400481a140f411afull }, + { 0x3ff5be76c8b43958ull, 0x4004854519cc96baull }, + { 0x3ff5c28f5c28f5c3ull, 0x400488e9980df434ull }, + { 0x3ff5c6a7ef9db22dull, 0x40048c8ebbd5869eull }, + { 0x3ff5cac083126e98ull, 0x400490348540afb4ull }, + { 0x3ff5ced916872b02ull, 0x400493daf46cd666ull }, + { 0x3ff5d2f1a9fbe76dull, 0x40049782097766deull }, + { 0x3ff5d70a3d70a3d7ull, 0x40049b29c47dd27dull }, + { 0x3ff5db22d0e56042ull, 0x40049ed2259d8fe1ull }, + { 0x3ff5df3b645a1cacull, 0x4004a27b2cf41adfull }, + { 0x3ff5e353f7ced917ull, 0x4004a624da9ef48bull }, + { 0x3ff5e76c8b439581ull, 0x4004a9cf2ebba334ull }, + { 0x3ff5eb851eb851ecull, 0x4004ad7a2967b268ull }, + { 0x3ff5ef9db22d0e56ull, 0x4004b125cac0b2f2ull }, + { 0x3ff5f3b645a1cac1ull, 0x4004b4d212e43adeull }, + { 0x3ff5f7ced916872bull, 0x4004b87f01efe576ull }, + { 0x3ff5fbe76c8b4396ull, 0x4004bc2c9801534aull }, + { 0x3ff6000000000000ull, 0x4004bfdad5362a27ull }, + { 0x3ff604189374bc6bull, 0x4004c389b9ac1521ull }, + { 0x3ff6083126e978d5ull, 0x4004c7394580c48eull }, + { 0x3ff60c49ba5e3540ull, 0x4004cae978d1ee0bull }, + { 0x3ff610624dd2f1aaull, 0x4004ce9a53bd4c79ull }, + { 0x3ff6147ae147ae15ull, 0x4004d24bd660a002ull }, + { 0x3ff6189374bc6a7full, 0x4004d5fe00d9ae16ull }, + { 0x3ff61cac083126eaull, 0x4004d9b0d3464170ull }, + { 0x3ff620c49ba5e354ull, 0x4004dd644dc42a11ull }, + { 0x3ff624dd2f1a9fbfull, 0x4004e11870713d4bull }, + { 0x3ff628f5c28f5c29ull, 0x4004e4cd3b6b55b5ull }, + { 0x3ff62d0e56041894ull, 0x4004e882aed05338ull }, + { 0x3ff63126e978d4feull, 0x4004ec38cabe1b05ull }, + { 0x3ff6353f7ced9169ull, 0x4004efef8f5297a3ull }, + { 0x3ff6395810624dd3ull, 0x4004f3a6fcabb8e0ull }, + { 0x3ff63d70a3d70a3eull, 0x4004f75f12e773e1ull }, + { 0x3ff64189374bc6a8ull, 0x4004fb17d223c319ull }, + { 0x3ff645a1cac08313ull, 0x4004fed13a7ea64full }, + { 0x3ff649ba5e353f7dull, 0x4005028b4c16229aull }, + { 0x3ff64dd2f1a9fbe8ull, 0x400506460708426aull }, + { 0x3ff651eb851eb852ull, 0x40050a016b731581ull }, + { 0x3ff65604189374bdull, 0x40050dbd7974b0f7ull }, + { 0x3ff65a1cac083127ull, 0x4005117a312b2f3bull }, + { 0x3ff65e353f7ced92ull, 0x4005153792b4b017ull }, + { 0x3ff6624dd2f1a9fcull, 0x400518f59e2f58a9ull }, + { 0x3ff6666666666667ull, 0x40051cb453b9536dull }, + { 0x3ff66a7ef9db22d1ull, 0x40052073b370d036ull }, + { 0x3ff66e978d4fdf3cull, 0x40052433bd740438ull }, + { 0x3ff672b020c49ba6ull, 0x400527f471e129feull }, + { 0x3ff676c8b4395811ull, 0x40052bb5d0d68175ull }, + { 0x3ff67ae147ae147bull, 0x40052f77da724fe5ull }, + { 0x3ff67ef9db22d0e6ull, 0x4005333a8ed2dff9ull }, + { 0x3ff683126e978d50ull, 0x400536fdee1681baull }, + { 0x3ff6872b020c49baull, 0x40053ac1f85b8a92ull }, + { 0x3ff68b4395810625ull, 0x40053e86adc05553ull }, + { 0x3ff68f5c28f5c28full, 0x4005424c0e634229ull }, + { 0x3ff69374bc6a7efaull, 0x400546121a62b6aeull }, + { 0x3ff6978d4fdf3b64ull, 0x400549d8d1dd1dd9ull }, + { 0x3ff69ba5e353f7cfull, 0x40054da034f0e80eull }, + { 0x3ff69fbe76c8b439ull, 0x4005516843bc8b13ull }, + { 0x3ff6a3d70a3d70a4ull, 0x40055530fe5e821aull }, + { 0x3ff6a7ef9db22d0eull, 0x400558fa64f54dbaull }, + { 0x3ff6ac083126e979ull, 0x40055cc4779f73faull }, + { 0x3ff6b020c49ba5e3ull, 0x4005608f367b8046ull }, + { 0x3ff6b4395810624eull, 0x4005645aa1a8037aull }, + { 0x3ff6b851eb851eb8ull, 0x40056826b94393ddull }, + { 0x3ff6bc6a7ef9db23ull, 0x40056bf37d6ccd25ull }, + { 0x3ff6c083126e978dull, 0x40056fc0ee425075ull }, + { 0x3ff6c49ba5e353f8ull, 0x4005738f0be2c463ull }, + { 0x3ff6c8b439581062ull, 0x4005775dd66cd4f3ull }, + { 0x3ff6cccccccccccdull, 0x40057b2d4dff339dull }, + { 0x3ff6d0e560418937ull, 0x40057efd72b89749ull }, + { 0x3ff6d4fdf3b645a2ull, 0x400582ce44b7bc56ull }, + { 0x3ff6d916872b020cull, 0x4005869fc41b6494ull }, + { 0x3ff6dd2f1a9fbe77ull, 0x40058a71f102574eull }, + { 0x3ff6e147ae147ae1ull, 0x40058e44cb8b613full }, + { 0x3ff6e5604189374cull, 0x4005921853d5549full }, + { 0x3ff6e978d4fdf3b6ull, 0x400595ec89ff091bull }, + { 0x3ff6ed916872b021ull, 0x400599c16e275bddull }, + { 0x3ff6f1a9fbe76c8bull, 0x40059d97006d2f86ull }, + { 0x3ff6f5c28f5c28f6ull, 0x4005a16d40ef6c34ull }, + { 0x3ff6f9db22d0e560ull, 0x4005a5442fccff82ull }, + { 0x3ff6fdf3b645a1cbull, 0x4005a91bcd24dc89ull }, + { 0x3ff7020c49ba5e35ull, 0x4005acf41915fbdeull }, + { 0x3ff70624dd2f1aa0ull, 0x4005b0cd13bf5b97ull }, + { 0x3ff70a3d70a3d70aull, 0x4005b4a6bd3fff49ull }, + { 0x3ff70e5604189375ull, 0x4005b88115b6f00eull }, + { 0x3ff7126e978d4fdfull, 0x4005bc5c1d433c7dull }, + { 0x3ff716872b020c4aull, 0x4005c037d403f8b6ull }, + { 0x3ff71a9fbe76c8b4ull, 0x4005c4143a183e57ull }, + { 0x3ff71eb851eb851full, 0x4005c7f14f9f2c89ull }, + { 0x3ff722d0e5604189ull, 0x4005cbcf14b7e7f6ull }, + { 0x3ff726e978d4fdf4ull, 0x4005cfad89819ad6ull }, + { 0x3ff72b020c49ba5eull, 0x4005d38cae1b74e2ull }, + { 0x3ff72f1a9fbe76c9ull, 0x4005d76c82a4ab60ull }, + { 0x3ff7333333333333ull, 0x4005db4d073c7920ull }, + { 0x3ff7374bc6a7ef9eull, 0x4005df2e3c021e7dull }, + { 0x3ff73b645a1cac08ull, 0x4005e3102114e15cull }, + { 0x3ff73f7ced916873ull, 0x4005e6f2b6940d33ull }, + { 0x3ff74395810624ddull, 0x4005ead5fc9ef302ull }, + { 0x3ff747ae147ae148ull, 0x4005eeb9f354e95cull }, + { 0x3ff74bc6a7ef9db2ull, 0x4005f29e9ad54c5full }, + { 0x3ff74fdf3b645a1dull, 0x4005f683f33f7dc1ull }, + { 0x3ff753f7ced91687ull, 0x4005fa69fcb2e4c3ull }, + { 0x3ff75810624dd2f2ull, 0x4005fe50b74eee3eull }, + { 0x3ff75c28f5c28f5cull, 0x4006023823330c9cull }, + { 0x3ff7604189374bc7ull, 0x40060620407eb7dfull }, + { 0x3ff7645a1cac0831ull, 0x40060a090f516d9bull }, + { 0x3ff76872b020c49cull, 0x40060df28fcab0ffull }, + { 0x3ff76c8b43958106ull, 0x400611dcc20a0aceull }, + { 0x3ff770a3d70a3d71ull, 0x400615c7a62f0969ull }, + { 0x3ff774bc6a7ef9dbull, 0x400619b33c5940c5ull }, + { 0x3ff778d4fdf3b646ull, 0x40061d9f84a84a78ull }, + { 0x3ff77ced916872b0ull, 0x4006218c7f3bc5afull }, + { 0x3ff7810624dd2f1bull, 0x4006257a2c335738ull }, + { 0x3ff7851eb851eb85ull, 0x400629688baea97aull }, + { 0x3ff789374bc6a7f0ull, 0x40062d579dcd6c81ull }, + { 0x3ff78d4fdf3b645aull, 0x4006314762af55f4ull }, + { 0x3ff7916872b020c5ull, 0x40063537da74211eull }, + { 0x3ff795810624dd2full, 0x40063929053b8ee8ull }, + { 0x3ff799999999999aull, 0x40063d1ae32565e5ull }, + { 0x3ff79db22d0e5604ull, 0x4006410d74517243ull }, + { 0x3ff7a1cac083126full, 0x40064500b8df85ddull }, + { 0x3ff7a5e353f7ced9ull, 0x400648f4b0ef782full }, + { 0x3ff7a9fbe76c8b44ull, 0x40064ce95ca1265eull }, + { 0x3ff7ae147ae147aeull, 0x400650debc147335ull }, + { 0x3ff7b22d0e560419ull, 0x400654d4cf69472cull }, + { 0x3ff7b645a1cac083ull, 0x400658cb96bf905full }, + { 0x3ff7ba5e353f7ceeull, 0x40065cc31237429cull }, + { 0x3ff7be76c8b43958ull, 0x400660bb41f05756ull }, + { 0x3ff7c28f5c28f5c3ull, 0x400664b4260acdb2ull }, + { 0x3ff7c6a7ef9db22dull, 0x400668adbea6aa80ull }, + { 0x3ff7cac083126e98ull, 0x40066ca80be3f842ull }, + { 0x3ff7ced916872b02ull, 0x400670a30de2c725ull }, + { 0x3ff7d2f1a9fbe76dull, 0x4006749ec4c32d0full }, + { 0x3ff7d70a3d70a3d7ull, 0x4006789b30a5458full }, + { 0x3ff7db22d0e56042ull, 0x40067c9851a931edull }, + { 0x3ff7df3b645a1cacull, 0x4006809627ef1923ull }, + { 0x3ff7e353f7ced917ull, 0x40068494b39727e2ull }, + { 0x3ff7e76c8b439581ull, 0x40068893f4c1908cull }, + { 0x3ff7eb851eb851ecull, 0x40068c93eb8e8b41ull }, + { 0x3ff7ef9db22d0e56ull, 0x40069094981e55d3ull }, + { 0x3ff7f3b645a1cac1ull, 0x40069495fa9133d2ull }, + { 0x3ff7f7ced916872bull, 0x4006989813076e84ull }, + { 0x3ff7fbe76c8b4396ull, 0x40069c9ae1a154eeull }, + { 0x3ff8000000000000ull, 0x4006a09e667f3bcdull }, + { 0x3ff804189374bc6bull, 0x4006a4a2a1c17d9full }, + { 0x3ff8083126e978d5ull, 0x4006a8a793887a9dull }, + { 0x3ff80c49ba5e3540ull, 0x4006acad3bf498c2ull }, + { 0x3ff810624dd2f1aaull, 0x4006b0b39b2643c8ull }, + { 0x3ff8147ae147ae15ull, 0x4006b4bab13ded2bull }, + { 0x3ff8189374bc6a7full, 0x4006b8c27e5c0c26ull }, + { 0x3ff81cac083126eaull, 0x4006bccb02a11dbdull }, + { 0x3ff820c49ba5e354ull, 0x4006c0d43e2da4b2ull }, + { 0x3ff824dd2f1a9fbfull, 0x4006c4de31222993ull }, + { 0x3ff828f5c28f5c29ull, 0x4006c8e8db9f3aaeull }, + { 0x3ff82d0e56041894ull, 0x4006ccf43dc56c1eull }, + { 0x3ff83126e978d4feull, 0x4006d10057b557c1ull }, + { 0x3ff8353f7ced9169ull, 0x4006d50d298f9d43ull }, + { 0x3ff8395810624dd3ull, 0x4006d91ab374e218ull }, + { 0x3ff83d70a3d70a3eull, 0x4006dd28f585d182ull }, + { 0x3ff84189374bc6a8ull, 0x4006e137efe31c8aull }, + { 0x3ff845a1cac08313ull, 0x4006e547a2ad7a0eull }, + { 0x3ff849ba5e353f7dull, 0x4006e9580e05a6b5ull }, + { 0x3ff84dd2f1a9fbe8ull, 0x4006ed69320c64f8ull }, + { 0x3ff851eb851eb852ull, 0x4006f17b0ee27d1full }, + { 0x3ff85604189374bdull, 0x4006f58da4a8bd48ull }, + { 0x3ff85a1cac083127ull, 0x4006f9a0f37ff95dull }, + { 0x3ff85e353f7ced92ull, 0x4006fdb4fb890b22ull }, + { 0x3ff8624dd2f1a9fcull, 0x400701c9bce4d22cull }, + { 0x3ff8666666666667ull, 0x400705df37b433e7ull }, + { 0x3ff86a7ef9db22d1ull, 0x400709f56c181b96ull }, + { 0x3ff86e978d4fdf3cull, 0x40070e0c5a317a54ull }, + { 0x3ff872b020c49ba6ull, 0x4007122402214714ull }, + { 0x3ff876c8b4395811ull, 0x4007163c64087ea5ull }, + { 0x3ff87ae147ae147bull, 0x40071a55800823aeull }, + { 0x3ff87ef9db22d0e6ull, 0x40071e6f56413eb5ull }, + { 0x3ff883126e978d50ull, 0x40072289e6d4de1aull }, + { 0x3ff8872b020c49bbull, 0x400726a531e41620ull }, + { 0x3ff88b4395810625ull, 0x40072ac1379000e3ull }, + { 0x3ff88f5c28f5c290ull, 0x40072eddf7f9be65ull }, + { 0x3ff89374bc6a7efaull, 0x400732fb73427484ull }, + { 0x3ff8978d4fdf3b64ull, 0x40073719a98b4f04ull }, + { 0x3ff89ba5e353f7cfull, 0x40073b389af57f8dull }, + { 0x3ff89fbe76c8b439ull, 0x40073f5847a23da7ull }, + { 0x3ff8a3d70a3d70a4ull, 0x40074378afb2c6c5ull }, + { 0x3ff8a7ef9db22d0eull, 0x40074799d3485e3aull }, + { 0x3ff8ac083126e979ull, 0x40074bbbb2844d47ull }, + { 0x3ff8b020c49ba5e3ull, 0x40074fde4d87e311ull }, + { 0x3ff8b4395810624eull, 0x40075401a47474aaull }, + { 0x3ff8b851eb851eb8ull, 0x40075825b76b5d0aull }, + { 0x3ff8bc6a7ef9db23ull, 0x40075c4a868dfd1cull }, + { 0x3ff8c083126e978dull, 0x4007607011fdbbafull }, + { 0x3ff8c49ba5e353f8ull, 0x4007649659dc0588ull }, + { 0x3ff8c8b439581062ull, 0x400768bd5e4a4d55ull }, + { 0x3ff8cccccccccccdull, 0x40076ce51f6a0bb8ull }, + { 0x3ff8d0e560418937ull, 0x4007710d9d5cbf40ull }, + { 0x3ff8d4fdf3b645a2ull, 0x40077536d843ec73ull }, + { 0x3ff8d916872b020cull, 0x40077960d0411dc4ull }, + { 0x3ff8dd2f1a9fbe77ull, 0x40077d8b8575e3a0ull }, + { 0x3ff8e147ae147ae1ull, 0x400781b6f803d465ull }, + { 0x3ff8e5604189374cull, 0x400785e3280c8c6bull }, + { 0x3ff8e978d4fdf3b6ull, 0x40078a1015b1adfdull }, + { 0x3ff8ed916872b021ull, 0x40078e3dc114e163ull }, + { 0x3ff8f1a9fbe76c8bull, 0x4007926c2a57d4d9ull }, + { 0x3ff8f5c28f5c28f6ull, 0x4007969b519c3c9cull }, + { 0x3ff8f9db22d0e560ull, 0x40079acb3703d2dfull }, + { 0x3ff8fdf3b645a1cbull, 0x40079efbdab057d6ull }, + { 0x3ff9020c49ba5e35ull, 0x4007a32d3cc391aeull }, + { 0x3ff90624dd2f1aa0ull, 0x4007a75f5d5f4c97ull }, + { 0x3ff90a3d70a3d70aull, 0x4007ab923ca55abeull }, + { 0x3ff90e5604189375ull, 0x4007afc5dab79454ull }, + { 0x3ff9126e978d4fdfull, 0x4007b3fa37b7d787ull }, + { 0x3ff916872b020c4aull, 0x4007b82f53c8088eull }, + { 0x3ff91a9fbe76c8b4ull, 0x4007bc652f0a119full }, + { 0x3ff91eb851eb851full, 0x4007c09bc99fe2f8ull }, + { 0x3ff922d0e5604189ull, 0x4007c4d323ab72dcull }, + { 0x3ff926e978d4fdf4ull, 0x4007c90b3d4ebd97ull }, + { 0x3ff92b020c49ba5eull, 0x4007cd4416abc57bull }, + { 0x3ff92f1a9fbe76c9ull, 0x4007d17dafe492e5ull }, + { 0x3ff9333333333333ull, 0x4007d5b8091b343cull }, + { 0x3ff9374bc6a7ef9eull, 0x4007d9f32271bdf3ull }, + { 0x3ff93b645a1cac08ull, 0x4007de2efc0a4a87ull }, + { 0x3ff93f7ced916873ull, 0x4007e26b9606fa89ull }, + { 0x3ff94395810624ddull, 0x4007e6a8f089f492ull }, + { 0x3ff947ae147ae148ull, 0x4007eae70bb5654full }, + { 0x3ff94bc6a7ef9db2ull, 0x4007ef25e7ab7f7bull }, + { 0x3ff94fdf3b645a1dull, 0x4007f365848e7be7ull }, + { 0x3ff953f7ced91687ull, 0x4007f7a5e2809973ull }, + { 0x3ff95810624dd2f2ull, 0x4007fbe701a41d16ull }, + { 0x3ff95c28f5c28f5cull, 0x40080028e21b51daull }, + { 0x3ff9604189374bc7ull, 0x4008046b840888e2ull }, + { 0x3ff9645a1cac0831ull, 0x400808aee78e1966ull }, + { 0x3ff96872b020c49cull, 0x40080cf30cce60b9ull }, + { 0x3ff96c8b43958106ull, 0x40081137f3ebc243ull }, + { 0x3ff970a3d70a3d71ull, 0x4008157d9d08a78eull }, + { 0x3ff974bc6a7ef9dbull, 0x400819c408478038ull }, + { 0x3ff978d4fdf3b646ull, 0x40081e0b35cac201ull }, + { 0x3ff97ced916872b0ull, 0x4008225325b4e8c5ull }, + { 0x3ff9810624dd2f1bull, 0x4008269bd828767full }, + { 0x3ff9851eb851eb85ull, 0x40082ae54d47f34bull }, + { 0x3ff989374bc6a7f0ull, 0x40082f2f8535ed65ull }, + { 0x3ff98d4fdf3b645aull, 0x4008337a8014f92cull }, + { 0x3ff9916872b020c5ull, 0x400837c63e07b122ull }, + { 0x3ff995810624dd2full, 0x40083c12bf30b5eeull }, + { 0x3ff999999999999aull, 0x4008406003b2ae5dull }, + { 0x3ff99db22d0e5604ull, 0x400844ae0bb0475full }, + { 0x3ff9a1cac083126full, 0x400848fcd74c3413ull }, + { 0x3ff9a5e353f7ced9ull, 0x40084d4c66a92db7ull }, + { 0x3ff9a9fbe76c8b44ull, 0x4008519cb9e9f3bdull }, + { 0x3ff9ae147ae147aeull, 0x400855edd1314bbbull }, + { 0x3ff9b22d0e560419ull, 0x40085a3faca20176ull }, + { 0x3ff9b645a1cac083ull, 0x40085e924c5ee6deull }, + { 0x3ff9ba5e353f7ceeull, 0x400862e5b08ad415ull }, + { 0x3ff9be76c8b43958ull, 0x40086739d948a769ull }, + { 0x3ff9c28f5c28f5c3ull, 0x40086b8ec6bb455bull }, + { 0x3ff9c6a7ef9db22dull, 0x40086fe479059899ull }, + { 0x3ff9cac083126e98ull, 0x4008743af04a920cull }, + { 0x3ff9ced916872b02ull, 0x400878922cad28c7ull }, + { 0x3ff9d2f1a9fbe76dull, 0x40087cea2e505a1aull }, + { 0x3ff9d70a3d70a3d7ull, 0x40088142f5572986ull }, + { 0x3ff9db22d0e56042ull, 0x4008859c81e4a0c5ull }, + { 0x3ff9df3b645a1cacull, 0x400889f6d41bcfc9ull }, + { 0x3ff9e353f7ced917ull, 0x40088e51ec1fccbeull }, + { 0x3ff9e76c8b439581ull, 0x400892adca13b407ull }, + { 0x3ff9eb851eb851ecull, 0x4008970a6e1aa848ull }, + { 0x3ff9ef9db22d0e56ull, 0x40089b67d857d25dull }, + { 0x3ff9f3b645a1cac1ull, 0x40089fc608ee6163ull }, + { 0x3ff9f7ced916872bull, 0x4008a42500018ab3ull }, + { 0x3ff9fbe76c8b4396ull, 0x4008a884bdb489e8ull }, + { 0x3ffa000000000000ull, 0x4008ace5422aa0dbull }, + { 0x3ffa04189374bc6bull, 0x4008b1468d8717adull }, + { 0x3ffa083126e978d5ull, 0x4008b5a89fed3cbcull }, + { 0x3ffa0c49ba5e3540ull, 0x4008ba0b798064aeull }, + { 0x3ffa10624dd2f1aaull, 0x4008be6f1a63ea6dull }, + { 0x3ffa147ae147ae15ull, 0x4008c2d382bb2f2cull }, + { 0x3ffa189374bc6a7full, 0x4008c738b2a99a61ull }, + { 0x3ffa1cac083126eaull, 0x4008cb9eaa5299d0ull }, + { 0x3ffa20c49ba5e354ull, 0x4008d00569d9a182ull }, + { 0x3ffa24dd2f1a9fbfull, 0x4008d46cf1622bd1ull }, + { 0x3ffa28f5c28f5c29ull, 0x4008d8d5410fb95dull }, + { 0x3ffa2d0e56041894ull, 0x4008dd3e5905d119ull }, + { 0x3ffa3126e978d4feull, 0x4008e1a839680041ull }, + { 0x3ffa353f7ced9169ull, 0x4008e612e259da64ull }, + { 0x3ffa395810624dd3ull, 0x4008ea7e53fef961ull }, + { 0x3ffa3d70a3d70a3eull, 0x4008eeea8e7afd69ull }, + { 0x3ffa4189374bc6a8ull, 0x4008f35791f18cfeull }, + { 0x3ffa45a1cac08313ull, 0x4008f7c55e8654faull }, + { 0x3ffa49ba5e353f7dull, 0x4008fc33f45d0886ull }, + { 0x3ffa4dd2f1a9fbe8ull, 0x400900a353996129ull }, + { 0x3ffa51eb851eb852ull, 0x400905137c5f1eb9ull }, + { 0x3ffa5604189374bdull, 0x400909846ed2076dull }, + { 0x3ffa5a1cac083127ull, 0x40090df62b15e7ceull }, + { 0x3ffa5e353f7ced92ull, 0x40091268b14e92c6ull }, + { 0x3ffa624dd2f1a9fcull, 0x400916dc019fe196ull }, + { 0x3ffa666666666667ull, 0x40091b501c2db3dfull }, + { 0x3ffa6a7ef9db22d1ull, 0x40091fc5011bef9full }, + { 0x3ffa6e978d4fdf3cull, 0x4009243ab08e8135ull }, + { 0x3ffa72b020c49ba6ull, 0x400928b12aa95b5dull }, + { 0x3ffa76c8b4395811ull, 0x40092d286f907738ull }, + { 0x3ffa7ae147ae147bull, 0x400931a07f67d447ull }, + { 0x3ffa7ef9db22d0e6ull, 0x400936195a537872ull }, + { 0x3ffa83126e978d50ull, 0x40093a9300777002ull }, + { 0x3ffa872b020c49bbull, 0x40093f0d71f7cdaaull }, + { 0x3ffa8b4395810625ull, 0x40094388aef8aa80ull }, + { 0x3ffa8f5c28f5c290ull, 0x40094804b79e2607ull }, + { 0x3ffa9374bc6a7efaull, 0x40094c818c0c6628ull }, + { 0x3ffa978d4fdf3b65ull, 0x400950ff2c679738ull }, + { 0x3ffa9ba5e353f7cfull, 0x4009557d98d3ebf8ull }, + { 0x3ffa9fbe76c8b43aull, 0x400959fcd1759d96ull }, + { 0x3ffaa3d70a3d70a4ull, 0x40095e7cd670ebabull }, + { 0x3ffaa7ef9db22d0eull, 0x400962fda7ea1c44ull }, + { 0x3ffaac083126e979ull, 0x4009677f46057bdeull }, + { 0x3ffab020c49ba5e3ull, 0x40096c01b0e75d61ull }, + { 0x3ffab4395810624eull, 0x40097084e8b41a31ull }, + { 0x3ffab851eb851eb8ull, 0x40097508ed90121cull }, + { 0x3ffabc6a7ef9db23ull, 0x4009798dbf9fab6full }, + { 0x3ffac083126e978dull, 0x40097e135f0752e4ull }, + { 0x3ffac49ba5e353f8ull, 0x40098299cbeb7bb3ull }, + { 0x3ffac8b439581062ull, 0x4009872106709f87ull }, + { 0x3ffacccccccccccdull, 0x40098ba90ebb3e8aull }, + { 0x3ffad0e560418937ull, 0x40099031e4efdf5aull }, + { 0x3ffad4fdf3b645a2ull, 0x400994bb89330f19ull }, + { 0x3ffad916872b020cull, 0x40099945fba9615dull }, + { 0x3ffadd2f1a9fbe77ull, 0x40099dd13c777042ull }, + { 0x3ffae147ae147ae1ull, 0x4009a25d4bc1dc5dull }, + { 0x3ffae5604189374cull, 0x4009a6ea29ad4ccaull }, + { 0x3ffae978d4fdf3b6ull, 0x4009ab77d65e6f1eull }, + { 0x3ffaed916872b021ull, 0x4009b00651f9f77aull }, + { 0x3ffaf1a9fbe76c8bull, 0x4009b4959ca4a07cull }, + { 0x3ffaf5c28f5c28f6ull, 0x4009b925b6832b4aull }, + { 0x3ffaf9db22d0e560ull, 0x4009bdb69fba5f8full }, + { 0x3ffafdf3b645a1cbull, 0x4009c248586f0b7full }, + { 0x3ffb020c49ba5e35ull, 0x4009c6dae0c603d5ull }, + { 0x3ffb0624dd2f1aa0ull, 0x4009cb6e38e423d7ull }, + { 0x3ffb0a3d70a3d70aull, 0x4009d00260ee4d52ull }, + { 0x3ffb0e5604189375ull, 0x4009d497590968a7ull }, + { 0x3ffb126e978d4fdfull, 0x4009d92d215a64baull }, + { 0x3ffb16872b020c4aull, 0x4009ddc3ba063707ull }, + { 0x3ffb1a9fbe76c8b4ull, 0x4009e25b2331db91ull }, + { 0x3ffb1eb851eb851full, 0x4009e6f35d0254f2ull }, + { 0x3ffb22d0e5604189ull, 0x4009eb8c679cac52ull }, + { 0x3ffb26e978d4fdf4ull, 0x4009f0264325f16full }, + { 0x3ffb2b020c49ba5eull, 0x4009f4c0efc33a98ull }, + { 0x3ffb2f1a9fbe76c9ull, 0x4009f95c6d99a4b4ull }, + { 0x3ffb333333333333ull, 0x4009fdf8bcce533dull }, + { 0x3ffb374bc6a7ef9eull, 0x400a0295dd86704aull }, + { 0x3ffb3b645a1cac08ull, 0x400a0733cfe72c86ull }, + { 0x3ffb3f7ced916873ull, 0x400a0bd29415bf38ull }, + { 0x3ffb4395810624ddull, 0x400a10722a376641ull }, + { 0x3ffb47ae147ae148ull, 0x400a151292716622ull }, + { 0x3ffb4bc6a7ef9db2ull, 0x400a19b3cce909f3ull }, + { 0x3ffb4fdf3b645a1dull, 0x400a1e55d9c3a371ull }, + { 0x3ffb53f7ced91687ull, 0x400a22f8b9268af5ull }, + { 0x3ffb5810624dd2f2ull, 0x400a279c6b371f7cull }, + { 0x3ffb5c28f5c28f5cull, 0x400a2c40f01ac6a1ull }, + { 0x3ffb604189374bc7ull, 0x400a30e647f6eca8ull }, + { 0x3ffb645a1cac0831ull, 0x400a358c72f10473ull }, + { 0x3ffb6872b020c49cull, 0x400a3a33712e8790ull }, + { 0x3ffb6c8b43958106ull, 0x400a3edb42d4f62full }, + { 0x3ffb70a3d70a3d71ull, 0x400a4383e809d72cull }, + { 0x3ffb74bc6a7ef9dbull, 0x400a482d60f2b809ull }, + { 0x3ffb78d4fdf3b646ull, 0x400a4cd7adb52cf6ull }, + { 0x3ffb7ced916872b0ull, 0x400a5182ce76d0caull }, + { 0x3ffb810624dd2f1bull, 0x400a562ec35d450eull }, + { 0x3ffb851eb851eb85ull, 0x400a5adb8c8e31f4ull }, + { 0x3ffb89374bc6a7f0ull, 0x400a5f892a2f4662ull }, + { 0x3ffb8d4fdf3b645aull, 0x400a64379c6637ebull }, + { 0x3ffb916872b020c5ull, 0x400a68e6e358c2d5ull }, + { 0x3ffb95810624dd2full, 0x400a6d96ff2caa17ull }, + { 0x3ffb99999999999aull, 0x400a7247f007b760ull }, + { 0x3ffb9db22d0e5604ull, 0x400a76f9b60fbb0full }, + { 0x3ffba1cac083126full, 0x400a7bac516a8c3eull }, + { 0x3ffba5e353f7ced9ull, 0x400a805fc23e08baull }, + { 0x3ffba9fbe76c8b44ull, 0x400a851408b0150eull }, + { 0x3ffbae147ae147aeull, 0x400a89c924e69c79ull }, + { 0x3ffbb22d0e560419ull, 0x400a8e7f170790fbull }, + { 0x3ffbb645a1cac083ull, 0x400a9335df38eb4bull }, + { 0x3ffbba5e353f7ceeull, 0x400a97ed7da0aae3ull }, + { 0x3ffbbe76c8b43958ull, 0x400a9ca5f264d5f4ull }, + { 0x3ffbc28f5c28f5c3ull, 0x400aa15f3dab7979ull }, + { 0x3ffbc6a7ef9db22dull, 0x400aa6195f9aa924ull }, + { 0x3ffbcac083126e98ull, 0x400aaad458587f70ull }, + { 0x3ffbced916872b02ull, 0x400aaf90280b1d99ull }, + { 0x3ffbd2f1a9fbe76dull, 0x400ab44cced8aba2ull }, + { 0x3ffbd70a3d70a3d7ull, 0x400ab90a4ce7584eull }, + { 0x3ffbdb22d0e56042ull, 0x400abdc8a25d592full }, + { 0x3ffbdf3b645a1cacull, 0x400ac287cf60ea98ull }, + { 0x3ffbe353f7ced917ull, 0x400ac747d4184facull }, + { 0x3ffbe76c8b439581ull, 0x400acc08b0a9d253ull }, + { 0x3ffbeb851eb851ecull, 0x400ad0ca653bc346ull }, + { 0x3ffbef9db22d0e56ull, 0x400ad58cf1f47a06ull }, + { 0x3ffbf3b645a1cac1ull, 0x400ada5056fa54e8ull }, + { 0x3ffbf7ced916872bull, 0x400adf149473b909ull }, + { 0x3ffbfbe76c8b4396ull, 0x400ae3d9aa871260ull }, + { 0x3ffc000000000000ull, 0x400ae89f995ad3adull }, + { 0x3ffc04189374bc6bull, 0x400aed666115768bull }, + { 0x3ffc083126e978d5ull, 0x400af22e01dd7b63ull }, + { 0x3ffc0c49ba5e3540ull, 0x400af6f67bd96978ull }, + { 0x3ffc10624dd2f1aaull, 0x400afbbfcf2fcee0ull }, + { 0x3ffc147ae147ae15ull, 0x400b0089fc07408eull }, + { 0x3ffc189374bc6a7full, 0x400b055502865a49ull }, + { 0x3ffc1cac083126eaull, 0x400b0a20e2d3beb6ull }, + { 0x3ffc20c49ba5e354ull, 0x400b0eed9d161753ull }, + { 0x3ffc24dd2f1a9fbfull, 0x400b13bb3174147eull }, + { 0x3ffc28f5c28f5c29ull, 0x400b1889a0146d6full }, + { 0x3ffc2d0e56041894ull, 0x400b1d58e91de043ull }, + { 0x3ffc3126e978d4feull, 0x400b22290cb731f1ull }, + { 0x3ffc353f7ced9169ull, 0x400b26fa0b072e58ull }, + { 0x3ffc395810624dd3ull, 0x400b2bcbe434a834ull }, + { 0x3ffc3d70a3d70a3eull, 0x400b309e9866792bull }, + { 0x3ffc4189374bc6a8ull, 0x400b357227c381c4ull }, + { 0x3ffc45a1cac08313ull, 0x400b3a469272a96eull }, + { 0x3ffc49ba5e353f7dull, 0x400b3f1bd89ade80ull }, + { 0x3ffc4dd2f1a9fbe8ull, 0x400b43f1fa63163cull }, + { 0x3ffc51eb851eb852ull, 0x400b48c8f7f24ccaull }, + { 0x3ffc5604189374bdull, 0x400b4da0d16f8543ull }, + { 0x3ffc5a1cac083127ull, 0x400b52798701c9a6ull }, + { 0x3ffc5e353f7ced92ull, 0x400b575318d02ae8ull }, + { 0x3ffc624dd2f1a9fcull, 0x400b5c2d8701c0e6ull }, + { 0x3ffc666666666667ull, 0x400b6108d1bdaa73ull }, + { 0x3ffc6a7ef9db22d1ull, 0x400b65e4f92b0d4full }, + { 0x3ffc6e978d4fdf3cull, 0x400b6ac1fd711631ull }, + { 0x3ffc72b020c49ba6ull, 0x400b6f9fdeb6f8c0ull }, + { 0x3ffc76c8b4395811ull, 0x400b747e9d23ef9full }, + { 0x3ffc7ae147ae147bull, 0x400b795e38df3c5eull }, + { 0x3ffc7ef9db22d0e6ull, 0x400b7e3eb2102790ull }, + { 0x3ffc83126e978d50ull, 0x400b832008de00b6ull }, + { 0x3ffc872b020c49bbull, 0x400b88023d701e55ull }, + { 0x3ffc8b4395810625ull, 0x400b8ce54feddde6ull }, + { 0x3ffc8f5c28f5c290ull, 0x400b91c9407ea3e5ull }, + { 0x3ffc9374bc6a7efaull, 0x400b96ae0f49dbc7ull }, + { 0x3ffc978d4fdf3b65ull, 0x400b9b93bc76f805ull }, + { 0x3ffc9ba5e353f7cfull, 0x400ba07a482d7213ull }, + { 0x3ffc9fbe76c8b43aull, 0x400ba561b294ca6dull }, + { 0x3ffca3d70a3d70a4ull, 0x400baa49fbd4888cull }, + { 0x3ffca7ef9db22d0full, 0x400baf3324143af3ull }, + { 0x3ffcac083126e979ull, 0x400bb41d2b7b7725ull }, + { 0x3ffcb020c49ba5e3ull, 0x400bb9081231d9b0ull }, + { 0x3ffcb4395810624eull, 0x400bbdf3d85f0629ull }, + { 0x3ffcb851eb851eb8ull, 0x400bc2e07e2aa72dull }, + { 0x3ffcbc6a7ef9db23ull, 0x400bc7ce03bc6e65ull }, + { 0x3ffcc083126e978dull, 0x400bccbc693c1483ull }, + { 0x3ffcc49ba5e353f8ull, 0x400bd1abaed1594bull }, + { 0x3ffcc8b439581062ull, 0x400bd69bd4a4038aull }, + { 0x3ffccccccccccccdull, 0x400bdb8cdadbe120ull }, + { 0x3ffcd0e560418937ull, 0x400be07ec1a0c6fcull }, + { 0x3ffcd4fdf3b645a2ull, 0x400be571891a9122ull }, + { 0x3ffcd916872b020cull, 0x400bea65317122a3ull }, + { 0x3ffcdd2f1a9fbe77ull, 0x400bef59bacc65acull }, + { 0x3ffce147ae147ae1ull, 0x400bf44f25544b7aull }, + { 0x3ffce5604189374cull, 0x400bf9457130cc66ull }, + { 0x3ffce978d4fdf3b6ull, 0x400bfe3c9e89e7dbull }, + { 0x3ffced916872b021ull, 0x400c0334ad87a465ull }, + { 0x3ffcf1a9fbe76c8bull, 0x400c082d9e520fa4ull }, + { 0x3ffcf5c28f5c28f6ull, 0x400c0d2771113e59ull }, + { 0x3ffcf9db22d0e560ull, 0x400c122225ed4c60ull }, + { 0x3ffcfdf3b645a1cbull, 0x400c171dbd0e5cb4ull }, + { 0x3ffd020c49ba5e35ull, 0x400c1c1a369c9970ull }, + { 0x3ffd0624dd2f1aa0ull, 0x400c211792c033d1ull }, + { 0x3ffd0a3d70a3d70aull, 0x400c2615d1a16434ull }, + { 0x3ffd0e5604189375ull, 0x400c2b14f3686a1eull }, + { 0x3ffd126e978d4fdfull, 0x400c3014f83d8c33ull }, + { 0x3ffd16872b020c4aull, 0x400c3515e0491843ull }, + { 0x3ffd1a9fbe76c8b4ull, 0x400c3a17abb36340ull }, + { 0x3ffd1eb851eb851full, 0x400c3f1a5aa4c94bull }, + { 0x3ffd22d0e5604189ull, 0x400c441ded45adaaull }, + { 0x3ffd26e978d4fdf4ull, 0x400c492263be7ad0ull }, + { 0x3ffd2b020c49ba5eull, 0x400c4e27be37a25dull }, + { 0x3ffd2f1a9fbe76c9ull, 0x400c532dfcd99d1eull }, + { 0x3ffd333333333333ull, 0x400c58351fcceb0full }, + { 0x3ffd374bc6a7ef9eull, 0x400c5d3d273a135full }, + { 0x3ffd3b645a1cac08ull, 0x400c62461349a46aull }, + { 0x3ffd3f7ced916873ull, 0x400c674fe42433c4ull }, + { 0x3ffd4395810624ddull, 0x400c6c5a99f25e30ull }, + { 0x3ffd47ae147ae148ull, 0x400c716634dcc7adull }, + { 0x3ffd4bc6a7ef9db2ull, 0x400c7672b50c1b68ull }, + { 0x3ffd4fdf3b645a1dull, 0x400c7b801aa90bd0ull }, + { 0x3ffd53f7ced91687ull, 0x400c808e65dc5285ull }, + { 0x3ffd5810624dd2f2ull, 0x400c859d96ceb067ull }, + { 0x3ffd5c28f5c28f5cull, 0x400c8aadada8ed8full }, + { 0x3ffd604189374bc7ull, 0x400c8fbeaa93d956ull }, + { 0x3ffd645a1cac0831ull, 0x400c94d08db84a4eull }, + { 0x3ffd6872b020c49cull, 0x400c99e3573f1e50ull }, + { 0x3ffd6c8b43958106ull, 0x400c9ef707513a70ull }, + { 0x3ffd70a3d70a3d71ull, 0x400ca40b9e178b09ull }, + { 0x3ffd74bc6a7ef9dbull, 0x400ca9211bbb03b5ull }, + { 0x3ffd78d4fdf3b646ull, 0x400cae3780649f58ull }, + { 0x3ffd7ced916872b0ull, 0x400cb34ecc3d6017ull }, + { 0x3ffd810624dd2f1bull, 0x400cb866ff6e4f64ull }, + { 0x3ffd851eb851eb85ull, 0x400cbd801a207df3ull }, + { 0x3ffd89374bc6a7f0ull, 0x400cc29a1c7d03caull }, + { 0x3ffd8d4fdf3b645aull, 0x400cc7b506ad0032ull }, + { 0x3ffd916872b020c5ull, 0x400cccd0d8d999c9ull }, + { 0x3ffd95810624dd2full, 0x400cd1ed932bfe74ull }, + { 0x3ffd99999999999aull, 0x400cd70b35cd636dull }, + { 0x3ffd9db22d0e5604ull, 0x400cdc29c0e70539ull }, + { 0x3ffda1cac083126full, 0x400ce14934a227b3ull }, + { 0x3ffda5e353f7ced9ull, 0x400ce66991281607ull }, + { 0x3ffda9fbe76c8b44ull, 0x400ceb8ad6a222b8ull }, + { 0x3ffdae147ae147aeull, 0x400cf0ad0539a79bull }, + { 0x3ffdb22d0e560419ull, 0x400cf5d01d1805e0ull }, + { 0x3ffdb645a1cac083ull, 0x400cfaf41e66a60aull }, + { 0x3ffdba5e353f7ceeull, 0x400d0019094ef7feull }, + { 0x3ffdbe76c8b43958ull, 0x400d053eddfa72f3ull }, + { 0x3ffdc28f5c28f5c3ull, 0x400d0a659c929582ull }, + { 0x3ffdc6a7ef9db22dull, 0x400d0f8d4540e5a0ull }, + { 0x3ffdcac083126e98ull, 0x400d14b5d82ef0a1ull }, + { 0x3ffdced916872b02ull, 0x400d19df55864b38ull }, + { 0x3ffdd2f1a9fbe76dull, 0x400d1f09bd70917cull }, + { 0x3ffdd70a3d70a3d7ull, 0x400d2435101766e3ull }, + { 0x3ffddb22d0e56042ull, 0x400d29614da4764dull }, + { 0x3ffddf3b645a1cacull, 0x400d2e8e764171f9ull }, + { 0x3ffde353f7ced917ull, 0x400d33bc8a181391ull }, + { 0x3ffde76c8b439581ull, 0x400d38eb89521c25ull }, + { 0x3ffdeb851eb851ecull, 0x400d3e1b74195431ull }, + { 0x3ffdef9db22d0e56ull, 0x400d434c4a978b97ull }, + { 0x3ffdf3b645a1cac1ull, 0x400d487e0cf699abull }, + { 0x3ffdf7ced916872bull, 0x400d4db0bb605d28ull }, + { 0x3ffdfbe76c8b4396ull, 0x400d52e455febc3eull }, + { 0x3ffe000000000000ull, 0x400d5818dcfba487ull }, + { 0x3ffe04189374bc6bull, 0x400d5d4e50810b14ull }, + { 0x3ffe083126e978d5ull, 0x400d6284b0b8ec63ull }, + { 0x3ffe0c49ba5e3540ull, 0x400d67bbfdcd4c6bull }, + { 0x3ffe10624dd2f1aaull, 0x400d6cf437e83693ull }, + { 0x3ffe147ae147ae15ull, 0x400d722d5f33bdc0ull }, + { 0x3ffe189374bc6a7full, 0x400d776773d9fc46ull }, + { 0x3ffe1cac083126eaull, 0x400d7ca2760513f9ull }, + { 0x3ffe20c49ba5e354ull, 0x400d81de65df2e25ull }, + { 0x3ffe24dd2f1a9fbfull, 0x400d871b43927b92ull }, + { 0x3ffe28f5c28f5c29ull, 0x400d8c590f493485ull }, + { 0x3ffe2d0e56041894ull, 0x400d9197c92d98c2ull }, + { 0x3ffe3126e978d4feull, 0x400d96d77169ef8dull }, + { 0x3ffe353f7ced9169ull, 0x400d9c18082887abull }, + { 0x3ffe395810624dd3ull, 0x400da1598d93b763ull }, + { 0x3ffe3d70a3d70a3eull, 0x400da69c01d5dc81ull }, + { 0x3ffe4189374bc6a8ull, 0x400dabdf65195c55ull }, + { 0x3ffe45a1cac08313ull, 0x400db123b788a3b8ull }, + { 0x3ffe49ba5e353f7dull, 0x400db668f94e2705ull }, + { 0x3ffe4dd2f1a9fbe8ull, 0x400dbbaf2a946229ull }, + { 0x3ffe51eb851eb852ull, 0x400dc0f64b85d892ull }, + { 0x3ffe5604189374bdull, 0x400dc63e5c4d1543ull }, + { 0x3ffe5a1cac083127ull, 0x400dcb875d14aac5ull }, + { 0x3ffe5e353f7ced92ull, 0x400dd0d14e073335ull }, + { 0x3ffe624dd2f1a9fcull, 0x400dd61c2f4f503aull }, + { 0x3ffe666666666667ull, 0x400ddb680117ab13ull }, + { 0x3ffe6a7ef9db22d1ull, 0x400de0b4c38af48bull }, + { 0x3ffe6e978d4fdf3cull, 0x400de60276d3e508ull }, + { 0x3ffe72b020c49ba6ull, 0x400deb511b1d3c7dull }, + { 0x3ffe76c8b4395811ull, 0x400df0a0b091c27cull }, + { 0x3ffe7ae147ae147bull, 0x400df5f1375c4628ull }, + { 0x3ffe7ef9db22d0e6ull, 0x400dfb42afa79e44ull }, + { 0x3ffe83126e978d50ull, 0x400e0095199ea926ull }, + { 0x3ffe872b020c49bbull, 0x400e05e8756c4cc7ull }, + { 0x3ffe8b4395810625ull, 0x400e0b3cc33b76b9ull }, + { 0x3ffe8f5c28f5c290ull, 0x400e109203371c30ull }, + { 0x3ffe9374bc6a7efaull, 0x400e15e8358a39fbull }, + { 0x3ffe978d4fdf3b65ull, 0x400e1b3f5a5fd491ull }, + { 0x3ffe9ba5e353f7cfull, 0x400e209771e2f806ull }, + { 0x3ffe9fbe76c8b43aull, 0x400e25f07c3eb818ull }, + { 0x3ffea3d70a3d70a4ull, 0x400e2b4a799e3023ull }, + { 0x3ffea7ef9db22d0full, 0x400e30a56a2c8331ull }, + { 0x3ffeac083126e979ull, 0x400e36014e14dbf0ull }, + { 0x3ffeb020c49ba5e4ull, 0x400e3b5e25826cb9ull }, + { 0x3ffeb4395810624eull, 0x400e40bbf0a06f90ull }, + { 0x3ffeb851eb851eb8ull, 0x400e461aaf9a2624ull }, + { 0x3ffebc6a7ef9db23ull, 0x400e4b7a629ad9d5ull }, + { 0x3ffec083126e978dull, 0x400e50db09cddbadull }, + { 0x3ffec49ba5e353f8ull, 0x400e563ca55e846bull }, + { 0x3ffec8b439581062ull, 0x400e5b9f3578347dull }, + { 0x3ffecccccccccccdull, 0x400e6102ba465406ull }, + { 0x3ffed0e560418937ull, 0x400e666733f452daull }, + { 0x3ffed4fdf3b645a2ull, 0x400e6bcca2ada889ull }, + { 0x3ffed916872b020cull, 0x400e7133069dd452ull }, + { 0x3ffedd2f1a9fbe77ull, 0x400e769a5ff05d36ull }, + { 0x3ffee147ae147ae1ull, 0x400e7c02aed0d1e7ull }, + { 0x3ffee5604189374cull, 0x400e816bf36ac8daull }, + { 0x3ffee978d4fdf3b6ull, 0x400e86d62de9e039ull }, + { 0x3ffeed916872b021ull, 0x400e8c415e79bdf4ull }, + { 0x3ffef1a9fbe76c8bull, 0x400e91ad85460fb4ull }, + { 0x3ffef5c28f5c28f6ull, 0x400e971aa27a8ae7ull }, + { 0x3ffef9db22d0e560ull, 0x400e9c88b642ecbbull }, + { 0x3ffefdf3b645a1cbull, 0x400ea1f7c0cafa23ull }, + { 0x3fff020c49ba5e35ull, 0x400ea767c23e7fd5ull }, + { 0x3fff0624dd2f1aa0ull, 0x400eacd8bac95250ull }, + { 0x3fff0a3d70a3d70aull, 0x400eb24aaa974dd7ull }, + { 0x3fff0e5604189375ull, 0x400eb7bd91d4567bull }, + { 0x3fff126e978d4fdfull, 0x400ebd3170ac5813ull }, + { 0x3fff16872b020c4aull, 0x400ec2a6474b4646ull }, + { 0x3fff1a9fbe76c8b4ull, 0x400ec81c15dd1c82ull }, + { 0x3fff1eb851eb851full, 0x400ecd92dc8dde0cull }, + { 0x3fff22d0e5604189ull, 0x400ed30a9b8995f0ull }, + { 0x3fff26e978d4fdf4ull, 0x400ed88352fc5715ull }, + { 0x3fff2b020c49ba5eull, 0x400eddfd03123c2cull }, + { 0x3fff2f1a9fbe76c9ull, 0x400ee377abf767c0ull }, + { 0x3fff333333333333ull, 0x400ee8f34dd8042eull }, + { 0x3fff374bc6a7ef9eull, 0x400eee6fe8e043aeull }, + { 0x3fff3b645a1cac08ull, 0x400ef3ed7d3c604bull }, + { 0x3fff3f7ced916873ull, 0x400ef96c0b189befull }, + { 0x3fff4395810624ddull, 0x400efeeb92a1405aull }, + { 0x3fff47ae147ae148ull, 0x400f046c14029f2eull }, + { 0x3fff4bc6a7ef9db2ull, 0x400f09ed8f6911e7ull }, + { 0x3fff4fdf3b645a1dull, 0x400f0f700500f9e2ull }, + { 0x3fff53f7ced91687ull, 0x400f14f374f6c05bull }, + { 0x3fff5810624dd2f2ull, 0x400f1a77df76d674ull }, + { 0x3fff5c28f5c28f5cull, 0x400f1ffd44adb52dull }, + { 0x3fff604189374bc7ull, 0x400f2583a4c7dd70ull }, + { 0x3fff645a1cac0831ull, 0x400f2b0afff1d809ull }, + { 0x3fff6872b020c49cull, 0x400f3093565835b1ull }, + { 0x3fff6c8b43958106ull, 0x400f361ca8278f03ull }, + { 0x3fff70a3d70a3d71ull, 0x400f3ba6f58c848dull }, + { 0x3fff74bc6a7ef9dbull, 0x400f41323eb3bec1ull }, + { 0x3fff78d4fdf3b646ull, 0x400f46be83c9ee04ull }, + { 0x3fff7ced916872b0ull, 0x400f4c4bc4fbcaa5ull }, + { 0x3fff810624dd2f1bull, 0x400f51da027614e9ull }, + { 0x3fff851eb851eb85ull, 0x400f57693c659500ull }, + { 0x3fff89374bc6a7f0ull, 0x400f5cf972f71b14ull }, + { 0x3fff8d4fdf3b645aull, 0x400f628aa6577f3cull }, + { 0x3fff916872b020c5ull, 0x400f681cd6b3a18bull }, + { 0x3fff95810624dd2full, 0x400f6db004386a06ull }, + { 0x3fff99999999999aull, 0x400f73442f12c8b2ull }, + { 0x3fff9db22d0e5604ull, 0x400f78d9576fb584ull }, + { 0x3fffa1cac083126full, 0x400f7e6f7d7c3076ull }, + { 0x3fffa5e353f7ced9ull, 0x400f8406a1654177ull }, + { 0x3fffa9fbe76c8b44ull, 0x400f899ec357f87cull }, + { 0x3fffae147ae147aeull, 0x400f8f37e3816d72ull }, + { 0x3fffb22d0e560419ull, 0x400f94d2020ec04dull }, + { 0x3fffb645a1cac083ull, 0x400f9a6d1f2d1901ull }, + { 0x3fffba5e353f7ceeull, 0x400fa0093b09a786ull }, + { 0x3fffbe76c8b43958ull, 0x400fa5a655d1a3daull }, + { 0x3fffc28f5c28f5c3ull, 0x400fab446fb24e02ull }, + { 0x3fffc6a7ef9db22dull, 0x400fb0e388d8ee09ull }, + { 0x3fffcac083126e98ull, 0x400fb683a172d409ull }, + { 0x3fffced916872b02ull, 0x400fbc24b9ad581full }, + { 0x3fffd2f1a9fbe76dull, 0x400fc1c6d1b5da7eull }, + { 0x3fffd70a3d70a3d7ull, 0x400fc769e9b9c35eull }, + { 0x3fffdb22d0e56042ull, 0x400fcd0e01e6830dull }, + { 0x3fffdf3b645a1cacull, 0x400fd2b31a6991e5ull }, + { 0x3fffe353f7ced917ull, 0x400fd85933707058ull }, + { 0x3fffe76c8b439581ull, 0x400fde004d28a6e4ull }, + { 0x3fffeb851eb851ecull, 0x400fe3a867bfc624ull }, + { 0x3fffef9db22d0e56ull, 0x400fe951836366c3ull }, + { 0x3ffff3b645a1cac1ull, 0x400feefba041298aull }, + { 0x3ffff7ced916872bull, 0x400ff4a6be86b754ull }, + { 0x3ffffbe76c8b4396ull, 0x400ffa52de61c120ull }, + { 0x4000000000000000ull, 0x4010000000000000ull }, + { 0x4000020c49ba5e35ull, 0x401002d711c79a95ull }, + { 0x400004189374bc6bull, 0x401005aea49e94faull }, + { 0x40000624dd2f1aa0ull, 0x40100886b89bd7e7ull }, + { 0x4000083126e978d5ull, 0x40100b5f4dd65028ull }, + { 0x40000a3d70a3d70aull, 0x40100e386464ee98ull }, + { 0x40000c49ba5e3540ull, 0x40101111fc5ea82bull }, + { 0x40000e5604189375ull, 0x401013ec15da75ddull }, + { 0x400010624dd2f1aaull, 0x401016c6b0ef54c9ull }, + { 0x4000126e978d4fdfull, 0x401019a1cdb44618ull }, + { 0x4000147ae147ae15ull, 0x40101c7d6c404f0cull }, + { 0x400016872b020c4aull, 0x40101f598caa78faull }, + { 0x4000189374bc6a7full, 0x401022362f09d14full }, + { 0x40001a9fbe76c8b4ull, 0x4010251353756991ull }, + { 0x40001cac083126eaull, 0x401027f0fa04575dull }, + { 0x40001eb851eb851full, 0x40102acf22cdb468ull }, + { 0x400020c49ba5e354ull, 0x40102dadcde89e83ull }, + { 0x400022d0e5604189ull, 0x4010308cfb6c3798ull }, + { 0x400024dd2f1a9fbfull, 0x4010336cab6fa5adull }, + { 0x400026e978d4fdf4ull, 0x4010364cde0a12e2ull }, + { 0x400028f5c28f5c29ull, 0x4010392d9352ad75ull }, + { 0x40002b020c49ba5eull, 0x40103c0ecb60a7c3ull }, + { 0x40002d0e56041894ull, 0x40103ef0864b3846ull }, + { 0x40002f1a9fbe76c9ull, 0x401041d2c4299993ull }, + { 0x40003126e978d4feull, 0x401044b585130a64ull }, + { 0x4000333333333333ull, 0x40104798c91ecd91ull }, + { 0x4000353f7ced9169ull, 0x40104a7c90642a15ull }, + { 0x4000374bc6a7ef9eull, 0x40104d60dafa6b08ull }, + { 0x4000395810624dd3ull, 0x40105045a8f8dfa8ull }, + { 0x40003b645a1cac08ull, 0x4010532afa76db57ull }, + { 0x40003d70a3d70a3eull, 0x40105610cf8bb59aull }, + { 0x40003f7ced916873ull, 0x401058f7284eca19ull }, + { 0x40004189374bc6a8ull, 0x40105bde04d778a2ull }, + { 0x40004395810624ddull, 0x40105ec5653d252cull }, + { 0x400045a1cac08313ull, 0x401061ad499737d3ull }, + { 0x400047ae147ae148ull, 0x40106495b1fd1cd8ull }, + { 0x400049ba5e353f7dull, 0x4010677e9e8644a9ull }, + { 0x40004bc6a7ef9db2ull, 0x40106a680f4a23daull }, + { 0x40004dd2f1a9fbe8ull, 0x40106d520460332cull }, + { 0x40004fdf3b645a1dull, 0x4010703c7ddfef85ull }, + { 0x400051eb851eb852ull, 0x401073277be0d9fcull }, + { 0x400053f7ced91687ull, 0x40107612fe7a77d1ull }, + { 0x40005604189374bdull, 0x401078ff05c45274ull }, + { 0x40005810624dd2f2ull, 0x40107beb91d5f77cull }, + { 0x40005a1cac083127ull, 0x40107ed8a2c6f8b6ull }, + { 0x40005c28f5c28f5cull, 0x401081c638aeec18ull }, + { 0x40005e353f7ced92ull, 0x401084b453a56bceull }, + { 0x4000604189374bc7ull, 0x401087a2f3c2162eull }, + { 0x4000624dd2f1a9fcull, 0x40108a92191c8dc2ull }, + { 0x4000645a1cac0831ull, 0x40108d81c3cc7947ull }, + { 0x4000666666666666ull, 0x40109071f3e983acull }, + { 0x40006872b020c49cull, 0x40109362a98b5c15ull }, + { 0x40006a7ef9db22d1ull, 0x40109653e4c9b5d6ull }, + { 0x40006c8b43958106ull, 0x40109945a5bc487bull }, + { 0x40006e978d4fdf3bull, 0x40109c37ec7acfc6ull }, + { 0x400070a3d70a3d71ull, 0x40109f2ab91d0bb1ull }, + { 0x400072b020c49ba6ull, 0x4010a21e0bbac068ull }, + { 0x400074bc6a7ef9dbull, 0x4010a511e46bb653ull }, + { 0x400076c8b4395810ull, 0x4010a8064347ba12ull }, + { 0x400078d4fdf3b646ull, 0x4010aafb28669c81ull }, + { 0x40007ae147ae147bull, 0x4010adf093e032afull }, + { 0x40007ced916872b0ull, 0x4010b0e685cc55edull }, + { 0x40007ef9db22d0e5ull, 0x4010b3dcfe42e3c5ull }, + { 0x4000810624dd2f1bull, 0x4010b6d3fd5bbe00ull }, + { 0x400083126e978d50ull, 0x4010b9cb832eca9dull }, + { 0x4000851eb851eb85ull, 0x4010bcc38fd3f3e2ull }, + { 0x4000872b020c49baull, 0x4010bfbc2363284full }, + { 0x400089374bc6a7f0ull, 0x4010c2b53df45aa5ull }, + { 0x40008b4395810625ull, 0x4010c5aedf9f81e2ull }, + { 0x40008d4fdf3b645aull, 0x4010c8a9087c9949ull }, + { 0x40008f5c28f5c28full, 0x4010cba3b8a3a05eull }, + { 0x4000916872b020c5ull, 0x4010ce9ef02c9ae7ull }, + { 0x40009374bc6a7efaull, 0x4010d19aaf2f90ecull }, + { 0x400095810624dd2full, 0x4010d496f5c48eb9ull }, + { 0x4000978d4fdf3b64ull, 0x4010d793c403a4e2ull }, + { 0x400099999999999aull, 0x4010da911a04e83full }, + { 0x40009ba5e353f7cfull, 0x4010dd8ef7e071ebull }, + { 0x40009db22d0e5604ull, 0x4010e08d5dae5f4dull }, + { 0x40009fbe76c8b439ull, 0x4010e38c4b86d210ull }, + { 0x4000a1cac083126full, 0x4010e68bc181f02cull }, + { 0x4000a3d70a3d70a4ull, 0x4010e98bbfb7e3ddull }, + { 0x4000a5e353f7ced9ull, 0x4010ec8c4640dbacull }, + { 0x4000a7ef9db22d0eull, 0x4010ef8d55350a6cull }, + { 0x4000a9fbe76c8b44ull, 0x4010f28eecaca740ull }, + { 0x4000ac083126e979ull, 0x4010f5910cbfed8full }, + { 0x4000ae147ae147aeull, 0x4010f893b5871d13ull }, + { 0x4000b020c49ba5e3ull, 0x4010fb96e71a79d3ull }, + { 0x4000b22d0e560419ull, 0x4010fe9aa1924c25ull }, + { 0x4000b4395810624eull, 0x4011019ee506e0adull }, + { 0x4000b645a1cac083ull, 0x401104a3b190885full }, + { 0x4000b851eb851eb8ull, 0x401107a907479882ull }, + { 0x4000ba5e353f7ceeull, 0x40110aaee6446aafull }, + { 0x4000bc6a7ef9db23ull, 0x40110db54e9f5ccdull }, + { 0x4000be76c8b43958ull, 0x401110bc4070d11cull }, + { 0x4000c083126e978dull, 0x401113c3bbd12e2cull }, + { 0x4000c28f5c28f5c3ull, 0x401116cbc0d8dee6ull }, + { 0x4000c49ba5e353f8ull, 0x401119d44fa05282ull }, + { 0x4000c6a7ef9db22dull, 0x40111cdd683ffc93ull }, + { 0x4000c8b439581062ull, 0x40111fe70ad05505ull }, + { 0x4000cac083126e98ull, 0x401122f13769d818ull }, + { 0x4000cccccccccccdull, 0x401125fbee250664ull }, + { 0x4000ced916872b02ull, 0x401129072f1a64dfull }, + { 0x4000d0e560418937ull, 0x40112c12fa627cd4ull }, + { 0x4000d2f1a9fbe76dull, 0x40112f1f5015dbefull }, + { 0x4000d4fdf3b645a2ull, 0x4011322c304d1431ull }, + { 0x4000d70a3d70a3d7ull, 0x401135399b20bbfbull }, + { 0x4000d916872b020cull, 0x4011384790a96e0cull }, + { 0x4000db22d0e56042ull, 0x40113b5610ffc982ull }, + { 0x4000dd2f1a9fbe77ull, 0x40113e651c3c71d5ull }, + { 0x4000df3b645a1cacull, 0x40114174b2780ee0ull }, + { 0x4000e147ae147ae1ull, 0x40114484d3cb4cdfull }, + { 0x4000e353f7ced917ull, 0x40114795804edc70ull }, + { 0x4000e5604189374cull, 0x40114aa6b81b728dull }, + { 0x4000e76c8b439581ull, 0x40114db87b49c899ull }, + { 0x4000e978d4fdf3b6ull, 0x401150cac9f29c58ull }, + { 0x4000eb851eb851ecull, 0x401153dda42eaff5ull }, + { 0x4000ed916872b021ull, 0x401156f10a16c9fbull }, + { 0x4000ef9db22d0e56ull, 0x40115a04fbc3b55dull }, + { 0x4000f1a9fbe76c8bull, 0x40115d19794e4177ull }, + { 0x4000f3b645a1cac1ull, 0x4011602e82cf420cull }, + { 0x4000f5c28f5c28f6ull, 0x40116344185f8f43ull }, + { 0x4000f7ced916872bull, 0x4011665a3a1805afull }, + { 0x4000f9db22d0e560ull, 0x40116970e811864eull }, + { 0x4000fbe76c8b4396ull, 0x40116c882264f688ull }, + { 0x4000fdf3b645a1cbull, 0x40116f9fe92b402cull }, + { 0x4001000000000000ull, 0x401172b83c7d517bull }, + { 0x4001020c49ba5e35ull, 0x401175d11c741d20ull }, + { 0x400104189374bc6bull, 0x401178ea89289a35ull }, + { 0x40010624dd2f1aa0ull, 0x40117c0482b3c43full }, + { 0x4001083126e978d5ull, 0x40117f1f092e9b37ull }, + { 0x40010a3d70a3d70aull, 0x4011823a1cb22383ull }, + { 0x40010c49ba5e3540ull, 0x40118555bd5765fdull }, + { 0x40010e5604189375ull, 0x40118871eb376feaull }, + { 0x400110624dd2f1aaull, 0x40118b8ea66b5309ull }, + { 0x4001126e978d4fdfull, 0x40118eabef0c2587ull }, + { 0x4001147ae147ae15ull, 0x401191c9c533020aull }, + { 0x400116872b020c4aull, 0x401194e828f907a5ull }, + { 0x4001189374bc6a7full, 0x401198071a7759e9ull }, + { 0x40011a9fbe76c8b4ull, 0x40119b2699c720d8ull }, + { 0x40011cac083126eaull, 0x40119e46a70188eeull }, + { 0x40011eb851eb851full, 0x4011a167423fc31cull }, + { 0x400120c49ba5e354ull, 0x4011a4886b9b04cdull }, + { 0x400122d0e5604189ull, 0x4011a7aa232c87e8ull }, + { 0x400124dd2f1a9fbfull, 0x4011aacc690d8acdull }, + { 0x400126e978d4fdf4ull, 0x4011adef3d575053ull }, + { 0x400128f5c28f5c29ull, 0x4011b112a0231fd2ull }, + { 0x40012b020c49ba5eull, 0x4011b436918a451dull }, + { 0x40012d0e56041894ull, 0x4011b75b11a61086ull }, + { 0x40012f1a9fbe76c9ull, 0x4011ba80208fd6d9ull }, + { 0x40013126e978d4feull, 0x4011bda5be60f165ull }, + { 0x4001333333333333ull, 0x4011c0cbeb32bdf8ull }, + { 0x4001353f7ced9169ull, 0x4011c3f2a71e9ee3ull }, + { 0x4001374bc6a7ef9eull, 0x4011c719f23dfaf1ull }, + { 0x4001395810624dd3ull, 0x4011ca41ccaa3d78ull }, + { 0x40013b645a1cac08ull, 0x4011cd6a367cd64cull }, + { 0x40013d70a3d70a3eull, 0x4011d0932fcf39c7ull }, + { 0x40013f7ced916873ull, 0x4011d3bcb8bae0c6ull }, + { 0x40014189374bc6a8ull, 0x4011d6e6d15948adull }, + { 0x40014395810624ddull, 0x4011da1179c3f366ull }, + { 0x400145a1cac08313ull, 0x4011dd3cb2146763ull }, + { 0x400147ae147ae148ull, 0x4011e0687a642f9aull }, + { 0x400149ba5e353f7dull, 0x4011e394d2ccdb8full }, + { 0x40014bc6a7ef9db2ull, 0x4011e6c1bb67ff4bull }, + { 0x40014dd2f1a9fbe8ull, 0x4011e9ef344f3366ull }, + { 0x40014fdf3b645a1dull, 0x4011ed1d3d9c14feull }, + { 0x400151eb851eb852ull, 0x4011f04bd76845c1ull }, + { 0x400153f7ced91687ull, 0x4011f37b01cd6be7ull }, + { 0x40015604189374bdull, 0x4011f6aabce53239ull }, + { 0x40015810624dd2f2ull, 0x4011f9db08c94809ull }, + { 0x40015a1cac083127ull, 0x4011fd0be593613dull }, + { 0x40015c28f5c28f5cull, 0x4012003d535d3649ull }, + { 0x40015e353f7ced92ull, 0x4012036f52408433ull }, + { 0x4001604189374bc7ull, 0x401206a1e2570c8full }, + { 0x4001624dd2f1a9fcull, 0x401209d503ba9588ull }, + { 0x4001645a1cac0831ull, 0x40120d08b684e9daull }, + { 0x4001666666666667ull, 0x4012103cfacfd8d7ull }, + { 0x40016872b020c49cull, 0x40121371d0b53661ull }, + { 0x40016a7ef9db22d1ull, 0x401216a7384edaf4ull }, + { 0x40016c8b43958106ull, 0x401219dd31b6a3a1ull }, + { 0x40016e978d4fdf3bull, 0x40121d13bd067211ull }, + { 0x400170a3d70a3d71ull, 0x4012204ada582c87ull }, + { 0x400172b020c49ba6ull, 0x4012238289c5bdd8ull }, + { 0x400174bc6a7ef9dbull, 0x401226bacb69157aull }, + { 0x400176c8b4395810ull, 0x401229f39f5c277aull }, + { 0x400178d4fdf3b646ull, 0x40122d2d05b8ec83ull }, + { 0x40017ae147ae147bull, 0x40123066fe9961d8ull }, + { 0x40017ced916872b0ull, 0x401233a18a17895cull }, + { 0x40017ef9db22d0e5ull, 0x401236dca84d6990ull }, + { 0x4001810624dd2f1bull, 0x40123a1859550d94ull }, + { 0x400183126e978d50ull, 0x40123d549d488524ull }, + { 0x4001851eb851eb85ull, 0x401240917441e49full }, + { 0x4001872b020c49baull, 0x401243cede5b4505ull }, + { 0x400189374bc6a7f0ull, 0x4012470cdbaec3faull }, + { 0x40018b4395810625ull, 0x40124a4b6c5683beull }, + { 0x40018d4fdf3b645aull, 0x40124d8a906cab3cull }, + { 0x40018f5c28f5c28full, 0x401250ca480b6600ull }, + { 0x4001916872b020c5ull, 0x4012540a934ce43cull }, + { 0x40019374bc6a7efaull, 0x4012574b724b5ac5ull }, + { 0x400195810624dd2full, 0x40125a8ce521031cull }, + { 0x4001978d4fdf3b64ull, 0x40125dceebe81b67ull }, + { 0x400199999999999aull, 0x4012611186bae675ull }, + { 0x40019ba5e353f7cfull, 0x40126454b5b3abbcull }, + { 0x40019db22d0e5604ull, 0x4012679878ecb760ull }, + { 0x40019fbe76c8b439ull, 0x40126adcd0805a2dull }, + { 0x4001a1cac083126full, 0x40126e21bc88e99full }, + { 0x4001a3d70a3d70a4ull, 0x401271673d20bfd8ull }, + { 0x4001a5e353f7ced9ull, 0x401274ad52623bacull }, + { 0x4001a7ef9db22d0eull, 0x401277f3fc67c09eull }, + { 0x4001a9fbe76c8b44ull, 0x40127b3b3b4bb6dfull }, + { 0x4001ac083126e979ull, 0x40127e830f288b4eull }, + { 0x4001ae147ae147aeull, 0x401281cb7818af7cull }, + { 0x4001b020c49ba5e3ull, 0x40128514763699aeull }, + { 0x4001b22d0e560419ull, 0x4012885e099cc4daull }, + { 0x4001b4395810624eull, 0x40128ba83265b0a6ull }, + { 0x4001b645a1cac083ull, 0x40128ef2f0abe171ull }, + { 0x4001b851eb851eb8ull, 0x4012923e4489e04dull }, + { 0x4001ba5e353f7ceeull, 0x4012958a2e1a3b04ull }, + { 0x4001bc6a7ef9db23ull, 0x401298d6ad778410ull }, + { 0x4001be76c8b43958ull, 0x40129c23c2bc52a9ull }, + { 0x4001c083126e978dull, 0x40129f716e0342beull }, + { 0x4001c28f5c28f5c3ull, 0x4012a2bfaf66f4f8ull }, + { 0x4001c49ba5e353f8ull, 0x4012a60e87020eb5ull }, + { 0x4001c6a7ef9db22dull, 0x4012a95df4ef3a12ull }, + { 0x4001c8b439581062ull, 0x4012acadf94925e9ull }, + { 0x4001cac083126e98ull, 0x4012affe942a85cfull }, + { 0x4001cccccccccccdull, 0x4012b34fc5ae1213ull }, + { 0x4001ced916872b02ull, 0x4012b6a18dee87c7ull }, + { 0x4001d0e560418937ull, 0x4012b9f3ed06a8baull }, + { 0x4001d2f1a9fbe76dull, 0x4012bd46e3113b7cull }, + { 0x4001d4fdf3b645a2ull, 0x4012c09a70290b5aull }, + { 0x4001d70a3d70a3d7ull, 0x4012c3ee9468e868ull }, + { 0x4001d916872b020cull, 0x4012c7434feba778ull }, + { 0x4001db22d0e56042ull, 0x4012ca98a2cc2225ull }, + { 0x4001dd2f1a9fbe77ull, 0x4012cdee8d2536c5ull }, + { 0x4001df3b645a1cacull, 0x4012d1450f11c87aull }, + { 0x4001e147ae147ae1ull, 0x4012d49c28acbf2aull }, + { 0x4001e353f7ced917ull, 0x4012d7f3da110785ull }, + { 0x4001e5604189374cull, 0x4012db4c235992f9ull }, + { 0x4001e76c8b439581ull, 0x4012dea504a157c6ull }, + { 0x4001e978d4fdf3b6ull, 0x4012e1fe7e0350f2ull }, + { 0x4001eb851eb851ecull, 0x4012e5588f9a7e4cull }, + { 0x4001ed916872b021ull, 0x4012e8b33981e46eull }, + { 0x4001ef9db22d0e56ull, 0x4012ec0e7bd48cbeull }, + { 0x4001f1a9fbe76c8bull, 0x4012ef6a56ad8570ull }, + { 0x4001f3b645a1cac1ull, 0x4012f2c6ca27e185ull }, + { 0x4001f5c28f5c28f6ull, 0x4012f623d65eb8caull }, + { 0x4001f7ced916872bull, 0x4012f9817b6d27ddull }, + { 0x4001f9db22d0e560ull, 0x4012fcdfb96e502dull }, + { 0x4001fbe76c8b4396ull, 0x4013003e907d57faull }, + { 0x4001fdf3b645a1cbull, 0x4013039e00b56a50ull }, + { 0x4002000000000000ull, 0x401306fe0a31b715ull }, + { 0x4002020c49ba5e35ull, 0x40130a5ead0d72ffull }, + { 0x400204189374bc6bull, 0x40130dbfe963d79bull }, + { 0x40020624dd2f1aa0ull, 0x40131121bf502344ull }, + { 0x4002083126e978d5ull, 0x401314842eed9933ull }, + { 0x40020a3d70a3d70aull, 0x401317e738578174ull }, + { 0x40020c49ba5e3540ull, 0x40131b4adba928eeull }, + { 0x40020e5604189375ull, 0x40131eaf18fde159ull }, + { 0x400210624dd2f1aaull, 0x40132213f071014full }, + { 0x4002126e978d4fdfull, 0x40132579621de43full }, + { 0x4002147ae147ae15ull, 0x401328df6e1fea78ull }, + { 0x400216872b020c4aull, 0x40132c461492791eull }, + { 0x4002189374bc6a7full, 0x40132fad5590fa38ull }, + { 0x40021a9fbe76c8b4ull, 0x401333153136dca8ull }, + { 0x40021cac083126eaull, 0x4013367da79f9431ull }, + { 0x40021eb851eb851full, 0x401339e6b8e69970ull }, + { 0x400220c49ba5e354ull, 0x40133d50652769e9ull }, + { 0x400222d0e5604189ull, 0x401340baac7d87fdull }, + { 0x400224dd2f1a9fbfull, 0x401344258f047af2ull }, + { 0x400226e978d4fdf4ull, 0x401347910cd7ceecull }, + { 0x400228f5c28f5c29ull, 0x40134afd261314f8ull }, + { 0x40022b020c49ba5eull, 0x40134e69dad1e306ull }, + { 0x40022d0e56041894ull, 0x401351d72b2fd3ecull }, + { 0x40022f1a9fbe76c9ull, 0x4013554517488763ull }, + { 0x40023126e978d4feull, 0x401358b39f37a20full }, + { 0x4002333333333333ull, 0x40135c22c318cd7dull }, + { 0x4002353f7ced9169ull, 0x40135f928307b820ull }, + { 0x4002374bc6a7ef9eull, 0x40136302df201555ull }, + { 0x4002395810624dd3ull, 0x40136673d77d9d65ull }, + { 0x40023b645a1cac08ull, 0x401369e56c3c0d85ull }, + { 0x40023d70a3d70a3eull, 0x40136d579d7727d8ull }, + { 0x40023f7ced916873ull, 0x401370ca6b4ab369ull }, + { 0x40024189374bc6a8ull, 0x4013743dd5d27c36ull }, + { 0x40024395810624ddull, 0x401377b1dd2a532aull }, + { 0x400245a1cac08313ull, 0x40137b26816e0e24ull }, + { 0x400247ae147ae148ull, 0x40137e9bc2b987ecull }, + { 0x400249ba5e353f7dull, 0x40138211a128a042ull }, + { 0x40024bc6a7ef9db2ull, 0x401385881cd73bd9ull }, + { 0x40024dd2f1a9fbe8ull, 0x401388ff35e14455ull }, + { 0x40024fdf3b645a1dull, 0x40138c76ec62a84cull }, + { 0x400251eb851eb852ull, 0x40138fef40775b50ull }, + { 0x400253f7ced91687ull, 0x40139368323b55e3ull }, + { 0x40025604189374bdull, 0x401396e1c1ca9583ull }, + { 0x40025810624dd2f2ull, 0x40139a5bef411ca1ull }, + { 0x40025a1cac083127ull, 0x40139dd6babaf2a9ull }, + { 0x40025c28f5c28f5cull, 0x4013a15224542403ull }, + { 0x40025e353f7ced92ull, 0x4013a4ce2c28c210ull }, + { 0x4002604189374bc7ull, 0x4013a84ad254e328ull }, + { 0x4002624dd2f1a9fcull, 0x4013abc816f4a2a7ull }, + { 0x4002645a1cac0831ull, 0x4013af45fa2420e0ull }, + { 0x4002666666666667ull, 0x4013b2c47bff832aull }, + { 0x40026872b020c49cull, 0x4013b6439ca2f3d4ull }, + { 0x40026a7ef9db22d1ull, 0x4013b9c35c2aa231ull }, + { 0x40026c8b43958106ull, 0x4013bd43bab2c296ull }, + { 0x40026e978d4fdf3cull, 0x4013c0c4b8578e58ull }, + { 0x400270a3d70a3d71ull, 0x4013c446553543cdull }, + { 0x400272b020c49ba6ull, 0x4013c7c891682650ull }, + { 0x400274bc6a7ef9dbull, 0x4013cb4b6d0c7e41ull }, + { 0x400276c8b4395810ull, 0x4013cecee83e9903ull }, + { 0x400278d4fdf3b646ull, 0x4013d253031ac905ull }, + { 0x40027ae147ae147bull, 0x4013d5d7bdbd65b3ull }, + { 0x40027ced916872b0ull, 0x4013d95d1842cb89ull }, + { 0x40027ef9db22d0e5ull, 0x4013dce312c75c08ull }, + { 0x4002810624dd2f1bull, 0x4013e069ad677dc0ull }, + { 0x400283126e978d50ull, 0x4013e3f0e83f9c42ull }, + { 0x4002851eb851eb85ull, 0x4013e778c36c2833ull }, + { 0x4002872b020c49baull, 0x4013eb013f099740ull }, + { 0x400289374bc6a7f0ull, 0x4013ee8a5b346428ull }, + { 0x40028b4395810625ull, 0x4013f21418090eb0ull }, + { 0x40028d4fdf3b645aull, 0x4013f59e75a41bb3ull }, + { 0x40028f5c28f5c28full, 0x4013f9297422151aull }, + { 0x4002916872b020c5ull, 0x4013fcb5139f89dfull }, + { 0x40029374bc6a7efaull, 0x4014004154390e0cull }, + { 0x400295810624dd2full, 0x401403ce360b3abfull }, + { 0x4002978d4fdf3b64ull, 0x4014075bb932ae2bull }, + { 0x400299999999999aull, 0x40140ae9ddcc0b97ull }, + { 0x40029ba5e353f7cfull, 0x40140e78a3f3fb5aull }, + { 0x40029db22d0e5604ull, 0x401412080bc72ae9ull }, + { 0x40029fbe76c8b439ull, 0x4014159815624cccull }, + { 0x4002a1cac083126full, 0x40141928c0e218a6ull }, + { 0x4002a3d70a3d70a4ull, 0x40141cba0e634b2cull }, + { 0x4002a5e353f7ced9ull, 0x4014204bfe02a635ull }, + { 0x4002a7ef9db22d0eull, 0x401423de8fdcf0afull }, + { 0x4002a9fbe76c8b44ull, 0x40142771c40ef6a6ull }, + { 0x4002ac083126e979ull, 0x40142b059ab5893dull }, + { 0x4002ae147ae147aeull, 0x40142e9a13ed7eb9ull }, + { 0x4002b020c49ba5e3ull, 0x4014322f2fd3b27full }, + { 0x4002b22d0e560419ull, 0x401435c4ee850510ull }, + { 0x4002b4395810624eull, 0x4014395b501e5c0dull }, + { 0x4002b645a1cac083ull, 0x40143cf254bca23aull }, + { 0x4002b851eb851eb8ull, 0x40144089fc7cc77cull }, + { 0x4002ba5e353f7ceeull, 0x40144422477bc0deull }, + { 0x4002bc6a7ef9db23ull, 0x401447bb35d68888ull }, + { 0x4002be76c8b43958ull, 0x40144b54c7aa1dcdull }, + { 0x4002c083126e978dull, 0x40144eeefd138524ull }, + { 0x4002c28f5c28f5c3ull, 0x40145289d62fc82bull }, + { 0x4002c49ba5e353f8ull, 0x40145625531bf5a4ull }, + { 0x4002c6a7ef9db22dull, 0x401459c173f5217eull }, + { 0x4002c8b439581062ull, 0x40145d5e38d864cfull }, + { 0x4002cac083126e98ull, 0x401460fba1e2dddaull }, + { 0x4002cccccccccccdull, 0x40146499af31b007ull }, + { 0x4002ced916872b02ull, 0x4014683860e203f1ull }, + { 0x4002d0e560418937ull, 0x40146bd7b711075cull }, + { 0x4002d2f1a9fbe76dull, 0x40146f77b1dbed3dull }, + { 0x4002d4fdf3b645a2ull, 0x40147318515fedb3ull }, + { 0x4002d70a3d70a3d7ull, 0x401476b995ba4611ull }, + { 0x4002d916872b020cull, 0x40147a5b7f0838daull }, + { 0x4002db22d0e56042ull, 0x40147dfe0d670dc3ull }, + { 0x4002dd2f1a9fbe77ull, 0x401481a140f411afull }, + { 0x4002df3b645a1cacull, 0x4014854519cc96baull }, + { 0x4002e147ae147ae1ull, 0x401488e9980df433ull }, + { 0x4002e353f7ced917ull, 0x40148c8ebbd5869full }, + { 0x4002e5604189374cull, 0x401490348540afb4ull }, + { 0x4002e76c8b439581ull, 0x401493daf46cd666ull }, + { 0x4002e978d4fdf3b6ull, 0x40149782097766ddull }, + { 0x4002eb851eb851ecull, 0x40149b29c47dd27eull }, + { 0x4002ed916872b021ull, 0x40149ed2259d8fe1ull }, + { 0x4002ef9db22d0e56ull, 0x4014a27b2cf41adfull }, + { 0x4002f1a9fbe76c8bull, 0x4014a624da9ef48aull }, + { 0x4002f3b645a1cac1ull, 0x4014a9cf2ebba335ull }, + { 0x4002f5c28f5c28f6ull, 0x4014ad7a2967b268ull }, + { 0x4002f7ced916872bull, 0x4014b125cac0b2f2ull }, + { 0x4002f9db22d0e560ull, 0x4014b4d212e43addull }, + { 0x4002fbe76c8b4396ull, 0x4014b87f01efe577ull }, + { 0x4002fdf3b645a1cbull, 0x4014bc2c9801534aull }, + { 0x4003000000000000ull, 0x4014bfdad5362a27ull }, + { 0x4003020c49ba5e35ull, 0x4014c389b9ac1521ull }, + { 0x400304189374bc6bull, 0x4014c7394580c48full }, + { 0x40030624dd2f1aa0ull, 0x4014cae978d1ee0bull }, + { 0x4003083126e978d5ull, 0x4014ce9a53bd4c79ull }, + { 0x40030a3d70a3d70aull, 0x4014d24bd660a001ull }, + { 0x40030c49ba5e3540ull, 0x4014d5fe00d9ae17ull }, + { 0x40030e5604189375ull, 0x4014d9b0d3464170ull }, + { 0x400310624dd2f1aaull, 0x4014dd644dc42a11ull }, + { 0x4003126e978d4fdfull, 0x4014e11870713d4aull }, + { 0x4003147ae147ae15ull, 0x4014e4cd3b6b55b6ull }, + { 0x400316872b020c4aull, 0x4014e882aed05338ull }, + { 0x4003189374bc6a7full, 0x4014ec38cabe1b05ull }, + { 0x40031a9fbe76c8b4ull, 0x4014efef8f5297a2ull }, + { 0x40031cac083126eaull, 0x4014f3a6fcabb8e1ull }, + { 0x40031eb851eb851full, 0x4014f75f12e773e1ull }, + { 0x400320c49ba5e354ull, 0x4014fb17d223c319ull }, + { 0x400322d0e5604189ull, 0x4014fed13a7ea64eull }, + { 0x400324dd2f1a9fbfull, 0x4015028b4c16229bull }, + { 0x400326e978d4fdf4ull, 0x401506460708426aull }, + { 0x400328f5c28f5c29ull, 0x40150a016b731581ull }, + { 0x40032b020c49ba5eull, 0x40150dbd7974b0f6ull }, + { 0x40032d0e56041894ull, 0x4015117a312b2f3cull }, + { 0x40032f1a9fbe76c9ull, 0x4015153792b4b017ull }, + { 0x40033126e978d4feull, 0x401518f59e2f58a9ull }, + { 0x4003333333333333ull, 0x40151cb453b9536cull }, + { 0x4003353f7ced9169ull, 0x40152073b370d037ull }, + { 0x4003374bc6a7ef9eull, 0x40152433bd740438ull }, + { 0x4003395810624dd3ull, 0x401527f471e129feull }, + { 0x40033b645a1cac08ull, 0x40152bb5d0d68174ull }, + { 0x40033d70a3d70a3eull, 0x40152f77da724fe6ull }, + { 0x40033f7ced916873ull, 0x4015333a8ed2dff9ull }, + { 0x40034189374bc6a8ull, 0x401536fdee1681baull }, + { 0x40034395810624ddull, 0x40153ac1f85b8a92ull }, + { 0x400345a1cac08313ull, 0x40153e86adc05554ull }, + { 0x400347ae147ae148ull, 0x4015424c0e63422aull }, + { 0x400349ba5e353f7dull, 0x401546121a62b6aeull }, + { 0x40034bc6a7ef9db2ull, 0x401549d8d1dd1dd9ull }, + { 0x40034dd2f1a9fbe8ull, 0x40154da034f0e80full }, + { 0x40034fdf3b645a1dull, 0x4015516843bc8b14ull }, + { 0x400351eb851eb852ull, 0x40155530fe5e821aull }, + { 0x400353f7ced91687ull, 0x401558fa64f54dbaull }, + { 0x40035604189374bdull, 0x40155cc4779f73fbull }, + { 0x40035810624dd2f2ull, 0x4015608f367b8047ull }, + { 0x40035a1cac083127ull, 0x4015645aa1a8037aull }, + { 0x40035c28f5c28f5cull, 0x40156826b94393ddull }, + { 0x40035e353f7ced92ull, 0x40156bf37d6ccd26ull }, + { 0x4003604189374bc7ull, 0x40156fc0ee425076ull }, + { 0x4003624dd2f1a9fcull, 0x4015738f0be2c463ull }, + { 0x4003645a1cac0831ull, 0x4015775dd66cd4f3ull }, + { 0x4003666666666667ull, 0x40157b2d4dff339eull }, + { 0x40036872b020c49cull, 0x40157efd72b8974aull }, + { 0x40036a7ef9db22d1ull, 0x401582ce44b7bc56ull }, + { 0x40036c8b43958106ull, 0x4015869fc41b6494ull }, + { 0x40036e978d4fdf3cull, 0x40158a71f102574full }, + { 0x400370a3d70a3d71ull, 0x40158e44cb8b6140ull }, + { 0x400372b020c49ba6ull, 0x4015921853d5549full }, + { 0x400374bc6a7ef9dbull, 0x401595ec89ff091bull }, + { 0x400376c8b4395810ull, 0x401599c16e275bdcull }, + { 0x400378d4fdf3b646ull, 0x40159d97006d2f87ull }, + { 0x40037ae147ae147bull, 0x4015a16d40ef6c34ull }, + { 0x40037ced916872b0ull, 0x4015a5442fccff82ull }, + { 0x40037ef9db22d0e5ull, 0x4015a91bcd24dc88ull }, + { 0x4003810624dd2f1bull, 0x4015acf41915fbdfull }, + { 0x400383126e978d50ull, 0x4015b0cd13bf5b97ull }, + { 0x4003851eb851eb85ull, 0x4015b4a6bd3fff49ull }, + { 0x4003872b020c49baull, 0x4015b88115b6f00dull }, + { 0x400389374bc6a7f0ull, 0x4015bc5c1d433c7eull }, + { 0x40038b4395810625ull, 0x4015c037d403f8b6ull }, + { 0x40038d4fdf3b645aull, 0x4015c4143a183e57ull }, + { 0x40038f5c28f5c28full, 0x4015c7f14f9f2c88ull }, + { 0x4003916872b020c5ull, 0x4015cbcf14b7e7f7ull }, + { 0x40039374bc6a7efaull, 0x4015cfad89819ad6ull }, + { 0x400395810624dd2full, 0x4015d38cae1b74e2ull }, + { 0x4003978d4fdf3b64ull, 0x4015d76c82a4ab60ull }, + { 0x400399999999999aull, 0x4015db4d073c7921ull }, + { 0x40039ba5e353f7cfull, 0x4015df2e3c021e7dull }, + { 0x40039db22d0e5604ull, 0x4015e3102114e15cull }, + { 0x40039fbe76c8b439ull, 0x4015e6f2b6940d32ull }, + { 0x4003a1cac083126full, 0x4015ead5fc9ef303ull }, + { 0x4003a3d70a3d70a4ull, 0x4015eeb9f354e95cull }, + { 0x4003a5e353f7ced9ull, 0x4015f29e9ad54c5full }, + { 0x4003a7ef9db22d0eull, 0x4015f683f33f7dc0ull }, + { 0x4003a9fbe76c8b44ull, 0x4015fa69fcb2e4c4ull }, + { 0x4003ac083126e979ull, 0x4015fe50b74eee3eull }, + { 0x4003ae147ae147aeull, 0x4016023823330c9cull }, + { 0x4003b020c49ba5e3ull, 0x40160620407eb7deull }, + { 0x4003b22d0e560419ull, 0x40160a090f516d9cull }, + { 0x4003b4395810624eull, 0x40160df28fcab0ffull }, + { 0x4003b645a1cac083ull, 0x401611dcc20a0aceull }, + { 0x4003b851eb851eb8ull, 0x401615c7a62f0968ull }, + { 0x4003ba5e353f7ceeull, 0x401619b33c5940c6ull }, + { 0x4003bc6a7ef9db23ull, 0x40161d9f84a84a78ull }, + { 0x4003be76c8b43958ull, 0x4016218c7f3bc5afull }, + { 0x4003c083126e978dull, 0x4016257a2c335737ull }, + { 0x4003c28f5c28f5c3ull, 0x401629688baea97bull }, + { 0x4003c49ba5e353f8ull, 0x40162d579dcd6c81ull }, + { 0x4003c6a7ef9db22dull, 0x4016314762af55f4ull }, + { 0x4003c8b439581062ull, 0x40163537da74211dull }, + { 0x4003cac083126e98ull, 0x40163929053b8ee9ull }, + { 0x4003cccccccccccdull, 0x40163d1ae32565e5ull }, + { 0x4003ced916872b02ull, 0x4016410d74517243ull }, + { 0x4003d0e560418937ull, 0x40164500b8df85dcull }, + { 0x4003d2f1a9fbe76dull, 0x401648f4b0ef7830ull }, + { 0x4003d4fdf3b645a2ull, 0x40164ce95ca1265eull }, + { 0x4003d70a3d70a3d7ull, 0x401650debc147335ull }, + { 0x4003d916872b020cull, 0x401654d4cf69472bull }, + { 0x4003db22d0e56042ull, 0x401658cb96bf9060ull }, + { 0x4003dd2f1a9fbe77ull, 0x40165cc31237429cull }, + { 0x4003df3b645a1cacull, 0x401660bb41f05756ull }, + { 0x4003e147ae147ae1ull, 0x401664b4260acdb1ull }, + { 0x4003e353f7ced917ull, 0x401668adbea6aa81ull }, + { 0x4003e5604189374cull, 0x40166ca80be3f842ull }, + { 0x4003e76c8b439581ull, 0x401670a30de2c725ull }, + { 0x4003e978d4fdf3b6ull, 0x4016749ec4c32d0eull }, + { 0x4003eb851eb851ecull, 0x4016789b30a54590ull }, + { 0x4003ed916872b021ull, 0x40167c9851a931edull }, + { 0x4003ef9db22d0e56ull, 0x4016809627ef1923ull }, + { 0x4003f1a9fbe76c8bull, 0x40168494b39727e1ull }, + { 0x4003f3b645a1cac1ull, 0x40168893f4c1908dull }, + { 0x4003f5c28f5c28f6ull, 0x40168c93eb8e8b41ull }, + { 0x4003f7ced916872bull, 0x40169094981e55d3ull }, + { 0x4003f9db22d0e560ull, 0x40169495fa9133d1ull }, + { 0x4003fbe76c8b4396ull, 0x4016989813076e85ull }, + { 0x4003fdf3b645a1cbull, 0x40169c9ae1a154eeull }, + { 0x4004000000000000ull, 0x4016a09e667f3bcdull }, + { 0x4004020c49ba5e35ull, 0x4016a4a2a1c17d9eull }, + { 0x400404189374bc6bull, 0x4016a8a793887a9eull }, + { 0x40040624dd2f1aa0ull, 0x4016acad3bf498c2ull }, + { 0x4004083126e978d5ull, 0x4016b0b39b2643c8ull }, + { 0x40040a3d70a3d70aull, 0x4016b4bab13ded2aull }, + { 0x40040c49ba5e3540ull, 0x4016b8c27e5c0c27ull }, + { 0x40040e5604189375ull, 0x4016bccb02a11dbdull }, + { 0x400410624dd2f1aaull, 0x4016c0d43e2da4b2ull }, + { 0x4004126e978d4fdfull, 0x4016c4de31222992ull }, + { 0x4004147ae147ae15ull, 0x4016c8e8db9f3aafull }, + { 0x400416872b020c4aull, 0x4016ccf43dc56c1eull }, + { 0x4004189374bc6a7full, 0x4016d10057b557c1ull }, + { 0x40041a9fbe76c8b4ull, 0x4016d50d298f9d43ull }, + { 0x40041cac083126eaull, 0x4016d91ab374e219ull }, + { 0x40041eb851eb851full, 0x4016dd28f585d182ull }, + { 0x400420c49ba5e354ull, 0x4016e137efe31c8aull }, + { 0x400422d0e5604189ull, 0x4016e547a2ad7a0dull }, + { 0x400424dd2f1a9fbfull, 0x4016e9580e05a6b6ull }, + { 0x400426e978d4fdf4ull, 0x4016ed69320c64f8ull }, + { 0x400428f5c28f5c29ull, 0x4016f17b0ee27d1full }, + { 0x40042b020c49ba5eull, 0x4016f58da4a8bd47ull }, + { 0x40042d0e56041894ull, 0x4016f9a0f37ff95eull }, + { 0x40042f1a9fbe76c9ull, 0x4016fdb4fb890b22ull }, + { 0x40043126e978d4feull, 0x401701c9bce4d22cull }, + { 0x4004333333333333ull, 0x401705df37b433e6ull }, + { 0x4004353f7ced9169ull, 0x401709f56c181b97ull }, + { 0x4004374bc6a7ef9eull, 0x40170e0c5a317a54ull }, + { 0x4004395810624dd3ull, 0x4017122402214714ull }, + { 0x40043b645a1cac08ull, 0x4017163c64087ea4ull }, + { 0x40043d70a3d70a3eull, 0x40171a55800823afull }, + { 0x40043f7ced916873ull, 0x40171e6f56413eb5ull }, + { 0x40044189374bc6a8ull, 0x40172289e6d4de1aull }, + { 0x40044395810624ddull, 0x401726a531e4161full }, + { 0x400445a1cac08313ull, 0x40172ac1379000e4ull }, + { 0x400447ae147ae148ull, 0x40172eddf7f9be65ull }, + { 0x400449ba5e353f7dull, 0x401732fb73427484ull }, + { 0x40044bc6a7ef9db2ull, 0x40173719a98b4f04ull }, + { 0x40044dd2f1a9fbe8ull, 0x40173b389af57f8eull }, + { 0x40044fdf3b645a1dull, 0x40173f5847a23da8ull }, + { 0x400451eb851eb852ull, 0x40174378afb2c6c5ull }, + { 0x400453f7ced91687ull, 0x40174799d3485e3aull }, + { 0x40045604189374bdull, 0x40174bbbb2844d48ull }, + { 0x40045810624dd2f2ull, 0x40174fde4d87e312ull }, + { 0x40045a1cac083127ull, 0x40175401a47474aaull }, + { 0x40045c28f5c28f5cull, 0x40175825b76b5d0aull }, + { 0x40045e353f7ced92ull, 0x40175c4a868dfd1dull }, + { 0x4004604189374bc7ull, 0x4017607011fdbbb0ull }, + { 0x4004624dd2f1a9fcull, 0x4017649659dc0588ull }, + { 0x4004645a1cac0831ull, 0x401768bd5e4a4d55ull }, + { 0x4004666666666667ull, 0x40176ce51f6a0bb9ull }, + { 0x40046872b020c49cull, 0x4017710d9d5cbf41ull }, + { 0x40046a7ef9db22d1ull, 0x40177536d843ec73ull }, + { 0x40046c8b43958106ull, 0x40177960d0411dc4ull }, + { 0x40046e978d4fdf3cull, 0x40177d8b8575e3a1ull }, + { 0x400470a3d70a3d71ull, 0x401781b6f803d466ull }, + { 0x400472b020c49ba6ull, 0x401785e3280c8c6bull }, + { 0x400474bc6a7ef9dbull, 0x40178a1015b1adfdull }, + { 0x400476c8b4395811ull, 0x40178e3dc114e164ull }, + { 0x400478d4fdf3b646ull, 0x4017926c2a57d4daull }, + { 0x40047ae147ae147bull, 0x4017969b519c3c9cull }, + { 0x40047ced916872b0ull, 0x40179acb3703d2dfull }, + { 0x40047ef9db22d0e5ull, 0x40179efbdab057d5ull }, + { 0x4004810624dd2f1bull, 0x4017a32d3cc391afull }, + { 0x400483126e978d50ull, 0x4017a75f5d5f4c97ull }, + { 0x4004851eb851eb85ull, 0x4017ab923ca55abeull }, + { 0x4004872b020c49baull, 0x4017afc5dab79453ull }, + { 0x400489374bc6a7f0ull, 0x4017b3fa37b7d788ull }, + { 0x40048b4395810625ull, 0x4017b82f53c8088eull }, + { 0x40048d4fdf3b645aull, 0x4017bc652f0a119full }, + { 0x40048f5c28f5c28full, 0x4017c09bc99fe2f7ull }, + { 0x4004916872b020c5ull, 0x4017c4d323ab72ddull }, + { 0x40049374bc6a7efaull, 0x4017c90b3d4ebd97ull }, + { 0x400495810624dd2full, 0x4017cd4416abc57bull }, + { 0x4004978d4fdf3b64ull, 0x4017d17dafe492e4ull }, + { 0x400499999999999aull, 0x4017d5b8091b343dull }, + { 0x40049ba5e353f7cfull, 0x4017d9f32271bdf3ull }, + { 0x40049db22d0e5604ull, 0x4017de2efc0a4a87ull }, + { 0x40049fbe76c8b439ull, 0x4017e26b9606fa88ull }, + { 0x4004a1cac083126full, 0x4017e6a8f089f493ull }, + { 0x4004a3d70a3d70a4ull, 0x4017eae70bb5654full }, + { 0x4004a5e353f7ced9ull, 0x4017ef25e7ab7f7bull }, + { 0x4004a7ef9db22d0eull, 0x4017f365848e7be6ull }, + { 0x4004a9fbe76c8b44ull, 0x4017f7a5e2809974ull }, + { 0x4004ac083126e979ull, 0x4017fbe701a41d16ull }, + { 0x4004ae147ae147aeull, 0x40180028e21b51daull }, + { 0x4004b020c49ba5e3ull, 0x4018046b840888e1ull }, + { 0x4004b22d0e560419ull, 0x401808aee78e1967ull }, + { 0x4004b4395810624eull, 0x40180cf30cce60b9ull }, + { 0x4004b645a1cac083ull, 0x40181137f3ebc243ull }, + { 0x4004b851eb851eb8ull, 0x4018157d9d08a78dull }, + { 0x4004ba5e353f7ceeull, 0x401819c408478039ull }, + { 0x4004bc6a7ef9db23ull, 0x40181e0b35cac201ull }, + { 0x4004be76c8b43958ull, 0x4018225325b4e8c5ull }, + { 0x4004c083126e978dull, 0x4018269bd828767eull }, + { 0x4004c28f5c28f5c3ull, 0x40182ae54d47f34cull }, + { 0x4004c49ba5e353f8ull, 0x40182f2f8535ed65ull }, + { 0x4004c6a7ef9db22dull, 0x4018337a8014f92cull }, + { 0x4004c8b439581062ull, 0x401837c63e07b121ull }, + { 0x4004cac083126e98ull, 0x40183c12bf30b5efull }, + { 0x4004cccccccccccdull, 0x4018406003b2ae5dull }, + { 0x4004ced916872b02ull, 0x401844ae0bb0475full }, + { 0x4004d0e560418937ull, 0x401848fcd74c3412ull }, + { 0x4004d2f1a9fbe76dull, 0x40184d4c66a92db9ull }, + { 0x4004d4fdf3b645a2ull, 0x4018519cb9e9f3bdull }, + { 0x4004d70a3d70a3d7ull, 0x401855edd1314bbbull }, + { 0x4004d916872b020cull, 0x40185a3faca20175ull }, + { 0x4004db22d0e56042ull, 0x40185e924c5ee6dfull }, + { 0x4004dd2f1a9fbe77ull, 0x401862e5b08ad415ull }, + { 0x4004df3b645a1cacull, 0x40186739d948a769ull }, + { 0x4004e147ae147ae1ull, 0x40186b8ec6bb4559ull }, + { 0x4004e353f7ced917ull, 0x40186fe47905989bull }, + { 0x4004e5604189374cull, 0x4018743af04a920cull }, + { 0x4004e76c8b439581ull, 0x401878922cad28c7ull }, + { 0x4004e978d4fdf3b6ull, 0x40187cea2e505a19ull }, + { 0x4004eb851eb851ecull, 0x40188142f5572987ull }, + { 0x4004ed916872b021ull, 0x4018859c81e4a0c5ull }, + { 0x4004ef9db22d0e56ull, 0x401889f6d41bcfc9ull }, + { 0x4004f1a9fbe76c8bull, 0x40188e51ec1fccbcull }, + { 0x4004f3b645a1cac1ull, 0x401892adca13b408ull }, + { 0x4004f5c28f5c28f6ull, 0x4018970a6e1aa848ull }, + { 0x4004f7ced916872bull, 0x40189b67d857d25dull }, + { 0x4004f9db22d0e560ull, 0x40189fc608ee6162ull }, + { 0x4004fbe76c8b4396ull, 0x4018a42500018ab4ull }, + { 0x4004fdf3b645a1cbull, 0x4018a884bdb489e8ull }, + { 0x4005000000000000ull, 0x4018ace5422aa0dbull }, + { 0x4005020c49ba5e35ull, 0x4018b1468d8717acull }, + { 0x400504189374bc6bull, 0x4018b5a89fed3cbdull }, + { 0x40050624dd2f1aa0ull, 0x4018ba0b798064aeull }, + { 0x4005083126e978d5ull, 0x4018be6f1a63ea6dull }, + { 0x40050a3d70a3d70aull, 0x4018c2d382bb2f2bull }, + { 0x40050c49ba5e3540ull, 0x4018c738b2a99a62ull }, + { 0x40050e5604189375ull, 0x4018cb9eaa5299d0ull }, + { 0x400510624dd2f1aaull, 0x4018d00569d9a182ull }, + { 0x4005126e978d4fdfull, 0x4018d46cf1622bd0ull }, + { 0x4005147ae147ae15ull, 0x4018d8d5410fb95eull }, + { 0x400516872b020c4aull, 0x4018dd3e5905d119ull }, + { 0x4005189374bc6a7full, 0x4018e1a839680041ull }, + { 0x40051a9fbe76c8b4ull, 0x4018e612e259da63ull }, + { 0x40051cac083126eaull, 0x4018ea7e53fef962ull }, + { 0x40051eb851eb851full, 0x4018eeea8e7afd69ull }, + { 0x400520c49ba5e354ull, 0x4018f35791f18cfeull }, + { 0x400522d0e5604189ull, 0x4018f7c55e8654f9ull }, + { 0x400524dd2f1a9fbfull, 0x4018fc33f45d0887ull }, + { 0x400526e978d4fdf4ull, 0x401900a353996129ull }, + { 0x400528f5c28f5c29ull, 0x401905137c5f1eb9ull }, + { 0x40052b020c49ba5eull, 0x401909846ed2076cull }, + { 0x40052d0e56041894ull, 0x40190df62b15e7cfull }, + { 0x40052f1a9fbe76c9ull, 0x40191268b14e92c6ull }, + { 0x40053126e978d4feull, 0x401916dc019fe196ull }, + { 0x4005333333333333ull, 0x40191b501c2db3deull }, + { 0x4005353f7ced9169ull, 0x40191fc5011befa0ull }, + { 0x4005374bc6a7ef9eull, 0x4019243ab08e8135ull }, + { 0x4005395810624dd3ull, 0x401928b12aa95b5dull }, + { 0x40053b645a1cac08ull, 0x40192d286f907737ull }, + { 0x40053d70a3d70a3eull, 0x401931a07f67d448ull }, + { 0x40053f7ced916873ull, 0x401936195a537872ull }, + { 0x40054189374bc6a8ull, 0x40193a9300777002ull }, + { 0x40054395810624ddull, 0x40193f0d71f7cda9ull }, + { 0x400545a1cac08313ull, 0x40194388aef8aa81ull }, + { 0x400547ae147ae148ull, 0x40194804b79e2607ull }, + { 0x400549ba5e353f7dull, 0x40194c818c0c6628ull }, + { 0x40054bc6a7ef9db2ull, 0x401950ff2c679737ull }, + { 0x40054dd2f1a9fbe8ull, 0x4019557d98d3ebf9ull }, + { 0x40054fdf3b645a1dull, 0x401959fcd1759d96ull }, + { 0x400551eb851eb852ull, 0x40195e7cd670ebabull }, + { 0x400553f7ced91687ull, 0x401962fda7ea1c44ull }, + { 0x40055604189374bdull, 0x4019677f46057bdfull }, + { 0x40055810624dd2f2ull, 0x40196c01b0e75d62ull }, + { 0x40055a1cac083127ull, 0x40197084e8b41a31ull }, + { 0x40055c28f5c28f5cull, 0x40197508ed90121cull }, + { 0x40055e353f7ced92ull, 0x4019798dbf9fab70ull }, + { 0x4005604189374bc7ull, 0x40197e135f0752e5ull }, + { 0x4005624dd2f1a9fcull, 0x40198299cbeb7bb3ull }, + { 0x4005645a1cac0831ull, 0x4019872106709f87ull }, + { 0x4005666666666667ull, 0x40198ba90ebb3e8bull }, + { 0x40056872b020c49cull, 0x40199031e4efdf5bull }, + { 0x40056a7ef9db22d1ull, 0x401994bb89330f19ull }, + { 0x40056c8b43958106ull, 0x40199945fba9615dull }, + { 0x40056e978d4fdf3cull, 0x40199dd13c777043ull }, + { 0x400570a3d70a3d71ull, 0x4019a25d4bc1dc5eull }, + { 0x400572b020c49ba6ull, 0x4019a6ea29ad4ccaull }, + { 0x400574bc6a7ef9dbull, 0x4019ab77d65e6f1eull }, + { 0x400576c8b4395811ull, 0x4019b00651f9f77bull }, + { 0x400578d4fdf3b646ull, 0x4019b4959ca4a07dull }, + { 0x40057ae147ae147bull, 0x4019b925b6832b4aull }, + { 0x40057ced916872b0ull, 0x4019bdb69fba5f8full }, + { 0x40057ef9db22d0e6ull, 0x4019c248586f0b80ull }, + { 0x4005810624dd2f1bull, 0x4019c6dae0c603d6ull }, + { 0x400583126e978d50ull, 0x4019cb6e38e423d7ull }, + { 0x4005851eb851eb85ull, 0x4019d00260ee4d52ull }, + { 0x4005872b020c49baull, 0x4019d497590968a6ull }, + { 0x400589374bc6a7f0ull, 0x4019d92d215a64bbull }, + { 0x40058b4395810625ull, 0x4019ddc3ba063707ull }, + { 0x40058d4fdf3b645aull, 0x4019e25b2331db91ull }, + { 0x40058f5c28f5c28full, 0x4019e6f35d0254f1ull }, + { 0x4005916872b020c5ull, 0x4019eb8c679cac53ull }, + { 0x40059374bc6a7efaull, 0x4019f0264325f16full }, + { 0x400595810624dd2full, 0x4019f4c0efc33a98ull }, + { 0x4005978d4fdf3b64ull, 0x4019f95c6d99a4b3ull }, + { 0x400599999999999aull, 0x4019fdf8bcce533eull }, + { 0x40059ba5e353f7cfull, 0x401a0295dd86704aull }, + { 0x40059db22d0e5604ull, 0x401a0733cfe72c86ull }, + { 0x40059fbe76c8b439ull, 0x401a0bd29415bf37ull }, + { 0x4005a1cac083126full, 0x401a10722a376642ull }, + { 0x4005a3d70a3d70a4ull, 0x401a151292716622ull }, + { 0x4005a5e353f7ced9ull, 0x401a19b3cce909f3ull }, + { 0x4005a7ef9db22d0eull, 0x401a1e55d9c3a370ull }, + { 0x4005a9fbe76c8b44ull, 0x401a22f8b9268af6ull }, + { 0x4005ac083126e979ull, 0x401a279c6b371f7cull }, + { 0x4005ae147ae147aeull, 0x401a2c40f01ac6a1ull }, + { 0x4005b020c49ba5e3ull, 0x401a30e647f6eca7ull }, + { 0x4005b22d0e560419ull, 0x401a358c72f10474ull }, + { 0x4005b4395810624eull, 0x401a3a33712e8790ull }, + { 0x4005b645a1cac083ull, 0x401a3edb42d4f62full }, + { 0x4005b851eb851eb8ull, 0x401a4383e809d72bull }, + { 0x4005ba5e353f7ceeull, 0x401a482d60f2b80aull }, + { 0x4005bc6a7ef9db23ull, 0x401a4cd7adb52cf6ull }, + { 0x4005be76c8b43958ull, 0x401a5182ce76d0caull }, + { 0x4005c083126e978dull, 0x401a562ec35d450dull }, + { 0x4005c28f5c28f5c3ull, 0x401a5adb8c8e31f5ull }, + { 0x4005c49ba5e353f8ull, 0x401a5f892a2f4662ull }, + { 0x4005c6a7ef9db22dull, 0x401a64379c6637ebull }, + { 0x4005c8b439581062ull, 0x401a68e6e358c2d4ull }, + { 0x4005cac083126e98ull, 0x401a6d96ff2caa18ull }, + { 0x4005cccccccccccdull, 0x401a7247f007b760ull }, + { 0x4005ced916872b02ull, 0x401a76f9b60fbb0full }, + { 0x4005d0e560418937ull, 0x401a7bac516a8c3dull }, + { 0x4005d2f1a9fbe76dull, 0x401a805fc23e08bbull }, + { 0x4005d4fdf3b645a2ull, 0x401a851408b0150eull }, + { 0x4005d70a3d70a3d7ull, 0x401a89c924e69c79ull }, + { 0x4005d916872b020cull, 0x401a8e7f170790faull }, + { 0x4005db22d0e56042ull, 0x401a9335df38eb4cull }, + { 0x4005dd2f1a9fbe77ull, 0x401a97ed7da0aae3ull }, + { 0x4005df3b645a1cacull, 0x401a9ca5f264d5f4ull }, + { 0x4005e147ae147ae1ull, 0x401aa15f3dab7977ull }, + { 0x4005e353f7ced917ull, 0x401aa6195f9aa925ull }, + { 0x4005e5604189374cull, 0x401aaad458587f70ull }, + { 0x4005e76c8b439581ull, 0x401aaf90280b1d99ull }, + { 0x4005e978d4fdf3b6ull, 0x401ab44cced8aba0ull }, + { 0x4005eb851eb851ecull, 0x401ab90a4ce7584full }, + { 0x4005ed916872b021ull, 0x401abdc8a25d592full }, + { 0x4005ef9db22d0e56ull, 0x401ac287cf60ea98ull }, + { 0x4005f1a9fbe76c8bull, 0x401ac747d4184fabull }, + { 0x4005f3b645a1cac1ull, 0x401acc08b0a9d255ull }, + { 0x4005f5c28f5c28f6ull, 0x401ad0ca653bc346ull }, + { 0x4005f7ced916872bull, 0x401ad58cf1f47a06ull }, + { 0x4005f9db22d0e560ull, 0x401ada5056fa54e6ull }, + { 0x4005fbe76c8b4396ull, 0x401adf149473b90aull }, + { 0x4005fdf3b645a1cbull, 0x401ae3d9aa871260ull }, + { 0x4006000000000000ull, 0x401ae89f995ad3adull }, + { 0x4006020c49ba5e35ull, 0x401aed666115768aull }, + { 0x400604189374bc6bull, 0x401af22e01dd7b64ull }, + { 0x40060624dd2f1aa0ull, 0x401af6f67bd96978ull }, + { 0x4006083126e978d5ull, 0x401afbbfcf2fcee0ull }, + { 0x40060a3d70a3d70aull, 0x401b0089fc07408dull }, + { 0x40060c49ba5e3540ull, 0x401b055502865a4aull }, + { 0x40060e5604189375ull, 0x401b0a20e2d3beb6ull }, + { 0x400610624dd2f1aaull, 0x401b0eed9d161753ull }, + { 0x4006126e978d4fdfull, 0x401b13bb3174147cull }, + { 0x4006147ae147ae15ull, 0x401b1889a0146d70ull }, + { 0x400616872b020c4aull, 0x401b1d58e91de043ull }, + { 0x4006189374bc6a7full, 0x401b22290cb731f1ull }, + { 0x40061a9fbe76c8b4ull, 0x401b26fa0b072e57ull }, + { 0x40061cac083126eaull, 0x401b2bcbe434a836ull }, + { 0x40061eb851eb851full, 0x401b309e9866792bull }, + { 0x400620c49ba5e354ull, 0x401b357227c381c4ull }, + { 0x400622d0e5604189ull, 0x401b3a469272a96dull }, + { 0x400624dd2f1a9fbfull, 0x401b3f1bd89ade81ull }, + { 0x400626e978d4fdf4ull, 0x401b43f1fa63163cull }, + { 0x400628f5c28f5c29ull, 0x401b48c8f7f24ccaull }, + { 0x40062b020c49ba5eull, 0x401b4da0d16f8542ull }, + { 0x40062d0e56041894ull, 0x401b52798701c9a8ull }, + { 0x40062f1a9fbe76c9ull, 0x401b575318d02ae8ull }, + { 0x40063126e978d4feull, 0x401b5c2d8701c0e6ull }, + { 0x4006333333333333ull, 0x401b6108d1bdaa72ull }, + { 0x4006353f7ced9169ull, 0x401b65e4f92b0d50ull }, + { 0x4006374bc6a7ef9eull, 0x401b6ac1fd711631ull }, + { 0x4006395810624dd3ull, 0x401b6f9fdeb6f8c0ull }, + { 0x40063b645a1cac08ull, 0x401b747e9d23ef9dull }, + { 0x40063d70a3d70a3eull, 0x401b795e38df3c60ull }, + { 0x40063f7ced916873ull, 0x401b7e3eb2102790ull }, + { 0x40064189374bc6a8ull, 0x401b832008de00b6ull }, + { 0x40064395810624ddull, 0x401b88023d701e54ull }, + { 0x400645a1cac08313ull, 0x401b8ce54feddde7ull }, + { 0x400647ae147ae148ull, 0x401b91c9407ea3e5ull }, + { 0x400649ba5e353f7dull, 0x401b96ae0f49dbc7ull }, + { 0x40064bc6a7ef9db2ull, 0x401b9b93bc76f804ull }, + { 0x40064dd2f1a9fbe8ull, 0x401ba07a482d7214ull }, + { 0x40064fdf3b645a1dull, 0x401ba561b294ca6dull }, + { 0x400651eb851eb852ull, 0x401baa49fbd4888cull }, + { 0x400653f7ced91687ull, 0x401baf3324143af2ull }, + { 0x40065604189374bdull, 0x401bb41d2b7b7726ull }, + { 0x40065810624dd2f2ull, 0x401bb9081231d9b1ull }, + { 0x40065a1cac083127ull, 0x401bbdf3d85f0629ull }, + { 0x40065c28f5c28f5cull, 0x401bc2e07e2aa72dull }, + { 0x40065e353f7ced92ull, 0x401bc7ce03bc6e66ull }, + { 0x4006604189374bc7ull, 0x401bccbc693c1484ull }, + { 0x4006624dd2f1a9fcull, 0x401bd1abaed1594bull }, + { 0x4006645a1cac0831ull, 0x401bd69bd4a4038aull }, + { 0x4006666666666667ull, 0x401bdb8cdadbe122ull }, + { 0x40066872b020c49cull, 0x401be07ec1a0c6feull }, + { 0x40066a7ef9db22d1ull, 0x401be571891a9122ull }, + { 0x40066c8b43958106ull, 0x401bea65317122a3ull }, + { 0x40066e978d4fdf3cull, 0x401bef59bacc65aeull }, + { 0x400670a3d70a3d71ull, 0x401bf44f25544b7cull }, + { 0x400672b020c49ba6ull, 0x401bf9457130cc66ull }, + { 0x400674bc6a7ef9dbull, 0x401bfe3c9e89e7dbull }, + { 0x400676c8b4395811ull, 0x401c0334ad87a466ull }, + { 0x400678d4fdf3b646ull, 0x401c082d9e520fa5ull }, + { 0x40067ae147ae147bull, 0x401c0d2771113e59ull }, + { 0x40067ced916872b0ull, 0x401c122225ed4c60ull }, + { 0x40067ef9db22d0e6ull, 0x401c171dbd0e5cb5ull }, + { 0x4006810624dd2f1bull, 0x401c1c1a369c9971ull }, + { 0x400683126e978d50ull, 0x401c211792c033d1ull }, + { 0x4006851eb851eb85ull, 0x401c2615d1a16434ull }, + { 0x4006872b020c49baull, 0x401c2b14f3686a1dull }, + { 0x400689374bc6a7f0ull, 0x401c3014f83d8c34ull }, + { 0x40068b4395810625ull, 0x401c3515e0491843ull }, + { 0x40068d4fdf3b645aull, 0x401c3a17abb36340ull }, + { 0x40068f5c28f5c28full, 0x401c3f1a5aa4c94aull }, + { 0x4006916872b020c5ull, 0x401c441ded45adabull }, + { 0x40069374bc6a7efaull, 0x401c492263be7ad0ull }, + { 0x400695810624dd2full, 0x401c4e27be37a25dull }, + { 0x4006978d4fdf3b64ull, 0x401c532dfcd99d1dull }, + { 0x400699999999999aull, 0x401c58351fcceb11ull }, + { 0x40069ba5e353f7cfull, 0x401c5d3d273a135full }, + { 0x40069db22d0e5604ull, 0x401c62461349a46aull }, + { 0x40069fbe76c8b439ull, 0x401c674fe42433c3ull }, + { 0x4006a1cac083126full, 0x401c6c5a99f25e32ull }, + { 0x4006a3d70a3d70a4ull, 0x401c716634dcc7adull }, + { 0x4006a5e353f7ced9ull, 0x401c7672b50c1b68ull }, + { 0x4006a7ef9db22d0eull, 0x401c7b801aa90bcfull }, + { 0x4006a9fbe76c8b44ull, 0x401c808e65dc5286ull }, + { 0x4006ac083126e979ull, 0x401c859d96ceb067ull }, + { 0x4006ae147ae147aeull, 0x401c8aadada8ed8full }, + { 0x4006b020c49ba5e3ull, 0x401c8fbeaa93d954ull }, + { 0x4006b22d0e560419ull, 0x401c94d08db84a4full }, + { 0x4006b4395810624eull, 0x401c99e3573f1e50ull }, + { 0x4006b645a1cac083ull, 0x401c9ef707513a70ull }, + { 0x4006b851eb851eb8ull, 0x401ca40b9e178b08ull }, + { 0x4006ba5e353f7ceeull, 0x401ca9211bbb03b7ull }, + { 0x4006bc6a7ef9db23ull, 0x401cae3780649f58ull }, + { 0x4006be76c8b43958ull, 0x401cb34ecc3d6017ull }, + { 0x4006c083126e978dull, 0x401cb866ff6e4f63ull }, + { 0x4006c28f5c28f5c3ull, 0x401cbd801a207df4ull }, + { 0x4006c49ba5e353f8ull, 0x401cc29a1c7d03caull }, + { 0x4006c6a7ef9db22dull, 0x401cc7b506ad0032ull }, + { 0x4006c8b439581062ull, 0x401cccd0d8d999c8ull }, + { 0x4006cac083126e98ull, 0x401cd1ed932bfe76ull }, + { 0x4006cccccccccccdull, 0x401cd70b35cd636dull }, + { 0x4006ced916872b02ull, 0x401cdc29c0e70539ull }, + { 0x4006d0e560418937ull, 0x401ce14934a227b2ull }, + { 0x4006d2f1a9fbe76dull, 0x401ce66991281608ull }, + { 0x4006d4fdf3b645a2ull, 0x401ceb8ad6a222b8ull }, + { 0x4006d70a3d70a3d7ull, 0x401cf0ad0539a79bull }, + { 0x4006d916872b020cull, 0x401cf5d01d1805deull }, + { 0x4006db22d0e56042ull, 0x401cfaf41e66a60cull }, + { 0x4006dd2f1a9fbe77ull, 0x401d0019094ef7feull }, + { 0x4006df3b645a1cacull, 0x401d053eddfa72f3ull }, + { 0x4006e147ae147ae1ull, 0x401d0a659c929581ull }, + { 0x4006e353f7ced917ull, 0x401d0f8d4540e5a1ull }, + { 0x4006e5604189374cull, 0x401d14b5d82ef0a1ull }, + { 0x4006e76c8b439581ull, 0x401d19df55864b38ull }, + { 0x4006e978d4fdf3b6ull, 0x401d1f09bd70917bull }, + { 0x4006eb851eb851ecull, 0x401d2435101766e5ull }, + { 0x4006ed916872b021ull, 0x401d29614da4764dull }, + { 0x4006ef9db22d0e56ull, 0x401d2e8e764171f9ull }, + { 0x4006f1a9fbe76c8bull, 0x401d33bc8a181390ull }, + { 0x4006f3b645a1cac1ull, 0x401d38eb89521c27ull }, + { 0x4006f5c28f5c28f6ull, 0x401d3e1b74195431ull }, + { 0x4006f7ced916872bull, 0x401d434c4a978b97ull }, + { 0x4006f9db22d0e560ull, 0x401d487e0cf699aaull }, + { 0x4006fbe76c8b4396ull, 0x401d4db0bb605d29ull }, + { 0x4006fdf3b645a1cbull, 0x401d52e455febc3eull }, + { 0x4007000000000000ull, 0x401d5818dcfba487ull }, + { 0x4007020c49ba5e35ull, 0x401d5d4e50810b13ull }, + { 0x400704189374bc6bull, 0x401d6284b0b8ec64ull }, + { 0x40070624dd2f1aa0ull, 0x401d67bbfdcd4c6bull }, + { 0x4007083126e978d5ull, 0x401d6cf437e83693ull }, + { 0x40070a3d70a3d70aull, 0x401d722d5f33bdbfull }, + { 0x40070c49ba5e3540ull, 0x401d776773d9fc47ull }, + { 0x40070e5604189375ull, 0x401d7ca2760513f9ull }, + { 0x400710624dd2f1aaull, 0x401d81de65df2e25ull }, + { 0x4007126e978d4fdfull, 0x401d871b43927b91ull }, + { 0x4007147ae147ae15ull, 0x401d8c590f493486ull }, + { 0x400716872b020c4aull, 0x401d9197c92d98c2ull }, + { 0x4007189374bc6a7full, 0x401d96d77169ef8dull }, + { 0x40071a9fbe76c8b4ull, 0x401d9c18082887aaull }, + { 0x40071cac083126eaull, 0x401da1598d93b764ull }, + { 0x40071eb851eb851full, 0x401da69c01d5dc81ull }, + { 0x400720c49ba5e354ull, 0x401dabdf65195c55ull }, + { 0x400722d0e5604189ull, 0x401db123b788a3b6ull }, + { 0x400724dd2f1a9fbfull, 0x401db668f94e2706ull }, + { 0x400726e978d4fdf4ull, 0x401dbbaf2a946229ull }, + { 0x400728f5c28f5c29ull, 0x401dc0f64b85d892ull }, + { 0x40072b020c49ba5eull, 0x401dc63e5c4d1542ull }, + { 0x40072d0e56041894ull, 0x401dcb875d14aac7ull }, + { 0x40072f1a9fbe76c9ull, 0x401dd0d14e073335ull }, + { 0x40073126e978d4feull, 0x401dd61c2f4f503aull }, + { 0x4007333333333333ull, 0x401ddb680117ab12ull }, + { 0x4007353f7ced9169ull, 0x401de0b4c38af48cull }, + { 0x4007374bc6a7ef9eull, 0x401de60276d3e508ull }, + { 0x4007395810624dd3ull, 0x401deb511b1d3c7dull }, + { 0x40073b645a1cac08ull, 0x401df0a0b091c27bull }, + { 0x40073d70a3d70a3eull, 0x401df5f1375c462aull }, + { 0x40073f7ced916873ull, 0x401dfb42afa79e44ull }, + { 0x40074189374bc6a8ull, 0x401e0095199ea926ull }, + { 0x40074395810624ddull, 0x401e05e8756c4cc6ull }, + { 0x400745a1cac08313ull, 0x401e0b3cc33b76baull }, + { 0x400747ae147ae148ull, 0x401e109203371c30ull }, + { 0x400749ba5e353f7dull, 0x401e15e8358a39fbull }, + { 0x40074bc6a7ef9db2ull, 0x401e1b3f5a5fd490ull }, + { 0x40074dd2f1a9fbe8ull, 0x401e209771e2f808ull }, + { 0x40074fdf3b645a1dull, 0x401e25f07c3eb818ull }, + { 0x400751eb851eb852ull, 0x401e2b4a799e3023ull }, + { 0x400753f7ced91687ull, 0x401e30a56a2c8330ull }, + { 0x40075604189374bdull, 0x401e36014e14dbf1ull }, + { 0x40075810624dd2f2ull, 0x401e3b5e25826cb9ull }, + { 0x40075a1cac083127ull, 0x401e40bbf0a06f90ull }, + { 0x40075c28f5c28f5cull, 0x401e461aaf9a2624ull }, + { 0x40075e353f7ced92ull, 0x401e4b7a629ad9d6ull }, + { 0x4007604189374bc7ull, 0x401e50db09cddbaeull }, + { 0x4007624dd2f1a9fcull, 0x401e563ca55e846bull }, + { 0x4007645a1cac0831ull, 0x401e5b9f3578347dull }, + { 0x4007666666666667ull, 0x401e6102ba465407ull }, + { 0x40076872b020c49cull, 0x401e666733f452dbull }, + { 0x40076a7ef9db22d1ull, 0x401e6bcca2ada889ull }, + { 0x40076c8b43958106ull, 0x401e7133069dd452ull }, + { 0x40076e978d4fdf3cull, 0x401e769a5ff05d37ull }, + { 0x400770a3d70a3d71ull, 0x401e7c02aed0d1e8ull }, + { 0x400772b020c49ba6ull, 0x401e816bf36ac8daull }, + { 0x400774bc6a7ef9dbull, 0x401e86d62de9e039ull }, + { 0x400776c8b4395811ull, 0x401e8c415e79bdf5ull }, + { 0x400778d4fdf3b646ull, 0x401e91ad85460fb5ull }, + { 0x40077ae147ae147bull, 0x401e971aa27a8ae7ull }, + { 0x40077ced916872b0ull, 0x401e9c88b642ecbbull }, + { 0x40077ef9db22d0e6ull, 0x401ea1f7c0cafa25ull }, + { 0x4007810624dd2f1bull, 0x401ea767c23e7fd6ull }, + { 0x400783126e978d50ull, 0x401eacd8bac95250ull }, + { 0x4007851eb851eb85ull, 0x401eb24aaa974dd7ull }, + { 0x4007872b020c49bbull, 0x401eb7bd91d4567dull }, + { 0x400789374bc6a7f0ull, 0x401ebd3170ac5815ull }, + { 0x40078b4395810625ull, 0x401ec2a6474b4646ull }, + { 0x40078d4fdf3b645aull, 0x401ec81c15dd1c82ull }, + { 0x40078f5c28f5c28full, 0x401ecd92dc8dde0aull }, + { 0x4007916872b020c5ull, 0x401ed30a9b8995f2ull }, + { 0x40079374bc6a7efaull, 0x401ed88352fc5715ull }, + { 0x400795810624dd2full, 0x401eddfd03123c2cull }, + { 0x4007978d4fdf3b64ull, 0x401ee377abf767bfull }, + { 0x400799999999999aull, 0x401ee8f34dd80430ull }, + { 0x40079ba5e353f7cfull, 0x401eee6fe8e043aeull }, + { 0x40079db22d0e5604ull, 0x401ef3ed7d3c604bull }, + { 0x40079fbe76c8b439ull, 0x401ef96c0b189bedull }, + { 0x4007a1cac083126full, 0x401efeeb92a1405bull }, + { 0x4007a3d70a3d70a4ull, 0x401f046c14029f2eull }, + { 0x4007a5e353f7ced9ull, 0x401f09ed8f6911e7ull }, + { 0x4007a7ef9db22d0eull, 0x401f0f700500f9e1ull }, + { 0x4007a9fbe76c8b44ull, 0x401f14f374f6c05cull }, + { 0x4007ac083126e979ull, 0x401f1a77df76d674ull }, + { 0x4007ae147ae147aeull, 0x401f1ffd44adb52dull }, + { 0x4007b020c49ba5e3ull, 0x401f2583a4c7dd6full }, + { 0x4007b22d0e560419ull, 0x401f2b0afff1d80aull }, + { 0x4007b4395810624eull, 0x401f3093565835b1ull }, + { 0x4007b645a1cac083ull, 0x401f361ca8278f03ull }, + { 0x4007b851eb851eb8ull, 0x401f3ba6f58c848cull }, + { 0x4007ba5e353f7ceeull, 0x401f41323eb3bec2ull }, + { 0x4007bc6a7ef9db23ull, 0x401f46be83c9ee04ull }, + { 0x4007be76c8b43958ull, 0x401f4c4bc4fbcaa5ull }, + { 0x4007c083126e978dull, 0x401f51da027614e8ull }, + { 0x4007c28f5c28f5c3ull, 0x401f57693c659502ull }, + { 0x4007c49ba5e353f8ull, 0x401f5cf972f71b14ull }, + { 0x4007c6a7ef9db22dull, 0x401f628aa6577f3cull }, + { 0x4007c8b439581062ull, 0x401f681cd6b3a189ull }, + { 0x4007cac083126e98ull, 0x401f6db004386a08ull }, + { 0x4007cccccccccccdull, 0x401f73442f12c8b2ull }, + { 0x4007ced916872b02ull, 0x401f78d9576fb584ull }, + { 0x4007d0e560418937ull, 0x401f7e6f7d7c3074ull }, + { 0x4007d2f1a9fbe76dull, 0x401f8406a1654178ull }, + { 0x4007d4fdf3b645a2ull, 0x401f899ec357f87cull }, + { 0x4007d70a3d70a3d7ull, 0x401f8f37e3816d72ull }, + { 0x4007d916872b020cull, 0x401f94d2020ec04cull }, + { 0x4007db22d0e56042ull, 0x401f9a6d1f2d1902ull }, + { 0x4007dd2f1a9fbe77ull, 0x401fa0093b09a786ull }, + { 0x4007df3b645a1cacull, 0x401fa5a655d1a3daull }, + { 0x4007e147ae147ae1ull, 0x401fab446fb24e01ull }, + { 0x4007e353f7ced917ull, 0x401fb0e388d8ee0bull }, + { 0x4007e5604189374cull, 0x401fb683a172d409ull }, + { 0x4007e76c8b439581ull, 0x401fbc24b9ad581full }, + { 0x4007e978d4fdf3b6ull, 0x401fc1c6d1b5da7dull }, + { 0x4007eb851eb851ecull, 0x401fc769e9b9c35full }, + { 0x4007ed916872b021ull, 0x401fcd0e01e6830dull }, + { 0x4007ef9db22d0e56ull, 0x401fd2b31a6991e5ull }, + { 0x4007f1a9fbe76c8bull, 0x401fd85933707056ull }, + { 0x4007f3b645a1cac1ull, 0x401fde004d28a6e5ull }, + { 0x4007f5c28f5c28f6ull, 0x401fe3a867bfc624ull }, + { 0x4007f7ced916872bull, 0x401fe951836366c3ull }, + { 0x4007f9db22d0e560ull, 0x401feefba0412988ull }, + { 0x4007fbe76c8b4396ull, 0x401ff4a6be86b756ull }, + { 0x4007fdf3b645a1cbull, 0x401ffa52de61c120ull }, + { 0x4008000000000000ull, 0x4020000000000000ull }, + { 0x4008020c49ba5e35ull, 0x402002d711c79a95ull }, + { 0x400804189374bc6bull, 0x402005aea49e94faull }, + { 0x40080624dd2f1aa0ull, 0x40200886b89bd7e7ull }, + { 0x4008083126e978d5ull, 0x40200b5f4dd65028ull }, + { 0x40080a3d70a3d70aull, 0x40200e386464ee98ull }, + { 0x40080c49ba5e3540ull, 0x40201111fc5ea82bull }, + { 0x40080e5604189375ull, 0x402013ec15da75ddull }, + { 0x400810624dd2f1aaull, 0x402016c6b0ef54c9ull }, + { 0x4008126e978d4fdfull, 0x402019a1cdb44618ull }, + { 0x4008147ae147ae15ull, 0x40201c7d6c404f0cull }, + { 0x400816872b020c4aull, 0x40201f598caa78faull }, + { 0x4008189374bc6a7full, 0x402022362f09d14full }, + { 0x40081a9fbe76c8b4ull, 0x4020251353756991ull }, + { 0x40081cac083126eaull, 0x402027f0fa04575dull }, + { 0x40081eb851eb851full, 0x40202acf22cdb468ull }, + { 0x400820c49ba5e354ull, 0x40202dadcde89e83ull }, + { 0x400822d0e5604189ull, 0x4020308cfb6c3798ull }, + { 0x400824dd2f1a9fbfull, 0x4020336cab6fa5adull }, + { 0x400826e978d4fdf4ull, 0x4020364cde0a12e2ull }, + { 0x400828f5c28f5c29ull, 0x4020392d9352ad75ull }, + { 0x40082b020c49ba5eull, 0x40203c0ecb60a7c3ull }, + { 0x40082d0e56041894ull, 0x40203ef0864b3846ull }, + { 0x40082f1a9fbe76c9ull, 0x402041d2c4299993ull }, + { 0x40083126e978d4feull, 0x402044b585130a64ull }, + { 0x4008333333333333ull, 0x40204798c91ecd91ull }, + { 0x4008353f7ced9169ull, 0x40204a7c90642a15ull }, + { 0x4008374bc6a7ef9eull, 0x40204d60dafa6b08ull }, + { 0x4008395810624dd3ull, 0x40205045a8f8dfa8ull }, + { 0x40083b645a1cac08ull, 0x4020532afa76db57ull }, + { 0x40083d70a3d70a3eull, 0x40205610cf8bb59aull }, + { 0x40083f7ced916873ull, 0x402058f7284eca19ull }, + { 0x40084189374bc6a8ull, 0x40205bde04d778a2ull }, + { 0x40084395810624ddull, 0x40205ec5653d252cull }, + { 0x400845a1cac08313ull, 0x402061ad499737d3ull }, + { 0x400847ae147ae148ull, 0x40206495b1fd1cd8ull }, + { 0x400849ba5e353f7dull, 0x4020677e9e8644a9ull }, + { 0x40084bc6a7ef9db2ull, 0x40206a680f4a23daull }, + { 0x40084dd2f1a9fbe8ull, 0x40206d520460332cull }, + { 0x40084fdf3b645a1dull, 0x4020703c7ddfef85ull }, + { 0x400851eb851eb852ull, 0x402073277be0d9fcull }, + { 0x400853f7ced91687ull, 0x40207612fe7a77d1ull }, + { 0x40085604189374bdull, 0x402078ff05c45274ull }, + { 0x40085810624dd2f2ull, 0x40207beb91d5f77cull }, + { 0x40085a1cac083127ull, 0x40207ed8a2c6f8b6ull }, + { 0x40085c28f5c28f5cull, 0x402081c638aeec18ull }, + { 0x40085e353f7ced92ull, 0x402084b453a56bceull }, + { 0x4008604189374bc7ull, 0x402087a2f3c2162eull }, + { 0x4008624dd2f1a9fcull, 0x40208a92191c8dc2ull }, + { 0x4008645a1cac0831ull, 0x40208d81c3cc7947ull }, + { 0x4008666666666667ull, 0x40209071f3e983aeull }, + { 0x40086872b020c49cull, 0x40209362a98b5c15ull }, + { 0x40086a7ef9db22d1ull, 0x40209653e4c9b5d6ull }, + { 0x40086c8b43958106ull, 0x40209945a5bc487bull }, + { 0x40086e978d4fdf3cull, 0x40209c37ec7acfc8ull }, + { 0x400870a3d70a3d71ull, 0x40209f2ab91d0bb1ull }, + { 0x400872b020c49ba6ull, 0x4020a21e0bbac068ull }, + { 0x400874bc6a7ef9dbull, 0x4020a511e46bb653ull }, + { 0x400876c8b4395811ull, 0x4020a8064347ba14ull }, + { 0x400878d4fdf3b646ull, 0x4020aafb28669c81ull }, + { 0x40087ae147ae147bull, 0x4020adf093e032afull }, + { 0x40087ced916872b0ull, 0x4020b0e685cc55edull }, + { 0x40087ef9db22d0e6ull, 0x4020b3dcfe42e3c7ull }, + { 0x4008810624dd2f1bull, 0x4020b6d3fd5bbe00ull }, + { 0x400883126e978d50ull, 0x4020b9cb832eca9dull }, + { 0x4008851eb851eb85ull, 0x4020bcc38fd3f3e2ull }, + { 0x4008872b020c49bbull, 0x4020bfbc23632850ull }, + { 0x400889374bc6a7f0ull, 0x4020c2b53df45aa5ull }, + { 0x40088b4395810625ull, 0x4020c5aedf9f81e2ull }, + { 0x40088d4fdf3b645aull, 0x4020c8a9087c9949ull }, + { 0x40088f5c28f5c290ull, 0x4020cba3b8a3a060ull }, + { 0x4008916872b020c5ull, 0x4020ce9ef02c9ae7ull }, + { 0x40089374bc6a7efaull, 0x4020d19aaf2f90ecull }, + { 0x400895810624dd2full, 0x4020d496f5c48eb9ull }, + { 0x4008978d4fdf3b64ull, 0x4020d793c403a4e2ull }, + { 0x400899999999999aull, 0x4020da911a04e83full }, + { 0x40089ba5e353f7cfull, 0x4020dd8ef7e071ebull }, + { 0x40089db22d0e5604ull, 0x4020e08d5dae5f4dull }, + { 0x40089fbe76c8b439ull, 0x4020e38c4b86d210ull }, + { 0x4008a1cac083126full, 0x4020e68bc181f02cull }, + { 0x4008a3d70a3d70a4ull, 0x4020e98bbfb7e3ddull }, + { 0x4008a5e353f7ced9ull, 0x4020ec8c4640dbacull }, + { 0x4008a7ef9db22d0eull, 0x4020ef8d55350a6cull }, + { 0x4008a9fbe76c8b44ull, 0x4020f28eecaca740ull }, + { 0x4008ac083126e979ull, 0x4020f5910cbfed8full }, + { 0x4008ae147ae147aeull, 0x4020f893b5871d13ull }, + { 0x4008b020c49ba5e3ull, 0x4020fb96e71a79d3ull }, + { 0x4008b22d0e560419ull, 0x4020fe9aa1924c25ull }, + { 0x4008b4395810624eull, 0x4021019ee506e0adull }, + { 0x4008b645a1cac083ull, 0x402104a3b190885full }, + { 0x4008b851eb851eb8ull, 0x402107a907479882ull }, + { 0x4008ba5e353f7ceeull, 0x40210aaee6446aafull }, + { 0x4008bc6a7ef9db23ull, 0x40210db54e9f5ccdull }, + { 0x4008be76c8b43958ull, 0x402110bc4070d11cull }, + { 0x4008c083126e978dull, 0x402113c3bbd12e2cull }, + { 0x4008c28f5c28f5c3ull, 0x402116cbc0d8dee6ull }, + { 0x4008c49ba5e353f8ull, 0x402119d44fa05282ull }, + { 0x4008c6a7ef9db22dull, 0x40211cdd683ffc93ull }, + { 0x4008c8b439581062ull, 0x40211fe70ad05505ull }, + { 0x4008cac083126e98ull, 0x402122f13769d818ull }, + { 0x4008cccccccccccdull, 0x402125fbee250664ull }, + { 0x4008ced916872b02ull, 0x402129072f1a64dfull }, + { 0x4008d0e560418937ull, 0x40212c12fa627cd4ull }, + { 0x4008d2f1a9fbe76dull, 0x40212f1f5015dbefull }, + { 0x4008d4fdf3b645a2ull, 0x4021322c304d1431ull }, + { 0x4008d70a3d70a3d7ull, 0x402135399b20bbfbull }, + { 0x4008d916872b020cull, 0x4021384790a96e0cull }, + { 0x4008db22d0e56042ull, 0x40213b5610ffc982ull }, + { 0x4008dd2f1a9fbe77ull, 0x40213e651c3c71d5ull }, + { 0x4008df3b645a1cacull, 0x40214174b2780ee0ull }, + { 0x4008e147ae147ae1ull, 0x40214484d3cb4cdfull }, + { 0x4008e353f7ced917ull, 0x40214795804edc70ull }, + { 0x4008e5604189374cull, 0x40214aa6b81b728dull }, + { 0x4008e76c8b439581ull, 0x40214db87b49c899ull }, + { 0x4008e978d4fdf3b6ull, 0x402150cac9f29c58ull }, + { 0x4008eb851eb851ecull, 0x402153dda42eaff5ull }, + { 0x4008ed916872b021ull, 0x402156f10a16c9fbull }, + { 0x4008ef9db22d0e56ull, 0x40215a04fbc3b55dull }, + { 0x4008f1a9fbe76c8bull, 0x40215d19794e4177ull }, + { 0x4008f3b645a1cac1ull, 0x4021602e82cf420cull }, + { 0x4008f5c28f5c28f6ull, 0x40216344185f8f43ull }, + { 0x4008f7ced916872bull, 0x4021665a3a1805afull }, + { 0x4008f9db22d0e560ull, 0x40216970e811864eull }, + { 0x4008fbe76c8b4396ull, 0x40216c882264f688ull }, + { 0x4008fdf3b645a1cbull, 0x40216f9fe92b402cull }, + { 0x4009000000000000ull, 0x402172b83c7d517bull }, + { 0x4009020c49ba5e35ull, 0x402175d11c741d20ull }, + { 0x400904189374bc6bull, 0x402178ea89289a35ull }, + { 0x40090624dd2f1aa0ull, 0x40217c0482b3c43full }, + { 0x4009083126e978d5ull, 0x40217f1f092e9b37ull }, + { 0x40090a3d70a3d70aull, 0x4021823a1cb22383ull }, + { 0x40090c49ba5e3540ull, 0x40218555bd5765fdull }, + { 0x40090e5604189375ull, 0x40218871eb376feaull }, + { 0x400910624dd2f1aaull, 0x40218b8ea66b5309ull }, + { 0x4009126e978d4fdfull, 0x40218eabef0c2587ull }, + { 0x4009147ae147ae15ull, 0x402191c9c533020aull }, + { 0x400916872b020c4aull, 0x402194e828f907a5ull }, + { 0x4009189374bc6a7full, 0x402198071a7759e9ull }, + { 0x40091a9fbe76c8b4ull, 0x40219b2699c720d8ull }, + { 0x40091cac083126eaull, 0x40219e46a70188eeull }, + { 0x40091eb851eb851full, 0x4021a167423fc31cull }, + { 0x400920c49ba5e354ull, 0x4021a4886b9b04cdull }, + { 0x400922d0e5604189ull, 0x4021a7aa232c87e8ull }, + { 0x400924dd2f1a9fbfull, 0x4021aacc690d8acdull }, + { 0x400926e978d4fdf4ull, 0x4021adef3d575053ull }, + { 0x400928f5c28f5c29ull, 0x4021b112a0231fd2ull }, + { 0x40092b020c49ba5eull, 0x4021b436918a451dull }, + { 0x40092d0e56041894ull, 0x4021b75b11a61086ull }, + { 0x40092f1a9fbe76c9ull, 0x4021ba80208fd6d9ull }, + { 0x40093126e978d4feull, 0x4021bda5be60f165ull }, + { 0x4009333333333333ull, 0x4021c0cbeb32bdf8ull }, + { 0x4009353f7ced9169ull, 0x4021c3f2a71e9ee3ull }, + { 0x4009374bc6a7ef9eull, 0x4021c719f23dfaf1ull }, + { 0x4009395810624dd3ull, 0x4021ca41ccaa3d78ull }, + { 0x40093b645a1cac08ull, 0x4021cd6a367cd64cull }, + { 0x40093d70a3d70a3eull, 0x4021d0932fcf39c7ull }, + { 0x40093f7ced916873ull, 0x4021d3bcb8bae0c6ull }, + { 0x40094189374bc6a8ull, 0x4021d6e6d15948adull }, + { 0x40094395810624ddull, 0x4021da1179c3f366ull }, + { 0x400945a1cac08313ull, 0x4021dd3cb2146763ull }, + { 0x400947ae147ae148ull, 0x4021e0687a642f9aull }, + { 0x400949ba5e353f7dull, 0x4021e394d2ccdb8full }, + { 0x40094bc6a7ef9db2ull, 0x4021e6c1bb67ff4bull }, + { 0x40094dd2f1a9fbe8ull, 0x4021e9ef344f3366ull }, + { 0x40094fdf3b645a1dull, 0x4021ed1d3d9c14feull }, + { 0x400951eb851eb852ull, 0x4021f04bd76845c1ull }, + { 0x400953f7ced91687ull, 0x4021f37b01cd6be7ull }, + { 0x40095604189374bdull, 0x4021f6aabce53239ull }, + { 0x40095810624dd2f2ull, 0x4021f9db08c94809ull }, + { 0x40095a1cac083127ull, 0x4021fd0be593613dull }, + { 0x40095c28f5c28f5cull, 0x4022003d535d3649ull }, + { 0x40095e353f7ced92ull, 0x4022036f52408433ull }, + { 0x4009604189374bc7ull, 0x402206a1e2570c8full }, + { 0x4009624dd2f1a9fcull, 0x402209d503ba9588ull }, + { 0x4009645a1cac0831ull, 0x40220d08b684e9daull }, + { 0x4009666666666667ull, 0x4022103cfacfd8d7ull }, + { 0x40096872b020c49cull, 0x40221371d0b53661ull }, + { 0x40096a7ef9db22d1ull, 0x402216a7384edaf4ull }, + { 0x40096c8b43958106ull, 0x402219dd31b6a3a1ull }, + { 0x40096e978d4fdf3cull, 0x40221d13bd067213ull }, + { 0x400970a3d70a3d71ull, 0x4022204ada582c87ull }, + { 0x400972b020c49ba6ull, 0x4022238289c5bdd8ull }, + { 0x400974bc6a7ef9dbull, 0x402226bacb69157aull }, + { 0x400976c8b4395811ull, 0x402229f39f5c277cull }, + { 0x400978d4fdf3b646ull, 0x40222d2d05b8ec83ull }, + { 0x40097ae147ae147bull, 0x40223066fe9961d8ull }, + { 0x40097ced916872b0ull, 0x402233a18a17895cull }, + { 0x40097ef9db22d0e6ull, 0x402236dca84d6992ull }, + { 0x4009810624dd2f1bull, 0x40223a1859550d94ull }, + { 0x400983126e978d50ull, 0x40223d549d488524ull }, + { 0x4009851eb851eb85ull, 0x402240917441e49full }, + { 0x4009872b020c49bbull, 0x402243cede5b4507ull }, + { 0x400989374bc6a7f0ull, 0x4022470cdbaec3faull }, + { 0x40098b4395810625ull, 0x40224a4b6c5683beull }, + { 0x40098d4fdf3b645aull, 0x40224d8a906cab3cull }, + { 0x40098f5c28f5c290ull, 0x402250ca480b6601ull }, + { 0x4009916872b020c5ull, 0x4022540a934ce43cull }, + { 0x40099374bc6a7efaull, 0x4022574b724b5ac5ull }, + { 0x400995810624dd2full, 0x40225a8ce521031cull }, + { 0x4009978d4fdf3b65ull, 0x40225dceebe81b68ull }, + { 0x400999999999999aull, 0x4022611186bae675ull }, + { 0x40099ba5e353f7cfull, 0x40226454b5b3abbcull }, + { 0x40099db22d0e5604ull, 0x4022679878ecb760ull }, + { 0x40099fbe76c8b439ull, 0x40226adcd0805a2dull }, + { 0x4009a1cac083126full, 0x40226e21bc88e99full }, + { 0x4009a3d70a3d70a4ull, 0x402271673d20bfd8ull }, + { 0x4009a5e353f7ced9ull, 0x402274ad52623bacull }, + { 0x4009a7ef9db22d0eull, 0x402277f3fc67c09eull }, + { 0x4009a9fbe76c8b44ull, 0x40227b3b3b4bb6dfull }, + { 0x4009ac083126e979ull, 0x40227e830f288b4eull }, + { 0x4009ae147ae147aeull, 0x402281cb7818af7cull }, + { 0x4009b020c49ba5e3ull, 0x40228514763699aeull }, + { 0x4009b22d0e560419ull, 0x4022885e099cc4daull }, + { 0x4009b4395810624eull, 0x40228ba83265b0a6ull }, + { 0x4009b645a1cac083ull, 0x40228ef2f0abe171ull }, + { 0x4009b851eb851eb8ull, 0x4022923e4489e04dull }, + { 0x4009ba5e353f7ceeull, 0x4022958a2e1a3b04ull }, + { 0x4009bc6a7ef9db23ull, 0x402298d6ad778410ull }, + { 0x4009be76c8b43958ull, 0x40229c23c2bc52a9ull }, + { 0x4009c083126e978dull, 0x40229f716e0342beull }, + { 0x4009c28f5c28f5c3ull, 0x4022a2bfaf66f4f8ull }, + { 0x4009c49ba5e353f8ull, 0x4022a60e87020eb5ull }, + { 0x4009c6a7ef9db22dull, 0x4022a95df4ef3a12ull }, + { 0x4009c8b439581062ull, 0x4022acadf94925e9ull }, + { 0x4009cac083126e98ull, 0x4022affe942a85cfull }, + { 0x4009cccccccccccdull, 0x4022b34fc5ae1213ull }, + { 0x4009ced916872b02ull, 0x4022b6a18dee87c7ull }, + { 0x4009d0e560418937ull, 0x4022b9f3ed06a8baull }, + { 0x4009d2f1a9fbe76dull, 0x4022bd46e3113b7cull }, + { 0x4009d4fdf3b645a2ull, 0x4022c09a70290b5aull }, + { 0x4009d70a3d70a3d7ull, 0x4022c3ee9468e868ull }, + { 0x4009d916872b020cull, 0x4022c7434feba778ull }, + { 0x4009db22d0e56042ull, 0x4022ca98a2cc2225ull }, + { 0x4009dd2f1a9fbe77ull, 0x4022cdee8d2536c5ull }, + { 0x4009df3b645a1cacull, 0x4022d1450f11c87aull }, + { 0x4009e147ae147ae1ull, 0x4022d49c28acbf2aull }, + { 0x4009e353f7ced917ull, 0x4022d7f3da110785ull }, + { 0x4009e5604189374cull, 0x4022db4c235992f9ull }, + { 0x4009e76c8b439581ull, 0x4022dea504a157c6ull }, + { 0x4009e978d4fdf3b6ull, 0x4022e1fe7e0350f2ull }, + { 0x4009eb851eb851ecull, 0x4022e5588f9a7e4cull }, + { 0x4009ed916872b021ull, 0x4022e8b33981e46eull }, + { 0x4009ef9db22d0e56ull, 0x4022ec0e7bd48cbeull }, + { 0x4009f1a9fbe76c8bull, 0x4022ef6a56ad8570ull }, + { 0x4009f3b645a1cac1ull, 0x4022f2c6ca27e185ull }, + { 0x4009f5c28f5c28f6ull, 0x4022f623d65eb8caull }, + { 0x4009f7ced916872bull, 0x4022f9817b6d27ddull }, + { 0x4009f9db22d0e560ull, 0x4022fcdfb96e502dull }, + { 0x4009fbe76c8b4396ull, 0x4023003e907d57faull }, + { 0x4009fdf3b645a1cbull, 0x4023039e00b56a50ull }, + { 0x400a000000000000ull, 0x402306fe0a31b715ull }, + { 0x400a020c49ba5e35ull, 0x40230a5ead0d72ffull }, + { 0x400a04189374bc6bull, 0x40230dbfe963d79bull }, + { 0x400a0624dd2f1aa0ull, 0x40231121bf502344ull }, + { 0x400a083126e978d5ull, 0x402314842eed9933ull }, + { 0x400a0a3d70a3d70aull, 0x402317e738578174ull }, + { 0x400a0c49ba5e3540ull, 0x40231b4adba928eeull }, + { 0x400a0e5604189375ull, 0x40231eaf18fde159ull }, + { 0x400a10624dd2f1aaull, 0x40232213f071014full }, + { 0x400a126e978d4fdfull, 0x40232579621de43full }, + { 0x400a147ae147ae15ull, 0x402328df6e1fea78ull }, + { 0x400a16872b020c4aull, 0x40232c461492791eull }, + { 0x400a189374bc6a7full, 0x40232fad5590fa38ull }, + { 0x400a1a9fbe76c8b4ull, 0x402333153136dca8ull }, + { 0x400a1cac083126eaull, 0x4023367da79f9431ull }, + { 0x400a1eb851eb851full, 0x402339e6b8e69970ull }, + { 0x400a20c49ba5e354ull, 0x40233d50652769e9ull }, + { 0x400a22d0e5604189ull, 0x402340baac7d87fdull }, + { 0x400a24dd2f1a9fbfull, 0x402344258f047af2ull }, + { 0x400a26e978d4fdf4ull, 0x402347910cd7ceecull }, + { 0x400a28f5c28f5c29ull, 0x40234afd261314f8ull }, + { 0x400a2b020c49ba5eull, 0x40234e69dad1e306ull }, + { 0x400a2d0e56041894ull, 0x402351d72b2fd3ecull }, + { 0x400a2f1a9fbe76c9ull, 0x4023554517488763ull }, + { 0x400a3126e978d4feull, 0x402358b39f37a20full }, + { 0x400a333333333333ull, 0x40235c22c318cd7dull }, + { 0x400a353f7ced9169ull, 0x40235f928307b820ull }, + { 0x400a374bc6a7ef9eull, 0x40236302df201555ull }, + { 0x400a395810624dd3ull, 0x40236673d77d9d65ull }, + { 0x400a3b645a1cac08ull, 0x402369e56c3c0d85ull }, + { 0x400a3d70a3d70a3eull, 0x40236d579d7727d8ull }, + { 0x400a3f7ced916873ull, 0x402370ca6b4ab369ull }, + { 0x400a4189374bc6a8ull, 0x4023743dd5d27c36ull }, + { 0x400a4395810624ddull, 0x402377b1dd2a532aull }, + { 0x400a45a1cac08313ull, 0x40237b26816e0e24ull }, + { 0x400a47ae147ae148ull, 0x40237e9bc2b987ecull }, + { 0x400a49ba5e353f7dull, 0x40238211a128a042ull }, + { 0x400a4bc6a7ef9db2ull, 0x402385881cd73bd9ull }, + { 0x400a4dd2f1a9fbe8ull, 0x402388ff35e14455ull }, + { 0x400a4fdf3b645a1dull, 0x40238c76ec62a84cull }, + { 0x400a51eb851eb852ull, 0x40238fef40775b50ull }, + { 0x400a53f7ced91687ull, 0x40239368323b55e3ull }, + { 0x400a5604189374bdull, 0x402396e1c1ca9583ull }, + { 0x400a5810624dd2f2ull, 0x40239a5bef411ca1ull }, + { 0x400a5a1cac083127ull, 0x40239dd6babaf2a9ull }, + { 0x400a5c28f5c28f5cull, 0x4023a15224542403ull }, + { 0x400a5e353f7ced92ull, 0x4023a4ce2c28c210ull }, + { 0x400a604189374bc7ull, 0x4023a84ad254e328ull }, + { 0x400a624dd2f1a9fcull, 0x4023abc816f4a2a7ull }, + { 0x400a645a1cac0831ull, 0x4023af45fa2420e0ull }, + { 0x400a666666666667ull, 0x4023b2c47bff832aull }, + { 0x400a6872b020c49cull, 0x4023b6439ca2f3d4ull }, + { 0x400a6a7ef9db22d1ull, 0x4023b9c35c2aa231ull }, + { 0x400a6c8b43958106ull, 0x4023bd43bab2c296ull }, + { 0x400a6e978d4fdf3cull, 0x4023c0c4b8578e58ull }, + { 0x400a70a3d70a3d71ull, 0x4023c446553543cdull }, + { 0x400a72b020c49ba6ull, 0x4023c7c891682650ull }, + { 0x400a74bc6a7ef9dbull, 0x4023cb4b6d0c7e41ull }, + { 0x400a76c8b4395811ull, 0x4023cecee83e9905ull }, + { 0x400a78d4fdf3b646ull, 0x4023d253031ac905ull }, + { 0x400a7ae147ae147bull, 0x4023d5d7bdbd65b3ull }, + { 0x400a7ced916872b0ull, 0x4023d95d1842cb89ull }, + { 0x400a7ef9db22d0e6ull, 0x4023dce312c75c0aull }, + { 0x400a810624dd2f1bull, 0x4023e069ad677dc0ull }, + { 0x400a83126e978d50ull, 0x4023e3f0e83f9c42ull }, + { 0x400a851eb851eb85ull, 0x4023e778c36c2833ull }, + { 0x400a872b020c49bbull, 0x4023eb013f099742ull }, + { 0x400a89374bc6a7f0ull, 0x4023ee8a5b346428ull }, + { 0x400a8b4395810625ull, 0x4023f21418090eb0ull }, + { 0x400a8d4fdf3b645aull, 0x4023f59e75a41bb3ull }, + { 0x400a8f5c28f5c290ull, 0x4023f9297422151bull }, + { 0x400a916872b020c5ull, 0x4023fcb5139f89dfull }, + { 0x400a9374bc6a7efaull, 0x4024004154390e0cull }, + { 0x400a95810624dd2full, 0x402403ce360b3abfull }, + { 0x400a978d4fdf3b65ull, 0x4024075bb932ae2dull }, + { 0x400a99999999999aull, 0x40240ae9ddcc0b97ull }, + { 0x400a9ba5e353f7cfull, 0x40240e78a3f3fb5aull }, + { 0x400a9db22d0e5604ull, 0x402412080bc72ae9ull }, + { 0x400a9fbe76c8b43aull, 0x4024159815624cceull }, + { 0x400aa1cac083126full, 0x40241928c0e218a6ull }, + { 0x400aa3d70a3d70a4ull, 0x40241cba0e634b2cull }, + { 0x400aa5e353f7ced9ull, 0x4024204bfe02a635ull }, + { 0x400aa7ef9db22d0eull, 0x402423de8fdcf0afull }, + { 0x400aa9fbe76c8b44ull, 0x40242771c40ef6a6ull }, + { 0x400aac083126e979ull, 0x40242b059ab5893dull }, + { 0x400aae147ae147aeull, 0x40242e9a13ed7eb9ull }, + { 0x400ab020c49ba5e3ull, 0x4024322f2fd3b27full }, + { 0x400ab22d0e560419ull, 0x402435c4ee850510ull }, + { 0x400ab4395810624eull, 0x4024395b501e5c0dull }, + { 0x400ab645a1cac083ull, 0x40243cf254bca23aull }, + { 0x400ab851eb851eb8ull, 0x40244089fc7cc77cull }, + { 0x400aba5e353f7ceeull, 0x40244422477bc0deull }, + { 0x400abc6a7ef9db23ull, 0x402447bb35d68888ull }, + { 0x400abe76c8b43958ull, 0x40244b54c7aa1dcdull }, + { 0x400ac083126e978dull, 0x40244eeefd138524ull }, + { 0x400ac28f5c28f5c3ull, 0x40245289d62fc82bull }, + { 0x400ac49ba5e353f8ull, 0x40245625531bf5a4ull }, + { 0x400ac6a7ef9db22dull, 0x402459c173f5217eull }, + { 0x400ac8b439581062ull, 0x40245d5e38d864cfull }, + { 0x400acac083126e98ull, 0x402460fba1e2dddaull }, + { 0x400acccccccccccdull, 0x40246499af31b007ull }, + { 0x400aced916872b02ull, 0x4024683860e203f1ull }, + { 0x400ad0e560418937ull, 0x40246bd7b711075cull }, + { 0x400ad2f1a9fbe76dull, 0x40246f77b1dbed3dull }, + { 0x400ad4fdf3b645a2ull, 0x40247318515fedb3ull }, + { 0x400ad70a3d70a3d7ull, 0x402476b995ba4611ull }, + { 0x400ad916872b020cull, 0x40247a5b7f0838daull }, + { 0x400adb22d0e56042ull, 0x40247dfe0d670dc3ull }, + { 0x400add2f1a9fbe77ull, 0x402481a140f411afull }, + { 0x400adf3b645a1cacull, 0x4024854519cc96baull }, + { 0x400ae147ae147ae1ull, 0x402488e9980df433ull }, + { 0x400ae353f7ced917ull, 0x40248c8ebbd5869full }, + { 0x400ae5604189374cull, 0x402490348540afb4ull }, + { 0x400ae76c8b439581ull, 0x402493daf46cd666ull }, + { 0x400ae978d4fdf3b6ull, 0x40249782097766ddull }, + { 0x400aeb851eb851ecull, 0x40249b29c47dd27eull }, + { 0x400aed916872b021ull, 0x40249ed2259d8fe1ull }, + { 0x400aef9db22d0e56ull, 0x4024a27b2cf41adfull }, + { 0x400af1a9fbe76c8bull, 0x4024a624da9ef48aull }, + { 0x400af3b645a1cac1ull, 0x4024a9cf2ebba335ull }, + { 0x400af5c28f5c28f6ull, 0x4024ad7a2967b268ull }, + { 0x400af7ced916872bull, 0x4024b125cac0b2f2ull }, + { 0x400af9db22d0e560ull, 0x4024b4d212e43addull }, + { 0x400afbe76c8b4396ull, 0x4024b87f01efe577ull }, + { 0x400afdf3b645a1cbull, 0x4024bc2c9801534aull }, + { 0x400b000000000000ull, 0x4024bfdad5362a27ull }, + { 0x400b020c49ba5e35ull, 0x4024c389b9ac1521ull }, + { 0x400b04189374bc6bull, 0x4024c7394580c48full }, + { 0x400b0624dd2f1aa0ull, 0x4024cae978d1ee0bull }, + { 0x400b083126e978d5ull, 0x4024ce9a53bd4c79ull }, + { 0x400b0a3d70a3d70aull, 0x4024d24bd660a001ull }, + { 0x400b0c49ba5e3540ull, 0x4024d5fe00d9ae17ull }, + { 0x400b0e5604189375ull, 0x4024d9b0d3464170ull }, + { 0x400b10624dd2f1aaull, 0x4024dd644dc42a11ull }, + { 0x400b126e978d4fdfull, 0x4024e11870713d4aull }, + { 0x400b147ae147ae15ull, 0x4024e4cd3b6b55b6ull }, + { 0x400b16872b020c4aull, 0x4024e882aed05338ull }, + { 0x400b189374bc6a7full, 0x4024ec38cabe1b05ull }, + { 0x400b1a9fbe76c8b4ull, 0x4024efef8f5297a2ull }, + { 0x400b1cac083126eaull, 0x4024f3a6fcabb8e1ull }, + { 0x400b1eb851eb851full, 0x4024f75f12e773e1ull }, + { 0x400b20c49ba5e354ull, 0x4024fb17d223c319ull }, + { 0x400b22d0e5604189ull, 0x4024fed13a7ea64eull }, + { 0x400b24dd2f1a9fbfull, 0x4025028b4c16229bull }, + { 0x400b26e978d4fdf4ull, 0x402506460708426aull }, + { 0x400b28f5c28f5c29ull, 0x40250a016b731581ull }, + { 0x400b2b020c49ba5eull, 0x40250dbd7974b0f6ull }, + { 0x400b2d0e56041894ull, 0x4025117a312b2f3cull }, + { 0x400b2f1a9fbe76c9ull, 0x4025153792b4b017ull }, + { 0x400b3126e978d4feull, 0x402518f59e2f58a9ull }, + { 0x400b333333333333ull, 0x40251cb453b9536cull }, + { 0x400b353f7ced9169ull, 0x40252073b370d037ull }, + { 0x400b374bc6a7ef9eull, 0x40252433bd740438ull }, + { 0x400b395810624dd3ull, 0x402527f471e129feull }, + { 0x400b3b645a1cac08ull, 0x40252bb5d0d68174ull }, + { 0x400b3d70a3d70a3eull, 0x40252f77da724fe6ull }, + { 0x400b3f7ced916873ull, 0x4025333a8ed2dff9ull }, + { 0x400b4189374bc6a8ull, 0x402536fdee1681baull }, + { 0x400b4395810624ddull, 0x40253ac1f85b8a92ull }, + { 0x400b45a1cac08313ull, 0x40253e86adc05554ull }, + { 0x400b47ae147ae148ull, 0x4025424c0e63422aull }, + { 0x400b49ba5e353f7dull, 0x402546121a62b6aeull }, + { 0x400b4bc6a7ef9db2ull, 0x402549d8d1dd1dd9ull }, + { 0x400b4dd2f1a9fbe8ull, 0x40254da034f0e80full }, + { 0x400b4fdf3b645a1dull, 0x4025516843bc8b14ull }, + { 0x400b51eb851eb852ull, 0x40255530fe5e821aull }, + { 0x400b53f7ced91687ull, 0x402558fa64f54dbaull }, + { 0x400b5604189374bdull, 0x40255cc4779f73fbull }, + { 0x400b5810624dd2f2ull, 0x4025608f367b8047ull }, + { 0x400b5a1cac083127ull, 0x4025645aa1a8037aull }, + { 0x400b5c28f5c28f5cull, 0x40256826b94393ddull }, + { 0x400b5e353f7ced92ull, 0x40256bf37d6ccd26ull }, + { 0x400b604189374bc7ull, 0x40256fc0ee425076ull }, + { 0x400b624dd2f1a9fcull, 0x4025738f0be2c463ull }, + { 0x400b645a1cac0831ull, 0x4025775dd66cd4f3ull }, + { 0x400b666666666667ull, 0x40257b2d4dff339eull }, + { 0x400b6872b020c49cull, 0x40257efd72b8974aull }, + { 0x400b6a7ef9db22d1ull, 0x402582ce44b7bc56ull }, + { 0x400b6c8b43958106ull, 0x4025869fc41b6494ull }, + { 0x400b6e978d4fdf3cull, 0x40258a71f102574full }, + { 0x400b70a3d70a3d71ull, 0x40258e44cb8b6140ull }, + { 0x400b72b020c49ba6ull, 0x4025921853d5549full }, + { 0x400b74bc6a7ef9dbull, 0x402595ec89ff091bull }, + { 0x400b76c8b4395811ull, 0x402599c16e275bdeull }, + { 0x400b78d4fdf3b646ull, 0x40259d97006d2f87ull }, + { 0x400b7ae147ae147bull, 0x4025a16d40ef6c34ull }, + { 0x400b7ced916872b0ull, 0x4025a5442fccff82ull }, + { 0x400b7ef9db22d0e6ull, 0x4025a91bcd24dc8aull }, + { 0x400b810624dd2f1bull, 0x4025acf41915fbdfull }, + { 0x400b83126e978d50ull, 0x4025b0cd13bf5b97ull }, + { 0x400b851eb851eb85ull, 0x4025b4a6bd3fff49ull }, + { 0x400b872b020c49bbull, 0x4025b88115b6f00full }, + { 0x400b89374bc6a7f0ull, 0x4025bc5c1d433c7eull }, + { 0x400b8b4395810625ull, 0x4025c037d403f8b6ull }, + { 0x400b8d4fdf3b645aull, 0x4025c4143a183e57ull }, + { 0x400b8f5c28f5c290ull, 0x4025c7f14f9f2c89ull }, + { 0x400b916872b020c5ull, 0x4025cbcf14b7e7f7ull }, + { 0x400b9374bc6a7efaull, 0x4025cfad89819ad6ull }, + { 0x400b95810624dd2full, 0x4025d38cae1b74e2ull }, + { 0x400b978d4fdf3b65ull, 0x4025d76c82a4ab61ull }, + { 0x400b99999999999aull, 0x4025db4d073c7921ull }, + { 0x400b9ba5e353f7cfull, 0x4025df2e3c021e7dull }, + { 0x400b9db22d0e5604ull, 0x4025e3102114e15cull }, + { 0x400b9fbe76c8b43aull, 0x4025e6f2b6940d34ull }, + { 0x400ba1cac083126full, 0x4025ead5fc9ef303ull }, + { 0x400ba3d70a3d70a4ull, 0x4025eeb9f354e95cull }, + { 0x400ba5e353f7ced9ull, 0x4025f29e9ad54c5full }, + { 0x400ba7ef9db22d0eull, 0x4025f683f33f7dc0ull }, + { 0x400ba9fbe76c8b44ull, 0x4025fa69fcb2e4c4ull }, + { 0x400bac083126e979ull, 0x4025fe50b74eee3eull }, + { 0x400bae147ae147aeull, 0x4026023823330c9cull }, + { 0x400bb020c49ba5e3ull, 0x40260620407eb7deull }, + { 0x400bb22d0e560419ull, 0x40260a090f516d9cull }, + { 0x400bb4395810624eull, 0x40260df28fcab0ffull }, + { 0x400bb645a1cac083ull, 0x402611dcc20a0aceull }, + { 0x400bb851eb851eb8ull, 0x402615c7a62f0968ull }, + { 0x400bba5e353f7ceeull, 0x402619b33c5940c6ull }, + { 0x400bbc6a7ef9db23ull, 0x40261d9f84a84a78ull }, + { 0x400bbe76c8b43958ull, 0x4026218c7f3bc5afull }, + { 0x400bc083126e978dull, 0x4026257a2c335737ull }, + { 0x400bc28f5c28f5c3ull, 0x402629688baea97bull }, + { 0x400bc49ba5e353f8ull, 0x40262d579dcd6c81ull }, + { 0x400bc6a7ef9db22dull, 0x4026314762af55f4ull }, + { 0x400bc8b439581062ull, 0x40263537da74211dull }, + { 0x400bcac083126e98ull, 0x40263929053b8ee9ull }, + { 0x400bcccccccccccdull, 0x40263d1ae32565e5ull }, + { 0x400bced916872b02ull, 0x4026410d74517243ull }, + { 0x400bd0e560418937ull, 0x40264500b8df85dcull }, + { 0x400bd2f1a9fbe76dull, 0x402648f4b0ef7830ull }, + { 0x400bd4fdf3b645a2ull, 0x40264ce95ca1265eull }, + { 0x400bd70a3d70a3d7ull, 0x402650debc147335ull }, + { 0x400bd916872b020cull, 0x402654d4cf69472bull }, + { 0x400bdb22d0e56042ull, 0x402658cb96bf9060ull }, + { 0x400bdd2f1a9fbe77ull, 0x40265cc31237429cull }, + { 0x400bdf3b645a1cacull, 0x402660bb41f05756ull }, + { 0x400be147ae147ae1ull, 0x402664b4260acdb1ull }, + { 0x400be353f7ced917ull, 0x402668adbea6aa81ull }, + { 0x400be5604189374cull, 0x40266ca80be3f842ull }, + { 0x400be76c8b439581ull, 0x402670a30de2c725ull }, + { 0x400be978d4fdf3b6ull, 0x4026749ec4c32d0eull }, + { 0x400beb851eb851ecull, 0x4026789b30a54590ull }, + { 0x400bed916872b021ull, 0x40267c9851a931edull }, + { 0x400bef9db22d0e56ull, 0x4026809627ef1923ull }, + { 0x400bf1a9fbe76c8bull, 0x40268494b39727e1ull }, + { 0x400bf3b645a1cac1ull, 0x40268893f4c1908dull }, + { 0x400bf5c28f5c28f6ull, 0x40268c93eb8e8b41ull }, + { 0x400bf7ced916872bull, 0x40269094981e55d3ull }, + { 0x400bf9db22d0e560ull, 0x40269495fa9133d1ull }, + { 0x400bfbe76c8b4396ull, 0x4026989813076e85ull }, + { 0x400bfdf3b645a1cbull, 0x40269c9ae1a154eeull }, + { 0x400c000000000000ull, 0x4026a09e667f3bcdull }, + { 0x400c020c49ba5e35ull, 0x4026a4a2a1c17d9eull }, + { 0x400c04189374bc6bull, 0x4026a8a793887a9eull }, + { 0x400c0624dd2f1aa0ull, 0x4026acad3bf498c2ull }, + { 0x400c083126e978d5ull, 0x4026b0b39b2643c8ull }, + { 0x400c0a3d70a3d70aull, 0x4026b4bab13ded2aull }, + { 0x400c0c49ba5e3540ull, 0x4026b8c27e5c0c27ull }, + { 0x400c0e5604189375ull, 0x4026bccb02a11dbdull }, + { 0x400c10624dd2f1aaull, 0x4026c0d43e2da4b2ull }, + { 0x400c126e978d4fdfull, 0x4026c4de31222992ull }, + { 0x400c147ae147ae15ull, 0x4026c8e8db9f3aafull }, + { 0x400c16872b020c4aull, 0x4026ccf43dc56c1eull }, + { 0x400c189374bc6a7full, 0x4026d10057b557c1ull }, + { 0x400c1a9fbe76c8b4ull, 0x4026d50d298f9d43ull }, + { 0x400c1cac083126eaull, 0x4026d91ab374e219ull }, + { 0x400c1eb851eb851full, 0x4026dd28f585d182ull }, + { 0x400c20c49ba5e354ull, 0x4026e137efe31c8aull }, + { 0x400c22d0e5604189ull, 0x4026e547a2ad7a0dull }, + { 0x400c24dd2f1a9fbfull, 0x4026e9580e05a6b6ull }, + { 0x400c26e978d4fdf4ull, 0x4026ed69320c64f8ull }, + { 0x400c28f5c28f5c29ull, 0x4026f17b0ee27d1full }, + { 0x400c2b020c49ba5eull, 0x4026f58da4a8bd47ull }, + { 0x400c2d0e56041894ull, 0x4026f9a0f37ff95eull }, + { 0x400c2f1a9fbe76c9ull, 0x4026fdb4fb890b22ull }, + { 0x400c3126e978d4feull, 0x402701c9bce4d22cull }, + { 0x400c333333333333ull, 0x402705df37b433e6ull }, + { 0x400c353f7ced9169ull, 0x402709f56c181b97ull }, + { 0x400c374bc6a7ef9eull, 0x40270e0c5a317a54ull }, + { 0x400c395810624dd3ull, 0x4027122402214714ull }, + { 0x400c3b645a1cac08ull, 0x4027163c64087ea4ull }, + { 0x400c3d70a3d70a3eull, 0x40271a55800823afull }, + { 0x400c3f7ced916873ull, 0x40271e6f56413eb5ull }, + { 0x400c4189374bc6a8ull, 0x40272289e6d4de1aull }, + { 0x400c4395810624ddull, 0x402726a531e4161full }, + { 0x400c45a1cac08313ull, 0x40272ac1379000e4ull }, + { 0x400c47ae147ae148ull, 0x40272eddf7f9be65ull }, + { 0x400c49ba5e353f7dull, 0x402732fb73427484ull }, + { 0x400c4bc6a7ef9db2ull, 0x40273719a98b4f04ull }, + { 0x400c4dd2f1a9fbe8ull, 0x40273b389af57f8eull }, + { 0x400c4fdf3b645a1dull, 0x40273f5847a23da8ull }, + { 0x400c51eb851eb852ull, 0x40274378afb2c6c5ull }, + { 0x400c53f7ced91687ull, 0x40274799d3485e3aull }, + { 0x400c5604189374bdull, 0x40274bbbb2844d48ull }, + { 0x400c5810624dd2f2ull, 0x40274fde4d87e312ull }, + { 0x400c5a1cac083127ull, 0x40275401a47474aaull }, + { 0x400c5c28f5c28f5cull, 0x40275825b76b5d0aull }, + { 0x400c5e353f7ced92ull, 0x40275c4a868dfd1dull }, + { 0x400c604189374bc7ull, 0x4027607011fdbbb0ull }, + { 0x400c624dd2f1a9fcull, 0x4027649659dc0588ull }, + { 0x400c645a1cac0831ull, 0x402768bd5e4a4d55ull }, + { 0x400c666666666667ull, 0x40276ce51f6a0bb9ull }, + { 0x400c6872b020c49cull, 0x4027710d9d5cbf41ull }, + { 0x400c6a7ef9db22d1ull, 0x40277536d843ec73ull }, + { 0x400c6c8b43958106ull, 0x40277960d0411dc4ull }, + { 0x400c6e978d4fdf3cull, 0x40277d8b8575e3a1ull }, + { 0x400c70a3d70a3d71ull, 0x402781b6f803d466ull }, + { 0x400c72b020c49ba6ull, 0x402785e3280c8c6bull }, + { 0x400c74bc6a7ef9dbull, 0x40278a1015b1adfdull }, + { 0x400c76c8b4395811ull, 0x40278e3dc114e164ull }, + { 0x400c78d4fdf3b646ull, 0x4027926c2a57d4daull }, + { 0x400c7ae147ae147bull, 0x4027969b519c3c9cull }, + { 0x400c7ced916872b0ull, 0x40279acb3703d2dfull }, + { 0x400c7ef9db22d0e6ull, 0x40279efbdab057d7ull }, + { 0x400c810624dd2f1bull, 0x4027a32d3cc391afull }, + { 0x400c83126e978d50ull, 0x4027a75f5d5f4c97ull }, + { 0x400c851eb851eb85ull, 0x4027ab923ca55abeull }, + { 0x400c872b020c49bbull, 0x4027afc5dab79455ull }, + { 0x400c89374bc6a7f0ull, 0x4027b3fa37b7d788ull }, + { 0x400c8b4395810625ull, 0x4027b82f53c8088eull }, + { 0x400c8d4fdf3b645aull, 0x4027bc652f0a119full }, + { 0x400c8f5c28f5c290ull, 0x4027c09bc99fe2f9ull }, + { 0x400c916872b020c5ull, 0x4027c4d323ab72ddull }, + { 0x400c9374bc6a7efaull, 0x4027c90b3d4ebd97ull }, + { 0x400c95810624dd2full, 0x4027cd4416abc57bull }, + { 0x400c978d4fdf3b65ull, 0x4027d17dafe492e6ull }, + { 0x400c99999999999aull, 0x4027d5b8091b343dull }, + { 0x400c9ba5e353f7cfull, 0x4027d9f32271bdf3ull }, + { 0x400c9db22d0e5604ull, 0x4027de2efc0a4a87ull }, + { 0x400c9fbe76c8b43aull, 0x4027e26b9606fa8aull }, + { 0x400ca1cac083126full, 0x4027e6a8f089f493ull }, + { 0x400ca3d70a3d70a4ull, 0x4027eae70bb5654full }, + { 0x400ca5e353f7ced9ull, 0x4027ef25e7ab7f7bull }, + { 0x400ca7ef9db22d0full, 0x4027f365848e7be8ull }, + { 0x400ca9fbe76c8b44ull, 0x4027f7a5e2809974ull }, + { 0x400cac083126e979ull, 0x4027fbe701a41d16ull }, + { 0x400cae147ae147aeull, 0x40280028e21b51daull }, + { 0x400cb020c49ba5e3ull, 0x4028046b840888e1ull }, + { 0x400cb22d0e560419ull, 0x402808aee78e1967ull }, + { 0x400cb4395810624eull, 0x40280cf30cce60b9ull }, + { 0x400cb645a1cac083ull, 0x40281137f3ebc243ull }, + { 0x400cb851eb851eb8ull, 0x4028157d9d08a78dull }, + { 0x400cba5e353f7ceeull, 0x402819c408478039ull }, + { 0x400cbc6a7ef9db23ull, 0x40281e0b35cac201ull }, + { 0x400cbe76c8b43958ull, 0x4028225325b4e8c5ull }, + { 0x400cc083126e978dull, 0x4028269bd828767eull }, + { 0x400cc28f5c28f5c3ull, 0x40282ae54d47f34cull }, + { 0x400cc49ba5e353f8ull, 0x40282f2f8535ed65ull }, + { 0x400cc6a7ef9db22dull, 0x4028337a8014f92cull }, + { 0x400cc8b439581062ull, 0x402837c63e07b121ull }, + { 0x400ccac083126e98ull, 0x40283c12bf30b5efull }, + { 0x400ccccccccccccdull, 0x4028406003b2ae5dull }, + { 0x400cced916872b02ull, 0x402844ae0bb0475full }, + { 0x400cd0e560418937ull, 0x402848fcd74c3412ull }, + { 0x400cd2f1a9fbe76dull, 0x40284d4c66a92db9ull }, + { 0x400cd4fdf3b645a2ull, 0x4028519cb9e9f3bdull }, + { 0x400cd70a3d70a3d7ull, 0x402855edd1314bbbull }, + { 0x400cd916872b020cull, 0x40285a3faca20175ull }, + { 0x400cdb22d0e56042ull, 0x40285e924c5ee6dfull }, + { 0x400cdd2f1a9fbe77ull, 0x402862e5b08ad415ull }, + { 0x400cdf3b645a1cacull, 0x40286739d948a769ull }, + { 0x400ce147ae147ae1ull, 0x40286b8ec6bb4559ull }, + { 0x400ce353f7ced917ull, 0x40286fe47905989bull }, + { 0x400ce5604189374cull, 0x4028743af04a920cull }, + { 0x400ce76c8b439581ull, 0x402878922cad28c7ull }, + { 0x400ce978d4fdf3b6ull, 0x40287cea2e505a19ull }, + { 0x400ceb851eb851ecull, 0x40288142f5572987ull }, + { 0x400ced916872b021ull, 0x4028859c81e4a0c5ull }, + { 0x400cef9db22d0e56ull, 0x402889f6d41bcfc9ull }, + { 0x400cf1a9fbe76c8bull, 0x40288e51ec1fccbcull }, + { 0x400cf3b645a1cac1ull, 0x402892adca13b408ull }, + { 0x400cf5c28f5c28f6ull, 0x4028970a6e1aa848ull }, + { 0x400cf7ced916872bull, 0x40289b67d857d25dull }, + { 0x400cf9db22d0e560ull, 0x40289fc608ee6162ull }, + { 0x400cfbe76c8b4396ull, 0x4028a42500018ab4ull }, + { 0x400cfdf3b645a1cbull, 0x4028a884bdb489e8ull }, + { 0x400d000000000000ull, 0x4028ace5422aa0dbull }, + { 0x400d020c49ba5e35ull, 0x4028b1468d8717acull }, + { 0x400d04189374bc6bull, 0x4028b5a89fed3cbdull }, + { 0x400d0624dd2f1aa0ull, 0x4028ba0b798064aeull }, + { 0x400d083126e978d5ull, 0x4028be6f1a63ea6dull }, + { 0x400d0a3d70a3d70aull, 0x4028c2d382bb2f2bull }, + { 0x400d0c49ba5e3540ull, 0x4028c738b2a99a62ull }, + { 0x400d0e5604189375ull, 0x4028cb9eaa5299d0ull }, + { 0x400d10624dd2f1aaull, 0x4028d00569d9a182ull }, + { 0x400d126e978d4fdfull, 0x4028d46cf1622bd0ull }, + { 0x400d147ae147ae15ull, 0x4028d8d5410fb95eull }, + { 0x400d16872b020c4aull, 0x4028dd3e5905d119ull }, + { 0x400d189374bc6a7full, 0x4028e1a839680041ull }, + { 0x400d1a9fbe76c8b4ull, 0x4028e612e259da63ull }, + { 0x400d1cac083126eaull, 0x4028ea7e53fef962ull }, + { 0x400d1eb851eb851full, 0x4028eeea8e7afd69ull }, + { 0x400d20c49ba5e354ull, 0x4028f35791f18cfeull }, + { 0x400d22d0e5604189ull, 0x4028f7c55e8654f9ull }, + { 0x400d24dd2f1a9fbfull, 0x4028fc33f45d0887ull }, + { 0x400d26e978d4fdf4ull, 0x402900a353996129ull }, + { 0x400d28f5c28f5c29ull, 0x402905137c5f1eb9ull }, + { 0x400d2b020c49ba5eull, 0x402909846ed2076cull }, + { 0x400d2d0e56041894ull, 0x40290df62b15e7cfull }, + { 0x400d2f1a9fbe76c9ull, 0x40291268b14e92c6ull }, + { 0x400d3126e978d4feull, 0x402916dc019fe196ull }, + { 0x400d333333333333ull, 0x40291b501c2db3deull }, + { 0x400d353f7ced9169ull, 0x40291fc5011befa0ull }, + { 0x400d374bc6a7ef9eull, 0x4029243ab08e8135ull }, + { 0x400d395810624dd3ull, 0x402928b12aa95b5dull }, + { 0x400d3b645a1cac08ull, 0x40292d286f907737ull }, + { 0x400d3d70a3d70a3eull, 0x402931a07f67d448ull }, + { 0x400d3f7ced916873ull, 0x402936195a537872ull }, + { 0x400d4189374bc6a8ull, 0x40293a9300777002ull }, + { 0x400d4395810624ddull, 0x40293f0d71f7cda9ull }, + { 0x400d45a1cac08313ull, 0x40294388aef8aa81ull }, + { 0x400d47ae147ae148ull, 0x40294804b79e2607ull }, + { 0x400d49ba5e353f7dull, 0x40294c818c0c6628ull }, + { 0x400d4bc6a7ef9db2ull, 0x402950ff2c679737ull }, + { 0x400d4dd2f1a9fbe8ull, 0x4029557d98d3ebf9ull }, + { 0x400d4fdf3b645a1dull, 0x402959fcd1759d96ull }, + { 0x400d51eb851eb852ull, 0x40295e7cd670ebabull }, + { 0x400d53f7ced91687ull, 0x402962fda7ea1c44ull }, + { 0x400d5604189374bdull, 0x4029677f46057bdfull }, + { 0x400d5810624dd2f2ull, 0x40296c01b0e75d62ull }, + { 0x400d5a1cac083127ull, 0x40297084e8b41a31ull }, + { 0x400d5c28f5c28f5cull, 0x40297508ed90121cull }, + { 0x400d5e353f7ced92ull, 0x4029798dbf9fab70ull }, + { 0x400d604189374bc7ull, 0x40297e135f0752e5ull }, + { 0x400d624dd2f1a9fcull, 0x40298299cbeb7bb3ull }, + { 0x400d645a1cac0831ull, 0x4029872106709f87ull }, + { 0x400d666666666667ull, 0x40298ba90ebb3e8bull }, + { 0x400d6872b020c49cull, 0x40299031e4efdf5bull }, + { 0x400d6a7ef9db22d1ull, 0x402994bb89330f19ull }, + { 0x400d6c8b43958106ull, 0x40299945fba9615dull }, + { 0x400d6e978d4fdf3cull, 0x40299dd13c777043ull }, + { 0x400d70a3d70a3d71ull, 0x4029a25d4bc1dc5eull }, + { 0x400d72b020c49ba6ull, 0x4029a6ea29ad4ccaull }, + { 0x400d74bc6a7ef9dbull, 0x4029ab77d65e6f1eull }, + { 0x400d76c8b4395811ull, 0x4029b00651f9f77bull }, + { 0x400d78d4fdf3b646ull, 0x4029b4959ca4a07dull }, + { 0x400d7ae147ae147bull, 0x4029b925b6832b4aull }, + { 0x400d7ced916872b0ull, 0x4029bdb69fba5f8full }, + { 0x400d7ef9db22d0e6ull, 0x4029c248586f0b80ull }, + { 0x400d810624dd2f1bull, 0x4029c6dae0c603d6ull }, + { 0x400d83126e978d50ull, 0x4029cb6e38e423d7ull }, + { 0x400d851eb851eb85ull, 0x4029d00260ee4d52ull }, + { 0x400d872b020c49bbull, 0x4029d497590968a8ull }, + { 0x400d89374bc6a7f0ull, 0x4029d92d215a64bbull }, + { 0x400d8b4395810625ull, 0x4029ddc3ba063707ull }, + { 0x400d8d4fdf3b645aull, 0x4029e25b2331db91ull }, + { 0x400d8f5c28f5c290ull, 0x4029e6f35d0254f3ull }, + { 0x400d916872b020c5ull, 0x4029eb8c679cac53ull }, + { 0x400d9374bc6a7efaull, 0x4029f0264325f16full }, + { 0x400d95810624dd2full, 0x4029f4c0efc33a98ull }, + { 0x400d978d4fdf3b65ull, 0x4029f95c6d99a4b5ull }, + { 0x400d99999999999aull, 0x4029fdf8bcce533eull }, + { 0x400d9ba5e353f7cfull, 0x402a0295dd86704aull }, + { 0x400d9db22d0e5604ull, 0x402a0733cfe72c86ull }, + { 0x400d9fbe76c8b43aull, 0x402a0bd29415bf39ull }, + { 0x400da1cac083126full, 0x402a10722a376642ull }, + { 0x400da3d70a3d70a4ull, 0x402a151292716622ull }, + { 0x400da5e353f7ced9ull, 0x402a19b3cce909f3ull }, + { 0x400da7ef9db22d0full, 0x402a1e55d9c3a372ull }, + { 0x400da9fbe76c8b44ull, 0x402a22f8b9268af6ull }, + { 0x400dac083126e979ull, 0x402a279c6b371f7cull }, + { 0x400dae147ae147aeull, 0x402a2c40f01ac6a1ull }, + { 0x400db020c49ba5e4ull, 0x402a30e647f6eca9ull }, + { 0x400db22d0e560419ull, 0x402a358c72f10474ull }, + { 0x400db4395810624eull, 0x402a3a33712e8790ull }, + { 0x400db645a1cac083ull, 0x402a3edb42d4f62full }, + { 0x400db851eb851eb8ull, 0x402a4383e809d72bull }, + { 0x400dba5e353f7ceeull, 0x402a482d60f2b80aull }, + { 0x400dbc6a7ef9db23ull, 0x402a4cd7adb52cf6ull }, + { 0x400dbe76c8b43958ull, 0x402a5182ce76d0caull }, + { 0x400dc083126e978dull, 0x402a562ec35d450dull }, + { 0x400dc28f5c28f5c3ull, 0x402a5adb8c8e31f5ull }, + { 0x400dc49ba5e353f8ull, 0x402a5f892a2f4662ull }, + { 0x400dc6a7ef9db22dull, 0x402a64379c6637ebull }, + { 0x400dc8b439581062ull, 0x402a68e6e358c2d4ull }, + { 0x400dcac083126e98ull, 0x402a6d96ff2caa18ull }, + { 0x400dcccccccccccdull, 0x402a7247f007b760ull }, + { 0x400dced916872b02ull, 0x402a76f9b60fbb0full }, + { 0x400dd0e560418937ull, 0x402a7bac516a8c3dull }, + { 0x400dd2f1a9fbe76dull, 0x402a805fc23e08bbull }, + { 0x400dd4fdf3b645a2ull, 0x402a851408b0150eull }, + { 0x400dd70a3d70a3d7ull, 0x402a89c924e69c79ull }, + { 0x400dd916872b020cull, 0x402a8e7f170790faull }, + { 0x400ddb22d0e56042ull, 0x402a9335df38eb4cull }, + { 0x400ddd2f1a9fbe77ull, 0x402a97ed7da0aae3ull }, + { 0x400ddf3b645a1cacull, 0x402a9ca5f264d5f4ull }, + { 0x400de147ae147ae1ull, 0x402aa15f3dab7977ull }, + { 0x400de353f7ced917ull, 0x402aa6195f9aa925ull }, + { 0x400de5604189374cull, 0x402aaad458587f70ull }, + { 0x400de76c8b439581ull, 0x402aaf90280b1d99ull }, + { 0x400de978d4fdf3b6ull, 0x402ab44cced8aba0ull }, + { 0x400deb851eb851ecull, 0x402ab90a4ce7584full }, + { 0x400ded916872b021ull, 0x402abdc8a25d592full }, + { 0x400def9db22d0e56ull, 0x402ac287cf60ea98ull }, + { 0x400df1a9fbe76c8bull, 0x402ac747d4184fabull }, + { 0x400df3b645a1cac1ull, 0x402acc08b0a9d255ull }, + { 0x400df5c28f5c28f6ull, 0x402ad0ca653bc346ull }, + { 0x400df7ced916872bull, 0x402ad58cf1f47a06ull }, + { 0x400df9db22d0e560ull, 0x402ada5056fa54e6ull }, + { 0x400dfbe76c8b4396ull, 0x402adf149473b90aull }, + { 0x400dfdf3b645a1cbull, 0x402ae3d9aa871260ull }, + { 0x400e000000000000ull, 0x402ae89f995ad3adull }, + { 0x400e020c49ba5e35ull, 0x402aed666115768aull }, + { 0x400e04189374bc6bull, 0x402af22e01dd7b64ull }, + { 0x400e0624dd2f1aa0ull, 0x402af6f67bd96978ull }, + { 0x400e083126e978d5ull, 0x402afbbfcf2fcee0ull }, + { 0x400e0a3d70a3d70aull, 0x402b0089fc07408dull }, + { 0x400e0c49ba5e3540ull, 0x402b055502865a4aull }, + { 0x400e0e5604189375ull, 0x402b0a20e2d3beb6ull }, + { 0x400e10624dd2f1aaull, 0x402b0eed9d161753ull }, + { 0x400e126e978d4fdfull, 0x402b13bb3174147cull }, + { 0x400e147ae147ae15ull, 0x402b1889a0146d70ull }, + { 0x400e16872b020c4aull, 0x402b1d58e91de043ull }, + { 0x400e189374bc6a7full, 0x402b22290cb731f1ull }, + { 0x400e1a9fbe76c8b4ull, 0x402b26fa0b072e57ull }, + { 0x400e1cac083126eaull, 0x402b2bcbe434a836ull }, + { 0x400e1eb851eb851full, 0x402b309e9866792bull }, + { 0x400e20c49ba5e354ull, 0x402b357227c381c4ull }, + { 0x400e22d0e5604189ull, 0x402b3a469272a96dull }, + { 0x400e24dd2f1a9fbfull, 0x402b3f1bd89ade81ull }, + { 0x400e26e978d4fdf4ull, 0x402b43f1fa63163cull }, + { 0x400e28f5c28f5c29ull, 0x402b48c8f7f24ccaull }, + { 0x400e2b020c49ba5eull, 0x402b4da0d16f8542ull }, + { 0x400e2d0e56041894ull, 0x402b52798701c9a8ull }, + { 0x400e2f1a9fbe76c9ull, 0x402b575318d02ae8ull }, + { 0x400e3126e978d4feull, 0x402b5c2d8701c0e6ull }, + { 0x400e333333333333ull, 0x402b6108d1bdaa72ull }, + { 0x400e353f7ced9169ull, 0x402b65e4f92b0d50ull }, + { 0x400e374bc6a7ef9eull, 0x402b6ac1fd711631ull }, + { 0x400e395810624dd3ull, 0x402b6f9fdeb6f8c0ull }, + { 0x400e3b645a1cac08ull, 0x402b747e9d23ef9dull }, + { 0x400e3d70a3d70a3eull, 0x402b795e38df3c60ull }, + { 0x400e3f7ced916873ull, 0x402b7e3eb2102790ull }, + { 0x400e4189374bc6a8ull, 0x402b832008de00b6ull }, + { 0x400e4395810624ddull, 0x402b88023d701e54ull }, + { 0x400e45a1cac08313ull, 0x402b8ce54feddde7ull }, + { 0x400e47ae147ae148ull, 0x402b91c9407ea3e5ull }, + { 0x400e49ba5e353f7dull, 0x402b96ae0f49dbc7ull }, + { 0x400e4bc6a7ef9db2ull, 0x402b9b93bc76f804ull }, + { 0x400e4dd2f1a9fbe8ull, 0x402ba07a482d7214ull }, + { 0x400e4fdf3b645a1dull, 0x402ba561b294ca6dull }, + { 0x400e51eb851eb852ull, 0x402baa49fbd4888cull }, + { 0x400e53f7ced91687ull, 0x402baf3324143af2ull }, + { 0x400e5604189374bdull, 0x402bb41d2b7b7726ull }, + { 0x400e5810624dd2f2ull, 0x402bb9081231d9b1ull }, + { 0x400e5a1cac083127ull, 0x402bbdf3d85f0629ull }, + { 0x400e5c28f5c28f5cull, 0x402bc2e07e2aa72dull }, + { 0x400e5e353f7ced92ull, 0x402bc7ce03bc6e66ull }, + { 0x400e604189374bc7ull, 0x402bccbc693c1484ull }, + { 0x400e624dd2f1a9fcull, 0x402bd1abaed1594bull }, + { 0x400e645a1cac0831ull, 0x402bd69bd4a4038aull }, + { 0x400e666666666667ull, 0x402bdb8cdadbe122ull }, + { 0x400e6872b020c49cull, 0x402be07ec1a0c6feull }, + { 0x400e6a7ef9db22d1ull, 0x402be571891a9122ull }, + { 0x400e6c8b43958106ull, 0x402bea65317122a3ull }, + { 0x400e6e978d4fdf3cull, 0x402bef59bacc65aeull }, + { 0x400e70a3d70a3d71ull, 0x402bf44f25544b7cull }, + { 0x400e72b020c49ba6ull, 0x402bf9457130cc66ull }, + { 0x400e74bc6a7ef9dbull, 0x402bfe3c9e89e7dbull }, + { 0x400e76c8b4395811ull, 0x402c0334ad87a466ull }, + { 0x400e78d4fdf3b646ull, 0x402c082d9e520fa5ull }, + { 0x400e7ae147ae147bull, 0x402c0d2771113e59ull }, + { 0x400e7ced916872b0ull, 0x402c122225ed4c60ull }, + { 0x400e7ef9db22d0e6ull, 0x402c171dbd0e5cb5ull }, + { 0x400e810624dd2f1bull, 0x402c1c1a369c9971ull }, + { 0x400e83126e978d50ull, 0x402c211792c033d1ull }, + { 0x400e851eb851eb85ull, 0x402c2615d1a16434ull }, + { 0x400e872b020c49bbull, 0x402c2b14f3686a1full }, + { 0x400e89374bc6a7f0ull, 0x402c3014f83d8c34ull }, + { 0x400e8b4395810625ull, 0x402c3515e0491843ull }, + { 0x400e8d4fdf3b645aull, 0x402c3a17abb36340ull }, + { 0x400e8f5c28f5c290ull, 0x402c3f1a5aa4c94dull }, + { 0x400e916872b020c5ull, 0x402c441ded45adabull }, + { 0x400e9374bc6a7efaull, 0x402c492263be7ad0ull }, + { 0x400e95810624dd2full, 0x402c4e27be37a25dull }, + { 0x400e978d4fdf3b65ull, 0x402c532dfcd99d20ull }, + { 0x400e99999999999aull, 0x402c58351fcceb11ull }, + { 0x400e9ba5e353f7cfull, 0x402c5d3d273a135full }, + { 0x400e9db22d0e5604ull, 0x402c62461349a46aull }, + { 0x400e9fbe76c8b43aull, 0x402c674fe42433c6ull }, + { 0x400ea1cac083126full, 0x402c6c5a99f25e32ull }, + { 0x400ea3d70a3d70a4ull, 0x402c716634dcc7adull }, + { 0x400ea5e353f7ced9ull, 0x402c7672b50c1b68ull }, + { 0x400ea7ef9db22d0full, 0x402c7b801aa90bd1ull }, + { 0x400ea9fbe76c8b44ull, 0x402c808e65dc5286ull }, + { 0x400eac083126e979ull, 0x402c859d96ceb067ull }, + { 0x400eae147ae147aeull, 0x402c8aadada8ed8full }, + { 0x400eb020c49ba5e4ull, 0x402c8fbeaa93d957ull }, + { 0x400eb22d0e560419ull, 0x402c94d08db84a4full }, + { 0x400eb4395810624eull, 0x402c99e3573f1e50ull }, + { 0x400eb645a1cac083ull, 0x402c9ef707513a70ull }, + { 0x400eb851eb851eb8ull, 0x402ca40b9e178b08ull }, + { 0x400eba5e353f7ceeull, 0x402ca9211bbb03b7ull }, + { 0x400ebc6a7ef9db23ull, 0x402cae3780649f58ull }, + { 0x400ebe76c8b43958ull, 0x402cb34ecc3d6017ull }, + { 0x400ec083126e978dull, 0x402cb866ff6e4f63ull }, + { 0x400ec28f5c28f5c3ull, 0x402cbd801a207df4ull }, + { 0x400ec49ba5e353f8ull, 0x402cc29a1c7d03caull }, + { 0x400ec6a7ef9db22dull, 0x402cc7b506ad0032ull }, + { 0x400ec8b439581062ull, 0x402cccd0d8d999c8ull }, + { 0x400ecac083126e98ull, 0x402cd1ed932bfe76ull }, + { 0x400ecccccccccccdull, 0x402cd70b35cd636dull }, + { 0x400eced916872b02ull, 0x402cdc29c0e70539ull }, + { 0x400ed0e560418937ull, 0x402ce14934a227b2ull }, + { 0x400ed2f1a9fbe76dull, 0x402ce66991281608ull }, + { 0x400ed4fdf3b645a2ull, 0x402ceb8ad6a222b8ull }, + { 0x400ed70a3d70a3d7ull, 0x402cf0ad0539a79bull }, + { 0x400ed916872b020cull, 0x402cf5d01d1805deull }, + { 0x400edb22d0e56042ull, 0x402cfaf41e66a60cull }, + { 0x400edd2f1a9fbe77ull, 0x402d0019094ef7feull }, + { 0x400edf3b645a1cacull, 0x402d053eddfa72f3ull }, + { 0x400ee147ae147ae1ull, 0x402d0a659c929581ull }, + { 0x400ee353f7ced917ull, 0x402d0f8d4540e5a1ull }, + { 0x400ee5604189374cull, 0x402d14b5d82ef0a1ull }, + { 0x400ee76c8b439581ull, 0x402d19df55864b38ull }, + { 0x400ee978d4fdf3b6ull, 0x402d1f09bd70917bull }, + { 0x400eeb851eb851ecull, 0x402d2435101766e5ull }, + { 0x400eed916872b021ull, 0x402d29614da4764dull }, + { 0x400eef9db22d0e56ull, 0x402d2e8e764171f9ull }, + { 0x400ef1a9fbe76c8bull, 0x402d33bc8a181390ull }, + { 0x400ef3b645a1cac1ull, 0x402d38eb89521c27ull }, + { 0x400ef5c28f5c28f6ull, 0x402d3e1b74195431ull }, + { 0x400ef7ced916872bull, 0x402d434c4a978b97ull }, + { 0x400ef9db22d0e560ull, 0x402d487e0cf699aaull }, + { 0x400efbe76c8b4396ull, 0x402d4db0bb605d29ull }, + { 0x400efdf3b645a1cbull, 0x402d52e455febc3eull }, + { 0x400f000000000000ull, 0x402d5818dcfba487ull }, + { 0x400f020c49ba5e35ull, 0x402d5d4e50810b13ull }, + { 0x400f04189374bc6bull, 0x402d6284b0b8ec64ull }, + { 0x400f0624dd2f1aa0ull, 0x402d67bbfdcd4c6bull }, + { 0x400f083126e978d5ull, 0x402d6cf437e83693ull }, + { 0x400f0a3d70a3d70aull, 0x402d722d5f33bdbfull }, + { 0x400f0c49ba5e3540ull, 0x402d776773d9fc47ull }, + { 0x400f0e5604189375ull, 0x402d7ca2760513f9ull }, + { 0x400f10624dd2f1aaull, 0x402d81de65df2e25ull }, + { 0x400f126e978d4fdfull, 0x402d871b43927b91ull }, + { 0x400f147ae147ae15ull, 0x402d8c590f493486ull }, + { 0x400f16872b020c4aull, 0x402d9197c92d98c2ull }, + { 0x400f189374bc6a7full, 0x402d96d77169ef8dull }, + { 0x400f1a9fbe76c8b4ull, 0x402d9c18082887aaull }, + { 0x400f1cac083126eaull, 0x402da1598d93b764ull }, + { 0x400f1eb851eb851full, 0x402da69c01d5dc81ull }, + { 0x400f20c49ba5e354ull, 0x402dabdf65195c55ull }, + { 0x400f22d0e5604189ull, 0x402db123b788a3b6ull }, + { 0x400f24dd2f1a9fbfull, 0x402db668f94e2706ull }, + { 0x400f26e978d4fdf4ull, 0x402dbbaf2a946229ull }, + { 0x400f28f5c28f5c29ull, 0x402dc0f64b85d892ull }, + { 0x400f2b020c49ba5eull, 0x402dc63e5c4d1542ull }, + { 0x400f2d0e56041894ull, 0x402dcb875d14aac7ull }, + { 0x400f2f1a9fbe76c9ull, 0x402dd0d14e073335ull }, + { 0x400f3126e978d4feull, 0x402dd61c2f4f503aull }, + { 0x400f333333333333ull, 0x402ddb680117ab12ull }, + { 0x400f353f7ced9169ull, 0x402de0b4c38af48cull }, + { 0x400f374bc6a7ef9eull, 0x402de60276d3e508ull }, + { 0x400f395810624dd3ull, 0x402deb511b1d3c7dull }, + { 0x400f3b645a1cac08ull, 0x402df0a0b091c27bull }, + { 0x400f3d70a3d70a3eull, 0x402df5f1375c462aull }, + { 0x400f3f7ced916873ull, 0x402dfb42afa79e44ull }, + { 0x400f4189374bc6a8ull, 0x402e0095199ea926ull }, + { 0x400f4395810624ddull, 0x402e05e8756c4cc6ull }, + { 0x400f45a1cac08313ull, 0x402e0b3cc33b76baull }, + { 0x400f47ae147ae148ull, 0x402e109203371c30ull }, + { 0x400f49ba5e353f7dull, 0x402e15e8358a39fbull }, + { 0x400f4bc6a7ef9db2ull, 0x402e1b3f5a5fd490ull }, + { 0x400f4dd2f1a9fbe8ull, 0x402e209771e2f808ull }, + { 0x400f4fdf3b645a1dull, 0x402e25f07c3eb818ull }, + { 0x400f51eb851eb852ull, 0x402e2b4a799e3023ull }, + { 0x400f53f7ced91687ull, 0x402e30a56a2c8330ull }, + { 0x400f5604189374bdull, 0x402e36014e14dbf1ull }, + { 0x400f5810624dd2f2ull, 0x402e3b5e25826cb9ull }, + { 0x400f5a1cac083127ull, 0x402e40bbf0a06f90ull }, + { 0x400f5c28f5c28f5cull, 0x402e461aaf9a2624ull }, + { 0x400f5e353f7ced92ull, 0x402e4b7a629ad9d6ull }, + { 0x400f604189374bc7ull, 0x402e50db09cddbaeull }, + { 0x400f624dd2f1a9fcull, 0x402e563ca55e846bull }, + { 0x400f645a1cac0831ull, 0x402e5b9f3578347dull }, + { 0x400f666666666667ull, 0x402e6102ba465407ull }, + { 0x400f6872b020c49cull, 0x402e666733f452dbull }, + { 0x400f6a7ef9db22d1ull, 0x402e6bcca2ada889ull }, + { 0x400f6c8b43958106ull, 0x402e7133069dd452ull }, + { 0x400f6e978d4fdf3cull, 0x402e769a5ff05d37ull }, + { 0x400f70a3d70a3d71ull, 0x402e7c02aed0d1e8ull }, + { 0x400f72b020c49ba6ull, 0x402e816bf36ac8daull }, + { 0x400f74bc6a7ef9dbull, 0x402e86d62de9e039ull }, + { 0x400f76c8b4395811ull, 0x402e8c415e79bdf5ull }, + { 0x400f78d4fdf3b646ull, 0x402e91ad85460fb5ull }, + { 0x400f7ae147ae147bull, 0x402e971aa27a8ae7ull }, + { 0x400f7ced916872b0ull, 0x402e9c88b642ecbbull }, + { 0x400f7ef9db22d0e6ull, 0x402ea1f7c0cafa25ull }, + { 0x400f810624dd2f1bull, 0x402ea767c23e7fd6ull }, + { 0x400f83126e978d50ull, 0x402eacd8bac95250ull }, + { 0x400f851eb851eb85ull, 0x402eb24aaa974dd7ull }, + { 0x400f872b020c49bbull, 0x402eb7bd91d4567dull }, + { 0x400f89374bc6a7f0ull, 0x402ebd3170ac5815ull }, + { 0x400f8b4395810625ull, 0x402ec2a6474b4646ull }, + { 0x400f8d4fdf3b645aull, 0x402ec81c15dd1c82ull }, + { 0x400f8f5c28f5c290ull, 0x402ecd92dc8dde0dull }, + { 0x400f916872b020c5ull, 0x402ed30a9b8995f2ull }, + { 0x400f9374bc6a7efaull, 0x402ed88352fc5715ull }, + { 0x400f95810624dd2full, 0x402eddfd03123c2cull }, + { 0x400f978d4fdf3b65ull, 0x402ee377abf767c2ull }, + { 0x400f99999999999aull, 0x402ee8f34dd80430ull }, + { 0x400f9ba5e353f7cfull, 0x402eee6fe8e043aeull }, + { 0x400f9db22d0e5604ull, 0x402ef3ed7d3c604bull }, + { 0x400f9fbe76c8b43aull, 0x402ef96c0b189bf0ull }, + { 0x400fa1cac083126full, 0x402efeeb92a1405bull }, + { 0x400fa3d70a3d70a4ull, 0x402f046c14029f2eull }, + { 0x400fa5e353f7ced9ull, 0x402f09ed8f6911e7ull }, + { 0x400fa7ef9db22d0full, 0x402f0f700500f9e3ull }, + { 0x400fa9fbe76c8b44ull, 0x402f14f374f6c05cull }, + { 0x400fac083126e979ull, 0x402f1a77df76d674ull }, + { 0x400fae147ae147aeull, 0x402f1ffd44adb52dull }, + { 0x400fb020c49ba5e4ull, 0x402f2583a4c7dd71ull }, + { 0x400fb22d0e560419ull, 0x402f2b0afff1d80aull }, + { 0x400fb4395810624eull, 0x402f3093565835b1ull }, + { 0x400fb645a1cac083ull, 0x402f361ca8278f03ull }, + { 0x400fb851eb851eb9ull, 0x402f3ba6f58c848full }, + { 0x400fba5e353f7ceeull, 0x402f41323eb3bec2ull }, + { 0x400fbc6a7ef9db23ull, 0x402f46be83c9ee04ull }, + { 0x400fbe76c8b43958ull, 0x402f4c4bc4fbcaa5ull }, + { 0x400fc083126e978dull, 0x402f51da027614e8ull }, + { 0x400fc28f5c28f5c3ull, 0x402f57693c659502ull }, + { 0x400fc49ba5e353f8ull, 0x402f5cf972f71b14ull }, + { 0x400fc6a7ef9db22dull, 0x402f628aa6577f3cull }, + { 0x400fc8b439581062ull, 0x402f681cd6b3a189ull }, + { 0x400fcac083126e98ull, 0x402f6db004386a08ull }, + { 0x400fcccccccccccdull, 0x402f73442f12c8b2ull }, + { 0x400fced916872b02ull, 0x402f78d9576fb584ull }, + { 0x400fd0e560418937ull, 0x402f7e6f7d7c3074ull }, + { 0x400fd2f1a9fbe76dull, 0x402f8406a1654178ull }, + { 0x400fd4fdf3b645a2ull, 0x402f899ec357f87cull }, + { 0x400fd70a3d70a3d7ull, 0x402f8f37e3816d72ull }, + { 0x400fd916872b020cull, 0x402f94d2020ec04cull }, + { 0x400fdb22d0e56042ull, 0x402f9a6d1f2d1902ull }, + { 0x400fdd2f1a9fbe77ull, 0x402fa0093b09a786ull }, + { 0x400fdf3b645a1cacull, 0x402fa5a655d1a3daull }, + { 0x400fe147ae147ae1ull, 0x402fab446fb24e01ull }, + { 0x400fe353f7ced917ull, 0x402fb0e388d8ee0bull }, + { 0x400fe5604189374cull, 0x402fb683a172d409ull }, + { 0x400fe76c8b439581ull, 0x402fbc24b9ad581full }, + { 0x400fe978d4fdf3b6ull, 0x402fc1c6d1b5da7dull }, + { 0x400feb851eb851ecull, 0x402fc769e9b9c35full }, + { 0x400fed916872b021ull, 0x402fcd0e01e6830dull }, + { 0x400fef9db22d0e56ull, 0x402fd2b31a6991e5ull }, + { 0x400ff1a9fbe76c8bull, 0x402fd85933707056ull }, + { 0x400ff3b645a1cac1ull, 0x402fde004d28a6e5ull }, + { 0x400ff5c28f5c28f6ull, 0x402fe3a867bfc624ull }, + { 0x400ff7ced916872bull, 0x402fe951836366c3ull }, + { 0x400ff9db22d0e560ull, 0x402feefba0412988ull }, + { 0x400ffbe76c8b4396ull, 0x402ff4a6be86b756ull }, + { 0x400ffdf3b645a1cbull, 0x402ffa52de61c120ull }, + { 0x4010000000000000ull, 0x4030000000000000ull }, + { 0x4010010624dd2f1bull, 0x403002d711c79a97ull }, + { 0x4010020c49ba5e35ull, 0x403005aea49e94f9ull }, + { 0x401003126e978d50ull, 0x40300886b89bd7e7ull }, + { 0x401004189374bc6bull, 0x40300b5f4dd65029ull }, + { 0x4010051eb851eb85ull, 0x40300e386464ee98ull }, + { 0x40100624dd2f1aa0ull, 0x40301111fc5ea82bull }, + { 0x4010072b020c49baull, 0x403013ec15da75dcull }, + { 0x4010083126e978d5ull, 0x403016c6b0ef54c9ull }, + { 0x401009374bc6a7f0ull, 0x403019a1cdb44619ull }, + { 0x40100a3d70a3d70aull, 0x40301c7d6c404f0bull }, + { 0x40100b4395810625ull, 0x40301f598caa78faull }, + { 0x40100c49ba5e3540ull, 0x403022362f09d150ull }, + { 0x40100d4fdf3b645aull, 0x4030251353756991ull }, + { 0x40100e5604189375ull, 0x403027f0fa04575dull }, + { 0x40100f5c28f5c28full, 0x40302acf22cdb467ull }, + { 0x401010624dd2f1aaull, 0x40302dadcde89e83ull }, + { 0x4010116872b020c5ull, 0x4030308cfb6c3799ull }, + { 0x4010126e978d4fdfull, 0x4030336cab6fa5acull }, + { 0x40101374bc6a7efaull, 0x4030364cde0a12e2ull }, + { 0x4010147ae147ae15ull, 0x4030392d9352ad77ull }, + { 0x401015810624dd2full, 0x40303c0ecb60a7c3ull }, + { 0x401016872b020c4aull, 0x40303ef0864b3846ull }, + { 0x4010178d4fdf3b64ull, 0x403041d2c4299991ull }, + { 0x4010189374bc6a7full, 0x403044b585130a64ull }, + { 0x401019999999999aull, 0x40304798c91ecd93ull }, + { 0x40101a9fbe76c8b4ull, 0x40304a7c90642a14ull }, + { 0x40101ba5e353f7cfull, 0x40304d60dafa6b08ull }, + { 0x40101cac083126eaull, 0x40305045a8f8dfaaull }, + { 0x40101db22d0e5604ull, 0x4030532afa76db57ull }, + { 0x40101eb851eb851full, 0x40305610cf8bb59aull }, + { 0x40101fbe76c8b439ull, 0x403058f7284eca17ull }, + { 0x401020c49ba5e354ull, 0x40305bde04d778a2ull }, + { 0x401021cac083126full, 0x40305ec5653d252dull }, + { 0x401022d0e5604189ull, 0x403061ad499737d2ull }, + { 0x401023d70a3d70a4ull, 0x40306495b1fd1cd8ull }, + { 0x401024dd2f1a9fbfull, 0x4030677e9e8644abull }, + { 0x401025e353f7ced9ull, 0x40306a680f4a23daull }, + { 0x401026e978d4fdf4ull, 0x40306d520460332cull }, + { 0x401027ef9db22d0eull, 0x4030703c7ddfef84ull }, + { 0x401028f5c28f5c29ull, 0x403073277be0d9fcull }, + { 0x401029fbe76c8b44ull, 0x40307612fe7a77d3ull }, + { 0x40102b020c49ba5eull, 0x403078ff05c45272ull }, + { 0x40102c083126e979ull, 0x40307beb91d5f77cull }, + { 0x40102d0e56041894ull, 0x40307ed8a2c6f8b7ull }, + { 0x40102e147ae147aeull, 0x403081c638aeec18ull }, + { 0x40102f1a9fbe76c9ull, 0x403084b453a56bceull }, + { 0x40103020c49ba5e3ull, 0x403087a2f3c2162cull }, + { 0x40103126e978d4feull, 0x40308a92191c8dc2ull }, + { 0x4010322d0e560419ull, 0x40308d81c3cc7948ull }, + { 0x4010333333333333ull, 0x40309071f3e983acull }, + { 0x401034395810624eull, 0x40309362a98b5c15ull }, + { 0x4010353f7ced9169ull, 0x40309653e4c9b5d7ull }, + { 0x40103645a1cac083ull, 0x40309945a5bc487bull }, + { 0x4010374bc6a7ef9eull, 0x40309c37ec7acfc8ull }, + { 0x40103851eb851eb8ull, 0x40309f2ab91d0bb0ull }, + { 0x4010395810624dd3ull, 0x4030a21e0bbac068ull }, + { 0x40103a5e353f7ceeull, 0x4030a511e46bb655ull }, + { 0x40103b645a1cac08ull, 0x4030a8064347ba12ull }, + { 0x40103c6a7ef9db23ull, 0x4030aafb28669c81ull }, + { 0x40103d70a3d70a3eull, 0x4030adf093e032b1ull }, + { 0x40103e76c8b43958ull, 0x4030b0e685cc55edull }, + { 0x40103f7ced916873ull, 0x4030b3dcfe42e3c7ull }, + { 0x40104083126e978dull, 0x4030b6d3fd5bbdfeull }, + { 0x40104189374bc6a8ull, 0x4030b9cb832eca9dull }, + { 0x4010428f5c28f5c3ull, 0x4030bcc38fd3f3e4ull }, + { 0x40104395810624ddull, 0x4030bfbc2363284full }, + { 0x4010449ba5e353f8ull, 0x4030c2b53df45aa5ull }, + { 0x401045a1cac08313ull, 0x4030c5aedf9f81e4ull }, + { 0x401046a7ef9db22dull, 0x4030c8a9087c9949ull }, + { 0x401047ae147ae148ull, 0x4030cba3b8a3a060ull }, + { 0x401048b439581062ull, 0x4030ce9ef02c9ae6ull }, + { 0x401049ba5e353f7dull, 0x4030d19aaf2f90ecull }, + { 0x40104ac083126e98ull, 0x4030d496f5c48ebbull }, + { 0x40104bc6a7ef9db2ull, 0x4030d793c403a4e2ull }, + { 0x40104ccccccccccdull, 0x4030da911a04e83full }, + { 0x40104dd2f1a9fbe8ull, 0x4030dd8ef7e071edull }, + { 0x40104ed916872b02ull, 0x4030e08d5dae5f4dull }, + { 0x40104fdf3b645a1dull, 0x4030e38c4b86d212ull }, + { 0x401050e560418937ull, 0x4030e68bc181f02bull }, + { 0x401051eb851eb852ull, 0x4030e98bbfb7e3ddull }, + { 0x401052f1a9fbe76dull, 0x4030ec8c4640dbadull }, + { 0x401053f7ced91687ull, 0x4030ef8d55350a6cull }, + { 0x401054fdf3b645a2ull, 0x4030f28eecaca740ull }, + { 0x40105604189374bdull, 0x4030f5910cbfed90ull }, + { 0x4010570a3d70a3d7ull, 0x4030f893b5871d13ull }, + { 0x40105810624dd2f2ull, 0x4030fb96e71a79d4ull }, + { 0x40105916872b020cull, 0x4030fe9aa1924c24ull }, + { 0x40105a1cac083127ull, 0x4031019ee506e0adull }, + { 0x40105b22d0e56042ull, 0x403104a3b1908860ull }, + { 0x40105c28f5c28f5cull, 0x403107a907479882ull }, + { 0x40105d2f1a9fbe77ull, 0x40310aaee6446aafull }, + { 0x40105e353f7ced92ull, 0x40310db54e9f5ccfull }, + { 0x40105f3b645a1cacull, 0x403110bc4070d11cull }, + { 0x4010604189374bc7ull, 0x403113c3bbd12e2eull }, + { 0x40106147ae147ae1ull, 0x403116cbc0d8dee4ull }, + { 0x4010624dd2f1a9fcull, 0x403119d44fa05282ull }, + { 0x40106353f7ced917ull, 0x40311cdd683ffc95ull }, + { 0x4010645a1cac0831ull, 0x40311fe70ad05505ull }, + { 0x401065604189374cull, 0x403122f13769d818ull }, + { 0x4010666666666666ull, 0x403125fbee250663ull }, + { 0x4010676c8b439581ull, 0x403129072f1a64dfull }, + { 0x40106872b020c49cull, 0x40312c12fa627cd6ull }, + { 0x40106978d4fdf3b6ull, 0x40312f1f5015dbeeull }, + { 0x40106a7ef9db22d1ull, 0x4031322c304d1431ull }, + { 0x40106b851eb851ecull, 0x403135399b20bbfdull }, + { 0x40106c8b43958106ull, 0x4031384790a96e0cull }, + { 0x40106d916872b021ull, 0x40313b5610ffc982ull }, + { 0x40106e978d4fdf3bull, 0x40313e651c3c71d4ull }, + { 0x40106f9db22d0e56ull, 0x40314174b2780ee0ull }, + { 0x401070a3d70a3d71ull, 0x40314484d3cb4ce1ull }, + { 0x401071a9fbe76c8bull, 0x40314795804edc6eull }, + { 0x401072b020c49ba6ull, 0x40314aa6b81b728dull }, + { 0x401073b645a1cac1ull, 0x40314db87b49c89bull }, + { 0x401074bc6a7ef9dbull, 0x403150cac9f29c58ull }, + { 0x401075c28f5c28f6ull, 0x403153dda42eaff5ull }, + { 0x401076c8b4395810ull, 0x403156f10a16c9f9ull }, + { 0x401077ced916872bull, 0x40315a04fbc3b55dull }, + { 0x401078d4fdf3b646ull, 0x40315d19794e4179ull }, + { 0x401079db22d0e560ull, 0x4031602e82cf420bull }, + { 0x40107ae147ae147bull, 0x40316344185f8f43ull }, + { 0x40107be76c8b4396ull, 0x4031665a3a1805b1ull }, + { 0x40107ced916872b0ull, 0x40316970e811864eull }, + { 0x40107df3b645a1cbull, 0x40316c882264f688ull }, + { 0x40107ef9db22d0e5ull, 0x40316f9fe92b402bull }, + { 0x4010800000000000ull, 0x403172b83c7d517bull }, + { 0x4010810624dd2f1bull, 0x403175d11c741d21ull }, + { 0x4010820c49ba5e35ull, 0x403178ea89289a33ull }, + { 0x401083126e978d50ull, 0x40317c0482b3c43full }, + { 0x401084189374bc6bull, 0x40317f1f092e9b38ull }, + { 0x4010851eb851eb85ull, 0x4031823a1cb22383ull }, + { 0x40108624dd2f1aa0ull, 0x40318555bd5765fdull }, + { 0x4010872b020c49baull, 0x40318871eb376fe9ull }, + { 0x4010883126e978d5ull, 0x40318b8ea66b5309ull }, + { 0x401089374bc6a7f0ull, 0x40318eabef0c2588ull }, + { 0x40108a3d70a3d70aull, 0x403191c9c5330208ull }, + { 0x40108b4395810625ull, 0x403194e828f907a5ull }, + { 0x40108c49ba5e3540ull, 0x403198071a7759eaull }, + { 0x40108d4fdf3b645aull, 0x40319b2699c720d8ull }, + { 0x40108e5604189375ull, 0x40319e46a70188eeull }, + { 0x40108f5c28f5c28full, 0x4031a167423fc31aull }, + { 0x401090624dd2f1aaull, 0x4031a4886b9b04cdull }, + { 0x4010916872b020c5ull, 0x4031a7aa232c87eaull }, + { 0x4010926e978d4fdfull, 0x4031aacc690d8acbull }, + { 0x40109374bc6a7efaull, 0x4031adef3d575053ull }, + { 0x4010947ae147ae15ull, 0x4031b112a0231fd3ull }, + { 0x401095810624dd2full, 0x4031b436918a451dull }, + { 0x401096872b020c4aull, 0x4031b75b11a61086ull }, + { 0x4010978d4fdf3b64ull, 0x4031ba80208fd6d7ull }, + { 0x4010989374bc6a7full, 0x4031bda5be60f165ull }, + { 0x401099999999999aull, 0x4031c0cbeb32bdfaull }, + { 0x40109a9fbe76c8b4ull, 0x4031c3f2a71e9ee1ull }, + { 0x40109ba5e353f7cfull, 0x4031c719f23dfaf1ull }, + { 0x40109cac083126eaull, 0x4031ca41ccaa3d79ull }, + { 0x40109db22d0e5604ull, 0x4031cd6a367cd64cull }, + { 0x40109eb851eb851full, 0x4031d0932fcf39c7ull }, + { 0x40109fbe76c8b439ull, 0x4031d3bcb8bae0c5ull }, + { 0x4010a0c49ba5e354ull, 0x4031d6e6d15948adull }, + { 0x4010a1cac083126full, 0x4031da1179c3f368ull }, + { 0x4010a2d0e5604189ull, 0x4031dd3cb2146762ull }, + { 0x4010a3d70a3d70a4ull, 0x4031e0687a642f9aull }, + { 0x4010a4dd2f1a9fbfull, 0x4031e394d2ccdb90ull }, + { 0x4010a5e353f7ced9ull, 0x4031e6c1bb67ff4bull }, + { 0x4010a6e978d4fdf4ull, 0x4031e9ef344f3366ull }, + { 0x4010a7ef9db22d0eull, 0x4031ed1d3d9c14fdull }, + { 0x4010a8f5c28f5c29ull, 0x4031f04bd76845c1ull }, + { 0x4010a9fbe76c8b44ull, 0x4031f37b01cd6be9ull }, + { 0x4010ab020c49ba5eull, 0x4031f6aabce53238ull }, + { 0x4010ac083126e979ull, 0x4031f9db08c94809ull }, + { 0x4010ad0e56041894ull, 0x4031fd0be593613eull }, + { 0x4010ae147ae147aeull, 0x4032003d535d3649ull }, + { 0x4010af1a9fbe76c9ull, 0x4032036f52408433ull }, + { 0x4010b020c49ba5e3ull, 0x403206a1e2570c8eull }, + { 0x4010b126e978d4feull, 0x403209d503ba9588ull }, + { 0x4010b22d0e560419ull, 0x40320d08b684e9dcull }, + { 0x4010b33333333333ull, 0x4032103cfacfd8d6ull }, + { 0x4010b4395810624eull, 0x40321371d0b53661ull }, + { 0x4010b53f7ced9169ull, 0x403216a7384edaf5ull }, + { 0x4010b645a1cac083ull, 0x403219dd31b6a3a1ull }, + { 0x4010b74bc6a7ef9eull, 0x40321d13bd067213ull }, + { 0x4010b851eb851eb8ull, 0x4032204ada582c85ull }, + { 0x4010b95810624dd3ull, 0x4032238289c5bdd8ull }, + { 0x4010ba5e353f7ceeull, 0x403226bacb69157bull }, + { 0x4010bb645a1cac08ull, 0x403229f39f5c277aull }, + { 0x4010bc6a7ef9db23ull, 0x40322d2d05b8ec83ull }, + { 0x4010bd70a3d70a3eull, 0x40323066fe9961daull }, + { 0x4010be76c8b43958ull, 0x403233a18a17895cull }, + { 0x4010bf7ced916873ull, 0x403236dca84d6992ull }, + { 0x4010c083126e978dull, 0x40323a1859550d93ull }, + { 0x4010c189374bc6a8ull, 0x40323d549d488524ull }, + { 0x4010c28f5c28f5c3ull, 0x403240917441e4a0ull }, + { 0x4010c395810624ddull, 0x403243cede5b4505ull }, + { 0x4010c49ba5e353f8ull, 0x4032470cdbaec3faull }, + { 0x4010c5a1cac08313ull, 0x40324a4b6c5683c0ull }, + { 0x4010c6a7ef9db22dull, 0x40324d8a906cab3cull }, + { 0x4010c7ae147ae148ull, 0x403250ca480b6601ull }, + { 0x4010c8b439581062ull, 0x4032540a934ce43aull }, + { 0x4010c9ba5e353f7dull, 0x4032574b724b5ac5ull }, + { 0x4010cac083126e98ull, 0x40325a8ce521031eull }, + { 0x4010cbc6a7ef9db2ull, 0x40325dceebe81b67ull }, + { 0x4010cccccccccccdull, 0x4032611186bae675ull }, + { 0x4010cdd2f1a9fbe8ull, 0x40326454b5b3abbeull }, + { 0x4010ced916872b02ull, 0x4032679878ecb760ull }, + { 0x4010cfdf3b645a1dull, 0x40326adcd0805a2full }, + { 0x4010d0e560418937ull, 0x40326e21bc88e99dull }, + { 0x4010d1eb851eb852ull, 0x403271673d20bfd8ull }, + { 0x4010d2f1a9fbe76dull, 0x403274ad52623baeull }, + { 0x4010d3f7ced91687ull, 0x403277f3fc67c09eull }, + { 0x4010d4fdf3b645a2ull, 0x40327b3b3b4bb6dfull }, + { 0x4010d604189374bdull, 0x40327e830f288b4full }, + { 0x4010d70a3d70a3d7ull, 0x403281cb7818af7cull }, + { 0x4010d810624dd2f2ull, 0x40328514763699afull }, + { 0x4010d916872b020cull, 0x4032885e099cc4d8ull }, + { 0x4010da1cac083127ull, 0x40328ba83265b0a6ull }, + { 0x4010db22d0e56042ull, 0x40328ef2f0abe173ull }, + { 0x4010dc28f5c28f5cull, 0x4032923e4489e04dull }, + { 0x4010dd2f1a9fbe77ull, 0x4032958a2e1a3b04ull }, + { 0x4010de353f7ced92ull, 0x403298d6ad778411ull }, + { 0x4010df3b645a1cacull, 0x40329c23c2bc52a9ull }, + { 0x4010e04189374bc7ull, 0x40329f716e0342c0ull }, + { 0x4010e147ae147ae1ull, 0x4032a2bfaf66f4f6ull }, + { 0x4010e24dd2f1a9fcull, 0x4032a60e87020eb5ull }, + { 0x4010e353f7ced917ull, 0x4032a95df4ef3a14ull }, + { 0x4010e45a1cac0831ull, 0x4032acadf94925e9ull }, + { 0x4010e5604189374cull, 0x4032affe942a85cfull }, + { 0x4010e66666666666ull, 0x4032b34fc5ae1212ull }, + { 0x4010e76c8b439581ull, 0x4032b6a18dee87c7ull }, + { 0x4010e872b020c49cull, 0x4032b9f3ed06a8bbull }, + { 0x4010e978d4fdf3b6ull, 0x4032bd46e3113b7aull }, + { 0x4010ea7ef9db22d1ull, 0x4032c09a70290b5aull }, + { 0x4010eb851eb851ecull, 0x4032c3ee9468e86aull }, + { 0x4010ec8b43958106ull, 0x4032c7434feba778ull }, + { 0x4010ed916872b021ull, 0x4032ca98a2cc2225ull }, + { 0x4010ee978d4fdf3bull, 0x4032cdee8d2536c3ull }, + { 0x4010ef9db22d0e56ull, 0x4032d1450f11c87aull }, + { 0x4010f0a3d70a3d71ull, 0x4032d49c28acbf2cull }, + { 0x4010f1a9fbe76c8bull, 0x4032d7f3da110783ull }, + { 0x4010f2b020c49ba6ull, 0x4032db4c235992f9ull }, + { 0x4010f3b645a1cac1ull, 0x4032dea504a157c8ull }, + { 0x4010f4bc6a7ef9dbull, 0x4032e1fe7e0350f2ull }, + { 0x4010f5c28f5c28f6ull, 0x4032e5588f9a7e4cull }, + { 0x4010f6c8b4395810ull, 0x4032e8b33981e46cull }, + { 0x4010f7ced916872bull, 0x4032ec0e7bd48cbeull }, + { 0x4010f8d4fdf3b646ull, 0x4032ef6a56ad8571ull }, + { 0x4010f9db22d0e560ull, 0x4032f2c6ca27e184ull }, + { 0x4010fae147ae147bull, 0x4032f623d65eb8caull }, + { 0x4010fbe76c8b4396ull, 0x4032f9817b6d27dfull }, + { 0x4010fced916872b0ull, 0x4032fcdfb96e502dull }, + { 0x4010fdf3b645a1cbull, 0x4033003e907d57faull }, + { 0x4010fef9db22d0e5ull, 0x4033039e00b56a4eull }, + { 0x4011000000000000ull, 0x403306fe0a31b715ull }, + { 0x4011010624dd2f1bull, 0x40330a5ead0d7301ull }, + { 0x4011020c49ba5e35ull, 0x40330dbfe963d799ull }, + { 0x401103126e978d50ull, 0x40331121bf502344ull }, + { 0x401104189374bc6bull, 0x403314842eed9935ull }, + { 0x4011051eb851eb85ull, 0x403317e738578174ull }, + { 0x40110624dd2f1aa0ull, 0x40331b4adba928eeull }, + { 0x4011072b020c49baull, 0x40331eaf18fde157ull }, + { 0x4011083126e978d5ull, 0x40332213f071014full }, + { 0x401109374bc6a7f0ull, 0x40332579621de441ull }, + { 0x40110a3d70a3d70aull, 0x403328df6e1fea77ull }, + { 0x40110b4395810625ull, 0x40332c461492791eull }, + { 0x40110c49ba5e3540ull, 0x40332fad5590fa3aull }, + { 0x40110d4fdf3b645aull, 0x403333153136dca8ull }, + { 0x40110e5604189375ull, 0x4033367da79f9431ull }, + { 0x40110f5c28f5c28full, 0x403339e6b8e6996full }, + { 0x401110624dd2f1aaull, 0x40333d50652769e9ull }, + { 0x4011116872b020c5ull, 0x403340baac7d87feull }, + { 0x4011126e978d4fdfull, 0x403344258f047af0ull }, + { 0x40111374bc6a7efaull, 0x403347910cd7ceecull }, + { 0x4011147ae147ae15ull, 0x40334afd261314faull }, + { 0x401115810624dd2full, 0x40334e69dad1e306ull }, + { 0x401116872b020c4aull, 0x403351d72b2fd3ecull }, + { 0x4011178d4fdf3b64ull, 0x4033554517488761ull }, + { 0x4011189374bc6a7full, 0x403358b39f37a20full }, + { 0x401119999999999aull, 0x40335c22c318cd7eull }, + { 0x40111a9fbe76c8b4ull, 0x40335f928307b81eull }, + { 0x40111ba5e353f7cfull, 0x40336302df201555ull }, + { 0x40111cac083126eaull, 0x40336673d77d9d67ull }, + { 0x40111db22d0e5604ull, 0x403369e56c3c0d85ull }, + { 0x40111eb851eb851full, 0x40336d579d7727d8ull }, + { 0x40111fbe76c8b439ull, 0x403370ca6b4ab367ull }, + { 0x401120c49ba5e354ull, 0x4033743dd5d27c36ull }, + { 0x401121cac083126full, 0x403377b1dd2a532cull }, + { 0x401122d0e5604189ull, 0x40337b26816e0e22ull }, + { 0x401123d70a3d70a4ull, 0x40337e9bc2b987ecull }, + { 0x401124dd2f1a9fbfull, 0x40338211a128a044ull }, + { 0x401125e353f7ced9ull, 0x403385881cd73bd9ull }, + { 0x401126e978d4fdf4ull, 0x403388ff35e14455ull }, + { 0x401127ef9db22d0eull, 0x40338c76ec62a84bull }, + { 0x401128f5c28f5c29ull, 0x40338fef40775b50ull }, + { 0x401129fbe76c8b44ull, 0x40339368323b55e5ull }, + { 0x40112b020c49ba5eull, 0x403396e1c1ca9582ull }, + { 0x40112c083126e979ull, 0x40339a5bef411ca1ull }, + { 0x40112d0e56041894ull, 0x40339dd6babaf2abull }, + { 0x40112e147ae147aeull, 0x4033a15224542403ull }, + { 0x40112f1a9fbe76c9ull, 0x4033a4ce2c28c210ull }, + { 0x40113020c49ba5e3ull, 0x4033a84ad254e327ull }, + { 0x40113126e978d4feull, 0x4033abc816f4a2a7ull }, + { 0x4011322d0e560419ull, 0x4033af45fa2420e2ull }, + { 0x4011333333333333ull, 0x4033b2c47bff8328ull }, + { 0x401134395810624eull, 0x4033b6439ca2f3d4ull }, + { 0x4011353f7ced9169ull, 0x4033b9c35c2aa233ull }, + { 0x40113645a1cac083ull, 0x4033bd43bab2c296ull }, + { 0x4011374bc6a7ef9eull, 0x4033c0c4b8578e58ull }, + { 0x40113851eb851eb8ull, 0x4033c446553543cbull }, + { 0x4011395810624dd3ull, 0x4033c7c891682650ull }, + { 0x40113a5e353f7ceeull, 0x4033cb4b6d0c7e42ull }, + { 0x40113b645a1cac08ull, 0x4033cecee83e9903ull }, + { 0x40113c6a7ef9db23ull, 0x4033d253031ac905ull }, + { 0x40113d70a3d70a3eull, 0x4033d5d7bdbd65b4ull }, + { 0x40113e76c8b43958ull, 0x4033d95d1842cb89ull }, + { 0x40113f7ced916873ull, 0x4033dce312c75c0aull }, + { 0x40114083126e978dull, 0x4033e069ad677dbeull }, + { 0x40114189374bc6a8ull, 0x4033e3f0e83f9c42ull }, + { 0x4011428f5c28f5c3ull, 0x4033e778c36c2835ull }, + { 0x40114395810624ddull, 0x4033eb013f099740ull }, + { 0x4011449ba5e353f8ull, 0x4033ee8a5b346428ull }, + { 0x401145a1cac08313ull, 0x4033f21418090eb2ull }, + { 0x401146a7ef9db22dull, 0x4033f59e75a41bb3ull }, + { 0x401147ae147ae148ull, 0x4033f9297422151bull }, + { 0x401148b439581062ull, 0x4033fcb5139f89deull }, + { 0x401149ba5e353f7dull, 0x4034004154390e0cull }, + { 0x40114ac083126e98ull, 0x403403ce360b3ac1ull }, + { 0x40114bc6a7ef9db2ull, 0x4034075bb932ae2bull }, + { 0x40114ccccccccccdull, 0x40340ae9ddcc0b97ull }, + { 0x40114dd2f1a9fbe8ull, 0x40340e78a3f3fb5cull }, + { 0x40114ed916872b02ull, 0x403412080bc72ae9ull }, + { 0x40114fdf3b645a1dull, 0x4034159815624cceull }, + { 0x401150e560418937ull, 0x40341928c0e218a4ull }, + { 0x401151eb851eb852ull, 0x40341cba0e634b2cull }, + { 0x401152f1a9fbe76dull, 0x4034204bfe02a637ull }, + { 0x401153f7ced91687ull, 0x403423de8fdcf0afull }, + { 0x401154fdf3b645a2ull, 0x40342771c40ef6a6ull }, + { 0x40115604189374bdull, 0x40342b059ab5893eull }, + { 0x4011570a3d70a3d7ull, 0x40342e9a13ed7eb9ull }, + { 0x40115810624dd2f2ull, 0x4034322f2fd3b280ull }, + { 0x40115916872b020cull, 0x403435c4ee85050full }, + { 0x40115a1cac083127ull, 0x4034395b501e5c0dull }, + { 0x40115b22d0e56042ull, 0x40343cf254bca23cull }, + { 0x40115c28f5c28f5cull, 0x40344089fc7cc77cull }, + { 0x40115d2f1a9fbe77ull, 0x40344422477bc0deull }, + { 0x40115e353f7ced92ull, 0x403447bb35d6888aull }, + { 0x40115f3b645a1cacull, 0x40344b54c7aa1dcdull }, + { 0x4011604189374bc7ull, 0x40344eeefd138526ull }, + { 0x40116147ae147ae1ull, 0x40345289d62fc829ull }, + { 0x4011624dd2f1a9fcull, 0x40345625531bf5a4ull }, + { 0x40116353f7ced917ull, 0x403459c173f5217full }, + { 0x4011645a1cac0831ull, 0x40345d5e38d864cfull }, + { 0x401165604189374cull, 0x403460fba1e2dddaull }, + { 0x4011666666666667ull, 0x40346499af31b009ull }, + { 0x4011676c8b439581ull, 0x4034683860e203f1ull }, + { 0x40116872b020c49cull, 0x40346bd7b711075eull }, + { 0x40116978d4fdf3b6ull, 0x40346f77b1dbed3bull }, + { 0x40116a7ef9db22d1ull, 0x40347318515fedb3ull }, + { 0x40116b851eb851ecull, 0x403476b995ba4613ull }, + { 0x40116c8b43958106ull, 0x40347a5b7f0838daull }, + { 0x40116d916872b021ull, 0x40347dfe0d670dc3ull }, + { 0x40116e978d4fdf3bull, 0x403481a140f411adull }, + { 0x40116f9db22d0e56ull, 0x4034854519cc96baull }, + { 0x401170a3d70a3d71ull, 0x403488e9980df435ull }, + { 0x401171a9fbe76c8bull, 0x40348c8ebbd5869dull }, + { 0x401172b020c49ba6ull, 0x403490348540afb4ull }, + { 0x401173b645a1cac1ull, 0x403493daf46cd668ull }, + { 0x401174bc6a7ef9dbull, 0x40349782097766ddull }, + { 0x401175c28f5c28f6ull, 0x40349b29c47dd27eull }, + { 0x401176c8b4395810ull, 0x40349ed2259d8fdfull }, + { 0x401177ced916872bull, 0x4034a27b2cf41adfull }, + { 0x401178d4fdf3b646ull, 0x4034a624da9ef48cull }, + { 0x401179db22d0e560ull, 0x4034a9cf2ebba333ull }, + { 0x40117ae147ae147bull, 0x4034ad7a2967b268ull }, + { 0x40117be76c8b4396ull, 0x4034b125cac0b2f4ull }, + { 0x40117ced916872b0ull, 0x4034b4d212e43addull }, + { 0x40117df3b645a1cbull, 0x4034b87f01efe577ull }, + { 0x40117ef9db22d0e5ull, 0x4034bc2c98015348ull }, + { 0x4011800000000000ull, 0x4034bfdad5362a27ull }, + { 0x4011810624dd2f1bull, 0x4034c389b9ac1522ull }, + { 0x4011820c49ba5e35ull, 0x4034c7394580c48dull }, + { 0x401183126e978d50ull, 0x4034cae978d1ee0bull }, + { 0x401184189374bc6bull, 0x4034ce9a53bd4c7bull }, + { 0x4011851eb851eb85ull, 0x4034d24bd660a001ull }, + { 0x40118624dd2f1aa0ull, 0x4034d5fe00d9ae17ull }, + { 0x4011872b020c49baull, 0x4034d9b0d346416eull }, + { 0x4011883126e978d5ull, 0x4034dd644dc42a11ull }, + { 0x401189374bc6a7f0ull, 0x4034e11870713d4cull }, + { 0x40118a3d70a3d70aull, 0x4034e4cd3b6b55b4ull }, + { 0x40118b4395810625ull, 0x4034e882aed05338ull }, + { 0x40118c49ba5e3540ull, 0x4034ec38cabe1b07ull }, + { 0x40118d4fdf3b645aull, 0x4034efef8f5297a2ull }, + { 0x40118e5604189375ull, 0x4034f3a6fcabb8e1ull }, + { 0x40118f5c28f5c28full, 0x4034f75f12e773dfull }, + { 0x401190624dd2f1aaull, 0x4034fb17d223c319ull }, + { 0x4011916872b020c5ull, 0x4034fed13a7ea64full }, + { 0x4011926e978d4fdfull, 0x4035028b4c162299ull }, + { 0x40119374bc6a7efaull, 0x403506460708426aull }, + { 0x4011947ae147ae15ull, 0x40350a016b731582ull }, + { 0x401195810624dd2full, 0x40350dbd7974b0f6ull }, + { 0x401196872b020c4aull, 0x4035117a312b2f3cull }, + { 0x4011978d4fdf3b64ull, 0x4035153792b4b015ull }, + { 0x4011989374bc6a7full, 0x403518f59e2f58a9ull }, + { 0x401199999999999aull, 0x40351cb453b9536eull }, + { 0x40119a9fbe76c8b4ull, 0x40352073b370d036ull }, + { 0x40119ba5e353f7cfull, 0x40352433bd740438ull }, + { 0x40119cac083126eaull, 0x403527f471e12a00ull }, + { 0x40119db22d0e5604ull, 0x40352bb5d0d68174ull }, + { 0x40119eb851eb851full, 0x40352f77da724fe6ull }, + { 0x40119fbe76c8b439ull, 0x4035333a8ed2dff7ull }, + { 0x4011a0c49ba5e354ull, 0x403536fdee1681baull }, + { 0x4011a1cac083126full, 0x40353ac1f85b8a94ull }, + { 0x4011a2d0e5604189ull, 0x40353e86adc05552ull }, + { 0x4011a3d70a3d70a4ull, 0x4035424c0e63422aull }, + { 0x4011a4dd2f1a9fbfull, 0x403546121a62b6b0ull }, + { 0x4011a5e353f7ced9ull, 0x403549d8d1dd1dd9ull }, + { 0x4011a6e978d4fdf4ull, 0x40354da034f0e80full }, + { 0x4011a7ef9db22d0eull, 0x4035516843bc8b12ull }, + { 0x4011a8f5c28f5c29ull, 0x40355530fe5e821aull }, + { 0x4011a9fbe76c8b44ull, 0x403558fa64f54dbcull }, + { 0x4011ab020c49ba5eull, 0x40355cc4779f73f9ull }, + { 0x4011ac083126e979ull, 0x4035608f367b8047ull }, + { 0x4011ad0e56041894ull, 0x4035645aa1a8037cull }, + { 0x4011ae147ae147aeull, 0x40356826b94393ddull }, + { 0x4011af1a9fbe76c9ull, 0x40356bf37d6ccd26ull }, + { 0x4011b020c49ba5e3ull, 0x40356fc0ee425074ull }, + { 0x4011b126e978d4feull, 0x4035738f0be2c463ull }, + { 0x4011b22d0e560419ull, 0x4035775dd66cd4f5ull }, + { 0x4011b33333333333ull, 0x40357b2d4dff339cull }, + { 0x4011b4395810624eull, 0x40357efd72b8974aull }, + { 0x4011b53f7ced9169ull, 0x403582ce44b7bc58ull }, + { 0x4011b645a1cac083ull, 0x4035869fc41b6494ull }, + { 0x4011b74bc6a7ef9eull, 0x40358a71f102574full }, + { 0x4011b851eb851eb8ull, 0x40358e44cb8b613eull }, + { 0x4011b95810624dd3ull, 0x4035921853d5549full }, + { 0x4011ba5e353f7ceeull, 0x403595ec89ff091dull }, + { 0x4011bb645a1cac08ull, 0x403599c16e275bdcull }, + { 0x4011bc6a7ef9db23ull, 0x40359d97006d2f87ull }, + { 0x4011bd70a3d70a3eull, 0x4035a16d40ef6c36ull }, + { 0x4011be76c8b43958ull, 0x4035a5442fccff82ull }, + { 0x4011bf7ced916873ull, 0x4035a91bcd24dc8aull }, + { 0x4011c083126e978dull, 0x4035acf41915fbddull }, + { 0x4011c189374bc6a8ull, 0x4035b0cd13bf5b97ull }, + { 0x4011c28f5c28f5c3ull, 0x4035b4a6bd3fff4bull }, + { 0x4011c395810624ddull, 0x4035b88115b6f00dull }, + { 0x4011c49ba5e353f8ull, 0x4035bc5c1d433c7eull }, + { 0x4011c5a1cac08313ull, 0x4035c037d403f8b7ull }, + { 0x4011c6a7ef9db22dull, 0x4035c4143a183e57ull }, + { 0x4011c7ae147ae148ull, 0x4035c7f14f9f2c89ull }, + { 0x4011c8b439581062ull, 0x4035cbcf14b7e7f5ull }, + { 0x4011c9ba5e353f7dull, 0x4035cfad89819ad6ull }, + { 0x4011cac083126e98ull, 0x4035d38cae1b74e4ull }, + { 0x4011cbc6a7ef9db2ull, 0x4035d76c82a4ab60ull }, + { 0x4011cccccccccccdull, 0x4035db4d073c7921ull }, + { 0x4011cdd2f1a9fbe8ull, 0x4035df2e3c021e7full }, + { 0x4011ced916872b02ull, 0x4035e3102114e15cull }, + { 0x4011cfdf3b645a1dull, 0x4035e6f2b6940d34ull }, + { 0x4011d0e560418937ull, 0x4035ead5fc9ef301ull }, + { 0x4011d1eb851eb852ull, 0x4035eeb9f354e95cull }, + { 0x4011d2f1a9fbe76dull, 0x4035f29e9ad54c61ull }, + { 0x4011d3f7ced91687ull, 0x4035f683f33f7dc0ull }, + { 0x4011d4fdf3b645a2ull, 0x4035fa69fcb2e4c4ull }, + { 0x4011d604189374bdull, 0x4035fe50b74eee40ull }, + { 0x4011d70a3d70a3d7ull, 0x4036023823330c9cull }, + { 0x4011d810624dd2f2ull, 0x40360620407eb7e0ull }, + { 0x4011d916872b020cull, 0x40360a090f516d9aull }, + { 0x4011da1cac083127ull, 0x40360df28fcab0ffull }, + { 0x4011db22d0e56042ull, 0x403611dcc20a0ad0ull }, + { 0x4011dc28f5c28f5cull, 0x403615c7a62f0968ull }, + { 0x4011dd2f1a9fbe77ull, 0x403619b33c5940c6ull }, + { 0x4011de353f7ced92ull, 0x40361d9f84a84a7aull }, + { 0x4011df3b645a1cacull, 0x4036218c7f3bc5afull }, + { 0x4011e04189374bc7ull, 0x4036257a2c335739ull }, + { 0x4011e147ae147ae1ull, 0x403629688baea979ull }, + { 0x4011e24dd2f1a9fcull, 0x40362d579dcd6c81ull }, + { 0x4011e353f7ced917ull, 0x4036314762af55f6ull }, + { 0x4011e45a1cac0831ull, 0x40363537da74211dull }, + { 0x4011e5604189374cull, 0x40363929053b8ee9ull }, + { 0x4011e66666666667ull, 0x40363d1ae32565e6ull }, + { 0x4011e76c8b439581ull, 0x4036410d74517243ull }, + { 0x4011e872b020c49cull, 0x40364500b8df85deull }, + { 0x4011e978d4fdf3b6ull, 0x403648f4b0ef782eull }, + { 0x4011ea7ef9db22d1ull, 0x40364ce95ca1265eull }, + { 0x4011eb851eb851ecull, 0x403650debc147337ull }, + { 0x4011ec8b43958106ull, 0x403654d4cf69472bull }, + { 0x4011ed916872b021ull, 0x403658cb96bf9060ull }, + { 0x4011ee978d4fdf3bull, 0x40365cc31237429aull }, + { 0x4011ef9db22d0e56ull, 0x403660bb41f05756ull }, + { 0x4011f0a3d70a3d71ull, 0x403664b4260acdb3ull }, + { 0x4011f1a9fbe76c8bull, 0x403668adbea6aa7full }, + { 0x4011f2b020c49ba6ull, 0x40366ca80be3f842ull }, + { 0x4011f3b645a1cac1ull, 0x403670a30de2c727ull }, + { 0x4011f4bc6a7ef9dbull, 0x4036749ec4c32d0eull }, + { 0x4011f5c28f5c28f6ull, 0x4036789b30a54590ull }, + { 0x4011f6c8b4395810ull, 0x40367c9851a931ebull }, + { 0x4011f7ced916872bull, 0x4036809627ef1923ull }, + { 0x4011f8d4fdf3b646ull, 0x40368494b39727e3ull }, + { 0x4011f9db22d0e560ull, 0x40368893f4c1908bull }, + { 0x4011fae147ae147bull, 0x40368c93eb8e8b41ull }, + { 0x4011fbe76c8b4396ull, 0x40369094981e55d5ull }, + { 0x4011fced916872b0ull, 0x40369495fa9133d1ull }, + { 0x4011fdf3b645a1cbull, 0x4036989813076e85ull }, + { 0x4011fef9db22d0e5ull, 0x40369c9ae1a154ecull }, + { 0x4012000000000000ull, 0x4036a09e667f3bcdull }, + { 0x4012010624dd2f1bull, 0x4036a4a2a1c17da0ull }, + { 0x4012020c49ba5e35ull, 0x4036a8a793887a9cull }, + { 0x401203126e978d50ull, 0x4036acad3bf498c2ull }, + { 0x401204189374bc6bull, 0x4036b0b39b2643caull }, + { 0x4012051eb851eb85ull, 0x4036b4bab13ded2aull }, + { 0x40120624dd2f1aa0ull, 0x4036b8c27e5c0c27ull }, + { 0x4012072b020c49baull, 0x4036bccb02a11dbbull }, + { 0x4012083126e978d5ull, 0x4036c0d43e2da4b2ull }, + { 0x401209374bc6a7f0ull, 0x4036c4de31222994ull }, + { 0x40120a3d70a3d70aull, 0x4036c8e8db9f3aadull }, + { 0x40120b4395810625ull, 0x4036ccf43dc56c1eull }, + { 0x40120c49ba5e3540ull, 0x4036d10057b557c3ull }, + { 0x40120d4fdf3b645aull, 0x4036d50d298f9d43ull }, + { 0x40120e5604189375ull, 0x4036d91ab374e219ull }, + { 0x40120f5c28f5c28full, 0x4036dd28f585d180ull }, + { 0x401210624dd2f1aaull, 0x4036e137efe31c8aull }, + { 0x4012116872b020c5ull, 0x4036e547a2ad7a0full }, + { 0x4012126e978d4fdfull, 0x4036e9580e05a6b4ull }, + { 0x40121374bc6a7efaull, 0x4036ed69320c64f8ull }, + { 0x4012147ae147ae15ull, 0x4036f17b0ee27d21ull }, + { 0x401215810624dd2full, 0x4036f58da4a8bd47ull }, + { 0x401216872b020c4aull, 0x4036f9a0f37ff95eull }, + { 0x4012178d4fdf3b64ull, 0x4036fdb4fb890b21ull }, + { 0x4012189374bc6a7full, 0x403701c9bce4d22cull }, + { 0x401219999999999aull, 0x403705df37b433e8ull }, + { 0x40121a9fbe76c8b4ull, 0x403709f56c181b95ull }, + { 0x40121ba5e353f7cfull, 0x40370e0c5a317a54ull }, + { 0x40121cac083126eaull, 0x4037122402214716ull }, + { 0x40121db22d0e5604ull, 0x4037163c64087ea4ull }, + { 0x40121eb851eb851full, 0x40371a55800823afull }, + { 0x40121fbe76c8b439ull, 0x40371e6f56413eb3ull }, + { 0x401220c49ba5e354ull, 0x40372289e6d4de1aull }, + { 0x401221cac083126full, 0x403726a531e41621ull }, + { 0x401222d0e5604189ull, 0x40372ac1379000e2ull }, + { 0x401223d70a3d70a4ull, 0x40372eddf7f9be65ull }, + { 0x401224dd2f1a9fbfull, 0x403732fb73427486ull }, + { 0x401225e353f7ced9ull, 0x40373719a98b4f04ull }, + { 0x401226e978d4fdf4ull, 0x40373b389af57f8eull }, + { 0x401227ef9db22d0eull, 0x40373f5847a23da6ull }, + { 0x401228f5c28f5c29ull, 0x40374378afb2c6c5ull }, + { 0x401229fbe76c8b44ull, 0x40374799d3485e3cull }, + { 0x40122b020c49ba5eull, 0x40374bbbb2844d46ull }, + { 0x40122c083126e979ull, 0x40374fde4d87e312ull }, + { 0x40122d0e56041894ull, 0x40375401a47474acull }, + { 0x40122e147ae147aeull, 0x40375825b76b5d0aull }, + { 0x40122f1a9fbe76c9ull, 0x40375c4a868dfd1dull }, + { 0x40123020c49ba5e3ull, 0x4037607011fdbbaeull }, + { 0x40123126e978d4feull, 0x4037649659dc0588ull }, + { 0x4012322d0e560419ull, 0x403768bd5e4a4d57ull }, + { 0x4012333333333333ull, 0x40376ce51f6a0bb7ull }, + { 0x401234395810624eull, 0x4037710d9d5cbf41ull }, + { 0x4012353f7ced9169ull, 0x40377536d843ec75ull }, + { 0x40123645a1cac083ull, 0x40377960d0411dc4ull }, + { 0x4012374bc6a7ef9eull, 0x40377d8b8575e3a1ull }, + { 0x40123851eb851eb8ull, 0x403781b6f803d464ull }, + { 0x4012395810624dd3ull, 0x403785e3280c8c6bull }, + { 0x40123a5e353f7ceeull, 0x40378a1015b1adffull }, + { 0x40123b645a1cac08ull, 0x40378e3dc114e162ull }, + { 0x40123c6a7ef9db23ull, 0x4037926c2a57d4daull }, + { 0x40123d70a3d70a3eull, 0x4037969b519c3c9full }, + { 0x40123e76c8b43958ull, 0x40379acb3703d2dfull }, + { 0x40123f7ced916873ull, 0x40379efbdab057d7ull }, + { 0x40124083126e978dull, 0x4037a32d3cc391adull }, + { 0x40124189374bc6a8ull, 0x4037a75f5d5f4c97ull }, + { 0x4012428f5c28f5c3ull, 0x4037ab923ca55ac0ull }, + { 0x40124395810624ddull, 0x4037afc5dab79453ull }, + { 0x4012449ba5e353f8ull, 0x4037b3fa37b7d788ull }, + { 0x401245a1cac08313ull, 0x4037b82f53c80890ull }, + { 0x401246a7ef9db22dull, 0x4037bc652f0a119full }, + { 0x401247ae147ae148ull, 0x4037c09bc99fe2f9ull }, + { 0x401248b439581062ull, 0x4037c4d323ab72dbull }, + { 0x401249ba5e353f7dull, 0x4037c90b3d4ebd97ull }, + { 0x40124ac083126e98ull, 0x4037cd4416abc57dull }, + { 0x40124bc6a7ef9db2ull, 0x4037d17dafe492e4ull }, + { 0x40124ccccccccccdull, 0x4037d5b8091b343dull }, + { 0x40124dd2f1a9fbe8ull, 0x4037d9f32271bdf5ull }, + { 0x40124ed916872b02ull, 0x4037de2efc0a4a87ull }, + { 0x40124fdf3b645a1dull, 0x4037e26b9606fa8aull }, + { 0x401250e560418937ull, 0x4037e6a8f089f491ull }, + { 0x401251eb851eb852ull, 0x4037eae70bb5654full }, + { 0x401252f1a9fbe76dull, 0x4037ef25e7ab7f7dull }, + { 0x401253f7ced91687ull, 0x4037f365848e7be6ull }, + { 0x401254fdf3b645a2ull, 0x4037f7a5e2809974ull }, + { 0x40125604189374bdull, 0x4037fbe701a41d18ull }, + { 0x4012570a3d70a3d7ull, 0x40380028e21b51daull }, + { 0x40125810624dd2f2ull, 0x4038046b840888e3ull }, + { 0x40125916872b020cull, 0x403808aee78e1965ull }, + { 0x40125a1cac083127ull, 0x40380cf30cce60b9ull }, + { 0x40125b22d0e56042ull, 0x40381137f3ebc245ull }, + { 0x40125c28f5c28f5cull, 0x4038157d9d08a78dull }, + { 0x40125d2f1a9fbe77ull, 0x403819c408478039ull }, + { 0x40125e353f7ced92ull, 0x40381e0b35cac203ull }, + { 0x40125f3b645a1cacull, 0x4038225325b4e8c5ull }, + { 0x4012604189374bc7ull, 0x4038269bd8287680ull }, + { 0x40126147ae147ae1ull, 0x40382ae54d47f349ull }, + { 0x4012624dd2f1a9fcull, 0x40382f2f8535ed65ull }, + { 0x40126353f7ced917ull, 0x4038337a8014f92eull }, + { 0x4012645a1cac0831ull, 0x403837c63e07b121ull }, + { 0x401265604189374cull, 0x40383c12bf30b5efull }, + { 0x4012666666666667ull, 0x4038406003b2ae5full }, + { 0x4012676c8b439581ull, 0x403844ae0bb0475full }, + { 0x40126872b020c49cull, 0x403848fcd74c3414ull }, + { 0x40126978d4fdf3b6ull, 0x40384d4c66a92db6ull }, + { 0x40126a7ef9db22d1ull, 0x4038519cb9e9f3bdull }, + { 0x40126b851eb851ecull, 0x403855edd1314bbdull }, + { 0x40126c8b43958106ull, 0x40385a3faca20175ull }, + { 0x40126d916872b021ull, 0x40385e924c5ee6dfull }, + { 0x40126e978d4fdf3cull, 0x403862e5b08ad417ull }, + { 0x40126f9db22d0e56ull, 0x40386739d948a769ull }, + { 0x401270a3d70a3d71ull, 0x40386b8ec6bb455cull }, + { 0x401271a9fbe76c8bull, 0x40386fe479059898ull }, + { 0x401272b020c49ba6ull, 0x4038743af04a920cull }, + { 0x401273b645a1cac1ull, 0x403878922cad28c9ull }, + { 0x401274bc6a7ef9dbull, 0x40387cea2e505a19ull }, + { 0x401275c28f5c28f6ull, 0x40388142f5572987ull }, + { 0x401276c8b4395810ull, 0x4038859c81e4a0c3ull }, + { 0x401277ced916872bull, 0x403889f6d41bcfc9ull }, + { 0x401278d4fdf3b646ull, 0x40388e51ec1fccbfull }, + { 0x401279db22d0e560ull, 0x403892adca13b406ull }, + { 0x40127ae147ae147bull, 0x4038970a6e1aa848ull }, + { 0x40127be76c8b4396ull, 0x40389b67d857d25full }, + { 0x40127ced916872b0ull, 0x40389fc608ee6162ull }, + { 0x40127df3b645a1cbull, 0x4038a42500018ab4ull }, + { 0x40127ef9db22d0e5ull, 0x4038a884bdb489e6ull }, + { 0x4012800000000000ull, 0x4038ace5422aa0dbull }, + { 0x4012810624dd2f1bull, 0x4038b1468d8717aeull }, + { 0x4012820c49ba5e35ull, 0x4038b5a89fed3cbbull }, + { 0x401283126e978d50ull, 0x4038ba0b798064aeull }, + { 0x401284189374bc6bull, 0x4038be6f1a63ea6full }, + { 0x4012851eb851eb85ull, 0x4038c2d382bb2f2bull }, + { 0x40128624dd2f1aa0ull, 0x4038c738b2a99a62ull }, + { 0x4012872b020c49baull, 0x4038cb9eaa5299cdull }, + { 0x4012883126e978d5ull, 0x4038d00569d9a182ull }, + { 0x401289374bc6a7f0ull, 0x4038d46cf1622bd2ull }, + { 0x40128a3d70a3d70aull, 0x4038d8d5410fb95cull }, + { 0x40128b4395810625ull, 0x4038dd3e5905d119ull }, + { 0x40128c49ba5e3540ull, 0x4038e1a839680043ull }, + { 0x40128d4fdf3b645aull, 0x4038e612e259da63ull }, + { 0x40128e5604189375ull, 0x4038ea7e53fef962ull }, + { 0x40128f5c28f5c28full, 0x4038eeea8e7afd67ull }, + { 0x401290624dd2f1aaull, 0x4038f35791f18cfeull }, + { 0x4012916872b020c5ull, 0x4038f7c55e8654fbull }, + { 0x4012926e978d4fdfull, 0x4038fc33f45d0885ull }, + { 0x40129374bc6a7efaull, 0x403900a353996129ull }, + { 0x4012947ae147ae15ull, 0x403905137c5f1ebcull }, + { 0x401295810624dd2full, 0x403909846ed2076cull }, + { 0x401296872b020c4aull, 0x40390df62b15e7cfull }, + { 0x4012978d4fdf3b64ull, 0x40391268b14e92c4ull }, + { 0x4012989374bc6a7full, 0x403916dc019fe196ull }, + { 0x401299999999999aull, 0x40391b501c2db3e0ull }, + { 0x40129a9fbe76c8b4ull, 0x40391fc5011bef9eull }, + { 0x40129ba5e353f7cfull, 0x4039243ab08e8135ull }, + { 0x40129cac083126eaull, 0x403928b12aa95b5full }, + { 0x40129db22d0e5604ull, 0x40392d286f907737ull }, + { 0x40129eb851eb851full, 0x403931a07f67d448ull }, + { 0x40129fbe76c8b439ull, 0x403936195a537870ull }, + { 0x4012a0c49ba5e354ull, 0x40393a9300777002ull }, + { 0x4012a1cac083126full, 0x40393f0d71f7cdabull }, + { 0x4012a2d0e5604189ull, 0x40394388aef8aa7full }, + { 0x4012a3d70a3d70a4ull, 0x40394804b79e2607ull }, + { 0x4012a4dd2f1a9fbfull, 0x40394c818c0c662aull }, + { 0x4012a5e353f7ced9ull, 0x403950ff2c679737ull }, + { 0x4012a6e978d4fdf4ull, 0x4039557d98d3ebf9ull }, + { 0x4012a7ef9db22d0eull, 0x403959fcd1759d93ull }, + { 0x4012a8f5c28f5c29ull, 0x40395e7cd670ebabull }, + { 0x4012a9fbe76c8b44ull, 0x403962fda7ea1c47ull }, + { 0x4012ab020c49ba5eull, 0x4039677f46057bddull }, + { 0x4012ac083126e979ull, 0x40396c01b0e75d62ull }, + { 0x4012ad0e56041894ull, 0x40397084e8b41a33ull }, + { 0x4012ae147ae147aeull, 0x40397508ed90121cull }, + { 0x4012af1a9fbe76c9ull, 0x4039798dbf9fab70ull }, + { 0x4012b020c49ba5e3ull, 0x40397e135f0752e3ull }, + { 0x4012b126e978d4feull, 0x40398299cbeb7bb3ull }, + { 0x4012b22d0e560419ull, 0x4039872106709f89ull }, + { 0x4012b33333333333ull, 0x40398ba90ebb3e89ull }, + { 0x4012b4395810624eull, 0x40399031e4efdf5bull }, + { 0x4012b53f7ced9169ull, 0x403994bb89330f1bull }, + { 0x4012b645a1cac083ull, 0x40399945fba9615dull }, + { 0x4012b74bc6a7ef9eull, 0x40399dd13c777043ull }, + { 0x4012b851eb851eb8ull, 0x4039a25d4bc1dc5cull }, + { 0x4012b95810624dd3ull, 0x4039a6ea29ad4ccaull }, + { 0x4012ba5e353f7ceeull, 0x4039ab77d65e6f21ull }, + { 0x4012bb645a1cac08ull, 0x4039b00651f9f779ull }, + { 0x4012bc6a7ef9db23ull, 0x4039b4959ca4a07dull }, + { 0x4012bd70a3d70a3eull, 0x4039b925b6832b4cull }, + { 0x4012be76c8b43958ull, 0x4039bdb69fba5f8full }, + { 0x4012bf7ced916873ull, 0x4039c248586f0b80ull }, + { 0x4012c083126e978dull, 0x4039c6dae0c603d4ull }, + { 0x4012c189374bc6a8ull, 0x4039cb6e38e423d7ull }, + { 0x4012c28f5c28f5c3ull, 0x4039d00260ee4d55ull }, + { 0x4012c395810624ddull, 0x4039d497590968a6ull }, + { 0x4012c49ba5e353f8ull, 0x4039d92d215a64bbull }, + { 0x4012c5a1cac08313ull, 0x4039ddc3ba063709ull }, + { 0x4012c6a7ef9db22dull, 0x4039e25b2331db91ull }, + { 0x4012c7ae147ae148ull, 0x4039e6f35d0254f3ull }, + { 0x4012c8b439581062ull, 0x4039eb8c679cac51ull }, + { 0x4012c9ba5e353f7dull, 0x4039f0264325f16full }, + { 0x4012cac083126e98ull, 0x4039f4c0efc33a9aull }, + { 0x4012cbc6a7ef9db2ull, 0x4039f95c6d99a4b3ull }, + { 0x4012cccccccccccdull, 0x4039fdf8bcce533eull }, + { 0x4012cdd2f1a9fbe8ull, 0x403a0295dd86704dull }, + { 0x4012ced916872b02ull, 0x403a0733cfe72c86ull }, + { 0x4012cfdf3b645a1dull, 0x403a0bd29415bf39ull }, + { 0x4012d0e560418937ull, 0x403a10722a376640ull }, + { 0x4012d1eb851eb852ull, 0x403a151292716622ull }, + { 0x4012d2f1a9fbe76dull, 0x403a19b3cce909f5ull }, + { 0x4012d3f7ced91687ull, 0x403a1e55d9c3a370ull }, + { 0x4012d4fdf3b645a2ull, 0x403a22f8b9268af6ull }, + { 0x4012d604189374bdull, 0x403a279c6b371f7eull }, + { 0x4012d70a3d70a3d7ull, 0x403a2c40f01ac6a1ull }, + { 0x4012d810624dd2f2ull, 0x403a30e647f6eca9ull }, + { 0x4012d916872b020cull, 0x403a358c72f10472ull }, + { 0x4012da1cac083127ull, 0x403a3a33712e8790ull }, + { 0x4012db22d0e56042ull, 0x403a3edb42d4f631ull }, + { 0x4012dc28f5c28f5cull, 0x403a4383e809d72bull }, + { 0x4012dd2f1a9fbe77ull, 0x403a482d60f2b80aull }, + { 0x4012de353f7ced92ull, 0x403a4cd7adb52cf8ull }, + { 0x4012df3b645a1cacull, 0x403a5182ce76d0caull }, + { 0x4012e04189374bc7ull, 0x403a562ec35d450full }, + { 0x4012e147ae147ae1ull, 0x403a5adb8c8e31f3ull }, + { 0x4012e24dd2f1a9fcull, 0x403a5f892a2f4662ull }, + { 0x4012e353f7ced917ull, 0x403a64379c6637edull }, + { 0x4012e45a1cac0831ull, 0x403a68e6e358c2d4ull }, + { 0x4012e5604189374cull, 0x403a6d96ff2caa18ull }, + { 0x4012e66666666667ull, 0x403a7247f007b762ull }, + { 0x4012e76c8b439581ull, 0x403a76f9b60fbb0full }, + { 0x4012e872b020c49cull, 0x403a7bac516a8c3full }, + { 0x4012e978d4fdf3b6ull, 0x403a805fc23e08b9ull }, + { 0x4012ea7ef9db22d1ull, 0x403a851408b0150eull }, + { 0x4012eb851eb851ecull, 0x403a89c924e69c7bull }, + { 0x4012ec8b43958106ull, 0x403a8e7f170790faull }, + { 0x4012ed916872b021ull, 0x403a9335df38eb4cull }, + { 0x4012ee978d4fdf3cull, 0x403a97ed7da0aae5ull }, + { 0x4012ef9db22d0e56ull, 0x403a9ca5f264d5f4ull }, + { 0x4012f0a3d70a3d71ull, 0x403aa15f3dab797aull }, + { 0x4012f1a9fbe76c8bull, 0x403aa6195f9aa922ull }, + { 0x4012f2b020c49ba6ull, 0x403aaad458587f70ull }, + { 0x4012f3b645a1cac1ull, 0x403aaf90280b1d9cull }, + { 0x4012f4bc6a7ef9dbull, 0x403ab44cced8aba0ull }, + { 0x4012f5c28f5c28f6ull, 0x403ab90a4ce7584full }, + { 0x4012f6c8b4395810ull, 0x403abdc8a25d592dull }, + { 0x4012f7ced916872bull, 0x403ac287cf60ea98ull }, + { 0x4012f8d4fdf3b646ull, 0x403ac747d4184faeull }, + { 0x4012f9db22d0e560ull, 0x403acc08b0a9d252ull }, + { 0x4012fae147ae147bull, 0x403ad0ca653bc346ull }, + { 0x4012fbe76c8b4396ull, 0x403ad58cf1f47a09ull }, + { 0x4012fced916872b0ull, 0x403ada5056fa54e6ull }, + { 0x4012fdf3b645a1cbull, 0x403adf149473b90aull }, + { 0x4012fef9db22d0e5ull, 0x403ae3d9aa87125eull }, + { 0x4013000000000000ull, 0x403ae89f995ad3adull }, + { 0x4013010624dd2f1bull, 0x403aed666115768cull }, + { 0x4013020c49ba5e35ull, 0x403af22e01dd7b62ull }, + { 0x401303126e978d50ull, 0x403af6f67bd96978ull }, + { 0x401304189374bc6bull, 0x403afbbfcf2fcee3ull }, + { 0x4013051eb851eb85ull, 0x403b0089fc07408dull }, + { 0x40130624dd2f1aa0ull, 0x403b055502865a4aull }, + { 0x4013072b020c49baull, 0x403b0a20e2d3beb3ull }, + { 0x4013083126e978d5ull, 0x403b0eed9d161753ull }, + { 0x401309374bc6a7f0ull, 0x403b13bb3174147full }, + { 0x40130a3d70a3d70aull, 0x403b1889a0146d6eull }, + { 0x40130b4395810625ull, 0x403b1d58e91de043ull }, + { 0x40130c49ba5e3540ull, 0x403b22290cb731f4ull }, + { 0x40130d4fdf3b645aull, 0x403b26fa0b072e57ull }, + { 0x40130e5604189375ull, 0x403b2bcbe434a836ull }, + { 0x40130f5c28f5c28full, 0x403b309e98667929ull }, + { 0x401310624dd2f1aaull, 0x403b357227c381c4ull }, + { 0x4013116872b020c5ull, 0x403b3a469272a96full }, + { 0x4013126e978d4fdfull, 0x403b3f1bd89ade7full }, + { 0x40131374bc6a7efaull, 0x403b43f1fa63163cull }, + { 0x4013147ae147ae15ull, 0x403b48c8f7f24ccdull }, + { 0x401315810624dd2full, 0x403b4da0d16f8542ull }, + { 0x401316872b020c4aull, 0x403b52798701c9a8ull }, + { 0x4013178d4fdf3b64ull, 0x403b575318d02ae6ull }, + { 0x4013189374bc6a7full, 0x403b5c2d8701c0e6ull }, + { 0x401319999999999aull, 0x403b6108d1bdaa74ull }, + { 0x40131a9fbe76c8b4ull, 0x403b65e4f92b0d4eull }, + { 0x40131ba5e353f7cfull, 0x403b6ac1fd711631ull }, + { 0x40131cac083126eaull, 0x403b6f9fdeb6f8c3ull }, + { 0x40131db22d0e5604ull, 0x403b747e9d23ef9dull }, + { 0x40131eb851eb851full, 0x403b795e38df3c60ull }, + { 0x40131fbe76c8b439ull, 0x403b7e3eb210278dull }, + { 0x401320c49ba5e354ull, 0x403b832008de00b6ull }, + { 0x401321cac083126full, 0x403b88023d701e56ull }, + { 0x401322d0e5604189ull, 0x403b8ce54feddde5ull }, + { 0x401323d70a3d70a4ull, 0x403b91c9407ea3e5ull }, + { 0x401324dd2f1a9fbfull, 0x403b96ae0f49dbcaull }, + { 0x401325e353f7ced9ull, 0x403b9b93bc76f804ull }, + { 0x401326e978d4fdf4ull, 0x403ba07a482d7214ull }, + { 0x401327ef9db22d0eull, 0x403ba561b294ca6bull }, + { 0x401328f5c28f5c29ull, 0x403baa49fbd4888cull }, + { 0x401329fbe76c8b44ull, 0x403baf3324143af4ull }, + { 0x40132b020c49ba5eull, 0x403bb41d2b7b7724ull }, + { 0x40132c083126e979ull, 0x403bb9081231d9b1ull }, + { 0x40132d0e56041894ull, 0x403bbdf3d85f062cull }, + { 0x40132e147ae147aeull, 0x403bc2e07e2aa72dull }, + { 0x40132f1a9fbe76c9ull, 0x403bc7ce03bc6e66ull }, + { 0x40133020c49ba5e3ull, 0x403bccbc693c1482ull }, + { 0x40133126e978d4feull, 0x403bd1abaed1594bull }, + { 0x4013322d0e560419ull, 0x403bd69bd4a4038cull }, + { 0x4013333333333333ull, 0x403bdb8cdadbe11full }, + { 0x401334395810624eull, 0x403be07ec1a0c6feull }, + { 0x4013353f7ced9169ull, 0x403be571891a9124ull }, + { 0x40133645a1cac083ull, 0x403bea65317122a3ull }, + { 0x4013374bc6a7ef9eull, 0x403bef59bacc65aeull }, + { 0x40133851eb851eb8ull, 0x403bf44f25544b79ull }, + { 0x4013395810624dd3ull, 0x403bf9457130cc66ull }, + { 0x40133a5e353f7ceeull, 0x403bfe3c9e89e7deull }, + { 0x40133b645a1cac08ull, 0x403c0334ad87a464ull }, + { 0x40133c6a7ef9db23ull, 0x403c082d9e520fa5ull }, + { 0x40133d70a3d70a3eull, 0x403c0d2771113e5cull }, + { 0x40133e76c8b43958ull, 0x403c122225ed4c60ull }, + { 0x40133f7ced916873ull, 0x403c171dbd0e5cb5ull }, + { 0x40134083126e978dull, 0x403c1c1a369c996full }, + { 0x40134189374bc6a8ull, 0x403c211792c033d1ull }, + { 0x4013428f5c28f5c3ull, 0x403c2615d1a16437ull }, + { 0x40134395810624ddull, 0x403c2b14f3686a1dull }, + { 0x4013449ba5e353f8ull, 0x403c3014f83d8c34ull }, + { 0x401345a1cac08313ull, 0x403c3515e0491845ull }, + { 0x401346a7ef9db22dull, 0x403c3a17abb36340ull }, + { 0x401347ae147ae148ull, 0x403c3f1a5aa4c94dull }, + { 0x401348b439581062ull, 0x403c441ded45ada9ull }, + { 0x401349ba5e353f7dull, 0x403c492263be7ad0ull }, + { 0x40134ac083126e98ull, 0x403c4e27be37a25full }, + { 0x40134bc6a7ef9db2ull, 0x403c532dfcd99d1dull }, + { 0x40134ccccccccccdull, 0x403c58351fcceb11ull }, + { 0x40134dd2f1a9fbe8ull, 0x403c5d3d273a1362ull }, + { 0x40134ed916872b02ull, 0x403c62461349a46aull }, + { 0x40134fdf3b645a1dull, 0x403c674fe42433c6ull }, + { 0x401350e560418937ull, 0x403c6c5a99f25e2full }, + { 0x401351eb851eb852ull, 0x403c716634dcc7adull }, + { 0x401352f1a9fbe76dull, 0x403c7672b50c1b6bull }, + { 0x401353f7ced91687ull, 0x403c7b801aa90bcfull }, + { 0x401354fdf3b645a2ull, 0x403c808e65dc5286ull }, + { 0x40135604189374bdull, 0x403c859d96ceb06aull }, + { 0x4013570a3d70a3d7ull, 0x403c8aadada8ed8full }, + { 0x40135810624dd2f2ull, 0x403c8fbeaa93d957ull }, + { 0x40135916872b020cull, 0x403c94d08db84a4dull }, + { 0x40135a1cac083127ull, 0x403c99e3573f1e50ull }, + { 0x40135b22d0e56042ull, 0x403c9ef707513a73ull }, + { 0x40135c28f5c28f5cull, 0x403ca40b9e178b08ull }, + { 0x40135d2f1a9fbe77ull, 0x403ca9211bbb03b7ull }, + { 0x40135e353f7ced92ull, 0x403cae3780649f5bull }, + { 0x40135f3b645a1cacull, 0x403cb34ecc3d6017ull }, + { 0x4013604189374bc7ull, 0x403cb866ff6e4f65ull }, + { 0x40136147ae147ae1ull, 0x403cbd801a207df2ull }, + { 0x4013624dd2f1a9fcull, 0x403cc29a1c7d03caull }, + { 0x40136353f7ced917ull, 0x403cc7b506ad0035ull }, + { 0x4013645a1cac0831ull, 0x403cccd0d8d999c8ull }, + { 0x401365604189374cull, 0x403cd1ed932bfe76ull }, + { 0x4013666666666667ull, 0x403cd70b35cd636full }, + { 0x4013676c8b439581ull, 0x403cdc29c0e70539ull }, + { 0x40136872b020c49cull, 0x403ce14934a227b4ull }, + { 0x40136978d4fdf3b6ull, 0x403ce66991281606ull }, + { 0x40136a7ef9db22d1ull, 0x403ceb8ad6a222b8ull }, + { 0x40136b851eb851ecull, 0x403cf0ad0539a79dull }, + { 0x40136c8b43958106ull, 0x403cf5d01d1805deull }, + { 0x40136d916872b021ull, 0x403cfaf41e66a60cull }, + { 0x40136e978d4fdf3cull, 0x403d0019094ef800ull }, + { 0x40136f9db22d0e56ull, 0x403d053eddfa72f3ull }, + { 0x401370a3d70a3d71ull, 0x403d0a659c929584ull }, + { 0x401371a9fbe76c8bull, 0x403d0f8d4540e59full }, + { 0x401372b020c49ba6ull, 0x403d14b5d82ef0a1ull }, + { 0x401373b645a1cac1ull, 0x403d19df55864b3aull }, + { 0x401374bc6a7ef9dbull, 0x403d1f09bd70917bull }, + { 0x401375c28f5c28f6ull, 0x403d2435101766e5ull }, + { 0x401376c8b4395810ull, 0x403d29614da4764bull }, + { 0x401377ced916872bull, 0x403d2e8e764171f9ull }, + { 0x401378d4fdf3b646ull, 0x403d33bc8a181393ull }, + { 0x401379db22d0e560ull, 0x403d38eb89521c24ull }, + { 0x40137ae147ae147bull, 0x403d3e1b74195431ull }, + { 0x40137be76c8b4396ull, 0x403d434c4a978b9aull }, + { 0x40137ced916872b0ull, 0x403d487e0cf699aaull }, + { 0x40137df3b645a1cbull, 0x403d4db0bb605d29ull }, + { 0x40137ef9db22d0e5ull, 0x403d52e455febc3cull }, + { 0x4013800000000000ull, 0x403d5818dcfba487ull }, + { 0x4013810624dd2f1bull, 0x403d5d4e50810b15ull }, + { 0x4013820c49ba5e35ull, 0x403d6284b0b8ec62ull }, + { 0x401383126e978d50ull, 0x403d67bbfdcd4c6bull }, + { 0x401384189374bc6bull, 0x403d6cf437e83696ull }, + { 0x4013851eb851eb85ull, 0x403d722d5f33bdbfull }, + { 0x40138624dd2f1aa0ull, 0x403d776773d9fc47ull }, + { 0x4013872b020c49baull, 0x403d7ca2760513f7ull }, + { 0x4013883126e978d5ull, 0x403d81de65df2e25ull }, + { 0x401389374bc6a7f0ull, 0x403d871b43927b93ull }, + { 0x40138a3d70a3d70aull, 0x403d8c590f493483ull }, + { 0x40138b4395810625ull, 0x403d9197c92d98c2ull }, + { 0x40138c49ba5e3540ull, 0x403d96d77169ef8full }, + { 0x40138d4fdf3b645aull, 0x403d9c18082887aaull }, + { 0x40138e5604189375ull, 0x403da1598d93b764ull }, + { 0x40138f5c28f5c28full, 0x403da69c01d5dc7full }, + { 0x401390624dd2f1aaull, 0x403dabdf65195c55ull }, + { 0x4013916872b020c5ull, 0x403db123b788a3b9ull }, + { 0x4013926e978d4fdfull, 0x403db668f94e2704ull }, + { 0x40139374bc6a7efaull, 0x403dbbaf2a946229ull }, + { 0x4013947ae147ae15ull, 0x403dc0f64b85d895ull }, + { 0x401395810624dd2full, 0x403dc63e5c4d1542ull }, + { 0x401396872b020c4aull, 0x403dcb875d14aac7ull }, + { 0x4013978d4fdf3b64ull, 0x403dd0d14e073332ull }, + { 0x4013989374bc6a7full, 0x403dd61c2f4f503aull }, + { 0x401399999999999aull, 0x403ddb680117ab14ull }, + { 0x40139a9fbe76c8b4ull, 0x403de0b4c38af48aull }, + { 0x40139ba5e353f7cfull, 0x403de60276d3e508ull }, + { 0x40139cac083126eaull, 0x403deb511b1d3c80ull }, + { 0x40139db22d0e5604ull, 0x403df0a0b091c27bull }, + { 0x40139eb851eb851full, 0x403df5f1375c462aull }, + { 0x40139fbe76c8b439ull, 0x403dfb42afa79e41ull }, + { 0x4013a0c49ba5e354ull, 0x403e0095199ea926ull }, + { 0x4013a1cac083126full, 0x403e05e8756c4cc9ull }, + { 0x4013a2d0e5604189ull, 0x403e0b3cc33b76b8ull }, + { 0x4013a3d70a3d70a4ull, 0x403e109203371c30ull }, + { 0x4013a4dd2f1a9fbfull, 0x403e15e8358a39feull }, + { 0x4013a5e353f7ced9ull, 0x403e1b3f5a5fd490ull }, + { 0x4013a6e978d4fdf4ull, 0x403e209771e2f808ull }, + { 0x4013a7ef9db22d0eull, 0x403e25f07c3eb815ull }, + { 0x4013a8f5c28f5c29ull, 0x403e2b4a799e3023ull }, + { 0x4013a9fbe76c8b44ull, 0x403e30a56a2c8333ull }, + { 0x4013ab020c49ba5eull, 0x403e36014e14dbeeull }, + { 0x4013ac083126e979ull, 0x403e3b5e25826cb9ull }, + { 0x4013ad0e56041894ull, 0x403e40bbf0a06f92ull }, + { 0x4013ae147ae147aeull, 0x403e461aaf9a2624ull }, + { 0x4013af1a9fbe76c9ull, 0x403e4b7a629ad9d6ull }, + { 0x4013b020c49ba5e3ull, 0x403e50db09cddbabull }, + { 0x4013b126e978d4feull, 0x403e563ca55e846bull }, + { 0x4013b22d0e560419ull, 0x403e5b9f3578347full }, + { 0x4013b33333333333ull, 0x403e6102ba465404ull }, + { 0x4013b4395810624eull, 0x403e666733f452dbull }, + { 0x4013b53f7ced9169ull, 0x403e6bcca2ada88bull }, + { 0x4013b645a1cac083ull, 0x403e7133069dd452ull }, + { 0x4013b74bc6a7ef9eull, 0x403e769a5ff05d37ull }, + { 0x4013b851eb851eb8ull, 0x403e7c02aed0d1e6ull }, + { 0x4013b95810624dd3ull, 0x403e816bf36ac8daull }, + { 0x4013ba5e353f7ceeull, 0x403e86d62de9e03cull }, + { 0x4013bb645a1cac08ull, 0x403e8c415e79bdf3ull }, + { 0x4013bc6a7ef9db23ull, 0x403e91ad85460fb5ull }, + { 0x4013bd70a3d70a3eull, 0x403e971aa27a8aeaull }, + { 0x4013be76c8b43958ull, 0x403e9c88b642ecbbull }, + { 0x4013bf7ced916873ull, 0x403ea1f7c0cafa25ull }, + { 0x4013c083126e978dull, 0x403ea767c23e7fd3ull }, + { 0x4013c189374bc6a8ull, 0x403eacd8bac95250ull }, + { 0x4013c28f5c28f5c3ull, 0x403eb24aaa974ddaull }, + { 0x4013c395810624ddull, 0x403eb7bd91d4567aull }, + { 0x4013c49ba5e353f8ull, 0x403ebd3170ac5815ull }, + { 0x4013c5a1cac08313ull, 0x403ec2a6474b4648ull }, + { 0x4013c6a7ef9db22dull, 0x403ec81c15dd1c82ull }, + { 0x4013c7ae147ae148ull, 0x403ecd92dc8dde0dull }, + { 0x4013c8b439581062ull, 0x403ed30a9b8995efull }, + { 0x4013c9ba5e353f7dull, 0x403ed88352fc5715ull }, + { 0x4013cac083126e98ull, 0x403eddfd03123c2full }, + { 0x4013cbc6a7ef9db2ull, 0x403ee377abf767bfull }, + { 0x4013cccccccccccdull, 0x403ee8f34dd80430ull }, + { 0x4013cdd2f1a9fbe8ull, 0x403eee6fe8e043b1ull }, + { 0x4013ced916872b02ull, 0x403ef3ed7d3c604bull }, + { 0x4013cfdf3b645a1dull, 0x403ef96c0b189bf0ull }, + { 0x4013d0e560418937ull, 0x403efeeb92a14058ull }, + { 0x4013d1eb851eb852ull, 0x403f046c14029f2eull }, + { 0x4013d2f1a9fbe76dull, 0x403f09ed8f6911e9ull }, + { 0x4013d3f7ced91687ull, 0x403f0f700500f9e1ull }, + { 0x4013d4fdf3b645a2ull, 0x403f14f374f6c05cull }, + { 0x4013d604189374bdull, 0x403f1a77df76d677ull }, + { 0x4013d70a3d70a3d7ull, 0x403f1ffd44adb52dull }, + { 0x4013d810624dd2f2ull, 0x403f2583a4c7dd71ull }, + { 0x4013d916872b020cull, 0x403f2b0afff1d808ull }, + { 0x4013da1cac083127ull, 0x403f3093565835b1ull }, + { 0x4013db22d0e56042ull, 0x403f361ca8278f06ull }, + { 0x4013dc28f5c28f5cull, 0x403f3ba6f58c848cull }, + { 0x4013dd2f1a9fbe77ull, 0x403f41323eb3bec2ull }, + { 0x4013de353f7ced92ull, 0x403f46be83c9ee06ull }, + { 0x4013df3b645a1cacull, 0x403f4c4bc4fbcaa5ull }, + { 0x4013e04189374bc7ull, 0x403f51da027614eaull }, + { 0x4013e147ae147ae1ull, 0x403f57693c6594ffull }, + { 0x4013e24dd2f1a9fcull, 0x403f5cf972f71b14ull }, + { 0x4013e353f7ced917ull, 0x403f628aa6577f3eull }, + { 0x4013e45a1cac0831ull, 0x403f681cd6b3a189ull }, + { 0x4013e5604189374cull, 0x403f6db004386a08ull }, + { 0x4013e66666666667ull, 0x403f73442f12c8b4ull }, + { 0x4013e76c8b439581ull, 0x403f78d9576fb584ull }, + { 0x4013e872b020c49cull, 0x403f7e6f7d7c3077ull }, + { 0x4013e978d4fdf3b6ull, 0x403f8406a1654176ull }, + { 0x4013ea7ef9db22d1ull, 0x403f899ec357f87cull }, + { 0x4013eb851eb851ecull, 0x403f8f37e3816d74ull }, + { 0x4013ec8b43958106ull, 0x403f94d2020ec04cull }, + { 0x4013ed916872b021ull, 0x403f9a6d1f2d1902ull }, + { 0x4013ee978d4fdf3cull, 0x403fa0093b09a789ull }, + { 0x4013ef9db22d0e56ull, 0x403fa5a655d1a3daull }, + { 0x4013f0a3d70a3d71ull, 0x403fab446fb24e04ull }, + { 0x4013f1a9fbe76c8bull, 0x403fb0e388d8ee08ull }, + { 0x4013f2b020c49ba6ull, 0x403fb683a172d409ull }, + { 0x4013f3b645a1cac1ull, 0x403fbc24b9ad5822ull }, + { 0x4013f4bc6a7ef9dbull, 0x403fc1c6d1b5da7dull }, + { 0x4013f5c28f5c28f6ull, 0x403fc769e9b9c35full }, + { 0x4013f6c8b4395811ull, 0x403fcd0e01e68310ull }, + { 0x4013f7ced916872bull, 0x403fd2b31a6991e5ull }, + { 0x4013f8d4fdf3b646ull, 0x403fd85933707059ull }, + { 0x4013f9db22d0e560ull, 0x403fde004d28a6e3ull }, + { 0x4013fae147ae147bull, 0x403fe3a867bfc624ull }, + { 0x4013fbe76c8b4396ull, 0x403fe951836366c6ull }, + { 0x4013fced916872b0ull, 0x403feefba0412988ull }, + { 0x4013fdf3b645a1cbull, 0x403ff4a6be86b756ull }, + { 0x4013fef9db22d0e5ull, 0x403ffa52de61c11dull }, + { 0x4014000000000000ull, 0x4040000000000000ull }, + { 0x4014010624dd2f1bull, 0x404002d711c79a97ull }, + { 0x4014020c49ba5e35ull, 0x404005aea49e94f9ull }, + { 0x401403126e978d50ull, 0x40400886b89bd7e7ull }, + { 0x401404189374bc6bull, 0x40400b5f4dd65029ull }, + { 0x4014051eb851eb85ull, 0x40400e386464ee98ull }, + { 0x40140624dd2f1aa0ull, 0x40401111fc5ea82bull }, + { 0x4014072b020c49baull, 0x404013ec15da75dcull }, + { 0x4014083126e978d5ull, 0x404016c6b0ef54c9ull }, + { 0x401409374bc6a7f0ull, 0x404019a1cdb44619ull }, + { 0x40140a3d70a3d70aull, 0x40401c7d6c404f0bull }, + { 0x40140b4395810625ull, 0x40401f598caa78faull }, + { 0x40140c49ba5e3540ull, 0x404022362f09d150ull }, + { 0x40140d4fdf3b645aull, 0x4040251353756991ull }, + { 0x40140e5604189375ull, 0x404027f0fa04575dull }, + { 0x40140f5c28f5c28full, 0x40402acf22cdb467ull }, + { 0x401410624dd2f1aaull, 0x40402dadcde89e83ull }, + { 0x4014116872b020c5ull, 0x4040308cfb6c3799ull }, + { 0x4014126e978d4fdfull, 0x4040336cab6fa5acull }, + { 0x40141374bc6a7efaull, 0x4040364cde0a12e2ull }, + { 0x4014147ae147ae15ull, 0x4040392d9352ad77ull }, + { 0x401415810624dd2full, 0x40403c0ecb60a7c3ull }, + { 0x401416872b020c4aull, 0x40403ef0864b3846ull }, + { 0x4014178d4fdf3b64ull, 0x404041d2c4299991ull }, + { 0x4014189374bc6a7full, 0x404044b585130a64ull }, + { 0x401419999999999aull, 0x40404798c91ecd93ull }, + { 0x40141a9fbe76c8b4ull, 0x40404a7c90642a14ull }, + { 0x40141ba5e353f7cfull, 0x40404d60dafa6b08ull }, + { 0x40141cac083126eaull, 0x40405045a8f8dfaaull }, + { 0x40141db22d0e5604ull, 0x4040532afa76db57ull }, + { 0x40141eb851eb851full, 0x40405610cf8bb59aull }, + { 0x40141fbe76c8b439ull, 0x404058f7284eca17ull }, + { 0x401420c49ba5e354ull, 0x40405bde04d778a2ull }, + { 0x401421cac083126full, 0x40405ec5653d252dull }, + { 0x401422d0e5604189ull, 0x404061ad499737d2ull }, + { 0x401423d70a3d70a4ull, 0x40406495b1fd1cd8ull }, + { 0x401424dd2f1a9fbfull, 0x4040677e9e8644abull }, + { 0x401425e353f7ced9ull, 0x40406a680f4a23daull }, + { 0x401426e978d4fdf4ull, 0x40406d520460332cull }, + { 0x401427ef9db22d0eull, 0x4040703c7ddfef84ull }, + { 0x401428f5c28f5c29ull, 0x404073277be0d9fcull }, + { 0x401429fbe76c8b44ull, 0x40407612fe7a77d3ull }, + { 0x40142b020c49ba5eull, 0x404078ff05c45272ull }, + { 0x40142c083126e979ull, 0x40407beb91d5f77cull }, + { 0x40142d0e56041894ull, 0x40407ed8a2c6f8b7ull }, + { 0x40142e147ae147aeull, 0x404081c638aeec18ull }, + { 0x40142f1a9fbe76c9ull, 0x404084b453a56bceull }, + { 0x40143020c49ba5e3ull, 0x404087a2f3c2162cull }, + { 0x40143126e978d4feull, 0x40408a92191c8dc2ull }, + { 0x4014322d0e560419ull, 0x40408d81c3cc7948ull }, + { 0x4014333333333333ull, 0x40409071f3e983acull }, + { 0x401434395810624eull, 0x40409362a98b5c15ull }, + { 0x4014353f7ced9169ull, 0x40409653e4c9b5d7ull }, + { 0x40143645a1cac083ull, 0x40409945a5bc487bull }, + { 0x4014374bc6a7ef9eull, 0x40409c37ec7acfc8ull }, + { 0x40143851eb851eb8ull, 0x40409f2ab91d0bb0ull }, + { 0x4014395810624dd3ull, 0x4040a21e0bbac068ull }, + { 0x40143a5e353f7ceeull, 0x4040a511e46bb655ull }, + { 0x40143b645a1cac08ull, 0x4040a8064347ba12ull }, + { 0x40143c6a7ef9db23ull, 0x4040aafb28669c81ull }, + { 0x40143d70a3d70a3eull, 0x4040adf093e032b1ull }, + { 0x40143e76c8b43958ull, 0x4040b0e685cc55edull }, + { 0x40143f7ced916873ull, 0x4040b3dcfe42e3c7ull }, + { 0x40144083126e978dull, 0x4040b6d3fd5bbdfeull }, + { 0x40144189374bc6a8ull, 0x4040b9cb832eca9dull }, + { 0x4014428f5c28f5c3ull, 0x4040bcc38fd3f3e4ull }, + { 0x40144395810624ddull, 0x4040bfbc2363284full }, + { 0x4014449ba5e353f8ull, 0x4040c2b53df45aa5ull }, + { 0x401445a1cac08313ull, 0x4040c5aedf9f81e4ull }, + { 0x401446a7ef9db22dull, 0x4040c8a9087c9949ull }, + { 0x401447ae147ae148ull, 0x4040cba3b8a3a060ull }, + { 0x401448b439581062ull, 0x4040ce9ef02c9ae6ull }, + { 0x401449ba5e353f7dull, 0x4040d19aaf2f90ecull }, + { 0x40144ac083126e98ull, 0x4040d496f5c48ebbull }, + { 0x40144bc6a7ef9db2ull, 0x4040d793c403a4e2ull }, + { 0x40144ccccccccccdull, 0x4040da911a04e83full }, + { 0x40144dd2f1a9fbe8ull, 0x4040dd8ef7e071edull }, + { 0x40144ed916872b02ull, 0x4040e08d5dae5f4dull }, + { 0x40144fdf3b645a1dull, 0x4040e38c4b86d212ull }, + { 0x401450e560418937ull, 0x4040e68bc181f02bull }, + { 0x401451eb851eb852ull, 0x4040e98bbfb7e3ddull }, + { 0x401452f1a9fbe76dull, 0x4040ec8c4640dbadull }, + { 0x401453f7ced91687ull, 0x4040ef8d55350a6cull }, + { 0x401454fdf3b645a2ull, 0x4040f28eecaca740ull }, + { 0x40145604189374bdull, 0x4040f5910cbfed90ull }, + { 0x4014570a3d70a3d7ull, 0x4040f893b5871d13ull }, + { 0x40145810624dd2f2ull, 0x4040fb96e71a79d4ull }, + { 0x40145916872b020cull, 0x4040fe9aa1924c24ull }, + { 0x40145a1cac083127ull, 0x4041019ee506e0adull }, + { 0x40145b22d0e56042ull, 0x404104a3b1908860ull }, + { 0x40145c28f5c28f5cull, 0x404107a907479882ull }, + { 0x40145d2f1a9fbe77ull, 0x40410aaee6446aafull }, + { 0x40145e353f7ced92ull, 0x40410db54e9f5ccfull }, + { 0x40145f3b645a1cacull, 0x404110bc4070d11cull }, + { 0x4014604189374bc7ull, 0x404113c3bbd12e2eull }, + { 0x40146147ae147ae1ull, 0x404116cbc0d8dee4ull }, + { 0x4014624dd2f1a9fcull, 0x404119d44fa05282ull }, + { 0x40146353f7ced917ull, 0x40411cdd683ffc95ull }, + { 0x4014645a1cac0831ull, 0x40411fe70ad05505ull }, + { 0x401465604189374cull, 0x404122f13769d818ull }, + { 0x4014666666666667ull, 0x404125fbee250666ull }, + { 0x4014676c8b439581ull, 0x404129072f1a64dfull }, + { 0x40146872b020c49cull, 0x40412c12fa627cd6ull }, + { 0x40146978d4fdf3b6ull, 0x40412f1f5015dbeeull }, + { 0x40146a7ef9db22d1ull, 0x4041322c304d1431ull }, + { 0x40146b851eb851ecull, 0x404135399b20bbfdull }, + { 0x40146c8b43958106ull, 0x4041384790a96e0cull }, + { 0x40146d916872b021ull, 0x40413b5610ffc982ull }, + { 0x40146e978d4fdf3cull, 0x40413e651c3c71d7ull }, + { 0x40146f9db22d0e56ull, 0x40414174b2780ee0ull }, + { 0x401470a3d70a3d71ull, 0x40414484d3cb4ce1ull }, + { 0x401471a9fbe76c8bull, 0x40414795804edc6eull }, + { 0x401472b020c49ba6ull, 0x40414aa6b81b728dull }, + { 0x401473b645a1cac1ull, 0x40414db87b49c89bull }, + { 0x401474bc6a7ef9dbull, 0x404150cac9f29c58ull }, + { 0x401475c28f5c28f6ull, 0x404153dda42eaff5ull }, + { 0x401476c8b4395811ull, 0x404156f10a16c9fcull }, + { 0x401477ced916872bull, 0x40415a04fbc3b55dull }, + { 0x401478d4fdf3b646ull, 0x40415d19794e4179ull }, + { 0x401479db22d0e560ull, 0x4041602e82cf420bull }, + { 0x40147ae147ae147bull, 0x40416344185f8f43ull }, + { 0x40147be76c8b4396ull, 0x4041665a3a1805b1ull }, + { 0x40147ced916872b0ull, 0x40416970e811864eull }, + { 0x40147df3b645a1cbull, 0x40416c882264f688ull }, + { 0x40147ef9db22d0e5ull, 0x40416f9fe92b402bull }, + { 0x4014800000000000ull, 0x404172b83c7d517bull }, + { 0x4014810624dd2f1bull, 0x404175d11c741d21ull }, + { 0x4014820c49ba5e35ull, 0x404178ea89289a33ull }, + { 0x401483126e978d50ull, 0x40417c0482b3c43full }, + { 0x401484189374bc6bull, 0x40417f1f092e9b38ull }, + { 0x4014851eb851eb85ull, 0x4041823a1cb22383ull }, + { 0x40148624dd2f1aa0ull, 0x40418555bd5765fdull }, + { 0x4014872b020c49baull, 0x40418871eb376fe9ull }, + { 0x4014883126e978d5ull, 0x40418b8ea66b5309ull }, + { 0x401489374bc6a7f0ull, 0x40418eabef0c2588ull }, + { 0x40148a3d70a3d70aull, 0x404191c9c5330208ull }, + { 0x40148b4395810625ull, 0x404194e828f907a5ull }, + { 0x40148c49ba5e3540ull, 0x404198071a7759eaull }, + { 0x40148d4fdf3b645aull, 0x40419b2699c720d8ull }, + { 0x40148e5604189375ull, 0x40419e46a70188eeull }, + { 0x40148f5c28f5c28full, 0x4041a167423fc31aull }, + { 0x401490624dd2f1aaull, 0x4041a4886b9b04cdull }, + { 0x4014916872b020c5ull, 0x4041a7aa232c87eaull }, + { 0x4014926e978d4fdfull, 0x4041aacc690d8acbull }, + { 0x40149374bc6a7efaull, 0x4041adef3d575053ull }, + { 0x4014947ae147ae15ull, 0x4041b112a0231fd3ull }, + { 0x401495810624dd2full, 0x4041b436918a451dull }, + { 0x401496872b020c4aull, 0x4041b75b11a61086ull }, + { 0x4014978d4fdf3b64ull, 0x4041ba80208fd6d7ull }, + { 0x4014989374bc6a7full, 0x4041bda5be60f165ull }, + { 0x401499999999999aull, 0x4041c0cbeb32bdfaull }, + { 0x40149a9fbe76c8b4ull, 0x4041c3f2a71e9ee1ull }, + { 0x40149ba5e353f7cfull, 0x4041c719f23dfaf1ull }, + { 0x40149cac083126eaull, 0x4041ca41ccaa3d79ull }, + { 0x40149db22d0e5604ull, 0x4041cd6a367cd64cull }, + { 0x40149eb851eb851full, 0x4041d0932fcf39c7ull }, + { 0x40149fbe76c8b439ull, 0x4041d3bcb8bae0c5ull }, + { 0x4014a0c49ba5e354ull, 0x4041d6e6d15948adull }, + { 0x4014a1cac083126full, 0x4041da1179c3f368ull }, + { 0x4014a2d0e5604189ull, 0x4041dd3cb2146762ull }, + { 0x4014a3d70a3d70a4ull, 0x4041e0687a642f9aull }, + { 0x4014a4dd2f1a9fbfull, 0x4041e394d2ccdb90ull }, + { 0x4014a5e353f7ced9ull, 0x4041e6c1bb67ff4bull }, + { 0x4014a6e978d4fdf4ull, 0x4041e9ef344f3366ull }, + { 0x4014a7ef9db22d0eull, 0x4041ed1d3d9c14fdull }, + { 0x4014a8f5c28f5c29ull, 0x4041f04bd76845c1ull }, + { 0x4014a9fbe76c8b44ull, 0x4041f37b01cd6be9ull }, + { 0x4014ab020c49ba5eull, 0x4041f6aabce53238ull }, + { 0x4014ac083126e979ull, 0x4041f9db08c94809ull }, + { 0x4014ad0e56041894ull, 0x4041fd0be593613eull }, + { 0x4014ae147ae147aeull, 0x4042003d535d3649ull }, + { 0x4014af1a9fbe76c9ull, 0x4042036f52408433ull }, + { 0x4014b020c49ba5e3ull, 0x404206a1e2570c8eull }, + { 0x4014b126e978d4feull, 0x404209d503ba9588ull }, + { 0x4014b22d0e560419ull, 0x40420d08b684e9dcull }, + { 0x4014b33333333333ull, 0x4042103cfacfd8d6ull }, + { 0x4014b4395810624eull, 0x40421371d0b53661ull }, + { 0x4014b53f7ced9169ull, 0x404216a7384edaf5ull }, + { 0x4014b645a1cac083ull, 0x404219dd31b6a3a1ull }, + { 0x4014b74bc6a7ef9eull, 0x40421d13bd067213ull }, + { 0x4014b851eb851eb8ull, 0x4042204ada582c85ull }, + { 0x4014b95810624dd3ull, 0x4042238289c5bdd8ull }, + { 0x4014ba5e353f7ceeull, 0x404226bacb69157bull }, + { 0x4014bb645a1cac08ull, 0x404229f39f5c277aull }, + { 0x4014bc6a7ef9db23ull, 0x40422d2d05b8ec83ull }, + { 0x4014bd70a3d70a3eull, 0x40423066fe9961daull }, + { 0x4014be76c8b43958ull, 0x404233a18a17895cull }, + { 0x4014bf7ced916873ull, 0x404236dca84d6992ull }, + { 0x4014c083126e978dull, 0x40423a1859550d93ull }, + { 0x4014c189374bc6a8ull, 0x40423d549d488524ull }, + { 0x4014c28f5c28f5c3ull, 0x404240917441e4a0ull }, + { 0x4014c395810624ddull, 0x404243cede5b4505ull }, + { 0x4014c49ba5e353f8ull, 0x4042470cdbaec3faull }, + { 0x4014c5a1cac08313ull, 0x40424a4b6c5683c0ull }, + { 0x4014c6a7ef9db22dull, 0x40424d8a906cab3cull }, + { 0x4014c7ae147ae148ull, 0x404250ca480b6601ull }, + { 0x4014c8b439581062ull, 0x4042540a934ce43aull }, + { 0x4014c9ba5e353f7dull, 0x4042574b724b5ac5ull }, + { 0x4014cac083126e98ull, 0x40425a8ce521031eull }, + { 0x4014cbc6a7ef9db2ull, 0x40425dceebe81b67ull }, + { 0x4014cccccccccccdull, 0x4042611186bae675ull }, + { 0x4014cdd2f1a9fbe8ull, 0x40426454b5b3abbeull }, + { 0x4014ced916872b02ull, 0x4042679878ecb760ull }, + { 0x4014cfdf3b645a1dull, 0x40426adcd0805a2full }, + { 0x4014d0e560418937ull, 0x40426e21bc88e99dull }, + { 0x4014d1eb851eb852ull, 0x404271673d20bfd8ull }, + { 0x4014d2f1a9fbe76dull, 0x404274ad52623baeull }, + { 0x4014d3f7ced91687ull, 0x404277f3fc67c09eull }, + { 0x4014d4fdf3b645a2ull, 0x40427b3b3b4bb6dfull }, + { 0x4014d604189374bdull, 0x40427e830f288b4full }, + { 0x4014d70a3d70a3d7ull, 0x404281cb7818af7cull }, + { 0x4014d810624dd2f2ull, 0x40428514763699afull }, + { 0x4014d916872b020cull, 0x4042885e099cc4d8ull }, + { 0x4014da1cac083127ull, 0x40428ba83265b0a6ull }, + { 0x4014db22d0e56042ull, 0x40428ef2f0abe173ull }, + { 0x4014dc28f5c28f5cull, 0x4042923e4489e04dull }, + { 0x4014dd2f1a9fbe77ull, 0x4042958a2e1a3b04ull }, + { 0x4014de353f7ced92ull, 0x404298d6ad778411ull }, + { 0x4014df3b645a1cacull, 0x40429c23c2bc52a9ull }, + { 0x4014e04189374bc7ull, 0x40429f716e0342c0ull }, + { 0x4014e147ae147ae1ull, 0x4042a2bfaf66f4f6ull }, + { 0x4014e24dd2f1a9fcull, 0x4042a60e87020eb5ull }, + { 0x4014e353f7ced917ull, 0x4042a95df4ef3a14ull }, + { 0x4014e45a1cac0831ull, 0x4042acadf94925e9ull }, + { 0x4014e5604189374cull, 0x4042affe942a85cfull }, + { 0x4014e66666666667ull, 0x4042b34fc5ae1215ull }, + { 0x4014e76c8b439581ull, 0x4042b6a18dee87c7ull }, + { 0x4014e872b020c49cull, 0x4042b9f3ed06a8bbull }, + { 0x4014e978d4fdf3b6ull, 0x4042bd46e3113b7aull }, + { 0x4014ea7ef9db22d1ull, 0x4042c09a70290b5aull }, + { 0x4014eb851eb851ecull, 0x4042c3ee9468e86aull }, + { 0x4014ec8b43958106ull, 0x4042c7434feba778ull }, + { 0x4014ed916872b021ull, 0x4042ca98a2cc2225ull }, + { 0x4014ee978d4fdf3cull, 0x4042cdee8d2536c6ull }, + { 0x4014ef9db22d0e56ull, 0x4042d1450f11c87aull }, + { 0x4014f0a3d70a3d71ull, 0x4042d49c28acbf2cull }, + { 0x4014f1a9fbe76c8bull, 0x4042d7f3da110783ull }, + { 0x4014f2b020c49ba6ull, 0x4042db4c235992f9ull }, + { 0x4014f3b645a1cac1ull, 0x4042dea504a157c8ull }, + { 0x4014f4bc6a7ef9dbull, 0x4042e1fe7e0350f2ull }, + { 0x4014f5c28f5c28f6ull, 0x4042e5588f9a7e4cull }, + { 0x4014f6c8b4395811ull, 0x4042e8b33981e46full }, + { 0x4014f7ced916872bull, 0x4042ec0e7bd48cbeull }, + { 0x4014f8d4fdf3b646ull, 0x4042ef6a56ad8571ull }, + { 0x4014f9db22d0e560ull, 0x4042f2c6ca27e184ull }, + { 0x4014fae147ae147bull, 0x4042f623d65eb8caull }, + { 0x4014fbe76c8b4396ull, 0x4042f9817b6d27dfull }, + { 0x4014fced916872b0ull, 0x4042fcdfb96e502dull }, + { 0x4014fdf3b645a1cbull, 0x4043003e907d57faull }, + { 0x4014fef9db22d0e5ull, 0x4043039e00b56a4eull }, + { 0x4015000000000000ull, 0x404306fe0a31b715ull }, + { 0x4015010624dd2f1bull, 0x40430a5ead0d7301ull }, + { 0x4015020c49ba5e35ull, 0x40430dbfe963d799ull }, + { 0x401503126e978d50ull, 0x40431121bf502344ull }, + { 0x401504189374bc6bull, 0x404314842eed9935ull }, + { 0x4015051eb851eb85ull, 0x404317e738578174ull }, + { 0x40150624dd2f1aa0ull, 0x40431b4adba928eeull }, + { 0x4015072b020c49baull, 0x40431eaf18fde157ull }, + { 0x4015083126e978d5ull, 0x40432213f071014full }, + { 0x401509374bc6a7f0ull, 0x40432579621de441ull }, + { 0x40150a3d70a3d70aull, 0x404328df6e1fea77ull }, + { 0x40150b4395810625ull, 0x40432c461492791eull }, + { 0x40150c49ba5e3540ull, 0x40432fad5590fa3aull }, + { 0x40150d4fdf3b645aull, 0x404333153136dca8ull }, + { 0x40150e5604189375ull, 0x4043367da79f9431ull }, + { 0x40150f5c28f5c28full, 0x404339e6b8e6996full }, + { 0x401510624dd2f1aaull, 0x40433d50652769e9ull }, + { 0x4015116872b020c5ull, 0x404340baac7d87feull }, + { 0x4015126e978d4fdfull, 0x404344258f047af0ull }, + { 0x40151374bc6a7efaull, 0x404347910cd7ceecull }, + { 0x4015147ae147ae15ull, 0x40434afd261314faull }, + { 0x401515810624dd2full, 0x40434e69dad1e306ull }, + { 0x401516872b020c4aull, 0x404351d72b2fd3ecull }, + { 0x4015178d4fdf3b64ull, 0x4043554517488761ull }, + { 0x4015189374bc6a7full, 0x404358b39f37a20full }, + { 0x401519999999999aull, 0x40435c22c318cd7eull }, + { 0x40151a9fbe76c8b4ull, 0x40435f928307b81eull }, + { 0x40151ba5e353f7cfull, 0x40436302df201555ull }, + { 0x40151cac083126eaull, 0x40436673d77d9d67ull }, + { 0x40151db22d0e5604ull, 0x404369e56c3c0d85ull }, + { 0x40151eb851eb851full, 0x40436d579d7727d8ull }, + { 0x40151fbe76c8b439ull, 0x404370ca6b4ab367ull }, + { 0x401520c49ba5e354ull, 0x4043743dd5d27c36ull }, + { 0x401521cac083126full, 0x404377b1dd2a532cull }, + { 0x401522d0e5604189ull, 0x40437b26816e0e22ull }, + { 0x401523d70a3d70a4ull, 0x40437e9bc2b987ecull }, + { 0x401524dd2f1a9fbfull, 0x40438211a128a044ull }, + { 0x401525e353f7ced9ull, 0x404385881cd73bd9ull }, + { 0x401526e978d4fdf4ull, 0x404388ff35e14455ull }, + { 0x401527ef9db22d0eull, 0x40438c76ec62a84bull }, + { 0x401528f5c28f5c29ull, 0x40438fef40775b50ull }, + { 0x401529fbe76c8b44ull, 0x40439368323b55e5ull }, + { 0x40152b020c49ba5eull, 0x404396e1c1ca9582ull }, + { 0x40152c083126e979ull, 0x40439a5bef411ca1ull }, + { 0x40152d0e56041894ull, 0x40439dd6babaf2abull }, + { 0x40152e147ae147aeull, 0x4043a15224542403ull }, + { 0x40152f1a9fbe76c9ull, 0x4043a4ce2c28c210ull }, + { 0x40153020c49ba5e3ull, 0x4043a84ad254e327ull }, + { 0x40153126e978d4feull, 0x4043abc816f4a2a7ull }, + { 0x4015322d0e560419ull, 0x4043af45fa2420e2ull }, + { 0x4015333333333333ull, 0x4043b2c47bff8328ull }, + { 0x401534395810624eull, 0x4043b6439ca2f3d4ull }, + { 0x4015353f7ced9169ull, 0x4043b9c35c2aa233ull }, + { 0x40153645a1cac083ull, 0x4043bd43bab2c296ull }, + { 0x4015374bc6a7ef9eull, 0x4043c0c4b8578e58ull }, + { 0x40153851eb851eb8ull, 0x4043c446553543cbull }, + { 0x4015395810624dd3ull, 0x4043c7c891682650ull }, + { 0x40153a5e353f7ceeull, 0x4043cb4b6d0c7e42ull }, + { 0x40153b645a1cac08ull, 0x4043cecee83e9903ull }, + { 0x40153c6a7ef9db23ull, 0x4043d253031ac905ull }, + { 0x40153d70a3d70a3eull, 0x4043d5d7bdbd65b4ull }, + { 0x40153e76c8b43958ull, 0x4043d95d1842cb89ull }, + { 0x40153f7ced916873ull, 0x4043dce312c75c0aull }, + { 0x40154083126e978dull, 0x4043e069ad677dbeull }, + { 0x40154189374bc6a8ull, 0x4043e3f0e83f9c42ull }, + { 0x4015428f5c28f5c3ull, 0x4043e778c36c2835ull }, + { 0x40154395810624ddull, 0x4043eb013f099740ull }, + { 0x4015449ba5e353f8ull, 0x4043ee8a5b346428ull }, + { 0x401545a1cac08313ull, 0x4043f21418090eb2ull }, + { 0x401546a7ef9db22dull, 0x4043f59e75a41bb3ull }, + { 0x401547ae147ae148ull, 0x4043f9297422151bull }, + { 0x401548b439581062ull, 0x4043fcb5139f89deull }, + { 0x401549ba5e353f7dull, 0x4044004154390e0cull }, + { 0x40154ac083126e98ull, 0x404403ce360b3ac1ull }, + { 0x40154bc6a7ef9db2ull, 0x4044075bb932ae2bull }, + { 0x40154ccccccccccdull, 0x40440ae9ddcc0b97ull }, + { 0x40154dd2f1a9fbe8ull, 0x40440e78a3f3fb5cull }, + { 0x40154ed916872b02ull, 0x404412080bc72ae9ull }, + { 0x40154fdf3b645a1dull, 0x4044159815624cceull }, + { 0x401550e560418937ull, 0x40441928c0e218a4ull }, + { 0x401551eb851eb852ull, 0x40441cba0e634b2cull }, + { 0x401552f1a9fbe76dull, 0x4044204bfe02a637ull }, + { 0x401553f7ced91687ull, 0x404423de8fdcf0afull }, + { 0x401554fdf3b645a2ull, 0x40442771c40ef6a6ull }, + { 0x40155604189374bdull, 0x40442b059ab5893eull }, + { 0x4015570a3d70a3d7ull, 0x40442e9a13ed7eb9ull }, + { 0x40155810624dd2f2ull, 0x4044322f2fd3b280ull }, + { 0x40155916872b020cull, 0x404435c4ee85050full }, + { 0x40155a1cac083127ull, 0x4044395b501e5c0dull }, + { 0x40155b22d0e56042ull, 0x40443cf254bca23cull }, + { 0x40155c28f5c28f5cull, 0x40444089fc7cc77cull }, + { 0x40155d2f1a9fbe77ull, 0x40444422477bc0deull }, + { 0x40155e353f7ced92ull, 0x404447bb35d6888aull }, + { 0x40155f3b645a1cacull, 0x40444b54c7aa1dcdull }, + { 0x4015604189374bc7ull, 0x40444eeefd138526ull }, + { 0x40156147ae147ae1ull, 0x40445289d62fc829ull }, + { 0x4015624dd2f1a9fcull, 0x40445625531bf5a4ull }, + { 0x40156353f7ced917ull, 0x404459c173f5217full }, + { 0x4015645a1cac0831ull, 0x40445d5e38d864cfull }, + { 0x401565604189374cull, 0x404460fba1e2dddaull }, + { 0x4015666666666667ull, 0x40446499af31b009ull }, + { 0x4015676c8b439581ull, 0x4044683860e203f1ull }, + { 0x40156872b020c49cull, 0x40446bd7b711075eull }, + { 0x40156978d4fdf3b6ull, 0x40446f77b1dbed3bull }, + { 0x40156a7ef9db22d1ull, 0x40447318515fedb3ull }, + { 0x40156b851eb851ecull, 0x404476b995ba4613ull }, + { 0x40156c8b43958106ull, 0x40447a5b7f0838daull }, + { 0x40156d916872b021ull, 0x40447dfe0d670dc3ull }, + { 0x40156e978d4fdf3cull, 0x404481a140f411b1ull }, + { 0x40156f9db22d0e56ull, 0x4044854519cc96baull }, + { 0x401570a3d70a3d71ull, 0x404488e9980df435ull }, + { 0x401571a9fbe76c8bull, 0x40448c8ebbd5869dull }, + { 0x401572b020c49ba6ull, 0x404490348540afb4ull }, + { 0x401573b645a1cac1ull, 0x404493daf46cd668ull }, + { 0x401574bc6a7ef9dbull, 0x40449782097766ddull }, + { 0x401575c28f5c28f6ull, 0x40449b29c47dd27eull }, + { 0x401576c8b4395811ull, 0x40449ed2259d8fe3ull }, + { 0x401577ced916872bull, 0x4044a27b2cf41adfull }, + { 0x401578d4fdf3b646ull, 0x4044a624da9ef48cull }, + { 0x401579db22d0e560ull, 0x4044a9cf2ebba333ull }, + { 0x40157ae147ae147bull, 0x4044ad7a2967b268ull }, + { 0x40157be76c8b4396ull, 0x4044b125cac0b2f4ull }, + { 0x40157ced916872b0ull, 0x4044b4d212e43addull }, + { 0x40157df3b645a1cbull, 0x4044b87f01efe577ull }, + { 0x40157ef9db22d0e6ull, 0x4044bc2c9801534cull }, + { 0x4015800000000000ull, 0x4044bfdad5362a27ull }, + { 0x4015810624dd2f1bull, 0x4044c389b9ac1522ull }, + { 0x4015820c49ba5e35ull, 0x4044c7394580c48dull }, + { 0x401583126e978d50ull, 0x4044cae978d1ee0bull }, + { 0x401584189374bc6bull, 0x4044ce9a53bd4c7bull }, + { 0x4015851eb851eb85ull, 0x4044d24bd660a001ull }, + { 0x40158624dd2f1aa0ull, 0x4044d5fe00d9ae17ull }, + { 0x4015872b020c49baull, 0x4044d9b0d346416eull }, + { 0x4015883126e978d5ull, 0x4044dd644dc42a11ull }, + { 0x401589374bc6a7f0ull, 0x4044e11870713d4cull }, + { 0x40158a3d70a3d70aull, 0x4044e4cd3b6b55b4ull }, + { 0x40158b4395810625ull, 0x4044e882aed05338ull }, + { 0x40158c49ba5e3540ull, 0x4044ec38cabe1b07ull }, + { 0x40158d4fdf3b645aull, 0x4044efef8f5297a2ull }, + { 0x40158e5604189375ull, 0x4044f3a6fcabb8e1ull }, + { 0x40158f5c28f5c28full, 0x4044f75f12e773dfull }, + { 0x401590624dd2f1aaull, 0x4044fb17d223c319ull }, + { 0x4015916872b020c5ull, 0x4044fed13a7ea64full }, + { 0x4015926e978d4fdfull, 0x4045028b4c162299ull }, + { 0x40159374bc6a7efaull, 0x404506460708426aull }, + { 0x4015947ae147ae15ull, 0x40450a016b731582ull }, + { 0x401595810624dd2full, 0x40450dbd7974b0f6ull }, + { 0x401596872b020c4aull, 0x4045117a312b2f3cull }, + { 0x4015978d4fdf3b64ull, 0x4045153792b4b015ull }, + { 0x4015989374bc6a7full, 0x404518f59e2f58a9ull }, + { 0x401599999999999aull, 0x40451cb453b9536eull }, + { 0x40159a9fbe76c8b4ull, 0x40452073b370d036ull }, + { 0x40159ba5e353f7cfull, 0x40452433bd740438ull }, + { 0x40159cac083126eaull, 0x404527f471e12a00ull }, + { 0x40159db22d0e5604ull, 0x40452bb5d0d68174ull }, + { 0x40159eb851eb851full, 0x40452f77da724fe6ull }, + { 0x40159fbe76c8b439ull, 0x4045333a8ed2dff7ull }, + { 0x4015a0c49ba5e354ull, 0x404536fdee1681baull }, + { 0x4015a1cac083126full, 0x40453ac1f85b8a94ull }, + { 0x4015a2d0e5604189ull, 0x40453e86adc05552ull }, + { 0x4015a3d70a3d70a4ull, 0x4045424c0e63422aull }, + { 0x4015a4dd2f1a9fbfull, 0x404546121a62b6b0ull }, + { 0x4015a5e353f7ced9ull, 0x404549d8d1dd1dd9ull }, + { 0x4015a6e978d4fdf4ull, 0x40454da034f0e80full }, + { 0x4015a7ef9db22d0eull, 0x4045516843bc8b12ull }, + { 0x4015a8f5c28f5c29ull, 0x40455530fe5e821aull }, + { 0x4015a9fbe76c8b44ull, 0x404558fa64f54dbcull }, + { 0x4015ab020c49ba5eull, 0x40455cc4779f73f9ull }, + { 0x4015ac083126e979ull, 0x4045608f367b8047ull }, + { 0x4015ad0e56041894ull, 0x4045645aa1a8037cull }, + { 0x4015ae147ae147aeull, 0x40456826b94393ddull }, + { 0x4015af1a9fbe76c9ull, 0x40456bf37d6ccd26ull }, + { 0x4015b020c49ba5e3ull, 0x40456fc0ee425074ull }, + { 0x4015b126e978d4feull, 0x4045738f0be2c463ull }, + { 0x4015b22d0e560419ull, 0x4045775dd66cd4f5ull }, + { 0x4015b33333333333ull, 0x40457b2d4dff339cull }, + { 0x4015b4395810624eull, 0x40457efd72b8974aull }, + { 0x4015b53f7ced9169ull, 0x404582ce44b7bc58ull }, + { 0x4015b645a1cac083ull, 0x4045869fc41b6494ull }, + { 0x4015b74bc6a7ef9eull, 0x40458a71f102574full }, + { 0x4015b851eb851eb8ull, 0x40458e44cb8b613eull }, + { 0x4015b95810624dd3ull, 0x4045921853d5549full }, + { 0x4015ba5e353f7ceeull, 0x404595ec89ff091dull }, + { 0x4015bb645a1cac08ull, 0x404599c16e275bdcull }, + { 0x4015bc6a7ef9db23ull, 0x40459d97006d2f87ull }, + { 0x4015bd70a3d70a3eull, 0x4045a16d40ef6c36ull }, + { 0x4015be76c8b43958ull, 0x4045a5442fccff82ull }, + { 0x4015bf7ced916873ull, 0x4045a91bcd24dc8aull }, + { 0x4015c083126e978dull, 0x4045acf41915fbddull }, + { 0x4015c189374bc6a8ull, 0x4045b0cd13bf5b97ull }, + { 0x4015c28f5c28f5c3ull, 0x4045b4a6bd3fff4bull }, + { 0x4015c395810624ddull, 0x4045b88115b6f00dull }, + { 0x4015c49ba5e353f8ull, 0x4045bc5c1d433c7eull }, + { 0x4015c5a1cac08313ull, 0x4045c037d403f8b7ull }, + { 0x4015c6a7ef9db22dull, 0x4045c4143a183e57ull }, + { 0x4015c7ae147ae148ull, 0x4045c7f14f9f2c89ull }, + { 0x4015c8b439581062ull, 0x4045cbcf14b7e7f5ull }, + { 0x4015c9ba5e353f7dull, 0x4045cfad89819ad6ull }, + { 0x4015cac083126e98ull, 0x4045d38cae1b74e4ull }, + { 0x4015cbc6a7ef9db2ull, 0x4045d76c82a4ab60ull }, + { 0x4015cccccccccccdull, 0x4045db4d073c7921ull }, + { 0x4015cdd2f1a9fbe8ull, 0x4045df2e3c021e7full }, + { 0x4015ced916872b02ull, 0x4045e3102114e15cull }, + { 0x4015cfdf3b645a1dull, 0x4045e6f2b6940d34ull }, + { 0x4015d0e560418937ull, 0x4045ead5fc9ef301ull }, + { 0x4015d1eb851eb852ull, 0x4045eeb9f354e95cull }, + { 0x4015d2f1a9fbe76dull, 0x4045f29e9ad54c61ull }, + { 0x4015d3f7ced91687ull, 0x4045f683f33f7dc0ull }, + { 0x4015d4fdf3b645a2ull, 0x4045fa69fcb2e4c4ull }, + { 0x4015d604189374bdull, 0x4045fe50b74eee40ull }, + { 0x4015d70a3d70a3d7ull, 0x4046023823330c9cull }, + { 0x4015d810624dd2f2ull, 0x40460620407eb7e0ull }, + { 0x4015d916872b020cull, 0x40460a090f516d9aull }, + { 0x4015da1cac083127ull, 0x40460df28fcab0ffull }, + { 0x4015db22d0e56042ull, 0x404611dcc20a0ad0ull }, + { 0x4015dc28f5c28f5cull, 0x404615c7a62f0968ull }, + { 0x4015dd2f1a9fbe77ull, 0x404619b33c5940c6ull }, + { 0x4015de353f7ced92ull, 0x40461d9f84a84a7aull }, + { 0x4015df3b645a1cacull, 0x4046218c7f3bc5afull }, + { 0x4015e04189374bc7ull, 0x4046257a2c335739ull }, + { 0x4015e147ae147ae1ull, 0x404629688baea979ull }, + { 0x4015e24dd2f1a9fcull, 0x40462d579dcd6c81ull }, + { 0x4015e353f7ced917ull, 0x4046314762af55f6ull }, + { 0x4015e45a1cac0831ull, 0x40463537da74211dull }, + { 0x4015e5604189374cull, 0x40463929053b8ee9ull }, + { 0x4015e66666666667ull, 0x40463d1ae32565e6ull }, + { 0x4015e76c8b439581ull, 0x4046410d74517243ull }, + { 0x4015e872b020c49cull, 0x40464500b8df85deull }, + { 0x4015e978d4fdf3b6ull, 0x404648f4b0ef782eull }, + { 0x4015ea7ef9db22d1ull, 0x40464ce95ca1265eull }, + { 0x4015eb851eb851ecull, 0x404650debc147337ull }, + { 0x4015ec8b43958106ull, 0x404654d4cf69472bull }, + { 0x4015ed916872b021ull, 0x404658cb96bf9060ull }, + { 0x4015ee978d4fdf3cull, 0x40465cc31237429eull }, + { 0x4015ef9db22d0e56ull, 0x404660bb41f05756ull }, + { 0x4015f0a3d70a3d71ull, 0x404664b4260acdb3ull }, + { 0x4015f1a9fbe76c8bull, 0x404668adbea6aa7full }, + { 0x4015f2b020c49ba6ull, 0x40466ca80be3f842ull }, + { 0x4015f3b645a1cac1ull, 0x404670a30de2c727ull }, + { 0x4015f4bc6a7ef9dbull, 0x4046749ec4c32d0eull }, + { 0x4015f5c28f5c28f6ull, 0x4046789b30a54590ull }, + { 0x4015f6c8b4395811ull, 0x40467c9851a931efull }, + { 0x4015f7ced916872bull, 0x4046809627ef1923ull }, + { 0x4015f8d4fdf3b646ull, 0x40468494b39727e3ull }, + { 0x4015f9db22d0e560ull, 0x40468893f4c1908bull }, + { 0x4015fae147ae147bull, 0x40468c93eb8e8b41ull }, + { 0x4015fbe76c8b4396ull, 0x40469094981e55d5ull }, + { 0x4015fced916872b0ull, 0x40469495fa9133d1ull }, + { 0x4015fdf3b645a1cbull, 0x4046989813076e85ull }, + { 0x4015fef9db22d0e6ull, 0x40469c9ae1a154f0ull }, + { 0x4016000000000000ull, 0x4046a09e667f3bcdull }, + { 0x4016010624dd2f1bull, 0x4046a4a2a1c17da0ull }, + { 0x4016020c49ba5e35ull, 0x4046a8a793887a9cull }, + { 0x401603126e978d50ull, 0x4046acad3bf498c2ull }, + { 0x401604189374bc6bull, 0x4046b0b39b2643caull }, + { 0x4016051eb851eb85ull, 0x4046b4bab13ded2aull }, + { 0x40160624dd2f1aa0ull, 0x4046b8c27e5c0c27ull }, + { 0x4016072b020c49baull, 0x4046bccb02a11dbbull }, + { 0x4016083126e978d5ull, 0x4046c0d43e2da4b2ull }, + { 0x401609374bc6a7f0ull, 0x4046c4de31222994ull }, + { 0x40160a3d70a3d70aull, 0x4046c8e8db9f3aadull }, + { 0x40160b4395810625ull, 0x4046ccf43dc56c1eull }, + { 0x40160c49ba5e3540ull, 0x4046d10057b557c3ull }, + { 0x40160d4fdf3b645aull, 0x4046d50d298f9d43ull }, + { 0x40160e5604189375ull, 0x4046d91ab374e219ull }, + { 0x40160f5c28f5c28full, 0x4046dd28f585d180ull }, + { 0x401610624dd2f1aaull, 0x4046e137efe31c8aull }, + { 0x4016116872b020c5ull, 0x4046e547a2ad7a0full }, + { 0x4016126e978d4fdfull, 0x4046e9580e05a6b4ull }, + { 0x40161374bc6a7efaull, 0x4046ed69320c64f8ull }, + { 0x4016147ae147ae15ull, 0x4046f17b0ee27d21ull }, + { 0x401615810624dd2full, 0x4046f58da4a8bd47ull }, + { 0x401616872b020c4aull, 0x4046f9a0f37ff95eull }, + { 0x4016178d4fdf3b64ull, 0x4046fdb4fb890b21ull }, + { 0x4016189374bc6a7full, 0x404701c9bce4d22cull }, + { 0x401619999999999aull, 0x404705df37b433e8ull }, + { 0x40161a9fbe76c8b4ull, 0x404709f56c181b95ull }, + { 0x40161ba5e353f7cfull, 0x40470e0c5a317a54ull }, + { 0x40161cac083126eaull, 0x4047122402214716ull }, + { 0x40161db22d0e5604ull, 0x4047163c64087ea4ull }, + { 0x40161eb851eb851full, 0x40471a55800823afull }, + { 0x40161fbe76c8b439ull, 0x40471e6f56413eb3ull }, + { 0x401620c49ba5e354ull, 0x40472289e6d4de1aull }, + { 0x401621cac083126full, 0x404726a531e41621ull }, + { 0x401622d0e5604189ull, 0x40472ac1379000e2ull }, + { 0x401623d70a3d70a4ull, 0x40472eddf7f9be65ull }, + { 0x401624dd2f1a9fbfull, 0x404732fb73427486ull }, + { 0x401625e353f7ced9ull, 0x40473719a98b4f04ull }, + { 0x401626e978d4fdf4ull, 0x40473b389af57f8eull }, + { 0x401627ef9db22d0eull, 0x40473f5847a23da6ull }, + { 0x401628f5c28f5c29ull, 0x40474378afb2c6c5ull }, + { 0x401629fbe76c8b44ull, 0x40474799d3485e3cull }, + { 0x40162b020c49ba5eull, 0x40474bbbb2844d46ull }, + { 0x40162c083126e979ull, 0x40474fde4d87e312ull }, + { 0x40162d0e56041894ull, 0x40475401a47474acull }, + { 0x40162e147ae147aeull, 0x40475825b76b5d0aull }, + { 0x40162f1a9fbe76c9ull, 0x40475c4a868dfd1dull }, + { 0x40163020c49ba5e3ull, 0x4047607011fdbbaeull }, + { 0x40163126e978d4feull, 0x4047649659dc0588ull }, + { 0x4016322d0e560419ull, 0x404768bd5e4a4d57ull }, + { 0x4016333333333333ull, 0x40476ce51f6a0bb7ull }, + { 0x401634395810624eull, 0x4047710d9d5cbf41ull }, + { 0x4016353f7ced9169ull, 0x40477536d843ec75ull }, + { 0x40163645a1cac083ull, 0x40477960d0411dc4ull }, + { 0x4016374bc6a7ef9eull, 0x40477d8b8575e3a1ull }, + { 0x40163851eb851eb8ull, 0x404781b6f803d464ull }, + { 0x4016395810624dd3ull, 0x404785e3280c8c6bull }, + { 0x40163a5e353f7ceeull, 0x40478a1015b1adffull }, + { 0x40163b645a1cac08ull, 0x40478e3dc114e162ull }, + { 0x40163c6a7ef9db23ull, 0x4047926c2a57d4daull }, + { 0x40163d70a3d70a3eull, 0x4047969b519c3c9full }, + { 0x40163e76c8b43958ull, 0x40479acb3703d2dfull }, + { 0x40163f7ced916873ull, 0x40479efbdab057d7ull }, + { 0x40164083126e978dull, 0x4047a32d3cc391adull }, + { 0x40164189374bc6a8ull, 0x4047a75f5d5f4c97ull }, + { 0x4016428f5c28f5c3ull, 0x4047ab923ca55ac0ull }, + { 0x40164395810624ddull, 0x4047afc5dab79453ull }, + { 0x4016449ba5e353f8ull, 0x4047b3fa37b7d788ull }, + { 0x401645a1cac08313ull, 0x4047b82f53c80890ull }, + { 0x401646a7ef9db22dull, 0x4047bc652f0a119full }, + { 0x401647ae147ae148ull, 0x4047c09bc99fe2f9ull }, + { 0x401648b439581062ull, 0x4047c4d323ab72dbull }, + { 0x401649ba5e353f7dull, 0x4047c90b3d4ebd97ull }, + { 0x40164ac083126e98ull, 0x4047cd4416abc57dull }, + { 0x40164bc6a7ef9db2ull, 0x4047d17dafe492e4ull }, + { 0x40164ccccccccccdull, 0x4047d5b8091b343dull }, + { 0x40164dd2f1a9fbe8ull, 0x4047d9f32271bdf5ull }, + { 0x40164ed916872b02ull, 0x4047de2efc0a4a87ull }, + { 0x40164fdf3b645a1dull, 0x4047e26b9606fa8aull }, + { 0x401650e560418937ull, 0x4047e6a8f089f491ull }, + { 0x401651eb851eb852ull, 0x4047eae70bb5654full }, + { 0x401652f1a9fbe76dull, 0x4047ef25e7ab7f7dull }, + { 0x401653f7ced91687ull, 0x4047f365848e7be6ull }, + { 0x401654fdf3b645a2ull, 0x4047f7a5e2809974ull }, + { 0x40165604189374bdull, 0x4047fbe701a41d18ull }, + { 0x4016570a3d70a3d7ull, 0x40480028e21b51daull }, + { 0x40165810624dd2f2ull, 0x4048046b840888e3ull }, + { 0x40165916872b020cull, 0x404808aee78e1965ull }, + { 0x40165a1cac083127ull, 0x40480cf30cce60b9ull }, + { 0x40165b22d0e56042ull, 0x40481137f3ebc245ull }, + { 0x40165c28f5c28f5cull, 0x4048157d9d08a78dull }, + { 0x40165d2f1a9fbe77ull, 0x404819c408478039ull }, + { 0x40165e353f7ced92ull, 0x40481e0b35cac203ull }, + { 0x40165f3b645a1cacull, 0x4048225325b4e8c5ull }, + { 0x4016604189374bc7ull, 0x4048269bd8287680ull }, + { 0x40166147ae147ae1ull, 0x40482ae54d47f349ull }, + { 0x4016624dd2f1a9fcull, 0x40482f2f8535ed65ull }, + { 0x40166353f7ced917ull, 0x4048337a8014f92eull }, + { 0x4016645a1cac0831ull, 0x404837c63e07b121ull }, + { 0x401665604189374cull, 0x40483c12bf30b5efull }, + { 0x4016666666666667ull, 0x4048406003b2ae5full }, + { 0x4016676c8b439581ull, 0x404844ae0bb0475full }, + { 0x40166872b020c49cull, 0x404848fcd74c3414ull }, + { 0x40166978d4fdf3b6ull, 0x40484d4c66a92db6ull }, + { 0x40166a7ef9db22d1ull, 0x4048519cb9e9f3bdull }, + { 0x40166b851eb851ecull, 0x404855edd1314bbdull }, + { 0x40166c8b43958106ull, 0x40485a3faca20175ull }, + { 0x40166d916872b021ull, 0x40485e924c5ee6dfull }, + { 0x40166e978d4fdf3cull, 0x404862e5b08ad417ull }, + { 0x40166f9db22d0e56ull, 0x40486739d948a769ull }, + { 0x401670a3d70a3d71ull, 0x40486b8ec6bb455cull }, + { 0x401671a9fbe76c8bull, 0x40486fe479059898ull }, + { 0x401672b020c49ba6ull, 0x4048743af04a920cull }, + { 0x401673b645a1cac1ull, 0x404878922cad28c9ull }, + { 0x401674bc6a7ef9dbull, 0x40487cea2e505a19ull }, + { 0x401675c28f5c28f6ull, 0x40488142f5572987ull }, + { 0x401676c8b4395811ull, 0x4048859c81e4a0c7ull }, + { 0x401677ced916872bull, 0x404889f6d41bcfc9ull }, + { 0x401678d4fdf3b646ull, 0x40488e51ec1fccbfull }, + { 0x401679db22d0e560ull, 0x404892adca13b406ull }, + { 0x40167ae147ae147bull, 0x4048970a6e1aa848ull }, + { 0x40167be76c8b4396ull, 0x40489b67d857d25full }, + { 0x40167ced916872b0ull, 0x40489fc608ee6162ull }, + { 0x40167df3b645a1cbull, 0x4048a42500018ab4ull }, + { 0x40167ef9db22d0e6ull, 0x4048a884bdb489eaull }, + { 0x4016800000000000ull, 0x4048ace5422aa0dbull }, + { 0x4016810624dd2f1bull, 0x4048b1468d8717aeull }, + { 0x4016820c49ba5e35ull, 0x4048b5a89fed3cbbull }, + { 0x401683126e978d50ull, 0x4048ba0b798064aeull }, + { 0x401684189374bc6bull, 0x4048be6f1a63ea6full }, + { 0x4016851eb851eb85ull, 0x4048c2d382bb2f2bull }, + { 0x40168624dd2f1aa0ull, 0x4048c738b2a99a62ull }, + { 0x4016872b020c49baull, 0x4048cb9eaa5299cdull }, + { 0x4016883126e978d5ull, 0x4048d00569d9a182ull }, + { 0x401689374bc6a7f0ull, 0x4048d46cf1622bd2ull }, + { 0x40168a3d70a3d70aull, 0x4048d8d5410fb95cull }, + { 0x40168b4395810625ull, 0x4048dd3e5905d119ull }, + { 0x40168c49ba5e3540ull, 0x4048e1a839680043ull }, + { 0x40168d4fdf3b645aull, 0x4048e612e259da63ull }, + { 0x40168e5604189375ull, 0x4048ea7e53fef962ull }, + { 0x40168f5c28f5c28full, 0x4048eeea8e7afd67ull }, + { 0x401690624dd2f1aaull, 0x4048f35791f18cfeull }, + { 0x4016916872b020c5ull, 0x4048f7c55e8654fbull }, + { 0x4016926e978d4fdfull, 0x4048fc33f45d0885ull }, + { 0x40169374bc6a7efaull, 0x404900a353996129ull }, + { 0x4016947ae147ae15ull, 0x404905137c5f1ebcull }, + { 0x401695810624dd2full, 0x404909846ed2076cull }, + { 0x401696872b020c4aull, 0x40490df62b15e7cfull }, + { 0x4016978d4fdf3b64ull, 0x40491268b14e92c4ull }, + { 0x4016989374bc6a7full, 0x404916dc019fe196ull }, + { 0x401699999999999aull, 0x40491b501c2db3e0ull }, + { 0x40169a9fbe76c8b4ull, 0x40491fc5011bef9eull }, + { 0x40169ba5e353f7cfull, 0x4049243ab08e8135ull }, + { 0x40169cac083126eaull, 0x404928b12aa95b5full }, + { 0x40169db22d0e5604ull, 0x40492d286f907737ull }, + { 0x40169eb851eb851full, 0x404931a07f67d448ull }, + { 0x40169fbe76c8b439ull, 0x404936195a537870ull }, + { 0x4016a0c49ba5e354ull, 0x40493a9300777002ull }, + { 0x4016a1cac083126full, 0x40493f0d71f7cdabull }, + { 0x4016a2d0e5604189ull, 0x40494388aef8aa7full }, + { 0x4016a3d70a3d70a4ull, 0x40494804b79e2607ull }, + { 0x4016a4dd2f1a9fbfull, 0x40494c818c0c662aull }, + { 0x4016a5e353f7ced9ull, 0x404950ff2c679737ull }, + { 0x4016a6e978d4fdf4ull, 0x4049557d98d3ebf9ull }, + { 0x4016a7ef9db22d0eull, 0x404959fcd1759d93ull }, + { 0x4016a8f5c28f5c29ull, 0x40495e7cd670ebabull }, + { 0x4016a9fbe76c8b44ull, 0x404962fda7ea1c47ull }, + { 0x4016ab020c49ba5eull, 0x4049677f46057bddull }, + { 0x4016ac083126e979ull, 0x40496c01b0e75d62ull }, + { 0x4016ad0e56041894ull, 0x40497084e8b41a33ull }, + { 0x4016ae147ae147aeull, 0x40497508ed90121cull }, + { 0x4016af1a9fbe76c9ull, 0x4049798dbf9fab70ull }, + { 0x4016b020c49ba5e3ull, 0x40497e135f0752e3ull }, + { 0x4016b126e978d4feull, 0x40498299cbeb7bb3ull }, + { 0x4016b22d0e560419ull, 0x4049872106709f89ull }, + { 0x4016b33333333333ull, 0x40498ba90ebb3e89ull }, + { 0x4016b4395810624eull, 0x40499031e4efdf5bull }, + { 0x4016b53f7ced9169ull, 0x404994bb89330f1bull }, + { 0x4016b645a1cac083ull, 0x40499945fba9615dull }, + { 0x4016b74bc6a7ef9eull, 0x40499dd13c777043ull }, + { 0x4016b851eb851eb8ull, 0x4049a25d4bc1dc5cull }, + { 0x4016b95810624dd3ull, 0x4049a6ea29ad4ccaull }, + { 0x4016ba5e353f7ceeull, 0x4049ab77d65e6f21ull }, + { 0x4016bb645a1cac08ull, 0x4049b00651f9f779ull }, + { 0x4016bc6a7ef9db23ull, 0x4049b4959ca4a07dull }, + { 0x4016bd70a3d70a3eull, 0x4049b925b6832b4cull }, + { 0x4016be76c8b43958ull, 0x4049bdb69fba5f8full }, + { 0x4016bf7ced916873ull, 0x4049c248586f0b80ull }, + { 0x4016c083126e978dull, 0x4049c6dae0c603d4ull }, + { 0x4016c189374bc6a8ull, 0x4049cb6e38e423d7ull }, + { 0x4016c28f5c28f5c3ull, 0x4049d00260ee4d55ull }, + { 0x4016c395810624ddull, 0x4049d497590968a6ull }, + { 0x4016c49ba5e353f8ull, 0x4049d92d215a64bbull }, + { 0x4016c5a1cac08313ull, 0x4049ddc3ba063709ull }, + { 0x4016c6a7ef9db22dull, 0x4049e25b2331db91ull }, + { 0x4016c7ae147ae148ull, 0x4049e6f35d0254f3ull }, + { 0x4016c8b439581062ull, 0x4049eb8c679cac51ull }, + { 0x4016c9ba5e353f7dull, 0x4049f0264325f16full }, + { 0x4016cac083126e98ull, 0x4049f4c0efc33a9aull }, + { 0x4016cbc6a7ef9db2ull, 0x4049f95c6d99a4b3ull }, + { 0x4016cccccccccccdull, 0x4049fdf8bcce533eull }, + { 0x4016cdd2f1a9fbe8ull, 0x404a0295dd86704dull }, + { 0x4016ced916872b02ull, 0x404a0733cfe72c86ull }, + { 0x4016cfdf3b645a1dull, 0x404a0bd29415bf39ull }, + { 0x4016d0e560418937ull, 0x404a10722a376640ull }, + { 0x4016d1eb851eb852ull, 0x404a151292716622ull }, + { 0x4016d2f1a9fbe76dull, 0x404a19b3cce909f5ull }, + { 0x4016d3f7ced91687ull, 0x404a1e55d9c3a370ull }, + { 0x4016d4fdf3b645a2ull, 0x404a22f8b9268af6ull }, + { 0x4016d604189374bdull, 0x404a279c6b371f7eull }, + { 0x4016d70a3d70a3d7ull, 0x404a2c40f01ac6a1ull }, + { 0x4016d810624dd2f2ull, 0x404a30e647f6eca9ull }, + { 0x4016d916872b020cull, 0x404a358c72f10472ull }, + { 0x4016da1cac083127ull, 0x404a3a33712e8790ull }, + { 0x4016db22d0e56042ull, 0x404a3edb42d4f631ull }, + { 0x4016dc28f5c28f5cull, 0x404a4383e809d72bull }, + { 0x4016dd2f1a9fbe77ull, 0x404a482d60f2b80aull }, + { 0x4016de353f7ced92ull, 0x404a4cd7adb52cf8ull }, + { 0x4016df3b645a1cacull, 0x404a5182ce76d0caull }, + { 0x4016e04189374bc7ull, 0x404a562ec35d450full }, + { 0x4016e147ae147ae1ull, 0x404a5adb8c8e31f3ull }, + { 0x4016e24dd2f1a9fcull, 0x404a5f892a2f4662ull }, + { 0x4016e353f7ced917ull, 0x404a64379c6637edull }, + { 0x4016e45a1cac0831ull, 0x404a68e6e358c2d4ull }, + { 0x4016e5604189374cull, 0x404a6d96ff2caa18ull }, + { 0x4016e66666666667ull, 0x404a7247f007b762ull }, + { 0x4016e76c8b439581ull, 0x404a76f9b60fbb0full }, + { 0x4016e872b020c49cull, 0x404a7bac516a8c3full }, + { 0x4016e978d4fdf3b6ull, 0x404a805fc23e08b9ull }, + { 0x4016ea7ef9db22d1ull, 0x404a851408b0150eull }, + { 0x4016eb851eb851ecull, 0x404a89c924e69c7bull }, + { 0x4016ec8b43958106ull, 0x404a8e7f170790faull }, + { 0x4016ed916872b021ull, 0x404a9335df38eb4cull }, + { 0x4016ee978d4fdf3cull, 0x404a97ed7da0aae5ull }, + { 0x4016ef9db22d0e56ull, 0x404a9ca5f264d5f4ull }, + { 0x4016f0a3d70a3d71ull, 0x404aa15f3dab797aull }, + { 0x4016f1a9fbe76c8bull, 0x404aa6195f9aa922ull }, + { 0x4016f2b020c49ba6ull, 0x404aaad458587f70ull }, + { 0x4016f3b645a1cac1ull, 0x404aaf90280b1d9cull }, + { 0x4016f4bc6a7ef9dbull, 0x404ab44cced8aba0ull }, + { 0x4016f5c28f5c28f6ull, 0x404ab90a4ce7584full }, + { 0x4016f6c8b4395811ull, 0x404abdc8a25d5931ull }, + { 0x4016f7ced916872bull, 0x404ac287cf60ea98ull }, + { 0x4016f8d4fdf3b646ull, 0x404ac747d4184faeull }, + { 0x4016f9db22d0e560ull, 0x404acc08b0a9d252ull }, + { 0x4016fae147ae147bull, 0x404ad0ca653bc346ull }, + { 0x4016fbe76c8b4396ull, 0x404ad58cf1f47a09ull }, + { 0x4016fced916872b0ull, 0x404ada5056fa54e6ull }, + { 0x4016fdf3b645a1cbull, 0x404adf149473b90aull }, + { 0x4016fef9db22d0e6ull, 0x404ae3d9aa871262ull }, + { 0x4017000000000000ull, 0x404ae89f995ad3adull }, + { 0x4017010624dd2f1bull, 0x404aed666115768cull }, + { 0x4017020c49ba5e35ull, 0x404af22e01dd7b62ull }, + { 0x401703126e978d50ull, 0x404af6f67bd96978ull }, + { 0x401704189374bc6bull, 0x404afbbfcf2fcee3ull }, + { 0x4017051eb851eb85ull, 0x404b0089fc07408dull }, + { 0x40170624dd2f1aa0ull, 0x404b055502865a4aull }, + { 0x4017072b020c49bbull, 0x404b0a20e2d3beb8ull }, + { 0x4017083126e978d5ull, 0x404b0eed9d161753ull }, + { 0x401709374bc6a7f0ull, 0x404b13bb3174147full }, + { 0x40170a3d70a3d70aull, 0x404b1889a0146d6eull }, + { 0x40170b4395810625ull, 0x404b1d58e91de043ull }, + { 0x40170c49ba5e3540ull, 0x404b22290cb731f4ull }, + { 0x40170d4fdf3b645aull, 0x404b26fa0b072e57ull }, + { 0x40170e5604189375ull, 0x404b2bcbe434a836ull }, + { 0x40170f5c28f5c28full, 0x404b309e98667929ull }, + { 0x401710624dd2f1aaull, 0x404b357227c381c4ull }, + { 0x4017116872b020c5ull, 0x404b3a469272a96full }, + { 0x4017126e978d4fdfull, 0x404b3f1bd89ade7full }, + { 0x40171374bc6a7efaull, 0x404b43f1fa63163cull }, + { 0x4017147ae147ae15ull, 0x404b48c8f7f24ccdull }, + { 0x401715810624dd2full, 0x404b4da0d16f8542ull }, + { 0x401716872b020c4aull, 0x404b52798701c9a8ull }, + { 0x4017178d4fdf3b64ull, 0x404b575318d02ae6ull }, + { 0x4017189374bc6a7full, 0x404b5c2d8701c0e6ull }, + { 0x401719999999999aull, 0x404b6108d1bdaa74ull }, + { 0x40171a9fbe76c8b4ull, 0x404b65e4f92b0d4eull }, + { 0x40171ba5e353f7cfull, 0x404b6ac1fd711631ull }, + { 0x40171cac083126eaull, 0x404b6f9fdeb6f8c3ull }, + { 0x40171db22d0e5604ull, 0x404b747e9d23ef9dull }, + { 0x40171eb851eb851full, 0x404b795e38df3c60ull }, + { 0x40171fbe76c8b439ull, 0x404b7e3eb210278dull }, + { 0x401720c49ba5e354ull, 0x404b832008de00b6ull }, + { 0x401721cac083126full, 0x404b88023d701e56ull }, + { 0x401722d0e5604189ull, 0x404b8ce54feddde5ull }, + { 0x401723d70a3d70a4ull, 0x404b91c9407ea3e5ull }, + { 0x401724dd2f1a9fbfull, 0x404b96ae0f49dbcaull }, + { 0x401725e353f7ced9ull, 0x404b9b93bc76f804ull }, + { 0x401726e978d4fdf4ull, 0x404ba07a482d7214ull }, + { 0x401727ef9db22d0eull, 0x404ba561b294ca6bull }, + { 0x401728f5c28f5c29ull, 0x404baa49fbd4888cull }, + { 0x401729fbe76c8b44ull, 0x404baf3324143af4ull }, + { 0x40172b020c49ba5eull, 0x404bb41d2b7b7724ull }, + { 0x40172c083126e979ull, 0x404bb9081231d9b1ull }, + { 0x40172d0e56041894ull, 0x404bbdf3d85f062cull }, + { 0x40172e147ae147aeull, 0x404bc2e07e2aa72dull }, + { 0x40172f1a9fbe76c9ull, 0x404bc7ce03bc6e66ull }, + { 0x40173020c49ba5e3ull, 0x404bccbc693c1482ull }, + { 0x40173126e978d4feull, 0x404bd1abaed1594bull }, + { 0x4017322d0e560419ull, 0x404bd69bd4a4038cull }, + { 0x4017333333333333ull, 0x404bdb8cdadbe11full }, + { 0x401734395810624eull, 0x404be07ec1a0c6feull }, + { 0x4017353f7ced9169ull, 0x404be571891a9124ull }, + { 0x40173645a1cac083ull, 0x404bea65317122a3ull }, + { 0x4017374bc6a7ef9eull, 0x404bef59bacc65aeull }, + { 0x40173851eb851eb8ull, 0x404bf44f25544b79ull }, + { 0x4017395810624dd3ull, 0x404bf9457130cc66ull }, + { 0x40173a5e353f7ceeull, 0x404bfe3c9e89e7deull }, + { 0x40173b645a1cac08ull, 0x404c0334ad87a464ull }, + { 0x40173c6a7ef9db23ull, 0x404c082d9e520fa5ull }, + { 0x40173d70a3d70a3eull, 0x404c0d2771113e5cull }, + { 0x40173e76c8b43958ull, 0x404c122225ed4c60ull }, + { 0x40173f7ced916873ull, 0x404c171dbd0e5cb5ull }, + { 0x40174083126e978dull, 0x404c1c1a369c996full }, + { 0x40174189374bc6a8ull, 0x404c211792c033d1ull }, + { 0x4017428f5c28f5c3ull, 0x404c2615d1a16437ull }, + { 0x40174395810624ddull, 0x404c2b14f3686a1dull }, + { 0x4017449ba5e353f8ull, 0x404c3014f83d8c34ull }, + { 0x401745a1cac08313ull, 0x404c3515e0491845ull }, + { 0x401746a7ef9db22dull, 0x404c3a17abb36340ull }, + { 0x401747ae147ae148ull, 0x404c3f1a5aa4c94dull }, + { 0x401748b439581062ull, 0x404c441ded45ada9ull }, + { 0x401749ba5e353f7dull, 0x404c492263be7ad0ull }, + { 0x40174ac083126e98ull, 0x404c4e27be37a25full }, + { 0x40174bc6a7ef9db2ull, 0x404c532dfcd99d1dull }, + { 0x40174ccccccccccdull, 0x404c58351fcceb11ull }, + { 0x40174dd2f1a9fbe8ull, 0x404c5d3d273a1362ull }, + { 0x40174ed916872b02ull, 0x404c62461349a46aull }, + { 0x40174fdf3b645a1dull, 0x404c674fe42433c6ull }, + { 0x401750e560418937ull, 0x404c6c5a99f25e2full }, + { 0x401751eb851eb852ull, 0x404c716634dcc7adull }, + { 0x401752f1a9fbe76dull, 0x404c7672b50c1b6bull }, + { 0x401753f7ced91687ull, 0x404c7b801aa90bcfull }, + { 0x401754fdf3b645a2ull, 0x404c808e65dc5286ull }, + { 0x40175604189374bdull, 0x404c859d96ceb06aull }, + { 0x4017570a3d70a3d7ull, 0x404c8aadada8ed8full }, + { 0x40175810624dd2f2ull, 0x404c8fbeaa93d957ull }, + { 0x40175916872b020cull, 0x404c94d08db84a4dull }, + { 0x40175a1cac083127ull, 0x404c99e3573f1e50ull }, + { 0x40175b22d0e56042ull, 0x404c9ef707513a73ull }, + { 0x40175c28f5c28f5cull, 0x404ca40b9e178b08ull }, + { 0x40175d2f1a9fbe77ull, 0x404ca9211bbb03b7ull }, + { 0x40175e353f7ced92ull, 0x404cae3780649f5bull }, + { 0x40175f3b645a1cacull, 0x404cb34ecc3d6017ull }, + { 0x4017604189374bc7ull, 0x404cb866ff6e4f65ull }, + { 0x40176147ae147ae1ull, 0x404cbd801a207df2ull }, + { 0x4017624dd2f1a9fcull, 0x404cc29a1c7d03caull }, + { 0x40176353f7ced917ull, 0x404cc7b506ad0035ull }, + { 0x4017645a1cac0831ull, 0x404cccd0d8d999c8ull }, + { 0x401765604189374cull, 0x404cd1ed932bfe76ull }, + { 0x4017666666666667ull, 0x404cd70b35cd636full }, + { 0x4017676c8b439581ull, 0x404cdc29c0e70539ull }, + { 0x40176872b020c49cull, 0x404ce14934a227b4ull }, + { 0x40176978d4fdf3b6ull, 0x404ce66991281606ull }, + { 0x40176a7ef9db22d1ull, 0x404ceb8ad6a222b8ull }, + { 0x40176b851eb851ecull, 0x404cf0ad0539a79dull }, + { 0x40176c8b43958106ull, 0x404cf5d01d1805deull }, + { 0x40176d916872b021ull, 0x404cfaf41e66a60cull }, + { 0x40176e978d4fdf3cull, 0x404d0019094ef800ull }, + { 0x40176f9db22d0e56ull, 0x404d053eddfa72f3ull }, + { 0x401770a3d70a3d71ull, 0x404d0a659c929584ull }, + { 0x401771a9fbe76c8bull, 0x404d0f8d4540e59full }, + { 0x401772b020c49ba6ull, 0x404d14b5d82ef0a1ull }, + { 0x401773b645a1cac1ull, 0x404d19df55864b3aull }, + { 0x401774bc6a7ef9dbull, 0x404d1f09bd70917bull }, + { 0x401775c28f5c28f6ull, 0x404d2435101766e5ull }, + { 0x401776c8b4395811ull, 0x404d29614da47650ull }, + { 0x401777ced916872bull, 0x404d2e8e764171f9ull }, + { 0x401778d4fdf3b646ull, 0x404d33bc8a181393ull }, + { 0x401779db22d0e560ull, 0x404d38eb89521c24ull }, + { 0x40177ae147ae147bull, 0x404d3e1b74195431ull }, + { 0x40177be76c8b4396ull, 0x404d434c4a978b9aull }, + { 0x40177ced916872b0ull, 0x404d487e0cf699aaull }, + { 0x40177df3b645a1cbull, 0x404d4db0bb605d29ull }, + { 0x40177ef9db22d0e6ull, 0x404d52e455febc41ull }, + { 0x4017800000000000ull, 0x404d5818dcfba487ull }, + { 0x4017810624dd2f1bull, 0x404d5d4e50810b15ull }, + { 0x4017820c49ba5e35ull, 0x404d6284b0b8ec62ull }, + { 0x401783126e978d50ull, 0x404d67bbfdcd4c6bull }, + { 0x401784189374bc6bull, 0x404d6cf437e83696ull }, + { 0x4017851eb851eb85ull, 0x404d722d5f33bdbfull }, + { 0x40178624dd2f1aa0ull, 0x404d776773d9fc47ull }, + { 0x4017872b020c49bbull, 0x404d7ca2760513fcull }, + { 0x4017883126e978d5ull, 0x404d81de65df2e25ull }, + { 0x401789374bc6a7f0ull, 0x404d871b43927b93ull }, + { 0x40178a3d70a3d70aull, 0x404d8c590f493483ull }, + { 0x40178b4395810625ull, 0x404d9197c92d98c2ull }, + { 0x40178c49ba5e3540ull, 0x404d96d77169ef8full }, + { 0x40178d4fdf3b645aull, 0x404d9c18082887aaull }, + { 0x40178e5604189375ull, 0x404da1598d93b764ull }, + { 0x40178f5c28f5c28full, 0x404da69c01d5dc7full }, + { 0x401790624dd2f1aaull, 0x404dabdf65195c55ull }, + { 0x4017916872b020c5ull, 0x404db123b788a3b9ull }, + { 0x4017926e978d4fdfull, 0x404db668f94e2704ull }, + { 0x40179374bc6a7efaull, 0x404dbbaf2a946229ull }, + { 0x4017947ae147ae15ull, 0x404dc0f64b85d895ull }, + { 0x401795810624dd2full, 0x404dc63e5c4d1542ull }, + { 0x401796872b020c4aull, 0x404dcb875d14aac7ull }, + { 0x4017978d4fdf3b64ull, 0x404dd0d14e073332ull }, + { 0x4017989374bc6a7full, 0x404dd61c2f4f503aull }, + { 0x401799999999999aull, 0x404ddb680117ab14ull }, + { 0x40179a9fbe76c8b4ull, 0x404de0b4c38af48aull }, + { 0x40179ba5e353f7cfull, 0x404de60276d3e508ull }, + { 0x40179cac083126eaull, 0x404deb511b1d3c80ull }, + { 0x40179db22d0e5604ull, 0x404df0a0b091c27bull }, + { 0x40179eb851eb851full, 0x404df5f1375c462aull }, + { 0x40179fbe76c8b439ull, 0x404dfb42afa79e41ull }, + { 0x4017a0c49ba5e354ull, 0x404e0095199ea926ull }, + { 0x4017a1cac083126full, 0x404e05e8756c4cc9ull }, + { 0x4017a2d0e5604189ull, 0x404e0b3cc33b76b8ull }, + { 0x4017a3d70a3d70a4ull, 0x404e109203371c30ull }, + { 0x4017a4dd2f1a9fbfull, 0x404e15e8358a39feull }, + { 0x4017a5e353f7ced9ull, 0x404e1b3f5a5fd490ull }, + { 0x4017a6e978d4fdf4ull, 0x404e209771e2f808ull }, + { 0x4017a7ef9db22d0eull, 0x404e25f07c3eb815ull }, + { 0x4017a8f5c28f5c29ull, 0x404e2b4a799e3023ull }, + { 0x4017a9fbe76c8b44ull, 0x404e30a56a2c8333ull }, + { 0x4017ab020c49ba5eull, 0x404e36014e14dbeeull }, + { 0x4017ac083126e979ull, 0x404e3b5e25826cb9ull }, + { 0x4017ad0e56041894ull, 0x404e40bbf0a06f92ull }, + { 0x4017ae147ae147aeull, 0x404e461aaf9a2624ull }, + { 0x4017af1a9fbe76c9ull, 0x404e4b7a629ad9d6ull }, + { 0x4017b020c49ba5e3ull, 0x404e50db09cddbabull }, + { 0x4017b126e978d4feull, 0x404e563ca55e846bull }, + { 0x4017b22d0e560419ull, 0x404e5b9f3578347full }, + { 0x4017b33333333333ull, 0x404e6102ba465404ull }, + { 0x4017b4395810624eull, 0x404e666733f452dbull }, + { 0x4017b53f7ced9169ull, 0x404e6bcca2ada88bull }, + { 0x4017b645a1cac083ull, 0x404e7133069dd452ull }, + { 0x4017b74bc6a7ef9eull, 0x404e769a5ff05d37ull }, + { 0x4017b851eb851eb8ull, 0x404e7c02aed0d1e6ull }, + { 0x4017b95810624dd3ull, 0x404e816bf36ac8daull }, + { 0x4017ba5e353f7ceeull, 0x404e86d62de9e03cull }, + { 0x4017bb645a1cac08ull, 0x404e8c415e79bdf3ull }, + { 0x4017bc6a7ef9db23ull, 0x404e91ad85460fb5ull }, + { 0x4017bd70a3d70a3eull, 0x404e971aa27a8aeaull }, + { 0x4017be76c8b43958ull, 0x404e9c88b642ecbbull }, + { 0x4017bf7ced916873ull, 0x404ea1f7c0cafa25ull }, + { 0x4017c083126e978dull, 0x404ea767c23e7fd3ull }, + { 0x4017c189374bc6a8ull, 0x404eacd8bac95250ull }, + { 0x4017c28f5c28f5c3ull, 0x404eb24aaa974ddaull }, + { 0x4017c395810624ddull, 0x404eb7bd91d4567aull }, + { 0x4017c49ba5e353f8ull, 0x404ebd3170ac5815ull }, + { 0x4017c5a1cac08313ull, 0x404ec2a6474b4648ull }, + { 0x4017c6a7ef9db22dull, 0x404ec81c15dd1c82ull }, + { 0x4017c7ae147ae148ull, 0x404ecd92dc8dde0dull }, + { 0x4017c8b439581062ull, 0x404ed30a9b8995efull }, + { 0x4017c9ba5e353f7dull, 0x404ed88352fc5715ull }, + { 0x4017cac083126e98ull, 0x404eddfd03123c2full }, + { 0x4017cbc6a7ef9db2ull, 0x404ee377abf767bfull }, + { 0x4017cccccccccccdull, 0x404ee8f34dd80430ull }, + { 0x4017cdd2f1a9fbe8ull, 0x404eee6fe8e043b1ull }, + { 0x4017ced916872b02ull, 0x404ef3ed7d3c604bull }, + { 0x4017cfdf3b645a1dull, 0x404ef96c0b189bf0ull }, + { 0x4017d0e560418937ull, 0x404efeeb92a14058ull }, + { 0x4017d1eb851eb852ull, 0x404f046c14029f2eull }, + { 0x4017d2f1a9fbe76dull, 0x404f09ed8f6911e9ull }, + { 0x4017d3f7ced91687ull, 0x404f0f700500f9e1ull }, + { 0x4017d4fdf3b645a2ull, 0x404f14f374f6c05cull }, + { 0x4017d604189374bdull, 0x404f1a77df76d677ull }, + { 0x4017d70a3d70a3d7ull, 0x404f1ffd44adb52dull }, + { 0x4017d810624dd2f2ull, 0x404f2583a4c7dd71ull }, + { 0x4017d916872b020cull, 0x404f2b0afff1d808ull }, + { 0x4017da1cac083127ull, 0x404f3093565835b1ull }, + { 0x4017db22d0e56042ull, 0x404f361ca8278f06ull }, + { 0x4017dc28f5c28f5cull, 0x404f3ba6f58c848cull }, + { 0x4017dd2f1a9fbe77ull, 0x404f41323eb3bec2ull }, + { 0x4017de353f7ced92ull, 0x404f46be83c9ee06ull }, + { 0x4017df3b645a1cacull, 0x404f4c4bc4fbcaa5ull }, + { 0x4017e04189374bc7ull, 0x404f51da027614eaull }, + { 0x4017e147ae147ae1ull, 0x404f57693c6594ffull }, + { 0x4017e24dd2f1a9fcull, 0x404f5cf972f71b14ull }, + { 0x4017e353f7ced917ull, 0x404f628aa6577f3eull }, + { 0x4017e45a1cac0831ull, 0x404f681cd6b3a189ull }, + { 0x4017e5604189374cull, 0x404f6db004386a08ull }, + { 0x4017e66666666667ull, 0x404f73442f12c8b4ull }, + { 0x4017e76c8b439581ull, 0x404f78d9576fb584ull }, + { 0x4017e872b020c49cull, 0x404f7e6f7d7c3077ull }, + { 0x4017e978d4fdf3b6ull, 0x404f8406a1654176ull }, + { 0x4017ea7ef9db22d1ull, 0x404f899ec357f87cull }, + { 0x4017eb851eb851ecull, 0x404f8f37e3816d74ull }, + { 0x4017ec8b43958106ull, 0x404f94d2020ec04cull }, + { 0x4017ed916872b021ull, 0x404f9a6d1f2d1902ull }, + { 0x4017ee978d4fdf3cull, 0x404fa0093b09a789ull }, + { 0x4017ef9db22d0e56ull, 0x404fa5a655d1a3daull }, + { 0x4017f0a3d70a3d71ull, 0x404fab446fb24e04ull }, + { 0x4017f1a9fbe76c8bull, 0x404fb0e388d8ee08ull }, + { 0x4017f2b020c49ba6ull, 0x404fb683a172d409ull }, + { 0x4017f3b645a1cac1ull, 0x404fbc24b9ad5822ull }, + { 0x4017f4bc6a7ef9dbull, 0x404fc1c6d1b5da7dull }, + { 0x4017f5c28f5c28f6ull, 0x404fc769e9b9c35full }, + { 0x4017f6c8b4395811ull, 0x404fcd0e01e68310ull }, + { 0x4017f7ced916872bull, 0x404fd2b31a6991e5ull }, + { 0x4017f8d4fdf3b646ull, 0x404fd85933707059ull }, + { 0x4017f9db22d0e560ull, 0x404fde004d28a6e3ull }, + { 0x4017fae147ae147bull, 0x404fe3a867bfc624ull }, + { 0x4017fbe76c8b4396ull, 0x404fe951836366c6ull }, + { 0x4017fced916872b0ull, 0x404feefba0412988ull }, + { 0x4017fdf3b645a1cbull, 0x404ff4a6be86b756ull }, + { 0x4017fef9db22d0e6ull, 0x404ffa52de61c123ull }, + { 0x4018000000000000ull, 0x4050000000000000ull }, + { 0x4018010624dd2f1bull, 0x405002d711c79a97ull }, + { 0x4018020c49ba5e35ull, 0x405005aea49e94f9ull }, + { 0x401803126e978d50ull, 0x40500886b89bd7e7ull }, + { 0x401804189374bc6bull, 0x40500b5f4dd65029ull }, + { 0x4018051eb851eb85ull, 0x40500e386464ee98ull }, + { 0x40180624dd2f1aa0ull, 0x40501111fc5ea82bull }, + { 0x4018072b020c49bbull, 0x405013ec15da75dfull }, + { 0x4018083126e978d5ull, 0x405016c6b0ef54c9ull }, + { 0x401809374bc6a7f0ull, 0x405019a1cdb44619ull }, + { 0x40180a3d70a3d70aull, 0x40501c7d6c404f0bull }, + { 0x40180b4395810625ull, 0x40501f598caa78faull }, + { 0x40180c49ba5e3540ull, 0x405022362f09d150ull }, + { 0x40180d4fdf3b645aull, 0x4050251353756991ull }, + { 0x40180e5604189375ull, 0x405027f0fa04575dull }, + { 0x40180f5c28f5c290ull, 0x40502acf22cdb46aull }, + { 0x401810624dd2f1aaull, 0x40502dadcde89e83ull }, + { 0x4018116872b020c5ull, 0x4050308cfb6c3799ull }, + { 0x4018126e978d4fdfull, 0x4050336cab6fa5acull }, + { 0x40181374bc6a7efaull, 0x4050364cde0a12e2ull }, + { 0x4018147ae147ae15ull, 0x4050392d9352ad77ull }, + { 0x401815810624dd2full, 0x40503c0ecb60a7c3ull }, + { 0x401816872b020c4aull, 0x40503ef0864b3846ull }, + { 0x4018178d4fdf3b64ull, 0x405041d2c4299991ull }, + { 0x4018189374bc6a7full, 0x405044b585130a64ull }, + { 0x401819999999999aull, 0x40504798c91ecd93ull }, + { 0x40181a9fbe76c8b4ull, 0x40504a7c90642a14ull }, + { 0x40181ba5e353f7cfull, 0x40504d60dafa6b08ull }, + { 0x40181cac083126eaull, 0x40505045a8f8dfaaull }, + { 0x40181db22d0e5604ull, 0x4050532afa76db57ull }, + { 0x40181eb851eb851full, 0x40505610cf8bb59aull }, + { 0x40181fbe76c8b439ull, 0x405058f7284eca17ull }, + { 0x401820c49ba5e354ull, 0x40505bde04d778a2ull }, + { 0x401821cac083126full, 0x40505ec5653d252dull }, + { 0x401822d0e5604189ull, 0x405061ad499737d2ull }, + { 0x401823d70a3d70a4ull, 0x40506495b1fd1cd8ull }, + { 0x401824dd2f1a9fbfull, 0x4050677e9e8644abull }, + { 0x401825e353f7ced9ull, 0x40506a680f4a23daull }, + { 0x401826e978d4fdf4ull, 0x40506d520460332cull }, + { 0x401827ef9db22d0eull, 0x4050703c7ddfef84ull }, + { 0x401828f5c28f5c29ull, 0x405073277be0d9fcull }, + { 0x401829fbe76c8b44ull, 0x40507612fe7a77d3ull }, + { 0x40182b020c49ba5eull, 0x405078ff05c45272ull }, + { 0x40182c083126e979ull, 0x40507beb91d5f77cull }, + { 0x40182d0e56041894ull, 0x40507ed8a2c6f8b7ull }, + { 0x40182e147ae147aeull, 0x405081c638aeec18ull }, + { 0x40182f1a9fbe76c9ull, 0x405084b453a56bceull }, + { 0x40183020c49ba5e3ull, 0x405087a2f3c2162cull }, + { 0x40183126e978d4feull, 0x40508a92191c8dc2ull }, + { 0x4018322d0e560419ull, 0x40508d81c3cc7948ull }, + { 0x4018333333333333ull, 0x40509071f3e983acull }, + { 0x401834395810624eull, 0x40509362a98b5c15ull }, + { 0x4018353f7ced9169ull, 0x40509653e4c9b5d7ull }, + { 0x40183645a1cac083ull, 0x40509945a5bc487bull }, + { 0x4018374bc6a7ef9eull, 0x40509c37ec7acfc8ull }, + { 0x40183851eb851eb8ull, 0x40509f2ab91d0bb0ull }, + { 0x4018395810624dd3ull, 0x4050a21e0bbac068ull }, + { 0x40183a5e353f7ceeull, 0x4050a511e46bb655ull }, + { 0x40183b645a1cac08ull, 0x4050a8064347ba12ull }, + { 0x40183c6a7ef9db23ull, 0x4050aafb28669c81ull }, + { 0x40183d70a3d70a3eull, 0x4050adf093e032b1ull }, + { 0x40183e76c8b43958ull, 0x4050b0e685cc55edull }, + { 0x40183f7ced916873ull, 0x4050b3dcfe42e3c7ull }, + { 0x40184083126e978dull, 0x4050b6d3fd5bbdfeull }, + { 0x40184189374bc6a8ull, 0x4050b9cb832eca9dull }, + { 0x4018428f5c28f5c3ull, 0x4050bcc38fd3f3e4ull }, + { 0x40184395810624ddull, 0x4050bfbc2363284full }, + { 0x4018449ba5e353f8ull, 0x4050c2b53df45aa5ull }, + { 0x401845a1cac08313ull, 0x4050c5aedf9f81e4ull }, + { 0x401846a7ef9db22dull, 0x4050c8a9087c9949ull }, + { 0x401847ae147ae148ull, 0x4050cba3b8a3a060ull }, + { 0x401848b439581062ull, 0x4050ce9ef02c9ae6ull }, + { 0x401849ba5e353f7dull, 0x4050d19aaf2f90ecull }, + { 0x40184ac083126e98ull, 0x4050d496f5c48ebbull }, + { 0x40184bc6a7ef9db2ull, 0x4050d793c403a4e2ull }, + { 0x40184ccccccccccdull, 0x4050da911a04e83full }, + { 0x40184dd2f1a9fbe8ull, 0x4050dd8ef7e071edull }, + { 0x40184ed916872b02ull, 0x4050e08d5dae5f4dull }, + { 0x40184fdf3b645a1dull, 0x4050e38c4b86d212ull }, + { 0x401850e560418937ull, 0x4050e68bc181f02bull }, + { 0x401851eb851eb852ull, 0x4050e98bbfb7e3ddull }, + { 0x401852f1a9fbe76dull, 0x4050ec8c4640dbadull }, + { 0x401853f7ced91687ull, 0x4050ef8d55350a6cull }, + { 0x401854fdf3b645a2ull, 0x4050f28eecaca740ull }, + { 0x40185604189374bdull, 0x4050f5910cbfed90ull }, + { 0x4018570a3d70a3d7ull, 0x4050f893b5871d13ull }, + { 0x40185810624dd2f2ull, 0x4050fb96e71a79d4ull }, + { 0x40185916872b020cull, 0x4050fe9aa1924c24ull }, + { 0x40185a1cac083127ull, 0x4051019ee506e0adull }, + { 0x40185b22d0e56042ull, 0x405104a3b1908860ull }, + { 0x40185c28f5c28f5cull, 0x405107a907479882ull }, + { 0x40185d2f1a9fbe77ull, 0x40510aaee6446aafull }, + { 0x40185e353f7ced92ull, 0x40510db54e9f5ccfull }, + { 0x40185f3b645a1cacull, 0x405110bc4070d11cull }, + { 0x4018604189374bc7ull, 0x405113c3bbd12e2eull }, + { 0x40186147ae147ae1ull, 0x405116cbc0d8dee4ull }, + { 0x4018624dd2f1a9fcull, 0x405119d44fa05282ull }, + { 0x40186353f7ced917ull, 0x40511cdd683ffc95ull }, + { 0x4018645a1cac0831ull, 0x40511fe70ad05505ull }, + { 0x401865604189374cull, 0x405122f13769d818ull }, + { 0x4018666666666667ull, 0x405125fbee250666ull }, + { 0x4018676c8b439581ull, 0x405129072f1a64dfull }, + { 0x40186872b020c49cull, 0x40512c12fa627cd6ull }, + { 0x40186978d4fdf3b6ull, 0x40512f1f5015dbeeull }, + { 0x40186a7ef9db22d1ull, 0x4051322c304d1431ull }, + { 0x40186b851eb851ecull, 0x405135399b20bbfdull }, + { 0x40186c8b43958106ull, 0x4051384790a96e0cull }, + { 0x40186d916872b021ull, 0x40513b5610ffc982ull }, + { 0x40186e978d4fdf3cull, 0x40513e651c3c71d7ull }, + { 0x40186f9db22d0e56ull, 0x40514174b2780ee0ull }, + { 0x401870a3d70a3d71ull, 0x40514484d3cb4ce1ull }, + { 0x401871a9fbe76c8bull, 0x40514795804edc6eull }, + { 0x401872b020c49ba6ull, 0x40514aa6b81b728dull }, + { 0x401873b645a1cac1ull, 0x40514db87b49c89bull }, + { 0x401874bc6a7ef9dbull, 0x405150cac9f29c58ull }, + { 0x401875c28f5c28f6ull, 0x405153dda42eaff5ull }, + { 0x401876c8b4395811ull, 0x405156f10a16c9fcull }, + { 0x401877ced916872bull, 0x40515a04fbc3b55dull }, + { 0x401878d4fdf3b646ull, 0x40515d19794e4179ull }, + { 0x401879db22d0e560ull, 0x4051602e82cf420bull }, + { 0x40187ae147ae147bull, 0x40516344185f8f43ull }, + { 0x40187be76c8b4396ull, 0x4051665a3a1805b1ull }, + { 0x40187ced916872b0ull, 0x40516970e811864eull }, + { 0x40187df3b645a1cbull, 0x40516c882264f688ull }, + { 0x40187ef9db22d0e6ull, 0x40516f9fe92b402eull }, + { 0x4018800000000000ull, 0x405172b83c7d517bull }, + { 0x4018810624dd2f1bull, 0x405175d11c741d21ull }, + { 0x4018820c49ba5e35ull, 0x405178ea89289a33ull }, + { 0x401883126e978d50ull, 0x40517c0482b3c43full }, + { 0x401884189374bc6bull, 0x40517f1f092e9b38ull }, + { 0x4018851eb851eb85ull, 0x4051823a1cb22383ull }, + { 0x40188624dd2f1aa0ull, 0x40518555bd5765fdull }, + { 0x4018872b020c49bbull, 0x40518871eb376fecull }, + { 0x4018883126e978d5ull, 0x40518b8ea66b5309ull }, + { 0x401889374bc6a7f0ull, 0x40518eabef0c2588ull }, + { 0x40188a3d70a3d70aull, 0x405191c9c5330208ull }, + { 0x40188b4395810625ull, 0x405194e828f907a5ull }, + { 0x40188c49ba5e3540ull, 0x405198071a7759eaull }, + { 0x40188d4fdf3b645aull, 0x40519b2699c720d8ull }, + { 0x40188e5604189375ull, 0x40519e46a70188eeull }, + { 0x40188f5c28f5c290ull, 0x4051a167423fc31dull }, + { 0x401890624dd2f1aaull, 0x4051a4886b9b04cdull }, + { 0x4018916872b020c5ull, 0x4051a7aa232c87eaull }, + { 0x4018926e978d4fdfull, 0x4051aacc690d8acbull }, + { 0x40189374bc6a7efaull, 0x4051adef3d575053ull }, + { 0x4018947ae147ae15ull, 0x4051b112a0231fd3ull }, + { 0x401895810624dd2full, 0x4051b436918a451dull }, + { 0x401896872b020c4aull, 0x4051b75b11a61086ull }, + { 0x4018978d4fdf3b64ull, 0x4051ba80208fd6d7ull }, + { 0x4018989374bc6a7full, 0x4051bda5be60f165ull }, + { 0x401899999999999aull, 0x4051c0cbeb32bdfaull }, + { 0x40189a9fbe76c8b4ull, 0x4051c3f2a71e9ee1ull }, + { 0x40189ba5e353f7cfull, 0x4051c719f23dfaf1ull }, + { 0x40189cac083126eaull, 0x4051ca41ccaa3d79ull }, + { 0x40189db22d0e5604ull, 0x4051cd6a367cd64cull }, + { 0x40189eb851eb851full, 0x4051d0932fcf39c7ull }, + { 0x40189fbe76c8b439ull, 0x4051d3bcb8bae0c5ull }, + { 0x4018a0c49ba5e354ull, 0x4051d6e6d15948adull }, + { 0x4018a1cac083126full, 0x4051da1179c3f368ull }, + { 0x4018a2d0e5604189ull, 0x4051dd3cb2146762ull }, + { 0x4018a3d70a3d70a4ull, 0x4051e0687a642f9aull }, + { 0x4018a4dd2f1a9fbfull, 0x4051e394d2ccdb90ull }, + { 0x4018a5e353f7ced9ull, 0x4051e6c1bb67ff4bull }, + { 0x4018a6e978d4fdf4ull, 0x4051e9ef344f3366ull }, + { 0x4018a7ef9db22d0eull, 0x4051ed1d3d9c14fdull }, + { 0x4018a8f5c28f5c29ull, 0x4051f04bd76845c1ull }, + { 0x4018a9fbe76c8b44ull, 0x4051f37b01cd6be9ull }, + { 0x4018ab020c49ba5eull, 0x4051f6aabce53238ull }, + { 0x4018ac083126e979ull, 0x4051f9db08c94809ull }, + { 0x4018ad0e56041894ull, 0x4051fd0be593613eull }, + { 0x4018ae147ae147aeull, 0x4052003d535d3649ull }, + { 0x4018af1a9fbe76c9ull, 0x4052036f52408433ull }, + { 0x4018b020c49ba5e3ull, 0x405206a1e2570c8eull }, + { 0x4018b126e978d4feull, 0x405209d503ba9588ull }, + { 0x4018b22d0e560419ull, 0x40520d08b684e9dcull }, + { 0x4018b33333333333ull, 0x4052103cfacfd8d6ull }, + { 0x4018b4395810624eull, 0x40521371d0b53661ull }, + { 0x4018b53f7ced9169ull, 0x405216a7384edaf5ull }, + { 0x4018b645a1cac083ull, 0x405219dd31b6a3a1ull }, + { 0x4018b74bc6a7ef9eull, 0x40521d13bd067213ull }, + { 0x4018b851eb851eb8ull, 0x4052204ada582c85ull }, + { 0x4018b95810624dd3ull, 0x4052238289c5bdd8ull }, + { 0x4018ba5e353f7ceeull, 0x405226bacb69157bull }, + { 0x4018bb645a1cac08ull, 0x405229f39f5c277aull }, + { 0x4018bc6a7ef9db23ull, 0x40522d2d05b8ec83ull }, + { 0x4018bd70a3d70a3eull, 0x40523066fe9961daull }, + { 0x4018be76c8b43958ull, 0x405233a18a17895cull }, + { 0x4018bf7ced916873ull, 0x405236dca84d6992ull }, + { 0x4018c083126e978dull, 0x40523a1859550d93ull }, + { 0x4018c189374bc6a8ull, 0x40523d549d488524ull }, + { 0x4018c28f5c28f5c3ull, 0x405240917441e4a0ull }, + { 0x4018c395810624ddull, 0x405243cede5b4505ull }, + { 0x4018c49ba5e353f8ull, 0x4052470cdbaec3faull }, + { 0x4018c5a1cac08313ull, 0x40524a4b6c5683c0ull }, + { 0x4018c6a7ef9db22dull, 0x40524d8a906cab3cull }, + { 0x4018c7ae147ae148ull, 0x405250ca480b6601ull }, + { 0x4018c8b439581062ull, 0x4052540a934ce43aull }, + { 0x4018c9ba5e353f7dull, 0x4052574b724b5ac5ull }, + { 0x4018cac083126e98ull, 0x40525a8ce521031eull }, + { 0x4018cbc6a7ef9db2ull, 0x40525dceebe81b67ull }, + { 0x4018cccccccccccdull, 0x4052611186bae675ull }, + { 0x4018cdd2f1a9fbe8ull, 0x40526454b5b3abbeull }, + { 0x4018ced916872b02ull, 0x4052679878ecb760ull }, + { 0x4018cfdf3b645a1dull, 0x40526adcd0805a2full }, + { 0x4018d0e560418937ull, 0x40526e21bc88e99dull }, + { 0x4018d1eb851eb852ull, 0x405271673d20bfd8ull }, + { 0x4018d2f1a9fbe76dull, 0x405274ad52623baeull }, + { 0x4018d3f7ced91687ull, 0x405277f3fc67c09eull }, + { 0x4018d4fdf3b645a2ull, 0x40527b3b3b4bb6dfull }, + { 0x4018d604189374bdull, 0x40527e830f288b4full }, + { 0x4018d70a3d70a3d7ull, 0x405281cb7818af7cull }, + { 0x4018d810624dd2f2ull, 0x40528514763699afull }, + { 0x4018d916872b020cull, 0x4052885e099cc4d8ull }, + { 0x4018da1cac083127ull, 0x40528ba83265b0a6ull }, + { 0x4018db22d0e56042ull, 0x40528ef2f0abe173ull }, + { 0x4018dc28f5c28f5cull, 0x4052923e4489e04dull }, + { 0x4018dd2f1a9fbe77ull, 0x4052958a2e1a3b04ull }, + { 0x4018de353f7ced92ull, 0x405298d6ad778411ull }, + { 0x4018df3b645a1cacull, 0x40529c23c2bc52a9ull }, + { 0x4018e04189374bc7ull, 0x40529f716e0342c0ull }, + { 0x4018e147ae147ae1ull, 0x4052a2bfaf66f4f6ull }, + { 0x4018e24dd2f1a9fcull, 0x4052a60e87020eb5ull }, + { 0x4018e353f7ced917ull, 0x4052a95df4ef3a14ull }, + { 0x4018e45a1cac0831ull, 0x4052acadf94925e9ull }, + { 0x4018e5604189374cull, 0x4052affe942a85cfull }, + { 0x4018e66666666667ull, 0x4052b34fc5ae1215ull }, + { 0x4018e76c8b439581ull, 0x4052b6a18dee87c7ull }, + { 0x4018e872b020c49cull, 0x4052b9f3ed06a8bbull }, + { 0x4018e978d4fdf3b6ull, 0x4052bd46e3113b7aull }, + { 0x4018ea7ef9db22d1ull, 0x4052c09a70290b5aull }, + { 0x4018eb851eb851ecull, 0x4052c3ee9468e86aull }, + { 0x4018ec8b43958106ull, 0x4052c7434feba778ull }, + { 0x4018ed916872b021ull, 0x4052ca98a2cc2225ull }, + { 0x4018ee978d4fdf3cull, 0x4052cdee8d2536c6ull }, + { 0x4018ef9db22d0e56ull, 0x4052d1450f11c87aull }, + { 0x4018f0a3d70a3d71ull, 0x4052d49c28acbf2cull }, + { 0x4018f1a9fbe76c8bull, 0x4052d7f3da110783ull }, + { 0x4018f2b020c49ba6ull, 0x4052db4c235992f9ull }, + { 0x4018f3b645a1cac1ull, 0x4052dea504a157c8ull }, + { 0x4018f4bc6a7ef9dbull, 0x4052e1fe7e0350f2ull }, + { 0x4018f5c28f5c28f6ull, 0x4052e5588f9a7e4cull }, + { 0x4018f6c8b4395811ull, 0x4052e8b33981e46full }, + { 0x4018f7ced916872bull, 0x4052ec0e7bd48cbeull }, + { 0x4018f8d4fdf3b646ull, 0x4052ef6a56ad8571ull }, + { 0x4018f9db22d0e560ull, 0x4052f2c6ca27e184ull }, + { 0x4018fae147ae147bull, 0x4052f623d65eb8caull }, + { 0x4018fbe76c8b4396ull, 0x4052f9817b6d27dfull }, + { 0x4018fced916872b0ull, 0x4052fcdfb96e502dull }, + { 0x4018fdf3b645a1cbull, 0x4053003e907d57faull }, + { 0x4018fef9db22d0e6ull, 0x4053039e00b56a52ull }, + { 0x4019000000000000ull, 0x405306fe0a31b715ull }, + { 0x4019010624dd2f1bull, 0x40530a5ead0d7301ull }, + { 0x4019020c49ba5e35ull, 0x40530dbfe963d799ull }, + { 0x401903126e978d50ull, 0x40531121bf502344ull }, + { 0x401904189374bc6bull, 0x405314842eed9935ull }, + { 0x4019051eb851eb85ull, 0x405317e738578174ull }, + { 0x40190624dd2f1aa0ull, 0x40531b4adba928eeull }, + { 0x4019072b020c49bbull, 0x40531eaf18fde15bull }, + { 0x4019083126e978d5ull, 0x40532213f071014full }, + { 0x401909374bc6a7f0ull, 0x40532579621de441ull }, + { 0x40190a3d70a3d70aull, 0x405328df6e1fea77ull }, + { 0x40190b4395810625ull, 0x40532c461492791eull }, + { 0x40190c49ba5e3540ull, 0x40532fad5590fa3aull }, + { 0x40190d4fdf3b645aull, 0x405333153136dca8ull }, + { 0x40190e5604189375ull, 0x4053367da79f9431ull }, + { 0x40190f5c28f5c290ull, 0x405339e6b8e69972ull }, + { 0x401910624dd2f1aaull, 0x40533d50652769e9ull }, + { 0x4019116872b020c5ull, 0x405340baac7d87feull }, + { 0x4019126e978d4fdfull, 0x405344258f047af0ull }, + { 0x40191374bc6a7efaull, 0x405347910cd7ceecull }, + { 0x4019147ae147ae15ull, 0x40534afd261314faull }, + { 0x401915810624dd2full, 0x40534e69dad1e306ull }, + { 0x401916872b020c4aull, 0x405351d72b2fd3ecull }, + { 0x4019178d4fdf3b64ull, 0x4053554517488761ull }, + { 0x4019189374bc6a7full, 0x405358b39f37a20full }, + { 0x401919999999999aull, 0x40535c22c318cd7eull }, + { 0x40191a9fbe76c8b4ull, 0x40535f928307b81eull }, + { 0x40191ba5e353f7cfull, 0x40536302df201555ull }, + { 0x40191cac083126eaull, 0x40536673d77d9d67ull }, + { 0x40191db22d0e5604ull, 0x405369e56c3c0d85ull }, + { 0x40191eb851eb851full, 0x40536d579d7727d8ull }, + { 0x40191fbe76c8b439ull, 0x405370ca6b4ab367ull }, + { 0x401920c49ba5e354ull, 0x4053743dd5d27c36ull }, + { 0x401921cac083126full, 0x405377b1dd2a532cull }, + { 0x401922d0e5604189ull, 0x40537b26816e0e22ull }, + { 0x401923d70a3d70a4ull, 0x40537e9bc2b987ecull }, + { 0x401924dd2f1a9fbfull, 0x40538211a128a044ull }, + { 0x401925e353f7ced9ull, 0x405385881cd73bd9ull }, + { 0x401926e978d4fdf4ull, 0x405388ff35e14455ull }, + { 0x401927ef9db22d0eull, 0x40538c76ec62a84bull }, + { 0x401928f5c28f5c29ull, 0x40538fef40775b50ull }, + { 0x401929fbe76c8b44ull, 0x40539368323b55e5ull }, + { 0x40192b020c49ba5eull, 0x405396e1c1ca9582ull }, + { 0x40192c083126e979ull, 0x40539a5bef411ca1ull }, + { 0x40192d0e56041894ull, 0x40539dd6babaf2abull }, + { 0x40192e147ae147aeull, 0x4053a15224542403ull }, + { 0x40192f1a9fbe76c9ull, 0x4053a4ce2c28c210ull }, + { 0x40193020c49ba5e3ull, 0x4053a84ad254e327ull }, + { 0x40193126e978d4feull, 0x4053abc816f4a2a7ull }, + { 0x4019322d0e560419ull, 0x4053af45fa2420e2ull }, + { 0x4019333333333333ull, 0x4053b2c47bff8328ull }, + { 0x401934395810624eull, 0x4053b6439ca2f3d4ull }, + { 0x4019353f7ced9169ull, 0x4053b9c35c2aa233ull }, + { 0x40193645a1cac083ull, 0x4053bd43bab2c296ull }, + { 0x4019374bc6a7ef9eull, 0x4053c0c4b8578e58ull }, + { 0x40193851eb851eb8ull, 0x4053c446553543cbull }, + { 0x4019395810624dd3ull, 0x4053c7c891682650ull }, + { 0x40193a5e353f7ceeull, 0x4053cb4b6d0c7e42ull }, + { 0x40193b645a1cac08ull, 0x4053cecee83e9903ull }, + { 0x40193c6a7ef9db23ull, 0x4053d253031ac905ull }, + { 0x40193d70a3d70a3eull, 0x4053d5d7bdbd65b4ull }, + { 0x40193e76c8b43958ull, 0x4053d95d1842cb89ull }, + { 0x40193f7ced916873ull, 0x4053dce312c75c0aull }, + { 0x40194083126e978dull, 0x4053e069ad677dbeull }, + { 0x40194189374bc6a8ull, 0x4053e3f0e83f9c42ull }, + { 0x4019428f5c28f5c3ull, 0x4053e778c36c2835ull }, + { 0x40194395810624ddull, 0x4053eb013f099740ull }, + { 0x4019449ba5e353f8ull, 0x4053ee8a5b346428ull }, + { 0x401945a1cac08313ull, 0x4053f21418090eb2ull }, + { 0x401946a7ef9db22dull, 0x4053f59e75a41bb3ull }, + { 0x401947ae147ae148ull, 0x4053f9297422151bull }, + { 0x401948b439581062ull, 0x4053fcb5139f89deull }, + { 0x401949ba5e353f7dull, 0x4054004154390e0cull }, + { 0x40194ac083126e98ull, 0x405403ce360b3ac1ull }, + { 0x40194bc6a7ef9db2ull, 0x4054075bb932ae2bull }, + { 0x40194ccccccccccdull, 0x40540ae9ddcc0b97ull }, + { 0x40194dd2f1a9fbe8ull, 0x40540e78a3f3fb5cull }, + { 0x40194ed916872b02ull, 0x405412080bc72ae9ull }, + { 0x40194fdf3b645a1dull, 0x4054159815624cceull }, + { 0x401950e560418937ull, 0x40541928c0e218a4ull }, + { 0x401951eb851eb852ull, 0x40541cba0e634b2cull }, + { 0x401952f1a9fbe76dull, 0x4054204bfe02a637ull }, + { 0x401953f7ced91687ull, 0x405423de8fdcf0afull }, + { 0x401954fdf3b645a2ull, 0x40542771c40ef6a6ull }, + { 0x40195604189374bdull, 0x40542b059ab5893eull }, + { 0x4019570a3d70a3d7ull, 0x40542e9a13ed7eb9ull }, + { 0x40195810624dd2f2ull, 0x4054322f2fd3b280ull }, + { 0x40195916872b020cull, 0x405435c4ee85050full }, + { 0x40195a1cac083127ull, 0x4054395b501e5c0dull }, + { 0x40195b22d0e56042ull, 0x40543cf254bca23cull }, + { 0x40195c28f5c28f5cull, 0x40544089fc7cc77cull }, + { 0x40195d2f1a9fbe77ull, 0x40544422477bc0deull }, + { 0x40195e353f7ced92ull, 0x405447bb35d6888aull }, + { 0x40195f3b645a1cacull, 0x40544b54c7aa1dcdull }, + { 0x4019604189374bc7ull, 0x40544eeefd138526ull }, + { 0x40196147ae147ae1ull, 0x40545289d62fc829ull }, + { 0x4019624dd2f1a9fcull, 0x40545625531bf5a4ull }, + { 0x40196353f7ced917ull, 0x405459c173f5217full }, + { 0x4019645a1cac0831ull, 0x40545d5e38d864cfull }, + { 0x401965604189374cull, 0x405460fba1e2dddaull }, + { 0x4019666666666667ull, 0x40546499af31b009ull }, + { 0x4019676c8b439581ull, 0x4054683860e203f1ull }, + { 0x40196872b020c49cull, 0x40546bd7b711075eull }, + { 0x40196978d4fdf3b6ull, 0x40546f77b1dbed3bull }, + { 0x40196a7ef9db22d1ull, 0x40547318515fedb3ull }, + { 0x40196b851eb851ecull, 0x405476b995ba4613ull }, + { 0x40196c8b43958106ull, 0x40547a5b7f0838daull }, + { 0x40196d916872b021ull, 0x40547dfe0d670dc3ull }, + { 0x40196e978d4fdf3cull, 0x405481a140f411b1ull }, + { 0x40196f9db22d0e56ull, 0x4054854519cc96baull }, + { 0x401970a3d70a3d71ull, 0x405488e9980df435ull }, + { 0x401971a9fbe76c8bull, 0x40548c8ebbd5869dull }, + { 0x401972b020c49ba6ull, 0x405490348540afb4ull }, + { 0x401973b645a1cac1ull, 0x405493daf46cd668ull }, + { 0x401974bc6a7ef9dbull, 0x40549782097766ddull }, + { 0x401975c28f5c28f6ull, 0x40549b29c47dd27eull }, + { 0x401976c8b4395811ull, 0x40549ed2259d8fe3ull }, + { 0x401977ced916872bull, 0x4054a27b2cf41adfull }, + { 0x401978d4fdf3b646ull, 0x4054a624da9ef48cull }, + { 0x401979db22d0e560ull, 0x4054a9cf2ebba333ull }, + { 0x40197ae147ae147bull, 0x4054ad7a2967b268ull }, + { 0x40197be76c8b4396ull, 0x4054b125cac0b2f4ull }, + { 0x40197ced916872b0ull, 0x4054b4d212e43addull }, + { 0x40197df3b645a1cbull, 0x4054b87f01efe577ull }, + { 0x40197ef9db22d0e6ull, 0x4054bc2c9801534cull }, + { 0x4019800000000000ull, 0x4054bfdad5362a27ull }, + { 0x4019810624dd2f1bull, 0x4054c389b9ac1522ull }, + { 0x4019820c49ba5e35ull, 0x4054c7394580c48dull }, + { 0x401983126e978d50ull, 0x4054cae978d1ee0bull }, + { 0x401984189374bc6bull, 0x4054ce9a53bd4c7bull }, + { 0x4019851eb851eb85ull, 0x4054d24bd660a001ull }, + { 0x40198624dd2f1aa0ull, 0x4054d5fe00d9ae17ull }, + { 0x4019872b020c49bbull, 0x4054d9b0d3464172ull }, + { 0x4019883126e978d5ull, 0x4054dd644dc42a11ull }, + { 0x401989374bc6a7f0ull, 0x4054e11870713d4cull }, + { 0x40198a3d70a3d70aull, 0x4054e4cd3b6b55b4ull }, + { 0x40198b4395810625ull, 0x4054e882aed05338ull }, + { 0x40198c49ba5e3540ull, 0x4054ec38cabe1b07ull }, + { 0x40198d4fdf3b645aull, 0x4054efef8f5297a2ull }, + { 0x40198e5604189375ull, 0x4054f3a6fcabb8e1ull }, + { 0x40198f5c28f5c290ull, 0x4054f75f12e773e3ull }, + { 0x401990624dd2f1aaull, 0x4054fb17d223c319ull }, + { 0x4019916872b020c5ull, 0x4054fed13a7ea64full }, + { 0x4019926e978d4fdfull, 0x4055028b4c162299ull }, + { 0x40199374bc6a7efaull, 0x405506460708426aull }, + { 0x4019947ae147ae15ull, 0x40550a016b731582ull }, + { 0x401995810624dd2full, 0x40550dbd7974b0f6ull }, + { 0x401996872b020c4aull, 0x4055117a312b2f3cull }, + { 0x4019978d4fdf3b65ull, 0x4055153792b4b019ull }, + { 0x4019989374bc6a7full, 0x405518f59e2f58a9ull }, + { 0x401999999999999aull, 0x40551cb453b9536eull }, + { 0x40199a9fbe76c8b4ull, 0x40552073b370d036ull }, + { 0x40199ba5e353f7cfull, 0x40552433bd740438ull }, + { 0x40199cac083126eaull, 0x405527f471e12a00ull }, + { 0x40199db22d0e5604ull, 0x40552bb5d0d68174ull }, + { 0x40199eb851eb851full, 0x40552f77da724fe6ull }, + { 0x40199fbe76c8b439ull, 0x4055333a8ed2dff7ull }, + { 0x4019a0c49ba5e354ull, 0x405536fdee1681baull }, + { 0x4019a1cac083126full, 0x40553ac1f85b8a94ull }, + { 0x4019a2d0e5604189ull, 0x40553e86adc05552ull }, + { 0x4019a3d70a3d70a4ull, 0x4055424c0e63422aull }, + { 0x4019a4dd2f1a9fbfull, 0x405546121a62b6b0ull }, + { 0x4019a5e353f7ced9ull, 0x405549d8d1dd1dd9ull }, + { 0x4019a6e978d4fdf4ull, 0x40554da034f0e80full }, + { 0x4019a7ef9db22d0eull, 0x4055516843bc8b12ull }, + { 0x4019a8f5c28f5c29ull, 0x40555530fe5e821aull }, + { 0x4019a9fbe76c8b44ull, 0x405558fa64f54dbcull }, + { 0x4019ab020c49ba5eull, 0x40555cc4779f73f9ull }, + { 0x4019ac083126e979ull, 0x4055608f367b8047ull }, + { 0x4019ad0e56041894ull, 0x4055645aa1a8037cull }, + { 0x4019ae147ae147aeull, 0x40556826b94393ddull }, + { 0x4019af1a9fbe76c9ull, 0x40556bf37d6ccd26ull }, + { 0x4019b020c49ba5e3ull, 0x40556fc0ee425074ull }, + { 0x4019b126e978d4feull, 0x4055738f0be2c463ull }, + { 0x4019b22d0e560419ull, 0x4055775dd66cd4f5ull }, + { 0x4019b33333333333ull, 0x40557b2d4dff339cull }, + { 0x4019b4395810624eull, 0x40557efd72b8974aull }, + { 0x4019b53f7ced9169ull, 0x405582ce44b7bc58ull }, + { 0x4019b645a1cac083ull, 0x4055869fc41b6494ull }, + { 0x4019b74bc6a7ef9eull, 0x40558a71f102574full }, + { 0x4019b851eb851eb8ull, 0x40558e44cb8b613eull }, + { 0x4019b95810624dd3ull, 0x4055921853d5549full }, + { 0x4019ba5e353f7ceeull, 0x405595ec89ff091dull }, + { 0x4019bb645a1cac08ull, 0x405599c16e275bdcull }, + { 0x4019bc6a7ef9db23ull, 0x40559d97006d2f87ull }, + { 0x4019bd70a3d70a3eull, 0x4055a16d40ef6c36ull }, + { 0x4019be76c8b43958ull, 0x4055a5442fccff82ull }, + { 0x4019bf7ced916873ull, 0x4055a91bcd24dc8aull }, + { 0x4019c083126e978dull, 0x4055acf41915fbddull }, + { 0x4019c189374bc6a8ull, 0x4055b0cd13bf5b97ull }, + { 0x4019c28f5c28f5c3ull, 0x4055b4a6bd3fff4bull }, + { 0x4019c395810624ddull, 0x4055b88115b6f00dull }, + { 0x4019c49ba5e353f8ull, 0x4055bc5c1d433c7eull }, + { 0x4019c5a1cac08313ull, 0x4055c037d403f8b7ull }, + { 0x4019c6a7ef9db22dull, 0x4055c4143a183e57ull }, + { 0x4019c7ae147ae148ull, 0x4055c7f14f9f2c89ull }, + { 0x4019c8b439581062ull, 0x4055cbcf14b7e7f5ull }, + { 0x4019c9ba5e353f7dull, 0x4055cfad89819ad6ull }, + { 0x4019cac083126e98ull, 0x4055d38cae1b74e4ull }, + { 0x4019cbc6a7ef9db2ull, 0x4055d76c82a4ab60ull }, + { 0x4019cccccccccccdull, 0x4055db4d073c7921ull }, + { 0x4019cdd2f1a9fbe8ull, 0x4055df2e3c021e7full }, + { 0x4019ced916872b02ull, 0x4055e3102114e15cull }, + { 0x4019cfdf3b645a1dull, 0x4055e6f2b6940d34ull }, + { 0x4019d0e560418937ull, 0x4055ead5fc9ef301ull }, + { 0x4019d1eb851eb852ull, 0x4055eeb9f354e95cull }, + { 0x4019d2f1a9fbe76dull, 0x4055f29e9ad54c61ull }, + { 0x4019d3f7ced91687ull, 0x4055f683f33f7dc0ull }, + { 0x4019d4fdf3b645a2ull, 0x4055fa69fcb2e4c4ull }, + { 0x4019d604189374bdull, 0x4055fe50b74eee40ull }, + { 0x4019d70a3d70a3d7ull, 0x4056023823330c9cull }, + { 0x4019d810624dd2f2ull, 0x40560620407eb7e0ull }, + { 0x4019d916872b020cull, 0x40560a090f516d9aull }, + { 0x4019da1cac083127ull, 0x40560df28fcab0ffull }, + { 0x4019db22d0e56042ull, 0x405611dcc20a0ad0ull }, + { 0x4019dc28f5c28f5cull, 0x405615c7a62f0968ull }, + { 0x4019dd2f1a9fbe77ull, 0x405619b33c5940c6ull }, + { 0x4019de353f7ced92ull, 0x40561d9f84a84a7aull }, + { 0x4019df3b645a1cacull, 0x4056218c7f3bc5afull }, + { 0x4019e04189374bc7ull, 0x4056257a2c335739ull }, + { 0x4019e147ae147ae1ull, 0x405629688baea979ull }, + { 0x4019e24dd2f1a9fcull, 0x40562d579dcd6c81ull }, + { 0x4019e353f7ced917ull, 0x4056314762af55f6ull }, + { 0x4019e45a1cac0831ull, 0x40563537da74211dull }, + { 0x4019e5604189374cull, 0x40563929053b8ee9ull }, + { 0x4019e66666666667ull, 0x40563d1ae32565e6ull }, + { 0x4019e76c8b439581ull, 0x4056410d74517243ull }, + { 0x4019e872b020c49cull, 0x40564500b8df85deull }, + { 0x4019e978d4fdf3b6ull, 0x405648f4b0ef782eull }, + { 0x4019ea7ef9db22d1ull, 0x40564ce95ca1265eull }, + { 0x4019eb851eb851ecull, 0x405650debc147337ull }, + { 0x4019ec8b43958106ull, 0x405654d4cf69472bull }, + { 0x4019ed916872b021ull, 0x405658cb96bf9060ull }, + { 0x4019ee978d4fdf3cull, 0x40565cc31237429eull }, + { 0x4019ef9db22d0e56ull, 0x405660bb41f05756ull }, + { 0x4019f0a3d70a3d71ull, 0x405664b4260acdb3ull }, + { 0x4019f1a9fbe76c8bull, 0x405668adbea6aa7full }, + { 0x4019f2b020c49ba6ull, 0x40566ca80be3f842ull }, + { 0x4019f3b645a1cac1ull, 0x405670a30de2c727ull }, + { 0x4019f4bc6a7ef9dbull, 0x4056749ec4c32d0eull }, + { 0x4019f5c28f5c28f6ull, 0x4056789b30a54590ull }, + { 0x4019f6c8b4395811ull, 0x40567c9851a931efull }, + { 0x4019f7ced916872bull, 0x4056809627ef1923ull }, + { 0x4019f8d4fdf3b646ull, 0x40568494b39727e3ull }, + { 0x4019f9db22d0e560ull, 0x40568893f4c1908bull }, + { 0x4019fae147ae147bull, 0x40568c93eb8e8b41ull }, + { 0x4019fbe76c8b4396ull, 0x40569094981e55d5ull }, + { 0x4019fced916872b0ull, 0x40569495fa9133d1ull }, + { 0x4019fdf3b645a1cbull, 0x4056989813076e85ull }, + { 0x4019fef9db22d0e6ull, 0x40569c9ae1a154f0ull }, + { 0x401a000000000000ull, 0x4056a09e667f3bcdull }, + { 0x401a010624dd2f1bull, 0x4056a4a2a1c17da0ull }, + { 0x401a020c49ba5e35ull, 0x4056a8a793887a9cull }, + { 0x401a03126e978d50ull, 0x4056acad3bf498c2ull }, + { 0x401a04189374bc6bull, 0x4056b0b39b2643caull }, + { 0x401a051eb851eb85ull, 0x4056b4bab13ded2aull }, + { 0x401a0624dd2f1aa0ull, 0x4056b8c27e5c0c27ull }, + { 0x401a072b020c49bbull, 0x4056bccb02a11dbfull }, + { 0x401a083126e978d5ull, 0x4056c0d43e2da4b2ull }, + { 0x401a09374bc6a7f0ull, 0x4056c4de31222994ull }, + { 0x401a0a3d70a3d70aull, 0x4056c8e8db9f3aadull }, + { 0x401a0b4395810625ull, 0x4056ccf43dc56c1eull }, + { 0x401a0c49ba5e3540ull, 0x4056d10057b557c3ull }, + { 0x401a0d4fdf3b645aull, 0x4056d50d298f9d43ull }, + { 0x401a0e5604189375ull, 0x4056d91ab374e219ull }, + { 0x401a0f5c28f5c290ull, 0x4056dd28f585d184ull }, + { 0x401a10624dd2f1aaull, 0x4056e137efe31c8aull }, + { 0x401a116872b020c5ull, 0x4056e547a2ad7a0full }, + { 0x401a126e978d4fdfull, 0x4056e9580e05a6b4ull }, + { 0x401a1374bc6a7efaull, 0x4056ed69320c64f8ull }, + { 0x401a147ae147ae15ull, 0x4056f17b0ee27d21ull }, + { 0x401a15810624dd2full, 0x4056f58da4a8bd47ull }, + { 0x401a16872b020c4aull, 0x4056f9a0f37ff95eull }, + { 0x401a178d4fdf3b65ull, 0x4056fdb4fb890b24ull }, + { 0x401a189374bc6a7full, 0x405701c9bce4d22cull }, + { 0x401a19999999999aull, 0x405705df37b433e8ull }, + { 0x401a1a9fbe76c8b4ull, 0x405709f56c181b95ull }, + { 0x401a1ba5e353f7cfull, 0x40570e0c5a317a54ull }, + { 0x401a1cac083126eaull, 0x4057122402214716ull }, + { 0x401a1db22d0e5604ull, 0x4057163c64087ea4ull }, + { 0x401a1eb851eb851full, 0x40571a55800823afull }, + { 0x401a1fbe76c8b439ull, 0x40571e6f56413eb3ull }, + { 0x401a20c49ba5e354ull, 0x40572289e6d4de1aull }, + { 0x401a21cac083126full, 0x405726a531e41621ull }, + { 0x401a22d0e5604189ull, 0x40572ac1379000e2ull }, + { 0x401a23d70a3d70a4ull, 0x40572eddf7f9be65ull }, + { 0x401a24dd2f1a9fbfull, 0x405732fb73427486ull }, + { 0x401a25e353f7ced9ull, 0x40573719a98b4f04ull }, + { 0x401a26e978d4fdf4ull, 0x40573b389af57f8eull }, + { 0x401a27ef9db22d0eull, 0x40573f5847a23da6ull }, + { 0x401a28f5c28f5c29ull, 0x40574378afb2c6c5ull }, + { 0x401a29fbe76c8b44ull, 0x40574799d3485e3cull }, + { 0x401a2b020c49ba5eull, 0x40574bbbb2844d46ull }, + { 0x401a2c083126e979ull, 0x40574fde4d87e312ull }, + { 0x401a2d0e56041894ull, 0x40575401a47474acull }, + { 0x401a2e147ae147aeull, 0x40575825b76b5d0aull }, + { 0x401a2f1a9fbe76c9ull, 0x40575c4a868dfd1dull }, + { 0x401a3020c49ba5e3ull, 0x4057607011fdbbaeull }, + { 0x401a3126e978d4feull, 0x4057649659dc0588ull }, + { 0x401a322d0e560419ull, 0x405768bd5e4a4d57ull }, + { 0x401a333333333333ull, 0x40576ce51f6a0bb7ull }, + { 0x401a34395810624eull, 0x4057710d9d5cbf41ull }, + { 0x401a353f7ced9169ull, 0x40577536d843ec75ull }, + { 0x401a3645a1cac083ull, 0x40577960d0411dc4ull }, + { 0x401a374bc6a7ef9eull, 0x40577d8b8575e3a1ull }, + { 0x401a3851eb851eb8ull, 0x405781b6f803d464ull }, + { 0x401a395810624dd3ull, 0x405785e3280c8c6bull }, + { 0x401a3a5e353f7ceeull, 0x40578a1015b1adffull }, + { 0x401a3b645a1cac08ull, 0x40578e3dc114e162ull }, + { 0x401a3c6a7ef9db23ull, 0x4057926c2a57d4daull }, + { 0x401a3d70a3d70a3eull, 0x4057969b519c3c9full }, + { 0x401a3e76c8b43958ull, 0x40579acb3703d2dfull }, + { 0x401a3f7ced916873ull, 0x40579efbdab057d7ull }, + { 0x401a4083126e978dull, 0x4057a32d3cc391adull }, + { 0x401a4189374bc6a8ull, 0x4057a75f5d5f4c97ull }, + { 0x401a428f5c28f5c3ull, 0x4057ab923ca55ac0ull }, + { 0x401a4395810624ddull, 0x4057afc5dab79453ull }, + { 0x401a449ba5e353f8ull, 0x4057b3fa37b7d788ull }, + { 0x401a45a1cac08313ull, 0x4057b82f53c80890ull }, + { 0x401a46a7ef9db22dull, 0x4057bc652f0a119full }, + { 0x401a47ae147ae148ull, 0x4057c09bc99fe2f9ull }, + { 0x401a48b439581062ull, 0x4057c4d323ab72dbull }, + { 0x401a49ba5e353f7dull, 0x4057c90b3d4ebd97ull }, + { 0x401a4ac083126e98ull, 0x4057cd4416abc57dull }, + { 0x401a4bc6a7ef9db2ull, 0x4057d17dafe492e4ull }, + { 0x401a4ccccccccccdull, 0x4057d5b8091b343dull }, + { 0x401a4dd2f1a9fbe8ull, 0x4057d9f32271bdf5ull }, + { 0x401a4ed916872b02ull, 0x4057de2efc0a4a87ull }, + { 0x401a4fdf3b645a1dull, 0x4057e26b9606fa8aull }, + { 0x401a50e560418937ull, 0x4057e6a8f089f491ull }, + { 0x401a51eb851eb852ull, 0x4057eae70bb5654full }, + { 0x401a52f1a9fbe76dull, 0x4057ef25e7ab7f7dull }, + { 0x401a53f7ced91687ull, 0x4057f365848e7be6ull }, + { 0x401a54fdf3b645a2ull, 0x4057f7a5e2809974ull }, + { 0x401a5604189374bdull, 0x4057fbe701a41d18ull }, + { 0x401a570a3d70a3d7ull, 0x40580028e21b51daull }, + { 0x401a5810624dd2f2ull, 0x4058046b840888e3ull }, + { 0x401a5916872b020cull, 0x405808aee78e1965ull }, + { 0x401a5a1cac083127ull, 0x40580cf30cce60b9ull }, + { 0x401a5b22d0e56042ull, 0x40581137f3ebc245ull }, + { 0x401a5c28f5c28f5cull, 0x4058157d9d08a78dull }, + { 0x401a5d2f1a9fbe77ull, 0x405819c408478039ull }, + { 0x401a5e353f7ced92ull, 0x40581e0b35cac203ull }, + { 0x401a5f3b645a1cacull, 0x4058225325b4e8c5ull }, + { 0x401a604189374bc7ull, 0x4058269bd8287680ull }, + { 0x401a6147ae147ae1ull, 0x40582ae54d47f349ull }, + { 0x401a624dd2f1a9fcull, 0x40582f2f8535ed65ull }, + { 0x401a6353f7ced917ull, 0x4058337a8014f92eull }, + { 0x401a645a1cac0831ull, 0x405837c63e07b121ull }, + { 0x401a65604189374cull, 0x40583c12bf30b5efull }, + { 0x401a666666666667ull, 0x4058406003b2ae5full }, + { 0x401a676c8b439581ull, 0x405844ae0bb0475full }, + { 0x401a6872b020c49cull, 0x405848fcd74c3414ull }, + { 0x401a6978d4fdf3b6ull, 0x40584d4c66a92db6ull }, + { 0x401a6a7ef9db22d1ull, 0x4058519cb9e9f3bdull }, + { 0x401a6b851eb851ecull, 0x405855edd1314bbdull }, + { 0x401a6c8b43958106ull, 0x40585a3faca20175ull }, + { 0x401a6d916872b021ull, 0x40585e924c5ee6dfull }, + { 0x401a6e978d4fdf3cull, 0x405862e5b08ad417ull }, + { 0x401a6f9db22d0e56ull, 0x40586739d948a769ull }, + { 0x401a70a3d70a3d71ull, 0x40586b8ec6bb455cull }, + { 0x401a71a9fbe76c8bull, 0x40586fe479059898ull }, + { 0x401a72b020c49ba6ull, 0x4058743af04a920cull }, + { 0x401a73b645a1cac1ull, 0x405878922cad28c9ull }, + { 0x401a74bc6a7ef9dbull, 0x40587cea2e505a19ull }, + { 0x401a75c28f5c28f6ull, 0x40588142f5572987ull }, + { 0x401a76c8b4395811ull, 0x4058859c81e4a0c7ull }, + { 0x401a77ced916872bull, 0x405889f6d41bcfc9ull }, + { 0x401a78d4fdf3b646ull, 0x40588e51ec1fccbfull }, + { 0x401a79db22d0e560ull, 0x405892adca13b406ull }, + { 0x401a7ae147ae147bull, 0x4058970a6e1aa848ull }, + { 0x401a7be76c8b4396ull, 0x40589b67d857d25full }, + { 0x401a7ced916872b0ull, 0x40589fc608ee6162ull }, + { 0x401a7df3b645a1cbull, 0x4058a42500018ab4ull }, + { 0x401a7ef9db22d0e6ull, 0x4058a884bdb489eaull }, + { 0x401a800000000000ull, 0x4058ace5422aa0dbull }, + { 0x401a810624dd2f1bull, 0x4058b1468d8717aeull }, + { 0x401a820c49ba5e35ull, 0x4058b5a89fed3cbbull }, + { 0x401a83126e978d50ull, 0x4058ba0b798064aeull }, + { 0x401a84189374bc6bull, 0x4058be6f1a63ea6full }, + { 0x401a851eb851eb85ull, 0x4058c2d382bb2f2bull }, + { 0x401a8624dd2f1aa0ull, 0x4058c738b2a99a62ull }, + { 0x401a872b020c49bbull, 0x4058cb9eaa5299d2ull }, + { 0x401a883126e978d5ull, 0x4058d00569d9a182ull }, + { 0x401a89374bc6a7f0ull, 0x4058d46cf1622bd2ull }, + { 0x401a8a3d70a3d70aull, 0x4058d8d5410fb95cull }, + { 0x401a8b4395810625ull, 0x4058dd3e5905d119ull }, + { 0x401a8c49ba5e3540ull, 0x4058e1a839680043ull }, + { 0x401a8d4fdf3b645aull, 0x4058e612e259da63ull }, + { 0x401a8e5604189375ull, 0x4058ea7e53fef962ull }, + { 0x401a8f5c28f5c290ull, 0x4058eeea8e7afd6bull }, + { 0x401a90624dd2f1aaull, 0x4058f35791f18cfeull }, + { 0x401a916872b020c5ull, 0x4058f7c55e8654fbull }, + { 0x401a926e978d4fdfull, 0x4058fc33f45d0885ull }, + { 0x401a9374bc6a7efaull, 0x405900a353996129ull }, + { 0x401a947ae147ae15ull, 0x405905137c5f1ebcull }, + { 0x401a95810624dd2full, 0x405909846ed2076cull }, + { 0x401a96872b020c4aull, 0x40590df62b15e7cfull }, + { 0x401a978d4fdf3b65ull, 0x40591268b14e92c8ull }, + { 0x401a989374bc6a7full, 0x405916dc019fe196ull }, + { 0x401a99999999999aull, 0x40591b501c2db3e0ull }, + { 0x401a9a9fbe76c8b4ull, 0x40591fc5011bef9eull }, + { 0x401a9ba5e353f7cfull, 0x4059243ab08e8135ull }, + { 0x401a9cac083126eaull, 0x405928b12aa95b5full }, + { 0x401a9db22d0e5604ull, 0x40592d286f907737ull }, + { 0x401a9eb851eb851full, 0x405931a07f67d448ull }, + { 0x401a9fbe76c8b43aull, 0x405936195a537874ull }, + { 0x401aa0c49ba5e354ull, 0x40593a9300777002ull }, + { 0x401aa1cac083126full, 0x40593f0d71f7cdabull }, + { 0x401aa2d0e5604189ull, 0x40594388aef8aa7full }, + { 0x401aa3d70a3d70a4ull, 0x40594804b79e2607ull }, + { 0x401aa4dd2f1a9fbfull, 0x40594c818c0c662aull }, + { 0x401aa5e353f7ced9ull, 0x405950ff2c679737ull }, + { 0x401aa6e978d4fdf4ull, 0x4059557d98d3ebf9ull }, + { 0x401aa7ef9db22d0eull, 0x405959fcd1759d93ull }, + { 0x401aa8f5c28f5c29ull, 0x40595e7cd670ebabull }, + { 0x401aa9fbe76c8b44ull, 0x405962fda7ea1c47ull }, + { 0x401aab020c49ba5eull, 0x4059677f46057bddull }, + { 0x401aac083126e979ull, 0x40596c01b0e75d62ull }, + { 0x401aad0e56041894ull, 0x40597084e8b41a33ull }, + { 0x401aae147ae147aeull, 0x40597508ed90121cull }, + { 0x401aaf1a9fbe76c9ull, 0x4059798dbf9fab70ull }, + { 0x401ab020c49ba5e3ull, 0x40597e135f0752e3ull }, + { 0x401ab126e978d4feull, 0x40598299cbeb7bb3ull }, + { 0x401ab22d0e560419ull, 0x4059872106709f89ull }, + { 0x401ab33333333333ull, 0x40598ba90ebb3e89ull }, + { 0x401ab4395810624eull, 0x40599031e4efdf5bull }, + { 0x401ab53f7ced9169ull, 0x405994bb89330f1bull }, + { 0x401ab645a1cac083ull, 0x40599945fba9615dull }, + { 0x401ab74bc6a7ef9eull, 0x40599dd13c777043ull }, + { 0x401ab851eb851eb8ull, 0x4059a25d4bc1dc5cull }, + { 0x401ab95810624dd3ull, 0x4059a6ea29ad4ccaull }, + { 0x401aba5e353f7ceeull, 0x4059ab77d65e6f21ull }, + { 0x401abb645a1cac08ull, 0x4059b00651f9f779ull }, + { 0x401abc6a7ef9db23ull, 0x4059b4959ca4a07dull }, + { 0x401abd70a3d70a3eull, 0x4059b925b6832b4cull }, + { 0x401abe76c8b43958ull, 0x4059bdb69fba5f8full }, + { 0x401abf7ced916873ull, 0x4059c248586f0b80ull }, + { 0x401ac083126e978dull, 0x4059c6dae0c603d4ull }, + { 0x401ac189374bc6a8ull, 0x4059cb6e38e423d7ull }, + { 0x401ac28f5c28f5c3ull, 0x4059d00260ee4d55ull }, + { 0x401ac395810624ddull, 0x4059d497590968a6ull }, + { 0x401ac49ba5e353f8ull, 0x4059d92d215a64bbull }, + { 0x401ac5a1cac08313ull, 0x4059ddc3ba063709ull }, + { 0x401ac6a7ef9db22dull, 0x4059e25b2331db91ull }, + { 0x401ac7ae147ae148ull, 0x4059e6f35d0254f3ull }, + { 0x401ac8b439581062ull, 0x4059eb8c679cac51ull }, + { 0x401ac9ba5e353f7dull, 0x4059f0264325f16full }, + { 0x401acac083126e98ull, 0x4059f4c0efc33a9aull }, + { 0x401acbc6a7ef9db2ull, 0x4059f95c6d99a4b3ull }, + { 0x401acccccccccccdull, 0x4059fdf8bcce533eull }, + { 0x401acdd2f1a9fbe8ull, 0x405a0295dd86704dull }, + { 0x401aced916872b02ull, 0x405a0733cfe72c86ull }, + { 0x401acfdf3b645a1dull, 0x405a0bd29415bf39ull }, + { 0x401ad0e560418937ull, 0x405a10722a376640ull }, + { 0x401ad1eb851eb852ull, 0x405a151292716622ull }, + { 0x401ad2f1a9fbe76dull, 0x405a19b3cce909f5ull }, + { 0x401ad3f7ced91687ull, 0x405a1e55d9c3a370ull }, + { 0x401ad4fdf3b645a2ull, 0x405a22f8b9268af6ull }, + { 0x401ad604189374bdull, 0x405a279c6b371f7eull }, + { 0x401ad70a3d70a3d7ull, 0x405a2c40f01ac6a1ull }, + { 0x401ad810624dd2f2ull, 0x405a30e647f6eca9ull }, + { 0x401ad916872b020cull, 0x405a358c72f10472ull }, + { 0x401ada1cac083127ull, 0x405a3a33712e8790ull }, + { 0x401adb22d0e56042ull, 0x405a3edb42d4f631ull }, + { 0x401adc28f5c28f5cull, 0x405a4383e809d72bull }, + { 0x401add2f1a9fbe77ull, 0x405a482d60f2b80aull }, + { 0x401ade353f7ced92ull, 0x405a4cd7adb52cf8ull }, + { 0x401adf3b645a1cacull, 0x405a5182ce76d0caull }, + { 0x401ae04189374bc7ull, 0x405a562ec35d450full }, + { 0x401ae147ae147ae1ull, 0x405a5adb8c8e31f3ull }, + { 0x401ae24dd2f1a9fcull, 0x405a5f892a2f4662ull }, + { 0x401ae353f7ced917ull, 0x405a64379c6637edull }, + { 0x401ae45a1cac0831ull, 0x405a68e6e358c2d4ull }, + { 0x401ae5604189374cull, 0x405a6d96ff2caa18ull }, + { 0x401ae66666666667ull, 0x405a7247f007b762ull }, + { 0x401ae76c8b439581ull, 0x405a76f9b60fbb0full }, + { 0x401ae872b020c49cull, 0x405a7bac516a8c3full }, + { 0x401ae978d4fdf3b6ull, 0x405a805fc23e08b9ull }, + { 0x401aea7ef9db22d1ull, 0x405a851408b0150eull }, + { 0x401aeb851eb851ecull, 0x405a89c924e69c7bull }, + { 0x401aec8b43958106ull, 0x405a8e7f170790faull }, + { 0x401aed916872b021ull, 0x405a9335df38eb4cull }, + { 0x401aee978d4fdf3cull, 0x405a97ed7da0aae5ull }, + { 0x401aef9db22d0e56ull, 0x405a9ca5f264d5f4ull }, + { 0x401af0a3d70a3d71ull, 0x405aa15f3dab797aull }, + { 0x401af1a9fbe76c8bull, 0x405aa6195f9aa922ull }, + { 0x401af2b020c49ba6ull, 0x405aaad458587f70ull }, + { 0x401af3b645a1cac1ull, 0x405aaf90280b1d9cull }, + { 0x401af4bc6a7ef9dbull, 0x405ab44cced8aba0ull }, + { 0x401af5c28f5c28f6ull, 0x405ab90a4ce7584full }, + { 0x401af6c8b4395811ull, 0x405abdc8a25d5931ull }, + { 0x401af7ced916872bull, 0x405ac287cf60ea98ull }, + { 0x401af8d4fdf3b646ull, 0x405ac747d4184faeull }, + { 0x401af9db22d0e560ull, 0x405acc08b0a9d252ull }, + { 0x401afae147ae147bull, 0x405ad0ca653bc346ull }, + { 0x401afbe76c8b4396ull, 0x405ad58cf1f47a09ull }, + { 0x401afced916872b0ull, 0x405ada5056fa54e6ull }, + { 0x401afdf3b645a1cbull, 0x405adf149473b90aull }, + { 0x401afef9db22d0e6ull, 0x405ae3d9aa871262ull }, + { 0x401b000000000000ull, 0x405ae89f995ad3adull }, + { 0x401b010624dd2f1bull, 0x405aed666115768cull }, + { 0x401b020c49ba5e35ull, 0x405af22e01dd7b62ull }, + { 0x401b03126e978d50ull, 0x405af6f67bd96978ull }, + { 0x401b04189374bc6bull, 0x405afbbfcf2fcee3ull }, + { 0x401b051eb851eb85ull, 0x405b0089fc07408dull }, + { 0x401b0624dd2f1aa0ull, 0x405b055502865a4aull }, + { 0x401b072b020c49bbull, 0x405b0a20e2d3beb8ull }, + { 0x401b083126e978d5ull, 0x405b0eed9d161753ull }, + { 0x401b09374bc6a7f0ull, 0x405b13bb3174147full }, + { 0x401b0a3d70a3d70aull, 0x405b1889a0146d6eull }, + { 0x401b0b4395810625ull, 0x405b1d58e91de043ull }, + { 0x401b0c49ba5e3540ull, 0x405b22290cb731f4ull }, + { 0x401b0d4fdf3b645aull, 0x405b26fa0b072e57ull }, + { 0x401b0e5604189375ull, 0x405b2bcbe434a836ull }, + { 0x401b0f5c28f5c290ull, 0x405b309e9866792eull }, + { 0x401b10624dd2f1aaull, 0x405b357227c381c4ull }, + { 0x401b116872b020c5ull, 0x405b3a469272a96full }, + { 0x401b126e978d4fdfull, 0x405b3f1bd89ade7full }, + { 0x401b1374bc6a7efaull, 0x405b43f1fa63163cull }, + { 0x401b147ae147ae15ull, 0x405b48c8f7f24ccdull }, + { 0x401b15810624dd2full, 0x405b4da0d16f8542ull }, + { 0x401b16872b020c4aull, 0x405b52798701c9a8ull }, + { 0x401b178d4fdf3b65ull, 0x405b575318d02aebull }, + { 0x401b189374bc6a7full, 0x405b5c2d8701c0e6ull }, + { 0x401b19999999999aull, 0x405b6108d1bdaa74ull }, + { 0x401b1a9fbe76c8b4ull, 0x405b65e4f92b0d4eull }, + { 0x401b1ba5e353f7cfull, 0x405b6ac1fd711631ull }, + { 0x401b1cac083126eaull, 0x405b6f9fdeb6f8c3ull }, + { 0x401b1db22d0e5604ull, 0x405b747e9d23ef9dull }, + { 0x401b1eb851eb851full, 0x405b795e38df3c60ull }, + { 0x401b1fbe76c8b43aull, 0x405b7e3eb2102792ull }, + { 0x401b20c49ba5e354ull, 0x405b832008de00b6ull }, + { 0x401b21cac083126full, 0x405b88023d701e56ull }, + { 0x401b22d0e5604189ull, 0x405b8ce54feddde5ull }, + { 0x401b23d70a3d70a4ull, 0x405b91c9407ea3e5ull }, + { 0x401b24dd2f1a9fbfull, 0x405b96ae0f49dbcaull }, + { 0x401b25e353f7ced9ull, 0x405b9b93bc76f804ull }, + { 0x401b26e978d4fdf4ull, 0x405ba07a482d7214ull }, + { 0x401b27ef9db22d0eull, 0x405ba561b294ca6bull }, + { 0x401b28f5c28f5c29ull, 0x405baa49fbd4888cull }, + { 0x401b29fbe76c8b44ull, 0x405baf3324143af4ull }, + { 0x401b2b020c49ba5eull, 0x405bb41d2b7b7724ull }, + { 0x401b2c083126e979ull, 0x405bb9081231d9b1ull }, + { 0x401b2d0e56041894ull, 0x405bbdf3d85f062cull }, + { 0x401b2e147ae147aeull, 0x405bc2e07e2aa72dull }, + { 0x401b2f1a9fbe76c9ull, 0x405bc7ce03bc6e66ull }, + { 0x401b3020c49ba5e3ull, 0x405bccbc693c1482ull }, + { 0x401b3126e978d4feull, 0x405bd1abaed1594bull }, + { 0x401b322d0e560419ull, 0x405bd69bd4a4038cull }, + { 0x401b333333333333ull, 0x405bdb8cdadbe11full }, + { 0x401b34395810624eull, 0x405be07ec1a0c6feull }, + { 0x401b353f7ced9169ull, 0x405be571891a9124ull }, + { 0x401b3645a1cac083ull, 0x405bea65317122a3ull }, + { 0x401b374bc6a7ef9eull, 0x405bef59bacc65aeull }, + { 0x401b3851eb851eb8ull, 0x405bf44f25544b79ull }, + { 0x401b395810624dd3ull, 0x405bf9457130cc66ull }, + { 0x401b3a5e353f7ceeull, 0x405bfe3c9e89e7deull }, + { 0x401b3b645a1cac08ull, 0x405c0334ad87a464ull }, + { 0x401b3c6a7ef9db23ull, 0x405c082d9e520fa5ull }, + { 0x401b3d70a3d70a3eull, 0x405c0d2771113e5cull }, + { 0x401b3e76c8b43958ull, 0x405c122225ed4c60ull }, + { 0x401b3f7ced916873ull, 0x405c171dbd0e5cb5ull }, + { 0x401b4083126e978dull, 0x405c1c1a369c996full }, + { 0x401b4189374bc6a8ull, 0x405c211792c033d1ull }, + { 0x401b428f5c28f5c3ull, 0x405c2615d1a16437ull }, + { 0x401b4395810624ddull, 0x405c2b14f3686a1dull }, + { 0x401b449ba5e353f8ull, 0x405c3014f83d8c34ull }, + { 0x401b45a1cac08313ull, 0x405c3515e0491845ull }, + { 0x401b46a7ef9db22dull, 0x405c3a17abb36340ull }, + { 0x401b47ae147ae148ull, 0x405c3f1a5aa4c94dull }, + { 0x401b48b439581062ull, 0x405c441ded45ada9ull }, + { 0x401b49ba5e353f7dull, 0x405c492263be7ad0ull }, + { 0x401b4ac083126e98ull, 0x405c4e27be37a25full }, + { 0x401b4bc6a7ef9db2ull, 0x405c532dfcd99d1dull }, + { 0x401b4ccccccccccdull, 0x405c58351fcceb11ull }, + { 0x401b4dd2f1a9fbe8ull, 0x405c5d3d273a1362ull }, + { 0x401b4ed916872b02ull, 0x405c62461349a46aull }, + { 0x401b4fdf3b645a1dull, 0x405c674fe42433c6ull }, + { 0x401b50e560418937ull, 0x405c6c5a99f25e2full }, + { 0x401b51eb851eb852ull, 0x405c716634dcc7adull }, + { 0x401b52f1a9fbe76dull, 0x405c7672b50c1b6bull }, + { 0x401b53f7ced91687ull, 0x405c7b801aa90bcfull }, + { 0x401b54fdf3b645a2ull, 0x405c808e65dc5286ull }, + { 0x401b5604189374bdull, 0x405c859d96ceb06aull }, + { 0x401b570a3d70a3d7ull, 0x405c8aadada8ed8full }, + { 0x401b5810624dd2f2ull, 0x405c8fbeaa93d957ull }, + { 0x401b5916872b020cull, 0x405c94d08db84a4dull }, + { 0x401b5a1cac083127ull, 0x405c99e3573f1e50ull }, + { 0x401b5b22d0e56042ull, 0x405c9ef707513a73ull }, + { 0x401b5c28f5c28f5cull, 0x405ca40b9e178b08ull }, + { 0x401b5d2f1a9fbe77ull, 0x405ca9211bbb03b7ull }, + { 0x401b5e353f7ced92ull, 0x405cae3780649f5bull }, + { 0x401b5f3b645a1cacull, 0x405cb34ecc3d6017ull }, + { 0x401b604189374bc7ull, 0x405cb866ff6e4f65ull }, + { 0x401b6147ae147ae1ull, 0x405cbd801a207df2ull }, + { 0x401b624dd2f1a9fcull, 0x405cc29a1c7d03caull }, + { 0x401b6353f7ced917ull, 0x405cc7b506ad0035ull }, + { 0x401b645a1cac0831ull, 0x405cccd0d8d999c8ull }, + { 0x401b65604189374cull, 0x405cd1ed932bfe76ull }, + { 0x401b666666666667ull, 0x405cd70b35cd636full }, + { 0x401b676c8b439581ull, 0x405cdc29c0e70539ull }, + { 0x401b6872b020c49cull, 0x405ce14934a227b4ull }, + { 0x401b6978d4fdf3b6ull, 0x405ce66991281606ull }, + { 0x401b6a7ef9db22d1ull, 0x405ceb8ad6a222b8ull }, + { 0x401b6b851eb851ecull, 0x405cf0ad0539a79dull }, + { 0x401b6c8b43958106ull, 0x405cf5d01d1805deull }, + { 0x401b6d916872b021ull, 0x405cfaf41e66a60cull }, + { 0x401b6e978d4fdf3cull, 0x405d0019094ef800ull }, + { 0x401b6f9db22d0e56ull, 0x405d053eddfa72f3ull }, + { 0x401b70a3d70a3d71ull, 0x405d0a659c929584ull }, + { 0x401b71a9fbe76c8bull, 0x405d0f8d4540e59full }, + { 0x401b72b020c49ba6ull, 0x405d14b5d82ef0a1ull }, + { 0x401b73b645a1cac1ull, 0x405d19df55864b3aull }, + { 0x401b74bc6a7ef9dbull, 0x405d1f09bd70917bull }, + { 0x401b75c28f5c28f6ull, 0x405d2435101766e5ull }, + { 0x401b76c8b4395811ull, 0x405d29614da47650ull }, + { 0x401b77ced916872bull, 0x405d2e8e764171f9ull }, + { 0x401b78d4fdf3b646ull, 0x405d33bc8a181393ull }, + { 0x401b79db22d0e560ull, 0x405d38eb89521c24ull }, + { 0x401b7ae147ae147bull, 0x405d3e1b74195431ull }, + { 0x401b7be76c8b4396ull, 0x405d434c4a978b9aull }, + { 0x401b7ced916872b0ull, 0x405d487e0cf699aaull }, + { 0x401b7df3b645a1cbull, 0x405d4db0bb605d29ull }, + { 0x401b7ef9db22d0e6ull, 0x405d52e455febc41ull }, + { 0x401b800000000000ull, 0x405d5818dcfba487ull }, + { 0x401b810624dd2f1bull, 0x405d5d4e50810b15ull }, + { 0x401b820c49ba5e35ull, 0x405d6284b0b8ec62ull }, + { 0x401b83126e978d50ull, 0x405d67bbfdcd4c6bull }, + { 0x401b84189374bc6bull, 0x405d6cf437e83696ull }, + { 0x401b851eb851eb85ull, 0x405d722d5f33bdbfull }, + { 0x401b8624dd2f1aa0ull, 0x405d776773d9fc47ull }, + { 0x401b872b020c49bbull, 0x405d7ca2760513fcull }, + { 0x401b883126e978d5ull, 0x405d81de65df2e25ull }, + { 0x401b89374bc6a7f0ull, 0x405d871b43927b93ull }, + { 0x401b8a3d70a3d70aull, 0x405d8c590f493483ull }, + { 0x401b8b4395810625ull, 0x405d9197c92d98c2ull }, + { 0x401b8c49ba5e3540ull, 0x405d96d77169ef8full }, + { 0x401b8d4fdf3b645aull, 0x405d9c18082887aaull }, + { 0x401b8e5604189375ull, 0x405da1598d93b764ull }, + { 0x401b8f5c28f5c290ull, 0x405da69c01d5dc84ull }, + { 0x401b90624dd2f1aaull, 0x405dabdf65195c55ull }, + { 0x401b916872b020c5ull, 0x405db123b788a3b9ull }, + { 0x401b926e978d4fdfull, 0x405db668f94e2704ull }, + { 0x401b9374bc6a7efaull, 0x405dbbaf2a946229ull }, + { 0x401b947ae147ae15ull, 0x405dc0f64b85d895ull }, + { 0x401b95810624dd2full, 0x405dc63e5c4d1542ull }, + { 0x401b96872b020c4aull, 0x405dcb875d14aac7ull }, + { 0x401b978d4fdf3b65ull, 0x405dd0d14e073337ull }, + { 0x401b989374bc6a7full, 0x405dd61c2f4f503aull }, + { 0x401b99999999999aull, 0x405ddb680117ab14ull }, + { 0x401b9a9fbe76c8b4ull, 0x405de0b4c38af48aull }, + { 0x401b9ba5e353f7cfull, 0x405de60276d3e508ull }, + { 0x401b9cac083126eaull, 0x405deb511b1d3c80ull }, + { 0x401b9db22d0e5604ull, 0x405df0a0b091c27bull }, + { 0x401b9eb851eb851full, 0x405df5f1375c462aull }, + { 0x401b9fbe76c8b43aull, 0x405dfb42afa79e47ull }, + { 0x401ba0c49ba5e354ull, 0x405e0095199ea926ull }, + { 0x401ba1cac083126full, 0x405e05e8756c4cc9ull }, + { 0x401ba2d0e5604189ull, 0x405e0b3cc33b76b8ull }, + { 0x401ba3d70a3d70a4ull, 0x405e109203371c30ull }, + { 0x401ba4dd2f1a9fbfull, 0x405e15e8358a39feull }, + { 0x401ba5e353f7ced9ull, 0x405e1b3f5a5fd490ull }, + { 0x401ba6e978d4fdf4ull, 0x405e209771e2f808ull }, + { 0x401ba7ef9db22d0eull, 0x405e25f07c3eb815ull }, + { 0x401ba8f5c28f5c29ull, 0x405e2b4a799e3023ull }, + { 0x401ba9fbe76c8b44ull, 0x405e30a56a2c8333ull }, + { 0x401bab020c49ba5eull, 0x405e36014e14dbeeull }, + { 0x401bac083126e979ull, 0x405e3b5e25826cb9ull }, + { 0x401bad0e56041894ull, 0x405e40bbf0a06f92ull }, + { 0x401bae147ae147aeull, 0x405e461aaf9a2624ull }, + { 0x401baf1a9fbe76c9ull, 0x405e4b7a629ad9d6ull }, + { 0x401bb020c49ba5e3ull, 0x405e50db09cddbabull }, + { 0x401bb126e978d4feull, 0x405e563ca55e846bull }, + { 0x401bb22d0e560419ull, 0x405e5b9f3578347full }, + { 0x401bb33333333333ull, 0x405e6102ba465404ull }, + { 0x401bb4395810624eull, 0x405e666733f452dbull }, + { 0x401bb53f7ced9169ull, 0x405e6bcca2ada88bull }, + { 0x401bb645a1cac083ull, 0x405e7133069dd452ull }, + { 0x401bb74bc6a7ef9eull, 0x405e769a5ff05d37ull }, + { 0x401bb851eb851eb8ull, 0x405e7c02aed0d1e6ull }, + { 0x401bb95810624dd3ull, 0x405e816bf36ac8daull }, + { 0x401bba5e353f7ceeull, 0x405e86d62de9e03cull }, + { 0x401bbb645a1cac08ull, 0x405e8c415e79bdf3ull }, + { 0x401bbc6a7ef9db23ull, 0x405e91ad85460fb5ull }, + { 0x401bbd70a3d70a3eull, 0x405e971aa27a8aeaull }, + { 0x401bbe76c8b43958ull, 0x405e9c88b642ecbbull }, + { 0x401bbf7ced916873ull, 0x405ea1f7c0cafa25ull }, + { 0x401bc083126e978dull, 0x405ea767c23e7fd3ull }, + { 0x401bc189374bc6a8ull, 0x405eacd8bac95250ull }, + { 0x401bc28f5c28f5c3ull, 0x405eb24aaa974ddaull }, + { 0x401bc395810624ddull, 0x405eb7bd91d4567aull }, + { 0x401bc49ba5e353f8ull, 0x405ebd3170ac5815ull }, + { 0x401bc5a1cac08313ull, 0x405ec2a6474b4648ull }, + { 0x401bc6a7ef9db22dull, 0x405ec81c15dd1c82ull }, + { 0x401bc7ae147ae148ull, 0x405ecd92dc8dde0dull }, + { 0x401bc8b439581062ull, 0x405ed30a9b8995efull }, + { 0x401bc9ba5e353f7dull, 0x405ed88352fc5715ull }, + { 0x401bcac083126e98ull, 0x405eddfd03123c2full }, + { 0x401bcbc6a7ef9db2ull, 0x405ee377abf767bfull }, + { 0x401bcccccccccccdull, 0x405ee8f34dd80430ull }, + { 0x401bcdd2f1a9fbe8ull, 0x405eee6fe8e043b1ull }, + { 0x401bced916872b02ull, 0x405ef3ed7d3c604bull }, + { 0x401bcfdf3b645a1dull, 0x405ef96c0b189bf0ull }, + { 0x401bd0e560418937ull, 0x405efeeb92a14058ull }, + { 0x401bd1eb851eb852ull, 0x405f046c14029f2eull }, + { 0x401bd2f1a9fbe76dull, 0x405f09ed8f6911e9ull }, + { 0x401bd3f7ced91687ull, 0x405f0f700500f9e1ull }, + { 0x401bd4fdf3b645a2ull, 0x405f14f374f6c05cull }, + { 0x401bd604189374bdull, 0x405f1a77df76d677ull }, + { 0x401bd70a3d70a3d7ull, 0x405f1ffd44adb52dull }, + { 0x401bd810624dd2f2ull, 0x405f2583a4c7dd71ull }, + { 0x401bd916872b020cull, 0x405f2b0afff1d808ull }, + { 0x401bda1cac083127ull, 0x405f3093565835b1ull }, + { 0x401bdb22d0e56042ull, 0x405f361ca8278f06ull }, + { 0x401bdc28f5c28f5cull, 0x405f3ba6f58c848cull }, + { 0x401bdd2f1a9fbe77ull, 0x405f41323eb3bec2ull }, + { 0x401bde353f7ced92ull, 0x405f46be83c9ee06ull }, + { 0x401bdf3b645a1cacull, 0x405f4c4bc4fbcaa5ull }, + { 0x401be04189374bc7ull, 0x405f51da027614eaull }, + { 0x401be147ae147ae1ull, 0x405f57693c6594ffull }, + { 0x401be24dd2f1a9fcull, 0x405f5cf972f71b14ull }, + { 0x401be353f7ced917ull, 0x405f628aa6577f3eull }, + { 0x401be45a1cac0831ull, 0x405f681cd6b3a189ull }, + { 0x401be5604189374cull, 0x405f6db004386a08ull }, + { 0x401be66666666667ull, 0x405f73442f12c8b4ull }, + { 0x401be76c8b439581ull, 0x405f78d9576fb584ull }, + { 0x401be872b020c49cull, 0x405f7e6f7d7c3077ull }, + { 0x401be978d4fdf3b6ull, 0x405f8406a1654176ull }, + { 0x401bea7ef9db22d1ull, 0x405f899ec357f87cull }, + { 0x401beb851eb851ecull, 0x405f8f37e3816d74ull }, + { 0x401bec8b43958106ull, 0x405f94d2020ec04cull }, + { 0x401bed916872b021ull, 0x405f9a6d1f2d1902ull }, + { 0x401bee978d4fdf3cull, 0x405fa0093b09a789ull }, + { 0x401bef9db22d0e56ull, 0x405fa5a655d1a3daull }, + { 0x401bf0a3d70a3d71ull, 0x405fab446fb24e04ull }, + { 0x401bf1a9fbe76c8bull, 0x405fb0e388d8ee08ull }, + { 0x401bf2b020c49ba6ull, 0x405fb683a172d409ull }, + { 0x401bf3b645a1cac1ull, 0x405fbc24b9ad5822ull }, + { 0x401bf4bc6a7ef9dbull, 0x405fc1c6d1b5da7dull }, + { 0x401bf5c28f5c28f6ull, 0x405fc769e9b9c35full }, + { 0x401bf6c8b4395811ull, 0x405fcd0e01e68310ull }, + { 0x401bf7ced916872bull, 0x405fd2b31a6991e5ull }, + { 0x401bf8d4fdf3b646ull, 0x405fd85933707059ull }, + { 0x401bf9db22d0e560ull, 0x405fde004d28a6e3ull }, + { 0x401bfae147ae147bull, 0x405fe3a867bfc624ull }, + { 0x401bfbe76c8b4396ull, 0x405fe951836366c6ull }, + { 0x401bfced916872b0ull, 0x405feefba0412988ull }, + { 0x401bfdf3b645a1cbull, 0x405ff4a6be86b756ull }, + { 0x401bfef9db22d0e6ull, 0x405ffa52de61c123ull }, + { 0x401c000000000000ull, 0x4060000000000000ull }, + { 0x401c010624dd2f1bull, 0x406002d711c79a97ull }, + { 0x401c020c49ba5e35ull, 0x406005aea49e94f9ull }, + { 0x401c03126e978d50ull, 0x40600886b89bd7e7ull }, + { 0x401c04189374bc6bull, 0x40600b5f4dd65029ull }, + { 0x401c051eb851eb85ull, 0x40600e386464ee98ull }, + { 0x401c0624dd2f1aa0ull, 0x40601111fc5ea82bull }, + { 0x401c072b020c49bbull, 0x406013ec15da75dfull }, + { 0x401c083126e978d5ull, 0x406016c6b0ef54c9ull }, + { 0x401c09374bc6a7f0ull, 0x406019a1cdb44619ull }, + { 0x401c0a3d70a3d70aull, 0x40601c7d6c404f0bull }, + { 0x401c0b4395810625ull, 0x40601f598caa78faull }, + { 0x401c0c49ba5e3540ull, 0x406022362f09d150ull }, + { 0x401c0d4fdf3b645aull, 0x4060251353756991ull }, + { 0x401c0e5604189375ull, 0x406027f0fa04575dull }, + { 0x401c0f5c28f5c290ull, 0x40602acf22cdb46aull }, + { 0x401c10624dd2f1aaull, 0x40602dadcde89e83ull }, + { 0x401c116872b020c5ull, 0x4060308cfb6c3799ull }, + { 0x401c126e978d4fdfull, 0x4060336cab6fa5acull }, + { 0x401c1374bc6a7efaull, 0x4060364cde0a12e2ull }, + { 0x401c147ae147ae15ull, 0x4060392d9352ad77ull }, + { 0x401c15810624dd2full, 0x40603c0ecb60a7c3ull }, + { 0x401c16872b020c4aull, 0x40603ef0864b3846ull }, + { 0x401c178d4fdf3b65ull, 0x406041d2c4299994ull }, + { 0x401c189374bc6a7full, 0x406044b585130a64ull }, + { 0x401c19999999999aull, 0x40604798c91ecd93ull }, + { 0x401c1a9fbe76c8b4ull, 0x40604a7c90642a14ull }, + { 0x401c1ba5e353f7cfull, 0x40604d60dafa6b08ull }, + { 0x401c1cac083126eaull, 0x40605045a8f8dfaaull }, + { 0x401c1db22d0e5604ull, 0x4060532afa76db57ull }, + { 0x401c1eb851eb851full, 0x40605610cf8bb59aull }, + { 0x401c1fbe76c8b43aull, 0x406058f7284eca1aull }, + { 0x401c20c49ba5e354ull, 0x40605bde04d778a2ull }, + { 0x401c21cac083126full, 0x40605ec5653d252dull }, + { 0x401c22d0e5604189ull, 0x406061ad499737d2ull }, + { 0x401c23d70a3d70a4ull, 0x40606495b1fd1cd8ull }, + { 0x401c24dd2f1a9fbfull, 0x4060677e9e8644abull }, + { 0x401c25e353f7ced9ull, 0x40606a680f4a23daull }, + { 0x401c26e978d4fdf4ull, 0x40606d520460332cull }, + { 0x401c27ef9db22d0full, 0x4060703c7ddfef87ull }, + { 0x401c28f5c28f5c29ull, 0x406073277be0d9fcull }, + { 0x401c29fbe76c8b44ull, 0x40607612fe7a77d3ull }, + { 0x401c2b020c49ba5eull, 0x406078ff05c45272ull }, + { 0x401c2c083126e979ull, 0x40607beb91d5f77cull }, + { 0x401c2d0e56041894ull, 0x40607ed8a2c6f8b7ull }, + { 0x401c2e147ae147aeull, 0x406081c638aeec18ull }, + { 0x401c2f1a9fbe76c9ull, 0x406084b453a56bceull }, + { 0x401c3020c49ba5e3ull, 0x406087a2f3c2162cull }, + { 0x401c3126e978d4feull, 0x40608a92191c8dc2ull }, + { 0x401c322d0e560419ull, 0x40608d81c3cc7948ull }, + { 0x401c333333333333ull, 0x40609071f3e983acull }, + { 0x401c34395810624eull, 0x40609362a98b5c15ull }, + { 0x401c353f7ced9169ull, 0x40609653e4c9b5d7ull }, + { 0x401c3645a1cac083ull, 0x40609945a5bc487bull }, + { 0x401c374bc6a7ef9eull, 0x40609c37ec7acfc8ull }, + { 0x401c3851eb851eb8ull, 0x40609f2ab91d0bb0ull }, + { 0x401c395810624dd3ull, 0x4060a21e0bbac068ull }, + { 0x401c3a5e353f7ceeull, 0x4060a511e46bb655ull }, + { 0x401c3b645a1cac08ull, 0x4060a8064347ba12ull }, + { 0x401c3c6a7ef9db23ull, 0x4060aafb28669c81ull }, + { 0x401c3d70a3d70a3eull, 0x4060adf093e032b1ull }, + { 0x401c3e76c8b43958ull, 0x4060b0e685cc55edull }, + { 0x401c3f7ced916873ull, 0x4060b3dcfe42e3c7ull }, + { 0x401c4083126e978dull, 0x4060b6d3fd5bbdfeull }, + { 0x401c4189374bc6a8ull, 0x4060b9cb832eca9dull }, + { 0x401c428f5c28f5c3ull, 0x4060bcc38fd3f3e4ull }, + { 0x401c4395810624ddull, 0x4060bfbc2363284full }, + { 0x401c449ba5e353f8ull, 0x4060c2b53df45aa5ull }, + { 0x401c45a1cac08313ull, 0x4060c5aedf9f81e4ull }, + { 0x401c46a7ef9db22dull, 0x4060c8a9087c9949ull }, + { 0x401c47ae147ae148ull, 0x4060cba3b8a3a060ull }, + { 0x401c48b439581062ull, 0x4060ce9ef02c9ae6ull }, + { 0x401c49ba5e353f7dull, 0x4060d19aaf2f90ecull }, + { 0x401c4ac083126e98ull, 0x4060d496f5c48ebbull }, + { 0x401c4bc6a7ef9db2ull, 0x4060d793c403a4e2ull }, + { 0x401c4ccccccccccdull, 0x4060da911a04e83full }, + { 0x401c4dd2f1a9fbe8ull, 0x4060dd8ef7e071edull }, + { 0x401c4ed916872b02ull, 0x4060e08d5dae5f4dull }, + { 0x401c4fdf3b645a1dull, 0x4060e38c4b86d212ull }, + { 0x401c50e560418937ull, 0x4060e68bc181f02bull }, + { 0x401c51eb851eb852ull, 0x4060e98bbfb7e3ddull }, + { 0x401c52f1a9fbe76dull, 0x4060ec8c4640dbadull }, + { 0x401c53f7ced91687ull, 0x4060ef8d55350a6cull }, + { 0x401c54fdf3b645a2ull, 0x4060f28eecaca740ull }, + { 0x401c5604189374bdull, 0x4060f5910cbfed90ull }, + { 0x401c570a3d70a3d7ull, 0x4060f893b5871d13ull }, + { 0x401c5810624dd2f2ull, 0x4060fb96e71a79d4ull }, + { 0x401c5916872b020cull, 0x4060fe9aa1924c24ull }, + { 0x401c5a1cac083127ull, 0x4061019ee506e0adull }, + { 0x401c5b22d0e56042ull, 0x406104a3b1908860ull }, + { 0x401c5c28f5c28f5cull, 0x406107a907479882ull }, + { 0x401c5d2f1a9fbe77ull, 0x40610aaee6446aafull }, + { 0x401c5e353f7ced92ull, 0x40610db54e9f5ccfull }, + { 0x401c5f3b645a1cacull, 0x406110bc4070d11cull }, + { 0x401c604189374bc7ull, 0x406113c3bbd12e2eull }, + { 0x401c6147ae147ae1ull, 0x406116cbc0d8dee4ull }, + { 0x401c624dd2f1a9fcull, 0x406119d44fa05282ull }, + { 0x401c6353f7ced917ull, 0x40611cdd683ffc95ull }, + { 0x401c645a1cac0831ull, 0x40611fe70ad05505ull }, + { 0x401c65604189374cull, 0x406122f13769d818ull }, + { 0x401c666666666667ull, 0x406125fbee250666ull }, + { 0x401c676c8b439581ull, 0x406129072f1a64dfull }, + { 0x401c6872b020c49cull, 0x40612c12fa627cd6ull }, + { 0x401c6978d4fdf3b6ull, 0x40612f1f5015dbeeull }, + { 0x401c6a7ef9db22d1ull, 0x4061322c304d1431ull }, + { 0x401c6b851eb851ecull, 0x406135399b20bbfdull }, + { 0x401c6c8b43958106ull, 0x4061384790a96e0cull }, + { 0x401c6d916872b021ull, 0x40613b5610ffc982ull }, + { 0x401c6e978d4fdf3cull, 0x40613e651c3c71d7ull }, + { 0x401c6f9db22d0e56ull, 0x40614174b2780ee0ull }, + { 0x401c70a3d70a3d71ull, 0x40614484d3cb4ce1ull }, + { 0x401c71a9fbe76c8bull, 0x40614795804edc6eull }, + { 0x401c72b020c49ba6ull, 0x40614aa6b81b728dull }, + { 0x401c73b645a1cac1ull, 0x40614db87b49c89bull }, + { 0x401c74bc6a7ef9dbull, 0x406150cac9f29c58ull }, + { 0x401c75c28f5c28f6ull, 0x406153dda42eaff5ull }, + { 0x401c76c8b4395811ull, 0x406156f10a16c9fcull }, + { 0x401c77ced916872bull, 0x40615a04fbc3b55dull }, + { 0x401c78d4fdf3b646ull, 0x40615d19794e4179ull }, + { 0x401c79db22d0e560ull, 0x4061602e82cf420bull }, + { 0x401c7ae147ae147bull, 0x40616344185f8f43ull }, + { 0x401c7be76c8b4396ull, 0x4061665a3a1805b1ull }, + { 0x401c7ced916872b0ull, 0x40616970e811864eull }, + { 0x401c7df3b645a1cbull, 0x40616c882264f688ull }, + { 0x401c7ef9db22d0e6ull, 0x40616f9fe92b402eull }, + { 0x401c800000000000ull, 0x406172b83c7d517bull }, + { 0x401c810624dd2f1bull, 0x406175d11c741d21ull }, + { 0x401c820c49ba5e35ull, 0x406178ea89289a33ull }, + { 0x401c83126e978d50ull, 0x40617c0482b3c43full }, + { 0x401c84189374bc6bull, 0x40617f1f092e9b38ull }, + { 0x401c851eb851eb85ull, 0x4061823a1cb22383ull }, + { 0x401c8624dd2f1aa0ull, 0x40618555bd5765fdull }, + { 0x401c872b020c49bbull, 0x40618871eb376fecull }, + { 0x401c883126e978d5ull, 0x40618b8ea66b5309ull }, + { 0x401c89374bc6a7f0ull, 0x40618eabef0c2588ull }, + { 0x401c8a3d70a3d70aull, 0x406191c9c5330208ull }, + { 0x401c8b4395810625ull, 0x406194e828f907a5ull }, + { 0x401c8c49ba5e3540ull, 0x406198071a7759eaull }, + { 0x401c8d4fdf3b645aull, 0x40619b2699c720d8ull }, + { 0x401c8e5604189375ull, 0x40619e46a70188eeull }, + { 0x401c8f5c28f5c290ull, 0x4061a167423fc31dull }, + { 0x401c90624dd2f1aaull, 0x4061a4886b9b04cdull }, + { 0x401c916872b020c5ull, 0x4061a7aa232c87eaull }, + { 0x401c926e978d4fdfull, 0x4061aacc690d8acbull }, + { 0x401c9374bc6a7efaull, 0x4061adef3d575053ull }, + { 0x401c947ae147ae15ull, 0x4061b112a0231fd3ull }, + { 0x401c95810624dd2full, 0x4061b436918a451dull }, + { 0x401c96872b020c4aull, 0x4061b75b11a61086ull }, + { 0x401c978d4fdf3b65ull, 0x4061ba80208fd6daull }, + { 0x401c989374bc6a7full, 0x4061bda5be60f165ull }, + { 0x401c99999999999aull, 0x4061c0cbeb32bdfaull }, + { 0x401c9a9fbe76c8b4ull, 0x4061c3f2a71e9ee1ull }, + { 0x401c9ba5e353f7cfull, 0x4061c719f23dfaf1ull }, + { 0x401c9cac083126eaull, 0x4061ca41ccaa3d79ull }, + { 0x401c9db22d0e5604ull, 0x4061cd6a367cd64cull }, + { 0x401c9eb851eb851full, 0x4061d0932fcf39c7ull }, + { 0x401c9fbe76c8b43aull, 0x4061d3bcb8bae0c8ull }, + { 0x401ca0c49ba5e354ull, 0x4061d6e6d15948adull }, + { 0x401ca1cac083126full, 0x4061da1179c3f368ull }, + { 0x401ca2d0e5604189ull, 0x4061dd3cb2146762ull }, + { 0x401ca3d70a3d70a4ull, 0x4061e0687a642f9aull }, + { 0x401ca4dd2f1a9fbfull, 0x4061e394d2ccdb90ull }, + { 0x401ca5e353f7ced9ull, 0x4061e6c1bb67ff4bull }, + { 0x401ca6e978d4fdf4ull, 0x4061e9ef344f3366ull }, + { 0x401ca7ef9db22d0full, 0x4061ed1d3d9c1500ull }, + { 0x401ca8f5c28f5c29ull, 0x4061f04bd76845c1ull }, + { 0x401ca9fbe76c8b44ull, 0x4061f37b01cd6be9ull }, + { 0x401cab020c49ba5eull, 0x4061f6aabce53238ull }, + { 0x401cac083126e979ull, 0x4061f9db08c94809ull }, + { 0x401cad0e56041894ull, 0x4061fd0be593613eull }, + { 0x401cae147ae147aeull, 0x4062003d535d3649ull }, + { 0x401caf1a9fbe76c9ull, 0x4062036f52408433ull }, + { 0x401cb020c49ba5e3ull, 0x406206a1e2570c8eull }, + { 0x401cb126e978d4feull, 0x406209d503ba9588ull }, + { 0x401cb22d0e560419ull, 0x40620d08b684e9dcull }, + { 0x401cb33333333333ull, 0x4062103cfacfd8d6ull }, + { 0x401cb4395810624eull, 0x40621371d0b53661ull }, + { 0x401cb53f7ced9169ull, 0x406216a7384edaf5ull }, + { 0x401cb645a1cac083ull, 0x406219dd31b6a3a1ull }, + { 0x401cb74bc6a7ef9eull, 0x40621d13bd067213ull }, + { 0x401cb851eb851eb8ull, 0x4062204ada582c85ull }, + { 0x401cb95810624dd3ull, 0x4062238289c5bdd8ull }, + { 0x401cba5e353f7ceeull, 0x406226bacb69157bull }, + { 0x401cbb645a1cac08ull, 0x406229f39f5c277aull }, + { 0x401cbc6a7ef9db23ull, 0x40622d2d05b8ec83ull }, + { 0x401cbd70a3d70a3eull, 0x40623066fe9961daull }, + { 0x401cbe76c8b43958ull, 0x406233a18a17895cull }, + { 0x401cbf7ced916873ull, 0x406236dca84d6992ull }, + { 0x401cc083126e978dull, 0x40623a1859550d93ull }, + { 0x401cc189374bc6a8ull, 0x40623d549d488524ull }, + { 0x401cc28f5c28f5c3ull, 0x406240917441e4a0ull }, + { 0x401cc395810624ddull, 0x406243cede5b4505ull }, + { 0x401cc49ba5e353f8ull, 0x4062470cdbaec3faull }, + { 0x401cc5a1cac08313ull, 0x40624a4b6c5683c0ull }, + { 0x401cc6a7ef9db22dull, 0x40624d8a906cab3cull }, + { 0x401cc7ae147ae148ull, 0x406250ca480b6601ull }, + { 0x401cc8b439581062ull, 0x4062540a934ce43aull }, + { 0x401cc9ba5e353f7dull, 0x4062574b724b5ac5ull }, + { 0x401ccac083126e98ull, 0x40625a8ce521031eull }, + { 0x401ccbc6a7ef9db2ull, 0x40625dceebe81b67ull }, + { 0x401ccccccccccccdull, 0x4062611186bae675ull }, + { 0x401ccdd2f1a9fbe8ull, 0x40626454b5b3abbeull }, + { 0x401cced916872b02ull, 0x4062679878ecb760ull }, + { 0x401ccfdf3b645a1dull, 0x40626adcd0805a2full }, + { 0x401cd0e560418937ull, 0x40626e21bc88e99dull }, + { 0x401cd1eb851eb852ull, 0x406271673d20bfd8ull }, + { 0x401cd2f1a9fbe76dull, 0x406274ad52623baeull }, + { 0x401cd3f7ced91687ull, 0x406277f3fc67c09eull }, + { 0x401cd4fdf3b645a2ull, 0x40627b3b3b4bb6dfull }, + { 0x401cd604189374bdull, 0x40627e830f288b4full }, + { 0x401cd70a3d70a3d7ull, 0x406281cb7818af7cull }, + { 0x401cd810624dd2f2ull, 0x40628514763699afull }, + { 0x401cd916872b020cull, 0x4062885e099cc4d8ull }, + { 0x401cda1cac083127ull, 0x40628ba83265b0a6ull }, + { 0x401cdb22d0e56042ull, 0x40628ef2f0abe173ull }, + { 0x401cdc28f5c28f5cull, 0x4062923e4489e04dull }, + { 0x401cdd2f1a9fbe77ull, 0x4062958a2e1a3b04ull }, + { 0x401cde353f7ced92ull, 0x406298d6ad778411ull }, + { 0x401cdf3b645a1cacull, 0x40629c23c2bc52a9ull }, + { 0x401ce04189374bc7ull, 0x40629f716e0342c0ull }, + { 0x401ce147ae147ae1ull, 0x4062a2bfaf66f4f6ull }, + { 0x401ce24dd2f1a9fcull, 0x4062a60e87020eb5ull }, + { 0x401ce353f7ced917ull, 0x4062a95df4ef3a14ull }, + { 0x401ce45a1cac0831ull, 0x4062acadf94925e9ull }, + { 0x401ce5604189374cull, 0x4062affe942a85cfull }, + { 0x401ce66666666667ull, 0x4062b34fc5ae1215ull }, + { 0x401ce76c8b439581ull, 0x4062b6a18dee87c7ull }, + { 0x401ce872b020c49cull, 0x4062b9f3ed06a8bbull }, + { 0x401ce978d4fdf3b6ull, 0x4062bd46e3113b7aull }, + { 0x401cea7ef9db22d1ull, 0x4062c09a70290b5aull }, + { 0x401ceb851eb851ecull, 0x4062c3ee9468e86aull }, + { 0x401cec8b43958106ull, 0x4062c7434feba778ull }, + { 0x401ced916872b021ull, 0x4062ca98a2cc2225ull }, + { 0x401cee978d4fdf3cull, 0x4062cdee8d2536c6ull }, + { 0x401cef9db22d0e56ull, 0x4062d1450f11c87aull }, + { 0x401cf0a3d70a3d71ull, 0x4062d49c28acbf2cull }, + { 0x401cf1a9fbe76c8bull, 0x4062d7f3da110783ull }, + { 0x401cf2b020c49ba6ull, 0x4062db4c235992f9ull }, + { 0x401cf3b645a1cac1ull, 0x4062dea504a157c8ull }, + { 0x401cf4bc6a7ef9dbull, 0x4062e1fe7e0350f2ull }, + { 0x401cf5c28f5c28f6ull, 0x4062e5588f9a7e4cull }, + { 0x401cf6c8b4395811ull, 0x4062e8b33981e46full }, + { 0x401cf7ced916872bull, 0x4062ec0e7bd48cbeull }, + { 0x401cf8d4fdf3b646ull, 0x4062ef6a56ad8571ull }, + { 0x401cf9db22d0e560ull, 0x4062f2c6ca27e184ull }, + { 0x401cfae147ae147bull, 0x4062f623d65eb8caull }, + { 0x401cfbe76c8b4396ull, 0x4062f9817b6d27dfull }, + { 0x401cfced916872b0ull, 0x4062fcdfb96e502dull }, + { 0x401cfdf3b645a1cbull, 0x4063003e907d57faull }, + { 0x401cfef9db22d0e6ull, 0x4063039e00b56a52ull }, + { 0x401d000000000000ull, 0x406306fe0a31b715ull }, + { 0x401d010624dd2f1bull, 0x40630a5ead0d7301ull }, + { 0x401d020c49ba5e35ull, 0x40630dbfe963d799ull }, + { 0x401d03126e978d50ull, 0x40631121bf502344ull }, + { 0x401d04189374bc6bull, 0x406314842eed9935ull }, + { 0x401d051eb851eb85ull, 0x406317e738578174ull }, + { 0x401d0624dd2f1aa0ull, 0x40631b4adba928eeull }, + { 0x401d072b020c49bbull, 0x40631eaf18fde15bull }, + { 0x401d083126e978d5ull, 0x40632213f071014full }, + { 0x401d09374bc6a7f0ull, 0x40632579621de441ull }, + { 0x401d0a3d70a3d70aull, 0x406328df6e1fea77ull }, + { 0x401d0b4395810625ull, 0x40632c461492791eull }, + { 0x401d0c49ba5e3540ull, 0x40632fad5590fa3aull }, + { 0x401d0d4fdf3b645aull, 0x406333153136dca8ull }, + { 0x401d0e5604189375ull, 0x4063367da79f9431ull }, + { 0x401d0f5c28f5c290ull, 0x406339e6b8e69972ull }, + { 0x401d10624dd2f1aaull, 0x40633d50652769e9ull }, + { 0x401d116872b020c5ull, 0x406340baac7d87feull }, + { 0x401d126e978d4fdfull, 0x406344258f047af0ull }, + { 0x401d1374bc6a7efaull, 0x406347910cd7ceecull }, + { 0x401d147ae147ae15ull, 0x40634afd261314faull }, + { 0x401d15810624dd2full, 0x40634e69dad1e306ull }, + { 0x401d16872b020c4aull, 0x406351d72b2fd3ecull }, + { 0x401d178d4fdf3b65ull, 0x4063554517488764ull }, + { 0x401d189374bc6a7full, 0x406358b39f37a20full }, + { 0x401d19999999999aull, 0x40635c22c318cd7eull }, + { 0x401d1a9fbe76c8b4ull, 0x40635f928307b81eull }, + { 0x401d1ba5e353f7cfull, 0x40636302df201555ull }, + { 0x401d1cac083126eaull, 0x40636673d77d9d67ull }, + { 0x401d1db22d0e5604ull, 0x406369e56c3c0d85ull }, + { 0x401d1eb851eb851full, 0x40636d579d7727d8ull }, + { 0x401d1fbe76c8b43aull, 0x406370ca6b4ab36bull }, + { 0x401d20c49ba5e354ull, 0x4063743dd5d27c36ull }, + { 0x401d21cac083126full, 0x406377b1dd2a532cull }, + { 0x401d22d0e5604189ull, 0x40637b26816e0e22ull }, + { 0x401d23d70a3d70a4ull, 0x40637e9bc2b987ecull }, + { 0x401d24dd2f1a9fbfull, 0x40638211a128a044ull }, + { 0x401d25e353f7ced9ull, 0x406385881cd73bd9ull }, + { 0x401d26e978d4fdf4ull, 0x406388ff35e14455ull }, + { 0x401d27ef9db22d0full, 0x40638c76ec62a84eull }, + { 0x401d28f5c28f5c29ull, 0x40638fef40775b50ull }, + { 0x401d29fbe76c8b44ull, 0x40639368323b55e5ull }, + { 0x401d2b020c49ba5eull, 0x406396e1c1ca9582ull }, + { 0x401d2c083126e979ull, 0x40639a5bef411ca1ull }, + { 0x401d2d0e56041894ull, 0x40639dd6babaf2abull }, + { 0x401d2e147ae147aeull, 0x4063a15224542403ull }, + { 0x401d2f1a9fbe76c9ull, 0x4063a4ce2c28c210ull }, + { 0x401d3020c49ba5e3ull, 0x4063a84ad254e327ull }, + { 0x401d3126e978d4feull, 0x4063abc816f4a2a7ull }, + { 0x401d322d0e560419ull, 0x4063af45fa2420e2ull }, + { 0x401d333333333333ull, 0x4063b2c47bff8328ull }, + { 0x401d34395810624eull, 0x4063b6439ca2f3d4ull }, + { 0x401d353f7ced9169ull, 0x4063b9c35c2aa233ull }, + { 0x401d3645a1cac083ull, 0x4063bd43bab2c296ull }, + { 0x401d374bc6a7ef9eull, 0x4063c0c4b8578e58ull }, + { 0x401d3851eb851eb8ull, 0x4063c446553543cbull }, + { 0x401d395810624dd3ull, 0x4063c7c891682650ull }, + { 0x401d3a5e353f7ceeull, 0x4063cb4b6d0c7e42ull }, + { 0x401d3b645a1cac08ull, 0x4063cecee83e9903ull }, + { 0x401d3c6a7ef9db23ull, 0x4063d253031ac905ull }, + { 0x401d3d70a3d70a3eull, 0x4063d5d7bdbd65b4ull }, + { 0x401d3e76c8b43958ull, 0x4063d95d1842cb89ull }, + { 0x401d3f7ced916873ull, 0x4063dce312c75c0aull }, + { 0x401d4083126e978dull, 0x4063e069ad677dbeull }, + { 0x401d4189374bc6a8ull, 0x4063e3f0e83f9c42ull }, + { 0x401d428f5c28f5c3ull, 0x4063e778c36c2835ull }, + { 0x401d4395810624ddull, 0x4063eb013f099740ull }, + { 0x401d449ba5e353f8ull, 0x4063ee8a5b346428ull }, + { 0x401d45a1cac08313ull, 0x4063f21418090eb2ull }, + { 0x401d46a7ef9db22dull, 0x4063f59e75a41bb3ull }, + { 0x401d47ae147ae148ull, 0x4063f9297422151bull }, + { 0x401d48b439581062ull, 0x4063fcb5139f89deull }, + { 0x401d49ba5e353f7dull, 0x4064004154390e0cull }, + { 0x401d4ac083126e98ull, 0x406403ce360b3ac1ull }, + { 0x401d4bc6a7ef9db2ull, 0x4064075bb932ae2bull }, + { 0x401d4ccccccccccdull, 0x40640ae9ddcc0b97ull }, + { 0x401d4dd2f1a9fbe8ull, 0x40640e78a3f3fb5cull }, + { 0x401d4ed916872b02ull, 0x406412080bc72ae9ull }, + { 0x401d4fdf3b645a1dull, 0x4064159815624cceull }, + { 0x401d50e560418937ull, 0x40641928c0e218a4ull }, + { 0x401d51eb851eb852ull, 0x40641cba0e634b2cull }, + { 0x401d52f1a9fbe76dull, 0x4064204bfe02a637ull }, + { 0x401d53f7ced91687ull, 0x406423de8fdcf0afull }, + { 0x401d54fdf3b645a2ull, 0x40642771c40ef6a6ull }, + { 0x401d5604189374bdull, 0x40642b059ab5893eull }, + { 0x401d570a3d70a3d7ull, 0x40642e9a13ed7eb9ull }, + { 0x401d5810624dd2f2ull, 0x4064322f2fd3b280ull }, + { 0x401d5916872b020cull, 0x406435c4ee85050full }, + { 0x401d5a1cac083127ull, 0x4064395b501e5c0dull }, + { 0x401d5b22d0e56042ull, 0x40643cf254bca23cull }, + { 0x401d5c28f5c28f5cull, 0x40644089fc7cc77cull }, + { 0x401d5d2f1a9fbe77ull, 0x40644422477bc0deull }, + { 0x401d5e353f7ced92ull, 0x406447bb35d6888aull }, + { 0x401d5f3b645a1cacull, 0x40644b54c7aa1dcdull }, + { 0x401d604189374bc7ull, 0x40644eeefd138526ull }, + { 0x401d6147ae147ae1ull, 0x40645289d62fc829ull }, + { 0x401d624dd2f1a9fcull, 0x40645625531bf5a4ull }, + { 0x401d6353f7ced917ull, 0x406459c173f5217full }, + { 0x401d645a1cac0831ull, 0x40645d5e38d864cfull }, + { 0x401d65604189374cull, 0x406460fba1e2dddaull }, + { 0x401d666666666667ull, 0x40646499af31b009ull }, + { 0x401d676c8b439581ull, 0x4064683860e203f1ull }, + { 0x401d6872b020c49cull, 0x40646bd7b711075eull }, + { 0x401d6978d4fdf3b6ull, 0x40646f77b1dbed3bull }, + { 0x401d6a7ef9db22d1ull, 0x40647318515fedb3ull }, + { 0x401d6b851eb851ecull, 0x406476b995ba4613ull }, + { 0x401d6c8b43958106ull, 0x40647a5b7f0838daull }, + { 0x401d6d916872b021ull, 0x40647dfe0d670dc3ull }, + { 0x401d6e978d4fdf3cull, 0x406481a140f411b1ull }, + { 0x401d6f9db22d0e56ull, 0x4064854519cc96baull }, + { 0x401d70a3d70a3d71ull, 0x406488e9980df435ull }, + { 0x401d71a9fbe76c8bull, 0x40648c8ebbd5869dull }, + { 0x401d72b020c49ba6ull, 0x406490348540afb4ull }, + { 0x401d73b645a1cac1ull, 0x406493daf46cd668ull }, + { 0x401d74bc6a7ef9dbull, 0x40649782097766ddull }, + { 0x401d75c28f5c28f6ull, 0x40649b29c47dd27eull }, + { 0x401d76c8b4395811ull, 0x40649ed2259d8fe3ull }, + { 0x401d77ced916872bull, 0x4064a27b2cf41adfull }, + { 0x401d78d4fdf3b646ull, 0x4064a624da9ef48cull }, + { 0x401d79db22d0e560ull, 0x4064a9cf2ebba333ull }, + { 0x401d7ae147ae147bull, 0x4064ad7a2967b268ull }, + { 0x401d7be76c8b4396ull, 0x4064b125cac0b2f4ull }, + { 0x401d7ced916872b0ull, 0x4064b4d212e43addull }, + { 0x401d7df3b645a1cbull, 0x4064b87f01efe577ull }, + { 0x401d7ef9db22d0e6ull, 0x4064bc2c9801534cull }, + { 0x401d800000000000ull, 0x4064bfdad5362a27ull }, + { 0x401d810624dd2f1bull, 0x4064c389b9ac1522ull }, + { 0x401d820c49ba5e35ull, 0x4064c7394580c48dull }, + { 0x401d83126e978d50ull, 0x4064cae978d1ee0bull }, + { 0x401d84189374bc6bull, 0x4064ce9a53bd4c7bull }, + { 0x401d851eb851eb85ull, 0x4064d24bd660a001ull }, + { 0x401d8624dd2f1aa0ull, 0x4064d5fe00d9ae17ull }, + { 0x401d872b020c49bbull, 0x4064d9b0d3464172ull }, + { 0x401d883126e978d5ull, 0x4064dd644dc42a11ull }, + { 0x401d89374bc6a7f0ull, 0x4064e11870713d4cull }, + { 0x401d8a3d70a3d70aull, 0x4064e4cd3b6b55b4ull }, + { 0x401d8b4395810625ull, 0x4064e882aed05338ull }, + { 0x401d8c49ba5e3540ull, 0x4064ec38cabe1b07ull }, + { 0x401d8d4fdf3b645aull, 0x4064efef8f5297a2ull }, + { 0x401d8e5604189375ull, 0x4064f3a6fcabb8e1ull }, + { 0x401d8f5c28f5c290ull, 0x4064f75f12e773e3ull }, + { 0x401d90624dd2f1aaull, 0x4064fb17d223c319ull }, + { 0x401d916872b020c5ull, 0x4064fed13a7ea64full }, + { 0x401d926e978d4fdfull, 0x4065028b4c162299ull }, + { 0x401d9374bc6a7efaull, 0x406506460708426aull }, + { 0x401d947ae147ae15ull, 0x40650a016b731582ull }, + { 0x401d95810624dd2full, 0x40650dbd7974b0f6ull }, + { 0x401d96872b020c4aull, 0x4065117a312b2f3cull }, + { 0x401d978d4fdf3b65ull, 0x4065153792b4b019ull }, + { 0x401d989374bc6a7full, 0x406518f59e2f58a9ull }, + { 0x401d99999999999aull, 0x40651cb453b9536eull }, + { 0x401d9a9fbe76c8b4ull, 0x40652073b370d036ull }, + { 0x401d9ba5e353f7cfull, 0x40652433bd740438ull }, + { 0x401d9cac083126eaull, 0x406527f471e12a00ull }, + { 0x401d9db22d0e5604ull, 0x40652bb5d0d68174ull }, + { 0x401d9eb851eb851full, 0x40652f77da724fe6ull }, + { 0x401d9fbe76c8b43aull, 0x4065333a8ed2dffbull }, + { 0x401da0c49ba5e354ull, 0x406536fdee1681baull }, + { 0x401da1cac083126full, 0x40653ac1f85b8a94ull }, + { 0x401da2d0e5604189ull, 0x40653e86adc05552ull }, + { 0x401da3d70a3d70a4ull, 0x4065424c0e63422aull }, + { 0x401da4dd2f1a9fbfull, 0x406546121a62b6b0ull }, + { 0x401da5e353f7ced9ull, 0x406549d8d1dd1dd9ull }, + { 0x401da6e978d4fdf4ull, 0x40654da034f0e80full }, + { 0x401da7ef9db22d0full, 0x4065516843bc8b15ull }, + { 0x401da8f5c28f5c29ull, 0x40655530fe5e821aull }, + { 0x401da9fbe76c8b44ull, 0x406558fa64f54dbcull }, + { 0x401dab020c49ba5eull, 0x40655cc4779f73f9ull }, + { 0x401dac083126e979ull, 0x4065608f367b8047ull }, + { 0x401dad0e56041894ull, 0x4065645aa1a8037cull }, + { 0x401dae147ae147aeull, 0x40656826b94393ddull }, + { 0x401daf1a9fbe76c9ull, 0x40656bf37d6ccd26ull }, + { 0x401db020c49ba5e4ull, 0x40656fc0ee425078ull }, + { 0x401db126e978d4feull, 0x4065738f0be2c463ull }, + { 0x401db22d0e560419ull, 0x4065775dd66cd4f5ull }, + { 0x401db33333333333ull, 0x40657b2d4dff339cull }, + { 0x401db4395810624eull, 0x40657efd72b8974aull }, + { 0x401db53f7ced9169ull, 0x406582ce44b7bc58ull }, + { 0x401db645a1cac083ull, 0x4065869fc41b6494ull }, + { 0x401db74bc6a7ef9eull, 0x40658a71f102574full }, + { 0x401db851eb851eb8ull, 0x40658e44cb8b613eull }, + { 0x401db95810624dd3ull, 0x4065921853d5549full }, + { 0x401dba5e353f7ceeull, 0x406595ec89ff091dull }, + { 0x401dbb645a1cac08ull, 0x406599c16e275bdcull }, + { 0x401dbc6a7ef9db23ull, 0x40659d97006d2f87ull }, + { 0x401dbd70a3d70a3eull, 0x4065a16d40ef6c36ull }, + { 0x401dbe76c8b43958ull, 0x4065a5442fccff82ull }, + { 0x401dbf7ced916873ull, 0x4065a91bcd24dc8aull }, + { 0x401dc083126e978dull, 0x4065acf41915fbddull }, + { 0x401dc189374bc6a8ull, 0x4065b0cd13bf5b97ull }, + { 0x401dc28f5c28f5c3ull, 0x4065b4a6bd3fff4bull }, + { 0x401dc395810624ddull, 0x4065b88115b6f00dull }, + { 0x401dc49ba5e353f8ull, 0x4065bc5c1d433c7eull }, + { 0x401dc5a1cac08313ull, 0x4065c037d403f8b7ull }, + { 0x401dc6a7ef9db22dull, 0x4065c4143a183e57ull }, + { 0x401dc7ae147ae148ull, 0x4065c7f14f9f2c89ull }, + { 0x401dc8b439581062ull, 0x4065cbcf14b7e7f5ull }, + { 0x401dc9ba5e353f7dull, 0x4065cfad89819ad6ull }, + { 0x401dcac083126e98ull, 0x4065d38cae1b74e4ull }, + { 0x401dcbc6a7ef9db2ull, 0x4065d76c82a4ab60ull }, + { 0x401dcccccccccccdull, 0x4065db4d073c7921ull }, + { 0x401dcdd2f1a9fbe8ull, 0x4065df2e3c021e7full }, + { 0x401dced916872b02ull, 0x4065e3102114e15cull }, + { 0x401dcfdf3b645a1dull, 0x4065e6f2b6940d34ull }, + { 0x401dd0e560418937ull, 0x4065ead5fc9ef301ull }, + { 0x401dd1eb851eb852ull, 0x4065eeb9f354e95cull }, + { 0x401dd2f1a9fbe76dull, 0x4065f29e9ad54c61ull }, + { 0x401dd3f7ced91687ull, 0x4065f683f33f7dc0ull }, + { 0x401dd4fdf3b645a2ull, 0x4065fa69fcb2e4c4ull }, + { 0x401dd604189374bdull, 0x4065fe50b74eee40ull }, + { 0x401dd70a3d70a3d7ull, 0x4066023823330c9cull }, + { 0x401dd810624dd2f2ull, 0x40660620407eb7e0ull }, + { 0x401dd916872b020cull, 0x40660a090f516d9aull }, + { 0x401dda1cac083127ull, 0x40660df28fcab0ffull }, + { 0x401ddb22d0e56042ull, 0x406611dcc20a0ad0ull }, + { 0x401ddc28f5c28f5cull, 0x406615c7a62f0968ull }, + { 0x401ddd2f1a9fbe77ull, 0x406619b33c5940c6ull }, + { 0x401dde353f7ced92ull, 0x40661d9f84a84a7aull }, + { 0x401ddf3b645a1cacull, 0x4066218c7f3bc5afull }, + { 0x401de04189374bc7ull, 0x4066257a2c335739ull }, + { 0x401de147ae147ae1ull, 0x406629688baea979ull }, + { 0x401de24dd2f1a9fcull, 0x40662d579dcd6c81ull }, + { 0x401de353f7ced917ull, 0x4066314762af55f6ull }, + { 0x401de45a1cac0831ull, 0x40663537da74211dull }, + { 0x401de5604189374cull, 0x40663929053b8ee9ull }, + { 0x401de66666666667ull, 0x40663d1ae32565e6ull }, + { 0x401de76c8b439581ull, 0x4066410d74517243ull }, + { 0x401de872b020c49cull, 0x40664500b8df85deull }, + { 0x401de978d4fdf3b6ull, 0x406648f4b0ef782eull }, + { 0x401dea7ef9db22d1ull, 0x40664ce95ca1265eull }, + { 0x401deb851eb851ecull, 0x406650debc147337ull }, + { 0x401dec8b43958106ull, 0x406654d4cf69472bull }, + { 0x401ded916872b021ull, 0x406658cb96bf9060ull }, + { 0x401dee978d4fdf3cull, 0x40665cc31237429eull }, + { 0x401def9db22d0e56ull, 0x406660bb41f05756ull }, + { 0x401df0a3d70a3d71ull, 0x406664b4260acdb3ull }, + { 0x401df1a9fbe76c8bull, 0x406668adbea6aa7full }, + { 0x401df2b020c49ba6ull, 0x40666ca80be3f842ull }, + { 0x401df3b645a1cac1ull, 0x406670a30de2c727ull }, + { 0x401df4bc6a7ef9dbull, 0x4066749ec4c32d0eull }, + { 0x401df5c28f5c28f6ull, 0x4066789b30a54590ull }, + { 0x401df6c8b4395811ull, 0x40667c9851a931efull }, + { 0x401df7ced916872bull, 0x4066809627ef1923ull }, + { 0x401df8d4fdf3b646ull, 0x40668494b39727e3ull }, + { 0x401df9db22d0e560ull, 0x40668893f4c1908bull }, + { 0x401dfae147ae147bull, 0x40668c93eb8e8b41ull }, + { 0x401dfbe76c8b4396ull, 0x40669094981e55d5ull }, + { 0x401dfced916872b0ull, 0x40669495fa9133d1ull }, + { 0x401dfdf3b645a1cbull, 0x4066989813076e85ull }, + { 0x401dfef9db22d0e6ull, 0x40669c9ae1a154f0ull }, + { 0x401e000000000000ull, 0x4066a09e667f3bcdull }, + { 0x401e010624dd2f1bull, 0x4066a4a2a1c17da0ull }, + { 0x401e020c49ba5e35ull, 0x4066a8a793887a9cull }, + { 0x401e03126e978d50ull, 0x4066acad3bf498c2ull }, + { 0x401e04189374bc6bull, 0x4066b0b39b2643caull }, + { 0x401e051eb851eb85ull, 0x4066b4bab13ded2aull }, + { 0x401e0624dd2f1aa0ull, 0x4066b8c27e5c0c27ull }, + { 0x401e072b020c49bbull, 0x4066bccb02a11dbfull }, + { 0x401e083126e978d5ull, 0x4066c0d43e2da4b2ull }, + { 0x401e09374bc6a7f0ull, 0x4066c4de31222994ull }, + { 0x401e0a3d70a3d70aull, 0x4066c8e8db9f3aadull }, + { 0x401e0b4395810625ull, 0x4066ccf43dc56c1eull }, + { 0x401e0c49ba5e3540ull, 0x4066d10057b557c3ull }, + { 0x401e0d4fdf3b645aull, 0x4066d50d298f9d43ull }, + { 0x401e0e5604189375ull, 0x4066d91ab374e219ull }, + { 0x401e0f5c28f5c290ull, 0x4066dd28f585d184ull }, + { 0x401e10624dd2f1aaull, 0x4066e137efe31c8aull }, + { 0x401e116872b020c5ull, 0x4066e547a2ad7a0full }, + { 0x401e126e978d4fdfull, 0x4066e9580e05a6b4ull }, + { 0x401e1374bc6a7efaull, 0x4066ed69320c64f8ull }, + { 0x401e147ae147ae15ull, 0x4066f17b0ee27d21ull }, + { 0x401e15810624dd2full, 0x4066f58da4a8bd47ull }, + { 0x401e16872b020c4aull, 0x4066f9a0f37ff95eull }, + { 0x401e178d4fdf3b65ull, 0x4066fdb4fb890b24ull }, + { 0x401e189374bc6a7full, 0x406701c9bce4d22cull }, + { 0x401e19999999999aull, 0x406705df37b433e8ull }, + { 0x401e1a9fbe76c8b4ull, 0x406709f56c181b95ull }, + { 0x401e1ba5e353f7cfull, 0x40670e0c5a317a54ull }, + { 0x401e1cac083126eaull, 0x4067122402214716ull }, + { 0x401e1db22d0e5604ull, 0x4067163c64087ea4ull }, + { 0x401e1eb851eb851full, 0x40671a55800823afull }, + { 0x401e1fbe76c8b43aull, 0x40671e6f56413eb7ull }, + { 0x401e20c49ba5e354ull, 0x40672289e6d4de1aull }, + { 0x401e21cac083126full, 0x406726a531e41621ull }, + { 0x401e22d0e5604189ull, 0x40672ac1379000e2ull }, + { 0x401e23d70a3d70a4ull, 0x40672eddf7f9be65ull }, + { 0x401e24dd2f1a9fbfull, 0x406732fb73427486ull }, + { 0x401e25e353f7ced9ull, 0x40673719a98b4f04ull }, + { 0x401e26e978d4fdf4ull, 0x40673b389af57f8eull }, + { 0x401e27ef9db22d0full, 0x40673f5847a23daaull }, + { 0x401e28f5c28f5c29ull, 0x40674378afb2c6c5ull }, + { 0x401e29fbe76c8b44ull, 0x40674799d3485e3cull }, + { 0x401e2b020c49ba5eull, 0x40674bbbb2844d46ull }, + { 0x401e2c083126e979ull, 0x40674fde4d87e312ull }, + { 0x401e2d0e56041894ull, 0x40675401a47474acull }, + { 0x401e2e147ae147aeull, 0x40675825b76b5d0aull }, + { 0x401e2f1a9fbe76c9ull, 0x40675c4a868dfd1dull }, + { 0x401e3020c49ba5e4ull, 0x4067607011fdbbb2ull }, + { 0x401e3126e978d4feull, 0x4067649659dc0588ull }, + { 0x401e322d0e560419ull, 0x406768bd5e4a4d57ull }, + { 0x401e333333333333ull, 0x40676ce51f6a0bb7ull }, + { 0x401e34395810624eull, 0x4067710d9d5cbf41ull }, + { 0x401e353f7ced9169ull, 0x40677536d843ec75ull }, + { 0x401e3645a1cac083ull, 0x40677960d0411dc4ull }, + { 0x401e374bc6a7ef9eull, 0x40677d8b8575e3a1ull }, + { 0x401e3851eb851eb8ull, 0x406781b6f803d464ull }, + { 0x401e395810624dd3ull, 0x406785e3280c8c6bull }, + { 0x401e3a5e353f7ceeull, 0x40678a1015b1adffull }, + { 0x401e3b645a1cac08ull, 0x40678e3dc114e162ull }, + { 0x401e3c6a7ef9db23ull, 0x4067926c2a57d4daull }, + { 0x401e3d70a3d70a3eull, 0x4067969b519c3c9full }, + { 0x401e3e76c8b43958ull, 0x40679acb3703d2dfull }, + { 0x401e3f7ced916873ull, 0x40679efbdab057d7ull }, + { 0x401e4083126e978dull, 0x4067a32d3cc391adull }, + { 0x401e4189374bc6a8ull, 0x4067a75f5d5f4c97ull }, + { 0x401e428f5c28f5c3ull, 0x4067ab923ca55ac0ull }, + { 0x401e4395810624ddull, 0x4067afc5dab79453ull }, + { 0x401e449ba5e353f8ull, 0x4067b3fa37b7d788ull }, + { 0x401e45a1cac08313ull, 0x4067b82f53c80890ull }, + { 0x401e46a7ef9db22dull, 0x4067bc652f0a119full }, + { 0x401e47ae147ae148ull, 0x4067c09bc99fe2f9ull }, + { 0x401e48b439581062ull, 0x4067c4d323ab72dbull }, + { 0x401e49ba5e353f7dull, 0x4067c90b3d4ebd97ull }, + { 0x401e4ac083126e98ull, 0x4067cd4416abc57dull }, + { 0x401e4bc6a7ef9db2ull, 0x4067d17dafe492e4ull }, + { 0x401e4ccccccccccdull, 0x4067d5b8091b343dull }, + { 0x401e4dd2f1a9fbe8ull, 0x4067d9f32271bdf5ull }, + { 0x401e4ed916872b02ull, 0x4067de2efc0a4a87ull }, + { 0x401e4fdf3b645a1dull, 0x4067e26b9606fa8aull }, + { 0x401e50e560418937ull, 0x4067e6a8f089f491ull }, + { 0x401e51eb851eb852ull, 0x4067eae70bb5654full }, + { 0x401e52f1a9fbe76dull, 0x4067ef25e7ab7f7dull }, + { 0x401e53f7ced91687ull, 0x4067f365848e7be6ull }, + { 0x401e54fdf3b645a2ull, 0x4067f7a5e2809974ull }, + { 0x401e5604189374bdull, 0x4067fbe701a41d18ull }, + { 0x401e570a3d70a3d7ull, 0x40680028e21b51daull }, + { 0x401e5810624dd2f2ull, 0x4068046b840888e3ull }, + { 0x401e5916872b020cull, 0x406808aee78e1965ull }, + { 0x401e5a1cac083127ull, 0x40680cf30cce60b9ull }, + { 0x401e5b22d0e56042ull, 0x40681137f3ebc245ull }, + { 0x401e5c28f5c28f5cull, 0x4068157d9d08a78dull }, + { 0x401e5d2f1a9fbe77ull, 0x406819c408478039ull }, + { 0x401e5e353f7ced92ull, 0x40681e0b35cac203ull }, + { 0x401e5f3b645a1cacull, 0x4068225325b4e8c5ull }, + { 0x401e604189374bc7ull, 0x4068269bd8287680ull }, + { 0x401e6147ae147ae1ull, 0x40682ae54d47f349ull }, + { 0x401e624dd2f1a9fcull, 0x40682f2f8535ed65ull }, + { 0x401e6353f7ced917ull, 0x4068337a8014f92eull }, + { 0x401e645a1cac0831ull, 0x406837c63e07b121ull }, + { 0x401e65604189374cull, 0x40683c12bf30b5efull }, + { 0x401e666666666667ull, 0x4068406003b2ae5full }, + { 0x401e676c8b439581ull, 0x406844ae0bb0475full }, + { 0x401e6872b020c49cull, 0x406848fcd74c3414ull }, + { 0x401e6978d4fdf3b6ull, 0x40684d4c66a92db6ull }, + { 0x401e6a7ef9db22d1ull, 0x4068519cb9e9f3bdull }, + { 0x401e6b851eb851ecull, 0x406855edd1314bbdull }, + { 0x401e6c8b43958106ull, 0x40685a3faca20175ull }, + { 0x401e6d916872b021ull, 0x40685e924c5ee6dfull }, + { 0x401e6e978d4fdf3cull, 0x406862e5b08ad417ull }, + { 0x401e6f9db22d0e56ull, 0x40686739d948a769ull }, + { 0x401e70a3d70a3d71ull, 0x40686b8ec6bb455cull }, + { 0x401e71a9fbe76c8bull, 0x40686fe479059898ull }, + { 0x401e72b020c49ba6ull, 0x4068743af04a920cull }, + { 0x401e73b645a1cac1ull, 0x406878922cad28c9ull }, + { 0x401e74bc6a7ef9dbull, 0x40687cea2e505a19ull }, + { 0x401e75c28f5c28f6ull, 0x40688142f5572987ull }, + { 0x401e76c8b4395811ull, 0x4068859c81e4a0c7ull }, + { 0x401e77ced916872bull, 0x406889f6d41bcfc9ull }, + { 0x401e78d4fdf3b646ull, 0x40688e51ec1fccbfull }, + { 0x401e79db22d0e560ull, 0x406892adca13b406ull }, + { 0x401e7ae147ae147bull, 0x4068970a6e1aa848ull }, + { 0x401e7be76c8b4396ull, 0x40689b67d857d25full }, + { 0x401e7ced916872b0ull, 0x40689fc608ee6162ull }, + { 0x401e7df3b645a1cbull, 0x4068a42500018ab4ull }, + { 0x401e7ef9db22d0e6ull, 0x4068a884bdb489eaull }, + { 0x401e800000000000ull, 0x4068ace5422aa0dbull }, + { 0x401e810624dd2f1bull, 0x4068b1468d8717aeull }, + { 0x401e820c49ba5e35ull, 0x4068b5a89fed3cbbull }, + { 0x401e83126e978d50ull, 0x4068ba0b798064aeull }, + { 0x401e84189374bc6bull, 0x4068be6f1a63ea6full }, + { 0x401e851eb851eb85ull, 0x4068c2d382bb2f2bull }, + { 0x401e8624dd2f1aa0ull, 0x4068c738b2a99a62ull }, + { 0x401e872b020c49bbull, 0x4068cb9eaa5299d2ull }, + { 0x401e883126e978d5ull, 0x4068d00569d9a182ull }, + { 0x401e89374bc6a7f0ull, 0x4068d46cf1622bd2ull }, + { 0x401e8a3d70a3d70aull, 0x4068d8d5410fb95cull }, + { 0x401e8b4395810625ull, 0x4068dd3e5905d119ull }, + { 0x401e8c49ba5e3540ull, 0x4068e1a839680043ull }, + { 0x401e8d4fdf3b645aull, 0x4068e612e259da63ull }, + { 0x401e8e5604189375ull, 0x4068ea7e53fef962ull }, + { 0x401e8f5c28f5c290ull, 0x4068eeea8e7afd6bull }, + { 0x401e90624dd2f1aaull, 0x4068f35791f18cfeull }, + { 0x401e916872b020c5ull, 0x4068f7c55e8654fbull }, + { 0x401e926e978d4fdfull, 0x4068fc33f45d0885ull }, + { 0x401e9374bc6a7efaull, 0x406900a353996129ull }, + { 0x401e947ae147ae15ull, 0x406905137c5f1ebcull }, + { 0x401e95810624dd2full, 0x406909846ed2076cull }, + { 0x401e96872b020c4aull, 0x40690df62b15e7cfull }, + { 0x401e978d4fdf3b65ull, 0x40691268b14e92c8ull }, + { 0x401e989374bc6a7full, 0x406916dc019fe196ull }, + { 0x401e99999999999aull, 0x40691b501c2db3e0ull }, + { 0x401e9a9fbe76c8b4ull, 0x40691fc5011bef9eull }, + { 0x401e9ba5e353f7cfull, 0x4069243ab08e8135ull }, + { 0x401e9cac083126eaull, 0x406928b12aa95b5full }, + { 0x401e9db22d0e5604ull, 0x40692d286f907737ull }, + { 0x401e9eb851eb851full, 0x406931a07f67d448ull }, + { 0x401e9fbe76c8b43aull, 0x406936195a537874ull }, + { 0x401ea0c49ba5e354ull, 0x40693a9300777002ull }, + { 0x401ea1cac083126full, 0x40693f0d71f7cdabull }, + { 0x401ea2d0e5604189ull, 0x40694388aef8aa7full }, + { 0x401ea3d70a3d70a4ull, 0x40694804b79e2607ull }, + { 0x401ea4dd2f1a9fbfull, 0x40694c818c0c662aull }, + { 0x401ea5e353f7ced9ull, 0x406950ff2c679737ull }, + { 0x401ea6e978d4fdf4ull, 0x4069557d98d3ebf9ull }, + { 0x401ea7ef9db22d0full, 0x406959fcd1759d98ull }, + { 0x401ea8f5c28f5c29ull, 0x40695e7cd670ebabull }, + { 0x401ea9fbe76c8b44ull, 0x406962fda7ea1c47ull }, + { 0x401eab020c49ba5eull, 0x4069677f46057bddull }, + { 0x401eac083126e979ull, 0x40696c01b0e75d62ull }, + { 0x401ead0e56041894ull, 0x40697084e8b41a33ull }, + { 0x401eae147ae147aeull, 0x40697508ed90121cull }, + { 0x401eaf1a9fbe76c9ull, 0x4069798dbf9fab70ull }, + { 0x401eb020c49ba5e4ull, 0x40697e135f0752e7ull }, + { 0x401eb126e978d4feull, 0x40698299cbeb7bb3ull }, + { 0x401eb22d0e560419ull, 0x4069872106709f89ull }, + { 0x401eb33333333333ull, 0x40698ba90ebb3e89ull }, + { 0x401eb4395810624eull, 0x40699031e4efdf5bull }, + { 0x401eb53f7ced9169ull, 0x406994bb89330f1bull }, + { 0x401eb645a1cac083ull, 0x40699945fba9615dull }, + { 0x401eb74bc6a7ef9eull, 0x40699dd13c777043ull }, + { 0x401eb851eb851eb8ull, 0x4069a25d4bc1dc5cull }, + { 0x401eb95810624dd3ull, 0x4069a6ea29ad4ccaull }, + { 0x401eba5e353f7ceeull, 0x4069ab77d65e6f21ull }, + { 0x401ebb645a1cac08ull, 0x4069b00651f9f779ull }, + { 0x401ebc6a7ef9db23ull, 0x4069b4959ca4a07dull }, + { 0x401ebd70a3d70a3eull, 0x4069b925b6832b4cull }, + { 0x401ebe76c8b43958ull, 0x4069bdb69fba5f8full }, + { 0x401ebf7ced916873ull, 0x4069c248586f0b80ull }, + { 0x401ec083126e978dull, 0x4069c6dae0c603d4ull }, + { 0x401ec189374bc6a8ull, 0x4069cb6e38e423d7ull }, + { 0x401ec28f5c28f5c3ull, 0x4069d00260ee4d55ull }, + { 0x401ec395810624ddull, 0x4069d497590968a6ull }, + { 0x401ec49ba5e353f8ull, 0x4069d92d215a64bbull }, + { 0x401ec5a1cac08313ull, 0x4069ddc3ba063709ull }, + { 0x401ec6a7ef9db22dull, 0x4069e25b2331db91ull }, + { 0x401ec7ae147ae148ull, 0x4069e6f35d0254f3ull }, + { 0x401ec8b439581062ull, 0x4069eb8c679cac51ull }, + { 0x401ec9ba5e353f7dull, 0x4069f0264325f16full }, + { 0x401ecac083126e98ull, 0x4069f4c0efc33a9aull }, + { 0x401ecbc6a7ef9db2ull, 0x4069f95c6d99a4b3ull }, + { 0x401ecccccccccccdull, 0x4069fdf8bcce533eull }, + { 0x401ecdd2f1a9fbe8ull, 0x406a0295dd86704dull }, + { 0x401eced916872b02ull, 0x406a0733cfe72c86ull }, + { 0x401ecfdf3b645a1dull, 0x406a0bd29415bf39ull }, + { 0x401ed0e560418937ull, 0x406a10722a376640ull }, + { 0x401ed1eb851eb852ull, 0x406a151292716622ull }, + { 0x401ed2f1a9fbe76dull, 0x406a19b3cce909f5ull }, + { 0x401ed3f7ced91687ull, 0x406a1e55d9c3a370ull }, + { 0x401ed4fdf3b645a2ull, 0x406a22f8b9268af6ull }, + { 0x401ed604189374bdull, 0x406a279c6b371f7eull }, + { 0x401ed70a3d70a3d7ull, 0x406a2c40f01ac6a1ull }, + { 0x401ed810624dd2f2ull, 0x406a30e647f6eca9ull }, + { 0x401ed916872b020cull, 0x406a358c72f10472ull }, + { 0x401eda1cac083127ull, 0x406a3a33712e8790ull }, + { 0x401edb22d0e56042ull, 0x406a3edb42d4f631ull }, + { 0x401edc28f5c28f5cull, 0x406a4383e809d72bull }, + { 0x401edd2f1a9fbe77ull, 0x406a482d60f2b80aull }, + { 0x401ede353f7ced92ull, 0x406a4cd7adb52cf8ull }, + { 0x401edf3b645a1cacull, 0x406a5182ce76d0caull }, + { 0x401ee04189374bc7ull, 0x406a562ec35d450full }, + { 0x401ee147ae147ae1ull, 0x406a5adb8c8e31f3ull }, + { 0x401ee24dd2f1a9fcull, 0x406a5f892a2f4662ull }, + { 0x401ee353f7ced917ull, 0x406a64379c6637edull }, + { 0x401ee45a1cac0831ull, 0x406a68e6e358c2d4ull }, + { 0x401ee5604189374cull, 0x406a6d96ff2caa18ull }, + { 0x401ee66666666667ull, 0x406a7247f007b762ull }, + { 0x401ee76c8b439581ull, 0x406a76f9b60fbb0full }, + { 0x401ee872b020c49cull, 0x406a7bac516a8c3full }, + { 0x401ee978d4fdf3b6ull, 0x406a805fc23e08b9ull }, + { 0x401eea7ef9db22d1ull, 0x406a851408b0150eull }, + { 0x401eeb851eb851ecull, 0x406a89c924e69c7bull }, + { 0x401eec8b43958106ull, 0x406a8e7f170790faull }, + { 0x401eed916872b021ull, 0x406a9335df38eb4cull }, + { 0x401eee978d4fdf3cull, 0x406a97ed7da0aae5ull }, + { 0x401eef9db22d0e56ull, 0x406a9ca5f264d5f4ull }, + { 0x401ef0a3d70a3d71ull, 0x406aa15f3dab797aull }, + { 0x401ef1a9fbe76c8bull, 0x406aa6195f9aa922ull }, + { 0x401ef2b020c49ba6ull, 0x406aaad458587f70ull }, + { 0x401ef3b645a1cac1ull, 0x406aaf90280b1d9cull }, + { 0x401ef4bc6a7ef9dbull, 0x406ab44cced8aba0ull }, + { 0x401ef5c28f5c28f6ull, 0x406ab90a4ce7584full }, + { 0x401ef6c8b4395811ull, 0x406abdc8a25d5931ull }, + { 0x401ef7ced916872bull, 0x406ac287cf60ea98ull }, + { 0x401ef8d4fdf3b646ull, 0x406ac747d4184faeull }, + { 0x401ef9db22d0e560ull, 0x406acc08b0a9d252ull }, + { 0x401efae147ae147bull, 0x406ad0ca653bc346ull }, + { 0x401efbe76c8b4396ull, 0x406ad58cf1f47a09ull }, + { 0x401efced916872b0ull, 0x406ada5056fa54e6ull }, + { 0x401efdf3b645a1cbull, 0x406adf149473b90aull }, + { 0x401efef9db22d0e6ull, 0x406ae3d9aa871262ull }, + { 0x401f000000000000ull, 0x406ae89f995ad3adull }, + { 0x401f010624dd2f1bull, 0x406aed666115768cull }, + { 0x401f020c49ba5e35ull, 0x406af22e01dd7b62ull }, + { 0x401f03126e978d50ull, 0x406af6f67bd96978ull }, + { 0x401f04189374bc6bull, 0x406afbbfcf2fcee3ull }, + { 0x401f051eb851eb85ull, 0x406b0089fc07408dull }, + { 0x401f0624dd2f1aa0ull, 0x406b055502865a4aull }, + { 0x401f072b020c49bbull, 0x406b0a20e2d3beb8ull }, + { 0x401f083126e978d5ull, 0x406b0eed9d161753ull }, + { 0x401f09374bc6a7f0ull, 0x406b13bb3174147full }, + { 0x401f0a3d70a3d70aull, 0x406b1889a0146d6eull }, + { 0x401f0b4395810625ull, 0x406b1d58e91de043ull }, + { 0x401f0c49ba5e3540ull, 0x406b22290cb731f4ull }, + { 0x401f0d4fdf3b645aull, 0x406b26fa0b072e57ull }, + { 0x401f0e5604189375ull, 0x406b2bcbe434a836ull }, + { 0x401f0f5c28f5c290ull, 0x406b309e9866792eull }, + { 0x401f10624dd2f1aaull, 0x406b357227c381c4ull }, + { 0x401f116872b020c5ull, 0x406b3a469272a96full }, + { 0x401f126e978d4fdfull, 0x406b3f1bd89ade7full }, + { 0x401f1374bc6a7efaull, 0x406b43f1fa63163cull }, + { 0x401f147ae147ae15ull, 0x406b48c8f7f24ccdull }, + { 0x401f15810624dd2full, 0x406b4da0d16f8542ull }, + { 0x401f16872b020c4aull, 0x406b52798701c9a8ull }, + { 0x401f178d4fdf3b65ull, 0x406b575318d02aebull }, + { 0x401f189374bc6a7full, 0x406b5c2d8701c0e6ull }, + { 0x401f19999999999aull, 0x406b6108d1bdaa74ull }, + { 0x401f1a9fbe76c8b4ull, 0x406b65e4f92b0d4eull }, + { 0x401f1ba5e353f7cfull, 0x406b6ac1fd711631ull }, + { 0x401f1cac083126eaull, 0x406b6f9fdeb6f8c3ull }, + { 0x401f1db22d0e5604ull, 0x406b747e9d23ef9dull }, + { 0x401f1eb851eb851full, 0x406b795e38df3c60ull }, + { 0x401f1fbe76c8b43aull, 0x406b7e3eb2102792ull }, + { 0x401f20c49ba5e354ull, 0x406b832008de00b6ull }, + { 0x401f21cac083126full, 0x406b88023d701e56ull }, + { 0x401f22d0e5604189ull, 0x406b8ce54feddde5ull }, + { 0x401f23d70a3d70a4ull, 0x406b91c9407ea3e5ull }, + { 0x401f24dd2f1a9fbfull, 0x406b96ae0f49dbcaull }, + { 0x401f25e353f7ced9ull, 0x406b9b93bc76f804ull }, + { 0x401f26e978d4fdf4ull, 0x406ba07a482d7214ull }, + { 0x401f27ef9db22d0full, 0x406ba561b294ca70ull }, + { 0x401f28f5c28f5c29ull, 0x406baa49fbd4888cull }, + { 0x401f29fbe76c8b44ull, 0x406baf3324143af4ull }, + { 0x401f2b020c49ba5eull, 0x406bb41d2b7b7724ull }, + { 0x401f2c083126e979ull, 0x406bb9081231d9b1ull }, + { 0x401f2d0e56041894ull, 0x406bbdf3d85f062cull }, + { 0x401f2e147ae147aeull, 0x406bc2e07e2aa72dull }, + { 0x401f2f1a9fbe76c9ull, 0x406bc7ce03bc6e66ull }, + { 0x401f3020c49ba5e4ull, 0x406bccbc693c1486ull }, + { 0x401f3126e978d4feull, 0x406bd1abaed1594bull }, + { 0x401f322d0e560419ull, 0x406bd69bd4a4038cull }, + { 0x401f333333333333ull, 0x406bdb8cdadbe11full }, + { 0x401f34395810624eull, 0x406be07ec1a0c6feull }, + { 0x401f353f7ced9169ull, 0x406be571891a9124ull }, + { 0x401f3645a1cac083ull, 0x406bea65317122a3ull }, + { 0x401f374bc6a7ef9eull, 0x406bef59bacc65aeull }, + { 0x401f3851eb851eb9ull, 0x406bf44f25544b7eull }, + { 0x401f395810624dd3ull, 0x406bf9457130cc66ull }, + { 0x401f3a5e353f7ceeull, 0x406bfe3c9e89e7deull }, + { 0x401f3b645a1cac08ull, 0x406c0334ad87a464ull }, + { 0x401f3c6a7ef9db23ull, 0x406c082d9e520fa5ull }, + { 0x401f3d70a3d70a3eull, 0x406c0d2771113e5cull }, + { 0x401f3e76c8b43958ull, 0x406c122225ed4c60ull }, + { 0x401f3f7ced916873ull, 0x406c171dbd0e5cb5ull }, + { 0x401f4083126e978dull, 0x406c1c1a369c996full }, + { 0x401f4189374bc6a8ull, 0x406c211792c033d1ull }, + { 0x401f428f5c28f5c3ull, 0x406c2615d1a16437ull }, + { 0x401f4395810624ddull, 0x406c2b14f3686a1dull }, + { 0x401f449ba5e353f8ull, 0x406c3014f83d8c34ull }, + { 0x401f45a1cac08313ull, 0x406c3515e0491845ull }, + { 0x401f46a7ef9db22dull, 0x406c3a17abb36340ull }, + { 0x401f47ae147ae148ull, 0x406c3f1a5aa4c94dull }, + { 0x401f48b439581062ull, 0x406c441ded45ada9ull }, + { 0x401f49ba5e353f7dull, 0x406c492263be7ad0ull }, + { 0x401f4ac083126e98ull, 0x406c4e27be37a25full }, + { 0x401f4bc6a7ef9db2ull, 0x406c532dfcd99d1dull }, + { 0x401f4ccccccccccdull, 0x406c58351fcceb11ull }, + { 0x401f4dd2f1a9fbe8ull, 0x406c5d3d273a1362ull }, + { 0x401f4ed916872b02ull, 0x406c62461349a46aull }, + { 0x401f4fdf3b645a1dull, 0x406c674fe42433c6ull }, + { 0x401f50e560418937ull, 0x406c6c5a99f25e2full }, + { 0x401f51eb851eb852ull, 0x406c716634dcc7adull }, + { 0x401f52f1a9fbe76dull, 0x406c7672b50c1b6bull }, + { 0x401f53f7ced91687ull, 0x406c7b801aa90bcfull }, + { 0x401f54fdf3b645a2ull, 0x406c808e65dc5286ull }, + { 0x401f5604189374bdull, 0x406c859d96ceb06aull }, + { 0x401f570a3d70a3d7ull, 0x406c8aadada8ed8full }, + { 0x401f5810624dd2f2ull, 0x406c8fbeaa93d957ull }, + { 0x401f5916872b020cull, 0x406c94d08db84a4dull }, + { 0x401f5a1cac083127ull, 0x406c99e3573f1e50ull }, + { 0x401f5b22d0e56042ull, 0x406c9ef707513a73ull }, + { 0x401f5c28f5c28f5cull, 0x406ca40b9e178b08ull }, + { 0x401f5d2f1a9fbe77ull, 0x406ca9211bbb03b7ull }, + { 0x401f5e353f7ced92ull, 0x406cae3780649f5bull }, + { 0x401f5f3b645a1cacull, 0x406cb34ecc3d6017ull }, + { 0x401f604189374bc7ull, 0x406cb866ff6e4f65ull }, + { 0x401f6147ae147ae1ull, 0x406cbd801a207df2ull }, + { 0x401f624dd2f1a9fcull, 0x406cc29a1c7d03caull }, + { 0x401f6353f7ced917ull, 0x406cc7b506ad0035ull }, + { 0x401f645a1cac0831ull, 0x406cccd0d8d999c8ull }, + { 0x401f65604189374cull, 0x406cd1ed932bfe76ull }, + { 0x401f666666666667ull, 0x406cd70b35cd636full }, + { 0x401f676c8b439581ull, 0x406cdc29c0e70539ull }, + { 0x401f6872b020c49cull, 0x406ce14934a227b4ull }, + { 0x401f6978d4fdf3b6ull, 0x406ce66991281606ull }, + { 0x401f6a7ef9db22d1ull, 0x406ceb8ad6a222b8ull }, + { 0x401f6b851eb851ecull, 0x406cf0ad0539a79dull }, + { 0x401f6c8b43958106ull, 0x406cf5d01d1805deull }, + { 0x401f6d916872b021ull, 0x406cfaf41e66a60cull }, + { 0x401f6e978d4fdf3cull, 0x406d0019094ef800ull }, + { 0x401f6f9db22d0e56ull, 0x406d053eddfa72f3ull }, + { 0x401f70a3d70a3d71ull, 0x406d0a659c929584ull }, + { 0x401f71a9fbe76c8bull, 0x406d0f8d4540e59full }, + { 0x401f72b020c49ba6ull, 0x406d14b5d82ef0a1ull }, + { 0x401f73b645a1cac1ull, 0x406d19df55864b3aull }, + { 0x401f74bc6a7ef9dbull, 0x406d1f09bd70917bull }, + { 0x401f75c28f5c28f6ull, 0x406d2435101766e5ull }, + { 0x401f76c8b4395811ull, 0x406d29614da47650ull }, + { 0x401f77ced916872bull, 0x406d2e8e764171f9ull }, + { 0x401f78d4fdf3b646ull, 0x406d33bc8a181393ull }, + { 0x401f79db22d0e560ull, 0x406d38eb89521c24ull }, + { 0x401f7ae147ae147bull, 0x406d3e1b74195431ull }, + { 0x401f7be76c8b4396ull, 0x406d434c4a978b9aull }, + { 0x401f7ced916872b0ull, 0x406d487e0cf699aaull }, + { 0x401f7df3b645a1cbull, 0x406d4db0bb605d29ull }, + { 0x401f7ef9db22d0e6ull, 0x406d52e455febc41ull }, + { 0x401f800000000000ull, 0x406d5818dcfba487ull }, + { 0x401f810624dd2f1bull, 0x406d5d4e50810b15ull }, + { 0x401f820c49ba5e35ull, 0x406d6284b0b8ec62ull }, + { 0x401f83126e978d50ull, 0x406d67bbfdcd4c6bull }, + { 0x401f84189374bc6bull, 0x406d6cf437e83696ull }, + { 0x401f851eb851eb85ull, 0x406d722d5f33bdbfull }, + { 0x401f8624dd2f1aa0ull, 0x406d776773d9fc47ull }, + { 0x401f872b020c49bbull, 0x406d7ca2760513fcull }, + { 0x401f883126e978d5ull, 0x406d81de65df2e25ull }, + { 0x401f89374bc6a7f0ull, 0x406d871b43927b93ull }, + { 0x401f8a3d70a3d70aull, 0x406d8c590f493483ull }, + { 0x401f8b4395810625ull, 0x406d9197c92d98c2ull }, + { 0x401f8c49ba5e3540ull, 0x406d96d77169ef8full }, + { 0x401f8d4fdf3b645aull, 0x406d9c18082887aaull }, + { 0x401f8e5604189375ull, 0x406da1598d93b764ull }, + { 0x401f8f5c28f5c290ull, 0x406da69c01d5dc84ull }, + { 0x401f90624dd2f1aaull, 0x406dabdf65195c55ull }, + { 0x401f916872b020c5ull, 0x406db123b788a3b9ull }, + { 0x401f926e978d4fdfull, 0x406db668f94e2704ull }, + { 0x401f9374bc6a7efaull, 0x406dbbaf2a946229ull }, + { 0x401f947ae147ae15ull, 0x406dc0f64b85d895ull }, + { 0x401f95810624dd2full, 0x406dc63e5c4d1542ull }, + { 0x401f96872b020c4aull, 0x406dcb875d14aac7ull }, + { 0x401f978d4fdf3b65ull, 0x406dd0d14e073337ull }, + { 0x401f989374bc6a7full, 0x406dd61c2f4f503aull }, + { 0x401f99999999999aull, 0x406ddb680117ab14ull }, + { 0x401f9a9fbe76c8b4ull, 0x406de0b4c38af48aull }, + { 0x401f9ba5e353f7cfull, 0x406de60276d3e508ull }, + { 0x401f9cac083126eaull, 0x406deb511b1d3c80ull }, + { 0x401f9db22d0e5604ull, 0x406df0a0b091c27bull }, + { 0x401f9eb851eb851full, 0x406df5f1375c462aull }, + { 0x401f9fbe76c8b43aull, 0x406dfb42afa79e47ull }, + { 0x401fa0c49ba5e354ull, 0x406e0095199ea926ull }, + { 0x401fa1cac083126full, 0x406e05e8756c4cc9ull }, + { 0x401fa2d0e5604189ull, 0x406e0b3cc33b76b8ull }, + { 0x401fa3d70a3d70a4ull, 0x406e109203371c30ull }, + { 0x401fa4dd2f1a9fbfull, 0x406e15e8358a39feull }, + { 0x401fa5e353f7ced9ull, 0x406e1b3f5a5fd490ull }, + { 0x401fa6e978d4fdf4ull, 0x406e209771e2f808ull }, + { 0x401fa7ef9db22d0full, 0x406e25f07c3eb81aull }, + { 0x401fa8f5c28f5c29ull, 0x406e2b4a799e3023ull }, + { 0x401fa9fbe76c8b44ull, 0x406e30a56a2c8333ull }, + { 0x401fab020c49ba5eull, 0x406e36014e14dbeeull }, + { 0x401fac083126e979ull, 0x406e3b5e25826cb9ull }, + { 0x401fad0e56041894ull, 0x406e40bbf0a06f92ull }, + { 0x401fae147ae147aeull, 0x406e461aaf9a2624ull }, + { 0x401faf1a9fbe76c9ull, 0x406e4b7a629ad9d6ull }, + { 0x401fb020c49ba5e4ull, 0x406e50db09cddbb0ull }, + { 0x401fb126e978d4feull, 0x406e563ca55e846bull }, + { 0x401fb22d0e560419ull, 0x406e5b9f3578347full }, + { 0x401fb33333333333ull, 0x406e6102ba465404ull }, + { 0x401fb4395810624eull, 0x406e666733f452dbull }, + { 0x401fb53f7ced9169ull, 0x406e6bcca2ada88bull }, + { 0x401fb645a1cac083ull, 0x406e7133069dd452ull }, + { 0x401fb74bc6a7ef9eull, 0x406e769a5ff05d37ull }, + { 0x401fb851eb851eb9ull, 0x406e7c02aed0d1ebull }, + { 0x401fb95810624dd3ull, 0x406e816bf36ac8daull }, + { 0x401fba5e353f7ceeull, 0x406e86d62de9e03cull }, + { 0x401fbb645a1cac08ull, 0x406e8c415e79bdf3ull }, + { 0x401fbc6a7ef9db23ull, 0x406e91ad85460fb5ull }, + { 0x401fbd70a3d70a3eull, 0x406e971aa27a8aeaull }, + { 0x401fbe76c8b43958ull, 0x406e9c88b642ecbbull }, + { 0x401fbf7ced916873ull, 0x406ea1f7c0cafa25ull }, + { 0x401fc083126e978dull, 0x406ea767c23e7fd3ull }, + { 0x401fc189374bc6a8ull, 0x406eacd8bac95250ull }, + { 0x401fc28f5c28f5c3ull, 0x406eb24aaa974ddaull }, + { 0x401fc395810624ddull, 0x406eb7bd91d4567aull }, + { 0x401fc49ba5e353f8ull, 0x406ebd3170ac5815ull }, + { 0x401fc5a1cac08313ull, 0x406ec2a6474b4648ull }, + { 0x401fc6a7ef9db22dull, 0x406ec81c15dd1c82ull }, + { 0x401fc7ae147ae148ull, 0x406ecd92dc8dde0dull }, + { 0x401fc8b439581062ull, 0x406ed30a9b8995efull }, + { 0x401fc9ba5e353f7dull, 0x406ed88352fc5715ull }, + { 0x401fcac083126e98ull, 0x406eddfd03123c2full }, + { 0x401fcbc6a7ef9db2ull, 0x406ee377abf767bfull }, + { 0x401fcccccccccccdull, 0x406ee8f34dd80430ull }, + { 0x401fcdd2f1a9fbe8ull, 0x406eee6fe8e043b1ull }, + { 0x401fced916872b02ull, 0x406ef3ed7d3c604bull }, + { 0x401fcfdf3b645a1dull, 0x406ef96c0b189bf0ull }, + { 0x401fd0e560418937ull, 0x406efeeb92a14058ull }, + { 0x401fd1eb851eb852ull, 0x406f046c14029f2eull }, + { 0x401fd2f1a9fbe76dull, 0x406f09ed8f6911e9ull }, + { 0x401fd3f7ced91687ull, 0x406f0f700500f9e1ull }, + { 0x401fd4fdf3b645a2ull, 0x406f14f374f6c05cull }, + { 0x401fd604189374bdull, 0x406f1a77df76d677ull }, + { 0x401fd70a3d70a3d7ull, 0x406f1ffd44adb52dull }, + { 0x401fd810624dd2f2ull, 0x406f2583a4c7dd71ull }, + { 0x401fd916872b020cull, 0x406f2b0afff1d808ull }, + { 0x401fda1cac083127ull, 0x406f3093565835b1ull }, + { 0x401fdb22d0e56042ull, 0x406f361ca8278f06ull }, + { 0x401fdc28f5c28f5cull, 0x406f3ba6f58c848cull }, + { 0x401fdd2f1a9fbe77ull, 0x406f41323eb3bec2ull }, + { 0x401fde353f7ced92ull, 0x406f46be83c9ee06ull }, + { 0x401fdf3b645a1cacull, 0x406f4c4bc4fbcaa5ull }, + { 0x401fe04189374bc7ull, 0x406f51da027614eaull }, + { 0x401fe147ae147ae1ull, 0x406f57693c6594ffull }, + { 0x401fe24dd2f1a9fcull, 0x406f5cf972f71b14ull }, + { 0x401fe353f7ced917ull, 0x406f628aa6577f3eull }, + { 0x401fe45a1cac0831ull, 0x406f681cd6b3a189ull }, + { 0x401fe5604189374cull, 0x406f6db004386a08ull }, + { 0x401fe66666666667ull, 0x406f73442f12c8b4ull }, + { 0x401fe76c8b439581ull, 0x406f78d9576fb584ull }, + { 0x401fe872b020c49cull, 0x406f7e6f7d7c3077ull }, + { 0x401fe978d4fdf3b6ull, 0x406f8406a1654176ull }, + { 0x401fea7ef9db22d1ull, 0x406f899ec357f87cull }, + { 0x401feb851eb851ecull, 0x406f8f37e3816d74ull }, + { 0x401fec8b43958106ull, 0x406f94d2020ec04cull }, + { 0x401fed916872b021ull, 0x406f9a6d1f2d1902ull }, + { 0x401fee978d4fdf3cull, 0x406fa0093b09a789ull }, + { 0x401fef9db22d0e56ull, 0x406fa5a655d1a3daull }, + { 0x401ff0a3d70a3d71ull, 0x406fab446fb24e04ull }, + { 0x401ff1a9fbe76c8bull, 0x406fb0e388d8ee08ull }, + { 0x401ff2b020c49ba6ull, 0x406fb683a172d409ull }, + { 0x401ff3b645a1cac1ull, 0x406fbc24b9ad5822ull }, + { 0x401ff4bc6a7ef9dbull, 0x406fc1c6d1b5da7dull }, + { 0x401ff5c28f5c28f6ull, 0x406fc769e9b9c35full }, + { 0x401ff6c8b4395811ull, 0x406fcd0e01e68310ull }, + { 0x401ff7ced916872bull, 0x406fd2b31a6991e5ull }, + { 0x401ff8d4fdf3b646ull, 0x406fd85933707059ull }, + { 0x401ff9db22d0e560ull, 0x406fde004d28a6e3ull }, + { 0x401ffae147ae147bull, 0x406fe3a867bfc624ull }, + { 0x401ffbe76c8b4396ull, 0x406fe951836366c6ull }, + { 0x401ffced916872b0ull, 0x406feefba0412988ull }, + { 0x401ffdf3b645a1cbull, 0x406ff4a6be86b756ull }, + { 0x401ffef9db22d0e6ull, 0x406ffa52de61c123ull }, + { 0x4020000000000000ull, 0x4070000000000000ull }, + { 0x40200083126e978dull, 0x407002d711c79a94ull }, + { 0x4020010624dd2f1bull, 0x407005aea49e94fcull }, + { 0x40200189374bc6a8ull, 0x40700886b89bd7e7ull }, + { 0x4020020c49ba5e35ull, 0x40700b5f4dd65026ull }, + { 0x4020028f5c28f5c3ull, 0x40700e386464ee9bull }, + { 0x402003126e978d50ull, 0x40701111fc5ea82bull }, + { 0x40200395810624ddull, 0x407013ec15da75dcull }, + { 0x402004189374bc6bull, 0x407016c6b0ef54ccull }, + { 0x4020049ba5e353f8ull, 0x407019a1cdb44619ull }, + { 0x4020051eb851eb85ull, 0x40701c7d6c404f0bull }, + { 0x402005a1cac08313ull, 0x40701f598caa78fdull }, + { 0x40200624dd2f1aa0ull, 0x407022362f09d150ull }, + { 0x402006a7ef9db22dull, 0x4070251353756991ull }, + { 0x4020072b020c49baull, 0x407027f0fa04575bull }, + { 0x402007ae147ae148ull, 0x40702acf22cdb46aull }, + { 0x4020083126e978d5ull, 0x40702dadcde89e83ull }, + { 0x402008b439581062ull, 0x4070308cfb6c3796ull }, + { 0x402009374bc6a7f0ull, 0x4070336cab6fa5aeull }, + { 0x402009ba5e353f7dull, 0x4070364cde0a12e2ull }, + { 0x40200a3d70a3d70aull, 0x4070392d9352ad74ull }, + { 0x40200ac083126e98ull, 0x40703c0ecb60a7c6ull }, + { 0x40200b4395810625ull, 0x40703ef0864b3846ull }, + { 0x40200bc6a7ef9db2ull, 0x407041d2c4299991ull }, + { 0x40200c49ba5e3540ull, 0x407044b585130a67ull }, + { 0x40200ccccccccccdull, 0x40704798c91ecd93ull }, + { 0x40200d4fdf3b645aull, 0x40704a7c90642a14ull }, + { 0x40200dd2f1a9fbe8ull, 0x40704d60dafa6b0bull }, + { 0x40200e5604189375ull, 0x40705045a8f8dfaaull }, + { 0x40200ed916872b02ull, 0x4070532afa76db57ull }, + { 0x40200f5c28f5c28full, 0x40705610cf8bb598ull }, + { 0x40200fdf3b645a1dull, 0x407058f7284eca1aull }, + { 0x402010624dd2f1aaull, 0x40705bde04d778a2ull }, + { 0x402010e560418937ull, 0x40705ec5653d252aull }, + { 0x4020116872b020c5ull, 0x407061ad499737d5ull }, + { 0x402011eb851eb852ull, 0x40706495b1fd1cd8ull }, + { 0x4020126e978d4fdfull, 0x4070677e9e8644a8ull }, + { 0x402012f1a9fbe76dull, 0x40706a680f4a23ddull }, + { 0x40201374bc6a7efaull, 0x40706d520460332cull }, + { 0x402013f7ced91687ull, 0x4070703c7ddfef84ull }, + { 0x4020147ae147ae15ull, 0x407073277be0d9ffull }, + { 0x402014fdf3b645a2ull, 0x40707612fe7a77d3ull }, + { 0x402015810624dd2full, 0x407078ff05c45272ull }, + { 0x40201604189374bdull, 0x40707beb91d5f77full }, + { 0x402016872b020c4aull, 0x40707ed8a2c6f8b7ull }, + { 0x4020170a3d70a3d7ull, 0x407081c638aeec18ull }, + { 0x4020178d4fdf3b64ull, 0x407084b453a56bccull }, + { 0x40201810624dd2f2ull, 0x407087a2f3c2162full }, + { 0x4020189374bc6a7full, 0x40708a92191c8dc2ull }, + { 0x40201916872b020cull, 0x40708d81c3cc7946ull }, + { 0x402019999999999aull, 0x40709071f3e983afull }, + { 0x40201a1cac083127ull, 0x40709362a98b5c15ull }, + { 0x40201a9fbe76c8b4ull, 0x40709653e4c9b5d4ull }, + { 0x40201b22d0e56042ull, 0x40709945a5bc487eull }, + { 0x40201ba5e353f7cfull, 0x40709c37ec7acfc8ull }, + { 0x40201c28f5c28f5cull, 0x40709f2ab91d0bb0ull }, + { 0x40201cac083126eaull, 0x4070a21e0bbac06bull }, + { 0x40201d2f1a9fbe77ull, 0x4070a511e46bb655ull }, + { 0x40201db22d0e5604ull, 0x4070a8064347ba12ull }, + { 0x40201e353f7ced92ull, 0x4070aafb28669c84ull }, + { 0x40201eb851eb851full, 0x4070adf093e032b1ull }, + { 0x40201f3b645a1cacull, 0x4070b0e685cc55edull }, + { 0x40201fbe76c8b439ull, 0x4070b3dcfe42e3c4ull }, + { 0x4020204189374bc7ull, 0x4070b6d3fd5bbe01ull }, + { 0x402020c49ba5e354ull, 0x4070b9cb832eca9dull }, + { 0x40202147ae147ae1ull, 0x4070bcc38fd3f3e1ull }, + { 0x402021cac083126full, 0x4070bfbc23632852ull }, + { 0x4020224dd2f1a9fcull, 0x4070c2b53df45aa5ull }, + { 0x402022d0e5604189ull, 0x4070c5aedf9f81e1ull }, + { 0x40202353f7ced917ull, 0x4070c8a9087c994cull }, + { 0x402023d70a3d70a4ull, 0x4070cba3b8a3a060ull }, + { 0x4020245a1cac0831ull, 0x4070ce9ef02c9ae6ull }, + { 0x402024dd2f1a9fbfull, 0x4070d19aaf2f90eeull }, + { 0x402025604189374cull, 0x4070d496f5c48ebbull }, + { 0x402025e353f7ced9ull, 0x4070d793c403a4e2ull }, + { 0x4020266666666666ull, 0x4070da911a04e83cull }, + { 0x402026e978d4fdf4ull, 0x4070dd8ef7e071edull }, + { 0x4020276c8b439581ull, 0x4070e08d5dae5f4dull }, + { 0x402027ef9db22d0eull, 0x4070e38c4b86d20full }, + { 0x40202872b020c49cull, 0x4070e68bc181f02eull }, + { 0x402028f5c28f5c29ull, 0x4070e98bbfb7e3ddull }, + { 0x40202978d4fdf3b6ull, 0x4070ec8c4640dbaaull }, + { 0x402029fbe76c8b44ull, 0x4070ef8d55350a6full }, + { 0x40202a7ef9db22d1ull, 0x4070f28eecaca740ull }, + { 0x40202b020c49ba5eull, 0x4070f5910cbfed8dull }, + { 0x40202b851eb851ecull, 0x4070f893b5871d16ull }, + { 0x40202c083126e979ull, 0x4070fb96e71a79d4ull }, + { 0x40202c8b43958106ull, 0x4070fe9aa1924c24ull }, + { 0x40202d0e56041894ull, 0x4071019ee506e0b0ull }, + { 0x40202d916872b021ull, 0x407104a3b1908860ull }, + { 0x40202e147ae147aeull, 0x407107a907479882ull }, + { 0x40202e978d4fdf3bull, 0x40710aaee6446aacull }, + { 0x40202f1a9fbe76c9ull, 0x40710db54e9f5ccfull }, + { 0x40202f9db22d0e56ull, 0x407110bc4070d11cull }, + { 0x40203020c49ba5e3ull, 0x407113c3bbd12e2bull }, + { 0x402030a3d70a3d71ull, 0x407116cbc0d8dee7ull }, + { 0x40203126e978d4feull, 0x407119d44fa05282ull }, + { 0x402031a9fbe76c8bull, 0x40711cdd683ffc92ull }, + { 0x4020322d0e560419ull, 0x40711fe70ad05508ull }, + { 0x402032b020c49ba6ull, 0x407122f13769d818ull }, + { 0x4020333333333333ull, 0x407125fbee250663ull }, + { 0x402033b645a1cac1ull, 0x407129072f1a64e2ull }, + { 0x402034395810624eull, 0x40712c12fa627cd6ull }, + { 0x402034bc6a7ef9dbull, 0x40712f1f5015dbeeull }, + { 0x4020353f7ced9169ull, 0x4071322c304d1434ull }, + { 0x402035c28f5c28f6ull, 0x407135399b20bbfdull }, + { 0x40203645a1cac083ull, 0x4071384790a96e0cull }, + { 0x402036c8b4395810ull, 0x40713b5610ffc97full }, + { 0x4020374bc6a7ef9eull, 0x40713e651c3c71d7ull }, + { 0x402037ced916872bull, 0x40714174b2780ee0ull }, + { 0x40203851eb851eb8ull, 0x40714484d3cb4cdeull }, + { 0x402038d4fdf3b646ull, 0x40714795804edc71ull }, + { 0x4020395810624dd3ull, 0x40714aa6b81b728dull }, + { 0x402039db22d0e560ull, 0x40714db87b49c898ull }, + { 0x40203a5e353f7ceeull, 0x407150cac9f29c5bull }, + { 0x40203ae147ae147bull, 0x407153dda42eaff5ull }, + { 0x40203b645a1cac08ull, 0x407156f10a16c9f9ull }, + { 0x40203be76c8b4396ull, 0x40715a04fbc3b560ull }, + { 0x40203c6a7ef9db23ull, 0x40715d19794e4179ull }, + { 0x40203ced916872b0ull, 0x4071602e82cf420bull }, + { 0x40203d70a3d70a3eull, 0x40716344185f8f46ull }, + { 0x40203df3b645a1cbull, 0x4071665a3a1805b1ull }, + { 0x40203e76c8b43958ull, 0x40716970e811864eull }, + { 0x40203ef9db22d0e5ull, 0x40716c882264f685ull }, + { 0x40203f7ced916873ull, 0x40716f9fe92b402eull }, + { 0x4020400000000000ull, 0x407172b83c7d517bull }, + { 0x40204083126e978dull, 0x407175d11c741d1eull }, + { 0x4020410624dd2f1bull, 0x407178ea89289a36ull }, + { 0x40204189374bc6a8ull, 0x40717c0482b3c43full }, + { 0x4020420c49ba5e35ull, 0x40717f1f092e9b35ull }, + { 0x4020428f5c28f5c3ull, 0x4071823a1cb22386ull }, + { 0x402043126e978d50ull, 0x40718555bd5765fdull }, + { 0x40204395810624ddull, 0x40718871eb376fe9ull }, + { 0x402044189374bc6bull, 0x40718b8ea66b530cull }, + { 0x4020449ba5e353f8ull, 0x40718eabef0c2588ull }, + { 0x4020451eb851eb85ull, 0x407191c9c5330208ull }, + { 0x402045a1cac08313ull, 0x407194e828f907a8ull }, + { 0x40204624dd2f1aa0ull, 0x407198071a7759eaull }, + { 0x402046a7ef9db22dull, 0x40719b2699c720d8ull }, + { 0x4020472b020c49baull, 0x40719e46a70188ebull }, + { 0x402047ae147ae148ull, 0x4071a167423fc31dull }, + { 0x4020483126e978d5ull, 0x4071a4886b9b04cdull }, + { 0x402048b439581062ull, 0x4071a7aa232c87e7ull }, + { 0x402049374bc6a7f0ull, 0x4071aacc690d8aceull }, + { 0x402049ba5e353f7dull, 0x4071adef3d575053ull }, + { 0x40204a3d70a3d70aull, 0x4071b112a0231fd0ull }, + { 0x40204ac083126e98ull, 0x4071b436918a4520ull }, + { 0x40204b4395810625ull, 0x4071b75b11a61086ull }, + { 0x40204bc6a7ef9db2ull, 0x4071ba80208fd6d7ull }, + { 0x40204c49ba5e3540ull, 0x4071bda5be60f168ull }, + { 0x40204ccccccccccdull, 0x4071c0cbeb32bdfaull }, + { 0x40204d4fdf3b645aull, 0x4071c3f2a71e9ee1ull }, + { 0x40204dd2f1a9fbe8ull, 0x4071c719f23dfaf4ull }, + { 0x40204e5604189375ull, 0x4071ca41ccaa3d79ull }, + { 0x40204ed916872b02ull, 0x4071cd6a367cd64cull }, + { 0x40204f5c28f5c28full, 0x4071d0932fcf39c4ull }, + { 0x40204fdf3b645a1dull, 0x4071d3bcb8bae0c8ull }, + { 0x402050624dd2f1aaull, 0x4071d6e6d15948adull }, + { 0x402050e560418937ull, 0x4071da1179c3f364ull }, + { 0x4020516872b020c5ull, 0x4071dd3cb2146765ull }, + { 0x402051eb851eb852ull, 0x4071e0687a642f9aull }, + { 0x4020526e978d4fdfull, 0x4071e394d2ccdb8dull }, + { 0x402052f1a9fbe76dull, 0x4071e6c1bb67ff4eull }, + { 0x40205374bc6a7efaull, 0x4071e9ef344f3366ull }, + { 0x402053f7ced91687ull, 0x4071ed1d3d9c14fdull }, + { 0x4020547ae147ae15ull, 0x4071f04bd76845c4ull }, + { 0x402054fdf3b645a2ull, 0x4071f37b01cd6be9ull }, + { 0x402055810624dd2full, 0x4071f6aabce53238ull }, + { 0x40205604189374bdull, 0x4071f9db08c9480cull }, + { 0x402056872b020c4aull, 0x4071fd0be593613eull }, + { 0x4020570a3d70a3d7ull, 0x4072003d535d3649ull }, + { 0x4020578d4fdf3b64ull, 0x4072036f52408430ull }, + { 0x40205810624dd2f2ull, 0x407206a1e2570c91ull }, + { 0x4020589374bc6a7full, 0x407209d503ba9588ull }, + { 0x40205916872b020cull, 0x40720d08b684e9d9ull }, + { 0x402059999999999aull, 0x4072103cfacfd8d9ull }, + { 0x40205a1cac083127ull, 0x40721371d0b53661ull }, + { 0x40205a9fbe76c8b4ull, 0x407216a7384edaf2ull }, + { 0x40205b22d0e56042ull, 0x407219dd31b6a3a4ull }, + { 0x40205ba5e353f7cfull, 0x40721d13bd067213ull }, + { 0x40205c28f5c28f5cull, 0x4072204ada582c85ull }, + { 0x40205cac083126eaull, 0x4072238289c5bddbull }, + { 0x40205d2f1a9fbe77ull, 0x407226bacb69157bull }, + { 0x40205db22d0e5604ull, 0x407229f39f5c277aull }, + { 0x40205e353f7ced92ull, 0x40722d2d05b8ec86ull }, + { 0x40205eb851eb851full, 0x40723066fe9961daull }, + { 0x40205f3b645a1cacull, 0x407233a18a17895cull }, + { 0x40205fbe76c8b439ull, 0x407236dca84d698full }, + { 0x4020604189374bc7ull, 0x40723a1859550d96ull }, + { 0x402060c49ba5e354ull, 0x40723d549d488524ull }, + { 0x40206147ae147ae1ull, 0x407240917441e49dull }, + { 0x402061cac083126full, 0x407243cede5b4508ull }, + { 0x4020624dd2f1a9fcull, 0x4072470cdbaec3faull }, + { 0x402062d0e5604189ull, 0x40724a4b6c5683bdull }, + { 0x40206353f7ced917ull, 0x40724d8a906cab3full }, + { 0x402063d70a3d70a4ull, 0x407250ca480b6601ull }, + { 0x4020645a1cac0831ull, 0x4072540a934ce43aull }, + { 0x402064dd2f1a9fbfull, 0x4072574b724b5ac9ull }, + { 0x402065604189374cull, 0x40725a8ce521031eull }, + { 0x402065e353f7ced9ull, 0x40725dceebe81b67ull }, + { 0x4020666666666666ull, 0x4072611186bae672ull }, + { 0x402066e978d4fdf4ull, 0x40726454b5b3abbeull }, + { 0x4020676c8b439581ull, 0x4072679878ecb760ull }, + { 0x402067ef9db22d0eull, 0x40726adcd0805a2cull }, + { 0x40206872b020c49cull, 0x40726e21bc88e9a1ull }, + { 0x402068f5c28f5c29ull, 0x407271673d20bfd8ull }, + { 0x40206978d4fdf3b6ull, 0x407274ad52623babull }, + { 0x402069fbe76c8b44ull, 0x407277f3fc67c0a1ull }, + { 0x40206a7ef9db22d1ull, 0x40727b3b3b4bb6dfull }, + { 0x40206b020c49ba5eull, 0x40727e830f288b4cull }, + { 0x40206b851eb851ecull, 0x407281cb7818af7full }, + { 0x40206c083126e979ull, 0x40728514763699afull }, + { 0x40206c8b43958106ull, 0x4072885e099cc4d8ull }, + { 0x40206d0e56041894ull, 0x40728ba83265b0aaull }, + { 0x40206d916872b021ull, 0x40728ef2f0abe173ull }, + { 0x40206e147ae147aeull, 0x4072923e4489e04dull }, + { 0x40206e978d4fdf3bull, 0x4072958a2e1a3b01ull }, + { 0x40206f1a9fbe76c9ull, 0x407298d6ad778411ull }, + { 0x40206f9db22d0e56ull, 0x40729c23c2bc52a9ull }, + { 0x40207020c49ba5e3ull, 0x40729f716e0342bcull }, + { 0x402070a3d70a3d71ull, 0x4072a2bfaf66f4f9ull }, + { 0x40207126e978d4feull, 0x4072a60e87020eb5ull }, + { 0x402071a9fbe76c8bull, 0x4072a95df4ef3a11ull }, + { 0x4020722d0e560419ull, 0x4072acadf94925ecull }, + { 0x402072b020c49ba6ull, 0x4072affe942a85cfull }, + { 0x4020733333333333ull, 0x4072b34fc5ae1212ull }, + { 0x402073b645a1cac1ull, 0x4072b6a18dee87caull }, + { 0x402074395810624eull, 0x4072b9f3ed06a8bbull }, + { 0x402074bc6a7ef9dbull, 0x4072bd46e3113b7aull }, + { 0x4020753f7ced9169ull, 0x4072c09a70290b5eull }, + { 0x402075c28f5c28f6ull, 0x4072c3ee9468e86aull }, + { 0x40207645a1cac083ull, 0x4072c7434feba778ull }, + { 0x402076c8b4395810ull, 0x4072ca98a2cc2221ull }, + { 0x4020774bc6a7ef9eull, 0x4072cdee8d2536c6ull }, + { 0x402077ced916872bull, 0x4072d1450f11c87aull }, + { 0x40207851eb851eb8ull, 0x4072d49c28acbf29ull }, + { 0x402078d4fdf3b646ull, 0x4072d7f3da110786ull }, + { 0x4020795810624dd3ull, 0x4072db4c235992f9ull }, + { 0x402079db22d0e560ull, 0x4072dea504a157c5ull }, + { 0x40207a5e353f7ceeull, 0x4072e1fe7e0350f5ull }, + { 0x40207ae147ae147bull, 0x4072e5588f9a7e4cull }, + { 0x40207b645a1cac08ull, 0x4072e8b33981e46cull }, + { 0x40207be76c8b4396ull, 0x4072ec0e7bd48cc1ull }, + { 0x40207c6a7ef9db23ull, 0x4072ef6a56ad8571ull }, + { 0x40207ced916872b0ull, 0x4072f2c6ca27e184ull }, + { 0x40207d70a3d70a3eull, 0x4072f623d65eb8cdull }, + { 0x40207df3b645a1cbull, 0x4072f9817b6d27dfull }, + { 0x40207e76c8b43958ull, 0x4072fcdfb96e502dull }, + { 0x40207ef9db22d0e5ull, 0x4073003e907d57f6ull }, + { 0x40207f7ced916873ull, 0x4073039e00b56a52ull }, + { 0x4020800000000000ull, 0x407306fe0a31b715ull }, + { 0x40208083126e978dull, 0x40730a5ead0d72feull }, + { 0x4020810624dd2f1bull, 0x40730dbfe963d79cull }, + { 0x40208189374bc6a8ull, 0x40731121bf502344ull }, + { 0x4020820c49ba5e35ull, 0x407314842eed9931ull }, + { 0x4020828f5c28f5c3ull, 0x407317e738578178ull }, + { 0x402083126e978d50ull, 0x40731b4adba928eeull }, + { 0x40208395810624ddull, 0x40731eaf18fde157ull }, + { 0x402084189374bc6bull, 0x40732213f0710152ull }, + { 0x4020849ba5e353f8ull, 0x40732579621de441ull }, + { 0x4020851eb851eb85ull, 0x407328df6e1fea77ull }, + { 0x402085a1cac08313ull, 0x40732c4614927922ull }, + { 0x40208624dd2f1aa0ull, 0x40732fad5590fa3aull }, + { 0x402086a7ef9db22dull, 0x407333153136dca8ull }, + { 0x4020872b020c49baull, 0x4073367da79f942eull }, + { 0x402087ae147ae148ull, 0x407339e6b8e69972ull }, + { 0x4020883126e978d5ull, 0x40733d50652769e9ull }, + { 0x402088b439581062ull, 0x407340baac7d87fbull }, + { 0x402089374bc6a7f0ull, 0x407344258f047af3ull }, + { 0x402089ba5e353f7dull, 0x407347910cd7ceecull }, + { 0x40208a3d70a3d70aull, 0x40734afd261314f6ull }, + { 0x40208ac083126e98ull, 0x40734e69dad1e309ull }, + { 0x40208b4395810625ull, 0x407351d72b2fd3ecull }, + { 0x40208bc6a7ef9db2ull, 0x4073554517488761ull }, + { 0x40208c49ba5e3540ull, 0x407358b39f37a213ull }, + { 0x40208ccccccccccdull, 0x40735c22c318cd7eull }, + { 0x40208d4fdf3b645aull, 0x40735f928307b81eull }, + { 0x40208dd2f1a9fbe8ull, 0x40736302df201558ull }, + { 0x40208e5604189375ull, 0x40736673d77d9d67ull }, + { 0x40208ed916872b02ull, 0x407369e56c3c0d85ull }, + { 0x40208f5c28f5c28full, 0x40736d579d7727d5ull }, + { 0x40208fdf3b645a1dull, 0x407370ca6b4ab36bull }, + { 0x402090624dd2f1aaull, 0x4073743dd5d27c36ull }, + { 0x402090e560418937ull, 0x407377b1dd2a5328ull }, + { 0x4020916872b020c5ull, 0x40737b26816e0e25ull }, + { 0x402091eb851eb852ull, 0x40737e9bc2b987ecull }, + { 0x4020926e978d4fdfull, 0x40738211a128a041ull }, + { 0x402092f1a9fbe76dull, 0x407385881cd73bdcull }, + { 0x40209374bc6a7efaull, 0x407388ff35e14455ull }, + { 0x402093f7ced91687ull, 0x40738c76ec62a84bull }, + { 0x4020947ae147ae15ull, 0x40738fef40775b53ull }, + { 0x402094fdf3b645a2ull, 0x40739368323b55e5ull }, + { 0x402095810624dd2full, 0x407396e1c1ca9582ull }, + { 0x40209604189374bdull, 0x40739a5bef411ca4ull }, + { 0x402096872b020c4aull, 0x40739dd6babaf2abull }, + { 0x4020970a3d70a3d7ull, 0x4073a15224542403ull }, + { 0x4020978d4fdf3b64ull, 0x4073a4ce2c28c20cull }, + { 0x40209810624dd2f2ull, 0x4073a84ad254e32aull }, + { 0x4020989374bc6a7full, 0x4073abc816f4a2a7ull }, + { 0x40209916872b020cull, 0x4073af45fa2420deull }, + { 0x402099999999999aull, 0x4073b2c47bff832cull }, + { 0x40209a1cac083127ull, 0x4073b6439ca2f3d4ull }, + { 0x40209a9fbe76c8b4ull, 0x4073b9c35c2aa22full }, + { 0x40209b22d0e56042ull, 0x4073bd43bab2c299ull }, + { 0x40209ba5e353f7cfull, 0x4073c0c4b8578e58ull }, + { 0x40209c28f5c28f5cull, 0x4073c446553543cbull }, + { 0x40209cac083126eaull, 0x4073c7c891682653ull }, + { 0x40209d2f1a9fbe77ull, 0x4073cb4b6d0c7e42ull }, + { 0x40209db22d0e5604ull, 0x4073cecee83e9903ull }, + { 0x40209e353f7ced92ull, 0x4073d253031ac908ull }, + { 0x40209eb851eb851full, 0x4073d5d7bdbd65b4ull }, + { 0x40209f3b645a1cacull, 0x4073d95d1842cb89ull }, + { 0x40209fbe76c8b439ull, 0x4073dce312c75c07ull }, + { 0x4020a04189374bc7ull, 0x4073e069ad677dc2ull }, + { 0x4020a0c49ba5e354ull, 0x4073e3f0e83f9c42ull }, + { 0x4020a147ae147ae1ull, 0x4073e778c36c2831ull }, + { 0x4020a1cac083126full, 0x4073eb013f099744ull }, + { 0x4020a24dd2f1a9fcull, 0x4073ee8a5b346428ull }, + { 0x4020a2d0e5604189ull, 0x4073f21418090eaeull }, + { 0x4020a353f7ced917ull, 0x4073f59e75a41bb6ull }, + { 0x4020a3d70a3d70a4ull, 0x4073f9297422151bull }, + { 0x4020a45a1cac0831ull, 0x4073fcb5139f89deull }, + { 0x4020a4dd2f1a9fbfull, 0x4074004154390e0full }, + { 0x4020a5604189374cull, 0x407403ce360b3ac1ull }, + { 0x4020a5e353f7ced9ull, 0x4074075bb932ae2bull }, + { 0x4020a66666666666ull, 0x40740ae9ddcc0b93ull }, + { 0x4020a6e978d4fdf4ull, 0x40740e78a3f3fb5cull }, + { 0x4020a76c8b439581ull, 0x407412080bc72ae9ull }, + { 0x4020a7ef9db22d0eull, 0x4074159815624ccaull }, + { 0x4020a872b020c49cull, 0x40741928c0e218a7ull }, + { 0x4020a8f5c28f5c29ull, 0x40741cba0e634b2cull }, + { 0x4020a978d4fdf3b6ull, 0x4074204bfe02a633ull }, + { 0x4020a9fbe76c8b44ull, 0x407423de8fdcf0b3ull }, + { 0x4020aa7ef9db22d1ull, 0x40742771c40ef6a6ull }, + { 0x4020ab020c49ba5eull, 0x40742b059ab5893bull }, + { 0x4020ab851eb851ecull, 0x40742e9a13ed7ebdull }, + { 0x4020ac083126e979ull, 0x4074322f2fd3b280ull }, + { 0x4020ac8b43958106ull, 0x407435c4ee85050full }, + { 0x4020ad0e56041894ull, 0x4074395b501e5c11ull }, + { 0x4020ad916872b021ull, 0x40743cf254bca23cull }, + { 0x4020ae147ae147aeull, 0x40744089fc7cc77cull }, + { 0x4020ae978d4fdf3bull, 0x40744422477bc0dbull }, + { 0x4020af1a9fbe76c9ull, 0x407447bb35d6888aull }, + { 0x4020af9db22d0e56ull, 0x40744b54c7aa1dcdull }, + { 0x4020b020c49ba5e3ull, 0x40744eeefd138522ull }, + { 0x4020b0a3d70a3d71ull, 0x40745289d62fc82dull }, + { 0x4020b126e978d4feull, 0x40745625531bf5a4ull }, + { 0x4020b1a9fbe76c8bull, 0x407459c173f5217cull }, + { 0x4020b22d0e560419ull, 0x40745d5e38d864d2ull }, + { 0x4020b2b020c49ba6ull, 0x407460fba1e2dddaull }, + { 0x4020b33333333333ull, 0x40746499af31b005ull }, + { 0x4020b3b645a1cac1ull, 0x4074683860e203f4ull }, + { 0x4020b4395810624eull, 0x40746bd7b711075eull }, + { 0x4020b4bc6a7ef9dbull, 0x40746f77b1dbed3bull }, + { 0x4020b53f7ced9169ull, 0x40747318515fedb7ull }, + { 0x4020b5c28f5c28f6ull, 0x407476b995ba4613ull }, + { 0x4020b645a1cac083ull, 0x40747a5b7f0838daull }, + { 0x4020b6c8b4395810ull, 0x40747dfe0d670dbfull }, + { 0x4020b74bc6a7ef9eull, 0x407481a140f411b1ull }, + { 0x4020b7ced916872bull, 0x4074854519cc96baull }, + { 0x4020b851eb851eb8ull, 0x407488e9980df431ull }, + { 0x4020b8d4fdf3b646ull, 0x40748c8ebbd586a1ull }, + { 0x4020b95810624dd3ull, 0x407490348540afb4ull }, + { 0x4020b9db22d0e560ull, 0x407493daf46cd664ull }, + { 0x4020ba5e353f7ceeull, 0x40749782097766e1ull }, + { 0x4020bae147ae147bull, 0x40749b29c47dd27eull }, + { 0x4020bb645a1cac08ull, 0x40749ed2259d8fdfull }, + { 0x4020bbe76c8b4396ull, 0x4074a27b2cf41ae2ull }, + { 0x4020bc6a7ef9db23ull, 0x4074a624da9ef48cull }, + { 0x4020bced916872b0ull, 0x4074a9cf2ebba333ull }, + { 0x4020bd70a3d70a3eull, 0x4074ad7a2967b26cull }, + { 0x4020bdf3b645a1cbull, 0x4074b125cac0b2f4ull }, + { 0x4020be76c8b43958ull, 0x4074b4d212e43addull }, + { 0x4020bef9db22d0e5ull, 0x4074b87f01efe574ull }, + { 0x4020bf7ced916873ull, 0x4074bc2c9801534cull }, + { 0x4020c00000000000ull, 0x4074bfdad5362a27ull }, + { 0x4020c083126e978dull, 0x4074c389b9ac151full }, + { 0x4020c10624dd2f1bull, 0x4074c7394580c491ull }, + { 0x4020c189374bc6a8ull, 0x4074cae978d1ee0bull }, + { 0x4020c20c49ba5e35ull, 0x4074ce9a53bd4c77ull }, + { 0x4020c28f5c28f5c3ull, 0x4074d24bd660a005ull }, + { 0x4020c3126e978d50ull, 0x4074d5fe00d9ae17ull }, + { 0x4020c395810624ddull, 0x4074d9b0d346416eull }, + { 0x4020c4189374bc6bull, 0x4074dd644dc42a15ull }, + { 0x4020c49ba5e353f8ull, 0x4074e11870713d4cull }, + { 0x4020c51eb851eb85ull, 0x4074e4cd3b6b55b4ull }, + { 0x4020c5a1cac08313ull, 0x4074e882aed0533bull }, + { 0x4020c624dd2f1aa0ull, 0x4074ec38cabe1b07ull }, + { 0x4020c6a7ef9db22dull, 0x4074efef8f5297a2ull }, + { 0x4020c72b020c49baull, 0x4074f3a6fcabb8ddull }, + { 0x4020c7ae147ae148ull, 0x4074f75f12e773e3ull }, + { 0x4020c83126e978d5ull, 0x4074fb17d223c319ull }, + { 0x4020c8b439581062ull, 0x4074fed13a7ea64cull }, + { 0x4020c9374bc6a7f0ull, 0x4075028b4c16229dull }, + { 0x4020c9ba5e353f7dull, 0x407506460708426aull }, + { 0x4020ca3d70a3d70aull, 0x40750a016b73157full }, + { 0x4020cac083126e98ull, 0x40750dbd7974b0faull }, + { 0x4020cb4395810625ull, 0x4075117a312b2f3cull }, + { 0x4020cbc6a7ef9db2ull, 0x4075153792b4b015ull }, + { 0x4020cc49ba5e3540ull, 0x407518f59e2f58acull }, + { 0x4020cccccccccccdull, 0x40751cb453b9536eull }, + { 0x4020cd4fdf3b645aull, 0x40752073b370d036ull }, + { 0x4020cdd2f1a9fbe8ull, 0x40752433bd74043cull }, + { 0x4020ce5604189375ull, 0x407527f471e12a00ull }, + { 0x4020ced916872b02ull, 0x40752bb5d0d68174ull }, + { 0x4020cf5c28f5c28full, 0x40752f77da724fe2ull }, + { 0x4020cfdf3b645a1dull, 0x4075333a8ed2dffbull }, + { 0x4020d0624dd2f1aaull, 0x407536fdee1681baull }, + { 0x4020d0e560418937ull, 0x40753ac1f85b8a91ull }, + { 0x4020d16872b020c5ull, 0x40753e86adc05555ull }, + { 0x4020d1eb851eb852ull, 0x4075424c0e63422aull }, + { 0x4020d26e978d4fdfull, 0x407546121a62b6acull }, + { 0x4020d2f1a9fbe76dull, 0x407549d8d1dd1dddull }, + { 0x4020d374bc6a7efaull, 0x40754da034f0e80full }, + { 0x4020d3f7ced91687ull, 0x4075516843bc8b12ull }, + { 0x4020d47ae147ae15ull, 0x40755530fe5e821dull }, + { 0x4020d4fdf3b645a2ull, 0x407558fa64f54dbcull }, + { 0x4020d5810624dd2full, 0x40755cc4779f73f9ull }, + { 0x4020d604189374bdull, 0x4075608f367b804bull }, + { 0x4020d6872b020c4aull, 0x4075645aa1a8037cull }, + { 0x4020d70a3d70a3d7ull, 0x40756826b94393ddull }, + { 0x4020d78d4fdf3b64ull, 0x40756bf37d6ccd22ull }, + { 0x4020d810624dd2f2ull, 0x40756fc0ee425078ull }, + { 0x4020d89374bc6a7full, 0x4075738f0be2c463ull }, + { 0x4020d916872b020cull, 0x4075775dd66cd4f1ull }, + { 0x4020d9999999999aull, 0x40757b2d4dff33a0ull }, + { 0x4020da1cac083127ull, 0x40757efd72b8974aull }, + { 0x4020da9fbe76c8b4ull, 0x407582ce44b7bc54ull }, + { 0x4020db22d0e56042ull, 0x4075869fc41b6498ull }, + { 0x4020dba5e353f7cfull, 0x40758a71f102574full }, + { 0x4020dc28f5c28f5cull, 0x40758e44cb8b613eull }, + { 0x4020dcac083126eaull, 0x4075921853d554a3ull }, + { 0x4020dd2f1a9fbe77ull, 0x407595ec89ff091dull }, + { 0x4020ddb22d0e5604ull, 0x407599c16e275bdcull }, + { 0x4020de353f7ced92ull, 0x40759d97006d2f8aull }, + { 0x4020deb851eb851full, 0x4075a16d40ef6c36ull }, + { 0x4020df3b645a1cacull, 0x4075a5442fccff82ull }, + { 0x4020dfbe76c8b439ull, 0x4075a91bcd24dc87ull }, + { 0x4020e04189374bc7ull, 0x4075acf41915fbe0ull }, + { 0x4020e0c49ba5e354ull, 0x4075b0cd13bf5b97ull }, + { 0x4020e147ae147ae1ull, 0x4075b4a6bd3fff47ull }, + { 0x4020e1cac083126full, 0x4075b88115b6f011ull }, + { 0x4020e24dd2f1a9fcull, 0x4075bc5c1d433c7eull }, + { 0x4020e2d0e5604189ull, 0x4075c037d403f8b4ull }, + { 0x4020e353f7ced917ull, 0x4075c4143a183e5aull }, + { 0x4020e3d70a3d70a4ull, 0x4075c7f14f9f2c89ull }, + { 0x4020e45a1cac0831ull, 0x4075cbcf14b7e7f5ull }, + { 0x4020e4dd2f1a9fbfull, 0x4075cfad89819adaull }, + { 0x4020e5604189374cull, 0x4075d38cae1b74e4ull }, + { 0x4020e5e353f7ced9ull, 0x4075d76c82a4ab60ull }, + { 0x4020e66666666666ull, 0x4075db4d073c791dull }, + { 0x4020e6e978d4fdf4ull, 0x4075df2e3c021e7full }, + { 0x4020e76c8b439581ull, 0x4075e3102114e15cull }, + { 0x4020e7ef9db22d0eull, 0x4075e6f2b6940d30ull }, + { 0x4020e872b020c49cull, 0x4075ead5fc9ef305ull }, + { 0x4020e8f5c28f5c29ull, 0x4075eeb9f354e95cull }, + { 0x4020e978d4fdf3b6ull, 0x4075f29e9ad54c5eull }, + { 0x4020e9fbe76c8b44ull, 0x4075f683f33f7dc4ull }, + { 0x4020ea7ef9db22d1ull, 0x4075fa69fcb2e4c4ull }, + { 0x4020eb020c49ba5eull, 0x4075fe50b74eee3cull }, + { 0x4020eb851eb851ecull, 0x4076023823330ca0ull }, + { 0x4020ec083126e979ull, 0x40760620407eb7e0ull }, + { 0x4020ec8b43958106ull, 0x40760a090f516d9aull }, + { 0x4020ed0e56041894ull, 0x40760df28fcab102ull }, + { 0x4020ed916872b021ull, 0x407611dcc20a0ad0ull }, + { 0x4020ee147ae147aeull, 0x407615c7a62f0968ull }, + { 0x4020ee978d4fdf3bull, 0x407619b33c5940c2ull }, + { 0x4020ef1a9fbe76c9ull, 0x40761d9f84a84a7aull }, + { 0x4020ef9db22d0e56ull, 0x4076218c7f3bc5afull }, + { 0x4020f020c49ba5e3ull, 0x4076257a2c335735ull }, + { 0x4020f0a3d70a3d71ull, 0x407629688baea97dull }, + { 0x4020f126e978d4feull, 0x40762d579dcd6c81ull }, + { 0x4020f1a9fbe76c8bull, 0x4076314762af55f2ull }, + { 0x4020f22d0e560419ull, 0x40763537da742120ull }, + { 0x4020f2b020c49ba6ull, 0x40763929053b8ee9ull }, + { 0x4020f33333333333ull, 0x40763d1ae32565e3ull }, + { 0x4020f3b645a1cac1ull, 0x4076410d74517247ull }, + { 0x4020f4395810624eull, 0x40764500b8df85deull }, + { 0x4020f4bc6a7ef9dbull, 0x407648f4b0ef782eull }, + { 0x4020f53f7ced9169ull, 0x40764ce95ca12662ull }, + { 0x4020f5c28f5c28f6ull, 0x407650debc147337ull }, + { 0x4020f645a1cac083ull, 0x407654d4cf69472bull }, + { 0x4020f6c8b4395810ull, 0x407658cb96bf905cull }, + { 0x4020f74bc6a7ef9eull, 0x40765cc31237429eull }, + { 0x4020f7ced916872bull, 0x407660bb41f05756ull }, + { 0x4020f851eb851eb8ull, 0x407664b4260acdafull }, + { 0x4020f8d4fdf3b646ull, 0x407668adbea6aa83ull }, + { 0x4020f95810624dd3ull, 0x40766ca80be3f842ull }, + { 0x4020f9db22d0e560ull, 0x407670a30de2c724ull }, + { 0x4020fa5e353f7ceeull, 0x4076749ec4c32d12ull }, + { 0x4020fae147ae147bull, 0x4076789b30a54590ull }, + { 0x4020fb645a1cac08ull, 0x40767c9851a931ebull }, + { 0x4020fbe76c8b4396ull, 0x4076809627ef1927ull }, + { 0x4020fc6a7ef9db23ull, 0x40768494b39727e3ull }, + { 0x4020fced916872b0ull, 0x40768893f4c1908bull }, + { 0x4020fd70a3d70a3eull, 0x40768c93eb8e8b45ull }, + { 0x4020fdf3b645a1cbull, 0x40769094981e55d5ull }, + { 0x4020fe76c8b43958ull, 0x40769495fa9133d1ull }, + { 0x4020fef9db22d0e5ull, 0x4076989813076e81ull }, + { 0x4020ff7ced916873ull, 0x40769c9ae1a154f0ull }, + { 0x4021000000000000ull, 0x4076a09e667f3bcdull }, + { 0x40210083126e978dull, 0x4076a4a2a1c17d9cull }, + { 0x4021010624dd2f1bull, 0x4076a8a793887aa0ull }, + { 0x40210189374bc6a8ull, 0x4076acad3bf498c2ull }, + { 0x4021020c49ba5e35ull, 0x4076b0b39b2643c6ull }, + { 0x4021028f5c28f5c3ull, 0x4076b4bab13ded2eull }, + { 0x402103126e978d50ull, 0x4076b8c27e5c0c27ull }, + { 0x40210395810624ddull, 0x4076bccb02a11dbbull }, + { 0x402104189374bc6bull, 0x4076c0d43e2da4b6ull }, + { 0x4021049ba5e353f8ull, 0x4076c4de31222994ull }, + { 0x4021051eb851eb85ull, 0x4076c8e8db9f3aadull }, + { 0x402105a1cac08313ull, 0x4076ccf43dc56c22ull }, + { 0x40210624dd2f1aa0ull, 0x4076d10057b557c3ull }, + { 0x402106a7ef9db22dull, 0x4076d50d298f9d43ull }, + { 0x4021072b020c49baull, 0x4076d91ab374e215ull }, + { 0x402107ae147ae148ull, 0x4076dd28f585d184ull }, + { 0x4021083126e978d5ull, 0x4076e137efe31c8aull }, + { 0x402108b439581062ull, 0x4076e547a2ad7a0bull }, + { 0x402109374bc6a7f0ull, 0x4076e9580e05a6b7ull }, + { 0x402109ba5e353f7dull, 0x4076ed69320c64f8ull }, + { 0x40210a3d70a3d70aull, 0x4076f17b0ee27d1dull }, + { 0x40210ac083126e98ull, 0x4076f58da4a8bd4bull }, + { 0x40210b4395810625ull, 0x4076f9a0f37ff95eull }, + { 0x40210bc6a7ef9db2ull, 0x4076fdb4fb890b21ull }, + { 0x40210c49ba5e3540ull, 0x407701c9bce4d230ull }, + { 0x40210ccccccccccdull, 0x407705df37b433e8ull }, + { 0x40210d4fdf3b645aull, 0x407709f56c181b95ull }, + { 0x40210dd2f1a9fbe8ull, 0x40770e0c5a317a58ull }, + { 0x40210e5604189375ull, 0x4077122402214716ull }, + { 0x40210ed916872b02ull, 0x4077163c64087ea4ull }, + { 0x40210f5c28f5c28full, 0x40771a55800823abull }, + { 0x40210fdf3b645a1dull, 0x40771e6f56413eb7ull }, + { 0x402110624dd2f1aaull, 0x40772289e6d4de1aull }, + { 0x402110e560418937ull, 0x407726a531e4161dull }, + { 0x4021116872b020c5ull, 0x40772ac1379000e6ull }, + { 0x402111eb851eb852ull, 0x40772eddf7f9be65ull }, + { 0x4021126e978d4fdfull, 0x407732fb73427482ull }, + { 0x402112f1a9fbe76dull, 0x40773719a98b4f08ull }, + { 0x40211374bc6a7efaull, 0x40773b389af57f8eull }, + { 0x402113f7ced91687ull, 0x40773f5847a23da6ull }, + { 0x4021147ae147ae15ull, 0x40774378afb2c6c9ull }, + { 0x402114fdf3b645a2ull, 0x40774799d3485e3cull }, + { 0x402115810624dd2full, 0x40774bbbb2844d46ull }, + { 0x40211604189374bdull, 0x40774fde4d87e316ull }, + { 0x402116872b020c4aull, 0x40775401a47474acull }, + { 0x4021170a3d70a3d7ull, 0x40775825b76b5d0aull }, + { 0x4021178d4fdf3b64ull, 0x40775c4a868dfd19ull }, + { 0x40211810624dd2f2ull, 0x4077607011fdbbb2ull }, + { 0x4021189374bc6a7full, 0x4077649659dc0588ull }, + { 0x40211916872b020cull, 0x407768bd5e4a4d53ull }, + { 0x402119999999999aull, 0x40776ce51f6a0bbbull }, + { 0x40211a1cac083127ull, 0x4077710d9d5cbf41ull }, + { 0x40211a9fbe76c8b4ull, 0x40777536d843ec71ull }, + { 0x40211b22d0e56042ull, 0x40777960d0411dc8ull }, + { 0x40211ba5e353f7cfull, 0x40777d8b8575e3a1ull }, + { 0x40211c28f5c28f5cull, 0x407781b6f803d464ull }, + { 0x40211cac083126eaull, 0x407785e3280c8c6full }, + { 0x40211d2f1a9fbe77ull, 0x40778a1015b1adffull }, + { 0x40211db22d0e5604ull, 0x40778e3dc114e162ull }, + { 0x40211e353f7ced92ull, 0x4077926c2a57d4deull }, + { 0x40211eb851eb851full, 0x4077969b519c3c9full }, + { 0x40211f3b645a1cacull, 0x40779acb3703d2dfull }, + { 0x40211fbe76c8b439ull, 0x40779efbdab057d3ull }, + { 0x4021204189374bc7ull, 0x4077a32d3cc391b1ull }, + { 0x402120c49ba5e354ull, 0x4077a75f5d5f4c97ull }, + { 0x40212147ae147ae1ull, 0x4077ab923ca55abcull }, + { 0x402121cac083126full, 0x4077afc5dab79457ull }, + { 0x4021224dd2f1a9fcull, 0x4077b3fa37b7d788ull }, + { 0x402122d0e5604189ull, 0x4077b82f53c8088cull }, + { 0x40212353f7ced917ull, 0x4077bc652f0a11a3ull }, + { 0x402123d70a3d70a4ull, 0x4077c09bc99fe2f9ull }, + { 0x4021245a1cac0831ull, 0x4077c4d323ab72dbull }, + { 0x402124dd2f1a9fbfull, 0x4077c90b3d4ebd9bull }, + { 0x402125604189374cull, 0x4077cd4416abc57dull }, + { 0x402125e353f7ced9ull, 0x4077d17dafe492e4ull }, + { 0x4021266666666667ull, 0x4077d5b8091b3441ull }, + { 0x402126e978d4fdf4ull, 0x4077d9f32271bdf5ull }, + { 0x4021276c8b439581ull, 0x4077de2efc0a4a87ull }, + { 0x402127ef9db22d0eull, 0x4077e26b9606fa86ull }, + { 0x40212872b020c49cull, 0x4077e6a8f089f495ull }, + { 0x402128f5c28f5c29ull, 0x4077eae70bb5654full }, + { 0x40212978d4fdf3b6ull, 0x4077ef25e7ab7f79ull }, + { 0x402129fbe76c8b44ull, 0x4077f365848e7beaull }, + { 0x40212a7ef9db22d1ull, 0x4077f7a5e2809974ull }, + { 0x40212b020c49ba5eull, 0x4077fbe701a41d14ull }, + { 0x40212b851eb851ecull, 0x40780028e21b51deull }, + { 0x40212c083126e979ull, 0x4078046b840888e3ull }, + { 0x40212c8b43958106ull, 0x407808aee78e1965ull }, + { 0x40212d0e56041894ull, 0x40780cf30cce60bdull }, + { 0x40212d916872b021ull, 0x40781137f3ebc245ull }, + { 0x40212e147ae147aeull, 0x4078157d9d08a78dull }, + { 0x40212e978d4fdf3bull, 0x407819c408478035ull }, + { 0x40212f1a9fbe76c9ull, 0x40781e0b35cac203ull }, + { 0x40212f9db22d0e56ull, 0x4078225325b4e8c5ull }, + { 0x40213020c49ba5e3ull, 0x4078269bd828767cull }, + { 0x402130a3d70a3d71ull, 0x40782ae54d47f34eull }, + { 0x40213126e978d4feull, 0x40782f2f8535ed65ull }, + { 0x402131a9fbe76c8bull, 0x4078337a8014f929ull }, + { 0x4021322d0e560419ull, 0x407837c63e07b126ull }, + { 0x402132b020c49ba6ull, 0x40783c12bf30b5efull }, + { 0x4021333333333333ull, 0x4078406003b2ae5bull }, + { 0x402133b645a1cac1ull, 0x407844ae0bb04764ull }, + { 0x402134395810624eull, 0x407848fcd74c3414ull }, + { 0x402134bc6a7ef9dbull, 0x40784d4c66a92db6ull }, + { 0x4021353f7ced9169ull, 0x4078519cb9e9f3c2ull }, + { 0x402135c28f5c28f6ull, 0x407855edd1314bbdull }, + { 0x40213645a1cac083ull, 0x40785a3faca20175ull }, + { 0x402136c8b4395810ull, 0x40785e924c5ee6dbull }, + { 0x4021374bc6a7ef9eull, 0x407862e5b08ad417ull }, + { 0x402137ced916872bull, 0x40786739d948a769ull }, + { 0x40213851eb851eb8ull, 0x40786b8ec6bb4557ull }, + { 0x402138d4fdf3b646ull, 0x40786fe47905989dull }, + { 0x4021395810624dd3ull, 0x4078743af04a920cull }, + { 0x402139db22d0e560ull, 0x407878922cad28c5ull }, + { 0x40213a5e353f7ceeull, 0x40787cea2e505a1dull }, + { 0x40213ae147ae147bull, 0x40788142f5572987ull }, + { 0x40213b645a1cac08ull, 0x4078859c81e4a0c3ull }, + { 0x40213be76c8b4396ull, 0x407889f6d41bcfcdull }, + { 0x40213c6a7ef9db23ull, 0x40788e51ec1fccbfull }, + { 0x40213ced916872b0ull, 0x407892adca13b406ull }, + { 0x40213d70a3d70a3eull, 0x4078970a6e1aa84dull }, + { 0x40213df3b645a1cbull, 0x40789b67d857d25full }, + { 0x40213e76c8b43958ull, 0x40789fc608ee6162ull }, + { 0x40213ef9db22d0e5ull, 0x4078a42500018ab0ull }, + { 0x40213f7ced916873ull, 0x4078a884bdb489eaull }, + { 0x4021400000000000ull, 0x4078ace5422aa0dbull }, + { 0x40214083126e978dull, 0x4078b1468d8717aaull }, + { 0x4021410624dd2f1bull, 0x4078b5a89fed3cbfull }, + { 0x40214189374bc6a8ull, 0x4078ba0b798064aeull }, + { 0x4021420c49ba5e35ull, 0x4078be6f1a63ea6bull }, + { 0x4021428f5c28f5c3ull, 0x4078c2d382bb2f2full }, + { 0x402143126e978d50ull, 0x4078c738b2a99a62ull }, + { 0x40214395810624ddull, 0x4078cb9eaa5299cdull }, + { 0x402144189374bc6bull, 0x4078d00569d9a186ull }, + { 0x4021449ba5e353f8ull, 0x4078d46cf1622bd2ull }, + { 0x4021451eb851eb85ull, 0x4078d8d5410fb95cull }, + { 0x402145a1cac08313ull, 0x4078dd3e5905d11dull }, + { 0x40214624dd2f1aa0ull, 0x4078e1a839680043ull }, + { 0x402146a7ef9db22dull, 0x4078e612e259da63ull }, + { 0x4021472b020c49baull, 0x4078ea7e53fef95eull }, + { 0x402147ae147ae148ull, 0x4078eeea8e7afd6bull }, + { 0x4021483126e978d5ull, 0x4078f35791f18cfeull }, + { 0x402148b439581062ull, 0x4078f7c55e8654f7ull }, + { 0x402149374bc6a7f0ull, 0x4078fc33f45d088aull }, + { 0x402149ba5e353f7dull, 0x407900a353996129ull }, + { 0x40214a3d70a3d70aull, 0x407905137c5f1eb7ull }, + { 0x40214ac083126e98ull, 0x407909846ed20770ull }, + { 0x40214b4395810625ull, 0x40790df62b15e7cfull }, + { 0x40214bc6a7ef9db2ull, 0x40791268b14e92c4ull }, + { 0x40214c49ba5e3540ull, 0x407916dc019fe19aull }, + { 0x40214ccccccccccdull, 0x40791b501c2db3e0ull }, + { 0x40214d4fdf3b645aull, 0x40791fc5011bef9eull }, + { 0x40214dd2f1a9fbe8ull, 0x4079243ab08e8139ull }, + { 0x40214e5604189375ull, 0x407928b12aa95b5full }, + { 0x40214ed916872b02ull, 0x40792d286f907737ull }, + { 0x40214f5c28f5c28full, 0x407931a07f67d444ull }, + { 0x40214fdf3b645a1dull, 0x407936195a537874ull }, + { 0x402150624dd2f1aaull, 0x40793a9300777002ull }, + { 0x402150e560418937ull, 0x40793f0d71f7cda6ull }, + { 0x4021516872b020c5ull, 0x40794388aef8aa83ull }, + { 0x402151eb851eb852ull, 0x40794804b79e2607ull }, + { 0x4021526e978d4fdfull, 0x40794c818c0c6625ull }, + { 0x402152f1a9fbe76dull, 0x407950ff2c67973cull }, + { 0x40215374bc6a7efaull, 0x4079557d98d3ebf9ull }, + { 0x402153f7ced91687ull, 0x407959fcd1759d93ull }, + { 0x4021547ae147ae15ull, 0x40795e7cd670ebb0ull }, + { 0x402154fdf3b645a2ull, 0x407962fda7ea1c47ull }, + { 0x402155810624dd2full, 0x4079677f46057bddull }, + { 0x40215604189374bdull, 0x40796c01b0e75d67ull }, + { 0x402156872b020c4aull, 0x40797084e8b41a33ull }, + { 0x4021570a3d70a3d7ull, 0x40797508ed90121cull }, + { 0x4021578d4fdf3b64ull, 0x4079798dbf9fab6cull }, + { 0x40215810624dd2f2ull, 0x40797e135f0752e7ull }, + { 0x4021589374bc6a7full, 0x40798299cbeb7bb3ull }, + { 0x40215916872b020cull, 0x4079872106709f85ull }, + { 0x402159999999999aull, 0x40798ba90ebb3e8dull }, + { 0x40215a1cac083127ull, 0x40799031e4efdf5bull }, + { 0x40215a9fbe76c8b4ull, 0x407994bb89330f16ull }, + { 0x40215b22d0e56042ull, 0x40799945fba96161ull }, + { 0x40215ba5e353f7cfull, 0x40799dd13c777043ull }, + { 0x40215c28f5c28f5cull, 0x4079a25d4bc1dc5cull }, + { 0x40215cac083126eaull, 0x4079a6ea29ad4cceull }, + { 0x40215d2f1a9fbe77ull, 0x4079ab77d65e6f21ull }, + { 0x40215db22d0e5604ull, 0x4079b00651f9f779ull }, + { 0x40215e353f7ced92ull, 0x4079b4959ca4a081ull }, + { 0x40215eb851eb851full, 0x4079b925b6832b4cull }, + { 0x40215f3b645a1cacull, 0x4079bdb69fba5f8full }, + { 0x40215fbe76c8b439ull, 0x4079c248586f0b7cull }, + { 0x4021604189374bc7ull, 0x4079c6dae0c603d8ull }, + { 0x402160c49ba5e354ull, 0x4079cb6e38e423d7ull }, + { 0x40216147ae147ae1ull, 0x4079d00260ee4d50ull }, + { 0x402161cac083126full, 0x4079d497590968aaull }, + { 0x4021624dd2f1a9fcull, 0x4079d92d215a64bbull }, + { 0x402162d0e5604189ull, 0x4079ddc3ba063704ull }, + { 0x40216353f7ced917ull, 0x4079e25b2331db95ull }, + { 0x402163d70a3d70a4ull, 0x4079e6f35d0254f3ull }, + { 0x4021645a1cac0831ull, 0x4079eb8c679cac51ull }, + { 0x402164dd2f1a9fbfull, 0x4079f0264325f174ull }, + { 0x402165604189374cull, 0x4079f4c0efc33a9aull }, + { 0x402165e353f7ced9ull, 0x4079f95c6d99a4b3ull }, + { 0x4021666666666667ull, 0x4079fdf8bcce5343ull }, + { 0x402166e978d4fdf4ull, 0x407a0295dd86704dull }, + { 0x4021676c8b439581ull, 0x407a0733cfe72c86ull }, + { 0x402167ef9db22d0eull, 0x407a0bd29415bf35ull }, + { 0x40216872b020c49cull, 0x407a10722a376645ull }, + { 0x402168f5c28f5c29ull, 0x407a151292716622ull }, + { 0x40216978d4fdf3b6ull, 0x407a19b3cce909f0ull }, + { 0x402169fbe76c8b44ull, 0x407a1e55d9c3a375ull }, + { 0x40216a7ef9db22d1ull, 0x407a22f8b9268af6ull }, + { 0x40216b020c49ba5eull, 0x407a279c6b371f7aull }, + { 0x40216b851eb851ecull, 0x407a2c40f01ac6a6ull }, + { 0x40216c083126e979ull, 0x407a30e647f6eca9ull }, + { 0x40216c8b43958106ull, 0x407a358c72f10472ull }, + { 0x40216d0e56041894ull, 0x407a3a33712e8794ull }, + { 0x40216d916872b021ull, 0x407a3edb42d4f631ull }, + { 0x40216e147ae147aeull, 0x407a4383e809d72bull }, + { 0x40216e978d4fdf3bull, 0x407a482d60f2b806ull }, + { 0x40216f1a9fbe76c9ull, 0x407a4cd7adb52cf8ull }, + { 0x40216f9db22d0e56ull, 0x407a5182ce76d0caull }, + { 0x40217020c49ba5e3ull, 0x407a562ec35d450aull }, + { 0x402170a3d70a3d71ull, 0x407a5adb8c8e31f7ull }, + { 0x40217126e978d4feull, 0x407a5f892a2f4662ull }, + { 0x402171a9fbe76c8bull, 0x407a64379c6637e8ull }, + { 0x4021722d0e560419ull, 0x407a68e6e358c2d8ull }, + { 0x402172b020c49ba6ull, 0x407a6d96ff2caa18ull }, + { 0x4021733333333333ull, 0x407a7247f007b75eull }, + { 0x402173b645a1cac1ull, 0x407a76f9b60fbb13ull }, + { 0x402174395810624eull, 0x407a7bac516a8c3full }, + { 0x402174bc6a7ef9dbull, 0x407a805fc23e08b9ull }, + { 0x4021753f7ced9169ull, 0x407a851408b01512ull }, + { 0x402175c28f5c28f6ull, 0x407a89c924e69c7bull }, + { 0x40217645a1cac083ull, 0x407a8e7f170790faull }, + { 0x402176c8b4395810ull, 0x407a9335df38eb48ull }, + { 0x4021774bc6a7ef9eull, 0x407a97ed7da0aae5ull }, + { 0x402177ced916872bull, 0x407a9ca5f264d5f4ull }, + { 0x40217851eb851eb8ull, 0x407aa15f3dab7975ull }, + { 0x402178d4fdf3b646ull, 0x407aa6195f9aa927ull }, + { 0x4021795810624dd3ull, 0x407aaad458587f70ull }, + { 0x402179db22d0e560ull, 0x407aaf90280b1d97ull }, + { 0x40217a5e353f7ceeull, 0x407ab44cced8aba5ull }, + { 0x40217ae147ae147bull, 0x407ab90a4ce7584full }, + { 0x40217b645a1cac08ull, 0x407abdc8a25d592dull }, + { 0x40217be76c8b4396ull, 0x407ac287cf60ea9dull }, + { 0x40217c6a7ef9db23ull, 0x407ac747d4184faeull }, + { 0x40217ced916872b0ull, 0x407acc08b0a9d252ull }, + { 0x40217d70a3d70a3eull, 0x407ad0ca653bc34bull }, + { 0x40217df3b645a1cbull, 0x407ad58cf1f47a09ull }, + { 0x40217e76c8b43958ull, 0x407ada5056fa54e6ull }, + { 0x40217ef9db22d0e5ull, 0x407adf149473b906ull }, + { 0x40217f7ced916873ull, 0x407ae3d9aa871262ull }, + { 0x4021800000000000ull, 0x407ae89f995ad3adull }, + { 0x40218083126e978dull, 0x407aed6661157688ull }, + { 0x4021810624dd2f1bull, 0x407af22e01dd7b67ull }, + { 0x40218189374bc6a8ull, 0x407af6f67bd96978ull }, + { 0x4021820c49ba5e35ull, 0x407afbbfcf2fcedeull }, + { 0x4021828f5c28f5c3ull, 0x407b0089fc074092ull }, + { 0x402183126e978d50ull, 0x407b055502865a4aull }, + { 0x40218395810624ddull, 0x407b0a20e2d3beb3ull }, + { 0x402184189374bc6bull, 0x407b0eed9d161757ull }, + { 0x4021849ba5e353f8ull, 0x407b13bb3174147full }, + { 0x4021851eb851eb85ull, 0x407b1889a0146d6eull }, + { 0x402185a1cac08313ull, 0x407b1d58e91de048ull }, + { 0x40218624dd2f1aa0ull, 0x407b22290cb731f4ull }, + { 0x402186a7ef9db22dull, 0x407b26fa0b072e57ull }, + { 0x4021872b020c49baull, 0x407b2bcbe434a831ull }, + { 0x402187ae147ae148ull, 0x407b309e9866792eull }, + { 0x4021883126e978d5ull, 0x407b357227c381c4ull }, + { 0x402188b439581062ull, 0x407b3a469272a96bull }, + { 0x402189374bc6a7f0ull, 0x407b3f1bd89ade84ull }, + { 0x402189ba5e353f7dull, 0x407b43f1fa63163cull }, + { 0x40218a3d70a3d70aull, 0x407b48c8f7f24cc8ull }, + { 0x40218ac083126e98ull, 0x407b4da0d16f8546ull }, + { 0x40218b4395810625ull, 0x407b52798701c9a8ull }, + { 0x40218bc6a7ef9db2ull, 0x407b575318d02ae6ull }, + { 0x40218c49ba5e3540ull, 0x407b5c2d8701c0ebull }, + { 0x40218ccccccccccdull, 0x407b6108d1bdaa74ull }, + { 0x40218d4fdf3b645aull, 0x407b65e4f92b0d4eull }, + { 0x40218dd2f1a9fbe8ull, 0x407b6ac1fd711636ull }, + { 0x40218e5604189375ull, 0x407b6f9fdeb6f8c3ull }, + { 0x40218ed916872b02ull, 0x407b747e9d23ef9dull }, + { 0x40218f5c28f5c28full, 0x407b795e38df3c5bull }, + { 0x40218fdf3b645a1dull, 0x407b7e3eb2102792ull }, + { 0x402190624dd2f1aaull, 0x407b832008de00b6ull }, + { 0x402190e560418937ull, 0x407b88023d701e51ull }, + { 0x4021916872b020c5ull, 0x407b8ce54fedddeaull }, + { 0x402191eb851eb852ull, 0x407b91c9407ea3e5ull }, + { 0x4021926e978d4fdfull, 0x407b96ae0f49dbc5ull }, + { 0x402192f1a9fbe76dull, 0x407b9b93bc76f809ull }, + { 0x40219374bc6a7efaull, 0x407ba07a482d7214ull }, + { 0x402193f7ced91687ull, 0x407ba561b294ca6bull }, + { 0x4021947ae147ae15ull, 0x407baa49fbd48891ull }, + { 0x402194fdf3b645a2ull, 0x407baf3324143af4ull }, + { 0x402195810624dd2full, 0x407bb41d2b7b7724ull }, + { 0x40219604189374bdull, 0x407bb9081231d9b6ull }, + { 0x402196872b020c4aull, 0x407bbdf3d85f062cull }, + { 0x4021970a3d70a3d7ull, 0x407bc2e07e2aa72dull }, + { 0x4021978d4fdf3b64ull, 0x407bc7ce03bc6e61ull }, + { 0x40219810624dd2f2ull, 0x407bccbc693c1486ull }, + { 0x4021989374bc6a7full, 0x407bd1abaed1594bull }, + { 0x40219916872b020cull, 0x407bd69bd4a40387ull }, + { 0x402199999999999aull, 0x407bdb8cdadbe124ull }, + { 0x40219a1cac083127ull, 0x407be07ec1a0c6feull }, + { 0x40219a9fbe76c8b4ull, 0x407be571891a911full }, + { 0x40219b22d0e56042ull, 0x407bea65317122a8ull }, + { 0x40219ba5e353f7cfull, 0x407bef59bacc65aeull }, + { 0x40219c28f5c28f5cull, 0x407bf44f25544b79ull }, + { 0x40219cac083126eaull, 0x407bf9457130cc6bull }, + { 0x40219d2f1a9fbe77ull, 0x407bfe3c9e89e7deull }, + { 0x40219db22d0e5604ull, 0x407c0334ad87a464ull }, + { 0x40219e353f7ced92ull, 0x407c082d9e520faaull }, + { 0x40219eb851eb851full, 0x407c0d2771113e5cull }, + { 0x40219f3b645a1cacull, 0x407c122225ed4c60ull }, + { 0x40219fbe76c8b439ull, 0x407c171dbd0e5cb1ull }, + { 0x4021a04189374bc7ull, 0x407c1c1a369c9974ull }, + { 0x4021a0c49ba5e354ull, 0x407c211792c033d1ull }, + { 0x4021a147ae147ae1ull, 0x407c2615d1a16432ull }, + { 0x4021a1cac083126full, 0x407c2b14f3686a22ull }, + { 0x4021a24dd2f1a9fcull, 0x407c3014f83d8c34ull }, + { 0x4021a2d0e5604189ull, 0x407c3515e0491840ull }, + { 0x4021a353f7ced917ull, 0x407c3a17abb36345ull }, + { 0x4021a3d70a3d70a4ull, 0x407c3f1a5aa4c94dull }, + { 0x4021a45a1cac0831ull, 0x407c441ded45ada9ull }, + { 0x4021a4dd2f1a9fbfull, 0x407c492263be7ad5ull }, + { 0x4021a5604189374cull, 0x407c4e27be37a25full }, + { 0x4021a5e353f7ced9ull, 0x407c532dfcd99d1dull }, + { 0x4021a66666666667ull, 0x407c58351fcceb16ull }, + { 0x4021a6e978d4fdf4ull, 0x407c5d3d273a1362ull }, + { 0x4021a76c8b439581ull, 0x407c62461349a46aull }, + { 0x4021a7ef9db22d0eull, 0x407c674fe42433c1ull }, + { 0x4021a872b020c49cull, 0x407c6c5a99f25e34ull }, + { 0x4021a8f5c28f5c29ull, 0x407c716634dcc7adull }, + { 0x4021a978d4fdf3b6ull, 0x407c7672b50c1b66ull }, + { 0x4021a9fbe76c8b44ull, 0x407c7b801aa90bd4ull }, + { 0x4021aa7ef9db22d1ull, 0x407c808e65dc5286ull }, + { 0x4021ab020c49ba5eull, 0x407c859d96ceb065ull }, + { 0x4021ab851eb851ecull, 0x407c8aadada8ed94ull }, + { 0x4021ac083126e979ull, 0x407c8fbeaa93d957ull }, + { 0x4021ac8b43958106ull, 0x407c94d08db84a4dull }, + { 0x4021ad0e56041894ull, 0x407c99e3573f1e55ull }, + { 0x4021ad916872b021ull, 0x407c9ef707513a73ull }, + { 0x4021ae147ae147aeull, 0x407ca40b9e178b08ull }, + { 0x4021ae978d4fdf3bull, 0x407ca9211bbb03b2ull }, + { 0x4021af1a9fbe76c9ull, 0x407cae3780649f5bull }, + { 0x4021af9db22d0e56ull, 0x407cb34ecc3d6017ull }, + { 0x4021b020c49ba5e3ull, 0x407cb866ff6e4f60ull }, + { 0x4021b0a3d70a3d71ull, 0x407cbd801a207df7ull }, + { 0x4021b126e978d4feull, 0x407cc29a1c7d03caull }, + { 0x4021b1a9fbe76c8bull, 0x407cc7b506ad0030ull }, + { 0x4021b22d0e560419ull, 0x407cccd0d8d999cdull }, + { 0x4021b2b020c49ba6ull, 0x407cd1ed932bfe76ull }, + { 0x4021b33333333333ull, 0x407cd70b35cd636aull }, + { 0x4021b3b645a1cac1ull, 0x407cdc29c0e7053eull }, + { 0x4021b4395810624eull, 0x407ce14934a227b4ull }, + { 0x4021b4bc6a7ef9dbull, 0x407ce66991281606ull }, + { 0x4021b53f7ced9169ull, 0x407ceb8ad6a222bdull }, + { 0x4021b5c28f5c28f6ull, 0x407cf0ad0539a79dull }, + { 0x4021b645a1cac083ull, 0x407cf5d01d1805deull }, + { 0x4021b6c8b4395810ull, 0x407cfaf41e66a607ull }, + { 0x4021b74bc6a7ef9eull, 0x407d0019094ef800ull }, + { 0x4021b7ced916872bull, 0x407d053eddfa72f3ull }, + { 0x4021b851eb851eb8ull, 0x407d0a659c92957full }, + { 0x4021b8d4fdf3b646ull, 0x407d0f8d4540e5a4ull }, + { 0x4021b95810624dd3ull, 0x407d14b5d82ef0a1ull }, + { 0x4021b9db22d0e560ull, 0x407d19df55864b35ull }, + { 0x4021ba5e353f7ceeull, 0x407d1f09bd709180ull }, + { 0x4021bae147ae147bull, 0x407d2435101766e5ull }, + { 0x4021bb645a1cac08ull, 0x407d29614da4764bull }, + { 0x4021bbe76c8b4396ull, 0x407d2e8e764171feull }, + { 0x4021bc6a7ef9db23ull, 0x407d33bc8a181393ull }, + { 0x4021bced916872b0ull, 0x407d38eb89521c24ull }, + { 0x4021bd70a3d70a3eull, 0x407d3e1b74195436ull }, + { 0x4021bdf3b645a1cbull, 0x407d434c4a978b9aull }, + { 0x4021be76c8b43958ull, 0x407d487e0cf699aaull }, + { 0x4021bef9db22d0e5ull, 0x407d4db0bb605d24ull }, + { 0x4021bf7ced916873ull, 0x407d52e455febc41ull }, + { 0x4021c00000000000ull, 0x407d5818dcfba487ull }, + { 0x4021c083126e978dull, 0x407d5d4e50810b10ull }, + { 0x4021c10624dd2f1bull, 0x407d6284b0b8ec67ull }, + { 0x4021c189374bc6a8ull, 0x407d67bbfdcd4c6bull }, + { 0x4021c20c49ba5e35ull, 0x407d6cf437e83691ull }, + { 0x4021c28f5c28f5c3ull, 0x407d722d5f33bdc4ull }, + { 0x4021c3126e978d50ull, 0x407d776773d9fc47ull }, + { 0x4021c395810624ddull, 0x407d7ca2760513f7ull }, + { 0x4021c4189374bc6bull, 0x407d81de65df2e2aull }, + { 0x4021c49ba5e353f8ull, 0x407d871b43927b93ull }, + { 0x4021c51eb851eb85ull, 0x407d8c590f493483ull }, + { 0x4021c5a1cac08313ull, 0x407d9197c92d98c7ull }, + { 0x4021c624dd2f1aa0ull, 0x407d96d77169ef8full }, + { 0x4021c6a7ef9db22dull, 0x407d9c18082887aaull }, + { 0x4021c72b020c49baull, 0x407da1598d93b75full }, + { 0x4021c7ae147ae148ull, 0x407da69c01d5dc84ull }, + { 0x4021c83126e978d5ull, 0x407dabdf65195c55ull }, + { 0x4021c8b439581062ull, 0x407db123b788a3b4ull }, + { 0x4021c9374bc6a7f0ull, 0x407db668f94e2709ull }, + { 0x4021c9ba5e353f7dull, 0x407dbbaf2a946229ull }, + { 0x4021ca3d70a3d70aull, 0x407dc0f64b85d890ull }, + { 0x4021cac083126e98ull, 0x407dc63e5c4d1547ull }, + { 0x4021cb4395810625ull, 0x407dcb875d14aac7ull }, + { 0x4021cbc6a7ef9db2ull, 0x407dd0d14e073332ull }, + { 0x4021cc49ba5e3540ull, 0x407dd61c2f4f503full }, + { 0x4021cccccccccccdull, 0x407ddb680117ab14ull }, + { 0x4021cd4fdf3b645aull, 0x407de0b4c38af48aull }, + { 0x4021cdd2f1a9fbe8ull, 0x407de60276d3e50dull }, + { 0x4021ce5604189375ull, 0x407deb511b1d3c80ull }, + { 0x4021ced916872b02ull, 0x407df0a0b091c27bull }, + { 0x4021cf5c28f5c28full, 0x407df5f1375c4625ull }, + { 0x4021cfdf3b645a1dull, 0x407dfb42afa79e47ull }, + { 0x4021d0624dd2f1aaull, 0x407e0095199ea926ull }, + { 0x4021d0e560418937ull, 0x407e05e8756c4cc4ull }, + { 0x4021d16872b020c5ull, 0x407e0b3cc33b76bdull }, + { 0x4021d1eb851eb852ull, 0x407e109203371c30ull }, + { 0x4021d26e978d4fdfull, 0x407e15e8358a39f8ull }, + { 0x4021d2f1a9fbe76dull, 0x407e1b3f5a5fd495ull }, + { 0x4021d374bc6a7efaull, 0x407e209771e2f808ull }, + { 0x4021d3f7ced91687ull, 0x407e25f07c3eb815ull }, + { 0x4021d47ae147ae15ull, 0x407e2b4a799e3028ull }, + { 0x4021d4fdf3b645a2ull, 0x407e30a56a2c8333ull }, + { 0x4021d5810624dd2full, 0x407e36014e14dbeeull }, + { 0x4021d604189374bdull, 0x407e3b5e25826cbfull }, + { 0x4021d6872b020c4aull, 0x407e40bbf0a06f92ull }, + { 0x4021d70a3d70a3d7ull, 0x407e461aaf9a2624ull }, + { 0x4021d78d4fdf3b64ull, 0x407e4b7a629ad9d1ull }, + { 0x4021d810624dd2f2ull, 0x407e50db09cddbb0ull }, + { 0x4021d89374bc6a7full, 0x407e563ca55e846bull }, + { 0x4021d916872b020cull, 0x407e5b9f3578347aull }, + { 0x4021d9999999999aull, 0x407e6102ba46540aull }, + { 0x4021da1cac083127ull, 0x407e666733f452dbull }, + { 0x4021da9fbe76c8b4ull, 0x407e6bcca2ada886ull }, + { 0x4021db22d0e56042ull, 0x407e7133069dd458ull }, + { 0x4021dba5e353f7cfull, 0x407e769a5ff05d37ull }, + { 0x4021dc28f5c28f5cull, 0x407e7c02aed0d1e6ull }, + { 0x4021dcac083126eaull, 0x407e816bf36ac8dfull }, + { 0x4021dd2f1a9fbe77ull, 0x407e86d62de9e03cull }, + { 0x4021ddb22d0e5604ull, 0x407e8c415e79bdf3ull }, + { 0x4021de353f7ced92ull, 0x407e91ad85460fbaull }, + { 0x4021deb851eb851full, 0x407e971aa27a8aeaull }, + { 0x4021df3b645a1cacull, 0x407e9c88b642ecbbull }, + { 0x4021dfbe76c8b439ull, 0x407ea1f7c0cafa1full }, + { 0x4021e04189374bc7ull, 0x407ea767c23e7fd9ull }, + { 0x4021e0c49ba5e354ull, 0x407eacd8bac95250ull }, + { 0x4021e147ae147ae1ull, 0x407eb24aaa974dd4ull }, + { 0x4021e1cac083126full, 0x407eb7bd91d4567full }, + { 0x4021e24dd2f1a9fcull, 0x407ebd3170ac5815ull }, + { 0x4021e2d0e5604189ull, 0x407ec2a6474b4643ull }, + { 0x4021e353f7ced917ull, 0x407ec81c15dd1c87ull }, + { 0x4021e3d70a3d70a4ull, 0x407ecd92dc8dde0dull }, + { 0x4021e45a1cac0831ull, 0x407ed30a9b8995efull }, + { 0x4021e4dd2f1a9fbfull, 0x407ed88352fc571bull }, + { 0x4021e5604189374cull, 0x407eddfd03123c2full }, + { 0x4021e5e353f7ced9ull, 0x407ee377abf767bfull }, + { 0x4021e66666666667ull, 0x407ee8f34dd80435ull }, + { 0x4021e6e978d4fdf4ull, 0x407eee6fe8e043b1ull }, + { 0x4021e76c8b439581ull, 0x407ef3ed7d3c604bull }, + { 0x4021e7ef9db22d0eull, 0x407ef96c0b189bebull }, + { 0x4021e872b020c49cull, 0x407efeeb92a1405eull }, + { 0x4021e8f5c28f5c29ull, 0x407f046c14029f2eull }, + { 0x4021e978d4fdf3b6ull, 0x407f09ed8f6911e4ull }, + { 0x4021e9fbe76c8b44ull, 0x407f0f700500f9e6ull }, + { 0x4021ea7ef9db22d1ull, 0x407f14f374f6c05cull }, + { 0x4021eb020c49ba5eull, 0x407f1a77df76d671ull }, + { 0x4021eb851eb851ecull, 0x407f1ffd44adb532ull }, + { 0x4021ec083126e979ull, 0x407f2583a4c7dd71ull }, + { 0x4021ec8b43958106ull, 0x407f2b0afff1d808ull }, + { 0x4021ed0e56041894ull, 0x407f3093565835b6ull }, + { 0x4021ed916872b021ull, 0x407f361ca8278f06ull }, + { 0x4021ee147ae147aeull, 0x407f3ba6f58c848cull }, + { 0x4021ee978d4fdf3bull, 0x407f41323eb3bebdull }, + { 0x4021ef1a9fbe76c9ull, 0x407f46be83c9ee06ull }, + { 0x4021ef9db22d0e56ull, 0x407f4c4bc4fbcaa5ull }, + { 0x4021f020c49ba5e3ull, 0x407f51da027614e5ull }, + { 0x4021f0a3d70a3d71ull, 0x407f57693c659504ull }, + { 0x4021f126e978d4feull, 0x407f5cf972f71b14ull }, + { 0x4021f1a9fbe76c8bull, 0x407f628aa6577f39ull }, + { 0x4021f22d0e560419ull, 0x407f681cd6b3a18full }, + { 0x4021f2b020c49ba6ull, 0x407f6db004386a08ull }, + { 0x4021f33333333333ull, 0x407f73442f12c8afull }, + { 0x4021f3b645a1cac1ull, 0x407f78d9576fb589ull }, + { 0x4021f4395810624eull, 0x407f7e6f7d7c3077ull }, + { 0x4021f4bc6a7ef9dbull, 0x407f8406a1654176ull }, + { 0x4021f53f7ced9169ull, 0x407f899ec357f881ull }, + { 0x4021f5c28f5c28f6ull, 0x407f8f37e3816d74ull }, + { 0x4021f645a1cac083ull, 0x407f94d2020ec04cull }, + { 0x4021f6c8b4395810ull, 0x407f9a6d1f2d18fdull }, + { 0x4021f74bc6a7ef9eull, 0x407fa0093b09a789ull }, + { 0x4021f7ced916872bull, 0x407fa5a655d1a3daull }, + { 0x4021f851eb851eb8ull, 0x407fab446fb24dfeull }, + { 0x4021f8d4fdf3b646ull, 0x407fb0e388d8ee0eull }, + { 0x4021f95810624dd3ull, 0x407fb683a172d409ull }, + { 0x4021f9db22d0e560ull, 0x407fbc24b9ad581dull }, + { 0x4021fa5e353f7ceeull, 0x407fc1c6d1b5da82ull }, + { 0x4021fae147ae147bull, 0x407fc769e9b9c35full }, + { 0x4021fb645a1cac08ull, 0x407fcd0e01e6830aull }, + { 0x4021fbe76c8b4396ull, 0x407fd2b31a6991ebull }, + { 0x4021fc6a7ef9db23ull, 0x407fd85933707059ull }, + { 0x4021fced916872b0ull, 0x407fde004d28a6e3ull }, + { 0x4021fd70a3d70a3eull, 0x407fe3a867bfc62aull }, + { 0x4021fdf3b645a1cbull, 0x407fe951836366c6ull }, + { 0x4021fe76c8b43958ull, 0x407feefba0412988ull }, + { 0x4021fef9db22d0e5ull, 0x407ff4a6be86b750ull }, + { 0x4021ff7ced916873ull, 0x407ffa52de61c123ull }, + { 0x4022000000000000ull, 0x4080000000000000ull }, + { 0x40220083126e978dull, 0x408002d711c79a94ull }, + { 0x4022010624dd2f1bull, 0x408005aea49e94fcull }, + { 0x40220189374bc6a8ull, 0x40800886b89bd7e7ull }, + { 0x4022020c49ba5e35ull, 0x40800b5f4dd65026ull }, + { 0x4022028f5c28f5c3ull, 0x40800e386464ee9bull }, + { 0x402203126e978d50ull, 0x40801111fc5ea82bull }, + { 0x40220395810624ddull, 0x408013ec15da75dcull }, + { 0x402204189374bc6bull, 0x408016c6b0ef54ccull }, + { 0x4022049ba5e353f8ull, 0x408019a1cdb44619ull }, + { 0x4022051eb851eb85ull, 0x40801c7d6c404f0bull }, + { 0x402205a1cac08313ull, 0x40801f598caa78fdull }, + { 0x40220624dd2f1aa0ull, 0x408022362f09d150ull }, + { 0x402206a7ef9db22dull, 0x4080251353756991ull }, + { 0x4022072b020c49baull, 0x408027f0fa04575bull }, + { 0x402207ae147ae148ull, 0x40802acf22cdb46aull }, + { 0x4022083126e978d5ull, 0x40802dadcde89e83ull }, + { 0x402208b439581062ull, 0x4080308cfb6c3796ull }, + { 0x402209374bc6a7f0ull, 0x4080336cab6fa5aeull }, + { 0x402209ba5e353f7dull, 0x4080364cde0a12e2ull }, + { 0x40220a3d70a3d70aull, 0x4080392d9352ad74ull }, + { 0x40220ac083126e98ull, 0x40803c0ecb60a7c6ull }, + { 0x40220b4395810625ull, 0x40803ef0864b3846ull }, + { 0x40220bc6a7ef9db2ull, 0x408041d2c4299991ull }, + { 0x40220c49ba5e3540ull, 0x408044b585130a67ull }, + { 0x40220ccccccccccdull, 0x40804798c91ecd93ull }, + { 0x40220d4fdf3b645aull, 0x40804a7c90642a14ull }, + { 0x40220dd2f1a9fbe8ull, 0x40804d60dafa6b0bull }, + { 0x40220e5604189375ull, 0x40805045a8f8dfaaull }, + { 0x40220ed916872b02ull, 0x4080532afa76db57ull }, + { 0x40220f5c28f5c28full, 0x40805610cf8bb598ull }, + { 0x40220fdf3b645a1dull, 0x408058f7284eca1aull }, + { 0x402210624dd2f1aaull, 0x40805bde04d778a2ull }, + { 0x402210e560418937ull, 0x40805ec5653d252aull }, + { 0x4022116872b020c5ull, 0x408061ad499737d5ull }, + { 0x402211eb851eb852ull, 0x40806495b1fd1cd8ull }, + { 0x4022126e978d4fdfull, 0x4080677e9e8644a8ull }, + { 0x402212f1a9fbe76dull, 0x40806a680f4a23ddull }, + { 0x40221374bc6a7efaull, 0x40806d520460332cull }, + { 0x402213f7ced91687ull, 0x4080703c7ddfef84ull }, + { 0x4022147ae147ae15ull, 0x408073277be0d9ffull }, + { 0x402214fdf3b645a2ull, 0x40807612fe7a77d3ull }, + { 0x402215810624dd2full, 0x408078ff05c45272ull }, + { 0x40221604189374bdull, 0x40807beb91d5f77full }, + { 0x402216872b020c4aull, 0x40807ed8a2c6f8b7ull }, + { 0x4022170a3d70a3d7ull, 0x408081c638aeec18ull }, + { 0x4022178d4fdf3b64ull, 0x408084b453a56bccull }, + { 0x40221810624dd2f2ull, 0x408087a2f3c2162full }, + { 0x4022189374bc6a7full, 0x40808a92191c8dc2ull }, + { 0x40221916872b020cull, 0x40808d81c3cc7946ull }, + { 0x402219999999999aull, 0x40809071f3e983afull }, + { 0x40221a1cac083127ull, 0x40809362a98b5c15ull }, + { 0x40221a9fbe76c8b4ull, 0x40809653e4c9b5d4ull }, + { 0x40221b22d0e56042ull, 0x40809945a5bc487eull }, + { 0x40221ba5e353f7cfull, 0x40809c37ec7acfc8ull }, + { 0x40221c28f5c28f5cull, 0x40809f2ab91d0bb0ull }, + { 0x40221cac083126eaull, 0x4080a21e0bbac06bull }, + { 0x40221d2f1a9fbe77ull, 0x4080a511e46bb655ull }, + { 0x40221db22d0e5604ull, 0x4080a8064347ba12ull }, + { 0x40221e353f7ced92ull, 0x4080aafb28669c84ull }, + { 0x40221eb851eb851full, 0x4080adf093e032b1ull }, + { 0x40221f3b645a1cacull, 0x4080b0e685cc55edull }, + { 0x40221fbe76c8b439ull, 0x4080b3dcfe42e3c4ull }, + { 0x4022204189374bc7ull, 0x4080b6d3fd5bbe01ull }, + { 0x402220c49ba5e354ull, 0x4080b9cb832eca9dull }, + { 0x40222147ae147ae1ull, 0x4080bcc38fd3f3e1ull }, + { 0x402221cac083126full, 0x4080bfbc23632852ull }, + { 0x4022224dd2f1a9fcull, 0x4080c2b53df45aa5ull }, + { 0x402222d0e5604189ull, 0x4080c5aedf9f81e1ull }, + { 0x40222353f7ced917ull, 0x4080c8a9087c994cull }, + { 0x402223d70a3d70a4ull, 0x4080cba3b8a3a060ull }, + { 0x4022245a1cac0831ull, 0x4080ce9ef02c9ae6ull }, + { 0x402224dd2f1a9fbfull, 0x4080d19aaf2f90eeull }, + { 0x402225604189374cull, 0x4080d496f5c48ebbull }, + { 0x402225e353f7ced9ull, 0x4080d793c403a4e2ull }, + { 0x4022266666666667ull, 0x4080da911a04e842ull }, + { 0x402226e978d4fdf4ull, 0x4080dd8ef7e071edull }, + { 0x4022276c8b439581ull, 0x4080e08d5dae5f4dull }, + { 0x402227ef9db22d0eull, 0x4080e38c4b86d20full }, + { 0x40222872b020c49cull, 0x4080e68bc181f02eull }, + { 0x402228f5c28f5c29ull, 0x4080e98bbfb7e3ddull }, + { 0x40222978d4fdf3b6ull, 0x4080ec8c4640dbaaull }, + { 0x402229fbe76c8b44ull, 0x4080ef8d55350a6full }, + { 0x40222a7ef9db22d1ull, 0x4080f28eecaca740ull }, + { 0x40222b020c49ba5eull, 0x4080f5910cbfed8dull }, + { 0x40222b851eb851ecull, 0x4080f893b5871d16ull }, + { 0x40222c083126e979ull, 0x4080fb96e71a79d4ull }, + { 0x40222c8b43958106ull, 0x4080fe9aa1924c24ull }, + { 0x40222d0e56041894ull, 0x4081019ee506e0b0ull }, + { 0x40222d916872b021ull, 0x408104a3b1908860ull }, + { 0x40222e147ae147aeull, 0x408107a907479882ull }, + { 0x40222e978d4fdf3bull, 0x40810aaee6446aacull }, + { 0x40222f1a9fbe76c9ull, 0x40810db54e9f5ccfull }, + { 0x40222f9db22d0e56ull, 0x408110bc4070d11cull }, + { 0x40223020c49ba5e3ull, 0x408113c3bbd12e2bull }, + { 0x402230a3d70a3d71ull, 0x408116cbc0d8dee7ull }, + { 0x40223126e978d4feull, 0x408119d44fa05282ull }, + { 0x402231a9fbe76c8bull, 0x40811cdd683ffc92ull }, + { 0x4022322d0e560419ull, 0x40811fe70ad05508ull }, + { 0x402232b020c49ba6ull, 0x408122f13769d818ull }, + { 0x4022333333333333ull, 0x408125fbee250663ull }, + { 0x402233b645a1cac1ull, 0x408129072f1a64e2ull }, + { 0x402234395810624eull, 0x40812c12fa627cd6ull }, + { 0x402234bc6a7ef9dbull, 0x40812f1f5015dbeeull }, + { 0x4022353f7ced9169ull, 0x4081322c304d1434ull }, + { 0x402235c28f5c28f6ull, 0x408135399b20bbfdull }, + { 0x40223645a1cac083ull, 0x4081384790a96e0cull }, + { 0x402236c8b4395810ull, 0x40813b5610ffc97full }, + { 0x4022374bc6a7ef9eull, 0x40813e651c3c71d7ull }, + { 0x402237ced916872bull, 0x40814174b2780ee0ull }, + { 0x40223851eb851eb8ull, 0x40814484d3cb4cdeull }, + { 0x402238d4fdf3b646ull, 0x40814795804edc71ull }, + { 0x4022395810624dd3ull, 0x40814aa6b81b728dull }, + { 0x402239db22d0e560ull, 0x40814db87b49c898ull }, + { 0x40223a5e353f7ceeull, 0x408150cac9f29c5bull }, + { 0x40223ae147ae147bull, 0x408153dda42eaff5ull }, + { 0x40223b645a1cac08ull, 0x408156f10a16c9f9ull }, + { 0x40223be76c8b4396ull, 0x40815a04fbc3b560ull }, + { 0x40223c6a7ef9db23ull, 0x40815d19794e4179ull }, + { 0x40223ced916872b0ull, 0x4081602e82cf420bull }, + { 0x40223d70a3d70a3eull, 0x40816344185f8f46ull }, + { 0x40223df3b645a1cbull, 0x4081665a3a1805b1ull }, + { 0x40223e76c8b43958ull, 0x40816970e811864eull }, + { 0x40223ef9db22d0e5ull, 0x40816c882264f685ull }, + { 0x40223f7ced916873ull, 0x40816f9fe92b402eull }, + { 0x4022400000000000ull, 0x408172b83c7d517bull }, + { 0x40224083126e978dull, 0x408175d11c741d1eull }, + { 0x4022410624dd2f1bull, 0x408178ea89289a36ull }, + { 0x40224189374bc6a8ull, 0x40817c0482b3c43full }, + { 0x4022420c49ba5e35ull, 0x40817f1f092e9b35ull }, + { 0x4022428f5c28f5c3ull, 0x4081823a1cb22386ull }, + { 0x402243126e978d50ull, 0x40818555bd5765fdull }, + { 0x40224395810624ddull, 0x40818871eb376fe9ull }, + { 0x402244189374bc6bull, 0x40818b8ea66b530cull }, + { 0x4022449ba5e353f8ull, 0x40818eabef0c2588ull }, + { 0x4022451eb851eb85ull, 0x408191c9c5330208ull }, + { 0x402245a1cac08313ull, 0x408194e828f907a8ull }, + { 0x40224624dd2f1aa0ull, 0x408198071a7759eaull }, + { 0x402246a7ef9db22dull, 0x40819b2699c720d8ull }, + { 0x4022472b020c49baull, 0x40819e46a70188ebull }, + { 0x402247ae147ae148ull, 0x4081a167423fc31dull }, + { 0x4022483126e978d5ull, 0x4081a4886b9b04cdull }, + { 0x402248b439581062ull, 0x4081a7aa232c87e7ull }, + { 0x402249374bc6a7f0ull, 0x4081aacc690d8aceull }, + { 0x402249ba5e353f7dull, 0x4081adef3d575053ull }, + { 0x40224a3d70a3d70aull, 0x4081b112a0231fd0ull }, + { 0x40224ac083126e98ull, 0x4081b436918a4520ull }, + { 0x40224b4395810625ull, 0x4081b75b11a61086ull }, + { 0x40224bc6a7ef9db2ull, 0x4081ba80208fd6d7ull }, + { 0x40224c49ba5e3540ull, 0x4081bda5be60f168ull }, + { 0x40224ccccccccccdull, 0x4081c0cbeb32bdfaull }, + { 0x40224d4fdf3b645aull, 0x4081c3f2a71e9ee1ull }, + { 0x40224dd2f1a9fbe8ull, 0x4081c719f23dfaf4ull }, + { 0x40224e5604189375ull, 0x4081ca41ccaa3d79ull }, + { 0x40224ed916872b02ull, 0x4081cd6a367cd64cull }, + { 0x40224f5c28f5c28full, 0x4081d0932fcf39c4ull }, + { 0x40224fdf3b645a1dull, 0x4081d3bcb8bae0c8ull }, + { 0x402250624dd2f1aaull, 0x4081d6e6d15948adull }, + { 0x402250e560418937ull, 0x4081da1179c3f364ull }, + { 0x4022516872b020c5ull, 0x4081dd3cb2146765ull }, + { 0x402251eb851eb852ull, 0x4081e0687a642f9aull }, + { 0x4022526e978d4fdfull, 0x4081e394d2ccdb8dull }, + { 0x402252f1a9fbe76dull, 0x4081e6c1bb67ff4eull }, + { 0x40225374bc6a7efaull, 0x4081e9ef344f3366ull }, + { 0x402253f7ced91687ull, 0x4081ed1d3d9c14fdull }, + { 0x4022547ae147ae15ull, 0x4081f04bd76845c4ull }, + { 0x402254fdf3b645a2ull, 0x4081f37b01cd6be9ull }, + { 0x402255810624dd2full, 0x4081f6aabce53238ull }, + { 0x40225604189374bdull, 0x4081f9db08c9480cull }, + { 0x402256872b020c4aull, 0x4081fd0be593613eull }, + { 0x4022570a3d70a3d7ull, 0x4082003d535d3649ull }, + { 0x4022578d4fdf3b64ull, 0x4082036f52408430ull }, + { 0x40225810624dd2f2ull, 0x408206a1e2570c91ull }, + { 0x4022589374bc6a7full, 0x408209d503ba9588ull }, + { 0x40225916872b020cull, 0x40820d08b684e9d9ull }, + { 0x402259999999999aull, 0x4082103cfacfd8d9ull }, + { 0x40225a1cac083127ull, 0x40821371d0b53661ull }, + { 0x40225a9fbe76c8b4ull, 0x408216a7384edaf2ull }, + { 0x40225b22d0e56042ull, 0x408219dd31b6a3a4ull }, + { 0x40225ba5e353f7cfull, 0x40821d13bd067213ull }, + { 0x40225c28f5c28f5cull, 0x4082204ada582c85ull }, + { 0x40225cac083126eaull, 0x4082238289c5bddbull }, + { 0x40225d2f1a9fbe77ull, 0x408226bacb69157bull }, + { 0x40225db22d0e5604ull, 0x408229f39f5c277aull }, + { 0x40225e353f7ced92ull, 0x40822d2d05b8ec86ull }, + { 0x40225eb851eb851full, 0x40823066fe9961daull }, + { 0x40225f3b645a1cacull, 0x408233a18a17895cull }, + { 0x40225fbe76c8b439ull, 0x408236dca84d698full }, + { 0x4022604189374bc7ull, 0x40823a1859550d96ull }, + { 0x402260c49ba5e354ull, 0x40823d549d488524ull }, + { 0x40226147ae147ae1ull, 0x408240917441e49dull }, + { 0x402261cac083126full, 0x408243cede5b4508ull }, + { 0x4022624dd2f1a9fcull, 0x4082470cdbaec3faull }, + { 0x402262d0e5604189ull, 0x40824a4b6c5683bdull }, + { 0x40226353f7ced917ull, 0x40824d8a906cab3full }, + { 0x402263d70a3d70a4ull, 0x408250ca480b6601ull }, + { 0x4022645a1cac0831ull, 0x4082540a934ce43aull }, + { 0x402264dd2f1a9fbfull, 0x4082574b724b5ac9ull }, + { 0x402265604189374cull, 0x40825a8ce521031eull }, + { 0x402265e353f7ced9ull, 0x40825dceebe81b67ull }, + { 0x4022666666666667ull, 0x4082611186bae678ull }, + { 0x402266e978d4fdf4ull, 0x40826454b5b3abbeull }, + { 0x4022676c8b439581ull, 0x4082679878ecb760ull }, + { 0x402267ef9db22d0eull, 0x40826adcd0805a2cull }, + { 0x40226872b020c49cull, 0x40826e21bc88e9a1ull }, + { 0x402268f5c28f5c29ull, 0x408271673d20bfd8ull }, + { 0x40226978d4fdf3b6ull, 0x408274ad52623babull }, + { 0x402269fbe76c8b44ull, 0x408277f3fc67c0a1ull }, + { 0x40226a7ef9db22d1ull, 0x40827b3b3b4bb6dfull }, + { 0x40226b020c49ba5eull, 0x40827e830f288b4cull }, + { 0x40226b851eb851ecull, 0x408281cb7818af7full }, + { 0x40226c083126e979ull, 0x40828514763699afull }, + { 0x40226c8b43958106ull, 0x4082885e099cc4d8ull }, + { 0x40226d0e56041894ull, 0x40828ba83265b0aaull }, + { 0x40226d916872b021ull, 0x40828ef2f0abe173ull }, + { 0x40226e147ae147aeull, 0x4082923e4489e04dull }, + { 0x40226e978d4fdf3cull, 0x4082958a2e1a3b07ull }, + { 0x40226f1a9fbe76c9ull, 0x408298d6ad778411ull }, + { 0x40226f9db22d0e56ull, 0x40829c23c2bc52a9ull }, + { 0x40227020c49ba5e3ull, 0x40829f716e0342bcull }, + { 0x402270a3d70a3d71ull, 0x4082a2bfaf66f4f9ull }, + { 0x40227126e978d4feull, 0x4082a60e87020eb5ull }, + { 0x402271a9fbe76c8bull, 0x4082a95df4ef3a11ull }, + { 0x4022722d0e560419ull, 0x4082acadf94925ecull }, + { 0x402272b020c49ba6ull, 0x4082affe942a85cfull }, + { 0x4022733333333333ull, 0x4082b34fc5ae1212ull }, + { 0x402273b645a1cac1ull, 0x4082b6a18dee87caull }, + { 0x402274395810624eull, 0x4082b9f3ed06a8bbull }, + { 0x402274bc6a7ef9dbull, 0x4082bd46e3113b7aull }, + { 0x4022753f7ced9169ull, 0x4082c09a70290b5eull }, + { 0x402275c28f5c28f6ull, 0x4082c3ee9468e86aull }, + { 0x40227645a1cac083ull, 0x4082c7434feba778ull }, + { 0x402276c8b4395810ull, 0x4082ca98a2cc2221ull }, + { 0x4022774bc6a7ef9eull, 0x4082cdee8d2536c6ull }, + { 0x402277ced916872bull, 0x4082d1450f11c87aull }, + { 0x40227851eb851eb8ull, 0x4082d49c28acbf29ull }, + { 0x402278d4fdf3b646ull, 0x4082d7f3da110786ull }, + { 0x4022795810624dd3ull, 0x4082db4c235992f9ull }, + { 0x402279db22d0e560ull, 0x4082dea504a157c5ull }, + { 0x40227a5e353f7ceeull, 0x4082e1fe7e0350f5ull }, + { 0x40227ae147ae147bull, 0x4082e5588f9a7e4cull }, + { 0x40227b645a1cac08ull, 0x4082e8b33981e46cull }, + { 0x40227be76c8b4396ull, 0x4082ec0e7bd48cc1ull }, + { 0x40227c6a7ef9db23ull, 0x4082ef6a56ad8571ull }, + { 0x40227ced916872b0ull, 0x4082f2c6ca27e184ull }, + { 0x40227d70a3d70a3eull, 0x4082f623d65eb8cdull }, + { 0x40227df3b645a1cbull, 0x4082f9817b6d27dfull }, + { 0x40227e76c8b43958ull, 0x4082fcdfb96e502dull }, + { 0x40227ef9db22d0e5ull, 0x4083003e907d57f6ull }, + { 0x40227f7ced916873ull, 0x4083039e00b56a52ull }, + { 0x4022800000000000ull, 0x408306fe0a31b715ull }, + { 0x40228083126e978dull, 0x40830a5ead0d72feull }, + { 0x4022810624dd2f1bull, 0x40830dbfe963d79cull }, + { 0x40228189374bc6a8ull, 0x40831121bf502344ull }, + { 0x4022820c49ba5e35ull, 0x408314842eed9931ull }, + { 0x4022828f5c28f5c3ull, 0x408317e738578178ull }, + { 0x402283126e978d50ull, 0x40831b4adba928eeull }, + { 0x40228395810624ddull, 0x40831eaf18fde157ull }, + { 0x402284189374bc6bull, 0x40832213f0710152ull }, + { 0x4022849ba5e353f8ull, 0x40832579621de441ull }, + { 0x4022851eb851eb85ull, 0x408328df6e1fea77ull }, + { 0x402285a1cac08313ull, 0x40832c4614927922ull }, + { 0x40228624dd2f1aa0ull, 0x40832fad5590fa3aull }, + { 0x402286a7ef9db22dull, 0x408333153136dca8ull }, + { 0x4022872b020c49baull, 0x4083367da79f942eull }, + { 0x402287ae147ae148ull, 0x408339e6b8e69972ull }, + { 0x4022883126e978d5ull, 0x40833d50652769e9ull }, + { 0x402288b439581062ull, 0x408340baac7d87fbull }, + { 0x402289374bc6a7f0ull, 0x408344258f047af3ull }, + { 0x402289ba5e353f7dull, 0x408347910cd7ceecull }, + { 0x40228a3d70a3d70aull, 0x40834afd261314f6ull }, + { 0x40228ac083126e98ull, 0x40834e69dad1e309ull }, + { 0x40228b4395810625ull, 0x408351d72b2fd3ecull }, + { 0x40228bc6a7ef9db2ull, 0x4083554517488761ull }, + { 0x40228c49ba5e3540ull, 0x408358b39f37a213ull }, + { 0x40228ccccccccccdull, 0x40835c22c318cd7eull }, + { 0x40228d4fdf3b645aull, 0x40835f928307b81eull }, + { 0x40228dd2f1a9fbe8ull, 0x40836302df201558ull }, + { 0x40228e5604189375ull, 0x40836673d77d9d67ull }, + { 0x40228ed916872b02ull, 0x408369e56c3c0d85ull }, + { 0x40228f5c28f5c28full, 0x40836d579d7727d5ull }, + { 0x40228fdf3b645a1dull, 0x408370ca6b4ab36bull }, + { 0x402290624dd2f1aaull, 0x4083743dd5d27c36ull }, + { 0x402290e560418937ull, 0x408377b1dd2a5328ull }, + { 0x4022916872b020c5ull, 0x40837b26816e0e25ull }, + { 0x402291eb851eb852ull, 0x40837e9bc2b987ecull }, + { 0x4022926e978d4fdfull, 0x40838211a128a041ull }, + { 0x402292f1a9fbe76dull, 0x408385881cd73bdcull }, + { 0x40229374bc6a7efaull, 0x408388ff35e14455ull }, + { 0x402293f7ced91687ull, 0x40838c76ec62a84bull }, + { 0x4022947ae147ae15ull, 0x40838fef40775b53ull }, + { 0x402294fdf3b645a2ull, 0x40839368323b55e5ull }, + { 0x402295810624dd2full, 0x408396e1c1ca9582ull }, + { 0x40229604189374bdull, 0x40839a5bef411ca4ull }, + { 0x402296872b020c4aull, 0x40839dd6babaf2abull }, + { 0x4022970a3d70a3d7ull, 0x4083a15224542403ull }, + { 0x4022978d4fdf3b64ull, 0x4083a4ce2c28c20cull }, + { 0x40229810624dd2f2ull, 0x4083a84ad254e32aull }, + { 0x4022989374bc6a7full, 0x4083abc816f4a2a7ull }, + { 0x40229916872b020cull, 0x4083af45fa2420deull }, + { 0x402299999999999aull, 0x4083b2c47bff832cull }, + { 0x40229a1cac083127ull, 0x4083b6439ca2f3d4ull }, + { 0x40229a9fbe76c8b4ull, 0x4083b9c35c2aa22full }, + { 0x40229b22d0e56042ull, 0x4083bd43bab2c299ull }, + { 0x40229ba5e353f7cfull, 0x4083c0c4b8578e58ull }, + { 0x40229c28f5c28f5cull, 0x4083c446553543cbull }, + { 0x40229cac083126eaull, 0x4083c7c891682653ull }, + { 0x40229d2f1a9fbe77ull, 0x4083cb4b6d0c7e42ull }, + { 0x40229db22d0e5604ull, 0x4083cecee83e9903ull }, + { 0x40229e353f7ced92ull, 0x4083d253031ac908ull }, + { 0x40229eb851eb851full, 0x4083d5d7bdbd65b4ull }, + { 0x40229f3b645a1cacull, 0x4083d95d1842cb89ull }, + { 0x40229fbe76c8b439ull, 0x4083dce312c75c07ull }, + { 0x4022a04189374bc7ull, 0x4083e069ad677dc2ull }, + { 0x4022a0c49ba5e354ull, 0x4083e3f0e83f9c42ull }, + { 0x4022a147ae147ae1ull, 0x4083e778c36c2831ull }, + { 0x4022a1cac083126full, 0x4083eb013f099744ull }, + { 0x4022a24dd2f1a9fcull, 0x4083ee8a5b346428ull }, + { 0x4022a2d0e5604189ull, 0x4083f21418090eaeull }, + { 0x4022a353f7ced917ull, 0x4083f59e75a41bb6ull }, + { 0x4022a3d70a3d70a4ull, 0x4083f9297422151bull }, + { 0x4022a45a1cac0831ull, 0x4083fcb5139f89deull }, + { 0x4022a4dd2f1a9fbfull, 0x4084004154390e0full }, + { 0x4022a5604189374cull, 0x408403ce360b3ac1ull }, + { 0x4022a5e353f7ced9ull, 0x4084075bb932ae2bull }, + { 0x4022a66666666667ull, 0x40840ae9ddcc0b9aull }, + { 0x4022a6e978d4fdf4ull, 0x40840e78a3f3fb5cull }, + { 0x4022a76c8b439581ull, 0x408412080bc72ae9ull }, + { 0x4022a7ef9db22d0eull, 0x4084159815624ccaull }, + { 0x4022a872b020c49cull, 0x40841928c0e218a7ull }, + { 0x4022a8f5c28f5c29ull, 0x40841cba0e634b2cull }, + { 0x4022a978d4fdf3b6ull, 0x4084204bfe02a633ull }, + { 0x4022a9fbe76c8b44ull, 0x408423de8fdcf0b3ull }, + { 0x4022aa7ef9db22d1ull, 0x40842771c40ef6a6ull }, + { 0x4022ab020c49ba5eull, 0x40842b059ab5893bull }, + { 0x4022ab851eb851ecull, 0x40842e9a13ed7ebdull }, + { 0x4022ac083126e979ull, 0x4084322f2fd3b280ull }, + { 0x4022ac8b43958106ull, 0x408435c4ee85050full }, + { 0x4022ad0e56041894ull, 0x4084395b501e5c11ull }, + { 0x4022ad916872b021ull, 0x40843cf254bca23cull }, + { 0x4022ae147ae147aeull, 0x40844089fc7cc77cull }, + { 0x4022ae978d4fdf3cull, 0x40844422477bc0e2ull }, + { 0x4022af1a9fbe76c9ull, 0x408447bb35d6888aull }, + { 0x4022af9db22d0e56ull, 0x40844b54c7aa1dcdull }, + { 0x4022b020c49ba5e3ull, 0x40844eeefd138522ull }, + { 0x4022b0a3d70a3d71ull, 0x40845289d62fc82dull }, + { 0x4022b126e978d4feull, 0x40845625531bf5a4ull }, + { 0x4022b1a9fbe76c8bull, 0x408459c173f5217cull }, + { 0x4022b22d0e560419ull, 0x40845d5e38d864d2ull }, + { 0x4022b2b020c49ba6ull, 0x408460fba1e2dddaull }, + { 0x4022b33333333333ull, 0x40846499af31b005ull }, + { 0x4022b3b645a1cac1ull, 0x4084683860e203f4ull }, + { 0x4022b4395810624eull, 0x40846bd7b711075eull }, + { 0x4022b4bc6a7ef9dbull, 0x40846f77b1dbed3bull }, + { 0x4022b53f7ced9169ull, 0x40847318515fedb7ull }, + { 0x4022b5c28f5c28f6ull, 0x408476b995ba4613ull }, + { 0x4022b645a1cac083ull, 0x40847a5b7f0838daull }, + { 0x4022b6c8b4395810ull, 0x40847dfe0d670dbfull }, + { 0x4022b74bc6a7ef9eull, 0x408481a140f411b1ull }, + { 0x4022b7ced916872bull, 0x4084854519cc96baull }, + { 0x4022b851eb851eb8ull, 0x408488e9980df431ull }, + { 0x4022b8d4fdf3b646ull, 0x40848c8ebbd586a1ull }, + { 0x4022b95810624dd3ull, 0x408490348540afb4ull }, + { 0x4022b9db22d0e560ull, 0x408493daf46cd664ull }, + { 0x4022ba5e353f7ceeull, 0x40849782097766e1ull }, + { 0x4022bae147ae147bull, 0x40849b29c47dd27eull }, + { 0x4022bb645a1cac08ull, 0x40849ed2259d8fdfull }, + { 0x4022bbe76c8b4396ull, 0x4084a27b2cf41ae2ull }, + { 0x4022bc6a7ef9db23ull, 0x4084a624da9ef48cull }, + { 0x4022bced916872b0ull, 0x4084a9cf2ebba333ull }, + { 0x4022bd70a3d70a3eull, 0x4084ad7a2967b26cull }, + { 0x4022bdf3b645a1cbull, 0x4084b125cac0b2f4ull }, + { 0x4022be76c8b43958ull, 0x4084b4d212e43addull }, + { 0x4022bef9db22d0e5ull, 0x4084b87f01efe574ull }, + { 0x4022bf7ced916873ull, 0x4084bc2c9801534cull }, + { 0x4022c00000000000ull, 0x4084bfdad5362a27ull }, + { 0x4022c083126e978dull, 0x4084c389b9ac151full }, + { 0x4022c10624dd2f1bull, 0x4084c7394580c491ull }, + { 0x4022c189374bc6a8ull, 0x4084cae978d1ee0bull }, + { 0x4022c20c49ba5e35ull, 0x4084ce9a53bd4c77ull }, + { 0x4022c28f5c28f5c3ull, 0x4084d24bd660a005ull }, + { 0x4022c3126e978d50ull, 0x4084d5fe00d9ae17ull }, + { 0x4022c395810624ddull, 0x4084d9b0d346416eull }, + { 0x4022c4189374bc6bull, 0x4084dd644dc42a15ull }, + { 0x4022c49ba5e353f8ull, 0x4084e11870713d4cull }, + { 0x4022c51eb851eb85ull, 0x4084e4cd3b6b55b4ull }, + { 0x4022c5a1cac08313ull, 0x4084e882aed0533bull }, + { 0x4022c624dd2f1aa0ull, 0x4084ec38cabe1b07ull }, + { 0x4022c6a7ef9db22dull, 0x4084efef8f5297a2ull }, + { 0x4022c72b020c49baull, 0x4084f3a6fcabb8ddull }, + { 0x4022c7ae147ae148ull, 0x4084f75f12e773e3ull }, + { 0x4022c83126e978d5ull, 0x4084fb17d223c319ull }, + { 0x4022c8b439581062ull, 0x4084fed13a7ea64cull }, + { 0x4022c9374bc6a7f0ull, 0x4085028b4c16229dull }, + { 0x4022c9ba5e353f7dull, 0x408506460708426aull }, + { 0x4022ca3d70a3d70aull, 0x40850a016b73157full }, + { 0x4022cac083126e98ull, 0x40850dbd7974b0faull }, + { 0x4022cb4395810625ull, 0x4085117a312b2f3cull }, + { 0x4022cbc6a7ef9db2ull, 0x4085153792b4b015ull }, + { 0x4022cc49ba5e3540ull, 0x408518f59e2f58acull }, + { 0x4022cccccccccccdull, 0x40851cb453b9536eull }, + { 0x4022cd4fdf3b645aull, 0x40852073b370d036ull }, + { 0x4022cdd2f1a9fbe8ull, 0x40852433bd74043cull }, + { 0x4022ce5604189375ull, 0x408527f471e12a00ull }, + { 0x4022ced916872b02ull, 0x40852bb5d0d68174ull }, + { 0x4022cf5c28f5c28full, 0x40852f77da724fe2ull }, + { 0x4022cfdf3b645a1dull, 0x4085333a8ed2dffbull }, + { 0x4022d0624dd2f1aaull, 0x408536fdee1681baull }, + { 0x4022d0e560418937ull, 0x40853ac1f85b8a91ull }, + { 0x4022d16872b020c5ull, 0x40853e86adc05555ull }, + { 0x4022d1eb851eb852ull, 0x4085424c0e63422aull }, + { 0x4022d26e978d4fdfull, 0x408546121a62b6acull }, + { 0x4022d2f1a9fbe76dull, 0x408549d8d1dd1dddull }, + { 0x4022d374bc6a7efaull, 0x40854da034f0e80full }, + { 0x4022d3f7ced91687ull, 0x4085516843bc8b12ull }, + { 0x4022d47ae147ae15ull, 0x40855530fe5e821dull }, + { 0x4022d4fdf3b645a2ull, 0x408558fa64f54dbcull }, + { 0x4022d5810624dd2full, 0x40855cc4779f73f9ull }, + { 0x4022d604189374bdull, 0x4085608f367b804bull }, + { 0x4022d6872b020c4aull, 0x4085645aa1a8037cull }, + { 0x4022d70a3d70a3d7ull, 0x40856826b94393ddull }, + { 0x4022d78d4fdf3b64ull, 0x40856bf37d6ccd22ull }, + { 0x4022d810624dd2f2ull, 0x40856fc0ee425078ull }, + { 0x4022d89374bc6a7full, 0x4085738f0be2c463ull }, + { 0x4022d916872b020cull, 0x4085775dd66cd4f1ull }, + { 0x4022d9999999999aull, 0x40857b2d4dff33a0ull }, + { 0x4022da1cac083127ull, 0x40857efd72b8974aull }, + { 0x4022da9fbe76c8b4ull, 0x408582ce44b7bc54ull }, + { 0x4022db22d0e56042ull, 0x4085869fc41b6498ull }, + { 0x4022dba5e353f7cfull, 0x40858a71f102574full }, + { 0x4022dc28f5c28f5cull, 0x40858e44cb8b613eull }, + { 0x4022dcac083126eaull, 0x4085921853d554a3ull }, + { 0x4022dd2f1a9fbe77ull, 0x408595ec89ff091dull }, + { 0x4022ddb22d0e5604ull, 0x408599c16e275bdcull }, + { 0x4022de353f7ced92ull, 0x40859d97006d2f8aull }, + { 0x4022deb851eb851full, 0x4085a16d40ef6c36ull }, + { 0x4022df3b645a1cacull, 0x4085a5442fccff82ull }, + { 0x4022dfbe76c8b439ull, 0x4085a91bcd24dc87ull }, + { 0x4022e04189374bc7ull, 0x4085acf41915fbe0ull }, + { 0x4022e0c49ba5e354ull, 0x4085b0cd13bf5b97ull }, + { 0x4022e147ae147ae1ull, 0x4085b4a6bd3fff47ull }, + { 0x4022e1cac083126full, 0x4085b88115b6f011ull }, + { 0x4022e24dd2f1a9fcull, 0x4085bc5c1d433c7eull }, + { 0x4022e2d0e5604189ull, 0x4085c037d403f8b4ull }, + { 0x4022e353f7ced917ull, 0x4085c4143a183e5aull }, + { 0x4022e3d70a3d70a4ull, 0x4085c7f14f9f2c89ull }, + { 0x4022e45a1cac0831ull, 0x4085cbcf14b7e7f5ull }, + { 0x4022e4dd2f1a9fbfull, 0x4085cfad89819adaull }, + { 0x4022e5604189374cull, 0x4085d38cae1b74e4ull }, + { 0x4022e5e353f7ced9ull, 0x4085d76c82a4ab60ull }, + { 0x4022e66666666667ull, 0x4085db4d073c7925ull }, + { 0x4022e6e978d4fdf4ull, 0x4085df2e3c021e7full }, + { 0x4022e76c8b439581ull, 0x4085e3102114e15cull }, + { 0x4022e7ef9db22d0eull, 0x4085e6f2b6940d30ull }, + { 0x4022e872b020c49cull, 0x4085ead5fc9ef305ull }, + { 0x4022e8f5c28f5c29ull, 0x4085eeb9f354e95cull }, + { 0x4022e978d4fdf3b6ull, 0x4085f29e9ad54c5eull }, + { 0x4022e9fbe76c8b44ull, 0x4085f683f33f7dc4ull }, + { 0x4022ea7ef9db22d1ull, 0x4085fa69fcb2e4c4ull }, + { 0x4022eb020c49ba5eull, 0x4085fe50b74eee3cull }, + { 0x4022eb851eb851ecull, 0x4086023823330ca0ull }, + { 0x4022ec083126e979ull, 0x40860620407eb7e0ull }, + { 0x4022ec8b43958106ull, 0x40860a090f516d9aull }, + { 0x4022ed0e56041894ull, 0x40860df28fcab102ull }, + { 0x4022ed916872b021ull, 0x408611dcc20a0ad0ull }, + { 0x4022ee147ae147aeull, 0x408615c7a62f0968ull }, + { 0x4022ee978d4fdf3cull, 0x408619b33c5940caull }, + { 0x4022ef1a9fbe76c9ull, 0x40861d9f84a84a7aull }, + { 0x4022ef9db22d0e56ull, 0x4086218c7f3bc5afull }, + { 0x4022f020c49ba5e3ull, 0x4086257a2c335735ull }, + { 0x4022f0a3d70a3d71ull, 0x408629688baea97dull }, + { 0x4022f126e978d4feull, 0x40862d579dcd6c81ull }, + { 0x4022f1a9fbe76c8bull, 0x4086314762af55f2ull }, + { 0x4022f22d0e560419ull, 0x40863537da742120ull }, + { 0x4022f2b020c49ba6ull, 0x40863929053b8ee9ull }, + { 0x4022f33333333333ull, 0x40863d1ae32565e3ull }, + { 0x4022f3b645a1cac1ull, 0x4086410d74517247ull }, + { 0x4022f4395810624eull, 0x40864500b8df85deull }, + { 0x4022f4bc6a7ef9dbull, 0x408648f4b0ef782eull }, + { 0x4022f53f7ced9169ull, 0x40864ce95ca12662ull }, + { 0x4022f5c28f5c28f6ull, 0x408650debc147337ull }, + { 0x4022f645a1cac083ull, 0x408654d4cf69472bull }, + { 0x4022f6c8b4395810ull, 0x408658cb96bf905cull }, + { 0x4022f74bc6a7ef9eull, 0x40865cc31237429eull }, + { 0x4022f7ced916872bull, 0x408660bb41f05756ull }, + { 0x4022f851eb851eb8ull, 0x408664b4260acdafull }, + { 0x4022f8d4fdf3b646ull, 0x408668adbea6aa83ull }, + { 0x4022f95810624dd3ull, 0x40866ca80be3f842ull }, + { 0x4022f9db22d0e560ull, 0x408670a30de2c724ull }, + { 0x4022fa5e353f7ceeull, 0x4086749ec4c32d12ull }, + { 0x4022fae147ae147bull, 0x4086789b30a54590ull }, + { 0x4022fb645a1cac08ull, 0x40867c9851a931ebull }, + { 0x4022fbe76c8b4396ull, 0x4086809627ef1927ull }, + { 0x4022fc6a7ef9db23ull, 0x40868494b39727e3ull }, + { 0x4022fced916872b0ull, 0x40868893f4c1908bull }, + { 0x4022fd70a3d70a3eull, 0x40868c93eb8e8b45ull }, + { 0x4022fdf3b645a1cbull, 0x40869094981e55d5ull }, + { 0x4022fe76c8b43958ull, 0x40869495fa9133d1ull }, + { 0x4022fef9db22d0e5ull, 0x4086989813076e81ull }, + { 0x4022ff7ced916873ull, 0x40869c9ae1a154f0ull }, + { 0x4023000000000000ull, 0x4086a09e667f3bcdull }, + { 0x40230083126e978dull, 0x4086a4a2a1c17d9cull }, + { 0x4023010624dd2f1bull, 0x4086a8a793887aa0ull }, + { 0x40230189374bc6a8ull, 0x4086acad3bf498c2ull }, + { 0x4023020c49ba5e35ull, 0x4086b0b39b2643c6ull }, + { 0x4023028f5c28f5c3ull, 0x4086b4bab13ded2eull }, + { 0x402303126e978d50ull, 0x4086b8c27e5c0c27ull }, + { 0x40230395810624ddull, 0x4086bccb02a11dbbull }, + { 0x402304189374bc6bull, 0x4086c0d43e2da4b6ull }, + { 0x4023049ba5e353f8ull, 0x4086c4de31222994ull }, + { 0x4023051eb851eb85ull, 0x4086c8e8db9f3aadull }, + { 0x402305a1cac08313ull, 0x4086ccf43dc56c22ull }, + { 0x40230624dd2f1aa0ull, 0x4086d10057b557c3ull }, + { 0x402306a7ef9db22dull, 0x4086d50d298f9d43ull }, + { 0x4023072b020c49baull, 0x4086d91ab374e215ull }, + { 0x402307ae147ae148ull, 0x4086dd28f585d184ull }, + { 0x4023083126e978d5ull, 0x4086e137efe31c8aull }, + { 0x402308b439581062ull, 0x4086e547a2ad7a0bull }, + { 0x402309374bc6a7f0ull, 0x4086e9580e05a6b7ull }, + { 0x402309ba5e353f7dull, 0x4086ed69320c64f8ull }, + { 0x40230a3d70a3d70aull, 0x4086f17b0ee27d1dull }, + { 0x40230ac083126e98ull, 0x4086f58da4a8bd4bull }, + { 0x40230b4395810625ull, 0x4086f9a0f37ff95eull }, + { 0x40230bc6a7ef9db2ull, 0x4086fdb4fb890b21ull }, + { 0x40230c49ba5e3540ull, 0x408701c9bce4d230ull }, + { 0x40230ccccccccccdull, 0x408705df37b433e8ull }, + { 0x40230d4fdf3b645aull, 0x408709f56c181b95ull }, + { 0x40230dd2f1a9fbe8ull, 0x40870e0c5a317a58ull }, + { 0x40230e5604189375ull, 0x4087122402214716ull }, + { 0x40230ed916872b02ull, 0x4087163c64087ea4ull }, + { 0x40230f5c28f5c28full, 0x40871a55800823abull }, + { 0x40230fdf3b645a1dull, 0x40871e6f56413eb7ull }, + { 0x402310624dd2f1aaull, 0x40872289e6d4de1aull }, + { 0x402310e560418937ull, 0x408726a531e4161dull }, + { 0x4023116872b020c5ull, 0x40872ac1379000e6ull }, + { 0x402311eb851eb852ull, 0x40872eddf7f9be65ull }, + { 0x4023126e978d4fdfull, 0x408732fb73427482ull }, + { 0x402312f1a9fbe76dull, 0x40873719a98b4f08ull }, + { 0x40231374bc6a7efaull, 0x40873b389af57f8eull }, + { 0x402313f7ced91687ull, 0x40873f5847a23da6ull }, + { 0x4023147ae147ae15ull, 0x40874378afb2c6c9ull }, + { 0x402314fdf3b645a2ull, 0x40874799d3485e3cull }, + { 0x402315810624dd2full, 0x40874bbbb2844d46ull }, + { 0x40231604189374bdull, 0x40874fde4d87e316ull }, + { 0x402316872b020c4aull, 0x40875401a47474acull }, + { 0x4023170a3d70a3d7ull, 0x40875825b76b5d0aull }, + { 0x4023178d4fdf3b64ull, 0x40875c4a868dfd19ull }, + { 0x40231810624dd2f2ull, 0x4087607011fdbbb2ull }, + { 0x4023189374bc6a7full, 0x4087649659dc0588ull }, + { 0x40231916872b020cull, 0x408768bd5e4a4d53ull }, + { 0x402319999999999aull, 0x40876ce51f6a0bbbull }, + { 0x40231a1cac083127ull, 0x4087710d9d5cbf41ull }, + { 0x40231a9fbe76c8b4ull, 0x40877536d843ec71ull }, + { 0x40231b22d0e56042ull, 0x40877960d0411dc8ull }, + { 0x40231ba5e353f7cfull, 0x40877d8b8575e3a1ull }, + { 0x40231c28f5c28f5cull, 0x408781b6f803d464ull }, + { 0x40231cac083126eaull, 0x408785e3280c8c6full }, + { 0x40231d2f1a9fbe77ull, 0x40878a1015b1adffull }, + { 0x40231db22d0e5604ull, 0x40878e3dc114e162ull }, + { 0x40231e353f7ced92ull, 0x4087926c2a57d4deull }, + { 0x40231eb851eb851full, 0x4087969b519c3c9full }, + { 0x40231f3b645a1cacull, 0x40879acb3703d2dfull }, + { 0x40231fbe76c8b439ull, 0x40879efbdab057d3ull }, + { 0x4023204189374bc7ull, 0x4087a32d3cc391b1ull }, + { 0x402320c49ba5e354ull, 0x4087a75f5d5f4c97ull }, + { 0x40232147ae147ae1ull, 0x4087ab923ca55abcull }, + { 0x402321cac083126full, 0x4087afc5dab79457ull }, + { 0x4023224dd2f1a9fcull, 0x4087b3fa37b7d788ull }, + { 0x402322d0e5604189ull, 0x4087b82f53c8088cull }, + { 0x40232353f7ced917ull, 0x4087bc652f0a11a3ull }, + { 0x402323d70a3d70a4ull, 0x4087c09bc99fe2f9ull }, + { 0x4023245a1cac0831ull, 0x4087c4d323ab72dbull }, + { 0x402324dd2f1a9fbfull, 0x4087c90b3d4ebd9bull }, + { 0x402325604189374cull, 0x4087cd4416abc57dull }, + { 0x402325e353f7ced9ull, 0x4087d17dafe492e4ull }, + { 0x4023266666666667ull, 0x4087d5b8091b3441ull }, + { 0x402326e978d4fdf4ull, 0x4087d9f32271bdf5ull }, + { 0x4023276c8b439581ull, 0x4087de2efc0a4a87ull }, + { 0x402327ef9db22d0eull, 0x4087e26b9606fa86ull }, + { 0x40232872b020c49cull, 0x4087e6a8f089f495ull }, + { 0x402328f5c28f5c29ull, 0x4087eae70bb5654full }, + { 0x40232978d4fdf3b6ull, 0x4087ef25e7ab7f79ull }, + { 0x402329fbe76c8b44ull, 0x4087f365848e7beaull }, + { 0x40232a7ef9db22d1ull, 0x4087f7a5e2809974ull }, + { 0x40232b020c49ba5eull, 0x4087fbe701a41d14ull }, + { 0x40232b851eb851ecull, 0x40880028e21b51deull }, + { 0x40232c083126e979ull, 0x4088046b840888e3ull }, + { 0x40232c8b43958106ull, 0x408808aee78e1965ull }, + { 0x40232d0e56041894ull, 0x40880cf30cce60bdull }, + { 0x40232d916872b021ull, 0x40881137f3ebc245ull }, + { 0x40232e147ae147aeull, 0x4088157d9d08a78dull }, + { 0x40232e978d4fdf3cull, 0x408819c40847803dull }, + { 0x40232f1a9fbe76c9ull, 0x40881e0b35cac203ull }, + { 0x40232f9db22d0e56ull, 0x4088225325b4e8c5ull }, + { 0x40233020c49ba5e3ull, 0x4088269bd828767cull }, + { 0x402330a3d70a3d71ull, 0x40882ae54d47f34eull }, + { 0x40233126e978d4feull, 0x40882f2f8535ed65ull }, + { 0x402331a9fbe76c8bull, 0x4088337a8014f929ull }, + { 0x4023322d0e560419ull, 0x408837c63e07b126ull }, + { 0x402332b020c49ba6ull, 0x40883c12bf30b5efull }, + { 0x4023333333333333ull, 0x4088406003b2ae5bull }, + { 0x402333b645a1cac1ull, 0x408844ae0bb04764ull }, + { 0x402334395810624eull, 0x408848fcd74c3414ull }, + { 0x402334bc6a7ef9dbull, 0x40884d4c66a92db6ull }, + { 0x4023353f7ced9169ull, 0x4088519cb9e9f3c2ull }, + { 0x402335c28f5c28f6ull, 0x408855edd1314bbdull }, + { 0x40233645a1cac083ull, 0x40885a3faca20175ull }, + { 0x402336c8b4395810ull, 0x40885e924c5ee6dbull }, + { 0x4023374bc6a7ef9eull, 0x408862e5b08ad417ull }, + { 0x402337ced916872bull, 0x40886739d948a769ull }, + { 0x40233851eb851eb8ull, 0x40886b8ec6bb4557ull }, + { 0x402338d4fdf3b646ull, 0x40886fe47905989dull }, + { 0x4023395810624dd3ull, 0x4088743af04a920cull }, + { 0x402339db22d0e560ull, 0x408878922cad28c5ull }, + { 0x40233a5e353f7ceeull, 0x40887cea2e505a1dull }, + { 0x40233ae147ae147bull, 0x40888142f5572987ull }, + { 0x40233b645a1cac08ull, 0x4088859c81e4a0c3ull }, + { 0x40233be76c8b4396ull, 0x408889f6d41bcfcdull }, + { 0x40233c6a7ef9db23ull, 0x40888e51ec1fccbfull }, + { 0x40233ced916872b0ull, 0x408892adca13b406ull }, + { 0x40233d70a3d70a3eull, 0x4088970a6e1aa84dull }, + { 0x40233df3b645a1cbull, 0x40889b67d857d25full }, + { 0x40233e76c8b43958ull, 0x40889fc608ee6162ull }, + { 0x40233ef9db22d0e5ull, 0x4088a42500018ab0ull }, + { 0x40233f7ced916873ull, 0x4088a884bdb489eaull }, + { 0x4023400000000000ull, 0x4088ace5422aa0dbull }, + { 0x40234083126e978dull, 0x4088b1468d8717aaull }, + { 0x4023410624dd2f1bull, 0x4088b5a89fed3cbfull }, + { 0x40234189374bc6a8ull, 0x4088ba0b798064aeull }, + { 0x4023420c49ba5e35ull, 0x4088be6f1a63ea6bull }, + { 0x4023428f5c28f5c3ull, 0x4088c2d382bb2f2full }, + { 0x402343126e978d50ull, 0x4088c738b2a99a62ull }, + { 0x40234395810624ddull, 0x4088cb9eaa5299cdull }, + { 0x402344189374bc6bull, 0x4088d00569d9a186ull }, + { 0x4023449ba5e353f8ull, 0x4088d46cf1622bd2ull }, + { 0x4023451eb851eb85ull, 0x4088d8d5410fb95cull }, + { 0x402345a1cac08313ull, 0x4088dd3e5905d11dull }, + { 0x40234624dd2f1aa0ull, 0x4088e1a839680043ull }, + { 0x402346a7ef9db22dull, 0x4088e612e259da63ull }, + { 0x4023472b020c49baull, 0x4088ea7e53fef95eull }, + { 0x402347ae147ae148ull, 0x4088eeea8e7afd6bull }, + { 0x4023483126e978d5ull, 0x4088f35791f18cfeull }, + { 0x402348b439581062ull, 0x4088f7c55e8654f7ull }, + { 0x402349374bc6a7f0ull, 0x4088fc33f45d088aull }, + { 0x402349ba5e353f7dull, 0x408900a353996129ull }, + { 0x40234a3d70a3d70aull, 0x408905137c5f1eb7ull }, + { 0x40234ac083126e98ull, 0x408909846ed20770ull }, + { 0x40234b4395810625ull, 0x40890df62b15e7cfull }, + { 0x40234bc6a7ef9db2ull, 0x40891268b14e92c4ull }, + { 0x40234c49ba5e3540ull, 0x408916dc019fe19aull }, + { 0x40234ccccccccccdull, 0x40891b501c2db3e0ull }, + { 0x40234d4fdf3b645aull, 0x40891fc5011bef9eull }, + { 0x40234dd2f1a9fbe8ull, 0x4089243ab08e8139ull }, + { 0x40234e5604189375ull, 0x408928b12aa95b5full }, + { 0x40234ed916872b02ull, 0x40892d286f907737ull }, + { 0x40234f5c28f5c28full, 0x408931a07f67d444ull }, + { 0x40234fdf3b645a1dull, 0x408936195a537874ull }, + { 0x402350624dd2f1aaull, 0x40893a9300777002ull }, + { 0x402350e560418937ull, 0x40893f0d71f7cda6ull }, + { 0x4023516872b020c5ull, 0x40894388aef8aa83ull }, + { 0x402351eb851eb852ull, 0x40894804b79e2607ull }, + { 0x4023526e978d4fdfull, 0x40894c818c0c6625ull }, + { 0x402352f1a9fbe76dull, 0x408950ff2c67973cull }, + { 0x40235374bc6a7efaull, 0x4089557d98d3ebf9ull }, + { 0x402353f7ced91687ull, 0x408959fcd1759d93ull }, + { 0x4023547ae147ae15ull, 0x40895e7cd670ebb0ull }, + { 0x402354fdf3b645a2ull, 0x408962fda7ea1c47ull }, + { 0x402355810624dd2full, 0x4089677f46057bddull }, + { 0x40235604189374bdull, 0x40896c01b0e75d67ull }, + { 0x402356872b020c4aull, 0x40897084e8b41a33ull }, + { 0x4023570a3d70a3d7ull, 0x40897508ed90121cull }, + { 0x4023578d4fdf3b64ull, 0x4089798dbf9fab6cull }, + { 0x40235810624dd2f2ull, 0x40897e135f0752e7ull }, + { 0x4023589374bc6a7full, 0x40898299cbeb7bb3ull }, + { 0x40235916872b020cull, 0x4089872106709f85ull }, + { 0x402359999999999aull, 0x40898ba90ebb3e8dull }, + { 0x40235a1cac083127ull, 0x40899031e4efdf5bull }, + { 0x40235a9fbe76c8b4ull, 0x408994bb89330f16ull }, + { 0x40235b22d0e56042ull, 0x40899945fba96161ull }, + { 0x40235ba5e353f7cfull, 0x40899dd13c777043ull }, + { 0x40235c28f5c28f5cull, 0x4089a25d4bc1dc5cull }, + { 0x40235cac083126eaull, 0x4089a6ea29ad4cceull }, + { 0x40235d2f1a9fbe77ull, 0x4089ab77d65e6f21ull }, + { 0x40235db22d0e5604ull, 0x4089b00651f9f779ull }, + { 0x40235e353f7ced92ull, 0x4089b4959ca4a081ull }, + { 0x40235eb851eb851full, 0x4089b925b6832b4cull }, + { 0x40235f3b645a1cacull, 0x4089bdb69fba5f8full }, + { 0x40235fbe76c8b439ull, 0x4089c248586f0b7cull }, + { 0x4023604189374bc7ull, 0x4089c6dae0c603d8ull }, + { 0x402360c49ba5e354ull, 0x4089cb6e38e423d7ull }, + { 0x40236147ae147ae1ull, 0x4089d00260ee4d50ull }, + { 0x402361cac083126full, 0x4089d497590968aaull }, + { 0x4023624dd2f1a9fcull, 0x4089d92d215a64bbull }, + { 0x402362d0e5604189ull, 0x4089ddc3ba063704ull }, + { 0x40236353f7ced917ull, 0x4089e25b2331db95ull }, + { 0x402363d70a3d70a4ull, 0x4089e6f35d0254f3ull }, + { 0x4023645a1cac0831ull, 0x4089eb8c679cac51ull }, + { 0x402364dd2f1a9fbfull, 0x4089f0264325f174ull }, + { 0x402365604189374cull, 0x4089f4c0efc33a9aull }, + { 0x402365e353f7ced9ull, 0x4089f95c6d99a4b3ull }, + { 0x4023666666666667ull, 0x4089fdf8bcce5343ull }, + { 0x402366e978d4fdf4ull, 0x408a0295dd86704dull }, + { 0x4023676c8b439581ull, 0x408a0733cfe72c86ull }, + { 0x402367ef9db22d0eull, 0x408a0bd29415bf35ull }, + { 0x40236872b020c49cull, 0x408a10722a376645ull }, + { 0x402368f5c28f5c29ull, 0x408a151292716622ull }, + { 0x40236978d4fdf3b6ull, 0x408a19b3cce909f0ull }, + { 0x402369fbe76c8b44ull, 0x408a1e55d9c3a375ull }, + { 0x40236a7ef9db22d1ull, 0x408a22f8b9268af6ull }, + { 0x40236b020c49ba5eull, 0x408a279c6b371f7aull }, + { 0x40236b851eb851ecull, 0x408a2c40f01ac6a6ull }, + { 0x40236c083126e979ull, 0x408a30e647f6eca9ull }, + { 0x40236c8b43958106ull, 0x408a358c72f10472ull }, + { 0x40236d0e56041894ull, 0x408a3a33712e8794ull }, + { 0x40236d916872b021ull, 0x408a3edb42d4f631ull }, + { 0x40236e147ae147aeull, 0x408a4383e809d72bull }, + { 0x40236e978d4fdf3cull, 0x408a482d60f2b80full }, + { 0x40236f1a9fbe76c9ull, 0x408a4cd7adb52cf8ull }, + { 0x40236f9db22d0e56ull, 0x408a5182ce76d0caull }, + { 0x40237020c49ba5e3ull, 0x408a562ec35d450aull }, + { 0x402370a3d70a3d71ull, 0x408a5adb8c8e31f7ull }, + { 0x40237126e978d4feull, 0x408a5f892a2f4662ull }, + { 0x402371a9fbe76c8bull, 0x408a64379c6637e8ull }, + { 0x4023722d0e560419ull, 0x408a68e6e358c2d8ull }, + { 0x402372b020c49ba6ull, 0x408a6d96ff2caa18ull }, + { 0x4023733333333333ull, 0x408a7247f007b75eull }, + { 0x402373b645a1cac1ull, 0x408a76f9b60fbb13ull }, + { 0x402374395810624eull, 0x408a7bac516a8c3full }, + { 0x402374bc6a7ef9dbull, 0x408a805fc23e08b9ull }, + { 0x4023753f7ced9169ull, 0x408a851408b01512ull }, + { 0x402375c28f5c28f6ull, 0x408a89c924e69c7bull }, + { 0x40237645a1cac083ull, 0x408a8e7f170790faull }, + { 0x402376c8b4395810ull, 0x408a9335df38eb48ull }, + { 0x4023774bc6a7ef9eull, 0x408a97ed7da0aae5ull }, + { 0x402377ced916872bull, 0x408a9ca5f264d5f4ull }, + { 0x40237851eb851eb8ull, 0x408aa15f3dab7975ull }, + { 0x402378d4fdf3b646ull, 0x408aa6195f9aa927ull }, + { 0x4023795810624dd3ull, 0x408aaad458587f70ull }, + { 0x402379db22d0e560ull, 0x408aaf90280b1d97ull }, + { 0x40237a5e353f7ceeull, 0x408ab44cced8aba5ull }, + { 0x40237ae147ae147bull, 0x408ab90a4ce7584full }, + { 0x40237b645a1cac08ull, 0x408abdc8a25d592dull }, + { 0x40237be76c8b4396ull, 0x408ac287cf60ea9dull }, + { 0x40237c6a7ef9db23ull, 0x408ac747d4184faeull }, + { 0x40237ced916872b0ull, 0x408acc08b0a9d252ull }, + { 0x40237d70a3d70a3eull, 0x408ad0ca653bc34bull }, + { 0x40237df3b645a1cbull, 0x408ad58cf1f47a09ull }, + { 0x40237e76c8b43958ull, 0x408ada5056fa54e6ull }, + { 0x40237ef9db22d0e5ull, 0x408adf149473b906ull }, + { 0x40237f7ced916873ull, 0x408ae3d9aa871262ull }, + { 0x4023800000000000ull, 0x408ae89f995ad3adull }, + { 0x40238083126e978dull, 0x408aed6661157688ull }, + { 0x4023810624dd2f1bull, 0x408af22e01dd7b67ull }, + { 0x40238189374bc6a8ull, 0x408af6f67bd96978ull }, + { 0x4023820c49ba5e35ull, 0x408afbbfcf2fcedeull }, + { 0x4023828f5c28f5c3ull, 0x408b0089fc074092ull }, + { 0x402383126e978d50ull, 0x408b055502865a4aull }, + { 0x40238395810624ddull, 0x408b0a20e2d3beb3ull }, + { 0x402384189374bc6bull, 0x408b0eed9d161757ull }, + { 0x4023849ba5e353f8ull, 0x408b13bb3174147full }, + { 0x4023851eb851eb85ull, 0x408b1889a0146d6eull }, + { 0x402385a1cac08313ull, 0x408b1d58e91de048ull }, + { 0x40238624dd2f1aa0ull, 0x408b22290cb731f4ull }, + { 0x402386a7ef9db22dull, 0x408b26fa0b072e57ull }, + { 0x4023872b020c49baull, 0x408b2bcbe434a831ull }, + { 0x402387ae147ae148ull, 0x408b309e9866792eull }, + { 0x4023883126e978d5ull, 0x408b357227c381c4ull }, + { 0x402388b439581062ull, 0x408b3a469272a96bull }, + { 0x402389374bc6a7f0ull, 0x408b3f1bd89ade84ull }, + { 0x402389ba5e353f7dull, 0x408b43f1fa63163cull }, + { 0x40238a3d70a3d70aull, 0x408b48c8f7f24cc8ull }, + { 0x40238ac083126e98ull, 0x408b4da0d16f8546ull }, + { 0x40238b4395810625ull, 0x408b52798701c9a8ull }, + { 0x40238bc6a7ef9db2ull, 0x408b575318d02ae6ull }, + { 0x40238c49ba5e3540ull, 0x408b5c2d8701c0ebull }, + { 0x40238ccccccccccdull, 0x408b6108d1bdaa74ull }, + { 0x40238d4fdf3b645aull, 0x408b65e4f92b0d4eull }, + { 0x40238dd2f1a9fbe8ull, 0x408b6ac1fd711636ull }, + { 0x40238e5604189375ull, 0x408b6f9fdeb6f8c3ull }, + { 0x40238ed916872b02ull, 0x408b747e9d23ef9dull }, + { 0x40238f5c28f5c28full, 0x408b795e38df3c5bull }, + { 0x40238fdf3b645a1dull, 0x408b7e3eb2102792ull }, + { 0x402390624dd2f1aaull, 0x408b832008de00b6ull }, + { 0x402390e560418937ull, 0x408b88023d701e51ull }, + { 0x4023916872b020c5ull, 0x408b8ce54fedddeaull }, + { 0x402391eb851eb852ull, 0x408b91c9407ea3e5ull }, + { 0x4023926e978d4fdfull, 0x408b96ae0f49dbc5ull }, + { 0x402392f1a9fbe76dull, 0x408b9b93bc76f809ull }, + { 0x40239374bc6a7efaull, 0x408ba07a482d7214ull }, + { 0x402393f7ced91687ull, 0x408ba561b294ca6bull }, + { 0x4023947ae147ae15ull, 0x408baa49fbd48891ull }, + { 0x402394fdf3b645a2ull, 0x408baf3324143af4ull }, + { 0x402395810624dd2full, 0x408bb41d2b7b7724ull }, + { 0x40239604189374bdull, 0x408bb9081231d9b6ull }, + { 0x402396872b020c4aull, 0x408bbdf3d85f062cull }, + { 0x4023970a3d70a3d7ull, 0x408bc2e07e2aa72dull }, + { 0x4023978d4fdf3b64ull, 0x408bc7ce03bc6e61ull }, + { 0x40239810624dd2f2ull, 0x408bccbc693c1486ull }, + { 0x4023989374bc6a7full, 0x408bd1abaed1594bull }, + { 0x40239916872b020cull, 0x408bd69bd4a40387ull }, + { 0x402399999999999aull, 0x408bdb8cdadbe124ull }, + { 0x40239a1cac083127ull, 0x408be07ec1a0c6feull }, + { 0x40239a9fbe76c8b4ull, 0x408be571891a911full }, + { 0x40239b22d0e56042ull, 0x408bea65317122a8ull }, + { 0x40239ba5e353f7cfull, 0x408bef59bacc65aeull }, + { 0x40239c28f5c28f5cull, 0x408bf44f25544b79ull }, + { 0x40239cac083126eaull, 0x408bf9457130cc6bull }, + { 0x40239d2f1a9fbe77ull, 0x408bfe3c9e89e7deull }, + { 0x40239db22d0e5604ull, 0x408c0334ad87a464ull }, + { 0x40239e353f7ced92ull, 0x408c082d9e520faaull }, + { 0x40239eb851eb851full, 0x408c0d2771113e5cull }, + { 0x40239f3b645a1cacull, 0x408c122225ed4c60ull }, + { 0x40239fbe76c8b439ull, 0x408c171dbd0e5cb1ull }, + { 0x4023a04189374bc7ull, 0x408c1c1a369c9974ull }, + { 0x4023a0c49ba5e354ull, 0x408c211792c033d1ull }, + { 0x4023a147ae147ae1ull, 0x408c2615d1a16432ull }, + { 0x4023a1cac083126full, 0x408c2b14f3686a22ull }, + { 0x4023a24dd2f1a9fcull, 0x408c3014f83d8c34ull }, + { 0x4023a2d0e5604189ull, 0x408c3515e0491840ull }, + { 0x4023a353f7ced917ull, 0x408c3a17abb36345ull }, + { 0x4023a3d70a3d70a4ull, 0x408c3f1a5aa4c94dull }, + { 0x4023a45a1cac0831ull, 0x408c441ded45ada9ull }, + { 0x4023a4dd2f1a9fbfull, 0x408c492263be7ad5ull }, + { 0x4023a5604189374cull, 0x408c4e27be37a25full }, + { 0x4023a5e353f7ced9ull, 0x408c532dfcd99d1dull }, + { 0x4023a66666666667ull, 0x408c58351fcceb16ull }, + { 0x4023a6e978d4fdf4ull, 0x408c5d3d273a1362ull }, + { 0x4023a76c8b439581ull, 0x408c62461349a46aull }, + { 0x4023a7ef9db22d0eull, 0x408c674fe42433c1ull }, + { 0x4023a872b020c49cull, 0x408c6c5a99f25e34ull }, + { 0x4023a8f5c28f5c29ull, 0x408c716634dcc7adull }, + { 0x4023a978d4fdf3b6ull, 0x408c7672b50c1b66ull }, + { 0x4023a9fbe76c8b44ull, 0x408c7b801aa90bd4ull }, + { 0x4023aa7ef9db22d1ull, 0x408c808e65dc5286ull }, + { 0x4023ab020c49ba5eull, 0x408c859d96ceb065ull }, + { 0x4023ab851eb851ecull, 0x408c8aadada8ed94ull }, + { 0x4023ac083126e979ull, 0x408c8fbeaa93d957ull }, + { 0x4023ac8b43958106ull, 0x408c94d08db84a4dull }, + { 0x4023ad0e56041894ull, 0x408c99e3573f1e55ull }, + { 0x4023ad916872b021ull, 0x408c9ef707513a73ull }, + { 0x4023ae147ae147aeull, 0x408ca40b9e178b08ull }, + { 0x4023ae978d4fdf3cull, 0x408ca9211bbb03bcull }, + { 0x4023af1a9fbe76c9ull, 0x408cae3780649f5bull }, + { 0x4023af9db22d0e56ull, 0x408cb34ecc3d6017ull }, + { 0x4023b020c49ba5e3ull, 0x408cb866ff6e4f60ull }, + { 0x4023b0a3d70a3d71ull, 0x408cbd801a207df7ull }, + { 0x4023b126e978d4feull, 0x408cc29a1c7d03caull }, + { 0x4023b1a9fbe76c8bull, 0x408cc7b506ad0030ull }, + { 0x4023b22d0e560419ull, 0x408cccd0d8d999cdull }, + { 0x4023b2b020c49ba6ull, 0x408cd1ed932bfe76ull }, + { 0x4023b33333333333ull, 0x408cd70b35cd636aull }, + { 0x4023b3b645a1cac1ull, 0x408cdc29c0e7053eull }, + { 0x4023b4395810624eull, 0x408ce14934a227b4ull }, + { 0x4023b4bc6a7ef9dbull, 0x408ce66991281606ull }, + { 0x4023b53f7ced9169ull, 0x408ceb8ad6a222bdull }, + { 0x4023b5c28f5c28f6ull, 0x408cf0ad0539a79dull }, + { 0x4023b645a1cac083ull, 0x408cf5d01d1805deull }, + { 0x4023b6c8b4395810ull, 0x408cfaf41e66a607ull }, + { 0x4023b74bc6a7ef9eull, 0x408d0019094ef800ull }, + { 0x4023b7ced916872bull, 0x408d053eddfa72f3ull }, + { 0x4023b851eb851eb8ull, 0x408d0a659c92957full }, + { 0x4023b8d4fdf3b646ull, 0x408d0f8d4540e5a4ull }, + { 0x4023b95810624dd3ull, 0x408d14b5d82ef0a1ull }, + { 0x4023b9db22d0e560ull, 0x408d19df55864b35ull }, + { 0x4023ba5e353f7ceeull, 0x408d1f09bd709180ull }, + { 0x4023bae147ae147bull, 0x408d2435101766e5ull }, + { 0x4023bb645a1cac08ull, 0x408d29614da4764bull }, + { 0x4023bbe76c8b4396ull, 0x408d2e8e764171feull }, + { 0x4023bc6a7ef9db23ull, 0x408d33bc8a181393ull }, + { 0x4023bced916872b0ull, 0x408d38eb89521c24ull }, + { 0x4023bd70a3d70a3eull, 0x408d3e1b74195436ull }, + { 0x4023bdf3b645a1cbull, 0x408d434c4a978b9aull }, + { 0x4023be76c8b43958ull, 0x408d487e0cf699aaull }, + { 0x4023bef9db22d0e5ull, 0x408d4db0bb605d24ull }, + { 0x4023bf7ced916873ull, 0x408d52e455febc41ull }, + { 0x4023c00000000000ull, 0x408d5818dcfba487ull }, + { 0x4023c083126e978dull, 0x408d5d4e50810b10ull }, + { 0x4023c10624dd2f1bull, 0x408d6284b0b8ec67ull }, + { 0x4023c189374bc6a8ull, 0x408d67bbfdcd4c6bull }, + { 0x4023c20c49ba5e35ull, 0x408d6cf437e83691ull }, + { 0x4023c28f5c28f5c3ull, 0x408d722d5f33bdc4ull }, + { 0x4023c3126e978d50ull, 0x408d776773d9fc47ull }, + { 0x4023c395810624ddull, 0x408d7ca2760513f7ull }, + { 0x4023c4189374bc6bull, 0x408d81de65df2e2aull }, + { 0x4023c49ba5e353f8ull, 0x408d871b43927b93ull }, + { 0x4023c51eb851eb85ull, 0x408d8c590f493483ull }, + { 0x4023c5a1cac08313ull, 0x408d9197c92d98c7ull }, + { 0x4023c624dd2f1aa0ull, 0x408d96d77169ef8full }, + { 0x4023c6a7ef9db22dull, 0x408d9c18082887aaull }, + { 0x4023c72b020c49baull, 0x408da1598d93b75full }, + { 0x4023c7ae147ae148ull, 0x408da69c01d5dc84ull }, + { 0x4023c83126e978d5ull, 0x408dabdf65195c55ull }, + { 0x4023c8b439581062ull, 0x408db123b788a3b4ull }, + { 0x4023c9374bc6a7f0ull, 0x408db668f94e2709ull }, + { 0x4023c9ba5e353f7dull, 0x408dbbaf2a946229ull }, + { 0x4023ca3d70a3d70aull, 0x408dc0f64b85d890ull }, + { 0x4023cac083126e98ull, 0x408dc63e5c4d1547ull }, + { 0x4023cb4395810625ull, 0x408dcb875d14aac7ull }, + { 0x4023cbc6a7ef9db2ull, 0x408dd0d14e073332ull }, + { 0x4023cc49ba5e3540ull, 0x408dd61c2f4f503full }, + { 0x4023cccccccccccdull, 0x408ddb680117ab14ull }, + { 0x4023cd4fdf3b645aull, 0x408de0b4c38af48aull }, + { 0x4023cdd2f1a9fbe8ull, 0x408de60276d3e50dull }, + { 0x4023ce5604189375ull, 0x408deb511b1d3c80ull }, + { 0x4023ced916872b02ull, 0x408df0a0b091c27bull }, + { 0x4023cf5c28f5c28full, 0x408df5f1375c4625ull }, + { 0x4023cfdf3b645a1dull, 0x408dfb42afa79e47ull }, + { 0x4023d0624dd2f1aaull, 0x408e0095199ea926ull }, + { 0x4023d0e560418937ull, 0x408e05e8756c4cc4ull }, + { 0x4023d16872b020c5ull, 0x408e0b3cc33b76bdull }, + { 0x4023d1eb851eb852ull, 0x408e109203371c30ull }, + { 0x4023d26e978d4fdfull, 0x408e15e8358a39f8ull }, + { 0x4023d2f1a9fbe76dull, 0x408e1b3f5a5fd495ull }, + { 0x4023d374bc6a7efaull, 0x408e209771e2f808ull }, + { 0x4023d3f7ced91687ull, 0x408e25f07c3eb815ull }, + { 0x4023d47ae147ae15ull, 0x408e2b4a799e3028ull }, + { 0x4023d4fdf3b645a2ull, 0x408e30a56a2c8333ull }, + { 0x4023d5810624dd2full, 0x408e36014e14dbeeull }, + { 0x4023d604189374bdull, 0x408e3b5e25826cbfull }, + { 0x4023d6872b020c4aull, 0x408e40bbf0a06f92ull }, + { 0x4023d70a3d70a3d7ull, 0x408e461aaf9a2624ull }, + { 0x4023d78d4fdf3b64ull, 0x408e4b7a629ad9d1ull }, + { 0x4023d810624dd2f2ull, 0x408e50db09cddbb0ull }, + { 0x4023d89374bc6a7full, 0x408e563ca55e846bull }, + { 0x4023d916872b020cull, 0x408e5b9f3578347aull }, + { 0x4023d9999999999aull, 0x408e6102ba46540aull }, + { 0x4023da1cac083127ull, 0x408e666733f452dbull }, + { 0x4023da9fbe76c8b4ull, 0x408e6bcca2ada886ull }, + { 0x4023db22d0e56042ull, 0x408e7133069dd458ull }, + { 0x4023dba5e353f7cfull, 0x408e769a5ff05d37ull }, + { 0x4023dc28f5c28f5cull, 0x408e7c02aed0d1e6ull }, + { 0x4023dcac083126eaull, 0x408e816bf36ac8dfull }, + { 0x4023dd2f1a9fbe77ull, 0x408e86d62de9e03cull }, + { 0x4023ddb22d0e5604ull, 0x408e8c415e79bdf3ull }, + { 0x4023de353f7ced92ull, 0x408e91ad85460fbaull }, + { 0x4023deb851eb851full, 0x408e971aa27a8aeaull }, + { 0x4023df3b645a1cacull, 0x408e9c88b642ecbbull }, + { 0x4023dfbe76c8b439ull, 0x408ea1f7c0cafa1full }, + { 0x4023e04189374bc7ull, 0x408ea767c23e7fd9ull }, + { 0x4023e0c49ba5e354ull, 0x408eacd8bac95250ull }, + { 0x4023e147ae147ae1ull, 0x408eb24aaa974dd4ull }, + { 0x4023e1cac083126full, 0x408eb7bd91d4567full }, + { 0x4023e24dd2f1a9fcull, 0x408ebd3170ac5815ull }, + { 0x4023e2d0e5604189ull, 0x408ec2a6474b4643ull }, + { 0x4023e353f7ced917ull, 0x408ec81c15dd1c87ull }, + { 0x4023e3d70a3d70a4ull, 0x408ecd92dc8dde0dull }, + { 0x4023e45a1cac0831ull, 0x408ed30a9b8995efull }, + { 0x4023e4dd2f1a9fbfull, 0x408ed88352fc571bull }, + { 0x4023e5604189374cull, 0x408eddfd03123c2full }, + { 0x4023e5e353f7ced9ull, 0x408ee377abf767bfull }, + { 0x4023e66666666667ull, 0x408ee8f34dd80435ull }, + { 0x4023e6e978d4fdf4ull, 0x408eee6fe8e043b1ull }, + { 0x4023e76c8b439581ull, 0x408ef3ed7d3c604bull }, + { 0x4023e7ef9db22d0eull, 0x408ef96c0b189bebull }, + { 0x4023e872b020c49cull, 0x408efeeb92a1405eull }, + { 0x4023e8f5c28f5c29ull, 0x408f046c14029f2eull }, + { 0x4023e978d4fdf3b6ull, 0x408f09ed8f6911e4ull }, + { 0x4023e9fbe76c8b44ull, 0x408f0f700500f9e6ull }, + { 0x4023ea7ef9db22d1ull, 0x408f14f374f6c05cull }, + { 0x4023eb020c49ba5eull, 0x408f1a77df76d671ull }, + { 0x4023eb851eb851ecull, 0x408f1ffd44adb532ull }, + { 0x4023ec083126e979ull, 0x408f2583a4c7dd71ull }, + { 0x4023ec8b43958106ull, 0x408f2b0afff1d808ull }, + { 0x4023ed0e56041894ull, 0x408f3093565835b6ull }, + { 0x4023ed916872b021ull, 0x408f361ca8278f06ull }, + { 0x4023ee147ae147aeull, 0x408f3ba6f58c848cull }, + { 0x4023ee978d4fdf3cull, 0x408f41323eb3bec8ull }, + { 0x4023ef1a9fbe76c9ull, 0x408f46be83c9ee06ull }, + { 0x4023ef9db22d0e56ull, 0x408f4c4bc4fbcaa5ull }, + { 0x4023f020c49ba5e3ull, 0x408f51da027614e5ull }, + { 0x4023f0a3d70a3d71ull, 0x408f57693c659504ull }, + { 0x4023f126e978d4feull, 0x408f5cf972f71b14ull }, + { 0x4023f1a9fbe76c8bull, 0x408f628aa6577f39ull }, + { 0x4023f22d0e560419ull, 0x408f681cd6b3a18full }, + { 0x4023f2b020c49ba6ull, 0x408f6db004386a08ull }, + { 0x4023f33333333333ull, 0x408f73442f12c8afull }, + { 0x4023f3b645a1cac1ull, 0x408f78d9576fb589ull }, + { 0x4023f4395810624eull, 0x408f7e6f7d7c3077ull }, + { 0x4023f4bc6a7ef9dbull, 0x408f8406a1654176ull }, + { 0x4023f53f7ced9169ull, 0x408f899ec357f881ull }, + { 0x4023f5c28f5c28f6ull, 0x408f8f37e3816d74ull }, + { 0x4023f645a1cac083ull, 0x408f94d2020ec04cull }, + { 0x4023f6c8b4395811ull, 0x408f9a6d1f2d1907ull }, + { 0x4023f74bc6a7ef9eull, 0x408fa0093b09a789ull }, + { 0x4023f7ced916872bull, 0x408fa5a655d1a3daull }, + { 0x4023f851eb851eb8ull, 0x408fab446fb24dfeull }, + { 0x4023f8d4fdf3b646ull, 0x408fb0e388d8ee0eull }, + { 0x4023f95810624dd3ull, 0x408fb683a172d409ull }, + { 0x4023f9db22d0e560ull, 0x408fbc24b9ad581dull }, + { 0x4023fa5e353f7ceeull, 0x408fc1c6d1b5da82ull }, + { 0x4023fae147ae147bull, 0x408fc769e9b9c35full }, + { 0x4023fb645a1cac08ull, 0x408fcd0e01e6830aull }, + { 0x4023fbe76c8b4396ull, 0x408fd2b31a6991ebull }, + { 0x4023fc6a7ef9db23ull, 0x408fd85933707059ull }, + { 0x4023fced916872b0ull, 0x408fde004d28a6e3ull }, + { 0x4023fd70a3d70a3eull, 0x408fe3a867bfc62aull }, + { 0x4023fdf3b645a1cbull, 0x408fe951836366c6ull }, + { 0x4023fe76c8b43958ull, 0x408feefba0412988ull }, + { 0x4023fef9db22d0e5ull, 0x408ff4a6be86b750ull }, + { 0x4023ff7ced916873ull, 0x408ffa52de61c123ull }, +}; diff --git a/tests/libc/math/test_floor.c b/tests/libc/math/test_floor.c index dc72c4062b..65204e6be8 100644 --- a/tests/libc/math/test_floor.c +++ b/tests/libc/math/test_floor.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/math/test_log.c b/tests/libc/math/test_log.c index 5ce4b5721f..0e5bb13a52 100644 --- a/tests/libc/math/test_log.c +++ b/tests/libc/math/test_log.c @@ -1,36 +1,32 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include -#include #include #include #include "clar.h" -double log_theirs(double x) { - return log(x); -} - // "Define" libc functions we're testing #include "pblibc_private.h" -void test_pow__initialize(void) { +// Correctly-rounded reference values, precomputed off-host so the expected +// results no longer depend on the host libm. See gen_log_reference.py. +#include "log_reference.h" + +static double double_from_bits(uint64_t bits) { + union { uint64_t bits; double value; } u = { .bits = bits }; + return u.value; +} + +static int64_t ulp_diff(double a, double b) { + union { double value; int64_t bits; } ua = { .value = a }; + union { double value; int64_t bits; } ub = { .value = b }; + int64_t diff = ua.bits - ub.bits; + return diff < 0 ? -diff : diff; +} + +void test_log__initialize(void) { fesetround(FE_TONEAREST); } @@ -38,15 +34,14 @@ void test_pow__initialize(void) { //! Tests void test_log__basic(void) { - for(int i = 1; i < 10000; i++) { - double v = i * 0.001; + // log.c (newlib) documents its error as always less than 1 ulp relative to + // the true value, so 1 ulp against the correctly-rounded reference is the + // tolerance. + for (int i = 0; i < LOG_REFERENCE_COUNT; i++) { + double v = double_from_bits(s_log_reference[i].input_bits); + double expected = double_from_bits(s_log_reference[i].expected_bits); double us = log(v); - double them = log_theirs(v); - - // 1 ulps is acceptable error - // To actually check this, we need to do some sorta gross raw operations on the doubles - int64_t diff = *(int64_t*)&us - *(int64_t*)&them; - cl_assert(diff <= 1 && diff >= -1); + cl_assert(ulp_diff(us, expected) <= 1); } cl_assert(isnan(log(-1.0))); cl_assert(log(0) == -HUGE_VAL); diff --git a/tests/libc/math/test_pow.c b/tests/libc/math/test_pow.c index a4d8afba3c..9980432c06 100644 --- a/tests/libc/math/test_pow.c +++ b/tests/libc/math/test_pow.c @@ -1,35 +1,31 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include -#include #include #include #include "clar.h" -double pow_theirs(double x, double y) { - return pow(x,y); -} - // "Define" libc functions we're testing #include "pblibc_private.h" +// Correctly-rounded reference values, precomputed off-host so the expected +// results no longer depend on the host libm. See gen_pow_reference.py. +#include "pow_reference.h" + +static double double_from_bits(uint64_t bits) { + union { uint64_t bits; double value; } u = { .bits = bits }; + return u.value; +} + +static int64_t ulp_diff(double a, double b) { + union { double value; int64_t bits; } ua = { .value = a }; + union { double value; int64_t bits; } ub = { .value = b }; + int64_t diff = ua.bits - ub.bits; + return diff < 0 ? -diff : diff; +} + void test_pow__initialize(void) { fesetround(FE_TONEAREST); } @@ -38,14 +34,13 @@ void test_pow__initialize(void) { //! Tests void test_pow__basic(void) { - for(int i = 0; i < 10000; i++) { - double v = i * 0.001; + // pow.c (newlib) documents its result as "nearly rounded"; measured against + // the correctly-rounded reference it is within 1 ulp, so 1 ulp is the + // tolerance. + for (int i = 0; i < POW_REFERENCE_COUNT; i++) { + double v = double_from_bits(s_pow_reference[i].input_bits); + double expected = double_from_bits(s_pow_reference[i].expected_bits); double us = pow(2, v); - double them = pow_theirs(2,v); - - // 1 ulps is acceptable error - // To actually check this, we need to do some sorta gross raw operations on the doubles - int64_t diff = *(int64_t*)&us - *(int64_t*)&them; - cl_assert(diff <= 1 && diff >= -1); + cl_assert(ulp_diff(us, expected) <= 1); } } diff --git a/tests/libc/math/test_round.c b/tests/libc/math/test_round.c index e6f9013399..36076aec47 100644 --- a/tests/libc/math/test_round.c +++ b/tests/libc/math/test_round.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/tests/libc/math/wscript b/tests/libc/math/wscript deleted file mode 100644 index 5b6a23d21e..0000000000 --- a/tests/libc/math/wscript +++ /dev/null @@ -1,28 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/libc/math/floor.c", - test_sources_ant_glob = "test_floor.c", - add_includes = ["src/libc"], - test_libs=['m']) - - clar(ctx, - sources_ant_glob = "src/libc/math/log.c", - test_sources_ant_glob = "test_log.c", - add_includes = ["src/libc"], - test_libs=['m']) - - clar(ctx, - sources_ant_glob = "src/libc/math/pow.c" \ - " src/libc/math/scalbn.c" \ - " src/libc/math/sqrt.c", - test_sources_ant_glob = "test_pow.c", - add_includes = ["src/libc"], - test_libs=['m']) - - clar(ctx, - sources_ant_glob = "src/libc/math/round.c", - test_sources_ant_glob = "test_round.c", - add_includes = ["src/libc"], - test_libs=['m']) diff --git a/tests/libc/math/wscript_build b/tests/libc/math/wscript_build new file mode 100644 index 0000000000..0a764cf2dd --- /dev/null +++ b/tests/libc/math/wscript_build @@ -0,0 +1,27 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/libc/math/floor.c", + test_sources_ant_glob = "test_floor.c", + add_includes = ["src/libc"], + test_libs=['m']) + +clar(ctx, + sources_ant_glob = "src/libc/math/log.c", + test_sources_ant_glob = "test_log.c", + add_includes = ["src/libc"], + test_libs=['m']) + +clar(ctx, + sources_ant_glob = "src/libc/math/pow.c" \ + " src/libc/math/scalbn.c" \ + " src/libc/math/sqrt.c", + test_sources_ant_glob = "test_pow.c", + add_includes = ["src/libc"], + test_libs=['m']) + +clar(ctx, + sources_ant_glob = "src/libc/math/round.c", + test_sources_ant_glob = "test_round.c", + add_includes = ["src/libc"], + test_libs=['m']) diff --git a/tests/libc/printf/test_sprintf.c b/tests/libc/printf/test_sprintf.c index dc39c17d98..1254656856 100644 --- a/tests/libc/printf/test_sprintf.c +++ b/tests/libc/printf/test_sprintf.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/printf/wscript b/tests/libc/printf/wscript deleted file mode 100644 index 4c41586484..0000000000 --- a/tests/libc/printf/wscript +++ /dev/null @@ -1,7 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/libc/vsprintf.c", - test_sources_ant_glob = "test_sprintf.c", - add_includes = ["src/libc"]) diff --git a/tests/libc/printf/wscript_build b/tests/libc/printf/wscript_build new file mode 100644 index 0000000000..8b2c3a1845 --- /dev/null +++ b/tests/libc/printf/wscript_build @@ -0,0 +1,6 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/libc/vsprintf.c", + test_sources_ant_glob = "test_sprintf.c", + add_includes = ["src/libc"]) diff --git a/tests/libc/string/test_atoi.c b/tests/libc/string/test_atoi.c index 4a2fca10ec..3fc5d710fc 100644 --- a/tests/libc/string/test_atoi.c +++ b/tests/libc/string/test_atoi.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_ctype.c b/tests/libc/string/test_ctype.c index 082387921c..e221630025 100644 --- a/tests/libc/string/test_ctype.c +++ b/tests/libc/string/test_ctype.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // Horrible hack to get glibc's ctype.h to define isascii et al. // Some of the weird options that are set in the unit test build environment diff --git a/tests/libc/string/test_memchr.c b/tests/libc/string/test_memchr.c index c45c05f70a..fb9617659a 100644 --- a/tests/libc/string/test_memchr.c +++ b/tests/libc/string/test_memchr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_memcmp.c b/tests/libc/string/test_memcmp.c index 962569f01d..2c2d344d59 100644 --- a/tests/libc/string/test_memcmp.c +++ b/tests/libc/string/test_memcmp.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_memcpy.c b/tests/libc/string/test_memcpy.c index 32a8026df9..29d6fae854 100644 --- a/tests/libc/string/test_memcpy.c +++ b/tests/libc/string/test_memcpy.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_memset.c b/tests/libc/string/test_memset.c index 57cf9a6e3d..cbbc89e98f 100644 --- a/tests/libc/string/test_memset.c +++ b/tests/libc/string/test_memset.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strcat.c b/tests/libc/string/test_strcat.c index 6b205a63a1..781c64bab9 100644 --- a/tests/libc/string/test_strcat.c +++ b/tests/libc/string/test_strcat.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strchr.c b/tests/libc/string/test_strchr.c index bb5ad57a2a..74dc7441dd 100644 --- a/tests/libc/string/test_strchr.c +++ b/tests/libc/string/test_strchr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strcmp.c b/tests/libc/string/test_strcmp.c index d5ea0158fd..8e8fd02b07 100644 --- a/tests/libc/string/test_strcmp.c +++ b/tests/libc/string/test_strcmp.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strcpy.c b/tests/libc/string/test_strcpy.c index 92052ec73f..8ae96fb00c 100644 --- a/tests/libc/string/test_strcpy.c +++ b/tests/libc/string/test_strcpy.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strlen.c b/tests/libc/string/test_strlen.c index 58181afb6d..671f77bf3b 100644 --- a/tests/libc/string/test_strlen.c +++ b/tests/libc/string/test_strlen.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strspn.c b/tests/libc/string/test_strspn.c index 619b4fe94e..90690b4516 100644 --- a/tests/libc/string/test_strspn.c +++ b/tests/libc/string/test_strspn.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strstr.c b/tests/libc/string/test_strstr.c index 360410ddc7..13a05694a0 100644 --- a/tests/libc/string/test_strstr.c +++ b/tests/libc/string/test_strstr.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/test_strtol.c b/tests/libc/string/test_strtol.c index bad4189c72..b7cfb54c60 100644 --- a/tests/libc/string/test_strtol.c +++ b/tests/libc/string/test_strtol.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/libc/string/wscript b/tests/libc/string/wscript deleted file mode 100644 index 94457870bc..0000000000 --- a/tests/libc/string/wscript +++ /dev/null @@ -1,87 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = "src/libc/string/memcmp.c", - test_sources_ant_glob = "test_memcmp.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/memcpy.c", - test_sources_ant_glob = "test_memcpy.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/memset.c", - test_sources_ant_glob = "test_memset.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/memchr.c", - test_sources_ant_glob = "test_memchr.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/atoi.c" \ - " src/libc/string/strtol.c", - test_sources_ant_glob = "test_atoi.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strtol.c", - test_sources_ant_glob = "test_strtol.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strcat.c" \ - " src/libc/string/strlen.c" \ - " src/libc/string/memcpy.c" \ - " src/libc/string/memset.c" \ - " src/libc/string/strcpy.c", - test_sources_ant_glob = "test_strcat.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strlen.c", - test_sources_ant_glob = "test_strlen.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strlen.c" \ - " src/libc/string/memcpy.c" \ - " src/libc/string/memset.c" \ - " src/libc/string/strcpy.c", - test_sources_ant_glob = "test_strcpy.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strlen.c" \ - " src/libc/string/memcmp.c" \ - " src/libc/string/strcmp.c", - test_sources_ant_glob = "test_strcmp.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strlen.c" \ - " src/libc/string/memchr.c" \ - " src/libc/string/strchr.c", - test_sources_ant_glob = "test_strchr.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strspn.c", - test_sources_ant_glob = "test_strspn.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/string/strlen.c" \ - " src/libc/string/memcmp.c" \ - " src/libc/string/strstr.c" \ - " src/libc/string/strcmp.c", - test_sources_ant_glob = "test_strstr.c", - add_includes = ["src/libc"]) - - clar(ctx, - sources_ant_glob = "src/libc/ctype_ptr.c", - test_sources_ant_glob = "test_ctype.c", - add_includes = ["src/libc"]) diff --git a/tests/libc/string/wscript_build b/tests/libc/string/wscript_build new file mode 100644 index 0000000000..35c9de74c4 --- /dev/null +++ b/tests/libc/string/wscript_build @@ -0,0 +1,86 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = "src/libc/string/memcmp.c", + test_sources_ant_glob = "test_memcmp.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/memcpy.c", + test_sources_ant_glob = "test_memcpy.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/memset.c", + test_sources_ant_glob = "test_memset.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/memchr.c", + test_sources_ant_glob = "test_memchr.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/atoi.c" \ + " src/libc/string/strtol.c", + test_sources_ant_glob = "test_atoi.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strtol.c", + test_sources_ant_glob = "test_strtol.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strcat.c" \ + " src/libc/string/strlen.c" \ + " src/libc/string/memcpy.c" \ + " src/libc/string/memset.c" \ + " src/libc/string/strcpy.c", + test_sources_ant_glob = "test_strcat.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strlen.c", + test_sources_ant_glob = "test_strlen.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strlen.c" \ + " src/libc/string/memcpy.c" \ + " src/libc/string/memset.c" \ + " src/libc/string/strcpy.c", + test_sources_ant_glob = "test_strcpy.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strlen.c" \ + " src/libc/string/memcmp.c" \ + " src/libc/string/strcmp.c", + test_sources_ant_glob = "test_strcmp.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strlen.c" \ + " src/libc/string/memchr.c" \ + " src/libc/string/strchr.c", + test_sources_ant_glob = "test_strchr.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strspn.c", + test_sources_ant_glob = "test_strspn.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/string/strlen.c" \ + " src/libc/string/memcmp.c" \ + " src/libc/string/strstr.c" \ + " src/libc/string/strcmp.c", + test_sources_ant_glob = "test_strstr.c", + add_includes = ["src/libc"]) + +clar(ctx, + sources_ant_glob = "src/libc/ctype_ptr.c", + test_sources_ant_glob = "test_ctype.c", + add_includes = ["src/libc"]) diff --git a/tests/libc/time/test_strftime.c b/tests/libc/time/test_strftime.c index f430758c8e..574b6c2fdc 100644 --- a/tests/libc/time/test_strftime.c +++ b/tests/libc/time/test_strftime.c @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/pbl_std/pbl_std.h" #include "applib/pbl_std/locale.h" -#include "services/common/i18n/i18n.h" +#include "pbl/services/i18n/i18n.h" #include "util/size.h" #include "clar.h" @@ -53,6 +40,9 @@ const char *get_timezone_abbr(void) { return s_timezone_abbr; } +int32_t time_get_gmtoffset(void) { return 0; } +int32_t time_get_dstoffset(void) { return 0; } + void sys_copy_timezone_abbr(char* timezone_abbr, time_t time) { const char* sys_tz = get_timezone_abbr(); strncpy(timezone_abbr, sys_tz, TZ_LEN); diff --git a/tests/libc/time/wscript b/tests/libc/time/wscript deleted file mode 100644 index 1508a6bfe1..0000000000 --- a/tests/libc/time/wscript +++ /dev/null @@ -1,15 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources_ant_glob = ( - " src/fw/flash_region/flash_region.c" - " src/fw/applib/pbl_std/pbl_std.c" - " src/fw/applib/pbl_std/strftime.c" - " src/fw/applib/pbl_std/timelocal.c" - " tests/fakes/fake_rtc.c" - " tests/fakes/fake_spi_flash.c" - ), - test_sources_ant_glob = "test_strftime.c", - override_includes=['dummy_board'], - add_includes = ["src/libc"]) diff --git a/tests/libc/time/wscript_build b/tests/libc/time/wscript_build new file mode 100644 index 0000000000..71404ad8bc --- /dev/null +++ b/tests/libc/time/wscript_build @@ -0,0 +1,14 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob = ( + " src/fw/flash_region/flash_region.c" + " src/fw/applib/pbl_std/pbl_std.c" + " src/fw/applib/pbl_std/strftime.c" + " src/fw/applib/pbl_std/timelocal.c" + " tests/fakes/fake_rtc.c" + " tests/fakes/fake_spi_flash.c" + ), + test_sources_ant_glob = "test_strftime.c", + override_includes=['dummy_board'], + add_includes = ["src/libc"]) diff --git a/tests/libutil/test_circular_buffer.c b/tests/libutil/test_circular_buffer.c index 5ac5ab2aea..c1a634f689 100644 --- a/tests/libutil/test_circular_buffer.c +++ b/tests/libutil/test_circular_buffer.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/circular_buffer.h" diff --git a/tests/libutil/test_circular_cache.c b/tests/libutil/test_circular_cache.c index ded48ac8e4..542690a43d 100644 --- a/tests/libutil/test_circular_cache.c +++ b/tests/libutil/test_circular_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/circular_cache.h" diff --git a/tests/libutil/test_crc32.c b/tests/libutil/test_crc32.c index eb4bcff455..4cfa77d33c 100644 --- a/tests/libutil/test_crc32.c +++ b/tests/libutil/test_crc32.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/libutil/test_keyed_circular_cache.c b/tests/libutil/test_keyed_circular_cache.c index d38f596ad7..60381ec36a 100644 --- a/tests/libutil/test_keyed_circular_cache.c +++ b/tests/libutil/test_keyed_circular_cache.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/keyed_circular_cache.h" diff --git a/tests/libutil/test_list.c b/tests/libutil/test_list.c index af10191f26..d6e9ab1dc1 100644 --- a/tests/libutil/test_list.c +++ b/tests/libutil/test_list.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/list.h" diff --git a/tests/libutil/test_math.c b/tests/libutil/test_math.c index ea5d21a82a..7662feab40 100644 --- a/tests/libutil/test_math.c +++ b/tests/libutil/test_math.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/trig.h" #include "util/math.h" diff --git a/tests/libutil/test_math_fixed.c b/tests/libutil/test_math_fixed.c index c84bf092d0..e7f174b09d 100644 --- a/tests/libutil/test_math_fixed.c +++ b/tests/libutil/test_math_fixed.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/math_fixed.h" diff --git a/tests/libutil/test_slist.c b/tests/libutil/test_slist.c new file mode 100644 index 0000000000..c35d4af405 --- /dev/null +++ b/tests/libutil/test_slist.c @@ -0,0 +1,376 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "util/slist.h" + +#include +#include +#include + +#include "clar.h" +#include "stubs_passert.h" + +// Stubs +/////////////////////////////////////////////////////////// +int g_pbl_log_level = 0; +void pbl_log(int level, const char* src_filename, int src_line_number, const char* fmt, ...) { } + +// Tests +/////////////////////////////////////////////////////////// + +void test_slist__initialize(void) { +} + +void test_slist__cleanup(void) { +} + +void test_slist__insert_after(void) { + SingleListNode *tail = NULL; + SingleListNode a = SINGLE_LIST_NODE_NULL, b = SINGLE_LIST_NODE_NULL; + tail = slist_insert_after(tail, &a); + cl_assert(tail == &a); + tail = slist_insert_after(&a, &b); + cl_assert(tail == &b); + cl_assert(a.next == &b); + cl_assert(b.next == NULL); +} + +void test_slist__insert_after_middle(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &c; + slist_insert_after(&a, &b); + cl_assert(a.next == &b); + cl_assert(b.next == &c); + cl_assert(c.next == NULL); +} + +void test_slist__prepend(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + SingleListNode *head; + head = slist_prepend(&c, &b); + cl_assert(head == &b); + cl_assert(b.next == &c); + head = slist_prepend(&b, &a); + cl_assert(head == &a); + cl_assert(a.next == &b); + cl_assert(b.next == &c); + cl_assert(c.next == NULL); +} + +void test_slist__prepend_null(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode *head = slist_prepend(NULL, &a); + cl_assert(head == &a); + cl_assert(a.next == NULL); +} + +void test_slist__append(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + SingleListNode *tail; + tail = slist_append(&a, &b); + cl_assert(tail == &b); + tail = slist_append(&a, &c); + cl_assert(tail == &c); + cl_assert(a.next == &b); + cl_assert(b.next == &c); + cl_assert(c.next == NULL); +} + +void test_slist__append_null(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode *tail = slist_append(NULL, &a); + cl_assert(tail == &a); +} + +void test_slist__pop_head(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL, b = SINGLE_LIST_NODE_NULL; + a.next = &b; + SingleListNode *new_head = slist_pop_head(&a); + cl_assert(new_head == &b); + cl_assert(a.next == NULL); + cl_assert(b.next == NULL); +} + +void test_slist__pop_head_single(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode *new_head = slist_pop_head(&a); + cl_assert(new_head == NULL); +} + +void test_slist__pop_head_null(void) { + cl_assert(slist_pop_head(NULL) == NULL); +} + +void test_slist__remove_head(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + SingleListNode *head = &a; + slist_remove(&a, &head); + cl_assert(head == &b); + cl_assert(a.next == NULL); + cl_assert(b.next == &c); +} + +void test_slist__remove_middle(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + SingleListNode *head = &a; + slist_remove(&b, &head); + cl_assert(head == &a); + cl_assert(a.next == &c); + cl_assert(b.next == NULL); + cl_assert(c.next == NULL); +} + +void test_slist__remove_tail(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + SingleListNode *head = &a; + slist_remove(&c, &head); + cl_assert(head == &a); + cl_assert(a.next == &b); + cl_assert(b.next == NULL); + cl_assert(c.next == NULL); +} + +void test_slist__remove_only(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode *head = &a; + slist_remove(&a, &head); + cl_assert(head == NULL); + cl_assert(a.next == NULL); +} + +void test_slist__remove_not_found(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode orphan = SINGLE_LIST_NODE_NULL; + a.next = &b; + SingleListNode *head = &a; + slist_remove(&orphan, &head); + // List should be unchanged + cl_assert(head == &a); + cl_assert(a.next == &b); + cl_assert(b.next == NULL); +} + +void test_slist__get_next(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL, b = SINGLE_LIST_NODE_NULL; + a.next = &b; + cl_assert(slist_get_next(&a) == &b); + cl_assert(slist_get_next(&b) == NULL); + cl_assert(slist_get_next(NULL) == NULL); +} + +void test_slist__get_tail(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + cl_assert(slist_get_tail(&a) == &c); + cl_assert(slist_get_tail(&b) == &c); + cl_assert(slist_get_tail(&c) == &c); + cl_assert(slist_get_tail(NULL) == NULL); +} + +void test_slist__is_tail(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL, b = SINGLE_LIST_NODE_NULL; + a.next = &b; + cl_assert(!slist_is_tail(&a)); + cl_assert(slist_is_tail(&b)); + cl_assert(!slist_is_tail(NULL)); +} + +void test_slist__count(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + cl_assert_equal_i(slist_count(&a), 3); + cl_assert_equal_i(slist_count(&b), 2); + cl_assert_equal_i(slist_count(&c), 1); + cl_assert_equal_i(slist_count(NULL), 0); +} + +void test_slist__contains(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + SingleListNode orphan = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + cl_assert(slist_contains(&a, &a)); + cl_assert(slist_contains(&a, &b)); + cl_assert(slist_contains(&a, &c)); + cl_assert(!slist_contains(&a, &orphan)); + cl_assert(!slist_contains(NULL, &a)); + cl_assert(!slist_contains(&a, NULL)); +} + +typedef struct SIntNode { + SingleListNode list_node; + int value; +} SIntNode; + +static bool prv_filter_value(SingleListNode *node, void *data) { + SIntNode *int_node = (SIntNode *)node; + return int_node->value == (int)(intptr_t)data; +} + +void test_slist__find(void) { + SIntNode a = { .value = 10 }; + SIntNode b = { .value = 20 }; + SIntNode c = { .value = 30 }; + slist_init(&a.list_node); + slist_init(&b.list_node); + slist_init(&c.list_node); + a.list_node.next = &b.list_node; + b.list_node.next = &c.list_node; + + cl_assert(slist_find(&a.list_node, prv_filter_value, (void *)(intptr_t)20) == &b.list_node); + cl_assert(slist_find(&a.list_node, prv_filter_value, (void *)(intptr_t)30) == &c.list_node); + cl_assert(slist_find(&a.list_node, prv_filter_value, (void *)(intptr_t)10) == &a.list_node); + cl_assert(slist_find(&a.list_node, prv_filter_value, (void *)(intptr_t)99) == NULL); + cl_assert(slist_find(NULL, prv_filter_value, (void *)(intptr_t)10) == NULL); +} + +static int prv_sort_comparator(SIntNode *a, SIntNode *b) { + return b->value - a->value; +} + +void test_slist__sort_ascending(void) { + SIntNode bar1 = { .value = 1 }; + SIntNode bar2 = { .value = 2 }; + SIntNode bar3 = { .value = 3 }; + slist_init(&bar1.list_node); + slist_init(&bar2.list_node); + slist_init(&bar3.list_node); + + SingleListNode *head = NULL; + + head = slist_sorted_add(head, &bar2.list_node, (Comparator)prv_sort_comparator, true); + cl_assert(head == &bar2.list_node); + + head = slist_sorted_add(head, &bar3.list_node, (Comparator)prv_sort_comparator, true); + cl_assert(head == &bar2.list_node); + cl_assert(slist_get_tail(head) == &bar3.list_node); + + head = slist_sorted_add(head, &bar1.list_node, (Comparator)prv_sort_comparator, true); + cl_assert(head == &bar1.list_node); + cl_assert(slist_get_next(head) == &bar2.list_node); + cl_assert(slist_get_tail(head) == &bar3.list_node); +} + +void test_slist__sort_descending(void) { + SIntNode bar1 = { .value = 1 }; + SIntNode bar2 = { .value = 2 }; + SIntNode bar3 = { .value = 3 }; + slist_init(&bar1.list_node); + slist_init(&bar2.list_node); + slist_init(&bar3.list_node); + + SingleListNode *head = NULL; + + head = slist_sorted_add(head, &bar2.list_node, (Comparator)prv_sort_comparator, false); + cl_assert(head == &bar2.list_node); + + head = slist_sorted_add(head, &bar3.list_node, (Comparator)prv_sort_comparator, false); + cl_assert(head == &bar3.list_node); + cl_assert(slist_get_tail(head) == &bar2.list_node); + + head = slist_sorted_add(head, &bar1.list_node, (Comparator)prv_sort_comparator, false); + cl_assert(head == &bar3.list_node); + cl_assert(slist_get_next(head) == &bar2.list_node); + cl_assert(slist_get_tail(head) == &bar1.list_node); +} + +void test_slist__concatenate(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + SingleListNode d = SINGLE_LIST_NODE_NULL; + SingleListNode e = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + d.next = &e; + + SingleListNode *head = slist_concatenate(&a, &d); + cl_assert(head == &a); + cl_assert(c.next == &d); + cl_assert(d.next == &e); + cl_assert(e.next == NULL); + cl_assert_equal_i(slist_count(head), 5); +} + +void test_slist__concatenate_null(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + cl_assert(slist_concatenate(NULL, &a) == &a); + cl_assert(slist_concatenate(&a, NULL) == &a); + cl_assert(slist_concatenate(NULL, NULL) == NULL); +} + +#define CTX_VALUE 0xdeadbeef +#define INT_VALUE 17 + +static bool prv_slist_set_val_each(SingleListNode *node, void *context) { + SIntNode *int_node = (SIntNode *)node; + int_node->value = INT_VALUE; + cl_assert_equal_i(CTX_VALUE, (uintptr_t)context); + return true; +} + +void test_slist__each(void) { + SIntNode a = {}, b = {}, c = {}; + SingleListNode *head; + head = slist_prepend((SingleListNode *)&c, (SingleListNode *)&b); + head = slist_prepend((SingleListNode *)&b, (SingleListNode *)&a); + + cl_assert_equal_i(slist_count(head), 3); + slist_foreach(head, prv_slist_set_val_each, (void *)(uintptr_t)CTX_VALUE); + + uint32_t num_nodes = 0; + SingleListNode *temp = head; + while (temp) { + SingleListNode *next = temp->next; + SIntNode *int_node = (SIntNode *)temp; + cl_assert_equal_i(int_node->value, INT_VALUE); + temp = next; + num_nodes++; + } + + cl_assert_equal_i(num_nodes, 3); +} + +static bool prv_stop_at_second(SingleListNode *node, void *context) { + int *count = (int *)context; + (*count)++; + return (*count < 2); +} + +void test_slist__each_early_stop(void) { + SingleListNode a = SINGLE_LIST_NODE_NULL; + SingleListNode b = SINGLE_LIST_NODE_NULL; + SingleListNode c = SINGLE_LIST_NODE_NULL; + a.next = &b; + b.next = &c; + int count = 0; + slist_foreach(&a, prv_stop_at_second, &count); + cl_assert_equal_i(count, 2); +} diff --git a/tests/libutil/test_sort.c b/tests/libutil/test_sort.c index 4c3f153ad5..e627b59fc4 100644 --- a/tests/libutil/test_sort.c +++ b/tests/libutil/test_sort.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/libutil/test_string.c b/tests/libutil/test_string.c index a107f83c2b..747926e387 100644 --- a/tests/libutil/test_string.c +++ b/tests/libutil/test_string.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/string.h" diff --git a/tests/libutil/test_struct.c b/tests/libutil/test_struct.c index 79d08b8ff1..5eb5704be9 100644 --- a/tests/libutil/test_struct.c +++ b/tests/libutil/test_struct.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/libutil/test_uuid.c b/tests/libutil/test_uuid.c index c004c23040..b979740254 100644 --- a/tests/libutil/test_uuid.c +++ b/tests/libutil/test_uuid.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/libutil/wscript b/tests/libutil/wscript deleted file mode 100644 index e0bfe47632..0000000000 --- a/tests/libutil/wscript +++ /dev/null @@ -1,50 +0,0 @@ -from waftools.pebble_test import clar - - -def build(ctx): - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob="test_circular_buffer.c") - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob="test_list.c") - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob="test_math.c", - test_libs=['m']) - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob="test_string.c") - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob="test_math_fixed.c") - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob='test_crc32.c') - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob='test_uuid.c') - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob='test_circular_cache.c') - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob='test_keyed_circular_cache.c') - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob='test_struct.c') - - clar(ctx, - sources_ant_glob=None, - test_sources_ant_glob='test_sort.c') - -# vim:filetype=python diff --git a/tests/libutil/wscript_build b/tests/libutil/wscript_build new file mode 100644 index 0000000000..5adc3a6171 --- /dev/null +++ b/tests/libutil/wscript_build @@ -0,0 +1,52 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob="test_circular_buffer.c") + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob="test_list.c") + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob="test_slist.c") + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob="test_math.c", + test_libs=['m']) + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob="test_string.c") + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob="test_math_fixed.c") + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob='test_crc32.c') + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob='test_uuid.c') + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob='test_circular_cache.c') + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob='test_keyed_circular_cache.c') + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob='test_struct.c') + +clar(ctx, + sources_ant_glob=None, + test_sources_ant_glob='test_sort.c') + +# vim:filetype=python diff --git a/tests/overrides/bluetooth_persistent_storage_v2/pbl/services/bluetooth/bluetooth_persistent_storage_unittest_impl.h b/tests/overrides/bluetooth_persistent_storage_v2/pbl/services/bluetooth/bluetooth_persistent_storage_unittest_impl.h new file mode 100644 index 0000000000..ca6ac31fdd --- /dev/null +++ b/tests/overrides/bluetooth_persistent_storage_v2/pbl/services/bluetooth/bluetooth_persistent_storage_unittest_impl.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/bluetooth/bluetooth_persistent_storage_v2_impl.h" diff --git a/tests/overrides/bluetooth_persistent_storage_v2/services/normal/bluetooth/bluetooth_persistent_storage_unittest_impl.h b/tests/overrides/bluetooth_persistent_storage_v2/services/normal/bluetooth/bluetooth_persistent_storage_unittest_impl.h deleted file mode 100644 index 3b97c1ecc7..0000000000 --- a/tests/overrides/bluetooth_persistent_storage_v2/services/normal/bluetooth/bluetooth_persistent_storage_unittest_impl.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define UNITTEST_BT_PERSISTENT_STORAGE_VERSION (2) - -#include "services/normal/bluetooth/bluetooth_persistent_storage_v2_impl.h" diff --git a/tests/overrides/da1468x/test_dialog_analytics/FreeRTOS.h b/tests/overrides/da1468x/test_dialog_analytics/FreeRTOS.h index bbf729c3bf..3ba00fbe08 100644 --- a/tests/overrides/da1468x/test_dialog_analytics/FreeRTOS.h +++ b/tests/overrides/da1468x/test_dialog_analytics/FreeRTOS.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/da1468x/test_dialog_analytics/FreeRTOSConfig.h b/tests/overrides/da1468x/test_dialog_analytics/FreeRTOSConfig.h index 49e71436c3..048fbf9513 100644 --- a/tests/overrides/da1468x/test_dialog_analytics/FreeRTOSConfig.h +++ b/tests/overrides/da1468x/test_dialog_analytics/FreeRTOSConfig.h @@ -1,16 +1,3 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/overrides/da1468x/test_dialog_analytics/portable.h b/tests/overrides/da1468x/test_dialog_analytics/portable.h index 49e71436c3..048fbf9513 100644 --- a/tests/overrides/da1468x/test_dialog_analytics/portable.h +++ b/tests/overrides/da1468x/test_dialog_analytics/portable.h @@ -1,16 +1,3 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ diff --git a/tests/overrides/da1468x/test_dialog_analytics/sys_rtc.h b/tests/overrides/da1468x/test_dialog_analytics/sys_rtc.h index 995f82b41d..7f68ec6232 100644 --- a/tests/overrides/da1468x/test_dialog_analytics/sys_rtc.h +++ b/tests/overrides/da1468x/test_dialog_analytics/sys_rtc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/default/_test/default_and_custom.h b/tests/overrides/default/_test/default_and_custom.h index 60adab27cb..125010943e 100644 --- a/tests/overrides/default/_test/default_and_custom.h +++ b/tests/overrides/default/_test/default_and_custom.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #define OVERRIDDEN_DEFINE (1) diff --git a/tests/overrides/default/_test/default_only.h b/tests/overrides/default/_test/default_only.h index 7d280e728c..58810caa4c 100644 --- a/tests/overrides/default/_test/default_only.h +++ b/tests/overrides/default/_test/default_only.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #define DEFAULT_ONLY_DEFINE (1) diff --git a/tests/overrides/default/applib/applib_malloc.auto.h b/tests/overrides/default/applib/applib_malloc.auto.h index 56398de5c8..00177bf39d 100644 --- a/tests/overrides/default/applib/applib_malloc.auto.h +++ b/tests/overrides/default/applib/applib_malloc.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/default/cmsis_core.h b/tests/overrides/default/cmsis_core.h new file mode 100644 index 0000000000..2b69b4fde8 --- /dev/null +++ b/tests/overrides/default/cmsis_core.h @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +/* Test stub: cmsis_core.h is a no-op in the test environment */ diff --git a/tests/overrides/default/font_resource_keys.auto.h b/tests/overrides/default/font_resource_keys.auto.h index 6784f70ca5..26cc4a751f 100644 --- a/tests/overrides/default/font_resource_keys.auto.h +++ b/tests/overrides/default/font_resource_keys.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -54,6 +41,7 @@ #define FONT_KEY_LECO_36_BOLD_NUMBERS "RESOURCE_ID_LECO_36_BOLD_NUMBERS" #define FONT_KEY_LECO_38_BOLD_NUMBERS "RESOURCE_ID_LECO_38_BOLD_NUMBERS" #define FONT_KEY_LECO_42_NUMBERS "RESOURCE_ID_LECO_42_NUMBERS" +#define FONT_KEY_LECO_60_NUMBERS_AM_PM "RESOURCE_ID_LECO_60_NUMBERS_AM_PM" #define FONT_KEY_FONT_FALLBACK "RESOURCE_ID_GOTHIC_14" #define FONT_KEY_LECO_28_LIGHT_NUMBERS "RESOURCE_ID_LECO_28_LIGHT_NUMBERS" #define FONT_KEY_AGENCY_FB_36_NUMBERS_AM_PM "RESOURCE_ID_AGENCY_FB_36_NUMBERS_AM_PM" diff --git a/tests/overrides/default/git_version.auto.h b/tests/overrides/default/git_version.auto.h index c8f8ecdee4..59a20d17ad 100644 --- a/tests/overrides/default/git_version.auto.h +++ b/tests/overrides/default/git_version.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/default/kernel/mpu_regions.auto.h b/tests/overrides/default/kernel/mpu_regions.auto.h index db2bbf1c80..eea22ce4da 100644 --- a/tests/overrides/default/kernel/mpu_regions.auto.h +++ b/tests/overrides/default/kernel/mpu_regions.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #define MPU_REGION_APP_BASE_ADDRESS 0x20000000 #define MPU_REGION_APP_SIZE 0x40000 diff --git a/tests/overrides/default/process_management/sdk_memory_limits.auto.h b/tests/overrides/default/process_management/sdk_memory_limits.auto.h index 37c8f71d96..ce5ae31f41 100644 --- a/tests/overrides/default/process_management/sdk_memory_limits.auto.h +++ b/tests/overrides/default/process_management/sdk_memory_limits.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ //! Memory limits for SDK applications //! diff --git a/tests/overrides/default/resources/asterix/resource/resource_ids.auto.h b/tests/overrides/default/resources/asterix/resource/resource_ids.auto.h new file mode 100644 index 0000000000..9519d4963c --- /dev/null +++ b/tests/overrides/default/resources/asterix/resource/resource_ids.auto.h @@ -0,0 +1,604 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED BY BUILD +// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN +// + +typedef enum { + INVALID_RESOURCE = 0, + RESOURCE_ID_INVALID = 0, + DEFAULT_MENU_ICON = 0, + RESOURCE_ID_ACTION_BAR_ICON_CHECK = 1, + RESOURCE_ID_ACTION_BAR_ICON_X = 2, + RESOURCE_ID_BT_PAIR_SUCCESS = 3, + RESOURCE_ID_BT_PAIR_FAILURE = 4, + RESOURCE_ID_BT_PAIR_APPROVE_ON_PHONE = 5, + RESOURCE_ID_BT_PAIR_CONFIRMATION = 6, + RESOURCE_ID_GENERIC_WARNING_LARGE = 7, + RESOURCE_ID_GOTHIC_18_BOLD = 8, + RESOURCE_ID_BATTERY_ICON_LOW_LARGE = 9, + RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE = 10, + RESOURCE_ID_BATTERY_ICON_FULL_LARGE = 11, + RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED = 12, + RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED = 13, + RESOURCE_ID_BATTERY_ICON_CHARGE = 14, + RESOURCE_ID_BATTERY_NEEDS_CHARGING = 15, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE = 16, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED = 17, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED = 18, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND = 19, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY = 20, + RESOURCE_ID_QUIET_TIME = 21, + RESOURCE_ID_MUSIC_APP_GLANCE_PLAY = 22, + RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE = 23, + RESOURCE_ID_NOTIFICATIONS_APP_GLANCE = 24, + RESOURCE_ID_SEND_TEXT_APP_GLANCE = 25, + RESOURCE_ID_WATCHFACES_APP_GLANCE = 26, + RESOURCE_ID_MENU_ICON_TICTOC_WATCH = 27, + RESOURCE_ID_MENU_ICON_KICKSTART_WATCH = 28, + RESOURCE_ID_SETTINGS_MENU_ICON_BLUETOOTH = 29, + RESOURCE_ID_SETTINGS_MENU_ICON_NOTIFICATIONS = 30, + RESOURCE_ID_SETTINGS_MENU_ICON_VIBRATIONS = 31, + RESOURCE_ID_SETTINGS_MENU_ICON_QUIET_TIME = 32, + RESOURCE_ID_SETTINGS_MENU_ICON_TIMELINE = 33, + RESOURCE_ID_SETTINGS_MENU_ICON_QUICK_LAUNCH = 34, + RESOURCE_ID_SETTINGS_MENU_ICON_DATE_TIME = 35, + RESOURCE_ID_SETTINGS_MENU_ICON_DISPLAY = 36, + RESOURCE_ID_SETTINGS_MENU_ICON_HEALTH = 37, + RESOURCE_ID_SETTINGS_MENU_ICON_THEMES = 38, + RESOURCE_ID_SETTINGS_MENU_ICON_BACKGROUND_APP = 39, + RESOURCE_ID_SETTINGS_MENU_ICON_SYSTEM = 40, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT = 41, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH = 42, + RESOURCE_ID_SETTINGS_ICON_AIRPLANE = 43, + RESOURCE_ID_ACTION_BAR_ICON_SMS = 44, + RESOURCE_ID_GOTHIC_09 = 45, + RESOURCE_ID_GOTHIC_14 = 46, + RESOURCE_ID_GOTHIC_14_EMOJI = 47, + RESOURCE_ID_GOTHIC_14_BOLD = 48, + RESOURCE_ID_GOTHIC_18 = 49, + RESOURCE_ID_GOTHIC_18_EMOJI = 50, + RESOURCE_ID_GOTHIC_24 = 51, + RESOURCE_ID_GOTHIC_24_BOLD = 52, + RESOURCE_ID_GOTHIC_24_EMOJI = 53, + RESOURCE_ID_GOTHIC_28 = 54, + RESOURCE_ID_GOTHIC_28_BOLD = 55, + RESOURCE_ID_GOTHIC_28_EMOJI = 56, + RESOURCE_ID_GOTHIC_36 = 57, + RESOURCE_ID_GOTHIC_36_BOLD = 58, + RESOURCE_ID_BITHAM_30_BLACK = 59, + RESOURCE_ID_BITHAM_42_BOLD = 60, + RESOURCE_ID_BITHAM_42_LIGHT = 61, + RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS = 62, + RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS = 63, + RESOURCE_ID_BITHAM_34_LIGHT_SUBSET = 64, + RESOURCE_ID_BITHAM_18_LIGHT_SUBSET = 65, + RESOURCE_ID_ROBOTO_CONDENSED_21 = 66, + RESOURCE_ID_ROBOTO_BOLD_SUBSET_49 = 67, + RESOURCE_ID_DROID_SERIF_28_BOLD = 68, + RESOURCE_ID_LECO_20_BOLD_NUMBERS = 69, + RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM = 70, + RESOURCE_ID_LECO_32_BOLD_NUMBERS = 71, + RESOURCE_ID_LECO_36_BOLD_NUMBERS = 72, + RESOURCE_ID_LECO_38_BOLD_NUMBERS = 73, + RESOURCE_ID_LECO_42_NUMBERS = 74, + RESOURCE_ID_LECO_28_LIGHT_NUMBERS = 75, + RESOURCE_ID_LECO_60_NUMBERS_AM_PM = 76, + RESOURCE_ID_LECO_60_BOLD_NUMBERS_AM_PM = 77, + RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD = 78, + RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD = 79, + RESOURCE_ID_MUSIC_ICON_ELLIPSIS = 80, + RESOURCE_ID_MUSIC_ICON_PAUSE = 81, + RESOURCE_ID_MUSIC_ICON_PLAY = 82, + RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE = 83, + RESOURCE_ID_MUSIC_ICON_VOLUME_UP = 84, + RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN = 85, + RESOURCE_ID_MUSIC_LARGE_CASSETTE = 86, + RESOURCE_ID_MUSIC_LARGE_VOLUME_UP = 87, + RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN = 88, + RESOURCE_ID_MUSIC_LARGE_PAUSED = 89, + RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC = 90, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHFACE_ICON = 91, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON = 92, + RESOURCE_ID_UNCHECKED_RADIO_BUTTON = 93, + RESOURCE_ID_CHECKED_RADIO_BUTTON = 94, + RESOURCE_ID_ACTION_BAR_ICON_UP = 95, + RESOURCE_ID_ACTION_BAR_ICON_DOWN = 96, + RESOURCE_ID_ACTION_BAR_ICON_MORE = 97, + RESOURCE_ID_ACTION_BAR_ICON_SNOOZE = 98, + RESOURCE_ID_ACTION_BAR_ICON_PAUSE = 99, + RESOURCE_ID_ACTION_BAR_ICON_START = 100, + RESOURCE_ID_ACTION_BAR_ICON_STOP = 101, + RESOURCE_ID_ACTION_BAR_ICON_TOGGLE = 102, + RESOURCE_ID_ACTION_MENU_FADE_TOP = 103, + RESOURCE_ID_ACTION_MENU_FADE_BOTTOM = 104, + RESOURCE_ID_CHECKBOX_ICON_CHECKED = 105, + RESOURCE_ID_CHECKBOX_ICON_UNCHECKED = 106, + RESOURCE_ID_CHECKMARK_ICON_BLACK = 107, + RESOURCE_ID_CHECKMARK_ICON_DOTTED = 108, + RESOURCE_ID_BLE_HRM_SHARING_TINY = 109, + RESOURCE_ID_BLE_HRM_SHARING_SMALL = 110, + RESOURCE_ID_BLE_HRM_SHARING_LARGE = 111, + RESOURCE_ID_NOTIFICATION_GENERIC_TINY = 112, + RESOURCE_ID_NOTIFICATION_GENERIC_SMALL = 113, + RESOURCE_ID_NOTIFICATION_GENERIC_LARGE = 114, + RESOURCE_ID_MISSED_CALL_TINY = 115, + RESOURCE_ID_MISSED_CALL_SMALL = 116, + RESOURCE_ID_GENERIC_REMINDER_TINY = 117, + RESOURCE_ID_WHATSAPP_NOTIFICATION_TINY = 118, + RESOURCE_ID_WHATSAPP_NOTIFICATION_SMALL = 119, + RESOURCE_ID_TWITTER_NOTIFICATION_TINY = 120, + RESOURCE_ID_TWITTER_NOTIFICATION_SMALL = 121, + RESOURCE_ID_TELEGRAM_APP_TINY = 122, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_TINY = 123, + RESOURCE_ID_GMAIL_NOTIFICATION_TINY = 124, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_TINY = 125, + RESOURCE_ID_FACEBOOK_NOTIFICATION_TINY = 126, + RESOURCE_ID_GENERIC_WEATHER_TINY = 127, + RESOURCE_ID_AUDIO_CASSETTE_TINY = 128, + RESOURCE_ID_SUNNY_DAY_TINY = 129, + RESOURCE_ID_GENERIC_SPORTS_TINY = 130, + RESOURCE_ID_GENERIC_EMAIL_TINY = 131, + RESOURCE_ID_AMERICAN_FOOTBALL_TINY = 132, + RESOURCE_ID_TIMELINE_CALENDAR_TINY = 133, + RESOURCE_ID_BASEBALL_GAME_TINY = 134, + RESOURCE_ID_BASEBALL_GAME_SMALL = 135, + RESOURCE_ID_ALARM_CLOCK_TINY = 136, + RESOURCE_ID_BIRTHDAY_EVENT_TINY = 137, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_TINY = 138, + RESOURCE_ID_CAR_RENTAL_TINY = 139, + RESOURCE_ID_SCHEDULED_FLIGHT_TINY = 140, + RESOURCE_ID_CLOUDY_DAY_TINY = 141, + RESOURCE_ID_CRICKET_GAME_TINY = 142, + RESOURCE_ID_CRICKET_GAME_SMALL = 143, + RESOURCE_ID_CRICKET_GAME_LARGE = 144, + RESOURCE_ID_DINNER_RESERVATION_TINY = 145, + RESOURCE_ID_DISMISSED_PHONE_CALL_TINY = 146, + RESOURCE_ID_GENERIC_CONFIRMATION_TINY = 147, + RESOURCE_ID_GENERIC_PIN_TINY = 148, + RESOURCE_ID_GENERIC_WARNING_TINY = 149, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_TINY = 150, + RESOURCE_ID_GLUCOSE_MONITOR_TINY = 151, + RESOURCE_ID_HEAVY_RAIN_TINY = 152, + RESOURCE_ID_HEAVY_SNOW_TINY = 153, + RESOURCE_ID_HOCKEY_GAME_TINY = 154, + RESOURCE_ID_HOCKEY_GAME_SMALL = 155, + RESOURCE_ID_HOCKEY_GAME_LARGE = 156, + RESOURCE_ID_HOTEL_RESERVATION_TINY = 157, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_TINY = 158, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_SMALL = 159, + RESOURCE_ID_LIGHT_RAIN_TINY = 160, + RESOURCE_ID_LIGHT_SNOW_TINY = 161, + RESOURCE_ID_MAILBOX_NOTIFICATION_TINY = 162, + RESOURCE_ID_MAILBOX_NOTIFICATION_SMALL = 163, + RESOURCE_ID_MOVIE_EVENT_TINY = 164, + RESOURCE_ID_MUSIC_EVENT_TINY = 165, + RESOURCE_ID_NEWS_EVENT_TINY = 166, + RESOURCE_ID_PARTLY_CLOUDY_TINY = 167, + RESOURCE_ID_PAY_BILL_TINY = 168, + RESOURCE_ID_REACHED_FITNESS_GOAL_TINY = 169, + RESOURCE_ID_RADIO_SHOW_TINY = 170, + RESOURCE_ID_SCHEDULED_EVENT_TINY = 171, + RESOURCE_ID_SOCCER_GAME_TINY = 172, + RESOURCE_ID_SOCCER_GAME_SMALL = 173, + RESOURCE_ID_STOCKS_EVENT_TINY = 174, + RESOURCE_ID_BIRTHDAY_EVENT_SMALL = 175, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_SMALL = 176, + RESOURCE_ID_TIMELINE_CALENDAR_SMALL = 177, + RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_SMALL = 178, + RESOURCE_ID_ALARM_CLOCK_SMALL = 179, + RESOURCE_ID_CAR_RENTAL_SMALL = 180, + RESOURCE_ID_CHECK_INTERNET_CONNECTION_SMALL = 181, + RESOURCE_ID_DINNER_RESERVATION_SMALL = 182, + RESOURCE_ID_DISMISSED_PHONE_CALL_SMALL = 183, + RESOURCE_ID_GENERIC_SMS_SMALL = 184, + RESOURCE_ID_GENERIC_SMS_TINY = 185, + RESOURCE_ID_GENERIC_SMS_LARGE = 186, + RESOURCE_ID_FACEBOOK_NOTIFICATION_SMALL = 187, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_SMALL = 188, + RESOURCE_ID_GENERIC_CONFIRMATION_SMALL = 189, + RESOURCE_ID_GENERIC_EMAIL_SMALL = 190, + RESOURCE_ID_GENERIC_PIN_SMALL = 191, + RESOURCE_ID_GENERIC_REMINDER_SMALL = 192, + RESOURCE_ID_GENERIC_WARNING_SMALL = 193, + RESOURCE_ID_GENERIC_WEATHER_SMALL = 194, + RESOURCE_ID_GLUCOSE_MONITOR_SMALL = 195, + RESOURCE_ID_GMAIL_NOTIFICATION_SMALL = 196, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_SMALL = 197, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_SMALL = 198, + RESOURCE_ID_HEAVY_RAIN_SMALL = 199, + RESOURCE_ID_HEAVY_SNOW_SMALL = 200, + RESOURCE_ID_HOTEL_RESERVATION_SMALL = 201, + RESOURCE_ID_LIGHT_RAIN_SMALL = 202, + RESOURCE_ID_LIGHT_SNOW_SMALL = 203, + RESOURCE_ID_MOVIE_EVENT_SMALL = 204, + RESOURCE_ID_MUSIC_EVENT_SMALL = 205, + RESOURCE_ID_NEWS_EVENT_SMALL = 206, + RESOURCE_ID_PARTLY_CLOUDY_SMALL = 207, + RESOURCE_ID_PAY_BILL_SMALL = 208, + RESOURCE_ID_RADIO_SHOW_SMALL = 209, + RESOURCE_ID_REACHED_FITNESS_GOAL_SMALL = 210, + RESOURCE_ID_SCHEDULED_EVENT_SMALL = 211, + RESOURCE_ID_SUNNY_DAY_SMALL = 212, + RESOURCE_ID_GENERIC_SPORTS_SMALL = 213, + RESOURCE_ID_TIDE_IS_HIGH_SMALL = 214, + RESOURCE_ID_WATCH_DISCONNECTED_SMALL = 215, + RESOURCE_ID_ALARM_CLOCK_LARGE_STATIC = 216, + RESOURCE_ID_AMERICAN_FOOTBALL_LARGE = 217, + RESOURCE_ID_AMERICAN_FOOTBALL_SMALL = 218, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_LARGE = 219, + RESOURCE_ID_WHATSAPP_NOTIFICATION_LARGE = 220, + RESOURCE_ID_AUDIO_CASSETTE_LARGE = 221, + RESOURCE_ID_AUDIO_CASSETTE_SMALL = 222, + RESOURCE_ID_BASEBALL_GAME_LARGE = 223, + RESOURCE_ID_BIRTHDAY_EVENT_LARGE = 224, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_LARGE = 225, + RESOURCE_ID_TIMELINE_CALENDAR_LARGE = 226, + RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_LARGE = 227, + RESOURCE_ID_CAR_RENTAL_LARGE = 228, + RESOURCE_ID_CHECK_INTERNET_CONNECTION_LARGE = 229, + RESOURCE_ID_CLOUDY_DAY_LARGE = 230, + RESOURCE_ID_CLOUDY_DAY_SMALL = 231, + RESOURCE_ID_DAY_SEPARATOR_TINY = 232, + RESOURCE_ID_DAY_SEPARATOR_SMALL = 233, + RESOURCE_ID_DAY_SEPARATOR_LARGE = 234, + RESOURCE_ID_RESULT_DELETED_TINY = 235, + RESOURCE_ID_RESULT_DELETED_SMALL = 236, + RESOURCE_ID_DINNER_RESERVATION_LARGE = 237, + RESOURCE_ID_DISMISSED_PHONE_CALL_LARGE = 238, + RESOURCE_ID_DURING_PHONE_CALL_TINY = 239, + RESOURCE_ID_DURING_PHONE_CALL_SMALL = 240, + RESOURCE_ID_DURING_PHONE_CALL_LARGE = 241, + RESOURCE_ID_DURING_PHONE_CALL_CENTERED_LARGE = 242, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_LARGE = 243, + RESOURCE_ID_FACEBOOK_NOTIFICATION_LARGE = 244, + RESOURCE_ID_GENERIC_EMAIL_LARGE = 245, + RESOURCE_ID_GENERIC_PIN_LARGE = 246, + RESOURCE_ID_GENERIC_REMINDER_LARGE = 247, + RESOURCE_ID_GENERIC_SPORTS_LARGE = 248, + RESOURCE_ID_GENERIC_WEATHER_LARGE = 249, + RESOURCE_ID_GLUCOSE_MONITOR_LARGE = 250, + RESOURCE_ID_GMAIL_NOTIFICATION_LARGE = 251, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_LARGE = 252, + RESOURCE_ID_HEAVY_RAIN_LARGE = 253, + RESOURCE_ID_HEAVY_SNOW_LARGE = 254, + RESOURCE_ID_HOTEL_RESERVATION_LARGE = 255, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_LARGE = 256, + RESOURCE_ID_LIGHT_RAIN_LARGE = 257, + RESOURCE_ID_LIGHT_SNOW_LARGE = 258, + RESOURCE_ID_MAILBOX_NOTIFICATION_LARGE = 259, + RESOURCE_ID_MISSED_CALL_LARGE = 260, + RESOURCE_ID_MOVIE_EVENT_LARGE = 261, + RESOURCE_ID_MUSIC_EVENT_LARGE = 262, + RESOURCE_ID_NEWS_EVENT_LARGE = 263, + RESOURCE_ID_PARTLY_CLOUDY_LARGE = 264, + RESOURCE_ID_PAY_BILL_LARGE = 265, + RESOURCE_ID_RADIO_SHOW_LARGE = 266, + RESOURCE_ID_REACHED_FITNESS_GOAL_LARGE = 267, + RESOURCE_ID_SCHEDULED_EVENT_LARGE = 268, + RESOURCE_ID_SCHEDULED_FLIGHT_LARGE = 269, + RESOURCE_ID_SCHEDULED_FLIGHT_SMALL = 270, + RESOURCE_ID_RESULT_SENT_TINY = 271, + RESOURCE_ID_RESULT_SENT_SMALL = 272, + RESOURCE_ID_SOCCER_GAME_LARGE = 273, + RESOURCE_ID_STOCKS_EVENT_LARGE = 274, + RESOURCE_ID_STOCKS_EVENT_SMALL = 275, + RESOURCE_ID_SUNNY_DAY_LARGE = 276, + RESOURCE_ID_TELEGRAM_APP_LARGE = 277, + RESOURCE_ID_TELEGRAM_APP_SMALL = 278, + RESOURCE_ID_TIDE_IS_HIGH_LARGE = 279, + RESOURCE_ID_TWITTER_NOTIFICATION_LARGE = 280, + RESOURCE_ID_WATCH_DISCONNECTED_LARGE = 281, + RESOURCE_ID_RESULT_FAILED_TINY = 282, + RESOURCE_ID_RESULT_FAILED_SMALL = 283, + RESOURCE_ID_RESULT_FAILED_LARGE = 284, + RESOURCE_ID_GENERIC_QUESTION_TINY = 285, + RESOURCE_ID_GENERIC_QUESTION_SMALL = 286, + RESOURCE_ID_GENERIC_QUESTION_LARGE = 287, + RESOURCE_ID_RAINING_AND_SNOWING_TINY = 288, + RESOURCE_ID_RAINING_AND_SNOWING_SMALL = 289, + RESOURCE_ID_RAINING_AND_SNOWING_LARGE = 290, + RESOURCE_ID_NOTIFICATION_FACETIME_TINY = 291, + RESOURCE_ID_NOTIFICATION_FACETIME_SMALL = 292, + RESOURCE_ID_NOTIFICATION_FACETIME_LARGE = 293, + RESOURCE_ID_NOTIFICATION_LINE_TINY = 294, + RESOURCE_ID_NOTIFICATION_LINE_SMALL = 295, + RESOURCE_ID_NOTIFICATION_LINE_LARGE = 296, + RESOURCE_ID_NOTIFICATION_SKYPE_TINY = 297, + RESOURCE_ID_NOTIFICATION_SKYPE_SMALL = 298, + RESOURCE_ID_NOTIFICATION_SKYPE_LARGE = 299, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_TINY = 300, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_SMALL = 301, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_LARGE = 302, + RESOURCE_ID_NOTIFICATION_VIBER_TINY = 303, + RESOURCE_ID_NOTIFICATION_VIBER_SMALL = 304, + RESOURCE_ID_NOTIFICATION_VIBER_LARGE = 305, + RESOURCE_ID_NOTIFICATION_WECHAT_TINY = 306, + RESOURCE_ID_NOTIFICATION_WECHAT_SMALL = 307, + RESOURCE_ID_NOTIFICATION_WECHAT_LARGE = 308, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_TINY = 309, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_SMALL = 310, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_LARGE = 311, + RESOURCE_ID_TV_SHOW_TINY = 312, + RESOURCE_ID_TV_SHOW_SMALL = 313, + RESOURCE_ID_TV_SHOW_LARGE = 314, + RESOURCE_ID_END_OF_TIMELINE = 315, + RESOURCE_ID_BASKETBALL_TINY = 316, + RESOURCE_ID_BASKETBALL_SMALL = 317, + RESOURCE_ID_BASKETBALL_LARGE = 318, + RESOURCE_ID_RESULT_DISMISSED_TINY = 319, + RESOURCE_ID_RESULT_DISMISSED_SMALL = 320, + RESOURCE_ID_TIDE_IS_HIGH_TINY = 321, + RESOURCE_ID_OUTGOING_CALL_LARGE = 322, + RESOURCE_ID_NOTIFICATION_HIPCHAT_TINY = 323, + RESOURCE_ID_NOTIFICATION_HIPCHAT_SMALL = 324, + RESOURCE_ID_NOTIFICATION_HIPCHAT_LARGE = 325, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_TINY = 326, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_SMALL = 327, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_LARGE = 328, + RESOURCE_ID_INCOMING_PHONE_CALL_TINY = 329, + RESOURCE_ID_INCOMING_PHONE_CALL_SMALL = 330, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_TINY = 331, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_SMALL = 332, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_LARGE = 333, + RESOURCE_ID_NOTIFICATION_KIK_TINY = 334, + RESOURCE_ID_NOTIFICATION_KIK_SMALL = 335, + RESOURCE_ID_NOTIFICATION_KIK_LARGE = 336, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_TINY = 337, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_SMALL = 338, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_LARGE = 339, + RESOURCE_ID_LOCATION_TINY = 340, + RESOURCE_ID_LOCATION_SMALL = 341, + RESOURCE_ID_LOCATION_LARGE = 342, + RESOURCE_ID_SUNSET_TINY = 343, + RESOURCE_ID_SUNSET_SMALL = 344, + RESOURCE_ID_SUNSET_LARGE = 345, + RESOURCE_ID_SUNRISE_TINY = 346, + RESOURCE_ID_SUNRISE_SMALL = 347, + RESOURCE_ID_SUNRISE_LARGE = 348, + RESOURCE_ID_SETTINGS_TINY = 349, + RESOURCE_ID_SETTINGS_SMALL = 350, + RESOURCE_ID_SETTINGS_LARGE = 351, + RESOURCE_ID_PLUS_ICON_BLACK = 352, + RESOURCE_ID_PLUS_ICON_DOTTED = 353, + RESOURCE_ID_RESULT_SHREDDED_TINY = 354, + RESOURCE_ID_RESULT_SHREDDED_SMALL = 355, + RESOURCE_ID_QUIET_TIME_MOUSE = 356, + RESOURCE_ID_REMINDER_SNOOZE = 357, + RESOURCE_ID_QUIET_TIME_STATUS_BAR = 358, + RESOURCE_ID_QUICK_DISMISS = 359, + RESOURCE_ID_THUMBS_UP_SMALL = 360, + RESOURCE_ID_THUMBS_UP_LARGE = 361, + RESOURCE_ID_ARROW_UP_SMALL = 362, + RESOURCE_ID_ARROW_DOWN_SMALL = 363, + RESOURCE_ID_ACTIVITY_TINY = 364, + RESOURCE_ID_ACTIVITY_SMALL = 365, + RESOURCE_ID_ACTIVITY_LARGE = 366, + RESOURCE_ID_SLEEP_TINY = 367, + RESOURCE_ID_SLEEP_SMALL = 368, + RESOURCE_ID_SLEEP_LARGE = 369, + RESOURCE_ID_REWARD_AVERAGE_LARGE = 370, + RESOURCE_ID_REWARD_GOOD_LARGE = 371, + RESOURCE_ID_REWARD_BAD_LARGE = 372, + RESOURCE_ID_CALORIES_TINY = 373, + RESOURCE_ID_DISTANCE_TINY = 374, + RESOURCE_ID_DURATION_TINY = 375, + RESOURCE_ID_PACE_TINY = 376, + RESOURCE_ID_RUN_TINY = 377, + RESOURCE_ID_RUN_LARGE = 378, + RESOURCE_ID_MOON_TINY = 379, + RESOURCE_ID_NOTIFICATION_AMAZON_TINY = 380, + RESOURCE_ID_NOTIFICATION_AMAZON_SMALL = 381, + RESOURCE_ID_NOTIFICATION_AMAZON_LARGE = 382, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_TINY = 383, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_SMALL = 384, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_LARGE = 385, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_TINY = 386, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_SMALL = 387, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_LARGE = 388, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_TINY = 389, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_SMALL = 390, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_LARGE = 391, + RESOURCE_ID_NOTIFICATION_LINKEDIN_TINY = 392, + RESOURCE_ID_NOTIFICATION_LINKEDIN_SMALL = 393, + RESOURCE_ID_NOTIFICATION_LINKEDIN_LARGE = 394, + RESOURCE_ID_NOTIFICATION_SLACK_TINY = 395, + RESOURCE_ID_NOTIFICATION_SLACK_SMALL = 396, + RESOURCE_ID_NOTIFICATION_SLACK_LARGE = 397, + RESOURCE_ID_NOTIFICATION_AIRMAIL_TINY = 398, + RESOURCE_ID_NOTIFICATION_AIRMAIL_SMALL = 399, + RESOURCE_ID_NOTIFICATION_AIRMAIL_LARGE = 400, + RESOURCE_ID_NOTIFICATION_BEEPER_TINY = 401, + RESOURCE_ID_NOTIFICATION_BEEPER_SMALL = 402, + RESOURCE_ID_NOTIFICATION_BEEPER_LARGE = 403, + RESOURCE_ID_NOTIFICATION_BLUESKY_TINY = 404, + RESOURCE_ID_NOTIFICATION_BLUESKY_SMALL = 405, + RESOURCE_ID_NOTIFICATION_BLUESKY_LARGE = 406, + RESOURCE_ID_NOTIFICATION_DISCORD_TINY = 407, + RESOURCE_ID_NOTIFICATION_DISCORD_SMALL = 408, + RESOURCE_ID_NOTIFICATION_DISCORD_LARGE = 409, + RESOURCE_ID_NOTIFICATION_DUOLINGO_TINY = 410, + RESOURCE_ID_NOTIFICATION_DUOLINGO_SMALL = 411, + RESOURCE_ID_NOTIFICATION_DUOLINGO_LARGE = 412, + RESOURCE_ID_NOTIFICATION_EBAY_TINY = 413, + RESOURCE_ID_NOTIFICATION_EBAY_SMALL = 414, + RESOURCE_ID_NOTIFICATION_EBAY_LARGE = 415, + RESOURCE_ID_NOTIFICATION_ELEMENT_TINY = 416, + RESOURCE_ID_NOTIFICATION_ELEMENT_SMALL = 417, + RESOURCE_ID_NOTIFICATION_ELEMENT_LARGE = 418, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_TINY = 419, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_SMALL = 420, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_LARGE = 421, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_TINY = 422, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_SMALL = 423, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_LARGE = 424, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_TINY = 425, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_SMALL = 426, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_LARGE = 427, + RESOURCE_ID_NOTIFICATION_OUTLOOK_TINY = 428, + RESOURCE_ID_NOTIFICATION_OUTLOOK_SMALL = 429, + RESOURCE_ID_NOTIFICATION_OUTLOOK_LARGE = 430, + RESOURCE_ID_NOTIFICATION_SIGNAL_TINY = 431, + RESOURCE_ID_NOTIFICATION_SIGNAL_SMALL = 432, + RESOURCE_ID_NOTIFICATION_SIGNAL_LARGE = 433, + RESOURCE_ID_NOTIFICATION_STEAM_TINY = 434, + RESOURCE_ID_NOTIFICATION_STEAM_SMALL = 435, + RESOURCE_ID_NOTIFICATION_STEAM_LARGE = 436, + RESOURCE_ID_NOTIFICATION_TEAMS_TINY = 437, + RESOURCE_ID_NOTIFICATION_TEAMS_SMALL = 438, + RESOURCE_ID_NOTIFICATION_TEAMS_LARGE = 439, + RESOURCE_ID_NOTIFICATION_THREADS_TINY = 440, + RESOURCE_ID_NOTIFICATION_THREADS_SMALL = 441, + RESOURCE_ID_NOTIFICATION_THREADS_LARGE = 442, + RESOURCE_ID_NOTIFICATION_TWITCH_TINY = 443, + RESOURCE_ID_NOTIFICATION_TWITCH_SMALL = 444, + RESOURCE_ID_NOTIFICATION_TWITCH_LARGE = 445, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_TINY = 446, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_SMALL = 447, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_LARGE = 448, + RESOURCE_ID_NOTIFICATION_YOUTUBE_TINY = 449, + RESOURCE_ID_NOTIFICATION_YOUTUBE_SMALL = 450, + RESOURCE_ID_NOTIFICATION_YOUTUBE_LARGE = 451, + RESOURCE_ID_NOTIFICATION_ZOOM_TINY = 452, + RESOURCE_ID_NOTIFICATION_ZOOM_SMALL = 453, + RESOURCE_ID_NOTIFICATION_ZOOM_LARGE = 454, + RESOURCE_ID_NOTIFICATION_REDDIT_TINY = 455, + RESOURCE_ID_NOTIFICATION_REDDIT_SMALL = 456, + RESOURCE_ID_NOTIFICATION_REDDIT_LARGE = 457, + RESOURCE_ID_NOTIFICATION_SWARM_TINY = 458, + RESOURCE_ID_NOTIFICATION_SWARM_SMALL = 459, + RESOURCE_ID_NOTIFICATION_SWARM_LARGE = 460, + RESOURCE_ID_NOTIFICATION_TAPO_TINY = 461, + RESOURCE_ID_NOTIFICATION_TAPO_SMALL = 462, + RESOURCE_ID_NOTIFICATION_TAPO_LARGE = 463, + RESOURCE_ID_STRINGS_CA_ES = 464, + RESOURCE_ID_STRINGS_DE_DE = 465, + RESOURCE_ID_STRINGS_ES_ES = 466, + RESOURCE_ID_STRINGS_FR_FR = 467, + RESOURCE_ID_STRINGS_IT_IT = 468, + RESOURCE_ID_STRINGS_NL_NL = 469, + RESOURCE_ID_STRINGS_PT_PT = 470, + RESOURCE_ID_SMART_ALARM_TINY = 471, + RESOURCE_ID_HEALTH_APP_ACTIVITY = 472, + RESOURCE_ID_HEALTH_APP_SLEEP = 473, + RESOURCE_ID_HEALTH_APP_CROWN = 474, + RESOURCE_ID_HEALTH_APP_HR = 475, + RESOURCE_ID_HEALTH_APP_PULSING_HEART = 476, + RESOURCE_ID_WORKOUT_APP_WALK = 477, + RESOURCE_ID_WORKOUT_APP_WALK_SMALL = 478, + RESOURCE_ID_WORKOUT_APP_WALK_TINY = 479, + RESOURCE_ID_WORKOUT_APP_RUN = 480, + RESOURCE_ID_WORKOUT_APP_RUN_SMALL = 481, + RESOURCE_ID_WORKOUT_APP_RUN_TINY = 482, + RESOURCE_ID_WORKOUT_APP_WORKOUT = 483, + RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL = 484, + RESOURCE_ID_WORKOUT_APP_DETECTED = 485, + RESOURCE_ID_WORKOUT_APP_HEART = 486, + RESOURCE_ID_WORKOUT_APP_MEASURING_HR = 487, + RESOURCE_ID_WORKOUT_APP_HR_PULSE_TINY = 488, + RESOURCE_ID_WORKOUT_APP_END = 489, + RESOURCE_ID_WORKOUT_APP_ONE = 490, + RESOURCE_ID_WORKOUT_APP_TWO = 491, + RESOURCE_ID_WORKOUT_APP_THREE = 492, + RESOURCE_ID_HEART_TINY = 493, + RESOURCE_ID_HEART_SMALL = 494, + RESOURCE_ID_HEART_LARGE = 495, + RESOURCE_ID_BACKLIGHT = 496, + RESOURCE_ID_AIRPLANE = 497, + RESOURCE_ID_NO_EVENTS_LARGE = 498, + RESOURCE_ID_ALARM_CLOCK_LARGE = 499, + RESOURCE_ID_RESULT_DELETED_LARGE = 500, + RESOURCE_ID_RESULT_SHREDDED_LARGE = 501, + RESOURCE_ID_RESULT_DISMISSED_LARGE = 502, + RESOURCE_ID_INCOMING_PHONE_CALL_LARGE = 503, + RESOURCE_ID_RESULT_MUTE_LARGE = 504, + RESOURCE_ID_RESULT_SENT_LARGE = 505, + RESOURCE_ID_RESULT_UNMUTE_LARGE = 506, + RESOURCE_ID_BATTERY_CHARGING_ICON = 507, + RESOURCE_ID_HEALTH_ICON_MOON = 508, + RESOURCE_ID_HEALTH_ICON_ROTATED_MOON = 509, + RESOURCE_ID_SYSTEM_FCC_MARK = 510, + RESOURCE_ID_SYSTEM_KCC_MARK = 511, + RESOURCE_ID_SYSTEM_CE_MARK = 512, + RESOURCE_ID_SYSTEM_WEEE_MARK = 513, + RESOURCE_ID_SYSTEM_UKCA_MARK = 514, + RESOURCE_ID_SYSTEM_R_MARK = 515, + RESOURCE_ID_SYSTEM_T_MARK = 516, + RESOURCE_ID_SYSTEM_AUS_RCM_MARK = 517, + RESOURCE_ID_SYSTEM_NOM_NYCE_MARK = 518, + RESOURCE_ID_STRIDE_SHOE = 519, + RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE = 520, + RESOURCE_ID_EMOJI_BIG_SMILE_LARGE = 521, + RESOURCE_ID_EMOJI_HEART_LARGE = 522, + RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE = 523, + RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE = 524, + RESOURCE_ID_EMOJI_SAD_LARGE = 525, + RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE = 526, + RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE = 527, + RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE = 528, + RESOURCE_ID_EMOJI_THUMBS_UP_LARGE = 529, + RESOURCE_ID_EMOJI_WINK_LARGE = 530, + RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE = 531, + RESOURCE_ID_VIBE_SCORE_REVEILLE = 532, + RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK = 533, + RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE = 534, + RESOURCE_ID_VIBE_SCORE_PULSE = 535, + RESOURCE_ID_VIBE_SCORE_JACKHAMMER = 536, + RESOURCE_ID_VIBE_SCORE_MARIO = 537, + RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW = 538, + RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH = 539, + RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW = 540, + RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH = 541, + RESOURCE_ID_VIBE_SCORE_ALARM_LPM = 542, + RESOURCE_ID_VIBE_SCORE_GENTLE = 543, + RESOURCE_ID_SMART_ALARM_ICON_BLACK = 544, + RESOURCE_ID_MENU_ICON_HEALTH = 545, + RESOURCE_ID_STORED_APP_GOLF = 546, + RESOURCE_ID_TIMEZONE_DATABASE = 547, + RESOURCE_ID_FONT_FALLBACK_INTERNAL = 548, + RESOURCE_ID_ARROW_DOWN = 549, + RESOURCE_ID_VOICE_MICROPHONE_LARGE = 550, + RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE = 551, + RESOURCE_ID_GENERIC_CONFIRMATION_LARGE = 552, + RESOURCE_ID_PUG = 553, + RESOURCE_ID_STRINGS = 554, + RESOURCE_ID_GOTHIC_14_EXTENDED = 555, + RESOURCE_ID_GOTHIC_14_BOLD_EXTENDED = 556, + RESOURCE_ID_GOTHIC_18_EXTENDED = 557, + RESOURCE_ID_GOTHIC_18_BOLD_EXTENDED = 558, + RESOURCE_ID_GOTHIC_24_EXTENDED = 559, + RESOURCE_ID_GOTHIC_24_BOLD_EXTENDED = 560, + RESOURCE_ID_GOTHIC_28_EXTENDED = 561, + RESOURCE_ID_GOTHIC_28_BOLD_EXTENDED = 562, + RESOURCE_ID_BITHAM_18_LIGHT_SUBSET_EXTENDED = 563, + RESOURCE_ID_BITHAM_30_BLACK_EXTENDED = 564, + RESOURCE_ID_BITHAM_34_LIGHT_SUBSET_EXTENDED = 565, + RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS_EXTENDED = 566, + RESOURCE_ID_BITHAM_42_BOLD_EXTENDED = 567, + RESOURCE_ID_BITHAM_42_LIGHT_EXTENDED = 568, + RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS_EXTENDED = 569, + RESOURCE_ID_ROBOTO_CONDENSED_21_EXTENDED = 570, + RESOURCE_ID_ROBOTO_BOLD_SUBSET_49_EXTENDED = 571, + RESOURCE_ID_DROID_SERIF_28_BOLD_EXTENDED = 572, + RESOURCE_ID_GOTHIC_09_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_14_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_18_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_24_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_28_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_36_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_36_BOLD_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_20_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_32_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_36_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_38_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_42_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_28_LIGHT_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_60_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_60_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_FONT_FALLBACK_INTERNAL_EXTENDED = INVALID_RESOURCE, +} ResourceId; \ No newline at end of file diff --git a/tests/overrides/default/resources/asterix/resource/resource_version.auto.h b/tests/overrides/default/resources/asterix/resource/resource_version.auto.h new file mode 100644 index 0000000000..c78d232816 --- /dev/null +++ b/tests/overrides/default/resources/asterix/resource/resource_version.auto.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED +// DO NOT MODIFY +// + +static const ResourceVersion SYSTEM_RESOURCE_VERSION = { + .crc = 3275651253, + .timestamp = 0 +}; diff --git a/tests/overrides/default/resources/asterix/resource/timeline_resource_ids.auto.h b/tests/overrides/default/resources/asterix/resource/timeline_resource_ids.auto.h new file mode 100644 index 0000000000..75ed14eb6c --- /dev/null +++ b/tests/overrides/default/resources/asterix/resource/timeline_resource_ids.auto.h @@ -0,0 +1,147 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + + +#pragma once + +// +// AUTOGENERATED +// DO NOT MODIFY +// + +typedef enum { + TIMELINE_RESOURCE_INVALID = 0, + TIMELINE_RESOURCE_NOTIFICATION_GENERIC = 0x80000001, + TIMELINE_RESOURCE_TIMELINE_MISSED_CALL = 0x80000002, + TIMELINE_RESOURCE_NOTIFICATION_REMINDER = 0x80000003, + TIMELINE_RESOURCE_NOTIFICATION_FLAG = 0x80000004, + TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP = 0x80000005, + TIMELINE_RESOURCE_NOTIFICATION_TWITTER = 0x80000006, + TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM = 0x80000007, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS = 0x80000008, + TIMELINE_RESOURCE_NOTIFICATION_GMAIL = 0x80000009, + TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER = 0x8000000a, + TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK = 0x8000000b, + TIMELINE_RESOURCE_AUDIO_CASSETTE = 0x8000000c, + TIMELINE_RESOURCE_ALARM_CLOCK = 0x8000000d, + TIMELINE_RESOURCE_TIMELINE_WEATHER = 0x8000000e, + TIMELINE_RESOURCE_TIMELINE_SUN = 0x80000010, + TIMELINE_RESOURCE_TIMELINE_SPORTS = 0x80000011, + TIMELINE_RESOURCE_GENERIC_EMAIL = 0x80000013, + TIMELINE_RESOURCE_AMERICAN_FOOTBALL = 0x80000014, + TIMELINE_RESOURCE_TIMELINE_CALENDAR = 0x80000015, + TIMELINE_RESOURCE_TIMELINE_BASEBALL = 0x80000016, + TIMELINE_RESOURCE_BIRTHDAY_EVENT = 0x80000017, + TIMELINE_RESOURCE_CAR_RENTAL = 0x80000018, + TIMELINE_RESOURCE_CLOUDY_DAY = 0x80000019, + TIMELINE_RESOURCE_CRICKET_GAME = 0x8000001a, + TIMELINE_RESOURCE_DINNER_RESERVATION = 0x8000001b, + TIMELINE_RESOURCE_GENERIC_WARNING = 0x8000001c, + TIMELINE_RESOURCE_GLUCOSE_MONITOR = 0x8000001d, + TIMELINE_RESOURCE_HOCKEY_GAME = 0x8000001e, + TIMELINE_RESOURCE_HOTEL_RESERVATION = 0x8000001f, + TIMELINE_RESOURCE_LIGHT_RAIN = 0x80000020, + TIMELINE_RESOURCE_LIGHT_SNOW = 0x80000021, + TIMELINE_RESOURCE_MOVIE_EVENT = 0x80000022, + TIMELINE_RESOURCE_MUSIC_EVENT = 0x80000023, + TIMELINE_RESOURCE_NEWS_EVENT = 0x80000024, + TIMELINE_RESOURCE_PARTLY_CLOUDY = 0x80000025, + TIMELINE_RESOURCE_PAY_BILL = 0x80000026, + TIMELINE_RESOURCE_RADIO_SHOW = 0x80000027, + TIMELINE_RESOURCE_SCHEDULED_EVENT = 0x80000028, + TIMELINE_RESOURCE_SOCCER_GAME = 0x80000029, + TIMELINE_RESOURCE_STOCKS_EVENT = 0x8000002a, + TIMELINE_RESOURCE_RESULT_DELETED = 0x8000002b, + TIMELINE_RESOURCE_CHECK_INTERNET_CONNECTION = 0x8000002c, + TIMELINE_RESOURCE_GENERIC_SMS = 0x8000002d, + TIMELINE_RESOURCE_RESULT_MUTE = 0x8000002e, + TIMELINE_RESOURCE_RESULT_SENT = 0x8000002f, + TIMELINE_RESOURCE_WATCH_DISCONNECTED = 0x80000030, + TIMELINE_RESOURCE_DURING_PHONE_CALL = 0x80000031, + TIMELINE_RESOURCE_TIDE_IS_HIGH = 0x80000032, + TIMELINE_RESOURCE_RESULT_DISMISSED = 0x80000033, + TIMELINE_RESOURCE_HEAVY_RAIN = 0x80000034, + TIMELINE_RESOURCE_HEAVY_SNOW = 0x80000035, + TIMELINE_RESOURCE_SCHEDULED_FLIGHT = 0x80000036, + TIMELINE_RESOURCE_GENERIC_CONFIRMATION = 0x80000037, + TIMELINE_RESOURCE_DAY_SEPARATOR = 0x80000038, + TIMELINE_RESOURCE_NO_EVENTS = 0x80000039, + TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER = 0x8000003a, + TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM = 0x8000003b, + TIMELINE_RESOURCE_NOTIFICATION_MAILBOX = 0x8000003c, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX = 0x8000003d, + TIMELINE_RESOURCE_RESULT_FAILED = 0x8000003e, + TIMELINE_RESOURCE_GENERIC_QUESTION = 0x8000003f, + TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK = 0x80000040, + TIMELINE_RESOURCE_RAINING_AND_SNOWING = 0x80000041, + TIMELINE_RESOURCE_REACHED_FITNESS_GOAL = 0x80000042, + TIMELINE_RESOURCE_NOTIFICATION_LINE = 0x80000043, + TIMELINE_RESOURCE_NOTIFICATION_SKYPE = 0x80000044, + TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT = 0x80000045, + TIMELINE_RESOURCE_NOTIFICATION_VIBER = 0x80000046, + TIMELINE_RESOURCE_NOTIFICATION_WECHAT = 0x80000047, + TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL = 0x80000048, + TIMELINE_RESOURCE_TV_SHOW = 0x80000049, + TIMELINE_RESOURCE_BASKETBALL = 0x8000004a, + TIMELINE_RESOURCE_DISMISSED_PHONE_CALL = 0x8000004b, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MESSENGER = 0x8000004c, + TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT = 0x8000004d, + TIMELINE_RESOURCE_INCOMING_PHONE_CALL = 0x8000004e, + TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK = 0x8000004f, + TIMELINE_RESOURCE_NOTIFICATION_KIK = 0x80000050, + TIMELINE_RESOURCE_NOTIFICATION_LIGHTHOUSE = 0x80000051, + TIMELINE_RESOURCE_LOCATION = 0x80000052, + TIMELINE_RESOURCE_SETTINGS = 0x80000053, + TIMELINE_RESOURCE_SUNRISE = 0x80000054, + TIMELINE_RESOURCE_SUNSET = 0x80000055, + TIMELINE_RESOURCE_RESULT_UNMUTE = 0x80000056, + TIMELINE_RESOURCE_RESULT_UNMUTE_ALT = 0x8000005e, + TIMELINE_RESOURCE_DURING_PHONE_CALL_CENTERED = 0x8000005f, + TIMELINE_RESOURCE_TIMELINE_EMPTY_CALENDAR = 0x80000060, + TIMELINE_RESOURCE_THUMBS_UP = 0x80000061, + TIMELINE_RESOURCE_ARROW_UP = 0x80000062, + TIMELINE_RESOURCE_ARROW_DOWN = 0x80000063, + TIMELINE_RESOURCE_ACTIVITY = 0x80000064, + TIMELINE_RESOURCE_SLEEP = 0x80000065, + TIMELINE_RESOURCE_REWARD_BAD = 0x80000066, + TIMELINE_RESOURCE_REWARD_GOOD = 0x80000067, + TIMELINE_RESOURCE_REWARD_AVERAGE = 0x80000068, + TIMELINE_RESOURCE_CALORIES = 0x80000069, + TIMELINE_RESOURCE_DISTANCE = 0x8000006a, + TIMELINE_RESOURCE_DURATION = 0x8000006b, + TIMELINE_RESOURCE_PACE = 0x8000006c, + TIMELINE_RESOURCE_RUN = 0x8000006d, + TIMELINE_RESOURCE_NOTIFICATION_FACETIME = 0x8000006e, + TIMELINE_RESOURCE_NOTIFICATION_AMAZON = 0x8000006f, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS = 0x80000070, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS = 0x80000071, + TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS = 0x80000072, + TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN = 0x80000073, + TIMELINE_RESOURCE_NOTIFICATION_SLACK = 0x80000074, + TIMELINE_RESOURCE_SMART_ALARM = 0x80000075, + TIMELINE_RESOURCE_HEART = 0x80000076, + TIMELINE_RESOURCE_BLE_HRM_SHARING = 0x80000077, + TIMELINE_RESOURCE_NOTIFICATION_YOUTUBE = 0x80000078, + TIMELINE_RESOURCE_NOTIFICATION_BEEPER = 0x80000079, + TIMELINE_RESOURCE_NOTIFICATION_BLUESKY = 0x8000007a, + TIMELINE_RESOURCE_NOTIFICATION_DISCORD = 0x8000007b, + TIMELINE_RESOURCE_NOTIFICATION_DUOLINGO = 0x8000007c, + TIMELINE_RESOURCE_NOTIFICATION_ELEMENT = 0x8000007d, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_CHAT = 0x8000007e, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_TASKS = 0x8000007f, + TIMELINE_RESOURCE_NOTIFICATION_HOME_ASSISTANT = 0x80000080, + TIMELINE_RESOURCE_NOTIFICATION_STEAM = 0x80000081, + TIMELINE_RESOURCE_NOTIFICATION_TEAMS = 0x80000082, + TIMELINE_RESOURCE_NOTIFICATION_THREADS = 0x80000083, + TIMELINE_RESOURCE_NOTIFICATION_UNIFI_PROTECT = 0x80000084, + TIMELINE_RESOURCE_NOTIFICATION_ZOOM = 0x80000085, + TIMELINE_RESOURCE_NOTIFICATION_EBAY = 0x80000086, + TIMELINE_RESOURCE_NOTIFICATION_SIGNAL = 0x80000087, + TIMELINE_RESOURCE_NOTIFICATION_TWITCH = 0x80000088, + TIMELINE_RESOURCE_NOTIFICATION_AIRMAIL = 0x80000089, + TIMELINE_RESOURCE_NOTIFICATION_REDDIT = 0x8000008a, + TIMELINE_RESOURCE_NOTIFICATION_SWARM = 0x8000008b, + TIMELINE_RESOURCE_NOTIFICATION_TAPO = 0x8000008c, +} TimelineResourceId; + +#define NUM_TIMELINE_RESOURCES 120 diff --git a/tests/overrides/default/resources/gabbro/resource/resource_ids.auto.h b/tests/overrides/default/resources/gabbro/resource/resource_ids.auto.h new file mode 100644 index 0000000000..ed2a06fce8 --- /dev/null +++ b/tests/overrides/default/resources/gabbro/resource/resource_ids.auto.h @@ -0,0 +1,626 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED BY BUILD +// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN +// + +typedef enum { + INVALID_RESOURCE = 0, + RESOURCE_ID_INVALID = 0, + DEFAULT_MENU_ICON = 0, + RESOURCE_ID_ACTION_BAR_ICON_X = 1, + RESOURCE_ID_BT_PAIR_SUCCESS = 2, + RESOURCE_ID_BT_PAIR_FAILURE = 3, + RESOURCE_ID_BT_PAIR_APPROVE_ON_PHONE = 4, + RESOURCE_ID_BT_PAIR_CONFIRMATION = 5, + RESOURCE_ID_GOTHIC_18_BOLD = 6, + RESOURCE_ID_BATTERY_ICON_LOW_LARGE = 7, + RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE = 8, + RESOURCE_ID_BATTERY_ICON_FULL_LARGE = 9, + RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED = 10, + RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED = 11, + RESOURCE_ID_BATTERY_ICON_CHARGE = 12, + RESOURCE_ID_BATTERY_NEEDS_CHARGING = 13, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE = 14, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED = 15, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED = 16, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND = 17, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY = 18, + RESOURCE_ID_QUIET_TIME = 19, + RESOURCE_ID_MUSIC_APP_GLANCE_PLAY = 20, + RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE = 21, + RESOURCE_ID_NOTIFICATIONS_APP_GLANCE = 22, + RESOURCE_ID_SEND_TEXT_APP_GLANCE = 23, + RESOURCE_ID_WATCHFACES_APP_GLANCE = 24, + RESOURCE_ID_MENU_ICON_TICTOC_WATCH = 25, + RESOURCE_ID_MENU_ICON_KICKSTART_WATCH = 26, + RESOURCE_ID_SETTINGS_MENU_ICON_BLUETOOTH = 27, + RESOURCE_ID_SETTINGS_MENU_ICON_NOTIFICATIONS = 28, + RESOURCE_ID_SETTINGS_MENU_ICON_VIBRATIONS = 29, + RESOURCE_ID_SETTINGS_MENU_ICON_QUIET_TIME = 30, + RESOURCE_ID_SETTINGS_MENU_ICON_TIMELINE = 31, + RESOURCE_ID_SETTINGS_MENU_ICON_QUICK_LAUNCH = 32, + RESOURCE_ID_SETTINGS_MENU_ICON_DATE_TIME = 33, + RESOURCE_ID_SETTINGS_MENU_ICON_DISPLAY = 34, + RESOURCE_ID_SETTINGS_MENU_ICON_HEALTH = 35, + RESOURCE_ID_SETTINGS_MENU_ICON_THEMES = 36, + RESOURCE_ID_SETTINGS_MENU_ICON_BACKGROUND_APP = 37, + RESOURCE_ID_SETTINGS_MENU_ICON_SYSTEM = 38, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT = 39, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH = 40, + RESOURCE_ID_SETTINGS_ICON_AIRPLANE = 41, + RESOURCE_ID_ACTION_BAR_ICON_SMS = 42, + RESOURCE_ID_GOTHIC_09 = 43, + RESOURCE_ID_GOTHIC_14 = 44, + RESOURCE_ID_GOTHIC_14_EMOJI = 45, + RESOURCE_ID_GOTHIC_14_BOLD = 46, + RESOURCE_ID_GOTHIC_18 = 47, + RESOURCE_ID_GOTHIC_18_EMOJI = 48, + RESOURCE_ID_GOTHIC_24 = 49, + RESOURCE_ID_GOTHIC_24_BOLD = 50, + RESOURCE_ID_GOTHIC_24_EMOJI = 51, + RESOURCE_ID_GOTHIC_28 = 52, + RESOURCE_ID_GOTHIC_28_BOLD = 53, + RESOURCE_ID_GOTHIC_28_EMOJI = 54, + RESOURCE_ID_GOTHIC_36 = 55, + RESOURCE_ID_GOTHIC_36_BOLD = 56, + RESOURCE_ID_BITHAM_30_BLACK = 57, + RESOURCE_ID_BITHAM_42_BOLD = 58, + RESOURCE_ID_BITHAM_42_LIGHT = 59, + RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS = 60, + RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS = 61, + RESOURCE_ID_BITHAM_34_LIGHT_SUBSET = 62, + RESOURCE_ID_BITHAM_18_LIGHT_SUBSET = 63, + RESOURCE_ID_ROBOTO_CONDENSED_21 = 64, + RESOURCE_ID_ROBOTO_BOLD_SUBSET_49 = 65, + RESOURCE_ID_DROID_SERIF_28_BOLD = 66, + RESOURCE_ID_LECO_20_BOLD_NUMBERS = 67, + RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM = 68, + RESOURCE_ID_LECO_32_BOLD_NUMBERS = 69, + RESOURCE_ID_LECO_36_BOLD_NUMBERS = 70, + RESOURCE_ID_LECO_38_BOLD_NUMBERS = 71, + RESOURCE_ID_LECO_42_NUMBERS = 72, + RESOURCE_ID_LECO_28_LIGHT_NUMBERS = 73, + RESOURCE_ID_LECO_60_NUMBERS_AM_PM = 74, + RESOURCE_ID_LECO_60_BOLD_NUMBERS_AM_PM = 75, + RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD = 76, + RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD = 77, + RESOURCE_ID_MUSIC_ICON_ELLIPSIS = 78, + RESOURCE_ID_MUSIC_ICON_PAUSE = 79, + RESOURCE_ID_MUSIC_ICON_PLAY = 80, + RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE = 81, + RESOURCE_ID_MUSIC_ICON_VOLUME_UP = 82, + RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN = 83, + RESOURCE_ID_MUSIC_LARGE_CASSETTE = 84, + RESOURCE_ID_MUSIC_LARGE_VOLUME_UP = 85, + RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN = 86, + RESOURCE_ID_MUSIC_LARGE_PAUSED = 87, + RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC = 88, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHFACE_ICON = 89, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON = 90, + RESOURCE_ID_UNCHECKED_RADIO_BUTTON = 91, + RESOURCE_ID_CHECKED_RADIO_BUTTON = 92, + RESOURCE_ID_ACTION_BAR_ICON_MORE = 93, + RESOURCE_ID_ACTION_BAR_ICON_SNOOZE = 94, + RESOURCE_ID_ACTION_BAR_ICON_PAUSE = 95, + RESOURCE_ID_ACTION_BAR_ICON_START = 96, + RESOURCE_ID_ACTION_BAR_ICON_STOP = 97, + RESOURCE_ID_ACTION_BAR_ICON_TOGGLE = 98, + RESOURCE_ID_ACTION_MENU_FADE_TOP = 99, + RESOURCE_ID_ACTION_MENU_FADE_BOTTOM = 100, + RESOURCE_ID_CHECKBOX_ICON_CHECKED = 101, + RESOURCE_ID_CHECKBOX_ICON_UNCHECKED = 102, + RESOURCE_ID_CHECKMARK_ICON_BLACK = 103, + RESOURCE_ID_CHECKMARK_ICON_DOTTED = 104, + RESOURCE_ID_BLE_HRM_SHARING_TINY = 105, + RESOURCE_ID_BLE_HRM_SHARING_SMALL = 106, + RESOURCE_ID_BLE_HRM_SHARING_LARGE = 107, + RESOURCE_ID_NOTIFICATION_GENERIC_TINY = 108, + RESOURCE_ID_NOTIFICATION_GENERIC_SMALL = 109, + RESOURCE_ID_NOTIFICATION_GENERIC_LARGE = 110, + RESOURCE_ID_MISSED_CALL_TINY = 111, + RESOURCE_ID_MISSED_CALL_SMALL = 112, + RESOURCE_ID_GENERIC_REMINDER_TINY = 113, + RESOURCE_ID_WHATSAPP_NOTIFICATION_TINY = 114, + RESOURCE_ID_WHATSAPP_NOTIFICATION_SMALL = 115, + RESOURCE_ID_TWITTER_NOTIFICATION_TINY = 116, + RESOURCE_ID_TWITTER_NOTIFICATION_SMALL = 117, + RESOURCE_ID_TELEGRAM_APP_TINY = 118, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_TINY = 119, + RESOURCE_ID_GMAIL_NOTIFICATION_TINY = 120, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_TINY = 121, + RESOURCE_ID_FACEBOOK_NOTIFICATION_TINY = 122, + RESOURCE_ID_GENERIC_WEATHER_TINY = 123, + RESOURCE_ID_AUDIO_CASSETTE_TINY = 124, + RESOURCE_ID_SUNNY_DAY_TINY = 125, + RESOURCE_ID_GENERIC_SPORTS_TINY = 126, + RESOURCE_ID_GENERIC_EMAIL_TINY = 127, + RESOURCE_ID_AMERICAN_FOOTBALL_TINY = 128, + RESOURCE_ID_TIMELINE_CALENDAR_TINY = 129, + RESOURCE_ID_BASEBALL_GAME_TINY = 130, + RESOURCE_ID_BASEBALL_GAME_SMALL = 131, + RESOURCE_ID_ALARM_CLOCK_TINY = 132, + RESOURCE_ID_BIRTHDAY_EVENT_TINY = 133, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_TINY = 134, + RESOURCE_ID_CAR_RENTAL_TINY = 135, + RESOURCE_ID_SCHEDULED_FLIGHT_TINY = 136, + RESOURCE_ID_CLOUDY_DAY_TINY = 137, + RESOURCE_ID_CRICKET_GAME_TINY = 138, + RESOURCE_ID_CRICKET_GAME_SMALL = 139, + RESOURCE_ID_CRICKET_GAME_LARGE = 140, + RESOURCE_ID_DINNER_RESERVATION_TINY = 141, + RESOURCE_ID_DISMISSED_PHONE_CALL_TINY = 142, + RESOURCE_ID_GENERIC_CONFIRMATION_TINY = 143, + RESOURCE_ID_GENERIC_PIN_TINY = 144, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_TINY = 145, + RESOURCE_ID_GLUCOSE_MONITOR_TINY = 146, + RESOURCE_ID_HEAVY_RAIN_TINY = 147, + RESOURCE_ID_HEAVY_SNOW_TINY = 148, + RESOURCE_ID_HOCKEY_GAME_TINY = 149, + RESOURCE_ID_HOCKEY_GAME_SMALL = 150, + RESOURCE_ID_HOCKEY_GAME_LARGE = 151, + RESOURCE_ID_HOTEL_RESERVATION_TINY = 152, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_TINY = 153, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_SMALL = 154, + RESOURCE_ID_LIGHT_RAIN_TINY = 155, + RESOURCE_ID_LIGHT_SNOW_TINY = 156, + RESOURCE_ID_MAILBOX_NOTIFICATION_TINY = 157, + RESOURCE_ID_MAILBOX_NOTIFICATION_SMALL = 158, + RESOURCE_ID_MOVIE_EVENT_TINY = 159, + RESOURCE_ID_MUSIC_EVENT_TINY = 160, + RESOURCE_ID_NEWS_EVENT_TINY = 161, + RESOURCE_ID_PARTLY_CLOUDY_TINY = 162, + RESOURCE_ID_PAY_BILL_TINY = 163, + RESOURCE_ID_REACHED_FITNESS_GOAL_TINY = 164, + RESOURCE_ID_RADIO_SHOW_TINY = 165, + RESOURCE_ID_SCHEDULED_EVENT_TINY = 166, + RESOURCE_ID_SOCCER_GAME_TINY = 167, + RESOURCE_ID_SOCCER_GAME_SMALL = 168, + RESOURCE_ID_STOCKS_EVENT_TINY = 169, + RESOURCE_ID_BIRTHDAY_EVENT_SMALL = 170, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_SMALL = 171, + RESOURCE_ID_TIMELINE_CALENDAR_SMALL = 172, + RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_SMALL = 173, + RESOURCE_ID_ALARM_CLOCK_SMALL = 174, + RESOURCE_ID_CAR_RENTAL_SMALL = 175, + RESOURCE_ID_CHECK_INTERNET_CONNECTION_SMALL = 176, + RESOURCE_ID_DINNER_RESERVATION_SMALL = 177, + RESOURCE_ID_DISMISSED_PHONE_CALL_SMALL = 178, + RESOURCE_ID_GENERIC_SMS_SMALL = 179, + RESOURCE_ID_GENERIC_SMS_TINY = 180, + RESOURCE_ID_GENERIC_SMS_LARGE = 181, + RESOURCE_ID_FACEBOOK_NOTIFICATION_SMALL = 182, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_SMALL = 183, + RESOURCE_ID_GENERIC_CONFIRMATION_SMALL = 184, + RESOURCE_ID_GENERIC_EMAIL_SMALL = 185, + RESOURCE_ID_GENERIC_PIN_SMALL = 186, + RESOURCE_ID_GENERIC_REMINDER_SMALL = 187, + RESOURCE_ID_GENERIC_WARNING_SMALL = 188, + RESOURCE_ID_GENERIC_WEATHER_SMALL = 189, + RESOURCE_ID_GLUCOSE_MONITOR_SMALL = 190, + RESOURCE_ID_GMAIL_NOTIFICATION_SMALL = 191, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_SMALL = 192, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_SMALL = 193, + RESOURCE_ID_HEAVY_RAIN_SMALL = 194, + RESOURCE_ID_HEAVY_SNOW_SMALL = 195, + RESOURCE_ID_HOTEL_RESERVATION_SMALL = 196, + RESOURCE_ID_LIGHT_RAIN_SMALL = 197, + RESOURCE_ID_LIGHT_SNOW_SMALL = 198, + RESOURCE_ID_MOVIE_EVENT_SMALL = 199, + RESOURCE_ID_MUSIC_EVENT_SMALL = 200, + RESOURCE_ID_NEWS_EVENT_SMALL = 201, + RESOURCE_ID_PARTLY_CLOUDY_SMALL = 202, + RESOURCE_ID_PAY_BILL_SMALL = 203, + RESOURCE_ID_RADIO_SHOW_SMALL = 204, + RESOURCE_ID_REACHED_FITNESS_GOAL_SMALL = 205, + RESOURCE_ID_SCHEDULED_EVENT_SMALL = 206, + RESOURCE_ID_SUNNY_DAY_SMALL = 207, + RESOURCE_ID_GENERIC_SPORTS_SMALL = 208, + RESOURCE_ID_TIDE_IS_HIGH_SMALL = 209, + RESOURCE_ID_WATCH_DISCONNECTED_SMALL = 210, + RESOURCE_ID_ALARM_CLOCK_LARGE_STATIC = 211, + RESOURCE_ID_AMERICAN_FOOTBALL_LARGE = 212, + RESOURCE_ID_AMERICAN_FOOTBALL_SMALL = 213, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_LARGE = 214, + RESOURCE_ID_WHATSAPP_NOTIFICATION_LARGE = 215, + RESOURCE_ID_AUDIO_CASSETTE_LARGE = 216, + RESOURCE_ID_AUDIO_CASSETTE_SMALL = 217, + RESOURCE_ID_BASEBALL_GAME_LARGE = 218, + RESOURCE_ID_BIRTHDAY_EVENT_LARGE = 219, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_LARGE = 220, + RESOURCE_ID_TIMELINE_CALENDAR_LARGE = 221, + RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_LARGE = 222, + RESOURCE_ID_CAR_RENTAL_LARGE = 223, + RESOURCE_ID_CLOUDY_DAY_LARGE = 224, + RESOURCE_ID_CLOUDY_DAY_SMALL = 225, + RESOURCE_ID_DAY_SEPARATOR_TINY = 226, + RESOURCE_ID_DAY_SEPARATOR_SMALL = 227, + RESOURCE_ID_DAY_SEPARATOR_LARGE = 228, + RESOURCE_ID_RESULT_DELETED_TINY = 229, + RESOURCE_ID_RESULT_DELETED_SMALL = 230, + RESOURCE_ID_DINNER_RESERVATION_LARGE = 231, + RESOURCE_ID_DISMISSED_PHONE_CALL_LARGE = 232, + RESOURCE_ID_DURING_PHONE_CALL_TINY = 233, + RESOURCE_ID_DURING_PHONE_CALL_SMALL = 234, + RESOURCE_ID_DURING_PHONE_CALL_LARGE = 235, + RESOURCE_ID_DURING_PHONE_CALL_CENTERED_LARGE = 236, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_LARGE = 237, + RESOURCE_ID_FACEBOOK_NOTIFICATION_LARGE = 238, + RESOURCE_ID_GENERIC_EMAIL_LARGE = 239, + RESOURCE_ID_GENERIC_PIN_LARGE = 240, + RESOURCE_ID_GENERIC_REMINDER_LARGE = 241, + RESOURCE_ID_GENERIC_SPORTS_LARGE = 242, + RESOURCE_ID_GENERIC_WEATHER_LARGE = 243, + RESOURCE_ID_GLUCOSE_MONITOR_LARGE = 244, + RESOURCE_ID_GMAIL_NOTIFICATION_LARGE = 245, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_LARGE = 246, + RESOURCE_ID_HEAVY_RAIN_LARGE = 247, + RESOURCE_ID_HEAVY_SNOW_LARGE = 248, + RESOURCE_ID_HOTEL_RESERVATION_LARGE = 249, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_LARGE = 250, + RESOURCE_ID_LIGHT_RAIN_LARGE = 251, + RESOURCE_ID_LIGHT_SNOW_LARGE = 252, + RESOURCE_ID_MAILBOX_NOTIFICATION_LARGE = 253, + RESOURCE_ID_MISSED_CALL_LARGE = 254, + RESOURCE_ID_MOVIE_EVENT_LARGE = 255, + RESOURCE_ID_MUSIC_EVENT_LARGE = 256, + RESOURCE_ID_NEWS_EVENT_LARGE = 257, + RESOURCE_ID_PARTLY_CLOUDY_LARGE = 258, + RESOURCE_ID_PAY_BILL_LARGE = 259, + RESOURCE_ID_RADIO_SHOW_LARGE = 260, + RESOURCE_ID_REACHED_FITNESS_GOAL_LARGE = 261, + RESOURCE_ID_SCHEDULED_EVENT_LARGE = 262, + RESOURCE_ID_SCHEDULED_FLIGHT_LARGE = 263, + RESOURCE_ID_SCHEDULED_FLIGHT_SMALL = 264, + RESOURCE_ID_RESULT_SENT_TINY = 265, + RESOURCE_ID_RESULT_SENT_SMALL = 266, + RESOURCE_ID_SOCCER_GAME_LARGE = 267, + RESOURCE_ID_STOCKS_EVENT_LARGE = 268, + RESOURCE_ID_STOCKS_EVENT_SMALL = 269, + RESOURCE_ID_SUNNY_DAY_LARGE = 270, + RESOURCE_ID_TELEGRAM_APP_LARGE = 271, + RESOURCE_ID_TELEGRAM_APP_SMALL = 272, + RESOURCE_ID_TIDE_IS_HIGH_LARGE = 273, + RESOURCE_ID_TWITTER_NOTIFICATION_LARGE = 274, + RESOURCE_ID_RESULT_FAILED_TINY = 275, + RESOURCE_ID_RESULT_FAILED_SMALL = 276, + RESOURCE_ID_RESULT_FAILED_LARGE = 277, + RESOURCE_ID_GENERIC_QUESTION_TINY = 278, + RESOURCE_ID_GENERIC_QUESTION_SMALL = 279, + RESOURCE_ID_GENERIC_QUESTION_LARGE = 280, + RESOURCE_ID_RAINING_AND_SNOWING_TINY = 281, + RESOURCE_ID_RAINING_AND_SNOWING_SMALL = 282, + RESOURCE_ID_RAINING_AND_SNOWING_LARGE = 283, + RESOURCE_ID_NOTIFICATION_FACETIME_TINY = 284, + RESOURCE_ID_NOTIFICATION_FACETIME_SMALL = 285, + RESOURCE_ID_NOTIFICATION_FACETIME_LARGE = 286, + RESOURCE_ID_NOTIFICATION_LINE_TINY = 287, + RESOURCE_ID_NOTIFICATION_LINE_SMALL = 288, + RESOURCE_ID_NOTIFICATION_LINE_LARGE = 289, + RESOURCE_ID_NOTIFICATION_SKYPE_TINY = 290, + RESOURCE_ID_NOTIFICATION_SKYPE_SMALL = 291, + RESOURCE_ID_NOTIFICATION_SKYPE_LARGE = 292, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_TINY = 293, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_SMALL = 294, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_LARGE = 295, + RESOURCE_ID_NOTIFICATION_VIBER_TINY = 296, + RESOURCE_ID_NOTIFICATION_VIBER_SMALL = 297, + RESOURCE_ID_NOTIFICATION_VIBER_LARGE = 298, + RESOURCE_ID_NOTIFICATION_WECHAT_TINY = 299, + RESOURCE_ID_NOTIFICATION_WECHAT_SMALL = 300, + RESOURCE_ID_NOTIFICATION_WECHAT_LARGE = 301, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_TINY = 302, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_SMALL = 303, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_LARGE = 304, + RESOURCE_ID_TV_SHOW_TINY = 305, + RESOURCE_ID_TV_SHOW_SMALL = 306, + RESOURCE_ID_TV_SHOW_LARGE = 307, + RESOURCE_ID_END_OF_TIMELINE = 308, + RESOURCE_ID_BASKETBALL_TINY = 309, + RESOURCE_ID_BASKETBALL_SMALL = 310, + RESOURCE_ID_BASKETBALL_LARGE = 311, + RESOURCE_ID_RESULT_DISMISSED_TINY = 312, + RESOURCE_ID_RESULT_DISMISSED_SMALL = 313, + RESOURCE_ID_TIDE_IS_HIGH_TINY = 314, + RESOURCE_ID_OUTGOING_CALL_LARGE = 315, + RESOURCE_ID_NOTIFICATION_HIPCHAT_TINY = 316, + RESOURCE_ID_NOTIFICATION_HIPCHAT_SMALL = 317, + RESOURCE_ID_NOTIFICATION_HIPCHAT_LARGE = 318, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_TINY = 319, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_SMALL = 320, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_LARGE = 321, + RESOURCE_ID_INCOMING_PHONE_CALL_TINY = 322, + RESOURCE_ID_INCOMING_PHONE_CALL_SMALL = 323, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_TINY = 324, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_SMALL = 325, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_LARGE = 326, + RESOURCE_ID_NOTIFICATION_KIK_TINY = 327, + RESOURCE_ID_NOTIFICATION_KIK_SMALL = 328, + RESOURCE_ID_NOTIFICATION_KIK_LARGE = 329, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_TINY = 330, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_SMALL = 331, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_LARGE = 332, + RESOURCE_ID_LOCATION_TINY = 333, + RESOURCE_ID_LOCATION_SMALL = 334, + RESOURCE_ID_LOCATION_LARGE = 335, + RESOURCE_ID_SUNSET_TINY = 336, + RESOURCE_ID_SUNSET_SMALL = 337, + RESOURCE_ID_SUNSET_LARGE = 338, + RESOURCE_ID_SUNRISE_TINY = 339, + RESOURCE_ID_SUNRISE_SMALL = 340, + RESOURCE_ID_SUNRISE_LARGE = 341, + RESOURCE_ID_SETTINGS_TINY = 342, + RESOURCE_ID_SETTINGS_SMALL = 343, + RESOURCE_ID_SETTINGS_LARGE = 344, + RESOURCE_ID_PLUS_ICON_BLACK = 345, + RESOURCE_ID_PLUS_ICON_DOTTED = 346, + RESOURCE_ID_RESULT_SHREDDED_TINY = 347, + RESOURCE_ID_RESULT_SHREDDED_SMALL = 348, + RESOURCE_ID_QUIET_TIME_MOUSE = 349, + RESOURCE_ID_REMINDER_SNOOZE = 350, + RESOURCE_ID_QUIET_TIME_STATUS_BAR = 351, + RESOURCE_ID_QUICK_DISMISS = 352, + RESOURCE_ID_THUMBS_UP_SMALL = 353, + RESOURCE_ID_THUMBS_UP_LARGE = 354, + RESOURCE_ID_ARROW_UP_SMALL = 355, + RESOURCE_ID_ARROW_DOWN_SMALL = 356, + RESOURCE_ID_ACTIVITY_TINY = 357, + RESOURCE_ID_ACTIVITY_SMALL = 358, + RESOURCE_ID_ACTIVITY_LARGE = 359, + RESOURCE_ID_SLEEP_TINY = 360, + RESOURCE_ID_SLEEP_SMALL = 361, + RESOURCE_ID_SLEEP_LARGE = 362, + RESOURCE_ID_REWARD_AVERAGE_LARGE = 363, + RESOURCE_ID_REWARD_GOOD_LARGE = 364, + RESOURCE_ID_REWARD_BAD_LARGE = 365, + RESOURCE_ID_CALORIES_TINY = 366, + RESOURCE_ID_DISTANCE_TINY = 367, + RESOURCE_ID_DURATION_TINY = 368, + RESOURCE_ID_PACE_TINY = 369, + RESOURCE_ID_RUN_TINY = 370, + RESOURCE_ID_RUN_LARGE = 371, + RESOURCE_ID_MOON_TINY = 372, + RESOURCE_ID_NOTIFICATION_AMAZON_TINY = 373, + RESOURCE_ID_NOTIFICATION_AMAZON_SMALL = 374, + RESOURCE_ID_NOTIFICATION_AMAZON_LARGE = 375, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_TINY = 376, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_SMALL = 377, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_LARGE = 378, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_TINY = 379, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_SMALL = 380, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_LARGE = 381, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_TINY = 382, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_SMALL = 383, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_LARGE = 384, + RESOURCE_ID_NOTIFICATION_LINKEDIN_TINY = 385, + RESOURCE_ID_NOTIFICATION_LINKEDIN_SMALL = 386, + RESOURCE_ID_NOTIFICATION_LINKEDIN_LARGE = 387, + RESOURCE_ID_NOTIFICATION_SLACK_TINY = 388, + RESOURCE_ID_NOTIFICATION_SLACK_SMALL = 389, + RESOURCE_ID_NOTIFICATION_SLACK_LARGE = 390, + RESOURCE_ID_NOTIFICATION_AIRMAIL_TINY = 391, + RESOURCE_ID_NOTIFICATION_AIRMAIL_SMALL = 392, + RESOURCE_ID_NOTIFICATION_AIRMAIL_LARGE = 393, + RESOURCE_ID_NOTIFICATION_BEEPER_TINY = 394, + RESOURCE_ID_NOTIFICATION_BEEPER_SMALL = 395, + RESOURCE_ID_NOTIFICATION_BEEPER_LARGE = 396, + RESOURCE_ID_NOTIFICATION_BLUESKY_TINY = 397, + RESOURCE_ID_NOTIFICATION_BLUESKY_SMALL = 398, + RESOURCE_ID_NOTIFICATION_BLUESKY_LARGE = 399, + RESOURCE_ID_NOTIFICATION_DISCORD_TINY = 400, + RESOURCE_ID_NOTIFICATION_DISCORD_SMALL = 401, + RESOURCE_ID_NOTIFICATION_DISCORD_LARGE = 402, + RESOURCE_ID_NOTIFICATION_DUOLINGO_TINY = 403, + RESOURCE_ID_NOTIFICATION_DUOLINGO_SMALL = 404, + RESOURCE_ID_NOTIFICATION_DUOLINGO_LARGE = 405, + RESOURCE_ID_NOTIFICATION_EBAY_TINY = 406, + RESOURCE_ID_NOTIFICATION_EBAY_SMALL = 407, + RESOURCE_ID_NOTIFICATION_EBAY_LARGE = 408, + RESOURCE_ID_NOTIFICATION_ELEMENT_TINY = 409, + RESOURCE_ID_NOTIFICATION_ELEMENT_SMALL = 410, + RESOURCE_ID_NOTIFICATION_ELEMENT_LARGE = 411, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_TINY = 412, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_SMALL = 413, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_LARGE = 414, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_TINY = 415, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_SMALL = 416, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_LARGE = 417, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_TINY = 418, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_SMALL = 419, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_LARGE = 420, + RESOURCE_ID_NOTIFICATION_OUTLOOK_TINY = 421, + RESOURCE_ID_NOTIFICATION_OUTLOOK_SMALL = 422, + RESOURCE_ID_NOTIFICATION_OUTLOOK_LARGE = 423, + RESOURCE_ID_NOTIFICATION_SIGNAL_TINY = 424, + RESOURCE_ID_NOTIFICATION_SIGNAL_SMALL = 425, + RESOURCE_ID_NOTIFICATION_SIGNAL_LARGE = 426, + RESOURCE_ID_NOTIFICATION_STEAM_TINY = 427, + RESOURCE_ID_NOTIFICATION_STEAM_SMALL = 428, + RESOURCE_ID_NOTIFICATION_STEAM_LARGE = 429, + RESOURCE_ID_NOTIFICATION_TEAMS_TINY = 430, + RESOURCE_ID_NOTIFICATION_TEAMS_SMALL = 431, + RESOURCE_ID_NOTIFICATION_TEAMS_LARGE = 432, + RESOURCE_ID_NOTIFICATION_THREADS_TINY = 433, + RESOURCE_ID_NOTIFICATION_THREADS_SMALL = 434, + RESOURCE_ID_NOTIFICATION_THREADS_LARGE = 435, + RESOURCE_ID_NOTIFICATION_TWITCH_TINY = 436, + RESOURCE_ID_NOTIFICATION_TWITCH_SMALL = 437, + RESOURCE_ID_NOTIFICATION_TWITCH_LARGE = 438, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_TINY = 439, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_SMALL = 440, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_LARGE = 441, + RESOURCE_ID_NOTIFICATION_YOUTUBE_TINY = 442, + RESOURCE_ID_NOTIFICATION_YOUTUBE_SMALL = 443, + RESOURCE_ID_NOTIFICATION_YOUTUBE_LARGE = 444, + RESOURCE_ID_NOTIFICATION_ZOOM_TINY = 445, + RESOURCE_ID_NOTIFICATION_ZOOM_SMALL = 446, + RESOURCE_ID_NOTIFICATION_ZOOM_LARGE = 447, + RESOURCE_ID_NOTIFICATION_REDDIT_TINY = 448, + RESOURCE_ID_NOTIFICATION_REDDIT_SMALL = 449, + RESOURCE_ID_NOTIFICATION_REDDIT_LARGE = 450, + RESOURCE_ID_NOTIFICATION_SWARM_TINY = 451, + RESOURCE_ID_NOTIFICATION_SWARM_SMALL = 452, + RESOURCE_ID_NOTIFICATION_SWARM_LARGE = 453, + RESOURCE_ID_NOTIFICATION_TAPO_TINY = 454, + RESOURCE_ID_NOTIFICATION_TAPO_SMALL = 455, + RESOURCE_ID_NOTIFICATION_TAPO_LARGE = 456, + RESOURCE_ID_STRINGS_CA_ES = 457, + RESOURCE_ID_STRINGS_DE_DE = 458, + RESOURCE_ID_STRINGS_ES_ES = 459, + RESOURCE_ID_STRINGS_FR_FR = 460, + RESOURCE_ID_STRINGS_IT_IT = 461, + RESOURCE_ID_STRINGS_NL_NL = 462, + RESOURCE_ID_STRINGS_PT_PT = 463, + RESOURCE_ID_SMART_ALARM_TINY = 464, + RESOURCE_ID_HEALTH_APP_ACTIVITY = 465, + RESOURCE_ID_HEALTH_APP_SLEEP = 466, + RESOURCE_ID_HEALTH_APP_CROWN = 467, + RESOURCE_ID_HEALTH_APP_HR = 468, + RESOURCE_ID_HEALTH_APP_PULSING_HEART = 469, + RESOURCE_ID_WORKOUT_APP_WALK = 470, + RESOURCE_ID_WORKOUT_APP_WALK_SMALL = 471, + RESOURCE_ID_WORKOUT_APP_WALK_TINY = 472, + RESOURCE_ID_WORKOUT_APP_RUN = 473, + RESOURCE_ID_WORKOUT_APP_RUN_SMALL = 474, + RESOURCE_ID_WORKOUT_APP_RUN_TINY = 475, + RESOURCE_ID_WORKOUT_APP_WORKOUT = 476, + RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL = 477, + RESOURCE_ID_WORKOUT_APP_DETECTED = 478, + RESOURCE_ID_WORKOUT_APP_HEART = 479, + RESOURCE_ID_WORKOUT_APP_MEASURING_HR = 480, + RESOURCE_ID_WORKOUT_APP_HR_PULSE_TINY = 481, + RESOURCE_ID_WORKOUT_APP_END = 482, + RESOURCE_ID_WORKOUT_APP_ONE = 483, + RESOURCE_ID_WORKOUT_APP_TWO = 484, + RESOURCE_ID_WORKOUT_APP_THREE = 485, + RESOURCE_ID_HEART_TINY = 486, + RESOURCE_ID_HEART_SMALL = 487, + RESOURCE_ID_HEART_LARGE = 488, + RESOURCE_ID_BACKLIGHT = 489, + RESOURCE_ID_AIRPLANE = 490, + RESOURCE_ID_MODAL_CONTRACT_TO_MODAL_SEQUENCE = 491, + RESOURCE_ID_MODAL_CONTRACT_FROM_MODAL_SEQUENCE = 492, + RESOURCE_ID_MODAL_EXPAND_TO_APP_SEQUENCE = 493, + RESOURCE_ID_NO_EVENTS_LARGE = 494, + RESOURCE_ID_TIMER_APP_FACE_ICON = 495, + RESOURCE_ID_STOPWATCH_APP_FACE_ICON = 496, + RESOURCE_ID_ESPN_APP_FACE_ICON = 497, + RESOURCE_ID_HEALTH_APP_FACE_ICON = 498, + RESOURCE_ID_SEND_TEXT_APP_FACE_ICON = 499, + RESOURCE_ID_QUIET_TIME_ACTIVE = 500, + RESOURCE_ID_QUIET_TIME_MOUSE_RIGHT_ALIGNED = 501, + RESOURCE_ID_BATTERY_CHARGING_ICON = 502, + RESOURCE_ID_HEALTH_ICON_MOON = 503, + RESOURCE_ID_HEALTH_ICON_ROTATED_MOON = 504, + RESOURCE_ID_STRIDE_HEART = 505, + RESOURCE_ID_STRIDE_SHOE_GREEN = 506, + RESOURCE_ID_STRIDE_SHOE_BLUE = 507, + RESOURCE_ID_SYSTEM_FCC_MARK = 508, + RESOURCE_ID_SYSTEM_KCC_MARK = 509, + RESOURCE_ID_SYSTEM_CE_MARK = 510, + RESOURCE_ID_SYSTEM_WEEE_MARK = 511, + RESOURCE_ID_SYSTEM_UKCA_MARK = 512, + RESOURCE_ID_SYSTEM_R_MARK = 513, + RESOURCE_ID_SYSTEM_T_MARK = 514, + RESOURCE_ID_SYSTEM_AUS_RCM_MARK = 515, + RESOURCE_ID_SYSTEM_NOM_NYCE_MARK = 516, + RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE = 517, + RESOURCE_ID_EMOJI_BIG_SMILE_LARGE = 518, + RESOURCE_ID_EMOJI_HEART_LARGE = 519, + RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE = 520, + RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE = 521, + RESOURCE_ID_EMOJI_SAD_LARGE = 522, + RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE = 523, + RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE = 524, + RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE = 525, + RESOURCE_ID_EMOJI_THUMBS_UP_LARGE = 526, + RESOURCE_ID_EMOJI_WINK_LARGE = 527, + RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE = 528, + RESOURCE_ID_VIBE_SCORE_REVEILLE = 529, + RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK = 530, + RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE = 531, + RESOURCE_ID_VIBE_SCORE_PULSE = 532, + RESOURCE_ID_VIBE_SCORE_JACKHAMMER = 533, + RESOURCE_ID_VIBE_SCORE_MARIO = 534, + RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW = 535, + RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH = 536, + RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW = 537, + RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH = 538, + RESOURCE_ID_VIBE_SCORE_ALARM_LPM = 539, + RESOURCE_ID_VIBE_SCORE_GENTLE = 540, + RESOURCE_ID_SMART_ALARM_ICON_BLACK = 541, + RESOURCE_ID_BLE_HRM_SHARE_REQUEST_LARGE = 542, + RESOURCE_ID_BLE_HRM_SHARED = 543, + RESOURCE_ID_BLE_HRM_NOT_SHARED = 544, + RESOURCE_ID_CONNECTIVITY_SHARING_HRM = 545, + RESOURCE_ID_MENU_ICON_HEALTH = 546, + RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM = 547, + RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM = 548, + RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM = 549, + RESOURCE_ID_STORED_APP_GOLF = 550, + RESOURCE_ID_TIMEZONE_DATABASE = 551, + RESOURCE_ID_ACTION_BAR_ICON_CHECK = 552, + RESOURCE_ID_GENERIC_WARNING_LARGE = 553, + RESOURCE_ID_FONT_FALLBACK_INTERNAL = 554, + RESOURCE_ID_ACTION_BAR_ICON_UP = 555, + RESOURCE_ID_ACTION_BAR_ICON_DOWN = 556, + RESOURCE_ID_GENERIC_WARNING_TINY = 557, + RESOURCE_ID_CHECK_INTERNET_CONNECTION_LARGE = 558, + RESOURCE_ID_WATCH_DISCONNECTED_LARGE = 559, + RESOURCE_ID_ARROW_DOWN = 560, + RESOURCE_ID_VOICE_MICROPHONE_LARGE = 561, + RESOURCE_ID_ALARM_CLOCK_LARGE = 562, + RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE = 563, + RESOURCE_ID_RESULT_DELETED_LARGE = 564, + RESOURCE_ID_RESULT_SHREDDED_LARGE = 565, + RESOURCE_ID_RESULT_DISMISSED_LARGE = 566, + RESOURCE_ID_GENERIC_CONFIRMATION_LARGE = 567, + RESOURCE_ID_INCOMING_PHONE_CALL_LARGE = 568, + RESOURCE_ID_RESULT_MUTE_LARGE = 569, + RESOURCE_ID_RESULT_SENT_LARGE = 570, + RESOURCE_ID_RESULT_UNMUTE_LARGE = 571, + RESOURCE_ID_PUG = 572, + RESOURCE_ID_STRINGS = 573, + RESOURCE_ID_GOTHIC_14_EXTENDED = 574, + RESOURCE_ID_GOTHIC_14_BOLD_EXTENDED = 575, + RESOURCE_ID_GOTHIC_18_EXTENDED = 576, + RESOURCE_ID_GOTHIC_18_BOLD_EXTENDED = 577, + RESOURCE_ID_GOTHIC_24_EXTENDED = 578, + RESOURCE_ID_GOTHIC_24_BOLD_EXTENDED = 579, + RESOURCE_ID_GOTHIC_28_EXTENDED = 580, + RESOURCE_ID_GOTHIC_28_BOLD_EXTENDED = 581, + RESOURCE_ID_BITHAM_18_LIGHT_SUBSET_EXTENDED = 582, + RESOURCE_ID_BITHAM_30_BLACK_EXTENDED = 583, + RESOURCE_ID_BITHAM_34_LIGHT_SUBSET_EXTENDED = 584, + RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS_EXTENDED = 585, + RESOURCE_ID_BITHAM_42_BOLD_EXTENDED = 586, + RESOURCE_ID_BITHAM_42_LIGHT_EXTENDED = 587, + RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS_EXTENDED = 588, + RESOURCE_ID_ROBOTO_CONDENSED_21_EXTENDED = 589, + RESOURCE_ID_ROBOTO_BOLD_SUBSET_49_EXTENDED = 590, + RESOURCE_ID_DROID_SERIF_28_BOLD_EXTENDED = 591, + RESOURCE_ID_GOTHIC_09_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_14_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_18_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_24_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_28_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_36_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_36_BOLD_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_20_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_32_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_36_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_38_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_42_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_28_LIGHT_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_60_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_60_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_FONT_FALLBACK_INTERNAL_EXTENDED = INVALID_RESOURCE, +} ResourceId; \ No newline at end of file diff --git a/tests/overrides/default/resources/gabbro/resource/resource_version.auto.h b/tests/overrides/default/resources/gabbro/resource/resource_version.auto.h new file mode 100644 index 0000000000..6bea71a453 --- /dev/null +++ b/tests/overrides/default/resources/gabbro/resource/resource_version.auto.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED +// DO NOT MODIFY +// + +static const ResourceVersion SYSTEM_RESOURCE_VERSION = { + .crc = 73659373, + .timestamp = 0 +}; diff --git a/tests/overrides/default/resources/gabbro/resource/timeline_resource_ids.auto.h b/tests/overrides/default/resources/gabbro/resource/timeline_resource_ids.auto.h new file mode 100644 index 0000000000..cc84cd48c5 --- /dev/null +++ b/tests/overrides/default/resources/gabbro/resource/timeline_resource_ids.auto.h @@ -0,0 +1,145 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ +#pragma once + +// +// AUTOGENERATED +// DO NOT MODIFY +// + +typedef enum { + TIMELINE_RESOURCE_INVALID = 0, + TIMELINE_RESOURCE_NOTIFICATION_GENERIC = 0x80000001, + TIMELINE_RESOURCE_TIMELINE_MISSED_CALL = 0x80000002, + TIMELINE_RESOURCE_NOTIFICATION_REMINDER = 0x80000003, + TIMELINE_RESOURCE_NOTIFICATION_FLAG = 0x80000004, + TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP = 0x80000005, + TIMELINE_RESOURCE_NOTIFICATION_TWITTER = 0x80000006, + TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM = 0x80000007, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS = 0x80000008, + TIMELINE_RESOURCE_NOTIFICATION_GMAIL = 0x80000009, + TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER = 0x8000000a, + TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK = 0x8000000b, + TIMELINE_RESOURCE_AUDIO_CASSETTE = 0x8000000c, + TIMELINE_RESOURCE_ALARM_CLOCK = 0x8000000d, + TIMELINE_RESOURCE_TIMELINE_WEATHER = 0x8000000e, + TIMELINE_RESOURCE_TIMELINE_SUN = 0x80000010, + TIMELINE_RESOURCE_TIMELINE_SPORTS = 0x80000011, + TIMELINE_RESOURCE_GENERIC_EMAIL = 0x80000013, + TIMELINE_RESOURCE_AMERICAN_FOOTBALL = 0x80000014, + TIMELINE_RESOURCE_TIMELINE_CALENDAR = 0x80000015, + TIMELINE_RESOURCE_TIMELINE_BASEBALL = 0x80000016, + TIMELINE_RESOURCE_BIRTHDAY_EVENT = 0x80000017, + TIMELINE_RESOURCE_CAR_RENTAL = 0x80000018, + TIMELINE_RESOURCE_CLOUDY_DAY = 0x80000019, + TIMELINE_RESOURCE_CRICKET_GAME = 0x8000001a, + TIMELINE_RESOURCE_DINNER_RESERVATION = 0x8000001b, + TIMELINE_RESOURCE_GENERIC_WARNING = 0x8000001c, + TIMELINE_RESOURCE_GLUCOSE_MONITOR = 0x8000001d, + TIMELINE_RESOURCE_HOCKEY_GAME = 0x8000001e, + TIMELINE_RESOURCE_HOTEL_RESERVATION = 0x8000001f, + TIMELINE_RESOURCE_LIGHT_RAIN = 0x80000020, + TIMELINE_RESOURCE_LIGHT_SNOW = 0x80000021, + TIMELINE_RESOURCE_MOVIE_EVENT = 0x80000022, + TIMELINE_RESOURCE_MUSIC_EVENT = 0x80000023, + TIMELINE_RESOURCE_NEWS_EVENT = 0x80000024, + TIMELINE_RESOURCE_PARTLY_CLOUDY = 0x80000025, + TIMELINE_RESOURCE_PAY_BILL = 0x80000026, + TIMELINE_RESOURCE_RADIO_SHOW = 0x80000027, + TIMELINE_RESOURCE_SCHEDULED_EVENT = 0x80000028, + TIMELINE_RESOURCE_SOCCER_GAME = 0x80000029, + TIMELINE_RESOURCE_STOCKS_EVENT = 0x8000002a, + TIMELINE_RESOURCE_RESULT_DELETED = 0x8000002b, + TIMELINE_RESOURCE_CHECK_INTERNET_CONNECTION = 0x8000002c, + TIMELINE_RESOURCE_GENERIC_SMS = 0x8000002d, + TIMELINE_RESOURCE_RESULT_MUTE = 0x8000002e, + TIMELINE_RESOURCE_RESULT_SENT = 0x8000002f, + TIMELINE_RESOURCE_WATCH_DISCONNECTED = 0x80000030, + TIMELINE_RESOURCE_DURING_PHONE_CALL = 0x80000031, + TIMELINE_RESOURCE_TIDE_IS_HIGH = 0x80000032, + TIMELINE_RESOURCE_RESULT_DISMISSED = 0x80000033, + TIMELINE_RESOURCE_HEAVY_RAIN = 0x80000034, + TIMELINE_RESOURCE_HEAVY_SNOW = 0x80000035, + TIMELINE_RESOURCE_SCHEDULED_FLIGHT = 0x80000036, + TIMELINE_RESOURCE_GENERIC_CONFIRMATION = 0x80000037, + TIMELINE_RESOURCE_DAY_SEPARATOR = 0x80000038, + TIMELINE_RESOURCE_NO_EVENTS = 0x80000039, + TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER = 0x8000003a, + TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM = 0x8000003b, + TIMELINE_RESOURCE_NOTIFICATION_MAILBOX = 0x8000003c, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX = 0x8000003d, + TIMELINE_RESOURCE_RESULT_FAILED = 0x8000003e, + TIMELINE_RESOURCE_GENERIC_QUESTION = 0x8000003f, + TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK = 0x80000040, + TIMELINE_RESOURCE_RAINING_AND_SNOWING = 0x80000041, + TIMELINE_RESOURCE_REACHED_FITNESS_GOAL = 0x80000042, + TIMELINE_RESOURCE_NOTIFICATION_LINE = 0x80000043, + TIMELINE_RESOURCE_NOTIFICATION_SKYPE = 0x80000044, + TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT = 0x80000045, + TIMELINE_RESOURCE_NOTIFICATION_VIBER = 0x80000046, + TIMELINE_RESOURCE_NOTIFICATION_WECHAT = 0x80000047, + TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL = 0x80000048, + TIMELINE_RESOURCE_TV_SHOW = 0x80000049, + TIMELINE_RESOURCE_BASKETBALL = 0x8000004a, + TIMELINE_RESOURCE_DISMISSED_PHONE_CALL = 0x8000004b, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MESSENGER = 0x8000004c, + TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT = 0x8000004d, + TIMELINE_RESOURCE_INCOMING_PHONE_CALL = 0x8000004e, + TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK = 0x8000004f, + TIMELINE_RESOURCE_NOTIFICATION_KIK = 0x80000050, + TIMELINE_RESOURCE_NOTIFICATION_LIGHTHOUSE = 0x80000051, + TIMELINE_RESOURCE_LOCATION = 0x80000052, + TIMELINE_RESOURCE_SETTINGS = 0x80000053, + TIMELINE_RESOURCE_SUNRISE = 0x80000054, + TIMELINE_RESOURCE_SUNSET = 0x80000055, + TIMELINE_RESOURCE_RESULT_UNMUTE = 0x80000056, + TIMELINE_RESOURCE_RESULT_UNMUTE_ALT = 0x8000005e, + TIMELINE_RESOURCE_DURING_PHONE_CALL_CENTERED = 0x8000005f, + TIMELINE_RESOURCE_TIMELINE_EMPTY_CALENDAR = 0x80000060, + TIMELINE_RESOURCE_THUMBS_UP = 0x80000061, + TIMELINE_RESOURCE_ARROW_UP = 0x80000062, + TIMELINE_RESOURCE_ARROW_DOWN = 0x80000063, + TIMELINE_RESOURCE_ACTIVITY = 0x80000064, + TIMELINE_RESOURCE_SLEEP = 0x80000065, + TIMELINE_RESOURCE_REWARD_BAD = 0x80000066, + TIMELINE_RESOURCE_REWARD_GOOD = 0x80000067, + TIMELINE_RESOURCE_REWARD_AVERAGE = 0x80000068, + TIMELINE_RESOURCE_CALORIES = 0x80000069, + TIMELINE_RESOURCE_DISTANCE = 0x8000006a, + TIMELINE_RESOURCE_DURATION = 0x8000006b, + TIMELINE_RESOURCE_PACE = 0x8000006c, + TIMELINE_RESOURCE_RUN = 0x8000006d, + TIMELINE_RESOURCE_NOTIFICATION_FACETIME = 0x8000006e, + TIMELINE_RESOURCE_NOTIFICATION_AMAZON = 0x8000006f, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS = 0x80000070, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS = 0x80000071, + TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS = 0x80000072, + TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN = 0x80000073, + TIMELINE_RESOURCE_NOTIFICATION_SLACK = 0x80000074, + TIMELINE_RESOURCE_SMART_ALARM = 0x80000075, + TIMELINE_RESOURCE_HEART = 0x80000076, + TIMELINE_RESOURCE_BLE_HRM_SHARING = 0x80000077, + TIMELINE_RESOURCE_NOTIFICATION_BEEPER = 0x80000078, + TIMELINE_RESOURCE_NOTIFICATION_DISCORD = 0x80000079, + TIMELINE_RESOURCE_NOTIFICATION_BLUESKY = 0x8000007a, + TIMELINE_RESOURCE_NOTIFICATION_DUOLINGO = 0x8000007b, + TIMELINE_RESOURCE_NOTIFICATION_ELEMENT = 0x8000007c, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_CHAT = 0x8000007d, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_TASKS = 0x8000007e, + TIMELINE_RESOURCE_NOTIFICATION_HOME_ASSISTANT = 0x8000007f, + TIMELINE_RESOURCE_NOTIFICATION_STEAM = 0x80000080, + TIMELINE_RESOURCE_NOTIFICATION_TEAMS = 0x80000081, + TIMELINE_RESOURCE_NOTIFICATION_THREADS = 0x80000082, + TIMELINE_RESOURCE_NOTIFICATION_UNIFI_PROTECT = 0x80000083, + TIMELINE_RESOURCE_NOTIFICATION_ZOOM = 0x80000084, + TIMELINE_RESOURCE_NOTIFICATION_EBAY = 0x80000085, + TIMELINE_RESOURCE_NOTIFICATION_YOUTUBE = 0x80000086, + TIMELINE_RESOURCE_NOTIFICATION_SIGNAL = 0x80000087, + TIMELINE_RESOURCE_NOTIFICATION_TWITCH = 0x80000088, + TIMELINE_RESOURCE_NOTIFICATION_AIRMAIL = 0x80000089, + TIMELINE_RESOURCE_NOTIFICATION_REDDIT = 0x8000008a, + TIMELINE_RESOURCE_NOTIFICATION_SWARM = 0x8000008b, + TIMELINE_RESOURCE_NOTIFICATION_TAPO = 0x8000008c, +} TimelineResourceId; + +#define NUM_TIMELINE_RESOURCES 141 diff --git a/tests/overrides/default/resources/obelix/resource/resource_ids.auto.h b/tests/overrides/default/resources/obelix/resource/resource_ids.auto.h new file mode 100644 index 0000000000..3280762873 --- /dev/null +++ b/tests/overrides/default/resources/obelix/resource/resource_ids.auto.h @@ -0,0 +1,625 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED BY BUILD +// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN +// + +typedef enum { + INVALID_RESOURCE = 0, + RESOURCE_ID_INVALID = 0, + DEFAULT_MENU_ICON = 0, + RESOURCE_ID_ACTION_BAR_ICON_X = 1, + RESOURCE_ID_BT_PAIR_SUCCESS = 2, + RESOURCE_ID_BT_PAIR_FAILURE = 3, + RESOURCE_ID_BT_PAIR_APPROVE_ON_PHONE = 4, + RESOURCE_ID_BT_PAIR_CONFIRMATION = 5, + RESOURCE_ID_GOTHIC_18_BOLD = 6, + RESOURCE_ID_BATTERY_ICON_LOW_LARGE = 7, + RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE = 8, + RESOURCE_ID_BATTERY_ICON_FULL_LARGE = 9, + RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED = 10, + RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED = 11, + RESOURCE_ID_BATTERY_ICON_CHARGE = 12, + RESOURCE_ID_BATTERY_NEEDS_CHARGING = 13, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE = 14, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED = 15, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED = 16, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND = 17, + RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY = 18, + RESOURCE_ID_QUIET_TIME = 19, + RESOURCE_ID_MUSIC_APP_GLANCE_PLAY = 20, + RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE = 21, + RESOURCE_ID_NOTIFICATIONS_APP_GLANCE = 22, + RESOURCE_ID_SEND_TEXT_APP_GLANCE = 23, + RESOURCE_ID_WATCHFACES_APP_GLANCE = 24, + RESOURCE_ID_MENU_ICON_TICTOC_WATCH = 25, + RESOURCE_ID_MENU_ICON_KICKSTART_WATCH = 26, + RESOURCE_ID_SETTINGS_MENU_ICON_BLUETOOTH = 27, + RESOURCE_ID_SETTINGS_MENU_ICON_NOTIFICATIONS = 28, + RESOURCE_ID_SETTINGS_MENU_ICON_VIBRATIONS = 29, + RESOURCE_ID_SETTINGS_MENU_ICON_QUIET_TIME = 30, + RESOURCE_ID_SETTINGS_MENU_ICON_TIMELINE = 31, + RESOURCE_ID_SETTINGS_MENU_ICON_QUICK_LAUNCH = 32, + RESOURCE_ID_SETTINGS_MENU_ICON_DATE_TIME = 33, + RESOURCE_ID_SETTINGS_MENU_ICON_DISPLAY = 34, + RESOURCE_ID_SETTINGS_MENU_ICON_HEALTH = 35, + RESOURCE_ID_SETTINGS_MENU_ICON_THEMES = 36, + RESOURCE_ID_SETTINGS_MENU_ICON_BACKGROUND_APP = 37, + RESOURCE_ID_SETTINGS_MENU_ICON_SYSTEM = 38, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT = 39, + RESOURCE_ID_SETTINGS_ICON_BLUETOOTH = 40, + RESOURCE_ID_SETTINGS_ICON_AIRPLANE = 41, + RESOURCE_ID_ACTION_BAR_ICON_SMS = 42, + RESOURCE_ID_GOTHIC_09 = 43, + RESOURCE_ID_GOTHIC_14 = 44, + RESOURCE_ID_GOTHIC_14_EMOJI = 45, + RESOURCE_ID_GOTHIC_14_BOLD = 46, + RESOURCE_ID_GOTHIC_18 = 47, + RESOURCE_ID_GOTHIC_18_EMOJI = 48, + RESOURCE_ID_GOTHIC_24 = 49, + RESOURCE_ID_GOTHIC_24_BOLD = 50, + RESOURCE_ID_GOTHIC_24_EMOJI = 51, + RESOURCE_ID_GOTHIC_28 = 52, + RESOURCE_ID_GOTHIC_28_BOLD = 53, + RESOURCE_ID_GOTHIC_28_EMOJI = 54, + RESOURCE_ID_GOTHIC_36 = 55, + RESOURCE_ID_GOTHIC_36_BOLD = 56, + RESOURCE_ID_BITHAM_30_BLACK = 57, + RESOURCE_ID_BITHAM_42_BOLD = 58, + RESOURCE_ID_BITHAM_42_LIGHT = 59, + RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS = 60, + RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS = 61, + RESOURCE_ID_BITHAM_34_LIGHT_SUBSET = 62, + RESOURCE_ID_BITHAM_18_LIGHT_SUBSET = 63, + RESOURCE_ID_ROBOTO_CONDENSED_21 = 64, + RESOURCE_ID_ROBOTO_BOLD_SUBSET_49 = 65, + RESOURCE_ID_DROID_SERIF_28_BOLD = 66, + RESOURCE_ID_LECO_20_BOLD_NUMBERS = 67, + RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM = 68, + RESOURCE_ID_LECO_32_BOLD_NUMBERS = 69, + RESOURCE_ID_LECO_36_BOLD_NUMBERS = 70, + RESOURCE_ID_LECO_38_BOLD_NUMBERS = 71, + RESOURCE_ID_LECO_42_NUMBERS = 72, + RESOURCE_ID_LECO_28_LIGHT_NUMBERS = 73, + RESOURCE_ID_LECO_60_NUMBERS_AM_PM = 74, + RESOURCE_ID_LECO_60_BOLD_NUMBERS_AM_PM = 75, + RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD = 76, + RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD = 77, + RESOURCE_ID_MUSIC_ICON_ELLIPSIS = 78, + RESOURCE_ID_MUSIC_ICON_PAUSE = 79, + RESOURCE_ID_MUSIC_ICON_PLAY = 80, + RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE = 81, + RESOURCE_ID_MUSIC_ICON_VOLUME_UP = 82, + RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN = 83, + RESOURCE_ID_MUSIC_LARGE_CASSETTE = 84, + RESOURCE_ID_MUSIC_LARGE_VOLUME_UP = 85, + RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN = 86, + RESOURCE_ID_MUSIC_LARGE_PAUSED = 87, + RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC = 88, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHFACE_ICON = 89, + RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON = 90, + RESOURCE_ID_UNCHECKED_RADIO_BUTTON = 91, + RESOURCE_ID_CHECKED_RADIO_BUTTON = 92, + RESOURCE_ID_ACTION_BAR_ICON_MORE = 93, + RESOURCE_ID_ACTION_BAR_ICON_SNOOZE = 94, + RESOURCE_ID_ACTION_BAR_ICON_PAUSE = 95, + RESOURCE_ID_ACTION_BAR_ICON_START = 96, + RESOURCE_ID_ACTION_BAR_ICON_STOP = 97, + RESOURCE_ID_ACTION_BAR_ICON_TOGGLE = 98, + RESOURCE_ID_ACTION_MENU_FADE_TOP = 99, + RESOURCE_ID_ACTION_MENU_FADE_BOTTOM = 100, + RESOURCE_ID_CHECKBOX_ICON_CHECKED = 101, + RESOURCE_ID_CHECKBOX_ICON_UNCHECKED = 102, + RESOURCE_ID_CHECKMARK_ICON_BLACK = 103, + RESOURCE_ID_CHECKMARK_ICON_DOTTED = 104, + RESOURCE_ID_BLE_HRM_SHARING_TINY = 105, + RESOURCE_ID_BLE_HRM_SHARING_SMALL = 106, + RESOURCE_ID_BLE_HRM_SHARING_LARGE = 107, + RESOURCE_ID_NOTIFICATION_GENERIC_TINY = 108, + RESOURCE_ID_NOTIFICATION_GENERIC_SMALL = 109, + RESOURCE_ID_NOTIFICATION_GENERIC_LARGE = 110, + RESOURCE_ID_MISSED_CALL_TINY = 111, + RESOURCE_ID_MISSED_CALL_SMALL = 112, + RESOURCE_ID_GENERIC_REMINDER_TINY = 113, + RESOURCE_ID_WHATSAPP_NOTIFICATION_TINY = 114, + RESOURCE_ID_WHATSAPP_NOTIFICATION_SMALL = 115, + RESOURCE_ID_TWITTER_NOTIFICATION_TINY = 116, + RESOURCE_ID_TWITTER_NOTIFICATION_SMALL = 117, + RESOURCE_ID_TELEGRAM_APP_TINY = 118, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_TINY = 119, + RESOURCE_ID_GMAIL_NOTIFICATION_TINY = 120, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_TINY = 121, + RESOURCE_ID_FACEBOOK_NOTIFICATION_TINY = 122, + RESOURCE_ID_GENERIC_WEATHER_TINY = 123, + RESOURCE_ID_AUDIO_CASSETTE_TINY = 124, + RESOURCE_ID_SUNNY_DAY_TINY = 125, + RESOURCE_ID_GENERIC_SPORTS_TINY = 126, + RESOURCE_ID_GENERIC_EMAIL_TINY = 127, + RESOURCE_ID_AMERICAN_FOOTBALL_TINY = 128, + RESOURCE_ID_TIMELINE_CALENDAR_TINY = 129, + RESOURCE_ID_BASEBALL_GAME_TINY = 130, + RESOURCE_ID_BASEBALL_GAME_SMALL = 131, + RESOURCE_ID_ALARM_CLOCK_TINY = 132, + RESOURCE_ID_BIRTHDAY_EVENT_TINY = 133, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_TINY = 134, + RESOURCE_ID_CAR_RENTAL_TINY = 135, + RESOURCE_ID_SCHEDULED_FLIGHT_TINY = 136, + RESOURCE_ID_CLOUDY_DAY_TINY = 137, + RESOURCE_ID_CRICKET_GAME_TINY = 138, + RESOURCE_ID_CRICKET_GAME_SMALL = 139, + RESOURCE_ID_CRICKET_GAME_LARGE = 140, + RESOURCE_ID_DINNER_RESERVATION_TINY = 141, + RESOURCE_ID_DISMISSED_PHONE_CALL_TINY = 142, + RESOURCE_ID_GENERIC_CONFIRMATION_TINY = 143, + RESOURCE_ID_GENERIC_PIN_TINY = 144, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_TINY = 145, + RESOURCE_ID_GLUCOSE_MONITOR_TINY = 146, + RESOURCE_ID_HEAVY_RAIN_TINY = 147, + RESOURCE_ID_HEAVY_SNOW_TINY = 148, + RESOURCE_ID_HOCKEY_GAME_TINY = 149, + RESOURCE_ID_HOCKEY_GAME_SMALL = 150, + RESOURCE_ID_HOCKEY_GAME_LARGE = 151, + RESOURCE_ID_HOTEL_RESERVATION_TINY = 152, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_TINY = 153, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_SMALL = 154, + RESOURCE_ID_LIGHT_RAIN_TINY = 155, + RESOURCE_ID_LIGHT_SNOW_TINY = 156, + RESOURCE_ID_MAILBOX_NOTIFICATION_TINY = 157, + RESOURCE_ID_MAILBOX_NOTIFICATION_SMALL = 158, + RESOURCE_ID_MOVIE_EVENT_TINY = 159, + RESOURCE_ID_MUSIC_EVENT_TINY = 160, + RESOURCE_ID_NEWS_EVENT_TINY = 161, + RESOURCE_ID_PARTLY_CLOUDY_TINY = 162, + RESOURCE_ID_PAY_BILL_TINY = 163, + RESOURCE_ID_REACHED_FITNESS_GOAL_TINY = 164, + RESOURCE_ID_RADIO_SHOW_TINY = 165, + RESOURCE_ID_SCHEDULED_EVENT_TINY = 166, + RESOURCE_ID_SOCCER_GAME_TINY = 167, + RESOURCE_ID_SOCCER_GAME_SMALL = 168, + RESOURCE_ID_STOCKS_EVENT_TINY = 169, + RESOURCE_ID_BIRTHDAY_EVENT_SMALL = 170, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_SMALL = 171, + RESOURCE_ID_TIMELINE_CALENDAR_SMALL = 172, + RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_SMALL = 173, + RESOURCE_ID_ALARM_CLOCK_SMALL = 174, + RESOURCE_ID_CAR_RENTAL_SMALL = 175, + RESOURCE_ID_CHECK_INTERNET_CONNECTION_SMALL = 176, + RESOURCE_ID_DINNER_RESERVATION_SMALL = 177, + RESOURCE_ID_DISMISSED_PHONE_CALL_SMALL = 178, + RESOURCE_ID_GENERIC_SMS_SMALL = 179, + RESOURCE_ID_GENERIC_SMS_TINY = 180, + RESOURCE_ID_GENERIC_SMS_LARGE = 181, + RESOURCE_ID_FACEBOOK_NOTIFICATION_SMALL = 182, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_SMALL = 183, + RESOURCE_ID_GENERIC_CONFIRMATION_SMALL = 184, + RESOURCE_ID_GENERIC_EMAIL_SMALL = 185, + RESOURCE_ID_GENERIC_PIN_SMALL = 186, + RESOURCE_ID_GENERIC_REMINDER_SMALL = 187, + RESOURCE_ID_GENERIC_WARNING_SMALL = 188, + RESOURCE_ID_GENERIC_WEATHER_SMALL = 189, + RESOURCE_ID_GLUCOSE_MONITOR_SMALL = 190, + RESOURCE_ID_GMAIL_NOTIFICATION_SMALL = 191, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_SMALL = 192, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_SMALL = 193, + RESOURCE_ID_HEAVY_RAIN_SMALL = 194, + RESOURCE_ID_HEAVY_SNOW_SMALL = 195, + RESOURCE_ID_HOTEL_RESERVATION_SMALL = 196, + RESOURCE_ID_LIGHT_RAIN_SMALL = 197, + RESOURCE_ID_LIGHT_SNOW_SMALL = 198, + RESOURCE_ID_MOVIE_EVENT_SMALL = 199, + RESOURCE_ID_MUSIC_EVENT_SMALL = 200, + RESOURCE_ID_NEWS_EVENT_SMALL = 201, + RESOURCE_ID_PARTLY_CLOUDY_SMALL = 202, + RESOURCE_ID_PAY_BILL_SMALL = 203, + RESOURCE_ID_RADIO_SHOW_SMALL = 204, + RESOURCE_ID_REACHED_FITNESS_GOAL_SMALL = 205, + RESOURCE_ID_SCHEDULED_EVENT_SMALL = 206, + RESOURCE_ID_SUNNY_DAY_SMALL = 207, + RESOURCE_ID_GENERIC_SPORTS_SMALL = 208, + RESOURCE_ID_TIDE_IS_HIGH_SMALL = 209, + RESOURCE_ID_WATCH_DISCONNECTED_SMALL = 210, + RESOURCE_ID_ALARM_CLOCK_LARGE_STATIC = 211, + RESOURCE_ID_AMERICAN_FOOTBALL_LARGE = 212, + RESOURCE_ID_AMERICAN_FOOTBALL_SMALL = 213, + RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_LARGE = 214, + RESOURCE_ID_WHATSAPP_NOTIFICATION_LARGE = 215, + RESOURCE_ID_AUDIO_CASSETTE_LARGE = 216, + RESOURCE_ID_AUDIO_CASSETTE_SMALL = 217, + RESOURCE_ID_BASEBALL_GAME_LARGE = 218, + RESOURCE_ID_BIRTHDAY_EVENT_LARGE = 219, + RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_LARGE = 220, + RESOURCE_ID_TIMELINE_CALENDAR_LARGE = 221, + RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_LARGE = 222, + RESOURCE_ID_CAR_RENTAL_LARGE = 223, + RESOURCE_ID_CLOUDY_DAY_LARGE = 224, + RESOURCE_ID_CLOUDY_DAY_SMALL = 225, + RESOURCE_ID_DAY_SEPARATOR_TINY = 226, + RESOURCE_ID_DAY_SEPARATOR_SMALL = 227, + RESOURCE_ID_DAY_SEPARATOR_LARGE = 228, + RESOURCE_ID_RESULT_DELETED_TINY = 229, + RESOURCE_ID_RESULT_DELETED_SMALL = 230, + RESOURCE_ID_DINNER_RESERVATION_LARGE = 231, + RESOURCE_ID_DISMISSED_PHONE_CALL_LARGE = 232, + RESOURCE_ID_DURING_PHONE_CALL_TINY = 233, + RESOURCE_ID_DURING_PHONE_CALL_SMALL = 234, + RESOURCE_ID_DURING_PHONE_CALL_LARGE = 235, + RESOURCE_ID_DURING_PHONE_CALL_CENTERED_LARGE = 236, + RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_LARGE = 237, + RESOURCE_ID_FACEBOOK_NOTIFICATION_LARGE = 238, + RESOURCE_ID_GENERIC_EMAIL_LARGE = 239, + RESOURCE_ID_GENERIC_PIN_LARGE = 240, + RESOURCE_ID_GENERIC_REMINDER_LARGE = 241, + RESOURCE_ID_GENERIC_SPORTS_LARGE = 242, + RESOURCE_ID_GENERIC_WEATHER_LARGE = 243, + RESOURCE_ID_GLUCOSE_MONITOR_LARGE = 244, + RESOURCE_ID_GMAIL_NOTIFICATION_LARGE = 245, + RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_LARGE = 246, + RESOURCE_ID_HEAVY_RAIN_LARGE = 247, + RESOURCE_ID_HEAVY_SNOW_LARGE = 248, + RESOURCE_ID_HOTEL_RESERVATION_LARGE = 249, + RESOURCE_ID_INSTAGRAM_NOTIFICATION_LARGE = 250, + RESOURCE_ID_LIGHT_RAIN_LARGE = 251, + RESOURCE_ID_LIGHT_SNOW_LARGE = 252, + RESOURCE_ID_MAILBOX_NOTIFICATION_LARGE = 253, + RESOURCE_ID_MISSED_CALL_LARGE = 254, + RESOURCE_ID_MOVIE_EVENT_LARGE = 255, + RESOURCE_ID_MUSIC_EVENT_LARGE = 256, + RESOURCE_ID_NEWS_EVENT_LARGE = 257, + RESOURCE_ID_PARTLY_CLOUDY_LARGE = 258, + RESOURCE_ID_PAY_BILL_LARGE = 259, + RESOURCE_ID_RADIO_SHOW_LARGE = 260, + RESOURCE_ID_REACHED_FITNESS_GOAL_LARGE = 261, + RESOURCE_ID_SCHEDULED_EVENT_LARGE = 262, + RESOURCE_ID_SCHEDULED_FLIGHT_LARGE = 263, + RESOURCE_ID_SCHEDULED_FLIGHT_SMALL = 264, + RESOURCE_ID_RESULT_SENT_TINY = 265, + RESOURCE_ID_RESULT_SENT_SMALL = 266, + RESOURCE_ID_SOCCER_GAME_LARGE = 267, + RESOURCE_ID_STOCKS_EVENT_LARGE = 268, + RESOURCE_ID_STOCKS_EVENT_SMALL = 269, + RESOURCE_ID_SUNNY_DAY_LARGE = 270, + RESOURCE_ID_TELEGRAM_APP_LARGE = 271, + RESOURCE_ID_TELEGRAM_APP_SMALL = 272, + RESOURCE_ID_TIDE_IS_HIGH_LARGE = 273, + RESOURCE_ID_TWITTER_NOTIFICATION_LARGE = 274, + RESOURCE_ID_RESULT_FAILED_TINY = 275, + RESOURCE_ID_RESULT_FAILED_SMALL = 276, + RESOURCE_ID_RESULT_FAILED_LARGE = 277, + RESOURCE_ID_GENERIC_QUESTION_TINY = 278, + RESOURCE_ID_GENERIC_QUESTION_SMALL = 279, + RESOURCE_ID_GENERIC_QUESTION_LARGE = 280, + RESOURCE_ID_RAINING_AND_SNOWING_TINY = 281, + RESOURCE_ID_RAINING_AND_SNOWING_SMALL = 282, + RESOURCE_ID_RAINING_AND_SNOWING_LARGE = 283, + RESOURCE_ID_NOTIFICATION_FACETIME_TINY = 284, + RESOURCE_ID_NOTIFICATION_FACETIME_SMALL = 285, + RESOURCE_ID_NOTIFICATION_FACETIME_LARGE = 286, + RESOURCE_ID_NOTIFICATION_LINE_TINY = 287, + RESOURCE_ID_NOTIFICATION_LINE_SMALL = 288, + RESOURCE_ID_NOTIFICATION_LINE_LARGE = 289, + RESOURCE_ID_NOTIFICATION_SKYPE_TINY = 290, + RESOURCE_ID_NOTIFICATION_SKYPE_SMALL = 291, + RESOURCE_ID_NOTIFICATION_SKYPE_LARGE = 292, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_TINY = 293, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_SMALL = 294, + RESOURCE_ID_NOTIFICATION_SNAPCHAT_LARGE = 295, + RESOURCE_ID_NOTIFICATION_VIBER_TINY = 296, + RESOURCE_ID_NOTIFICATION_VIBER_SMALL = 297, + RESOURCE_ID_NOTIFICATION_VIBER_LARGE = 298, + RESOURCE_ID_NOTIFICATION_WECHAT_TINY = 299, + RESOURCE_ID_NOTIFICATION_WECHAT_SMALL = 300, + RESOURCE_ID_NOTIFICATION_WECHAT_LARGE = 301, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_TINY = 302, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_SMALL = 303, + RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_LARGE = 304, + RESOURCE_ID_TV_SHOW_TINY = 305, + RESOURCE_ID_TV_SHOW_SMALL = 306, + RESOURCE_ID_TV_SHOW_LARGE = 307, + RESOURCE_ID_END_OF_TIMELINE = 308, + RESOURCE_ID_BASKETBALL_TINY = 309, + RESOURCE_ID_BASKETBALL_SMALL = 310, + RESOURCE_ID_BASKETBALL_LARGE = 311, + RESOURCE_ID_RESULT_DISMISSED_TINY = 312, + RESOURCE_ID_RESULT_DISMISSED_SMALL = 313, + RESOURCE_ID_TIDE_IS_HIGH_TINY = 314, + RESOURCE_ID_OUTGOING_CALL_LARGE = 315, + RESOURCE_ID_NOTIFICATION_HIPCHAT_TINY = 316, + RESOURCE_ID_NOTIFICATION_HIPCHAT_SMALL = 317, + RESOURCE_ID_NOTIFICATION_HIPCHAT_LARGE = 318, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_TINY = 319, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_SMALL = 320, + RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_LARGE = 321, + RESOURCE_ID_INCOMING_PHONE_CALL_TINY = 322, + RESOURCE_ID_INCOMING_PHONE_CALL_SMALL = 323, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_TINY = 324, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_SMALL = 325, + RESOURCE_ID_NOTIFICATION_KAKAOTALK_LARGE = 326, + RESOURCE_ID_NOTIFICATION_KIK_TINY = 327, + RESOURCE_ID_NOTIFICATION_KIK_SMALL = 328, + RESOURCE_ID_NOTIFICATION_KIK_LARGE = 329, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_TINY = 330, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_SMALL = 331, + RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_LARGE = 332, + RESOURCE_ID_LOCATION_TINY = 333, + RESOURCE_ID_LOCATION_SMALL = 334, + RESOURCE_ID_LOCATION_LARGE = 335, + RESOURCE_ID_SUNSET_TINY = 336, + RESOURCE_ID_SUNSET_SMALL = 337, + RESOURCE_ID_SUNSET_LARGE = 338, + RESOURCE_ID_SUNRISE_TINY = 339, + RESOURCE_ID_SUNRISE_SMALL = 340, + RESOURCE_ID_SUNRISE_LARGE = 341, + RESOURCE_ID_SETTINGS_TINY = 342, + RESOURCE_ID_SETTINGS_SMALL = 343, + RESOURCE_ID_SETTINGS_LARGE = 344, + RESOURCE_ID_PLUS_ICON_BLACK = 345, + RESOURCE_ID_PLUS_ICON_DOTTED = 346, + RESOURCE_ID_RESULT_SHREDDED_TINY = 347, + RESOURCE_ID_RESULT_SHREDDED_SMALL = 348, + RESOURCE_ID_QUIET_TIME_MOUSE = 349, + RESOURCE_ID_REMINDER_SNOOZE = 350, + RESOURCE_ID_QUIET_TIME_STATUS_BAR = 351, + RESOURCE_ID_QUICK_DISMISS = 352, + RESOURCE_ID_THUMBS_UP_SMALL = 353, + RESOURCE_ID_THUMBS_UP_LARGE = 354, + RESOURCE_ID_ARROW_UP_SMALL = 355, + RESOURCE_ID_ARROW_DOWN_SMALL = 356, + RESOURCE_ID_ACTIVITY_TINY = 357, + RESOURCE_ID_ACTIVITY_SMALL = 358, + RESOURCE_ID_ACTIVITY_LARGE = 359, + RESOURCE_ID_SLEEP_TINY = 360, + RESOURCE_ID_SLEEP_SMALL = 361, + RESOURCE_ID_SLEEP_LARGE = 362, + RESOURCE_ID_REWARD_AVERAGE_LARGE = 363, + RESOURCE_ID_REWARD_GOOD_LARGE = 364, + RESOURCE_ID_REWARD_BAD_LARGE = 365, + RESOURCE_ID_CALORIES_TINY = 366, + RESOURCE_ID_DISTANCE_TINY = 367, + RESOURCE_ID_DURATION_TINY = 368, + RESOURCE_ID_PACE_TINY = 369, + RESOURCE_ID_RUN_TINY = 370, + RESOURCE_ID_RUN_LARGE = 371, + RESOURCE_ID_MOON_TINY = 372, + RESOURCE_ID_NOTIFICATION_AMAZON_TINY = 373, + RESOURCE_ID_NOTIFICATION_AMAZON_SMALL = 374, + RESOURCE_ID_NOTIFICATION_AMAZON_LARGE = 375, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_TINY = 376, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_SMALL = 377, + RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_LARGE = 378, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_TINY = 379, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_SMALL = 380, + RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_LARGE = 381, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_TINY = 382, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_SMALL = 383, + RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_LARGE = 384, + RESOURCE_ID_NOTIFICATION_LINKEDIN_TINY = 385, + RESOURCE_ID_NOTIFICATION_LINKEDIN_SMALL = 386, + RESOURCE_ID_NOTIFICATION_LINKEDIN_LARGE = 387, + RESOURCE_ID_NOTIFICATION_SLACK_TINY = 388, + RESOURCE_ID_NOTIFICATION_SLACK_SMALL = 389, + RESOURCE_ID_NOTIFICATION_SLACK_LARGE = 390, + RESOURCE_ID_NOTIFICATION_AIRMAIL_TINY = 391, + RESOURCE_ID_NOTIFICATION_AIRMAIL_SMALL = 392, + RESOURCE_ID_NOTIFICATION_AIRMAIL_LARGE = 393, + RESOURCE_ID_NOTIFICATION_BEEPER_TINY = 394, + RESOURCE_ID_NOTIFICATION_BEEPER_SMALL = 395, + RESOURCE_ID_NOTIFICATION_BEEPER_LARGE = 396, + RESOURCE_ID_NOTIFICATION_BLUESKY_TINY = 397, + RESOURCE_ID_NOTIFICATION_BLUESKY_SMALL = 398, + RESOURCE_ID_NOTIFICATION_BLUESKY_LARGE = 399, + RESOURCE_ID_NOTIFICATION_DISCORD_TINY = 400, + RESOURCE_ID_NOTIFICATION_DISCORD_SMALL = 401, + RESOURCE_ID_NOTIFICATION_DISCORD_LARGE = 402, + RESOURCE_ID_NOTIFICATION_DUOLINGO_TINY = 403, + RESOURCE_ID_NOTIFICATION_DUOLINGO_SMALL = 404, + RESOURCE_ID_NOTIFICATION_DUOLINGO_LARGE = 405, + RESOURCE_ID_NOTIFICATION_EBAY_TINY = 406, + RESOURCE_ID_NOTIFICATION_EBAY_SMALL = 407, + RESOURCE_ID_NOTIFICATION_EBAY_LARGE = 408, + RESOURCE_ID_NOTIFICATION_ELEMENT_TINY = 409, + RESOURCE_ID_NOTIFICATION_ELEMENT_SMALL = 410, + RESOURCE_ID_NOTIFICATION_ELEMENT_LARGE = 411, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_TINY = 412, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_SMALL = 413, + RESOURCE_ID_NOTIFICATION_GOOGLE_CHAT_LARGE = 414, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_TINY = 415, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_SMALL = 416, + RESOURCE_ID_NOTIFICATION_GOOGLE_TASKS_LARGE = 417, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_TINY = 418, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_SMALL = 419, + RESOURCE_ID_NOTIFICATION_HOME_ASSISTANT_LARGE = 420, + RESOURCE_ID_NOTIFICATION_OUTLOOK_TINY = 421, + RESOURCE_ID_NOTIFICATION_OUTLOOK_SMALL = 422, + RESOURCE_ID_NOTIFICATION_OUTLOOK_LARGE = 423, + RESOURCE_ID_NOTIFICATION_SIGNAL_TINY = 424, + RESOURCE_ID_NOTIFICATION_SIGNAL_SMALL = 425, + RESOURCE_ID_NOTIFICATION_SIGNAL_LARGE = 426, + RESOURCE_ID_NOTIFICATION_STEAM_TINY = 427, + RESOURCE_ID_NOTIFICATION_STEAM_SMALL = 428, + RESOURCE_ID_NOTIFICATION_STEAM_LARGE = 429, + RESOURCE_ID_NOTIFICATION_TEAMS_TINY = 430, + RESOURCE_ID_NOTIFICATION_TEAMS_SMALL = 431, + RESOURCE_ID_NOTIFICATION_TEAMS_LARGE = 432, + RESOURCE_ID_NOTIFICATION_THREADS_TINY = 433, + RESOURCE_ID_NOTIFICATION_THREADS_SMALL = 434, + RESOURCE_ID_NOTIFICATION_THREADS_LARGE = 435, + RESOURCE_ID_NOTIFICATION_TWITCH_TINY = 436, + RESOURCE_ID_NOTIFICATION_TWITCH_SMALL = 437, + RESOURCE_ID_NOTIFICATION_TWITCH_LARGE = 438, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_TINY = 439, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_SMALL = 440, + RESOURCE_ID_NOTIFICATION_UNIFI_PROTECT_LARGE = 441, + RESOURCE_ID_NOTIFICATION_YOUTUBE_TINY = 442, + RESOURCE_ID_NOTIFICATION_YOUTUBE_SMALL = 443, + RESOURCE_ID_NOTIFICATION_YOUTUBE_LARGE = 444, + RESOURCE_ID_NOTIFICATION_ZOOM_TINY = 445, + RESOURCE_ID_NOTIFICATION_ZOOM_SMALL = 446, + RESOURCE_ID_NOTIFICATION_ZOOM_LARGE = 447, + RESOURCE_ID_NOTIFICATION_REDDIT_TINY = 448, + RESOURCE_ID_NOTIFICATION_REDDIT_SMALL = 449, + RESOURCE_ID_NOTIFICATION_REDDIT_LARGE = 450, + RESOURCE_ID_NOTIFICATION_SWARM_TINY = 451, + RESOURCE_ID_NOTIFICATION_SWARM_SMALL = 452, + RESOURCE_ID_NOTIFICATION_SWARM_LARGE = 453, + RESOURCE_ID_NOTIFICATION_TAPO_TINY = 454, + RESOURCE_ID_NOTIFICATION_TAPO_SMALL = 455, + RESOURCE_ID_NOTIFICATION_TAPO_LARGE = 456, + RESOURCE_ID_STRINGS_CA_ES = 457, + RESOURCE_ID_STRINGS_DE_DE = 458, + RESOURCE_ID_STRINGS_ES_ES = 459, + RESOURCE_ID_STRINGS_FR_FR = 460, + RESOURCE_ID_STRINGS_IT_IT = 461, + RESOURCE_ID_STRINGS_NL_NL = 462, + RESOURCE_ID_STRINGS_PT_PT = 463, + RESOURCE_ID_SMART_ALARM_TINY = 464, + RESOURCE_ID_HEALTH_APP_ACTIVITY = 465, + RESOURCE_ID_HEALTH_APP_SLEEP = 466, + RESOURCE_ID_HEALTH_APP_CROWN = 467, + RESOURCE_ID_HEALTH_APP_HR = 468, + RESOURCE_ID_HEALTH_APP_PULSING_HEART = 469, + RESOURCE_ID_WORKOUT_APP_WALK = 470, + RESOURCE_ID_WORKOUT_APP_WALK_SMALL = 471, + RESOURCE_ID_WORKOUT_APP_WALK_TINY = 472, + RESOURCE_ID_WORKOUT_APP_RUN = 473, + RESOURCE_ID_WORKOUT_APP_RUN_SMALL = 474, + RESOURCE_ID_WORKOUT_APP_RUN_TINY = 475, + RESOURCE_ID_WORKOUT_APP_WORKOUT = 476, + RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL = 477, + RESOURCE_ID_WORKOUT_APP_DETECTED = 478, + RESOURCE_ID_WORKOUT_APP_HEART = 479, + RESOURCE_ID_WORKOUT_APP_MEASURING_HR = 480, + RESOURCE_ID_WORKOUT_APP_HR_PULSE_TINY = 481, + RESOURCE_ID_WORKOUT_APP_END = 482, + RESOURCE_ID_WORKOUT_APP_ONE = 483, + RESOURCE_ID_WORKOUT_APP_TWO = 484, + RESOURCE_ID_WORKOUT_APP_THREE = 485, + RESOURCE_ID_HEART_TINY = 486, + RESOURCE_ID_HEART_SMALL = 487, + RESOURCE_ID_HEART_LARGE = 488, + RESOURCE_ID_BACKLIGHT = 489, + RESOURCE_ID_AIRPLANE = 490, + RESOURCE_ID_MODAL_CONTRACT_TO_MODAL_SEQUENCE = 491, + RESOURCE_ID_MODAL_CONTRACT_FROM_MODAL_SEQUENCE = 492, + RESOURCE_ID_MODAL_EXPAND_TO_APP_SEQUENCE = 493, + RESOURCE_ID_NO_EVENTS_LARGE = 494, + RESOURCE_ID_TIMER_APP_FACE_ICON = 495, + RESOURCE_ID_STOPWATCH_APP_FACE_ICON = 496, + RESOURCE_ID_ESPN_APP_FACE_ICON = 497, + RESOURCE_ID_HEALTH_APP_FACE_ICON = 498, + RESOURCE_ID_SEND_TEXT_APP_FACE_ICON = 499, + RESOURCE_ID_QUIET_TIME_ACTIVE = 500, + RESOURCE_ID_BATTERY_CHARGING_ICON = 501, + RESOURCE_ID_HEALTH_ICON_MOON = 502, + RESOURCE_ID_HEALTH_ICON_ROTATED_MOON = 503, + RESOURCE_ID_STRIDE_HEART = 504, + RESOURCE_ID_STRIDE_SHOE_GREEN = 505, + RESOURCE_ID_STRIDE_SHOE_BLUE = 506, + RESOURCE_ID_SYSTEM_FCC_MARK = 507, + RESOURCE_ID_SYSTEM_KCC_MARK = 508, + RESOURCE_ID_SYSTEM_CE_MARK = 509, + RESOURCE_ID_SYSTEM_WEEE_MARK = 510, + RESOURCE_ID_SYSTEM_UKCA_MARK = 511, + RESOURCE_ID_SYSTEM_R_MARK = 512, + RESOURCE_ID_SYSTEM_T_MARK = 513, + RESOURCE_ID_SYSTEM_AUS_RCM_MARK = 514, + RESOURCE_ID_SYSTEM_NOM_NYCE_MARK = 515, + RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE = 516, + RESOURCE_ID_EMOJI_BIG_SMILE_LARGE = 517, + RESOURCE_ID_EMOJI_HEART_LARGE = 518, + RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE = 519, + RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE = 520, + RESOURCE_ID_EMOJI_SAD_LARGE = 521, + RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE = 522, + RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE = 523, + RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE = 524, + RESOURCE_ID_EMOJI_THUMBS_UP_LARGE = 525, + RESOURCE_ID_EMOJI_WINK_LARGE = 526, + RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE = 527, + RESOURCE_ID_VIBE_SCORE_REVEILLE = 528, + RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK = 529, + RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE = 530, + RESOURCE_ID_VIBE_SCORE_PULSE = 531, + RESOURCE_ID_VIBE_SCORE_JACKHAMMER = 532, + RESOURCE_ID_VIBE_SCORE_MARIO = 533, + RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW = 534, + RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH = 535, + RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW = 536, + RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH = 537, + RESOURCE_ID_VIBE_SCORE_ALARM_LPM = 538, + RESOURCE_ID_VIBE_SCORE_GENTLE = 539, + RESOURCE_ID_SMART_ALARM_ICON_BLACK = 540, + RESOURCE_ID_BLE_HRM_SHARE_REQUEST_LARGE = 541, + RESOURCE_ID_BLE_HRM_SHARED = 542, + RESOURCE_ID_BLE_HRM_NOT_SHARED = 543, + RESOURCE_ID_CONNECTIVITY_SHARING_HRM = 544, + RESOURCE_ID_MENU_ICON_HEALTH = 545, + RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM = 546, + RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM = 547, + RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM = 548, + RESOURCE_ID_STORED_APP_GOLF = 549, + RESOURCE_ID_TIMEZONE_DATABASE = 550, + RESOURCE_ID_ACTION_BAR_ICON_CHECK = 551, + RESOURCE_ID_GENERIC_WARNING_LARGE = 552, + RESOURCE_ID_FONT_FALLBACK_INTERNAL = 553, + RESOURCE_ID_ACTION_BAR_ICON_UP = 554, + RESOURCE_ID_ACTION_BAR_ICON_DOWN = 555, + RESOURCE_ID_GENERIC_WARNING_TINY = 556, + RESOURCE_ID_CHECK_INTERNET_CONNECTION_LARGE = 557, + RESOURCE_ID_WATCH_DISCONNECTED_LARGE = 558, + RESOURCE_ID_ARROW_DOWN = 559, + RESOURCE_ID_VOICE_MICROPHONE_LARGE = 560, + RESOURCE_ID_ALARM_CLOCK_LARGE = 561, + RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE = 562, + RESOURCE_ID_RESULT_DELETED_LARGE = 563, + RESOURCE_ID_RESULT_SHREDDED_LARGE = 564, + RESOURCE_ID_RESULT_DISMISSED_LARGE = 565, + RESOURCE_ID_GENERIC_CONFIRMATION_LARGE = 566, + RESOURCE_ID_INCOMING_PHONE_CALL_LARGE = 567, + RESOURCE_ID_RESULT_MUTE_LARGE = 568, + RESOURCE_ID_RESULT_SENT_LARGE = 569, + RESOURCE_ID_RESULT_UNMUTE_LARGE = 570, + RESOURCE_ID_PUG = 571, + RESOURCE_ID_STRINGS = 572, + RESOURCE_ID_GOTHIC_14_EXTENDED = 573, + RESOURCE_ID_GOTHIC_14_BOLD_EXTENDED = 574, + RESOURCE_ID_GOTHIC_18_EXTENDED = 575, + RESOURCE_ID_GOTHIC_18_BOLD_EXTENDED = 576, + RESOURCE_ID_GOTHIC_24_EXTENDED = 577, + RESOURCE_ID_GOTHIC_24_BOLD_EXTENDED = 578, + RESOURCE_ID_GOTHIC_28_EXTENDED = 579, + RESOURCE_ID_GOTHIC_28_BOLD_EXTENDED = 580, + RESOURCE_ID_BITHAM_18_LIGHT_SUBSET_EXTENDED = 581, + RESOURCE_ID_BITHAM_30_BLACK_EXTENDED = 582, + RESOURCE_ID_BITHAM_34_LIGHT_SUBSET_EXTENDED = 583, + RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS_EXTENDED = 584, + RESOURCE_ID_BITHAM_42_BOLD_EXTENDED = 585, + RESOURCE_ID_BITHAM_42_LIGHT_EXTENDED = 586, + RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS_EXTENDED = 587, + RESOURCE_ID_ROBOTO_CONDENSED_21_EXTENDED = 588, + RESOURCE_ID_ROBOTO_BOLD_SUBSET_49_EXTENDED = 589, + RESOURCE_ID_DROID_SERIF_28_BOLD_EXTENDED = 590, + RESOURCE_ID_GOTHIC_09_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_14_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_18_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_24_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_28_EMOJI_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_36_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_GOTHIC_36_BOLD_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_20_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_32_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_36_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_38_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_42_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_28_LIGHT_NUMBERS_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_60_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_LECO_60_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, + RESOURCE_ID_FONT_FALLBACK_INTERNAL_EXTENDED = INVALID_RESOURCE, +} ResourceId; \ No newline at end of file diff --git a/tests/overrides/default/resources/obelix/resource/resource_version.auto.h b/tests/overrides/default/resources/obelix/resource/resource_version.auto.h new file mode 100644 index 0000000000..d53e6819dc --- /dev/null +++ b/tests/overrides/default/resources/obelix/resource/resource_version.auto.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED +// DO NOT MODIFY +// + +static const ResourceVersion SYSTEM_RESOURCE_VERSION = { + .crc = 448372378, + .timestamp = 0 +}; diff --git a/tests/overrides/default/resources/obelix/resource/timeline_resource_ids.auto.h b/tests/overrides/default/resources/obelix/resource/timeline_resource_ids.auto.h new file mode 100644 index 0000000000..b1721dc200 --- /dev/null +++ b/tests/overrides/default/resources/obelix/resource/timeline_resource_ids.auto.h @@ -0,0 +1,146 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +// +// AUTOGENERATED +// DO NOT MODIFY +// + +typedef enum { + TIMELINE_RESOURCE_INVALID = 0, + TIMELINE_RESOURCE_NOTIFICATION_GENERIC = 0x80000001, + TIMELINE_RESOURCE_TIMELINE_MISSED_CALL = 0x80000002, + TIMELINE_RESOURCE_NOTIFICATION_REMINDER = 0x80000003, + TIMELINE_RESOURCE_NOTIFICATION_FLAG = 0x80000004, + TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP = 0x80000005, + TIMELINE_RESOURCE_NOTIFICATION_TWITTER = 0x80000006, + TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM = 0x80000007, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS = 0x80000008, + TIMELINE_RESOURCE_NOTIFICATION_GMAIL = 0x80000009, + TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER = 0x8000000a, + TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK = 0x8000000b, + TIMELINE_RESOURCE_AUDIO_CASSETTE = 0x8000000c, + TIMELINE_RESOURCE_ALARM_CLOCK = 0x8000000d, + TIMELINE_RESOURCE_TIMELINE_WEATHER = 0x8000000e, + TIMELINE_RESOURCE_TIMELINE_SUN = 0x80000010, + TIMELINE_RESOURCE_TIMELINE_SPORTS = 0x80000011, + TIMELINE_RESOURCE_GENERIC_EMAIL = 0x80000013, + TIMELINE_RESOURCE_AMERICAN_FOOTBALL = 0x80000014, + TIMELINE_RESOURCE_TIMELINE_CALENDAR = 0x80000015, + TIMELINE_RESOURCE_TIMELINE_BASEBALL = 0x80000016, + TIMELINE_RESOURCE_BIRTHDAY_EVENT = 0x80000017, + TIMELINE_RESOURCE_CAR_RENTAL = 0x80000018, + TIMELINE_RESOURCE_CLOUDY_DAY = 0x80000019, + TIMELINE_RESOURCE_CRICKET_GAME = 0x8000001a, + TIMELINE_RESOURCE_DINNER_RESERVATION = 0x8000001b, + TIMELINE_RESOURCE_GENERIC_WARNING = 0x8000001c, + TIMELINE_RESOURCE_GLUCOSE_MONITOR = 0x8000001d, + TIMELINE_RESOURCE_HOCKEY_GAME = 0x8000001e, + TIMELINE_RESOURCE_HOTEL_RESERVATION = 0x8000001f, + TIMELINE_RESOURCE_LIGHT_RAIN = 0x80000020, + TIMELINE_RESOURCE_LIGHT_SNOW = 0x80000021, + TIMELINE_RESOURCE_MOVIE_EVENT = 0x80000022, + TIMELINE_RESOURCE_MUSIC_EVENT = 0x80000023, + TIMELINE_RESOURCE_NEWS_EVENT = 0x80000024, + TIMELINE_RESOURCE_PARTLY_CLOUDY = 0x80000025, + TIMELINE_RESOURCE_PAY_BILL = 0x80000026, + TIMELINE_RESOURCE_RADIO_SHOW = 0x80000027, + TIMELINE_RESOURCE_SCHEDULED_EVENT = 0x80000028, + TIMELINE_RESOURCE_SOCCER_GAME = 0x80000029, + TIMELINE_RESOURCE_STOCKS_EVENT = 0x8000002a, + TIMELINE_RESOURCE_RESULT_DELETED = 0x8000002b, + TIMELINE_RESOURCE_CHECK_INTERNET_CONNECTION = 0x8000002c, + TIMELINE_RESOURCE_GENERIC_SMS = 0x8000002d, + TIMELINE_RESOURCE_RESULT_MUTE = 0x8000002e, + TIMELINE_RESOURCE_RESULT_SENT = 0x8000002f, + TIMELINE_RESOURCE_WATCH_DISCONNECTED = 0x80000030, + TIMELINE_RESOURCE_DURING_PHONE_CALL = 0x80000031, + TIMELINE_RESOURCE_TIDE_IS_HIGH = 0x80000032, + TIMELINE_RESOURCE_RESULT_DISMISSED = 0x80000033, + TIMELINE_RESOURCE_HEAVY_RAIN = 0x80000034, + TIMELINE_RESOURCE_HEAVY_SNOW = 0x80000035, + TIMELINE_RESOURCE_SCHEDULED_FLIGHT = 0x80000036, + TIMELINE_RESOURCE_GENERIC_CONFIRMATION = 0x80000037, + TIMELINE_RESOURCE_DAY_SEPARATOR = 0x80000038, + TIMELINE_RESOURCE_NO_EVENTS = 0x80000039, + TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER = 0x8000003a, + TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM = 0x8000003b, + TIMELINE_RESOURCE_NOTIFICATION_MAILBOX = 0x8000003c, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX = 0x8000003d, + TIMELINE_RESOURCE_RESULT_FAILED = 0x8000003e, + TIMELINE_RESOURCE_GENERIC_QUESTION = 0x8000003f, + TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK = 0x80000040, + TIMELINE_RESOURCE_RAINING_AND_SNOWING = 0x80000041, + TIMELINE_RESOURCE_REACHED_FITNESS_GOAL = 0x80000042, + TIMELINE_RESOURCE_NOTIFICATION_LINE = 0x80000043, + TIMELINE_RESOURCE_NOTIFICATION_SKYPE = 0x80000044, + TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT = 0x80000045, + TIMELINE_RESOURCE_NOTIFICATION_VIBER = 0x80000046, + TIMELINE_RESOURCE_NOTIFICATION_WECHAT = 0x80000047, + TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL = 0x80000048, + TIMELINE_RESOURCE_TV_SHOW = 0x80000049, + TIMELINE_RESOURCE_BASKETBALL = 0x8000004a, + TIMELINE_RESOURCE_DISMISSED_PHONE_CALL = 0x8000004b, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MESSENGER = 0x8000004c, + TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT = 0x8000004d, + TIMELINE_RESOURCE_INCOMING_PHONE_CALL = 0x8000004e, + TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK = 0x8000004f, + TIMELINE_RESOURCE_NOTIFICATION_KIK = 0x80000050, + TIMELINE_RESOURCE_NOTIFICATION_LIGHTHOUSE = 0x80000051, + TIMELINE_RESOURCE_LOCATION = 0x80000052, + TIMELINE_RESOURCE_SETTINGS = 0x80000053, + TIMELINE_RESOURCE_SUNRISE = 0x80000054, + TIMELINE_RESOURCE_SUNSET = 0x80000055, + TIMELINE_RESOURCE_RESULT_UNMUTE = 0x80000056, + TIMELINE_RESOURCE_RESULT_UNMUTE_ALT = 0x8000005e, + TIMELINE_RESOURCE_DURING_PHONE_CALL_CENTERED = 0x8000005f, + TIMELINE_RESOURCE_TIMELINE_EMPTY_CALENDAR = 0x80000060, + TIMELINE_RESOURCE_THUMBS_UP = 0x80000061, + TIMELINE_RESOURCE_ARROW_UP = 0x80000062, + TIMELINE_RESOURCE_ARROW_DOWN = 0x80000063, + TIMELINE_RESOURCE_ACTIVITY = 0x80000064, + TIMELINE_RESOURCE_SLEEP = 0x80000065, + TIMELINE_RESOURCE_REWARD_BAD = 0x80000066, + TIMELINE_RESOURCE_REWARD_GOOD = 0x80000067, + TIMELINE_RESOURCE_REWARD_AVERAGE = 0x80000068, + TIMELINE_RESOURCE_CALORIES = 0x80000069, + TIMELINE_RESOURCE_DISTANCE = 0x8000006a, + TIMELINE_RESOURCE_DURATION = 0x8000006b, + TIMELINE_RESOURCE_PACE = 0x8000006c, + TIMELINE_RESOURCE_RUN = 0x8000006d, + TIMELINE_RESOURCE_NOTIFICATION_FACETIME = 0x8000006e, + TIMELINE_RESOURCE_NOTIFICATION_AMAZON = 0x8000006f, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS = 0x80000070, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS = 0x80000071, + TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS = 0x80000072, + TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN = 0x80000073, + TIMELINE_RESOURCE_NOTIFICATION_SLACK = 0x80000074, + TIMELINE_RESOURCE_SMART_ALARM = 0x80000075, + TIMELINE_RESOURCE_HEART = 0x80000076, + TIMELINE_RESOURCE_BLE_HRM_SHARING = 0x80000077, + TIMELINE_RESOURCE_NOTIFICATION_BEEPER = 0x80000078, + TIMELINE_RESOURCE_NOTIFICATION_DISCORD = 0x80000079, + TIMELINE_RESOURCE_NOTIFICATION_BLUESKY = 0x8000007a, + TIMELINE_RESOURCE_NOTIFICATION_DUOLINGO = 0x8000007b, + TIMELINE_RESOURCE_NOTIFICATION_ELEMENT = 0x8000007c, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_CHAT = 0x8000007d, + TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_TASKS = 0x8000007e, + TIMELINE_RESOURCE_NOTIFICATION_HOME_ASSISTANT = 0x8000007f, + TIMELINE_RESOURCE_NOTIFICATION_STEAM = 0x80000080, + TIMELINE_RESOURCE_NOTIFICATION_TEAMS = 0x80000081, + TIMELINE_RESOURCE_NOTIFICATION_THREADS = 0x80000082, + TIMELINE_RESOURCE_NOTIFICATION_UNIFI_PROTECT = 0x80000083, + TIMELINE_RESOURCE_NOTIFICATION_ZOOM = 0x80000084, + TIMELINE_RESOURCE_NOTIFICATION_EBAY = 0x80000085, + TIMELINE_RESOURCE_NOTIFICATION_YOUTUBE = 0x80000086, + TIMELINE_RESOURCE_NOTIFICATION_SIGNAL = 0x80000087, + TIMELINE_RESOURCE_NOTIFICATION_TWITCH = 0x80000088, + TIMELINE_RESOURCE_NOTIFICATION_AIRMAIL = 0x80000089, + TIMELINE_RESOURCE_NOTIFICATION_REDDIT = 0x8000008a, + TIMELINE_RESOURCE_NOTIFICATION_SWARM = 0x8000008b, + TIMELINE_RESOURCE_NOTIFICATION_TAPO = 0x8000008c, +} TimelineResourceId; + +#define NUM_TIMELINE_RESOURCES 141 diff --git a/tests/overrides/default/resources/robert/resource/resource_ids.auto.h b/tests/overrides/default/resources/robert/resource/resource_ids.auto.h deleted file mode 100644 index 3f48d5f38d..0000000000 --- a/tests/overrides/default/resources/robert/resource/resource_ids.auto.h +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// -// AUTOGENERATED BY BUILD -// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN -// - -typedef enum { - INVALID_RESOURCE = 0, - RESOURCE_ID_INVALID = 0, - DEFAULT_MENU_ICON = 0, - RESOURCE_ID_ACTION_BAR_ICON_X = 1, - RESOURCE_ID_BT_PAIR_SUCCESS = 2, - RESOURCE_ID_BT_PAIR_FAILURE = 3, - RESOURCE_ID_BT_PAIR_APPROVE_ON_PHONE = 4, - RESOURCE_ID_BT_PAIR_CONFIRMATION = 5, - RESOURCE_ID_SPINNER_BACKGROUND = 6, - RESOURCE_ID_GOTHIC_18_BOLD = 7, - RESOURCE_ID_BATTERY_ICON_LOW_LARGE = 8, - RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE = 9, - RESOURCE_ID_BATTERY_ICON_FULL_LARGE = 10, - RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED = 11, - RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED = 12, - RESOURCE_ID_BATTERY_ICON_CHARGE = 13, - RESOURCE_ID_BATTERY_NEEDS_CHARGING = 14, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE = 15, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED = 16, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED = 17, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND = 18, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY = 19, - RESOURCE_ID_QUIET_TIME = 20, - RESOURCE_ID_MUSIC_APP_GLANCE_PLAY = 21, - RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE = 22, - RESOURCE_ID_NOTIFICATIONS_APP_GLANCE = 23, - RESOURCE_ID_SEND_TEXT_APP_GLANCE = 24, - RESOURCE_ID_WATCHFACES_APP_GLANCE = 25, - RESOURCE_ID_MENU_ICON_TICTOC_WATCH = 26, - RESOURCE_ID_MENU_ICON_KICKSTART_WATCH = 27, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT = 28, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH = 29, - RESOURCE_ID_SETTINGS_ICON_AIRPLANE = 30, - RESOURCE_ID_ACTION_BAR_ICON_SMS = 31, - RESOURCE_ID_GOTHIC_09 = 32, - RESOURCE_ID_GOTHIC_14 = 33, - RESOURCE_ID_GOTHIC_14_EMOJI = 34, - RESOURCE_ID_GOTHIC_14_BOLD = 35, - RESOURCE_ID_GOTHIC_18 = 36, - RESOURCE_ID_GOTHIC_18_COMPRESSED = 37, - RESOURCE_ID_GOTHIC_18_EMOJI = 38, - RESOURCE_ID_GOTHIC_24 = 39, - RESOURCE_ID_GOTHIC_24_BOLD = 40, - RESOURCE_ID_GOTHIC_24_EMOJI = 41, - RESOURCE_ID_GOTHIC_28 = 42, - RESOURCE_ID_GOTHIC_28_BOLD = 43, - RESOURCE_ID_GOTHIC_28_EMOJI = 44, - RESOURCE_ID_GOTHIC_36 = 45, - RESOURCE_ID_GOTHIC_36_BOLD = 46, - RESOURCE_ID_BITHAM_30_BLACK = 47, - RESOURCE_ID_BITHAM_42_BOLD = 48, - RESOURCE_ID_BITHAM_42_LIGHT = 49, - RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS = 50, - RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS = 51, - RESOURCE_ID_BITHAM_34_LIGHT_SUBSET = 52, - RESOURCE_ID_BITHAM_18_LIGHT_SUBSET = 53, - RESOURCE_ID_ROBOTO_CONDENSED_21 = 54, - RESOURCE_ID_ROBOTO_BOLD_SUBSET_49 = 55, - RESOURCE_ID_DROID_SERIF_28_BOLD = 56, - RESOURCE_ID_LECO_20_BOLD_NUMBERS = 57, - RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM = 58, - RESOURCE_ID_LECO_32_BOLD_NUMBERS = 59, - RESOURCE_ID_LECO_36_BOLD_NUMBERS = 60, - RESOURCE_ID_LECO_38_BOLD_NUMBERS = 61, - RESOURCE_ID_LECO_42_NUMBERS = 62, - RESOURCE_ID_LECO_28_LIGHT_NUMBERS = 63, - RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD = 64, - RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD = 65, - RESOURCE_ID_MUSIC_ICON_ELLIPSIS = 66, - RESOURCE_ID_MUSIC_ICON_PAUSE = 67, - RESOURCE_ID_MUSIC_ICON_PLAY = 68, - RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE = 69, - RESOURCE_ID_MUSIC_ICON_VOLUME_UP = 70, - RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN = 71, - RESOURCE_ID_MUSIC_LARGE_CASSETTE = 72, - RESOURCE_ID_MUSIC_LARGE_VOLUME_UP = 73, - RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN = 74, - RESOURCE_ID_MUSIC_LARGE_PAUSED = 75, - RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC = 76, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHFACE_ICON = 77, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON = 78, - RESOURCE_ID_UNCHECKED_RADIO_BUTTON = 79, - RESOURCE_ID_CHECKED_RADIO_BUTTON = 80, - RESOURCE_ID_ACTION_BAR_ICON_MORE = 81, - RESOURCE_ID_ACTION_BAR_ICON_SNOOZE = 82, - RESOURCE_ID_ACTION_BAR_ICON_PAUSE = 83, - RESOURCE_ID_ACTION_BAR_ICON_START = 84, - RESOURCE_ID_ACTION_BAR_ICON_STOP = 85, - RESOURCE_ID_ACTION_BAR_ICON_TOGGLE = 86, - RESOURCE_ID_ACTION_MENU_FADE_TOP = 87, - RESOURCE_ID_ACTION_MENU_FADE_BOTTOM = 88, - RESOURCE_ID_CHECKBOX_ICON_CHECKED = 89, - RESOURCE_ID_CHECKBOX_ICON_UNCHECKED = 90, - RESOURCE_ID_CHECKMARK_ICON_BLACK = 91, - RESOURCE_ID_CHECKMARK_ICON_DOTTED = 92, - RESOURCE_ID_BLE_HRM_SHARING_TINY = 93, - RESOURCE_ID_BLE_HRM_SHARING_SMALL = 94, - RESOURCE_ID_BLE_HRM_SHARING_LARGE = 95, - RESOURCE_ID_NOTIFICATION_GENERIC_TINY = 96, - RESOURCE_ID_NOTIFICATION_GENERIC_SMALL = 97, - RESOURCE_ID_NOTIFICATION_GENERIC_LARGE = 98, - RESOURCE_ID_MISSED_CALL_TINY = 99, - RESOURCE_ID_MISSED_CALL_SMALL = 100, - RESOURCE_ID_GENERIC_REMINDER_TINY = 101, - RESOURCE_ID_WHATSAPP_NOTIFICATION_TINY = 102, - RESOURCE_ID_WHATSAPP_NOTIFICATION_SMALL = 103, - RESOURCE_ID_TWITTER_NOTIFICATION_TINY = 104, - RESOURCE_ID_TWITTER_NOTIFICATION_SMALL = 105, - RESOURCE_ID_TELEGRAM_APP_TINY = 106, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_TINY = 107, - RESOURCE_ID_GMAIL_NOTIFICATION_TINY = 108, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_TINY = 109, - RESOURCE_ID_FACEBOOK_NOTIFICATION_TINY = 110, - RESOURCE_ID_GENERIC_WEATHER_TINY = 111, - RESOURCE_ID_AUDIO_CASSETTE_TINY = 112, - RESOURCE_ID_SUNNY_DAY_TINY = 113, - RESOURCE_ID_GENERIC_SPORTS_TINY = 114, - RESOURCE_ID_GENERIC_EMAIL_TINY = 115, - RESOURCE_ID_AMERICAN_FOOTBALL_TINY = 116, - RESOURCE_ID_TIMELINE_CALENDAR_TINY = 117, - RESOURCE_ID_BASEBALL_GAME_TINY = 118, - RESOURCE_ID_BASEBALL_GAME_SMALL = 119, - RESOURCE_ID_ALARM_CLOCK_TINY = 120, - RESOURCE_ID_BIRTHDAY_EVENT_TINY = 121, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_TINY = 122, - RESOURCE_ID_CAR_RENTAL_TINY = 123, - RESOURCE_ID_SCHEDULED_FLIGHT_TINY = 124, - RESOURCE_ID_CLOUDY_DAY_TINY = 125, - RESOURCE_ID_CRICKET_GAME_TINY = 126, - RESOURCE_ID_CRICKET_GAME_SMALL = 127, - RESOURCE_ID_CRICKET_GAME_LARGE = 128, - RESOURCE_ID_DINNER_RESERVATION_TINY = 129, - RESOURCE_ID_DISMISSED_PHONE_CALL_TINY = 130, - RESOURCE_ID_GENERIC_CONFIRMATION_TINY = 131, - RESOURCE_ID_GENERIC_PIN_TINY = 132, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_TINY = 133, - RESOURCE_ID_GLUCOSE_MONITOR_TINY = 134, - RESOURCE_ID_HEAVY_RAIN_TINY = 135, - RESOURCE_ID_HEAVY_SNOW_TINY = 136, - RESOURCE_ID_HOCKEY_GAME_TINY = 137, - RESOURCE_ID_HOCKEY_GAME_SMALL = 138, - RESOURCE_ID_HOCKEY_GAME_LARGE = 139, - RESOURCE_ID_HOTEL_RESERVATION_TINY = 140, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_TINY = 141, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_SMALL = 142, - RESOURCE_ID_LIGHT_RAIN_TINY = 143, - RESOURCE_ID_LIGHT_SNOW_TINY = 144, - RESOURCE_ID_MAILBOX_NOTIFICATION_TINY = 145, - RESOURCE_ID_MAILBOX_NOTIFICATION_SMALL = 146, - RESOURCE_ID_MOVIE_EVENT_TINY = 147, - RESOURCE_ID_MUSIC_EVENT_TINY = 148, - RESOURCE_ID_NEWS_EVENT_TINY = 149, - RESOURCE_ID_PARTLY_CLOUDY_TINY = 150, - RESOURCE_ID_PAY_BILL_TINY = 151, - RESOURCE_ID_REACHED_FITNESS_GOAL_TINY = 152, - RESOURCE_ID_RADIO_SHOW_TINY = 153, - RESOURCE_ID_SCHEDULED_EVENT_TINY = 154, - RESOURCE_ID_SOCCER_GAME_TINY = 155, - RESOURCE_ID_SOCCER_GAME_SMALL = 156, - RESOURCE_ID_STOCKS_EVENT_TINY = 157, - RESOURCE_ID_BIRTHDAY_EVENT_SMALL = 158, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_SMALL = 159, - RESOURCE_ID_TIMELINE_CALENDAR_SMALL = 160, - RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_SMALL = 161, - RESOURCE_ID_ALARM_CLOCK_SMALL = 162, - RESOURCE_ID_CAR_RENTAL_SMALL = 163, - RESOURCE_ID_CHECK_INTERNET_CONNECTION_SMALL = 164, - RESOURCE_ID_DINNER_RESERVATION_SMALL = 165, - RESOURCE_ID_DISMISSED_PHONE_CALL_SMALL = 166, - RESOURCE_ID_GENERIC_SMS_SMALL = 167, - RESOURCE_ID_GENERIC_SMS_TINY = 168, - RESOURCE_ID_GENERIC_SMS_LARGE = 169, - RESOURCE_ID_FACEBOOK_NOTIFICATION_SMALL = 170, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_SMALL = 171, - RESOURCE_ID_GENERIC_CONFIRMATION_SMALL = 172, - RESOURCE_ID_GENERIC_EMAIL_SMALL = 173, - RESOURCE_ID_GENERIC_PIN_SMALL = 174, - RESOURCE_ID_GENERIC_REMINDER_SMALL = 175, - RESOURCE_ID_GENERIC_WARNING_SMALL = 176, - RESOURCE_ID_GENERIC_WEATHER_SMALL = 177, - RESOURCE_ID_GLUCOSE_MONITOR_SMALL = 178, - RESOURCE_ID_GMAIL_NOTIFICATION_SMALL = 179, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_SMALL = 180, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_SMALL = 181, - RESOURCE_ID_HEAVY_RAIN_SMALL = 182, - RESOURCE_ID_HEAVY_SNOW_SMALL = 183, - RESOURCE_ID_HOTEL_RESERVATION_SMALL = 184, - RESOURCE_ID_LIGHT_RAIN_SMALL = 185, - RESOURCE_ID_LIGHT_SNOW_SMALL = 186, - RESOURCE_ID_MOVIE_EVENT_SMALL = 187, - RESOURCE_ID_MUSIC_EVENT_SMALL = 188, - RESOURCE_ID_NEWS_EVENT_SMALL = 189, - RESOURCE_ID_PARTLY_CLOUDY_SMALL = 190, - RESOURCE_ID_PAY_BILL_SMALL = 191, - RESOURCE_ID_RADIO_SHOW_SMALL = 192, - RESOURCE_ID_REACHED_FITNESS_GOAL_SMALL = 193, - RESOURCE_ID_SCHEDULED_EVENT_SMALL = 194, - RESOURCE_ID_SUNNY_DAY_SMALL = 195, - RESOURCE_ID_GENERIC_SPORTS_SMALL = 196, - RESOURCE_ID_TIDE_IS_HIGH_SMALL = 197, - RESOURCE_ID_WATCH_DISCONNECTED_SMALL = 198, - RESOURCE_ID_ALARM_CLOCK_LARGE_STATIC = 199, - RESOURCE_ID_AMERICAN_FOOTBALL_LARGE = 200, - RESOURCE_ID_AMERICAN_FOOTBALL_SMALL = 201, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_LARGE = 202, - RESOURCE_ID_WHATSAPP_NOTIFICATION_LARGE = 203, - RESOURCE_ID_AUDIO_CASSETTE_LARGE = 204, - RESOURCE_ID_AUDIO_CASSETTE_SMALL = 205, - RESOURCE_ID_BASEBALL_GAME_LARGE = 206, - RESOURCE_ID_BIRTHDAY_EVENT_LARGE = 207, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_LARGE = 208, - RESOURCE_ID_TIMELINE_CALENDAR_LARGE = 209, - RESOURCE_ID_CAR_RENTAL_LARGE = 210, - RESOURCE_ID_CLOUDY_DAY_LARGE = 211, - RESOURCE_ID_CLOUDY_DAY_SMALL = 212, - RESOURCE_ID_DAY_SEPARATOR_TINY = 213, - RESOURCE_ID_DAY_SEPARATOR_SMALL = 214, - RESOURCE_ID_DAY_SEPARATOR_LARGE = 215, - RESOURCE_ID_RESULT_DELETED_TINY = 216, - RESOURCE_ID_RESULT_DELETED_SMALL = 217, - RESOURCE_ID_DINNER_RESERVATION_LARGE = 218, - RESOURCE_ID_DISMISSED_PHONE_CALL_LARGE = 219, - RESOURCE_ID_DURING_PHONE_CALL_TINY = 220, - RESOURCE_ID_DURING_PHONE_CALL_SMALL = 221, - RESOURCE_ID_DURING_PHONE_CALL_LARGE = 222, - RESOURCE_ID_DURING_PHONE_CALL_CENTERED_LARGE = 223, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_LARGE = 224, - RESOURCE_ID_FACEBOOK_NOTIFICATION_LARGE = 225, - RESOURCE_ID_GENERIC_EMAIL_LARGE = 226, - RESOURCE_ID_GENERIC_PIN_LARGE = 227, - RESOURCE_ID_GENERIC_REMINDER_LARGE = 228, - RESOURCE_ID_GENERIC_SPORTS_LARGE = 229, - RESOURCE_ID_GENERIC_WEATHER_LARGE = 230, - RESOURCE_ID_GLUCOSE_MONITOR_LARGE = 231, - RESOURCE_ID_GMAIL_NOTIFICATION_LARGE = 232, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_LARGE = 233, - RESOURCE_ID_HEAVY_RAIN_LARGE = 234, - RESOURCE_ID_HEAVY_SNOW_LARGE = 235, - RESOURCE_ID_HOTEL_RESERVATION_LARGE = 236, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_LARGE = 237, - RESOURCE_ID_LIGHT_RAIN_LARGE = 238, - RESOURCE_ID_LIGHT_SNOW_LARGE = 239, - RESOURCE_ID_MAILBOX_NOTIFICATION_LARGE = 240, - RESOURCE_ID_MISSED_CALL_LARGE = 241, - RESOURCE_ID_MOVIE_EVENT_LARGE = 242, - RESOURCE_ID_MUSIC_EVENT_LARGE = 243, - RESOURCE_ID_NEWS_EVENT_LARGE = 244, - RESOURCE_ID_PARTLY_CLOUDY_LARGE = 245, - RESOURCE_ID_PAY_BILL_LARGE = 246, - RESOURCE_ID_RADIO_SHOW_LARGE = 247, - RESOURCE_ID_REACHED_FITNESS_GOAL_LARGE = 248, - RESOURCE_ID_SCHEDULED_EVENT_LARGE = 249, - RESOURCE_ID_SCHEDULED_FLIGHT_LARGE = 250, - RESOURCE_ID_SCHEDULED_FLIGHT_SMALL = 251, - RESOURCE_ID_RESULT_SENT_TINY = 252, - RESOURCE_ID_RESULT_SENT_SMALL = 253, - RESOURCE_ID_SOCCER_GAME_LARGE = 254, - RESOURCE_ID_STOCKS_EVENT_LARGE = 255, - RESOURCE_ID_STOCKS_EVENT_SMALL = 256, - RESOURCE_ID_SUNNY_DAY_LARGE = 257, - RESOURCE_ID_TELEGRAM_APP_LARGE = 258, - RESOURCE_ID_TELEGRAM_APP_SMALL = 259, - RESOURCE_ID_TIDE_IS_HIGH_LARGE = 260, - RESOURCE_ID_TWITTER_NOTIFICATION_LARGE = 261, - RESOURCE_ID_RESULT_FAILED_TINY = 262, - RESOURCE_ID_RESULT_FAILED_SMALL = 263, - RESOURCE_ID_RESULT_FAILED_LARGE = 264, - RESOURCE_ID_GENERIC_QUESTION_TINY = 265, - RESOURCE_ID_GENERIC_QUESTION_SMALL = 266, - RESOURCE_ID_GENERIC_QUESTION_LARGE = 267, - RESOURCE_ID_NOTIFICATION_OUTLOOK_TINY = 268, - RESOURCE_ID_NOTIFICATION_OUTLOOK_SMALL = 269, - RESOURCE_ID_NOTIFICATION_OUTLOOK_LARGE = 270, - RESOURCE_ID_RAINING_AND_SNOWING_TINY = 271, - RESOURCE_ID_RAINING_AND_SNOWING_SMALL = 272, - RESOURCE_ID_RAINING_AND_SNOWING_LARGE = 273, - RESOURCE_ID_NOTIFICATION_FACETIME_TINY = 274, - RESOURCE_ID_NOTIFICATION_FACETIME_SMALL = 275, - RESOURCE_ID_NOTIFICATION_FACETIME_LARGE = 276, - RESOURCE_ID_NOTIFICATION_LINE_TINY = 277, - RESOURCE_ID_NOTIFICATION_LINE_SMALL = 278, - RESOURCE_ID_NOTIFICATION_LINE_LARGE = 279, - RESOURCE_ID_NOTIFICATION_SKYPE_TINY = 280, - RESOURCE_ID_NOTIFICATION_SKYPE_SMALL = 281, - RESOURCE_ID_NOTIFICATION_SKYPE_LARGE = 282, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_TINY = 283, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_SMALL = 284, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_LARGE = 285, - RESOURCE_ID_NOTIFICATION_VIBER_TINY = 286, - RESOURCE_ID_NOTIFICATION_VIBER_SMALL = 287, - RESOURCE_ID_NOTIFICATION_VIBER_LARGE = 288, - RESOURCE_ID_NOTIFICATION_WECHAT_TINY = 289, - RESOURCE_ID_NOTIFICATION_WECHAT_SMALL = 290, - RESOURCE_ID_NOTIFICATION_WECHAT_LARGE = 291, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_TINY = 292, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_SMALL = 293, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_LARGE = 294, - RESOURCE_ID_TV_SHOW_TINY = 295, - RESOURCE_ID_TV_SHOW_SMALL = 296, - RESOURCE_ID_TV_SHOW_LARGE = 297, - RESOURCE_ID_END_OF_TIMELINE = 298, - RESOURCE_ID_BASKETBALL_TINY = 299, - RESOURCE_ID_BASKETBALL_SMALL = 300, - RESOURCE_ID_BASKETBALL_LARGE = 301, - RESOURCE_ID_RESULT_DISMISSED_TINY = 302, - RESOURCE_ID_RESULT_DISMISSED_SMALL = 303, - RESOURCE_ID_TIDE_IS_HIGH_TINY = 304, - RESOURCE_ID_OUTGOING_CALL_LARGE = 305, - RESOURCE_ID_NOTIFICATION_HIPCHAT_TINY = 306, - RESOURCE_ID_NOTIFICATION_HIPCHAT_SMALL = 307, - RESOURCE_ID_NOTIFICATION_HIPCHAT_LARGE = 308, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_TINY = 309, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_SMALL = 310, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_LARGE = 311, - RESOURCE_ID_INCOMING_PHONE_CALL_TINY = 312, - RESOURCE_ID_INCOMING_PHONE_CALL_SMALL = 313, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_TINY = 314, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_SMALL = 315, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_LARGE = 316, - RESOURCE_ID_NOTIFICATION_KIK_TINY = 317, - RESOURCE_ID_NOTIFICATION_KIK_SMALL = 318, - RESOURCE_ID_NOTIFICATION_KIK_LARGE = 319, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_TINY = 320, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_SMALL = 321, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_LARGE = 322, - RESOURCE_ID_LOCATION_TINY = 323, - RESOURCE_ID_LOCATION_SMALL = 324, - RESOURCE_ID_LOCATION_LARGE = 325, - RESOURCE_ID_SUNSET_TINY = 326, - RESOURCE_ID_SUNSET_SMALL = 327, - RESOURCE_ID_SUNSET_LARGE = 328, - RESOURCE_ID_SUNRISE_TINY = 329, - RESOURCE_ID_SUNRISE_SMALL = 330, - RESOURCE_ID_SUNRISE_LARGE = 331, - RESOURCE_ID_SETTINGS_TINY = 332, - RESOURCE_ID_SETTINGS_SMALL = 333, - RESOURCE_ID_SETTINGS_LARGE = 334, - RESOURCE_ID_PLUS_ICON_BLACK = 335, - RESOURCE_ID_PLUS_ICON_DOTTED = 336, - RESOURCE_ID_RESULT_SHREDDED_TINY = 337, - RESOURCE_ID_RESULT_SHREDDED_SMALL = 338, - RESOURCE_ID_QUIET_TIME_MOUSE = 339, - RESOURCE_ID_REMINDER_SNOOZE = 340, - RESOURCE_ID_QUIET_TIME_STATUS_BAR = 341, - RESOURCE_ID_QUICK_DISMISS = 342, - RESOURCE_ID_THUMBS_UP_SMALL = 343, - RESOURCE_ID_THUMBS_UP_LARGE = 344, - RESOURCE_ID_ARROW_UP_SMALL = 345, - RESOURCE_ID_ARROW_DOWN_SMALL = 346, - RESOURCE_ID_ACTIVITY_TINY = 347, - RESOURCE_ID_ACTIVITY_SMALL = 348, - RESOURCE_ID_ACTIVITY_LARGE = 349, - RESOURCE_ID_SLEEP_TINY = 350, - RESOURCE_ID_SLEEP_SMALL = 351, - RESOURCE_ID_SLEEP_LARGE = 352, - RESOURCE_ID_REWARD_AVERAGE_LARGE = 353, - RESOURCE_ID_REWARD_GOOD_LARGE = 354, - RESOURCE_ID_REWARD_BAD_LARGE = 355, - RESOURCE_ID_CALORIES_TINY = 356, - RESOURCE_ID_DISTANCE_TINY = 357, - RESOURCE_ID_DURATION_TINY = 358, - RESOURCE_ID_PACE_TINY = 359, - RESOURCE_ID_RUN_TINY = 360, - RESOURCE_ID_RUN_LARGE = 361, - RESOURCE_ID_MOON_TINY = 362, - RESOURCE_ID_NOTIFICATION_AMAZON_TINY = 363, - RESOURCE_ID_NOTIFICATION_AMAZON_SMALL = 364, - RESOURCE_ID_NOTIFICATION_AMAZON_LARGE = 365, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_TINY = 366, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_SMALL = 367, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_LARGE = 368, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_TINY = 369, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_SMALL = 370, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_LARGE = 371, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_TINY = 372, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_SMALL = 373, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_LARGE = 374, - RESOURCE_ID_NOTIFICATION_LINKEDIN_TINY = 375, - RESOURCE_ID_NOTIFICATION_LINKEDIN_SMALL = 376, - RESOURCE_ID_NOTIFICATION_LINKEDIN_LARGE = 377, - RESOURCE_ID_NOTIFICATION_SLACK_TINY = 378, - RESOURCE_ID_NOTIFICATION_SLACK_SMALL = 379, - RESOURCE_ID_NOTIFICATION_SLACK_LARGE = 380, - RESOURCE_ID_SMART_ALARM_TINY = 381, - RESOURCE_ID_HEALTH_APP_ACTIVITY = 382, - RESOURCE_ID_HEALTH_APP_SLEEP = 383, - RESOURCE_ID_HEALTH_APP_CROWN = 384, - RESOURCE_ID_HEALTH_APP_HR = 385, - RESOURCE_ID_HEALTH_APP_PULSING_HEART = 386, - RESOURCE_ID_WORKOUT_APP_WALK = 387, - RESOURCE_ID_WORKOUT_APP_WALK_SMALL = 388, - RESOURCE_ID_WORKOUT_APP_WALK_TINY = 389, - RESOURCE_ID_WORKOUT_APP_RUN = 390, - RESOURCE_ID_WORKOUT_APP_RUN_SMALL = 391, - RESOURCE_ID_WORKOUT_APP_RUN_TINY = 392, - RESOURCE_ID_WORKOUT_APP_WORKOUT = 393, - RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL = 394, - RESOURCE_ID_WORKOUT_APP_DETECTED = 395, - RESOURCE_ID_WORKOUT_APP_HEART = 396, - RESOURCE_ID_WORKOUT_APP_MEASURING_HR = 397, - RESOURCE_ID_WORKOUT_APP_HR_PULSE_TINY = 398, - RESOURCE_ID_WORKOUT_APP_END = 399, - RESOURCE_ID_WORKOUT_APP_ONE = 400, - RESOURCE_ID_WORKOUT_APP_TWO = 401, - RESOURCE_ID_WORKOUT_APP_THREE = 402, - RESOURCE_ID_HEART_TINY = 403, - RESOURCE_ID_HEART_SMALL = 404, - RESOURCE_ID_HEART_LARGE = 405, - RESOURCE_ID_BACKLIGHT = 406, - RESOURCE_ID_AIRPLANE = 407, - RESOURCE_ID_MODAL_CONTRACT_TO_MODAL_SEQUENCE = 408, - RESOURCE_ID_MODAL_CONTRACT_FROM_MODAL_SEQUENCE = 409, - RESOURCE_ID_MODAL_EXPAND_TO_APP_SEQUENCE = 410, - RESOURCE_ID_NO_EVENTS_LARGE = 411, - RESOURCE_ID_TIMER_APP_FACE_ICON = 412, - RESOURCE_ID_STOPWATCH_APP_FACE_ICON = 413, - RESOURCE_ID_ESPN_APP_FACE_ICON = 414, - RESOURCE_ID_HEALTH_APP_FACE_ICON = 415, - RESOURCE_ID_SEND_TEXT_APP_FACE_ICON = 416, - RESOURCE_ID_QUIET_TIME_ACTIVE = 417, - RESOURCE_ID_BATTERY_CHARGING_ICON = 418, - RESOURCE_ID_HEALTH_ICON_MOON = 419, - RESOURCE_ID_HEALTH_ICON_ROTATED_MOON = 420, - RESOURCE_ID_STRIDE_HEART = 421, - RESOURCE_ID_STRIDE_SHOE_GREEN = 422, - RESOURCE_ID_STRIDE_SHOE_BLUE = 423, - RESOURCE_ID_WEATHER_CHANNEL_LOGO = 424, - RESOURCE_ID_SYSTEM_FCC_MARK = 425, - RESOURCE_ID_SYSTEM_KCC_MARK = 426, - RESOURCE_ID_SYSTEM_CE_MARK = 427, - RESOURCE_ID_SYSTEM_WEEE_MARK = 428, - RESOURCE_ID_SYSTEM_R_MARK = 429, - RESOURCE_ID_SYSTEM_T_MARK = 430, - RESOURCE_ID_SYSTEM_AUS_RCM_MARK = 431, - RESOURCE_ID_SYSTEM_NOM_NYCE_MARK = 432, - RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE = 433, - RESOURCE_ID_EMOJI_BIG_SMILE_LARGE = 434, - RESOURCE_ID_EMOJI_HEART_LARGE = 435, - RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE = 436, - RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE = 437, - RESOURCE_ID_EMOJI_SAD_LARGE = 438, - RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE = 439, - RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE = 440, - RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE = 441, - RESOURCE_ID_EMOJI_THUMBS_UP_LARGE = 442, - RESOURCE_ID_EMOJI_WINK_LARGE = 443, - RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE = 444, - RESOURCE_ID_VIBE_SCORE_REVEILLE = 445, - RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK = 446, - RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE = 447, - RESOURCE_ID_VIBE_SCORE_PULSE = 448, - RESOURCE_ID_VIBE_SCORE_JACKHAMMER = 449, - RESOURCE_ID_VIBE_SCORE_MARIO = 450, - RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW = 451, - RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH = 452, - RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW = 453, - RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH = 454, - RESOURCE_ID_VIBE_SCORE_ALARM_LPM = 455, - RESOURCE_ID_SMART_ALARM_ICON_BLACK = 456, - RESOURCE_ID_BLE_HRM_SHARE_REQUEST_LARGE = 457, - RESOURCE_ID_BLE_HRM_SHARED = 458, - RESOURCE_ID_BLE_HRM_NOT_SHARED = 459, - RESOURCE_ID_CONNECTIVITY_SHARING_HRM = 460, - RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM = 461, - RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM = 462, - RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM = 463, - RESOURCE_ID_STORED_APP_GOLF = 464, - RESOURCE_ID_BT_BOOT_IMAGE = 465, - RESOURCE_ID_BT_FW_IMAGE = 466, - RESOURCE_ID_AS7000_FW_IMAGE = 467, - RESOURCE_ID_TIMEZONE_DATABASE = 468, - RESOURCE_ID_ACTION_BAR_ICON_CHECK = 469, - RESOURCE_ID_GENERIC_WARNING_LARGE = 470, - RESOURCE_ID_FONT_FALLBACK_INTERNAL = 471, - RESOURCE_ID_ACTION_BAR_ICON_UP = 472, - RESOURCE_ID_ACTION_BAR_ICON_DOWN = 473, - RESOURCE_ID_GENERIC_WARNING_TINY = 474, - RESOURCE_ID_CHECK_INTERNET_CONNECTION_LARGE = 475, - RESOURCE_ID_WATCH_DISCONNECTED_LARGE = 476, - RESOURCE_ID_ARROW_DOWN = 477, - RESOURCE_ID_VOICE_MICROPHONE_LARGE = 478, - RESOURCE_ID_ALARM_CLOCK_LARGE = 479, - RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE = 480, - RESOURCE_ID_RESULT_DELETED_LARGE = 481, - RESOURCE_ID_RESULT_SHREDDED_LARGE = 482, - RESOURCE_ID_RESULT_DISMISSED_LARGE = 483, - RESOURCE_ID_GENERIC_CONFIRMATION_LARGE = 484, - RESOURCE_ID_INCOMING_PHONE_CALL_LARGE = 485, - RESOURCE_ID_RESULT_MUTE_LARGE = 486, - RESOURCE_ID_RESULT_SENT_LARGE = 487, - RESOURCE_ID_RESULT_UNMUTE_LARGE = 488, - RESOURCE_ID_JS_TICTOC = 489, - RESOURCE_ID_PUG = 490, - RESOURCE_ID_STRINGS = 491, - RESOURCE_ID_GOTHIC_14_EXTENDED = 492, - RESOURCE_ID_GOTHIC_14_BOLD_EXTENDED = 493, - RESOURCE_ID_GOTHIC_18_EXTENDED = 494, - RESOURCE_ID_GOTHIC_18_BOLD_EXTENDED = 495, - RESOURCE_ID_GOTHIC_24_EXTENDED = 496, - RESOURCE_ID_GOTHIC_24_BOLD_EXTENDED = 497, - RESOURCE_ID_GOTHIC_28_EXTENDED = 498, - RESOURCE_ID_GOTHIC_28_BOLD_EXTENDED = 499, - RESOURCE_ID_BITHAM_18_LIGHT_SUBSET_EXTENDED = 500, - RESOURCE_ID_BITHAM_30_BLACK_EXTENDED = 501, - RESOURCE_ID_BITHAM_34_LIGHT_SUBSET_EXTENDED = 502, - RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS_EXTENDED = 503, - RESOURCE_ID_BITHAM_42_BOLD_EXTENDED = 504, - RESOURCE_ID_BITHAM_42_LIGHT_EXTENDED = 505, - RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS_EXTENDED = 506, - RESOURCE_ID_ROBOTO_CONDENSED_21_EXTENDED = 507, - RESOURCE_ID_ROBOTO_BOLD_SUBSET_49_EXTENDED = 508, - RESOURCE_ID_DROID_SERIF_28_BOLD_EXTENDED = 509, - RESOURCE_ID_GOTHIC_09_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_14_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_18_COMPRESSED_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_18_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_24_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_28_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_36_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_36_BOLD_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_20_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_32_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_36_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_38_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_42_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_28_LIGHT_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_AGENCY_FB_46_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_AGENCY_FB_88_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_AGENCY_FB_88_THIN_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_FONT_FALLBACK_INTERNAL_EXTENDED = INVALID_RESOURCE, -} ResourceId; \ No newline at end of file diff --git a/tests/overrides/default/resources/robert/resource/resource_version.auto.h b/tests/overrides/default/resources/robert/resource/resource_version.auto.h deleted file mode 100644 index be2d651f8e..0000000000 --- a/tests/overrides/default/resources/robert/resource/resource_version.auto.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -// -// AUTOGENERATED -// DO NOT MODIFY -// - -static const ResourceVersion SYSTEM_RESOURCE_VERSION = { - .crc = 3735539727, - .timestamp = 0 -}; diff --git a/tests/overrides/default/resources/robert/resource/timeline_resource_ids.auto.h b/tests/overrides/default/resources/robert/resource/timeline_resource_ids.auto.h deleted file mode 100644 index 912c402543..0000000000 --- a/tests/overrides/default/resources/robert/resource/timeline_resource_ids.auto.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -// -// AUTOGENERATED -// DO NOT MODIFY -// - -typedef enum { - TIMELINE_RESOURCE_INVALID = 0, - TIMELINE_RESOURCE_NOTIFICATION_GENERIC = 0x80000001, - TIMELINE_RESOURCE_TIMELINE_MISSED_CALL = 0x80000002, - TIMELINE_RESOURCE_NOTIFICATION_REMINDER = 0x80000003, - TIMELINE_RESOURCE_NOTIFICATION_FLAG = 0x80000004, - TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP = 0x80000005, - TIMELINE_RESOURCE_NOTIFICATION_TWITTER = 0x80000006, - TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM = 0x80000007, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS = 0x80000008, - TIMELINE_RESOURCE_NOTIFICATION_GMAIL = 0x80000009, - TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER = 0x8000000a, - TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK = 0x8000000b, - TIMELINE_RESOURCE_AUDIO_CASSETTE = 0x8000000c, - TIMELINE_RESOURCE_ALARM_CLOCK = 0x8000000d, - TIMELINE_RESOURCE_TIMELINE_WEATHER = 0x8000000e, - TIMELINE_RESOURCE_TIMELINE_SUN = 0x80000010, - TIMELINE_RESOURCE_TIMELINE_SPORTS = 0x80000011, - TIMELINE_RESOURCE_GENERIC_EMAIL = 0x80000013, - TIMELINE_RESOURCE_AMERICAN_FOOTBALL = 0x80000014, - TIMELINE_RESOURCE_TIMELINE_CALENDAR = 0x80000015, - TIMELINE_RESOURCE_TIMELINE_BASEBALL = 0x80000016, - TIMELINE_RESOURCE_BIRTHDAY_EVENT = 0x80000017, - TIMELINE_RESOURCE_CAR_RENTAL = 0x80000018, - TIMELINE_RESOURCE_CLOUDY_DAY = 0x80000019, - TIMELINE_RESOURCE_CRICKET_GAME = 0x8000001a, - TIMELINE_RESOURCE_DINNER_RESERVATION = 0x8000001b, - TIMELINE_RESOURCE_GENERIC_WARNING = 0x8000001c, - TIMELINE_RESOURCE_GLUCOSE_MONITOR = 0x8000001d, - TIMELINE_RESOURCE_HOCKEY_GAME = 0x8000001e, - TIMELINE_RESOURCE_HOTEL_RESERVATION = 0x8000001f, - TIMELINE_RESOURCE_LIGHT_RAIN = 0x80000020, - TIMELINE_RESOURCE_LIGHT_SNOW = 0x80000021, - TIMELINE_RESOURCE_MOVIE_EVENT = 0x80000022, - TIMELINE_RESOURCE_MUSIC_EVENT = 0x80000023, - TIMELINE_RESOURCE_NEWS_EVENT = 0x80000024, - TIMELINE_RESOURCE_PARTLY_CLOUDY = 0x80000025, - TIMELINE_RESOURCE_PAY_BILL = 0x80000026, - TIMELINE_RESOURCE_RADIO_SHOW = 0x80000027, - TIMELINE_RESOURCE_SCHEDULED_EVENT = 0x80000028, - TIMELINE_RESOURCE_SOCCER_GAME = 0x80000029, - TIMELINE_RESOURCE_STOCKS_EVENT = 0x8000002a, - TIMELINE_RESOURCE_RESULT_DELETED = 0x8000002b, - TIMELINE_RESOURCE_CHECK_INTERNET_CONNECTION = 0x8000002c, - TIMELINE_RESOURCE_GENERIC_SMS = 0x8000002d, - TIMELINE_RESOURCE_RESULT_MUTE = 0x8000002e, - TIMELINE_RESOURCE_RESULT_SENT = 0x8000002f, - TIMELINE_RESOURCE_WATCH_DISCONNECTED = 0x80000030, - TIMELINE_RESOURCE_DURING_PHONE_CALL = 0x80000031, - TIMELINE_RESOURCE_TIDE_IS_HIGH = 0x80000032, - TIMELINE_RESOURCE_RESULT_DISMISSED = 0x80000033, - TIMELINE_RESOURCE_HEAVY_RAIN = 0x80000034, - TIMELINE_RESOURCE_HEAVY_SNOW = 0x80000035, - TIMELINE_RESOURCE_SCHEDULED_FLIGHT = 0x80000036, - TIMELINE_RESOURCE_GENERIC_CONFIRMATION = 0x80000037, - TIMELINE_RESOURCE_DAY_SEPARATOR = 0x80000038, - TIMELINE_RESOURCE_NO_EVENTS = 0x80000039, - TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER = 0x8000003a, - TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM = 0x8000003b, - TIMELINE_RESOURCE_NOTIFICATION_MAILBOX = 0x8000003c, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX = 0x8000003d, - TIMELINE_RESOURCE_RESULT_FAILED = 0x8000003e, - TIMELINE_RESOURCE_GENERIC_QUESTION = 0x8000003f, - TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK = 0x80000040, - TIMELINE_RESOURCE_RAINING_AND_SNOWING = 0x80000041, - TIMELINE_RESOURCE_REACHED_FITNESS_GOAL = 0x80000042, - TIMELINE_RESOURCE_NOTIFICATION_LINE = 0x80000043, - TIMELINE_RESOURCE_NOTIFICATION_SKYPE = 0x80000044, - TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT = 0x80000045, - TIMELINE_RESOURCE_NOTIFICATION_VIBER = 0x80000046, - TIMELINE_RESOURCE_NOTIFICATION_WECHAT = 0x80000047, - TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL = 0x80000048, - TIMELINE_RESOURCE_TV_SHOW = 0x80000049, - TIMELINE_RESOURCE_BASKETBALL = 0x8000004a, - TIMELINE_RESOURCE_DISMISSED_PHONE_CALL = 0x8000004b, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MESSENGER = 0x8000004c, - TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT = 0x8000004d, - TIMELINE_RESOURCE_INCOMING_PHONE_CALL = 0x8000004e, - TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK = 0x8000004f, - TIMELINE_RESOURCE_NOTIFICATION_KIK = 0x80000050, - TIMELINE_RESOURCE_NOTIFICATION_LIGHTHOUSE = 0x80000051, - TIMELINE_RESOURCE_LOCATION = 0x80000052, - TIMELINE_RESOURCE_SETTINGS = 0x80000053, - TIMELINE_RESOURCE_SUNRISE = 0x80000054, - TIMELINE_RESOURCE_SUNSET = 0x80000055, - TIMELINE_RESOURCE_RESULT_UNMUTE = 0x80000056, - TIMELINE_RESOURCE_RESULT_UNMUTE_ALT = 0x8000005e, - TIMELINE_RESOURCE_DURING_PHONE_CALL_CENTERED = 0x8000005f, - TIMELINE_RESOURCE_TIMELINE_EMPTY_CALENDAR = 0x80000060, - TIMELINE_RESOURCE_THUMBS_UP = 0x80000061, - TIMELINE_RESOURCE_ARROW_UP = 0x80000062, - TIMELINE_RESOURCE_ARROW_DOWN = 0x80000063, - TIMELINE_RESOURCE_ACTIVITY = 0x80000064, - TIMELINE_RESOURCE_SLEEP = 0x80000065, - TIMELINE_RESOURCE_REWARD_BAD = 0x80000066, - TIMELINE_RESOURCE_REWARD_GOOD = 0x80000067, - TIMELINE_RESOURCE_REWARD_AVERAGE = 0x80000068, - TIMELINE_RESOURCE_CALORIES = 0x80000069, - TIMELINE_RESOURCE_DISTANCE = 0x8000006a, - TIMELINE_RESOURCE_DURATION = 0x8000006b, - TIMELINE_RESOURCE_PACE = 0x8000006c, - TIMELINE_RESOURCE_RUN = 0x8000006d, - TIMELINE_RESOURCE_NOTIFICATION_FACETIME = 0x8000006e, - TIMELINE_RESOURCE_NOTIFICATION_AMAZON = 0x8000006f, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS = 0x80000070, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS = 0x80000071, - TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS = 0x80000072, - TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN = 0x80000073, - TIMELINE_RESOURCE_NOTIFICATION_SLACK = 0x80000074, - TIMELINE_RESOURCE_SMART_ALARM = 0x80000075, - TIMELINE_RESOURCE_HEART = 0x80000076, - TIMELINE_RESOURCE_BLE_HRM_SHARING = 0x80000077, -} TimelineResourceId; - -#define NUM_TIMELINE_RESOURCES 120 diff --git a/tests/overrides/default/resources/silk/resource/resource_ids.auto.h b/tests/overrides/default/resources/silk/resource/resource_ids.auto.h deleted file mode 100644 index f2010f7fc8..0000000000 --- a/tests/overrides/default/resources/silk/resource/resource_ids.auto.h +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// -// AUTOGENERATED BY BUILD -// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN -// - -typedef enum { - INVALID_RESOURCE = 0, - RESOURCE_ID_INVALID = 0, - DEFAULT_MENU_ICON = 0, - RESOURCE_ID_ACTION_BAR_ICON_CHECK = 1, - RESOURCE_ID_ACTION_BAR_ICON_X = 2, - RESOURCE_ID_BT_PAIR_SUCCESS = 3, - RESOURCE_ID_BT_PAIR_FAILURE = 4, - RESOURCE_ID_BT_PAIR_APPROVE_ON_PHONE = 5, - RESOURCE_ID_BT_PAIR_CONFIRMATION = 6, - RESOURCE_ID_GENERIC_WARNING_LARGE = 7, - RESOURCE_ID_SPINNER_BACKGROUND = 8, - RESOURCE_ID_GOTHIC_18_BOLD = 9, - RESOURCE_ID_BATTERY_ICON_LOW_LARGE = 10, - RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE = 11, - RESOURCE_ID_BATTERY_ICON_FULL_LARGE = 12, - RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED = 13, - RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED = 14, - RESOURCE_ID_BATTERY_ICON_CHARGE = 15, - RESOURCE_ID_BATTERY_NEEDS_CHARGING = 16, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE = 17, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED = 18, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED = 19, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND = 20, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY = 21, - RESOURCE_ID_QUIET_TIME = 22, - RESOURCE_ID_MUSIC_APP_GLANCE_PLAY = 23, - RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE = 24, - RESOURCE_ID_NOTIFICATIONS_APP_GLANCE = 25, - RESOURCE_ID_SEND_TEXT_APP_GLANCE = 26, - RESOURCE_ID_WATCHFACES_APP_GLANCE = 27, - RESOURCE_ID_MENU_ICON_TICTOC_WATCH = 28, - RESOURCE_ID_MENU_ICON_KICKSTART_WATCH = 29, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT = 30, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH = 31, - RESOURCE_ID_SETTINGS_ICON_AIRPLANE = 32, - RESOURCE_ID_ACTION_BAR_ICON_SMS = 33, - RESOURCE_ID_GOTHIC_09 = 34, - RESOURCE_ID_GOTHIC_14 = 35, - RESOURCE_ID_GOTHIC_14_EMOJI = 36, - RESOURCE_ID_GOTHIC_14_BOLD = 37, - RESOURCE_ID_GOTHIC_18 = 38, - RESOURCE_ID_GOTHIC_18_EMOJI = 39, - RESOURCE_ID_GOTHIC_24 = 40, - RESOURCE_ID_GOTHIC_24_BOLD = 41, - RESOURCE_ID_GOTHIC_24_EMOJI = 42, - RESOURCE_ID_GOTHIC_28 = 43, - RESOURCE_ID_GOTHIC_28_BOLD = 44, - RESOURCE_ID_GOTHIC_28_EMOJI = 45, - RESOURCE_ID_GOTHIC_36 = 46, - RESOURCE_ID_GOTHIC_36_BOLD = 47, - RESOURCE_ID_BITHAM_30_BLACK = 48, - RESOURCE_ID_BITHAM_42_BOLD = 49, - RESOURCE_ID_BITHAM_42_LIGHT = 50, - RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS = 51, - RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS = 52, - RESOURCE_ID_BITHAM_34_LIGHT_SUBSET = 53, - RESOURCE_ID_BITHAM_18_LIGHT_SUBSET = 54, - RESOURCE_ID_ROBOTO_CONDENSED_21 = 55, - RESOURCE_ID_ROBOTO_BOLD_SUBSET_49 = 56, - RESOURCE_ID_DROID_SERIF_28_BOLD = 57, - RESOURCE_ID_LECO_20_BOLD_NUMBERS = 58, - RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM = 59, - RESOURCE_ID_LECO_32_BOLD_NUMBERS = 60, - RESOURCE_ID_LECO_36_BOLD_NUMBERS = 61, - RESOURCE_ID_LECO_38_BOLD_NUMBERS = 62, - RESOURCE_ID_LECO_42_NUMBERS = 63, - RESOURCE_ID_LECO_28_LIGHT_NUMBERS = 64, - RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD = 65, - RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD = 66, - RESOURCE_ID_MUSIC_ICON_ELLIPSIS = 67, - RESOURCE_ID_MUSIC_ICON_PAUSE = 68, - RESOURCE_ID_MUSIC_ICON_PLAY = 69, - RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE = 70, - RESOURCE_ID_MUSIC_ICON_VOLUME_UP = 71, - RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN = 72, - RESOURCE_ID_MUSIC_LARGE_CASSETTE = 73, - RESOURCE_ID_MUSIC_LARGE_VOLUME_UP = 74, - RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN = 75, - RESOURCE_ID_MUSIC_LARGE_PAUSED = 76, - RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC = 77, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHFACE_ICON = 78, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON = 79, - RESOURCE_ID_UNCHECKED_RADIO_BUTTON = 80, - RESOURCE_ID_CHECKED_RADIO_BUTTON = 81, - RESOURCE_ID_ACTION_BAR_ICON_UP = 82, - RESOURCE_ID_ACTION_BAR_ICON_DOWN = 83, - RESOURCE_ID_ACTION_BAR_ICON_MORE = 84, - RESOURCE_ID_ACTION_BAR_ICON_SNOOZE = 85, - RESOURCE_ID_ACTION_BAR_ICON_PAUSE = 86, - RESOURCE_ID_ACTION_BAR_ICON_START = 87, - RESOURCE_ID_ACTION_BAR_ICON_STOP = 88, - RESOURCE_ID_ACTION_BAR_ICON_TOGGLE = 89, - RESOURCE_ID_ACTION_MENU_FADE_TOP = 90, - RESOURCE_ID_ACTION_MENU_FADE_BOTTOM = 91, - RESOURCE_ID_CHECKBOX_ICON_CHECKED = 92, - RESOURCE_ID_CHECKBOX_ICON_UNCHECKED = 93, - RESOURCE_ID_CHECKMARK_ICON_BLACK = 94, - RESOURCE_ID_CHECKMARK_ICON_DOTTED = 95, - RESOURCE_ID_BLE_HRM_SHARING_TINY = 96, - RESOURCE_ID_BLE_HRM_SHARING_SMALL = 97, - RESOURCE_ID_BLE_HRM_SHARING_LARGE = 98, - RESOURCE_ID_NOTIFICATION_GENERIC_TINY = 99, - RESOURCE_ID_NOTIFICATION_GENERIC_SMALL = 100, - RESOURCE_ID_NOTIFICATION_GENERIC_LARGE = 101, - RESOURCE_ID_MISSED_CALL_TINY = 102, - RESOURCE_ID_MISSED_CALL_SMALL = 103, - RESOURCE_ID_GENERIC_REMINDER_TINY = 104, - RESOURCE_ID_WHATSAPP_NOTIFICATION_TINY = 105, - RESOURCE_ID_WHATSAPP_NOTIFICATION_SMALL = 106, - RESOURCE_ID_TWITTER_NOTIFICATION_TINY = 107, - RESOURCE_ID_TWITTER_NOTIFICATION_SMALL = 108, - RESOURCE_ID_TELEGRAM_APP_TINY = 109, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_TINY = 110, - RESOURCE_ID_GMAIL_NOTIFICATION_TINY = 111, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_TINY = 112, - RESOURCE_ID_FACEBOOK_NOTIFICATION_TINY = 113, - RESOURCE_ID_GENERIC_WEATHER_TINY = 114, - RESOURCE_ID_AUDIO_CASSETTE_TINY = 115, - RESOURCE_ID_SUNNY_DAY_TINY = 116, - RESOURCE_ID_GENERIC_SPORTS_TINY = 117, - RESOURCE_ID_GENERIC_EMAIL_TINY = 118, - RESOURCE_ID_AMERICAN_FOOTBALL_TINY = 119, - RESOURCE_ID_TIMELINE_CALENDAR_TINY = 120, - RESOURCE_ID_BASEBALL_GAME_TINY = 121, - RESOURCE_ID_BASEBALL_GAME_SMALL = 122, - RESOURCE_ID_ALARM_CLOCK_TINY = 123, - RESOURCE_ID_BIRTHDAY_EVENT_TINY = 124, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_TINY = 125, - RESOURCE_ID_CAR_RENTAL_TINY = 126, - RESOURCE_ID_SCHEDULED_FLIGHT_TINY = 127, - RESOURCE_ID_CLOUDY_DAY_TINY = 128, - RESOURCE_ID_CRICKET_GAME_TINY = 129, - RESOURCE_ID_CRICKET_GAME_SMALL = 130, - RESOURCE_ID_CRICKET_GAME_LARGE = 131, - RESOURCE_ID_DINNER_RESERVATION_TINY = 132, - RESOURCE_ID_DISMISSED_PHONE_CALL_TINY = 133, - RESOURCE_ID_GENERIC_CONFIRMATION_TINY = 134, - RESOURCE_ID_GENERIC_PIN_TINY = 135, - RESOURCE_ID_GENERIC_WARNING_TINY = 136, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_TINY = 137, - RESOURCE_ID_GLUCOSE_MONITOR_TINY = 138, - RESOURCE_ID_HEAVY_RAIN_TINY = 139, - RESOURCE_ID_HEAVY_SNOW_TINY = 140, - RESOURCE_ID_HOCKEY_GAME_TINY = 141, - RESOURCE_ID_HOCKEY_GAME_SMALL = 142, - RESOURCE_ID_HOCKEY_GAME_LARGE = 143, - RESOURCE_ID_HOTEL_RESERVATION_TINY = 144, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_TINY = 145, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_SMALL = 146, - RESOURCE_ID_LIGHT_RAIN_TINY = 147, - RESOURCE_ID_LIGHT_SNOW_TINY = 148, - RESOURCE_ID_MAILBOX_NOTIFICATION_TINY = 149, - RESOURCE_ID_MAILBOX_NOTIFICATION_SMALL = 150, - RESOURCE_ID_MOVIE_EVENT_TINY = 151, - RESOURCE_ID_MUSIC_EVENT_TINY = 152, - RESOURCE_ID_NEWS_EVENT_TINY = 153, - RESOURCE_ID_PARTLY_CLOUDY_TINY = 154, - RESOURCE_ID_PAY_BILL_TINY = 155, - RESOURCE_ID_REACHED_FITNESS_GOAL_TINY = 156, - RESOURCE_ID_RADIO_SHOW_TINY = 157, - RESOURCE_ID_SCHEDULED_EVENT_TINY = 158, - RESOURCE_ID_SOCCER_GAME_TINY = 159, - RESOURCE_ID_SOCCER_GAME_SMALL = 160, - RESOURCE_ID_STOCKS_EVENT_TINY = 161, - RESOURCE_ID_BIRTHDAY_EVENT_SMALL = 162, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_SMALL = 163, - RESOURCE_ID_TIMELINE_CALENDAR_SMALL = 164, - RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_SMALL = 165, - RESOURCE_ID_ALARM_CLOCK_SMALL = 166, - RESOURCE_ID_CAR_RENTAL_SMALL = 167, - RESOURCE_ID_CHECK_INTERNET_CONNECTION_SMALL = 168, - RESOURCE_ID_DINNER_RESERVATION_SMALL = 169, - RESOURCE_ID_DISMISSED_PHONE_CALL_SMALL = 170, - RESOURCE_ID_GENERIC_SMS_SMALL = 171, - RESOURCE_ID_GENERIC_SMS_TINY = 172, - RESOURCE_ID_GENERIC_SMS_LARGE = 173, - RESOURCE_ID_FACEBOOK_NOTIFICATION_SMALL = 174, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_SMALL = 175, - RESOURCE_ID_GENERIC_CONFIRMATION_SMALL = 176, - RESOURCE_ID_GENERIC_EMAIL_SMALL = 177, - RESOURCE_ID_GENERIC_PIN_SMALL = 178, - RESOURCE_ID_GENERIC_REMINDER_SMALL = 179, - RESOURCE_ID_GENERIC_WARNING_SMALL = 180, - RESOURCE_ID_GENERIC_WEATHER_SMALL = 181, - RESOURCE_ID_GLUCOSE_MONITOR_SMALL = 182, - RESOURCE_ID_GMAIL_NOTIFICATION_SMALL = 183, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_SMALL = 184, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_SMALL = 185, - RESOURCE_ID_HEAVY_RAIN_SMALL = 186, - RESOURCE_ID_HEAVY_SNOW_SMALL = 187, - RESOURCE_ID_HOTEL_RESERVATION_SMALL = 188, - RESOURCE_ID_LIGHT_RAIN_SMALL = 189, - RESOURCE_ID_LIGHT_SNOW_SMALL = 190, - RESOURCE_ID_MOVIE_EVENT_SMALL = 191, - RESOURCE_ID_MUSIC_EVENT_SMALL = 192, - RESOURCE_ID_NEWS_EVENT_SMALL = 193, - RESOURCE_ID_PARTLY_CLOUDY_SMALL = 194, - RESOURCE_ID_PAY_BILL_SMALL = 195, - RESOURCE_ID_RADIO_SHOW_SMALL = 196, - RESOURCE_ID_REACHED_FITNESS_GOAL_SMALL = 197, - RESOURCE_ID_SCHEDULED_EVENT_SMALL = 198, - RESOURCE_ID_SUNNY_DAY_SMALL = 199, - RESOURCE_ID_GENERIC_SPORTS_SMALL = 200, - RESOURCE_ID_TIDE_IS_HIGH_SMALL = 201, - RESOURCE_ID_WATCH_DISCONNECTED_SMALL = 202, - RESOURCE_ID_ALARM_CLOCK_LARGE_STATIC = 203, - RESOURCE_ID_AMERICAN_FOOTBALL_LARGE = 204, - RESOURCE_ID_AMERICAN_FOOTBALL_SMALL = 205, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_LARGE = 206, - RESOURCE_ID_WHATSAPP_NOTIFICATION_LARGE = 207, - RESOURCE_ID_AUDIO_CASSETTE_LARGE = 208, - RESOURCE_ID_AUDIO_CASSETTE_SMALL = 209, - RESOURCE_ID_BASEBALL_GAME_LARGE = 210, - RESOURCE_ID_BIRTHDAY_EVENT_LARGE = 211, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_LARGE = 212, - RESOURCE_ID_TIMELINE_CALENDAR_LARGE = 213, - RESOURCE_ID_CAR_RENTAL_LARGE = 214, - RESOURCE_ID_CHECK_INTERNET_CONNECTION_LARGE = 215, - RESOURCE_ID_CLOUDY_DAY_LARGE = 216, - RESOURCE_ID_CLOUDY_DAY_SMALL = 217, - RESOURCE_ID_DAY_SEPARATOR_TINY = 218, - RESOURCE_ID_DAY_SEPARATOR_SMALL = 219, - RESOURCE_ID_DAY_SEPARATOR_LARGE = 220, - RESOURCE_ID_RESULT_DELETED_TINY = 221, - RESOURCE_ID_RESULT_DELETED_SMALL = 222, - RESOURCE_ID_DINNER_RESERVATION_LARGE = 223, - RESOURCE_ID_DISMISSED_PHONE_CALL_LARGE = 224, - RESOURCE_ID_DURING_PHONE_CALL_TINY = 225, - RESOURCE_ID_DURING_PHONE_CALL_SMALL = 226, - RESOURCE_ID_DURING_PHONE_CALL_LARGE = 227, - RESOURCE_ID_DURING_PHONE_CALL_CENTERED_LARGE = 228, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_LARGE = 229, - RESOURCE_ID_FACEBOOK_NOTIFICATION_LARGE = 230, - RESOURCE_ID_GENERIC_EMAIL_LARGE = 231, - RESOURCE_ID_GENERIC_PIN_LARGE = 232, - RESOURCE_ID_GENERIC_REMINDER_LARGE = 233, - RESOURCE_ID_GENERIC_SPORTS_LARGE = 234, - RESOURCE_ID_GENERIC_WEATHER_LARGE = 235, - RESOURCE_ID_GLUCOSE_MONITOR_LARGE = 236, - RESOURCE_ID_GMAIL_NOTIFICATION_LARGE = 237, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_LARGE = 238, - RESOURCE_ID_HEAVY_RAIN_LARGE = 239, - RESOURCE_ID_HEAVY_SNOW_LARGE = 240, - RESOURCE_ID_HOTEL_RESERVATION_LARGE = 241, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_LARGE = 242, - RESOURCE_ID_LIGHT_RAIN_LARGE = 243, - RESOURCE_ID_LIGHT_SNOW_LARGE = 244, - RESOURCE_ID_MAILBOX_NOTIFICATION_LARGE = 245, - RESOURCE_ID_MISSED_CALL_LARGE = 246, - RESOURCE_ID_MOVIE_EVENT_LARGE = 247, - RESOURCE_ID_MUSIC_EVENT_LARGE = 248, - RESOURCE_ID_NEWS_EVENT_LARGE = 249, - RESOURCE_ID_PARTLY_CLOUDY_LARGE = 250, - RESOURCE_ID_PAY_BILL_LARGE = 251, - RESOURCE_ID_RADIO_SHOW_LARGE = 252, - RESOURCE_ID_REACHED_FITNESS_GOAL_LARGE = 253, - RESOURCE_ID_SCHEDULED_EVENT_LARGE = 254, - RESOURCE_ID_SCHEDULED_FLIGHT_LARGE = 255, - RESOURCE_ID_SCHEDULED_FLIGHT_SMALL = 256, - RESOURCE_ID_RESULT_SENT_TINY = 257, - RESOURCE_ID_RESULT_SENT_SMALL = 258, - RESOURCE_ID_SOCCER_GAME_LARGE = 259, - RESOURCE_ID_STOCKS_EVENT_LARGE = 260, - RESOURCE_ID_STOCKS_EVENT_SMALL = 261, - RESOURCE_ID_SUNNY_DAY_LARGE = 262, - RESOURCE_ID_TELEGRAM_APP_LARGE = 263, - RESOURCE_ID_TELEGRAM_APP_SMALL = 264, - RESOURCE_ID_TIDE_IS_HIGH_LARGE = 265, - RESOURCE_ID_TWITTER_NOTIFICATION_LARGE = 266, - RESOURCE_ID_WATCH_DISCONNECTED_LARGE = 267, - RESOURCE_ID_RESULT_FAILED_TINY = 268, - RESOURCE_ID_RESULT_FAILED_SMALL = 269, - RESOURCE_ID_RESULT_FAILED_LARGE = 270, - RESOURCE_ID_GENERIC_QUESTION_TINY = 271, - RESOURCE_ID_GENERIC_QUESTION_SMALL = 272, - RESOURCE_ID_GENERIC_QUESTION_LARGE = 273, - RESOURCE_ID_NOTIFICATION_OUTLOOK_TINY = 274, - RESOURCE_ID_NOTIFICATION_OUTLOOK_SMALL = 275, - RESOURCE_ID_NOTIFICATION_OUTLOOK_LARGE = 276, - RESOURCE_ID_RAINING_AND_SNOWING_TINY = 277, - RESOURCE_ID_RAINING_AND_SNOWING_SMALL = 278, - RESOURCE_ID_RAINING_AND_SNOWING_LARGE = 279, - RESOURCE_ID_NOTIFICATION_FACETIME_TINY = 280, - RESOURCE_ID_NOTIFICATION_FACETIME_SMALL = 281, - RESOURCE_ID_NOTIFICATION_FACETIME_LARGE = 282, - RESOURCE_ID_NOTIFICATION_LINE_TINY = 283, - RESOURCE_ID_NOTIFICATION_LINE_SMALL = 284, - RESOURCE_ID_NOTIFICATION_LINE_LARGE = 285, - RESOURCE_ID_NOTIFICATION_SKYPE_TINY = 286, - RESOURCE_ID_NOTIFICATION_SKYPE_SMALL = 287, - RESOURCE_ID_NOTIFICATION_SKYPE_LARGE = 288, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_TINY = 289, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_SMALL = 290, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_LARGE = 291, - RESOURCE_ID_NOTIFICATION_VIBER_TINY = 292, - RESOURCE_ID_NOTIFICATION_VIBER_SMALL = 293, - RESOURCE_ID_NOTIFICATION_VIBER_LARGE = 294, - RESOURCE_ID_NOTIFICATION_WECHAT_TINY = 295, - RESOURCE_ID_NOTIFICATION_WECHAT_SMALL = 296, - RESOURCE_ID_NOTIFICATION_WECHAT_LARGE = 297, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_TINY = 298, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_SMALL = 299, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_LARGE = 300, - RESOURCE_ID_TV_SHOW_TINY = 301, - RESOURCE_ID_TV_SHOW_SMALL = 302, - RESOURCE_ID_TV_SHOW_LARGE = 303, - RESOURCE_ID_END_OF_TIMELINE = 304, - RESOURCE_ID_BASKETBALL_TINY = 305, - RESOURCE_ID_BASKETBALL_SMALL = 306, - RESOURCE_ID_BASKETBALL_LARGE = 307, - RESOURCE_ID_RESULT_DISMISSED_TINY = 308, - RESOURCE_ID_RESULT_DISMISSED_SMALL = 309, - RESOURCE_ID_TIDE_IS_HIGH_TINY = 310, - RESOURCE_ID_OUTGOING_CALL_LARGE = 311, - RESOURCE_ID_NOTIFICATION_HIPCHAT_TINY = 312, - RESOURCE_ID_NOTIFICATION_HIPCHAT_SMALL = 313, - RESOURCE_ID_NOTIFICATION_HIPCHAT_LARGE = 314, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_TINY = 315, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_SMALL = 316, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_LARGE = 317, - RESOURCE_ID_INCOMING_PHONE_CALL_TINY = 318, - RESOURCE_ID_INCOMING_PHONE_CALL_SMALL = 319, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_TINY = 320, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_SMALL = 321, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_LARGE = 322, - RESOURCE_ID_NOTIFICATION_KIK_TINY = 323, - RESOURCE_ID_NOTIFICATION_KIK_SMALL = 324, - RESOURCE_ID_NOTIFICATION_KIK_LARGE = 325, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_TINY = 326, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_SMALL = 327, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_LARGE = 328, - RESOURCE_ID_LOCATION_TINY = 329, - RESOURCE_ID_LOCATION_SMALL = 330, - RESOURCE_ID_LOCATION_LARGE = 331, - RESOURCE_ID_SUNSET_TINY = 332, - RESOURCE_ID_SUNSET_SMALL = 333, - RESOURCE_ID_SUNSET_LARGE = 334, - RESOURCE_ID_SUNRISE_TINY = 335, - RESOURCE_ID_SUNRISE_SMALL = 336, - RESOURCE_ID_SUNRISE_LARGE = 337, - RESOURCE_ID_SETTINGS_TINY = 338, - RESOURCE_ID_SETTINGS_SMALL = 339, - RESOURCE_ID_SETTINGS_LARGE = 340, - RESOURCE_ID_PLUS_ICON_BLACK = 341, - RESOURCE_ID_PLUS_ICON_DOTTED = 342, - RESOURCE_ID_RESULT_SHREDDED_TINY = 343, - RESOURCE_ID_RESULT_SHREDDED_SMALL = 344, - RESOURCE_ID_QUIET_TIME_MOUSE = 345, - RESOURCE_ID_REMINDER_SNOOZE = 346, - RESOURCE_ID_QUIET_TIME_STATUS_BAR = 347, - RESOURCE_ID_QUICK_DISMISS = 348, - RESOURCE_ID_THUMBS_UP_SMALL = 349, - RESOURCE_ID_THUMBS_UP_LARGE = 350, - RESOURCE_ID_ARROW_UP_SMALL = 351, - RESOURCE_ID_ARROW_DOWN_SMALL = 352, - RESOURCE_ID_ACTIVITY_TINY = 353, - RESOURCE_ID_ACTIVITY_SMALL = 354, - RESOURCE_ID_ACTIVITY_LARGE = 355, - RESOURCE_ID_SLEEP_TINY = 356, - RESOURCE_ID_SLEEP_SMALL = 357, - RESOURCE_ID_SLEEP_LARGE = 358, - RESOURCE_ID_REWARD_AVERAGE_LARGE = 359, - RESOURCE_ID_REWARD_GOOD_LARGE = 360, - RESOURCE_ID_REWARD_BAD_LARGE = 361, - RESOURCE_ID_CALORIES_TINY = 362, - RESOURCE_ID_DISTANCE_TINY = 363, - RESOURCE_ID_DURATION_TINY = 364, - RESOURCE_ID_PACE_TINY = 365, - RESOURCE_ID_RUN_TINY = 366, - RESOURCE_ID_RUN_LARGE = 367, - RESOURCE_ID_MOON_TINY = 368, - RESOURCE_ID_NOTIFICATION_AMAZON_TINY = 369, - RESOURCE_ID_NOTIFICATION_AMAZON_SMALL = 370, - RESOURCE_ID_NOTIFICATION_AMAZON_LARGE = 371, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_TINY = 372, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_SMALL = 373, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_LARGE = 374, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_TINY = 375, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_SMALL = 376, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_LARGE = 377, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_TINY = 378, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_SMALL = 379, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_LARGE = 380, - RESOURCE_ID_NOTIFICATION_LINKEDIN_TINY = 381, - RESOURCE_ID_NOTIFICATION_LINKEDIN_SMALL = 382, - RESOURCE_ID_NOTIFICATION_LINKEDIN_LARGE = 383, - RESOURCE_ID_NOTIFICATION_SLACK_TINY = 384, - RESOURCE_ID_NOTIFICATION_SLACK_SMALL = 385, - RESOURCE_ID_NOTIFICATION_SLACK_LARGE = 386, - RESOURCE_ID_SMART_ALARM_TINY = 387, - RESOURCE_ID_HEALTH_APP_ACTIVITY = 388, - RESOURCE_ID_HEALTH_APP_SLEEP = 389, - RESOURCE_ID_HEALTH_APP_CROWN = 390, - RESOURCE_ID_HEALTH_APP_HR = 391, - RESOURCE_ID_HEALTH_APP_PULSING_HEART = 392, - RESOURCE_ID_WORKOUT_APP_WALK = 393, - RESOURCE_ID_WORKOUT_APP_WALK_SMALL = 394, - RESOURCE_ID_WORKOUT_APP_WALK_TINY = 395, - RESOURCE_ID_WORKOUT_APP_RUN = 396, - RESOURCE_ID_WORKOUT_APP_RUN_SMALL = 397, - RESOURCE_ID_WORKOUT_APP_RUN_TINY = 398, - RESOURCE_ID_WORKOUT_APP_WORKOUT = 399, - RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL = 400, - RESOURCE_ID_WORKOUT_APP_DETECTED = 401, - RESOURCE_ID_WORKOUT_APP_HEART = 402, - RESOURCE_ID_WORKOUT_APP_MEASURING_HR = 403, - RESOURCE_ID_WORKOUT_APP_HR_PULSE_TINY = 404, - RESOURCE_ID_WORKOUT_APP_END = 405, - RESOURCE_ID_WORKOUT_APP_ONE = 406, - RESOURCE_ID_WORKOUT_APP_TWO = 407, - RESOURCE_ID_WORKOUT_APP_THREE = 408, - RESOURCE_ID_HEART_TINY = 409, - RESOURCE_ID_HEART_SMALL = 410, - RESOURCE_ID_HEART_LARGE = 411, - RESOURCE_ID_BACKLIGHT = 412, - RESOURCE_ID_AIRPLANE = 413, - RESOURCE_ID_ALARM_CLOCK_LARGE = 414, - RESOURCE_ID_RESULT_DELETED_LARGE = 415, - RESOURCE_ID_RESULT_SHREDDED_LARGE = 416, - RESOURCE_ID_RESULT_DISMISSED_LARGE = 417, - RESOURCE_ID_GENERIC_CONFIRMATION_LARGE = 418, - RESOURCE_ID_INCOMING_PHONE_CALL_LARGE = 419, - RESOURCE_ID_RESULT_SENT_LARGE = 420, - RESOURCE_ID_RESULT_UNMUTE_LARGE = 421, - RESOURCE_ID_BATTERY_CHARGING_ICON = 422, - RESOURCE_ID_HEALTH_ICON_MOON = 423, - RESOURCE_ID_HEALTH_ICON_ROTATED_MOON = 424, - RESOURCE_ID_SYSTEM_FCC_MARK = 425, - RESOURCE_ID_SYSTEM_KCC_MARK = 426, - RESOURCE_ID_SYSTEM_CE_MARK = 427, - RESOURCE_ID_SYSTEM_WEEE_MARK = 428, - RESOURCE_ID_SYSTEM_R_MARK = 429, - RESOURCE_ID_SYSTEM_T_MARK = 430, - RESOURCE_ID_SYSTEM_AUS_RCM_MARK = 431, - RESOURCE_ID_SYSTEM_NOM_NYCE_MARK = 432, - RESOURCE_ID_WEATHER_CHANNEL_LOGO = 433, - RESOURCE_ID_STRIDE_SHOE = 434, - RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE = 435, - RESOURCE_ID_EMOJI_BIG_SMILE_LARGE = 436, - RESOURCE_ID_EMOJI_HEART_LARGE = 437, - RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE = 438, - RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE = 439, - RESOURCE_ID_EMOJI_SAD_LARGE = 440, - RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE = 441, - RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE = 442, - RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE = 443, - RESOURCE_ID_EMOJI_THUMBS_UP_LARGE = 444, - RESOURCE_ID_EMOJI_WINK_LARGE = 445, - RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE = 446, - RESOURCE_ID_VIBE_SCORE_REVEILLE = 447, - RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK = 448, - RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE = 449, - RESOURCE_ID_VIBE_SCORE_PULSE = 450, - RESOURCE_ID_VIBE_SCORE_JACKHAMMER = 451, - RESOURCE_ID_VIBE_SCORE_MARIO = 452, - RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW = 453, - RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH = 454, - RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW = 455, - RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH = 456, - RESOURCE_ID_VIBE_SCORE_ALARM_LPM = 457, - RESOURCE_ID_SMART_ALARM_ICON_BLACK = 458, - RESOURCE_ID_BLE_HRM_SHARE_REQUEST_LARGE = 459, - RESOURCE_ID_BLE_HRM_SHARED = 460, - RESOURCE_ID_BLE_HRM_NOT_SHARED = 461, - RESOURCE_ID_CONNECTIVITY_SHARING_HRM = 462, - RESOURCE_ID_STORED_APP_GOLF = 463, - RESOURCE_ID_BT_BOOT_IMAGE = 464, - RESOURCE_ID_BT_FW_IMAGE = 465, - RESOURCE_ID_AS7000_FW_IMAGE = 466, - RESOURCE_ID_TIMEZONE_DATABASE = 467, - RESOURCE_ID_FONT_FALLBACK_INTERNAL = 468, - RESOURCE_ID_ARROW_DOWN = 469, - RESOURCE_ID_VOICE_MICROPHONE_LARGE = 470, - RESOURCE_ID_NO_EVENTS_LARGE = 471, - RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE = 472, - RESOURCE_ID_RESULT_MUTE_LARGE = 473, - RESOURCE_ID_JS_TICTOC = 474, - RESOURCE_ID_PUG = 475, - RESOURCE_ID_STRINGS = 476, - RESOURCE_ID_GOTHIC_14_EXTENDED = 477, - RESOURCE_ID_GOTHIC_14_BOLD_EXTENDED = 478, - RESOURCE_ID_GOTHIC_18_EXTENDED = 479, - RESOURCE_ID_GOTHIC_18_BOLD_EXTENDED = 480, - RESOURCE_ID_GOTHIC_24_EXTENDED = 481, - RESOURCE_ID_GOTHIC_24_BOLD_EXTENDED = 482, - RESOURCE_ID_GOTHIC_28_EXTENDED = 483, - RESOURCE_ID_GOTHIC_28_BOLD_EXTENDED = 484, - RESOURCE_ID_BITHAM_18_LIGHT_SUBSET_EXTENDED = 485, - RESOURCE_ID_BITHAM_30_BLACK_EXTENDED = 486, - RESOURCE_ID_BITHAM_34_LIGHT_SUBSET_EXTENDED = 487, - RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS_EXTENDED = 488, - RESOURCE_ID_BITHAM_42_BOLD_EXTENDED = 489, - RESOURCE_ID_BITHAM_42_LIGHT_EXTENDED = 490, - RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS_EXTENDED = 491, - RESOURCE_ID_ROBOTO_CONDENSED_21_EXTENDED = 492, - RESOURCE_ID_ROBOTO_BOLD_SUBSET_49_EXTENDED = 493, - RESOURCE_ID_DROID_SERIF_28_BOLD_EXTENDED = 494, - RESOURCE_ID_GOTHIC_09_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_14_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_18_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_24_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_28_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_36_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_36_BOLD_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_20_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_32_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_36_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_38_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_42_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_28_LIGHT_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_FONT_FALLBACK_INTERNAL_EXTENDED = INVALID_RESOURCE, -} ResourceId; \ No newline at end of file diff --git a/tests/overrides/default/resources/silk/resource/resource_version.auto.h b/tests/overrides/default/resources/silk/resource/resource_version.auto.h deleted file mode 100644 index 1802590f33..0000000000 --- a/tests/overrides/default/resources/silk/resource/resource_version.auto.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -// -// AUTOGENERATED -// DO NOT MODIFY -// - -static const ResourceVersion SYSTEM_RESOURCE_VERSION = { - .crc = 2259022077, - .timestamp = 0 -}; diff --git a/tests/overrides/default/resources/silk/resource/timeline_resource_ids.auto.h b/tests/overrides/default/resources/silk/resource/timeline_resource_ids.auto.h deleted file mode 100644 index 912c402543..0000000000 --- a/tests/overrides/default/resources/silk/resource/timeline_resource_ids.auto.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -// -// AUTOGENERATED -// DO NOT MODIFY -// - -typedef enum { - TIMELINE_RESOURCE_INVALID = 0, - TIMELINE_RESOURCE_NOTIFICATION_GENERIC = 0x80000001, - TIMELINE_RESOURCE_TIMELINE_MISSED_CALL = 0x80000002, - TIMELINE_RESOURCE_NOTIFICATION_REMINDER = 0x80000003, - TIMELINE_RESOURCE_NOTIFICATION_FLAG = 0x80000004, - TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP = 0x80000005, - TIMELINE_RESOURCE_NOTIFICATION_TWITTER = 0x80000006, - TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM = 0x80000007, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS = 0x80000008, - TIMELINE_RESOURCE_NOTIFICATION_GMAIL = 0x80000009, - TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER = 0x8000000a, - TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK = 0x8000000b, - TIMELINE_RESOURCE_AUDIO_CASSETTE = 0x8000000c, - TIMELINE_RESOURCE_ALARM_CLOCK = 0x8000000d, - TIMELINE_RESOURCE_TIMELINE_WEATHER = 0x8000000e, - TIMELINE_RESOURCE_TIMELINE_SUN = 0x80000010, - TIMELINE_RESOURCE_TIMELINE_SPORTS = 0x80000011, - TIMELINE_RESOURCE_GENERIC_EMAIL = 0x80000013, - TIMELINE_RESOURCE_AMERICAN_FOOTBALL = 0x80000014, - TIMELINE_RESOURCE_TIMELINE_CALENDAR = 0x80000015, - TIMELINE_RESOURCE_TIMELINE_BASEBALL = 0x80000016, - TIMELINE_RESOURCE_BIRTHDAY_EVENT = 0x80000017, - TIMELINE_RESOURCE_CAR_RENTAL = 0x80000018, - TIMELINE_RESOURCE_CLOUDY_DAY = 0x80000019, - TIMELINE_RESOURCE_CRICKET_GAME = 0x8000001a, - TIMELINE_RESOURCE_DINNER_RESERVATION = 0x8000001b, - TIMELINE_RESOURCE_GENERIC_WARNING = 0x8000001c, - TIMELINE_RESOURCE_GLUCOSE_MONITOR = 0x8000001d, - TIMELINE_RESOURCE_HOCKEY_GAME = 0x8000001e, - TIMELINE_RESOURCE_HOTEL_RESERVATION = 0x8000001f, - TIMELINE_RESOURCE_LIGHT_RAIN = 0x80000020, - TIMELINE_RESOURCE_LIGHT_SNOW = 0x80000021, - TIMELINE_RESOURCE_MOVIE_EVENT = 0x80000022, - TIMELINE_RESOURCE_MUSIC_EVENT = 0x80000023, - TIMELINE_RESOURCE_NEWS_EVENT = 0x80000024, - TIMELINE_RESOURCE_PARTLY_CLOUDY = 0x80000025, - TIMELINE_RESOURCE_PAY_BILL = 0x80000026, - TIMELINE_RESOURCE_RADIO_SHOW = 0x80000027, - TIMELINE_RESOURCE_SCHEDULED_EVENT = 0x80000028, - TIMELINE_RESOURCE_SOCCER_GAME = 0x80000029, - TIMELINE_RESOURCE_STOCKS_EVENT = 0x8000002a, - TIMELINE_RESOURCE_RESULT_DELETED = 0x8000002b, - TIMELINE_RESOURCE_CHECK_INTERNET_CONNECTION = 0x8000002c, - TIMELINE_RESOURCE_GENERIC_SMS = 0x8000002d, - TIMELINE_RESOURCE_RESULT_MUTE = 0x8000002e, - TIMELINE_RESOURCE_RESULT_SENT = 0x8000002f, - TIMELINE_RESOURCE_WATCH_DISCONNECTED = 0x80000030, - TIMELINE_RESOURCE_DURING_PHONE_CALL = 0x80000031, - TIMELINE_RESOURCE_TIDE_IS_HIGH = 0x80000032, - TIMELINE_RESOURCE_RESULT_DISMISSED = 0x80000033, - TIMELINE_RESOURCE_HEAVY_RAIN = 0x80000034, - TIMELINE_RESOURCE_HEAVY_SNOW = 0x80000035, - TIMELINE_RESOURCE_SCHEDULED_FLIGHT = 0x80000036, - TIMELINE_RESOURCE_GENERIC_CONFIRMATION = 0x80000037, - TIMELINE_RESOURCE_DAY_SEPARATOR = 0x80000038, - TIMELINE_RESOURCE_NO_EVENTS = 0x80000039, - TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER = 0x8000003a, - TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM = 0x8000003b, - TIMELINE_RESOURCE_NOTIFICATION_MAILBOX = 0x8000003c, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX = 0x8000003d, - TIMELINE_RESOURCE_RESULT_FAILED = 0x8000003e, - TIMELINE_RESOURCE_GENERIC_QUESTION = 0x8000003f, - TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK = 0x80000040, - TIMELINE_RESOURCE_RAINING_AND_SNOWING = 0x80000041, - TIMELINE_RESOURCE_REACHED_FITNESS_GOAL = 0x80000042, - TIMELINE_RESOURCE_NOTIFICATION_LINE = 0x80000043, - TIMELINE_RESOURCE_NOTIFICATION_SKYPE = 0x80000044, - TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT = 0x80000045, - TIMELINE_RESOURCE_NOTIFICATION_VIBER = 0x80000046, - TIMELINE_RESOURCE_NOTIFICATION_WECHAT = 0x80000047, - TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL = 0x80000048, - TIMELINE_RESOURCE_TV_SHOW = 0x80000049, - TIMELINE_RESOURCE_BASKETBALL = 0x8000004a, - TIMELINE_RESOURCE_DISMISSED_PHONE_CALL = 0x8000004b, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MESSENGER = 0x8000004c, - TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT = 0x8000004d, - TIMELINE_RESOURCE_INCOMING_PHONE_CALL = 0x8000004e, - TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK = 0x8000004f, - TIMELINE_RESOURCE_NOTIFICATION_KIK = 0x80000050, - TIMELINE_RESOURCE_NOTIFICATION_LIGHTHOUSE = 0x80000051, - TIMELINE_RESOURCE_LOCATION = 0x80000052, - TIMELINE_RESOURCE_SETTINGS = 0x80000053, - TIMELINE_RESOURCE_SUNRISE = 0x80000054, - TIMELINE_RESOURCE_SUNSET = 0x80000055, - TIMELINE_RESOURCE_RESULT_UNMUTE = 0x80000056, - TIMELINE_RESOURCE_RESULT_UNMUTE_ALT = 0x8000005e, - TIMELINE_RESOURCE_DURING_PHONE_CALL_CENTERED = 0x8000005f, - TIMELINE_RESOURCE_TIMELINE_EMPTY_CALENDAR = 0x80000060, - TIMELINE_RESOURCE_THUMBS_UP = 0x80000061, - TIMELINE_RESOURCE_ARROW_UP = 0x80000062, - TIMELINE_RESOURCE_ARROW_DOWN = 0x80000063, - TIMELINE_RESOURCE_ACTIVITY = 0x80000064, - TIMELINE_RESOURCE_SLEEP = 0x80000065, - TIMELINE_RESOURCE_REWARD_BAD = 0x80000066, - TIMELINE_RESOURCE_REWARD_GOOD = 0x80000067, - TIMELINE_RESOURCE_REWARD_AVERAGE = 0x80000068, - TIMELINE_RESOURCE_CALORIES = 0x80000069, - TIMELINE_RESOURCE_DISTANCE = 0x8000006a, - TIMELINE_RESOURCE_DURATION = 0x8000006b, - TIMELINE_RESOURCE_PACE = 0x8000006c, - TIMELINE_RESOURCE_RUN = 0x8000006d, - TIMELINE_RESOURCE_NOTIFICATION_FACETIME = 0x8000006e, - TIMELINE_RESOURCE_NOTIFICATION_AMAZON = 0x8000006f, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS = 0x80000070, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS = 0x80000071, - TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS = 0x80000072, - TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN = 0x80000073, - TIMELINE_RESOURCE_NOTIFICATION_SLACK = 0x80000074, - TIMELINE_RESOURCE_SMART_ALARM = 0x80000075, - TIMELINE_RESOURCE_HEART = 0x80000076, - TIMELINE_RESOURCE_BLE_HRM_SHARING = 0x80000077, -} TimelineResourceId; - -#define NUM_TIMELINE_RESOURCES 120 diff --git a/tests/overrides/default/resources/snowy/resource/resource_ids.auto.h b/tests/overrides/default/resources/snowy/resource/resource_ids.auto.h deleted file mode 100644 index d9ffedde8f..0000000000 --- a/tests/overrides/default/resources/snowy/resource/resource_ids.auto.h +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// -// AUTOGENERATED BY BUILD -// DO NOT MODIFY - CHANGES WILL BE OVERWRITTEN -// - -typedef enum { - INVALID_RESOURCE = 0, - RESOURCE_ID_INVALID = 0, - DEFAULT_MENU_ICON = 0, - RESOURCE_ID_ACTION_BAR_ICON_X = 1, - RESOURCE_ID_BT_PAIR_SUCCESS = 2, - RESOURCE_ID_BT_PAIR_FAILURE = 3, - RESOURCE_ID_BT_PAIR_APPROVE_ON_PHONE = 4, - RESOURCE_ID_BT_PAIR_CONFIRMATION = 5, - RESOURCE_ID_SPINNER_BACKGROUND = 6, - RESOURCE_ID_GOTHIC_18_BOLD = 7, - RESOURCE_ID_BATTERY_ICON_LOW_LARGE = 8, - RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE = 9, - RESOURCE_ID_BATTERY_ICON_FULL_LARGE = 10, - RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED = 11, - RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED = 12, - RESOURCE_ID_BATTERY_ICON_CHARGE = 13, - RESOURCE_ID_BATTERY_NEEDS_CHARGING = 14, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_AIRPLANE_MODE = 15, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CONNECTED = 16, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DISCONNECTED = 17, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_DND = 18, - RESOURCE_ID_CONNECTIVITY_BLUETOOTH_CALLS_ONLY = 19, - RESOURCE_ID_QUIET_TIME = 20, - RESOURCE_ID_MUSIC_APP_GLANCE_PLAY = 21, - RESOURCE_ID_MUSIC_APP_GLANCE_PAUSE = 22, - RESOURCE_ID_NOTIFICATIONS_APP_GLANCE = 23, - RESOURCE_ID_SEND_TEXT_APP_GLANCE = 24, - RESOURCE_ID_WATCHFACES_APP_GLANCE = 25, - RESOURCE_ID_MENU_ICON_TICTOC_WATCH = 26, - RESOURCE_ID_MENU_ICON_KICKSTART_WATCH = 27, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH_ALT = 28, - RESOURCE_ID_SETTINGS_ICON_BLUETOOTH = 29, - RESOURCE_ID_SETTINGS_ICON_AIRPLANE = 30, - RESOURCE_ID_ACTION_BAR_ICON_SMS = 31, - RESOURCE_ID_GOTHIC_09 = 32, - RESOURCE_ID_GOTHIC_14 = 33, - RESOURCE_ID_GOTHIC_14_EMOJI = 34, - RESOURCE_ID_GOTHIC_14_BOLD = 35, - RESOURCE_ID_GOTHIC_18 = 36, - RESOURCE_ID_GOTHIC_18_COMPRESSED = 37, - RESOURCE_ID_GOTHIC_18_EMOJI = 38, - RESOURCE_ID_GOTHIC_24 = 39, - RESOURCE_ID_GOTHIC_24_BOLD = 40, - RESOURCE_ID_GOTHIC_24_EMOJI = 41, - RESOURCE_ID_GOTHIC_28 = 42, - RESOURCE_ID_GOTHIC_28_BOLD = 43, - RESOURCE_ID_GOTHIC_28_EMOJI = 44, - RESOURCE_ID_GOTHIC_36 = 45, - RESOURCE_ID_GOTHIC_36_BOLD = 46, - RESOURCE_ID_BITHAM_30_BLACK = 47, - RESOURCE_ID_BITHAM_42_BOLD = 48, - RESOURCE_ID_BITHAM_42_LIGHT = 49, - RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS = 50, - RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS = 51, - RESOURCE_ID_BITHAM_34_LIGHT_SUBSET = 52, - RESOURCE_ID_BITHAM_18_LIGHT_SUBSET = 53, - RESOURCE_ID_ROBOTO_CONDENSED_21 = 54, - RESOURCE_ID_ROBOTO_BOLD_SUBSET_49 = 55, - RESOURCE_ID_DROID_SERIF_28_BOLD = 56, - RESOURCE_ID_LECO_20_BOLD_NUMBERS = 57, - RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM = 58, - RESOURCE_ID_LECO_32_BOLD_NUMBERS = 59, - RESOURCE_ID_LECO_36_BOLD_NUMBERS = 60, - RESOURCE_ID_LECO_38_BOLD_NUMBERS = 61, - RESOURCE_ID_LECO_42_NUMBERS = 62, - RESOURCE_ID_LECO_28_LIGHT_NUMBERS = 63, - RESOURCE_ID_MUSIC_ICON_SKIP_FORWARD = 64, - RESOURCE_ID_MUSIC_ICON_SKIP_BACKWARD = 65, - RESOURCE_ID_MUSIC_ICON_ELLIPSIS = 66, - RESOURCE_ID_MUSIC_ICON_PAUSE = 67, - RESOURCE_ID_MUSIC_ICON_PLAY = 68, - RESOURCE_ID_MUSIC_ICON_PLAY_PAUSE = 69, - RESOURCE_ID_MUSIC_ICON_VOLUME_UP = 70, - RESOURCE_ID_MUSIC_ICON_VOLUME_DOWN = 71, - RESOURCE_ID_MUSIC_LARGE_CASSETTE = 72, - RESOURCE_ID_MUSIC_LARGE_VOLUME_UP = 73, - RESOURCE_ID_MUSIC_LARGE_VOLUME_DOWN = 74, - RESOURCE_ID_MUSIC_LARGE_PAUSED = 75, - RESOURCE_ID_MUSIC_IMAGE_NO_MUSIC = 76, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHFACE_ICON = 77, - RESOURCE_ID_MENU_LAYER_GENERIC_WATCHAPP_ICON = 78, - RESOURCE_ID_UNCHECKED_RADIO_BUTTON = 79, - RESOURCE_ID_CHECKED_RADIO_BUTTON = 80, - RESOURCE_ID_ACTION_BAR_ICON_MORE = 81, - RESOURCE_ID_ACTION_BAR_ICON_SNOOZE = 82, - RESOURCE_ID_ACTION_BAR_ICON_PAUSE = 83, - RESOURCE_ID_ACTION_BAR_ICON_START = 84, - RESOURCE_ID_ACTION_BAR_ICON_STOP = 85, - RESOURCE_ID_ACTION_BAR_ICON_TOGGLE = 86, - RESOURCE_ID_ACTION_MENU_FADE_TOP = 87, - RESOURCE_ID_ACTION_MENU_FADE_BOTTOM = 88, - RESOURCE_ID_CHECKBOX_ICON_CHECKED = 89, - RESOURCE_ID_CHECKBOX_ICON_UNCHECKED = 90, - RESOURCE_ID_CHECKMARK_ICON_BLACK = 91, - RESOURCE_ID_CHECKMARK_ICON_DOTTED = 92, - RESOURCE_ID_BLE_HRM_SHARING_TINY = 93, - RESOURCE_ID_BLE_HRM_SHARING_SMALL = 94, - RESOURCE_ID_BLE_HRM_SHARING_LARGE = 95, - RESOURCE_ID_NOTIFICATION_GENERIC_TINY = 96, - RESOURCE_ID_NOTIFICATION_GENERIC_SMALL = 97, - RESOURCE_ID_NOTIFICATION_GENERIC_LARGE = 98, - RESOURCE_ID_MISSED_CALL_TINY = 99, - RESOURCE_ID_MISSED_CALL_SMALL = 100, - RESOURCE_ID_GENERIC_REMINDER_TINY = 101, - RESOURCE_ID_WHATSAPP_NOTIFICATION_TINY = 102, - RESOURCE_ID_WHATSAPP_NOTIFICATION_SMALL = 103, - RESOURCE_ID_TWITTER_NOTIFICATION_TINY = 104, - RESOURCE_ID_TWITTER_NOTIFICATION_SMALL = 105, - RESOURCE_ID_TELEGRAM_APP_TINY = 106, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_TINY = 107, - RESOURCE_ID_GMAIL_NOTIFICATION_TINY = 108, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_TINY = 109, - RESOURCE_ID_FACEBOOK_NOTIFICATION_TINY = 110, - RESOURCE_ID_GENERIC_WEATHER_TINY = 111, - RESOURCE_ID_AUDIO_CASSETTE_TINY = 112, - RESOURCE_ID_SUNNY_DAY_TINY = 113, - RESOURCE_ID_GENERIC_SPORTS_TINY = 114, - RESOURCE_ID_GENERIC_EMAIL_TINY = 115, - RESOURCE_ID_AMERICAN_FOOTBALL_TINY = 116, - RESOURCE_ID_TIMELINE_CALENDAR_TINY = 117, - RESOURCE_ID_BASEBALL_GAME_TINY = 118, - RESOURCE_ID_BASEBALL_GAME_SMALL = 119, - RESOURCE_ID_ALARM_CLOCK_TINY = 120, - RESOURCE_ID_BIRTHDAY_EVENT_TINY = 121, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_TINY = 122, - RESOURCE_ID_CAR_RENTAL_TINY = 123, - RESOURCE_ID_SCHEDULED_FLIGHT_TINY = 124, - RESOURCE_ID_CLOUDY_DAY_TINY = 125, - RESOURCE_ID_CRICKET_GAME_TINY = 126, - RESOURCE_ID_CRICKET_GAME_SMALL = 127, - RESOURCE_ID_CRICKET_GAME_LARGE = 128, - RESOURCE_ID_DINNER_RESERVATION_TINY = 129, - RESOURCE_ID_DISMISSED_PHONE_CALL_TINY = 130, - RESOURCE_ID_GENERIC_CONFIRMATION_TINY = 131, - RESOURCE_ID_GENERIC_PIN_TINY = 132, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_TINY = 133, - RESOURCE_ID_GLUCOSE_MONITOR_TINY = 134, - RESOURCE_ID_HEAVY_RAIN_TINY = 135, - RESOURCE_ID_HEAVY_SNOW_TINY = 136, - RESOURCE_ID_HOCKEY_GAME_TINY = 137, - RESOURCE_ID_HOCKEY_GAME_SMALL = 138, - RESOURCE_ID_HOCKEY_GAME_LARGE = 139, - RESOURCE_ID_HOTEL_RESERVATION_TINY = 140, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_TINY = 141, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_SMALL = 142, - RESOURCE_ID_LIGHT_RAIN_TINY = 143, - RESOURCE_ID_LIGHT_SNOW_TINY = 144, - RESOURCE_ID_MAILBOX_NOTIFICATION_TINY = 145, - RESOURCE_ID_MAILBOX_NOTIFICATION_SMALL = 146, - RESOURCE_ID_MOVIE_EVENT_TINY = 147, - RESOURCE_ID_MUSIC_EVENT_TINY = 148, - RESOURCE_ID_NEWS_EVENT_TINY = 149, - RESOURCE_ID_PARTLY_CLOUDY_TINY = 150, - RESOURCE_ID_PAY_BILL_TINY = 151, - RESOURCE_ID_REACHED_FITNESS_GOAL_TINY = 152, - RESOURCE_ID_RADIO_SHOW_TINY = 153, - RESOURCE_ID_SCHEDULED_EVENT_TINY = 154, - RESOURCE_ID_SOCCER_GAME_TINY = 155, - RESOURCE_ID_SOCCER_GAME_SMALL = 156, - RESOURCE_ID_STOCKS_EVENT_TINY = 157, - RESOURCE_ID_BIRTHDAY_EVENT_SMALL = 158, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_SMALL = 159, - RESOURCE_ID_TIMELINE_CALENDAR_SMALL = 160, - RESOURCE_ID_TIMELINE_EMPTY_CALENDAR_SMALL = 161, - RESOURCE_ID_ALARM_CLOCK_SMALL = 162, - RESOURCE_ID_CAR_RENTAL_SMALL = 163, - RESOURCE_ID_CHECK_INTERNET_CONNECTION_SMALL = 164, - RESOURCE_ID_DINNER_RESERVATION_SMALL = 165, - RESOURCE_ID_DISMISSED_PHONE_CALL_SMALL = 166, - RESOURCE_ID_GENERIC_SMS_SMALL = 167, - RESOURCE_ID_GENERIC_SMS_TINY = 168, - RESOURCE_ID_GENERIC_SMS_LARGE = 169, - RESOURCE_ID_FACEBOOK_NOTIFICATION_SMALL = 170, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_SMALL = 171, - RESOURCE_ID_GENERIC_CONFIRMATION_SMALL = 172, - RESOURCE_ID_GENERIC_EMAIL_SMALL = 173, - RESOURCE_ID_GENERIC_PIN_SMALL = 174, - RESOURCE_ID_GENERIC_REMINDER_SMALL = 175, - RESOURCE_ID_GENERIC_WARNING_SMALL = 176, - RESOURCE_ID_GENERIC_WEATHER_SMALL = 177, - RESOURCE_ID_GLUCOSE_MONITOR_SMALL = 178, - RESOURCE_ID_GMAIL_NOTIFICATION_SMALL = 179, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_SMALL = 180, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_SMALL = 181, - RESOURCE_ID_HEAVY_RAIN_SMALL = 182, - RESOURCE_ID_HEAVY_SNOW_SMALL = 183, - RESOURCE_ID_HOTEL_RESERVATION_SMALL = 184, - RESOURCE_ID_LIGHT_RAIN_SMALL = 185, - RESOURCE_ID_LIGHT_SNOW_SMALL = 186, - RESOURCE_ID_MOVIE_EVENT_SMALL = 187, - RESOURCE_ID_MUSIC_EVENT_SMALL = 188, - RESOURCE_ID_NEWS_EVENT_SMALL = 189, - RESOURCE_ID_PARTLY_CLOUDY_SMALL = 190, - RESOURCE_ID_PAY_BILL_SMALL = 191, - RESOURCE_ID_RADIO_SHOW_SMALL = 192, - RESOURCE_ID_REACHED_FITNESS_GOAL_SMALL = 193, - RESOURCE_ID_SCHEDULED_EVENT_SMALL = 194, - RESOURCE_ID_SUNNY_DAY_SMALL = 195, - RESOURCE_ID_GENERIC_SPORTS_SMALL = 196, - RESOURCE_ID_TIDE_IS_HIGH_SMALL = 197, - RESOURCE_ID_WATCH_DISCONNECTED_SMALL = 198, - RESOURCE_ID_ALARM_CLOCK_LARGE_STATIC = 199, - RESOURCE_ID_AMERICAN_FOOTBALL_LARGE = 200, - RESOURCE_ID_AMERICAN_FOOTBALL_SMALL = 201, - RESOURCE_ID_GOOGLE_HANGOUTS_NOTIFICATION_LARGE = 202, - RESOURCE_ID_WHATSAPP_NOTIFICATION_LARGE = 203, - RESOURCE_ID_AUDIO_CASSETTE_LARGE = 204, - RESOURCE_ID_AUDIO_CASSETTE_SMALL = 205, - RESOURCE_ID_BASEBALL_GAME_LARGE = 206, - RESOURCE_ID_BIRTHDAY_EVENT_LARGE = 207, - RESOURCE_ID_BLACKBERRY_MESSENGER_NOTIFICATION_LARGE = 208, - RESOURCE_ID_TIMELINE_CALENDAR_LARGE = 209, - RESOURCE_ID_CAR_RENTAL_LARGE = 210, - RESOURCE_ID_CLOUDY_DAY_LARGE = 211, - RESOURCE_ID_CLOUDY_DAY_SMALL = 212, - RESOURCE_ID_DAY_SEPARATOR_TINY = 213, - RESOURCE_ID_DAY_SEPARATOR_SMALL = 214, - RESOURCE_ID_DAY_SEPARATOR_LARGE = 215, - RESOURCE_ID_RESULT_DELETED_TINY = 216, - RESOURCE_ID_RESULT_DELETED_SMALL = 217, - RESOURCE_ID_DINNER_RESERVATION_LARGE = 218, - RESOURCE_ID_DISMISSED_PHONE_CALL_LARGE = 219, - RESOURCE_ID_DURING_PHONE_CALL_TINY = 220, - RESOURCE_ID_DURING_PHONE_CALL_SMALL = 221, - RESOURCE_ID_DURING_PHONE_CALL_LARGE = 222, - RESOURCE_ID_DURING_PHONE_CALL_CENTERED_LARGE = 223, - RESOURCE_ID_FACEBOOK_MESSENGER_NOTIFICATION_LARGE = 224, - RESOURCE_ID_FACEBOOK_NOTIFICATION_LARGE = 225, - RESOURCE_ID_GENERIC_EMAIL_LARGE = 226, - RESOURCE_ID_GENERIC_PIN_LARGE = 227, - RESOURCE_ID_GENERIC_REMINDER_LARGE = 228, - RESOURCE_ID_GENERIC_SPORTS_LARGE = 229, - RESOURCE_ID_GENERIC_WEATHER_LARGE = 230, - RESOURCE_ID_GLUCOSE_MONITOR_LARGE = 231, - RESOURCE_ID_GMAIL_NOTIFICATION_LARGE = 232, - RESOURCE_ID_GOOGLE_INBOX_NOTIFICATION_LARGE = 233, - RESOURCE_ID_HEAVY_RAIN_LARGE = 234, - RESOURCE_ID_HEAVY_SNOW_LARGE = 235, - RESOURCE_ID_HOTEL_RESERVATION_LARGE = 236, - RESOURCE_ID_INSTAGRAM_NOTIFICATION_LARGE = 237, - RESOURCE_ID_LIGHT_RAIN_LARGE = 238, - RESOURCE_ID_LIGHT_SNOW_LARGE = 239, - RESOURCE_ID_MAILBOX_NOTIFICATION_LARGE = 240, - RESOURCE_ID_MISSED_CALL_LARGE = 241, - RESOURCE_ID_MOVIE_EVENT_LARGE = 242, - RESOURCE_ID_MUSIC_EVENT_LARGE = 243, - RESOURCE_ID_NEWS_EVENT_LARGE = 244, - RESOURCE_ID_PARTLY_CLOUDY_LARGE = 245, - RESOURCE_ID_PAY_BILL_LARGE = 246, - RESOURCE_ID_RADIO_SHOW_LARGE = 247, - RESOURCE_ID_REACHED_FITNESS_GOAL_LARGE = 248, - RESOURCE_ID_SCHEDULED_EVENT_LARGE = 249, - RESOURCE_ID_SCHEDULED_FLIGHT_LARGE = 250, - RESOURCE_ID_SCHEDULED_FLIGHT_SMALL = 251, - RESOURCE_ID_RESULT_SENT_TINY = 252, - RESOURCE_ID_RESULT_SENT_SMALL = 253, - RESOURCE_ID_SOCCER_GAME_LARGE = 254, - RESOURCE_ID_STOCKS_EVENT_LARGE = 255, - RESOURCE_ID_STOCKS_EVENT_SMALL = 256, - RESOURCE_ID_SUNNY_DAY_LARGE = 257, - RESOURCE_ID_TELEGRAM_APP_LARGE = 258, - RESOURCE_ID_TELEGRAM_APP_SMALL = 259, - RESOURCE_ID_TIDE_IS_HIGH_LARGE = 260, - RESOURCE_ID_TWITTER_NOTIFICATION_LARGE = 261, - RESOURCE_ID_RESULT_FAILED_TINY = 262, - RESOURCE_ID_RESULT_FAILED_SMALL = 263, - RESOURCE_ID_RESULT_FAILED_LARGE = 264, - RESOURCE_ID_GENERIC_QUESTION_TINY = 265, - RESOURCE_ID_GENERIC_QUESTION_SMALL = 266, - RESOURCE_ID_GENERIC_QUESTION_LARGE = 267, - RESOURCE_ID_NOTIFICATION_OUTLOOK_TINY = 268, - RESOURCE_ID_NOTIFICATION_OUTLOOK_SMALL = 269, - RESOURCE_ID_NOTIFICATION_OUTLOOK_LARGE = 270, - RESOURCE_ID_RAINING_AND_SNOWING_TINY = 271, - RESOURCE_ID_RAINING_AND_SNOWING_SMALL = 272, - RESOURCE_ID_RAINING_AND_SNOWING_LARGE = 273, - RESOURCE_ID_NOTIFICATION_FACETIME_TINY = 274, - RESOURCE_ID_NOTIFICATION_FACETIME_SMALL = 275, - RESOURCE_ID_NOTIFICATION_FACETIME_LARGE = 276, - RESOURCE_ID_NOTIFICATION_LINE_TINY = 277, - RESOURCE_ID_NOTIFICATION_LINE_SMALL = 278, - RESOURCE_ID_NOTIFICATION_LINE_LARGE = 279, - RESOURCE_ID_NOTIFICATION_SKYPE_TINY = 280, - RESOURCE_ID_NOTIFICATION_SKYPE_SMALL = 281, - RESOURCE_ID_NOTIFICATION_SKYPE_LARGE = 282, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_TINY = 283, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_SMALL = 284, - RESOURCE_ID_NOTIFICATION_SNAPCHAT_LARGE = 285, - RESOURCE_ID_NOTIFICATION_VIBER_TINY = 286, - RESOURCE_ID_NOTIFICATION_VIBER_SMALL = 287, - RESOURCE_ID_NOTIFICATION_VIBER_LARGE = 288, - RESOURCE_ID_NOTIFICATION_WECHAT_TINY = 289, - RESOURCE_ID_NOTIFICATION_WECHAT_SMALL = 290, - RESOURCE_ID_NOTIFICATION_WECHAT_LARGE = 291, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_TINY = 292, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_SMALL = 293, - RESOURCE_ID_NOTIFICATION_YAHOO_MAIL_LARGE = 294, - RESOURCE_ID_TV_SHOW_TINY = 295, - RESOURCE_ID_TV_SHOW_SMALL = 296, - RESOURCE_ID_TV_SHOW_LARGE = 297, - RESOURCE_ID_END_OF_TIMELINE = 298, - RESOURCE_ID_BASKETBALL_TINY = 299, - RESOURCE_ID_BASKETBALL_SMALL = 300, - RESOURCE_ID_BASKETBALL_LARGE = 301, - RESOURCE_ID_RESULT_DISMISSED_TINY = 302, - RESOURCE_ID_RESULT_DISMISSED_SMALL = 303, - RESOURCE_ID_TIDE_IS_HIGH_TINY = 304, - RESOURCE_ID_OUTGOING_CALL_LARGE = 305, - RESOURCE_ID_NOTIFICATION_HIPCHAT_TINY = 306, - RESOURCE_ID_NOTIFICATION_HIPCHAT_SMALL = 307, - RESOURCE_ID_NOTIFICATION_HIPCHAT_LARGE = 308, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_TINY = 309, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_SMALL = 310, - RESOURCE_ID_NOTIFICATION_GOOGLE_MESSENGER_LARGE = 311, - RESOURCE_ID_INCOMING_PHONE_CALL_TINY = 312, - RESOURCE_ID_INCOMING_PHONE_CALL_SMALL = 313, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_TINY = 314, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_SMALL = 315, - RESOURCE_ID_NOTIFICATION_KAKAOTALK_LARGE = 316, - RESOURCE_ID_NOTIFICATION_KIK_TINY = 317, - RESOURCE_ID_NOTIFICATION_KIK_SMALL = 318, - RESOURCE_ID_NOTIFICATION_KIK_LARGE = 319, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_TINY = 320, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_SMALL = 321, - RESOURCE_ID_NOTIFICATION_LIGHTHOUSE_LARGE = 322, - RESOURCE_ID_LOCATION_TINY = 323, - RESOURCE_ID_LOCATION_SMALL = 324, - RESOURCE_ID_LOCATION_LARGE = 325, - RESOURCE_ID_SUNSET_TINY = 326, - RESOURCE_ID_SUNSET_SMALL = 327, - RESOURCE_ID_SUNSET_LARGE = 328, - RESOURCE_ID_SUNRISE_TINY = 329, - RESOURCE_ID_SUNRISE_SMALL = 330, - RESOURCE_ID_SUNRISE_LARGE = 331, - RESOURCE_ID_SETTINGS_TINY = 332, - RESOURCE_ID_SETTINGS_SMALL = 333, - RESOURCE_ID_SETTINGS_LARGE = 334, - RESOURCE_ID_PLUS_ICON_BLACK = 335, - RESOURCE_ID_PLUS_ICON_DOTTED = 336, - RESOURCE_ID_RESULT_SHREDDED_TINY = 337, - RESOURCE_ID_RESULT_SHREDDED_SMALL = 338, - RESOURCE_ID_QUIET_TIME_MOUSE = 339, - RESOURCE_ID_REMINDER_SNOOZE = 340, - RESOURCE_ID_QUIET_TIME_STATUS_BAR = 341, - RESOURCE_ID_QUICK_DISMISS = 342, - RESOURCE_ID_THUMBS_UP_SMALL = 343, - RESOURCE_ID_THUMBS_UP_LARGE = 344, - RESOURCE_ID_ARROW_UP_SMALL = 345, - RESOURCE_ID_ARROW_DOWN_SMALL = 346, - RESOURCE_ID_ACTIVITY_TINY = 347, - RESOURCE_ID_ACTIVITY_SMALL = 348, - RESOURCE_ID_ACTIVITY_LARGE = 349, - RESOURCE_ID_SLEEP_TINY = 350, - RESOURCE_ID_SLEEP_SMALL = 351, - RESOURCE_ID_SLEEP_LARGE = 352, - RESOURCE_ID_REWARD_AVERAGE_LARGE = 353, - RESOURCE_ID_REWARD_GOOD_LARGE = 354, - RESOURCE_ID_REWARD_BAD_LARGE = 355, - RESOURCE_ID_CALORIES_TINY = 356, - RESOURCE_ID_DISTANCE_TINY = 357, - RESOURCE_ID_DURATION_TINY = 358, - RESOURCE_ID_PACE_TINY = 359, - RESOURCE_ID_RUN_TINY = 360, - RESOURCE_ID_RUN_LARGE = 361, - RESOURCE_ID_MOON_TINY = 362, - RESOURCE_ID_NOTIFICATION_AMAZON_TINY = 363, - RESOURCE_ID_NOTIFICATION_AMAZON_SMALL = 364, - RESOURCE_ID_NOTIFICATION_AMAZON_LARGE = 365, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_TINY = 366, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_SMALL = 367, - RESOURCE_ID_NOTIFICATION_GOOGLE_MAPS_LARGE = 368, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_TINY = 369, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_SMALL = 370, - RESOURCE_ID_NOTIFICATION_GOOGLE_PHOTOS_LARGE = 371, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_TINY = 372, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_SMALL = 373, - RESOURCE_ID_NOTIFICATION_IOS_PHOTOS_LARGE = 374, - RESOURCE_ID_NOTIFICATION_LINKEDIN_TINY = 375, - RESOURCE_ID_NOTIFICATION_LINKEDIN_SMALL = 376, - RESOURCE_ID_NOTIFICATION_LINKEDIN_LARGE = 377, - RESOURCE_ID_NOTIFICATION_SLACK_TINY = 378, - RESOURCE_ID_NOTIFICATION_SLACK_SMALL = 379, - RESOURCE_ID_NOTIFICATION_SLACK_LARGE = 380, - RESOURCE_ID_SMART_ALARM_TINY = 381, - RESOURCE_ID_HEALTH_APP_ACTIVITY = 382, - RESOURCE_ID_HEALTH_APP_SLEEP = 383, - RESOURCE_ID_HEALTH_APP_CROWN = 384, - RESOURCE_ID_HEALTH_APP_HR = 385, - RESOURCE_ID_HEALTH_APP_PULSING_HEART = 386, - RESOURCE_ID_WORKOUT_APP_WALK = 387, - RESOURCE_ID_WORKOUT_APP_WALK_SMALL = 388, - RESOURCE_ID_WORKOUT_APP_WALK_TINY = 389, - RESOURCE_ID_WORKOUT_APP_RUN = 390, - RESOURCE_ID_WORKOUT_APP_RUN_SMALL = 391, - RESOURCE_ID_WORKOUT_APP_RUN_TINY = 392, - RESOURCE_ID_WORKOUT_APP_WORKOUT = 393, - RESOURCE_ID_WORKOUT_APP_WORKOUT_SMALL = 394, - RESOURCE_ID_WORKOUT_APP_DETECTED = 395, - RESOURCE_ID_WORKOUT_APP_HEART = 396, - RESOURCE_ID_WORKOUT_APP_MEASURING_HR = 397, - RESOURCE_ID_WORKOUT_APP_HR_PULSE_TINY = 398, - RESOURCE_ID_WORKOUT_APP_END = 399, - RESOURCE_ID_WORKOUT_APP_ONE = 400, - RESOURCE_ID_WORKOUT_APP_TWO = 401, - RESOURCE_ID_WORKOUT_APP_THREE = 402, - RESOURCE_ID_HEART_TINY = 403, - RESOURCE_ID_HEART_SMALL = 404, - RESOURCE_ID_HEART_LARGE = 405, - RESOURCE_ID_BACKLIGHT = 406, - RESOURCE_ID_AIRPLANE = 407, - RESOURCE_ID_MODAL_CONTRACT_TO_MODAL_SEQUENCE = 408, - RESOURCE_ID_MODAL_CONTRACT_FROM_MODAL_SEQUENCE = 409, - RESOURCE_ID_MODAL_EXPAND_TO_APP_SEQUENCE = 410, - RESOURCE_ID_NO_EVENTS_LARGE = 411, - RESOURCE_ID_ALARM_CLOCK_LARGE = 412, - RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE = 413, - RESOURCE_ID_RESULT_DELETED_LARGE = 414, - RESOURCE_ID_RESULT_SHREDDED_LARGE = 415, - RESOURCE_ID_RESULT_DISMISSED_LARGE = 416, - RESOURCE_ID_GENERIC_CONFIRMATION_LARGE = 417, - RESOURCE_ID_INCOMING_PHONE_CALL_LARGE = 418, - RESOURCE_ID_RESULT_MUTE_LARGE = 419, - RESOURCE_ID_RESULT_SENT_LARGE = 420, - RESOURCE_ID_RESULT_UNMUTE_LARGE = 421, - RESOURCE_ID_TIMER_APP_FACE_ICON = 422, - RESOURCE_ID_STOPWATCH_APP_FACE_ICON = 423, - RESOURCE_ID_ESPN_APP_FACE_ICON = 424, - RESOURCE_ID_HEALTH_APP_FACE_ICON = 425, - RESOURCE_ID_SEND_TEXT_APP_FACE_ICON = 426, - RESOURCE_ID_QUIET_TIME_ACTIVE = 427, - RESOURCE_ID_BATTERY_CHARGING_ICON = 428, - RESOURCE_ID_HEALTH_ICON_MOON = 429, - RESOURCE_ID_HEALTH_ICON_ROTATED_MOON = 430, - RESOURCE_ID_SYSTEM_FCC_MARK = 431, - RESOURCE_ID_SYSTEM_KCC_MARK = 432, - RESOURCE_ID_SYSTEM_CE_MARK = 433, - RESOURCE_ID_SYSTEM_WEEE_MARK = 434, - RESOURCE_ID_SYSTEM_R_MARK = 435, - RESOURCE_ID_SYSTEM_T_MARK = 436, - RESOURCE_ID_SYSTEM_AUS_RCM_MARK = 437, - RESOURCE_ID_SYSTEM_NOM_NYCE_MARK = 438, - RESOURCE_ID_WEATHER_CHANNEL_LOGO = 439, - RESOURCE_ID_STRIDE_SHOE_GREEN = 440, - RESOURCE_ID_STRIDE_SHOE_GREEN_SMALL = 441, - RESOURCE_ID_STRIDE_SHOE_BLUE = 442, - RESOURCE_ID_STRIDE_SHOE_BLUE_SMALL = 443, - RESOURCE_ID_VIBE_SCORE_FLUTTER_PULSE = 444, - RESOURCE_ID_VIBE_SCORE_REVEILLE = 445, - RESOURCE_ID_VIBE_SCORE_HAPTIC_FEEDBACK = 446, - RESOURCE_ID_VIBE_SCORE_NUDGE_NUDGE = 447, - RESOURCE_ID_VIBE_SCORE_PULSE = 448, - RESOURCE_ID_VIBE_SCORE_JACKHAMMER = 449, - RESOURCE_ID_VIBE_SCORE_MARIO = 450, - RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_LOW = 451, - RESOURCE_ID_VIBE_SCORE_STANDARD_SHORT_HIGH = 452, - RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_LOW = 453, - RESOURCE_ID_VIBE_SCORE_STANDARD_LONG_HIGH = 454, - RESOURCE_ID_VIBE_SCORE_ALARM_LPM = 455, - RESOURCE_ID_EMOJI_BIG_OPEN_SMILE_LARGE = 456, - RESOURCE_ID_EMOJI_BIG_SMILE_LARGE = 457, - RESOURCE_ID_EMOJI_HEART_LARGE = 458, - RESOURCE_ID_EMOJI_KISSING_WITH_HEART_LARGE = 459, - RESOURCE_ID_EMOJI_LAUGHING_WITH_TEARS_LARGE = 460, - RESOURCE_ID_EMOJI_SAD_LARGE = 461, - RESOURCE_ID_EMOJI_SMILING_BLUSH_LARGE = 462, - RESOURCE_ID_EMOJI_SMILING_HEARTS_LARGE = 463, - RESOURCE_ID_EMOJI_SMILING_WITH_TEETH_LARGE = 464, - RESOURCE_ID_EMOJI_THUMBS_UP_LARGE = 465, - RESOURCE_ID_EMOJI_WINK_LARGE = 466, - RESOURCE_ID_EMOJI_WINK_TONGUE_LARGE = 467, - RESOURCE_ID_SMART_ALARM_ICON_BLACK = 468, - RESOURCE_ID_AGENCY_FB_36_NUMBERS_AM_PM = 469, - RESOURCE_ID_AGENCY_FB_60_NUMBERS_AM_PM = 470, - RESOURCE_ID_AGENCY_FB_60_THIN_NUMBERS_AM_PM = 471, - RESOURCE_ID_STORED_APP_GOLF = 472, - RESOURCE_ID_BT_PATCH = 473, - RESOURCE_ID_TIMEZONE_DATABASE = 474, - RESOURCE_ID_ACTION_BAR_ICON_CHECK = 475, - RESOURCE_ID_GENERIC_WARNING_LARGE = 476, - RESOURCE_ID_FONT_FALLBACK_INTERNAL = 477, - RESOURCE_ID_ACTION_BAR_ICON_UP = 478, - RESOURCE_ID_ACTION_BAR_ICON_DOWN = 479, - RESOURCE_ID_GENERIC_WARNING_TINY = 480, - RESOURCE_ID_CHECK_INTERNET_CONNECTION_LARGE = 481, - RESOURCE_ID_WATCH_DISCONNECTED_LARGE = 482, - RESOURCE_ID_ARROW_DOWN = 483, - RESOURCE_ID_VOICE_MICROPHONE_LARGE = 484, - RESOURCE_ID_JS_TICTOC = 485, - RESOURCE_ID_PUG = 486, - RESOURCE_ID_STRINGS = 487, - RESOURCE_ID_GOTHIC_14_EXTENDED = 488, - RESOURCE_ID_GOTHIC_14_BOLD_EXTENDED = 489, - RESOURCE_ID_GOTHIC_18_EXTENDED = 490, - RESOURCE_ID_GOTHIC_18_BOLD_EXTENDED = 491, - RESOURCE_ID_GOTHIC_24_EXTENDED = 492, - RESOURCE_ID_GOTHIC_24_BOLD_EXTENDED = 493, - RESOURCE_ID_GOTHIC_28_EXTENDED = 494, - RESOURCE_ID_GOTHIC_28_BOLD_EXTENDED = 495, - RESOURCE_ID_BITHAM_18_LIGHT_SUBSET_EXTENDED = 496, - RESOURCE_ID_BITHAM_30_BLACK_EXTENDED = 497, - RESOURCE_ID_BITHAM_34_LIGHT_SUBSET_EXTENDED = 498, - RESOURCE_ID_BITHAM_34_MEDIUM_NUMBERS_EXTENDED = 499, - RESOURCE_ID_BITHAM_42_BOLD_EXTENDED = 500, - RESOURCE_ID_BITHAM_42_LIGHT_EXTENDED = 501, - RESOURCE_ID_BITHAM_42_MEDIUM_NUMBERS_EXTENDED = 502, - RESOURCE_ID_ROBOTO_CONDENSED_21_EXTENDED = 503, - RESOURCE_ID_ROBOTO_BOLD_SUBSET_49_EXTENDED = 504, - RESOURCE_ID_DROID_SERIF_28_BOLD_EXTENDED = 505, - RESOURCE_ID_GOTHIC_09_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_14_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_18_COMPRESSED_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_18_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_24_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_28_EMOJI_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_36_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_GOTHIC_36_BOLD_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_20_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_26_BOLD_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_32_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_36_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_38_BOLD_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_42_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_LECO_28_LIGHT_NUMBERS_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_AGENCY_FB_36_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_AGENCY_FB_60_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_AGENCY_FB_60_THIN_NUMBERS_AM_PM_EXTENDED = INVALID_RESOURCE, - RESOURCE_ID_FONT_FALLBACK_INTERNAL_EXTENDED = INVALID_RESOURCE, -} ResourceId; \ No newline at end of file diff --git a/tests/overrides/default/resources/snowy/resource/resource_version.auto.h b/tests/overrides/default/resources/snowy/resource/resource_version.auto.h deleted file mode 100644 index ab7e0afc7a..0000000000 --- a/tests/overrides/default/resources/snowy/resource/resource_version.auto.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -// -// AUTOGENERATED -// DO NOT MODIFY -// - -static const ResourceVersion SYSTEM_RESOURCE_VERSION = { - .crc = 349639890, - .timestamp = 0 -}; diff --git a/tests/overrides/default/resources/snowy/resource/timeline_resource_ids.auto.h b/tests/overrides/default/resources/snowy/resource/timeline_resource_ids.auto.h deleted file mode 100644 index 912c402543..0000000000 --- a/tests/overrides/default/resources/snowy/resource/timeline_resource_ids.auto.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#pragma once - -// -// AUTOGENERATED -// DO NOT MODIFY -// - -typedef enum { - TIMELINE_RESOURCE_INVALID = 0, - TIMELINE_RESOURCE_NOTIFICATION_GENERIC = 0x80000001, - TIMELINE_RESOURCE_TIMELINE_MISSED_CALL = 0x80000002, - TIMELINE_RESOURCE_NOTIFICATION_REMINDER = 0x80000003, - TIMELINE_RESOURCE_NOTIFICATION_FLAG = 0x80000004, - TIMELINE_RESOURCE_NOTIFICATION_WHATSAPP = 0x80000005, - TIMELINE_RESOURCE_NOTIFICATION_TWITTER = 0x80000006, - TIMELINE_RESOURCE_NOTIFICATION_TELEGRAM = 0x80000007, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_HANGOUTS = 0x80000008, - TIMELINE_RESOURCE_NOTIFICATION_GMAIL = 0x80000009, - TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK_MESSENGER = 0x8000000a, - TIMELINE_RESOURCE_NOTIFICATION_FACEBOOK = 0x8000000b, - TIMELINE_RESOURCE_AUDIO_CASSETTE = 0x8000000c, - TIMELINE_RESOURCE_ALARM_CLOCK = 0x8000000d, - TIMELINE_RESOURCE_TIMELINE_WEATHER = 0x8000000e, - TIMELINE_RESOURCE_TIMELINE_SUN = 0x80000010, - TIMELINE_RESOURCE_TIMELINE_SPORTS = 0x80000011, - TIMELINE_RESOURCE_GENERIC_EMAIL = 0x80000013, - TIMELINE_RESOURCE_AMERICAN_FOOTBALL = 0x80000014, - TIMELINE_RESOURCE_TIMELINE_CALENDAR = 0x80000015, - TIMELINE_RESOURCE_TIMELINE_BASEBALL = 0x80000016, - TIMELINE_RESOURCE_BIRTHDAY_EVENT = 0x80000017, - TIMELINE_RESOURCE_CAR_RENTAL = 0x80000018, - TIMELINE_RESOURCE_CLOUDY_DAY = 0x80000019, - TIMELINE_RESOURCE_CRICKET_GAME = 0x8000001a, - TIMELINE_RESOURCE_DINNER_RESERVATION = 0x8000001b, - TIMELINE_RESOURCE_GENERIC_WARNING = 0x8000001c, - TIMELINE_RESOURCE_GLUCOSE_MONITOR = 0x8000001d, - TIMELINE_RESOURCE_HOCKEY_GAME = 0x8000001e, - TIMELINE_RESOURCE_HOTEL_RESERVATION = 0x8000001f, - TIMELINE_RESOURCE_LIGHT_RAIN = 0x80000020, - TIMELINE_RESOURCE_LIGHT_SNOW = 0x80000021, - TIMELINE_RESOURCE_MOVIE_EVENT = 0x80000022, - TIMELINE_RESOURCE_MUSIC_EVENT = 0x80000023, - TIMELINE_RESOURCE_NEWS_EVENT = 0x80000024, - TIMELINE_RESOURCE_PARTLY_CLOUDY = 0x80000025, - TIMELINE_RESOURCE_PAY_BILL = 0x80000026, - TIMELINE_RESOURCE_RADIO_SHOW = 0x80000027, - TIMELINE_RESOURCE_SCHEDULED_EVENT = 0x80000028, - TIMELINE_RESOURCE_SOCCER_GAME = 0x80000029, - TIMELINE_RESOURCE_STOCKS_EVENT = 0x8000002a, - TIMELINE_RESOURCE_RESULT_DELETED = 0x8000002b, - TIMELINE_RESOURCE_CHECK_INTERNET_CONNECTION = 0x8000002c, - TIMELINE_RESOURCE_GENERIC_SMS = 0x8000002d, - TIMELINE_RESOURCE_RESULT_MUTE = 0x8000002e, - TIMELINE_RESOURCE_RESULT_SENT = 0x8000002f, - TIMELINE_RESOURCE_WATCH_DISCONNECTED = 0x80000030, - TIMELINE_RESOURCE_DURING_PHONE_CALL = 0x80000031, - TIMELINE_RESOURCE_TIDE_IS_HIGH = 0x80000032, - TIMELINE_RESOURCE_RESULT_DISMISSED = 0x80000033, - TIMELINE_RESOURCE_HEAVY_RAIN = 0x80000034, - TIMELINE_RESOURCE_HEAVY_SNOW = 0x80000035, - TIMELINE_RESOURCE_SCHEDULED_FLIGHT = 0x80000036, - TIMELINE_RESOURCE_GENERIC_CONFIRMATION = 0x80000037, - TIMELINE_RESOURCE_DAY_SEPARATOR = 0x80000038, - TIMELINE_RESOURCE_NO_EVENTS = 0x80000039, - TIMELINE_RESOURCE_NOTIFICATION_BLACKBERRY_MESSENGER = 0x8000003a, - TIMELINE_RESOURCE_NOTIFICATION_INSTAGRAM = 0x8000003b, - TIMELINE_RESOURCE_NOTIFICATION_MAILBOX = 0x8000003c, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_INBOX = 0x8000003d, - TIMELINE_RESOURCE_RESULT_FAILED = 0x8000003e, - TIMELINE_RESOURCE_GENERIC_QUESTION = 0x8000003f, - TIMELINE_RESOURCE_NOTIFICATION_OUTLOOK = 0x80000040, - TIMELINE_RESOURCE_RAINING_AND_SNOWING = 0x80000041, - TIMELINE_RESOURCE_REACHED_FITNESS_GOAL = 0x80000042, - TIMELINE_RESOURCE_NOTIFICATION_LINE = 0x80000043, - TIMELINE_RESOURCE_NOTIFICATION_SKYPE = 0x80000044, - TIMELINE_RESOURCE_NOTIFICATION_SNAPCHAT = 0x80000045, - TIMELINE_RESOURCE_NOTIFICATION_VIBER = 0x80000046, - TIMELINE_RESOURCE_NOTIFICATION_WECHAT = 0x80000047, - TIMELINE_RESOURCE_NOTIFICATION_YAHOO_MAIL = 0x80000048, - TIMELINE_RESOURCE_TV_SHOW = 0x80000049, - TIMELINE_RESOURCE_BASKETBALL = 0x8000004a, - TIMELINE_RESOURCE_DISMISSED_PHONE_CALL = 0x8000004b, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MESSENGER = 0x8000004c, - TIMELINE_RESOURCE_NOTIFICATION_HIPCHAT = 0x8000004d, - TIMELINE_RESOURCE_INCOMING_PHONE_CALL = 0x8000004e, - TIMELINE_RESOURCE_NOTIFICATION_KAKAOTALK = 0x8000004f, - TIMELINE_RESOURCE_NOTIFICATION_KIK = 0x80000050, - TIMELINE_RESOURCE_NOTIFICATION_LIGHTHOUSE = 0x80000051, - TIMELINE_RESOURCE_LOCATION = 0x80000052, - TIMELINE_RESOURCE_SETTINGS = 0x80000053, - TIMELINE_RESOURCE_SUNRISE = 0x80000054, - TIMELINE_RESOURCE_SUNSET = 0x80000055, - TIMELINE_RESOURCE_RESULT_UNMUTE = 0x80000056, - TIMELINE_RESOURCE_RESULT_UNMUTE_ALT = 0x8000005e, - TIMELINE_RESOURCE_DURING_PHONE_CALL_CENTERED = 0x8000005f, - TIMELINE_RESOURCE_TIMELINE_EMPTY_CALENDAR = 0x80000060, - TIMELINE_RESOURCE_THUMBS_UP = 0x80000061, - TIMELINE_RESOURCE_ARROW_UP = 0x80000062, - TIMELINE_RESOURCE_ARROW_DOWN = 0x80000063, - TIMELINE_RESOURCE_ACTIVITY = 0x80000064, - TIMELINE_RESOURCE_SLEEP = 0x80000065, - TIMELINE_RESOURCE_REWARD_BAD = 0x80000066, - TIMELINE_RESOURCE_REWARD_GOOD = 0x80000067, - TIMELINE_RESOURCE_REWARD_AVERAGE = 0x80000068, - TIMELINE_RESOURCE_CALORIES = 0x80000069, - TIMELINE_RESOURCE_DISTANCE = 0x8000006a, - TIMELINE_RESOURCE_DURATION = 0x8000006b, - TIMELINE_RESOURCE_PACE = 0x8000006c, - TIMELINE_RESOURCE_RUN = 0x8000006d, - TIMELINE_RESOURCE_NOTIFICATION_FACETIME = 0x8000006e, - TIMELINE_RESOURCE_NOTIFICATION_AMAZON = 0x8000006f, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_MAPS = 0x80000070, - TIMELINE_RESOURCE_NOTIFICATION_GOOGLE_PHOTOS = 0x80000071, - TIMELINE_RESOURCE_NOTIFICATION_IOS_PHOTOS = 0x80000072, - TIMELINE_RESOURCE_NOTIFICATION_LINKEDIN = 0x80000073, - TIMELINE_RESOURCE_NOTIFICATION_SLACK = 0x80000074, - TIMELINE_RESOURCE_SMART_ALARM = 0x80000075, - TIMELINE_RESOURCE_HEART = 0x80000076, - TIMELINE_RESOURCE_BLE_HRM_SHARING = 0x80000077, -} TimelineResourceId; - -#define NUM_TIMELINE_RESOURCES 120 diff --git a/tests/overrides/default/services/normal/notifications/ancs_known_apps.h b/tests/overrides/default/services/normal/notifications/ancs_known_apps.h index 2db94dcc80..2a497df718 100644 --- a/tests/overrides/default/services/normal/notifications/ancs_known_apps.h +++ b/tests/overrides/default/services/normal/notifications/ancs_known_apps.h @@ -1,29 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // @nolint // please don't change these values manually, they are derived from the spreadsheet // "Notification Colors" -#if PLATFORM_TINTIN -// Tintin does not have the color arg in its App Metadata. Remove it. -#define APP(id, icon, color) { id, icon } -#else #define APP(id, icon, color) { id, icon, color } -#endif APP(IOS_SMS_APP_ID, TIMELINE_RESOURCE_GENERIC_SMS, GColorIslamicGreenARGB8), #undef APP diff --git a/tests/overrides/default/shell/system_app_ids.auto.h b/tests/overrides/default/shell/system_app_ids.auto.h index 30bcb4a4c8..084b8c7be9 100644 --- a/tests/overrides/default/shell/system_app_ids.auto.h +++ b/tests/overrides/default/shell/system_app_ids.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_management/app_install_types.h" diff --git a/tests/overrides/default/shell/system_app_registry_list.auto.h b/tests/overrides/default/shell/system_app_registry_list.auto.h index 5a37b22aef..4767343af7 100644 --- a/tests/overrides/default/shell/system_app_registry_list.auto.h +++ b/tests/overrides/default/shell/system_app_registry_list.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system_app_ids.auto.h" #include "resource/resource_ids.auto.h" diff --git a/tests/overrides/default/stdio.h b/tests/overrides/default/stdio.h index d7143c8320..9b5729fe7d 100644 --- a/tests/overrides/default/stdio.h +++ b/tests/overrides/default/stdio.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/dummy_board/board/board.h b/tests/overrides/dummy_board/board/board.h index 5766885732..6a546e6472 100644 --- a/tests/overrides/dummy_board/board/board.h +++ b/tests/overrides/dummy_board/board/board.h @@ -1,34 +1,16 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -typedef enum { - CC2564A = 0, - CC2564B, -} BluetoothController; - typedef struct { const uint8_t backlight_on_percent; // percent of max possible brightness + const uint32_t ambient_light_dark_threshold; } BoardConfig; typedef struct { - const BluetoothController controller; } BoardConfigBTCommon; typedef struct { @@ -44,7 +26,6 @@ static const BoardConfig BOARD_CONFIG = { }; static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564A, }; static const BoardConfigPower BOARD_CONFIG_POWER = { @@ -52,6 +33,18 @@ static const BoardConfigPower BOARD_CONFIG_POWER = { .battery_capacity_hours = 144, }; +typedef struct { + uint8_t default_motion_sensitivity; +} AccelConfig; + +typedef struct { + const AccelConfig accel_config; +} BoardConfigAccel; + +static const BoardConfigAccel BOARD_CONFIG_ACCEL = { + .accel_config = { .default_motion_sensitivity = 0 }, +}; + typedef const struct MicDevice MicDevice; static MicDevice * const MIC = (void *)0; diff --git a/tests/overrides/fake_app_registry/shell/system_app_ids.auto.h b/tests/overrides/fake_app_registry/shell/system_app_ids.auto.h index 0c3170cd60..4eace5060a 100644 --- a/tests/overrides/fake_app_registry/shell/system_app_ids.auto.h +++ b/tests/overrides/fake_app_registry/shell/system_app_ids.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "process_management/app_install_types.h" diff --git a/tests/overrides/fake_app_registry/shell/system_app_registry_list.auto.h b/tests/overrides/fake_app_registry/shell/system_app_registry_list.auto.h index c5864711fe..214107ccff 100644 --- a/tests/overrides/fake_app_registry/shell/system_app_registry_list.auto.h +++ b/tests/overrides/fake_app_registry/shell/system_app_registry_list.auto.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "system_app_ids.auto.h" #include "resource/resource_ids.auto.h" diff --git a/tests/overrides/font_compression/board/board.h b/tests/overrides/font_compression/board/board.h index 06d5fbe882..57f78c918d 100644 --- a/tests/overrides/font_compression/board/board.h +++ b/tests/overrides/font_compression/board/board.h @@ -1,36 +1,15 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#define PLATFORM_TINTIN 1 - -typedef enum { - CC2564A = 0, - CC2564B, -} BluetoothController; - typedef struct { const uint8_t backlight_on_percent; // percent of max possible brightness } BoardConfig; typedef struct { - const BluetoothController controller; } BoardConfigBTCommon; typedef struct { @@ -42,7 +21,6 @@ static const BoardConfig BOARD_CONFIG = { }; static const BoardConfigBTCommon BOARD_CONFIG_BT_COMMON = { - .controller = CC2564A, }; static const BoardConfigPower BOARD_CONFIG_POWER = { diff --git a/tests/overrides/fpc_pinstrap_board/board/board.h b/tests/overrides/fpc_pinstrap_board/board/board.h index defa90205c..ff7ec524b3 100644 --- a/tests/overrides/fpc_pinstrap_board/board/board.h +++ b/tests/overrides/fpc_pinstrap_board/board/board.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -50,13 +37,3 @@ typedef struct { GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA. const uint32_t gpio_pin; ///< One of GPIO_Pin_X. } InputConfig; - -typedef struct { - const InputConfig fpc_pinstrap_1; - const InputConfig fpc_pinstrap_2; -} BoardConfig; - -static const BoardConfig BOARD_CONFIG = { - .fpc_pinstrap_1 = { GPIOA, GPIO_Pin_1 }, - .fpc_pinstrap_2 = { GPIOA, GPIO_Pin_2 }, -}; diff --git a/tests/overrides/pp_endpoints/pbl/services/comm_session/test_endpoint_ids.h b/tests/overrides/pp_endpoints/pbl/services/comm_session/test_endpoint_ids.h new file mode 100644 index 0000000000..4ed8e99ecf --- /dev/null +++ b/tests/overrides/pp_endpoints/pbl/services/comm_session/test_endpoint_ids.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#define NON_EXISTENT_ENDPOINT_ID (0) +#define OTHER_NON_EXISTENT_ENDPOINT_ID (0xffff) +#define PRIVATE_TEST_ENDPOINT_ID (1) +#define PUBLIC_TEST_ENDPOINT_ID (2) +#define ANY_TEST_ENDPOINT_ID (3) diff --git a/tests/overrides/pp_endpoints/services/comm_session/protocol_endpoints_table.auto.h b/tests/overrides/pp_endpoints/services/comm_session/protocol_endpoints_table.auto.h new file mode 100644 index 0000000000..78805bbfd8 --- /dev/null +++ b/tests/overrides/pp_endpoints/services/comm_session/protocol_endpoints_table.auto.h @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +// GENERATED -- DO NOT EDIT + +#include "pbl/services/comm_session/test_endpoint_ids.h" + +extern void private_test_protocol_msg_callback(CommSession *session, + const uint8_t* data, size_t length); +extern void public_test_protocol_msg_callback(CommSession *session, + const uint8_t* data, size_t length); +extern void any_test_protocol_msg_callback(CommSession *session, + const uint8_t* data, size_t length); + +extern ReceiverImplementation g_system_test_receiver_imp; + +static const PebbleProtocolEndpoint s_protocol_endpoints[] = { + { PRIVATE_TEST_ENDPOINT_ID, private_test_protocol_msg_callback, + PebbleProtocolAccessPrivate, &g_system_test_receiver_imp }, + { PUBLIC_TEST_ENDPOINT_ID, public_test_protocol_msg_callback, + PebbleProtocolAccessPublic, &g_system_test_receiver_imp }, + { ANY_TEST_ENDPOINT_ID, any_test_protocol_msg_callback, + PebbleProtocolAccessAny, &g_system_test_receiver_imp }, +}; + diff --git a/tests/overrides/pp_endpoints/services/common/comm_session/protocol_endpoints_table.auto.h b/tests/overrides/pp_endpoints/services/common/comm_session/protocol_endpoints_table.auto.h deleted file mode 100644 index 1f22dd1d6a..0000000000 --- a/tests/overrides/pp_endpoints/services/common/comm_session/protocol_endpoints_table.auto.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// GENERATED -- DO NOT EDIT - -#include "test_endpoint_ids.h" - -extern void private_test_protocol_msg_callback(CommSession *session, - const uint8_t* data, size_t length); -extern void public_test_protocol_msg_callback(CommSession *session, - const uint8_t* data, size_t length); -extern void any_test_protocol_msg_callback(CommSession *session, - const uint8_t* data, size_t length); - -extern ReceiverImplementation g_system_test_receiver_imp; - -static const PebbleProtocolEndpoint s_protocol_endpoints[] = { - { PRIVATE_TEST_ENDPOINT_ID, private_test_protocol_msg_callback, - PebbleProtocolAccessPrivate, &g_system_test_receiver_imp }, - { PUBLIC_TEST_ENDPOINT_ID, public_test_protocol_msg_callback, - PebbleProtocolAccessPublic, &g_system_test_receiver_imp }, - { ANY_TEST_ENDPOINT_ID, any_test_protocol_msg_callback, - PebbleProtocolAccessAny, &g_system_test_receiver_imp }, -}; - diff --git a/tests/overrides/pp_endpoints/services/common/comm_session/test_endpoint_ids.h b/tests/overrides/pp_endpoints/services/common/comm_session/test_endpoint_ids.h deleted file mode 100644 index 0047112201..0000000000 --- a/tests/overrides/pp_endpoints/services/common/comm_session/test_endpoint_ids.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define NON_EXISTENT_ENDPOINT_ID (0) -#define OTHER_NON_EXISTENT_ENDPOINT_ID (0xffff) -#define PRIVATE_TEST_ENDPOINT_ID (1) -#define PUBLIC_TEST_ENDPOINT_ID (2) -#define ANY_TEST_ENDPOINT_ID (3) diff --git a/tests/overrides/rtc_calibration/mcu.h b/tests/overrides/rtc_calibration/mcu.h index edf8b8bbaa..d931267297 100644 --- a/tests/overrides/rtc_calibration/mcu.h +++ b/tests/overrides/rtc_calibration/mcu.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h b/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h index 472e658281..ae5907f8c4 100644 --- a/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h +++ b/tests/overrides/shared_prf_storage_v3/flash_region/flash_region.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/snowy_mfg_board/board/board.h b/tests/overrides/snowy_mfg_board/board/board.h deleted file mode 100644 index 49e71436c3..0000000000 --- a/tests/overrides/snowy_mfg_board/board/board.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - diff --git a/tests/overrides/snowy_mfg_board/flash_region/flash_region.h b/tests/overrides/snowy_mfg_board/flash_region/flash_region.h deleted file mode 100644 index 878ddbe380..0000000000 --- a/tests/overrides/snowy_mfg_board/flash_region/flash_region.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "flash_region/flash_region_s29vs.h" diff --git a/tests/overrides/snowy_mfg_board/mfg/spalding/spalding_boot.fpga.auto.h b/tests/overrides/snowy_mfg_board/mfg/spalding/spalding_boot.fpga.auto.h deleted file mode 100644 index a06ea8daab..0000000000 --- a/tests/overrides/snowy_mfg_board/mfg/spalding/spalding_boot.fpga.auto.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include - -// Truncated version of the real thing. -static const uint8_t s_boot_fpga[] = { -0xff,0x00,0x4c,0x61,0x74,0x74,0x69,0x63,0x65,0x00,0x69,0x43,0x45,0x63,0x75,0x62, -0x65,0x32,0x20,0x32,0x30,0x31,0x35,0x2e,0x30,0x34,0x2e,0x32,0x37,0x34,0x30,0x39, -0x00,0x50,0x61,0x72,0x74,0x3a,0x20,0x69,0x43,0x45,0x34,0x30,0x55,0x4c,0x31,0x4b, -0x2d,0x43,0x4d,0x33,0x36,0x41,0x00,0x44,0x61,0x74,0x65,0x3a,0x20,0x4e,0x6f,0x76, -0x20,0x35,0x20,0x32,0x30,0x31,0x35,0x20,0x31,0x37,0x3a,0x33,0x30,0x3a,0x00,0xff, -0x33,0x36,0x00,0x7e,0xaa,0x99,0x7e,0x51,0x00,0x01,0x05,0x92,0x00,0x20,0x62,0x01, -0x6f,0x82,0x00,0x00,0x72,0x00,0x70,0x11,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x02, -0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, -0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; diff --git a/tests/overrides/test_infra_test/_test/custom_only.h b/tests/overrides/test_infra_test/_test/custom_only.h index 30cfbf9720..db5b11e2ab 100644 --- a/tests/overrides/test_infra_test/_test/custom_only.h +++ b/tests/overrides/test_infra_test/_test/custom_only.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #define CUSTOM_ONLY_DEFINE (1) diff --git a/tests/overrides/test_infra_test/_test/default_and_custom.h b/tests/overrides/test_infra_test/_test/default_and_custom.h index 08f3f638bb..f011274c5a 100644 --- a/tests/overrides/test_infra_test/_test/default_and_custom.h +++ b/tests/overrides/test_infra_test/_test/default_and_custom.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #define OVERRIDDEN_DEFINE (42) diff --git a/tests/overrides/test_pbl_std/process_state/app_state/app_state.c b/tests/overrides/test_pbl_std/process_state/app_state/app_state.c index 00b1541456..fc3279519e 100644 --- a/tests/overrides/test_pbl_std/process_state/app_state/app_state.c +++ b/tests/overrides/test_pbl_std/process_state/app_state/app_state.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "app_state.h" diff --git a/tests/overrides/test_pbl_std/process_state/app_state/app_state.h b/tests/overrides/test_pbl_std/process_state/app_state/app_state.h index c217bd5f22..0d7ba516b9 100644 --- a/tests/overrides/test_pbl_std/process_state/app_state/app_state.h +++ b/tests/overrides/test_pbl_std/process_state/app_state/app_state.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/overrides/test_timezone/board/board.h b/tests/overrides/test_timezone/board/board.h index 620adbe2c9..36ce718c23 100644 --- a/tests/overrides/test_timezone/board/board.h +++ b/tests/overrides/test_timezone/board/board.h @@ -1,25 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include -#define CAPABILITY_USE_PARALLEL_FLASH 1 - typedef struct { const uint8_t backlight_on_percent; // percent of max possible brightness } BoardConfig; diff --git a/tests/overrides/test_timezone/flash_region/flash_region.h b/tests/overrides/test_timezone/flash_region/flash_region.h index 878ddbe380..6629c1d5d8 100644 --- a/tests/overrides/test_timezone/flash_region/flash_region.h +++ b/tests/overrides/test_timezone/flash_region/flash_region.h @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "flash_region/flash_region_s29vs.h" +#include "flash_region/flash_region_gd25q256e.h" diff --git a/tests/pebble_asserts.h b/tests/pebble_asserts.h index 43b1f763f7..ca0b5fb3e0 100644 --- a/tests/pebble_asserts.h +++ b/tests/pebble_asserts.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_HCIAPI.h b/tests/stubs/stubs_HCIAPI.h index 0b779745ac..e25ec02913 100644 --- a/tests/stubs/stubs_HCIAPI.h +++ b/tests/stubs/stubs_HCIAPI.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_L2CAPAPI.h b/tests/stubs/stubs_L2CAPAPI.h index b2dbbd79ab..30765b1ba1 100644 --- a/tests/stubs/stubs_L2CAPAPI.h +++ b/tests/stubs/stubs_L2CAPAPI.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_accel_manager.h b/tests/stubs/stubs_accel_manager.h index c947078ae4..0150bf3d69 100644 --- a/tests/stubs/stubs_accel_manager.h +++ b/tests/stubs/stubs_accel_manager.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/common/accel_manager.h" +#include "pbl/services/accel_manager.h" AccelManagerState* sys_accel_manager_data_subscribe( AccelSamplingRate rate, AccelDataReadyCallback data_cb, void* context, diff --git a/tests/stubs/stubs_accel_service.h b/tests/stubs/stubs_accel_service.h index 55415517a9..e2de7413d7 100644 --- a/tests/stubs/stubs_accel_service.h +++ b/tests/stubs/stubs_accel_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_action_chaining_window.h b/tests/stubs/stubs_action_chaining_window.h index 427e086d0d..7a3d9604ba 100644 --- a/tests/stubs/stubs_action_chaining_window.h +++ b/tests/stubs/stubs_action_chaining_window.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/normal/notifications/action_chaining_window.h" +#include "pbl/services/notifications/action_chaining_window.h" void action_chaining_window_push(WindowStack *window_stack, const char *title, TimelineItemActionGroup *action_group, diff --git a/tests/stubs/stubs_action_menu.h b/tests/stubs/stubs_action_menu.h index cc7ce89e59..3cad2de410 100644 --- a/tests/stubs/stubs_action_menu.h +++ b/tests/stubs/stubs_action_menu.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_activity.h b/tests/stubs/stubs_activity.h index 9bf0388fa8..a784778a52 100644 --- a/tests/stubs/stubs_activity.h +++ b/tests/stubs/stubs_activity.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/activity/activity_private.h" +#include "pbl/services/activity/activity_private.h" #include "util/attributes.h" bool WEAK activity_tracking_on(void) { @@ -69,26 +56,40 @@ uint32_t activity_private_compute_resting_calories(uint32_t elapsed_minutes) { return 0; } -uint8_t activity_prefs_heart_get_resting_hr(void) { +uint8_t WEAK activity_prefs_heart_get_resting_hr(void) { return 70; } -uint8_t activity_prefs_heart_get_elevated_hr(void) { +uint8_t WEAK activity_prefs_heart_get_elevated_hr(void) { return 100; } -uint8_t activity_prefs_heart_get_max_hr(void) { +uint8_t WEAK activity_prefs_heart_get_max_hr(void) { return 190; } -uint8_t activity_prefs_heart_get_zone1_threshold(void) { +uint8_t WEAK activity_prefs_heart_get_zone1_threshold(void) { return 130; } -uint8_t activity_prefs_heart_get_zone2_threshold(void) { +uint8_t WEAK activity_prefs_heart_get_zone2_threshold(void) { return 154; } -uint8_t activity_prefs_heart_get_zone3_threshold(void) { +uint8_t WEAK activity_prefs_heart_get_zone3_threshold(void) { return 172; } + +bool WEAK activity_is_initialized(void) { + return true; +} + +void WEAK activity_set_enabled(bool enabled) {} + +bool WEAK activity_start_tracking(bool test_mode) { + return true; +} + +bool WEAK activity_stop_tracking(void) { + return true; +} diff --git a/tests/stubs/stubs_activity_insights.h b/tests/stubs/stubs_activity_insights.h index c38585d5ce..cac68dcb45 100644 --- a/tests/stubs/stubs_activity_insights.h +++ b/tests/stubs/stubs_activity_insights.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_alarm.h b/tests/stubs/stubs_alarm.h index 7692b3ebcf..bc54e9eca7 100644 --- a/tests/stubs/stubs_alarm.h +++ b/tests/stubs/stubs_alarm.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_alarm_layout.h b/tests/stubs/stubs_alarm_layout.h index 775c914948..89e41ebb44 100644 --- a/tests/stubs/stubs_alarm_layout.h +++ b/tests/stubs/stubs_alarm_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/alarm_layout.h" +#include "pbl/services/timeline/alarm_layout.h" #include "util/attributes.h" LayoutLayer * WEAK alarm_layout_create(const LayoutLayerConfig *config) { diff --git a/tests/stubs/stubs_alarm_pin.h b/tests/stubs/stubs_alarm_pin.h index cb5ce0a587..a2451de3e4 100644 --- a/tests/stubs/stubs_alarm_pin.h +++ b/tests/stubs/stubs_alarm_pin.h @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/uuid.h" -#include "services/normal/alarms/alarm.h" +#include "pbl/services/alarms/alarm.h" void alarm_pin_add(time_t alarm_time, AlarmId id, AlarmType type, AlarmKind kind, Uuid *uuid_out) { } diff --git a/tests/stubs/stubs_alerts.h b/tests/stubs/stubs_alerts.h index dcceb7803e..5d0888e596 100644 --- a/tests/stubs/stubs_alerts.h +++ b/tests/stubs/stubs_alerts.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/notifications/alerts_private.h" +#include "pbl/services/notifications/alerts_private.h" #include "util/attributes.h" AlertMask s_alert_mask; diff --git a/tests/stubs/stubs_alerts_preferences.h b/tests/stubs/stubs_alerts_preferences.h index 73daaa278a..0ca865e1e6 100644 --- a/tests/stubs/stubs_alerts_preferences.h +++ b/tests/stubs/stubs_alerts_preferences.h @@ -1,24 +1,31 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/notifications/alerts_preferences_private.h" -#include "services/normal/vibes/vibe_intensity.h" +#include "pbl/services/notifications/alerts_preferences_private.h" +#include "util/attributes.h" -VibeIntensity alerts_preferences_get_vibe_intensity(void) { - return DEFAULT_VIBE_INTENSITY; +VibeScoreId WEAK alerts_preferences_get_vibe_score_for_client(VibeClient client) { + return VibeScoreId_Invalid; +} + +VibeIntensity WEAK alerts_preferences_get_vibe_intensity(void) { + return VibeIntensityLow; +} + +bool WEAK alerts_preferences_get_notification_alternative_design(void) { + return false; +} + +DndNotificationMode WEAK alerts_preferences_dnd_get_show_notifications(void) { + return DndNotificationModeShow; +} + +bool WEAK alerts_preferences_get_notification_vibe_delay(void) { + return false; +} + +NotificationStatusBarStyle WEAK alerts_preferences_get_notification_status_bar_style(void) { + return NotificationStatusBarStyle_Default; } diff --git a/tests/stubs/stubs_ambient_light.h b/tests/stubs/stubs_ambient_light.h index 636df20a8c..c7a6ba1cd2 100644 --- a/tests/stubs/stubs_ambient_light.h +++ b/tests/stubs/stubs_ambient_light.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_analytics.h b/tests/stubs/stubs_analytics.h index 603d64a945..57ba3b2a51 100644 --- a/tests/stubs/stubs_analytics.h +++ b/tests/stubs/stubs_analytics.h @@ -1,65 +1,36 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/analytics/analytics.h" +#include "pbl/services/analytics/analytics.h" -void analytics_init(void) {} +void pbl_analytics_init(void) {} -void analytics_set(AnalyticsMetric metric, int64_t val, AnalyticsClient client) {} -void analytics_set_for_uuid(AnalyticsMetric metric, int64_t val, const Uuid *uuid) {} +void sys_pbl_analytics_set_signed(enum pbl_analytics_key key, int32_t signed_value) { + (void)key; + (void)signed_value; +} -// TODO: Remove this, and add analytics_append_array or something. See PBL-5333 -void analytics_set_entire_array(AnalyticsMetric metric, const void *data, AnalyticsClient client) {} +void sys_pbl_analytics_set_unsigned(enum pbl_analytics_key key, uint32_t unsigned_value) { + (void)key; + (void)unsigned_value; +} -void analytics_inc(AnalyticsMetric metric, AnalyticsClient client) {} -void analytics_inc_for_uuid(AnalyticsMetric metric, const Uuid *uuid) {} +void sys_pbl_analytics_set_string(enum pbl_analytics_key key, const char *value) { + (void)key; + (void)value; +} -void analytics_add(AnalyticsMetric metric, int64_t amount, AnalyticsClient client) {} -void analytics_add_for_uuid(AnalyticsMetric metric, int64_t amount, const Uuid *uuid) {} +void sys_pbl_analytics_timer_start(enum pbl_analytics_key key) { + (void)key; +} -void analytics_stopwatch_start(AnalyticsMetric metric, AnalyticsClient client) {} -void analytics_stopwatch_stop(AnalyticsMetric metric) {} +void sys_pbl_analytics_timer_stop(enum pbl_analytics_key key) { + (void)key; +} -void analytics_stopwatch_start_at_rate(AnalyticsMetric metric, uint32_t count_per_sec, AnalyticsClient client) {} - -void analytics_event_app_launch(const Uuid *uuid) {} -void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) {} -void analytics_event_pin_created(time_t timestamp, const Uuid *parent_id) {} -void analytics_event_pin_updated(time_t timestamp, const Uuid *parent_id) {} - -void analytics_event_pin_open(time_t timestamp, const Uuid *parent_id) {} - -void analytics_event_pin_action(time_t timestamp, const Uuid *parent_id, - TimelineItemActionType action_type) {} - -void analytics_event_pin_app_launch(time_t timestamp, const Uuid *parent_id) {} - -void analytics_event_canned_response(const char *response, bool successfully_sent) {} - -void analytics_event_health_insight_created(time_t timestamp, - ActivityInsightType insight_type, - PercentTier pct_tier) {} -void analytics_event_health_insight_response(time_t timestamp, ActivityInsightType insight_type, - ActivitySessionType activity_type, - ActivityInsightResponseType response_id) {} - -void analytics_event_alarm(AnalyticsEvent event_type, const AlarmInfo *info) {} - -void analytics_event_PPoGATT_disconnect(time_t timestamp, bool successful_reconnect) {} - -void analytics_event_ble_hrm(BleHrmEventSubtype subtype) {} +void sys_pbl_analytics_add(enum pbl_analytics_key key, int32_t amount) { + (void)key; + (void)amount; +} diff --git a/tests/stubs/stubs_analytics_external.h b/tests/stubs/stubs_analytics_external.h deleted file mode 100644 index 16de131789..0000000000 --- a/tests/stubs/stubs_analytics_external.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -void analytics_external_update(void) {} - -void analytics_external_battery_init(void) {} -void analytics_external_collect_app_cpu_stats(void) {} -void analytics_external_collect_app_flash_read_stats(void) {} -void analytics_external_collect_kernel_heap_stats(void) {} diff --git a/tests/stubs/stubs_ancs.h b/tests/stubs/stubs_ancs.h index 09364100d0..3c8c93164d 100644 --- a/tests/stubs/stubs_ancs.h +++ b/tests/stubs/stubs_ancs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_ancs_filtering.h b/tests/stubs/stubs_ancs_filtering.h index a1a4804bdd..dc1651b940 100644 --- a/tests/stubs/stubs_ancs_filtering.h +++ b/tests/stubs/stubs_ancs_filtering.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/notifications/alerts_preferences.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/notifications/alerts_preferences.h" #include "util/attributes.h" uint8_t WEAK ancs_filtering_get_mute_type(const iOSNotifPrefs *app_notif_prefs) { diff --git a/tests/stubs/stubs_ancs_notifications.h b/tests/stubs/stubs_ancs_notifications.h index 37d1466f98..de1bfc7ec8 100644 --- a/tests/stubs/stubs_ancs_notifications.h +++ b/tests/stubs/stubs_ancs_notifications.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/notifications/ancs/ancs_notifications.h" +#include "pbl/services/notifications/ancs/ancs_notifications.h" void ancs_notifications_enable_bulk_action_mode(bool enable) { } diff --git a/tests/stubs/stubs_animation.c b/tests/stubs/stubs_animation.c index c2d80825d9..cf0312f1b5 100644 --- a/tests/stubs/stubs_animation.c +++ b/tests/stubs/stubs_animation.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/stubs/stubs_animation_service.h b/tests/stubs/stubs_animation_service.h index 48f8eda3da..6634be0877 100644 --- a/tests/stubs/stubs_animation_service.h +++ b/tests/stubs/stubs_animation_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_animation_timing.h b/tests/stubs/stubs_animation_timing.h index fbe111943e..8b76b250aa 100644 --- a/tests/stubs/stubs_animation_timing.h +++ b/tests/stubs/stubs_animation_timing.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app.h b/tests/stubs/stubs_app.h index abbdd5ebf7..c794c33361 100644 --- a/tests/stubs/stubs_app.h +++ b/tests/stubs/stubs_app.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_cache.h b/tests/stubs/stubs_app_cache.h index fcad8b7415..55912bc853 100644 --- a/tests/stubs/stubs_app_cache.h +++ b/tests/stubs/stubs_app_cache.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_custom_icon.h b/tests/stubs/stubs_app_custom_icon.h index a7e83ca176..a892a48c65 100644 --- a/tests/stubs/stubs_app_custom_icon.h +++ b/tests/stubs/stubs_app_custom_icon.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_db.h b/tests/stubs/stubs_app_db.h index fd3f7203a9..3e834546c0 100644 --- a/tests/stubs/stubs_app_db.h +++ b/tests/stubs/stubs_app_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -39,3 +26,7 @@ status_t app_db_delete(const uint8_t *key, int key_len) { status_t app_db_flush(void) { return S_SUCCESS; } + +status_t app_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_app_event_service.h b/tests/stubs/stubs_app_event_service.h index 24899c71fd..f1c0762ffc 100644 --- a/tests/stubs/stubs_app_event_service.h +++ b/tests/stubs/stubs_app_event_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_fetch_endpoint.h b/tests/stubs/stubs_app_fetch_endpoint.h index 041f8e5da7..ca93ac56c5 100644 --- a/tests/stubs/stubs_app_fetch_endpoint.h +++ b/tests/stubs/stubs_app_fetch_endpoint.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/app_fetch_endpoint.h" +#include "pbl/services/app_fetch_endpoint.h" #include "util/attributes.h" bool WEAK app_fetch_in_progress(void) { diff --git a/tests/stubs/stubs_app_glance_db.h b/tests/stubs/stubs_app_glance_db.h new file mode 100644 index 0000000000..db244e27d0 --- /dev/null +++ b/tests/stubs/stubs_app_glance_db.h @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/app_glance_db.h" + +void app_glance_db_init(void) {} + +status_t app_glance_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { + return S_SUCCESS; +} + +int app_glance_db_get_len(const uint8_t *key, int key_len) { + return 0; +} + +status_t app_glance_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) { + return S_SUCCESS; +} + +status_t app_glance_db_delete(const uint8_t *key, int key_len) { + return S_SUCCESS; +} + +status_t app_glance_db_flush(void) { + return S_SUCCESS; +} + +status_t app_glance_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_app_install_manager.c b/tests/stubs/stubs_app_install_manager.c index 2bd88ea016..ecaf96cbbd 100644 --- a/tests/stubs/stubs_app_install_manager.c +++ b/tests/stubs/stubs_app_install_manager.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_app_install_manager.h" diff --git a/tests/stubs/stubs_app_install_manager.h b/tests/stubs/stubs_app_install_manager.h index 5e226c1bb6..13f37c50c9 100644 --- a/tests/stubs/stubs_app_install_manager.h +++ b/tests/stubs/stubs_app_install_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_launch_reason.h b/tests/stubs/stubs_app_launch_reason.h index bd37b1ab73..7ffc4331e6 100644 --- a/tests/stubs/stubs_app_launch_reason.h +++ b/tests/stubs/stubs_app_launch_reason.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_manager.c b/tests/stubs/stubs_app_manager.c index b4ec372fe2..08163977f1 100644 --- a/tests/stubs/stubs_app_manager.c +++ b/tests/stubs/stubs_app_manager.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_app_manager.h" diff --git a/tests/stubs/stubs_app_manager.h b/tests/stubs/stubs_app_manager.h index 26a5c3301d..55e0a13944 100644 --- a/tests/stubs/stubs_app_manager.h +++ b/tests/stubs/stubs_app_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_menu_data_source.c b/tests/stubs/stubs_app_menu_data_source.c index 9da0bd5e90..d3154c30b1 100644 --- a/tests/stubs/stubs_app_menu_data_source.c +++ b/tests/stubs/stubs_app_menu_data_source.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_app_menu_data_source.h" diff --git a/tests/stubs/stubs_app_menu_data_source.h b/tests/stubs/stubs_app_menu_data_source.h index 79397c7675..15f32c2cd3 100644 --- a/tests/stubs/stubs_app_menu_data_source.h +++ b/tests/stubs/stubs_app_menu_data_source.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_state.c b/tests/stubs/stubs_app_state.c index b438df4bf5..0553aa532d 100644 --- a/tests/stubs/stubs_app_state.c +++ b/tests/stubs/stubs_app_state.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_app_state.h" diff --git a/tests/stubs/stubs_app_state.h b/tests/stubs/stubs_app_state.h index 88c714b649..ac1467dae6 100644 --- a/tests/stubs/stubs_app_state.h +++ b/tests/stubs/stubs_app_state.h @@ -1,22 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "applib/app_smartstrap_private.h" #include "applib/graphics/graphics.h" #include "applib/ui/animation_private.h" #include "applib/ui/click_internal.h" @@ -24,7 +10,7 @@ #include "applib/ui/window_stack_private.h" #include "applib/unobstructed_area_service_private.h" #include "process_state/app_state/app_state.h" -#include "services/normal/app_glances/app_glance_service.h" +#include "pbl/services/app_glances/app_glance_service.h" #include "util/attributes.h" #include "util/heap.h" @@ -104,12 +90,6 @@ WindowStack *app_state_get_window_stack(void) { return &s_window_stack; } -static SmartstrapConnectionState s_smartstrap_state; - -SmartstrapConnectionState *app_state_get_smartstrap_state(void) { - return &s_smartstrap_state; -} - static ClickManager click_manager; ClickManager *app_state_get_click_manager(void) { @@ -125,29 +105,29 @@ void *app_state_get_user_data(void) { return s_user_data; } -static RockyRuntimeContext *s_rocky_runtime_context = NULL; +static JsRuntimeContext *s_js_runtime_context = NULL; static uint8_t *s_runtime_context_buffer = NULL; -void app_state_set_rocky_runtime_context(uint8_t *unaligned_buffer, - RockyRuntimeContext *rocky_runtime_context) { - s_rocky_runtime_context = rocky_runtime_context; +void app_state_set_js_runtime_context(uint8_t *unaligned_buffer, + JsRuntimeContext *js_runtime_context) { + s_js_runtime_context = js_runtime_context; s_runtime_context_buffer = unaligned_buffer; } -uint8_t *app_state_get_rocky_runtime_context_buffer(void) { +uint8_t *app_state_get_js_runtime_context_buffer(void) { return s_runtime_context_buffer; } -RockyRuntimeContext *app_state_get_rocky_runtime_context(void) { - return s_rocky_runtime_context; +JsRuntimeContext *app_state_get_js_runtime_context(void) { + return s_js_runtime_context; } -static RockyMemoryAPIContext *s_rocky_memory_api_context = NULL; -void app_state_set_rocky_memory_api_context(RockyMemoryAPIContext *context) { - s_rocky_memory_api_context = context; +static JsMemoryAPIContext *s_js_memory_api_context = NULL; +void app_state_set_js_memory_api_context(JsMemoryAPIContext *context) { + s_js_memory_api_context = context; } -RockyMemoryAPIContext *app_state_get_rocky_memory_api_context(void) { - return s_rocky_memory_api_context; +JsMemoryAPIContext *app_state_get_js_memory_api_context(void) { + return s_js_memory_api_context; } UnobstructedAreaState s_stub_unobstructed_area_state; diff --git a/tests/stubs/stubs_app_timer.h b/tests/stubs/stubs_app_timer.h index 77638d2435..d618d6884f 100644 --- a/tests/stubs/stubs_app_timer.h +++ b/tests/stubs/stubs_app_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_app_window_stack.h b/tests/stubs/stubs_app_window_stack.h index 8cdfe2f020..42fe906ec6 100644 --- a/tests/stubs/stubs_app_window_stack.h +++ b/tests/stubs/stubs_app_window_stack.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_applib_resource.h b/tests/stubs/stubs_applib_resource.h index b5faa0b6a2..dfcd29aec5 100644 --- a/tests/stubs/stubs_applib_resource.h +++ b/tests/stubs/stubs_applib_resource.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_attribute.h b/tests/stubs/stubs_attribute.h index c108b36d34..91d273a30d 100644 --- a/tests/stubs/stubs_attribute.h +++ b/tests/stubs/stubs_attribute.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/attribute.h" +#include "pbl/services/timeline/attribute.h" const char *attribute_get_string(const AttributeList *attr_list, AttributeId id, char *default_value) { diff --git a/tests/stubs/stubs_battery.h b/tests/stubs/stubs_battery.h index b8ded00f1c..6d65f483e0 100644 --- a/tests/stubs/stubs_battery.h +++ b/tests/stubs/stubs_battery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ bool battery_is_usb_connected(void) { return false; diff --git a/tests/stubs/stubs_battery_monitor.h b/tests/stubs/stubs_battery_monitor.h index d13c50b494..b8e1b96193 100644 --- a/tests/stubs/stubs_battery_monitor.h +++ b/tests/stubs/stubs_battery_monitor.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ bool battery_monitor_critical_lockout(void) { return false; diff --git a/tests/stubs/stubs_battery_state_service.h b/tests/stubs/stubs_battery_state_service.h index 1d1afdc5f4..242a8443fa 100644 --- a/tests/stubs/stubs_battery_state_service.h +++ b/tests/stubs/stubs_battery_state_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bitblt.c b/tests/stubs/stubs_bitblt.c index a0b9b642bb..69cb78a6c4 100644 --- a/tests/stubs/stubs_bitblt.c +++ b/tests/stubs/stubs_bitblt.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_bitblt.h" diff --git a/tests/stubs/stubs_bitblt.h b/tests/stubs/stubs_bitblt.h index 6eb7aa5f60..a99ba4cc71 100644 --- a/tests/stubs/stubs_bitblt.h +++ b/tests/stubs/stubs_bitblt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_ble.h b/tests/stubs/stubs_ble.h index d973f08806..e6aa4fb629 100644 --- a/tests/stubs/stubs_ble.h +++ b/tests/stubs/stubs_ble.h @@ -1,25 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "stubs_bluetooth_pairing_ui.h" #include "stubs_events.h" -#include "stubs_HCIAPI.h" -#include "stubs_L2CAPAPI.h" #include "stubs_hexdump.h" #include "stubs_queue.h" diff --git a/tests/stubs/stubs_ble_app_support.h b/tests/stubs/stubs_ble_app_support.h index 2d484b643e..9cb63d5218 100644 --- a/tests/stubs/stubs_ble_app_support.h +++ b/tests/stubs/stubs_ble_app_support.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_ble_syscalls.h b/tests/stubs/stubs_ble_syscalls.h index 0965e917fe..44f24d9419 100644 --- a/tests/stubs/stubs_ble_syscalls.h +++ b/tests/stubs/stubs_ble_syscalls.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_blob_db.c b/tests/stubs/stubs_blob_db.c index d94b6fe49c..cd45dd8e2e 100644 --- a/tests/stubs/stubs_blob_db.c +++ b/tests/stubs/stubs_blob_db.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_blob_db.h" diff --git a/tests/stubs/stubs_blob_db.h b/tests/stubs/stubs_blob_db.h index cec7bff8a4..96a3dc6d6c 100644 --- a/tests/stubs/stubs_blob_db.h +++ b/tests/stubs/stubs_blob_db.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "kernel/events.h" -#include "services/normal/blob_db/api.h" +#include "pbl/services/blob_db/api.h" #include "util/attributes.h" status_t WEAK blob_db_delete(BlobDBId db_id, const uint8_t *key, int key_len) { diff --git a/tests/stubs/stubs_blob_db_sync.h b/tests/stubs/stubs_blob_db_sync.h index e96491ccbc..e75efd899b 100644 --- a/tests/stubs/stubs_blob_db_sync.h +++ b/tests/stubs/stubs_blob_db_sync.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/normal/blob_db/sync.h" +#include "pbl/services/blob_db/sync.h" status_t blob_db_sync_db(BlobDBId db_id) { return S_SUCCESS; diff --git a/tests/stubs/stubs_blob_db_sync_util.h b/tests/stubs/stubs_blob_db_sync_util.h index b8b8c21c4c..59e3d714b8 100644 --- a/tests/stubs/stubs_blob_db_sync_util.h +++ b/tests/stubs/stubs_blob_db_sync_util.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/api.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/settings/settings_file.h" bool sync_util_is_dirty_cb(SettingsFile *file, SettingsRecordInfo *info, void *context) { return false; diff --git a/tests/stubs/stubs_bluetooth_analytics.h b/tests/stubs/stubs_bluetooth_analytics.h index d01d10a169..97fa02065c 100644 --- a/tests/stubs/stubs_bluetooth_analytics.h +++ b/tests/stubs/stubs_bluetooth_analytics.h @@ -1,34 +1,30 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "GAPAPI.h" +// The bluetooth_analytics interface was reworked off the Bluetopia GAPAPI types onto the +// transport-agnostic BleConnectionParams / BTDeviceInternal types from bluetooth/gap_le_connect.h. +// These stubs mirror the current declarations in src/fw/comm/bluetooth_analytics.h. + +#include "bluetooth/gap_le_connect.h" #include +#include + +typedef struct SlaveConnEventStats SlaveConnEventStats; void bluetooth_analytics_get_param_averages(uint16_t *params) { } -void bluetooth_analytics_handle_connection_params_update( - const GAP_LE_Current_Connection_Parameters_t *params) { +void bluetooth_analytics_handle_param_update_failed(void) { +} + +void bluetooth_analytics_handle_connection_params_update(const BleConnectionParams *params) { } -void bluetooth_analytics_handle_connect(unsigned int stack_id, - const GAP_LE_Connection_Complete_Event_Data_t *event) { +void bluetooth_analytics_handle_connect( + const BTDeviceInternal *peer_addr, const BleConnectionParams *conn_params) { } void bluetooth_analytics_handle_disconnect(bool local_is_master) { @@ -39,3 +35,26 @@ void bluetooth_analytics_handle_encryption_change(void) { void bluetooth_analytics_handle_no_intent_for_connection(void) { } + +void bluetooth_analytics_handle_ble_pairing_request(void) { +} + +void bluetooth_analytics_handle_ble_pairing_error(uint32_t error) { +} + +void bluetooth_analytics_handle_connection_disconnection_event( + uint8_t reason, const BleRemoteVersionInfo *vers_info) { +} + +void bluetooth_analytics_handle_put_bytes_stats(bool successful, uint8_t type, uint32_t total_size, + uint32_t elapsed_time_ms, + const SlaveConnEventStats *orig_stats) { +} + +void bluetooth_analytics_handle_get_bytes_stats(uint8_t type, uint32_t total_size, + uint32_t elapsed_time_ms, + const SlaveConnEventStats *orig_stats) { +} + +void bluetooth_analytics_ble_mic_error(uint32_t num_sequential_mic_errors) { +} diff --git a/tests/stubs/stubs_bluetooth_ctl.h b/tests/stubs/stubs_bluetooth_ctl.h index 30a96e9c01..ff5541da57 100644 --- a/tests/stubs/stubs_bluetooth_ctl.h +++ b/tests/stubs/stubs_bluetooth_ctl.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bluetooth_pairing_ui.h b/tests/stubs/stubs_bluetooth_pairing_ui.h index 230c3c2a97..61f26ea69f 100644 --- a/tests/stubs/stubs_bluetooth_pairing_ui.h +++ b/tests/stubs/stubs_bluetooth_pairing_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bluetooth_persistent_storage.h b/tests/stubs/stubs_bluetooth_persistent_storage.h index 0a0c81a868..d464961d95 100644 --- a/tests/stubs/stubs_bluetooth_persistent_storage.h +++ b/tests/stubs/stubs_bluetooth_persistent_storage.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session_remote_version.h" +#include "pbl/services/comm_session/session_remote_version.h" #include "util/attributes.h" void WEAK bt_persistent_storage_get_cached_system_capabilities( diff --git a/tests/stubs/stubs_bluetooth_persistent_storage_debug.h b/tests/stubs/stubs_bluetooth_persistent_storage_debug.h index 1aeb907004..cce1c4b9ce 100644 --- a/tests/stubs/stubs_bluetooth_persistent_storage_debug.h +++ b/tests/stubs/stubs_bluetooth_persistent_storage_debug.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bluetopia_interface.h b/tests/stubs/stubs_bluetopia_interface.h index 2b4d9391a1..30fde225bb 100644 --- a/tests/stubs/stubs_bluetopia_interface.h +++ b/tests/stubs/stubs_bluetopia_interface.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bootbits.h b/tests/stubs/stubs_bootbits.h index 96d3da29d6..8c45afded9 100644 --- a/tests/stubs/stubs_bootbits.h +++ b/tests/stubs/stubs_bootbits.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bt_conn_mgr.h b/tests/stubs/stubs_bt_conn_mgr.h index 22d7405a67..684136f1a8 100644 --- a/tests/stubs/stubs_bt_conn_mgr.h +++ b/tests/stubs/stubs_bt_conn_mgr.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "comm/ble/gap_le_connection.h" #include "comm/bt_conn_mgr.h" diff --git a/tests/stubs/stubs_bt_driver.h b/tests/stubs/stubs_bt_driver.h deleted file mode 100644 index e465f1be00..0000000000 --- a/tests/stubs/stubs_bt_driver.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void bt_driver_classic_update_connectability(void) { -} - -bool bt_driver_supports_bt_classic(void) { - return true; -} diff --git a/tests/stubs/stubs_bt_driver_gatt.h b/tests/stubs/stubs_bt_driver_gatt.h index f057d3cbc2..203d0874c6 100644 --- a/tests/stubs/stubs_bt_driver_gatt.h +++ b/tests/stubs/stubs_bt_driver_gatt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,12 +8,22 @@ // TODO: Rethink how we want to stub out these new driver wrapper calls. -void bt_driver_gatt_send_changed_indication(uint32_t connection_id, const ATTHandleRange *data) { +void bt_driver_gatt_send_changed_indication(const BTDeviceInternal *device, + const ATTHandleRange *data) { GATT_Service_Changed_Data_t all_changed_range = { .Affected_Start_Handle = data->start, .Affected_End_Handle = data->end, }; - GATT_Service_Changed_Indication(bt_stack_id(), connection_id, &all_changed_range); + // The legacy Bluetopia test stub needs a connection ID to pass through to + // GATT_Service_Changed_Indication. Look up the connection by device address. + // If no match is found the indication is silently dropped (matches the stub's + // no-op behaviour for unresolvable devices). + uint16_t conn_id = 0; + GAPLEConnection *connection = gap_le_connection_by_device(device); + if (connection) { + conn_id = connection->gatt_connection_id; + } + GATT_Service_Changed_Indication(bt_stack_id(), conn_id, &all_changed_range); } void bt_driver_gatt_respond_read_subscription(uint32_t transaction_id, uint16_t response_code) { diff --git a/tests/stubs/stubs_bt_driver_gatt_client_discovery.h b/tests/stubs/stubs_bt_driver_gatt_client_discovery.h index 6732389a9b..4f1acad3a7 100644 --- a/tests/stubs/stubs_bt_driver_gatt_client_discovery.h +++ b/tests/stubs/stubs_bt_driver_gatt_client_discovery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bt_lock.h b/tests/stubs/stubs_bt_lock.h index cf6a218207..c306f21caa 100644 --- a/tests/stubs/stubs_bt_lock.h +++ b/tests/stubs/stubs_bt_lock.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_bt_stack.h b/tests/stubs/stubs_bt_stack.h index 2971c69399..781ae9dd3a 100644 --- a/tests/stubs/stubs_bt_stack.h +++ b/tests/stubs/stubs_bt_stack.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_buffer.h b/tests/stubs/stubs_buffer.h index 01a2bf7d50..ba9dcf7cc8 100644 --- a/tests/stubs/stubs_buffer.h +++ b/tests/stubs/stubs_buffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_build_id.h b/tests/stubs/stubs_build_id.h index 65b604bf66..b835e24533 100644 --- a/tests/stubs/stubs_build_id.h +++ b/tests/stubs/stubs_build_id.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_cache.h b/tests/stubs/stubs_cache.h index fdcd1f56f3..d8c5f8a805 100644 --- a/tests/stubs/stubs_cache.h +++ b/tests/stubs/stubs_cache.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_calendar.h b/tests/stubs/stubs_calendar.h index 4ba8b2609b..822f4d538d 100644 --- a/tests/stubs/stubs_calendar.h +++ b/tests/stubs/stubs_calendar.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_calendar_layout.h b/tests/stubs/stubs_calendar_layout.h index 6176cf12ee..94f2c94d98 100644 --- a/tests/stubs/stubs_calendar_layout.h +++ b/tests/stubs/stubs_calendar_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/calendar_layout.h" +#include "pbl/services/timeline/calendar_layout.h" #include "util/attributes.h" LayoutLayer * WEAK calendar_layout_create(const LayoutLayerConfig *config) { diff --git a/tests/stubs/stubs_clar.c b/tests/stubs/stubs_clar.c index e1c5c48529..4f0a3712ce 100644 --- a/tests/stubs/stubs_clar.c +++ b/tests/stubs/stubs_clar.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/stubs/stubs_click.c b/tests/stubs/stubs_click.c index 6a809fa65f..43cf8538d6 100644 --- a/tests/stubs/stubs_click.c +++ b/tests/stubs/stubs_click.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_click.h" diff --git a/tests/stubs/stubs_click.h b/tests/stubs/stubs_click.h index 94ba62c888..0eb9cd603d 100644 --- a/tests/stubs/stubs_click.h +++ b/tests/stubs/stubs_click.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_clock.c b/tests/stubs/stubs_clock.c index cfad29dff1..f89159fcd4 100644 --- a/tests/stubs/stubs_clock.c +++ b/tests/stubs/stubs_clock.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_clock.h" diff --git a/tests/stubs/stubs_clock.h b/tests/stubs/stubs_clock.h index 86be4d24da..c0bef1dea9 100644 --- a/tests/stubs/stubs_clock.h +++ b/tests/stubs/stubs_clock.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/clock.h" +#include "pbl/services/clock.h" #include "util/attributes.h" void WEAK clock_get_since_time(char *buffer, int buf_size, time_t timestamp) {} diff --git a/tests/stubs/stubs_codepoint.h b/tests/stubs/stubs_codepoint.h index cea833c3d0..2b3508c393 100644 --- a/tests/stubs/stubs_codepoint.h +++ b/tests/stubs/stubs_codepoint.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_comm_session.h b/tests/stubs/stubs_comm_session.h index 7cb329c9f6..413cf0c5a2 100644 --- a/tests/stubs/stubs_comm_session.h +++ b/tests/stubs/stubs_comm_session.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/app_session_capabilities.h" +#include "pbl/services/comm_session/app_session_capabilities.h" #include "util/attributes.h" void WEAK comm_session_app_session_capabilities_evict(const Uuid *app_uuid) {} diff --git a/tests/stubs/stubs_compiled_with_legacy2_sdk.h b/tests/stubs/stubs_compiled_with_legacy2_sdk.h index 72e5b6e64d..e519cba01d 100644 --- a/tests/stubs/stubs_compiled_with_legacy2_sdk.h +++ b/tests/stubs/stubs_compiled_with_legacy2_sdk.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_compositor.h b/tests/stubs/stubs_compositor.h index ebc7383fb8..9655e5b193 100644 --- a/tests/stubs/stubs_compositor.h +++ b/tests/stubs/stubs_compositor.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ void compositor_transition(const CompositorTransition *type) { } diff --git a/tests/stubs/stubs_compositor_dma.h b/tests/stubs/stubs_compositor_dma.h deleted file mode 100644 index f71eb64696..0000000000 --- a/tests/stubs/stubs_compositor_dma.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void compositor_dma_init(void) { } diff --git a/tests/stubs/stubs_compositor_transitions.h b/tests/stubs/stubs_compositor_transitions.h index 935f51565f..6e58149345 100644 --- a/tests/stubs/stubs_compositor_transitions.h +++ b/tests/stubs/stubs_compositor_transitions.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/compositor/compositor_transitions.h" +#include "pbl/services/compositor/compositor_transitions.h" #include "util/attributes.h" const CompositorTransition *WEAK compositor_slide_transition_timeline_get( diff --git a/tests/stubs/stubs_confirmation_dialog.h b/tests/stubs/stubs_confirmation_dialog.h index ce3081b9e0..e7fddb5f13 100644 --- a/tests/stubs/stubs_confirmation_dialog.h +++ b/tests/stubs/stubs_confirmation_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_connection_service.h b/tests/stubs/stubs_connection_service.h index f0a182876c..5305a584ab 100644 --- a/tests/stubs/stubs_connection_service.h +++ b/tests/stubs/stubs_connection_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/connection_service.h" #include "applib/connection_service_private.h" diff --git a/tests/stubs/stubs_contacts.h b/tests/stubs/stubs_contacts.h index bd23f7664b..012d932e94 100644 --- a/tests/stubs/stubs_contacts.h +++ b/tests/stubs/stubs_contacts.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_contacts_db.h b/tests/stubs/stubs_contacts_db.h index 8189268c75..9d02bd9fda 100644 --- a/tests/stubs/stubs_contacts_db.h +++ b/tests/stubs/stubs_contacts_db.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/contacts_db.h" +#include "pbl/services/blob_db/contacts_db.h" int contacts_db_get_serialized_contact(const Uuid *uuid, SerializedContact **contact_out) { return 0; @@ -49,3 +36,7 @@ status_t contacts_db_delete(const uint8_t *key, int key_len) { status_t contacts_db_flush(void) { return S_SUCCESS; } + +status_t contacts_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_content_indicator.h b/tests/stubs/stubs_content_indicator.h index aed45c104d..3eabc1ded9 100644 --- a/tests/stubs/stubs_content_indicator.h +++ b/tests/stubs/stubs_content_indicator.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_crc.h b/tests/stubs/stubs_crc.h index 4418a3f847..89c8d518b8 100644 --- a/tests/stubs/stubs_crc.h +++ b/tests/stubs/stubs_crc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ void crc_init(void) { } diff --git a/tests/stubs/stubs_cron.h b/tests/stubs/stubs_cron.h index b2282e936d..cf166730ff 100644 --- a/tests/stubs/stubs_cron.h +++ b/tests/stubs/stubs_cron.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_dialog.h b/tests/stubs/stubs_dialog.h index 0e33b78767..ead9251bbd 100644 --- a/tests/stubs/stubs_dialog.h +++ b/tests/stubs/stubs_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_dls.h b/tests/stubs/stubs_dls.h index dc791d9a49..ffac9f9022 100644 --- a/tests/stubs/stubs_dls.h +++ b/tests/stubs/stubs_dls.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/normal/data_logging/data_logging_service.h" +#include "pbl/services/data_logging/data_logging_service.h" void dls_inactivate_sessions(PebbleTask task) { } diff --git a/tests/stubs/stubs_do_not_disturb.h b/tests/stubs/stubs_do_not_disturb.h index 9a70fc232e..b8ae964e0e 100644 --- a/tests/stubs/stubs_do_not_disturb.h +++ b/tests/stubs/stubs_do_not_disturb.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/notifications/do_not_disturb.h" +#include "pbl/services/notifications/do_not_disturb.h" #include "util/attributes.h" bool WEAK do_not_disturb_is_active(void) { diff --git a/tests/stubs/stubs_event_loop.h b/tests/stubs/stubs_event_loop.h index 4b21e8d352..c8d4c007f2 100644 --- a/tests/stubs/stubs_event_loop.h +++ b/tests/stubs/stubs_event_loop.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_event_service_client.h b/tests/stubs/stubs_event_service_client.h index 731f7cba47..f4a2910eea 100644 --- a/tests/stubs/stubs_event_service_client.h +++ b/tests/stubs/stubs_event_service_client.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_evented_timer.c b/tests/stubs/stubs_evented_timer.c index 470a6a53b9..98134a218b 100644 --- a/tests/stubs/stubs_evented_timer.c +++ b/tests/stubs/stubs_evented_timer.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_evented_timer.h" diff --git a/tests/stubs/stubs_evented_timer.h b/tests/stubs/stubs_evented_timer.h index 9e0cf3fda4..ef1576e183 100644 --- a/tests/stubs/stubs_evented_timer.h +++ b/tests/stubs/stubs_evented_timer.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/evented_timer.h" +#include "pbl/services/evented_timer.h" #include "util/attributes.h" void WEAK evented_timer_init(void) {} diff --git a/tests/stubs/stubs_events.c b/tests/stubs/stubs_events.c index 3a7f4179f0..99b6373a1f 100644 --- a/tests/stubs/stubs_events.c +++ b/tests/stubs/stubs_events.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_events.h" diff --git a/tests/stubs/stubs_events.h b/tests/stubs/stubs_events.h index 244c666f4a..2a834f5f55 100644 --- a/tests/stubs/stubs_events.h +++ b/tests/stubs/stubs_events.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_expandable_dialog.h b/tests/stubs/stubs_expandable_dialog.h index eb19f06786..f5d371294c 100644 --- a/tests/stubs/stubs_expandable_dialog.h +++ b/tests/stubs/stubs_expandable_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_features.h b/tests/stubs/stubs_features.h deleted file mode 100644 index 0a4c169a78..0000000000 --- a/tests/stubs/stubs_features.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -bool bt_driver_supports_bt_classic(void) { - return false; -} diff --git a/tests/stubs/stubs_firmware_update.h b/tests/stubs/stubs_firmware_update.h index cf5fcd8a27..2c5ffbc380 100644 --- a/tests/stubs/stubs_firmware_update.h +++ b/tests/stubs/stubs_firmware_update.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_fonts.h b/tests/stubs/stubs_fonts.h index 79745a0310..d22efc7c01 100644 --- a/tests/stubs/stubs_fonts.h +++ b/tests/stubs/stubs_fonts.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_framebuffer.h b/tests/stubs/stubs_framebuffer.h index 999b4ce9b0..b78698e4b5 100644 --- a/tests/stubs/stubs_framebuffer.h +++ b/tests/stubs/stubs_framebuffer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -27,3 +14,7 @@ void WEAK framebuffer_mark_dirty_rect(FrameBuffer *f, GRect rect) {} void WEAK framebuffer_init(FrameBuffer *f, const GSize *size) { f->size = *size; } GSize WEAK framebuffer_get_size(FrameBuffer *f) { return f->size; } + +// 8-bit (rectangular) framebuffers store one byte per pixel, matching the production +// framebuffer_get_size_bytes() for the non-round case. +size_t WEAK framebuffer_get_size_bytes(FrameBuffer *f) { return f->size.w * f->size.h; } diff --git a/tests/stubs/stubs_freertos.h b/tests/stubs/stubs_freertos.h index c918d6c789..8aa2a96e5f 100644 --- a/tests/stubs/stubs_freertos.h +++ b/tests/stubs/stubs_freertos.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_gap_le_advert.h b/tests/stubs/stubs_gap_le_advert.h index 5db8c25d9e..5e19c57946 100644 --- a/tests/stubs/stubs_gap_le_advert.h +++ b/tests/stubs/stubs_gap_le_advert.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_gatt_client_discovery.h b/tests/stubs/stubs_gatt_client_discovery.h index 51d02769f0..de56e5021c 100644 --- a/tests/stubs/stubs_gatt_client_discovery.h +++ b/tests/stubs/stubs_gatt_client_discovery.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_gatt_client_subscriptions.h b/tests/stubs/stubs_gatt_client_subscriptions.h index fa1a1146cd..6c224a06d2 100644 --- a/tests/stubs/stubs_gatt_client_subscriptions.h +++ b/tests/stubs/stubs_gatt_client_subscriptions.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_gbitmap.h b/tests/stubs/stubs_gbitmap.h index 6f1ae261b1..e6a712b40d 100644 --- a/tests/stubs/stubs_gbitmap.h +++ b/tests/stubs/stubs_gbitmap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -45,3 +32,14 @@ uint16_t gbitmap_format_get_row_size_bytes(int16_t width, GBitmapFormat format) GRect gbitmap_get_bounds(const GBitmap *bitmap) { return bitmap->bounds; } + +void gbitmap_init_as_sub_bitmap(GBitmap *sub_bitmap, const GBitmap *base_bitmap, GRect sub_rect) { + // Mirrors the non-legacy (GBITMAP_VERSION_1) path of the production implementation: the sub + // bitmap shares the base bitmap's data, owns none of it, and is clipped to the base bounds. + // The compositor only ever passes the version-1 framebuffer bitmaps here. + *sub_bitmap = *base_bitmap; + sub_bitmap->info.is_palette_heap_allocated = false; + sub_bitmap->info.is_bitmap_heap_allocated = false; + grect_clip(&sub_rect, &base_bitmap->bounds); + sub_bitmap->bounds = sub_rect; +} diff --git a/tests/stubs/stubs_gcolor.h b/tests/stubs/stubs_gcolor.h index ddc3303386..513d2ef01b 100644 --- a/tests/stubs/stubs_gcolor.h +++ b/tests/stubs/stubs_gcolor.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_generic_layout.h b/tests/stubs/stubs_generic_layout.h index 1c4245c6bf..cacdf014b6 100644 --- a/tests/stubs/stubs_generic_layout.h +++ b/tests/stubs/stubs_generic_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/generic_layout.h" +#include "pbl/services/timeline/generic_layout.h" #include "util/attributes.h" LayoutLayer * WEAK generic_layout_create(const LayoutLayerConfig *config) { diff --git a/tests/stubs/stubs_gettext.h b/tests/stubs/stubs_gettext.h index bb5cfd14c2..5e789b556f 100644 --- a/tests/stubs/stubs_gettext.h +++ b/tests/stubs/stubs_gettext.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_gpath.h b/tests/stubs/stubs_gpath.h index 8188b158ee..406adeec8b 100644 --- a/tests/stubs/stubs_gpath.h +++ b/tests/stubs/stubs_gpath.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_graphics.h b/tests/stubs/stubs_graphics.h index 7cdb7fe7be..fc848cb35f 100644 --- a/tests/stubs/stubs_graphics.h +++ b/tests/stubs/stubs_graphics.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_graphics_circle.h b/tests/stubs/stubs_graphics_circle.h index 53b7bd7a3e..3ec2573cdf 100644 --- a/tests/stubs/stubs_graphics_circle.h +++ b/tests/stubs/stubs_graphics_circle.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_graphics_context.h b/tests/stubs/stubs_graphics_context.h index fdb052c2da..798421f7ac 100644 --- a/tests/stubs/stubs_graphics_context.h +++ b/tests/stubs/stubs_graphics_context.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_graphics_line.h b/tests/stubs/stubs_graphics_line.h index 4d196c68b4..779ca57663 100644 --- a/tests/stubs/stubs_graphics_line.h +++ b/tests/stubs/stubs_graphics_line.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_graphics_private.h b/tests/stubs/stubs_graphics_private.h index 52534d22dc..3bcb8b1279 100644 --- a/tests/stubs/stubs_graphics_private.h +++ b/tests/stubs/stubs_graphics_private.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_health_db.h b/tests/stubs/stubs_health_db.h index 76e8110ec1..9257eb33f3 100644 --- a/tests/stubs/stubs_health_db.h +++ b/tests/stubs/stubs_health_db.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/normal/blob_db/health_db.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/blob_db/health_db.h" bool health_db_get_typical_value(ActivityMetric metric, DayInWeek day, @@ -38,3 +25,29 @@ bool health_db_set_typical_values(ActivityMetric metric, int num_values) { return false; } + +void health_db_init(void) {} + +status_t health_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) { + return S_SUCCESS; +} + +int health_db_get_len(const uint8_t *key, int key_len) { + return 0; +} + +status_t health_db_read(const uint8_t *key, int key_len, uint8_t *val_out, int val_out_len) { + return S_SUCCESS; +} + +status_t health_db_delete(const uint8_t *key, int key_len) { + return S_SUCCESS; +} + +status_t health_db_flush(void) { + return S_SUCCESS; +} + +status_t health_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_health_layout.h b/tests/stubs/stubs_health_layout.h index db8be0e17e..7d3927b045 100644 --- a/tests/stubs/stubs_health_layout.h +++ b/tests/stubs/stubs_health_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/health_layout.h" +#include "pbl/services/timeline/health_layout.h" #include "util/attributes.h" LayoutLayer * WEAK health_layout_create(const LayoutLayerConfig *config) { diff --git a/tests/stubs/stubs_health_service.h b/tests/stubs/stubs_health_service.h index 0a9e04d4f0..e4aec82245 100644 --- a/tests/stubs/stubs_health_service.h +++ b/tests/stubs/stubs_health_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_health_util.h b/tests/stubs/stubs_health_util.h index b341a0e753..65fe1c44e5 100644 --- a/tests/stubs/stubs_health_util.h +++ b/tests/stubs/stubs_health_util.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/graphics.h" -#include "apps/system_apps/timeline/text_node.h" +#include "apps/system/timeline/text_node.h" int health_util_format_hours_and_minutes(char *buffer, size_t buffer_size, int duration_s, void *i18n_owner) { diff --git a/tests/stubs/stubs_heap.h b/tests/stubs/stubs_heap.h index 2a30492044..f042a50a0c 100644 --- a/tests/stubs/stubs_heap.h +++ b/tests/stubs/stubs_heap.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "util/heap.h" diff --git a/tests/stubs/stubs_hexdump.h b/tests/stubs/stubs_hexdump.h index a630296867..5a1fde9ffc 100644 --- a/tests/stubs/stubs_hexdump.h +++ b/tests/stubs/stubs_hexdump.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_hr_util.h b/tests/stubs/stubs_hr_util.h index 9a88c581f3..2a1ead86f8 100644 --- a/tests/stubs/stubs_hr_util.h +++ b/tests/stubs/stubs_hr_util.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/activity/hr_util.h" +#include "pbl/services/activity/hr_util.h" HRZone hr_util_get_hr_zone(int bpm) { return HRZone_Zone1; diff --git a/tests/stubs/stubs_i18n.c b/tests/stubs/stubs_i18n.c index 6656899012..8f69a721e7 100644 --- a/tests/stubs/stubs_i18n.c +++ b/tests/stubs/stubs_i18n.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_i18n.h" diff --git a/tests/stubs/stubs_i18n.h b/tests/stubs/stubs_i18n.h index 464e78e7c5..a18f6d567f 100644 --- a/tests/stubs/stubs_i18n.h +++ b/tests/stubs/stubs_i18n.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/i18n/i18n.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/i18n/i18n.h" #include "util/attributes.h" #include @@ -59,3 +46,5 @@ size_t WEAK sys_i18n_get_length(const char *string) { } void WEAK i18n_enable(bool enable) { } + +void WEAK i18n_set_resource(uint32_t resource_id) { } diff --git a/tests/stubs/stubs_ios_notif_pref_db.h b/tests/stubs/stubs_ios_notif_pref_db.h index becfd0b603..d839e85f7e 100644 --- a/tests/stubs/stubs_ios_notif_pref_db.h +++ b/tests/stubs/stubs_ios_notif_pref_db.h @@ -1,24 +1,11 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/api.h" -#include "services/normal/blob_db/ios_notif_pref_db.h" -#include "services/normal/timeline/item.h" +#include "pbl/services/blob_db/api.h" +#include "pbl/services/blob_db/ios_notif_pref_db.h" +#include "pbl/services/timeline/item.h" iOSNotifPrefs* ios_notif_pref_db_get_prefs(const uint8_t *app_id, int length) { return NULL; @@ -71,3 +58,7 @@ BlobDBDirtyItem* ios_notif_pref_db_get_dirty_list(void) { status_t ios_notif_pref_db_mark_synced(const uint8_t *key, int key_len) { return S_SUCCESS; } + +status_t ios_notif_pref_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_ispp.h b/tests/stubs/stubs_ispp.h index 67242d0650..3b139075c2 100644 --- a/tests/stubs/stubs_ispp.h +++ b/tests/stubs/stubs_ispp.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_kino_layer.h b/tests/stubs/stubs_kino_layer.h index 57d2d88595..7966240c47 100644 --- a/tests/stubs/stubs_kino_layer.h +++ b/tests/stubs/stubs_kino_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/kino/kino_layer.h" @@ -39,7 +26,7 @@ void kino_layer_set_reel(KinoLayer *kino_layer, KinoReel *reel, bool take_owners void kino_layer_set_reel_with_resource(KinoLayer *kino_layer, uint32_t resource_id) { } void kino_layer_set_reel_with_resource_system(KinoLayer *kino_layer, ResAppNum app_num, - uint32_t resource_id) { + uint32_t resource_id, bool invert) { } KinoReel *kino_layer_get_reel(KinoLayer *kino_layer) { diff --git a/tests/stubs/stubs_kino_player.h b/tests/stubs/stubs_kino_player.h index b91086715c..1f60128273 100644 --- a/tests/stubs/stubs_kino_player.h +++ b/tests/stubs/stubs_kino_player.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_kino_reel.h b/tests/stubs/stubs_kino_reel.h index 21342ae030..d5d627ce33 100644 --- a/tests/stubs/stubs_kino_reel.h +++ b/tests/stubs/stubs_kino_reel.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/kino/kino_reel.h" diff --git a/tests/stubs/stubs_language_ui.h b/tests/stubs/stubs_language_ui.h index 63c9ecc00e..4d63cf1d86 100644 --- a/tests/stubs/stubs_language_ui.h +++ b/tests/stubs/stubs_language_ui.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_layer.h b/tests/stubs/stubs_layer.h index 50c1e95721..e34825ebfb 100644 --- a/tests/stubs/stubs_layer.h +++ b/tests/stubs/stubs_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_layout_layer.h b/tests/stubs/stubs_layout_layer.h index 13f1adaf9e..a182bbd4c8 100644 --- a/tests/stubs/stubs_layout_layer.h +++ b/tests/stubs/stubs_layout_layer.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/normal/timeline/layout_layer.h" +#include "pbl/services/timeline/layout_layer.h" LayoutLayer *layout_create(LayoutId id, const LayoutLayerConfig *config) { return NULL; diff --git a/tests/stubs/stubs_layout_node.h b/tests/stubs/stubs_layout_node.h index ad1064eedd..e5b4e59758 100644 --- a/tests/stubs/stubs_layout_node.h +++ b/tests/stubs/stubs_layout_node.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/layout_node.h" +#include "pbl/services/timeline/layout_node.h" GTextNode *layout_create_text_node_from_config(const LayoutLayer *layout, const LayoutNodeConfig *config) { diff --git a/tests/stubs/stubs_light.h b/tests/stubs/stubs_light.h index cc447ea659..2635e58e60 100644 --- a/tests/stubs/stubs_light.h +++ b/tests/stubs/stubs_light.h @@ -1,21 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "util/attributes.h" void WEAK light_enable_interaction(void) {} +void WEAK light_system_color_request(void) {} +void WEAK light_system_color_release(void) {} diff --git a/tests/stubs/stubs_logging.h b/tests/stubs/stubs_logging.h index 031f7cd0fd..8f3a7754be 100644 --- a/tests/stubs/stubs_logging.h +++ b/tests/stubs/stubs_logging.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_low_power.h b/tests/stubs/stubs_low_power.h index c44013a61a..84ba329536 100644 --- a/tests/stubs/stubs_low_power.h +++ b/tests/stubs/stubs_low_power.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ void low_power_standby(void) { } diff --git a/tests/stubs/stubs_memory_layout.h b/tests/stubs/stubs_memory_layout.h index d658086c8e..0b86d089d0 100644 --- a/tests/stubs/stubs_memory_layout.h +++ b/tests/stubs/stubs_memory_layout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_menu_cell_layer.h b/tests/stubs/stubs_menu_cell_layer.h index ee5c30ddb7..fd3ddc7aae 100644 --- a/tests/stubs/stubs_menu_cell_layer.h +++ b/tests/stubs/stubs_menu_cell_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_menu_layer.h b/tests/stubs/stubs_menu_layer.h index 415f677dd1..0ae950d23f 100644 --- a/tests/stubs/stubs_menu_layer.h +++ b/tests/stubs/stubs_menu_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_mfg_info.h b/tests/stubs/stubs_mfg_info.h index fd21d8a6df..d4e4a02833 100644 --- a/tests/stubs/stubs_mfg_info.h +++ b/tests/stubs/stubs_mfg_info.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -41,7 +28,3 @@ const char* mfg_get_hw_version(void) { WatchInfoColor mfg_info_get_watch_color(void) { return WATCH_INFO_COLOR_PINK; } - -GPoint mfg_info_get_disp_offsets(void) { - return GPointZero; -} diff --git a/tests/stubs/stubs_modal_manager.c b/tests/stubs/stubs_modal_manager.c index 95c32c09aa..e6cd50e84c 100644 --- a/tests/stubs/stubs_modal_manager.c +++ b/tests/stubs/stubs_modal_manager.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_modal_manager.h" diff --git a/tests/stubs/stubs_modal_manager.h b/tests/stubs/stubs_modal_manager.h index ce7136d085..968eb76344 100644 --- a/tests/stubs/stubs_modal_manager.h +++ b/tests/stubs/stubs_modal_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_mpu.h b/tests/stubs/stubs_mpu.h index 367c7a0a93..6a77ff1a1f 100644 --- a/tests/stubs/stubs_mpu.h +++ b/tests/stubs/stubs_mpu.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_music.h b/tests/stubs/stubs_music.h index 32c359b847..20db210684 100644 --- a/tests/stubs/stubs_music.h +++ b/tests/stubs/stubs_music.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/music.h" +#include "pbl/services/music.h" #include "util/attributes.h" void WEAK music_get_now_playing(char* title, char* artist, char* album) {} diff --git a/tests/stubs/stubs_mutex.h b/tests/stubs/stubs_mutex.h index 4088769d5a..52337d4266 100644 --- a/tests/stubs/stubs_mutex.h +++ b/tests/stubs/stubs_mutex.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_new_timer.h b/tests/stubs/stubs_new_timer.h index 0c4d18b2b7..e2eefcfd03 100644 --- a/tests/stubs/stubs_new_timer.h +++ b/tests/stubs/stubs_new_timer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_nexmo.h b/tests/stubs/stubs_nexmo.h index 126b62f4c8..8eb05d2be3 100644 --- a/tests/stubs/stubs_nexmo.h +++ b/tests/stubs/stubs_nexmo.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_notif_db.h b/tests/stubs/stubs_notif_db.h index 01fcbf0146..23bd4073f1 100644 --- a/tests/stubs/stubs_notif_db.h +++ b/tests/stubs/stubs_notif_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_notification_storage.h b/tests/stubs/stubs_notification_storage.h index 919b3e47e3..35087b5d94 100644 --- a/tests/stubs/stubs_notification_storage.h +++ b/tests/stubs/stubs_notification_storage.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/notifications/notification_storage.h" +#include "pbl/services/notifications/notification_storage.h" #include "util/attributes.h" void WEAK notification_storage_init(void) { diff --git a/tests/stubs/stubs_notifications.h b/tests/stubs/stubs_notifications.h index 4af883aa56..5c537213fc 100644 --- a/tests/stubs/stubs_notifications.h +++ b/tests/stubs/stubs_notifications.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/notifications/notifications.h" +#include "pbl/services/notifications/notifications.h" void notifications_init(void) {} diff --git a/tests/stubs/stubs_passert.h b/tests/stubs/stubs_passert.h index 46e1c77539..b423d68d90 100644 --- a/tests/stubs/stubs_passert.h +++ b/tests/stubs/stubs_passert.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_pbl_malloc.h b/tests/stubs/stubs_pbl_malloc.h index cc442bea38..52c28b0f52 100644 --- a/tests/stubs/stubs_pbl_malloc.h +++ b/tests/stubs/stubs_pbl_malloc.h @@ -1,21 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include #include #include @@ -90,7 +78,17 @@ void app_free(void *ptr) { free(ptr); } +// Set to make kernel_malloc/kernel_strdup return NULL, for OOM testing. +static bool s_kernel_malloc_should_fail = false; + +void stub_pbl_malloc_set_kernel_malloc_should_fail(bool should_fail) { + s_kernel_malloc_should_fail = should_fail; +} + void *kernel_malloc(size_t bytes) { + if (s_kernel_malloc_should_fail) { + return NULL; + } return malloc(bytes); } @@ -125,6 +123,10 @@ void* kernel_calloc_check(size_t count, size_t size) { } char* kernel_strdup(const char* s) { + if (s_kernel_malloc_should_fail) { + return NULL; + } + char *r = malloc(strlen(s) + 1); if (!r) { return NULL; diff --git a/tests/stubs/stubs_pebble_pairing_service.h b/tests/stubs/stubs_pebble_pairing_service.h index 3c78e0cca4..e41852887b 100644 --- a/tests/stubs/stubs_pebble_pairing_service.h +++ b/tests/stubs/stubs_pebble_pairing_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_pebble_process_info.h b/tests/stubs/stubs_pebble_process_info.h index cca1a76f60..f54b0c3c65 100644 --- a/tests/stubs/stubs_pebble_process_info.h +++ b/tests/stubs/stubs_pebble_process_info.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_pebble_process_md.h b/tests/stubs/stubs_pebble_process_md.h index d9c530082f..f604b42bc4 100644 --- a/tests/stubs/stubs_pebble_process_md.h +++ b/tests/stubs/stubs_pebble_process_md.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_pebble_tasks.h b/tests/stubs/stubs_pebble_tasks.h index 7b535d492e..261c43ecd6 100644 --- a/tests/stubs/stubs_pebble_tasks.h +++ b/tests/stubs/stubs_pebble_tasks.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_peek_layer.h b/tests/stubs/stubs_peek_layer.h index e90142950c..88006dd4ab 100644 --- a/tests/stubs/stubs_peek_layer.h +++ b/tests/stubs/stubs_peek_layer.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "applib/graphics/gtypes.h" -#include "apps/system_apps/timeline/peek_layer.h" +#include "apps/system/timeline/peek_layer.h" #include "util/attributes.h" PeekLayer * WEAK peek_layer_create(GRect frame) { @@ -85,3 +72,6 @@ void WEAK peek_layer_set_subtitle_font(PeekLayer *peek_layer, GFont font, int16_ void WEAK peek_layer_set_dot_diameter(PeekLayer *peek_layer, uint8_t dot_diameter) {} void WEAK peek_layer_set_icon_offset_y(PeekLayer *peek_layer, int16_t icon_offset_y) {} + +void WEAK peek_layer_set_icon_with_invert(PeekLayer *peek_layer, + const TimelineResourceInfo *timeline_res, bool invert) {} diff --git a/tests/stubs/stubs_persist.h b/tests/stubs/stubs_persist.h index 8191a1aadb..cba1d9048b 100644 --- a/tests/stubs/stubs_persist.h +++ b/tests/stubs/stubs_persist.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ bool persist_exists(const uint32_t key) { diff --git a/tests/stubs/stubs_pfs.h b/tests/stubs/stubs_pfs.h index 2d534ae090..2e41bec579 100644 --- a/tests/stubs/stubs_pfs.h +++ b/tests/stubs/stubs_pfs.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ -#include "services/normal/filesystem/pfs.h" +#include "pbl/services/filesystem/pfs.h" status_t pfs_remove(const char *name) { return S_SUCCESS; diff --git a/tests/stubs/stubs_phone_call_util.h b/tests/stubs/stubs_phone_call_util.h index 453541ba97..67330a9232 100644 --- a/tests/stubs/stubs_phone_call_util.h +++ b/tests/stubs/stubs_phone_call_util.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/phone_call.h" +#include "pbl/services/phone_call.h" PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name) { return NULL; diff --git a/tests/stubs/stubs_pin_db.h b/tests/stubs/stubs_pin_db.h index aaf3f35c18..91dd46fe18 100644 --- a/tests/stubs/stubs_pin_db.h +++ b/tests/stubs/stubs_pin_db.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/pin_db.h" +#include "pbl/services/blob_db/pin_db.h" #include "util/attributes.h" bool WEAK pin_db_has_entry_expired(time_t pin_end_timestamp) { @@ -91,3 +78,7 @@ status_t WEAK pin_db_mark_synced(const uint8_t *key, int key_len) { status_t WEAK pin_db_set_status_bits(const TimelineItemId *id, uint8_t status) { return S_SUCCESS; } + +status_t WEAK pin_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_plugin_service.h b/tests/stubs/stubs_plugin_service.h index 3ab3dd69c6..a9cde6b60a 100644 --- a/tests/stubs/stubs_plugin_service.h +++ b/tests/stubs/stubs_plugin_service.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_powermode_service.h b/tests/stubs/stubs_powermode_service.h new file mode 100644 index 0000000000..73ebad0eb8 --- /dev/null +++ b/tests/stubs/stubs_powermode_service.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/powermode_service.h" + +void powermode_service_init(void) {} +void powermode_service_set_enabled(bool enabled) {} +void powermode_service_request_hp(void) {} +void powermode_service_release_hp(void) {} diff --git a/tests/stubs/stubs_prefs_db.h b/tests/stubs/stubs_prefs_db.h index 034b5ce5d6..d7171faed3 100644 --- a/tests/stubs/stubs_prefs_db.h +++ b/tests/stubs/stubs_prefs_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_print.h b/tests/stubs/stubs_print.h index e0d38ddd28..e142bf778e 100644 --- a/tests/stubs/stubs_print.h +++ b/tests/stubs/stubs_print.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ // This file used to be used for defining sniprintf. // It has been replaced with overrides/default/stdio.h diff --git a/tests/stubs/stubs_process_loader.h b/tests/stubs/stubs_process_loader.h index 721e26eaa9..5c64a9d887 100644 --- a/tests/stubs/stubs_process_loader.h +++ b/tests/stubs/stubs_process_loader.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_process_manager.h b/tests/stubs/stubs_process_manager.h index f5155a3255..f95926f186 100644 --- a/tests/stubs/stubs_process_manager.h +++ b/tests/stubs/stubs_process_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_progress_window.h b/tests/stubs/stubs_progress_window.h index 63669fecb1..685fb11f09 100644 --- a/tests/stubs/stubs_progress_window.h +++ b/tests/stubs/stubs_progress_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/progress_window.h" diff --git a/tests/stubs/stubs_prompt.h b/tests/stubs/stubs_prompt.h index 59da19407c..7e02dddc68 100644 --- a/tests/stubs/stubs_prompt.h +++ b/tests/stubs/stubs_prompt.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include diff --git a/tests/stubs/stubs_property_animation.h b/tests/stubs/stubs_property_animation.h index 478a020e94..aa41c4a8b6 100644 --- a/tests/stubs/stubs_property_animation.h +++ b/tests/stubs/stubs_property_animation.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_pstring.h b/tests/stubs/stubs_pstring.h index f2071e6ec7..2a831c5a28 100644 --- a/tests/stubs/stubs_pstring.h +++ b/tests/stubs/stubs_pstring.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_put_bytes.h b/tests/stubs/stubs_put_bytes.h index d0d56f69ab..418645a0ca 100644 --- a/tests/stubs/stubs_put_bytes.h +++ b/tests/stubs/stubs_put_bytes.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/prf_update.h" +#include "pbl/services/prf_update.h" #include "util/attributes.h" void WEAK put_bytes_cancel(void) {} diff --git a/tests/stubs/stubs_queue.h b/tests/stubs/stubs_queue.h index 35f3115b78..e6dc401e44 100644 --- a/tests/stubs/stubs_queue.h +++ b/tests/stubs/stubs_queue.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_quick_launch.h b/tests/stubs/stubs_quick_launch.h index 2837c32ed4..89ede7ca58 100644 --- a/tests/stubs/stubs_quick_launch.h +++ b/tests/stubs/stubs_quick_launch.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_rand_ptr.h b/tests/stubs/stubs_rand_ptr.h index 29f7ea1fa0..728a661e17 100644 --- a/tests/stubs/stubs_rand_ptr.h +++ b/tests/stubs/stubs_rand_ptr.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ void* app_state_get_rand_ptr(void) { return NULL; } void* worker_state_get_rand_ptr(void) { return NULL; } diff --git a/tests/stubs/stubs_reboot_reason.h b/tests/stubs/stubs_reboot_reason.h index e8e5be4671..6cf2d2174d 100644 --- a/tests/stubs/stubs_reboot_reason.h +++ b/tests/stubs/stubs_reboot_reason.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_reconnect.h b/tests/stubs/stubs_reconnect.h deleted file mode 100644 index 051c074058..0000000000 --- a/tests/stubs/stubs_reconnect.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -void bt_driver_reconnect_try_now(bool ignore_paused) { -} diff --git a/tests/stubs/stubs_regular_timer.h b/tests/stubs/stubs_regular_timer.h index 89aed1772d..12b01f6b92 100644 --- a/tests/stubs/stubs_regular_timer.h +++ b/tests/stubs/stubs_regular_timer.h @@ -1,20 +1,7 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "services/common/regular_timer.h" +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pbl/services/regular_timer.h" void regular_timer_init(void) { } diff --git a/tests/stubs/stubs_reminder_db.h b/tests/stubs/stubs_reminder_db.h index 2ddf919f08..91cfce47a7 100644 --- a/tests/stubs/stubs_reminder_db.h +++ b/tests/stubs/stubs_reminder_db.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/timeline_item_storage.h" -#include "services/normal/settings/settings_file.h" +#include "pbl/services/blob_db/timeline_item_storage.h" +#include "pbl/services/settings/settings_file.h" void reminder_db_init(void) { return; @@ -84,3 +71,7 @@ BlobDBDirtyItem* reminder_db_get_dirty_list(void) { status_t reminder_db_mark_synced(const uint8_t *key, int key_len) { return S_SUCCESS; } + +status_t reminder_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_reminders.h b/tests/stubs/stubs_reminders.h index 4e141a2d30..7b87c4929f 100644 --- a/tests/stubs/stubs_reminders.h +++ b/tests/stubs/stubs_reminders.h @@ -1,21 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "pbl/services/timeline/reminders.h" #include "system/status_codes.h" #include "util/attributes.h" diff --git a/tests/stubs/stubs_remote.h b/tests/stubs/stubs_remote.h index d9a9783b49..69804d5c64 100644 --- a/tests/stubs/stubs_remote.h +++ b/tests/stubs/stubs_remote.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_resources.h b/tests/stubs/stubs_resources.h index e64e7bfed6..013a81822f 100644 --- a/tests/stubs/stubs_resources.h +++ b/tests/stubs/stubs_resources.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "resource/resource.h" diff --git a/tests/stubs/stubs_rtc.h b/tests/stubs/stubs_rtc.h index 82ffcace84..260e027543 100644 --- a/tests/stubs/stubs_rtc.h +++ b/tests/stubs/stubs_rtc.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_scroll_layer.h b/tests/stubs/stubs_scroll_layer.h index 6447e69b67..ada352920d 100644 --- a/tests/stubs/stubs_scroll_layer.h +++ b/tests/stubs/stubs_scroll_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/scroll_layer.h" diff --git a/tests/stubs/stubs_serial.h b/tests/stubs/stubs_serial.h index c1fef43914..6d846b4fb4 100644 --- a/tests/stubs/stubs_serial.h +++ b/tests/stubs/stubs_serial.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_session.h b/tests/stubs/stubs_session.h index ba71150bac..52e8642a37 100644 --- a/tests/stubs/stubs_session.h +++ b/tests/stubs/stubs_session.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/comm_session/session.h" +#include "pbl/services/comm_session/session.h" bool comm_session_has_capability(CommSession *session, CommSessionCapability capability){ return true; diff --git a/tests/stubs/stubs_settings_blob_db.h b/tests/stubs/stubs_settings_blob_db.h new file mode 100644 index 0000000000..caffc17c24 --- /dev/null +++ b/tests/stubs/stubs_settings_blob_db.h @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/blob_db/api.h" + +void settings_blob_db_init(void) { +} + +status_t settings_blob_db_insert(const uint8_t *key, int key_len, + const uint8_t *val, int val_len) { + return S_SUCCESS; +} + +int settings_blob_db_get_len(const uint8_t *key, int key_len) { + return 0; +} + +status_t settings_blob_db_read(const uint8_t *key, int key_len, + uint8_t *val_out, int val_len) { + return E_DOES_NOT_EXIST; +} + +status_t settings_blob_db_delete(const uint8_t *key, int key_len) { + return S_SUCCESS; +} + +BlobDBDirtyItem *settings_blob_db_get_dirty_list(void) { + return NULL; +} + +status_t settings_blob_db_mark_synced(const uint8_t *key, int key_len) { + return S_SUCCESS; +} + +status_t settings_blob_db_is_dirty(bool *is_dirty_out) { + if (is_dirty_out) { + *is_dirty_out = false; + } + return S_SUCCESS; +} + +status_t settings_blob_db_flush(void) { + return S_SUCCESS; +} + +status_t settings_blob_db_mark_all_dirty(void) { + return S_SUCCESS; +} + +status_t settings_blob_db_insert_with_timestamp(const uint8_t *key, int key_len, + const uint8_t *val, int val_len, + time_t timestamp) { + return S_SUCCESS; +} + +bool settings_blob_db_phone_supports_sync(void) { + return false; +} diff --git a/tests/stubs/stubs_settings_file.h b/tests/stubs/stubs_settings_file.h index 501d2aa0b3..32cdade83a 100644 --- a/tests/stubs/stubs_settings_file.h +++ b/tests/stubs/stubs_settings_file.h @@ -1,27 +1,19 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/settings/settings_file.h" +#include "pbl/services/settings/settings_file.h" status_t settings_file_open(SettingsFile *file, const char *name, int max_used_space) { return S_SUCCESS; } +status_t settings_file_open_growable(SettingsFile *file, const char *name, + int max_used_space, int initial_alloc_size) { + return settings_file_open(file, name, max_used_space); +} + status_t settings_file_get(SettingsFile *file, const void *key, size_t key_len, void *val_out, size_t val_out_len) { return S_SUCCESS; @@ -44,4 +36,8 @@ status_t settings_file_delete(SettingsFile *file, const void *key, size_t key_le return S_SUCCESS; } +status_t settings_file_compact(SettingsFile *file) { + return S_SUCCESS; +} + void settings_file_close(SettingsFile *file) {} diff --git a/tests/stubs/stubs_shared_prf_storage.h b/tests/stubs/stubs_shared_prf_storage.h index f4304ed8a9..0275586b4b 100644 --- a/tests/stubs/stubs_shared_prf_storage.h +++ b/tests/stubs/stubs_shared_prf_storage.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/shared_prf_storage/shared_prf_storage.h" +#include "pbl/services/shared_prf_storage/shared_prf_storage.h" void shared_prf_storage_erase_ble_pairing_data(void) { } diff --git a/tests/stubs/stubs_shell_prefs.h b/tests/stubs/stubs_shell_prefs.h index b41ef97917..368d2e86e0 100644 --- a/tests/stubs/stubs_shell_prefs.h +++ b/tests/stubs/stubs_shell_prefs.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -39,6 +26,16 @@ void WEAK shell_prefs_set_timezone_source_manual(bool manual) { s_clock_timeezone_manual = manual; } +static bool s_clock_time_source_manual; + +bool WEAK shell_prefs_is_time_source_manual(void) { + return s_clock_time_source_manual; +} + +void WEAK shell_prefs_set_time_source_manual(bool manual) { + s_clock_time_source_manual = manual; +} + static int16_t s_timezone_id; int16_t shell_prefs_get_automatic_timezone_id(void) { @@ -66,3 +63,23 @@ void WEAK system_theme_set_content_size(PreferredContentSize content_size) { PreferredContentSize WEAK system_theme_get_content_size(void) { return (PreferredContentSize)s_content_size; } + +static bool s_menu_scroll_enable = false; + +bool WEAK shell_prefs_get_menu_scroll_wrap_around_enable(void) { + return s_menu_scroll_enable; +} + +void WEAK shell_prefs_set_menu_scroll_wrap_around_enable(bool enable) { + s_menu_scroll_enable = enable; +} + +static MenuScrollVibeBehavior s_menu_scroll_vibe_behavior = MenuScrollNoVibe; + +MenuScrollVibeBehavior WEAK shell_prefs_get_menu_scroll_vibe_behavior(void) { + return s_menu_scroll_vibe_behavior; +} + +void WEAK shell_prefs_set_menu_scroll_vibe_behavior(MenuScrollVibeBehavior behavior) { + s_menu_scroll_vibe_behavior = behavior; +} \ No newline at end of file diff --git a/tests/stubs/stubs_simple_dialog.h b/tests/stubs/stubs_simple_dialog.h index 45399b81c5..9653e3f69b 100644 --- a/tests/stubs/stubs_simple_dialog.h +++ b/tests/stubs/stubs_simple_dialog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_sleep.h b/tests/stubs/stubs_sleep.h index 747461af11..5f152d0bf9 100644 --- a/tests/stubs/stubs_sleep.h +++ b/tests/stubs/stubs_sleep.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include diff --git a/tests/stubs/stubs_sports_layout.h b/tests/stubs/stubs_sports_layout.h index 67ccd604c7..026237f221 100644 --- a/tests/stubs/stubs_sports_layout.h +++ b/tests/stubs/stubs_sports_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/sports_layout.h" +#include "pbl/services/timeline/sports_layout.h" #include "util/attributes.h" LayoutLayer * WEAK sports_layout_create(const LayoutLayerConfig *config) { diff --git a/tests/stubs/stubs_status_bar_layer.h b/tests/stubs/stubs_status_bar_layer.h index 67d818ea58..d61216cb39 100644 --- a/tests/stubs/stubs_status_bar_layer.h +++ b/tests/stubs/stubs_status_bar_layer.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_stop.h b/tests/stubs/stubs_stop.h index d2bfaee76a..6eb0e4af0e 100644 --- a/tests/stubs/stubs_stop.h +++ b/tests/stubs/stubs_stop.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/util/stop.h" diff --git a/tests/stubs/stubs_stringlist.h b/tests/stubs/stubs_stringlist.h index 1304737410..5829066f7b 100644 --- a/tests/stubs/stubs_stringlist.h +++ b/tests/stubs/stubs_stringlist.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_sys_exit.h b/tests/stubs/stubs_sys_exit.h index 7a9d7bd5a0..cb039039b2 100644 --- a/tests/stubs/stubs_sys_exit.h +++ b/tests/stubs/stubs_sys_exit.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include diff --git a/tests/stubs/stubs_syscall_internal.h b/tests/stubs/stubs_syscall_internal.h index b3f6a6fd00..8eb9bc07f8 100644 --- a/tests/stubs/stubs_syscall_internal.h +++ b/tests/stubs/stubs_syscall_internal.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_syscalls.h b/tests/stubs/stubs_syscalls.h index eced0b3f7d..ea14ef7a02 100644 --- a/tests/stubs/stubs_syscalls.h +++ b/tests/stubs/stubs_syscalls.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_system_reset.h b/tests/stubs/stubs_system_reset.h index b50a45414e..b667ecb485 100644 --- a/tests/stubs/stubs_system_reset.h +++ b/tests/stubs/stubs_system_reset.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,7 +7,7 @@ #include "stubs_passert.h" -void system_reset_prepare(bool skip_bt_teardown) {} +void system_reset_prepare(void) {} void system_reset(void) { PBL_ASSERT(false, "System reset triggered!"); diff --git a/tests/stubs/stubs_system_task.h b/tests/stubs/stubs_system_task.h index f1f038aa44..ab6161d0c2 100644 --- a/tests/stubs/stubs_system_task.h +++ b/tests/stubs/stubs_system_task.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include -#include "services/common/system_task.h" +#include "pbl/services/system_task.h" bool system_task_add_callback(SystemTaskEventCallback cb, void *data) { cb(data); diff --git a/tests/stubs/stubs_system_theme.c b/tests/stubs/stubs_system_theme.c index 211967ca6e..b7948af16d 100644 --- a/tests/stubs/stubs_system_theme.c +++ b/tests/stubs/stubs_system_theme.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_system_theme.h" diff --git a/tests/stubs/stubs_system_theme.h b/tests/stubs/stubs_system_theme.h index b167bdb649..4283c414a9 100644 --- a/tests/stubs/stubs_system_theme.h +++ b/tests/stubs/stubs_system_theme.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_task.h b/tests/stubs/stubs_task.h index 3996aa5bea..cf7ccf5689 100644 --- a/tests/stubs/stubs_task.h +++ b/tests/stubs/stubs_task.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "freertos_types.h" #include "projdefs.h" diff --git a/tests/stubs/stubs_task_watchdog.h b/tests/stubs/stubs_task_watchdog.h index 9185a84076..9a93ef7a59 100644 --- a/tests/stubs/stubs_task_watchdog.h +++ b/tests/stubs/stubs_task_watchdog.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "kernel/pebble_tasks.h" @@ -31,3 +18,9 @@ void task_watchdog_feed(void) { void task_watchdog_bit_set(PebbleTask task) { } + +void task_watchdog_pause(unsigned int seconds) { +} + +void task_watchdog_resume(void) { +} diff --git a/tests/stubs/stubs_text_layer_flow.h b/tests/stubs/stubs_text_layer_flow.h index f9c8eac456..8b340421d4 100644 --- a/tests/stubs/stubs_text_layer_flow.h +++ b/tests/stubs/stubs_text_layer_flow.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "applib/ui/text_layer.h" diff --git a/tests/stubs/stubs_text_layout.c b/tests/stubs/stubs_text_layout.c index 771b068fe7..0d8fb82535 100644 --- a/tests/stubs/stubs_text_layout.c +++ b/tests/stubs/stubs_text_layout.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_text_layout.h" diff --git a/tests/stubs/stubs_text_layout.h b/tests/stubs/stubs_text_layout.h index 880809eafd..2409fb1d30 100644 --- a/tests/stubs/stubs_text_layout.h +++ b/tests/stubs/stubs_text_layout.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_text_node.h b/tests/stubs/stubs_text_node.h index 8eab58e1ce..7068737a11 100644 --- a/tests/stubs/stubs_text_node.h +++ b/tests/stubs/stubs_text_node.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "apps/system_apps/timeline/text_node.h" +#include "apps/system/timeline/text_node.h" void graphics_text_node_destroy(GTextNode *node) { } @@ -31,3 +18,5 @@ bool graphics_text_node_container_add_child(GTextNodeContainer *parent, GTextNod } GTextNodeText *graphics_text_node_create_text(size_t buffer_size) { return NULL; } + +GTextNodeHorizontal *graphics_text_node_create_horizontal(size_t max_nodes) { return NULL; } diff --git a/tests/stubs/stubs_text_render.h b/tests/stubs/stubs_text_render.h index 24f45ed510..4bd62e69f4 100644 --- a/tests/stubs/stubs_text_render.h +++ b/tests/stubs/stubs_text_render.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_text_resources.h b/tests/stubs/stubs_text_resources.h index ebf846d07f..a3b6442235 100644 --- a/tests/stubs/stubs_text_resources.h +++ b/tests/stubs/stubs_text_resources.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -44,3 +31,7 @@ int8_t text_resources_get_glyph_height(FontCache* font_cache, Codepoint codepoin return 10; } +const GlyphData *text_resources_get_glyph(FontCache* font_cache, Codepoint codepoint, FontInfo* fontinfo) { + return NULL; +} + diff --git a/tests/stubs/stubs_tick.h b/tests/stubs/stubs_tick.h index 2644597807..f7c54da63e 100644 --- a/tests/stubs/stubs_tick.h +++ b/tests/stubs/stubs_tick.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "FreeRTOS.h" #include "portmacro.h" diff --git a/tests/stubs/stubs_time.h b/tests/stubs/stubs_time.h index d09c39686b..fb0714e32d 100644 --- a/tests/stubs/stubs_time.h +++ b/tests/stubs/stubs_time.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_timeline.h b/tests/stubs/stubs_timeline.h index 83be75fcd9..febcf0cd67 100644 --- a/tests/stubs/stubs_timeline.h +++ b/tests/stubs/stubs_timeline.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/item.h" -#include "apps/system_apps/timeline/timeline.h" +#include "pbl/services/timeline/item.h" +#include "apps/system/timeline/timeline.h" #include "util/attributes.h" void WEAK timeline_invoke_action(const TimelineItem *item, const TimelineItemAction *action, diff --git a/tests/stubs/stubs_timeline_actions.h b/tests/stubs/stubs_timeline_actions.h index 4e64c1ec92..2644539c46 100644 --- a/tests/stubs/stubs_timeline_actions.h +++ b/tests/stubs/stubs_timeline_actions.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/item.h" -#include "services/normal/timeline/timeline_actions.h" +#include "pbl/services/timeline/item.h" +#include "pbl/services/timeline/timeline_actions.h" #include "util/attributes.h" void WEAK timeline_actions_add_action_to_root_level(TimelineItemAction *action, diff --git a/tests/stubs/stubs_timeline_event.h b/tests/stubs/stubs_timeline_event.h index 3ff10c1076..66e9008c99 100644 --- a/tests/stubs/stubs_timeline_event.h +++ b/tests/stubs/stubs_timeline_event.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/event.h" +#include "pbl/services/timeline/event.h" #include "util/attributes.h" void WEAK timeline_event_refresh(void) {} diff --git a/tests/stubs/stubs_timeline_item.h b/tests/stubs/stubs_timeline_item.h index 837021fa97..bab3bb0d29 100644 --- a/tests/stubs/stubs_timeline_item.h +++ b/tests/stubs/stubs_timeline_item.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/item.h" +#include "pbl/services/timeline/item.h" void WEAK timeline_item_destroy(TimelineItem* item) {} diff --git a/tests/stubs/stubs_timeline_layer.h b/tests/stubs/stubs_timeline_layer.h index cb1787f8a8..d5189391d6 100644 --- a/tests/stubs/stubs_timeline_layer.h +++ b/tests/stubs/stubs_timeline_layer.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "apps/system_apps/timeline/timeline_layer.h" +#include "apps/system/timeline/layer.h" #include "util/attributes.h" uint16_t WEAK timeline_layer_get_ideal_sidebar_width(void) { diff --git a/tests/stubs/stubs_timeline_layout.c b/tests/stubs/stubs_timeline_layout.c index 61fa1e1b0e..5c7c292e69 100644 --- a/tests/stubs/stubs_timeline_layout.c +++ b/tests/stubs/stubs_timeline_layout.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_timeline_layout.h" diff --git a/tests/stubs/stubs_timeline_layout.h b/tests/stubs/stubs_timeline_layout.h index 1171cbecb3..1997f5cc21 100644 --- a/tests/stubs/stubs_timeline_layout.h +++ b/tests/stubs/stubs_timeline_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/timeline_layout.h" +#include "pbl/services/timeline/timeline_layout.h" #include "util/attributes.h" void WEAK timeline_layout_init(TimelineLayout *layout, const LayoutLayerConfig *config, diff --git a/tests/stubs/stubs_timeline_layout_animations.h b/tests/stubs/stubs_timeline_layout_animations.h index 522a2995e1..a8a6c50467 100644 --- a/tests/stubs/stubs_timeline_layout_animations.h +++ b/tests/stubs/stubs_timeline_layout_animations.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/timeline_layout_animations.h" +#include "pbl/services/timeline/timeline_layout_animations.h" #include "util/attributes.h" void WEAK timeline_layout_transition_pin_to_card(TimelineLayout *pin_timeline_layout, diff --git a/tests/stubs/stubs_timeline_peek.c b/tests/stubs/stubs_timeline_peek.c index 5b07c7e17b..17b7c82da0 100644 --- a/tests/stubs/stubs_timeline_peek.c +++ b/tests/stubs/stubs_timeline_peek.c @@ -1,17 +1,4 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "stubs_timeline_peek.h" diff --git a/tests/stubs/stubs_timeline_peek.h b/tests/stubs/stubs_timeline_peek.h index 4389941634..229f87b316 100644 --- a/tests/stubs/stubs_timeline_peek.h +++ b/tests/stubs/stubs_timeline_peek.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "popups/timeline/peek.h" -#include "services/normal/timeline/peek.h" +#include "pbl/services/timeline/peek.h" #include "util/attributes.h" unsigned int WEAK timeline_peek_get_concurrent_height(unsigned int num_concurrent) { @@ -38,6 +25,8 @@ void WEAK timeline_peek_handle_process_kill(void) {} void WEAK timeline_peek_set_show_before_time(unsigned int before_time_s) {}; +void WEAK timeline_peek_set_enabled(bool enabled) {} + bool WEAK timeline_peek_prefs_get_enabled() { return true; } diff --git a/tests/stubs/stubs_timeline_pin_window.h b/tests/stubs/stubs_timeline_pin_window.h index 232e933378..ea8853d09c 100644 --- a/tests/stubs/stubs_timeline_pin_window.h +++ b/tests/stubs/stubs_timeline_pin_window.h @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "apps/system_apps/timeline/pin_window.h" +#include "apps/system/timeline/pin_window.h" void timeline_pin_window_push_modal(TimelineItem *item) { } diff --git a/tests/stubs/stubs_timeline_resources.h b/tests/stubs/stubs_timeline_resources.h index 62c0e407ca..c9bd2a0964 100644 --- a/tests/stubs/stubs_timeline_resources.h +++ b/tests/stubs/stubs_timeline_resources.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" bool timeline_resources_get_id_system(TimelineResourceId timeline_id, TimelineResourceSize size, ResAppNum res_app_num, AppResourceInfo *res_info_out) { diff --git a/tests/stubs/stubs_timezone_database.h b/tests/stubs/stubs_timezone_database.h index ac5409058a..969797578e 100644 --- a/tests/stubs/stubs_timezone_database.h +++ b/tests/stubs/stubs_timezone_database.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timezone_database.h" +#include "pbl/services/timezone_database.h" #include "util/attributes.h" int WEAK timezone_database_get_region_count(void) { diff --git a/tests/stubs/stubs_ui_window.h b/tests/stubs/stubs_ui_window.h index f1d459a090..ad0895ad2a 100644 --- a/tests/stubs/stubs_ui_window.h +++ b/tests/stubs/stubs_ui_window.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_unobstructed_area.h b/tests/stubs/stubs_unobstructed_area.h index 02d7284aa3..b9f96cde3f 100644 --- a/tests/stubs/stubs_unobstructed_area.h +++ b/tests/stubs/stubs_unobstructed_area.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -21,6 +8,10 @@ void WEAK unobstructed_area_service_get_area(UnobstructedAreaState *state, GRect *area) { } +bool WEAK unobstructed_area_service_has_requested_area(UnobstructedAreaState *state) { + return false; +} + void WEAK unobstructed_area_service_will_change(int16_t current_y, int16_t final_y) { } void WEAK unobstructed_area_service_change(int16_t current_y, int16_t final_y, diff --git a/tests/stubs/stubs_utf8.h b/tests/stubs/stubs_utf8.h index 49f24eb854..7c8db71669 100644 --- a/tests/stubs/stubs_utf8.h +++ b/tests/stubs/stubs_utf8.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_uuid.h b/tests/stubs/stubs_uuid.h index 80738c9543..c528d4260f 100644 --- a/tests/stubs/stubs_uuid.h +++ b/tests/stubs/stubs_uuid.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_version.h b/tests/stubs/stubs_version.h index 4c4b0a848b..739b64618a 100644 --- a/tests/stubs/stubs_version.h +++ b/tests/stubs/stubs_version.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once #include "system/version.h" diff --git a/tests/stubs/stubs_vibe_client.h b/tests/stubs/stubs_vibe_client.h new file mode 100644 index 0000000000..f6e3c31c43 --- /dev/null +++ b/tests/stubs/stubs_vibe_client.h @@ -0,0 +1,10 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/vibes/vibe_client.h" + +VibeScore *vibe_client_get_score(VibeClient client) { + return NULL; +} diff --git a/tests/stubs/stubs_vibe_intensity.h b/tests/stubs/stubs_vibe_intensity.h index e98eb18508..ef457384a1 100644 --- a/tests/stubs/stubs_vibe_intensity.h +++ b/tests/stubs/stubs_vibe_intensity.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_vibe_pattern.h b/tests/stubs/stubs_vibe_pattern.h index bf4be6e788..ac262fb5da 100644 --- a/tests/stubs/stubs_vibe_pattern.h +++ b/tests/stubs/stubs_vibe_pattern.h @@ -1,21 +1,8 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/common/vibe_pattern.h" +#include "pbl/services/vibe_pattern.h" void vibes_set_default_vibe_strength(int32_t vibe_strength_default) {} diff --git a/tests/stubs/stubs_vibe_score.h b/tests/stubs/stubs_vibe_score.h new file mode 100644 index 0000000000..b243fcac38 --- /dev/null +++ b/tests/stubs/stubs_vibe_score.h @@ -0,0 +1,14 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#include "pbl/services/vibes/vibe_score.h" + +VibeScore *vibe_score_create_with_resource_system(ResAppNum app_num, uint32_t resource_id) { + return NULL; +} + +void vibe_score_do_vibe(VibeScore *score) {} + +void vibe_score_destroy(VibeScore *score) {} diff --git a/tests/stubs/stubs_vibe_score_info.h b/tests/stubs/stubs_vibe_score_info.h index 3e2c548eb8..f16b876836 100644 --- a/tests/stubs/stubs_vibe_score_info.h +++ b/tests/stubs/stubs_vibe_score_info.h @@ -1,23 +1,18 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/vibes/vibe_score_info.h" +#include "pbl/services/vibes/vibe_score_info.h" bool vibe_score_info_is_valid(VibeScoreId id) { return true; } + +const char *vibe_score_info_get_name(VibeScoreId id) { + return "test"; +} + +uint32_t vibe_score_info_get_resource_id(VibeScoreId id) { + return 0; +} diff --git a/tests/stubs/stubs_vibes.h b/tests/stubs/stubs_vibes.h index e5b7f79a5d..bb72dddc72 100644 --- a/tests/stubs/stubs_vibes.h +++ b/tests/stubs/stubs_vibes.h @@ -1,21 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "applib/ui/vibes.h" #include "util/attributes.h" void WEAK vibes_long_pulse(void) {} @@ -25,3 +13,5 @@ void WEAK vibes_short_pulse(void) {} void WEAK vibes_double_pulse(void) {} void WEAK vibes_cancel(void) {} + +void WEAK vibes_enqueue_custom_pattern(VibePattern pattern) {} diff --git a/tests/stubs/stubs_wakeup.h b/tests/stubs/stubs_wakeup.h index 4f70384474..6470a9bbf8 100644 --- a/tests/stubs/stubs_wakeup.h +++ b/tests/stubs/stubs_wakeup.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/wakeup.h" +#include "pbl/services/wakeup.h" #include "util/attributes.h" void wakeup_migrate_timezone(int utc_diff) {} diff --git a/tests/stubs/stubs_watch_app_prefs_db.h b/tests/stubs/stubs_watch_app_prefs_db.h index a17c017eb3..4c584cf86c 100644 --- a/tests/stubs/stubs_watch_app_prefs_db.h +++ b/tests/stubs/stubs_watch_app_prefs_db.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/blob_db/watch_app_prefs_db.h" +#include "pbl/services/blob_db/watch_app_prefs_db.h" SerializedSendTextPrefs *watch_app_prefs_get_send_text(void); @@ -48,3 +35,7 @@ status_t watch_app_prefs_db_delete(const uint8_t *key, int key_len) { status_t watch_app_prefs_db_flush(void) { return S_SUCCESS; } + +status_t watch_app_prefs_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_watchface.h b/tests/stubs/stubs_watchface.h index 9a607737ff..eefdc7523b 100644 --- a/tests/stubs/stubs_watchface.h +++ b/tests/stubs/stubs_watchface.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_weather_db.h b/tests/stubs/stubs_weather_db.h index 0997dbbef9..60f91c9ab2 100644 --- a/tests/stubs/stubs_weather_db.h +++ b/tests/stubs/stubs_weather_db.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -39,3 +26,7 @@ status_t weather_db_delete(const uint8_t *key, int key_len) { status_t weather_db_flush(void) { return S_SUCCESS; } + +status_t weather_db_compact(void) { + return S_SUCCESS; +} diff --git a/tests/stubs/stubs_weather_layout.h b/tests/stubs/stubs_weather_layout.h index 6378d9ac72..76c6104bf2 100644 --- a/tests/stubs/stubs_weather_layout.h +++ b/tests/stubs/stubs_weather_layout.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/weather_layout.h" +#include "pbl/services/timeline/weather_layout.h" #include "util/attributes.h" LayoutLayer * WEAK weather_layout_create(const LayoutLayerConfig *config) { diff --git a/tests/stubs/stubs_weather_service.h b/tests/stubs/stubs_weather_service.h index 9bf1237696..595bf0b38e 100644 --- a/tests/stubs/stubs_weather_service.h +++ b/tests/stubs/stubs_weather_service.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/weather/weather_service.h" +#include "pbl/services/weather/weather_service.h" #include "util/attributes.h" WeatherLocationForecast * WEAK weather_service_create_default_forecast(void) { diff --git a/tests/stubs/stubs_weather_types.h b/tests/stubs/stubs_weather_types.h index d4753091f2..c3e9de1290 100644 --- a/tests/stubs/stubs_weather_types.h +++ b/tests/stubs/stubs_weather_types.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/timeline/timeline_resources.h" +#include "pbl/services/timeline/timeline_resources.h" #include "util/attributes.h" TimelineResourceId WEAK weather_type_get_timeline_resource_id(WeatherType weather_type) { diff --git a/tests/stubs/stubs_window_manager.h b/tests/stubs/stubs_window_manager.h index 5726356e9f..5a0ecf59e4 100644 --- a/tests/stubs/stubs_window_manager.h +++ b/tests/stubs/stubs_window_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_window_stack.h b/tests/stubs/stubs_window_stack.h index b2f8961489..e8a0486f9c 100644 --- a/tests/stubs/stubs_window_stack.h +++ b/tests/stubs/stubs_window_stack.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_worker_manager.h b/tests/stubs/stubs_worker_manager.h index 4b85eda4af..fc1f10b64e 100644 --- a/tests/stubs/stubs_worker_manager.h +++ b/tests/stubs/stubs_worker_manager.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_worker_state.h b/tests/stubs/stubs_worker_state.h index f0ec963904..425d3aae57 100644 --- a/tests/stubs/stubs_worker_state.h +++ b/tests/stubs/stubs_worker_state.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/stubs/stubs_workout_service.h b/tests/stubs/stubs_workout_service.h index be5d54aa65..99f8618f1b 100644 --- a/tests/stubs/stubs_workout_service.h +++ b/tests/stubs/stubs_workout_service.h @@ -1,23 +1,10 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/activity/activity.h" -#include "services/normal/activity/hr_util.h" +#include "pbl/services/activity/activity.h" +#include "pbl/services/activity/hr_util.h" #include "kernel/events.h" diff --git a/tests/stubs/stubs_workout_utils.h b/tests/stubs/stubs_workout_utils.h index 5c505b0d2c..bdad31756b 100644 --- a/tests/stubs/stubs_workout_utils.h +++ b/tests/stubs/stubs_workout_utils.h @@ -1,22 +1,9 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "services/normal/activity/activity.h" +#include "pbl/services/activity/activity.h" bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out) { return false; diff --git a/tests/test_images/blendtest_0_100.png b/tests/test_images/blendtest_0_100.png index 5e6feeb886..e610777394 100644 Binary files a/tests/test_images/blendtest_0_100.png and b/tests/test_images/blendtest_0_100.png differ diff --git a/tests/test_images/blendtest_0_100_backdrop.png b/tests/test_images/blendtest_0_100_backdrop.png index 0caec25ad0..871a6d8126 100644 Binary files a/tests/test_images/blendtest_0_100_backdrop.png and b/tests/test_images/blendtest_0_100_backdrop.png differ diff --git a/tests/test_images/blendtest_33_66.png b/tests/test_images/blendtest_33_66.png index d08326fc78..5b718c36c1 100644 Binary files a/tests/test_images/blendtest_33_66.png and b/tests/test_images/blendtest_33_66.png differ diff --git a/tests/test_images/draw_core_assign_horizontal_line_delta_raw~obelix.png b/tests/test_images/draw_core_assign_horizontal_line_delta_raw~obelix.png new file mode 100644 index 0000000000..3acf617e92 Binary files /dev/null and b/tests/test_images/draw_core_assign_horizontal_line_delta_raw~obelix.png differ diff --git a/tests/test_images/draw_core_assign_horizontal_line_delta_raw~snowy.png b/tests/test_images/draw_core_assign_horizontal_line_delta_raw~snowy.png deleted file mode 100644 index 50094294db..0000000000 Binary files a/tests/test_images/draw_core_assign_horizontal_line_delta_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_core_assign_horizontal_line_raw~obelix.png b/tests/test_images/draw_core_assign_horizontal_line_raw~obelix.png new file mode 100644 index 0000000000..2a27e36949 Binary files /dev/null and b/tests/test_images/draw_core_assign_horizontal_line_raw~obelix.png differ diff --git a/tests/test_images/draw_core_assign_horizontal_line_raw~snowy.png b/tests/test_images/draw_core_assign_horizontal_line_raw~snowy.png deleted file mode 100644 index 17b97169f7..0000000000 Binary files a/tests/test_images/draw_core_assign_horizontal_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_core_assign_vertical_line_raw~obelix.png b/tests/test_images/draw_core_assign_vertical_line_raw~obelix.png new file mode 100644 index 0000000000..f7c0c0ff0f Binary files /dev/null and b/tests/test_images/draw_core_assign_vertical_line_raw~obelix.png differ diff --git a/tests/test_images/draw_core_assign_vertical_line_raw~snowy.png b/tests/test_images/draw_core_assign_vertical_line_raw~snowy.png deleted file mode 100644 index f0d34a3da6..0000000000 Binary files a/tests/test_images/draw_core_assign_vertical_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_core_blend_horizontal_line_raw~obelix.png b/tests/test_images/draw_core_blend_horizontal_line_raw~obelix.png new file mode 100644 index 0000000000..3cca569cc4 Binary files /dev/null and b/tests/test_images/draw_core_blend_horizontal_line_raw~obelix.png differ diff --git a/tests/test_images/draw_core_blend_horizontal_line_raw~snowy.png b/tests/test_images/draw_core_blend_horizontal_line_raw~snowy.png deleted file mode 100644 index fa8937b4e2..0000000000 Binary files a/tests/test_images/draw_core_blend_horizontal_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_core_blend_vertical_line_raw~obelix.png b/tests/test_images/draw_core_blend_vertical_line_raw~obelix.png new file mode 100644 index 0000000000..892dba6244 Binary files /dev/null and b/tests/test_images/draw_core_blend_vertical_line_raw~obelix.png differ diff --git a/tests/test_images/draw_core_blend_vertical_line_raw~snowy.png b/tests/test_images/draw_core_blend_vertical_line_raw~snowy.png deleted file mode 100644 index 5ca67b0ffc..0000000000 Binary files a/tests/test_images/draw_core_blend_vertical_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_core_plot_pixel~obelix.png b/tests/test_images/draw_core_plot_pixel~obelix.png new file mode 100644 index 0000000000..7a4dc7c7de Binary files /dev/null and b/tests/test_images/draw_core_plot_pixel~obelix.png differ diff --git a/tests/test_images/draw_core_plot_pixel~snowy.png b/tests/test_images/draw_core_plot_pixel~snowy.png deleted file mode 100644 index 1f6aff301a..0000000000 Binary files a/tests/test_images/draw_core_plot_pixel~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_core_set_pixel_raw_8bit_replicate_column_row_raw~obelix.png b/tests/test_images/draw_core_set_pixel_raw_8bit_replicate_column_row_raw~obelix.png new file mode 100644 index 0000000000..d8bea09529 Binary files /dev/null and b/tests/test_images/draw_core_set_pixel_raw_8bit_replicate_column_row_raw~obelix.png differ diff --git a/tests/test_images/draw_core_set_pixel_raw_8bit_replicate_column_row_raw~snowy.png b/tests/test_images/draw_core_set_pixel_raw_8bit_replicate_column_row_raw~snowy.png deleted file mode 100644 index 8b6d53364b..0000000000 Binary files a/tests/test_images/draw_core_set_pixel_raw_8bit_replicate_column_row_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_dotted_line_cross_color~snowy.png b/tests/test_images/draw_dotted_line_cross_color~snowy.png deleted file mode 100644 index 2bba85896f..0000000000 Binary files a/tests/test_images/draw_dotted_line_cross_color~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_dotted_line_cross~snowy.png b/tests/test_images/draw_dotted_line_cross~asterix.png similarity index 100% rename from tests/test_images/draw_dotted_line_cross~snowy.png rename to tests/test_images/draw_dotted_line_cross~asterix.png diff --git a/tests/test_images/draw_dotted_line_cross~tintin.png b/tests/test_images/draw_dotted_line_cross~tintin.png deleted file mode 100644 index 48b8cd5a30..0000000000 Binary files a/tests/test_images/draw_dotted_line_cross~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_checkerboard_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_even_offset_checkerboard_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_checkerboard_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_even_offset_checkerboard_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_even_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_even_offset_even_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_even_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_even_offset_even_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~tintin.png deleted file mode 100644 index 6cdc12a537..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_even_offset_even_rows_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~tintin.png deleted file mode 100644 index 09b4c0697d..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_even_offset_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_odd_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_even_offset_odd_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_odd_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_even_offset_odd_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~tintin.png deleted file mode 100644 index ccb2a8803c..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_even_offset_odd_rows_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_checkerboard_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_checkerboard_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_checkerboard_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_odd_offset_checkerboard_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_even_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_even_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_even_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_odd_offset_even_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~tintin.png deleted file mode 100644 index 953a2b49fa..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_odd_offset_even_rows_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~tintin.png deleted file mode 100644 index 0ae0909640..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_odd_offset_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_odd_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_odd_offset_odd_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~tintin.png deleted file mode 100644 index 4dd082023d..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_rows_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_origin_checkerboard_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_origin_checkerboard_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_checkerboard_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_origin_checkerboard_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_origin_even_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_origin_even_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_even_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_origin_even_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~tintin.png deleted file mode 100644 index ef668c0651..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_origin_even_rows_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_origin_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_origin_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_origin_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_origin_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_origin_no_clip~tintin.png deleted file mode 100644 index 57a579a01b..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_origin_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_origin_odd_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_origin_odd_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_odd_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_origin_odd_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~snowy.png b/tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~snowy.png rename to tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~asterix.png diff --git a/tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~tintin.png b/tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~tintin.png deleted file mode 100644 index 7a68dafec1..0000000000 Binary files a/tests/test_images/draw_horiz_dotted_line_origin_odd_rows_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_nx_offset_layer~snowy.png b/tests/test_images/draw_line_across_nx_offset_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_nx_offset_layer~snowy.png rename to tests/test_images/draw_line_across_nx_offset_layer~asterix.png diff --git a/tests/test_images/draw_line_across_nx_offset_layer~tintin.png b/tests/test_images/draw_line_across_nx_offset_layer~tintin.png deleted file mode 100644 index 30580c1abf..0000000000 Binary files a/tests/test_images/draw_line_across_nx_offset_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_nx_origin_layer~snowy.png b/tests/test_images/draw_line_across_nx_origin_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_nx_origin_layer~snowy.png rename to tests/test_images/draw_line_across_nx_origin_layer~asterix.png diff --git a/tests/test_images/draw_line_across_nx_origin_layer~tintin.png b/tests/test_images/draw_line_across_nx_origin_layer~tintin.png deleted file mode 100644 index 086e726129..0000000000 Binary files a/tests/test_images/draw_line_across_nx_origin_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_ny_offset_layer~snowy.png b/tests/test_images/draw_line_across_ny_offset_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_ny_offset_layer~snowy.png rename to tests/test_images/draw_line_across_ny_offset_layer~asterix.png diff --git a/tests/test_images/draw_line_across_ny_offset_layer~tintin.png b/tests/test_images/draw_line_across_ny_offset_layer~tintin.png deleted file mode 100644 index 286350cfd5..0000000000 Binary files a/tests/test_images/draw_line_across_ny_offset_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_ny_origin_layer~snowy.png b/tests/test_images/draw_line_across_ny_origin_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_ny_origin_layer~snowy.png rename to tests/test_images/draw_line_across_ny_origin_layer~asterix.png diff --git a/tests/test_images/draw_line_across_ny_origin_layer~tintin.png b/tests/test_images/draw_line_across_ny_origin_layer~tintin.png deleted file mode 100644 index 3ae4f0d30a..0000000000 Binary files a/tests/test_images/draw_line_across_ny_origin_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_x_offset_layer~snowy.png b/tests/test_images/draw_line_across_x_offset_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_x_offset_layer~snowy.png rename to tests/test_images/draw_line_across_x_offset_layer~asterix.png diff --git a/tests/test_images/draw_line_across_x_offset_layer~tintin.png b/tests/test_images/draw_line_across_x_offset_layer~tintin.png deleted file mode 100644 index cd6f3a5d06..0000000000 Binary files a/tests/test_images/draw_line_across_x_offset_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_x_origin_layer~snowy.png b/tests/test_images/draw_line_across_x_origin_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_x_origin_layer~snowy.png rename to tests/test_images/draw_line_across_x_origin_layer~asterix.png diff --git a/tests/test_images/draw_line_across_x_origin_layer~tintin.png b/tests/test_images/draw_line_across_x_origin_layer~tintin.png deleted file mode 100644 index 47c0c7bdd0..0000000000 Binary files a/tests/test_images/draw_line_across_x_origin_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_y_offset_layer~snowy.png b/tests/test_images/draw_line_across_y_offset_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_y_offset_layer~snowy.png rename to tests/test_images/draw_line_across_y_offset_layer~asterix.png diff --git a/tests/test_images/draw_line_across_y_offset_layer~tintin.png b/tests/test_images/draw_line_across_y_offset_layer~tintin.png deleted file mode 100644 index 4372ecfb70..0000000000 Binary files a/tests/test_images/draw_line_across_y_offset_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_across_y_origin_layer~snowy.png b/tests/test_images/draw_line_across_y_origin_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_across_y_origin_layer~snowy.png rename to tests/test_images/draw_line_across_y_origin_layer~asterix.png diff --git a/tests/test_images/draw_line_across_y_origin_layer~tintin.png b/tests/test_images/draw_line_across_y_origin_layer~tintin.png deleted file mode 100644 index 239277dabd..0000000000 Binary files a/tests/test_images/draw_line_across_y_origin_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_clip_rect_aa~tintin.png b/tests/test_images/draw_line_clip_rect_aa~asterix.png similarity index 100% rename from tests/test_images/draw_line_clip_rect_aa~tintin.png rename to tests/test_images/draw_line_clip_rect_aa~asterix.png diff --git a/tests/test_images/draw_line_clip_rect_aa~snowy.png b/tests/test_images/draw_line_clip_rect_aa~snowy.png deleted file mode 100644 index 14a3f6c789..0000000000 Binary files a/tests/test_images/draw_line_clip_rect_aa~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_line_clip_rect~snowy.png b/tests/test_images/draw_line_clip_rect~asterix.png similarity index 100% rename from tests/test_images/draw_line_clip_rect~snowy.png rename to tests/test_images/draw_line_clip_rect~asterix.png diff --git a/tests/test_images/draw_line_clip_rect~tintin.png b/tests/test_images/draw_line_clip_rect~tintin.png deleted file mode 100644 index 8a193703d5..0000000000 Binary files a/tests/test_images/draw_line_clip_rect~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_inside_offset_layer~snowy.png b/tests/test_images/draw_line_inside_offset_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_inside_offset_layer~snowy.png rename to tests/test_images/draw_line_inside_offset_layer~asterix.png diff --git a/tests/test_images/draw_line_inside_offset_layer~tintin.png b/tests/test_images/draw_line_inside_offset_layer~tintin.png deleted file mode 100644 index bd5a358ae4..0000000000 Binary files a/tests/test_images/draw_line_inside_offset_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_clear~snowy.png b/tests/test_images/draw_line_inside_origin_layer~asterix.png similarity index 100% rename from tests/test_images/draw_line_clear~snowy.png rename to tests/test_images/draw_line_inside_origin_layer~asterix.png diff --git a/tests/test_images/draw_line_inside_origin_layer~snowy.png b/tests/test_images/draw_line_inside_origin_layer~snowy.png deleted file mode 100644 index 6b2c048ed5..0000000000 Binary files a/tests/test_images/draw_line_inside_origin_layer~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_line_inside_origin_layer~tintin.png b/tests/test_images/draw_line_inside_origin_layer~tintin.png deleted file mode 100644 index 6b2c048ed5..0000000000 Binary files a/tests/test_images/draw_line_inside_origin_layer~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_line_same_point_color~snowy.png b/tests/test_images/draw_line_same_point_color~snowy.png deleted file mode 100644 index def957e69f..0000000000 Binary files a/tests/test_images/draw_line_same_point_color~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_line_same_point~tintin.png b/tests/test_images/draw_line_same_point~asterix.png similarity index 100% rename from tests/test_images/draw_line_same_point~tintin.png rename to tests/test_images/draw_line_same_point~asterix.png diff --git a/tests/test_images/draw_line_same_point~snowy.png b/tests/test_images/draw_line_same_point~snowy.png deleted file mode 100644 index e67edbc329..0000000000 Binary files a/tests/test_images/draw_line_same_point~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_pixel_clear.8bit.png b/tests/test_images/draw_pixel_clear.8bit.png index cd9b49abb7..dee1a55279 100644 Binary files a/tests/test_images/draw_pixel_clear.8bit.png and b/tests/test_images/draw_pixel_clear.8bit.png differ diff --git a/tests/test_images/draw_pixel_clip_rect.8bit.png b/tests/test_images/draw_pixel_clip_rect.8bit.png new file mode 100644 index 0000000000..e4a22a6b8b Binary files /dev/null and b/tests/test_images/draw_pixel_clip_rect.8bit.png differ diff --git a/tests/test_images/draw_pixel_inside_offset_layer.8bit.png b/tests/test_images/draw_pixel_inside_offset_layer.8bit.png new file mode 100644 index 0000000000..b1144d91ad Binary files /dev/null and b/tests/test_images/draw_pixel_inside_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_pixel_inside_origin_layer.8bit.png b/tests/test_images/draw_pixel_inside_origin_layer.8bit.png new file mode 100644 index 0000000000..dee1a55279 Binary files /dev/null and b/tests/test_images/draw_pixel_inside_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_pixel_transparent.8bit.png b/tests/test_images/draw_pixel_transparent.8bit.png index 09c465a14d..9f5b5d93b8 100644 Binary files a/tests/test_images/draw_pixel_transparent.8bit.png and b/tests/test_images/draw_pixel_transparent.8bit.png differ diff --git a/tests/test_images/draw_rect_across_nx_offset_layer.8bit.png b/tests/test_images/draw_rect_across_nx_offset_layer.8bit.png new file mode 100644 index 0000000000..ff2fef60d1 Binary files /dev/null and b/tests/test_images/draw_rect_across_nx_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_nx_origin_layer.8bit.png b/tests/test_images/draw_rect_across_nx_origin_layer.8bit.png new file mode 100644 index 0000000000..0d050b4fb7 Binary files /dev/null and b/tests/test_images/draw_rect_across_nx_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_ny_offset_layer.8bit.png b/tests/test_images/draw_rect_across_ny_offset_layer.8bit.png new file mode 100644 index 0000000000..976d56abe5 Binary files /dev/null and b/tests/test_images/draw_rect_across_ny_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_ny_origin_layer.8bit.png b/tests/test_images/draw_rect_across_ny_origin_layer.8bit.png new file mode 100644 index 0000000000..a810596f7c Binary files /dev/null and b/tests/test_images/draw_rect_across_ny_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_x_offset_layer.8bit.png b/tests/test_images/draw_rect_across_x_offset_layer.8bit.png new file mode 100644 index 0000000000..afddd77f00 Binary files /dev/null and b/tests/test_images/draw_rect_across_x_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_x_origin_layer.8bit.png b/tests/test_images/draw_rect_across_x_origin_layer.8bit.png new file mode 100644 index 0000000000..9070877ce4 Binary files /dev/null and b/tests/test_images/draw_rect_across_x_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_y_offset_layer.8bit.png b/tests/test_images/draw_rect_across_y_offset_layer.8bit.png new file mode 100644 index 0000000000..580ac4bb1e Binary files /dev/null and b/tests/test_images/draw_rect_across_y_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_across_y_origin_layer.8bit.png b/tests/test_images/draw_rect_across_y_origin_layer.8bit.png new file mode 100644 index 0000000000..adbad7a1b3 Binary files /dev/null and b/tests/test_images/draw_rect_across_y_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_clip_rect.8bit.png b/tests/test_images/draw_rect_clip_rect.8bit.png new file mode 100644 index 0000000000..b8558e638a Binary files /dev/null and b/tests/test_images/draw_rect_clip_rect.8bit.png differ diff --git a/tests/test_images/draw_rect_clip_rect_aa.8bit.png b/tests/test_images/draw_rect_clip_rect_aa.8bit.png index a35cd30005..1d2e1a4130 100644 Binary files a/tests/test_images/draw_rect_clip_rect_aa.8bit.png and b/tests/test_images/draw_rect_clip_rect_aa.8bit.png differ diff --git a/tests/test_images/draw_rect_clip_rect_aa_nudge.8bit.png b/tests/test_images/draw_rect_clip_rect_aa_nudge.8bit.png index 4d04d3d18e..3854999b3f 100644 Binary files a/tests/test_images/draw_rect_clip_rect_aa_nudge.8bit.png and b/tests/test_images/draw_rect_clip_rect_aa_nudge.8bit.png differ diff --git a/tests/test_images/draw_rect_clip_rect_nudge.8bit.png b/tests/test_images/draw_rect_clip_rect_nudge.8bit.png new file mode 100644 index 0000000000..a9651ab6f3 Binary files /dev/null and b/tests/test_images/draw_rect_clip_rect_nudge.8bit.png differ diff --git a/tests/test_images/draw_rect_inside_offset_layer.8bit.png b/tests/test_images/draw_rect_inside_offset_layer.8bit.png new file mode 100644 index 0000000000..4a204787fb Binary files /dev/null and b/tests/test_images/draw_rect_inside_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_inside_origin_layer.8bit.png b/tests/test_images/draw_rect_inside_origin_layer.8bit.png new file mode 100644 index 0000000000..99576d1812 Binary files /dev/null and b/tests/test_images/draw_rect_inside_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw11_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_aa_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..506f08df26 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw11_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_aa_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..acc5605881 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_rect_offset_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..a486835b84 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw1_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_aa_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..b10bea05c0 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw1_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_aa_sw1_clip_xy.8bit.png new file mode 100644 index 0000000000..fdb853051d Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_rect_offset_aa_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..1aaf18b217 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw2_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_aa_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..b10bea05c0 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw2_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_aa_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..fdb853051d Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw2_no_clip.8bit.png b/tests/test_images/draw_rect_offset_aa_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..1aaf18b217 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw3_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_aa_sw3_clip_nxny.8bit.png new file mode 100644 index 0000000000..05ad3d8b9b Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw3_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_aa_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..b6341f98dc Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw3_no_clip.8bit.png b/tests/test_images/draw_rect_offset_aa_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..431aaaded9 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw4_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_aa_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..4eec793024 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw4_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_aa_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..6e21652d58 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw4_no_clip.8bit.png b/tests/test_images/draw_rect_offset_aa_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..c4b20f3ec6 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw5_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_aa_sw5_clip_nxny.8bit.png new file mode 100644 index 0000000000..4eec793024 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw5_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_aa_sw5_clip_xy.8bit.png new file mode 100644 index 0000000000..6e21652d58 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_aa_sw5_no_clip.8bit.png b/tests/test_images/draw_rect_offset_aa_sw5_no_clip.8bit.png new file mode 100644 index 0000000000..c4b20f3ec6 Binary files /dev/null and b/tests/test_images/draw_rect_offset_aa_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw11_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..72fa92e1c5 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw11_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..2430504084 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw11_no_clip.8bit.png b/tests/test_images/draw_rect_offset_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..9fb5df2720 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw1_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..b10bea05c0 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw1_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_sw1_clip_xy.8bit.png new file mode 100644 index 0000000000..fdb853051d Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw1_no_clip.8bit.png b/tests/test_images/draw_rect_offset_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..1aaf18b217 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw2_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..b10bea05c0 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw2_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..fdb853051d Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw2_no_clip.8bit.png b/tests/test_images/draw_rect_offset_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..1aaf18b217 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw3_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_sw3_clip_nxny.8bit.png new file mode 100644 index 0000000000..05ad3d8b9b Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw3_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..b6341f98dc Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw3_no_clip.8bit.png b/tests/test_images/draw_rect_offset_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..431aaaded9 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw4_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..9a50b0e09f Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw4_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..0fb16219a9 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw4_no_clip.8bit.png b/tests/test_images/draw_rect_offset_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..7507b730f4 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw5_clip_nxny.8bit.png b/tests/test_images/draw_rect_offset_sw5_clip_nxny.8bit.png new file mode 100644 index 0000000000..9a50b0e09f Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw5_clip_xy.8bit.png b/tests/test_images/draw_rect_offset_sw5_clip_xy.8bit.png new file mode 100644 index 0000000000..0fb16219a9 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_offset_sw5_no_clip.8bit.png b/tests/test_images/draw_rect_offset_sw5_no_clip.8bit.png new file mode 100644 index 0000000000..7507b730f4 Binary files /dev/null and b/tests/test_images/draw_rect_offset_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw11_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_aa_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..e4d2b4a972 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw11_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_aa_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..b6f2b8f055 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_rect_origin_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..03811dfe81 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw1_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_aa_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..65ab3f4c4b Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw1_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_aa_sw1_clip_xy.8bit.png new file mode 100644 index 0000000000..1817b3d710 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_rect_origin_aa_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..81b45811b2 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw2_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_aa_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..65ab3f4c4b Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw2_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_aa_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..1817b3d710 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw2_no_clip.8bit.png b/tests/test_images/draw_rect_origin_aa_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..81b45811b2 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw3_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_aa_sw3_clip_nxny.8bit.png new file mode 100644 index 0000000000..4fa4755f69 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw3_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_aa_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..1af1357350 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw3_no_clip.8bit.png b/tests/test_images/draw_rect_origin_aa_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..3267752f8e Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw4_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_aa_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..8bfa3016b4 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw4_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_aa_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..ae998f1bd4 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw4_no_clip.8bit.png b/tests/test_images/draw_rect_origin_aa_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..f390b949a1 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw5_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_aa_sw5_clip_nxny.8bit.png new file mode 100644 index 0000000000..8bfa3016b4 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw5_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_aa_sw5_clip_xy.8bit.png new file mode 100644 index 0000000000..ae998f1bd4 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_aa_sw5_no_clip.8bit.png b/tests/test_images/draw_rect_origin_aa_sw5_no_clip.8bit.png new file mode 100644 index 0000000000..f390b949a1 Binary files /dev/null and b/tests/test_images/draw_rect_origin_aa_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw11_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..e0edc17419 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw11_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..1c8178644f Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw11_no_clip.8bit.png b/tests/test_images/draw_rect_origin_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..8059d654fd Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw1_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..65ab3f4c4b Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw1_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_sw1_clip_xy.8bit.png new file mode 100644 index 0000000000..1817b3d710 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw1_no_clip.8bit.png b/tests/test_images/draw_rect_origin_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..81b45811b2 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw2_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..65ab3f4c4b Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw2_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..1817b3d710 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw2_no_clip.8bit.png b/tests/test_images/draw_rect_origin_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..81b45811b2 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw3_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_sw3_clip_nxny.8bit.png new file mode 100644 index 0000000000..4fa4755f69 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw3_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..1af1357350 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw3_no_clip.8bit.png b/tests/test_images/draw_rect_origin_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..3267752f8e Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw4_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..42d7bb70b9 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw4_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..84b2e149e0 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw4_no_clip.8bit.png b/tests/test_images/draw_rect_origin_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..ff1e0002f2 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw5_clip_nxny.8bit.png b/tests/test_images/draw_rect_origin_sw5_clip_nxny.8bit.png new file mode 100644 index 0000000000..42d7bb70b9 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw5_clip_xy.8bit.png b/tests/test_images/draw_rect_origin_sw5_clip_xy.8bit.png new file mode 100644 index 0000000000..84b2e149e0 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_rect_origin_sw5_no_clip.8bit.png b/tests/test_images/draw_rect_origin_sw5_no_clip.8bit.png new file mode 100644 index 0000000000..ff1e0002f2 Binary files /dev/null and b/tests/test_images/draw_rect_origin_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_across_nx_offset_layer.8bit.png b/tests/test_images/draw_round_rect_across_nx_offset_layer.8bit.png new file mode 100644 index 0000000000..d9a2fbc6ea Binary files /dev/null and b/tests/test_images/draw_round_rect_across_nx_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_across_ny_offset_layer.8bit.png b/tests/test_images/draw_round_rect_across_ny_offset_layer.8bit.png new file mode 100644 index 0000000000..dd1ba7944e Binary files /dev/null and b/tests/test_images/draw_round_rect_across_ny_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_across_x_offset_layer.8bit.png b/tests/test_images/draw_round_rect_across_x_offset_layer.8bit.png new file mode 100644 index 0000000000..bc78d00a29 Binary files /dev/null and b/tests/test_images/draw_round_rect_across_x_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_across_x_origin_layer.8bit.png b/tests/test_images/draw_round_rect_across_x_origin_layer.8bit.png new file mode 100644 index 0000000000..fd96dd2779 Binary files /dev/null and b/tests/test_images/draw_round_rect_across_x_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_across_y_offset_layer.8bit.png b/tests/test_images/draw_round_rect_across_y_offset_layer.8bit.png new file mode 100644 index 0000000000..6db62d9058 Binary files /dev/null and b/tests/test_images/draw_round_rect_across_y_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_across_y_origin_layer.8bit.png b/tests/test_images/draw_round_rect_across_y_origin_layer.8bit.png new file mode 100644 index 0000000000..69a66ea460 Binary files /dev/null and b/tests/test_images/draw_round_rect_across_y_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_clear.8bit.png b/tests/test_images/draw_round_rect_clear.8bit.png index da05497888..6ad86a18ca 100644 Binary files a/tests/test_images/draw_round_rect_clear.8bit.png and b/tests/test_images/draw_round_rect_clear.8bit.png differ diff --git a/tests/test_images/draw_round_rect_inside_offset_layer.8bit.png b/tests/test_images/draw_round_rect_inside_offset_layer.8bit.png new file mode 100644 index 0000000000..bf2971b4b6 Binary files /dev/null and b/tests/test_images/draw_round_rect_inside_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_inside_origin_layer.8bit.png b/tests/test_images/draw_round_rect_inside_origin_layer.8bit.png new file mode 100644 index 0000000000..6ad86a18ca Binary files /dev/null and b/tests/test_images/draw_round_rect_inside_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw11_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..2a8990b9c9 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw11_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..edd109eed3 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..cdb10c074b Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_nxny.8bit.png index c1bda7a6ce..352644b835 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_nxny.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_xy.8bit.png index 53caad19ad..be8a08ee8e 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_xy.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw1_no_clip.8bit.png index b786b41ef9..a205d7e12a 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw1_no_clip.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw2_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..7767a5af45 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw2_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..2d64f212e5 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw2_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..a74b5f27be Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_nxny.8bit.png index e4433ed36d..7767a5af45 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_nxny.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..2d64f212e5 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw3_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..a74b5f27be Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw4_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..75e5550046 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw4_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..f4eaf1a145 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw4_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..c847055fb0 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_aa_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_nxny.8bit.png index 46721d0e85..75e5550046 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_nxny.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_xy.8bit.png index a5dbd1eef8..f4eaf1a145 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_xy.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_aa_sw5_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_aa_sw5_no_clip.8bit.png index e1f0505324..c847055fb0 100644 Binary files a/tests/test_images/draw_round_rect_offset_r4_aa_sw5_no_clip.8bit.png and b/tests/test_images/draw_round_rect_offset_r4_aa_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw11_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..47a108bb47 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw11_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..418a502844 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..09f13b8fcb Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw1_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..6d2a34e95f Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw1_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw1_clip_xy.8bit.png new file mode 100644 index 0000000000..79571466cb Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..a33c9df4c2 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw2_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..1b8fc22244 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw2_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..cd758bf43c Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw2_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..3398301784 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw3_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw3_clip_nxny.8bit.png new file mode 100644 index 0000000000..1b8fc22244 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw3_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..cd758bf43c Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw3_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..3398301784 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw4_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..5bba9c0f32 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw4_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..a82cfe3086 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw4_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..ce60e03de7 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw5_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw5_clip_nxny.8bit.png new file mode 100644 index 0000000000..5bba9c0f32 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw5_clip_xy.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw5_clip_xy.8bit.png new file mode 100644 index 0000000000..a82cfe3086 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_offset_r4_sw5_no_clip.8bit.png b/tests/test_images/draw_round_rect_offset_r4_sw5_no_clip.8bit.png new file mode 100644 index 0000000000..ce60e03de7 Binary files /dev/null and b/tests/test_images/draw_round_rect_offset_r4_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r0_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r0_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..03811dfe81 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r0_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r0_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r0_aa_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..81b45811b2 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r0_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r0_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r0_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..8059d654fd Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r0_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r0_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r0_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..81b45811b2 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r0_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r1_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r1_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..3119c8e97f Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r1_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r1_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r1_aa_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..b1a28228a4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r1_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r1_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r1_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..4785acd727 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r1_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r1_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r1_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..b1a28228a4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r1_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r2_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r2_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..4260c4b1a3 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r2_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r2_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r2_aa_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..b1a28228a4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r2_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r2_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r2_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..eb1a3446fd Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r2_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r2_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r2_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..b1a28228a4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r2_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r3_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r3_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..75079b66b3 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r3_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r3_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r3_aa_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..305cd0e502 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r3_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r3_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r3_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..41ea0fd100 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r3_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r3_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r3_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..e053c9d870 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r3_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw11_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..ea176aa557 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw11_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..ecca67d54f Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..f99b3b97aa Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..dd1b31d60b Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_xy.8bit.png index 98f0354d42..5252da9b86 100644 Binary files a/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_xy.8bit.png and b/tests/test_images/draw_round_rect_origin_r4_aa_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw1_no_clip.8bit.png index 30dcbaa50b..d7c141b280 100644 Binary files a/tests/test_images/draw_round_rect_origin_r4_aa_sw1_no_clip.8bit.png and b/tests/test_images/draw_round_rect_origin_r4_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw2_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..fc8ca43b0c Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw2_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..5f200f0b99 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw2_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..d9cf48febf Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_nxny.8bit.png index 3256ef7953..fc8ca43b0c 100644 Binary files a/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_nxny.8bit.png and b/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..5f200f0b99 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw3_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..d9cf48febf Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw4_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..59e2234e4e Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw4_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..bc3e699bdf Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw4_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..b01b4b8f97 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_aa_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_nxny.8bit.png index 83c7a761ca..59e2234e4e 100644 Binary files a/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_nxny.8bit.png and b/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_xy.8bit.png index 381a52fba0..bc3e699bdf 100644 Binary files a/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_xy.8bit.png and b/tests/test_images/draw_round_rect_origin_r4_aa_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_aa_sw5_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_aa_sw5_no_clip.8bit.png index 5d527ebbc2..b01b4b8f97 100644 Binary files a/tests/test_images/draw_round_rect_origin_r4_aa_sw5_no_clip.8bit.png and b/tests/test_images/draw_round_rect_origin_r4_aa_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw11_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw11_clip_nxny.8bit.png new file mode 100644 index 0000000000..0e4ff3e063 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw11_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw11_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw11_clip_xy.8bit.png new file mode 100644 index 0000000000..ae6886c23a Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw11_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..614b5c7d80 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw1_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw1_clip_nxny.8bit.png new file mode 100644 index 0000000000..4aa731a1e4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw1_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw1_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw1_clip_xy.8bit.png new file mode 100644 index 0000000000..9b24f71f83 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw1_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..db8bc0275e Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw2_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw2_clip_nxny.8bit.png new file mode 100644 index 0000000000..5e7d26545e Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw2_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw2_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw2_clip_xy.8bit.png new file mode 100644 index 0000000000..e4f8212f28 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw2_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw2_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw2_no_clip.8bit.png new file mode 100644 index 0000000000..214fdf97a9 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw2_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw3_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw3_clip_nxny.8bit.png new file mode 100644 index 0000000000..5e7d26545e Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw3_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw3_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw3_clip_xy.8bit.png new file mode 100644 index 0000000000..e4f8212f28 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw3_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw3_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw3_no_clip.8bit.png new file mode 100644 index 0000000000..214fdf97a9 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw3_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw4_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw4_clip_nxny.8bit.png new file mode 100644 index 0000000000..c680d94c3d Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw4_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw4_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw4_clip_xy.8bit.png new file mode 100644 index 0000000000..c6c2571be4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw4_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw4_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw4_no_clip.8bit.png new file mode 100644 index 0000000000..4bb9b03b45 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw4_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw5_clip_nxny.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw5_clip_nxny.8bit.png new file mode 100644 index 0000000000..c680d94c3d Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw5_clip_nxny.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw5_clip_xy.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw5_clip_xy.8bit.png new file mode 100644 index 0000000000..c6c2571be4 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw5_clip_xy.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_r4_sw5_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_r4_sw5_no_clip.8bit.png new file mode 100644 index 0000000000..4bb9b03b45 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_r4_sw5_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax1_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax1_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..7532bed2d5 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax1_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax1_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax1_aa_sw1_no_clip.8bit.png index a6921c8c92..54294cefab 100644 Binary files a/tests/test_images/draw_round_rect_origin_rmax1_aa_sw1_no_clip.8bit.png and b/tests/test_images/draw_round_rect_origin_rmax1_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax1_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax1_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..602c3ac10f Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax1_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax1_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax1_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..bd0d0688ef Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax1_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax2_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax2_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..7532bed2d5 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax2_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax2_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax2_aa_sw1_no_clip.8bit.png index a6921c8c92..54294cefab 100644 Binary files a/tests/test_images/draw_round_rect_origin_rmax2_aa_sw1_no_clip.8bit.png and b/tests/test_images/draw_round_rect_origin_rmax2_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax2_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax2_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..602c3ac10f Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax2_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax2_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax2_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..bd0d0688ef Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax2_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax_aa_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax_aa_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..1d8d2a50cd Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax_aa_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax_aa_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax_aa_sw1_no_clip.8bit.png index c429d55609..0a2ef896da 100644 Binary files a/tests/test_images/draw_round_rect_origin_rmax_aa_sw1_no_clip.8bit.png and b/tests/test_images/draw_round_rect_origin_rmax_aa_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax_sw11_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax_sw11_no_clip.8bit.png new file mode 100644 index 0000000000..94cb01bc0b Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax_sw11_no_clip.8bit.png differ diff --git a/tests/test_images/draw_round_rect_origin_rmax_sw1_no_clip.8bit.png b/tests/test_images/draw_round_rect_origin_rmax_sw1_no_clip.8bit.png new file mode 100644 index 0000000000..69de81dad9 Binary files /dev/null and b/tests/test_images/draw_round_rect_origin_rmax_sw1_no_clip.8bit.png differ diff --git a/tests/test_images/draw_stroke_across_nxny_offset_layer.8bit.png b/tests/test_images/draw_stroke_across_nxny_offset_layer.8bit.png new file mode 100644 index 0000000000..243dd80c67 Binary files /dev/null and b/tests/test_images/draw_stroke_across_nxny_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_across_nxny_origin_layer.8bit.png b/tests/test_images/draw_stroke_across_nxny_origin_layer.8bit.png new file mode 100644 index 0000000000..fb624eecd3 Binary files /dev/null and b/tests/test_images/draw_stroke_across_nxny_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_across_x_offset_layer.8bit.png b/tests/test_images/draw_stroke_across_x_offset_layer.8bit.png new file mode 100644 index 0000000000..04c1379a89 Binary files /dev/null and b/tests/test_images/draw_stroke_across_x_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_across_x_origin_layer.8bit.png b/tests/test_images/draw_stroke_across_x_origin_layer.8bit.png new file mode 100644 index 0000000000..3d7c713ecd Binary files /dev/null and b/tests/test_images/draw_stroke_across_x_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_inside_offset_layer.8bit.png b/tests/test_images/draw_stroke_inside_offset_layer.8bit.png new file mode 100644 index 0000000000..611aacf7f0 Binary files /dev/null and b/tests/test_images/draw_stroke_inside_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_inside_origin_layer.8bit.png b/tests/test_images/draw_stroke_inside_origin_layer.8bit.png new file mode 100644 index 0000000000..1a9fb3f692 Binary files /dev/null and b/tests/test_images/draw_stroke_inside_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_nxny_offset_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_nxny_offset_layer_aa.8bit.png new file mode 100644 index 0000000000..ec974c69d6 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_nxny_offset_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_nxny_offset_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_nxny_offset_layer_non_aa.8bit.png new file mode 100644 index 0000000000..b405f6a0aa Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_nxny_offset_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_nxny_origin_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_nxny_origin_layer_aa.8bit.png new file mode 100644 index 0000000000..8d7b43965a Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_nxny_origin_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_nxny_origin_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_nxny_origin_layer_non_aa.8bit.png new file mode 100644 index 0000000000..41959e9fb0 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_nxny_origin_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_x_offset_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_x_offset_layer_aa.8bit.png new file mode 100644 index 0000000000..eae14d965f Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_x_offset_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_x_offset_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_x_offset_layer_non_aa.8bit.png new file mode 100644 index 0000000000..d372535db6 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_x_offset_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_x_origin_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_x_origin_layer_aa.8bit.png new file mode 100644 index 0000000000..7e4c002d81 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_x_origin_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_across_x_origin_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_across_x_origin_layer_non_aa.8bit.png new file mode 100644 index 0000000000..b2600e4e02 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_across_x_origin_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_close_points_around_1px_aa.8bit.png b/tests/test_images/draw_stroke_precise_close_points_around_1px_aa.8bit.png index d06a753ab2..03787b0636 100644 Binary files a/tests/test_images/draw_stroke_precise_close_points_around_1px_aa.8bit.png and b/tests/test_images/draw_stroke_precise_close_points_around_1px_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_close_points_around_1px_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_close_points_around_1px_non_aa.8bit.png new file mode 100644 index 0000000000..826ea3c649 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_close_points_around_1px_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_close_points_less_than_1px_aa.8bit.png b/tests/test_images/draw_stroke_precise_close_points_less_than_1px_aa.8bit.png new file mode 100644 index 0000000000..2355dcf3d9 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_close_points_less_than_1px_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_close_points_less_than_1px_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_close_points_less_than_1px_non_aa.8bit.png new file mode 100644 index 0000000000..d0157010f6 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_close_points_less_than_1px_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_inside_offset_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_inside_offset_layer_aa.8bit.png new file mode 100644 index 0000000000..66bde12333 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_inside_offset_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_inside_offset_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_inside_offset_layer_non_aa.8bit.png new file mode 100644 index 0000000000..aa837a835f Binary files /dev/null and b/tests/test_images/draw_stroke_precise_inside_offset_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_inside_origin_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_inside_origin_layer_aa.8bit.png new file mode 100644 index 0000000000..a53bee0dd1 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_inside_origin_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_inside_origin_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_inside_origin_layer_non_aa.8bit.png new file mode 100644 index 0000000000..d24839a80f Binary files /dev/null and b/tests/test_images/draw_stroke_precise_inside_origin_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_same_point_offset_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_same_point_offset_layer_aa.8bit.png new file mode 100644 index 0000000000..fea4d77727 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_same_point_offset_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_same_point_offset_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_same_point_offset_layer_non_aa.8bit.png new file mode 100644 index 0000000000..ba3544204d Binary files /dev/null and b/tests/test_images/draw_stroke_precise_same_point_offset_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_same_point_origin_layer_aa.8bit.png b/tests/test_images/draw_stroke_precise_same_point_origin_layer_aa.8bit.png new file mode 100644 index 0000000000..9dec467467 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_same_point_origin_layer_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_same_point_origin_layer_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_same_point_origin_layer_non_aa.8bit.png new file mode 100644 index 0000000000..f882ba58ba Binary files /dev/null and b/tests/test_images/draw_stroke_precise_same_point_origin_layer_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_same_points_pattern_aa.8bit.png b/tests/test_images/draw_stroke_precise_same_points_pattern_aa.8bit.png index e856890098..8542654f27 100644 Binary files a/tests/test_images/draw_stroke_precise_same_points_pattern_aa.8bit.png and b/tests/test_images/draw_stroke_precise_same_points_pattern_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_precise_same_points_pattern_non_aa.8bit.png b/tests/test_images/draw_stroke_precise_same_points_pattern_non_aa.8bit.png new file mode 100644 index 0000000000..3a277311f3 Binary files /dev/null and b/tests/test_images/draw_stroke_precise_same_points_pattern_non_aa.8bit.png differ diff --git a/tests/test_images/draw_stroke_same_point_offset_layer.8bit.png b/tests/test_images/draw_stroke_same_point_offset_layer.8bit.png index cb2c6cc2ea..19d4dd1079 100644 Binary files a/tests/test_images/draw_stroke_same_point_offset_layer.8bit.png and b/tests/test_images/draw_stroke_same_point_offset_layer.8bit.png differ diff --git a/tests/test_images/draw_stroke_same_point_origin_layer.8bit.png b/tests/test_images/draw_stroke_same_point_origin_layer.8bit.png index cd02110ac8..f56e4c14de 100644 Binary files a/tests/test_images/draw_stroke_same_point_origin_layer.8bit.png and b/tests/test_images/draw_stroke_same_point_origin_layer.8bit.png differ diff --git a/tests/test_images/draw_text_aaa22.8bit.png b/tests/test_images/draw_text_aaa22.8bit.png new file mode 100644 index 0000000000..7d422c3750 Binary files /dev/null and b/tests/test_images/draw_text_aaa22.8bit.png differ diff --git a/tests/test_images/draw_text_ajj22.8bit.png b/tests/test_images/draw_text_ajj22.8bit.png new file mode 100644 index 0000000000..2a3e93b0f4 Binary files /dev/null and b/tests/test_images/draw_text_ajj22.8bit.png differ diff --git a/tests/test_images/draw_text_color_assign.8bit.png b/tests/test_images/draw_text_color_assign.8bit.png index bb99a4400e..f6a24d8e7c 100644 Binary files a/tests/test_images/draw_text_color_assign.8bit.png and b/tests/test_images/draw_text_color_assign.8bit.png differ diff --git a/tests/test_images/draw_text_color_set.8bit.png b/tests/test_images/draw_text_color_set.8bit.png index d1ec6fe6c9..c9f9797c18 100644 Binary files a/tests/test_images/draw_text_color_set.8bit.png and b/tests/test_images/draw_text_color_set.8bit.png differ diff --git a/tests/test_images/draw_text_data_row_offsets.8bit.png b/tests/test_images/draw_text_data_row_offsets.8bit.png index e8beb142ba..abf51d8d06 100644 Binary files a/tests/test_images/draw_text_data_row_offsets.8bit.png and b/tests/test_images/draw_text_data_row_offsets.8bit.png differ diff --git a/tests/test_images/draw_text_jja00.8bit.png b/tests/test_images/draw_text_jja00.8bit.png new file mode 100644 index 0000000000..f1cbe27eba Binary files /dev/null and b/tests/test_images/draw_text_jja00.8bit.png differ diff --git a/tests/test_images/draw_text_jja20.8bit.png b/tests/test_images/draw_text_jja20.8bit.png new file mode 100644 index 0000000000..eb42f64fca Binary files /dev/null and b/tests/test_images/draw_text_jja20.8bit.png differ diff --git a/tests/test_images/draw_text_jja22.8bit.png b/tests/test_images/draw_text_jja22.8bit.png new file mode 100644 index 0000000000..b8ddfffaf0 Binary files /dev/null and b/tests/test_images/draw_text_jja22.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_across_nx.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_across_nx.8bit.png new file mode 100644 index 0000000000..0e9ace8623 Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_across_nx.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_across_nx_zero_y_offset.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_across_nx_zero_y_offset.8bit.png new file mode 100644 index 0000000000..4a12bd5e34 Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_across_nx_zero_y_offset.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_across_ny.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_across_ny.8bit.png new file mode 100644 index 0000000000..e06fec55c9 Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_across_ny.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_across_ny_descender.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_across_ny_descender.8bit.png new file mode 100644 index 0000000000..6c132c9832 Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_across_ny_descender.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_across_x.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_across_x.8bit.png new file mode 100644 index 0000000000..e9bb4633cf Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_across_x.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_across_y.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_across_y.8bit.png new file mode 100644 index 0000000000..56fab7c85e Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_across_y.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_outside_nx.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_nx.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_nx.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_outside_ny.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_ny.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_ny.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_outside_x.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_x.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_x.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_ellipsis_clip_outside_y.8bit.png b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_y.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_ellipsis_clip_outside_y.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_across_nx.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_across_nx.8bit.png new file mode 100644 index 0000000000..61d4579224 Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_across_nx.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_across_ny.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_across_ny.8bit.png new file mode 100644 index 0000000000..28da147625 Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_across_ny.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_across_ny_second_line.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_across_ny_second_line.8bit.png new file mode 100644 index 0000000000..c4494ee1ba Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_across_ny_second_line.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_across_x.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_across_x.8bit.png new file mode 100644 index 0000000000..22b693b4b3 Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_across_x.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_across_y.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_across_y.8bit.png new file mode 100644 index 0000000000..aa7a52dd05 Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_across_y.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_across_y_second_line.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_across_y_second_line.8bit.png new file mode 100644 index 0000000000..320effa307 Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_across_y_second_line.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_outside_nx.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_nx.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_nx.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_outside_ny.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_ny.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_ny.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_outside_x.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_x.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_x.8bit.png differ diff --git a/tests/test_images/draw_text_single_line_wordwrap_clip_outside_y.8bit.png b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_y.8bit.png new file mode 100644 index 0000000000..913172414a Binary files /dev/null and b/tests/test_images/draw_text_single_line_wordwrap_clip_outside_y.8bit.png differ diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_checkerboard_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_checkerboard_no_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~snowy.png deleted file mode 100644 index 6e0a4f0eab..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~tintin.png deleted file mode 100644 index 6e0a4f0eab..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_checkerboard_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_even_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_even_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_even_offset_even_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~snowy.png deleted file mode 100644 index ec7eab365c..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~tintin.png deleted file mode 100644 index ec7eab365c..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_even_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~tintin.png deleted file mode 100644 index 39eb0e9800..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_even_cols_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_even_offset_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_even_offset_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_even_offset_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_no_clip~tintin.png deleted file mode 100644 index 06dfe7b9b7..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_even_offset_odd_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_even_offset_odd_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~snowy.png deleted file mode 100644 index 490d243edf..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~tintin.png deleted file mode 100644 index 490d243edf..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_odd_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~tintin.png deleted file mode 100644 index c4ab1b5425..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_even_offset_odd_cols_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_checkerboard_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_checkerboard_no_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~snowy.png deleted file mode 100644 index 346c4261c0..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~tintin.png deleted file mode 100644 index 346c4261c0..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_checkerboard_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_even_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_even_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~snowy.png deleted file mode 100644 index 5a78195132..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~tintin.png deleted file mode 100644 index 5a78195132..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_even_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~tintin.png deleted file mode 100644 index b1de7dd8bf..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_even_cols_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~tintin.png deleted file mode 100644 index 5d51f7bc80..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_odd_offset_odd_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_odd_offset_odd_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~snowy.png deleted file mode 100644 index d293e604c6..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~tintin.png deleted file mode 100644 index d293e604c6..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~tintin.png deleted file mode 100644 index 6819d9622a..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_odd_offset_odd_cols_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_origin_checkerboard_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_checkerboard_no_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~snowy.png deleted file mode 100644 index eebe612fab..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~tintin.png deleted file mode 100644 index eebe612fab..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_checkerboard_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_origin_even_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_even_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_even_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_origin_even_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_origin_even_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_origin_even_clip~snowy.png deleted file mode 100644 index c0c09a380f..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_even_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_origin_even_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_even_clip~tintin.png deleted file mode 100644 index c0c09a380f..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_even_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~tintin.png deleted file mode 100644 index 7e0a159a1e..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_even_cols_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_origin_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_origin_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_origin_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_origin_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_origin_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_no_clip~tintin.png deleted file mode 100644 index 13b4f56c86..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_horiz_dotted_line_origin_odd_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_odd_clip~asterix.png similarity index 100% rename from tests/test_images/draw_horiz_dotted_line_origin_odd_clip~tintin.png rename to tests/test_images/draw_vert_dotted_line_origin_odd_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_origin_odd_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_origin_odd_clip~snowy.png deleted file mode 100644 index 2a9c583365..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_odd_clip~snowy.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_origin_odd_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_odd_clip~tintin.png deleted file mode 100644 index 2a9c583365..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_odd_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~snowy.png b/tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~asterix.png similarity index 100% rename from tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~snowy.png rename to tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~asterix.png diff --git a/tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~tintin.png b/tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~tintin.png deleted file mode 100644 index fd33140e88..0000000000 Binary files a/tests/test_images/draw_vert_dotted_line_origin_odd_cols_no_clip~tintin.png and /dev/null differ diff --git a/tests/test_images/fill_rect_across_nx_offset_layer.8bit.png b/tests/test_images/fill_rect_across_nx_offset_layer.8bit.png new file mode 100644 index 0000000000..e8a86600cf Binary files /dev/null and b/tests/test_images/fill_rect_across_nx_offset_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_nx_origin_layer.8bit.png b/tests/test_images/fill_rect_across_nx_origin_layer.8bit.png new file mode 100644 index 0000000000..7a54565d95 Binary files /dev/null and b/tests/test_images/fill_rect_across_nx_origin_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_ny_offset_layer.8bit.png b/tests/test_images/fill_rect_across_ny_offset_layer.8bit.png new file mode 100644 index 0000000000..0ce51ef3b3 Binary files /dev/null and b/tests/test_images/fill_rect_across_ny_offset_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_ny_origin_layer.8bit.png b/tests/test_images/fill_rect_across_ny_origin_layer.8bit.png new file mode 100644 index 0000000000..f06605efc4 Binary files /dev/null and b/tests/test_images/fill_rect_across_ny_origin_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_x_offset_layer.8bit.png b/tests/test_images/fill_rect_across_x_offset_layer.8bit.png new file mode 100644 index 0000000000..5fdf93df93 Binary files /dev/null and b/tests/test_images/fill_rect_across_x_offset_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_x_origin_layer.8bit.png b/tests/test_images/fill_rect_across_x_origin_layer.8bit.png new file mode 100644 index 0000000000..335da2ed3b Binary files /dev/null and b/tests/test_images/fill_rect_across_x_origin_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_y_offset_layer.8bit.png b/tests/test_images/fill_rect_across_y_offset_layer.8bit.png new file mode 100644 index 0000000000..5507e89ea4 Binary files /dev/null and b/tests/test_images/fill_rect_across_y_offset_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_across_y_origin_layer.8bit.png b/tests/test_images/fill_rect_across_y_origin_layer.8bit.png new file mode 100644 index 0000000000..8871062971 Binary files /dev/null and b/tests/test_images/fill_rect_across_y_origin_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_clip_rect.8bit.png b/tests/test_images/fill_rect_clip_rect.8bit.png new file mode 100644 index 0000000000..9ec00548cb Binary files /dev/null and b/tests/test_images/fill_rect_clip_rect.8bit.png differ diff --git a/tests/test_images/fill_rect_clip_rect_aa.8bit.png b/tests/test_images/fill_rect_clip_rect_aa.8bit.png index 8d4d35adee..0b392e79b5 100644 Binary files a/tests/test_images/fill_rect_clip_rect_aa.8bit.png and b/tests/test_images/fill_rect_clip_rect_aa.8bit.png differ diff --git a/tests/test_images/fill_rect_clipped.8bit.png b/tests/test_images/fill_rect_clipped.8bit.png new file mode 100644 index 0000000000..8717b57153 Binary files /dev/null and b/tests/test_images/fill_rect_clipped.8bit.png differ diff --git a/tests/test_images/fill_rect_corners_all.8bit.png b/tests/test_images/fill_rect_corners_all.8bit.png new file mode 100644 index 0000000000..a3e1872fcc Binary files /dev/null and b/tests/test_images/fill_rect_corners_all.8bit.png differ diff --git a/tests/test_images/fill_rect_inside_offset_layer.8bit.png b/tests/test_images/fill_rect_inside_offset_layer.8bit.png new file mode 100644 index 0000000000..da28de8785 Binary files /dev/null and b/tests/test_images/fill_rect_inside_offset_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_inside_origin_layer.8bit.png b/tests/test_images/fill_rect_inside_origin_layer.8bit.png new file mode 100644 index 0000000000..bf89a0909f Binary files /dev/null and b/tests/test_images/fill_rect_inside_origin_layer.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r0_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_r0_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..874644f8ea Binary files /dev/null and b/tests/test_images/fill_rect_origin_r0_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r0_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_r0_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..414b43314a Binary files /dev/null and b/tests/test_images/fill_rect_origin_r0_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r0_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r0_aa_no_clip.8bit.png new file mode 100644 index 0000000000..143a92f63d Binary files /dev/null and b/tests/test_images/fill_rect_origin_r0_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r0_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r0_no_clip.8bit.png new file mode 100644 index 0000000000..143a92f63d Binary files /dev/null and b/tests/test_images/fill_rect_origin_r0_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r1_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_r1_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..48038fe17f Binary files /dev/null and b/tests/test_images/fill_rect_origin_r1_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r1_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_r1_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..4544bd63a9 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r1_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r1_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r1_aa_no_clip.8bit.png new file mode 100644 index 0000000000..0bfc0b5862 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r1_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r1_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r1_no_clip.8bit.png new file mode 100644 index 0000000000..6dcd799416 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r1_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r2_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_r2_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..910702e3a2 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r2_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r2_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_r2_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..6aba18a4f8 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r2_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r2_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r2_aa_no_clip.8bit.png new file mode 100644 index 0000000000..2b2e71a4f9 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r2_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r2_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r2_no_clip.8bit.png new file mode 100644 index 0000000000..6dcd799416 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r2_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r3_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_r3_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..eaec1d4650 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r3_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r3_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_r3_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..c9e4e25404 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r3_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r3_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r3_aa_no_clip.8bit.png new file mode 100644 index 0000000000..bff70eb086 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r3_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r3_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r3_no_clip.8bit.png new file mode 100644 index 0000000000..0e998d9dad Binary files /dev/null and b/tests/test_images/fill_rect_origin_r3_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r4_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r4_aa_no_clip.8bit.png new file mode 100644 index 0000000000..6ae54140c2 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r4_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r4_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r4_no_clip.8bit.png new file mode 100644 index 0000000000..9a3791c484 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r4_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r5_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r5_aa_no_clip.8bit.png new file mode 100644 index 0000000000..01cc8697d5 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r5_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r5_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r5_no_clip.8bit.png new file mode 100644 index 0000000000..1263e96036 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r5_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r6_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r6_aa_no_clip.8bit.png new file mode 100644 index 0000000000..51d351fc97 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r6_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r6_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r6_no_clip.8bit.png new file mode 100644 index 0000000000..e36c709eb4 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r6_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r7_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r7_aa_no_clip.8bit.png new file mode 100644 index 0000000000..c12b560de5 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r7_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r7_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r7_no_clip.8bit.png new file mode 100644 index 0000000000..150c964dce Binary files /dev/null and b/tests/test_images/fill_rect_origin_r7_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r8_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r8_aa_no_clip.8bit.png new file mode 100644 index 0000000000..44810cb5a8 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r8_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r8_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r8_no_clip.8bit.png new file mode 100644 index 0000000000..c858309103 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r8_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r9_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r9_aa_no_clip.8bit.png new file mode 100644 index 0000000000..ade0201192 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r9_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_r9_no_clip.8bit.png b/tests/test_images/fill_rect_origin_r9_no_clip.8bit.png new file mode 100644 index 0000000000..1b87e0f294 Binary files /dev/null and b/tests/test_images/fill_rect_origin_r9_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax1_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_rmax1_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..8274aa6f96 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax1_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax1_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_rmax1_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..1fdf5d68ef Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax1_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax1_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_rmax1_aa_no_clip.8bit.png new file mode 100644 index 0000000000..fa4fcb8eae Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax1_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax2_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_rmax2_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..8274aa6f96 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax2_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax2_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_rmax2_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..1fdf5d68ef Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax2_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax2_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_rmax2_aa_no_clip.8bit.png new file mode 100644 index 0000000000..fa4fcb8eae Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax2_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_bottom.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_bottom.8bit.png new file mode 100644 index 0000000000..b123659405 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_bottom.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_bottomleft.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_bottomleft.8bit.png new file mode 100644 index 0000000000..0847c21c96 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_bottomleft.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_bottomright.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_bottomright.8bit.png new file mode 100644 index 0000000000..d4ac0e4610 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_bottomright.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_clip_nxny.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_clip_nxny.8bit.png new file mode 100644 index 0000000000..63aab27d53 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_clip_nxny.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_clip_xy.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_clip_xy.8bit.png new file mode 100644 index 0000000000..366a10a474 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_clip_xy.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_left.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_left.8bit.png new file mode 100644 index 0000000000..28afac2247 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_left.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_no_clip.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_no_clip.8bit.png new file mode 100644 index 0000000000..3ad596b162 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_no_clip.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_right.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_right.8bit.png new file mode 100644 index 0000000000..65f06868dc Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_right.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_top.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_top.8bit.png new file mode 100644 index 0000000000..c1c1ee497c Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_top.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_topleft.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_topleft.8bit.png new file mode 100644 index 0000000000..75ea86b21c Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_topleft.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_aa_topright.8bit.png b/tests/test_images/fill_rect_origin_rmax_aa_topright.8bit.png new file mode 100644 index 0000000000..f64ef39c09 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_aa_topright.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_bottom.8bit.png b/tests/test_images/fill_rect_origin_rmax_bottom.8bit.png new file mode 100644 index 0000000000..ebec9a5009 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_bottom.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_bottomleft.8bit.png b/tests/test_images/fill_rect_origin_rmax_bottomleft.8bit.png new file mode 100644 index 0000000000..9dfb385083 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_bottomleft.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_bottomright.8bit.png b/tests/test_images/fill_rect_origin_rmax_bottomright.8bit.png new file mode 100644 index 0000000000..130ffbd337 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_bottomright.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_left.8bit.png b/tests/test_images/fill_rect_origin_rmax_left.8bit.png new file mode 100644 index 0000000000..45683e48a8 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_left.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_right.8bit.png b/tests/test_images/fill_rect_origin_rmax_right.8bit.png new file mode 100644 index 0000000000..004f5b1cfc Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_right.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_top.8bit.png b/tests/test_images/fill_rect_origin_rmax_top.8bit.png new file mode 100644 index 0000000000..db8eab6064 Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_top.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_topleft.8bit.png b/tests/test_images/fill_rect_origin_rmax_topleft.8bit.png new file mode 100644 index 0000000000..09e7ff044d Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_topleft.8bit.png differ diff --git a/tests/test_images/fill_rect_origin_rmax_topright.8bit.png b/tests/test_images/fill_rect_origin_rmax_topright.8bit.png new file mode 100644 index 0000000000..e0c8888b6d Binary files /dev/null and b/tests/test_images/fill_rect_origin_rmax_topright.8bit.png differ diff --git a/tests/test_images/fill_rect_transparent.8bit.png b/tests/test_images/fill_rect_transparent.8bit.png index 6c1b8bbbe5..1ca2de52c4 100644 Binary files a/tests/test_images/fill_rect_transparent.8bit.png and b/tests/test_images/fill_rect_transparent.8bit.png differ diff --git a/tests/test_images/gpath_clipping_aa.8bit.png b/tests/test_images/gpath_clipping_aa.8bit.png index 7a5c8d5c65..520c604e81 100644 Binary files a/tests/test_images/gpath_clipping_aa.8bit.png and b/tests/test_images/gpath_clipping_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled.8bit.png b/tests/test_images/gpath_filled.8bit.png new file mode 100644 index 0000000000..9eedc6723c Binary files /dev/null and b/tests/test_images/gpath_filled.8bit.png differ diff --git a/tests/test_images/gpath_filled_aa.8bit.png b/tests/test_images/gpath_filled_aa.8bit.png new file mode 100644 index 0000000000..9eedc6723c Binary files /dev/null and b/tests/test_images/gpath_filled_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_bolt_aa.8bit.png b/tests/test_images/gpath_filled_bolt_aa.8bit.png index 32ca858c0d..e4f378209d 100644 Binary files a/tests/test_images/gpath_filled_bolt_aa.8bit.png and b/tests/test_images/gpath_filled_bolt_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_bottom_clipped.8bit.png b/tests/test_images/gpath_filled_bottom_clipped.8bit.png new file mode 100644 index 0000000000..08e5f3f3e3 Binary files /dev/null and b/tests/test_images/gpath_filled_bottom_clipped.8bit.png differ diff --git a/tests/test_images/gpath_filled_bottom_clipped_aa.8bit.png b/tests/test_images/gpath_filled_bottom_clipped_aa.8bit.png new file mode 100644 index 0000000000..08e5f3f3e3 Binary files /dev/null and b/tests/test_images/gpath_filled_bottom_clipped_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_crossing_aa.8bit.png b/tests/test_images/gpath_filled_crossing_aa.8bit.png index 1a6d280286..a1c04a7332 100644 Binary files a/tests/test_images/gpath_filled_crossing_aa.8bit.png and b/tests/test_images/gpath_filled_crossing_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_duplicates_aa.8bit.png b/tests/test_images/gpath_filled_duplicates_aa.8bit.png index bda28ea0a8..47a00df469 100644 Binary files a/tests/test_images/gpath_filled_duplicates_aa.8bit.png and b/tests/test_images/gpath_filled_duplicates_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_house_105_aa.8bit.png b/tests/test_images/gpath_filled_house_105_aa.8bit.png index 2c945642ec..a67ec85f9b 100644 Binary files a/tests/test_images/gpath_filled_house_105_aa.8bit.png and b/tests/test_images/gpath_filled_house_105_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_house_20_aa.8bit.png b/tests/test_images/gpath_filled_house_20_aa.8bit.png index 02d3f7d092..f0f0638185 100644 Binary files a/tests/test_images/gpath_filled_house_20_aa.8bit.png and b/tests/test_images/gpath_filled_house_20_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_infinite_45_aa.8bit.png b/tests/test_images/gpath_filled_infinite_45_aa.8bit.png index 2502952307..78b28f7a23 100644 Binary files a/tests/test_images/gpath_filled_infinite_45_aa.8bit.png and b/tests/test_images/gpath_filled_infinite_45_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_infinite_70_aa.8bit.png b/tests/test_images/gpath_filled_infinite_70_aa.8bit.png index ad2aa9f979..29ed5c4107 100644 Binary files a/tests/test_images/gpath_filled_infinite_70_aa.8bit.png and b/tests/test_images/gpath_filled_infinite_70_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_infinite_aa.8bit.png b/tests/test_images/gpath_filled_infinite_aa.8bit.png index d832849b13..7b6d2fe2b6 100644 Binary files a/tests/test_images/gpath_filled_infinite_aa.8bit.png and b/tests/test_images/gpath_filled_infinite_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_left_clipped.8bit.png b/tests/test_images/gpath_filled_left_clipped.8bit.png new file mode 100644 index 0000000000..b384e172ab Binary files /dev/null and b/tests/test_images/gpath_filled_left_clipped.8bit.png differ diff --git a/tests/test_images/gpath_filled_left_clipped_aa.8bit.png b/tests/test_images/gpath_filled_left_clipped_aa.8bit.png new file mode 100644 index 0000000000..b384e172ab Binary files /dev/null and b/tests/test_images/gpath_filled_left_clipped_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_right_clipped.8bit.png b/tests/test_images/gpath_filled_right_clipped.8bit.png new file mode 100644 index 0000000000..e3cf9ed3da Binary files /dev/null and b/tests/test_images/gpath_filled_right_clipped.8bit.png differ diff --git a/tests/test_images/gpath_filled_right_clipped_aa.8bit.png b/tests/test_images/gpath_filled_right_clipped_aa.8bit.png new file mode 100644 index 0000000000..e3cf9ed3da Binary files /dev/null and b/tests/test_images/gpath_filled_right_clipped_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_single_duplicate_aa.8bit.png b/tests/test_images/gpath_filled_single_duplicate_aa.8bit.png new file mode 100644 index 0000000000..2caea57477 Binary files /dev/null and b/tests/test_images/gpath_filled_single_duplicate_aa.8bit.png differ diff --git a/tests/test_images/gpath_filled_top_clipped.8bit.png b/tests/test_images/gpath_filled_top_clipped.8bit.png new file mode 100644 index 0000000000..c5bee1cb36 Binary files /dev/null and b/tests/test_images/gpath_filled_top_clipped.8bit.png differ diff --git a/tests/test_images/gpath_filled_top_clipped_aa.8bit.png b/tests/test_images/gpath_filled_top_clipped_aa.8bit.png new file mode 100644 index 0000000000..c5bee1cb36 Binary files /dev/null and b/tests/test_images/gpath_filled_top_clipped_aa.8bit.png differ diff --git a/tests/test_images/left_flip_first_frame_clipping~spalding.png b/tests/test_images/left_flip_first_frame_clipping~spalding.png deleted file mode 100644 index c65c6ee6b2..0000000000 Binary files a/tests/test_images/left_flip_first_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/left_flip_first_quarter_frame_clipping~spalding.png b/tests/test_images/left_flip_first_quarter_frame_clipping~spalding.png deleted file mode 100644 index 3f24ccfb5c..0000000000 Binary files a/tests/test_images/left_flip_first_quarter_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/left_flip_half_frame_clipping~spalding.png b/tests/test_images/left_flip_half_frame_clipping~spalding.png deleted file mode 100644 index 90d3cf4e61..0000000000 Binary files a/tests/test_images/left_flip_half_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/left_flip_last_frame_clipping~spalding.png b/tests/test_images/left_flip_last_frame_clipping~spalding.png deleted file mode 100644 index f9bd71cfdb..0000000000 Binary files a/tests/test_images/left_flip_last_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/left_flip_third_quarter_frame_clipping~spalding.png b/tests/test_images/left_flip_third_quarter_frame_clipping~spalding.png deleted file mode 100644 index edb001edd1..0000000000 Binary files a/tests/test_images/left_flip_third_quarter_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/move_horizontal_left_filled~spalding.png b/tests/test_images/move_horizontal_left_filled~spalding.png deleted file mode 100644 index 1652540a1a..0000000000 Binary files a/tests/test_images/move_horizontal_left_filled~spalding.png and /dev/null differ diff --git a/tests/test_images/move_horizontal_left~spalding.png b/tests/test_images/move_horizontal_left~spalding.png deleted file mode 100644 index fe46029aa3..0000000000 Binary files a/tests/test_images/move_horizontal_left~spalding.png and /dev/null differ diff --git a/tests/test_images/move_horizontal_right_too_far~spalding.png b/tests/test_images/move_horizontal_right_too_far~spalding.png deleted file mode 100644 index fe46029aa3..0000000000 Binary files a/tests/test_images/move_horizontal_right_too_far~spalding.png and /dev/null differ diff --git a/tests/test_images/move_horizontal_right~spalding.png b/tests/test_images/move_horizontal_right~spalding.png deleted file mode 100644 index dcb1a8e39e..0000000000 Binary files a/tests/test_images/move_horizontal_right~spalding.png and /dev/null differ diff --git a/tests/test_images/off_screen_left_aa_clipping~spalding.png b/tests/test_images/off_screen_left_aa_clipping~spalding.png deleted file mode 100644 index 08e60fdeb9..0000000000 Binary files a/tests/test_images/off_screen_left_aa_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/off_screen_right_aa_clipping~spalding.png b/tests/test_images/off_screen_right_aa_clipping~spalding.png deleted file mode 100644 index ac3abff83a..0000000000 Binary files a/tests/test_images/off_screen_right_aa_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/right_flip_first_frame_clipping~spalding.png b/tests/test_images/right_flip_first_frame_clipping~spalding.png deleted file mode 100644 index c65c6ee6b2..0000000000 Binary files a/tests/test_images/right_flip_first_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/right_flip_first_quarter_frame_clipping~spalding.png b/tests/test_images/right_flip_first_quarter_frame_clipping~spalding.png deleted file mode 100644 index 00c0fed5cd..0000000000 Binary files a/tests/test_images/right_flip_first_quarter_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/right_flip_half_frame_clipping~spalding.png b/tests/test_images/right_flip_half_frame_clipping~spalding.png deleted file mode 100644 index ec56ebaa5b..0000000000 Binary files a/tests/test_images/right_flip_half_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/right_flip_last_frame_clipping~spalding.png b/tests/test_images/right_flip_last_frame_clipping~spalding.png deleted file mode 100644 index f9bd71cfdb..0000000000 Binary files a/tests/test_images/right_flip_last_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/right_flip_third_quarter_frame_clipping~spalding.png b/tests/test_images/right_flip_third_quarter_frame_clipping~spalding.png deleted file mode 100644 index a8fa56e1ca..0000000000 Binary files a/tests/test_images/right_flip_third_quarter_frame_clipping~spalding.png and /dev/null differ diff --git a/tests/test_images/stroke_circle_offset_aa_r16_clip_nxny.8bit.png b/tests/test_images/stroke_circle_offset_aa_r16_clip_nxny.8bit.png new file mode 100644 index 0000000000..7471dc13b3 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r16_clip_nxny.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r16_clip_xy.8bit.png b/tests/test_images/stroke_circle_offset_aa_r16_clip_xy.8bit.png new file mode 100644 index 0000000000..3768b1f9a4 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r16_clip_xy.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r16_no_clip.8bit.png b/tests/test_images/stroke_circle_offset_aa_r16_no_clip.8bit.png new file mode 100644 index 0000000000..331072861c Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r16_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r1_clip_nxny.8bit.png b/tests/test_images/stroke_circle_offset_aa_r1_clip_nxny.8bit.png new file mode 100644 index 0000000000..2b3d748403 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r1_clip_nxny.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r1_clip_xy.8bit.png b/tests/test_images/stroke_circle_offset_aa_r1_clip_xy.8bit.png new file mode 100644 index 0000000000..f9ffddb9b5 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r1_clip_xy.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r1_no_clip.8bit.png b/tests/test_images/stroke_circle_offset_aa_r1_no_clip.8bit.png new file mode 100644 index 0000000000..f9ffddb9b5 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r1_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r8_clip_nxny.8bit.png b/tests/test_images/stroke_circle_offset_aa_r8_clip_nxny.8bit.png new file mode 100644 index 0000000000..3907926881 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r8_clip_nxny.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r8_clip_xy.8bit.png b/tests/test_images/stroke_circle_offset_aa_r8_clip_xy.8bit.png new file mode 100644 index 0000000000..c2d6198184 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r8_clip_xy.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_aa_r8_no_clip.8bit.png b/tests/test_images/stroke_circle_offset_aa_r8_no_clip.8bit.png new file mode 100644 index 0000000000..c2d6198184 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_aa_r8_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quad_bottom_left.8bit.png b/tests/test_images/stroke_circle_offset_r8_quad_bottom_left.8bit.png new file mode 100644 index 0000000000..c408db9f1e Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quad_bottom_left.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quad_bottom_right.8bit.png b/tests/test_images/stroke_circle_offset_r8_quad_bottom_right.8bit.png new file mode 100644 index 0000000000..c2d7593e3e Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quad_bottom_right.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quad_top_left.8bit.png b/tests/test_images/stroke_circle_offset_r8_quad_top_left.8bit.png new file mode 100644 index 0000000000..1e19943189 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quad_top_left.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quad_top_right.8bit.png b/tests/test_images/stroke_circle_offset_r8_quad_top_right.8bit.png new file mode 100644 index 0000000000..bfd378840c Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quad_top_right.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quads_bottom.8bit.png b/tests/test_images/stroke_circle_offset_r8_quads_bottom.8bit.png new file mode 100644 index 0000000000..16a7629b34 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quads_bottom.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quads_left.8bit.png b/tests/test_images/stroke_circle_offset_r8_quads_left.8bit.png new file mode 100644 index 0000000000..4467c33ebb Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quads_left.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quads_right.8bit.png b/tests/test_images/stroke_circle_offset_r8_quads_right.8bit.png new file mode 100644 index 0000000000..2176c84f0a Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quads_right.8bit.png differ diff --git a/tests/test_images/stroke_circle_offset_r8_quads_top.8bit.png b/tests/test_images/stroke_circle_offset_r8_quads_top.8bit.png new file mode 100644 index 0000000000..ad945064a5 Binary files /dev/null and b/tests/test_images/stroke_circle_offset_r8_quads_top.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r0_no_clip.8bit.png b/tests/test_images/stroke_circle_origin_aa_r0_no_clip.8bit.png new file mode 100644 index 0000000000..33e2b18b61 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r0_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r16_clip_nxny.8bit.png b/tests/test_images/stroke_circle_origin_aa_r16_clip_nxny.8bit.png new file mode 100644 index 0000000000..76a233c048 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r16_clip_nxny.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r16_clip_xy.8bit.png b/tests/test_images/stroke_circle_origin_aa_r16_clip_xy.8bit.png new file mode 100644 index 0000000000..934a6246e2 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r16_clip_xy.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r16_no_clip.8bit.png b/tests/test_images/stroke_circle_origin_aa_r16_no_clip.8bit.png new file mode 100644 index 0000000000..bacb247653 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r16_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r1_clip_nxny.8bit.png b/tests/test_images/stroke_circle_origin_aa_r1_clip_nxny.8bit.png new file mode 100644 index 0000000000..6f4d2974a1 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r1_clip_nxny.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r1_clip_xy.8bit.png b/tests/test_images/stroke_circle_origin_aa_r1_clip_xy.8bit.png new file mode 100644 index 0000000000..f9ffddb9b5 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r1_clip_xy.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r1_no_clip.8bit.png b/tests/test_images/stroke_circle_origin_aa_r1_no_clip.8bit.png new file mode 100644 index 0000000000..f9ffddb9b5 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r1_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r2_no_clip.8bit.png b/tests/test_images/stroke_circle_origin_aa_r2_no_clip.8bit.png new file mode 100644 index 0000000000..6f4d2974a1 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r2_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r3_no_clip.8bit.png b/tests/test_images/stroke_circle_origin_aa_r3_no_clip.8bit.png new file mode 100644 index 0000000000..5582b8796e Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r3_no_clip.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r8_clip_nxny.8bit.png b/tests/test_images/stroke_circle_origin_aa_r8_clip_nxny.8bit.png new file mode 100644 index 0000000000..9e0fe320d2 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r8_clip_nxny.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r8_clip_xy.8bit.png b/tests/test_images/stroke_circle_origin_aa_r8_clip_xy.8bit.png new file mode 100644 index 0000000000..2016979f01 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r8_clip_xy.8bit.png differ diff --git a/tests/test_images/stroke_circle_origin_aa_r8_no_clip.8bit.png b/tests/test_images/stroke_circle_origin_aa_r8_no_clip.8bit.png new file mode 100644 index 0000000000..c2d6198184 Binary files /dev/null and b/tests/test_images/stroke_circle_origin_aa_r8_no_clip.8bit.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~tintin.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__thin_display_mode_one_item~tintin.png rename to tests/test_images/test_action_menu_window__thin_display_mode_one_item~asterix.png diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~gabbro.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~gabbro.png new file mode 100644 index 0000000000..118dbdff88 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~obelix.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~obelix.png new file mode 100644 index 0000000000..1b2ffd7e74 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~robert.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~robert.png deleted file mode 100644 index 9ea9e90b0f..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~snowy.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~snowy.png deleted file mode 100644 index c66d44e770..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~spalding.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_item~spalding.png deleted file mode 100644 index 649535040b..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_one_item~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~tintin.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__thin_display_mode_one_row~tintin.png rename to tests/test_images/test_action_menu_window__thin_display_mode_one_row~asterix.png diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~gabbro.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~gabbro.png new file mode 100644 index 0000000000..684facbfc0 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~obelix.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~obelix.png new file mode 100644 index 0000000000..fa92e34ba8 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~robert.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~robert.png deleted file mode 100644 index a5880c4d12..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~snowy.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~snowy.png deleted file mode 100644 index 286c81be98..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~spalding.png b/tests/test_images/test_action_menu_window__thin_display_mode_one_row~spalding.png deleted file mode 100644 index bfd4457eaa..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_one_row~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~tintin.png b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__thin_display_mode_two_row~tintin.png rename to tests/test_images/test_action_menu_window__thin_display_mode_two_row~asterix.png diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~gabbro.png b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~gabbro.png new file mode 100644 index 0000000000..7229025bfb Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~obelix.png b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~obelix.png new file mode 100644 index 0000000000..914c428b38 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~robert.png b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~robert.png deleted file mode 100644 index 1ab954d166..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~snowy.png b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~snowy.png deleted file mode 100644 index d1a9a02003..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~spalding.png b/tests/test_images/test_action_menu_window__thin_display_mode_two_row~spalding.png deleted file mode 100644 index a10d82376e..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_two_row~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~tintin.png b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~tintin.png rename to tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~asterix.png diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~gabbro.png b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~gabbro.png new file mode 100644 index 0000000000..8addeb4d98 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~obelix.png b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~obelix.png new file mode 100644 index 0000000000..d30c247c68 Binary files /dev/null and b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~robert.png b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~robert.png deleted file mode 100644 index 67fe3dd666..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~snowy.png b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~snowy.png deleted file mode 100644 index a0c07a19d5..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~spalding.png b/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~spalding.png deleted file mode 100644 index b6fede6218..0000000000 Binary files a/tests/test_images/test_action_menu_window__thin_display_mode_with_emoji~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~tintin.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~tintin.png rename to tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~asterix.png diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~gabbro.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~gabbro.png new file mode 100644 index 0000000000..1fdd6cf221 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~obelix.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~obelix.png new file mode 100644 index 0000000000..87f1c843dd Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~robert.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~robert.png deleted file mode 100644 index 895fedcc11..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~snowy.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~snowy.png deleted file mode 100644 index 48065fc2dd..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~spalding.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~spalding.png deleted file mode 100644 index c5eadb5d52..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels_hyphenated~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~tintin.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~tintin.png rename to tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~asterix.png diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~gabbro.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~gabbro.png new file mode 100644 index 0000000000..fa03c98f7d Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~obelix.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~obelix.png new file mode 100644 index 0000000000..3cde559551 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~robert.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~robert.png deleted file mode 100644 index a7ccc8f937..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~snowy.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~snowy.png deleted file mode 100644 index 607be4b8a6..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~spalding.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~spalding.png deleted file mode 100644 index 88d5abce2c..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron_and_long_labels~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~tintin.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~tintin.png rename to tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~asterix.png diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~gabbro.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~gabbro.png new file mode 100644 index 0000000000..d5d25f2296 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~obelix.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~obelix.png new file mode 100644 index 0000000000..95dddb2097 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~robert.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~robert.png deleted file mode 100644 index 8586342d83..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~snowy.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~snowy.png deleted file mode 100644 index 6e68ca5c92..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~spalding.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~spalding.png deleted file mode 100644 index 756c2a3761..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_chevron~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~tintin.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~tintin.png rename to tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~asterix.png diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~gabbro.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~gabbro.png new file mode 100644 index 0000000000..8c7c2b2c52 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~obelix.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~obelix.png new file mode 100644 index 0000000000..36c2b34a15 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~robert.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~robert.png deleted file mode 100644 index 9b6bdcdee7..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~snowy.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~snowy.png deleted file mode 100644 index 2f8e87320e..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~spalding.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~spalding.png deleted file mode 100644 index 688f72ec16..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_just_titles~spalding.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~tintin.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~asterix.png similarity index 100% rename from tests/test_images/test_action_menu_window__wide_display_mode_with_separator~tintin.png rename to tests/test_images/test_action_menu_window__wide_display_mode_with_separator~asterix.png diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~gabbro.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~gabbro.png new file mode 100644 index 0000000000..3497b853a1 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~gabbro.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~obelix.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~obelix.png new file mode 100644 index 0000000000..b993f6ae08 Binary files /dev/null and b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~obelix.png differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~robert.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~robert.png deleted file mode 100644 index f65baa3fc8..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~robert.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~snowy.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~snowy.png deleted file mode 100644 index e1598f08f3..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~snowy.png and /dev/null differ diff --git a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~spalding.png b/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~spalding.png deleted file mode 100644 index de4b6b5d27..0000000000 Binary files a/tests/test_images/test_action_menu_window__wide_display_mode_with_separator~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__8_bit_converted_circular_offset~spalding.png b/tests/test_images/test_bitblt_circular__8_bit_converted_circular_offset~spalding.png deleted file mode 100644 index 988763971d..0000000000 Binary files a/tests/test_images/test_bitblt_circular__8_bit_converted_circular_offset~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__8_bit_converted_circular~spalding.png b/tests/test_images/test_bitblt_circular__8_bit_converted_circular~spalding.png deleted file mode 100644 index c0746ad0be..0000000000 Binary files a/tests/test_images/test_bitblt_circular__8_bit_converted_circular~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__color_1_bit~spalding.png b/tests/test_images/test_bitblt_circular__color_1_bit~spalding.png deleted file mode 100644 index 6f95176028..0000000000 Binary files a/tests/test_images/test_bitblt_circular__color_1_bit~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__color_2_bit~spalding.png b/tests/test_images/test_bitblt_circular__color_2_bit~spalding.png deleted file mode 100644 index 7f0b4cb6e8..0000000000 Binary files a/tests/test_images/test_bitblt_circular__color_2_bit~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__color_4_bit~spalding.png b/tests/test_images/test_bitblt_circular__color_4_bit~spalding.png deleted file mode 100644 index 9e8ef14a76..0000000000 Binary files a/tests/test_images/test_bitblt_circular__color_4_bit~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__color_8_bit_tiling_palettized~spalding.png b/tests/test_images/test_bitblt_circular__color_8_bit_tiling_palettized~spalding.png deleted file mode 100644 index 92b0652353..0000000000 Binary files a/tests/test_images/test_bitblt_circular__color_8_bit_tiling_palettized~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__color_8_bit_tiling~spalding.png b/tests/test_images/test_bitblt_circular__color_8_bit_tiling~spalding.png deleted file mode 100644 index 93b6bbb3e3..0000000000 Binary files a/tests/test_images/test_bitblt_circular__color_8_bit_tiling~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__color_8_bit~spalding.png b/tests/test_images/test_bitblt_circular__color_8_bit~spalding.png deleted file mode 100644 index c0746ad0be..0000000000 Binary files a/tests/test_images/test_bitblt_circular__color_8_bit~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__spiral~spalding.png b/tests/test_images/test_bitblt_circular__spiral~spalding.png deleted file mode 100644 index c0746ad0be..0000000000 Binary files a/tests/test_images/test_bitblt_circular__spiral~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__tile_palettized~spalding.png b/tests/test_images/test_bitblt_circular__tile_palettized~spalding.png deleted file mode 100644 index 6e4d61ebfb..0000000000 Binary files a/tests/test_images/test_bitblt_circular__tile_palettized~spalding.png and /dev/null differ diff --git a/tests/test_images/test_bitblt_circular__tile~spalding.png b/tests/test_images/test_bitblt_circular__tile~spalding.png deleted file mode 100644 index 2bfd751dab..0000000000 Binary files a/tests/test_images/test_bitblt_circular__tile~spalding.png and /dev/null differ diff --git a/tests/test_images/test_emoji_fonts__gothic_14_emoji.png b/tests/test_images/test_emoji_fonts__gothic_14_emoji.png index 0de9d9b034..3241909082 100644 Binary files a/tests/test_images/test_emoji_fonts__gothic_14_emoji.png and b/tests/test_images/test_emoji_fonts__gothic_14_emoji.png differ diff --git a/tests/test_images/test_emoji_fonts__gothic_28_emoji.png b/tests/test_images/test_emoji_fonts__gothic_28_emoji.png index c70c81b34e..043132439d 100644 Binary files a/tests/test_images/test_emoji_fonts__gothic_28_emoji.png and b/tests/test_images/test_emoji_fonts__gothic_28_emoji.png differ diff --git a/tests/test_images/test_expandable_dialog__dismiss_tutorial_portuguese_orphan~gabbro.png b/tests/test_images/test_expandable_dialog__dismiss_tutorial_portuguese_orphan~gabbro.png new file mode 100644 index 0000000000..23e69f8295 Binary files /dev/null and b/tests/test_images/test_expandable_dialog__dismiss_tutorial_portuguese_orphan~gabbro.png differ diff --git a/tests/test_images/test_expandable_dialog__dismiss_tutorial_portuguese_orphan~spalding.png b/tests/test_images/test_expandable_dialog__dismiss_tutorial_portuguese_orphan~spalding.png deleted file mode 100644 index 3079714846..0000000000 Binary files a/tests/test_images/test_expandable_dialog__dismiss_tutorial_portuguese_orphan~spalding.png and /dev/null differ diff --git a/tests/test_images/test_gdraw_command_transforms__segmented_scale.8bit.png b/tests/test_images/test_gdraw_command_transforms__segmented_scale.8bit.png index b22a3a8872..811633b357 100644 Binary files a/tests/test_images/test_gdraw_command_transforms__segmented_scale.8bit.png and b/tests/test_images/test_gdraw_command_transforms__segmented_scale.8bit.png differ diff --git a/tests/test_images/test_gdraw_command_transforms__to_square.8bit.png b/tests/test_images/test_gdraw_command_transforms__to_square.8bit.png index c8892f6dc5..6daf35f853 100644 Binary files a/tests/test_images/test_gdraw_command_transforms__to_square.8bit.png and b/tests/test_images/test_gdraw_command_transforms__to_square.8bit.png differ diff --git a/tests/test_images/test_graphics_context_mask__apply_assign_horizontal_line_delta_raw~snowy.png b/tests/test_images/test_graphics_context_mask__apply_assign_horizontal_line_delta_raw~snowy.png deleted file mode 100644 index 53eeb252c3..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__apply_assign_horizontal_line_delta_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__apply_assign_horizontal_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__apply_assign_horizontal_line_raw~snowy.png deleted file mode 100644 index 3c60144fb1..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__apply_assign_horizontal_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__apply_assign_vertical_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__apply_assign_vertical_line_raw~snowy.png deleted file mode 100644 index faa220ea7f..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__apply_assign_vertical_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__apply_blend_horizontal_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__apply_blend_horizontal_line_raw~snowy.png deleted file mode 100644 index 19bcd34390..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__apply_blend_horizontal_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__apply_blend_vertical_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__apply_blend_vertical_line_raw~snowy.png deleted file mode 100644 index 6ec37fe350..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__apply_blend_vertical_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__record_assign_horizontal_line_delta_raw~snowy.png b/tests/test_images/test_graphics_context_mask__record_assign_horizontal_line_delta_raw~snowy.png deleted file mode 100644 index db02340bac..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__record_assign_horizontal_line_delta_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__record_assign_horizontal_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__record_assign_horizontal_line_raw~snowy.png deleted file mode 100644 index 4be3d58b3e..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__record_assign_horizontal_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__record_assign_vertical_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__record_assign_vertical_line_raw~snowy.png deleted file mode 100644 index 74fd61a3f8..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__record_assign_vertical_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__record_blend_horizontal_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__record_blend_horizontal_line_raw~snowy.png deleted file mode 100644 index 7fafb1efd1..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__record_blend_horizontal_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_context_mask__record_blend_vertical_line_raw~snowy.png b/tests/test_images/test_graphics_context_mask__record_blend_vertical_line_raw~snowy.png deleted file mode 100644 index 2ad9fbde51..0000000000 Binary files a/tests/test_images/test_graphics_context_mask__record_blend_vertical_line_raw~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~obelix.png new file mode 100644 index 0000000000..faad60c2bc Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~snowy.png deleted file mode 100644 index f920c840cc..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_and_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~obelix.png new file mode 100644 index 0000000000..4d37aa144a Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~snowy.png deleted file mode 100644 index 5d22ceb930..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_assign_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~obelix.png new file mode 100644 index 0000000000..39354b6ba7 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~snowy.png deleted file mode 100644 index 6b026f04cf..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_assign_2bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_4bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_4bitTrns~obelix.png new file mode 100644 index 0000000000..87139e46c1 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_assign_4bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_4bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_4bitTrns~snowy.png deleted file mode 100644 index fba1f2c5b9..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_assign_4bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_8bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_8bitTrns~obelix.png new file mode 100644 index 0000000000..90148beb49 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_assign_8bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_8bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_8bitTrns~snowy.png deleted file mode 100644 index 45300986c9..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_assign_8bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~obelix.png new file mode 100644 index 0000000000..f073ea5ea5 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~snowy.png deleted file mode 100644 index b4cd1514af..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_assign_inverted_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~obelix.png new file mode 100644 index 0000000000..acf38e5fdf Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~snowy.png deleted file mode 100644 index 471aa6b1f0..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_clear_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~obelix.png new file mode 100644 index 0000000000..7c2068f81c Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~snowy.png deleted file mode 100644 index 8583a221f9..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_or_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~obelix.png new file mode 100644 index 0000000000..5e1393bc73 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~snowy.png deleted file mode 100644 index feef545eb6..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_set_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~obelix.png new file mode 100644 index 0000000000..d129c4ef50 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~snowy.png deleted file mode 100644 index dc0c4d68a7..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_set_2bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_4bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_4bitTrns~obelix.png new file mode 100644 index 0000000000..8018c6200e Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_set_4bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_4bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_4bitTrns~snowy.png deleted file mode 100644 index d6ca870440..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_set_4bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_8bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_8bitTrns~obelix.png new file mode 100644 index 0000000000..f50d339735 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_set_8bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_set_8bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_set_8bitTrns~snowy.png deleted file mode 100644 index be9542fd2f..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_set_8bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~obelix.png new file mode 100644 index 0000000000..dcad085cc3 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~snowy.png deleted file mode 100644 index c9983dd900..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~obelix.png new file mode 100644 index 0000000000..7b2d78a4e2 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~snowy.png deleted file mode 100644 index 9408915a80..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_2bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_4bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_4bitTrns~obelix.png new file mode 100644 index 0000000000..61ea038785 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_4bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_4bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_4bitTrns~snowy.png deleted file mode 100644 index 780fb4a060..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_4bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_8bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_8bitTrns~obelix.png new file mode 100644 index 0000000000..a7d0497646 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_8bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_8bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_8bitTrns~snowy.png deleted file mode 100644 index bd2abbdc00..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_8bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~obelix.png new file mode 100644 index 0000000000..faad60c2bc Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~snowy.png deleted file mode 100644 index f920c840cc..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~obelix.png new file mode 100644 index 0000000000..e6240b796d Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~snowy.png deleted file mode 100644 index cda6a8c081..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_2bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_4bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_4bitTrns~obelix.png new file mode 100644 index 0000000000..a0ff91dd33 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_4bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_4bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_4bitTrns~snowy.png deleted file mode 100644 index 5897a5c881..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_4bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_8bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_8bitTrns~obelix.png new file mode 100644 index 0000000000..6d5284bb56 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_8bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_8bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_8bitTrns~snowy.png deleted file mode 100644 index 07d11f1ff0..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_opaque_8bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~obelix.png new file mode 100644 index 0000000000..f3f9e9d3c9 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~snowy.png deleted file mode 100644 index 605ad4605c..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~obelix.png new file mode 100644 index 0000000000..2833f02d6b Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~snowy.png deleted file mode 100644 index f74680c235..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_2bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_4bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_4bitTrns~obelix.png new file mode 100644 index 0000000000..05c514c577 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_4bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_4bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_4bitTrns~snowy.png deleted file mode 100644 index 5cb9e56363..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_4bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_8bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_8bitTrns~obelix.png new file mode 100644 index 0000000000..8dfbe7229a Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_8bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_8bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_8bitTrns~snowy.png deleted file mode 100644 index d657e0fabf..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent_8bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~obelix.png new file mode 100644 index 0000000000..7e26256dac Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~snowy.png deleted file mode 100644 index a4385e8ba5..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_1bitBW~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~silk.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~silk.png rename to tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~obelix.png new file mode 100644 index 0000000000..1c5461df73 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~snowy.png deleted file mode 100644 index c151328cef..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_2bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_4bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_4bitTrns~obelix.png new file mode 100644 index 0000000000..ab7de594b4 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_4bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_4bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_4bitTrns~snowy.png deleted file mode 100644 index 8b80c8f9f2..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_4bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_8bitTrns~obelix.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_8bitTrns~obelix.png new file mode 100644 index 0000000000..62b683296c Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_8bitTrns~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_8bitTrns~snowy.png b/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_8bitTrns~snowy.png deleted file mode 100644 index 6b52d88384..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque_8bitTrns~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~obelix.png new file mode 100644 index 0000000000..ba0abc6bda Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~snowy.png deleted file mode 100644 index 88724a97f5..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_nx~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~obelix.png new file mode 100644 index 0000000000..fc2cbb34f7 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~snowy.png deleted file mode 100644 index 4cc20ceb12..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_ny~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~obelix.png new file mode 100644 index 0000000000..f8029d60da Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~snowy.png deleted file mode 100644 index 8405ca7c6a..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_x~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~obelix.png new file mode 100644 index 0000000000..672db63526 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~snowy.png deleted file mode 100644 index dd1c991362..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_across_y~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~obelix.png new file mode 100644 index 0000000000..57d90bedb7 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~snowy.png deleted file mode 100644 index 8ad159d67f..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_bitmap_layer_inside~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~obelix.png new file mode 100644 index 0000000000..8cf607d55a Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~snowy.png deleted file mode 100644 index 42cb88c3e0..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_nx~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~obelix.png new file mode 100644 index 0000000000..e8058f6d05 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~snowy.png deleted file mode 100644 index 5d1f84d407..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_ny~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~obelix.png new file mode 100644 index 0000000000..5fd78851c9 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~snowy.png deleted file mode 100644 index 1ce158055d..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_x~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~obelix.png new file mode 100644 index 0000000000..405942775c Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~snowy.png deleted file mode 100644 index 43d93fec1f..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_layer_across_y~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~silk.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~silk.png rename to tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~obelix.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~obelix.png new file mode 100644 index 0000000000..8ed5c871aa Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~snowy.png b/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~snowy.png deleted file mode 100644 index adff9d9c36..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__offset_layer_inside~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~obelix.png new file mode 100644 index 0000000000..6aea002a17 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~snowy.png deleted file mode 100644 index 715b488da0..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_nx~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~obelix.png new file mode 100644 index 0000000000..aaf1eb7891 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~snowy.png deleted file mode 100644 index 2f4931e692..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_ny~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~obelix.png new file mode 100644 index 0000000000..7faf8397ca Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~snowy.png deleted file mode 100644 index 19feb14940..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_x~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~obelix.png new file mode 100644 index 0000000000..b032fda174 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~snowy.png deleted file mode 100644 index 92e1adcb9d..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_across_y~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~obelix.png new file mode 100644 index 0000000000..c1c4720114 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~snowy.png deleted file mode 100644 index 5bcbb0ce07..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_bitmap_layer_inside~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~obelix.png new file mode 100644 index 0000000000..8a7cb035ad Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~snowy.png deleted file mode 100644 index f034890416..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_nx~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~obelix.png new file mode 100644 index 0000000000..74e21a12c7 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~snowy.png deleted file mode 100644 index a25fcde590..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_ny~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~obelix.png new file mode 100644 index 0000000000..7018756992 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~snowy.png deleted file mode 100644 index 930afe61df..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_x~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~obelix.png new file mode 100644 index 0000000000..c7ccf3f97c Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~snowy.png deleted file mode 100644 index 7dc2024732..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_layer_across_y~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~silk.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~asterix.png similarity index 100% rename from tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~silk.png rename to tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~asterix.png diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~obelix.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~obelix.png new file mode 100644 index 0000000000..7e809aefd8 Binary files /dev/null and b/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~obelix.png differ diff --git a/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~snowy.png b/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~snowy.png deleted file mode 100644 index b9af8bce9e..0000000000 Binary files a/tests/test_images/test_graphics_draw_bitmap__origin_layer_inside~snowy.png and /dev/null differ diff --git a/tests/test_images/test_graphics_draw_text_flow__avoid_repeat_text_to_avoid_orphans~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__avoid_repeat_text_to_avoid_orphans~gabbro.png new file mode 100644 index 0000000000..0c021c10f2 Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__avoid_repeat_text_to_avoid_orphans~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro.png new file mode 100644 index 0000000000..f54e4567e9 Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro__clipped~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro__clipped~gabbro.png new file mode 100644 index 0000000000..555928a209 Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro__clipped~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro__with_orphan~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro__with_orphan~gabbro.png new file mode 100644 index 0000000000..1c7cfa3d7b Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__draw_text_doom~gabbro__with_orphan~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__flow_no_paging.png b/tests/test_images/test_graphics_draw_text_flow__flow_no_paging.png index 7693e4d4c9..5544c46259 100644 Binary files a/tests/test_images/test_graphics_draw_text_flow__flow_no_paging.png and b/tests/test_images/test_graphics_draw_text_flow__flow_no_paging.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__flow_no_paging~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__flow_no_paging~gabbro.png new file mode 100644 index 0000000000..5544c46259 Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__flow_no_paging~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__no_infinite_loop2~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__no_infinite_loop2~gabbro.png new file mode 100644 index 0000000000..787c1600f8 Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__no_infinite_loop2~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__with_origin_non_zero~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__with_origin_non_zero~gabbro.png new file mode 100644 index 0000000000..5f7c57cf26 Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__with_origin_non_zero~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__with_origin_zero~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__with_origin_zero~gabbro.png new file mode 100644 index 0000000000..30eb943eee Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__with_origin_zero~gabbro.png differ diff --git a/tests/test_images/test_graphics_draw_text_flow__with_paging~gabbro.png b/tests/test_images/test_graphics_draw_text_flow__with_paging~gabbro.png new file mode 100644 index 0000000000..5fac568ebc Binary files /dev/null and b/tests/test_images/test_graphics_draw_text_flow__with_paging~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~silk.png b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~silk.png rename to tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~asterix.png diff --git a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~gabbro.png b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~gabbro.png new file mode 100644 index 0000000000..09f15e31a8 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~obelix.png b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~obelix.png new file mode 100644 index 0000000000..44c9bcd49b Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~obelix.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~snowy.png b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~snowy.png deleted file mode 100644 index a6207ca2b9..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~spalding.png b/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~spalding.png deleted file mode 100644 index 9cde4184ad..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_current_calories_and_distance~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~silk.png b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~silk.png rename to tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~asterix.png diff --git a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~gabbro.png b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~gabbro.png new file mode 100644 index 0000000000..3f1e4edede Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~obelix.png b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~obelix.png new file mode 100644 index 0000000000..747f51b335 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~obelix.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~snowy.png b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~snowy.png deleted file mode 100644 index d18b24d659..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~spalding.png b/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~spalding.png deleted file mode 100644 index 42e410bb1c..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_day_label_no_steps~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_calories~silk.png b/tests/test_images/test_health_activity_detail_card__render_no_calories~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_detail_card__render_no_calories~silk.png rename to tests/test_images/test_health_activity_detail_card__render_no_calories~asterix.png diff --git a/tests/test_images/test_health_activity_detail_card__render_no_calories~gabbro.png b/tests/test_images/test_health_activity_detail_card__render_no_calories~gabbro.png new file mode 100644 index 0000000000..8ccfe54ce8 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_no_calories~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_calories~obelix.png b/tests/test_images/test_health_activity_detail_card__render_no_calories~obelix.png new file mode 100644 index 0000000000..1aca5b3a5f Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_no_calories~obelix.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_calories~snowy.png b/tests/test_images/test_health_activity_detail_card__render_no_calories~snowy.png deleted file mode 100644 index b1d9aa0c16..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_no_calories~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_calories~spalding.png b/tests/test_images/test_health_activity_detail_card__render_no_calories~spalding.png deleted file mode 100644 index 97efdf57fe..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_no_calories~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_data~silk.png b/tests/test_images/test_health_activity_detail_card__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_detail_card__render_no_data~silk.png rename to tests/test_images/test_health_activity_detail_card__render_no_data~asterix.png diff --git a/tests/test_images/test_health_activity_detail_card__render_no_data~gabbro.png b/tests/test_images/test_health_activity_detail_card__render_no_data~gabbro.png new file mode 100644 index 0000000000..8ccfe54ce8 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_data~obelix.png b/tests/test_images/test_health_activity_detail_card__render_no_data~obelix.png new file mode 100644 index 0000000000..1aca5b3a5f Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_no_data~obelix.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_data~snowy.png b/tests/test_images/test_health_activity_detail_card__render_no_data~snowy.png deleted file mode 100644 index b1d9aa0c16..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_data~spalding.png b/tests/test_images/test_health_activity_detail_card__render_no_data~spalding.png deleted file mode 100644 index 97efdf57fe..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_distance~silk.png b/tests/test_images/test_health_activity_detail_card__render_no_distance~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_detail_card__render_no_distance~silk.png rename to tests/test_images/test_health_activity_detail_card__render_no_distance~asterix.png diff --git a/tests/test_images/test_health_activity_detail_card__render_no_distance~gabbro.png b/tests/test_images/test_health_activity_detail_card__render_no_distance~gabbro.png new file mode 100644 index 0000000000..8ccfe54ce8 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_no_distance~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_distance~obelix.png b/tests/test_images/test_health_activity_detail_card__render_no_distance~obelix.png new file mode 100644 index 0000000000..1aca5b3a5f Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_no_distance~obelix.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_distance~snowy.png b/tests/test_images/test_health_activity_detail_card__render_no_distance~snowy.png deleted file mode 100644 index b1d9aa0c16..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_no_distance~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_no_distance~spalding.png b/tests/test_images/test_health_activity_detail_card__render_no_distance~spalding.png deleted file mode 100644 index 97efdf57fe..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_no_distance~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_step_data~silk.png b/tests/test_images/test_health_activity_detail_card__render_step_data~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_detail_card__render_step_data~silk.png rename to tests/test_images/test_health_activity_detail_card__render_step_data~asterix.png diff --git a/tests/test_images/test_health_activity_detail_card__render_step_data~gabbro.png b/tests/test_images/test_health_activity_detail_card__render_step_data~gabbro.png new file mode 100644 index 0000000000..5cda25ac01 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_step_data~gabbro.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_step_data~obelix.png b/tests/test_images/test_health_activity_detail_card__render_step_data~obelix.png new file mode 100644 index 0000000000..348b18bfa8 Binary files /dev/null and b/tests/test_images/test_health_activity_detail_card__render_step_data~obelix.png differ diff --git a/tests/test_images/test_health_activity_detail_card__render_step_data~snowy.png b/tests/test_images/test_health_activity_detail_card__render_step_data~snowy.png deleted file mode 100644 index 2fdf4eef7e..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_step_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_detail_card__render_step_data~spalding.png b/tests/test_images/test_health_activity_detail_card__render_step_data~spalding.png deleted file mode 100644 index 4a42019aff..0000000000 Binary files a/tests/test_images/test_health_activity_detail_card__render_step_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__no_current_steps~silk.png b/tests/test_images/test_health_activity_summary_card__no_current_steps~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__no_current_steps~silk.png rename to tests/test_images/test_health_activity_summary_card__no_current_steps~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__no_current_steps~gabbro.png b/tests/test_images/test_health_activity_summary_card__no_current_steps~gabbro.png new file mode 100644 index 0000000000..e883dac5c4 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__no_current_steps~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__no_current_steps~obelix.png b/tests/test_images/test_health_activity_summary_card__no_current_steps~obelix.png new file mode 100644 index 0000000000..972a3ecb73 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__no_current_steps~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__no_current_steps~snowy.png b/tests/test_images/test_health_activity_summary_card__no_current_steps~snowy.png deleted file mode 100644 index ebfd8cd348..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__no_current_steps~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__no_current_steps~spalding.png b/tests/test_images/test_health_activity_summary_card__no_current_steps~spalding.png deleted file mode 100644 index b3d7e4af56..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__no_current_steps~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_above_expected~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_above_expected~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~gabbro.png new file mode 100644 index 0000000000..adcdd82904 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~obelix.png new file mode 100644 index 0000000000..7a25e979c9 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~snowy.png deleted file mode 100644 index 81ca3e1cc5..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_above_expected~spalding.png deleted file mode 100644 index 5f23385099..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_expected~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_above_typical1~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_above_typical1~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~gabbro.png new file mode 100644 index 0000000000..cc4b860dd5 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~obelix.png new file mode 100644 index 0000000000..727539175f Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~snowy.png deleted file mode 100644 index 228a62b9ab..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~spalding.png deleted file mode 100644 index c3dc14c1af..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_above_typical2~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_above_typical2~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~gabbro.png new file mode 100644 index 0000000000..0d8c074d15 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~obelix.png new file mode 100644 index 0000000000..8ff76109f7 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~snowy.png deleted file mode 100644 index 2ed308bc89..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~spalding.png deleted file mode 100644 index cdd42ebf32..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_above_typical3~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_above_typical3~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~gabbro.png new file mode 100644 index 0000000000..c9b41c225d Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~obelix.png new file mode 100644 index 0000000000..1468711934 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~snowy.png deleted file mode 100644 index 297d63bc0b..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~spalding.png deleted file mode 100644 index ff85ddd266..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical3~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_above_typical4~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_above_typical4~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~gabbro.png new file mode 100644 index 0000000000..08e6d52749 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~obelix.png new file mode 100644 index 0000000000..f20ae9982c Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~snowy.png deleted file mode 100644 index 66edb60b90..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~spalding.png deleted file mode 100644 index b591fa692b..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical4~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_above_typical5~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_above_typical5~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~gabbro.png new file mode 100644 index 0000000000..acead582ad Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~obelix.png new file mode 100644 index 0000000000..76a6e2f302 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~snowy.png deleted file mode 100644 index 0b1b589011..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~spalding.png deleted file mode 100644 index 6eff6d728b..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_above_typical5~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~gabbro.png new file mode 100644 index 0000000000..aaba42e7c2 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~obelix.png new file mode 100644 index 0000000000..126b3f1595 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~snowy.png deleted file mode 100644 index cda7934b8c..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~spalding.png deleted file mode 100644 index 61617ce9d1..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~gabbro.png new file mode 100644 index 0000000000..599a87e0f8 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~obelix.png new file mode 100644 index 0000000000..ff9495ad18 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~snowy.png deleted file mode 100644 index d1a3b3e8c2..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~spalding.png deleted file mode 100644 index 69328bbdf0..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~gabbro.png new file mode 100644 index 0000000000..62a9f7f11d Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~obelix.png new file mode 100644 index 0000000000..070423444e Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~snowy.png deleted file mode 100644 index 08595fc7ae..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~spalding.png deleted file mode 100644 index 03dd2da74f..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical3~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~gabbro.png new file mode 100644 index 0000000000..33a4c68586 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~obelix.png new file mode 100644 index 0000000000..be850c32da Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~snowy.png deleted file mode 100644 index 2497669680..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~spalding.png deleted file mode 100644 index 1bb0251550..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical4~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~gabbro.png new file mode 100644 index 0000000000..f227e09c69 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~obelix.png new file mode 100644 index 0000000000..db6ebecf16 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~snowy.png deleted file mode 100644 index 38813aec9f..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~spalding.png deleted file mode 100644 index 4e31902406..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_behind_typical5~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~silk.png b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_current_equals_typical~silk.png rename to tests/test_images/test_health_activity_summary_card__render_current_equals_typical~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~gabbro.png new file mode 100644 index 0000000000..555d5f59a0 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~obelix.png b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~obelix.png new file mode 100644 index 0000000000..51bc6d422d Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~snowy.png b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~snowy.png deleted file mode 100644 index 7ae72c40cf..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~spalding.png b/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~spalding.png deleted file mode 100644 index 8c6192c9bb..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_current_equals_typical~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_no_data~silk.png b/tests/test_images/test_health_activity_summary_card__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_health_activity_summary_card__render_no_data~silk.png rename to tests/test_images/test_health_activity_summary_card__render_no_data~asterix.png diff --git a/tests/test_images/test_health_activity_summary_card__render_no_data~gabbro.png b/tests/test_images/test_health_activity_summary_card__render_no_data~gabbro.png new file mode 100644 index 0000000000..f65106d279 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_no_data~obelix.png b/tests/test_images/test_health_activity_summary_card__render_no_data~obelix.png new file mode 100644 index 0000000000..6fe36a8878 Binary files /dev/null and b/tests/test_images/test_health_activity_summary_card__render_no_data~obelix.png differ diff --git a/tests/test_images/test_health_activity_summary_card__render_no_data~snowy.png b/tests/test_images/test_health_activity_summary_card__render_no_data~snowy.png deleted file mode 100644 index 82c390f1f1..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_activity_summary_card__render_no_data~spalding.png b/tests/test_images/test_health_activity_summary_card__render_no_data~spalding.png deleted file mode 100644 index 6a787910a4..0000000000 Binary files a/tests/test_images/test_health_activity_summary_card__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_card_view__render_indicators~silk.png b/tests/test_images/test_health_card_view__render_indicators~asterix.png similarity index 100% rename from tests/test_images/test_health_card_view__render_indicators~silk.png rename to tests/test_images/test_health_card_view__render_indicators~asterix.png diff --git a/tests/test_images/test_health_card_view__render_indicators~gabbro.png b/tests/test_images/test_health_card_view__render_indicators~gabbro.png new file mode 100644 index 0000000000..86e3b1454c Binary files /dev/null and b/tests/test_images/test_health_card_view__render_indicators~gabbro.png differ diff --git a/tests/test_images/test_health_card_view__render_indicators~obelix.png b/tests/test_images/test_health_card_view__render_indicators~obelix.png new file mode 100644 index 0000000000..eaa71a7612 Binary files /dev/null and b/tests/test_images/test_health_card_view__render_indicators~obelix.png differ diff --git a/tests/test_images/test_health_card_view__render_indicators~snowy.png b/tests/test_images/test_health_card_view__render_indicators~snowy.png deleted file mode 100644 index 2d4a9b4837..0000000000 Binary files a/tests/test_images/test_health_card_view__render_indicators~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_card_view__render_indicators~spalding.png b/tests/test_images/test_health_card_view__render_indicators~spalding.png deleted file mode 100644 index 9cbc07e599..0000000000 Binary files a/tests/test_images/test_health_card_view__render_indicators~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~silk.png b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_bg_and_zone_colors~silk.png rename to tests/test_images/test_health_detail_card__render_bg_and_zone_colors~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~gabbro.png b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~gabbro.png new file mode 100644 index 0000000000..03a9054ebc Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~obelix.png b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~obelix.png new file mode 100644 index 0000000000..f6428e18db Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~snowy.png b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~snowy.png deleted file mode 100644 index 4d535dcfa2..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~spalding.png b/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~spalding.png deleted file mode 100644 index c74dcfc4a3..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_bg_and_zone_colors~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_crown~silk.png b/tests/test_images/test_health_detail_card__render_crown~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_crown~silk.png rename to tests/test_images/test_health_detail_card__render_crown~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_crown~gabbro.png b/tests/test_images/test_health_detail_card__render_crown~gabbro.png new file mode 100644 index 0000000000..88a5f5d397 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_crown~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_crown~obelix.png b/tests/test_images/test_health_detail_card__render_crown~obelix.png new file mode 100644 index 0000000000..4f6a812c03 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_crown~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_crown~snowy.png b/tests/test_images/test_health_detail_card__render_crown~snowy.png deleted file mode 100644 index 3c22a0eed9..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_crown~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_crown~spalding.png b/tests/test_images/test_health_detail_card__render_crown~spalding.png deleted file mode 100644 index 889ddb26a4..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_crown~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_no_data~silk.png b/tests/test_images/test_health_detail_card__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_no_data~silk.png rename to tests/test_images/test_health_detail_card__render_no_data~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_no_data~gabbro.png b/tests/test_images/test_health_detail_card__render_no_data~gabbro.png new file mode 100644 index 0000000000..e566c0245f Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_no_data~obelix.png b/tests/test_images/test_health_detail_card__render_no_data~obelix.png new file mode 100644 index 0000000000..2caea57477 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_no_data~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_no_data~snowy.png b/tests/test_images/test_health_detail_card__render_no_data~snowy.png deleted file mode 100644 index fbec404d58..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_no_data~spalding.png b/tests/test_images/test_health_detail_card__render_no_data~spalding.png deleted file mode 100644 index 3b711361db..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_no_subtitle~silk.png b/tests/test_images/test_health_detail_card__render_no_subtitle~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_no_subtitle~silk.png rename to tests/test_images/test_health_detail_card__render_no_subtitle~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_no_subtitle~gabbro.png b/tests/test_images/test_health_detail_card__render_no_subtitle~gabbro.png new file mode 100644 index 0000000000..002db407d6 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_no_subtitle~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_no_subtitle~obelix.png b/tests/test_images/test_health_detail_card__render_no_subtitle~obelix.png new file mode 100644 index 0000000000..49c3ae16dd Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_no_subtitle~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_no_subtitle~snowy.png b/tests/test_images/test_health_detail_card__render_no_subtitle~snowy.png deleted file mode 100644 index d04b9ea7cf..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_no_subtitle~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_no_subtitle~spalding.png b/tests/test_images/test_health_detail_card__render_no_subtitle~spalding.png deleted file mode 100644 index 3c80024b7c..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_no_subtitle~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_one_heading~silk.png b/tests/test_images/test_health_detail_card__render_one_heading~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_one_heading~silk.png rename to tests/test_images/test_health_detail_card__render_one_heading~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_one_heading~gabbro.png b/tests/test_images/test_health_detail_card__render_one_heading~gabbro.png new file mode 100644 index 0000000000..fcc1fc0e6e Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_one_heading~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_one_heading~obelix.png b/tests/test_images/test_health_detail_card__render_one_heading~obelix.png new file mode 100644 index 0000000000..0e7aaa43ae Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_one_heading~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_one_heading~snowy.png b/tests/test_images/test_health_detail_card__render_one_heading~snowy.png deleted file mode 100644 index f7ea9913b1..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_one_heading~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_one_heading~spalding.png b/tests/test_images/test_health_detail_card__render_one_heading~spalding.png deleted file mode 100644 index 9d427eedcc..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_one_heading~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_subtitle_text~silk.png b/tests/test_images/test_health_detail_card__render_subtitle_text~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_subtitle_text~silk.png rename to tests/test_images/test_health_detail_card__render_subtitle_text~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_subtitle_text~gabbro.png b/tests/test_images/test_health_detail_card__render_subtitle_text~gabbro.png new file mode 100644 index 0000000000..2d56b966d7 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_subtitle_text~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_subtitle_text~obelix.png b/tests/test_images/test_health_detail_card__render_subtitle_text~obelix.png new file mode 100644 index 0000000000..310d6cf01d Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_subtitle_text~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_subtitle_text~snowy.png b/tests/test_images/test_health_detail_card__render_subtitle_text~snowy.png deleted file mode 100644 index b262d9b369..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_subtitle_text~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_subtitle_text~spalding.png b/tests/test_images/test_health_detail_card__render_subtitle_text~spalding.png deleted file mode 100644 index 8696757629..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_subtitle_text~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_two_headings~silk.png b/tests/test_images/test_health_detail_card__render_two_headings~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_two_headings~silk.png rename to tests/test_images/test_health_detail_card__render_two_headings~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_two_headings~gabbro.png b/tests/test_images/test_health_detail_card__render_two_headings~gabbro.png new file mode 100644 index 0000000000..dc0be804e4 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_two_headings~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_two_headings~obelix.png b/tests/test_images/test_health_detail_card__render_two_headings~obelix.png new file mode 100644 index 0000000000..32a58c726f Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_two_headings~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_two_headings~snowy.png b/tests/test_images/test_health_detail_card__render_two_headings~snowy.png deleted file mode 100644 index ced30d2fba..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_two_headings~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_two_headings~spalding.png b/tests/test_images/test_health_detail_card__render_two_headings~spalding.png deleted file mode 100644 index eafcb905e7..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_two_headings~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_zone_hide_typical~silk.png b/tests/test_images/test_health_detail_card__render_zone_hide_typical~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_zone_hide_typical~silk.png rename to tests/test_images/test_health_detail_card__render_zone_hide_typical~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_zone_hide_typical~gabbro.png b/tests/test_images/test_health_detail_card__render_zone_hide_typical~gabbro.png new file mode 100644 index 0000000000..bd602864f4 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_zone_hide_typical~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_zone_hide_typical~obelix.png b/tests/test_images/test_health_detail_card__render_zone_hide_typical~obelix.png new file mode 100644 index 0000000000..49c3ae16dd Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_zone_hide_typical~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_zone_hide_typical~snowy.png b/tests/test_images/test_health_detail_card__render_zone_hide_typical~snowy.png deleted file mode 100644 index d04b9ea7cf..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_zone_hide_typical~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_zone_hide_typical~spalding.png b/tests/test_images/test_health_detail_card__render_zone_hide_typical~spalding.png deleted file mode 100644 index c828e6d960..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_zone_hide_typical~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_zones~silk.png b/tests/test_images/test_health_detail_card__render_zones~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__render_zones~silk.png rename to tests/test_images/test_health_detail_card__render_zones~asterix.png diff --git a/tests/test_images/test_health_detail_card__render_zones~gabbro.png b/tests/test_images/test_health_detail_card__render_zones~gabbro.png new file mode 100644 index 0000000000..e949b9dc73 Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_zones~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__render_zones~obelix.png b/tests/test_images/test_health_detail_card__render_zones~obelix.png new file mode 100644 index 0000000000..e882d3448c Binary files /dev/null and b/tests/test_images/test_health_detail_card__render_zones~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__render_zones~snowy.png b/tests/test_images/test_health_detail_card__render_zones~snowy.png deleted file mode 100644 index 4ef7c77ef0..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_zones~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__render_zones~spalding.png b/tests/test_images/test_health_detail_card__render_zones~spalding.png deleted file mode 100644 index 8043d9c49c..0000000000 Binary files a/tests/test_images/test_health_detail_card__render_zones~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__scroll_down~silk.png b/tests/test_images/test_health_detail_card__scroll_down~asterix.png similarity index 100% rename from tests/test_images/test_health_detail_card__scroll_down~silk.png rename to tests/test_images/test_health_detail_card__scroll_down~asterix.png diff --git a/tests/test_images/test_health_detail_card__scroll_down~gabbro.png b/tests/test_images/test_health_detail_card__scroll_down~gabbro.png new file mode 100644 index 0000000000..052f17868d Binary files /dev/null and b/tests/test_images/test_health_detail_card__scroll_down~gabbro.png differ diff --git a/tests/test_images/test_health_detail_card__scroll_down~obelix.png b/tests/test_images/test_health_detail_card__scroll_down~obelix.png new file mode 100644 index 0000000000..9c75ca2ae7 Binary files /dev/null and b/tests/test_images/test_health_detail_card__scroll_down~obelix.png differ diff --git a/tests/test_images/test_health_detail_card__scroll_down~snowy.png b/tests/test_images/test_health_detail_card__scroll_down~snowy.png deleted file mode 100644 index 8fab10e14c..0000000000 Binary files a/tests/test_images/test_health_detail_card__scroll_down~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_detail_card__scroll_down~spalding.png b/tests/test_images/test_health_detail_card__scroll_down~spalding.png deleted file mode 100644 index 087fc85624..0000000000 Binary files a/tests/test_images/test_health_detail_card__scroll_down~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_detail_card__render_no_data~silk.png b/tests/test_images/test_health_hr_detail_card__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_health_hr_detail_card__render_no_data~silk.png rename to tests/test_images/test_health_hr_detail_card__render_no_data~asterix.png diff --git a/tests/test_images/test_health_hr_detail_card__render_no_data~gabbro.png b/tests/test_images/test_health_hr_detail_card__render_no_data~gabbro.png new file mode 100644 index 0000000000..47eca31f3e Binary files /dev/null and b/tests/test_images/test_health_hr_detail_card__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_health_hr_detail_card__render_no_data~obelix.png b/tests/test_images/test_health_hr_detail_card__render_no_data~obelix.png new file mode 100644 index 0000000000..fa70e7be3a Binary files /dev/null and b/tests/test_images/test_health_hr_detail_card__render_no_data~obelix.png differ diff --git a/tests/test_images/test_health_hr_detail_card__render_no_data~snowy.png b/tests/test_images/test_health_hr_detail_card__render_no_data~snowy.png deleted file mode 100644 index b373b119bd..0000000000 Binary files a/tests/test_images/test_health_hr_detail_card__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_detail_card__render_no_data~spalding.png b/tests/test_images/test_health_hr_detail_card__render_no_data~spalding.png deleted file mode 100644 index 65f8998c99..0000000000 Binary files a/tests/test_images/test_health_hr_detail_card__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones2~silk.png b/tests/test_images/test_health_hr_detail_card__render_zones2~asterix.png similarity index 100% rename from tests/test_images/test_health_hr_detail_card__render_zones2~silk.png rename to tests/test_images/test_health_hr_detail_card__render_zones2~asterix.png diff --git a/tests/test_images/test_health_hr_detail_card__render_zones2~gabbro.png b/tests/test_images/test_health_hr_detail_card__render_zones2~gabbro.png new file mode 100644 index 0000000000..a4cfd3574a Binary files /dev/null and b/tests/test_images/test_health_hr_detail_card__render_zones2~gabbro.png differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones2~obelix.png b/tests/test_images/test_health_hr_detail_card__render_zones2~obelix.png new file mode 100644 index 0000000000..40b7ff7a8e Binary files /dev/null and b/tests/test_images/test_health_hr_detail_card__render_zones2~obelix.png differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones2~snowy.png b/tests/test_images/test_health_hr_detail_card__render_zones2~snowy.png deleted file mode 100644 index c21bdfd0f6..0000000000 Binary files a/tests/test_images/test_health_hr_detail_card__render_zones2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones2~spalding.png b/tests/test_images/test_health_hr_detail_card__render_zones2~spalding.png deleted file mode 100644 index 1674df3c67..0000000000 Binary files a/tests/test_images/test_health_hr_detail_card__render_zones2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones~silk.png b/tests/test_images/test_health_hr_detail_card__render_zones~asterix.png similarity index 100% rename from tests/test_images/test_health_hr_detail_card__render_zones~silk.png rename to tests/test_images/test_health_hr_detail_card__render_zones~asterix.png diff --git a/tests/test_images/test_health_hr_detail_card__render_zones~gabbro.png b/tests/test_images/test_health_hr_detail_card__render_zones~gabbro.png new file mode 100644 index 0000000000..a12c457faf Binary files /dev/null and b/tests/test_images/test_health_hr_detail_card__render_zones~gabbro.png differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones~obelix.png b/tests/test_images/test_health_hr_detail_card__render_zones~obelix.png new file mode 100644 index 0000000000..8b122d7369 Binary files /dev/null and b/tests/test_images/test_health_hr_detail_card__render_zones~obelix.png differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones~snowy.png b/tests/test_images/test_health_hr_detail_card__render_zones~snowy.png deleted file mode 100644 index ab0e405567..0000000000 Binary files a/tests/test_images/test_health_hr_detail_card__render_zones~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_detail_card__render_zones~spalding.png b/tests/test_images/test_health_hr_detail_card__render_zones~spalding.png deleted file mode 100644 index ce5637aa61..0000000000 Binary files a/tests/test_images/test_health_hr_detail_card__render_zones~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_summary_card__render_current_bpm~asterix.png b/tests/test_images/test_health_hr_summary_card__render_current_bpm~asterix.png new file mode 100644 index 0000000000..5805e01db0 Binary files /dev/null and b/tests/test_images/test_health_hr_summary_card__render_current_bpm~asterix.png differ diff --git a/tests/test_images/test_health_hr_summary_card__render_current_bpm~silk.png b/tests/test_images/test_health_hr_summary_card__render_current_bpm~silk.png deleted file mode 100644 index 9223506283..0000000000 Binary files a/tests/test_images/test_health_hr_summary_card__render_current_bpm~silk.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_summary_card__render_no_data~asterix.png b/tests/test_images/test_health_hr_summary_card__render_no_data~asterix.png new file mode 100644 index 0000000000..017f0bf1a8 Binary files /dev/null and b/tests/test_images/test_health_hr_summary_card__render_no_data~asterix.png differ diff --git a/tests/test_images/test_health_hr_summary_card__render_no_data~silk.png b/tests/test_images/test_health_hr_summary_card__render_no_data~silk.png deleted file mode 100644 index e0cf027a25..0000000000 Binary files a/tests/test_images/test_health_hr_summary_card__render_no_data~silk.png and /dev/null differ diff --git a/tests/test_images/test_health_hr_summary_card__render_timestamp~asterix.png b/tests/test_images/test_health_hr_summary_card__render_timestamp~asterix.png new file mode 100644 index 0000000000..574e22676b Binary files /dev/null and b/tests/test_images/test_health_hr_summary_card__render_timestamp~asterix.png differ diff --git a/tests/test_images/test_health_hr_summary_card__render_timestamp~silk.png b/tests/test_images/test_health_hr_summary_card__render_timestamp~silk.png deleted file mode 100644 index ab5ad56d5b..0000000000 Binary files a/tests/test_images/test_health_hr_summary_card__render_timestamp~silk.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~silk.png b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_30_day_avg~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_30_day_avg~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~gabbro.png new file mode 100644 index 0000000000..2482f067b6 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~obelix.png new file mode 100644 index 0000000000..6b58e656c1 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~snowy.png deleted file mode 100644 index a0dbfc32ac..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~spalding.png deleted file mode 100644 index 03d61f3c14..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_30_day_avg~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~silk.png b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_deep_sleep~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_deep_sleep~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~gabbro.png new file mode 100644 index 0000000000..15e1c7738c Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~obelix.png new file mode 100644 index 0000000000..294dc94fb0 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~snowy.png deleted file mode 100644 index b530c65fc4..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~spalding.png deleted file mode 100644 index 620a405ffe..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_deep_sleep~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_no_data~silk.png b/tests/test_images/test_health_sleep_detail_card__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_no_data~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_no_data~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_no_data~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_no_data~gabbro.png new file mode 100644 index 0000000000..358739cb98 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_no_data~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_no_data~obelix.png new file mode 100644 index 0000000000..d3a0b352c2 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_no_data~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_no_data~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_no_data~snowy.png deleted file mode 100644 index e2359379a1..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_no_data~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_no_data~spalding.png deleted file mode 100644 index 84ca22da1f..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~silk.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~gabbro.png new file mode 100644 index 0000000000..3180da2717 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~obelix.png new file mode 100644 index 0000000000..a769606db5 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~snowy.png deleted file mode 100644 index bced11cd2b..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~spalding.png deleted file mode 100644 index cb366432cd..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~silk.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~gabbro.png new file mode 100644 index 0000000000..d5fc6c2ee0 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~obelix.png new file mode 100644 index 0000000000..66e255d981 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~snowy.png deleted file mode 100644 index d4c95483f0..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~spalding.png deleted file mode 100644 index a7d1c4a599..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_data_2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~silk.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~gabbro.png new file mode 100644 index 0000000000..358739cb98 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~obelix.png new file mode 100644 index 0000000000..d3a0b352c2 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~snowy.png deleted file mode 100644 index e2359379a1..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~spalding.png deleted file mode 100644 index 84ca22da1f..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_session_same_start_end_time~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~silk.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_detail_card__render_sleep_session~silk.png rename to tests/test_images/test_health_sleep_detail_card__render_sleep_session~asterix.png diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~gabbro.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~gabbro.png new file mode 100644 index 0000000000..7b201062b4 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~obelix.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~obelix.png new file mode 100644 index 0000000000..08550cc682 Binary files /dev/null and b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~obelix.png differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~snowy.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~snowy.png deleted file mode 100644 index 5834cbf712..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~spalding.png b/tests/test_images/test_health_sleep_detail_card__render_sleep_session~spalding.png deleted file mode 100644 index 40d85ba4a1..0000000000 Binary files a/tests/test_images/test_health_sleep_detail_card__render_sleep_session~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_data~silk.png b/tests/test_images/test_health_sleep_summary_card__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_summary_card__render_no_data~silk.png rename to tests/test_images/test_health_sleep_summary_card__render_no_data~asterix.png diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_data~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_no_data~gabbro.png new file mode 100644 index 0000000000..a1981c91f1 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_data~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_no_data~obelix.png new file mode 100644 index 0000000000..f3bf732bff Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_no_data~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_data~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_no_data~snowy.png deleted file mode 100644 index b6201a5a07..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_data~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_no_data~spalding.png deleted file mode 100644 index e1f3ba511c..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~silk.png b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~silk.png rename to tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~asterix.png diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~gabbro.png new file mode 100644 index 0000000000..9269e74aef Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~obelix.png new file mode 100644 index 0000000000..4b453e448d Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~snowy.png deleted file mode 100644 index 5daaf36ec6..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~spalding.png deleted file mode 100644 index 68a79738e3..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_no_sleep_last_night~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_typical~silk.png b/tests/test_images/test_health_sleep_summary_card__render_no_typical~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_summary_card__render_no_typical~silk.png rename to tests/test_images/test_health_sleep_summary_card__render_no_typical~asterix.png diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_typical~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_no_typical~gabbro.png new file mode 100644 index 0000000000..8ef4330351 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_no_typical~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_typical~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_no_typical~obelix.png new file mode 100644 index 0000000000..41f51b65e3 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_no_typical~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_typical~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_no_typical~snowy.png deleted file mode 100644 index b4344a2a11..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_no_typical~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_no_typical~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_no_typical~spalding.png deleted file mode 100644 index 3eabe4e30c..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_no_typical~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~asterix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~asterix.png new file mode 100644 index 0000000000..9f08c6f0bb Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~asterix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~gabbro.png new file mode 100644 index 0000000000..7566e9495d Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~obelix.png new file mode 100644 index 0000000000..4f42a4c10f Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~silk.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~silk.png deleted file mode 100644 index 555ee669a6..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~silk.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~snowy.png deleted file mode 100644 index 82a8650ae3..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~spalding.png deleted file mode 100644 index 4c2dac1bd4..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_early_end1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~silk.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~silk.png rename to tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~asterix.png diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~gabbro.png new file mode 100644 index 0000000000..4866206a0d Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~obelix.png new file mode 100644 index 0000000000..18cf9fcd6c Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~snowy.png deleted file mode 100644 index 500b216d77..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~spalding.png deleted file mode 100644 index b461451f62..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_early_start_late_end1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~asterix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~asterix.png new file mode 100644 index 0000000000..59b463828c Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~asterix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~gabbro.png new file mode 100644 index 0000000000..19d0975e77 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~obelix.png new file mode 100644 index 0000000000..13fc80353a Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~silk.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~silk.png deleted file mode 100644 index a1c75c0f80..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~silk.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~snowy.png deleted file mode 100644 index d8bc179de6..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~spalding.png deleted file mode 100644 index e9e74826fe..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~asterix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~asterix.png new file mode 100644 index 0000000000..978f1636fb Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~asterix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~gabbro.png new file mode 100644 index 0000000000..c6adddce0c Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~obelix.png new file mode 100644 index 0000000000..683d484721 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~silk.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~silk.png deleted file mode 100644 index 8a51689ae9..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~silk.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~snowy.png deleted file mode 100644 index b7a81e8c7b..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~spalding.png deleted file mode 100644 index 6396dd5d30..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_early_end2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~silk.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~asterix.png similarity index 100% rename from tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~silk.png rename to tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~asterix.png diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~gabbro.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~gabbro.png new file mode 100644 index 0000000000..e9307cb872 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~gabbro.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~obelix.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~obelix.png new file mode 100644 index 0000000000..a307e89774 Binary files /dev/null and b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~obelix.png differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~snowy.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~snowy.png deleted file mode 100644 index 38b17c18d8..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~spalding.png b/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~spalding.png deleted file mode 100644 index 3b1e45df01..0000000000 Binary files a/tests/test_images/test_health_sleep_summary_card__render_sleep_late_start_late_end1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_PBL_43681~silk.png b/tests/test_images/test_kickstart__render_PBL_43681~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_PBL_43681~silk.png rename to tests/test_images/test_kickstart__render_PBL_43681~asterix.png diff --git a/tests/test_images/test_kickstart__render_PBL_43681~gabbro.png b/tests/test_images/test_kickstart__render_PBL_43681~gabbro.png new file mode 100644 index 0000000000..61ab7fc59a Binary files /dev/null and b/tests/test_images/test_kickstart__render_PBL_43681~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_PBL_43681~robert.png b/tests/test_images/test_kickstart__render_PBL_43681~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_PBL_43681~robert.png rename to tests/test_images/test_kickstart__render_PBL_43681~obelix.png diff --git a/tests/test_images/test_kickstart__render_PBL_43681~snowy.png b/tests/test_images/test_kickstart__render_PBL_43681~snowy.png deleted file mode 100644 index 2b05bc84a6..0000000000 Binary files a/tests/test_images/test_kickstart__render_PBL_43681~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_PBL_43681~spalding.png b/tests/test_images/test_kickstart__render_PBL_43681~spalding.png deleted file mode 100644 index 517797c278..0000000000 Binary files a/tests/test_images/test_kickstart__render_PBL_43681~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_PBL_43717~silk.png b/tests/test_images/test_kickstart__render_PBL_43717~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_PBL_43717~silk.png rename to tests/test_images/test_kickstart__render_PBL_43717~asterix.png diff --git a/tests/test_images/test_kickstart__render_PBL_43717~gabbro.png b/tests/test_images/test_kickstart__render_PBL_43717~gabbro.png new file mode 100644 index 0000000000..fe2bf32214 Binary files /dev/null and b/tests/test_images/test_kickstart__render_PBL_43717~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_PBL_43717~robert.png b/tests/test_images/test_kickstart__render_PBL_43717~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_PBL_43717~robert.png rename to tests/test_images/test_kickstart__render_PBL_43717~obelix.png diff --git a/tests/test_images/test_kickstart__render_PBL_43717~snowy.png b/tests/test_images/test_kickstart__render_PBL_43717~snowy.png deleted file mode 100644 index 93f65edb8a..0000000000 Binary files a/tests/test_images/test_kickstart__render_PBL_43717~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_PBL_43717~spalding.png b/tests/test_images/test_kickstart__render_PBL_43717~spalding.png deleted file mode 100644 index 61c058dc96..0000000000 Binary files a/tests/test_images/test_kickstart__render_PBL_43717~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_hr_bpm_24h~silk.png b/tests/test_images/test_kickstart__render_hr_bpm_24h~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm_24h~silk.png rename to tests/test_images/test_kickstart__render_hr_bpm_24h~asterix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm_24h~robert.png b/tests/test_images/test_kickstart__render_hr_bpm_24h~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm_24h~robert.png rename to tests/test_images/test_kickstart__render_hr_bpm_24h~obelix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~silk.png b/tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~silk.png rename to tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~asterix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~robert.png b/tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~robert.png rename to tests/test_images/test_kickstart__render_hr_bpm_obstructed_24h~obelix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm_obstructed~silk.png b/tests/test_images/test_kickstart__render_hr_bpm_obstructed~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm_obstructed~silk.png rename to tests/test_images/test_kickstart__render_hr_bpm_obstructed~asterix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm_obstructed~robert.png b/tests/test_images/test_kickstart__render_hr_bpm_obstructed~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm_obstructed~robert.png rename to tests/test_images/test_kickstart__render_hr_bpm_obstructed~obelix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm~silk.png b/tests/test_images/test_kickstart__render_hr_bpm~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm~silk.png rename to tests/test_images/test_kickstart__render_hr_bpm~asterix.png diff --git a/tests/test_images/test_kickstart__render_hr_bpm~robert.png b/tests/test_images/test_kickstart__render_hr_bpm~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_hr_bpm~robert.png rename to tests/test_images/test_kickstart__render_hr_bpm~obelix.png diff --git a/tests/test_images/test_kickstart__render_no_data~silk.png b/tests/test_images/test_kickstart__render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_no_data~silk.png rename to tests/test_images/test_kickstart__render_no_data~asterix.png diff --git a/tests/test_images/test_kickstart__render_no_data~gabbro.png b/tests/test_images/test_kickstart__render_no_data~gabbro.png new file mode 100644 index 0000000000..61ab7fc59a Binary files /dev/null and b/tests/test_images/test_kickstart__render_no_data~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_no_data~robert.png b/tests/test_images/test_kickstart__render_no_data~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_no_data~robert.png rename to tests/test_images/test_kickstart__render_no_data~obelix.png diff --git a/tests/test_images/test_kickstart__render_no_data~snowy.png b/tests/test_images/test_kickstart__render_no_data~snowy.png deleted file mode 100644 index 2b05bc84a6..0000000000 Binary files a/tests/test_images/test_kickstart__render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_no_data~spalding.png b/tests/test_images/test_kickstart__render_no_data~spalding.png deleted file mode 100644 index 517797c278..0000000000 Binary files a/tests/test_images/test_kickstart__render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_obstructed_area~silk.png b/tests/test_images/test_kickstart__render_obstructed_area~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_obstructed_area~silk.png rename to tests/test_images/test_kickstart__render_obstructed_area~asterix.png diff --git a/tests/test_images/test_kickstart__render_obstructed_area~robert.png b/tests/test_images/test_kickstart__render_obstructed_area~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_obstructed_area~robert.png rename to tests/test_images/test_kickstart__render_obstructed_area~obelix.png diff --git a/tests/test_images/test_kickstart__render_obstructed_area~snowy.png b/tests/test_images/test_kickstart__render_obstructed_area~snowy.png deleted file mode 100644 index e85dfc239e..0000000000 Binary files a/tests/test_images/test_kickstart__render_obstructed_area~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~silk.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~silk.png rename to tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~asterix.png diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~gabbro.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~gabbro.png new file mode 100644 index 0000000000..87c27faa6f Binary files /dev/null and b/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~robert.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~robert.png rename to tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~obelix.png diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~snowy.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~snowy.png deleted file mode 100644 index 5272ebf344..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~spalding.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~spalding.png deleted file mode 100644 index 2657f459c6..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_above_daily_avg_24h~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg~silk.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_above_daily_avg~silk.png rename to tests/test_images/test_kickstart__render_steps_above_daily_avg~asterix.png diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg~gabbro.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg~gabbro.png new file mode 100644 index 0000000000..f1fe27e8f4 Binary files /dev/null and b/tests/test_images/test_kickstart__render_steps_above_daily_avg~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg~robert.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_above_daily_avg~robert.png rename to tests/test_images/test_kickstart__render_steps_above_daily_avg~obelix.png diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg~snowy.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg~snowy.png deleted file mode 100644 index 95e703ada7..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_above_daily_avg~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_above_daily_avg~spalding.png b/tests/test_images/test_kickstart__render_steps_above_daily_avg~spalding.png deleted file mode 100644 index 1e4135a135..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_above_daily_avg~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_above_typical~silk.png b/tests/test_images/test_kickstart__render_steps_above_typical~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_above_typical~silk.png rename to tests/test_images/test_kickstart__render_steps_above_typical~asterix.png diff --git a/tests/test_images/test_kickstart__render_steps_above_typical~gabbro.png b/tests/test_images/test_kickstart__render_steps_above_typical~gabbro.png new file mode 100644 index 0000000000..08d66c7043 Binary files /dev/null and b/tests/test_images/test_kickstart__render_steps_above_typical~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_steps_above_typical~robert.png b/tests/test_images/test_kickstart__render_steps_above_typical~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_above_typical~robert.png rename to tests/test_images/test_kickstart__render_steps_above_typical~obelix.png diff --git a/tests/test_images/test_kickstart__render_steps_above_typical~snowy.png b/tests/test_images/test_kickstart__render_steps_above_typical~snowy.png deleted file mode 100644 index c24375f20a..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_above_typical~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_above_typical~spalding.png b/tests/test_images/test_kickstart__render_steps_above_typical~spalding.png deleted file mode 100644 index 19d85209d5..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_above_typical~spalding.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_below_typical~silk.png b/tests/test_images/test_kickstart__render_steps_below_typical~asterix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_below_typical~silk.png rename to tests/test_images/test_kickstart__render_steps_below_typical~asterix.png diff --git a/tests/test_images/test_kickstart__render_steps_below_typical~gabbro.png b/tests/test_images/test_kickstart__render_steps_below_typical~gabbro.png new file mode 100644 index 0000000000..158d25ebcd Binary files /dev/null and b/tests/test_images/test_kickstart__render_steps_below_typical~gabbro.png differ diff --git a/tests/test_images/test_kickstart__render_steps_below_typical~robert.png b/tests/test_images/test_kickstart__render_steps_below_typical~obelix.png similarity index 100% rename from tests/test_images/test_kickstart__render_steps_below_typical~robert.png rename to tests/test_images/test_kickstart__render_steps_below_typical~obelix.png diff --git a/tests/test_images/test_kickstart__render_steps_below_typical~snowy.png b/tests/test_images/test_kickstart__render_steps_below_typical~snowy.png deleted file mode 100644 index 12626f40a5..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_below_typical~snowy.png and /dev/null differ diff --git a/tests/test_images/test_kickstart__render_steps_below_typical~spalding.png b/tests/test_images/test_kickstart__render_steps_below_typical~spalding.png deleted file mode 100644 index 398a34e8c8..0000000000 Binary files a/tests/test_images/test_kickstart__render_steps_below_typical~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~silk.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~silk.png rename to tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~gabbro.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~gabbro.png new file mode 100644 index 0000000000..28b9c3941e Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~obelix.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~obelix.png new file mode 100644 index 0000000000..ea886c375c Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~robert.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~robert.png deleted file mode 100644 index 43a43e69a6..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~snowy.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~snowy.png deleted file mode 100644 index 8390f02521..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~spalding.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~spalding.png deleted file mode 100644 index c51f5d11a7..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances_pdc~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~silk.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~silk.png rename to tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~gabbro.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~gabbro.png new file mode 100644 index 0000000000..54b8de526e Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~obelix.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~obelix.png new file mode 100644 index 0000000000..6c745b8755 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~robert.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~robert.png deleted file mode 100644 index 37dd873ff0..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~snowy.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~snowy.png deleted file mode 100644 index 287b9d0de3..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~spalding.png b/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~spalding.png deleted file mode 100644 index a2f991a499..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__app_selected_and_apps_above_and_below_with_glances~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~silk.png b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__interior_app_pdc~silk.png rename to tests/test_images/test_launcher_menu_layer__interior_app_pdc~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~gabbro.png b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~gabbro.png new file mode 100644 index 0000000000..69396d7d24 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~obelix.png b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~obelix.png new file mode 100644 index 0000000000..1db40fea5b Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~robert.png b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~robert.png deleted file mode 100644 index 6f6f309ac3..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~snowy.png b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~snowy.png deleted file mode 100644 index 33191e82fc..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~spalding.png b/tests/test_images/test_launcher_menu_layer__interior_app_pdc~spalding.png deleted file mode 100644 index c5f9a87bf7..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__interior_app_pdc~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app~silk.png b/tests/test_images/test_launcher_menu_layer__interior_app~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__interior_app~silk.png rename to tests/test_images/test_launcher_menu_layer__interior_app~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__interior_app~gabbro.png b/tests/test_images/test_launcher_menu_layer__interior_app~gabbro.png new file mode 100644 index 0000000000..cb3c29b71e Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__interior_app~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app~obelix.png b/tests/test_images/test_launcher_menu_layer__interior_app~obelix.png new file mode 100644 index 0000000000..324d6a8233 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__interior_app~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app~robert.png b/tests/test_images/test_launcher_menu_layer__interior_app~robert.png deleted file mode 100644 index 51c865140c..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__interior_app~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app~snowy.png b/tests/test_images/test_launcher_menu_layer__interior_app~snowy.png deleted file mode 100644 index 152fe610c9..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__interior_app~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__interior_app~spalding.png b/tests/test_images/test_launcher_menu_layer__interior_app~spalding.png deleted file mode 100644 index c4d875fd2c..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__interior_app~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title_pdc~silk.png b/tests/test_images/test_launcher_menu_layer__long_title_pdc~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__long_title_pdc~silk.png rename to tests/test_images/test_launcher_menu_layer__long_title_pdc~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__long_title_pdc~gabbro.png b/tests/test_images/test_launcher_menu_layer__long_title_pdc~gabbro.png new file mode 100644 index 0000000000..adaeb7b1df Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__long_title_pdc~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title_pdc~obelix.png b/tests/test_images/test_launcher_menu_layer__long_title_pdc~obelix.png new file mode 100644 index 0000000000..1db40fea5b Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__long_title_pdc~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title_pdc~robert.png b/tests/test_images/test_launcher_menu_layer__long_title_pdc~robert.png deleted file mode 100644 index 2faf8d6c29..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__long_title_pdc~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title_pdc~snowy.png b/tests/test_images/test_launcher_menu_layer__long_title_pdc~snowy.png deleted file mode 100644 index c7ac2bd275..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__long_title_pdc~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title_pdc~spalding.png b/tests/test_images/test_launcher_menu_layer__long_title_pdc~spalding.png deleted file mode 100644 index c9294fb009..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__long_title_pdc~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title~silk.png b/tests/test_images/test_launcher_menu_layer__long_title~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__long_title~silk.png rename to tests/test_images/test_launcher_menu_layer__long_title~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__long_title~gabbro.png b/tests/test_images/test_launcher_menu_layer__long_title~gabbro.png new file mode 100644 index 0000000000..d4197b73e4 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__long_title~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title~obelix.png b/tests/test_images/test_launcher_menu_layer__long_title~obelix.png new file mode 100644 index 0000000000..324d6a8233 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__long_title~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title~robert.png b/tests/test_images/test_launcher_menu_layer__long_title~robert.png deleted file mode 100644 index b9414e4ccf..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__long_title~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title~snowy.png b/tests/test_images/test_launcher_menu_layer__long_title~snowy.png deleted file mode 100644 index b009548a3a..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__long_title~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__long_title~spalding.png b/tests/test_images/test_launcher_menu_layer__long_title~spalding.png deleted file mode 100644 index f4e13e73ec..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__long_title~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~silk.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~silk.png rename to tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~gabbro.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~gabbro.png new file mode 100644 index 0000000000..ffb53e3450 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~obelix.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~obelix.png new file mode 100644 index 0000000000..a830c4b813 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~robert.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~robert.png deleted file mode 100644 index dae90fd409..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~snowy.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~snowy.png deleted file mode 100644 index 39640e6202..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~spalding.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~spalding.png deleted file mode 100644 index 898e7dd168..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance_pdc~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~silk.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~silk.png rename to tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~gabbro.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~gabbro.png new file mode 100644 index 0000000000..6de3c2a5ec Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~obelix.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~obelix.png new file mode 100644 index 0000000000..c54b7ebebc Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~robert.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~robert.png deleted file mode 100644 index 84398e92a8..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~snowy.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~snowy.png deleted file mode 100644 index 598918b990..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~spalding.png b/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~spalding.png deleted file mode 100644 index ef10e0226f..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_app_with_glance~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~silk.png b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__no_icon_pdc~silk.png rename to tests/test_images/test_launcher_menu_layer__no_icon_pdc~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~gabbro.png b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~gabbro.png new file mode 100644 index 0000000000..2e74b1fd48 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~obelix.png b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~obelix.png new file mode 100644 index 0000000000..1db40fea5b Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~robert.png b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~robert.png deleted file mode 100644 index 3ac4c19946..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~snowy.png b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~snowy.png deleted file mode 100644 index 8388cf0837..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~spalding.png b/tests/test_images/test_launcher_menu_layer__no_icon_pdc~spalding.png deleted file mode 100644 index 5d92761653..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon_pdc~spalding.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon~silk.png b/tests/test_images/test_launcher_menu_layer__no_icon~asterix.png similarity index 100% rename from tests/test_images/test_launcher_menu_layer__no_icon~silk.png rename to tests/test_images/test_launcher_menu_layer__no_icon~asterix.png diff --git a/tests/test_images/test_launcher_menu_layer__no_icon~gabbro.png b/tests/test_images/test_launcher_menu_layer__no_icon~gabbro.png new file mode 100644 index 0000000000..ac52f35b89 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon~gabbro.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon~obelix.png b/tests/test_images/test_launcher_menu_layer__no_icon~obelix.png new file mode 100644 index 0000000000..324d6a8233 Binary files /dev/null and b/tests/test_images/test_launcher_menu_layer__no_icon~obelix.png differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon~robert.png b/tests/test_images/test_launcher_menu_layer__no_icon~robert.png deleted file mode 100644 index 7f9d764081..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon~robert.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon~snowy.png b/tests/test_images/test_launcher_menu_layer__no_icon~snowy.png deleted file mode 100644 index 1559b4c541..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon~snowy.png and /dev/null differ diff --git a/tests/test_images/test_launcher_menu_layer__no_icon~spalding.png b/tests/test_images/test_launcher_menu_layer__no_icon~spalding.png deleted file mode 100644 index bbd9c55713..0000000000 Binary files a/tests/test_images/test_launcher_menu_layer__no_icon~spalding.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144_legacy2~silk.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144_legacy2~asterix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__basic_cell_width_144_legacy2~silk.png rename to tests/test_images/test_menu_layer_system_cells__basic_cell_width_144_legacy2~asterix.png diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~silk.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~asterix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~silk.png rename to tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~asterix.png diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~gabbro.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~gabbro.png new file mode 100644 index 0000000000..1dae79fbaf Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~gabbro.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~obelix.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~obelix.png new file mode 100644 index 0000000000..ef00b86af2 Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~obelix.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~robert.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~robert.png deleted file mode 100644 index 82780ed785..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~robert.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~snowy.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~snowy.png deleted file mode 100644 index 760e18d3ea..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~snowy.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~spalding.png b/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~spalding.png deleted file mode 100644 index 141ba1c7ff..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__basic_cell_width_144~spalding.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~silk.png b/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~asterix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~silk.png rename to tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~asterix.png diff --git a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~gabbro.png b/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~gabbro.png new file mode 100644 index 0000000000..71aed81751 Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~gabbro.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~robert.png b/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~obelix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~robert.png rename to tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~obelix.png diff --git a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~snowy.png b/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~snowy.png deleted file mode 100644 index 6b076e9b15..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~snowy.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~spalding.png b/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~spalding.png deleted file mode 100644 index 677dfbea5a..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__basic_custom_cell_width_144~spalding.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_100~silk.png b/tests/test_images/test_menu_layer_system_cells__cell_width_100~asterix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__cell_width_100~silk.png rename to tests/test_images/test_menu_layer_system_cells__cell_width_100~asterix.png diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_100~gabbro.png b/tests/test_images/test_menu_layer_system_cells__cell_width_100~gabbro.png new file mode 100644 index 0000000000..35cd3c087c Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__cell_width_100~gabbro.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_100~robert.png b/tests/test_images/test_menu_layer_system_cells__cell_width_100~obelix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__cell_width_100~robert.png rename to tests/test_images/test_menu_layer_system_cells__cell_width_100~obelix.png diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_100~snowy.png b/tests/test_images/test_menu_layer_system_cells__cell_width_100~snowy.png deleted file mode 100644 index b7908e3264..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_100~snowy.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_100~spalding.png b/tests/test_images/test_menu_layer_system_cells__cell_width_100~spalding.png deleted file mode 100644 index f184178175..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_100~spalding.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_144~silk.png b/tests/test_images/test_menu_layer_system_cells__cell_width_144~asterix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__cell_width_144~silk.png rename to tests/test_images/test_menu_layer_system_cells__cell_width_144~asterix.png diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_144~gabbro.png b/tests/test_images/test_menu_layer_system_cells__cell_width_144~gabbro.png new file mode 100644 index 0000000000..aa9f6a801f Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__cell_width_144~gabbro.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_144~robert.png b/tests/test_images/test_menu_layer_system_cells__cell_width_144~obelix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__cell_width_144~robert.png rename to tests/test_images/test_menu_layer_system_cells__cell_width_144~obelix.png diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_144~snowy.png b/tests/test_images/test_menu_layer_system_cells__cell_width_144~snowy.png deleted file mode 100644 index d55242892b..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_144~snowy.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_144~spalding.png b/tests/test_images/test_menu_layer_system_cells__cell_width_144~spalding.png deleted file mode 100644 index 95b93a76b3..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_144~spalding.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_180~silk.png b/tests/test_images/test_menu_layer_system_cells__cell_width_180~asterix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__cell_width_180~silk.png rename to tests/test_images/test_menu_layer_system_cells__cell_width_180~asterix.png diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_180~gabbro.png b/tests/test_images/test_menu_layer_system_cells__cell_width_180~gabbro.png new file mode 100644 index 0000000000..1399455130 Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__cell_width_180~gabbro.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_180~robert.png b/tests/test_images/test_menu_layer_system_cells__cell_width_180~obelix.png similarity index 100% rename from tests/test_images/test_menu_layer_system_cells__cell_width_180~robert.png rename to tests/test_images/test_menu_layer_system_cells__cell_width_180~obelix.png diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_180~snowy.png b/tests/test_images/test_menu_layer_system_cells__cell_width_180~snowy.png deleted file mode 100644 index 54613cce0e..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_180~snowy.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_180~spalding.png b/tests/test_images/test_menu_layer_system_cells__cell_width_180~spalding.png deleted file mode 100644 index a753e16623..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_180~spalding.png and /dev/null differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_32~gabbro.png b/tests/test_images/test_menu_layer_system_cells__cell_width_32~gabbro.png new file mode 100644 index 0000000000..84752fcc53 Binary files /dev/null and b/tests/test_images/test_menu_layer_system_cells__cell_width_32~gabbro.png differ diff --git a/tests/test_images/test_menu_layer_system_cells__cell_width_32~spalding.png b/tests/test_images/test_menu_layer_system_cells__cell_width_32~spalding.png deleted file mode 100644 index f87716bce0..0000000000 Binary files a/tests/test_images/test_menu_layer_system_cells__cell_width_32~spalding.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__big_bold~gabbro.png b/tests/test_images/test_notification_window__big_bold~gabbro.png new file mode 100644 index 0000000000..269eafafd4 Binary files /dev/null and b/tests/test_images/test_notification_window__big_bold~gabbro.png differ diff --git a/tests/test_images/test_notification_window__big_bold~obelix.png b/tests/test_images/test_notification_window__big_bold~obelix.png new file mode 100644 index 0000000000..62f8d7cdb7 Binary files /dev/null and b/tests/test_images/test_notification_window__big_bold~obelix.png differ diff --git a/tests/test_images/test_notification_window__body_icon~silk.png b/tests/test_images/test_notification_window__body_icon~asterix.png similarity index 100% rename from tests/test_images/test_notification_window__body_icon~silk.png rename to tests/test_images/test_notification_window__body_icon~asterix.png diff --git a/tests/test_images/test_notification_window__body_icon~gabbro.png b/tests/test_images/test_notification_window__body_icon~gabbro.png new file mode 100644 index 0000000000..844a895e2a Binary files /dev/null and b/tests/test_images/test_notification_window__body_icon~gabbro.png differ diff --git a/tests/test_images/test_notification_window__body_icon~robert.png b/tests/test_images/test_notification_window__body_icon~obelix.png similarity index 100% rename from tests/test_images/test_notification_window__body_icon~robert.png rename to tests/test_images/test_notification_window__body_icon~obelix.png diff --git a/tests/test_images/test_notification_window__body_icon~snowy.png b/tests/test_images/test_notification_window__body_icon~snowy.png deleted file mode 100644 index b353388733..0000000000 Binary files a/tests/test_images/test_notification_window__body_icon~snowy.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__body_icon~spalding.png b/tests/test_images/test_notification_window__body_icon~spalding.png deleted file mode 100644 index 95ec01f42c..0000000000 Binary files a/tests/test_images/test_notification_window__body_icon~spalding.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__reminder~silk.png b/tests/test_images/test_notification_window__reminder~asterix.png similarity index 100% rename from tests/test_images/test_notification_window__reminder~silk.png rename to tests/test_images/test_notification_window__reminder~asterix.png diff --git a/tests/test_images/test_notification_window__reminder~gabbro.png b/tests/test_images/test_notification_window__reminder~gabbro.png new file mode 100644 index 0000000000..114ffaf8c2 Binary files /dev/null and b/tests/test_images/test_notification_window__reminder~gabbro.png differ diff --git a/tests/test_images/test_notification_window__reminder~robert.png b/tests/test_images/test_notification_window__reminder~obelix.png similarity index 100% rename from tests/test_images/test_notification_window__reminder~robert.png rename to tests/test_images/test_notification_window__reminder~obelix.png diff --git a/tests/test_images/test_notification_window__reminder~snowy.png b/tests/test_images/test_notification_window__reminder~snowy.png deleted file mode 100644 index 972c1c8e29..0000000000 Binary files a/tests/test_images/test_notification_window__reminder~snowy.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__reminder~spalding.png b/tests/test_images/test_notification_window__reminder~spalding.png deleted file mode 100644 index e068d5f5d7..0000000000 Binary files a/tests/test_images/test_notification_window__reminder~spalding.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__title_body~silk.png b/tests/test_images/test_notification_window__title_body~asterix.png similarity index 100% rename from tests/test_images/test_notification_window__title_body~silk.png rename to tests/test_images/test_notification_window__title_body~asterix.png diff --git a/tests/test_images/test_notification_window__title_body~gabbro.png b/tests/test_images/test_notification_window__title_body~gabbro.png new file mode 100644 index 0000000000..311041ec2d Binary files /dev/null and b/tests/test_images/test_notification_window__title_body~gabbro.png differ diff --git a/tests/test_images/test_notification_window__title_body~robert.png b/tests/test_images/test_notification_window__title_body~obelix.png similarity index 100% rename from tests/test_images/test_notification_window__title_body~robert.png rename to tests/test_images/test_notification_window__title_body~obelix.png diff --git a/tests/test_images/test_notification_window__title_body~snowy.png b/tests/test_images/test_notification_window__title_body~snowy.png deleted file mode 100644 index a237db1696..0000000000 Binary files a/tests/test_images/test_notification_window__title_body~snowy.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__title_body~spalding.png b/tests/test_images/test_notification_window__title_body~spalding.png deleted file mode 100644 index f2f685643d..0000000000 Binary files a/tests/test_images/test_notification_window__title_body~spalding.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__title_subtitle_body~silk.png b/tests/test_images/test_notification_window__title_subtitle_body~asterix.png similarity index 100% rename from tests/test_images/test_notification_window__title_subtitle_body~silk.png rename to tests/test_images/test_notification_window__title_subtitle_body~asterix.png diff --git a/tests/test_images/test_notification_window__title_subtitle_body~gabbro.png b/tests/test_images/test_notification_window__title_subtitle_body~gabbro.png new file mode 100644 index 0000000000..fa50ee9272 Binary files /dev/null and b/tests/test_images/test_notification_window__title_subtitle_body~gabbro.png differ diff --git a/tests/test_images/test_notification_window__title_subtitle_body~robert.png b/tests/test_images/test_notification_window__title_subtitle_body~obelix.png similarity index 100% rename from tests/test_images/test_notification_window__title_subtitle_body~robert.png rename to tests/test_images/test_notification_window__title_subtitle_body~obelix.png diff --git a/tests/test_images/test_notification_window__title_subtitle_body~snowy.png b/tests/test_images/test_notification_window__title_subtitle_body~snowy.png deleted file mode 100644 index 32b06c43d7..0000000000 Binary files a/tests/test_images/test_notification_window__title_subtitle_body~snowy.png and /dev/null differ diff --git a/tests/test_images/test_notification_window__title_subtitle_body~spalding.png b/tests/test_images/test_notification_window__title_subtitle_body~spalding.png deleted file mode 100644 index 0828297d1f..0000000000 Binary files a/tests/test_images/test_notification_window__title_subtitle_body~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height_icons~silk.png b/tests/test_images/test_option_menu_window__long_title_default_height_icons~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__long_title_default_height_icons~silk.png rename to tests/test_images/test_option_menu_window__long_title_default_height_icons~asterix.png diff --git a/tests/test_images/test_option_menu_window__long_title_default_height_icons~gabbro.png b/tests/test_images/test_option_menu_window__long_title_default_height_icons~gabbro.png new file mode 100644 index 0000000000..3863ede05f Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_default_height_icons~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height_icons~obelix.png b/tests/test_images/test_option_menu_window__long_title_default_height_icons~obelix.png new file mode 100644 index 0000000000..3785fbf6ae Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_default_height_icons~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height_icons~robert.png b/tests/test_images/test_option_menu_window__long_title_default_height_icons~robert.png deleted file mode 100644 index e25b7b2863..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_default_height_icons~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height_icons~snowy.png b/tests/test_images/test_option_menu_window__long_title_default_height_icons~snowy.png deleted file mode 100644 index 66bba92b2c..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_default_height_icons~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height_icons~spalding.png b/tests/test_images/test_option_menu_window__long_title_default_height_icons~spalding.png deleted file mode 100644 index bba75235bf..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_default_height_icons~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height~silk.png b/tests/test_images/test_option_menu_window__long_title_default_height~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__long_title_default_height~silk.png rename to tests/test_images/test_option_menu_window__long_title_default_height~asterix.png diff --git a/tests/test_images/test_option_menu_window__long_title_default_height~gabbro.png b/tests/test_images/test_option_menu_window__long_title_default_height~gabbro.png new file mode 100644 index 0000000000..04a922039c Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_default_height~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height~obelix.png b/tests/test_images/test_option_menu_window__long_title_default_height~obelix.png new file mode 100644 index 0000000000..e58c72c646 Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_default_height~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height~robert.png b/tests/test_images/test_option_menu_window__long_title_default_height~robert.png deleted file mode 100644 index 7fabde5d07..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_default_height~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height~snowy.png b/tests/test_images/test_option_menu_window__long_title_default_height~snowy.png deleted file mode 100644 index 703f23ba57..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_default_height~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_default_height~spalding.png b/tests/test_images/test_option_menu_window__long_title_default_height~spalding.png deleted file mode 100644 index 9b2f1a6547..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_default_height~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height_icons~silk.png b/tests/test_images/test_option_menu_window__long_title_special_height_icons~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__long_title_special_height_icons~silk.png rename to tests/test_images/test_option_menu_window__long_title_special_height_icons~asterix.png diff --git a/tests/test_images/test_option_menu_window__long_title_special_height_icons~gabbro.png b/tests/test_images/test_option_menu_window__long_title_special_height_icons~gabbro.png new file mode 100644 index 0000000000..56d9c7bd43 Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_special_height_icons~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height_icons~obelix.png b/tests/test_images/test_option_menu_window__long_title_special_height_icons~obelix.png new file mode 100644 index 0000000000..832a87131c Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_special_height_icons~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height_icons~robert.png b/tests/test_images/test_option_menu_window__long_title_special_height_icons~robert.png deleted file mode 100644 index 17c086dc40..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_special_height_icons~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height_icons~snowy.png b/tests/test_images/test_option_menu_window__long_title_special_height_icons~snowy.png deleted file mode 100644 index db4b2aeef6..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_special_height_icons~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height_icons~spalding.png b/tests/test_images/test_option_menu_window__long_title_special_height_icons~spalding.png deleted file mode 100644 index 8bfa6831b9..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_special_height_icons~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height~silk.png b/tests/test_images/test_option_menu_window__long_title_special_height~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__long_title_special_height~silk.png rename to tests/test_images/test_option_menu_window__long_title_special_height~asterix.png diff --git a/tests/test_images/test_option_menu_window__long_title_special_height~gabbro.png b/tests/test_images/test_option_menu_window__long_title_special_height~gabbro.png new file mode 100644 index 0000000000..a321ccc31f Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_special_height~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height~obelix.png b/tests/test_images/test_option_menu_window__long_title_special_height~obelix.png new file mode 100644 index 0000000000..7f937969d0 Binary files /dev/null and b/tests/test_images/test_option_menu_window__long_title_special_height~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height~robert.png b/tests/test_images/test_option_menu_window__long_title_special_height~robert.png deleted file mode 100644 index 6957bab3be..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_special_height~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height~snowy.png b/tests/test_images/test_option_menu_window__long_title_special_height~snowy.png deleted file mode 100644 index 7fa4c9cffc..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_special_height~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__long_title_special_height~spalding.png b/tests/test_images/test_option_menu_window__long_title_special_height~spalding.png deleted file mode 100644 index 77f581cad1..0000000000 Binary files a/tests/test_images/test_option_menu_window__long_title_special_height~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height_icons~silk.png b/tests/test_images/test_option_menu_window__short_title_default_height_icons~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__short_title_default_height_icons~silk.png rename to tests/test_images/test_option_menu_window__short_title_default_height_icons~asterix.png diff --git a/tests/test_images/test_option_menu_window__short_title_default_height_icons~gabbro.png b/tests/test_images/test_option_menu_window__short_title_default_height_icons~gabbro.png new file mode 100644 index 0000000000..7cfb6bc800 Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_default_height_icons~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height_icons~obelix.png b/tests/test_images/test_option_menu_window__short_title_default_height_icons~obelix.png new file mode 100644 index 0000000000..e21a6ca915 Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_default_height_icons~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height_icons~robert.png b/tests/test_images/test_option_menu_window__short_title_default_height_icons~robert.png deleted file mode 100644 index 871e623137..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_default_height_icons~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height_icons~snowy.png b/tests/test_images/test_option_menu_window__short_title_default_height_icons~snowy.png deleted file mode 100644 index 3bb2da4317..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_default_height_icons~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height_icons~spalding.png b/tests/test_images/test_option_menu_window__short_title_default_height_icons~spalding.png deleted file mode 100644 index f085911ce9..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_default_height_icons~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height~silk.png b/tests/test_images/test_option_menu_window__short_title_default_height~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__short_title_default_height~silk.png rename to tests/test_images/test_option_menu_window__short_title_default_height~asterix.png diff --git a/tests/test_images/test_option_menu_window__short_title_default_height~gabbro.png b/tests/test_images/test_option_menu_window__short_title_default_height~gabbro.png new file mode 100644 index 0000000000..4182619582 Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_default_height~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height~obelix.png b/tests/test_images/test_option_menu_window__short_title_default_height~obelix.png new file mode 100644 index 0000000000..d4060c7754 Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_default_height~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height~robert.png b/tests/test_images/test_option_menu_window__short_title_default_height~robert.png deleted file mode 100644 index 44be1cccc4..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_default_height~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height~snowy.png b/tests/test_images/test_option_menu_window__short_title_default_height~snowy.png deleted file mode 100644 index 9cf4b800c4..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_default_height~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_default_height~spalding.png b/tests/test_images/test_option_menu_window__short_title_default_height~spalding.png deleted file mode 100644 index 3d947636b3..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_default_height~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height_icons~silk.png b/tests/test_images/test_option_menu_window__short_title_special_height_icons~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__short_title_special_height_icons~silk.png rename to tests/test_images/test_option_menu_window__short_title_special_height_icons~asterix.png diff --git a/tests/test_images/test_option_menu_window__short_title_special_height_icons~gabbro.png b/tests/test_images/test_option_menu_window__short_title_special_height_icons~gabbro.png new file mode 100644 index 0000000000..5219bfde6a Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_special_height_icons~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height_icons~obelix.png b/tests/test_images/test_option_menu_window__short_title_special_height_icons~obelix.png new file mode 100644 index 0000000000..98ba403e8e Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_special_height_icons~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height_icons~robert.png b/tests/test_images/test_option_menu_window__short_title_special_height_icons~robert.png deleted file mode 100644 index 54ec3d2650..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_special_height_icons~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height_icons~snowy.png b/tests/test_images/test_option_menu_window__short_title_special_height_icons~snowy.png deleted file mode 100644 index 3eccb42142..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_special_height_icons~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height_icons~spalding.png b/tests/test_images/test_option_menu_window__short_title_special_height_icons~spalding.png deleted file mode 100644 index a0904aa0e9..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_special_height_icons~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height~silk.png b/tests/test_images/test_option_menu_window__short_title_special_height~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__short_title_special_height~silk.png rename to tests/test_images/test_option_menu_window__short_title_special_height~asterix.png diff --git a/tests/test_images/test_option_menu_window__short_title_special_height~gabbro.png b/tests/test_images/test_option_menu_window__short_title_special_height~gabbro.png new file mode 100644 index 0000000000..e093db0884 Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_special_height~gabbro.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height~obelix.png b/tests/test_images/test_option_menu_window__short_title_special_height~obelix.png new file mode 100644 index 0000000000..4e86494b52 Binary files /dev/null and b/tests/test_images/test_option_menu_window__short_title_special_height~obelix.png differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height~robert.png b/tests/test_images/test_option_menu_window__short_title_special_height~robert.png deleted file mode 100644 index 9fd9c99786..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_special_height~robert.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height~snowy.png b/tests/test_images/test_option_menu_window__short_title_special_height~snowy.png deleted file mode 100644 index f15d6a7392..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_special_height~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__short_title_special_height~spalding.png b/tests/test_images/test_option_menu_window__short_title_special_height~spalding.png deleted file mode 100644 index 60d1c99892..0000000000 Binary files a/tests/test_images/test_option_menu_window__short_title_special_height~spalding.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__title_and_subtitle_future~tintin.png b/tests/test_images/test_option_menu_window__title_and_subtitle_future~asterix.png similarity index 100% rename from tests/test_images/test_option_menu_window__title_and_subtitle_future~tintin.png rename to tests/test_images/test_option_menu_window__title_and_subtitle_future~asterix.png diff --git a/tests/test_images/test_option_menu_window__title_and_subtitle_future~snowy.png b/tests/test_images/test_option_menu_window__title_and_subtitle_future~snowy.png deleted file mode 100644 index 52e08f8284..0000000000 Binary files a/tests/test_images/test_option_menu_window__title_and_subtitle_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_option_menu_window__title_and_subtitle_future~spalding.png b/tests/test_images/test_option_menu_window__title_and_subtitle_future~spalding.png deleted file mode 100644 index 6c33a23458..0000000000 Binary files a/tests/test_images/test_option_menu_window__title_and_subtitle_future~spalding.png and /dev/null differ diff --git a/tests/test_images/test_selection_windows__time_range_selection_window~tintin.png b/tests/test_images/test_selection_windows__time_range_selection_window~asterix.png similarity index 100% rename from tests/test_images/test_selection_windows__time_range_selection_window~tintin.png rename to tests/test_images/test_selection_windows__time_range_selection_window~asterix.png diff --git a/tests/test_images/test_selection_windows__time_range_selection_window~gabbro.png b/tests/test_images/test_selection_windows__time_range_selection_window~gabbro.png new file mode 100644 index 0000000000..20644c5b5d Binary files /dev/null and b/tests/test_images/test_selection_windows__time_range_selection_window~gabbro.png differ diff --git a/tests/test_images/test_selection_windows__time_range_selection_window~robert.png b/tests/test_images/test_selection_windows__time_range_selection_window~obelix.png similarity index 100% rename from tests/test_images/test_selection_windows__time_range_selection_window~robert.png rename to tests/test_images/test_selection_windows__time_range_selection_window~obelix.png diff --git a/tests/test_images/test_selection_windows__time_range_selection_window~snowy.png b/tests/test_images/test_selection_windows__time_range_selection_window~snowy.png deleted file mode 100644 index ca48503e87..0000000000 Binary files a/tests/test_images/test_selection_windows__time_range_selection_window~snowy.png and /dev/null differ diff --git a/tests/test_images/test_selection_windows__time_range_selection_window~spalding.png b/tests/test_images/test_selection_windows__time_range_selection_window~spalding.png deleted file mode 100644 index 78d53804c9..0000000000 Binary files a/tests/test_images/test_selection_windows__time_range_selection_window~spalding.png and /dev/null differ diff --git a/tests/test_images/test_selection_windows__time_selection_window~tintin.png b/tests/test_images/test_selection_windows__time_selection_window~asterix.png similarity index 100% rename from tests/test_images/test_selection_windows__time_selection_window~tintin.png rename to tests/test_images/test_selection_windows__time_selection_window~asterix.png diff --git a/tests/test_images/test_selection_windows__time_selection_window~gabbro.png b/tests/test_images/test_selection_windows__time_selection_window~gabbro.png new file mode 100644 index 0000000000..70667113d6 Binary files /dev/null and b/tests/test_images/test_selection_windows__time_selection_window~gabbro.png differ diff --git a/tests/test_images/test_selection_windows__time_selection_window~robert.png b/tests/test_images/test_selection_windows__time_selection_window~obelix.png similarity index 100% rename from tests/test_images/test_selection_windows__time_selection_window~robert.png rename to tests/test_images/test_selection_windows__time_selection_window~obelix.png diff --git a/tests/test_images/test_selection_windows__time_selection_window~snowy.png b/tests/test_images/test_selection_windows__time_selection_window~snowy.png deleted file mode 100644 index fc96198559..0000000000 Binary files a/tests/test_images/test_selection_windows__time_selection_window~snowy.png and /dev/null differ diff --git a/tests/test_images/test_selection_windows__time_selection_window~spalding.png b/tests/test_images/test_selection_windows__time_selection_window~spalding.png deleted file mode 100644 index 7e1f377d1d..0000000000 Binary files a/tests/test_images/test_selection_windows__time_selection_window~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__alarm_deleted~asterix.png b/tests/test_images/test_simple_dialog__alarm_deleted~asterix.png new file mode 100644 index 0000000000..03b2a28019 Binary files /dev/null and b/tests/test_images/test_simple_dialog__alarm_deleted~asterix.png differ diff --git a/tests/test_images/test_simple_dialog__alarm_deleted~gabbro.png b/tests/test_images/test_simple_dialog__alarm_deleted~gabbro.png new file mode 100644 index 0000000000..2b69d8d4cd Binary files /dev/null and b/tests/test_images/test_simple_dialog__alarm_deleted~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__alarm_deleted~obelix.png b/tests/test_images/test_simple_dialog__alarm_deleted~obelix.png new file mode 100644 index 0000000000..dce5683636 Binary files /dev/null and b/tests/test_images/test_simple_dialog__alarm_deleted~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__alarm_deleted~snowy.png b/tests/test_images/test_simple_dialog__alarm_deleted~snowy.png deleted file mode 100644 index efb370659a..0000000000 Binary files a/tests/test_images/test_simple_dialog__alarm_deleted~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__alarm_deleted~spalding.png b/tests/test_images/test_simple_dialog__alarm_deleted~spalding.png deleted file mode 100644 index aed7a0b0ad..0000000000 Binary files a/tests/test_images/test_simple_dialog__alarm_deleted~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__alarm_deleted~tintin.png b/tests/test_images/test_simple_dialog__alarm_deleted~tintin.png deleted file mode 100644 index 60ea1abc6d..0000000000 Binary files a/tests/test_images/test_simple_dialog__alarm_deleted~tintin.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__alarm_snooze~asterix.png b/tests/test_images/test_simple_dialog__alarm_snooze~asterix.png new file mode 100644 index 0000000000..8897192d6e Binary files /dev/null and b/tests/test_images/test_simple_dialog__alarm_snooze~asterix.png differ diff --git a/tests/test_images/test_simple_dialog__alarm_snooze~gabbro.png b/tests/test_images/test_simple_dialog__alarm_snooze~gabbro.png new file mode 100644 index 0000000000..15ba5f2b2c Binary files /dev/null and b/tests/test_images/test_simple_dialog__alarm_snooze~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__alarm_snooze~obelix.png b/tests/test_images/test_simple_dialog__alarm_snooze~obelix.png new file mode 100644 index 0000000000..52ec0cfeeb Binary files /dev/null and b/tests/test_images/test_simple_dialog__alarm_snooze~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__alarm_snooze~snowy.png b/tests/test_images/test_simple_dialog__alarm_snooze~snowy.png deleted file mode 100644 index 868d4cb851..0000000000 Binary files a/tests/test_images/test_simple_dialog__alarm_snooze~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__alarm_snooze~spalding.png b/tests/test_images/test_simple_dialog__alarm_snooze~spalding.png deleted file mode 100644 index 86ad7dea93..0000000000 Binary files a/tests/test_images/test_simple_dialog__alarm_snooze~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__alarm_snooze~tintin.png b/tests/test_images/test_simple_dialog__alarm_snooze~tintin.png deleted file mode 100644 index fa5c546903..0000000000 Binary files a/tests/test_images/test_simple_dialog__alarm_snooze~tintin.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__battery_charged~asterix.png b/tests/test_images/test_simple_dialog__battery_charged~asterix.png new file mode 100644 index 0000000000..95df9de60a Binary files /dev/null and b/tests/test_images/test_simple_dialog__battery_charged~asterix.png differ diff --git a/tests/test_images/test_simple_dialog__battery_charged~gabbro.png b/tests/test_images/test_simple_dialog__battery_charged~gabbro.png new file mode 100644 index 0000000000..d171d9194e Binary files /dev/null and b/tests/test_images/test_simple_dialog__battery_charged~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__battery_charged~obelix.png b/tests/test_images/test_simple_dialog__battery_charged~obelix.png new file mode 100644 index 0000000000..7518488433 Binary files /dev/null and b/tests/test_images/test_simple_dialog__battery_charged~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__battery_charged~snowy.png b/tests/test_images/test_simple_dialog__battery_charged~snowy.png deleted file mode 100644 index fe0e0ce336..0000000000 Binary files a/tests/test_images/test_simple_dialog__battery_charged~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__battery_charged~spalding.png b/tests/test_images/test_simple_dialog__battery_charged~spalding.png deleted file mode 100644 index 72bb57e890..0000000000 Binary files a/tests/test_images/test_simple_dialog__battery_charged~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__battery_charged~tintin.png b/tests/test_images/test_simple_dialog__battery_charged~tintin.png deleted file mode 100644 index 009de02524..0000000000 Binary files a/tests/test_images/test_simple_dialog__battery_charged~tintin.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__battery_warning~asterix.png b/tests/test_images/test_simple_dialog__battery_warning~asterix.png new file mode 100644 index 0000000000..1709f78679 Binary files /dev/null and b/tests/test_images/test_simple_dialog__battery_warning~asterix.png differ diff --git a/tests/test_images/test_simple_dialog__battery_warning~gabbro.png b/tests/test_images/test_simple_dialog__battery_warning~gabbro.png new file mode 100644 index 0000000000..c9f5908567 Binary files /dev/null and b/tests/test_images/test_simple_dialog__battery_warning~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__battery_warning~obelix.png b/tests/test_images/test_simple_dialog__battery_warning~obelix.png new file mode 100644 index 0000000000..8d0a3272f9 Binary files /dev/null and b/tests/test_images/test_simple_dialog__battery_warning~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__battery_warning~snowy.png b/tests/test_images/test_simple_dialog__battery_warning~snowy.png deleted file mode 100644 index 9edf00279f..0000000000 Binary files a/tests/test_images/test_simple_dialog__battery_warning~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__battery_warning~spalding.png b/tests/test_images/test_simple_dialog__battery_warning~spalding.png deleted file mode 100644 index 3e4033564b..0000000000 Binary files a/tests/test_images/test_simple_dialog__battery_warning~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__battery_warning~tintin.png b/tests/test_images/test_simple_dialog__battery_warning~tintin.png deleted file mode 100644 index 693fb47fc8..0000000000 Binary files a/tests/test_images/test_simple_dialog__battery_warning~tintin.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__calendar_unmute~tintin.png b/tests/test_images/test_simple_dialog__calendar_unmute~asterix.png similarity index 100% rename from tests/test_images/test_simple_dialog__calendar_unmute~tintin.png rename to tests/test_images/test_simple_dialog__calendar_unmute~asterix.png diff --git a/tests/test_images/test_simple_dialog__calendar_unmute~gabbro.png b/tests/test_images/test_simple_dialog__calendar_unmute~gabbro.png new file mode 100644 index 0000000000..2b8608f383 Binary files /dev/null and b/tests/test_images/test_simple_dialog__calendar_unmute~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__calendar_unmute~obelix.png b/tests/test_images/test_simple_dialog__calendar_unmute~obelix.png new file mode 100644 index 0000000000..33295540bb Binary files /dev/null and b/tests/test_images/test_simple_dialog__calendar_unmute~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__calendar_unmute~snowy.png b/tests/test_images/test_simple_dialog__calendar_unmute~snowy.png deleted file mode 100644 index 1813514f8e..0000000000 Binary files a/tests/test_images/test_simple_dialog__calendar_unmute~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__calendar_unmute~spalding.png b/tests/test_images/test_simple_dialog__calendar_unmute~spalding.png deleted file mode 100644 index d7edd37e99..0000000000 Binary files a/tests/test_images/test_simple_dialog__calendar_unmute~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__ping~asterix.png b/tests/test_images/test_simple_dialog__ping~asterix.png new file mode 100644 index 0000000000..b9ee637809 Binary files /dev/null and b/tests/test_images/test_simple_dialog__ping~asterix.png differ diff --git a/tests/test_images/test_simple_dialog__ping~gabbro.png b/tests/test_images/test_simple_dialog__ping~gabbro.png new file mode 100644 index 0000000000..baf9e87ab0 Binary files /dev/null and b/tests/test_images/test_simple_dialog__ping~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__ping~obelix.png b/tests/test_images/test_simple_dialog__ping~obelix.png new file mode 100644 index 0000000000..ee756aad55 Binary files /dev/null and b/tests/test_images/test_simple_dialog__ping~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__ping~snowy.png b/tests/test_images/test_simple_dialog__ping~snowy.png deleted file mode 100644 index c007d70854..0000000000 Binary files a/tests/test_images/test_simple_dialog__ping~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__ping~spalding.png b/tests/test_images/test_simple_dialog__ping~spalding.png deleted file mode 100644 index aea218ff1f..0000000000 Binary files a/tests/test_images/test_simple_dialog__ping~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__ping~tintin.png b/tests/test_images/test_simple_dialog__ping~tintin.png deleted file mode 100644 index 6470173c53..0000000000 Binary files a/tests/test_images/test_simple_dialog__ping~tintin.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__watchface_crashed~asterix.png b/tests/test_images/test_simple_dialog__watchface_crashed~asterix.png new file mode 100644 index 0000000000..6033d4b1f8 Binary files /dev/null and b/tests/test_images/test_simple_dialog__watchface_crashed~asterix.png differ diff --git a/tests/test_images/test_simple_dialog__watchface_crashed~gabbro.png b/tests/test_images/test_simple_dialog__watchface_crashed~gabbro.png new file mode 100644 index 0000000000..2a48775ac0 Binary files /dev/null and b/tests/test_images/test_simple_dialog__watchface_crashed~gabbro.png differ diff --git a/tests/test_images/test_simple_dialog__watchface_crashed~obelix.png b/tests/test_images/test_simple_dialog__watchface_crashed~obelix.png new file mode 100644 index 0000000000..64d8220360 Binary files /dev/null and b/tests/test_images/test_simple_dialog__watchface_crashed~obelix.png differ diff --git a/tests/test_images/test_simple_dialog__watchface_crashed~snowy.png b/tests/test_images/test_simple_dialog__watchface_crashed~snowy.png deleted file mode 100644 index 585745380b..0000000000 Binary files a/tests/test_images/test_simple_dialog__watchface_crashed~snowy.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__watchface_crashed~spalding.png b/tests/test_images/test_simple_dialog__watchface_crashed~spalding.png deleted file mode 100644 index 7bbd92cec8..0000000000 Binary files a/tests/test_images/test_simple_dialog__watchface_crashed~spalding.png and /dev/null differ diff --git a/tests/test_images/test_simple_dialog__watchface_crashed~tintin.png b/tests/test_images/test_simple_dialog__watchface_crashed~tintin.png deleted file mode 100644 index 444b56f648..0000000000 Binary files a/tests/test_images/test_simple_dialog__watchface_crashed~tintin.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__generic~silk_details1.png b/tests/test_images/test_timeline_layouts__generic~asterix_details1.png similarity index 100% rename from tests/test_images/test_timeline_layouts__generic~silk_details1.png rename to tests/test_images/test_timeline_layouts__generic~asterix_details1.png diff --git a/tests/test_images/test_timeline_layouts__generic~silk_details2.png b/tests/test_images/test_timeline_layouts__generic~asterix_details2.png similarity index 100% rename from tests/test_images/test_timeline_layouts__generic~silk_details2.png rename to tests/test_images/test_timeline_layouts__generic~asterix_details2.png diff --git a/tests/test_images/test_timeline_layouts__generic~silk_peek.png b/tests/test_images/test_timeline_layouts__generic~asterix_peek.png similarity index 100% rename from tests/test_images/test_timeline_layouts__generic~silk_peek.png rename to tests/test_images/test_timeline_layouts__generic~asterix_peek.png diff --git a/tests/test_images/test_timeline_layouts__generic~gabbro_details1.png b/tests/test_images/test_timeline_layouts__generic~gabbro_details1.png new file mode 100644 index 0000000000..5340a6dcb2 Binary files /dev/null and b/tests/test_images/test_timeline_layouts__generic~gabbro_details1.png differ diff --git a/tests/test_images/test_timeline_layouts__generic~gabbro_peek.png b/tests/test_images/test_timeline_layouts__generic~gabbro_peek.png new file mode 100644 index 0000000000..83ec3fb8b2 Binary files /dev/null and b/tests/test_images/test_timeline_layouts__generic~gabbro_peek.png differ diff --git a/tests/test_images/test_timeline_layouts__generic~robert_details1.png b/tests/test_images/test_timeline_layouts__generic~obelix_details1.png similarity index 100% rename from tests/test_images/test_timeline_layouts__generic~robert_details1.png rename to tests/test_images/test_timeline_layouts__generic~obelix_details1.png diff --git a/tests/test_images/test_timeline_layouts__generic~robert_details2.png b/tests/test_images/test_timeline_layouts__generic~obelix_details2.png similarity index 100% rename from tests/test_images/test_timeline_layouts__generic~robert_details2.png rename to tests/test_images/test_timeline_layouts__generic~obelix_details2.png diff --git a/tests/test_images/test_timeline_layouts__generic~robert_peek.png b/tests/test_images/test_timeline_layouts__generic~obelix_peek.png similarity index 100% rename from tests/test_images/test_timeline_layouts__generic~robert_peek.png rename to tests/test_images/test_timeline_layouts__generic~obelix_peek.png diff --git a/tests/test_images/test_timeline_layouts__generic~snowy_details1.png b/tests/test_images/test_timeline_layouts__generic~snowy_details1.png deleted file mode 100644 index 5f7e4ebd0c..0000000000 Binary files a/tests/test_images/test_timeline_layouts__generic~snowy_details1.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__generic~snowy_details2.png b/tests/test_images/test_timeline_layouts__generic~snowy_details2.png deleted file mode 100644 index db6aafd2da..0000000000 Binary files a/tests/test_images/test_timeline_layouts__generic~snowy_details2.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__generic~snowy_peek.png b/tests/test_images/test_timeline_layouts__generic~snowy_peek.png deleted file mode 100644 index 887bab4b92..0000000000 Binary files a/tests/test_images/test_timeline_layouts__generic~snowy_peek.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__generic~spalding_details1.png b/tests/test_images/test_timeline_layouts__generic~spalding_details1.png deleted file mode 100644 index 3589c9a788..0000000000 Binary files a/tests/test_images/test_timeline_layouts__generic~spalding_details1.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__generic~spalding_peek.png b/tests/test_images/test_timeline_layouts__generic~spalding_peek.png deleted file mode 100644 index f95b3ae481..0000000000 Binary files a/tests/test_images/test_timeline_layouts__generic~spalding_peek.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__weather~silk_details1.png b/tests/test_images/test_timeline_layouts__weather~asterix_details1.png similarity index 100% rename from tests/test_images/test_timeline_layouts__weather~silk_details1.png rename to tests/test_images/test_timeline_layouts__weather~asterix_details1.png diff --git a/tests/test_images/test_timeline_layouts__weather~silk_peek.png b/tests/test_images/test_timeline_layouts__weather~asterix_peek.png similarity index 100% rename from tests/test_images/test_timeline_layouts__weather~silk_peek.png rename to tests/test_images/test_timeline_layouts__weather~asterix_peek.png diff --git a/tests/test_images/test_timeline_layouts__weather~gabbro_details1.png b/tests/test_images/test_timeline_layouts__weather~gabbro_details1.png new file mode 100644 index 0000000000..c84532fd42 Binary files /dev/null and b/tests/test_images/test_timeline_layouts__weather~gabbro_details1.png differ diff --git a/tests/test_images/test_timeline_layouts__weather~gabbro_details2.png b/tests/test_images/test_timeline_layouts__weather~gabbro_details2.png new file mode 100644 index 0000000000..c84532fd42 Binary files /dev/null and b/tests/test_images/test_timeline_layouts__weather~gabbro_details2.png differ diff --git a/tests/test_images/test_timeline_layouts__weather~gabbro_peek.png b/tests/test_images/test_timeline_layouts__weather~gabbro_peek.png new file mode 100644 index 0000000000..a7b2e8bb2a Binary files /dev/null and b/tests/test_images/test_timeline_layouts__weather~gabbro_peek.png differ diff --git a/tests/test_images/test_timeline_layouts__weather~robert_details1.png b/tests/test_images/test_timeline_layouts__weather~obelix_details1.png similarity index 100% rename from tests/test_images/test_timeline_layouts__weather~robert_details1.png rename to tests/test_images/test_timeline_layouts__weather~obelix_details1.png diff --git a/tests/test_images/test_timeline_layouts__weather~obelix_peek.png b/tests/test_images/test_timeline_layouts__weather~obelix_peek.png new file mode 100644 index 0000000000..6160bfd2b2 Binary files /dev/null and b/tests/test_images/test_timeline_layouts__weather~obelix_peek.png differ diff --git a/tests/test_images/test_timeline_layouts__weather~robert_peek.png b/tests/test_images/test_timeline_layouts__weather~robert_peek.png deleted file mode 100644 index 65bd231033..0000000000 Binary files a/tests/test_images/test_timeline_layouts__weather~robert_peek.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__weather~snowy_details1.png b/tests/test_images/test_timeline_layouts__weather~snowy_details1.png deleted file mode 100644 index eab457c5cc..0000000000 Binary files a/tests/test_images/test_timeline_layouts__weather~snowy_details1.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__weather~snowy_peek.png b/tests/test_images/test_timeline_layouts__weather~snowy_peek.png deleted file mode 100644 index 7fe55a7cce..0000000000 Binary files a/tests/test_images/test_timeline_layouts__weather~snowy_peek.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__weather~spalding_details1.png b/tests/test_images/test_timeline_layouts__weather~spalding_details1.png deleted file mode 100644 index 4ffb14626e..0000000000 Binary files a/tests/test_images/test_timeline_layouts__weather~spalding_details1.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__weather~spalding_details2.png b/tests/test_images/test_timeline_layouts__weather~spalding_details2.png deleted file mode 100644 index 25a29d73b4..0000000000 Binary files a/tests/test_images/test_timeline_layouts__weather~spalding_details2.png and /dev/null differ diff --git a/tests/test_images/test_timeline_layouts__weather~spalding_peek.png b/tests/test_images/test_timeline_layouts__weather~spalding_peek.png deleted file mode 100644 index ff6cdc8962..0000000000 Binary files a/tests/test_images/test_timeline_layouts__weather~spalding_peek.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~silk.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~silk.png rename to tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~asterix.png diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~gabbro.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~gabbro.png new file mode 100644 index 0000000000..4cbe9e7133 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~robert.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~obelix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~robert.png rename to tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~obelix.png diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~snowy.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~snowy.png deleted file mode 100644 index fdc9c4f47f..0000000000 Binary files a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~spalding.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~spalding.png deleted file mode 100644 index aeb78d8a9d..0000000000 Binary files a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_future~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~silk.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~silk.png rename to tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~asterix.png diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~gabbro.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~gabbro.png new file mode 100644 index 0000000000..3acc31c61d Binary files /dev/null and b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~robert.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~obelix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~robert.png rename to tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~obelix.png diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~snowy.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~snowy.png deleted file mode 100644 index 5bd6531cae..0000000000 Binary files a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~spalding.png b/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~spalding.png deleted file mode 100644 index 1085c6b4a3..0000000000 Binary files a/tests/test_images/test_timeline_list_view__day_sep_tomorrow_past~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_future~silk.png b/tests/test_images/test_timeline_list_view__pin_and_dot_future~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__pin_and_dot_future~silk.png rename to tests/test_images/test_timeline_list_view__pin_and_dot_future~asterix.png diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_future~gabbro.png b/tests/test_images/test_timeline_list_view__pin_and_dot_future~gabbro.png new file mode 100644 index 0000000000..8a41f45d46 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_dot_future~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_future~obelix.png b/tests/test_images/test_timeline_list_view__pin_and_dot_future~obelix.png new file mode 100644 index 0000000000..7675608a84 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_dot_future~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_future~robert.png b/tests/test_images/test_timeline_list_view__pin_and_dot_future~robert.png deleted file mode 100644 index 37d9d98b11..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_dot_future~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_future~snowy.png b/tests/test_images/test_timeline_list_view__pin_and_dot_future~snowy.png deleted file mode 100644 index dc80b4afd4..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_dot_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_future~spalding.png b/tests/test_images/test_timeline_list_view__pin_and_dot_future~spalding.png deleted file mode 100644 index 3645c9e15d..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_dot_future~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_past~silk.png b/tests/test_images/test_timeline_list_view__pin_and_dot_past~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__pin_and_dot_past~silk.png rename to tests/test_images/test_timeline_list_view__pin_and_dot_past~asterix.png diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_past~gabbro.png b/tests/test_images/test_timeline_list_view__pin_and_dot_past~gabbro.png new file mode 100644 index 0000000000..533508d348 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_dot_past~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_past~obelix.png b/tests/test_images/test_timeline_list_view__pin_and_dot_past~obelix.png new file mode 100644 index 0000000000..12961779a8 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_dot_past~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_past~robert.png b/tests/test_images/test_timeline_list_view__pin_and_dot_past~robert.png deleted file mode 100644 index 3b8c208aca..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_dot_past~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_past~snowy.png b/tests/test_images/test_timeline_list_view__pin_and_dot_past~snowy.png deleted file mode 100644 index 25c71ff0b8..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_dot_past~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_dot_past~spalding.png b/tests/test_images/test_timeline_list_view__pin_and_dot_past~spalding.png deleted file mode 100644 index 5f74dd99bb..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_dot_past~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_future~silk.png b/tests/test_images/test_timeline_list_view__pin_and_fin_future~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__pin_and_fin_future~silk.png rename to tests/test_images/test_timeline_list_view__pin_and_fin_future~asterix.png diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_future~gabbro.png b/tests/test_images/test_timeline_list_view__pin_and_fin_future~gabbro.png new file mode 100644 index 0000000000..2bf6ee028d Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_fin_future~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_future~obelix.png b/tests/test_images/test_timeline_list_view__pin_and_fin_future~obelix.png new file mode 100644 index 0000000000..81d9ca319b Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_fin_future~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_future~robert.png b/tests/test_images/test_timeline_list_view__pin_and_fin_future~robert.png deleted file mode 100644 index 9c4b327509..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_fin_future~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_future~snowy.png b/tests/test_images/test_timeline_list_view__pin_and_fin_future~snowy.png deleted file mode 100644 index cde53d0a46..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_fin_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_future~spalding.png b/tests/test_images/test_timeline_list_view__pin_and_fin_future~spalding.png deleted file mode 100644 index 3db509e8b9..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_fin_future~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_past~silk.png b/tests/test_images/test_timeline_list_view__pin_and_fin_past~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__pin_and_fin_past~silk.png rename to tests/test_images/test_timeline_list_view__pin_and_fin_past~asterix.png diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_past~gabbro.png b/tests/test_images/test_timeline_list_view__pin_and_fin_past~gabbro.png new file mode 100644 index 0000000000..f03c69d809 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_fin_past~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_past~obelix.png b/tests/test_images/test_timeline_list_view__pin_and_fin_past~obelix.png new file mode 100644 index 0000000000..2089935117 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__pin_and_fin_past~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_past~robert.png b/tests/test_images/test_timeline_list_view__pin_and_fin_past~robert.png deleted file mode 100644 index c271d7ffaf..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_fin_past~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_past~snowy.png b/tests/test_images/test_timeline_list_view__pin_and_fin_past~snowy.png deleted file mode 100644 index 7bff1cadb4..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_fin_past~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__pin_and_fin_past~spalding.png b/tests/test_images/test_timeline_list_view__pin_and_fin_past~spalding.png deleted file mode 100644 index 2d36f93454..0000000000 Binary files a/tests/test_images/test_timeline_list_view__pin_and_fin_past~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~silk.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~silk.png rename to tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~asterix.png diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~gabbro.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~gabbro.png new file mode 100644 index 0000000000..32f51312ca Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~obelix.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~obelix.png new file mode 100644 index 0000000000..f0500f1178 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~robert.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~robert.png deleted file mode 100644 index 54009dfc05..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~snowy.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~snowy.png deleted file mode 100644 index 42e7221706..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_back_to_back_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~silk.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~silk.png rename to tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~asterix.png diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~gabbro.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~gabbro.png new file mode 100644 index 0000000000..32f51312ca Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~obelix.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~obelix.png new file mode 100644 index 0000000000..5d14cc76a1 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~robert.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~robert.png deleted file mode 100644 index 455cf95330..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~snowy.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~snowy.png deleted file mode 100644 index 86c8dd80c3..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~spalding.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~spalding.png deleted file mode 100644 index 98bf61805e..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_future~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~silk.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~silk.png rename to tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~asterix.png diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~gabbro.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~gabbro.png new file mode 100644 index 0000000000..a1c06dc3d5 Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~obelix.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~obelix.png new file mode 100644 index 0000000000..e91605335c Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~robert.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~robert.png deleted file mode 100644 index c3b69ff23c..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~snowy.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~snowy.png deleted file mode 100644 index b8dfb19870..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~spalding.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~spalding.png deleted file mode 100644 index 38ec1ee42b..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_free_time_past~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~silk.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~asterix.png similarity index 100% rename from tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~silk.png rename to tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~asterix.png diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~gabbro.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~gabbro.png new file mode 100644 index 0000000000..32f51312ca Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~gabbro.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~obelix.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~obelix.png new file mode 100644 index 0000000000..8d3c6c725f Binary files /dev/null and b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~obelix.png differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~robert.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~robert.png deleted file mode 100644 index 0fabaa71b8..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~robert.png and /dev/null differ diff --git a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~snowy.png b/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~snowy.png deleted file mode 100644 index 6bed4a99a8..0000000000 Binary files a/tests/test_images/test_timeline_list_view__title_and_subtitle_overlap_future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_no_events__future~gabbro.png b/tests/test_images/test_timeline_no_events__future~gabbro.png new file mode 100644 index 0000000000..bdeeeab5a9 Binary files /dev/null and b/tests/test_images/test_timeline_no_events__future~gabbro.png differ diff --git a/tests/test_images/test_timeline_no_events__future~robert.png b/tests/test_images/test_timeline_no_events__future~obelix.png similarity index 100% rename from tests/test_images/test_timeline_no_events__future~robert.png rename to tests/test_images/test_timeline_no_events__future~obelix.png diff --git a/tests/test_images/test_timeline_no_events__future~snowy.png b/tests/test_images/test_timeline_no_events__future~snowy.png deleted file mode 100644 index e7656baca3..0000000000 Binary files a/tests/test_images/test_timeline_no_events__future~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_no_events__future~spalding.png b/tests/test_images/test_timeline_no_events__future~spalding.png deleted file mode 100644 index 11691ecb1c..0000000000 Binary files a/tests/test_images/test_timeline_no_events__future~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_no_events__past~gabbro.png b/tests/test_images/test_timeline_no_events__past~gabbro.png new file mode 100644 index 0000000000..cdfe5f6530 Binary files /dev/null and b/tests/test_images/test_timeline_no_events__past~gabbro.png differ diff --git a/tests/test_images/test_timeline_no_events__past~robert.png b/tests/test_images/test_timeline_no_events__past~obelix.png similarity index 100% rename from tests/test_images/test_timeline_no_events__past~robert.png rename to tests/test_images/test_timeline_no_events__past~obelix.png diff --git a/tests/test_images/test_timeline_no_events__past~snowy.png b/tests/test_images/test_timeline_no_events__past~snowy.png deleted file mode 100644 index 63db4b75be..0000000000 Binary files a/tests/test_images/test_timeline_no_events__past~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_no_events__past~spalding.png b/tests/test_images/test_timeline_no_events__past~spalding.png deleted file mode 100644 index c81e3b27cb..0000000000 Binary files a/tests/test_images/test_timeline_no_events__past~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_1~silk.png b/tests/test_images/test_timeline_peek__peek_concurrent_1~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_concurrent_1~silk.png rename to tests/test_images/test_timeline_peek__peek_concurrent_1~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_1~gabbro.png b/tests/test_images/test_timeline_peek__peek_concurrent_1~gabbro.png new file mode 100644 index 0000000000..6609ab87c0 Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_concurrent_1~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_1~robert.png b/tests/test_images/test_timeline_peek__peek_concurrent_1~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_concurrent_1~robert.png rename to tests/test_images/test_timeline_peek__peek_concurrent_1~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_1~snowy.png b/tests/test_images/test_timeline_peek__peek_concurrent_1~snowy.png deleted file mode 100644 index a6ee356576..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_concurrent_1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_1~spalding.png b/tests/test_images/test_timeline_peek__peek_concurrent_1~spalding.png deleted file mode 100644 index ae8fcab34f..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_concurrent_1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~silk.png b/tests/test_images/test_timeline_peek__peek_concurrent_2_max~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_concurrent_2_max~silk.png rename to tests/test_images/test_timeline_peek__peek_concurrent_2_max~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~gabbro.png b/tests/test_images/test_timeline_peek__peek_concurrent_2_max~gabbro.png new file mode 100644 index 0000000000..9680e7f02d Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_concurrent_2_max~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~robert.png b/tests/test_images/test_timeline_peek__peek_concurrent_2_max~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_concurrent_2_max~robert.png rename to tests/test_images/test_timeline_peek__peek_concurrent_2_max~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~snowy.png b/tests/test_images/test_timeline_peek__peek_concurrent_2_max~snowy.png deleted file mode 100644 index 89938e532c..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~spalding.png b/tests/test_images/test_timeline_peek__peek_concurrent_2_max~spalding.png deleted file mode 100644 index ed393248c5..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_concurrent_2_max~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2~silk.png b/tests/test_images/test_timeline_peek__peek_concurrent_2~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_concurrent_2~silk.png rename to tests/test_images/test_timeline_peek__peek_concurrent_2~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2~gabbro.png b/tests/test_images/test_timeline_peek__peek_concurrent_2~gabbro.png new file mode 100644 index 0000000000..8674c3e6c6 Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_concurrent_2~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2~robert.png b/tests/test_images/test_timeline_peek__peek_concurrent_2~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_concurrent_2~robert.png rename to tests/test_images/test_timeline_peek__peek_concurrent_2~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2~snowy.png b/tests/test_images/test_timeline_peek__peek_concurrent_2~snowy.png deleted file mode 100644 index 2eb62de76b..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_concurrent_2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_concurrent_2~spalding.png b/tests/test_images/test_timeline_peek__peek_concurrent_2~spalding.png deleted file mode 100644 index 82a1be798e..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_concurrent_2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_in_5_minutes~silk.png b/tests/test_images/test_timeline_peek__peek_in_5_minutes~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_in_5_minutes~silk.png rename to tests/test_images/test_timeline_peek__peek_in_5_minutes~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_in_5_minutes~gabbro.png b/tests/test_images/test_timeline_peek__peek_in_5_minutes~gabbro.png new file mode 100644 index 0000000000..815137ef5c Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_in_5_minutes~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_in_5_minutes~robert.png b/tests/test_images/test_timeline_peek__peek_in_5_minutes~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_in_5_minutes~robert.png rename to tests/test_images/test_timeline_peek__peek_in_5_minutes~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_in_5_minutes~snowy.png b/tests/test_images/test_timeline_peek__peek_in_5_minutes~snowy.png deleted file mode 100644 index 2b56cc4be1..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_in_5_minutes~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_in_5_minutes~spalding.png b/tests/test_images/test_timeline_peek__peek_in_5_minutes~spalding.png deleted file mode 100644 index 565656e237..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_in_5_minutes~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_newline~silk.png b/tests/test_images/test_timeline_peek__peek_newline~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_newline~silk.png rename to tests/test_images/test_timeline_peek__peek_newline~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_newline~gabbro.png b/tests/test_images/test_timeline_peek__peek_newline~gabbro.png new file mode 100644 index 0000000000..7b2f29ca13 Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_newline~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_newline~robert.png b/tests/test_images/test_timeline_peek__peek_newline~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_newline~robert.png rename to tests/test_images/test_timeline_peek__peek_newline~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_newline~snowy.png b/tests/test_images/test_timeline_peek__peek_newline~snowy.png deleted file mode 100644 index e9e92bae1e..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_newline~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_newline~spalding.png b/tests/test_images/test_timeline_peek__peek_newline~spalding.png deleted file mode 100644 index 0273b4951f..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_newline~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~silk.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~silk.png rename to tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~gabbro.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~gabbro.png new file mode 100644 index 0000000000..ac60e3e4c8 Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~robert.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~robert.png rename to tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~snowy.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~snowy.png deleted file mode 100644 index b11b9bd782..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~spalding.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~spalding.png deleted file mode 100644 index f9a7568d00..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~silk.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~silk.png rename to tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~gabbro.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~gabbro.png new file mode 100644 index 0000000000..e924f26057 Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~robert.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~robert.png rename to tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~snowy.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~snowy.png deleted file mode 100644 index 17d68b867a..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~spalding.png b/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~spalding.png deleted file mode 100644 index c6ede29d83..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only_concurrent_2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_newline~silk.png b/tests/test_images/test_timeline_peek__peek_title_only_newline~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only_newline~silk.png rename to tests/test_images/test_timeline_peek__peek_title_only_newline~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only_newline~gabbro.png b/tests/test_images/test_timeline_peek__peek_title_only_newline~gabbro.png new file mode 100644 index 0000000000..3caf170dab Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_title_only_newline~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_newline~robert.png b/tests/test_images/test_timeline_peek__peek_title_only_newline~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only_newline~robert.png rename to tests/test_images/test_timeline_peek__peek_title_only_newline~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only_newline~snowy.png b/tests/test_images/test_timeline_peek__peek_title_only_newline~snowy.png deleted file mode 100644 index eb352f59e3..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only_newline~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only_newline~spalding.png b/tests/test_images/test_timeline_peek__peek_title_only_newline~spalding.png deleted file mode 100644 index 9992bb81d7..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only_newline~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only~silk.png b/tests/test_images/test_timeline_peek__peek_title_only~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only~silk.png rename to tests/test_images/test_timeline_peek__peek_title_only~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only~gabbro.png b/tests/test_images/test_timeline_peek__peek_title_only~gabbro.png new file mode 100644 index 0000000000..2b7604d25d Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek_title_only~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only~robert.png b/tests/test_images/test_timeline_peek__peek_title_only~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek_title_only~robert.png rename to tests/test_images/test_timeline_peek__peek_title_only~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek_title_only~snowy.png b/tests/test_images/test_timeline_peek__peek_title_only~snowy.png deleted file mode 100644 index d66f2ce504..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek_title_only~spalding.png b/tests/test_images/test_timeline_peek__peek_title_only~spalding.png deleted file mode 100644 index 4635c088be..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek_title_only~spalding.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek~silk.png b/tests/test_images/test_timeline_peek__peek~asterix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek~silk.png rename to tests/test_images/test_timeline_peek__peek~asterix.png diff --git a/tests/test_images/test_timeline_peek__peek~gabbro.png b/tests/test_images/test_timeline_peek__peek~gabbro.png new file mode 100644 index 0000000000..aac2df29ca Binary files /dev/null and b/tests/test_images/test_timeline_peek__peek~gabbro.png differ diff --git a/tests/test_images/test_timeline_peek__peek~robert.png b/tests/test_images/test_timeline_peek__peek~obelix.png similarity index 100% rename from tests/test_images/test_timeline_peek__peek~robert.png rename to tests/test_images/test_timeline_peek__peek~obelix.png diff --git a/tests/test_images/test_timeline_peek__peek~snowy.png b/tests/test_images/test_timeline_peek__peek~snowy.png deleted file mode 100644 index 4a2c455d53..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek~snowy.png and /dev/null differ diff --git a/tests/test_images/test_timeline_peek__peek~spalding.png b/tests/test_images/test_timeline_peek__peek~spalding.png deleted file mode 100644 index dd1725ac6b..0000000000 Binary files a/tests/test_images/test_timeline_peek__peek~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_all_unknown_values~silk.png b/tests/test_images/test_weather_app_layout__render_all_unknown_values~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_all_unknown_values~silk.png rename to tests/test_images/test_weather_app_layout__render_all_unknown_values~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_all_unknown_values~gabbro.png b/tests/test_images/test_weather_app_layout__render_all_unknown_values~gabbro.png new file mode 100644 index 0000000000..7d12dd3845 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_all_unknown_values~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_all_unknown_values~obelix.png b/tests/test_images/test_weather_app_layout__render_all_unknown_values~obelix.png new file mode 100644 index 0000000000..88f9292acb Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_all_unknown_values~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_all_unknown_values~snowy.png b/tests/test_images/test_weather_app_layout__render_all_unknown_values~snowy.png deleted file mode 100644 index b399c06108..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_all_unknown_values~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_all_unknown_values~spalding.png b/tests/test_images/test_weather_app_layout__render_all_unknown_values~spalding.png deleted file mode 100644 index a52b72e901..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_all_unknown_values~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~silk.png b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_cloudy_light_snow~silk.png rename to tests/test_images/test_weather_app_layout__render_cloudy_light_snow~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~gabbro.png b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~gabbro.png new file mode 100644 index 0000000000..31d7897a11 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~obelix.png b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~obelix.png new file mode 100644 index 0000000000..8eaf31cddc Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~snowy.png b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~snowy.png deleted file mode 100644 index 5af58f7af1..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~spalding.png b/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~spalding.png deleted file mode 100644 index eae8c3268a..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_cloudy_light_snow~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_current_location~asterix.png b/tests/test_images/test_weather_app_layout__render_current_location~asterix.png new file mode 100644 index 0000000000..2b0ef5ee1a Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_current_location~asterix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_current_location~gabbro.png b/tests/test_images/test_weather_app_layout__render_current_location~gabbro.png new file mode 100644 index 0000000000..f289268ed4 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_current_location~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_current_location~obelix.png b/tests/test_images/test_weather_app_layout__render_current_location~obelix.png new file mode 100644 index 0000000000..0cffcdc359 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_current_location~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_current_location~silk.png b/tests/test_images/test_weather_app_layout__render_current_location~silk.png deleted file mode 100644 index 9e03887df6..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_current_location~silk.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_current_location~snowy.png b/tests/test_images/test_weather_app_layout__render_current_location~snowy.png deleted file mode 100644 index 582ebb179a..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_current_location~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_current_location~spalding.png b/tests/test_images/test_weather_app_layout__render_current_location~spalding.png deleted file mode 100644 index c7d4d1a1f0..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_current_location~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_down_arrow~asterix.png b/tests/test_images/test_weather_app_layout__render_down_arrow~asterix.png new file mode 100644 index 0000000000..0d1bdd414e Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_down_arrow~asterix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_down_arrow~gabbro.png b/tests/test_images/test_weather_app_layout__render_down_arrow~gabbro.png new file mode 100644 index 0000000000..7d7e82676d Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_down_arrow~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_down_arrow~obelix.png b/tests/test_images/test_weather_app_layout__render_down_arrow~obelix.png new file mode 100644 index 0000000000..8e89eb9c38 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_down_arrow~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_down_arrow~silk.png b/tests/test_images/test_weather_app_layout__render_down_arrow~silk.png deleted file mode 100644 index e1d4d983d3..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_down_arrow~silk.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_down_arrow~snowy.png b/tests/test_images/test_weather_app_layout__render_down_arrow~snowy.png deleted file mode 100644 index 5c935bd485..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_down_arrow~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_down_arrow~spalding.png b/tests/test_images/test_weather_app_layout__render_down_arrow~spalding.png deleted file mode 100644 index 5130548ac5..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_down_arrow~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_empty_view~silk.png b/tests/test_images/test_weather_app_layout__render_empty_view~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_empty_view~silk.png rename to tests/test_images/test_weather_app_layout__render_empty_view~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_empty_view~gabbro.png b/tests/test_images/test_weather_app_layout__render_empty_view~gabbro.png new file mode 100644 index 0000000000..e566c0245f Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_empty_view~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_empty_view~obelix.png b/tests/test_images/test_weather_app_layout__render_empty_view~obelix.png new file mode 100644 index 0000000000..2caea57477 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_empty_view~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_empty_view~snowy.png b/tests/test_images/test_weather_app_layout__render_empty_view~snowy.png deleted file mode 100644 index fbec404d58..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_empty_view~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_empty_view~spalding.png b/tests/test_images/test_weather_app_layout__render_empty_view~spalding.png deleted file mode 100644 index 3b711361db..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_empty_view~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_generic_generic~silk.png b/tests/test_images/test_weather_app_layout__render_generic_generic~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_generic_generic~silk.png rename to tests/test_images/test_weather_app_layout__render_generic_generic~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_generic_generic~gabbro.png b/tests/test_images/test_weather_app_layout__render_generic_generic~gabbro.png new file mode 100644 index 0000000000..23670b082a Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_generic_generic~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_generic_generic~obelix.png b/tests/test_images/test_weather_app_layout__render_generic_generic~obelix.png new file mode 100644 index 0000000000..e5e6589e2f Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_generic_generic~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_generic_generic~snowy.png b/tests/test_images/test_weather_app_layout__render_generic_generic~snowy.png deleted file mode 100644 index 0357189844..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_generic_generic~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_generic_generic~spalding.png b/tests/test_images/test_weather_app_layout__render_generic_generic~spalding.png deleted file mode 100644 index 659d989525..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_generic_generic~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~silk.png b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~silk.png rename to tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~gabbro.png b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~gabbro.png new file mode 100644 index 0000000000..b8c3d79f18 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~obelix.png b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~obelix.png new file mode 100644 index 0000000000..4760eec487 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~snowy.png b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~snowy.png deleted file mode 100644 index 7e966830c4..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~spalding.png b/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~spalding.png deleted file mode 100644 index bd030e1502..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_heavy_snow_rain_snow~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_large_numbers~silk.png b/tests/test_images/test_weather_app_layout__render_large_numbers~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_large_numbers~silk.png rename to tests/test_images/test_weather_app_layout__render_large_numbers~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_large_numbers~gabbro.png b/tests/test_images/test_weather_app_layout__render_large_numbers~gabbro.png new file mode 100644 index 0000000000..69cde33df8 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_large_numbers~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_large_numbers~obelix.png b/tests/test_images/test_weather_app_layout__render_large_numbers~obelix.png new file mode 100644 index 0000000000..851d462e1e Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_large_numbers~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_large_numbers~snowy.png b/tests/test_images/test_weather_app_layout__render_large_numbers~snowy.png deleted file mode 100644 index ae6445c13d..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_large_numbers~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_large_numbers~spalding.png b/tests/test_images/test_weather_app_layout__render_large_numbers~spalding.png deleted file mode 100644 index 55cdeff16d..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_large_numbers~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~silk.png b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~silk.png rename to tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~gabbro.png b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~gabbro.png new file mode 100644 index 0000000000..625fe21f62 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~obelix.png b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~obelix.png new file mode 100644 index 0000000000..18be236efa Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~snowy.png b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~snowy.png deleted file mode 100644 index d82570e994..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~spalding.png b/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~spalding.png deleted file mode 100644 index 4777c605ee..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_light_rain_heavy_rain~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~asterix.png b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~asterix.png new file mode 100644 index 0000000000..e8f50fdc1b Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~asterix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~gabbro.png b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~gabbro.png new file mode 100644 index 0000000000..9a86df6d9f Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~obelix.png b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~obelix.png new file mode 100644 index 0000000000..7d65d420aa Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~silk.png b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~silk.png deleted file mode 100644 index 32c3e980ac..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~silk.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~snowy.png b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~snowy.png deleted file mode 100644 index 02ff4b107e..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~spalding.png b/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~spalding.png deleted file mode 100644 index 7decc2baa6..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_long_current_location_name_pbl_38049~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~asterix.png b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~asterix.png new file mode 100644 index 0000000000..13d36f3c23 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~asterix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~gabbro.png b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~gabbro.png new file mode 100644 index 0000000000..598491512c Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~obelix.png b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~obelix.png new file mode 100644 index 0000000000..6e26a27758 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~silk.png b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~silk.png deleted file mode 100644 index a84b61480e..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~silk.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~snowy.png b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~snowy.png deleted file mode 100644 index 52fb315dfd..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~spalding.png b/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~spalding.png deleted file mode 100644 index dbc57ab843..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_longer_strings_for_current_location~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings~silk.png b/tests/test_images/test_weather_app_layout__render_longer_strings~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_longer_strings~silk.png rename to tests/test_images/test_weather_app_layout__render_longer_strings~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings~gabbro.png b/tests/test_images/test_weather_app_layout__render_longer_strings~gabbro.png new file mode 100644 index 0000000000..9a96235047 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_longer_strings~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings~obelix.png b/tests/test_images/test_weather_app_layout__render_longer_strings~obelix.png new file mode 100644 index 0000000000..18997cdc85 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_longer_strings~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings~snowy.png b/tests/test_images/test_weather_app_layout__render_longer_strings~snowy.png deleted file mode 100644 index 715a184596..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_longer_strings~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_longer_strings~spalding.png b/tests/test_images/test_weather_app_layout__render_longer_strings~spalding.png deleted file mode 100644 index abff682d6a..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_longer_strings~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_palo_alto~silk.png b/tests/test_images/test_weather_app_layout__render_palo_alto~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_palo_alto~silk.png rename to tests/test_images/test_weather_app_layout__render_palo_alto~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_palo_alto~gabbro.png b/tests/test_images/test_weather_app_layout__render_palo_alto~gabbro.png new file mode 100644 index 0000000000..a45e27dde1 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_palo_alto~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_palo_alto~obelix.png b/tests/test_images/test_weather_app_layout__render_palo_alto~obelix.png new file mode 100644 index 0000000000..31b00e7e2f Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_palo_alto~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_palo_alto~snowy.png b/tests/test_images/test_weather_app_layout__render_palo_alto~snowy.png deleted file mode 100644 index 141abf585e..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_palo_alto~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_palo_alto~spalding.png b/tests/test_images/test_weather_app_layout__render_palo_alto~spalding.png deleted file mode 100644 index 296c985198..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_palo_alto~spalding.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_some_unknown_values~silk.png b/tests/test_images/test_weather_app_layout__render_some_unknown_values~asterix.png similarity index 100% rename from tests/test_images/test_weather_app_layout__render_some_unknown_values~silk.png rename to tests/test_images/test_weather_app_layout__render_some_unknown_values~asterix.png diff --git a/tests/test_images/test_weather_app_layout__render_some_unknown_values~gabbro.png b/tests/test_images/test_weather_app_layout__render_some_unknown_values~gabbro.png new file mode 100644 index 0000000000..db66a94cd1 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_some_unknown_values~gabbro.png differ diff --git a/tests/test_images/test_weather_app_layout__render_some_unknown_values~obelix.png b/tests/test_images/test_weather_app_layout__render_some_unknown_values~obelix.png new file mode 100644 index 0000000000..acbe3fb876 Binary files /dev/null and b/tests/test_images/test_weather_app_layout__render_some_unknown_values~obelix.png differ diff --git a/tests/test_images/test_weather_app_layout__render_some_unknown_values~snowy.png b/tests/test_images/test_weather_app_layout__render_some_unknown_values~snowy.png deleted file mode 100644 index 40266732af..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_some_unknown_values~snowy.png and /dev/null differ diff --git a/tests/test_images/test_weather_app_layout__render_some_unknown_values~spalding.png b/tests/test_images/test_weather_app_layout__render_some_unknown_values~spalding.png deleted file mode 100644 index 3763a53ff7..0000000000 Binary files a/tests/test_images/test_weather_app_layout__render_some_unknown_values~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_custom_field~silk.png b/tests/test_images/test_workout_active__sports_custom_field~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_custom_field~silk.png rename to tests/test_images/test_workout_active__sports_custom_field~asterix.png diff --git a/tests/test_images/test_workout_active__sports_custom_field~gabbro.png b/tests/test_images/test_workout_active__sports_custom_field~gabbro.png new file mode 100644 index 0000000000..cc664c0df3 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_custom_field~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_custom_field~obelix.png b/tests/test_images/test_workout_active__sports_custom_field~obelix.png new file mode 100644 index 0000000000..7c3c42b10a Binary files /dev/null and b/tests/test_images/test_workout_active__sports_custom_field~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_custom_field~snowy.png b/tests/test_images/test_workout_active__sports_custom_field~snowy.png deleted file mode 100644 index 22e3f63214..0000000000 Binary files a/tests/test_images/test_workout_active__sports_custom_field~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_custom_field~spalding.png b/tests/test_images/test_workout_active__sports_custom_field~spalding.png deleted file mode 100644 index a234e67a00..0000000000 Binary files a/tests/test_images/test_workout_active__sports_custom_field~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_custom_hanging_label~silk.png b/tests/test_images/test_workout_active__sports_custom_hanging_label~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_custom_hanging_label~silk.png rename to tests/test_images/test_workout_active__sports_custom_hanging_label~asterix.png diff --git a/tests/test_images/test_workout_active__sports_custom_hanging_label~gabbro.png b/tests/test_images/test_workout_active__sports_custom_hanging_label~gabbro.png new file mode 100644 index 0000000000..7f67fa255f Binary files /dev/null and b/tests/test_images/test_workout_active__sports_custom_hanging_label~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_custom_hanging_label~obelix.png b/tests/test_images/test_workout_active__sports_custom_hanging_label~obelix.png new file mode 100644 index 0000000000..7f2c44f6a0 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_custom_hanging_label~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_custom_hanging_label~snowy.png b/tests/test_images/test_workout_active__sports_custom_hanging_label~snowy.png deleted file mode 100644 index a8acd96913..0000000000 Binary files a/tests/test_images/test_workout_active__sports_custom_hanging_label~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_custom_hanging_label~spalding.png b/tests/test_images/test_workout_active__sports_custom_hanging_label~spalding.png deleted file mode 100644 index aad64c300c..0000000000 Binary files a/tests/test_images/test_workout_active__sports_custom_hanging_label~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_custom_long_values~silk.png b/tests/test_images/test_workout_active__sports_custom_long_values~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_custom_long_values~silk.png rename to tests/test_images/test_workout_active__sports_custom_long_values~asterix.png diff --git a/tests/test_images/test_workout_active__sports_custom_long_values~gabbro.png b/tests/test_images/test_workout_active__sports_custom_long_values~gabbro.png new file mode 100644 index 0000000000..55230b94d9 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_custom_long_values~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_custom_long_values~obelix.png b/tests/test_images/test_workout_active__sports_custom_long_values~obelix.png new file mode 100644 index 0000000000..58084cd56c Binary files /dev/null and b/tests/test_images/test_workout_active__sports_custom_long_values~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_custom_long_values~snowy.png b/tests/test_images/test_workout_active__sports_custom_long_values~snowy.png deleted file mode 100644 index 0dd4911238..0000000000 Binary files a/tests/test_images/test_workout_active__sports_custom_long_values~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_custom_long_values~spalding.png b/tests/test_images/test_workout_active__sports_custom_long_values~spalding.png deleted file mode 100644 index 2c84e5c826..0000000000 Binary files a/tests/test_images/test_workout_active__sports_custom_long_values~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z0~silk.png b/tests/test_images/test_workout_active__sports_hr_z0~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_hr_z0~silk.png rename to tests/test_images/test_workout_active__sports_hr_z0~asterix.png diff --git a/tests/test_images/test_workout_active__sports_hr_z0~gabbro.png b/tests/test_images/test_workout_active__sports_hr_z0~gabbro.png new file mode 100644 index 0000000000..e8083d0bc1 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z0~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z0~obelix.png b/tests/test_images/test_workout_active__sports_hr_z0~obelix.png new file mode 100644 index 0000000000..a51aeede38 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z0~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z0~snowy.png b/tests/test_images/test_workout_active__sports_hr_z0~snowy.png deleted file mode 100644 index a738adaa88..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z0~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z0~spalding.png b/tests/test_images/test_workout_active__sports_hr_z0~spalding.png deleted file mode 100644 index 7c72df38f1..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z0~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z1~silk.png b/tests/test_images/test_workout_active__sports_hr_z1~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_hr_z1~silk.png rename to tests/test_images/test_workout_active__sports_hr_z1~asterix.png diff --git a/tests/test_images/test_workout_active__sports_hr_z1~gabbro.png b/tests/test_images/test_workout_active__sports_hr_z1~gabbro.png new file mode 100644 index 0000000000..0c7535f7fa Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z1~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z1~obelix.png b/tests/test_images/test_workout_active__sports_hr_z1~obelix.png new file mode 100644 index 0000000000..95c257fa73 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z1~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z1~snowy.png b/tests/test_images/test_workout_active__sports_hr_z1~snowy.png deleted file mode 100644 index efeb983b67..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z1~spalding.png b/tests/test_images/test_workout_active__sports_hr_z1~spalding.png deleted file mode 100644 index d35f7ba2d5..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z2~silk.png b/tests/test_images/test_workout_active__sports_hr_z2~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_hr_z2~silk.png rename to tests/test_images/test_workout_active__sports_hr_z2~asterix.png diff --git a/tests/test_images/test_workout_active__sports_hr_z2~gabbro.png b/tests/test_images/test_workout_active__sports_hr_z2~gabbro.png new file mode 100644 index 0000000000..06c9327282 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z2~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z2~obelix.png b/tests/test_images/test_workout_active__sports_hr_z2~obelix.png new file mode 100644 index 0000000000..971d914398 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z2~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z2~snowy.png b/tests/test_images/test_workout_active__sports_hr_z2~snowy.png deleted file mode 100644 index 0356a5d686..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z2~spalding.png b/tests/test_images/test_workout_active__sports_hr_z2~spalding.png deleted file mode 100644 index 6c10460ee3..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z3~silk.png b/tests/test_images/test_workout_active__sports_hr_z3~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_hr_z3~silk.png rename to tests/test_images/test_workout_active__sports_hr_z3~asterix.png diff --git a/tests/test_images/test_workout_active__sports_hr_z3~gabbro.png b/tests/test_images/test_workout_active__sports_hr_z3~gabbro.png new file mode 100644 index 0000000000..061dd5390a Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z3~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z3~obelix.png b/tests/test_images/test_workout_active__sports_hr_z3~obelix.png new file mode 100644 index 0000000000..ffd7c8aae2 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_hr_z3~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_hr_z3~snowy.png b/tests/test_images/test_workout_active__sports_hr_z3~snowy.png deleted file mode 100644 index 26efd37424..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z3~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_hr_z3~spalding.png b/tests/test_images/test_workout_active__sports_hr_z3~spalding.png deleted file mode 100644 index fcbf1520e3..0000000000 Binary files a/tests/test_images/test_workout_active__sports_hr_z3~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_no_hrm~silk.png b/tests/test_images/test_workout_active__sports_no_hrm~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_no_hrm~silk.png rename to tests/test_images/test_workout_active__sports_no_hrm~asterix.png diff --git a/tests/test_images/test_workout_active__sports_no_hrm~gabbro.png b/tests/test_images/test_workout_active__sports_no_hrm~gabbro.png new file mode 100644 index 0000000000..47aac97631 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_no_hrm~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_no_hrm~obelix.png b/tests/test_images/test_workout_active__sports_no_hrm~obelix.png new file mode 100644 index 0000000000..2cd340f18d Binary files /dev/null and b/tests/test_images/test_workout_active__sports_no_hrm~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_no_hrm~snowy.png b/tests/test_images/test_workout_active__sports_no_hrm~snowy.png deleted file mode 100644 index 77e71e41b1..0000000000 Binary files a/tests/test_images/test_workout_active__sports_no_hrm~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_no_hrm~spalding.png b/tests/test_images/test_workout_active__sports_no_hrm~spalding.png deleted file mode 100644 index d594dd8f04..0000000000 Binary files a/tests/test_images/test_workout_active__sports_no_hrm~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_pace_long_values~silk.png b/tests/test_images/test_workout_active__sports_pace_long_values~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_pace_long_values~silk.png rename to tests/test_images/test_workout_active__sports_pace_long_values~asterix.png diff --git a/tests/test_images/test_workout_active__sports_pace_long_values~gabbro.png b/tests/test_images/test_workout_active__sports_pace_long_values~gabbro.png new file mode 100644 index 0000000000..1ce56f26f0 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_pace_long_values~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_pace_long_values~obelix.png b/tests/test_images/test_workout_active__sports_pace_long_values~obelix.png new file mode 100644 index 0000000000..596a3c1a64 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_pace_long_values~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_pace_long_values~snowy.png b/tests/test_images/test_workout_active__sports_pace_long_values~snowy.png deleted file mode 100644 index b4e8fc6183..0000000000 Binary files a/tests/test_images/test_workout_active__sports_pace_long_values~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_pace_long_values~spalding.png b/tests/test_images/test_workout_active__sports_pace_long_values~spalding.png deleted file mode 100644 index 73737cd421..0000000000 Binary files a/tests/test_images/test_workout_active__sports_pace_long_values~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_pace~silk.png b/tests/test_images/test_workout_active__sports_pace~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_pace~silk.png rename to tests/test_images/test_workout_active__sports_pace~asterix.png diff --git a/tests/test_images/test_workout_active__sports_pace~gabbro.png b/tests/test_images/test_workout_active__sports_pace~gabbro.png new file mode 100644 index 0000000000..0d8166ac4c Binary files /dev/null and b/tests/test_images/test_workout_active__sports_pace~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_pace~obelix.png b/tests/test_images/test_workout_active__sports_pace~obelix.png new file mode 100644 index 0000000000..41ab2464bd Binary files /dev/null and b/tests/test_images/test_workout_active__sports_pace~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_pace~snowy.png b/tests/test_images/test_workout_active__sports_pace~snowy.png deleted file mode 100644 index 1872f79c8d..0000000000 Binary files a/tests/test_images/test_workout_active__sports_pace~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_pace~spalding.png b/tests/test_images/test_workout_active__sports_pace~spalding.png deleted file mode 100644 index 5f2cd6c004..0000000000 Binary files a/tests/test_images/test_workout_active__sports_pace~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_speed~silk.png b/tests/test_images/test_workout_active__sports_speed~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__sports_speed~silk.png rename to tests/test_images/test_workout_active__sports_speed~asterix.png diff --git a/tests/test_images/test_workout_active__sports_speed~gabbro.png b/tests/test_images/test_workout_active__sports_speed~gabbro.png new file mode 100644 index 0000000000..29d70dbde6 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_speed~gabbro.png differ diff --git a/tests/test_images/test_workout_active__sports_speed~obelix.png b/tests/test_images/test_workout_active__sports_speed~obelix.png new file mode 100644 index 0000000000..40e9008743 Binary files /dev/null and b/tests/test_images/test_workout_active__sports_speed~obelix.png differ diff --git a/tests/test_images/test_workout_active__sports_speed~snowy.png b/tests/test_images/test_workout_active__sports_speed~snowy.png deleted file mode 100644 index 9905a756ff..0000000000 Binary files a/tests/test_images/test_workout_active__sports_speed~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__sports_speed~spalding.png b/tests/test_images/test_workout_active__sports_speed~spalding.png deleted file mode 100644 index 793feac3a8..0000000000 Binary files a/tests/test_images/test_workout_active__sports_speed~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_1~silk.png b/tests/test_images/test_workout_active__workout_render_hr_zone_1~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_hr_zone_1~silk.png rename to tests/test_images/test_workout_active__workout_render_hr_zone_1~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_1~gabbro.png b/tests/test_images/test_workout_active__workout_render_hr_zone_1~gabbro.png new file mode 100644 index 0000000000..653088c3f3 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_hr_zone_1~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_1~obelix.png b/tests/test_images/test_workout_active__workout_render_hr_zone_1~obelix.png new file mode 100644 index 0000000000..35ccd96a04 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_hr_zone_1~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_1~snowy.png b/tests/test_images/test_workout_active__workout_render_hr_zone_1~snowy.png deleted file mode 100644 index 20cd98f89a..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_hr_zone_1~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_1~spalding.png b/tests/test_images/test_workout_active__workout_render_hr_zone_1~spalding.png deleted file mode 100644 index 1f1f0d9729..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_hr_zone_1~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_2~silk.png b/tests/test_images/test_workout_active__workout_render_hr_zone_2~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_hr_zone_2~silk.png rename to tests/test_images/test_workout_active__workout_render_hr_zone_2~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_2~gabbro.png b/tests/test_images/test_workout_active__workout_render_hr_zone_2~gabbro.png new file mode 100644 index 0000000000..5e998ca1aa Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_hr_zone_2~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_2~obelix.png b/tests/test_images/test_workout_active__workout_render_hr_zone_2~obelix.png new file mode 100644 index 0000000000..1eb9416bd0 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_hr_zone_2~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_2~snowy.png b/tests/test_images/test_workout_active__workout_render_hr_zone_2~snowy.png deleted file mode 100644 index 35c7479e34..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_hr_zone_2~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_2~spalding.png b/tests/test_images/test_workout_active__workout_render_hr_zone_2~spalding.png deleted file mode 100644 index af53f15d48..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_hr_zone_2~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_3~silk.png b/tests/test_images/test_workout_active__workout_render_hr_zone_3~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_hr_zone_3~silk.png rename to tests/test_images/test_workout_active__workout_render_hr_zone_3~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_3~gabbro.png b/tests/test_images/test_workout_active__workout_render_hr_zone_3~gabbro.png new file mode 100644 index 0000000000..06159269fc Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_hr_zone_3~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_3~obelix.png b/tests/test_images/test_workout_active__workout_render_hr_zone_3~obelix.png new file mode 100644 index 0000000000..ca3b03a3a3 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_hr_zone_3~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_3~snowy.png b/tests/test_images/test_workout_active__workout_render_hr_zone_3~snowy.png deleted file mode 100644 index 1a8475839d..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_hr_zone_3~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_hr_zone_3~spalding.png b/tests/test_images/test_workout_active__workout_render_hr_zone_3~spalding.png deleted file mode 100644 index 94fdf7b5ba..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_hr_zone_3~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_no_data~silk.png b/tests/test_images/test_workout_active__workout_render_no_data~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_no_data~silk.png rename to tests/test_images/test_workout_active__workout_render_no_data~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_no_data~gabbro.png b/tests/test_images/test_workout_active__workout_render_no_data~gabbro.png new file mode 100644 index 0000000000..d90e9f14fd Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_no_data~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_no_data~obelix.png b/tests/test_images/test_workout_active__workout_render_no_data~obelix.png new file mode 100644 index 0000000000..8233948a98 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_no_data~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_no_data~snowy.png b/tests/test_images/test_workout_active__workout_render_no_data~snowy.png deleted file mode 100644 index 0c6be9d926..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_no_data~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_no_data~spalding.png b/tests/test_images/test_workout_active__workout_render_no_data~spalding.png deleted file mode 100644 index 2f9f5a8e08..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_no_data~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~silk.png b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~silk.png rename to tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~gabbro.png b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~gabbro.png new file mode 100644 index 0000000000..14fb64b14f Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~obelix.png b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~obelix.png new file mode 100644 index 0000000000..d12954fb0f Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~snowy.png b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~snowy.png deleted file mode 100644 index 2e491d3097..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~spalding.png b/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~spalding.png deleted file mode 100644 index 24b308d9e0..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_open_workout_no_hrm~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout~silk.png b/tests/test_images/test_workout_active__workout_render_open_workout~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_open_workout~silk.png rename to tests/test_images/test_workout_active__workout_render_open_workout~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_open_workout~gabbro.png b/tests/test_images/test_workout_active__workout_render_open_workout~gabbro.png new file mode 100644 index 0000000000..3c9355ab56 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_open_workout~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout~obelix.png b/tests/test_images/test_workout_active__workout_render_open_workout~obelix.png new file mode 100644 index 0000000000..d9c1ba1191 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_open_workout~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout~snowy.png b/tests/test_images/test_workout_active__workout_render_open_workout~snowy.png deleted file mode 100644 index a85f59fcf8..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_open_workout~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_open_workout~spalding.png b/tests/test_images/test_workout_active__workout_render_open_workout~spalding.png deleted file mode 100644 index bbd05c63ab..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_open_workout~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_run_no_hrm~silk.png b/tests/test_images/test_workout_active__workout_render_run_no_hrm~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_run_no_hrm~silk.png rename to tests/test_images/test_workout_active__workout_render_run_no_hrm~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_run_no_hrm~gabbro.png b/tests/test_images/test_workout_active__workout_render_run_no_hrm~gabbro.png new file mode 100644 index 0000000000..73377c1c4e Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_run_no_hrm~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_run_no_hrm~obelix.png b/tests/test_images/test_workout_active__workout_render_run_no_hrm~obelix.png new file mode 100644 index 0000000000..4087aeef17 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_run_no_hrm~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_run_no_hrm~snowy.png b/tests/test_images/test_workout_active__workout_render_run_no_hrm~snowy.png deleted file mode 100644 index befcfe6a86..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_run_no_hrm~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_run_no_hrm~spalding.png b/tests/test_images/test_workout_active__workout_render_run_no_hrm~spalding.png deleted file mode 100644 index efb631dc90..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_run_no_hrm~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_run~silk.png b/tests/test_images/test_workout_active__workout_render_run~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_run~silk.png rename to tests/test_images/test_workout_active__workout_render_run~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_run~gabbro.png b/tests/test_images/test_workout_active__workout_render_run~gabbro.png new file mode 100644 index 0000000000..100879c32e Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_run~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_run~obelix.png b/tests/test_images/test_workout_active__workout_render_run~obelix.png new file mode 100644 index 0000000000..69078ddf58 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_run~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_run~snowy.png b/tests/test_images/test_workout_active__workout_render_run~snowy.png deleted file mode 100644 index d0e954d076..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_run~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_run~spalding.png b/tests/test_images/test_workout_active__workout_render_run~spalding.png deleted file mode 100644 index a08edaf009..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_run~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_very_slow_pace~silk.png b/tests/test_images/test_workout_active__workout_render_very_slow_pace~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_very_slow_pace~silk.png rename to tests/test_images/test_workout_active__workout_render_very_slow_pace~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_very_slow_pace~gabbro.png b/tests/test_images/test_workout_active__workout_render_very_slow_pace~gabbro.png new file mode 100644 index 0000000000..eb773af5a5 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_very_slow_pace~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_very_slow_pace~obelix.png b/tests/test_images/test_workout_active__workout_render_very_slow_pace~obelix.png new file mode 100644 index 0000000000..e146dc34d3 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_very_slow_pace~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_very_slow_pace~snowy.png b/tests/test_images/test_workout_active__workout_render_very_slow_pace~snowy.png deleted file mode 100644 index b9c90555f0..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_very_slow_pace~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_very_slow_pace~spalding.png b/tests/test_images/test_workout_active__workout_render_very_slow_pace~spalding.png deleted file mode 100644 index f47610daf4..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_very_slow_pace~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~silk.png b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_walk_no_hrm~silk.png rename to tests/test_images/test_workout_active__workout_render_walk_no_hrm~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~gabbro.png b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~gabbro.png new file mode 100644 index 0000000000..c746113d67 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~obelix.png b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~obelix.png new file mode 100644 index 0000000000..6d43aa9880 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~snowy.png b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~snowy.png deleted file mode 100644 index 47c67aa4e4..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~spalding.png b/tests/test_images/test_workout_active__workout_render_walk_no_hrm~spalding.png deleted file mode 100644 index fc49cae9f7..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_walk_no_hrm~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_walk~silk.png b/tests/test_images/test_workout_active__workout_render_walk~asterix.png similarity index 100% rename from tests/test_images/test_workout_active__workout_render_walk~silk.png rename to tests/test_images/test_workout_active__workout_render_walk~asterix.png diff --git a/tests/test_images/test_workout_active__workout_render_walk~gabbro.png b/tests/test_images/test_workout_active__workout_render_walk~gabbro.png new file mode 100644 index 0000000000..100879c32e Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_walk~gabbro.png differ diff --git a/tests/test_images/test_workout_active__workout_render_walk~obelix.png b/tests/test_images/test_workout_active__workout_render_walk~obelix.png new file mode 100644 index 0000000000..69078ddf58 Binary files /dev/null and b/tests/test_images/test_workout_active__workout_render_walk~obelix.png differ diff --git a/tests/test_images/test_workout_active__workout_render_walk~snowy.png b/tests/test_images/test_workout_active__workout_render_walk~snowy.png deleted file mode 100644 index d0e954d076..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_walk~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_active__workout_render_walk~spalding.png b/tests/test_images/test_workout_active__workout_render_walk~spalding.png deleted file mode 100644 index a08edaf009..0000000000 Binary files a/tests/test_images/test_workout_active__workout_render_walk~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_dialog__render_detected_workout~silk.png b/tests/test_images/test_workout_dialog__render_detected_workout~asterix.png similarity index 100% rename from tests/test_images/test_workout_dialog__render_detected_workout~silk.png rename to tests/test_images/test_workout_dialog__render_detected_workout~asterix.png diff --git a/tests/test_images/test_workout_dialog__render_detected_workout~gabbro.png b/tests/test_images/test_workout_dialog__render_detected_workout~gabbro.png new file mode 100644 index 0000000000..9c21ffcf3f Binary files /dev/null and b/tests/test_images/test_workout_dialog__render_detected_workout~gabbro.png differ diff --git a/tests/test_images/test_workout_dialog__render_detected_workout~obelix.png b/tests/test_images/test_workout_dialog__render_detected_workout~obelix.png new file mode 100644 index 0000000000..387fbb252a Binary files /dev/null and b/tests/test_images/test_workout_dialog__render_detected_workout~obelix.png differ diff --git a/tests/test_images/test_workout_dialog__render_detected_workout~snowy.png b/tests/test_images/test_workout_dialog__render_detected_workout~snowy.png deleted file mode 100644 index dbffc943eb..0000000000 Binary files a/tests/test_images/test_workout_dialog__render_detected_workout~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_dialog__render_detected_workout~spalding.png b/tests/test_images/test_workout_dialog__render_detected_workout~spalding.png deleted file mode 100644 index 38a544013e..0000000000 Binary files a/tests/test_images/test_workout_dialog__render_detected_workout~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_dialog__render_end_workout~silk.png b/tests/test_images/test_workout_dialog__render_end_workout~asterix.png similarity index 100% rename from tests/test_images/test_workout_dialog__render_end_workout~silk.png rename to tests/test_images/test_workout_dialog__render_end_workout~asterix.png diff --git a/tests/test_images/test_workout_dialog__render_end_workout~gabbro.png b/tests/test_images/test_workout_dialog__render_end_workout~gabbro.png new file mode 100644 index 0000000000..52e4cc7034 Binary files /dev/null and b/tests/test_images/test_workout_dialog__render_end_workout~gabbro.png differ diff --git a/tests/test_images/test_workout_dialog__render_end_workout~obelix.png b/tests/test_images/test_workout_dialog__render_end_workout~obelix.png new file mode 100644 index 0000000000..1a94ea6864 Binary files /dev/null and b/tests/test_images/test_workout_dialog__render_end_workout~obelix.png differ diff --git a/tests/test_images/test_workout_dialog__render_end_workout~snowy.png b/tests/test_images/test_workout_dialog__render_end_workout~snowy.png deleted file mode 100644 index f1b5c4414c..0000000000 Binary files a/tests/test_images/test_workout_dialog__render_end_workout~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_dialog__render_end_workout~spalding.png b/tests/test_images/test_workout_dialog__render_end_workout~spalding.png deleted file mode 100644 index ab04f90b1f..0000000000 Binary files a/tests/test_images/test_workout_dialog__render_end_workout~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_dialog__render_workout_ended~silk.png b/tests/test_images/test_workout_dialog__render_workout_ended~asterix.png similarity index 100% rename from tests/test_images/test_workout_dialog__render_workout_ended~silk.png rename to tests/test_images/test_workout_dialog__render_workout_ended~asterix.png diff --git a/tests/test_images/test_workout_dialog__render_workout_ended~gabbro.png b/tests/test_images/test_workout_dialog__render_workout_ended~gabbro.png new file mode 100644 index 0000000000..035e67999c Binary files /dev/null and b/tests/test_images/test_workout_dialog__render_workout_ended~gabbro.png differ diff --git a/tests/test_images/test_workout_dialog__render_workout_ended~obelix.png b/tests/test_images/test_workout_dialog__render_workout_ended~obelix.png new file mode 100644 index 0000000000..db30eb91a9 Binary files /dev/null and b/tests/test_images/test_workout_dialog__render_workout_ended~obelix.png differ diff --git a/tests/test_images/test_workout_dialog__render_workout_ended~snowy.png b/tests/test_images/test_workout_dialog__render_workout_ended~snowy.png deleted file mode 100644 index d0c11ee176..0000000000 Binary files a/tests/test_images/test_workout_dialog__render_workout_ended~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_dialog__render_workout_ended~spalding.png b/tests/test_images/test_workout_dialog__render_workout_ended~spalding.png deleted file mode 100644 index 58fb203718..0000000000 Binary files a/tests/test_images/test_workout_dialog__render_workout_ended~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_selection__render_run_selected~silk.png b/tests/test_images/test_workout_selection__render_run_selected~asterix.png similarity index 100% rename from tests/test_images/test_workout_selection__render_run_selected~silk.png rename to tests/test_images/test_workout_selection__render_run_selected~asterix.png diff --git a/tests/test_images/test_workout_selection__render_run_selected~gabbro.png b/tests/test_images/test_workout_selection__render_run_selected~gabbro.png new file mode 100644 index 0000000000..91b257c6f8 Binary files /dev/null and b/tests/test_images/test_workout_selection__render_run_selected~gabbro.png differ diff --git a/tests/test_images/test_workout_selection__render_run_selected~obelix.png b/tests/test_images/test_workout_selection__render_run_selected~obelix.png new file mode 100644 index 0000000000..43d93a10a1 Binary files /dev/null and b/tests/test_images/test_workout_selection__render_run_selected~obelix.png differ diff --git a/tests/test_images/test_workout_selection__render_run_selected~snowy.png b/tests/test_images/test_workout_selection__render_run_selected~snowy.png deleted file mode 100644 index 2487d5157c..0000000000 Binary files a/tests/test_images/test_workout_selection__render_run_selected~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_selection__render_run_selected~spalding.png b/tests/test_images/test_workout_selection__render_run_selected~spalding.png deleted file mode 100644 index cdeeeaa473..0000000000 Binary files a/tests/test_images/test_workout_selection__render_run_selected~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_selection__render_walk_selected~silk.png b/tests/test_images/test_workout_selection__render_walk_selected~asterix.png similarity index 100% rename from tests/test_images/test_workout_selection__render_walk_selected~silk.png rename to tests/test_images/test_workout_selection__render_walk_selected~asterix.png diff --git a/tests/test_images/test_workout_selection__render_walk_selected~gabbro.png b/tests/test_images/test_workout_selection__render_walk_selected~gabbro.png new file mode 100644 index 0000000000..f45079d59c Binary files /dev/null and b/tests/test_images/test_workout_selection__render_walk_selected~gabbro.png differ diff --git a/tests/test_images/test_workout_selection__render_walk_selected~obelix.png b/tests/test_images/test_workout_selection__render_walk_selected~obelix.png new file mode 100644 index 0000000000..039dc4a939 Binary files /dev/null and b/tests/test_images/test_workout_selection__render_walk_selected~obelix.png differ diff --git a/tests/test_images/test_workout_selection__render_walk_selected~snowy.png b/tests/test_images/test_workout_selection__render_walk_selected~snowy.png deleted file mode 100644 index b8a9864bf0..0000000000 Binary files a/tests/test_images/test_workout_selection__render_walk_selected~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_selection__render_walk_selected~spalding.png b/tests/test_images/test_workout_selection__render_walk_selected~spalding.png deleted file mode 100644 index f4affe3ce7..0000000000 Binary files a/tests/test_images/test_workout_selection__render_walk_selected~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_selection__render_workout_selected~silk.png b/tests/test_images/test_workout_selection__render_workout_selected~asterix.png similarity index 100% rename from tests/test_images/test_workout_selection__render_workout_selected~silk.png rename to tests/test_images/test_workout_selection__render_workout_selected~asterix.png diff --git a/tests/test_images/test_workout_selection__render_workout_selected~gabbro.png b/tests/test_images/test_workout_selection__render_workout_selected~gabbro.png new file mode 100644 index 0000000000..0e9b82ec11 Binary files /dev/null and b/tests/test_images/test_workout_selection__render_workout_selected~gabbro.png differ diff --git a/tests/test_images/test_workout_selection__render_workout_selected~obelix.png b/tests/test_images/test_workout_selection__render_workout_selected~obelix.png new file mode 100644 index 0000000000..34e50ecf16 Binary files /dev/null and b/tests/test_images/test_workout_selection__render_workout_selected~obelix.png differ diff --git a/tests/test_images/test_workout_selection__render_workout_selected~snowy.png b/tests/test_images/test_workout_selection__render_workout_selected~snowy.png deleted file mode 100644 index 52986f7a71..0000000000 Binary files a/tests/test_images/test_workout_selection__render_workout_selected~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_selection__render_workout_selected~spalding.png b/tests/test_images/test_workout_selection__render_workout_selected~spalding.png deleted file mode 100644 index 46368f33c8..0000000000 Binary files a/tests/test_images/test_workout_selection__render_workout_selected~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_summary__render_open_workout~silk.png b/tests/test_images/test_workout_summary__render_open_workout~asterix.png similarity index 100% rename from tests/test_images/test_workout_summary__render_open_workout~silk.png rename to tests/test_images/test_workout_summary__render_open_workout~asterix.png diff --git a/tests/test_images/test_workout_summary__render_open_workout~gabbro.png b/tests/test_images/test_workout_summary__render_open_workout~gabbro.png new file mode 100644 index 0000000000..b2ff4420f6 Binary files /dev/null and b/tests/test_images/test_workout_summary__render_open_workout~gabbro.png differ diff --git a/tests/test_images/test_workout_summary__render_open_workout~obelix.png b/tests/test_images/test_workout_summary__render_open_workout~obelix.png new file mode 100644 index 0000000000..3077e49b08 Binary files /dev/null and b/tests/test_images/test_workout_summary__render_open_workout~obelix.png differ diff --git a/tests/test_images/test_workout_summary__render_open_workout~snowy.png b/tests/test_images/test_workout_summary__render_open_workout~snowy.png deleted file mode 100644 index 3d144715cb..0000000000 Binary files a/tests/test_images/test_workout_summary__render_open_workout~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_summary__render_open_workout~spalding.png b/tests/test_images/test_workout_summary__render_open_workout~spalding.png deleted file mode 100644 index 08f5787ddc..0000000000 Binary files a/tests/test_images/test_workout_summary__render_open_workout~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_summary__render_run~silk.png b/tests/test_images/test_workout_summary__render_run~asterix.png similarity index 100% rename from tests/test_images/test_workout_summary__render_run~silk.png rename to tests/test_images/test_workout_summary__render_run~asterix.png diff --git a/tests/test_images/test_workout_summary__render_run~gabbro.png b/tests/test_images/test_workout_summary__render_run~gabbro.png new file mode 100644 index 0000000000..7f6b280276 Binary files /dev/null and b/tests/test_images/test_workout_summary__render_run~gabbro.png differ diff --git a/tests/test_images/test_workout_summary__render_run~obelix.png b/tests/test_images/test_workout_summary__render_run~obelix.png new file mode 100644 index 0000000000..6f5a788ee8 Binary files /dev/null and b/tests/test_images/test_workout_summary__render_run~obelix.png differ diff --git a/tests/test_images/test_workout_summary__render_run~snowy.png b/tests/test_images/test_workout_summary__render_run~snowy.png deleted file mode 100644 index b12e370d1a..0000000000 Binary files a/tests/test_images/test_workout_summary__render_run~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_summary__render_run~spalding.png b/tests/test_images/test_workout_summary__render_run~spalding.png deleted file mode 100644 index 4d6a0c7bb2..0000000000 Binary files a/tests/test_images/test_workout_summary__render_run~spalding.png and /dev/null differ diff --git a/tests/test_images/test_workout_summary__render_walk~silk.png b/tests/test_images/test_workout_summary__render_walk~asterix.png similarity index 100% rename from tests/test_images/test_workout_summary__render_walk~silk.png rename to tests/test_images/test_workout_summary__render_walk~asterix.png diff --git a/tests/test_images/test_workout_summary__render_walk~gabbro.png b/tests/test_images/test_workout_summary__render_walk~gabbro.png new file mode 100644 index 0000000000..c6d4525097 Binary files /dev/null and b/tests/test_images/test_workout_summary__render_walk~gabbro.png differ diff --git a/tests/test_images/test_workout_summary__render_walk~obelix.png b/tests/test_images/test_workout_summary__render_walk~obelix.png new file mode 100644 index 0000000000..449b50abc4 Binary files /dev/null and b/tests/test_images/test_workout_summary__render_walk~obelix.png differ diff --git a/tests/test_images/test_workout_summary__render_walk~snowy.png b/tests/test_images/test_workout_summary__render_walk~snowy.png deleted file mode 100644 index 7028b3bdfc..0000000000 Binary files a/tests/test_images/test_workout_summary__render_walk~snowy.png and /dev/null differ diff --git a/tests/test_images/test_workout_summary__render_walk~spalding.png b/tests/test_images/test_workout_summary__render_walk~spalding.png deleted file mode 100644 index b96876873d..0000000000 Binary files a/tests/test_images/test_workout_summary__render_walk~spalding.png and /dev/null differ diff --git a/tests/test_includes/clar_asserts.h b/tests/test_includes/clar_asserts.h index 38c7f7dc1f..1126f5d25a 100644 --- a/tests/test_includes/clar_asserts.h +++ b/tests/test_includes/clar_asserts.h @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/tests/test_infra/header_overrides.c b/tests/test_infra/header_overrides.c index 0c52b5e99b..4061fc31dd 100644 --- a/tests/test_infra/header_overrides.c +++ b/tests/test_infra/header_overrides.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "_test/default_only.h" #include "_test/default_and_custom.h" diff --git a/tests/test_infra/test_test_infra.c b/tests/test_infra/test_test_infra.c index 5276358d5c..84e6ed9732 100644 --- a/tests/test_infra/test_test_infra.c +++ b/tests/test_infra/test_test_infra.c @@ -1,18 +1,5 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "clar.h" diff --git a/tests/test_infra/wscript b/tests/test_infra/wscript deleted file mode 100644 index 02bd399eab..0000000000 --- a/tests/test_infra/wscript +++ /dev/null @@ -1,11 +0,0 @@ -from waftools.pebble_test import clar - -def build(ctx): - clar(ctx, - sources=['header_overrides.c'], - test_sources_ant_glob='test_test_infra.c', - override_includes=['test_infra_test']) - - -# vim:filetype=python - diff --git a/tests/test_infra/wscript_build b/tests/test_infra/wscript_build new file mode 100644 index 0000000000..53cc5d46f9 --- /dev/null +++ b/tests/test_infra/wscript_build @@ -0,0 +1,8 @@ +from waftools.pebble_test import clar + +clar(ctx, + sources=['header_overrides.c'], + test_sources_ant_glob='test_test_infra.c', + override_includes=['test_infra_test']) + +# vim:filetype=python diff --git a/tests/vendor/duma/duma_config.h b/tests/vendor/duma/duma_config.h index 36e51381f0..4a1758ba9e 100644 --- a/tests/vendor/duma/duma_config.h +++ b/tests/vendor/duma/duma_config.h @@ -186,7 +186,11 @@ /* * Number of bytes per virtual-memory page, as returned by Page_Size(). */ +#if defined(__APPLE__) && defined(__aarch64__) +#define DUMA_PAGE_SIZE 16384 +#else #define DUMA_PAGE_SIZE 4096 +#endif /* * Minimum required alignment by CPU. diff --git a/tests/vendor/duma/wscript b/tests/vendor/duma/wscript deleted file mode 100644 index c19ab89f50..0000000000 --- a/tests/vendor/duma/wscript +++ /dev/null @@ -1,21 +0,0 @@ -def configure(conf): - pass - -def build(bld): - if 'DUMA_DISABLED' in bld.env.DEFINES: - return - - import sys - - defines = [] - if 'linux' in sys.platform: - defines.append('_POSIX_C_SOURCE') - elif 'darwin' in sys.platform: - defines.append('DUMA_SO_PREFER_GETENV') - - bld.stlib(source=['duma.c', 'print.c', 'sem_inc.c'], - cflags=['-Wno-unused-but-set-variable'], # Disabling a warning -Werror is tripping over - defines=defines, - target='duma') - -# vim:filetype=python diff --git a/tests/vendor/duma/wscript_build b/tests/vendor/duma/wscript_build new file mode 100644 index 0000000000..cb41f9bf77 --- /dev/null +++ b/tests/vendor/duma/wscript_build @@ -0,0 +1,15 @@ +import sys + +if 'DUMA_DISABLED' not in bld.env.DEFINES: + defines = [] + if 'linux' in sys.platform: + defines.append('_POSIX_C_SOURCE') + elif 'darwin' in sys.platform: + defines.append('DUMA_SO_PREFER_GETENV') + + bld.stlib(source=['duma.c', 'print.c', 'sem_inc.c'], + cflags=['-Wno-unused-but-set-variable'], # Disabling a warning -Werror is tripping over + defines=defines, + target='duma') + +# vim:filetype=python diff --git a/tests/wscript b/tests/wscript deleted file mode 100644 index 53cd96dcab..0000000000 --- a/tests/wscript +++ /dev/null @@ -1,366 +0,0 @@ -# FIXME: PBL-17362 Script should be build type (FW or SDK) agnostic (waf & PEP8 compliant) -import bitmapgen -import png2pblpng -import os -import re -import sh -import sys -import waflib -from waftools.pebble_test import clar - -def remove_old_coverage_files(bld): - # Remove old .gcda files: - old_coverage_files = bld.path.get_bld().ant_glob('**/*.gcda lcov.info', remove=False) - for old_file in old_coverage_files: - os.remove(old_file.abspath()) - -def update_lcov(bld): - """ Update lcov-related files based on the results of `./waf test`""" - print("Generating code coverage information using lcov...") - lcov_version_cmd = ['lcov', '--version'] - # Send stdout of checking for lcov to /dev/null to hide it; stderr will still be visible - with open(os.devnull, 'w') as devnull_fp: - ret = bld.exec_command(lcov_version_cmd, stdout=devnull_fp) - if ret != 0: - bld.fatal("Error running `lcov`. Is it installed?") - tests_path = bld.path.get_bld().abspath() - lcov_info_out_file = os.path.join(tests_path, 'lcov.info') - try: - platform_specific_lcov_args = [] - if sys.platform.startswith('linux'): - platform_specific_lcov_args.extend(['--gcov-tool', 'llvm-cov']) - cmd = ['lcov', '--capture', '--directory', tests_path, '--output-file', lcov_info_out_file] - cmd += platform_specific_lcov_args - bld.cmd_and_log(cmd, quiet=waflib.Context.BOTH) - # remove unit-tests directory itself from lcov report - cmd = ['lcov', '--remove', lcov_info_out_file, 'tests/**', '-o', lcov_info_out_file] - cmd += platform_specific_lcov_args - bld.cmd_and_log(cmd, quiet=waflib.Context.BOTH) - except waflib.Errors.WafError as e: - print(e.stdout, '\n', e.stderr) - bld.fatal("Error running `lcov`") - if bld.options.coverage: - lcov_html_directory = os.path.join(tests_path, 'lcov-html') - genhtml_cmd = ['genhtml', lcov_info_out_file, '--output-directory', lcov_html_directory] - try: - bld.cmd_and_log(genhtml_cmd, quiet=waflib.Context.BOTH) - except waflib.Errors.WafError as e: - print(e.stdout, '\n', e.stderr) - bld.fatal("Error running `genhtml`") - index_html = os.path.join(lcov_html_directory, 'index.html') - print("Updated coverage report at %s" % index_html) - -def convert_png_to_pbi(task): - src_png = task.inputs[0].srcpath() - dest_pbi = task.outputs[0].srcpath() - bitdepth = None - - if any(word in dest_pbi for word in ['.8bit.', '~snowy', '~spalding', '~cutts', '~robert']): - img_fmt = 'color_raw' - elif any(word in dest_pbi for word in ['.1bit.', '~tintin']): - img_fmt = 'bw' - else: - img_fmt = 'color' # raw and palettized color images - bit_suffix = re.search(r'(\d)bitpalette\.png', dest_pbi) - if bit_suffix: - bitdepth = int(bit_suffix.group(1)) - - pb = bitmapgen.PebbleBitmap(src_png, bitmap_format=img_fmt, crop=False, bitdepth=bitdepth) - pb.convert_to_pbi_file(dest_pbi) - -def convert_png_to_pblpng(task): - src_png = task.inputs[0].srcpath() - dest_png = task.outputs[0].srcpath() - - # we need to be able to skip the png generator for specific test pngs flagged 'raw' - # and copy over the original file - if dest_png.endswith('.raw.png'): - task.exec_command('cp -f {0} {1}'.format(task.inputs[0].abspath(), task.outputs[0].abspath())) - else: - palette_name = 'pebble64' - bitdepth = None - bit_suffix = re.search(r'(\d)bit(palette)?\.png', dest_png) - - if bit_suffix: - bitdepth = int(bit_suffix.group(1)) - elif any(word in dest_png for word in ['~snowy', '~spalding', '~cutts', '~robert']): - bitdepth = 8 - elif any(word in dest_png for word in ['~tintin']): - bitdepth = 1 - palette_name = 'pebble2' - - png2pblpng.convert_png_to_pebble_png(src_png, dest_png, - palette_name=palette_name, bitdepth=bitdepth) - -# Creates a job for each PNG in the test_images directory. -# Each of these PNGs will be converted into a PBI in the build directory. -# Also exports TEST_IMAGES_PATH to point to the location of the PBIs. -def generate_test_pbis(ctx): - - test_image_pbis = [] - - bitmapgen_path = ctx.path.find_node('../tools/bitmapgen.py').abspath() - - for png_file in ctx.path.find_node('test_images').ant_glob("*.png"): - dest_pbi = png_file.get_bld().change_ext('.pbi') - - # if the image contains Xbit in the name, then generate both 1bit and 8bit PBI images - if ".Xbit." in str(dest_pbi): - dest_pbi = png_file.get_bld().change_ext('.1bit.pbi', '.Xbit.png') - ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, - bmp_script=bitmapgen_path) - test_image_pbis.append(dest_pbi) - - dest_pbi = png_file.get_bld().change_ext('.8bit.pbi', '.Xbit.png') - ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, - bmp_script=bitmapgen_path) - test_image_pbis.append(dest_pbi) - else: - ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, - bmp_script=bitmapgen_path) - test_image_pbis.append(dest_pbi) - - return test_image_pbis - -# Creates a job for select PNG in the test_images directory. -# Each of these PNGs will be converted into a Pebble PNG8 in the build directory. -def generate_test_pngs(ctx): - - test_image_pngs = [] - - pblpng_resources_list = [] - pblpng_resources_list.extend( - ctx.path.find_node('test_images').ant_glob("test_png__*.png")) - - for png_file in pblpng_resources_list: - dest_png = png_file.get_bld() - - ctx(name='png_to_pblpng', rule=convert_png_to_pblpng, source=png_file, target=dest_png) - test_image_pngs.append(dest_png) - - return test_image_pngs - -def copy_test_pngs_to_build_dir(ctx): - test_image_pngs = [] - - # copy over test specific files such as png, apng - copy_resources_list = [] - copy_resources_list.extend( - ctx.path.find_node('test_images').ant_glob("test_bitblt_circular__*.png")) - copy_resources_list.extend( - ctx.path.find_node('test_images').ant_glob("test_gbitmap_sequence__*.apng")) - copy_resources_list.extend( - ctx.path.find_node('test_images').ant_glob("test_kino_reel__*.apng")) - copy_resources_list.extend( - ctx.path.find_node('test_images').ant_glob("test_graphics_draw_text_flow__*.png")) - for copy_file in copy_resources_list: - dest_file = copy_file.get_bld() - ctx(name='copy_png', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) - test_image_pngs.append(dest_file) - - return test_image_pngs - - -def copy_pdc_files_to_build_dir(ctx): - test_image_pdc_files = [] - copy_resources_list = ctx.path.find_node('test_images').ant_glob("*.pdc") - for copy_file in copy_resources_list: - dest_file = copy_file.get_bld() - ctx(name='copy_pdc', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) - test_image_pdc_files.append(dest_file) - - return test_image_pdc_files - - -def copy_pfo_files_to_build_dir(ctx): - test_image_pfo_files = [] - copy_resources_list = ctx.path.find_node('test_images').ant_glob("*.pfo") - for copy_file in copy_resources_list: - dest_file = copy_file.get_bld() - ctx(name='copy_pfo', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) - test_image_pfo_files.append(dest_file) - - return test_image_pfo_files - - -def convert_test_pdcs(ctx): - test_image_pdc_files = [] - - resources_list = ctx.path.find_node('test_images').ant_glob("*.svg") - resources_list.extend(ctx.path.find_node('test_images').ant_glob("*", src=False, dir=True)) - import sys - sys.path.insert(0, ctx.path.parent.abspath()) - - from tools.generate_pdcs import pdc_gen - def convert_svg_image(task): - pdc_gen.create_pdc_from_path( - task.inputs[0].abspath(), - task.outputs[0].abspath(), - viewbox_size=(0, 0), - verbose=False, - duration=0, - play_count=0) - - def convert_svg_sequence(task): - dir_name = os.path.dirname(task.inputs[0].abspath()) - pdc_gen.create_pdc_from_path( - dir_name, - task.outputs[0].abspath(), - viewbox_size=(0, 0), - verbose=False, - duration=33, - play_count=1) - - for input_node in resources_list: - output_pdc = input_node.get_bld().change_ext('.pdc') - - test_image_pdc_files.append(output_pdc) - if os.path.isdir(input_node.abspath()): - conversion_rule = convert_svg_sequence - source_files = input_node.ant_glob("*.svg") - else: - conversion_rule = convert_svg_image - source_files = [input_node] - - ctx(rule=conversion_rule, - source=source_files, - target=output_pdc) - - return test_image_pdc_files - - -def convert_pdc_to_pbi(ctx): - bitmapgen_path = ctx.path.find_node('../tools/bitmapgen.py').abspath() - - test_pdc_pbis = [] - - pdc_files = ctx.path.find_node('test_images').ant_glob("test_pdc__*.pdc") - pdc2png = ctx.path.get_bld().parent.make_node('pdc2png') - - for pdc in pdc_files: - dest_pdc = pdc.get_bld().change_ext('.pdc.pdc') - src_png = dest_pdc.change_ext('.png') - dest_pbi = dest_pdc.change_ext('.pbi') - ctx(rule='cp ${SRC} ${TGT}', source=pdc, target=dest_pdc) - ctx(rule='${SRC[0].abspath()} ${SRC[1].abspath()}', source=[pdc2png, dest_pdc], target=src_png) - ctx(rule=convert_png_to_pbi, source=src_png, target=dest_pbi, bmp_script=bitmapgen_path) - test_pdc_pbis.append(dest_pbi) - - return test_pdc_pbis - -def options(opt): - gr = opt.add_option_group('test options') - gr.add_option('-D', '--debug_test', action='store_true', - help='Execute tests within GDB. Use alongside -M.') - gr.add_option('-M', '--match', dest='regex', default=None, action='store', - help='Run regex match tests. Example: ./waf test -M "test.*resource.*"') - gr.add_option('-L', '--list_tests', dest='list_tests', action='store_true', - help='List all test names. Usually used in conjunction with -M. Example: ' - './waf test -M test_animation -L') - gr.add_option('-T', '--test_name', dest='test_name', default=None, action='store', - help='Run only the given test name. Usually used in conjunction with -M. Example: ' - './waf test -M test_animation -T unschedule') - gr.add_option('-C', '--coverage', dest='coverage', action='store_true', help='Generate gcov test coverage data and use lcov to generate HTML report') - gr.add_option('--show_output', action='store_true', help='show test output') - gr.add_option('--no_run', action='store_true', help='Do not run the tests, just build them') - gr.add_option('--no_images', action='store_true', help='skip generation of test images, ' - 'which are only required for some tests and can slow down build times') - -def build(bld): - if bld.options.debug_test: - if not bld.options.regex: - bld.fatal('When using --debug_test, you must also use --match to' - ' specify the test file to debug') - bld.env.append_value('DEFINES', 'UNITTEST_DEBUG') - - bld.env.CFLAGS.append('-I' + bld.path.abspath() + '/../src/fw/util/time') - bld.env.CFLAGS.append('-I' + bld.path.abspath() + '/../src/include') - - # clang on Linux errors on true == true or false == false compile-time assertions - bld.env.CFLAGS.append('-Wno-tautological-compare') - - # Any test in this list won't be compiled - bld.env.BROKEN_TESTS = [ - 'test_app_fetch_endpoint.c', - 'test_graphics_draw_text_flow.c', - 'test_perimeter.c', - 'test_ancs_pebble_actions.c', - 'test_timeline_actions.c', - 'test_bluetooth_persistent_storage_prf.c', - 'test_bluetooth_persistent_storage.c', - 'test_session.c', - 'test_session_receive_router.c', - 'test_compositor.c', - 'test_floor.c', - 'test_pow.c', - 'test_ams.c', - 'test_ams_util.c', - 'test_gap_le_advert.c', - 'test_bt_conn_mgr.c', - 'test_gatt_client_accessors.c', - 'test_gatt_client_discovery.c', - 'test_gatt_client_subscriptions.c', - 'test_gatt_service_changed_client.c', - 'test_gatt_service_changed_server.c', - 'test_gap_le_connect.c', - 'test_ancs_util.c', - 'test_ancs.c', - 'test_kernel_le_client.c', - 'test_ppogatt.c', - 'test_graphics_circle.c' - ] - - # Don't run the python tool tests because they exercise a lot of old python2 code that still needs to be updated - bld.env.PYTHON_TOOL_TESTS_DISABLED = True - - # Disable warning promotion. Not ideal, but gets most of the tests running again without straight up disabling the new warnings - bld.env.CFLAGS.append('-Wno-error') - - # Many tests operate on a set of test images and require tools to process these - # images and therefore need extra defines. Set up our environment first before running any - # tests. - test_images_dest_dir = bld.path.find_node('test_images').get_bld() - - # Set up the fail directory, and make it. This is used to output data from the tests for - # comparison with the expected results. - fail_dir = test_images_dest_dir.parent.make_node('failed') - fail_path = fail_dir.abspath().strip() - sh.rm('-rf', fail_path) - fail_dir.mkdir() - - def convert_to_emscripten_fs_path_if_needed(node): - real_fs_abspath = node.abspath() - if bld.variant != 'test_rocky_emx': - return real_fs_abspath - # When transpiling unittests with Emscripten, the host machine's - # filesystem is mounted at /node_fs, so we need to translate paths. - return '/node_fs' + real_fs_abspath - - bld.env.test_image_defines = [ - 'TEST_IMAGES_PATH="%s"' % convert_to_emscripten_fs_path_if_needed(test_images_dest_dir), - 'TEST_OUTPUT_PATH="%s"' % convert_to_emscripten_fs_path_if_needed(fail_dir), - 'PBI2PNG_EXE="%s"' % bld.path.find_node('../tools/pbi2png.py').abspath()] - - # Add test_pbis or test_pngs to runtime_deps for tests that require them - if not bld.options.no_images: - bld.env.test_pbis = generate_test_pbis(bld) - bld.env.test_pngs = copy_test_pngs_to_build_dir(bld) - bld.env.test_pngs.extend(generate_test_pngs(bld)) - bld.env.test_pfos = copy_pfo_files_to_build_dir(bld) - # Includes reference pdc and pbi generated from ref png - bld.env.test_pdcs = bld.env.test_pbis + convert_test_pdcs(bld) + copy_pdc_files_to_build_dir(bld) - bld.env.pdcs2png_test_files = bld.env.test_pbis + convert_pdc_to_pbi(bld) - - if bld.options.coverage: - bld.env.append_value('CFLAGS', '-fprofile-arcs') - bld.env.append_value('CFLAGS', '-ftest-coverage') - bld.env.append_value('LINKFLAGS', '--coverage') - test_wscript_dirs = [os.path.dirname(f.abspath()) for f in bld.path.ant_glob('**/wscript')] - for dir in test_wscript_dirs: - bld.recurse(dir) - if bld.options.coverage: - bld.add_pre_fun(remove_old_coverage_files) - bld.add_post_fun(update_lcov) - - -# vim:filetype=python diff --git a/tests/wscript_build b/tests/wscript_build new file mode 100644 index 0000000000..0789ef79ce --- /dev/null +++ b/tests/wscript_build @@ -0,0 +1,331 @@ +# FIXME: PBL-17362 Script should be build type (FW or SDK) agnostic (waf & PEP8 compliant) +import bitmapgen +import png2pblpng +import os +import re +import sh +import sys +import waflib +from waftools.pebble_test import clar + +def remove_old_coverage_files(bld): + # Remove old .gcda files: + old_coverage_files = bld.path.get_bld().ant_glob('**/*.gcda lcov.info', remove=False) + for old_file in old_coverage_files: + os.remove(old_file.abspath()) + +def update_lcov(bld): + """ Update lcov-related files based on the results of `./waf test`""" + print("Generating code coverage information using lcov...") + lcov_version_cmd = ['lcov', '--version'] + # Send stdout of checking for lcov to /dev/null to hide it; stderr will still be visible + with open(os.devnull, 'w') as devnull_fp: + ret = bld.exec_command(lcov_version_cmd, stdout=devnull_fp) + if ret != 0: + bld.fatal("Error running `lcov`. Is it installed?") + tests_path = bld.path.get_bld().abspath() + lcov_info_out_file = os.path.join(tests_path, 'lcov.info') + try: + platform_specific_lcov_args = [] + if sys.platform.startswith('linux'): + platform_specific_lcov_args.extend(['--gcov-tool', 'llvm-cov']) + cmd = ['lcov', '--capture', '--directory', tests_path, '--output-file', lcov_info_out_file] + cmd += platform_specific_lcov_args + bld.cmd_and_log(cmd, quiet=waflib.Context.BOTH) + # remove unit-tests directory itself from lcov report + cmd = ['lcov', '--remove', lcov_info_out_file, 'tests/**', '-o', lcov_info_out_file] + cmd += platform_specific_lcov_args + bld.cmd_and_log(cmd, quiet=waflib.Context.BOTH) + except waflib.Errors.WafError as e: + print(e.stdout, '\n', e.stderr) + bld.fatal("Error running `lcov`") + if bld.options.coverage: + lcov_html_directory = os.path.join(tests_path, 'lcov-html') + genhtml_cmd = ['genhtml', lcov_info_out_file, '--output-directory', lcov_html_directory] + try: + bld.cmd_and_log(genhtml_cmd, quiet=waflib.Context.BOTH) + except waflib.Errors.WafError as e: + print(e.stdout, '\n', e.stderr) + bld.fatal("Error running `genhtml`") + index_html = os.path.join(lcov_html_directory, 'index_html') + print("Updated coverage report at %s" % index_html) + +def convert_png_to_pbi(task): + src_png = task.inputs[0].srcpath() + dest_pbi = task.outputs[0].srcpath() + bitdepth = None + + if any(word in dest_pbi for word in ['.8bit.', '~obelix', '~gabbro']): + img_fmt = 'color_raw' + elif any(word in dest_pbi for word in ['.1bit.', '~asterix']): + img_fmt = 'bw' + else: + img_fmt = 'color' # raw and palettized color images + bit_suffix = re.search(r'(\d)bitpalette\.png', dest_pbi) + if bit_suffix: + bitdepth = int(bit_suffix.group(1)) + + pb = bitmapgen.PebbleBitmap(src_png, bitmap_format=img_fmt, crop=False, bitdepth=bitdepth) + pb.convert_to_pbi_file(dest_pbi) + +def convert_png_to_pblpng(task): + src_png = task.inputs[0].srcpath() + dest_png = task.outputs[0].srcpath() + + # we need to be able to skip the png generator for specific test pngs flagged 'raw' + # and copy over the original file + if dest_png.endswith('.raw.png'): + task.exec_command('cp -f {0} {1}'.format(task.inputs[0].abspath(), task.outputs[0].abspath())) + else: + palette_name = 'pebble64' + bitdepth = None + bit_suffix = re.search(r'(\d)bit(palette)?\.png', dest_png) + + if bit_suffix: + bitdepth = int(bit_suffix.group(1)) + elif any(word in dest_png for word in ['~obelix', '~gabbro']): + bitdepth = 8 + elif any(word in dest_png for word in ['~asterix']): + bitdepth = 1 + palette_name = 'pebble2' + + png2pblpng.convert_png_to_pebble_png(src_png, dest_png, + palette_name=palette_name, bitdepth=bitdepth) + +# Creates a job for each PNG in the test_images directory. +# Each of these PNGs will be converted into a PBI in the build directory. +# Also exports TEST_IMAGES_PATH to point to the location of the PBIs. +def generate_test_pbis(ctx): + + test_image_pbis = [] + + bitmapgen_path = ctx.path.find_node('../tools/bitmapgen.py').abspath() + + test_images_node = ctx.path.find_node('test_images') + available_pngs = {n.name for n in test_images_node.ant_glob("*.png")} + + for png_file in test_images_node.ant_glob("*.png"): + dest_pbi = png_file.get_bld().change_ext('.pbi') + + # if the image contains Xbit in the name, then generate both 1bit and 8bit PBI images + if ".Xbit." in str(dest_pbi): + dest_pbi = png_file.get_bld().change_ext('.1bit.pbi', '.Xbit.png') + ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, + bmp_script=bitmapgen_path) + test_image_pbis.append(dest_pbi) + + # Only emit the 8bit.pbi from the Xbit.png if there is no platform- + # specific .8bit.png baseline alongside it. The latter takes + # precedence (e.g. when obelix's 200x228 framebuffer needs different + # baseline dimensions than a 1bit asterix render). + sibling_8bit = png_file.name.replace(".Xbit.", ".8bit.") + if sibling_8bit not in available_pngs: + dest_pbi = png_file.get_bld().change_ext('.8bit.pbi', '.Xbit.png') + ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, + bmp_script=bitmapgen_path) + test_image_pbis.append(dest_pbi) + else: + ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, + bmp_script=bitmapgen_path) + test_image_pbis.append(dest_pbi) + + return test_image_pbis + +# Creates a job for select PNG in the test_images directory. +# Each of these PNGs will be converted into a Pebble PNG8 in the build directory. +def generate_test_pngs(ctx): + + test_image_pngs = [] + + pblpng_resources_list = [] + pblpng_resources_list.extend( + ctx.path.find_node('test_images').ant_glob("test_png__*.png")) + + for png_file in pblpng_resources_list: + dest_png = png_file.get_bld() + + ctx(name='png_to_pblpng', rule=convert_png_to_pblpng, source=png_file, target=dest_png) + test_image_pngs.append(dest_png) + + return test_image_pngs + +def copy_test_pngs_to_build_dir(ctx): + test_image_pngs = [] + + # copy over test specific files such as png, apng + copy_resources_list = [] + copy_resources_list.extend( + ctx.path.find_node('test_images').ant_glob("test_bitblt_circular__*.png")) + copy_resources_list.extend( + ctx.path.find_node('test_images').ant_glob("test_gbitmap_sequence__*.apng")) + copy_resources_list.extend( + ctx.path.find_node('test_images').ant_glob("test_kino_reel__*.apng")) + copy_resources_list.extend( + ctx.path.find_node('test_images').ant_glob("test_graphics_draw_text_flow__*.png")) + for copy_file in copy_resources_list: + dest_file = copy_file.get_bld() + ctx(name='copy_png', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) + test_image_pngs.append(dest_file) + + return test_image_pngs + + +def copy_pdc_files_to_build_dir(ctx): + test_image_pdc_files = [] + copy_resources_list = ctx.path.find_node('test_images').ant_glob("*.pdc") + for copy_file in copy_resources_list: + dest_file = copy_file.get_bld() + ctx(name='copy_pdc', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) + test_image_pdc_files.append(dest_file) + + return test_image_pdc_files + + +def copy_pfo_files_to_build_dir(ctx): + test_image_pfo_files = [] + copy_resources_list = ctx.path.find_node('test_images').ant_glob("*.pfo") + for copy_file in copy_resources_list: + dest_file = copy_file.get_bld() + ctx(name='copy_pfo', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) + test_image_pfo_files.append(dest_file) + + return test_image_pfo_files + + +def convert_test_pdcs(ctx): + test_image_pdc_files = [] + + resources_list = ctx.path.find_node('test_images').ant_glob("*.svg") + resources_list.extend(ctx.path.find_node('test_images').ant_glob("*", src=False, dir=True)) + import sys + sys.path.insert(0, ctx.path.parent.abspath()) + + from tools.generate_pdcs import pdc_gen + def convert_svg_image(task): + pdc_gen.create_pdc_from_path( + task.inputs[0].abspath(), + task.outputs[0].abspath(), + viewbox_size=(0, 0), + verbose=False, + duration=0, + play_count=0) + + def convert_svg_sequence(task): + dir_name = os.path.dirname(task.inputs[0].abspath()) + pdc_gen.create_pdc_from_path( + dir_name, + task.outputs[0].abspath(), + viewbox_size=(0, 0), + verbose=False, + duration=33, + play_count=1) + + for input_node in resources_list: + output_pdc = input_node.get_bld().change_ext('.pdc') + + test_image_pdc_files.append(output_pdc) + if os.path.isdir(input_node.abspath()): + conversion_rule = convert_svg_sequence + source_files = input_node.ant_glob("*.svg") + else: + conversion_rule = convert_svg_image + source_files = [input_node] + + ctx(rule=conversion_rule, + source=source_files, + target=output_pdc) + + return test_image_pdc_files + + +def convert_pdc_to_pbi(ctx): + bitmapgen_path = ctx.path.find_node('../tools/bitmapgen.py').abspath() + + test_pdc_pbis = [] + + pdc_files = ctx.path.find_node('test_images').ant_glob("test_pdc__*.pdc") + pdc2png = ctx.path.get_bld().parent.make_node('pdc2png') + + for pdc in pdc_files: + dest_pdc = pdc.get_bld().change_ext('.pdc.pdc') + src_png = dest_pdc.change_ext('.png') + dest_pbi = dest_pdc.change_ext('.pbi') + ctx(rule='cp ${SRC} ${TGT}', source=pdc, target=dest_pdc) + ctx(rule='${SRC[0].abspath()} ${SRC[1].abspath()}', source=[pdc2png, dest_pdc], target=src_png) + ctx(rule=convert_png_to_pbi, source=src_png, target=dest_pbi, bmp_script=bitmapgen_path) + test_pdc_pbis.append(dest_pbi) + + return test_pdc_pbis + + +if bld.options.debug_test: + if not bld.options.regex: + bld.fatal('When using --debug_test, you must also use --match to' + ' specify the test file to debug') + bld.env.append_value('DEFINES', 'UNITTEST_DEBUG') + +bld.env.CFLAGS.append('-I' + bld.path.abspath() + '/../src/fw/util/time') +bld.env.CFLAGS.append('-I' + bld.path.abspath() + '/../include') + +# clang on Linux errors on true == true or false == false compile-time assertions +bld.env.CFLAGS.append('-Wno-tautological-compare') + +# Any test in this list won't be compiled +bld.env.BROKEN_TESTS = [ + 'test_graphics_draw_circle_8bit.c', + 'test_graphics_fill_circle_8bit.c', + 'test_graphics_draw_line.c', + 'test_graphics_draw_rotated_bitmap.c', + # Crash (round-display segfault) and build (rect/round, platform) issues are + # fixed; remaining failures are missing gabbro golden .pbi images that need + # visual review before regeneration. Keep quarantined until goldens land. + 'test_gap_le_connect.c', +] + +# Don't run the python tool tests because they exercise a lot of old python2 code that still needs to be updated +bld.env.PYTHON_TOOL_TESTS_DISABLED = True + +# Disable warning promotion. Not ideal, but gets most of the tests running again without straight up disabling the new warnings +bld.env.CFLAGS.append('-Wno-error') + +# Many tests operate on a set of test images and require tools to process these +# images and therefore need extra defines. Set up our environment first before running any +# tests. +test_images_dest_dir = bld.path.find_node('test_images').get_bld() + +# Set up the fail directory, and make it. This is used to output data from the tests for +# comparison with the expected results. +fail_dir = test_images_dest_dir.parent.make_node('failed') +fail_path = fail_dir.abspath().strip() +sh.rm('-rf', fail_path) +fail_dir.mkdir() + +bld.env.test_image_defines = [ + 'TEST_IMAGES_PATH="%s"' % test_images_dest_dir.abspath(), + 'TEST_OUTPUT_PATH="%s"' % fail_dir.abspath(), + 'PBI2PNG_EXE="%s"' % bld.path.find_node('../tools/pbi2png.py').abspath()] + +# Add test_pbis or test_pngs to runtime_deps for tests that require them +if not bld.options.no_images: + bld.env.test_pbis = generate_test_pbis(bld) + bld.env.test_pngs = copy_test_pngs_to_build_dir(bld) + bld.env.test_pngs.extend(generate_test_pngs(bld)) + bld.env.test_pfos = copy_pfo_files_to_build_dir(bld) + # Includes reference pdc and pbi generated from ref png + bld.env.test_pdcs = bld.env.test_pbis + convert_test_pdcs(bld) + copy_pdc_files_to_build_dir(bld) + bld.env.pdcs2png_test_files = bld.env.test_pbis + convert_pdc_to_pbi(bld) + +if bld.options.coverage: + bld.env.append_value('CFLAGS', '-fprofile-arcs') + bld.env.append_value('CFLAGS', '-ftest-coverage') + bld.env.append_value('LINKFLAGS', '--coverage') +test_wscript_dirs = sorted({os.path.dirname(f.abspath()) + for f in bld.path.ant_glob(['**/wscript', '**/wscript_build'])}) +for dir in test_wscript_dirs: + bld.recurse(dir) +if bld.options.coverage: + bld.add_pre_fun(remove_old_coverage_files) + bld.add_post_fun(update_lcov) + + +# vim:filetype=python diff --git a/third_party/Kconfig b/third_party/Kconfig new file mode 100644 index 0000000000..e941d53fdf --- /dev/null +++ b/third_party/Kconfig @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menu "Third-party" + +rsource "hal_nordic/Kconfig" +rsource "hal_sifli/Kconfig" +rsource "memfault/Kconfig" +rsource "nimble/Kconfig" + +endmenu diff --git a/third_party/cmsis_core/cmsis_core.h b/third_party/cmsis_core/cmsis_core.h new file mode 100644 index 0000000000..8c82796558 --- /dev/null +++ b/third_party/cmsis_core/cmsis_core.h @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + +#if defined(CONFIG_QEMU) && defined(CONFIG_CORTEX_M4) +#include "pebble/qemu_cm4.h" +#elif defined(CONFIG_QEMU) && defined(CONFIG_CORTEX_M33) +#include "pebble/qemu_cm33.h" +#elif defined(MICRO_FAMILY_STM32F4) +#include +#elif defined(CONFIG_SOC_NRF52) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#include +#pragma GCC diagnostic pop +#elif defined(CONFIG_SOC_SF32LB52) +#include +#else +#error "Unknown or missing MICRO_FAMILY_* define" +#endif \ No newline at end of file diff --git a/third_party/cmsis_core/pebble/qemu_cm33.h b/third_party/cmsis_core/pebble/qemu_cm33.h new file mode 100644 index 0000000000..ff5a9f9c9d --- /dev/null +++ b/third_party/cmsis_core/pebble/qemu_cm33.h @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* CMSIS device header for QEMU Pebble Cortex-M33 targets + * (qemu_emery, qemu_gabbro). */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* Cortex-M33 core configuration */ +#define __CM33_REV 0x0000U +#define __NVIC_PRIO_BITS 3U +#define __Vendor_SysTickConfig 0U +#define __MPU_PRESENT 1U +#define __VTOR_PRESENT 1U +#define __FPU_PRESENT 1U +#define __DSP_PRESENT 0U +#define __SAUREGION_PRESENT 0U + +/* QEMU Pebble IRQ numbers */ +typedef enum IRQn { + /* Cortex-M processor exceptions */ + NonMaskableInt_IRQn = -14, + HardFault_IRQn = -13, + MemoryManagement_IRQn = -12, + BusFault_IRQn = -11, + UsageFault_IRQn = -10, + SecureFault_IRQn = -9, + SVCall_IRQn = -5, + DebugMonitor_IRQn = -4, + PendSV_IRQn = -2, + SysTick_IRQn = -1, + + /* QEMU device interrupts */ + UART0_IRQn = 0, + UART1_IRQn = 1, + UART2_IRQn = 2, + TIMER0_IRQn = 3, + TIMER1_IRQn = 4, + RTC_IRQn = 5, + GPIO_IRQn = 6, + DISPLAY_IRQn = 7, + EXTFLASH_IRQn = 8, + TOUCH_IRQn = 9, + AUDIO_IRQn = 10, + WATCHDOG_IRQn = 11, +} IRQn_Type; + +#include "core_cm33.h" + +/* Compatibility: ARMv8-M MPU uses RBAR/RLAR instead of RBAR/RASR. + * The codebase uses ARMv7-M MPU register names via vendor HAL compat. + * Provide RASR as an alias for RLAR for code that still references the + * ARMv7-M register name. */ +#ifndef MPU_RASR_TEX_Pos +#define MPU_RASR_TEX_Pos MPU_RLAR_AttrIndx_Pos +#define MPU_RASR_S_Msk 0 +#define MPU_RASR_C_Msk 0 +#define MPU_RASR_B_Msk 0 +#define RASR RLAR +#endif + +/* ARM_CM33 FreeRTOS port expects SCB_CCR_STKALIGN_Msk (always set on M33) */ +#ifndef SCB_CCR_STKALIGN_Msk +#define SCB_CCR_STKALIGN_Msk (1UL << 9) +#endif + +/* Generic SRAM base address */ +#ifndef SRAM_BASE +#define SRAM_BASE 0x20000000UL +#endif + +extern uint32_t SystemCoreClock; + +#ifdef __cplusplus +} +#endif diff --git a/third_party/cmsis_core/pebble/qemu_cm4.h b/third_party/cmsis_core/pebble/qemu_cm4.h new file mode 100644 index 0000000000..51970a8e4b --- /dev/null +++ b/third_party/cmsis_core/pebble/qemu_cm4.h @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2026 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* CMSIS device header for QEMU Pebble Cortex-M4 targets (qemu_flint). */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* Cortex-M4 core configuration */ +#define __CM4_REV 0x0000U +#define __NVIC_PRIO_BITS 3U +#define __Vendor_SysTickConfig 0U +#define __MPU_PRESENT 1U +#define __VTOR_PRESENT 1U +#define __FPU_PRESENT 0U + +/* QEMU Pebble IRQ numbers */ +typedef enum IRQn { + /* Cortex-M processor exceptions */ + NonMaskableInt_IRQn = -14, + HardFault_IRQn = -13, + MemoryManagement_IRQn = -12, + BusFault_IRQn = -11, + UsageFault_IRQn = -10, + SVCall_IRQn = -5, + DebugMonitor_IRQn = -4, + PendSV_IRQn = -2, + SysTick_IRQn = -1, + + /* QEMU device interrupts */ + UART0_IRQn = 0, + UART1_IRQn = 1, + UART2_IRQn = 2, + TIMER0_IRQn = 3, + TIMER1_IRQn = 4, + RTC_IRQn = 5, + GPIO_IRQn = 6, + DISPLAY_IRQn = 7, + EXTFLASH_IRQn = 8, + TOUCH_IRQn = 9, + AUDIO_IRQn = 10, + WATCHDOG_IRQn = 11, +} IRQn_Type; + +#include "core_cm4.h" + +/* Generic SRAM base address */ +#ifndef SRAM_BASE +#define SRAM_BASE 0x20000000UL +#endif + +extern uint32_t SystemCoreClock; + +#ifdef __cplusplus +} +#endif diff --git a/third_party/cmsis_core/wscript b/third_party/cmsis_core/wscript deleted file mode 100644 index b6c8922814..0000000000 --- a/third_party/cmsis_core/wscript +++ /dev/null @@ -1,3 +0,0 @@ -def build(bld): - bld(export_includes=['CMSIS/CMSIS/Core/Include'], - name='cmsis_core') diff --git a/third_party/cmsis_core/wscript_build b/third_party/cmsis_core/wscript_build new file mode 100644 index 0000000000..8680e40b63 --- /dev/null +++ b/third_party/cmsis_core/wscript_build @@ -0,0 +1,2 @@ +bld(export_includes=['CMSIS/CMSIS/Core/Include', '.'], + name='cmsis_core') diff --git a/third_party/freertos/FreeRTOS-Kernel b/third_party/freertos/FreeRTOS-Kernel index 9d0dfa4ddd..8583941bff 160000 --- a/third_party/freertos/FreeRTOS-Kernel +++ b/third_party/freertos/FreeRTOS-Kernel @@ -1 +1 @@ -Subproject commit 9d0dfa4ddd3dd816e52fdf541e3d57ada06329e5 +Subproject commit 8583941bff119369cad8dc65489a7d05140f5f67 diff --git a/third_party/freertos/FreeRTOSConfig.h b/third_party/freertos/FreeRTOSConfig.h index 5fc3412b5d..28f8e75cc0 100644 --- a/third_party/freertos/FreeRTOSConfig.h +++ b/third_party/freertos/FreeRTOSConfig.h @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2024 Google LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + // @nolint /* @@ -70,8 +73,7 @@ #include "drivers/rtc.h" -#define CMSIS_COMPATIBLE -#include +#include extern uint32_t SystemCoreClock; @@ -84,7 +86,7 @@ extern uint32_t SystemCoreClock; #define configMINIMAL_STACK_SIZE ((unsigned short)196) #define configTOTAL_HEAP_SIZE ((size_t)(30 * 1024)) #define configMAX_TASK_NAME_LEN (16) -#define configUSE_TRACE_FACILITY 0 +#define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_TICKLESS_IDLE 2 // Use STOP mode @@ -112,7 +114,7 @@ extern uint32_t SystemCoreClock; #define configCHECK_FOR_STACK_OVERFLOW 2 #define configUSE_RECURSIVE_MUTEXES 0 #define configQUEUE_REGISTRY_SIZE 0 -#define configGENERATE_RUN_TIME_STATS 0 +#define configGENERATE_RUN_TIME_STATS 1 #define configUSE_NEWLIB_REENTRANT 0 #define configUSE_QUEUE_SETS 1 #define configUSE_LIGHT_MUTEXES 1 @@ -155,28 +157,33 @@ NVIC value of 255. */ #define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 +#ifdef CONFIG_SOC_SF32LB52 +#define xPortSysTickHandler RTOS_SysTick_Handler +#else #define xPortSysTickHandler SysTick_Handler +#endif #define xPortPendSVHandler PendSV_Handler #define vPortSVCHandler SVC_Handler /* Set this to add error checking code to the entry of each FreeRTOS function to ensure that the caller is in the correct state to make the call (ISR priority, critical section, etc.) */ -#if !RELEASE -#define configCHECK_CALL_SAFETY 1 -#else +#ifdef CONFIG_RELEASE #define configCHECK_CALL_SAFETY 0 +#else +#define configCHECK_CALL_SAFETY 1 #endif /*----------------------------------------------------------- * Macros required to setup the timer for the run time stats. *-----------------------------------------------------------*/ -/* The run time stats time base just uses the existing high frequency timer -test clock. All these macros do is clear and return the high frequency -interrupt count respectively. */ -//extern unsigned long ulRunTimeStatsClock; -//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ulRunTimeStatsClock = 0 -//#define portGET_RUN_TIME_COUNTER_VALUE() ulRunTimeStatsClock +/* Use the FreeRTOS tick counter as the run-time stats clock. It runs at + * 1024 Hz and wraps every ~48.5 days on a 32-bit platform, so the task + * accumulators never wrap within a heartbeat period. Sleep time is + * accounted for by tickless idle, so the idle task's counter reflects + * real sleep + idle-loop time. */ +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() xTaskGetTickCount() #include "system/passert.h" #define configASSERT( x ) \ diff --git a/third_party/freertos/wscript b/third_party/freertos/wscript deleted file mode 100644 index 76e55d9e77..0000000000 --- a/third_party/freertos/wscript +++ /dev/null @@ -1,52 +0,0 @@ -def build(bld): - # FreeRTOS vendor code - if bld.env.MICRO_FAMILY == 'STM32F2': - freertos_port_name = 'ARM_CM3' - elif bld.env.MICRO_FAMILY == 'STM32F4': - freertos_port_name = 'ARM_CM4F' - elif bld.env.MICRO_FAMILY == 'STM32F7': - freertos_port_name = 'ARM_CM4F' # fix to CM7 when we have it - elif bld.env.MICRO_FAMILY == 'NRF52840': - freertos_port_name = 'ARM_CM4F' - elif bld.env.MICRO_FAMILY == 'SF32LB52': - freertos_port_name = 'ARM_CM33' - else: - bld.fatal('Unrecognized env.MICRO_FAMILY value %r' % - bld.env.MICRO_FAMILY) - - freertos_includes = [ - '.', - 'FreeRTOS-Kernel/FreeRTOS/Source/include', - f'FreeRTOS-Kernel/FreeRTOS/Source/portable/GCC/{freertos_port_name}' - ] - - freertos_source_paths = [ - 'FreeRTOS-Kernel/FreeRTOS/Source', - f'FreeRTOS-Kernel/FreeRTOS/Source/portable/GCC/{freertos_port_name}' - ] - freertos_sources = sum([bld.path.ant_glob(d + '/*.c') - for d in freertos_source_paths], []) - freertos_defines = [] - - # FIXME(SF32LB52): Add valid regions once MPU can be used - # This currently results in 0 regions! - if bld.env.MICRO_FAMILY == 'SF32LB52': - freertos_defines += [ - 'FREERTOS_FIRST_MPU_REGION=8', - 'FREERTOS_LAST_MPU_REGION=7', - ] - - bld(export_includes=freertos_includes, - export_defines=freertos_defines, - name='freertos_includes') - - bld.stlib(source=freertos_sources, - target='freertos', - use=['pblibc', - 'fw_includes', - 'freertos_includes'], - export_defines='GCC_{}'.format(freertos_port_name)) - -# vim:filetype=python - - diff --git a/third_party/freertos/wscript_build b/third_party/freertos/wscript_build new file mode 100644 index 0000000000..0b94f66565 --- /dev/null +++ b/third_party/freertos/wscript_build @@ -0,0 +1,54 @@ +# FreeRTOS vendor code +if bld.env.CONFIG_CORTEX_M4: + freertos_port_name = 'ARM_CM4F' +elif bld.env.CONFIG_CORTEX_M33: + freertos_port_name = 'ARM_CM33' +else: + bld.fatal('Unrecognized Cortex core for FreeRTOS port') + +freertos_includes = [ + '.', + 'FreeRTOS-Kernel/FreeRTOS/Source/include', + f'FreeRTOS-Kernel/FreeRTOS/Source/portable/GCC/{freertos_port_name}' +] + +freertos_source_paths = [ + 'FreeRTOS-Kernel/FreeRTOS/Source', + f'FreeRTOS-Kernel/FreeRTOS/Source/portable/GCC/{freertos_port_name}' +] +freertos_sources = sum([bld.path.ant_glob(d + '/*.c') + for d in freertos_source_paths], []) +freertos_defines = [] + +# SF32LB52: SiFli's HAL programs MPU regions 0..4 and Pebble's static +# MPU setup uses 5..7, so per-task configurable regions live at 8..11 +# (four slots: AppRAM, WorkerRAM, plus two NULL placeholders to satisfy +# the FreeRTOS port's multiple-of-four invariant). +if bld.env.CONFIG_SOC_SF32LB52: + freertos_defines += [ + 'FREERTOS_FIRST_MPU_REGION=8', + 'FREERTOS_LAST_MPU_REGION=11', + ] + +# QEMU CM33 does not have vendor-reserved MPU regions, so Pebble's +# static regions occupy 0..3 and FreeRTOS task regions occupy 4..7. +if bld.env.CONFIG_QEMU and bld.env.CONFIG_CORTEX_M33: + freertos_defines += [ + 'FREERTOS_FIRST_MPU_REGION=4', + 'FREERTOS_LAST_MPU_REGION=7', + ] + +bld(export_includes=freertos_includes, + export_defines=freertos_defines, + use=['cmsis_core'], + name='freertos_includes') + +bld.stlib(source=freertos_sources, + target='freertos', + use=['pblibc', + 'fw_includes', + 'freertos_includes', + 'cmsis_core'], + export_defines='GCC_{}'.format(freertos_port_name)) + +# vim:filetype=python diff --git a/third_party/hal_lsm6dso/wscript b/third_party/hal_lsm6dso/wscript deleted file mode 100644 index 804c5ce6c7..0000000000 --- a/third_party/hal_lsm6dso/wscript +++ /dev/null @@ -1,14 +0,0 @@ -def build(bld): - lsm6dso_includes = [ - 'lsm6dso-pid' - ] - - lsm6dso_sources = [ - 'lsm6dso-pid/lsm6dso_reg.c', - ] - - bld.stlib(source=lsm6dso_sources, - includes=lsm6dso_includes, - export_includes=lsm6dso_includes, - target='hal_lsm6dso', - use=['pblibc']) diff --git a/third_party/hal_lsm6dso/wscript_build b/third_party/hal_lsm6dso/wscript_build new file mode 100644 index 0000000000..5c92586508 --- /dev/null +++ b/third_party/hal_lsm6dso/wscript_build @@ -0,0 +1,13 @@ +lsm6dso_includes = [ + 'lsm6dso-pid' +] + +lsm6dso_sources = [ + 'lsm6dso-pid/lsm6dso_reg.c', +] + +bld.stlib(source=lsm6dso_sources, + includes=lsm6dso_includes, + export_includes=lsm6dso_includes, + target='hal_lsm6dso', + use=['pblibc']) diff --git a/third_party/hal_nordic/Kconfig b/third_party/hal_nordic/Kconfig new file mode 100644 index 0000000000..dd27d303b6 --- /dev/null +++ b/third_party/hal_nordic/Kconfig @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig HAL_NORDIC + bool "Nordic nrfx HAL" + help + Build the Nordic nrfx HAL. + +if HAL_NORDIC + +config HAL_NORDIC_NRF52840 + bool "nRF52840 SoC variant" + help + Configure the nrfx HAL for the Nordic nRF52840 SoC. This sets + the NRF52840_XXAA target define and enables the nrfx drivers + used by Pebble (I2S, GPIOTE, PDM, PPI, PWM, SPIM3, TIMER1/2, + TWIM0/1, UARTE0). It also disables NFCT pin usage so those pins + can be used as GPIOs and enables GPIO-as-pin-reset. + +endif # HAL_NORDIC diff --git a/third_party/hal_nordic/wscript b/third_party/hal_nordic/wscript deleted file mode 100644 index 37765ddeb9..0000000000 --- a/third_party/hal_nordic/wscript +++ /dev/null @@ -1,52 +0,0 @@ -def configure(conf): - if conf.env.MICRO_FAMILY == 'NRF52840': - conf.env.append_unique('DEFINES', 'NRF52840_XXAA') - if conf.is_asterix(): - conf.env.append_unique('DEFINES', 'NRF_CONFIG_NFCT_PINS_AS_GPIOS') - conf.env.append_unique('DEFINES', 'CONFIG_GPIO_AS_PINRESET') - conf.env.append_unique('DEFINES', 'NRFX_I2S_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_I2S0_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_GPIOTE_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_GPIOTE_CONFIG_NUM_OF_EVT_HANDLERS=4') - conf.env.append_unique('DEFINES', 'NRFX_PDM_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_PPI_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_PWM_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_PWM0_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_QSPI_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_SPIM_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_SPIM3_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TIMER_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TIMER1_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TIMER2_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TWIM_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TWIM0_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_TWIM1_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_UARTE_ENABLED=1') - conf.env.append_unique('DEFINES', 'NRFX_UARTE0_ENABLED=1') - - -def build(bld): - if bld.env.MICRO_FAMILY == 'NRF52840': - micro_sources = bld.path.ant_glob('nrfx/**/*.c', excl = ['**/system_nrf*.c', '**/startup_nrf_common.c', '**/nrfx_twi_twim.c']) - - micro_includes = [ - '.', - 'nrfx', - 'nrfx/hal', - 'nrfx/helpers', - 'nrfx/mdk', - 'nrfx/drivers/include', - 'nrfx/templates', - ] - - if bld.is_asterix(): - # is there a better way to do this? - micro_includes += ['../../src/fw/board/boards/board_asterix'] - else: - bld.fatal("no board for nRF5 includes") - - bld.stlib(source=micro_sources, - target='hal_nordic', - use=['cmsis_core', 'pblibc'], - includes=micro_includes, - export_includes=micro_includes) diff --git a/third_party/hal_nordic/wscript_build b/third_party/hal_nordic/wscript_build new file mode 100644 index 0000000000..ff462243e1 --- /dev/null +++ b/third_party/hal_nordic/wscript_build @@ -0,0 +1,49 @@ +if bld.env.CONFIG_HAL_NORDIC_NRF52840: + bld.env.append_unique('DEFINES', [ + 'NRF52840_XXAA', + 'NRF_CONFIG_NFCT_PINS_AS_GPIOS', + 'CONFIG_GPIO_AS_PINRESET', + 'NRFX_I2S_ENABLED=1', + 'NRFX_I2S0_ENABLED=1', + 'NRFX_GPIOTE_ENABLED=1', + 'NRFX_GPIOTE_CONFIG_NUM_OF_EVT_HANDLERS=4', + 'NRFX_PDM_ENABLED=1', + 'NRFX_PPI_ENABLED=1', + 'NRFX_PWM_ENABLED=1', + 'NRFX_PWM0_ENABLED=1', + 'NRFX_SPIM_ENABLED=1', + 'NRFX_SPIM3_ENABLED=1', + 'NRFX_TIMER_ENABLED=1', + 'NRFX_TIMER1_ENABLED=1', + 'NRFX_TIMER2_ENABLED=1', + 'NRFX_TWIM_ENABLED=1', + 'NRFX_TWIM0_ENABLED=1', + 'NRFX_TWIM1_ENABLED=1', + 'NRFX_UARTE_ENABLED=1', + 'NRFX_UARTE0_ENABLED=1', + ]) + +micro_sources = bld.path.ant_glob('nrfx/**/*.c', excl=['**/system_nrf*.c', '**/startup_nrf_common.c', '**/nrfx_twi_twim.c']) + +micro_includes = [ + '.', + 'nrfx', + 'nrfx/hal', + 'nrfx/helpers', + 'nrfx/mdk', + 'nrfx/drivers/include', + 'nrfx/templates', +] + +if bld.env.CONFIG_BOARD_FAMILY_ASTERIX: + # is there a better way to do this? + micro_includes += ['../../src/fw/board/boards/board_asterix'] +else: + bld.fatal("no board for nRF5 includes") + +bld.stlib(source=micro_sources, + target='hal_nordic', + cflags=['-Wno-unused-variable'], + use=['cmsis_core', 'pblibc'], + includes=micro_includes, + export_includes=micro_includes) diff --git a/third_party/hal_sifli/Kconfig b/third_party/hal_sifli/Kconfig new file mode 100644 index 0000000000..68426fac8e --- /dev/null +++ b/third_party/hal_sifli/Kconfig @@ -0,0 +1,246 @@ +# SPDX-FileCopyrightText: 2026 Core Devices LLC +# SPDX-License-Identifier: Apache-2.0 + +menuconfig HAL_SIFLI + bool "SiFli SDK HAL" + help + Build the SiFli SDK HAL. This enables the HAL driver + umbrella (USE_HAL_DRIVER), selects the high-performance CPU + (SOC_BF0_HCPU), sets the HAL tick rate to 1024 Hz, and lets + our NMI handler override SiFli's default. The HAL core + (bf0_hal, ADC, Cortex, DMA, eFuse, HLP, PMU, RCC, system + config) is always built; other modules are gated by the + options below. + +if HAL_SIFLI + +config HAL_SIFLI_SF32LB52 + bool "SF32LB52 SoC variant" + select HAL_SIFLI_AON + select HAL_SIFLI_GPT + select HAL_SIFLI_IPC_QUEUE + select HAL_SIFLI_LPTIM + select HAL_SIFLI_LRC_CAL + select HAL_SIFLI_MPI + select HAL_SIFLI_PINMUX + select HAL_SIFLI_RTC + help + Configure the SiFli HAL for the SF32LB52 SoC. This sets the + SF32LB52X target define and adds the SF32LB52-specific cmsis + and local glue sources to the build. It also selects the HAL + modules required by SoC-level PebbleOS code (clock/tick, + sleep, flash XIP, pinmux, RTC and RC calibration). + +config HAL_SIFLI_AES + bool "AES HAL module" + help + Enable the AES SiFli HAL module driver. + +config HAL_SIFLI_AON + bool "AON HAL module" + select HAL_SIFLI_RTC + help + Enable the AON (always-on, HPSYS/LPSYS power and wakeup) + SiFli HAL module driver. + +config HAL_SIFLI_ATIM + bool "ATIM HAL module" + help + Enable the ATIM (advanced timer) SiFli HAL module driver. + +config HAL_SIFLI_AUDCODEC + bool "AUDCODEC HAL module" + help + Enable the AUDCODEC SiFli HAL module driver. + +config HAL_SIFLI_AUDPRC + bool "AUDPRC HAL module" + help + Enable the AUDPRC (audio processor) SiFli HAL module driver. + +config HAL_SIFLI_BUSMON + bool "BUSMON HAL module" + help + Enable the BUSMON (bus monitor) SiFli HAL module driver. + +config HAL_SIFLI_CRC + bool "CRC HAL module" + help + Enable the CRC SiFli HAL module driver. + +config HAL_SIFLI_EPIC + bool "EPIC HAL module" + help + Enable the EPIC (graphics engine) SiFli HAL module driver. + +config HAL_SIFLI_EXTDMA + bool "EXTDMA HAL module" + help + Enable the EXTDMA SiFli HAL module driver. + +config HAL_SIFLI_EZIP + bool "EZIP HAL module" + help + Enable the EZIP (image decompression) SiFli HAL module + driver. + +config HAL_SIFLI_GPIO + bool "GPIO HAL module" + select HAL_SIFLI_AON + help + Enable the GPIO SiFli HAL module driver. + +config HAL_SIFLI_GPT + bool "GPT HAL module" + help + Enable the GPT (general purpose timer) SiFli HAL module + driver. + +config HAL_SIFLI_HASH + bool "HASH HAL module" + help + Enable the HASH SiFli HAL module driver. + +config HAL_SIFLI_HCD + bool "HCD HAL module" + help + Enable the HCD (USB host controller) SiFli HAL module + driver. + +config HAL_SIFLI_I2C + bool "I2C HAL module" + help + Enable the I2C SiFli HAL module driver. + +config HAL_SIFLI_I2S + bool "I2S HAL module" + help + Enable the I2S SiFli HAL module driver. + +config HAL_SIFLI_IPC_QUEUE + bool "IPC queue" + select HAL_SIFLI_MAILBOX + help + Enable the IPC queue middleware for inter-core + communication. + +config HAL_SIFLI_LCD + bool "LCDC HAL module" + help + Enable the LCDC (LCD controller) SiFli HAL module driver. + +config HAL_SIFLI_LCPU + bool "LCPU boot support" + depends on HAL_SIFLI_SF32LB52 + select HAL_SIFLI_LCPU_CONFIG + select HAL_SIFLI_LCPU_PATCH + help + Enable LCPU power-on, patch installation and BT RF + calibration support. + +config HAL_SIFLI_LCPU_CONFIG + bool "LCPU_CONFIG HAL module" + help + Enable the LCPU_CONFIG (LCPU configuration exchange) SiFli + HAL module. + +config HAL_SIFLI_LCPU_PATCH + bool "LCPU_PATCH HAL module" + help + Enable the LCPU_PATCH (ROM patch) SiFli HAL module. + +config HAL_SIFLI_LPTIM + bool "LPTIM HAL module" + help + Enable the LPTIM (low-power timer) SiFli HAL module driver. + +config HAL_SIFLI_LRC_CAL + bool "LRC_CAL HAL module" + select HAL_SIFLI_LCPU_CONFIG + select HAL_SIFLI_MAILBOX + help + Enable the LRC_CAL (low-power RC oscillator calibration) + SiFli HAL module. + +config HAL_SIFLI_MAILBOX + bool "MAILBOX HAL module" + help + Enable the MAILBOX (inter-core mailbox) SiFli HAL module + driver. + +config HAL_SIFLI_MPI + bool "MPI HAL module" + help + Enable the MPI (memory peripheral interface, NOR flash) + SiFli HAL module driver. + +config HAL_SIFLI_PCD + bool "PCD HAL module" + help + Enable the PCD (USB device controller) SiFli HAL module + driver. + +config HAL_SIFLI_PDM + bool "PDM HAL module" + help + Enable the PDM (digital microphone) SiFli HAL module driver. + +config HAL_SIFLI_PINMUX + bool "PINMUX HAL module" + help + Enable the PINMUX SiFli HAL module driver. + +config HAL_SIFLI_PTC + bool "PTC HAL module" + help + Enable the PTC (peripheral-to-peripheral trigger) SiFli HAL + module driver. + +config HAL_SIFLI_RNG + bool "RNG HAL module" + help + Enable the RNG SiFli HAL module driver. + +config HAL_SIFLI_RTC + bool "RTC HAL module" + help + Enable the RTC SiFli HAL module driver. + +config HAL_SIFLI_SD + bool "SD HAL module" + help + Enable the SD/MMC SiFli HAL module driver. + +config HAL_SIFLI_SDHCI + bool "SDHCI HAL module" + help + Enable the SDHCI SiFli HAL module driver. + +config HAL_SIFLI_SECU + bool "SECU HAL module" + help + Enable the SECU (security configuration) SiFli HAL module + driver. + +config HAL_SIFLI_SPI + bool "SPI HAL module" + help + Enable the SPI SiFli HAL module driver. + +config HAL_SIFLI_TSEN + bool "TSEN HAL module" + help + Enable the TSEN (temperature sensor) SiFli HAL module + driver. + +config HAL_SIFLI_UART + bool "UART HAL module" + help + Enable the UART SiFli HAL module driver. + +config HAL_SIFLI_WDT + bool "WDT HAL module" + help + Enable the WDT (watchdog) SiFli HAL module driver. + +endif # HAL_SIFLI diff --git a/third_party/hal_sifli/SiFli-SDK b/third_party/hal_sifli/SiFli-SDK index 663325ef81..595e61556f 160000 --- a/third_party/hal_sifli/SiFli-SDK +++ b/third_party/hal_sifli/SiFli-SDK @@ -1 +1 @@ -Subproject commit 663325ef81abca5f0416cfb4f9441efcab0afc9e +Subproject commit 595e61556f77078912eb6d1b7f117e962e9b0563 diff --git a/third_party/hal_sifli/sf32lb52/ipc_os_port.h b/third_party/hal_sifli/sf32lb52/ipc_os_port.h index 4d72f787e4..8ab0061027 100644 --- a/third_party/hal_sifli/sf32lb52/ipc_os_port.h +++ b/third_party/hal_sifli/sf32lb52/ipc_os_port.h @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #pragma once diff --git a/third_party/hal_sifli/sf32lb52/lcpu_config.c b/third_party/hal_sifli/sf32lb52/lcpu_config.c index 825125f199..59fa1fa037 100644 --- a/third_party/hal_sifli/sf32lb52/lcpu_config.c +++ b/third_party/hal_sifli/sf32lb52/lcpu_config.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include #include @@ -22,11 +9,16 @@ #define NVDS_BUFF_START 0x2040FE00 -static const uint8_t s_ble_slp_default_rc10k[] = { +static const uint8_t s_ble_slp_default[] = { // Control pre-wakeup time for the sleep of BT subsysm in LCPU. - // The value is different in RC10K and LXT32K - // FIXME(SF32LB52): Adjust depending on the configured board clock source! - 0x0D, 0x02, 0x64, 0x19, + // See SiFli-SDK EXT_WAKEUP_TIME_LXT32K/EXT_WAKEUP_TIME_RC10K for details. + // RC10K -> 4500us (0x1194) + // LXT32K -> 3500us (0x0DAC) +#ifdef LXT_DISABLE + 0x0D, 0x02, 0x19, 0x11, +#else + 0x0D, 0x02, 0xAC, 0x0D, +#endif // Control maximum sleep duration of BT subsystem. // The last 0x01 means 10s in BLE only and 30s in dual mode. 0 means 500ms 0x12, 0x01, 0x01, @@ -81,12 +73,12 @@ void lcpu_custom_nvds_config(void) { assert(res); *(uint32_t *)nvds_addr = 0x4E564453; - *(uint16_t *)(nvds_addr + 4) = sizeof(s_ble_slp_default_rc10k) + 8U; + *(uint16_t *)(nvds_addr + 4) = sizeof(s_ble_slp_default) + 8U; *(uint16_t *)(nvds_addr + 6) = 0; *(uint8_t *)(nvds_addr + 8) = 0x01; *(uint8_t *)(nvds_addr + 9) = 0x06; memcpy(nvds_addr + 10, mac_addr, 6); - memcpy(nvds_addr + 16, s_ble_slp_default_rc10k, sizeof(s_ble_slp_default_rc10k)); + memcpy(nvds_addr + 16, s_ble_slp_default, sizeof(s_ble_slp_default)); } diff --git a/third_party/hal_sifli/sf32lb52/rtconfig.h b/third_party/hal_sifli/sf32lb52/rtconfig.h index bb979b91f9..162831e135 100644 --- a/third_party/hal_sifli/sf32lb52/rtconfig.h +++ b/third_party/hal_sifli/sf32lb52/rtconfig.h @@ -1,22 +1,9 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef RT_CONFIG_H -#define RT_CONFIG_H - -#define SF32LB52X_REV_AUTO 1 - -#endif +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef RT_CONFIG_H +#define RT_CONFIG_H + +#define SF32LB52X_REV_AUTO 1 + +#endif diff --git a/third_party/hal_sifli/sf32lb52/system_bf0_ap.c b/third_party/hal_sifli/sf32lb52/system_bf0_ap.c index e6c28a66bd..dc16000b25 100644 --- a/third_party/hal_sifli/sf32lb52/system_bf0_ap.c +++ b/third_party/hal_sifli/sf32lb52/system_bf0_ap.c @@ -1,18 +1,5 @@ -/* - * Copyright 2025 Core Devices LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* SPDX-FileCopyrightText: 2025 Core Devices LLC */ +/* SPDX-License-Identifier: Apache-2.0 */ #include "bf0_hal.h" #include "register.h" @@ -26,6 +13,11 @@ uint32_t __Vectors; uint32_t SystemCoreClock = 48000000UL; +extern uint8_t __ramfunc_start[]; +extern uint8_t __ramfunc_end[]; +extern const uint32_t __FLASH_start__[]; +extern const uint32_t __FLASH_size__[]; + void SystemCoreClockUpdate(void) {} enum { @@ -43,9 +35,6 @@ enum { static void prv_mpu_config(void) { uint32_t rbar, rlar; - SCB_InvalidateDCache(); - SCB_InvalidateICache(); - ARM_MPU_Disable(); for (uint8_t i = 0U; i < MPU_REGION_NUM; i++) { @@ -56,31 +45,43 @@ static void prv_mpu_config(void) { ARM_MPU_SetMemAttr(ATTR_RAM_IDX, ATTR_RAM); ARM_MPU_SetMemAttr(ATTR_DEVICE_IDX, ATTR_DEVICE); - // PSRAM and FLASH2, region 1 + // Flash code, region 1 // Non-shareable, RO, any privilege, executable - rbar = ARM_MPU_RBAR(0x10000000, ARM_MPU_SH_NON, 1, 1, 0); - rlar = ARM_MPU_RLAR(0x1fffffff, ATTR_CODE_IDX); + rbar = ARM_MPU_RBAR((uint32_t)__FLASH_start__, ARM_MPU_SH_NON, 1, 1, 0); + rlar = ARM_MPU_RLAR((uint32_t)__FLASH_start__ + (uint32_t)__FLASH_size__ - 1U, ATTR_CODE_IDX); ARM_MPU_SetRegion(0U, rbar, rlar); // Peripheral space - // Non-shareable, RW, any privilege, non-executable - rbar = ARM_MPU_RBAR(0x40000000, ARM_MPU_SH_NON, 0, 1, 1); + // Non-shareable, RW, privileged only, non-executable + rbar = ARM_MPU_RBAR(0x40000000, ARM_MPU_SH_NON, 0, 0, 1); rlar = ARM_MPU_RLAR(0x5fffffff, ATTR_DEVICE_IDX); ARM_MPU_SetRegion(1U, rbar, rlar); - // hpsys ram - // Non-shareable, RW, any privilege, executable - rbar = ARM_MPU_RBAR(0x20000000, ARM_MPU_SH_NON, 0, 1, 0); - rlar = ARM_MPU_RLAR(0x2027ffff, ATTR_RAM_IDX); + // hpsys ram, .ramfunc range only: vendor HAL code copied here from flash + // must be executable. Keep it privileged-only and read-only after startup + // has copied the section into RAM. The rest of HPSYS RAM is reachable for + // privileged code via the background map (PRIVDEFENA is set in + // ARM_MPU_Enable below). + // Non-shareable, RO privileged only, executable + rbar = ARM_MPU_RBAR((uint32_t)__ramfunc_start, ARM_MPU_SH_NON, 1, 0, 0); + rlar = ARM_MPU_RLAR((uint32_t)__ramfunc_end - 1U, ATTR_RAM_IDX); ARM_MPU_SetRegion(2U, rbar, rlar); // lpsys ram - // Non-shareable, RW, any privilege, executable - rbar = ARM_MPU_RBAR(0x203fc000, ARM_MPU_SH_NON, 0, 1, 0); + // Non-shareable, RW, privileged only, executable + rbar = ARM_MPU_RBAR(0x203fc000, ARM_MPU_SH_NON, 0, 0, 0); rlar = ARM_MPU_RLAR(0x204fffff, ATTR_RAM_IDX); ARM_MPU_SetRegion(3U, rbar, rlar); - ARM_MPU_Enable(MPU_CTRL_HFNMIENA_Msk); + // HCPU<->LCPU mailbox (last 1K of HPSYS SRAM). The BT controller running + // on LPSYS reads/writes this window directly, so it must be non-cacheable + // on the HCPU side -- otherwise the two cores see incoherent contents. + // Non-shareable, RW privileged only, non-executable. + rbar = ARM_MPU_RBAR(0x2007fc00, ARM_MPU_SH_NON, 0, 0, 1); + rlar = ARM_MPU_RLAR(0x2007ffff, ATTR_RAM_IDX); + ARM_MPU_SetRegion(4U, rbar, rlar); + + ARM_MPU_Enable(MPU_CTRL_HFNMIENA_Msk | MPU_CTRL_PRIVDEFENA_Msk); } int mpu_dcache_invalidate(void *data, uint32_t size) { @@ -127,4 +128,7 @@ void SystemInit(void) { #endif prv_mpu_config(); -} \ No newline at end of file + + SCB_EnableICache(); + SCB_EnableDCache(); +} diff --git a/third_party/hal_sifli/wscript b/third_party/hal_sifli/wscript deleted file mode 100644 index c8efac033e..0000000000 --- a/third_party/hal_sifli/wscript +++ /dev/null @@ -1,73 +0,0 @@ -def configure(conf): - if conf.env.MICRO_FAMILY.startswith('SF32'): - conf.env.append_unique('DEFINES', 'USE_HAL_DRIVER') - conf.env.append_unique('DEFINES', 'SOC_BF0_HCPU') - conf.env.append_unique('DEFINES', 'HAL_TICK_PER_SECOND=1024') - conf.env.append_unique('DEFINES', 'HAL_NMI_HANLDER_OVERRIDED') - - if conf.env.MICRO_FAMILY == 'SF32LB52': - conf.env.append_unique('DEFINES', 'SF32LB52X') - - -def build(bld): - micro_sources = bld.path.ant_glob('SiFli-SDK/drivers/hal/*.c', excl=['**/bf0_hal_audcodec.c']) - - micro_sources += [ - 'SiFli-SDK/middleware/ipc_queue/common/circular_buf.c', - 'SiFli-SDK/middleware/ipc_queue/common/ipc_hw.c', - 'SiFli-SDK/middleware/ipc_queue/common/ipc_queue.c', - ] - - if bld.env.MICRO_FAMILY == 'SF32LB52': - micro_sources += [ - 'SiFli-SDK/drivers/cmsis/sf32lb52x/bf0_pin_const.c', - 'SiFli-SDK/drivers/cmsis/sf32lb52x/bf0_lcpu_init.c', - 'SiFli-SDK/drivers/cmsis/sf32lb52x/lcpu_patch.c', - 'SiFli-SDK/drivers/cmsis/sf32lb52x/lcpu_config_type.c', - 'SiFli-SDK/drivers/cmsis/sf32lb52x/lcpu_patch_rev_b.c', - 'SiFli-SDK/drivers/cmsis/sf32lb52x/bt_rf_fulcal.c', - 'SiFli-SDK/middleware/ipc_queue/porting/sf32lb52x/hcpu/ipc_hw_port.c', - 'sf32lb52/system_bf0_ap.c', - 'sf32lb52/lcpu_config.c', - ] - - micro_includes = [ - 'SiFli-SDK/customer/boards/include', - 'SiFli-SDK/drivers/Include', - 'SiFli-SDK/drivers/cmsis/Include', - 'SiFli-SDK/external/CMSIS/Include', - 'SiFli-SDK/middleware/include', - 'SiFli-SDK/middleware/ipc_queue/common', - 'SiFli-SDK/middleware/ipc_queue/include', - ] - - if bld.env.MICRO_FAMILY == 'SF32LB52': - micro_includes += [ - 'sf32lb52', - 'SiFli-SDK/drivers/cmsis/sf32lb52x', - 'SiFli-SDK/middleware/ipc_queue/porting/sf32lb52x/hcpu', - ] - - bld.env.append_unique( - 'CFLAGS', - [ - '-Wno-unused-value', - '-Wno-unused-variable', - '-Wno-unused-function', - '-Wno-unused-but-set-variable', - ], - ) - - # FIXME(SF32LB52): board-specific settings should not be here - micro_defines = [] - if bld.is_obelix(): - micro_defines += ['LXT_DISABLE'] - - bld.stlib( - source=micro_sources, - target='hal_sifli', - use=['pblibc'], - includes=micro_includes, - export_includes=micro_includes, - defines=micro_defines, - ) diff --git a/third_party/hal_sifli/wscript_build b/third_party/hal_sifli/wscript_build new file mode 100644 index 0000000000..c5ea201ab2 --- /dev/null +++ b/third_party/hal_sifli/wscript_build @@ -0,0 +1,212 @@ +bld.env.append_unique('DEFINES', [ + 'USE_HAL_DRIVER', + 'SOC_BF0_HCPU', + 'HAL_TICK_PER_SECOND=1024', + 'HAL_NMI_HANLDER_OVERRIDED', +]) +bld.env.append_unique('LINKFLAGS', '-Wl,--undefined=HAL_GetTick') + +if bld.env.CONFIG_HAL_SIFLI_SF32LB52: + bld.env.append_unique('DEFINES', 'SF32LB52X') + +# HAL core. ADC, DMA and system config are pulled in by bf0_hal.c (HAL_Init) +# and bf0_hal_pmu.c since the SDK enables all HAL_x_MODULE_ENABLED macros. +# HLP provides the backup registers and debug print helpers. +micro_sources = [ + 'SiFli-SDK/drivers/hal/bf0_hal.c', + 'SiFli-SDK/drivers/hal/bf0_hal_adc.c', + 'SiFli-SDK/drivers/hal/bf0_hal_cortex.c', + 'SiFli-SDK/drivers/hal/bf0_hal_dma.c', + 'SiFli-SDK/drivers/hal/bf0_hal_efuse.c', + 'SiFli-SDK/drivers/hal/bf0_hal_hlp.c', + 'SiFli-SDK/drivers/hal/bf0_hal_pmu.c', + 'SiFli-SDK/drivers/hal/bf0_hal_rcc.c', + 'SiFli-SDK/drivers/hal/bf0_sys_cfg.c', +] + +# bf0_hal_aes.c implements both AES and HASH +if bld.env.CONFIG_HAL_SIFLI_AES or bld.env.CONFIG_HAL_SIFLI_HASH: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_aes.c'] + +if bld.env.CONFIG_HAL_SIFLI_AON: + micro_sources += [ + 'SiFli-SDK/drivers/hal/bf0_hal_hpaon.c', + 'SiFli-SDK/drivers/hal/bf0_hal_lpaon.c', + ] + +if bld.env.CONFIG_HAL_SIFLI_ATIM: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_tim_ex.c'] + +if bld.env.CONFIG_HAL_SIFLI_AUDCODEC: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_audcodec_m.c'] + +if bld.env.CONFIG_HAL_SIFLI_AUDPRC: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_audprc.c'] + +if bld.env.CONFIG_HAL_SIFLI_BUSMON: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_busmon.c'] + +if bld.env.CONFIG_HAL_SIFLI_CRC: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_crc.c'] + +if bld.env.CONFIG_HAL_SIFLI_EPIC: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_epic.c'] + +if bld.env.CONFIG_HAL_SIFLI_EXTDMA: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_ext_dma.c'] + +if bld.env.CONFIG_HAL_SIFLI_EZIP: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_ezip.c'] + +if bld.env.CONFIG_HAL_SIFLI_GPIO: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_gpio.c'] + +if bld.env.CONFIG_HAL_SIFLI_GPT: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_tim.c'] + +if bld.env.CONFIG_HAL_SIFLI_HCD: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_hcd.c'] + +if bld.env.CONFIG_HAL_SIFLI_I2C: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_i2c.c'] + +if bld.env.CONFIG_HAL_SIFLI_I2S: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_i2s.c'] + +if bld.env.CONFIG_HAL_SIFLI_LCD: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_lcdc.c'] + +if bld.env.CONFIG_HAL_SIFLI_LCPU_CONFIG: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_lcpu_config.c'] + +if bld.env.CONFIG_HAL_SIFLI_LCPU_PATCH: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_patch.c'] + +if bld.env.CONFIG_HAL_SIFLI_LPTIM: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_lptim.c'] + +if bld.env.CONFIG_HAL_SIFLI_LRC_CAL: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_lrc_cal.c'] + +if bld.env.CONFIG_HAL_SIFLI_MAILBOX: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_mailbox.c'] + +if bld.env.CONFIG_HAL_SIFLI_MPI: + micro_sources += [ + 'SiFli-SDK/drivers/hal/bf0_hal_mpi.c', + 'SiFli-SDK/drivers/hal/bf0_hal_mpi_ex.c', + 'SiFli-SDK/drivers/hal/flash_table.c', + ] + +if bld.env.CONFIG_HAL_SIFLI_PCD: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_pcd.c'] + +if bld.env.CONFIG_HAL_SIFLI_PDM: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_pdm.c'] + +if bld.env.CONFIG_HAL_SIFLI_PINMUX: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_pinmux.c'] + +if bld.env.CONFIG_HAL_SIFLI_PTC: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_ptc.c'] + +if bld.env.CONFIG_HAL_SIFLI_RNG: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_rng.c'] + +if bld.env.CONFIG_HAL_SIFLI_RTC: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_rtc.c'] + +if bld.env.CONFIG_HAL_SIFLI_SD: + micro_sources += [ + 'SiFli-SDK/drivers/hal/bf0_hal_sdmmc.c', + 'SiFli-SDK/drivers/hal/bf0_hal_sd_ex.c', + ] + +if bld.env.CONFIG_HAL_SIFLI_SDHCI: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_sdhci.c'] + +if bld.env.CONFIG_HAL_SIFLI_SECU: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_secu.c'] + +if bld.env.CONFIG_HAL_SIFLI_SPI: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_spi.c'] + +if bld.env.CONFIG_HAL_SIFLI_TSEN: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_tsen.c'] + +if bld.env.CONFIG_HAL_SIFLI_UART: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_uart.c'] + +if bld.env.CONFIG_HAL_SIFLI_WDT: + micro_sources += ['SiFli-SDK/drivers/hal/bf0_hal_wdt.c'] + +if bld.env.CONFIG_HAL_SIFLI_IPC_QUEUE: + micro_sources += [ + 'SiFli-SDK/middleware/ipc_queue/common/circular_buf.c', + 'SiFli-SDK/middleware/ipc_queue/common/ipc_hw.c', + 'SiFli-SDK/middleware/ipc_queue/common/ipc_queue.c', + ] + +if bld.env.CONFIG_HAL_SIFLI_SF32LB52: + micro_sources += [ + 'SiFli-SDK/drivers/cmsis/sf32lb52x/bf0_pin_const.c', + 'sf32lb52/system_bf0_ap.c', + ] + + if bld.env.CONFIG_HAL_SIFLI_IPC_QUEUE: + micro_sources += [ + 'SiFli-SDK/middleware/ipc_queue/porting/sf32lb52x/hcpu/ipc_hw_port.c', + ] + + if bld.env.CONFIG_HAL_SIFLI_LCPU_CONFIG: + micro_sources += [ + 'SiFli-SDK/drivers/cmsis/sf32lb52x/lcpu_config_type.c', + ] + + if bld.env.CONFIG_HAL_SIFLI_LCPU: + micro_sources += [ + 'SiFli-SDK/drivers/cmsis/sf32lb52x/bf0_lcpu_init.c', + 'SiFli-SDK/drivers/cmsis/sf32lb52x/lcpu_patch.c', + 'SiFli-SDK/drivers/cmsis/sf32lb52x/lcpu_patch_rev_b.c', + 'SiFli-SDK/drivers/cmsis/sf32lb52x/bt_rf_fulcal.c', + 'sf32lb52/lcpu_config.c', + ] + +micro_includes = [ + 'SiFli-SDK/customer/boards/include', + 'SiFli-SDK/drivers/Include', + 'SiFli-SDK/drivers/cmsis/Include', + 'SiFli-SDK/external/CMSIS/Include', + 'SiFli-SDK/middleware/include', + 'SiFli-SDK/middleware/ipc_queue/common', + 'SiFli-SDK/middleware/ipc_queue/include', +] + +if bld.env.CONFIG_HAL_SIFLI_SF32LB52: + micro_includes += [ + 'sf32lb52', + 'SiFli-SDK/drivers/cmsis/sf32lb52x', + 'SiFli-SDK/middleware/ipc_queue/porting/sf32lb52x/hcpu', + ] + +# FIXME(SF32LB52): board-specific settings should not be here +micro_defines = [] +if bld.env.CONFIG_BOARD_FAMILY_OBELIX or bld.env.CONFIG_BOARD_FAMILY_GETAFIX: + micro_defines += ['LXT_DISABLE'] + +bld.stlib( + source=micro_sources, + target='hal_sifli', + use=['pblibc'], + includes=micro_includes, + export_includes=micro_includes, + defines=micro_defines, + cflags=[ + '-Wno-unused-value', + '-Wno-unused-variable', + '-Wno-unused-function', + '-Wno-unused-but-set-variable', + '-Wno-sign-compare', + '-Wno-type-limits', + ], +) diff --git a/third_party/hal_stm32/stm32-sdk b/third_party/hal_stm32/stm32-sdk deleted file mode 160000 index c4e498c04a..0000000000 --- a/third_party/hal_stm32/stm32-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c4e498c04a023f4e611ce18b1e28f5fe94f63607 diff --git a/third_party/hal_stm32/wscript b/third_party/hal_stm32/wscript deleted file mode 100644 index 2b2100ed02..0000000000 --- a/third_party/hal_stm32/wscript +++ /dev/null @@ -1,44 +0,0 @@ -def configure(conf): - if conf.is_silk(): - conf.env.append_unique('DEFINES', 'STM32F412xG') - elif conf.env.MICRO_FAMILY == 'STM32F4': - conf.env.append_unique('DEFINES', 'STM32F429_439xx') - elif conf.env.MICRO_FAMILY == 'STM32F7': - conf.env.append_unique('DEFINES', 'STM32F779xx') - - -def build(bld): - if bld.env.MICRO_FAMILY == 'STM32F4': - if bld.is_silk(): - excludes = ['**/stm32f4xx_fmc.c'] - else: - excludes = ['**/stm32f4xx_fsmc.c'] - - micro_sources = bld.path.ant_glob('stm32-sdk/STM32F4xx_StdPeriph_Driver/src/*.c', - excl=excludes) - - micro_sources += bld.path.ant_glob('stm32-sdk/OpenPDMFilter/*.c') - - micro_includes = ['stm32-sdk/Device/ST/STM32F4xx/Include', - 'stm32-sdk/STM32F4xx_StdPeriph_Driver/inc', - 'stm32-sdk/OpenPDMFilter'] - - elif bld.env.MICRO_FAMILY == 'STM32F2': - micro_sources = bld.path.ant_glob('STM32F2xx_StdPeriph_Driver/src/*.c') - - micro_includes = ['stm32-sdk/Device/ST/STM32F2xx/Include', - 'stm32-sdk/STM32F2xx_StdPeriph_Driver/inc'] - - elif bld.env.MICRO_FAMILY == 'STM32F7': - micro_sources = bld.path.ant_glob('stm32f7haxx_stdperiph/*.c') - - micro_includes = ['stm32-sdk/Device/ST/STM32F7xx/Include', - 'stm32-sdk/stm32f7haxx_stdperiph'] - - bld.stlib(source=micro_sources, - target='stm32_stdlib', - use=['cmsis_core', 'pblibc'], - includes=micro_includes, - export_includes=micro_includes) - -# vim:filetype=python diff --git a/third_party/jerryscript/jerryscript/.gitignore b/third_party/jerryscript/jerryscript/.gitignore deleted file mode 100644 index 3acc327913..0000000000 --- a/third_party/jerryscript/jerryscript/.gitignore +++ /dev/null @@ -1,40 +0,0 @@ -# Produced files -.mbedignore -build/* - -# IDE related files -nbproject -**.sublime-project -**.sublime-workspace -.idea - -# Random Trash -*.swp -*.swo -*~ -core -vgcore.* -**.orig -**.directory -**.patch -.tags* -cscope.* -__pycache__ -*.pyc - -# ctags and ID database -tags -ID - -# targets -jerry_targetjs.h -targets/mbedk64f/libjerry -targets/mbedk64f/build -targets/mbedk64f/yotta_modules -targets/mbedk64f/yotta_targets -.output -targets/esp8266/output.map -targets/esp8266/libs - -# Generated documentation -docs/doxygen diff --git a/third_party/jerryscript/jerryscript/.travis.yml b/third_party/jerryscript/jerryscript/.travis.yml deleted file mode 100644 index ea52d652bc..0000000000 --- a/third_party/jerryscript/jerryscript/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -language: c - -os: linux -dist: trusty -sudo: required - -env: - - OPTS="--check-signed-off-travis --check-cppcheck --check-vera --check-license" - - OPTS="--jerry-tests --jerry-test-suite" - - OPTS="--jerry-tests --jerry-test-suite --toolchain=cmake/toolchain_linux_armv7l.cmake" TIMEOUT=300 INSTALL_QEMU_ARM=yes - - OPTS=--buildoption-test - - OPTS=--unittests - -matrix: - include: - - os: osx - env: OPTS="--jerry-tests --jerry-test-suite" - - os: osx - env: OPTS=--unittests - -before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then tools/apt-get-install-deps.sh; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$INSTALL_QEMU_ARM" == "yes" ]]; then tools/apt-get-install-qemu-arm.sh; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then tools/brew-install-deps.sh; fi - -install: - -script: "python tools/run-tests.py $OPTS" - -# The channel name "chat.freenode.net#jerryscript" -# is encrypted against Samsung/jerryscript -# to prevent IRC spam of forks -# -# travis encrypt -r "Samsung/jerryscript" "chat.freenode.net#jerryscript" -notifications: - irc: - channels: - - secure: "4kML4uZywOPaT3r/bHCvZCeQWooyzZumESmKuHG2Y8/B29WtMBobsoRQZRfOmlUP5kshfjh0Itp5WFpdACiBCoorHch/8z3VT7fIbKF4UnxrAvNiFArqxXC0OWGIu93e7uyyXJCsQ/JiOXU7bD31Mh8LbnfS1z3wBAMXi+AwcaGiVVH4VTL6O8sR3ij5WmsqpECWhyWTgTP3MiLquZ+09Lv9mp5GGciEemq4p8VnaQt2BdyEBmUJJ1EAyMCJlKNObQudegOzYsY3CVON9C87dCuHf7DYstsxb8AzwRAKn8LHiaWhYaWLfvHqoXmc4w1ZgN0HZ5Qyx8KMkZkXKUiHxuCSoXDxNAHWTGQBsTDid5drZeqOFucOHEKJzkqaWSUKUF3pY/hq/h2kjAn230DlBNkJt+ikSxwy6Mm8GG8LnH5gRMl37zHDHrtyRsKR8GIst9B1B95LAOLA5t8U/ucGKXqLsohS8glXaM4jjh69it3GeHj6NhB8NbC/LsmRrhjKzV+VnjPI6gZvN+5tDiuxMbsMal+0DdWVNCst/aO3Jz0iaA5ahyo2ZwBb2efw3CekRLMKmHtnjqB0SWWXT3/t2+5zNoM6gBjo4RPOg7k5eTOXcfk8okWtQ5d3n8UtvZ5rSiDl3rssHwp1yHuuC8rGGov74DLvyDlpM6p/dmtu2o8=" - on_success: always - on_failure: always - use_notice: true - template: - - "%{repository_name} (%{branch}@%{commit}): %{author} - %{commit_subject} [%{result}]" - - "Commit: %{compare_url}" - - "Build: %{build_url}" diff --git a/third_party/jerryscript/jerryscript/CONTRIBUTING.md b/third_party/jerryscript/jerryscript/CONTRIBUTING.md deleted file mode 100644 index 222720d2c1..0000000000 --- a/third_party/jerryscript/jerryscript/CONTRIBUTING.md +++ /dev/null @@ -1,76 +0,0 @@ -# Contribution Guidelines -## Patch Submission Process - -The following guidelines on the submission process are provided to help you be more effective when submitting code to the JerryScript project. - -When development is complete, a patch set should be submitted via GitHub pull requests. A review of the patch set will take place. When accepted, the patch set will be integrated into the master branch, verified, and tested. It is then the responsibility of the authoring developer to maintain the code throughout its lifecycle. - -Please submit all patches in public by opening a pull request. Patches sent privately to Maintainers and Committers will not be considered. Because the JerryScript Project is an Open Source project, be prepared for feedback and criticism-it happens to everyone-. If asked to rework your code, be persistent and resubmit after making changes. - -### 1. Scope the patch - -Smaller patches are generally easier to understand and test, so please submit changes in the smallest increments possible, within reason. Smaller patches are less likely to have unintended consequences, and if they do, getting to the root cause is much easier for you and the Maintainers and Committers. Additionally, smaller patches are much more likely to be accepted. - -### 2. Sign your work with the JerryScript [Developer's Certificate of Origin](DCO.md) - -The sign-off is a simple line at the end of the commit message of the patch, which certifies that you wrote it or otherwise have the right to pass it on as an Open Source patch. The sign-off is required for a patch to be accepted. In addition, any code that you want to contribute to the project must be licensed under the [Apache License 2.0](LICENSE). Contributions under a different license can not be accepted. - -We have the same requirements for using the signed-off-by process as the Linux kernel. -In short, you need to include a signed-off-by tag in every patch. - -You should use your real name and email address in the format below: - -> JerryScript-DCO-1.0-Signed-off-by: Random J Developer random@developer.example.org - -"JerryScript-DCO-1.0-Signed-off-by:" this is a developer's certification that he or she has the right to submit the patch for inclusion into the project. It is an agreement to the JerryScript [Developer's Certificate of Origin](DCO.md). **Code without a proper signoff cannot be merged into the mainline.** - -### 3. Open a GitHub [pull request](https://github.com/Samsung/jerryscript/pulls) - -You can find instructions about opening a pull request [here](https://help.github.com/articles/creating-a-pull-request). - -### 4. What if my patch is rejected? - -It happens all the time, for many reasons, and not necessarily because the code is bad. Take the feedback, adapt your code, and try again. Remember, the ultimate goal is to preserve the quality of the code and maintain the focus of the Project through intensive review. - -Maintainers and Committers typically have to process a lot of submissions, and the time for any individual response is generally limited. If the reason for rejection is unclear, please ask for more information from the Maintainers and Committers. -If you have a solid technical reason to disagree with feedback and you feel that reason has been overlooked, take the time to thoroughly explain it in your response. - -### 5. Code review - -Code review can be performed by all the members of the Project (not just Maintainers and Committers). Members can review code changes and share their opinion through comments guided by the following principles: -* Discuss code; never discuss the code's author -* Respect and acknowledge contributions, suggestions, and comments -* Listen and be open to all different opinions -* Help each other - -Changes are submitted via pull requests and only the Maintainers and Committers should approve or reject the pull request (note that only Maintainers can give binding review scores). -Changes should be reviewed in reasonable amount of time. Maintainers and Committers should leave changes open for some time (at least 1 full business day) so others can offer feedback. Review times increase with the complexity of the review. - -## Tips on GitHub Pull Requests - -* [Fork](https://guides.github.com/activities/forking) the GitHub repository and clone it locally -* Connect your local repository to the original upstream repository by adding it as a remote -* Create a [branch](https://guides.github.com/introduction/flow) for your edits -* Pull in upstream changes often to stay up-to-date so that when you submit your pull request, merge conflicts will be less likely - -For more details, see the GitHub [fork syncing](https://help.github.com/articles/syncing-a-fork) guidelines. - -## How to add the DCO line to every single commit automatically - -It is easy to forget adding the DCO line to the end of every commit message. Fortunately there is a nice way to do it automatically. Once you've cloned the repository into your local machine, you can add `prepare commit message hook` in `.git/hooks` directory like this: - -``` -#!/usr/bin/env python - -import sys - -commit_msg_filepath = sys.argv[1] - -with open(commit_msg_filepath, "r+") as f: - content = f.read() - f.seek(0, 0) - f.write("%s\n\nJerryScript-DCO-1.0-Signed-off-by: " % content) -``` - -Please refer [Git Hooks](http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) for more information. - diff --git a/third_party/jerryscript/jerryscript/DCO.md b/third_party/jerryscript/jerryscript/DCO.md deleted file mode 100644 index 9350b88cd7..0000000000 --- a/third_party/jerryscript/jerryscript/DCO.md +++ /dev/null @@ -1,22 +0,0 @@ -# JerryScript Developer's Certificate of Origin - -The JerryScript project uses the signed-off-by language and process to give us a clear chain of trust for every patch received. - -> By making a contribution to this project, I certify that: - -> (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or - -> (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or - -> (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. - -> (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project, under the same open source license. - -We have the same requirements for using the signed-off-by process as the Linux kernel. -In short, you need to include a signed-off-by tag in the commit message of every patch. - -You should use your real name and email address in the format below: - -> JerryScript-DCO-1.0-Signed-off-by: Random J Developer random@developer.example.org - -"JerryScript-DCO-1.0-Signed-off-by:" this is a developer's certification that he or she has the right to submit the patch for inclusion into the project. It is an agreement to the Developer's Certificate of Origin (above). **Code without a proper signoff cannot be merged into the mainline.** diff --git a/third_party/jerryscript/jerryscript/Doxyfile b/third_party/jerryscript/jerryscript/Doxyfile deleted file mode 100644 index f217dcda6c..0000000000 --- a/third_party/jerryscript/jerryscript/Doxyfile +++ /dev/null @@ -1,2384 +0,0 @@ -# Doxyfile 1.8.9.1 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = "JerryScript" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "JavaScript Engine for Internet of Things" - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = "docs/doxygen" - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = YES - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 2 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = YES - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = YES - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = jerry-core jerry-libc - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = YES - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = YES - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = YES - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /